use of org.apache.cassandra.cql3.statements.BatchStatement.Type in project cassandra by apache.
the class AlterTableStatement method announceMigration.
public Event.SchemaChange announceMigration(QueryState queryState, boolean isLocalOnly) throws RequestValidationException {
TableMetadata current = Schema.instance.validateTable(keyspace(), columnFamily());
if (current.isView())
throw new InvalidRequestException("Cannot use ALTER TABLE on Materialized View");
TableMetadata.Builder builder = current.unbuild();
ColumnIdentifier columnName = null;
ColumnMetadata def = null;
CQL3Type.Raw dataType = null;
boolean isStatic = false;
CQL3Type validator = null;
List<ViewMetadata> viewUpdates = new ArrayList<>();
Iterable<ViewMetadata> views = View.findAll(keyspace(), columnFamily());
switch(oType) {
case ALTER:
throw new InvalidRequestException("Altering of types is not allowed");
case ADD:
if (current.isDense())
throw new InvalidRequestException("Cannot add new column to a COMPACT STORAGE table");
for (AlterTableStatementColumn colData : colNameList) {
columnName = colData.getColumnName().getIdentifier(current);
def = builder.getColumn(columnName);
dataType = colData.getColumnType();
assert dataType != null;
isStatic = colData.getStaticType();
validator = dataType.prepare(keyspace());
if (isStatic) {
if (!current.isCompound())
throw new InvalidRequestException("Static columns are not allowed in COMPACT STORAGE tables");
if (current.clusteringColumns().isEmpty())
throw new InvalidRequestException("Static columns are only useful (and thus allowed) if the table has at least one clustering column");
}
if (def != null) {
switch(def.kind) {
case PARTITION_KEY:
case CLUSTERING:
throw new InvalidRequestException(String.format("Invalid column name %s because it conflicts with a PRIMARY KEY part", columnName));
default:
throw new InvalidRequestException(String.format("Invalid column name %s because it conflicts with an existing column", columnName));
}
}
// Cannot re-add a dropped counter column. See #7831.
if (current.isCounter() && current.getDroppedColumn(columnName.bytes) != null)
throw new InvalidRequestException(String.format("Cannot re-add previously dropped counter column %s", columnName));
AbstractType<?> type = validator.getType();
if (type.isCollection() && type.isMultiCell()) {
if (!current.isCompound())
throw new InvalidRequestException("Cannot use non-frozen collections in COMPACT STORAGE tables");
if (current.isSuper())
throw new InvalidRequestException("Cannot use non-frozen collections with super column families");
// If there used to be a non-frozen collection column with the same name (that has been dropped),
// we could still have some data using the old type, and so we can't allow adding a collection
// with the same name unless the types are compatible (see #6276).
DroppedColumn dropped = current.droppedColumns.get(columnName.bytes);
if (dropped != null && dropped.column.type instanceof CollectionType && dropped.column.type.isMultiCell() && !type.isCompatibleWith(dropped.column.type)) {
String message = String.format("Cannot add a collection with the name %s because a collection with the same name" + " and a different type (%s) has already been used in the past", columnName, dropped.column.type.asCQL3Type());
throw new InvalidRequestException(message);
}
}
builder.addColumn(isStatic ? ColumnMetadata.staticColumn(current, columnName.bytes, type) : ColumnMetadata.regularColumn(current, columnName.bytes, type));
// as well
if (!isStatic)
for (ViewMetadata view : views) if (view.includeAllColumns)
viewUpdates.add(view.withAddedRegularColumn(ColumnMetadata.regularColumn(view.metadata, columnName.bytes, type)));
}
break;
case DROP:
if (!current.isCQLTable())
throw new InvalidRequestException("Cannot drop columns from a non-CQL3 table");
for (AlterTableStatementColumn colData : colNameList) {
columnName = colData.getColumnName().getIdentifier(current);
def = builder.getColumn(columnName);
if (def == null)
throw new InvalidRequestException(String.format("Column %s was not found in table %s", columnName, columnFamily()));
switch(def.kind) {
case PARTITION_KEY:
case CLUSTERING:
throw new InvalidRequestException(String.format("Cannot drop PRIMARY KEY part %s", columnName));
case REGULAR:
case STATIC:
builder.removeRegularOrStaticColumn(def.name);
builder.recordColumnDrop(def, deleteTimestamp == null ? queryState.getTimestamp() : deleteTimestamp);
break;
}
// If the dropped column is required by any secondary indexes
// we reject the operation, as the indexes must be dropped first
Indexes allIndexes = current.indexes;
if (!allIndexes.isEmpty()) {
ColumnFamilyStore store = Keyspace.openAndGetStore(current);
Set<IndexMetadata> dependentIndexes = store.indexManager.getDependentIndexes(def);
if (!dependentIndexes.isEmpty()) {
throw new InvalidRequestException(String.format("Cannot drop column %s because it has " + "dependent secondary indexes (%s)", def, dependentIndexes.stream().map(i -> i.name).collect(Collectors.joining(","))));
}
}
// If a column is dropped which is included in a view, we don't allow the drop to take place.
boolean rejectAlter = false;
StringBuilder viewNames = new StringBuilder();
for (ViewMetadata view : views) {
if (!view.includes(columnName))
continue;
if (rejectAlter)
viewNames.append(',');
rejectAlter = true;
viewNames.append(view.name);
}
if (rejectAlter)
throw new InvalidRequestException(String.format("Cannot drop column %s, depended on by materialized views (%s.{%s})", columnName.toString(), keyspace(), viewNames.toString()));
}
break;
case OPTS:
if (attrs == null)
throw new InvalidRequestException("ALTER TABLE WITH invoked, but no parameters found");
attrs.validate();
TableParams params = attrs.asAlteredTableParams(current.params);
if (!Iterables.isEmpty(views) && params.gcGraceSeconds == 0) {
throw new InvalidRequestException("Cannot alter gc_grace_seconds of the base table of a " + "materialized view to 0, since this value is used to TTL " + "undelivered updates. Setting gc_grace_seconds too low might " + "cause undelivered updates to expire " + "before being replayed.");
}
if (current.isCounter() && params.defaultTimeToLive > 0)
throw new InvalidRequestException("Cannot set default_time_to_live on a table with counters");
builder.params(params);
break;
case RENAME:
for (Map.Entry<ColumnMetadata.Raw, ColumnMetadata.Raw> entry : renames.entrySet()) {
ColumnIdentifier from = entry.getKey().getIdentifier(current);
ColumnIdentifier to = entry.getValue().getIdentifier(current);
def = current.getColumn(from);
if (def == null)
throw new InvalidRequestException(String.format("Cannot rename unknown column %s in table %s", from, current.name));
if (current.getColumn(to) != null)
throw new InvalidRequestException(String.format("Cannot rename column %s to %s in table %s; another column of that name already exist", from, to, current.name));
if (!def.isPrimaryKeyColumn())
throw new InvalidRequestException(String.format("Cannot rename non PRIMARY KEY part %s", from));
if (!current.indexes.isEmpty()) {
ColumnFamilyStore store = Keyspace.openAndGetStore(current);
Set<IndexMetadata> dependentIndexes = store.indexManager.getDependentIndexes(def);
if (!dependentIndexes.isEmpty())
throw new InvalidRequestException(String.format("Cannot rename column %s because it has " + "dependent secondary indexes (%s)", from, dependentIndexes.stream().map(i -> i.name).collect(Collectors.joining(","))));
}
builder.renamePrimaryKeyColumn(from, to);
// If the view includes a renamed column, it must be renamed in the view table and the definition.
for (ViewMetadata view : views) {
if (!view.includes(from))
continue;
ColumnIdentifier viewFrom = entry.getKey().getIdentifier(view.metadata);
ColumnIdentifier viewTo = entry.getValue().getIdentifier(view.metadata);
viewUpdates.add(view.renamePrimaryKeyColumn(viewFrom, viewTo));
}
}
break;
}
// FIXME: Should really be a single announce for the table and views.
MigrationManager.announceTableUpdate(builder.build(), isLocalOnly);
for (ViewMetadata viewUpdate : viewUpdates) MigrationManager.announceViewUpdate(viewUpdate, isLocalOnly);
return new Event.SchemaChange(Event.SchemaChange.Change.UPDATED, Event.SchemaChange.Target.TABLE, keyspace(), columnFamily());
}
use of org.apache.cassandra.cql3.statements.BatchStatement.Type in project cassandra by apache.
the class FunctionResolver method get.
/**
* @param keyspace the current keyspace
* @param name the name of the function
* @param providedArgs the arguments provided for the function call
* @param receiverKs the receiver's keyspace
* @param receiverCf the receiver's table
* @param receiverType if the receiver type is known (during inserts, for example), this should be the type of
* the receiver
* @throws InvalidRequestException
*/
public static Function get(String keyspace, FunctionName name, List<? extends AssignmentTestable> providedArgs, String receiverKs, String receiverCf, AbstractType<?> receiverType) throws InvalidRequestException {
if (name.equalsNativeFunction(TOKEN_FUNCTION_NAME))
return new TokenFct(Schema.instance.getTableMetadata(receiverKs, receiverCf));
// due to needing to know the argument types in advance).
if (name.equalsNativeFunction(ToJsonFct.NAME))
throw new InvalidRequestException("toJson() may only be used within the selection clause of SELECT statements");
// Similarly, we can only use fromJson when we know the receiver type (such as inserts)
if (name.equalsNativeFunction(FromJsonFct.NAME)) {
if (receiverType == null)
throw new InvalidRequestException("fromJson() cannot be used in the selection clause of a SELECT statement");
return FromJsonFct.getInstance(receiverType);
}
Collection<Function> candidates;
if (!name.hasKeyspace()) {
// function name not fully qualified
candidates = new ArrayList<>();
// add 'SYSTEM' (native) candidates
candidates.addAll(Schema.instance.getFunctions(name.asNativeFunction()));
// add 'current keyspace' candidates
candidates.addAll(Schema.instance.getFunctions(new FunctionName(keyspace, name.name)));
} else {
// function name is fully qualified (keyspace + name)
candidates = Schema.instance.getFunctions(name);
}
if (candidates.isEmpty())
return null;
// Fast path if there is only one choice
if (candidates.size() == 1) {
Function fun = candidates.iterator().next();
validateTypes(keyspace, fun, providedArgs, receiverKs, receiverCf);
return fun;
}
List<Function> compatibles = null;
for (Function toTest : candidates) {
if (matchReturnType(toTest, receiverType)) {
AssignmentTestable.TestResult r = matchAguments(keyspace, toTest, providedArgs, receiverKs, receiverCf);
switch(r) {
case EXACT_MATCH:
// We always favor exact matches
return toTest;
case WEAKLY_ASSIGNABLE:
if (compatibles == null)
compatibles = new ArrayList<>();
compatibles.add(toTest);
break;
}
}
}
if (compatibles == null) {
if (OperationFcts.isOperation(name))
throw invalidRequest("the '%s' operation is not supported between %s and %s", OperationFcts.getOperator(name), providedArgs.get(0), providedArgs.get(1));
throw invalidRequest("Invalid call to function %s, none of its type signatures match (known type signatures: %s)", name, format(candidates));
}
if (compatibles.size() > 1) {
if (OperationFcts.isOperation(name)) {
if (receiverType != null && !containsMarkers(providedArgs)) {
for (Function toTest : compatibles) {
List<AbstractType<?>> argTypes = toTest.argTypes();
if (receiverType.equals(argTypes.get(0)) && receiverType.equals(argTypes.get(1)))
return toTest;
}
}
throw invalidRequest("Ambiguous '%s' operation: use type casts to disambiguate", OperationFcts.getOperator(name), providedArgs.get(0), providedArgs.get(1));
}
if (OperationFcts.isNegation(name))
throw invalidRequest("Ambiguous negation: use type casts to disambiguate");
throw invalidRequest("Ambiguous call to function %s (can be matched by following signatures: %s): use type casts to disambiguate", name, format(compatibles));
}
return compatibles.get(0);
}
use of org.apache.cassandra.cql3.statements.BatchStatement.Type in project cassandra by apache.
the class UDHelper method driverType.
/**
* Returns the {@link DataType} for the C* internal type.
*/
public static DataType driverType(AbstractType abstractType) {
CQL3Type cqlType = abstractType.asCQL3Type();
String abstractTypeDef = cqlType.getType().toString();
return driverTypeFromAbstractType(abstractTypeDef);
}
use of org.apache.cassandra.cql3.statements.BatchStatement.Type in project cassandra by apache.
the class FunctionResolver method validateTypes.
// This method and matchArguments are somewhat duplicate, but this method allows us to provide more precise errors in the common
// case where there is no override for a given function. This is thus probably worth the minor code duplication.
private static void validateTypes(String keyspace, Function fun, List<? extends AssignmentTestable> providedArgs, String receiverKs, String receiverCf) {
if (providedArgs.size() != fun.argTypes().size())
throw invalidRequest("Invalid number of arguments in call to function %s: %d required but %d provided", fun.name(), fun.argTypes().size(), providedArgs.size());
for (int i = 0; i < providedArgs.size(); i++) {
AssignmentTestable provided = providedArgs.get(i);
// We'll validate the actually provided value at execution time.
if (provided == null)
continue;
ColumnSpecification expected = makeArgSpec(receiverKs, receiverCf, fun, i);
if (!provided.testAssignment(keyspace, expected).isAssignable())
throw invalidRequest("Type error: %s cannot be passed as argument %d of function %s of type %s", provided, i, fun.name(), expected.type.asCQL3Type());
}
}
use of org.apache.cassandra.cql3.statements.BatchStatement.Type in project cassandra by apache.
the class CreateFunctionStatement method apply.
// TODO: replace affected aggregates !!
public Keyspaces apply(Keyspaces schema) {
if (ifNotExists && orReplace)
throw ire("Cannot use both 'OR REPLACE' and 'IF NOT EXISTS' directives");
UDFunction.assertUdfsEnabled(language);
if (new HashSet<>(argumentNames).size() != argumentNames.size())
throw ire("Duplicate argument names for given function %s with argument names %s", functionName, argumentNames);
rawArgumentTypes.stream().filter(raw -> !raw.isTuple() && raw.isFrozen()).findFirst().ifPresent(t -> {
throw ire("Argument '%s' cannot be frozen; remove frozen<> modifier from '%s'", t, t);
});
if (!rawReturnType.isTuple() && rawReturnType.isFrozen())
throw ire("Return type '%s' cannot be frozen; remove frozen<> modifier from '%s'", rawReturnType, rawReturnType);
KeyspaceMetadata keyspace = schema.getNullable(keyspaceName);
if (null == keyspace)
throw ire("Keyspace '%s' doesn't exist", keyspaceName);
List<AbstractType<?>> argumentTypes = rawArgumentTypes.stream().map(t -> t.prepare(keyspaceName, keyspace.types).getType()).collect(toList());
AbstractType<?> returnType = rawReturnType.prepare(keyspaceName, keyspace.types).getType();
UDFunction function = UDFunction.create(new FunctionName(keyspaceName, functionName), argumentNames, argumentTypes, returnType, calledOnNullInput, language, body);
Function existingFunction = keyspace.functions.find(function.name(), argumentTypes).orElse(null);
if (null != existingFunction) {
if (existingFunction.isAggregate())
throw ire("Function '%s' cannot replace an aggregate", functionName);
if (ifNotExists)
return schema;
if (!orReplace)
throw ire("Function '%s' already exists", functionName);
if (calledOnNullInput != ((UDFunction) existingFunction).isCalledOnNullInput()) {
throw ire("Function '%s' must have %s directive", functionName, calledOnNullInput ? "CALLED ON NULL INPUT" : "RETURNS NULL ON NULL INPUT");
}
if (!returnType.isCompatibleWith(existingFunction.returnType())) {
throw ire("Cannot replace function '%s', the new return type %s is not compatible with the return type %s of existing function", functionName, returnType.asCQL3Type(), existingFunction.returnType().asCQL3Type());
}
// TODO: update dependent aggregates
}
return schema.withAddedOrUpdated(keyspace.withSwapped(keyspace.functions.withAddedOrUpdated(function)));
}
Aggregations