Hello Mr.J——SQL分类

    xiaoxiao2021-03-25  143

      看了很长很长时间的建立连接的源码,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。

    转载请注明原文地址: https://ju.6miu.com/read-2642.html

    最新回复(0)