Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support parsing SQL Server SELECT name, sql #29627

Merged
merged 13 commits into from
Jan 5, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ unreservedWord
| DATA_RETENTION | TEMPORAL_HISTORY_RETENTION | EDITION | MIXED_PAGE_ALLOCATION | DISABLED | ALLOWED | HADR | MULTI_USER | RESTRICTED_USER | SINGLE_USER | OFFLINE | EMERGENCY | SUSPEND | DATE_CORRELATION_OPTIMIZATION
| ELASTIC_POOL | SERVICE_OBJECTIVE | DATABASE_NAME | ALLOW_CONNECTIONS | GEO | NAMED | DATEFIRST | BACKUP_STORAGE_REDUNDANCY | FORCE_FAILOVER_ALLOW_DATA_LOSS | SECONDARY | FAILOVER | DEFAULT_FULLTEXT_LANGUAGE
| DEFAULT_LANGUAGE | INLINE | NESTED_TRIGGERS | TRANSFORM_NOISE_WORDS | TWO_DIGIT_YEAR_CUTOFF | PERSISTENT_LOG_BUFFER | DIRECTORY_NAME | DATEFORMAT | DELAYED_DURABILITY | TRANSFER | SCHEMA | PASSWORD | AUTHORIZATION
| MEMBER | SEARCH | TEXT | SECOND | PRECISION | VIEWS
| MEMBER | SEARCH | TEXT | SECOND | PRECISION | VIEWS | PROVIDER
;

databaseName
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ grammar DMLStatement;
import BaseRule;

insert
: withClause? INSERT top? INTO? tableName (AS? alias)? (insertDefaultValue | insertValuesClause | insertSelectClause)
: withClause? INSERT top? INTO? tableName (AS? alias)? (insertDefaultValue | insertValuesClause | insertSelectClause | insertExecClause)
;

insertDefaultValue
Expand All @@ -35,6 +35,14 @@ insertSelectClause
: columnNames? outputClause? select
;

insertExecClause
: columnNames? exec
;

exec
: (EXEC | EXECUTE) procedureName (expr (COMMA_ expr)*)?
;

update
: withClause? UPDATE top? tableReferences setAssignmentsClause whereClause? (OPTION queryHint)?
;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -692,3 +692,6 @@ WITHOUT
: W I T H O U T
;

APPLY
: A P P L Y
;
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@
import org.apache.shardingsphere.sql.parser.autogen.SQLServerStatementParser.ViewNameContext;
import org.apache.shardingsphere.sql.parser.autogen.SQLServerStatementParser.WhereClauseContext;
import org.apache.shardingsphere.sql.parser.autogen.SQLServerStatementParser.WithClauseContext;
import org.apache.shardingsphere.sql.parser.autogen.SQLServerStatementParser.InsertExecClauseContext;
import org.apache.shardingsphere.sql.parser.autogen.SQLServerStatementParser.ExecContext;
import org.apache.shardingsphere.sql.parser.autogen.SQLServerStatementParser.ProcedureNameContext;
import org.apache.shardingsphere.sql.parser.sql.common.enums.AggregationType;
import org.apache.shardingsphere.sql.parser.sql.common.enums.JoinType;
import org.apache.shardingsphere.sql.parser.sql.common.enums.OrderDirection;
Expand All @@ -115,6 +118,7 @@
import org.apache.shardingsphere.sql.parser.sql.common.segment.ddl.constraint.ConstraintSegment;
import org.apache.shardingsphere.sql.parser.sql.common.segment.ddl.index.IndexNameSegment;
import org.apache.shardingsphere.sql.parser.sql.common.segment.ddl.index.IndexSegment;
import org.apache.shardingsphere.sql.parser.sql.common.segment.ddl.routine.FunctionNameSegment;
import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.assignment.ColumnAssignmentSegment;
import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.assignment.InsertValuesSegment;
import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.assignment.SetAssignmentSegment;
Expand Down Expand Up @@ -181,6 +185,7 @@
import org.apache.shardingsphere.sql.parser.sql.common.value.literal.impl.OtherLiteralValue;
import org.apache.shardingsphere.sql.parser.sql.common.value.literal.impl.StringLiteralValue;
import org.apache.shardingsphere.sql.parser.sql.common.value.parametermarker.ParameterMarkerValue;
import org.apache.shardingsphere.sql.parser.sql.dialect.segment.sqlserver.exec.ExecSegment;
import org.apache.shardingsphere.sql.parser.sql.dialect.statement.sqlserver.ddl.SQLServerCreateTableStatement;
import org.apache.shardingsphere.sql.parser.sql.dialect.statement.sqlserver.dml.SQLServerDeleteStatement;
import org.apache.shardingsphere.sql.parser.sql.dialect.statement.sqlserver.dml.SQLServerInsertStatement;
Expand Down Expand Up @@ -914,6 +919,8 @@ public ASTNode visitInsert(final InsertContext ctx) {
result = (SQLServerInsertStatement) visit(ctx.insertDefaultValue());
} else if (null != ctx.insertValuesClause()) {
result = (SQLServerInsertStatement) visit(ctx.insertValuesClause());
} else if (null != ctx.insertExecClause()) {
result = (SQLServerInsertStatement) visit(ctx.insertExecClause());
} else {
result = (SQLServerInsertStatement) visit(ctx.insertSelectClause());
}
Expand All @@ -935,6 +942,39 @@ public ASTNode visitInsertDefaultValue(final InsertDefaultValueContext ctx) {
return result;
}

@Override
public ASTNode visitInsertExecClause(final InsertExecClauseContext ctx) {
SQLServerInsertStatement result = new SQLServerInsertStatement();
result.setInsertColumns(createInsertColumns(ctx.columnNames(), ctx.start.getStartIndex()));
result.setExecSegment((ExecSegment) visit(ctx.exec()));
return result;
}

@Override
public ASTNode visitExec(final ExecContext ctx) {
ExecSegment result = new ExecSegment(ctx.getStart().getStartIndex(), ctx.getStop().getStopIndex());
if (null != ctx.procedureName()) {
result.setProcedureName((FunctionNameSegment) visitProcedureName(ctx.procedureName()));
}
if (null != ctx.expr()) {
Collection<ExpressionSegment> items = new LinkedList<>();
for (ExprContext each : ctx.expr()) {
items.add((ExpressionSegment) visit(each));
}
result.getExpressionSegments().addAll(items);
}
return result;
}

@Override
public ASTNode visitProcedureName(final ProcedureNameContext ctx) {
FunctionNameSegment result = new FunctionNameSegment(ctx.name().start.getStartIndex(), ctx.name().stop.getStopIndex(), (IdentifierValue) visit(ctx.name()));
if (null != ctx.owner()) {
result.setOwner(new OwnerSegment(ctx.owner().start.getStartIndex(), ctx.owner().stop.getStopIndex(), (IdentifierValue) visit(ctx.owner())));
}
return result;
}

@Override
public ASTNode visitOutputClause(final OutputClauseContext ctx) {
OutputSegment result = new OutputSegment(ctx.start.getStartIndex(), ctx.stop.getStopIndex());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,5 @@
*/
public enum JoinType {

INNER, FULL, CROSS, LEFT, RIGHT, COMMA
INNER, FULL, CROSS, LEFT, RIGHT, COMMA, APPLY
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import org.apache.shardingsphere.sql.parser.sql.dialect.segment.oracle.table.MultiTableConditionalIntoSegment;
import org.apache.shardingsphere.sql.parser.sql.dialect.segment.oracle.table.MultiTableInsertIntoSegment;
import org.apache.shardingsphere.sql.parser.sql.dialect.segment.oracle.table.MultiTableInsertType;
import org.apache.shardingsphere.sql.parser.sql.dialect.segment.sqlserver.exec.ExecSegment;
import org.apache.shardingsphere.sql.parser.sql.dialect.statement.mysql.dml.MySQLInsertStatement;
import org.apache.shardingsphere.sql.parser.sql.dialect.statement.opengauss.dml.OpenGaussInsertStatement;
import org.apache.shardingsphere.sql.parser.sql.dialect.statement.oracle.dml.OracleInsertStatement;
Expand Down Expand Up @@ -295,4 +296,29 @@ public static void setWhereSegment(final InsertStatement insertStatement, final
((OracleInsertStatement) insertStatement).setWhere(whereSegment);
}
}

/**
* Get execute segment.
*
* @param insertStatement insert statement
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add new line after javadoc and param.

* @return execute segment
*/
public static Optional<ExecSegment> getExecSegment(final InsertStatement insertStatement) {
if (insertStatement instanceof SQLServerInsertStatement) {
return ((SQLServerInsertStatement) insertStatement).getExecSegment();
}
return Optional.empty();
}

/**
* Set execute segment.
*
* @param insertStatement insert statement
* @param execSegment execute segment
*/
public static void setExecSegment(final InsertStatement insertStatement, final ExecSegment execSegment) {
if (insertStatement instanceof SQLServerInsertStatement) {
((SQLServerInsertStatement) insertStatement).setExecSegment(execSegment);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* 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.sql.parser.sql.dialect.segment.sqlserver.exec;

import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import org.apache.shardingsphere.sql.parser.sql.common.segment.SQLSegment;
import org.apache.shardingsphere.sql.parser.sql.common.segment.ddl.routine.FunctionNameSegment;
import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr.ExpressionSegment;

import java.util.Collection;
import java.util.LinkedList;

/**
* Execute segment.
*/
@RequiredArgsConstructor
@Getter
public final class ExecSegment implements SQLSegment {

private final int startIndex;

private final int stopIndex;

@Setter
private FunctionNameSegment procedureName;

private final Collection<ExpressionSegment> expressionSegments = new LinkedList<>();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* 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.sql.parser.sql.dialect.statement.sqlserver.dml;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr.ExpressionSegment;
import org.apache.shardingsphere.sql.parser.sql.common.statement.dml.CallStatement;
import org.apache.shardingsphere.sql.parser.sql.dialect.statement.sqlserver.SQLServerStatement;

import java.util.List;

/**
* SQLServer call statement.
*/
@AllArgsConstructor
@NoArgsConstructor
@Getter
public final class SQLServerCallStatement extends CallStatement implements SQLServerStatement {

private String procedureName;

private List<ExpressionSegment> parameters;
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.apache.shardingsphere.sql.parser.sql.common.segment.generic.OutputSegment;
import org.apache.shardingsphere.sql.parser.sql.common.segment.generic.WithSegment;
import org.apache.shardingsphere.sql.parser.sql.common.statement.dml.InsertStatement;
import org.apache.shardingsphere.sql.parser.sql.dialect.segment.sqlserver.exec.ExecSegment;
import org.apache.shardingsphere.sql.parser.sql.dialect.statement.sqlserver.SQLServerStatement;

import java.util.Optional;
Expand All @@ -35,6 +36,8 @@ public final class SQLServerInsertStatement extends InsertStatement implements S

private OutputSegment outputSegment;

private ExecSegment execSegment;

/**
* Get with segment.
*
Expand All @@ -52,4 +55,13 @@ public Optional<WithSegment> getWithSegment() {
public Optional<OutputSegment> getOutputSegment() {
return Optional.ofNullable(outputSegment);
}

/**
* Get execute segment.
*
* @return execute segment.
*/
public Optional<ExecSegment> getExecSegment() {
return Optional.ofNullable(execSegment);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* 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.test.it.sql.parser.internal.asserts.segment.insert;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.expr.ExpressionSegment;
import org.apache.shardingsphere.sql.parser.sql.dialect.segment.sqlserver.exec.ExecSegment;
import org.apache.shardingsphere.test.it.sql.parser.internal.asserts.SQLCaseAssertContext;
import org.apache.shardingsphere.test.it.sql.parser.internal.asserts.segment.SQLSegmentAssert;
import org.apache.shardingsphere.test.it.sql.parser.internal.asserts.segment.expression.ExpressionAssert;
import org.apache.shardingsphere.test.it.sql.parser.internal.asserts.segment.owner.OwnerAssert;
import org.apache.shardingsphere.test.it.sql.parser.internal.cases.parser.jaxb.segment.impl.exec.ExpectedExecClause;
import org.hamcrest.CoreMatchers;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

/**
* Insert execute clause assert.
**/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class InsertExecClauseAssert {

/**
* Assert actual execute segment is correct with expected execute clause.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add new line after java doc.

*
* @param assertContext assert context
* @param actual actual execute segment
* @param expected expected execute clause
*/
public static void assertIs(final SQLCaseAssertContext assertContext, final ExecSegment actual, final ExpectedExecClause expected) {
assertThat(assertContext.getText("exec procedure name assertion error: "), actual.getProcedureName().getIdentifier().getValue(), CoreMatchers.is(expected.getName()));
if (null == expected.getOwner()) {
assertFalse(actual.getProcedureName().getOwner().isPresent(), assertContext.getText("Actual owner should not exist."));
} else {
assertTrue(actual.getProcedureName().getOwner().isPresent(), assertContext.getText("Actual owner should exist."));
OwnerAssert.assertIs(assertContext, actual.getProcedureName().getOwner().get(), expected.getOwner());
}
if (null == expected.getParameters()) {
assertThat(assertContext.getText("exec procedure parameters assertion error: "), actual.getExpressionSegments().size(), CoreMatchers.is(expected.getParameters().size()));
} else {
int count = 0;
for (ExpressionSegment expressionSegment : actual.getExpressionSegments()) {
ExpressionAssert.assertExpression(assertContext, expressionSegment, expected.getParameters().get(count));
count++;
}
}
SQLSegmentAssert.assertIs(assertContext, actual, expected);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@
import org.apache.shardingsphere.sql.parser.sql.dialect.segment.oracle.table.MultiTableConditionalIntoSegment;
import org.apache.shardingsphere.sql.parser.sql.dialect.segment.oracle.table.MultiTableInsertIntoSegment;
import org.apache.shardingsphere.sql.parser.sql.dialect.segment.oracle.table.MultiTableInsertType;
import org.apache.shardingsphere.sql.parser.sql.dialect.segment.sqlserver.exec.ExecSegment;
import org.apache.shardingsphere.test.it.sql.parser.internal.asserts.SQLCaseAssertContext;
import org.apache.shardingsphere.test.it.sql.parser.internal.asserts.segment.insert.InsertExecClauseAssert;
import org.apache.shardingsphere.test.it.sql.parser.internal.asserts.segment.insert.InsertColumnsClauseAssert;
import org.apache.shardingsphere.test.it.sql.parser.internal.asserts.segment.insert.InsertValuesClauseAssert;
import org.apache.shardingsphere.test.it.sql.parser.internal.asserts.segment.insert.MultiTableConditionalIntoClauseAssert;
Expand Down Expand Up @@ -77,6 +79,7 @@ public static void assertIs(final SQLCaseAssertContext assertContext, final Inse
assertMultiTableInsertIntoClause(assertContext, actual, expected);
assertMultiTableConditionalIntoClause(assertContext, actual, expected);
assertReturningClause(assertContext, actual, expected);
assertInsertExecClause(assertContext, actual, expected);
}

private static void assertTable(final SQLCaseAssertContext assertContext, final InsertStatement actual, final InsertStatementTestCase expected) {
Expand Down Expand Up @@ -195,4 +198,14 @@ private static void assertReturningClause(final SQLCaseAssertContext assertConte
ReturningClauseAssert.assertIs(assertContext, returningSegment.get(), expected.getReturningClause());
}
}

private static void assertInsertExecClause(final SQLCaseAssertContext assertContext, final InsertStatement actual, final InsertStatementTestCase expected) {
Optional<ExecSegment> execSegment = InsertStatementHandler.getExecSegment(actual);
if (null == expected.getExecClause()) {
assertFalse(execSegment.isPresent(), assertContext.getText("Actual exec segment should not exist."));
} else {
assertTrue(execSegment.isPresent(), assertContext.getText("Actual exec segment should exist."));
InsertExecClauseAssert.assertIs(assertContext, execSegment.get(), expected.getExecClause());
}
}
}
Loading