From fa025f71188a5b48607fa25deb708d4cc9d74ed4 Mon Sep 17 00:00:00 2001 From: Jia-Xuan Liu Date: Tue, 10 May 2022 12:00:23 +0800 Subject: [PATCH] Bump trino-parser to Trino 358 (#1) --- core/trino-parser/pom.xml | 10 - .../antlr4/io/trino/sql/parser/SqlBase.g4 | 145 +++++-- .../io/trino/sql/ExpressionFormatter.java | 30 ++ .../io/trino/sql/RowPatternFormatter.java | 150 ++++++++ .../main/java/io/trino/sql/SqlFormatter.java | 149 ++++++-- .../java/io/trino/sql/parser/AstBuilder.java | 309 ++++++++++++++- .../java/io/trino/sql/parser/SqlParser.java | 6 + .../java/io/trino/sql/tree/AddColumn.java | 2 +- .../main/java/io/trino/sql/tree/Analyze.java | 2 +- .../java/io/trino/sql/tree/AnchorPattern.java | 96 +++++ .../java/io/trino/sql/tree/AstVisitor.java | 120 ++++++ .../io/trino/sql/tree/BindExpression.java | 2 +- .../main/java/io/trino/sql/tree/Comment.java | 2 +- .../trino/sql/tree/ComparisonExpression.java | 2 +- .../java/io/trino/sql/tree/CreateTable.java | 2 +- .../io/trino/sql/tree/CurrentCatalog.java | 70 ++++ .../java/io/trino/sql/tree/CurrentSchema.java | 70 ++++ .../java/io/trino/sql/tree/CurrentTime.java | 2 +- .../sql/tree/DefaultTraversalVisitor.java | 62 +++ .../java/io/trino/sql/tree/EmptyPattern.java | 78 ++++ .../io/trino/sql/tree/ExcludedPattern.java | 90 +++++ .../main/java/io/trino/sql/tree/Execute.java | 2 +- .../io/trino/sql/tree/ExpressionRewriter.java | 15 + .../sql/tree/ExpressionTreeRewriter.java | 59 ++- .../java/io/trino/sql/tree/FunctionCall.java | 19 +- .../java/io/trino/sql/tree/Identifier.java | 10 + .../io/trino/sql/tree/LabelDereference.java | 93 +++++ .../io/trino/sql/tree/MeasureDefinition.java | 109 ++++++ .../trino/sql/tree/OneOrMoreQuantifier.java | 41 ++ .../io/trino/sql/tree/PatternAlternation.java | 92 +++++ .../trino/sql/tree/PatternConcatenation.java | 92 +++++ .../io/trino/sql/tree/PatternPermutation.java | 92 +++++ .../io/trino/sql/tree/PatternQuantifier.java | 89 +++++ .../sql/tree/PatternRecognitionRelation.java | 356 ++++++++++++++++++ .../io/trino/sql/tree/PatternSearchMode.java | 93 +++++ .../io/trino/sql/tree/PatternVariable.java | 90 +++++ .../io/trino/sql/tree/ProcessingMode.java | 88 +++++ .../tree/QuantifiedComparisonExpression.java | 2 +- .../io/trino/sql/tree/QuantifiedPattern.java | 99 +++++ .../io/trino/sql/tree/RangeQuantifier.java | 103 +++++ .../java/io/trino/sql/tree/RowPattern.java | 31 ++ .../main/java/io/trino/sql/tree/SetPath.java | 2 +- .../main/java/io/trino/sql/tree/SkipTo.java | 178 +++++++++ .../io/trino/sql/tree/SubsetDefinition.java | 110 ++++++ .../main/java/io/trino/sql/tree/Table.java | 23 +- .../java/io/trino/sql/tree/TypeParameter.java | 2 +- .../main/java/io/trino/sql/tree/Update.java | 2 +- .../io/trino/sql/tree/VariableDefinition.java | 109 ++++++ .../trino/sql/tree/ZeroOrMoreQuantifier.java | 41 ++ .../trino/sql/tree/ZeroOrOneQuantifier.java | 41 ++ .../java/io/trino/sql/util/EscapedChars.java | 15 +- .../io/trino/sql/util/EscapedCharsUtil.java | 15 +- .../trino/sql/util/IntervalLiteralUtil.java | 19 +- .../io/trino/sql/parser/ParserAssert.java | 8 +- .../io/trino/sql/parser/TestSqlParser.java | 297 +++++++++++++++ .../parser/TestSqlParserErrorHandling.java | 8 +- .../sql/parser/TestStatementBuilder.java | 8 + 57 files changed, 3710 insertions(+), 142 deletions(-) create mode 100644 core/trino-parser/src/main/java/io/trino/sql/RowPatternFormatter.java create mode 100644 core/trino-parser/src/main/java/io/trino/sql/tree/AnchorPattern.java create mode 100644 core/trino-parser/src/main/java/io/trino/sql/tree/CurrentCatalog.java create mode 100644 core/trino-parser/src/main/java/io/trino/sql/tree/CurrentSchema.java create mode 100644 core/trino-parser/src/main/java/io/trino/sql/tree/EmptyPattern.java create mode 100644 core/trino-parser/src/main/java/io/trino/sql/tree/ExcludedPattern.java create mode 100644 core/trino-parser/src/main/java/io/trino/sql/tree/LabelDereference.java create mode 100644 core/trino-parser/src/main/java/io/trino/sql/tree/MeasureDefinition.java create mode 100644 core/trino-parser/src/main/java/io/trino/sql/tree/OneOrMoreQuantifier.java create mode 100644 core/trino-parser/src/main/java/io/trino/sql/tree/PatternAlternation.java create mode 100644 core/trino-parser/src/main/java/io/trino/sql/tree/PatternConcatenation.java create mode 100644 core/trino-parser/src/main/java/io/trino/sql/tree/PatternPermutation.java create mode 100644 core/trino-parser/src/main/java/io/trino/sql/tree/PatternQuantifier.java create mode 100644 core/trino-parser/src/main/java/io/trino/sql/tree/PatternRecognitionRelation.java create mode 100644 core/trino-parser/src/main/java/io/trino/sql/tree/PatternSearchMode.java create mode 100644 core/trino-parser/src/main/java/io/trino/sql/tree/PatternVariable.java create mode 100644 core/trino-parser/src/main/java/io/trino/sql/tree/ProcessingMode.java create mode 100644 core/trino-parser/src/main/java/io/trino/sql/tree/QuantifiedPattern.java create mode 100644 core/trino-parser/src/main/java/io/trino/sql/tree/RangeQuantifier.java create mode 100644 core/trino-parser/src/main/java/io/trino/sql/tree/RowPattern.java create mode 100644 core/trino-parser/src/main/java/io/trino/sql/tree/SkipTo.java create mode 100644 core/trino-parser/src/main/java/io/trino/sql/tree/SubsetDefinition.java create mode 100644 core/trino-parser/src/main/java/io/trino/sql/tree/VariableDefinition.java create mode 100644 core/trino-parser/src/main/java/io/trino/sql/tree/ZeroOrMoreQuantifier.java create mode 100644 core/trino-parser/src/main/java/io/trino/sql/tree/ZeroOrOneQuantifier.java diff --git a/core/trino-parser/pom.xml b/core/trino-parser/pom.xml index c681420a0..a4112cb4a 100644 --- a/core/trino-parser/pom.xml +++ b/core/trino-parser/pom.xml @@ -44,7 +44,6 @@ antlr4-runtime - org.assertj assertj-core @@ -63,16 +62,7 @@ org.antlr antlr4-maven-plugin - ${dep.antlr.version} - - - - antlr4 - - - - diff --git a/core/trino-parser/src/main/antlr4/io/trino/sql/parser/SqlBase.g4 b/core/trino-parser/src/main/antlr4/io/trino/sql/parser/SqlBase.g4 index 88e2b56d9..eda0badcf 100644 --- a/core/trino-parser/src/main/antlr4/io/trino/sql/parser/SqlBase.g4 +++ b/core/trino-parser/src/main/antlr4/io/trino/sql/parser/SqlBase.g4 @@ -34,6 +34,10 @@ standaloneType : type EOF ; +standaloneRowPattern + : rowPattern EOF + ; + statement : query #statementDefault | USE schema=identifier #use @@ -121,7 +125,7 @@ statement | SHOW COLUMNS (FROM | IN) qualifiedName? (LIKE pattern=string (ESCAPE escape=string)?)? #showColumns | SHOW STATS FOR qualifiedName #showStats - | SHOW STATS FOR '(' querySpecification ')' #showStatsForQuery + | SHOW STATS FOR '(' query ')' #showStatsForQuery | SHOW CURRENT? ROLES ((FROM | IN) identifier)? #showRoles | SHOW ROLE GRANTS ((FROM | IN) identifier)? #showRoleGrants | DESCRIBE qualifiedName #showColumns @@ -191,7 +195,7 @@ limitRowCount rowCount : INTEGER_VALUE - | PARAMETER + | QUESTION_MARK ; queryTerm @@ -284,7 +288,7 @@ joinCriteria ; sampledRelation - : aliasedRelation ( + : patternRecognition ( TABLESAMPLE sampleType '(' percentage=expression ')' )? ; @@ -294,6 +298,53 @@ sampleType | SYSTEM ; +patternRecognition + : aliasedRelation ( + MATCH_RECOGNIZE '(' + (PARTITION BY partition+=expression (',' partition+=expression)*)? + (ORDER BY sortItem (',' sortItem)*)? + (MEASURES measureDefinition (',' measureDefinition)*)? + rowsPerMatch? + (AFTER MATCH skipTo)? + (INITIAL | SEEK)? + PATTERN '(' rowPattern ')' + (SUBSET subsetDefinition (',' subsetDefinition)*)? + DEFINE variableDefinition (',' variableDefinition)* + ')' + (AS? identifier columnAliases?)?)? + ; + +measureDefinition + : expression AS identifier + ; + +rowsPerMatch + : ONE ROW PER MATCH + | ALL ROWS PER MATCH emptyMatchHandling? + ; + +emptyMatchHandling + : SHOW EMPTY MATCHES + | OMIT EMPTY MATCHES + | WITH UNMATCHED ROWS + ; + +skipTo + : 'SKIP' TO NEXT ROW + | 'SKIP' PAST LAST ROW + | 'SKIP' TO FIRST identifier + | 'SKIP' TO LAST identifier + | 'SKIP' TO identifier + ; + +subsetDefinition + : name=identifier EQ '(' union+=identifier (',' union+=identifier)* ')' + ; + +variableDefinition + : identifier AS expression + ; + aliasedRelation : relationPrimary (AS? identifier columnAliases?)? ; @@ -356,16 +407,18 @@ primaryExpression | interval #intervalLiteral | identifier string #typeConstructor | DOUBLE PRECISION string #typeConstructor + // for wireprotocol pg style type consturctor + | string PG_CAST identifier #typeConstructor | number #numericLiteral | booleanValue #booleanLiteral | string #stringLiteral | BINARY_LITERAL #binaryLiteral - | PARAMETER #parameter + | QUESTION_MARK #parameter | POSITION '(' valueExpression IN valueExpression ')' #position | '(' expression (',' expression)+ ')' #rowConstructor | ROW '(' expression (',' expression)* ')' #rowConstructor | qualifiedName '(' ASTERISK ')' filter? over? #functionCall - | qualifiedName '(' (setQuantifier? expression (',' expression)*)? + | processingMode? qualifiedName '(' (setQuantifier? expression (',' expression)*)? (ORDER BY sortItem (',' sortItem)*)? ')' filter? (nullTreatment? over)? #functionCall | identifier '->' expression #lambda | '(' (identifier (',' identifier)*)? ')' '->' expression #lambda @@ -388,6 +441,9 @@ primaryExpression | name=LOCALTIME ('(' precision=INTEGER_VALUE ')')? #specialDateTimeFunction | name=LOCALTIMESTAMP ('(' precision=INTEGER_VALUE ')')? #specialDateTimeFunction | name=CURRENT_USER #currentUser + | name=CURRENT_CATALOG #currentCatalog + // To support pg-style current_schema() + | name=CURRENT_SCHEMA ('(' ')')? #currentSchema | name=CURRENT_PATH #currentPath | SUBSTRING '(' valueExpression FROM valueExpression (FOR valueExpression)? ')' #substring | NORMALIZE '(' valueExpression (',' normalForm)? ')' #normalize @@ -396,6 +452,11 @@ primaryExpression | GROUPING '(' (qualifiedName (',' qualifiedName)*)? ')' #groupingOperation ; +processingMode + : RUNNING + | FINAL + ; + nullTreatment : IGNORE NULLS | RESPECT NULLS @@ -496,6 +557,30 @@ frameBound | expression boundType=(PRECEDING | FOLLOWING) #boundedFrame ; +rowPattern + : patternPrimary patternQuantifier? #quantifiedPrimary + | rowPattern rowPattern #patternConcatenation + | rowPattern '|' rowPattern #patternAlternation + ; + +patternPrimary + : identifier #patternVariable + | '(' ')' #emptyPattern + | PERMUTE '(' rowPattern (',' rowPattern)* ')' #patternPermutation + | '(' rowPattern ')' #groupedPattern + | '^' #partitionStartAnchor + | '$' #partitionEndAnchor + | '{-' rowPattern '-}' #excludedPattern + ; + +patternQuantifier + : ASTERISK (reluctant=QUESTION_MARK)? #zeroOrMoreQuantifier + | PLUS (reluctant=QUESTION_MARK)? #oneOrMoreQuantifier + | QUESTION_MARK (reluctant=QUESTION_MARK)? #zeroOrOneQuantifier + | '{' exactly=INTEGER_VALUE '}' (reluctant=QUESTION_MARK)? #rangeQuantifier + | '{' (atLeast=INTEGER_VALUE)? ',' (atMost=INTEGER_VALUE)? '}' (reluctant=QUESTION_MARK)? #rangeQuantifier + ; + updateAssignment : identifier EQ expression ; @@ -571,26 +656,26 @@ number nonReserved // IMPORTANT: this rule must only contain tokens. Nested rules are not supported. See SqlParser.exitNonReserved - : ADD | ADMIN | ALL | ANALYZE | ANY | ARRAY | ASC | AT | AUTHORIZATION + : ADD | ADMIN | AFTER | ALL | ANALYZE | ANY | ARRAY | ASC | AT | AUTHORIZATION | BERNOULLI | CALL | CASCADE | CATALOGS | COLUMN | COLUMNS | COMMENT | COMMIT | COMMITTED | CURRENT - | DATA | DATE | DAY | DEFINER | DESC | DISTRIBUTED | DOUBLE - | EXCLUDING | EXPLAIN - | FETCH | FILTER | FIRST | FOLLOWING | FORMAT | FUNCTIONS + | DATA | DATE | DAY | DEFINE | DEFINER | DESC | DISTRIBUTED | DOUBLE + | EMPTY | EXCLUDING | EXPLAIN + | FETCH | FILTER | FINAL | FIRST | FOLLOWING | FORMAT | FUNCTIONS | GRANT | GRANTED | GRANTS | GRAPHVIZ | GROUPS | HOUR - | IF | IGNORE | INCLUDING | INPUT | INTERVAL | INVOKER | IO | ISOLATION + | IF | IGNORE | INCLUDING | INITIAL | INPUT | INTERVAL | INVOKER | IO | ISOLATION | JSON | LAST | LATERAL | LEVEL | LIMIT | LOGICAL - | MAP | MATCHED | MATERIALIZED | MERGE | MINUTE | MONTH + | MAP | MATCH | MATCHED | MATCHES | MATCH_RECOGNIZE | MATERIALIZED | MEASURES | MERGE | MINUTE | MONTH | NEXT | NFC | NFD | NFKC | NFKD | NO | NONE | NULLIF | NULLS - | OFFSET | ONLY | OPTION | ORDINALITY | OUTPUT | OVER - | PARTITION | PARTITIONS | PATH | POSITION | PRECEDING | PRECISION | PRIVILEGES | PROPERTIES - | RANGE | READ | REFRESH | RENAME | REPEATABLE | REPLACE | RESET | RESPECT | RESTRICT | REVOKE | ROLE | ROLES | ROLLBACK | ROW | ROWS - | SCHEMA | SCHEMAS | SECOND | SECURITY | SERIALIZABLE | SESSION | SET | SETS - | SHOW | SOME | START | STATS | SUBSTRING | SYSTEM + | OFFSET | OMIT | ONE | ONLY | OPTION | ORDINALITY | OUTPUT | OVER + | PARTITION | PARTITIONS | PAST | PATH | PATTERN | PER | PERMUTE | POSITION | PRECEDING | PRECISION | PRIVILEGES | PROPERTIES + | RANGE | READ | REFRESH | RENAME | REPEATABLE | REPLACE | RESET | RESPECT | RESTRICT | REVOKE | ROLE | ROLES | ROLLBACK | ROW | ROWS | RUNNING + | SCHEMA | SCHEMAS | SECOND | SECURITY | SEEK | SERIALIZABLE | SESSION | SET | SETS + | SHOW | SOME | START | STATS | SUBSET | SUBSTRING | SYSTEM | TABLES | TABLESAMPLE | TEXT | TIES | TIME | TIMESTAMP | TO | TRANSACTION | TRY_CAST | TYPE - | UNBOUNDED | UNCOMMITTED | UPDATE | USE | USER | SESSION_USER + | UNBOUNDED | UNCOMMITTED | UNMATCHED| UPDATE | USE | USER | SESSION_USER | VALIDATE | VERBOSE | VIEW | WINDOW | WITHOUT | WORK | WRITE | YEAR @@ -599,6 +684,7 @@ nonReserved ADD: 'ADD'; ADMIN: 'ADMIN'; +AFTER: 'AFTER'; ALL: 'ALL'; ALTER: 'ALTER'; ANALYZE: 'ANALYZE'; @@ -627,9 +713,11 @@ CREATE: 'CREATE'; CROSS: 'CROSS'; CUBE: 'CUBE'; CURRENT: 'CURRENT'; +CURRENT_CATALOG: 'CURRENT_CATALOG'; CURRENT_DATE: 'CURRENT_DATE'; CURRENT_PATH: 'CURRENT_PATH'; CURRENT_ROLE: 'CURRENT_ROLE'; +CURRENT_SCHEMA: 'CURRENT_SCHEMA'; CURRENT_TIME: 'CURRENT_TIME'; CURRENT_TIMESTAMP: 'CURRENT_TIMESTAMP'; CURRENT_USER: 'CURRENT_USER'; @@ -641,11 +729,13 @@ DEFINER: 'DEFINER'; DELETE: 'DELETE'; DESC: 'DESC'; DESCRIBE: 'DESCRIBE'; +DEFINE: 'DEFINE'; DISTINCT: 'DISTINCT'; DISTRIBUTED: 'DISTRIBUTED'; DOUBLE: 'DOUBLE'; DROP: 'DROP'; ELSE: 'ELSE'; +EMPTY: 'EMPTY'; END: 'END'; ESCAPE: 'ESCAPE'; EXCEPT: 'EXCEPT'; @@ -657,6 +747,7 @@ EXTRACT: 'EXTRACT'; FALSE: 'FALSE'; FETCH: 'FETCH'; FILTER: 'FILTER'; +FINAL: 'FINAL'; FIRST: 'FIRST'; FOLLOWING: 'FOLLOWING'; FOR: 'FOR'; @@ -677,6 +768,7 @@ IF: 'IF'; IGNORE: 'IGNORE'; IN: 'IN'; INCLUDING: 'INCLUDING'; +INITIAL: 'INITIAL'; INNER: 'INNER'; INPUT: 'INPUT'; INSERT: 'INSERT'; @@ -699,8 +791,12 @@ LOCALTIME: 'LOCALTIME'; LOCALTIMESTAMP: 'LOCALTIMESTAMP'; LOGICAL: 'LOGICAL'; MAP: 'MAP'; +MATCH: 'MATCH'; MATCHED: 'MATCHED'; +MATCHES: 'MATCHES'; +MATCH_RECOGNIZE: 'MATCH_RECOGNIZE'; MATERIALIZED: 'MATERIALIZED'; +MEASURES: 'MEASURES'; MERGE: 'MERGE'; MINUTE: 'MINUTE'; MONTH: 'MONTH'; @@ -718,7 +814,9 @@ NULL: 'NULL'; NULLIF: 'NULLIF'; NULLS: 'NULLS'; OFFSET: 'OFFSET'; +OMIT: 'OMIT'; ON: 'ON'; +ONE: 'ONE'; ONLY: 'ONLY'; OPTION: 'OPTION'; OR: 'OR'; @@ -729,7 +827,11 @@ OUTPUT: 'OUTPUT'; OVER: 'OVER'; PARTITION: 'PARTITION'; PARTITIONS: 'PARTITIONS'; +PAST: 'PAST'; PATH: 'PATH'; +PATTERN: 'PATTERN'; +PER: 'PER'; +PERMUTE: 'PERMUTE'; POSITION: 'POSITION'; PRECEDING: 'PRECEDING'; PRECISION: 'PRECISION'; @@ -754,10 +856,12 @@ ROLLBACK: 'ROLLBACK'; ROLLUP: 'ROLLUP'; ROW: 'ROW'; ROWS: 'ROWS'; +RUNNING: 'RUNNING'; SCHEMA: 'SCHEMA'; SCHEMAS: 'SCHEMAS'; SECOND: 'SECOND'; SECURITY: 'SECURITY'; +SEEK: 'SEEK'; SELECT: 'SELECT'; SERIALIZABLE: 'SERIALIZABLE'; SESSION: 'SESSION'; @@ -768,6 +872,7 @@ SHOW: 'SHOW'; SOME: 'SOME'; START: 'START'; STATS: 'STATS'; +SUBSET: 'SUBSET'; SUBSTRING: 'SUBSTRING'; SYSTEM: 'SYSTEM'; TABLE: 'TABLE'; @@ -787,6 +892,7 @@ UESCAPE: 'UESCAPE'; UNBOUNDED: 'UNBOUNDED'; UNCOMMITTED: 'UNCOMMITTED'; UNION: 'UNION'; +UNMATCHED: 'UNMATCHED'; UNNEST: 'UNNEST'; UPDATE: 'UPDATE'; USE: 'USE'; @@ -819,6 +925,7 @@ ASTERISK: '*'; SLASH: '/'; PERCENT: '%'; CONCAT: '||'; +QUESTION_MARK : '?'; // for wireprotocol to use POSIX regular expressions REGEX_MATCH: '~'; @@ -891,10 +998,6 @@ WS : [ \r\n\t]+ -> channel(HIDDEN) ; -PARAMETER - : '?' - ; - // Catch-all for anything we can't recognize. // We use this to be able to ignore and recover all the text // when splitting statements with DelimiterLexer diff --git a/core/trino-parser/src/main/java/io/trino/sql/ExpressionFormatter.java b/core/trino-parser/src/main/java/io/trino/sql/ExpressionFormatter.java index 8cb2f8804..4bd009415 100644 --- a/core/trino-parser/src/main/java/io/trino/sql/ExpressionFormatter.java +++ b/core/trino-parser/src/main/java/io/trino/sql/ExpressionFormatter.java @@ -32,7 +32,9 @@ import io.trino.sql.tree.CharLiteral; import io.trino.sql.tree.CoalesceExpression; import io.trino.sql.tree.ComparisonExpression; import io.trino.sql.tree.Cube; +import io.trino.sql.tree.CurrentCatalog; import io.trino.sql.tree.CurrentPath; +import io.trino.sql.tree.CurrentSchema; import io.trino.sql.tree.CurrentTime; import io.trino.sql.tree.CurrentUser; import io.trino.sql.tree.DateTimeDataType; @@ -59,6 +61,7 @@ import io.trino.sql.tree.IntervalDayTimeDataType; import io.trino.sql.tree.IntervalLiteral; import io.trino.sql.tree.IsNotNullPredicate; import io.trino.sql.tree.IsNullPredicate; +import io.trino.sql.tree.LabelDereference; import io.trino.sql.tree.LambdaArgumentDeclaration; import io.trino.sql.tree.LambdaExpression; import io.trino.sql.tree.LikePredicate; @@ -158,6 +161,18 @@ public final class ExpressionFormatter .append(process(node.getTimeZone(), context)).toString(); } + @Override + protected String visitCurrentCatalog(CurrentCatalog node, Void context) + { + return "CURRENT_CATALOG"; + } + + @Override + protected String visitCurrentSchema(CurrentSchema node, Void context) + { + return "CURRENT_SCHEMA"; + } + @Override protected String visitCurrentUser(CurrentUser node, Void context) { @@ -363,6 +378,11 @@ public final class ExpressionFormatter { StringBuilder builder = new StringBuilder(); + if (node.getProcessingMode().isPresent()) { + builder.append(node.getProcessingMode().get().getMode()) + .append(" "); + } + String arguments = joinExpressions(node.getArguments()); if (node.getArguments().isEmpty() && "count".equalsIgnoreCase(node.getName().getSuffix())) { arguments = "*"; @@ -736,6 +756,16 @@ public final class ExpressionFormatter return builder.toString(); } + @Override + protected String visitLabelDereference(LabelDereference node, Void context) + { + // format LabelDereference L.x as "LABEL_DEREFERENCE("L", "x")" + // LabelDereference, like SymbolReference, is an IR-type expression. It is never a result of the parser. + // After being formatted this way for serialization, it will be parsed as functionCall + // and swapped back for LabelDereference. + return "LABEL_DEREFERENCE(" + formatIdentifier(node.getLabel()) + ", " + process(node.getReference()) + ")"; + } + private String formatBinaryExpression(String operator, Expression left, Expression right) { return '(' + process(left, null) + ' ' + operator + ' ' + process(right, null) + ')'; diff --git a/core/trino-parser/src/main/java/io/trino/sql/RowPatternFormatter.java b/core/trino-parser/src/main/java/io/trino/sql/RowPatternFormatter.java new file mode 100644 index 000000000..c430c7377 --- /dev/null +++ b/core/trino-parser/src/main/java/io/trino/sql/RowPatternFormatter.java @@ -0,0 +1,150 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.sql; + +import io.trino.sql.tree.AnchorPattern; +import io.trino.sql.tree.AstVisitor; +import io.trino.sql.tree.EmptyPattern; +import io.trino.sql.tree.ExcludedPattern; +import io.trino.sql.tree.Node; +import io.trino.sql.tree.OneOrMoreQuantifier; +import io.trino.sql.tree.PatternAlternation; +import io.trino.sql.tree.PatternConcatenation; +import io.trino.sql.tree.PatternPermutation; +import io.trino.sql.tree.PatternVariable; +import io.trino.sql.tree.QuantifiedPattern; +import io.trino.sql.tree.RangeQuantifier; +import io.trino.sql.tree.RowPattern; +import io.trino.sql.tree.ZeroOrMoreQuantifier; +import io.trino.sql.tree.ZeroOrOneQuantifier; + +import static java.lang.String.format; +import static java.util.stream.Collectors.joining; + +public final class RowPatternFormatter +{ + private RowPatternFormatter() {} + + public static String formatPattern(RowPattern pattern) + { + return new Formatter().process(pattern, null); + } + + public static class Formatter + extends AstVisitor + { + @Override + protected String visitNode(Node node, Void context) + { + throw new UnsupportedOperationException(); + } + + @Override + protected String visitRowPattern(RowPattern node, Void context) + { + throw new UnsupportedOperationException(format("not yet implemented: %s.visit%s", getClass().getName(), node.getClass().getSimpleName())); + } + + @Override + protected String visitPatternAlternation(PatternAlternation node, Void context) + { + return node.getPatterns().stream() + .map(child -> process(child, context)) + .collect(joining(" | ", "(", ")")); + } + + @Override + protected String visitPatternConcatenation(PatternConcatenation node, Void context) + { + return node.getPatterns().stream() + .map(child -> process(child, context)) + .collect(joining(" ", "(", ")")); + } + + @Override + protected String visitQuantifiedPattern(QuantifiedPattern node, Void context) + { + return "(" + process(node.getPattern(), context) + process(node.getPatternQuantifier(), context) + ")"; + } + + @Override + protected String visitPatternVariable(PatternVariable node, Void context) + { + return ExpressionFormatter.formatExpression(node.getName()); + } + + @Override + protected String visitEmptyPattern(EmptyPattern node, Void context) + { + return "()"; + } + + @Override + protected String visitPatternPermutation(PatternPermutation node, Void context) + { + return node.getPatterns().stream() + .map(child -> process(child, context)) + .collect(joining(", ", "PERMUTE(", ")")); + } + + @Override + protected String visitAnchorPattern(AnchorPattern node, Void context) + { + switch (node.getType()) { + case PARTITION_START: + return "^"; + case PARTITION_END: + return "$"; + default: + throw new IllegalStateException("unexpected anchor pattern type: " + node.getType()); + } + } + + @Override + protected String visitExcludedPattern(ExcludedPattern node, Void context) + { + return "{-" + process(node.getPattern(), context) + "-}"; + } + + @Override + protected String visitZeroOrMoreQuantifier(ZeroOrMoreQuantifier node, Void context) + { + String greedy = node.isGreedy() ? "" : "?"; + return "*" + greedy; + } + + @Override + protected String visitOneOrMoreQuantifier(OneOrMoreQuantifier node, Void context) + { + String greedy = node.isGreedy() ? "" : "?"; + return "+" + greedy; + } + + @Override + protected String visitZeroOrOneQuantifier(ZeroOrOneQuantifier node, Void context) + { + String greedy = node.isGreedy() ? "" : "?"; + return "?" + greedy; + } + + @Override + protected String visitRangeQuantifier(RangeQuantifier node, Void context) + { + String greedy = node.isGreedy() ? "" : "?"; + String atLeast = node.getAtLeast().map(ExpressionFormatter::formatExpression).orElse(""); + String atMost = node.getAtMost().map(ExpressionFormatter::formatExpression).orElse(""); + return "{" + atLeast + "," + atMost + "}" + greedy; + } + } +} diff --git a/core/trino-parser/src/main/java/io/trino/sql/SqlFormatter.java b/core/trino-parser/src/main/java/io/trino/sql/SqlFormatter.java index 51725a24c..eca7d14d8 100644 --- a/core/trino-parser/src/main/java/io/trino/sql/SqlFormatter.java +++ b/core/trino-parser/src/main/java/io/trino/sql/SqlFormatter.java @@ -72,6 +72,7 @@ import io.trino.sql.tree.NaturalJoin; import io.trino.sql.tree.Node; import io.trino.sql.tree.Offset; import io.trino.sql.tree.OrderBy; +import io.trino.sql.tree.PatternRecognitionRelation; import io.trino.sql.tree.Prepare; import io.trino.sql.tree.PrincipalSpecification; import io.trino.sql.tree.Property; @@ -89,6 +90,7 @@ import io.trino.sql.tree.Revoke; import io.trino.sql.tree.RevokeRoles; import io.trino.sql.tree.Rollback; import io.trino.sql.tree.Row; +import io.trino.sql.tree.RowPattern; import io.trino.sql.tree.SampledRelation; import io.trino.sql.tree.Select; import io.trino.sql.tree.SelectItem; @@ -120,7 +122,6 @@ import io.trino.sql.tree.Unnest; import io.trino.sql.tree.Update; import io.trino.sql.tree.UpdateAssignment; import io.trino.sql.tree.Values; -import io.trino.sql.tree.WindowDefinition; import io.trino.sql.tree.With; import io.trino.sql.tree.WithQuery; @@ -131,6 +132,7 @@ import java.util.Optional; import java.util.stream.Collectors; import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.collect.Iterables.getOnlyElement; import static com.google.common.collect.Iterables.transform; @@ -139,6 +141,7 @@ import static io.trino.sql.ExpressionFormatter.formatGroupBy; import static io.trino.sql.ExpressionFormatter.formatOrderBy; import static io.trino.sql.ExpressionFormatter.formatStringLiteral; import static io.trino.sql.ExpressionFormatter.formatWindowSpecification; +import static io.trino.sql.RowPatternFormatter.formatPattern; import static java.lang.String.format; import static java.util.stream.Collectors.joining; @@ -186,6 +189,14 @@ public final class SqlFormatter return null; } + @Override + protected Void visitRowPattern(RowPattern node, Integer indent) + { + checkArgument(indent == 0, "visitRowPattern should only be called at root"); + builder.append(formatPattern(node)); + return null; + } + @Override protected Void visitUnnest(Unnest node, Integer indent) { @@ -327,21 +338,9 @@ public final class SqlFormatter if (!node.getWindows().isEmpty()) { append(indent, "WINDOW"); - if (node.getWindows().size() == 1) { - builder.append(" ") - .append(formatWindowDefinition(node.getWindows().get(0))) - .append("\n"); - } - else { - int size = node.getWindows().size(); - builder.append("\n"); - for (int i = 0; i < size - 1; i++) { - append(indent + 1, formatWindowDefinition(node.getWindows().get(i))) - .append(",\n"); - } - append(indent + 1, formatWindowDefinition(node.getWindows().get(size - 1))) - .append("\n"); - } + formatDefinitionList(node.getWindows().stream() + .map(definition -> formatExpression(definition.getName()) + " AS " + formatWindowSpecification(definition.getWindow())) + .collect(toImmutableList()), indent + 1); } if (node.getOrderBy().isPresent()) { @@ -358,11 +357,6 @@ public final class SqlFormatter return null; } - private String formatWindowDefinition(WindowDefinition definition) - { - return formatExpression(definition.getName()) + " AS " + formatWindowSpecification(definition.getWindow()); - } - @Override protected Void visitOrderBy(OrderBy node, Integer indent) { @@ -526,6 +520,97 @@ public final class SqlFormatter return null; } + @Override + protected Void visitPatternRecognitionRelation(PatternRecognitionRelation node, Integer indent) + { + processRelationSuffix(node.getInput(), indent); + + builder.append(" MATCH_RECOGNIZE (\n"); + if (!node.getPartitionBy().isEmpty()) { + append(indent + 1, "PARTITION BY ") + .append(node.getPartitionBy().stream() + .map(ExpressionFormatter::formatExpression) + .collect(joining(", "))) + .append("\n"); + } + if (node.getOrderBy().isPresent()) { + process(node.getOrderBy().get(), indent + 1); + } + if (!node.getMeasures().isEmpty()) { + append(indent + 1, "MEASURES"); + formatDefinitionList(node.getMeasures().stream() + .map(measure -> formatExpression(measure.getExpression()) + " AS " + formatExpression(measure.getName())) + .collect(toImmutableList()), indent + 2); + } + if (node.getRowsPerMatch().isPresent()) { + String rowsPerMatch; + switch (node.getRowsPerMatch().get()) { + case ONE: + rowsPerMatch = "ONE ROW PER MATCH"; + break; + case ALL_SHOW_EMPTY: + rowsPerMatch = "ALL ROWS PER MATCH SHOW EMPTY MATCHES"; + break; + case ALL_OMIT_EMPTY: + rowsPerMatch = "ALL ROWS PER MATCH OMIT EMPTY MATCHES"; + break; + case ALL_WITH_UNMATCHED: + rowsPerMatch = "ALL ROWS PER MATCH WITH UNMATCHED ROWS"; + break; + default: + // RowsPerMatch of type WINDOW cannot occur in MATCH_RECOGNIZE clause + throw new IllegalStateException("unexpected rowsPerMatch: " + node.getRowsPerMatch().get()); + } + append(indent + 1, rowsPerMatch) + .append("\n"); + } + if (node.getAfterMatchSkipTo().isPresent()) { + String skipTo; + switch (node.getAfterMatchSkipTo().get().getPosition()) { + case PAST_LAST: + skipTo = "AFTER MATCH SKIP PAST LAST ROW"; + break; + case NEXT: + skipTo = "AFTER MATCH SKIP TO NEXT ROW"; + break; + case LAST: + checkState(node.getAfterMatchSkipTo().get().getIdentifier().isPresent(), "missing identifier in AFTER MATCH SKIP TO LAST"); + skipTo = "AFTER MATCH SKIP TO LAST " + formatExpression(node.getAfterMatchSkipTo().get().getIdentifier().get()); + break; + case FIRST: + checkState(node.getAfterMatchSkipTo().get().getIdentifier().isPresent(), "missing identifier in AFTER MATCH SKIP TO FIRST"); + skipTo = "AFTER MATCH SKIP TO FIRST " + formatExpression(node.getAfterMatchSkipTo().get().getIdentifier().get()); + break; + default: + throw new IllegalStateException("unexpected skipTo: " + node.getAfterMatchSkipTo().get()); + } + append(indent + 1, skipTo) + .append("\n"); + } + if (node.getPatternSearchMode().isPresent()) { + append(indent + 1, node.getPatternSearchMode().get().getMode().name()) + .append("\n"); + } + append(indent + 1, "PATTERN (") + .append(formatPattern(node.getPattern())) + .append(")\n"); + if (!node.getSubsets().isEmpty()) { + append(indent + 1, "SUBSET"); + formatDefinitionList(node.getSubsets().stream() + .map(subset -> formatExpression(subset.getName()) + " = " + subset.getIdentifiers().stream() + .map(ExpressionFormatter::formatExpression).collect(joining(", ", "(", ")"))) + .collect(toImmutableList()), indent + 2); + } + append(indent + 1, "DEFINE"); + formatDefinitionList(node.getVariableDefinitions().stream() + .map(variable -> formatExpression(variable.getName()) + " AS " + formatExpression(variable.getExpression())) + .collect(toImmutableList()), indent + 2); + + builder.append(")"); + + return null; + } + @Override protected Void visitSampledRelation(SampledRelation node, Integer indent) { @@ -542,7 +627,7 @@ public final class SqlFormatter private void processRelationSuffix(Relation relation, Integer indent) { - if ((relation instanceof AliasedRelation) || (relation instanceof SampledRelation)) { + if ((relation instanceof AliasedRelation) || (relation instanceof SampledRelation) || (relation instanceof PatternRecognitionRelation)) { builder.append("( "); process(relation, indent + 1); append(indent, ")"); @@ -1572,7 +1657,7 @@ public final class SqlFormatter builder.append(node.getType().get()); builder.append(" "); } - builder.append(node.getName()) + builder.append(formatName(node.getName())) .append(" TO ") .append(formatPrincipal(node.getGrantee())); if (node.isWithGrantOption()) { @@ -1689,6 +1774,24 @@ public final class SqlFormatter { return Strings.repeat(INDENT, indent); } + + private void formatDefinitionList(List elements, int indent) + { + if (elements.size() == 1) { + builder.append(" ") + .append(getOnlyElement(elements)) + .append("\n"); + } + else { + builder.append("\n"); + for (int i = 0; i < elements.size() - 1; i++) { + append(indent, elements.get(i)) + .append(",\n"); + } + append(indent, elements.get(elements.size() - 1)) + .append("\n"); + } + } } private static void appendAliasColumns(StringBuilder builder, List columns) diff --git a/core/trino-parser/src/main/java/io/trino/sql/parser/AstBuilder.java b/core/trino-parser/src/main/java/io/trino/sql/parser/AstBuilder.java index 4202a983e..08beee088 100644 --- a/core/trino-parser/src/main/java/io/trino/sql/parser/AstBuilder.java +++ b/core/trino-parser/src/main/java/io/trino/sql/parser/AstBuilder.java @@ -22,6 +22,7 @@ import io.trino.sql.tree.AliasedRelation; import io.trino.sql.tree.AllColumns; import io.trino.sql.tree.AllRows; import io.trino.sql.tree.Analyze; +import io.trino.sql.tree.AnchorPattern; import io.trino.sql.tree.ArithmeticBinaryExpression; import io.trino.sql.tree.ArithmeticUnaryExpression; import io.trino.sql.tree.ArrayConstructor; @@ -46,7 +47,9 @@ import io.trino.sql.tree.CreateTable; import io.trino.sql.tree.CreateTableAsSelect; import io.trino.sql.tree.CreateView; import io.trino.sql.tree.Cube; +import io.trino.sql.tree.CurrentCatalog; import io.trino.sql.tree.CurrentPath; +import io.trino.sql.tree.CurrentSchema; import io.trino.sql.tree.CurrentTime; import io.trino.sql.tree.CurrentUser; import io.trino.sql.tree.DataType; @@ -65,7 +68,9 @@ import io.trino.sql.tree.DropRole; import io.trino.sql.tree.DropSchema; import io.trino.sql.tree.DropTable; import io.trino.sql.tree.DropView; +import io.trino.sql.tree.EmptyPattern; import io.trino.sql.tree.Except; +import io.trino.sql.tree.ExcludedPattern; import io.trino.sql.tree.Execute; import io.trino.sql.tree.ExistsPredicate; import io.trino.sql.tree.Explain; @@ -112,6 +117,7 @@ import io.trino.sql.tree.LikePredicate; import io.trino.sql.tree.Limit; import io.trino.sql.tree.LogicalBinaryExpression; import io.trino.sql.tree.LongLiteral; +import io.trino.sql.tree.MeasureDefinition; import io.trino.sql.tree.Merge; import io.trino.sql.tree.MergeCase; import io.trino.sql.tree.MergeDelete; @@ -125,18 +131,30 @@ import io.trino.sql.tree.NullIfExpression; import io.trino.sql.tree.NullLiteral; import io.trino.sql.tree.NumericParameter; import io.trino.sql.tree.Offset; +import io.trino.sql.tree.OneOrMoreQuantifier; import io.trino.sql.tree.OrderBy; import io.trino.sql.tree.Parameter; import io.trino.sql.tree.PathElement; import io.trino.sql.tree.PathSpecification; +import io.trino.sql.tree.PatternAlternation; +import io.trino.sql.tree.PatternConcatenation; +import io.trino.sql.tree.PatternPermutation; +import io.trino.sql.tree.PatternQuantifier; +import io.trino.sql.tree.PatternRecognitionRelation; +import io.trino.sql.tree.PatternRecognitionRelation.RowsPerMatch; +import io.trino.sql.tree.PatternSearchMode; +import io.trino.sql.tree.PatternVariable; import io.trino.sql.tree.Prepare; import io.trino.sql.tree.PrincipalSpecification; +import io.trino.sql.tree.ProcessingMode; import io.trino.sql.tree.Property; import io.trino.sql.tree.QualifiedName; import io.trino.sql.tree.QuantifiedComparisonExpression; +import io.trino.sql.tree.QuantifiedPattern; import io.trino.sql.tree.Query; import io.trino.sql.tree.QueryBody; import io.trino.sql.tree.QuerySpecification; +import io.trino.sql.tree.RangeQuantifier; import io.trino.sql.tree.RefreshMaterializedView; import io.trino.sql.tree.Relation; import io.trino.sql.tree.RenameColumn; @@ -150,6 +168,7 @@ import io.trino.sql.tree.Rollback; import io.trino.sql.tree.Rollup; import io.trino.sql.tree.Row; import io.trino.sql.tree.RowDataType; +import io.trino.sql.tree.RowPattern; import io.trino.sql.tree.SampledRelation; import io.trino.sql.tree.SearchedCaseExpression; import io.trino.sql.tree.Select; @@ -174,12 +193,14 @@ import io.trino.sql.tree.ShowTables; import io.trino.sql.tree.SimpleCaseExpression; import io.trino.sql.tree.SimpleGroupBy; import io.trino.sql.tree.SingleColumn; +import io.trino.sql.tree.SkipTo; import io.trino.sql.tree.SortItem; import io.trino.sql.tree.StartTransaction; import io.trino.sql.tree.Statement; import io.trino.sql.tree.StringLiteral; import io.trino.sql.tree.SubqueryExpression; import io.trino.sql.tree.SubscriptExpression; +import io.trino.sql.tree.SubsetDefinition; import io.trino.sql.tree.Table; import io.trino.sql.tree.TableElement; import io.trino.sql.tree.TableSubquery; @@ -195,6 +216,7 @@ import io.trino.sql.tree.Update; import io.trino.sql.tree.UpdateAssignment; import io.trino.sql.tree.Use; import io.trino.sql.tree.Values; +import io.trino.sql.tree.VariableDefinition; import io.trino.sql.tree.WhenClause; import io.trino.sql.tree.Window; import io.trino.sql.tree.WindowDefinition; @@ -203,6 +225,8 @@ import io.trino.sql.tree.WindowReference; import io.trino.sql.tree.WindowSpecification; import io.trino.sql.tree.With; import io.trino.sql.tree.WithQuery; +import io.trino.sql.tree.ZeroOrMoreQuantifier; +import io.trino.sql.tree.ZeroOrOneQuantifier; import io.trino.sql.util.IntervalLiteralUtil; import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.Token; @@ -217,12 +241,25 @@ import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.collect.Iterables.getOnlyElement; import static io.trino.sql.QueryUtil.functionCall; import static io.trino.sql.QueryUtil.identifier; -import static io.trino.sql.QueryUtil.query; import static io.trino.sql.QueryUtil.selectAll; import static io.trino.sql.QueryUtil.selectList; import static io.trino.sql.QueryUtil.simpleQuery; import static io.trino.sql.parser.SqlBaseParser.TIME; import static io.trino.sql.parser.SqlBaseParser.TIMESTAMP; +import static io.trino.sql.tree.AnchorPattern.Type.PARTITION_END; +import static io.trino.sql.tree.AnchorPattern.Type.PARTITION_START; +import static io.trino.sql.tree.PatternRecognitionRelation.RowsPerMatch.ALL_OMIT_EMPTY; +import static io.trino.sql.tree.PatternRecognitionRelation.RowsPerMatch.ALL_SHOW_EMPTY; +import static io.trino.sql.tree.PatternRecognitionRelation.RowsPerMatch.ALL_WITH_UNMATCHED; +import static io.trino.sql.tree.PatternRecognitionRelation.RowsPerMatch.ONE; +import static io.trino.sql.tree.PatternSearchMode.Mode.INITIAL; +import static io.trino.sql.tree.PatternSearchMode.Mode.SEEK; +import static io.trino.sql.tree.ProcessingMode.Mode.FINAL; +import static io.trino.sql.tree.ProcessingMode.Mode.RUNNING; +import static io.trino.sql.tree.SkipTo.skipPastLastRow; +import static io.trino.sql.tree.SkipTo.skipToFirst; +import static io.trino.sql.tree.SkipTo.skipToLast; +import static io.trino.sql.tree.SkipTo.skipToNextRow; import static io.trino.sql.util.EscapedCharsUtil.replaceEscapedChars; import static java.lang.String.format; import static java.util.Locale.ENGLISH; @@ -265,6 +302,12 @@ class AstBuilder return visit(context.pathSpecification()); } + @Override + public Node visitStandaloneRowPattern(SqlBaseParser.StandaloneRowPatternContext context) + { + return visit(context.rowPattern()); + } + // ******************* statements ********************** @Override @@ -805,7 +848,7 @@ class AstBuilder rowCount = new LongLiteral(getLocation(context.offset.INTEGER_VALUE()), context.offset.getText()); } else { - rowCount = new Parameter(getLocation(context.offset.PARAMETER()), parameterPosition); + rowCount = new Parameter(getLocation(context.offset.QUESTION_MARK()), parameterPosition); parameterPosition++; } offset = Optional.of(new Offset(Optional.of(getLocation(context.OFFSET())), rowCount)); @@ -819,7 +862,7 @@ class AstBuilder rowCount = Optional.of(new LongLiteral(getLocation(context.fetchFirst.INTEGER_VALUE()), context.fetchFirst.getText())); } else { - rowCount = Optional.of(new Parameter(getLocation(context.fetchFirst.PARAMETER()), parameterPosition)); + rowCount = Optional.of(new Parameter(getLocation(context.fetchFirst.QUESTION_MARK()), parameterPosition)); parameterPosition++; } } @@ -837,7 +880,7 @@ class AstBuilder rowCount = new LongLiteral(getLocation(context.limit.rowCount().INTEGER_VALUE()), context.limit.getText()); } else { - rowCount = new Parameter(getLocation(context.limit.rowCount().PARAMETER()), parameterPosition); + rowCount = new Parameter(getLocation(context.limit.rowCount().QUESTION_MARK()), parameterPosition); parameterPosition++; } @@ -1125,8 +1168,8 @@ class AstBuilder @Override public Node visitShowStatsForQuery(SqlBaseParser.ShowStatsForQueryContext context) { - QuerySpecification specification = (QuerySpecification) visitQuerySpecification(context.querySpecification()); - return new ShowStats(Optional.of(getLocation(context)), new TableSubquery(query(specification))); + Query query = (Query) visit(context.query()); + return new ShowStats(Optional.of(getLocation(context)), new TableSubquery(query)); } @Override @@ -1404,7 +1447,7 @@ class AstBuilder @Override public Node visitSampledRelation(SqlBaseParser.SampledRelationContext context) { - Relation child = (Relation) visit(context.aliasedRelation()); + Relation child = (Relation) visit(context.patternRecognition()); if (context.TABLESAMPLE() == null) { return child; @@ -1417,6 +1460,114 @@ class AstBuilder (Expression) visit(context.percentage)); } + @Override + public Node visitPatternRecognition(SqlBaseParser.PatternRecognitionContext context) + { + Relation child = (Relation) visit(context.aliasedRelation()); + + if (context.MATCH_RECOGNIZE() == null) { + return child; + } + + Optional orderBy = Optional.empty(); + if (context.ORDER() != null) { + orderBy = Optional.of(new OrderBy(getLocation(context.ORDER()), visit(context.sortItem(), SortItem.class))); + } + + Optional searchMode = Optional.empty(); + if (context.INITIAL() != null) { + searchMode = Optional.of(new PatternSearchMode(getLocation(context.INITIAL()), INITIAL)); + } + else if (context.SEEK() != null) { + searchMode = Optional.of(new PatternSearchMode(getLocation(context.SEEK()), SEEK)); + } + + PatternRecognitionRelation relation = new PatternRecognitionRelation( + getLocation(context), + child, + visit(context.partition, Expression.class), + orderBy, + visit(context.measureDefinition(), MeasureDefinition.class), + getRowsPerMatch(context.rowsPerMatch()), + visitIfPresent(context.skipTo(), SkipTo.class), + searchMode, + (RowPattern) visit(context.rowPattern()), + visit(context.subsetDefinition(), SubsetDefinition.class), + visit(context.variableDefinition(), VariableDefinition.class)); + + if (context.identifier() == null) { + return relation; + } + + List aliases = null; + if (context.columnAliases() != null) { + aliases = visit(context.columnAliases().identifier(), Identifier.class); + } + + return new AliasedRelation(getLocation(context), relation, (Identifier) visit(context.identifier()), aliases); + } + + @Override + public Node visitMeasureDefinition(SqlBaseParser.MeasureDefinitionContext context) + { + return new MeasureDefinition(getLocation(context), (Expression) visit(context.expression()), (Identifier) visit(context.identifier())); + } + + private Optional getRowsPerMatch(SqlBaseParser.RowsPerMatchContext context) + { + if (context == null) { + return Optional.empty(); + } + + if (context.ONE() != null) { + return Optional.of(ONE); + } + + if (context.emptyMatchHandling() == null) { + return Optional.of(ALL_SHOW_EMPTY); + } + + if (context.emptyMatchHandling().SHOW() != null) { + return Optional.of(ALL_SHOW_EMPTY); + } + + if (context.emptyMatchHandling().OMIT() != null) { + return Optional.of(ALL_OMIT_EMPTY); + } + + return Optional.of(ALL_WITH_UNMATCHED); + } + + @Override + public Node visitSkipTo(SqlBaseParser.SkipToContext context) + { + if (context.PAST() != null) { + return skipPastLastRow(getLocation(context)); + } + + if (context.NEXT() != null) { + return skipToNextRow(getLocation(context)); + } + + if (context.FIRST() != null) { + return skipToFirst(getLocation(context), (Identifier) visit(context.identifier())); + } + + return skipToLast(getLocation(context), (Identifier) visit(context.identifier())); + } + + @Override + public Node visitSubsetDefinition(SqlBaseParser.SubsetDefinitionContext context) + { + return new SubsetDefinition(getLocation(context), (Identifier) visit(context.name), visit(context.union, Identifier.class)); + } + + @Override + public Node visitVariableDefinition(SqlBaseParser.VariableDefinitionContext context) + { + return new VariableDefinition(getLocation(context), (Identifier) visit(context.identifier()), (Expression) visit(context.expression())); + } + @Override public Node visitAliasedRelation(SqlBaseParser.AliasedRelationContext context) { @@ -1752,6 +1903,18 @@ class AstBuilder return new CurrentTime(getLocation(context), function); } + @Override + public Node visitCurrentCatalog(SqlBaseParser.CurrentCatalogContext context) + { + return new CurrentCatalog(getLocation(context.CURRENT_CATALOG())); + } + + @Override + public Node visitCurrentSchema(SqlBaseParser.CurrentSchemaContext context) + { + return new CurrentSchema(getLocation(context.CURRENT_SCHEMA())); + } + @Override public Node visitCurrentUser(SqlBaseParser.CurrentUserContext context) { @@ -1871,11 +2034,14 @@ class AstBuilder SqlBaseParser.NullTreatmentContext nullTreatment = context.nullTreatment(); + SqlBaseParser.ProcessingModeContext processingMode = context.processingMode(); + if (name.toString().equalsIgnoreCase("if")) { check(context.expression().size() == 2 || context.expression().size() == 3, "Invalid number of arguments for 'if' function", context); check(!window.isPresent(), "OVER clause not valid for 'if' function", context); check(!distinct, "DISTINCT not valid for 'if' function", context); check(nullTreatment == null, "Null treatment clause not valid for 'if' function", context); + check(processingMode == null, "Running or final semantics not valid for 'if' function", context); check(!filter.isPresent(), "FILTER not valid for 'if' function", context); Expression elseExpression = null; @@ -1895,6 +2061,7 @@ class AstBuilder check(!window.isPresent(), "OVER clause not valid for 'nullif' function", context); check(!distinct, "DISTINCT not valid for 'nullif' function", context); check(nullTreatment == null, "Null treatment clause not valid for 'nullif' function", context); + check(processingMode == null, "Running or final semantics not valid for 'nullif' function", context); check(!filter.isPresent(), "FILTER not valid for 'nullif' function", context); return new NullIfExpression( @@ -1908,6 +2075,7 @@ class AstBuilder check(!window.isPresent(), "OVER clause not valid for 'coalesce' function", context); check(!distinct, "DISTINCT not valid for 'coalesce' function", context); check(nullTreatment == null, "Null treatment clause not valid for 'coalesce' function", context); + check(processingMode == null, "Running or final semantics not valid for 'coalesce' function", context); check(!filter.isPresent(), "FILTER not valid for 'coalesce' function", context); return new CoalesceExpression(getLocation(context), visit(context.expression(), Expression.class)); @@ -1918,6 +2086,7 @@ class AstBuilder check(!window.isPresent(), "OVER clause not valid for 'try' function", context); check(!distinct, "DISTINCT not valid for 'try' function", context); check(nullTreatment == null, "Null treatment clause not valid for 'try' function", context); + check(processingMode == null, "Running or final semantics not valid for 'try' function", context); check(!filter.isPresent(), "FILTER not valid for 'try' function", context); return new TryExpression(getLocation(context), (Expression) visit(getOnlyElement(context.expression()))); @@ -1928,6 +2097,7 @@ class AstBuilder check(!window.isPresent(), "OVER clause not valid for 'format' function", context); check(!distinct, "DISTINCT not valid for 'format' function", context); check(nullTreatment == null, "Null treatment clause not valid for 'format' function", context); + check(processingMode == null, "Running or final semantics not valid for 'format' function", context); check(!filter.isPresent(), "FILTER not valid for 'format' function", context); return new Format(getLocation(context), visit(context.expression(), Expression.class)); @@ -1938,6 +2108,7 @@ class AstBuilder check(!window.isPresent(), "OVER clause not valid for '$internal$bind' function", context); check(!distinct, "DISTINCT not valid for '$internal$bind' function", context); check(nullTreatment == null, "Null treatment clause not valid for '$internal$bind' function", context); + check(processingMode == null, "Running or final semantics not valid for '$internal$bind' function", context); check(!filter.isPresent(), "FILTER not valid for '$internal$bind' function", context); int numValues = context.expression().size() - 1; @@ -1962,6 +2133,16 @@ class AstBuilder } } + Optional mode = Optional.empty(); + if (processingMode != null) { + if (processingMode.RUNNING() != null) { + mode = Optional.of(new ProcessingMode(getLocation(processingMode), RUNNING)); + } + else if (processingMode.FINAL() != null) { + mode = Optional.of(new ProcessingMode(getLocation(processingMode), FINAL)); + } + } + return new FunctionCall( Optional.of(getLocation(context)), name, @@ -1970,6 +2151,7 @@ class AstBuilder orderBy, distinct, nulls, + mode, visit(context.expression(), Expression.class)); } @@ -2103,6 +2285,114 @@ class AstBuilder return new Identifier(getLocation(context), identifier, true); } + @Override + public Node visitPatternAlternation(SqlBaseParser.PatternAlternationContext context) + { + List parts = visit(context.rowPattern(), RowPattern.class); + return new PatternAlternation(getLocation(context), parts); + } + + @Override + public Node visitPatternConcatenation(SqlBaseParser.PatternConcatenationContext context) + { + List parts = visit(context.rowPattern(), RowPattern.class); + return new PatternConcatenation(getLocation(context), parts); + } + + @Override + public Node visitQuantifiedPrimary(SqlBaseParser.QuantifiedPrimaryContext context) + { + RowPattern primary = (RowPattern) visit(context.patternPrimary()); + if (context.patternQuantifier() != null) { + return new QuantifiedPattern(getLocation(context), primary, (PatternQuantifier) visit(context.patternQuantifier())); + } + return primary; + } + + @Override + public Node visitPatternVariable(SqlBaseParser.PatternVariableContext context) + { + return new PatternVariable(getLocation(context), (Identifier) visit(context.identifier())); + } + + @Override + public Node visitEmptyPattern(SqlBaseParser.EmptyPatternContext context) + { + return new EmptyPattern(getLocation(context)); + } + + @Override + public Node visitPatternPermutation(SqlBaseParser.PatternPermutationContext context) + { + return new PatternPermutation(getLocation(context), visit(context.rowPattern(), RowPattern.class)); + } + + @Override + public Node visitGroupedPattern(SqlBaseParser.GroupedPatternContext context) + { + // skip parentheses + return visit(context.rowPattern()); + } + + @Override + public Node visitPartitionStartAnchor(SqlBaseParser.PartitionStartAnchorContext context) + { + return new AnchorPattern(getLocation(context), PARTITION_START); + } + + @Override + public Node visitPartitionEndAnchor(SqlBaseParser.PartitionEndAnchorContext context) + { + return new AnchorPattern(getLocation(context), PARTITION_END); + } + + @Override + public Node visitExcludedPattern(SqlBaseParser.ExcludedPatternContext context) + { + return new ExcludedPattern(getLocation(context), (RowPattern) visit(context.rowPattern())); + } + + @Override + public Node visitZeroOrMoreQuantifier(SqlBaseParser.ZeroOrMoreQuantifierContext context) + { + boolean greedy = context.reluctant == null; + return new ZeroOrMoreQuantifier(getLocation(context), greedy); + } + + @Override + public Node visitOneOrMoreQuantifier(SqlBaseParser.OneOrMoreQuantifierContext context) + { + boolean greedy = context.reluctant == null; + return new OneOrMoreQuantifier(getLocation(context), greedy); + } + + @Override + public Node visitZeroOrOneQuantifier(SqlBaseParser.ZeroOrOneQuantifierContext context) + { + boolean greedy = context.reluctant == null; + return new ZeroOrOneQuantifier(getLocation(context), greedy); + } + + @Override + public Node visitRangeQuantifier(SqlBaseParser.RangeQuantifierContext context) + { + boolean greedy = context.reluctant == null; + + Optional atLeast = Optional.empty(); + Optional atMost = Optional.empty(); + if (context.exactly != null) { + atLeast = Optional.of(new LongLiteral(getLocation(context.exactly), context.exactly.getText())); + atMost = Optional.of(new LongLiteral(getLocation(context.exactly), context.exactly.getText())); + } + if (context.atLeast != null) { + atLeast = Optional.of(new LongLiteral(getLocation(context.atLeast), context.atLeast.getText())); + } + if (context.atMost != null) { + atMost = Optional.of(new LongLiteral(getLocation(context.atMost), context.atMost.getText())); + } + return new RangeQuantifier(getLocation(context), greedy, atLeast, atMost); + } + // ************** literals ************** @Override @@ -2157,7 +2447,8 @@ class AstBuilder if (type.equalsIgnoreCase("decimal")) { return new DecimalLiteral(getLocation(context), value); } - if (type.equalsIgnoreCase("char")) { + // bpchar for PostgreSQL wire protocol, handle blank-padding char + if (type.equalsIgnoreCase("char") || type.equalsIgnoreCase("bpchar")) { return new CharLiteral(getLocation(context), value); } // for PostgreSQL wire protocol, handle interval pattern @@ -2219,7 +2510,7 @@ class AstBuilder @Override public Node visitParameter(SqlBaseParser.ParameterContext context) { - io.trino.sql.tree.Parameter parameter = new io.trino.sql.tree.Parameter(getLocation(context), parameterPosition); + Parameter parameter = new Parameter(getLocation(context), parameterPosition); parameterPosition++; return parameter; } diff --git a/core/trino-parser/src/main/java/io/trino/sql/parser/SqlParser.java b/core/trino-parser/src/main/java/io/trino/sql/parser/SqlParser.java index 8db618609..3b8c856de 100644 --- a/core/trino-parser/src/main/java/io/trino/sql/parser/SqlParser.java +++ b/core/trino-parser/src/main/java/io/trino/sql/parser/SqlParser.java @@ -17,6 +17,7 @@ import io.trino.sql.tree.DataType; import io.trino.sql.tree.Expression; import io.trino.sql.tree.Node; import io.trino.sql.tree.PathSpecification; +import io.trino.sql.tree.RowPattern; import io.trino.sql.tree.Statement; import org.antlr.v4.runtime.BaseErrorListener; import org.antlr.v4.runtime.CharStreams; @@ -99,6 +100,11 @@ public class SqlParser return (PathSpecification) invokeParser("path specification", expression, SqlBaseParser::standalonePathSpecification, new ParsingOptions()); } + public RowPattern createRowPattern(String pattern) + { + return (RowPattern) invokeParser("row pattern", pattern, SqlBaseParser::standaloneRowPattern, new ParsingOptions()); + } + private Node invokeParser(String name, String sql, Function parseFunction, ParsingOptions parsingOptions) { try { diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/AddColumn.java b/core/trino-parser/src/main/java/io/trino/sql/tree/AddColumn.java index c945ea7fe..5ed85a0f0 100644 --- a/core/trino-parser/src/main/java/io/trino/sql/tree/AddColumn.java +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/AddColumn.java @@ -43,7 +43,7 @@ public class AddColumn private AddColumn(Optional location, QualifiedName name, ColumnDefinition column, boolean tableExists, boolean columnNotExists) { super(location); - this.name = requireNonNull(name, "table is null"); + this.name = requireNonNull(name, "name is null"); this.column = requireNonNull(column, "column is null"); this.tableExists = tableExists; this.columnNotExists = columnNotExists; diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/Analyze.java b/core/trino-parser/src/main/java/io/trino/sql/tree/Analyze.java index 0d3465e19..9a3515235 100644 --- a/core/trino-parser/src/main/java/io/trino/sql/tree/Analyze.java +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/Analyze.java @@ -41,7 +41,7 @@ public class Analyze private Analyze(Optional location, QualifiedName tableName, List properties) { super(location); - this.tableName = requireNonNull(tableName, "table is null"); + this.tableName = requireNonNull(tableName, "tableName is null"); this.properties = ImmutableList.copyOf(requireNonNull(properties, "properties is null")); } diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/AnchorPattern.java b/core/trino-parser/src/main/java/io/trino/sql/tree/AnchorPattern.java new file mode 100644 index 000000000..9ee05d898 --- /dev/null +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/AnchorPattern.java @@ -0,0 +1,96 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.sql.tree; + +import com.google.common.collect.ImmutableList; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import static com.google.common.base.MoreObjects.toStringHelper; +import static java.util.Objects.requireNonNull; + +public class AnchorPattern + extends RowPattern +{ + public enum Type + { + PARTITION_START, + PARTITION_END + } + + private final Type type; + + public AnchorPattern(NodeLocation location, Type type) + { + this(Optional.of(location), type); + } + + private AnchorPattern(Optional location, Type type) + { + super(location); + this.type = requireNonNull(type, "type is null"); + } + + public Type getType() + { + return type; + } + + @Override + public R accept(AstVisitor visitor, C context) + { + return visitor.visitAnchorPattern(this, context); + } + + @Override + public List getChildren() + { + return ImmutableList.of(); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) { + return true; + } + if ((obj == null) || (getClass() != obj.getClass())) { + return false; + } + AnchorPattern o = (AnchorPattern) obj; + return Objects.equals(type, o.type); + } + + @Override + public int hashCode() + { + return getClass().hashCode(); + } + + @Override + public String toString() + { + return toStringHelper(this) + .add("type", type) + .toString(); + } + + @Override + public boolean shallowEquals(Node other) + { + return sameClass(this, other); + } +} diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/AstVisitor.java b/core/trino-parser/src/main/java/io/trino/sql/tree/AstVisitor.java index 4571455a5..dac58fc6b 100644 --- a/core/trino-parser/src/main/java/io/trino/sql/tree/AstVisitor.java +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/AstVisitor.java @@ -292,6 +292,11 @@ public abstract class AstVisitor return visitExpression(node, context); } + protected R visitProcessingMode(ProcessingMode node, C context) + { + return visitNode(node, context); + } + protected R visitLambdaExpression(LambdaExpression node, C context) { return visitExpression(node, context); @@ -842,6 +847,16 @@ public abstract class AstVisitor return visitExpression(node, context); } + protected R visitCurrentCatalog(CurrentCatalog node, C context) + { + return visitExpression(node, context); + } + + protected R visitCurrentSchema(CurrentSchema node, C context) + { + return visitExpression(node, context); + } + protected R visitCurrentUser(CurrentUser node, C context) { return visitExpression(node, context); @@ -916,4 +931,109 @@ public abstract class AstVisitor { return visitStatement(node, context); } + + protected R visitMeasureDefinition(MeasureDefinition node, C context) + { + return visitNode(node, context); + } + + protected R visitSkipTo(SkipTo node, C context) + { + return visitNode(node, context); + } + + protected R visitPatternSearchMode(PatternSearchMode node, C context) + { + return visitNode(node, context); + } + + protected R visitSubsetDefinition(SubsetDefinition node, C context) + { + return visitNode(node, context); + } + + protected R visitVariableDefinition(VariableDefinition node, C context) + { + return visitNode(node, context); + } + + protected R visitPatternRecognitionRelation(PatternRecognitionRelation node, C context) + { + return visitRelation(node, context); + } + + protected R visitLabelDereference(LabelDereference node, C context) + { + return visitExpression(node, context); + } + + protected R visitRowPattern(RowPattern node, C context) + { + return visitNode(node, context); + } + + protected R visitPatternAlternation(PatternAlternation node, C context) + { + return visitRowPattern(node, context); + } + + protected R visitPatternConcatenation(PatternConcatenation node, C context) + { + return visitRowPattern(node, context); + } + + protected R visitQuantifiedPattern(QuantifiedPattern node, C context) + { + return visitRowPattern(node, context); + } + + protected R visitAnchorPattern(AnchorPattern node, C context) + { + return visitRowPattern(node, context); + } + + protected R visitEmptyPattern(EmptyPattern node, C context) + { + return visitRowPattern(node, context); + } + + protected R visitExcludedPattern(ExcludedPattern node, C context) + { + return visitRowPattern(node, context); + } + + protected R visitPatternPermutation(PatternPermutation node, C context) + { + return visitRowPattern(node, context); + } + + protected R visitPatternVariable(PatternVariable node, C context) + { + return visitRowPattern(node, context); + } + + protected R visitPatternQuantifier(PatternQuantifier node, C context) + { + return visitNode(node, context); + } + + protected R visitZeroOrMoreQuantifier(ZeroOrMoreQuantifier node, C context) + { + return visitPatternQuantifier(node, context); + } + + protected R visitOneOrMoreQuantifier(OneOrMoreQuantifier node, C context) + { + return visitPatternQuantifier(node, context); + } + + protected R visitZeroOrOneQuantifier(ZeroOrOneQuantifier node, C context) + { + return visitPatternQuantifier(node, context); + } + + protected R visitRangeQuantifier(RangeQuantifier node, C context) + { + return visitPatternQuantifier(node, context); + } } diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/BindExpression.java b/core/trino-parser/src/main/java/io/trino/sql/tree/BindExpression.java index 2f8dafbcc..3f25ed1bd 100644 --- a/core/trino-parser/src/main/java/io/trino/sql/tree/BindExpression.java +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/BindExpression.java @@ -65,7 +65,7 @@ public class BindExpression private BindExpression(Optional location, List values, Expression function) { super(location); - this.values = requireNonNull(values, "value is null"); + this.values = requireNonNull(values, "values is null"); this.function = requireNonNull(function, "function is null"); } diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/Comment.java b/core/trino-parser/src/main/java/io/trino/sql/tree/Comment.java index 7f8b5c8c1..9b4f8b26b 100644 --- a/core/trino-parser/src/main/java/io/trino/sql/tree/Comment.java +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/Comment.java @@ -48,7 +48,7 @@ public final class Comment { super(location); this.type = requireNonNull(type, "type is null"); - this.name = requireNonNull(name, "table is null"); + this.name = requireNonNull(name, "name is null"); this.comment = requireNonNull(comment, "comment is null"); } diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/ComparisonExpression.java b/core/trino-parser/src/main/java/io/trino/sql/tree/ComparisonExpression.java index 32d01701c..71c59bbc1 100644 --- a/core/trino-parser/src/main/java/io/trino/sql/tree/ComparisonExpression.java +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/ComparisonExpression.java @@ -41,7 +41,7 @@ public class ComparisonExpression private ComparisonExpression(Optional location, Operator operator, Expression left, Expression right) { super(location); - requireNonNull(operator, "type is null"); + requireNonNull(operator, "operator is null"); requireNonNull(left, "left is null"); requireNonNull(right, "right is null"); diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/CreateTable.java b/core/trino-parser/src/main/java/io/trino/sql/tree/CreateTable.java index 5603f4714..e288062fb 100644 --- a/core/trino-parser/src/main/java/io/trino/sql/tree/CreateTable.java +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/CreateTable.java @@ -44,7 +44,7 @@ public class CreateTable private CreateTable(Optional location, QualifiedName name, List elements, boolean notExists, List properties, Optional comment) { super(location); - this.name = requireNonNull(name, "table is null"); + this.name = requireNonNull(name, "name is null"); this.elements = ImmutableList.copyOf(requireNonNull(elements, "elements is null")); this.notExists = notExists; this.properties = requireNonNull(properties, "properties is null"); diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/CurrentCatalog.java b/core/trino-parser/src/main/java/io/trino/sql/tree/CurrentCatalog.java new file mode 100644 index 000000000..41cf63718 --- /dev/null +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/CurrentCatalog.java @@ -0,0 +1,70 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.sql.tree; + +import com.google.common.collect.ImmutableList; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +public class CurrentCatalog + extends Expression +{ + public CurrentCatalog(NodeLocation location) + { + this(Optional.of(location)); + } + + private CurrentCatalog(Optional location) + { + super(location); + } + + @Override + public List getChildren() + { + return ImmutableList.of(); + } + + @Override + public R accept(AstVisitor visitor, C context) + { + return visitor.visitCurrentCatalog(this, context); + } + + @Override + public int hashCode() + { + return Objects.hash(); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) { + return true; + } + if ((obj == null) || (getClass() != obj.getClass())) { + return false; + } + return true; + } + + @Override + public boolean shallowEquals(Node other) + { + return sameClass(this, other); + } +} diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/CurrentSchema.java b/core/trino-parser/src/main/java/io/trino/sql/tree/CurrentSchema.java new file mode 100644 index 000000000..18c8aee4a --- /dev/null +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/CurrentSchema.java @@ -0,0 +1,70 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.sql.tree; + +import com.google.common.collect.ImmutableList; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +public class CurrentSchema + extends Expression +{ + public CurrentSchema(NodeLocation location) + { + this(Optional.of(location)); + } + + private CurrentSchema(Optional location) + { + super(location); + } + + @Override + public List getChildren() + { + return ImmutableList.of(); + } + + @Override + public R accept(AstVisitor visitor, C context) + { + return visitor.visitCurrentSchema(this, context); + } + + @Override + public int hashCode() + { + return Objects.hash(); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) { + return true; + } + if ((obj == null) || (getClass() != obj.getClass())) { + return false; + } + return true; + } + + @Override + public boolean shallowEquals(Node other) + { + return sameClass(this, other); + } +} diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/CurrentTime.java b/core/trino-parser/src/main/java/io/trino/sql/tree/CurrentTime.java index 15d132156..c111018e2 100644 --- a/core/trino-parser/src/main/java/io/trino/sql/tree/CurrentTime.java +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/CurrentTime.java @@ -71,7 +71,7 @@ public class CurrentTime private CurrentTime(Optional location, Function function, Integer precision) { super(location); - requireNonNull(function, "type is null"); + requireNonNull(function, "function is null"); this.function = function; this.precision = precision; } diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/DefaultTraversalVisitor.java b/core/trino-parser/src/main/java/io/trino/sql/tree/DefaultTraversalVisitor.java index 267d1bcbd..39458d059 100644 --- a/core/trino-parser/src/main/java/io/trino/sql/tree/DefaultTraversalVisitor.java +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/DefaultTraversalVisitor.java @@ -801,4 +801,66 @@ public abstract class DefaultTraversalVisitor return null; } + + @Override + protected Void visitExcludedPattern(ExcludedPattern node, C context) + { + process(node.getPattern(), context); + + return null; + } + + @Override + protected Void visitPatternAlternation(PatternAlternation node, C context) + { + for (RowPattern rowPattern : node.getPatterns()) { + process(rowPattern, context); + } + + return null; + } + + @Override + protected Void visitPatternConcatenation(PatternConcatenation node, C context) + { + for (RowPattern rowPattern : node.getPatterns()) { + process(rowPattern, context); + } + + return null; + } + + @Override + protected Void visitPatternPermutation(PatternPermutation node, C context) + { + for (RowPattern rowPattern : node.getPatterns()) { + process(rowPattern, context); + } + + return null; + } + + @Override + protected Void visitPatternVariable(PatternVariable node, C context) + { + process(node.getName(), context); + + return null; + } + + @Override + protected Void visitQuantifiedPattern(QuantifiedPattern node, C context) + { + process(node.getPattern(), context); + + return null; + } + + @Override + protected Void visitLabelDereference(LabelDereference node, C context) + { + process(node.getReference(), context); + + return null; + } } diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/EmptyPattern.java b/core/trino-parser/src/main/java/io/trino/sql/tree/EmptyPattern.java new file mode 100644 index 000000000..ffb6480bd --- /dev/null +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/EmptyPattern.java @@ -0,0 +1,78 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.sql.tree; + +import com.google.common.collect.ImmutableList; + +import java.util.List; +import java.util.Optional; + +import static com.google.common.base.MoreObjects.toStringHelper; + +public class EmptyPattern + extends RowPattern +{ + public EmptyPattern(NodeLocation location) + { + this(Optional.of(location)); + } + + private EmptyPattern(Optional location) + { + super(location); + } + + @Override + public R accept(AstVisitor visitor, C context) + { + return visitor.visitEmptyPattern(this, context); + } + + @Override + public List getChildren() + { + return ImmutableList.of(); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) { + return true; + } + if ((obj == null) || (getClass() != obj.getClass())) { + return false; + } + return true; + } + + @Override + public int hashCode() + { + return getClass().hashCode(); + } + + @Override + public String toString() + { + return toStringHelper(this) + .toString(); + } + + @Override + public boolean shallowEquals(Node other) + { + return sameClass(this, other); + } +} diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/ExcludedPattern.java b/core/trino-parser/src/main/java/io/trino/sql/tree/ExcludedPattern.java new file mode 100644 index 000000000..33ca22b56 --- /dev/null +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/ExcludedPattern.java @@ -0,0 +1,90 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.sql.tree; + +import com.google.common.collect.ImmutableList; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import static com.google.common.base.MoreObjects.toStringHelper; +import static java.util.Objects.requireNonNull; + +public class ExcludedPattern + extends RowPattern +{ + private final RowPattern pattern; + + public ExcludedPattern(NodeLocation location, RowPattern pattern) + { + this(Optional.of(location), pattern); + } + + private ExcludedPattern(Optional location, RowPattern pattern) + { + super(location); + this.pattern = requireNonNull(pattern, "pattern is null"); + } + + public RowPattern getPattern() + { + return pattern; + } + + @Override + public R accept(AstVisitor visitor, C context) + { + return visitor.visitExcludedPattern(this, context); + } + + @Override + public List getChildren() + { + return ImmutableList.of(pattern); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) { + return true; + } + if ((obj == null) || (getClass() != obj.getClass())) { + return false; + } + ExcludedPattern o = (ExcludedPattern) obj; + return Objects.equals(pattern, o.pattern); + } + + @Override + public int hashCode() + { + return Objects.hash(pattern); + } + + @Override + public String toString() + { + return toStringHelper(this) + .add("pattern", pattern) + .toString(); + } + + @Override + public boolean shallowEquals(Node other) + { + return sameClass(this, other); + } +} diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/Execute.java b/core/trino-parser/src/main/java/io/trino/sql/tree/Execute.java index 05273d7fa..908f16d7e 100644 --- a/core/trino-parser/src/main/java/io/trino/sql/tree/Execute.java +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/Execute.java @@ -42,7 +42,7 @@ public class Execute { super(location); this.name = requireNonNull(name, "name is null"); - this.parameters = requireNonNull(ImmutableList.copyOf(parameters), "parameters is null"); + this.parameters = ImmutableList.copyOf(requireNonNull(parameters, "parameters is null")); } public Identifier getName() diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/ExpressionRewriter.java b/core/trino-parser/src/main/java/io/trino/sql/tree/ExpressionRewriter.java index c511991dd..7e18d1a17 100644 --- a/core/trino-parser/src/main/java/io/trino/sql/tree/ExpressionRewriter.java +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/ExpressionRewriter.java @@ -185,6 +185,16 @@ public class ExpressionRewriter return rewriteExpression(node, context, treeRewriter); } + public Expression rewriteCurrentCatalog(CurrentCatalog node, C context, ExpressionTreeRewriter treeRewriter) + { + return rewriteExpression(node, context, treeRewriter); + } + + public Expression rewriteCurrentSchema(CurrentSchema node, C context, ExpressionTreeRewriter treeRewriter) + { + return rewriteExpression(node, context, treeRewriter); + } + public Expression rewriteCurrentUser(CurrentUser node, C context, ExpressionTreeRewriter treeRewriter) { return rewriteExpression(node, context, treeRewriter); @@ -244,4 +254,9 @@ public class ExpressionRewriter { return rewriteExpression(node, context, treeRewriter); } + + public Expression rewriteLabelDereference(LabelDereference node, C context, ExpressionTreeRewriter treeRewriter) + { + return rewriteExpression(node, context, treeRewriter); + } } diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/ExpressionTreeRewriter.java b/core/trino-parser/src/main/java/io/trino/sql/tree/ExpressionTreeRewriter.java index d60a3886d..b89e09297 100644 --- a/core/trino-parser/src/main/java/io/trino/sql/tree/ExpressionTreeRewriter.java +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/ExpressionTreeRewriter.java @@ -25,7 +25,7 @@ import static com.google.common.collect.ImmutableList.toImmutableList; public final class ExpressionTreeRewriter { private final ExpressionRewriter rewriter; - private final AstVisitor> visitor; + private final AstVisitor> visitor; public static T rewriteWith(ExpressionRewriter rewriter, T node) { @@ -68,7 +68,7 @@ public final class ExpressionTreeRewriter } private class RewritingVisitor - extends AstVisitor> + extends AstVisitor> { @Override protected Expression visitExpression(Expression node, Context context) @@ -547,7 +547,16 @@ public final class ExpressionTreeRewriter if (!sameElements(node.getArguments(), arguments) || !sameElements(rewrittenWindow, node.getWindow()) || !sameElements(filter, node.getFilter())) { - return new FunctionCall(node.getLocation(), node.getName(), rewrittenWindow, filter, node.getOrderBy().map(orderBy -> rewriteOrderBy(orderBy, context)), node.isDistinct(), node.getNullTreatment(), arguments); + return new FunctionCall( + node.getLocation(), + node.getName(), + rewrittenWindow, + filter, + node.getOrderBy().map(orderBy -> rewriteOrderBy(orderBy, context)), + node.isDistinct(), + node.getNullTreatment(), + node.getProcessingMode(), + arguments); } return node; } @@ -989,6 +998,32 @@ public final class ExpressionTreeRewriter return node; } + @Override + protected Expression visitCurrentCatalog(CurrentCatalog node, Context context) + { + if (!context.isDefaultRewrite()) { + Expression result = rewriter.rewriteCurrentCatalog(node, context.get(), ExpressionTreeRewriter.this); + if (result != null) { + return result; + } + } + + return node; + } + + @Override + protected Expression visitCurrentSchema(CurrentSchema node, Context context) + { + if (!context.isDefaultRewrite()) { + Expression result = rewriter.rewriteCurrentSchema(node, context.get(), ExpressionTreeRewriter.this); + if (result != null) { + return result; + } + } + + return node; + } + @Override protected Expression visitCurrentUser(CurrentUser node, Context context) { @@ -1032,6 +1067,24 @@ public final class ExpressionTreeRewriter return node; } + + @Override + protected Expression visitLabelDereference(LabelDereference node, Context context) + { + if (!context.isDefaultRewrite()) { + Expression result = rewriter.rewriteLabelDereference(node, context.get(), ExpressionTreeRewriter.this); + if (result != null) { + return result; + } + } + + SymbolReference reference = rewrite(node.getReference(), context.get()); + if (node.getReference() != reference) { + return new LabelDereference(node.getLabel(), reference); + } + + return node; + } } public static class Context diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/FunctionCall.java b/core/trino-parser/src/main/java/io/trino/sql/tree/FunctionCall.java index fd94228cb..a24d0787a 100644 --- a/core/trino-parser/src/main/java/io/trino/sql/tree/FunctionCall.java +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/FunctionCall.java @@ -31,16 +31,17 @@ public class FunctionCall private final Optional orderBy; private final boolean distinct; private final Optional nullTreatment; + private final Optional processingMode; private final List arguments; public FunctionCall(QualifiedName name, List arguments) { - this(Optional.empty(), name, Optional.empty(), Optional.empty(), Optional.empty(), false, Optional.empty(), arguments); + this(Optional.empty(), name, Optional.empty(), Optional.empty(), Optional.empty(), false, Optional.empty(), Optional.empty(), arguments); } public FunctionCall(NodeLocation location, QualifiedName name, List arguments) { - this(Optional.of(location), name, Optional.empty(), Optional.empty(), Optional.empty(), false, Optional.empty(), arguments); + this(Optional.of(location), name, Optional.empty(), Optional.empty(), Optional.empty(), false, Optional.empty(), Optional.empty(), arguments); } public FunctionCall( @@ -51,6 +52,7 @@ public class FunctionCall Optional orderBy, boolean distinct, Optional nullTreatment, + Optional processingMode, List arguments) { super(location); @@ -60,6 +62,7 @@ public class FunctionCall requireNonNull(filter, "filter is null"); requireNonNull(orderBy, "orderBy is null"); requireNonNull(nullTreatment, "nullTreatment is null"); + requireNonNull(processingMode, "processingMode is null"); requireNonNull(arguments, "arguments is null"); this.name = name; @@ -68,6 +71,7 @@ public class FunctionCall this.orderBy = orderBy; this.distinct = distinct; this.nullTreatment = nullTreatment; + this.processingMode = processingMode; this.arguments = arguments; } @@ -96,6 +100,11 @@ public class FunctionCall return nullTreatment; } + public Optional getProcessingMode() + { + return processingMode; + } + public List getArguments() { return arguments; @@ -139,13 +148,14 @@ public class FunctionCall Objects.equals(orderBy, o.orderBy) && Objects.equals(distinct, o.distinct) && Objects.equals(nullTreatment, o.nullTreatment) && + Objects.equals(processingMode, o.processingMode) && Objects.equals(arguments, o.arguments); } @Override public int hashCode() { - return Objects.hash(name, distinct, nullTreatment, window, filter, orderBy, arguments); + return Objects.hash(name, distinct, nullTreatment, processingMode, window, filter, orderBy, arguments); } // TODO: make this a proper Tree node so that we can report error @@ -166,6 +176,7 @@ public class FunctionCall return name.equals(otherFunction.name) && distinct == otherFunction.distinct && - nullTreatment.equals(otherFunction.nullTreatment); + nullTreatment.equals(otherFunction.nullTreatment) && + processingMode.equals(otherFunction.processingMode); } } diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/Identifier.java b/core/trino-parser/src/main/java/io/trino/sql/tree/Identifier.java index 716e085f8..c5cad342b 100644 --- a/core/trino-parser/src/main/java/io/trino/sql/tree/Identifier.java +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/Identifier.java @@ -21,6 +21,7 @@ import java.util.Optional; import java.util.regex.Pattern; import static com.google.common.base.Preconditions.checkArgument; +import static java.util.Locale.ENGLISH; import static java.util.Objects.requireNonNull; public class Identifier @@ -66,6 +67,15 @@ public class Identifier return delimited; } + public String getCanonicalValue() + { + if (isDelimited()) { + return value; + } + + return value.toUpperCase(ENGLISH); + } + @Override public R accept(AstVisitor visitor, C context) { diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/LabelDereference.java b/core/trino-parser/src/main/java/io/trino/sql/tree/LabelDereference.java new file mode 100644 index 000000000..450f34111 --- /dev/null +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/LabelDereference.java @@ -0,0 +1,93 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.sql.tree; + +import com.google.common.collect.ImmutableList; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import static java.util.Objects.requireNonNull; + +/** + * A temporary IR representation of a label-prefixed column reference + * in the context of row pattern recognition. + *

+ * It is created from a DereferenceExpression when the MEASURES or DEFINE + * expressions are rewritten using the TranslationMap: + * A.price -> DereferenceExpression("A", "price") -> LabelDereference("A", price_symbol). + * Next, the LabelDereference is processed by the LogicalIndexExtractor, + * and it is removed from the expression. + *

+ * LabelDereference is a synthetic AST node. It had to be introduced in order to carry + * the rewritten symbol (`price_symbol` in the example). The DereferenceExpression + * cannot be used for that purpose, because it only contains identifiers, and a Symbol + * cannot be safely converted to an Identifier. + */ +public class LabelDereference + extends Expression +{ + private final String label; + private final SymbolReference reference; + + public LabelDereference(String label, SymbolReference reference) + { + super(Optional.empty()); + this.label = requireNonNull(label, "label is null"); + this.reference = requireNonNull(reference, "reference is null"); + } + + public String getLabel() + { + return label; + } + + public SymbolReference getReference() + { + return reference; + } + + @Override + public R accept(AstVisitor visitor, C context) + { + return visitor.visitLabelDereference(this, context); + } + + @Override + public List getChildren() + { + return ImmutableList.of(reference); + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + LabelDereference that = (LabelDereference) o; + return Objects.equals(label, that.label) && + Objects.equals(reference, that.reference); + } + + @Override + public int hashCode() + { + return Objects.hash(label, reference); + } +} diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/MeasureDefinition.java b/core/trino-parser/src/main/java/io/trino/sql/tree/MeasureDefinition.java new file mode 100644 index 000000000..7eae5bbe3 --- /dev/null +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/MeasureDefinition.java @@ -0,0 +1,109 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.sql.tree; + +import com.google.common.collect.ImmutableList; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import static com.google.common.base.MoreObjects.toStringHelper; +import static java.util.Objects.requireNonNull; + +public class MeasureDefinition + extends Node +{ + private final Expression expression; + private final Identifier name; + + public MeasureDefinition(Expression expression, Identifier name) + { + this(Optional.empty(), expression, name); + } + + public MeasureDefinition(NodeLocation location, Expression expression, Identifier name) + { + this(Optional.of(location), expression, name); + } + + private MeasureDefinition(Optional location, Expression expression, Identifier name) + { + super(location); + this.expression = requireNonNull(expression, "expression is null"); + this.name = requireNonNull(name, "name is null"); + } + + public Expression getExpression() + { + return expression; + } + + public Identifier getName() + { + return name; + } + + @Override + public R accept(AstVisitor visitor, C context) + { + return visitor.visitMeasureDefinition(this, context); + } + + @Override + public List getChildren() + { + return ImmutableList.of(expression); + } + + @Override + public String toString() + { + return toStringHelper(this) + .add("expression", expression) + .add("name", name) + .toString(); + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + MeasureDefinition that = (MeasureDefinition) o; + return Objects.equals(expression, that.expression) && + Objects.equals(name, that.name); + } + + @Override + public int hashCode() + { + return Objects.hash(expression, name); + } + + @Override + public boolean shallowEquals(Node other) + { + if (!sameClass(this, other)) { + return false; + } + + return Objects.equals(name, ((MeasureDefinition) other).name); + } +} diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/OneOrMoreQuantifier.java b/core/trino-parser/src/main/java/io/trino/sql/tree/OneOrMoreQuantifier.java new file mode 100644 index 000000000..409a6c53a --- /dev/null +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/OneOrMoreQuantifier.java @@ -0,0 +1,41 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.sql.tree; + +import java.util.Optional; + +public class OneOrMoreQuantifier + extends PatternQuantifier +{ + public OneOrMoreQuantifier(boolean greedy) + { + this(Optional.empty(), greedy); + } + + public OneOrMoreQuantifier(NodeLocation location, boolean greedy) + { + this(Optional.of(location), greedy); + } + + public OneOrMoreQuantifier(Optional location, boolean greedy) + { + super(location, greedy); + } + + @Override + public R accept(AstVisitor visitor, C context) + { + return visitor.visitOneOrMoreQuantifier(this, context); + } +} diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/PatternAlternation.java b/core/trino-parser/src/main/java/io/trino/sql/tree/PatternAlternation.java new file mode 100644 index 000000000..9239d2c66 --- /dev/null +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/PatternAlternation.java @@ -0,0 +1,92 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.sql.tree; + +import com.google.common.collect.ImmutableList; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import static com.google.common.base.MoreObjects.toStringHelper; +import static com.google.common.base.Preconditions.checkArgument; +import static java.util.Objects.requireNonNull; + +public class PatternAlternation + extends RowPattern +{ + private final List patterns; + + public PatternAlternation(NodeLocation location, List patterns) + { + this(Optional.of(location), patterns); + } + + private PatternAlternation(Optional location, List patterns) + { + super(location); + this.patterns = requireNonNull(patterns, "patterns is null"); + checkArgument(!patterns.isEmpty(), "patterns list is empty"); + } + + public List getPatterns() + { + return patterns; + } + + @Override + public R accept(AstVisitor visitor, C context) + { + return visitor.visitPatternAlternation(this, context); + } + + @Override + public List getChildren() + { + return ImmutableList.copyOf(patterns); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) { + return true; + } + if ((obj == null) || (getClass() != obj.getClass())) { + return false; + } + PatternAlternation o = (PatternAlternation) obj; + return Objects.equals(patterns, o.patterns); + } + + @Override + public int hashCode() + { + return Objects.hash(patterns); + } + + @Override + public String toString() + { + return toStringHelper(this) + .add("patterns", patterns) + .toString(); + } + + @Override + public boolean shallowEquals(Node other) + { + return sameClass(this, other); + } +} diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/PatternConcatenation.java b/core/trino-parser/src/main/java/io/trino/sql/tree/PatternConcatenation.java new file mode 100644 index 000000000..0a62a31e5 --- /dev/null +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/PatternConcatenation.java @@ -0,0 +1,92 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.sql.tree; + +import com.google.common.collect.ImmutableList; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import static com.google.common.base.MoreObjects.toStringHelper; +import static com.google.common.base.Preconditions.checkArgument; +import static java.util.Objects.requireNonNull; + +public class PatternConcatenation + extends RowPattern +{ + private final List patterns; + + public PatternConcatenation(NodeLocation location, List patterns) + { + this(Optional.of(location), patterns); + } + + private PatternConcatenation(Optional location, List patterns) + { + super(location); + this.patterns = requireNonNull(patterns, "patterns is null"); + checkArgument(!patterns.isEmpty(), "patterns list is empty"); + } + + public List getPatterns() + { + return patterns; + } + + @Override + public R accept(AstVisitor visitor, C context) + { + return visitor.visitPatternConcatenation(this, context); + } + + @Override + public List getChildren() + { + return ImmutableList.copyOf(patterns); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) { + return true; + } + if ((obj == null) || (getClass() != obj.getClass())) { + return false; + } + PatternConcatenation o = (PatternConcatenation) obj; + return Objects.equals(patterns, o.patterns); + } + + @Override + public int hashCode() + { + return Objects.hash(patterns); + } + + @Override + public String toString() + { + return toStringHelper(this) + .add("patterns", patterns) + .toString(); + } + + @Override + public boolean shallowEquals(Node other) + { + return sameClass(this, other); + } +} diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/PatternPermutation.java b/core/trino-parser/src/main/java/io/trino/sql/tree/PatternPermutation.java new file mode 100644 index 000000000..438ec6dd2 --- /dev/null +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/PatternPermutation.java @@ -0,0 +1,92 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.sql.tree; + +import com.google.common.collect.ImmutableList; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import static com.google.common.base.MoreObjects.toStringHelper; +import static com.google.common.base.Preconditions.checkArgument; +import static java.util.Objects.requireNonNull; + +public class PatternPermutation + extends RowPattern +{ + private final List patterns; + + public PatternPermutation(NodeLocation location, List patterns) + { + this(Optional.of(location), patterns); + } + + private PatternPermutation(Optional location, List patterns) + { + super(location); + this.patterns = requireNonNull(patterns, "patterns is null"); + checkArgument(!patterns.isEmpty(), "patterns list is empty"); + } + + public List getPatterns() + { + return patterns; + } + + @Override + public R accept(AstVisitor visitor, C context) + { + return visitor.visitPatternPermutation(this, context); + } + + @Override + public List getChildren() + { + return ImmutableList.copyOf(patterns); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) { + return true; + } + if ((obj == null) || (getClass() != obj.getClass())) { + return false; + } + PatternPermutation o = (PatternPermutation) obj; + return Objects.equals(patterns, o.patterns); + } + + @Override + public int hashCode() + { + return Objects.hash(patterns); + } + + @Override + public String toString() + { + return toStringHelper(this) + .add("patterns", patterns) + .toString(); + } + + @Override + public boolean shallowEquals(Node other) + { + return sameClass(this, other); + } +} diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/PatternQuantifier.java b/core/trino-parser/src/main/java/io/trino/sql/tree/PatternQuantifier.java new file mode 100644 index 000000000..a66e0e9e4 --- /dev/null +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/PatternQuantifier.java @@ -0,0 +1,89 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.sql.tree; + +import com.google.common.collect.ImmutableList; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import static com.google.common.base.MoreObjects.toStringHelper; + +public abstract class PatternQuantifier + extends Node +{ + private final boolean greedy; + + protected PatternQuantifier(Optional location, boolean greedy) + { + super(location); + this.greedy = greedy; + } + + public boolean isGreedy() + { + return greedy; + } + + @Override + public R accept(AstVisitor visitor, C context) + { + return visitor.visitPatternQuantifier(this, context); + } + + @Override + public List getChildren() + { + return ImmutableList.of(); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) { + return true; + } + if ((obj == null) || (getClass() != obj.getClass())) { + return false; + } + PatternQuantifier o = (PatternQuantifier) obj; + return greedy == o.greedy; + } + + @Override + public int hashCode() + { + return Objects.hash(greedy); + } + + @Override + public boolean shallowEquals(Node other) + { + if (!sameClass(this, other)) { + return false; + } + + PatternQuantifier otherNode = (PatternQuantifier) other; + return greedy == otherNode.greedy; + } + + @Override + public String toString() + { + return toStringHelper(this) + .add("greedy", greedy) + .toString(); + } +} diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/PatternRecognitionRelation.java b/core/trino-parser/src/main/java/io/trino/sql/tree/PatternRecognitionRelation.java new file mode 100644 index 000000000..0dc5eb55b --- /dev/null +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/PatternRecognitionRelation.java @@ -0,0 +1,356 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.sql.tree; + +import com.google.common.collect.ImmutableList; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import static com.google.common.base.MoreObjects.toStringHelper; +import static com.google.common.base.Preconditions.checkArgument; +import static java.util.Objects.requireNonNull; + +public class PatternRecognitionRelation + extends Relation +{ + private final Relation input; + private final List partitionBy; + private final Optional orderBy; + private final List measures; + private final Optional rowsPerMatch; + private final Optional afterMatchSkipTo; + private final Optional patternSearchMode; + private final RowPattern pattern; + private final List subsets; + private final List variableDefinitions; + + public PatternRecognitionRelation( + Relation input, + List partitionBy, + Optional orderBy, + List measures, + Optional rowsPerMatch, + Optional afterMatchSkipTo, + Optional patternSearchMode, + RowPattern pattern, + List subsets, + List variableDefinitions) + { + this(Optional.empty(), input, partitionBy, orderBy, measures, rowsPerMatch, afterMatchSkipTo, patternSearchMode, pattern, subsets, variableDefinitions); + } + + public PatternRecognitionRelation( + NodeLocation location, + Relation input, + List partitionBy, + Optional orderBy, + List measures, + Optional rowsPerMatch, + Optional afterMatchSkipTo, + Optional patternSearchMode, + RowPattern pattern, + List subsets, + List variableDefinitions) + { + this(Optional.of(location), input, partitionBy, orderBy, measures, rowsPerMatch, afterMatchSkipTo, patternSearchMode, pattern, subsets, variableDefinitions); + } + + private PatternRecognitionRelation( + Optional location, + Relation input, + List partitionBy, + Optional orderBy, + List measures, + Optional rowsPerMatch, + Optional afterMatchSkipTo, + Optional patternSearchMode, + RowPattern pattern, + List subsets, + List variableDefinitions) + { + super(location); + this.input = requireNonNull(input, "input is null"); + this.partitionBy = requireNonNull(partitionBy, "partitionBy is null"); + this.orderBy = requireNonNull(orderBy, "orderBy is null"); + this.measures = requireNonNull(measures, "measures is null"); + this.rowsPerMatch = requireNonNull(rowsPerMatch, "rowsPerMatch is null"); + this.afterMatchSkipTo = requireNonNull(afterMatchSkipTo, "afterMatchSkipTo is null"); + this.patternSearchMode = requireNonNull(patternSearchMode, "patternSearchMode is null"); + this.pattern = requireNonNull(pattern, "pattern is null"); + this.subsets = requireNonNull(subsets, "subsets is null"); + requireNonNull(variableDefinitions, "variableDefinitions is null"); + checkArgument(!variableDefinitions.isEmpty(), "variableDefinitions is empty"); + this.variableDefinitions = variableDefinitions; + } + + public Relation getInput() + { + return input; + } + + public List getPartitionBy() + { + return partitionBy; + } + + public Optional getOrderBy() + { + return orderBy; + } + + public List getMeasures() + { + return measures; + } + + public Optional getRowsPerMatch() + { + return rowsPerMatch; + } + + public Optional getAfterMatchSkipTo() + { + return afterMatchSkipTo; + } + + public Optional getPatternSearchMode() + { + return patternSearchMode; + } + + public RowPattern getPattern() + { + return pattern; + } + + public List getSubsets() + { + return subsets; + } + + public List getVariableDefinitions() + { + return variableDefinitions; + } + + @Override + public R accept(AstVisitor visitor, C context) + { + return visitor.visitPatternRecognitionRelation(this, context); + } + + @Override + public List getChildren() + { + ImmutableList.Builder builder = ImmutableList.builder(); + builder.add(input); + builder.addAll(partitionBy); + orderBy.ifPresent(builder::add); + builder.addAll(measures); + afterMatchSkipTo.ifPresent(builder::add); + builder.add(pattern) + .addAll(subsets) + .addAll(variableDefinitions); + patternSearchMode.ifPresent(builder::add); + + return builder.build(); + } + + @Override + public String toString() + { + return toStringHelper(this) + .add("input", input) + .add("partitionBy", partitionBy) + .add("orderBy", orderBy.orElse(null)) + .add("measures", measures) + .add("rowsPerMatch", rowsPerMatch.orElse(null)) + .add("afterMatchSkipTo", afterMatchSkipTo) + .add("patternSearchMode", patternSearchMode.orElse(null)) + .add("pattern", pattern) + .add("subsets", subsets) + .add("variableDefinitions", variableDefinitions) + .omitNullValues() + .toString(); + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + PatternRecognitionRelation that = (PatternRecognitionRelation) o; + return Objects.equals(input, that.input) && + Objects.equals(partitionBy, that.partitionBy) && + Objects.equals(orderBy, that.orderBy) && + Objects.equals(measures, that.measures) && + Objects.equals(rowsPerMatch, that.rowsPerMatch) && + Objects.equals(afterMatchSkipTo, that.afterMatchSkipTo) && + Objects.equals(patternSearchMode, that.patternSearchMode) && + Objects.equals(pattern, that.pattern) && + Objects.equals(subsets, that.subsets) && + Objects.equals(variableDefinitions, that.variableDefinitions); + } + + @Override + public int hashCode() + { + return Objects.hash(input, partitionBy, orderBy, measures, rowsPerMatch, afterMatchSkipTo, patternSearchMode, pattern, subsets, variableDefinitions); + } + + @Override + public boolean shallowEquals(Node other) + { + if (!sameClass(this, other)) { + return false; + } + + return rowsPerMatch.equals(((PatternRecognitionRelation) other).rowsPerMatch); + } + + public enum RowsPerMatch + { + // ONE option applies to the MATCH_RECOGNIZE clause. This is the default option. + // Output a single summary row for every match, including empty matches. + // In the case of an empty match, output the starting row of the match attempt. + ONE { + @Override + public boolean isOneRow() + { + return true; + } + + @Override + public boolean isEmptyMatches() + { + return true; + } + + @Override + public boolean isUnmatchedRows() + { + return false; + } + }, + + // ALL_SHOW_EMPTY option applies to the MATCH_RECOGNIZE clause. + // Output all rows of every match, including empty matches. + // In the case of an empty match, output the starting row of the match attempt. + // Do not produce output for the rows matched within exclusion `{- ... -}`. + ALL_SHOW_EMPTY { + @Override + public boolean isOneRow() + { + return false; + } + + @Override + public boolean isEmptyMatches() + { + return true; + } + + @Override + public boolean isUnmatchedRows() + { + return false; + } + }, + + // ALL_OMIT_EMPTY option applies to the MATCH_RECOGNIZE clause. + // Output all rows of every non-empty match. + // Do not produce output for the rows matched within exclusion `{- ... -}` + ALL_OMIT_EMPTY { + @Override + public boolean isOneRow() + { + return false; + } + + @Override + public boolean isEmptyMatches() + { + return false; + } + + @Override + public boolean isUnmatchedRows() + { + return false; + } + }, + + // ALL_WITH_UNMATCHED option applies to the MATCH_RECOGNIZE clause. + // Output all rows of every match, including empty matches. + // Produce an additional output row for every unmatched row. + // Pattern exclusions are not allowed with this option. + ALL_WITH_UNMATCHED { + @Override + public boolean isOneRow() + { + return false; + } + + @Override + public boolean isEmptyMatches() + { + return true; + } + + @Override + public boolean isUnmatchedRows() + { + return true; + } + }, + + // WINDOW option applies to pattern recognition within window specification. + // Output one row for every input row: + // - if the row is skipped by some previous match, produce output as for unmatched row + // - if match is found (either empty or non-empty), output a single-row summary + // - if no match is found, produce output as for unmatched row + WINDOW { + @Override + public boolean isOneRow() + { + return true; + } + + @Override + public boolean isEmptyMatches() + { + return true; + } + + @Override + public boolean isUnmatchedRows() + { + return true; + } + }; + + public abstract boolean isOneRow(); + + public abstract boolean isEmptyMatches(); + + public abstract boolean isUnmatchedRows(); + } +} diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/PatternSearchMode.java b/core/trino-parser/src/main/java/io/trino/sql/tree/PatternSearchMode.java new file mode 100644 index 000000000..1302f1eba --- /dev/null +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/PatternSearchMode.java @@ -0,0 +1,93 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.sql.tree; + +import com.google.common.collect.ImmutableList; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import static com.google.common.base.MoreObjects.toStringHelper; +import static java.util.Objects.requireNonNull; + +public final class PatternSearchMode + extends Node +{ + private final Mode mode; + + public PatternSearchMode(Mode mode) + { + this(Optional.empty(), mode); + } + + public PatternSearchMode(NodeLocation location, Mode mode) + { + this(Optional.of(location), mode); + } + + public PatternSearchMode(Optional location, Mode mode) + { + super(location); + this.mode = requireNonNull(mode, "mode is null"); + } + + public Mode getMode() + { + return mode; + } + + @Override + public R accept(AstVisitor visitor, C context) + { + return visitor.visitPatternSearchMode(this, context); + } + + @Override + public List getChildren() + { + return ImmutableList.of(); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) { + return true; + } + if ((obj == null) || (getClass() != obj.getClass())) { + return false; + } + return mode == ((PatternSearchMode) obj).mode; + } + + @Override + public int hashCode() + { + return Objects.hash(mode); + } + + @Override + public String toString() + { + return toStringHelper(this) + .add("mode", mode) + .toString(); + } + + public enum Mode + { + INITIAL, SEEK + } +} diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/PatternVariable.java b/core/trino-parser/src/main/java/io/trino/sql/tree/PatternVariable.java new file mode 100644 index 000000000..7aa932600 --- /dev/null +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/PatternVariable.java @@ -0,0 +1,90 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.sql.tree; + +import com.google.common.collect.ImmutableList; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import static com.google.common.base.MoreObjects.toStringHelper; +import static java.util.Objects.requireNonNull; + +public class PatternVariable + extends RowPattern +{ + private final Identifier name; + + public PatternVariable(NodeLocation location, Identifier name) + { + this(Optional.of(location), name); + } + + private PatternVariable(Optional location, Identifier name) + { + super(location); + this.name = requireNonNull(name, "name is null"); + } + + public Identifier getName() + { + return name; + } + + @Override + public R accept(AstVisitor visitor, C context) + { + return visitor.visitPatternVariable(this, context); + } + + @Override + public List getChildren() + { + return ImmutableList.of(name); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) { + return true; + } + if ((obj == null) || (getClass() != obj.getClass())) { + return false; + } + PatternVariable o = (PatternVariable) obj; + return Objects.equals(name, o.name); + } + + @Override + public int hashCode() + { + return Objects.hash(name); + } + + @Override + public String toString() + { + return toStringHelper(this) + .add("name", name) + .toString(); + } + + @Override + public boolean shallowEquals(Node other) + { + return sameClass(this, other); + } +} diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/ProcessingMode.java b/core/trino-parser/src/main/java/io/trino/sql/tree/ProcessingMode.java new file mode 100644 index 000000000..b569cc723 --- /dev/null +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/ProcessingMode.java @@ -0,0 +1,88 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.sql.tree; + +import com.google.common.collect.ImmutableList; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import static com.google.common.base.MoreObjects.toStringHelper; +import static java.util.Objects.requireNonNull; + +public final class ProcessingMode + extends Node +{ + private final Mode mode; + + public ProcessingMode(NodeLocation location, Mode mode) + { + this(Optional.of(location), mode); + } + + public ProcessingMode(Optional location, Mode mode) + { + super(location); + this.mode = requireNonNull(mode, "mode is null"); + } + + public Mode getMode() + { + return mode; + } + + @Override + public R accept(AstVisitor visitor, C context) + { + return visitor.visitProcessingMode(this, context); + } + + @Override + public List getChildren() + { + return ImmutableList.of(); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) { + return true; + } + if ((obj == null) || (getClass() != obj.getClass())) { + return false; + } + return mode == ((ProcessingMode) obj).mode; + } + + @Override + public int hashCode() + { + return Objects.hash(mode); + } + + @Override + public String toString() + { + return toStringHelper(this) + .add("mode", mode) + .toString(); + } + + public enum Mode + { + RUNNING, FINAL + } +} diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/QuantifiedComparisonExpression.java b/core/trino-parser/src/main/java/io/trino/sql/tree/QuantifiedComparisonExpression.java index 0eabc2606..10ef15623 100644 --- a/core/trino-parser/src/main/java/io/trino/sql/tree/QuantifiedComparisonExpression.java +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/QuantifiedComparisonExpression.java @@ -49,7 +49,7 @@ public class QuantifiedComparisonExpression private QuantifiedComparisonExpression(Optional location, ComparisonExpression.Operator operator, Quantifier quantifier, Expression value, Expression subquery) { super(location); - this.operator = requireNonNull(operator, "comparisonType is null"); + this.operator = requireNonNull(operator, "operator is null"); this.quantifier = requireNonNull(quantifier, "quantifier is null"); this.value = requireNonNull(value, "value is null"); this.subquery = requireNonNull(subquery, "subquery is null"); diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/QuantifiedPattern.java b/core/trino-parser/src/main/java/io/trino/sql/tree/QuantifiedPattern.java new file mode 100644 index 000000000..c1a4bdd83 --- /dev/null +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/QuantifiedPattern.java @@ -0,0 +1,99 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.sql.tree; + +import com.google.common.collect.ImmutableList; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import static com.google.common.base.MoreObjects.toStringHelper; +import static java.util.Objects.requireNonNull; + +public class QuantifiedPattern + extends RowPattern +{ + private final RowPattern pattern; + private final PatternQuantifier patternQuantifier; + + public QuantifiedPattern(NodeLocation location, RowPattern pattern, PatternQuantifier patternQuantifier) + { + this(Optional.of(location), pattern, patternQuantifier); + } + + private QuantifiedPattern(Optional location, RowPattern pattern, PatternQuantifier patternQuantifier) + { + super(location); + this.pattern = requireNonNull(pattern, "pattern is null"); + this.patternQuantifier = requireNonNull(patternQuantifier, "patternQuantifier is null"); + } + + public RowPattern getPattern() + { + return pattern; + } + + public PatternQuantifier getPatternQuantifier() + { + return patternQuantifier; + } + + @Override + public R accept(AstVisitor visitor, C context) + { + return visitor.visitQuantifiedPattern(this, context); + } + + @Override + public List getChildren() + { + return ImmutableList.of(pattern, patternQuantifier); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) { + return true; + } + if ((obj == null) || (getClass() != obj.getClass())) { + return false; + } + QuantifiedPattern o = (QuantifiedPattern) obj; + return Objects.equals(pattern, o.pattern) && + Objects.equals(patternQuantifier, o.patternQuantifier); + } + + @Override + public int hashCode() + { + return Objects.hash(pattern, patternQuantifier); + } + + @Override + public String toString() + { + return toStringHelper(this) + .add("pattern", pattern) + .add("patternQuantifier", patternQuantifier) + .toString(); + } + + @Override + public boolean shallowEquals(Node other) + { + return sameClass(this, other); + } +} diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/RangeQuantifier.java b/core/trino-parser/src/main/java/io/trino/sql/tree/RangeQuantifier.java new file mode 100644 index 000000000..402acf513 --- /dev/null +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/RangeQuantifier.java @@ -0,0 +1,103 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.sql.tree; + +import com.google.common.collect.ImmutableList; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import static com.google.common.base.MoreObjects.toStringHelper; +import static java.util.Objects.requireNonNull; + +public class RangeQuantifier + extends PatternQuantifier +{ + private final Optional atLeast; + private final Optional atMost; + + public RangeQuantifier(boolean greedy, Optional atLeast, Optional atMost) + { + this(Optional.empty(), greedy, atLeast, atMost); + } + + public RangeQuantifier(NodeLocation location, boolean greedy, Optional atLeast, Optional atMost) + { + this(Optional.of(location), greedy, atLeast, atMost); + } + + private RangeQuantifier(Optional location, boolean greedy, Optional atLeast, Optional atMost) + { + super(location, greedy); + this.atLeast = requireNonNull(atLeast, "atLeast is null"); + this.atMost = requireNonNull(atMost, "atMost is null"); + } + + public Optional getAtLeast() + { + return atLeast; + } + + public Optional getAtMost() + { + return atMost; + } + + @Override + public R accept(AstVisitor visitor, C context) + { + return visitor.visitRangeQuantifier(this, context); + } + + @Override + public List getChildren() + { + ImmutableList.Builder children = ImmutableList.builder(); + atLeast.ifPresent(children::add); + atMost.ifPresent(children::add); + return children.build(); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) { + return true; + } + if ((obj == null) || (getClass() != obj.getClass())) { + return false; + } + RangeQuantifier o = (RangeQuantifier) obj; + return isGreedy() == o.isGreedy() && + Objects.equals(atLeast, o.atLeast) && + Objects.equals(atMost, o.atMost); + } + + @Override + public int hashCode() + { + return Objects.hash(isGreedy(), atLeast, atMost); + } + + @Override + public String toString() + { + return toStringHelper(this) + .add("atLeast", atLeast) + .add("atMost", atMost) + .add("greedy", isGreedy()) + .toString(); + } +} diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/RowPattern.java b/core/trino-parser/src/main/java/io/trino/sql/tree/RowPattern.java new file mode 100644 index 000000000..dc7833d1b --- /dev/null +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/RowPattern.java @@ -0,0 +1,31 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.sql.tree; + +import java.util.Optional; + +public abstract class RowPattern + extends Node +{ + protected RowPattern(Optional location) + { + super(location); + } + + @Override + public R accept(AstVisitor visitor, C context) + { + return visitor.visitRowPattern(this, context); + } +} diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/SetPath.java b/core/trino-parser/src/main/java/io/trino/sql/tree/SetPath.java index 6e1a512c6..ebbb55d81 100644 --- a/core/trino-parser/src/main/java/io/trino/sql/tree/SetPath.java +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/SetPath.java @@ -40,7 +40,7 @@ public class SetPath private SetPath(Optional location, PathSpecification pathSpecification) { super(location); - this.pathSpecification = requireNonNull(pathSpecification, "path is null"); + this.pathSpecification = requireNonNull(pathSpecification, "pathSpecification is null"); } public PathSpecification getPathSpecification() diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/SkipTo.java b/core/trino-parser/src/main/java/io/trino/sql/tree/SkipTo.java new file mode 100644 index 000000000..4b4e8c2bd --- /dev/null +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/SkipTo.java @@ -0,0 +1,178 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.sql.tree; + +import com.google.common.collect.ImmutableList; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import static com.google.common.base.MoreObjects.toStringHelper; +import static com.google.common.base.Preconditions.checkArgument; +import static io.trino.sql.tree.SkipTo.Position.FIRST; +import static io.trino.sql.tree.SkipTo.Position.LAST; +import static io.trino.sql.tree.SkipTo.Position.NEXT; +import static io.trino.sql.tree.SkipTo.Position.PAST_LAST; +import static java.util.Objects.requireNonNull; + +public class SkipTo + extends Node +{ + private final Position position; + private final Optional identifier; + + public enum Position + { + PAST_LAST, + NEXT, + FIRST, + LAST + } + + // default + public static SkipTo skipPastLastRow() + { + return skipPastLastRow(Optional.empty()); + } + + public static SkipTo skipPastLastRow(NodeLocation location) + { + return skipPastLastRow(Optional.of(location)); + } + + private static SkipTo skipPastLastRow(Optional location) + { + return new SkipTo(location, PAST_LAST, Optional.empty()); + } + + public static SkipTo skipToNextRow() + { + return skipToNextRow(Optional.empty()); + } + + public static SkipTo skipToNextRow(NodeLocation location) + { + return skipToNextRow(Optional.of(location)); + } + + private static SkipTo skipToNextRow(Optional location) + { + return new SkipTo(location, NEXT, Optional.empty()); + } + + public static SkipTo skipToFirst(Identifier identifier) + { + return skipToFirst(Optional.empty(), identifier); + } + + public static SkipTo skipToFirst(NodeLocation location, Identifier identifier) + { + return skipToFirst(Optional.of(location), identifier); + } + + private static SkipTo skipToFirst(Optional location, Identifier identifier) + { + return new SkipTo(location, FIRST, Optional.of(identifier)); + } + + public static SkipTo skipToLast(Identifier identifier) + { + return skipToLast(Optional.empty(), identifier); + } + + public static SkipTo skipToLast(NodeLocation location, Identifier identifier) + { + return skipToLast(Optional.of(location), identifier); + } + + private static SkipTo skipToLast(Optional location, Identifier identifier) + { + return new SkipTo(location, LAST, Optional.of(identifier)); + } + + private SkipTo(Optional location, Position position, Optional identifier) + { + super(location); + requireNonNull(position, "position is null"); + requireNonNull(identifier, "identifier is null"); + checkArgument(identifier.isPresent() || (position == PAST_LAST || position == NEXT), "missing identifier in SKIP TO " + position.name()); + checkArgument(!identifier.isPresent() || (position == FIRST || position == LAST), "unexpected identifier in SKIP TO " + position.name()); + this.position = position; + this.identifier = identifier; + } + + public Position getPosition() + { + return position; + } + + public Optional getIdentifier() + { + return identifier; + } + + @Override + public R accept(AstVisitor visitor, C context) + { + return visitor.visitSkipTo(this, context); + } + + @Override + public List getChildren() + { + return identifier.map(ImmutableList::of).orElse(ImmutableList.of()); + } + + @Override + public String toString() + { + return toStringHelper(this) + .add("position", position) + .add("identifier", identifier.orElse(null)) + .omitNullValues() + .toString(); + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + SkipTo that = (SkipTo) o; + return Objects.equals(position, that.position) && + Objects.equals(identifier, that.identifier); + } + + @Override + public int hashCode() + { + return Objects.hash(position, identifier); + } + + @Override + public boolean shallowEquals(Node other) + { + if (!sameClass(this, other)) { + return false; + } + + return position == ((SkipTo) other).position; + } +} diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/SubsetDefinition.java b/core/trino-parser/src/main/java/io/trino/sql/tree/SubsetDefinition.java new file mode 100644 index 000000000..bd2474124 --- /dev/null +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/SubsetDefinition.java @@ -0,0 +1,110 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.sql.tree; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import static com.google.common.base.MoreObjects.toStringHelper; +import static com.google.common.base.Preconditions.checkArgument; +import static java.util.Objects.requireNonNull; + +public class SubsetDefinition + extends Node +{ + private final Identifier name; + private final List identifiers; + + public SubsetDefinition(Identifier name, List identifiers) + { + this(Optional.empty(), name, identifiers); + } + + public SubsetDefinition(NodeLocation location, Identifier name, List identifiers) + { + this(Optional.of(location), name, identifiers); + } + + private SubsetDefinition(Optional location, Identifier name, List identifiers) + { + super(location); + this.name = requireNonNull(name, "name is null"); + requireNonNull(identifiers, "identifiers is null"); + checkArgument(!identifiers.isEmpty(), "identifiers is empty"); + this.identifiers = identifiers; + } + + public Identifier getName() + { + return name; + } + + public List getIdentifiers() + { + return identifiers; + } + + @Override + public R accept(AstVisitor visitor, C context) + { + return visitor.visitSubsetDefinition(this, context); + } + + @Override + public List getChildren() + { + return identifiers; + } + + @Override + public String toString() + { + return toStringHelper(this) + .add("name", name) + .add("identifiers", identifiers) + .toString(); + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + SubsetDefinition that = (SubsetDefinition) o; + return Objects.equals(name, that.name) && + Objects.equals(identifiers, that.identifiers); + } + + @Override + public int hashCode() + { + return Objects.hash(name, identifiers); + } + + @Override + public boolean shallowEquals(Node other) + { + if (!sameClass(this, other)) { + return false; + } + + return Objects.equals(name, ((SubsetDefinition) other).name); + } +} diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/Table.java b/core/trino-parser/src/main/java/io/trino/sql/tree/Table.java index cea24c7fc..de4585366 100644 --- a/core/trino-parser/src/main/java/io/trino/sql/tree/Table.java +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/Table.java @@ -25,33 +25,21 @@ public class Table extends QueryBody { private final QualifiedName name; - private final Optional cannerflowName; public Table(QualifiedName name) { - this(Optional.empty(), name, Optional.empty()); - } - - public Table(QualifiedName name, QualifiedName cannerflowName) - { - this(Optional.empty(), name, Optional.of(cannerflowName)); + this(Optional.empty(), name); } public Table(NodeLocation location, QualifiedName name) { - this(Optional.of(location), name, Optional.empty()); + this(Optional.of(location), name); } - public Table(NodeLocation location, QualifiedName name, QualifiedName cannerflowName) - { - this(Optional.of(location), name, Optional.of(cannerflowName)); - } - - private Table(Optional location, QualifiedName name, Optional cannerflowName) + private Table(Optional location, QualifiedName name) { super(location); this.name = name; - this.cannerflowName = cannerflowName; } public QualifiedName getName() @@ -59,11 +47,6 @@ public class Table return name; } - public Optional getCannerflowName() - { - return cannerflowName; - } - @Override public R accept(AstVisitor visitor, C context) { diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/TypeParameter.java b/core/trino-parser/src/main/java/io/trino/sql/tree/TypeParameter.java index 0f22818a7..a72a3f782 100644 --- a/core/trino-parser/src/main/java/io/trino/sql/tree/TypeParameter.java +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/TypeParameter.java @@ -29,7 +29,7 @@ public class TypeParameter public TypeParameter(DataType type) { super(Optional.empty()); - this.type = requireNonNull(type, "value is null"); + this.type = requireNonNull(type, "type is null"); } public DataType getValue() diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/Update.java b/core/trino-parser/src/main/java/io/trino/sql/tree/Update.java index 0aa6419db..6a717427a 100644 --- a/core/trino-parser/src/main/java/io/trino/sql/tree/Update.java +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/Update.java @@ -43,7 +43,7 @@ public class Update { super(location); this.table = requireNonNull(table, "table is null"); - this.assignments = requireNonNull(assignments, "targets is null"); + this.assignments = requireNonNull(assignments, "assignments is null"); this.where = requireNonNull(where, "where is null"); } diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/VariableDefinition.java b/core/trino-parser/src/main/java/io/trino/sql/tree/VariableDefinition.java new file mode 100644 index 000000000..9c79ce1cd --- /dev/null +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/VariableDefinition.java @@ -0,0 +1,109 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.sql.tree; + +import com.google.common.collect.ImmutableList; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import static com.google.common.base.MoreObjects.toStringHelper; +import static java.util.Objects.requireNonNull; + +public class VariableDefinition + extends Node +{ + private final Identifier name; + private final Expression expression; + + public VariableDefinition(Identifier name, Expression expression) + { + this(Optional.empty(), name, expression); + } + + public VariableDefinition(NodeLocation location, Identifier name, Expression expression) + { + this(Optional.of(location), name, expression); + } + + private VariableDefinition(Optional location, Identifier name, Expression expression) + { + super(location); + this.name = requireNonNull(name, "name is null"); + this.expression = requireNonNull(expression, "expression is null"); + } + + public Identifier getName() + { + return name; + } + + public Expression getExpression() + { + return expression; + } + + @Override + public R accept(AstVisitor visitor, C context) + { + return visitor.visitVariableDefinition(this, context); + } + + @Override + public List getChildren() + { + return ImmutableList.of(expression); + } + + @Override + public String toString() + { + return toStringHelper(this) + .add("name", name) + .add("expression", expression) + .toString(); + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + VariableDefinition that = (VariableDefinition) o; + return Objects.equals(name, that.name) && + Objects.equals(expression, that.expression); + } + + @Override + public int hashCode() + { + return Objects.hash(name, expression); + } + + @Override + public boolean shallowEquals(Node other) + { + if (!sameClass(this, other)) { + return false; + } + + return Objects.equals(name, ((VariableDefinition) other).name); + } +} diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/ZeroOrMoreQuantifier.java b/core/trino-parser/src/main/java/io/trino/sql/tree/ZeroOrMoreQuantifier.java new file mode 100644 index 000000000..46f378d17 --- /dev/null +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/ZeroOrMoreQuantifier.java @@ -0,0 +1,41 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.sql.tree; + +import java.util.Optional; + +public class ZeroOrMoreQuantifier + extends PatternQuantifier +{ + public ZeroOrMoreQuantifier(boolean greedy) + { + this(Optional.empty(), greedy); + } + + public ZeroOrMoreQuantifier(NodeLocation location, boolean greedy) + { + this(Optional.of(location), greedy); + } + + public ZeroOrMoreQuantifier(Optional location, boolean greedy) + { + super(location, greedy); + } + + @Override + public R accept(AstVisitor visitor, C context) + { + return visitor.visitZeroOrMoreQuantifier(this, context); + } +} diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/ZeroOrOneQuantifier.java b/core/trino-parser/src/main/java/io/trino/sql/tree/ZeroOrOneQuantifier.java new file mode 100644 index 000000000..551645adf --- /dev/null +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/ZeroOrOneQuantifier.java @@ -0,0 +1,41 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.sql.tree; + +import java.util.Optional; + +public class ZeroOrOneQuantifier + extends PatternQuantifier +{ + public ZeroOrOneQuantifier(boolean greedy) + { + this(Optional.empty(), greedy); + } + + public ZeroOrOneQuantifier(NodeLocation location, boolean greedy) + { + this(Optional.of(location), greedy); + } + + public ZeroOrOneQuantifier(Optional location, boolean greedy) + { + super(location, greedy); + } + + @Override + public R accept(AstVisitor visitor, C context) + { + return visitor.visitZeroOrOneQuantifier(this, context); + } +} diff --git a/core/trino-parser/src/main/java/io/trino/sql/util/EscapedChars.java b/core/trino-parser/src/main/java/io/trino/sql/util/EscapedChars.java index c51004881..f36ae791d 100644 --- a/core/trino-parser/src/main/java/io/trino/sql/util/EscapedChars.java +++ b/core/trino-parser/src/main/java/io/trino/sql/util/EscapedChars.java @@ -1,15 +1,8 @@ /* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright (C) Canner, Inc - All Rights Reserved + * Unauthorized copying of this file, via any medium is strictly prohibited + * Proprietary and confidential + * Written by Canner dev team contact@canner.io, Nov 2021 */ package io.trino.sql.util; diff --git a/core/trino-parser/src/main/java/io/trino/sql/util/EscapedCharsUtil.java b/core/trino-parser/src/main/java/io/trino/sql/util/EscapedCharsUtil.java index b442c3ed0..48ba74658 100644 --- a/core/trino-parser/src/main/java/io/trino/sql/util/EscapedCharsUtil.java +++ b/core/trino-parser/src/main/java/io/trino/sql/util/EscapedCharsUtil.java @@ -1,15 +1,8 @@ /* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright (C) Canner, Inc - All Rights Reserved + * Unauthorized copying of this file, via any medium is strictly prohibited + * Proprietary and confidential + * Written by Canner dev team contact@canner.io, Nov 2021 */ package io.trino.sql.util; diff --git a/core/trino-parser/src/main/java/io/trino/sql/util/IntervalLiteralUtil.java b/core/trino-parser/src/main/java/io/trino/sql/util/IntervalLiteralUtil.java index a10027c6d..b3ff9c785 100644 --- a/core/trino-parser/src/main/java/io/trino/sql/util/IntervalLiteralUtil.java +++ b/core/trino-parser/src/main/java/io/trino/sql/util/IntervalLiteralUtil.java @@ -1,15 +1,8 @@ /* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright (C) Canner, Inc - All Rights Reserved + * Unauthorized copying of this file, via any medium is strictly prohibited + * Proprietary and confidential + * Written by Canner dev team contact@canner.io, Feb 2022 */ package io.trino.sql.util; @@ -32,9 +25,9 @@ public final class IntervalLiteralUtil *

* e.g. * when client sends a query like - * select CAST((CAST(now() AS timestamp) + (INTERVAL '-30 day')) AS date); + * select CAST((CAST(now() AS timestamp) + (INTERVAL '-30 day')) AS date); * will get the same result as - * select CAST((CAST(now() AS timestamp) + (INTERVAL - '30' day)) AS date); + * select CAST((CAST(now() AS timestamp) + (INTERVAL - '30' day)) AS date); */ public static IntervalLiteral parse(NodeLocation location, String text) { diff --git a/core/trino-parser/src/test/java/io/trino/sql/parser/ParserAssert.java b/core/trino-parser/src/test/java/io/trino/sql/parser/ParserAssert.java index 21f70b7a4..8c20a7a4b 100644 --- a/core/trino-parser/src/test/java/io/trino/sql/parser/ParserAssert.java +++ b/core/trino-parser/src/test/java/io/trino/sql/parser/ParserAssert.java @@ -16,6 +16,7 @@ package io.trino.sql.parser; import io.trino.sql.SqlFormatter; import io.trino.sql.tree.Expression; import io.trino.sql.tree.Node; +import io.trino.sql.tree.RowPattern; import io.trino.sql.tree.Statement; import org.assertj.core.api.AssertProvider; import org.assertj.core.api.RecursiveComparisonAssert; @@ -35,7 +36,7 @@ public class ParserAssert @Override public String toStringOf(Object object) { - if (object instanceof Statement || object instanceof Expression) { + if (object instanceof Statement || object instanceof Expression || object instanceof RowPattern) { return SqlFormatter.formatSql((Node) object); } return super.toStringOf(object); @@ -57,6 +58,11 @@ public class ParserAssert return createAssertion(statement -> new SqlParser().createStatement(statement, new ParsingOptions(AS_DECIMAL)), sql); } + public static AssertProvider rowPattern(String sql) + { + return createAssertion(new SqlParser()::createRowPattern, sql); + } + private ParserAssert(Node actual, RecursiveComparisonConfiguration recursiveComparisonConfiguration) { super(actual, recursiveComparisonConfiguration); diff --git a/core/trino-parser/src/test/java/io/trino/sql/parser/TestSqlParser.java b/core/trino-parser/src/test/java/io/trino/sql/parser/TestSqlParser.java index 940760e17..cdc684799 100644 --- a/core/trino-parser/src/test/java/io/trino/sql/parser/TestSqlParser.java +++ b/core/trino-parser/src/test/java/io/trino/sql/parser/TestSqlParser.java @@ -22,6 +22,7 @@ import io.trino.sql.tree.AliasedRelation; import io.trino.sql.tree.AllColumns; import io.trino.sql.tree.AllRows; import io.trino.sql.tree.Analyze; +import io.trino.sql.tree.AnchorPattern; import io.trino.sql.tree.ArithmeticBinaryExpression; import io.trino.sql.tree.ArrayConstructor; import io.trino.sql.tree.AtTimeZone; @@ -58,6 +59,7 @@ import io.trino.sql.tree.DropRole; import io.trino.sql.tree.DropSchema; import io.trino.sql.tree.DropTable; import io.trino.sql.tree.DropView; +import io.trino.sql.tree.EmptyPattern; import io.trino.sql.tree.Execute; import io.trino.sql.tree.ExistsPredicate; import io.trino.sql.tree.Explain; @@ -106,17 +108,24 @@ import io.trino.sql.tree.NotExpression; import io.trino.sql.tree.NullIfExpression; import io.trino.sql.tree.NullLiteral; import io.trino.sql.tree.Offset; +import io.trino.sql.tree.OneOrMoreQuantifier; import io.trino.sql.tree.OrderBy; import io.trino.sql.tree.Parameter; import io.trino.sql.tree.PathElement; import io.trino.sql.tree.PathSpecification; +import io.trino.sql.tree.PatternAlternation; +import io.trino.sql.tree.PatternConcatenation; +import io.trino.sql.tree.PatternVariable; import io.trino.sql.tree.Prepare; import io.trino.sql.tree.PrincipalSpecification; +import io.trino.sql.tree.ProcessingMode; import io.trino.sql.tree.Property; import io.trino.sql.tree.QualifiedName; import io.trino.sql.tree.QuantifiedComparisonExpression; +import io.trino.sql.tree.QuantifiedPattern; import io.trino.sql.tree.Query; import io.trino.sql.tree.QuerySpecification; +import io.trino.sql.tree.RangeQuantifier; import io.trino.sql.tree.RefreshMaterializedView; import io.trino.sql.tree.RenameColumn; import io.trino.sql.tree.RenameSchema; @@ -172,6 +181,8 @@ import io.trino.sql.tree.WindowReference; import io.trino.sql.tree.WindowSpecification; import io.trino.sql.tree.With; import io.trino.sql.tree.WithQuery; +import io.trino.sql.tree.ZeroOrMoreQuantifier; +import io.trino.sql.tree.ZeroOrOneQuantifier; import org.testng.annotations.Test; import java.util.ArrayList; @@ -195,6 +206,7 @@ import static io.trino.sql.QueryUtil.table; import static io.trino.sql.QueryUtil.values; import static io.trino.sql.SqlFormatter.formatSql; import static io.trino.sql.parser.ParserAssert.expression; +import static io.trino.sql.parser.ParserAssert.rowPattern; import static io.trino.sql.parser.ParserAssert.statement; import static io.trino.sql.parser.ParsingOptions.DecimalLiteralTreatment.AS_DECIMAL; import static io.trino.sql.parser.TreeNodes.columnDefinition; @@ -210,6 +222,8 @@ import static io.trino.sql.tree.ArithmeticUnaryExpression.negative; import static io.trino.sql.tree.ArithmeticUnaryExpression.positive; import static io.trino.sql.tree.DateTimeDataType.Type.TIMESTAMP; import static io.trino.sql.tree.FrameBound.Type.CURRENT_ROW; +import static io.trino.sql.tree.ProcessingMode.Mode.FINAL; +import static io.trino.sql.tree.ProcessingMode.Mode.RUNNING; import static io.trino.sql.tree.SortItem.NullOrdering.UNDEFINED; import static io.trino.sql.tree.SortItem.Ordering.ASCENDING; import static io.trino.sql.tree.SortItem.Ordering.DESCENDING; @@ -647,6 +661,113 @@ public class TestSqlParser subquery(valuesQuery))); } + @Test + public void testRowPattern() + { + assertThat(rowPattern("(A B)* | CC+? DD?? E | (F | G)")) + .isEqualTo( + new PatternAlternation( + location(1, 1), + ImmutableList.of( + new PatternAlternation( + location(1, 1), + ImmutableList.of( + new QuantifiedPattern( + location(1, 1), + new PatternConcatenation( + location(1, 2), + ImmutableList.of( + new PatternVariable(location(1, 2), new Identifier(location(1, 2), "A", false)), + new PatternVariable(location(1, 4), new Identifier(location(1, 4), "B", false)))), + new ZeroOrMoreQuantifier(location(1, 6), true)), + new PatternConcatenation( + location(1, 10), + ImmutableList.of( + new PatternConcatenation( + location(1, 10), + ImmutableList.of( + new QuantifiedPattern(location(1, 10), new PatternVariable(location(1, 10), new Identifier(location(1, 10), "CC", false)), new OneOrMoreQuantifier(location(1, 12), false)), + new QuantifiedPattern(location(1, 15), new PatternVariable(location(1, 15), new Identifier(location(1, 15), "DD", false)), new ZeroOrOneQuantifier(location(1, 17), false)))), + new PatternVariable(location(1, 20), new Identifier(location(1, 20), "E", false)))))), + new PatternAlternation( + location(1, 25), + ImmutableList.of( + new PatternVariable(location(1, 25), new Identifier(location(1, 25), "F", false)), + new PatternVariable(location(1, 29), new Identifier(location(1, 29), "G", false))))))); + + assertThat(rowPattern("A | B | C D E F")) + .isEqualTo( + new PatternAlternation( + location(1, 1), + ImmutableList.of( + new PatternAlternation( + location(1, 1), + ImmutableList.of( + new PatternVariable(location(1, 1), new Identifier(location(1, 1), "A", false)), + new PatternVariable(location(1, 5), new Identifier(location(1, 5), "B", false)))), + new PatternConcatenation( + location(1, 9), + ImmutableList.of( + new PatternConcatenation( + location(1, 9), + ImmutableList.of( + new PatternConcatenation( + location(1, 9), + ImmutableList.of( + new PatternVariable(location(1, 9), new Identifier(location(1, 9), "C", false)), + new PatternVariable(location(1, 11), new Identifier(location(1, 11), "D", false)))), + new PatternVariable(location(1, 13), new Identifier(location(1, 13), "E", false)))), + new PatternVariable(location(1, 15), new Identifier(location(1, 15), "F", false))))))); + + assertThatThrownBy(() -> SQL_PARSER.createRowPattern("A!")) + .isInstanceOf(ParsingException.class) + .hasMessageMatching("line 1:2: mismatched input '!'.*"); + + assertThatThrownBy(() -> SQL_PARSER.createRowPattern("A**")) + .isInstanceOf(ParsingException.class) + .hasMessageMatching("line 1:3: mismatched input '*'.*"); + + assertThat(rowPattern("A??")) + .isEqualTo(new QuantifiedPattern( + location(1, 1), + new PatternVariable(location(1, 1), new Identifier(location(1, 1), "A", false)), + new ZeroOrOneQuantifier(location(1, 2), false))); + + assertThat(rowPattern("^$")) + .isEqualTo(new PatternConcatenation( + location(1, 1), + ImmutableList.of( + new AnchorPattern(location(1, 1), AnchorPattern.Type.PARTITION_START), + new AnchorPattern(location(1, 2), AnchorPattern.Type.PARTITION_END)))); + + assertThat(rowPattern("()")) + .isEqualTo(new EmptyPattern(location(1, 1))); + + assertThat(rowPattern("A{3}")) + .isEqualTo(new QuantifiedPattern( + location(1, 1), + new PatternVariable(location(1, 1), new Identifier(location(1, 1), "A", false)), + new RangeQuantifier(location(1, 2), true, Optional.of(new LongLiteral(location(1, 3), "3")), Optional.of(new LongLiteral(location(1, 3), "3"))))); + + assertThat(rowPattern("A{3,}")) + .isEqualTo(new QuantifiedPattern( + location(1, 1), + new PatternVariable(location(1, 1), new Identifier(location(1, 1), "A", false)), + new RangeQuantifier(location(1, 2), true, Optional.of(new LongLiteral(location(1, 3), "3")), Optional.empty()))); + + assertThat(rowPattern("A{,3}")) + .isEqualTo(new QuantifiedPattern( + location(1, 1), + new PatternVariable(location(1, 1), new Identifier(location(1, 1), "A", false)), + new RangeQuantifier(location(1, 2), true, Optional.empty(), Optional.of(new LongLiteral(location(1, 4), "3"))))); + + assertThat(rowPattern("A{3,4}")) + .isEqualTo(new QuantifiedPattern( + location(1, 1), + new PatternVariable(location(1, 1), new Identifier(location(1, 1), "A", false)), + new RangeQuantifier(location(1, 2), true, Optional.of(new LongLiteral(location(1, 3), "3")), Optional.of(new LongLiteral(location(1, 5), "4"))))); + } + @Test public void testPrecedenceAndAssociativity() { @@ -2402,8 +2523,12 @@ public class TestSqlParser for (String fullName : tableNames) { QualifiedName qualifiedName = makeQualifiedName(fullName); + + // Simple SELECT assertStatement(format("SHOW STATS FOR (SELECT * FROM %s)", qualifiedName), createShowStats(qualifiedName, ImmutableList.of(new AllColumns()), Optional.empty())); + + // SELECT with predicate assertStatement(format("SHOW STATS FOR (SELECT * FROM %s WHERE field > 0)", qualifiedName), createShowStats(qualifiedName, ImmutableList.of(new AllColumns()), @@ -2411,6 +2536,8 @@ public class TestSqlParser new ComparisonExpression(ComparisonExpression.Operator.GREATER_THAN, new Identifier("field"), new LongLiteral("0"))))); + + // SELECT with more complex predicate assertStatement(format("SHOW STATS FOR (SELECT * FROM %s WHERE field > 0 or field < 0)", qualifiedName), createShowStats(qualifiedName, ImmutableList.of(new AllColumns()), @@ -2423,6 +2550,128 @@ public class TestSqlParser new Identifier("field"), new LongLiteral("0")))))); } + + // SELECT with LIMIT + assertThat(statement("SHOW STATS FOR (SELECT * FROM t LIMIT 10)")) + .isEqualTo( + new ShowStats( + Optional.of(location(1, 1)), + new TableSubquery( + new Query( + location(1, 17), + Optional.empty(), + new QuerySpecification( + location(1, 17), + new Select( + location(1, 17), + false, + ImmutableList.of(new AllColumns(location(1, 24), Optional.empty(), ImmutableList.of()))), + Optional.of(new Table( + location(1, 31), + QualifiedName.of(ImmutableList.of(new Identifier(location(1, 31), "t", false))))), + Optional.empty(), + Optional.empty(), + Optional.empty(), + ImmutableList.of(), + Optional.empty(), + Optional.empty(), + Optional.of(new Limit(location(1, 33), new LongLiteral(location(1, 39), "10")))), + Optional.empty(), + Optional.empty(), + Optional.empty())))); + + // SELECT with ORDER BY ... LIMIT + assertThat(statement("SHOW STATS FOR (SELECT * FROM t ORDER BY field LIMIT 10)")) + .isEqualTo( + new ShowStats( + Optional.of(location(1, 1)), + new TableSubquery( + new Query( + location(1, 17), + Optional.empty(), + new QuerySpecification( + location(1, 17), + new Select( + location(1, 17), + false, + ImmutableList.of(new AllColumns(location(1, 24), Optional.empty(), ImmutableList.of()))), + Optional.of(new Table( + location(1, 31), + QualifiedName.of(ImmutableList.of(new Identifier(location(1, 31), "t", false))))), + Optional.empty(), + Optional.empty(), + Optional.empty(), + ImmutableList.of(), + Optional.of(new OrderBy(location(1, 33), ImmutableList.of( + new SortItem(location(1, 42), new Identifier(location(1, 42), "field", false), ASCENDING, UNDEFINED)))), + Optional.empty(), + Optional.of(new Limit(location(1, 48), new LongLiteral(location(1, 54), "10")))), + Optional.empty(), + Optional.empty(), + Optional.empty())))); + + // SELECT with WITH + assertThat(statement("SHOW STATS FOR (\n" + + " WITH t AS (SELECT 1 )\n" + + " SELECT * FROM t)")) + .isEqualTo( + new ShowStats( + Optional.of(location(1, 1)), + new TableSubquery( + new Query( + location(2, 4), + Optional.of( + new With( + location(2, 4), + false, + ImmutableList.of( + new WithQuery( + location(2, 9), + new Identifier(location(2, 9), "t", false), + new Query( + location(2, 15), + Optional.empty(), + new QuerySpecification( + location(2, 15), + new Select( + location(2, 15), + false, + ImmutableList.of( + new SingleColumn( + location(2, 22), + new LongLiteral(location(2, 22), "1"), + Optional.empty()))), + Optional.empty(), + Optional.empty(), + Optional.empty(), + Optional.empty(), + ImmutableList.of(), + Optional.empty(), + Optional.empty(), + Optional.empty()), + Optional.empty(), + Optional.empty(), + Optional.empty()), + Optional.empty())))), + new QuerySpecification( + location(3, 4), + new Select( + location(3, 4), + false, + ImmutableList.of(new AllColumns(location(3, 11), Optional.empty(), ImmutableList.of()))), + Optional.of(new Table( + location(3, 18), + QualifiedName.of(ImmutableList.of(new Identifier(location(3, 18), "t", false))))), + Optional.empty(), + Optional.empty(), + Optional.empty(), + ImmutableList.of(), + Optional.empty(), + Optional.empty(), + Optional.empty()), + Optional.empty(), + Optional.empty(), + Optional.empty())))); } private static ShowStats createShowStats(QualifiedName name, List selects, Optional where) @@ -2462,6 +2711,7 @@ public class TestSqlParser Optional.empty(), false, Optional.empty(), + Optional.empty(), ImmutableList.of(new Identifier("x")))))); } @@ -2500,6 +2750,7 @@ public class TestSqlParser Optional.of(new OrderBy(ImmutableList.of(new SortItem(identifier("x"), DESCENDING, UNDEFINED)))), false, Optional.empty(), + Optional.empty(), ImmutableList.of(identifier("x")))); assertStatement("SELECT array_agg(x ORDER BY t.y) FROM t", simpleQuery( @@ -2511,6 +2762,7 @@ public class TestSqlParser Optional.of(new OrderBy(ImmutableList.of(new SortItem(new DereferenceExpression(new Identifier("t"), identifier("y")), ASCENDING, UNDEFINED)))), false, Optional.empty(), + Optional.empty(), ImmutableList.of(new Identifier("x")))), table(QualifiedName.of("t")))); } @@ -2778,6 +3030,7 @@ public class TestSqlParser Optional.empty(), false, Optional.of(NullTreatment.IGNORE), + Optional.empty(), ImmutableList.of(new Identifier("x"), new LongLiteral("1")))); assertExpression("lead(x, 1) respect nulls over()", new FunctionCall( @@ -2788,6 +3041,34 @@ public class TestSqlParser Optional.empty(), false, Optional.of(NullTreatment.RESPECT), + Optional.empty(), + ImmutableList.of(new Identifier("x"), new LongLiteral("1")))); + } + + @Test + public void testProcessingMode() + { + assertExpression("RUNNING LAST(x, 1)", + new FunctionCall( + Optional.empty(), + QualifiedName.of("LAST"), + Optional.empty(), + Optional.empty(), + Optional.empty(), + false, + Optional.empty(), + Optional.of(new ProcessingMode(Optional.empty(), RUNNING)), + ImmutableList.of(new Identifier("x"), new LongLiteral("1")))); + assertExpression("FINAL FIRST(x, 1)", + new FunctionCall( + Optional.empty(), + QualifiedName.of("FIRST"), + Optional.empty(), + Optional.empty(), + Optional.empty(), + false, + Optional.empty(), + Optional.of(new ProcessingMode(Optional.empty(), FINAL)), ImmutableList.of(new Identifier("x"), new LongLiteral("1")))); } @@ -2803,6 +3084,7 @@ public class TestSqlParser Optional.empty(), false, Optional.empty(), + Optional.empty(), ImmutableList.of())); assertExpression("rank() OVER (someWindow PARTITION BY x ORDER BY y ROWS CURRENT ROW)", @@ -2818,6 +3100,7 @@ public class TestSqlParser Optional.empty(), false, Optional.empty(), + Optional.empty(), ImmutableList.of())); assertExpression("rank() OVER (PARTITION BY x ORDER BY y ROWS CURRENT ROW)", @@ -2833,6 +3116,7 @@ public class TestSqlParser Optional.empty(), false, Optional.empty(), + Optional.empty(), ImmutableList.of())); } @@ -2866,6 +3150,7 @@ public class TestSqlParser Optional.empty())); } + @Test public void testUpdate() { assertStatement("" + @@ -2904,18 +3189,30 @@ public class TestSqlParser return QualifiedName.of(parts); } + /** + * @deprecated use {@link ParserAssert#statement(String)} instead + */ + @Deprecated private static void assertStatement(String query, Statement expected) { assertParsed(query, expected, SQL_PARSER.createStatement(query, new ParsingOptions())); assertFormattedSql(SQL_PARSER, expected); } + /** + * @deprecated use {@link ParserAssert#statement(String)} instead + */ + @Deprecated private static void assertInvalidStatement(String statement, String expectedErrorMessageRegex) { assertThatThrownBy(() -> SQL_PARSER.createStatement(statement, new ParsingOptions())) .isInstanceOfSatisfying(ParsingException.class, e -> assertTrue(e.getErrorMessage().matches(expectedErrorMessageRegex))); } + /** + * @deprecated use {@link ParserAssert#expression(String)} instead + */ + @Deprecated private static void assertExpression(String expression, Expression expected) { requireNonNull(expression, "expression is null"); diff --git a/core/trino-parser/src/test/java/io/trino/sql/parser/TestSqlParserErrorHandling.java b/core/trino-parser/src/test/java/io/trino/sql/parser/TestSqlParserErrorHandling.java index 4a004a6fa..3e6620302 100644 --- a/core/trino-parser/src/test/java/io/trino/sql/parser/TestSqlParserErrorHandling.java +++ b/core/trino-parser/src/test/java/io/trino/sql/parser/TestSqlParserErrorHandling.java @@ -50,8 +50,8 @@ public class TestSqlParserErrorHandling {"select * from 'oops", "line 1:15: mismatched input '''. Expecting: '(', 'LATERAL', 'UNNEST', "}, {"select *\nfrom x\nfrom", - "line 3:1: mismatched input 'from'. Expecting: ',', '.', 'AS', 'CROSS', 'EXCEPT', 'FETCH', 'FULL', 'GROUP', 'HAVING', 'INNER', 'INTERSECT', 'JOIN', 'LEFT', 'LIMIT', 'NATURAL', 'OFFSET', " + - "'ORDER', 'RIGHT', 'TABLESAMPLE', 'UNION', 'WHERE', 'WINDOW', , "}, + "line 3:1: mismatched input 'from'. Expecting: ',', '.', 'AS', 'CROSS', 'EXCEPT', 'FETCH', 'FULL', 'GROUP', 'HAVING', 'INNER', 'INTERSECT', 'JOIN', 'LEFT', " + + "'LIMIT', 'MATCH_RECOGNIZE', 'NATURAL', 'OFFSET', 'ORDER', 'RIGHT', 'TABLESAMPLE', 'UNION', 'WHERE', 'WINDOW', , "}, {"select *\nfrom x\nwhere from", "line 3:7: mismatched input 'from'. Expecting: "}, {"select ", @@ -121,8 +121,8 @@ public class TestSqlParserErrorHandling {"SELECT foo(*) filter (", "line 1:23: mismatched input ''. Expecting: 'WHERE'"}, {"SELECT * FROM t t x", - "line 1:19: mismatched input 'x'. Expecting: '(', ',', 'CROSS', 'EXCEPT', 'FETCH', 'FULL', 'GROUP', 'HAVING', 'INNER', 'INTERSECT', 'JOIN', 'LEFT', 'LIMIT', 'NATURAL', 'OFFSET', 'ORDER', " + - "'RIGHT', 'TABLESAMPLE', 'UNION', 'WHERE', 'WINDOW', "}, + "line 1:19: mismatched input 'x'. Expecting: '(', ',', 'CROSS', 'EXCEPT', 'FETCH', 'FULL', 'GROUP', 'HAVING', 'INNER', 'INTERSECT', 'JOIN', 'LEFT', 'LIMIT', " + + "'MATCH_RECOGNIZE', 'NATURAL', 'OFFSET', 'ORDER', 'RIGHT', 'TABLESAMPLE', 'UNION', 'WHERE', 'WINDOW', "}, {"SELECT * FROM t WHERE EXISTS (", "line 1:31: mismatched input ''. Expecting: "}, {"SELECT \"\" FROM t", diff --git a/core/trino-parser/src/test/java/io/trino/sql/parser/TestStatementBuilder.java b/core/trino-parser/src/test/java/io/trino/sql/parser/TestStatementBuilder.java index 755ca0829..a1796face 100644 --- a/core/trino-parser/src/test/java/io/trino/sql/parser/TestStatementBuilder.java +++ b/core/trino-parser/src/test/java/io/trino/sql/parser/TestStatementBuilder.java @@ -34,6 +34,14 @@ public class TestStatementBuilder { private static final SqlParser SQL_PARSER = new SqlParser(); + @Test + public void testPreparedGrantWithQuotes() + { + printStatement("prepare p from grant select on table hive.test.\"case\" to role test"); + printStatement("prepare p from grant select on hive.test.\"case\" to role test"); + printStatement("prepare p from grant select on table hive.test.\"case\" to role \"case\""); + } + @Test public void testStatementBuilder() {