mirror of https://github.com/apache/cassandra
Use CQL type names in schema metadata tables
patch by Aleksey Yeschenko; reviewed by Sylvain Lebresne for CASSANDRA-10365
This commit is contained in:
parent
f4af154637
commit
340df43fb7
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -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.
|
||||
|
|
@ -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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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'))");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 " +
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue