From 02461753f3ac0f17284ca40068f8dfb5f80d271f Mon Sep 17 00:00:00 2001 From: Alexey Eryomenko <32419783+aeryomenko@users.noreply.github.com> Date: Fri, 3 Nov 2017 13:31:33 +0300 Subject: [PATCH] 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 --- lib/tokenize.cpp | 30 ++++++++++++++++++++++++++---- lib/tokenize.h | 3 +++ test/testtokenize.cpp | 12 +++++++++++- 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index bde722b91..4f0f991be 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -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; +} + diff --git a/lib/tokenize.h b/lib/tokenize.h index 10a15c55e..82bc4204d 100644 --- a/lib/tokenize.h +++ b/lib/tokenize.h @@ -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? */ diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index ad7133e61..ad98e151a 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -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() {