Repository navigation
Expand file tree
/
Copy pathRoutineCharacteristic.java
More file actions
263 lines (234 loc) · 8.78 KB
/
Copy pathRoutineCharacteristic.java
File metadata and controls
263 lines (234 loc) · 8.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
/*-
* #%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;
import java.io.Serializable;
import java.util.function.Consumer;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.StringValue;
/**
* A characteristic set by {@code ALTER FUNCTION}, {@code ALTER PROCEDURE} or {@code ALTER ROUTINE}.
* PostgreSQL and MySQL share the model, and each {@link Kind} renders its own SQL:
*
* <ul>
* <li>PostgreSQL: the volatility, the null input behavior, {@code [NOT] LEAKPROOF},
* {@code [EXTERNAL] SECURITY}, {@code PARALLEL}, {@code COST}, {@code ROWS}, {@code SUPPORT} and
* the {@code SET} or {@code RESET} of a configuration parameter;</li>
* <li>MySQL: {@code COMMENT}, {@code LANGUAGE SQL}, the SQL data access and
* {@code SQL SECURITY}.</li>
* </ul>
*
* The security kinds are shared; {@link #getSecurityClause()} keeps the spelling. Values keep their
* SQL text, such as {@code 100}. The {@code COMMENT} literal is a {@link StringValue}.
*/
public class RoutineCharacteristic implements Serializable {
public enum Kind {
/** PostgreSQL {@code IMMUTABLE}. */
IMMUTABLE("IMMUTABLE"),
/** PostgreSQL {@code STABLE}. */
STABLE("STABLE"),
/** PostgreSQL {@code VOLATILE}. */
VOLATILE("VOLATILE"),
/** PostgreSQL {@code CALLED ON NULL INPUT}. */
CALLED_ON_NULL_INPUT("CALLED ON NULL INPUT"),
/** PostgreSQL {@code RETURNS NULL ON NULL INPUT}. */
RETURNS_NULL_ON_NULL_INPUT("RETURNS NULL ON NULL INPUT"),
/** PostgreSQL {@code STRICT}. */
STRICT("STRICT"),
/** PostgreSQL {@code LEAKPROOF}. */
LEAKPROOF("LEAKPROOF"),
/** PostgreSQL {@code NOT LEAKPROOF}. */
NOT_LEAKPROOF("NOT LEAKPROOF"),
/** {@code DEFINER}, preceded by the {@link SecurityClause}. */
SECURITY_DEFINER("DEFINER"),
/** {@code INVOKER}, preceded by the {@link SecurityClause}. */
SECURITY_INVOKER("INVOKER"),
/** PostgreSQL {@code PARALLEL UNSAFE}. */
PARALLEL_UNSAFE("PARALLEL UNSAFE"),
/** PostgreSQL {@code PARALLEL RESTRICTED}. */
PARALLEL_RESTRICTED("PARALLEL RESTRICTED"),
/** PostgreSQL {@code PARALLEL SAFE}. */
PARALLEL_SAFE("PARALLEL SAFE"),
/** PostgreSQL {@code COST} followed by the numeric {@link #getValue()}. */
COST("COST"),
/** PostgreSQL {@code ROWS} followed by the numeric {@link #getValue()}. */
ROWS("ROWS"),
/** PostgreSQL {@code SUPPORT} followed by the function name in {@link #getValue()}. */
SUPPORT("SUPPORT"),
/** PostgreSQL {@code SET} or {@code RESET}, see {@link #getParameterChange()}. */
CONFIGURATION_PARAMETER(null),
/** MySQL {@code COMMENT} followed by the string literal of {@link #getComment()}. */
COMMENT("COMMENT"),
/** MySQL {@code LANGUAGE SQL}. */
LANGUAGE_SQL("LANGUAGE SQL"),
/** MySQL {@code CONTAINS SQL}. */
CONTAINS_SQL("CONTAINS SQL"),
/** MySQL {@code NO SQL}. */
NO_SQL("NO SQL"),
/** MySQL {@code READS SQL DATA}. */
READS_SQL_DATA("READS SQL DATA"),
/** MySQL {@code MODIFIES SQL DATA}. */
MODIFIES_SQL_DATA("MODIFIES SQL DATA");
private final String keywords;
Kind(String keywords) {
this.keywords = keywords;
}
private boolean hasValue() {
return this == COST || this == ROWS || this == SUPPORT || this == COMMENT;
}
}
/** The words before {@code DEFINER} or {@code INVOKER}. */
public enum SecurityClause {
/** PostgreSQL {@code SECURITY}. */
SECURITY("SECURITY"),
/** PostgreSQL {@code EXTERNAL SECURITY}; {@code EXTERNAL} has no effect. */
EXTERNAL_SECURITY("EXTERNAL SECURITY"),
/** MySQL {@code SQL SECURITY}. */
SQL_SECURITY("SQL SECURITY");
private final String keywords;
SecurityClause(String keywords) {
this.keywords = keywords;
}
}
private Kind kind;
private String value;
private StringValue comment;
private SecurityClause securityClause;
private ConfigurationParameterChange parameterChange;
public RoutineCharacteristic(Kind kind) {
this.kind = kind;
}
public RoutineCharacteristic(Kind kind, String value) {
this.kind = kind;
setValue(value);
}
public RoutineCharacteristic(ConfigurationParameterChange parameterChange) {
this.kind = Kind.CONFIGURATION_PARAMETER;
this.parameterChange = parameterChange;
}
/**
* Creates a MySQL {@code COMMENT} characteristic.
*
* @param comment the string literal
* @return the characteristic
*/
public static RoutineCharacteristic comment(StringValue comment) {
return new RoutineCharacteristic(Kind.COMMENT).withComment(comment);
}
public Kind getKind() {
return kind;
}
public void setKind(Kind kind) {
this.kind = kind;
}
/**
* The SQL text after the keyword of {@link Kind#COST}, {@link Kind#ROWS}, {@link Kind#SUPPORT}
* and {@link Kind#COMMENT}. For {@code COMMENT} it is derived from {@link #getComment()}, such
* as {@code 'it\'s'}.
*
* @return the value as written, or {@code null} for other kinds
*/
public String getValue() {
if (kind == Kind.COMMENT) {
return comment == null ? null : comment.toString();
}
return value;
}
/**
* Sets the SQL text of the value. For {@link Kind#COMMENT} it replaces {@link #getComment()}
* with a literal read without dialect escaping; use {@link #setComment(StringValue)} to keep
* MySQL backslash escapes.
*
* @param value the SQL text
*/
public void setValue(String value) {
if (kind == Kind.COMMENT) {
comment = value == null ? null : new StringValue(value);
} else {
this.value = value;
}
}
/**
* The string literal of {@link Kind#COMMENT}. {@link StringValue#getNotExcapedValue()} returns
* the text that MySQL stores.
*
* @return the literal, or {@code null} for other kinds
*/
public StringValue getComment() {
return comment;
}
public void setComment(StringValue comment) {
this.comment = comment;
}
public RoutineCharacteristic withComment(StringValue comment) {
setComment(comment);
return this;
}
/**
* The spelling of {@link Kind#SECURITY_DEFINER} and {@link Kind#SECURITY_INVOKER}; {@code null}
* renders {@link SecurityClause#SECURITY}.
*
* @return the clause, or {@code null}
*/
public SecurityClause getSecurityClause() {
return securityClause;
}
public void setSecurityClause(SecurityClause securityClause) {
this.securityClause = securityClause;
}
/**
* The change of {@link Kind#CONFIGURATION_PARAMETER}.
*
* @return the change, or {@code null} for other kinds
*/
public ConfigurationParameterChange getParameterChange() {
return parameterChange;
}
public void setParameterChange(ConfigurationParameterChange parameterChange) {
this.parameterChange = parameterChange;
}
public RoutineCharacteristic withValue(String value) {
setValue(value);
return this;
}
public RoutineCharacteristic withSecurityClause(SecurityClause securityClause) {
setSecurityClause(securityClause);
return this;
}
/**
* Appends this characteristic to a builder.
*
* @param builder the target
* @param valueRenderer appends each configuration parameter value
* @return the builder
*/
public StringBuilder appendTo(StringBuilder builder, Consumer<Expression> valueRenderer) {
switch (kind) {
case CONFIGURATION_PARAMETER:
if (parameterChange == null) {
throw new IllegalStateException("Expected a configuration parameter change");
}
return parameterChange.appendTo(builder, valueRenderer);
case SECURITY_DEFINER:
case SECURITY_INVOKER:
return builder
.append((securityClause == null ? SecurityClause.SECURITY
: securityClause).keywords)
.append(' ').append(kind.keywords);
default:
builder.append(kind.keywords);
return kind.hasValue() ? builder.append(' ').append(getValue()) : builder;
}
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
return appendTo(builder, builder::append).toString();
}
}