/* * Licensed to CRATE Technology GmbH ("Crate") under one or more contributor * license agreements. See the NOTICE file distributed with this work for * additional information regarding copyright ownership. Crate 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. * * However, if you have executed another commercial license agreement * with Crate these terms will supersede the license and you may use the * software solely pursuant to the terms of the relevant commercial agreement. */ package io.crate.analyze; import io.crate.metadata.*; import io.crate.metadata.doc.DocSysColumns; import io.crate.metadata.doc.DocTableInfo; import io.crate.metadata.table.Operation; import io.crate.metadata.table.TableInfo; import io.crate.sql.tree.AlterTableAddColumn; import io.crate.types.CollectionType; import java.util.List; import java.util.Locale; class AlterTableAddColumnAnalyzer { private final Schemas schemas; private final FulltextAnalyzerResolver fulltextAnalyzerResolver; private final Functions functions; AlterTableAddColumnAnalyzer(Schemas schemas, FulltextAnalyzerResolver fulltextAnalyzerResolver, Functions functions) { this.schemas = schemas; this.fulltextAnalyzerResolver = fulltextAnalyzerResolver; this.functions = functions; } public AddColumnAnalyzedStatement analyze(AlterTableAddColumn node, Analysis analysis) { if (!node.table().partitionProperties().isEmpty()) { throw new UnsupportedOperationException("Adding a column to a single partition is not supported"); } TableIdent tableIdent = TableIdent.of(node.table(), analysis.sessionContext().defaultSchema()); DocTableInfo tableInfo = schemas.getTableInfo(tableIdent, Operation.ALTER); AnalyzedTableElements tableElements = TableElementsAnalyzer.analyze( node.tableElement(), analysis.parameterContext().parameters(), fulltextAnalyzerResolver, tableInfo ); for (AnalyzedColumnDefinition column : tableElements.columns()) { ensureColumnLeafsAreNew(column, tableInfo); } addExistingPrimaryKeys(tableInfo, tableElements); ensureNoIndexDefinitions(tableElements.columns()); tableElements.finalizeAndValidate( tableInfo.ident(), tableInfo.columns(), functions, analysis.parameterContext(), analysis.sessionContext()); int numCurrentPks = tableInfo.primaryKey().size(); if (tableInfo.primaryKey().contains(DocSysColumns.ID)) { numCurrentPks -= 1; } boolean hasNewPrimaryKeys = tableElements.primaryKeys().size() > numCurrentPks; boolean hasGeneratedColumns = tableElements.hasGeneratedColumns(); return new AddColumnAnalyzedStatement( tableInfo, tableElements, hasNewPrimaryKeys, hasGeneratedColumns ); } private static void ensureColumnLeafsAreNew(AnalyzedColumnDefinition column, TableInfo tableInfo) { if ((!column.isParentColumn() || !column.hasChildren()) && tableInfo.getReference(column.ident()) != null) { throw new IllegalArgumentException(String.format(Locale.ENGLISH, "The table %s already has a column named %s", tableInfo.ident().sqlFqn(), column.ident().sqlFqn())); } for (AnalyzedColumnDefinition child : column.children()) { ensureColumnLeafsAreNew(child, tableInfo); } } private static void addExistingPrimaryKeys(DocTableInfo tableInfo, AnalyzedTableElements tableElements) { for (ColumnIdent pkIdent : tableInfo.primaryKey()) { if (pkIdent.name().equals("_id")) { continue; } Reference pkInfo = tableInfo.getReference(pkIdent); assert pkInfo != null : "pk must not be null"; AnalyzedColumnDefinition pkColumn = new AnalyzedColumnDefinition(null); pkColumn.ident(pkIdent); pkColumn.name(pkIdent.name()); pkColumn.setPrimaryKeyConstraint(); assert !(pkInfo.valueType() instanceof CollectionType) : "pk can't be an array"; pkColumn.dataType(pkInfo.valueType().getName()); tableElements.add(pkColumn); } for (ColumnIdent columnIdent : tableInfo.partitionedBy()) { tableElements.changeToPartitionedByColumn(columnIdent, true); } } private static void ensureNoIndexDefinitions(List<AnalyzedColumnDefinition> columns) { for (AnalyzedColumnDefinition column : columns) { if (column.isIndexColumn()) { throw new UnsupportedOperationException( "Adding an index using ALTER TABLE ADD COLUMN is not supported"); } ensureNoIndexDefinitions(column.children()); } } }