Hello Mr.J——SQL分类

  看了很长很长时间的建立连接的源码,NIO,SOCKET,连接这部分确实是我的短处,到现在都没看懂。

  恩,换了一部分研究,就看了一下解析SQL的过程,这个过程在mycat的源码贡献者——http://www.hashzhang.com/frontend/html/index.html 这大兄弟这里是归类到了路由模块中。

  既然人家是源码贡献者,站在巨人的肩膀上,顺着他的分类继续分析吧。

  一个SQL语句从应用程序中发送到mycat上,mycat首先需要判断这个SQL语句是哪种。比如SELECT语句和SHOW语句就属于不同的大分类。

  解析第一个词的方法在这,其实就是判断了一下第一个字符是哪个字母,然后case不同的方法进行继续判断。这里就不写多余的注释了,很简单。

    public static int parse(String stmt) {
		int length = stmt.length();
		//FIX BUG FOR SQL SUCH AS /XXXX/SQL
		int rt = -1;
		for (int i = 0; i < length; ++i) {
			switch (stmt.charAt(i)) {
			case ' ':
			case '\t':
			case '\r':
			case '\n':
				continue;
			case '/':
				// such as /*!40101 SET character_set_client = @saved_cs_client
				// */;
				if (i == 0 && stmt.charAt(1) == '*' && stmt.charAt(2) == '!' && stmt.charAt(length - 2) == '*'
						&& stmt.charAt(length - 1) == '/') {
					return MYSQL_CMD_COMMENT;
				}
			case '#':
				i = ParseUtil.comment(stmt, i);
				if (i + 1 == length) {
					return MYSQL_COMMENT;
				}
				continue;
			case 'A':
			case 'a':
				rt = aCheck(stmt, i);
				if (rt != OTHER) {
					return rt;
				}
				continue;
			case 'B':
			case 'b':
				rt = beginCheck(stmt, i);
				if (rt != OTHER) {
					return rt;
				}
				continue;
			case 'C':
			case 'c':
				rt = commitOrCallCheckOrCreate(stmt, i);
				if (rt != OTHER) {
					return rt;
				}
				continue;
			case 'D':
			case 'd':
				rt = deleteOrdCheck(stmt, i);
				if (rt != OTHER) {
					return rt;
				}
				continue;
			case 'E':
			case 'e':
				rt = explainCheck(stmt, i);
				if (rt != OTHER) {
					return rt;
				}
				continue;
			case 'I':
			case 'i':
				rt = insertCheck(stmt, i);
				if (rt != OTHER) {
					return rt;
				}
				continue;
				case 'M':
				case 'm':
					rt = migrateCheck(stmt, i);
					if (rt != OTHER) {
						return rt;
					}
					continue;
			case 'R':
			case 'r':
				rt = rCheck(stmt, i);
				if (rt != OTHER) {
					return rt;
				}
				continue;
			case 'S':
			case 's':
				rt = sCheck(stmt, i);
				if (rt != OTHER) {
					return rt;
				}
				continue;
			case 'T':
			case 't':
				rt = tCheck(stmt, i);
				if (rt != OTHER) {
					return rt;
				}
				continue;
			case 'U':
			case 'u':
				rt = uCheck(stmt, i);
				if (rt != OTHER) {
					return rt;
				}
				continue;
			case 'K':
			case 'k':
				rt = killCheck(stmt, i);
				if (rt != OTHER) {
					return rt;
				}
				continue;
			case 'H':
			case 'h':
				rt = helpCheck(stmt, i);
				if (rt != OTHER) {
					return rt;
				}
				continue;
			case 'L':
			case 'l':
				rt = lCheck(stmt, i);
				if (rt != OTHER) {
					return rt;
				}
				continue;
			default:
				continue;
			}
		}
		return OTHER;
	}
  在判断第一个字母之后,会调用不同的方法进行继续判断,比如,第一个字母是S或者s,那么可能会有Select、show、start、savepoint等等语句,这样还需要进行第二个字母的判断。

	static int sCheck(String stmt, int offset) {
		if (stmt.length() > ++offset) {
			switch (stmt.charAt(offset)) {
			case 'A':
			case 'a':
				return savepointCheck(stmt, offset);
			case 'E':
			case 'e':
				return seCheck(stmt, offset);
			case 'H':
			case 'h':
				return showCheck(stmt, offset);
			case 'T':
			case 't':
				return startCheck(stmt, offset);
			default:
				return OTHER;
			}
		}
		return OTHER;
	}
  第二个字母之后有很多语句可以确定了,有的还不能确定,比如Select和Set,这个时候还需要继续判断,第三个判断我就不贴了,假装看完了,然后选择Select语句的处理方法看看吧。

	static int selectCheck(String stmt, int offset) {
		if (stmt.length() > offset + 4) {
			char c1 = stmt.charAt(++offset);
			char c2 = stmt.charAt(++offset);
			char c3 = stmt.charAt(++offset);
			char c4 = stmt.charAt(++offset);
			if ((c1 == 'E' || c1 == 'e')
					&& (c2 == 'C' || c2 == 'c')
					&& (c3 == 'T' || c3 == 't')
					&& (c4 == ' ' || c4 == '\t' || c4 == '\r' || c4 == '\n'
							|| c4 == '/' || c4 == '#')) {
				return (offset << 8) | SELECT;
			}
		}
		return OTHER;
	}
  进入Select的语句检查后,会逐一判断select后面的几个字母,最后会将传入的表示第几个字符做一个移位运算再与表示SELECT的数值做一个与运算,然后返回去。其实并不理解为什么要这么做。。。

  在判断了语句属于哪个大类之后,mycat会把语句交给专门处理这种大类语句的类来进行后续的处理。

	public void query(String sql) {
		
		ServerConnection c = this.source;
		if (LOGGER.isDebugEnabled()) {
			LOGGER.debug(new StringBuilder().append(c).append(sql).toString());
		}
		//解析SQL语句
		int rs = ServerParse.parse(sql);
		//与运算
		int sqlType = rs & 255;
		
		switch (sqlType) {
		//explain sql
		case ServerParse.EXPLAIN:
			ExplainHandler.handle(sql, c, rs >>> 8);
			break;
		//explain2 datanode=? sql=?
		case ServerParse.EXPLAIN2:
			Explain2Handler.handle(sql, c, rs >>> 8);
			break;
		case ServerParse.SET:
			SetHandler.handle(sql, c, rs >>> 8);
			break;
		case ServerParse.SHOW:
			ShowHandler.handle(sql, c, rs >>> 8);
			break;
		case ServerParse.SELECT:
			SelectHandler.handle(sql, c, rs >>> 8);
			break;
  后面太长了,就不截了,都是一样的调用某个大类语句的处理。

  在进入Select语句处理类之后,mycat还会对第二个词进行分析,有些语句不需要访问后面的数据库返回数据,直接返回的是mycat模拟的mysql服务端的信息。

  而需要处理的语句,就会执行路由查询,获取分片信息,执行数据库的查询。

public final class SelectHandler {

	public static void handle(String stmt, ServerConnection c, int offs) {
		int offset = offs;
		switch (ServerParseSelect.parse(stmt, offs)) {
		case ServerParseSelect.VERSION_COMMENT:
			SelectVersionComment.response(c);
			break;
		case ServerParseSelect.DATABASE:
			SelectDatabase.response(c);
			break;
		case ServerParseSelect.USER:
			SelectUser.response(c);
			break;
		case ServerParseSelect.VERSION:
			SelectVersion.response(c);
			break;
		case ServerParseSelect.SESSION_INCREMENT:
			SessionIncrement.response(c);
			break;
		case ServerParseSelect.SESSION_ISOLATION:
			SessionIsolation.response(c);
			break;
		case ServerParseSelect.LAST_INSERT_ID:
			// offset = ParseUtil.move(stmt, 0, "select".length());
			loop:for (int l=stmt.length(); offset < l; ++offset) {
				switch (stmt.charAt(offset)) {
				case ' ':
					continue;
				case '/':
				case '#':
					offset = ParseUtil.comment(stmt, offset);
					continue;
				case 'L':
				case 'l':
					break loop;
				}
			}
			offset = ServerParseSelect.indexAfterLastInsertIdFunc(stmt, offset);
			offset = ServerParseSelect.skipAs(stmt, offset);
			SelectLastInsertId.response(c, stmt, offset);
			break;
		case ServerParseSelect.IDENTITY:
			// offset = ParseUtil.move(stmt, 0, "select".length());
			loop:for (int l=stmt.length(); offset < l; ++offset) {
				switch (stmt.charAt(offset)) {
				case ' ':
					continue;
				case '/':
				case '#':
					offset = ParseUtil.comment(stmt, offset);
					continue;
				case '@':
					break loop;
				}
			}
			int indexOfAtAt = offset;
			offset += 2;
			offset = ServerParseSelect.indexAfterIdentity(stmt, offset);
			String orgName = stmt.substring(indexOfAtAt, offset);
			offset = ServerParseSelect.skipAs(stmt, offset);
			SelectIdentity.response(c, stmt, offset, orgName);
			break;
            case ServerParseSelect.SELECT_VAR_ALL:
                SelectVariables.execute(c,stmt);
                break;
			case ServerParseSelect.SESSION_TX_READ_ONLY:
				SelectTxReadOnly.response(c);
				break;
		default:
			c.execute(stmt, ServerParse.SELECT);
		}
	}

}
  最后的c.execute就是执行数据库查询,剩下的都是不用查询的- -

  ok,这里就写到这了,大概是懂了SQL的解析过程了,其实可以看做是一个树形结构,广度优先搜索。从周更变成月更了。不应该呀,兄dei。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值