Skip to content

Fix parsing of TRIM specification keywords (BOTH, LEADING, and TRAILING) #326

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

Merged
merged 3 commits into from
Dec 8, 2020
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 @@ -101,7 +101,7 @@ internal class TrimExprFunction(valueFactory: ExprValueFactory) : NullPropagatin
1 -> Triple(DEFAULT_SPECIFICATION, DEFAULT_TO_REMOVE, args[0].codePoints())
2 -> {

if(args[0].type != ExprValueType.STRING){
if(!args[0].type.isText){
errNoContext("with two arguments trim's first argument must be either the " +
"specification or a 'to remove' string",
internal = false)
Expand Down
3 changes: 2 additions & 1 deletion lang/src/org/partiql/lang/syntax/LexerConstants.kt
Original file line number Diff line number Diff line change
Expand Up @@ -245,9 +245,10 @@ internal val DATE_PART_KEYWORDS: Set<String> = DatePart.values()
"work",
"write",
"zone"
).union(TRIM_SPECIFICATION_KEYWORDS)
)
// Note: DATE_PART_KEYWORDs are not keywords in the traditional sense--they are only keywords within
// the context of the DATE_ADD, DATE_DIFF and EXTRACT functions, for which [SqlParser] has special support.
// Similarly, TRIM_SPECIFICATION_KEYWORDS are only keywords within the context of the TRIM function.

/** PartiQL additional keywords. */
@JvmField internal val SQLPP_KEYWORDS = setOf(
Expand Down
5 changes: 0 additions & 5 deletions lang/src/org/partiql/lang/syntax/SqlLexer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -515,11 +515,6 @@ class SqlLexer(private val ion: IonSystem) : Lexer {
tokenType = FOR
ion.newSymbol(lower)
}
lower in TRIM_SPECIFICATION_KEYWORDS -> {
// used to determine the type of trim
tokenType = TRIM_SPECIFICATION
ion.newString(lower)
}
lower in BOOLEAN_KEYWORDS -> {
// literal boolean
tokenType = LITERAL
Expand Down
8 changes: 5 additions & 3 deletions lang/src/org/partiql/lang/syntax/SqlParser.kt
Original file line number Diff line number Diff line change
Expand Up @@ -1779,9 +1779,11 @@ class SqlParser(private val ion: IonSystem) : Parser {
return node.remaining
}

val hasSpecification = when(rem.head?.type) {
TRIM_SPECIFICATION -> {
rem = parseArgument()
val maybeTrimSpec = rem.head
val hasSpecification = when {
maybeTrimSpec?.type == IDENTIFIER && TRIM_SPECIFICATION_KEYWORDS.contains(maybeTrimSpec.text?.toLowerCase()) -> {
arguments.add(ParseNode(ATOM, maybeTrimSpec.copy(type = TRIM_SPECIFICATION), listOf(), rem.tail))
rem = rem.tail

true
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,4 +198,48 @@ class EvaluatingCompilerExceptionsTest : EvaluatorTestBase() {
"""SELECT ? FROM <<1>>""",
ErrorCode.EVALUATOR_UNBOUND_PARAMETER,
sourceLocationProperties(1, 8) + mapOf(Property.EXPECTED_PARAMETER_ORDINAL to 1, Property.BOUND_PARAMETER_COUNT to 0))

@Test
fun trimSpecKeywordBothNotUsedInTrim() =
checkInputThrowingEvaluationException(
"SELECT 1 FROM both",
ErrorCode.EVALUATOR_BINDING_DOES_NOT_EXIST,
mapOf(
Property.LINE_NUMBER to 1L,
Property.COLUMN_NUMBER to 15L,
Property.BINDING_NAME to "both")
)

@Test
fun trimSpecKeywordLeadingNotUsedInTrim() =
checkInputThrowingEvaluationException(
"SELECT 1 FROM leading",
ErrorCode.EVALUATOR_BINDING_DOES_NOT_EXIST,
mapOf(
Property.LINE_NUMBER to 1L,
Property.COLUMN_NUMBER to 15L,
Property.BINDING_NAME to "leading")
)

@Test
fun trimSpecKeywordTrailingNotUsedInTrim() =
checkInputThrowingEvaluationException(
"SELECT 1 FROM trailing",
ErrorCode.EVALUATOR_BINDING_DOES_NOT_EXIST,
mapOf(
Property.LINE_NUMBER to 1L,
Property.COLUMN_NUMBER to 15L,
Property.BINDING_NAME to "trailing")
)

@Test
fun trimSpecKeywordLeadingUsedAsSecondArgInTrim() =
checkInputThrowingEvaluationException(
"trim(both leading from 'foo')",
ErrorCode.EVALUATOR_BINDING_DOES_NOT_EXIST,
mapOf(
Property.LINE_NUMBER to 1L,
Property.COLUMN_NUMBER to 11L,
Property.BINDING_NAME to "leading")
)
}
6 changes: 3 additions & 3 deletions lang/test/org/partiql/lang/syntax/SqlParserTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -252,17 +252,17 @@ class SqlParserTest : SqlParserTestBase() {
@Test
fun callTrimTwoArgumentsUsingBoth() = assertExpression(
"trim(both from 'test')",
"(call trim (lit \"both\") (lit \"test\"))")
"(call trim (lit both) (lit \"test\"))")

@Test
fun callTrimTwoArgumentsUsingLeading() = assertExpression(
"trim(leading from 'test')",
"(call trim (lit \"leading\") (lit \"test\"))")
"(call trim (lit leading) (lit \"test\"))")

@Test
fun callTrimTwoArgumentsUsingTrailing() = assertExpression(
"trim(trailing from 'test')",
"(call trim (lit \"trailing\") (lit \"test\"))")
"(call trim (lit trailing) (lit \"test\"))")

//****************************************
// Unary operators
Expand Down