{"record":{"id":"2096873192728469","repo":"apache/cassandra","slug":"primary-key-column-s-cannot-be-restricted-prec","errorCode":null,"errorMessage":"PRIMARY KEY column \"%s\" cannot be restricted (preceding column \"%s\" is restricted by a non-EQ relation)","messagePattern":"PRIMARY KEY column \"(.+?)\" cannot be restricted \\(preceding column \"(.+?)\" is restricted by a non-EQ relation\\)","errorType":"validation","errorClass":"InvalidRequestException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/cql3/restrictions/ClusteringColumnRestrictions.java","lineNumber":98,"sourceCode":"    {\n        SingleRestriction newRestriction = (SingleRestriction) restriction;\n        RestrictionSet newRestrictionSet = restrictions.addRestriction(newRestriction);\n\n        if (!isEmpty() && !allowFiltering && (indexRegistry == null || !newRestriction.hasSupportingIndex(indexRegistry, indexHints)))\n        {\n            SingleRestriction lastRestriction = restrictions.lastRestriction();\n            assert lastRestriction != null;\n\n            ColumnMetadata lastRestrictionStart = lastRestriction.firstColumn();\n            ColumnMetadata newRestrictionStart = restriction.firstColumn();\n\n            checkFalse(lastRestriction.isSlice() && newRestrictionStart.position() > lastRestrictionStart.position(),\n                       \"Clustering column \\\"%s\\\" cannot be restricted (preceding column \\\"%s\\\" is restricted by a non-EQ relation)\",\n                       newRestrictionStart.name,\n                       lastRestrictionStart.name);\n\n            if (newRestrictionStart.position() < lastRestrictionStart.position() && newRestriction.isSlice())\n                throw invalidRequest(\"PRIMARY KEY column \\\"%s\\\" cannot be restricted (preceding column \\\"%s\\\" is restricted by a non-EQ relation)\",\n                                     restrictions.nextColumn(newRestrictionStart).name,\n                                     newRestrictionStart.name);\n        }\n\n        return new ClusteringColumnRestrictions(this.comparator, newRestrictionSet, allowFiltering, partitioner);\n    }\n\n    public NavigableSet<Clustering<?>> valuesAsClustering(QueryOptions options, ClientState state) throws InvalidRequestException\n    {\n        // fast path, a typical case when a single full restriction is used\n        // for example, when we specify a single clustering key (a single row) to insert/update\n        if (restrictions.size() == 1 && !restrictions.hasIN())\n        {\n            SingleRestriction r = restrictions.lastRestriction();\n            List<ClusteringElements> values = r.values(options);\n            return MultiCBuilder.build(comparator, values);\n        }\n        MultiCBuilder builder = new MultiCBuilder(comparator);","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/cql3/restrictions/ClusteringColumnRestrictions.java#L80-L116","documentation":"Cassandra rejects CQL WHERE clauses where a clustering (PRIMARY KEY) column is restricted while an earlier clustering column is only restricted by a non-EQ relation (e.g. a slice like > or <). Since clustering columns are sorted, a restriction on a later column only makes sense when all preceding columns are fixed by EQ relations; otherwise the result set is ill-defined without filtering.","triggerScenarios":"A mergeWith of clustering restrictions occurs when parsing a query like: SELECT ... WHERE clustering1 > 5 AND clustering2 = 3. The slice restriction on clustering1 at an earlier position, plus an EQ on a later column clustering2, triggers this in ClusteringColumnRestrictions.mergeWith.","commonSituations":"Developers write range queries on multi-column clustering keys assuming per-column filtering works like SQL. It appears after schema changes that add clustering columns, or when porting SQL-style queries to CQL.","solutions":["Restrict all clustering columns preceding the sliced column with EQ relations, or drop the restriction on later clustering columns","Use ALLOW FILTERING to explicitly accept a non-efficient query, though this does not bypass the primary-key ordering rule in all cases","Model the data differently: denormalize into a table whose clustering order matches your access pattern"],"exampleFix":"// before\nSELECT * FROM sensor_data WHERE device_id = 1 AND reading_time > '2026-01-01' AND metric = 'temp';\n// after\nSELECT * FROM sensor_data WHERE device_id = 1 AND metric = 'temp' AND reading_time > '2026-01-01';","handlingStrategy":"validation","validationCode":"// before issuing: ensure EQ restrictions precede any slice on clustering columns\nClusterMetaData cmd = table.clusteringColumns();\nboolean seenSlice = false;\nfor (ColumnMetadata c : cmd) {\n    Restriction r = where.restrictionFor(c);\n    if (seenSlice && r != null) throw new IllegalArgumentException(\"restricting '\" + c.name + \"' after a non-EQ restriction\");\n    if (r != null && r.isSlice()) seenSlice = true;\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Design clustering order to match your query patterns up front","Verify with a dry parse (prepare statement) in tests for all query templates","Avoid mixing slice and later-column EQ restrictions without ALLOW FILTERing fallback logic"],"tags":["cql","query-validation","primary-key"],"backgroundTag":"invalid-argument-value","analyzedSha":"88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1","analyzedAt":"2026-09-10T07:29:22.284Z","contentChangedAt":"2026-09-10T07:29:22.284Z","schemaVersion":2},"datasetVersion":"2026-09-14T21:17:11.552Z"}