From 43894ccee9ffd8c10ababc7b89d8754a0f82218a Mon Sep 17 00:00:00 2001
From: Palash Chauhan
Date: Fri, 23 Jan 2026 15:33:53 -0800
Subject: [PATCH 1/2] PHOENIX-7753 : Allow uncovered index creation on tables
with relaxed conditional TTL
---
.../apache/phoenix/schema/MetaDataClient.java | 11 ++-
.../schema/ConditionalTTLExpressionTest.java | 74 ++++++++++++-------
2 files changed, 57 insertions(+), 28 deletions(-)
diff --git a/phoenix-core-client/src/main/java/org/apache/phoenix/schema/MetaDataClient.java b/phoenix-core-client/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
index edb38da970c..4795c39dcf5 100644
--- a/phoenix-core-client/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
+++ b/phoenix-core-client/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
@@ -2529,7 +2529,11 @@ private PTable createTableInternal(CreateTableStatement statement, byte[][] spli
} else {
ttlFromHierarchy = checkAndGetTTLFromHierarchy(parent, tableName);
if (!ttlFromHierarchy.equals(TTL_EXPRESSION_NOT_DEFINED)) {
- ttlFromHierarchy.validateTTLOnCreate(connection, statement, parent, tableProps);
+ if (parent.hasConditionalTTL() && !parent.isStrictTTL()) {
+ ttlFromHierarchy = TTL_EXPRESSION_NOT_DEFINED;
+ } else {
+ ttlFromHierarchy.validateTTLOnCreate(connection, statement, parent, tableProps);
+ }
}
}
@@ -6413,7 +6417,10 @@ private boolean evaluateStmtProperties(MetaProperties metaProperties,
}
if (metaProperties.getTTL() != table.getTTLExpression()) {
TTLExpression newTTL = metaProperties.getTTL();
- newTTL.validateTTLOnAlter(connection, table);
+ boolean isStrictTTL = metaProperties.isStrictTTL() != null ? metaProperties.isStrictTTL : true;
+ if (!(newTTL instanceof ConditionalTTLExpression) || isStrictTTL) {
+ newTTL.validateTTLOnAlter(connection, table);
+ }
metaPropertiesEvaluated.setTTL(getCompatibleTTLExpression(metaProperties.getTTL(),
table.getType(), table.getViewType(), table.getName().toString()));
changingPhoenixTableProperty = true;
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/schema/ConditionalTTLExpressionTest.java b/phoenix-core/src/test/java/org/apache/phoenix/schema/ConditionalTTLExpressionTest.java
index b82dd5e72d8..b81a9998130 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/schema/ConditionalTTLExpressionTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/schema/ConditionalTTLExpressionTest.java
@@ -55,7 +55,7 @@
public class ConditionalTTLExpressionTest extends BaseConnectionlessQueryTest {
- private static void assertConditonTTL(Connection conn, String tableName, String ttlExpr)
+ private static void assertConditionTTL(Connection conn, String tableName, String ttlExpr)
throws SQLException {
TTLExpression expected = new ConditionalTTLExpression(ttlExpr);
assertTTL(conn, tableName, expected);
@@ -105,7 +105,7 @@ public void testBasicExpression() throws SQLException {
String ddl = String.format(ddlTemplate, tableName, retainSingleQuotes(ttl));
try (Connection conn = DriverManager.getConnection(getUrl())) {
conn.createStatement().execute(ddl);
- assertConditonTTL(conn, tableName, ttl);
+ assertConditionTTL(conn, tableName, ttl);
String query = String.format("SELECT count(*) from %s where k1 > 3", tableName);
validateScan(conn, tableName, query, ttl, false, Lists.newArrayList("col1"));
}
@@ -174,17 +174,17 @@ public void testSingleNonDefaultColumnFamilyIsAllowed() throws SQLException {
String ddl = String.format(ddlTemplate, tableName, retainSingleQuotes(ttl));
try (Connection conn = DriverManager.getConnection(getUrl())) {
conn.createStatement().execute(ddl);
- assertConditonTTL(conn, tableName, ttl);
+ assertConditionTTL(conn, tableName, ttl);
// create global index
String indexName = "I_" + generateUniqueName();
ddl = String.format("create index %s on %s (col2) include(col1)", indexName, tableName);
conn.createStatement().execute(ddl);
- assertConditonTTL(conn, indexName, ttl);
+ assertConditionTTL(conn, indexName, ttl);
// create local index
indexName = "L_" + generateUniqueName();
ddl = String.format("create local index %s on %s (col2) include(col1)", indexName, tableName);
conn.createStatement().execute(ddl);
- assertConditonTTL(conn, indexName, ttl);
+ assertConditionTTL(conn, indexName, ttl);
}
}
@@ -198,23 +198,23 @@ public void testDefaultColumnFamily() throws SQLException {
String ddl = String.format(ddlTemplate, tableName, retainSingleQuotes(ttl));
try (Connection conn = DriverManager.getConnection(getUrl())) {
conn.createStatement().execute(ddl);
- assertConditonTTL(conn, tableName, ttl);
+ assertConditionTTL(conn, tableName, ttl);
// create view
String viewName = "GV_" + generateUniqueName();
ddl = String.format("create view %s (col3 varchar) as select * from %s where k1 = 2",
viewName, tableName);
conn.createStatement().execute(ddl);
- assertConditonTTL(conn, viewName, ttl);
+ assertConditionTTL(conn, viewName, ttl);
// create global index
String indexName = "I_" + generateUniqueName();
ddl = String.format("create index %s on %s (col2) include(col1)", indexName, tableName);
conn.createStatement().execute(ddl);
- assertConditonTTL(conn, indexName, ttl);
+ assertConditionTTL(conn, indexName, ttl);
// create local index
indexName = "L_" + generateUniqueName();
ddl = String.format("create local index %s on %s (col2) include(col1)", indexName, tableName);
conn.createStatement().execute(ddl);
- assertConditonTTL(conn, indexName, ttl);
+ assertConditionTTL(conn, indexName, ttl);
}
}
@@ -301,7 +301,7 @@ public void testMultipleColumnFamilyNotAllowedOnAddColumn3() throws SQLException
String ddl = String.format(ddlTemplate, tableName, retainSingleQuotes(ttl));
try (Connection conn = DriverManager.getConnection(getUrl())) {
conn.createStatement().execute(ddl);
- assertConditonTTL(conn, tableName, ttl);
+ assertConditionTTL(conn, tableName, ttl);
// add a new column in a different column family
String alterDDL = String.format("alter table %s add A.col3 varchar", tableName);
try {
@@ -369,7 +369,7 @@ public void testNullExpression() throws SQLException {
String ddl = String.format(ddlTemplate, tableName, ttl);
try (Connection conn = DriverManager.getConnection(getUrl())) {
conn.createStatement().execute(ddl);
- assertConditonTTL(conn, tableName, ttl);
+ assertConditionTTL(conn, tableName, ttl);
String query = String.format("SELECT count(*) from %s", tableName);
validateScan(conn, tableName, query, ttl, false, Lists.newArrayList("col1", "col2"));
}
@@ -387,14 +387,14 @@ public void testBooleanColumn() throws SQLException {
try (Connection conn = DriverManager.getConnection(getUrl())) {
String ddl = String.format(ddlTemplate, tableName, ttl);
conn.createStatement().execute(ddl);
- assertConditonTTL(conn, tableName, ttl);
+ assertConditionTTL(conn, tableName, ttl);
query = String.format("SELECT k1, k2 from %s where (k1,k2) IN ((1,2), (3,4))", tableName);
validateScan(conn, tableName, query, ttl, false, Lists.newArrayList("expired"));
ddl = String.format(indexTemplate, indexName, tableName);
conn.createStatement().execute(ddl);
- assertConditonTTL(conn, indexName, ttl);
+ assertConditionTTL(conn, indexName, ttl);
// validate the scan on index
query = String.format("SELECT count(*) from %s", tableName);
@@ -411,7 +411,7 @@ public void testNot() throws SQLException {
String ddl = String.format(ddlTemplate, tableName, ttl);
try (Connection conn = DriverManager.getConnection(getUrl())) {
conn.createStatement().execute(ddl);
- assertConditonTTL(conn, tableName, ttl);
+ assertConditionTTL(conn, tableName, ttl);
}
}
@@ -425,7 +425,7 @@ public void testPhoenixRowTimestamp() throws SQLException {
String query;
try (Connection conn = DriverManager.getConnection(getUrl())) {
conn.createStatement().execute(ddl);
- assertConditonTTL(conn, tableName, ttl);
+ assertConditionTTL(conn, tableName, ttl);
query = String.format("select col1 from %s where k1 = 7 AND k2 > 12", tableName);
validateScan(conn, tableName, query, ttl, false, Lists.newArrayList("col1"));
}
@@ -441,7 +441,7 @@ public void testBooleanCaseExpression() throws SQLException {
String ddl = String.format(ddlTemplate, tableName, ttl);
try (Connection conn = DriverManager.getConnection(getUrl())) {
conn.createStatement().execute(ddl);
- assertConditonTTL(conn, tableName, expectedTTLExpr);
+ assertConditionTTL(conn, tableName, expectedTTLExpr);
}
}
@@ -460,7 +460,7 @@ public void testCondTTLOnTopLevelView() throws SQLException {
ddl = String.format(viewTemplate, viewName, tableName, ttl);
conn.createStatement().execute(ddl);
assertTTL(conn, tableName, TTL_EXPRESSION_NOT_DEFINED);
- assertConditonTTL(conn, viewName, ttl);
+ assertConditionTTL(conn, viewName, ttl);
String query = String.format("select k3 from %s", viewName);
validateScan(conn, viewName, query, ttl, false, Lists.newArrayList("k2", "k3"));
}
@@ -487,11 +487,11 @@ public void testCondTTLOnMultiLevelView() throws SQLException {
conn.createStatement().execute(ddl);
assertTTL(conn, tableName, TTL_EXPRESSION_NOT_DEFINED);
assertTTL(conn, parentView, TTL_EXPRESSION_NOT_DEFINED);
- assertConditonTTL(conn, childView, ttl);
+ assertConditionTTL(conn, childView, ttl);
// create an index on child view
ddl = String.format(indexOnChildTemplate, indexName, childView);
conn.createStatement().execute(ddl);
- assertConditonTTL(conn, indexName, ttl);
+ assertConditionTTL(conn, indexName, ttl);
}
}
@@ -505,7 +505,7 @@ public void testInListTTLExpr() throws Exception {
try (Connection conn = DriverManager.getConnection(getUrl())) {
String ddl = String.format(ddlTemplate, tableName, retainSingleQuotes(ttl));
conn.createStatement().execute(ddl);
- assertConditonTTL(conn, tableName, ttl);
+ assertConditionTTL(conn, tableName, ttl);
query = String.format("select col1 from %s where id IN ('abc', 'fff')", tableName);
validateScan(conn, tableName, query, ttl, false, Lists.newArrayList("col1", "col2"));
}
@@ -524,10 +524,10 @@ public void testPartialIndex() throws Exception {
try (Connection conn = DriverManager.getConnection(getUrl())) {
String ddl = String.format(ddlTemplate, tableName, retainSingleQuotes(ttl));
conn.createStatement().execute(ddl);
- assertConditonTTL(conn, tableName, ttl);
+ assertConditionTTL(conn, tableName, ttl);
ddl = String.format(indexTemplate, indexName, tableName);
conn.createStatement().execute(ddl);
- assertConditonTTL(conn, indexName, ttl);
+ assertConditionTTL(conn, indexName, ttl);
query = String.format("select col3 from %s where col1 > 60", tableName);
validateScan(conn, tableName, query, ttl, true,
Lists.newArrayList("0:col2", "0:col3", "0:col4"));
@@ -535,7 +535,7 @@ public void testPartialIndex() throws Exception {
}
@Test
- public void testUncoveredIndex() throws Exception {
+ public void testUncoveredIndexStrictTTL() throws Exception {
String ddlTemplate = "create table %s (id varchar not null primary key, "
+ "col1 integer, col2 integer, col3 double, col4 varchar) TTL = '%s'";
String tableName = generateUniqueName();
@@ -546,7 +546,7 @@ public void testUncoveredIndex() throws Exception {
try (Connection conn = DriverManager.getConnection(getUrl())) {
String ddl = String.format(ddlTemplate, tableName, retainSingleQuotes(ttl));
conn.createStatement().execute(ddl);
- assertConditonTTL(conn, tableName, ttl);
+ assertConditionTTL(conn, tableName, ttl);
ddl = String.format(indexTemplate, indexName, tableName);
try {
conn.createStatement().execute(ddl);
@@ -557,10 +557,28 @@ public void testUncoveredIndex() throws Exception {
indexTemplate = "create uncovered index %s on %s (col4, col2) ";
ddl = String.format(indexTemplate, indexName, tableName);
conn.createStatement().execute(ddl);
- assertConditonTTL(conn, indexName, ttl);
+ assertConditionTTL(conn, indexName, ttl);
}
}
+ @Test
+ public void testUncoveredIndexRelaxedTTL() throws Exception {
+ String ddlTemplate = "create table %s (id varchar not null primary key, "
+ + "col1 integer, col2 integer, col3 double, col4 varchar) TTL = '%s', IS_STRICT_TTL=false";
+ String tableName = generateUniqueName();
+ String indexTemplate = "create uncovered index %s on %s (col1) ";
+ String indexName = generateUniqueName();
+ String ttl = "col2 > 100 AND col4='expired'";
+ try (Connection conn = DriverManager.getConnection(getUrl())) {
+ String ddl = String.format(ddlTemplate, tableName, retainSingleQuotes(ttl));
+ conn.createStatement().execute(ddl);
+ assertConditionTTL(conn, tableName, ttl);
+ ddl = String.format(indexTemplate, indexName, tableName);
+ conn.createStatement().execute(ddl);
+ assertTTL(conn, indexName, TTL_EXPRESSION_NOT_DEFINED);
+ }
+ }
+
@Test
public void testCreatingIndexWithMissingExprCols() throws Exception {
String ddlTemplate = "create table %s (id varchar not null primary key, "
@@ -602,6 +620,10 @@ public void testSettingCondTTLOnTableWithIndexWithMissingExprCols() throws Excep
} catch (SQLException e) {
assertTrue(e.getCause() instanceof ColumnNotFoundException);
}
+ // relaxed ttl
+ ddl = String.format("alter table %s set TTL = '%s', IS_STRICT_TTL = false", tableName, retainSingleQuotes(ttl));
+ conn.createStatement().execute(ddl);
+ assertTTL(conn, indexName, TTL_EXPRESSION_NOT_DEFINED);
}
}
@@ -615,7 +637,7 @@ public void testScanColumns() throws Exception {
String ddl = String.format(ddlTemplate, tableName, retainSingleQuotes(ttl));
try (Connection conn = DriverManager.getConnection(getUrl())) {
conn.createStatement().execute(ddl);
- assertConditonTTL(conn, tableName, ttl);
+ assertConditionTTL(conn, tableName, ttl);
String query = String.format("select * from %s where k1 > 3", tableName);
// select * so all columns should be read
validateScan(conn, tableName, query, ttl, false, Collections.EMPTY_LIST);
From 14ec5e087c21dc64f303803e7f4cf592e79c8956 Mon Sep 17 00:00:00 2001
From: Palash Chauhan
Date: Fri, 23 Jan 2026 15:44:17 -0800
Subject: [PATCH 2/2] spotless and more tests
---
.../apache/phoenix/schema/MetaDataClient.java | 15 +++---
.../schema/ConditionalTTLExpressionTest.java | 54 +++++++++++++------
2 files changed, 45 insertions(+), 24 deletions(-)
diff --git a/phoenix-core-client/src/main/java/org/apache/phoenix/schema/MetaDataClient.java b/phoenix-core-client/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
index 4795c39dcf5..481b44bfa9e 100644
--- a/phoenix-core-client/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
+++ b/phoenix-core-client/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
@@ -2529,11 +2529,11 @@ private PTable createTableInternal(CreateTableStatement statement, byte[][] spli
} else {
ttlFromHierarchy = checkAndGetTTLFromHierarchy(parent, tableName);
if (!ttlFromHierarchy.equals(TTL_EXPRESSION_NOT_DEFINED)) {
- if (parent.hasConditionalTTL() && !parent.isStrictTTL()) {
- ttlFromHierarchy = TTL_EXPRESSION_NOT_DEFINED;
- } else {
- ttlFromHierarchy.validateTTLOnCreate(connection, statement, parent, tableProps);
- }
+ if (parent.hasConditionalTTL() && !parent.isStrictTTL()) {
+ ttlFromHierarchy = TTL_EXPRESSION_NOT_DEFINED;
+ } else {
+ ttlFromHierarchy.validateTTLOnCreate(connection, statement, parent, tableProps);
+ }
}
}
@@ -6417,9 +6417,10 @@ private boolean evaluateStmtProperties(MetaProperties metaProperties,
}
if (metaProperties.getTTL() != table.getTTLExpression()) {
TTLExpression newTTL = metaProperties.getTTL();
- boolean isStrictTTL = metaProperties.isStrictTTL() != null ? metaProperties.isStrictTTL : true;
+ boolean isStrictTTL =
+ metaProperties.isStrictTTL() != null ? metaProperties.isStrictTTL : table.isStrictTTL();
if (!(newTTL instanceof ConditionalTTLExpression) || isStrictTTL) {
- newTTL.validateTTLOnAlter(connection, table);
+ newTTL.validateTTLOnAlter(connection, table);
}
metaPropertiesEvaluated.setTTL(getCompatibleTTLExpression(metaProperties.getTTL(),
table.getType(), table.getViewType(), table.getName().toString()));
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/schema/ConditionalTTLExpressionTest.java b/phoenix-core/src/test/java/org/apache/phoenix/schema/ConditionalTTLExpressionTest.java
index b81a9998130..7e6fa9128f2 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/schema/ConditionalTTLExpressionTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/schema/ConditionalTTLExpressionTest.java
@@ -561,23 +561,23 @@ public void testUncoveredIndexStrictTTL() throws Exception {
}
}
- @Test
- public void testUncoveredIndexRelaxedTTL() throws Exception {
- String ddlTemplate = "create table %s (id varchar not null primary key, "
- + "col1 integer, col2 integer, col3 double, col4 varchar) TTL = '%s', IS_STRICT_TTL=false";
- String tableName = generateUniqueName();
- String indexTemplate = "create uncovered index %s on %s (col1) ";
- String indexName = generateUniqueName();
- String ttl = "col2 > 100 AND col4='expired'";
- try (Connection conn = DriverManager.getConnection(getUrl())) {
- String ddl = String.format(ddlTemplate, tableName, retainSingleQuotes(ttl));
- conn.createStatement().execute(ddl);
- assertConditionTTL(conn, tableName, ttl);
- ddl = String.format(indexTemplate, indexName, tableName);
- conn.createStatement().execute(ddl);
- assertTTL(conn, indexName, TTL_EXPRESSION_NOT_DEFINED);
- }
+ @Test
+ public void testUncoveredIndexRelaxedTTL() throws Exception {
+ String ddlTemplate = "create table %s (id varchar not null primary key, "
+ + "col1 integer, col2 integer, col3 double, col4 varchar) TTL = '%s', IS_STRICT_TTL=false";
+ String tableName = generateUniqueName();
+ String indexTemplate = "create uncovered index %s on %s (col1) ";
+ String indexName = generateUniqueName();
+ String ttl = "col2 > 100 AND col4='expired'";
+ try (Connection conn = DriverManager.getConnection(getUrl())) {
+ String ddl = String.format(ddlTemplate, tableName, retainSingleQuotes(ttl));
+ conn.createStatement().execute(ddl);
+ assertConditionTTL(conn, tableName, ttl);
+ ddl = String.format(indexTemplate, indexName, tableName);
+ conn.createStatement().execute(ddl);
+ assertTTL(conn, indexName, TTL_EXPRESSION_NOT_DEFINED);
}
+ }
@Test
public void testCreatingIndexWithMissingExprCols() throws Exception {
@@ -621,7 +621,27 @@ public void testSettingCondTTLOnTableWithIndexWithMissingExprCols() throws Excep
assertTrue(e.getCause() instanceof ColumnNotFoundException);
}
// relaxed ttl
- ddl = String.format("alter table %s set TTL = '%s', IS_STRICT_TTL = false", tableName, retainSingleQuotes(ttl));
+ ddl = String.format("alter table %s set TTL = '%s', IS_STRICT_TTL = false", tableName,
+ retainSingleQuotes(ttl));
+ conn.createStatement().execute(ddl);
+ assertTTL(conn, indexName, TTL_EXPRESSION_NOT_DEFINED);
+ }
+ }
+
+ @Test
+ public void testSettingCondTTLOnTableWithRelaxedTTLAndUncoveredIndex() throws Exception {
+ String ddlTemplate = "create table %s (id varchar not null primary key, "
+ + "col1 integer, col2 integer, col3 double, col4 varchar) IS_STRICT_TTL = false";
+ String tableName = generateUniqueName();
+ String indexTemplate = "create uncovered index %s on %s (col1)";
+ String indexName = generateUniqueName();
+ String ttl = "col2 > 100 AND col4='expired'";
+ try (Connection conn = DriverManager.getConnection(getUrl())) {
+ String ddl = String.format(ddlTemplate, tableName);
+ conn.createStatement().execute(ddl);
+ ddl = String.format(indexTemplate, indexName, tableName);
+ conn.createStatement().execute(ddl);
+ ddl = String.format("alter table %s set TTL = '%s'", tableName, retainSingleQuotes(ttl));
conn.createStatement().execute(ddl);
assertTTL(conn, indexName, TTL_EXPRESSION_NOT_DEFINED);
}