activerecord-jdbc-adapter 5.0.pre1 → 51.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -2
  3. data/.travis.yml +15 -416
  4. data/Gemfile +35 -37
  5. data/README.md +23 -118
  6. data/RUNNING_TESTS.md +31 -26
  7. data/Rakefile +2 -3
  8. data/activerecord-jdbc-adapter.gemspec +1 -2
  9. data/lib/arjdbc/abstract/connection_management.rb +21 -0
  10. data/lib/arjdbc/abstract/core.rb +62 -0
  11. data/lib/arjdbc/abstract/database_statements.rb +46 -0
  12. data/lib/arjdbc/abstract/statement_cache.rb +58 -0
  13. data/lib/arjdbc/abstract/transaction_support.rb +86 -0
  14. data/lib/arjdbc/derby/adapter.rb +6 -1
  15. data/lib/arjdbc/discover.rb +0 -7
  16. data/lib/arjdbc/firebird/adapter.rb +2 -2
  17. data/lib/arjdbc/jdbc/adapter.rb +10 -252
  18. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  19. data/lib/arjdbc/jdbc/connection.rb +6 -0
  20. data/lib/arjdbc/jdbc.rb +2 -2
  21. data/lib/arjdbc/mysql/adapter.rb +87 -944
  22. data/lib/arjdbc/mysql/connection_methods.rb +4 -2
  23. data/lib/arjdbc/postgresql/adapter.rb +288 -1023
  24. data/lib/arjdbc/postgresql/base/array_decoder.rb +26 -0
  25. data/lib/arjdbc/postgresql/base/array_encoder.rb +25 -0
  26. data/lib/arjdbc/postgresql/base/pgconn.rb +8 -5
  27. data/lib/arjdbc/postgresql/column.rb +10 -599
  28. data/lib/arjdbc/postgresql/connection_methods.rb +9 -0
  29. data/lib/arjdbc/postgresql/name.rb +24 -0
  30. data/lib/arjdbc/postgresql/oid_types.rb +25 -110
  31. data/lib/arjdbc/sqlite3/adapter.rb +171 -170
  32. data/lib/arjdbc/tasks/database_tasks.rb +1 -3
  33. data/lib/arjdbc/tasks/db2_database_tasks.rb +2 -2
  34. data/lib/arjdbc/version.rb +1 -1
  35. data/pom.xml +3 -3
  36. data/rakelib/02-test.rake +0 -12
  37. data/rakelib/compile.rake +1 -1
  38. data/rakelib/db.rake +7 -5
  39. data/rakelib/rails.rake +63 -64
  40. data/src/java/arjdbc/firebird/FirebirdRubyJdbcConnection.java +1 -17
  41. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +518 -1260
  42. data/src/java/arjdbc/mysql/MySQLModule.java +3 -3
  43. data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +53 -134
  44. data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +214 -240
  45. data/src/java/arjdbc/sqlite3/SQLite3Module.java +0 -20
  46. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +85 -10
  47. metadata +20 -34
  48. data/Appraisals +0 -41
  49. data/lib/active_record/connection_adapters/oracle_adapter.rb +0 -1
  50. data/lib/arjdbc/common_jdbc_methods.rb +0 -89
  51. data/lib/arjdbc/mysql/bulk_change_table.rb +0 -150
  52. data/lib/arjdbc/mysql/column.rb +0 -162
  53. data/lib/arjdbc/mysql/explain_support.rb +0 -82
  54. data/lib/arjdbc/mysql/schema_creation.rb +0 -58
  55. data/lib/arjdbc/oracle/adapter.rb +0 -952
  56. data/lib/arjdbc/oracle/column.rb +0 -126
  57. data/lib/arjdbc/oracle/connection_methods.rb +0 -21
  58. data/lib/arjdbc/oracle.rb +0 -4
  59. data/lib/arjdbc/postgresql/_bc_time_cast_patch.rb +0 -21
  60. data/lib/arjdbc/postgresql/base/oid.rb +0 -412
  61. data/lib/arjdbc/postgresql/base/schema_definitions.rb +0 -131
  62. data/lib/arjdbc/postgresql/explain_support.rb +0 -53
  63. data/lib/arjdbc/postgresql/oid/bytea.rb +0 -2
  64. data/lib/arjdbc/postgresql/schema_creation.rb +0 -60
  65. data/lib/arjdbc/tasks/oracle/enhanced_structure_dump.rb +0 -297
  66. data/lib/arjdbc/tasks/oracle_database_tasks.rb +0 -65
  67. data/src/java/arjdbc/oracle/OracleModule.java +0 -75
  68. data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +0 -465
@@ -1,465 +0,0 @@
1
- /***** BEGIN LICENSE BLOCK *****
2
- * Copyright (c) 2012-2014 Karol Bucek <self@kares.org>
3
- * Copyright (c) 2006-2010 Nick Sieger <nick@nicksieger.com>
4
- * Copyright (c) 2006-2007 Ola Bini <ola.bini@gmail.com>
5
- * Copyright (c) 2008-2009 Thomas E Enebo <enebo@acm.org>
6
- *
7
- * Permission is hereby granted, free of charge, to any person obtaining
8
- * a copy of this software and associated documentation files (the
9
- * "Software"), to deal in the Software without restriction, including
10
- * without limitation the rights to use, copy, modify, merge, publish,
11
- * distribute, sublicense, and/or sell copies of the Software, and to
12
- * permit persons to whom the Software is furnished to do so, subject to
13
- * the following conditions:
14
- *
15
- * The above copyright notice and this permission notice shall be
16
- * included in all copies or substantial portions of the Software.
17
- *
18
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22
- * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23
- * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
- ***** END LICENSE BLOCK *****/
26
- // NOTE: file contains code adapted from **oracle-enhanced** adapter, license follows
27
- /*
28
- Copyright (c) 2008-2011 Graham Jenkins, Michael Schoen, Raimonds Simanovskis
29
-
30
- ... LICENSING TERMS ARE THE VERY SAME AS ACTIVERECORD-JDBC-ADAPTER'S ABOVE ...
31
- */
32
- package arjdbc.oracle;
33
-
34
- import arjdbc.jdbc.Callable;
35
- import arjdbc.jdbc.RubyJdbcConnection;
36
- import arjdbc.util.CallResultSet;
37
-
38
- import java.io.IOException;
39
- import java.io.Reader;
40
- import java.sql.CallableStatement;
41
- import java.sql.Connection;
42
- import java.sql.ResultSet;
43
- import java.sql.SQLException;
44
- import java.sql.DatabaseMetaData;
45
- import java.sql.PreparedStatement;
46
- import java.sql.ResultSetMetaData;
47
- import java.sql.Statement;
48
- import java.sql.Types;
49
- import java.util.Collections;
50
- import java.util.List;
51
- import java.util.regex.Pattern;
52
-
53
- import org.jruby.Ruby;
54
- import org.jruby.RubyArray;
55
- import org.jruby.RubyClass;
56
- import org.jruby.RubyString;
57
- import org.jruby.anno.JRubyMethod;
58
- import org.jruby.runtime.ObjectAllocator;
59
- import org.jruby.runtime.ThreadContext;
60
- import org.jruby.runtime.builtin.IRubyObject;
61
-
62
- /**
63
- *
64
- * @author nicksieger
65
- */
66
- public class OracleRubyJdbcConnection extends RubyJdbcConnection {
67
- private static final long serialVersionUID = -6469731781108431512L;
68
-
69
- protected OracleRubyJdbcConnection(Ruby runtime, RubyClass metaClass) {
70
- super(runtime, metaClass);
71
- }
72
-
73
- public static RubyClass createOracleJdbcConnectionClass(Ruby runtime, RubyClass jdbcConnection) {
74
- final RubyClass clazz = RubyJdbcConnection.getConnectionAdapters(runtime).
75
- defineClassUnder("OracleJdbcConnection", jdbcConnection, ORACLE_JDBCCONNECTION_ALLOCATOR);
76
- clazz.defineAnnotatedMethods(OracleRubyJdbcConnection.class);
77
- return clazz;
78
- }
79
-
80
- private static ObjectAllocator ORACLE_JDBCCONNECTION_ALLOCATOR = new ObjectAllocator() {
81
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
82
- return new OracleRubyJdbcConnection(runtime, klass);
83
- }
84
- };
85
-
86
- @JRubyMethod(name = "next_sequence_value", required = 1)
87
- public IRubyObject next_sequence_value(final ThreadContext context, final IRubyObject sequence) {
88
- return withConnection(context, new Callable<IRubyObject>() {
89
- public IRubyObject call(final Connection connection) throws SQLException {
90
- Statement statement = null; ResultSet value = null;
91
- try {
92
- statement = connection.createStatement();
93
- value = statement.executeQuery("SELECT "+ sequence +".NEXTVAL id FROM dual");
94
- if ( ! value.next() ) return context.getRuntime().getNil();
95
- return context.getRuntime().newFixnum( value.getLong(1) );
96
- }
97
- catch (final SQLException e) {
98
- debugMessage(context, "failed to get " + sequence + ".NEXTVAL : " + e.getMessage());
99
- throw e;
100
- }
101
- finally { close(value); close(statement); }
102
- }
103
- });
104
- }
105
-
106
- @JRubyMethod(name = "execute_insert_returning", required = 2)
107
- public IRubyObject execute_insert_returning(final ThreadContext context,
108
- final IRubyObject sql, final IRubyObject binds) {
109
- final String query = sql.convertToString().getUnicodeValue();
110
- final int outType = Types.VARCHAR;
111
- if ( binds == null || binds.isNil() ) { // no prepared statements
112
- return executePreparedCall(context, query, Collections.EMPTY_LIST, outType);
113
- }
114
- // allow prepared statements with empty binds parameters
115
- return executePreparedCall(context, query, (List) binds, outType);
116
- }
117
-
118
- private IRubyObject executePreparedCall(final ThreadContext context, final String query,
119
- final List<?> binds, final int outType) {
120
- return withConnection(context, new Callable<IRubyObject>() {
121
- public IRubyObject call(final Connection connection) throws SQLException {
122
- CallableStatement statement = null;
123
- final int outIndex = binds.size() + 1;
124
- try {
125
- statement = connection.prepareCall("{call " + query + " }");
126
- setStatementParameters(context, connection, statement, binds);
127
- statement.registerOutParameter(outIndex, outType);
128
- statement.executeUpdate();
129
- ResultSet resultSet = new CallResultSet(statement);
130
- return jdbcToRuby(context, context.getRuntime(), outIndex, outType, resultSet);
131
- }
132
- catch (final SQLException e) {
133
- debugErrorSQL(context, query);
134
- throw e;
135
- }
136
- finally { close(statement); }
137
- }
138
- });
139
- }
140
-
141
- protected static final boolean generatedKeys;
142
- static {
143
- final String genKeys = System.getProperty("arjdbc.oracle.generated_keys");
144
- if ( genKeys == null ) {
145
- generatedKeys = true; // by default
146
- }
147
- else {
148
- generatedKeys = Boolean.parseBoolean(genKeys);
149
- }
150
- }
151
-
152
- @Override
153
- protected IRubyObject mapGeneratedKeys(
154
- final Ruby runtime, final Connection connection,
155
- final Statement statement, final Boolean singleResult)
156
- throws SQLException {
157
- if ( generatedKeys ) {
158
- return super.mapGeneratedKeys(runtime, connection, statement, singleResult);
159
- }
160
- return null; // disabled using -Darjdbc.oracle.generated_keys=false
161
- }
162
-
163
- private static final boolean returnRowID = Boolean.getBoolean("arjdbc.oracle.generated_keys.rowid");
164
-
165
- @Override // NOTE: Invalid column type:
166
- // getLong not implemented for class oracle.jdbc.driver.T4CRowidAccessor
167
- protected IRubyObject mapGeneratedKey(final Ruby runtime, final ResultSet genKeys)
168
- throws SQLException {
169
- // NOTE: it's likely a ROWID which we do not care about :
170
- final String value = genKeys.getString(1); // "AAAsOjAAFAAABUlAAA"
171
- if ( isPositiveInteger(value) ) {
172
- return runtime.newFixnum( Long.parseLong(value) );
173
- }
174
- else {
175
- return returnRowID ? runtime.newString(value) : runtime.getNil();
176
- }
177
- }
178
-
179
- private static boolean isPositiveInteger(final String value) {
180
- for ( int i = 0; i < value.length(); i++ ) {
181
- if ( ! Character.isDigit(value.charAt(i)) ) return false;
182
- }
183
- return true;
184
- }
185
-
186
- @Override // resultSet.wasNull() might be falsy for '' treated as null
187
- protected IRubyObject stringToRuby(final ThreadContext context,
188
- final Ruby runtime, final ResultSet resultSet, final int column)
189
- throws SQLException {
190
- final String value = resultSet.getString(column);
191
- if ( value == null ) return runtime.getNil();
192
- return RubyString.newUnicodeString(runtime, value);
193
- }
194
-
195
- @Override
196
- protected IRubyObject readerToRuby(final ThreadContext context,
197
- final Ruby runtime, final ResultSet resultSet, final int column)
198
- throws SQLException, IOException {
199
- final Reader reader = resultSet.getCharacterStream(column);
200
- try {
201
- if ( resultSet.wasNull() ) return RubyString.newEmptyString(runtime);
202
-
203
- final int bufSize = streamBufferSize;
204
- final StringBuilder string = new StringBuilder(bufSize);
205
-
206
- final char[] buf = new char[ bufSize / 2 ];
207
- for (int len = reader.read(buf); len != -1; len = reader.read(buf)) {
208
- string.append(buf, 0, len);
209
- }
210
-
211
- return RubyString.newUnicodeString(runtime, string.toString());
212
- }
213
- finally { if ( reader != null ) reader.close(); }
214
- }
215
-
216
- @Override // booleans are emulated can not setNull(index, Types.BOOLEAN)
217
- protected void setBooleanParameter(final ThreadContext context,
218
- final Connection connection, final PreparedStatement statement,
219
- final int index, final Object value,
220
- final IRubyObject column, final int type) throws SQLException {
221
- if ( value instanceof IRubyObject ) {
222
- setBooleanParameter(context, connection, statement, index, (IRubyObject) value, column, type);
223
- }
224
- else {
225
- if ( value == null ) statement.setNull(index, Types.TINYINT);
226
- else {
227
- statement.setBoolean(index, ((Boolean) value).booleanValue());
228
- }
229
- }
230
- }
231
-
232
- @Override // booleans are emulated can not setNull(index, Types.BOOLEAN)
233
- protected void setBooleanParameter(final ThreadContext context,
234
- final Connection connection, final PreparedStatement statement,
235
- final int index, final IRubyObject value,
236
- final IRubyObject column, final int type) throws SQLException {
237
- if ( value.isNil() ) statement.setNull(index, Types.TINYINT);
238
- else {
239
- statement.setBoolean(index, value.isTrue());
240
- }
241
- }
242
-
243
- /**
244
- * Oracle needs this override to reconstruct NUMBER which is different
245
- * from NUMBER(x) or NUMBER(x,y).
246
- */
247
- @Override
248
- protected String typeFromResultSet(final ResultSet resultSet) throws SQLException {
249
- int precision = intFromResultSet(resultSet, COLUMN_SIZE);
250
- int scale = intFromResultSet(resultSet, DECIMAL_DIGITS);
251
-
252
- // According to http://forums.oracle.com/forums/thread.jspa?threadID=658646
253
- // Unadorned NUMBER reports scale == null, so we look for that here.
254
- if ( scale < 0 && resultSet.getInt(DATA_TYPE) == java.sql.Types.DECIMAL ) {
255
- precision = -1;
256
- }
257
-
258
- final String type = resultSet.getString(TYPE_NAME);
259
- return formatTypeWithPrecisionAndScale(type, precision, scale);
260
- }
261
-
262
- @Override
263
- protected RubyArray mapTables(final Ruby runtime, final DatabaseMetaData metaData,
264
- final String catalog, final String schemaPattern, final String tablePattern,
265
- final ResultSet tablesSet) throws SQLException {
266
- final RubyArray tables = RubyArray.newArray(runtime, 32);
267
- while ( tablesSet.next() ) {
268
- String name = tablesSet.getString(TABLES_TABLE_NAME);
269
- name = caseConvertIdentifierForRails(metaData, name);
270
- // Handle stupid Oracle 10g RecycleBin feature
271
- if ( name.startsWith("bin$") ) continue;
272
- tables.append(RubyString.newUnicodeString(runtime, name));
273
- }
274
- return tables;
275
- }
276
-
277
- @Override
278
- protected ColumnData[] extractColumns(final Ruby runtime,
279
- final Connection connection, final ResultSet resultSet,
280
- final boolean downCase) throws SQLException {
281
-
282
- final ResultSetMetaData resultMetaData = resultSet.getMetaData();
283
-
284
- final int columnCount = resultMetaData.getColumnCount();
285
- final ColumnData[] columns = new ColumnData[columnCount];
286
-
287
- for ( int i = 1; i <= columnCount; i++ ) { // metadata is one-based
288
- String name = resultMetaData.getColumnLabel(i);
289
- if ( downCase ) {
290
- name = name.toLowerCase();
291
- } else {
292
- name = caseConvertIdentifierForRails(connection, name);
293
- }
294
- final RubyString columnName = RubyString.newUnicodeString(runtime, name);
295
-
296
- int columnType = resultMetaData.getColumnType(i);
297
- if (columnType == Types.NUMERIC) {
298
- // avoid extracting all NUMBER columns as BigDecimal :
299
- if (resultMetaData.getScale(i) == 0) {
300
- final int prec = resultMetaData.getPrecision(i);
301
- if ( prec < 10 ) { // fits into int
302
- columnType = Types.INTEGER;
303
- }
304
- else if ( prec < 19 ) { // fits into long
305
- columnType = Types.BIGINT;
306
- }
307
- }
308
- }
309
-
310
- columns[i - 1] = new ColumnData(columnName, columnType, i);
311
- }
312
-
313
- return columns;
314
- }
315
-
316
- // storesMixedCaseIdentifiers() return false;
317
- // storesLowerCaseIdentifiers() return false;
318
- // storesUpperCaseIdentifiers() return true;
319
-
320
- @Override
321
- protected String caseConvertIdentifierForRails(final Connection connection, final String value)
322
- throws SQLException {
323
- return value == null ? null : value.toLowerCase();
324
- }
325
-
326
- @Override
327
- protected String caseConvertIdentifierForJdbc(final Connection connection, final String value)
328
- throws SQLException {
329
- return value == null ? null : value.toUpperCase();
330
- }
331
-
332
- // based on OracleEnhanced's Ruby connection.describe
333
- @JRubyMethod(name = "describe", required = 1)
334
- public IRubyObject describe(final ThreadContext context, final IRubyObject name) {
335
- final RubyArray desc = describe(context, name.toString(), null);
336
- return desc == null ? context.nil : desc; // TODO raise instead of nil
337
- }
338
-
339
- @JRubyMethod(name = "describe", required = 2)
340
- public IRubyObject describe(final ThreadContext context, final IRubyObject name, final IRubyObject owner) {
341
- final RubyArray desc = describe(context, name.toString(), owner.isNil() ? null : owner.toString());
342
- return desc == null ? context.nil : desc; // TODO raise instead of nil
343
- }
344
-
345
- private RubyArray describe(final ThreadContext context, final String name, final String owner) {
346
- final String dbLink; String defaultOwner, theName = name; int delim;
347
- if ( ( delim = theName.indexOf('@') ) > 0 ) {
348
- dbLink = theName.substring(delim).toUpperCase(); // '@DBLINK'
349
- theName = theName.substring(0, delim);
350
- defaultOwner = null; // will SELECT username FROM all_dbLinks ...
351
- }
352
- else {
353
- dbLink = ""; defaultOwner = owner; // config[:username] || meta_data.user_name
354
- }
355
-
356
- theName = isValidTableName(theName) ? theName.toUpperCase() : unquoteTableName(theName);
357
-
358
- final String tableName; final String tableOwner;
359
- if ( ( delim = theName.indexOf('.') ) > 0 ) {
360
- tableOwner = theName.substring(0, delim);
361
- tableName = theName.substring(delim + 1);
362
- }
363
- else {
364
- tableName = theName;
365
- tableOwner = (defaultOwner == null && dbLink.length() > 0) ? selectOwner(context, dbLink) : defaultOwner;
366
- }
367
-
368
- return withConnection(context, new Callable<RubyArray>() {
369
- public RubyArray call(final Connection connection) throws SQLException {
370
- String owner = tableOwner == null ? connection.getMetaData().getUserName() : tableOwner;
371
- final String sql =
372
- "SELECT owner, table_name, 'TABLE' name_type" +
373
- " FROM all_tables" + dbLink +
374
- " WHERE owner = '" + owner + "' AND table_name = '" + tableName + "'" +
375
- " UNION ALL " +
376
- "SELECT owner, view_name table_name, 'VIEW' name_type" +
377
- " FROM all_views" + dbLink +
378
- " WHERE owner = '" + owner + "' AND view_name = '" + tableName + "'" +
379
- " UNION ALL " +
380
- "SELECT table_owner, DECODE(db_link, NULL, table_name, table_name||'@'||db_link), 'SYNONYM' name_type" +
381
- " FROM all_synonyms" + dbLink +
382
- " WHERE owner = '" + owner + "' AND synonym_name = '" + tableName + "'" +
383
- " UNION ALL " +
384
- "SELECT table_owner, DECODE(db_link, NULL, table_name, table_name||'@'||db_link), 'SYNONYM' name_type" +
385
- " FROM all_synonyms" + dbLink +
386
- " WHERE owner = 'PUBLIC' AND synonym_name = '" + tableName + "'" ;
387
-
388
- Statement statement = null; ResultSet result = null;
389
- try {
390
- statement = connection.createStatement();
391
- result = statement.executeQuery(sql);
392
-
393
- if ( ! result.next() ) return null; // NOTE: should raise
394
-
395
- owner = result.getString("owner");
396
- final String table_name = result.getString("table_name");
397
- final String name_type = result.getString("name_type");
398
-
399
- if ( "SYNONYM".equals(name_type) ) {
400
- final StringBuilder name = new StringBuilder();
401
- if ( owner != null && owner.length() > 0 ) {
402
- name.append(owner).append('.');
403
- }
404
- name.append(table_name);
405
- if ( dbLink != null ) name.append(dbLink);
406
- return describe(context, name.toString(), owner);
407
- }
408
-
409
- final RubyArray arr = RubyArray.newArray(context.runtime, 3);
410
- arr.append( context.runtime.newString(owner) );
411
- arr.append( context.runtime.newString(table_name) );
412
- if ( dbLink != null ) arr.append( context.runtime.newString(dbLink) );
413
- return arr;
414
- }
415
- catch (final SQLException e) {
416
- debugMessage(context, "failed to describe '" + name + "' : " + e.getMessage());
417
- throw e;
418
- }
419
- finally { close(result); close(statement); }
420
- }
421
- });
422
- }
423
-
424
- private String selectOwner(final ThreadContext context, final String dbLink) {
425
- return withConnection(context, new Callable<String>() {
426
- public String call(final Connection connection) throws SQLException {
427
- Statement statement = null; ResultSet result = null;
428
- final String sql = "SELECT username FROM all_db_links WHERE db_link = '" + dbLink + "'";
429
- try {
430
- statement = connection.createStatement();
431
- result = statement.executeQuery(sql);
432
- // if ( ! result.next() ) return null;
433
- return result.getString(1);
434
- }
435
- catch (final SQLException e) {
436
- debugMessage(context, "\"" + sql + "\" failed : " + e.getMessage());
437
- throw e;
438
- }
439
- finally { close(result); close(statement); }
440
- }
441
- });
442
- }
443
-
444
- private static final Pattern VALID_TABLE_NAME;
445
- static {
446
- final String NONQUOTED_OBJECT_NAME = "[A-Za-z][A-z0-9$#]{0,29}";
447
- final String NONQUOTED_DATABASE_LINK = "[A-Za-z][A-z0-9$#\\.@]{0,127}";
448
- VALID_TABLE_NAME = Pattern.compile(
449
- "\\A(?:" + NONQUOTED_OBJECT_NAME + "\\.)?" + NONQUOTED_OBJECT_NAME + "(?:@" + NONQUOTED_DATABASE_LINK + ")?\\Z");
450
- }
451
-
452
- private static boolean isValidTableName(final String name) {
453
- return VALID_TABLE_NAME.matcher(name).matches();
454
- }
455
-
456
- private static String unquoteTableName(String name) {
457
- name = name.trim();
458
- final int len = name.length();
459
- if (len > 0 && name.charAt(0) == '"' && name.charAt(len - 1) == '"') {
460
- return name.substring(1, len - 1);
461
- }
462
- return name;
463
- }
464
-
465
- }