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

Add to non-reserved keywords; rework functionName and symbolPrimitive parse rules #1457

Merged
merged 3 commits into from
May 9, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -198,19 +198,29 @@ internal class PartiQLPigVisitor(

override fun visitByIdent(ctx: PartiQLParser.ByIdentContext) = visitSymbolPrimitive(ctx.symbolPrimitive())

override fun visitSymbolPrimitive(ctx: PartiQLParser.SymbolPrimitiveContext) = PartiqlAst.build {
val metas = ctx.ident.getSourceMetaContainer()
when (ctx.ident.type) {
PartiQLParser.IDENTIFIER_QUOTED -> id(
ctx.IDENTIFIER_QUOTED().getStringValue(),
caseSensitive(),
unqualified(),
metas
)

PartiQLParser.IDENTIFIER -> id(ctx.IDENTIFIER().getStringValue(), caseInsensitive(), unqualified(), metas)
private fun visitSymbolPrimitive(ctx: PartiQLParser.SymbolPrimitiveContext): PartiqlAst.Expr.Id =
when (ctx) {
is PartiQLParser.IdentifierQuotedContext -> visitIdentifierQuoted(ctx)
is PartiQLParser.IdentifierUnquotedContext -> visitIdentifierUnquoted(ctx)
else -> throw ParserException("Invalid symbol reference.", ErrorCode.PARSE_INVALID_QUERY)
}

override fun visitIdentifierQuoted(ctx: PartiQLParser.IdentifierQuotedContext): PartiqlAst.Expr.Id = PartiqlAst.build {
id(
ctx.IDENTIFIER_QUOTED().getStringValue(),
caseSensitive(),
unqualified(),
ctx.IDENTIFIER_QUOTED().getSourceMetaContainer()
)
}

override fun visitIdentifierUnquoted(ctx: PartiQLParser.IdentifierUnquotedContext): PartiqlAst.Expr.Id = PartiqlAst.build {
id(
ctx.text,
caseInsensitive(),
unqualified(),
ctx.IDENTIFIER().getSourceMetaContainer()
)
}

/**
Expand Down Expand Up @@ -660,9 +670,9 @@ internal class PartiQLPigVisitor(

override fun visitExcludeExprTupleAttr(ctx: PartiQLParser.ExcludeExprTupleAttrContext) = PartiqlAst.build {
val attr = ctx.symbolPrimitive().getString()
val caseSensitivity = when (ctx.symbolPrimitive().ident.type) {
PartiQLParser.IDENTIFIER_QUOTED -> caseSensitive()
PartiQLParser.IDENTIFIER -> caseInsensitive()
val caseSensitivity = when (ctx.symbolPrimitive()) {
is PartiQLParser.IdentifierQuotedContext -> caseSensitive()
is PartiQLParser.IdentifierUnquotedContext -> caseInsensitive()
else -> throw ParserException("Invalid symbol reference.", ErrorCode.PARSE_INVALID_QUERY)
}
excludeTupleAttr(identifier(attr, caseSensitivity))
Expand Down Expand Up @@ -1192,7 +1202,7 @@ internal class PartiQLPigVisitor(

override fun visitVariableKeyword(ctx: PartiQLParser.VariableKeywordContext): PartiqlAst.PartiqlAstNode =
PartiqlAst.build {
val keyword = ctx.nonReservedKeywords().start.text
val keyword = ctx.nonReserved().start.text
val metas = ctx.start.getSourceMetaContainer()
val qualifier = ctx.qualifier?.let { localsFirst() } ?: unqualified()
id(keyword, caseInsensitive(), qualifier, metas)
Expand Down Expand Up @@ -1316,16 +1326,11 @@ internal class PartiQLPigVisitor(
}

override fun visitFunctionCall(ctx: PartiQLParser.FunctionCallContext) = PartiqlAst.build {
val name = when (val nameCtx = ctx.functionName()) {
is PartiQLParser.FunctionNameReservedContext -> {
if (nameCtx.qualifier.isNotEmpty()) error("Legacy AST does not support qualified function names")
nameCtx.name.text.lowercase()
}
is PartiQLParser.FunctionNameSymbolContext -> {
if (nameCtx.qualifier.isNotEmpty()) error("Legacy AST does not support qualified function names")
nameCtx.name.getString().lowercase()
}
else -> error("Expected context FunctionNameReserved or FunctionNameSymbol")
val nameCtx = ctx.qualifiedName()
val name = if (nameCtx.qualifier.isNotEmpty()) {
error("Legacy AST does not support qualified function names")
} else {
nameCtx.name.getString().lowercase()
}
val args = ctx.expr().map { visitExpr(it) }
val metas = ctx.start.getSourceMetaContainer()
Expand Down Expand Up @@ -1819,9 +1824,9 @@ internal class PartiQLPigVisitor(
}
}

private fun PartiQLParser.SymbolPrimitiveContext.getSourceMetaContainer() = when (this.ident.type) {
PartiQLParser.IDENTIFIER -> this.IDENTIFIER().getSourceMetaContainer()
PartiQLParser.IDENTIFIER_QUOTED -> this.IDENTIFIER_QUOTED().getSourceMetaContainer()
private fun PartiQLParser.SymbolPrimitiveContext.getSourceMetaContainer() = when (this) {
is PartiQLParser.IdentifierQuotedContext -> this.IDENTIFIER_QUOTED().getSourceMetaContainer()
is PartiQLParser.IdentifierUnquotedContext -> this.start.getSourceMetaContainer()
else -> throw ParserException(
"Unable to get identifier's source meta-container.",
ErrorCode.PARSE_INVALID_QUERY
Expand Down Expand Up @@ -2125,25 +2130,23 @@ internal class PartiQLPigVisitor(
}

private fun PartiQLParser.SymbolPrimitiveContext.getString(): String {
return when {
this.IDENTIFIER_QUOTED() != null -> this.IDENTIFIER_QUOTED().getStringValue()
this.IDENTIFIER() != null -> this.IDENTIFIER().text
return when (this) {
is PartiQLParser.IdentifierQuotedContext -> this.IDENTIFIER_QUOTED().getStringValue()
is PartiQLParser.IdentifierUnquotedContext -> this.text
else -> throw ParserException("Unable to get symbol's text.", ErrorCode.PARSE_INVALID_QUERY)
}
}

private fun getSymbolPathExpr(ctx: PartiQLParser.SymbolPrimitiveContext) = PartiqlAst.build {
when {
ctx.IDENTIFIER_QUOTED() != null -> pathExpr(
when (ctx) {
is PartiQLParser.IdentifierQuotedContext -> pathExpr(
lit(ionString(ctx.IDENTIFIER_QUOTED().getStringValue())), caseSensitive(),
metas = ctx.IDENTIFIER_QUOTED().getSourceMetaContainer()
metas = ctx.getSourceMetaContainer()
)

ctx.IDENTIFIER() != null -> pathExpr(
lit(ionString(ctx.IDENTIFIER().text)), caseInsensitive(),
metas = ctx.IDENTIFIER().getSourceMetaContainer()
is PartiQLParser.IdentifierUnquotedContext -> pathExpr(
lit(ionString(ctx.text)), caseInsensitive(),
metas = ctx.getSourceMetaContainer()
)

else -> throw ParserException("Unable to get symbol's text.", ErrorCode.PARSE_INVALID_QUERY)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package org.partiql.lang.errors

import com.amazon.ion.Timestamp
import org.junit.Ignore
import org.junit.Test
import org.partiql.errors.ErrorCode
import org.partiql.errors.Property
Expand Down Expand Up @@ -655,6 +656,7 @@ class ParserErrorsTest : PartiQLParserTestBase() {
}

@Test
@Ignore("No longer a parser error; evaluation error")
fun callTrimNoArgs() {
checkInputThrowingParserException(
"trim()",
Expand Down Expand Up @@ -683,6 +685,7 @@ class ParserErrorsTest : PartiQLParserTestBase() {
}

@Test
@Ignore("No longer a parser error; evaluation error")
fun callTrimZeroArguments() {
checkInputThrowingParserException(
"trim()",
Expand Down Expand Up @@ -795,6 +798,7 @@ class ParserErrorsTest : PartiQLParserTestBase() {
}

@Test
@Ignore("No longer a parser error; evaluation error")
fun aggregateWithNoArgs() {
checkInputThrowingParserException(
"SUM()",
Expand All @@ -809,6 +813,7 @@ class ParserErrorsTest : PartiQLParserTestBase() {
}

@Test
@Ignore("No longer a parser error; evaluation error")
fun aggregateWithTooManyArgs() {
checkInputThrowingParserException(
"SUM(a, b)",
Expand Down Expand Up @@ -1672,6 +1677,7 @@ class ParserErrorsTest : PartiQLParserTestBase() {
}

@Test
@Ignore("No longer a parser error; evaluation error")
fun callExtractMissingFromWithComma() {
checkInputThrowingParserException(
"extract(year, b)",
Expand Down Expand Up @@ -1717,6 +1723,7 @@ class ParserErrorsTest : PartiQLParserTestBase() {
}

@Test
@Ignore("No longer a parser error; evaluation error")
fun callExtractOnlySecondArgument() {
checkInputThrowingParserException(
"extract(b)",
Expand Down Expand Up @@ -1746,6 +1753,7 @@ class ParserErrorsTest : PartiQLParserTestBase() {
}

@Test
@Ignore("No longer a parser error; evaluation error")
fun callExtractOnlyDateTimePart() {
checkInputThrowingParserException(
"extract(year)",
Expand Down
65 changes: 51 additions & 14 deletions partiql-parser/src/main/antlr/PartiQL.g4
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ byIdent
: BY symbolPrimitive;

symbolPrimitive
: ident=( IDENTIFIER | IDENTIFIER_QUOTED )
: IDENTIFIER # IdentifierUnquoted
| IDENTIFIER_QUOTED # IdentifierQuoted
| nonReserved # IdentifierUnquoted
;

/**
Expand Down Expand Up @@ -653,8 +655,8 @@ exprPrimary
| dateFunction # ExprPrimaryBase
| aggregate # ExprPrimaryBase
| trimFunction # ExprPrimaryBase
| functionCall # ExprPrimaryBase
| nullIf # ExprPrimaryBase
| functionCall # ExprPrimaryBase
| exprPrimary pathStep+ # ExprPrimaryPath
| exprGraphMatchMany # ExprPrimaryBase
| caseExpr # ExprPrimaryBase
Expand Down Expand Up @@ -761,13 +763,7 @@ dateFunction

// SQL-99 10.4 — <routine invocation> ::= <routine name> <SQL argument list>
functionCall
: functionName PAREN_LEFT ( expr ( COMMA expr )* )? PAREN_RIGHT
;

// SQL-99 10.4 — <routine name> ::= [ <schema name> <period> ] <qualified identifier>
functionName
: (qualifier+=symbolPrimitive PERIOD)* name=( CHAR_LENGTH | CHARACTER_LENGTH | OCTET_LENGTH | BIT_LENGTH | UPPER | LOWER | SIZE | EXISTS | COUNT | MOD ) # FunctionNameReserved
Copy link
Member Author

Choose a reason for hiding this comment

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

(self-review) -- this is the rule we were aiming to remove from the ANTLR grammar in this PR

| (qualifier+=symbolPrimitive PERIOD)* name=symbolPrimitive # FunctionNameSymbol
: qualifiedName PAREN_LEFT ( expr ( COMMA expr )* )? PAREN_RIGHT
;

pathStep
Expand All @@ -789,11 +785,52 @@ parameter

varRefExpr
: qualifier=AT_SIGN? ident=(IDENTIFIER|IDENTIFIER_QUOTED) # VariableIdentifier
| qualifier=AT_SIGN? key=nonReservedKeywords # VariableKeyword
;

nonReservedKeywords
: EXCLUDED
| qualifier=AT_SIGN? key=nonReserved # VariableKeyword
;

nonReserved
Copy link
Member Author

Choose a reason for hiding this comment

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

(self-review) -- the non-reserved words were taken directly from the SQL-99 grammar. At the end are some, PartiQL specific-keywords marked as non-reserved.

If you'll notice, postgresql along with other SQL dialects use their own set of non-reserved keywords that diverges from the SQL standards. Also between different SQL standards, there are some changes to the reserved vs non-reserved keywords. We choose to follow SQL-99 as we do in other places in the project.

: /* From SQL99 <non-reserved word> https://ronsavage.github.io/SQL/sql-99.bnf.html#non-reserved%20word */
ABS | ADA | ADMIN | ASENSITIVE | ASSIGNMENT | ASYMMETRIC | ATOMIC
| ATTRIBUTE | AVG
| BIT_LENGTH
| C | CALLED | CARDINALITY | CATALOG_NAME | CHAIN | CHAR_LENGTH
| CHARACTERISTICS | CHARACTER_LENGTH | CHARACTER_SET_CATALOG
| CHARACTER_SET_NAME | CHARACTER_SET_SCHEMA | CHECKED | CLASS_ORIGIN
| COALESCE | COBOL | COLLATION_CATALOG | COLLATION_NAME | COLLATION_SCHEMA
| COLUMN_NAME | COMMAND_FUNCTION | COMMAND_FUNCTION_CODE | COMMITTED
| CONDITION_IDENTIFIER | CONDITION_NUMBER | CONNECTION_NAME
| CONSTRAINT_CATALOG | CONSTRAINT_NAME | CONSTRAINT_SCHEMA | CONTAINS
| CONVERT | COUNT | CURSOR_NAME
| DATETIME_INTERVAL_CODE | DATETIME_INTERVAL_PRECISION | DEFINED
| DEFINER | DEGREE | DERIVED | DISPATCH
| EVERY | EXTRACT
| FINAL | FORTRAN
| G | GENERATED | GRANTED
| HIERARCHY
| IMPLEMENTATION | INSENSITIVE | INSTANCE | INSTANTIABLE | INVOKER
| K | KEY_MEMBER | KEY_TYPE
| LENGTH | LOWER
| M | MAX | MIN | MESSAGE_LENGTH | MESSAGE_OCTET_LENGTH | MESSAGE_TEXT
| MOD | MORE | MUMPS
| NAME | NULLABLE | NUMBER | NULLIF
| OCTET_LENGTH | ORDERING | OPTIONS | OVERLAY | OVERRIDING
| PASCAL | PARAMETER_MODE | PARAMETER_NAME
| PARAMETER_ORDINAL_POSITION | PARAMETER_SPECIFIC_CATALOG
| PARAMETER_SPECIFIC_NAME | PARAMETER_SPECIFIC_SCHEMA | PLI | POSITION
| REPEATABLE | RETURNED_CARDINALITY | RETURNED_LENGTH
| RETURNED_OCTET_LENGTH | RETURNED_SQLSTATE | ROUTINE_CATALOG
| ROUTINE_NAME | ROUTINE_SCHEMA | ROW_COUNT
| SCALE | SCHEMA_NAME | SCOPE | SECURITY | SELF | SENSITIVE | SERIALIZABLE
| SERVER_NAME | SIMPLE | SOURCE | SPECIFIC_NAME | STATEMENT | STRUCTURE
| STYLE | SUBCLASS_ORIGIN | SUBSTRING | SUM | SYMMETRIC | SYSTEM
| TABLE_NAME | TOP_LEVEL_COUNT | TRANSACTIONS_COMMITTED
| TRANSACTIONS_ROLLED_BACK | TRANSACTION_ACTIVE | TRANSFORM
| TRANSFORMS | TRANSLATE | TRIGGER_CATALOG | TRIGGER_SCHEMA
| TRIGGER_NAME | TRIM | TYPE
| UNCOMMITTED | UNNAMED | UPPER
/* PartiQL */
| EXCLUDED | EXISTS
| SIZE
;

/**
Expand Down
Loading
Loading