You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by bl...@apache.org on 2017/01/18 15:07:51 UTC
[2/3] cassandra git commit: Merge branch cassandra-3.0 into
cassandra-3.11
http://git-wip-us.apache.org/repos/asf/cassandra/blob/d3882895/doc/source/cql/types.rst
----------------------------------------------------------------------
diff --cc doc/source/cql/types.rst
index b0d205b,0000000..8954d87
mode 100644,000000..100644
--- a/doc/source/cql/types.rst
+++ b/doc/source/cql/types.rst
@@@ -1,560 -1,0 +1,557 @@@
+.. 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.
+
+.. highlight:: cql
+
+.. _UUID: https://en.wikipedia.org/wiki/Universally_unique_identifier
+
+.. _data-types:
+
+Data Types
+----------
+
+CQL is a typed language and supports a rich set of data types, including :ref:`native types <native-types>`,
+:ref:`collection types <collections>`, :ref:`user-defined types <udts>`, :ref:`tuple types <tuples>` and :ref:`custom
+types <custom-types>`:
+
+.. productionlist::
+ cql_type: `native_type` | `collection_type` | `user_defined_type` | `tuple_type` | `custom_type`
+
+
+.. _native-types:
+
+Native Types
+^^^^^^^^^^^^
+
+The native types supported by CQL are:
+
+.. productionlist::
+ native_type: ASCII
+ : | BIGINT
+ : | BLOB
+ : | BOOLEAN
+ : | COUNTER
+ : | DATE
+ : | DECIMAL
+ : | DOUBLE
+ : | DURATION
+ : | FLOAT
+ : | INET
+ : | INT
+ : | SMALLINT
+ : | TEXT
+ : | TIME
+ : | TIMESTAMP
+ : | TIMEUUID
+ : | TINYINT
+ : | UUID
+ : | VARCHAR
+ : | VARINT
+
+The following table gives additional informations on the native data types, and on which kind of :ref:`constants
+<constants>` each type supports:
+
+=============== ===================== ==================================================================================
+ type constants supported description
+=============== ===================== ==================================================================================
+ ``ascii`` :token:`string` ASCII character string
+ ``bigint`` :token:`integer` 64-bit signed long
+ ``blob`` :token:`blob` Arbitrary bytes (no validation)
+ ``boolean`` :token:`boolean` Either ``true`` or ``false``
+ ``counter`` :token:`integer` Counter column (64-bit signed value). See :ref:`counters` for details
+ ``date`` :token:`integer`, A date (with no corresponding time value). See :ref:`dates` below for details
+ :token:`string`
+ ``decimal`` :token:`integer`, Variable-precision decimal
+ :token:`float`
+ ``double`` :token:`integer` 64-bit IEEE-754 floating point
+ :token:`float`
+ ``duration`` :token:`duration`, A duration with nanosecond precision. See :ref:`durations` below for details
+ ``float`` :token:`integer`, 32-bit IEEE-754 floating point
+ :token:`float`
+ ``inet`` :token:`string` An IP address, either IPv4 (4 bytes long) or IPv6 (16 bytes long). Note that
+ there is no ``inet`` constant, IP address should be input as strings
+ ``int`` :token:`integer` 32-bit signed int
+ ``smallint`` :token:`integer` 16-bit signed int
+ ``text`` :token:`string` UTF8 encoded string
+ ``time`` :token:`integer`, A time (with no corresponding date value) with nanosecond precision. See
+ :token:`string` :ref:`times` below for details
+ ``timestamp`` :token:`integer`, A timestamp (date and time) with millisecond precision. See :ref:`timestamps`
+ :token:`string` below for details
+ ``timeuuid`` :token:`uuid` Version 1 UUID_, generally used as a \u201cconflict-free\u201d timestamp. Also see
+ :ref:`timeuuid-functions`
+ ``tinyint`` :token:`integer` 8-bit signed int
+ ``uuid`` :token:`uuid` A UUID_ (of any version)
+ ``varchar`` :token:`string` UTF8 encoded string
+ ``varint`` :token:`integer` Arbitrary-precision integer
+=============== ===================== ==================================================================================
+
+.. _counters:
+
+Counters
+~~~~~~~~
+
+The ``counter`` type is used to define *counter columns*. A counter column is a column whose value is a 64-bit signed
+integer and on which 2 operations are supported: incrementing and decrementing (see the :ref:`UPDATE statement
+<update-statement>` for syntax). Note that the value of a counter cannot be set: a counter does not exist until first
+incremented/decremented, and that first increment/decrement is made as if the prior value was 0.
+
+.. _counter-limitations:
+
+Counters have a number of important limitations:
+
+- They cannot be used for columns part of the ``PRIMARY KEY`` of a table.
+- A table that contains a counter can only contain counters. In other words, either all the columns of a table outside
+ the ``PRIMARY KEY`` have the ``counter`` type, or none of them have it.
+- Counters do not support :ref:`expiration <ttls>`.
+- The deletion of counters is supported, but is only guaranteed to work the first time you delete a counter. In other
+ words, you should not re-update a counter that you have deleted (if you do, proper behavior is not guaranteed).
+- Counter updates are, by nature, not `idemptotent <https://en.wikipedia.org/wiki/Idempotence>`__. An important
+ consequence is that if a counter update fails unexpectedly (timeout or loss of connection to the coordinator node),
+ the client has no way to know if the update has been applied or not. In particular, replaying the update may or may
+ not lead to an over count.
+
+.. _timestamps:
+
+Working with timestamps
+^^^^^^^^^^^^^^^^^^^^^^^
+
+Values of the ``timestamp`` type are encoded as 64-bit signed integers representing a number of milliseconds since the
+standard base time known as `the epoch <https://en.wikipedia.org/wiki/Unix_time>`__: January 1 1970 at 00:00:00 GMT.
+
+Timestamps can be input in CQL either using their value as an :token:`integer`, or using a :token:`string` that
+represents an `ISO 8601 <https://en.wikipedia.org/wiki/ISO_8601>`__ date. For instance, all of the values below are
+valid ``timestamp`` values for Mar 2, 2011, at 04:05:00 AM, GMT:
+
+- ``1299038700000``
+- ``'2011-02-03 04:05+0000'``
+- ``'2011-02-03 04:05:00+0000'``
+- ``'2011-02-03 04:05:00.000+0000'``
+- ``'2011-02-03T04:05+0000'``
+- ``'2011-02-03T04:05:00+0000'``
+- ``'2011-02-03T04:05:00.000+0000'``
+
+The ``+0000`` above is an RFC 822 4-digit time zone specification; ``+0000`` refers to GMT. US Pacific Standard Time is
+``-0800``. The time zone may be omitted if desired (``'2011-02-03 04:05:00'``), and if so, the date will be interpreted
+as being in the time zone under which the coordinating Cassandra node is configured. There are however difficulties
+inherent in relying on the time zone configuration being as expected, so it is recommended that the time zone always be
+specified for timestamps when feasible.
+
+The time of day may also be omitted (``'2011-02-03'`` or ``'2011-02-03+0000'``), in which case the time of day will
+default to 00:00:00 in the specified or default time zone. However, if only the date part is relevant, consider using
+the :ref:`date <dates>` type.
+
+.. _dates:
+
+Working with dates
+^^^^^^^^^^^^^^^^^^
+
+Values of the ``date`` type are encoded as 32-bit unsigned integers representing a number of days with \u201cthe epoch\u201d at
+the center of the range (2^31). Epoch is January 1st, 1970
+
+As for :ref:`timestamp <timestamps>`, a date can be input either as an :token:`integer` or using a date
+:token:`string`. In the later case, the format should be ``yyyy-mm-dd`` (so ``'2011-02-03'`` for instance).
+
+.. _times:
+
+Working with times
+^^^^^^^^^^^^^^^^^^
+
+Values of the ``time`` type are encoded as 64-bit signed integers representing the number of nanoseconds since midnight.
+
+As for :ref:`timestamp <timestamps>`, a time can be input either as an :token:`integer` or using a :token:`string`
+representing the time. In the later case, the format should be ``hh:mm:ss[.fffffffff]`` (where the sub-second precision
+is optional and if provided, can be less than the nanosecond). So for instance, the following are valid inputs for a
+time:
+
+- ``'08:12:54'``
+- ``'08:12:54.123'``
+- ``'08:12:54.123456'``
+- ``'08:12:54.123456789'``
+
+.. _durations:
+
+Working with durations
+^^^^^^^^^^^^^^^^^^^^^^
+
+Values of the ``duration`` type are encoded as 3 signed integer of variable lengths. The first integer represents the
+number of months, the second the number of days and the third the number of nanoseconds. This is due to the fact that
+the number of days in a month can change, and a day can have 23 or 25 hours depending on the daylight saving.
+
+A duration can be input as:
+
+ #. ``(quantity unit)+`` like ``12h30m`` where the unit can be:
+
+ * ``y``: years (12 months)
+ * ``mo``: months (1 month)
+ * ``w``: weeks (7 days)
+ * ``d``: days (1 day)
+ * ``h``: hours (3,600,000,000,000 nanoseconds)
+ * ``m``: minutes (60,000,000,000 nanoseconds)
+ * ``s``: seconds (1,000,000,000 nanoseconds)
+ * ``ms``: milliseconds (1,000,000 nanoseconds)
+ * ``us`` or ``�s`` : microseconds (1000 nanoseconds)
+ * ``ns``: nanoseconds (1 nanosecond)
+ #. ISO 8601 format: ``P[n]Y[n]M[n]DT[n]H[n]M[n]S or P[n]W``
+ #. ISO 8601 alternative format: ``P[YYYY]-[MM]-[DD]T[hh]:[mm]:[ss]``
+
+For example::
+
+ INSERT INTO RiderResults (rider, race, result) VALUES ('Christopher Froome', 'Tour de France', 89h4m48s);
+ INSERT INTO RiderResults (rider, race, result) VALUES ('BARDET Romain', 'Tour de France', PT89H8M53S);
+ INSERT INTO RiderResults (rider, race, result) VALUES ('QUINTANA Nairo', 'Tour de France', P0000-00-00T89:09:09);
+
+.. _duration-limitation:
+
+Duration columns cannot be used in a table's ``PRIMARY KEY``. This limitation is due to the fact that
+durations cannot be ordered. It is effectively not possible to know if ``1mo`` is greater than ``29d`` without a date
+context.
+
+A ``1d`` duration is not equals to a ``24h`` one as the duration type has been created to be able to support daylight
+saving.
+
+.. _collections:
+
+Collections
+^^^^^^^^^^^
+
+CQL supports 3 kind of collections: :ref:`maps`, :ref:`sets` and :ref:`lists`. The types of those collections is defined
+by:
+
+.. productionlist::
+ collection_type: MAP '<' `cql_type` ',' `cql_type` '>'
+ : | SET '<' `cql_type` '>'
+ : | LIST '<' `cql_type` '>'
+
+and their values can be inputd using collection literals:
+
+.. productionlist::
+ collection_literal: `map_literal` | `set_literal` | `list_literal`
+ map_literal: '{' [ `term` ':' `term` (',' `term` : `term`)* ] '}'
+ set_literal: '{' [ `term` (',' `term`)* ] '}'
+ list_literal: '[' [ `term` (',' `term`)* ] ']'
+
+Note however that neither :token:`bind_marker` nor ``NULL`` are supported inside collection literals.
+
+Noteworthy characteristics
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Collections are meant for storing/denormalizing relatively small amount of data. They work well for things like \u201cthe
+phone numbers of a given user\u201d, \u201clabels applied to an email\u201d, etc. But when items are expected to grow unbounded (\u201call
+messages sent by a user\u201d, \u201cevents registered by a sensor\u201d...), then collections are not appropriate and a specific table
+(with clustering columns) should be used. Concretely, (non-frozen) collections have the following noteworthy
+characteristics and limitations:
+
+- Individual collections are not indexed internally. Which means that even to access a single element of a collection,
+ the while collection has to be read (and reading one is not paged internally).
+- While insertion operations on sets and maps never incur a read-before-write internally, some operations on lists do.
+ Further, some lists operations are not idempotent by nature (see the section on :ref:`lists <lists>` below for
+ details), making their retry in case of timeout problematic. It is thus advised to prefer sets over lists when
+ possible.
+
+Please note that while some of those limitations may or may not be removed/improved upon in the future, it is a
+anti-pattern to use a (single) collection to store large amounts of data.
+
+.. _maps:
+
+Maps
+~~~~
+
+A ``map`` is a (sorted) set of key-value pairs, where keys are unique and the map is sorted by its keys. You can define
+and insert a map with::
+
+ CREATE TABLE users (
+ id text PRIMARY KEY,
+ name text,
+ favs map<text, text> // A map of text keys, and text values
+ );
+
+ INSERT INTO users (id, name, favs)
+ VALUES ('jsmith', 'John Smith', { 'fruit' : 'Apple', 'band' : 'Beatles' });
+
+ // Replace the existing map entirely.
+ UPDATE users SET favs = { 'fruit' : 'Banana' } WHERE id = 'jsmith';
+
+Further, maps support:
+
+- Updating or inserting one or more elements::
+
+ UPDATE users SET favs['author'] = 'Ed Poe' WHERE id = 'jsmith';
+ UPDATE users SET favs = favs + { 'movie' : 'Cassablanca', 'band' : 'ZZ Top' } WHERE id = 'jsmith';
+
+- Removing one or more element (if an element doesn't exist, removing it is a no-op but no error is thrown)::
+
+ DELETE favs['author'] FROM users WHERE id = 'jsmith';
+ UPDATE users SET favs = favs - { 'movie', 'band'} WHERE id = 'jsmith';
+
+ Note that for removing multiple elements in a ``map``, you remove from it a ``set`` of keys.
+
+Lastly, TTLs are allowed for both ``INSERT`` and ``UPDATE``, but in both case the TTL set only apply to the newly
+inserted/updated elements. In other words::
+
+ UPDATE users USING TTL 10 SET favs['color'] = 'green' WHERE id = 'jsmith';
+
+will only apply the TTL to the ``{ 'color' : 'green' }`` record, the rest of the map remaining unaffected.
+
+
+.. _sets:
+
+Sets
+~~~~
+
+A ``set`` is a (sorted) collection of unique values. You can define and insert a map with::
+
+ CREATE TABLE images (
+ name text PRIMARY KEY,
+ owner text,
+ tags set<text> // A set of text values
+ );
+
+ INSERT INTO images (name, owner, tags)
+ VALUES ('cat.jpg', 'jsmith', { 'pet', 'cute' });
+
+ // Replace the existing set entirely
+ UPDATE images SET tags = { 'kitten', 'cat', 'lol' } WHERE name = 'cat.jpg';
+
+Further, sets support:
+
+- Adding one or multiple elements (as this is a set, inserting an already existing element is a no-op)::
+
+ UPDATE images SET tags = tags + { 'gray', 'cuddly' } WHERE name = 'cat.jpg';
+
+- Removing one or multiple elements (if an element doesn't exist, removing it is a no-op but no error is thrown)::
+
+ UPDATE images SET tags = tags - { 'cat' } WHERE name = 'cat.jpg';
+
+Lastly, as for :ref:`maps <maps>`, TTLs if used only apply to the newly inserted values.
+
+.. _lists:
+
+Lists
+~~~~~
+
+.. note:: As mentioned above and further discussed at the end of this section, lists have limitations and specific
+ performance considerations that you should take into account before using them. In general, if you can use a
+ :ref:`set <sets>` instead of list, always prefer a set.
+
+A ``list`` is a (sorted) collection of non-unique values where elements are ordered by there position in the list. You
+can define and insert a list with::
+
+ CREATE TABLE plays (
+ id text PRIMARY KEY,
+ game text,
+ players int,
+ scores list<int> // A list of integers
+ )
+
+ INSERT INTO plays (id, game, players, scores)
+ VALUES ('123-afde', 'quake', 3, [17, 4, 2]);
+
+ // Replace the existing list entirely
+ UPDATE plays SET scores = [ 3, 9, 4] WHERE id = '123-afde';
+
+Further, lists support:
+
+- Appending and prepending values to a list::
+
+ UPDATE plays SET players = 5, scores = scores + [ 14, 21 ] WHERE id = '123-afde';
+ UPDATE plays SET players = 6, scores = [ 3 ] + scores WHERE id = '123-afde';
+
+- Setting the value at a particular position in the list. This imply that the list has a pre-existing element for that
+ position or an error will be thrown that the list is too small::
+
+ UPDATE plays SET scores[1] = 7 WHERE id = '123-afde';
+
+- Removing an element by its position in the list. This imply that the list has a pre-existing element for that position
+ or an error will be thrown that the list is too small. Further, as the operation removes an element from the list, the
+ list size will be diminished by 1, shifting the position of all the elements following the one deleted::
+
+ DELETE scores[1] FROM plays WHERE id = '123-afde';
+
+- Deleting *all* the occurrences of particular values in the list (if a particular element doesn't occur at all in the
+ list, it is simply ignored and no error is thrown)::
+
+ UPDATE plays SET scores = scores - [ 12, 21 ] WHERE id = '123-afde';
+
+.. warning:: The append and prepend operations are not idempotent by nature. So in particular, if one of these operation
+ timeout, then retrying the operation is not safe and it may (or may not) lead to appending/prepending the value
+ twice.
+
+.. warning:: Setting and removing an element by position and removing occurences of particular values incur an internal
+ *read-before-write*. They will thus run more slowly and take more ressources than usual updates (with the exclusion
+ of conditional write that have their own cost).
+
+Lastly, as for :ref:`maps <maps>`, TTLs when used only apply to the newly inserted values.
+
+.. _udts:
+
+User-Defined Types
+^^^^^^^^^^^^^^^^^^
+
+CQL support the definition of user-defined types (UDT for short). Such a type can be created, modified and removed using
+the :token:`create_type_statement`, :token:`alter_type_statement` and :token:`drop_type_statement` described below. But
+once created, a UDT is simply referred to by its name:
+
+.. productionlist::
+ user_defined_type: `udt_name`
+ udt_name: [ `keyspace_name` '.' ] `identifier`
+
+
+Creating a UDT
+~~~~~~~~~~~~~~
+
+Creating a new user-defined type is done using a ``CREATE TYPE`` statement defined by:
+
+.. productionlist::
+ create_type_statement: CREATE TYPE [ IF NOT EXISTS ] `udt_name`
+ : '(' `field_definition` ( ',' `field_definition` )* ')'
+ field_definition: `identifier` `cql_type`
+
+A UDT has a name (used to declared columns of that type) and is a set of named and typed fields. Fields name can be any
+type, including collections or other UDT. For instance::
+
+ CREATE TYPE phone (
+ country_code int,
+ number text,
+ )
+
+ CREATE TYPE address (
+ street text,
+ city text,
+ zip text,
+ phones map<text, phone>
+ )
+
+ CREATE TABLE user (
+ name text PRIMARY KEY,
+ addresses map<text, frozen<address>>
+ )
+
+Note that:
+
+- Attempting to create an already existing type will result in an error unless the ``IF NOT EXISTS`` option is used. If
+ it is used, the statement will be a no-op if the type already exists.
+- A type is intrinsically bound to the keyspace in which it is created, and can only be used in that keyspace. At
+ creation, if the type name is prefixed by a keyspace name, it is created in that keyspace. Otherwise, it is created in
+ the current keyspace.
+- As of Cassandra |version|, UDT have to be frozen in most cases, hence the ``frozen<address>`` in the table definition
+ above. Please see the section on :ref:`frozen <frozen>` for more details.
+
+UDT literals
+~~~~~~~~~~~~
+
+Once a used-defined type has been created, value can be input using a UDT literal:
+
+.. productionlist::
+ udt_literal: '{' `identifier` ':' `term` ( ',' `identifier` ':' `term` )* '}'
+
+In other words, a UDT literal is like a :ref:`map <maps>` literal but its keys are the names of the fields of the type.
+For instance, one could insert into the table define in the previous section using::
+
+ INSERT INTO user (name, addresses)
+ VALUES ('z3 Pr3z1den7', {
+ 'home' : {
+ street: '1600 Pennsylvania Ave NW',
+ city: 'Washington',
+ zip: '20500',
+ phones: { 'cell' : { country_code: 1, number: '202 456-1111' },
+ 'landline' : { country_code: 1, number: '...' } }
+ },
+ 'work' : {
+ street: '1600 Pennsylvania Ave NW',
+ city: 'Washington',
+ zip: '20500',
+ phones: { 'fax' : { country_code: 1, number: '...' } }
+ }
+ })
+
+To be valid, a UDT literal should only include fields defined by the type it is a literal of, but it can omit some field
+(in which case those will be ``null``).
+
+Altering a UDT
+~~~~~~~~~~~~~~
+
+An existing user-defined type can be modified using an ``ALTER TYPE`` statement:
+
+.. productionlist::
+ alter_type_statement: ALTER TYPE `udt_name` `alter_type_modification`
- alter_type_modification: ALTER `identifier` TYPE `cql_type`
- : | ADD `field_definition`
++ alter_type_modification: ADD `field_definition`
+ : | RENAME `identifier` TO `identifier` ( `identifier` TO `identifier` )*
+
+You can:
+
- - modify the type of particular field (``ALTER TYPE address ALTER zip TYPE bigint``). The restrictions for such change
- are the same than when :ref:`altering the type of column <alter-table-statement>`.
+- add a new field to the type (``ALTER TYPE address ADD country text``). That new field will be ``null`` for any values
+ of the type created before the addition.
+- rename the fields of the type (``ALTER TYPE address RENAME zip TO zipcode``).
+
+Dropping a UDT
+~~~~~~~~~~~~~~
+
+You can drop an existing user-defined type using a ``DROP TYPE`` statement:
+
+.. productionlist::
+ drop_type_statement: DROP TYPE [ IF EXISTS ] `udt_name`
+
+Dropping a type results in the immediate, irreversible removal of that type. However, attempting to drop a type that is
+still in use by another type, table or function will result in an error.
+
+If the type dropped does not exist, an error will be returned unless ``IF EXISTS`` is used, in which case the operation
+is a no-op.
+
+.. _tuples:
+
+Tuples
+^^^^^^
+
+CQL also support tuples and tuple types (where the elements can be of different types). Functionally, tuples can be
+though as anonymous UDT with anonymous fields. Tuple types and tuple literals are defined by:
+
+.. productionlist::
+ tuple_type: TUPLE '<' `cql_type` ( ',' `cql_type` )* '>'
+ tuple_literal: '(' `term` ( ',' `term` )* ')'
+
+and can be used thusly::
+
+ CREATE TABLE durations (
+ event text,
+ duration tuple<int, text>,
+ )
+
+ INSERT INTO durations (event, duration) VALUES ('ev1', (3, 'hours'));
+
+Unlike other "composed" types (collections and UDT), a tuple is always :ref:`frozen <frozen>` (without the need of the
+`frozen` keyword) and it is not possible to update only some elements of a tuple (without updating the whole tuple).
+Also, a tuple literal should always have the same number of value than declared in the type it is a tuple of (some of
+those values can be null but they need to be explicitly declared as so).
+
+.. _custom-types:
+
+Custom Types
+^^^^^^^^^^^^
+
+.. note:: Custom types exists mostly for backward compatiliby purposes and their usage is discouraged. Their usage is
+ complex, not user friendly and the other provided types, particularly :ref:`user-defined types <udts>`, should almost
+ always be enough.
+
+A custom type is defined by:
+
+.. productionlist::
+ custom_type: `string`
+
+A custom type is a :token:`string` that contains the name of Java class that extends the server side ``AbstractType``
+class and that can be loaded by Cassandra (it should thus be in the ``CLASSPATH`` of every node running Cassandra). That
+class will define what values are valid for the type and how the time sorts when used for a clustering column. For any
+other purpose, a value of a custom type is the same than that of a ``blob``, and can in particular be input using the
+:token:`blob` literal syntax.
http://git-wip-us.apache.org/repos/asf/cassandra/blob/d3882895/pylib/cqlshlib/cql3handling.py
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/d3882895/src/java/org/apache/cassandra/config/CFMetaData.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/config/CFMetaData.java
index ca76bec,a3370dc..1d3bb3a
--- a/src/java/org/apache/cassandra/config/CFMetaData.java
+++ b/src/java/org/apache/cassandra/config/CFMetaData.java
@@@ -298,21 -283,20 +297,22 @@@ public final class CFMetaDat
this.clusteringColumns = clusteringColumns;
this.partitionColumns = partitionColumns;
- //This needs to happen before serializers are set
- //because they use comparator.subtypes()
- this.serializers = new Serializers(this);
-
+ this.comparator = new ClusteringComparator(extractTypes(clusteringColumns));
+ List<AbstractType<?>> keyTypes = extractTypes(partitionKeyColumns);
+ this.keyValidator = keyTypes.size() == 1 ? keyTypes.get(0) : CompositeType.getInstance(keyTypes);
+
rebuild();
+
+ this.resource = DataResource.table(ksName, cfName);
+ this.serializers = new Serializers(this);
}
// This rebuild informations that are intrinsically duplicate of the table definition but
// are kept because they are often useful in a different format.
private void rebuild()
{
- this.comparator = new ClusteringComparator(extractTypes(clusteringColumns));
-
- Map<ByteBuffer, ColumnDefinition> newColumnMetadata = new HashMap<>();
+ Map<ByteBuffer, ColumnDefinition> newColumnMetadata = Maps.newHashMapWithExpectedSize(partitionKeyColumns.size() + clusteringColumns.size() + partitionColumns.size());
++
for (ColumnDefinition def : partitionKeyColumns)
newColumnMetadata.put(def.name.bytes, def);
for (ColumnDefinition def : clusteringColumns)
@@@ -325,13 -306,8 +322,10 @@@
this.columnMetadata = newColumnMetadata;
- List<AbstractType<?>> keyTypes = extractTypes(partitionKeyColumns);
- this.keyValidator = keyTypes.size() == 1 ? keyTypes.get(0) : CompositeType.getInstance(keyTypes);
-
if (isCompactTable())
this.compactValueColumn = CompactTables.getCompactValueColumn(partitionColumns, isSuper());
+
+ this.allColumnFilter = ColumnFilter.all(this);
}
public Indexes getIndexes()
http://git-wip-us.apache.org/repos/asf/cassandra/blob/d3882895/src/java/org/apache/cassandra/cql3/QueryProcessor.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/QueryProcessor.java
index 288fd4b,af94d3e..f5ce7e4
--- a/src/java/org/apache/cassandra/cql3/QueryProcessor.java
+++ b/src/java/org/apache/cassandra/cql3/QueryProcessor.java
@@@ -60,7 -59,7 +60,7 @@@ import static org.apache.cassandra.cql3
public class QueryProcessor implements QueryHandler
{
- public static final CassandraVersion CQL_VERSION = new CassandraVersion("3.4.3");
- public static final CassandraVersion CQL_VERSION = new CassandraVersion("3.4.0");
++ public static final CassandraVersion CQL_VERSION = new CassandraVersion("3.4.4");
public static final QueryProcessor instance = new QueryProcessor();
http://git-wip-us.apache.org/repos/asf/cassandra/blob/d3882895/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java
index 5f3b2ff,756bb96..04f01a5
--- a/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java
@@@ -99,7 -111,10 +99,9 @@@ public class AlterTableStatement extend
switch (oType)
{
+ case ALTER:
+ throw new InvalidRequestException("Altering of types is not allowed");
case ADD:
- assert columnName != null;
if (cfm.isDense())
throw new InvalidRequestException("Cannot add new column to a COMPACT STORAGE table");
@@@ -184,109 -188,67 +186,73 @@@
}
break;
- case ALTER:
- columnName = colNameList.get(0).getColumnName().getIdentifier(cfm);
- def = cfm.getColumnDefinition(columnName);
- dataType = colNameList.get(0).getColumnType();
- assert dataType != null;
- validator = dataType.prepare(keyspace());
-
- if (def == null)
- throw new InvalidRequestException(String.format("Column %s was not found in table %s", columnName, columnFamily()));
-
- AbstractType<?> validatorType = def.isReversedType() && !validator.getType().isReversed()
- ? ReversedType.getInstance(validator.getType())
- : validator.getType();
- validateAlter(cfm, def, validatorType);
- // In any case, we update the column definition
- cfm.addOrReplaceColumnDefinition(def.withNewType(validatorType));
-
- // We also have to validate the view types here. If we have a view which includes a column as part of
- // the clustering key, we need to make sure that it is indeed compatible.
- for (ViewDefinition view : views)
- {
- if (!view.includes(columnName)) continue;
- ViewDefinition viewCopy = view.copy();
- ColumnDefinition viewDef = view.metadata.getColumnDefinition(columnName);
- AbstractType viewType = viewDef.isReversedType() && !validator.getType().isReversed()
- ? ReversedType.getInstance(validator.getType())
- : validator.getType();
- validateAlter(view.metadata, viewDef, viewType);
- viewCopy.metadata.addOrReplaceColumnDefinition(viewDef.withNewType(viewType));
-
- if (viewUpdates == null)
- viewUpdates = new ArrayList<>();
- viewUpdates.add(viewCopy);
- }
- break;
-
case DROP:
- assert columnName != null;
if (!cfm.isCQLTable())
throw new InvalidRequestException("Cannot drop columns from a non-CQL3 table");
- if (def == null)
- throw new InvalidRequestException(String.format("Column %s was not found in table %s", columnName, columnFamily()));
- switch (def.kind)
+ for (AlterTableStatementColumn colData : colNameList)
{
- case PARTITION_KEY:
- case CLUSTERING:
- throw new InvalidRequestException(String.format("Cannot drop PRIMARY KEY part %s", columnName));
- case REGULAR:
- case STATIC:
- ColumnDefinition toDelete = null;
- for (ColumnDefinition columnDef : cfm.partitionColumns())
- {
- if (columnDef.name.equals(columnName))
- {
- toDelete = columnDef;
- break;
- }
- }
- assert toDelete != null;
- cfm.removeColumnDefinition(toDelete);
- cfm.recordColumnDrop(toDelete, deleteTimestamp == null ? queryState.getTimestamp() : deleteTimestamp);
- break;
- }
+ columnName = colData.getColumnName().getIdentifier(cfm);
+ def = cfm.getColumnDefinition(columnName);
- // If the dropped column is required by any secondary indexes
- // we reject the operation, as the indexes must be dropped first
- Indexes allIndexes = cfm.getIndexes();
- if (!allIndexes.isEmpty())
- {
- ColumnFamilyStore store = Keyspace.openAndGetStore(cfm);
- Set<IndexMetadata> dependentIndexes = store.indexManager.getDependentIndexes(def);
- if (!dependentIndexes.isEmpty())
- throw new InvalidRequestException(String.format("Cannot drop column %s because it has " +
- "dependent secondary indexes (%s)",
- def,
- dependentIndexes.stream()
- .map(i -> i.name)
- .collect(Collectors.joining(","))));
- }
+ if (def == null)
+ throw new InvalidRequestException(String.format("Column %s was not found in table %s", columnName, columnFamily()));
- // If a column is dropped which is included in a view, we don't allow the drop to take place.
- boolean rejectAlter = false;
- StringBuilder builder = new StringBuilder();
- for (ViewDefinition view : views)
- {
- if (!view.includes(columnName)) continue;
+ switch (def.kind)
+ {
+ case PARTITION_KEY:
+ case CLUSTERING:
+ throw new InvalidRequestException(String.format("Cannot drop PRIMARY KEY part %s", columnName));
+ case REGULAR:
+ case STATIC:
+ ColumnDefinition toDelete = null;
+ for (ColumnDefinition columnDef : cfm.partitionColumns())
+ {
+ if (columnDef.name.equals(columnName))
+ {
+ toDelete = columnDef;
+ break;
+ }
+ }
+ assert toDelete != null;
+ cfm.removeColumnDefinition(toDelete);
+ cfm.recordColumnDrop(toDelete, deleteTimestamp == null ? queryState.getTimestamp() : deleteTimestamp);
+ break;
+ }
+
+ // If the dropped column is required by any secondary indexes
+ // we reject the operation, as the indexes must be dropped first
+ Indexes allIndexes = cfm.getIndexes();
+ if (!allIndexes.isEmpty())
+ {
+ ColumnFamilyStore store = Keyspace.openAndGetStore(cfm);
+ Set<IndexMetadata> dependentIndexes = store.indexManager.getDependentIndexes(def);
+ if (!dependentIndexes.isEmpty())
+ throw new InvalidRequestException(String.format("Cannot drop column %s because it has " +
+ "dependent secondary indexes (%s)",
+ def,
+ dependentIndexes.stream()
+ .map(i -> i.name)
+ .collect(Collectors.joining(","))));
+ }
+
+ // If a column is dropped which is included in a view, we don't allow the drop to take place.
+ boolean rejectAlter = false;
+ StringBuilder builder = new StringBuilder();
+ for (ViewDefinition view : views)
+ {
+ if (!view.includes(columnName)) continue;
+ if (rejectAlter)
+ builder.append(',');
+ rejectAlter = true;
+ builder.append(view.viewName);
+ }
if (rejectAlter)
- builder.append(',');
- rejectAlter = true;
- builder.append(view.viewName);
+ throw new InvalidRequestException(String.format("Cannot drop column %s, depended on by materialized views (%s.{%s})",
+ columnName.toString(),
+ keyspace(),
+ builder.toString()));
}
- if (rejectAlter)
- throw new InvalidRequestException(String.format("Cannot drop column %s, depended on by materialized views (%s.{%s})",
- columnName.toString(),
- keyspace(),
- builder.toString()));
break;
case OPTS:
if (attrs == null)
@@@ -345,58 -307,12 +311,10 @@@
return new Event.SchemaChange(Event.SchemaChange.Change.UPDATED, Event.SchemaChange.Target.TABLE, keyspace(), columnFamily());
}
- private static void validateAlter(CFMetaData cfm, ColumnDefinition def, AbstractType<?> validatorType)
- {
- switch (def.kind)
- {
- case PARTITION_KEY:
- if (validatorType instanceof CounterColumnType)
- throw new InvalidRequestException(String.format("counter type is not supported for PRIMARY KEY part %s", def.name));
-
- AbstractType<?> currentType = cfm.getKeyValidatorAsClusteringComparator().subtype(def.position());
- if (!validatorType.isValueCompatibleWith(currentType))
- throw new ConfigurationException(String.format("Cannot change %s from type %s to type %s: types are incompatible.",
- def.name,
- currentType.asCQL3Type(),
- validatorType.asCQL3Type()));
- break;
- case CLUSTERING:
- if (!cfm.isCQLTable())
- throw new InvalidRequestException(String.format("Cannot alter clustering column %s in a non-CQL3 table", def.name));
-
- AbstractType<?> oldType = cfm.comparator.subtype(def.position());
- // Note that CFMetaData.validateCompatibility already validate the change we're about to do. However, the error message it
- // sends is a bit cryptic for a CQL3 user, so validating here for a sake of returning a better error message
- // Do note that we need isCompatibleWith here, not just isValueCompatibleWith.
- if (!validatorType.isCompatibleWith(oldType))
- {
- throw new ConfigurationException(String.format("Cannot change %s from type %s to type %s: types are not order-compatible.",
- def.name,
- oldType.asCQL3Type(),
- validatorType.asCQL3Type()));
- }
- break;
- case REGULAR:
- case STATIC:
- // Thrift allows to change a column validator so CFMetaData.validateCompatibility will let it slide
- // if we change to an incompatible type (contrarily to the comparator case). But we don't want to
- // allow it for CQL3 (see #5882) so validating it explicitly here. We only care about value compatibility
- // though since we won't compare values (except when there is an index, but that is validated by
- // ColumnDefinition already).
- if (!validatorType.isValueCompatibleWith(def.type))
- throw new ConfigurationException(String.format("Cannot change %s from type %s to type %s: types are incompatible.",
- def.name,
- def.type.asCQL3Type(),
- validatorType.asCQL3Type()));
- break;
- }
- }
-
- @Override
public String toString()
{
- return String.format("AlterTableStatement(name=%s, type=%s, column=%s, validator=%s)",
+ return String.format("AlterTableStatement(name=%s, type=%s)",
cfName,
- oType,
- rawColumnName,
- validator);
+ oType);
}
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/d3882895/src/java/org/apache/cassandra/cql3/statements/AlterTypeStatement.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/statements/AlterTypeStatement.java
index 2c525b9,f2e1578..71d19fa
--- a/src/java/org/apache/cassandra/cql3/statements/AlterTypeStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/AlterTypeStatement.java
@@@ -52,17 -52,17 +52,17 @@@ public abstract class AlterTypeStatemen
protected abstract UserType makeUpdatedType(UserType toUpdate, KeyspaceMetadata ksm) throws InvalidRequestException;
- public static AlterTypeStatement addition(UTName name, ColumnIdentifier fieldName, CQL3Type.Raw type)
+ public static AlterTypeStatement addition(UTName name, FieldIdentifier fieldName, CQL3Type.Raw type)
{
- return new AddOrAlter(name, true, fieldName, type);
+ return new Add(name, fieldName, type);
}
- public static AlterTypeStatement alter(UTName name, ColumnIdentifier fieldName, CQL3Type.Raw type)
+ public static AlterTypeStatement alter(UTName name, FieldIdentifier fieldName, CQL3Type.Raw type)
{
- return new AddOrAlter(name, false, fieldName, type);
+ throw new InvalidRequestException("Altering of types is not allowed");
}
- public static AlterTypeStatement renames(UTName name, Map<ColumnIdentifier, ColumnIdentifier> renames)
+ public static AlterTypeStatement renames(UTName name, Map<FieldIdentifier, FieldIdentifier> renames)
{
return new Renames(name, renames);
}
@@@ -237,28 -245,26 +237,26 @@@
});
}
- private static class AddOrAlter extends AlterTypeStatement
+ private static class Add extends AlterTypeStatement
{
- private final boolean isAdd;
- private final ColumnIdentifier fieldName;
+ private final FieldIdentifier fieldName;
private final CQL3Type.Raw type;
- public AddOrAlter(UTName name, boolean isAdd, FieldIdentifier fieldName, CQL3Type.Raw type)
- public Add(UTName name, ColumnIdentifier fieldName, CQL3Type.Raw type)
++ public Add(UTName name, FieldIdentifier fieldName, CQL3Type.Raw type)
{
super(name);
- this.isAdd = isAdd;
this.fieldName = fieldName;
this.type = type;
}
- private UserType doAdd(UserType toUpdate) throws InvalidRequestException
+ protected UserType makeUpdatedType(UserType toUpdate, KeyspaceMetadata ksm) throws InvalidRequestException
{
- if (getIdxOfField(toUpdate, fieldName) >= 0)
+ if (toUpdate.fieldPosition(fieldName) >= 0)
throw new InvalidRequestException(String.format("Cannot add new field %s to type %s: a field of the same name already exists", fieldName, name));
- List<ByteBuffer> newNames = new ArrayList<>(toUpdate.size() + 1);
+ List<FieldIdentifier> newNames = new ArrayList<>(toUpdate.size() + 1);
newNames.addAll(toUpdate.fieldNames());
- newNames.add(fieldName.bytes);
+ newNames.add(fieldName);
AbstractType<?> addType = type.prepare(keyspace()).getType();
if (addType.referencesUserType(toUpdate.getNameAsString()))
@@@ -268,32 -274,8 +266,8 @@@
newTypes.addAll(toUpdate.fieldTypes());
newTypes.add(addType);
- return new UserType(toUpdate.keyspace, toUpdate.name, newNames, newTypes);
+ return new UserType(toUpdate.keyspace, toUpdate.name, newNames, newTypes, toUpdate.isMultiCell());
}
-
- private UserType doAlter(UserType toUpdate, KeyspaceMetadata ksm) throws InvalidRequestException
- {
- checkTypeNotUsedByAggregate(ksm);
-
- int idx = toUpdate.fieldPosition(fieldName);
- if (idx < 0)
- throw new InvalidRequestException(String.format("Unknown field %s in type %s", fieldName, name));
-
- AbstractType<?> previous = toUpdate.fieldType(idx);
- if (!type.prepare(keyspace()).getType().isCompatibleWith(previous))
- throw new InvalidRequestException(String.format("Type %s is incompatible with previous type %s of field %s in user type %s", type, previous.asCQL3Type(), fieldName, name));
-
- List<FieldIdentifier> newNames = new ArrayList<>(toUpdate.fieldNames());
- List<AbstractType<?>> newTypes = new ArrayList<>(toUpdate.fieldTypes());
- newTypes.set(idx, type.prepare(keyspace()).getType());
-
- return new UserType(toUpdate.keyspace, toUpdate.name, newNames, newTypes, toUpdate.isMultiCell());
- }
-
- protected UserType makeUpdatedType(UserType toUpdate, KeyspaceMetadata ksm) throws InvalidRequestException
- {
- return isAdd ? doAdd(toUpdate) : doAlter(toUpdate, ksm);
- }
}
private static class Renames extends AlterTypeStatement
http://git-wip-us.apache.org/repos/asf/cassandra/blob/d3882895/test/unit/org/apache/cassandra/cql3/ViewSchemaTest.java
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/d3882895/test/unit/org/apache/cassandra/cql3/validation/entities/FrozenCollectionsTest.java
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/d3882895/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/d3882895/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java
index 51ffab2,245be30..c728ad7
--- a/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java
@@@ -17,19 -17,18 +17,16 @@@
*/
package org.apache.cassandra.cql3.validation.operations;
++import org.junit.Assert;
++import org.junit.Test;
++
+import org.apache.cassandra.config.SchemaConstants;
import org.apache.cassandra.cql3.CQLTester;
import org.apache.cassandra.db.ColumnFamilyStore;
import org.apache.cassandra.db.Keyspace;
import org.apache.cassandra.exceptions.ConfigurationException;
--import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.exceptions.SyntaxException;
import org.apache.cassandra.schema.SchemaKeyspace;
- import org.apache.cassandra.transport.ProtocolVersion;
-import org.apache.cassandra.transport.Server;
--import org.apache.cassandra.utils.ByteBufferUtil;
--
--import org.junit.Assert;
--import org.junit.Test;
import static java.lang.String.format;
import static org.junit.Assert.assertEquals;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/d3882895/test/unit/org/apache/cassandra/cql3/validation/operations/UpdateTest.java
----------------------------------------------------------------------