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 2017/11/27 19:04:06 UTC

[1/5] calcite git commit: [CALCITE-707] Add "server" module, with built-in support for simple DDL statements

Repository: calcite
Updated Branches:
  refs/heads/master 0ea976eed -> a5d520df7


http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/server/src/test/resources/sql/table_as.iq
----------------------------------------------------------------------
diff --git a/server/src/test/resources/sql/table_as.iq b/server/src/test/resources/sql/table_as.iq
new file mode 100644
index 0000000..68ca9f1
--- /dev/null
+++ b/server/src/test/resources/sql/table_as.iq
@@ -0,0 +1,251 @@
+# table_as.iq - "CREATE TABLE AS ..." DDL
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to you under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+!use server
+!set outputformat mysql
+
+# Create a source table
+create table dept (deptno int not null, name varchar(10));
+(0 rows modified)
+
+!update
+
+insert into dept
+values (10, 'Sales'), (20, 'Marketing'), (30, 'Engineering');
+(3 rows modified)
+
+!update
+
+# Create as select
+create table d as
+select * from dept where deptno > 10;
+(0 rows modified)
+
+!update
+
+# Check contents
+select * from d;
++--------+-------------+
+| DEPTNO | NAME        |
++--------+-------------+
+|     20 | Marketing   |
+|     30 | Engineering |
++--------+-------------+
+(2 rows)
+
+!ok
+
+# Try to create again - fails
+create table d as
+select * from dept where deptno < 30;
+Table 'D' already exists
+!error
+
+# Try to create again - fails silently
+create table if not exists d as
+select * from dept where deptno < 30;
+(0 rows modified)
+
+!update
+
+# Check contents are unchanged
+select * from d;
++--------+-------------+
+| DEPTNO | NAME        |
++--------+-------------+
+|     20 | Marketing   |
+|     30 | Engineering |
++--------+-------------+
+(2 rows)
+
+!ok
+
+# Drop
+drop table if exists d;
+(0 rows modified)
+
+!update
+
+# It's gone
+select * from d;
+Object 'D' not found
+!error
+
+# Drop does nothing because table does not exist
+drop table if exists d;
+(0 rows modified)
+
+!update
+
+# Create table without either AS or column list - fails
+create table d;
+At line 1, column 14: Missing column list
+!error
+
+# Create table without AS or column types - fails
+create table d (x, y);
+At line 1, column 17: Type required for column 'X' in CREATE TABLE without AS
+!error
+
+# Create table without AS or column types - fails
+create table d (x int, y);
+At line 1, column 24: Type required for column 'Y' in CREATE TABLE without AS
+!error
+
+# Create based on itself - fails
+create table d2 as select * from d2;
+Object 'D2' not found
+!error
+
+# Create table based on UNION
+create table d3 as
+select deptno as dd from dept where deptno < 15
+union all
+select deptno as ee from dept where deptno > 25;
+(0 rows modified)
+
+!update
+
+# Check contents
+select * from d3;
++----+
+| DD |
++----+
+| 10 |
+| 30 |
++----+
+(2 rows)
+
+!ok
+
+# Drop
+drop table d3;
+(0 rows modified)
+
+!update
+
+# Create table based on UNION and ORDER BY
+create table d4 as
+select deptno as dd from dept where deptno < 15
+union all
+select deptno as dd from dept where deptno > 25
+order by 1 desc;
+(0 rows modified)
+
+!update
+
+# Check contents
+select * from d4;
++----+
+| DD |
++----+
+| 10 |
+| 30 |
++----+
+(2 rows)
+
+!ok
+
+# Drop
+drop table d4;
+
+# Create table based on VALUES
+create table d5 as
+values (1, 'a'), (2, 'b');
+(0 rows modified)
+
+!update
+
+# Check contents
+select * from d5;
++--------+--------+
+| EXPR$0 | EXPR$1 |
++--------+--------+
+|      1 | a      |
+|      2 | b      |
++--------+--------+
+(2 rows)
+
+!ok
+
+# Use just aliases
+create table d6 (x, y) as
+select * from dept where deptno < 15;
+(0 rows modified)
+
+!update
+
+# Check contents
+select * from d6;
++----+-------+
+| X  | Y     |
++----+-------+
+| 10 | Sales |
++----+-------+
+(1 row)
+
+!ok
+
+# Use a mixture of aliases and column declarations
+create table d7 (x int, y) as
+select * from dept where deptno < 15;
+(0 rows modified)
+
+!update
+
+# Check contents
+select * from d7;
++----+-------+
+| X  | Y     |
++----+-------+
+| 10 | Sales |
++----+-------+
+(1 row)
+
+!ok
+
+# Too many columns
+create table d8 (x, y, z) as
+select * from dept where deptno < 15;
+Number of columns must match number of query columns
+!error
+
+# Too few columns
+create table d9 (x) as
+select * from dept where deptno < 15;
+Number of columns must match number of query columns
+!error
+
+# Specify column names and types
+create table d10 (x int, y varchar(20)) as
+select * from dept where deptno < 15;
+(0 rows modified)
+
+!update
+
+# Check contents
+select * from d10;
++----+-------+
+| X  | Y     |
++----+-------+
+| 10 | Sales |
++----+-------+
+(1 row)
+
+!ok
+
+# End table_as.iq

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/server/src/test/resources/sql/view.iq
----------------------------------------------------------------------
diff --git a/server/src/test/resources/sql/view.iq b/server/src/test/resources/sql/view.iq
new file mode 100755
index 0000000..e1aeff5
--- /dev/null
+++ b/server/src/test/resources/sql/view.iq
@@ -0,0 +1,183 @@
+# view.iq - DDL on views
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to you under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+!use server
+!set outputformat mysql
+
+# Create a view
+create view v as
+select a, a + 1 as b
+from (values 1, 2) as t(a);
+(0 rows modified)
+
+!update
+
+select * from v;
++---+---+
+| A | B |
++---+---+
+| 1 | 2 |
+| 2 | 3 |
++---+---+
+(2 rows)
+
+!ok
+
+# Try to create a view that already exists
+create view v as
+select a, a + 2 as b
+from (values 1, 2) as t(a);
+View 'V' already exists and REPLACE not specified
+!error
+
+create or replace view v as
+select a, a + 3 as b
+from (values 1, 2) as t(a);
+(0 rows modified)
+
+!update
+
+select * from v;
++---+---+
+| A | B |
++---+---+
+| 1 | 4 |
+| 2 | 5 |
++---+---+
+(2 rows)
+
+!ok
+
+# Drop view
+drop view v;
+(0 rows modified)
+
+!update
+
+# Explicit column names
+create view v (x, "y z") as
+select a, a + 4 as b
+from (values 1, 2) as t(a);
+(0 rows modified)
+
+!update
+
+select * from v;
++---+-----+
+| X | y z |
++---+-----+
+| 1 |   5 |
+| 2 |   6 |
++---+-----+
+(2 rows)
+
+!ok
+
+# Wrong number of columns
+create or replace view v (x, y, z) as
+select a, a + 5 as b
+from (values 1, 2) as t(a);
+List of column aliases must have same degree as table; table has 2 columns ('A', 'B'), whereas alias list has 3 columns
+!error
+
+# Column names not unique
+create or replace view v (x, x) as
+select a, a + 6 as b
+from (values 1, 2) as t(a);
+Duplicate name 'X' in column alias list
+!error
+
+# View based on VALUES
+create or replace view v (p, q) as
+values (1, 'a'), (2, 'b');
+(0 rows modified)
+
+!update
+
+select * from v;
++---+---+
+| P | Q |
++---+---+
+| 1 | a |
+| 2 | b |
++---+---+
+(2 rows)
+
+!ok
+
+# View based on table
+create table t (i int);
+(0 rows modified)
+
+!update
+
+insert into t values (1), (2), (3);
+(3 rows modified)
+
+!update
+
+create or replace view v (e, d) as
+select i, i + 1 from t;
+(0 rows modified)
+
+!update
+
+# View based on UNION of another view
+create or replace view v2 as
+select * from v
+union all
+select e + d, e - d from v;
+(0 rows modified)
+
+!update
+
+select * from v2;
++---+----+
+| E | D  |
++---+----+
+| 1 |  2 |
+| 2 |  3 |
+| 3 | -1 |
+| 3 |  4 |
+| 5 | -1 |
+| 7 | -1 |
++---+----+
+(6 rows)
+
+!ok
+
+# Drop view, then make sure that a query can't find it
+drop view if exists v2;
+(0 rows modified)
+
+!update
+
+select * from v2;
+Object 'V2' not found
+!error
+
+# Try to drop view that does not exist
+drop view v3;
+View 'V3' not found
+!error
+
+drop view if exists v3;
+(0 rows modified)
+
+!update
+
+# End view.iq

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/site/_docs/adapter.md
----------------------------------------------------------------------
diff --git a/site/_docs/adapter.md b/site/_docs/adapter.md
index 1c2a312..5562c4c 100644
--- a/site/_docs/adapter.md
+++ b/site/_docs/adapter.md
@@ -137,6 +137,82 @@ makes a connection to the Cassandra adapter, equivalent to writing the following
 
 Note how each key in the `operand` section appears with a `schema.` prefix in the connect string.
 
+## Server
+
+Calcite's core module (`calcite-core`) supports SQL queries (`SELECT`) and DML
+operations  (`INSERT`, `UPDATE`, `DELETE`, `MERGE`)
+but does not support DDL operations such as `CREATE SCHEMA` or `CREATE TABLE`.
+As we shall see, DDL complicates the state model of the repository and makes
+the parser more difficult to extend, so we left DDL out of core.
+
+The server module (`calcite-server`) adds DDL support to Calcite.
+It extends the SQL parser,
+[using the same mechanism used by sub-projects](#extending-the-parser),
+adding some DDL commands:
+
+* `CREATE` and `DROP SCHEMA`
+* `CREATE` and `DROP FOREIGN SCHEMA`
+* `CREATE` and `DROP TABLE` (including `CREATE TABLE ... AS SELECT`)
+* `CREATE` and `DROP MATERIALIZED VIEW`
+* `CREATE` and `DROP VIEW`
+
+Commands are described in the [SQL reference](reference.html#ddl-extensions).
+
+To enable, include `calcite-server.jar` in your class path, and add
+`parserFactory=org.apache.calcite.sql.parser.ddl.SqlDdlParserImpl#FACTORY`
+to the JDBC connect string (see connect string property
+[parserFactory]({{ site.apiRoot }}/org/apache/calcite/config/CalciteConnectionProperty.html#PARSER_FACTORY)).
+Here is an example using the `sqlline` shell.
+
+{% highlight sql %}
+$ ./sqlline
+sqlline version 1.3.0
+> !connect jdbc:calcite:parserFactory=org.apache.calcite.sql.parser.ddl.SqlDdlParserImpl#FACTORY sa ""
+> CREATE TABLE t (i INTEGER, j VARCHAR(10));
+No rows affected (0.293 seconds)
+> INSERT INTO t VALUES (1, 'a'), (2, 'bc');
+2 rows affected (0.873 seconds)
+> CREATE VIEW v AS SELECT * FROM t WHERE i > 1;
+No rows affected (0.072 seconds)
+> SELECT count(*) FROM v;
++---------------------+
+|       EXPR$0        |
++---------------------+
+| 1                   |
++---------------------+
+1 row selected (0.148 seconds)
+> !quit
+{% endhighlight %}
+
+The `calcite-server` module is optional.
+One of its goals is to showcase Calcite's capabilities
+(for example materialized views, foreign tables and generated columns) using
+concise examples that you can try from the SQL command line.
+All of the capabilities used by `calcite-server` are available via APIs in
+`calcite-core`.
+
+If you are the author of a sub-project, it is unlikely that your syntax
+extensions match those in `calcite-server`, so we recommend that you add your
+SQL syntax extensions by [extending the core parser](#extending-the-parser);
+if you want DDL commands, you may be able to copy-paste from `calcite-server`
+into your project.
+
+At present, the repository is not persisted. As you execute DDL commands, you
+are modifying an in-memory repository by adding and removing objects
+reachable from a root
+[<tt>Schema</tt>]({{ site.apiRoot }}/org/apache/calcite/schema/Schema.html).
+All commands within the same SQL session will see those objects.
+You can create the same objects in a future session by executing the same
+script of SQL commands.
+
+Calcite could also act as a data virtualization or federation server:
+Calcite manages data in multiple foreign schemas, but to a client the data
+all seems to be in the same place. Calcite chooses where processing should
+occur, and whether to create copies of data for efficiency.
+The `calcite-server` module is a step towards that goal; an
+industry-strength solution would require further on packaging (to make Calcite
+runnable as a service), repository persistence, authorization and security.
+
 ## Extensibility
 
 There are many other APIs that allow you to extend Calcite's capabilities.

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/site/_docs/reference.md
----------------------------------------------------------------------
diff --git a/site/_docs/reference.md b/site/_docs/reference.md
index ea6e983..ce85272 100644
--- a/site/_docs/reference.md
+++ b/site/_docs/reference.md
@@ -2035,7 +2035,7 @@ measureColumn:
       expression AS alias
 
 pattern:
-      patternTerm ['|' patternTerm ]*
+      patternTerm [ '|' patternTerm ]*
 
 patternTerm:
       patternFactor [ patternFactor ]*
@@ -2067,3 +2067,98 @@ intervalLiteral:
 
 In *patternQuantifier*, *repeat* is a positive integer,
 and *minRepeat* and *maxRepeat* are non-negative integers.
+
+### DDL Extensions
+
+DDL extensions are only available in the calcite-server module.
+To enable, include `calcite-server.jar` in your class path, and add
+`parserFactory=org.apache.calcite.sql.parser.ddl.SqlDdlParserImpl#FACTORY`
+to the JDBC connect string (see connect string property
+[parserFactory]({{ site.apiRoot }}/org/apache/calcite/config/CalciteConnectionProperty.html#PARSER_FACTORY)).
+
+{% highlight sql %}
+ddlStatement:
+      createSchemaStatement
+  |   createForeignSchemaStatement
+  |   createTableStatement
+  |   createViewStatement
+  |   createMaterializedViewStatement
+  |   dropSchemaStatement
+  |   dropForeignSchemaStatement
+  |   dropTableStatement
+  |   dropViewStatement
+  |   dropMaterializedViewStatement
+
+createSchemaStatement:
+      CREATE [ OR REPLACE ] SCHEMA [ IF NOT EXISTS ] name
+
+createForeignSchemaStatement:
+      CREATE [ OR REPLACE ] FOREIGN SCHEMA [ IF NOT EXISTS ] name
+      (
+          TYPE 'type'
+      |   LIBRARY 'com.example.calcite.ExampleSchemaFactory'
+      )
+      [ OPTIONS '(' option [, option ]* ')' ]
+
+option:
+      name literal
+
+createTableStatement:
+      CREATE TABLE [ IF NOT EXISTS ] name
+      [ '(' tableElement [, tableElement ]* ')' ]
+      [ AS query ]
+
+tableElement:
+      columnName type [ columnGenerator ] [ columnConstraint ]
+  |   columnName
+  |   tableConstraint
+
+columnGenerator:
+      DEFAULT expression
+  |   [ GENERATED ALWAYS ] AS '(' expression ')'
+      { VIRTUAL | STORED }
+
+columnConstraint:
+      [ CONSTRAINT name ]
+      [ NOT ] NULL
+
+tableConstraint:
+      [ CONSTRAINT name ]
+      {
+          CHECK '(' expression ')'
+      |   PRIMARY KEY '(' columnName [, columnName ]* ')'
+      |   UNIQUE '(' columnName [, columnName ]* ')'
+      }
+
+createViewStatement:
+      CREATE [ OR REPLACE ] VIEW name
+      [ '(' columnName [, columnName ]* ')' ]
+      AS query
+
+createMaterializedViewStatement:
+      CREATE MATERIALIZED VIEW [ IF NOT EXISTS ] name
+      [ '(' columnName [, columnName ]* ')' ]
+      AS query
+
+dropSchemaStatement:
+      DROP SCHEMA name [ IF EXISTS ]
+
+dropForeignSchemaStatement:
+      DROP FOREIGN SCHEMA name [ IF EXISTS ]
+
+dropTableStatement:
+      DROP TABLE name [ IF EXISTS ]
+
+dropViewStatement:
+      DROP VIEW name [ IF EXISTS ]
+
+dropMaterializedViewStatement:
+      DROP MATERIALIZED VIEW name [ IF EXISTS ]
+{% endhighlight %}
+
+In *createTableStatement*, if you specify *AS query*, you may omit the list of
+*tableElement*s, or you can omit the data type of any *tableElement*, in which
+case it just renames the underlying column.
+
+In *columnGenerator*, if you do not specify `VIRTUAL` or `STORED` for a
+generated column, `VIRTUAL` is the default.

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/sqlline
----------------------------------------------------------------------
diff --git a/sqlline b/sqlline
index b0fc489..b23dcd9 100755
--- a/sqlline
+++ b/sqlline
@@ -37,7 +37,7 @@ if [ ! -f target/fullclasspath.txt ]; then
 fi
 
 CP=
-for module in core cassandra druid elasticsearch2 elasticsearch5 file mongodb spark splunk example/csv example/function; do
+for module in core cassandra druid elasticsearch2 elasticsearch5 file mongodb server spark splunk example/csv example/function; do
   CP=${CP}${module}/target/classes:
   CP=${CP}${module}/target/test-classes:
 done

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/sqlline.bat
----------------------------------------------------------------------
diff --git a/sqlline.bat b/sqlline.bat
index 53f7159..f7165c0 100644
--- a/sqlline.bat
+++ b/sqlline.bat
@@ -23,6 +23,6 @@
 :: Copy dependency jars on first call. (To force jar refresh, remove target\dependencies)
 if not exist target\dependencies (call mvn -B dependency:copy-dependencies -DoverWriteReleases=false -DoverWriteSnapshots=false -DoverWriteIfNewer=true -DoutputDirectory=target\dependencies)
 
-java -Xmx1G -cp ".\target\dependencies\*;core\target\dependencies\*;cassandra\target\dependencies\*;druid\target\dependencies\*;elasticsearch2\target\dependencies\*;elasticsearch5\target\dependencies\*;file\target\dependencies\*;mongodb\target\dependencies\*;spark\target\dependencies\*;splunk\target\dependencies\*" sqlline.SqlLine --verbose=true %*
+java -Xmx1G -cp ".\target\dependencies\*;core\target\dependencies\*;cassandra\target\dependencies\*;druid\target\dependencies\*;elasticsearch2\target\dependencies\*;elasticsearch5\target\dependencies\*;file\target\dependencies\*;mongodb\target\dependencies\*;server\target\dependencies\*;spark\target\dependencies\*;splunk\target\dependencies\*" sqlline.SqlLine --verbose=true %*
 
 :: End sqlline.bat


[3/5] calcite git commit: [CALCITE-707] Add "server" module, with built-in support for simple DDL statements

Posted by jh...@apache.org.
http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
----------------------------------------------------------------------
diff --git a/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties b/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
index 74d0c0d..5a0f888 100644
--- a/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
+++ b/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
@@ -149,6 +149,7 @@ FunctionQuantifierNotAllowed=DISTINCT/ALL not allowed with {0} function
 SomeButNotAllArgumentsAreNamed=Some but not all arguments are named
 DuplicateArgumentName=Duplicate argument name ''{0}''
 DefaultForOptionalParameter=DEFAULT is only allowed for optional parameters
+DefaultNotAllowed=DEFAULT not allowed here
 AccessNotAllowed=Not allowed to perform {0} on {1}
 MinMaxBadType=The {0} function does not support the {1} data type.
 OnlyScalarSubQueryAllowed=Only scalar sub-queries allowed in select list.
@@ -172,7 +173,7 @@ InvalidBoolean=''{0}'' is not a valid boolean value
 ArgumentMustBeValidPrecision=Argument to function ''{0}'' must be a valid precision between ''{1,number,#}'' and ''{2,number,#}''
 IllegalArgumentForTableFunctionCall=Wrong arguments for table function ''{0}'' call. Expected ''{1}'', actual ''{2}''
 InvalidDatetimeFormat=''{0}'' is not a valid datetime format
-InsertIntoAlwaysGenerated=Cannot explicitly insert value into IDENTITY column ''{0}'' which is ALWAYS GENERATED
+InsertIntoAlwaysGenerated=Cannot INSERT into generated column ''{0}''
 ArgumentMustHaveScaleZero=Argument to function ''{0}'' must have a scale of 0
 PreparationAborted=Statement preparation aborted
 SQLFeature_E051_01=SELECT DISTINCT not supported
@@ -235,4 +236,12 @@ CannotUseWithinWithoutOrderBy=Must contain an ORDER BY clause when WITHIN is use
 FirstColumnOfOrderByMustBeTimestamp=First column of ORDER BY must be of type TIMESTAMP
 ExtendNotAllowed=Extended columns not allowed under the current SQL conformance level
 RolledUpNotAllowed=Rolled up column ''{0}'' is not allowed in {1}
+SchemaExists=Schema ''{0}'' already exists
+SchemaInvalidType=Invalid schema type ''{0}''; valid values: {1}
+TableExists=Table ''{0}'' already exists
+CreateTableRequiresColumnList=Missing column list
+CreateTableRequiresColumnTypes=Type required for column ''{0}'' in CREATE TABLE without AS
+ViewExists=View ''{0}'' already exists and REPLACE not specified
+SchemaNotFound=Schema ''{0}'' not found
+ViewNotFound=View ''{0}'' not found
 # End CalciteResource.properties

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/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 4f2d9cb..90603db 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
@@ -1442,18 +1442,18 @@ public class SqlParserTest {
   }
 
   @Test public void testFunctionDefaultArgument() {
-    checkExp("foo(1, DEFAULT, default, 'default', \"default\", 3)",
-        "`FOO`(1, DEFAULT, DEFAULT, 'default', `default`, 3)");
-    checkExp("foo(DEFAULT)",
-        "`FOO`(DEFAULT)");
-    checkExp("foo(x => 1, DEFAULT)",
-        "`FOO`(`X` => 1, DEFAULT)");
-    checkExp("foo(y => DEFAULT, x => 1)",
-        "`FOO`(`Y` => DEFAULT, `X` => 1)");
-    checkExp("foo(x => 1, y => DEFAULT)",
-        "`FOO`(`X` => 1, `Y` => DEFAULT)");
-    check("select sum(DISTINCT DEFAULT) from t group by x",
-        "SELECT SUM(DISTINCT DEFAULT)\n"
+    sql("foo(1, DEFAULT, default, 'default', \"default\", 3)").expression()
+        .ok("`FOO`(1, DEFAULT, DEFAULT, 'default', `default`, 3)");
+    sql("foo(DEFAULT)").expression()
+        .ok("`FOO`(DEFAULT)");
+    sql("foo(x => 1, DEFAULT)").expression()
+        .ok("`FOO`(`X` => 1, DEFAULT)");
+    sql("foo(y => DEFAULT, x => 1)").expression()
+        .ok("`FOO`(`Y` => DEFAULT, `X` => 1)");
+    sql("foo(x => 1, y => DEFAULT)").expression()
+        .ok("`FOO`(`X` => 1, `Y` => DEFAULT)");
+    sql("select sum(DISTINCT DEFAULT) from t group by x")
+        .ok("SELECT SUM(DISTINCT DEFAULT)\n"
             + "FROM `T`\n"
             + "GROUP BY `X`");
     checkExpFails("foo(x ^+^ DEFAULT)",
@@ -1464,6 +1464,31 @@ public class SqlParserTest {
         "(?s).*Encountered \"\\+\" at .*");
   }
 
+  @Test public void testDefault() {
+    sql("select ^DEFAULT^ from emp")
+        .fails("(?s)Encountered \"DEFAULT\" at .*");
+    sql("select cast(empno ^+^ DEFAULT as double) from emp")
+        .fails("(?s)Encountered \"\\+ DEFAULT\" at .*");
+    sql("select empno ^+^ DEFAULT + deptno from emp")
+        .fails("(?s)Encountered \"\\+ DEFAULT\" at .*");
+    sql("select power(0, DEFAULT ^+^ empno) from emp")
+        .fails("(?s)Encountered \"\\+\" at .*");
+    sql("select * from emp join dept ^on^ DEFAULT")
+        .fails("(?s)Encountered \"on DEFAULT\" at .*");
+    sql("select * from emp where empno ^>^ DEFAULT or deptno < 10")
+        .fails("(?s)Encountered \"> DEFAULT\" at .*");
+    sql("select * from emp order by ^DEFAULT^ desc")
+        .fails("(?s)Encountered \"DEFAULT\" at .*");
+    final String expected = "INSERT INTO `DEPT` (`NAME`, `DEPTNO`)\n"
+        + "VALUES (ROW('a', DEFAULT))";
+    sql("insert into dept (name, deptno) values ('a', DEFAULT)")
+        .ok(expected);
+    sql("insert into dept (name, deptno) values ('a', 1 ^+^ DEFAULT)")
+        .fails("(?s)Encountered \"\\+ DEFAULT\" at .*");
+    sql("insert into dept (name, deptno) select 'a'^,^ DEFAULT from (values 0)")
+        .fails("(?s)Encountered \", DEFAULT\" at .*");
+  }
+
   @Test public void testAggregateFilter() {
     sql("select sum(sal) filter (where gender = 'F') as femaleSal,\n"
         + " sum(sal) filter (where true) allSal,\n"
@@ -3340,6 +3365,24 @@ public class SqlParserTest {
         .node(not(isDdl()));
   }
 
+  @Test public void testInsertValuesDefault() {
+    final String expected = "INSERT INTO `EMPS`\n"
+        + "VALUES (ROW(1, DEFAULT, 'Fredkin'))";
+    sql("insert into emps values (1,DEFAULT,'Fredkin')")
+        .ok(expected)
+        .node(not(isDdl()));
+  }
+
+  @Test public void testInsertValuesRawDefault() {
+    final String expected = "INSERT INTO `EMPS`\n"
+        + "VALUES (ROW(DEFAULT))";
+    sql("insert into emps ^values^ default")
+        .fails("(?s).*Encountered \"values default\" at .*");
+    sql("insert into emps values (default)")
+        .ok(expected)
+        .node(not(isDdl()));
+  }
+
   @Test public void testInsertColumnList() {
     final String expected = "INSERT INTO `EMPS` (`X`, `Y`)\n"
         + "(SELECT *\n"
@@ -8268,18 +8311,32 @@ public class SqlParserTest {
    * {@code sql("values 1").ok();}. */
   protected class Sql {
     private final String sql;
+    private final boolean expression;
 
     Sql(String sql) {
+      this(sql, false);
+    }
+
+    Sql(String sql, boolean expression) {
       this.sql = sql;
+      this.expression = expression;
     }
 
     public Sql ok(String expected) {
-      getTester().check(sql, expected);
+      if (expression) {
+        getTester().checkExp(sql, expected);
+      } else {
+        getTester().check(sql, expected);
+      }
       return this;
     }
 
     public Sql fails(String expectedMsgPattern) {
-      getTester().checkFails(sql, expectedMsgPattern);
+      if (expression) {
+        getTester().checkExpFails(sql, expectedMsgPattern);
+      } else {
+        getTester().checkFails(sql, expectedMsgPattern);
+      }
       return this;
     }
 
@@ -8287,6 +8344,11 @@ public class SqlParserTest {
       getTester().checkNode(sql, matcher);
       return this;
     }
+
+    /** Flags that this is an expression, not a whole query. */
+    public Sql expression() {
+      return expression ? this : new Sql(sql, true);
+    }
   }
 
   /** Runs tests on period operators such as OVERLAPS, IMMEDIATELY PRECEDES. */

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/test/java/org/apache/calcite/sql/parser/parserextensiontesting/SqlCreateTable.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/sql/parser/parserextensiontesting/SqlCreateTable.java b/core/src/test/java/org/apache/calcite/sql/parser/parserextensiontesting/SqlCreateTable.java
index 2f57a5f..fa57d30 100644
--- a/core/src/test/java/org/apache/calcite/sql/parser/parserextensiontesting/SqlCreateTable.java
+++ b/core/src/test/java/org/apache/calcite/sql/parser/parserextensiontesting/SqlCreateTable.java
@@ -63,20 +63,16 @@ public class SqlCreateTable extends SqlCreate
   private final SqlNodeList columnList;
 
   private static final SqlOperator OPERATOR =
-      new SqlSpecialOperator("CREATE TABLE", SqlKind.OTHER_DDL);
+      new SqlSpecialOperator("CREATE TABLE", SqlKind.CREATE_TABLE);
 
   /** Creates a SqlCreateTable. */
   public SqlCreateTable(SqlParserPos pos, SqlIdentifier name,
       SqlNodeList columnList) {
-    super(pos, false);
+    super(OPERATOR, pos, false, false);
     this.name = name;
     this.columnList = columnList;
   }
 
-  @Override public SqlOperator getOperator() {
-    return OPERATOR;
-  }
-
   @Override public List<SqlNode> getOperandList() {
     return ImmutableList.of(name, columnList);
   }

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/test/java/org/apache/calcite/test/CalciteAssert.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/CalciteAssert.java b/core/src/test/java/org/apache/calcite/test/CalciteAssert.java
index 3e6b414..06e351a 100644
--- a/core/src/test/java/org/apache/calcite/test/CalciteAssert.java
+++ b/core/src/test/java/org/apache/calcite/test/CalciteAssert.java
@@ -855,6 +855,11 @@ public class CalciteAssert {
     return (Function<F, T>) (Function) Functions.<T>constant(null);
   }
 
+  /** Returns a {@link PropBuilder}. */
+  static PropBuilder propBuilder() {
+    return new PropBuilder();
+  }
+
   /**
    * Result of calling {@link CalciteAssert#that}.
    */
@@ -1850,6 +1855,21 @@ public class CalciteAssert {
       return s;
     }
   }
+
+  /** Builds a {@link java.util.Properties} containing connection property
+   * settings. */
+  static class PropBuilder {
+    final Properties properties = new Properties();
+
+    PropBuilder set(CalciteConnectionProperty p, String v) {
+      properties.setProperty(p.camelName(), v);
+      return this;
+    }
+
+    Properties build() {
+      return properties;
+    }
+  }
 }
 
 // End CalciteAssert.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/test/java/org/apache/calcite/test/CalciteSuite.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/CalciteSuite.java b/core/src/test/java/org/apache/calcite/test/CalciteSuite.java
index b6f2b9f..d577057 100644
--- a/core/src/test/java/org/apache/calcite/test/CalciteSuite.java
+++ b/core/src/test/java/org/apache/calcite/test/CalciteSuite.java
@@ -159,7 +159,7 @@ import org.junit.runners.Suite;
     LatticeTest.class,
     ReflectiveSchemaTest.class,
     JdbcTest.class,
-    QuidemTest.class,
+    CoreQuidemTest.class,
     CalciteRemoteDriverTest.class,
     StreamTest.class,
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/test/java/org/apache/calcite/test/CoreQuidemTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/CoreQuidemTest.java b/core/src/test/java/org/apache/calcite/test/CoreQuidemTest.java
new file mode 100644
index 0000000..a4e9446
--- /dev/null
+++ b/core/src/test/java/org/apache/calcite/test/CoreQuidemTest.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.test;
+
+import org.apache.calcite.prepare.Prepare;
+import org.apache.calcite.util.TryThreadLocal;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Collection;
+
+/**
+ * Test that runs every Quidem file in the "core" module as a test.
+ */
+@RunWith(Parameterized.class)
+public class CoreQuidemTest extends QuidemTest {
+  public CoreQuidemTest(String path) {
+    super(path);
+  }
+
+  /** Runs a test from the command line.
+   *
+   * <p>For example:
+   *
+   * <blockquote>
+   *   <code>java CoreQuidemTest sql/dummy.iq</code>
+   * </blockquote> */
+  public static void main(String[] args) throws Exception {
+    for (String arg : args) {
+      new CoreQuidemTest(arg).test();
+    }
+  }
+
+  /** For {@link Parameterized} runner. */
+  @Parameterized.Parameters(name = "{index}: quidem({0})")
+  public static Collection<Object[]> data() {
+    // Start with a test file we know exists, then find the directory and list
+    // its files.
+    final String first = "sql/agg.iq";
+    return data(first);
+  }
+
+  /** Override settings for "sql/misc.iq". */
+  public void testSqlMisc() throws Exception {
+    switch (CalciteAssert.DB) {
+    case ORACLE:
+      // There are formatting differences (e.g. "4.000" vs "4") when using
+      // Oracle as the JDBC data source.
+      return;
+    }
+    try (final TryThreadLocal.Memo ignored = Prepare.THREAD_EXPAND.push(true)) {
+      checkRun(path);
+    }
+  }
+
+  /** Override settings for "sql/scalar.iq". */
+  public void testSqlScalar() throws Exception {
+    try (final TryThreadLocal.Memo ignored = Prepare.THREAD_EXPAND.push(true)) {
+      checkRun(path);
+    }
+  }
+
+  /** Runs the dummy script "sql/dummy.iq", which is checked in empty but
+   * which you may use as scratch space during development. */
+
+  // Do not disable this test; just remember not to commit changes to dummy.iq
+  public void testSqlDummy() throws Exception {
+    try (final TryThreadLocal.Memo ignored = Prepare.THREAD_EXPAND.push(true)) {
+      checkRun(path);
+    }
+  }
+
+}
+
+// End CoreQuidemTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/test/java/org/apache/calcite/test/JdbcTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/JdbcTest.java b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
index 8760d82..7d6a479 100644
--- a/core/src/test/java/org/apache/calcite/test/JdbcTest.java
+++ b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
@@ -730,7 +730,7 @@ public class JdbcTest {
              driver.connect("jdbc:calcite:", new Properties());
         Statement statement = connection.createStatement()) {
       assertThat(driver.counter, is(0));
-      statement.executeQuery("COMMIT");
+      statement.executeUpdate("COMMIT");
       assertThat(driver.counter, is(1));
     }
   }

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/test/java/org/apache/calcite/test/MockCatalogReader.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/MockCatalogReader.java b/core/src/test/java/org/apache/calcite/test/MockCatalogReader.java
index b25ff10..5f96848 100644
--- a/core/src/test/java/org/apache/calcite/test/MockCatalogReader.java
+++ b/core/src/test/java/org/apache/calcite/test/MockCatalogReader.java
@@ -54,6 +54,7 @@ import org.apache.calcite.rex.RexBuilder;
 import org.apache.calcite.rex.RexInputRef;
 import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.rex.RexUtil;
+import org.apache.calcite.schema.ColumnStrategy;
 import org.apache.calcite.schema.CustomColumnResolvingTable;
 import org.apache.calcite.schema.ExtensibleTable;
 import org.apache.calcite.schema.Path;
@@ -173,7 +174,7 @@ public class MockCatalogReader extends CalciteCatalogReader {
     // Register "EMP" table with customer InitializerExpressionFactory
     // to check whether newDefaultValue method called or not.
     final InitializerExpressionFactory countingInitializerExpressionFactory =
-        new CountingFactory(typeFactory);
+        new CountingFactory(ImmutableList.of("DEPTNO"));
 
     // Register "EMP" table.
     final MockTable empTable =
@@ -205,28 +206,9 @@ public class MockCatalogReader extends CalciteCatalogReader {
     registerTable(empNullablesTable);
 
     // Register "EMPDEFAULTS" table with default values for some columns.
-    final InitializerExpressionFactory empInitializerExpressionFactory =
-        new NullInitializerExpressionFactory() {
-          @Override public RexNode newColumnDefaultValue(RelOptTable table,
-              int iColumn, InitializerContext context) {
-            final RexBuilder rexBuilder = context.getRexBuilder();
-            switch (iColumn) {
-            case 0:
-              return rexBuilder.makeExactLiteral(new BigDecimal(123),
-                  typeFactory.createSqlType(SqlTypeName.INTEGER));
-            case 1:
-              return rexBuilder.makeLiteral("Bob");
-            case 5:
-              return rexBuilder.makeExactLiteral(new BigDecimal(555),
-                  typeFactory.createSqlType(SqlTypeName.INTEGER));
-            default:
-              return rexBuilder.constantNull();
-            }
-          }
-        };
     final MockTable empDefaultsTable =
         MockTable.create(this, salesSchema, "EMPDEFAULTS", false, 14, null,
-            empInitializerExpressionFactory);
+            new EmpInitializerExpressionFactory());
     empDefaultsTable.addColumn("EMPNO", f.intType, true);
     empDefaultsTable.addColumn("ENAME", f.varchar20Type);
     empDefaultsTable.addColumn("JOB", f.varchar10TypeNull);
@@ -381,12 +363,11 @@ public class MockCatalogReader extends CalciteCatalogReader {
     //   SELECT EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, SLACKER
     //   FROM EMP
     //   WHERE DEPTNO = 20 AND SAL > 1000
-    final NullInitializerExpressionFactory nullInitializerFactory =
-        new NullInitializerExpressionFactory();
     final ImmutableIntList m0 = ImmutableIntList.of(0, 1, 2, 3, 4, 5, 6, 8);
     MockTable emp20View =
         new MockViewTable(this, salesSchema.getCatalogName(), salesSchema.name,
-            "EMP_20", false, 600, empTable, m0, null, nullInitializerFactory) {
+            "EMP_20", false, 600, empTable, m0, null,
+            NullInitializerExpressionFactory.INSTANCE) {
           public RexNode getConstraint(RexBuilder rexBuilder,
               RelDataType tableRowType) {
             final RelDataTypeField deptnoField =
@@ -421,7 +402,7 @@ public class MockCatalogReader extends CalciteCatalogReader {
     MockTable empNullables20View =
         new MockViewTable(this, salesSchema.getCatalogName(), salesSchema.name,
             "EMPNULLABLES_20", false, 600, empNullablesTable, m0, null,
-            nullInitializerFactory) {
+            NullInitializerExpressionFactory.INSTANCE) {
           public RexNode getConstraint(RexBuilder rexBuilder,
               RelDataType tableRowType) {
             final RelDataTypeField deptnoField =
@@ -506,7 +487,8 @@ public class MockCatalogReader extends CalciteCatalogReader {
     MockTable struct10View =
         new MockViewTable(this, structTypeSchema.getCatalogName(),
             structTypeSchema.name, "T_10", false, 20, structTypeTable,
-            m1, structTypeTableResolver, nullInitializerFactory) {
+            m1, structTypeTableResolver,
+            NullInitializerExpressionFactory.INSTANCE) {
           @Override public RexNode getConstraint(RexBuilder rexBuilder,
               RelDataType tableRowType) {
             final RelDataTypeField c0Field =
@@ -813,7 +795,7 @@ public class MockCatalogReader extends CalciteCatalogReader {
         } else if (aClass.isInstance(MockTable.this)) {
           return aClass.cast(MockTable.this);
         }
-        return null;
+        return super.unwrap(aClass);
       }
 
       @Override public Table extend(final List<RelDataTypeField> fields) {
@@ -841,23 +823,6 @@ public class MockCatalogReader extends CalciteCatalogReader {
       };
     }
 
-    /**
-     * Subclass of ModifiableTable that also implements
-     * CustomColumnResolvingTable.
-     */
-    private class ModifiableTableWithCustomColumnResolving
-        extends ModifiableTable implements CustomColumnResolvingTable, Wrapper {
-
-      protected ModifiableTableWithCustomColumnResolving(String tableName) {
-        super(tableName);
-      }
-
-      @Override public List<Pair<RelDataTypeField, List<String>>> resolveColumn(
-          RelDataType rowType, RelDataTypeFactory typeFactory, List<String> names) {
-        return resolver.resolveColumn(rowType, typeFactory, names);
-      }
-    }
-
     public static MockTable create(MockCatalogReader catalogReader,
         MockSchema schema, String name, boolean stream, double rowCount) {
       return create(catalogReader, schema, name, stream, rowCount, null);
@@ -867,7 +832,7 @@ public class MockCatalogReader extends CalciteCatalogReader {
         MockSchema schema, String name, boolean stream, double rowCount,
         ColumnResolver resolver) {
       return create(catalogReader, schema, name, stream, rowCount, resolver,
-          new NullInitializerExpressionFactory());
+          NullInitializerExpressionFactory.INSTANCE);
     }
 
     public static MockTable create(MockCatalogReader catalogReader,
@@ -885,6 +850,9 @@ public class MockCatalogReader extends CalciteCatalogReader {
       if (clazz.isInstance(this)) {
         return clazz.cast(this);
       }
+      if (clazz.isInstance(initializerFactory)) {
+        return clazz.cast(initializerFactory);
+      }
       if (clazz.isAssignableFrom(Table.class)) {
         final Table table = resolver == null
             ? new ModifiableTable(Util.last(names))
@@ -978,6 +946,24 @@ public class MockCatalogReader extends CalciteCatalogReader {
     public StructKind getKind() {
       return kind;
     }
+
+    /**
+     * Subclass of {@link ModifiableTable} that also implements
+     * {@link CustomColumnResolvingTable}.
+     */
+    private class ModifiableTableWithCustomColumnResolving
+        extends ModifiableTable implements CustomColumnResolvingTable, Wrapper {
+
+      ModifiableTableWithCustomColumnResolving(String tableName) {
+        super(tableName);
+      }
+
+      @Override public List<Pair<RelDataTypeField, List<String>>> resolveColumn(
+          RelDataType rowType, RelDataTypeFactory typeFactory,
+          List<String> names) {
+        return resolver.resolveColumn(rowType, typeFactory, names);
+      }
+    }
   }
 
   /**
@@ -1014,16 +1000,14 @@ public class MockCatalogReader extends CalciteCatalogReader {
         MockCatalogReader catalogReader, String catalogName, String schemaName, String name,
         boolean stream, double rowCount, ColumnResolver resolver) {
       final Table underlying = modifiableViewTable.unwrap(Table.class);
-      final InitializerExpressionFactory maybeInitializerExpressionFactory =
+      final InitializerExpressionFactory initializerExpressionFactory =
           underlying != null && underlying instanceof Wrapper
               ? ((Wrapper) underlying).unwrap(InitializerExpressionFactory.class)
-              : new NullInitializerExpressionFactory();
-      final InitializerExpressionFactory initializerExpressionFactory =
-          maybeInitializerExpressionFactory == null
-              ? new NullInitializerExpressionFactory()
-              : maybeInitializerExpressionFactory;
-      return new MockModifiableViewRelOptTable(modifiableViewTable, catalogReader, catalogName,
-          schemaName, name, stream, rowCount, resolver, initializerExpressionFactory);
+              : NullInitializerExpressionFactory.INSTANCE;
+      return new MockModifiableViewRelOptTable(modifiableViewTable,
+          catalogReader, catalogName, schemaName, name, stream, rowCount,
+          resolver, Util.first(initializerExpressionFactory,
+          NullInitializerExpressionFactory.INSTANCE));
     }
 
     public static MockViewTableMacro viewMacro(CalciteSchema schema, String viewSql,
@@ -1105,7 +1089,7 @@ public class MockCatalogReader extends CalciteCatalogReader {
     MockViewTable(MockCatalogReader catalogReader, String catalogName,
         String schemaName, String name, boolean stream, double rowCount,
         MockTable fromTable, ImmutableIntList mapping, ColumnResolver resolver,
-        NullInitializerExpressionFactory initializerFactory) {
+        InitializerExpressionFactory initializerFactory) {
       super(catalogReader, catalogName, schemaName, name, stream, rowCount,
           resolver, initializerFactory);
       this.fromTable = fromTable;
@@ -1156,9 +1140,12 @@ public class MockCatalogReader extends CalciteCatalogReader {
 
       @Override public <C> C unwrap(Class<C> aClass) {
         if (table instanceof Wrapper) {
-          return ((Wrapper) table).unwrap(aClass);
+          final C c = ((Wrapper) table).unwrap(aClass);
+          if (c != null) {
+            return c;
+          }
         }
-        return null;
+        return super.unwrap(aClass);
       }
     }
 
@@ -1176,9 +1163,12 @@ public class MockCatalogReader extends CalciteCatalogReader {
 
       @Override public <C> C unwrap(Class<C> aClass) {
         if (table instanceof Wrapper) {
-          return ((Wrapper) table).unwrap(aClass);
+          final C c = ((Wrapper) table).unwrap(aClass);
+          if (c != null) {
+            return c;
+          }
         }
-        return null;
+        return super.unwrap(aClass);
       }
     }
 
@@ -1232,7 +1222,7 @@ public class MockCatalogReader extends CalciteCatalogReader {
     MockDynamicTable(MockCatalogReader catalogReader, String catalogName,
         String schemaName, String name, boolean stream, double rowCount) {
       super(catalogReader, catalogName, schemaName, name, stream, rowCount,
-          null, new NullInitializerExpressionFactory());
+          null, NullInitializerExpressionFactory.INSTANCE);
     }
 
     public void onRegister(RelDataTypeFactory typeFactory) {
@@ -1569,6 +1559,40 @@ public class MockCatalogReader extends CalciteCatalogReader {
     }
   }
 
+  /** Default values for the "EMPDEFAULTS" table. */
+  private static class EmpInitializerExpressionFactory
+      extends NullInitializerExpressionFactory {
+    @Override public ColumnStrategy generationStrategy(RelOptTable table,
+        int iColumn) {
+      switch (iColumn) {
+      case 0:
+      case 1:
+      case 5:
+        return ColumnStrategy.DEFAULT;
+      default:
+        return super.generationStrategy(table, iColumn);
+      }
+    }
+
+    @Override public RexNode newColumnDefaultValue(RelOptTable table,
+        int iColumn, InitializerContext context) {
+      final RexBuilder rexBuilder = context.getRexBuilder();
+      final RelDataTypeFactory typeFactory = rexBuilder.getTypeFactory();
+      switch (iColumn) {
+      case 0:
+        return rexBuilder.makeExactLiteral(new BigDecimal(123),
+            typeFactory.createSqlType(SqlTypeName.INTEGER));
+      case 1:
+        return rexBuilder.makeLiteral("Bob");
+      case 5:
+        return rexBuilder.makeExactLiteral(new BigDecimal(555),
+            typeFactory.createSqlType(SqlTypeName.INTEGER));
+      default:
+        return rexBuilder.constantNull();
+      }
+    }
+  }
+
   /** Types used during initialization. */
   private class Fixture {
     final RelDataType intType =
@@ -1644,7 +1668,10 @@ public class MockCatalogReader extends CalciteCatalogReader {
   }
 
   /** To check whether
-   * {@link InitializerExpressionFactory#newColumnDefaultValue} is called. */
+   * {@link InitializerExpressionFactory#newColumnDefaultValue} is called.
+   *
+   * <p>If a column is in {@code defaultColumns}, returns 1 as the default
+   * value. */
   public static class CountingFactory extends NullInitializerExpressionFactory {
     static final ThreadLocal<AtomicInteger> THREAD_CALL_COUNT =
         new ThreadLocal<AtomicInteger>() {
@@ -1653,13 +1680,31 @@ public class MockCatalogReader extends CalciteCatalogReader {
           }
         };
 
-    CountingFactory(RelDataTypeFactory typeFactory) {
-      super();
+    private final List<String> defaultColumns;
+
+    CountingFactory(List<String> defaultColumns) {
+      this.defaultColumns = ImmutableList.copyOf(defaultColumns);
+    }
+
+    @Override public ColumnStrategy generationStrategy(RelOptTable table,
+        int iColumn) {
+      final RelDataTypeField field =
+          table.getRowType().getFieldList().get(iColumn);
+      if (defaultColumns.contains(field.getName())) {
+        return ColumnStrategy.DEFAULT;
+      }
+      return super.generationStrategy(table, iColumn);
     }
 
     @Override public RexNode newColumnDefaultValue(RelOptTable table,
         int iColumn, InitializerContext context) {
       THREAD_CALL_COUNT.get().incrementAndGet();
+      final RelDataTypeField field =
+          table.getRowType().getFieldList().get(iColumn);
+      if (defaultColumns.contains(field.getName())) {
+        final RexBuilder rexBuilder = context.getRexBuilder();
+        return rexBuilder.makeExactLiteral(BigDecimal.ONE);
+      }
       return super.newColumnDefaultValue(table, iColumn, context);
     }
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/test/java/org/apache/calcite/test/QuidemTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/QuidemTest.java b/core/src/test/java/org/apache/calcite/test/QuidemTest.java
index f30ebc8..b97318e 100644
--- a/core/src/test/java/org/apache/calcite/test/QuidemTest.java
+++ b/core/src/test/java/org/apache/calcite/test/QuidemTest.java
@@ -29,7 +29,6 @@ import org.apache.calcite.schema.impl.AbstractTable;
 import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.calcite.util.Bug;
 import org.apache.calcite.util.Closer;
-import org.apache.calcite.util.TryThreadLocal;
 import org.apache.calcite.util.Util;
 
 import com.google.common.base.Function;
@@ -60,26 +59,16 @@ import static org.junit.Assert.fail;
  * Test that runs every Quidem file as a test.
  */
 @RunWith(Parameterized.class)
-public class QuidemTest {
-  private final String path;
-  private final Method method;
+public abstract class QuidemTest {
+  protected final String path;
+  protected final Method method;
 
-  public QuidemTest(String path) {
+  /** Creates a QuidemTest. */
+  protected QuidemTest(String path) {
     this.path = path;
     this.method = findMethod(path);
   }
 
-  /** Runs a test from the command line.
-   *
-   * <p>For example:
-   *
-   * <blockquote><code>java QuidemTest sql/dummy.iq</code></blockquote> */
-  public static void main(String[] args) throws Exception {
-    for (String arg : args) {
-      new QuidemTest(arg).test();
-    }
-  }
-
   private Method findMethod(String path) {
     // E.g. path "sql/agg.iq" gives method "testSqlAgg"
     String methodName =
@@ -93,12 +82,7 @@ public class QuidemTest {
     return m;
   }
 
-  /** For {@link org.junit.runners.Parameterized} runner. */
-  @Parameterized.Parameters(name = "{index}: quidem({0})")
-  public static Collection<Object[]> data() {
-    // Start with a test file we know exists, then find the directory and list
-    // its files.
-    final String first = "sql/agg.iq";
+  protected static Collection<Object[]> data(String first) {
     // inUrl = "file:/home/fred/calcite/core/target/test-classes/sql/agg.iq"
     final URL inUrl = JdbcTest.class.getResource("/" + first);
     String x = inUrl.getFile();
@@ -124,7 +108,7 @@ public class QuidemTest {
     });
   }
 
-  private void checkRun(String path) throws Exception {
+  protected void checkRun(String path) throws Exception {
     final File inFile;
     final File outFile;
     final File f = new File(path);
@@ -150,7 +134,7 @@ public class QuidemTest {
     try (final Reader reader = Util.reader(inFile);
          final Writer writer = Util.printWriter(outFile);
          final Closer closer = new Closer()) {
-      new Quidem(reader, writer, env(), new QuidemConnectionFactory())
+      new Quidem(reader, writer, env(), createConnectionFactory())
           .withPropertyHandler(new Quidem.PropertyHandler() {
             public void onSet(String propertyName, Object value) {
               if (propertyName.equals("bindable")) {
@@ -174,6 +158,11 @@ public class QuidemTest {
     }
   }
 
+  /** Creates a connection factory. */
+  protected Quidem.ConnectionFactory createConnectionFactory() {
+    return new QuidemConnectionFactory();
+  }
+
   /** Converts a path from Unix to native. On Windows, converts
    * forward-slashes to back-slashes; on Linux, does nothing. */
   private static String u2n(String s) {
@@ -221,37 +210,8 @@ public class QuidemTest {
     }
   }
 
-  /** Override settings for "sql/misc.iq". */
-  public void testSqlMisc() throws Exception {
-    switch (CalciteAssert.DB) {
-    case ORACLE:
-      // There are formatting differences (e.g. "4.000" vs "4") when using
-      // Oracle as the JDBC data source.
-      return;
-    }
-    try (final TryThreadLocal.Memo ignored = Prepare.THREAD_EXPAND.push(true)) {
-      checkRun(path);
-    }
-  }
-
-  /** Override settings for "sql/scalar.iq". */
-  public void testSqlScalar() throws Exception {
-    try (final TryThreadLocal.Memo ignored = Prepare.THREAD_EXPAND.push(true)) {
-      checkRun(path);
-    }
-  }
-
-  /** Runs the dummy script "sql/dummy.iq", which is checked in empty but
-   * which you may use as scratch space during development. */
-  // Do not add disable this test; just remember not to commit changes to dummy.iq
-  public void testSqlDummy() throws Exception {
-    try (final TryThreadLocal.Memo ignored = Prepare.THREAD_EXPAND.push(true)) {
-      checkRun(path);
-    }
-  }
-
   /** Quidem connection factory for Calcite's built-in test schemas. */
-  private static class QuidemConnectionFactory
+  protected static class QuidemConnectionFactory
       implements Quidem.ConnectionFactory {
     public Connection connect(String name) throws Exception {
       return connect(name, false);

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
index 67da90b..3f1158f 100644
--- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
@@ -114,8 +114,6 @@ import java.util.List;
 
 import static org.junit.Assert.assertTrue;
 
-
-
 /**
  * Unit test for rules in {@code org.apache.calcite.rel} and subpackages.
  *

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/test/java/org/apache/calcite/test/SqlToRelTestBase.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/SqlToRelTestBase.java b/core/src/test/java/org/apache/calcite/test/SqlToRelTestBase.java
index a6b8db4..c8ece76 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlToRelTestBase.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlToRelTestBase.java
@@ -39,6 +39,7 @@ import org.apache.calcite.rel.type.RelDataTypeFactory;
 import org.apache.calcite.rel.type.RelDataTypeField;
 import org.apache.calcite.rel.type.RelDataTypeSystem;
 import org.apache.calcite.rex.RexBuilder;
+import org.apache.calcite.schema.ColumnStrategy;
 import org.apache.calcite.sql.SqlNode;
 import org.apache.calcite.sql.SqlOperatorTable;
 import org.apache.calcite.sql.fun.SqlStdOperatorTable;
@@ -403,6 +404,10 @@ public abstract class SqlToRelTestBase {
         return ImmutableList.of();
       }
 
+      public List<ColumnStrategy> getColumnStrategies() {
+        throw new UnsupportedOperationException();
+      }
+
       public Expression getExpression(Class clazz) {
         return null;
       }
@@ -476,6 +481,10 @@ public abstract class SqlToRelTestBase {
     public List<RelReferentialConstraint> getReferentialConstraints() {
       return parent.getReferentialConstraints();
     }
+
+    public List<ColumnStrategy> getColumnStrategies() {
+      return parent.getColumnStrategies();
+    }
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
index 49de12d..c7378a3 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
@@ -8712,9 +8712,22 @@ public class SqlValidatorTest extends SqlValidatorTestCase {
         + "  timestamp '1970-01-01 00:00:00', 1, 1, 1)";
     pragmaticTester.checkQueryFails(sql3,
         "Column 'SLACKER' has no default value and does not allow NULLs");
-    assertThat("Missing non-NULL column generates a call to factory",
+    assertThat("Should not check for default value, even if if column is missing"
+            + "from INSERT and nullable",
         MockCatalogReader.CountingFactory.THREAD_CALL_COUNT.get().get(),
-        is(c + 1));
+        is(c));
+
+    // Now remove DEPTNO, which has a default value, from the target list.
+    // Will generate an extra call to newColumnDefaultValue at sql-to-rel time,
+    // just not yet.
+    final String sql4 = "insert into ^emp^ (empno, ename, job, mgr, hiredate,\n"
+        + "  sal, comm, slacker)\n"
+        + "values(1, 'nom', 'job', 0,\n"
+        + "  timestamp '1970-01-01 00:00:00', 1, 1, false)";
+    pragmaticTester.checkQuery(sql4);
+    assertThat("Missing DEFAULT column generates a call to factory",
+        MockCatalogReader.CountingFactory.THREAD_CALL_COUNT.get().get(),
+        is(c));
   }
 
   @Test public void testInsertView() {

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/test/resources/sql/blank.iq
----------------------------------------------------------------------
diff --git a/core/src/test/resources/sql/blank.iq b/core/src/test/resources/sql/blank.iq
index 6375030..bcaa9e8 100644
--- a/core/src/test/resources/sql/blank.iq
+++ b/core/src/test/resources/sql/blank.iq
@@ -19,7 +19,7 @@
 !set outputformat mysql
 
 create table foo (i int not null, j int);
-(-1 rows modified)
+(0 rows modified)
 
 !update
 insert into foo values (1, 0);
@@ -55,11 +55,11 @@ select * from foo as f where i in (
 
 # [CALCITE-1493] Wrong plan for NOT IN correlated queries
 create table table1(i int, j int);
-(-1 rows modified)
+(0 rows modified)
 
 !update
 create table table2(i int, j int);
-(-1 rows modified)
+(0 rows modified)
 
 !update
 insert into table1 values (1, 2), (1, 3);

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/mongodb/src/main/java/org/apache/calcite/adapter/mongodb/MongoTable.java
----------------------------------------------------------------------
diff --git a/mongodb/src/main/java/org/apache/calcite/adapter/mongodb/MongoTable.java b/mongodb/src/main/java/org/apache/calcite/adapter/mongodb/MongoTable.java
index 9cd05c8..fde5927 100644
--- a/mongodb/src/main/java/org/apache/calcite/adapter/mongodb/MongoTable.java
+++ b/mongodb/src/main/java/org/apache/calcite/adapter/mongodb/MongoTable.java
@@ -28,7 +28,6 @@ import org.apache.calcite.plan.RelOptTable;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeFactory;
-import org.apache.calcite.runtime.ConsList;
 import org.apache.calcite.schema.SchemaPlus;
 import org.apache.calcite.schema.TranslatableTable;
 import org.apache.calcite.schema.impl.AbstractTableQueryable;
@@ -51,6 +50,8 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
+import static org.apache.calcite.util.Static.cons;
+
 /**
  * Table based on a MongoDB collection.
  */
@@ -214,7 +215,7 @@ public class MongoTable extends AbstractQueryableTable
    */
   private AggregationOutput aggregateOldWay(DBCollection dbCollection,
        DBObject first, List<DBObject> rest) {
-    return dbCollection.aggregate(ConsList.of(first, rest));
+    return dbCollection.aggregate(cons(first, rest));
   }
 
   /** Implementation of {@link org.apache.calcite.linq4j.Queryable} based on

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 36ad85e..1feec71 100644
--- a/pom.xml
+++ b/pom.xml
@@ -156,6 +156,7 @@ limitations under the License.
     <module>pig</module>
     <module>piglet</module>
     <module>plus</module>
+    <module>server</module>
     <module>spark</module>
     <module>splunk</module>
     <module>ubenchmark</module>
@@ -655,9 +656,9 @@ limitations under the License.
         <artifactId>maven-javadoc-plugin</artifactId>
         <configuration>
           <links>
-            <link>https://docs.oracle.com/javase/8/docs/api/</link>
+            <link>https://docs.oracle.com/javase/9/docs/api/</link>
           </links>
-          <excludePackageNames>org.apache.calcite.benchmarks.generated,org.apache.calcite.sql.parser.impl,org.apache.calcite.sql.parser.parserextensiontesting,org.apache.calcite.piglet.parser,org.openjdk.jmh</excludePackageNames>
+          <excludePackageNames>org.apache.calcite.benchmarks.generated,org.apache.calcite.sql.parser.ddl,org.apache.calcite.sql.parser.impl,org.apache.calcite.sql.parser.parserextensiontesting,org.apache.calcite.piglet.parser,org.openjdk.jmh</excludePackageNames>
           <show>private</show>
         </configuration>
       </plugin>
@@ -869,7 +870,7 @@ limitations under the License.
           <links>
             <link>https://docs.oracle.com/javase/8/docs/api/</link>
           </links>
-          <excludePackageNames>org.apache.calcite.benchmarks.generated,org.apache.calcite.sql.parser.impl,org.apache.calcite.sql.parser.parserextensiontesting,org.apache.calcite.piglet.parser,org.openjdk.jmh</excludePackageNames>
+          <excludePackageNames>org.apache.calcite.benchmarks.generated,org.apache.calcite.sql.parser.ddl,org.apache.calcite.sql.parser.impl,org.apache.calcite.sql.parser.parserextensiontesting,org.apache.calcite.piglet.parser,org.openjdk.jmh</excludePackageNames>
           <notimestamp>true</notimestamp>
           <windowtitle>Apache Calcite API</windowtitle>
         </configuration>

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/server/pom.xml
----------------------------------------------------------------------
diff --git a/server/pom.xml b/server/pom.xml
new file mode 100644
index 0000000..562f8f4
--- /dev/null
+++ b/server/pom.xml
@@ -0,0 +1,245 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to you under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.calcite</groupId>
+    <artifactId>calcite</artifactId>
+    <version>1.15.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>calcite-server</artifactId>
+  <packaging>jar</packaging>
+  <version>1.15.0-SNAPSHOT</version>
+  <name>Calcite Server</name>
+  <description>Calcite Server</description>
+
+  <properties>
+    <top.dir>${project.basedir}/..</top.dir>
+  </properties>
+
+  <dependencies>
+    <!-- Sorted by groupId, artifactId; calcite dependencies first. Put versions
+         in dependencyManagement in the root POM, not here. -->
+    <dependency>
+      <groupId>org.apache.calcite.avatica</groupId>
+      <artifactId>avatica-core</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.calcite</groupId>
+      <artifactId>calcite-core</artifactId>
+      <type>jar</type>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.calcite</groupId>
+      <artifactId>calcite-core</artifactId>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.calcite</groupId>
+      <artifactId>calcite-linq4j</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>com.google.guava</groupId>
+      <artifactId>guava</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>net.hydromatic</groupId>
+      <artifactId>quidem</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>net.hydromatic</groupId>
+      <artifactId>scott-data-hsqldb</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.hamcrest</groupId>
+      <artifactId>hamcrest-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.incava</groupId>
+      <artifactId>java-diff</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.hsqldb</groupId>
+      <artifactId>hsqldb</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-log4j12</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <!-- Sorted by groupId, artifactId. Put versions in
+           pluginManagement in the root POM, not here. -->
+      <plugin>
+        <artifactId>maven-dependency-plugin</artifactId>
+        <version>${maven-dependency-plugin.version}</version>
+        <executions>
+          <execution>
+            <id>analyze</id>
+            <goals>
+              <goal>analyze-only</goal>
+            </goals>
+            <configuration>
+              <failOnWarning>true</failOnWarning>
+              <!-- ignore "unused but declared" warnings -->
+              <ignoredUnusedDeclaredDependencies>
+                <ignoredUnusedDeclaredDependency>net.hydromatic:scott-data-hsqldb</ignoredUnusedDeclaredDependency>
+                <ignoredUnusedDeclaredDependency>org.hsqldb:hsqldb</ignoredUnusedDeclaredDependency>
+                <ignoredUnusedDeclaredDependency>org.incava:java-diff</ignoredUnusedDeclaredDependency>
+                <ignoredUnusedDeclaredDependency>org.slf4j:slf4j-api</ignoredUnusedDeclaredDependency>
+                <ignoredUnusedDeclaredDependency>org.slf4j:slf4j-log4j12</ignoredUnusedDeclaredDependency>
+              </ignoredUnusedDeclaredDependencies>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <executions>
+          <execution>
+            <goals>
+              <goal>test-jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <artifactId>maven-resources-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>copy-fmpp-resources</id>
+            <phase>initialize</phase>
+            <goals>
+              <goal>copy-resources</goal>
+            </goals>
+            <configuration>
+              <outputDirectory>${project.build.directory}/codegen</outputDirectory>
+              <resources>
+                <resource>
+                  <directory>src/main/codegen</directory>
+                  <filtering>false</filtering>
+                </resource>
+              </resources>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-release-plugin</artifactId>
+      </plugin>
+      <!-- Parent module has the same plugin and does the work of
+           generating -sources.jar for each project. But without the
+           plugin declared here, IDEs don't know the sources are
+           available. -->
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-source-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>attach-sources</id>
+            <phase>verify</phase>
+            <goals>
+              <goal>jar-no-fork</goal>
+              <goal>test-jar-no-fork</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>javacc-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>javacc</id>
+            <goals>
+              <goal>javacc</goal>
+            </goals>
+            <configuration>
+              <sourceDirectory>${project.build.directory}/generated-sources/fmpp</sourceDirectory>
+              <outputDirectory>${project.build.directory}/generated-sources/javacc</outputDirectory>
+              <includes>
+                <include>**/Parser.jj</include>
+              </includes>
+              <lookAhead>2</lookAhead>
+              <isStatic>false</isStatic>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+  <profiles>
+    <profile>
+      <!-- CALCITE-538: workaround for https://github.com/freemarker/fmpp/issues/11
+        FMPP always overwrites destination file, however we do not want
+        recompile the whole module every time.
+      -->
+      <id>generate-parser</id>
+      <activation>
+        <property>
+          <name>!skipGenerate</name>
+        </property>
+      </activation>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>com.googlecode.fmpp-maven-plugin</groupId>
+            <artifactId>fmpp-maven-plugin</artifactId>
+            <executions>
+              <execution>
+                <configuration>
+                  <cfgFile>src/main/codegen/config.fmpp</cfgFile>
+                  <outputDirectory>${project.build.directory}/generated-sources/fmpp</outputDirectory>
+                  <templateDirectory>${top.dir}/core/src/main/codegen/templates</templateDirectory>
+                </configuration>
+                <id>generate-fmpp-sources</id>
+                <phase>validate</phase>
+                <goals>
+                  <goal>generate</goal>
+                </goals>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
+</project>

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/server/src/main/codegen/config.fmpp
----------------------------------------------------------------------
diff --git a/server/src/main/codegen/config.fmpp b/server/src/main/codegen/config.fmpp
new file mode 100644
index 0000000..08b715a
--- /dev/null
+++ b/server/src/main/codegen/config.fmpp
@@ -0,0 +1,99 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to you under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+data: {
+    parser: {
+      # Generated parser implementation class package and name
+      package: "org.apache.calcite.sql.parser.ddl",
+      class: "SqlDdlParserImpl",
+
+      # List of import statements.
+      imports: [
+        "org.apache.calcite.schema.ColumnStrategy"
+        "org.apache.calcite.sql.SqlCreate"
+        "org.apache.calcite.sql.SqlDrop"
+        "org.apache.calcite.sql.ddl.SqlDdlNodes"
+      ]
+
+      # List of keywords.
+      keywords: [
+        "IF"
+        "MATERIALIZED"
+        "STORED"
+        "VIRTUAL"
+      ]
+
+      # List of keywords from "keywords" section that are not reserved.
+      nonReservedKeywords: [
+        "IF"
+        "MATERIALIZED"
+        "STORED"
+        "VIRTUAL"
+      ]
+
+      # List of methods for parsing custom SQL statements.
+      statementParserMethods: [
+      ]
+
+      # List of methods for parsing custom literals.
+      # Example: ParseJsonLiteral().
+      literalParserMethods: [
+      ]
+
+      # List of methods for parsing custom data types.
+      dataTypeParserMethods: [
+      ]
+
+      # List of methods for parsing extensions to "ALTER <scope>" calls.
+      # Each must accept arguments "(SqlParserPos pos, String scope)".
+      alterStatementParserMethods: [
+      ]
+
+      # List of methods for parsing extensions to "CREATE [OR REPLACE]" calls.
+      # Each must accept arguments "(SqlParserPos pos, boolean replace)".
+      createStatementParserMethods: [
+        "SqlCreateForeignSchema"
+        "SqlCreateMaterializedView"
+        "SqlCreateSchema"
+        "SqlCreateTable"
+        "SqlCreateView"
+      ]
+
+      # List of methods for parsing extensions to "DROP" calls.
+      # Each must accept arguments "(SqlParserPos pos)".
+      dropStatementParserMethods: [
+        "SqlDropMaterializedView"
+        "SqlDropSchema"
+        "SqlDropTable"
+        "SqlDropView"
+      ]
+
+      # List of files in @includes directory that have parser method
+      # implementations for parsing custom SQL statements, literals or types
+      # given as part of "statementParserMethods", "literalParserMethods" or
+      # "dataTypeParserMethods".
+      implementationFiles: [
+        "parserImpls.ftl"
+      ]
+
+      includeCompoundIdentifier: true
+      includeBraces: true
+      includeAdditionalDeclarations: false
+
+    }
+}
+freemarkerLinks: {
+    includes: includes/
+}

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/server/src/main/codegen/includes/parserImpls.ftl
----------------------------------------------------------------------
diff --git a/server/src/main/codegen/includes/parserImpls.ftl b/server/src/main/codegen/includes/parserImpls.ftl
new file mode 100644
index 0000000..9c54540
--- /dev/null
+++ b/server/src/main/codegen/includes/parserImpls.ftl
@@ -0,0 +1,293 @@
+<#--
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements.  See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to you under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License.  You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+-->
+
+boolean IfNotExistsOpt() :
+{
+}
+{
+    <IF> <NOT> <EXISTS> { return true; }
+|
+    { return false; }
+}
+
+boolean IfExistsOpt() :
+{
+}
+{
+    <IF> <EXISTS> { return true; }
+|
+    { return false; }
+}
+
+SqlCreate SqlCreateSchema(Span s, boolean replace) :
+{
+    final boolean ifNotExists;
+    final SqlIdentifier id;
+}
+{
+    <SCHEMA> ifNotExists = IfNotExistsOpt() id = CompoundIdentifier()
+    {
+        return SqlDdlNodes.createSchema(s.end(this), replace, ifNotExists, id);
+    }
+}
+
+SqlCreate SqlCreateForeignSchema(Span s, boolean replace) :
+{
+    final boolean ifNotExists;
+    final SqlIdentifier id;
+    SqlNode type = null;
+    SqlNode library = null;
+    SqlNodeList optionList = null;
+}
+{
+    <FOREIGN> <SCHEMA> ifNotExists = IfNotExistsOpt() id = CompoundIdentifier()
+    (
+         <TYPE> type = StringLiteral()
+    |
+         <LIBRARY> library = StringLiteral()
+    )
+    [ optionList = Options() ]
+    {
+        return SqlDdlNodes.createForeignSchema(s.end(this), replace,
+            ifNotExists, id, type, library, optionList);
+    }
+}
+
+SqlNodeList Options() :
+{
+    final Span s;
+    final List<SqlNode> list = Lists.newArrayList();
+}
+{
+    <OPTIONS> { s = span(); } <LPAREN>
+    [
+        Option(list)
+        (
+            <COMMA>
+            Option(list)
+        )*
+    ]
+    <RPAREN> {
+        return new SqlNodeList(list, s.end(this));
+    }
+}
+
+void Option(List<SqlNode> list) :
+{
+    final SqlIdentifier id;
+    final SqlNode value;
+}
+{
+    id = SimpleIdentifier()
+    value = Literal() {
+        list.add(id);
+        list.add(value);
+    }
+}
+
+SqlNodeList TableElementList() :
+{
+    final Span s;
+    final List<SqlNode> list = Lists.newArrayList();
+}
+{
+    <LPAREN> { s = span(); }
+    TableElement(list)
+    (
+        <COMMA> TableElement(list)
+    )*
+    <RPAREN> {
+        return new SqlNodeList(list, s.end(this));
+    }
+}
+
+void TableElement(List<SqlNode> list) :
+{
+    final SqlIdentifier id;
+    final SqlDataTypeSpec type;
+    final boolean nullable;
+    final SqlNode e;
+    final SqlNode constraint;
+    SqlIdentifier name = null;
+    final SqlNodeList columnList;
+    final Span s = Span.of();
+    final ColumnStrategy strategy;
+}
+{
+    id = SimpleIdentifier()
+    (
+        type = DataType()
+        (
+            <NULL> { nullable = true; }
+        |
+            <NOT> <NULL> { nullable = false; }
+        |
+            { nullable = true; }
+        )
+        (
+            [ <GENERATED> <ALWAYS> ] <AS> <LPAREN>
+            e = Expression(ExprContext.ACCEPT_SUB_QUERY) <RPAREN>
+            (
+                <VIRTUAL> { strategy = ColumnStrategy.VIRTUAL; }
+            |
+                <STORED> { strategy = ColumnStrategy.STORED; }
+            |
+                { strategy = ColumnStrategy.VIRTUAL; }
+            )
+        |
+            <DEFAULT_> e = Expression(ExprContext.ACCEPT_SUB_QUERY) {
+                strategy = ColumnStrategy.DEFAULT;
+            }
+        |
+            {
+                e = null;
+                strategy = nullable ? ColumnStrategy.NULLABLE
+                    : ColumnStrategy.NOT_NULLABLE;
+            }
+        )
+        {
+            list.add(
+                SqlDdlNodes.column(s.add(id).end(this), id,
+                    type.withNullable(nullable), e, strategy));
+        }
+    |
+        { list.add(id); }
+    )
+|
+    id = SimpleIdentifier() {
+        list.add(id);
+    }
+|
+    [ <CONSTRAINT> { s.add(this); } name = SimpleIdentifier() ]
+    (
+        <CHECK> { s.add(this); } <LPAREN>
+        e = Expression(ExprContext.ACCEPT_SUB_QUERY) <RPAREN> {
+            list.add(SqlDdlNodes.check(s.end(this), name, e));
+        }
+    |
+        <UNIQUE> { s.add(this); }
+        columnList = ParenthesizedSimpleIdentifierList() {
+            list.add(SqlDdlNodes.unique(s.end(columnList), name, columnList));
+        }
+    |
+        <PRIMARY>  { s.add(this); } <KEY>
+        columnList = ParenthesizedSimpleIdentifierList() {
+            list.add(SqlDdlNodes.primary(s.end(columnList), name, columnList));
+        }
+    )
+}
+
+SqlCreate SqlCreateTable(Span s, boolean replace) :
+{
+    final boolean ifNotExists;
+    final SqlIdentifier id;
+    SqlNodeList tableElementList = null;
+    SqlNode query = null;
+}
+{
+    <TABLE> ifNotExists = IfNotExistsOpt() id = CompoundIdentifier()
+    [ tableElementList = TableElementList() ]
+    [ <AS> query = OrderedQueryOrExpr(ExprContext.ACCEPT_QUERY) ]
+    {
+        return SqlDdlNodes.createTable(s.end(this), replace, ifNotExists, id,
+            tableElementList, query);
+    }
+}
+
+SqlCreate SqlCreateView(Span s, boolean replace) :
+{
+    final SqlIdentifier id;
+    SqlNodeList columnList = null;
+    final SqlNode query;
+}
+{
+    <VIEW> id = CompoundIdentifier()
+    [ columnList = ParenthesizedSimpleIdentifierList() ]
+    <AS> query = OrderedQueryOrExpr(ExprContext.ACCEPT_QUERY) {
+        return SqlDdlNodes.createView(s.end(this), replace, id, columnList,
+            query);
+    }
+}
+
+SqlCreate SqlCreateMaterializedView(Span s, boolean replace) :
+{
+    final boolean ifNotExists;
+    final SqlIdentifier id;
+    SqlNodeList columnList = null;
+    final SqlNode query;
+}
+{
+    <MATERIALIZED> <VIEW> ifNotExists = IfNotExistsOpt()
+    id = CompoundIdentifier()
+    [ columnList = ParenthesizedSimpleIdentifierList() ]
+    <AS> query = OrderedQueryOrExpr(ExprContext.ACCEPT_QUERY) {
+        return SqlDdlNodes.createMaterializedView(s.end(this), replace,
+            ifNotExists, id, columnList, query);
+    }
+}
+
+SqlDrop SqlDropSchema(Span s, boolean replace) :
+{
+    final boolean ifExists;
+    final SqlIdentifier id;
+    final boolean foreign;
+}
+{
+    (
+        <FOREIGN> { foreign = true; }
+    |
+        { foreign = false; }
+    )
+    <SCHEMA> ifExists = IfExistsOpt() id = CompoundIdentifier() {
+        return SqlDdlNodes.dropSchema(s.end(this), foreign, ifExists, id);
+    }
+}
+
+SqlDrop SqlDropTable(Span s, boolean replace) :
+{
+    final boolean ifExists;
+    final SqlIdentifier id;
+}
+{
+    <TABLE> ifExists = IfExistsOpt() id = CompoundIdentifier() {
+        return SqlDdlNodes.dropTable(s.end(this), ifExists, id);
+    }
+}
+
+SqlDrop SqlDropView(Span s, boolean replace) :
+{
+    final boolean ifExists;
+    final SqlIdentifier id;
+}
+{
+    <VIEW> ifExists = IfExistsOpt() id = CompoundIdentifier() {
+        return SqlDdlNodes.dropView(s.end(this), ifExists, id);
+    }
+}
+
+SqlDrop SqlDropMaterializedView(Span s, boolean replace) :
+{
+    final boolean ifExists;
+    final SqlIdentifier id;
+}
+{
+    <MATERIALIZED> <VIEW> ifExists = IfExistsOpt() id = CompoundIdentifier() {
+        return SqlDdlNodes.dropMaterializedView(s.end(this), ifExists, id);
+    }
+}
+
+// End parserImpls.ftl

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/server/src/main/java/org/apache/calcite/sql/ddl/SqlCheckConstraint.java
----------------------------------------------------------------------
diff --git a/server/src/main/java/org/apache/calcite/sql/ddl/SqlCheckConstraint.java b/server/src/main/java/org/apache/calcite/sql/ddl/SqlCheckConstraint.java
new file mode 100644
index 0000000..b80e092
--- /dev/null
+++ b/server/src/main/java/org/apache/calcite/sql/ddl/SqlCheckConstraint.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.sql.ddl;
+
+import org.apache.calcite.sql.SqlCall;
+import org.apache.calcite.sql.SqlIdentifier;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.SqlNode;
+import org.apache.calcite.sql.SqlOperator;
+import org.apache.calcite.sql.SqlSpecialOperator;
+import org.apache.calcite.sql.SqlWriter;
+import org.apache.calcite.sql.parser.SqlParserPos;
+import org.apache.calcite.util.ImmutableNullableList;
+
+import java.util.List;
+
+/**
+ * Parse tree for {@code UNIQUE}, {@code PRIMARY KEY} constraints.
+ *
+ * <p>And {@code FOREIGN KEY}, when we support it.
+ */
+public class SqlCheckConstraint extends SqlCall {
+  private static final SqlSpecialOperator OPERATOR =
+      new SqlSpecialOperator("CHECK", SqlKind.CHECK);
+
+  private final SqlIdentifier name;
+  private final SqlNode expression;
+
+  /** Creates a SqlCheckConstraint; use {@link SqlDdlNodes#check}. */
+  SqlCheckConstraint(SqlParserPos pos, SqlIdentifier name,
+      SqlNode expression) {
+    super(pos);
+    this.name = name; // may be null
+    this.expression = expression;
+  }
+
+  @Override public SqlOperator getOperator() {
+    return OPERATOR;
+  }
+
+  @Override public List<SqlNode> getOperandList() {
+    return ImmutableNullableList.of(name, expression);
+  }
+
+  @Override public void unparse(SqlWriter writer, int leftPrec, int rightPrec) {
+    if (name != null) {
+      writer.keyword("CONSTRAINT");
+      name.unparse(writer, 0, 0);
+    }
+    writer.keyword("CHECK");
+    if (writer.isAlwaysUseParentheses()) {
+      expression.unparse(writer, 0, 0);
+    } else {
+      writer.sep("(");
+      expression.unparse(writer, 0, 0);
+      writer.sep(")");
+    }
+  }
+}
+
+// End SqlCheckConstraint.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/server/src/main/java/org/apache/calcite/sql/ddl/SqlColumnDeclaration.java
----------------------------------------------------------------------
diff --git a/server/src/main/java/org/apache/calcite/sql/ddl/SqlColumnDeclaration.java b/server/src/main/java/org/apache/calcite/sql/ddl/SqlColumnDeclaration.java
new file mode 100644
index 0000000..95ff82c
--- /dev/null
+++ b/server/src/main/java/org/apache/calcite/sql/ddl/SqlColumnDeclaration.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.sql.ddl;
+
+import org.apache.calcite.schema.ColumnStrategy;
+import org.apache.calcite.sql.SqlCall;
+import org.apache.calcite.sql.SqlDataTypeSpec;
+import org.apache.calcite.sql.SqlIdentifier;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.SqlNode;
+import org.apache.calcite.sql.SqlOperator;
+import org.apache.calcite.sql.SqlSpecialOperator;
+import org.apache.calcite.sql.SqlWriter;
+import org.apache.calcite.sql.parser.SqlParserPos;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+
+/**
+ * Parse tree for {@code UNIQUE}, {@code PRIMARY KEY} constraints.
+ *
+ * <p>And {@code FOREIGN KEY}, when we support it.
+ */
+public class SqlColumnDeclaration extends SqlCall {
+  private static final SqlSpecialOperator OPERATOR =
+      new SqlSpecialOperator("COLUMN_DECL", SqlKind.COLUMN_DECL);
+
+  final SqlIdentifier name;
+  final SqlDataTypeSpec dataType;
+  final SqlNode expression;
+  final ColumnStrategy strategy;
+
+  /** Creates a SqlColumnDeclaration; use {@link SqlDdlNodes#column}. */
+  SqlColumnDeclaration(SqlParserPos pos, SqlIdentifier name,
+      SqlDataTypeSpec dataType, SqlNode expression,
+      ColumnStrategy strategy) {
+    super(pos);
+    this.name = name;
+    this.dataType = dataType;
+    this.expression = expression;
+    this.strategy = strategy;
+  }
+
+  @Override public SqlOperator getOperator() {
+    return OPERATOR;
+  }
+
+  @Override public List<SqlNode> getOperandList() {
+    return ImmutableList.of(name, dataType);
+  }
+
+  @Override public void unparse(SqlWriter writer, int leftPrec, int rightPrec) {
+    name.unparse(writer, 0, 0);
+    dataType.unparse(writer, 0, 0);
+    if (dataType.getNullable() != null && !dataType.getNullable()) {
+      writer.keyword("NOT NULL");
+    }
+    if (expression != null) {
+      switch (strategy) {
+      case VIRTUAL:
+      case STORED:
+        writer.keyword("AS");
+        exp(writer);
+        writer.keyword(strategy.name());
+        break;
+      case DEFAULT:
+        writer.keyword("DEFAULT");
+        exp(writer);
+        break;
+      default:
+        throw new AssertionError("unexpected: " + strategy);
+      }
+    }
+  }
+
+  private void exp(SqlWriter writer) {
+    if (writer.isAlwaysUseParentheses()) {
+      expression.unparse(writer, 0, 0);
+    } else {
+      writer.sep("(");
+      expression.unparse(writer, 0, 0);
+      writer.sep(")");
+    }
+  }
+}
+
+// End SqlColumnDeclaration.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/server/src/main/java/org/apache/calcite/sql/ddl/SqlCreateForeignSchema.java
----------------------------------------------------------------------
diff --git a/server/src/main/java/org/apache/calcite/sql/ddl/SqlCreateForeignSchema.java b/server/src/main/java/org/apache/calcite/sql/ddl/SqlCreateForeignSchema.java
new file mode 100644
index 0000000..96ce3f9
--- /dev/null
+++ b/server/src/main/java/org/apache/calcite/sql/ddl/SqlCreateForeignSchema.java
@@ -0,0 +1,191 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.sql.ddl;
+
+import org.apache.calcite.adapter.jdbc.JdbcSchema;
+import org.apache.calcite.avatica.AvaticaUtils;
+import org.apache.calcite.jdbc.CalcitePrepare;
+import org.apache.calcite.jdbc.CalciteSchema;
+import org.apache.calcite.model.JsonSchema;
+import org.apache.calcite.schema.Schema;
+import org.apache.calcite.schema.SchemaFactory;
+import org.apache.calcite.schema.SchemaPlus;
+import org.apache.calcite.sql.SqlCreate;
+import org.apache.calcite.sql.SqlExecutableStatement;
+import org.apache.calcite.sql.SqlIdentifier;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.SqlLiteral;
+import org.apache.calcite.sql.SqlNode;
+import org.apache.calcite.sql.SqlNodeList;
+import org.apache.calcite.sql.SqlOperator;
+import org.apache.calcite.sql.SqlSpecialOperator;
+import org.apache.calcite.sql.SqlUtil;
+import org.apache.calcite.sql.SqlWriter;
+import org.apache.calcite.sql.parser.SqlParserPos;
+import org.apache.calcite.util.ImmutableNullableList;
+import org.apache.calcite.util.NlsString;
+import org.apache.calcite.util.Pair;
+import org.apache.calcite.util.Util;
+
+import com.google.common.base.Preconditions;
+
+import java.util.AbstractList;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import static org.apache.calcite.util.Static.RESOURCE;
+
+/**
+ * Parse tree for {@code CREATE FOREIGN SCHEMA} statement.
+ */
+public class SqlCreateForeignSchema extends SqlCreate
+    implements SqlExecutableStatement {
+  private final SqlIdentifier name;
+  private final SqlNode type;
+  private final SqlNode library;
+  private final SqlNodeList optionList;
+
+  private static final SqlOperator OPERATOR =
+      new SqlSpecialOperator("CREATE FOREIGN SCHEMA",
+          SqlKind.CREATE_FOREIGN_SCHEMA);
+
+  /** Creates a SqlCreateForeignSchema. */
+  SqlCreateForeignSchema(SqlParserPos pos, boolean replace, boolean ifNotExists,
+      SqlIdentifier name, SqlNode type, SqlNode library,
+      SqlNodeList optionList) {
+    super(OPERATOR, pos, replace, ifNotExists);
+    this.name = Preconditions.checkNotNull(name);
+    this.type = type;
+    this.library = library;
+    Preconditions.checkArgument((type == null) != (library == null),
+        "of type and library, exactly one must be specified");
+    this.optionList = optionList; // may be null
+  }
+
+  @Override public List<SqlNode> getOperandList() {
+    return ImmutableNullableList.of(name, type, library, optionList);
+  }
+
+  @Override public void unparse(SqlWriter writer, int leftPrec, int rightPrec) {
+    if (getReplace()) {
+      writer.keyword("CREATE OR REPLACE");
+    } else {
+      writer.keyword("CREATE");
+    }
+    writer.keyword("FOREIGN SCHEMA");
+    if (ifNotExists) {
+      writer.keyword("IF NOT EXISTS");
+    }
+    name.unparse(writer, leftPrec, rightPrec);
+    if (library != null) {
+      writer.keyword("LIBRARY");
+      library.unparse(writer, 0, 0);
+    }
+    if (type != null) {
+      writer.keyword("TYPE");
+      type.unparse(writer, 0, 0);
+    }
+    if (optionList != null) {
+      writer.keyword("OPTIONS");
+      SqlWriter.Frame frame = writer.startList("(", ")");
+      int i = 0;
+      for (Pair<SqlIdentifier, SqlNode> c : options(optionList)) {
+        if (i++ > 0) {
+          writer.sep(",");
+        }
+        c.left.unparse(writer, 0, 0);
+        c.right.unparse(writer, 0, 0);
+      }
+      writer.endList(frame);
+    }
+  }
+
+  public void execute(CalcitePrepare.Context context) {
+    final Pair<CalciteSchema, String> pair =
+        SqlDdlNodes.schema(context, true, name);
+    final SchemaPlus subSchema0 = pair.left.plus().getSubSchema(pair.right);
+    if (subSchema0 != null) {
+      if (!getReplace() && !ifNotExists) {
+        throw SqlUtil.newContextException(name.getParserPosition(),
+            RESOURCE.schemaExists(pair.right));
+      }
+    }
+    final Schema subSchema;
+    final String libraryName;
+    if (type != null) {
+      Preconditions.checkArgument(library == null);
+      final String typeName = (String) value(this.type);
+      final JsonSchema.Type type =
+          Util.enumVal(JsonSchema.Type.class,
+              typeName.toUpperCase(Locale.ROOT));
+      if (type != null) {
+        switch (type) {
+        case JDBC:
+          libraryName = JdbcSchema.Factory.class.getName();
+          break;
+        default:
+          libraryName = null;
+        }
+      } else {
+        libraryName = null;
+      }
+      if (libraryName == null) {
+        throw SqlUtil.newContextException(this.type.getParserPosition(),
+            RESOURCE.schemaInvalidType(typeName,
+                Arrays.toString(JsonSchema.Type.values())));
+      }
+    } else {
+      Preconditions.checkArgument(library != null);
+      libraryName = (String) value(library);
+    }
+    final SchemaFactory schemaFactory =
+        AvaticaUtils.instantiatePlugin(SchemaFactory.class, libraryName);
+    final Map<String, Object> operandMap = new LinkedHashMap<>();
+    for (Pair<SqlIdentifier, SqlNode> option : options(optionList)) {
+      operandMap.put(option.left.getSimple(), value(option.right));
+    }
+    subSchema =
+        schemaFactory.create(pair.left.plus(), pair.right, operandMap);
+    pair.left.add(pair.right, subSchema);
+  }
+
+  /** Returns the value of a literal, converting
+   * {@link NlsString} into String. */
+  private static Comparable value(SqlNode node) {
+    final Comparable v = SqlLiteral.value(node);
+    return v instanceof NlsString ? ((NlsString) v).getValue() : v;
+  }
+
+  private static List<Pair<SqlIdentifier, SqlNode>> options(
+      final SqlNodeList optionList) {
+    return new AbstractList<Pair<SqlIdentifier, SqlNode>>() {
+      public Pair<SqlIdentifier, SqlNode> get(int index) {
+        return Pair.of((SqlIdentifier) optionList.get(index * 2),
+            optionList.get(index * 2 + 1));
+      }
+
+      public int size() {
+        return optionList.size() / 2;
+      }
+    };
+  }
+}
+
+// End SqlCreateForeignSchema.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/server/src/main/java/org/apache/calcite/sql/ddl/SqlCreateMaterializedView.java
----------------------------------------------------------------------
diff --git a/server/src/main/java/org/apache/calcite/sql/ddl/SqlCreateMaterializedView.java b/server/src/main/java/org/apache/calcite/sql/ddl/SqlCreateMaterializedView.java
new file mode 100644
index 0000000..9e32429
--- /dev/null
+++ b/server/src/main/java/org/apache/calcite/sql/ddl/SqlCreateMaterializedView.java
@@ -0,0 +1,157 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.sql.ddl;
+
+import org.apache.calcite.jdbc.CalcitePrepare;
+import org.apache.calcite.jdbc.CalciteSchema;
+import org.apache.calcite.materialize.MaterializationKey;
+import org.apache.calcite.materialize.MaterializationService;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeImpl;
+import org.apache.calcite.rel.type.RelProtoDataType;
+import org.apache.calcite.schema.Schema;
+import org.apache.calcite.schema.TranslatableTable;
+import org.apache.calcite.schema.impl.ViewTable;
+import org.apache.calcite.schema.impl.ViewTableMacro;
+import org.apache.calcite.sql.SqlCreate;
+import org.apache.calcite.sql.SqlExecutableStatement;
+import org.apache.calcite.sql.SqlIdentifier;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.SqlNode;
+import org.apache.calcite.sql.SqlNodeList;
+import org.apache.calcite.sql.SqlOperator;
+import org.apache.calcite.sql.SqlSpecialOperator;
+import org.apache.calcite.sql.SqlUtil;
+import org.apache.calcite.sql.SqlWriter;
+import org.apache.calcite.sql.dialect.CalciteSqlDialect;
+import org.apache.calcite.sql.parser.SqlParserPos;
+import org.apache.calcite.sql2rel.NullInitializerExpressionFactory;
+import org.apache.calcite.util.ImmutableNullableList;
+import org.apache.calcite.util.Pair;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+
+import static org.apache.calcite.util.Static.RESOURCE;
+
+/**
+ * Parse tree for {@code CREATE MATERIALIZED VIEW} statement.
+ */
+public class SqlCreateMaterializedView extends SqlCreate
+    implements SqlExecutableStatement {
+  private final SqlIdentifier name;
+  private final SqlNodeList columnList;
+  private final SqlNode query;
+
+  private static final SqlOperator OPERATOR =
+      new SqlSpecialOperator("CREATE MATERIALIZED VIEW",
+          SqlKind.CREATE_MATERIALIZED_VIEW);
+
+  /** Creates a SqlCreateView. */
+  SqlCreateMaterializedView(SqlParserPos pos, boolean replace,
+      boolean ifNotExists, SqlIdentifier name, SqlNodeList columnList,
+      SqlNode query) {
+    super(OPERATOR, pos, replace, ifNotExists);
+    this.name = Preconditions.checkNotNull(name);
+    this.columnList = columnList; // may be null
+    this.query = Preconditions.checkNotNull(query);
+  }
+
+  public List<SqlNode> getOperandList() {
+    return ImmutableNullableList.of(name, columnList, query);
+  }
+
+  @Override public void unparse(SqlWriter writer, int leftPrec, int rightPrec) {
+    writer.keyword("CREATE");
+    writer.keyword("MATERIALIZED VIEW");
+    if (ifNotExists) {
+      writer.keyword("IF NOT EXISTS");
+    }
+    name.unparse(writer, leftPrec, rightPrec);
+    if (columnList != null) {
+      SqlWriter.Frame frame = writer.startList("(", ")");
+      for (SqlNode c : columnList) {
+        writer.sep(",");
+        c.unparse(writer, 0, 0);
+      }
+      writer.endList(frame);
+    }
+    writer.keyword("AS");
+    writer.newlineAndIndent();
+    query.unparse(writer, 0, 0);
+  }
+
+  public void execute(CalcitePrepare.Context context) {
+    final Pair<CalciteSchema, String> pair =
+        SqlDdlNodes.schema(context, true, name);
+    if (pair.left.plus().getTable(pair.right) != null) {
+      // Materialized view exists.
+      if (!ifNotExists) {
+        // They did not specify IF NOT EXISTS, so give error.
+        throw SqlUtil.newContextException(name.getParserPosition(),
+            RESOURCE.tableExists(pair.right));
+      }
+      return;
+    }
+    final SqlNode q = SqlDdlNodes.renameColumns(columnList, query);
+    final String sql = q.toSqlString(CalciteSqlDialect.DEFAULT).getSql();
+    final List<String> schemaPath = pair.left.path(null);
+    final ViewTableMacro viewTableMacro =
+        ViewTable.viewMacro(pair.left.plus(), sql, schemaPath,
+            context.getObjectPath(), false);
+    final TranslatableTable x = viewTableMacro.apply(ImmutableList.of());
+    final RelDataType rowType = x.getRowType(context.getTypeFactory());
+
+    // Table does not exist. Create it.
+    final MaterializedViewTable table =
+        new MaterializedViewTable(pair.right, RelDataTypeImpl.proto(rowType));
+    pair.left.add(pair.right, table);
+    SqlDdlNodes.populate(name, query, context);
+    table.key =
+        MaterializationService.instance().defineMaterialization(pair.left, null,
+            sql, schemaPath, pair.right, true, true);
+  }
+
+  /** A table that implements a materialized view. */
+  private static class MaterializedViewTable
+      extends SqlCreateTable.MutableArrayTable {
+    /** The key with which this was stored in the materialization service,
+     * or null if not (yet) materialized. */
+    MaterializationKey key;
+
+    MaterializedViewTable(String name, RelProtoDataType protoRowType) {
+      super(name, protoRowType, protoRowType,
+          NullInitializerExpressionFactory.INSTANCE);
+    }
+
+    @Override public Schema.TableType getJdbcTableType() {
+      return Schema.TableType.MATERIALIZED_VIEW;
+    }
+
+    @Override public <C> C unwrap(Class<C> aClass) {
+      if (MaterializationKey.class.isAssignableFrom(aClass)
+          && aClass.isInstance(key)) {
+        return aClass.cast(key);
+      }
+      return super.unwrap(aClass);
+    }
+  }
+}
+
+// End SqlCreateMaterializedView.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/server/src/main/java/org/apache/calcite/sql/ddl/SqlCreateSchema.java
----------------------------------------------------------------------
diff --git a/server/src/main/java/org/apache/calcite/sql/ddl/SqlCreateSchema.java b/server/src/main/java/org/apache/calcite/sql/ddl/SqlCreateSchema.java
new file mode 100644
index 0000000..84eab77
--- /dev/null
+++ b/server/src/main/java/org/apache/calcite/sql/ddl/SqlCreateSchema.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.sql.ddl;
+
+import org.apache.calcite.jdbc.CalcitePrepare;
+import org.apache.calcite.jdbc.CalciteSchema;
+import org.apache.calcite.schema.Schema;
+import org.apache.calcite.schema.SchemaPlus;
+import org.apache.calcite.schema.impl.AbstractSchema;
+import org.apache.calcite.sql.SqlCreate;
+import org.apache.calcite.sql.SqlExecutableStatement;
+import org.apache.calcite.sql.SqlIdentifier;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.SqlNode;
+import org.apache.calcite.sql.SqlOperator;
+import org.apache.calcite.sql.SqlSpecialOperator;
+import org.apache.calcite.sql.SqlUtil;
+import org.apache.calcite.sql.SqlWriter;
+import org.apache.calcite.sql.parser.SqlParserPos;
+import org.apache.calcite.util.ImmutableNullableList;
+import org.apache.calcite.util.Pair;
+
+import com.google.common.base.Preconditions;
+
+import java.util.List;
+
+import static org.apache.calcite.util.Static.RESOURCE;
+
+/**
+ * Parse tree for {@code CREATE SCHEMA} statement.
+ */
+public class SqlCreateSchema extends SqlCreate
+    implements SqlExecutableStatement {
+  private final SqlIdentifier name;
+
+  private static final SqlOperator OPERATOR =
+      new SqlSpecialOperator("CREATE SCHEMA", SqlKind.CREATE_SCHEMA);
+
+  /** Creates a SqlCreateSchema. */
+  SqlCreateSchema(SqlParserPos pos, boolean replace, boolean ifNotExists,
+      SqlIdentifier name) {
+    super(OPERATOR, pos, replace, ifNotExists);
+    this.name = Preconditions.checkNotNull(name);
+  }
+
+  @Override public List<SqlNode> getOperandList() {
+    return ImmutableNullableList.<SqlNode>of(name);
+  }
+
+  @Override public void unparse(SqlWriter writer, int leftPrec, int rightPrec) {
+    if (getReplace()) {
+      writer.keyword("CREATE OR REPLACE");
+    } else {
+      writer.keyword("CREATE");
+    }
+    writer.keyword("SCHEMA");
+    if (ifNotExists) {
+      writer.keyword("IF NOT EXISTS");
+    }
+    name.unparse(writer, leftPrec, rightPrec);
+  }
+
+  public void execute(CalcitePrepare.Context context) {
+    final Pair<CalciteSchema, String> pair =
+        SqlDdlNodes.schema(context, true, name);
+    final SchemaPlus subSchema0 = pair.left.plus().getSubSchema(pair.right);
+    if (subSchema0 != null) {
+      if (!getReplace() && !ifNotExists) {
+        throw SqlUtil.newContextException(name.getParserPosition(),
+            RESOURCE.schemaExists(pair.right));
+      }
+    }
+    final Schema subSchema = new AbstractSchema();
+    pair.left.add(pair.right, subSchema);
+  }
+}
+
+// End SqlCreateSchema.java


[4/5] calcite git commit: [CALCITE-707] Add "server" module, with built-in support for simple DDL statements

Posted by jh...@apache.org.
[CALCITE-707] Add "server" module, with built-in support for simple DDL statements

The default parser in core does not contain DDL. We do not want to
impose our DDL dialect on sub-projects.

In server module's parser, add CREATE [FOREIGN] SCHEMA, DROP SCHEMA,
CREATE TABLE, CREATE TABLE AS ..., DROP TABLE, CREATE VIEW,
CREATE MATERIALIZED VIEW, DROP VIEW.

CREATE TABLE supports STORED and VIRTUAL generated
columns, default column values, and constraints.

Add Quidem test in server module; QuidemTest is now abstract, and has
sub-class CoreQuidemTest in core module.

Add class ColumnStrategy, which describes how a column is populated.

All CREATE commands have IF NOT EXISTS (except CREATE VIEW, which has
OR REPLACE), and all DROP commands have IF EXISTS.

Add SqlDdl as base class for SqlCreate and SqlDrop. Add SqlOperator as
first argument to SqlCreate and SqlDrop constructors, and deprecate
previous constructors.

Ensure that collations deduced for Calc are sorted.

Add Static.cons as short-hand for ConsList.of.


Project: http://git-wip-us.apache.org/repos/asf/calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/238b3225
Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/238b3225
Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/238b3225

Branch: refs/heads/master
Commit: 238b3225a2309a1a72bd1383b57982feaa2068e8
Parents: 0ea976e
Author: Julian Hyde <jh...@apache.org>
Authored: Thu Sep 14 17:33:17 2017 -0700
Committer: Julian Hyde <jh...@apache.org>
Committed: Sun Nov 26 19:39:28 2017 -0800

----------------------------------------------------------------------
 core/src/main/codegen/templates/Parser.jj       |  80 ++++-
 .../calcite/jdbc/CachingCalciteSchema.java      |  18 +
 .../calcite/jdbc/CalciteConnectionImpl.java     |  21 +-
 .../apache/calcite/jdbc/CalciteMetaImpl.java    |  16 +-
 .../org/apache/calcite/jdbc/CalcitePrepare.java |  21 +-
 .../apache/calcite/jdbc/CalciteResultSet.java   |   3 +-
 .../org/apache/calcite/jdbc/CalciteSchema.java  |  21 ++
 .../materialize/MaterializationService.java     |   4 +
 .../calcite/plan/RelOptAbstractTable.java       |   7 +
 .../calcite/plan/RelOptMaterializations.java    |   2 +-
 .../org/apache/calcite/plan/RelOptTable.java    |   5 +
 .../calcite/prepare/CalcitePrepareImpl.java     |   9 +-
 .../org/apache/calcite/prepare/Prepare.java     |   7 +
 .../apache/calcite/prepare/RelOptTableImpl.java |  87 +++++
 .../apache/calcite/rel/type/RelDataType.java    |   2 +-
 .../java/org/apache/calcite/rex/RexProgram.java |   8 +-
 .../apache/calcite/runtime/CalciteResource.java |  34 +-
 .../org/apache/calcite/runtime/ConsList.java    |   5 +-
 .../apache/calcite/schema/ColumnStrategy.java   |  42 +++
 .../java/org/apache/calcite/schema/Schemas.java |   9 +
 .../calcite/schema/impl/AbstractTable.java      |  15 +-
 .../schema/impl/ModifiableViewTable.java        |  33 +-
 .../java/org/apache/calcite/sql/SqlCreate.java  |  17 +-
 .../java/org/apache/calcite/sql/SqlDdl.java     |  42 +++
 .../java/org/apache/calcite/sql/SqlDrop.java    |  14 +-
 .../calcite/sql/SqlExecutableStatement.java     |   2 +
 .../java/org/apache/calcite/sql/SqlKind.java    |  36 ++
 .../calcite/sql/validate/SqlNameMatcher.java    |   2 +-
 .../calcite/sql/validate/SqlValidatorImpl.java  |  67 +++-
 .../calcite/sql/validate/SqlValidatorTable.java |   8 +-
 .../calcite/sql2rel/InitializerContext.java     |   4 +
 .../sql2rel/InitializerExpressionFactory.java   |  21 ++
 .../NullInitializerExpressionFactory.java       |  19 +-
 .../calcite/sql2rel/SqlToRelConverter.java      | 157 +++++++--
 .../sql2rel/StandardConvertletTable.java        |   4 +
 .../java/org/apache/calcite/util/NameMap.java   |   7 +
 .../org/apache/calcite/util/NameMultimap.java   |  14 +
 .../java/org/apache/calcite/util/Static.java    |   8 +
 .../main/java/org/apache/calcite/util/Util.java |   6 +-
 .../org/apache/calcite/util/graph/Graphs.java   |   5 +-
 .../calcite/runtime/CalciteResource.properties  |  11 +-
 .../calcite/sql/parser/SqlParserTest.java       |  90 ++++-
 .../parserextensiontesting/SqlCreateTable.java  |   8 +-
 .../org/apache/calcite/test/CalciteAssert.java  |  20 ++
 .../org/apache/calcite/test/CalciteSuite.java   |   2 +-
 .../org/apache/calcite/test/CoreQuidemTest.java |  90 +++++
 .../java/org/apache/calcite/test/JdbcTest.java  |   2 +-
 .../apache/calcite/test/MockCatalogReader.java  | 169 ++++++----
 .../org/apache/calcite/test/QuidemTest.java     |  68 +---
 .../apache/calcite/test/RelOptRulesTest.java    |   2 -
 .../apache/calcite/test/SqlToRelTestBase.java   |   9 +
 .../apache/calcite/test/SqlValidatorTest.java   |  17 +-
 core/src/test/resources/sql/blank.iq            |   6 +-
 .../calcite/adapter/mongodb/MongoTable.java     |   5 +-
 pom.xml                                         |   7 +-
 server/pom.xml                                  | 245 ++++++++++++++
 server/src/main/codegen/config.fmpp             |  99 ++++++
 .../src/main/codegen/includes/parserImpls.ftl   | 293 ++++++++++++++++
 .../calcite/sql/ddl/SqlCheckConstraint.java     |  75 ++++
 .../calcite/sql/ddl/SqlColumnDeclaration.java   | 102 ++++++
 .../calcite/sql/ddl/SqlCreateForeignSchema.java | 191 +++++++++++
 .../sql/ddl/SqlCreateMaterializedView.java      | 157 +++++++++
 .../apache/calcite/sql/ddl/SqlCreateSchema.java |  92 +++++
 .../apache/calcite/sql/ddl/SqlCreateTable.java  | 338 +++++++++++++++++++
 .../apache/calcite/sql/ddl/SqlCreateView.java   | 120 +++++++
 .../org/apache/calcite/sql/ddl/SqlDdlNodes.java | 221 ++++++++++++
 .../sql/ddl/SqlDropMaterializedView.java        |  65 ++++
 .../apache/calcite/sql/ddl/SqlDropObject.java   |  93 +++++
 .../apache/calcite/sql/ddl/SqlDropSchema.java   |  89 +++++
 .../apache/calcite/sql/ddl/SqlDropTable.java    |  38 +++
 .../org/apache/calcite/sql/ddl/SqlDropView.java |  38 +++
 .../calcite/sql/ddl/SqlKeyConstraint.java       |  89 +++++
 .../apache/calcite/sql/ddl/package-info.java    |  35 ++
 .../apache/calcite/test/ServerParserTest.java   | 245 ++++++++++++++
 .../apache/calcite/test/ServerQuidemTest.java   |  82 +++++
 .../org/apache/calcite/test/ServerTest.java     | 274 +++++++++++++++
 .../src/test/resources/sql/materialized_view.iq | 272 +++++++++++++++
 server/src/test/resources/sql/schema.iq         | 168 +++++++++
 server/src/test/resources/sql/table.iq          | 154 +++++++++
 server/src/test/resources/sql/table_as.iq       | 251 ++++++++++++++
 server/src/test/resources/sql/view.iq           | 183 ++++++++++
 site/_docs/adapter.md                           |  76 +++++
 site/_docs/reference.md                         |  97 +++++-
 sqlline                                         |   2 +-
 sqlline.bat                                     |   2 +-
 85 files changed, 5314 insertions(+), 260 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/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 f71abce..fd81f7b 100644
--- a/core/src/main/codegen/templates/Parser.jj
+++ b/core/src/main/codegen/templates/Parser.jj
@@ -704,7 +704,7 @@ SqlNode ParenthesizedExpression(ExprContext exprContext) :
  * <p>which should be illegal.  The above is interpreted as equivalent to
  *
  * <blockquote><pre>
- * <code>WHERE x IN ((select count(*) from t where c=d),5)</pre>
+ * WHERE x IN ((select count(*) from t where c=d),5)</pre>
  * </blockquote>
  *
  * <p>which is a legal use of a sub-query.  The only way to fix the hole is to
@@ -759,6 +759,61 @@ SqlNodeList ParenthesizedQueryOrCommaList(
     }
 }
 
+/** As ParenthesizedQueryOrCommaList, but allows DEFAULT
+ * in place of any of the expressions. For example,
+ * {@code (x, DEFAULT, null, DEFAULT)}. */
+SqlNodeList ParenthesizedQueryOrCommaListWithDefault(
+    ExprContext exprContext) :
+{
+    SqlNode e;
+    List<SqlNode> list;
+    ExprContext firstExprContext = exprContext;
+    final Span s;
+}
+{
+    <LPAREN>
+    {
+        // we've now seen left paren, so a query by itself should
+        // be interpreted as a sub-query
+        s = span();
+        switch (exprContext) {
+        case ACCEPT_SUB_QUERY:
+            firstExprContext = ExprContext.ACCEPT_NONCURSOR;
+            break;
+        case ACCEPT_CURSOR:
+            firstExprContext = ExprContext.ACCEPT_ALL;
+            break;
+        }
+    }
+    (
+        e = OrderedQueryOrExpr(firstExprContext)
+    |
+        e = Default()
+    )
+    {
+        list = startList(e);
+    }
+    (
+        <COMMA>
+        {
+            // a comma-list can't appear where only a query is expected
+            checkNonQueryExpression(exprContext);
+        }
+        (
+            e = Expression(exprContext)
+        |
+            e = Default()
+        )
+        {
+            list.add(e);
+        }
+    )*
+    <RPAREN>
+    {
+        return new SqlNodeList(list, s.end(this));
+    }
+}
+
 /**
  * Parses function parameter lists including DISTINCT keyword recognition,
  * DEFAULT, and named argument assignment.
@@ -823,9 +878,7 @@ void Arg0(List list, ExprContext exprContext) :
         name = SimpleIdentifier() <NAMED_ARGUMENT_ASSIGNMENT>
     ]
     (
-        <DEFAULT_> {
-            e = SqlStdOperatorTable.DEFAULT.createCall(getPos());
-        }
+        e = Default()
     |
         e = OrderedQueryOrExpr(firstExprContext)
     )
@@ -850,9 +903,7 @@ void Arg(List list, ExprContext exprContext) :
         name = SimpleIdentifier() <NAMED_ARGUMENT_ASSIGNMENT>
     ]
     (
-        <DEFAULT_> {
-            e = SqlStdOperatorTable.DEFAULT.createCall(getPos());
-        }
+        e = Default()
     |
         e = Expression(exprContext)
     )
@@ -867,6 +918,13 @@ void Arg(List list, ExprContext exprContext) :
     }
 }
 
+SqlNode Default() : {}
+{
+    <DEFAULT_> {
+        return SqlStdOperatorTable.DEFAULT.createCall(getPos());
+    }
+}
+
 /**
  * Parses a query (SELECT, UNION, INTERSECT, EXCEPT, VALUES, TABLE) followed by
  * the end-of-file symbol.
@@ -903,6 +961,10 @@ SqlNode SqlStmt() :
         stmt = SqlCreate()
     |
 </#if>
+<#if parser.dropStatementParserMethods?size != 0>
+        stmt = SqlDrop()
+    |
+</#if>
         stmt = OrderedQueryOrExpr(ExprContext.ACCEPT_QUERY)
     |
         stmt = SqlExplain()
@@ -2087,7 +2149,7 @@ SqlNode RowConstructor() :
         LOOKAHEAD(3)
         <LPAREN> { s = span(); }
         <ROW>
-        valueList = ParenthesizedQueryOrCommaList(ExprContext.ACCEPT_NONCURSOR)
+        valueList = ParenthesizedQueryOrCommaListWithDefault(ExprContext.ACCEPT_NONCURSOR)
         <RPAREN> { s.add(this); }
     |
         LOOKAHEAD(3)
@@ -2096,7 +2158,7 @@ SqlNode RowConstructor() :
         |
             { s = Span.of(); }
         )
-        valueList = ParenthesizedQueryOrCommaList(ExprContext.ACCEPT_NONCURSOR)
+        valueList = ParenthesizedQueryOrCommaListWithDefault(ExprContext.ACCEPT_NONCURSOR)
     |
         value = Expression(ExprContext.ACCEPT_NONCURSOR)
         {

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/main/java/org/apache/calcite/jdbc/CachingCalciteSchema.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/jdbc/CachingCalciteSchema.java b/core/src/main/java/org/apache/calcite/jdbc/CachingCalciteSchema.java
index 4c61a21..96476f1 100644
--- a/core/src/main/java/org/apache/calcite/jdbc/CachingCalciteSchema.java
+++ b/core/src/main/java/org/apache/calcite/jdbc/CachingCalciteSchema.java
@@ -226,6 +226,24 @@ class CachingCalciteSchema extends CalciteSchema {
     return snapshot;
   }
 
+  @Override public boolean removeTable(String name) {
+    if (cache) {
+      final long now = System.nanoTime();
+      implicitTableCache.enable(now, false);
+      implicitTableCache.enable(now, true);
+    }
+    return super.removeTable(name);
+  }
+
+  @Override public boolean removeFunction(String name) {
+    if (cache) {
+      final long now = System.nanoTime();
+      implicitFunctionCache.enable(now, false);
+      implicitFunctionCache.enable(now, true);
+    }
+    return super.removeFunction(name);
+  }
+
   /** Strategy for caching the value of an object and re-creating it if its
    * value is out of date as of a given timestamp.
    *

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/main/java/org/apache/calcite/jdbc/CalciteConnectionImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/jdbc/CalciteConnectionImpl.java b/core/src/main/java/org/apache/calcite/jdbc/CalciteConnectionImpl.java
index 85dfc65..ff6fb9f 100644
--- a/core/src/main/java/org/apache/calcite/jdbc/CalciteConnectionImpl.java
+++ b/core/src/main/java/org/apache/calcite/jdbc/CalciteConnectionImpl.java
@@ -465,13 +465,15 @@ abstract class CalciteConnectionImpl
   /** Implementation of Context. */
   static class ContextImpl implements CalcitePrepare.Context {
     private final CalciteConnectionImpl connection;
+    private final CalciteSchema mutableRootSchema;
     private final CalciteSchema rootSchema;
 
     ContextImpl(CalciteConnectionImpl connection) {
       this.connection = Preconditions.checkNotNull(connection);
       long now = System.currentTimeMillis();
       SchemaVersion schemaVersion = new LongSchemaVersion(now);
-      this.rootSchema = connection.rootSchema.createSnapshot(schemaVersion);
+      this.mutableRootSchema = connection.rootSchema;
+      this.rootSchema = mutableRootSchema.createSnapshot(schemaVersion);
     }
 
     public JavaTypeFactory getTypeFactory() {
@@ -482,6 +484,10 @@ abstract class CalciteConnectionImpl
       return rootSchema;
     }
 
+    public CalciteSchema getMutableRootSchema() {
+      return mutableRootSchema;
+    }
+
     public List<String> getDefaultSchemaPath() {
       final String schemaName = connection.getSchema();
       return schemaName == null
@@ -502,6 +508,19 @@ abstract class CalciteConnectionImpl
           rootSchema);
     }
 
+    public RelRunner getRelRunner() {
+      final RelRunner runner;
+      try {
+        runner = connection.unwrap(RelRunner.class);
+      } catch (SQLException e) {
+        throw new RuntimeException(e);
+      }
+      if (runner == null) {
+        throw new UnsupportedOperationException();
+      }
+      return runner;
+    }
+
     public CalcitePrepare.SparkHandler spark() {
       final boolean enable = config().spark();
       return CalcitePrepare.Dummy.getSparkHandler(enable);

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/main/java/org/apache/calcite/jdbc/CalciteMetaImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/jdbc/CalciteMetaImpl.java b/core/src/main/java/org/apache/calcite/jdbc/CalciteMetaImpl.java
index c17b6bf..12d6296 100644
--- a/core/src/main/java/org/apache/calcite/jdbc/CalciteMetaImpl.java
+++ b/core/src/main/java/org/apache/calcite/jdbc/CalciteMetaImpl.java
@@ -596,13 +596,25 @@ public class CalciteMetaImpl extends MetaImpl {
       synchronized (callback.getMonitor()) {
         callback.clear();
         final CalciteConnectionImpl calciteConnection = getConnection();
-        CalciteServerStatement statement =
+        final CalciteServerStatement statement =
             calciteConnection.server.getStatement(h);
         final Context context = statement.createPrepareContext();
         final CalcitePrepare.Query<Object> query = toQuery(context, sql);
         signature = calciteConnection.parseQuery(query, context, maxRowCount);
         statement.setSignature(signature);
-        callback.assign(signature, null, -1);
+        final int updateCount;
+        switch (signature.statementType) {
+        case CREATE:
+        case DROP:
+        case ALTER:
+        case OTHER_DDL:
+          updateCount = 0; // DDL produces no result set
+          break;
+        default:
+          updateCount = -1; // SELECT and DML produces result set
+          break;
+        }
+        callback.assign(signature, null, updateCount);
       }
       callback.execute();
       final MetaResultSet metaResultSet =

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/main/java/org/apache/calcite/jdbc/CalcitePrepare.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/jdbc/CalcitePrepare.java b/core/src/main/java/org/apache/calcite/jdbc/CalcitePrepare.java
index 56c5385..e0be075 100644
--- a/core/src/main/java/org/apache/calcite/jdbc/CalcitePrepare.java
+++ b/core/src/main/java/org/apache/calcite/jdbc/CalcitePrepare.java
@@ -43,6 +43,7 @@ import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.sql.SqlNode;
 import org.apache.calcite.sql.validate.CyclicDefinitionException;
 import org.apache.calcite.sql.validate.SqlValidator;
+import org.apache.calcite.tools.RelRunner;
 import org.apache.calcite.util.ImmutableIntList;
 
 import com.fasterxml.jackson.annotation.JsonIgnore;
@@ -109,8 +110,15 @@ public interface CalcitePrepare {
   interface Context {
     JavaTypeFactory getTypeFactory();
 
+    /** Returns the root schema for statements that need a read-consistent
+     * snapshot. */
     CalciteSchema getRootSchema();
 
+    /** Returns the root schema for statements that need to be able to modify
+     * schemas and have the results available to other statements. Viz, DDL
+     * statements. */
+    CalciteSchema getMutableRootSchema();
+
     List<String> getDefaultSchemaPath();
 
     CalciteConnectionConfig config();
@@ -126,6 +134,9 @@ public interface CalcitePrepare {
      * being analyzed further up the stack, the view definition can be deduced
      * to be cyclic. */
     List<String> getObjectPath();
+
+    /** Gets a runner; it can execute a relational expression. */
+    RelRunner getRelRunner();
   }
 
   /** Callback to register Spark as the main engine. */
@@ -319,17 +330,15 @@ public interface CalcitePrepare {
     private final long maxRowCount;
     private final Bindable<T> bindable;
 
+    @Deprecated // to be removed before 2.0
     public CalciteSignature(String sql, List<AvaticaParameter> parameterList,
         Map<String, Object> internalParameters, RelDataType rowType,
         List<ColumnMetaData> columns, Meta.CursorFactory cursorFactory,
         CalciteSchema rootSchema, List<RelCollation> collationList,
         long maxRowCount, Bindable<T> bindable) {
-      super(columns, sql, parameterList, internalParameters, cursorFactory, null);
-      this.rowType = rowType;
-      this.rootSchema = rootSchema;
-      this.collationList = collationList;
-      this.maxRowCount = maxRowCount;
-      this.bindable = bindable;
+      this(sql, parameterList, internalParameters, rowType, columns,
+          cursorFactory, rootSchema, collationList, maxRowCount, bindable,
+          null);
     }
 
     public CalciteSignature(String sql,

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/main/java/org/apache/calcite/jdbc/CalciteResultSet.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/jdbc/CalciteResultSet.java b/core/src/main/java/org/apache/calcite/jdbc/CalciteResultSet.java
index 6bc59c7..bf7957f 100644
--- a/core/src/main/java/org/apache/calcite/jdbc/CalciteResultSet.java
+++ b/core/src/main/java/org/apache/calcite/jdbc/CalciteResultSet.java
@@ -83,7 +83,8 @@ public class CalciteResultSet extends AvaticaResultSet {
         new CalcitePrepare.CalciteSignature<>(signature.sql,
             signature.parameters, signature.internalParameters,
             signature.rowType, columnMetaDataList, Meta.CursorFactory.ARRAY,
-            signature.rootSchema, ImmutableList.<RelCollation>of(), -1, null);
+            signature.rootSchema, ImmutableList.<RelCollation>of(), -1, null,
+            statement.getStatementType());
     ResultSetMetaData subResultSetMetaData =
         new AvaticaResultSetMetaData(statement, null, newSignature);
     final CalciteResultSet resultSet =

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/main/java/org/apache/calcite/jdbc/CalciteSchema.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/jdbc/CalciteSchema.java b/core/src/main/java/org/apache/calcite/jdbc/CalciteSchema.java
index f120ec7..b189e1b 100644
--- a/core/src/main/java/org/apache/calcite/jdbc/CalciteSchema.java
+++ b/core/src/main/java/org/apache/calcite/jdbc/CalciteSchema.java
@@ -16,6 +16,7 @@
  */
 package org.apache.calcite.jdbc;
 
+import org.apache.calcite.linq4j.function.Experimental;
 import org.apache.calcite.linq4j.tree.Expression;
 import org.apache.calcite.materialize.Lattice;
 import org.apache.calcite.schema.Function;
@@ -460,6 +461,26 @@ public abstract class CalciteSchema {
     return rootSchema;
   }
 
+  @Experimental
+  public boolean removeSubSchema(String name) {
+    return subSchemaMap.remove(name) != null;
+  }
+
+  @Experimental
+  public boolean removeTable(String name) {
+    return tableMap.remove(name) != null;
+  }
+
+  @Experimental
+  public boolean removeFunction(String name) {
+    final FunctionEntry remove = nullaryFunctionMap.remove(name);
+    if (remove == null) {
+      return false;
+    }
+    functionMap.remove(name, remove);
+    return true;
+  }
+
   /**
    * Entry in a schema, such as a table or sub-schema.
    *

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/main/java/org/apache/calcite/materialize/MaterializationService.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/materialize/MaterializationService.java b/core/src/main/java/org/apache/calcite/materialize/MaterializationService.java
index 3625a02..39a077f 100644
--- a/core/src/main/java/org/apache/calcite/materialize/MaterializationService.java
+++ b/core/src/main/java/org/apache/calcite/materialize/MaterializationService.java
@@ -351,6 +351,10 @@ public class MaterializationService {
     return INSTANCE;
   }
 
+  public void removeMaterialization(MaterializationKey key) {
+    actor.keyMap.remove(key);
+  }
+
   /**
    * Creates tables that represent a materialized view.
    */

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/main/java/org/apache/calcite/plan/RelOptAbstractTable.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptAbstractTable.java b/core/src/main/java/org/apache/calcite/plan/RelOptAbstractTable.java
index d640aa8..854e42f 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelOptAbstractTable.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelOptAbstractTable.java
@@ -17,6 +17,7 @@
 package org.apache.calcite.plan;
 
 import org.apache.calcite.linq4j.tree.Expression;
+import org.apache.calcite.prepare.RelOptTableImpl;
 import org.apache.calcite.rel.RelCollation;
 import org.apache.calcite.rel.RelDistribution;
 import org.apache.calcite.rel.RelDistributions;
@@ -25,6 +26,7 @@ import org.apache.calcite.rel.RelReferentialConstraint;
 import org.apache.calcite.rel.logical.LogicalTableScan;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeField;
+import org.apache.calcite.schema.ColumnStrategy;
 import org.apache.calcite.util.ImmutableBitSet;
 
 import com.google.common.collect.ImmutableList;
@@ -111,6 +113,11 @@ public abstract class RelOptAbstractTable implements RelOptTable {
   public RelOptTable extend(List<RelDataTypeField> extendedFields) {
     throw new UnsupportedOperationException();
   }
+
+  public List<ColumnStrategy> getColumnStrategies() {
+    return RelOptTableImpl.columnStrategies(this);
+  }
+
 }
 
 // End RelOptAbstractTable.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/main/java/org/apache/calcite/plan/RelOptMaterializations.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptMaterializations.java b/core/src/main/java/org/apache/calcite/plan/RelOptMaterializations.java
index ce0c033..3c0362c 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelOptMaterializations.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelOptMaterializations.java
@@ -46,7 +46,7 @@ import java.util.Map;
 import java.util.Set;
 
 /**
- * <code>MaterializationOptUtil</code> defines static utility methods for using
+ * Utility methods for using
  * materialized views and lattices for queries.
  */
 public abstract class RelOptMaterializations {

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/main/java/org/apache/calcite/plan/RelOptTable.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptTable.java b/core/src/main/java/org/apache/calcite/plan/RelOptTable.java
index 668dc1c..1e351ac 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelOptTable.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelOptTable.java
@@ -25,6 +25,7 @@ import org.apache.calcite.rel.RelRoot;
 import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeField;
+import org.apache.calcite.schema.ColumnStrategy;
 import org.apache.calcite.schema.Wrapper;
 import org.apache.calcite.util.ImmutableBitSet;
 
@@ -119,6 +120,10 @@ public interface RelOptTable extends Wrapper {
    */
   RelOptTable extend(List<RelDataTypeField> extendedFields);
 
+  /** Returns a list describing how each column is populated. The list has the
+   *  same number of entries as there are fields, and is immutable. */
+  List<ColumnStrategy> getColumnStrategies();
+
   /** Can expand a view into relational expressions. */
   interface ViewExpander {
     /**

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java b/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java
index 74b62d5..a7de759 100644
--- a/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java
+++ b/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java
@@ -765,17 +765,12 @@ public class CalcitePrepareImpl implements CalcitePrepare {
       if (sqlNode.getKind().belongsTo(SqlKind.DDL)) {
         executeDdl(context, sqlNode);
 
-        // Return a dummy signature that contains no rows
-        final Bindable<T> bindable = new Bindable<T>() {
-          public Enumerable<T> bind(DataContext dataContext) {
-            return Linq4j.emptyEnumerable();
-          }
-        };
         return new CalciteSignature<>(query.sql,
             ImmutableList.<AvaticaParameter>of(),
             ImmutableMap.<String, Object>of(), null,
             ImmutableList.<ColumnMetaData>of(), Meta.CursorFactory.OBJECT,
-            null, ImmutableList.<RelCollation>of(), -1, bindable);
+            null, ImmutableList.<RelCollation>of(), -1, null,
+            Meta.StatementType.OTHER_DDL);
       }
 
       final SqlValidator validator =

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/main/java/org/apache/calcite/prepare/Prepare.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/prepare/Prepare.java b/core/src/main/java/org/apache/calcite/prepare/Prepare.java
index 42d8cdf..95758e5 100644
--- a/core/src/main/java/org/apache/calcite/prepare/Prepare.java
+++ b/core/src/main/java/org/apache/calcite/prepare/Prepare.java
@@ -43,6 +43,7 @@ import org.apache.calcite.rex.RexExecutorImpl;
 import org.apache.calcite.runtime.Bindable;
 import org.apache.calcite.runtime.Hook;
 import org.apache.calcite.runtime.Typed;
+import org.apache.calcite.schema.ColumnStrategy;
 import org.apache.calcite.schema.ExtensibleTable;
 import org.apache.calcite.schema.Table;
 import org.apache.calcite.schema.Wrapper;
@@ -429,8 +430,10 @@ public abstract class Prepare {
    * for {@link #columnHasDefaultValue}. */
   public abstract static class AbstractPreparingTable
       implements PreparingTable {
+    @SuppressWarnings("deprecation")
     public boolean columnHasDefaultValue(RelDataType rowType, int ordinal,
         InitializerContext initializerContext) {
+      // This method is no longer used
       final Table table = this.unwrap(Table.class);
       if (table != null && table instanceof Wrapper) {
         final InitializerExpressionFactory initializerExpressionFactory =
@@ -476,6 +479,10 @@ public abstract class Prepare {
     /** Implementation-specific code to instantiate a new {@link RelOptTable}
      * based on a {@link Table} that has been extended. */
     protected abstract RelOptTable extend(Table extendedTable);
+
+    public List<ColumnStrategy> getColumnStrategies() {
+      return RelOptTableImpl.columnStrategies(AbstractPreparingTable.this);
+    }
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/main/java/org/apache/calcite/prepare/RelOptTableImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/prepare/RelOptTableImpl.java b/core/src/main/java/org/apache/calcite/prepare/RelOptTableImpl.java
index d7a8526..8ed16aa 100644
--- a/core/src/main/java/org/apache/calcite/prepare/RelOptTableImpl.java
+++ b/core/src/main/java/org/apache/calcite/prepare/RelOptTableImpl.java
@@ -31,8 +31,11 @@ import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.RelReferentialConstraint;
 import org.apache.calcite.rel.logical.LogicalTableScan;
 import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
+import org.apache.calcite.rel.type.RelDataTypeField;
 import org.apache.calcite.rel.type.RelRecordType;
 import org.apache.calcite.runtime.Hook;
+import org.apache.calcite.schema.ColumnStrategy;
 import org.apache.calcite.schema.FilterableTable;
 import org.apache.calcite.schema.ModifiableTable;
 import org.apache.calcite.schema.Path;
@@ -46,9 +49,12 @@ import org.apache.calcite.schema.Schemas;
 import org.apache.calcite.schema.StreamableTable;
 import org.apache.calcite.schema.Table;
 import org.apache.calcite.schema.TranslatableTable;
+import org.apache.calcite.schema.Wrapper;
 import org.apache.calcite.sql.SqlAccessType;
 import org.apache.calcite.sql.validate.SqlModality;
 import org.apache.calcite.sql.validate.SqlMonotonicity;
+import org.apache.calcite.sql2rel.InitializerExpressionFactory;
+import org.apache.calcite.sql2rel.NullInitializerExpressionFactory;
 import org.apache.calcite.util.ImmutableBitSet;
 import org.apache.calcite.util.Pair;
 import org.apache.calcite.util.Util;
@@ -58,6 +64,7 @@ import com.google.common.base.Functions;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 
+import java.util.AbstractList;
 import java.util.Collection;
 import java.util.List;
 import java.util.Set;
@@ -185,6 +192,12 @@ public class RelOptTableImpl extends Prepare.AbstractPreparingTable {
     if (clazz.isInstance(table)) {
       return clazz.cast(table);
     }
+    if (table instanceof Wrapper) {
+      final T t = ((Wrapper) table).unwrap(clazz);
+      if (t != null) {
+        return t;
+      }
+    }
     if (clazz == CalciteSchema.class) {
       return clazz.cast(
           Schemas.subSchema(((CalciteCatalogReader) schema).rootSchema,
@@ -243,6 +256,30 @@ public class RelOptTableImpl extends Prepare.AbstractPreparingTable {
       return relOptTable.toRel(context);
     }
 
+    // If there are any virtual columns, create a copy of this table without
+    // those virtual columns.
+    final List<ColumnStrategy> strategies = getColumnStrategies();
+    if (strategies.contains(ColumnStrategy.VIRTUAL)) {
+      final RelDataTypeFactory.Builder b =
+          context.getCluster().getTypeFactory().builder();
+      for (RelDataTypeField field : rowType.getFieldList()) {
+        if (strategies.get(field.getIndex()) != ColumnStrategy.VIRTUAL) {
+          b.add(field.getName(), field.getType());
+        }
+      }
+      final RelOptTable relOptTable =
+          new RelOptTableImpl(this.schema, b.build(), this.names, this.table,
+              this.expressionFunction, this.rowCount) {
+            @Override public <T> T unwrap(Class<T> clazz) {
+              if (clazz.isAssignableFrom(InitializerExpressionFactory.class)) {
+                return clazz.cast(NullInitializerExpressionFactory.INSTANCE);
+              }
+              return super.unwrap(clazz);
+            }
+          };
+      return relOptTable.toRel(context);
+    }
+
     if (table instanceof TranslatableTable) {
       return ((TranslatableTable) table).toRel(context, this);
     }
@@ -328,6 +365,56 @@ public class RelOptTableImpl extends Prepare.AbstractPreparingTable {
     return SqlAccessType.ALL;
   }
 
+  /** Helper for {@link #getColumnStrategies()}. */
+  public static List<ColumnStrategy> columnStrategies(final RelOptTable table) {
+    final int fieldCount = table.getRowType().getFieldCount();
+    final InitializerExpressionFactory ief =
+        Util.first(table.unwrap(InitializerExpressionFactory.class),
+            NullInitializerExpressionFactory.INSTANCE);
+    return new AbstractList<ColumnStrategy>() {
+      public int size() {
+        return fieldCount;
+      }
+
+      public ColumnStrategy get(int index) {
+        return ief.generationStrategy(table, index);
+      }
+    };
+  }
+
+  /** Converts the ordinal of a field into the ordinal of a stored field.
+   * That is, it subtracts the number of virtual fields that come before it. */
+  public static int realOrdinal(final RelOptTable table, int i) {
+    List<ColumnStrategy> strategies = table.getColumnStrategies();
+    int n = 0;
+    for (int j = 0; j < i; j++) {
+      switch (strategies.get(j)) {
+      case VIRTUAL:
+        ++n;
+      }
+    }
+    return i - n;
+  }
+
+  /** Returns the row type of a table after any {@link ColumnStrategy#VIRTUAL}
+   * columns have been removed. This is the type of the records that are
+   * actually stored. */
+  public static RelDataType realRowType(RelOptTable table) {
+    final RelDataType rowType = table.getRowType();
+    final List<ColumnStrategy> strategies = columnStrategies(table);
+    if (!strategies.contains(ColumnStrategy.VIRTUAL)) {
+      return rowType;
+    }
+    final RelDataTypeFactory.Builder builder =
+        table.getRelOptSchema().getTypeFactory().builder();
+    for (RelDataTypeField field : rowType.getFieldList()) {
+      if (strategies.get(field.getIndex()) != ColumnStrategy.VIRTUAL) {
+        builder.add(field);
+      }
+    }
+    return builder.build();
+  }
+
   /** Implementation of {@link SchemaPlus} that wraps a regular schema and knows
    * its name and parent.
    *

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/main/java/org/apache/calcite/rel/type/RelDataType.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/type/RelDataType.java b/core/src/main/java/org/apache/calcite/rel/type/RelDataType.java
index aa8d495..8299d00 100644
--- a/core/src/main/java/org/apache/calcite/rel/type/RelDataType.java
+++ b/core/src/main/java/org/apache/calcite/rel/type/RelDataType.java
@@ -32,7 +32,7 @@ import java.util.List;
  * different type classes into one. Inelegant, but since our type system was
  * defined before the advent of Java generics, it avoids a lot of typecasting.
  */
-public interface RelDataType /*extends Type*/ {
+public interface RelDataType {
   int SCALE_NOT_SPECIFIED = Integer.MIN_VALUE;
   int PRECISION_NOT_SPECIFIED = -1;
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/main/java/org/apache/calcite/rex/RexProgram.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rex/RexProgram.java b/core/src/main/java/org/apache/calcite/rex/RexProgram.java
index c91c0f0..152bf35 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexProgram.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexProgram.java
@@ -35,6 +35,7 @@ import org.apache.calcite.util.Permutation;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
+import com.google.common.collect.Ordering;
 
 import java.io.PrintWriter;
 import java.io.StringWriter;
@@ -527,10 +528,10 @@ public class RexProgram {
   /**
    * Given a list of collations which hold for the input to this program,
    * returns a list of collations which hold for its output. The result is
-   * mutable.
+   * mutable and sorted.
    */
   public List<RelCollation> getCollations(List<RelCollation> inputCollations) {
-    List<RelCollation> outputCollations = new ArrayList<>(1);
+    final List<RelCollation> outputCollations = new ArrayList<>();
     deduceCollations(
         outputCollations,
         inputRowType.getFieldCount(), projects,
@@ -540,7 +541,7 @@ public class RexProgram {
 
   /**
    * Given a list of expressions and a description of which are ordered,
-   * computes a list of collations. The result is mutable.
+   * populates a list of collations, sorted in natural order.
    */
   public static void deduceCollations(
       List<RelCollation> outputCollations,
@@ -573,6 +574,7 @@ public class RexProgram {
       // to the output.
       outputCollations.add(RelCollations.of(fieldCollations));
     }
+    Collections.sort(outputCollations, Ordering.natural());
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java b/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
index 1f34ec7..f122076 100644
--- a/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
+++ b/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
@@ -445,6 +445,9 @@ public interface CalciteResource {
   @BaseMessage("DEFAULT is only allowed for optional parameters")
   ExInst<SqlValidatorException> defaultForOptionalParameter();
 
+  @BaseMessage("DEFAULT not allowed here")
+  ExInst<SqlValidatorException> defaultNotAllowed();
+
   @BaseMessage("Not allowed to perform {0} on {1}")
   ExInst<SqlValidatorException> accessNotAllowed(String a0, String a1);
 
@@ -521,8 +524,8 @@ public interface CalciteResource {
   @BaseMessage("''{0}'' is not a valid datetime format")
   ExInst<CalciteException> invalidDatetimeFormat(String a0);
 
-  @BaseMessage("Cannot explicitly insert value into IDENTITY column ''{0}'' which is ALWAYS GENERATED")
-  ExInst<CalciteException> insertIntoAlwaysGenerated(String a0);
+  @BaseMessage("Cannot INSERT into generated column ''{0}''")
+  ExInst<SqlValidatorException> insertIntoAlwaysGenerated(String a0);
 
   @BaseMessage("Argument to function ''{0}'' must have a scale of 0")
   ExInst<CalciteException> argumentMustHaveScaleZero(String a0);
@@ -721,6 +724,33 @@ public interface CalciteResource {
 
   @BaseMessage("Rolled up column ''{0}'' is not allowed in {1}")
   ExInst<SqlValidatorException> rolledUpNotAllowed(String column, String context);
+
+  @BaseMessage("Schema ''{0}'' already exists")
+  ExInst<SqlValidatorException> schemaExists(String name);
+
+  @BaseMessage("Invalid schema type ''{0}''; valid values: {1}")
+  ExInst<SqlValidatorException> schemaInvalidType(String type, String values);
+
+  @BaseMessage("Table ''{0}'' already exists")
+  ExInst<SqlValidatorException> tableExists(String name);
+
+  // If CREATE TABLE does not have "AS query", there must be a column list
+  @BaseMessage("Missing column list")
+  ExInst<SqlValidatorException> createTableRequiresColumnList();
+
+  // If CREATE TABLE does not have "AS query", a type must be specified for each
+  // column
+  @BaseMessage("Type required for column ''{0}'' in CREATE TABLE without AS")
+  ExInst<SqlValidatorException> createTableRequiresColumnTypes(String columnName);
+
+  @BaseMessage("View ''{0}'' already exists and REPLACE not specified")
+  ExInst<SqlValidatorException> viewExists(String name);
+
+  @BaseMessage("Schema ''{0}'' not found")
+  ExInst<SqlValidatorException> schemaNotFound(String name);
+
+  @BaseMessage("View ''{0}'' not found")
+  ExInst<SqlValidatorException> viewNotFound(String name);
 }
 
 // End CalciteResource.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/main/java/org/apache/calcite/runtime/ConsList.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/runtime/ConsList.java b/core/src/main/java/org/apache/calcite/runtime/ConsList.java
index a30af84..fe53901 100644
--- a/core/src/main/java/org/apache/calcite/runtime/ConsList.java
+++ b/core/src/main/java/org/apache/calcite/runtime/ConsList.java
@@ -37,11 +37,12 @@ public class ConsList<E> extends AbstractImmutableList<E> {
   /** Creates a ConsList.
    * It consists of an element pre-pended to another list.
    * If the other list is mutable, creates an immutable copy. */
-  public static <E> List<E> of(E first, List<E> rest) {
+  public static <E> List<E> of(E first, List<? extends E> rest) {
     if (rest instanceof ConsList
         || rest instanceof ImmutableList
         && !rest.isEmpty()) {
-      return new ConsList<>(first, rest);
+      //noinspection unchecked
+      return new ConsList<>(first, (List<E>) rest);
     } else {
       return ImmutableList.<E>builder().add(first).addAll(rest).build();
     }

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/main/java/org/apache/calcite/schema/ColumnStrategy.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/schema/ColumnStrategy.java b/core/src/main/java/org/apache/calcite/schema/ColumnStrategy.java
new file mode 100644
index 0000000..f51175d
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/schema/ColumnStrategy.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.schema;
+
+import org.apache.calcite.plan.RelOptTable;
+
+/** Describes how a column gets populated.
+ *
+ * @see org.apache.calcite.sql2rel.InitializerExpressionFactory#generationStrategy
+ * @see RelOptTable#getColumnStrategies()
+ */
+public enum ColumnStrategy {
+  /** Column does not have a default value, but does allow null values.
+   * If you don't specify it in an INSERT, it will get a NULL value. */
+  NULLABLE,
+  /** Column does not have a default value, and does not allow nulls.
+   * You must specify it in an INSERT. */
+  NOT_NULLABLE,
+  /** Column has a default value.
+   * If you don't specify it in an INSERT, it will get a NULL value. */
+  DEFAULT,
+  /** Column is computed and stored. You cannot insert into it. */
+  STORED,
+  /** Column is computed and not stored. You cannot insert into it. */
+  VIRTUAL
+}
+
+// End ColumnStrategy.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/main/java/org/apache/calcite/schema/Schemas.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/schema/Schemas.java b/core/src/main/java/org/apache/calcite/schema/Schemas.java
index 6466d31..a13df14 100644
--- a/core/src/main/java/org/apache/calcite/schema/Schemas.java
+++ b/core/src/main/java/org/apache/calcite/schema/Schemas.java
@@ -37,6 +37,7 @@ import org.apache.calcite.rel.type.RelDataTypeFactory;
 import org.apache.calcite.rel.type.RelProtoDataType;
 import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.sql.type.SqlTypeUtil;
+import org.apache.calcite.tools.RelRunner;
 import org.apache.calcite.util.BuiltInMethod;
 import org.apache.calcite.util.Pair;
 
@@ -407,6 +408,10 @@ public final class Schemas {
         return schema.root();
       }
 
+      public CalciteSchema getMutableRootSchema() {
+        return getRootSchema();
+      }
+
       public List<String> getDefaultSchemaPath() {
         // schemaPath is usually null. If specified, it overrides schema
         // as the context within which the SQL is validated.
@@ -428,6 +433,10 @@ public final class Schemas {
         return dataContext;
       }
 
+      public RelRunner getRelRunner() {
+        throw new UnsupportedOperationException();
+      }
+
       public CalcitePrepare.SparkHandler spark() {
         final boolean enable = config().spark();
         return CalcitePrepare.Dummy.getSparkHandler(enable);

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/main/java/org/apache/calcite/schema/impl/AbstractTable.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/schema/impl/AbstractTable.java b/core/src/main/java/org/apache/calcite/schema/impl/AbstractTable.java
index 2f2b6bf..d2e92cb 100644
--- a/core/src/main/java/org/apache/calcite/schema/impl/AbstractTable.java
+++ b/core/src/main/java/org/apache/calcite/schema/impl/AbstractTable.java
@@ -21,6 +21,7 @@ import org.apache.calcite.schema.Schema;
 import org.apache.calcite.schema.Statistic;
 import org.apache.calcite.schema.Statistics;
 import org.apache.calcite.schema.Table;
+import org.apache.calcite.schema.Wrapper;
 import org.apache.calcite.sql.SqlCall;
 import org.apache.calcite.sql.SqlNode;
 
@@ -32,7 +33,7 @@ import org.apache.calcite.sql.SqlNode;
  * if their table can potentially contain rolled up values. This information is
  * used by the validator to check for illegal uses of these columns.
  */
-public abstract class AbstractTable implements Table {
+public abstract class AbstractTable implements Table, Wrapper {
   protected AbstractTable() {
   }
 
@@ -45,13 +46,19 @@ public abstract class AbstractTable implements Table {
     return Schema.TableType.TABLE;
   }
 
+  public <C> C unwrap(Class<C> aClass) {
+    if (aClass.isInstance(this)) {
+      return aClass.cast(this);
+    }
+    return null;
+  }
+
   @Override public boolean isRolledUp(String column) {
     return false;
   }
 
-  @Override public boolean rolledUpColumnValidInsideAgg(String column, SqlCall call,
-                                                        SqlNode parent,
-                                                        CalciteConnectionConfig config) {
+  @Override public boolean rolledUpColumnValidInsideAgg(String column,
+      SqlCall call, SqlNode parent, CalciteConnectionConfig config) {
     return true;
   }
 }

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/main/java/org/apache/calcite/schema/impl/ModifiableViewTable.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/schema/impl/ModifiableViewTable.java b/core/src/main/java/org/apache/calcite/schema/impl/ModifiableViewTable.java
index 241927b..a312188 100644
--- a/core/src/main/java/org/apache/calcite/schema/impl/ModifiableViewTable.java
+++ b/core/src/main/java/org/apache/calcite/schema/impl/ModifiableViewTable.java
@@ -25,6 +25,7 @@ import org.apache.calcite.rel.type.RelDataTypeImpl;
 import org.apache.calcite.rel.type.RelProtoDataType;
 import org.apache.calcite.rex.RexBuilder;
 import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.schema.ColumnStrategy;
 import org.apache.calcite.schema.ExtensibleTable;
 import org.apache.calcite.schema.ModifiableView;
 import org.apache.calcite.schema.Path;
@@ -92,7 +93,7 @@ public class ModifiableViewTable extends ViewTable
     } else if (aClass.isInstance(table)) {
       return aClass.cast(table);
     }
-    return null;
+    return super.unwrap(aClass);
   }
 
   /**
@@ -192,9 +193,31 @@ public class ModifiableViewTable extends ViewTable
       this.projectMap = ImmutableMap.copyOf(projectMap);
     }
 
-    @Override public boolean isGeneratedAlways(RelOptTable table, int iColumn) {
-      assert table.unwrap(ModifiableViewTable.class) != null;
-      return false;
+    @Override public ColumnStrategy generationStrategy(RelOptTable table,
+        int iColumn) {
+      final ModifiableViewTable viewTable =
+          table.unwrap(ModifiableViewTable.class);
+      assert iColumn < viewTable.columnMapping.size();
+
+      // Use the view constraint to generate the default value if the column is
+      // constrained.
+      final int mappedOrdinal = viewTable.columnMapping.get(iColumn);
+      final RexNode viewConstraint = projectMap.get(mappedOrdinal);
+      if (viewConstraint != null) {
+        return ColumnStrategy.DEFAULT;
+      }
+
+      // Otherwise use the default value of the underlying table.
+      final Table schemaTable = viewTable.getTable();
+      if (schemaTable instanceof Wrapper) {
+        final InitializerExpressionFactory initializerExpressionFactory =
+            ((Wrapper) schemaTable).unwrap(InitializerExpressionFactory.class);
+        if (initializerExpressionFactory != null) {
+          return initializerExpressionFactory.generationStrategy(table,
+              iColumn);
+        }
+      }
+      return super.generationStrategy(table, iColumn);
     }
 
     @Override public RexNode newColumnDefaultValue(RelOptTable table,
@@ -214,7 +237,7 @@ public class ModifiableViewTable extends ViewTable
       }
 
       // Otherwise use the default value of the underlying table.
-      final Table schemaTable = viewTable.unwrap(Table.class);
+      final Table schemaTable = viewTable.getTable();
       if (schemaTable instanceof Wrapper) {
         final InitializerExpressionFactory initializerExpressionFactory =
             ((Wrapper) schemaTable).unwrap(InitializerExpressionFactory.class);

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/main/java/org/apache/calcite/sql/SqlCreate.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlCreate.java b/core/src/main/java/org/apache/calcite/sql/SqlCreate.java
index 57e9a71..b03390e 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlCreate.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlCreate.java
@@ -23,14 +23,25 @@ import org.apache.calcite.sql.parser.SqlParserPos;
  * statement covered by this class is "CREATE [ OR REPLACE ]". Subclasses handle
  * whatever comes afterwards.
  */
-public abstract class SqlCreate extends SqlCall {
+public abstract class SqlCreate extends SqlDdl {
 
   /** Whether "OR REPLACE" was specified. */
   boolean replace;
 
-  public SqlCreate(SqlParserPos pos, boolean replace) {
-    super(pos);
+  /** Whether "IF NOT EXISTS" was specified. */
+  protected final boolean ifNotExists;
+
+  /** Creates a SqlCreate. */
+  public SqlCreate(SqlOperator operator, SqlParserPos pos, boolean replace,
+      boolean ifNotExists) {
+    super(operator, pos);
     this.replace = replace;
+    this.ifNotExists = ifNotExists;
+  }
+
+  @Deprecated // to be removed before 2.0
+  public SqlCreate(SqlParserPos pos, boolean replace) {
+    this(SqlDdl.DDL_OPERATOR, pos, replace, false);
   }
 
   public boolean getReplace() {

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/main/java/org/apache/calcite/sql/SqlDdl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlDdl.java b/core/src/main/java/org/apache/calcite/sql/SqlDdl.java
new file mode 100644
index 0000000..c22abb6
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/sql/SqlDdl.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.sql;
+
+import org.apache.calcite.sql.parser.SqlParserPos;
+
+import com.google.common.base.Preconditions;
+
+/** Base class for CREATE, DROP and other DDL statements. */
+public abstract class SqlDdl extends SqlCall {
+  /** Use this operator only if you don't have a better one. */
+  protected static final SqlOperator DDL_OPERATOR =
+      new SqlSpecialOperator("DDL", SqlKind.OTHER_DDL);
+
+  private final SqlOperator operator;
+
+  /** Creates a SqlDdl. */
+  public SqlDdl(SqlOperator operator, SqlParserPos pos) {
+    super(pos);
+    this.operator = Preconditions.checkNotNull(operator);
+  }
+
+  public SqlOperator getOperator() {
+    return operator;
+  }
+}
+
+// End SqlDdl.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/main/java/org/apache/calcite/sql/SqlDrop.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlDrop.java b/core/src/main/java/org/apache/calcite/sql/SqlDrop.java
index 1716d32..55dc217 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlDrop.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlDrop.java
@@ -23,10 +23,20 @@ import org.apache.calcite.sql.parser.SqlParserPos;
  * statement covered by this class is "DROP". Subclasses handle
  * whatever comes afterwards.
  */
-public abstract class SqlDrop extends SqlCall {
+public abstract class SqlDrop extends SqlDdl {
 
+  /** Whether "IF EXISTS" was specified. */
+  protected final boolean ifExists;
+
+  /** Creates a SqlDrop. */
+  public SqlDrop(SqlOperator operator, SqlParserPos pos, boolean ifExists) {
+    super(operator, pos);
+    this.ifExists = ifExists;
+  }
+
+  @Deprecated // to be removed before 2.0
   public SqlDrop(SqlParserPos pos) {
-    super(pos);
+    this(DDL_OPERATOR, pos, false);
   }
 
 }

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/main/java/org/apache/calcite/sql/SqlExecutableStatement.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlExecutableStatement.java b/core/src/main/java/org/apache/calcite/sql/SqlExecutableStatement.java
index 06ca55f..c309a27 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlExecutableStatement.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlExecutableStatement.java
@@ -17,6 +17,7 @@
 package org.apache.calcite.sql;
 
 import org.apache.calcite.jdbc.CalcitePrepare;
+import org.apache.calcite.linq4j.function.Experimental;
 
 /**
  * Mix-in interface for {@link SqlNode} that allows DDL commands to be
@@ -24,6 +25,7 @@ import org.apache.calcite.jdbc.CalcitePrepare;
  *
  * <p>NOTE: Subject to change without notice.
  */
+@Experimental
 public interface SqlExecutableStatement {
   void execute(CalcitePrepare.Context context);
 }

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/main/java/org/apache/calcite/sql/SqlKind.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlKind.java b/core/src/main/java/org/apache/calcite/sql/SqlKind.java
index 8d7c8aa..76d7f3c 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlKind.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlKind.java
@@ -938,6 +938,21 @@ public enum SqlKind {
    * the {@link #SESSION} group function. */
   SESSION_END,
 
+  /** Column declaration. */
+  COLUMN_DECL,
+
+  /** {@code CHECK} constraint. */
+  CHECK,
+
+  /** {@code UNIQUE} constraint. */
+  UNIQUE,
+
+  /** {@code PRIMARY KEY} constraint. */
+  PRIMARY_KEY,
+
+  /** {@code FOREIGN KEY} constraint. */
+  FOREIGN_KEY,
+
   // DDL and session control statements follow. The list is not exhaustive: feel
   // free to add more.
 
@@ -950,6 +965,15 @@ public enum SqlKind {
   /** {@code ALTER SESSION} DDL statement. */
   ALTER_SESSION,
 
+  /** {@code CREATE SCHEMA} DDL statement. */
+  CREATE_SCHEMA,
+
+  /** {@code CREATE FOREIGN SCHEMA} DDL statement. */
+  CREATE_FOREIGN_SCHEMA,
+
+  /** {@code DROP SCHEMA} DDL statement. */
+  DROP_SCHEMA,
+
   /** {@code CREATE TABLE} DDL statement. */
   CREATE_TABLE,
 
@@ -968,6 +992,15 @@ public enum SqlKind {
   /** {@code DROP VIEW} DDL statement. */
   DROP_VIEW,
 
+  /** {@code CREATE MATERIALIZED VIEW} DDL statement. */
+  CREATE_MATERIALIZED_VIEW,
+
+  /** {@code ALTER MATERIALIZED VIEW} DDL statement. */
+  ALTER_MATERIALIZED_VIEW,
+
+  /** {@code DROP MATERIALIZED VIEW} DDL statement. */
+  DROP_MATERIALIZED_VIEW,
+
   /** {@code CREATE SEQUENCE} DDL statement. */
   CREATE_SEQUENCE,
 
@@ -1045,8 +1078,11 @@ public enum SqlKind {
    */
   public static final EnumSet<SqlKind> DDL =
       EnumSet.of(COMMIT, ROLLBACK, ALTER_SESSION,
+          CREATE_SCHEMA, CREATE_FOREIGN_SCHEMA, DROP_SCHEMA,
           CREATE_TABLE, ALTER_TABLE, DROP_TABLE,
           CREATE_VIEW, ALTER_VIEW, DROP_VIEW,
+          CREATE_MATERIALIZED_VIEW, ALTER_MATERIALIZED_VIEW,
+          DROP_MATERIALIZED_VIEW,
           CREATE_SEQUENCE, ALTER_SEQUENCE, DROP_SEQUENCE,
           CREATE_INDEX, ALTER_INDEX, DROP_INDEX,
           SET_OPTION, OTHER_DDL);

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/main/java/org/apache/calcite/sql/validate/SqlNameMatcher.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlNameMatcher.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlNameMatcher.java
index 104f3d2..c8d75ee 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlNameMatcher.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlNameMatcher.java
@@ -49,7 +49,7 @@ public interface SqlNameMatcher {
    * throws {@link UnsupportedOperationException}. */
   String bestString();
 
-  /** Finds a field with a given name, using the currenct case-sensitivity,
+  /** Finds a field with a given name, using the current case-sensitivity,
    * returning null if not found.
    *
    * @param rowType    Row type

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
index 940c8ac..cb134cf 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
@@ -37,6 +37,7 @@ import org.apache.calcite.runtime.CalciteContextException;
 import org.apache.calcite.runtime.CalciteException;
 import org.apache.calcite.runtime.Feature;
 import org.apache.calcite.runtime.Resources;
+import org.apache.calcite.schema.ColumnStrategy;
 import org.apache.calcite.schema.Table;
 import org.apache.calcite.schema.impl.ModifiableViewTable;
 import org.apache.calcite.sql.JoinConditionType;
@@ -3727,7 +3728,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
       }
     }
     final SqlValidatorScope orderScope = getOrderScope(select);
-    Preconditions.checkNotNull(orderScope != null);
+    Preconditions.checkNotNull(orderScope);
 
     List<SqlNode> expandList = new ArrayList<>();
     for (SqlNode orderItem : orderList) {
@@ -4154,8 +4155,8 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
     final RelDataType logicalSourceRowType =
         getLogicalSourceRowType(sourceRowType, insert);
 
-    checkFieldCount(insert.getTargetTable(), table, logicalSourceRowType,
-        logicalTargetRowType);
+    checkFieldCount(insert.getTargetTable(), table, source,
+        logicalSourceRowType, logicalTargetRowType);
 
     checkTypeAssignment(logicalSourceRowType, logicalTargetRowType, insert);
 
@@ -4260,10 +4261,8 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
     }
   }
 
-  private void checkFieldCount(
-      SqlNode node,
-      SqlValidatorTable table,
-      RelDataType logicalSourceRowType,
+  private void checkFieldCount(SqlNode node, SqlValidatorTable table,
+      SqlNode source, RelDataType logicalSourceRowType,
       RelDataType logicalTargetRowType) {
     final int sourceFieldCount = logicalSourceRowType.getFieldCount();
     final int targetFieldCount = logicalTargetRowType.getFieldCount();
@@ -4277,19 +4276,61 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
           public RexBuilder getRexBuilder() {
             return new RexBuilder(typeFactory);
           }
+
+          public RexNode convertExpression(SqlNode e) {
+            throw new UnsupportedOperationException();
+          }
         };
+    final List<ColumnStrategy> strategies =
+        table.unwrap(RelOptTable.class).getColumnStrategies();
     for (final RelDataTypeField field : table.getRowType().getFieldList()) {
-      if (!field.getType().isNullable()) {
-        final RelDataTypeField targetField =
-            logicalTargetRowType.getField(field.getName(), true, false);
-        if (targetField == null
-            && !table.columnHasDefaultValue(table.getRowType(),
-                field.getIndex(), rexBuilder)) {
+      final RelDataTypeField targetField =
+          logicalTargetRowType.getField(field.getName(), true, false);
+      switch (strategies.get(field.getIndex())) {
+      case NOT_NULLABLE:
+        assert !field.getType().isNullable();
+        if (targetField == null) {
           throw newValidationError(node,
               RESOURCE.columnNotNullable(field.getName()));
         }
+        break;
+      case NULLABLE:
+        assert field.getType().isNullable();
+        break;
+      case VIRTUAL:
+      case STORED:
+        if (targetField != null
+            && !isValuesWithDefault(source, targetField.getIndex())) {
+          throw newValidationError(node,
+              RESOURCE.insertIntoAlwaysGenerated(field.getName()));
+        }
+      }
+    }
+  }
+
+  /** Returns whether a query uses {@code DEFAULT} to populate a given
+   *  column. */
+  private boolean isValuesWithDefault(SqlNode source, int column) {
+    switch (source.getKind()) {
+    case VALUES:
+      for (SqlNode operand : ((SqlCall) source).getOperandList()) {
+        if (!isRowWithDefault(operand, column)) {
+          return false;
+        }
       }
+      return true;
     }
+    return false;
+  }
+
+  private boolean isRowWithDefault(SqlNode operand, int column) {
+    switch (operand.getKind()) {
+    case ROW:
+      final SqlCall row = (SqlCall) operand;
+      return row.getOperandList().size() >= column
+          && row.getOperandList().get(column).getKind() == SqlKind.DEFAULT;
+    }
+    return false;
   }
 
   protected RelDataType getLogicalTargetRowType(

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorTable.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorTable.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorTable.java
index fd923f0..b660050 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorTable.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorTable.java
@@ -17,6 +17,7 @@
 package org.apache.calcite.sql.validate;
 
 import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.schema.Wrapper;
 import org.apache.calcite.sql.SqlAccessType;
 import org.apache.calcite.sql2rel.InitializerContext;
 
@@ -27,7 +28,7 @@ import java.util.List;
  *
  * @see SqlValidatorCatalogReader
  */
-public interface SqlValidatorTable {
+public interface SqlValidatorTable extends Wrapper {
 
   //~ Methods ----------------------------------------------------------------
 
@@ -50,13 +51,10 @@ public interface SqlValidatorTable {
   /**
    * Returns whether the ordinal column has a default value.
    */
+  @Deprecated // to be removed before 2.0
   boolean columnHasDefaultValue(RelDataType rowType, int ordinal,
       InitializerContext initializerContext);
 
-  /**
-   * Finds an interface implemented by this table.
-   */
-  <T> T unwrap(Class<T> clazz);
 }
 
 // End SqlValidatorTable.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/main/java/org/apache/calcite/sql2rel/InitializerContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql2rel/InitializerContext.java b/core/src/main/java/org/apache/calcite/sql2rel/InitializerContext.java
index 66692e3..b16bede 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/InitializerContext.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/InitializerContext.java
@@ -17,12 +17,16 @@
 package org.apache.calcite.sql2rel;
 
 import org.apache.calcite.rex.RexBuilder;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.sql.SqlNode;
 
 /**
  * Provides context for {@link InitializerExpressionFactory} methods.
  */
 public interface InitializerContext {
   RexBuilder getRexBuilder();
+
+  RexNode convertExpression(SqlNode e);
 }
 
 // End InitializerContext.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/main/java/org/apache/calcite/sql2rel/InitializerExpressionFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql2rel/InitializerExpressionFactory.java b/core/src/main/java/org/apache/calcite/sql2rel/InitializerExpressionFactory.java
index 4fa40b9..3f300ab 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/InitializerExpressionFactory.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/InitializerExpressionFactory.java
@@ -19,6 +19,7 @@ package org.apache.calcite.sql2rel;
 import org.apache.calcite.plan.RelOptTable;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.schema.ColumnStrategy;
 import org.apache.calcite.sql.SqlFunction;
 
 import java.util.List;
@@ -32,12 +33,32 @@ public interface InitializerExpressionFactory {
   /**
    * Whether a column is always generated. If a column is always generated,
    * then non-generated values cannot be inserted into the column.
+   *
+   * @see #generationStrategy(RelOptTable, int)
+   *
+   * @deprecated Use {@code c.generationStrategy(t, i) == VIRTUAL
+   * || c.generationStrategy(t, i) == STORED}
    */
+  @Deprecated // to be removed before 2.0
   boolean isGeneratedAlways(
       RelOptTable table,
       int iColumn);
 
   /**
+   * Returns how a column is populated.
+   *
+   * @param table   the table containing the column
+   * @param iColumn the 0-based offset of the column in the table
+   *
+   * @return generation strategy, never null
+   *
+   * @see RelOptTable#getColumnStrategies()
+   */
+  ColumnStrategy generationStrategy(
+      RelOptTable table,
+      int iColumn);
+
+  /**
    * Creates an expression which evaluates to the default value for a
    * particular column.
    *

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/main/java/org/apache/calcite/sql2rel/NullInitializerExpressionFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql2rel/NullInitializerExpressionFactory.java b/core/src/main/java/org/apache/calcite/sql2rel/NullInitializerExpressionFactory.java
index 09925f0..1959790 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/NullInitializerExpressionFactory.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/NullInitializerExpressionFactory.java
@@ -19,6 +19,7 @@ package org.apache.calcite.sql2rel;
 import org.apache.calcite.plan.RelOptTable;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.schema.ColumnStrategy;
 import org.apache.calcite.sql.SqlFunction;
 
 import java.util.List;
@@ -28,11 +29,27 @@ import java.util.List;
  */
 public class NullInitializerExpressionFactory implements InitializerExpressionFactory {
 
+  public static final InitializerExpressionFactory INSTANCE =
+      new NullInitializerExpressionFactory();
+
   public NullInitializerExpressionFactory() {
   }
 
+  @SuppressWarnings("deprecation")
   public boolean isGeneratedAlways(RelOptTable table, int iColumn) {
-    return false;
+    switch (generationStrategy(table, iColumn)) {
+    case VIRTUAL:
+    case STORED:
+      return true;
+    default:
+      return false;
+    }
+  }
+
+  public ColumnStrategy generationStrategy(RelOptTable table, int iColumn) {
+    return table.getRowType().getFieldList().get(iColumn).getType().isNullable()
+        ? ColumnStrategy.NULLABLE
+        : ColumnStrategy.NOT_NULLABLE;
   }
 
   public RexNode newColumnDefaultValue(RelOptTable table, int iColumn,

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
index 08fc4b0..24fd111 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
@@ -85,6 +85,7 @@ import org.apache.calcite.rex.RexShuttle;
 import org.apache.calcite.rex.RexSubQuery;
 import org.apache.calcite.rex.RexUtil;
 import org.apache.calcite.rex.RexWindowBound;
+import org.apache.calcite.schema.ColumnStrategy;
 import org.apache.calcite.schema.ModifiableTable;
 import org.apache.calcite.schema.ModifiableView;
 import org.apache.calcite.schema.Table;
@@ -170,6 +171,7 @@ import org.apache.calcite.util.trace.CalciteTrace;
 
 import com.google.common.base.Function;
 import com.google.common.base.Preconditions;
+import com.google.common.base.Supplier;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableList.Builder;
 import com.google.common.collect.ImmutableMap;
@@ -198,7 +200,6 @@ import java.util.Set;
 import java.util.TreeSet;
 
 import static org.apache.calcite.sql.SqlUtil.stripAs;
-import static org.apache.calcite.util.Static.RESOURCE;
 
 /**
  * Converts a SQL parse tree (consisting of
@@ -3110,7 +3111,8 @@ public class SqlToRelConverter {
   private RelNode createModify(RelOptTable targetTable, RelNode source) {
     final ModifiableTable modifiableTable =
         targetTable.unwrap(ModifiableTable.class);
-    if (modifiableTable != null) {
+    if (modifiableTable != null
+        && modifiableTable == targetTable.unwrap(Table.class)) {
       return modifiableTable.toModificationRel(cluster, targetTable,
           catalogReader, source, LogicalTableModify.Operation.INSERT, null,
           null, false);
@@ -3199,8 +3201,44 @@ public class SqlToRelConverter {
     };
   }
 
-  public RelNode toRel(RelOptTable table) {
-    return table.toRel(createToRelContext());
+  public RelNode toRel(final RelOptTable table) {
+    final RelNode scan = table.toRel(createToRelContext());
+
+    final InitializerExpressionFactory ief =
+        Util.first(table.unwrap(InitializerExpressionFactory.class),
+            NullInitializerExpressionFactory.INSTANCE);
+
+    // Lazily create a blackboard that contains all non-generated columns.
+    final Supplier<Blackboard> bb = new Supplier<Blackboard>() {
+      public Blackboard get() {
+        RexNode sourceRef = rexBuilder.makeRangeReference(scan);
+        return createInsertBlackboard(table, sourceRef,
+            table.getRowType().getFieldNames());
+      }
+    };
+
+    int virtualCount = 0;
+    final List<RexNode> list = new ArrayList<>();
+    for (RelDataTypeField f : table.getRowType().getFieldList()) {
+      final ColumnStrategy strategy =
+          ief.generationStrategy(table, f.getIndex());
+      switch (strategy) {
+      case VIRTUAL:
+        list.add(ief.newColumnDefaultValue(table, f.getIndex(), bb.get()));
+        ++virtualCount;
+        break;
+      default:
+        list.add(
+            rexBuilder.makeInputRef(scan,
+                RelOptTableImpl.realOrdinal(table, f.getIndex())));
+      }
+    }
+    if (virtualCount > 0) {
+      relBuilder.push(scan);
+      relBuilder.project(list);
+      return relBuilder.build();
+    }
+    return scan;
   }
 
   protected RelOptTable getTargetTable(SqlNode call) {
@@ -3226,13 +3264,11 @@ public class SqlToRelConverter {
    * default values and the source expressions provided.
    *
    * @param call      Insert expression
-   * @param sourceRel Source relational expression
+   * @param source Source relational expression
    * @return Converted INSERT statement
    */
-  protected RelNode convertColumnList(
-      SqlInsert call,
-      RelNode sourceRel) {
-    RelDataType sourceRowType = sourceRel.getRowType();
+  protected RelNode convertColumnList(final SqlInsert call, RelNode source) {
+    RelDataType sourceRowType = source.getRowType();
     final RexNode sourceRef =
         rexBuilder.makeRangeReference(sourceRowType, 0, false);
     final List<String> targetColumnNames = new ArrayList<>();
@@ -3240,9 +3276,8 @@ public class SqlToRelConverter {
     collectInsertTargets(call, sourceRef, targetColumnNames, columnExprs);
 
     final RelOptTable targetTable = getTargetTable(call);
-    final RelDataType targetRowType = targetTable.getRowType();
-    final List<RelDataTypeField> targetFields =
-        targetRowType.getFieldList();
+    final RelDataType targetRowType = RelOptTableImpl.realRowType(targetTable);
+    final List<RelDataTypeField> targetFields = targetRowType.getFieldList();
     final List<RexNode> sourceExps =
         new ArrayList<>(
             Collections.<RexNode>nCopies(targetFields.size(), null));
@@ -3264,32 +3299,56 @@ public class SqlToRelConverter {
       sourceExps.set(field.getIndex(), p.right);
     }
 
+    // Lazily create a blackboard that contains all non-generated columns.
+    final Supplier<Blackboard> bb = new Supplier<Blackboard>() {
+      public Blackboard get() {
+        return createInsertBlackboard(targetTable, sourceRef,
+            targetColumnNames);
+      }
+    };
+
     // Walk the expression list and get default values for any columns
     // that were not supplied in the statement. Get field names too.
     for (int i = 0; i < targetFields.size(); ++i) {
       final RelDataTypeField field = targetFields.get(i);
       final String fieldName = field.getName();
       fieldNames.set(i, fieldName);
-      if (sourceExps.get(i) != null) {
-        if (initializerFactory.isGeneratedAlways(targetTable, i)) {
-          throw RESOURCE.insertIntoAlwaysGenerated(fieldName).ex();
-        }
-        continue;
-      }
-      sourceExps.set(i,
-          initializerFactory.newColumnDefaultValue(targetTable, i,
-              new InitializerContext() {
-                public RexBuilder getRexBuilder() {
-                  return rexBuilder;
-                }
-              }));
+      if (sourceExps.get(i) == null
+          || sourceExps.get(i).getKind() == SqlKind.DEFAULT) {
+        sourceExps.set(i,
+            initializerFactory.newColumnDefaultValue(targetTable, i, bb.get()));
 
-      // bare nulls are dangerous in the wrong hands
-      sourceExps.set(i,
-          castNullLiteralIfNeeded(sourceExps.get(i), field.getType()));
+        // bare nulls are dangerous in the wrong hands
+        sourceExps.set(i,
+            castNullLiteralIfNeeded(sourceExps.get(i), field.getType()));
+      }
     }
 
-    return RelOptUtil.createProject(sourceRel, sourceExps, fieldNames, true);
+    return RelOptUtil.createProject(source, sourceExps, fieldNames, true);
+  }
+
+  /** Creates a blackboard for translating the expressions of generated columns
+   * in an INSERT statement. */
+  private Blackboard createInsertBlackboard(RelOptTable targetTable,
+      RexNode sourceRef, List<String> targetColumnNames) {
+    final Map<String, RexNode> nameToNodeMap = new HashMap<>();
+    int j = 0;
+
+    // Assign expressions for non-generated columns.
+    final List<ColumnStrategy> strategies = targetTable.getColumnStrategies();
+    final List<String> targetFields = targetTable.getRowType().getFieldNames();
+    for (String targetColumnName : targetColumnNames) {
+      final int i = targetFields.indexOf(targetColumnName);
+      switch (strategies.get(i)) {
+      case STORED:
+      case VIRTUAL:
+        break;
+      default:
+        nameToNodeMap.put(targetColumnName,
+            rexBuilder.makeFieldAccess(sourceRef, j++));
+      }
+    }
+    return createBlackboard(null, nameToNodeMap, false);
   }
 
   private InitializerExpressionFactory getInitializerFactory(
@@ -3303,7 +3362,7 @@ public class SqlToRelConverter {
         return f;
       }
     }
-    return new NullInitializerExpressionFactory();
+    return NullInitializerExpressionFactory.INSTANCE;
   }
 
   private static <T> T unwrap(Object o, Class<T> clazz) {
@@ -3359,10 +3418,38 @@ public class SqlToRelConverter {
       }
     }
 
-    for (int i = 0; i < targetColumnNames.size(); i++) {
-      final RexNode expr = rexBuilder.makeFieldAccess(sourceRef, i);
+    final Blackboard bb =
+        createInsertBlackboard(targetTable, sourceRef, targetColumnNames);
+
+    // Next, assign expressions for generated columns.
+    final List<ColumnStrategy> strategies = targetTable.getColumnStrategies();
+    for (String columnName : targetColumnNames) {
+      final int i = tableRowType.getFieldNames().indexOf(columnName);
+      final RexNode expr;
+      switch (strategies.get(i)) {
+      case STORED:
+        final InitializerExpressionFactory f =
+            Util.first(targetTable.unwrap(InitializerExpressionFactory.class),
+                NullInitializerExpressionFactory.INSTANCE);
+        expr = f.newColumnDefaultValue(targetTable, i, bb);
+        break;
+      case VIRTUAL:
+        expr = null;
+        break;
+      default:
+        expr = bb.nameToNodeMap.get(columnName);
+      }
       columnExprs.add(expr);
     }
+
+    // Remove virtual columns from the list.
+    for (int i = 0; i < targetColumnNames.size(); i++) {
+      if (columnExprs.get(i) == null) {
+        columnExprs.remove(i);
+        targetColumnNames.remove(i);
+        --i;
+      }
+    }
   }
 
   private RelNode convertDelete(SqlDelete call) {
@@ -3890,7 +3977,8 @@ public class SqlToRelConverter {
   /**
    * Workspace for translating an individual SELECT statement (or sub-SELECT).
    */
-  protected class Blackboard implements SqlRexContext, SqlVisitor<RexNode> {
+  protected class Blackboard implements SqlRexContext, SqlVisitor<RexNode>,
+      InitializerContext {
     /**
      * Collection of {@link RelNode} objects which correspond to a SELECT
      * statement.
@@ -4231,6 +4319,9 @@ public class SqlToRelConverter {
     }
 
     RelDataTypeField getRootField(RexInputRef inputRef) {
+      if (inputs == null) {
+        return null;
+      }
       int fieldOffset = inputRef.getIndex();
       for (RelNode input : inputs) {
         RelDataType rowType = input.getRowType();

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/main/java/org/apache/calcite/sql2rel/StandardConvertletTable.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql2rel/StandardConvertletTable.java b/core/src/main/java/org/apache/calcite/sql2rel/StandardConvertletTable.java
index 158ffa6..fe92d4b 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/StandardConvertletTable.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/StandardConvertletTable.java
@@ -781,6 +781,10 @@ public class StandardConvertletTable extends ReflectiveConvertletTable {
       public RexBuilder getRexBuilder() {
         return rexBuilder;
       }
+
+      public RexNode convertExpression(SqlNode e) {
+        throw new UnsupportedOperationException();
+      }
     };
     for (int i = 0; i < n; ++i) {
       initializationExprs.add(

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/main/java/org/apache/calcite/util/NameMap.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/util/NameMap.java b/core/src/main/java/org/apache/calcite/util/NameMap.java
index 19a5507..3a34d51 100644
--- a/core/src/main/java/org/apache/calcite/util/NameMap.java
+++ b/core/src/main/java/org/apache/calcite/util/NameMap.java
@@ -16,6 +16,8 @@
  */
 package org.apache.calcite.util;
 
+import org.apache.calcite.linq4j.function.Experimental;
+
 import com.google.common.collect.ImmutableSortedMap;
 
 import java.util.Locale;
@@ -78,6 +80,11 @@ public class NameMap<V> {
   public NavigableMap<String, V> map() {
     return map;
   }
+
+  @Experimental
+  public V remove(String key) {
+    return map.remove(key);
+  }
 }
 
 // End NameMap.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/main/java/org/apache/calcite/util/NameMultimap.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/util/NameMultimap.java b/core/src/main/java/org/apache/calcite/util/NameMultimap.java
index 4d42a29..5422983 100644
--- a/core/src/main/java/org/apache/calcite/util/NameMultimap.java
+++ b/core/src/main/java/org/apache/calcite/util/NameMultimap.java
@@ -16,6 +16,8 @@
  */
 package org.apache.calcite.util;
 
+import org.apache.calcite.linq4j.function.Experimental;
+
 import com.google.common.collect.ImmutableList;
 
 import java.util.ArrayList;
@@ -56,6 +58,18 @@ public class NameMultimap<V> {
     list.add(v);
   }
 
+  /** Removes all entries that have the given case-sensitive key.
+   *
+   * @return Whether a value was removed */
+  @Experimental
+  public boolean remove(String key, V value) {
+    final List<V> list = map.get(key);
+    if (list == null) {
+      return false;
+    }
+    return list.remove(value);
+  }
+
   /** Returns a map containing all the entries in this multimap that match the
    * given name. */
   public Collection<Map.Entry<String, V>> range(String name,

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/main/java/org/apache/calcite/util/Static.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/util/Static.java b/core/src/main/java/org/apache/calcite/util/Static.java
index a3badca..0e05e0b 100644
--- a/core/src/main/java/org/apache/calcite/util/Static.java
+++ b/core/src/main/java/org/apache/calcite/util/Static.java
@@ -18,8 +18,11 @@ package org.apache.calcite.util;
 
 import org.apache.calcite.runtime.CalciteResource;
 
+import org.apache.calcite.runtime.ConsList;
 import org.apache.calcite.runtime.Resources;
 
+import java.util.List;
+
 /**
  * Definitions of objects to be statically imported.
  *
@@ -45,6 +48,11 @@ public abstract class Static {
   /** Resources. */
   public static final CalciteResource RESOURCE =
       Resources.create(CalciteResource.class);
+
+  /** Builds a list. */
+  public static <E> List<E> cons(E first, List<? extends E> rest) {
+    return ConsList.of(first, rest);
+  }
 }
 
 // End Static.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/main/java/org/apache/calcite/util/Util.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/util/Util.java b/core/src/main/java/org/apache/calcite/util/Util.java
index bdea891..ae9286f 100644
--- a/core/src/main/java/org/apache/calcite/util/Util.java
+++ b/core/src/main/java/org/apache/calcite/util/Util.java
@@ -225,8 +225,10 @@ public class Util {
    * you are not interested in, but you don't want the compiler to warn that
    * you are not using it.
    */
-  public static boolean discard(boolean b) {
-    return b;
+  public static void discard(boolean b) {
+    if (false) {
+      discard(b);
+    }
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/core/src/main/java/org/apache/calcite/util/graph/Graphs.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/util/graph/Graphs.java b/core/src/main/java/org/apache/calcite/util/graph/Graphs.java
index 72d691f..cd230c1 100644
--- a/core/src/main/java/org/apache/calcite/util/graph/Graphs.java
+++ b/core/src/main/java/org/apache/calcite/util/graph/Graphs.java
@@ -16,7 +16,6 @@
  */
 package org.apache.calcite.util.graph;
 
-import org.apache.calcite.runtime.ConsList;
 import org.apache.calcite.util.Pair;
 
 import com.google.common.collect.ImmutableList;
@@ -29,6 +28,8 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import static org.apache.calcite.util.Static.cons;
+
 /**
  * Miscellaneous graph utilities.
  */
@@ -80,7 +81,7 @@ public class Graphs {
             if ((bestPath == null)
                 || (bestPath.size() > (arc2Path.size() + 1))) {
               shortestPaths.put(key,
-                  ConsList.of(graph1.source(edge), arc2Path));
+                  cons(graph1.source(edge), arc2Path));
               changeCount++;
             }
           }


[5/5] calcite git commit: [CALCITE-2061] Dynamic parameters in OFFSET, FETCH and LIMIT clauses (Enrico Olivelli)

Posted by jh...@apache.org.
[CALCITE-2061] Dynamic parameters in OFFSET, FETCH and LIMIT clauses (Enrico Olivelli)

Fix how dynamic parameters are un-parsed (Julian Hyde)

Close apache/calcite#569


Project: http://git-wip-us.apache.org/repos/asf/calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/a5d520df
Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/a5d520df
Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/a5d520df

Branch: refs/heads/master
Commit: a5d520df76602d25ed66627f08f5e0db4d048a77
Parents: 238b322
Author: Enrico Olivelli <eo...@apache.org>
Authored: Tue Nov 21 08:59:20 2017 +0100
Committer: Julian Hyde <jh...@apache.org>
Committed: Sun Nov 26 19:42:36 2017 -0800

----------------------------------------------------------------------
 core/src/main/codegen/templates/Parser.jj       | 27 +++++++++--
 .../adapter/enumerable/EnumerableLimit.java     | 19 +++++++-
 .../calcite/rel/metadata/RelMdRowCount.java     | 13 +++++
 .../calcite/rel/rules/PruneEmptyRules.java      |  2 +
 .../org/apache/calcite/sql/SqlDynamicParam.java |  2 +-
 .../calcite/sql/validate/SqlValidatorImpl.java  | 24 ++++++++--
 .../apache/calcite/sql2rel/RelFieldTrimmer.java |  7 +++
 .../calcite/sql2rel/SqlToRelConverter.java      |  3 +-
 .../rel/rel2sql/RelToSqlConverterTest.java      |  2 +-
 .../calcite/sql/parser/SqlParserTest.java       |  7 +++
 .../java/org/apache/calcite/test/JdbcTest.java  | 41 ++++++++++++++++
 .../calcite/test/SqlToRelConverterTest.java     | 22 +++++++++
 .../apache/calcite/test/SqlValidatorTest.java   | 11 +++++
 .../calcite/test/SqlToRelConverterTest.xml      | 50 ++++++++++++++++++++
 site/_docs/reference.md                         |  3 ++
 15 files changed, 220 insertions(+), 13 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/a5d520df/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 fd81f7b..881ab11 100644
--- a/core/src/main/codegen/templates/Parser.jj
+++ b/core/src/main/codegen/templates/Parser.jj
@@ -609,14 +609,14 @@ SqlNode OrderedQueryOrExpr(ExprContext exprContext) :
         <LIMIT>
         (
             // MySQL-style syntax. "LIMIT start, count"
-            start = UnsignedNumericLiteral()
-            <COMMA> count = UnsignedNumericLiteral() {
+            start = UnsignedNumericLiteralOrParam()
+            <COMMA> count = UnsignedNumericLiteralOrParam() {
                 if (!this.conformance.isLimitStartCountAllowed()) {
                     throw new ParseException(RESOURCE.limitStartCountNotAllowed().str());
                 }
             }
         |
-            count = UnsignedNumericLiteral()
+            count = UnsignedNumericLiteralOrParam()
         |
             <ALL>
         )
@@ -625,12 +625,13 @@ SqlNode OrderedQueryOrExpr(ExprContext exprContext) :
         // ROW or ROWS is required in SQL:2008 but we make it optional
         // because it is not present in Postgres-style syntax.
         // If you specify both LIMIT start and OFFSET, OFFSET wins.
-        <OFFSET> start = UnsignedNumericLiteral() [ <ROW> | <ROWS> ]
+        <OFFSET> start = UnsignedNumericLiteralOrParam() [ <ROW> | <ROWS> ]
     ]
     [
         // SQL:2008-style syntax. "OFFSET ... FETCH ...".
         // If you specify both LIMIT and FETCH, FETCH wins.
-        <FETCH> ( <FIRST> | <NEXT> ) count = UnsignedNumericLiteral() ( <ROW> | <ROWS> ) <ONLY>
+        <FETCH> ( <FIRST> | <NEXT> ) count = UnsignedNumericLiteralOrParam()
+        ( <ROW> | <ROWS> ) <ONLY>
     ]
     {
         if (orderBy != null || start != null || count != null) {
@@ -3347,6 +3348,22 @@ SqlCollation CollateClause() :
 }
 
 /**
+ * Numeric literal or parameter; used in LIMIT, OFFSET and FETCH clauses.
+ */
+SqlNode UnsignedNumericLiteralOrParam() :
+{
+    final SqlNode e;
+}
+{
+    (
+        e = UnsignedNumericLiteral()
+    |
+        e = DynamicParam()
+    )
+    { return e; }
+}
+
+/**
  * Parses an atomic row expression.
  */
 SqlNode AtomicRowExpression() :

http://git-wip-us.apache.org/repos/asf/calcite/blob/a5d520df/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableLimit.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableLimit.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableLimit.java
index 0e00dda..577a5a4 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableLimit.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableLimit.java
@@ -16,6 +16,7 @@
  */
 package org.apache.calcite.adapter.enumerable;
 
+import org.apache.calcite.DataContext;
 import org.apache.calcite.linq4j.tree.BlockBuilder;
 import org.apache.calcite.linq4j.tree.Expression;
 import org.apache.calcite.linq4j.tree.Expressions;
@@ -31,6 +32,7 @@ import org.apache.calcite.rel.SingleRel;
 import org.apache.calcite.rel.metadata.RelMdCollation;
 import org.apache.calcite.rel.metadata.RelMdDistribution;
 import org.apache.calcite.rel.metadata.RelMetadataQuery;
+import org.apache.calcite.rex.RexDynamicParam;
 import org.apache.calcite.rex.RexLiteral;
 import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.util.BuiltInMethod;
@@ -117,7 +119,7 @@ public class EnumerableLimit extends SingleRel implements EnumerableRel {
           Expressions.call(
               v,
               BuiltInMethod.SKIP.method,
-              Expressions.constant(RexLiteral.intValue(offset))));
+              getExpression(offset)));
     }
     if (fetch != null) {
       v = builder.append(
@@ -125,7 +127,7 @@ public class EnumerableLimit extends SingleRel implements EnumerableRel {
           Expressions.call(
               v,
               BuiltInMethod.TAKE.method,
-              Expressions.constant(RexLiteral.intValue(fetch))));
+              getExpression(fetch)));
     }
 
     builder.add(
@@ -134,6 +136,19 @@ public class EnumerableLimit extends SingleRel implements EnumerableRel {
             v));
     return implementor.result(physType, builder.toBlock());
   }
+
+  private static Expression getExpression(RexNode offset) {
+    if (offset instanceof RexDynamicParam) {
+      final RexDynamicParam param = (RexDynamicParam) offset;
+      return Expressions.convert_(
+          Expressions.call(DataContext.ROOT,
+              BuiltInMethod.DATA_CONTEXT_GET.method,
+              Expressions.constant("?" + param.getIndex())),
+          Integer.class);
+    } else {
+      return Expressions.constant(RexLiteral.intValue(offset));
+    }
+  }
 }
 
 // End EnumerableLimit.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/a5d520df/core/src/main/java/org/apache/calcite/rel/metadata/RelMdRowCount.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdRowCount.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdRowCount.java
index c9113a1..b5f035b 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdRowCount.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdRowCount.java
@@ -32,6 +32,7 @@ import org.apache.calcite.rel.core.Sort;
 import org.apache.calcite.rel.core.TableScan;
 import org.apache.calcite.rel.core.Union;
 import org.apache.calcite.rel.core.Values;
+import org.apache.calcite.rex.RexDynamicParam;
 import org.apache.calcite.rex.RexLiteral;
 import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.util.Bug;
@@ -137,10 +138,16 @@ public class RelMdRowCount
     if (rowCount == null) {
       return null;
     }
+    if (rel.offset instanceof RexDynamicParam) {
+      return rowCount;
+    }
     final int offset = rel.offset == null ? 0 : RexLiteral.intValue(rel.offset);
     rowCount = Math.max(rowCount - offset, 0D);
 
     if (rel.fetch != null) {
+      if (rel.fetch instanceof RexDynamicParam) {
+        return rowCount;
+      }
       final int limit = RexLiteral.intValue(rel.fetch);
       if (limit < rowCount) {
         return (double) limit;
@@ -154,10 +161,16 @@ public class RelMdRowCount
     if (rowCount == null) {
       return null;
     }
+    if (rel.offset instanceof RexDynamicParam) {
+      return rowCount;
+    }
     final int offset = rel.offset == null ? 0 : RexLiteral.intValue(rel.offset);
     rowCount = Math.max(rowCount - offset, 0D);
 
     if (rel.fetch != null) {
+      if (rel.fetch instanceof RexDynamicParam) {
+        return rowCount;
+      }
       final int limit = RexLiteral.intValue(rel.fetch);
       if (limit < rowCount) {
         return (double) limit;

http://git-wip-us.apache.org/repos/asf/calcite/blob/a5d520df/core/src/main/java/org/apache/calcite/rel/rules/PruneEmptyRules.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/PruneEmptyRules.java b/core/src/main/java/org/apache/calcite/rel/rules/PruneEmptyRules.java
index 51aefe1..f6a89ad 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/PruneEmptyRules.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/PruneEmptyRules.java
@@ -32,6 +32,7 @@ import org.apache.calcite.rel.logical.LogicalIntersect;
 import org.apache.calcite.rel.logical.LogicalMinus;
 import org.apache.calcite.rel.logical.LogicalUnion;
 import org.apache.calcite.rel.logical.LogicalValues;
+import org.apache.calcite.rex.RexDynamicParam;
 import org.apache.calcite.rex.RexLiteral;
 import org.apache.calcite.tools.RelBuilder;
 import org.apache.calcite.tools.RelBuilderFactory;
@@ -247,6 +248,7 @@ public abstract class PruneEmptyRules {
         @Override public void onMatch(RelOptRuleCall call) {
           Sort sort = call.rel(0);
           if (sort.fetch != null
+              && !(sort.fetch instanceof RexDynamicParam)
               && RexLiteral.intValue(sort.fetch) == 0) {
             call.transformTo(call.builder().push(sort).empty().build());
           }

http://git-wip-us.apache.org/repos/asf/calcite/blob/a5d520df/core/src/main/java/org/apache/calcite/sql/SqlDynamicParam.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlDynamicParam.java b/core/src/main/java/org/apache/calcite/sql/SqlDynamicParam.java
index 7db5fd7..db2a6b0 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlDynamicParam.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlDynamicParam.java
@@ -62,7 +62,7 @@ public class SqlDynamicParam extends SqlNode {
       int leftPrec,
       int rightPrec) {
     writer.print("?");
-    writer.setNeedWhitespace(false);
+    writer.setNeedWhitespace(true);
   }
 
   public void validate(SqlValidator validator, SqlValidatorScope scope) {

http://git-wip-us.apache.org/repos/asf/calcite/blob/a5d520df/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
index cb134cf..2e66e54 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
@@ -1063,6 +1063,17 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
     }
   }
 
+  private void handleOffsetFetch(SqlNode offset, SqlNode fetch) {
+    if (offset instanceof SqlDynamicParam) {
+      setValidatedNodeType(offset,
+          typeFactory.createSqlType(SqlTypeName.INTEGER));
+    }
+    if (fetch instanceof SqlDynamicParam) {
+      setValidatedNodeType(fetch,
+          typeFactory.createSqlType(SqlTypeName.INTEGER));
+    }
+  }
+
   /**
    * Performs expression rewrites which are always used unconditionally. These
    * rewrites massage the expression tree into a standard form so that the
@@ -1160,6 +1171,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
 
     case ORDER_BY: {
       SqlOrderBy orderBy = (SqlOrderBy) node;
+      handleOffsetFetch(orderBy.offset, orderBy.fetch);
       if (orderBy.query instanceof SqlSelect) {
         SqlSelect select = (SqlSelect) orderBy.query;
 
@@ -3144,7 +3156,6 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
       SqlSelect select,
       RelDataType targetRowType) {
     assert targetRowType != null;
-
     // Namespace is either a select namespace or a wrapper around one.
     final SelectNamespace ns =
         getNamespace(select).unwrap(SelectNamespace.class);
@@ -3208,6 +3219,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
     validateGroupClause(select);
     validateHavingClause(select);
     validateWindowClause(select);
+    handleOffsetFetch(select.getOffset(), select.getFetch());
 
     // Validate the SELECT clause late, because a select item might
     // depend on the GROUP BY list, or the window function might reference
@@ -5222,11 +5234,17 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
     // NOTE: We assume that bind variables occur in depth-first tree
     // traversal in the same order that they occurred in the SQL text.
     final List<RelDataType> types = new ArrayList<>();
+    // NOTE: but parameters on fetch/offset would be counted twice
+    // as they are counted in the SqlOrderBy call and the inner SqlSelect call
+    final Set<SqlNode> alreadyVisited = new HashSet<>();
     sqlQuery.accept(
         new SqlShuttle() {
+
           @Override public SqlNode visit(SqlDynamicParam param) {
-            RelDataType type = getValidatedNodeType(param);
-            types.add(type);
+            if (alreadyVisited.add(param)) {
+              RelDataType type = getValidatedNodeType(param);
+              types.add(type);
+            }
             return param;
           }
         });

http://git-wip-us.apache.org/repos/asf/calcite/blob/a5d520df/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java b/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
index 2d7ec59..db6588f 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
@@ -43,6 +43,7 @@ import org.apache.calcite.rel.type.RelDataTypeField;
 import org.apache.calcite.rel.type.RelDataTypeImpl;
 import org.apache.calcite.rex.RexBuilder;
 import org.apache.calcite.rex.RexCorrelVariable;
+import org.apache.calcite.rex.RexDynamicParam;
 import org.apache.calcite.rex.RexFieldAccess;
 import org.apache.calcite.rex.RexLiteral;
 import org.apache.calcite.rex.RexNode;
@@ -527,6 +528,12 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
       return result(sort, Mappings.createIdentity(fieldCount));
     }
 
+    // leave the Sort unchanged in case we have dynamic limits
+    if (sort.offset instanceof RexDynamicParam
+        || sort.fetch instanceof RexDynamicParam) {
+      return result(sort, inputMapping);
+    }
+
     relBuilder.push(newInput);
     final int offset =
         sort.offset == null ? 0 : RexLiteral.intValue(sort.offset);

http://git-wip-us.apache.org/repos/asf/calcite/blob/a5d520df/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
index 24fd111..0672dbe 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
@@ -799,7 +799,8 @@ public class SqlToRelConverter {
         || select.getOrderList().getList().isEmpty()) {
       assert collation.getFieldCollations().isEmpty();
       if ((offset == null
-            || ((SqlLiteral) offset).bigDecimalValue().equals(BigDecimal.ZERO))
+            || (offset instanceof SqlLiteral
+                && ((SqlLiteral) offset).bigDecimalValue().equals(BigDecimal.ZERO)))
           && fetch == null) {
         return;
       }

http://git-wip-us.apache.org/repos/asf/calcite/blob/a5d520df/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
index d89df0b..e194456 100644
--- a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
+++ b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
@@ -592,7 +592,7 @@ public class RelToSqlConverterTest {
         + "AND ? >= \"shelf_width\"";
     final String expected = "SELECT *\n"
         + "FROM \"foodmart\".\"product\"\n"
-        + "WHERE \"product_id\" = ?"
+        + "WHERE \"product_id\" = ? "
         + "AND ? >= \"shelf_width\"";
     sql(query).ok(expected);
   }

http://git-wip-us.apache.org/repos/asf/calcite/blob/a5d520df/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 90603db..de9ca4d 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
@@ -2506,6 +2506,13 @@ public class SqlParserTest {
             + "FROM `FOO`\n"
             + "OFFSET 1 ROWS\n"
             + "FETCH NEXT 3 ROWS ONLY");
+    // OFFSET and FETCH, with dynamic parameters
+    check(
+        "select a from foo offset ? row fetch next ? rows only",
+        "SELECT `A`\n"
+            + "FROM `FOO`\n"
+            + "OFFSET ? ROWS\n"
+            + "FETCH NEXT ? ROWS ONLY");
     // missing ROWS after FETCH
     checkFails(
         "select a from foo offset 1 fetch next 3 ^only^",

http://git-wip-us.apache.org/repos/asf/calcite/blob/a5d520df/core/src/test/java/org/apache/calcite/test/JdbcTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/JdbcTest.java b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
index 7d6a479..f269ba6 100644
--- a/core/src/test/java/org/apache/calcite/test/JdbcTest.java
+++ b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
@@ -100,6 +100,7 @@ import com.google.common.collect.ImmutableList;
 import com.google.common.collect.LinkedListMultimap;
 import com.google.common.collect.Multimap;
 
+import org.hamcrest.Matcher;
 import org.hsqldb.jdbcDriver;
 
 import org.junit.Ignore;
@@ -116,12 +117,14 @@ import java.sql.DatabaseMetaData;
 import java.sql.Date;
 import java.sql.DriverManager;
 import java.sql.DriverPropertyInfo;
+import java.sql.ParameterMetaData;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.ResultSetMetaData;
 import java.sql.SQLException;
 import java.sql.Statement;
 import java.sql.Timestamp;
+import java.sql.Types;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Calendar;
@@ -4889,6 +4892,44 @@ public class JdbcTest {
             });
   }
 
+  /** Test case for
+   * <a href="https://issues.apache.org/jira/browse/CALCITE-2061">[CALCITE-2061]
+   * Dynamic parameters in offset/fetch</a>. */
+  @Test public void testPreparedOffsetFetch() throws Exception {
+    checkPreparedOffsetFetch(0, 0, Matchers.returnsUnordered());
+    checkPreparedOffsetFetch(100, 4, Matchers.returnsUnordered());
+    checkPreparedOffsetFetch(3, 4,
+        Matchers.returnsUnordered("name=Eric"));
+  }
+
+  private void checkPreparedOffsetFetch(final int offset, final int fetch,
+      final Matcher<? super ResultSet> matcher) throws Exception {
+    CalciteAssert.hr()
+        .doWithConnection(
+            new Function<CalciteConnection, Object>() {
+              public Object apply(CalciteConnection connection) {
+                final String sql = "select \"name\"\n"
+                    + "from \"hr\".\"emps\"\n"
+                    + "order by \"empid\" offset ? fetch next ? rows only";
+                try (final PreparedStatement p =
+                         connection.prepareStatement(sql)) {
+                  final ParameterMetaData pmd = p.getParameterMetaData();
+                  assertThat(pmd.getParameterCount(), is(2));
+                  assertThat(pmd.getParameterType(1), is(Types.INTEGER));
+                  assertThat(pmd.getParameterType(2), is(Types.INTEGER));
+                  p.setInt(1, offset);
+                  p.setInt(2, fetch);
+                  try (final ResultSet r = p.executeQuery()) {
+                    assertThat(r, matcher);
+                    return null;
+                  }
+                } catch (SQLException e) {
+                  throw new RuntimeException(e);
+                }
+              }
+            });
+  }
+
   /** Tests a JDBC connection that provides a model (a single schema based on
    * a JDBC database). */
   @Test public void testModel() {

http://git-wip-us.apache.org/repos/asf/calcite/blob/a5d520df/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
index dd46878..5424041 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
@@ -669,22 +669,44 @@ public class SqlToRelConverterTest extends SqlToRelTestBase {
     sql(sql).ok();
   }
 
+  @Test public void testOrderOffsetFetchWithDynamicParameter() {
+    final String sql = "select empno from emp\n"
+        + "order by empno offset ? rows fetch next ? rows only";
+    sql(sql).ok();
+  }
+
   @Test public void testOffsetFetch() {
     final String sql = "select empno from emp\n"
         + "offset 10 rows fetch next 5 rows only";
     sql(sql).ok();
   }
 
+  @Test public void testOffsetFetchWithDynamicParameter() {
+    final String sql = "select empno from emp\n"
+        + "offset ? rows fetch next ? rows only";
+    sql(sql).ok();
+  }
+
   @Test public void testOffset() {
     final String sql = "select empno from emp offset 10 rows";
     sql(sql).ok();
   }
 
+  @Test public void testOffsetWithDynamicParameter() {
+    final String sql = "select empno from emp offset ? rows";
+    sql(sql).ok();
+  }
+
   @Test public void testFetch() {
     final String sql = "select empno from emp fetch next 5 rows only";
     sql(sql).ok();
   }
 
+  @Test public void testFetchWithDynamicParameter() {
+    final String sql = "select empno from emp fetch next ? rows only";
+    sql(sql).ok();
+  }
+
   /** Test case for
    * <a href="https://issues.apache.org/jira/browse/CALCITE-439">[CALCITE-439]
    * SqlValidatorUtil.uniquify() may not terminate under some

http://git-wip-us.apache.org/repos/asf/calcite/blob/a5d520df/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
index c7378a3..c8b9d46 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
@@ -7918,6 +7918,17 @@ public class SqlValidatorTest extends SqlValidatorTestCase {
     tester.checkRewrite(validator, sql, expected);
   }
 
+  @Test public void testRewriteWithLimitWithDynamicParameters() {
+    SqlValidator validator = tester.getValidator();
+    validator.setIdentifierExpansion(false);
+    final String sql = "select name from dept offset ? rows fetch next ? rows only";
+    final String expected = "SELECT `NAME`\n"
+        + "FROM `DEPT`\n"
+        + "OFFSET ? ROWS\n"
+        + "FETCH NEXT ? ROWS ONLY";
+    tester.checkRewrite(validator, sql, expected);
+  }
+
   @Test public void testRewriteWithOffsetWithoutOrderBy() {
     SqlValidator validator = tester.getValidator();
     validator.setIdentifierExpansion(false);

http://git-wip-us.apache.org/repos/asf/calcite/blob/a5d520df/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
----------------------------------------------------------------------
diff --git a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
index 34037ba..18f4494 100644
--- a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
@@ -1461,6 +1461,19 @@ LogicalSort(sort0=[$0], dir0=[ASC], offset=[10], fetch=[5])
 ]]>
         </Resource>
     </TestCase>
+    <TestCase name="testOrderOffsetFetchWithDynamicParameter">
+        <Resource name="sql">
+            <![CDATA[select empno from emp
+order by empno offset ? rows fetch next ? rows only]]>
+        </Resource>
+        <Resource name="plan">
+            <![CDATA[
+LogicalSort(sort0=[$0], dir0=[ASC], offset=[?0], fetch=[?1])
+  LogicalProject(EMPNO=[$0])
+    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+    </TestCase>
     <TestCase name="testFetch">
         <Resource name="sql">
             <![CDATA[select empno from emp fetch next 5 rows only]]>
@@ -1473,6 +1486,18 @@ LogicalSort(fetch=[5])
 ]]>
         </Resource>
     </TestCase>
+    <TestCase name="testFetchWithDynamicParameter">
+        <Resource name="sql">
+            <![CDATA[select empno from emp fetch next ? rows only]]>
+        </Resource>
+        <Resource name="plan">
+            <![CDATA[
+LogicalSort(fetch=[?0])
+  LogicalProject(EMPNO=[$0])
+    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+    </TestCase>
     <TestCase name="testOffsetFetch">
         <Resource name="sql">
             <![CDATA[select empno from emp
@@ -1486,6 +1511,19 @@ LogicalSort(offset=[10], fetch=[5])
 ]]>
         </Resource>
     </TestCase>
+<TestCase name="testOffsetFetchWithDynamicParameter">
+        <Resource name="sql">
+            <![CDATA[select empno from emp
+offset ? rows fetch next ? rows only]]>
+        </Resource>
+        <Resource name="plan">
+            <![CDATA[
+LogicalSort(offset=[?0], fetch=[?1])
+  LogicalProject(EMPNO=[$0])
+    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+    </TestCase>
     <TestCase name="testOffset">
         <Resource name="sql">
             <![CDATA[select empno from emp offset 10 rows]]>
@@ -1498,6 +1536,18 @@ LogicalSort(offset=[10])
 ]]>
         </Resource>
     </TestCase>
+    <TestCase name="testOffsetWithDynamicParameter">
+        <Resource name="sql">
+            <![CDATA[select empno from emp offset ? rows]]>
+        </Resource>
+        <Resource name="plan">
+            <![CDATA[
+LogicalSort(offset=[?0])
+  LogicalProject(EMPNO=[$0])
+    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+    </TestCase>
     <TestCase name="testMultiAnd">
         <Resource name="sql">
             <![CDATA[select * from emp

http://git-wip-us.apache.org/repos/asf/calcite/blob/a5d520df/site/_docs/reference.md
----------------------------------------------------------------------
diff --git a/site/_docs/reference.md b/site/_docs/reference.md
index ce85272..f9b8b14 100644
--- a/site/_docs/reference.md
+++ b/site/_docs/reference.md
@@ -251,6 +251,9 @@ in those same conformance levels, any *column* in *insert* may be replaced by
 In *orderItem*, if *expression* is a positive integer *n*, it denotes
 the <em>n</em>th item in the SELECT clause.
 
+In *query*, *count* and *start* may each be either an unsigned integer literal
+or a dynamic parameter whose value is an integer.
+
 An aggregate query is a query that contains a GROUP BY or a HAVING
 clause, or aggregate functions in the SELECT clause. In the SELECT,
 HAVING and ORDER BY clauses of an aggregate query, all expressions


[2/5] calcite git commit: [CALCITE-707] Add "server" module, with built-in support for simple DDL statements

Posted by jh...@apache.org.
http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/server/src/main/java/org/apache/calcite/sql/ddl/SqlCreateTable.java
----------------------------------------------------------------------
diff --git a/server/src/main/java/org/apache/calcite/sql/ddl/SqlCreateTable.java b/server/src/main/java/org/apache/calcite/sql/ddl/SqlCreateTable.java
new file mode 100644
index 0000000..22cc2d3
--- /dev/null
+++ b/server/src/main/java/org/apache/calcite/sql/ddl/SqlCreateTable.java
@@ -0,0 +1,338 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.sql.ddl;
+
+import org.apache.calcite.adapter.java.JavaTypeFactory;
+import org.apache.calcite.jdbc.CalcitePrepare;
+import org.apache.calcite.jdbc.CalciteSchema;
+import org.apache.calcite.jdbc.JavaTypeFactoryImpl;
+import org.apache.calcite.linq4j.Enumerator;
+import org.apache.calcite.linq4j.Linq4j;
+import org.apache.calcite.linq4j.Ord;
+import org.apache.calcite.linq4j.QueryProvider;
+import org.apache.calcite.linq4j.Queryable;
+import org.apache.calcite.linq4j.tree.Expression;
+import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelOptTable;
+import org.apache.calcite.prepare.Prepare;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.TableModify;
+import org.apache.calcite.rel.logical.LogicalTableModify;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
+import org.apache.calcite.rel.type.RelDataTypeField;
+import org.apache.calcite.rel.type.RelDataTypeImpl;
+import org.apache.calcite.rel.type.RelProtoDataType;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.schema.ColumnStrategy;
+import org.apache.calcite.schema.ModifiableTable;
+import org.apache.calcite.schema.SchemaPlus;
+import org.apache.calcite.schema.Schemas;
+import org.apache.calcite.schema.TranslatableTable;
+import org.apache.calcite.schema.Wrapper;
+import org.apache.calcite.schema.impl.AbstractTable;
+import org.apache.calcite.schema.impl.AbstractTableQueryable;
+import org.apache.calcite.schema.impl.ViewTable;
+import org.apache.calcite.schema.impl.ViewTableMacro;
+import org.apache.calcite.sql.SqlCreate;
+import org.apache.calcite.sql.SqlExecutableStatement;
+import org.apache.calcite.sql.SqlIdentifier;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.SqlNode;
+import org.apache.calcite.sql.SqlNodeList;
+import org.apache.calcite.sql.SqlOperator;
+import org.apache.calcite.sql.SqlSpecialOperator;
+import org.apache.calcite.sql.SqlUtil;
+import org.apache.calcite.sql.SqlWriter;
+import org.apache.calcite.sql.dialect.CalciteSqlDialect;
+import org.apache.calcite.sql.parser.SqlParserPos;
+import org.apache.calcite.sql2rel.InitializerContext;
+import org.apache.calcite.sql2rel.InitializerExpressionFactory;
+import org.apache.calcite.sql2rel.NullInitializerExpressionFactory;
+import org.apache.calcite.util.ImmutableNullableList;
+import org.apache.calcite.util.Pair;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import static org.apache.calcite.util.Static.RESOURCE;
+
+/**
+ * Parse tree for {@code CREATE TABLE} statement.
+ */
+public class SqlCreateTable extends SqlCreate
+    implements SqlExecutableStatement {
+  private final SqlIdentifier name;
+  private final SqlNodeList columnList;
+  private final SqlNode query;
+
+  private static final SqlOperator OPERATOR =
+      new SqlSpecialOperator("CREATE TABLE", SqlKind.CREATE_TABLE);
+
+  /** Creates a SqlCreateTable. */
+  SqlCreateTable(SqlParserPos pos, boolean replace, boolean ifNotExists,
+      SqlIdentifier name, SqlNodeList columnList, SqlNode query) {
+    super(OPERATOR, pos, replace, ifNotExists);
+    this.name = Preconditions.checkNotNull(name);
+    this.columnList = columnList; // may be null
+    this.query = query; // for "CREATE TABLE ... AS query"; may be null
+  }
+
+  public List<SqlNode> getOperandList() {
+    return ImmutableNullableList.of(name, columnList, query);
+  }
+
+  @Override public void unparse(SqlWriter writer, int leftPrec, int rightPrec) {
+    writer.keyword("CREATE");
+    writer.keyword("TABLE");
+    if (ifNotExists) {
+      writer.keyword("IF NOT EXISTS");
+    }
+    name.unparse(writer, leftPrec, rightPrec);
+    if (columnList != null) {
+      SqlWriter.Frame frame = writer.startList("(", ")");
+      for (SqlNode c : columnList) {
+        writer.sep(",");
+        c.unparse(writer, 0, 0);
+      }
+      writer.endList(frame);
+    }
+    if (query != null) {
+      writer.keyword("AS");
+      writer.newlineAndIndent();
+      query.unparse(writer, 0, 0);
+    }
+  }
+
+  public void execute(CalcitePrepare.Context context) {
+    final Pair<CalciteSchema, String> pair =
+        SqlDdlNodes.schema(context, true, name);
+    final JavaTypeFactory typeFactory = new JavaTypeFactoryImpl();
+    final RelDataType queryRowType;
+    if (query != null) {
+      // A bit of a hack: pretend it's a view, to get its row type
+      final String sql = query.toSqlString(CalciteSqlDialect.DEFAULT).getSql();
+      final ViewTableMacro viewTableMacro =
+          ViewTable.viewMacro(pair.left.plus(), sql, pair.left.path(null),
+              context.getObjectPath(), false);
+      final TranslatableTable x = viewTableMacro.apply(ImmutableList.of());
+      queryRowType = x.getRowType(typeFactory);
+
+      if (columnList != null
+          && queryRowType.getFieldCount() != columnList.size()) {
+        throw SqlUtil.newContextException(columnList.getParserPosition(),
+            RESOURCE.columnCountMismatch());
+      }
+    } else {
+      queryRowType = null;
+    }
+    final List<SqlNode> columnList;
+    if (this.columnList != null) {
+      columnList = this.columnList.getList();
+    } else {
+      if (queryRowType == null) {
+        // "CREATE TABLE t" is invalid; because there is no "AS query" we need
+        // a list of column names and types, "CREATE TABLE t (INT c)".
+        throw SqlUtil.newContextException(name.getParserPosition(),
+            RESOURCE.createTableRequiresColumnList());
+      }
+      columnList = new ArrayList<>();
+      for (String name : queryRowType.getFieldNames()) {
+        columnList.add(new SqlIdentifier(name, SqlParserPos.ZERO));
+      }
+    }
+    final ImmutableList.Builder<ColumnDef> b = ImmutableList.builder();
+    final RelDataTypeFactory.Builder builder = typeFactory.builder();
+    final RelDataTypeFactory.Builder storedBuilder = typeFactory.builder();
+    for (Ord<SqlNode> c : Ord.zip(columnList)) {
+      if (c.e instanceof SqlColumnDeclaration) {
+        final SqlColumnDeclaration d = (SqlColumnDeclaration) c.e;
+        final RelDataType type = d.dataType.deriveType(typeFactory, true);
+        builder.add(d.name.getSimple(), type);
+        if (d.strategy != ColumnStrategy.VIRTUAL) {
+          storedBuilder.add(d.name.getSimple(), type);
+        }
+        b.add(ColumnDef.of(d.expression, type, d.strategy));
+      } else if (c.e instanceof SqlIdentifier) {
+        final SqlIdentifier id = (SqlIdentifier) c.e;
+        if (queryRowType == null) {
+          throw SqlUtil.newContextException(id.getParserPosition(),
+              RESOURCE.createTableRequiresColumnTypes(id.getSimple()));
+        }
+        final RelDataTypeField f = queryRowType.getFieldList().get(c.i);
+        final ColumnStrategy strategy = f.getType().isNullable()
+            ? ColumnStrategy.NULLABLE
+            : ColumnStrategy.NOT_NULLABLE;
+        b.add(ColumnDef.of(c.e, f.getType(), strategy));
+        builder.add(id.getSimple(), f.getType());
+        storedBuilder.add(id.getSimple(), f.getType());
+      } else {
+        throw new AssertionError(c.e.getClass());
+      }
+    }
+    final RelDataType rowType = builder.build();
+    final RelDataType storedRowType = storedBuilder.build();
+    final List<ColumnDef> columns = b.build();
+    final InitializerExpressionFactory ief =
+        new NullInitializerExpressionFactory() {
+          @Override public ColumnStrategy generationStrategy(RelOptTable table,
+              int iColumn) {
+            return columns.get(iColumn).strategy;
+          }
+
+          @Override public RexNode newColumnDefaultValue(RelOptTable table,
+              int iColumn, InitializerContext context) {
+            final ColumnDef c = columns.get(iColumn);
+            if (c.expr != null) {
+              return context.convertExpression(c.expr);
+            }
+            return super.newColumnDefaultValue(table, iColumn, context);
+          }
+        };
+    if (pair.left.plus().getTable(pair.right) != null) {
+      // Table exists.
+      if (!ifNotExists) {
+        // They did not specify IF NOT EXISTS, so give error.
+        throw SqlUtil.newContextException(name.getParserPosition(),
+            RESOURCE.tableExists(pair.right));
+      }
+      return;
+    }
+    // Table does not exist. Create it.
+    pair.left.add(pair.right,
+        new MutableArrayTable(pair.right,
+            RelDataTypeImpl.proto(storedRowType),
+            RelDataTypeImpl.proto(rowType), ief));
+    if (query != null) {
+      SqlDdlNodes.populate(name, query, context);
+    }
+  }
+
+  /** Column definition. */
+  private static class ColumnDef {
+    final SqlNode expr;
+    final RelDataType type;
+    final ColumnStrategy strategy;
+
+    private ColumnDef(SqlNode expr, RelDataType type,
+        ColumnStrategy strategy) {
+      this.expr = expr;
+      this.type = type;
+      this.strategy = Preconditions.checkNotNull(strategy);
+      Preconditions.checkArgument(
+          strategy == ColumnStrategy.NULLABLE
+              || strategy == ColumnStrategy.NOT_NULLABLE
+              || expr != null);
+    }
+
+    static ColumnDef of(SqlNode expr, RelDataType type,
+        ColumnStrategy strategy) {
+      return new ColumnDef(expr, type, strategy);
+    }
+  }
+
+  /** Abstract base class for implementations of {@link ModifiableTable}. */
+  abstract static class AbstractModifiableTable
+      extends AbstractTable implements ModifiableTable {
+    AbstractModifiableTable(String tableName) {
+      super();
+    }
+
+    public TableModify toModificationRel(
+        RelOptCluster cluster,
+        RelOptTable table,
+        Prepare.CatalogReader catalogReader,
+        RelNode child,
+        TableModify.Operation operation,
+        List<String> updateColumnList,
+        List<RexNode> sourceExpressionList,
+        boolean flattened) {
+      return LogicalTableModify.create(table, catalogReader, child, operation,
+          updateColumnList, sourceExpressionList, flattened);
+    }
+  }
+
+  /** Table backed by a Java list. */
+  static class MutableArrayTable extends AbstractModifiableTable
+      implements Wrapper {
+    final List rows = new ArrayList();
+    private final RelProtoDataType protoStoredRowType;
+    private final RelProtoDataType protoRowType;
+    private final InitializerExpressionFactory initializerExpressionFactory;
+
+    /** Creates a MutableArrayTable.
+     *
+     * @param name Name of table within its schema
+     * @param protoStoredRowType Prototype of row type of stored columns (all
+     *     columns except virtual columns)
+     * @param protoRowType Prototype of row type (all columns)
+     * @param initializerExpressionFactory How columns are populated
+     */
+    MutableArrayTable(String name, RelProtoDataType protoStoredRowType,
+        RelProtoDataType protoRowType,
+        InitializerExpressionFactory initializerExpressionFactory) {
+      super(name);
+      this.protoStoredRowType = Preconditions.checkNotNull(protoStoredRowType);
+      this.protoRowType = Preconditions.checkNotNull(protoRowType);
+      this.initializerExpressionFactory =
+          Preconditions.checkNotNull(initializerExpressionFactory);
+    }
+
+    public Collection getModifiableCollection() {
+      return rows;
+    }
+
+    public <T> Queryable<T> asQueryable(QueryProvider queryProvider,
+        SchemaPlus schema, String tableName) {
+      return new AbstractTableQueryable<T>(queryProvider, schema, this,
+          tableName) {
+        public Enumerator<T> enumerator() {
+          //noinspection unchecked
+          return (Enumerator<T>) Linq4j.enumerator(rows);
+        }
+      };
+    }
+
+    public Type getElementType() {
+      return Object[].class;
+    }
+
+    public Expression getExpression(SchemaPlus schema, String tableName,
+        Class clazz) {
+      return Schemas.tableExpression(schema, getElementType(),
+          tableName, clazz);
+    }
+
+    public RelDataType getRowType(RelDataTypeFactory typeFactory) {
+      return protoRowType.apply(typeFactory);
+    }
+
+    @Override public <C> C unwrap(Class<C> aClass) {
+      if (aClass.isInstance(initializerExpressionFactory)) {
+        return aClass.cast(initializerExpressionFactory);
+      }
+      return super.unwrap(aClass);
+    }
+  }
+}
+
+// End SqlCreateTable.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/server/src/main/java/org/apache/calcite/sql/ddl/SqlCreateView.java
----------------------------------------------------------------------
diff --git a/server/src/main/java/org/apache/calcite/sql/ddl/SqlCreateView.java b/server/src/main/java/org/apache/calcite/sql/ddl/SqlCreateView.java
new file mode 100644
index 0000000..e9e3076
--- /dev/null
+++ b/server/src/main/java/org/apache/calcite/sql/ddl/SqlCreateView.java
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.sql.ddl;
+
+import org.apache.calcite.jdbc.CalcitePrepare;
+import org.apache.calcite.jdbc.CalciteSchema;
+import org.apache.calcite.schema.Function;
+import org.apache.calcite.schema.SchemaPlus;
+import org.apache.calcite.schema.TranslatableTable;
+import org.apache.calcite.schema.impl.ViewTable;
+import org.apache.calcite.schema.impl.ViewTableMacro;
+import org.apache.calcite.sql.SqlCreate;
+import org.apache.calcite.sql.SqlExecutableStatement;
+import org.apache.calcite.sql.SqlIdentifier;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.SqlNode;
+import org.apache.calcite.sql.SqlNodeList;
+import org.apache.calcite.sql.SqlOperator;
+import org.apache.calcite.sql.SqlSpecialOperator;
+import org.apache.calcite.sql.SqlUtil;
+import org.apache.calcite.sql.SqlWriter;
+import org.apache.calcite.sql.dialect.CalciteSqlDialect;
+import org.apache.calcite.sql.parser.SqlParserPos;
+import org.apache.calcite.util.ImmutableNullableList;
+import org.apache.calcite.util.Pair;
+import org.apache.calcite.util.Util;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+
+import static org.apache.calcite.util.Static.RESOURCE;
+
+/**
+ * Parse tree for {@code CREATE VIEW} statement.
+ */
+public class SqlCreateView extends SqlCreate
+    implements SqlExecutableStatement {
+  private final SqlIdentifier name;
+  private final SqlNodeList columnList;
+  private final SqlNode query;
+
+  private static final SqlOperator OPERATOR =
+      new SqlSpecialOperator("CREATE VIEW", SqlKind.CREATE_VIEW);
+
+  /** Creates a SqlCreateView. */
+  SqlCreateView(SqlParserPos pos, boolean replace, SqlIdentifier name,
+      SqlNodeList columnList, SqlNode query) {
+    super(OPERATOR, pos, replace, false);
+    this.name = Preconditions.checkNotNull(name);
+    this.columnList = columnList; // may be null
+    this.query = Preconditions.checkNotNull(query);
+  }
+
+  public List<SqlNode> getOperandList() {
+    return ImmutableNullableList.of(name, columnList, query);
+  }
+
+  @Override public void unparse(SqlWriter writer, int leftPrec, int rightPrec) {
+    if (getReplace()) {
+      writer.keyword("CREATE OR REPLACE");
+    } else {
+      writer.keyword("CREATE");
+    }
+    writer.keyword("VIEW");
+    name.unparse(writer, leftPrec, rightPrec);
+    if (columnList != null) {
+      SqlWriter.Frame frame = writer.startList("(", ")");
+      for (SqlNode c : columnList) {
+        writer.sep(",");
+        c.unparse(writer, 0, 0);
+      }
+      writer.endList(frame);
+    }
+    writer.keyword("AS");
+    writer.newlineAndIndent();
+    query.unparse(writer, 0, 0);
+  }
+
+  public void execute(CalcitePrepare.Context context) {
+    final Pair<CalciteSchema, String> pair =
+        SqlDdlNodes.schema(context, true, name);
+    final SchemaPlus schemaPlus = pair.left.plus();
+    for (Function function : schemaPlus.getFunctions(pair.right)) {
+      if (function.getParameters().isEmpty()) {
+        if (!getReplace()) {
+          throw SqlUtil.newContextException(name.getParserPosition(),
+              RESOURCE.viewExists(pair.right));
+        }
+        pair.left.removeFunction(pair.right);
+      }
+    }
+    final SqlNode q = SqlDdlNodes.renameColumns(columnList, query);
+    final String sql = q.toSqlString(CalciteSqlDialect.DEFAULT).getSql();
+    final ViewTableMacro viewTableMacro =
+        ViewTable.viewMacro(schemaPlus, sql, pair.left.path(null),
+            context.getObjectPath(), false);
+    final TranslatableTable x = viewTableMacro.apply(ImmutableList.of());
+    Util.discard(x);
+    schemaPlus.add(pair.right, viewTableMacro);
+  }
+
+}
+
+// End SqlCreateView.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/server/src/main/java/org/apache/calcite/sql/ddl/SqlDdlNodes.java
----------------------------------------------------------------------
diff --git a/server/src/main/java/org/apache/calcite/sql/ddl/SqlDdlNodes.java b/server/src/main/java/org/apache/calcite/sql/ddl/SqlDdlNodes.java
new file mode 100644
index 0000000..057c2ee
--- /dev/null
+++ b/server/src/main/java/org/apache/calcite/sql/ddl/SqlDdlNodes.java
@@ -0,0 +1,221 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.sql.ddl;
+
+import org.apache.calcite.jdbc.CalcitePrepare;
+import org.apache.calcite.jdbc.CalciteSchema;
+import org.apache.calcite.rel.RelRoot;
+import org.apache.calcite.schema.ColumnStrategy;
+import org.apache.calcite.sql.SqlCall;
+import org.apache.calcite.sql.SqlDataTypeSpec;
+import org.apache.calcite.sql.SqlDrop;
+import org.apache.calcite.sql.SqlIdentifier;
+import org.apache.calcite.sql.SqlNode;
+import org.apache.calcite.sql.SqlNodeList;
+import org.apache.calcite.sql.SqlOperator;
+import org.apache.calcite.sql.SqlSelect;
+import org.apache.calcite.sql.dialect.CalciteSqlDialect;
+import org.apache.calcite.sql.fun.SqlStdOperatorTable;
+import org.apache.calcite.sql.parser.SqlParseException;
+import org.apache.calcite.sql.parser.SqlParserPos;
+import org.apache.calcite.sql.pretty.SqlPrettyWriter;
+import org.apache.calcite.tools.FrameworkConfig;
+import org.apache.calcite.tools.Frameworks;
+import org.apache.calcite.tools.Planner;
+import org.apache.calcite.tools.RelConversionException;
+import org.apache.calcite.tools.ValidationException;
+import org.apache.calcite.util.Pair;
+import org.apache.calcite.util.Util;
+
+import com.google.common.collect.ImmutableList;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.List;
+
+/**
+ * Utilities concerning {@link SqlNode} for DDL.
+ */
+public class SqlDdlNodes {
+  private SqlDdlNodes() {}
+
+  /** Creates a CREATE SCHEMA. */
+  public static SqlCreateSchema createSchema(SqlParserPos pos, boolean replace,
+      boolean ifNotExists, SqlIdentifier name) {
+    return new SqlCreateSchema(pos, replace, ifNotExists, name);
+  }
+
+  /** Creates a CREATE FOREIGN SCHEMA. */
+  public static SqlCreateForeignSchema createForeignSchema(SqlParserPos pos,
+      boolean replace, boolean ifNotExists, SqlIdentifier name, SqlNode type,
+      SqlNode library, SqlNodeList optionList) {
+    return new SqlCreateForeignSchema(pos, replace, ifNotExists, name, type,
+        library, optionList);
+  }
+
+  /** Creates a CREATE TABLE. */
+  public static SqlCreateTable createTable(SqlParserPos pos, boolean replace,
+      boolean ifNotExists, SqlIdentifier name, SqlNodeList columnList,
+      SqlNode query) {
+    return new SqlCreateTable(pos, replace, ifNotExists, name, columnList,
+        query);
+  }
+
+  /** Creates a CREATE VIEW. */
+  public static SqlCreateView createView(SqlParserPos pos, boolean replace,
+      SqlIdentifier name, SqlNodeList columnList, SqlNode query) {
+    return new SqlCreateView(pos, replace, name, columnList, query);
+  }
+
+  /** Creates a CREATE MATERIALIZED VIEW. */
+  public static SqlCreateMaterializedView createMaterializedView(
+      SqlParserPos pos, boolean replace, boolean ifNotExists,
+      SqlIdentifier name, SqlNodeList columnList, SqlNode query) {
+    return new SqlCreateMaterializedView(pos, replace, ifNotExists, name,
+        columnList, query);
+  }
+
+  /** Creates a DROP [ FOREIGN ] SCHEMA. */
+  public static SqlDropSchema dropSchema(SqlParserPos pos, boolean foreign,
+      boolean ifExists, SqlIdentifier name) {
+    return new SqlDropSchema(pos, foreign, ifExists, name);
+  }
+
+  /** Creates a DROP TABLE. */
+  public static SqlDropTable dropTable(SqlParserPos pos, boolean ifExists,
+      SqlIdentifier name) {
+    return new SqlDropTable(pos, ifExists, name);
+  }
+
+  /** Creates a DROP VIEW. */
+  public static SqlDrop dropView(SqlParserPos pos, boolean ifExists,
+      SqlIdentifier name) {
+    return new SqlDropView(pos, ifExists, name);
+  }
+
+  /** Creates a DROP MATERIALIZED VIEW. */
+  public static SqlDrop dropMaterializedView(SqlParserPos pos,
+      boolean ifExists, SqlIdentifier name) {
+    return new SqlDropMaterializedView(pos, ifExists, name);
+  }
+
+  /** Creates a column declaration. */
+  public static SqlNode column(SqlParserPos pos, SqlIdentifier name,
+      SqlDataTypeSpec dataType, SqlNode expression, ColumnStrategy strategy) {
+    return new SqlColumnDeclaration(pos, name, dataType, expression, strategy);
+  }
+
+  /** Creates a CHECK constraint. */
+  public static SqlNode check(SqlParserPos pos, SqlIdentifier name,
+      SqlNode expression) {
+    return new SqlCheckConstraint(pos, name, expression);
+  }
+
+  /** Creates a UNIQUE constraint. */
+  public static SqlKeyConstraint unique(SqlParserPos pos, SqlIdentifier name,
+      SqlNodeList columnList) {
+    return new SqlKeyConstraint(pos, name, columnList);
+  }
+
+  /** Creates a PRIMARY KEY constraint. */
+  public static SqlKeyConstraint primary(SqlParserPos pos, SqlIdentifier name,
+      SqlNodeList columnList) {
+    return new SqlKeyConstraint(pos, name, columnList) {
+      @Override public SqlOperator getOperator() {
+        return PRIMARY;
+      }
+    };
+  }
+
+  /** Returns the schema in which to create an object. */
+  static Pair<CalciteSchema, String> schema(CalcitePrepare.Context context,
+      boolean mutable, SqlIdentifier id) {
+    final String name;
+    final List<String> path;
+    if (id.isSimple()) {
+      path = context.getDefaultSchemaPath();
+      name = id.getSimple();
+    } else {
+      path = Util.skipLast(id.names);
+      name = Util.last(id.names);
+    }
+    CalciteSchema schema = mutable ? context.getMutableRootSchema()
+        : context.getRootSchema();
+    for (String p : path) {
+      schema = schema.getSubSchema(p, true);
+    }
+    return Pair.of(schema, name);
+  }
+
+  /** Wraps a query to rename its columns. Used by CREATE VIEW and CREATE
+   * MATERIALIZED VIEW. */
+  static SqlNode renameColumns(SqlNodeList columnList, SqlNode query) {
+    if (columnList == null) {
+      return query;
+    }
+    final SqlParserPos p = query.getParserPosition();
+    final SqlNodeList selectList =
+        new SqlNodeList(ImmutableList.<SqlNode>of(SqlIdentifier.star(p)), p);
+    final SqlCall from =
+        SqlStdOperatorTable.AS.createCall(p,
+            ImmutableList.<SqlNode>builder()
+                .add(query)
+                .add(new SqlIdentifier("_", p))
+                .addAll(columnList)
+                .build());
+    return new SqlSelect(p, null, selectList, from, null, null, null, null,
+        null, null, null);
+  }
+
+  /** Populates the table called {@code name} by executing {@code query}. */
+  protected static void populate(SqlIdentifier name, SqlNode query,
+      CalcitePrepare.Context context) {
+    // Generate, prepare and execute an "INSERT INTO table query" statement.
+    // (It's a bit inefficient that we convert from SqlNode to SQL and back
+    // again.)
+    final FrameworkConfig config = Frameworks.newConfigBuilder()
+        .defaultSchema(context.getRootSchema().plus())
+        .build();
+    final Planner planner = Frameworks.getPlanner(config);
+    try {
+      final StringWriter sw = new StringWriter();
+      final PrintWriter pw = new PrintWriter(sw);
+      final SqlPrettyWriter w =
+          new SqlPrettyWriter(CalciteSqlDialect.DEFAULT, false, pw);
+      pw.print("INSERT INTO ");
+      name.unparse(w, 0, 0);
+      pw.print(" ");
+      query.unparse(w, 0, 0);
+      pw.flush();
+      final String sql = sw.toString();
+      final SqlNode query1 = planner.parse(sql);
+      final SqlNode query2 = planner.validate(query1);
+      final RelRoot r = planner.rel(query2);
+      final PreparedStatement prepare = context.getRelRunner().prepare(r.rel);
+      int rowCount = prepare.executeUpdate();
+      Util.discard(rowCount);
+      prepare.close();
+    } catch (SqlParseException | ValidationException
+        | RelConversionException | SQLException e) {
+      throw new RuntimeException(e);
+    }
+  }
+}
+
+// End SqlDdlNodes.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/server/src/main/java/org/apache/calcite/sql/ddl/SqlDropMaterializedView.java
----------------------------------------------------------------------
diff --git a/server/src/main/java/org/apache/calcite/sql/ddl/SqlDropMaterializedView.java b/server/src/main/java/org/apache/calcite/sql/ddl/SqlDropMaterializedView.java
new file mode 100644
index 0000000..14f68fd
--- /dev/null
+++ b/server/src/main/java/org/apache/calcite/sql/ddl/SqlDropMaterializedView.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.sql.ddl;
+
+import org.apache.calcite.jdbc.CalcitePrepare;
+import org.apache.calcite.jdbc.CalciteSchema;
+import org.apache.calcite.materialize.MaterializationKey;
+import org.apache.calcite.materialize.MaterializationService;
+import org.apache.calcite.schema.Table;
+import org.apache.calcite.schema.Wrapper;
+import org.apache.calcite.sql.SqlIdentifier;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.SqlOperator;
+import org.apache.calcite.sql.SqlSpecialOperator;
+import org.apache.calcite.sql.parser.SqlParserPos;
+import org.apache.calcite.util.Pair;
+
+/**
+ * Parse tree for {@code DROP MATERIALIZED VIEW} statement.
+ */
+public class SqlDropMaterializedView extends SqlDropObject {
+  private static final SqlOperator OPERATOR =
+      new SqlSpecialOperator("DROP MATERIALIZED VIEW",
+          SqlKind.DROP_MATERIALIZED_VIEW);
+
+  /** Creates a SqlDropMaterializedView. */
+  SqlDropMaterializedView(SqlParserPos pos, boolean ifExists,
+      SqlIdentifier name) {
+    super(OPERATOR, pos, ifExists, name);
+  }
+
+  @Override public void execute(CalcitePrepare.Context context) {
+    final Pair<CalciteSchema, String> pair =
+        SqlDdlNodes.schema(context, true, name);
+    final Table table = pair.left.plus().getTable(pair.right);
+    if (table != null) {
+      // Materialized view exists.
+      super.execute(context);
+      if (table instanceof Wrapper) {
+        final MaterializationKey materializationKey =
+            ((Wrapper) table).unwrap(MaterializationKey.class);
+        if (materializationKey != null) {
+          MaterializationService.instance()
+              .removeMaterialization(materializationKey);
+        }
+      }
+    }
+  }
+}
+
+// End SqlDropMaterializedView.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/server/src/main/java/org/apache/calcite/sql/ddl/SqlDropObject.java
----------------------------------------------------------------------
diff --git a/server/src/main/java/org/apache/calcite/sql/ddl/SqlDropObject.java b/server/src/main/java/org/apache/calcite/sql/ddl/SqlDropObject.java
new file mode 100644
index 0000000..81672f2
--- /dev/null
+++ b/server/src/main/java/org/apache/calcite/sql/ddl/SqlDropObject.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.sql.ddl;
+
+import org.apache.calcite.jdbc.CalcitePrepare;
+import org.apache.calcite.jdbc.CalciteSchema;
+import org.apache.calcite.sql.SqlDrop;
+import org.apache.calcite.sql.SqlExecutableStatement;
+import org.apache.calcite.sql.SqlIdentifier;
+import org.apache.calcite.sql.SqlNode;
+import org.apache.calcite.sql.SqlOperator;
+import org.apache.calcite.sql.SqlUtil;
+import org.apache.calcite.sql.SqlWriter;
+import org.apache.calcite.sql.parser.SqlParserPos;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+
+import static org.apache.calcite.util.Static.RESOURCE;
+
+/**
+ * Base class for parse trees of {@code DROP TABLE}, {@code DROP VIEW} and
+ * {@code DROP MATERIALIZED VIEW} statements.
+ */
+abstract class SqlDropObject extends SqlDrop
+    implements SqlExecutableStatement {
+  protected final SqlIdentifier name;
+
+  /** Creates a SqlDropObject. */
+  SqlDropObject(SqlOperator operator, SqlParserPos pos, boolean ifExists,
+      SqlIdentifier name) {
+    super(operator, pos, ifExists);
+    this.name = name;
+  }
+
+  public List<SqlNode> getOperandList() {
+    return ImmutableList.<SqlNode>of(name);
+  }
+
+  @Override public void unparse(SqlWriter writer, int leftPrec, int rightPrec) {
+    writer.keyword(getOperator().getName()); // "DROP TABLE" etc.
+    if (ifExists) {
+      writer.keyword("IF EXISTS");
+    }
+    name.unparse(writer, leftPrec, rightPrec);
+  }
+
+  public void execute(CalcitePrepare.Context context) {
+    final List<String> path = context.getDefaultSchemaPath();
+    CalciteSchema schema = context.getRootSchema();
+    for (String p : path) {
+      schema = schema.getSubSchema(p, true);
+    }
+    final boolean existed;
+    switch (getKind()) {
+    case DROP_TABLE:
+    case DROP_MATERIALIZED_VIEW:
+      existed = schema.removeTable(name.getSimple());
+      if (!existed && !ifExists) {
+        throw SqlUtil.newContextException(name.getParserPosition(),
+            RESOURCE.tableNotFound(name.getSimple()));
+      }
+      break;
+    case DROP_VIEW:
+      // Not quite right: removes any other functions with the same name
+      existed = schema.removeFunction(name.getSimple());
+      if (!existed && !ifExists) {
+        throw SqlUtil.newContextException(name.getParserPosition(),
+            RESOURCE.viewNotFound(name.getSimple()));
+      }
+      break;
+    default:
+      throw new AssertionError(getKind());
+    }
+  }
+}
+
+// End SqlDropObject.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/server/src/main/java/org/apache/calcite/sql/ddl/SqlDropSchema.java
----------------------------------------------------------------------
diff --git a/server/src/main/java/org/apache/calcite/sql/ddl/SqlDropSchema.java b/server/src/main/java/org/apache/calcite/sql/ddl/SqlDropSchema.java
new file mode 100644
index 0000000..074fb8b
--- /dev/null
+++ b/server/src/main/java/org/apache/calcite/sql/ddl/SqlDropSchema.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.sql.ddl;
+
+import org.apache.calcite.jdbc.CalcitePrepare;
+import org.apache.calcite.jdbc.CalciteSchema;
+import org.apache.calcite.sql.SqlDrop;
+import org.apache.calcite.sql.SqlExecutableStatement;
+import org.apache.calcite.sql.SqlIdentifier;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.SqlLiteral;
+import org.apache.calcite.sql.SqlNode;
+import org.apache.calcite.sql.SqlOperator;
+import org.apache.calcite.sql.SqlSpecialOperator;
+import org.apache.calcite.sql.SqlUtil;
+import org.apache.calcite.sql.SqlWriter;
+import org.apache.calcite.sql.parser.SqlParserPos;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+
+import static org.apache.calcite.util.Static.RESOURCE;
+
+/**
+ * Parse tree for {@code DROP TABLE} statement.
+ */
+public class SqlDropSchema extends SqlDrop
+    implements SqlExecutableStatement {
+  private final boolean foreign;
+  private final SqlIdentifier name;
+
+  private static final SqlOperator OPERATOR =
+      new SqlSpecialOperator("DROP SCHEMA", SqlKind.DROP_TABLE);
+
+  /** Creates a SqlDropSchema. */
+  SqlDropSchema(SqlParserPos pos, boolean foreign, boolean ifExists,
+      SqlIdentifier name) {
+    super(OPERATOR, pos, ifExists);
+    this.foreign = foreign;
+    this.name = name;
+  }
+
+  public List<SqlNode> getOperandList() {
+    return ImmutableList.of(
+        SqlLiteral.createBoolean(foreign, SqlParserPos.ZERO), name);
+  }
+
+  @Override public void unparse(SqlWriter writer, int leftPrec, int rightPrec) {
+    writer.keyword("DROP");
+    if (foreign) {
+      writer.keyword("FOREIGN");
+    }
+    writer.keyword("SCHEMA");
+    if (ifExists) {
+      writer.keyword("IF EXISTS");
+    }
+    name.unparse(writer, leftPrec, rightPrec);
+  }
+
+  public void execute(CalcitePrepare.Context context) {
+    final List<String> path = context.getDefaultSchemaPath();
+    CalciteSchema schema = context.getRootSchema();
+    for (String p : path) {
+      schema = schema.getSubSchema(p, true);
+    }
+    final boolean existed = schema.removeSubSchema(name.getSimple());
+    if (!existed && !ifExists) {
+      throw SqlUtil.newContextException(name.getParserPosition(),
+          RESOURCE.schemaNotFound(name.getSimple()));
+    }
+  }
+}
+
+// End SqlDropSchema.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/server/src/main/java/org/apache/calcite/sql/ddl/SqlDropTable.java
----------------------------------------------------------------------
diff --git a/server/src/main/java/org/apache/calcite/sql/ddl/SqlDropTable.java b/server/src/main/java/org/apache/calcite/sql/ddl/SqlDropTable.java
new file mode 100644
index 0000000..dda24e0
--- /dev/null
+++ b/server/src/main/java/org/apache/calcite/sql/ddl/SqlDropTable.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.sql.ddl;
+
+import org.apache.calcite.sql.SqlIdentifier;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.SqlOperator;
+import org.apache.calcite.sql.SqlSpecialOperator;
+import org.apache.calcite.sql.parser.SqlParserPos;
+
+/**
+ * Parse tree for {@code DROP TABLE} statement.
+ */
+public class SqlDropTable extends SqlDropObject {
+  private static final SqlOperator OPERATOR =
+      new SqlSpecialOperator("DROP TABLE", SqlKind.DROP_TABLE);
+
+  /** Creates a SqlDropTable. */
+  SqlDropTable(SqlParserPos pos, boolean ifExists, SqlIdentifier name) {
+    super(OPERATOR, pos, ifExists, name);
+  }
+}
+
+// End SqlDropTable.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/server/src/main/java/org/apache/calcite/sql/ddl/SqlDropView.java
----------------------------------------------------------------------
diff --git a/server/src/main/java/org/apache/calcite/sql/ddl/SqlDropView.java b/server/src/main/java/org/apache/calcite/sql/ddl/SqlDropView.java
new file mode 100644
index 0000000..091de18
--- /dev/null
+++ b/server/src/main/java/org/apache/calcite/sql/ddl/SqlDropView.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.sql.ddl;
+
+import org.apache.calcite.sql.SqlIdentifier;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.SqlOperator;
+import org.apache.calcite.sql.SqlSpecialOperator;
+import org.apache.calcite.sql.parser.SqlParserPos;
+
+/**
+ * Parse tree for {@code DROP VIEW} statement.
+ */
+public class SqlDropView extends SqlDropObject {
+  private static final SqlOperator OPERATOR =
+      new SqlSpecialOperator("DROP VIEW", SqlKind.DROP_VIEW);
+
+  /** Creates a SqlDropView. */
+  SqlDropView(SqlParserPos pos, boolean ifExists, SqlIdentifier name) {
+    super(OPERATOR, pos, ifExists, name);
+  }
+}
+
+// End SqlDropView.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/server/src/main/java/org/apache/calcite/sql/ddl/SqlKeyConstraint.java
----------------------------------------------------------------------
diff --git a/server/src/main/java/org/apache/calcite/sql/ddl/SqlKeyConstraint.java b/server/src/main/java/org/apache/calcite/sql/ddl/SqlKeyConstraint.java
new file mode 100644
index 0000000..6c75975
--- /dev/null
+++ b/server/src/main/java/org/apache/calcite/sql/ddl/SqlKeyConstraint.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.sql.ddl;
+
+import org.apache.calcite.sql.SqlCall;
+import org.apache.calcite.sql.SqlIdentifier;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.SqlNode;
+import org.apache.calcite.sql.SqlNodeList;
+import org.apache.calcite.sql.SqlOperator;
+import org.apache.calcite.sql.SqlSpecialOperator;
+import org.apache.calcite.sql.SqlWriter;
+import org.apache.calcite.sql.parser.SqlParserPos;
+import org.apache.calcite.util.ImmutableNullableList;
+
+import java.util.List;
+
+/**
+ * Parse tree for {@code UNIQUE}, {@code PRIMARY KEY} constraints.
+ *
+ * <p>And {@code FOREIGN KEY}, when we support it.
+ */
+public class SqlKeyConstraint extends SqlCall {
+  private static final SqlSpecialOperator UNIQUE =
+      new SqlSpecialOperator("UNIQUE", SqlKind.UNIQUE);
+
+  protected static final SqlSpecialOperator PRIMARY =
+      new SqlSpecialOperator("PRIMARY KEY", SqlKind.PRIMARY_KEY);
+
+  private final SqlIdentifier name;
+  private final SqlNodeList columnList;
+
+  /** Creates a SqlKeyConstraint. */
+  SqlKeyConstraint(SqlParserPos pos, SqlIdentifier name,
+      SqlNodeList columnList) {
+    super(pos);
+    this.name = name;
+    this.columnList = columnList;
+  }
+
+  /** Creates a UNIQUE constraint. */
+  public static SqlKeyConstraint unique(SqlParserPos pos, SqlIdentifier name,
+      SqlNodeList columnList) {
+    return new SqlKeyConstraint(pos, name, columnList);
+  }
+
+  /** Creates a PRIMARY KEY constraint. */
+  public static SqlKeyConstraint primary(SqlParserPos pos, SqlIdentifier name,
+      SqlNodeList columnList) {
+    return new SqlKeyConstraint(pos, name, columnList) {
+      @Override public SqlOperator getOperator() {
+        return PRIMARY;
+      }
+    };
+  }
+
+  @Override public SqlOperator getOperator() {
+    return UNIQUE;
+  }
+
+  @Override public List<SqlNode> getOperandList() {
+    return ImmutableNullableList.of(name, columnList);
+  }
+
+  @Override public void unparse(SqlWriter writer, int leftPrec, int rightPrec) {
+    if (name != null) {
+      writer.keyword("CONSTRAINT");
+      name.unparse(writer, 0, 0);
+    }
+    writer.keyword(getOperator().getName()); // "UNIQUE" or "PRIMARY KEY"
+    columnList.unparse(writer, 1, 1);
+  }
+}
+
+// End SqlKeyConstraint.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/server/src/main/java/org/apache/calcite/sql/ddl/package-info.java
----------------------------------------------------------------------
diff --git a/server/src/main/java/org/apache/calcite/sql/ddl/package-info.java b/server/src/main/java/org/apache/calcite/sql/ddl/package-info.java
new file mode 100644
index 0000000..89ff42f
--- /dev/null
+++ b/server/src/main/java/org/apache/calcite/sql/ddl/package-info.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Parse tree for SQL DDL statements.
+ *
+ * <p>These are available in the extended SQL parser that is part of Calcite's
+ * "server" module; the core parser in the "core" module only supports SELECT
+ * and DML.
+ *
+ * <p>If you are writing a project that requires DDL it is likely that your
+ * DDL syntax is different than ours. We recommend that you copy-paste this
+ * the parser and its supporting classes into your own module, rather than try
+ * to extend this one.
+ */
+@PackageMarker
+package org.apache.calcite.sql.ddl;
+
+import org.apache.calcite.avatica.util.PackageMarker;
+
+// End package-info.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/server/src/test/java/org/apache/calcite/test/ServerParserTest.java
----------------------------------------------------------------------
diff --git a/server/src/test/java/org/apache/calcite/test/ServerParserTest.java b/server/src/test/java/org/apache/calcite/test/ServerParserTest.java
new file mode 100644
index 0000000..8cdf336
--- /dev/null
+++ b/server/src/test/java/org/apache/calcite/test/ServerParserTest.java
@@ -0,0 +1,245 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.test;
+
+import org.apache.calcite.sql.parser.SqlParserImplFactory;
+import org.apache.calcite.sql.parser.SqlParserTest;
+import org.apache.calcite.sql.parser.ddl.SqlDdlParserImpl;
+
+import org.junit.Test;
+
+/**
+ * Tests SQL parser extensions for DDL.
+ *
+ * <p>Remaining tasks:
+ * <ul>
+ *
+ * <li>"create table x (a int) as values 1, 2" should fail validation;
+ * data type not allowed in "create table ... as".
+ *
+ * <li>"create table x (a int, b int as (a + 1)) stored"
+ * should not allow b to be specified in insert;
+ * should generate check constraint on b;
+ * should populate b in insert as if it had a default
+ *
+ * <li>"create table as select" should store constraints
+ * deduced by planner
+ *
+ * <li>during CREATE VIEW, check for a table and a materialized view
+ * with the same name (they have the same namespace)
+ *
+ * </ul>
+ */
+public class ServerParserTest extends SqlParserTest {
+
+  @Override protected SqlParserImplFactory parserImplFactory() {
+    return SqlDdlParserImpl.FACTORY;
+  }
+
+  @Override public void testGenerateKeyWords() {
+    // by design, method only works in base class; no-ops in this sub-class
+  }
+
+  @Test public void testCreateSchema() {
+    sql("create schema x")
+        .ok("CREATE SCHEMA `X`");
+  }
+
+  @Test public void testCreateOrReplaceSchema() {
+    sql("create or replace schema x")
+        .ok("CREATE OR REPLACE SCHEMA `X`");
+  }
+
+  @Test public void testCreateForeignSchema() {
+    final String sql = "create or replace foreign schema x\n"
+        + "type 'jdbc'\n"
+        + "options (\n"
+        + "  aBoolean true,\n"
+        + "  anInteger -45,\n"
+        + "  aDate DATE '1970-03-21',\n"
+        + "  \"quoted.id\" TIMESTAMP '1970-03-21 12:4:56.78',\n"
+        + "  aString 'foo''bar')";
+    final String expected = "CREATE OR REPLACE FOREIGN SCHEMA `X` TYPE 'jdbc' "
+        + "OPTIONS (`ABOOLEAN` TRUE,"
+        + " `ANINTEGER` -45,"
+        + " `ADATE` DATE '1970-03-21',"
+        + " `quoted.id` TIMESTAMP '1970-03-21 12:04:56.78',"
+        + " `ASTRING` 'foo''bar')";
+    sql(sql).ok(expected);
+  }
+
+  @Test public void testCreateForeignSchema2() {
+    final String sql = "create or replace foreign schema x\n"
+        + "library 'com.example.ExampleSchemaFactory'\n"
+        + "options ()";
+    final String expected = "CREATE OR REPLACE FOREIGN SCHEMA `X` "
+        + "LIBRARY 'com.example.ExampleSchemaFactory' "
+        + "OPTIONS ()";
+    sql(sql).ok(expected);
+  }
+
+  @Test public void testCreateTable() {
+    sql("create table x (i int not null, j varchar(5) null)")
+        .ok("CREATE TABLE `X` (`I` INTEGER NOT NULL, `J` VARCHAR(5))");
+  }
+
+  @Test public void testCreateTableAsSelect() {
+    final String expected = "CREATE TABLE `X` AS\n"
+        + "SELECT *\n"
+        + "FROM `EMP`";
+    sql("create table x as select * from emp")
+        .ok(expected);
+  }
+
+  @Test public void testCreateTableIfNotExistsAsSelect() {
+    final String expected = "CREATE TABLE IF NOT EXISTS `X`.`Y` AS\n"
+        + "SELECT *\n"
+        + "FROM `EMP`";
+    sql("create table if not exists x.y as select * from emp")
+        .ok(expected);
+  }
+
+  @Test public void testCreateTableAsValues() {
+    final String expected = "CREATE TABLE `X` AS\n"
+        + "VALUES (ROW(1)),\n"
+        + "(ROW(2))";
+    sql("create table x as values 1, 2")
+        .ok(expected);
+  }
+
+  @Test public void testCreateTableAsSelectColumnList() {
+    final String expected = "CREATE TABLE `X` (`A`, `B`) AS\n"
+        + "SELECT *\n"
+        + "FROM `EMP`";
+    sql("create table x (a, b) as select * from emp")
+        .ok(expected);
+  }
+
+  @Test public void testCreateTableCheck() {
+    final String expected = "CREATE TABLE `X` (`I` INTEGER NOT NULL,"
+        + " CONSTRAINT `C1` CHECK (`I` < 10), `J` INTEGER)";
+    sql("create table x (i int not null, constraint c1 check (i < 10), j int)")
+        .ok(expected);
+  }
+
+  @Test public void testCreateTableVirtualColumn() {
+    final String sql = "create table if not exists x (\n"
+        + " i int not null,\n"
+        + " j int generated always as (i + 1) stored,\n"
+        + " k int as (j + 1) virtual,\n"
+        + " m int as (k + 1))";
+    final String expected = "CREATE TABLE IF NOT EXISTS `X` "
+        + "(`I` INTEGER NOT NULL,"
+        + " `J` INTEGER AS (`I` + 1) STORED,"
+        + " `K` INTEGER AS (`J` + 1) VIRTUAL,"
+        + " `M` INTEGER AS (`K` + 1) VIRTUAL)";
+    sql(sql).ok(expected);
+  }
+
+  @Test public void testCreateView() {
+    final String sql = "create or replace view v as\n"
+        + "select * from (values (1, '2'), (3, '45')) as t (x, y)";
+    final String expected = "CREATE OR REPLACE VIEW `V` AS\n"
+        + "SELECT *\n"
+        + "FROM (VALUES (ROW(1, '2')),\n"
+        + "(ROW(3, '45'))) AS `T` (`X`, `Y`)";
+    sql(sql).ok(expected);
+  }
+
+  @Test public void testCreateMaterializedView() {
+    final String sql = "create materialized view mv (d, v) as\n"
+        + "select deptno, count(*) from emp\n"
+        + "group by deptno order by deptno desc";
+    final String expected = "CREATE MATERIALIZED VIEW `MV` (`D`, `V`) AS\n"
+        + "SELECT `DEPTNO`, COUNT(*)\n"
+        + "FROM `EMP`\n"
+        + "GROUP BY `DEPTNO`\n"
+        + "ORDER BY `DEPTNO` DESC";
+    sql(sql).ok(expected);
+  }
+
+  @Test public void testCreateMaterializedView2() {
+    final String sql = "create materialized view if not exists mv as\n"
+        + "select deptno, count(*) from emp\n"
+        + "group by deptno order by deptno desc";
+    final String expected = "CREATE MATERIALIZED VIEW IF NOT EXISTS `MV` AS\n"
+        + "SELECT `DEPTNO`, COUNT(*)\n"
+        + "FROM `EMP`\n"
+        + "GROUP BY `DEPTNO`\n"
+        + "ORDER BY `DEPTNO` DESC";
+    sql(sql).ok(expected);
+  }
+
+  // "OR REPLACE" is allowed by the parser, but the validator will give an
+  // error later
+  @Test public void testCreateOrReplaceMaterializedView() {
+    final String sql = "create or replace materialized view mv as\n"
+        + "select * from emp";
+    final String expected = "CREATE MATERIALIZED VIEW `MV` AS\n"
+        + "SELECT *\n"
+        + "FROM `EMP`";
+    sql(sql).ok(expected);
+  }
+
+  @Test public void testDropSchema() {
+    sql("drop schema x")
+        .ok("DROP SCHEMA `X`");
+  }
+
+  @Test public void testDropSchemaIfExists() {
+    sql("drop schema if exists x")
+        .ok("DROP SCHEMA IF EXISTS `X`");
+  }
+
+  @Test public void testDropForeignSchema() {
+    sql("drop foreign schema x")
+        .ok("DROP FOREIGN SCHEMA `X`");
+  }
+
+  @Test public void testDropTable() {
+    sql("drop table x")
+        .ok("DROP TABLE `X`");
+  }
+
+  @Test public void testDropTableComposite() {
+    sql("drop table x.y")
+        .ok("DROP TABLE `X`.`Y`");
+  }
+
+  @Test public void testDropTableIfExists() {
+    sql("drop table if exists x")
+        .ok("DROP TABLE IF EXISTS `X`");
+  }
+
+  @Test public void testDropView() {
+    sql("drop view x")
+        .ok("DROP VIEW `X`");
+  }
+
+  @Test public void testDropMaterializedView() {
+    sql("drop materialized view x")
+        .ok("DROP MATERIALIZED VIEW `X`");
+  }
+
+  @Test public void testDropMaterializedViewIfExists() {
+    sql("drop materialized view if exists x")
+        .ok("DROP MATERIALIZED VIEW IF EXISTS `X`");
+  }
+
+}
+
+// End ServerParserTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/server/src/test/java/org/apache/calcite/test/ServerQuidemTest.java
----------------------------------------------------------------------
diff --git a/server/src/test/java/org/apache/calcite/test/ServerQuidemTest.java b/server/src/test/java/org/apache/calcite/test/ServerQuidemTest.java
new file mode 100644
index 0000000..9e2efc3
--- /dev/null
+++ b/server/src/test/java/org/apache/calcite/test/ServerQuidemTest.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.test;
+
+import org.apache.calcite.materialize.MaterializationService;
+
+import net.hydromatic.quidem.Quidem;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.sql.Connection;
+import java.util.Collection;
+
+/**
+ * Unit tests for server and DDL.
+ */
+@RunWith(Parameterized.class)
+public class ServerQuidemTest extends QuidemTest {
+  /** Creates a ServerQuidemTest. Public per {@link Parameterized}. */
+  @SuppressWarnings("WeakerAccess")
+  public ServerQuidemTest(String path) {
+    super(path);
+  }
+
+  /** Runs a test from the command line.
+   *
+   * <p>For example:
+   *
+   * <blockquote>
+   *   <code>java ServerQuidemTest sql/table.iq</code>
+   * </blockquote> */
+  public static void main(String[] args) throws Exception {
+    for (String arg : args) {
+      new ServerQuidemTest(arg).test();
+    }
+  }
+
+  @Override @Test public void test() throws Exception {
+    MaterializationService.setThreadLocal();
+    super.test();
+  }
+
+  /** For {@link Parameterized} runner. */
+  @Parameterized.Parameters(name = "{index}: quidem({0})")
+  public static Collection<Object[]> data() {
+    // Start with a test file we know exists, then find the directory and list
+    // its files.
+    final String first = "sql/table.iq";
+    return data(first);
+  }
+
+  @Override protected Quidem.ConnectionFactory createConnectionFactory() {
+    return new QuidemConnectionFactory() {
+      @Override public Connection connect(String name, boolean reference)
+          throws Exception {
+        switch (name) {
+        case "server":
+          return ServerTest.connect();
+        }
+        return super.connect(name, reference);
+      }
+    };
+  }
+}
+
+// End ServerQuidemTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/server/src/test/java/org/apache/calcite/test/ServerTest.java
----------------------------------------------------------------------
diff --git a/server/src/test/java/org/apache/calcite/test/ServerTest.java b/server/src/test/java/org/apache/calcite/test/ServerTest.java
new file mode 100644
index 0000000..3a97412
--- /dev/null
+++ b/server/src/test/java/org/apache/calcite/test/ServerTest.java
@@ -0,0 +1,274 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.test;
+
+import org.apache.calcite.config.CalciteConnectionProperty;
+import org.apache.calcite.sql.parser.ddl.SqlDdlParserImpl;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.fail;
+
+/**
+ * Unit tests for server and DDL.
+ */
+public class ServerTest {
+
+  static final String URL = "jdbc:calcite:";
+
+  static Connection connect() throws SQLException {
+    return DriverManager.getConnection(URL,
+        CalciteAssert.propBuilder()
+            .set(CalciteConnectionProperty.PARSER_FACTORY,
+                SqlDdlParserImpl.class.getName() + "#FACTORY")
+            .set(CalciteConnectionProperty.MATERIALIZATIONS_ENABLED,
+                "true")
+            .build());
+  }
+
+  @Test public void testStatement() throws Exception {
+    try (Connection c = connect();
+         Statement s = c.createStatement();
+         ResultSet r = s.executeQuery("values 1, 2")) {
+      assertThat(r.next(), is(true));
+      assertThat(r.getString(1), notNullValue());
+      assertThat(r.next(), is(true));
+      assertThat(r.next(), is(false));
+    }
+  }
+
+  @Test public void testCreateSchema() throws Exception {
+    try (Connection c = connect();
+         Statement s = c.createStatement()) {
+      boolean b = s.execute("create schema s");
+      assertThat(b, is(false));
+      b = s.execute("create table s.t (i int not null)");
+      assertThat(b, is(false));
+      int x = s.executeUpdate("insert into s.t values 1");
+      assertThat(x, is(1));
+      try (ResultSet r = s.executeQuery("select count(*) from s.t")) {
+        assertThat(r.next(), is(true));
+        assertThat(r.getInt(1), is(1));
+        assertThat(r.next(), is(false));
+      }
+    }
+  }
+
+  @Test public void testCreateTable() throws Exception {
+    try (Connection c = connect();
+         Statement s = c.createStatement()) {
+      boolean b = s.execute("create table t (i int not null)");
+      assertThat(b, is(false));
+      int x = s.executeUpdate("insert into t values 1");
+      assertThat(x, is(1));
+      x = s.executeUpdate("insert into t values 3");
+      assertThat(x, is(1));
+      try (ResultSet r = s.executeQuery("select sum(i) from t")) {
+        assertThat(r.next(), is(true));
+        assertThat(r.getInt(1), is(4));
+        assertThat(r.next(), is(false));
+      }
+    }
+  }
+
+  @Test public void testStoredGeneratedColumn() throws Exception {
+    try (Connection c = connect();
+         Statement s = c.createStatement()) {
+      final String sql0 = "create table t (\n"
+          + " h int not null,\n"
+          + " i int,\n"
+          + " j int as (i + 1) stored)";
+      boolean b = s.execute(sql0);
+      assertThat(b, is(false));
+
+      int x;
+
+      // A successful row.
+      x = s.executeUpdate("insert into t (h, i) values (3, 4)");
+      assertThat(x, is(1));
+
+      final String sql1 = "explain plan for\n"
+          + "insert into t (h, i) values (3, 4)";
+      try (ResultSet r = s.executeQuery(sql1)) {
+        assertThat(r.next(), is(true));
+        final String plan = ""
+            + "EnumerableTableModify(table=[[T]], operation=[INSERT], flattened=[false])\n"
+            + "  EnumerableCalc(expr#0..1=[{inputs}], expr#2=[1], expr#3=[+($t1, $t2)], proj#0..1=[{exprs}], J=[$t3])\n"
+            + "    EnumerableValues(tuples=[[{ 3, 4 }]])\n";
+        assertThat(r.getString(1), is(plan));
+        assertThat(r.next(), is(false));
+      }
+
+      try (ResultSet r = s.executeQuery("select * from t")) {
+        assertThat(r.next(), is(true));
+        assertThat(r.getInt("H"), is(3));
+        assertThat(r.wasNull(), is(false));
+        assertThat(r.getInt("I"), is(4));
+        assertThat(r.getInt("J"), is(5)); // j = i + 1
+        assertThat(r.next(), is(false));
+      }
+
+      // No target column list; too few values provided
+      try {
+        x = s.executeUpdate("insert into t values (2, 3)");
+        fail("expected error, got " + x);
+      } catch (SQLException e) {
+        assertThat(e.getMessage(),
+            containsString("Number of INSERT target columns (3) does not equal "
+                + "number of source items (2)"));
+      }
+
+      // No target column list; too many values provided
+      try {
+        x = s.executeUpdate("insert into t values (3, 4, 5, 6)");
+        fail("expected error, got " + x);
+      } catch (SQLException e) {
+        assertThat(e.getMessage(),
+            containsString("Number of INSERT target columns (3) does not equal "
+                + "number of source items (4)"));
+      }
+
+      // No target column list;
+      // source count = target count;
+      // but one of the target columns is virtual.
+      try {
+        x = s.executeUpdate("insert into t values (3, 4, 5)");
+        fail("expected error, got " + x);
+      } catch (SQLException e) {
+        assertThat(e.getMessage(),
+            containsString("Cannot INSERT into generated column 'J'"));
+      }
+
+      // Explicit target column list, omits virtual column
+      x = s.executeUpdate("insert into t (h, i) values (1, 2)");
+      assertThat(x, is(1));
+
+      // Explicit target column list, includes virtual column but assigns
+      // DEFAULT.
+      x = s.executeUpdate("insert into t (h, i, j) values (1, 2, DEFAULT)");
+      assertThat(x, is(1));
+
+      // As previous, re-order columns.
+      x = s.executeUpdate("insert into t (h, j, i) values (1, DEFAULT, 3)");
+      assertThat(x, is(1));
+
+      // Target column list exists,
+      // target column count equals the number of non-virtual columns;
+      // but one of the target columns is virtual.
+      try {
+        x = s.executeUpdate("insert into t (h, j) values (1, 3)");
+        fail("expected error, got " + x);
+      } catch (SQLException e) {
+        assertThat(e.getMessage(),
+            containsString("Cannot INSERT into generated column 'J'"));
+      }
+
+      // Target column list exists and contains all columns,
+      // expression for virtual column is not DEFAULT.
+      try {
+        x = s.executeUpdate("insert into t (h, i, j) values (2, 3, 3 + 1)");
+        fail("expected error, got " + x);
+      } catch (SQLException e) {
+        assertThat(e.getMessage(),
+            containsString("Cannot INSERT into generated column 'J'"));
+      }
+      x = s.executeUpdate("insert into t (h, i) values (0, 1)");
+      assertThat(x, is(1));
+      x = s.executeUpdate("insert into t (h, i, j) values (0, 1, DEFAULT)");
+      assertThat(x, is(1));
+      x = s.executeUpdate("insert into t (j, i, h) values (DEFAULT, NULL, 7)");
+      assertThat(x, is(1));
+      x = s.executeUpdate("insert into t (h, i) values (6, 5), (7, 4)");
+      assertThat(x, is(2));
+      try (ResultSet r = s.executeQuery("select sum(i), count(*) from t")) {
+        assertThat(r.next(), is(true));
+        assertThat(r.getInt(1), is(19));
+        assertThat(r.getInt(2), is(9));
+        assertThat(r.next(), is(false));
+      }
+    }
+  }
+
+  @Ignore("not working yet")
+  @Test public void testStoredGeneratedColumn2() throws Exception {
+    try (Connection c = connect();
+         Statement s = c.createStatement()) {
+      final String sql = "create table t (\n"
+          + " h int not null,\n"
+          + " i int,\n"
+          + " j int as (i + 1) stored)";
+      boolean b = s.execute(sql);
+      assertThat(b, is(false));
+
+      // Planner uses constraint to optimize away condition.
+      final String sql2 = "explain plan for\n"
+          + "select * from t where j = i + 1";
+      final String plan = "EnumerableTableScan(table=[[T]])\n";
+      try (ResultSet r = s.executeQuery(sql2)) {
+        assertThat(r.next(), is(true));
+        assertThat(r.getString(1), is(plan));
+        assertThat(r.next(), is(false));
+      }
+    }
+  }
+
+  @Test public void testVirtualColumn() throws Exception {
+    try (Connection c = connect();
+         Statement s = c.createStatement()) {
+      final String sql0 = "create table t (\n"
+          + " h int not null,\n"
+          + " i int,\n"
+          + " j int as (i + 1) virtual)";
+      boolean b = s.execute(sql0);
+      assertThat(b, is(false));
+
+      int x = s.executeUpdate("insert into t (h, i) values (1, 2)");
+      assertThat(x, is(1));
+
+      // In plan, "j" is replaced by "i + 1".
+      final String sql = "select * from t";
+      try (ResultSet r = s.executeQuery(sql)) {
+        assertThat(r.next(), is(true));
+        assertThat(r.getInt(1), is(1));
+        assertThat(r.getInt(2), is(2));
+        assertThat(r.getInt(3), is(3));
+        assertThat(r.next(), is(false));
+      }
+
+      final String plan = ""
+          + "EnumerableCalc(expr#0..1=[{inputs}], expr#2=[1], expr#3=[+($t1, $t2)], proj#0..1=[{exprs}], $f2=[$t3])\n"
+          + "  EnumerableTableScan(table=[[T]])\n";
+      try (ResultSet r = s.executeQuery("explain plan for " + sql)) {
+        assertThat(r.next(), is(true));
+        assertThat(r.getString(1), is(plan));
+      }
+    }
+  }
+}
+
+// End ServerTest.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/server/src/test/resources/sql/materialized_view.iq
----------------------------------------------------------------------
diff --git a/server/src/test/resources/sql/materialized_view.iq b/server/src/test/resources/sql/materialized_view.iq
new file mode 100644
index 0000000..e587313
--- /dev/null
+++ b/server/src/test/resources/sql/materialized_view.iq
@@ -0,0 +1,272 @@
+# materialized_view.iq - Materialized view DDL
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to you under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+!use server
+!set outputformat mysql
+
+# Create a source table
+create table dept (deptno int not null, name varchar(10));
+(0 rows modified)
+
+!update
+
+insert into dept
+values (10, 'Sales'), (20, 'Marketing'), (30, 'Engineering');
+(3 rows modified)
+
+!update
+
+# Create as select
+create materialized view v as
+select * from dept where deptno > 10;
+(0 rows modified)
+
+!update
+
+# Check contents
+select * from v;
++--------+-------------+
+| DEPTNO | NAME        |
++--------+-------------+
+|     20 | Marketing   |
+|     30 | Engineering |
++--------+-------------+
+(2 rows)
+
+!ok
+
+# Try to create again - fails
+create materialized view v as
+select * from dept where deptno < 30;
+Table 'V' already exists
+!error
+
+# Try to create again - fails silently
+create materialized view if not exists v as
+select * from dept where deptno < 30;
+(0 rows modified)
+
+!update
+
+# Check contents are unchanged
+select * from v;
++--------+-------------+
+| DEPTNO | NAME        |
++--------+-------------+
+|     20 | Marketing   |
+|     30 | Engineering |
++--------+-------------+
+(2 rows)
+
+!ok
+
+# Drop
+drop materialized view if exists v;
+(0 rows modified)
+
+!update
+
+# It's gone
+select * from v;
+Object 'V' not found
+!error
+
+# Drop does nothing because materialized view does not exist
+drop materialized view if exists v;
+(0 rows modified)
+
+!update
+
+# Create materialized view without AS - fails
+create materialized view d;
+Encountered "<EOF>" at line 1, column 27.
+!error
+
+# Create materialized view without AS - fails
+create materialized view d (x, y);
+Encountered "<EOF>" at line 1, column 34.
+!error
+
+# Create materialized view without AS - fails
+create materialized view d (x int, y);
+Encountered "int" at line 1, column 31.
+!error
+
+# Create based on itself - fails
+create materialized view d2 as select * from d2;
+Object 'D2' not found
+!error
+
+# Create materialized view based on UNION
+create materialized view d3 as
+select deptno as dd from dept where deptno < 15
+union all
+select deptno as ee from dept where deptno > 25;
+(0 rows modified)
+
+!update
+
+# Check contents
+select * from d3;
++----+
+| DD |
++----+
+| 10 |
+| 30 |
++----+
+(2 rows)
+
+!ok
+
+# Drop
+drop materialized view d3;
+(0 rows modified)
+
+!update
+
+# Create materialized view based on UNION and ORDER BY
+create materialized view d4 as
+select deptno as dd from dept where deptno < 15
+union all
+select deptno as dd from dept where deptno > 25
+order by 1 desc;
+(0 rows modified)
+
+!update
+
+# Check contents
+select * from d4;
++----+
+| DD |
++----+
+| 10 |
+| 30 |
++----+
+(2 rows)
+
+!ok
+
+# Drop
+drop materialized view d4;
+
+# Create materialized view based on VALUES
+create materialized view d5 as
+values (1, 'a'), (2, 'b');
+(0 rows modified)
+
+!update
+
+# Check contents
+select * from d5;
++--------+--------+
+| EXPR$0 | EXPR$1 |
++--------+--------+
+|      1 | a      |
+|      2 | b      |
++--------+--------+
+(2 rows)
+
+!ok
+
+# Use just aliases
+create materialized view d6 (x, y) as
+select * from dept where deptno < 15;
+(0 rows modified)
+
+!update
+
+# Check contents
+select * from d6;
++----+-------+
+| X  | Y     |
++----+-------+
+| 10 | Sales |
++----+-------+
+(1 row)
+
+!ok
+
+# Use a mixture of aliases and column declarations - fails
+create materialized view d7 (x int, y) as
+select * from dept where deptno < 15;
+Encountered "int" at line 1, column 32.
+!error
+
+# Too many columns
+create materialized view d8 (x, y, z) as
+select * from dept where deptno < 15;
+List of column aliases must have same degree as table; table has 2 columns ('DEPTNO', 'NAME'), whereas alias list has 3 columns
+!error
+
+# Too few columns
+create materialized view d9 (x) as
+select * from dept where deptno < 15;
+List of column aliases must have same degree as table; table has 2 columns ('DEPTNO', 'NAME'), whereas alias list has 1 columns
+!error
+
+create schema s;
+(0 rows modified)
+
+!update
+
+# Materialized view in explicit schema
+create materialized view s.d10 (x, y) as
+select * from dept where deptno < 25;
+(0 rows modified)
+
+!update
+
+# Check contents
+select * from s.d10;
++----+-----------+
+| X  | Y         |
++----+-----------+
+| 10 | Sales     |
+| 20 | Marketing |
++----+-----------+
+(2 rows)
+
+!ok
+
+# Appears in catalog, with table type 'MATERIALIZED VIEW'
+# (Materialized views in root schema should also, but currently do not.)
+select * from "metadata".TABLES;
++----------+------------+-----------+-------------------+---------+---------+-----------+----------+------------------------+---------------+
+| tableCat | tableSchem | tableName | tableType         | remarks | typeCat | typeSchem | typeName | selfReferencingColName | refGeneration |
++----------+------------+-----------+-------------------+---------+---------+-----------+----------+------------------------+---------------+
+|          | S          | D10       | MATERIALIZED VIEW |         |         |           |          |                        |               |
+|          | metadata   | COLUMNS   | SYSTEM TABLE      |         |         |           |          |                        |               |
+|          | metadata   | TABLES    | SYSTEM TABLE      |         |         |           |          |                        |               |
++----------+------------+-----------+-------------------+---------+---------+-----------+----------+------------------------+---------------+
+(3 rows)
+
+!ok
+
+# Check that exact match materialized view is used
+select * from dept where deptno < 15;
+EnumerableTableScan(table=[[D6]])
+!plan
++--------+-------+
+| DEPTNO | NAME  |
++--------+-------+
+|     10 | Sales |
++--------+-------+
+(1 row)
+
+!ok
+
+# End materialized_view.iq

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/server/src/test/resources/sql/schema.iq
----------------------------------------------------------------------
diff --git a/server/src/test/resources/sql/schema.iq b/server/src/test/resources/sql/schema.iq
new file mode 100755
index 0000000..65c6396
--- /dev/null
+++ b/server/src/test/resources/sql/schema.iq
@@ -0,0 +1,168 @@
+# schema.iq - DDL on schemas
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to you under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+!use server
+!set outputformat mysql
+
+# Create a schema
+create schema s;
+(0 rows modified)
+
+!update
+
+# Create a table and a view in the schema
+create table s.t (i int);
+(0 rows modified)
+
+!update
+
+create view s.v as select * from s.t;
+(0 rows modified)
+
+!update
+
+select count(*) as c from s.v;
++---+
+| C |
++---+
+| 0 |
++---+
+(1 row)
+
+!ok
+
+# Try to create a schema that already exists
+create schema s;
+Schema 'S' already exists
+!error
+
+create or replace schema s;
+(0 rows modified)
+
+!update
+
+create schema if exists s;
+Encountered "exists" at line 1, column 18.
+!error
+
+create schema if not exists s;
+(0 rows modified)
+
+!update
+
+# Bad library
+create foreign schema fs library 'com.example.BadSchemaFactory';
+Property 'com.example.BadSchemaFactory' not valid for plugin type org.apache.calcite.schema.SchemaFactory
+!error
+
+# Bad type
+create foreign schema fs type 'bad';
+Invalid schema type 'bad'; valid values: [MAP, JDBC, CUSTOM]
+!error
+
+# Can not specify both type and library
+create foreign schema fs
+  type 'jdbc'
+  library 'org.apache.calcite.test.JdbcTest.MySchemaFactory';
+Encountered "library" at line 3, column 3.
+!error
+
+# Cannot specify type or library with non-foreign schema
+create schema fs type 'jdbc';
+Encountered "type" at line 1, column 18.
+!error
+
+create schema fs library 'org.apache.calcite.test.JdbcTest.MySchemaFactory';
+Encountered "library" at line 1, column 18.
+!error
+
+create foreign schema fs;
+Encountered "<EOF>" at line 1, column 25.
+Was expecting one of:
+    "TYPE" ...
+    "LIBRARY" ...
+    "." ...
+!error
+
+# JDBC schema
+create foreign schema scott type 'jdbc' options (
+  "jdbcUrl" 'jdbc:hsqldb:res:scott',
+  "jdbcSchema" 'SCOTT',
+  "jdbcUser" 'SCOTT',
+  "jdbcPassword" 'TIGER');
+(0 rows modified)
+
+!update
+
+select count(*) as c from scott.dept;
++---+
+| C |
++---+
+| 4 |
++---+
+(1 row)
+
+!ok
+
+# Drop schema, then make sure that a query can't find it
+drop schema if exists s;
+(0 rows modified)
+
+!update
+
+select * from s.t;
+Object 'T' not found
+!error
+
+# Create again and objects are still gone
+create schema s;
+
+select * from s.t;
+Object 'T' not found
+!error
+
+select * from s.v;
+Object 'V' not found
+!error
+
+# Try to drop schema that does not exist
+drop schema sss;
+Schema 'SSS' not found
+!error
+
+drop schema if exists sss;
+(0 rows modified)
+
+!update
+
+drop foreign schema if exists sss;
+(0 rows modified)
+
+!update
+
+# Use 'if exists' to drop a foreign schema that does exist
+drop foreign schema if exists scott;
+(0 rows modified)
+
+!update
+
+drop foreign schema if exists scott;
+(0 rows modified)
+
+!update
+
+# End schema.iq

http://git-wip-us.apache.org/repos/asf/calcite/blob/238b3225/server/src/test/resources/sql/table.iq
----------------------------------------------------------------------
diff --git a/server/src/test/resources/sql/table.iq b/server/src/test/resources/sql/table.iq
new file mode 100755
index 0000000..ed89e0f
--- /dev/null
+++ b/server/src/test/resources/sql/table.iq
@@ -0,0 +1,154 @@
+# table.iq - Table DDL
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to you under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+!use server
+!set outputformat mysql
+
+# Create a basic table
+create table t (i int, j int not null);
+(0 rows modified)
+
+!update
+
+create table if not exists t (i int, j int not null, k date);
+(0 rows modified)
+
+!update
+
+# There is no "K" column, because table was not re-created
+select * from t;
+I INTEGER(10)
+J INTEGER(10) NOT NULL
+!type
+
+insert into t values (1, 2);
+(1 row modified)
+
+!update
+
+select * from t;
++---+---+
+| I | J |
++---+---+
+| 1 | 2 |
++---+---+
+(1 row)
+
+!ok
+
+drop table t;
+(0 rows modified)
+
+!update
+
+# Create a table with a DEFAULT column
+create table t (i int, j int default i + 2);
+(0 rows modified)
+
+!update
+
+insert into t values (1, 2);
+(1 row modified)
+
+!update
+
+insert into t (i) values (3);
+(1 row modified)
+
+!update
+
+select * from t;
++---+---+
+| I | J |
++---+---+
+| 1 | 2 |
+| 3 | 5 |
++---+---+
+(2 rows)
+
+!ok
+
+drop table t;
+(0 rows modified)
+
+!update
+
+# Create a table with a VIRTUAL column
+
+create table t (i int, j int as (i + k + 2) virtual, k int);
+(0 rows modified)
+
+!update
+
+insert into t values (1, 2, 3);
+Cannot INSERT into generated column 'J'
+!error
+
+insert into t (i, j) values (1, 2);
+Cannot INSERT into generated column 'J'
+!error
+
+insert into t (i, k) values (1, 3);
+(1 row modified)
+
+!update
+EnumerableTableModify(table=[[T]], operation=[INSERT], flattened=[false])
+  EnumerableValues(tuples=[[{ 1, 3 }]])
+!plan
+
+insert into t (k, i) values (5, 2);
+(1 row modified)
+
+!update
+EnumerableTableModify(table=[[T]], operation=[INSERT], flattened=[false])
+  EnumerableCalc(expr#0..1=[{inputs}], I=[$t1], K=[$t0])
+    EnumerableValues(tuples=[[{ 5, 2 }]])
+!plan
+
+select * from t;
++---+---+---+
+| I | J | K |
++---+---+---+
+| 1 | 6 | 3 |
+| 2 | 9 | 5 |
++---+---+---+
+(2 rows)
+
+!ok
+EnumerableCalc(expr#0..1=[{inputs}], expr#2=[+($t0, $t1)], expr#3=[2], expr#4=[+($t2, $t3)], I=[$t0], $f1=[$t4], K=[$t1])
+  EnumerableTableScan(table=[[T]])
+!plan
+
+drop table if exists t;
+(0 rows modified)
+
+!update
+
+select * from t;
+Object 'T' not found
+!error
+
+drop table t;
+Table 'T' not found
+!error
+
+drop table if exists t;
+(0 rows modified)
+
+!update
+
+# End table.iq