clintropolis commented on code in PR #19830: URL: https://github.com/apache/druid/pull/19830#discussion_r3936886428
########## sql/src/main/java/org/apache/druid/sql/calcite/planner/CatalogDdlHandler.java: ########## @@ -0,0 +1,690 @@ +/* + * 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.druid.sql.calcite.planner; + +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import org.apache.calcite.jdbc.CalciteSchema; +import org.apache.calcite.rel.type.RelDataType; +import org.apache.calcite.rel.type.RelDataTypeFactory; +import org.apache.calcite.sql.SqlIdentifier; +import org.apache.calcite.sql.SqlLiteral; +import org.apache.calcite.sql.SqlNode; +import org.apache.calcite.sql.SqlNodeList; +import org.apache.calcite.sql.type.SqlTypeName; +import org.apache.druid.catalog.model.ClusteredValueGroupsBaseTableMetadata; +import org.apache.druid.catalog.model.ColumnSpec; +import org.apache.druid.catalog.model.Columns; +import org.apache.druid.catalog.model.DatasourceProjectionMetadata; +import org.apache.druid.catalog.model.TableId; +import org.apache.druid.catalog.model.TableMetadata; +import org.apache.druid.catalog.model.TableSpec; +import org.apache.druid.catalog.model.table.ClusterKeySpec; +import org.apache.druid.catalog.model.table.DatasourceDefn; +import org.apache.druid.common.utils.IdUtils; +import org.apache.druid.error.DruidException; +import org.apache.druid.error.InvalidSqlInput; +import org.apache.druid.java.util.common.IAE; +import org.apache.druid.java.util.common.granularity.Granularities; +import org.apache.druid.java.util.common.granularity.Granularity; +import org.apache.druid.java.util.common.granularity.PeriodGranularity; +import org.apache.druid.java.util.common.guava.Sequences; +import org.apache.druid.query.explain.ExplainAttributes; +import org.apache.druid.segment.column.ColumnType; +import org.apache.druid.segment.projections.Projections; +import org.apache.druid.server.QueryResponse; +import org.apache.druid.server.security.Action; +import org.apache.druid.server.security.Resource; +import org.apache.druid.server.security.ResourceAction; +import org.apache.druid.server.security.ResourceType; +import org.apache.druid.sql.calcite.parser.DruidSqlAlterTable; +import org.apache.druid.sql.calcite.parser.DruidSqlColumnDeclaration; +import org.apache.druid.sql.calcite.parser.DruidSqlCreateTable; +import org.apache.druid.sql.calcite.parser.DruidSqlParser; +import org.apache.druid.sql.calcite.parser.DruidSqlPropertyAssignment; +import org.apache.druid.sql.calcite.parser.SqlGranularityLiteral; +import org.apache.druid.sql.calcite.parser.SqlProjectionSpec; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Handles the catalog DDL statements: {@code CREATE TABLE} and {@code ALTER TABLE}. + * <p> + * These statements are metadata operations, not queries. They are validated here, converted to a catalog + * {@link TableSpec} or column/property edit, and applied through {@link CatalogTableWriter}, which forwards them to + * the Coordinator. No Calcite validation or query planning takes place, and no rows are returned. + * <p> + * Validation is deliberately split. This class checks what it can attribute to a position in the statement (type + * spellings, duplicate columns, granularity, clustering) so that the error names the offending SQL. The Coordinator + * remains authoritative: {@code DatasourceDefn.validate} runs on write, and its message is surfaced verbatim. + */ +public abstract class CatalogDdlHandler extends SqlStatementHandler.BaseStatementHandler +{ + /** + * DDL produces no rows. A single-column type still has to be declared, because JDBC clients ask for a result set + * signature when preparing the statement. + */ + private static final RelDataType RESULT_TYPE = resultType(); + + protected final SqlIdentifier tableIdentifier; + protected TableId tableId; + + protected CatalogDdlHandler(SqlStatementHandler.HandlerContext handlerContext, SqlIdentifier tableIdentifier) + { + super(handlerContext); + this.tableIdentifier = tableIdentifier; + } + + /** + * The runtime property that gates these statements. Read from {@link PlannerConfig} rather than the query context + * so that a user cannot turn the feature on for their own statement. + */ + public static final String ENABLE_CATALOG_DDL_PROPERTY = "druid.sql.planner.enableCatalogDdl"; + + /** + * The reserved name of the base-table projection, which describes the physical layout of the table itself. Handled + * as a separate catalog property, not as one of the aggregate projections. + */ + public static final String BASE_PROJECTION_NAME = "__base"; + + @Override + public void validate() + { + if (!handlerContext.plannerContext().getPlannerConfig().isEnableCatalogDdl()) { + throw DruidException.forPersona(DruidException.Persona.ADMIN) + .ofCategory(DruidException.Category.UNSUPPORTED) + .build( + "Catalog DDL statements are disabled. Set [%s] to true on the Broker to enable [%s].", + ENABLE_CATALOG_DDL_PROPERTY, + operationName() + ); + } + if (!handlerContext.plannerContext().getParameters().isEmpty()) { + throw InvalidSqlInput.exception("Dynamic parameters are not supported for [%s]", operationName()); + } + tableId = TableId.datasource(resolveTableName()); + resourceActions = Collections.singleton( + new ResourceAction(new Resource(tableId.name(), ResourceType.DATASOURCE), Action.WRITE) + ); + validateStatement(); + } + + /** + * Statement-specific validation, which also prepares whatever {@link #execute} will apply. + */ + protected abstract void validateStatement(); + + protected abstract void execute(CatalogTableWriter writer); + + protected abstract String operationName(); + + @Override + public void prepare() + { + // Nothing to prepare: there is no query to plan. + } + + @Override + public PrepareResult prepareResult() + { + return new PrepareResult(RESULT_TYPE, RESULT_TYPE, DruidTypeSystem.TYPE_FACTORY.createStructType( + Collections.emptyList(), + Collections.emptyList() + )); + } + + @Override + public PlannerResult plan() + { + execute(handlerContext.plannerContext().getPlannerToolbox().catalogTableWriter()); Review Comment: good catch, fixed -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
