You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@shardingsphere.apache.org by hu...@apache.org on 2026/03/10 06:07:59 UTC
(shardingsphere) branch master updated: Support select order by index bind metadata (#38386)
This is an automated email from the ASF dual-hosted git repository.
hucong 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 76a95be97cb Support select order by index bind metadata (#38386)
76a95be97cb is described below
commit 76a95be97cb095fb2c41a39fbdaae87e6b39d3d4
Author: Zhengqiang Duan <du...@apache.org>
AuthorDate: Tue Mar 10 14:07:53 2026 +0800
Support select order by index bind metadata (#38386)
---
RELEASE-NOTES.md | 1 +
.../order/item/IndexOrderByItemSegmentBinder.java | 64 +++++++++++++++++++++
.../dml/order/item/OrderByItemSegmentBinder.java | 4 ++
.../item/IndexOrderByItemSegmentBinderTest.java | 65 ++++++++++++++++++++++
.../dml/order/item/IndexOrderByItemSegment.java | 16 ++++++
.../binder/src/test/resources/cases/dml/select.xml | 62 ++++++++++++++++++++-
.../binder/src/test/resources/sqls/dml/select.xml | 1 +
.../asserts/segment/orderby/OrderByItemAssert.java | 4 ++
.../item/impl/ExpectedIndexOrderByItem.java | 5 ++
9 files changed, 221 insertions(+), 1 deletion(-)
diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md
index c50fb017f3c..d783dcb0830 100644
--- a/RELEASE-NOTES.md
+++ b/RELEASE-NOTES.md
@@ -14,6 +14,7 @@
1. SQL Parser: Support parsing MySQL SELECT sql - [#38233](https://github.com/apache/shardingsphere/pull/38233)
1. SQL Parser: Support parsing MySQL CREATE sql - [#38237](https://github.com/apache/shardingsphere/pull/38237)
1. SQL Parser: Support mysql subquery table projection alias visit to sql statement - [#38320](https://github.com/apache/shardingsphere/pull/38320)
+1. SQL Binder: Support select order by index bind metadata - [#38386](https://github.com/apache/shardingsphere/pull/38386)
1. JDBC: Support setMaxRows and getMaxRows method in jdbc when not execute sql - [#38337](https://github.com/apache/shardingsphere/pull/38337)
1. Sharding: Fix HASH_MOD routing mismatch for same negative numeric values across numeric Java types with compatibility switch `normalize-numeric-int-range` - [#38327](https://github.com/apache/shardingsphere/pull/38327)
diff --git a/infra/binder/core/src/main/java/org/apache/shardingsphere/infra/binder/engine/segment/dml/order/item/IndexOrderByItemSegmentBinder.java b/infra/binder/core/src/main/java/org/apache/shardingsphere/infra/binder/engine/segment/dml/order/item/IndexOrderByItemSegmentBinder.java
new file mode 100644
index 00000000000..fcba507f686
--- /dev/null
+++ b/infra/binder/core/src/main/java/org/apache/shardingsphere/infra/binder/engine/segment/dml/order/item/IndexOrderByItemSegmentBinder.java
@@ -0,0 +1,64 @@
+/*
+ * 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.shardingsphere.infra.binder.engine.segment.dml.order.item;
+
+import com.cedarsoftware.util.CaseInsensitiveMap.CaseInsensitiveString;
+import com.google.common.collect.Multimap;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import org.apache.shardingsphere.infra.binder.engine.segment.dml.from.context.TableSegmentBinderContext;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.column.ColumnSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.ColumnProjectionSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.ProjectionSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.order.item.IndexOrderByItemSegment;
+
+import java.util.Optional;
+
+/**
+ * Index order by item segment binder.
+ */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public final class IndexOrderByItemSegmentBinder {
+
+ /**
+ * Bind index order by item segment.
+ *
+ * @param segment index order by item segment
+ * @param tableBinderContexts table binder contexts
+ * @return bound index order by item segment
+ */
+ public static IndexOrderByItemSegment bind(final IndexOrderByItemSegment segment, final Multimap<CaseInsensitiveString, TableSegmentBinderContext> tableBinderContexts) {
+ IndexOrderByItemSegment result = new IndexOrderByItemSegment(segment.getStartIndex(),
+ segment.getStopIndex(), segment.getColumnIndex(), segment.getOrderDirection(), segment.getNullsOrderType().orElse(null));
+ findBoundColumn(segment.getColumnIndex(), tableBinderContexts).ifPresent(result::setBoundColumn);
+ return result;
+ }
+
+ private static Optional<ColumnSegment> findBoundColumn(final int columnIndex, final Multimap<CaseInsensitiveString, TableSegmentBinderContext> tableBinderContexts) {
+ for (TableSegmentBinderContext each : tableBinderContexts.values()) {
+ int index = 1;
+ for (ProjectionSegment projectionSegment : each.getProjectionSegments()) {
+ if (index == columnIndex) {
+ return projectionSegment instanceof ColumnProjectionSegment ? Optional.of(((ColumnProjectionSegment) projectionSegment).getColumn()) : Optional.empty();
+ }
+ index++;
+ }
+ }
+ return Optional.empty();
+ }
+}
diff --git a/infra/binder/core/src/main/java/org/apache/shardingsphere/infra/binder/engine/segment/dml/order/item/OrderByItemSegmentBinder.java b/infra/binder/core/src/main/java/org/apache/shardingsphere/infra/binder/engine/segment/dml/order/item/OrderByItemSegmentBinder.java
index c967cc415af..72d30e906c0 100644
--- a/infra/binder/core/src/main/java/org/apache/shardingsphere/infra/binder/engine/segment/dml/order/item/OrderByItemSegmentBinder.java
+++ b/infra/binder/core/src/main/java/org/apache/shardingsphere/infra/binder/engine/segment/dml/order/item/OrderByItemSegmentBinder.java
@@ -26,6 +26,7 @@ import org.apache.shardingsphere.infra.binder.engine.segment.dml.from.context.Ta
import org.apache.shardingsphere.infra.binder.engine.statement.SQLStatementBinderContext;
import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.order.item.ColumnOrderByItemSegment;
import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.order.item.ExpressionOrderByItemSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.order.item.IndexOrderByItemSegment;
import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.order.item.OrderByItemSegment;
/**
@@ -53,6 +54,9 @@ public final class OrderByItemSegmentBinder {
if (segment instanceof ExpressionOrderByItemSegment) {
return ExpressionOrderByItemSegmentBinder.bind((ExpressionOrderByItemSegment) segment, binderContext, tableBinderContexts, outerTableBinderContexts, segmentType);
}
+ if (segment instanceof IndexOrderByItemSegment) {
+ return IndexOrderByItemSegmentBinder.bind((IndexOrderByItemSegment) segment, tableBinderContexts);
+ }
return segment;
}
}
diff --git a/infra/binder/core/src/test/java/org/apache/shardingsphere/infra/binder/engine/segment/dml/order/item/IndexOrderByItemSegmentBinderTest.java b/infra/binder/core/src/test/java/org/apache/shardingsphere/infra/binder/engine/segment/dml/order/item/IndexOrderByItemSegmentBinderTest.java
new file mode 100644
index 00000000000..f0e93d2755d
--- /dev/null
+++ b/infra/binder/core/src/test/java/org/apache/shardingsphere/infra/binder/engine/segment/dml/order/item/IndexOrderByItemSegmentBinderTest.java
@@ -0,0 +1,65 @@
+/*
+ * 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.shardingsphere.infra.binder.engine.segment.dml.order.item;
+
+import com.cedarsoftware.util.CaseInsensitiveMap.CaseInsensitiveString;
+import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.Multimap;
+import org.apache.shardingsphere.database.connector.core.metadata.database.enums.NullsOrderType;
+import org.apache.shardingsphere.infra.binder.engine.segment.dml.from.context.TableSegmentBinderContext;
+import org.apache.shardingsphere.infra.binder.engine.segment.dml.from.context.type.SimpleTableSegmentBinderContext;
+import org.apache.shardingsphere.sql.parser.statement.core.enums.OrderDirection;
+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.item.ColumnProjectionSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.order.item.IndexOrderByItemSegment;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.generic.bound.ColumnSegmentBoundInfo;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.generic.bound.TableSegmentBoundInfo;
+import org.apache.shardingsphere.sql.parser.statement.core.value.identifier.IdentifierValue;
+import org.junit.jupiter.api.Test;
+
+import java.util.Collections;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class IndexOrderByItemSegmentBinderTest {
+
+ @Test
+ void assertBind() {
+ ColumnSegment columnSegment = new ColumnSegment(0, 0, new IdentifierValue("user_name"));
+ columnSegment.setColumnBoundInfo(new ColumnSegmentBoundInfo(new TableSegmentBoundInfo(new IdentifierValue("foo_db"), new IdentifierValue("")),
+ new IdentifierValue("t_user"), new IdentifierValue("user_name"), TableSourceType.PHYSICAL_TABLE));
+ Multimap<CaseInsensitiveString, TableSegmentBinderContext> tableBinderContexts = LinkedHashMultimap.create();
+ tableBinderContexts.put(CaseInsensitiveString.of(""), new SimpleTableSegmentBinderContext(Collections.singleton(new ColumnProjectionSegment(columnSegment)), TableSourceType.MIXED_TABLE));
+ IndexOrderByItemSegment actual = IndexOrderByItemSegmentBinder.bind(new IndexOrderByItemSegment(6, 6, 1, OrderDirection.ASC, NullsOrderType.FIRST), tableBinderContexts);
+ assertTrue(actual.getBoundColumn().isPresent());
+ assertThat(actual.getBoundColumn().get().getColumnBoundInfo().getOriginalTable().getValue(), is("t_user"));
+ assertThat(actual.getBoundColumn().get().getColumnBoundInfo().getOriginalColumn().getValue(), is("user_name"));
+ }
+
+ @Test
+ void assertBindWithoutBoundColumn() {
+ Multimap<CaseInsensitiveString, TableSegmentBinderContext> tableBinderContexts = LinkedHashMultimap.create();
+ tableBinderContexts.put(CaseInsensitiveString.of(""), new SimpleTableSegmentBinderContext(Collections.emptyList(), TableSourceType.MIXED_TABLE));
+ IndexOrderByItemSegment actual = IndexOrderByItemSegmentBinder.bind(new IndexOrderByItemSegment(6, 6, 1, OrderDirection.ASC, NullsOrderType.FIRST), tableBinderContexts);
+ assertFalse(actual.getBoundColumn().isPresent());
+ }
+}
diff --git a/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/segment/dml/order/item/IndexOrderByItemSegment.java b/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/segment/dml/order/item/IndexOrderByItemSegment.java
index 24ef2c1f36f..c202fd259e9 100644
--- a/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/segment/dml/order/item/IndexOrderByItemSegment.java
+++ b/parser/sql/statement/core/src/main/java/org/apache/shardingsphere/sql/parser/statement/core/segment/dml/order/item/IndexOrderByItemSegment.java
@@ -18,8 +18,12 @@
package org.apache.shardingsphere.sql.parser.statement.core.segment.dml.order.item;
import lombok.Getter;
+import lombok.Setter;
import org.apache.shardingsphere.database.connector.core.metadata.database.enums.NullsOrderType;
import org.apache.shardingsphere.sql.parser.statement.core.enums.OrderDirection;
+import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.column.ColumnSegment;
+
+import java.util.Optional;
/**
* Order by item segment for index.
@@ -29,8 +33,20 @@ public final class IndexOrderByItemSegment extends OrderByItemSegment {
private final int columnIndex;
+ @Setter
+ private ColumnSegment boundColumn;
+
public IndexOrderByItemSegment(final int startIndex, final int stopIndex, final int columnIndex, final OrderDirection orderDirection, final NullsOrderType nullsOrderType) {
super(startIndex, stopIndex, orderDirection, nullsOrderType);
this.columnIndex = columnIndex;
}
+
+ /**
+ * Get bound column.
+ *
+ * @return bound column
+ */
+ public Optional<ColumnSegment> getBoundColumn() {
+ return Optional.ofNullable(boundColumn);
+ }
}
diff --git a/test/it/binder/src/test/resources/cases/dml/select.xml b/test/it/binder/src/test/resources/cases/dml/select.xml
index 0f18d928dfa..c3f50b33ab0 100644
--- a/test/it/binder/src/test/resources/cases/dml/select.xml
+++ b/test/it/binder/src/test/resources/cases/dml/select.xml
@@ -669,7 +669,67 @@
</simple-table>
</from>
</select>
-
+
+ <select sql-case-id="select_with_order_by_index_bound">
+ <projections start-index="7" stop-index="31">
+ <column-projection name="user_id" start-index="7" stop-index="13">
+ <column-bound>
+ <original-database name="foo_db_1" />
+ <original-schema name="foo_db_1" />
+ <original-table name="t_user" />
+ <original-column name="user_id" start-delimiter="`" end-delimiter="`" />
+ <table-source-type name="PHYSICAL_TABLE"/>
+ </column-bound>
+ </column-projection>
+ <column-projection name="user_name" start-index="16" stop-index="24">
+ <column-bound>
+ <original-database name="foo_db_1" />
+ <original-schema name="foo_db_1" />
+ <original-table name="t_user" />
+ <original-column name="user_name" start-delimiter="`" end-delimiter="`" />
+ <table-source-type name="PHYSICAL_TABLE"/>
+ </column-bound>
+ </column-projection>
+ <column-projection name="email" start-index="27" stop-index="31">
+ <column-bound>
+ <original-database name="foo_db_1" />
+ <original-schema name="foo_db_1" />
+ <original-table name="t_user" />
+ <original-column name="email" start-delimiter="`" end-delimiter="`" />
+ <table-source-type name="PHYSICAL_TABLE"/>
+ </column-bound>
+ </column-projection>
+ </projections>
+ <from start-index="33" stop-index="43">
+ <simple-table name="t_user" start-index="38" stop-index="43">
+ <table-bound>
+ <original-database name="foo_db_1" />
+ <original-schema name="foo_db_1" />
+ </table-bound>
+ </simple-table>
+ </from>
+ <order-by>
+ <index-item index="2" order-direction="ASC" start-index="54" stop-index="54">
+ <column-bound>
+ <original-database name="foo_db_1" />
+ <original-schema name="foo_db_1" />
+ <original-table name="t_user" />
+ <original-column name="user_name" start-delimiter="`" end-delimiter="`" />
+ <table-source-type name="PHYSICAL_TABLE"/>
+ </column-bound>
+ </index-item>
+ <index-item index="1" order-direction="DESC" start-index="61" stop-index="61">
+ <column-bound>
+ <original-database name="foo_db_1" />
+ <original-schema name="foo_db_1" />
+ <original-table name="t_user" />
+ <original-column name="user_id" start-delimiter="`" end-delimiter="`" />
+ <table-source-type name="PHYSICAL_TABLE"/>
+ </column-bound>
+ </index-item>
+ </order-by>
+ </select>
+
<select sql-case-id="select_with_multiple_ctes_left_join_and_order_by">
<with start-index="0" stop-index="142">
<common-table-expression name="user_orders" start-index="5" stop-index="88">
diff --git a/test/it/binder/src/test/resources/sqls/dml/select.xml b/test/it/binder/src/test/resources/sqls/dml/select.xml
index c0d19ed2534..1d5c6f549f8 100644
--- a/test/it/binder/src/test/resources/sqls/dml/select.xml
+++ b/test/it/binder/src/test/resources/sqls/dml/select.xml
@@ -23,6 +23,7 @@
<sql-case id="select_with_clause_with_multiple_cte_definitions" value="WITH cte1(status, user_id) AS (SELECT status, user_id FROM t_order a), cte2(user_id, item_id) AS (SELECT user_id, item_id FROM t_order_item b) SELECT status, user_id, item_id FROM cte1 INNER JOIN cte2 ON cte1.user_id = cte2.user_id" db-types="MySQL" />
<sql-case id="select_with_with_clause_with_column_definition" value="WITH t_order_tmp (col1, col2, col3, col4, col5, col6) AS (SELECT * FROM t_order o) SELECT col1 FROM t_order_tmp" db-types="MySQL"/>
<sql-case id="select_with_multiple_ctes_left_join_and_order_by" value="WITH user_orders AS (SELECT user_id, COUNT(*) AS order_cnt FROM t_order GROUP BY user_id), user_info AS (SELECT user_id, user_name FROM t_user) SELECT ui.user_name, uo.order_cnt FROM user_info ui LEFT JOIN user_orders uo ON ui.user_id = uo.user_id ORDER BY ui.user_id, ui.user_name" db-types="MySQL"/>
+ <sql-case id="select_with_order_by_index_bound" value="SELECT user_id, user_name, email FROM t_user ORDER BY 2 ASC, 1 DESC" db-types="MySQL"/>
<sql-case id="select_with_current_select_projection_reference" value="SELECT order_id AS orderId, (SELECT orderId) AS tempOrderId FROM t_order" db-types="MySQL"/>
<sql-case id="select_with_subquery_table_alias_columns_and_order_by" value="SELECT * FROM (SELECT user_id, user_name FROM t_user WHERE user_id <= 10 UNION SELECT merchant_id, merchant_name FROM t_merchant WHERE merchant_id <= 5) AS combined(id, name) ORDER BY id" db-types="MySQL"/>
<sql-case id="select_with_outer_join_operator" value="SELECT o.order_id FROM t_order o, t_order_item i WHERE o.order_id = i.order_id(+)" db-types="Oracle"/>
diff --git a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/segment/orderby/OrderByItemAssert.java b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/segment/orderby/OrderByItemAssert.java
index c9e2e0ad9e1..8b4279bc8e9 100644
--- a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/segment/orderby/OrderByItemAssert.java
+++ b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/segment/orderby/OrderByItemAssert.java
@@ -120,6 +120,10 @@ public final class OrderByItemAssert {
private static void assertIndexOrderByItem(final SQLCaseAssertContext assertContext,
final IndexOrderByItemSegment actual, final ExpectedIndexOrderByItem expected, final String type) {
assertThat(assertContext.getText(String.format("%s item index assertion error: ", type)), actual.getColumnIndex(), is(expected.getIndex()));
+ if (null != expected.getColumnBound()) {
+ assertTrue(actual.getBoundColumn().isPresent(), assertContext.getText("Actual bound column should exist."));
+ ColumnBoundAssert.assertIs(assertContext, actual.getBoundColumn().get().getColumnBoundInfo(), expected.getColumnBound());
+ }
SQLSegmentAssert.assertIs(assertContext, actual, expected);
}
diff --git a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/segment/impl/orderby/item/impl/ExpectedIndexOrderByItem.java b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/segment/impl/orderby/item/impl/ExpectedIndexOrderByItem.java
index a5183a107d7..ee9e9a1f1c2 100644
--- a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/segment/impl/orderby/item/impl/ExpectedIndexOrderByItem.java
+++ b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/cases/parser/jaxb/segment/impl/orderby/item/impl/ExpectedIndexOrderByItem.java
@@ -19,9 +19,11 @@ package org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.
import lombok.Getter;
import lombok.Setter;
+import org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.bound.ExpectedColumnBoundInfo;
import org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.orderby.item.ExpectedOrderByItem;
import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
/**
* Expected index order by item.
@@ -32,4 +34,7 @@ public final class ExpectedIndexOrderByItem extends ExpectedOrderByItem {
@XmlAttribute
private int index;
+
+ @XmlElement(name = "column-bound")
+ private ExpectedColumnBoundInfo columnBound;
}