use of org.apache.cassandra.schema.TableParams 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.schema.TableParams in project cassandra by apache.
the class AlterViewStatement method announceMigration.
public Event.SchemaChange announceMigration(QueryState queryState, boolean isLocalOnly) throws RequestValidationException {
TableMetadata meta = Schema.instance.validateTable(keyspace(), columnFamily());
if (!meta.isView())
throw new InvalidRequestException("Cannot use ALTER MATERIALIZED VIEW on Table");
ViewMetadata current = Schema.instance.getView(keyspace(), columnFamily());
if (attrs == null)
throw new InvalidRequestException("ALTER MATERIALIZED VIEW WITH invoked, but no parameters found");
attrs.validate();
TableParams params = attrs.asAlteredTableParams(current.metadata.params);
if (params.gcGraceSeconds == 0) {
throw new InvalidRequestException("Cannot alter gc_grace_seconds 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 (params.defaultTimeToLive > 0) {
throw new InvalidRequestException("Cannot set or alter default_time_to_live for a materialized view. " + "Data in a materialized view always expire at the same time than " + "the corresponding data in the parent table.");
}
ViewMetadata updated = current.copy(current.metadata.unbuild().params(params).build());
MigrationManager.announceViewUpdate(updated, isLocalOnly);
return new Event.SchemaChange(Event.SchemaChange.Change.UPDATED, Event.SchemaChange.Target.TABLE, keyspace(), columnFamily());
}
use of org.apache.cassandra.schema.TableParams in project cassandra by apache.
the class CreateViewStatement method announceMigration.
public Event.SchemaChange announceMigration(QueryState queryState, boolean isLocalOnly) throws RequestValidationException {
// We need to make sure that:
// - primary key includes all columns in base table's primary key
// - make sure that the select statement does not have anything other than columns
// and their names match the base table's names
// - make sure that primary key does not include any collections
// - make sure there is no where clause in the select statement
// - make sure there is not currently a table or view
// - make sure baseTable gcGraceSeconds > 0
properties.validate();
if (properties.useCompactStorage)
throw new InvalidRequestException("Cannot use 'COMPACT STORAGE' when defining a materialized view");
// specific replica would break
if (!baseName.getKeyspace().equals(keyspace()))
throw new InvalidRequestException("Cannot create a materialized view on a table in a separate keyspace");
TableMetadata metadata = Schema.instance.validateTable(baseName.getKeyspace(), baseName.getColumnFamily());
if (metadata.isCounter())
throw new InvalidRequestException("Materialized views are not supported on counter tables");
if (metadata.isView())
throw new InvalidRequestException("Materialized views cannot be created against other materialized views");
if (metadata.params.gcGraceSeconds == 0) {
throw new InvalidRequestException(String.format("Cannot create materialized view '%s' for base table " + "'%s' with gc_grace_seconds of 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.", cfName.getColumnFamily(), baseName.getColumnFamily()));
}
Set<ColumnIdentifier> included = Sets.newHashSetWithExpectedSize(selectClause.size());
for (RawSelector selector : selectClause) {
Selectable.Raw selectable = selector.selectable;
if (selectable instanceof Selectable.WithFieldSelection.Raw)
throw new InvalidRequestException("Cannot select out a part of type when defining a materialized view");
if (selectable instanceof Selectable.WithFunction.Raw)
throw new InvalidRequestException("Cannot use function when defining a materialized view");
if (selectable instanceof Selectable.WritetimeOrTTL.Raw)
throw new InvalidRequestException("Cannot use function when defining a materialized view");
if (selector.alias != null)
throw new InvalidRequestException("Cannot use alias when defining a materialized view");
Selectable s = selectable.prepare(metadata);
if (s instanceof Term.Raw)
throw new InvalidRequestException("Cannot use terms in selection when defining a materialized view");
ColumnMetadata cdef = (ColumnMetadata) s;
included.add(cdef.name);
}
Set<ColumnMetadata.Raw> targetPrimaryKeys = new HashSet<>();
for (ColumnMetadata.Raw identifier : Iterables.concat(partitionKeys, clusteringKeys)) {
if (!targetPrimaryKeys.add(identifier))
throw new InvalidRequestException("Duplicate entry found in PRIMARY KEY: " + identifier);
ColumnMetadata cdef = identifier.prepare(metadata);
if (cdef.type.isMultiCell())
throw new InvalidRequestException(String.format("Cannot use MultiCell column '%s' in PRIMARY KEY of materialized view", identifier));
if (cdef.isStatic())
throw new InvalidRequestException(String.format("Cannot use Static column '%s' in PRIMARY KEY of materialized view", identifier));
if (cdef.type instanceof DurationType)
throw new InvalidRequestException(String.format("Cannot use Duration column '%s' in PRIMARY KEY of materialized view", identifier));
}
// build the select statement
Map<ColumnMetadata.Raw, Boolean> orderings = Collections.emptyMap();
List<ColumnMetadata.Raw> groups = Collections.emptyList();
SelectStatement.Parameters parameters = new SelectStatement.Parameters(orderings, groups, false, true, false);
SelectStatement.RawStatement rawSelect = new SelectStatement.RawStatement(baseName, parameters, selectClause, whereClause, null, null);
ClientState state = ClientState.forInternalCalls();
state.setKeyspace(keyspace());
rawSelect.prepareKeyspace(state);
rawSelect.setBoundVariables(getBoundVariables());
ParsedStatement.Prepared prepared = rawSelect.prepare(true);
SelectStatement select = (SelectStatement) prepared.statement;
StatementRestrictions restrictions = select.getRestrictions();
if (!prepared.boundNames.isEmpty())
throw new InvalidRequestException("Cannot use query parameters in CREATE MATERIALIZED VIEW statements");
String whereClauseText = View.relationsToWhereClause(whereClause.relations);
Set<ColumnIdentifier> basePrimaryKeyCols = new HashSet<>();
for (ColumnMetadata definition : Iterables.concat(metadata.partitionKeyColumns(), metadata.clusteringColumns())) basePrimaryKeyCols.add(definition.name);
List<ColumnIdentifier> targetClusteringColumns = new ArrayList<>();
List<ColumnIdentifier> targetPartitionKeys = new ArrayList<>();
// This is only used as an intermediate state; this is to catch whether multiple non-PK columns are used
boolean hasNonPKColumn = false;
for (ColumnMetadata.Raw raw : partitionKeys) hasNonPKColumn |= getColumnIdentifier(metadata, basePrimaryKeyCols, hasNonPKColumn, raw, targetPartitionKeys, restrictions);
for (ColumnMetadata.Raw raw : clusteringKeys) hasNonPKColumn |= getColumnIdentifier(metadata, basePrimaryKeyCols, hasNonPKColumn, raw, targetClusteringColumns, restrictions);
// We need to include all of the primary key columns from the base table in order to make sure that we do not
// overwrite values in the view. We cannot support "collapsing" the base table into a smaller number of rows in
// the view because if we need to generate a tombstone, we have no way of knowing which value is currently being
// used in the view and whether or not to generate a tombstone. In order to not surprise our users, we require
// that they include all of the columns. We provide them with a list of all of the columns left to include.
boolean missingClusteringColumns = false;
StringBuilder columnNames = new StringBuilder();
List<ColumnIdentifier> includedColumns = new ArrayList<>();
for (ColumnMetadata def : metadata.columns()) {
ColumnIdentifier identifier = def.name;
boolean includeDef = included.isEmpty() || included.contains(identifier);
if (includeDef && def.isStatic()) {
throw new InvalidRequestException(String.format("Unable to include static column '%s' which would be included by Materialized View SELECT * statement", identifier));
}
boolean defInTargetPrimaryKey = targetClusteringColumns.contains(identifier) || targetPartitionKeys.contains(identifier);
if (includeDef && !defInTargetPrimaryKey) {
includedColumns.add(identifier);
}
if (!def.isPrimaryKeyColumn())
continue;
if (!defInTargetPrimaryKey) {
if (missingClusteringColumns)
columnNames.append(',');
else
missingClusteringColumns = true;
columnNames.append(identifier);
}
}
if (missingClusteringColumns)
throw new InvalidRequestException(String.format("Cannot create Materialized View %s without primary key columns from base %s (%s)", columnFamily(), baseName.getColumnFamily(), columnNames.toString()));
if (targetPartitionKeys.isEmpty())
throw new InvalidRequestException("Must select at least a column for a Materialized View");
if (targetClusteringColumns.isEmpty())
throw new InvalidRequestException("No columns are defined for Materialized View other than primary key");
TableParams params = properties.properties.asNewTableParams();
if (params.defaultTimeToLive > 0) {
throw new InvalidRequestException("Cannot set default_time_to_live for a materialized view. " + "Data in a materialized view always expire at the same time than " + "the corresponding data in the parent table.");
}
TableMetadata.Builder builder = TableMetadata.builder(keyspace(), columnFamily(), properties.properties.getId()).isView(true).params(params);
add(metadata, targetPartitionKeys, builder::addPartitionKeyColumn);
add(metadata, targetClusteringColumns, builder::addClusteringColumn);
add(metadata, includedColumns, builder::addRegularColumn);
ViewMetadata definition = new ViewMetadata(keyspace(), columnFamily(), metadata.id, metadata.name, included.isEmpty(), rawSelect, whereClauseText, builder.build());
try {
MigrationManager.announceNewView(definition, isLocalOnly);
return new Event.SchemaChange(Event.SchemaChange.Change.CREATED, Event.SchemaChange.Target.TABLE, keyspace(), columnFamily());
} catch (AlreadyExistsException e) {
if (ifNotExists)
return null;
throw e;
}
}
Aggregations