Further to [CALCITE-1510] INSERT/UPSERT should allow fewer values than columns, check for default value only when target field is null (Rajeshbabu Chintaguntla)
Close apache/calcite#383 Project: http://git-wip-us.apache.org/repos/asf/calcite/repo Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/2218343f Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/2218343f Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/2218343f Branch: refs/heads/master Commit: 2218343fcc7b7d75811f7014645a4b2fbea95a24 Parents: 4ba9d1b Author: Rajeshbabu Chintaguntla <[email protected]> Authored: Sat Feb 25 01:11:35 2017 +0530 Committer: Julian Hyde <[email protected]> Committed: Mon Feb 27 13:38:33 2017 -0800 ---------------------------------------------------------------------- .../calcite/sql/validate/SqlValidatorImpl.java | 6 +-- .../apache/calcite/test/MockCatalogReader.java | 39 +++++++++++++++++++- .../apache/calcite/test/SqlValidatorTest.java | 36 ++++++++++++++++++ 3 files changed, 77 insertions(+), 4 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/calcite/blob/2218343f/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 66c467e..d270f57 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 @@ -3808,9 +3808,9 @@ public class SqlValidatorImpl implements SqlValidatorWithHints { if (!field.getType().isNullable()) { final RelDataTypeField targetField = logicalTargetRowType.getField(field.getName(), true, false); - final boolean haveDefaultValue = - table.columnHasDefaultValue(table.getRowType(), field.getIndex()); - if (targetField == null && !haveDefaultValue) { + if (targetField == null + && !table.columnHasDefaultValue(table.getRowType(), + field.getIndex())) { throw newValidationError(node, RESOURCE.columnNotNullable(field.getName())); } http://git-wip-us.apache.org/repos/asf/calcite/blob/2218343f/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 9d84d9f..420354d 100644 --- a/core/src/test/java/org/apache/calcite/test/MockCatalogReader.java +++ b/core/src/test/java/org/apache/calcite/test/MockCatalogReader.java @@ -62,6 +62,7 @@ import org.apache.calcite.schema.Wrapper; import org.apache.calcite.schema.impl.AbstractSchema; import org.apache.calcite.sql.SqlAccessType; import org.apache.calcite.sql.SqlCollation; +import org.apache.calcite.sql.SqlFunction; import org.apache.calcite.sql.SqlIdentifier; import org.apache.calcite.sql.SqlIntervalQualifier; import org.apache.calcite.sql.fun.SqlStdOperatorTable; @@ -99,6 +100,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; /** * Mock implementation of {@link SqlValidatorCatalogReader} which returns tables @@ -153,9 +155,15 @@ public class MockCatalogReader extends CalciteCatalogReader { MockSchema salesSchema = new MockSchema("SALES"); registerSchema(salesSchema); + // Register "EMP" table with customer InitializerExpressionFactory + // to check whether newDefaultValue method called or not. + final InitializerExpressionFactory countingInitializerExpressionFactory = + new CountingFactory(typeFactory); + // Register "EMP" table. final MockTable empTable = - MockTable.create(this, salesSchema, "EMP", false, 14); + MockTable.create(this, salesSchema, "EMP", false, 14, null, + countingInitializerExpressionFactory); empTable.addColumn("EMPNO", f.intType, true); empTable.addColumn("ENAME", f.varchar20Type); empTable.addColumn("JOB", f.varchar10Type); @@ -1321,6 +1329,35 @@ public class MockCatalogReader extends CalciteCatalogReader { new RelDataTypeFieldImpl("STATE", 3, varchar20Type)), RelDataTypeComparability.NONE); } + + /** To check whether {@link #newColumnDefaultValue} is called. */ + public static class CountingFactory + extends NullInitializerExpressionFactory { + static final ThreadLocal<AtomicInteger> THREAD_CALL_COUNT = + new ThreadLocal<AtomicInteger>() { + protected AtomicInteger initialValue() { + return new AtomicInteger(); + } + }; + + CountingFactory(RelDataTypeFactory typeFactory) { + super(typeFactory); + } + + @Override public RexNode newColumnDefaultValue(RelOptTable table, + int iColumn) { + THREAD_CALL_COUNT.get().incrementAndGet(); + return super.newColumnDefaultValue(table, iColumn); + } + + @Override public RexNode newAttributeInitializer(RelDataType type, + SqlFunction constructor, int iAttribute, + List<RexNode> constructorArgs) { + THREAD_CALL_COUNT.get().incrementAndGet(); + return super.newAttributeInitializer(type, constructor, iAttribute, + constructorArgs); + } + } } // End MockCatalogReader.java http://git-wip-us.apache.org/repos/asf/calcite/blob/2218343f/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 26bde91..09a6077 100644 --- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java +++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java @@ -8112,6 +8112,42 @@ public class SqlValidatorTest extends SqlValidatorTestCase { pragmaticTester.checkQuery(sql2); } + /** Test case for + * <a href="https://issues.apache.org/jira/browse/CALCITE-1510">[CALCITE-1510] + * INSERT/UPSERT should allow fewer values than columns</a>, + * check for default value only when target field is null. */ + @Test public void testInsertShouldNotCheckForDefaultValue() { + final int c = + MockCatalogReader.CountingFactory.THREAD_CALL_COUNT.get().get(); + final SqlTester pragmaticTester = + tester.withConformance(SqlConformanceEnum.PRAGMATIC_2003); + final String sql1 = "insert into emp values(1, 'nom', 'job', 0, " + + "timestamp '1970-01-01 00:00:00', 1, 1, 1, false)"; + pragmaticTester.checkQuery(sql1); + assertThat("Should not check for default value if column is in INSERT", + MockCatalogReader.CountingFactory.THREAD_CALL_COUNT.get().get(), is(c)); + + // Now add a list of target columns, keeping the query otherwise the same. + final String sql2 = "insert into emp (empno, ename, job, mgr, hiredate,\n" + + " sal, comm, deptno, slacker)\n" + + "values(1, 'nom', 'job', 0,\n" + + " timestamp '1970-01-01 00:00:00', 1, 1, 1, false)"; + pragmaticTester.checkQuery(sql2); + assertThat("Should not check for default value if column is in INSERT", + MockCatalogReader.CountingFactory.THREAD_CALL_COUNT.get().get(), is(c)); + + // Now remove SLACKER, which is NOT NULL, from the target list. + final String sql3 = "insert into ^emp^ (empno, ename, job, mgr, hiredate,\n" + + " sal, comm, deptno)\n" + + "values(1, 'nom', 'job', 0,\n" + + " 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", + MockCatalogReader.CountingFactory.THREAD_CALL_COUNT.get().get(), + is(c + 1)); + } + @Test public void testInsertView() { tester.checkQuery("insert into empnullables_20 (ename, empno, comm)\n" + "values ('Karl', 1, 1)");
