/* * 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.relations; import io.crate.analyze.OrderBy; import io.crate.analyze.symbol.*; import io.crate.analyze.symbol.format.SymbolFormatter; import io.crate.exceptions.ColumnUnknownException; import io.crate.exceptions.ColumnValidationException; import io.crate.metadata.ColumnIdent; import io.crate.metadata.GeneratedReference; import io.crate.metadata.Path; import io.crate.metadata.Reference; import io.crate.metadata.doc.DocTableInfo; import io.crate.metadata.table.Operation; import javax.annotation.Nullable; import java.util.List; import java.util.Optional; public class DocTableRelation extends AbstractTableRelation<DocTableInfo> { private final static SortValidator SORT_VALIDATOR = new SortValidator(); private static class SortValidator extends SymbolVisitor<DocTableRelation, Void> { @Override public Void visitFunction(Function symbol, DocTableRelation context) { for (Symbol arg : symbol.arguments()) { process(arg, context); } return null; } @Override public Void visitReference(Reference symbol, DocTableRelation context) { if (context.tableInfo.partitionedBy().contains(symbol.ident().columnIdent())) { throw new UnsupportedOperationException( SymbolFormatter.format( "cannot use partitioned column %s in ORDER BY clause", symbol)); } else if (symbol.indexType() == Reference.IndexType.ANALYZED) { throw new UnsupportedOperationException( SymbolFormatter.format("Cannot ORDER BY '%s': sorting on analyzed/fulltext columns is not possible", symbol)); } else if (symbol.indexType() == Reference.IndexType.NO) { throw new UnsupportedOperationException( SymbolFormatter.format("Cannot ORDER BY '%s': sorting on non-indexed columns is not possible", symbol)); } return null; } } public DocTableRelation(DocTableInfo tableInfo) { super(tableInfo); } @Override public <C, R> R accept(AnalyzedRelationVisitor<C, R> visitor, C context) { return visitor.visitDocTableRelation(this, context); } @Nullable @Override public Field getField(Path path) { return getField(path, Operation.READ); } @Override public Field getField(Path path, Operation operation) throws UnsupportedOperationException, ColumnUnknownException { ColumnIdent ci = toColumnIdent(path); if (operation == Operation.UPDATE) { ensureColumnCanBeUpdated(ci); } Reference reference = tableInfo.getReference(ci); if (reference == null) { reference = tableInfo.indexColumn(ci); if (reference == null) { DynamicReference dynamic = tableInfo.getDynamic(ci, operation == Operation.INSERT || operation == Operation.UPDATE); if (dynamic == null) { return null; } else { return allocate(ci, dynamic); } } } reference = checkNestedArray(ci, reference); // TODO: check allocated fields first? return allocate(ci, reference); } /** * @throws io.crate.exceptions.ColumnValidationException if the column cannot be updated */ private void ensureColumnCanBeUpdated(ColumnIdent ci) { if (ci.isSystemColumn()) { throw new ColumnValidationException(ci.toString(), "Updating a system column is not supported"); } if (tableInfo.clusteredBy() != null) { ensureNotUpdated(ci, tableInfo.clusteredBy(), "Updating a clustered-by column is not supported"); } for (ColumnIdent pkIdent : tableInfo.primaryKey()) { ensureNotUpdated(ci, pkIdent, "Updating a primary key is not supported"); } List<GeneratedReference> generatedReferences = tableInfo.generatedColumns(); for (ColumnIdent partitionIdent : tableInfo.partitionedBy()) { ensureNotUpdated(ci, partitionIdent, "Updating a partitioned-by column is not supported"); int idx = generatedReferences.indexOf(tableInfo.getReference(partitionIdent)); if (idx >= 0) { GeneratedReference generatedReference = generatedReferences.get(idx); for (Reference reference : generatedReference.referencedReferences()) { ensureNotUpdated(ci, reference.ident().columnIdent(), "Updating a column which is referenced in a partitioned by generated column expression is not supported"); } } } } private static void ensureNotUpdated(ColumnIdent columnUpdated, ColumnIdent protectedColumnIdent, String errorMessage) { if (columnUpdated.equals(protectedColumnIdent) || protectedColumnIdent.isChildOf(columnUpdated)) { throw new ColumnValidationException(columnUpdated.toString(), errorMessage); } } public void validateOrderBy(Optional<OrderBy> orderBy) { if (orderBy.isPresent()) { for (Symbol symbol : orderBy.get().orderBySymbols()) { SORT_VALIDATOR.process(symbol, this); } } } }