activerecord-jdbc-adapter 5.0.pre1 → 50.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord-jdbc-adapter might be problematic. Click here for more details.

Files changed (66) 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/lib/arjdbc/abstract/connection_management.rb +21 -0
  9. data/lib/arjdbc/abstract/core.rb +62 -0
  10. data/lib/arjdbc/abstract/database_statements.rb +46 -0
  11. data/lib/arjdbc/abstract/statement_cache.rb +58 -0
  12. data/lib/arjdbc/abstract/transaction_support.rb +86 -0
  13. data/lib/arjdbc/derby/adapter.rb +6 -1
  14. data/lib/arjdbc/discover.rb +0 -7
  15. data/lib/arjdbc/firebird/adapter.rb +2 -2
  16. data/lib/arjdbc/jdbc.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/mysql/adapter.rb +82 -946
  21. data/lib/arjdbc/mysql/connection_methods.rb +4 -2
  22. data/lib/arjdbc/postgresql/adapter.rb +270 -970
  23. data/lib/arjdbc/postgresql/base/array_decoder.rb +26 -0
  24. data/lib/arjdbc/postgresql/base/array_encoder.rb +25 -0
  25. data/lib/arjdbc/postgresql/base/pgconn.rb +8 -5
  26. data/lib/arjdbc/postgresql/column.rb +10 -599
  27. data/lib/arjdbc/postgresql/connection_methods.rb +9 -0
  28. data/lib/arjdbc/postgresql/name.rb +24 -0
  29. data/lib/arjdbc/postgresql/oid_types.rb +28 -109
  30. data/lib/arjdbc/sqlite3/adapter.rb +18 -42
  31. data/lib/arjdbc/tasks/database_tasks.rb +1 -3
  32. data/lib/arjdbc/tasks/db2_database_tasks.rb +2 -2
  33. data/lib/arjdbc/version.rb +1 -1
  34. data/pom.xml +3 -3
  35. data/rakelib/02-test.rake +0 -12
  36. data/rakelib/compile.rake +1 -1
  37. data/rakelib/db.rake +7 -5
  38. data/rakelib/rails.rake +67 -64
  39. data/src/java/arjdbc/firebird/FirebirdRubyJdbcConnection.java +1 -17
  40. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +518 -1260
  41. data/src/java/arjdbc/mysql/MySQLModule.java +3 -3
  42. data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +53 -134
  43. data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +214 -240
  44. data/src/java/arjdbc/sqlite3/SQLite3Module.java +0 -20
  45. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +85 -10
  46. metadata +16 -29
  47. data/Appraisals +0 -41
  48. data/lib/active_record/connection_adapters/oracle_adapter.rb +0 -1
  49. data/lib/arjdbc/common_jdbc_methods.rb +0 -89
  50. data/lib/arjdbc/mysql/bulk_change_table.rb +0 -150
  51. data/lib/arjdbc/mysql/column.rb +0 -162
  52. data/lib/arjdbc/mysql/explain_support.rb +0 -82
  53. data/lib/arjdbc/mysql/schema_creation.rb +0 -58
  54. data/lib/arjdbc/oracle.rb +0 -4
  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/postgresql/base/oid.rb +0 -412
  59. data/lib/arjdbc/postgresql/base/schema_definitions.rb +0 -131
  60. data/lib/arjdbc/postgresql/explain_support.rb +0 -53
  61. data/lib/arjdbc/postgresql/oid/bytea.rb +0 -2
  62. data/lib/arjdbc/postgresql/schema_creation.rb +0 -60
  63. data/lib/arjdbc/tasks/oracle/enhanced_structure_dump.rb +0 -297
  64. data/lib/arjdbc/tasks/oracle_database_tasks.rb +0 -65
  65. data/src/java/arjdbc/oracle/OracleModule.java +0 -75
  66. data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +0 -465
@@ -1,65 +0,0 @@
1
- require 'arjdbc/tasks/jdbc_database_tasks'
2
-
3
- module ArJdbc
4
- module Tasks
5
- class OracleDatabaseTasks < JdbcDatabaseTasks
6
-
7
- def create
8
- print "Please provide the SYSTEM password for your oracle installation\n>"
9
- system_password = $stdin.gets.strip
10
- establish_connection(config.merge('username' => 'SYSTEM', 'password' => system_password))
11
- unless ( config = self.config ).key?('username')
12
- config = config_from_url(config['url']) if config['url']
13
- end
14
- begin
15
- connection.execute "CREATE USER #{config['username']} IDENTIFIED BY #{config['password']}"
16
- rescue => e
17
- if e.message =~ /ORA-01920/ # user name conflicts with another user or role name
18
- connection.execute "ALTER USER #{config['username']} IDENTIFIED BY #{config['password']}"
19
- else
20
- raise e
21
- end
22
- end
23
- connection.execute "GRANT unlimited tablespace TO #{config['username']}"
24
- connection.execute "GRANT create session TO #{config['username']}"
25
- connection.execute "GRANT create table TO #{config['username']}"
26
- connection.execute "GRANT create sequence TO #{config['username']}"
27
- end
28
-
29
- def drop
30
- self.class.load_enhanced_structure_dump
31
- establish_connection(config)
32
- connection.execute_structure_dump(connection.full_drop)
33
- end
34
-
35
- def purge
36
- self.class.load_enhanced_structure_dump
37
- establish_connection(:test)
38
- connection.execute_structure_dump(connection.full_drop)
39
- connection.execute("PURGE RECYCLEBIN") rescue nil
40
- end
41
-
42
- def structure_dump(filename)
43
- self.class.load_enhanced_structure_dump
44
- establish_connection(config)
45
- File.open(filename, "w:utf-8") { |f| f << connection.structure_dump }
46
- end
47
-
48
- def structure_load(filename)
49
- self.class.load_enhanced_structure_dump
50
- establish_connection(config)
51
- connection.execute_structure_dump(File.read(filename))
52
- end
53
-
54
- def self.load_enhanced_structure_dump
55
- unless defined? ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter
56
- ActiveRecord::ConnectionAdapters.module_eval do
57
- const_set :OracleEnhancedAdapter, ActiveRecord::ConnectionAdapters::OracleAdapter
58
- end
59
- end
60
- require 'arjdbc/tasks/oracle/enhanced_structure_dump'
61
- end
62
-
63
- end
64
- end
65
- end
@@ -1,75 +0,0 @@
1
- /*
2
- * The MIT License
3
- *
4
- * Copyright 2013-2014 Karol Bucek.
5
- *
6
- * Permission is hereby granted, free of charge, to any person obtaining a copy
7
- * of this software and associated documentation files (the "Software"), to deal
8
- * in the Software without restriction, including without limitation the rights
9
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
- * copies of the Software, and to permit persons to whom the Software is
11
- * furnished to do so, subject to the following conditions:
12
- *
13
- * The above copyright notice and this permission notice shall be included in
14
- * all copies or substantial portions of the Software.
15
- *
16
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
- * THE SOFTWARE.
23
- */
24
- package arjdbc.oracle;
25
-
26
- import static arjdbc.util.QuotingUtils.BYTES_0;
27
- import static arjdbc.util.QuotingUtils.BYTES_1;
28
- import static arjdbc.util.QuotingUtils.quoteCharWith;
29
-
30
- import org.jruby.RubyModule;
31
- import org.jruby.RubyString;
32
- import org.jruby.anno.JRubyMethod;
33
- import org.jruby.runtime.ThreadContext;
34
- import org.jruby.runtime.builtin.IRubyObject;
35
-
36
- /**
37
- * ArJdbc::Oracle
38
- *
39
- * @author kares
40
- */
41
- public class OracleModule {
42
-
43
- public static RubyModule load(final RubyModule arJdbc) {
44
- RubyModule oracle = arJdbc.defineModuleUnder("Oracle");
45
- oracle.defineAnnotatedMethods( OracleModule.class );
46
- return oracle;
47
- }
48
-
49
- @JRubyMethod(name = "quote_string", required = 1)
50
- public static IRubyObject quote_string(
51
- final ThreadContext context,
52
- final IRubyObject self,
53
- final IRubyObject string) { // string.gsub("'", "''") :
54
- final char single = '\'';
55
- final RubyString quoted = quoteCharWith(
56
- context, (RubyString) string, single, single
57
- );
58
- return quoted;
59
- }
60
-
61
- @JRubyMethod(name = "quoted_true", required = 0)
62
- public static IRubyObject quoted_true(
63
- final ThreadContext context,
64
- final IRubyObject self) {
65
- return RubyString.newString(context.getRuntime(), BYTES_1);
66
- }
67
-
68
- @JRubyMethod(name = "quoted_false", required = 0)
69
- public static IRubyObject quoted_false(
70
- final ThreadContext context,
71
- final IRubyObject self) {
72
- return RubyString.newString(context.getRuntime(), BYTES_0);
73
- }
74
-
75
- }
@@ -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
- }