Skip to content

Commit 1e1a71f

Browse files
authored
Adds qualifiers to function call expressions (#1337)
1 parent dae1ab3 commit 1e1a71f

File tree

8 files changed

+226
-52
lines changed

8 files changed

+226
-52
lines changed

partiql-lang/src/main/kotlin/org/partiql/lang/syntax/impl/PartiQLPigVisitor.kt

Lines changed: 51 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ import com.amazon.ionelement.api.loadSingleElement
3636
import org.antlr.v4.runtime.ParserRuleContext
3737
import org.antlr.v4.runtime.Token
3838
import org.antlr.v4.runtime.tree.TerminalNode
39-
import org.partiql.ast.Identifier
4039
import org.partiql.errors.ErrorCode
4140
import org.partiql.errors.Property
4241
import org.partiql.errors.PropertyValueMap
@@ -118,7 +117,7 @@ import java.time.format.DateTimeParseException
118117
*/
119118
internal class PartiQLPigVisitor(
120119
val customTypes: List<CustomType> = listOf(),
121-
private val parameterIndexes: Map<Int, Int> = mapOf()
120+
private val parameterIndexes: Map<Int, Int> = mapOf(),
122121
) :
123122
PartiQLBaseVisitor<PartiqlAst.PartiqlAstNode>() {
124123

@@ -648,19 +647,22 @@ internal class PartiQLPigVisitor(
648647
excludeTupleAttr(identifier(attr, caseSensitivity))
649648
}
650649

651-
override fun visitExcludeExprCollectionIndex(ctx: PartiQLParser.ExcludeExprCollectionIndexContext) = PartiqlAst.build {
652-
val index = ctx.index.text.toInteger().toLong()
653-
excludeCollectionIndex(index)
654-
}
650+
override fun visitExcludeExprCollectionIndex(ctx: PartiQLParser.ExcludeExprCollectionIndexContext) =
651+
PartiqlAst.build {
652+
val index = ctx.index.text.toInteger().toLong()
653+
excludeCollectionIndex(index)
654+
}
655655

656-
override fun visitExcludeExprCollectionAttr(ctx: PartiQLParser.ExcludeExprCollectionAttrContext) = PartiqlAst.build {
657-
val attr = ctx.attr.getStringValue()
658-
excludeTupleAttr(identifier(attr, caseSensitive()))
659-
}
656+
override fun visitExcludeExprCollectionAttr(ctx: PartiQLParser.ExcludeExprCollectionAttrContext) =
657+
PartiqlAst.build {
658+
val attr = ctx.attr.getStringValue()
659+
excludeTupleAttr(identifier(attr, caseSensitive()))
660+
}
660661

661-
override fun visitExcludeExprCollectionWildcard(ctx: PartiQLParser.ExcludeExprCollectionWildcardContext) = PartiqlAst.build {
662-
excludeCollectionWildcard()
663-
}
662+
override fun visitExcludeExprCollectionWildcard(ctx: PartiQLParser.ExcludeExprCollectionWildcardContext) =
663+
PartiqlAst.build {
664+
excludeCollectionWildcard()
665+
}
664666

665667
override fun visitExcludeExprTupleWildcard(ctx: PartiQLParser.ExcludeExprTupleWildcardContext) = PartiqlAst.build {
666668
excludeTupleWildcard()
@@ -1292,17 +1294,20 @@ internal class PartiQLPigVisitor(
12921294
canLosslessCast(expr, type, metas)
12931295
}
12941296

1295-
override fun visitFunctionCallIdent(ctx: PartiQLParser.FunctionCallIdentContext) = PartiqlAst.build {
1296-
val name = ctx.name.getString().lowercase()
1297-
val args = ctx.expr().map { visitExpr(it) }
1298-
val metas = ctx.name.getSourceMetaContainer()
1299-
call(name, args = args, metas = metas)
1300-
}
1301-
1302-
override fun visitFunctionCallReserved(ctx: PartiQLParser.FunctionCallReservedContext) = PartiqlAst.build {
1303-
val name = ctx.name.text.lowercase()
1297+
override fun visitFunctionCall(ctx: PartiQLParser.FunctionCallContext) = PartiqlAst.build {
1298+
val name = when (val nameCtx = ctx.functionName()) {
1299+
is PartiQLParser.FunctionNameReservedContext -> {
1300+
if (nameCtx.qualifier.isNotEmpty()) error("Legacy AST does not support qualified function names")
1301+
nameCtx.name.text.lowercase()
1302+
}
1303+
is PartiQLParser.FunctionNameSymbolContext -> {
1304+
if (nameCtx.qualifier.isNotEmpty()) error("Legacy AST does not support qualified function names")
1305+
nameCtx.name.getString().lowercase()
1306+
}
1307+
else -> error("Expected context FunctionNameReserved or FunctionNameSymbol")
1308+
}
13041309
val args = ctx.expr().map { visitExpr(it) }
1305-
val metas = ctx.name.getSourceMetaContainer()
1310+
val metas = ctx.start.getSourceMetaContainer()
13061311
call(name, args = args, metas = metas)
13071312
}
13081313

@@ -1693,7 +1698,7 @@ internal class PartiQLPigVisitor(
16931698
lhs: ParserRuleContext?,
16941699
rhs: ParserRuleContext?,
16951700
op: Token?,
1696-
parent: ParserRuleContext? = null
1701+
parent: ParserRuleContext? = null,
16971702
) = PartiqlAst.build {
16981703
if (parent != null) return@build visit(parent) as PartiqlAst.Expr
16991704
val args = listOf(lhs!!, rhs!!).map { visit(it) as PartiqlAst.Expr }
@@ -1847,7 +1852,7 @@ internal class PartiQLPigVisitor(
18471852
withTimeZone: Boolean,
18481853
precision: Long,
18491854
stringNode: TerminalNode,
1850-
timeNode: TerminalNode
1855+
timeNode: TerminalNode,
18511856
) = PartiqlAst.build {
18521857
val time: LocalTime
18531858
val formatter = when (withTimeZone) {
@@ -1871,7 +1876,7 @@ internal class PartiQLPigVisitor(
18711876

18721877
private fun getTimestampStringAndPrecision(
18731878
stringNode: TerminalNode,
1874-
integerNode: TerminalNode?
1879+
integerNode: TerminalNode?,
18751880
): Pair<String, Long?> {
18761881
val timestampString = stringNode.getStringValue()
18771882
val precision = when (integerNode) {
@@ -1890,7 +1895,7 @@ internal class PartiQLPigVisitor(
18901895
private fun getTimestampDynamic(
18911896
timestampString: String,
18921897
precision: Long?,
1893-
node: TerminalNode
1898+
node: TerminalNode,
18941899
) = PartiqlAst.build {
18951900
val timestamp =
18961901
try {
@@ -1901,17 +1906,22 @@ internal class PartiQLPigVisitor(
19011906
val timeZone = timestamp.timeZone?.let { getTimeZone(it) }
19021907
timestamp(
19031908
timestampValue(
1904-
timestamp.year.toLong(), timestamp.month.toLong(), timestamp.day.toLong(),
1905-
timestamp.hour.toLong(), timestamp.minute.toLong(), ionDecimal(Decimal.valueOf(timestamp.decimalSecond)),
1906-
timeZone, precision
1909+
timestamp.year.toLong(),
1910+
timestamp.month.toLong(),
1911+
timestamp.day.toLong(),
1912+
timestamp.hour.toLong(),
1913+
timestamp.minute.toLong(),
1914+
ionDecimal(Decimal.valueOf(timestamp.decimalSecond)),
1915+
timeZone,
1916+
precision
19071917
)
19081918
)
19091919
}
19101920

19111921
private fun getTimestampWithTimezone(
19121922
timestampString: String,
19131923
precision: Long?,
1914-
node: TerminalNode
1924+
node: TerminalNode,
19151925
) = PartiqlAst.build {
19161926
val timestamp = try {
19171927
DateTimeUtils.parseTimestamp(timestampString)
@@ -1926,9 +1936,14 @@ internal class PartiQLPigVisitor(
19261936
val timeZone = timestamp.timeZone?.let { getTimeZone(it) }
19271937
timestamp(
19281938
timestampValue(
1929-
timestamp.year.toLong(), timestamp.month.toLong(), timestamp.day.toLong(),
1930-
timestamp.hour.toLong(), timestamp.minute.toLong(), ionDecimal(Decimal.valueOf(timestamp.decimalSecond)),
1931-
timeZone, precision
1939+
timestamp.year.toLong(),
1940+
timestamp.month.toLong(),
1941+
timestamp.day.toLong(),
1942+
timestamp.hour.toLong(),
1943+
timestamp.minute.toLong(),
1944+
ionDecimal(Decimal.valueOf(timestamp.decimalSecond)),
1945+
timeZone,
1946+
precision
19321947
)
19331948
)
19341949
}
@@ -2124,13 +2139,13 @@ internal class PartiQLPigVisitor(
21242139
msg: String,
21252140
code: ErrorCode,
21262141
ctx: PropertyValueMap = PropertyValueMap(),
2127-
cause: Throwable? = null
2142+
cause: Throwable? = null,
21282143
) = this.error(msg, code, ctx, cause)
21292144

21302145
private fun Token?.err(
21312146
msg: String,
21322147
code: ErrorCode,
21332148
ctx: PropertyValueMap = PropertyValueMap(),
2134-
cause: Throwable? = null
2149+
cause: Throwable? = null,
21352150
) = this.error(msg, code, ctx, cause)
21362151
}

partiql-parser/src/main/antlr/PartiQL.g4

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -709,11 +709,15 @@ trimFunction
709709
dateFunction
710710
: func=(DATE_ADD|DATE_DIFF) PAREN_LEFT dt=IDENTIFIER COMMA expr COMMA expr PAREN_RIGHT;
711711

712+
// SQL-99 10.4 — <routine invocation> ::= <routine name> <SQL argument list>
712713
functionCall
713-
: name=( CHAR_LENGTH | CHARACTER_LENGTH | OCTET_LENGTH |
714-
BIT_LENGTH | UPPER | LOWER | SIZE | EXISTS | COUNT )
715-
PAREN_LEFT ( expr ( COMMA expr )* )? PAREN_RIGHT # FunctionCallReserved
716-
| name=symbolPrimitive PAREN_LEFT ( expr ( COMMA expr )* )? PAREN_RIGHT # FunctionCallIdent
714+
: functionName PAREN_LEFT ( expr ( COMMA expr )* )? PAREN_RIGHT
715+
;
716+
717+
// SQL-99 10.4 — <routine name> ::= [ <schema name> <period> ] <qualified identifier>
718+
functionName
719+
: (qualifier+=symbolPrimitive PERIOD)* name=( CHAR_LENGTH | CHARACTER_LENGTH | OCTET_LENGTH | BIT_LENGTH | UPPER | LOWER | SIZE | EXISTS | COUNT ) # FunctionNameReserved
720+
| (qualifier+=symbolPrimitive PERIOD)* name=symbolPrimitive # FunctionNameSymbol
717721
;
718722

719723
pathStep

partiql-parser/src/main/kotlin/org/partiql/parser/PartiQLParser.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
package org.partiql.parser
1616

1717
import org.partiql.ast.Statement
18-
import org.partiql.parser.impl.PartiQLParserDefault
18+
import org.partiql.parser.internal.PartiQLParserDefault
1919

2020
public interface PartiQLParser {
2121

partiql-parser/src/main/kotlin/org/partiql/parser/PartiQLParserBuilder.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
package org.partiql.parser
1616

17-
import org.partiql.parser.impl.PartiQLParserDefault
17+
import org.partiql.parser.internal.PartiQLParserDefault
1818

1919
/**
2020
* A builder class to instantiate a [PartiQLParser].

partiql-parser/src/main/kotlin/org/partiql/parser/impl/PartiQLParserDefault.kt renamed to partiql-parser/src/main/kotlin/org/partiql/parser/internal/PartiQLParserDefault.kt

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
* language governing permissions and limitations under the License.
1313
*/
1414

15-
package org.partiql.parser.impl
15+
package org.partiql.parser.internal
1616

1717
import com.amazon.ionelement.api.IntElement
1818
import com.amazon.ionelement.api.IntElementSize
@@ -118,6 +118,7 @@ import org.partiql.ast.graphMatchSelectorShortestK
118118
import org.partiql.ast.graphMatchSelectorShortestKGroup
119119
import org.partiql.ast.groupBy
120120
import org.partiql.ast.groupByKey
121+
import org.partiql.ast.identifierQualified
121122
import org.partiql.ast.identifierSymbol
122123
import org.partiql.ast.let
123124
import org.partiql.ast.letBinding
@@ -208,7 +209,7 @@ import org.partiql.parser.PartiQLSyntaxException
208209
import org.partiql.parser.SourceLocation
209210
import org.partiql.parser.SourceLocations
210211
import org.partiql.parser.antlr.PartiQLBaseVisitor
211-
import org.partiql.parser.impl.util.DateTimeUtils
212+
import org.partiql.parser.internal.util.DateTimeUtils
212213
import org.partiql.value.NumericValue
213214
import org.partiql.value.PartiQLValueExperimental
214215
import org.partiql.value.StringValue
@@ -1723,16 +1724,34 @@ internal class PartiQLParserDefault : PartiQLParser {
17231724
exprCanLosslessCast(expr, type)
17241725
}
17251726

1726-
override fun visitFunctionCallIdent(ctx: GeneratedParser.FunctionCallIdentContext) = translate(ctx) {
1727-
val function = visitSymbolPrimitive(ctx.name)
1727+
override fun visitFunctionCall(ctx: GeneratedParser.FunctionCallContext) = translate(ctx) {
1728+
val function = visit(ctx.functionName()) as Identifier
17281729
val args = visitOrEmpty<Expr>(ctx.expr())
17291730
exprCall(function, args)
17301731
}
17311732

1732-
override fun visitFunctionCallReserved(ctx: GeneratedParser.FunctionCallReservedContext) = translate(ctx) {
1733-
val function = ctx.name.text.toIdentifier()
1734-
val args = visitOrEmpty<Expr>(ctx.expr())
1735-
exprCall(function, args)
1733+
override fun visitFunctionNameReserved(ctx: GeneratedParser.FunctionNameReservedContext): Identifier {
1734+
val path = ctx.qualifier.map { visitSymbolPrimitive(it) }
1735+
val name = identifierSymbol(ctx.name.text, Identifier.CaseSensitivity.INSENSITIVE)
1736+
return if (path.isEmpty()) {
1737+
name
1738+
} else {
1739+
val root = path.first()
1740+
val steps = path.drop(1) + listOf(name)
1741+
identifierQualified(root, steps)
1742+
}
1743+
}
1744+
1745+
override fun visitFunctionNameSymbol(ctx: GeneratedParser.FunctionNameSymbolContext): Identifier {
1746+
val path = ctx.qualifier.map { visitSymbolPrimitive(it) }
1747+
val name = visitSymbolPrimitive(ctx.name)
1748+
return if (path.isEmpty()) {
1749+
name
1750+
} else {
1751+
val root = path.first()
1752+
val steps = path.drop(1) + listOf(name)
1753+
identifierQualified(root, steps)
1754+
}
17361755
}
17371756

17381757
/**

partiql-parser/src/main/kotlin/org/partiql/parser/impl/util/DateTimeUtils.kt renamed to partiql-parser/src/main/kotlin/org/partiql/parser/internal/util/DateTimeUtils.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package org.partiql.parser.impl.util
1+
package org.partiql.parser.internal.util
22

33
import org.partiql.value.datetime.Date
44
import org.partiql.value.datetime.DateTimeException

0 commit comments

Comments
 (0)