Use CQL type names in schema metadata tables

patch by Aleksey Yeschenko; reviewed by Sylvain Lebresne for
CASSANDRA-10365
This commit is contained in:
Aleksey Yeschenko 2015-09-21 12:02:53 -07:00
parent f4af154637
commit 340df43fb7
31 changed files with 1290 additions and 997 deletions

View File

@ -1,4 +1,5 @@
3.0
* Use CQL type names in schema metadata tables (CASSANDRA-10365)
* Guard batchlog replay against integer division by zero (CASSANDRA-9223)
* Fix bug when adding a column to thrift with the same name than a primary key (CASSANDRA-10608)
* Add client address argument to IAuthenticator::newSaslNegotiator (CASSANDRA-8068)

View File

@ -83,3 +83,7 @@ BSD 3-clause
ASM
(http://asm.ow2.org/)
Copyright (c) 2000-2011 INRIA, France Telecom
JGraphT
(http://jgrapht.org)
Copyright 2003-2015, by Barak Naveh and Contributors.

View File

@ -406,6 +406,7 @@
<dependency groupId="org.mindrot" artifactId="jbcrypt" version="0.3m" />
<dependency groupId="io.airlift" artifactId="airline" version="0.6" />
<dependency groupId="io.netty" artifactId="netty-all" version="4.0.23.Final" />
<dependency groupId="org.jgrapht" artifactId="jgrapht-core" version="0.9.1" />
<dependency groupId="com.google.code.findbugs" artifactId="jsr305" version="2.0.2" />
<dependency groupId="com.clearspring.analytics" artifactId="stream" version="2.5.2" />
<!-- TODO CASSANDRA-9543
@ -561,16 +562,14 @@
<!-- don't need jna to run, but nice to have -->
<dependency groupId="net.java.dev.jna" artifactId="jna"/>
<!-- don't need jamm unless running a server in which case it needs to be a -javagent to be used anyway -->
<dependency groupId="com.github.jbellis" artifactId="jamm"/>
<dependency groupId="io.netty" artifactId="netty-all"/>
<dependency groupId="joda-time" artifactId="joda-time"/>
<dependency groupId="org.jgrapht" artifactId="jgrapht-core"/>
<dependency groupId="joda-time" artifactId="joda-time"/>
<dependency groupId="org.fusesource" artifactId="sigar"/>
<dependency groupId="org.eclipse.jdt.core.compiler" artifactId="ecj" />
</artifact:pom>
<artifact:pom id="thrift-pom"

BIN
lib/jgrapht-core-0.9.1.jar Normal file

Binary file not shown.

View File

@ -0,0 +1,227 @@
Eclipse Public License - v 1.0
THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF
THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
1. DEFINITIONS
"Contribution" means:
a) in the case of the initial Contributor, the initial code and
documentation distributed under this Agreement, and
b) in the case of each subsequent Contributor:
i) changes to the Program, and
ii) additions to the Program;
where such changes and/or additions to the Program originate from and
are distributed by that particular Contributor. A Contribution
'originates' from a Contributor if it was added to the Program by such
Contributor itself or anyone acting on such Contributor's behalf.
Contributions do not include additions to the Program which: (i) are
separate modules of software distributed in conjunction with the
Program under their own license agreement, and (ii) are not derivative
works of the Program.
"Contributor" means any person or entity that distributes the Program.
"Licensed Patents" mean patent claims licensable by a Contributor which
are necessarily infringed by the use or sale of its Contribution alone
or when combined with the Program.
"Program" means the Contributions distributed in accordance with this
Agreement.
"Recipient" means anyone who receives the Program under this Agreement,
including all Contributors.
2. GRANT OF RIGHTS
a) Subject to the terms of this Agreement, each Contributor hereby
grants Recipient a non-exclusive, worldwide, royalty-free copyright
license to reproduce, prepare derivative works of, publicly display,
publicly perform, distribute and sublicense the Contribution of such
Contributor, if any, and such derivative works, in source code and
object code form.
b) Subject to the terms of this Agreement, each Contributor hereby
grants Recipient a non-exclusive, worldwide, royalty-free patent
license under Licensed Patents to make, use, sell, offer to sell,
import and otherwise transfer the Contribution of such Contributor, if
any, in source code and object code form. This patent license shall
apply to the combination of the Contribution and the Program if, at the
time the Contribution is added by the Contributor, such addition of the
Contribution causes such combination to be covered by the Licensed
Patents. The patent license shall not apply to any other combinations
which include the Contribution. No hardware per se is licensed
hereunder.
c) Recipient understands that although each Contributor grants the
licenses to its Contributions set forth herein, no assurances are
provided by any Contributor that the Program does not infringe the
patent or other intellectual property rights of any other entity. Each
Contributor disclaims any liability to Recipient for claims brought by
any other entity based on infringement of intellectual property rights
or otherwise. As a condition to exercising the rights and licenses
granted hereunder, each Recipient hereby assumes sole responsibility to
secure any other intellectual property rights needed, if any. For
example, if a third party patent license is required to allow Recipient
to distribute the Program, it is Recipient's responsibility to acquire
that license before distributing the Program.
d) Each Contributor represents that to its knowledge it has sufficient
copyright rights in its Contribution, if any, to grant the copyright
license set forth in this Agreement.
3. REQUIREMENTS
A Contributor may choose to distribute the Program in object code form
under its own license agreement, provided that:
a) it complies with the terms and conditions of this Agreement; and
b) its license agreement:
i) effectively disclaims on behalf of all Contributors all warranties
and conditions, express and implied, including warranties or conditions
of title and non-infringement, and implied warranties or conditions of
merchantability and fitness for a particular purpose;
ii) effectively excludes on behalf of all Contributors all liability
for damages, including direct, indirect, special, incidental and
consequential damages, such as lost profits;
iii) states that any provisions which differ from this Agreement are
offered by that Contributor alone and not by any other party; and
iv) states that source code for the Program is available from such
Contributor, and informs licensees how to obtain it in a reasonable
manner on or through a medium customarily used for software exchange.
When the Program is made available in source code form:
a) it must be made available under this Agreement; and
b) a copy of this Agreement must be included with each copy of the
Program.
Contributors may not remove or alter any copyright notices contained
within the Program.
Each Contributor must identify itself as the originator of its
Contribution, if any, in a manner that reasonably allows subsequent
Recipients to identify the originator of the Contribution.
4. COMMERCIAL DISTRIBUTION
Commercial distributors of software may accept certain responsibilities
with respect to end users, business partners and the like. While this
license is intended to facilitate the commercial use of the Program,
the Contributor who includes the Program in a commercial product
offering should do so in a manner which does not create potential
liability for other Contributors. Therefore, if a Contributor includes
the Program in a commercial product offering, such Contributor
("Commercial Contributor") hereby agrees to defend and indemnify every
other Contributor ("Indemnified Contributor") against any losses,
damages and costs (collectively "Losses") arising from claims, lawsuits
and other legal actions brought by a third party against the
Indemnified Contributor to the extent caused by the acts or omissions
of such Commercial Contributor in connection with its distribution of
the Program in a commercial product offering. The obligations in this
section do not apply to any claims or Losses relating to any actual or
alleged intellectual property infringement. In order to qualify, an
Indemnified Contributor must: a) promptly notify the Commercial
Contributor in writing of such claim, and b) allow the Commercial
Contributor to control, and cooperate with the Commercial Contributor
in, the defense and any related settlement negotiations. The
Indemnified Contributor may participate in any such claim at its own
expense.
For example, a Contributor might include the Program in a commercial
product offering, Product X. That Contributor is then a Commercial
Contributor. If that Commercial Contributor then makes performance
claims, or offers warranties related to Product X, those performance
claims and warranties are such Commercial Contributor's responsibility
alone. Under this section, the Commercial Contributor would have to
defend claims against the other Contributors related to those
performance claims and warranties, and if a court requires any other
Contributor to pay any damages as a result, the Commercial Contributor
must pay those damages.
5. NO WARRANTY
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS
PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR
FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible
for determining the appropriateness of using and distributing the
Program and assumes all risks associated with its exercise of rights
under this Agreement , including but not limited to the risks and costs
of program errors, compliance with applicable laws, damage to or loss
of data, programs or equipment, and unavailability or interruption of
operations.
6. DISCLAIMER OF LIABILITY
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR
ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING
WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR
DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
7. GENERAL
If any provision of this Agreement is invalid or unenforceable under
applicable law, it shall not affect the validity or enforceability of
the remainder of the terms of this Agreement, and without further
action by the parties hereto, such provision shall be reformed to the
minimum extent necessary to make such provision valid and enforceable.
If Recipient institutes patent litigation against any entity (including
a cross-claim or counterclaim in a lawsuit) alleging that the Program
itself (excluding combinations of the Program with other software or
hardware) infringes such Recipient's patent(s), then such Recipient's
rights granted under Section 2(b) shall terminate as of the date such
litigation is filed.
All Recipient's rights under this Agreement shall terminate if it fails
to comply with any of the material terms or conditions of this
Agreement and does not cure such failure in a reasonable period of time
after becoming aware of such noncompliance. If all Recipient's rights
under this Agreement terminate, Recipient agrees to cease use and
distribution of the Program as soon as reasonably practicable. However,
Recipient's obligations under this Agreement and any licenses granted
by Recipient relating to the Program shall continue and survive.
Everyone is permitted to copy and distribute copies of this Agreement,
but in order to avoid inconsistency the Agreement is copyrighted and
may only be modified in the following manner. The Agreement Steward
reserves the right to publish new versions (including revisions) of
this Agreement from time to time. No one other than the Agreement
Steward has the right to modify this Agreement. The Eclipse Foundation
is the initial Agreement Steward. The Eclipse Foundation may assign the
responsibility to serve as the Agreement Steward to a suitable separate
entity. Each new version of the Agreement will be given a
distinguishing version number. The Program (including Contributions)
may always be distributed subject to the version of the Agreement under
which it was received. In addition, after a new version of the
Agreement is published, Contributor may elect to distribute the Program
(including its Contributions) under the new version. Except as
expressly stated in Sections 2(a) and 2(b) above, Recipient receives no
rights or licenses to the intellectual property of any Contributor
under this Agreement, whether expressly, by implication, estoppel or
otherwise. All rights in the Program not expressly granted under this
Agreement are reserved.
This Agreement is governed by the laws of the State of New York and the
intellectual property laws of the United States of America. No party to
this Agreement will bring a legal action under this Agreement more than
one year after the cause of action arose. Each party waives its rights
to a jury trial in any resulting litigation.

View File

@ -401,7 +401,7 @@ public final class CFMetaData
{
CFStatement parsed = (CFStatement)QueryProcessor.parseStatement(cql);
parsed.prepareKeyspace(keyspace);
CreateTableStatement statement = (CreateTableStatement) parsed.prepare().statement;
CreateTableStatement statement = (CreateTableStatement) ((CreateTableStatement.RawStatement) parsed).prepare(Types.none()).statement;
return statement.metadataBuilder()
.withId(generateLegacyCfId(keyspace, statement.columnFamily()))
@ -720,16 +720,6 @@ public final class CFMetaData
.toHashCode();
}
/**
* Updates this object in place to match the definition in the system schema tables.
* @return true if any columns were added, removed, or altered; otherwise, false is returned
*/
public boolean reload()
{
return apply(isView ? SchemaKeyspace.createViewFromName(ksName, cfName).metadata
: SchemaKeyspace.createTableFromName(ksName, cfName));
}
/**
* Updates CFMetaData in-place to match cfm
*

View File

@ -122,7 +122,7 @@ public class Schema
*/
public Schema loadFromDisk(boolean updateVersion)
{
load(SchemaKeyspace.readSchemaFromSystemTables());
load(SchemaKeyspace.fetchNonSystemKeyspaces());
if (updateVersion)
updateVersion();
return this;
@ -135,7 +135,7 @@ public class Schema
*
* @return self to support chaining calls
*/
public Schema load(Collection<KeyspaceMetadata> keyspaceDefs)
public Schema load(Iterable<KeyspaceMetadata> keyspaceDefs)
{
keyspaceDefs.forEach(this::load);
return this;
@ -354,6 +354,16 @@ public class Schema
return keyspaces.keySet();
}
public Keyspaces getKeyspaces(Set<String> includedKeyspaceNames)
{
Keyspaces.Builder builder = Keyspaces.builder();
keyspaces.values()
.stream()
.filter(k -> includedKeyspaceNames.contains(k.name))
.forEach(builder::add);
return builder.build();
}
/**
* Update (or insert) new keyspace definition
*
@ -611,15 +621,15 @@ public class Schema
MigrationManager.instance.notifyCreateColumnFamily(cfm);
}
public void updateTable(String ksName, String tableName)
public void updateTable(CFMetaData table)
{
CFMetaData cfm = getCFMetaData(ksName, tableName);
assert cfm != null;
boolean columnsDidChange = cfm.reload();
CFMetaData current = getCFMetaData(table.ksName, table.cfName);
assert current != null;
boolean columnsDidChange = current.apply(table);
Keyspace keyspace = Keyspace.open(cfm.ksName);
keyspace.getColumnFamilyStore(cfm.cfName).reload();
MigrationManager.instance.notifyUpdateColumnFamily(cfm, columnsDidChange);
Keyspace keyspace = Keyspace.open(current.ksName);
keyspace.getColumnFamilyStore(current.cfName).reload();
MigrationManager.instance.notifyUpdateColumnFamily(current, columnsDidChange);
}
public void dropTable(String ksName, String tableName)
@ -669,17 +679,15 @@ public class Schema
MigrationManager.instance.notifyCreateView(view);
}
public void updateView(String ksName, String viewName)
public void updateView(ViewDefinition view)
{
Optional<ViewDefinition> optView = getKSMetaData(ksName).views.get(viewName);
assert optView.isPresent();
ViewDefinition view = optView.get();
boolean columnsDidChange = view.metadata.reload();
ViewDefinition current = getKSMetaData(view.ksName).views.get(view.viewName).get();
boolean columnsDidChange = current.metadata.apply(view.metadata);
Keyspace keyspace = Keyspace.open(view.ksName);
keyspace.getColumnFamilyStore(view.viewName).reload();
Keyspace.open(view.ksName).viewManager.update(view.viewName);
MigrationManager.instance.notifyUpdateView(view, columnsDidChange);
Keyspace keyspace = Keyspace.open(current.ksName);
keyspace.getColumnFamilyStore(current.viewName).reload();
Keyspace.open(current.ksName).viewManager.update(current.viewName);
MigrationManager.instance.notifyUpdateView(current, columnsDidChange);
}
public void dropView(String ksName, String viewName)

View File

@ -17,19 +17,21 @@
*/
package org.apache.cassandra.cql3;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.cassandra.config.Schema;
import org.apache.cassandra.db.SystemKeyspace;
import org.apache.cassandra.db.marshal.*;
import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.exceptions.SyntaxException;
import org.apache.cassandra.schema.KeyspaceMetadata;
import org.apache.cassandra.schema.Types;
public interface CQL3Type
{
@ -45,21 +47,22 @@ public interface CQL3Type
BLOB (BytesType.instance),
BOOLEAN (BooleanType.instance),
COUNTER (CounterColumnType.instance),
DATE (SimpleDateType.instance),
DECIMAL (DecimalType.instance),
DOUBLE (DoubleType.instance),
EMPTY (EmptyType.instance),
FLOAT (FloatType.instance),
INET (InetAddressType.instance),
INT (Int32Type.instance),
SMALLINT (ShortType.instance),
TEXT (UTF8Type.instance),
TIME (TimeType.instance),
TIMESTAMP (TimestampType.instance),
TIMEUUID (TimeUUIDType.instance),
TINYINT (ByteType.instance),
UUID (UUIDType.instance),
VARCHAR (UTF8Type.instance),
VARINT (IntegerType.instance),
TIMEUUID (TimeUUIDType.instance),
DATE (SimpleDateType.instance),
TIME (TimeType.instance);
VARINT (IntegerType.instance);
private final AbstractType<?> type;
@ -243,7 +246,7 @@ public interface CQL3Type
@Override
public String toString()
{
return name;
return "frozen<" + ColumnIdentifier.maybeQuote(name) + '>';
}
}
@ -291,14 +294,14 @@ public interface CQL3Type
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("tuple<");
sb.append("frozen<tuple<");
for (int i = 0; i < type.size(); i++)
{
if (i > 0)
sb.append(", ");
sb.append(type.type(i).asCQL3Type());
}
sb.append(">");
sb.append(">>");
return sb.toString();
}
}
@ -342,7 +345,20 @@ public interface CQL3Type
throw new InvalidRequestException(message);
}
public abstract CQL3Type prepare(String keyspace) throws InvalidRequestException;
public CQL3Type prepare(String keyspace)
{
KeyspaceMetadata ksm = Schema.instance.getKSMetaData(keyspace);
if (ksm == null)
throw new ConfigurationException(String.format("Keyspace %s doesn't exist", keyspace));
return prepare(keyspace, ksm.types);
}
public abstract CQL3Type prepare(String keyspace, Types udts) throws InvalidRequestException;
public boolean referencesUserType(String name)
{
return false;
}
public static Raw from(CQL3Type type)
{
@ -382,14 +398,14 @@ public interface CQL3Type
private static class RawType extends Raw
{
private CQL3Type type;
private final CQL3Type type;
private RawType(CQL3Type type)
{
this.type = type;
}
public CQL3Type prepare(String keyspace) throws InvalidRequestException
public CQL3Type prepare(String keyspace, Types udts) throws InvalidRequestException
{
return type;
}
@ -443,7 +459,7 @@ public interface CQL3Type
return true;
}
public CQL3Type prepare(String keyspace) throws InvalidRequestException
public CQL3Type prepare(String keyspace, Types udts) throws InvalidRequestException
{
assert values != null : "Got null values type for a collection";
@ -461,16 +477,21 @@ public interface CQL3Type
switch (kind)
{
case LIST:
return new Collection(ListType.getInstance(values.prepare(keyspace).getType(), !frozen));
return new Collection(ListType.getInstance(values.prepare(keyspace, udts).getType(), !frozen));
case SET:
return new Collection(SetType.getInstance(values.prepare(keyspace).getType(), !frozen));
return new Collection(SetType.getInstance(values.prepare(keyspace, udts).getType(), !frozen));
case MAP:
assert keys != null : "Got null keys type for a collection";
return new Collection(MapType.getInstance(keys.prepare(keyspace).getType(), values.prepare(keyspace).getType(), !frozen));
return new Collection(MapType.getInstance(keys.prepare(keyspace, udts).getType(), values.prepare(keyspace, udts).getType(), !frozen));
}
throw new AssertionError();
}
public boolean referencesUserType(String name)
{
return (keys != null && keys.referencesUserType(name)) || values.referencesUserType(name);
}
@Override
public String toString()
{
@ -510,13 +531,13 @@ public interface CQL3Type
return false;
}
public CQL3Type prepare(String keyspace) throws InvalidRequestException
public CQL3Type prepare(String keyspace, Types udts) throws InvalidRequestException
{
if (name.hasKeyspace())
{
// The provided keyspace is the one of the current statement this is part of. If it's different from the keyspace of
// the UTName, we reject since we want to limit user types to their own keyspace (see #6643)
if (keyspace != null && !SystemKeyspace.NAME.equals(name.getKeyspace()) && !keyspace.equals(name.getKeyspace()))
if (!keyspace.equals(name.getKeyspace()))
throw new InvalidRequestException(String.format("Statement on keyspace %s cannot refer to a user type in keyspace %s; "
+ "user types can only be used in the keyspace they are defined in",
keyspace, name.getKeyspace()));
@ -526,10 +547,7 @@ public interface CQL3Type
name.setKeyspace(keyspace);
}
KeyspaceMetadata ksm = Schema.instance.getKSMetaData(name.getKeyspace());
if (ksm == null)
throw new InvalidRequestException("Unknown keyspace " + name.getKeyspace());
UserType type = ksm.types.getNullable(name.getUserTypeName());
UserType type = udts.getNullable(name.getUserTypeName());
if (type == null)
throw new InvalidRequestException("Unknown type " + name);
@ -539,6 +557,11 @@ public interface CQL3Type
return new UserDefined(name.toString(), type);
}
public boolean referencesUserType(String name)
{
return this.name.getStringTypeName().equals(name);
}
protected boolean supportsFreezing()
{
return true;
@ -573,14 +596,13 @@ public interface CQL3Type
public void freeze() throws InvalidRequestException
{
for (CQL3Type.Raw t : types)
{
if (t.supportsFreezing())
t.freeze();
}
frozen = true;
}
public CQL3Type prepare(String keyspace) throws InvalidRequestException
public CQL3Type prepare(String keyspace, Types udts) throws InvalidRequestException
{
if (!frozen)
freeze();
@ -591,11 +613,16 @@ public interface CQL3Type
if (t.isCounter())
throw new InvalidRequestException("Counters are not allowed inside tuples");
ts.add(t.prepare(keyspace).getType());
ts.add(t.prepare(keyspace, udts).getType());
}
return new Tuple(new TupleType(ts));
}
public boolean referencesUserType(String name)
{
return types.stream().anyMatch(t -> t.referencesUserType(name));
}
@Override
public String toString()
{

View File

@ -328,7 +328,7 @@ public class ColumnIdentifier extends org.apache.cassandra.cql3.selection.Select
}
}
private static String maybeQuote(String text)
static String maybeQuote(String text)
{
if (UNQUOTED_IDENTIFIER.matcher(text).matches())
return text;

View File

@ -30,7 +30,9 @@ import org.apache.cassandra.cql3.*;
import org.apache.cassandra.db.*;
import org.apache.cassandra.db.marshal.*;
import org.apache.cassandra.exceptions.*;
import org.apache.cassandra.schema.KeyspaceMetadata;
import org.apache.cassandra.schema.TableParams;
import org.apache.cassandra.schema.Types;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.MigrationManager;
import org.apache.cassandra.service.QueryState;
@ -192,6 +194,14 @@ public class CreateTableStatement extends SchemaAlteringStatement
* Transform this raw statement into a CreateTableStatement.
*/
public ParsedStatement.Prepared prepare() throws RequestValidationException
{
KeyspaceMetadata ksm = Schema.instance.getKSMetaData(keyspace());
if (ksm == null)
throw new ConfigurationException(String.format("Keyspace %s doesn't exist", keyspace()));
return prepare(ksm.types);
}
public ParsedStatement.Prepared prepare(Types udts) throws RequestValidationException
{
// Column family name
if (!columnFamily().matches("\\w+"))
@ -212,7 +222,7 @@ public class CreateTableStatement extends SchemaAlteringStatement
for (Map.Entry<ColumnIdentifier, CQL3Type.Raw> entry : definitions.entrySet())
{
ColumnIdentifier id = entry.getKey();
CQL3Type pt = entry.getValue().prepare(keyspace());
CQL3Type pt = entry.getValue().prepare(keyspace(), udts);
if (pt.isCollection() && ((CollectionType)pt.getType()).isMultiCell())
stmt.collections.put(id.bytes, (CollectionType)pt.getType());
if (entry.getValue().isCounter())

View File

@ -24,6 +24,7 @@ import org.slf4j.LoggerFactory;
import org.apache.cassandra.concurrent.Stage;
import org.apache.cassandra.concurrent.StageManager;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.net.IVerbHandler;
import org.apache.cassandra.net.MessageIn;
import org.apache.cassandra.schema.SchemaKeyspace;
@ -45,7 +46,7 @@ public class DefinitionsUpdateVerbHandler implements IVerbHandler<Collection<Mut
StageManager.getStage(Stage.MIGRATION).submit(new WrappedRunnable()
{
public void runMayThrow() throws Exception
public void runMayThrow() throws ConfigurationException
{
SchemaKeyspace.mergeSchemaAndAnnounceVersion(message.payload);
}

View File

@ -377,7 +377,6 @@ public class Keyspace
// re-initializing an existing CF. This will happen if you cleared the schema
// on this node and it's getting repopulated from the rest of the cluster.
assert cfs.name.equals(cfName);
cfs.metadata.reload();
cfs.reload();
}
}

View File

@ -19,6 +19,7 @@ package org.apache.cassandra.db.marshal;
import java.nio.ByteBuffer;
import org.apache.cassandra.cql3.CQL3Type;
import org.apache.cassandra.cql3.Constants;
import org.apache.cassandra.cql3.Term;
import org.apache.cassandra.serializers.TypeSerializer;
@ -65,6 +66,12 @@ public class EmptyType extends AbstractType<Void>
return new Constants.Value(ByteBufferUtil.EMPTY_BYTE_BUFFER);
}
@Override
public CQL3Type asCQL3Type()
{
return CQL3Type.Native.EMPTY;
}
public TypeSerializer<Void> getSerializer()
{
return EmptySerializer.instance;

View File

@ -23,7 +23,6 @@ import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.util.*;
import org.apache.cassandra.cql3.CQL3Type;
import org.apache.cassandra.exceptions.*;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.FBUtilities;
@ -94,50 +93,6 @@ public class TypeParser
return parse(compareWith == null ? null : compareWith.toString());
}
public static String parseCqlNativeType(String str)
{
return CQL3Type.Native.valueOf(str.trim().toUpperCase(Locale.ENGLISH)).getType().toString();
}
public static String parseCqlCollectionOrFrozenType(String str) throws SyntaxException
{
str = str.trim().toLowerCase();
switch (str)
{
case "map": return "MapType";
case "set": return "SetType";
case "list": return "ListType";
case "frozen": return "FrozenType";
default: throw new SyntaxException("Invalid type name" + str);
}
}
/**
* Turns user facing type names into Abstract Types, 'text' -> UTF8Type
*/
public static AbstractType<?> parseCqlName(String str) throws SyntaxException, ConfigurationException
{
return parse(parseCqlNameRecurse(str));
}
private static String parseCqlNameRecurse(String str) throws SyntaxException
{
if (str.indexOf(',') >= 0 && (!str.contains("<") || (str.indexOf(',') < str.indexOf('<'))))
{
String[] parseString = str.split(",", 2);
return parseCqlNameRecurse(parseString[0]) + "," + parseCqlNameRecurse(parseString[1]);
}
else if (str.contains("<"))
{
String[] parseString = str.trim().split("<", 2);
return parseCqlCollectionOrFrozenType(parseString[0]) + "(" + parseCqlNameRecurse(parseString[1].substring(0, parseString[1].length()-1)) + ")";
}
else
{
return parseCqlNativeType(str);
}
}
/**
* Parse an AbstractType from current position of this parser.
*/

View File

@ -25,6 +25,7 @@ import java.util.*;
import org.apache.cassandra.config.*;
import org.apache.cassandra.cql3.*;
import org.apache.cassandra.cql3.statements.CFStatement;
import org.apache.cassandra.cql3.statements.CreateTableStatement;
import org.apache.cassandra.cql3.statements.ParsedStatement;
import org.apache.cassandra.cql3.statements.UpdateStatement;
@ -40,6 +41,7 @@ import org.apache.cassandra.io.sstable.format.SSTableFormat;
import org.apache.cassandra.schema.KeyspaceMetadata;
import org.apache.cassandra.schema.KeyspaceParams;
import org.apache.cassandra.schema.Tables;
import org.apache.cassandra.schema.Types;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.utils.Pair;
@ -348,7 +350,7 @@ public class CQLSSTableWriter implements Closeable
{
synchronized (CQLSSTableWriter.class)
{
this.schema = getStatement(schema, CreateTableStatement.class, "CREATE TABLE").left.getCFMetaData();
this.schema = getTableMetadata(schema);
// We need to register the keyspace/table metadata through Schema, otherwise we won't be able to properly
// build the insert statement in using().
@ -482,6 +484,16 @@ public class CQLSSTableWriter implements Closeable
return this;
}
private static CFMetaData getTableMetadata(String schema)
{
CFStatement parsed = (CFStatement)QueryProcessor.parseStatement(schema);
// tables with UDTs are currently not supported by CQLSSTableWrite, so we just use Types.none(), for now
// see CASSANDRA-10624 for more details
CreateTableStatement statement = (CreateTableStatement) ((CreateTableStatement.RawStatement) parsed).prepare(Types.none()).statement;
statement.validate(ClientState.forInternalCalls());
return statement.getCFMetaData();
}
private static <T extends CQLStatement> Pair<T, List<ColumnSpecification>> getStatement(String query, Class<T> klass, String type)
{
try

View File

@ -0,0 +1,93 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
*/
package org.apache.cassandra.schema;
import com.google.common.collect.ImmutableSet;
import org.antlr.runtime.*;
import org.apache.cassandra.cql3.*;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.marshal.UserType;
import org.apache.cassandra.exceptions.SyntaxException;
import static org.apache.cassandra.utils.ByteBufferUtil.bytes;
public final class CQLTypeParser
{
private static final ImmutableSet<String> PRIMITIVE_TYPES;
static
{
ImmutableSet.Builder<String> builder = ImmutableSet.builder();
for (CQL3Type.Native primitive : CQL3Type.Native.values())
builder.add(primitive.name().toLowerCase());
PRIMITIVE_TYPES = builder.build();
}
public static AbstractType<?> parse(String keyspace, String unparsed, Types userTypes)
{
String lowercased = unparsed.toLowerCase();
// fast path for the common case of a primitive type
if (PRIMITIVE_TYPES.contains(lowercased))
return CQL3Type.Native.valueOf(unparsed.toUpperCase()).getType();
// special-case top-level UDTs
UserType udt = userTypes.getNullable(bytes(lowercased));
if (udt != null)
return udt;
return parseRaw(unparsed).prepare(keyspace, userTypes).getType();
}
static CQL3Type.Raw parseRaw(String type)
{
try
{
// Lexer and parser
ErrorCollector errorCollector = new ErrorCollector(type);
CharStream stream = new ANTLRStringStream(type);
CqlLexer lexer = new CqlLexer(stream);
lexer.addErrorListener(errorCollector);
TokenStream tokenStream = new CommonTokenStream(lexer);
CqlParser parser = new CqlParser(tokenStream);
parser.addErrorListener(errorCollector);
// Parse the query string to a statement instance
CQL3Type.Raw rawType = parser.comparatorType();
// The errorCollector has queue up any errors that the lexer and parser may have encountered
// along the way, if necessary, we turn the last error into exceptions here.
errorCollector.throwFirstSyntaxError();
return rawType;
}
catch (RuntimeException re)
{
throw new SyntaxException(String.format("Failed parsing statement: [%s] reason: %s %s",
type,
re.getClass().getSimpleName(),
re.getMessage()));
}
catch (RecognitionException e)
{
throw new SyntaxException("Invalid or malformed CQL type: " + e.getMessage());
}
}
}

View File

@ -0,0 +1,124 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
*/
package org.apache.cassandra.schema;
import java.util.Iterator;
import java.util.function.Predicate;
import java.util.stream.Stream;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.MapDifference;
import com.google.common.collect.Maps;
public final class Keyspaces implements Iterable<KeyspaceMetadata>
{
private final ImmutableMap<String, KeyspaceMetadata> keyspaces;
private Keyspaces(Builder builder)
{
keyspaces = builder.keyspaces.build();
}
public static Builder builder()
{
return new Builder();
}
public static Keyspaces none()
{
return builder().build();
}
public static Keyspaces of(KeyspaceMetadata... keyspaces)
{
return builder().add(keyspaces).build();
}
public Iterator<KeyspaceMetadata> iterator()
{
return keyspaces.values().iterator();
}
public Stream<KeyspaceMetadata> stream()
{
return keyspaces.values().stream();
}
public Keyspaces filter(Predicate<KeyspaceMetadata> predicate)
{
Builder builder = builder();
stream().filter(predicate).forEach(builder::add);
return builder.build();
}
MapDifference<String, KeyspaceMetadata> diff(Keyspaces other)
{
return Maps.difference(keyspaces, other.keyspaces);
}
@Override
public boolean equals(Object o)
{
return this == o || (o instanceof Keyspaces && keyspaces.equals(((Keyspaces) o).keyspaces));
}
@Override
public int hashCode()
{
return keyspaces.hashCode();
}
@Override
public String toString()
{
return keyspaces.values().toString();
}
public static final class Builder
{
private final ImmutableMap.Builder<String, KeyspaceMetadata> keyspaces = new ImmutableMap.Builder<>();
private Builder()
{
}
public Keyspaces build()
{
return new Keyspaces(this);
}
public Builder add(KeyspaceMetadata keyspace)
{
keyspaces.put(keyspace.name, keyspace);
return this;
}
public Builder add(KeyspaceMetadata... keyspaces)
{
for (KeyspaceMetadata keyspace : keyspaces)
add(keyspace);
return this;
}
public Builder add(Iterable<KeyspaceMetadata> keyspaces)
{
keyspaces.forEach(this::add);
return this;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -23,6 +23,8 @@ import java.util.Optional;
import javax.annotation.Nullable;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.MapDifference;
import com.google.common.collect.Maps;
import org.apache.cassandra.config.CFMetaData;
@ -115,6 +117,11 @@ public final class Tables implements Iterable<CFMetaData>
return builder().add(filter(this, t -> t != table)).build();
}
MapDifference<String, CFMetaData> diff(Tables other)
{
return Maps.difference(tables, other.tables);
}
@Override
public boolean equals(Object o)
{

View File

@ -18,37 +18,61 @@
package org.apache.cassandra.schema;
import java.nio.ByteBuffer;
import java.util.Iterator;
import java.util.Optional;
import java.util.*;
import javax.annotation.Nullable;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.MapDifference;
import com.google.common.collect.Maps;
import org.apache.cassandra.cql3.CQL3Type;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.marshal.UserType;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.jgrapht.graph.DefaultDirectedGraph;
import org.jgrapht.graph.DefaultEdge;
import org.jgrapht.traverse.TopologicalOrderIterator;
import static com.google.common.collect.Iterables.filter;
import static java.util.stream.Collectors.toList;
import static org.apache.cassandra.utils.ByteBufferUtil.bytes;
/**
* An immutable container for a keyspace's UDTs.
*/
public final class Types implements Iterable<UserType>
{
private final ImmutableMap<ByteBuffer, UserType> types;
private static final Types NONE = new Types(ImmutableMap.of());
private final Map<ByteBuffer, UserType> types;
private Types(Builder builder)
{
types = builder.types.build();
}
/*
* For use in RawBuilder::build only.
*/
private Types(Map<ByteBuffer, UserType> types)
{
this.types = types;
}
public static Builder builder()
{
return new Builder();
}
public static RawBuilder rawBuilder(String keyspace)
{
return new RawBuilder(keyspace);
}
public static Types none()
{
return builder().build();
return NONE;
}
public static Types of(UserType... types)
@ -106,6 +130,11 @@ public final class Types implements Iterable<UserType>
return builder().add(filter(this, t -> t != type)).build();
}
MapDifference<ByteBuffer, UserType> diff(Types other)
{
return Maps.difference(types, other.types);
}
@Override
public boolean equals(Object o)
{
@ -126,7 +155,7 @@ public final class Types implements Iterable<UserType>
public static final class Builder
{
final ImmutableMap.Builder<ByteBuffer, UserType> types = new ImmutableMap.Builder<>();
final ImmutableMap.Builder<ByteBuffer, UserType> types = ImmutableMap.builder();
private Builder()
{
@ -156,4 +185,100 @@ public final class Types implements Iterable<UserType>
return this;
}
}
public static final class RawBuilder
{
final String keyspace;
final List<RawUDT> definitions;
private RawBuilder(String keyspace)
{
this.keyspace = keyspace;
this.definitions = new ArrayList<>();
}
/**
* Build a Types instance from Raw definitions.
*
* Constructs a DAG of graph dependencies and resolves them 1 by 1 in topological order.
*/
public Types build()
{
if (definitions.isEmpty())
return Types.none();
/*
* build a DAG of UDT dependencies
*/
DefaultDirectedGraph<RawUDT, DefaultEdge> graph = new DefaultDirectedGraph<>(DefaultEdge.class);
definitions.forEach(graph::addVertex);
for (RawUDT udt1: definitions)
for (RawUDT udt2 : definitions)
if (udt1 != udt2 && udt1.referencesUserType(udt2.name))
graph.addEdge(udt2, udt1);
/*
* iterate in topological order,
*/
Types types = new Types(new HashMap<>());
TopologicalOrderIterator<RawUDT, DefaultEdge> iterator = new TopologicalOrderIterator<>(graph);
while (iterator.hasNext())
{
UserType udt = iterator.next().prepare(keyspace, types); // will throw InvalidRequestException if meets an unknown type
types.types.put(udt.name, udt);
}
/*
* return an immutable copy
*/
return Types.builder().add(types).build();
}
void add(String name, List<String> fieldNames, List<String> fieldTypes)
{
List<CQL3Type.Raw> rawFieldTypes =
fieldTypes.stream()
.map(CQLTypeParser::parseRaw)
.collect(toList());
definitions.add(new RawUDT(name, fieldNames, rawFieldTypes));
}
private static final class RawUDT
{
final String name;
final List<String> fieldNames;
final List<CQL3Type.Raw> fieldTypes;
RawUDT(String name, List<String> fieldNames, List<CQL3Type.Raw> fieldTypes)
{
this.name = name;
this.fieldNames = fieldNames;
this.fieldTypes = fieldTypes;
}
boolean referencesUserType(String typeName)
{
return fieldTypes.stream().anyMatch(t -> t.referencesUserType(typeName));
}
UserType prepare(String keyspace, Types types)
{
List<ByteBuffer> preparedFieldNames =
fieldNames.stream()
.map(ByteBufferUtil::bytes)
.collect(toList());
List<AbstractType<?>> preparedFieldTypes =
fieldTypes.stream()
.map(t -> t.prepare(keyspace, types).getType())
.collect(toList());
return new UserType(keyspace, bytes(name), preparedFieldNames, preparedFieldTypes);
}
}
}
}

View File

@ -26,6 +26,8 @@ import javax.annotation.Nullable;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.MapDifference;
import com.google.common.collect.Maps;
import org.apache.cassandra.config.CFMetaData;
import org.apache.cassandra.config.ViewDefinition;
@ -124,6 +126,11 @@ public final class Views implements Iterable<ViewDefinition>
return without(view.viewName).with(view);
}
MapDifference<String, ViewDefinition> diff(Views other)
{
return Maps.difference(views, other.views);
}
@Override
public boolean equals(Object o)
{

View File

@ -468,20 +468,9 @@ public class MigrationManager
private static void announce(Mutation schema, boolean announceLocally)
{
if (announceLocally)
{
try
{
SchemaKeyspace.mergeSchema(Collections.singletonList(schema));
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
SchemaKeyspace.mergeSchema(Collections.singletonList(schema));
else
{
FBUtilities.waitOnFuture(announce(Collections.singletonList(schema)));
}
}
private static void pushSchemaMutation(InetAddress endpoint, Collection<Mutation> schema)
@ -497,7 +486,7 @@ public class MigrationManager
{
Future<?> f = StageManager.getStage(Stage.MIGRATION).submit(new WrappedRunnable()
{
protected void runMayThrow() throws IOException, ConfigurationException
protected void runMayThrow() throws ConfigurationException
{
SchemaKeyspace.mergeSchemaAndAnnounceVersion(schema);
}

View File

@ -74,10 +74,6 @@ class MigrationTask extends WrappedRunnable
{
SchemaKeyspace.mergeSchemaAndAnnounceVersion(message.payload);
}
catch (IOException e)
{
logger.error("IOException merging remote schema", e);
}
catch (ConfigurationException e)
{
logger.error("Configuration exception merging remote schema", e);

View File

@ -21,18 +21,23 @@ package org.apache.cassandra.config;
import java.util.*;
import org.apache.cassandra.SchemaLoader;
import org.apache.cassandra.db.*;
import org.apache.cassandra.cql3.QueryProcessor;
import org.apache.cassandra.cql3.UntypedResultSet;
import org.apache.cassandra.db.ColumnFamilyStore;
import org.apache.cassandra.db.Keyspace;
import org.apache.cassandra.db.Mutation;
import org.apache.cassandra.db.marshal.AsciiType;
import org.apache.cassandra.db.marshal.Int32Type;
import org.apache.cassandra.db.marshal.UTF8Type;
import org.apache.cassandra.db.rows.UnfilteredRowIterators;
import org.apache.cassandra.db.partitions.PartitionUpdate;
import org.apache.cassandra.db.rows.UnfilteredRowIterators;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.schema.CompressionParams;
import org.apache.cassandra.schema.KeyspaceMetadata;
import org.apache.cassandra.schema.KeyspaceParams;
import org.apache.cassandra.schema.SchemaKeyspace;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.schema.TableParams;
import org.apache.cassandra.schema.Types;
import org.apache.cassandra.thrift.CfDef;
import org.apache.cassandra.thrift.ColumnDef;
import org.apache.cassandra.thrift.IndexType;
@ -154,10 +159,18 @@ public class CFMetaDataTest
PartitionUpdate cfU = rm.getPartitionUpdate(Schema.instance.getId(SchemaKeyspace.NAME, SchemaKeyspace.TABLES));
PartitionUpdate cdU = rm.getPartitionUpdate(Schema.instance.getId(SchemaKeyspace.NAME, SchemaKeyspace.COLUMNS));
CFMetaData newCfm = SchemaKeyspace.createTableFromTablePartitionAndColumnsPartition(
UnfilteredRowIterators.filter(cfU.unfilteredIterator(), FBUtilities.nowInSeconds()),
UnfilteredRowIterators.filter(cdU.unfilteredIterator(), FBUtilities.nowInSeconds())
);
assert cfm.equals(newCfm) : String.format("%n%s%n!=%n%s", cfm, newCfm);
UntypedResultSet.Row tableRow = QueryProcessor.resultify(String.format("SELECT * FROM %s.%s", SchemaKeyspace.NAME, SchemaKeyspace.TABLES),
UnfilteredRowIterators.filter(cfU.unfilteredIterator(), FBUtilities.nowInSeconds()))
.one();
TableParams params = SchemaKeyspace.createTableParamsFromRow(tableRow);
UntypedResultSet columnsRows = QueryProcessor.resultify(String.format("SELECT * FROM %s.%s", SchemaKeyspace.NAME, SchemaKeyspace.COLUMNS),
UnfilteredRowIterators.filter(cdU.unfilteredIterator(), FBUtilities.nowInSeconds()));
Set<ColumnDefinition> columns = new HashSet<>();
for (UntypedResultSet.Row row : columnsRows)
columns.add(SchemaKeyspace.createColumnFromRow(row, Types.none()));
assertEquals(cfm.params, params);
assertEquals(new HashSet<>(cfm.allColumns()), columns);
}
}

View File

@ -110,7 +110,7 @@ public class TupleTypeTest extends CQLTester
row(0, 4, tuple(null, "1"))
);
assertInvalidMessage("Invalid tuple literal: too many elements. Type tuple<int, text> expects 2 but got 3",
assertInvalidMessage("Invalid tuple literal: too many elements. Type frozen<tuple<int, text>> expects 2 but got 3",
"INSERT INTO %s(k, t) VALUES (1,'1:2:3')");
}
@ -121,7 +121,7 @@ public class TupleTypeTest extends CQLTester
assertInvalidSyntax("INSERT INTO %s (k, t) VALUES (0, ())");
assertInvalidMessage("Invalid tuple literal for t: too many elements. Type tuple<int, text, double> expects 3 but got 4",
assertInvalidMessage("Invalid tuple literal for t: too many elements. Type frozen<tuple<int, text, double>> expects 3 but got 4",
"INSERT INTO %s (k, t) VALUES (0, (2, 'foo', 3.1, 'bar'))");
}

View File

@ -797,7 +797,7 @@ public class UFTest extends CQLTester
@Test
public void testFunctionNonExistingKeyspace() throws Throwable
{
assertInvalidMessage("to non existing keyspace",
assertInvalidMessage("Keyspace this_ks_does_not_exist doesn't exist",
"CREATE OR REPLACE FUNCTION this_ks_does_not_exist.jnft(val double) " +
"RETURNS NULL ON NULL INPUT " +
"RETURNS double " +
@ -810,7 +810,7 @@ public class UFTest extends CQLTester
{
dropPerTestKeyspace();
assertInvalidMessage("to non existing keyspace",
assertInvalidMessage("Keyspace " + KEYSPACE_PER_TEST + " doesn't exist",
"CREATE OR REPLACE FUNCTION " + KEYSPACE_PER_TEST + ".jnft(val double) " +
"RETURNS NULL ON NULL INPUT " +
"RETURNS double " +

View File

@ -63,8 +63,7 @@ public class LegacySchemaMigratorTest
{
CQLTester.cleanupAndLeaveDirs();
List<KeyspaceMetadata> expected = keyspaceToMigrate();
expected.sort((k1, k2) -> k1.name.compareTo(k2.name));
Keyspaces expected = keyspacesToMigrate();
// write the keyspaces into the legacy tables
expected.forEach(LegacySchemaMigratorTest::legacySerializeKeyspace);
@ -73,8 +72,7 @@ public class LegacySchemaMigratorTest
LegacySchemaMigrator.migrate();
// read back all the metadata from the new schema tables
List<KeyspaceMetadata> actual = SchemaKeyspace.readSchemaFromSystemTables();
actual.sort((k1, k2) -> k1.name.compareTo(k2.name));
Keyspaces actual = SchemaKeyspace.fetchNonSystemKeyspaces();
// need to load back CFMetaData of those tables (CFS instances will still be loaded)
loadLegacySchemaTables();
@ -104,9 +102,9 @@ public class LegacySchemaMigratorTest
Schema.instance.setKeyspaceMetadata(systemKeyspace.withSwapped(systemTables));
}
private static List<KeyspaceMetadata> keyspaceToMigrate()
private static Keyspaces keyspacesToMigrate()
{
List<KeyspaceMetadata> keyspaces = new ArrayList<>();
Keyspaces.Builder keyspaces = Keyspaces.builder();
// A whole bucket of shorthand
String ks1 = KEYSPACE_PREFIX + "Keyspace1";
@ -255,7 +253,7 @@ public class LegacySchemaMigratorTest
keyspaces.add(keyspaceWithUDAs());
keyspaces.add(keyspaceWithUDAsAndUDTs());
return keyspaces;
return keyspaces.build();
}
private static KeyspaceMetadata keyspaceWithDroppedCollections()

View File

@ -24,6 +24,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.google.common.collect.ImmutableMap;
@ -32,11 +33,13 @@ import org.junit.Test;
import org.apache.cassandra.SchemaLoader;
import org.apache.cassandra.config.CFMetaData;
import org.apache.cassandra.config.ColumnDefinition;
import org.apache.cassandra.config.Schema;
import org.apache.cassandra.cql3.QueryProcessor;
import org.apache.cassandra.cql3.UntypedResultSet;
import org.apache.cassandra.db.ColumnFamilyStore;
import org.apache.cassandra.db.Keyspace;
import org.apache.cassandra.db.Mutation;
import org.apache.cassandra.db.SystemKeyspace;
import org.apache.cassandra.db.marshal.AsciiType;
import org.apache.cassandra.db.marshal.UTF8Type;
import org.apache.cassandra.db.partitions.PartitionUpdate;
@ -158,14 +161,14 @@ public class SchemaKeyspaceTest
assertEquals(extensions, metadata.params.extensions);
}
private static void updateTable(String keyspace, CFMetaData oldTable, CFMetaData newTable) throws IOException
private static void updateTable(String keyspace, CFMetaData oldTable, CFMetaData newTable)
{
KeyspaceMetadata ksm = Schema.instance.getKeyspaceInstance(keyspace).getMetadata();
Mutation mutation = SchemaKeyspace.makeUpdateTableMutation(ksm, oldTable, newTable, FBUtilities.timestampMicros(), false);
SchemaKeyspace.mergeSchema(Collections.singleton(mutation));
}
private static void createTable(String keyspace, String cql) throws IOException
private static void createTable(String keyspace, String cql)
{
CFMetaData table = CFMetaData.compile(cql, keyspace);
@ -185,10 +188,21 @@ public class SchemaKeyspaceTest
// Test schema conversion
Mutation rm = SchemaKeyspace.makeCreateTableMutation(keyspace, cfm, FBUtilities.timestampMicros());
PartitionUpdate serializedCf = rm.getPartitionUpdate(Schema.instance.getId(SystemKeyspace.NAME, SchemaKeyspace.TABLES));
PartitionUpdate serializedCD = rm.getPartitionUpdate(Schema.instance.getId(SystemKeyspace.NAME, SchemaKeyspace.COLUMNS));
CFMetaData newCfm = SchemaKeyspace.createTableFromTablePartitionAndColumnsPartition(UnfilteredRowIterators.filter(serializedCf.unfilteredIterator(), FBUtilities.nowInSeconds()),
UnfilteredRowIterators.filter(serializedCD.unfilteredIterator(), FBUtilities.nowInSeconds()));
assert cfm.equals(newCfm) : String.format("%n%s%n!=%n%s", cfm, newCfm);
PartitionUpdate serializedCf = rm.getPartitionUpdate(Schema.instance.getId(SchemaKeyspace.NAME, SchemaKeyspace.TABLES));
PartitionUpdate serializedCD = rm.getPartitionUpdate(Schema.instance.getId(SchemaKeyspace.NAME, SchemaKeyspace.COLUMNS));
UntypedResultSet.Row tableRow = QueryProcessor.resultify(String.format("SELECT * FROM %s.%s", SchemaKeyspace.NAME, SchemaKeyspace.TABLES),
UnfilteredRowIterators.filter(serializedCf.unfilteredIterator(), FBUtilities.nowInSeconds()))
.one();
TableParams params = SchemaKeyspace.createTableParamsFromRow(tableRow);
UntypedResultSet columnsRows = QueryProcessor.resultify(String.format("SELECT * FROM %s.%s", SchemaKeyspace.NAME, SchemaKeyspace.COLUMNS),
UnfilteredRowIterators.filter(serializedCD.unfilteredIterator(), FBUtilities.nowInSeconds()));
Set<ColumnDefinition> columns = new HashSet<>();
for (UntypedResultSet.Row row : columnsRows)
columns.add(SchemaKeyspace.createColumnFromRow(row, Types.none()));
assertEquals(cfm.params, params);
assertEquals(new HashSet<>(cfm.allColumns()), columns);
}
}