From 436c127c21ec7f5efa506bd43b23ed705078bd21 Mon Sep 17 00:00:00 2001 From: Mathias Fussenegger Date: Thu, 22 Jan 2015 15:32:58 +0100 Subject: [PATCH] support ON DUPLICATE KEY in the parser --- .../main/java/io/crate/sql/parser/Statement.g | 12 +++++++++-- .../io/crate/sql/parser/StatementBuilder.g | 20 +++++++++++++++---- .../main/java/io/crate/sql/tree/Insert.java | 11 ++++++++-- .../io/crate/sql/tree/InsertFromSubquery.java | 10 +++++++--- .../io/crate/sql/tree/InsertFromValues.java | 7 +++++-- .../sql/parser/TestStatementBuilder.java | 5 +++++ .../analyze/InsertFromSubQueryAnalyzer.java | 4 +++- .../analyze/InsertFromValuesAnalyzer.java | 3 +++ .../InsertFromSubQueryAnalyzerTest.java | 5 +++++ .../analyze/InsertFromValuesAnalyzerTest.java | 6 ++++++ 10 files changed, 69 insertions(+), 14 deletions(-) diff --git a/sql-parser/src/main/java/io/crate/sql/parser/Statement.g b/sql-parser/src/main/java/io/crate/sql/parser/Statement.g index b1d76b28aecc..1d27e0b644fb 100644 --- a/sql-parser/src/main/java/io/crate/sql/parser/Statement.g +++ b/sql-parser/src/main/java/io/crate/sql/parser/Statement.g @@ -115,6 +115,7 @@ tokens { ARRAY_NOT_LIKE; ARRAY_LITERAL; OBJECT_LITERAL; + ON_DUP_KEY; KEY_VALUE; MATCH; MATCH_PREDICATE_IDENT; @@ -586,7 +587,7 @@ specialFunction | CURRENT_TIMESTAMP ('(' integer ')')? -> ^(CURRENT_TIMESTAMP integer?) | SUBSTRING '(' expr FROM expr (FOR expr)? ')' -> ^(FUNCTION_CALL ^(QNAME IDENT["substr"]) expr expr expr?) | EXTRACT '(' ident FROM expr ')' -> ^(EXTRACT ident expr) - | CAST '(' expr AS dataType ')' -> ^(CAST expr dataType) + | CAST '(' expr AS dataType ')' -> ^(CAST expr dataType) ; @@ -745,7 +746,11 @@ objectKeyValue ; insertStmt - : INSERT INTO table identList? insertSource -> ^(INSERT insertSource table identList?) + : INSERT INTO table identList? insertSource onDuplicateKey? -> ^(INSERT insertSource table identList? onDuplicateKey?) + ; + +onDuplicateKey + : ON DUPLICATE KEY UPDATE assignmentList -> ^(ON_DUP_KEY assignmentList) ; insertSource @@ -1021,6 +1026,7 @@ nonReserved | PRECEDING | RANGE | REFRESH | ROW | ROWS | SCHEMA | SCHEMAS | SECOND | SHARDS | SHOW | STRICT | SYSTEM | TABLES | TABLESAMPLE | TEXT | TIME | TIMESTAMP | TO | TOKENIZER | TOKEN_FILTERS | TYPE | VIEW | YEAR + | DUPLICATE | KEY | VALUES ; SELECT: 'SELECT'; @@ -1157,6 +1163,8 @@ INTO: 'INTO'; VALUES: 'VALUES'; DELETE: 'DELETE'; UPDATE: 'UPDATE'; +KEY: 'KEY'; +DUPLICATE: 'DUPLICATE'; SET: 'SET'; RESET: 'RESET'; COPY: 'COPY'; diff --git a/sql-parser/src/main/java/io/crate/sql/parser/StatementBuilder.g b/sql-parser/src/main/java/io/crate/sql/parser/StatementBuilder.g index 69c47db2a894..db552ec337ec 100644 --- a/sql-parser/src/main/java/io/crate/sql/parser/StatementBuilder.g +++ b/sql-parser/src/main/java/io/crate/sql/parser/StatementBuilder.g @@ -657,16 +657,28 @@ dropTable returns [Statement value] ; insert returns [Statement value] - : ^(INSERT values=insertValues namedTable cols=columnIdentList?) + : ^(INSERT values=insertValues namedTable cols=columnIdentList? onDuplicateKey?) { - $value = new InsertFromValues($namedTable.value, $values.value, $cols.value); + $value = new InsertFromValues( + $namedTable.value, + $values.value, + $cols.value, + $onDuplicateKey.value); } - | ^(INSERT subQuery=query namedTable cols=columnIdentList?) + | ^(INSERT subQuery=query namedTable cols=columnIdentList? onDuplicateKey?) { - $value = new InsertFromSubquery($namedTable.value, $subQuery.value, $cols.value); + $value = new InsertFromSubquery( + $namedTable.value, + $subQuery.value, + $cols.value, + $onDuplicateKey.value); } ; +onDuplicateKey returns [List value] + : ^(ON_DUP_KEY assignmentList) { $value = $assignmentList.value; } + ; + insertValues returns [List value = new ArrayList<>()] : ^(INSERT_VALUES (valuesList { $value.add($valuesList.value); })+) ; diff --git a/sql-parser/src/main/java/io/crate/sql/tree/Insert.java b/sql-parser/src/main/java/io/crate/sql/tree/Insert.java index 75927057023b..962a1b988fba 100644 --- a/sql-parser/src/main/java/io/crate/sql/tree/Insert.java +++ b/sql-parser/src/main/java/io/crate/sql/tree/Insert.java @@ -21,6 +21,7 @@ package io.crate.sql.tree; +import com.google.common.base.MoreObjects; import com.google.common.base.Objects; import com.google.common.collect.ImmutableList; @@ -31,12 +32,14 @@ public abstract class Insert extends Statement { protected final Table table; + protected final List onDuplicateKeyAssignments; protected final List columns; - public Insert(Table table, @Nullable List columns) { + public Insert(Table table, @Nullable List columns, @Nullable List onDuplicateKeyAssignments) { this.table = table; - this.columns = Objects.firstNonNull(columns, ImmutableList.of()); + this.onDuplicateKeyAssignments = MoreObjects.firstNonNull(onDuplicateKeyAssignments, ImmutableList.of()); + this.columns = MoreObjects.firstNonNull(columns, ImmutableList.of()); } public Table table() { @@ -47,6 +50,10 @@ public List columns() { return columns; } + public List onDuplicateKeyAssignments() { + return onDuplicateKeyAssignments; + } + @Override public int hashCode() { return Objects.hashCode(table, columns); diff --git a/sql-parser/src/main/java/io/crate/sql/tree/InsertFromSubquery.java b/sql-parser/src/main/java/io/crate/sql/tree/InsertFromSubquery.java index 491757d339e3..13dbc3e0e036 100644 --- a/sql-parser/src/main/java/io/crate/sql/tree/InsertFromSubquery.java +++ b/sql-parser/src/main/java/io/crate/sql/tree/InsertFromSubquery.java @@ -21,6 +21,7 @@ package io.crate.sql.tree; +import com.google.common.base.MoreObjects; import com.google.common.base.Objects; import javax.annotation.Nullable; @@ -30,8 +31,11 @@ public class InsertFromSubquery extends Insert { private final Query subQuery; - public InsertFromSubquery(Table table, Query subQuery, @Nullable List columns) { - super(table, columns); + public InsertFromSubquery(Table table, + Query subQuery, + @Nullable List columns, + @Nullable List onDuplicateKeyAssignments) { + super(table, columns, onDuplicateKeyAssignments); this.subQuery = subQuery; } @@ -59,7 +63,7 @@ public boolean equals(Object o) { @Override public String toString() { - return Objects.toStringHelper(this) + return MoreObjects.toStringHelper(this) .add("table", table) .add("columns", columns) .add("subquery", subQuery) diff --git a/sql-parser/src/main/java/io/crate/sql/tree/InsertFromValues.java b/sql-parser/src/main/java/io/crate/sql/tree/InsertFromValues.java index bbea37c00f8e..6a261acc43db 100644 --- a/sql-parser/src/main/java/io/crate/sql/tree/InsertFromValues.java +++ b/sql-parser/src/main/java/io/crate/sql/tree/InsertFromValues.java @@ -31,8 +31,11 @@ public class InsertFromValues extends Insert { private final List valuesLists; private final int maxValuesLength; - public InsertFromValues(Table table, List valuesLists, @Nullable List columns) { - super(table, columns); + public InsertFromValues(Table table, + List valuesLists, + @Nullable List columns, + @Nullable List onDuplicateKeyAssignments) { + super(table, columns, onDuplicateKeyAssignments); this.valuesLists = valuesLists; int i = 0; diff --git a/sql-parser/src/test/java/io/crate/sql/parser/TestStatementBuilder.java b/sql-parser/src/test/java/io/crate/sql/parser/TestStatementBuilder.java index aeef8d60cc47..9f2e99245b5b 100644 --- a/sql-parser/src/test/java/io/crate/sql/parser/TestStatementBuilder.java +++ b/sql-parser/src/test/java/io/crate/sql/parser/TestStatementBuilder.java @@ -227,6 +227,11 @@ public void testStatementBuilder() printStatement("select * from t where 'source' !~ 'pattern'"); printStatement("select * from t where source_column ~ pattern_column"); printStatement("select * from t where ? !~ ?"); + + printStatement("insert into t (a, b) values (1, 2) on duplicate key update a = a + 1"); + printStatement("insert into t (a, b) values (1, 2) on duplicate key update a = a + 1, b = 3"); + printStatement("insert into t (a, b) values (1, 2), (3, 4) on duplicate key update a = values (a) + 1, b = 4"); + printStatement("insert into t (a, b) values (1, 2), (3, 4) on duplicate key update a = values (a) + 1, b = values(b) - 2"); } @Test diff --git a/sql/src/main/java/io/crate/analyze/InsertFromSubQueryAnalyzer.java b/sql/src/main/java/io/crate/analyze/InsertFromSubQueryAnalyzer.java index 1b641eb273bd..a01030edf38f 100644 --- a/sql/src/main/java/io/crate/analyze/InsertFromSubQueryAnalyzer.java +++ b/sql/src/main/java/io/crate/analyze/InsertFromSubQueryAnalyzer.java @@ -48,7 +48,9 @@ public InsertFromSubQueryAnalyzer(AnalysisMetaData analysisMetaData, ParameterCo @Override public AnalyzedStatement visitInsertFromSubquery(InsertFromSubquery node, Void context) { - + if (!node.onDuplicateKeyAssignments().isEmpty()) { + throw new UnsupportedOperationException("ON DUPLICATE KEY UPDATE clause is not supported"); + } TableInfo tableInfo = analysisMetaData.referenceInfos().getTableInfoUnsafe(TableIdent.of(node.table())); SelectStatementAnalyzer selectStatementAnalyzer = new SelectStatementAnalyzer(analysisMetaData, parameterContext); diff --git a/sql/src/main/java/io/crate/analyze/InsertFromValuesAnalyzer.java b/sql/src/main/java/io/crate/analyze/InsertFromValuesAnalyzer.java index 98cf4d637a51..577ee20aa230 100644 --- a/sql/src/main/java/io/crate/analyze/InsertFromValuesAnalyzer.java +++ b/sql/src/main/java/io/crate/analyze/InsertFromValuesAnalyzer.java @@ -66,6 +66,9 @@ public InsertFromValuesAnalyzer(AnalysisMetaData analysisMetaData, ParameterCont @Override public AnalyzedStatement visitInsertFromValues(InsertFromValues node, Void context) { + if (!node.onDuplicateKeyAssignments().isEmpty()) { + throw new UnsupportedOperationException("ON DUPLICATE KEY UPDATE clause is not supported"); + } TableInfo tableInfo = analysisMetaData.referenceInfos().getTableInfoUnsafe(TableIdent.of(node.table())); TableRelation tableRelation = new TableRelation(tableInfo); validateTable(tableInfo); diff --git a/sql/src/test/java/io/crate/analyze/InsertFromSubQueryAnalyzerTest.java b/sql/src/test/java/io/crate/analyze/InsertFromSubQueryAnalyzerTest.java index 57523d0f6f65..97543cc6311b 100644 --- a/sql/src/test/java/io/crate/analyze/InsertFromSubQueryAnalyzerTest.java +++ b/sql/src/test/java/io/crate/analyze/InsertFromSubQueryAnalyzerTest.java @@ -188,4 +188,9 @@ public void testImplicitTypeCasting() throws Exception { Function castFunction = (Function)outputSymbols.get(1); assertThat(castFunction.info().ident().name(), is(ToStringFunction.NAME)); } + + @Test (expected = UnsupportedOperationException.class) + public void testInsertFromValuesWithOnDuplicateKey() throws Exception { + analyze("insert into users (id, name) (select id, name from users) on duplicate key update name = substr(values (name), 1, 1)"); + } } diff --git a/sql/src/test/java/io/crate/analyze/InsertFromValuesAnalyzerTest.java b/sql/src/test/java/io/crate/analyze/InsertFromValuesAnalyzerTest.java index 74f0cb649b14..28ccb3b0d86f 100644 --- a/sql/src/test/java/io/crate/analyze/InsertFromValuesAnalyzerTest.java +++ b/sql/src/test/java/io/crate/analyze/InsertFromValuesAnalyzerTest.java @@ -786,5 +786,11 @@ public void testInvalidColumnName() throws Exception { expectedException.expectMessage("column name \"newCol[\" is invalid"); analyze("insert into users (\"newCol[\") values(test)"); } + + @Test + public void testInsertFromValuesWithOnDuplicateKey() throws Exception { + expectedException.expect(UnsupportedOperationException.class); + analyze("insert into users (id, name) values (1, 'Arthur') on duplicate key update name = substr(values (name), 1, 1)"); + } }