mirror of
https://github.com/scylladb/scylladb.git
synced 2026-05-30 03:30:49 +00:00
cql3: complete translation of alter table statement
Signed-off-by: Paweł Dziepak <pdziepak@scylladb.com>
This commit is contained in:
committed by
Tomasz Grabiec
parent
f24f677dde
commit
0276919819
@@ -79,214 +79,187 @@ void alter_table_statement::validate(distributed<service::storage_proxy>& proxy,
|
||||
|
||||
future<bool> alter_table_statement::announce_migration(distributed<service::storage_proxy>& proxy, bool is_local_only)
|
||||
{
|
||||
throw std::runtime_error(sprint("%s not implemented", __PRETTY_FUNCTION__));
|
||||
#if 0
|
||||
CFMetaData meta = validateColumnFamily(keyspace(), columnFamily());
|
||||
CFMetaData cfm = meta.copy();
|
||||
auto& db = proxy.local().get_db().local();
|
||||
auto schema = validation::validate_column_family(db, keyspace(), column_family());
|
||||
auto cfm = schema_builder(schema);
|
||||
|
||||
CQL3Type validator = this.validator == null ? null : this.validator.prepare(keyspace());
|
||||
ColumnIdentifier columnName = null;
|
||||
ColumnDefinition def = null;
|
||||
if (rawColumnName != null)
|
||||
{
|
||||
columnName = rawColumnName.prepare(cfm);
|
||||
def = cfm.getColumnDefinition(columnName);
|
||||
shared_ptr<cql3_type> validator;
|
||||
if (_validator) {
|
||||
validator = _validator->prepare(db, keyspace());
|
||||
}
|
||||
shared_ptr<column_identifier> column_name;
|
||||
const column_definition* def = nullptr;
|
||||
if (_raw_column_name) {
|
||||
column_name = _raw_column_name->prepare_column_identifier(schema);
|
||||
def = get_column_definition(schema, *column_name);
|
||||
}
|
||||
|
||||
switch (oType)
|
||||
switch (_type) {
|
||||
case alter_table_statement::type::add:
|
||||
{
|
||||
case ADD:
|
||||
assert columnName != null;
|
||||
if (cfm.comparator.isDense())
|
||||
throw new InvalidRequestException("Cannot add new column to a COMPACT STORAGE table");
|
||||
assert(column_name);
|
||||
if (schema->is_dense()) {
|
||||
throw exceptions::invalid_request_exception("Cannot add new column to a COMPACT STORAGE table");
|
||||
}
|
||||
|
||||
if (isStatic)
|
||||
{
|
||||
if (!cfm.comparator.isCompound())
|
||||
throw new InvalidRequestException("Static columns are not allowed in COMPACT STORAGE tables");
|
||||
if (cfm.clusteringColumns().isEmpty())
|
||||
throw new InvalidRequestException("Static columns are only useful (and thus allowed) if the table has at least one clustering column");
|
||||
if (_is_static) {
|
||||
if (!schema->is_compound()) {
|
||||
throw exceptions::invalid_request_exception("Static columns are not allowed in COMPACT STORAGE tables");
|
||||
}
|
||||
if (!schema->clustering_key_size()) {
|
||||
throw exceptions::invalid_request_exception("Static columns are only useful (and thus allowed) if the table has at least one clustering column");
|
||||
}
|
||||
}
|
||||
|
||||
if (def) {
|
||||
if (def->is_partition_key()) {
|
||||
throw exceptions::invalid_request_exception(sprint("Invalid column name %s because it conflicts with a PRIMARY KEY part", column_name));
|
||||
} else {
|
||||
throw exceptions::invalid_request_exception(sprint("Invalid column name %s because it conflicts with an existing column", column_name));
|
||||
}
|
||||
}
|
||||
|
||||
// Cannot re-add a dropped counter column. See #7831.
|
||||
if (schema->is_counter() && schema->dropped_columns().count(column_name->text())) {
|
||||
throw exceptions::invalid_request_exception(sprint("Cannot re-add previously dropped counter column %s", column_name));
|
||||
}
|
||||
|
||||
auto type = validator->get_type();
|
||||
if (type->is_collection() && type->is_multi_cell()) {
|
||||
if (!schema->is_compound()) {
|
||||
throw exceptions::invalid_request_exception("Cannot use non-frozen collections with a non-composite PRIMARY KEY");
|
||||
}
|
||||
if (schema->is_super()) {
|
||||
throw exceptions::invalid_request_exception("Cannot use non-frozen collections with super column families");
|
||||
}
|
||||
}
|
||||
|
||||
cfm.with_column(column_name->name(), type, _is_static ? column_kind::static_column : column_kind::regular_column);
|
||||
break;
|
||||
}
|
||||
case alter_table_statement::type::alter:
|
||||
{
|
||||
assert(column_name);
|
||||
if (!def) {
|
||||
throw exceptions::invalid_request_exception(sprint("Column %s was not found in table %s", column_name, column_family()));
|
||||
}
|
||||
|
||||
auto type = validator->get_type();
|
||||
switch (def->kind) {
|
||||
case column_kind::partition_key:
|
||||
if (type->is_counter()) {
|
||||
throw exceptions::invalid_request_exception(sprint("counter type is not supported for PRIMARY KEY part %s", column_name));
|
||||
}
|
||||
|
||||
if (def != null)
|
||||
{
|
||||
switch (def.kind)
|
||||
{
|
||||
case PARTITION_KEY:
|
||||
case CLUSTERING_COLUMN:
|
||||
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));
|
||||
if (!type->is_value_compatible_with(*def->type)) {
|
||||
throw exceptions::configuration_exception(sprint("Cannot change %s from type %s to type %s: types are incompatible.",
|
||||
column_name,
|
||||
def->type->as_cql3_type(),
|
||||
validator));
|
||||
}
|
||||
break;
|
||||
|
||||
case column_kind::clustering_key:
|
||||
if (!schema->is_cql3_table()) {
|
||||
throw exceptions::invalid_request_exception(sprint("Cannot alter clustering column %s in a non-CQL3 table", column_name));
|
||||
}
|
||||
|
||||
// Note that CFMetaData.validateCompatibility already validate the change we're about to do. However, the error message it
|
||||
// sends is a bit cryptic for a CQL3 user, so validating here for a sake of returning a better error message
|
||||
// Do note that we need isCompatibleWith here, not just isValueCompatibleWith.
|
||||
if (!type->is_compatible_with(*def->type)) {
|
||||
throw exceptions::configuration_exception(sprint("Cannot change %s from type %s to type %s: types are not order-compatible.",
|
||||
column_name,
|
||||
def->type->as_cql3_type(),
|
||||
validator));
|
||||
}
|
||||
break;
|
||||
|
||||
case column_kind::compact_column:
|
||||
case column_kind::regular_column:
|
||||
case column_kind::static_column:
|
||||
// Thrift allows to change a column validator so CFMetaData.validateCompatibility will let it slide
|
||||
// if we change to an incompatible type (contrarily to the comparator case). But we don't want to
|
||||
// allow it for CQL3 (see #5882) so validating it explicitly here. We only care about value compatibility
|
||||
// though since we won't compare values (except when there is an index, but that is validated by
|
||||
// ColumnDefinition already).
|
||||
if (!type->is_value_compatible_with(*def->type)) {
|
||||
throw exceptions::configuration_exception(sprint("Cannot change %s from type %s to type %s: types are incompatible.",
|
||||
column_name,
|
||||
def->type->as_cql3_type(),
|
||||
validator));
|
||||
}
|
||||
break;
|
||||
}
|
||||
// In any case, we update the column definition
|
||||
cfm.with_altered_column_type(column_name->name(), type);
|
||||
break;
|
||||
}
|
||||
case alter_table_statement::type::drop:
|
||||
assert(column_name);
|
||||
if (!schema->is_cql3_table()) {
|
||||
throw exceptions::invalid_request_exception("Cannot drop columns from a non-CQL3 table");
|
||||
}
|
||||
if (!def) {
|
||||
throw exceptions::invalid_request_exception(sprint("Column %s was not found in table %s", column_name, column_family()));
|
||||
}
|
||||
|
||||
if (def->is_primary_key()) {
|
||||
throw exceptions::invalid_request_exception(sprint("Cannot drop PRIMARY KEY part %s", column_name));
|
||||
} else {
|
||||
for (auto&& column_def : boost::range::join(schema->static_columns(), schema->regular_columns())) { // find
|
||||
if (column_def.name() == column_name->name()) {
|
||||
cfm.without_column(column_name->name());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// Cannot re-add a dropped counter column. See #7831.
|
||||
if (meta.isCounter() && meta.getDroppedColumns().containsKey(columnName))
|
||||
throw new InvalidRequestException(String.format("Cannot re-add previously dropped counter column %s", columnName));
|
||||
case alter_table_statement::type::opts:
|
||||
if (!_properties) {
|
||||
throw exceptions::invalid_request_exception("ALTER COLUMNFAMILY WITH invoked, but no parameters found");
|
||||
}
|
||||
|
||||
AbstractType<?> type = validator.getType();
|
||||
if (type.isCollection() && type.isMultiCell())
|
||||
{
|
||||
if (!cfm.comparator.supportCollections())
|
||||
throw new InvalidRequestException("Cannot use non-frozen collections with a non-composite PRIMARY KEY");
|
||||
if (cfm.isSuper())
|
||||
throw new InvalidRequestException("Cannot use non-frozen collections with super column families");
|
||||
_properties->validate();
|
||||
|
||||
// If there used to be a collection column with the same name (that has been dropped), it will
|
||||
// still be appear in the ColumnToCollectionType because or reasons explained on #6276. The same
|
||||
// reason mean that we can't allow adding a new collection with that name (see the ticket for details).
|
||||
if (cfm.comparator.hasCollections())
|
||||
{
|
||||
CollectionType previous = cfm.comparator.collectionType() == null ? null : cfm.comparator.collectionType().defined.get(columnName.bytes);
|
||||
if (previous != null && !type.isCompatibleWith(previous))
|
||||
throw new InvalidRequestException(String.format("Cannot add a collection with the name %s " +
|
||||
"because a collection with the same name and a different type has already been used in the past", columnName));
|
||||
}
|
||||
if (schema->is_counter() && _properties->get_default_time_to_live() > 0) {
|
||||
throw exceptions::invalid_request_exception("Cannot set default_time_to_live on a table with counters");
|
||||
}
|
||||
|
||||
cfm.comparator = cfm.comparator.addOrUpdateCollection(columnName, (CollectionType)type);
|
||||
_properties->apply_to_builder(cfm);
|
||||
break;
|
||||
|
||||
case alter_table_statement::type::rename:
|
||||
for (auto&& entry : _renames) {
|
||||
auto from = entry.first->prepare_column_identifier(schema);
|
||||
auto to = entry.second->prepare_column_identifier(schema);
|
||||
|
||||
auto def = schema->get_column_definition(from->name());
|
||||
if (!def) {
|
||||
throw exceptions::invalid_request_exception(sprint("Cannot rename unknown column %s in table %s", from, column_family()));
|
||||
}
|
||||
|
||||
Integer componentIndex = cfm.comparator.isCompound() ? cfm.comparator.clusteringPrefixSize() : null;
|
||||
cfm.addColumnDefinition(isStatic
|
||||
? ColumnDefinition.staticDef(cfm, columnName.bytes, type, componentIndex)
|
||||
: ColumnDefinition.regularDef(cfm, columnName.bytes, type, componentIndex));
|
||||
break;
|
||||
|
||||
case ALTER:
|
||||
assert columnName != null;
|
||||
if (def == null)
|
||||
throw new InvalidRequestException(String.format("Column %s was not found in table %s", columnName, columnFamily()));
|
||||
|
||||
AbstractType<?> validatorType = validator.getType();
|
||||
switch (def.kind)
|
||||
{
|
||||
case PARTITION_KEY:
|
||||
if (validatorType instanceof CounterColumnType)
|
||||
throw new InvalidRequestException(String.format("counter type is not supported for PRIMARY KEY part %s", columnName));
|
||||
if (cfm.getKeyValidator() instanceof CompositeType)
|
||||
{
|
||||
List<AbstractType<?>> oldTypes = ((CompositeType) cfm.getKeyValidator()).types;
|
||||
if (!validatorType.isValueCompatibleWith(oldTypes.get(def.position())))
|
||||
throw new ConfigurationException(String.format("Cannot change %s from type %s to type %s: types are incompatible.",
|
||||
columnName,
|
||||
oldTypes.get(def.position()).asCQL3Type(),
|
||||
validator));
|
||||
|
||||
List<AbstractType<?>> newTypes = new ArrayList<AbstractType<?>>(oldTypes);
|
||||
newTypes.set(def.position(), validatorType);
|
||||
cfm.keyValidator(CompositeType.getInstance(newTypes));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!validatorType.isValueCompatibleWith(cfm.getKeyValidator()))
|
||||
throw new ConfigurationException(String.format("Cannot change %s from type %s to type %s: types are incompatible.",
|
||||
columnName,
|
||||
cfm.getKeyValidator().asCQL3Type(),
|
||||
validator));
|
||||
cfm.keyValidator(validatorType);
|
||||
}
|
||||
break;
|
||||
case CLUSTERING_COLUMN:
|
||||
if (!cfm.isCQL3Table())
|
||||
throw new InvalidRequestException(String.format("Cannot alter clustering column %s in a non-CQL3 table", columnName));
|
||||
|
||||
AbstractType<?> oldType = cfm.comparator.subtype(def.position());
|
||||
// Note that CFMetaData.validateCompatibility already validate the change we're about to do. However, the error message it
|
||||
// sends is a bit cryptic for a CQL3 user, so validating here for a sake of returning a better error message
|
||||
// Do note that we need isCompatibleWith here, not just isValueCompatibleWith.
|
||||
if (!validatorType.isCompatibleWith(oldType))
|
||||
throw new ConfigurationException(String.format("Cannot change %s from type %s to type %s: types are not order-compatible.",
|
||||
columnName,
|
||||
oldType.asCQL3Type(),
|
||||
validator));
|
||||
|
||||
cfm.comparator = cfm.comparator.setSubtype(def.position(), validatorType);
|
||||
break;
|
||||
case COMPACT_VALUE:
|
||||
// See below
|
||||
if (!validatorType.isValueCompatibleWith(cfm.getDefaultValidator()))
|
||||
throw new ConfigurationException(String.format("Cannot change %s from type %s to type %s: types are incompatible.",
|
||||
columnName,
|
||||
cfm.getDefaultValidator().asCQL3Type(),
|
||||
validator));
|
||||
cfm.defaultValidator(validatorType);
|
||||
break;
|
||||
case REGULAR:
|
||||
case STATIC:
|
||||
// Thrift allows to change a column validator so CFMetaData.validateCompatibility will let it slide
|
||||
// if we change to an incompatible type (contrarily to the comparator case). But we don't want to
|
||||
// allow it for CQL3 (see #5882) so validating it explicitly here. We only care about value compatibility
|
||||
// though since we won't compare values (except when there is an index, but that is validated by
|
||||
// ColumnDefinition already).
|
||||
if (!validatorType.isValueCompatibleWith(def.type))
|
||||
throw new ConfigurationException(String.format("Cannot change %s from type %s to type %s: types are incompatible.",
|
||||
columnName,
|
||||
def.type.asCQL3Type(),
|
||||
validator));
|
||||
|
||||
// For collections, if we alter the type, we need to update the comparator too since it includes
|
||||
// the type too (note that isValueCompatibleWith above has validated that the new type doesn't
|
||||
// change the underlying sorting order, but we still don't want to have a discrepancy between the type
|
||||
// in the comparator and the one in the ColumnDefinition as that would be dodgy).
|
||||
if (validatorType.isCollection() && validatorType.isMultiCell())
|
||||
cfm.comparator = cfm.comparator.addOrUpdateCollection(def.name, (CollectionType)validatorType);
|
||||
|
||||
break;
|
||||
if (schema->get_column_definition(to->name())) {
|
||||
throw exceptions::invalid_request_exception(sprint("Cannot rename column %s to %s in table %s; another column of that name already exist", from, to, column_family()));
|
||||
}
|
||||
// In any case, we update the column definition
|
||||
cfm.addOrReplaceColumnDefinition(def.withNewType(validatorType));
|
||||
break;
|
||||
|
||||
case DROP:
|
||||
assert columnName != null;
|
||||
if (!cfm.isCQL3Table())
|
||||
throw new InvalidRequestException("Cannot drop columns from a non-CQL3 table");
|
||||
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_COLUMN:
|
||||
throw new InvalidRequestException(String.format("Cannot drop PRIMARY KEY part %s", columnName));
|
||||
case REGULAR:
|
||||
case STATIC:
|
||||
ColumnDefinition toDelete = null;
|
||||
for (ColumnDefinition columnDef : cfm.regularAndStaticColumns())
|
||||
{
|
||||
if (columnDef.name.equals(columnName))
|
||||
toDelete = columnDef;
|
||||
}
|
||||
assert toDelete != null;
|
||||
cfm.removeColumnDefinition(toDelete);
|
||||
cfm.recordColumnDrop(toDelete);
|
||||
break;
|
||||
if (def->is_part_of_cell_name()) {
|
||||
throw exceptions::invalid_request_exception(sprint("Cannot rename non PRIMARY KEY part %s", from));
|
||||
}
|
||||
break;
|
||||
case OPTS:
|
||||
if (cfProps == null)
|
||||
throw new InvalidRequestException(String.format("ALTER COLUMNFAMILY WITH invoked, but no parameters found"));
|
||||
|
||||
cfProps.validate();
|
||||
|
||||
if (meta.isCounter() && cfProps.getDefaultTimeToLive() > 0)
|
||||
throw new InvalidRequestException("Cannot set default_time_to_live on a table with counters");
|
||||
|
||||
cfProps.applyToCFMetadata(cfm);
|
||||
break;
|
||||
case RENAME:
|
||||
for (Map.Entry<ColumnIdentifier.Raw, ColumnIdentifier.Raw> entry : renames.entrySet())
|
||||
{
|
||||
ColumnIdentifier from = entry.getKey().prepare(cfm);
|
||||
ColumnIdentifier to = entry.getValue().prepare(cfm);
|
||||
cfm.renameColumn(from, to);
|
||||
if (def->is_indexed()) {
|
||||
throw exceptions::invalid_request_exception(sprint("Cannot rename column %s because it is secondary indexed", from));
|
||||
}
|
||||
break;
|
||||
|
||||
cfm.with_column_rename(from->name(), to->name());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
MigrationManager.announceColumnFamilyUpdate(cfm, false, isLocalOnly);
|
||||
return true;
|
||||
#endif
|
||||
return service::get_local_migration_manager().announce_column_family_update(cfm.build(), false, is_local_only).then([] {
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
shared_ptr<transport::event::schema_change> alter_table_statement::change_event()
|
||||
|
||||
Reference in New Issue
Block a user