This is an automated email from the ASF dual-hosted git repository. jianglongtao 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 2ca5873ea78 [Oracle SQL] Support accessing object for Oracle SQL (#27890) 2ca5873ea78 is described below commit 2ca5873ea781ed3ff3da22ec3b69e7d2ad53d5f9 Author: Liao Lanyu <1435078...@qq.com> AuthorDate: Thu Aug 3 17:56:24 2023 +0800 [Oracle SQL] Support accessing object for Oracle SQL (#27890) * support access object and fix sonar issues * test * test * check style * check style --- .../src/main/antlr4/imports/oracle/BaseRule.g4 | 2 +- .../visitor/statement/OracleStatementVisitor.java | 3 ++ .../statement/type/OracleDDLStatementVisitor.java | 48 +++++++++-------- .../statement/type/OracleDMLStatementVisitor.java | 62 +++++++++++++++------- .../common/segment/dml/column/ColumnSegment.java | 16 +++++- .../segment/dml/expr/XmlPiFunctionSegment.java | 3 +- .../segment/projection/ProjectionAssert.java | 6 ++- .../parser/src/main/resources/case/dml/select.xml | 14 +++++ .../main/resources/sql/supported/dml/select.xml | 1 + 9 files changed, 110 insertions(+), 45 deletions(-) diff --git a/parser/sql/dialect/oracle/src/main/antlr4/imports/oracle/BaseRule.g4 b/parser/sql/dialect/oracle/src/main/antlr4/imports/oracle/BaseRule.g4 index fc3a8b8db6e..ff70e0f8ef8 100644 --- a/parser/sql/dialect/oracle/src/main/antlr4/imports/oracle/BaseRule.g4 +++ b/parser/sql/dialect/oracle/src/main/antlr4/imports/oracle/BaseRule.g4 @@ -352,7 +352,7 @@ materializedViewName ; columnName - : (owner DOT_)? name + : (owner DOT_)? name (DOT_ nestedItem)* ; objectName diff --git a/parser/sql/dialect/oracle/src/main/java/org/apache/shardingsphere/sql/parser/oracle/visitor/statement/OracleStatementVisitor.java b/parser/sql/dialect/oracle/src/main/java/org/apache/shardingsphere/sql/parser/oracle/visitor/statement/OracleStatementVisitor.java index 009256afeac..81d7c0e6c33 100644 --- a/parser/sql/dialect/oracle/src/main/java/org/apache/shardingsphere/sql/parser/oracle/visitor/statement/OracleStatementVisitor.java +++ b/parser/sql/dialect/oracle/src/main/java/org/apache/shardingsphere/sql/parser/oracle/visitor/statement/OracleStatementVisitor.java @@ -275,6 +275,9 @@ public abstract class OracleStatementVisitor extends OracleStatementBaseVisitor< if (null != owner) { result.setOwner(new OwnerSegment(owner.getStart().getStartIndex(), owner.getStop().getStopIndex(), (IdentifierValue) visit(owner.identifier()))); } + if (null != ctx.nestedItem() && !ctx.nestedItem().isEmpty()) { + result.setNestedObjectAttributes(ctx.nestedItem().stream().map(item -> (IdentifierValue) visit(item.identifier())).collect(Collectors.toList())); + } return result; } diff --git a/parser/sql/dialect/oracle/src/main/java/org/apache/shardingsphere/sql/parser/oracle/visitor/statement/type/OracleDDLStatementVisitor.java b/parser/sql/dialect/oracle/src/main/java/org/apache/shardingsphere/sql/parser/oracle/visitor/statement/type/OracleDDLStatementVisitor.java index e9070e11924..ba553528c15 100644 --- a/parser/sql/dialect/oracle/src/main/java/org/apache/shardingsphere/sql/parser/oracle/visitor/statement/type/OracleDDLStatementVisitor.java +++ b/parser/sql/dialect/oracle/src/main/java/org/apache/shardingsphere/sql/parser/oracle/visitor/statement/type/OracleDDLStatementVisitor.java @@ -318,28 +318,32 @@ public final class OracleDDLStatementVisitor extends OracleStatementVisitor impl typeSegment, objectSubTypeDefContext.dataTypeDefinition().stream().map(definition -> (TypeDefinitionSegment) visit(definition)).collect(Collectors.toList())); } else { - if (null != ctx.plsqlTypeSource().objectBaseTypeDef().objectTypeDef()) { - OracleStatementParser.ObjectTypeDefContext objectTypeDefContext = ctx.plsqlTypeSource().objectBaseTypeDef().objectTypeDef(); - return new OracleCreateObjectTypeStatement(isReplace, isEditionable, null == objectTypeDefContext.finalClause() || null == objectTypeDefContext.finalClause().NOT(), - null == objectTypeDefContext.instantiableClause() || null == objectTypeDefContext.instantiableClause().NOT(), - null == objectTypeDefContext.persistableClause() || null == objectTypeDefContext.persistableClause().NOT(), - typeSegment, objectTypeDefContext.dataTypeDefinition().stream().map(definition -> (TypeDefinitionSegment) visit(definition)).collect(Collectors.toList())); - } else if (null != ctx.plsqlTypeSource().objectBaseTypeDef().varrayTypeSpec()) { - OracleStatementParser.VarrayTypeSpecContext varrayTypeSpecContext = ctx.plsqlTypeSource().objectBaseTypeDef().varrayTypeSpec(); - return new OracleCreateVarrayTypeStatement(isReplace, isEditionable, - null == varrayTypeSpecContext.INTEGER_() ? -1 : Integer.parseInt(varrayTypeSpecContext.INTEGER_().getText()), - null != varrayTypeSpecContext.typeSpec().NULL(), - null == varrayTypeSpecContext.typeSpec().persistableClause() || null == varrayTypeSpecContext.typeSpec().persistableClause().NOT(), - typeSegment, - (DataTypeSegment) visit(varrayTypeSpecContext.typeSpec().dataType())); - } else { - OracleStatementParser.NestedTableTypeSpecContext nestedTableTypeSpecContext = ctx.plsqlTypeSource().objectBaseTypeDef().nestedTableTypeSpec(); - return new OracleCreateNestedTableTypeStatement(isReplace, isEditionable, - null != nestedTableTypeSpecContext.typeSpec().NULL(), - null == nestedTableTypeSpecContext.typeSpec().persistableClause() || null == nestedTableTypeSpecContext.typeSpec().persistableClause().NOT(), - typeSegment, - (DataTypeSegment) visit(nestedTableTypeSpecContext.typeSpec().dataType())); - } + return visitCreateTypeObjectBaseTypeDef(ctx.plsqlTypeSource().objectBaseTypeDef(), isReplace, isEditionable, typeSegment); + } + } + + private ASTNode visitCreateTypeObjectBaseTypeDef(final OracleStatementParser.ObjectBaseTypeDefContext ctx, final boolean isReplace, final boolean isEditionable, final TypeSegment typeSegment) { + if (null != ctx.objectTypeDef()) { + OracleStatementParser.ObjectTypeDefContext objectTypeDefContext = ctx.objectTypeDef(); + return new OracleCreateObjectTypeStatement(isReplace, isEditionable, null == objectTypeDefContext.finalClause() || null == objectTypeDefContext.finalClause().NOT(), + null == objectTypeDefContext.instantiableClause() || null == objectTypeDefContext.instantiableClause().NOT(), + null == objectTypeDefContext.persistableClause() || null == objectTypeDefContext.persistableClause().NOT(), + typeSegment, objectTypeDefContext.dataTypeDefinition().stream().map(definition -> (TypeDefinitionSegment) visit(definition)).collect(Collectors.toList())); + } else if (null != ctx.varrayTypeSpec()) { + OracleStatementParser.VarrayTypeSpecContext varrayTypeSpecContext = ctx.varrayTypeSpec(); + return new OracleCreateVarrayTypeStatement(isReplace, isEditionable, + null == varrayTypeSpecContext.INTEGER_() ? -1 : Integer.parseInt(varrayTypeSpecContext.INTEGER_().getText()), + null != varrayTypeSpecContext.typeSpec().NULL(), + null == varrayTypeSpecContext.typeSpec().persistableClause() || null == varrayTypeSpecContext.typeSpec().persistableClause().NOT(), + typeSegment, + (DataTypeSegment) visit(varrayTypeSpecContext.typeSpec().dataType())); + } else { + OracleStatementParser.NestedTableTypeSpecContext nestedTableTypeSpecContext = ctx.nestedTableTypeSpec(); + return new OracleCreateNestedTableTypeStatement(isReplace, isEditionable, + null != nestedTableTypeSpecContext.typeSpec().NULL(), + null == nestedTableTypeSpecContext.typeSpec().persistableClause() || null == nestedTableTypeSpecContext.typeSpec().persistableClause().NOT(), + typeSegment, + (DataTypeSegment) visit(nestedTableTypeSpecContext.typeSpec().dataType())); } } diff --git a/parser/sql/dialect/oracle/src/main/java/org/apache/shardingsphere/sql/parser/oracle/visitor/statement/type/OracleDMLStatementVisitor.java b/parser/sql/dialect/oracle/src/main/java/org/apache/shardingsphere/sql/parser/oracle/visitor/statement/type/OracleDMLStatementVisitor.java index bc77c1a7766..eba9e7230ac 100644 --- a/parser/sql/dialect/oracle/src/main/java/org/apache/shardingsphere/sql/parser/oracle/visitor/statement/type/OracleDMLStatementVisitor.java +++ b/parser/sql/dialect/oracle/src/main/java/org/apache/shardingsphere/sql/parser/oracle/visitor/statement/type/OracleDMLStatementVisitor.java @@ -116,6 +116,8 @@ import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr.Datetime import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr.ExpressionSegment; import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr.FunctionSegment; import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr.MultisetExpression; +import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr.complex.ComplexExpressionSegment; +import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr.simple.SimpleExpressionSegment; import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.item.IntervalExpressionProjection; import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr.XmlPiFunctionSegment; import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr.XmlQueryAndExistsFunctionSegment; @@ -689,6 +691,23 @@ public final class OracleDMLStatementVisitor extends OracleStatementVisitor impl private ASTNode createProjection(final SelectProjectionExprClauseContext ctx) { AliasSegment alias = null == ctx.alias() ? null : (AliasSegment) visit(ctx.alias()); ASTNode projection = visit(ctx.expr()); + if (projection instanceof AliasAvailable) { + ((AliasAvailable) projection).setAlias(alias); + return projection; + } + if (projection instanceof ComplexExpressionSegment) { + return createProjectionForComplexExpressionSegment(projection, alias); + } + if (projection instanceof SimpleExpressionSegment) { + return createProjectionForSimpleExpressionSegment(projection, alias, ctx); + } + if (projection instanceof ExpressionSegment) { + return createProjectionForExpressionSegment(projection, alias); + } + throw new UnsupportedOperationException("Unhandled case"); + } + + private ASTNode createProjectionForComplexExpressionSegment(final ASTNode projection, final AliasSegment alias) { if (projection instanceof FunctionSegment) { FunctionSegment segment = (FunctionSegment) projection; ExpressionProjectionSegment result = new ExpressionProjectionSegment(segment.getStartIndex(), segment.getStopIndex(), segment.getText(), segment); @@ -701,12 +720,13 @@ public final class OracleDMLStatementVisitor extends OracleStatementVisitor impl result.setAlias(alias); return result; } - // FIXME :For DISTINCT() - if (projection instanceof ColumnSegment) { - ColumnProjectionSegment result = new ColumnProjectionSegment((ColumnSegment) projection); - result.setAlias(alias); - return result; + if (projection instanceof XmlQueryAndExistsFunctionSegment || projection instanceof XmlPiFunctionSegment || projection instanceof XmlSerializeFunctionSegment) { + return projection; } + throw new UnsupportedOperationException("Unsupported Complex Expression"); + } + + private ASTNode createProjectionForSimpleExpressionSegment(final ASTNode projection, final AliasSegment alias, final SelectProjectionExprClauseContext ctx) { if (projection instanceof SubqueryExpressionSegment) { SubqueryExpressionSegment subqueryExpressionSegment = (SubqueryExpressionSegment) projection; String text = ctx.start.getInputStream().getText(new Interval(subqueryExpressionSegment.getStartIndex(), subqueryExpressionSegment.getStopIndex())); @@ -714,6 +734,24 @@ public final class OracleDMLStatementVisitor extends OracleStatementVisitor impl result.setAlias(alias); return result; } + if (projection instanceof LiteralExpressionSegment) { + LiteralExpressionSegment column = (LiteralExpressionSegment) projection; + ExpressionProjectionSegment result = null == alias + ? new ExpressionProjectionSegment(column.getStartIndex(), column.getStopIndex(), String.valueOf(column.getLiterals()), column) + : new ExpressionProjectionSegment(column.getStartIndex(), ctx.alias().stop.getStopIndex(), String.valueOf(column.getLiterals()), column); + result.setAlias(alias); + return result; + } + throw new UnsupportedOperationException("Unsupported Simple Expression"); + } + + private ASTNode createProjectionForExpressionSegment(final ASTNode projection, final AliasSegment alias) { + // FIXME :For DISTINCT() + if (projection instanceof ColumnSegment) { + ColumnProjectionSegment result = new ColumnProjectionSegment((ColumnSegment) projection); + result.setAlias(alias); + return result; + } if (projection instanceof BinaryOperationExpression) { BinaryOperationExpression binaryExpression = (BinaryOperationExpression) projection; int startIndex = binaryExpression.getStartIndex(); @@ -737,13 +775,6 @@ public final class OracleDMLStatementVisitor extends OracleStatementVisitor impl : new DatetimeProjectionSegment(datetimeExpression.getStartIndex(), datetimeExpression.getStopIndex(), datetimeExpression.getLeft(), datetimeExpression.getRight(), datetimeExpression.getText()); } - if (projection instanceof XmlQueryAndExistsFunctionSegment || projection instanceof XmlPiFunctionSegment || projection instanceof XmlSerializeFunctionSegment) { - return projection; - } - if (projection instanceof AliasAvailable) { - ((AliasAvailable) projection).setAlias(alias); - return projection; - } if (projection instanceof IntervalExpressionProjection) { IntervalExpressionProjection intervalExpressionProjection = (IntervalExpressionProjection) projection; IntervalExpressionProjection result = new IntervalExpressionProjection(intervalExpressionProjection.getStartIndex(), intervalExpressionProjection.getStopIndex(), @@ -755,12 +786,7 @@ public final class OracleDMLStatementVisitor extends OracleStatementVisitor impl } return result; } - LiteralExpressionSegment column = (LiteralExpressionSegment) projection; - ExpressionProjectionSegment result = null == alias - ? new ExpressionProjectionSegment(column.getStartIndex(), column.getStopIndex(), String.valueOf(column.getLiterals()), column) - : new ExpressionProjectionSegment(column.getStartIndex(), ctx.alias().stop.getStopIndex(), String.valueOf(column.getLiterals()), column); - result.setAlias(alias); - return result; + throw new UnsupportedOperationException("Unsupported Expression"); } @Override diff --git a/parser/sql/statement/src/main/java/org/apache/shardingsphere/sql/parser/sql/common/segment/dml/column/ColumnSegment.java b/parser/sql/statement/src/main/java/org/apache/shardingsphere/sql/parser/sql/common/segment/dml/column/ColumnSegment.java index 026d5e8e91f..478f1f4d6ca 100644 --- a/parser/sql/statement/src/main/java/org/apache/shardingsphere/sql/parser/sql/common/segment/dml/column/ColumnSegment.java +++ b/parser/sql/statement/src/main/java/org/apache/shardingsphere/sql/parser/sql/common/segment/dml/column/ColumnSegment.java @@ -25,7 +25,9 @@ import org.apache.shardingsphere.sql.parser.sql.common.segment.generic.OwnerAvai import org.apache.shardingsphere.sql.parser.sql.common.segment.generic.OwnerSegment; import org.apache.shardingsphere.sql.parser.sql.common.value.identifier.IdentifierValue; +import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; /** * Column segment. @@ -41,6 +43,8 @@ public final class ColumnSegment implements ExpressionSegment, OwnerAvailable { private final IdentifierValue identifier; + private List<IdentifierValue> nestedObjectAttributes; + private OwnerSegment owner; private IdentifierValue originalDatabase; @@ -58,7 +62,11 @@ public final class ColumnSegment implements ExpressionSegment, OwnerAvailable { * @return qualified name with quote characters */ public String getQualifiedName() { - return null == owner ? identifier.getValueWithQuoteCharacters() : String.join(".", owner.getIdentifier().getValueWithQuoteCharacters(), identifier.getValueWithQuoteCharacters()); + String column = identifier.getValueWithQuoteCharacters(); + if (null != nestedObjectAttributes && !nestedObjectAttributes.isEmpty()) { + column = String.join(".", column, nestedObjectAttributes.stream().map(IdentifierValue::getValueWithQuoteCharacters).collect(Collectors.joining("."))); + } + return null == owner ? column : String.join(".", owner.getIdentifier().getValueWithQuoteCharacters(), column); } /** @@ -67,7 +75,11 @@ public final class ColumnSegment implements ExpressionSegment, OwnerAvailable { * @return expression */ public String getExpression() { - return null == owner ? identifier.getValue() : String.join(".", owner.getIdentifier().getValue(), identifier.getValue()); + String column = identifier.getValue(); + if (null != nestedObjectAttributes && !nestedObjectAttributes.isEmpty()) { + column = String.join(".", column, nestedObjectAttributes.stream().map(IdentifierValue::getValue).collect(Collectors.joining("."))); + } + return null == owner ? column : String.join(".", owner.getIdentifier().getValue(), column); } @Override diff --git a/parser/sql/statement/src/main/java/org/apache/shardingsphere/sql/parser/sql/common/segment/dml/expr/XmlPiFunctionSegment.java b/parser/sql/statement/src/main/java/org/apache/shardingsphere/sql/parser/sql/common/segment/dml/expr/XmlPiFunctionSegment.java index c8d18122dfd..e56f77064dc 100644 --- a/parser/sql/statement/src/main/java/org/apache/shardingsphere/sql/parser/sql/common/segment/dml/expr/XmlPiFunctionSegment.java +++ b/parser/sql/statement/src/main/java/org/apache/shardingsphere/sql/parser/sql/common/segment/dml/expr/XmlPiFunctionSegment.java @@ -19,6 +19,7 @@ package org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr; import lombok.Getter; import lombok.Setter; +import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr.complex.ComplexExpressionSegment; import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.item.ProjectionSegment; /** @@ -26,7 +27,7 @@ import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.item.Projecti */ @Getter @Setter -public final class XmlPiFunctionSegment implements ExpressionSegment, ProjectionSegment { +public final class XmlPiFunctionSegment implements ComplexExpressionSegment, ProjectionSegment { private final int startIndex; diff --git a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/segment/projection/ProjectionAssert.java b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/segment/projection/ProjectionAssert.java index a7dcf3eb0ab..0e0d29775f9 100644 --- a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/segment/projection/ProjectionAssert.java +++ b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/internal/asserts/segment/projection/ProjectionAssert.java @@ -130,8 +130,12 @@ public final class ProjectionAssert { } private static void assertColumnProjection(final SQLCaseAssertContext assertContext, final ColumnProjectionSegment actual, final ExpectedColumnProjection expected) { - IdentifierValueAssert.assertIs(assertContext, actual.getColumn().getIdentifier(), expected, "Column projection"); assertThat(assertContext.getText("Column projection alias assertion error: "), actual.getAliasName().orElse(null), is(expected.getAlias())); + if (null != actual.getColumn().getNestedObjectAttributes()) { + assertThat(assertContext.getText("Nested Object attributes assertion error: "), actual.getColumn().getExpression(), is(expected.getName())); + } else { + IdentifierValueAssert.assertIs(assertContext, actual.getColumn().getIdentifier(), expected, "Column projection"); + } if (null == expected.getOwner()) { assertFalse(actual.getColumn().getOwner().isPresent(), assertContext.getText("Actual owner should not exist.")); } else { diff --git a/test/it/parser/src/main/resources/case/dml/select.xml b/test/it/parser/src/main/resources/case/dml/select.xml index 7561d8e8783..b60855fae9f 100644 --- a/test/it/parser/src/main/resources/case/dml/select.xml +++ b/test/it/parser/src/main/resources/case/dml/select.xml @@ -6476,4 +6476,18 @@ <simple-table name="DUAL" start-index="355" stop-index="358" literal-start-index="355" literal-stop-index="358" /> </from> </select> + + <select sql-case-id="select_with_nested_object"> + <projections start-index="7" stop-index="42" literal-start-index="7" literal-stop-index="42"> + <column-projection name="o.item.line_item_id" start-index="7" stop-index="25" > + <owner name="o" start-index="7" stop-index="7" /> + </column-projection>> + <column-projection name="o.item.quantity" start-index="28" stop-index="42" > + <owner name="o" start-index="28" stop-index="28" /> + </column-projection>> + </projections> + <from> + <simple-table name="short_orders" alias="o" start-index="49" stop-index="62" literal-start-index="49" literal-stop-index="62" /> + </from> + </select> </sql-parser-test-cases> diff --git a/test/it/parser/src/main/resources/sql/supported/dml/select.xml b/test/it/parser/src/main/resources/sql/supported/dml/select.xml index e62f82d64d5..27e0dfc222d 100644 --- a/test/it/parser/src/main/resources/sql/supported/dml/select.xml +++ b/test/it/parser/src/main/resources/sql/supported/dml/select.xml @@ -204,4 +204,5 @@ <sql-case id="select_with_rownumber_function" value="SELECT department_id, first_name, last_name, salary FROM (SELECT department_id, first_name, last_name, salary, ROW_NUMBER() OVER (PARTITION BY department_id ORDER BY salary desc) rn FROM employees) WHERE rn = 3 ORDER BY department_id, salary DESC, last_name" db-types="Oracle" /> <sql-case id="select_with_xml_functions" value="SELECT INSERTCHILDXML(warehouse_spec, '/Warehouse/Building', 'Owner', XMLType('<Owner>LesserCo</Owner>')), SYS_DBURIGEN(employee_id, email), INSERTCHILDXMLAFTER(warehouse_spec, '/Warehouse/Building','Owner[2]', XMLType('<Owner>ThirdOwner</Owner>')), INSERTCHILDXMLBEFORE(warehouse_spec, '/Warehouse/Building','Owner[2]', XMLType('<Owner>ThirdOwner</Owner>')), INSERTXMLAFTER(warehouse_spec,'/Warehouse/Building/Owner[1]', [...] <sql-case id="select_with_more_xml_functions" value="SELECT XMLCOMMENT('OrderAnalysisComp imported, reconfigured, disassembled'), XMLCONCAT(XMLELEMENT('First', e.first_name), XMLELEMENT('Last', e.last_name)) AS 'Result', XMLPATCH(XMLTYPE('xml')), XMLDIFF(XMLTYPE('xml'),XMLTYPE('xml2')), XMLELEMENT('Emp', XMLATTRIBUTES(e.employee_id, e.last_name)), XMLSEQUENCE(EXTRACT(warehouse_spec, '/Warehouse/*')) FROM DUAL" db-types="Oracle" /> + <sql-case id="select_with_nested_object" value="SELECT o.item.line_item_id, o.item.quantity FROM short_orders o;" db-types="Oracle" /> </sql-cases>