blob: cb3e28b220e4896cfe5131530c2d817f42473e68 [file] [log] [blame]
[[cql-functions]]
= Functions
CQL supports 2 main categories of functions:
* xref:cassandra:developing/cql/functions.adoc#scalar-native-functions[scalar functions] that take a number of values and produce an output
* xref:cassandra:developing/cql/functions.adoc#aggregate-functions[aggregate functions] that aggregate multiple rows resulting from a `SELECT` statement
In both cases, CQL provides a number of native "hard-coded" functions and also allows for the creation of custom user-defined functions.
[NOTE]
====
By default, the use of user-defined functions is disabled by default for
security concerns (even when enabled, the execution of user-defined
functions is sandboxed and a "rogue" function should not be allowed to
do evil, but no sandbox is perfect so using user-defined functions is
opt-in). See the `user_defined_functions_enabled` in `cassandra.yaml` to
enable them.
====
A function is identifier by its name:
[source, bnf]
----
include::cassandra:example$BNF/function.bnf[]
----
== Scalar functions
[[scalar-native-functions]]
=== Native functions
==== Cast
The `cast` function can be used to converts one native datatype to
another.
The following table describes the conversions supported by the `cast`
function. Cassandra will silently ignore any cast converting a datatype
into its own datatype.
[cols=",",options="header",]
|===
|From |To
| `ascii` | `text`, `varchar`
| `bigint` | `tinyint`, `smallint`, `int`, `float`, `double`, `decimal`, `varint`,
`text`, `varchar`
| `boolean` | `text`, `varchar`
| `counter` | `tinyint`, `smallint`, `int`, `bigint`, `float`, `double`, `decimal`,
`varint`, `text`, `varchar`
| `date` | `timestamp`
| `decimal` | `tinyint`, `smallint`, `int`, `bigint`, `float`, `double`, `varint`,
`text`, `varchar`
| `double` | `tinyint`, `smallint`, `int`, `bigint`, `float`, `decimal`, `varint`,
`text`, `varchar`
| `float` | `tinyint`, `smallint`, `int`, `bigint`, `double`, `decimal`, `varint`,
`text`, `varchar`
| `inet` | `text`, `varchar`
| `int` | `tinyint`, `smallint`, `bigint`, `float`, `double`, `decimal`, `varint`,
`text`, `varchar`
| `smallint` | `tinyint`, `int`, `bigint`, `float`, `double`, `decimal`, `varint`,
`text`, `varchar`
| `time` | `text`, `varchar`
| `timestamp` | `date`, `text`, `varchar`
| `timeuuid` | `timestamp`, `date`, `text`, `varchar`
| `tinyint` | `tinyint`, `smallint`, `int`, `bigint`, `float`, `double`, `decimal`,
`varint`, `text`, `varchar`
| `uuid` | `text`, `varchar`
| `varint` | `tinyint`, `smallint`, `int`, `bigint`, `float`, `double`, `decimal`,
`text`, `varchar`
|===
The conversions rely strictly on Java's semantics. For example, the
double value 1 will be converted to the text value '1.0'. For instance:
[source,sql]
----
SELECT avg(cast(count as double)) FROM myTable;
----
==== Token
The `token` function computes the token for a given partition key.
The exact signature of the token function depends on the table concerned and the partitioner used by the cluster.
The type of the arguments of the `token` depend on the partition key column type. The returned type depends on the defined partitioner:
[cols=",",options="header",]
|===
|Partitioner | Returned type
| Murmur3Partitioner | `bigint`
| RandomPartitioner | `varint`
| ByteOrderedPartitioner | `blob`
|===
For example, consider the following table:
[source,sql]
----
include::cassandra:example$CQL/create_table_simple.cql[]
----
The table uses the default Murmur3Partitioner.
The `token` function uses the single argument `text`, because the partition key is `userid` of text type.
The returned type will be `bigint`.
==== Uuid
The `uuid` function takes no parameters and generates a random type 4
uuid suitable for use in `INSERT` or `UPDATE` statements.
==== Timeuuid functions
===== `now`
The `now` function takes no arguments and generates, on the coordinator
node, a new unique timeuuid at the time the function is invoked. Note
that this method is useful for insertion but is largely nonsensical in
`WHERE` clauses.
For example, a query of the form:
[source,sql]
----
include::cassandra:example$CQL/timeuuid_now.cql[]
----
will not return a result, by design, since the value returned by
`now()` is guaranteed to be unique.
`current_timeuuid` is an alias of `now`.
===== `min_timeuuid` and `max_timeuuid`
The `min_timeuuid` function takes a `timestamp` value `t`, either a timestamp or a date string.
It returns a _fake_ `timeuuid` corresponding to the _smallest_ possible `timeuuid` for timestamp `t`.
The `max_timeuuid` works similarly, but returns the _largest_ possible `timeuuid`.
For example:
[source,sql]
----
include::cassandra:example$CQL/timeuuid_min_max.cql[]
----
will select all rows where the `timeuuid` column `t` is later than `'2013-01-01 00:05+0000'` and earlier than `'2013-02-02 10:00+0000'`.
The clause `t >= maxTimeuuid('2013-01-01 00:05+0000')` would still _not_ select a `timeuuid` generated exactly at '2013-01-01 00:05+0000', and is essentially equivalent to `t > maxTimeuuid('2013-01-01 00:05+0000')`.
[NOTE]
====
The values generated by `min_timeuuid` and `max_timeuuid` are called _fake_ UUIDs because they do not respect the time-based UUID generation process
specified by the http://www.ietf.org/rfc/rfc4122.txt[IETF RFC 4122].
In particular, the value returned by these two methods will not be unique.
Thus, only use these methods for *querying*, not for *insertion*, to prevent possible data overwriting.
====
==== Datetime functions
Retrieving the current date and time:
[cols=",",options="header",]
|===
|Function name |Output type
| `current_date` | `date`
| `current_time` | `time`
| `current_timestamp` | `timestamp`
| `current_timeuuid` | `timeUUID`
|===
For example the last two days of data can be retrieved using:
[source,sql]
----
include::cassandra:example$CQL/current_date.cql[]
----
===== Time conversion functions
A number of functions are provided to convert a `timeuuid`, a `timestamp` or a `date` into another `native` type.
[cols=",,",options="header",]
|===
|Function name |Input type |Description
| `to_date` | `timeuuid` | Converts the `timeuuid` argument into a `date` type
| `to_date` | `timestamp` | Converts the `timestamp` argument into a `date` type
| `to_timestamp` | `timeuuid` | Converts the `timeuuid` argument into a `timestamp` type
| `to_timestamp` | `date` | Converts the `date` argument into a `timestamp` type
| `to_unix_timestamp` | `timeuuid` | Converts the `timeuuid` argument into a `bigInt` raw value
| `to_unix_timestamp` | `timestamp` | Converts the `timestamp` argument into a `bigInt` raw value
| `to_unix_timestamp` | `date` | Converts the `date` argument into a `bigInt` raw value
|===
For example, a timestamp can be converted to a date with the following:
[source,cql]
----
include::cassandra:example$CQL/to_date.cql[]
----
==== Blob conversion functions
A number of functions are provided to convert the native types into
binary data, or a `blob`.
For every xref:cassandra:developing/cql/types.adoc#native-types[type] supported by CQL, the function `type_as_blob` takes an argument of type `type` and returns it as a `blob`.
Conversely, the function `blob_as_type` takes a 64-bit `blob` argument and converts it to a `bigint` value.
For example, `bigint_as_blob(3)` returns `0x0000000000000003` and `blob_as_bigint(0x0000000000000003)` returns `3`.
==== Length Functions
CQL supports two functions to retrieve the length of data without returning the data itself. Note that while
network bandwidth is reduced and memory is freed earlier, these functions still require data to be read from disk.
[cols=",,",options="header",]
|===
|Function name |Input type |Description
| `octet_length` | All xref:cassandra:developing/cql/types.adoc#native-types[types] | Returns the length of the data in bytes.
| `length` | `text` | Returns the string's length - the count of UTF-8 code units.
|===
The `octet_length` function is defined for every xref:cassandra:developing/cql/types.adoc#native-types[type]
supported by CQL, and represents the number of bytes of the underlying `ByteBuffer` representation, not accounting for
metadata overhead. Equivalent to Java's
https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/nio/Buffer.html#remaining()[`ByteBuffer#remaining()`]
when data has just been read, which is also the `length` of the type when encoded to `byte[]`. Some examples
for common types:
[cols=",,",options="header",]
|===
|CQL Type | `octet_length` |Description
| `tinyint` | `1` | 8-bit signed integer
| `smallint` | `2` | 16-bit signed integer
| `int` | `4` | 32-bit signed integer
| `bigint` | `8` | 64-bit signed integer
| `float` | `4` | 32-bit IEEE-754 floating point
| `double` | `8` | 64-bit IEEE-754 floating point
| `blob` | Number of bytes in blob | Variable length byte string
| `text` | Number of bytes in `text_as_blob` representation | Variable length character string
|===
The `length` function is only defined for UTF-8 strings (`text`) and returns the length of that string, meaning the
number of UTF-8 code units. Equivalent to Java's
https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/lang/String.html#length()[`String#length()`] or
Python 3's `len` function when applied to a string.
These length functions return `null` when the input is `null`.
==== Math Functions
Cql provides the following math functions: `abs`, `exp`, `log`, `log10`, and `round`.
The return type for these functions is always the same as the input type.
[cols=",",options="header",]
|===
|Function name |Description
|`abs` | Returns the absolute value of the input.
|`exp` | Returns the number e to the power of the input.
|`log` | Returns the natural log of the input.
|`log10` | Returns the log base 10 of the input.
|`round` | Rounds the input to the nearest whole number using rounding mode `HALF_UP`.
|===
==== Collection functions
A number of functions are provided to operate on collection columns.
[cols=",,",options="header",]
|===
|Function name |Input type |Description
| `map_keys` | `map` | Gets the keys of the `map` argument, returned as a `set`.
| `map_values` | `map` | Gets the values of the `map` argument, returned as a `list`.
| `collection_count` | `map`, `set` or `list` | Gets the number of elements in the collection argument.
| `collection_min` | `set` or `list` | Gets the minimum element in the collection argument.
| `collection_max` | `set` or `list` | Gets the maximum element in the collection argument.
| `collection_sum` | numeric `set` or `list` | Computes the sum of the elements in the collection argument. The returned value is of the same type as the input collection elements, so there is a risk of overflowing the data type if the sum of the values exceeds the maximum value that the type can represent.
| `collection_avg` | numeric `set` or `list` | Computes the average of the elements in the collection argument. The average of an empty collection returns zero. The returned value is of the same type as the input collection elements, which might include rounding and truncations. For example `collection_avg([1, 2])` returns `1` instead of `1.5`.
|===
[[data-masking-functions]]
==== Data masking functions
A number of functions allow to obscure the real contents of a column containing sensitive data.
include::cassandra:partial$masking_functions.adoc[]
[[vector-similarity-functions]]
===== Vector similarity functions
A number of functions allow to obtain the similarity score between vectors of floats.
include::cassandra:partial$vector-search/vector_functions.adoc[]
[[human-helper-functions]]
==== Human helper functions
For user's convenience, there are currently two functions which are converting values to more human-friendly representations.
[cols=",,",options="header",]
|===
| Function name | Input type | Description
| `format_bytes` |`int`, `tinyint`, `smallint`, `bigint`, `varint`, `ascii`, `text` | Converts values in bytes to a more human-friendly representation.
| `format_time` |`int`, `tinyint`, `smallint`, `bigint`, `varint`, `ascii`, `text` | Converts values in milliseconds to a more human-friendly representation.
|===
===== format_bytes
This function looks at values in a column as if it was in bytes, and it will convert it to whatever a user pleases. Supported units are: `B`, `KiB`, `MiB` and `GiB`. The result will be rounded to two decimal places.
Supported column types on which this function is possible to be applied:
`INT`, `TINYINT`, `SMALLINT`, `BIGINT`, `VARINT`, `ASCII`, `TEXT`.
For `ASCII` and `TEXT` types, text of such column has to be a non-negative number.
Return values can be max of `Long.MAX_VALUE`, If the conversion produces overflown value, `Long.MAX_VALUE` will be returned.
[NOTE]
====
The actual return value of the `Long.MAX_VALUE` will be 9223372036854776000 due to the limitations of floating-point precision.
====
There are three ways how to call this function.
Let's have this table:
[source,sql]
----
cqlsh> select * from ks.tb;
id | val
----+----------------
5 | 60000
1 | 1234234
2 | 12342341234234
4 | 60001
7 | null
6 | 43
3 | 123423
----
with schema
[source,sql]
----
CREATE TABLE ks.tb (
id int PRIMARY KEY,
val bigint
);
----
Imagine that we wanted to look at `val` values as if they were in mebibytes. We would like to have more human-friendly output in order to not visually divide the values by 1024 in order to get them in respective bigger units. The following function call may take just a column itself as an argument, and it will
automatically convert it.
[NOTE]
====
The default source unit for `format_bytes` function is _bytes_, (`B`).
====
[source,sql]
----
cqlsh> select format_bytes(val) from ks.tb;
system.format_bytes(val)
--------------------------
58.59 KiB
1.18 MiB
11494.7 GiB
58.59 KiB
null
43 B
120.53 KiB
----
The second way to call `format_bytes` functions is to specify into what size unit we would like to see all
values to be converted to. For example, we want all size to be represented in mebibytes, hence we do:
[source,sql]
----
cqlsh> select format_bytes(val, 'MiB') from ks.tb;
system.format_bytes(val, 'MiB')
----------------------------------
0.06 MiB
1.18 MiB
11770573.84 MiB
0.06 MiB
null
0 MiB
0.12 MiB
----
Lastly, we can specify a source unit and a target unit. A source unit tells what unit that column is logically of, the target unit tells what unit we want these values to be converted to. For example,
if our column values are in kibibytes and we want to convert them to mebibytes, we would perform the following conversion:
[source,sql]
----
cqlsh> select format_bytes(val, 'Kib', 'MiB') from ks.tb;
system.format_bytes(val, 'KiB', 'MiB')
----------------------------------------
58.59 MiB
1205.31 MiB
12053067611.56 MiB
58.59 MiB
null
0.04 MiB
120.53 MiB
----
===== format_time
Similarly to `format_bytes`, we can do transformations on duration-like columns.
Supported units are: `d`, `h`, `m`, `s`, `ms`, `us`, `µs`, `ns`.
Supported column types on which this function is possible to be applied:
`INT`, `TINYINT`, `SMALLINT`, `BIGINT`, `VARINT`, `ASCII`, `TEXT`. For `ASCII` and `TEXT` types, text of such column has to be a non-negative number.
Return values can be max of `Double.MAX_VALUE`, If the conversion produces overflown value, `Double.MAX_VALUE` will be returned.
[NOTE]
====
The default source unit for `format_time` function is _milliseconds_, (`ms`).
====
[source,sql]
----
cqlsh> select format_time(val) from ks.tb;
system.format_time(val)
-------------------------
1 m
20.57 m
142851.17 d
1 m
null
43 ms
2.06 m
----
We may specify what unit we want that value to be converted to, give the column's values are in milliseconds:
[source,sql]
----
cqlsh> select format_time(val, 'm') from ks.tb;
system.format_time(val, 'm')
------------------------------
1 m
20.57 m
205705687.24 m
1 m
null
0 m
2.06 m
----
Lastly, we can specify both source and target values:
[source,sql]
----
cqlsh> select format_time(val, 's', 'h') from ks.tb;
system.format_time(val, 's', 'h')
-----------------------------------
16.67 h
342.84 h
3428428120.62 h
16.67 h
null
0.01 h
34.28 h
----
[[user-defined-scalar-functions]]
=== User-defined functions
User-defined functions (UDFs) execute user-provided code in Cassandra.
By default, Cassandra supports defining functions in _Java_.
UDFs are part of the Cassandra schema, and are automatically propagated to all nodes in the cluster.
UDFs can be _overloaded_, so that multiple UDFs with different argument types can have the same function name.
[NOTE]
====
_JavaScript_ user-defined functions have been deprecated in Cassandra 4.1. In preparation for Cassandra 5.0, their removal is
already in progress. For more information - https://issues.apache.org/jira/browse/CASSANDRA-17281[CASSANDRA-17281], https://issues.apache.org/jira/browse/CASSANDRA-18252[CASSANDRA-18252].
====
For example:
[source,sql]
----
include::cassandra:example$CQL/function_overload.cql[]
----
User-defined functions (UDFs) are prone to the typical issues associated with the programming language they are written in.
Accordingly, implementations should be protected against null pointer exceptions, illegal arguments, or any other potential source of exceptions.
An exception during function execution will result in the entire statement failing.
Valid queries for UDF use are `SELECT`, `INSERT` and `UPDATE` statements.
_Complex_ types like collections, tuple types and user-defined types are valid argument and return types in UDFs.
Tuple types and user-defined types use the DataStax Java Driver conversion functions.
Please see the Java Driver documentation for details on handling tuple types and user-defined types.
Arguments for functions can be literals or terms.
Prepared statement placeholders can be used, too.
Note the use the double dollar-sign syntax to enclose the UDF source code.
For example:
[source,sql]
----
include::cassandra:example$CQL/function_dollarsign.cql[]
----
The implicitly available `udfContext` field (or binding for script UDFs) provides the necessary functionality to create new UDT and tuple values:
[source,sql]
----
include::cassandra:example$CQL/function_udfcontext.cql[]
----
The definition of the `UDFContext` interface can be found in the Apache Cassandra source code for `org.apache.cassandra.cql3.functions.UDFContext`.
[source,java]
----
include::cassandra:example$JAVA/udfcontext.java[]
----
Java UDFs already have some imports for common interfaces and classes defined. These imports are:
[source,java]
----
include::cassandra:example$JAVA/udf_imports.java[]
----
Please note, that these convenience imports are not available for script UDFs.
[[create-function-statement]]
=== CREATE FUNCTION statement
Creating a new user-defined function uses the `CREATE FUNCTION` statement:
[source,bnf]
----
include::cassandra:example$BNF/create_function_statement.bnf[]
----
For example:
[source,sql]
----
include::cassandra:example$CQL/create_function.cql[]
----
`CREATE FUNCTION` with the optional `OR REPLACE` keywords creates either a function or replaces an existing one with the same signature.
A `CREATE FUNCTION` without `OR REPLACE` fails if a function with the same signature already exists.
If the optional `IF NOT EXISTS` keywords are used, the function will only be created only if another function with the same signature does not
exist.
`OR REPLACE` and `IF NOT EXISTS` cannot be used together.
Behavior for `null` input values must be defined for each function:
* `RETURNS NULL ON NULL INPUT` declares that the function will always return `null` if any of the input arguments is `null`.
* `CALLED ON NULL INPUT` declares that the function will always be executed.
==== Function Signature
Signatures are used to distinguish individual functions. The signature consists of a fully-qualified function name of the <keyspace>.<function_name> and a concatenated list of all the argument types.
Note that keyspace names, function names and argument types are subject to the default naming conventions and case-sensitivity rules.
Functions belong to a keyspace; if no keyspace is specified, the current keyspace is used.
User-defined functions are not allowed in the system keyspaces.
[[drop-function-statement]]
=== DROP FUNCTION statement
Dropping a function uses the `DROP FUNCTION` statement:
[source, bnf]
----
include::cassandra:example$BNF/drop_function_statement.bnf[]
----
For example:
[source,sql]
----
include::cassandra:example$CQL/drop_function.cql[]
----
You must specify the argument types of the function, the arguments_signature, in the drop command if there are multiple overloaded functions with the same name but different signatures.
`DROP FUNCTION` with the optional `IF EXISTS` keywords drops a function if it exists, but does not throw an error if it doesn't.
[[aggregate-functions]]
== Aggregate functions
Aggregate functions work on a set of rows.
Values for each row are input, to return a single value for the set of rows aggregated.
If `normal` columns, `scalar functions`, `UDT` fields, `writetime`, or `ttl` are selected together with aggregate functions, the values
returned for them will be the ones of the first row matching the query.
=== Native aggregates
[[count-function]]
==== Count
The `count` function can be used to count the rows returned by a query.
For example:
[source,sql]
----
include::cassandra:example$CQL/count.cql[]
----
It also can count the non-null values of a given column:
[source,sql]
----
include::cassandra:example$CQL/count_nonnull.cql[]
----
==== Max and Min
The `max` and `min` functions compute the maximum and the minimum value returned by a query for a given column.
For example:
[source,sql]
----
include::cassandra:example$CQL/min_max.cql[]
----
==== Sum
The `sum` function sums up all the values returned by a query for a given column.
The returned value is of the same type as the input collection elements, so there is a risk of overflowing if the sum of the values exceeds the maximum value that the type can represent.
For example:
[source,sql]
----
include::cassandra:example$CQL/sum.cql[]
----
The returned value is of the same type as the input values, so there is a risk of overflowing the type if the sum of the
values exceeds the maximum value that the type can represent. You can use type casting to cast the input values as a
type large enough to contain the type. For example:
[source,sql]
----
include::cassandra:example$CQL/sum_with_cast.cql[]
----
==== Avg
The `avg` function computes the average of all the values returned by a query for a given column.
For example:
[source,sql]
----
include::cassandra:example$CQL/avg.cql[]
----
The average of an empty collection returns zero.
The returned value is of the same type as the input values, which might include rounding and truncations.
For example `collection_avg([1, 2])` returns `1` instead of `1.5`.
You can use type casting to cast to a type with the desired decimal precision. For example:
[source,sql]
----
include::cassandra:example$CQL/avg_with_cast.cql[]
----
[[user-defined-aggregates-functions]]
=== User-Defined Aggregates (UDAs)
User-defined aggregates allow the creation of custom aggregate functions.
User-defined aggregates can be used in `SELECT` statement.
Each aggregate requires an _initial state_ of type `STYPE` defined with the `INITCOND`value (default value: `null`).
The first argument of the state function must have type `STYPE`.
The remaining arguments of the state function must match the types of the user-defined aggregate arguments.
The state function is called once for each row, and the value returned by the state function becomes the new state.
After all rows are processed, the optional `FINALFUNC` is executed with last state value as its argument.
The `STYPE` value is mandatory in order to distinguish possibly overloaded versions of the state and/or final function, since the
overload can appear after creation of the aggregate.
A complete working example for user-defined aggregates (assuming that a
keyspace has been selected using the `USE` statement):
[source,sql]
----
include::cassandra:example$CQL/uda.cql[]
----
[[create-aggregate-statement]]
=== CREATE AGGREGATE statement
Creating (or replacing) a user-defined aggregate function uses the
`CREATE AGGREGATE` statement:
[source, bnf]
----
include::cassandra:example$BNF/create_aggregate_statement.bnf[]
----
See above for a complete example.
The `CREATE AGGREGATE` command with the optional `OR REPLACE` keywords creates either an aggregate or replaces an existing one with the same
signature.
A `CREATE AGGREGATE` without `OR REPLACE` fails if an aggregate with the same signature already exists.
The `CREATE AGGREGATE` command with the optional `IF NOT EXISTS` keywords creates an aggregate if it does not already exist.
The `OR REPLACE` and `IF NOT EXISTS` phrases cannot be used together.
The `STYPE` value defines the type of the state value and must be specified.
The optional `INITCOND` defines the initial state value for the aggregate; the default value is `null`.
A non-null `INITCOND` must be specified for state functions that are declared with `RETURNS NULL ON NULL INPUT`.
The `SFUNC` value references an existing function to use as the state-modifying function.
The first argument of the state function must have type `STYPE`.
The remaining arguments of the state function must match the types of the user-defined aggregate arguments.
The state function is called once for each row, and the value returned by the state function becomes the new state.
State is not updated for state functions declared with `RETURNS NULL ON NULL INPUT` and called with `null`.
After all rows are processed, the optional `FINALFUNC` is executed with last state value as its argument.
It must take only one argument with type `STYPE`, but the return type of the `FINALFUNC` may be a different type.
A final function declared with `RETURNS NULL ON NULL INPUT` means that the aggregate's return value will be `null`, if the last state is `null`.
If no `FINALFUNC` is defined, the overall return type of the aggregate function is `STYPE`.
If a `FINALFUNC` is defined, it is the return type of that function.
[[drop-aggregate-statement]]
=== DROP AGGREGATE statement
Dropping a user-defined aggregate function uses the `DROP AGGREGATE`
statement:
[source, bnf]
----
include::cassandra:example$BNF/drop_aggregate_statement.bnf[]
----
For instance:
[source,sql]
----
include::cassandra:example$CQL/drop_aggregate.cql[]
----
The `DROP AGGREGATE` statement removes an aggregate created using `CREATE AGGREGATE`.
You must specify the argument types of the aggregate to drop if there are multiple overloaded aggregates with the same name but a
different signature.
The `DROP AGGREGATE` command with the optional `IF EXISTS` keywords drops an aggregate if it exists, and does nothing if a function with the
signature does not exist.
[[writetime-and-ttl-functions]]
==== `WRITETIME`, `MINWRITETIME`, `MAXWRITETIME` and `TTL` functions
These metadata functions are only allowed in `SELECT` statements: `WRITETIME`, `MINWRITETIME`, `MAXWRITETIME` and `TTL`.
The functions take only one argument, a column name, and retrieve metadata stored internally for the column. If the column is a collection or UDT, it's possible to add element
selectors, such as `WRITETTIME(phones[2..4])` or `WRITETTIME(user.name)`.
* `WRITETIME` stores the timestamp of the value of the column.
* `MINWRITETIME` stores the smallest timestamp of the value of the column. For non-collection and non-UDT columns, `MINWRITETIME`
is equivalent to `WRITETIME`. In the other cases, it returns the smallest timestamp of the values in the column.
* `MAXWRITETIME` stores the largest timestamp of the value of the column. For non-collection and non-UDT columns, `MAXWRITETIME`
is equivalent to `WRITETIME`. In the other cases, it returns the largest timestamp of the values in the column.
* `TTL` stores the remaining time to live (in seconds) for the value of the column if it is set to expire; otherwise the value is `null`.
The `WRITETIME` and `TTL` functions can be used on multi-cell columns such as non-frozen collections or non-frozen
user-defined types. In that case, the functions will return the list of timestamps or TTLs for each selected cell.