{"record":{"id":"8a7484b4b39f54b0","repo":"apache/cassandra","slug":"batch-with-conditions-cannot-span-multiple-tables","errorCode":null,"errorMessage":"Batch with conditions cannot span multiple tables: %s","messagePattern":"Batch with conditions cannot span multiple tables: (.+?)","errorType":"validation","errorClass":"InvalidRequestException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/cql3/statements/BatchStatement.java","lineNumber":318,"sourceCode":"            throw new InvalidRequestException(\"Cannot include a counter statement in a logged batch\");\n\n        if (isLogged() && hasVirtualTables)\n            throw new InvalidRequestException(\"Cannot include a virtual table statement in a logged batch\");\n\n        if (hasVirtualTables && hasRegularTables)\n            throw new InvalidRequestException(\"Mutations for virtual and regular tables cannot exist in the same batch\");\n\n        if (hasConditions && hasVirtualTables)\n            throw new InvalidRequestException(\"Conditional BATCH statements cannot include mutations for virtual tables\");\n\n        if (hasConditions)\n        {\n            String ksName = null;\n            String cfName = null;\n            for (ModificationStatement stmt : statements)\n            {\n                if (ksName != null && (!stmt.keyspace().equals(ksName) || !stmt.table().equals(cfName)))\n                    throw new InvalidRequestException(\"Batch with conditions cannot span multiple tables: \" + stmt.source);\n                ksName = stmt.keyspace();\n                cfName = stmt.table();\n            }\n        }\n    }\n\n    private boolean isCounter()\n    {\n        return type == Type.COUNTER;\n    }\n\n    private boolean isLogged()\n    {\n        return type == Type.LOGGED;\n    }\n\n    // The batch itself will be validated in either Parsed#prepare() - for regular CQL3 batches,\n    //   or in QueryProcessor.processBatch() - for native protocol batches.","sourceCodeStart":300,"sourceCodeEnd":336,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java#L300-L336","documentation":"Batches using lightweight transactions (conditions) are only atomic within a single table. When validate() finds conditional statements targeting more than one keyspace/table, it rejects the batch, reporting the offending statement's source location.","triggerScenarios":"'BEGIN BATCH UPDATE t1 ... IF x = 1; UPDATE t2 ... IF y = 2; APPLY BATCH' where t1 and t2 are different tables (or different keyspaces).","commonSituations":"Multi-entity transaction attempts ported from relational databases; applications assuming CAS batches span tables.","solutions":["Restrict the conditional batch so all statements target one table (and as of the partition check, ideally one partition)","Split into per-table conditional statements and handle cross-table coordination in the application","Use a single denormalized table if atomic conditional updates across what are now multiple tables are required"],"exampleFix":"// before\nBEGIN BATCH UPDATE users SET name='a' WHERE id=1 IF name='b';\n             UPDATE orders SET status='shipped' WHERE id=9 IF status='paid'; APPLY BATCH;\n// after\nBEGIN BATCH UPDATE users SET name='a' WHERE id=1 IF name='b'; APPLY BATCH;\nUPDATE orders SET status='shipped' WHERE id=9 IF status='paid';\n","handlingStrategy":"validation","validationCode":"if (batchHasConditions && stmts.stream().map(s -> s.getTable()).distinct().count() > 1)\n    throw new IllegalArgumentException(\"LWT batch must target a single table\");","typeGuard":null,"tryCatchPattern":"catch (InvalidRequestException e) { if (e.getMessage().startsWith(\"Batch with conditions cannot span multiple tables\")) { splitPerTableAndRetry(); } else throw e; }","preventionTips":["Design conditional writes around a single (denormalized) table","Never port multi-table SQL transactions directly to Cassandra LWT batches"],"tags":["cql","batch","lwt","multiple-tables"],"backgroundTag":"unsupported-operation","analyzedSha":"88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1","analyzedAt":"2026-09-10T07:29:22.284Z","contentChangedAt":"2026-09-10T07:29:22.284Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}