Skip to main content

New limitations in v24.1

This section describes newly identified limitations in CockroachDB v24.1.

PL/pgSQL

  • It is not possible to use a variable as a target more than once in the same INTO clause. For example, SELECT 1, 2 INTO x, x;.
  • PLpgSQL variable declarations cannot inherit the type of a table row or column using %TYPE or %ROWTYPE syntax.

UDFs and stored procedures

  • Routines cannot be invoked with named arguments, e.g., SELECT foo(a => 1, b => 2); or SELECT foo(b := 1, a := 2);.
  • Routines cannot be created if they reference temporary tables.
  • Routines cannot be created with unnamed INOUT parameters. For example, CREATE PROCEDURE p(INOUT INT) AS $$ BEGIN NULL; END; $$ LANGUAGE PLpgSQL;.
  • Routines cannot be created if they return fewer columns than declared. For example, CREATE FUNCTION f(OUT sum INT, INOUT a INT, INOUT b INT) LANGUAGE SQL AS $$ SELECT (a + b, b); $$;.
  • A RECORD-returning UDF cannot be created without a RETURN statement in the root block, which would restrict the wildcard type to a concrete one.

Physical cluster replication fail back to primary cluster

When you to a cluster that was previously the primary cluster, you should fail over to the LATEST timestamp. Using a may lead to the failback failing.

Generic query plans

  • Because use lookup joins instead of the scans and revscans used by custom query plans, generic query plans do not perform as well as custom query plans in some cases.
  • are not included in the . This means a generic query plan built and optimized for a prepared statement in one session cannot be used by another session. To reuse generic query plans for maximum performance, a prepared statement should be executed multiple times instead of prepared and executed once.

Limitations from v23.2 and earlier

This section describes limitations from previous CockroachDB versions that still impact v24.1.

SQL statements

Syntax and behavior differences from PostgreSQL

CockroachDB supports the PostgreSQL wire protocol and the majority of its syntax. For a list of known differences in syntax and behavior between CockroachDB and PostgreSQL, see .

AS OF SYSTEM TIME limitations

  • CockroachDB does not support placeholders in . The time value must be a constant value embedded in the SQL string.
  • The ANALYZE alias of does not support specifying an timestamp. ANALYZE statements use AS OF SYSTEM TIME '-0.001ms' automatically. For more control over the statistics interval, use the CREATE STATISTICS syntax instead.

COPY syntax not supported by CockroachDB

CockroachDB does not yet support the following COPY syntax:
  • COPY ... WITH FREEZE.
  • COPY ... WITH QUOTE.
  • COPY ... FROM ... WHERE <expr>.

IMPORT INTO limitations

has the following limitations:
  • While importing into an existing table, the table is taken offline.
  • After importing into an existing table, will be un-validated and need to be .
  • Imported rows must not conflict with existing rows in the table or any unique secondary indexes.
  • IMPORT INTO works for only a single existing table.
  • IMPORT INTO can sometimes fail with a “context canceled” error, or can restart itself many times without ever finishing. If this is happening, it is likely due to a high amount of disk contention. This can be mitigated by setting the kv.bulk_io_write.max_rate to a value below your max disk write speed. For example, to set it to 10MB/s, execute:

ALTER VIEW limitations

ALTER VIEW does not currently support:
  • Changing the statement executed by a view. Instead, you must drop the existing view and create a new view.
  • Renaming a view that other views depend on. This feature may be added in the future.

Row-Level TTL limitations

  • Any queries you run against tables with Row-Level TTL enabled (or against tables that have that reference TTL-enabled tables) do not filter out expired rows from the result set (this includes and ). This feature may be added in a future release. For now, follow the instructions in .
  • Tables with Row-Level TTL can be referenced by . TTL deletes are issued as regular statements, so inbound foreign keys apply. If an inbound foreign key uses ON DELETE RESTRICT and referencing rows exist, the TTL job fails with a foreign key violation.
  • Enabling Row-Level TTL on a table with multiple can have negative performance impacts on a cluster, including increased and . This is particularly true for large tables with terabytes of data and billions of rows that are split up into multiple ranges across separate nodes.
    • Increased latency may occur because secondary indexes aren’t necessarily stored on the same underlying as a table’s . Further, the secondary indexes’ ranges may have located on different nodes than the primary index.
    • Increased contention may occur because must be written as part of performing the deletions.
    • Finally, secondary indexes can also have a negative impact on the overall performance of . According to internal testing, the is worse on tables with secondary indexes. If you encounter this situation, decreasing the may help by decreasing the number of ranges that need to be accessed by the job.

CAST expressions containing a subquery with an ENUM target are not supported

Casting subqueries to ENUMs in views and UDFs is not supported.

Statements containing multiple modification subqueries of the same table are disallowed

Statements containing multiple modification subqueries mutating the same row could cause corruption. These statements are disallowed by default, but you can enable multiple modification subqueries with one the following:
  • Set the sql.multiple_modifications_of_table.enabled to true.
  • Use the enable_multiple_modifications_of_table .
If multiple mutations inside the same statement affect different tables with relations and ON CASCADE clauses between them, the results will be different from what is expected in PostgreSQL.

Using default_int_size session variable in batch of statements

When setting the default_int_size in a batch of statements such as SET default_int_size='int4'; SELECT 1::IN, the default_int_size variable will not take effect until the next statement. Statement parsing is asynchronous with statement execution. As a workaround, set default_int_size via your database driver, or ensure that SET default_int_size is in its own statement.

Overload resolution for collated strings

Many string operations are not properly overloaded for , for example:

Current sequence value not checked when updating min/max value

Altering the minimum or maximum value of a series does not check the current value of a series. This means that it is possible to silently set the maximum to a value less than, or a minimum value greater than, the current value.

null_ordered_last does not produce correct results with tuples

By default, CockroachDB orders NULLs before all other values. For compatibility with PostgreSQL, the null_ordered_last was added, which changes the default to order NULL values after all other values. This works in most cases, due to some transformations CockroachDB makes in the optimizer to add extra ordering columns. However, it does not work when the ordering column is a tuple.

Functions and procedures

PL/pgSQL support

  • PL/pgSQL arguments cannot be referenced with ordinals (e.g., $1, $2).
  • The following statements are not supported:
    • FOR loops, including FOR cursor loops, FOR query loops, and FOREACH loops.
    • RETURN NEXT and RETURN QUERY.
    • PERFORM, EXECUTE, GET DIAGNOSTICS, and CASE.
  • PL/pgSQL exception blocks cannot catch .
  • RAISE statements cannot be annotated with names of schema objects related to the error (i.e., using COLUMN, CONSTRAINT, DATATYPE, TABLE, or SCHEMA).
  • RAISE statements message the client directly, and do not produce log output.
  • ASSERT debugging checks are not supported.
  • RECORD parameters and variables are not supported in .
  • Variable shadowing (e.g., declaring a variable with the same name in an inner block) is not supported in PL/pgSQL.
  • Syntax for accessing members of composite types without parentheses is not supported.
  • NOT NULL variable declarations are not supported.
  • Cursors opened in PL/pgSQL execute their queries on opening, affecting performance and resource usage.
  • Cursors in PL/pgSQL cannot be declared with arguments.
  • OPEN FOR EXECUTE is not supported for opening cursors.
  • The print_strict_params option is not supported in PL/pgSQL.
  • The FOUND local variable, which checks whether a statement affected any rows, is not supported in PL/pgSQL.
  • By default, when a PL/pgSQL variable conflicts with a column name, CockroachDB resolves the ambiguity by treating it as a column reference rather than a variable reference. This behavior differs from PostgreSQL, where an ambiguous column error is reported, and it is possible to change the plpgsql.variable_conflict setting in order to prefer either columns or variables.
  • It is not possible to define a RECORD-returning PL/pgSQL function that returns different-typed expressions from different RETURN statements. CockroachDB requires a consistent return type for RECORD-returning functions.
  • Variables cannot be declared with an associated collation using the COLLATE keyword.
  • Variables cannot be accessed using the label.var_name pattern.

UDF and stored procedure support

  • Routines cannot be created with an OUT parameter of type RECORD.
  • DDL statements (e.g., CREATE TABLE, CREATE INDEX) are not allowed within UDFs or stored procedures.
  • Polymorphic types cannot be cast to other types (e.g., TEXT) within routine parameters.
  • COMMIT and ROLLBACK statements are not supported within nested procedures.
  • User-defined functions are not currently supported in:
    • Expressions (column, index, constraint) in tables.
    • Views.
  • User-defined functions cannot call themselves recursively.
  • (CTE), recursive or non-recursive, are not supported in (UDF). That is, you cannot use a WITH clause in the body of a UDF.
  • The setval function cannot be resolved when used inside UDF bodies.
  • Casting subqueries to in UDFs is not supported.

Transactions

Read Committed features and performance

has the following limitations:
  • Schema changes (e.g., , , ) cannot be performed within explicit READ COMMITTED transactions, and will cause transactions to abort. As a workaround, to SERIALIZABLE.
  • READ COMMITTED transactions performing INSERT, UPDATE, or UPSERT cannot access tables in which and constraints exist, the region is not included in the constraint, and the region cannot be computed from the constraint columns.
  • Multi-column-family checks during updates are not supported under READ COMMITTED isolation.
  • Because locks acquired by checks, , and are fully replicated under READ COMMITTED isolation, some queries experience a delay for Raft replication.
  • checks are not performed in parallel under READ COMMITTED isolation.
  • statements are less optimized under READ COMMITTED isolation than under SERIALIZABLE isolation. Under READ COMMITTED isolation, SELECT FOR UPDATE and SELECT FOR SHARE usually perform an extra lookup join for every locked table when compared to the same queries under SERIALIZABLE. In addition, some optimization steps (such as de-correlation of correlated ) are not currently performed on these queries.
  • Regardless of isolation level, statements in CockroachDB do not prevent insertion of new rows matching the search condition (i.e., ). This matches PostgreSQL behavior at all isolation levels.

Follower reads

Exact staleness reads and long-running writes
Long-running write transactions will create with a timestamp near when the transaction began. When an exact staleness follower read encounters a write intent, it will often end up in a , waiting for the operation to complete; however, this runs counter to the benefit exact staleness reads provide. To counteract this, you can issue all follower reads in explicit :
Exact staleness read timestamps must be far enough in the past
If an exact staleness read is not using an value far enough in the past, CockroachDB cannot perform a follower read. Instead, the read must access the . This adds network latency if the leaseholder is not the closest replica to the gateway node. Most users will to get a timestamp far enough in the past that there is a high probability of getting a follower read.
Bounded staleness read limitations
Bounded staleness reads have the following limitations:
  • They must be used in a .
  • They must read from a single row.
  • They must not require an . In other words, the index used by the read query must be either a , or some other index that covers the entire query by all columns.
For example, let’s look at a read query that cannot be served as a bounded staleness read. We will use a , which automatically loads the .
As noted by the error message, this query cannot be served as a bounded staleness read because in this case it would touch more than one row. Even though we used a , the query would still have to touch more than one row in order to filter out the additional results. We can verify that more than one row would be touched by issuing on the same query, but without the clause:
The output verifies that this query performs a scan of the primary on the promo_codes table, which is why it cannot be used for a bounded staleness read. For an example showing how to successfully perform a bounded staleness read, see .

SELECT FOR UPDATE locks are dropped on lease transfers and range splits/merges

  • SKIP LOCKED cannot be used for tables with multiple .
  • By default under SERIALIZABLE isolation, locks acquired using SELECT ... FOR UPDATE and SELECT ... FOR SHARE are implemented as fast, in-memory . If a or occurs on a range held by an unreplicated lock, the lock is dropped. The following behaviors can occur:
    • The desired ordering of concurrent accesses to one or more rows of a table expressed by your use of SELECT ... FOR UPDATE may not be preserved (that is, a transaction B against some table T that was supposed to wait behind another transaction A operating on T may not wait for transaction A).
    • The transaction that acquired the (now dropped) unreplicated lock may fail to commit, leading to and the .
    When running under SERIALIZABLE isolation, SELECT ... FOR UPDATE and SELECT ... FOR SHARE locks should be thought of as best-effort, and should not be relied upon for correctness. Note that is preserved despite this limitation. This limitation is fixed when the enable_durable_locking_for_serializable is set to true. This limitation does not apply to transactions.

SET does not ROLLBACK in a transaction

does not properly apply within a transaction. For example, in the following transaction, showing the TIME ZONE does not return 2 as expected after the rollback:

ROLLBACK TO SAVEPOINT in high-priority transactions containing DDL

1 Transactions with that contain DDL and ROLLBACK TO SAVEPOINT are not supported, as they could result in a deadlock. For example:

CANCEL JOB limitations

  • To avoid transaction states that cannot properly , the following statements cannot be cancelled with :
    • DROP statements (e.g., ).
    • ALTER ... RENAME statements (e.g., ).
    • statements.
    • statements, except for those that drop values.
  • When an Enterprise is canceled, partially restored data is properly cleaned up. This can have a minor, temporary impact on cluster performance.

SQL cursor support

CockroachDB implements SQL support with the following limitations:
  • DECLARE only supports forward cursors. Reverse cursors created with DECLARE SCROLL are not supported.
  • FETCH supports forward, relative, and absolute variants, but only for forward cursors.
  • BINARY CURSOR, which returns data in the Postgres binary format, is not supported.
  • WITH HOLD, which allows keeping a cursor open for longer than a transaction by writing its results into a buffer, is accepted as valid syntax within a single transaction but is not supported. It acts as a no-op and does not actually perform the function of WITH HOLD, which is to make the cursor live outside its parent transaction. Instead, if you are using WITH HOLD, you will be forced to close that cursor within the transaction it was created in.
    • This syntax is accepted (but does not have any effect):
    • This syntax is not accepted, and will result in an error:
  • Scrollable cursor (also known as reverse FETCH) is not supported.
  • with a cursor is not supported.
  • Respect for is not supported. Cursor definitions do not disappear properly if rolled back to a SAVEPOINT from before they were created.

Materialized views inside transactions

  • CockroachDB cannot refresh inside . Trying to refresh a materialized view inside an explicit transaction will result in an error.
    1. Start with the sample bank data set:
    2. Create the materialized view described in .
    3. Start a new multi-statement transaction with :
    4. Inside the open transaction, attempt to . This will result in an error.

Schemas and indexes

Online schema change limitations

Schema changes within transactions
Most schema changes should not be performed within an explicit transaction with multiple statements, as they do not have the same atomicity guarantees as other SQL statements. Execute schema changes either as single statements (as an implicit transaction), or in an explicit transaction consisting of the single schema change statement. There are some exceptions to this, detailed below. Schema changes keep your data consistent at all times, but they do not run inside [transactions][txns] in the general case. Making schema changes transactional would mean requiring a given schema change to propagate across all the nodes of a cluster. This would block all user-initiated transactions being run by your application, since the schema change would have to commit before any other transactions could make progress. This would prevent the cluster from servicing reads and writes during the schema change, requiring application downtime.
New in v24.1: Some tools and applications may be able to workaround CockroachDB’s lack of transactional schema changes by .
Some schema change operations can be run within explicit, multiple statement transactions. CREATE TABLE and CREATE INDEX statements can be run within the same transaction with the same atomicity guarantees as other SQL statements. There are no performance or rollback issues when using these statements within a multiple statement transaction. Within a single :
  • You can run schema changes inside the same transaction as a statement. For more information, see . However, a CREATE TABLE statement containing clauses cannot be followed by statements that reference the new table.
  • Schema change DDL statements inside a multi-statement transaction can fail while other statements succeed.
  • can result in data loss if one of the other schema changes in the transaction fails or is canceled. To work around this, move the DROP COLUMN statement to its own explicit transaction or run it in a single statement outside the existing transaction.
If a schema change within a transaction fails, manual intervention may be needed to determine which statement has failed. After determining which schema change(s) failed, you can then retry the schema change.
Schema change DDL statements inside a multi-statement transaction can fail while other statements succeed
Most schema change DDL statements that run inside a multi-statement transaction with non-DDL statements can fail at time, even if other statements in the transaction succeed. This leaves such transactions in a “partially committed, partially aborted” state that may require manual intervention to determine whether the DDL statements succeeded. Some DDL statements do not have this limitation. CREATE TABLE and CREATE INDEX statements have the same atomicity guarantees as other statements within a transaction. If such a failure occurs, CockroachDB will emit a CockroachDB-specific error code, XXA00, and the following error message:
If you must execute schema change DDL statements inside a multi-statement transaction, we strongly recommend checking for this error code and handling it appropriately every time you execute such transactions.
This error will occur in various scenarios, including but not limited to:
  • Creating a unique index fails because values aren’t unique.
  • The evaluation of a computed value fails.
  • Adding a constraint (or a column with a constraint) fails because the constraint is violated for the default/computed values in the column.
To see an example of this error, start by creating the following table.
Then, enter the following multi-statement transaction, which will trigger the error.
In this example, the statement committed, but the statement adding a failed. We can verify this by looking at the data in table t and seeing that the additional non-unique value 3 was successfully inserted.
No online schema changes if primary key change in progress
You cannot start an online schema change on a table if a is currently in progress on the same table.
No online schema changes between executions of prepared statements
When the schema of a table targeted by a prepared statement changes after the prepared statement is created, future executions of the prepared statement could result in an error. For example, adding a column to a table referenced in a prepared statement with a SELECT * clause will result in an error:
It’s therefore recommended to explicitly list result columns instead of using SELECT * in prepared statements, when possible.

Adding a column with sequence-based DEFAULT values

It is currently not possible to to a table when the column uses a as the value, for example:

Dropping a column referenced by a partial index

CockroachDB prevents a column from being dropped using if it is referenced by a partial index predicate. To drop such a column, the partial indexes need to be dropped first using .

Schema change DDL statements inside a multi-statement transaction can fail while other statements succeed

Most schema change DDL statements that run inside a multi-statement transaction with non-DDL statements can fail at time, even if other statements in the transaction succeed. This leaves such transactions in a “partially committed, partially aborted” state that may require manual intervention to determine whether the DDL statements succeeded. Some DDL statements do not have this limitation. CREATE TABLE and CREATE INDEX statements have the same atomicity guarantees as other statements within a transaction. If such a failure occurs, CockroachDB will emit a CockroachDB-specific error code, XXA00, and the following error message:
If you must execute schema change DDL statements inside a multi-statement transaction, we strongly recommend checking for this error code and handling it appropriately every time you execute such transactions.
This error will occur in various scenarios, including but not limited to:
  • Creating a unique index fails because values aren’t unique.
  • The evaluation of a computed value fails.
  • Adding a constraint (or a column with a constraint) fails because the constraint is violated for the default/computed values in the column.
To see an example of this error, start by creating the following table.
Then, enter the following multi-statement transaction, which will trigger the error.
In this example, the statement committed, but the statement adding a failed. We can verify this by looking at the data in table t and seeing that the additional non-unique value 3 was successfully inserted.

Schema changes between executions of prepared statements

When the schema of a table targeted by a prepared statement changes after the prepared statement is created, future executions of the prepared statement could result in an error. For example, adding a column to a table referenced in a prepared statement with a SELECT * clause will result in an error:
It’s therefore recommended to explicitly list result columns instead of using SELECT * in prepared statements, when possible.

New values generated by DEFAULT expressions during ALTER TABLE ADD COLUMN

When executing an statement with a expression, new values generated:
  • use the default regardless of the search path configured in the current session via SET SEARCH_PATH.
  • use the UTC time zone regardless of the time zone configured in the current session via .
  • have no default database regardless of the default database configured in the current session via , so you must specify the database of any tables they reference.
  • use the transaction timestamp for the statement_timestamp() function regardless of the time at which the ALTER statement was issued.

Some column-dropping schema changes do not roll back properly

Some that cannot be properly. In some cases, the rollback will succeed, but the column data might be partially or totally missing, or stale due to the asynchronous nature of the schema change. In other cases, the rollback will fail in such a way that will never be cleaned up properly, leaving the table descriptor in a state where no other schema changes can be run successfully. To reduce the chance that a column drop will roll back incorrectly:
  • Perform column drops in transactions separate from other schema changes. This ensures that other schema change failures will not cause the column drop to be rolled back.
  • Drop all (including ) on the column in a separate transaction, before dropping the column.
  • Drop any or on a column before attempting to drop the column. This prevents conflicts between constraints and default/computed values during a column drop rollback.
If you think a rollback of a column-dropping schema change has occurred, check the . Schema changes with an error prefaced by cannot be reverted, manual cleanup may be required might require manual intervention.

ALTER COLUMN limitations

You cannot alter the data type of a column if:
  • The column is part of an .
  • The column has .
  • The column owns a .
  • The column has a . This will result in an ERROR: ... column ... cannot also have a DEFAULT expression with SQLSTATE: 42P16.
  • The ALTER COLUMN TYPE statement is part of a combined ALTER TABLE statement.
  • The ALTER COLUMN TYPE statement is inside an .
Most ALTER COLUMN TYPE changes are finalized asynchronously. Schema changes on the table with the altered column may be restricted, and writes to the altered column may be rejected until the schema change is finalized.

CREATE TABLE AS limitations

The of tables created with CREATE TABLE ... AS is not automatically derived from the query results. You must specify new primary keys at table creation. For examples, see .

Remove a UNIQUE index created as part of CREATE TABLE

created as part of a statement cannot be removed without using . Unique indexes created with do not have this limitation.

Max size of a single column family

When creating or updating a row, if the combined size of all values in a single exceeds the for the table, the operation may fail, or cluster performance may suffer. As a workaround, you can either , or you can with an increased max range size.

Dropping a single partition

CockroachDB does not currently support dropping a single partition from a table. In order to remove partitions, you can the table.

Placeholders in PARTITION BY

When defining a , either during table creation or table alteration, it is not possible to use placeholders in the PARTITION BY clause.

Unsupported trigram syntax

The following PostgreSQL syntax and features are currently unsupported for :
  • word_similarity() built-in function.
  • strict_word_similarity() built-in function.
  • %> and <% comparisons and acceleration.
  • <<% and %>> comparisons and acceleration.
  • <->, <<->, <->>, <<<->, and <->>> comparisons.
  • Acceleration on .
  • % comparisons, show_trgm, and trigram index creation on .

Unsupported full-text search features

The following PostgreSQL syntax and features are currently unsupported for :
  • Aspects of other than the specified dictionary.
  • websearch_to_tsquery() built-in function.
  • tsquery_phrase() built-in function.
  • ts_rank_cd() built-in function.
  • setweight() built-in function.
  • Inverted joins on TSVECTOR values.
  • tsvector || tsvector comparisons.
  • tsquery || tsquery comparisons.
  • tsquery && tsquery comparisons.
  • tsquery <-> tsquery comparisons.
  • !! tsquery comparisons.
  • tsquery @> tsquery and tsquery <@ tsquery comparisons.

CockroachDB does not allow inverted indexes with STORING

CockroachDB does not allow inverted indexes with a .

Multiple arbiter indexes for INSERT ON CONFLICT DO UPDATE

CockroachDB does not currently support multiple arbiter indexes for , and will return an error if there are multiple unique or exclusion constraints matching the ON CONFLICT DO UPDATE specification.

Expression index limitations

  • The expression cannot reference columns outside the index’s table.
  • Functional expression output must be determined by the input arguments. For example, you can’t use the now() to create an index because its output depends on more than just the function arguments.
  • CockroachDB does not allow to reference .
  • CockroachDB does not support expressions as ON CONFLICT targets. This means that unique cannot be selected as arbiters for statements. For example:

Secondary regions and regional by row tables

are not compatible with databases containing tables. CockroachDB does not prevent you from defining secondary regions on databases with regional by row tables, but the interaction of these features is not supported. Therefore, Cockroach Labs recommends that you avoid defining secondary regions on databases that use regional by row table configurations.

Data types

Spatial support limitations

CockroachDB supports efficiently storing and querying , with the following limitations:
  • Not all PostGIS spatial functions are supported.
  • The AddGeometryColumn only allows constant arguments.
  • The AddGeometryColumn spatial function only allows the true value for its use_typmod parameter.
  • CockroachDB does not support the @ operator. Instead of using @ in spatial expressions, we recommend using the inverse, with ~. For example, instead of a @ b, use b ~ a.
  • CockroachDB does not yet support s into the . This limitation also blocks the ogr2ogr -f PostgreSQL file conversion command.
  • CockroachDB does not yet support k-nearest neighbors.
  • CockroachDB does not support using to refer to with type modifiers (e.g., public.geometry(linestring, 4326)). Instead, use fully-unqualified names to refer to data types with type modifiers (e.g., geometry(linestring,4326)).
  • Defining a custom SRID by inserting rows into is not currently supported.

OID limitations

Refer to .

Limitations for composite types

  • Changefeed types are not fully integrated with . Running changefeeds with user-defined composite types is in . Certain changefeed types do not support user-defined composite types. Refer to the change data capture for more detail. The following limitations apply:
    • A changefeed in will not be able to serialize .
    • A changefeed emitting will include AS labels in the message format when the changefeed serializes a .
  • Updating subfields of composite types using dot syntax results in a syntax error.
  • Tuple elements cannot be accessed without enclosing the name in parentheses. For example, (v).x.

ALTER TYPE limitations

  • When running the statement, you can only reference a user-defined type from the database that contains the type.
  • You can only ALTER TYPE that drop values. This is because when you drop a value, CockroachDB searches through every row that could contain the type’s value, which could take a long time. All other ALTER TYPE schema change jobs are .

JSONB limitations

  • You cannot use , , and on JSONB values.

Security and privileges

GRANT/REVOKE limitations

User/role management operations (such as and ) are . As such, they inherit the . For example, schema changes wait for concurrent using the same resources as the schema changes to complete. In the case of being modified inside a transaction, most transactions need access to the set of role memberships. Using the default settings, role modifications require schema leases to expire, which can take up to 5 minutes. This means that elsewhere in the system can cause user/role management operations inside transactions to take several minutes to complete. This can have a cascading effect. When a user/role management operation inside a transaction takes a long time to complete, it can in turn block all user-initiated transactions being run by your application, since the user/role management operation in the transaction has to commit before any other transactions that access role memberships (i.e., most transactions) can make progress. If you want user/role management operations to finish more quickly, and do not care whether concurrent transactions will immediately see the side effects of those operations, set the allow_role_memberships_to_change_during_transaction to true. When this session variable is enabled, any user/role management operations issued in the current session will only need to wait for the completion of statements in other sessions where allow_role_memberships_to_change_during_transaction is not enabled. To accelerate user/role management operations across your entire application, you have the following options:
  1. Set the session variable in all sessions by .
  2. Apply the allow_role_memberships_to_change_during_transaction setting globally to an entire cluster using the statement:

DROP OWNED BY limitations

  • types are not dropped.
  • drops all owned objects as well as any on objects not owned by the .
  • If the for which you are trying to DROP OWNED BY was granted a (i.e., using the statement), the following error will be signalled:
    The phrase “synthetic privileges” in the error message refers to . The workaround is to use and then use for each privilege in the result.

Privileges for DELETE and UPDATE

Every or statement constructs a SELECT statement, even when no WHERE clause is involved. As a result, the user executing DELETE or UPDATE requires both the DELETE and SELECT or UPDATE and SELECT on the table.

Deployment and operations

Admission control

Admission control works on the level of each node, not at the cluster level. The admission control system queues requests until the operations are processed or the request exceeds the timeout value (for example by using ). If you specify aggressive timeout values, the system may operate correctly but have low throughput as the operations exceed the timeout value while only completing part of the work. There is no mechanism for preemptively rejecting requests when the work queues are long. Organizing operations by priority can mean that higher priority operations consume all the available resources while lower priority operations remain in the queue until the operation times out.

Data domiciling

  • When columns are , a subset of data from the indexed columns may appear in or other system tables. CockroachDB synchronizes these system ranges and system tables across nodes. This synchronization does not respect any multi-region settings applied via either the , or the low-level mechanism.
  • can be used for data placement but these features were historically built for performance, not for domiciling. The replication system’s top priority is to prevent the loss of data and it may override the zone configurations if necessary to ensure data durability. For more information, see .
  • If your are kept in the region where they were generated, there is some cross-region leakage (like the system tables described previously), but the majority of user data that makes it into the logs is going to be homed in that region. If that’s not strong enough, you can use the to strip all raw data from the logs. You can also limit your log retention entirely.
  • If you start a node with a flag that says the node is in region A, but the node is actually running in some region B, data domiciling based on the inferred node placement will not work. A CockroachDB node only knows its locality based on the text supplied to the --locality flag; it can not ensure that it is actually running in that physical location.

CockroachDB does not test for all connection failure scenarios

CockroachDB servers rely on the network to report when a TCP connection fails. In most scenarios when a connection fails, the network immediately reports a connection failure, resulting in a Connection refused error. However, if there is no host at the target IP address, or if a firewall rule blocks traffic to the target address and port, a TCP handshake can linger while the client network stack waits for a TCP packet in response to network requests. To work around this kind of scenario, we recommend the following:
  • When migrating a node to a new machine, keep the server listening at the previous IP address until the cluster has completed the migration.
  • Configure any active network firewalls to allow node-to-node traffic.
  • Verify that orchestration tools (e.g., Kubernetes) are configured to use the correct network connection information.

No guaranteed state switch from DECOMMISSIONING to DECOMMISSIONED if node decommission is interrupted

There is no guaranteed state switch from DECOMMISSIONING to DECOMMISSIONED if is interrupted in one of the following ways:
  • The cockroach node decommission --wait-all command was run and then interrupted
  • The cockroach node decommission --wait=none command was run
This is because the state flip is effected by the CLI program at the end. Only the CLI (or its underlying API call) is able to finalize the “decommissioned” state. If the command is interrupted, or --wait=none is used, the state will only flip to “decommissioned” when the CLI program is run again after decommissioning has done all its work.

Simultaneous client connections and running queries on a single node

When a node has both a high number of client connections and running queries, the node may crash due to memory exhaustion. This is due to CockroachDB not accurately limiting the number of clients and queries based on the amount of available RAM on the node. To prevent memory exhaustion, monitor each node’s memory usage and ensure there is some margin between maximum CockroachDB memory usage and available system RAM. For more details about memory usage in CockroachDB, see this blog post. To control the maximum number of non-superuser ( user or other ) connections a can have open at one time, use the server.max_connections_per_gateway . If a new non-superuser connection would exceed this limit, the error message "sorry, too many clients already" is returned, along with error code 53300. This may be useful in addition to your memory monitoring.

Load-based lease rebalancing in uneven latency deployments

When nodes are started with the flag, CockroachDB attempts to place the replica lease holder (the replica that client requests are forwarded to) on the node closest to the source of the request. This means as client requests move geographically, so too does the replica lease holder. However, you might see increased latency caused by a consistently high rate of lease transfers between datacenters in the following case:
  • Your cluster runs in datacenters which are very different distances away from each other.
  • Each node was started with a single tier of --locality, e.g., --locality=datacenter=a.
  • Most client requests get sent to a single datacenter because that’s where all your application traffic is.
To detect if this is happening, open the , select the Queues dashboard, hover over the Replication Queue graph, and check the Leases Transferred / second data point. If the value is consistently larger than 0, you should consider stopping and restarting each node with additional tiers of locality to improve request latency. For example, let’s say that latency is 10ms from nodes in datacenter A to nodes in datacenter B but is 100ms from nodes in datacenter A to nodes in datacenter C. To ensure A’s and B’s relative proximity is factored into lease holder rebalancing, you could restart the nodes in datacenter A and B with a common region, --locality=region=foo,datacenter=a and --locality=region=foo,datacenter=b, while restarting nodes in datacenter C with a different region, --locality=region=bar,datacenter=c.

Size limits on statement input from SQL clients

CockroachDB imposes a hard limit of 16MiB on the data input for a single statement passed to CockroachDB from a client (including the SQL shell). We do not recommend attempting to execute statements from clients with large input.

Using \| to perform a large input in the SQL shell

In the , using the operator to perform a large number of inputs from a file can cause the server to close the connection. This is because \| sends the entire file as a single query to the server, which can exceed the upper bound on the size of a packet the server can accept from any client (16MB). As a workaround, with cat data.sql | cockroach sql instead of from within the interactive shell.

Spatial features disabled for ARM Macs

are disabled due to an issue with macOS code signing for the GEOS libraries. Users needing spatial features on an ARM Mac may instead use Rosetta to or use the distribution.

Logging system limitations

Log files can only be accessed in the DB Console if they are stored in the same directory as the file sink for the DEV channel.

Per-replica circuit breaker limitations

have the following limitations:
  • They cannot prevent requests from hanging when the node’s is unavailable. For more information about troubleshooting a cluster that’s having node liveness issues, see .
  • They are not tripped if all replicas of a range , because the circuit breaker mechanism operates per-replica. This means at least one replica needs to be available to receive the request in order for the breaker to trip.

Kubernetes limitations

Refer to .

Observability

Datadog

The integration of your CockroachDB self-hosted cluster with Datadog only supports displaying cluster-wide averages of reported metrics. Filtering by a specific node is unsupported.

DB Console may become inaccessible for secure clusters

Accessing the DB Console for a secure cluster now requires login information (i.e., username and password). This login information is stored in a system table that is replicated like other data in the cluster. If a majority of the nodes with the replicas of the system table data go down, users will be locked out of the DB Console.

Available capacity metric in the DB Console

If you are testing your deployment locally with multiple CockroachDB nodes running on a single machine (this is ), you must explicitly per node in order to display the correct capacity. Otherwise, the machine’s actual disk capacity will be counted as a separate store for each node, thus inflating the computed capacity.

Disaster recovery

Physical cluster replication

  • Physical cluster replication is supported in CockroachDB self-hosted clusters on v23.2 or later. The primary cluster can be a or cluster. The standby cluster must be a .
  • Read queries are not supported on the standby cluster before .
  • In CockroachDB self-hosted, the primary and standby clusters must have the same in order to respect data placement configurations.
  • After the for , will continue on the promoted cluster. You will need to manage or the schedule on the promoted standby cluster to avoid two clusters running the same changefeed to one sink.
  • After a cutover, there is no mechanism to stop applications from connecting to the original primary cluster. It is necessary to redirect application traffic manually, such as by using a network load balancer or adjusting DNS records.

RESTORE limitations

  • RESTORE will not restore a table that references a , unless you skip restoring the function with the option. Alternatively, take a to include everything needed to restore the table.
  • Restoring and tables into a non-multi-region database is not supported.
  • and tables can be restored only if the regions of the backed-up table match those of the destination database. All of the following must be true for RESTORE to be successful:
    • The of the source database and the regions of the destination database have the same set of regions.
    • The regions were added to each of the databases in the same order.
    • The databases have the same .
    The following example would be considered as having mismatched regions because the database regions were not added in the same order and the primary regions do not match. Running on the source database:
    Running on the destination database:
    In addition, the following scenario has mismatched regions between the databases since the regions were not added to the database in the same order. Running on the source database:
    Running on the destination database:

Enterprise BACKUP does not capture database/table/column comments

The statement associates comments to databases, tables, or columns. However, the internal table (system.comments) in which these comments are stored is not captured by a of a table or database. As a workaround, take a cluster backup instead, as the system.comments table is included in cluster backups. does not support listing backups if the storage location is a symlink.

Change data capture

Change data capture (CDC) provides efficient, distributed, row-level changefeeds into Apache Kafka for downstream processing such as reporting, caching, or full-text indexing. It has the following known limitations:
  • Changefeed target options are limited to tables and .
  • and in CockroachDB Advanced clusters do not support connecting to a sink’s internal IP addresses for . To connect to a Kafka sink from CockroachDB Advanced, it is necessary to expose the Kafka cluster’s external IP address and open ports with firewall rules to allow access from a CockroachDB Advanced cluster.
  • Webhook sinks only support HTTPS. Use the parameter when testing to disable certificate verification; however, this still requires HTTPS and certificates.
  • Formats for changefeed messages are not supported by all changefeed sinks. Refer to the page for details on compatible formats with each sink and the option to specify a changefeed message format.
  • Using the and options on the same changefeed will cause an error when using the following : Kafka and Google Cloud Pub/Sub. Instead, use the individual FAMILY keyword to specify column families when creating a changefeed.
  • Changefeed types are not fully integrated with . Running changefeeds with user-defined composite types is in . Certain changefeed types do not support user-defined composite types. Refer to the change data capture for more detail. The following limitations apply:
    • A changefeed in will not be able to serialize .
    • A changefeed emitting will include AS labels in the message format when the changefeed serializes a .
  • After the for , will continue on the promoted cluster. You will need to manage or the schedule on the promoted standby cluster to avoid two clusters running the same changefeed to one sink.
  • You can only apply CDC queries on a single table in each statement.
  • Some , notably functions that return MVCC timestamps, are overridden to return the MVCC timestamp of the event, e.g., transaction_timestamp or statement_timestamp. Additionally, some , such as now() are not supported. We recommend using the transaction_timestamp() function or the column instead.
  • The following are not permitted in CDC queries:
    • .
    • Sub-select queries.
    • and (i.e., functions operating over many rows).
  • delete changefeed events will only contain the . All other columns will emit as NULL. See for detail on running a CDC query that emits the deleted values.
  • Creating a changefeed with on tables with more than one is not supported.
  • When you create a changefeed on a table with more than one , the changefeed will emit messages per column family in separate streams. As a result, for different column families will arrive at the under separate topics.

ALTER CHANGEFEED limitations

  • It is necessary to the changefeed before performing any statement.
  • CockroachDB does not keep track of the option applied to tables when it is set to yes or only. For example:
    This will trigger an initial scan of the table and the changefeed will track table. The changefeed will not track initial_scan specified as an option, so it will not display in the output or after a SHOW CHANGEFEED JOB statement.
  • is not fully supported with changefeeds that use . You can alter the options that a changefeed uses, but you cannot alter the changefeed target tables.

Performance optimization

Optimizer and locking behavior

The SQL optimizer has limitations under certain isolation levels:
  • The new implementation of SELECT FOR UPDATE is not yet the default setting under SERIALIZABLE isolation. It can be used under SERIALIZABLE isolation by setting the optimizer_use_lock_op_for_serializable to true.
  • SELECT FOR UPDATE does not lock completely-NULL column families in multi-column-family tables.

Statistics limitations

  • The automatically checks whether it needs to refresh statistics for every table in the database upon startup of each node in the cluster. If statistics for a table have not been refreshed in a while, this will trigger collection of statistics for that table. If statistics have been refreshed recently, it will not force a refresh. As a result, the automatic statistics refresher does not necessarily perform a refresh of statistics after an . This could cause a problem, for example, if the upgrade moves from a version without to a version with histograms. To refresh statistics manually, use .
  • The following do not immediately take effect, and instead only take effect when new statistics are collected for a table.
    Although shows the settings taking effect immediately, they do not actually take effect until new statistics are collected (as can be verified with ). As a workaround, disable and enable forecasting at the or level. This will invalidate the statistics cache and cause these settings to take effect immediately.

Incorrect query plans for partitions with NULL values

In cases where the partition definition includes a comparison with NULL and a query constraint, incorrect query plans are returned. However, this case uses non-standard partitioning which defines partitions which could never hold values, so it is not likely to occur in production environments.

Vectorized engine limitations

  • The vectorized engine does not support queries containing a join filtered with an .
  • The vectorized engine does not support . Queries with or will revert to the row-oriented execution engine.

transaction_rows_read_err and transaction_rows_written_err do not halt query execution

The transaction_rows_read_err and transaction_rows_written_err limit the number of rows read or written by a single . These session settings will fail the transaction with an error, but not until the current query finishes executing and the results have been returned to the client.

sql.guardrails.max_row_size_err misses indexed virtual computed columns

The sql.guardrails.max_row_size_err misses large rows caused by indexed virtual computed columns. This is because the guardrail only checks the size of primary key rows, not secondary index rows.

Using LIKE...ESCAPE in WHERE and HAVING constraints

CockroachDB tries to optimize most comparisons operators in WHERE and HAVING clauses into constraints on SQL indexes by only accessing selected rows. This is done for LIKE clauses when a common prefix for all selected rows can be determined in the search pattern (e.g., ... LIKE 'Joe%'). However, this optimization is not yet available if the ESCAPE keyword is also used.

Import with a high amount of disk contention

can sometimes fail with a “context canceled” error, or can restart itself many times without ever finishing. If this is happening, it is likely due to a high amount of disk contention. This can be mitigated by setting the kv.bulk_io_write.max_rate to a value below your max disk write speed. For example, to set it to 10MB/s, execute:

CockroachDB does not properly optimize some left and anti joins with GIN indexes

and anti joins involving , , or columns with a multi-column or will not take advantage of the index if the prefix columns of the index are unconstrained, or if they are constrained to multiple, constant values. To work around this limitation, make sure that the prefix columns of the index are either constrained to single constant values, or are part of an equality condition with an input column (e.g., col1 = col2, where col1 is a prefix column and col2 is an input column). For example, suppose you have the following and tables:
And you some data into the tables:
If you attempt a left join between t1 and t2 on only the geometry columns, CockroachDB will not be able to plan an :
However, if you constrain the crdb_region column to a single value, CockroachDB can plan an inverted join:
If you do not know which region to use, you can combine queries with :

Locality optimized search limitations

  • does not work for queries that use on . A workaround for computed columns is to make the virtual computed column a . Locality optimized search does not work for queries that use partitioned unique .
  • works only for queries selecting a limited number of records (up to 100,000 unique keys).

Query plans for materialized views

  • The optimizer may not select the most optimal query plan when querying materialized views because CockroachDB does not on materialized views.

Inverted join for tsvector and tsquery types is not supported

CockroachDB cannot index-accelerate queries with @@ predicates when both sides of the operator are variables.