Skip to content

Commit 8619500

Browse files
committed
#918 JDBC 4.5: Firebird-specific implementation of various enquote methods on Connection
1 parent e0ad272 commit 8619500

16 files changed

Lines changed: 674 additions & 121 deletions

src/docs/asciidoc/release_notes.adoc

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,13 @@ The following has been changed or fixed since Jaybird 5.0.10:
4444
+
4545
Applications referencing this constant may need to be recompiled to function correctly with Jaybird 5.0.12 and higher.
4646
If you use Java 26 or higher, we recommend using `java.sql.Type.DECFLOAT` and `java.sql.JDBCType.DECFLOAT` instead.
47+
* JDBC 4.5 support: implemented methods `enquoteIdentifier`, `enquoteLiteral`, `enquoteNCharLiteral`, and `isSimpleIdentifier` on `FBConnection`, and added them to interface `FirebirdConnection` so for older Java versions (https://github.com/FirebirdSQL/jaybird/issues/908[#908])
48+
+
49+
The methods of the same name in the `java.sql.Statement` implementations now call the methods on the connection.
50+
This results in two minor breaking changes:
51+
+
52+
** Reserved words are no longer considered simple identifiers by `enquoteIdentifier` and `isSimpleIdentifier` and will be quoted (or -- for dialect 1 -- result in a `SQLFeatureNotSupportedException`)
53+
** Presence of the NUL character (U+0000) in an identifier passed to `enquoteIdentifier` will result in a `SQLSyntaxErrorException`
4754
4855
[#jaybird-5-0-11-changelog]
4956
=== Jaybird 5.0.11
@@ -436,11 +443,13 @@ Specifically affected are:
436443
At this time, Jaybird still uses its own constants in `JaybirdType.DECFLOAT` and `JaybirdTypeCodes.DECFLOAT` with value `-6001`.
437444
This value will be changed to match the JDBC specified value, and the constants deprecated, in a future maintenance release of Jaybird.
438445
+
439-
Addressed in Jaybird 6.0.5.
446+
Addressed in Jaybird 5.0.12.
440447
* Introduction of new methods on `java.sql.Connection` -- `enquoteIdentifier`, `enquoteLiteral`, `enquoteNCharLiteral`, and `isSimpleIdentifier`
441448
+
442449
The default implementation provided is not sufficient for Firebird, especially not when connecting to a dialect 1 database.
443450
For the time being, we recommend using the equivalent JDBC 4.3 methods (with the same name) on a `java.sql.Statement` instance of the connection.
451+
+
452+
Addressed in Jaybird 5.0.12
444453
* The JDBC escape to selectively disable escape processing ([.nowrap]`++{\ ... \}++`) is not yet implemented.
445454

446455
We plan to address these issues in Jaybird 5 and Jaybird 6 maintenance releases _after_ the release of Java 26 (tracked by https://github.com/FirebirdSQL/jaybird/issues/907[#907]).

src/main/org/firebirdsql/gds/JaybirdErrorCodes.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,9 @@ public interface JaybirdErrorCodes {
102102
int jb_localTransactionActive = 337248309;
103103
// gaps due to selective backporting
104104
int jb_invalidStringLength = 337248330;
105+
int jb_invalidIdentifierLength = 337248350;
106+
int jb_invalidIdentifierName = 337248351;
107+
int jb_noDelimitedIdentifiersInDialect1 = 337248352;
105108

106109
@SuppressWarnings("unused")
107110
int jb_range_end = 337264639;

src/main/org/firebirdsql/jaybird/parser/FirebirdReservedWords.java

Lines changed: 130 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,38 @@
3030
/**
3131
* All reserved words per Firebird version.
3232
*
33-
* @author <a href="mailto:mrotteveel@users.sourceforge.net">Mark Rotteveel</a>
33+
* @author Mark Rotteveel
3434
* @since 5
3535
*/
3636
@InternalApi
3737
public enum FirebirdReservedWords implements ReservedWords {
3838

39-
// TODO Add other versions if it turns out we do need reserved words per version
40-
41-
FIREBIRD_5_0("ADD", "ADMIN", "ALL", "ALTER", "AND", "ANY", "AS", "AT", "AVG", "BEGIN", "BETWEEN", "BIGINT",
39+
// Order is intentionally from higher versions to lower versions, see of(AbstractVersion) and latest()
40+
FIREBIRD_5_0(5, 0, "ADD", "ADMIN", "ALL", "ALTER", "AND", "ANY", "AS", "AT", "AVG", "BEGIN", "BETWEEN", "BIGINT",
41+
"BINARY", "BIT_LENGTH", "BLOB", "BOOLEAN", "BOTH", "BY", "CASE", "CAST", "CHAR", "CHARACTER",
42+
"CHARACTER_LENGTH", "CHAR_LENGTH", "CHECK", "CLOSE", "COLLATE", "COLUMN", "COMMENT", "COMMIT", "CONNECT",
43+
"CONSTRAINT", "CORR", "COUNT", "COVAR_POP", "COVAR_SAMP", "CREATE", "CROSS", "CURRENT",
44+
"CURRENT_CONNECTION", "CURRENT_DATE", "CURRENT_ROLE", "CURRENT_TIME", "CURRENT_TIMESTAMP",
45+
"CURRENT_TRANSACTION", "CURRENT_USER", "CURSOR", "DATE", "DAY", "DEC", "DECFLOAT", "DECIMAL", "DECLARE",
46+
"DEFAULT", "DELETE", "DELETING", "DETERMINISTIC", "DISCONNECT", "DISTINCT", "DOUBLE", "DROP", "ELSE", "END",
47+
"ESCAPE", "EXECUTE", "EXISTS", "EXTERNAL", "EXTRACT", "FALSE", "FETCH", "FILTER", "FLOAT", "FOR", "FOREIGN",
48+
"FROM", "FULL", "FUNCTION", "GDSCODE", "GLOBAL", "GRANT", "GROUP", "HAVING", "HOUR", "IN", "INDEX", "INNER",
49+
"INSENSITIVE", "INSERT", "INSERTING", "INT", "INT128", "INTEGER", "INTO", "IS", "JOIN", "LATERAL",
50+
"LEADING", "LEFT", "LIKE", "LOCAL", "LOCALTIME", "LOCALTIMESTAMP", "LONG", "LOWER", "MAX", "MERGE", "MIN",
51+
"MINUTE", "MONTH", "NATIONAL", "NATURAL", "NCHAR", "NO", "NOT", "NULL", "NUMERIC", "OCTET_LENGTH", "OF",
52+
"OFFSET", "ON", "ONLY", "OPEN", "OR", "ORDER", "OUTER", "OVER", "PARAMETER", "PLAN", "POSITION",
53+
"POST_EVENT", "PRECISION", "PRIMARY", "PROCEDURE", "PUBLICATION", "RDB$DB_KEY", "RDB$ERROR",
54+
"RDB$GET_CONTEXT", "RDB$GET_TRANSACTION_CN", "RDB$RECORD_VERSION", "RDB$ROLE_IN_USE", "RDB$SET_CONTEXT",
55+
"RDB$SYSTEM_PRIVILEGE", "REAL", "RECORD_VERSION", "RECREATE", "RECURSIVE", "REFERENCES", "REGR_AVGX",
56+
"REGR_AVGY", "REGR_COUNT", "REGR_INTERCEPT", "REGR_R2", "REGR_SLOPE", "REGR_SXX", "REGR_SXY", "REGR_SYY",
57+
"RELEASE", "RESETTING", "RETURN", "RETURNING_VALUES", "RETURNS", "REVOKE", "RIGHT", "ROLLBACK", "ROW",
58+
"ROWS", "ROW_COUNT", "SAVEPOINT", "SCHEMA", "SCROLL", "SECOND", "SELECT", "SENSITIVE", "SET", "SIMILAR",
59+
"SMALLINT", "SOME", "SQLCODE", "SQLSTATE", "START", "STDDEV_POP", "STDDEV_SAMP", "SUM", "TABLE", "THEN",
60+
"TIME", "TIMESTAMP", "TIMEZONE_HOUR", "TIMEZONE_MINUTE", "TO", "TRAILING", "TRIGGER", "TRIM", "TRUE",
61+
"UNBOUNDED", "UNION", "UNIQUE", "UNKNOWN", "UPDATE", "UPDATING", "UPPER", "USER", "USING", "VALUE",
62+
"VALUES", "VARBINARY", "VARCHAR", "VARIABLE", "VARYING", "VAR_POP", "VAR_SAMP", "VIEW", "WHEN", "WHERE",
63+
"WHILE", "WINDOW", "WITH", "WITHOUT", "YEAR"),
64+
FIREBIRD_4_0(4, 0, "ADD", "ADMIN", "ALL", "ALTER", "AND", "ANY", "AS", "AT", "AVG", "BEGIN", "BETWEEN", "BIGINT",
4265
"BINARY", "BIT_LENGTH", "BLOB", "BOOLEAN", "BOTH", "BY", "CASE", "CAST", "CHAR", "CHARACTER",
4366
"CHARACTER_LENGTH", "CHAR_LENGTH", "CHECK", "CLOSE", "COLLATE", "COLUMN", "COMMENT", "COMMIT", "CONNECT",
4467
"CONSTRAINT", "CORR", "COUNT", "COVAR_POP", "COVAR_SAMP", "CREATE", "CROSS", "CURRENT",
@@ -62,14 +85,93 @@ public enum FirebirdReservedWords implements ReservedWords {
6285
"UNION", "UNIQUE", "UNKNOWN", "UPDATE", "UPDATING", "UPPER", "USER", "USING", "VALUE", "VALUES",
6386
"VARBINARY", "VARCHAR", "VARIABLE", "VARYING", "VAR_POP", "VAR_SAMP", "VIEW", "WHEN", "WHERE", "WHILE",
6487
"WINDOW", "WITH", "WITHOUT", "YEAR"),
88+
FIREBIRD_3_0(3, 0, "ADD", "ADMIN", "ALL", "ALTER", "AND", "ANY", "AS", "AT", "AVG", "BEGIN", "BETWEEN", "BIGINT",
89+
"BIT_LENGTH", "BLOB", "BOOLEAN", "BOTH", "BY", "CASE", "CAST", "CHAR", "CHARACTER", "CHARACTER_LENGTH",
90+
"CHAR_LENGTH", "CHECK", "CLOSE", "COLLATE", "COLUMN", "COMMIT", "CONNECT", "CONSTRAINT", "CORR", "COUNT",
91+
"COVAR_POP", "COVAR_SAMP", "CREATE", "CROSS", "CURRENT", "CURRENT_CONNECTION", "CURRENT_DATE",
92+
"CURRENT_ROLE", "CURRENT_TIME", "CURRENT_TIMESTAMP", "CURRENT_TRANSACTION", "CURRENT_USER", "CURSOR",
93+
"DATE", "DAY", "DEC", "DECIMAL", "DECLARE", "DEFAULT", "DELETE", "DELETING", "DETERMINISTIC", "DISCONNECT",
94+
"DISTINCT", "DOUBLE", "DROP", "ELSE", "END", "ESCAPE", "EXECUTE", "EXISTS", "EXTERNAL", "EXTRACT", "FALSE",
95+
"FETCH", "FILTER", "FLOAT", "FOR", "FOREIGN", "FROM", "FULL", "FUNCTION", "GDSCODE", "GLOBAL", "GRANT",
96+
"GROUP", "HAVING", "HOUR", "IN", "INDEX", "INNER", "INSENSITIVE", "INSERT", "INSERTING", "INT", "INTEGER",
97+
"INTO", "IS", "JOIN", "LEADING", "LEFT", "LIKE", "LONG", "LOWER", "MAX", "MERGE", "MIN", "MINUTE", "MONTH",
98+
"NATIONAL", "NATURAL", "NCHAR", "NO", "NOT", "NULL", "NUMERIC", "OCTET_LENGTH", "OF", "OFFSET", "ON",
99+
"ONLY", "OPEN", "OR", "ORDER", "OUTER", "OVER", "PARAMETER", "PLAN", "POSITION", "POST_EVENT", "PRECISION",
100+
"PRIMARY", "PROCEDURE", "RDB$DB_KEY", "RDB$RECORD_VERSION", "REAL", "RECORD_VERSION", "RECREATE",
101+
"RECURSIVE", "REFERENCES", "REGR_AVGX", "REGR_AVGY", "REGR_COUNT", "REGR_INTERCEPT", "REGR_R2",
102+
"REGR_SLOPE", "REGR_SXX", "REGR_SXY", "REGR_SYY", "RELEASE", "RETURN", "RETURNING_VALUES", "RETURNS",
103+
"REVOKE", "RIGHT", "ROLLBACK", "ROW", "ROWS", "ROW_COUNT", "SAVEPOINT", "SCROLL", "SECOND", "SELECT",
104+
"SENSITIVE", "SET", "SIMILAR", "SMALLINT", "SOME", "SQLCODE", "SQLSTATE", "START", "STDDEV_POP",
105+
"STDDEV_SAMP", "SUM", "TABLE", "THEN", "TIME", "TIMESTAMP", "TO", "TRAILING", "TRIGGER", "TRIM", "TRUE",
106+
"UNION", "UNIQUE", "UNKNOWN", "UPDATE", "UPDATING", "UPPER", "USER", "USING", "VALUE", "VALUES", "VARCHAR",
107+
"VARIABLE", "VARYING", "VAR_POP", "VAR_SAMP", "VIEW", "WHEN", "WHERE", "WHILE", "WITH", "YEAR"),
108+
FIREBIRD_2_5(2, 5, "ADD", "ADMIN", "ALL", "ALTER", "AND", "ANY", "AS", "AT", "AVG", "BEGIN", "BETWEEN", "BIGINT",
109+
"BIT_LENGTH", "BLOB", "BOTH", "BY", "CASE", "CAST", "CHAR", "CHARACTER", "CHARACTER_LENGTH", "CHAR_LENGTH",
110+
"CHECK", "CLOSE", "COLLATE", "COLUMN", "COMMIT", "CONNECT", "CONSTRAINT", "COUNT", "CREATE", "CROSS",
111+
"CURRENT", "CURRENT_CONNECTION", "CURRENT_DATE", "CURRENT_ROLE", "CURRENT_TIME", "CURRENT_TIMESTAMP",
112+
"CURRENT_TRANSACTION", "CURRENT_USER", "CURSOR", "DATE", "DAY", "DEC", "DECIMAL", "DECLARE", "DEFAULT",
113+
"DELETE", "DISCONNECT", "DISTINCT", "DOUBLE", "DROP", "ELSE", "END", "ESCAPE", "EXECUTE", "EXISTS",
114+
"EXTERNAL", "EXTRACT", "FETCH", "FILTER", "FLOAT", "FOR", "FOREIGN", "FROM", "FULL", "FUNCTION", "GDSCODE",
115+
"GLOBAL", "GRANT", "GROUP", "HAVING", "HOUR", "IN", "INDEX", "INNER", "INSENSITIVE", "INSERT", "INT",
116+
"INTEGER", "INTO", "IS", "JOIN", "LEADING", "LEFT", "LIKE", "LOCALTIME", "LOCALTIMESTAMP", "LONG", "LOWER",
117+
"MAX", "MAXIMUM_SEGMENT", "MERGE", "MIN", "MINUTE", "MONTH", "NATIONAL", "NATURAL", "NCHAR", "NO", "NOT",
118+
"NULL", "NUMERIC", "OCTET_LENGTH", "OF", "ON", "ONLY", "OPEN", "OR", "ORDER", "OUTER", "PARAMETER", "PLAN",
119+
"POSITION", "POST_EVENT", "PRECISION", "PRIMARY", "PROCEDURE", "RDB$DB_KEY", "REAL", "RECORD_VERSION",
120+
"RECREATE", "RECURSIVE", "REFERENCES", "RELEASE", "RETURNING_VALUES", "RETURNS", "REVOKE", "RIGHT",
121+
"ROLLBACK", "ROWS", "ROW_COUNT", "SAVEPOINT", "SECOND", "SELECT", "SENSITIVE", "SET", "SIMILAR", "SMALLINT",
122+
"SOME", "SQLCODE", "SQLSTATE", "START", "SUM", "TABLE", "THEN", "TIME", "TIMESTAMP", "TO", "TRAILING",
123+
"TRIGGER", "TRIM", "UNION", "UNIQUE", "UPDATE", "UPPER", "USER", "USING", "VALUE", "VALUES", "VARCHAR",
124+
"VARIABLE", "VARYING", "VIEW", "WHEN", "WHERE", "WHILE", "WITH", "YEAR"),
125+
FIREBIRD_2_1(2, 1, "ACTIVE", "ADD", "ADMIN", "AFTER", "ALL", "ALTER", "AND", "ANY", "AS", "ASC", "ASCENDING", "AT",
126+
"AUTO", "AVG", "BEFORE", "BEGIN", "BETWEEN", "BIGINT", "BIT_LENGTH", "BLOB", "BOTH", "BY", "CASE", "CAST",
127+
"CHAR", "CHARACTER", "CHARACTER_LENGTH", "CHAR_LENGTH", "CHECK", "CLOSE", "COLLATE", "COLUMN", "COMMIT",
128+
"COMMITTED", "COMPUTED", "CONDITIONAL", "CONNECT", "CONSTRAINT", "CONTAINING", "COUNT", "CREATE", "CROSS",
129+
"CSTRING", "CURRENT", "CURRENT_CONNECTION", "CURRENT_DATE", "CURRENT_ROLE", "CURRENT_TIME",
130+
"CURRENT_TIMESTAMP", "CURRENT_TRANSACTION", "CURRENT_USER", "CURSOR", "DATABASE", "DATE", "DAY", "DEBUG",
131+
"DEC", "DECIMAL", "DECLARE", "DEFAULT", "DELETE", "DESC", "DESCENDING", "DISCONNECT", "DISTINCT", "DO",
132+
"DOMAIN", "DOUBLE", "DROP", "ELSE", "END", "ENTRY_POINT", "ESCAPE", "EXCEPTION", "EXECUTE", "EXISTS",
133+
"EXIT", "EXTERNAL", "EXTRACT", "FETCH", "FILE", "FILTER", "FLOAT", "FOR", "FOREIGN", "FROM", "FULL",
134+
"FUNCTION", "GDSCODE", "GENERATOR", "GEN_ID", "GLOBAL", "GRANT", "GROUP", "HAVING", "HOUR", "IF", "IN",
135+
"INACTIVE", "INDEX", "INNER", "INPUT_TYPE", "INSENSITIVE", "INSERT", "INT", "INTEGER", "INTO", "IS",
136+
"ISOLATION", "JOIN", "KEY", "LEADING", "LEFT", "LENGTH", "LEVEL", "LIKE", "LONG", "LOWER", "MANUAL", "MAX",
137+
"MAXIMUM_SEGMENT", "MERGE", "MIN", "MINUTE", "MODULE_NAME", "MONTH", "NAMES", "NATIONAL", "NATURAL",
138+
"NCHAR", "NO", "NOT", "NULL", "NUMERIC", "OCTET_LENGTH", "OF", "ON", "ONLY", "OPEN", "OPTION", "OR",
139+
"ORDER", "OUTER", "OUTPUT_TYPE", "OVERFLOW", "PAGE", "PAGES", "PAGE_SIZE", "PARAMETER", "PASSWORD", "PLAN",
140+
"POSITION", "POST_EVENT", "PRECISION", "PRIMARY", "PRIVILEGES", "PROCEDURE", "PROTECTED", "RDB$DB_KEY",
141+
"READ", "REAL", "RECORD_VERSION", "RECREATE", "RECURSIVE", "REFERENCES", "RELEASE", "RESERV", "RESERVING",
142+
"RETAIN", "RETURNING_VALUES", "RETURNS", "REVOKE", "RIGHT", "ROLLBACK", "ROWS", "ROW_COUNT", "SAVEPOINT",
143+
"SCHEMA", "SECOND", "SEGMENT", "SELECT", "SENSITIVE", "SET", "SHADOW", "SHARED", "SINGULAR", "SIZE",
144+
"SMALLINT", "SNAPSHOT", "SOME", "SORT", "SQLCODE", "STABILITY", "START", "STARTING", "STARTS", "STATISTICS",
145+
"SUB_TYPE", "SUM", "SUSPEND", "TABLE", "THEN", "TIME", "TIMESTAMP", "TO", "TRAILING", "TRANSACTION",
146+
"TRIGGER", "TRIM", "UNCOMMITTED", "UNION", "UNIQUE", "UPDATE", "UPPER", "USER", "USING", "VALUE", "VALUES",
147+
"VARCHAR", "VARIABLE", "VARYING", "VIEW", "WAIT", "WHEN", "WHERE", "WHILE", "WITH", "WORK", "WRITE",
148+
"YEAR"),
65149
;
66150

151+
private final int major;
152+
private final int minor;
67153
private final Set<CharSequence> reservedWords;
68154

69-
FirebirdReservedWords(CharSequence... reservedWords) {
155+
FirebirdReservedWords(int major, int minor, CharSequence... reservedWords) {
156+
this.major = major;
157+
this.minor = minor;
70158
this.reservedWords = toUnmodifiableCaseInsensitiveSet(Arrays.asList(reservedWords));
71159
}
72160

161+
/**
162+
* @return Firebird major version
163+
*/
164+
int major() {
165+
return major;
166+
}
167+
168+
/**
169+
* @return Firebird minor version
170+
*/
171+
int minor() {
172+
return minor;
173+
}
174+
73175
/**
74176
* Reserved words for latest known Firebird version.
75177
*
@@ -79,14 +181,36 @@ public static FirebirdReservedWords latest() {
79181
return FIREBIRD_5_0;
80182
}
81183

184+
/**
185+
* Reserved words for the specified Firebird version.
186+
*
187+
* @param major
188+
* Firebird major version
189+
* @param minor Firebird minor version
190+
* @return reserved words for the specified version, or the closest known version
191+
* @since 5.0.12
192+
*/
193+
public static FirebirdReservedWords of(int major, int minor) {
194+
FirebirdReservedWords[] values = values();
195+
for (FirebirdReservedWords reservedWords : values) {
196+
if (major > reservedWords.major ||
197+
major == reservedWords.major && minor >= reservedWords.minor) {
198+
return reservedWords;
199+
}
200+
}
201+
// fall back to the lowest known version
202+
return values[values.length - 1];
203+
}
204+
82205
private static Set<CharSequence> toUnmodifiableCaseInsensitiveSet(Collection<CharSequence> values) {
83206
Set<CharSequence> set = new TreeSet<>(CharSequenceComparison.caseInsensitiveComparator());
84207
set.addAll(values);
85208
return unmodifiableSet(set);
86209
}
87210

88211
@Override
89-
public boolean isReservedWord(CharSequence tokenText) {
212+
public final boolean isReservedWord(CharSequence tokenText) {
90213
return reservedWords.contains(tokenText);
91214
}
215+
92216
}

src/main/org/firebirdsql/jdbc/FBConnection.java

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,12 @@
4444
import java.util.concurrent.TimeUnit;
4545
import java.util.concurrent.TimeoutException;
4646
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
47+
import java.util.regex.Pattern;
4748

49+
import static java.util.Objects.requireNonNull;
50+
import static org.firebirdsql.gds.JaybirdErrorCodes.jb_invalidIdentifierLength;
51+
import static org.firebirdsql.gds.JaybirdErrorCodes.jb_invalidIdentifierName;
52+
import static org.firebirdsql.gds.JaybirdErrorCodes.jb_noDelimitedIdentifiersInDialect1;
4853
import static org.firebirdsql.jdbc.SQLStateConstants.SQL_STATE_CONNECTION_CLOSED;
4954

5055
/**
@@ -1277,6 +1282,65 @@ public int getNetworkTimeout() throws SQLException {
12771282
return getFbDatabase().getNetworkTimeout();
12781283
}
12791284

1285+
@Override
1286+
public final String enquoteLiteral(String val) throws SQLException {
1287+
if (getFbDatabase().getDatabaseDialect() == 1) {
1288+
return '"' + val.replace("\"", "\"\"") + '"';
1289+
}
1290+
return "'" + val.replace("'", "''") + "'";
1291+
}
1292+
1293+
@Override
1294+
public final String enquoteNCharLiteral(String val) throws SQLException {
1295+
return enquoteLiteral(val);
1296+
}
1297+
1298+
// NOTE: This intentionally does not take case sensitivity into account
1299+
private static final Pattern SIMPLE_IDENTIFIER_PATTERN = Pattern.compile("\\p{Alpha}[\\p{Alnum}_$]*");
1300+
1301+
@Override
1302+
public final String enquoteIdentifier(String identifier, boolean alwaysDelimit) throws SQLException {
1303+
int len = identifier.length();
1304+
// See also comment in isSimpleIdentifier(String) regarding length check
1305+
if (len < 1 || len > getMetaData().getMaxColumnNameLength()) {
1306+
throw FbExceptionBuilder.forException(jb_invalidIdentifierLength).toSQLException();
1307+
}
1308+
if (!alwaysDelimit && isSimpleIdentifier(identifier)) {
1309+
return identifier;
1310+
}
1311+
QuoteStrategy quoteStrategy = getQuoteStrategy();
1312+
if (quoteStrategy == QuoteStrategy.NO_QUOTES /* dialect 1 */) {
1313+
throw FbExceptionBuilder.forException(jb_noDelimitedIdentifiersInDialect1).toSQLException();
1314+
}
1315+
if (identifier.matches("^\".+\"$")) {
1316+
// Delimited identifier: remove delimiter and unescape double quotes
1317+
identifier = identifier.substring(1, len - 1).replace("\"\"", "\"");
1318+
}
1319+
// If the identifier contains a null character, throw a SQLException
1320+
if (identifier.indexOf(0) != -1) {
1321+
throw FbExceptionBuilder.forException(jb_invalidIdentifierName).toSQLException();
1322+
}
1323+
// Delimit identifier and escape double quotes
1324+
return quoteStrategy.quoteObjectName(identifier);
1325+
}
1326+
1327+
@Override
1328+
public final boolean isSimpleIdentifier(String identifier) throws SQLException {
1329+
int len = identifier.length();
1330+
/* This length check can be incorrect for Firebird 3.0 and lower, if the identifier contains non-ASCII (limit is
1331+
* 31 characters UNICODE_FSS and 31 bytes).
1332+
* This length check can be incorrect for Firebird 4.0 and higher, if the identifier contains codepoints that
1333+
* require surrogate pairs.
1334+
* We accept those limitations. */
1335+
return len >= 1 && len <= getMetaData().getMaxColumnNameLength()
1336+
&& SIMPLE_IDENTIFIER_PATTERN.matcher(identifier).matches()
1337+
&& !isReservedWord(identifier);
1338+
}
1339+
1340+
private boolean isReservedWord(String word) throws SQLException {
1341+
return ((FirebirdDatabaseMetaData) getMetaData()).isReservedWord(word);
1342+
}
1343+
12801344
/**
12811345
* @see org.firebirdsql.gds.ng.FbAttachment#withLock()
12821346
*/

src/main/org/firebirdsql/jdbc/FBDatabaseMetaData.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,11 @@ public String getSQLKeywords() throws SQLException {
248248
return versionMetaData.getSqlKeywords();
249249
}
250250

251+
@Override
252+
public boolean isReservedWord(String word) {
253+
return versionMetaData.isReservedWord(word);
254+
}
255+
251256
/**
252257
* {@inheritDoc}
253258
* <p>

0 commit comments

Comments
 (0)