cql3: complete translation of alter table statement

Signed-off-by: Paweł Dziepak <pdziepak@scylladb.com>
This commit is contained in:
Paweł Dziepak
2016-01-05 14:04:58 +01:00
committed by Tomasz Grabiec
parent f24f677dde
commit 0276919819

View File

@@ -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()