You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@phoenix.apache.org by ap...@apache.org on 2026/10/09 05:10:13 UTC
(phoenix) 14/16: PHOENIX-7998 Phase 14: Compatibility and version validation
This is an automated email from the ASF dual-hosted git repository.
asf-gitbox-commits pushed a commit to branch PHOENIX-7998
in repository https://gitbox.apache.org/repos/asf/phoenix.git
commit deae510427e6ede49028e9f016262c83d9fcf000
Author: Andrew Purtell <ap...@apache.org>
AuthorDate: Tue Oct 6 00:32:54 2026 -0700
PHOENIX-7998 Phase 14: Compatibility and version validation
Co-Authored-By: Claude Opus 5.5 (1M context) <no...@anthropic.com>
---
.../java/org/apache/phoenix/schema/PTable.java | 3 +-
.../expression/ExpressionTypeOrdinalTest.java | 99 ++++++++++++++++++++++
.../apache/phoenix/schema/VectorIndexTypeTest.java | 2 +
.../phoenix/schema/types/VectorDataTypeTest.java | 18 ++++
4 files changed, 121 insertions(+), 1 deletion(-)
diff --git a/phoenix-core-client/src/main/java/org/apache/phoenix/schema/PTable.java b/phoenix-core-client/src/main/java/org/apache/phoenix/schema/PTable.java
index e89fa4affe..ea2b781733 100644
--- a/phoenix-core-client/src/main/java/org/apache/phoenix/schema/PTable.java
+++ b/phoenix-core-client/src/main/java/org/apache/phoenix/schema/PTable.java
@@ -137,7 +137,8 @@ public interface PTable extends PMetaDataEntity {
public static IndexType fromSerializedValue(byte serializedValue) {
if (serializedValue < 1 || serializedValue > IndexType.values().length) {
- throw new IllegalArgumentException("Invalid IndexType " + serializedValue);
+ throw new IllegalArgumentException("Unsupported IndexType " + serializedValue
+ + ": the index was created by a newer Phoenix version. Upgrade this client to read it.");
}
return IndexType.values()[serializedValue - 1];
}
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/expression/ExpressionTypeOrdinalTest.java b/phoenix-core/src/test/java/org/apache/phoenix/expression/ExpressionTypeOrdinalTest.java
new file mode 100644
index 0000000000..9df7940000
--- /dev/null
+++ b/phoenix-core/src/test/java/org/apache/phoenix/expression/ExpressionTypeOrdinalTest.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.expression;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+/**
+ * Makes sure that the ordinals of {@link ExpressionType} entries do not change. Clients and servers
+ * of different versions serialize expressions by ordinal. Thus, add new entries only at the end of
+ * the enum, and do not move or remove a released entry.
+ */
+public class ExpressionTypeOrdinalTest {
+
+ /** The entries that existed before vector support, in their released order. */
+ private static final String[] RELEASED = { "ReverseFunction", "RowKey", "KeyValue",
+ "LiteralValue", "RoundFunction", "FloorFunction", "CeilFunction", "RoundDateExpression",
+ "FloorDateExpression", "CeilDateExpression", "RoundTimestampExpression",
+ "CeilTimestampExpression", "RoundDecimalExpression", "FloorDecimalExpression",
+ "CeilDecimalExpression", "TruncFunction", "ToDateFunction", "ToCharFunction",
+ "ToNumberFunction", "CoerceFunction", "SubstrFunction", "AndExpression", "OrExpression",
+ "ComparisonExpression", "CountAggregateFunction", "SumAggregateFunction",
+ "MinAggregateFunction", "MaxAggregateFunction", "StringBasedLikeExpression", "NotExpression",
+ "CaseExpression", "InListExpression", "IsNullExpression", "LongSubtractExpression",
+ "DateSubtractExpression", "DecimalSubtractExpression", "LongAddExpression",
+ "DecimalAddExpression", "DateAddExpression", "LongMultiplyExpression",
+ "DecimalMultiplyExpression", "LongDivideExpression", "DecimalDivideExpression",
+ "CoalesceFunction", "StringBasedRegexpReplaceFunction", "SQLTypeNameFunction",
+ "StringBasedRegexpSubstrFunction", "StringConcatExpression", "LengthFunction", "LTrimFunction",
+ "RTrimFunction", "UpperFunction", "LowerFunction", "TrimFunction",
+ "DistinctCountAggregateFunction", "PercentileContAggregateFunction",
+ "PercentRankAggregateFunction", "StddevPopFunction", "StddevSampFunction",
+ "PercentileDiscAggregateFunction", "DoubleAddExpression", "DoubleSubtractExpression",
+ "DoubleMultiplyExpression", "DoubleDivideExpression", "RowValueConstructorExpression",
+ "MD5Function", "SQLTableTypeFunction", "IndexStateName", "InvertFunction",
+ "ProjectedColumnExpression", "TimestampAddExpression", "TimestampSubtractExpression",
+ "ArrayIndexFunction", "ArrayLengthFunction", "ArrayConstructorExpression",
+ "SQLViewTypeFunction", "ExternalSqlTypeIdFunction", "ConvertTimezoneFunction", "DecodeFunction",
+ "TimezoneOffsetFunction", "EncodeFunction", "LpadFunction", "NthValueFunction",
+ "FirstValueFunction", "LastValueFunction", "ArrayAnyComparisonExpression",
+ "ArrayAllComparisonExpression", "InlineArrayElemRefExpression", "SQLIndexTypeFunction",
+ "ModulusExpression", "DistinctValueAggregateFunction", "StringBasedRegexpSplitFunction",
+ "RandomFunction", "ToTimeFunction", "ToTimestampFunction", "ByteBasedLikeExpression",
+ "ByteBasedRegexpReplaceFunction", "ByteBasedRegexpSubstrFunction",
+ "ByteBasedRegexpSplitFunction", "LikeExpression", "RegexpReplaceFunction",
+ "RegexpSubstrFunction", "RegexpSplitFunction", "SignFunction", "YearFunction", "MonthFunction",
+ "SecondFunction", "WeekFunction", "HourFunction", "NowFunction", "InstrFunction",
+ "MinuteFunction", "DayOfMonthFunction", "ArrayAppendFunction", "UDFExpression",
+ "ArrayPrependFunction", "SqrtFunction", "AbsFunction", "CbrtFunction", "LnFunction",
+ "LogFunction", "ExpFunction", "PowerFunction", "ArrayConcatFunction", "ArrayFillFunction",
+ "ArrayToStringFunction", "StringToArrayFunction", "GetByteFunction", "SetByteFunction",
+ "GetBitFunction", "SetBitFunction", "OctetLengthFunction", "RoundWeekExpression",
+ "RoundMonthExpression", "RoundYearExpression", "FloorWeekExpression", "FloorMonthExpression",
+ "FloorYearExpression", "CeilWeekExpression", "CeilMonthExpression", "CeilYearExpression",
+ "DayOfWeekFunction", "DayOfYearFunction", "DefaultValueExpression", "ArrayColumnExpression",
+ "FirstValuesFunction", "LastValuesFunction", "DistinctCountHyperLogLogAggregateFunction",
+ "CollationKeyFunction", "ArrayRemoveFunction", "TransactionProviderNameFunction",
+ "MathPIFunction", "SinFunction", "CosFunction", "TanFunction", "RowKeyBytesStringFunction",
+ "PhoenixRowTimestampFunction", "JsonValueFunction", "JsonQueryFunction", "JsonExistsFunction",
+ "JsonModifyFunction", "BsonConditionExpressionFunction", "BsonUpdateExpressionFunction",
+ "BsonValueFunction", "BsonValueTypeFunction", "PartitionIdFunction", "DecodeBinaryFunction",
+ "EncodeBinaryFunction", "DecodeViewIdFunction", "SubBinaryFunction", "ScanStartKeyFunction",
+ "ScanEndKeyFunction", "TotalSegmentsFunction", "RowSizeFunction", "RawRowSizeFunction",
+ "RegexpLikeFunction", "ByteBasedRegexpLikeFunction", "StringBasedRegexpLikeFunction", };
+
+ @Test
+ public void testReleasedOrdinalsAreUnchanged() {
+ for (int i = 0; i < RELEASED.length; i++) {
+ assertEquals("ordinal of " + RELEASED[i], i, ExpressionType.valueOf(RELEASED[i]).ordinal());
+ }
+ }
+
+ @Test
+ public void testVectorExpressionsAreAppended() {
+ String[] vectorTypes = { "L2DistanceFunction", "L2DistanceSquaredFunction",
+ "CosineDistanceFunction", "InnerProductDistanceFunction", "BsonVectorValueFunction" };
+ for (String name : vectorTypes) {
+ assertTrue(name + " must follow every released entry",
+ ExpressionType.valueOf(name).ordinal() >= RELEASED.length);
+ }
+ }
+}
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/schema/VectorIndexTypeTest.java b/phoenix-core/src/test/java/org/apache/phoenix/schema/VectorIndexTypeTest.java
index 79abe73d50..235b1834ed 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/schema/VectorIndexTypeTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/schema/VectorIndexTypeTest.java
@@ -67,6 +67,8 @@ public class VectorIndexTypeTest {
fail("Expected IllegalArgumentException for unknown serialized value 99");
} catch (IllegalArgumentException e) {
assertTrue("Exception message should mention invalid value", e.getMessage().contains("99"));
+ assertTrue("Exception message should direct an upgrade",
+ e.getMessage().toLowerCase().contains("upgrade"));
}
}
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/schema/types/VectorDataTypeTest.java b/phoenix-core/src/test/java/org/apache/phoenix/schema/types/VectorDataTypeTest.java
index e1cd9ed490..b32aea37c9 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/schema/types/VectorDataTypeTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/schema/types/VectorDataTypeTest.java
@@ -55,6 +55,24 @@ public class VectorDataTypeTest {
assertTrue(factory.getTypes().contains(PVectorDouble.INSTANCE));
}
+ /**
+ * The SQL type ID and the serialization ordinal identify a type across versions. Thus, the values
+ * of each vector type must not change, and no other registered type can use them.
+ */
+ @Test
+ public void testTypeIdentifiersAreUniqueAndStable() {
+ assertEquals(51, PVectorFloat.INSTANCE.ordinal());
+ assertEquals(52, PVectorDouble.INSTANCE.ordinal());
+ java.util.Set<Integer> sqlTypes = new java.util.HashSet<>();
+ java.util.Set<Integer> ordinals = new java.util.HashSet<>();
+ for (PDataType<?> type : PDataTypeFactory.getInstance().getTypes()) {
+ assertTrue("duplicate sql type " + type.getSqlType() + " for " + type,
+ sqlTypes.add(type.getSqlType()));
+ assertTrue("duplicate ordinal " + type.ordinal() + " for " + type,
+ ordinals.add(type.ordinal()));
+ }
+ }
+
@Test
public void testSqlTypeNameResolution() {
PDataTypeFactory factory = PDataTypeFactory.getInstance();