getStatementInfo method
Implementation
TSQLStatementInfo getStatementInfo(String aSQL) {
// TParsePart / TPhraseSeparator / TKeyword (local types)
const ppStart = 0, ppWith = 1, ppSelect = 2, ppTableName = 3,
ppFrom = 4, ppWhere = 5, ppBogus = 8;
const sepNone = 0, sepWhiteSpace = 1, sepComma = 2, sepComment = 3,
sepParentheses = 4, sepDoubleQuote = 5, sepEnd = 6;
const keywordNames = [
'WITH', "SELECT", "INSERT", "UPDATE", "DELETE", "FROM", "JOIN",
"WHERE", "GROUP", "ORDER", "UNION", "ROWS", "LIMIT", ""
];
const kwWITH = 0, kwSELECT = 1, kwINSERT = 2, kwUPDATE = 3,
kwDELETE = 4, kwFROM = 5, kwJOIN = 6, kwWHERE = 7, kwGROUP = 8,
kwORDER = 9, kwUNION = 10, kwROWS = 11, kwLIMIT = 12,
kwUnknown = 13;
final result = TSQLStatementInfo();
var parsePart = ppStart;
final len = aSQL.length;
String charAt(int i) => i >= len ? '\x00' : aSQL[i];
var currentP = -1;
var phraseP = 0;
var pStatementPart = 0;
var breakLoop = false;
do {
currentP++;
var savedP = currentP;
int separator;
final c = charAt(currentP);
if (c == ' ' || (c.codeUnitAt(0) >= 9 && c.codeUnitAt(0) <= 13)) {
separator = sepWhiteSpace;
} else if (c == ',') {
separator = sepComma;
} else if (c == '\x00' || c == ';') {
separator = sepEnd;
} else if (c == '(') {
separator = sepParentheses;
// skip everything between brackets, since it could be a
// sub-select, and further nothing between brackets could be
// interesting for the parser. (original comment kept as-is)
var bracketCount = 1;
do {
currentP++;
if (charAt(currentP) == '(') {
bracketCount++;
} else if (charAt(currentP) == ')') {
bracketCount--;
}
} while (charAt(currentP) != '\x00' && bracketCount != 0);
if (charAt(currentP) != '\x00') currentP++;
} else if (c == '"' || c == '`') {
final p = TIntRef(currentP);
if (skipComments(aSQL, p,
connOptions.contains(TConnOption.sqEscapeSlash),
connOptions.contains(TConnOption.sqEscapeRepeat))) {
separator = sepDoubleQuote;
currentP = p.value;
} else {
separator = sepNone;
}
} else {
final p = TIntRef(currentP);
if (skipComments(aSQL, p,
connOptions.contains(TConnOption.sqEscapeSlash),
connOptions.contains(TConnOption.sqEscapeRepeat))) {
separator = sepComment;
currentP = p.value;
} else {
separator = sepNone;
}
}
if (separator != sepNone) {
if (currentP > savedP && savedP > phraseP) {
currentP = savedP; // there is something before comment or left
// parenthesis or double quote (original comment kept as-is)
}
if ((separator == sepWhiteSpace || separator == sepComment) &&
savedP == phraseP) {
phraseP = currentP; // skip comments (but not parentheses) and
// white spaces (original comment kept as-is)
}
if (currentP - phraseP > 0 || separator == sepEnd) {
final s = aSQL.substring(
phraseP, currentP > len ? len : currentP);
var keyword = kwUnknown;
for (var k = 0; k < keywordNames.length; k++) {
if (s.toUpperCase() == keywordNames[k]) {
keyword = k;
break;
}
}
switch (parsePart) {
case ppStart:
result.statementType = strToStatementType(s);
if (keyword == kwWITH) {
parsePart = ppWith;
} else if (keyword == kwSELECT) {
parsePart = ppSelect;
} else {
breakLoop = true;
}
break;
case ppWith:
// WITH [RECURSIVE] CTE_name [(column_names)] AS
// (CTE_query_definition) [, ...]
// { SELECT | INSERT | UPDATE | DELETE } ... (original comment kept as-is)
switch (keyword) {
case kwSELECT:
result.statementType = TStatementType.stSelect;
break;
case kwINSERT:
result.statementType = TStatementType.stInsert;
break;
case kwUPDATE:
result.statementType = TStatementType.stUpdate;
break;
case kwDELETE:
result.statementType = TStatementType.stDelete;
break;
}
if (result.statementType != TStatementType.stUnknown) {
breakLoop = true;
}
break;
case ppSelect:
if (keyword == kwFROM) {
parsePart = ppTableName;
}
break;
case ppTableName:
// Meta-data requests are never updateable and select
// statements from more than one table and/or derived
// tables are also not updateable (original comment kept as-is)
if (separator == sepWhiteSpace ||
separator == sepComment ||
separator == sepDoubleQuote ||
separator == sepEnd) {
result.tableName = result.tableName + s;
result.updateable = true;
}
// compound delimited classifier like:
// "schema name"."table name" (original comment kept as-is)
final nc = charAt(currentP);
if (nc != '.' && nc != '"') {
parsePart = ppFrom;
}
break;
case ppFrom:
if ((keyword == kwWHERE ||
keyword == kwGROUP ||
keyword == kwORDER ||
keyword == kwLIMIT ||
keyword == kwROWS) ||
separator == sepEnd) {
if (keyword == kwWHERE) {
parsePart = ppWhere;
} else if (keyword == kwGROUP || keyword == kwORDER) {
parsePart = ppBogus;
} else {
parsePart = ppBogus;
}
result.whereStartPos = phraseP + 1; // PhraseP-PSQL+1
pStatementPart = currentP;
} else if (keyword == kwJOIN ||
separator == sepComma ||
separator == sepParentheses) {
// joined table or user_defined_function (...) (original comment kept as-is)
result.tableName = '';
result.updateable = false;
}
break;
case ppWhere:
if ((keyword == kwGROUP ||
keyword == kwORDER ||
keyword == kwLIMIT ||
keyword == kwROWS) ||
separator == sepEnd) {
parsePart = ppBogus;
result.whereStartPos = pStatementPart;
if (separator == sepEnd) {
result.whereStopPos = currentP + 1;
} else {
result.whereStopPos = phraseP + 1;
}
} else if (keyword == kwUNION) {
parsePart = ppBogus;
result.updateable = false;
}
break;
}
}
if (separator == sepComment ||
separator == sepParentheses ||
separator == sepDoubleQuote) {
currentP--;
}
phraseP = currentP + 1;
}
} while (!breakLoop && charAt(currentP) != '\x00');
return result;
}