You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by jh...@apache.org on 2015/01/05 10:10:15 UTC
incubator-calcite git commit: [CALCITE-492] Support UPSERT statement
in parser
Repository: incubator-calcite
Updated Branches:
refs/heads/master 175d0705c -> 2727c5b33
[CALCITE-492] Support UPSERT statement in parser
Add ALTER SESSION/SYSTEM, INSERT, UPSERT, UPDATE, MERGE, DELETE, EXPLAIN to SQL reference.
Project: http://git-wip-us.apache.org/repos/asf/incubator-calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-calcite/commit/2727c5b3
Tree: http://git-wip-us.apache.org/repos/asf/incubator-calcite/tree/2727c5b3
Diff: http://git-wip-us.apache.org/repos/asf/incubator-calcite/diff/2727c5b3
Branch: refs/heads/master
Commit: 2727c5b33a40b9af193a5f7f86fc28227ebe62df
Parents: 175d070
Author: Julian Hyde <jh...@apache.org>
Authored: Mon Jan 5 00:46:27 2015 -0800
Committer: Julian Hyde <jh...@apache.org>
Committed: Mon Jan 5 00:46:27 2015 -0800
----------------------------------------------------------------------
core/src/main/codegen/templates/Parser.jj | 18 ++++---
.../java/org/apache/calcite/sql/SqlInsert.java | 11 ++++-
.../apache/calcite/sql/SqlInsertKeyword.java | 17 +++++--
.../calcite/sql/parser/SqlParserTest.java | 27 +++++++++++
doc/REFERENCE.md | 50 +++++++++++++++++++-
5 files changed, 112 insertions(+), 11 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/2727c5b3/core/src/main/codegen/templates/Parser.jj
----------------------------------------------------------------------
diff --git a/core/src/main/codegen/templates/Parser.jj b/core/src/main/codegen/templates/Parser.jj
index 5f540e7..3f9a608 100644
--- a/core/src/main/codegen/templates/Parser.jj
+++ b/core/src/main/codegen/templates/Parser.jj
@@ -54,6 +54,7 @@ import org.apache.calcite.sql.SqlExplainLevel;
import org.apache.calcite.sql.SqlFunctionCategory;
import org.apache.calcite.sql.SqlIdentifier;
import org.apache.calcite.sql.SqlInsert;
+import org.apache.calcite.sql.SqlInsertKeyword;
import org.apache.calcite.sql.SqlIntervalLiteral;
import org.apache.calcite.sql.SqlIntervalQualifier;
import org.apache.calcite.sql.SqlJdbcFunctionCall;
@@ -274,7 +275,7 @@ SqlNode TableOverOpt() :
/*
* Parses dialect-specific keywords immediately following the SELECT keyword.
*/
-void SqlSelectKeywords(List<SqlNode> keywords) :
+void SqlSelectKeywords(List<SqlLiteral> keywords) :
{}
{
E()
@@ -283,7 +284,7 @@ void SqlSelectKeywords(List<SqlNode> keywords) :
/*
* Parses dialect-specific keywords immediately following the INSERT keyword.
*/
-void SqlInsertKeywords(List keywords) :
+void SqlInsertKeywords(List<SqlLiteral> keywords) :
{}
{
E()
@@ -923,7 +924,7 @@ SqlNode SqlStmtEof() :
*/
SqlSelect SqlSelect() :
{
- List<SqlNode> keywords = new ArrayList<SqlNode>();
+ final List<SqlLiteral> keywords = Lists.newArrayList();
List<SqlNode> selectList;
SqlNode fromClause;
SqlNode where;
@@ -1127,14 +1128,18 @@ SqlNode NamedRoutineCall(
*/
SqlNode SqlInsert() :
{
- List keywords = new ArrayList();
+ final List<SqlLiteral> keywords = Lists.newArrayList();
SqlIdentifier table;
SqlNode source;
SqlNodeList columnList = null;
SqlParserPos pos;
}
{
- <INSERT>
+ (
+ <INSERT>
+ |
+ <UPSERT> { keywords.add(SqlInsertKeyword.UPSERT.symbol(getPos())); }
+ )
SqlInsertKeywords(keywords)
<INTO> table = CompoundIdentifier()
{
@@ -1333,7 +1338,7 @@ SqlUpdate WhenMatchedClause(SqlNode table, SqlIdentifier alias) :
SqlInsert WhenNotMatchedClause(SqlNode table) :
{
SqlParserPos pos, insertPos;
- List keywords = new ArrayList();
+ List<SqlLiteral> keywords = Lists.newArrayList();
SqlNodeList insertColumnList = null;
SqlNode rowConstructor;
SqlNode insertValues;
@@ -5007,6 +5012,7 @@ SqlPostfixOperator PostfixRowOperator() :
| < UNNEST: "UNNEST" >
| < UPDATE: "UPDATE" >
| < UPPER: "UPPER" >
+ | < UPSERT: "UPSERT" >
| < USAGE: "USAGE" >
| < USER: "USER" >
| < USER_DEFINED_TYPE_CATALOG: "USER_DEFINED_TYPE_CATALOG" >
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/2727c5b3/core/src/main/java/org/apache/calcite/sql/SqlInsert.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlInsert.java b/core/src/main/java/org/apache/calcite/sql/SqlInsert.java
index 43861a9..7b58c8f 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlInsert.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlInsert.java
@@ -65,6 +65,15 @@ public class SqlInsert extends SqlCall {
return ImmutableNullableList.of(keywords, targetTable, source, columnList);
}
+ /** Returns whether this is an UPSERT statement.
+ *
+ * <p>In SQL, this is represented using the {@code UPSERT} keyword rather than
+ * {@code INSERT}; in the abstract syntax tree, an UPSERT is indicated by the
+ * presence of a {@link SqlInsertKeyword#UPSERT} keyword. */
+ public final boolean isUpsert() {
+ return getModifierNode(SqlInsertKeyword.UPSERT) != null;
+ }
+
@Override public void setOperand(int i, SqlNode operand) {
switch (i) {
case 0:
@@ -122,7 +131,7 @@ public class SqlInsert extends SqlCall {
@Override public void unparse(SqlWriter writer, int leftPrec, int rightPrec) {
writer.startList(SqlWriter.FrameTypeEnum.SELECT);
- writer.sep("INSERT INTO");
+ writer.sep(isUpsert() ? "UPSERT INTO" : "INSERT INTO");
final int opLeft = getOperator().getLeftPrec();
final int opRight = getOperator().getRightPrec();
targetTable.unparse(writer, opLeft, opRight);
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/2727c5b3/core/src/main/java/org/apache/calcite/sql/SqlInsertKeyword.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlInsertKeyword.java b/core/src/main/java/org/apache/calcite/sql/SqlInsertKeyword.java
index 8e93954..e4704c7 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlInsertKeyword.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlInsertKeyword.java
@@ -16,12 +16,23 @@
*/
package org.apache.calcite.sql;
+import org.apache.calcite.sql.parser.SqlParserPos;
+
/**
- * Defines the keywords which can occur immediately after the "INSERT" keyword.
- * Standard SQL has no such keywords. This enumeration exists only to allow
- * extension projects to define them.
+ * Defines the keywords that can occur immediately after the "INSERT" keyword.
+ *
+ * <p>Standard SQL has no such keywords, but extension projects may define them.
*/
public enum SqlInsertKeyword implements SqlLiteral.SqlSymbol {
+ UPSERT;
+
+ /**
+ * Creates a parse-tree node representing an occurrence of this keyword
+ * at a particular position in the parsed text.
+ */
+ public SqlLiteral symbol(SqlParserPos pos) {
+ return SqlLiteral.createSymbol(this, pos);
+ }
}
// End SqlInsertKeyword.java
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/2727c5b3/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java b/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java
index adcaf93..1a66182 100644
--- a/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java
+++ b/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java
@@ -2111,6 +2111,26 @@ public class SqlParserTest {
+ "FROM `EMPS2`)");
}
+ @Test public void testUpsertValues() {
+ sql("upsert into emps values (1,'Fredkin')")
+ .ok("UPSERT INTO `EMPS`\n"
+ + "(VALUES (ROW(1, 'Fredkin')))");
+ }
+
+ @Test public void testUpsertSelect() {
+ sql("upsert into emps select * from emp as e")
+ .ok("UPSERT INTO `EMPS`\n"
+ + "(SELECT *\n"
+ + "FROM `EMP` AS `E`)");
+ }
+
+ @Test public void testExplainUpsert() {
+ sql("explain plan for upsert into emps1 values (1, 2)")
+ .ok("EXPLAIN PLAN INCLUDING ATTRIBUTES WITH IMPLEMENTATION FOR\n"
+ + "UPSERT INTO `EMPS1`\n"
+ + "(VALUES (ROW(1, 2)))");
+ }
+
@Test public void testDelete() {
check("delete from emps", "DELETE FROM `EMPS`");
}
@@ -2122,6 +2142,13 @@ public class SqlParserTest {
+ "WHERE (`EMPNO` = 12)");
}
+ @Test public void testUpdate() {
+ sql("update emps set empno = empno + 1, sal = sal - 1 where empno=12")
+ .ok("UPDATE `EMPS` (`EMPNO`, `SAL`) SET `EMPNO` = (`EMPNO` + 1)\n"
+ + ", `SAL` = (`SAL` - 1)\n"
+ + "WHERE (`EMPNO` = 12)");
+ }
+
@Test public void testMergeSelectSource() {
check(
"merge into emps e "
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/2727c5b3/doc/REFERENCE.md
----------------------------------------------------------------------
diff --git a/doc/REFERENCE.md b/doc/REFERENCE.md
index d21ab24..412bec6 100644
--- a/doc/REFERENCE.md
+++ b/doc/REFERENCE.md
@@ -3,6 +3,48 @@
## SQL constructs
```SQL
+statement:
+ setStatement
+ | explain
+ | insert
+ | update
+ | merge
+ | delete
+ | query
+
+setStatement:
+ ALTER ( SYSTEM | SESSION ) SET identifier = expression
+
+explain:
+ EXPLAIN PLAN
+ [ WITH TYPE | WITH IMPLEMENTATION | WITHOUT IMPLEMENTATION ]
+ [ EXCLUDING ATTRIBUTES | INCLUDING [ ALL ] ATTRIBUTES ]
+ FOR ( insert | update | merge | delete | query )
+
+insert:
+ ( INSERT | UPSERT ) INTO tablePrimary
+ [ '(' column [, column ]* ')' ]
+ query
+
+update:
+ UPDATE tablePrimary
+ SET assign [, assign ]*
+ [ WHERE booleanExpression ]
+
+assign:
+ identifier '=' expression
+
+merge:
+ MERGE INTO tablePrimary [ [ AS ] alias ]
+ USING tablePrimary
+ ON booleanExpression
+ [ WHEN MATCHED THEN UPDATE SET assign [, assign ]* ]
+ [ WHEN NOT MATCHED THEN INSERT VALUES '(' value [ , value ]* ')' ]
+
+delete:
+ DELETE FROM tablePrimary [ [ AS ] alias ]
+ [ WHERE booleanExpression ]
+
query:
[ WITH withItem [ , withItem ]* query ]
| {
@@ -53,10 +95,13 @@ tableReference:
tablePrimary:
[ TABLE ] [ [ catalogName . ] schemaName . ] tableName
| '(' query ')'
- | VALUES expression [, expression ]*
+ | values
| UNNEST '(' expression ')'
| '(' TABLE expression ')'
+values:
+ VALUES expression [, expression ]*
+
groupItem:
expression
| '(' ')'
@@ -81,6 +126,9 @@ windowSpec:
')'
```
+In *merge*, at least one of the WHEN MATCHED and WHEN NOT MATCHED clauses must
+be present.
+
In *orderItem*, if *expression* is a positive integer *n*, it denotes
the <em>n</em>th item in the SELECT clause.