This is an automated email from the ASF dual-hosted git repository.

korlov pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git


The following commit(s) were added to refs/heads/main by this push:
     new 53a36f27e5 IGNITE-18965 Sql. CREATE TABLE fails when a column with 
DEFAULT constraint NULL is present (#2489)
53a36f27e5 is described below

commit 53a36f27e511c897fb1eacedba78fcd2bf889ad4
Author: Max Zhuravkov <shh...@gmail.com>
AuthorDate: Fri Aug 25 10:24:01 2023 +0300

    IGNITE-18965 Sql. CREATE TABLE fails when a column with DEFAULT constraint 
NULL is present (#2489)
---
 .../ignite/internal/sql/engine/ItDmlTest.java      | 38 +++++++--
 .../prepare/ddl/DdlSqlToCommandConverter.java      | 91 +++++-----------------
 2 files changed, 53 insertions(+), 76 deletions(-)

diff --git 
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItDmlTest.java
 
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItDmlTest.java
index 18bd95c4b6..7bac2876ed 100644
--- 
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItDmlTest.java
+++ 
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItDmlTest.java
@@ -32,11 +32,13 @@ import java.time.LocalTime;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 import org.apache.ignite.internal.sql.engine.exec.rel.AbstractNode;
 import org.apache.ignite.internal.testframework.WithSystemProperty;
 import org.apache.ignite.lang.IgniteException;
 import org.apache.ignite.sql.SqlException;
 import org.apache.ignite.tx.Transaction;
+import org.jetbrains.annotations.Nullable;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
@@ -189,6 +191,17 @@ public class ItDmlTest extends 
ClusterPerClassIntegrationTest {
                 .check();
     }
 
+    @Test
+    public void testNullDefault() {
+        sql("CREATE TABLE test_null_def (id INTEGER PRIMARY KEY, col INTEGER 
DEFAULT NULL)");
+
+        sql("INSERT INTO test_null_def VALUES(1, DEFAULT)");
+        assertQuery("SELECT col FROM test_null_def WHERE id = 
1").returns(null).check();
+
+        sql("INSERT INTO test_null_def (id, col) VALUES(2, DEFAULT)");
+        assertQuery("SELECT col FROM test_null_def WHERE id = 
2").returns(null).check();
+    }
+
     /**Test full MERGE command. */
     @Test
     public void testMerge() {
@@ -440,9 +453,8 @@ public class ItDmlTest extends 
ClusterPerClassIntegrationTest {
         assertEquals(3, pkVals.size());
     }
 
-    @Test
-    public void testInsertDefaultValue() {
-        var args = List.of(
+    private static Stream<DefaultValueArg> defaultValueArgs() {
+        return Stream.of(
                 new DefaultValueArg("BOOLEAN", "TRUE", Boolean.TRUE),
                 new DefaultValueArg("BOOLEAN NOT NULL", "TRUE", Boolean.TRUE),
 
@@ -476,8 +488,11 @@ public class ItDmlTest extends 
ClusterPerClassIntegrationTest {
                 // TODO: IGNITE-17374
                 // new DefaultValueArg("VARBINARY", "x'010203'", new byte[]{1, 
2, 3})
         );
+    }
 
-        checkDefaultValue(args);
+    @Test
+    public void testInsertDefaultValue() {
+        checkDefaultValue(defaultValueArgs().collect(Collectors.toList()));
 
         checkWrongDefault("VARCHAR", "10");
         checkWrongDefault("INT", "'10'");
@@ -495,6 +510,14 @@ public class ItDmlTest extends 
ClusterPerClassIntegrationTest {
         checkWrongDefault("VARBINARY", "10");
     }
 
+    @Test
+    public void testInsertDefaultNullValue() {
+        checkDefaultValue(defaultValueArgs()
+                .filter(a -> !a.sqlType.endsWith("NOT NULL"))
+                .map(a -> new DefaultValueArg(a.sqlType, "NULL", null))
+                .collect(Collectors.toList()));
+    }
+
     private void checkDefaultValue(List<DefaultValueArg> args) {
         assert !args.isEmpty();
 
@@ -566,11 +589,16 @@ public class ItDmlTest extends 
ClusterPerClassIntegrationTest {
         final String sqlVal;
         final Object expectedVal;
 
-        private DefaultValueArg(String sqlType, String sqlVal, Object 
expectedVal) {
+        private DefaultValueArg(String sqlType, String sqlVal, @Nullable 
Object expectedVal) {
             this.sqlType = sqlType;
             this.sqlVal = sqlVal;
             this.expectedVal = expectedVal;
         }
+
+        @Override
+        public String toString() {
+            return sqlType + " sql val: " + sqlVal + " java val: " + 
expectedVal;
+        }
     }
 
     @Test
diff --git 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DdlSqlToCommandConverter.java
 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DdlSqlToCommandConverter.java
index e3b0dce2c4..9cc9b9aa7a 100644
--- 
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DdlSqlToCommandConverter.java
+++ 
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/ddl/DdlSqlToCommandConverter.java
@@ -88,6 +88,7 @@ import 
org.apache.ignite.internal.sql.engine.sql.IgniteSqlDropZone;
 import org.apache.ignite.internal.sql.engine.sql.IgniteSqlIndexType;
 import org.apache.ignite.internal.sql.engine.sql.IgniteSqlZoneOption;
 import org.apache.ignite.internal.sql.engine.util.Commons;
+import org.apache.ignite.internal.sql.engine.util.TypeUtils;
 import org.apache.ignite.lang.IgniteException;
 import org.apache.ignite.lang.SchemaNotFoundException;
 import org.apache.ignite.sql.ColumnType;
@@ -402,18 +403,20 @@ public class DdlSqlToCommandConverter {
         return alterTblCmd;
     }
 
-    private DefaultValueDefinition convertDefault(SqlNode expression, 
RelDataType relType) {
-        if (expression instanceof SqlIdentifier) {
+    private static DefaultValueDefinition convertDefault(@Nullable SqlNode 
expression, RelDataType relType) {
+        if (expression == null) {
+            return DefaultValueDefinition.constant(null);
+        } else if (expression instanceof SqlIdentifier) {
             return DefaultValueDefinition.functionCall(((SqlIdentifier) 
expression).getSimple());
-        }
-
-        Object val = null;
+        } else if (expression instanceof SqlLiteral) {
+            ColumnType columnType = TypeUtils.columnType(relType);
+            assert columnType != null : "RelType to columnType conversion 
should not return null";
 
-        if (expression instanceof SqlLiteral) {
-            val = fromLiteral(relType, (SqlLiteral) expression);
+            Object val = fromLiteral(columnType, (SqlLiteral) expression);
+            return DefaultValueDefinition.constant(val);
+        } else {
+            throw new IllegalArgumentException("Unsupported default 
expression: " + expression.getKind());
         }
-
-        return DefaultValueDefinition.constant(val);
     }
 
     private AlterColumnCommand convertAlterColumn(IgniteSqlAlterColumn 
alterColumnNode, PlanningContext ctx) {
@@ -440,7 +443,7 @@ public class DdlSqlToCommandConverter {
             if (expr instanceof SqlLiteral) {
                 resolveDfltFunc = type -> 
DefaultValue.constant(fromLiteral(type, (SqlLiteral) expr));
             } else {
-                throw new IllegalStateException("Invalid expression type " + 
expr.getClass().getName());
+                throw new IllegalStateException("Invalid expression type " + 
expr.getKind());
             }
 
             cmd.defaultValueResolver(resolveDfltFunc);
@@ -820,70 +823,13 @@ public class DdlSqlToCommandConverter {
     /**
      * Creates a value of required type from the literal.
      */
-    private static Object fromLiteral(RelDataType columnType, SqlLiteral 
literal) {
-        try {
-            SqlTypeName sqlColumnType = columnType.getSqlTypeName();
-
-            switch (sqlColumnType) {
-                case VARCHAR:
-                case CHAR:
-                    return literal.getValueAs(String.class);
-                case DATE: {
-                    SqlLiteral literal0 = ((SqlUnknownLiteral) 
literal).resolve(sqlColumnType);
-                    return 
LocalDate.ofEpochDay(literal0.getValueAs(DateString.class).getDaysSinceEpoch());
-                }
-                case TIME: {
-                    SqlLiteral literal0 = ((SqlUnknownLiteral) 
literal).resolve(sqlColumnType);
-                    return 
LocalTime.ofNanoOfDay(TimeUnit.MILLISECONDS.toNanos(literal0.getValueAs(TimeString.class).getMillisOfDay()));
-                }
-                case TIMESTAMP: {
-                    SqlLiteral literal0 = ((SqlUnknownLiteral) 
literal).resolve(sqlColumnType);
-                    var tsString = literal0.getValueAs(TimestampString.class);
-
-                    return LocalDateTime.ofEpochSecond(
-                            
TimeUnit.MILLISECONDS.toSeconds(tsString.getMillisSinceEpoch()),
-                            (int) 
(TimeUnit.MILLISECONDS.toNanos(tsString.getMillisSinceEpoch() % 1000)),
-                            ZoneOffset.UTC
-                    );
-                }
-                case TIMESTAMP_WITH_LOCAL_TIME_ZONE: {
-                    // TODO: IGNITE-17376
-                    throw new 
UnsupportedOperationException("https://issues.apache.org/jira/browse/IGNITE-17376";);
-                }
-                case INTEGER:
-                    return literal.getValueAs(Integer.class);
-                case BIGINT:
-                    return literal.getValueAs(Long.class);
-                case SMALLINT:
-                    return literal.getValueAs(Short.class);
-                case TINYINT:
-                    return literal.getValueAs(Byte.class);
-                case DECIMAL:
-                    return literal.getValueAs(BigDecimal.class);
-                case DOUBLE:
-                    return literal.getValueAs(Double.class);
-                case REAL:
-                case FLOAT:
-                    return literal.getValueAs(Float.class);
-                case BINARY:
-                case VARBINARY:
-                    return literal.getValueAs(byte[].class);
-                case BOOLEAN:
-                    return literal.getValueAs(Boolean.class);
-                default:
-                    throw new IllegalStateException("Unknown type [type=" + 
columnType + ']');
-            }
-        } catch (Throwable th) {
-            // catch throwable here because literal throws an AssertionError 
when unable to cast value to a given class
-            throw new SqlException(STMT_VALIDATION_ERR, "Unable co convert 
literal", th);
+    private static @Nullable Object fromLiteral(ColumnType columnType, 
SqlLiteral literal) {
+        if (literal.getValue() == null) {
+            return null;
         }
-    }
 
-    private static @Nullable Object fromLiteral(ColumnType columnType, 
SqlLiteral literal) {
         try {
             switch (columnType) {
-                case NULL:
-                    return null;
                 case STRING:
                     return literal.getValueAs(String.class);
                 case DATE: {
@@ -894,7 +840,7 @@ public class DdlSqlToCommandConverter {
                     SqlLiteral literal0 = ((SqlUnknownLiteral) 
literal).resolve(SqlTypeName.TIME);
                     return 
LocalTime.ofNanoOfDay(TimeUnit.MILLISECONDS.toNanos(literal0.getValueAs(TimeString.class).getMillisOfDay()));
                 }
-                case TIMESTAMP: {
+                case DATETIME: {
                     SqlLiteral literal0 = ((SqlUnknownLiteral) 
literal).resolve(SqlTypeName.TIMESTAMP);
                     var tsString = literal0.getValueAs(TimestampString.class);
 
@@ -904,6 +850,9 @@ public class DdlSqlToCommandConverter {
                             ZoneOffset.UTC
                     );
                 }
+                case TIMESTAMP:
+                    // TODO: IGNITE-17376
+                    throw new UnsupportedOperationException("Type is not 
supported: " + columnType);
                 case INT32:
                     return literal.getValueAs(Integer.class);
                 case INT64:

Reply via email to