This is an automated email from the ASF dual-hosted git repository.
iamhucong pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shardingsphere.git
The following commit(s) were added to refs/heads/master by this push:
new d2c170fa34c Refactor Oracle literal projection binding (#39280)
d2c170fa34c is described below
commit d2c170fa34c94d09d1ebf5d2e75c474c5e301284
Author: Zhengqiang Duan <[email protected]>
AuthorDate: Thu Jul 30 19:24:53 2026 +0800
Refactor Oracle literal projection binding (#39280)
* Refactor Oracle literal projection binding
* Update RELEASE-NOTES.md
---
RELEASE-NOTES.md | 1 +
.../segment/util/SubqueryTableBindUtils.java | 31 +++++++++++++++---
.../statement/dml/SelectStatementBinderTest.java | 37 ++++++++++++++++++++++
.../OracleProjectionIdentifierExtractor.java | 8 +++++
.../OracleProjectionIdentifierExtractorTest.java | 6 ++++
5 files changed, 79 insertions(+), 4 deletions(-)
diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md
index d48b21b7238..10184f9e1f3 100644
--- a/RELEASE-NOTES.md
+++ b/RELEASE-NOTES.md
@@ -50,6 +50,7 @@
1. Sharding: Fix generated actual index names exceeding database identifier
length limits while preserving legacy generated index name compatibility -
[#38449](https://github.com/apache/shardingsphere/pull/38449)
1. Sharding: Fix AUTO_INTERVAL sharding failure under JVM default locales that
use comma decimal separators -
[#38806](https://github.com/apache/shardingsphere/pull/38806)
1. Sharding: Compute the Snowflake key generator epoch in UTC instead of the
JVM default timezone -
[#38932](https://github.com/apache/shardingsphere/pull/38932)
+1. SQL Federation: Fix SQL Federation pagination binding for long LIMIT
parameters - [#39237](https://github.com/apache/shardingsphere/pull/39237)
### Enhancements
diff --git
a/infra/binder/core/src/main/java/org/apache/shardingsphere/infra/binder/engine/segment/util/SubqueryTableBindUtils.java
b/infra/binder/core/src/main/java/org/apache/shardingsphere/infra/binder/engine/segment/util/SubqueryTableBindUtils.java
index 61949c8e0eb..c03f5f6b448 100644
---
a/infra/binder/core/src/main/java/org/apache/shardingsphere/infra/binder/engine/segment/util/SubqueryTableBindUtils.java
+++
b/infra/binder/core/src/main/java/org/apache/shardingsphere/infra/binder/engine/segment/util/SubqueryTableBindUtils.java
@@ -70,9 +70,9 @@ public final class SubqueryTableBindUtils {
} else if (each instanceof ShorthandProjectionSegment) {
result.addAll(createSubqueryProjections(((ShorthandProjectionSegment)
each).getActualProjectionSegments(), subqueryTableName, databaseType,
tableSourceType));
} else if (each instanceof ExpressionProjectionSegment) {
-
result.add(createColumnProjection((ExpressionProjectionSegment) each,
subqueryTableName, databaseType));
+
result.add(createColumnProjection((ExpressionProjectionSegment) each,
subqueryTableName, databaseType, tableSourceType));
} else if (each instanceof AggregationProjectionSegment) {
-
result.add(createColumnProjection((AggregationProjectionSegment) each,
subqueryTableName, databaseType));
+
result.add(createColumnProjection((AggregationProjectionSegment) each,
subqueryTableName, databaseType, tableSourceType));
} else {
result.add(each);
}
@@ -94,11 +94,14 @@ public final class SubqueryTableBindUtils {
return result;
}
- private static ColumnProjectionSegment createColumnProjection(final
ExpressionSegment expressionSegment, final IdentifierValue subqueryTableName,
final DatabaseType databaseType) {
+ private static ColumnProjectionSegment createColumnProjection(final
ExpressionSegment expressionSegment, final IdentifierValue subqueryTableName,
final DatabaseType databaseType,
+ final
TableSourceType tableSourceType) {
ColumnSegment newColumnSegment = new ColumnSegment(0, 0,
new
IdentifierValue(getColumnNameFromExpression(expressionSegment, databaseType),
new
DatabaseTypeRegistry(databaseType).getDialectDatabaseMetaData().getQuoteCharacter()));
if (isReturnedColumnDerivedExpression(expressionSegment)) {
setColumnBoundInfo(newColumnSegment, expressionSegment);
+ } else {
+ setColumnBoundInfo(newColumnSegment, tableSourceType);
}
if (!Strings.isNullOrEmpty(subqueryTableName.getValue())) {
newColumnSegment.setOwner(new OwnerSegment(0, 0,
subqueryTableName));
@@ -112,10 +115,30 @@ public final class SubqueryTableBindUtils {
if (expressionSegment instanceof ExpressionProjectionSegment) {
return
isReturnedColumnDerivedExpression(((ExpressionProjectionSegment)
expressionSegment).getExpr());
}
+ if (expressionSegment instanceof CaseWhenExpression) {
+ return
isReturnedColumnDerivedCaseWhenExpression((CaseWhenExpression)
expressionSegment);
+ }
if (expressionSegment instanceof FunctionSegment) {
return
RETURNED_COLUMN_DERIVED_FUNCTION_NAMES.contains(((FunctionSegment)
expressionSegment).getFunctionName().toUpperCase());
}
- return expressionSegment instanceof CaseWhenExpression;
+ return false;
+ }
+
+ private static boolean isReturnedColumnDerivedCaseWhenExpression(final
CaseWhenExpression expressionSegment) {
+ if (!(expressionSegment.getElseExpr() instanceof ColumnSegment)) {
+ return false;
+ }
+ for (ExpressionSegment each : expressionSegment.getThenExprs()) {
+ if (!(each instanceof ColumnSegment)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static void setColumnBoundInfo(final ColumnSegment columnSegment,
final TableSourceType tableSourceType) {
+ TableSourceType columnTableSourceType = TableSourceType.MIXED_TABLE ==
tableSourceType ? TableSourceType.TEMPORARY_TABLE : tableSourceType;
+ columnSegment.setColumnBoundInfo(new ColumnSegmentBoundInfo(new
TableSegmentBoundInfo(null, null), new IdentifierValue(""),
columnSegment.getIdentifier(), columnTableSourceType));
}
private static void setColumnBoundInfo(final ColumnSegment columnSegment,
final ExpressionSegment expressionSegment) {
diff --git
a/infra/binder/core/src/test/java/org/apache/shardingsphere/infra/binder/engine/statement/dml/SelectStatementBinderTest.java
b/infra/binder/core/src/test/java/org/apache/shardingsphere/infra/binder/engine/statement/dml/SelectStatementBinderTest.java
index ba8046c52bb..1b134278ef6 100644
---
a/infra/binder/core/src/test/java/org/apache/shardingsphere/infra/binder/engine/statement/dml/SelectStatementBinderTest.java
+++
b/infra/binder/core/src/test/java/org/apache/shardingsphere/infra/binder/engine/statement/dml/SelectStatementBinderTest.java
@@ -35,6 +35,7 @@ import
org.apache.shardingsphere.sql.parser.statement.core.enums.TableSourceType
import
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.column.ColumnSegment;
import
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.combine.CombineSegment;
import
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.BinaryOperationExpression;
+import
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.CaseWhenExpression;
import
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.FunctionSegment;
import
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.ListExpression;
import
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.complex.CommonTableExpressionSegment;
@@ -59,6 +60,7 @@ import
org.apache.shardingsphere.sql.parser.statement.core.segment.generic.Windo
import
org.apache.shardingsphere.sql.parser.statement.core.segment.generic.WithSegment;
import
org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table.JoinTableSegment;
import
org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table.SimpleTableSegment;
+import
org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table.SubqueryTableSegment;
import
org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table.TableNameSegment;
import
org.apache.shardingsphere.sql.parser.statement.core.statement.type.dml.SelectStatement;
import
org.apache.shardingsphere.sql.parser.statement.core.value.identifier.IdentifierValue;
@@ -321,6 +323,41 @@ class SelectStatementBinderTest {
assertThat(((ColumnProjectionSegment)
actualProjectionSegments.get(1)).getColumn().getColumnBoundInfo().getTableSourceType(),
is(TableSourceType.TEMPORARY_TABLE));
}
+ @Test
+ void assertBindWithCaseWhenLiteralBranchExpressionInSubquery() {
+ ProjectionsSegment subqueryProjections = new ProjectionsSegment(0, 0);
+ subqueryProjections.getProjections().add(new
ColumnProjectionSegment(new ColumnSegment(0, 0, new
IdentifierValue("user_id"))));
+ ExpressionProjectionSegment caseWhenProjection = new
ExpressionProjectionSegment(0, 0, "CASE WHEN user_id <= 10 THEN '' ELSE
user_name END",
+ new CaseWhenExpression(0, 0, null, Collections.singleton(new
BinaryOperationExpression(0, 0, new ColumnSegment(0, 0, new
IdentifierValue("user_id")),
+ new LiteralExpressionSegment(0, 0, 10), "<=", "user_id
<= 10")),
+ Collections.singleton(new LiteralExpressionSegment(0,
0, "")), new ColumnSegment(0, 0, new IdentifierValue("user_name")),
+ "CASE WHEN user_id <= 10 THEN '' ELSE user_name END"));
+ caseWhenProjection.setAlias(new AliasSegment(0, 0, new
IdentifierValue("a")));
+ subqueryProjections.getProjections().add(caseWhenProjection);
+ SelectStatement subquerySelectStatement =
SelectStatement.builder().databaseType(databaseType).projections(subqueryProjections)
+ .from(new SimpleTableSegment(new TableNameSegment(0, 0, new
IdentifierValue("t_user")))).build();
+ SubqueryTableSegment subqueryTableSegment = new
SubqueryTableSegment(0, 0, new SubquerySegment(0, 0, subquerySelectStatement,
""));
+ subqueryTableSegment.setAlias(new AliasSegment(0, 0, new
IdentifierValue("text")));
+ ProjectionsSegment projections = new ProjectionsSegment(0, 0);
+ projections.getProjections().add(new ShorthandProjectionSegment(0, 0));
+ BinaryOperationExpression whereExpression =
+ new BinaryOperationExpression(0, 0, new ColumnSegment(0, 0,
new IdentifierValue("a")), new LiteralExpressionSegment(0, 0, ""), "=", "a =
''");
+ SelectStatement selectStatement =
SelectStatement.builder().databaseType(databaseType).projections(projections).from(subqueryTableSegment)
+ .where(new WhereSegment(0, 0, whereExpression)).build();
+ SelectStatement actual = new
SelectStatementBinder().bind(selectStatement,
+ new SQLStatementBinderContext(mockMetaData(), "foo_db", new
HintValueContext(), selectStatement));
+ ProjectionSegment actualProjection =
actual.getProjections().getProjections().iterator().next();
+ List<ProjectionSegment> actualProjectionSegments = new
ArrayList<>(((ShorthandProjectionSegment)
actualProjection).getActualProjectionSegments());
+ ColumnSegment actualCaseWhenProjection = ((ColumnProjectionSegment)
actualProjectionSegments.get(1)).getColumn();
+
assertThat(actualCaseWhenProjection.getColumnBoundInfo().getOriginalTable().getValue(),
is(""));
+
assertThat(actualCaseWhenProjection.getColumnBoundInfo().getOriginalColumn().getValue(),
is("a"));
+
assertThat(actualCaseWhenProjection.getColumnBoundInfo().getTableSourceType(),
is(TableSourceType.TEMPORARY_TABLE));
+ ColumnSegment actualWhereColumn = (ColumnSegment)
((BinaryOperationExpression) actual.getWhere().get().getExpr()).getLeft();
+
assertThat(actualWhereColumn.getColumnBoundInfo().getOriginalTable().getValue(),
is(""));
+
assertThat(actualWhereColumn.getColumnBoundInfo().getOriginalColumn().getValue(),
is("a"));
+
assertThat(actualWhereColumn.getColumnBoundInfo().getTableSourceType(),
is(TableSourceType.TEMPORARY_TABLE));
+ }
+
@Test
void assertBindWithAggregationDerivedExpression() {
ProjectionsSegment withProjections = new ProjectionsSegment(0, 0);
diff --git
a/infra/binder/dialect/oracle/src/main/java/org/apache/shardingsphere/infra/binder/oracle/OracleProjectionIdentifierExtractor.java
b/infra/binder/dialect/oracle/src/main/java/org/apache/shardingsphere/infra/binder/oracle/OracleProjectionIdentifierExtractor.java
index 46f33ac3a74..b7c591e8398 100644
---
a/infra/binder/dialect/oracle/src/main/java/org/apache/shardingsphere/infra/binder/oracle/OracleProjectionIdentifierExtractor.java
+++
b/infra/binder/dialect/oracle/src/main/java/org/apache/shardingsphere/infra/binder/oracle/OracleProjectionIdentifierExtractor.java
@@ -19,6 +19,8 @@ package org.apache.shardingsphere.infra.binder.oracle;
import
org.apache.shardingsphere.infra.binder.context.segment.select.projection.extractor.DialectProjectionIdentifierExtractor;
import
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.ExpressionSegment;
+import
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.simple.LiteralExpressionSegment;
+import
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.ExpressionProjectionSegment;
import
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.SubqueryProjectionSegment;
import
org.apache.shardingsphere.sql.parser.statement.core.value.identifier.IdentifierValue;
@@ -39,6 +41,12 @@ public final class OracleProjectionIdentifierExtractor
implements DialectProject
@Override
public String getColumnNameFromExpression(final ExpressionSegment
expressionSegment) {
+ if (expressionSegment instanceof ExpressionProjectionSegment &&
((ExpressionProjectionSegment) expressionSegment).getExpr() instanceof
LiteralExpressionSegment) {
+ Object literal = ((LiteralExpressionSegment)
((ExpressionProjectionSegment) expressionSegment).getExpr()).getLiterals();
+ if (literal instanceof String) {
+ return String.format("'%s'", literal.toString().replace("'",
"''"));
+ }
+ }
return expressionSegment.getText().replace(" ", "").toUpperCase();
}
diff --git
a/infra/binder/dialect/oracle/src/test/java/org/apache/shardingsphere/infra/binder/oracle/OracleProjectionIdentifierExtractorTest.java
b/infra/binder/dialect/oracle/src/test/java/org/apache/shardingsphere/infra/binder/oracle/OracleProjectionIdentifierExtractorTest.java
index e237061da79..510f8311718 100644
---
a/infra/binder/dialect/oracle/src/test/java/org/apache/shardingsphere/infra/binder/oracle/OracleProjectionIdentifierExtractorTest.java
+++
b/infra/binder/dialect/oracle/src/test/java/org/apache/shardingsphere/infra/binder/oracle/OracleProjectionIdentifierExtractorTest.java
@@ -22,6 +22,7 @@ import
org.apache.shardingsphere.database.connector.core.metadata.database.enums
import
org.apache.shardingsphere.database.connector.core.spi.DatabaseTypedSPILoader;
import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
+import
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.simple.LiteralExpressionSegment;
import
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.subquery.SubquerySegment;
import
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.ExpressionProjectionSegment;
import
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.SubqueryProjectionSegment;
@@ -53,6 +54,11 @@ class OracleProjectionIdentifierExtractorTest {
assertThat(extractor.getColumnNameFromExpression(new
ExpressionProjectionSegment(0, 0, "expression")), is("EXPRESSION"));
}
+ @Test
+ void assertGetColumnNameFromStringLiteralExpression() {
+ assertThat(extractor.getColumnNameFromExpression(new
ExpressionProjectionSegment(0, 7, "EXTMSG", new LiteralExpressionSegment(0, 7,
"EXTMSG"))), is("'EXTMSG'"));
+ }
+
@Test
void assertGetColumnNameFromSubquery() {
assertThat(extractor.getColumnNameFromSubquery(new
SubqueryProjectionSegment(mock(SubquerySegment.class), "text")), is("TEXT"));