Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/main/java/net/sf/jsqlparser/parser/feature/Feature.java
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,8 @@ public enum Feature {
* @see CreateFunctionalStatement
*/
functionalStatement,

alterFunction, alterProcedure, createOrAlterRoutine,
/**
* SQL block starting with "BEGIN" and ends with "END" statement is allowed
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
import net.sf.jsqlparser.statement.create.function.FunctionReturnType;
import net.sf.jsqlparser.statement.create.table.TableElement;

/**
* A base for the declaration of function like statements
Expand All @@ -23,6 +26,39 @@ public abstract class CreateFunctionalStatement implements Statement {
private String kind;
private boolean orReplace = false;

public enum Operation {
CREATE, ALTER, CREATE_OR_ALTER
}

private Operation operation = Operation.CREATE;
private FunctionReturnType returnType;
private List<String> routineBodyParts;

public Operation getOperation() {
return operation;
}

public void setOperation(Operation operation) {
this.operation = operation;
}

public FunctionReturnType getReturnType() {
return returnType;
}

public void setReturnType(FunctionReturnType returnType) {
this.returnType = returnType;
}

public List<String> getRoutineBodyParts() {
return routineBodyParts;
}

public void setRoutineBodyParts(List<String> parts) {
routineBodyParts = parts;
}


private List<String> functionDeclarationParts;

protected CreateFunctionalStatement(String kind) {
Expand All @@ -41,7 +77,9 @@ protected CreateFunctionalStatement(boolean orReplace, String kind,
}

/**
* @return the declaration parts after {@code CREATE FUNCTION|PROCEDURE}
* @return the declaration parts after {@code CREATE FUNCTION|PROCEDURE}. For a SQL Server
* function with a structured {@link #getReturnType()}, these are the name and parameter
* tokens before RETURNS; {@link #getRoutineBodyParts()} holds the remaining tokens.
*/
public List<String> getFunctionDeclarationParts() {
return functionDeclarationParts;
Expand All @@ -66,22 +104,43 @@ public void setOrReplace(boolean orReplace) {
* @return a whitespace appended String with the declaration parts with some minimal formatting.
*/
public String formatDeclaration() {
StringBuilder declaration = new StringBuilder();
int currIndex = 0;
while (currIndex < functionDeclarationParts.size()) {
String token = functionDeclarationParts.get(currIndex);
declaration.append(token);
// if the next token is a ; don't put a space
if (currIndex + 1 < functionDeclarationParts.size()) {
// peek ahead just to format nicely
String nextToken = functionDeclarationParts.get(currIndex + 1);
if (!nextToken.equals(";")) {
declaration.append(" ");
}
StringBuilder builder = new StringBuilder();
return appendDeclarationTo(builder, builder::append).toString();
}

private StringBuilder appendDeclarationTo(StringBuilder builder,
Consumer<TableElement> printer) {
appendTokens(builder, functionDeclarationParts);
if (returnType != null) {
builder.append(' ');
returnType.appendTo(builder, printer);
if (routineBodyParts != null && !routineBodyParts.isEmpty()) {
builder.append(' ');
appendTokens(builder, routineBodyParts);
}
currIndex++;
}
return declaration.toString();
return builder;
}

private static void appendTokens(StringBuilder builder, List<String> tokens) {
if (tokens == null) {
return;
}
for (int i = 0; i < tokens.size(); i++) {
if (i > 0 && !";".equals(tokens.get(i))) {
builder.append(' ');
}
builder.append(tokens.get(i));
}
}

public StringBuilder appendTo(StringBuilder builder, Consumer<TableElement> printer) {
builder.append(operation.name().replace('_', ' ')).append(' ');
if (orReplace && operation == Operation.CREATE) {
builder.append("OR REPLACE ");
}
builder.append(kind).append(' ');
return appendDeclarationTo(builder, printer);
}

@Override
Expand All @@ -91,9 +150,8 @@ public <T, S> T accept(StatementVisitor<T> statementVisitor, S context) {

@Override
public String toString() {
return "CREATE "
+ (orReplace ? "OR REPLACE " : "")
+ kind + " " + formatDeclaration();
StringBuilder builder = new StringBuilder();
return appendTo(builder, builder::append).toString();
}

public CreateFunctionalStatement withFunctionDeclarationParts(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,13 @@ public <S> T visit(AlterSequence alterSequence, S context) {

@Override
public <S> T visit(CreateFunctionalStatement createFunctionalStatement, S context) {
if (createFunctionalStatement.getReturnType() != null
&& createFunctionalStatement.getReturnType().getTableElements() != null) {
createFunctionalStatement.getReturnType().getTableElements()
.forEach(element -> TableDefinitionTraversal.visit(element,
expression -> expression.accept(expressionVisitor, context),
table -> table.accept(fromItemVisitor, context)));
}
return null;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*-
* #%L
* JSQLParser library
* %%
* Copyright (C) 2004 - 2026 JSQLParser
* %%
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
* #L%
*/
package net.sf.jsqlparser.statement.create.function;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import net.sf.jsqlparser.statement.create.table.ColDataType;
import net.sf.jsqlparser.statement.create.table.TableElement;

/** A SQL Server scalar, inline table or declared table return type. */
public class FunctionReturnType implements Serializable {
private ColDataType dataType;
private boolean table;
private String tableVariable;
private List<TableElement> tableElements;

public ColDataType getDataType() {
return dataType;
}

public void setDataType(ColDataType dataType) {
this.dataType = dataType;
}

public boolean isTable() {
return table;
}

public void setTable(boolean table) {
this.table = table;
}

public String getTableVariable() {
return tableVariable;
}

public void setTableVariable(String variable) {
this.tableVariable = variable;
}

/** Null for an inline return table; otherwise columns and constraints in source order. */
public List<TableElement> getTableElements() {
return tableElements;
}

public void setTableElements(List<TableElement> elements) {
this.tableElements = elements;
}

public <T extends TableElement> List<T> getTableElements(Class<T> type) {
List<T> result = new ArrayList<>();
if (tableElements != null) {
for (TableElement element : tableElements) {
if (type.isInstance(element)) {
result.add(type.cast(element));
}
}
}
return result;
}

public StringBuilder appendTo(StringBuilder builder, Consumer<TableElement> printer) {
builder.append("RETURNS ");
if (!table) {
return builder.append(dataType);
}
if (tableVariable != null) {
builder.append(tableVariable).append(' ');
}
builder.append("TABLE");
if (tableElements != null) {
builder.append(" (");
for (int i = 0; i < tableElements.size(); i++) {
if (i > 0) {
builder.append(", ");
}
printer.accept(tableElements.get(i));
}
builder.append(')');
}
return builder;
}

@Override
public String toString() {
StringBuilder builder = new StringBuilder();
return appendTo(builder, builder::append).toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,8 @@ public <S> StringBuilder visit(AlterSequence alterSequence, S context) {

@Override
public <S> StringBuilder visit(CreateFunctionalStatement createFunctionalStatement, S context) {
builder.append(createFunctionalStatement.toString());
createFunctionalStatement.appendTo(builder,
new TableElementDeParser(builder, expressionDeParser)::deParse);
return builder;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ public enum SqlServerVersion implements Version {
Feature.createTableFromSelect, // https://docs.microsoft.com/en-us/sql/t-sql/statements/create-function-transact-sql?view=sql-server-ver15
// https://docs.microsoft.com/en-us/sql/t-sql/statements/create-procedure-transact-sql?view=sql-server-ver15
Feature.functionalStatement, Feature.createProcedure, Feature.createFunction,
Feature.alterFunction, Feature.alterProcedure, Feature.createOrAlterRoutine,
Feature.block,
Feature.declare,
Feature.tableVariable, // https://docs.microsoft.com/en-us/sql/t-sql/statements/create-schema-transact-sql?view=sql-server-ver15
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -382,10 +382,27 @@ public <S> Void visit(AlterSequence alterSequence, S context) {
@Override
public <S> Void visit(CreateFunctionalStatement createFunctionalStatement, S context) {
validateFeature(Feature.functionalStatement);
if (createFunctionalStatement
.getOperation() == CreateFunctionalStatement.Operation.CREATE_OR_ALTER) {
validateFeature(Feature.createOrAlterRoutine);
}
if (createFunctionalStatement.getReturnType() != null
&& createFunctionalStatement.getReturnType().getTableElements() != null) {
createFunctionalStatement.getReturnType().getTableElements()
.forEach(element -> net.sf.jsqlparser.util.TableDefinitionTraversal.visit(
element,
this::validateOptionalExpression, this::validateOptionalFromItem));
}
if (createFunctionalStatement instanceof CreateFunction) {
validateFeature(Feature.createFunction);
validateFeature(createFunctionalStatement
.getOperation() == CreateFunctionalStatement.Operation.ALTER
? Feature.alterFunction
: Feature.createFunction);
} else if (createFunctionalStatement instanceof CreateProcedure) {
validateFeature(Feature.createProcedure);
validateFeature(createFunctionalStatement
.getOperation() == CreateFunctionalStatement.Operation.ALTER
? Feature.alterProcedure
: Feature.createProcedure);
}
return null;
}
Expand Down
Loading
Loading