Fix for embedded PL/SQL blocks (Oracle Pro*C) (#985)

* fix for correct parsing of embedded PL/SQL blocks (Oracle Pro*C)

* enforce SQL block end at the end of nearest outer C block, when
appropriate terminator is not found

* added check for ; at the end of END-EXEC and made SQL block detection
more readable
This commit is contained in:
Alexey Eryomenko 2017-11-03 13:31:33 +03:00 committed by Daniel Marjamäki
parent a8700f5622
commit 02461753f3
3 changed files with 40 additions and 5 deletions

View File

@ -1981,12 +1981,12 @@ void Tokenizer::simplifySQL()
{
for (Token *tok = list.front(); tok; tok = tok->next()) {
if (Token::simpleMatch(tok, "EXEC SQL")) {
const Token *end = tok->tokAt(2);
while (end && end->str() != ";")
end = end->next();
const Token *end = findSQLBlockEnd(tok);
if (end == nullptr)
syntaxError(nullptr);
std::string instruction = tok->stringifyList(end);
// delete all tokens until ';'
// delete all tokens until the embedded SQL block end
Token::eraseTokens(tok, end);
// insert "asm ( "instruction" ) ;"
@ -9886,3 +9886,25 @@ void Tokenizer::SimplifyNamelessRValueReferences()
}
}
}
const Token *Tokenizer::findSQLBlockEnd(const Token *tokSQLStart) const
{
const Token *tokSQLEnd = nullptr;
const Token *tokLastEnd = nullptr;
for (const Token *tok = tokSQLStart->tokAt(2); tok != nullptr && tokSQLEnd == nullptr; tok = tok->next()) {
if (tokLastEnd == nullptr && tok->str() == ";")
tokLastEnd = tok;
else if (tok->str() == "EXEC") {
if (Token::simpleMatch(tok->tokAt(-2), "END - EXEC ;"))
tokSQLEnd = tok->next();
else
tokSQLEnd = tokLastEnd;
break;
}
else if (Token::Match(tok, "{|}|==|&&|!|&|^|<<|>>|++|+=|-=|/=|*=|>>=|<<=|->|::|~"))
break; // We are obviously outside the SQL block
}
return tokSQLEnd ? tokSQLEnd : tokLastEnd;
}

View File

@ -718,6 +718,9 @@ private:
*/
void printUnknownTypes() const;
/** Find end of SQL (or PL/SQL) block */
const Token *findSQLBlockEnd(const Token *tokSQLStart) const;
public:
/** Was there templates in the code? */

View File

@ -5783,8 +5783,18 @@ private:
// Oracle PRO*C extensions for inline SQL. Just replace the SQL with "asm()" to fix wrong error messages
// ticket: #1959
ASSERT_EQUALS("asm ( \"\"EXEC SQL SELECT A FROM B\"\" ) ;", tokenizeAndStringify("EXEC SQL SELECT A FROM B;",false));
ASSERT_EQUALS("asm ( \"\"EXEC SQL\"\" ) ;", tokenizeAndStringify("EXEC SQL",false));
ASSERT_THROW(tokenizeAndStringify("EXEC SQL",false), InternalError);
ASSERT_EQUALS("asm ( \"\"EXEC SQL EXECUTE BEGIN Proc1 ( A ) ; END ; END - EXEC\"\" ) ; asm ( \"\"EXEC SQL COMMIT\"\" ) ;",
tokenizeAndStringify("EXEC SQL EXECUTE BEGIN Proc1(A); END; END-EXEC; EXEC SQL COMMIT;",false));
ASSERT_EQUALS("asm ( \"\"EXEC SQL UPDATE A SET B = C\"\" ) ; asm ( \"\"EXEC SQL COMMIT\"\" ) ;",
tokenizeAndStringify("EXEC SQL UPDATE A SET B = C; EXEC SQL COMMIT;",false));
ASSERT_EQUALS("asm ( \"\"EXEC SQL COMMIT\"\" ) ; asm ( \"\"EXEC SQL EXECUTE BEGIN Proc1 ( A ) ; END ; END - EXEC\"\" ) ;",
tokenizeAndStringify("EXEC SQL COMMIT; EXEC SQL EXECUTE BEGIN Proc1(A); END; END-EXEC;",false));
ASSERT_THROW(tokenizeAndStringify("int f(){ EXEC SQL } int a;",false), InternalError);
ASSERT_THROW(tokenizeAndStringify("EXEC SQL int f(){",false), InternalError);
ASSERT_THROW(tokenizeAndStringify("EXEC SQL END-EXEC int a;",false), InternalError);
}
void simplifyCAlternativeTokens() {