Neki is currently in Platform Preview. Platform Preview features are “Beta Features” under the PlanetScale Terms of Service or your applicable agreement with PlanetScale. Accordingly, Neki is subject to the limitations and disclaimers applicable to Beta Features and is not covered by any service level agreement.
NK013 and a message in
this form:
NK013 means the gap is one Neki intends to close. A limit that is a design
decision rather than a missing implementation is reported as not supported
instead, and the broad ones are listed in Platform preview
limitations. For how other Neki errors use
the DETAIL and HINT fields, see Error hints and
details.
How to read an entry
Each entry gives the code, the message sentence the router renders after the bracketed code, an example that triggers it, and a note on which part of the query causes the rejection. The examples use the example schema. A few entries name another small table whose own name describes the property that matters to that code. Where a rejection depends on how the data topology places the tables rather than on the SQL text, the note says so — the same statement can plan successfully with a different topology. Codes that only an internal invariant can raise are not listed, because no query produces them. A code absent from this page is one an application is unlikely to meet. A single statement can be rejected by more than one of these codes. Which one you see is the first gap the planner reaches, so fixing one can reveal another.Statements, transactions, and sessions
Codes raised while the router decides what a statement is and how the session should behave.1 — SELECT INTO statements are unavailable.
INTO clause is what Neki rejects. At the top level Postgres treats SELECT ... INTO as CREATE TABLE AS; write that form instead.
20 — This statement type is unavailable.
21 — Reading an inheritance parent without ONLY is unavailable.
inheritance_parent has child tables. Reading the parent without ONLY would have to expand to the children, so add ONLY to read just the parent.
23 — This transaction command is unavailable.
BEGIN, COMMIT, ROLLBACK, SAVEPOINT, RELEASE SAVEPOINT, and ROLLBACK TO SAVEPOINT succeed. PREPARE TRANSACTION, COMMIT PREPARED, and ROLLBACK PREPARED return this code.
26 — Atomic transaction mode is unavailable.
__neki.tx_mode accepts multi (the default) and single.
28 — SET FROM CURRENT is unavailable.
FROM CURRENT form is rejected; give the parameter an explicit value.
29 — SET TRANSACTION SNAPSHOT is unavailable.
30 — This statement cannot be analyzed with EXPLAIN neki_plan.
EXPLAIN (NEKI_PLAN, ANALYZE) runs the statement to show its router plan, and some statement types, such as CREATE TABLE AS, have none to show. The statement itself may still be supported when run directly.
31 — Data-modifying CTEs cannot be analyzed with EXPLAIN neki_plan.
EXPLAIN (NEKI_PLAN, ANALYZE) does not run a statement whose CTE writes.
32 — This EXPLAIN option is unavailable.
GENERIC_PLAN is one of the PostgreSQL EXPLAIN options the NEKI_PLAN renderer does not honor; the message names the option. Drop it, or run the EXPLAIN without NEKI_PLAN.
518 — This configuration parameter value is unavailable.
537 — A WITH HOLD cursor whose router-computed part evaluates a function, operator or cast that is not IMMUTABLE is unavailable.
COMMIT. The router computes part of this query itself, here the aggregate across shards and now(), at each later FETCH, so a function that is not IMMUTABLE would answer for the FETCH and not for the commit. Declare the cursor without WITH HOLD, or compute the value in the client.
563 — Changing DateStyle, TimeZone, IntervalStyle, extra_float_digits, or client_encoding while a cursor over a router-computed query holds rows is unavailable.
FETCH after one of them changes. Change the setting before the first FETCH.
660 — A cursor over this query shape is unavailable.
name crosses shards, so the router computes it, and a row-locking cursor over a query the router computes is refused: a row mark would lock every row a shard returns, not only the rows you fetch. The message names the shape. Join on the shard key so the cursor runs on one shard, or lock the rows with a separate SELECT ... FOR UPDATE.
817 — Planning against … is unavailable: earlier DDL in this transaction is not reflected in the router’s schema view.
Topology and evaluation context
Codes that depend on how the data topology is configured, or on the context the router is evaluating in.10 — Multi-column partitioning indexes are unavailable.
devices is sharded on owner_id + device_no, an expression over two columns, and INSERT ... VALUES cannot route on it. A columns list with more than one entry is rejected when the topology is applied; an expression over one column, such as lower(email), routes.
11 — This shard index cannot route advisory locks.
user_data as the advisory-lock shard group, and user_data routes on a modulo shard index. Neki hashes advisory-lock keys with xxhash, so a multi-shard advisory-lock group must use an xxhash default shard index, as the Example schema’s does.
41 — File COPY is unavailable for sharded tables.
ClientCOPY TO STDOUT works for sharded tables, reference tables, and materialized views, including text, CSV, and binary. The codes in this section are for file COPY.
COPY against a sharded table. Use COPY ... FROM STDIN for a sharded table, or an unsharded table for file COPY.
43 — File COPY is unavailable for reference tables.
COPY into a reference table. Use COPY ... FROM STDIN instead.
44 — COPY FROM cannot maintain this table’s secondary index.
COPY FROM cannot maintain. Load the table with INSERT instead.
45 — COPY does not support a multi-column primary shard index.
COPY FROM routes each row on a single-column shard key, and devices is sharded on an expression over two columns.
46 — COPY FROM cannot evaluate this reference-table default consistently.
47 — COPY TO PROGRAM is unavailable.
TO PROGRAM destination is rejected; it would run a command on a Postgres host.
49 — COPY of a SELECT result is unavailable.
SELECT source is what is rejected. Copy a table, or run the query and handle the rows in your application.
50 — COPY FROM with a WHERE clause is unavailable.
WHERE clause on COPY FROM is rejected. Filter the data before feeding it to COPY.
51 — COPY FROM cannot evaluate this column default on the router.
created_at, whose default now() the router would have to evaluate per row. Include the column and supply its values.
52 — This COPY option is unavailable for sharded COPY.
COPY options such as ON_ERROR, REJECT_LIMIT, and LOG_VERBOSITY are rejected. The same option is accepted for an unsharded table.
53 — This input encoding is unavailable for sharded COPY.
COPY stream byte by byte, which is unsafe for an encoding such as SJIS, whose multi-byte characters can contain a backslash byte. Send UTF-8.
54 — COPY is unavailable in a multi-statement query.
COPY must be the only statement in the query. The second statement in the batch is what makes this fail.
55 — COPY is unavailable in the extended query protocol.
COPY works over the simple query protocol.
350 — Notify channel routing requires an xxhash shard index.
notify_shard_group: Neki hashes notification channel names with xxhash, so a multi-shard notify group must use an xxhash default shard index.
531 — COPY FROM PROGRAM is unavailable.
COPY ... FROM STDIN and send the data from the client.
Advisory locks
Codes raised when the router cannot place an advisory lock on a single shard, or take it at all.60 — A session cannot hold session advisory locks on multiple shards.
user_data is the advisory-lock shard group, and keys 1 and 2 hash to different shards. A session holds its session-level locks on one shard, so release the first lock before taking one that lands elsewhere.
61 — Mixing session and transaction advisory locks is unavailable.
139 — Advisory locks are unavailable in this plan position.
CREATE TABLE AS — where the router cannot take the lock. Take the lock in a separate statement.
Temporary tables
Codes raised for temporary tables. A session can create a temporary table withCREATE TEMP TABLE and a column list, insert into it with an INSERT that has a VALUES list, and read it with a SELECT, all outside a transaction block. The data topology places a temporary table through the pg_temp schema, as the example schema does. Anything else that creates, names, or reads a temporary table is rejected before a shard sees it.
338 — Creating this temporary relation is unavailable.
CREATE TEMP TABLE with a column list creates a temporary relation. CREATE TEMP TABLE ... AS, CREATE TEMP SEQUENCE, and a temporary materialized view report the same code, and so does a CREATE TEMP TABLE that the data topology places on a reference table or on a sharded shard group without a sharding key. Create a regular relation and drop it when done.
619 — Creating a function or procedure is unavailable while the session has temporary tables.
DISCARD TEMP first.
627 — Using a temporary table inside a transaction is unavailable, including a statement the router runs as a transaction, such as an INSERT whose VALUES contain a subquery or a SELECT with FOR UPDATE.
SELECT runs inside a transaction block. Use the temporary table outside a transaction block.
690 — A relation or type name passed as a string or a bound parameter is unavailable when it can name a temporary table.
'session_cart' is a name in a string, which a shard resolves, and the router cannot route a shard to the session’s temporary tables. Name the table in the statement itself.
717 — DDL that names an object spelled like a temporary table is unavailable while the session has temporary tables.
session_cart. The router cannot tell whether the statement names the temporary table or a new object, so it rejects the statement. Choose a name that no temporary table has, or run DISCARD TEMP first.
749 — … on a temporary table is unavailable.
CREATE TEMP TABLE with a column list, INSERT with a VALUES list, and SELECT. The message names the rejected statement, here UPDATE.
917 — The storage size of a temporary relation is unavailable.
pg_relation_size, pg_table_size, pg_indexes_size and pg_total_relation_size work for tables that are not temporary.
PL/pgSQL
Codes raised while translating or executing a PL/pgSQL routine on the router.86 — Record fields with varying runtime types are unavailable in PL/pgSQL.
r.a is an integer after one branch and text after the other, so the read has no single type. Give the field the same type on every path, for example with a cast.
87 — Function configuration parameters are unavailable for router-side execution.
SET clause names a parameter the router cannot scope around a call, here the Neki session option __neki.target. Set it on the session instead.
90 — This PL/pgSQL statement is unavailable.
GET DIAGNOSTICS ... PG_CONTEXT.
575 — RETURN QUERY EXECUTE of a FETCH or SQL EXECUTE into a function result with a domain column is unavailable unless the cursor was opened by a PL/pgSQL OPEN over a static query.
DECLARE, so the router cannot check its rows against the function’s domain result. Open the cursor with a PL/pgSQL OPEN over a static query.
757 — DISTINCT, and an ORDER BY, GROUP BY or DISTINCT ON key naming the result column, are unavailable in a PL/pgSQL assignment that coerces its value or assigns an array element.
numeric to int) and its query uses DISTINCT. Cast the value in the query, or assign without DISTINCT.
807 — RETURN QUERY in a PL/pgSQL function that returns SETOF record without OUT parameters is unavailable.
OUT parameters, or return a named composite type.
837 — A record or composite INTO target of a non-constant EXECUTE command string is unavailable in PL/pgSQL.
EXECUTE command string is built at run time, so the router cannot know the shape of the record it fills. Select into scalar variables, or use a constant command string.
842 — A record loop target of a non-constant FOR IN EXECUTE command string is unavailable in PL/pgSQL.
FOR ... IN EXECUTE.
844 — Reading a PL/pgSQL cursor over a data-modifying query or an EXPLAIN after search_path, the role, or the shard or sidecar target changed since its OPEN is unavailable.
EXPLAIN, and search_path changed between its OPEN and the FETCH. Read the cursor before changing the setting.
857 — FETCH into a record variable requires the cursor to be opened by a PL/pgSQL OPEN or cursor FOR loop over a static query whose row shape, collations and nested record shapes included, equals a query the fetching function opens a cursor over; a cursor opened with a dynamic command string, by SQL DECLARE, or over a query of another shape has none.
935 — A record value of function … is unavailable in PL/pgSQL: the function names no shape for it.
json_to_record returns a bare record, and its columns come from the column definition list of the FROM clause, so the function itself names no shape for the record that PL/pgSQL receives. Run the query as a plain SQL statement outside the routine.
Query shapes and routing
The largest group: codes raised while the planner turns a query into routed work. Many depend on the data topology, and the same statement can plan against an unsharded table and be rejected against a sharded one.69 — Subqueries in VALUES are unavailable.
VALUES cell. Compute the value in a SELECT and feed it in, or use INSERT ... SELECT.
100 — Subqueries are unavailable in this DML clause.
SET value of an UPDATE ... FROM. Move the subquery into the FROM source and reference its column.
101 — Per-row router-only expression evaluation is unavailable here.
name is a column), so the router cannot compute it before the statement is routed.
102 — This RETURNING expression extraction is unavailable.
RETURNING mixes an OLD/NEW row alias with an expression the router must evaluate itself. Return the columns and compute the router-side expression in your application.
103 — This DML source join cannot be planned safely.
USING/FROM source is not something the planner can evaluate on one side, so the join cannot be executed safely.
104 — This mutation cannot maintain active secondary indexes.
accounts owns global secondary indexes, and this mutation shape cannot keep their lookup rows in step with the rows they point at.
106 — This window-function shape is unavailable across shards.
107 — Expression extraction is unavailable in this clause.
now()) appears in a clause the planner cannot lift it out of — here GROUP BY. Compute the value in your application and pass it as a parameter.
108 — This subquery shape is unavailable in ORDER BY.
ALL subquery is the ORDER BY sort key, and the router cannot evaluate that shape. Sort by a column, or sort in your application.
109 — This quantified-subquery shape is unavailable.
HAVING clause tests an aggregate against an IN subquery that reads the outer query (orders.user_id), so the router cannot pull the subquery up. Remove the reference to the outer query from the subquery.
110 — This correlated-subquery shape cannot be decorrelated safely.
GROUP BY, so decorrelating it would change which groups exist. Rewrite it as an explicit join.
111 — This lateral or range-function shape is unavailable.
FROM is the rejected part. Compute the argument in a CTE or a join instead.
112 — This full outer join has no usable equality key.
> does not give one. Join on an equality.
116 — This INSERT subquery shape is unavailable.
VALUES cell rather than being the whole cell. Use INSERT ... SELECT.
117 — This INSERT SELECT shape is unavailable.
INSERT ... SELECT source with ON CONFLICT DO UPDATE against a sharded target returns this code, except when the source is one table in the same shard group and the inserted shard key is copied from that table’s shard key. Insert explicit VALUES, or drop the DO UPDATE. INSERT ... SELECT into a table sharded by an expression over more than one column also returns this code. INSERT ... VALUES into that table returns code 10.
118 — These INSERT values cannot be materialized on the router.
VALUES cell means the router cannot know the rows — and therefore the routing values — before executing. Expand the rows in your application, or use INSERT ... SELECT.
119 — This ON CONFLICT shape is unavailable.
ON CONFLICT arbiter index (order_id) does not include the table’s shard key (user_id), so the router cannot match the rows that RETURNING produces to the rows you inserted. Arbitrate on an index that includes the shard key, insert one row per statement, or drop the WHERE or the RETURNING.
120 — This column default cannot be materialized on the router.
121 — This MERGE shape is unavailable.
MERGE source is a derived table, so the planner cannot route the merge to the target’s shards. Use a MERGE whose source is a table in the same shard group.
122 — This view cannot be routed safely.
123 — This materialized view cannot be routed safely.
124 — This set-operation shape is unavailable.
INTERSECT and EXCEPT are not planned for application tables; only UNION is. This limit does not apply to statements Neki forwards unchanged.
125 — This grouping-set form is unavailable.
GROUPING SETS (or ROLLUP/CUBE) form passes validation but has no cross-shard execution: each grouping set would need its own aggregation pass. Issue the grouping levels as separate queries.
126 — TABLESAMPLE is unavailable.
TABLESAMPLE clause is rejected; a per-shard sample would not be a sample of the whole table. Sample with a WHERE predicate such as random() < 0.5.
128 — WHERE CURRENT OF is unavailable for DML.
WHERE CURRENT OF identifies a row by cursor position, which the router does not track across shards. Delete or update by key.
129 — DISTINCT cannot safely evaluate this volatile record expression.
DISTINCT would have to compare a record value built from a volatile function, whose result changes per evaluation, so duplicate elimination is not well defined.
130 — This combination of select-list subqueries is unavailable.
131 — TRUNCATE RESTART IDENTITY with CASCADE is unavailable.
RESTART IDENTITY together with CASCADE is rejected because the cascade set — and so the sequences to restart — is only known per shard. Truncate the tables explicitly.
132 — TRUNCATE RESTART IDENTITY cannot include descendant tables.
TRUNCATE would reach them and their sequences. Use TRUNCATE ONLY, or truncate each table separately.
133 — This whole-row reference is ambiguous to the SQL builder.
t.* is shadowed by a column also named t, so the SQL builder cannot tell which the query means. Rename the column or the alias.
135 — Aggregates over columns of an enclosing query are unavailable.
136 — Correlated column references are unavailable in this clause.
LIMIT (or OFFSET), a clause that is evaluated once rather than per outer row. Pass the limit as a parameter.
138 — FETCH FIRST WITH TIES is unavailable across shards.
WITH TIES needs to know every row tied at the cut-off, which no single shard can determine. Use a plain LIMIT, or rank in a subquery and filter.
140 — Set-returning functions are unavailable in router-side ORDER BY.
ORDER BY must be expanded on the router, which cannot happen while merging sorted streams from several shards. Move the expansion into the FROM clause.
141 — INSERT OVERRIDING USER VALUE is unavailable.
OVERRIDING USER VALUE clause is the rejected part. Omit it, or use OVERRIDING SYSTEM VALUE where you need to write an ALWAYS identity column.
142 — Parameterized subscripts are unavailable in INSERT targets.
INSERT target list is a parameter, so the router does not know which element is being written at plan time. Use a literal subscript, or write the whole array.
143 — OLD and NEW references are unavailable in sharded INSERT ON CONFLICT RETURNING.
OLD/NEW aliases in the RETURNING of an ON CONFLICT statement are not available. Return plain columns.
144 — Router-only expressions are unavailable in sharded INSERT ON CONFLICT RETURNING.
OLD/NEW reference. Return columns and compute the expression in your application.
145 — Updating an index column is unavailable.
user_id, which is the table’s shard index column — changing it could move the row to another shard. Delete the row and insert the new one.
146 — This volatile router-only WHERE expression is unavailable.
WHERE, so the router cannot evaluate it once and route on the result. Compute the value first and pass it in.
147 — This volatile router-only UPDATE assignment is unavailable.
149 — Window functions inside aggregates are unavailable.
150 — This ORDER BY expression is unavailable for VALUES.
ORDER BY attached to a VALUES list is an expression rather than a plain column reference. Sort in the enclosing query instead.
151 — INTERSECT and EXCEPT subqueries are unavailable.
INTERSECT (or EXCEPT). Rewrite it as a join or an IN/NOT IN predicate, or use array functions.
152 — This subquery statement type is unavailable.
VALUES list used as a scalar subquery is not a SELECT. Write (SELECT 1) instead.
153 — ARRAY subqueries are unavailable.
ARRAY(...) subquery the router would have to evaluate, here in ORDER BY, is the rejected part. Use array_agg over a normal subquery: SELECT array_agg(order_id) FROM ....
576 — A whole-row reference to a join is unavailable.
j names the whole row of an aliased join. Select the columns you need instead.
597 — Nested grouped expressions in SELECT targets are unavailable.
733 — Tables ”…” from different schemas cannot both appear unaliased in one query.
notes. Give one of them an alias.
739 — JSON_ARRAY(query) evaluated by the router is unavailable.
JSON_ARRAY over a subquery, evaluated by the router. Use json_agg over the subquery instead.
798 — A record a shard builds is unavailable on the router when the statement builds records of other row types with the same field types: its field names, typmods or collations cannot be told apart.
CASE build a one-field record of type varchar, with different type modifiers. The max merges the records from every shard, and the router cannot tell from a shard’s reply which of the two row types a record has. The same happens when the branches differ in COLLATE. Give the records the same type modifier and COLLATE in every branch.
822 — A LATERAL XMLTABLE or JSON_TABLE is unavailable.
LATERAL JSON_TABLE or XMLTABLE in FROM is the rejected part.
824 — Table ”…” shares its name with another table in the query, which a reference past a column alias list needs to tell apart.
users u(a) renames its first column to a, and the subquery’s orders u reuses the name u. Give the two tables distinct aliases.
945 — Table ”…” inside an aliased join cannot share its name with another table in the query.
public.users sits inside the aliased join j, and the alias users names another table. Give the tables distinct names.
Evaluation engine
Codes raised when the router — rather than a shard — has to compute an expression and has no implementation for it. A query that a shard can evaluate on its own is unaffected.64 — Set-returning function … has no opcode factory.
200 — Built-in function … is unavailable in the evaluation engine.
594 — … is unavailable for this index or access method.
pg_am, so it cannot tell an OID that no access method has from the OID of a custom access method. PostgreSQL returns NULL for this OID.
DDL
Codes raised by the DDL surface, mostly for objects that are local to one Postgres host and so would not reach a shard that joins the cluster later.300 — Creating an operator class is unavailable.
301 — Creating an operator family is unavailable.
302 — Creating a text search configuration is unavailable.
303 — Creating a text search dictionary is unavailable.
304 — Event triggers are unavailable.
ALTER EVENT TRIGGER is rejected the same way.
305 — Rewrite rules are unavailable.
308 — Creating a tablespace is unavailable.
309 — Creating an access method is unavailable.
310 — Creating a collation is unavailable.
311 — Creating a conversion is unavailable.
312 — Foreign data wrappers are unavailable.
ALTER FOREIGN DATA WRAPPER is rejected the same way.
313 — Creating a foreign server is unavailable.
314 — Foreign tables are unavailable.
315 — User mappings are unavailable.
ALTER and DROP USER MAPPING are rejected the same way.
316 — Creating a text search parser is unavailable.
317 — Creating a text search template is unavailable.
318 — Creating a transform is unavailable.
319 — Changing composite-type attributes is unavailable.
320 — Shared library loading is unavailable.
LOAD loads a shared library into one Postgres backend, and it is not carried to a later-joining shard. Install the library on every shard host before starting Neki.
321 — IMPORT FOREIGN SCHEMA is unavailable.
IMPORT FOREIGN SCHEMA creates foreign tables, which Neki does not support (code 314).
323 — Per-column statistics overrides are unavailable.
324 — Per-column foreign-table options are unavailable.
325 — This function language is unavailable.
326 — This procedure language is unavailable.
CREATE PROCEDURE.
327 — CREATE LANGUAGE with a handler is unavailable.
HANDLER clause names a host-local C function. Install the language through a supported extension instead.
328 — Renaming a domain constraint is unavailable.
ALTER DOMAIN ... RENAME TO, SET SCHEMA, and OWNER TO are supported.
329 — ALTER DOMAIN is unavailable.
330 — The IS_TEMPLATE database option is unavailable.
IS_TEMPLATE option is what is rejected; ALTER DATABASE ... IS_TEMPLATE reports the same code.
331 — Database tablespace selection is unavailable.
TABLESPACE option on a database selects host-local storage (code 308). ALTER DATABASE ... SET TABLESPACE reports the same code.
332 — This database locale is unavailable to the router.
icu or builtin locale provider.
333 — Renaming an enum label used as a sharding key is unavailable.
regional_users is sharded on the enum column region, so renaming a label would change where existing rows route. The same statement is allowed for an enum not used as a sharding key.
334 — Column storage selection is unavailable.
SET STORAGE action is the rejected part: Neki cannot preserve a column’s storage setting for a shard that joins the cluster later, so the setting would not be uniform across shards.
335 — Column compression selection is unavailable.
336 — Changing a routing column’s generated expression is unavailable.
account_id is both a generated column and the shard key of regional_accounts. Changing or dropping its generation expression could send existing rows to another shard without moving them. The same change is allowed for a generated column that does not drive routing.
337 — Restarting an identity column through ALTER TABLE is unavailable.
ALTER SEQUENCE instead.
339 — Temporary types and domains are unavailable.
340 — Temporary functions, procedures, aggregates and operators are unavailable.
341 — Temporary statistics are unavailable.
342 — This database encoding is unavailable to the router.
UTF8, the default, or another encoding the router supports.
343 — Converting an existing table to a typed table is unavailable.
347 — Attaching or detaching partitions with different routing is unavailable.
348 — Changing all triggers through ALTER TABLE is unavailable.
ALL change to a shard that joins later. Name the trigger, or use the USER form.
351 — LISTEN, UNLISTEN and NOTIFY are unavailable in stored expressions shards evaluate.
515 — A recursive CTE with a composite-typed column is unavailable on the router.
554 — A multiply referenced MATERIALIZED CTE returning an anonymous record with a non-static shape is unavailable on the router.
CASE branch. Use a fixed record shape.
568 — Creating user-defined base types is unavailable.
573 — A data-modifying CTE statement that reads a written table outside a pre-write stage is unavailable on the router.
users, which the data-modifying CTE writes, outside the stage that runs before the write.
579 — This data-modifying CTE body is unavailable on the router.
MERGE body cannot be hosted by the router CTE materialization boundary.
593 — Recursive CTE ”…” with a SEARCH or CYCLE clause is unavailable when its recursion cannot run on one shard.
name, not on the shard key, so the recursion cannot run on one shard. The router then runs it one step at a time, and it does not compute the SEARCH or CYCLE column, which PostgreSQL derives from the row each new row came from. Join the recursive term on the shard key so the whole recursion runs on one shard.
626 — Refreshing a database collation version is unavailable.
631 — Nested CTE name shadowing is unavailable on the router.
c inside the derived table shadows the outer CTE c. Give the inner CTE a distinct name.
635 — A recursive CTE whose body reads another CTE of its own WITH clause or holds a WITH clause of its own, or a WITH RECURSIVE item referenced before its definition, is unavailable on the router.
t reads seed, another CTE of the same WITH clause. Write the body of seed inside the non-recursive term of t.
640 — A routine that runs a utility statement or dynamic SQL is unavailable in SQL that shards run without the router.
TRUNCATE would act on that shard’s part of notes alone. Creating the function succeeds; creating the trigger that stores it is refused. Run the statement outside the trigger.
645 — A data-modifying CTE body whose output differs from its write’s RETURNING columns is unavailable on the router.
now() output does not match the write producer’s RETURNING columns. This rejection depends on the query plan and data topology.
696 — ALTER EXTENSION membership changes are unavailable.
ALTER EXTENSION ... UPDATE.
700 — Changing base-type I/O options is unavailable.
SET option changes a base type’s input or output functions. Neki cannot
refresh the router’s type metadata after that change, so it rejects the
statement before it reaches a shard.
705 — Data-modifying CTE ”…” read through CTE ”…”, which the planner copies for each of its references, is unavailable on the router.
LATERAL subquery both read copied, so the planner copies it for each reference, and each copy would run the DELETE in deleted. Mark copied as AS MATERIALIZED.
707 — A data-modifying CTE read by a query block the planner eliminates, or by a CTE that does not run, is unavailable on the router.
inserted has a constant-false WHERE, so the planner drops it, and with it the reference that keeps the INSERT in the plan. PostgreSQL still runs the INSERT. Remove the condition, or read inserted from a block that runs.
786 — A data-modifying CTE whose rows the statement reads, writing a table with triggers, is unavailable on the router.
UPDATE once for each shard, so the statement-level trigger products_price_changed would fire more than once for one statement. Any trigger on the written table that fires for the CTE’s command (UPDATE or DELETE) causes this rejection, also when the trigger is disabled. The router rejects the statement for the same reason when the written table:
- is referenced by a foreign key whose
ON UPDATEorON DELETEaction for that command isCASCADE,SET NULLorSET DEFAULT, because PostgreSQL runs that action as a trigger; - has a rewrite rule for that command. Neki rejects
CREATE RULE(code 305), so only a rule created outside Neki can cause this; - is a partition, a partitioned table, or an inheritance parent that the statement writes without
ONLY.
801 — A whole-row CTE reference with a target-list subquery is unavailable on the router.
803 — Session state is unavailable in stored expressions shards evaluate.
SET would outlive the statement and reach other sessions. Creating the function succeeds; creating the trigger that stores it is refused. Use SET LOCAL.
810 — A NOT MATERIALIZED SELECT CTE read more than once by a statement with a data-modifying CTE is unavailable on the router.
batch is read by the UPDATE and by the outer query. Mark it MATERIALIZED.
816 — A data-modifying CTE body without verifiable write output columns is unavailable on the router.
INSERT ... SELECT reads a join across shards, so the router runs the write below its own operators, and the CTE has no RETURNING clause from which it could verify the write’s output columns. This rejection depends on the data topology. Add a RETURNING clause.
821 — A CTE exposing an unnamed subquery column is unavailable on the router.
888 — Recursive CTE ”…” carries a column whose type has no array type, which is unavailable on the router.
order_id, not on the shard key, so the recursion cannot run on one shard. The router then binds every carried column as an array of its type, and tags is already an array, which PostgreSQL has no array type for. Join the recursive term on the shard key so the whole recursion runs on one shard.
896 — SECURITY LABEL statements are unavailable.
SECURITY LABEL statements for all object types.
900 — This CREATE TABLE AS body cannot be populated safely.
WITH NO DATA, add routing for it, then fill it with INSERT ... SELECT.
A topology update is a complete replacement, not a partial patch. Retrieve the current document, add the new table to the shard group the source rows are read from — beside users, under databases.app.schemas.public.tables — and re-apply it:
user_id, which the copied table keeps.
902 — CREATE TABLE AS EXECUTE is unavailable.
CREATE TABLE AS in place of EXECUTE.
905 — GROUP BY or DISTINCT on column ”…” of CTE ”…”, which an outer join inside the CTE body can null, is unavailable on the router.
c.user_id comes from the nullable side of the CTE’s LEFT JOIN, and the outer query deduplicates on it. Mark the CTE AS MATERIALIZED, or write it as a derived table.
933 — This nested CTE inside a data-modifying statement is unavailable on the router.
picked reads the CTE recent, and the UPDATE reads both. Mark picked as AS MATERIALIZED, or read recent directly. This rejection depends on the query plan and data topology.
961 — This nested recursive CTE is unavailable on the router.
967 — A nested CTE referenced more than once is unavailable on the router.
972 — An unreferenced data-modifying CTE that references another CTE is unavailable on the router.
981 — Changing a shard-index routing column is unavailable.
user_id drives routing, and changing it would alter routing without moving existing rows.
992 — A data-modifying CTE body with ON CONFLICT is unavailable on the router.
ON CONFLICT. This rejection depends on the query plan and data topology.
1018 — A data-modifying CTE returning an anonymous record with a non-static shape is unavailable on the router.
RETURNING record has a different number of fields in each CASE branch. The router cannot serialize this record through the CTE materialization boundary. Use a fixed record shape.

