[
https://issues.apache.org/jira/browse/PHOENIX-7778?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18132186#comment-18132186
]
Hari Krishna Dara commented on PHOENIX-7778:
--------------------------------------------
This is not specific to CDC. ALTER TABLE ... SET MULTI_TENANT changes the flag
on the data table only, so any table that has an index (global, local, covered,
functional or CDC) ends up with an index whose layout no longer matches what
the rest of the code derives from the data table. CDC is the index type used in
the repro above. A CDC index is listed in PTable.getIndexes() like any other
index (CDCUtil.hasActiveCDCIndex, CDCUtil.java:123-127).
Gap 1: MULTI_TENANT is copied to the index at CREATE, but not at ALTER
- CREATE INDEX prepends the tenant id column to the index PK when the data
table is multi-tenant (MetaDataClient.java:1712-1722). The index's own
multiTenant is taken from the parent (MetaDataClient.java:2654) and persisted
in its SYSTEM.CATALOG row (MetaDataClient.java:3675). CREATE CDC copies the
property too (MetaDataClient.java:2044-2046).
- ALTER TABLE SET MULTI_TENANT writes the flag on the data table's row only
(mutateBooleanProperty, MetaDataClient.java:4635-4637). The block in addColumn
that touches the indexes runs only when PK columns are added, for a
non-transactional to transactional change, or for UPDATE_CACHE_FREQUENCY
(MetaDataClient.java:5224-5240). MULTI_TENANT is not in that condition, so the
index rows are not changed and their sequence numbers are not bumped. Adding a
PK column does propagate to every index (MetaDataClient.java:5170-5192); the
flag has no equivalent.
- The server does not propagate it either. The only MULTI_TENANT transition
check is AddColumnMutator.validateWithChildViews
(AddColumnMutator.java:98-105), which covers views only.
ConnectionQueryServicesImpl.java:3104-3118 only creates or drops the view index
physical table.
After the ALTER, the data table says MULTI_TENANT=true, and the index still
says MULTI_TENANT=false and has no tenant id column. The reverse direction
(true to false) leaves the index with a tenant id column that the data table no
longer has.
Gap 2: the readers disagree on which flag describes the index layout
- IndexMaintainer takes isMultiTenant from the data table
(IndexMaintainer.java:495). It uses that to compute the index PK offset
(IndexMaintainer.java:526-535) and to write the tenant id into the index row
key (IndexMaintainer.java:808). Rows written after the flip carry a tenant id
prefix that the index definition does not describe, and rows written before the
flip do not.
- BaseResultIterators.computeColumnsInCommon also uses the data table's flag
(BaseResultIterators.java:805) and then walks the index PK columns from the
shifted position (BaseResultIterators.java:815).
IndexUtil.getDataColumnFamilyName throws StringIndexOutOfBoundsException for a
name with no ':' separator (IndexUtil.java:200-202), the exception reported
here. The stack in the description is truncated, so I have not identified which
caller produced it.
- Other readers use the index's own flag, which still describes the old layout
(IndexExpressionParseNodeRewriter.java:58, PartitionIdParseNode.java:45-47).
- ALTER INDEX ... REBUILD builds with an IndexMaintainer derived from the same
pair, the data table's current flag and the unchanged index definition
(ServerBuildIndexCompiler.java:118, PTableImpl.java:1815). It cannot be
expected to repair the layout. Dropping and re-creating the index does.
Gap 3: related, independent of indexes
throwIfInsufficientColumns (MetaDataClient.java:4100-4122) validates the PK
when a table becomes multi-tenant, but it is only called when the statement
adds no columns (MetaDataClient.java:5219-5222). ALTER TABLE t ADD c VARCHAR
MULTI_TENANT=true skips the check.
Proposal
- Reject any change to MULTI_TENANT, in either direction, on a table that has
any index (PTable.getIndexes() non-empty, including CDC indexes). Use the same
error as the views guard (CANNOT_MUTATE_TABLE) and a message that says to drop
the indexes first and re-create them after the ALTER.
- Enforce it on the server next to validateWithChildViews, because a client's
cached PTable can be stale. A client-side pre-check in addColumn gives an
earlier error.
- Call throwIfInsufficientColumns for the combined ADD ... MULTI_TENANT=true
case as well.
- Tests: the ALTER is rejected in both directions with a global, a local and a
CDC index. It succeeds with no index. After the indexes are dropped it
succeeds, and a re-created index has the tenant id column.
- Tables where the flag was already flipped with indexes in place stay in the
mismatched state until their indexes are dropped and re-created.
File and line references are from a 5.3.0-based tree and may be off by a few
lines on master.
> ALTER TABLE SET MULTI_TENANT=true on a table with a CDC object causes
> StringIndexOutOfBoundsException when querying the CDC index — should be
> blocked at DDL time
> -----------------------------------------------------------------------------------------------------------------------------------------------------------------
>
> Key: PHOENIX-7778
> URL: https://issues.apache.org/jira/browse/PHOENIX-7778
> Project: Phoenix
> Issue Type: Bug
> Components: core, phoenix
> Affects Versions: 5.3.0, 5.3.1
> Reporter: Shubham Roy
> Assignee: Shubham Roy
> Priority: Major
>
> When a Phoenix table has an associated CDC object, issuing ALTER TABLE SET
> MULTI_TENANT=true on it puts the CDC index into an irrecoverably inconsistent
> state. Any subsequent query on the CDC index that uses PARTITION_ID() or
> PHOENIX_ROW_TIMESTAMP() in the WHERE clause crashes with
> StringIndexOutOfBoundsException. The ALTER should be blocked with a clear
> error message at DDL time.
>
> Steps to Reproduce:
> {code:java}
> -- 1. Create a regular table
> CREATE TABLE PERF_TEST_SET1 (ID INTEGER NOT NULL PRIMARY KEY, V1 VARCHAR);
> -- 2. Create a CDC object
> CREATE CDC PERF_TEST_SET1_CDC ON PERF_TEST_SET1;
> -- 3. Alter to multi-tenant (this should be blocked, but currently is not)
> ALTER TABLE PERF_TEST_SET1 SET MULTI_TENANT=true;
> -- 4. Query the CDC index — crashes
> SELECT * FROM PHOENIX_CDC_INDEX_PERF_TEST_SET1_CDC
> WHERE " :PHOENIX_ROW_TIMESTAMP()" BETWEEN
> TO_TIMESTAMP('2026-02-28 01:00:00') AND TO_TIMESTAMP('2026-02-28
> 02:00:00')
> LIMIT 5; {code}
>
> Observed behaviour:
> {code:java}
> java.lang.StringIndexOutOfBoundsException: String index out of range: -1
> at java.lang.String.substring(String.java:1967)
> at
> org.apache.phoenix.util.IndexUtil.getDataColumnFamilyName(IndexUtil.java:201)
> at
> org.apache.phoenix.schema.IndexUncoveredDataColumnRef.<init>(IndexUncoveredDataColumnRef.java:51)
> at
> org.apache.phoenix.compile.ExpressionCompiler.resolveColumn(ExpressionCompiler.java:414)
> at
> org.apache.phoenix.compile.WhereCompiler$WhereExpressionCompiler.resolveColumn(WhereCompiler.java:293)
> at
> org.apache.phoenix.compile.WhereCompiler$WhereExpressionCompiler.visit(WhereCompiler.java:275)
> at
> org.apache.phoenix.parse.ColumnParseNode.accept(ColumnParseNode.java:56)
> at
> org.apache.phoenix.parse.CompoundParseNode.acceptChildren(CompoundParseNode.java:58)
> at
> org.apache.phoenix.parse.ComparisonParseNode.accept(ComparisonParseNode.java:41)
> at
> org.apache.phoenix.parse.CompoundParseNode.acceptChildren(CompoundParseNode.java:58)
> at org.apache.phoenix.parse.AndParseNode.accept(AndParseNode.java:40)
> at
> org.apache.phoenix.compile.WhereCompiler.compile(WhereCompiler.java:202)
> at
> org.apache.phoenix.compile.WhereCompiler.compile(WhereCompiler.java:164)
> at
> org.apache.phoenix.compile.QueryCompiler.compileSingleFlatQuery(QueryCompiler.java:834)
> at
> org.apache.phoenix.compile.QueryCompiler.compileSingleQuery(QueryCompiler.java:721)
> at
> org.apache.phoenix.compile.QueryCompiler.compileSelect(QueryCompiler.java:319)
> at
> org.apache.phoenix.jdbc.PhoenixStatement$ExecutableSelectStatement.compilePlan(PhoenixStatement.java:871)
> ... {code}
> Expected Behaviour:
> ALTER TABLE SET MULTI_TENANT=true should fail with a clear error message if
> the table has any CDC objects, e.g.:
> {code:java}
> ERROR: Cannot change MULTI_TENANT property on table 'PERF_TEST_SET1' because
> it has an associated CDC object 'PERF_TEST_SET1_CDC'. {code}
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)