[ 
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)

Reply via email to