-
Notifications
You must be signed in to change notification settings - Fork 63
Add stored procedure calls with unnamed args #345
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
Changes from 1 commit
922d4a3
6c27840
d218a50
c6aea9d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -114,7 +114,8 @@ class SqlParser(private val ion: IonSystem) : Parser { | |
DROP_TABLE, | ||
DROP_INDEX, | ||
CREATE_INDEX, | ||
PARAMETER; | ||
PARAMETER, | ||
EXEC; | ||
|
||
val identifier = name.toLowerCase() | ||
} | ||
|
@@ -335,6 +336,10 @@ class SqlParser(private val ion: IonSystem) : Parser { | |
} | ||
} | ||
} | ||
EXEC -> { | ||
val procedureName = SymbolicName(token?.text!!.toLowerCase(), token.toSourceLocationMetaContainer()) | ||
Exec(procedureName, children.map { it.toExprNode() }, metas) | ||
} | ||
CALL_AGG -> { | ||
val funcExpr = | ||
VariableReference( | ||
|
@@ -1015,6 +1020,7 @@ class SqlParser(private val ion: IonSystem) : Parser { | |
tail.tail.parseFunctionCall(head!!) | ||
else -> err("Unexpected keyword", PARSE_UNEXPECTED_KEYWORD) | ||
} | ||
"exec" -> tail.parseExec() | ||
else -> err("Unexpected keyword", PARSE_UNEXPECTED_KEYWORD) | ||
} | ||
LEFT_PAREN -> { | ||
|
@@ -1712,6 +1718,28 @@ class SqlParser(private val ion: IonSystem) : Parser { | |
} | ||
} | ||
|
||
private fun List<Token>.parseExec(): ParseNode { | ||
var rem = this | ||
if (rem.head?.type == EOF) { | ||
rem.err("No stored procedure provided", PARSE_NO_STORED_PROCEDURE_PROVIDED) | ||
} | ||
|
||
val procedureName = rem.head | ||
rem = rem.tail | ||
|
||
// Stored procedure call has no args | ||
if (rem.head?.type == EOF) { | ||
return ParseNode(EXEC, procedureName, emptyList(), rem) | ||
} | ||
|
||
else if (rem.head?.type == LEFT_PAREN) { | ||
rem.err("Unexpected $LEFT_PAREN found following stored procedure call", PARSE_UNEXPECTED_TOKEN) | ||
} | ||
|
||
return rem.parseArgList(aliasSupportType = NONE, mode = NORMAL_ARG_LIST) | ||
.copy(type = EXEC, token = procedureName) | ||
} | ||
|
||
/** | ||
* Parses substring | ||
* | ||
|
@@ -2214,9 +2242,23 @@ class SqlParser(private val ion: IonSystem) : Parser { | |
return ParseNode(ARG_LIST, null, items, rem) | ||
} | ||
|
||
/** | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are other tokens that should only exist at top level right? Why is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The other tokens that should only exist at the top level (i.e. DML and DDL ops) weren't properly checked in the parser (created an issue #354 to add these checks in the future). I will rename the |
||
* Checks that the given [Token] list does not have `EXEC` calls outside of the top level query. Throws | ||
* an error if so. | ||
*/ | ||
private fun List<Token>.checkForUnexpectedExec() { | ||
for ((index, token) in this.withIndex()) { | ||
if (token.keywordText == "exec" && index != 0) { | ||
token.err("EXEC call found at unexpected location", PARSE_EXEC_AT_UNEXPECTED_LOCATION) | ||
} | ||
} | ||
} | ||
|
||
/** Entry point into the parser. */ | ||
override fun parseExprNode(source: String): ExprNode { | ||
val node = SqlLexer(ion).tokenize(source).parseExpression() | ||
val tokens = SqlLexer(ion).tokenize(source) | ||
tokens.checkForUnexpectedExec() | ||
val node = tokens.parseExpression() | ||
val rem = node.remaining | ||
if (!rem.onlyEndOfStatement()) { | ||
when(rem.head?.type ) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1546,4 +1546,87 @@ class ParserErrorsTest : TestBase() { | |
Property.TOKEN_TYPE to TokenType.EOF, | ||
Property.TOKEN_VALUE to ion.newSymbol("EOF"))) | ||
|
||
//**************************************** | ||
// EXEC clause parsing errors | ||
//**************************************** | ||
|
||
@Test | ||
fun execNoStoredProcedureProvided() = checkInputThrowingParserException( | ||
"EXEC", | ||
ErrorCode.PARSE_NO_STORED_PROCEDURE_PROVIDED, | ||
mapOf( | ||
Property.LINE_NUMBER to 1L, | ||
Property.COLUMN_NUMBER to 5L, | ||
Property.TOKEN_TYPE to TokenType.EOF, | ||
Property.TOKEN_VALUE to ion.newSymbol("EOF"))) | ||
|
||
@Test | ||
fun execCommaBetweenStoredProcedureAndArg() = checkInputThrowingParserException( | ||
"EXEC foo, arg0, arg1", | ||
ErrorCode.PARSE_UNEXPECTED_TERM, | ||
mapOf( | ||
Property.LINE_NUMBER to 1L, | ||
Property.COLUMN_NUMBER to 9L, | ||
Property.TOKEN_TYPE to TokenType.COMMA, | ||
Property.TOKEN_VALUE to ion.newSymbol(","))) | ||
|
||
@Test | ||
fun execArgTrailingComma() = checkInputThrowingParserException( | ||
"EXEC foo arg0, arg1,", | ||
ErrorCode.PARSE_UNEXPECTED_TERM, | ||
mapOf( | ||
Property.LINE_NUMBER to 1L, | ||
Property.COLUMN_NUMBER to 21L, | ||
Property.TOKEN_TYPE to TokenType.EOF, | ||
Property.TOKEN_VALUE to ion.newSymbol("EOF"))) | ||
|
||
@Test | ||
fun execUnexpectedParen() = checkInputThrowingParserException( | ||
"EXEC foo()", | ||
ErrorCode.PARSE_UNEXPECTED_TOKEN, | ||
mapOf( | ||
Property.LINE_NUMBER to 1L, | ||
Property.COLUMN_NUMBER to 9L, | ||
Property.TOKEN_TYPE to TokenType.LEFT_PAREN, | ||
Property.TOKEN_VALUE to ion.newSymbol("("))) | ||
|
||
@Test | ||
fun execUnexpectedParenWithArgs() = checkInputThrowingParserException( | ||
"EXEC foo(arg0, arg1)", | ||
ErrorCode.PARSE_UNEXPECTED_TOKEN, | ||
mapOf( | ||
Property.LINE_NUMBER to 1L, | ||
Property.COLUMN_NUMBER to 9L, | ||
Property.TOKEN_TYPE to TokenType.LEFT_PAREN, | ||
Property.TOKEN_VALUE to ion.newSymbol("("))) | ||
|
||
@Test | ||
fun execAtUnexpectedLocation() = checkInputThrowingParserException( | ||
"EXEC EXEC", | ||
ErrorCode.PARSE_EXEC_AT_UNEXPECTED_LOCATION, | ||
mapOf( | ||
Property.LINE_NUMBER to 1L, | ||
Property.COLUMN_NUMBER to 6L, | ||
Property.TOKEN_TYPE to TokenType.KEYWORD, | ||
Property.TOKEN_VALUE to ion.newSymbol("exec"))) | ||
|
||
@Test | ||
fun execAtUnexpectedLocationAfterExec() = checkInputThrowingParserException( | ||
"EXEC foo EXEC", | ||
ErrorCode.PARSE_EXEC_AT_UNEXPECTED_LOCATION, | ||
mapOf( | ||
Property.LINE_NUMBER to 1L, | ||
Property.COLUMN_NUMBER to 10L, | ||
Property.TOKEN_TYPE to TokenType.KEYWORD, | ||
Property.TOKEN_VALUE to ion.newSymbol("exec"))) | ||
|
||
@Test | ||
fun execAtUnexpectedLocationInExpression() = checkInputThrowingParserException( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What will happen for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Currently for both of those queries, the |
||
"SELECT * FROM (EXEC undrop 'foo')", | ||
ErrorCode.PARSE_EXEC_AT_UNEXPECTED_LOCATION, | ||
mapOf( | ||
Property.LINE_NUMBER to 1L, | ||
Property.COLUMN_NUMBER to 16L, | ||
Property.TOKEN_TYPE to TokenType.KEYWORD, | ||
Property.TOKEN_VALUE to ion.newSymbol("exec"))) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
procedureName.meta
is not kept inPartiqlAst.Statement
? Not to say it's important but just want to confirm.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That particular
meta
may not be too important, but we should be copying metas wherever possible. I will add that in the next commit.I noticed there are quite a few other metas that aren't copied when converting
ExprNode
<->PartiqlAst
, so I created an issue (#353) to track.