Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SQL: Skip PL/SQL selection directives and add sanity check for inquiry directive size #3654

Merged
merged 1 commit into from
Mar 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Units/parser-sql.r/sql-plsql-selection-directive.d/args.ctags
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
--sort=no
--extras=+q
--kinds-SQL=+d
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
demo_pkg input.sql /^create or replace package body demo_pkg is$/;" P
demo_pkg.test_func1 input.sql /^function test_func1 return INTEGER$/;" f package:demo_pkg
test_func1 input.sql /^function test_func1 return INTEGER$/;" f package:demo_pkg
demo_pkg.test_func2 input.sql /^function test_func2 return INTEGER$/;" f package:demo_pkg
test_func2 input.sql /^function test_func2 return INTEGER$/;" f package:demo_pkg
demo_pkg.test_func5 input.sql /^function test_func5 return INTEGER$/;" f package:demo_pkg
test_func5 input.sql /^function test_func5 return INTEGER$/;" f package:demo_pkg
42 changes: 42 additions & 0 deletions Units/parser-sql.r/sql-plsql-selection-directive.d/input.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
create or replace package body demo_pkg is

function test_func1 return INTEGER
as
begin
return 1;
end;

$IF $$MY_VERSION_CODE > 3 $THEN

function test_func2 return INTEGER
as
begin
return 3;
end;

$ELSIF $$MY_VERSION_CODE > 5 $THEN

function test_func3 return INTEGER
as
begin
return 5;
end;

$ELSE

function test_func4 return INTEGER
as
begin
return 7;
end;

$END

function test_func5 return INTEGER
as
begin
return 9;
end;

end demo_pkg;

72 changes: 65 additions & 7 deletions parsers/sql.c
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ enum eKeywordId {
KEYWORD_drop,
KEYWORD_else,
KEYWORD_elseif,
KEYWORD_elsif,
KEYWORD_end,
KEYWORD_endif,
KEYWORD_event,
Expand Down Expand Up @@ -177,7 +178,12 @@ typedef enum eTokenType {
TOKEN_CLOSE_SQUARE,
TOKEN_TILDE,
TOKEN_FORWARD_SLASH,
TOKEN_EQUAL
TOKEN_EQUAL,
TOKEN_PREPROC_IF,
TOKEN_PREPROC_ELSIF,
TOKEN_PREPROC_ELSE,
TOKEN_PREPROC_THEN,
TOKEN_PREPROC_END,
} tokenType;

typedef struct sTokenInfoSQL {
Expand Down Expand Up @@ -294,6 +300,7 @@ static const keywordTable SqlKeywordTable [] = {
{ "drop", KEYWORD_drop },
{ "else", KEYWORD_else },
{ "elseif", KEYWORD_elseif },
{ "elsif", KEYWORD_elsif },
Copy link
Member

@masatake masatake Mar 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add an entry for KEYWORD_elsif to

static struct SqlReservedWord SqlReservedWord [SQLKEYWORD_COUNT] = {
	/*
	 * RESERVED_BIT: MYSQL & POSTGRESQL&SQL2016&SQL2011&SQL92 & ORACLE11g&PLSQL & SQLANYWERE
	 *
	 * {  0  } means we have not inspect whether the keyword is reserved or not.
	 */
        ...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, thanks for noticing. I'll then squash the commits when you think there's not more work to do.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, please. Could you remove c3b0324 and 9f532d3?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

{ "end", KEYWORD_end },
{ "endif", KEYWORD_endif },
{ "event", KEYWORD_event },
Expand Down Expand Up @@ -431,6 +438,7 @@ static struct SqlReservedWord SqlReservedWord [SQLKEYWORD_COUNT] = {
[KEYWORD_drop] = {1 & 0&1&1&1 & 1&1 & 1},
[KEYWORD_else] = {1 & 1&1&1&1 & 1&1 & 1},
[KEYWORD_elseif] = {1 & 0&0&0&0 & 0&0 & 1},
[KEYWORD_elsif] = {0 & 0&0&0&0 & 0&1 & 0},
[KEYWORD_end] = {0 & 1&1&1&1 & 0&1 & 1},
[KEYWORD_endif] = {0 & 0&0&0&0 & 0&0 & 1},
[KEYWORD_event] = {0 & 0&0&0&0 & 0&0 & 0},
Expand Down Expand Up @@ -725,6 +733,10 @@ static bool isCCFlag(const char *str)
* The syntax for dollar-quoted string ca collide with PL/SQL inquiry directive ($$name).
* https://docs.oracle.com/en/database/oracle/oracle-database/18/lnpls/plsql-language-fundamentals.html#GUID-E918087C-D5A8-4CEE-841B-5333DE6D4C15
* https://github.com/universal-ctags/ctags/issues/3006

* In addition, it can also collide with variable checks in PL/SQL selection directives such as:
* $IF $$my_var > 1 $THEN ... $END
* https://docs.oracle.com/en/database/oracle/oracle-database/19/lnpls/plsql-language-fundamentals.html#GUID-78F2074C-C799-4CF9-9290-EB8473D0C8FB
*/
static tokenType parseDollarQuote (vString *const string, const int delimiter, int *promise)
{
Expand Down Expand Up @@ -752,8 +764,20 @@ static tokenType parseDollarQuote (vString *const string, const int delimiter, i

if (c != delimiter)
{
/* damn that's not valid, what can we do? */
/* not a dollar quote */
keywordId kw = lookupCaseKeyword (tag+1, Lang_sql);
ungetcToInputFile (c);

if (kw == KEYWORD_if)
return TOKEN_PREPROC_IF;
else if (kw == KEYWORD_elsif)
return TOKEN_PREPROC_ELSIF;
else if (kw == KEYWORD_else)
return TOKEN_PREPROC_ELSE;
else if (kw == KEYWORD_then)
return TOKEN_PREPROC_THEN;
else if (kw == KEYWORD_end)
return TOKEN_PREPROC_END;
return TOKEN_UNDEFINED;
}

Expand Down Expand Up @@ -828,7 +852,7 @@ static tokenType parseDollarQuote (vString *const string, const int delimiter, i
return TOKEN_STRING;
}

static void readToken (tokenInfo *const token)
static void readTokenFull (tokenInfo *const token, bool skippingPreproc)
{
int c;

Expand Down Expand Up @@ -952,10 +976,39 @@ static void readToken (tokenInfo *const token)
}

case '$':
token->type = parseDollarQuote (token->string, c, &token->promise);
token->lineNumber = getInputLineNumber ();
token->filePosition = getInputFilePosition ();
break;
{
tokenType t;
if (skippingPreproc)
{
int d = getcFromInputFile ();
if (d != '$')
ungetcToInputFile (d);
}
t = parseDollarQuote (token->string, c, &token->promise);
if (t == TOKEN_PREPROC_IF)
{
/* skip until $THEN and keep the content of this branch */
readTokenFull (token, true);
while (!isType (token, TOKEN_PREPROC_THEN) && !isType (token, TOKEN_EOF))
readTokenFull (token, true);
readTokenFull (token, false);
}
else if (!skippingPreproc && (t == TOKEN_PREPROC_ELSIF || t == TOKEN_PREPROC_ELSE))
{
/* skip until $END and drop $ELSIF and $ELSE branches */
readTokenFull (token, true);
while (!isType (token, TOKEN_PREPROC_END) && !isType (token, TOKEN_EOF))
readTokenFull (token, true);
readTokenFull (token, false);
}
else
{
token->type = t;
token->lineNumber = getInputLineNumber ();
token->filePosition = getInputFilePosition ();
}
break;
}

default:
if (! isIdentChar1 (c))
Expand All @@ -981,6 +1034,11 @@ static void readToken (tokenInfo *const token)
}
}

static void readToken (tokenInfo *const token)
{
readTokenFull (token, false);
}

/*
* reads an identifier, possibly quoted:
* identifier
Expand Down