Chinaunix首页 | 论坛 | 博客
  • 博客访问: 31016
  • 博文数量: 6
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 91
  • 用 户 组: 普通用户
  • 注册时间: 2014-10-19 20:11
文章分类

全部博文(6)

文章存档

2016年(1)

2014年(5)

我的朋友

分类: Java

2014-10-22 23:00:49

上篇在Session.java中

  1. Statement cs = parser.compileStatement(ResultProperties.defaultPropsValue)

本来服务器端完全不知道客户端想要做什么事情,是要查询某个表的数据,还是更新哪条记录,还是要创建一个表,经过parser对象的处理之后,服务器端就能明白客户端到底要做什么了。

今天,我们就来深入看parser这个对象,是怎么做的。

 

在compileStatement之前,其实先调用了reset(String sql) ,有什么作用呢?基本上就是要通知各部门准备,下一个要编译的sql变成这个,该复位的复位。——因为每一个Session对象中有一个parser对象,而不是来一个sql,new一个parser。

在ParserDDL.java中,reset如下

点击(此处)折叠或打开

  1. void reset(String sql) {
  2.         super.reset(sql);
  3. }


[不过这里个人感觉,只是调用super.reset(sql)的话,好像没有必要写个方法哦]

在ParserDDL的父类ParserDQL.java,reset如下:

点击(此处)折叠或打开

  1. void reset(String sql) {
  2.         super.reset(sql);
  3.         compileContext.reset();
  4.         lastError = null;
  5.     }

这里做了一个清除工作(复归没有编译的状态)

在ParserDQL的父类ParserBase.java中,reset如下:

点击(此处)折叠或打开

  1. void reset(String sql) {
  2.         scanner.reset(sql);
  3.         //
  4.         parsePosition = 0;
  5.         isCheckOrTriggerCondition = false;
  6.         isSchemaDefinition = false;
  7.         isRecording = false;
  8.         recordedStatement = null;
  9.     }

scanner.reset

点击(此处)折叠或打开

  1. public void reset(String sql) {
  2.         sqlString = sql;
  3.         currentPosition = 0;
  4.         tokenPosition = 0;
  5.         limit = sqlString.length();
  6.         hasNonSpaceSeparator = false;
  7.         eolPosition = -1;
  8.         lineNumber = 1;
  9.         token.reset();
  10.         token.tokenType = Tokens.X_STARTPARSE;
  11.     }

token.reset

点击(此处)折叠或打开

  1. void reset() {
  2.         tokenString = "";
  3.         tokenType = Tokens.X_UNKNOWN_TOKEN;
  4.         dataType = null;
  5.         tokenValue = null;
  6.         namePrefix = null;
  7.         namePrePrefix = null;
  8.         namePrePrePrefix = null;
  9.         charsetSchema = null;
  10.         charsetName = null;
  11.         fullString = null;
  12.         lobMultiplierType = Tokens.X_UNKNOWN_TOKEN;
  13.         isDelimiter = false;
  14.         isDelimitedIdentifier = false;
  15.         isDelimitedPrefix = false;
  16.         isDelimitedPrePrefix = false;
  17.         isDelimitedPrePrePrefix = false;
  18.         isUndelimitedIdentifier = false;
  19.         hasIrregularChar = false;
  20.         isReservedIdentifier = false;
  21.         isCoreReservedIdentifier = false;
  22.         isHostParameter = false;
  23.         isMalformed = false;
  24.         //
  25.         expression = null;
  26.         hasColumnList = false;
  27.     }

哎呀,终于把该复位的都复位了。为什么不直接new 一个parser对象呢?从以上也可以看到,要操作的字段也挺多,我的理解,如果new的成本比逐一复位成本要高吧。

 

裤子都脱了不能只看reset就完事了啊,真正的实质还没开始呢,下面就进入compile

点击(此处)折叠或打开

  1. Statement compileStatement(int props) {
  2.         Statement cs = compilePart(props);
  3.         if (token.tokenType == Tokens.X_ENDPARSE) {
  4.             if (cs.getSchemaName() == null) {
  5.                 cs.setSchemaHsqlName(session.getCurrentSchemaHsqlName());
  6.             }
  7.             return cs;
  8.         }
  9.         throw unexpectedToken();
  10.     }
代码不难懂,直接进入compilePart:


点击(此处)折叠或打开

  1. private Statement compilePart(int props) {

  2.         Statement cs;

  3.         compileContext.reset();
  4.         setParsePosition(getPosition());

  5.         if (token.tokenType == Tokens.X_STARTPARSE) {
  6.             read();
  7.         }

  8.         //此处先省略100多行处理内容

  9.         return cs;
  10.     }
我们这里先不看省略的100多行,进到 read:


点击(此处)折叠或打开

  1. void read() {
  2.         scanner.scanNext();
  3.         //malformed:畸形的
  4.         if (token.isMalformed) {
  5.             int errorCode = -1;
  6.             //错误代码处理,略
  7.             switch (token.tokenType) {
  8.             }
  9.             throw Error.error(errorCode, token.getFullString());
  10.         }
  11.         if (isRecording) {
  12.             Token dup = token.duplicate();
  13.             dup.position = scanner.getTokenPosition();
  14.             recordedStatement.add(dup);
  15.         }
  16.     }

这里主体工作在scanNext:

点击(此处)折叠或打开

  1. public void scanNext() {
  2.         if (currentPosition == limit) {
  3.             resetState();
  4.             token.tokenType = Tokens.X_ENDPARSE;
  5.             return;
  6.         }
  7.         if (scanSeparator()) {
  8. // token.isDelimiter = true;
  9.         }
  10.         if (currentPosition == limit) {
  11.             resetState();
  12.             token.tokenType = Tokens.X_ENDPARSE;
  13.             return;
  14.         }
  15.         boolean needsDelimiter = !token.isDelimiter;
  16.         scanToken();
  17.         if (needsDelimiter && !token.isDelimiter) {
  18. // token.tokenType = Token.X_UNKNOWN_TOKEN;
  19.         }
  20.         if (token.isMalformed) {
  21.             token.fullString = getPart(tokenPosition, currentPosition);
  22.         }
  23.     }

currentPosition == limit, limit就是指字符串的长度,表明当前位置已到最后,则当然是结束了。而scanSeparator(),则是忽略掉空格制表符等分隔符号。

所以该函数的本质内容在scanToken。

scanToken的全文内容就不在此贴了,相当长。这里我挑一些代表性的代码,贴出来:

点击(此处)折叠或打开

  1. case '[' :
  2.                 token.tokenString = Tokens.T_LEFTBRACKET;
  3.                 token.tokenType = Tokens.LEFTBRACKET;

  4.                 currentPosition++;

  5.                 token.isDelimiter = true;

  6.                 return;


这种类型,就是一个字符,代表一个token的处理过程,设置token的类型,以及字符串,然后将扫描位置往后移动。

点击(此处)折叠或打开

  1. case '<' :
  2.                 if (charAt(currentPosition + 1) == '>') {
  3.                     token.tokenString = Tokens.T_NOT_EQUALS;
  4.                     token.tokenType = Tokens.NOT_EQUALS;
  5.                     currentPosition += 2;
  6.                     token.isDelimiter = true;
  7.                     return;
  8.                 }
  9.                 if (charAt(currentPosition + 1) == '=') {
  10.                     token.tokenString = Tokens.T_LESS_EQUALS;
  11.                     token.tokenType = Tokens.LESS_EQUALS;
  12.                     currentPosition += 2;
  13.                     token.isDelimiter = true;
  14.                     return;
  15.                 }
  16.                 token.tokenString = Tokens.T_LESS;
  17.                 token.tokenType = Tokens.LESS;
  18.                 currentPosition++;
  19.                 token.isDelimiter = true;
  20.                 return;

这代表来另一种类型,本身单独一个字符,能作为一个token,但是如果后面跟着某些特定的字符时,又是另外的Token

 

点击(此处)折叠或打开

  1. case 'b' :
  2.             case 'B' :
  3.                 if (charAt(currentPosition + 1) == '\'') {
  4.                     currentPosition++;

  5.                     scanBitString();

  6.                     if (token.isMalformed) {
  7.                         return;
  8.                     }

  9.                     token.dataType = BitType.getBitType(
  10.                         Types.SQL_BIT,
  11.                         ((BinaryData) token.tokenValue).bitLength(null));
  12.                     token.tokenType = Tokens.X_VALUE;

  13.                     return;
  14.                 }
  15.                 break;


以某些字母开头,可能会是关键字,又可能是一个普通的标识符,如bit是一种数据类型,但bxxxx,则是一个标识符。

  1. scanIdentifierChain();
  2. setIdentifierProperties();


在处理了所有特殊的地方之后,做为标识符读取。

Scanner.java文件行数有2500多行,功能就是将字符串分成一个个的Token(Token流)。之所以这么“庞大”,是由HSQLDB支持的SQL功能决定的:功能性包括支持的sql语句、关键字的数量、数据类型的数量,这个在scanner里需要一一区分出来。

这个过程是编译的第一步叫:词法分析

本篇先到这里,本文主要分析了编译的准备工作,以及SQL语句词法分析过程。

[相关源码文件]

Session.java

ParserCommand.java[及其父类:ParserDDL.java、ParserDML.java、ParserDQL.java、ParserBase.java]

Scanner.java

阅读(1245) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~