db_lookup_retval_t SqlParser::parseSql(std::set<std::string> &dbs, int *txLevel, bool parseMaster) { dbs.clear(); StringVector tables, aliases; if (getTokensLen() <= 0) { log_warning("empty sql for dababase lookup!\n"); return RET_ERROR_UNPARSABLE; } switch (getTokenId(0)) { case TK_SQL_SELECT: { int usemaster = 0; if (parseMaster) usemaster = 1; // special handling for our get unique id function call. if (getTokensLen() > 1 && getTokenStr(1) == "get_next_id") return RET_USE_DEFAULT_DATABASE; int fromStart, fromEnd; if (!getSqlFrom(0, &fromStart, &fromEnd)) { if ((getTokensLen() > 3 && getTokenId(1) == TK_LITERAL && getTokenId(2) == TK_OBRACE) || (getTokensLen() == 2 && getTokenId(1) == TK_LITERAL)) { // for special stored procedures return RET_USE_ALL_PARTITIONS; } printTokens("no FROM found, using default db: "); return RET_USE_DEFAULT_DATABASE; } if (!parseTableNameAndAlias(fromStart, fromEnd, tables, aliases)) { printTokens("could not parse table alias, using default db: "); return RET_USE_DEFAULT_DATABASE; } // for non-partitioned tables, we can use any db // since each db should have a view of it bool partitioned = false; for (size_t i = 0; i < tables.size(); i++) { if (dbPart->isPartitionedTable(tables[i])) { partitioned = true; break; } } if (!setDefaultLimit()) { printTokens("error in modifying LIMIT: "); return RET_ERROR_UNPARSABLE; } /* if (!partitioned) return ((*txLevel) > 0 ? RET_USE_DEFAULT_DATABASE : RET_USE_ANY_PARTITION); */ int whereStart, whereEnd; if (!getSqlWhere(fromEnd, &whereStart, &whereEnd)) { // add LIMIT, change the offset to 0 if needed uint64_t aa = 0; dbPart->getPartitionNum(tables[0], &aa); for (size_t i = 0; i < aa; i++) { std::string db; getDbMapping(tables[0], "", i, db, usemaster, 0); if (!db.empty()) dbs.insert(db); } return RET_USE_ALL_PARTITIONS; } for (size_t i = 0; i < tables.size(); i++) { std::string partitionKey; getPartitionKey(tables[i], partitionKey); if (partitionKey.empty()) { std::string db; getDbMapping(tables[i], "", 0, db, usemaster, 0); if (!db.empty()) dbs.insert(db); continue; } std::vector<uint64_t> keyValues; if (!findPartitionKeyValue(whereStart, whereEnd, tables[i], aliases[i], partitionKey, keyValues)) { printTokens("unrecognized key ranges: "); return RET_ERROR_UNPARSABLE; } if (keyValues.size() == 0) { uint64_t aa = 0; dbPart->getPartitionNum(tables[0], &aa); for (size_t i = 0; i < aa; i++) { std::string db; getDbMapping(tables[0], "", i, db, usemaster, 0); if (!db.empty()) dbs.insert(db); } return RET_USE_ALL_PARTITIONS; } // find the db partition for all the IDs for (size_t k = 0; k < keyValues.size(); k++) { std::string db; getDbMapping(tables[i], partitionKey, keyValues[k], db, usemaster, 0); if (!db.empty()) dbs.insert(db); } } if (dbs.empty()) return RET_USE_ALL_PARTITIONS; return RET_DB_LOOKUP_SUCCESS; } case TK_SQL_UPDATE: { int setPos; if (!findToken(0, getTokensLen(), TK_SQL_SET, &setPos)) { printTokens("could not find SET in UPDATE: "); return RET_ERROR_UNPARSABLE; }; if (getTokenId(setPos - 1) != TK_LITERAL) { printTokens("expecting table name before SET: "); return RET_ERROR_UNPARSABLE; } std::string table = getTokenStr(setPos - 1); // for nonpartitioned tables, update the default master db if (!(dbPart->isPartitionedTable(table))) { std::string db; getDbMapping(table, "", 0, db, 1, 0); if (!db.empty()) dbs.insert(db); return RET_USE_ALL_PARTITIONS; } int whereStart, whereEnd; if (!getSqlWhere(setPos + 1, &whereStart, &whereEnd)) { printTokens("no WHERE found: "); return RET_ERROR_UNPARSABLE; } std::string partitionKey; getPartitionKey(table, partitionKey); g_assert(!partitionKey.empty()); std::vector<uint64_t> keyValues; if (!findPartitionKeyValue(whereStart, whereEnd, table, "", partitionKey, keyValues)) { printTokens("unrecognized ranges: "); return RET_ERROR_UNPARSABLE; } // find the db partition for all the IDs for (size_t k = 0; k < keyValues.size(); k++) { std::string db; getDbMapping(table, partitionKey, keyValues[k], db, 1, 0); if (!db.empty()) dbs.insert(db); } if (dbs.empty()) return RET_USE_ALL_PARTITIONS; return RET_DB_LOOKUP_SUCCESS; } case TK_SQL_INSERT: { // support format: INSERT ... <table> (...) VALUES (....) int pos; uint64_t insertid = 0; if (!findToken(1, getTokensLen(), TK_LITERAL, &pos)) { printTokens("could not find table name: "); return RET_ERROR_UNPARSABLE; } std::string table = getTokenStr(pos); std::string partitionKey; getPartitionKey(table, partitionKey); if (getTokenId(++pos) != TK_OBRACE) { printTokens("unrecognized INSERT: "); return RET_ERROR_UNPARSABLE; } pos++; std::string autoIncrementColumn; dbPart->getAutoIncrementColumn(table, autoIncrementColumn); int keyPos = -1; int autoColPos = -1; for (int i = pos; i < getTokensLen(); i++) { if ((getTokenId(i) == TK_CBRACE) || (autoColPos >= 0 && keyPos >= 0)) break; if (getTokenId(i) == TK_LITERAL && tokComp(i, partitionKey) == 0) { keyPos = i - pos; continue; } if (getTokenId(i) == TK_LITERAL && tokComp(i, autoIncrementColumn) == 0) { autoColPos = i - pos; } } if ((!partitionKey.empty()) && keyPos == -1 && partitionKey != autoIncrementColumn) { log_warning("could not find the partition key %s:", partitionKey.c_str()); printTokens(); return RET_ERROR_UNPARSABLE; } if ((!partitionKey.empty()) && keyPos == -1) { // special handling for the case in which partition key type is auto increment. // need to get the id first and then modify the INSERT uint64_t id; if (!dbPart->getNextUniqueId(table, &id)) { log_warning("could not get next unique id for %s", partitionKey.c_str()); printTokens(); return RET_DB_LOOKUP_ERROR; } insertid = id; std::string db; getDbMapping(table, partitionKey, id, db, 1, id); if (!db.empty()) dbs.insert(db); else { printTokens("could not find db for id %d: "); return RET_DB_LOOKUP_ERROR; } if (modifySqlForInsert(partitionKey, id)) { if (partitionKey == autoIncrementColumn || autoIncrementColumn.empty()) return RET_DB_LOOKUP_SUCCESS; } else { log_warning("could not insert id for %s ", partitionKey.c_str()); printTokens(); return RET_DB_LOOKUP_ERROR; } } if (!autoIncrementColumn.empty() && autoColPos < 0 && (partitionKey != autoIncrementColumn)) { // need to get unique ids for auto increment columns uint64_t id; if (!dbPart->getNextUniqueId(table, &id)) { log_warning("could not get next unique id for %s", autoIncrementColumn.c_str()); printTokens(); return RET_DB_LOOKUP_ERROR; } insertid = id; if (modifySqlForInsert(autoIncrementColumn, id)) { // for nonparitioned table INSERT, use the default master db if (partitionKey.empty()) return RET_USE_DEFAULT_DATABASE; if (keyPos == -1) return RET_DB_LOOKUP_SUCCESS; } else { log_warning("could not insert id for %s ", autoIncrementColumn.c_str()); printTokens(); return RET_DB_LOOKUP_ERROR; } } // for nonparitioned table INSERT, use the default master db if (partitionKey.empty()) { std::string db; getDbMapping(table, "", 0, db, 1, insertid); if (!db.empty()) dbs.insert(db); return RET_USE_ALL_PARTITIONS; } pos += keyPos; int valPos; if (!findToken(pos, getTokensLen(), TK_SQL_VALUES, &valPos)) { printTokens("VALUES is not found: "); return RET_ERROR_UNPARSABLE; } if (getTokenId(valPos + 1) != TK_OBRACE) { printTokens("expecting '(' after VALUES: "); return RET_ERROR_UNPARSABLE; } pos = valPos + 2 + keyPos; if (pos < getTokensLen()) {//dqm //if (pos < getTokensLen() && getTokenId(pos) == TK_INTEGER) { uint64_t id = tokenToUint64(pos); std::string db; getDbMapping(table, partitionKey, id, db, 1, insertid); if (!db.empty()) dbs.insert(db); if (dbs.empty()) { printTokens("could not find db mapping: "); return RET_ERROR_UNPARSABLE; } return RET_DB_LOOKUP_SUCCESS; } else { log_warning("could not recognize value for %s:", partitionKey.c_str()); printTokens(); return RET_ERROR_UNPARSABLE; } break; } case TK_SQL_REPLACE: { // support format: replace ... <table> (...) VALUES (....) int pos; uint64_t insertid = 0; if (!findToken(1, getTokensLen(), TK_LITERAL, &pos)) { printTokens("could not find table name: "); return RET_ERROR_UNPARSABLE; } std::string table = getTokenStr(pos); std::string partitionKey; getPartitionKey(table, partitionKey); if (getTokenId(++pos) != TK_OBRACE) { printTokens("unrecognized INSERT: "); return RET_ERROR_UNPARSABLE; } pos++; std::string autoIncrementColumn; dbPart->getAutoIncrementColumn(table, autoIncrementColumn); int keyPos = -1; int autoColPos = -1; for (int i = pos; i < getTokensLen(); i++) { if ((getTokenId(i) == TK_CBRACE) || (autoColPos >= 0 && keyPos >= 0)) break; if (getTokenId(i) == TK_LITERAL && tokComp(i, partitionKey) == 0) { keyPos = i - pos; continue; } if (getTokenId(i) == TK_LITERAL && tokComp(i, autoIncrementColumn) == 0) { autoColPos = i - pos; } } if ((!partitionKey.empty()) && keyPos == -1 && partitionKey != autoIncrementColumn) { log_warning("could not find the partition key %s:", partitionKey.c_str()); printTokens(); return RET_ERROR_UNPARSABLE; } if ((!partitionKey.empty()) && keyPos == -1) { // special handling for the case in which partition key type is auto increment. // need to get the id first and then modify the INSERT uint64_t id; if (!dbPart->getNextUniqueId(table, &id)) { log_warning("could not get next unique id for %s", partitionKey.c_str()); printTokens(); return RET_DB_LOOKUP_ERROR; } insertid = id; std::string db; getDbMapping(table, partitionKey, id, db, 1, id); if (!db.empty()) dbs.insert(db); else { printTokens("could not find db for id %d: "); return RET_DB_LOOKUP_ERROR; } if (modifySqlForInsert(partitionKey, id)) { if (partitionKey == autoIncrementColumn || autoIncrementColumn.empty()) return RET_DB_LOOKUP_SUCCESS; } else { log_warning("could not insert id for %s ", partitionKey.c_str()); printTokens(); return RET_DB_LOOKUP_ERROR; } } if (!autoIncrementColumn.empty() && autoColPos < 0 && (partitionKey != autoIncrementColumn)) { // need to get unique ids for auto increment columns uint64_t id; if (!dbPart->getNextUniqueId(table, &id)) { log_warning("could not get next unique id for %s", autoIncrementColumn.c_str()); printTokens(); return RET_DB_LOOKUP_ERROR; } insertid = id; if (modifySqlForInsert(autoIncrementColumn, id)) { // for nonparitioned table INSERT, use the default master db if (partitionKey.empty()) return RET_USE_DEFAULT_DATABASE; if (keyPos == -1) return RET_DB_LOOKUP_SUCCESS; } else { log_warning("could not insert id for %s ", autoIncrementColumn.c_str()); printTokens(); return RET_DB_LOOKUP_ERROR; } } // for nonparitioned table INSERT, use the default master db if (partitionKey.empty()) { std::string db; getDbMapping(table, "", 0, db, 1, insertid); if (!db.empty()) dbs.insert(db); return RET_USE_ALL_PARTITIONS; } pos += keyPos; int valPos; if (!findToken(pos, getTokensLen(), TK_SQL_VALUES, &valPos)) { printTokens("VALUES is not found: "); return RET_ERROR_UNPARSABLE; } if (getTokenId(valPos + 1) != TK_OBRACE) { printTokens("expecting '(' after VALUES: "); return RET_ERROR_UNPARSABLE; } pos = valPos + 2 + keyPos; if (pos < getTokensLen()) {//dqm //if (pos < getTokensLen() && getTokenId(pos) == TK_INTEGER) { uint64_t id = tokenToUint64(pos); std::string db; getDbMapping(table, partitionKey, id, db, 1, insertid); if (!db.empty()) dbs.insert(db); if (dbs.empty()) { printTokens("could not find db mapping: "); return RET_ERROR_UNPARSABLE; } return RET_DB_LOOKUP_SUCCESS; } else { log_warning("could not recognize value for %s:", partitionKey.c_str()); printTokens(); return RET_ERROR_UNPARSABLE; } break; } case TK_SQL_ALTER: { std::string tableName; if (getTokensLen() >= 3 && getTokenId(1) == TK_SQL_TABLE) { tableName = getTokenStr(2); } else if (getTokensLen() >= 4 && getTokenId(1) == TK_SQL_IGNORE && getTokenId(2) == TK_SQL_TABLE) { tableName = getTokenStr(3); } else break; if (dbPart->isPartitionedTable(tableName)) return RET_USE_ALL_PARTITIONS; else return RET_USE_DEFAULT_DATABASE; } case TK_SQL_CALL: { return RET_USE_ALL_PARTITIONS; } case TK_SQL_SHOW: { if (getTokensLen() == 4 && getTokenId(2) == TK_SQL_FROM && strcasecmp(getTokenStr(1).c_str(), "fields") == 0) { if (dbPart->isPartitionedTable(getTokenStr(3))) { return RET_USE_ANY_PARTITION; } return RET_USE_DEFAULT_DATABASE; } if (getTokensLen() == 2 && strcasecmp(getTokenStr(1).c_str(), "tables") == 0) { //special handling for show tables; // std::string sql = "select table_name "; sql.append(" from kind_setting order by table_name"); g_string_truncate(inputSql, NET_HEADER_SIZE + 1); g_string_append_len(inputSql, sql.data(), sql.size()); network_mysqld_proto_set_header_len((unsigned char *) (inputSql->str), inputSql->len - NET_HEADER_SIZE); return RET_USE_DEFAULT_DATABASE; } else return RET_USE_DEFAULT_DATABASE; break; } case TK_SQL_DELETE: { int fromPos; if (!findToken(1, getTokensLen(), TK_SQL_FROM, &fromPos)) { printTokens("could not find FROM in DELETE: "); return RET_ERROR_UNPARSABLE; }; if (fromPos >= getTokensLen() - 1) { printTokens("could not find table name in DELETE: "); return RET_ERROR_UNPARSABLE; } std::string table = getTokenStr(fromPos + 1); // for nonpartitioned tables, update the default master db if (!(dbPart->isPartitionedTable(table))) { std::string db; getDbMapping(table, "", 0, db, 1, 0); if (!db.empty()) dbs.insert(db); return RET_USE_ALL_PARTITIONS; } int whereStart, whereEnd; if (!getSqlWhere(fromPos + 1, &whereStart, &whereEnd)) { printTokens("no WHERE found: "); return RET_ERROR_UNPARSABLE; } std::string partitionKey; getPartitionKey(table, partitionKey); g_assert(!partitionKey.empty()); std::vector<uint64_t> keyValues; if (!findPartitionKeyValue(whereStart, whereEnd, table, "", partitionKey, keyValues)) { printTokens("unrecognized ranges: "); return RET_ERROR_UNPARSABLE; } // find the db partition for all the IDs for (size_t k = 0; k < keyValues.size(); k++) { std::string db; getDbMapping(table, partitionKey, keyValues[k], db, 1, 0); if (!db.empty()) dbs.insert(db); } if (dbs.empty()) return RET_USE_ALL_PARTITIONS; return RET_DB_LOOKUP_SUCCESS; } case TK_SQL_DESC: case TK_SQL_DESCRIBE: { if (getTokensLen() >= 2) { std::string tableName = getTokenStr(1); if (dbPart->isPartitionedTable(tableName)) return RET_USE_ANY_PARTITION; else return RET_USE_DEFAULT_DATABASE; } return RET_ERROR_UNPARSABLE; } case TK_SQL_SET: { if ((getTokensLen() >= 4) && (getTokenId(1) == TK_SQL_AUTOCOMMIT) && (getTokenStr(3).compare("0") == 0)) { (*txLevel)++; } return RET_USE_ALL_DATABASES; } case TK_SQL_START: case TK_SQL_BEGIN: { (*txLevel)++; return RET_USE_ALL_DATABASES; } case TK_SQL_COMMIT: case TK_SQL_ROLLBACK: { (*txLevel)--; return RET_USE_ALL_DATABASES; } default: { break; } } printTokens("unrecognized query, using default master db: "); return RET_USE_DEFAULT_DATABASE; }
inline bool getDistributionKey() const { return getPartitionKey(); };