Skip to content

Commit ff71ca1

Browse files
mjmj
authored andcommitted
Merge master and preserve concurrent documentation additions
2 parents e2482b8 + 35f84d5 commit ff71ca1

18 files changed

Lines changed: 987 additions & 43 deletions

File tree

src/main/java/net/sf/jsqlparser/parser/AbstractJSqlParser.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public enum Dialect {
4646
AdjacentStringLiterals.WHITESPACE,
4747
Feature.allowDoubleQuotedStrings,
4848
Feature.allowBackslashEscapeCharacter), SNOWFLAKE(
49-
Feature.allowBackslashEscapeCharacter), INFORMIX, SPANNER, DORIS;
49+
Feature.allowBackslashEscapeCharacter), INFORMIX, SPANNER, DORIS, COCKROACHDB;
5050

5151
private final Set<Feature> lexerFeatures;
5252
private final AdjacentStringLiterals adjacentStringLiterals;

src/main/java/net/sf/jsqlparser/parser/feature/Feature.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -664,6 +664,8 @@ public enum Feature {
664664
* @see CreateFunctionalStatement
665665
*/
666666
functionalStatement,
667+
668+
alterFunction, alterProcedure, createOrAlterRoutine,
667669
/**
668670
* SQL block starting with "BEGIN" and ends with "END" statement is allowed
669671
*

src/main/java/net/sf/jsqlparser/statement/CreateFunctionalStatement.java

Lines changed: 76 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
import java.util.Collections;
1515
import java.util.List;
1616
import java.util.Optional;
17+
import java.util.function.Consumer;
18+
import net.sf.jsqlparser.statement.create.function.FunctionReturnType;
19+
import net.sf.jsqlparser.statement.create.table.TableElement;
1720

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

29+
public enum Operation {
30+
CREATE, ALTER, CREATE_OR_ALTER
31+
}
32+
33+
private Operation operation = Operation.CREATE;
34+
private FunctionReturnType returnType;
35+
private List<String> routineBodyParts;
36+
37+
public Operation getOperation() {
38+
return operation;
39+
}
40+
41+
public void setOperation(Operation operation) {
42+
this.operation = operation;
43+
}
44+
45+
public FunctionReturnType getReturnType() {
46+
return returnType;
47+
}
48+
49+
public void setReturnType(FunctionReturnType returnType) {
50+
this.returnType = returnType;
51+
}
52+
53+
public List<String> getRoutineBodyParts() {
54+
return routineBodyParts;
55+
}
56+
57+
public void setRoutineBodyParts(List<String> parts) {
58+
routineBodyParts = parts;
59+
}
60+
61+
2662
private List<String> functionDeclarationParts;
2763

2864
protected CreateFunctionalStatement(String kind) {
@@ -41,7 +77,9 @@ protected CreateFunctionalStatement(boolean orReplace, String kind,
4177
}
4278

4379
/**
44-
* @return the declaration parts after {@code CREATE FUNCTION|PROCEDURE}
80+
* @return the declaration parts after {@code CREATE FUNCTION|PROCEDURE}. For a SQL Server
81+
* function with a structured {@link #getReturnType()}, these are the name and parameter
82+
* tokens before RETURNS; {@link #getRoutineBodyParts()} holds the remaining tokens.
4583
*/
4684
public List<String> getFunctionDeclarationParts() {
4785
return functionDeclarationParts;
@@ -66,22 +104,43 @@ public void setOrReplace(boolean orReplace) {
66104
* @return a whitespace appended String with the declaration parts with some minimal formatting.
67105
*/
68106
public String formatDeclaration() {
69-
StringBuilder declaration = new StringBuilder();
70-
int currIndex = 0;
71-
while (currIndex < functionDeclarationParts.size()) {
72-
String token = functionDeclarationParts.get(currIndex);
73-
declaration.append(token);
74-
// if the next token is a ; don't put a space
75-
if (currIndex + 1 < functionDeclarationParts.size()) {
76-
// peek ahead just to format nicely
77-
String nextToken = functionDeclarationParts.get(currIndex + 1);
78-
if (!nextToken.equals(";")) {
79-
declaration.append(" ");
80-
}
107+
StringBuilder builder = new StringBuilder();
108+
return appendDeclarationTo(builder, builder::append).toString();
109+
}
110+
111+
private StringBuilder appendDeclarationTo(StringBuilder builder,
112+
Consumer<TableElement> printer) {
113+
appendTokens(builder, functionDeclarationParts);
114+
if (returnType != null) {
115+
builder.append(' ');
116+
returnType.appendTo(builder, printer);
117+
if (routineBodyParts != null && !routineBodyParts.isEmpty()) {
118+
builder.append(' ');
119+
appendTokens(builder, routineBodyParts);
81120
}
82-
currIndex++;
83121
}
84-
return declaration.toString();
122+
return builder;
123+
}
124+
125+
private static void appendTokens(StringBuilder builder, List<String> tokens) {
126+
if (tokens == null) {
127+
return;
128+
}
129+
for (int i = 0; i < tokens.size(); i++) {
130+
if (i > 0 && !";".equals(tokens.get(i))) {
131+
builder.append(' ');
132+
}
133+
builder.append(tokens.get(i));
134+
}
135+
}
136+
137+
public StringBuilder appendTo(StringBuilder builder, Consumer<TableElement> printer) {
138+
builder.append(operation.name().replace('_', ' ')).append(' ');
139+
if (orReplace && operation == Operation.CREATE) {
140+
builder.append("OR REPLACE ");
141+
}
142+
builder.append(kind).append(' ');
143+
return appendDeclarationTo(builder, printer);
85144
}
86145

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

92151
@Override
93152
public String toString() {
94-
return "CREATE "
95-
+ (orReplace ? "OR REPLACE " : "")
96-
+ kind + " " + formatDeclaration();
153+
StringBuilder builder = new StringBuilder();
154+
return appendTo(builder, builder::append).toString();
97155
}
98156

99157
public CreateFunctionalStatement withFunctionDeclarationParts(

src/main/java/net/sf/jsqlparser/statement/StatementVisitorAdapter.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -397,16 +397,9 @@ public <S> T visit(CreateView createView, S context) {
397397
public <S> T visit(Alter alter, S context) {
398398
alter.getTable().accept(fromItemVisitor, context);
399399
for (AlterExpression action : alter.getAlterExpressions()) {
400-
if (action.getColDataTypeList() != null) {
401-
action.getColDataTypeList().forEach(column -> TableDefinitionTraversal.visit(column,
402-
expression -> expression.accept(expressionVisitor, context),
403-
table -> table.accept(fromItemVisitor, context)));
404-
}
405-
if (action.getIndex() != null) {
406-
TableDefinitionTraversal.visit(action.getIndex(),
407-
expression -> expression.accept(expressionVisitor, context),
408-
table -> table.accept(fromItemVisitor, context));
409-
}
400+
TableDefinitionTraversal.visit(action,
401+
expression -> expression.accept(expressionVisitor, context),
402+
table -> table.accept(fromItemVisitor, context));
410403
}
411404
return null;
412405
}
@@ -553,6 +546,13 @@ public <S> T visit(AlterSequence alterSequence, S context) {
553546

554547
@Override
555548
public <S> T visit(CreateFunctionalStatement createFunctionalStatement, S context) {
549+
if (createFunctionalStatement.getReturnType() != null
550+
&& createFunctionalStatement.getReturnType().getTableElements() != null) {
551+
createFunctionalStatement.getReturnType().getTableElements()
552+
.forEach(element -> TableDefinitionTraversal.visit(element,
553+
expression -> expression.accept(expressionVisitor, context),
554+
table -> table.accept(fromItemVisitor, context)));
555+
}
556556
return null;
557557
}
558558

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*-
2+
* #%L
3+
* JSQLParser library
4+
* %%
5+
* Copyright (C) 2004 - 2026 JSQLParser
6+
* %%
7+
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
8+
* #L%
9+
*/
10+
package net.sf.jsqlparser.statement.alter;
11+
12+
import java.util.Iterator;
13+
import java.util.function.Consumer;
14+
import net.sf.jsqlparser.expression.Expression;
15+
import net.sf.jsqlparser.statement.create.table.Index;
16+
17+
/**
18+
* CockroachDB's ALTER PRIMARY KEY USING COLUMNS operation. Key elements and storage options are
19+
* available through {@link #getIndex()}; hash sharding and the legacy WITH BUCKET_COUNT expression
20+
* are represented separately.
21+
*/
22+
public class AlterExpressionPrimaryKey extends AlterExpression {
23+
private boolean usingHash;
24+
private Expression bucketCount;
25+
26+
public AlterExpressionPrimaryKey() {
27+
setOperation(AlterOperation.ALTER_PRIMARY_KEY);
28+
setIndex(new Index().withType("PRIMARY KEY"));
29+
}
30+
31+
public boolean isUsingHash() {
32+
return usingHash;
33+
}
34+
35+
public void setUsingHash(boolean usingHash) {
36+
this.usingHash = usingHash;
37+
}
38+
39+
public Expression getBucketCount() {
40+
return bucketCount;
41+
}
42+
43+
public void setBucketCount(Expression bucketCount) {
44+
this.bucketCount = bucketCount;
45+
}
46+
47+
@Override
48+
protected void appendBody(StringBuilder builder) {
49+
appendDefinition(builder, expression -> builder.append(expression));
50+
}
51+
52+
/** Shares statement rendering while preserving expression visitor customization. */
53+
public StringBuilder appendTo(StringBuilder builder, Consumer<Expression> expressionPrinter) {
54+
appendDefinition(builder, expressionPrinter);
55+
appendCommonTail(builder);
56+
return builder;
57+
}
58+
59+
private void appendDefinition(StringBuilder builder, Consumer<Expression> expressionPrinter) {
60+
builder.append("ALTER PRIMARY KEY USING COLUMNS (");
61+
if (getIndex().getColumns() != null) {
62+
for (Iterator<Index.ColumnParams> columns = getIndex().getColumns().iterator(); columns
63+
.hasNext();) {
64+
columns.next().appendTo(builder, expressionPrinter);
65+
if (columns.hasNext()) {
66+
builder.append(", ");
67+
}
68+
}
69+
}
70+
builder.append(')');
71+
appendSharding(builder, expressionPrinter);
72+
appendStorageOptions(builder, expressionPrinter);
73+
}
74+
75+
private void appendSharding(StringBuilder builder, Consumer<Expression> expressionPrinter) {
76+
if (usingHash) {
77+
builder.append(" USING HASH");
78+
if (bucketCount != null) {
79+
builder.append(" WITH BUCKET_COUNT = ");
80+
expressionPrinter.accept(bucketCount);
81+
}
82+
}
83+
}
84+
85+
private void appendStorageOptions(StringBuilder builder,
86+
Consumer<Expression> expressionPrinter) {
87+
if (getIndex().getStorageParameters() != null) {
88+
builder.append(" WITH (");
89+
for (Iterator<Index.Option> options =
90+
getIndex().getStorageParameters().iterator(); options.hasNext();) {
91+
options.next().appendTo(builder, expressionPrinter);
92+
if (options.hasNext()) {
93+
builder.append(", ");
94+
}
95+
}
96+
builder.append(')');
97+
}
98+
}
99+
}

src/main/java/net/sf/jsqlparser/statement/alter/AlterOperation.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
import java.util.Locale;
1313

1414
public enum AlterOperation {
15-
ADD, ALTER, DROP, DROP_PRIMARY_KEY, DROP_UNIQUE, DROP_FOREIGN_KEY, MODIFY, CHANGE, CONVERT, COLLATE, ALGORITHM, RENAME, RENAME_TABLE, RENAME_INDEX, RENAME_KEY, RENAME_CONSTRAINT, COMMENT, COMMENT_WITH_EQUAL_SIGN, UNSPECIFIC, ADD_PARTITION, DROP_PARTITION, ATTACH_PARTITION, DETACH_PARTITION, DISCARD_PARTITION, IMPORT_PARTITION, TRUNCATE_PARTITION, COALESCE_PARTITION, REORGANIZE_PARTITION, EXCHANGE_PARTITION, ANALYZE_PARTITION, CHECK_PARTITION, OPTIMIZE_PARTITION, REBUILD_PARTITION, REPAIR_PARTITION, REMOVE_PARTITIONING, PARTITION_BY, SET_TABLE_OPTION, ENGINE, FORCE, KEY_BLOCK_SIZE, LOCK, DISCARD_TABLESPACE, IMPORT_TABLESPACE, DISABLE_KEYS, ENABLE_KEYS, ENABLE_ROW_LEVEL_SECURITY, DISABLE_ROW_LEVEL_SECURITY, FORCE_ROW_LEVEL_SECURITY, NO_FORCE_ROW_LEVEL_SECURITY;
15+
ADD, ALTER, DROP, DROP_PRIMARY_KEY, DROP_UNIQUE, DROP_FOREIGN_KEY, MODIFY, CHANGE, CONVERT, COLLATE, ALGORITHM, RENAME, RENAME_TABLE, RENAME_INDEX, RENAME_KEY, RENAME_CONSTRAINT, COMMENT, COMMENT_WITH_EQUAL_SIGN, UNSPECIFIC, ADD_PARTITION, DROP_PARTITION, ATTACH_PARTITION, DETACH_PARTITION, DISCARD_PARTITION, IMPORT_PARTITION, TRUNCATE_PARTITION, COALESCE_PARTITION, REORGANIZE_PARTITION, EXCHANGE_PARTITION, ANALYZE_PARTITION, CHECK_PARTITION, OPTIMIZE_PARTITION, REBUILD_PARTITION, REPAIR_PARTITION, REMOVE_PARTITIONING, PARTITION_BY, SET_TABLE_OPTION, ENGINE, FORCE, KEY_BLOCK_SIZE, LOCK, DISCARD_TABLESPACE, IMPORT_TABLESPACE, DISABLE_KEYS, ENABLE_KEYS, ENABLE_ROW_LEVEL_SECURITY, DISABLE_ROW_LEVEL_SECURITY, FORCE_ROW_LEVEL_SECURITY, NO_FORCE_ROW_LEVEL_SECURITY, ALTER_PRIMARY_KEY;
1616

1717
public static AlterOperation from(String operation) {
1818
return Enum.valueOf(AlterOperation.class, operation.toUpperCase(Locale.ROOT));
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/*-
2+
* #%L
3+
* JSQLParser library
4+
* %%
5+
* Copyright (C) 2004 - 2026 JSQLParser
6+
* %%
7+
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
8+
* #L%
9+
*/
10+
package net.sf.jsqlparser.statement.create.function;
11+
12+
import java.io.Serializable;
13+
import java.util.ArrayList;
14+
import java.util.List;
15+
import java.util.function.Consumer;
16+
import net.sf.jsqlparser.statement.create.table.ColDataType;
17+
import net.sf.jsqlparser.statement.create.table.TableElement;
18+
19+
/** A SQL Server scalar, inline table or declared table return type. */
20+
public class FunctionReturnType implements Serializable {
21+
private ColDataType dataType;
22+
private boolean table;
23+
private String tableVariable;
24+
private List<TableElement> tableElements;
25+
26+
public ColDataType getDataType() {
27+
return dataType;
28+
}
29+
30+
public void setDataType(ColDataType dataType) {
31+
this.dataType = dataType;
32+
}
33+
34+
public boolean isTable() {
35+
return table;
36+
}
37+
38+
public void setTable(boolean table) {
39+
this.table = table;
40+
}
41+
42+
public String getTableVariable() {
43+
return tableVariable;
44+
}
45+
46+
public void setTableVariable(String variable) {
47+
this.tableVariable = variable;
48+
}
49+
50+
/** Null for an inline return table; otherwise columns and constraints in source order. */
51+
public List<TableElement> getTableElements() {
52+
return tableElements;
53+
}
54+
55+
public void setTableElements(List<TableElement> elements) {
56+
this.tableElements = elements;
57+
}
58+
59+
public <T extends TableElement> List<T> getTableElements(Class<T> type) {
60+
List<T> result = new ArrayList<>();
61+
if (tableElements != null) {
62+
for (TableElement element : tableElements) {
63+
if (type.isInstance(element)) {
64+
result.add(type.cast(element));
65+
}
66+
}
67+
}
68+
return result;
69+
}
70+
71+
public StringBuilder appendTo(StringBuilder builder, Consumer<TableElement> printer) {
72+
builder.append("RETURNS ");
73+
if (!table) {
74+
return builder.append(dataType);
75+
}
76+
if (tableVariable != null) {
77+
builder.append(tableVariable).append(' ');
78+
}
79+
builder.append("TABLE");
80+
if (tableElements != null) {
81+
builder.append(" (");
82+
for (int i = 0; i < tableElements.size(); i++) {
83+
if (i > 0) {
84+
builder.append(", ");
85+
}
86+
printer.accept(tableElements.get(i));
87+
}
88+
builder.append(')');
89+
}
90+
return builder;
91+
}
92+
93+
@Override
94+
public String toString() {
95+
StringBuilder builder = new StringBuilder();
96+
return appendTo(builder, builder::append).toString();
97+
}
98+
}

0 commit comments

Comments
 (0)