From 94f281e33babf0abe19afe2a36ac39032bd05b03 Mon Sep 17 00:00:00 2001 From: Veyndan Stuart Date: Sun, 30 Aug 2020 00:10:27 +0200 Subject: [PATCH] Support PostgreSQL RETURNING --- .../sql/psi/core/postgresql/PostgreSql.bnf | 49 +++++++++++++++++-- .../psi/core/psi/mixins/ResultColumnMixin.kt | 7 ++- .../fixtures_postgresql/returning/Sample.s | 18 +++++++ 3 files changed, 66 insertions(+), 8 deletions(-) create mode 100644 core/src/test/fixtures_postgresql/returning/Sample.s diff --git a/core/src/main/kotlin/com/alecstrong/sql/psi/core/postgresql/PostgreSql.bnf b/core/src/main/kotlin/com/alecstrong/sql/psi/core/postgresql/PostgreSql.bnf index 40ced541..80447602 100644 --- a/core/src/main/kotlin/com/alecstrong/sql/psi/core/postgresql/PostgreSql.bnf +++ b/core/src/main/kotlin/com/alecstrong/sql/psi/core/postgresql/PostgreSql.bnf @@ -8,20 +8,41 @@ psiClassPrefix = "PostgreSql" parserImports=[ - "static com.alecstrong.sql.psi.core.psi.SqlTypes.CONSTRAINT" - "static com.alecstrong.sql.psi.core.psi.SqlTypes.PRIMARY" - "static com.alecstrong.sql.psi.core.psi.SqlTypes.KEY" + "static com.alecstrong.sql.psi.core.psi.SqlTypes.ABORT" + "static com.alecstrong.sql.psi.core.psi.SqlTypes.AS" "static com.alecstrong.sql.psi.core.psi.SqlTypes.ASC" + "static com.alecstrong.sql.psi.core.psi.SqlTypes.BY" + "static com.alecstrong.sql.psi.core.psi.SqlTypes.COLLATE" + "static com.alecstrong.sql.psi.core.psi.SqlTypes.CONSTRAINT" + "static com.alecstrong.sql.psi.core.psi.SqlTypes.DELETE" "static com.alecstrong.sql.psi.core.psi.SqlTypes.DESC" + "static com.alecstrong.sql.psi.core.psi.SqlTypes.FAIL" + "static com.alecstrong.sql.psi.core.psi.SqlTypes.FROM" + "static com.alecstrong.sql.psi.core.psi.SqlTypes.IGNORE" + "static com.alecstrong.sql.psi.core.psi.SqlTypes.INSERT" + "static com.alecstrong.sql.psi.core.psi.SqlTypes.INTO" + "static com.alecstrong.sql.psi.core.psi.SqlTypes.KEY" + "static com.alecstrong.sql.psi.core.psi.SqlTypes.LIMIT" "static com.alecstrong.sql.psi.core.psi.SqlTypes.NOT" "static com.alecstrong.sql.psi.core.psi.SqlTypes.NULL" + "static com.alecstrong.sql.psi.core.psi.SqlTypes.OFFSET" + "static com.alecstrong.sql.psi.core.psi.SqlTypes.OR" + "static com.alecstrong.sql.psi.core.psi.SqlTypes.ORDER" + "static com.alecstrong.sql.psi.core.psi.SqlTypes.PRIMARY" + "static com.alecstrong.sql.psi.core.psi.SqlTypes.REPLACE" + "static com.alecstrong.sql.psi.core.psi.SqlTypes.ROLLBACK" + "static com.alecstrong.sql.psi.core.psi.SqlTypes.SET" "static com.alecstrong.sql.psi.core.psi.SqlTypes.UNIQUE" - "static com.alecstrong.sql.psi.core.psi.SqlTypes.COLLATE" + "static com.alecstrong.sql.psi.core.psi.SqlTypes.UPDATE" + "static com.alecstrong.sql.psi.core.psi.SqlTypes.WHERE" ] } overrides ::= type_name | column_constraint | bind_parameter + | delete_stmt_limited + | insert_stmt + | update_stmt_limited column_constraint ::= [ CONSTRAINT {identifier} ] ( PRIMARY KEY [ ASC | DESC ] {conflict_clause} | @@ -79,3 +100,23 @@ date_data_type ::= 'DATE' | (('TIME' | 'TIMESTAMP') [ '(' {signed_number} ')' ]) boolean_data_type ::= 'BOOLEAN' | 'BOOL' json_data_type ::= 'JSON' + +delete_stmt_limited ::= [ {with_clause} ] DELETE FROM {qualified_table_name} [ WHERE <> ] [ [ ORDER BY {ordering_term} ( ',' {ordering_term} ) * ] LIMIT <> [ ( OFFSET | ',' ) <> ] ] [ returning_clause ] { + extends = "com.alecstrong.sql.psi.core.psi.impl.SqlDeleteStmtLimitedImpl" + implements = "com.alecstrong.sql.psi.core.psi.SqlDeleteStmtLimited" + override = true +} + +insert_stmt ::= [ {with_clause} ] ( INSERT OR REPLACE | REPLACE | INSERT OR ROLLBACK | INSERT OR ABORT | INSERT OR FAIL | INSERT OR IGNORE | INSERT ) INTO [ {database_name} '.' ] {table_name} [ AS {table_alias} ] [ '(' {column_name} ( ',' {column_name} ) * ')' ] {insert_stmt_values} [ returning_clause ] { + extends = "com.alecstrong.sql.psi.core.psi.impl.SqlInsertStmtImpl" + implements = "com.alecstrong.sql.psi.core.psi.SqlInsertStmt" + override = true +} + +update_stmt_limited ::= [ {with_clause} ] UPDATE [ OR ROLLBACK | OR ABORT | OR REPLACE | OR FAIL | OR IGNORE ] {qualified_table_name} SET {column_name} '=' {setter_expression} {update_stmt_subsequent_setter} * [ WHERE <> ] [ [ ORDER BY {ordering_term} ( ',' {ordering_term} ) * ] LIMIT <> [ ( OFFSET | ',' ) <> ] ] [ returning_clause ] { + extends = "com.alecstrong.sql.psi.core.psi.impl.SqlUpdateStmtLimitedImpl" + implements = "com.alecstrong.sql.psi.core.psi.SqlUpdateStmtLimited" + override = true +} + +returning_clause ::= 'RETURNING' {result_column} ( ',' {result_column} ) * diff --git a/core/src/main/kotlin/com/alecstrong/sql/psi/core/psi/mixins/ResultColumnMixin.kt b/core/src/main/kotlin/com/alecstrong/sql/psi/core/psi/mixins/ResultColumnMixin.kt index f4154c5b..d3b47640 100644 --- a/core/src/main/kotlin/com/alecstrong/sql/psi/core/psi/mixins/ResultColumnMixin.kt +++ b/core/src/main/kotlin/com/alecstrong/sql/psi/core/psi/mixins/ResultColumnMixin.kt @@ -12,12 +12,11 @@ internal abstract class ResultColumnMixin( node: ASTNode ) : SqlCompositeElementImpl(node), SqlResultColumn { - override fun getParent(): SelectStmtMixin? = super.getParent() as SelectStmtMixin? - private val queryExposed: Collection by ModifiableFileLazy(containingFile) lazy@{ + val parent = parent as? SelectStmtMixin ?: return@lazy emptyList() tableName?.let { tableNameElement -> // table_name '.' '*' - return@lazy parent!!.fromQuery().filter { it.table?.name == tableNameElement.name } + return@lazy parent.fromQuery().filter { it.table?.name == tableNameElement.name } } expr?.let { var column: QueryElement.QueryColumn @@ -38,7 +37,7 @@ internal abstract class ResultColumnMixin( } // * - val queryAvailable = parent!!.fromQuery() + val queryAvailable = parent.fromQuery() if (queryAvailable.size <= 1) { return@lazy queryAvailable } diff --git a/core/src/test/fixtures_postgresql/returning/Sample.s b/core/src/test/fixtures_postgresql/returning/Sample.s new file mode 100644 index 00000000..18d2fa8f --- /dev/null +++ b/core/src/test/fixtures_postgresql/returning/Sample.s @@ -0,0 +1,18 @@ +CREATE TABLE account( + id SERIAL PRIMARY KEY, + name TEXT NOT NULL, + balance INT NOT NULL +); + +INSERT INTO account(name, balance) +VALUES ('Jane Doe', 0), ('John Doe', 0) +RETURNING id; + +UPDATE account +SET balance = 100 +WHERE name = 'Jane Doe' +RETURNING *; + +DELETE FROM account +WHERE name = 'Jane Doe' +RETURNING id, name;