private CreateTableCommand convertCreateTable()

in modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/ddl/DdlSqlToCommandConverter.java [186:368]


    private CreateTableCommand convertCreateTable(IgniteSqlCreateTable createTblNode, PlanningContext ctx) {
        CreateTableCommand createTblCmd = new CreateTableCommand();

        String schemaName = deriveSchemaName(createTblNode.name(), ctx);
        String tableName = deriveObjectName(createTblNode.name(), ctx, "tableName");

        createTblCmd.schemaName(schemaName);
        createTblCmd.tableName(tableName);
        createTblCmd.ifNotExists(createTblNode.ifNotExists());
        createTblCmd.templateName(QueryUtils.TEMPLATE_PARTITIONED);

        if (createTblNode.createOptionList() != null) {
            for (SqlNode optNode : createTblNode.createOptionList().getList()) {
                IgniteSqlCreateTableOption opt = (IgniteSqlCreateTableOption)optNode;

                tblOptionProcessors.getOrDefault(opt.key(), UNSUPPORTED_OPTION_PROCESSOR).process(opt, ctx, createTblCmd);
            }
        }

        IgnitePlanner planner = ctx.planner();

        if (createTblNode.query() == null) {
            List<SqlColumnDeclaration> colDeclarations = createTblNode.columnList().getList().stream()
                .filter(SqlColumnDeclaration.class::isInstance)
                .map(SqlColumnDeclaration.class::cast)
                .collect(Collectors.toList());

            List<ColumnDefinition> cols = new ArrayList<>();

            for (SqlColumnDeclaration col : colDeclarations) {
                if (!col.name.isSimple())
                    throw new IgniteSQLException("Unexpected value of columnName [" +
                        "expected a simple identifier, but was " + col.name + "; " +
                        "querySql=\"" + ctx.query() + "\"]", IgniteQueryErrorCode.PARSING);

                String name = col.name.getSimple();
                RelDataType type = planner.convert(col.dataType);
                Object dflt = null;

                assert col.expression == null || col.expression instanceof SqlLiteral;

                if (col.expression != null
                    && (((SqlLiteral)col.expression).getTypeName() != SqlTypeName.NULL || type instanceof OtherType)) {
                    if (type instanceof OtherType)
                        throw new IgniteSQLException("Type '" + type + "' doesn't support default value.");

                    Type storageType = ctx.typeFactory().getResultClass(type);

                    DataContext dataCtx = new BaseDataContext(ctx.typeFactory());

                    dflt = TypeUtils.fromLiteral(dataCtx, storageType, (SqlLiteral)col.expression);
                }

                cols.add(new ColumnDefinition(name, type, dflt));
            }

            createTblCmd.columns(cols);

            List<SqlKeyConstraint> pkConstraints = createTblNode.columnList().getList().stream()
                .filter(SqlKeyConstraint.class::isInstance)
                .map(SqlKeyConstraint.class::cast)
                .collect(Collectors.toList());

            if (pkConstraints.size() > 1)
                throw new IgniteSQLException("Unexpected amount of primary key constraints [" +
                    "expected at most one, but was " + pkConstraints.size() + "; " +
                    "querySql=\"" + ctx.query() + "\"]", IgniteQueryErrorCode.PARSING);

            if (!F.isEmpty(pkConstraints)) {
                Set<String> dedupSet = new HashSet<>();

                List<String> pkCols = pkConstraints.stream()
                    .map(pk -> pk.getOperandList().get(1))
                    .map(SqlNodeList.class::cast)
                    .flatMap(l -> l.getList().stream())
                    .map(SqlIdentifier.class::cast)
                    .map(SqlIdentifier::getSimple)
                    .filter(dedupSet::add)
                    .collect(Collectors.toList());

                createTblCmd.primaryKeyColumns(pkCols);
            }
        }
        else { // CREATE AS SELECT.
            ValidationResult res = planner.validateAndGetTypeMetadata(createTblNode.query());

            // Create INSERT node on top of AS SELECT node.
            SqlInsert sqlInsert = new SqlInsert(
                createTblNode.query().getParserPosition(),
                SqlNodeList.EMPTY,
                createTblNode.name(),
                res.sqlNode(),
                null
            );

            createTblCmd.insertStatement(sqlInsert);

            List<RelDataTypeField> fields = res.dataType().getFieldList();
            List<ColumnDefinition> cols = new ArrayList<>(fields.size());

            if (createTblNode.columnList() != null) {
                // Derive column names from the CREATE TABLE clause and column types from the query.
                List<SqlIdentifier> colNames = createTblNode.columnList().getList().stream()
                    .map(SqlIdentifier.class::cast)
                    .collect(Collectors.toList());

                if (fields.size() != colNames.size()) {
                    throw new IgniteSQLException("Number of columns must match number of query columns",
                        IgniteQueryErrorCode.PARSING);
                }

                for (int i = 0; i < colNames.size(); i++) {
                    SqlIdentifier colName = colNames.get(i);

                    assert colName.isSimple();

                    RelDataType type = fields.get(i).getType();

                    cols.add(new ColumnDefinition(colName.getSimple(), type, null));
                }
            }
            else {
                // Derive column names and column types from the query.
                for (RelDataTypeField field : fields)
                    cols.add(new ColumnDefinition(field.getName(), field.getType(), null));
            }

            createTblCmd.columns(cols);
        }

        if (createTblCmd.columns() == null) {
            throw new IgniteSQLException("Column list or query should be specified for CREATE TABLE command",
                IgniteQueryErrorCode.PARSING);
        }

        // Validate affinity key.
        if (createTblCmd.affinityKey() != null) {
            String affColName = null;
            String val = createTblCmd.affinityKey();

            if (val.startsWith("'")) {
                if (val.length() == 1 || !val.endsWith("'")) {
                    throw new IgniteSQLException("Affinity key column name does not have trailing quote: " + val,
                        IgniteQueryErrorCode.PARSING);
                }

                val = val.substring(1, val.length() - 1);

                if (F.isEmpty(val))
                    throw new IgniteSQLException("Affinity key cannot be empty", IgniteQueryErrorCode.PARSING);

                affColName = val;
            }
            else {
                for (ColumnDefinition col : createTblCmd.columns()) {
                    if (col.name().equalsIgnoreCase(val)) {
                        if (affColName != null) {
                            throw new IgniteSQLException("Ambiguous affinity column name, use single quotes " +
                                "for case sensitivity: " + val, IgniteQueryErrorCode.PARSING);
                        }

                        affColName = col.name();
                    }
                }
            }

            String affColFinal = affColName;

            if (affColName == null || createTblCmd.columns().stream().noneMatch(col -> affColFinal.equals(col.name()))) {
                throw new IgniteSQLException("Affinity key column with given name not found: " + val,
                    IgniteQueryErrorCode.PARSING);
            }

            if (!createTblCmd.primaryKeyColumns().contains(affColName)) {
                throw new IgniteSQLException("Affinity key column must be one of key columns: " + affColName,
                    IgniteQueryErrorCode.PARSING);
            }

            createTblCmd.affinityKey(affColName);
        }

        return createTblCmd;
    }