{"record":{"id":"982d7c144bce52eb","repo":"apache/cassandra","slug":"prefix-queries-in-contains-mode-are-not-supported","errorCode":null,"errorMessage":"prefix queries in CONTAINS mode are not supported by this index","messagePattern":"prefix queries in CONTAINS mode are not supported by this index","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/index/sasi/disk/OnDiskIndex.java","lineNumber":229,"sourceCode":"    public DataTerm max()\n    {\n        DataBlock block = dataLevel.getBlock(dataLevel.blockCount - 1);\n        return block.getTerm(block.termCount() - 1);\n    }\n\n    /**\n     * Search for rows which match all of the terms inside the given expression in the index file.\n     *\n     * @param exp The expression to use for the query.\n     *\n     * @return Iterator which contains rows for all of the terms from the given range.\n     */\n    public RangeIterator<Long, Token> search(Expression exp)\n    {\n        assert mode.supports(exp.getOp());\n\n        if (exp.getOp() == Expression.Op.PREFIX && mode == OnDiskIndexBuilder.Mode.CONTAINS && !hasMarkedPartials)\n            throw new UnsupportedOperationException(\"prefix queries in CONTAINS mode are not supported by this index\");\n\n        // optimization in case single term is requested from index\n        // we don't really need to build additional union iterator\n        if (exp.getOp() == Op.EQ)\n        {\n            DataTerm term = getTerm(exp.lower.value);\n            return term == null ? null : term.getTokens();\n        }\n\n        // convert single NOT_EQ to range with exclusion\n        final Expression expression = (exp.getOp() != Op.NOT_EQ)\n                                        ? exp\n                                        : new Expression(exp).setOp(Op.RANGE)\n                                                .setLower(new Expression.Bound(minTerm, true))\n                                                .setUpper(new Expression.Bound(maxTerm, true))\n                                                .addExclusion(exp.lower.value);\n\n        List<ByteBuffer> exclusions = new ArrayList<>(expression.exclusions.size());","sourceCodeStart":211,"sourceCodeEnd":247,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/index/sasi/disk/OnDiskIndex.java#L211-L247","documentation":"OnDiskIndex.search rejects PREFIX expressions when the index was built in CONTAINS mode without marked partials: CONTAINS-mode SASI indexes only store suffix matches, so a prefix (LIKE 'foo%') query cannot be answered. Throws UnsupportedOperationException.","triggerScenarios":"Executing a LIKE 'abc%' (prefix) query against a column indexed with mode=CONTAINS when the on-disk index lacks marked partial terms (hasMarkedPartials == false).","commonSituations":"Application assumes CONTAINS mode supports all LIKE patterns; schema evolved so index built without support for partial prefix terms; tests (testStringSAConstruction, rows, etc.) exercise prefix queries against CONTAINS indexes.","solutions":["Rewrite the query as a suffix/substring match (LIKE '%abc%') in CONTAINS mode","Rebuild the index in PREFIX mode if prefix queries are needed","Use CONTAINS mode index built with support for marked partials so prefix queries are answerable"],"exampleFix":"// before\nSELECT * FROM t WHERE col LIKE 'foo%';  // CONTAINS-mode SASI index\n// after\nSELECT * FROM t WHERE col LIKE '%foo%';  // or rebuild index in PREFIX mode","handlingStrategy":"try-catch","validationCode":"boolean isPrefixQuery(String likePattern) { return !likePattern.startsWith(\"%\") && likePattern.endsWith(\"%\"); }\n// reject prefix patterns when the column's SASI index is mode=CONTAINS","typeGuard":null,"tryCatchPattern":"try { rs = session.execute(query); } catch (UnsupportedOperationException e) { if (e.getMessage().contains(\"prefix queries in CONTAINS mode\")) { rs = runAlternativeQuery(suffixPattern); } else throw e; }","preventionTips":["Match query shape to index mode: PREFIX index for 'foo%', CONTAINS index for '%foo%'","Document index mode alongside query templates","Verify mode in schema metadata before issuing LIKE queries"],"tags":["sasi","unsupported-query","contains-mode","query"],"backgroundTag":"unsupported-operation","analyzedSha":"88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1","analyzedAt":"2026-09-10T07:29:22.284Z","contentChangedAt":"2026-09-10T07:29:22.284Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}