diff --git a/cloud/docker-image/src/main/docker/assembly.xml b/cloud/docker-image/src/main/docker/assembly.xml index eaf132aa3e..7e0d989f1a 100644 --- a/cloud/docker-image/src/main/docker/assembly.xml +++ b/cloud/docker-image/src/main/docker/assembly.xml @@ -66,7 +66,6 @@ org/junit/** com/google/** org/checkerframework/** - com/github/jsqlparser/** diff --git a/incubator/binding-pgsql-kafka/NOTICE b/incubator/binding-pgsql-kafka/NOTICE index c6b7d9c015..3913dbbc71 100644 --- a/incubator/binding-pgsql-kafka/NOTICE +++ b/incubator/binding-pgsql-kafka/NOTICE @@ -10,5 +10,5 @@ WARRANTIES OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. This project includes: - JSQLParser library under GNU Library or Lesser General Public License (LGPL) V2.1 or The Apache Software License, Version 2.0 + zilla::incubator::binding-pgsql under Aklivity Community License Agreement diff --git a/incubator/binding-pgsql-kafka/pom.xml b/incubator/binding-pgsql-kafka/pom.xml index 5779c09cb9..c3bcc8b6ab 100644 --- a/incubator/binding-pgsql-kafka/pom.xml +++ b/incubator/binding-pgsql-kafka/pom.xml @@ -24,7 +24,7 @@ - 0.82 + 0.86 0 @@ -42,8 +42,8 @@ provided - com.github.jsqlparser - jsqlparser + io.aklivity.zilla + binding-pgsql ${project.groupId} @@ -195,7 +195,6 @@ io/aklivity/zilla/runtime/binding/pgsql/kafka/internal/types/**/*.class - net/sf/jsqlparser/parser/* diff --git a/incubator/binding-pgsql-kafka/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/kafka/internal/schema/PgsqlKafkaKeyAvroSchemaTemplate.java b/incubator/binding-pgsql-kafka/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/kafka/internal/schema/PgsqlKafkaKeyAvroSchemaTemplate.java index cecef1e11f..6885f5ee94 100644 --- a/incubator/binding-pgsql-kafka/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/kafka/internal/schema/PgsqlKafkaKeyAvroSchemaTemplate.java +++ b/incubator/binding-pgsql-kafka/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/kafka/internal/schema/PgsqlKafkaKeyAvroSchemaTemplate.java @@ -14,11 +14,9 @@ */ package io.aklivity.zilla.runtime.binding.pgsql.kafka.internal.schema; -import java.util.List; +import java.util.Map; -import net.sf.jsqlparser.statement.create.table.ColumnDefinition; -import net.sf.jsqlparser.statement.create.table.CreateTable; -import net.sf.jsqlparser.statement.create.table.Index; +import io.aklivity.zilla.runtime.binding.pgsql.parser.model.TableInfo; public class PgsqlKafkaKeyAvroSchemaTemplate extends PgsqlKafkaAvroSchemaTemplate { @@ -35,12 +33,12 @@ public PgsqlKafkaKeyAvroSchemaTemplate( public String generateSchema( String database, - CreateTable createTable) + TableInfo createTable) { schemaBuilder.setLength(0); final String newNamespace = namespace.replace(DATABASE_PLACEHOLDER, database); - final String recordName = String.format("%s_key", createTable.getTable().getName()); + final String recordName = String.format("%s_key", createTable.name()); schemaBuilder.append("{\n"); schemaBuilder.append("\"schemaType\": \"AVRO\",\n"); @@ -52,10 +50,10 @@ public String generateSchema( schemaBuilder.append(" \\\"namespace\\\": \\\"").append(newNamespace).append("\\\","); schemaBuilder.append(" \\\"fields\\\": ["); - for (ColumnDefinition column : createTable.getColumnDefinitions()) + for (Map.Entry column : createTable.columns().entrySet()) { - String fieldName = column.getColumnName(); - String pgsqlType = column.getColDataType().getDataType(); + String fieldName = column.getKey(); + String pgsqlType = column.getValue(); String avroType = convertPgsqlTypeToAvro(pgsqlType); @@ -72,28 +70,4 @@ public String generateSchema( return schemaBuilder.toString(); } - - public String primaryKey( - CreateTable statement) - { - String primaryKey = null; - - final List indexes = statement.getIndexes(); - - if (indexes != null && !indexes.isEmpty()) - { - match: - for (Index index : indexes) - { - if ("PRIMARY KEY".equalsIgnoreCase(index.getType())) - { - final List primaryKeyColumns = index.getColumns(); - primaryKey = primaryKeyColumns.get(0).columnName; - break match; - } - } - } - - return primaryKey; - } } diff --git a/incubator/binding-pgsql-kafka/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/kafka/internal/schema/PgsqlKafkaValueAvroSchemaTemplate.java b/incubator/binding-pgsql-kafka/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/kafka/internal/schema/PgsqlKafkaValueAvroSchemaTemplate.java index fe53279963..ebfaea7915 100644 --- a/incubator/binding-pgsql-kafka/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/kafka/internal/schema/PgsqlKafkaValueAvroSchemaTemplate.java +++ b/incubator/binding-pgsql-kafka/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/kafka/internal/schema/PgsqlKafkaValueAvroSchemaTemplate.java @@ -14,11 +14,9 @@ */ package io.aklivity.zilla.runtime.binding.pgsql.kafka.internal.schema; -import java.util.List; +import java.util.Map; -import net.sf.jsqlparser.statement.create.table.ColumnDefinition; -import net.sf.jsqlparser.statement.create.table.CreateTable; -import net.sf.jsqlparser.statement.create.table.Index; +import io.aklivity.zilla.runtime.binding.pgsql.parser.model.TableInfo; public class PgsqlKafkaValueAvroSchemaTemplate extends PgsqlKafkaAvroSchemaTemplate { @@ -35,27 +33,26 @@ public PgsqlKafkaValueAvroSchemaTemplate( public String generateSchema( String database, - CreateTable createTable) + TableInfo createTable) { schemaBuilder.setLength(0); final String newNamespace = namespace.replace(DATABASE_PLACEHOLDER, database); - final String recordName = createTable.getTable().getName(); + final String recordName = createTable.name(); schemaBuilder.append("{\n"); schemaBuilder.append("\"schemaType\": \"AVRO\",\n"); - schemaBuilder.append("\"schema\": \""); // Begin the schema field + schemaBuilder.append("\"schema\": \""); - // Building the actual Avro schema schemaBuilder.append("{\\\"type\\\": \\\"record\\\","); schemaBuilder.append(" \\\"name\\\": \\\"").append(recordName).append("\\\","); schemaBuilder.append(" \\\"namespace\\\": \\\"").append(newNamespace).append("\\\","); schemaBuilder.append(" \\\"fields\\\": ["); - for (ColumnDefinition column : createTable.getColumnDefinitions()) + for (Map.Entry column : createTable.columns().entrySet()) { - String fieldName = column.getColumnName(); - String pgsqlType = column.getColDataType().getDataType(); + String fieldName = column.getKey(); + String pgsqlType = column.getValue(); String avroType = convertPgsqlTypeToAvro(pgsqlType); @@ -63,60 +60,11 @@ public String generateSchema( schemaBuilder.append(" \\\"type\\\": ").append(avroType).append("},"); } - // Remove the last comma and close the fields array schemaBuilder.setLength(schemaBuilder.length() - 1); schemaBuilder.append("]"); - // Closing the Avro schema schemaBuilder.append("}\"\n}"); return schemaBuilder.toString(); } - - public String primaryKey( - CreateTable statement) - { - String primaryKey = null; - - final List indexes = statement.getIndexes(); - - if (indexes != null && !indexes.isEmpty()) - { - match: - for (Index index : indexes) - { - if ("PRIMARY KEY".equalsIgnoreCase(index.getType())) - { - final List primaryKeyColumns = index.getColumns(); - primaryKey = primaryKeyColumns.get(0).columnName; - break match; - } - } - } - - return primaryKey; - } - - public int primaryKeyCount( - CreateTable statement) - { - int primaryKeyCount = 0; - - final List indexes = statement.getIndexes(); - - if (indexes != null && !indexes.isEmpty()) - { - match: - for (Index index : indexes) - { - if ("PRIMARY KEY".equalsIgnoreCase(index.getType())) - { - primaryKeyCount = index.getColumns().size(); - break match; - } - } - } - - return primaryKeyCount; - } } diff --git a/incubator/binding-pgsql-kafka/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/kafka/internal/stream/PgsqlKafkaProxyFactory.java b/incubator/binding-pgsql-kafka/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/kafka/internal/stream/PgsqlKafkaProxyFactory.java index 6461b89336..5c4e740d83 100644 --- a/incubator/binding-pgsql-kafka/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/kafka/internal/stream/PgsqlKafkaProxyFactory.java +++ b/incubator/binding-pgsql-kafka/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/kafka/internal/stream/PgsqlKafkaProxyFactory.java @@ -19,13 +19,11 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Objects.requireNonNull; -import java.io.InputStreamReader; -import java.io.Reader; -import java.io.StringReader; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.function.Consumer; import java.util.function.LongFunction; import java.util.function.LongUnaryOperator; @@ -36,7 +34,6 @@ import org.agrona.collections.Long2ObjectHashMap; import org.agrona.collections.Object2ObjectHashMap; import org.agrona.concurrent.UnsafeBuffer; -import org.agrona.io.DirectBufferInputStream; import io.aklivity.zilla.runtime.binding.pgsql.kafka.internal.PgsqlKafkaConfiguration; import io.aklivity.zilla.runtime.binding.pgsql.kafka.internal.config.PgsqlKafkaBindingConfig; @@ -56,16 +53,14 @@ import io.aklivity.zilla.runtime.binding.pgsql.kafka.internal.types.stream.ResetFW; import io.aklivity.zilla.runtime.binding.pgsql.kafka.internal.types.stream.SignalFW; import io.aklivity.zilla.runtime.binding.pgsql.kafka.internal.types.stream.WindowFW; +import io.aklivity.zilla.runtime.binding.pgsql.parser.PgsqlParser; +import io.aklivity.zilla.runtime.binding.pgsql.parser.model.TableInfo; import io.aklivity.zilla.runtime.engine.EngineContext; import io.aklivity.zilla.runtime.engine.binding.BindingHandler; import io.aklivity.zilla.runtime.engine.binding.function.MessageConsumer; import io.aklivity.zilla.runtime.engine.buffer.BufferPool; import io.aklivity.zilla.runtime.engine.catalog.CatalogHandler; import io.aklivity.zilla.runtime.engine.config.BindingConfig; -import net.sf.jsqlparser.parser.CCJSqlParserManager; -import net.sf.jsqlparser.statement.Statement; -import net.sf.jsqlparser.statement.create.table.CreateTable; -import net.sf.jsqlparser.statement.drop.Drop; public final class PgsqlKafkaProxyFactory implements PgsqlKafkaStreamFactory { @@ -75,8 +70,7 @@ public final class PgsqlKafkaProxyFactory implements PgsqlKafkaStreamFactory "schema": "{\\"type\\": \\"string\\"}" }"""; - - private static final Byte STATEMENT_SEMICOLON = ';'; + private static final String SPLIT_STATEMENTS = "\"(?<=;)(?!\\x00)\""; private static final int END_OF_FIELD = 0x00; private static final int NO_ERROR_SCHEMA_VERSION_ID = -1; @@ -87,11 +81,8 @@ public final class PgsqlKafkaProxyFactory implements PgsqlKafkaStreamFactory private static final DirectBuffer EMPTY_BUFFER = new UnsafeBuffer(new byte[0]); private static final OctetsFW EMPTY_OCTETS = new OctetsFW().wrap(EMPTY_BUFFER, 0, 0); - private static final Consumer EMPTY_EXTENSION = ex -> {}; - private final CCJSqlParserManager parserManager = new CCJSqlParserManager(); - private final DirectBufferInputStream inputStream = new DirectBufferInputStream(EMPTY_BUFFER); - private final Reader reader = new InputStreamReader(inputStream); + private final PgsqlParser parser = new PgsqlParser(); private final BeginFW beginRO = new BeginFW(); private final DataFW dataRO = new DataFW(); @@ -657,26 +648,16 @@ private void doParseQuery( { final MutableDirectBuffer parserBuffer = bufferPool.buffer(parserSlot); - int statementOffset = 0; - int progress = 0; + String sql = parserBuffer.getStringWithoutLengthAscii(0, parserSlotOffset); + String[] statements = sql.split(SPLIT_STATEMENTS); - parse: - while (progress <= parserSlotOffset) + int length = statements.length; + if (length > 0) { - if (parserBuffer.getByte(progress) == STATEMENT_SEMICOLON) - { - int length = progress - statementOffset + Byte.BYTES; - if (parserBuffer.getByte(progress + Byte.BYTES) == END_OF_FIELD) - { - length += Byte.BYTES; - } - final PgsqlKafkaCommandType command = decodeCommandType(parserBuffer, statementOffset, length); - final PgsqlDecoder decoder = pgsqlDecoder.get(command); - decoder.decode(this, traceId, authorizationId, parserBuffer, statementOffset, length); - break parse; - } - - progress++; + String statement = statements[0]; + String command = parser.parseCommand(statement); + final PgsqlDecoder decoder = pgsqlDecoder.get(PgsqlKafkaCommandType.valueOf(command.getBytes())); + decoder.decode(this, traceId, authorizationId, statement); } } } @@ -1317,33 +1298,32 @@ private void decodeCreateTopicCommand( PgsqlProxy server, long traceId, long authorization, - DirectBuffer buffer, - int offset, - int length) + String statement) { if (server.commandsProcessed == 1) { + final int length = statement.length(); server.onCommandCompleted(traceId, authorization, length, PgsqlKafkaCompletionCommand.CREATE_TOPIC_COMMAND); } else if (server.commandsProcessed == 0) { - final CreateTable createTable = (CreateTable) parseStatement(buffer, offset, length); - final String topic = createTable.getTable().getName(); + final TableInfo createTable = parser.parseCreateTable(statement); + final String topic = createTable.name(); topics.clear(); topics.add(String.format("%s.%s", server.database, topic)); final PgsqlKafkaBindingConfig binding = server.binding; - final String primaryKey = binding.avroValueSchema.primaryKey(createTable); - final int primaryKeyCount = binding.avroValueSchema.primaryKeyCount(createTable); + int versionId = NO_ERROR_SCHEMA_VERSION_ID; - if (primaryKey != null) + Set primaryKeys = createTable.primaryKeys(); + if (!primaryKeys.isEmpty()) { //TODO: assign versionId to avoid test failure final String subjectKey = String.format("%s.%s-key", server.database, topic); - String keySchema = primaryKeyCount > 1 + String keySchema = primaryKeys.size() > 1 ? binding.avroKeySchema.generateSchema(server.database, createTable) : AVRO_KEY_SCHEMA; binding.catalog.register(subjectKey, keySchema); @@ -1357,7 +1337,7 @@ else if (server.commandsProcessed == 0) if (versionId != NO_VERSION_ID) { - final String policy = primaryKey != null && primaryKeyCount == 1 + final String policy = primaryKeys.size() == 1 ? "compact" : "delete"; @@ -1375,28 +1355,28 @@ private void decodeDropTopicCommand( PgsqlProxy server, long traceId, long authorization, - DirectBuffer buffer, - int offset, - int length) + String statement) { if (server.commandsProcessed == 1) { + final int length = statement.length(); server.onCommandCompleted(traceId, authorization, length, PgsqlKafkaCompletionCommand.DROP_TOPIC_COMMAND); } else if (server.commandsProcessed == 0) { - final Drop drop = (Drop) parseStatement(buffer, offset, length); - final String topic = drop.getName().getName(); - - final PgsqlKafkaBindingConfig binding = server.binding; - final String subjectKey = String.format("%s.%s-key", server.database, topic); - final String subjectValue = String.format("%s.%s-value", server.database, topic); + List drops = parser.parseDrop(statement); + drops.stream().findFirst().ifPresent(d -> + { + final PgsqlKafkaBindingConfig binding = server.binding; + final String subjectKey = String.format("%s.%s-key", server.database, d); + final String subjectValue = String.format("%s.%s-value", server.database, d); - binding.catalog.unregister(subjectKey); - binding.catalog.unregister(subjectValue); + binding.catalog.unregister(subjectKey); + binding.catalog.unregister(subjectValue); - final KafkaDeleteTopicsProxy deleteTopicsProxy = server.deleteTopicsProxy; - deleteTopicsProxy.doKafkaBegin(traceId, authorization, List.of("%s.%s".formatted(server.database, topic))); + final KafkaDeleteTopicsProxy deleteTopicsProxy = server.deleteTopicsProxy; + deleteTopicsProxy.doKafkaBegin(traceId, authorization, List.of("%s.%s".formatted(server.database, d))); + }); } } @@ -1404,82 +1384,11 @@ private void decodeUnknownCommand( PgsqlProxy server, long traceId, long authorization, - DirectBuffer buffer, - int offset, - int length) + String statement) { server.cleanup(traceId, authorization); } - private PgsqlKafkaCommandType decodeCommandType( - DirectBuffer buffer, - int offset, - int length) - { - PgsqlKafkaCommandType matchingCommand = PgsqlKafkaCommandType.UNKNOWN_COMMAND; - - command: - for (PgsqlKafkaCommandType command : PgsqlKafkaCommandType.values()) - { - int progressOffset = offset; - - boolean match = true; - for (byte b : command.value()) - { - if (buffer.getByte(progressOffset) != b) - { - match = false; - break; - } - progressOffset++; - } - - if (match) - { - matchingCommand = command; - break command; - } - } - - return matchingCommand; - } - - private Statement parseStatement( - DirectBuffer buffer, - int offset, - int length) - { - Statement statement = null; - try - { - if (decodeCommandType(buffer, offset, length). - equals(PgsqlKafkaCommandType.CREATE_TOPIC_COMMAND)) - { - String sql = buffer.getStringWithoutLengthUtf8(offset, length); - sql = sql.replace("CREATE TOPIC", "CREATE TABLE"); - statement = parserManager.parse(new StringReader(sql)); - } - if (decodeCommandType(buffer, offset, length). - equals(PgsqlKafkaCommandType.DROP_TOPIC_COMMAND)) - { - String sql = buffer.getStringWithoutLengthUtf8(offset, length); - sql = sql.replace("DROP TOPIC", "DROP TABLE"); - statement = parserManager.parse(new StringReader(sql)); - } - else - { - inputStream.wrap(buffer, offset, length); - statement = parserManager.parse(reader); - } - } - catch (Exception ignored) - { - //NOOP - } - - return statement; - } - @FunctionalInterface private interface PgsqlDecoder { @@ -1487,8 +1396,6 @@ void decode( PgsqlProxy server, long traceId, long authorization, - DirectBuffer writeBuffer, - int offset, - int length); + String statement); } } diff --git a/incubator/binding-pgsql-kafka/src/main/moditect/module-info.java b/incubator/binding-pgsql-kafka/src/main/moditect/module-info.java index ca4d84298d..e4c6b9efab 100644 --- a/incubator/binding-pgsql-kafka/src/main/moditect/module-info.java +++ b/incubator/binding-pgsql-kafka/src/main/moditect/module-info.java @@ -14,8 +14,8 @@ */ module io.aklivity.zilla.runtime.binding.pgsql.kafka { - requires net.sf.jsqlparser; requires io.aklivity.zilla.runtime.engine; + requires io.aklivity.zilla.runtime.binding.pgsql; provides io.aklivity.zilla.runtime.engine.binding.BindingFactorySpi with io.aklivity.zilla.runtime.binding.pgsql.kafka.internal.PgsqlKafkaBindingFactorySpi; diff --git a/incubator/binding-pgsql/pom.xml b/incubator/binding-pgsql/pom.xml index 73bf387b5f..4c0cd40a3d 100644 --- a/incubator/binding-pgsql/pom.xml +++ b/incubator/binding-pgsql/pom.xml @@ -24,7 +24,7 @@ - 0.89 + 0.91 0 @@ -35,6 +35,11 @@ ${project.version} provided + + org.antlr + antlr4-runtime + provided + ${project.groupId} engine @@ -107,6 +112,10 @@ + + org.antlr + antlr4-maven-plugin + com.mycila license-maven-plugin @@ -191,6 +200,7 @@ io/aklivity/zilla/runtime/binding/pgsql/internal/types/**/*.class + io/aklivity/zilla/runtime/binding/pgsql/parser/**/*.class diff --git a/incubator/binding-pgsql/src/main/antlr4/io/aklivity/zilla/runtime/binding/pgsql/parser/PostgreSqlLexer.g4 b/incubator/binding-pgsql/src/main/antlr4/io/aklivity/zilla/runtime/binding/pgsql/parser/PostgreSqlLexer.g4 new file mode 100644 index 0000000000..6c7dfef3cb --- /dev/null +++ b/incubator/binding-pgsql/src/main/antlr4/io/aklivity/zilla/runtime/binding/pgsql/parser/PostgreSqlLexer.g4 @@ -0,0 +1,1667 @@ +/* + * Copyright 2021-2023 Aklivity Inc + * + * Licensed under the Aklivity Community License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of the + * License at + * + * https://www.aklivity.io/aklivity-community-license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +lexer grammar PostgreSqlLexer; +/* Reference: + * http://www.postgresql.org/docs/9.3/static/sql-syntax-lexical.html + */ + +options { + superClass = PostgreSqlLexerBase; + caseInsensitive = true; +} + +@header { +} +@members { +/* This field stores the tags which are used to detect the end of a dollar-quoted string literal. + */ +} +// + +// SPECIAL CHARACTERS (4.1.4) + +// + +// Note that Asterisk is a valid operator, but does not have the type Operator due to its syntactic use in locations + +// that are not expressions. + +Dollar: '$'; + +OPEN_PAREN: '('; + +CLOSE_PAREN: ')'; + +OPEN_BRACKET: '['; + +CLOSE_BRACKET: ']'; + +COMMA: ','; + +SEMI: ';'; + +COLON: ':'; + +STAR: '*'; + +EQUAL: '='; + +DOT: '.'; +//NamedArgument : ':='; + +PLUS: '+'; + +MINUS: '-'; + +SLASH: '/'; + +CARET: '^'; + +LT: '<'; + +GT: '>'; + +LESS_LESS: '<<'; + +GREATER_GREATER: '>>'; + +COLON_EQUALS: ':='; + +LESS_EQUALS: '<='; + +EQUALS_GREATER: '=>'; + +GREATER_EQUALS: '>='; + +DOT_DOT: '..'; + +NOT_EQUALS: '<>'; + +TYPECAST: '::'; + +PERCENT: '%'; + +PARAM: '$' ([0-9])+; +// + +// OPERATORS (4.1.3) + +// + +// this rule does not allow + or - at the end of a multi-character operator + +Operator: + ( + ( + OperatorCharacter + | ('+' | '-' {checkLA('-')}?)+ (OperatorCharacter | '/' {checkLA('*')}?) + | '/' {checkLA('*')}? + )+ + | // special handling for the single-character operators + and - + [+-] + ) + //TODO somehow rewrite this part without using Actions + { + handleLessLessGreaterGreater(); + } +; +/* This rule handles operators which end with + or -, and sets the token type to Operator. It is comprised of four + * parts, in order: + * + * 1. A prefix, which does not contain a character from the required set which allows + or - to appear at the end of + * the operator. + * 2. A character from the required set which allows + or - to appear at the end of the operator. + * 3. An optional sub-token which takes the form of an operator which does not include a + or - at the end of the + * sub-token. + * 4. A suffix sequence of + and - characters. + */ + +OperatorEndingWithPlusMinus: + (OperatorCharacterNotAllowPlusMinusAtEnd | '-' {checkLA('-')}? | '/' {checkLA('*')}?)* OperatorCharacterAllowPlusMinusAtEnd Operator? ( + '+' + | '-' {checkLA('-')}? + )+ -> type (Operator) +; +// Each of the following fragment rules omits the +, -, and / characters, which must always be handled in a special way + +// by the operator rules above. + +fragment OperatorCharacter: [*<>=~!@%^&|`?#]; +// these are the operator characters that don't count towards one ending with + or - + +fragment OperatorCharacterNotAllowPlusMinusAtEnd: [*<>=+]; +// an operator may end with + or - if it contains one of these characters + +fragment OperatorCharacterAllowPlusMinusAtEnd: [~!@%^&|`?#]; +// + +// KEYWORDS (Appendix C) + +// + +// + +// reserved keywords + +// + +ALL: 'ALL'; + +ANALYSE: 'ANALYSE'; + +ANALYZE: 'ANALYZE'; + +AND: 'AND'; + +ANY: 'ANY'; + +ARRAY: 'ARRAY'; + +AS: 'AS'; + +ASC: 'ASC'; + +ASYMMETRIC: 'ASYMMETRIC'; + +BOTH: 'BOTH'; + +CASE: 'CASE'; + +CAST: 'CAST'; + +CHECK: 'CHECK'; + +COLLATE: 'COLLATE'; + +COLUMN: 'COLUMN'; + +CONSTRAINT: 'CONSTRAINT'; + +CREATE: 'CREATE'; + +CURRENT_CATALOG: 'CURRENT_CATALOG'; + +CURRENT_DATE: 'CURRENT_DATE'; + +CURRENT_ROLE: 'CURRENT_ROLE'; + +CURRENT_TIME: 'CURRENT_TIME'; + +CURRENT_TIMESTAMP: 'CURRENT_TIMESTAMP'; + +CURRENT_USER: 'CURRENT_USER'; + +DEFAULT: 'DEFAULT'; + +DEFERRABLE: 'DEFERRABLE'; + +DESC: 'DESC'; + +DISTINCT: 'DISTINCT'; + +DO: 'DO'; + +ELSE: 'ELSE'; + +EXCEPT: 'EXCEPT'; + +FALSE_P: 'FALSE'; + +FETCH: 'FETCH'; + +FOR: 'FOR'; + +FOREIGN: 'FOREIGN'; + +FROM: 'FROM'; + +GRANT: 'GRANT'; + +GROUP_P: 'GROUP'; + +HAVING: 'HAVING'; + +IN_P: 'IN'; + +INITIALLY: 'INITIALLY'; + +INTERSECT: 'INTERSECT'; + +INTO: 'INTO'; + +LATERAL_P: 'LATERAL'; + +LEADING: 'LEADING'; + +LIMIT: 'LIMIT'; + +LOCALTIME: 'LOCALTIME'; + +LOCALTIMESTAMP: 'LOCALTIMESTAMP'; + +NOT: 'NOT'; + +NULL_P: 'NULL'; + +OFFSET: 'OFFSET'; + +ON: 'ON'; + +ONLY: 'ONLY'; + +OR: 'OR'; + +ORDER: 'ORDER'; + +PLACING: 'PLACING'; + +PRIMARY: 'PRIMARY'; + +REFERENCES: 'REFERENCES'; + +RETURNING: 'RETURNING'; + +SELECT: 'SELECT'; + +SESSION_USER: 'SESSION_USER'; + +SOME: 'SOME'; + +SYMMETRIC: 'SYMMETRIC'; + +TABLE: 'TABLE'; + +THEN: 'THEN'; + +TO: 'TO'; + +TOPIC: 'TOPIC'; + +STREAM: 'STREAM'; + +TRAILING: 'TRAILING'; + +TRUE_P: 'TRUE'; + +UNION: 'UNION'; + +UNIQUE: 'UNIQUE'; + +USER: 'USER'; + +USING: 'USING'; + +VARIADIC: 'VARIADIC'; + +WHEN: 'WHEN'; + +WHERE: 'WHERE'; + +WINDOW: 'WINDOW'; + +WITH: 'WITH'; + +// + +// reserved keywords (can be function or type) + +// + +AUTHORIZATION: 'AUTHORIZATION'; + +BINARY: 'BINARY'; + +COLLATION: 'COLLATION'; + +CONCURRENTLY: 'CONCURRENTLY'; + +CROSS: 'CROSS'; + +CURRENT_SCHEMA: 'CURRENT_SCHEMA'; + +FREEZE: 'FREEZE'; + +FULL: 'FULL'; + +ILIKE: 'ILIKE'; + +INNER_P: 'INNER'; + +IS: 'IS'; + +ISNULL: 'ISNULL'; + +JOIN: 'JOIN'; + +LEFT: 'LEFT'; + +LIKE: 'LIKE'; + +NATURAL: 'NATURAL'; + +NOTNULL: 'NOTNULL'; + +OUTER_P: 'OUTER'; + +OVER: 'OVER'; + +OVERLAPS: 'OVERLAPS'; + +RIGHT: 'RIGHT'; + +SIMILAR: 'SIMILAR'; + +VERBOSE: 'VERBOSE'; +// + +// non-reserved keywords + +// + +ABORT_P: 'ABORT'; + +ABSOLUTE_P: 'ABSOLUTE'; + +ACCESS: 'ACCESS'; + +ACTION: 'ACTION'; + +ADD_P: 'ADD'; + +ADMIN: 'ADMIN'; + +AFTER: 'AFTER'; + +AGGREGATE: 'AGGREGATE'; + +ALSO: 'ALSO'; + +ALTER: 'ALTER'; + +ALWAYS: 'ALWAYS'; + +ASSERTION: 'ASSERTION'; + +ASSIGNMENT: 'ASSIGNMENT'; + +AT: 'AT'; + +ATTRIBUTE: 'ATTRIBUTE'; + +BACKWARD: 'BACKWARD'; + +BEFORE: 'BEFORE'; + +BEGIN_P: 'BEGIN'; + +BY: 'BY'; + +CACHE: 'CACHE'; + +CALLED: 'CALLED'; + +CASCADE: 'CASCADE'; + +CASCADED: 'CASCADED'; + +CATALOG: 'CATALOG'; + +CHAIN: 'CHAIN'; + +CHARACTERISTICS: 'CHARACTERISTICS'; + +CHECKPOINT: 'CHECKPOINT'; + +CLASS: 'CLASS'; + +CLOSE: 'CLOSE'; + +CLUSTER: 'CLUSTER'; + +COMMENT: 'COMMENT'; + +COMMENTS: 'COMMENTS'; + +COMMIT: 'COMMIT'; + +COMMITTED: 'COMMITTED'; + +CONFIGURATION: 'CONFIGURATION'; + +CONNECTION: 'CONNECTION'; + +CONSTRAINTS: 'CONSTRAINTS'; + +CONTENT_P: 'CONTENT'; + +CONTINUE_P: 'CONTINUE'; + +CONVERSION_P: 'CONVERSION'; + +COPY: 'COPY'; + +COST: 'COST'; + +CSV: 'CSV'; + +CURSOR: 'CURSOR'; + +CYCLE: 'CYCLE'; + +DATA_P: 'DATA'; + +DATABASE: 'DATABASE'; + +DAY_P: 'DAY'; + +DEALLOCATE: 'DEALLOCATE'; + +DECLARE: 'DECLARE'; + +DEFAULTS: 'DEFAULTS'; + +DEFERRED: 'DEFERRED'; + +DEFINER: 'DEFINER'; + +DELETE_P: 'DELETE'; + +DELIMITER: 'DELIMITER'; + +DELIMITERS: 'DELIMITERS'; + +DICTIONARY: 'DICTIONARY'; + +DISABLE_P: 'DISABLE'; + +DISCARD: 'DISCARD'; + +DOCUMENT_P: 'DOCUMENT'; + +DOMAIN_P: 'DOMAIN'; + +DOUBLE_P: 'DOUBLE'; + +DROP: 'DROP'; + +EACH: 'EACH'; + +ENABLE_P: 'ENABLE'; + +ENCODING: 'ENCODING'; + +ENCRYPTED: 'ENCRYPTED'; + +ENUM_P: 'ENUM'; + +ESCAPE: 'ESCAPE'; + +EVENT: 'EVENT'; + +EXCLUDE: 'EXCLUDE'; + +EXCLUDING: 'EXCLUDING'; + +EXCLUSIVE: 'EXCLUSIVE'; + +EXECUTE: 'EXECUTE'; + +EXPLAIN: 'EXPLAIN'; + +EXTENSION: 'EXTENSION'; + +EXTERNAL: 'EXTERNAL'; + +FAMILY: 'FAMILY'; + +FIRST_P: 'FIRST'; + +FOLLOWING: 'FOLLOWING'; + +FORCE: 'FORCE'; + +FORWARD: 'FORWARD'; + +FUNCTION: 'FUNCTION'; + +FUNCTIONS: 'FUNCTIONS'; + +GLOBAL: 'GLOBAL'; + +GRANTED: 'GRANTED'; + +HANDLER: 'HANDLER'; + +HEADER_P: 'HEADER'; + +HOLD: 'HOLD'; + +HOUR_P: 'HOUR'; + +IDENTITY_P: 'IDENTITY'; + +IF_P: 'IF'; + +IMMEDIATE: 'IMMEDIATE'; + +IMMUTABLE: 'IMMUTABLE'; + +IMPLICIT_P: 'IMPLICIT'; + +INCLUDING: 'INCLUDING'; + +INCREMENT: 'INCREMENT'; + +INDEX: 'INDEX'; + +INDEXES: 'INDEXES'; + +INHERIT: 'INHERIT'; + +INHERITS: 'INHERITS'; + +INLINE_P: 'INLINE'; + +INSENSITIVE: 'INSENSITIVE'; + +INSERT: 'INSERT'; + +INSTEAD: 'INSTEAD'; + +INVOKER: 'INVOKER'; + +ISOLATION: 'ISOLATION'; + +KEY: 'KEY'; + +LABEL: 'LABEL'; + +LANGUAGE: 'LANGUAGE'; + +LARGE_P: 'LARGE'; + +LAST_P: 'LAST'; +//LC_COLLATE : 'LC'_'COLLATE; + +//LC_CTYPE : 'LC'_'CTYPE; + +LEAKPROOF: 'LEAKPROOF'; + +LEVEL: 'LEVEL'; + +LISTEN: 'LISTEN'; + +LOAD: 'LOAD'; + +LOCAL: 'LOCAL'; + +LOCATION: 'LOCATION'; + +LOCK_P: 'LOCK'; + +MAPPING: 'MAPPING'; + +MATCH: 'MATCH'; + +MATCHED: 'MATCHED'; + +MATERIALIZED: 'MATERIALIZED'; + +MAXVALUE: 'MAXVALUE'; + +MERGE: 'MERGE'; + +MINUTE_P: 'MINUTE'; + +MINVALUE: 'MINVALUE'; + +MODE: 'MODE'; + +MONTH_P: 'MONTH'; + +MOVE: 'MOVE'; + +NAME_P: 'NAME'; + +NAMES: 'NAMES'; + +NEXT: 'NEXT'; + +NO: 'NO'; + +NOTHING: 'NOTHING'; + +NOTIFY: 'NOTIFY'; + +NOWAIT: 'NOWAIT'; + +NULLS_P: 'NULLS'; + +OBJECT_P: 'OBJECT'; + +OF: 'OF'; + +OFF: 'OFF'; + +OIDS: 'OIDS'; + +OPERATOR: 'OPERATOR'; + +OPTION: 'OPTION'; + +OPTIONS: 'OPTIONS'; + +OWNED: 'OWNED'; + +OWNER: 'OWNER'; + +PARSER: 'PARSER'; + +PARTIAL: 'PARTIAL'; + +PARTITION: 'PARTITION'; + +PASSING: 'PASSING'; + +PASSWORD: 'PASSWORD'; + +PLANS: 'PLANS'; + +PRECEDING: 'PRECEDING'; + +PREPARE: 'PREPARE'; + +PREPARED: 'PREPARED'; + +PRESERVE: 'PRESERVE'; + +PRIOR: 'PRIOR'; + +PRIVILEGES: 'PRIVILEGES'; + +PROCEDURAL: 'PROCEDURAL'; + +PROCEDURE: 'PROCEDURE'; + +PROGRAM: 'PROGRAM'; + +QUOTE: 'QUOTE'; + +RANGE: 'RANGE'; + +READ: 'READ'; + +REASSIGN: 'REASSIGN'; + +RECHECK: 'RECHECK'; + +RECURSIVE: 'RECURSIVE'; + +REF: 'REF'; + +REFRESH: 'REFRESH'; + +REINDEX: 'REINDEX'; + +RELATIVE_P: 'RELATIVE'; + +RELEASE: 'RELEASE'; + +RENAME: 'RENAME'; + +REPEATABLE: 'REPEATABLE'; + +REPLACE: 'REPLACE'; + +REPLICA: 'REPLICA'; + +RESET: 'RESET'; + +RESTART: 'RESTART'; + +RESTRICT: 'RESTRICT'; + +RETURNS: 'RETURNS'; + +REVOKE: 'REVOKE'; + +ROLE: 'ROLE'; + +ROLLBACK: 'ROLLBACK'; + +ROWS: 'ROWS'; + +RULE: 'RULE'; + +SAVEPOINT: 'SAVEPOINT'; + +SCHEMA: 'SCHEMA'; + +SCROLL: 'SCROLL'; + +SEARCH: 'SEARCH'; + +SECOND_P: 'SECOND'; + +SECURITY: 'SECURITY'; + +SEQUENCE: 'SEQUENCE'; + +SEQUENCES: 'SEQUENCES'; + +SERIALIZABLE: 'SERIALIZABLE'; + +SERVER: 'SERVER'; + +SESSION: 'SESSION'; + +SET: 'SET'; + +SHARE: 'SHARE'; + +SHOW: 'SHOW'; + +SIMPLE: 'SIMPLE'; + +SNAPSHOT: 'SNAPSHOT'; + +STABLE: 'STABLE'; + +STANDALONE_P: 'STANDALONE'; + +START: 'START'; + +STATEMENT: 'STATEMENT'; + +STATISTICS: 'STATISTICS'; + +STDIN: 'STDIN'; + +STDOUT: 'STDOUT'; + +STORAGE: 'STORAGE'; + +STRICT_P: 'STRICT'; + +STRIP_P: 'STRIP'; + +SYSID: 'SYSID'; + +SYSTEM_P: 'SYSTEM'; + +TABLES: 'TABLES'; + +TABLESPACE: 'TABLESPACE'; + +TEMP: 'TEMP'; + +TEMPLATE: 'TEMPLATE'; + +TEMPORARY: 'TEMPORARY'; + +TEXT_P: 'TEXT'; + +TRANSACTION: 'TRANSACTION'; + +TRIGGER: 'TRIGGER'; + +TRUNCATE: 'TRUNCATE'; + +TRUSTED: 'TRUSTED'; + +TYPE_P: 'TYPE'; + +TYPES_P: 'TYPES'; + +UNBOUNDED: 'UNBOUNDED'; + +UNCOMMITTED: 'UNCOMMITTED'; + +UNENCRYPTED: 'UNENCRYPTED'; + +UNKNOWN: 'UNKNOWN'; + +UNLISTEN: 'UNLISTEN'; + +UNLOGGED: 'UNLOGGED'; + +UNTIL: 'UNTIL'; + +UPDATE: 'UPDATE'; + +VACUUM: 'VACUUM'; + +VALID: 'VALID'; + +VALIDATE: 'VALIDATE'; + +VALIDATOR: 'VALIDATOR'; +//VALUE : 'VALUE; + +VARYING: 'VARYING'; + +VERSION_P: 'VERSION'; + +VIEW: 'VIEW'; + +VOLATILE: 'VOLATILE'; + +WHITESPACE_P: 'WHITESPACE'; + +WITHOUT: 'WITHOUT'; + +WORK: 'WORK'; + +WRAPPER: 'WRAPPER'; + +WRITE: 'WRITE'; + +XML_P: 'XML'; + +YEAR_P: 'YEAR'; + +YES_P: 'YES'; + +ZONE: 'ZONE'; +// + +// non-reserved keywords (can not be function or type) + +// + +BETWEEN: 'BETWEEN'; + +BIGINT: 'BIGINT'; + +BIT: 'BIT'; + +BOOLEAN_P: 'BOOLEAN'; + +CHAR_P: 'CHAR'; + +CHARACTER: 'CHARACTER'; + +COALESCE: 'COALESCE'; + +DEC: 'DEC'; + +DECIMAL_P: 'DECIMAL'; + +EXISTS: 'EXISTS'; + +EXTRACT: 'EXTRACT'; + +FLOAT_P: 'FLOAT'; + +GREATEST: 'GREATEST'; + +INOUT: 'INOUT'; + +INT_P: 'INT'; + +INTEGER: 'INTEGER'; + +INTERVAL: 'INTERVAL'; + +LEAST: 'LEAST'; + +NATIONAL: 'NATIONAL'; + +NCHAR: 'NCHAR'; + +NONE: 'NONE'; + +NULLIF: 'NULLIF'; + +NUMERIC: 'NUMERIC'; + +OVERLAY: 'OVERLAY'; + +POSITION: 'POSITION'; + +PRECISION: 'PRECISION'; + +REAL: 'REAL'; + +ROW: 'ROW'; + +SETOF: 'SETOF'; + +SMALLINT: 'SMALLINT'; + +SUBSTRING: 'SUBSTRING'; + +TIME: 'TIME'; + +TIMESTAMP: 'TIMESTAMP'; + +TREAT: 'TREAT'; + +TRIM: 'TRIM'; + +VALUES: 'VALUES'; + +VARCHAR: 'VARCHAR'; + +XMLATTRIBUTES: 'XMLATTRIBUTES'; + +XMLCOMMENT: 'XMLCOMMENT'; + +XMLAGG: 'XMLAGG'; + +XML_IS_WELL_FORMED: 'XML_IS_WELL_FORMED'; + +XML_IS_WELL_FORMED_DOCUMENT: 'XML_IS_WELL_FORMED_DOCUMENT'; + +XML_IS_WELL_FORMED_CONTENT: 'XML_IS_WELL_FORMED_CONTENT'; + +XPATH: 'XPATH'; + +XPATH_EXISTS: 'XPATH_EXISTS'; + +XMLCONCAT: 'XMLCONCAT'; + +XMLELEMENT: 'XMLELEMENT'; + +XMLEXISTS: 'XMLEXISTS'; + +XMLFOREST: 'XMLFOREST'; + +XMLPARSE: 'XMLPARSE'; + +XMLPI: 'XMLPI'; + +XMLROOT: 'XMLROOT'; + +XMLSERIALIZE: 'XMLSERIALIZE'; +//MISSED + +CALL: 'CALL'; + +CURRENT_P: 'CURRENT'; + +ATTACH: 'ATTACH'; + +DETACH: 'DETACH'; + +EXPRESSION: 'EXPRESSION'; + +GENERATED: 'GENERATED'; + +LOGGED: 'LOGGED'; + +STORED: 'STORED'; + +INCLUDE: 'INCLUDE'; + +ROUTINE: 'ROUTINE'; + +TRANSFORM: 'TRANSFORM'; + +IMPORT_P: 'IMPORT'; + +POLICY: 'POLICY'; + +METHOD: 'METHOD'; + +REFERENCING: 'REFERENCING'; + +NEW: 'NEW'; + +OLD: 'OLD'; + +VALUE_P: 'VALUE'; + +SUBSCRIPTION: 'SUBSCRIPTION'; + +PUBLICATION: 'PUBLICATION'; + +OUT_P: 'OUT'; + +END_P: 'END'; + +ROUTINES: 'ROUTINES'; + +SCHEMAS: 'SCHEMAS'; + +PROCEDURES: 'PROCEDURES'; + +INPUT_P: 'INPUT'; + +SUPPORT: 'SUPPORT'; + +PARALLEL: 'PARALLEL'; + +SQL_P: 'SQL'; + +DEPENDS: 'DEPENDS'; + +OVERRIDING: 'OVERRIDING'; + +CONFLICT: 'CONFLICT'; + +SKIP_P: 'SKIP'; + +LOCKED: 'LOCKED'; + +TIES: 'TIES'; + +ROLLUP: 'ROLLUP'; + +CUBE: 'CUBE'; + +GROUPING: 'GROUPING'; + +SETS: 'SETS'; + +TABLESAMPLE: 'TABLESAMPLE'; + +ORDINALITY: 'ORDINALITY'; + +XMLTABLE: 'XMLTABLE'; + +COLUMNS: 'COLUMNS'; + +XMLNAMESPACES: 'XMLNAMESPACES'; + +ROWTYPE: 'ROWTYPE'; + +NORMALIZED: 'NORMALIZED'; + +WITHIN: 'WITHIN'; + +FILTER: 'FILTER'; + +GROUPS: 'GROUPS'; + +OTHERS: 'OTHERS'; + +NFC: 'NFC'; + +NFD: 'NFD'; + +NFKC: 'NFKC'; + +NFKD: 'NFKD'; + +UESCAPE: 'UESCAPE'; + +VIEWS: 'VIEWS'; + +NORMALIZE: 'NORMALIZE'; + +DUMP: 'DUMP'; + +PRINT_STRICT_PARAMS: 'PRINT_STRICT_PARAMS'; + +VARIABLE_CONFLICT: 'VARIABLE_CONFLICT'; + +ERROR: 'ERROR'; + +USE_VARIABLE: 'USE_VARIABLE'; + +USE_COLUMN: 'USE_COLUMN'; + +ALIAS: 'ALIAS'; + +CONSTANT: 'CONSTANT'; + +PERFORM: 'PERFORM'; + +GET: 'GET'; + +DIAGNOSTICS: 'DIAGNOSTICS'; + +STACKED: 'STACKED'; + +ELSIF: 'ELSIF'; + +WHILE: 'WHILE'; + +REVERSE: 'REVERSE'; + +FOREACH: 'FOREACH'; + +SLICE: 'SLICE'; + +EXIT: 'EXIT'; + +RETURN: 'RETURN'; + +QUERY: 'QUERY'; + +RAISE: 'RAISE'; + +SQLSTATE: 'SQLSTATE'; + +DEBUG: 'DEBUG'; + +LOG: 'LOG'; + +INFO: 'INFO'; + +NOTICE: 'NOTICE'; + +WARNING: 'WARNING'; + +EXCEPTION: 'EXCEPTION'; + +ASSERT: 'ASSERT'; + +LOOP: 'LOOP'; + +OPEN: 'OPEN'; +// + +// IDENTIFIERS (4.1.1) + +// + +ABS: 'ABS'; + +CBRT: 'CBRT'; + +CEIL: 'CEIL'; + +CEILING: 'CEILING'; + +DEGREES: 'DEGREES'; + +DIV: 'DIV'; + +EXP: 'EXP'; + +FACTORIAL: 'FACTORIAL'; + +FLOOR: 'FLOOR'; + +GCD: 'GCD'; + +LCM: 'LCM'; + +LN: 'LN'; + +LOG10: 'LOG10'; + +MIN_SCALE: 'MIN_SCALE'; + +MOD: 'MOD'; + +PI: 'PI'; + +POWER: 'POWER'; + +RADIANS: 'RADIANS'; + +ROUND: 'ROUND'; + +SCALE: 'SCALE'; + +SIGN: 'SIGN'; + +SQRT: 'SQRT'; + +TRIM_SCALE: 'TRIM_SCALE'; + +TRUNC: 'TRUNC'; + +WIDTH_BUCKET: 'WIDTH_BUCKET'; + +RANDOM: 'RANDOM'; + +SETSEED: 'SETSEED'; + +ACOS: 'ACOS'; + +ACOSD: 'ACOSD'; + +ASIN: 'ASIN'; + +ASIND: 'ASIND'; + +ATAN: 'ATAN'; + +ATAND: 'ATAND'; + +ATAN2: 'ATAN2'; + +ATAN2D: 'ATAN2D'; + +COS: 'COS'; + +COSD: 'COSD'; + +COT: 'COT'; + +COTD: 'COTD'; + +SIN: 'SIN'; + +SIND: 'SIND'; + +TAN: 'TAN'; + +TAND: 'TAND'; + +SINH: 'SINH'; + +COSH: 'COSH'; + +TANH: 'TANH'; + +ASINH: 'ASINH'; + +ACOSH: 'ACOSH'; + +ATANH: 'ATANH'; + +BIT_LENGTH: 'BIT_LENGTH'; + +CHAR_LENGTH: 'CHAR_LENGTH'; + +CHARACTER_LENGTH: 'CHARACTER_LENGTH'; + +LOWER: 'LOWER'; + +OCTET_LENGTH: 'OCTET_LENGTH'; + +UPPER: 'UPPER'; + +ASCII: 'ASCII'; + +BTRIM: 'BTRIM'; + +CHR: 'CHR'; + +CONCAT: 'CONCAT'; + +CONCAT_WS: 'CONCAT_WS'; + +FORMAT: 'FORMAT'; + +INITCAP: 'INITCAP'; + +LENGTH: 'LENGTH'; + +LPAD: 'LPAD'; + +LTRIM: 'LTRIM'; + +MD5: 'MD5'; + +PARSE_IDENT: 'PARSE_IDENT'; + +PG_CLIENT_ENCODING: 'PG_CLIENT_ENCODING'; + +QUOTE_IDENT: 'QUOTE_IDENT'; + +QUOTE_LITERAL: 'QUOTE_LITERAL'; + +QUOTE_NULLABLE: 'QUOTE_NULLABLE'; + +REGEXP_COUNT: 'REGEXP_COUNT'; + +REGEXP_INSTR: 'REGEXP_INSTR'; + +REGEXP_LIKE: 'REGEXP_LIKE'; + +REGEXP_MATCH: 'REGEXP_MATCH'; + +REGEXP_MATCHES: 'REGEXP_MATCHES'; + +REGEXP_REPLACE: 'REGEXP_REPLACE'; + +REGEXP_SPLIT_TO_ARRAY: 'REGEXP_SPLIT_TO_ARRAY'; + +REGEXP_SPLIT_TO_TABLE: 'REGEXP_SPLIT_TO_TABLE'; + +REGEXP_SUBSTR: 'REGEXP_SUBSTR'; + +REPEAT: 'REPEAT'; + +RPAD: 'RPAD'; + +RTRIM: 'RTRIM'; + +SPLIT_PART: 'SPLIT_PART'; + +STARTS_WITH: 'STARTS_WITH'; + +STRING_TO_ARRAY: 'STRING_TO_ARRAY'; + +STRING_TO_TABLE: 'STRING_TO_TABLE'; + +STRPOS: 'STRPOS'; + +SUBSTR: 'SUBSTR'; + +TO_ASCII: 'TO_ASCII'; + +TO_HEX: 'TO_HEX'; + +TRANSLATE: 'TRANSLATE'; + +UNISTR: 'UNISTR'; + +AGE: 'AGE'; + +CLOCK_TIMESTAMP: 'CLOCK_TIMESTAMP'; + +DATE_BIN: 'DATE_BIN'; + +DATE_PART: 'DATE_PART'; + +DATE_TRUNC: 'DATE_TRUNC'; + +ISFINITE: 'ISFINITE'; + +JUSTIFY_DAYS: 'JUSTIFY_DAYS'; + +JUSTIFY_HOURS: 'JUSTIFY_HOURS'; + +JUSTIFY_INTERVAL: 'JUSTIFY_INTERVAL'; + +MAKE_DATE: 'MAKE_DATE'; + +MAKE_INTERVAL: 'MAKE_INTERVAL'; + +MAKE_TIME: 'MAKE_TIME'; + +MAKE_TIMESTAMP: 'MAKE_TIMESTAMP'; + +MAKE_TIMESTAMPTZ: 'MAKE_TIMESTAMPTZ'; + +NOW: 'NOW'; + +STATEMENT_TIMESTAMP: 'STATEMENT_TIMESTAMP'; + +TIMEOFDAY: 'TIMEOFDAY'; + +TRANSACTION_TIMESTAMP: 'TRANSACTION_TIMESTAMP'; + +TO_TIMESTAMP: 'TO_TIMESTAMP'; + +TO_CHAR: 'TO_CHAR'; + +TO_DATE: 'TO_DATE'; + +TO_NUMBER: 'TO_NUMBER'; + +Identifier: IdentifierStartChar IdentifierChar*; + +fragment IdentifierStartChar options { + caseInsensitive = false; +}: // these are the valid identifier start characters below 0x7F + [a-zA-Z_] + | // these are the valid characters from 0x80 to 0xFF + [\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF] + | // these are the letters above 0xFF which only need a single UTF-16 code unit + [\u0100-\uD7FF\uE000-\uFFFF] {charIsLetter()}? + | // letters which require multiple UTF-16 code units + [\uD800-\uDBFF] [\uDC00-\uDFFF] { + checkIfUtf32Letter() + }? +; + +fragment IdentifierChar: StrictIdentifierChar | '$'; + +fragment StrictIdentifierChar: IdentifierStartChar | [0-9]; +/* Quoted Identifiers + * + * These are divided into four separate tokens, allowing distinction of valid quoted identifiers from invalid quoted + * identifiers without sacrificing the ability of the lexer to reliably recover from lexical errors in the input. + */ + +QuotedIdentifier: UnterminatedQuotedIdentifier '"'; +// This is a quoted identifier which only contains valid characters but is not terminated + +UnterminatedQuotedIdentifier: '"' ('""' | ~ [\u0000"])*; +// This is a quoted identifier which is terminated but contains a \u0000 character + +InvalidQuotedIdentifier: InvalidUnterminatedQuotedIdentifier '"'; +// This is a quoted identifier which is unterminated and contains a \u0000 character + +InvalidUnterminatedQuotedIdentifier: '"' ('""' | ~ '"')*; +/* Unicode Quoted Identifiers + * + * These are divided into four separate tokens, allowing distinction of valid Unicode quoted identifiers from invalid + * Unicode quoted identifiers without sacrificing the ability of the lexer to reliably recover from lexical errors in + * the input. Note that escape sequences are never checked as part of this determination due to the ability of users + * to change the escape character with a UESCAPE clause following the Unicode quoted identifier. + * + * TODO: these rules assume "" is still a valid escape sequence within a Unicode quoted identifier. + */ + +UnicodeQuotedIdentifier: 'U' '&' QuotedIdentifier; +// This is a Unicode quoted identifier which only contains valid characters but is not terminated + +UnterminatedUnicodeQuotedIdentifier: 'U' '&' UnterminatedQuotedIdentifier; +// This is a Unicode quoted identifier which is terminated but contains a \u0000 character + +InvalidUnicodeQuotedIdentifier: 'U' '&' InvalidQuotedIdentifier; +// This is a Unicode quoted identifier which is unterminated and contains a \u0000 character + +InvalidUnterminatedUnicodeQuotedIdentifier: 'U' '&' InvalidUnterminatedQuotedIdentifier; +// + +// CONSTANTS (4.1.2) + +// + +// String Constants (4.1.2.1) + +StringConstant: UnterminatedStringConstant '\''; + +UnterminatedStringConstant: '\'' ('\'\'' | ~ '\'')*; +// String Constants with C-style Escapes (4.1.2.2) + +BeginEscapeStringConstant: 'E' '\'' -> more, pushMode (EscapeStringConstantMode); +// String Constants with Unicode Escapes (4.1.2.3) + +// + +// Note that escape sequences are never checked as part of this token due to the ability of users to change the escape + +// character with a UESCAPE clause following the Unicode string constant. + +// + +// TODO: these rules assume '' is still a valid escape sequence within a Unicode string constant. + +UnicodeEscapeStringConstant: UnterminatedUnicodeEscapeStringConstant '\''; + +UnterminatedUnicodeEscapeStringConstant: 'U' '&' UnterminatedStringConstant; +// Dollar-quoted String Constants (4.1.2.4) + +BeginDollarStringConstant: '$' Tag? '$' {pushTag();} -> pushMode (DollarQuotedStringMode); +/* "The tag, if any, of a dollar-quoted string follows the same rules as an + * unquoted identifier, except that it cannot contain a dollar sign." + */ + +fragment Tag: IdentifierStartChar StrictIdentifierChar*; +// Bit-strings Constants (4.1.2.5) + +BinaryStringConstant: UnterminatedBinaryStringConstant '\''; + +UnterminatedBinaryStringConstant: 'B' '\'' [01]*; + +InvalidBinaryStringConstant: InvalidUnterminatedBinaryStringConstant '\''; + +InvalidUnterminatedBinaryStringConstant: 'B' UnterminatedStringConstant; + +HexadecimalStringConstant: UnterminatedHexadecimalStringConstant '\''; + +UnterminatedHexadecimalStringConstant: 'X' '\'' [0-9A-F]*; + +InvalidHexadecimalStringConstant: InvalidUnterminatedHexadecimalStringConstant '\''; + +InvalidUnterminatedHexadecimalStringConstant: 'X' UnterminatedStringConstant; +// Numeric Constants (4.1.2.6) + +Integral: Digits; + +NumericFail: Digits '..' {handleNumericFail();}; + +Numeric: + Digits '.' Digits? /*? replaced with + to solve problem with DOT_DOT .. but this surely must be rewriten */ ( + 'E' [+-]? Digits + )? + | '.' Digits ('E' [+-]? Digits)? + | Digits 'E' [+-]? Digits +; + +fragment Digits: [0-9]+; + +PLSQLVARIABLENAME: ':' [A-Z_] [A-Z_0-9$]*; + +PLSQLIDENTIFIER: ':"' ('\\' . | '""' | ~ ('"' | '\\'))* '"'; +// + +// WHITESPACE (4.1) + +// + +Whitespace: [ \t]+ -> channel (HIDDEN); + +Newline: ('\r' '\n'? | '\n') -> channel (HIDDEN); +// + +// COMMENTS (4.1.5) + +// + +LineComment: '--' ~ [\r\n]* -> channel (HIDDEN); + +BlockComment: + ('/*' ('/'* BlockComment | ~ [/*] | '/'+ ~ [/*] | '*'+ ~ [/*])* '*'* '*/') -> channel (HIDDEN) +; + +UnterminatedBlockComment: + '/*' ( + '/'* BlockComment + | // these characters are not part of special sequences in a block comment + ~ [/*] + | // handle / or * characters which are not part of /* or */ and do not appear at the end of the file + ('/'+ ~ [/*] | '*'+ ~ [/*]) + )* + // Handle the case of / or * characters at the end of the file, or a nested unterminated block comment + ('/'+ | '*'+ | '/'* UnterminatedBlockComment)? + // Optional assertion to make sure this rule is working as intended + { + unterminatedBlockCommentDebugAssert(); + } +; +// + +// META-COMMANDS + +// + +// http://www.postgresql.org/docs/9.3/static/app-psql.html + +MetaCommand: '\\' (~ [\r\n\\"] | '"' ~ [\r\n"]* '"')* ('"' ~ [\r\n"]*)?; + +EndMetaCommand: '\\\\'; +// + +// ERROR + +// + +// Any character which does not match one of the above rules will appear in the token stream as an ErrorCharacter token. + +// This ensures the lexer itself will never encounter a syntax error, so all error handling may be performed by the + +// parser. + +ErrorCharacter: .; + +mode EscapeStringConstantMode; +EscapeStringConstant: EscapeStringText '\'' -> mode (AfterEscapeStringConstantMode); + +UnterminatedEscapeStringConstant: + EscapeStringText + // Handle a final unmatched \ character appearing at the end of the file + '\\'? EOF +; + +fragment EscapeStringText options { + caseInsensitive = false; +}: + ( + '\'\'' + | '\\' ( + // two-digit hex escapes are still valid when treated as single-digit escapes + 'x' [0-9a-fA-F] + | 'u' [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] + | 'U' [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] + | // Any character other than the Unicode escapes can follow a backslash. Some have special meaning, + // but that doesn't affect the syntax. + ~ [xuU] + ) + | ~ ['\\] + )* +; + +InvalidEscapeStringConstant: InvalidEscapeStringText '\'' -> mode (AfterEscapeStringConstantMode); + +InvalidUnterminatedEscapeStringConstant: + InvalidEscapeStringText + // Handle a final unmatched \ character appearing at the end of the file + '\\'? EOF +; + +fragment InvalidEscapeStringText: ('\'\'' | '\\' . | ~ ['\\])*; + +mode AfterEscapeStringConstantMode; +AfterEscapeStringConstantMode_Whitespace: Whitespace -> type (Whitespace), channel (HIDDEN); + +AfterEscapeStringConstantMode_Newline: + Newline -> type (Newline), channel (HIDDEN), mode (AfterEscapeStringConstantWithNewlineMode) +; + +AfterEscapeStringConstantMode_NotContinued: + {} // intentionally empty + -> skip, popMode +; + +mode AfterEscapeStringConstantWithNewlineMode; +AfterEscapeStringConstantWithNewlineMode_Whitespace: + Whitespace -> type (Whitespace), channel (HIDDEN) +; + +AfterEscapeStringConstantWithNewlineMode_Newline: Newline -> type (Newline), channel (HIDDEN); + +AfterEscapeStringConstantWithNewlineMode_Continued: + '\'' -> more, mode (EscapeStringConstantMode) +; + +AfterEscapeStringConstantWithNewlineMode_NotContinued: + {} // intentionally empty + -> skip, popMode +; + +mode DollarQuotedStringMode; +DollarText: + ~ '$'+ + //| '$'([0-9])+ + | // this alternative improves the efficiency of handling $ characters within a dollar-quoted string which are + + // not part of the ending tag. + '$' ~ '$'* +; + +EndDollarStringConstant: ('$' Tag? '$') {isTag()}? {popTag();} -> popMode; diff --git a/incubator/binding-pgsql/src/main/antlr4/io/aklivity/zilla/runtime/binding/pgsql/parser/PostgreSqlParser.g4 b/incubator/binding-pgsql/src/main/antlr4/io/aklivity/zilla/runtime/binding/pgsql/parser/PostgreSqlParser.g4 new file mode 100644 index 0000000000..d2bf3dcbd2 --- /dev/null +++ b/incubator/binding-pgsql/src/main/antlr4/io/aklivity/zilla/runtime/binding/pgsql/parser/PostgreSqlParser.g4 @@ -0,0 +1,5616 @@ +/* + * Copyright 2021-2023 Aklivity Inc + * + * Licensed under the Aklivity Community License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of the + * License at + * + * https://www.aklivity.io/aklivity-community-license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +parser grammar PostgreSqlParser; + +options { + tokenVocab = PostgreSqlLexer; + superClass = PostgreSqlParserBase; +} + +@header { +} + +@members { +} + +root + : stmtblock EOF + ; + +plsqlroot + : pl_function + ; + +stmtblock + : stmtmulti + ; + +stmtmulti + : (stmt SEMI?)* + ; + +stmt + : altereventtrigstmt + | altercollationstmt + | alterdatabasestmt + | alterdatabasesetstmt + | alterdefaultprivilegesstmt + | alterdomainstmt + | alterenumstmt + | alterextensionstmt + | alterextensioncontentsstmt + | alterfdwstmt + | alterforeignserverstmt + | alterfunctionstmt + | altergroupstmt + | alterobjectdependsstmt + | alterobjectschemastmt + | alterownerstmt + | alteroperatorstmt + | altertypestmt + | alterpolicystmt + | alterseqstmt + | altersystemstmt + | altertablestmt + | altertblspcstmt + | altercompositetypestmt + | alterpublicationstmt + | alterrolesetstmt + | alterrolestmt + | altersubscriptionstmt + | alterstatsstmt + | altertsconfigurationstmt + | altertsdictionarystmt + | alterusermappingstmt + | analyzestmt + | callstmt + | checkpointstmt + | closeportalstmt + | clusterstmt + | commentstmt + | constraintssetstmt + | copystmt + | createamstmt + | createasstmt + | createassertionstmt + | createcaststmt + | createconversionstmt + | createdomainstmt + | createextensionstmt + | createfdwstmt + | createforeignserverstmt + | createforeigntablestmt + | createfunctionstmt + | creategroupstmt + | creatematviewstmt + | createopclassstmt + | createopfamilystmt + | createpublicationstmt + | alteropfamilystmt + | createpolicystmt + | createplangstmt + | createschemastmt + | createseqstmt + | createstmt + | createstreamstmt + | createsubscriptionstmt + | createstatsstmt + | createtablespacestmt + | createtransformstmt + | createtrigstmt + | createeventtrigstmt + | createrolestmt + | createuserstmt + | createusermappingstmt + | createdbstmt + | deallocatestmt + | declarecursorstmt + | definestmt + | deletestmt + | discardstmt + | dostmt + | dropcaststmt + | dropopclassstmt + | dropopfamilystmt + | dropownedstmt + | dropstmt + | dropsubscriptionstmt + | droptablespacestmt + | droptransformstmt + | droprolestmt + | dropusermappingstmt + | dropdbstmt + | executestmt + | explainstmt + | fetchstmt + | grantstmt + | grantrolestmt + | importforeignschemastmt + | indexstmt + | insertstmt + | mergestmt + | listenstmt + | refreshmatviewstmt + | loadstmt + | lockstmt + | notifystmt + | preparestmt + | reassignownedstmt + | reindexstmt + | removeaggrstmt + | removefuncstmt + | removeoperstmt + | renamestmt + | revokestmt + | revokerolestmt + | rulestmt + | seclabelstmt + | selectstmt + | transactionstmt + | truncatestmt + | unlistenstmt + | updatestmt + | vacuumstmt + | variableresetstmt + | variablesetstmt + | variableshowstmt + | viewstmt + | plsqlconsolecommand + ; + +plsqlconsolecommand + : MetaCommand EndMetaCommand? + ; + +callstmt + : CALL func_application + ; + +createrolestmt + : CREATE ROLE roleid opt_with optrolelist + ; + +opt_with + : WITH + //| WITH_LA + | + ; + +optrolelist + : createoptroleelem* + ; + +alteroptrolelist + : alteroptroleelem* + ; + +alteroptroleelem + : PASSWORD (sconst | NULL_P) + | (ENCRYPTED | UNENCRYPTED) PASSWORD sconst + | INHERIT + | CONNECTION LIMIT signediconst + | VALID UNTIL sconst + | USER role_list + | identifier + ; + +createoptroleelem + : alteroptroleelem + | SYSID iconst + | ADMIN role_list + | ROLE role_list + | IN_P (ROLE | GROUP_P) role_list + ; + +createuserstmt + : CREATE USER roleid opt_with optrolelist + ; + +alterrolestmt + : ALTER (ROLE | USER) rolespec opt_with alteroptrolelist + ; + +opt_in_database + : + | IN_P DATABASE name + ; + +alterrolesetstmt + : ALTER (ROLE | USER) ALL? rolespec opt_in_database setresetclause + ; + +droprolestmt + : DROP (ROLE | USER | GROUP_P) (IF_P EXISTS)? role_list + ; + +creategroupstmt + : CREATE GROUP_P roleid opt_with optrolelist + ; + +altergroupstmt + : ALTER GROUP_P rolespec add_drop USER role_list + ; + +add_drop + : ADD_P + | DROP + ; + +createschemastmt + : CREATE SCHEMA (IF_P NOT EXISTS)? (optschemaname AUTHORIZATION rolespec | colid) optschemaeltlist + ; + +optschemaname + : colid + | + ; + +optschemaeltlist + : schema_stmt* + ; + +schema_stmt + : createstmt + | indexstmt + | createseqstmt + | createtrigstmt + | grantstmt + | viewstmt + ; + +variablesetstmt + : SET (LOCAL | SESSION)? set_rest + ; + +set_rest + : TRANSACTION transaction_mode_list + | SESSION CHARACTERISTICS AS TRANSACTION transaction_mode_list + | set_rest_more + ; + +generic_set + : var_name (TO | EQUAL) var_list + ; + +set_rest_more + : generic_set + | var_name FROM CURRENT_P + | TIME ZONE zone_value + | CATALOG sconst + | SCHEMA sconst + | NAMES opt_encoding + | ROLE nonreservedword_or_sconst + | SESSION AUTHORIZATION nonreservedword_or_sconst + | XML_P OPTION document_or_content + | TRANSACTION SNAPSHOT sconst + ; + +var_name + : colid (DOT colid)* + ; + +var_list + : var_value (COMMA var_value)* + ; + +var_value + : opt_boolean_or_string + | numericonly + ; + +iso_level + : READ (UNCOMMITTED | COMMITTED) + | REPEATABLE READ + | SERIALIZABLE + ; + +opt_boolean_or_string + : TRUE_P + | FALSE_P + | ON + | nonreservedword_or_sconst + ; + +zone_value + : sconst + | identifier + | constinterval sconst opt_interval + | constinterval OPEN_PAREN iconst CLOSE_PAREN sconst + | numericonly + | DEFAULT + | LOCAL + ; + +opt_encoding + : sconst + | DEFAULT + | + ; + +nonreservedword_or_sconst + : nonreservedword + | sconst + ; + +variableresetstmt + : RESET reset_rest + ; + +reset_rest + : generic_reset + | TIME ZONE + | TRANSACTION ISOLATION LEVEL + | SESSION AUTHORIZATION + ; + +generic_reset + : var_name + | ALL + ; + +setresetclause + : SET set_rest + | variableresetstmt + ; + +functionsetresetclause + : SET set_rest_more + | variableresetstmt + ; + +variableshowstmt + : SHOW (var_name | TIME ZONE | TRANSACTION ISOLATION LEVEL | SESSION AUTHORIZATION | ALL) + ; + +constraintssetstmt + : SET CONSTRAINTS constraints_set_list constraints_set_mode + ; + +constraints_set_list + : ALL + | qualified_name_list + ; + +constraints_set_mode + : DEFERRED + | IMMEDIATE + ; + +checkpointstmt + : CHECKPOINT + ; + +discardstmt + : DISCARD (ALL | TEMP | TEMPORARY | PLANS | SEQUENCES) + ; + +altertablestmt + : ALTER TABLE (IF_P EXISTS)? relation_expr (alter_table_cmds | partition_cmd) + | ALTER TABLE ALL IN_P TABLESPACE name (OWNED BY role_list)? SET TABLESPACE name opt_nowait + | ALTER INDEX (IF_P EXISTS)? qualified_name (alter_table_cmds | index_partition_cmd) + | ALTER INDEX ALL IN_P TABLESPACE name (OWNED BY role_list)? SET TABLESPACE name opt_nowait + | ALTER SEQUENCE (IF_P EXISTS)? qualified_name alter_table_cmds + | ALTER VIEW (IF_P EXISTS)? qualified_name alter_table_cmds + | ALTER MATERIALIZED VIEW (IF_P EXISTS)? qualified_name alter_table_cmds + | ALTER MATERIALIZED VIEW ALL IN_P TABLESPACE name (OWNED BY role_list)? SET TABLESPACE name opt_nowait + | ALTER FOREIGN TABLE (IF_P EXISTS)? relation_expr alter_table_cmds + ; + +alter_table_cmds + : alter_table_cmd (COMMA alter_table_cmd)* + ; + +partition_cmd + : ATTACH PARTITION qualified_name partitionboundspec + | DETACH PARTITION qualified_name + ; + +index_partition_cmd + : ATTACH PARTITION qualified_name + ; + +alter_table_cmd + : ADD_P columnDef + | ADD_P IF_P NOT EXISTS columnDef + | ADD_P COLUMN columnDef + | ADD_P COLUMN IF_P NOT EXISTS columnDef + | ALTER opt_column colid alter_column_default + | ALTER opt_column colid DROP NOT NULL_P + | ALTER opt_column colid SET NOT NULL_P + | ALTER opt_column colid DROP EXPRESSION + | ALTER opt_column colid DROP EXPRESSION IF_P EXISTS + | ALTER opt_column colid SET STATISTICS signediconst + | ALTER opt_column iconst SET STATISTICS signediconst + | ALTER opt_column colid SET reloptions + | ALTER opt_column colid RESET reloptions + | ALTER opt_column colid SET STORAGE colid + | ALTER opt_column colid ADD_P GENERATED generated_when AS IDENTITY_P optparenthesizedseqoptlist + | ALTER opt_column colid alter_identity_column_option_list + | ALTER opt_column colid DROP IDENTITY_P + | ALTER opt_column colid DROP IDENTITY_P IF_P EXISTS + | DROP opt_column IF_P EXISTS colid opt_drop_behavior + | DROP opt_column colid opt_drop_behavior + | ALTER opt_column colid opt_set_data TYPE_P typename opt_collate_clause alter_using + | ALTER opt_column colid alter_generic_options + | ADD_P tableconstraint + | ALTER CONSTRAINT name constraintattributespec + | VALIDATE CONSTRAINT name + | DROP CONSTRAINT IF_P EXISTS name opt_drop_behavior + | DROP CONSTRAINT name opt_drop_behavior + | SET WITHOUT OIDS + | CLUSTER ON name + | SET WITHOUT CLUSTER + | SET LOGGED + | SET UNLOGGED + | ENABLE_P TRIGGER name + | ENABLE_P ALWAYS TRIGGER name + | ENABLE_P REPLICA TRIGGER name + | ENABLE_P TRIGGER ALL + | ENABLE_P TRIGGER USER + | DISABLE_P TRIGGER name + | DISABLE_P TRIGGER ALL + | DISABLE_P TRIGGER USER + | ENABLE_P RULE name + | ENABLE_P ALWAYS RULE name + | ENABLE_P REPLICA RULE name + | DISABLE_P RULE name + | INHERIT qualified_name + | NO INHERIT qualified_name + | OF any_name + | NOT OF + | OWNER TO rolespec + | SET TABLESPACE name + | SET reloptions + | RESET reloptions + | REPLICA IDENTITY_P replica_identity + | ENABLE_P ROW LEVEL SECURITY + | DISABLE_P ROW LEVEL SECURITY + | FORCE ROW LEVEL SECURITY + | NO FORCE ROW LEVEL SECURITY + | alter_generic_options + ; + +alter_column_default + : SET DEFAULT a_expr + | DROP DEFAULT + ; + +opt_drop_behavior + : CASCADE + | RESTRICT + | + ; + +opt_collate_clause + : COLLATE any_name + | + ; + +alter_using + : USING a_expr + | + ; + +replica_identity + : NOTHING + | FULL + | DEFAULT + | USING INDEX name + ; + +reloptions + : OPEN_PAREN reloption_list CLOSE_PAREN + ; + +opt_reloptions + : WITH reloptions + | + ; + +reloption_list + : reloption_elem (COMMA reloption_elem)* + ; + +reloption_elem + : collabel (EQUAL def_arg | DOT collabel (EQUAL def_arg)?)? + ; + +alter_identity_column_option_list + : alter_identity_column_option+ + ; + +alter_identity_column_option + : RESTART (opt_with numericonly)? + | SET (seqoptelem | GENERATED generated_when) + ; + +partitionboundspec + : FOR VALUES WITH OPEN_PAREN hash_partbound CLOSE_PAREN + | FOR VALUES IN_P OPEN_PAREN expr_list CLOSE_PAREN + | FOR VALUES FROM OPEN_PAREN expr_list CLOSE_PAREN TO OPEN_PAREN expr_list CLOSE_PAREN + | DEFAULT + ; + +hash_partbound_elem + : nonreservedword iconst + ; + +hash_partbound + : hash_partbound_elem (COMMA hash_partbound_elem)* + ; + +altercompositetypestmt + : ALTER TYPE_P any_name alter_type_cmds + ; + +alter_type_cmds + : alter_type_cmd (COMMA alter_type_cmd)* + ; + +alter_type_cmd + : ADD_P ATTRIBUTE tablefuncelement opt_drop_behavior + | DROP ATTRIBUTE (IF_P EXISTS)? colid opt_drop_behavior + | ALTER ATTRIBUTE colid opt_set_data TYPE_P typename opt_collate_clause opt_drop_behavior + ; + +closeportalstmt + : CLOSE (cursor_name | ALL) + ; + +copystmt + : COPY opt_binary qualified_name opt_column_list copy_from opt_program copy_file_name copy_delimiter opt_with copy_options where_clause + | COPY OPEN_PAREN preparablestmt CLOSE_PAREN TO opt_program copy_file_name opt_with copy_options + ; + +copy_from + : FROM + | TO + ; + +opt_program + : PROGRAM + | + ; + +copy_file_name + : sconst + | STDIN + | STDOUT + ; + +copy_options + : copy_opt_list + | OPEN_PAREN copy_generic_opt_list CLOSE_PAREN + ; + +copy_opt_list + : copy_opt_item* + ; + +copy_opt_item + : BINARY + | FREEZE + | DELIMITER opt_as sconst + | NULL_P opt_as sconst + | CSV + | HEADER_P + | QUOTE opt_as sconst + | ESCAPE opt_as sconst + | FORCE QUOTE columnlist + | FORCE QUOTE STAR + | FORCE NOT NULL_P columnlist + | FORCE NULL_P columnlist + | ENCODING sconst + ; + +opt_binary + : BINARY + | + ; + +copy_delimiter + : opt_using DELIMITERS sconst + | + ; + +opt_using + : USING + | + ; + +copy_generic_opt_list + : copy_generic_opt_elem (COMMA copy_generic_opt_elem)* + ; + +copy_generic_opt_elem + : collabel copy_generic_opt_arg + ; + +copy_generic_opt_arg + : opt_boolean_or_string + | numericonly + | STAR + | OPEN_PAREN copy_generic_opt_arg_list CLOSE_PAREN + | + ; + +copy_generic_opt_arg_list + : copy_generic_opt_arg_list_item (COMMA copy_generic_opt_arg_list_item)* + ; + +copy_generic_opt_arg_list_item + : opt_boolean_or_string + ; + +createstreamstmt + : CREATE STREAM (IF_P NOT EXISTS)? stream_name OPEN_PAREN stream_columns CLOSE_PAREN opt_with_stream + ; + +stream_name + : qualified_name + ; + +stream_columns + : stream_column (COMMA stream_column)* + ; + +stream_column + : colid typename + ; + +opt_with_stream + : WITH reloptions + | + ; + +createstmt + : CREATE opttemp opttable_type (IF_P NOT EXISTS)? qualified_name ( + OPEN_PAREN opttableelementlist CLOSE_PAREN optinherit optpartitionspec table_access_method_clause optwith oncommitoption opttablespace + | OF any_name opttypedtableelementlist optpartitionspec table_access_method_clause optwith oncommitoption opttablespace + | PARTITION OF qualified_name opttypedtableelementlist partitionboundspec optpartitionspec table_access_method_clause optwith oncommitoption + opttablespace + ) + ; + +opttable_type + : TABLE + | TOPIC + ; + +opttemp + : TEMPORARY + | TEMP + | LOCAL (TEMPORARY | TEMP) + | GLOBAL (TEMPORARY | TEMP) + | UNLOGGED + | + ; + +opttableelementlist + : tableelementlist + | + ; + +opttypedtableelementlist + : OPEN_PAREN typedtableelementlist CLOSE_PAREN + | + ; + +tableelementlist + : tableelement (COMMA tableelement)* + ; + +typedtableelementlist + : typedtableelement (COMMA typedtableelement)* + ; + +tableelement + : tableconstraint + | tablelikeclause + | columnDef + ; + +typedtableelement + : columnOptions + | tableconstraint + ; + +columnDef + : colid typename create_generic_options colquallist + ; + +columnOptions + : colid (WITH OPTIONS)? colquallist + ; + +colquallist + : colconstraint* + ; + +colconstraint + : CONSTRAINT name colconstraintelem + | colconstraintelem + | constraintattr + | COLLATE any_name + ; + +colconstraintelem + : NOT NULL_P + | NULL_P + | UNIQUE opt_definition optconstablespace + | PRIMARY KEY opt_definition optconstablespace + | CHECK OPEN_PAREN a_expr CLOSE_PAREN opt_no_inherit + | DEFAULT b_expr + | GENERATED generated_when AS ( + IDENTITY_P optparenthesizedseqoptlist + | OPEN_PAREN a_expr CLOSE_PAREN STORED + ) + | REFERENCES qualified_name opt_column_list key_match key_actions + ; + +generated_when + : ALWAYS + | BY DEFAULT + ; + +constraintattr + : DEFERRABLE + | NOT DEFERRABLE + | INITIALLY (DEFERRED | IMMEDIATE) + ; + +tablelikeclause + : LIKE qualified_name tablelikeoptionlist + ; + +tablelikeoptionlist + : ((INCLUDING | EXCLUDING) tablelikeoption)* + ; + +tablelikeoption + : COMMENTS + | CONSTRAINTS + | DEFAULTS + | IDENTITY_P + | GENERATED + | INDEXES + | STATISTICS + | STORAGE + | ALL + ; + +tableconstraint + : CONSTRAINT name constraintelem + | constraintelem + ; + +constraintelem + : CHECK OPEN_PAREN a_expr CLOSE_PAREN constraintattributespec + | UNIQUE ( + OPEN_PAREN columnlist CLOSE_PAREN opt_c_include opt_definition optconstablespace constraintattributespec + | existingindex constraintattributespec + ) + | PRIMARY KEY ( + OPEN_PAREN columnlist CLOSE_PAREN opt_c_include opt_definition optconstablespace constraintattributespec + | existingindex constraintattributespec + ) + | EXCLUDE access_method_clause OPEN_PAREN exclusionconstraintlist CLOSE_PAREN opt_c_include opt_definition optconstablespace exclusionwhereclause + constraintattributespec + | FOREIGN KEY OPEN_PAREN columnlist CLOSE_PAREN REFERENCES qualified_name opt_column_list key_match key_actions constraintattributespec + ; + +opt_no_inherit + : NO INHERIT + | + ; + +opt_column_list + : OPEN_PAREN columnlist CLOSE_PAREN + | + ; + +columnlist + : columnElem (COMMA columnElem)* + ; + +columnElem + : colid + ; + +opt_c_include + : INCLUDE OPEN_PAREN columnlist CLOSE_PAREN + | + ; + +key_match + : MATCH (FULL | PARTIAL | SIMPLE) + | + ; + +exclusionconstraintlist + : exclusionconstraintelem (COMMA exclusionconstraintelem)* + ; + +exclusionconstraintelem + : index_elem WITH (any_operator | OPERATOR OPEN_PAREN any_operator CLOSE_PAREN) + ; + +exclusionwhereclause + : WHERE OPEN_PAREN a_expr CLOSE_PAREN + | + ; + +key_actions + : key_update + | key_delete + | key_update key_delete + | key_delete key_update + | + ; + +key_update + : ON UPDATE key_action + ; + +key_delete + : ON DELETE_P key_action + ; + +key_action + : NO ACTION + | RESTRICT + | CASCADE + | SET (NULL_P | DEFAULT) + ; + +optinherit + : INHERITS OPEN_PAREN qualified_name_list CLOSE_PAREN + | + ; + +optpartitionspec + : partitionspec + | + ; + +partitionspec + : PARTITION BY colid OPEN_PAREN part_params CLOSE_PAREN + ; + +part_params + : part_elem (COMMA part_elem)* + ; + +part_elem + : colid opt_collate opt_class + | func_expr_windowless opt_collate opt_class + | OPEN_PAREN a_expr CLOSE_PAREN opt_collate opt_class + ; + +table_access_method_clause + : USING name + | + ; + +optwith + : WITH reloptions + | WITHOUT OIDS + | + ; + +oncommitoption + : ON COMMIT (DROP | DELETE_P ROWS | PRESERVE ROWS) + | + ; + +opttablespace + : TABLESPACE name + | + ; + +optconstablespace + : USING INDEX TABLESPACE name + | + ; + +existingindex + : USING INDEX name + ; + +createstatsstmt + : CREATE STATISTICS (IF_P NOT EXISTS)? any_name opt_name_list ON expr_list FROM from_list + ; + +alterstatsstmt + : ALTER STATISTICS (IF_P EXISTS)? any_name SET STATISTICS signediconst + ; + +createasstmt + : CREATE opttemp TABLE (IF_P NOT EXISTS)? create_as_target AS selectstmt opt_with_data + ; + +create_as_target + : qualified_name opt_column_list table_access_method_clause optwith oncommitoption opttablespace + ; + +opt_with_data + : WITH (DATA_P | NO DATA_P) + | + ; + +creatematviewstmt + : CREATE optnolog MATERIALIZED VIEW (IF_P NOT EXISTS)? create_mv_target AS selectstmt opt_with_data + ; + +create_mv_target + : qualified_name opt_column_list table_access_method_clause opt_reloptions opttablespace + ; + +optnolog + : UNLOGGED + | + ; + +refreshmatviewstmt + : REFRESH MATERIALIZED VIEW opt_concurrently qualified_name opt_with_data + ; + +createseqstmt + : CREATE opttemp SEQUENCE (IF_P NOT EXISTS)? qualified_name optseqoptlist + ; + +alterseqstmt + : ALTER SEQUENCE (IF_P EXISTS)? qualified_name seqoptlist + ; + +optseqoptlist + : seqoptlist + | + ; + +optparenthesizedseqoptlist + : OPEN_PAREN seqoptlist CLOSE_PAREN + | + ; + +seqoptlist + : seqoptelem+ + ; + +seqoptelem + : AS simpletypename + | CACHE numericonly + | CYCLE + | INCREMENT opt_by numericonly + | MAXVALUE numericonly + | MINVALUE numericonly + | NO (MAXVALUE | MINVALUE | CYCLE) + | OWNED BY any_name + | SEQUENCE NAME_P any_name + | START opt_with numericonly + | RESTART opt_with numericonly? + ; + +opt_by + : BY + | + ; + +numericonly + : fconst + | PLUS fconst + | MINUS fconst + | signediconst + ; + +numericonly_list + : numericonly (COMMA numericonly)* + ; + +createplangstmt + : CREATE opt_or_replace opt_trusted opt_procedural LANGUAGE name ( + HANDLER handler_name opt_inline_handler opt_validator + )? + ; + +opt_trusted + : TRUSTED + | + ; + +handler_name + : name attrs? + ; + +opt_inline_handler + : INLINE_P handler_name + | + ; + +validator_clause + : VALIDATOR handler_name + | NO VALIDATOR + ; + +opt_validator + : validator_clause + | + ; + +opt_procedural + : PROCEDURAL + | + ; + +createtablespacestmt + : CREATE TABLESPACE name opttablespaceowner LOCATION sconst opt_reloptions + ; + +opttablespaceowner + : OWNER rolespec + | + ; + +droptablespacestmt + : DROP TABLESPACE (IF_P EXISTS)? name + ; + +createextensionstmt + : CREATE EXTENSION (IF_P NOT EXISTS)? name opt_with create_extension_opt_list + ; + +create_extension_opt_list + : create_extension_opt_item* + ; + +create_extension_opt_item + : SCHEMA name + | VERSION_P nonreservedword_or_sconst + | FROM nonreservedword_or_sconst + | CASCADE + ; + +alterextensionstmt + : ALTER EXTENSION name UPDATE alter_extension_opt_list + ; + +alter_extension_opt_list + : alter_extension_opt_item* + ; + +alter_extension_opt_item + : TO nonreservedword_or_sconst + ; + +alterextensioncontentsstmt + : ALTER EXTENSION name add_drop object_type_name name + | ALTER EXTENSION name add_drop object_type_any_name any_name + | ALTER EXTENSION name add_drop AGGREGATE aggregate_with_argtypes + | ALTER EXTENSION name add_drop CAST OPEN_PAREN typename AS typename CLOSE_PAREN + | ALTER EXTENSION name add_drop DOMAIN_P typename + | ALTER EXTENSION name add_drop FUNCTION function_with_argtypes + | ALTER EXTENSION name add_drop OPERATOR operator_with_argtypes + | ALTER EXTENSION name add_drop OPERATOR CLASS any_name USING name + | ALTER EXTENSION name add_drop OPERATOR FAMILY any_name USING name + | ALTER EXTENSION name add_drop PROCEDURE function_with_argtypes + | ALTER EXTENSION name add_drop ROUTINE function_with_argtypes + | ALTER EXTENSION name add_drop TRANSFORM FOR typename LANGUAGE name + | ALTER EXTENSION name add_drop TYPE_P typename + ; + +createfdwstmt + : CREATE FOREIGN DATA_P WRAPPER name opt_fdw_options create_generic_options + ; + +fdw_option + : HANDLER handler_name + | NO HANDLER + | VALIDATOR handler_name + | NO VALIDATOR + ; + +fdw_options + : fdw_option+ + ; + +opt_fdw_options + : fdw_options + | + ; + +alterfdwstmt + : ALTER FOREIGN DATA_P WRAPPER name opt_fdw_options alter_generic_options + | ALTER FOREIGN DATA_P WRAPPER name fdw_options + ; + +create_generic_options + : OPTIONS OPEN_PAREN generic_option_list CLOSE_PAREN + | + ; + +generic_option_list + : generic_option_elem (COMMA generic_option_elem)* + ; + +alter_generic_options + : OPTIONS OPEN_PAREN alter_generic_option_list CLOSE_PAREN + ; + +alter_generic_option_list + : alter_generic_option_elem (COMMA alter_generic_option_elem)* + ; + +alter_generic_option_elem + : generic_option_elem + | SET generic_option_elem + | ADD_P generic_option_elem + | DROP generic_option_name + ; + +generic_option_elem + : generic_option_name generic_option_arg + ; + +generic_option_name + : collabel + ; + +generic_option_arg + : sconst + ; + +createforeignserverstmt + : CREATE SERVER name opt_type opt_foreign_server_version FOREIGN DATA_P WRAPPER name create_generic_options + | CREATE SERVER IF_P NOT EXISTS name opt_type opt_foreign_server_version FOREIGN DATA_P WRAPPER name create_generic_options + ; + +opt_type + : TYPE_P sconst + | + ; + +foreign_server_version + : VERSION_P (sconst | NULL_P) + ; + +opt_foreign_server_version + : foreign_server_version + | + ; + +alterforeignserverstmt + : ALTER SERVER name (alter_generic_options | foreign_server_version alter_generic_options?) + ; + +createforeigntablestmt + : CREATE FOREIGN TABLE qualified_name OPEN_PAREN opttableelementlist CLOSE_PAREN optinherit SERVER name create_generic_options + | CREATE FOREIGN TABLE IF_P NOT EXISTS qualified_name OPEN_PAREN opttableelementlist CLOSE_PAREN optinherit SERVER name create_generic_options + | CREATE FOREIGN TABLE qualified_name PARTITION OF qualified_name opttypedtableelementlist partitionboundspec SERVER name create_generic_options + | CREATE FOREIGN TABLE IF_P NOT EXISTS qualified_name PARTITION OF qualified_name opttypedtableelementlist partitionboundspec SERVER name + create_generic_options + ; + +importforeignschemastmt + : IMPORT_P FOREIGN SCHEMA name import_qualification FROM SERVER name INTO name create_generic_options + ; + +import_qualification_type + : LIMIT TO + | EXCEPT + ; + +import_qualification + : import_qualification_type OPEN_PAREN relation_expr_list CLOSE_PAREN + | + ; + +createusermappingstmt + : CREATE USER MAPPING FOR auth_ident SERVER name create_generic_options + | CREATE USER MAPPING IF_P NOT EXISTS FOR auth_ident SERVER name create_generic_options + ; + +auth_ident + : rolespec + | USER + ; + +dropusermappingstmt + : DROP USER MAPPING FOR auth_ident SERVER name + | DROP USER MAPPING IF_P EXISTS FOR auth_ident SERVER name + ; + +alterusermappingstmt + : ALTER USER MAPPING FOR auth_ident SERVER name alter_generic_options + ; + +createpolicystmt + : CREATE POLICY name ON qualified_name rowsecuritydefaultpermissive rowsecuritydefaultforcmd rowsecuritydefaulttorole rowsecurityoptionalexpr + rowsecurityoptionalwithcheck + ; + +alterpolicystmt + : ALTER POLICY name ON qualified_name rowsecurityoptionaltorole rowsecurityoptionalexpr rowsecurityoptionalwithcheck + ; + +rowsecurityoptionalexpr + : USING OPEN_PAREN a_expr CLOSE_PAREN + | + ; + +rowsecurityoptionalwithcheck + : WITH CHECK OPEN_PAREN a_expr CLOSE_PAREN + | + ; + +rowsecuritydefaulttorole + : TO role_list + | + ; + +rowsecurityoptionaltorole + : TO role_list + | + ; + +rowsecuritydefaultpermissive + : AS identifier + | + ; + +rowsecuritydefaultforcmd + : FOR row_security_cmd + | + ; + +row_security_cmd + : ALL + | SELECT + | INSERT + | UPDATE + | DELETE_P + ; + +createamstmt + : CREATE ACCESS METHOD name TYPE_P am_type HANDLER handler_name + ; + +am_type + : INDEX + | TABLE + ; + +createtrigstmt + : CREATE TRIGGER name triggeractiontime triggerevents ON qualified_name triggerreferencing triggerforspec triggerwhen EXECUTE + function_or_procedure func_name OPEN_PAREN triggerfuncargs CLOSE_PAREN + | CREATE CONSTRAINT TRIGGER name AFTER triggerevents ON qualified_name optconstrfromtable constraintattributespec FOR EACH ROW triggerwhen EXECUTE + function_or_procedure func_name OPEN_PAREN triggerfuncargs CLOSE_PAREN + ; + +triggeractiontime + : BEFORE + | AFTER + | INSTEAD OF + ; + +triggerevents + : triggeroneevent (OR triggeroneevent)* + ; + +triggeroneevent + : INSERT + | DELETE_P + | UPDATE + | UPDATE OF columnlist + | TRUNCATE + ; + +triggerreferencing + : REFERENCING triggertransitions + | + ; + +triggertransitions + : triggertransition+ + ; + +triggertransition + : transitionoldornew transitionrowortable opt_as transitionrelname + ; + +transitionoldornew + : NEW + | OLD + ; + +transitionrowortable + : TABLE + | ROW + ; + +transitionrelname + : colid + ; + +triggerforspec + : FOR triggerforopteach triggerfortype + | + ; + +triggerforopteach + : EACH + | + ; + +triggerfortype + : ROW + | STATEMENT + ; + +triggerwhen + : WHEN OPEN_PAREN a_expr CLOSE_PAREN + | + ; + +function_or_procedure + : FUNCTION + | PROCEDURE + ; + +triggerfuncargs + : (triggerfuncarg |) (COMMA triggerfuncarg)* + ; + +triggerfuncarg + : iconst + | fconst + | sconst + | collabel + ; + +optconstrfromtable + : FROM qualified_name + | + ; + +constraintattributespec + : constraintattributeElem* + ; + +constraintattributeElem + : NOT DEFERRABLE + | DEFERRABLE + | INITIALLY IMMEDIATE + | INITIALLY DEFERRED + | NOT VALID + | NO INHERIT + ; + +createeventtrigstmt + : CREATE EVENT TRIGGER name ON collabel EXECUTE function_or_procedure func_name OPEN_PAREN CLOSE_PAREN + | CREATE EVENT TRIGGER name ON collabel WHEN event_trigger_when_list EXECUTE function_or_procedure func_name OPEN_PAREN CLOSE_PAREN + ; + +event_trigger_when_list + : event_trigger_when_item (AND event_trigger_when_item)* + ; + +event_trigger_when_item + : colid IN_P OPEN_PAREN event_trigger_value_list CLOSE_PAREN + ; + +event_trigger_value_list + : sconst (COMMA sconst)* + ; + +altereventtrigstmt + : ALTER EVENT TRIGGER name enable_trigger + ; + +enable_trigger + : ENABLE_P + | ENABLE_P REPLICA + | ENABLE_P ALWAYS + | DISABLE_P + ; + +createassertionstmt + : CREATE ASSERTION any_name CHECK OPEN_PAREN a_expr CLOSE_PAREN constraintattributespec + ; + +definestmt + : CREATE opt_or_replace AGGREGATE func_name aggr_args definition + | CREATE opt_or_replace AGGREGATE func_name old_aggr_definition + | CREATE OPERATOR any_operator definition + | CREATE TYPE_P any_name definition + | CREATE TYPE_P any_name + | CREATE TYPE_P any_name AS OPEN_PAREN opttablefuncelementlist CLOSE_PAREN + | CREATE TYPE_P any_name AS ENUM_P OPEN_PAREN opt_enum_val_list CLOSE_PAREN + | CREATE TYPE_P any_name AS RANGE definition + | CREATE TEXT_P SEARCH PARSER any_name definition + | CREATE TEXT_P SEARCH DICTIONARY any_name definition + | CREATE TEXT_P SEARCH TEMPLATE any_name definition + | CREATE TEXT_P SEARCH CONFIGURATION any_name definition + | CREATE COLLATION any_name definition + | CREATE COLLATION IF_P NOT EXISTS any_name definition + | CREATE COLLATION any_name FROM any_name + | CREATE COLLATION IF_P NOT EXISTS any_name FROM any_name + ; + +definition + : OPEN_PAREN def_list CLOSE_PAREN + ; + +def_list + : def_elem (COMMA def_elem)* + ; + +def_elem + : collabel (EQUAL def_arg)? + ; + +def_arg + : func_type + | reserved_keyword + | qual_all_op + | numericonly + | sconst + | NONE + ; + +old_aggr_definition + : OPEN_PAREN old_aggr_list CLOSE_PAREN + ; + +old_aggr_list + : old_aggr_elem (COMMA old_aggr_elem)* + ; + +old_aggr_elem + : identifier EQUAL def_arg + ; + +opt_enum_val_list + : enum_val_list + | + ; + +enum_val_list + : sconst (COMMA sconst)* + ; + +alterenumstmt + : ALTER TYPE_P any_name ADD_P VALUE_P opt_if_not_exists sconst + | ALTER TYPE_P any_name ADD_P VALUE_P opt_if_not_exists sconst BEFORE sconst + | ALTER TYPE_P any_name ADD_P VALUE_P opt_if_not_exists sconst AFTER sconst + | ALTER TYPE_P any_name RENAME VALUE_P sconst TO sconst + ; + +opt_if_not_exists + : IF_P NOT EXISTS + | + ; + +createopclassstmt + : CREATE OPERATOR CLASS any_name opt_default FOR TYPE_P typename USING name opt_opfamily AS opclass_item_list + ; + +opclass_item_list + : opclass_item (COMMA opclass_item)* + ; + +opclass_item + : OPERATOR iconst any_operator opclass_purpose opt_recheck + | OPERATOR iconst operator_with_argtypes opclass_purpose opt_recheck + | FUNCTION iconst function_with_argtypes + | FUNCTION iconst OPEN_PAREN type_list CLOSE_PAREN function_with_argtypes + | STORAGE typename + ; + +opt_default + : DEFAULT + | + ; + +opt_opfamily + : FAMILY any_name + | + ; + +opclass_purpose + : FOR SEARCH + | FOR ORDER BY any_name + | + ; + +opt_recheck + : RECHECK + | + ; + +createopfamilystmt + : CREATE OPERATOR FAMILY any_name USING name + ; + +alteropfamilystmt + : ALTER OPERATOR FAMILY any_name USING name ADD_P opclass_item_list + | ALTER OPERATOR FAMILY any_name USING name DROP opclass_drop_list + ; + +opclass_drop_list + : opclass_drop (COMMA opclass_drop)* + ; + +opclass_drop + : OPERATOR iconst OPEN_PAREN type_list CLOSE_PAREN + | FUNCTION iconst OPEN_PAREN type_list CLOSE_PAREN + ; + +dropopclassstmt + : DROP OPERATOR CLASS any_name USING name opt_drop_behavior + | DROP OPERATOR CLASS IF_P EXISTS any_name USING name opt_drop_behavior + ; + +dropopfamilystmt + : DROP OPERATOR FAMILY any_name USING name opt_drop_behavior + | DROP OPERATOR FAMILY IF_P EXISTS any_name USING name opt_drop_behavior + ; + +dropownedstmt + : DROP OWNED BY role_list opt_drop_behavior + ; + +reassignownedstmt + : REASSIGN OWNED BY role_list TO rolespec + ; + +dropstmt + : DROP object_type_any_name IF_P EXISTS any_name_list opt_drop_behavior + | DROP object_type_any_name any_name_list opt_drop_behavior + | DROP drop_type_name IF_P EXISTS name_list opt_drop_behavior + | DROP drop_type_name name_list opt_drop_behavior + | DROP object_type_name_on_any_name name ON any_name opt_drop_behavior + | DROP object_type_name_on_any_name IF_P EXISTS name ON any_name opt_drop_behavior + | DROP TYPE_P type_name_list opt_drop_behavior + | DROP TYPE_P IF_P EXISTS type_name_list opt_drop_behavior + | DROP DOMAIN_P type_name_list opt_drop_behavior + | DROP DOMAIN_P IF_P EXISTS type_name_list opt_drop_behavior + | DROP INDEX CONCURRENTLY any_name_list opt_drop_behavior + | DROP INDEX CONCURRENTLY IF_P EXISTS any_name_list opt_drop_behavior + ; + +object_type_any_name + : TABLE + | SEQUENCE + | VIEW + | MATERIALIZED VIEW + | TOPIC + | STREAM + | INDEX + | FOREIGN TABLE + | COLLATION + | CONVERSION_P + | STATISTICS + | TEXT_P SEARCH PARSER + | TEXT_P SEARCH DICTIONARY + | TEXT_P SEARCH TEMPLATE + | TEXT_P SEARCH CONFIGURATION + ; + +object_type_name + : drop_type_name + | DATABASE + | ROLE + | SUBSCRIPTION + | TABLESPACE + ; + +drop_type_name + : ACCESS METHOD + | EVENT TRIGGER + | EXTENSION + | FOREIGN DATA_P WRAPPER + | opt_procedural LANGUAGE + | PUBLICATION + | SCHEMA + | SERVER + ; + +object_type_name_on_any_name + : POLICY + | RULE + | TRIGGER + ; + +any_name_list + : any_name (COMMA any_name)* + ; + +any_name + : colid attrs? + ; + +attrs + : (DOT attr_name)+ + ; + +type_name_list + : typename (COMMA typename)* + ; + +truncatestmt + : TRUNCATE opt_table relation_expr_list opt_restart_seqs opt_drop_behavior + ; + +opt_restart_seqs + : CONTINUE_P IDENTITY_P + | RESTART IDENTITY_P + | + ; + +commentstmt + : COMMENT ON object_type_any_name any_name IS comment_text + | COMMENT ON COLUMN any_name IS comment_text + | COMMENT ON object_type_name name IS comment_text + | COMMENT ON TYPE_P typename IS comment_text + | COMMENT ON DOMAIN_P typename IS comment_text + | COMMENT ON AGGREGATE aggregate_with_argtypes IS comment_text + | COMMENT ON FUNCTION function_with_argtypes IS comment_text + | COMMENT ON OPERATOR operator_with_argtypes IS comment_text + | COMMENT ON CONSTRAINT name ON any_name IS comment_text + | COMMENT ON CONSTRAINT name ON DOMAIN_P any_name IS comment_text + | COMMENT ON object_type_name_on_any_name name ON any_name IS comment_text + | COMMENT ON PROCEDURE function_with_argtypes IS comment_text + | COMMENT ON ROUTINE function_with_argtypes IS comment_text + | COMMENT ON TRANSFORM FOR typename LANGUAGE name IS comment_text + | COMMENT ON OPERATOR CLASS any_name USING name IS comment_text + | COMMENT ON OPERATOR FAMILY any_name USING name IS comment_text + | COMMENT ON LARGE_P OBJECT_P numericonly IS comment_text + | COMMENT ON CAST OPEN_PAREN typename AS typename CLOSE_PAREN IS comment_text + ; + +comment_text + : sconst + | NULL_P + ; + +seclabelstmt + : SECURITY LABEL opt_provider ON object_type_any_name any_name IS security_label + | SECURITY LABEL opt_provider ON COLUMN any_name IS security_label + | SECURITY LABEL opt_provider ON object_type_name name IS security_label + | SECURITY LABEL opt_provider ON TYPE_P typename IS security_label + | SECURITY LABEL opt_provider ON DOMAIN_P typename IS security_label + | SECURITY LABEL opt_provider ON AGGREGATE aggregate_with_argtypes IS security_label + | SECURITY LABEL opt_provider ON FUNCTION function_with_argtypes IS security_label + | SECURITY LABEL opt_provider ON LARGE_P OBJECT_P numericonly IS security_label + | SECURITY LABEL opt_provider ON PROCEDURE function_with_argtypes IS security_label + | SECURITY LABEL opt_provider ON ROUTINE function_with_argtypes IS security_label + ; + +opt_provider + : FOR nonreservedword_or_sconst + | + ; + +security_label + : sconst + | NULL_P + ; + +fetchstmt + : FETCH fetch_args + | MOVE fetch_args + ; + +fetch_args + : cursor_name + | from_in cursor_name + | NEXT opt_from_in cursor_name + | PRIOR opt_from_in cursor_name + | FIRST_P opt_from_in cursor_name + | LAST_P opt_from_in cursor_name + | ABSOLUTE_P signediconst opt_from_in cursor_name + | RELATIVE_P signediconst opt_from_in cursor_name + | signediconst opt_from_in cursor_name + | ALL opt_from_in cursor_name + | FORWARD opt_from_in cursor_name + | FORWARD signediconst opt_from_in cursor_name + | FORWARD ALL opt_from_in cursor_name + | BACKWARD opt_from_in cursor_name + | BACKWARD signediconst opt_from_in cursor_name + | BACKWARD ALL opt_from_in cursor_name + ; + +from_in + : FROM + | IN_P + ; + +opt_from_in + : from_in + | + ; + +grantstmt + : GRANT privileges ON privilege_target TO grantee_list opt_grant_grant_option + ; + +revokestmt + : REVOKE privileges ON privilege_target FROM grantee_list opt_drop_behavior + | REVOKE GRANT OPTION FOR privileges ON privilege_target FROM grantee_list opt_drop_behavior + ; + +privileges + : privilege_list + | ALL + | ALL PRIVILEGES + | ALL OPEN_PAREN columnlist CLOSE_PAREN + | ALL PRIVILEGES OPEN_PAREN columnlist CLOSE_PAREN + ; + +privilege_list + : privilege (COMMA privilege)* + ; + +privilege + : SELECT opt_column_list + | REFERENCES opt_column_list + | CREATE opt_column_list + | colid opt_column_list + ; + +privilege_target + : qualified_name_list + | TABLE qualified_name_list + | SEQUENCE qualified_name_list + | FOREIGN DATA_P WRAPPER name_list + | FOREIGN SERVER name_list + | FUNCTION function_with_argtypes_list + | PROCEDURE function_with_argtypes_list + | ROUTINE function_with_argtypes_list + | DATABASE name_list + | DOMAIN_P any_name_list + | LANGUAGE name_list + | LARGE_P OBJECT_P numericonly_list + | SCHEMA name_list + | TABLESPACE name_list + | TYPE_P any_name_list + | ALL TABLES IN_P SCHEMA name_list + | ALL SEQUENCES IN_P SCHEMA name_list + | ALL FUNCTIONS IN_P SCHEMA name_list + | ALL PROCEDURES IN_P SCHEMA name_list + | ALL ROUTINES IN_P SCHEMA name_list + ; + +grantee_list + : grantee (COMMA grantee)* + ; + +grantee + : rolespec + | GROUP_P rolespec + ; + +opt_grant_grant_option + : WITH GRANT OPTION + | + ; + +grantrolestmt + : GRANT privilege_list TO role_list opt_grant_admin_option opt_granted_by + ; + +revokerolestmt + : REVOKE privilege_list FROM role_list opt_granted_by opt_drop_behavior + | REVOKE ADMIN OPTION FOR privilege_list FROM role_list opt_granted_by opt_drop_behavior + ; + +opt_grant_admin_option + : WITH ADMIN OPTION + | + ; + +opt_granted_by + : GRANTED BY rolespec + | + ; + +alterdefaultprivilegesstmt + : ALTER DEFAULT PRIVILEGES defacloptionlist defaclaction + ; + +defacloptionlist + : defacloption* + ; + +defacloption + : IN_P SCHEMA name_list + | FOR ROLE role_list + | FOR USER role_list + ; + +defaclaction + : GRANT privileges ON defacl_privilege_target TO grantee_list opt_grant_grant_option + | REVOKE privileges ON defacl_privilege_target FROM grantee_list opt_drop_behavior + | REVOKE GRANT OPTION FOR privileges ON defacl_privilege_target FROM grantee_list opt_drop_behavior + ; + +defacl_privilege_target + : TABLES + | FUNCTIONS + | ROUTINES + | SEQUENCES + | TYPES_P + | SCHEMAS + ; + +//create index + +indexstmt + : CREATE opt_unique INDEX opt_concurrently opt_index_name ON relation_expr access_method_clause OPEN_PAREN index_params CLOSE_PAREN opt_include + opt_reloptions opttablespace where_clause + | CREATE opt_unique INDEX opt_concurrently IF_P NOT EXISTS name ON relation_expr access_method_clause OPEN_PAREN index_params CLOSE_PAREN + opt_include opt_reloptions opttablespace where_clause + ; + +opt_unique + : UNIQUE + | + ; + +opt_concurrently + : CONCURRENTLY + | + ; + +opt_index_name + : name + | + ; + +access_method_clause + : USING name + | + ; + +index_params + : index_elem (COMMA index_elem)* + ; + +index_elem_options + : opt_collate opt_class opt_asc_desc opt_nulls_order + | opt_collate any_name reloptions opt_asc_desc opt_nulls_order + ; + +index_elem + : colid index_elem_options + | func_expr_windowless index_elem_options + | OPEN_PAREN a_expr CLOSE_PAREN index_elem_options + ; + +opt_include + : INCLUDE OPEN_PAREN index_including_params CLOSE_PAREN + | + ; + +index_including_params + : index_elem (COMMA index_elem)* + ; + +opt_collate + : COLLATE any_name + | + ; + +opt_class + : any_name + | + ; + +opt_asc_desc + : ASC + | DESC + | + ; + +//TOD NULLS_LA was used + +opt_nulls_order + : NULLS_P FIRST_P + | NULLS_P LAST_P + | + ; + +createfunctionstmt + : CREATE opt_or_replace (FUNCTION | PROCEDURE) func_name func_args_with_defaults ( + RETURNS (func_return | TABLE OPEN_PAREN table_func_column_list CLOSE_PAREN) + )? createfunc_opt_list + ; + +opt_type_parameters + : '<' type_parameters '>' + | + ; + +type_parameters + : type_parameter (COMMA type_parameter)* + ; + +type_parameter + : colid typename + ; + +opt_or_replace + : OR REPLACE + | + ; + +func_args + : OPEN_PAREN func_args_list? CLOSE_PAREN + ; + +func_args_list + : func_arg (COMMA func_arg)* + ; + +function_with_argtypes_list + : function_with_argtypes (COMMA function_with_argtypes)* + ; + +function_with_argtypes + : func_name func_args + | type_func_name_keyword + | colid indirection? + ; + +func_args_with_defaults + : OPEN_PAREN func_args_with_defaults_list? CLOSE_PAREN + ; + +func_args_with_defaults_list + : func_arg_with_default (COMMA func_arg_with_default)* + ; + +func_arg + : arg_class param_name? func_type + | param_name arg_class? func_type + | func_type + ; + +arg_class + : IN_P OUT_P? + | OUT_P + | INOUT + | VARIADIC + ; + +param_name + : type_function_name + | builtin_function_name + | LEFT + | RIGHT + ; + +func_return + : func_type + ; + +func_type + : typename + ; + +func_arg_with_default + : func_arg ((DEFAULT | EQUAL) a_expr)? + ; + +aggr_arg + : func_arg + ; + +aggr_args + : OPEN_PAREN ( + STAR + | aggr_args_list + | ORDER BY aggr_args_list + | aggr_args_list ORDER BY aggr_args_list + ) CLOSE_PAREN + ; + +aggr_args_list + : aggr_arg (COMMA aggr_arg)* + ; + +aggregate_with_argtypes + : func_name aggr_args + ; + +aggregate_with_argtypes_list + : aggregate_with_argtypes (COMMA aggregate_with_argtypes)* + ; + +createfunc_opt_list + : createfunc_opt_item+ { + parseRoutineBody(_localctx); + } + // | createfunc_opt_list createfunc_opt_item + ; + +common_func_opt_item + : CALLED ON NULL_P INPUT_P + | RETURNS NULL_P ON NULL_P INPUT_P + | STRICT_P + | IMMUTABLE + | STABLE + | VOLATILE + | EXTERNAL SECURITY DEFINER + | EXTERNAL SECURITY INVOKER + | SECURITY DEFINER + | SECURITY INVOKER + | LEAKPROOF + | NOT LEAKPROOF + | COST numericonly + | ROWS numericonly + | SUPPORT any_name + | functionsetresetclause + | PARALLEL colid + ; + +createfunc_opt_item + : AS func_as + | LANGUAGE nonreservedword_or_sconst + | TRANSFORM transform_type_list + | WINDOW + | common_func_opt_item + ; + +//https://www.postgresql.org/docs/9.1/sql-createfunction.html + +// | AS 'definition' + +// | AS 'obj_file', 'link_symbol' + +func_as + locals[ParserRuleContext Definition] + : + /* |AS 'definition'*/ def = sconst + /*| AS 'obj_file', 'link_symbol'*/ + | sconst COMMA sconst + ; + +transform_type_list + : FOR TYPE_P typename (COMMA FOR TYPE_P typename)* + ; + +opt_definition + : WITH definition + | + ; + +table_func_column + : param_name func_type + ; + +table_func_column_list + : table_func_column (COMMA table_func_column)* + ; + +alterfunctionstmt + : ALTER (FUNCTION | PROCEDURE | ROUTINE) function_with_argtypes alterfunc_opt_list opt_restrict + ; + +alterfunc_opt_list + : common_func_opt_item+ + ; + +opt_restrict + : RESTRICT + | + ; + +removefuncstmt + : DROP FUNCTION function_with_argtypes_list opt_drop_behavior + | DROP FUNCTION IF_P EXISTS function_with_argtypes_list opt_drop_behavior + | DROP PROCEDURE function_with_argtypes_list opt_drop_behavior + | DROP PROCEDURE IF_P EXISTS function_with_argtypes_list opt_drop_behavior + | DROP ROUTINE function_with_argtypes_list opt_drop_behavior + | DROP ROUTINE IF_P EXISTS function_with_argtypes_list opt_drop_behavior + ; + +removeaggrstmt + : DROP AGGREGATE aggregate_with_argtypes_list opt_drop_behavior + | DROP AGGREGATE IF_P EXISTS aggregate_with_argtypes_list opt_drop_behavior + ; + +removeoperstmt + : DROP OPERATOR operator_with_argtypes_list opt_drop_behavior + | DROP OPERATOR IF_P EXISTS operator_with_argtypes_list opt_drop_behavior + ; + +oper_argtypes + : OPEN_PAREN typename CLOSE_PAREN + | OPEN_PAREN typename COMMA typename CLOSE_PAREN + | OPEN_PAREN NONE COMMA typename CLOSE_PAREN + | OPEN_PAREN typename COMMA NONE CLOSE_PAREN + ; + +any_operator + : (colid DOT)* all_op + ; + +operator_with_argtypes_list + : operator_with_argtypes (COMMA operator_with_argtypes)* + ; + +operator_with_argtypes + : any_operator oper_argtypes + ; + +dostmt + : DO dostmt_opt_list + ; + +dostmt_opt_list + : dostmt_opt_item+ + ; + +dostmt_opt_item + : sconst + | LANGUAGE nonreservedword_or_sconst + ; + +createcaststmt + : CREATE CAST OPEN_PAREN typename AS typename CLOSE_PAREN WITH FUNCTION function_with_argtypes cast_context + | CREATE CAST OPEN_PAREN typename AS typename CLOSE_PAREN WITHOUT FUNCTION cast_context + | CREATE CAST OPEN_PAREN typename AS typename CLOSE_PAREN WITH INOUT cast_context + ; + +cast_context + : AS IMPLICIT_P + | AS ASSIGNMENT + | + ; + +dropcaststmt + : DROP CAST opt_if_exists OPEN_PAREN typename AS typename CLOSE_PAREN opt_drop_behavior + ; + +opt_if_exists + : IF_P EXISTS + | + ; + +createtransformstmt + : CREATE opt_or_replace TRANSFORM FOR typename LANGUAGE name OPEN_PAREN transform_element_list CLOSE_PAREN + ; + +transform_element_list + : FROM SQL_P WITH FUNCTION function_with_argtypes COMMA TO SQL_P WITH FUNCTION function_with_argtypes + | TO SQL_P WITH FUNCTION function_with_argtypes COMMA FROM SQL_P WITH FUNCTION function_with_argtypes + | FROM SQL_P WITH FUNCTION function_with_argtypes + | TO SQL_P WITH FUNCTION function_with_argtypes + ; + +droptransformstmt + : DROP TRANSFORM opt_if_exists FOR typename LANGUAGE name opt_drop_behavior + ; + +reindexstmt + : REINDEX reindex_target_type opt_concurrently qualified_name + | REINDEX reindex_target_multitable opt_concurrently name + | REINDEX OPEN_PAREN reindex_option_list CLOSE_PAREN reindex_target_type opt_concurrently qualified_name + | REINDEX OPEN_PAREN reindex_option_list CLOSE_PAREN reindex_target_multitable opt_concurrently name + ; + +reindex_target_type + : INDEX + | TABLE + | SCHEMA + | DATABASE + | SYSTEM_P + ; + +reindex_target_multitable + : SCHEMA + | SYSTEM_P + | DATABASE + ; + +reindex_option_list + : reindex_option_elem (COMMA reindex_option_elem)* + ; + +reindex_option_elem + : VERBOSE + | TABLESPACE + | CONCURRENTLY + ; + +altertblspcstmt + : ALTER TABLESPACE name SET reloptions + | ALTER TABLESPACE name RESET reloptions + ; + +renamestmt + : ALTER AGGREGATE aggregate_with_argtypes RENAME TO name + | ALTER COLLATION any_name RENAME TO name + | ALTER CONVERSION_P any_name RENAME TO name + | ALTER DATABASE name RENAME TO name + | ALTER DOMAIN_P any_name RENAME TO name + | ALTER DOMAIN_P any_name RENAME CONSTRAINT name TO name + | ALTER FOREIGN DATA_P WRAPPER name RENAME TO name + | ALTER FUNCTION function_with_argtypes RENAME TO name + | ALTER GROUP_P roleid RENAME TO roleid + | ALTER opt_procedural LANGUAGE name RENAME TO name + | ALTER OPERATOR CLASS any_name USING name RENAME TO name + | ALTER OPERATOR FAMILY any_name USING name RENAME TO name + | ALTER POLICY name ON qualified_name RENAME TO name + | ALTER POLICY IF_P EXISTS name ON qualified_name RENAME TO name + | ALTER PROCEDURE function_with_argtypes RENAME TO name + | ALTER PUBLICATION name RENAME TO name + | ALTER ROUTINE function_with_argtypes RENAME TO name + | ALTER SCHEMA name RENAME TO name + | ALTER SERVER name RENAME TO name + | ALTER SUBSCRIPTION name RENAME TO name + | ALTER TABLE relation_expr RENAME TO name + | ALTER TABLE IF_P EXISTS relation_expr RENAME TO name + | ALTER SEQUENCE qualified_name RENAME TO name + | ALTER SEQUENCE IF_P EXISTS qualified_name RENAME TO name + | ALTER VIEW qualified_name RENAME TO name + | ALTER VIEW IF_P EXISTS qualified_name RENAME TO name + | ALTER MATERIALIZED VIEW qualified_name RENAME TO name + | ALTER MATERIALIZED VIEW IF_P EXISTS qualified_name RENAME TO name + | ALTER INDEX qualified_name RENAME TO name + | ALTER INDEX IF_P EXISTS qualified_name RENAME TO name + | ALTER FOREIGN TABLE relation_expr RENAME TO name + | ALTER FOREIGN TABLE IF_P EXISTS relation_expr RENAME TO name + | ALTER TABLE relation_expr RENAME opt_column name TO name + | ALTER TABLE IF_P EXISTS relation_expr RENAME opt_column name TO name + | ALTER VIEW qualified_name RENAME opt_column name TO name + | ALTER VIEW IF_P EXISTS qualified_name RENAME opt_column name TO name + | ALTER MATERIALIZED VIEW qualified_name RENAME opt_column name TO name + | ALTER MATERIALIZED VIEW IF_P EXISTS qualified_name RENAME opt_column name TO name + | ALTER TABLE relation_expr RENAME CONSTRAINT name TO name + | ALTER TABLE IF_P EXISTS relation_expr RENAME CONSTRAINT name TO name + | ALTER FOREIGN TABLE relation_expr RENAME opt_column name TO name + | ALTER FOREIGN TABLE IF_P EXISTS relation_expr RENAME opt_column name TO name + | ALTER RULE name ON qualified_name RENAME TO name + | ALTER TRIGGER name ON qualified_name RENAME TO name + | ALTER EVENT TRIGGER name RENAME TO name + | ALTER ROLE roleid RENAME TO roleid + | ALTER USER roleid RENAME TO roleid + | ALTER TABLESPACE name RENAME TO name + | ALTER STATISTICS any_name RENAME TO name + | ALTER TEXT_P SEARCH PARSER any_name RENAME TO name + | ALTER TEXT_P SEARCH DICTIONARY any_name RENAME TO name + | ALTER TEXT_P SEARCH TEMPLATE any_name RENAME TO name + | ALTER TEXT_P SEARCH CONFIGURATION any_name RENAME TO name + | ALTER TYPE_P any_name RENAME TO name + | ALTER TYPE_P any_name RENAME ATTRIBUTE name TO name opt_drop_behavior + ; + +opt_column + : COLUMN + | + ; + +opt_set_data + : SET DATA_P + | + ; + +alterobjectdependsstmt + : ALTER FUNCTION function_with_argtypes opt_no DEPENDS ON EXTENSION name + | ALTER PROCEDURE function_with_argtypes opt_no DEPENDS ON EXTENSION name + | ALTER ROUTINE function_with_argtypes opt_no DEPENDS ON EXTENSION name + | ALTER TRIGGER name ON qualified_name opt_no DEPENDS ON EXTENSION name + | ALTER MATERIALIZED VIEW qualified_name opt_no DEPENDS ON EXTENSION name + | ALTER INDEX qualified_name opt_no DEPENDS ON EXTENSION name + ; + +opt_no + : NO + | + ; + +alterobjectschemastmt + : ALTER AGGREGATE aggregate_with_argtypes SET SCHEMA name + | ALTER COLLATION any_name SET SCHEMA name + | ALTER CONVERSION_P any_name SET SCHEMA name + | ALTER DOMAIN_P any_name SET SCHEMA name + | ALTER EXTENSION name SET SCHEMA name + | ALTER FUNCTION function_with_argtypes SET SCHEMA name + | ALTER OPERATOR operator_with_argtypes SET SCHEMA name + | ALTER OPERATOR CLASS any_name USING name SET SCHEMA name + | ALTER OPERATOR FAMILY any_name USING name SET SCHEMA name + | ALTER PROCEDURE function_with_argtypes SET SCHEMA name + | ALTER ROUTINE function_with_argtypes SET SCHEMA name + | ALTER TABLE relation_expr SET SCHEMA name + | ALTER TABLE IF_P EXISTS relation_expr SET SCHEMA name + | ALTER STATISTICS any_name SET SCHEMA name + | ALTER TEXT_P SEARCH PARSER any_name SET SCHEMA name + | ALTER TEXT_P SEARCH DICTIONARY any_name SET SCHEMA name + | ALTER TEXT_P SEARCH TEMPLATE any_name SET SCHEMA name + | ALTER TEXT_P SEARCH CONFIGURATION any_name SET SCHEMA name + | ALTER SEQUENCE qualified_name SET SCHEMA name + | ALTER SEQUENCE IF_P EXISTS qualified_name SET SCHEMA name + | ALTER VIEW qualified_name SET SCHEMA name + | ALTER VIEW IF_P EXISTS qualified_name SET SCHEMA name + | ALTER MATERIALIZED VIEW qualified_name SET SCHEMA name + | ALTER MATERIALIZED VIEW IF_P EXISTS qualified_name SET SCHEMA name + | ALTER FOREIGN TABLE relation_expr SET SCHEMA name + | ALTER FOREIGN TABLE IF_P EXISTS relation_expr SET SCHEMA name + | ALTER TYPE_P any_name SET SCHEMA name + ; + +alteroperatorstmt + : ALTER OPERATOR operator_with_argtypes SET OPEN_PAREN operator_def_list CLOSE_PAREN + ; + +operator_def_list + : operator_def_elem (COMMA operator_def_elem)* + ; + +operator_def_elem + : collabel EQUAL NONE + | collabel EQUAL operator_def_arg + ; + +operator_def_arg + : func_type + | reserved_keyword + | qual_all_op + | numericonly + | sconst + ; + +altertypestmt + : ALTER TYPE_P any_name SET OPEN_PAREN operator_def_list CLOSE_PAREN + ; + +alterownerstmt + : ALTER AGGREGATE aggregate_with_argtypes OWNER TO rolespec + | ALTER COLLATION any_name OWNER TO rolespec + | ALTER CONVERSION_P any_name OWNER TO rolespec + | ALTER DATABASE name OWNER TO rolespec + | ALTER DOMAIN_P any_name OWNER TO rolespec + | ALTER FUNCTION function_with_argtypes OWNER TO rolespec + | ALTER opt_procedural LANGUAGE name OWNER TO rolespec + | ALTER LARGE_P OBJECT_P numericonly OWNER TO rolespec + | ALTER OPERATOR operator_with_argtypes OWNER TO rolespec + | ALTER OPERATOR CLASS any_name USING name OWNER TO rolespec + | ALTER OPERATOR FAMILY any_name USING name OWNER TO rolespec + | ALTER PROCEDURE function_with_argtypes OWNER TO rolespec + | ALTER ROUTINE function_with_argtypes OWNER TO rolespec + | ALTER SCHEMA name OWNER TO rolespec + | ALTER TYPE_P any_name OWNER TO rolespec + | ALTER TABLESPACE name OWNER TO rolespec + | ALTER STATISTICS any_name OWNER TO rolespec + | ALTER TEXT_P SEARCH DICTIONARY any_name OWNER TO rolespec + | ALTER TEXT_P SEARCH CONFIGURATION any_name OWNER TO rolespec + | ALTER FOREIGN DATA_P WRAPPER name OWNER TO rolespec + | ALTER SERVER name OWNER TO rolespec + | ALTER EVENT TRIGGER name OWNER TO rolespec + | ALTER PUBLICATION name OWNER TO rolespec + | ALTER SUBSCRIPTION name OWNER TO rolespec + ; + +createpublicationstmt + : CREATE PUBLICATION name opt_publication_for_tables opt_definition + ; + +opt_publication_for_tables + : publication_for_tables + | + ; + +publication_for_tables + : FOR TABLE relation_expr_list + | FOR ALL TABLES + ; + +alterpublicationstmt + : ALTER PUBLICATION name SET definition + | ALTER PUBLICATION name ADD_P TABLE relation_expr_list + | ALTER PUBLICATION name SET TABLE relation_expr_list + | ALTER PUBLICATION name DROP TABLE relation_expr_list + ; + +createsubscriptionstmt + : CREATE SUBSCRIPTION name CONNECTION sconst PUBLICATION publication_name_list opt_definition + ; + +publication_name_list + : publication_name_item (COMMA publication_name_item)* + ; + +publication_name_item + : collabel + ; + +altersubscriptionstmt + : ALTER SUBSCRIPTION name SET definition + | ALTER SUBSCRIPTION name CONNECTION sconst + | ALTER SUBSCRIPTION name REFRESH PUBLICATION opt_definition + | ALTER SUBSCRIPTION name SET PUBLICATION publication_name_list opt_definition + | ALTER SUBSCRIPTION name ENABLE_P + | ALTER SUBSCRIPTION name DISABLE_P + ; + +dropsubscriptionstmt + : DROP SUBSCRIPTION name opt_drop_behavior + | DROP SUBSCRIPTION IF_P EXISTS name opt_drop_behavior + ; + +rulestmt + : CREATE opt_or_replace RULE name AS ON event TO qualified_name where_clause DO opt_instead ruleactionlist + ; + +ruleactionlist + : NOTHING + | ruleactionstmt + | OPEN_PAREN ruleactionmulti CLOSE_PAREN + ; + +ruleactionmulti + : ruleactionstmtOrEmpty (SEMI ruleactionstmtOrEmpty)* + ; + +ruleactionstmt + : selectstmt + | insertstmt + | updatestmt + | deletestmt + | notifystmt + ; + +ruleactionstmtOrEmpty + : ruleactionstmt + | + ; + +event + : SELECT + | UPDATE + | DELETE_P + | INSERT + ; + +opt_instead + : INSTEAD + | ALSO + | + ; + +notifystmt + : NOTIFY colid notify_payload + ; + +notify_payload + : COMMA sconst + | + ; + +listenstmt + : LISTEN colid + ; + +unlistenstmt + : UNLISTEN colid + | UNLISTEN STAR + ; + +transactionstmt + : ABORT_P opt_transaction opt_transaction_chain + | BEGIN_P opt_transaction transaction_mode_list_or_empty + | START TRANSACTION transaction_mode_list_or_empty + | COMMIT opt_transaction opt_transaction_chain + | END_P opt_transaction opt_transaction_chain + | ROLLBACK opt_transaction opt_transaction_chain + | SAVEPOINT colid + | RELEASE SAVEPOINT colid + | RELEASE colid + | ROLLBACK opt_transaction TO SAVEPOINT colid + | ROLLBACK opt_transaction TO colid + | PREPARE TRANSACTION sconst + | COMMIT PREPARED sconst + | ROLLBACK PREPARED sconst + ; + +opt_transaction + : WORK + | TRANSACTION + | + ; + +transaction_mode_item + : ISOLATION LEVEL iso_level + | READ ONLY + | READ WRITE + | DEFERRABLE + | NOT DEFERRABLE + ; + +transaction_mode_list + : transaction_mode_item (COMMA? transaction_mode_item)* + ; + +transaction_mode_list_or_empty + : transaction_mode_list + | + ; + +opt_transaction_chain + : AND NO? CHAIN + | + ; + +viewstmt + : CREATE (OR REPLACE)? opttemp ( + VIEW qualified_name opt_column_list opt_reloptions + | RECURSIVE VIEW qualified_name OPEN_PAREN columnlist CLOSE_PAREN opt_reloptions + ) AS selectstmt opt_check_option + ; + +opt_check_option + : WITH (CASCADED | LOCAL)? CHECK OPTION + | + ; + +loadstmt + : LOAD file_name + ; + +createdbstmt + : CREATE DATABASE name opt_with createdb_opt_list + ; + +createdb_opt_list + : createdb_opt_items + | + ; + +createdb_opt_items + : createdb_opt_item+ + ; + +createdb_opt_item + : createdb_opt_name opt_equal (signediconst | opt_boolean_or_string | DEFAULT) + ; + +createdb_opt_name + : identifier + | CONNECTION LIMIT + | ENCODING + | LOCATION + | OWNER + | TABLESPACE + | TEMPLATE + ; + +opt_equal + : EQUAL + | + ; + +alterdatabasestmt + : ALTER DATABASE name (WITH createdb_opt_list | createdb_opt_list | SET TABLESPACE name) + ; + +alterdatabasesetstmt + : ALTER DATABASE name setresetclause + ; + +dropdbstmt + : DROP DATABASE (IF_P EXISTS)? name (opt_with OPEN_PAREN drop_option_list CLOSE_PAREN)? + ; + +drop_option_list + : drop_option (COMMA drop_option)* + ; + +drop_option + : FORCE + ; + +altercollationstmt + : ALTER COLLATION any_name REFRESH VERSION_P + ; + +altersystemstmt + : ALTER SYSTEM_P (SET | RESET) generic_set + ; + +createdomainstmt + : CREATE DOMAIN_P any_name opt_as typename colquallist + ; + +alterdomainstmt + : ALTER DOMAIN_P any_name ( + alter_column_default + | DROP NOT NULL_P + | SET NOT NULL_P + | ADD_P tableconstraint + | DROP CONSTRAINT (IF_P EXISTS)? name opt_drop_behavior + | VALIDATE CONSTRAINT name + ) + ; + +opt_as + : AS + | + ; + +altertsdictionarystmt + : ALTER TEXT_P SEARCH DICTIONARY any_name definition + ; + +altertsconfigurationstmt + : ALTER TEXT_P SEARCH CONFIGURATION any_name ADD_P MAPPING FOR name_list any_with any_name_list + | ALTER TEXT_P SEARCH CONFIGURATION any_name ALTER MAPPING FOR name_list any_with any_name_list + | ALTER TEXT_P SEARCH CONFIGURATION any_name ALTER MAPPING REPLACE any_name any_with any_name + | ALTER TEXT_P SEARCH CONFIGURATION any_name ALTER MAPPING FOR name_list REPLACE any_name any_with any_name + | ALTER TEXT_P SEARCH CONFIGURATION any_name DROP MAPPING FOR name_list + | ALTER TEXT_P SEARCH CONFIGURATION any_name DROP MAPPING IF_P EXISTS FOR name_list + ; + +any_with + : WITH + //TODO + + // | WITH_LA + ; + +createconversionstmt + : CREATE opt_default CONVERSION_P any_name FOR sconst TO sconst FROM any_name + ; + +clusterstmt + : CLUSTER opt_verbose qualified_name cluster_index_specification + | CLUSTER opt_verbose + | CLUSTER opt_verbose name ON qualified_name + ; + +cluster_index_specification + : USING name + | + ; + +vacuumstmt + : VACUUM opt_full opt_freeze opt_verbose opt_analyze opt_vacuum_relation_list + | VACUUM OPEN_PAREN vac_analyze_option_list CLOSE_PAREN opt_vacuum_relation_list + ; + +analyzestmt + : analyze_keyword opt_verbose opt_vacuum_relation_list + | analyze_keyword OPEN_PAREN vac_analyze_option_list CLOSE_PAREN opt_vacuum_relation_list + ; + +vac_analyze_option_list + : vac_analyze_option_elem (COMMA vac_analyze_option_elem)* + ; + +analyze_keyword + : ANALYZE + | ANALYSE + ; + +vac_analyze_option_elem + : vac_analyze_option_name vac_analyze_option_arg + ; + +vac_analyze_option_name + : nonreservedword + | analyze_keyword + ; + +vac_analyze_option_arg + : opt_boolean_or_string + | numericonly + | + ; + +opt_analyze + : analyze_keyword + | + ; + +opt_verbose + : VERBOSE + | + ; + +opt_full + : FULL + | + ; + +opt_freeze + : FREEZE + | + ; + +opt_name_list + : OPEN_PAREN name_list CLOSE_PAREN + | + ; + +vacuum_relation + : qualified_name opt_name_list + ; + +vacuum_relation_list + : vacuum_relation (COMMA vacuum_relation)* + ; + +opt_vacuum_relation_list + : vacuum_relation_list + | + ; + +explainstmt + : EXPLAIN explainablestmt + | EXPLAIN analyze_keyword opt_verbose explainablestmt + | EXPLAIN VERBOSE explainablestmt + | EXPLAIN OPEN_PAREN explain_option_list CLOSE_PAREN explainablestmt + ; + +explainablestmt + : selectstmt + | insertstmt + | updatestmt + | deletestmt + | declarecursorstmt + | createasstmt + | creatematviewstmt + | refreshmatviewstmt + | executestmt + ; + +explain_option_list + : explain_option_elem (COMMA explain_option_elem)* + ; + +explain_option_elem + : explain_option_name explain_option_arg + ; + +explain_option_name + : nonreservedword + | analyze_keyword + ; + +explain_option_arg + : opt_boolean_or_string + | numericonly + | + ; + +preparestmt + : PREPARE name prep_type_clause AS preparablestmt + ; + +prep_type_clause + : OPEN_PAREN type_list CLOSE_PAREN + | + ; + +preparablestmt + : selectstmt + | insertstmt + | updatestmt + | deletestmt + ; + +executestmt + : EXECUTE name execute_param_clause + | CREATE opttemp TABLE create_as_target AS EXECUTE name execute_param_clause opt_with_data + | CREATE opttemp TABLE IF_P NOT EXISTS create_as_target AS EXECUTE name execute_param_clause opt_with_data + ; + +execute_param_clause + : OPEN_PAREN expr_list CLOSE_PAREN + | + ; + +deallocatestmt + : DEALLOCATE name + | DEALLOCATE PREPARE name + | DEALLOCATE ALL + | DEALLOCATE PREPARE ALL + ; + +insertstmt + : opt_with_clause INSERT INTO insert_target insert_rest opt_on_conflict returning_clause + ; + +insert_target + : qualified_name (AS colid)? + ; + +insert_rest + : selectstmt + | OVERRIDING override_kind VALUE_P selectstmt + | OPEN_PAREN insert_column_list CLOSE_PAREN (OVERRIDING override_kind VALUE_P)? selectstmt + | DEFAULT VALUES + ; + +override_kind + : USER + | SYSTEM_P + ; + +insert_column_list + : insert_column_item (COMMA insert_column_item)* + ; + +insert_column_item + : colid opt_indirection + ; + +opt_on_conflict + : ON CONFLICT opt_conf_expr DO (UPDATE SET set_clause_list where_clause | NOTHING) + | + ; + +opt_conf_expr + : OPEN_PAREN index_params CLOSE_PAREN where_clause + | ON CONSTRAINT name + | + ; + +returning_clause + : RETURNING target_list + | + ; + +// https://www.postgresql.org/docs/current/sql-merge.html +mergestmt + : MERGE INTO? qualified_name alias_clause? USING (select_with_parens | qualified_name) alias_clause? ON a_expr ( + merge_insert_clause merge_update_clause? + | merge_update_clause merge_insert_clause? + ) merge_delete_clause? + ; + +merge_insert_clause + : WHEN NOT MATCHED (AND a_expr)? THEN? INSERT (OPEN_PAREN insert_column_list CLOSE_PAREN)? values_clause + ; + +merge_update_clause + : WHEN MATCHED (AND a_expr)? THEN? UPDATE SET set_clause_list + ; + +merge_delete_clause + : WHEN MATCHED THEN? DELETE_P + ; + +deletestmt + : opt_with_clause DELETE_P FROM relation_expr_opt_alias using_clause where_or_current_clause returning_clause + ; + +using_clause + : USING from_list + | + ; + +lockstmt + : LOCK_P opt_table relation_expr_list opt_lock opt_nowait + ; + +opt_lock + : IN_P lock_type MODE + | + ; + +lock_type + : ACCESS (SHARE | EXCLUSIVE) + | ROW (SHARE | EXCLUSIVE) + | SHARE (UPDATE EXCLUSIVE | ROW EXCLUSIVE)? + | EXCLUSIVE + ; + +opt_nowait + : NOWAIT + | + ; + +opt_nowait_or_skip + : NOWAIT + | SKIP_P LOCKED + | + ; + +updatestmt + : opt_with_clause UPDATE relation_expr_opt_alias SET set_clause_list from_clause where_or_current_clause returning_clause + ; + +set_clause_list + : set_clause (COMMA set_clause)* + ; + +set_clause + : set_target EQUAL a_expr + | OPEN_PAREN set_target_list CLOSE_PAREN EQUAL a_expr + ; + +set_target + : colid opt_indirection + ; + +set_target_list + : set_target (COMMA set_target)* + ; + +declarecursorstmt + : DECLARE cursor_name cursor_options CURSOR opt_hold FOR selectstmt + ; + +cursor_name + : name + ; + +cursor_options + : (NO SCROLL | SCROLL | BINARY | INSENSITIVE)* + ; + +opt_hold + : + | WITH HOLD + | WITHOUT HOLD + ; + +/* +TODO: why select_with_parens alternative is needed at all? +i guess it because original byson grammar can choose selectstmt(2)->select_with_parens on only OPEN_PARENT/SELECT kewords at the begining of statement; +(select * from tab); +parse can go through selectstmt( )->select_no_parens(1)->select_clause(2)->select_with_parens(1)->select_no_parens(1)->select_clause(1)->simple_select +instead of selectstmt(1)->select_no_parens(1)->select_clause(2)->select_with_parens(1)->select_no_parens(1)->select_clause(1)->simple_select +all standard tests passed on both variants +*/ + +selectstmt + : select_no_parens + | select_with_parens + ; + +select_with_parens + : OPEN_PAREN select_no_parens CLOSE_PAREN + | OPEN_PAREN select_with_parens CLOSE_PAREN + ; + +select_no_parens + : select_clause opt_sort_clause ( + for_locking_clause opt_select_limit + | select_limit opt_for_locking_clause + )? + | with_clause select_clause opt_sort_clause ( + for_locking_clause opt_select_limit + | select_limit opt_for_locking_clause + )? + ; + +select_clause + : simple_select_intersect ((UNION | EXCEPT) all_or_distinct simple_select_intersect)* + ; + +simple_select_intersect + : simple_select_pramary (INTERSECT all_or_distinct simple_select_pramary)* + ; + +simple_select_pramary + : ( + SELECT (opt_all_clause into_clause opt_target_list | distinct_clause target_list) into_clause from_clause where_clause group_clause + having_clause window_clause + ) + | values_clause + | TABLE relation_expr + | select_with_parens + ; + +with_clause + : WITH RECURSIVE? cte_list + ; + +cte_list + : common_table_expr (COMMA common_table_expr)* + ; + +common_table_expr + : name opt_name_list AS opt_materialized OPEN_PAREN preparablestmt CLOSE_PAREN + ; + +opt_materialized + : MATERIALIZED + | NOT MATERIALIZED + | + ; + +opt_with_clause + : with_clause + | + ; + +into_clause + : INTO (opt_strict opttempTableName | into_target) + | + ; + +opt_strict + : + | STRICT_P + ; + +opttempTableName + : (LOCAL | GLOBAL)? (TEMPORARY | TEMP) opt_table qualified_name + | UNLOGGED opt_table qualified_name + | TABLE qualified_name + | qualified_name + ; + +opt_table + : TABLE + | + ; + +all_or_distinct + : ALL + | DISTINCT + | + ; + +distinct_clause + : DISTINCT (ON OPEN_PAREN expr_list CLOSE_PAREN)? + ; + +opt_all_clause + : ALL + | + ; + +opt_sort_clause + : sort_clause + | + ; + +sort_clause + : ORDER BY sortby_list + ; + +sortby_list + : sortby (COMMA sortby)* + ; + +sortby + : a_expr (USING qual_all_op | opt_asc_desc) opt_nulls_order + ; + +select_limit + : limit_clause offset_clause? + | offset_clause limit_clause? + ; + +opt_select_limit + : select_limit + | + ; + +limit_clause + : LIMIT select_limit_value (COMMA select_offset_value)? + | FETCH first_or_next ( + select_fetch_first_value row_or_rows (ONLY | WITH TIES) + | row_or_rows (ONLY | WITH TIES) + ) + ; + +offset_clause + : OFFSET (select_offset_value | select_fetch_first_value row_or_rows) + ; + +select_limit_value + : a_expr + | ALL + ; + +select_offset_value + : a_expr + ; + +select_fetch_first_value + : c_expr + | PLUS i_or_f_const + | MINUS i_or_f_const + ; + +i_or_f_const + : iconst + | fconst + ; + +row_or_rows + : ROW + | ROWS + ; + +first_or_next + : FIRST_P + | NEXT + ; + +group_clause + : GROUP_P BY group_by_list + | + ; + +group_by_list + : group_by_item (COMMA group_by_item)* + ; + +group_by_item + : empty_grouping_set + | cube_clause + | rollup_clause + | grouping_sets_clause + | a_expr + ; + +empty_grouping_set + : OPEN_PAREN CLOSE_PAREN + ; + +rollup_clause + : ROLLUP OPEN_PAREN expr_list CLOSE_PAREN + ; + +cube_clause + : CUBE OPEN_PAREN expr_list CLOSE_PAREN + ; + +grouping_sets_clause + : GROUPING SETS OPEN_PAREN group_by_list CLOSE_PAREN + ; + +having_clause + : HAVING a_expr + | + ; + +for_locking_clause + : for_locking_items + | FOR READ ONLY + ; + +opt_for_locking_clause + : for_locking_clause + | + ; + +for_locking_items + : for_locking_item+ + ; + +for_locking_item + : for_locking_strength locked_rels_list opt_nowait_or_skip + ; + +for_locking_strength + : FOR ((NO KEY)? UPDATE | KEY? SHARE) + ; + +locked_rels_list + : OF qualified_name_list + | + ; + +values_clause + : VALUES OPEN_PAREN expr_list CLOSE_PAREN (COMMA OPEN_PAREN expr_list CLOSE_PAREN)* + ; + +from_clause + : FROM from_list + | + ; + +from_list + : non_ansi_join + | table_ref (COMMA table_ref)* + ; + +non_ansi_join + : table_ref (COMMA table_ref)+ + ; + +table_ref + : ( + relation_expr opt_alias_clause tablesample_clause? + | func_table func_alias_clause + | xmltable opt_alias_clause + | select_with_parens opt_alias_clause + | LATERAL_P ( + xmltable opt_alias_clause + | func_table func_alias_clause + | select_with_parens opt_alias_clause + ) + | OPEN_PAREN table_ref ( + CROSS JOIN table_ref + | NATURAL join_type? JOIN table_ref + | join_type? JOIN table_ref join_qual + )? CLOSE_PAREN opt_alias_clause + ) ( + CROSS JOIN table_ref + | NATURAL join_type? JOIN table_ref + | join_type? JOIN table_ref join_qual + )* + ; + +alias_clause + : AS? colid (OPEN_PAREN name_list CLOSE_PAREN)? + ; + +opt_alias_clause + : table_alias_clause + | + ; + +table_alias_clause + : AS? table_alias (OPEN_PAREN name_list CLOSE_PAREN)? + ; + +func_alias_clause + : alias_clause + | (AS colid? | colid) OPEN_PAREN tablefuncelementlist CLOSE_PAREN + | + ; + +join_type + : (FULL | LEFT | RIGHT | INNER_P) OUTER_P? + ; + +join_qual + : USING OPEN_PAREN name_list CLOSE_PAREN + | ON a_expr + ; + +relation_expr + : qualified_name STAR? + | ONLY (qualified_name | OPEN_PAREN qualified_name CLOSE_PAREN) + ; + +relation_expr_list + : relation_expr (COMMA relation_expr)* + ; + +relation_expr_opt_alias + : relation_expr (AS? colid)? + ; + +tablesample_clause + : TABLESAMPLE func_name OPEN_PAREN expr_list CLOSE_PAREN opt_repeatable_clause + ; + +opt_repeatable_clause + : REPEATABLE OPEN_PAREN a_expr CLOSE_PAREN + | + ; + +func_table + : func_expr_windowless opt_ordinality + | ROWS FROM OPEN_PAREN rowsfrom_list CLOSE_PAREN opt_ordinality + ; + +rowsfrom_item + : func_expr_windowless opt_col_def_list + ; + +rowsfrom_list + : rowsfrom_item (COMMA rowsfrom_item)* + ; + +opt_col_def_list + : AS OPEN_PAREN tablefuncelementlist CLOSE_PAREN + | + ; + +//TODO WITH_LA was used + +opt_ordinality + : WITH ORDINALITY + | + ; + +where_clause + : WHERE a_expr + | + ; + +where_or_current_clause + : WHERE (CURRENT_P OF cursor_name | a_expr) + | + ; + +opttablefuncelementlist + : tablefuncelementlist + | + ; + +tablefuncelementlist + : tablefuncelement (COMMA tablefuncelement)* + ; + +tablefuncelement + : colid typename opt_collate_clause + ; + +xmltable + : XMLTABLE OPEN_PAREN ( + c_expr xmlexists_argument COLUMNS xmltable_column_list + | XMLNAMESPACES OPEN_PAREN xml_namespace_list CLOSE_PAREN COMMA c_expr xmlexists_argument COLUMNS xmltable_column_list + ) CLOSE_PAREN + ; + +xmltable_column_list + : xmltable_column_el (COMMA xmltable_column_el)* + ; + +xmltable_column_el + : colid (typename xmltable_column_option_list? | FOR ORDINALITY) + ; + +xmltable_column_option_list + : xmltable_column_option_el+ + ; + +xmltable_column_option_el + : DEFAULT a_expr + | identifier a_expr + | NOT NULL_P + | NULL_P + ; + +xml_namespace_list + : xml_namespace_el (COMMA xml_namespace_el)* + ; + +xml_namespace_el + : b_expr AS collabel + | DEFAULT b_expr + ; + +typename + : SETOF? simpletypename (opt_array_bounds | ARRAY (OPEN_BRACKET iconst CLOSE_BRACKET)?) + | qualified_name PERCENT (ROWTYPE | TYPE_P) + ; + +opt_array_bounds + : (OPEN_BRACKET iconst? CLOSE_BRACKET)* + ; + +simpletypename + : generictype + | numeric + | bit + | character + | constdatetime + | constinterval (opt_interval | OPEN_PAREN iconst CLOSE_PAREN) + ; + +consttypename + : numeric + | constbit + | constcharacter + | constdatetime + ; + +generictype + : (builtin_function_name | type_function_name | LEFT | RIGHT) attrs? opt_type_modifiers opt_type_parameters + ; + +opt_type_modifiers + : OPEN_PAREN expr_list CLOSE_PAREN + | + ; + +numeric + : INT_P + | INTEGER + | SMALLINT + | BIGINT + | REAL + | FLOAT_P opt_float + | DOUBLE_P PRECISION + | DECIMAL_P opt_type_modifiers + | DEC opt_type_modifiers + | NUMERIC opt_type_modifiers + | BOOLEAN_P + ; + +opt_float + : OPEN_PAREN iconst CLOSE_PAREN + | + ; + +//todo: merge alts + +bit + : bitwithlength + | bitwithoutlength + ; + +constbit + : bitwithlength + | bitwithoutlength + ; + +bitwithlength + : BIT opt_varying OPEN_PAREN expr_list CLOSE_PAREN + ; + +bitwithoutlength + : BIT opt_varying + ; + +character + : character_c (OPEN_PAREN iconst CLOSE_PAREN)? + ; + +constcharacter + : character_c (OPEN_PAREN iconst CLOSE_PAREN)? + ; + +character_c + : (CHARACTER | CHAR_P | NCHAR) opt_varying + | VARCHAR + | NATIONAL (CHARACTER | CHAR_P) opt_varying + ; + +opt_varying + : VARYING + | + ; + +constdatetime + : (TIMESTAMP | TIME) (OPEN_PAREN iconst CLOSE_PAREN)? opt_timezone + ; + +constinterval + : INTERVAL + ; + +//TODO with_la was used + +opt_timezone + : WITH TIME ZONE + | WITHOUT TIME ZONE + | + ; + +opt_interval + : YEAR_P + | MONTH_P + | DAY_P + | HOUR_P + | MINUTE_P + | interval_second + | YEAR_P TO MONTH_P + | DAY_P TO (HOUR_P | MINUTE_P | interval_second) + | HOUR_P TO (MINUTE_P | interval_second) + | MINUTE_P TO interval_second + | + ; + +interval_second + : SECOND_P (OPEN_PAREN iconst CLOSE_PAREN)? + ; + +opt_escape + : ESCAPE a_expr + | + ; + +//precendence accroding to Table 4.2. Operator Precedence (highest to lowest) + +//https://www.postgresql.org/docs/12/sql-syntax-lexical.html#SQL-PRECEDENCE + +/* +original version of a_expr, for info + a_expr: c_expr + //:: left PostgreSQL-style typecast + | a_expr TYPECAST typename -- 1 + | a_expr COLLATE any_name -- 2 + | a_expr AT TIME ZONE a_expr-- 3 + //right unary plus, unary minus + | (PLUS| MINUS) a_expr -- 4 + //left exponentiation + | a_expr CARET a_expr -- 5 + //left multiplication, division, modulo + | a_expr (STAR | SLASH | PERCENT) a_expr -- 6 + //left addition, subtraction + | a_expr (PLUS | MINUS) a_expr -- 7 + //left all other native and user-defined operators + | a_expr qual_op a_expr -- 8 + | qual_op a_expr -- 9 + //range containment, set membership, string matching BETWEEN IN LIKE ILIKE SIMILAR + | a_expr NOT? (LIKE|ILIKE|SIMILAR TO|(BETWEEN SYMMETRIC?)) a_expr opt_escape -- 10 + //< > = <= >= <> comparison operators + | a_expr (LT | GT | EQUAL | LESS_EQUALS | GREATER_EQUALS | NOT_EQUALS) a_expr -- 11 + //IS ISNULL NOTNULL IS TRUE, IS FALSE, IS NULL, IS DISTINCT FROM, etc + | a_expr IS NOT? + ( + NULL_P + |TRUE_P + |FALSE_P + |UNKNOWN + |DISTINCT FROM a_expr + |OF OPEN_PAREN type_list CLOSE_PAREN + |DOCUMENT_P + |unicode_normal_form? NORMALIZED + ) -- 12 + | a_expr (ISNULL|NOTNULL) -- 13 + | row OVERLAPS row -- 14 + //NOT right logical negation + | NOT a_expr -- 15 + //AND left logical conjunction + | a_expr AND a_expr -- 16 + //OR left logical disjunction + | a_expr OR a_expr -- 17 + | a_expr (LESS_LESS|GREATER_GREATER) a_expr -- 18 + | a_expr qual_op -- 19 + | a_expr NOT? IN_P in_expr -- 20 + | a_expr subquery_Op sub_type (select_with_parens|OPEN_PAREN a_expr CLOSE_PAREN) -- 21 + | UNIQUE select_with_parens -- 22 + | DEFAULT -- 23 +; +*/ + +a_expr + : a_expr_qual + ; + +/*23*/ + +/*moved to c_expr*/ + +/*22*/ + +/*moved to c_expr*/ + +/*19*/ + +a_expr_qual + : a_expr_lessless qual_op? + ; + +/*18*/ + +a_expr_lessless + : a_expr_or ((LESS_LESS | GREATER_GREATER) a_expr_or)* + ; + +/*17*/ + +a_expr_or + : a_expr_and (OR a_expr_and)* + ; + +/*16*/ + +a_expr_and + : a_expr_between (AND a_expr_between)* + ; + +/*21*/ + +a_expr_between + : a_expr_in (NOT? BETWEEN SYMMETRIC? a_expr_in AND a_expr_in)? + ; + +/*20*/ + +a_expr_in + : a_expr_unary_not (NOT? IN_P in_expr)? + ; + +/*15*/ + +a_expr_unary_not + : NOT? a_expr_isnull + ; + +/*14*/ + +/*moved to c_expr*/ + +/*13*/ + +a_expr_isnull + : a_expr_is_not (ISNULL | NOTNULL)? + ; + +/*12*/ + +a_expr_is_not + : a_expr_compare ( + IS NOT? ( + NULL_P + | TRUE_P + | FALSE_P + | UNKNOWN + | DISTINCT FROM a_expr + | OF OPEN_PAREN type_list CLOSE_PAREN + | DOCUMENT_P + | unicode_normal_form? NORMALIZED + ) + )? + ; + +/*11*/ + +a_expr_compare + : a_expr_like ( + (LT | GT | EQUAL | LESS_EQUALS | GREATER_EQUALS | NOT_EQUALS) a_expr_like + | subquery_Op sub_type (select_with_parens | OPEN_PAREN a_expr CLOSE_PAREN) /*21*/ + )? + ; + +/*10*/ + +a_expr_like + : a_expr_qual_op (NOT? (LIKE | ILIKE | SIMILAR TO) a_expr_qual_op opt_escape)? + ; + +/* 8*/ + +a_expr_qual_op + : a_expr_unary_qualop (qual_op a_expr_unary_qualop)* + ; + +/* 9*/ + +a_expr_unary_qualop + : qual_op? a_expr_add + ; + +/* 7*/ + +a_expr_add + : a_expr_mul ((MINUS | PLUS) a_expr_mul)* + ; + +/* 6*/ + +a_expr_mul + : a_expr_caret ((STAR | SLASH | PERCENT) a_expr_caret)* + ; + +/* 5*/ + +a_expr_caret + : a_expr_unary_sign (CARET a_expr)? + ; + +/* 4*/ + +a_expr_unary_sign + : (MINUS | PLUS)? a_expr_at_time_zone /* */ + ; + +/* 3*/ + +a_expr_at_time_zone + : a_expr_collate (AT TIME ZONE a_expr)? + ; + +/* 2*/ + +a_expr_collate + : a_expr_typecast (COLLATE any_name)? + ; + +/* 1*/ + +a_expr_typecast + : c_expr (TYPECAST typename)* + ; + +b_expr + : c_expr + | b_expr TYPECAST typename + //right unary plus, unary minus + | (PLUS | MINUS) b_expr + //^ left exponentiation + | b_expr CARET b_expr + //* / % left multiplication, division, modulo + | b_expr (STAR | SLASH | PERCENT) b_expr + //+ - left addition, subtraction + | b_expr (PLUS | MINUS) b_expr + //(any other operator) left all other native and user-defined operators + | b_expr qual_op b_expr + //< > = <= >= <> comparison operators + | b_expr (LT | GT | EQUAL | LESS_EQUALS | GREATER_EQUALS | NOT_EQUALS) b_expr + | qual_op b_expr + | b_expr qual_op + //S ISNULL NOTNULL IS TRUE, IS FALSE, IS NULL, IS DISTINCT FROM, etc + | b_expr IS NOT? (DISTINCT FROM b_expr | OF OPEN_PAREN type_list CLOSE_PAREN | DOCUMENT_P) + ; + +c_expr + : EXISTS select_with_parens # c_expr_exists + | ARRAY (select_with_parens | array_expr) # c_expr_expr + | PARAM opt_indirection # c_expr_expr + | GROUPING OPEN_PAREN expr_list CLOSE_PAREN # c_expr_expr + | /*22*/ UNIQUE select_with_parens # c_expr_expr + | columnref # c_expr_expr + | aexprconst # c_expr_expr + | plsqlvariablename # c_expr_expr + | OPEN_PAREN a_expr_in_parens = a_expr CLOSE_PAREN opt_indirection # c_expr_expr + | case_expr # c_expr_case + | func_expr # c_expr_expr + | select_with_parens indirection? # c_expr_expr + | explicit_row # c_expr_expr + | implicit_row # c_expr_expr + | row OVERLAPS row /* 14*/ # c_expr_expr + ; + +plsqlvariablename + : PLSQLVARIABLENAME + ; + +func_application + : func_name OPEN_PAREN ( + func_arg_list (COMMA VARIADIC func_arg_expr)? opt_sort_clause + | VARIADIC func_arg_expr opt_sort_clause + | (ALL | DISTINCT) func_arg_list opt_sort_clause + | STAR + | + ) CLOSE_PAREN + ; + +func_expr + : func_application within_group_clause filter_clause over_clause + | func_expr_common_subexpr + ; + +func_expr_windowless + : func_application + | func_expr_common_subexpr + ; + +func_expr_common_subexpr + : COLLATION FOR OPEN_PAREN a_expr CLOSE_PAREN + | CURRENT_DATE + | CURRENT_TIME (OPEN_PAREN iconst CLOSE_PAREN)? + | CURRENT_TIMESTAMP (OPEN_PAREN iconst CLOSE_PAREN)? + | LOCALTIME (OPEN_PAREN iconst CLOSE_PAREN)? + | LOCALTIMESTAMP (OPEN_PAREN iconst CLOSE_PAREN)? + | CURRENT_ROLE + | CURRENT_USER + | SESSION_USER + | USER + | CURRENT_CATALOG + | CURRENT_SCHEMA + | CAST OPEN_PAREN a_expr AS typename CLOSE_PAREN + | EXTRACT OPEN_PAREN extract_list CLOSE_PAREN + | NORMALIZE OPEN_PAREN a_expr (COMMA unicode_normal_form)? CLOSE_PAREN + | OVERLAY OPEN_PAREN overlay_list CLOSE_PAREN + | POSITION OPEN_PAREN position_list CLOSE_PAREN + | SUBSTRING OPEN_PAREN substr_list CLOSE_PAREN + | TREAT OPEN_PAREN a_expr AS typename CLOSE_PAREN + | TRIM OPEN_PAREN (BOTH | LEADING | TRAILING)? trim_list CLOSE_PAREN + | NULLIF OPEN_PAREN a_expr COMMA a_expr CLOSE_PAREN + | COALESCE OPEN_PAREN expr_list CLOSE_PAREN + | GREATEST OPEN_PAREN expr_list CLOSE_PAREN + | LEAST OPEN_PAREN expr_list CLOSE_PAREN + | XMLCONCAT OPEN_PAREN expr_list CLOSE_PAREN + | XMLELEMENT OPEN_PAREN NAME_P collabel (COMMA (xml_attributes | expr_list))? CLOSE_PAREN + | XMLEXISTS OPEN_PAREN c_expr xmlexists_argument CLOSE_PAREN + | XMLFOREST OPEN_PAREN xml_attribute_list CLOSE_PAREN + | XMLPARSE OPEN_PAREN document_or_content a_expr xml_whitespace_option CLOSE_PAREN + | XMLPI OPEN_PAREN NAME_P collabel (COMMA a_expr)? CLOSE_PAREN + | XMLROOT OPEN_PAREN XML_P a_expr COMMA xml_root_version opt_xml_root_standalone CLOSE_PAREN + | XMLSERIALIZE OPEN_PAREN document_or_content a_expr AS simpletypename CLOSE_PAREN + ; + +xml_root_version + : VERSION_P a_expr + | VERSION_P NO VALUE_P + ; + +opt_xml_root_standalone + : COMMA STANDALONE_P YES_P + | COMMA STANDALONE_P NO + | COMMA STANDALONE_P NO VALUE_P + | + ; + +xml_attributes + : XMLATTRIBUTES OPEN_PAREN xml_attribute_list CLOSE_PAREN + ; + +xml_attribute_list + : xml_attribute_el (COMMA xml_attribute_el)* + ; + +xml_attribute_el + : a_expr (AS collabel)? + ; + +document_or_content + : DOCUMENT_P + | CONTENT_P + ; + +xml_whitespace_option + : PRESERVE WHITESPACE_P + | STRIP_P WHITESPACE_P + | + ; + +xmlexists_argument + : PASSING c_expr + | PASSING c_expr xml_passing_mech + | PASSING xml_passing_mech c_expr + | PASSING xml_passing_mech c_expr xml_passing_mech + ; + +xml_passing_mech + : BY (REF | VALUE_P) + ; + +within_group_clause + : WITHIN GROUP_P OPEN_PAREN sort_clause CLOSE_PAREN + | + ; + +filter_clause + : FILTER OPEN_PAREN WHERE a_expr CLOSE_PAREN + | + ; + +window_clause + : WINDOW window_definition_list + | + ; + +window_definition_list + : window_definition (COMMA window_definition)* + ; + +window_definition + : colid AS window_specification + ; + +over_clause + : OVER (window_specification | colid) + | + ; + +window_specification + : OPEN_PAREN opt_existing_window_name opt_partition_clause opt_sort_clause opt_frame_clause CLOSE_PAREN + ; + +opt_existing_window_name + : colid + | + ; + +opt_partition_clause + : PARTITION BY expr_list + | + ; + +opt_frame_clause + : RANGE frame_extent opt_window_exclusion_clause + | ROWS frame_extent opt_window_exclusion_clause + | GROUPS frame_extent opt_window_exclusion_clause + | + ; + +frame_extent + : frame_bound + | BETWEEN frame_bound AND frame_bound + ; + +frame_bound + : UNBOUNDED (PRECEDING | FOLLOWING) + | CURRENT_P ROW + | a_expr (PRECEDING | FOLLOWING) + ; + +opt_window_exclusion_clause + : EXCLUDE (CURRENT_P ROW | GROUP_P | TIES | NO OTHERS) + | + ; + +row + : ROW OPEN_PAREN expr_list? CLOSE_PAREN + | OPEN_PAREN expr_list COMMA a_expr CLOSE_PAREN + ; + +explicit_row + : ROW OPEN_PAREN expr_list? CLOSE_PAREN + ; + +/* +TODO: +for some reason v1 +implicit_row: OPEN_PAREN expr_list COMMA a_expr CLOSE_PAREN; +works better than v2 +implicit_row: OPEN_PAREN expr_list CLOSE_PAREN; +while looks like they are almost the same, except v2 requieres at least 2 items in list +while v1 allows single item in list +*/ + +implicit_row + : OPEN_PAREN expr_list COMMA a_expr CLOSE_PAREN + ; + +sub_type + : ANY + | SOME + | ALL + ; + +all_op + : Operator + | mathop + ; + +mathop + : PLUS + | MINUS + | STAR + | SLASH + | PERCENT + | CARET + | LT + | GT + | EQUAL + | LESS_EQUALS + | GREATER_EQUALS + | NOT_EQUALS + ; + +qual_op + : Operator + | OPERATOR OPEN_PAREN any_operator CLOSE_PAREN + ; + +qual_all_op + : all_op + | OPERATOR OPEN_PAREN any_operator CLOSE_PAREN + ; + +subquery_Op + : all_op + | OPERATOR OPEN_PAREN any_operator CLOSE_PAREN + | LIKE + | NOT LIKE + | ILIKE + | NOT ILIKE + ; + +expr_list + : a_expr (COMMA a_expr)* + ; + +func_arg_list + : func_arg_expr (COMMA func_arg_expr)* + ; + +func_arg_expr + : a_expr + | param_name (COLON_EQUALS | EQUALS_GREATER) a_expr + ; + +type_list + : typename (COMMA typename)* + ; + +array_expr + : OPEN_BRACKET (expr_list | array_expr_list)? CLOSE_BRACKET + ; + +array_expr_list + : array_expr (COMMA array_expr)* + ; + +extract_list + : extract_arg FROM a_expr + | + ; + +extract_arg + : identifier + | YEAR_P + | MONTH_P + | DAY_P + | HOUR_P + | MINUTE_P + | SECOND_P + | sconst + ; + +unicode_normal_form + : NFC + | NFD + | NFKC + | NFKD + ; + +overlay_list + : a_expr PLACING a_expr FROM a_expr (FOR a_expr)? + ; + +position_list + : b_expr IN_P b_expr + | + ; + +substr_list + : a_expr FROM a_expr FOR a_expr + | a_expr FOR a_expr FROM a_expr + | a_expr FROM a_expr + | a_expr FOR a_expr + | a_expr SIMILAR a_expr ESCAPE a_expr + | expr_list + ; + +trim_list + : a_expr FROM expr_list + | FROM expr_list + | expr_list + ; + +in_expr + : select_with_parens # in_expr_select + | OPEN_PAREN expr_list CLOSE_PAREN # in_expr_list + ; + +case_expr + : CASE case_arg when_clause_list case_default END_P + ; + +when_clause_list + : when_clause+ + ; + +when_clause + : WHEN a_expr THEN a_expr + ; + +case_default + : ELSE a_expr + | + ; + +case_arg + : a_expr + | + ; + +columnref + : colid indirection? + ; + +indirection_el + : DOT (attr_name | STAR) + | OPEN_BRACKET (a_expr | opt_slice_bound COLON opt_slice_bound) CLOSE_BRACKET + ; + +opt_slice_bound + : a_expr + | + ; + +indirection + : indirection_el+ + ; + +opt_indirection + : indirection_el* + ; + +opt_target_list + : target_list + | + ; + +target_list + : target_el (COMMA target_el)* + ; + +target_el + : a_expr (AS collabel | identifier |) # target_label + | STAR # target_star + ; + +qualified_name_list + : qualified_name (COMMA qualified_name)* + ; + +qualified_name + : colid indirection? + ; + +name_list + : name (COMMA name)* + ; + +name + : colid + ; + +attr_name + : collabel + ; + +file_name + : sconst + ; + +func_name + : builtin_function_name + | type_function_name + | colid indirection + | LEFT + | RIGHT + ; + +aexprconst + : iconst + | fconst + | sconst + | bconst + | xconst + | func_name (sconst | OPEN_PAREN func_arg_list opt_sort_clause CLOSE_PAREN sconst) + | consttypename sconst + | constinterval (sconst opt_interval | OPEN_PAREN iconst CLOSE_PAREN sconst) + | TRUE_P + | FALSE_P + | NULL_P + ; + +xconst + : HexadecimalStringConstant + ; + +bconst + : BinaryStringConstant + ; + +fconst + : Numeric + ; + +iconst + : Integral + ; + +sconst + : anysconst opt_uescape + ; + +anysconst + : StringConstant + | UnicodeEscapeStringConstant + | BeginDollarStringConstant DollarText* EndDollarStringConstant + | EscapeStringConstant + ; + +opt_uescape + : UESCAPE anysconst + | + ; + +signediconst + : iconst + | PLUS iconst + | MINUS iconst + ; + +roleid + : rolespec + ; + +rolespec + : nonreservedword + | CURRENT_USER + | SESSION_USER + ; + +role_list + : rolespec (COMMA rolespec)* + ; + +colid + : identifier + | unreserved_keyword + | col_name_keyword + | plsql_unreserved_keyword + | LEFT + | RIGHT + ; + +table_alias + : identifier + | unreserved_keyword + | col_name_keyword + | plsql_unreserved_keyword + ; + +type_function_name + : identifier + | unreserved_keyword + | plsql_unreserved_keyword + | type_func_name_keyword + ; + +nonreservedword + : identifier + | unreserved_keyword + | col_name_keyword + | type_func_name_keyword + ; + +collabel + : identifier + | plsql_unreserved_keyword + | unreserved_keyword + | col_name_keyword + | type_func_name_keyword + | reserved_keyword + ; + +identifier + : Identifier opt_uescape + | QuotedIdentifier + | UnicodeQuotedIdentifier + | plsqlvariablename + | plsqlidentifier + | plsql_unreserved_keyword + ; + +plsqlidentifier + : PLSQLIDENTIFIER + ; + +unreserved_keyword + : ABORT_P + | ABSOLUTE_P + | ACCESS + | ACTION + | ADD_P + | ADMIN + | AFTER + | AGGREGATE + | ALSO + | ALTER + | ALWAYS + | ASSERTION + | ASSIGNMENT + | AT + | ATTACH + | ATTRIBUTE + | BACKWARD + | BEFORE + | BEGIN_P + | BY + | CACHE + | CALL + | CALLED + | CASCADE + | CASCADED + | CATALOG + | CHAIN + | CHARACTERISTICS + | CHECKPOINT + | CLASS + | CLOSE + | CLUSTER + | COLUMNS + | COMMENT + | COMMENTS + | COMMIT + | COMMITTED + | CONFIGURATION + | CONFLICT + | CONNECTION + | CONSTRAINTS + | CONTENT_P + | CONTINUE_P + | CONVERSION_P + | COPY + | COST + | CSV + | CUBE + | CURRENT_P + | CURSOR + | CYCLE + | DATA_P + | DATABASE + | DAY_P + | DEALLOCATE + | DECLARE + | DEFAULTS + | DEFERRED + | DEFINER + | DELETE_P + | DELIMITER + | DELIMITERS + | DEPENDS + | DETACH + | DICTIONARY + | DISABLE_P + | DISCARD + | DOCUMENT_P + | DOMAIN_P + | DOUBLE_P + | DROP + | EACH + | ENABLE_P + | ENCODING + | ENCRYPTED + | ENUM_P + | ESCAPE + | EVENT + | EXCLUDE + | EXCLUDING + | EXCLUSIVE + | EXECUTE + | EXPLAIN + | EXPRESSION + | EXTENSION + | EXTERNAL + | FAMILY + | FILTER + | FIRST_P + | FOLLOWING + | FORCE + | FORWARD + | FUNCTION + | FUNCTIONS + | GENERATED + | GLOBAL + | GRANTED + | GROUPS + | HANDLER + | HEADER_P + | HOLD + | HOUR_P + | IDENTITY_P + | IF_P + | IMMEDIATE + | IMMUTABLE + | IMPLICIT_P + | IMPORT_P + | INCLUDE + | INCLUDING + | INCREMENT + | INDEX + | INDEXES + | INHERIT + | INHERITS + | INLINE_P + | INPUT_P + | INSENSITIVE + | INSERT + | INSTEAD + | INVOKER + | ISOLATION + | KEY + | LABEL + | LANGUAGE + | LARGE_P + | LAST_P + | LEAKPROOF + | LEVEL + | LISTEN + | LOAD + | LOCAL + | LOCATION + | LOCK_P + | LOCKED + | LOGGED + | MAPPING + | MATCH + | MATERIALIZED + | MAXVALUE + | METHOD + | MINUTE_P + | MINVALUE + | MODE + | MONTH_P + | MOVE + | NAME_P + | NAMES + | NEW + | NEXT + | NFC + | NFD + | NFKC + | NFKD + | NO + | NORMALIZED + | NOTHING + | NOTIFY + | NOWAIT + | NULLS_P + | OBJECT_P + | OF + | OFF + | OIDS + | OLD + | OPERATOR + | OPTION + | OPTIONS + | ORDINALITY + | OTHERS + | OVER + | OVERRIDING + | OWNED + | OWNER + | PARALLEL + | PARSER + | PARTIAL + | PARTITION + | PASSING + | PASSWORD + | PLANS + | POLICY + | PRECEDING + | PREPARE + | PREPARED + | PRESERVE + | PRIOR + | PRIVILEGES + | PROCEDURAL + | PROCEDURE + | PROCEDURES + | PROGRAM + | PUBLICATION + | QUOTE + | RANGE + | READ + | REASSIGN + | RECHECK + | RECURSIVE + | REF + | REFERENCING + | REFRESH + | REINDEX + | RELATIVE_P + | RELEASE + | RENAME + | REPEATABLE + | REPLICA + | RESET + | RESTART + | RESTRICT + | RETURNS + | REVOKE + | ROLE + | ROLLBACK + | ROLLUP + | ROUTINE + | ROUTINES + | ROWS + | RULE + | SAVEPOINT + | SCHEMA + | SCHEMAS + | SCROLL + | SEARCH + | SECOND_P + | SECURITY + | SEQUENCE + | SEQUENCES + | SERIALIZABLE + | SERVER + | SESSION + | SET + | SETS + | SHARE + | SHOW + | SIMPLE + | SKIP_P + | SNAPSHOT + | SQL_P + | STABLE + | STANDALONE_P + | START + | STATEMENT + | STATISTICS + | STDIN + | STDOUT + | STORAGE + | STORED + | STRICT_P + | STRIP_P + | SUBSCRIPTION + | SUPPORT + | SYSID + | SYSTEM_P + | TABLES + | TABLESPACE + | TEMP + | TEMPLATE + | TEMPORARY + | TEXT_P + | TIES + | TRANSACTION + | TRANSFORM + | TRIGGER + | TRUNCATE + | TRUSTED + | TYPE_P + | TYPES_P + | UESCAPE + | UNBOUNDED + | UNCOMMITTED + | UNENCRYPTED + | UNKNOWN + | UNLISTEN + | UNLOGGED + | UNTIL + | UPDATE + | VACUUM + | VALID + | VALIDATE + | VALIDATOR + | VALUE_P + | VARYING + | VERSION_P + | VIEW + | VIEWS + | VOLATILE + | WHITESPACE_P + | WITHIN + | WITHOUT + | WORK + | WRAPPER + | WRITE + | XML_P + | YEAR_P + | YES_P + | ZONE + ; + +col_name_keyword + : BETWEEN + | BIGINT + | bit + | BOOLEAN_P + | CHAR_P + | character + | COALESCE + | DEC + | DECIMAL_P + | EXISTS + | EXTRACT + | FLOAT_P + | GREATEST + | GROUPING + | INOUT + | INT_P + | INTEGER + | INTERVAL + | LEAST + | NATIONAL + | NCHAR + | NONE + | NORMALIZE + | NULLIF + | numeric + | OUT_P + | OVERLAY + | POSITION + | PRECISION + | REAL + | ROW + | SETOF + | SMALLINT + | SUBSTRING + | TIME + | TIMESTAMP + | TREAT + | TRIM + | VALUES + | VARCHAR + | XMLATTRIBUTES + | XMLCONCAT + | XMLELEMENT + | XMLEXISTS + | XMLFOREST + | XMLNAMESPACES + | XMLPARSE + | XMLPI + | XMLROOT + | XMLSERIALIZE + | XMLTABLE + | builtin_function_name + ; + +type_func_name_keyword + : AUTHORIZATION + | BINARY + | COLLATION + | CONCURRENTLY + | CROSS + | CURRENT_SCHEMA + | FREEZE + | FULL + | ILIKE + | INNER_P + | IS + | ISNULL + | JOIN + | LIKE + | NATURAL + | NOTNULL + | OUTER_P + | OVERLAPS + | SIMILAR + | TABLESAMPLE + | VERBOSE + ; + +reserved_keyword + : ALL + | ANALYSE + | ANALYZE + | AND + | ANY + | ARRAY + | AS + | ASC + | ASYMMETRIC + | BOTH + | CASE + | CAST + | CHECK + | COLLATE + | COLUMN + | CONSTRAINT + | CREATE + | CURRENT_CATALOG + | CURRENT_DATE + | CURRENT_ROLE + | CURRENT_TIME + | CURRENT_TIMESTAMP + | CURRENT_USER + // | DEFAULT + | DEFERRABLE + | DESC + | DISTINCT + | DO + | ELSE + | END_P + | EXCEPT + | FALSE_P + | FETCH + | FOR + | FOREIGN + | FROM + | GRANT + | GROUP_P + | HAVING + | IN_P + | INITIALLY + | INTERSECT + /* +from pl_gram.y, line ~2982 + * Fortunately, INTO is a fully reserved word in the main grammar, so + * at least we need not worry about it appearing as an identifier. +*/ + + // | INTO + | LATERAL_P + | LEADING + | LIMIT + | LOCALTIME + | LOCALTIMESTAMP + | NOT + | NULL_P + | OFFSET + | ON + | ONLY + | OR + | ORDER + | PLACING + | PRIMARY + | REFERENCES + | RETURNING + | SELECT + | SESSION_USER + | SOME + | SYMMETRIC + | TABLE + | THEN + | TO + | TRAILING + | TRUE_P + | UNION + | UNIQUE + | USER + | USING + | VARIADIC + | WHEN + | WHERE + | WINDOW + | WITH + ; + +builtin_function_name + : XMLCOMMENT + | XML_IS_WELL_FORMED + | XML_IS_WELL_FORMED_DOCUMENT + | XML_IS_WELL_FORMED_CONTENT + | XMLAGG + | XPATH + | XPATH_EXISTS + | ABS + | CBRT + | CEIL + | CEILING + | DEGREES + | DIV + | EXP + | FACTORIAL + | FLOOR + | GCD + | LCM + | LN + | LOG + | LOG10 + | MIN_SCALE + | MOD + | PI + | POWER + | RADIANS + | ROUND + | SCALE + | SIGN + | SQRT + | TRIM_SCALE + | TRUNC + | WIDTH_BUCKET + | RANDOM + | SETSEED + | ACOS + | ACOSD + | ACOSH + | ASIN + | ASIND + | ASINH + | ATAN + | ATAND + | ATANH + | ATAN2 + | ATAN2D + | COS + | COSD + | COSH + | COT + | COTD + | SIN + | SIND + | SINH + | TAN + | TAND + | TANH + | BIT_LENGTH + | CHAR_LENGTH + | CHARACTER_LENGTH + | LOWER + | OCTET_LENGTH + | OCTET_LENGTH + | UPPER + | ASCII + | BTRIM + | CHR + | CONCAT + | CONCAT_WS + | FORMAT + | INITCAP + | LENGTH + | LPAD + | LTRIM + | MD5 + | PARSE_IDENT + | PG_CLIENT_ENCODING + | QUOTE_IDENT + | QUOTE_LITERAL + | QUOTE_NULLABLE + | REGEXP_COUNT + | REGEXP_INSTR + | REGEXP_LIKE + | REGEXP_MATCH + | REGEXP_MATCHES + | REGEXP_REPLACE + | REGEXP_SPLIT_TO_ARRAY + | REGEXP_SPLIT_TO_TABLE + | REGEXP_SUBSTR + | REPEAT + | REPLACE + | REVERSE + | RPAD + | RTRIM + | SPLIT_PART + | STARTS_WITH + | STRING_TO_ARRAY + | STRING_TO_TABLE + | STRPOS + | SUBSTR + | TO_ASCII + | TO_HEX + | TRANSLATE + | UNISTR + | AGE + | DATE_BIN + | DATE_PART + | DATE_TRUNC + | ISFINITE + | JUSTIFY_DAYS + | JUSTIFY_HOURS + | JUSTIFY_INTERVAL + | MAKE_DATE + | MAKE_INTERVAL + | MAKE_TIME + | MAKE_TIMESTAMP + | MAKE_TIMESTAMPTZ + | CLOCK_TIMESTAMP + | NOW + | STATEMENT_TIMESTAMP + | TIMEOFDAY + | TRANSACTION_TIMESTAMP + | TO_TIMESTAMP + | JUSTIFY_INTERVAL + | JUSTIFY_INTERVAL + | TO_CHAR + | TO_DATE + | TO_NUMBER + ; + +/************************************************************************************************************************************************************/ +/*PL/SQL GRAMMAR */ + +/*PLSQL grammar */ + +/************************************************************************************************************************************************************/ +pl_function + : comp_options pl_block opt_semi + ; + +comp_options + : comp_option* + ; + +comp_option + : sharp OPTION DUMP + | sharp PRINT_STRICT_PARAMS option_value + | sharp VARIABLE_CONFLICT ERROR + | sharp VARIABLE_CONFLICT USE_VARIABLE + | sharp VARIABLE_CONFLICT USE_COLUMN + ; + +sharp + : Operator + ; + +option_value + : sconst + | reserved_keyword + | plsql_unreserved_keyword + | unreserved_keyword + ; + +opt_semi + : + | SEMI + ; + +// exception_sect means opt_exception_sect in original grammar, don't be confused! + +pl_block + : decl_sect BEGIN_P proc_sect exception_sect END_P opt_label + ; + +decl_sect + : opt_block_label (decl_start decl_stmts?)? + ; + +decl_start + : DECLARE + ; + +decl_stmts + : decl_stmt+ + ; + +label_decl + : LESS_LESS any_identifier GREATER_GREATER + ; + +decl_stmt + : decl_statement + | DECLARE + | label_decl + ; + +decl_statement + : decl_varname ( + ALIAS FOR decl_aliasitem + | decl_const decl_datatype decl_collate decl_notnull decl_defval + | opt_scrollable CURSOR decl_cursor_args decl_is_for decl_cursor_query + ) SEMI + ; + +opt_scrollable + : + | NO SCROLL + | SCROLL + ; + +decl_cursor_query + : selectstmt + ; + +decl_cursor_args + : + | OPEN_PAREN decl_cursor_arglist CLOSE_PAREN + ; + +decl_cursor_arglist + : decl_cursor_arg (COMMA decl_cursor_arg)* + ; + +decl_cursor_arg + : decl_varname decl_datatype + ; + +decl_is_for + : IS + | FOR + ; + +decl_aliasitem + : PARAM + | colid + ; + +decl_varname + : any_identifier + ; + +decl_const + : + | CONSTANT + ; + +decl_datatype + : typename + ; //TODO: $$ = read_datatype(yychar); + +decl_collate + : + | COLLATE any_name + ; + +decl_notnull + : + | NOT NULL_P + ; + +decl_defval + : + | decl_defkey sql_expression + ; + +decl_defkey + : assign_operator + | DEFAULT + ; + +assign_operator + : EQUAL + | COLON_EQUALS + ; + +proc_sect + : proc_stmt* + ; + +proc_stmt + : pl_block SEMI + | stmt_return + | stmt_raise + | stmt_assign + | stmt_if + | stmt_case + | stmt_loop + | stmt_while + | stmt_for + | stmt_foreach_a + | stmt_exit + | stmt_assert + | stmt_execsql + | stmt_dynexecute + | stmt_perform + | stmt_call + | stmt_getdiag + | stmt_open + | stmt_fetch + | stmt_move + | stmt_close + | stmt_null + | stmt_commit + | stmt_rollback + | stmt_set + ; + +stmt_perform + : PERFORM expr_until_semi SEMI + ; + +stmt_call + : CALL any_identifier OPEN_PAREN opt_expr_list CLOSE_PAREN SEMI + | DO any_identifier OPEN_PAREN opt_expr_list CLOSE_PAREN SEMI + ; + +opt_expr_list + : + | expr_list + ; + +stmt_assign + : assign_var assign_operator sql_expression SEMI + ; + +stmt_getdiag + : GET getdiag_area_opt DIAGNOSTICS getdiag_list SEMI + ; + +getdiag_area_opt + : + | CURRENT_P + | STACKED + ; + +getdiag_list + : getdiag_list_item (COMMA getdiag_list_item)* + ; + +getdiag_list_item + : getdiag_target assign_operator getdiag_item + ; + +getdiag_item + : colid + ; + +getdiag_target + : assign_var + ; + +assign_var + : (any_name | PARAM) (OPEN_BRACKET expr_until_rightbracket CLOSE_BRACKET)* + ; + +stmt_if + : IF_P expr_until_then THEN proc_sect stmt_elsifs stmt_else END_P IF_P SEMI + ; + +stmt_elsifs + : (ELSIF a_expr THEN proc_sect)* + ; + +stmt_else + : + | ELSE proc_sect + ; + +stmt_case + : CASE opt_expr_until_when case_when_list opt_case_else END_P CASE SEMI + ; + +opt_expr_until_when + : + | sql_expression + ; + +case_when_list + : case_when+ + ; + +case_when + : WHEN expr_list THEN proc_sect + ; + +opt_case_else + : + | ELSE proc_sect + ; + +stmt_loop + : opt_loop_label loop_body + ; + +stmt_while + : opt_loop_label WHILE expr_until_loop loop_body + ; + +stmt_for + : opt_loop_label FOR for_control loop_body + ; + +//TODO: rewrite using read_sql_expression logic? + +for_control + : for_variable IN_P ( + cursor_name opt_cursor_parameters + | selectstmt + | explainstmt + | EXECUTE a_expr opt_for_using_expression + | opt_reverse a_expr DOT_DOT a_expr opt_by_expression + ) + ; + +opt_for_using_expression + : + | USING expr_list + ; + +opt_cursor_parameters + : + | OPEN_PAREN a_expr (COMMA a_expr)* CLOSE_PAREN + ; + +opt_reverse + : + | REVERSE + ; + +opt_by_expression + : + | BY a_expr + ; + +for_variable + : any_name_list + ; + +stmt_foreach_a + : opt_loop_label FOREACH for_variable foreach_slice IN_P ARRAY a_expr loop_body + ; + +foreach_slice + : + | SLICE iconst + ; + +stmt_exit + : exit_type opt_label opt_exitcond SEMI + ; + +exit_type + : EXIT + | CONTINUE_P + ; + +//todo implement RETURN statement according to initial grammar line 1754 + +stmt_return + : RETURN ( + NEXT sql_expression + | QUERY (EXECUTE a_expr opt_for_using_expression | selectstmt) + | opt_return_result + ) SEMI + ; + +opt_return_result + : + | sql_expression + ; + +//https://www.postgresql.org/docs/current/plpgsql-errors-and-messages.html + +//RAISE [ level ] 'format' [, expression [, ... ]] [ USING option = expression [, ... ] ]; + +//RAISE [ level ] condition_name [ USING option = expression [, ... ] ]; + +//RAISE [ level ] SQLSTATE 'sqlstate' [ USING option = expression [, ... ] ]; + +//RAISE [ level ] USING option = expression [, ... ]; + +//RAISE ; + +stmt_raise + : RAISE opt_stmt_raise_level sconst opt_raise_list opt_raise_using SEMI + | RAISE opt_stmt_raise_level identifier opt_raise_using SEMI + | RAISE opt_stmt_raise_level SQLSTATE sconst opt_raise_using SEMI + | RAISE opt_stmt_raise_level opt_raise_using SEMI + | RAISE + ; + +opt_stmt_raise_level + : + | + | DEBUG + | LOG + | INFO + | NOTICE + | WARNING + | EXCEPTION + ; + +opt_raise_list + : + | (COMMA a_expr)+ + ; + +opt_raise_using + : + | USING opt_raise_using_elem_list + ; + +opt_raise_using_elem + : identifier EQUAL a_expr + ; + +opt_raise_using_elem_list + : opt_raise_using_elem (COMMA opt_raise_using_elem)* + ; + +//todo imnplement + +stmt_assert + : ASSERT sql_expression opt_stmt_assert_message SEMI + ; + +opt_stmt_assert_message + : + | COMMA sql_expression + ; + +loop_body + : LOOP proc_sect END_P LOOP opt_label SEMI + ; + +//TODO: looks like all other statements like INSERT/SELECT/UPDATE/DELETE are handled here; + +//pls take a look at original grammar + +stmt_execsql + : make_execsql_stmt SEMI + /*K_IMPORT + | K_INSERT + | t_word + | t_cword +*/ + ; + +//https://www.postgresql.org/docs/current/plpgsql-statements.html#PLPGSQL-STATEMENTS-SQL-NORESULT + +//EXECUTE command-string [ INTO [STRICT] target ] [ USING expression [, ... ] ]; + +stmt_dynexecute + : EXECUTE a_expr ( + /*this is silly, but i have to time to find nice way to code */ opt_execute_into opt_execute_using + | opt_execute_using opt_execute_into + | + ) SEMI + ; + +opt_execute_using + : + | USING opt_execute_using_list + ; + +opt_execute_using_list + : a_expr (COMMA a_expr)* + ; + +opt_execute_into + : + | INTO STRICT_P? into_target + ; + +//https://www.postgresql.org/docs/current/plpgsql-cursors.html#PLPGSQL-CURSOR-OPENING + +//OPEN unbound_cursorvar [ [ NO ] SCROLL ] FOR query; + +//OPEN unbound_cursorvar [ [ NO ] SCROLL ] FOR EXECUTE query_string + +// [ USING expression [, ... ] ]; + +//OPEN bound_cursorvar [ ( [ argument_name := ] argument_value [, ...] ) ]; + +stmt_open + : OPEN ( + cursor_variable opt_scroll_option FOR (selectstmt | EXECUTE sql_expression opt_open_using) + | colid (OPEN_PAREN opt_open_bound_list CLOSE_PAREN)? + ) SEMI + ; + +opt_open_bound_list_item + : colid COLON_EQUALS a_expr + | a_expr + ; + +opt_open_bound_list + : opt_open_bound_list_item (COMMA opt_open_bound_list_item)* + ; + +opt_open_using + : + | USING expr_list + ; + +opt_scroll_option + : + | opt_scroll_option_no SCROLL + ; + +opt_scroll_option_no + : + | NO + ; + +//https://www.postgresql.org/docs/current/plpgsql-cursors.html#PLPGSQL-CURSOR-OPENING + +//FETCH [ direction { FROM | IN } ] cursor INTO target; + +stmt_fetch + : FETCH direction = opt_fetch_direction opt_cursor_from cursor_variable INTO into_target SEMI + ; + +into_target + : expr_list + ; + +opt_cursor_from + : + | FROM + | IN_P + ; + +opt_fetch_direction + : + | + | NEXT + | PRIOR + | FIRST_P + | LAST_P + | ABSOLUTE_P a_expr + | RELATIVE_P a_expr + | a_expr + | ALL + | (FORWARD | BACKWARD) (a_expr | ALL)? + ; + +//https://www.postgresql.org/docs/current/plpgsql-cursors.html#PLPGSQL-CURSOR-OPENING + +//MOVE [ direction { FROM | IN } ] cursor; + +stmt_move + : MOVE opt_fetch_direction cursor_variable SEMI + ; + +stmt_close + : CLOSE cursor_variable SEMI + ; + +stmt_null + : NULL_P SEMI + ; + +stmt_commit + : COMMIT plsql_opt_transaction_chain SEMI + ; + +stmt_rollback + : ROLLBACK plsql_opt_transaction_chain SEMI + ; + +plsql_opt_transaction_chain + : AND NO? CHAIN + | + ; + +stmt_set + : SET any_name TO DEFAULT SEMI + | RESET (any_name | ALL) SEMI + ; + +cursor_variable + : colid + | PARAM + ; + +exception_sect + : + | EXCEPTION proc_exceptions + ; + +proc_exceptions + : proc_exception+ + ; + +proc_exception + : WHEN proc_conditions THEN proc_sect + ; + +proc_conditions + : proc_condition (OR proc_condition)* + ; + +proc_condition + : any_identifier + | SQLSTATE sconst + ; + +//expr_until_semi: + +//; + +//expr_until_rightbracket: + +//; + +//expr_until_loop: + +//; + +opt_block_label + : + | label_decl + ; + +opt_loop_label + : + | label_decl + ; + +opt_label + : + | any_identifier + ; + +opt_exitcond + : WHEN expr_until_semi + | + ; + +any_identifier + : colid + | plsql_unreserved_keyword + ; + +plsql_unreserved_keyword + : ABSOLUTE_P + | ALIAS + | AND + | ARRAY + | ASSERT + | BACKWARD + | CALL + | CHAIN + | CLOSE + | COLLATE + | COLUMN + //| COLUMN_NAME + | COMMIT + | CONSTANT + | CONSTRAINT + //| CONSTRAINT_NAME + | CONTINUE_P + | CURRENT_P + | CURSOR + //| DATATYPE + | DEBUG + | DEFAULT + //| DETAIL + | DIAGNOSTICS + | DO + | DUMP + | ELSIF + //| ERRCODE + | ERROR + | EXCEPTION + | EXIT + | FETCH + | FIRST_P + | FORWARD + | GET + //| HINT + + //| IMPORT + | INFO + | INSERT + | IS + | LAST_P + //| MESSAGE + + //| MESSAGE_TEXT + | MOVE + | NEXT + | NO + | NOTICE + | OPEN + | OPTION + | PERFORM + //| PG_CONTEXT + + //| PG_DATATYPE_NAME + + //| PG_EXCEPTION_CONTEXT + + //| PG_EXCEPTION_DETAIL + + //| PG_EXCEPTION_HINT + | PRINT_STRICT_PARAMS + | PRIOR + | QUERY + | RAISE + | RELATIVE_P + | RESET + | RETURN + //| RETURNED_SQLSTATE + | ROLLBACK + //| ROW_COUNT + | ROWTYPE + | SCHEMA + //| SCHEMA_NAME + | SCROLL + | SET + | SLICE + | SQLSTATE + | STACKED + | TABLE + //| TABLE_NAME + | TYPE_P + | USE_COLUMN + | USE_VARIABLE + | VARIABLE_CONFLICT + | WARNING + | OUTER_P + ; + +sql_expression + : opt_target_list into_clause from_clause where_clause group_clause having_clause window_clause + ; + +expr_until_then + : sql_expression + ; + +expr_until_semi + : sql_expression + ; + +expr_until_rightbracket + : a_expr + ; + +expr_until_loop + : a_expr + ; + +make_execsql_stmt + : stmt opt_returning_clause_into + ; + +opt_returning_clause_into + : INTO opt_strict into_target + | + ; diff --git a/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/LexerDispatchingErrorListener.java b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/LexerDispatchingErrorListener.java new file mode 100644 index 0000000000..b9a7168cd9 --- /dev/null +++ b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/LexerDispatchingErrorListener.java @@ -0,0 +1,86 @@ +/* + * Copyright 2021-2023 Aklivity Inc + * + * Licensed under the Aklivity Community License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of the + * License at + * + * https://www.aklivity.io/aklivity-community-license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.aklivity.zilla.runtime.binding.pgsql.parser; + +import java.util.BitSet; + +import org.antlr.v4.runtime.ANTLRErrorListener; +import org.antlr.v4.runtime.Lexer; +import org.antlr.v4.runtime.Parser; +import org.antlr.v4.runtime.ProxyErrorListener; +import org.antlr.v4.runtime.RecognitionException; +import org.antlr.v4.runtime.Recognizer; +import org.antlr.v4.runtime.atn.ATNConfigSet; +import org.antlr.v4.runtime.dfa.DFA; + +public class LexerDispatchingErrorListener implements ANTLRErrorListener +{ + Lexer parent; + + public LexerDispatchingErrorListener( + Lexer parent) + { + this.parent = parent; + } + + public void syntaxError( + Recognizer recognizer, + Object offendingSymbol, + int line, + int charPositionInLine, + String msg, + RecognitionException e) + { + ProxyErrorListener foo = new ProxyErrorListener(parent.getErrorListeners()); + foo.syntaxError(recognizer, offendingSymbol, line, charPositionInLine, msg, e); + } + + public void reportAmbiguity( + Parser recognizer, + DFA dfa, + int startIndex, + int stopIndex, + boolean exact, + BitSet ambigAlts, + ATNConfigSet configs) + { + ProxyErrorListener proxyError = new ProxyErrorListener(parent.getErrorListeners()); + proxyError.reportAmbiguity(recognizer, dfa, startIndex, stopIndex, exact, ambigAlts, configs); + } + + public void reportAttemptingFullContext( + Parser recognizer, + DFA dfa, + int startIndex, + int stopIndex, + BitSet conflictingAlts, + ATNConfigSet configs) + { + ProxyErrorListener proxyError = new ProxyErrorListener(parent.getErrorListeners()); + proxyError.reportAttemptingFullContext(recognizer, dfa, startIndex, stopIndex, conflictingAlts, configs); + } + + public void reportContextSensitivity( + Parser recognizer, + DFA dfa, + int startIndex, + int stopIndex, + int prediction, + ATNConfigSet configs) + { + ProxyErrorListener proxyError = new ProxyErrorListener(parent.getErrorListeners()); + proxyError.reportContextSensitivity(recognizer, dfa, startIndex, stopIndex, prediction, configs); + } +} diff --git a/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/ParserDispatchingErrorListener.java b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/ParserDispatchingErrorListener.java new file mode 100644 index 0000000000..3531e2c33f --- /dev/null +++ b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/ParserDispatchingErrorListener.java @@ -0,0 +1,86 @@ +/* + * Copyright 2021-2023 Aklivity Inc + * + * Licensed under the Aklivity Community License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of the + * License at + * + * https://www.aklivity.io/aklivity-community-license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.aklivity.zilla.runtime.binding.pgsql.parser; + +import java.util.BitSet; + +import org.antlr.v4.runtime.ANTLRErrorListener; +import org.antlr.v4.runtime.Parser; +import org.antlr.v4.runtime.ProxyErrorListener; +import org.antlr.v4.runtime.RecognitionException; +import org.antlr.v4.runtime.Recognizer; +import org.antlr.v4.runtime.atn.ATNConfigSet; +import org.antlr.v4.runtime.dfa.DFA; + + +public class ParserDispatchingErrorListener implements ANTLRErrorListener +{ + @SuppressWarnings("checkstyle:MemberName") + Parser parent; + + public ParserDispatchingErrorListener( + Parser parent) + { + this.parent = parent; + } + + public void syntaxError( + Recognizer recognizer, Object offendingSymbol, + int line, + int charPositionInLine, + String msg, + RecognitionException e) + { + var foo = new ProxyErrorListener(parent.getErrorListeners()); + foo.syntaxError(recognizer, offendingSymbol, line, charPositionInLine, msg, e); + } + + public void reportAmbiguity( + Parser recognizer, + DFA dfa, + int startIndex, + int stopIndex, + boolean exact, + BitSet ambigAlts, + ATNConfigSet configs) + { + ProxyErrorListener foo = new ProxyErrorListener(parent.getErrorListeners()); + foo.reportAmbiguity(recognizer, dfa, startIndex, stopIndex, exact, ambigAlts, configs); + } + + public void reportAttemptingFullContext( + Parser recognizer, + DFA dfa, + int startIndex, + int stopIndex, + BitSet conflictingAlts, + ATNConfigSet configs) + { + ProxyErrorListener foo = new ProxyErrorListener(parent.getErrorListeners()); + foo.reportAttemptingFullContext(recognizer, dfa, startIndex, stopIndex, conflictingAlts, configs); + } + + public void reportContextSensitivity( + Parser recognizer, + DFA dfa, + int startIndex, + int stopIndex, + int prediction, + ATNConfigSet configs) + { + ProxyErrorListener foo = new ProxyErrorListener(parent.getErrorListeners()); + foo.reportContextSensitivity(recognizer, dfa, startIndex, stopIndex, prediction, configs); + } +} diff --git a/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/PgsqlParser.java b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/PgsqlParser.java new file mode 100644 index 0000000000..0479aa08a8 --- /dev/null +++ b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/PgsqlParser.java @@ -0,0 +1,123 @@ +/* + * Copyright 2021-2023 Aklivity Inc + * + * Licensed under the Aklivity Community License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of the + * License at + * + * https://www.aklivity.io/aklivity-community-license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.aklivity.zilla.runtime.binding.pgsql.parser; + +import java.util.List; + +import org.antlr.v4.runtime.BailErrorStrategy; +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.tree.ParseTreeWalker; + +import io.aklivity.zilla.runtime.binding.pgsql.parser.listener.SqlCommandListener; +import io.aklivity.zilla.runtime.binding.pgsql.parser.listener.SqlCreateFunctionListener; +import io.aklivity.zilla.runtime.binding.pgsql.parser.listener.SqlCreateMaterializedViewListener; +import io.aklivity.zilla.runtime.binding.pgsql.parser.listener.SqlCreateStreamListener; +import io.aklivity.zilla.runtime.binding.pgsql.parser.listener.SqlCreateTableTopicListener; +import io.aklivity.zilla.runtime.binding.pgsql.parser.listener.SqlDropListener; +import io.aklivity.zilla.runtime.binding.pgsql.parser.model.FunctionInfo; +import io.aklivity.zilla.runtime.binding.pgsql.parser.model.StreamInfo; +import io.aklivity.zilla.runtime.binding.pgsql.parser.model.TableInfo; +import io.aklivity.zilla.runtime.binding.pgsql.parser.model.ViewInfo; + +public final class PgsqlParser +{ + private final ParseTreeWalker walker; + private final BailErrorStrategy errorStrategy; + private final PostgreSqlLexer lexer; + private final CommonTokenStream tokens; + private final PostgreSqlParser parser; + private final SqlCommandListener commandListener; + private final SqlCreateStreamListener createStreamListener; + private final SqlCreateTableTopicListener createTableListener; + private final SqlCreateFunctionListener createFunctionListener; + private final SqlCreateMaterializedViewListener createMaterializedViewListener; + private final SqlDropListener dropListener; + + public PgsqlParser() + { + this.walker = new ParseTreeWalker(); + this.errorStrategy = new BailErrorStrategy(); + this.lexer = new PostgreSqlLexer(null); + this.tokens = new CommonTokenStream(lexer); + this.parser = new PostgreSqlParser(tokens); + this.commandListener = new SqlCommandListener(); + this.createTableListener = new SqlCreateTableTopicListener(tokens); + this.createStreamListener = new SqlCreateStreamListener(tokens); + this.createFunctionListener = new SqlCreateFunctionListener(tokens); + this.createMaterializedViewListener = new SqlCreateMaterializedViewListener(tokens); + this.dropListener = new SqlDropListener(); + parser.setErrorHandler(errorStrategy); + } + + public String parseCommand( + String sql) + { + parser(sql, commandListener); + return commandListener.command(); + } + + public TableInfo parseCreateTable( + String sql) + { + parser(sql, createTableListener); + return createTableListener.tableInfo(); + } + + public StreamInfo parseCreateStream( + String sql) + { + parser(sql, createStreamListener); + return createStreamListener.streamInfo(); + } + + public FunctionInfo parseCreateFunction( + String sql) + { + parser(sql, createFunctionListener); + return createFunctionListener.functionInfo(); + } + + public ViewInfo parseCreateMaterializedView( + String sql) + { + parser(sql, createMaterializedViewListener); + return createMaterializedViewListener.viewInfo(); + } + + public List parseDrop( + String sql) + { + parser(sql, dropListener); + return dropListener.drops(); + } + + private void parser( + String sql, + PostgreSqlParserBaseListener listener) + { + sql = sql.replace("\u0000", ""); + + CharStream input = CharStreams.fromString(sql); + lexer.reset(); + lexer.setInputStream(input); + + tokens.setTokenSource(lexer); + parser.setTokenStream(tokens); + + walker.walk(listener, parser.root()); + } +} diff --git a/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/PostgreSqlLexerBase.java b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/PostgreSqlLexerBase.java new file mode 100644 index 0000000000..59295577b9 --- /dev/null +++ b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/PostgreSqlLexerBase.java @@ -0,0 +1,95 @@ +/* + * Copyright 2021-2023 Aklivity Inc + * + * Licensed under the Aklivity Community License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of the + * License at + * + * https://www.aklivity.io/aklivity-community-license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.aklivity.zilla.runtime.binding.pgsql.parser; + +import java.util.ArrayDeque; +import java.util.Deque; + +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.Lexer; + +public abstract class PostgreSqlLexerBase extends Lexer +{ + protected final Deque tags = new ArrayDeque<>(); + + protected PostgreSqlLexerBase(CharStream input) + { + super(input); + } + + public void pushTag() + { + tags.push(getText()); + } + + public boolean isTag() + { + return getText().equals(tags.peek()); + } + + public void popTag() + { + tags.pop(); + } + + public boolean checkLA(int c) + { + return getInputStream().LA(1) != c; + } + + public boolean charIsLetter() + { + return Character.isLetter(getInputStream().LA(-1)); + } + + public void handleNumericFail() + { + getInputStream().seek(getInputStream().index() - 2); + setType(PostgreSqlLexer.Integral); + } + + public void handleLessLessGreaterGreater() + { + if ("<<".equals(getText())) + { + setType(PostgreSqlLexer.LESS_LESS); + } + if (">>".equals(getText())) + { + setType(PostgreSqlLexer.GREATER_GREATER); + } + } + + public void unterminatedBlockCommentDebugAssert() + { + //Debug.Assert(InputStream.LA(1) == -1 /*EOF*/); + } + + public boolean checkIfUtf32Letter() + { + int codePoint = getInputStream().LA(-2) << 8 + getInputStream().LA(-1); + char[] c; + if (codePoint < 0x10000) + { + c = new char[]{(char) codePoint}; + } + else + { + codePoint -= 0x10000; + c = new char[]{(char) (codePoint / 0x400 + 0xd800), (char) (codePoint % 0x400 + 0xdc00)}; + } + return Character.isLetter(c[0]); + } +} diff --git a/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/PostgreSqlParserBase.java b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/PostgreSqlParserBase.java new file mode 100644 index 0000000000..71f927759e --- /dev/null +++ b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/PostgreSqlParserBase.java @@ -0,0 +1,171 @@ +/* + * Copyright 2021-2023 Aklivity Inc + * + * Licensed under the Aklivity Community License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of the + * License at + * + * https://www.aklivity.io/aklivity-community-license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.aklivity.zilla.runtime.binding.pgsql.parser; + +import java.util.List; + +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.Lexer; +import org.antlr.v4.runtime.Parser; +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.TokenStream; + +public abstract class PostgreSqlParserBase extends Parser +{ + + public PostgreSqlParserBase(TokenStream input) + { + super(input); + } + + ParserRuleContext getParsedSqlTree( + String script, + int line) + { + PostgreSqlParser ph = getPostgreSqlParser(script); + ParserRuleContext result = ph.root(); + return result; + } + + public void parseRoutineBody( + PostgreSqlParser.Createfunc_opt_listContext localctx) + { + String lang = null; + for (PostgreSqlParser.Createfunc_opt_itemContext coi : localctx.createfunc_opt_item()) + { + if (coi.LANGUAGE() != null) + { + if (coi.nonreservedword_or_sconst() != null) + { + if (coi.nonreservedword_or_sconst().sconst() != null) + { + lang = coi.nonreservedword_or_sconst().sconst().getText(); + break; + } + if (coi.nonreservedword_or_sconst().nonreservedword() != null) + { + if (coi.nonreservedword_or_sconst().nonreservedword().identifier() != null) + { + if (coi.nonreservedword_or_sconst().nonreservedword().identifier().Identifier() != null) + { + lang = coi.nonreservedword_or_sconst().nonreservedword().identifier().Identifier().getText(); + break; + } + } + } + } + } + } + if (null == lang) + { + return; + } + PostgreSqlParser.Createfunc_opt_itemContext funcAs = null; + for (PostgreSqlParser.Createfunc_opt_itemContext a : localctx.createfunc_opt_item()) + { + if (a.func_as() != null) + { + funcAs = a; + break; + } + + } + if (funcAs != null) + { + String txt = getRoutineBodyString(funcAs.func_as().sconst(0)); + PostgreSqlParser ph = getPostgreSqlParser(txt); + switch (lang) + { + case "plpgsql": + funcAs.func_as().Definition = ph.plsqlroot(); + break; + case "sql": + funcAs.func_as().Definition = ph.root(); + break; + } + } + } + + private String trimQuotes(String s) + { + return (s == null || s.isEmpty()) ? s : s.substring(1, s.length() - 1); + } + + public String unquote( + String s) + { + int slength = s.length(); + StringBuilder r = new StringBuilder(slength); + int i = 0; + while (i < slength) + { + Character c = s.charAt(i); + r.append(c); + if (c == '\'' && i < slength - 1 && s.charAt(i + 1) == '\'') + { + i++; + } + i++; + } + return r.toString(); + } + + public String getRoutineBodyString( + PostgreSqlParser.SconstContext rule) + { + PostgreSqlParser.AnysconstContext anysconst = rule.anysconst(); + org.antlr.v4.runtime.tree.TerminalNode stringConstant = anysconst.StringConstant(); + if (null != stringConstant) + { + return trimQuotes(stringConstant.getText()); + } + org.antlr.v4.runtime.tree.TerminalNode unicodeEscapeStringConstant = anysconst.UnicodeEscapeStringConstant(); + if (null != unicodeEscapeStringConstant) + { + return unquote(unicodeEscapeStringConstant.getText()); + } + org.antlr.v4.runtime.tree.TerminalNode escapeStringConstant = anysconst.EscapeStringConstant(); + if (null != escapeStringConstant) + { + return unquote(escapeStringConstant.getText()); + } + String result = ""; + List dollartext = anysconst.DollarText(); + for (org.antlr.v4.runtime.tree.TerminalNode s : dollartext) + { + result += s.getText(); + } + return result; + } + + public PostgreSqlParser getPostgreSqlParser( + String script) + { + CharStream charStream = CharStreams.fromString(script); + Lexer lexer = new PostgreSqlLexer(charStream); + CommonTokenStream tokens = new CommonTokenStream(lexer); + PostgreSqlParser parser = new PostgreSqlParser(tokens); + lexer.removeErrorListeners(); + parser.removeErrorListeners(); + LexerDispatchingErrorListener listenerLexer = + new LexerDispatchingErrorListener((Lexer)(this.getInputStream().getTokenSource())); + ParserDispatchingErrorListener listenerParser = new ParserDispatchingErrorListener(this); + lexer.addErrorListener(listenerLexer); + parser.addErrorListener(listenerParser); + return parser; + } +} diff --git a/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/listener/SqlCommandListener.java b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/listener/SqlCommandListener.java new file mode 100644 index 0000000000..ce9f2ce2ce --- /dev/null +++ b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/listener/SqlCommandListener.java @@ -0,0 +1,75 @@ +/* + * Copyright 2021-2023 Aklivity Inc + * + * Licensed under the Aklivity Community License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of the + * License at + * + * https://www.aklivity.io/aklivity-community-license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.aklivity.zilla.runtime.binding.pgsql.parser.listener; + +import io.aklivity.zilla.runtime.binding.pgsql.parser.PostgreSqlParser; +import io.aklivity.zilla.runtime.binding.pgsql.parser.PostgreSqlParserBaseListener; + +public class SqlCommandListener extends PostgreSqlParserBaseListener +{ + private String command = ""; + + public String command() + { + return command; + } + + @Override + public void enterRoot( + PostgreSqlParser.RootContext ctx) + { + command = ""; + } + + @Override + public void enterCreatestmt( + PostgreSqlParser.CreatestmtContext ctx) + { + command = "CREATE %s".formatted(ctx.opttable_type().getText()); + } + + @Override + public void enterCreatestreamstmt( + PostgreSqlParser.CreatestreamstmtContext ctx) + { + command = "CREATE STREAM"; + } + + @Override + public void enterCreatematviewstmt( + PostgreSqlParser.CreatematviewstmtContext ctx) + { + command = "CREATE MATERIALIZED VIEW"; + } + + @Override + public void enterCreatefunctionstmt( + PostgreSqlParser.CreatefunctionstmtContext ctx) + { + String functionBody = ctx.getText(); + + if (!functionBody.contains("$$")) + { + command = "CREATE FUNCTION"; + } + } + + @Override + public void enterDropstmt( + PostgreSqlParser.DropstmtContext ctx) + { + command = "DROP %s".formatted(ctx.object_type_any_name().getText()); + } +} diff --git a/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/listener/SqlCreateFunctionListener.java b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/listener/SqlCreateFunctionListener.java new file mode 100644 index 0000000000..85c1a98127 --- /dev/null +++ b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/listener/SqlCreateFunctionListener.java @@ -0,0 +1,107 @@ +/* + * Copyright 2021-2023 Aklivity Inc + * + * Licensed under the Aklivity Community License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of the + * License at + * + * https://www.aklivity.io/aklivity-community-license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.aklivity.zilla.runtime.binding.pgsql.parser.listener; + +import java.util.ArrayList; +import java.util.List; + +import org.antlr.v4.runtime.TokenStream; + +import io.aklivity.zilla.runtime.binding.pgsql.parser.PostgreSqlParser; +import io.aklivity.zilla.runtime.binding.pgsql.parser.PostgreSqlParserBaseListener; +import io.aklivity.zilla.runtime.binding.pgsql.parser.model.FunctionArgument; +import io.aklivity.zilla.runtime.binding.pgsql.parser.model.FunctionInfo; + +public class SqlCreateFunctionListener extends PostgreSqlParserBaseListener +{ + private final List arguments = new ArrayList<>(); + private final List tables = new ArrayList<>(); + + private final TokenStream tokens; + + private String name; + private String returnType; + private String asFunction; + private String language; + + public SqlCreateFunctionListener( + TokenStream tokens) + { + this.tokens = tokens; + } + + public FunctionInfo functionInfo() + { + return new FunctionInfo(name, arguments, returnType, tables, asFunction, language); + } + + @Override + public void enterRoot( + PostgreSqlParser.RootContext ctx) + { + name = null; + returnType = null; + asFunction = null; + language = null; + arguments.clear(); + tables.clear(); + } + + @Override + public void enterCreatefunctionstmt( + PostgreSqlParser.CreatefunctionstmtContext ctx) + { + name = ctx.func_name().getText(); + } + + @Override + public void enterFunc_arg( + PostgreSqlParser.Func_argContext ctx) + { + String argName = ctx.param_name() != null ? ctx.param_name().getText() : null; + String argType = tokens.getText(ctx.func_type()); + arguments.add(new FunctionArgument(argName, argType)); + } + + @Override + public void enterTable_func_column( + PostgreSqlParser.Table_func_columnContext ctx) + { + String argName = ctx.param_name() != null ? ctx.param_name().getText() : null; + String argType = ctx.func_type().getText(); + tables.add(new FunctionArgument(argName, argType)); + } + + @Override + public void enterFunc_type( + PostgreSqlParser.Func_typeContext ctx) + { + returnType = tokens.getText(ctx.typename()); + } + + @Override + public void enterFunc_as( + PostgreSqlParser.Func_asContext ctx) + { + asFunction = ctx.getText(); + } + + @Override + public void enterNonreservedword_or_sconst( + PostgreSqlParser.Nonreservedword_or_sconstContext ctx) + { + language = ctx.getText(); + } +} diff --git a/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/listener/SqlCreateMaterializedViewListener.java b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/listener/SqlCreateMaterializedViewListener.java new file mode 100644 index 0000000000..f4e8c5d5a8 --- /dev/null +++ b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/listener/SqlCreateMaterializedViewListener.java @@ -0,0 +1,62 @@ +/* + * Copyright 2021-2023 Aklivity Inc + * + * Licensed under the Aklivity Community License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of the + * License at + * + * https://www.aklivity.io/aklivity-community-license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.aklivity.zilla.runtime.binding.pgsql.parser.listener; + +import org.antlr.v4.runtime.TokenStream; + +import io.aklivity.zilla.runtime.binding.pgsql.parser.PostgreSqlParser; +import io.aklivity.zilla.runtime.binding.pgsql.parser.PostgreSqlParserBaseListener; +import io.aklivity.zilla.runtime.binding.pgsql.parser.model.ViewInfo; + +public class SqlCreateMaterializedViewListener extends PostgreSqlParserBaseListener +{ + private final TokenStream tokens; + + private String name; + private String select; + + public SqlCreateMaterializedViewListener( + TokenStream tokens) + { + this.tokens = tokens; + } + + public ViewInfo viewInfo() + { + return new ViewInfo(name, select); + } + + @Override + public void enterRoot( + PostgreSqlParser.RootContext ctx) + { + name = null; + select = null; + } + + @Override + public void enterCreatematviewstmt( + PostgreSqlParser.CreatematviewstmtContext ctx) + { + name = ctx.create_mv_target().qualified_name().getText(); + } + + @Override + public void enterSelectstmt( + PostgreSqlParser.SelectstmtContext ctx) + { + select = tokens.getText(ctx); + } +} diff --git a/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/listener/SqlCreateStreamListener.java b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/listener/SqlCreateStreamListener.java new file mode 100644 index 0000000000..34a2638676 --- /dev/null +++ b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/listener/SqlCreateStreamListener.java @@ -0,0 +1,73 @@ +/* + * Copyright 2021-2023 Aklivity Inc + * + * Licensed under the Aklivity Community License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of the + * License at + * + * https://www.aklivity.io/aklivity-community-license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.aklivity.zilla.runtime.binding.pgsql.parser.listener; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.antlr.v4.runtime.TokenStream; + +import io.aklivity.zilla.runtime.binding.pgsql.parser.PostgreSqlParser; +import io.aklivity.zilla.runtime.binding.pgsql.parser.PostgreSqlParserBaseListener; +import io.aklivity.zilla.runtime.binding.pgsql.parser.model.StreamInfo; + +public class SqlCreateStreamListener extends PostgreSqlParserBaseListener +{ + private final Map columns = new LinkedHashMap<>(); + private final TokenStream tokens; + + private String name; + + public SqlCreateStreamListener( + TokenStream tokens) + { + this.tokens = tokens; + } + + public StreamInfo streamInfo() + { + return new StreamInfo(name, columns); + } + + @Override + public void enterRoot( + PostgreSqlParser.RootContext ctx) + { + name = null; + columns.clear(); + } + + @Override + public void enterQualified_name( + PostgreSqlParser.Qualified_nameContext ctx) + { + name = ctx.getText(); + } + + @Override + public void enterCreatestreamstmt( + PostgreSqlParser.CreatestreamstmtContext ctx) + { + if (ctx.stream_columns() != null) + { + for (PostgreSqlParser.Stream_columnContext streamElement : ctx.stream_columns().stream_column()) + { + String columnName = streamElement.colid().getText(); + String dataType = tokens.getText(streamElement.typename()); + columns.put(columnName, dataType); + } + } + } +} diff --git a/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/listener/SqlCreateTableTopicListener.java b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/listener/SqlCreateTableTopicListener.java new file mode 100644 index 0000000000..9ec17a7243 --- /dev/null +++ b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/listener/SqlCreateTableTopicListener.java @@ -0,0 +1,99 @@ +/* + * Copyright 2021-2023 Aklivity Inc + * + * Licensed under the Aklivity Community License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of the + * License at + * + * https://www.aklivity.io/aklivity-community-license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.aklivity.zilla.runtime.binding.pgsql.parser.listener; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +import org.agrona.collections.ObjectHashSet; +import org.antlr.v4.runtime.TokenStream; + +import io.aklivity.zilla.runtime.binding.pgsql.parser.PostgreSqlParser; +import io.aklivity.zilla.runtime.binding.pgsql.parser.PostgreSqlParserBaseListener; +import io.aklivity.zilla.runtime.binding.pgsql.parser.model.TableInfo; + +public class SqlCreateTableTopicListener extends PostgreSqlParserBaseListener +{ + private final Map columns = new LinkedHashMap<>(); + private final Set primaryKeys = new ObjectHashSet<>(); + private final TokenStream tokens; + + private String name; + + public SqlCreateTableTopicListener( + TokenStream tokens) + { + this.tokens = tokens; + } + + public TableInfo tableInfo() + { + return new TableInfo(name, columns, primaryKeys); + } + + @Override + public void enterRoot( + PostgreSqlParser.RootContext ctx) + { + name = null; + columns.clear(); + primaryKeys.clear(); + } + + @Override + public void enterQualified_name( + PostgreSqlParser.Qualified_nameContext ctx) + { + name = ctx.getText(); + } + + @Override + public void enterCreatestmt( + PostgreSqlParser.CreatestmtContext ctx) + { + if (ctx.opttableelementlist().tableelementlist() != null) + { + for (PostgreSqlParser.TableelementContext tableElement : ctx.opttableelementlist().tableelementlist().tableelement()) + { + if (tableElement.columnDef() != null) + { + String columnName = tableElement.columnDef().colid().getText(); + String dataType = tokens.getText(tableElement.columnDef().typename()); + columns.put(columnName, dataType); + + for (PostgreSqlParser.ColconstraintContext constraint : + tableElement.columnDef().colquallist().colconstraint()) + { + if (constraint.colconstraintelem().PRIMARY() != null && + constraint.colconstraintelem().KEY() != null) + { + primaryKeys.add(columnName); + } + } + } + else if (tableElement.tableconstraint() != null) + { + if (tableElement.tableconstraint().constraintelem().PRIMARY() != null && + tableElement.tableconstraint().constraintelem().KEY() != null) + { + tableElement.tableconstraint().constraintelem().columnlist().columnElem().forEach( + column -> primaryKeys.add(column.getText())); + } + } + } + } + } +} diff --git a/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/listener/SqlDropListener.java b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/listener/SqlDropListener.java new file mode 100644 index 0000000000..a7fecb380f --- /dev/null +++ b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/listener/SqlDropListener.java @@ -0,0 +1,45 @@ +/* + * Copyright 2021-2023 Aklivity Inc + * + * Licensed under the Aklivity Community License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of the + * License at + * + * https://www.aklivity.io/aklivity-community-license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.aklivity.zilla.runtime.binding.pgsql.parser.listener; + +import java.util.ArrayList; +import java.util.List; + +import io.aklivity.zilla.runtime.binding.pgsql.parser.PostgreSqlParser; +import io.aklivity.zilla.runtime.binding.pgsql.parser.PostgreSqlParserBaseListener; + +public class SqlDropListener extends PostgreSqlParserBaseListener +{ + private final List drops = new ArrayList<>(); + + public List drops() + { + return drops; + } + + @Override + public void enterRoot( + PostgreSqlParser.RootContext ctx) + { + drops.clear(); + } + + @Override + public void enterDropstmt( + PostgreSqlParser.DropstmtContext ctx) + { + ctx.any_name_list().any_name().forEach(name -> drops.add(name.getText())); + } +} diff --git a/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/model/FunctionArgument.java b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/model/FunctionArgument.java new file mode 100644 index 0000000000..91f04b17f4 --- /dev/null +++ b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/model/FunctionArgument.java @@ -0,0 +1,21 @@ +/* + * Copyright 2021-2023 Aklivity Inc + * + * Licensed under the Aklivity Community License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of the + * License at + * + * https://www.aklivity.io/aklivity-community-license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.aklivity.zilla.runtime.binding.pgsql.parser.model; + +public record FunctionArgument( + String name, + String type) +{ +} diff --git a/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/model/FunctionInfo.java b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/model/FunctionInfo.java new file mode 100644 index 0000000000..f41b7939de --- /dev/null +++ b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/model/FunctionInfo.java @@ -0,0 +1,27 @@ +/* + * Copyright 2021-2023 Aklivity Inc + * + * Licensed under the Aklivity Community License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of the + * License at + * + * https://www.aklivity.io/aklivity-community-license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.aklivity.zilla.runtime.binding.pgsql.parser.model; + +import java.util.List; + +public record FunctionInfo( + String name, + List arguments, + String returnType, + List tables, + String asFunction, + String language) +{ +} diff --git a/incubator/binding-risingwave/src/main/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveCreateTableCommand.java b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/model/StreamInfo.java similarity index 56% rename from incubator/binding-risingwave/src/main/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveCreateTableCommand.java rename to incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/model/StreamInfo.java index 02c14773b0..a666131e4f 100644 --- a/incubator/binding-risingwave/src/main/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveCreateTableCommand.java +++ b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/model/StreamInfo.java @@ -12,22 +12,12 @@ * WARRANTIES OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. */ -package io.aklivity.zilla.runtime.binding.risingwave.internal.statement; +package io.aklivity.zilla.runtime.binding.pgsql.parser.model; import java.util.Map; -import net.sf.jsqlparser.statement.create.table.CreateTable; - -public final class RisingwaveCreateTableCommand +public record StreamInfo( + String name, + Map columns) { - public final CreateTable createTable; - public final Map includes; - - public RisingwaveCreateTableCommand( - CreateTable createTable, - Map includes) - { - this.createTable = createTable; - this.includes = includes; - } } diff --git a/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/model/TableInfo.java b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/model/TableInfo.java new file mode 100644 index 0000000000..2f1b3e272b --- /dev/null +++ b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/model/TableInfo.java @@ -0,0 +1,25 @@ +/* + * Copyright 2021-2023 Aklivity Inc + * + * Licensed under the Aklivity Community License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of the + * License at + * + * https://www.aklivity.io/aklivity-community-license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.aklivity.zilla.runtime.binding.pgsql.parser.model; + +import java.util.Map; +import java.util.Set; + +public record TableInfo( + String name, + Map columns, + Set primaryKeys) +{ +} diff --git a/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/model/ViewInfo.java b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/model/ViewInfo.java new file mode 100644 index 0000000000..2f6f43e280 --- /dev/null +++ b/incubator/binding-pgsql/src/main/java/io/aklivity/zilla/runtime/binding/pgsql/parser/model/ViewInfo.java @@ -0,0 +1,21 @@ +/* + * Copyright 2021-2023 Aklivity Inc + * + * Licensed under the Aklivity Community License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of the + * License at + * + * https://www.aklivity.io/aklivity-community-license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.aklivity.zilla.runtime.binding.pgsql.parser.model; + +public record ViewInfo( + String name, + String select) +{ +} diff --git a/incubator/binding-pgsql/src/main/moditect/module-info.java b/incubator/binding-pgsql/src/main/moditect/module-info.java index 3868d46eb3..b8ee14bb35 100644 --- a/incubator/binding-pgsql/src/main/moditect/module-info.java +++ b/incubator/binding-pgsql/src/main/moditect/module-info.java @@ -15,6 +15,11 @@ module io.aklivity.zilla.runtime.binding.pgsql { requires io.aklivity.zilla.runtime.engine; + requires org.antlr.antlr4.runtime; + + exports io.aklivity.zilla.runtime.binding.pgsql.parser; + exports io.aklivity.zilla.runtime.binding.pgsql.parser.listener; + exports io.aklivity.zilla.runtime.binding.pgsql.parser.model; provides io.aklivity.zilla.runtime.engine.binding.BindingFactorySpi with io.aklivity.zilla.runtime.binding.pgsql.internal.PgsqlBindingFactorySpi; diff --git a/incubator/binding-pgsql/src/test/java/io/aklivity/zilla/runtime/binding/pgsql/parser/PgsqlParserTest.java b/incubator/binding-pgsql/src/test/java/io/aklivity/zilla/runtime/binding/pgsql/parser/PgsqlParserTest.java new file mode 100644 index 0000000000..201977e28f --- /dev/null +++ b/incubator/binding-pgsql/src/test/java/io/aklivity/zilla/runtime/binding/pgsql/parser/PgsqlParserTest.java @@ -0,0 +1,356 @@ +/* + * Copyright 2021-2023 Aklivity Inc + * + * Licensed under the Aklivity Community License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of the + * License at + * + * https://www.aklivity.io/aklivity-community-license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.aklivity.zilla.runtime.binding.pgsql.parser; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.List; + +import org.antlr.v4.runtime.misc.ParseCancellationException; +import org.junit.Before; +import org.junit.Test; + +import io.aklivity.zilla.runtime.binding.pgsql.parser.model.FunctionInfo; +import io.aklivity.zilla.runtime.binding.pgsql.parser.model.StreamInfo; +import io.aklivity.zilla.runtime.binding.pgsql.parser.model.TableInfo; +import io.aklivity.zilla.runtime.binding.pgsql.parser.model.ViewInfo; + +public class PgsqlParserTest +{ + private PgsqlParser parser; + + @Before + public void setUp() + { + parser = new PgsqlParser(); + } + + @Test + public void shouldParseWithPrimaryKeySql() + { + String sql = "CREATE TABLE test (id INT PRIMARY KEY, name VARCHAR(100));"; + TableInfo tableInfo = parser.parseCreateTable(sql); + assertNotNull(tableInfo); + assertTrue(tableInfo.primaryKeys().contains("id")); + } + + @Test + public void shouldCreateParseWithPrimaryKeysSql() + { + String sql = """ + CREATE TABLE example_table ( + id INT PRIMARY KEY, + name VARCHAR(100), + age INT, + PRIMARY KEY (id, name) + );"""; + TableInfo tableInfo = parser.parseCreateTable(sql); + assertNotNull(tableInfo); + assertEquals(2, tableInfo.primaryKeys().size()); + assertEquals(3, tableInfo.columns().size()); + assertTrue(tableInfo.primaryKeys().contains("id")); + assertTrue(tableInfo.primaryKeys().contains("name")); + } + + @Test + public void shouldParseCreateTableName() + { + String sql = "CREATE TABLE test (id INT);"; + TableInfo tableInfo = parser.parseCreateTable(sql); + assertEquals("test", tableInfo.name()); + } + + @Test + public void shouldParseCreateTableNameWithDoublePrecisionTypeField() + { + String sql = "CREATE TABLE test (id DOUBLE PRECISION);"; + TableInfo tableInfo = parser.parseCreateTable(sql); + assertEquals("test", tableInfo.name()); + assertEquals("DOUBLE PRECISION", tableInfo.columns().get("id")); + } + + @Test + public void shouldParseCreateTableColumns() + { + String sql = "CREATE TABLE test (id INT, name VARCHAR(100));"; + TableInfo tableInfo = parser.parseCreateTable(sql); + assertEquals(2, tableInfo.columns().size()); + assertEquals("INT", tableInfo.columns().get("id")); + assertEquals("VARCHAR(100)", tableInfo.columns().get("name")); + } + + @Test + public void shouldParseCreatreTablePrimaryKey() + { + String sql = "CREATE TABLE test (id INT PRIMARY KEY, name VARCHAR(100));"; + TableInfo tableInfo = parser.parseCreateTable(sql); + assertEquals(1, tableInfo.primaryKeys().size()); + assertTrue(tableInfo.primaryKeys().contains("id")); + } + + @Test + public void shouldParseCreateTableCompositePrimaryKey() + { + String sql = "CREATE TABLE test (id INT, name VARCHAR(100), PRIMARY KEY (id, name));"; + TableInfo tableInfo = parser.parseCreateTable(sql); + assertEquals(2, tableInfo.primaryKeys().size()); + assertTrue(tableInfo.primaryKeys().contains("id")); + assertTrue(tableInfo.primaryKeys().contains("name")); + } + + @Test + public void shouldHandleEmptyCreateTable() + { + String sql = "CREATE TABLE test ();"; + TableInfo tableInfo = parser.parseCreateTable(sql); + assertEquals(0, tableInfo.columns().size()); + assertEquals(0, tableInfo.primaryKeys().size()); + } + + @Test + public void shouldHandleEmptySql() + { + String sql = ""; + TableInfo tableInfo = parser.parseCreateTable(sql); + assertNotNull(tableInfo); + } + + @Test + public void shouldParseCreateMaterializedView() + { + String sql = "CREATE MATERIALIZED VIEW test_view AS SELECT * FROM test_table;"; + ViewInfo viewInfo = parser.parseCreateMaterializedView(sql); + assertNotNull(viewInfo); + assertEquals("test_view", viewInfo.name()); + assertEquals("SELECT * FROM test_table", viewInfo.select()); + } + + @Test(expected = ParseCancellationException.class) + public void shouldHandleEmptyCreateMaterializedView() + { + String sql = "CREATE MATERIALIZED VIEW test_view AS ;"; + ViewInfo viewInfo = parser.parseCreateMaterializedView(sql); + assertNotNull(viewInfo); + assertEquals("test_view", viewInfo.name()); + } + + @Test(expected = ParseCancellationException.class) + public void shouldHandleInvalidCreateMaterializedView() + { + String sql = "CREATE MATERIALIZED VIEW test_view"; + parser.parseCreateMaterializedView(sql); + } + + @Test(expected = ParseCancellationException.class) + public void shouldHandleInvalidCreateTable() + { + String sql = "CREATE TABLE test"; + parser.parseCreateTable(sql); + } + + @Test + public void shouldParseDropSingleTable() + { + String sql = "DROP TABLE test_table;"; + List drops = parser.parseDrop(sql); + assertEquals(1, drops.size()); + assertTrue(drops.contains("test_table")); + } + + @Test + public void shouldParseDropMultipleTables() + { + String sql = "DROP TABLE table1, table2;"; + List drops = parser.parseDrop(sql); + assertEquals(2, drops.size()); + assertTrue(drops.contains("table1")); + assertTrue(drops.contains("table2")); + } + + @Test(expected = ParseCancellationException.class) + public void shouldHandleEmptyDropStatement() + { + String sql = "DROP TABLE;"; + List drops = parser.parseDrop(sql); + assertEquals(0, drops.size()); + } + + @Test + public void shouldParseDropView() + { + String sql = "DROP VIEW test_view;"; + List drops = parser.parseDrop(sql); + assertEquals(1, drops.size()); + assertTrue(drops.contains("test_view")); + } + + @Test + public void shouldParseDropMaterializedView() + { + String sql = "DROP MATERIALIZED VIEW test_materialized_view;"; + List drops = parser.parseDrop(sql); + assertEquals(1, drops.size()); + assertTrue(drops.contains("test_materialized_view")); + } + + @Test + public void shouldParseCreateStream() + { + String sql = "CREATE STREAM test_stream (id INT, name VARCHAR(100));"; + StreamInfo streamInfo = parser.parseCreateStream(sql); + assertNotNull(streamInfo); + assertEquals("test_stream", streamInfo.name()); + assertEquals(2, streamInfo.columns().size()); + assertEquals("INT", streamInfo.columns().get("id")); + assertEquals("VARCHAR(100)", streamInfo.columns().get("name")); + } + + @Test + public void shouldParseCreateStreamIfNotExists() + { + String sql = "CREATE STREAM IF NOT EXISTS test_stream (id INT, name VARCHAR(100));"; + StreamInfo streamInfo = parser.parseCreateStream(sql); + assertNotNull(streamInfo); + assertEquals("test_stream", streamInfo.name()); + assertEquals(2, streamInfo.columns().size()); + assertEquals("INT", streamInfo.columns().get("id")); + assertEquals("VARCHAR(100)", streamInfo.columns().get("name")); + } + + @Test(expected = ParseCancellationException.class) + public void shouldHandleInvalidCreateStream() + { + String sql = "CREATE STREAM test_stream"; + parser.parseCreateStream(sql); + } + + @Test + public void shouldParseCreateFunction() + { + String sql = "CREATE FUNCTION test_function() RETURNS INT AS $$ BEGIN RETURN 1; END $$ LANGUAGE plpgsql;"; + FunctionInfo functionInfo = parser.parseCreateFunction(sql); + assertNotNull(functionInfo); + assertEquals("test_function", functionInfo.name()); + assertEquals("INT", functionInfo.returnType()); + } + + @Test + public void shouldParseCreateFunctionWithLanguage() + { + String sql = "CREATE FUNCTION test_function(int) RETURNS TABLE (x INT) LANGUAGE python AS 'test_function';"; + FunctionInfo functionInfo = parser.parseCreateFunction(sql); + assertNotNull(functionInfo); + assertEquals("test_function", functionInfo.name()); + assertEquals("INT", functionInfo.returnType()); + assertEquals("python", functionInfo.language()); + } + + @Test + public void shouldParseCreateFunctionWithStructReturnType() + { + String sql = "CREATE FUNCTION test_function(int) RETURNS struct" + + " LANGUAGE python AS 'test_function';"; + FunctionInfo functionInfo = parser.parseCreateFunction(sql); + assertNotNull(functionInfo); + assertEquals("test_function", functionInfo.name()); + assertEquals("struct", functionInfo.returnType()); + assertEquals("python", functionInfo.language()); + } + + @Test(expected = ParseCancellationException.class) + public void shouldHandleInvalidCreateFunction() + { + String sql = "CREATE FUNCTION test_function()"; + parser.parseCreateFunction(sql); + } + + @Test + public void shouldParseCreateTableWithUniqueConstraint() + { + String sql = "CREATE TABLE test (id INT UNIQUE, name VARCHAR(100));"; + TableInfo tableInfo = parser.parseCreateTable(sql); + assertNotNull(tableInfo); + assertEquals(2, tableInfo.columns().size()); + assertTrue(tableInfo.columns().containsKey("id")); + assertTrue(tableInfo.columns().containsKey("name")); + } + + @Test + public void shouldParseCreateTableWithForeignKey() + { + String sql = "CREATE TABLE test (id INT, name VARCHAR(100), CONSTRAINT fk_name FOREIGN KEY (name)" + + " REFERENCES other_table(name));"; + TableInfo tableInfo = parser.parseCreateTable(sql); + assertNotNull(tableInfo); + assertEquals(2, tableInfo.columns().size()); + assertTrue(tableInfo.columns().containsKey("id")); + assertTrue(tableInfo.columns().containsKey("name")); + } + + @Test + public void shouldParseCreateTableWithCheckConstraint() + { + String sql = "CREATE TABLE test (id INT, name VARCHAR(100), CHECK (id > 0));"; + TableInfo tableInfo = parser.parseCreateTable(sql); + assertNotNull(tableInfo); + assertEquals(2, tableInfo.columns().size()); + assertTrue(tableInfo.columns().containsKey("id")); + assertTrue(tableInfo.columns().containsKey("name")); + } + + @Test + public void shouldHandleInvalidCreateTableWithMissingColumns() + { + String sql = "CREATE TABLE test ();"; + parser.parseCreateTable(sql); + } + + @Test + public void shouldParseCreateTableWithDefaultValues() + { + String sql = "CREATE TABLE test (id INT DEFAULT 0, name VARCHAR(100) DEFAULT 'unknown');"; + TableInfo tableInfo = parser.parseCreateTable(sql); + assertNotNull(tableInfo); + assertEquals(2, tableInfo.columns().size()); + assertEquals("INT", tableInfo.columns().get("id")); + assertEquals("VARCHAR(100)", tableInfo.columns().get("name")); + } + + @Test + public void shouldParseCreateTableWithNotNullConstraint() + { + String sql = "CREATE TABLE test (id INT NOT NULL, name VARCHAR(100) NOT NULL);"; + TableInfo tableInfo = parser.parseCreateTable(sql); + assertNotNull(tableInfo); + assertEquals(2, tableInfo.columns().size()); + assertTrue(tableInfo.columns().containsKey("id")); + assertTrue(tableInfo.columns().containsKey("name")); + } + + @Test + public void shouldParseCreateTableWithMultipleConstraints() + { + String sql = "CREATE TABLE test (id INT PRIMARY KEY, name VARCHAR(100) UNIQUE, age INT CHECK (age > 0));"; + TableInfo tableInfo = parser.parseCreateTable(sql); + assertNotNull(tableInfo); + assertEquals(3, tableInfo.columns().size()); + assertTrue(tableInfo.primaryKeys().contains("id")); + assertTrue(tableInfo.columns().containsKey("name")); + assertTrue(tableInfo.columns().containsKey("age")); + } + +} diff --git a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.function.python/client.rpt b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.function.python/client.rpt index 33ee75c8cb..9612e522fa 100644 --- a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.function.python/client.rpt +++ b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.function.python/client.rpt @@ -32,9 +32,9 @@ write zilla:data.ext ${pgsql:dataEx() .query() .build() .build()} -write "CREATE FUNCTION gcd(int , int)\n" +write "CREATE FUNCTION gcd(int, int)\n" "RETURNS int\n" - "AS gcd\n" + "AS 'gcd'\n" "LANGUAGE python\n" "USING LINK 'http://localhost:8816';" [0x00] diff --git a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.function.python/server.rpt b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.function.python/server.rpt index 7bda0b7335..538c98b141 100644 --- a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.function.python/server.rpt +++ b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.function.python/server.rpt @@ -34,9 +34,9 @@ read zilla:data.ext ${pgsql:dataEx() .query() .build() .build()} -read "CREATE FUNCTION gcd(int , int)\n" +read "CREATE FUNCTION gcd(int, int)\n" "RETURNS int\n" - "AS gcd\n" + "AS 'gcd'\n" "LANGUAGE python\n" "USING LINK 'http://localhost:8816';" [0x00] diff --git a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.function.return.struct/client.rpt b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.function.return.struct/client.rpt index 6c81524818..0a33a54114 100644 --- a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.function.return.struct/client.rpt +++ b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.function.return.struct/client.rpt @@ -33,8 +33,8 @@ write zilla:data.ext ${pgsql:dataEx() .build() .build()} write "CREATE FUNCTION key_value(bytea)\n" - "RETURNS struct < key varchar , value varchar >\n" - "AS key_value\n" + "RETURNS struct\n" + "AS 'key_value'\n" "LANGUAGE python\n" "USING LINK 'http://localhost:8816';" [0x00] diff --git a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.function.return.struct/server.rpt b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.function.return.struct/server.rpt index 23335bb651..cffa0b2496 100644 --- a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.function.return.struct/server.rpt +++ b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.function.return.struct/server.rpt @@ -35,8 +35,8 @@ read zilla:data.ext ${pgsql:dataEx() .build() .build()} read "CREATE FUNCTION key_value(bytea)\n" - "RETURNS struct < key varchar , value varchar >\n" - "AS key_value\n" + "RETURNS struct\n" + "AS 'key_value'\n" "LANGUAGE python\n" "USING LINK 'http://localhost:8816';" [0x00] diff --git a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.function.return.table/client.rpt b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.function.return.table/client.rpt index ebcab805e4..88160cdb0d 100644 --- a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.function.return.table/client.rpt +++ b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.function.return.table/client.rpt @@ -33,8 +33,8 @@ write zilla:data.ext ${pgsql:dataEx() .build() .build()} write "CREATE FUNCTION series(int)\n" - "RETURNS TABLE ( x int )\n" - "AS series\n" + "RETURNS TABLE (x int)\n" + "AS 'series'\n" "LANGUAGE java\n" "USING LINK 'http://localhost:8815';" [0x00] diff --git a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.function.return.table/server.rpt b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.function.return.table/server.rpt index ec7a49322a..f02bca873d 100644 --- a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.function.return.table/server.rpt +++ b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.function.return.table/server.rpt @@ -35,8 +35,8 @@ read zilla:data.ext ${pgsql:dataEx() .build() .build()} read "CREATE FUNCTION series(int)\n" - "RETURNS TABLE ( x int )\n" - "AS series\n" + "RETURNS TABLE (x int)\n" + "AS 'series'\n" "LANGUAGE java\n" "USING LINK 'http://localhost:8815';" [0x00] diff --git a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.function/client.rpt b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.function/client.rpt index 182d430247..1a9a69c6e0 100644 --- a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.function/client.rpt +++ b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.function/client.rpt @@ -32,9 +32,9 @@ write zilla:data.ext ${pgsql:dataEx() .query() .build() .build()} -write "CREATE FUNCTION gcd(int , int)\n" +write "CREATE FUNCTION gcd(int, int)\n" "RETURNS int\n" - "AS gcd\n" + "AS 'gcd'\n" "LANGUAGE java\n" "USING LINK 'http://localhost:8815';" [0x00] diff --git a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.function/server.rpt b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.function/server.rpt index 475e1d7c17..b925f5bcdd 100644 --- a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.function/server.rpt +++ b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.function/server.rpt @@ -34,9 +34,9 @@ read zilla:data.ext ${pgsql:dataEx() .query() .build() .build()} -read "CREATE FUNCTION gcd(int , int)\n" +read "CREATE FUNCTION gcd(int, int)\n" "RETURNS int\n" - "AS gcd\n" + "AS 'gcd'\n" "LANGUAGE java\n" "USING LINK 'http://localhost:8815';" [0x00] diff --git a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.stream.with.includes/client.rpt b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.stream.with.includes/client.rpt index 679a78508e..ca19a105fb 100644 --- a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.stream.with.includes/client.rpt +++ b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.stream.with.includes/client.rpt @@ -33,9 +33,9 @@ write zilla:data.ext ${pgsql:dataEx() .build() .build()} write "CREATE SOURCE IF NOT EXISTS weather (*)\n" - "INCLUDE header 'zilla:correlation-id' AS correlation_id\n" - "INCLUDE header 'zilla:identity' AS identity\n" - "INCLUDE timestamp AS timestamp\n" + "INCLUDE header 'zilla:correlation-id' AS zilla_correlation_id\n" + "INCLUDE header 'zilla:identity' AS zilla_identity\n" + "INCLUDE timestamp AS zilla_timestamp\n" "WITH (\n" " connector='kafka',\n" " properties.bootstrap.server='localhost:9092',\n" diff --git a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.stream.with.includes/server.rpt b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.stream.with.includes/server.rpt index 9871eea900..cabb3d2999 100644 --- a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.stream.with.includes/server.rpt +++ b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.stream.with.includes/server.rpt @@ -37,9 +37,9 @@ read zilla:data.ext ${pgsql:dataEx() .build() .build()} read "CREATE SOURCE IF NOT EXISTS weather (*)\n" - "INCLUDE header 'zilla:correlation-id' AS correlation_id\n" - "INCLUDE header 'zilla:identity' AS identity\n" - "INCLUDE timestamp AS timestamp\n" + "INCLUDE header 'zilla:correlation-id' AS zilla_correlation_id\n" + "INCLUDE header 'zilla:identity' AS zilla_identity\n" + "INCLUDE timestamp AS zilla_timestamp\n" "WITH (\n" " connector='kafka',\n" " properties.bootstrap.server='localhost:9092',\n" diff --git a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.table.with.primary.key.and.includes/client.rpt b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.table.with.primary.key.and.includes/client.rpt index 7c587a897b..f5775ad345 100644 --- a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.table.with.primary.key.and.includes/client.rpt +++ b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.table.with.primary.key.and.includes/client.rpt @@ -35,7 +35,7 @@ write zilla:data.ext ${pgsql:dataEx() write "CREATE SOURCE IF NOT EXISTS cities_source (*)\n" "INCLUDE header 'zilla:correlation-id' AS zilla_correlation_id_header\n" "INCLUDE header 'zilla:identity' AS zilla_identity_header\n" - "INCLUDE timestamp AS timestamp\n" + "INCLUDE timestamp AS zilla_timestamp_timestamp\n" "WITH (\n" " connector='kafka',\n" " properties.bootstrap.server='localhost:9092',\n" @@ -67,11 +67,11 @@ write zilla:data.ext ${pgsql:dataEx() .query() .build() .build()} -write "CREATE MATERIALIZED VIEW IF NOT EXISTS cities_view AS" - " SELECT id, name, description," - " COALESCE(correlation_id, zilla_correlation_id_header::varchar) as correlation_id," - " COALESCE(identity, zilla_identity_header::varchar) as identity," - " timestamp" +write "CREATE MATERIALIZED VIEW IF NOT EXISTS cities_view" + " AS SELECT id, name, description," + " COALESCE(zilla_correlation_id, zilla_correlation_id_header::varchar) as zilla_correlation_id," + " COALESCE(zilla_identity, zilla_identity_header::varchar) as zilla_identity," + " COALESCE(zilla_timestamp, zilla_timestamp_timestamp::varchar) as zilla_timestamp" " FROM cities_source;" [0x00] write flush @@ -97,7 +97,7 @@ write zilla:data.ext ${pgsql:dataEx() .build()} write "CREATE TABLE IF NOT EXISTS cities" " (id VARCHAR, name VARCHAR, description VARCHAR," - " correlation_id VARCHAR, identity VARCHAR, timestamp TIMESTAMP," + " zilla_correlation_id VARCHAR, zilla_identity VARCHAR, zilla_timestamp TIMESTAMP," " PRIMARY KEY (id));" [0x00] write flush @@ -192,7 +192,7 @@ write zilla:data.ext ${pgsql:dataEx() .build()} write "CREATE TOPIC IF NOT EXISTS cities " "(id VARCHAR, name VARCHAR, description VARCHAR," - " correlation_id VARCHAR, identity VARCHAR, timestamp TIMESTAMP," + " zilla_correlation_id VARCHAR, zilla_identity VARCHAR, zilla_timestamp TIMESTAMP," " PRIMARY KEY (id));" [0x00] write flush diff --git a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.table.with.primary.key.and.includes/server.rpt b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.table.with.primary.key.and.includes/server.rpt index 4bc34b76a3..48be7e8fd1 100644 --- a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.table.with.primary.key.and.includes/server.rpt +++ b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/effective/create.table.with.primary.key.and.includes/server.rpt @@ -39,7 +39,7 @@ read zilla:data.ext ${pgsql:dataEx() read "CREATE SOURCE IF NOT EXISTS cities_source (*)\n" "INCLUDE header 'zilla:correlation-id' AS zilla_correlation_id_header\n" "INCLUDE header 'zilla:identity' AS zilla_identity_header\n" - "INCLUDE timestamp AS timestamp\n" + "INCLUDE timestamp AS zilla_timestamp_timestamp\n" "WITH (\n" " connector='kafka',\n" " properties.bootstrap.server='localhost:9092',\n" @@ -70,11 +70,11 @@ read zilla:data.ext ${pgsql:dataEx() .query() .build() .build()} -read "CREATE MATERIALIZED VIEW IF NOT EXISTS cities_view AS" - " SELECT id, name, description," - " COALESCE(correlation_id, zilla_correlation_id_header::varchar) as correlation_id," - " COALESCE(identity, zilla_identity_header::varchar) as identity," - " timestamp" +read "CREATE MATERIALIZED VIEW IF NOT EXISTS cities_view" + " AS SELECT id, name, description," + " COALESCE(zilla_correlation_id, zilla_correlation_id_header::varchar) as zilla_correlation_id," + " COALESCE(zilla_identity, zilla_identity_header::varchar) as zilla_identity," + " COALESCE(zilla_timestamp, zilla_timestamp_timestamp::varchar) as zilla_timestamp" " FROM cities_source;" [0x00] @@ -99,7 +99,7 @@ read zilla:data.ext ${pgsql:dataEx() .build()} read "CREATE TABLE IF NOT EXISTS cities" " (id VARCHAR, name VARCHAR, description VARCHAR," - " correlation_id VARCHAR, identity VARCHAR, timestamp TIMESTAMP," + " zilla_correlation_id VARCHAR, zilla_identity VARCHAR, zilla_timestamp TIMESTAMP," " PRIMARY KEY (id));" [0x00] @@ -193,7 +193,7 @@ read zilla:data.ext ${pgsql:dataEx() .build()} read "CREATE TOPIC IF NOT EXISTS cities " "(id VARCHAR, name VARCHAR, description VARCHAR," - " correlation_id VARCHAR, identity VARCHAR, timestamp TIMESTAMP," + " zilla_correlation_id VARCHAR, zilla_identity VARCHAR, zilla_timestamp TIMESTAMP," " PRIMARY KEY (id));" [0x00] diff --git a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.function.python/client.rpt b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.function.python/client.rpt index 340e999227..93bfa33205 100644 --- a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.function.python/client.rpt +++ b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.function.python/client.rpt @@ -33,7 +33,7 @@ write zilla:data.ext ${pgsql:dataEx() .build() .build()} write "CREATE FUNCTION gcd(int, int) RETURNS int " - "LANGUAGE python AS gcd;" + "LANGUAGE python AS 'gcd';" [0x00] write flush diff --git a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.function.python/server.rpt b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.function.python/server.rpt index 20e83d8382..0170ffc27a 100644 --- a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.function.python/server.rpt +++ b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.function.python/server.rpt @@ -37,7 +37,7 @@ read zilla:data.ext ${pgsql:dataEx() .build() .build()} read "CREATE FUNCTION gcd(int, int) RETURNS int " - "LANGUAGE python AS gcd;" + "LANGUAGE python AS 'gcd';" [0x00] write advise zilla:flush ${pgsql:flushEx() diff --git a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.function.return.struct/client.rpt b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.function.return.struct/client.rpt index bf1030e822..7b03821097 100644 --- a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.function.return.struct/client.rpt +++ b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.function.return.struct/client.rpt @@ -33,7 +33,7 @@ write zilla:data.ext ${pgsql:dataEx() .build() .build()} write "CREATE FUNCTION key_value(bytea) RETURNS struct " - "LANGUAGE python AS key_value;" + "LANGUAGE python AS 'key_value';" [0x00] write flush diff --git a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.function.return.struct/server.rpt b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.function.return.struct/server.rpt index 05a9d55584..dddbbe6eee 100644 --- a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.function.return.struct/server.rpt +++ b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.function.return.struct/server.rpt @@ -37,7 +37,7 @@ read zilla:data.ext ${pgsql:dataEx() .build() .build()} read "CREATE FUNCTION key_value(bytea) RETURNS struct " - "LANGUAGE python AS key_value;" + "LANGUAGE python AS 'key_value';" [0x00] write advise zilla:flush ${pgsql:flushEx() diff --git a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.function.return.table/client.rpt b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.function.return.table/client.rpt index 8f7384ab62..e8599cdbf5 100644 --- a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.function.return.table/client.rpt +++ b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.function.return.table/client.rpt @@ -33,7 +33,7 @@ write zilla:data.ext ${pgsql:dataEx() .build() .build()} write "CREATE FUNCTION series(int) RETURNS TABLE (x int) " - "AS series;" + "AS 'series';" [0x00] write flush diff --git a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.function.return.table/server.rpt b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.function.return.table/server.rpt index 073520fff6..38ada2e027 100644 --- a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.function.return.table/server.rpt +++ b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.function.return.table/server.rpt @@ -37,7 +37,7 @@ read zilla:data.ext ${pgsql:dataEx() .build() .build()} read "CREATE FUNCTION series(int) RETURNS TABLE (x int) " - "AS series;" + "AS 'series';" [0x00] write advise zilla:flush ${pgsql:flushEx() diff --git a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.function/client.rpt b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.function/client.rpt index 49ccf60200..cada6175a2 100644 --- a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.function/client.rpt +++ b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.function/client.rpt @@ -33,7 +33,7 @@ write zilla:data.ext ${pgsql:dataEx() .build() .build()} write "CREATE FUNCTION gcd(int, int) RETURNS int " - "AS gcd;" + "AS 'gcd';" [0x00] write flush diff --git a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.function/server.rpt b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.function/server.rpt index 4fc06f7a40..8036c14b74 100644 --- a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.function/server.rpt +++ b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.function/server.rpt @@ -37,7 +37,7 @@ read zilla:data.ext ${pgsql:dataEx() .build() .build()} read "CREATE FUNCTION gcd(int, int) RETURNS int " - "AS gcd;" + "AS 'gcd';" [0x00] write advise zilla:flush ${pgsql:flushEx() diff --git a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.stream.with.includes/client.rpt b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.stream.with.includes/client.rpt index dbb1c245b7..61a9befb91 100644 --- a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.stream.with.includes/client.rpt +++ b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.stream.with.includes/client.rpt @@ -33,10 +33,8 @@ write zilla:data.ext ${pgsql:dataEx() .build() .build()} write "CREATE STREAM IF NOT EXISTS weather " - "(city VARCHAR, temperature DOUBLE, date DATE)\n" - "INCLUDE zilla_correlation_id AS correlation_id\n" - "INCLUDE zilla_identity AS identity\n" - "INCLUDE timestamp as timestamp;" + "(city VARCHAR, temperature DOUBLE, date DATE," + " zilla_correlation_id VARCHAR, zilla_identity VARCHAR, zilla_timestamp TIMESTAMP);" [0x00] write flush diff --git a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.stream.with.includes/server.rpt b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.stream.with.includes/server.rpt index 19833142cf..e328e36ca6 100644 --- a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.stream.with.includes/server.rpt +++ b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.stream.with.includes/server.rpt @@ -37,12 +37,11 @@ read zilla:data.ext ${pgsql:dataEx() .build() .build()} read "CREATE STREAM IF NOT EXISTS weather " - "(city VARCHAR, temperature DOUBLE, date DATE)\n" - "INCLUDE zilla_correlation_id AS correlation_id\n" - "INCLUDE zilla_identity AS identity\n" - "INCLUDE timestamp as timestamp;" + "(city VARCHAR, temperature DOUBLE, date DATE," + " zilla_correlation_id VARCHAR, zilla_identity VARCHAR, zilla_timestamp TIMESTAMP);" [0x00] + write advise zilla:flush ${pgsql:flushEx() .typeId(zilla:id("pgsql")) .completion() diff --git a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.table.with.primary.key.and.includes/client.rpt b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.table.with.primary.key.and.includes/client.rpt index f40e1041f9..823f4ffbe8 100644 --- a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.table.with.primary.key.and.includes/client.rpt +++ b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.table.with.primary.key.and.includes/client.rpt @@ -33,10 +33,9 @@ write zilla:data.ext ${pgsql:dataEx() .build() .build()} write "CREATE TABLE IF NOT EXISTS cities " - "(id VARCHAR, name VARCHAR, description VARCHAR, PRIMARY KEY (id))\n" - "INCLUDE zilla_correlation_id AS correlation_id\n" - "INCLUDE zilla_identity AS identity\n" - "INCLUDE timestamp as timestamp;" + "(id VARCHAR, name VARCHAR, description VARCHAR," + " zilla_correlation_id VARCHAR, zilla_identity VARCHAR, zilla_timestamp TIMESTAMP," + " PRIMARY KEY (id));" [0x00] write flush diff --git a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.table.with.primary.key.and.includes/server.rpt b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.table.with.primary.key.and.includes/server.rpt index ed5fda502c..3c916e9f9c 100644 --- a/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.table.with.primary.key.and.includes/server.rpt +++ b/incubator/binding-risingwave.spec/src/main/scripts/io/aklivity/zilla/specs/binding/risingwave/streams/pgsql/create.table.with.primary.key.and.includes/server.rpt @@ -37,10 +37,9 @@ read zilla:data.ext ${pgsql:dataEx() .build() .build()} read "CREATE TABLE IF NOT EXISTS cities " - "(id VARCHAR, name VARCHAR, description VARCHAR, PRIMARY KEY (id))\n" - "INCLUDE zilla_correlation_id AS correlation_id\n" - "INCLUDE zilla_identity AS identity\n" - "INCLUDE timestamp as timestamp;" + "(id VARCHAR, name VARCHAR, description VARCHAR," + " zilla_correlation_id VARCHAR, zilla_identity VARCHAR, zilla_timestamp TIMESTAMP," + " PRIMARY KEY (id));" [0x00] write advise zilla:flush ${pgsql:flushEx() diff --git a/incubator/binding-risingwave/NOTICE b/incubator/binding-risingwave/NOTICE index c6b7d9c015..3913dbbc71 100644 --- a/incubator/binding-risingwave/NOTICE +++ b/incubator/binding-risingwave/NOTICE @@ -10,5 +10,5 @@ WARRANTIES OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. This project includes: - JSQLParser library under GNU Library or Lesser General Public License (LGPL) V2.1 or The Apache Software License, Version 2.0 + zilla::incubator::binding-pgsql under Aklivity Community License Agreement diff --git a/incubator/binding-risingwave/pom.xml b/incubator/binding-risingwave/pom.xml index 98571514a4..a087cd9681 100644 --- a/incubator/binding-risingwave/pom.xml +++ b/incubator/binding-risingwave/pom.xml @@ -24,7 +24,7 @@ - 0.86 + 0.91 0 @@ -42,8 +42,8 @@ provided - com.github.jsqlparser - jsqlparser + io.aklivity.zilla + binding-pgsql ${project.groupId} @@ -195,7 +195,6 @@ io/aklivity/zilla/runtime/binding/risingwave/internal/types/**/*.class - net/sf/jsqlparser/parser/* diff --git a/incubator/binding-risingwave/src/main/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveCommandTemplate.java b/incubator/binding-risingwave/src/main/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveCommandTemplate.java index bb65d54a24..c107737304 100644 --- a/incubator/binding-risingwave/src/main/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveCommandTemplate.java +++ b/incubator/binding-risingwave/src/main/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveCommandTemplate.java @@ -14,138 +14,32 @@ */ package io.aklivity.zilla.runtime.binding.risingwave.internal.statement; -import java.io.StringReader; -import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; -import org.agrona.DirectBuffer; import org.agrona.collections.Object2ObjectHashMap; -import net.sf.jsqlparser.parser.CCJSqlParserManager; -import net.sf.jsqlparser.statement.create.table.CreateTable; -import net.sf.jsqlparser.statement.create.table.Index; public abstract class RisingwaveCommandTemplate { - private final CCJSqlParserManager parserManager = new CCJSqlParserManager(); - private final Map includeMap = new LinkedHashMap<>(); + protected static final String ZILLA_CORRELATION_ID = "zilla_correlation_id"; + protected static final String ZILLA_IDENTITY = "zilla_identity"; + protected static final String ZILLA_TIMESTAMP = "zilla_timestamp"; protected final StringBuilder fieldBuilder = new StringBuilder(); protected final StringBuilder includeBuilder = new StringBuilder(); protected static final Map ZILLA_MAPPINGS = new Object2ObjectHashMap<>(); static { - ZILLA_MAPPINGS.put("zilla_correlation_id", "INCLUDE header 'zilla:correlation-id' AS %s\n"); - ZILLA_MAPPINGS.put("zilla_identity", "INCLUDE header 'zilla:identity' AS %s\n"); - ZILLA_MAPPINGS.put("timestamp", "INCLUDE timestamp AS %s\n"); + ZILLA_MAPPINGS.put(ZILLA_CORRELATION_ID, "INCLUDE header 'zilla:correlation-id' AS %s\n"); + ZILLA_MAPPINGS.put(ZILLA_IDENTITY, "INCLUDE header 'zilla:identity' AS %s\n"); + ZILLA_MAPPINGS.put(ZILLA_TIMESTAMP, "INCLUDE timestamp AS %s\n"); } protected static final Map ZILLA_INCLUDE_TYPE_MAPPINGS = new Object2ObjectHashMap<>(); static { - ZILLA_INCLUDE_TYPE_MAPPINGS.put("zilla_correlation_id", "VARCHAR"); - ZILLA_INCLUDE_TYPE_MAPPINGS.put("zilla_identity", "VARCHAR"); - ZILLA_INCLUDE_TYPE_MAPPINGS.put("timestamp", "TIMESTAMP"); - } - - public String primaryKey( - CreateTable statement) - { - String primaryKey = null; - - final List indexes = statement.getIndexes(); - - if (indexes != null && !indexes.isEmpty()) - { - match: - for (Index index : indexes) - { - if ("PRIMARY KEY".equalsIgnoreCase(index.getType())) - { - final List primaryKeyColumns = index.getColumns(); - primaryKey = primaryKeyColumns.get(0).columnName; - break match; - } - } - } - - return primaryKey; - } - - public RisingwaveCreateTableCommand parserCreateTable( - DirectBuffer buffer, - int offset, - int length) - { - String query = buffer.getStringWithoutLengthUtf8(offset, length); - query = query.replaceAll("(?i)\\bCREATE\\s+STREAM\\b", "CREATE TABLE"); - - int includeIndex = query.indexOf("INCLUDE"); - - String createTablePart; - String includePart = null; - - CreateTable createTable = null; - Map includes = null; - - if (includeIndex != -1) - { - createTablePart = query.substring(0, includeIndex).trim(); - includePart = query.substring(includeIndex).trim(); - } - else - { - createTablePart = query.trim(); - } - - try - { - createTable = (CreateTable) parserManager.parse(new StringReader(createTablePart)); - } - catch (Exception ignore) - { - } - - if (includePart != null) - { - includes = parseSpecificIncludes(includePart); - } - - return new RisingwaveCreateTableCommand(createTable, includes); - } - - private Map parseSpecificIncludes( - String includePart) - { - String[] includeClauses = includePart.toLowerCase().split("include"); - for (String clause : includeClauses) - { - clause = clause.trim(); - if (!clause.isEmpty()) - { - String[] parts = clause.toLowerCase().split("as"); - if (parts.length == 2) - { - String key = parts[0].trim(); - String value = parts[1].trim().replace(";", ""); - - if (isValidInclude(key)) - { - includeMap.put(key, value); - } - } - } - } - - return includeMap; - } - - private static boolean isValidInclude( - String key) - { - return "zilla_correlation_id".equals(key) || - "zilla_identity".equals(key) || - "timestamp".equals(key); + ZILLA_INCLUDE_TYPE_MAPPINGS.put(ZILLA_CORRELATION_ID, "VARCHAR"); + ZILLA_INCLUDE_TYPE_MAPPINGS.put(ZILLA_IDENTITY, "VARCHAR"); + ZILLA_INCLUDE_TYPE_MAPPINGS.put(ZILLA_TIMESTAMP, "TIMESTAMP"); } } diff --git a/incubator/binding-risingwave/src/main/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveCreateFunctionTemplate.java b/incubator/binding-risingwave/src/main/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveCreateFunctionTemplate.java index 6c687b2e90..b171265ba2 100644 --- a/incubator/binding-risingwave/src/main/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveCreateFunctionTemplate.java +++ b/incubator/binding-risingwave/src/main/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveCreateFunctionTemplate.java @@ -16,13 +16,12 @@ import java.util.List; +import io.aklivity.zilla.runtime.binding.pgsql.parser.model.FunctionArgument; +import io.aklivity.zilla.runtime.binding.pgsql.parser.model.FunctionInfo; import io.aklivity.zilla.runtime.binding.risingwave.config.RisingwaveUdfConfig; -import net.sf.jsqlparser.statement.Statement; -import net.sf.jsqlparser.statement.create.function.CreateFunction; public class RisingwaveCreateFunctionTemplate extends RisingwaveCommandTemplate { - private static final List KEYWORDS = List.of("LANGUAGE", "AS", "USING"); private final String sqlFormat = """ CREATE FUNCTION %s(%s) RETURNS %s @@ -30,8 +29,8 @@ public class RisingwaveCreateFunctionTemplate extends RisingwaveCommandTemplate LANGUAGE %s USING LINK '%s';\u0000"""; - private final String java; - private final String python; + private final String javaServer; + private final String pythonServer; public RisingwaveCreateFunctionTemplate( List udfs) @@ -54,58 +53,50 @@ else if (udf.language.equalsIgnoreCase("python")) } } - this.java = javaServer; - this.python = pythonServer; + this.javaServer = javaServer; + this.pythonServer = pythonServer; } public String generate( - Statement statement) + FunctionInfo functionInfo) { - CreateFunction createFunction = (CreateFunction) statement; - List parts = createFunction.getFunctionDeclarationParts(); + String functionName = functionInfo.name(); + String asFunction = functionInfo.asFunction(); + List arguments = functionInfo.arguments(); + List tables = functionInfo.tables(); - String functionName = parts.get(0); + fieldBuilder.setLength(0); - int paramStartIndex = parts.indexOf("("); - int paramEndIndex = parts.indexOf(")"); - String parameters = paramStartIndex >= 0 && paramEndIndex > paramStartIndex - ? String.join(" ", parts.subList(paramStartIndex + 1, paramEndIndex)) - : ""; + arguments + .forEach(arg -> fieldBuilder.append( + arg.name() != null + ? "%s %s, ".formatted(arg.name(), arg.type()) + : "%s, ".formatted(arg.type()))); - int returnsIndex = parts.indexOf("RETURNS"); - String returnType = returnsIndex >= 0 ? parts.get(returnsIndex + 1) : ""; - - if (returnsIndex >= 0) + if (!arguments.isEmpty()) { - int nextKeywordIndex = findNextKeywordIndex(parts, returnsIndex + 1); - returnType = nextKeywordIndex >= 0 - ? String.join(" ", parts.subList(returnsIndex + 1, nextKeywordIndex)) - : String.join(" ", parts.subList(returnsIndex + 1, parts.size())); + fieldBuilder.delete(fieldBuilder.length() - 2, fieldBuilder.length()); } + String funcArguments = fieldBuilder.toString(); - int asIndex = parts.indexOf("AS"); - String body = asIndex >= 0 ? parts.get(asIndex + 1) : ""; - - int langIndex = parts.indexOf("LANGUAGE"); - String language = langIndex >= 0 ? parts.get(langIndex + 1) : "java"; - - return sqlFormat.formatted(functionName, parameters, returnType.trim(), body, language, - "python".equalsIgnoreCase(language) ? python : java); - } + String language = functionInfo.language() != null ? functionInfo.language() : "java"; + String server = "python".equalsIgnoreCase(language) ? pythonServer : javaServer; - private int findNextKeywordIndex( - List parts, - int startIndex) - { - int endIndex = -1; - for (int index = startIndex; index < parts.size(); index++) + String returnType = functionInfo.returnType(); + if (!tables.isEmpty()) { - if (KEYWORDS.contains(parts.get(index).toUpperCase())) - { - endIndex = index; - break; - } + fieldBuilder.setLength(0); + fieldBuilder.append("TABLE ("); + tables.forEach(arg -> fieldBuilder.append( + arg.name() != null + ? "%s %s, ".formatted(arg.name(), arg.type()) + : "%s, ".formatted(arg.type()))); + fieldBuilder.delete(fieldBuilder.length() - 2, fieldBuilder.length()); + fieldBuilder.append(")"); + + returnType = fieldBuilder.toString(); } - return endIndex; + + return sqlFormat.formatted(functionName, funcArguments, returnType, asFunction, language, server); } } diff --git a/incubator/binding-risingwave/src/main/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveCreateMaterializedViewTemplate.java b/incubator/binding-risingwave/src/main/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveCreateMaterializedViewTemplate.java index 4a57891eb3..a6f852d0b0 100644 --- a/incubator/binding-risingwave/src/main/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveCreateMaterializedViewTemplate.java +++ b/incubator/binding-risingwave/src/main/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveCreateMaterializedViewTemplate.java @@ -14,53 +14,63 @@ */ package io.aklivity.zilla.runtime.binding.risingwave.internal.statement; -import net.sf.jsqlparser.statement.create.table.CreateTable; -import net.sf.jsqlparser.statement.create.view.CreateView; +import java.util.LinkedHashMap; +import java.util.Map; + +import io.aklivity.zilla.runtime.binding.pgsql.parser.model.TableInfo; +import io.aklivity.zilla.runtime.binding.pgsql.parser.model.ViewInfo; + public class RisingwaveCreateMaterializedViewTemplate extends RisingwaveCommandTemplate { private final String sqlFormat = """ CREATE MATERIALIZED VIEW IF NOT EXISTS %s AS %s;\u0000"""; private final String fieldFormat = "%s, "; - private final String includeFormat = "COALESCE(%s, zilla_%s_header::varchar) as %s, "; + private final String includeFormat = "COALESCE(%s, %s_header::varchar) as %s, "; + private final String timestampFormat = "COALESCE(%s, %s_timestamp::varchar) as %s, "; public RisingwaveCreateMaterializedViewTemplate() { } public String generate( - CreateView createView) + ViewInfo viewInfo) { - String view = createView.getView().getName(); - String select = createView.getSelect().toString(); + String name = viewInfo.name(); + String select = viewInfo.select(); - return String.format(sqlFormat, view, select); + return String.format(sqlFormat, name, select); } public String generate( - RisingwaveCreateTableCommand command) + TableInfo tableInfo) { - CreateTable createTable = command.createTable; - String name = createTable.getTable().getName(); + String name = tableInfo.name(); String select = "*"; - if (command.includes != null) + Map includes = tableInfo.columns().entrySet().stream() + .filter(e -> ZILLA_MAPPINGS.containsKey(e.getKey())) + .collect(LinkedHashMap::new, (m, e) -> m.put(e.getKey(), e.getValue()), Map::putAll); + + if (!includes.isEmpty()) { fieldBuilder.setLength(0); - createTable.getColumnDefinitions() - .forEach(c -> fieldBuilder.append( - String.format(fieldFormat, c.getColumnName()))); - command.includes.forEach((k, v) -> + tableInfo.columns().keySet() + .stream() + .filter(c -> !ZILLA_MAPPINGS.containsKey(c)) + .forEach(c -> fieldBuilder.append(String.format(fieldFormat, c))); + + includes.forEach((k, v) -> { - if ("timestamp".equals(k)) + if (ZILLA_TIMESTAMP.equals(k)) { - fieldBuilder.append(String.format(fieldFormat, v)); + fieldBuilder.append(String.format(timestampFormat, k, k, k)); } else { - fieldBuilder.append(String.format(includeFormat, v, v, v)); + fieldBuilder.append(String.format(includeFormat, k, k, k)); } }); diff --git a/incubator/binding-risingwave/src/main/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveCreateSinkTemplate.java b/incubator/binding-risingwave/src/main/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveCreateSinkTemplate.java index e30a55b5f3..2025b81d0e 100644 --- a/incubator/binding-risingwave/src/main/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveCreateSinkTemplate.java +++ b/incubator/binding-risingwave/src/main/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveCreateSinkTemplate.java @@ -17,8 +17,8 @@ import java.util.Map; import java.util.Optional; -import net.sf.jsqlparser.statement.create.table.CreateTable; -import net.sf.jsqlparser.statement.create.view.CreateView; +import io.aklivity.zilla.runtime.binding.pgsql.parser.model.TableInfo; +import io.aklivity.zilla.runtime.binding.pgsql.parser.model.ViewInfo; public class RisingwaveCreateSinkTemplate extends RisingwaveCommandTemplate { @@ -51,9 +51,9 @@ public RisingwaveCreateSinkTemplate( public String generate( String database, Map columns, - CreateView createView) + ViewInfo viewInfo) { - String viewName = createView.getView().getName(); + String viewName = viewInfo.name(); Optional> primaryKeyMatch = columns.entrySet().stream() .filter(e -> "id".equalsIgnoreCase(e.getKey())) @@ -74,19 +74,18 @@ public String generate( public String generate( String database, - String primaryKey, - CreateTable createTable) + TableInfo tableInfo) { - String table = createTable.getTable().getName(); + String table = tableInfo.name(); return String.format(sqlKafkaFormat, table, table, bootstrapServer, database, table, - primaryKeyFormat.formatted(primaryKey), schemaRegistry); + primaryKeyFormat.formatted(tableInfo.primaryKeys().stream().findFirst().get()), schemaRegistry); } public String generate( - CreateTable createTable) + TableInfo tableInfo) { - String table = createTable.getTable().getName(); + String table = tableInfo.name(); return String.format(sqlFormat, table, table, table); } diff --git a/incubator/binding-risingwave/src/main/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveCreateSourceTemplate.java b/incubator/binding-risingwave/src/main/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveCreateSourceTemplate.java index 1e64e8456d..f154574431 100644 --- a/incubator/binding-risingwave/src/main/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveCreateSourceTemplate.java +++ b/incubator/binding-risingwave/src/main/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveCreateSourceTemplate.java @@ -14,8 +14,12 @@ */ package io.aklivity.zilla.runtime.binding.risingwave.internal.statement; +import java.util.LinkedHashMap; import java.util.Map; +import io.aklivity.zilla.runtime.binding.pgsql.parser.model.StreamInfo; +import io.aklivity.zilla.runtime.binding.pgsql.parser.model.TableInfo; + public class RisingwaveCreateSourceTemplate extends RisingwaveCommandTemplate { private final String sqlFormat = """ @@ -46,16 +50,19 @@ public RisingwaveCreateSourceTemplate( public String generateStreamSource( String database, - RisingwaveCreateTableCommand command) + StreamInfo streamInfo) { - String table = command.createTable.getTable().getName(); + String table = streamInfo.name(); includeBuilder.setLength(0); - final Map includes = command.includes; - if (includes != null && !includes.isEmpty()) + Map includes = streamInfo.columns().entrySet().stream() + .filter(e -> ZILLA_MAPPINGS.containsKey(e.getKey())) + .collect(LinkedHashMap::new, (m, e) -> m.put(e.getKey(), e.getValue()), Map::putAll); + + if (!includes.isEmpty()) { includeBuilder.append("\n"); - includes.forEach((k, v) -> includeBuilder.append(String.format(ZILLA_MAPPINGS.get(k), v))); + includes.forEach((k, v) -> includeBuilder.append(String.format(ZILLA_MAPPINGS.get(k), k))); includeBuilder.delete(includeBuilder.length() - 1, includeBuilder.length()); } @@ -64,27 +71,29 @@ public String generateStreamSource( public String generateTableSource( String database, - RisingwaveCreateTableCommand command) + TableInfo tableInfo) { - String table = command.createTable.getTable().getName(); + String table = tableInfo.name(); String sourceName = "%s_source".formatted(table); includeBuilder.setLength(0); - final Map includes = command.includes; - if (includes != null && !includes.isEmpty()) + Map includes = tableInfo.columns().entrySet().stream() + .filter(e -> ZILLA_MAPPINGS.containsKey(e.getKey())) + .collect(LinkedHashMap::new, (m, e) -> m.put(e.getKey(), e.getValue()), Map::putAll); + + if (!includes.isEmpty()) { includeBuilder.append("\n"); includes.forEach((k, v) -> { - if ("timestamp".equals(k)) + if (ZILLA_TIMESTAMP.equals(k)) { - includeBuilder.append(String.format(ZILLA_MAPPINGS.get(k), v)); + includeBuilder.append(String.format(ZILLA_MAPPINGS.get(k), "%s_timestamp".formatted(k))); } else { - includeBuilder.append(String.format(ZILLA_MAPPINGS.get(k), "zilla_%s_header".formatted(v))); + includeBuilder.append(String.format(ZILLA_MAPPINGS.get(k), "%s_header".formatted(k))); } - }); includeBuilder.delete(includeBuilder.length() - 1, includeBuilder.length()); } diff --git a/incubator/binding-risingwave/src/main/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveCreateTableTemplate.java b/incubator/binding-risingwave/src/main/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveCreateTableTemplate.java index 9e8f98acf0..9c225454af 100644 --- a/incubator/binding-risingwave/src/main/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveCreateTableTemplate.java +++ b/incubator/binding-risingwave/src/main/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveCreateTableTemplate.java @@ -14,7 +14,7 @@ */ package io.aklivity.zilla.runtime.binding.risingwave.internal.statement; -import net.sf.jsqlparser.statement.create.table.CreateTable; +import io.aklivity.zilla.runtime.binding.pgsql.parser.model.TableInfo; public class RisingwaveCreateTableTemplate extends RisingwaveCommandTemplate { @@ -28,24 +28,18 @@ public RisingwaveCreateTableTemplate() } public String generate( - RisingwaveCreateTableCommand command) + TableInfo tableInfo) { - CreateTable createTable = command.createTable; - String topic = createTable.getTable().getName(); - String primaryKeyField = primaryKey(createTable); - String primaryKey = primaryKeyField != null ? String.format(primaryKeyFormat, primaryKeyField) : ""; + String topic = tableInfo.name(); + String primaryKey = !tableInfo.primaryKeys().isEmpty() + ? String.format(primaryKeyFormat, tableInfo.primaryKeys().stream().findFirst().get()) + : ""; fieldBuilder.setLength(0); - createTable.getColumnDefinitions() - .forEach(c -> fieldBuilder.append( - String.format(fieldFormat, c.getColumnName(), c.getColDataType().getDataType()))); - - if (command.includes != null) - { - command.includes.forEach((k, v) -> fieldBuilder.append( - String.format(fieldFormat, v, ZILLA_INCLUDE_TYPE_MAPPINGS.get(k)))); - } + tableInfo.columns() + .forEach((k, v) -> fieldBuilder.append( + String.format(fieldFormat, k, v))); fieldBuilder.delete(fieldBuilder.length() - 2, fieldBuilder.length()); diff --git a/incubator/binding-risingwave/src/main/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveCreateTopicTemplate.java b/incubator/binding-risingwave/src/main/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveCreateTopicTemplate.java index f088a3111a..39a1b16c2f 100644 --- a/incubator/binding-risingwave/src/main/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveCreateTopicTemplate.java +++ b/incubator/binding-risingwave/src/main/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveCreateTopicTemplate.java @@ -16,8 +16,9 @@ import java.util.Map; -import net.sf.jsqlparser.statement.create.table.CreateTable; -import net.sf.jsqlparser.statement.create.view.CreateView; +import io.aklivity.zilla.runtime.binding.pgsql.parser.model.StreamInfo; +import io.aklivity.zilla.runtime.binding.pgsql.parser.model.TableInfo; +import io.aklivity.zilla.runtime.binding.pgsql.parser.model.ViewInfo; public class RisingwaveCreateTopicTemplate extends RisingwaveCommandTemplate { @@ -34,17 +35,18 @@ public RisingwaveCreateTopicTemplate() } public String generate( - CreateTable createTable) + TableInfo tableInfo) { - String topic = createTable.getTable().getName(); - String primaryKeyField = primaryKey(createTable); - String primaryKey = primaryKeyField != null ? String.format(primaryKeyFormat, primaryKeyField) : ""; + String topic = tableInfo.name(); + String primaryKey = !tableInfo.primaryKeys().isEmpty() + ? String.format(primaryKeyFormat, tableInfo.primaryKeys().stream().findFirst().get()) + : ""; fieldBuilder.setLength(0); - createTable.getColumnDefinitions() - .forEach(c -> fieldBuilder.append( - String.format(fieldFormat, c.getColumnName(), c.getColDataType().getDataType()))); + tableInfo.columns() + .forEach((k, v) -> fieldBuilder.append( + String.format(fieldFormat, k, v))); fieldBuilder.delete(fieldBuilder.length() - 2, fieldBuilder.length()); @@ -52,35 +54,29 @@ public String generate( } public String generate( - RisingwaveCreateTableCommand command) + StreamInfo streamInfo) { - CreateTable createTable = command.createTable; - String topic = createTable.getTable().getName(); - String primaryKeyField = primaryKey(createTable); - String primaryKey = primaryKeyField != null ? String.format(primaryKeyFormat, primaryKeyField) : ""; + String topic = streamInfo.name(); fieldBuilder.setLength(0); - createTable.getColumnDefinitions() - .forEach(c -> fieldBuilder.append( - String.format(fieldFormat, c.getColumnName(), c.getColDataType().getDataType()))); - - if (command.includes != null) - { - command.includes.forEach((k, v) -> fieldBuilder.append( - String.format(fieldFormat, v, ZILLA_INCLUDE_TYPE_MAPPINGS.get(k)))); - } + streamInfo.columns() + .entrySet() + .stream() + .filter(e -> !ZILLA_MAPPINGS.containsKey(e.getKey())) + .forEach(e -> fieldBuilder.append( + String.format(fieldFormat, e.getKey(), e.getValue()))); fieldBuilder.delete(fieldBuilder.length() - 2, fieldBuilder.length()); - return String.format(sqlFormat, topic, fieldBuilder, primaryKey); + return String.format(sqlFormat, topic, fieldBuilder, ""); } public String generate( - CreateView createView, + ViewInfo viewInfo, Map columns) { - String topic = createView.getView().getName(); + String topic = viewInfo.name(); primaryKeyBuilder.setLength(0); columns.keySet().forEach(k -> primaryKeyBuilder.append(k).append(", ")); diff --git a/incubator/binding-risingwave/src/main/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveDescribeTemplate.java b/incubator/binding-risingwave/src/main/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveDescribeTemplate.java index 238d75bb06..4f0718acd4 100644 --- a/incubator/binding-risingwave/src/main/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveDescribeTemplate.java +++ b/incubator/binding-risingwave/src/main/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveDescribeTemplate.java @@ -14,8 +14,7 @@ */ package io.aklivity.zilla.runtime.binding.risingwave.internal.statement; -import net.sf.jsqlparser.statement.Statement; -import net.sf.jsqlparser.statement.create.view.CreateView; +import io.aklivity.zilla.runtime.binding.pgsql.parser.model.ViewInfo; public class RisingwaveDescribeTemplate extends RisingwaveCommandTemplate { @@ -27,11 +26,10 @@ public RisingwaveDescribeTemplate() } public String generate( - Statement statement) + ViewInfo viewInfo) { - CreateView createView = (CreateView) statement; - String view = createView.getView().getName(); + String name = viewInfo.name(); - return String.format(sqlFormat, view); + return String.format(sqlFormat, name); } } diff --git a/incubator/binding-risingwave/src/main/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveSqlCommandParser.java b/incubator/binding-risingwave/src/main/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveSqlCommandParser.java deleted file mode 100644 index a3ddf19ed0..0000000000 --- a/incubator/binding-risingwave/src/main/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveSqlCommandParser.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2021-2023 Aklivity Inc - * - * Licensed under the Aklivity Community License (the "License"); you may not use - * this file except in compliance with the License. You may obtain a copy of the - * License at - * - * https://www.aklivity.io/aklivity-community-license/ - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ -package io.aklivity.zilla.runtime.binding.risingwave.internal.statement; - -import java.nio.charset.StandardCharsets; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import io.aklivity.zilla.runtime.binding.risingwave.internal.config.RisingwaveCommandType; - -public final class RisingwaveSqlCommandParser -{ - private static final String SQL_FUNCTION_PATTERN = - "CREATE\\s+FUNCTION[\\s\\S]+?\\$\\$[\\s\\S]+?\\$\\$"; - private static final String SQL_COMMAND_PATTERN = - "(?i)\\b(CREATE FUNCTION)\\b.*?\\$\\$(.*?)\\$\\$\\s*;[\\x00\\n]*" + - "|\\b(CREATE FUNCTION)\\b.*?RETURNS .*?AS.*?;[\\x00\\n]*" + - "|\\b(CREATE MATERIALIZED VIEW|CREATE SOURCE|CREATE SINK|CREATE INDEX|CREATE STREAM" + - "|CREATE VIEW|SHOW TABLES|DESCRIBE|SHOW)\\b.*?;[\\x00\\n]*" + - "|\\b(SELECT|INSERT|UPDATE|DELETE|ALTER|DROP|CREATE TABLE|CREATE SCHEMA|CREATE DATABASE)\\b.*?;[\\x00\\n]*" + - "|\\b(SET)\\b\\s+.*?=.*[\\x00\\n]*"; - - private final Pattern functionPattern = Pattern.compile(SQL_FUNCTION_PATTERN, Pattern.CASE_INSENSITIVE); - private final Pattern sqlPattern = Pattern.compile(SQL_COMMAND_PATTERN, Pattern.DOTALL); - - public RisingwaveSqlCommandParser() - { - } - - public String detectFirstSQLCommand( - String input) - { - String command = null; - Matcher matcher = sqlPattern.matcher(input); - - if (matcher.find()) - { - command = matcher.group(); - } - - return command; - } - - public RisingwaveCommandType decodeCommandType( - String input) - { - RisingwaveCommandType matchingCommand = RisingwaveCommandType.UNKNOWN_COMMAND; - - command: - for (RisingwaveCommandType command : RisingwaveCommandType.values()) - { - byte[] strBytes = input.getBytes(StandardCharsets.UTF_8); - byte[] prefixBytes = command.value(); - - boolean match = strBytes.length > prefixBytes.length; - - if (match) - { - for (int i = 0; i < prefixBytes.length; i++) - { - if (strBytes[i] != prefixBytes[i]) - { - match = false; - break; - } - } - } - - if (match) - { - matchingCommand = command; - break command; - } - } - - if (matchingCommand == RisingwaveCommandType.CREATE_FUNCTION_COMMAND && - isEmbeddedFunction(input)) - { - matchingCommand = RisingwaveCommandType.UNKNOWN_COMMAND; - } - - - return matchingCommand; - } - - private boolean isEmbeddedFunction( - String sqlQuery) - { - Matcher matcher = functionPattern.matcher(sqlQuery); - return matcher.find(); - } -} diff --git a/incubator/binding-risingwave/src/main/java/io/aklivity/zilla/runtime/binding/risingwave/internal/stream/RisingwaveProxyFactory.java b/incubator/binding-risingwave/src/main/java/io/aklivity/zilla/runtime/binding/risingwave/internal/stream/RisingwaveProxyFactory.java index 3037753452..5225180c8b 100644 --- a/incubator/binding-risingwave/src/main/java/io/aklivity/zilla/runtime/binding/risingwave/internal/stream/RisingwaveProxyFactory.java +++ b/incubator/binding-risingwave/src/main/java/io/aklivity/zilla/runtime/binding/risingwave/internal/stream/RisingwaveProxyFactory.java @@ -17,9 +17,6 @@ import static io.aklivity.zilla.runtime.engine.buffer.BufferPool.NO_SLOT; import static java.util.Objects.requireNonNull; -import java.io.InputStreamReader; -import java.io.Reader; -import java.io.StringReader; import java.nio.ByteOrder; import java.util.ArrayList; import java.util.LinkedHashMap; @@ -36,14 +33,16 @@ import org.agrona.collections.LongArrayQueue; import org.agrona.collections.Object2ObjectHashMap; import org.agrona.concurrent.UnsafeBuffer; -import org.agrona.io.DirectBufferInputStream; +import io.aklivity.zilla.runtime.binding.pgsql.parser.PgsqlParser; +import io.aklivity.zilla.runtime.binding.pgsql.parser.model.FunctionInfo; +import io.aklivity.zilla.runtime.binding.pgsql.parser.model.StreamInfo; +import io.aklivity.zilla.runtime.binding.pgsql.parser.model.TableInfo; +import io.aklivity.zilla.runtime.binding.pgsql.parser.model.ViewInfo; import io.aklivity.zilla.runtime.binding.risingwave.internal.RisingwaveConfiguration; import io.aklivity.zilla.runtime.binding.risingwave.internal.config.RisingwaveBindingConfig; import io.aklivity.zilla.runtime.binding.risingwave.internal.config.RisingwaveCommandType; import io.aklivity.zilla.runtime.binding.risingwave.internal.config.RisingwaveRouteConfig; -import io.aklivity.zilla.runtime.binding.risingwave.internal.statement.RisingwaveCreateTableCommand; -import io.aklivity.zilla.runtime.binding.risingwave.internal.statement.RisingwaveSqlCommandParser; import io.aklivity.zilla.runtime.binding.risingwave.internal.types.Flyweight; import io.aklivity.zilla.runtime.binding.risingwave.internal.types.OctetsFW; import io.aklivity.zilla.runtime.binding.risingwave.internal.types.String32FW; @@ -65,10 +64,6 @@ import io.aklivity.zilla.runtime.engine.buffer.BufferPool; import io.aklivity.zilla.runtime.engine.catalog.CatalogHandler; import io.aklivity.zilla.runtime.engine.config.BindingConfig; -import net.sf.jsqlparser.parser.CCJSqlParserManager; -import net.sf.jsqlparser.statement.Statement; -import net.sf.jsqlparser.statement.create.function.CreateFunction; -import net.sf.jsqlparser.statement.create.view.CreateView; public final class RisingwaveProxyFactory implements RisingwaveStreamFactory { @@ -86,10 +81,9 @@ public final class RisingwaveProxyFactory implements RisingwaveStreamFactory private static final OctetsFW EMPTY_OCTETS = new OctetsFW().wrap(EMPTY_BUFFER, 0, 0); private static final Consumer EMPTY_EXTENSION = ex -> {}; - private final RisingwaveSqlCommandParser sqlCommandParser = new RisingwaveSqlCommandParser(); - private final CCJSqlParserManager parserManager = new CCJSqlParserManager(); - private final DirectBufferInputStream inputStream = new DirectBufferInputStream(EMPTY_BUFFER); - private final Reader reader = new InputStreamReader(inputStream); + private final PgsqlParser parser = new PgsqlParser(); + private final List statements = new ArrayList<>(); + private final StringBuilder currentStatement = new StringBuilder(); private final BeginFW beginRO = new BeginFW(); private final DataFW dataRO = new DataFW(); @@ -731,24 +725,17 @@ private void doParseQuery( { final MutableDirectBuffer parserBuffer = bufferPool.buffer(parserSlot); - int progress = 0; - - parse: - while (progress <= parserSlotOffset) - { - final String query = parserBuffer.getStringWithoutLengthUtf8(progress, parserSlotOffset); - final String statement = sqlCommandParser.detectFirstSQLCommand(query); - int statementLength = statement != null ? statement.length() : parserSlotOffset; - - if (statement != null) + String sql = parserBuffer.getStringWithoutLengthAscii(0, parserSlotOffset); + splitStatements(sql) + .stream() + .findFirst() + .ifPresent(s -> { - final RisingwaveCommandType command = sqlCommandParser.decodeCommandType(statement); - final PgsqlTransform transform = clientTransforms.get(command); - transform.transform(this, traceId, authorizationId, parserBuffer, progress, statementLength); - break parse; - } - progress += statementLength; - } + String statement = s; + String command = parser.parseCommand(statement); + final PgsqlTransform transform = clientTransforms.get(RisingwaveCommandType.valueOf(command.getBytes())); + transform.transform(this, traceId, authorizationId, statement); + }); } } @@ -1497,47 +1484,45 @@ private void decodeCreateTableCommand( PgsqlServer server, long traceId, long authorization, - DirectBuffer buffer, - int offset, - int length) + String statement) { if (server.commandsProcessed == 6 || server.commandsProcessed == COMMAND_PROCESSED_ERRORED) { + final int length = statement.length(); server.onCommandCompleted(traceId, authorization, length, RisingwaveCompletionCommand.CREATE_TABLE_COMMAND); } else { final RisingwaveBindingConfig binding = server.binding; - final RisingwaveCreateTableCommand command = binding.createTable.parserCreateTable(buffer, offset, length); - final String primaryKey = binding.createTable.primaryKey(command.createTable); + final TableInfo tableInfo = parser.parseCreateTable(statement); String newStatement = ""; int progress = 0; if (server.commandsProcessed == 0) { - newStatement = binding.createTopic.generate(command); + newStatement = binding.createTopic.generate(tableInfo); } else if (server.commandsProcessed == 1) { - newStatement = binding.createSource.generateTableSource(server.database, command); + newStatement = binding.createSource.generateTableSource(server.database, tableInfo); } else if (server.commandsProcessed == 2) { - newStatement = binding.createView.generate(command); + newStatement = binding.createView.generate(tableInfo); } else if (server.commandsProcessed == 3) { - newStatement = binding.createTable.generate(command); + newStatement = binding.createTable.generate(tableInfo); } else if (server.commandsProcessed == 4) { - newStatement = binding.createSink.generate(command.createTable); + newStatement = binding.createSink.generate(tableInfo); } else if (server.commandsProcessed == 5) { - newStatement = binding.createSink.generate(server.database, primaryKey, command.createTable); + newStatement = binding.createSink.generate(server.database, tableInfo); } statementBuffer.putBytes(progress, newStatement.getBytes()); @@ -1556,30 +1541,29 @@ private void decodeCreateStreamCommand( PgsqlServer server, long traceId, long authorization, - DirectBuffer buffer, - int offset, - int length) + String statement) { if (server.commandsProcessed == 2 || server.commandsProcessed == COMMAND_PROCESSED_ERRORED) { + final int length = statement.length(); server.onCommandCompleted(traceId, authorization, length, RisingwaveCompletionCommand.CREATE_STREAM_COMMAND); } else { final RisingwaveBindingConfig binding = server.binding; - final RisingwaveCreateTableCommand command = binding.createTable.parserCreateTable(buffer, offset, length); + final StreamInfo streamInfo = parser.parseCreateStream(statement); String newStatement = ""; int progress = 0; if (server.commandsProcessed == 0) { - newStatement = binding.createTopic.generate(command.createTable); + newStatement = binding.createTopic.generate(streamInfo); } else if (server.commandsProcessed == 1) { - newStatement = binding.createSource.generateStreamSource(server.database, command); + newStatement = binding.createSource.generateStreamSource(server.database, streamInfo); } statementBuffer.putBytes(progress, newStatement.getBytes()); @@ -1598,20 +1582,19 @@ private void decodeCreateMaterializedViewCommand( PgsqlServer server, long traceId, long authorization, - DirectBuffer buffer, - int offset, - int length) + String statement) { if (server.commandsProcessed == 4 || server.commandsProcessed == COMMAND_PROCESSED_ERRORED) { + final int length = statement.length(); server.onCommandCompleted(traceId, authorization, length, RisingwaveCompletionCommand.CREATE_MATERIALIZED_VIEW_COMMAND); } else { final RisingwaveBindingConfig binding = server.binding; - final CreateView statement = (CreateView) parseStatement(buffer, offset, length); + final ViewInfo viewInfo = parser.parseCreateMaterializedView(statement); PgsqlFlushCommand typeCommand = ignoreFlushCommand; PgsqlDataCommand dataCommand = proxyDataCommand; @@ -1620,22 +1603,22 @@ private void decodeCreateMaterializedViewCommand( if (server.commandsProcessed == 0) { - newStatement = binding.createView.generate(statement); + newStatement = binding.createView.generate(viewInfo); } else if (server.commandsProcessed == 1) { - newStatement = binding.describeView.generate(statement); + newStatement = binding.describeView.generate(viewInfo); typeCommand = typeFlushCommand; dataCommand = rowDataCommand; server.columns.clear(); } else if (server.commandsProcessed == 2) { - newStatement = binding.createTopic.generate(statement, server.columns); + newStatement = binding.createTopic.generate(viewInfo, server.columns); } else if (server.commandsProcessed == 3) { - newStatement = binding.createSink.generate(server.database, server.columns, statement); + newStatement = binding.createSink.generate(server.database, server.columns, viewInfo); } statementBuffer.putBytes(progress, newStatement.getBytes()); @@ -1656,26 +1639,25 @@ private void decodeCreateFunctionCommand( PgsqlServer server, long traceId, long authorization, - DirectBuffer buffer, - int offset, - int length) + String statement) { if (server.commandsProcessed == 1 || server.commandsProcessed == COMMAND_PROCESSED_ERRORED) { + final int length = statement.length(); server.onCommandCompleted(traceId, authorization, length, RisingwaveCompletionCommand.CREATE_FUNCTION_COMMAND); } else { final RisingwaveBindingConfig binding = server.binding; - final CreateFunction statement = (CreateFunction) parseStatement(buffer, offset, length); + final FunctionInfo functionInfo = parser.parseCreateFunction(statement); String newStatement = ""; int progress = 0; if (server.commandsProcessed == 0) { - newStatement = binding.createFunction.generate(statement); + newStatement = binding.createFunction.generate(functionInfo); } statementBuffer.putBytes(progress, newStatement.getBytes()); @@ -1694,10 +1676,10 @@ private void decodeUnknownCommand( PgsqlServer server, long traceId, long authorization, - DirectBuffer buffer, - int offset, - int length) + String statement) { + final int length = statement.length(); + if (server.commandsProcessed == 1 || server.commandsProcessed == COMMAND_PROCESSED_ERRORED) { @@ -1705,11 +1687,13 @@ private void decodeUnknownCommand( } else { + statementBuffer.putBytes(0, statement.getBytes()); + final RisingwaveRouteConfig route = - server.binding.resolve(authorization, buffer, offset, length); + server.binding.resolve(authorization, statementBuffer, 0, length); final PgsqlClient client = server.streamsByRouteIds.get(route.id); - client.doPgsqlQuery(traceId, authorization, buffer, offset, length); + client.doPgsqlQuery(traceId, authorization, statementBuffer, 0, length); client.completionCommand = proxyFlushCommand; } } @@ -1809,42 +1793,50 @@ private void proxyDataCommand( server.doAppData(routedId, traceId, authorization, flags, buffer, offset, limit, extension); } - private Statement parseStatement( - DirectBuffer buffer, - int offset, - int length) + public List splitStatements( + String sql) { - Statement statement = null; - try + statements.clear(); + currentStatement.setLength(0); + + boolean inDollarQuotes = false; + int length = sql.length(); + int start = 0; + + for (int i = 0; i < length; i++) { - //TODO: Avoid object creation - String query = buffer.getStringWithoutLengthUtf8(offset, length); - RisingwaveCommandType commandType = sqlCommandParser.decodeCommandType(query); - if (commandType.equals(RisingwaveCommandType.CREATE_MATERIALIZED_VIEW_COMMAND)) - { - String sql = buffer.getStringWithoutLengthUtf8(offset, length); - // Replace "CREATE MATERIALIZED VIEW" with "CREATE VIEW" for compatibility - sql = sql.replace("CREATE MATERIALIZED VIEW", "CREATE VIEW"); - // Remove "IF NOT EXISTS" clause because JSqlParser doesn't support it - sql = sql.replace("IF NOT EXISTS", ""); - statement = parserManager.parse(new StringReader(sql)); - } - else if (commandType.equals(RisingwaveCommandType.CREATE_FUNCTION_COMMAND)) + char c = sql.charAt(i); + + if (c == '$' && i + 1 < length && sql.charAt(i + 1) == '$') { - query = query.substring(0, query.length() - 1); - statement = parserManager.parse(new StringReader(query)); + inDollarQuotes = !inDollarQuotes; + i++; } - else + else if (c == ';' && !inDollarQuotes) { - inputStream.wrap(buffer, offset, length); - statement = parserManager.parse(reader); + int j = i + 1; + while (j < length && Character.isWhitespace(sql.charAt(j))) + { + j++; + } + + if (j < length && sql.charAt(j) == '\0') + { + i = j; + } + + statements.add(sql.substring(start, i + 1)); + start = j; + i = j - 1; } } - catch (Exception ignored) + + if (start < length) { + statements.add(sql.substring(start, length)); } - return statement; + return statements; } @FunctionalInterface @@ -1854,9 +1846,7 @@ void transform( PgsqlServer server, long traceId, long authorization, - DirectBuffer writeBuffer, - int offset, - int length); + String statement); } @FunctionalInterface diff --git a/incubator/binding-risingwave/src/main/moditect/module-info.java b/incubator/binding-risingwave/src/main/moditect/module-info.java index 77bc6206bd..0155d5c356 100644 --- a/incubator/binding-risingwave/src/main/moditect/module-info.java +++ b/incubator/binding-risingwave/src/main/moditect/module-info.java @@ -14,8 +14,8 @@ */ module io.aklivity.zilla.runtime.binding.risingwave { - requires net.sf.jsqlparser; requires io.aklivity.zilla.runtime.engine; + requires io.aklivity.zilla.runtime.binding.pgsql; exports io.aklivity.zilla.runtime.binding.risingwave.config; diff --git a/incubator/binding-risingwave/src/test/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveCreateFunctionTemplateTest.java b/incubator/binding-risingwave/src/test/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveCreateFunctionTemplateTest.java new file mode 100644 index 0000000000..b0defa56e0 --- /dev/null +++ b/incubator/binding-risingwave/src/test/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveCreateFunctionTemplateTest.java @@ -0,0 +1,178 @@ +/* + * Copyright 2021-2023 Aklivity Inc + * + * Licensed under the Aklivity Community License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of the + * License at + * + * https://www.aklivity.io/aklivity-community-license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.aklivity.zilla.runtime.binding.risingwave.internal.statement; + +import static org.junit.Assert.assertEquals; + +import java.util.List; + +import org.junit.BeforeClass; +import org.junit.Test; + +import io.aklivity.zilla.runtime.binding.pgsql.parser.model.FunctionArgument; +import io.aklivity.zilla.runtime.binding.pgsql.parser.model.FunctionInfo; +import io.aklivity.zilla.runtime.binding.risingwave.config.RisingwaveUdfConfig; + + +public class RisingwaveCreateFunctionTemplateTest +{ + private static RisingwaveCreateFunctionTemplate template; + + @BeforeClass + public static void setUp() + { + template = new RisingwaveCreateFunctionTemplate(List.of( + RisingwaveUdfConfig.builder().server("http://localhost:8815").language("java").build(), + RisingwaveUdfConfig.builder().server("http://localhost:8816").language("python").build())); + } + + @Test + public void shouldGenerateFunctionWithValidFunctionInfo() + { + FunctionInfo functionInfo = new FunctionInfo( + "test_function", + List.of(new FunctionArgument("arg1", "INT")), + "INT", + List.of(), + "test_function", + "java"); + String expectedSQL = """ + CREATE FUNCTION test_function(arg1 INT) + RETURNS INT + AS test_function + LANGUAGE java + USING LINK 'http://localhost:8815';\u0000"""; + + String actualSQL = template.generate(functionInfo); + + assertEquals(expectedSQL, actualSQL); + } + + @Test + public void shouldGenerateFunctionWithTableReturnType() + { + FunctionInfo functionInfo = new FunctionInfo( + "test_function", + List.of(new FunctionArgument("arg1", "INT")), + "INT", + List.of(new FunctionArgument("tab1", "INT")), + "test_function", + "java"); + + String expectedSQL = """ + CREATE FUNCTION test_function(arg1 INT) + RETURNS TABLE (tab1 INT) + AS test_function + LANGUAGE java + USING LINK 'http://localhost:8815';\u0000"""; + + String actualSQL = template.generate(functionInfo); + + assertEquals(expectedSQL, actualSQL); + } + + @Test + public void shouldGenerateFunctionWithTableAsReturnType() + { + FunctionInfo functionInfo = new FunctionInfo( + "test_function", + List.of(new FunctionArgument("arg1", "INT")), + "INT", + List.of(), + "test_function", + "java"); + + String expectedSQL = """ + CREATE FUNCTION test_function(arg1 INT) + RETURNS INT + AS test_function + LANGUAGE java + USING LINK 'http://localhost:8815';\u0000"""; + + String actualSQL = template.generate(functionInfo); + + assertEquals(expectedSQL, actualSQL); + } + + @Test + public void shouldGenerateFunctionWithMultipleArguments() + { + FunctionInfo functionInfo = new FunctionInfo( + "test_function", + List.of(new FunctionArgument("arg1", "INT"), new FunctionArgument("arg2", "STRING")), + "STRING", + List.of(), + "test_function", + "python"); + + String expectedSQL = """ + CREATE FUNCTION test_function(arg1 INT, arg2 STRING) + RETURNS STRING + AS test_function + LANGUAGE python + USING LINK 'http://localhost:8816';\u0000"""; + + String actualSQL = template.generate(functionInfo); + + assertEquals(expectedSQL, actualSQL); + } + + @Test + public void shouldGenerateFunctionWithUnnamedArguments() + { + FunctionInfo functionInfo = new FunctionInfo( + "test_function", + List.of(new FunctionArgument(null, "INT"), new FunctionArgument(null, "STRING")), + "STRING", + List.of(), + "test_function", + "java"); + + String expectedSQL = """ + CREATE FUNCTION test_function(INT, STRING) + RETURNS STRING + AS test_function + LANGUAGE java + USING LINK 'http://localhost:8815';\u0000"""; + + String actualSQL = template.generate(functionInfo); + + assertEquals(expectedSQL, actualSQL); + } + + @Test + public void shouldGenerateFunctionWithEmptyArguments() + { + FunctionInfo functionInfo = new FunctionInfo( + "test_function", + List.of(), + "VOID", + List.of(), + "test_function", + "python"); + + String expectedSQL = """ + CREATE FUNCTION test_function() + RETURNS VOID + AS test_function + LANGUAGE python + USING LINK 'http://localhost:8816';\u0000"""; + + String actualSQL = template.generate(functionInfo); + + assertEquals(expectedSQL, actualSQL); + } + +} diff --git a/incubator/binding-risingwave/src/test/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveCreateMaterializedViewTemplateTest.java b/incubator/binding-risingwave/src/test/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveCreateMaterializedViewTemplateTest.java new file mode 100644 index 0000000000..079d0011f5 --- /dev/null +++ b/incubator/binding-risingwave/src/test/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveCreateMaterializedViewTemplateTest.java @@ -0,0 +1,93 @@ +/* + * Copyright 2021-2023 Aklivity Inc + * + * Licensed under the Aklivity Community License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of the + * License at + * + * https://www.aklivity.io/aklivity-community-license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.aklivity.zilla.runtime.binding.risingwave.internal.statement; + +import static org.junit.Assert.assertEquals; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +import org.junit.Ignore; +import org.junit.Test; + +import io.aklivity.zilla.runtime.binding.pgsql.parser.model.TableInfo; +import io.aklivity.zilla.runtime.binding.pgsql.parser.model.ViewInfo; + +public class RisingwaveCreateMaterializedViewTemplateTest +{ + private final RisingwaveCreateMaterializedViewTemplate template = new RisingwaveCreateMaterializedViewTemplate(); + + @Test + public void shouldGenerateMaterializedViewWithValidViewInfo() + { + ViewInfo viewInfo = new ViewInfo("test_view", "SELECT * FROM test_table"); + String expectedSQL = """ + CREATE MATERIALIZED VIEW IF NOT EXISTS test_view AS SELECT * FROM test_table;\u0000"""; + + String actualSQL = template.generate(viewInfo); + + assertEquals(expectedSQL, actualSQL); + } + + @Ignore("TODO") + @Test + public void shouldGenerateMaterializedViewWithValidTableInfo() + { + TableInfo tableInfo = new TableInfo( + "test_table", + Map.of("id", "INT", "name", "STRING"), + Set.of("id")); + String expectedSQL = """ + CREATE MATERIALIZED VIEW IF NOT EXISTS test_table_view AS SELECT id, name FROM test_table_source;\u0000"""; + + String actualSQL = template.generate(tableInfo); + + assertEquals(expectedSQL, actualSQL); + } + + @Test + public void shouldGenerateMaterializedViewWithEmptyColumns() + { + TableInfo tableInfo = new TableInfo("empty_table", Map.of(), Set.of()); + String expectedSQL = """ + CREATE MATERIALIZED VIEW IF NOT EXISTS empty_table_view AS SELECT * FROM empty_table_source;\u0000"""; + + String actualSQL = template.generate(tableInfo); + + assertEquals(expectedSQL, actualSQL); + } + + @Test + public void shouldGenerateMaterializedViewWithIncludes() + { + Map columns = new LinkedHashMap<>(); + columns.put("id", "INT"); + columns.put("zilla_correlation_id", "VARCHAR"); + columns.put("zilla_identity", "VARCHAR"); + columns.put("zilla_timestamp", "TIMESTAMP"); + + TableInfo tableInfo = new TableInfo("test_table", columns, Set.of("id")); + String expectedSQL = "CREATE MATERIALIZED VIEW IF NOT EXISTS test_table_view AS SELECT id," + + " COALESCE(zilla_correlation_id, zilla_correlation_id_header::varchar) as zilla_correlation_id," + + " COALESCE(zilla_identity, zilla_identity_header::varchar) as zilla_identity," + + " COALESCE(zilla_timestamp, zilla_timestamp_timestamp::varchar) as zilla_timestamp" + + " FROM test_table_source;\u0000"; + + String actualSQL = template.generate(tableInfo); + + assertEquals(expectedSQL, actualSQL); + } +} diff --git a/incubator/binding-risingwave/src/test/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveCreateSourceTemplateTest.java b/incubator/binding-risingwave/src/test/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveCreateSourceTemplateTest.java new file mode 100644 index 0000000000..b6f7d8ed11 --- /dev/null +++ b/incubator/binding-risingwave/src/test/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveCreateSourceTemplateTest.java @@ -0,0 +1,162 @@ +/* + * Copyright 2021-2023 Aklivity Inc + * + * Licensed under the Aklivity Community License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of the + * License at + * + * https://www.aklivity.io/aklivity-community-license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.aklivity.zilla.runtime.binding.risingwave.internal.statement; + +import static org.junit.Assert.assertEquals; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +import org.junit.BeforeClass; +import org.junit.Test; + +import io.aklivity.zilla.runtime.binding.pgsql.parser.model.StreamInfo; +import io.aklivity.zilla.runtime.binding.pgsql.parser.model.TableInfo; + +public class RisingwaveCreateSourceTemplateTest +{ + private static RisingwaveCreateSourceTemplate template; + + @BeforeClass + public static void setUp() + { + template = new RisingwaveCreateSourceTemplate("localhost:9092", "http://localhost:8081", 1627846260000L); + } + + @Test + public void shouldGenerateStreamSourceWithValidStreamInfo() + { + StreamInfo streamInfo = new StreamInfo("test_stream", Map.of("id", "INT", "name", "STRING")); + String expectedSQL = """ + CREATE SOURCE IF NOT EXISTS test_stream (*) + WITH ( + connector='kafka', + properties.bootstrap.server='localhost:9092', + topic='test_db.test_stream', + scan.startup.mode='latest', + scan.startup.timestamp.millis='1627846260000' + ) FORMAT PLAIN ENCODE AVRO ( + schema.registry = 'http://localhost:8081' + );\u0000"""; + + String actualSQL = template.generateStreamSource("test_db", streamInfo); + + assertEquals(expectedSQL, actualSQL); + } + + @Test + public void shouldGenerateTableSourceWithValidTableInfoAndIncludes() + { + Map columns = new LinkedHashMap<>(); + columns.put("id", "INT"); + columns.put("zilla_correlation_id", "VARCHAR"); + columns.put("zilla_identity", "VARCHAR"); + columns.put("zilla_timestamp", "TIMESTAMP"); + + TableInfo tableInfo = new TableInfo( + "test_table", columns, Set.of("id")); + String expectedSQL = """ + CREATE SOURCE IF NOT EXISTS test_table_source (*) + INCLUDE header 'zilla:correlation-id' AS zilla_correlation_id_header + INCLUDE header 'zilla:identity' AS zilla_identity_header + INCLUDE timestamp AS zilla_timestamp_timestamp + WITH ( + connector='kafka', + properties.bootstrap.server='localhost:9092', + topic='test_db.test_table', + scan.startup.mode='latest', + scan.startup.timestamp.millis='1627846260000' + ) FORMAT PLAIN ENCODE AVRO ( + schema.registry = 'http://localhost:8081' + );\u0000"""; + + String actualSQL = template.generateTableSource("test_db", tableInfo); + + assertEquals(expectedSQL, actualSQL); + } + + @Test + public void shouldGenerateStreamSourceWithEmptyColumnsReturnsSQLWithoutIncludes() + { + StreamInfo streamInfo = new StreamInfo("empty_stream", Map.of()); + String expectedSQL = """ + CREATE SOURCE IF NOT EXISTS empty_stream (*) + WITH ( + connector='kafka', + properties.bootstrap.server='localhost:9092', + topic='test_db.empty_stream', + scan.startup.mode='latest', + scan.startup.timestamp.millis='1627846260000' + ) FORMAT PLAIN ENCODE AVRO ( + schema.registry = 'http://localhost:8081' + );\u0000"""; + + String actualSQL = template.generateStreamSource("test_db", streamInfo); + + assertEquals(expectedSQL, actualSQL); + } + + @Test + public void shouldGenerateStreamSourceWithEmptyColumnsReturnsSQLWithIncludes() + { + Map columns = new LinkedHashMap<>(); + columns.put("id", "INT"); + columns.put("zilla_correlation_id", "VARCHAR"); + columns.put("zilla_identity", "VARCHAR"); + columns.put("zilla_timestamp", "TIMESTAMP"); + + String expectedSQL = """ + CREATE SOURCE IF NOT EXISTS include_stream (*) + INCLUDE header 'zilla:correlation-id' AS zilla_correlation_id + INCLUDE header 'zilla:identity' AS zilla_identity + INCLUDE timestamp AS zilla_timestamp + WITH ( + connector='kafka', + properties.bootstrap.server='localhost:9092', + topic='test_db.include_stream', + scan.startup.mode='latest', + scan.startup.timestamp.millis='1627846260000' + ) FORMAT PLAIN ENCODE AVRO ( + schema.registry = 'http://localhost:8081' + );\u0000"""; + StreamInfo streamInfo = new StreamInfo("include_stream", columns); + + String actualSQL = template.generateStreamSource("test_db", streamInfo); + + assertEquals(expectedSQL, actualSQL); + } + + @Test + public void shouldGenerateTableSourceWithEmptyColumnsAndWithoutIncludes() + { + TableInfo tableInfo = new TableInfo("empty_table", Map.of(), Set.of()); + String expectedSQL = """ + CREATE SOURCE IF NOT EXISTS empty_table_source (*) + WITH ( + connector='kafka', + properties.bootstrap.server='localhost:9092', + topic='test_db.empty_table', + scan.startup.mode='latest', + scan.startup.timestamp.millis='1627846260000' + ) FORMAT PLAIN ENCODE AVRO ( + schema.registry = 'http://localhost:8081' + );\u0000"""; + + String actualSQL = template.generateTableSource("test_db", tableInfo); + + assertEquals(expectedSQL, actualSQL); + } +} diff --git a/incubator/binding-risingwave/src/test/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveCreateTableTemplateTest.java b/incubator/binding-risingwave/src/test/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveCreateTableTemplateTest.java new file mode 100644 index 0000000000..231384f573 --- /dev/null +++ b/incubator/binding-risingwave/src/test/java/io/aklivity/zilla/runtime/binding/risingwave/internal/statement/RisingwaveCreateTableTemplateTest.java @@ -0,0 +1,96 @@ +/* + * Copyright 2021-2023 Aklivity Inc + * + * Licensed under the Aklivity Community License (the "License"); you may not use + * this file except in compliance with the License. You may obtain a copy of the + * License at + * + * https://www.aklivity.io/aklivity-community-license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package io.aklivity.zilla.runtime.binding.risingwave.internal.statement; + +import static org.junit.Assert.assertEquals; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; + +import io.aklivity.zilla.runtime.binding.pgsql.parser.model.TableInfo; + +public class RisingwaveCreateTableTemplateTest +{ + private static RisingwaveCreateTableTemplate template; + + @BeforeClass + public static void setUp() + { + template = new RisingwaveCreateTableTemplate(); + } + + @Test + public void shouldGenerateTableWithValidTableInfo() + { + Map columns = new LinkedHashMap<>(); + columns.put("id", "INT"); + columns.put("name", "STRING"); + + TableInfo tableInfo = new TableInfo( + "test_table", + columns, + Set.of("id")); + String expectedSQL = """ + CREATE TABLE IF NOT EXISTS test_table (id INT, name STRING, PRIMARY KEY (id));\u0000"""; + + String actualSQL = template.generate(tableInfo); + + assertEquals(expectedSQL, actualSQL); + } + + @Test + public void shouldGenerateTableWithoutPrimaryKey() + { + Map columns = new LinkedHashMap<>(); + columns.put("id", "INT"); + columns.put("name", "STRING"); + + TableInfo tableInfo = new TableInfo( + "test_table", + columns, + Set.of()); + String expectedSQL = """ + CREATE TABLE IF NOT EXISTS test_table (id INT, name STRING);\u0000"""; + + String actualSQL = template.generate(tableInfo); + + assertEquals(expectedSQL, actualSQL); + } + + @Ignore("TODO") + @Test + public void shouldGenerateTableWithMultiplePrimaryKeys() + { + Map columns = new LinkedHashMap<>(); + columns.put("id", "INT"); + columns.put("name", "STRING"); + + TableInfo tableInfo = new TableInfo( + "test_table", + columns, + Set.of("id", "name")); + String expectedSQL = """ + CREATE TABLE IF NOT EXISTS test_table (id INT, name STRING, PRIMARY KEY (id));\u0000"""; + + String actualSQL = template.generate(tableInfo); + + assertEquals(expectedSQL, actualSQL); + } +} diff --git a/pom.xml b/pom.xml index 7fe8c4d9ca..d4f4318538 100644 --- a/pom.xml +++ b/pom.xml @@ -204,11 +204,6 @@ jmh-profilers 0.1.4 - - com.github.jsqlparser - jsqlparser - 5.0 - io.aklivity.k3po driver