activerecord-jdbc-adapter 1.2.9.1 → 1.3.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +3 -0
- data/Appraisals +12 -4
- data/Gemfile +3 -3
- data/Gemfile.lock +19 -19
- data/History.txt +90 -16
- data/LICENSE.txt +2 -1
- data/README.md +14 -1
- data/activerecord-jdbc-adapter.gemspec +2 -2
- data/gemfiles/rails23.gemfile +5 -5
- data/gemfiles/rails23.gemfile.lock +27 -27
- data/gemfiles/rails30.gemfile +3 -3
- data/gemfiles/rails30.gemfile.lock +8 -8
- data/gemfiles/rails31.gemfile +4 -4
- data/gemfiles/rails31.gemfile.lock +18 -18
- data/gemfiles/rails32.gemfile +4 -4
- data/gemfiles/rails32.gemfile.lock +17 -17
- data/gemfiles/rails40.gemfile +17 -0
- data/gemfiles/rails40.gemfile.lock +126 -0
- data/lib/activerecord-jdbc-adapter.rb +0 -7
- data/lib/arjdbc.rb +6 -5
- data/lib/arjdbc/db2.rb +1 -1
- data/lib/arjdbc/db2/adapter.rb +52 -29
- data/lib/arjdbc/db2/connection_methods.rb +13 -14
- data/lib/arjdbc/derby.rb +1 -1
- data/lib/arjdbc/derby/adapter.rb +29 -9
- data/lib/arjdbc/derby/connection_methods.rb +17 -20
- data/lib/arjdbc/firebird.rb +1 -1
- data/lib/arjdbc/h2.rb +2 -2
- data/lib/arjdbc/h2/adapter.rb +1 -1
- data/lib/arjdbc/h2/connection_methods.rb +12 -16
- data/lib/arjdbc/hsqldb.rb +1 -1
- data/lib/arjdbc/hsqldb/connection_methods.rb +13 -16
- data/lib/arjdbc/informix.rb +1 -1
- data/lib/arjdbc/informix/connection_methods.rb +8 -10
- data/lib/arjdbc/jdbc.rb +1 -1
- data/lib/arjdbc/jdbc/adapter.rb +125 -53
- data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
- data/lib/arjdbc/jdbc/base_ext.rb +34 -9
- data/lib/arjdbc/jdbc/column.rb +15 -2
- data/lib/arjdbc/jdbc/connection.rb +0 -2
- data/lib/arjdbc/jdbc/connection_methods.rb +10 -3
- data/lib/arjdbc/jdbc/driver.rb +2 -2
- data/lib/arjdbc/jdbc/extension.rb +35 -21
- data/lib/arjdbc/jdbc/java.rb +0 -2
- data/lib/arjdbc/jdbc/missing_functionality_helper.rb +35 -25
- data/lib/arjdbc/jdbc/railtie.rb +2 -9
- data/lib/arjdbc/mimer.rb +1 -1
- data/lib/arjdbc/mssql.rb +2 -2
- data/lib/arjdbc/mssql/adapter.rb +271 -92
- data/lib/arjdbc/mssql/connection_methods.rb +30 -32
- data/lib/arjdbc/mssql/explain_support.rb +107 -0
- data/lib/arjdbc/mssql/limit_helpers.rb +48 -18
- data/lib/arjdbc/mysql.rb +1 -1
- data/lib/arjdbc/mysql/adapter.rb +63 -14
- data/lib/arjdbc/mysql/connection_methods.rb +22 -24
- data/lib/arjdbc/mysql/explain_support.rb +2 -5
- data/lib/arjdbc/oracle.rb +1 -1
- data/lib/arjdbc/oracle/adapter.rb +78 -38
- data/lib/arjdbc/oracle/connection_methods.rb +9 -10
- data/lib/arjdbc/postgresql.rb +1 -1
- data/lib/arjdbc/postgresql/adapter.rb +964 -380
- data/lib/arjdbc/postgresql/column_cast.rb +136 -0
- data/lib/arjdbc/postgresql/connection_methods.rb +19 -21
- data/lib/arjdbc/postgresql/explain_support.rb +3 -6
- data/lib/arjdbc/railtie.rb +9 -0
- data/lib/arjdbc/sqlite3.rb +1 -1
- data/lib/arjdbc/sqlite3/adapter.rb +73 -26
- data/lib/arjdbc/sqlite3/connection_methods.rb +27 -28
- data/lib/arjdbc/sqlite3/explain_support.rb +2 -5
- data/lib/arjdbc/sybase.rb +1 -1
- data/lib/arjdbc/version.rb +5 -4
- data/pom.xml +8 -0
- data/rakelib/02-test.rake +57 -51
- data/rakelib/compile.rake +17 -5
- data/rakelib/rails.rake +42 -31
- data/src/java/arjdbc/db2/DB2RubyJdbcConnection.java +4 -3
- data/src/java/arjdbc/derby/DerbyModule.java +98 -85
- data/src/java/arjdbc/derby/DerbyRubyJdbcConnection.java +70 -0
- data/src/java/arjdbc/h2/H2RubyJdbcConnection.java +0 -4
- data/src/java/arjdbc/jdbc/AdapterJavaService.java +26 -15
- data/src/java/arjdbc/jdbc/Callable.java +44 -0
- data/src/java/arjdbc/jdbc/JdbcConnectionFactory.java +10 -2
- data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +1675 -834
- data/src/java/arjdbc/jdbc/SQLBlock.java +9 -3
- data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +73 -36
- data/src/java/arjdbc/mysql/MySQLModule.java +11 -10
- data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +86 -80
- data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +27 -7
- data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +214 -0
- data/src/java/arjdbc/postgresql/PostgresqlRubyJdbcConnection.java +25 -67
- data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +52 -49
- data/src/java/arjdbc/util/QuotingUtils.java +6 -6
- data/test/abstract_db_create.rb +11 -11
- data/test/activerecord/connection_adapters/type_conversion_test.rb +18 -12
- data/test/db/db2.rb +1 -1
- data/test/{db2_binary_test.rb → db/db2/binary_test.rb} +0 -0
- data/test/db/db2/has_many_through_test.rb +6 -0
- data/test/{db2_reset_column_information_test.rb → db/db2/reset_column_information_test.rb} +1 -2
- data/test/{db2_serialize_test.rb → db/db2/serialize_test.rb} +0 -0
- data/test/{db2_simple_test.rb → db/db2/simple_test.rb} +1 -8
- data/test/db/db2/test_helper.rb +6 -0
- data/test/{db2_test.rb → db/db2/unit_test.rb} +1 -1
- data/test/db/derby.rb +1 -1
- data/test/{derby_binary_test.rb → db/derby/binary_test.rb} +0 -0
- data/test/{derby_migration_test.rb → db/derby/migration_test.rb} +0 -0
- data/test/{derby_reset_column_information_test.rb → db/derby/reset_column_information_test.rb} +0 -0
- data/test/{derby_row_locking_test.rb → db/derby/row_locking_test.rb} +1 -4
- data/test/db/derby/schema_dump_test.rb +5 -0
- data/test/{derby_serialize_test.rb → db/derby/serialize_test.rb} +0 -0
- data/test/{derby_simple_test.rb → db/derby/simple_test.rb} +23 -38
- data/test/db/derby/test_helper.rb +6 -0
- data/test/db/derby/unit_test.rb +32 -0
- data/test/db/derby/xml_column_test.rb +17 -0
- data/test/db/h2.rb +1 -1
- data/test/{h2_binary_test.rb → db/h2/binary_test.rb} +0 -0
- data/test/{h2_change_column_test.rb → db/h2/change_column_test.rb} +1 -0
- data/test/{h2_schema_dump_test.rb → db/h2/schema_dump_test.rb} +0 -0
- data/test/{h2_serialize_test.rb → db/h2/serialize_test.rb} +0 -0
- data/test/{h2_simple_test.rb → db/h2/simple_test.rb} +3 -1
- data/test/db/hsqldb.rb +1 -1
- data/test/{hsqldb_binary_test.rb → db/hsqldb/binary_test.rb} +0 -0
- data/test/{hsqldb_schema_dump_test.rb → db/hsqldb/schema_dump_test.rb} +0 -0
- data/test/{hsqldb_serialize_test.rb → db/hsqldb/serialize_test.rb} +0 -0
- data/test/{hsqldb_simple_test.rb → db/hsqldb/simple_test.rb} +3 -1
- data/test/db/informix.rb +1 -1
- data/test/db/jdbc.rb +3 -2
- data/test/db/jdbc_derby.rb +1 -1
- data/test/db/jdbc_h2.rb +1 -1
- data/test/db/jdbc_mysql.rb +1 -1
- data/test/db/jdbc_postgres.rb +1 -1
- data/test/db/jndi_config.rb +1 -2
- data/test/db/jndi_pooled_config.rb +2 -3
- data/test/db/mssql.rb +2 -2
- data/test/{mssql_binary_test.rb → db/mssql/binary_test.rb} +0 -0
- data/test/{mssql_db_create_test.rb → db/mssql/db_create_test.rb} +1 -1
- data/test/db/mssql/exec_proc_test.rb +46 -0
- data/test/{mssql_identity_insert_test.rb → db/mssql/identity_insert_test.rb} +0 -0
- data/test/db/mssql/ignore_system_views_test.rb +40 -0
- data/test/{mssql_limit_offset_test.rb → db/mssql/limit_offset_test.rb} +10 -1
- data/test/{mssql_multibyte_test.rb → db/mssql/multibyte_test.rb} +0 -0
- data/test/db/mssql/multiple_connections_test.rb +71 -0
- data/test/{mssql_reset_column_information_test.rb → db/mssql/reset_column_information_test.rb} +0 -0
- data/test/{mssql_row_locking_test.rb → db/mssql/row_locking_test.rb} +0 -0
- data/test/{mssql_serialize_test.rb → db/mssql/serialize_test.rb} +1 -1
- data/test/db/mssql/simple_test.rb +140 -0
- data/test/db/mssql/transaction_test.rb +6 -0
- data/test/db/mssql/types_test.rb +205 -0
- data/test/{mssql_test.rb → db/mssql/unit_test.rb} +2 -2
- data/test/db/mysql.rb +1 -2
- data/test/db/mysql/_rails_test_mysql.32.out +6768 -0
- data/test/{mysql_binary_test.rb → db/mysql/binary_test.rb} +0 -0
- data/test/db/mysql/connection_test.rb +51 -0
- data/test/{mysql_db_create_test.rb → db/mysql/db_create_test.rb} +0 -0
- data/test/{mysql_index_length_test.rb → db/mysql/index_length_test.rb} +0 -0
- data/test/{mysql_multibyte_test.rb → db/mysql/multibyte_test.rb} +0 -0
- data/test/{mysql_nonstandard_primary_key_test.rb → db/mysql/nonstandard_primary_key_test.rb} +0 -0
- data/test/{mysql_reset_column_information_test.rb → db/mysql/reset_column_information_test.rb} +0 -0
- data/test/{mysql_schema_dump_test.rb → db/mysql/schema_dump_test.rb} +9 -1
- data/test/{mysql_serialize_test.rb → db/mysql/serialize_test.rb} +0 -0
- data/test/{mysql_simple_test.rb → db/mysql/simple_test.rb} +16 -8
- data/test/db/mysql/transaction_test.rb +6 -0
- data/test/db/mysql/types_test.rb +30 -0
- data/test/{mysql_test.rb → db/mysql/unit_test.rb} +1 -1
- data/test/db/mysql_config.rb +1 -1
- data/test/db/oracle.rb +1 -1
- data/test/{oracle_binary_test.rb → db/oracle/binary_test.rb} +0 -0
- data/test/{oracle_limit_test.rb → db/oracle/limit_test.rb} +0 -0
- data/test/db/oracle/multibyte_test.rb +22 -0
- data/test/{oracle_reset_column_information_test.rb → db/oracle/reset_column_information_test.rb} +0 -0
- data/test/{oracle_serialize_test.rb → db/oracle/serialize_test.rb} +0 -0
- data/test/{oracle_simple_test.rb → db/oracle/simple_test.rb} +14 -19
- data/test/{oracle_specific_test.rb → db/oracle/specific_test.rb} +62 -16
- data/test/db/oracle/transaction_test.rb +31 -0
- data/test/db/oracle/unit_test.rb +31 -0
- data/test/db/postgres.rb +1 -1
- data/test/db/postgres/_rails_test_postgres.32.out +6777 -0
- data/test/db/postgres/a_custom_primary_key_test.rb +50 -0
- data/test/db/postgres/array_type_test.rb +101 -0
- data/test/{postgres_binary_test.rb → db/postgres/binary_test.rb} +0 -0
- data/test/db/postgres/connection_test.rb +55 -0
- data/test/db/postgres/data_types_test.rb +703 -0
- data/test/{postgres_db_create_test.rb → db/postgres/db_create_test.rb} +1 -1
- data/test/{postgres_drop_db_test.rb → db/postgres/db_drop_test.rb} +2 -0
- data/test/db/postgres/hstore_test.rb +200 -0
- data/test/db/postgres/information_schema_leak_test.rb +30 -0
- data/test/db/postgres/json_test.rb +86 -0
- data/test/db/postgres/ltree_test.rb +50 -0
- data/test/{postgres_mixed_case_test.rb → db/postgres/mixed_case_test.rb} +0 -0
- data/test/db/postgres/native_types_test.rb +128 -0
- data/test/{postgres_reserved_test.rb → db/postgres/reserved_test.rb} +0 -0
- data/test/{postgres_reset_column_information_test.rb → db/postgres/reset_column_information_test.rb} +0 -0
- data/test/{postgres_row_locking_test.rb → db/postgres/row_locking_test.rb} +0 -0
- data/test/{postgres_schema_dump_test.rb → db/postgres/schema_dump_test.rb} +4 -4
- data/test/db/postgres/schema_test.rb +113 -0
- data/test/{postgres_simple_test.rb → db/postgres/simple_test.rb} +48 -8
- data/test/{postgres_table_alias_length_test.rb → db/postgres/table_alias_length_test.rb} +2 -1
- data/test/db/postgres/transaction_test.rb +6 -0
- data/test/{postgres_test.rb → db/postgres/unit_test.rb} +3 -3
- data/test/db/sqlite3.rb +1 -1
- data/test/db/sqlite3/_rails_test_sqlite3.32.out +6502 -0
- data/test/db/sqlite3/has_many_though_test.rb +6 -0
- data/test/{sqlite3_reset_column_information_test.rb → db/sqlite3/reset_column_information_test.rb} +0 -0
- data/test/{sqlite3_schema_dump_test.rb → db/sqlite3/schema_dump_test.rb} +0 -0
- data/test/{sqlite3_serialize_test.rb → db/sqlite3/serialize_test.rb} +0 -0
- data/test/{sqlite3_simple_test.rb → db/sqlite3/simple_test.rb} +63 -63
- data/test/db/sqlite3/transaction_test.rb +32 -0
- data/test/{sqlite3_type_conversion_test.rb → db/sqlite3/type_conversion_test.rb} +0 -0
- data/test/has_many_through.rb +29 -64
- data/test/jdbc/oracle.rb +11 -0
- data/test/jndi_test.rb +16 -4
- data/test/models/auto_id.rb +1 -1
- data/test/models/rights_and_roles.rb +57 -0
- data/test/row_locking.rb +3 -0
- data/test/schema_dump.rb +24 -10
- data/test/simple.rb +359 -104
- data/test/test_helper.rb +4 -2
- data/test/transaction.rb +109 -0
- metadata +119 -86
- data/lib/arjdbc/jdbc/compatibility.rb +0 -51
- data/lib/arjdbc/jdbc/core_ext.rb +0 -24
- data/lib/arjdbc/jdbc/discover.rb +0 -18
- data/test/derby_schema_dump_test.rb +0 -9
- data/test/mssql_ignore_system_views_test.rb +0 -30
- data/test/mssql_legacy_types_test.rb +0 -58
- data/test/mssql_null_test.rb +0 -14
- data/test/mssql_simple_test.rb +0 -51
- data/test/postgres_information_schema_leak_test.rb +0 -28
- data/test/postgres_native_type_mapping_test.rb +0 -93
- data/test/postgres_nonseq_pkey_test.rb +0 -38
- data/test/postgres_schema_search_path_test.rb +0 -48
- data/test/postgres_type_conversion_test.rb +0 -33
@@ -0,0 +1,70 @@
|
|
1
|
+
/*
|
2
|
+
* The MIT License
|
3
|
+
*
|
4
|
+
* Copyright 2013 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.derby;
|
25
|
+
|
26
|
+
import java.sql.Connection;
|
27
|
+
import java.sql.SQLException;
|
28
|
+
|
29
|
+
import org.jruby.Ruby;
|
30
|
+
import org.jruby.RubyClass;
|
31
|
+
import org.jruby.runtime.ObjectAllocator;
|
32
|
+
import org.jruby.runtime.builtin.IRubyObject;
|
33
|
+
|
34
|
+
/**
|
35
|
+
* ArJdbc::DerbyJdbcConnection
|
36
|
+
*
|
37
|
+
* @author kares
|
38
|
+
*/
|
39
|
+
public class DerbyRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection {
|
40
|
+
|
41
|
+
protected DerbyRubyJdbcConnection(Ruby runtime, RubyClass metaClass) {
|
42
|
+
super(runtime, metaClass);
|
43
|
+
}
|
44
|
+
|
45
|
+
public static RubyClass createDerbyJdbcConnectionClass(Ruby runtime, RubyClass jdbcConnection) {
|
46
|
+
final RubyClass clazz = getConnectionAdapters(runtime). // ActiveRecord::ConnectionAdapters
|
47
|
+
defineClassUnder("DerbyJdbcConnection", jdbcConnection, DERBY_JDBCCONNECTION_ALLOCATOR);
|
48
|
+
clazz.defineAnnotatedMethods(DerbyRubyJdbcConnection.class);
|
49
|
+
return clazz;
|
50
|
+
}
|
51
|
+
|
52
|
+
private static ObjectAllocator DERBY_JDBCCONNECTION_ALLOCATOR = new ObjectAllocator() {
|
53
|
+
public IRubyObject allocate(Ruby runtime, RubyClass klass) {
|
54
|
+
return new DerbyRubyJdbcConnection(runtime, klass);
|
55
|
+
}
|
56
|
+
};
|
57
|
+
|
58
|
+
@Override
|
59
|
+
protected IRubyObject matchTables(final Ruby runtime,
|
60
|
+
final Connection connection,
|
61
|
+
final String catalog, String schemaPattern,
|
62
|
+
final String tablePattern, final String[] types,
|
63
|
+
final boolean checkExistsOnly) throws SQLException {
|
64
|
+
if (schemaPattern != null && schemaPattern.equals("")) {
|
65
|
+
schemaPattern = null; // Derby doesn't like empty-string schema name
|
66
|
+
}
|
67
|
+
return super.matchTables(runtime, connection, catalog, schemaPattern, tablePattern, types, checkExistsOnly);
|
68
|
+
}
|
69
|
+
|
70
|
+
}
|
@@ -31,6 +31,7 @@ import java.io.IOException;
|
|
31
31
|
import arjdbc.db2.DB2Module;
|
32
32
|
import arjdbc.db2.DB2RubyJdbcConnection;
|
33
33
|
import arjdbc.derby.DerbyModule;
|
34
|
+
import arjdbc.derby.DerbyRubyJdbcConnection;
|
34
35
|
import arjdbc.h2.H2RubyJdbcConnection;
|
35
36
|
import arjdbc.hsqldb.HSQLDBModule;
|
36
37
|
import arjdbc.informix.InformixRubyJdbcConnection;
|
@@ -40,7 +41,7 @@ import arjdbc.mysql.MySQLModule;
|
|
40
41
|
import arjdbc.mysql.MySQLRubyJdbcConnection;
|
41
42
|
import arjdbc.oracle.OracleModule;
|
42
43
|
import arjdbc.oracle.OracleRubyJdbcConnection;
|
43
|
-
import arjdbc.postgresql.
|
44
|
+
import arjdbc.postgresql.PostgreSQLRubyJdbcConnection;
|
44
45
|
import arjdbc.sqlite3.SQLite3Module;
|
45
46
|
import arjdbc.sqlite3.SQLite3RubyJdbcConnection;
|
46
47
|
|
@@ -53,24 +54,34 @@ public class AdapterJavaService implements BasicLibraryService {
|
|
53
54
|
|
54
55
|
public boolean basicLoad(final Ruby runtime) throws IOException {
|
55
56
|
// ActiveRecord::ConnectionAdapter-s :
|
56
|
-
RubyClass jdbcConnection = RubyJdbcConnection.createJdbcConnectionClass(runtime);
|
57
|
-
|
58
|
-
|
59
|
-
InformixRubyJdbcConnection.createInformixJdbcConnectionClass(runtime, jdbcConnection);
|
60
|
-
OracleRubyJdbcConnection.createOracleJdbcConnectionClass(runtime, jdbcConnection);
|
61
|
-
SQLite3RubyJdbcConnection.createSQLite3JdbcConnectionClass(runtime, jdbcConnection);
|
62
|
-
H2RubyJdbcConnection.createH2JdbcConnectionClass(runtime, jdbcConnection);
|
63
|
-
MySQLRubyJdbcConnection.createMySQLJdbcConnectionClass(runtime, jdbcConnection);
|
64
|
-
DB2RubyJdbcConnection.createDB2JdbcConnectionClass(runtime, jdbcConnection);
|
65
|
-
// ArJdbc :
|
66
|
-
RubyModule arJdbc = runtime.getOrCreateModule("ArJdbc");
|
57
|
+
final RubyClass jdbcConnection = RubyJdbcConnection.createJdbcConnectionClass(runtime);
|
58
|
+
final RubyModule arJdbc = runtime.getOrCreateModule("ArJdbc");
|
59
|
+
// MySQL
|
67
60
|
MySQLModule.load(arJdbc);
|
68
|
-
|
69
|
-
|
70
|
-
|
61
|
+
MySQLRubyJdbcConnection.createMySQLJdbcConnectionClass(runtime, jdbcConnection);
|
62
|
+
// PostgreSQL
|
63
|
+
PostgreSQLRubyJdbcConnection.createPostgreSQLJdbcConnectionClass(runtime, jdbcConnection);
|
64
|
+
// SQLite3
|
71
65
|
SQLite3Module.load(arJdbc);
|
66
|
+
SQLite3RubyJdbcConnection.createSQLite3JdbcConnectionClass(runtime, jdbcConnection);
|
67
|
+
// Oracle
|
72
68
|
OracleModule.load(arJdbc);
|
69
|
+
OracleRubyJdbcConnection.createOracleJdbcConnectionClass(runtime, jdbcConnection);
|
70
|
+
// MS-SQL
|
71
|
+
MSSQLModule.load(arJdbc);
|
72
|
+
MSSQLRubyJdbcConnection.createMSSQLJdbcConnectionClass(runtime, jdbcConnection);
|
73
|
+
// DB2
|
73
74
|
DB2Module.load(arJdbc);
|
75
|
+
DB2RubyJdbcConnection.createDB2JdbcConnectionClass(runtime, jdbcConnection);
|
76
|
+
// Derby
|
77
|
+
DerbyModule.load(arJdbc);
|
78
|
+
DerbyRubyJdbcConnection.createDerbyJdbcConnectionClass(runtime, jdbcConnection);
|
79
|
+
// HSQLDB
|
80
|
+
HSQLDBModule.load(arJdbc);
|
81
|
+
// H2
|
82
|
+
H2RubyJdbcConnection.createH2JdbcConnectionClass(runtime, jdbcConnection);
|
83
|
+
// Informix
|
84
|
+
InformixRubyJdbcConnection.createInformixJdbcConnectionClass(runtime, jdbcConnection);
|
74
85
|
return true;
|
75
86
|
}
|
76
87
|
|
@@ -0,0 +1,44 @@
|
|
1
|
+
/*
|
2
|
+
* The MIT License
|
3
|
+
*
|
4
|
+
* Copyright 2013 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.jdbc;
|
25
|
+
|
26
|
+
import java.sql.Connection;
|
27
|
+
import java.sql.SQLException;
|
28
|
+
|
29
|
+
/**
|
30
|
+
* A JDBC connection callable ("block") of code.
|
31
|
+
*
|
32
|
+
* @author kares
|
33
|
+
*/
|
34
|
+
public interface Callable<T> {
|
35
|
+
|
36
|
+
/**
|
37
|
+
* Perform an operation in the context of the given connection.
|
38
|
+
* @param connection
|
39
|
+
* @return
|
40
|
+
* @throws SQLException
|
41
|
+
*/
|
42
|
+
T call(final Connection connection) throws SQLException;
|
43
|
+
|
44
|
+
}
|
@@ -25,12 +25,20 @@
|
|
25
25
|
package arjdbc.jdbc;
|
26
26
|
|
27
27
|
import java.sql.Connection;
|
28
|
+
import java.sql.SQLException;
|
28
29
|
|
29
30
|
/**
|
30
|
-
* Interface to be implemented in Ruby for retrieving a new connection
|
31
|
+
* Interface to be implemented in Ruby for retrieving a new connection.
|
31
32
|
*
|
32
33
|
* @author nicksieger
|
33
34
|
*/
|
34
35
|
public interface JdbcConnectionFactory {
|
35
|
-
|
36
|
+
|
37
|
+
/**
|
38
|
+
* Retrieve a (new) connection from the factory.
|
39
|
+
* @return a connection
|
40
|
+
* @throws SQLException
|
41
|
+
*/
|
42
|
+
Connection newConnection() throws SQLException;
|
43
|
+
|
36
44
|
}
|
@@ -31,31 +31,35 @@ import java.io.InputStream;
|
|
31
31
|
import java.io.Reader;
|
32
32
|
import java.io.StringReader;
|
33
33
|
import java.math.BigInteger;
|
34
|
+
import java.sql.Array;
|
34
35
|
import java.sql.Connection;
|
35
36
|
import java.sql.DatabaseMetaData;
|
36
37
|
import java.sql.PreparedStatement;
|
37
38
|
import java.sql.ResultSet;
|
38
39
|
import java.sql.ResultSetMetaData;
|
39
40
|
import java.sql.SQLException;
|
41
|
+
import java.sql.SQLXML;
|
40
42
|
import java.sql.Statement;
|
43
|
+
import java.sql.Date;
|
44
|
+
import java.sql.Time;
|
41
45
|
import java.sql.Timestamp;
|
42
46
|
import java.sql.Types;
|
43
47
|
import java.text.DateFormat;
|
44
48
|
import java.text.SimpleDateFormat;
|
45
49
|
import java.util.ArrayList;
|
46
50
|
import java.util.Calendar;
|
47
|
-
import java.util.Date;
|
48
51
|
import java.util.List;
|
49
52
|
|
50
53
|
import org.jruby.Ruby;
|
51
54
|
import org.jruby.RubyArray;
|
52
55
|
import org.jruby.RubyBignum;
|
56
|
+
import org.jruby.RubyBoolean;
|
53
57
|
import org.jruby.RubyClass;
|
54
58
|
import org.jruby.RubyHash;
|
59
|
+
import org.jruby.RubyInteger;
|
55
60
|
import org.jruby.RubyModule;
|
56
61
|
import org.jruby.RubyNumeric;
|
57
62
|
import org.jruby.RubyObject;
|
58
|
-
import org.jruby.RubyObjectAdapter;
|
59
63
|
import org.jruby.RubyString;
|
60
64
|
import org.jruby.RubySymbol;
|
61
65
|
import org.jruby.RubyTime;
|
@@ -68,7 +72,6 @@ import org.jruby.runtime.Arity;
|
|
68
72
|
import org.jruby.runtime.Block;
|
69
73
|
import org.jruby.runtime.ObjectAllocator;
|
70
74
|
import org.jruby.runtime.ThreadContext;
|
71
|
-
import org.jruby.runtime.backtrace.RubyStackTraceElement;
|
72
75
|
import org.jruby.runtime.builtin.IRubyObject;
|
73
76
|
import org.jruby.util.ByteList;
|
74
77
|
|
@@ -76,10 +79,9 @@ import org.jruby.util.ByteList;
|
|
76
79
|
* Part of our ActiveRecord::ConnectionAdapters::Connection impl.
|
77
80
|
*/
|
78
81
|
public class RubyJdbcConnection extends RubyObject {
|
79
|
-
|
80
|
-
private static final String[]
|
81
|
-
|
82
|
-
private static RubyObjectAdapter rubyApi;
|
82
|
+
|
83
|
+
private static final String[] TABLE_TYPE = new String[] { "TABLE" };
|
84
|
+
private static final String[] TABLE_TYPES = new String[] { "TABLE", "VIEW", "SYNONYM" };
|
83
85
|
|
84
86
|
protected RubyJdbcConnection(Ruby runtime, RubyClass metaClass) {
|
85
87
|
super(runtime, metaClass);
|
@@ -95,81 +97,187 @@ public class RubyJdbcConnection extends RubyObject {
|
|
95
97
|
RubyClass jdbcConnection = getConnectionAdapters(runtime).
|
96
98
|
defineClassUnder("JdbcConnection", runtime.getObject(), JDBCCONNECTION_ALLOCATOR);
|
97
99
|
jdbcConnection.defineAnnotatedMethods(RubyJdbcConnection.class);
|
98
|
-
|
99
|
-
rubyApi = JavaEmbedUtils.newObjectAdapter();
|
100
|
-
|
101
100
|
return jdbcConnection;
|
102
101
|
}
|
103
102
|
|
104
|
-
|
103
|
+
/**
|
104
|
+
* @param runtime
|
105
|
+
* @return <code>ActiveRecord::ConnectionAdapters</code>
|
106
|
+
*/
|
107
|
+
protected static RubyModule getConnectionAdapters(final Ruby runtime) {
|
105
108
|
return (RubyModule) runtime.getModule("ActiveRecord").getConstant("ConnectionAdapters");
|
106
109
|
}
|
110
|
+
|
111
|
+
/**
|
112
|
+
* @param runtime
|
113
|
+
* @return <code>ActiveRecord::Result</code>
|
114
|
+
*/
|
115
|
+
static RubyClass getResult(final Ruby runtime) {
|
116
|
+
return runtime.getModule("ActiveRecord").getClass("Result");
|
117
|
+
}
|
107
118
|
|
108
|
-
|
109
|
-
|
119
|
+
/**
|
120
|
+
* @param runtime
|
121
|
+
* @return <code>ActiveRecord::ConnectionAdapters::IndexDefinition</code>
|
122
|
+
*/
|
123
|
+
protected static RubyClass getIndexDefinition(final Ruby runtime) {
|
124
|
+
return getConnectionAdapters(runtime).getClass("IndexDefinition");
|
125
|
+
}
|
126
|
+
|
127
|
+
/**
|
128
|
+
* @param runtime
|
129
|
+
* @return <code>ActiveRecord::JDBCError</code>
|
130
|
+
*/
|
131
|
+
protected static RubyClass getJDBCError(final Ruby runtime) {
|
132
|
+
return runtime.getModule("ActiveRecord").getClass("JDBCError");
|
110
133
|
}
|
111
134
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
135
|
+
/**
|
136
|
+
* @param runtime
|
137
|
+
* @return <code>ActiveRecord::ConnectionNotEstablished</code>
|
138
|
+
*/
|
139
|
+
protected static RubyClass getConnectionNotEstablished(final Ruby runtime) {
|
140
|
+
return runtime.getModule("ActiveRecord").getClass("ConnectionNotEstablished");
|
141
|
+
}
|
142
|
+
|
143
|
+
/**
|
144
|
+
* NOTE: Only available since AR-4.0
|
145
|
+
* @param runtime
|
146
|
+
* @return <code>ActiveRecord::TransactionIsolationError</code>
|
147
|
+
*/
|
148
|
+
protected static RubyClass getTransactionIsolationError(final Ruby runtime) {
|
149
|
+
return (RubyClass) runtime.getModule("ActiveRecord").getConstant("TransactionIsolationError");
|
150
|
+
}
|
151
|
+
|
152
|
+
/**
|
153
|
+
* @param runtime
|
154
|
+
* @return <code>ActiveRecord::ConnectionAdapters::JdbcTypeConverter</code>
|
155
|
+
*/
|
156
|
+
private static RubyClass getJdbcTypeConverter(final Ruby runtime) {
|
157
|
+
return getConnectionAdapters(runtime).getClass("JdbcTypeConverter");
|
158
|
+
}
|
159
|
+
|
160
|
+
/*
|
161
|
+
def transaction_isolation_levels
|
162
|
+
{
|
163
|
+
read_uncommitted: "READ UNCOMMITTED",
|
164
|
+
read_committed: "READ COMMITTED",
|
165
|
+
repeatable_read: "REPEATABLE READ",
|
166
|
+
serializable: "SERIALIZABLE"
|
167
|
+
}
|
168
|
+
end
|
169
|
+
*/
|
128
170
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
171
|
+
public static int mapTransactionIsolationLevel(IRubyObject isolation) {
|
172
|
+
if ( ! ( isolation instanceof RubySymbol ) ) {
|
173
|
+
isolation = isolation.convertToString().callMethod("intern");
|
174
|
+
}
|
175
|
+
|
176
|
+
final Object isolationString = isolation.toString(); // RubySymbol.toString
|
177
|
+
if ( isolationString == "read_uncommitted" ) return Connection.TRANSACTION_READ_UNCOMMITTED; // 1
|
178
|
+
if ( isolationString == "read_committed" ) return Connection.TRANSACTION_READ_COMMITTED; // 2
|
179
|
+
if ( isolationString == "repeatable_read" ) return Connection.TRANSACTION_REPEATABLE_READ; // 4
|
180
|
+
if ( isolationString == "serializable" ) return Connection.TRANSACTION_SERIALIZABLE; // 8
|
181
|
+
|
182
|
+
throw new IllegalArgumentException(
|
183
|
+
"unexpected isolation level: " + isolation + " (" + isolationString + ")"
|
184
|
+
);
|
185
|
+
}
|
186
|
+
|
187
|
+
@JRubyMethod(name = "supports_transaction_isolation?", optional = 1)
|
188
|
+
public IRubyObject supports_transaction_isolation_p(final ThreadContext context,
|
189
|
+
final IRubyObject[] args) throws SQLException {
|
190
|
+
final IRubyObject isolation = args.length > 0 ? args[0] : null;
|
191
|
+
|
192
|
+
return withConnection(context, new Callable<IRubyObject>() {
|
193
|
+
public IRubyObject call(final Connection connection) throws SQLException {
|
194
|
+
final DatabaseMetaData metaData = connection.getMetaData();
|
195
|
+
final boolean supported;
|
196
|
+
if ( isolation != null && ! isolation.isNil() ) {
|
197
|
+
final int level = mapTransactionIsolationLevel(isolation);
|
198
|
+
supported = metaData.supportsTransactionIsolationLevel(level);
|
199
|
+
}
|
200
|
+
else {
|
201
|
+
final int level = metaData.getDefaultTransactionIsolation();
|
202
|
+
supported = level > Connection.TRANSACTION_NONE; // > 0
|
136
203
|
}
|
204
|
+
return context.getRuntime().newBoolean(supported);
|
137
205
|
}
|
138
206
|
});
|
139
207
|
}
|
140
|
-
|
141
|
-
@JRubyMethod(name = "begin")
|
142
|
-
public IRubyObject begin(ThreadContext context
|
143
|
-
final
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
208
|
+
|
209
|
+
@JRubyMethod(name = "begin", optional = 1) // optional isolation argument for AR-4.0
|
210
|
+
public IRubyObject begin(final ThreadContext context, final IRubyObject[] args) {
|
211
|
+
final IRubyObject isolation = args.length > 0 ? args[0] : null;
|
212
|
+
try { // handleException == false so we can handle setTXIsolation
|
213
|
+
return withConnection(context, false, new Callable<IRubyObject>() {
|
214
|
+
public IRubyObject call(final Connection connection) throws SQLException {
|
215
|
+
connection.setAutoCommit(false);
|
216
|
+
if ( isolation != null && ! isolation.isNil() ) {
|
217
|
+
final int level = mapTransactionIsolationLevel(isolation);
|
218
|
+
try {
|
219
|
+
connection.setTransactionIsolation(level);
|
220
|
+
}
|
221
|
+
catch (SQLException e) {
|
222
|
+
RubyClass txError = getTransactionIsolationError(context.getRuntime());
|
223
|
+
if ( txError != null ) throw wrapException(context, txError, e);
|
224
|
+
throw e; // let it roll - will be wrapped into a JDBCError (non 4.0)
|
225
|
+
}
|
226
|
+
}
|
227
|
+
return context.getRuntime().getNil();
|
228
|
+
}
|
229
|
+
});
|
230
|
+
}
|
231
|
+
catch (SQLException e) {
|
232
|
+
return handleException(context, e);
|
233
|
+
}
|
150
234
|
}
|
151
235
|
|
152
236
|
@JRubyMethod(name = "commit")
|
153
|
-
public IRubyObject commit(ThreadContext context)
|
154
|
-
Connection connection = getConnection(true);
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
237
|
+
public IRubyObject commit(final ThreadContext context) {
|
238
|
+
final Connection connection = getConnection(true);
|
239
|
+
try {
|
240
|
+
if ( ! connection.getAutoCommit() ) {
|
241
|
+
try {
|
242
|
+
connection.commit();
|
243
|
+
return context.getRuntime().newBoolean(true);
|
244
|
+
}
|
245
|
+
finally {
|
246
|
+
connection.setAutoCommit(true);
|
247
|
+
}
|
161
248
|
}
|
249
|
+
return context.getRuntime().getNil();
|
250
|
+
}
|
251
|
+
catch (SQLException e) {
|
252
|
+
return handleException(context, e);
|
162
253
|
}
|
163
|
-
|
164
|
-
return context.getRuntime().getNil();
|
165
254
|
}
|
166
255
|
|
256
|
+
@JRubyMethod(name = "rollback")
|
257
|
+
public IRubyObject rollback(final ThreadContext context) {
|
258
|
+
final Connection connection = getConnection(true);
|
259
|
+
try {
|
260
|
+
if ( ! connection.getAutoCommit() ) {
|
261
|
+
try {
|
262
|
+
connection.rollback();
|
263
|
+
return context.getRuntime().newBoolean(true);
|
264
|
+
} finally {
|
265
|
+
connection.setAutoCommit(true);
|
266
|
+
}
|
267
|
+
}
|
268
|
+
return context.getRuntime().getNil();
|
269
|
+
}
|
270
|
+
catch (SQLException e) {
|
271
|
+
return handleException(context, e);
|
272
|
+
}
|
273
|
+
}
|
274
|
+
|
167
275
|
@JRubyMethod(name = "connection")
|
168
|
-
public IRubyObject connection() {
|
276
|
+
public IRubyObject connection(final ThreadContext context) {
|
169
277
|
if ( getConnection(false) == null ) {
|
170
278
|
synchronized (this) {
|
171
279
|
if ( getConnection(false) == null ) {
|
172
|
-
reconnect();
|
280
|
+
reconnect(context);
|
173
281
|
}
|
174
282
|
}
|
175
283
|
}
|
@@ -185,15 +293,22 @@ public class RubyJdbcConnection extends RubyObject {
|
|
185
293
|
final Ruby runtime = context.getRuntime();
|
186
294
|
List backtrace = (List) context.createCallerBacktrace(runtime, 0);
|
187
295
|
runtime.getOut().println(this + " connection.disconnect! occured: ");
|
188
|
-
for ( Object element : backtrace )
|
296
|
+
for ( Object element : backtrace ) {
|
297
|
+
runtime.getOut().println(element);
|
298
|
+
}
|
189
299
|
runtime.getOut().flush();
|
190
300
|
}
|
191
301
|
return setConnection(null);
|
192
302
|
}
|
193
303
|
|
194
304
|
@JRubyMethod(name = "reconnect!")
|
195
|
-
public IRubyObject reconnect() {
|
196
|
-
|
305
|
+
public IRubyObject reconnect(final ThreadContext context) {
|
306
|
+
try {
|
307
|
+
return setConnection( getConnectionFactory().newConnection() );
|
308
|
+
}
|
309
|
+
catch (SQLException e) {
|
310
|
+
return handleException(context, e);
|
311
|
+
}
|
197
312
|
}
|
198
313
|
|
199
314
|
@JRubyMethod(name = "database_name")
|
@@ -212,63 +327,77 @@ public class RubyJdbcConnection extends RubyObject {
|
|
212
327
|
|
213
328
|
@JRubyMethod(name = "execute", required = 1)
|
214
329
|
public IRubyObject execute(final ThreadContext context, final IRubyObject sql) {
|
215
|
-
return (
|
216
|
-
public
|
217
|
-
Statement
|
218
|
-
String query =
|
330
|
+
return withConnection(context, new Callable<IRubyObject>() {
|
331
|
+
public IRubyObject call(final Connection connection) throws SQLException {
|
332
|
+
Statement statement = null;
|
333
|
+
final String query = sql.convertToString().getUnicodeValue();
|
219
334
|
try {
|
220
|
-
|
221
|
-
if (
|
222
|
-
return unmarshalResults(context,
|
335
|
+
statement = connection.createStatement();
|
336
|
+
if ( doExecute(statement, query) ) {
|
337
|
+
return unmarshalResults(context, connection.getMetaData(), statement, false);
|
223
338
|
} else {
|
224
|
-
return unmarshalKeysOrUpdateCount(context,
|
339
|
+
return unmarshalKeysOrUpdateCount(context, connection, statement);
|
225
340
|
}
|
226
|
-
} catch (SQLException sqe) {
|
227
|
-
if (context.getRuntime().isDebug()) {
|
228
|
-
System.out.println("Error SQL: " + query);
|
229
|
-
}
|
230
|
-
throw sqe;
|
231
|
-
} finally {
|
232
|
-
close(stmt);
|
233
341
|
}
|
342
|
+
catch (final SQLException e) {
|
343
|
+
debugErrorSQL(context, query);
|
344
|
+
throw e;
|
345
|
+
}
|
346
|
+
finally { close(statement); }
|
234
347
|
}
|
235
348
|
});
|
236
349
|
}
|
237
350
|
|
238
|
-
|
239
|
-
|
351
|
+
/**
|
352
|
+
* Execute a query using the given statement.
|
353
|
+
* @param statement
|
354
|
+
* @param query
|
355
|
+
* @return true if the first result is a <code>ResultSet</code>;
|
356
|
+
* false if it is an update count or there are no results
|
357
|
+
* @throws SQLException
|
358
|
+
*/
|
359
|
+
protected boolean doExecute(final Statement statement, final String query) throws SQLException {
|
360
|
+
return genericExecute(statement, query);
|
240
361
|
}
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
362
|
+
|
363
|
+
/**
|
364
|
+
* @deprecated renamed to {@link #doExecute(Statement, String)}
|
365
|
+
*/
|
366
|
+
@Deprecated
|
367
|
+
protected boolean genericExecute(final Statement statement, final String query) throws SQLException {
|
368
|
+
return statement.execute(query);
|
369
|
+
}
|
370
|
+
|
371
|
+
protected IRubyObject unmarshalKeysOrUpdateCount(final ThreadContext context,
|
372
|
+
final Connection connection, final Statement statement) throws SQLException {
|
373
|
+
final Ruby runtime = context.getRuntime();
|
374
|
+
final IRubyObject key;
|
375
|
+
if ( connection.getMetaData().supportsGetGeneratedKeys() ) {
|
376
|
+
key = unmarshalIdResult(runtime, statement);
|
246
377
|
}
|
247
|
-
|
248
|
-
|
249
|
-
} else {
|
250
|
-
return key;
|
378
|
+
else {
|
379
|
+
key = runtime.getNil();
|
251
380
|
}
|
252
|
-
|
381
|
+
return key.isNil() ? runtime.newFixnum( statement.getUpdateCount() ) : key;
|
382
|
+
}
|
253
383
|
|
254
384
|
@JRubyMethod(name = "execute_id_insert", required = 2)
|
255
|
-
public IRubyObject execute_id_insert(final ThreadContext context,
|
256
|
-
|
257
|
-
return (
|
258
|
-
public
|
259
|
-
|
260
|
-
|
385
|
+
public IRubyObject execute_id_insert(final ThreadContext context,
|
386
|
+
final IRubyObject sql, final IRubyObject id) throws SQLException {
|
387
|
+
return withConnection(context, new Callable<IRubyObject>() {
|
388
|
+
public IRubyObject call(final Connection connection) throws SQLException {
|
389
|
+
PreparedStatement statement = null;
|
390
|
+
final String insertSQL = sql.convertToString().getUnicodeValue();
|
261
391
|
try {
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
throw
|
269
|
-
} finally {
|
270
|
-
close(ps);
|
392
|
+
statement = connection.prepareStatement(insertSQL);
|
393
|
+
statement.setLong(1, RubyNumeric.fix2long(id));
|
394
|
+
statement.executeUpdate();
|
395
|
+
}
|
396
|
+
catch (final SQLException e) {
|
397
|
+
debugErrorSQL(context, insertSQL);
|
398
|
+
throw e;
|
271
399
|
}
|
400
|
+
finally { close(statement); }
|
272
401
|
return id;
|
273
402
|
}
|
274
403
|
});
|
@@ -276,191 +405,174 @@ public class RubyJdbcConnection extends RubyObject {
|
|
276
405
|
|
277
406
|
@JRubyMethod(name = "execute_insert", required = 1)
|
278
407
|
public IRubyObject execute_insert(final ThreadContext context, final IRubyObject sql)
|
279
|
-
|
280
|
-
return (
|
281
|
-
public
|
282
|
-
Statement
|
283
|
-
String
|
408
|
+
throws SQLException {
|
409
|
+
return withConnection(context, new Callable<IRubyObject>() {
|
410
|
+
public IRubyObject call(final Connection connection) throws SQLException {
|
411
|
+
Statement statement = null;
|
412
|
+
final String insertSQL = sql.convertToString().getUnicodeValue();
|
284
413
|
try {
|
285
|
-
|
286
|
-
|
287
|
-
return
|
288
|
-
} catch (SQLException sqe) {
|
289
|
-
if (context.getRuntime().isDebug()) {
|
290
|
-
System.out.println("Error SQL: " + insert);
|
291
|
-
}
|
292
|
-
throw sqe;
|
293
|
-
} finally {
|
294
|
-
close(stmt);
|
414
|
+
statement = connection.createStatement();
|
415
|
+
statement.executeUpdate(insertSQL, Statement.RETURN_GENERATED_KEYS);
|
416
|
+
return unmarshalIdResult(context.getRuntime(), statement);
|
295
417
|
}
|
418
|
+
catch (final SQLException e) {
|
419
|
+
debugErrorSQL(context, insertSQL);
|
420
|
+
throw e;
|
421
|
+
}
|
422
|
+
finally { close(statement); }
|
296
423
|
}
|
297
424
|
});
|
298
425
|
}
|
299
426
|
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
427
|
+
/**
|
428
|
+
* NOTE: since 1.3 this behaves like <code>execute_query</code> in AR-JDBC 1.2
|
429
|
+
* @param context
|
430
|
+
* @param sql
|
431
|
+
* @param block
|
432
|
+
* @return raw query result in case no block given
|
433
|
+
* @throws SQLException
|
434
|
+
*/
|
435
|
+
@JRubyMethod(name = "execute_query_raw", required = 1) // optional block
|
436
|
+
public IRubyObject execute_query_raw(final ThreadContext context,
|
437
|
+
final IRubyObject sql, final Block block) throws SQLException {
|
438
|
+
final String query = sql.convertToString().getUnicodeValue();
|
439
|
+
return executeQueryRaw(context, query, 0, block);
|
306
440
|
}
|
307
441
|
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
442
|
+
/**
|
443
|
+
* NOTE: since 1.3 this behaves like <code>execute_query</code> in AR-JDBC 1.2
|
444
|
+
* @param context
|
445
|
+
* @param sql
|
446
|
+
* @param maxRows
|
447
|
+
* @param block
|
448
|
+
* @return raw query result in case no block given
|
449
|
+
* @throws SQLException
|
450
|
+
*/
|
451
|
+
@JRubyMethod(name = "execute_query_raw", required = 2)
|
452
|
+
public IRubyObject execute_query_raw(final ThreadContext context,
|
453
|
+
final IRubyObject sql, final IRubyObject maxRows, final Block block)
|
454
|
+
throws SQLException {
|
455
|
+
final String query = sql.convertToString().getUnicodeValue();
|
456
|
+
return executeQueryRaw(context, query, RubyNumeric.fix2int(maxRows), block);
|
315
457
|
}
|
316
458
|
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
459
|
+
/**
|
460
|
+
* @param context
|
461
|
+
* @param query
|
462
|
+
* @param maxRows
|
463
|
+
* @param block
|
464
|
+
* @return raw query result (in case no block was given)
|
465
|
+
*
|
466
|
+
* @see #execute_raw_query(ThreadContext, IRubyObject, Block)
|
467
|
+
* @see #execute_raw_query(ThreadContext, IRubyObject, IRubyObject, Block)
|
468
|
+
*/
|
469
|
+
protected IRubyObject executeQueryRaw(final ThreadContext context, final String query, final int maxRows,
|
470
|
+
final Block block) { // TODO implement block support
|
471
|
+
return withConnection(context, new Callable<IRubyObject>() {
|
472
|
+
public IRubyObject call(final Connection connection) throws SQLException {
|
473
|
+
final Ruby runtime = context.getRuntime();
|
474
|
+
Statement statement = null; ResultSet resultSet = null;
|
321
475
|
try {
|
322
|
-
DatabaseMetaData
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
if (context.getRuntime().isDebug()) {
|
328
|
-
System.out.println("Error SQL: " + query);
|
329
|
-
}
|
330
|
-
throw sqe;
|
331
|
-
} finally {
|
332
|
-
close(stmt);
|
476
|
+
final DatabaseMetaData metaData = connection.getMetaData();
|
477
|
+
statement = connection.createStatement();
|
478
|
+
statement.setMaxRows(maxRows); // zero means there is no limit
|
479
|
+
resultSet = statement.executeQuery(query);
|
480
|
+
return mapToRawResult(context, runtime, metaData, resultSet, false);
|
333
481
|
}
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
@JRubyMethod(name = "execute_update", required = 1)
|
339
|
-
public IRubyObject execute_update(final ThreadContext context, final IRubyObject sql)
|
340
|
-
throws SQLException {
|
341
|
-
return (IRubyObject) withConnectionAndRetry(context, new SQLBlock() {
|
342
|
-
public Object call(Connection c) throws SQLException {
|
343
|
-
Statement stmt = null;
|
344
|
-
String update = rubyApi.convertToRubyString(sql).getUnicodeValue();
|
345
|
-
try {
|
346
|
-
stmt = c.createStatement();
|
347
|
-
return context.getRuntime().newFixnum((long)stmt.executeUpdate(update));
|
348
|
-
} catch (SQLException sqe) {
|
349
|
-
if (context.getRuntime().isDebug()) {
|
350
|
-
System.out.println("Error SQL: " + update);
|
351
|
-
}
|
352
|
-
throw sqe;
|
353
|
-
} finally {
|
354
|
-
close(stmt);
|
482
|
+
catch (final SQLException e) {
|
483
|
+
debugErrorSQL(context, query);
|
484
|
+
throw e;
|
355
485
|
}
|
486
|
+
finally { close(resultSet); close(statement); }
|
356
487
|
}
|
357
488
|
});
|
358
489
|
}
|
359
490
|
|
360
|
-
|
361
|
-
|
362
|
-
|
491
|
+
/**
|
492
|
+
* Executes a query and returns the (AR) result.
|
493
|
+
* @param context
|
494
|
+
* @param sql
|
495
|
+
* @return
|
496
|
+
* @throws SQLException
|
497
|
+
*
|
498
|
+
* @see #execute_raw_query(ThreadContext, IRubyObject, Block)
|
499
|
+
*/
|
500
|
+
@JRubyMethod(name = "execute_query", required = 1)
|
501
|
+
public IRubyObject execute_query(final ThreadContext context, final IRubyObject sql)
|
502
|
+
throws SQLException {
|
503
|
+
final String query = sql.convertToString().getUnicodeValue();
|
504
|
+
return executeQuery(context, query, 0);
|
363
505
|
}
|
364
506
|
|
365
|
-
private static final int INDEX_TABLE_NAME = 3;
|
366
|
-
private static final int INDEX_NON_UNIQUE = 4;
|
367
|
-
private static final int INDEX_NAME = 6;
|
368
|
-
private static final int INDEX_COLUMN_NAME = 9;
|
369
|
-
|
370
507
|
/**
|
371
|
-
*
|
372
|
-
*
|
373
|
-
*
|
374
|
-
*
|
375
|
-
*
|
376
|
-
*
|
508
|
+
* Executes a query and returns the (AR) result.
|
509
|
+
* @param context
|
510
|
+
* @param sql
|
511
|
+
* @param maxRows
|
512
|
+
* @return
|
513
|
+
* @throws SQLException
|
514
|
+
*
|
515
|
+
* @see #execute_raw_query(ThreadContext, IRubyObject, IRubyObject, Block)
|
377
516
|
*/
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
517
|
+
@JRubyMethod(name = "execute_query", required = 2)
|
518
|
+
public IRubyObject execute_query(final ThreadContext context,
|
519
|
+
final IRubyObject sql, final IRubyObject maxRows) throws SQLException {
|
520
|
+
final String query = sql.convertToString().getUnicodeValue();
|
521
|
+
return executeQuery(context, query, RubyNumeric.fix2int(maxRows));
|
522
|
+
}
|
523
|
+
|
524
|
+
/**
|
525
|
+
* NOTE: This methods behavior changed in AR-JDBC 1.3 the old behavior is
|
526
|
+
* achievable using {@link #executeQueryRaw(ThreadContext, String, int, Block)}.
|
527
|
+
*
|
528
|
+
* @param context
|
529
|
+
* @param query
|
530
|
+
* @param maxRows
|
531
|
+
* @return AR (mapped) query result
|
532
|
+
*
|
533
|
+
* @see #execute_query(ThreadContext, IRubyObject)
|
534
|
+
* @see #execute_query(ThreadContext, IRubyObject, IRubyObject)
|
535
|
+
* @see #mapToResult(ThreadContext, Ruby, DatabaseMetaData, ResultSet, RubyJdbcConnection.ColumnData[])
|
536
|
+
*/
|
537
|
+
protected IRubyObject executeQuery(final ThreadContext context, final String query, final int maxRows) {
|
538
|
+
return withConnection(context, new Callable<IRubyObject>() {
|
539
|
+
public IRubyObject call(final Connection connection) throws SQLException {
|
540
|
+
final Ruby runtime = context.getRuntime();
|
541
|
+
Statement statement = null; ResultSet resultSet = null;
|
388
542
|
try {
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
indexName = caseConvertIdentifierForRails(metadata, indexName);
|
400
|
-
|
401
|
-
RubyString columnName = RubyString.newUnicodeString(runtime, caseConvertIdentifierForRails(metadata, resultSet.getString(INDEX_COLUMN_NAME)));
|
402
|
-
|
403
|
-
if (primaryKeys.contains(columnName)) continue;
|
404
|
-
|
405
|
-
// We are working on a new index
|
406
|
-
if (!indexName.equals(currentIndex)) {
|
407
|
-
currentIndex = indexName;
|
408
|
-
|
409
|
-
tableName = caseConvertIdentifierForRails(metadata, resultSet.getString(INDEX_TABLE_NAME));
|
410
|
-
boolean nonUnique = resultSet.getBoolean(INDEX_NON_UNIQUE);
|
411
|
-
|
412
|
-
IRubyObject indexDefinition = indexDefinitionClass.callMethod(context, "new",
|
413
|
-
new IRubyObject[] {
|
414
|
-
RubyString.newUnicodeString(runtime, tableName),
|
415
|
-
RubyString.newUnicodeString(runtime, indexName),
|
416
|
-
runtime.newBoolean(!nonUnique),
|
417
|
-
runtime.newArray()
|
418
|
-
});
|
419
|
-
|
420
|
-
// empty list for column names, we'll add to that in just a bit
|
421
|
-
indexes.add(indexDefinition);
|
422
|
-
}
|
423
|
-
|
424
|
-
// One or more columns can be associated with an index
|
425
|
-
IRubyObject lastIndex = (IRubyObject) indexes.get(indexes.size() - 1);
|
426
|
-
|
427
|
-
if (lastIndex != null) {
|
428
|
-
lastIndex.callMethod(context, "columns").callMethod(context, "<<", columnName);
|
429
|
-
}
|
430
|
-
}
|
431
|
-
|
432
|
-
return runtime.newArray(indexes);
|
433
|
-
} finally {
|
434
|
-
close(resultSet);
|
543
|
+
final DatabaseMetaData metaData = connection.getMetaData();
|
544
|
+
statement = connection.createStatement();
|
545
|
+
statement.setMaxRows(maxRows); // zero means there is no limit
|
546
|
+
resultSet = statement.executeQuery(query);
|
547
|
+
final ColumnData[] columns = setupColumns(runtime, metaData, resultSet.getMetaData(), false);
|
548
|
+
return mapToResult(context, runtime, metaData, resultSet, columns);
|
549
|
+
}
|
550
|
+
catch (final SQLException e) {
|
551
|
+
debugErrorSQL(context, query);
|
552
|
+
throw e;
|
435
553
|
}
|
554
|
+
finally { close(resultSet); close(statement); }
|
436
555
|
}
|
437
556
|
});
|
438
557
|
}
|
439
|
-
|
440
|
-
@JRubyMethod(name = "
|
441
|
-
public
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
/*
|
448
|
-
* sql, values, types, name = nil, pk = nil, id_value = nil, sequence_name = nil
|
449
|
-
*/
|
450
|
-
@JRubyMethod(name = "insert_bind", required = 3, rest = true)
|
451
|
-
public IRubyObject insert_bind(final ThreadContext context, final IRubyObject[] args) throws SQLException {
|
452
|
-
final Ruby runtime = context.getRuntime();
|
453
|
-
return (IRubyObject) withConnectionAndRetry(context, new SQLBlock() {
|
454
|
-
public Object call(Connection c) throws SQLException {
|
455
|
-
PreparedStatement ps = null;
|
558
|
+
|
559
|
+
@JRubyMethod(name = {"execute_update", "execute_delete"}, required = 1)
|
560
|
+
public IRubyObject execute_update(final ThreadContext context, final IRubyObject sql)
|
561
|
+
throws SQLException {
|
562
|
+
return withConnection(context, new Callable<RubyInteger>() {
|
563
|
+
public RubyInteger call(final Connection connection) throws SQLException {
|
564
|
+
Statement statement = null;
|
565
|
+
final String updateSQL = sql.convertToString().getUnicodeValue();
|
456
566
|
try {
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
return unmarshal_id_result(runtime, ps.getGeneratedKeys());
|
461
|
-
} finally {
|
462
|
-
close(ps);
|
567
|
+
statement = connection.createStatement();
|
568
|
+
final int rowCount = statement.executeUpdate(updateSQL);
|
569
|
+
return context.getRuntime().newFixnum(rowCount);
|
463
570
|
}
|
571
|
+
catch (final SQLException e) {
|
572
|
+
debugErrorSQL(context, updateSQL);
|
573
|
+
throw e;
|
574
|
+
}
|
575
|
+
finally { close(statement); }
|
464
576
|
}
|
465
577
|
});
|
466
578
|
}
|
@@ -473,71 +585,49 @@ public class RubyJdbcConnection extends RubyObject {
|
|
473
585
|
|
474
586
|
@JRubyMethod(name = "primary_keys", required = 1)
|
475
587
|
public IRubyObject primary_keys(ThreadContext context, IRubyObject tableName) throws SQLException {
|
476
|
-
|
588
|
+
@SuppressWarnings("unchecked")
|
589
|
+
List<IRubyObject> primaryKeys = (List) primaryKeys(context, tableName.toString());
|
590
|
+
return context.getRuntime().newArray(primaryKeys);
|
477
591
|
}
|
478
592
|
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
593
|
+
private static final int PRIMARY_KEYS_COLUMN_NAME = 4;
|
594
|
+
|
595
|
+
protected List<RubyString> primaryKeys(final ThreadContext context, final String tableName) {
|
596
|
+
return withConnection(context, new Callable<List<RubyString>>() {
|
597
|
+
public List<RubyString> call(final Connection connection) throws SQLException {
|
598
|
+
final Ruby runtime = context.getRuntime();
|
599
|
+
final DatabaseMetaData metaData = connection.getMetaData();
|
600
|
+
final String _tableName = caseConvertIdentifierForJdbc(metaData, tableName);
|
485
601
|
ResultSet resultSet = null;
|
486
|
-
List keyNames = new ArrayList();
|
602
|
+
final List<RubyString> keyNames = new ArrayList<RubyString>();
|
487
603
|
try {
|
488
|
-
|
489
|
-
resultSet =
|
604
|
+
TableName components = extractTableName(connection, null, _tableName);
|
605
|
+
resultSet = metaData.getPrimaryKeys(components.catalog, components.schema, components.name);
|
490
606
|
|
491
607
|
while (resultSet.next()) {
|
492
|
-
|
493
|
-
|
608
|
+
String columnName = resultSet.getString(PRIMARY_KEYS_COLUMN_NAME);
|
609
|
+
columnName = caseConvertIdentifierForRails(metaData, columnName);
|
610
|
+
keyNames.add( RubyString.newUnicodeString(runtime, columnName) );
|
494
611
|
}
|
495
|
-
} finally {
|
496
|
-
close(resultSet);
|
497
612
|
}
|
498
|
-
|
613
|
+
finally { close(resultSet); }
|
499
614
|
return keyNames;
|
500
615
|
}
|
501
616
|
});
|
502
617
|
}
|
503
|
-
|
504
|
-
|
505
|
-
@JRubyMethod(name = "rollback")
|
506
|
-
public IRubyObject rollback(ThreadContext context) throws SQLException {
|
507
|
-
final Ruby runtime = context.getRuntime();
|
508
|
-
return (IRubyObject) withConnectionAndRetry(context, new SQLBlock() {
|
509
|
-
public Object call(Connection c) throws SQLException {
|
510
|
-
Connection connection = getConnection(true);
|
511
|
-
|
512
|
-
if (!connection.getAutoCommit()) {
|
513
|
-
try {
|
514
|
-
connection.rollback();
|
515
|
-
} finally {
|
516
|
-
connection.setAutoCommit(true);
|
517
|
-
}
|
518
|
-
}
|
519
|
-
|
520
|
-
return runtime.getNil();
|
521
|
-
}
|
522
|
-
});
|
523
|
-
}
|
524
|
-
|
525
|
-
@JRubyMethod(name = "select?", required = 1, meta = true, frame = false)
|
526
|
-
public static IRubyObject select_p(ThreadContext context, IRubyObject recv, IRubyObject _sql) {
|
527
|
-
ByteList sql = rubyApi.convertToRubyString(_sql).getByteList();
|
528
|
-
|
529
|
-
return context.getRuntime().newBoolean(startsWithNoCaseCmp(sql, SELECT) || startsWithNoCaseCmp(sql, WITH) ||
|
530
|
-
startsWithNoCaseCmp(sql, SHOW) || startsWithNoCaseCmp(sql, CALL));
|
531
|
-
}
|
532
|
-
|
618
|
+
|
533
619
|
@JRubyMethod(name = "set_native_database_types")
|
534
|
-
public IRubyObject set_native_database_types(ThreadContext context) throws SQLException
|
535
|
-
Ruby runtime = context.getRuntime();
|
536
|
-
DatabaseMetaData
|
537
|
-
IRubyObject types
|
538
|
-
|
539
|
-
|
540
|
-
|
620
|
+
public IRubyObject set_native_database_types(final ThreadContext context) throws SQLException {
|
621
|
+
final Ruby runtime = context.getRuntime();
|
622
|
+
final DatabaseMetaData metaData = getConnection(true).getMetaData();
|
623
|
+
final IRubyObject types; final ResultSet typeDesc = metaData.getTypeInfo();
|
624
|
+
try {
|
625
|
+
types = mapToRawResult(context, runtime, metaData, typeDesc, true);
|
626
|
+
}
|
627
|
+
finally { close(typeDesc); }
|
628
|
+
|
629
|
+
final IRubyObject typeConverter = getJdbcTypeConverter(runtime).callMethod("new", types);
|
630
|
+
setInstanceVariable("@native_types", typeConverter.callMethod(context, "choose_best_types"));
|
541
631
|
|
542
632
|
return runtime.getNil();
|
543
633
|
}
|
@@ -567,90 +657,267 @@ public class RubyJdbcConnection extends RubyObject {
|
|
567
657
|
return tables(context, toStringOrNull(args[0]), toStringOrNull(args[1]), toStringOrNull(args[2]), getTypes(args[3]));
|
568
658
|
}
|
569
659
|
|
570
|
-
protected IRubyObject tables(ThreadContext context,
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
* sql, values, types, name = nil
|
576
|
-
*/
|
577
|
-
@JRubyMethod(name = "update_bind", required = 3, rest = true)
|
578
|
-
public IRubyObject update_bind(final ThreadContext context, final IRubyObject[] args) throws SQLException {
|
579
|
-
final Ruby runtime = context.getRuntime();
|
580
|
-
Arity.checkArgumentCount(runtime, args, 3, 4);
|
581
|
-
return (IRubyObject) withConnectionAndRetry(context, new SQLBlock() {
|
582
|
-
public Object call(Connection c) throws SQLException {
|
583
|
-
PreparedStatement ps = null;
|
584
|
-
try {
|
585
|
-
ps = c.prepareStatement(rubyApi.convertToRubyString(args[0]).toString());
|
586
|
-
setValuesOnPS(ps, context, args[1], args[2]);
|
587
|
-
ps.executeUpdate();
|
588
|
-
} finally {
|
589
|
-
close(ps);
|
590
|
-
}
|
591
|
-
return runtime.getNil();
|
660
|
+
protected IRubyObject tables(final ThreadContext context,
|
661
|
+
final String catalog, final String schemaPattern, final String tablePattern, final String[] types) {
|
662
|
+
return withConnection(context, new Callable<IRubyObject>() {
|
663
|
+
public IRubyObject call(final Connection connection) throws SQLException {
|
664
|
+
return matchTables(context.getRuntime(), connection, catalog, schemaPattern, tablePattern, types, false);
|
592
665
|
}
|
593
666
|
});
|
594
667
|
}
|
595
668
|
|
596
|
-
|
597
|
-
|
598
|
-
return (IRubyObject) withConnectionAndRetry(context, new SQLBlock() {
|
599
|
-
public Object call(Connection c) throws SQLException {
|
600
|
-
return block.call(context, new IRubyObject[] { wrappedConnection(c) });
|
601
|
-
}
|
602
|
-
});
|
669
|
+
protected String[] getTableTypes() {
|
670
|
+
return TABLE_TYPES;
|
603
671
|
}
|
604
672
|
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
673
|
+
@JRubyMethod(name = "table_exists?", required = 1, optional = 1)
|
674
|
+
public IRubyObject table_exists_p(final ThreadContext context, final IRubyObject[] args) {
|
675
|
+
IRubyObject name = args[0], schema_name = args.length > 1 ? args[1] : null;
|
676
|
+
if ( ! ( name instanceof RubyString ) ) {
|
677
|
+
name = name.callMethod(context, "to_s");
|
678
|
+
}
|
679
|
+
final String tableName = ((RubyString) name).getUnicodeValue();
|
680
|
+
final String tableSchema = schema_name == null ? null : schema_name.convertToString().getUnicodeValue();
|
611
681
|
final Ruby runtime = context.getRuntime();
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
682
|
+
|
683
|
+
return withConnection(context, new Callable<RubyBoolean>() {
|
684
|
+
public RubyBoolean call(final Connection connection) throws SQLException {
|
685
|
+
final TableName components = extractTableName(connection, tableSchema, tableName);
|
686
|
+
return runtime.newBoolean( tableExists(runtime, connection, components) );
|
687
|
+
}
|
688
|
+
});
|
689
|
+
}
|
690
|
+
|
691
|
+
@JRubyMethod(name = {"columns", "columns_internal"}, required = 1, optional = 2)
|
692
|
+
public IRubyObject columns_internal(final ThreadContext context, final IRubyObject[] args)
|
693
|
+
throws SQLException {
|
694
|
+
return withConnection(context, new Callable<IRubyObject>() {
|
695
|
+
public IRubyObject call(final Connection connection) throws SQLException {
|
696
|
+
ResultSet columns = null, primaryKeys = null;
|
619
697
|
try {
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
698
|
+
final String tableName = args[0].convertToString().getUnicodeValue();
|
699
|
+
// optionals (NOTE: catalog argumnet was never used before 1.3.0) :
|
700
|
+
final String catalog = args.length > 1 ? toStringOrNull(args[1]) : null;
|
701
|
+
final String defaultSchema = args.length > 2 ? toStringOrNull(args[2]) : null;
|
702
|
+
|
703
|
+
final TableName components;
|
704
|
+
if ( catalog == null ) { // backwards-compatibility with < 1.3.0
|
705
|
+
components = extractTableName(connection, defaultSchema, tableName);
|
628
706
|
}
|
629
|
-
|
630
|
-
|
631
|
-
|
707
|
+
else {
|
708
|
+
components = extractTableName(connection, catalog, defaultSchema, tableName);
|
709
|
+
}
|
710
|
+
|
711
|
+
if ( ! tableExists(context.getRuntime(), connection, components) ) {
|
712
|
+
throw new SQLException("table: " + tableName + " does not exist");
|
713
|
+
}
|
714
|
+
|
715
|
+
final DatabaseMetaData metaData = connection.getMetaData();
|
716
|
+
columns = metaData.getColumns(components.catalog, components.schema, components.name, null);
|
717
|
+
primaryKeys = metaData.getPrimaryKeys(components.catalog, components.schema, components.name);
|
718
|
+
return unmarshalColumns(context, metaData, columns, primaryKeys);
|
719
|
+
}
|
720
|
+
finally {
|
721
|
+
close(columns);
|
722
|
+
close(primaryKeys);
|
632
723
|
}
|
633
|
-
return runtime.getNil();
|
634
724
|
}
|
635
725
|
});
|
636
726
|
}
|
727
|
+
|
728
|
+
@JRubyMethod(name = "indexes")
|
729
|
+
public IRubyObject indexes(ThreadContext context, IRubyObject tableName, IRubyObject name, IRubyObject schemaName) {
|
730
|
+
return indexes(context, toStringOrNull(tableName), toStringOrNull(name), toStringOrNull(schemaName));
|
731
|
+
}
|
732
|
+
|
733
|
+
// NOTE: metaData.getIndexInfo row mappings :
|
734
|
+
private static final int INDEX_INFO_TABLE_NAME = 3;
|
735
|
+
private static final int INDEX_INFO_NON_UNIQUE = 4;
|
736
|
+
private static final int INDEX_INFO_NAME = 6;
|
737
|
+
private static final int INDEX_INFO_COLUMN_NAME = 9;
|
637
738
|
|
638
739
|
/**
|
639
|
-
*
|
740
|
+
* Default JDBC introspection for index metadata on the JdbcConnection.
|
640
741
|
*
|
641
|
-
*
|
642
|
-
*
|
643
|
-
*
|
644
|
-
*
|
742
|
+
* JDBC index metadata is denormalized (multiple rows may be returned for
|
743
|
+
* one index, one row per column in the index), so a simple block-based
|
744
|
+
* filter like that used for tables doesn't really work here. Callers
|
745
|
+
* should filter the return from this method instead.
|
746
|
+
*/
|
747
|
+
protected IRubyObject indexes(final ThreadContext context, final String tableName, final String name, final String schemaName) {
|
748
|
+
return withConnection(context, new Callable<IRubyObject>() {
|
749
|
+
public IRubyObject call(final Connection connection) throws SQLException {
|
750
|
+
final Ruby runtime = context.getRuntime();
|
751
|
+
final RubyClass indexDefinition = getIndexDefinition(runtime);
|
752
|
+
|
753
|
+
final DatabaseMetaData metaData = connection.getMetaData();
|
754
|
+
String _tableName = caseConvertIdentifierForJdbc(metaData, tableName);
|
755
|
+
String _schemaName = caseConvertIdentifierForJdbc(metaData, schemaName);
|
756
|
+
|
757
|
+
final List<RubyString> primaryKeys = primaryKeys(context, _tableName);
|
758
|
+
ResultSet indexInfoSet = null;
|
759
|
+
final List<IRubyObject> indexes = new ArrayList<IRubyObject>();
|
760
|
+
try {
|
761
|
+
indexInfoSet = metaData.getIndexInfo(null, _schemaName, _tableName, false, true);
|
762
|
+
String currentIndex = null;
|
763
|
+
|
764
|
+
while ( indexInfoSet.next() ) {
|
765
|
+
String indexName = indexInfoSet.getString(INDEX_INFO_NAME);
|
766
|
+
if ( indexName == null ) continue;
|
767
|
+
indexName = caseConvertIdentifierForRails(metaData, indexName);
|
768
|
+
|
769
|
+
final String columnName = indexInfoSet.getString(INDEX_INFO_COLUMN_NAME);
|
770
|
+
final RubyString rubyColumnName = RubyString.newUnicodeString(
|
771
|
+
runtime, caseConvertIdentifierForRails(metaData, columnName)
|
772
|
+
);
|
773
|
+
if ( primaryKeys.contains(rubyColumnName) ) continue;
|
774
|
+
|
775
|
+
// We are working on a new index
|
776
|
+
if ( ! indexName.equals(currentIndex) ) {
|
777
|
+
currentIndex = indexName;
|
778
|
+
|
779
|
+
String indexTableName = indexInfoSet.getString(INDEX_INFO_TABLE_NAME);
|
780
|
+
indexTableName = caseConvertIdentifierForRails(metaData, indexTableName);
|
781
|
+
|
782
|
+
final boolean nonUnique = indexInfoSet.getBoolean(INDEX_INFO_NON_UNIQUE);
|
783
|
+
|
784
|
+
IRubyObject[] args = new IRubyObject[] {
|
785
|
+
RubyString.newUnicodeString(runtime, indexTableName), // table_name
|
786
|
+
RubyString.newUnicodeString(runtime, indexName), // index_name
|
787
|
+
runtime.newBoolean( ! nonUnique ), // unique
|
788
|
+
runtime.newArray() // [] for column names, we'll add to that in just a bit
|
789
|
+
// orders, (since AR 3.2) where, type, using (AR 4.0)
|
790
|
+
};
|
791
|
+
|
792
|
+
indexes.add( indexDefinition.callMethod(context, "new", args) ); // IndexDefinition.new
|
793
|
+
}
|
794
|
+
|
795
|
+
// One or more columns can be associated with an index
|
796
|
+
IRubyObject lastIndexDef = indexes.isEmpty() ? null : indexes.get(indexes.size() - 1);
|
797
|
+
if (lastIndexDef != null) {
|
798
|
+
lastIndexDef.callMethod(context, "columns").callMethod(context, "<<", rubyColumnName);
|
799
|
+
}
|
800
|
+
}
|
801
|
+
|
802
|
+
return runtime.newArray(indexes);
|
803
|
+
|
804
|
+
} finally { close(indexInfoSet); }
|
805
|
+
}
|
806
|
+
});
|
807
|
+
}
|
808
|
+
|
809
|
+
// NOTE: this seems to be not used ... at all ?!
|
810
|
+
/*
|
811
|
+
* sql, values, types, name = nil, pk = nil, id_value = nil, sequence_name = nil
|
812
|
+
*/
|
813
|
+
@Deprecated
|
814
|
+
@JRubyMethod(name = "insert_bind", required = 3, rest = true)
|
815
|
+
public IRubyObject insert_bind(final ThreadContext context, final IRubyObject[] args) throws SQLException {
|
816
|
+
final Ruby runtime = context.getRuntime();
|
817
|
+
return withConnection(context, new Callable<IRubyObject>() {
|
818
|
+
public IRubyObject call(final Connection connection) throws SQLException {
|
819
|
+
final String sql = args[0].convertToString().toString();
|
820
|
+
PreparedStatement statement = null;
|
821
|
+
try {
|
822
|
+
statement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
|
823
|
+
setValues(context, args[1], args[2], statement);
|
824
|
+
statement.executeUpdate();
|
825
|
+
return unmarshalIdResult(runtime, statement);
|
826
|
+
}
|
827
|
+
finally { close(statement); }
|
828
|
+
}
|
829
|
+
});
|
830
|
+
}
|
831
|
+
|
832
|
+
// NOTE: this seems to be not used ... at all ?!
|
833
|
+
/*
|
834
|
+
* sql, values, types, name = nil
|
835
|
+
*/
|
836
|
+
@Deprecated
|
837
|
+
@JRubyMethod(name = "update_bind", required = 3, rest = true)
|
838
|
+
public IRubyObject update_bind(final ThreadContext context, final IRubyObject[] args) throws SQLException {
|
839
|
+
final Ruby runtime = context.getRuntime();
|
840
|
+
Arity.checkArgumentCount(runtime, args, 3, 4);
|
841
|
+
return withConnection(context, new Callable<IRubyObject>() {
|
842
|
+
public IRubyObject call(final Connection connection) throws SQLException {
|
843
|
+
final String sql = args[0].convertToString().toString();
|
844
|
+
PreparedStatement statement = null;
|
845
|
+
try {
|
846
|
+
statement = connection.prepareStatement(sql);
|
847
|
+
setValues(context, args[1], args[2], statement);
|
848
|
+
statement.executeUpdate();
|
849
|
+
}
|
850
|
+
finally { close(statement); }
|
851
|
+
return runtime.getNil();
|
852
|
+
}
|
853
|
+
});
|
854
|
+
}
|
855
|
+
|
856
|
+
@JRubyMethod(name = "with_connection_retry_guard", frame = true)
|
857
|
+
public IRubyObject with_connection_retry_guard(final ThreadContext context, final Block block) {
|
858
|
+
return withConnection(context, new Callable<IRubyObject>() {
|
859
|
+
public IRubyObject call(final Connection connection) throws SQLException {
|
860
|
+
return block.call(context, new IRubyObject[] { convertJavaToRuby(connection) });
|
861
|
+
}
|
862
|
+
});
|
863
|
+
}
|
864
|
+
|
865
|
+
/*
|
866
|
+
* (is binary?, colname, tablename, primary_key, id, lob_value)
|
867
|
+
*/
|
868
|
+
@JRubyMethod(name = "write_large_object", required = 6)
|
869
|
+
public IRubyObject write_large_object(final ThreadContext context, final IRubyObject[] args)
|
870
|
+
throws SQLException {
|
871
|
+
|
872
|
+
final boolean isBinary = args[0].isTrue();
|
873
|
+
final RubyString columnName = args[1].convertToString();
|
874
|
+
final RubyString tableName = args[2].convertToString();
|
875
|
+
final RubyString idKey = args[3].convertToString();
|
876
|
+
final RubyString idVal = args[4].convertToString();
|
877
|
+
final IRubyObject lobValue = args[5];
|
878
|
+
|
879
|
+
final Ruby runtime = context.getRuntime();
|
880
|
+
return withConnection(context, new Callable<IRubyObject>() {
|
881
|
+
public IRubyObject call(final Connection connection) throws SQLException {
|
882
|
+
final String sql = "UPDATE "+ tableName +
|
883
|
+
" SET "+ columnName +" = ? WHERE "+ idKey +" = "+ idVal;
|
884
|
+
PreparedStatement statement = null;
|
885
|
+
try {
|
886
|
+
statement = connection.prepareStatement(sql);
|
887
|
+
if ( isBinary ) { // binary
|
888
|
+
final ByteList blob = lobValue.convertToString().getByteList();
|
889
|
+
final int realSize = blob.getRealSize();
|
890
|
+
statement.setBinaryStream(1,
|
891
|
+
new ByteArrayInputStream(blob.unsafeBytes(), blob.getBegin(), realSize), realSize
|
892
|
+
);
|
893
|
+
} else { // clob
|
894
|
+
String clob = lobValue.convertToString().getUnicodeValue();
|
895
|
+
statement.setCharacterStream(1, new StringReader(clob), clob.length());
|
896
|
+
}
|
897
|
+
statement.executeUpdate();
|
898
|
+
}
|
899
|
+
finally { close(statement); }
|
900
|
+
return runtime.getNil();
|
901
|
+
}
|
902
|
+
});
|
903
|
+
}
|
904
|
+
|
905
|
+
/**
|
906
|
+
* Convert an identifier coming back from the database to a case which Rails is expecting.
|
907
|
+
*
|
908
|
+
* Assumption: Rails identifiers will be quoted for mixed or will stay mixed
|
909
|
+
* as identifier names in Rails itself. Otherwise, they expect identifiers to
|
910
|
+
* be lower-case. Databases which store identifiers uppercase should be made
|
911
|
+
* lower-case.
|
645
912
|
*
|
646
913
|
* Assumption 2: It is always safe to convert all upper case names since it appears that
|
647
914
|
* some adapters do not report StoresUpper/Lower/Mixed correctly (am I right postgres/mysql?).
|
648
915
|
*/
|
649
|
-
|
650
|
-
|
651
|
-
if (value == null) return null;
|
652
|
-
|
653
|
-
return
|
916
|
+
protected static String caseConvertIdentifierForRails(final DatabaseMetaData metaData, final String value)
|
917
|
+
throws SQLException {
|
918
|
+
if ( value == null ) return null;
|
919
|
+
|
920
|
+
return metaData.storesUpperCaseIdentifiers() ? value.toLowerCase() : value;
|
654
921
|
}
|
655
922
|
|
656
923
|
/**
|
@@ -658,61 +925,37 @@ public class RubyJdbcConnection extends RubyObject {
|
|
658
925
|
* storage case. Methods like DatabaseMetaData.getPrimaryKeys() needs the table name to match
|
659
926
|
* the internal storage name. Arbtrary queries and the like DO NOT need to do this.
|
660
927
|
*/
|
661
|
-
|
662
|
-
|
663
|
-
if (value == null) return null;
|
664
|
-
|
665
|
-
|
666
|
-
if (metadata.storesUpperCaseIdentifiers()) {
|
928
|
+
protected String caseConvertIdentifierForJdbc(final DatabaseMetaData metaData, final String value)
|
929
|
+
throws SQLException {
|
930
|
+
if ( value == null ) return null;
|
931
|
+
|
932
|
+
if ( metaData.storesUpperCaseIdentifiers() ) {
|
667
933
|
return value.toUpperCase();
|
668
|
-
}
|
934
|
+
}
|
935
|
+
else if ( metaData.storesLowerCaseIdentifiers() ) {
|
669
936
|
return value.toLowerCase();
|
670
937
|
}
|
671
938
|
|
672
939
|
return value;
|
673
940
|
}
|
674
941
|
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
try {
|
679
|
-
connection.close();
|
680
|
-
} catch(Exception e) {}
|
681
|
-
}
|
682
|
-
}
|
683
|
-
|
684
|
-
public static void close(ResultSet resultSet) {
|
685
|
-
if (resultSet != null) {
|
686
|
-
try {
|
687
|
-
resultSet.close();
|
688
|
-
} catch(Exception e) {}
|
689
|
-
}
|
690
|
-
}
|
691
|
-
|
692
|
-
public static void close(Statement statement) {
|
693
|
-
if (statement != null) {
|
694
|
-
try {
|
695
|
-
statement.close();
|
696
|
-
} catch(Exception e) {}
|
697
|
-
}
|
942
|
+
protected IRubyObject getConfigValue(final ThreadContext context, final String key) {
|
943
|
+
final IRubyObject config = getInstanceVariable("@config");
|
944
|
+
return config.callMethod(context, "[]", context.getRuntime().newSymbol(key));
|
698
945
|
}
|
699
|
-
|
946
|
+
|
947
|
+
/**
|
948
|
+
* @deprecated renamed to {@link #getConfigValue(ThreadContext, String)}
|
949
|
+
*/
|
950
|
+
@Deprecated
|
700
951
|
protected IRubyObject config_value(ThreadContext context, String key) {
|
701
|
-
|
702
|
-
|
703
|
-
return config_hash.callMethod(context, "[]", context.getRuntime().newSymbol(key));
|
952
|
+
return getConfigValue(context, key);
|
704
953
|
}
|
705
954
|
|
706
955
|
private static String toStringOrNull(IRubyObject arg) {
|
707
956
|
return arg.isNil() ? null : arg.toString();
|
708
957
|
}
|
709
958
|
|
710
|
-
protected IRubyObject doubleToRuby(Ruby runtime, ResultSet resultSet, double doubleValue)
|
711
|
-
throws SQLException, IOException {
|
712
|
-
if (doubleValue == 0 && resultSet.wasNull()) return runtime.getNil();
|
713
|
-
return runtime.newFloat(doubleValue);
|
714
|
-
}
|
715
|
-
|
716
959
|
protected IRubyObject getAdapter(ThreadContext context) {
|
717
960
|
return callMethod(context, "adapter");
|
718
961
|
}
|
@@ -723,7 +966,7 @@ public class RubyJdbcConnection extends RubyObject {
|
|
723
966
|
|
724
967
|
protected JdbcConnectionFactory getConnectionFactory() throws RaiseException {
|
725
968
|
IRubyObject connection_factory = getInstanceVariable("@connection_factory");
|
726
|
-
if (connection_factory == null) {
|
969
|
+
if ( connection_factory == null ) {
|
727
970
|
throw getRuntime().newRuntimeError("@connection_factory not set");
|
728
971
|
}
|
729
972
|
JdbcConnectionFactory connectionFactory;
|
@@ -731,161 +974,402 @@ public class RubyJdbcConnection extends RubyObject {
|
|
731
974
|
connectionFactory = (JdbcConnectionFactory)
|
732
975
|
connection_factory.toJava(JdbcConnectionFactory.class);
|
733
976
|
}
|
734
|
-
catch (Exception e) {
|
735
|
-
|
736
|
-
}
|
737
|
-
if (connectionFactory == null) {
|
738
|
-
throw getRuntime().newRuntimeError("@connection_factory not set properly");
|
977
|
+
catch (Exception e) {
|
978
|
+
throw getRuntime().newRuntimeError("@connection_factory not set properly: " + e);
|
739
979
|
}
|
740
980
|
return connectionFactory;
|
741
981
|
}
|
742
982
|
|
743
|
-
private static String[] getTypes(IRubyObject typeArg) {
|
744
|
-
if (
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
return types;
|
753
|
-
}
|
754
|
-
|
755
|
-
private static int getTypeValueFor(Ruby runtime, IRubyObject type) throws SQLException {
|
756
|
-
// How could this ever yield anything useful?
|
757
|
-
if (!(type instanceof RubySymbol)) type = rubyApi.callMethod(type, "class");
|
758
|
-
|
759
|
-
// Assumption; If this is a symbol then it will be backed by an interned string. (enebo)
|
760
|
-
String internedValue = type.asJavaString();
|
761
|
-
|
762
|
-
if(internedValue == "string") {
|
763
|
-
return Types.VARCHAR;
|
764
|
-
} else if(internedValue == "text") {
|
765
|
-
return Types.CLOB;
|
766
|
-
} else if(internedValue == "integer") {
|
767
|
-
return Types.INTEGER;
|
768
|
-
} else if(internedValue == "decimal") {
|
769
|
-
return Types.DECIMAL;
|
770
|
-
} else if(internedValue == "float") {
|
771
|
-
return Types.FLOAT;
|
772
|
-
} else if(internedValue == "datetime") {
|
773
|
-
return Types.TIMESTAMP;
|
774
|
-
} else if(internedValue == "timestamp") {
|
775
|
-
return Types.TIMESTAMP;
|
776
|
-
} else if(internedValue == "time") {
|
777
|
-
return Types.TIME;
|
778
|
-
} else if(internedValue == "date") {
|
779
|
-
return Types.DATE;
|
780
|
-
} else if(internedValue == "binary") {
|
781
|
-
return Types.BLOB;
|
782
|
-
} else if(internedValue == "boolean") {
|
783
|
-
return Types.BOOLEAN;
|
784
|
-
} else {
|
785
|
-
return -1;
|
983
|
+
private static String[] getTypes(final IRubyObject typeArg) {
|
984
|
+
if ( typeArg instanceof RubyArray ) {
|
985
|
+
IRubyObject[] rubyTypes = ((RubyArray) typeArg).toJavaArray();
|
986
|
+
|
987
|
+
final String[] types = new String[rubyTypes.length];
|
988
|
+
for ( int i = 0; i < types.length; i++ ) {
|
989
|
+
types[i] = rubyTypes[i].toString();
|
990
|
+
}
|
991
|
+
return types;
|
786
992
|
}
|
993
|
+
return new String[] { typeArg.toString() }; // expect a RubyString
|
787
994
|
}
|
788
|
-
|
789
|
-
private
|
790
|
-
|
791
|
-
|
792
|
-
if (
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
close(s);
|
995
|
+
|
996
|
+
private static int jdbcTypeFor(final ThreadContext context, IRubyObject type)
|
997
|
+
throws SQLException {
|
998
|
+
if ( ! ( type instanceof RubySymbol ) ) {
|
999
|
+
if ( type instanceof RubyString ) { // to_sym
|
1000
|
+
if ( context.getRuntime().is1_9() ) {
|
1001
|
+
type = ( (RubyString) type ).intern19();
|
1002
|
+
}
|
1003
|
+
else {
|
1004
|
+
type = ( (RubyString) type ).intern();
|
799
1005
|
}
|
800
|
-
return false;
|
801
|
-
} else {
|
802
|
-
return !c.isClosed();
|
803
1006
|
}
|
804
|
-
|
805
|
-
|
1007
|
+
else {
|
1008
|
+
throw new IllegalArgumentException(
|
1009
|
+
"expected a Ruby string/symbol but got: " + type + " (" + type.getMetaClass().getName() + ")"
|
1010
|
+
);
|
1011
|
+
}
|
806
1012
|
}
|
1013
|
+
|
1014
|
+
final String internedValue = type.asJavaString();
|
1015
|
+
|
1016
|
+
if ( internedValue == (Object) "string" ) return Types.VARCHAR;
|
1017
|
+
else if ( internedValue == (Object) "text" ) return Types.CLOB;
|
1018
|
+
else if ( internedValue == (Object) "integer" ) return Types.INTEGER;
|
1019
|
+
else if ( internedValue == (Object) "decimal" ) return Types.DECIMAL;
|
1020
|
+
else if ( internedValue == (Object) "float" ) return Types.FLOAT;
|
1021
|
+
else if ( internedValue == (Object) "datetime") return Types.TIMESTAMP;
|
1022
|
+
else if ( internedValue == (Object) "timestamp" ) return Types.TIMESTAMP;
|
1023
|
+
else if ( internedValue == (Object) "time" ) return Types.TIME;
|
1024
|
+
else if ( internedValue == (Object) "date" ) return Types.DATE;
|
1025
|
+
else if ( internedValue == (Object) "binary" ) return Types.BLOB;
|
1026
|
+
else if ( internedValue == (Object) "boolean" ) return Types.BOOLEAN;
|
1027
|
+
else if ( internedValue == (Object) "xml" ) return Types.SQLXML;
|
1028
|
+
else if ( internedValue == (Object) "array" ) return Types.ARRAY;
|
1029
|
+
else return -1;
|
807
1030
|
}
|
808
1031
|
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
1032
|
+
/**
|
1033
|
+
* @deprecated this method is no longer used, instead consider overriding
|
1034
|
+
* {@link #mapToResult(ThreadContext, Ruby, DatabaseMetaData, ResultSet, RubyJdbcConnection.ColumnData[])}
|
1035
|
+
*/
|
1036
|
+
@Deprecated
|
1037
|
+
protected void populateFromResultSet(
|
1038
|
+
final ThreadContext context, final Ruby runtime,
|
1039
|
+
final List<IRubyObject> results, final ResultSet resultSet,
|
1040
|
+
final ColumnData[] columns) throws SQLException {
|
1041
|
+
final ResultHandler resultHandler = ResultHandler.getInstance(context);
|
1042
|
+
while ( resultSet.next() ) {
|
1043
|
+
results.add( resultHandler.mapRawRow(context, runtime, columns, resultSet, this) );
|
1044
|
+
}
|
814
1045
|
}
|
815
1046
|
|
816
|
-
|
817
|
-
|
1047
|
+
/**
|
1048
|
+
* Maps a query result into a <code>ActiveRecord</code> result.
|
1049
|
+
* @param context
|
1050
|
+
* @param runtime
|
1051
|
+
* @param metaData
|
1052
|
+
* @param resultSet
|
1053
|
+
* @param columns
|
1054
|
+
* @return since 3.1 expected to return a <code>ActiveRecord::Result</code>
|
1055
|
+
* @throws SQLException
|
1056
|
+
*/
|
1057
|
+
protected IRubyObject mapToResult(final ThreadContext context, final Ruby runtime,
|
1058
|
+
final DatabaseMetaData metaData, final ResultSet resultSet,
|
1059
|
+
final ColumnData[] columns) throws SQLException {
|
818
1060
|
|
819
|
-
|
1061
|
+
final ResultHandler resultHandler = ResultHandler.getInstance(context);
|
1062
|
+
final RubyArray resultRows = runtime.newArray();
|
1063
|
+
|
1064
|
+
while ( resultSet.next() ) {
|
1065
|
+
resultRows.add( resultHandler.mapRow(context, runtime, columns, resultSet, this) );
|
1066
|
+
}
|
1067
|
+
|
1068
|
+
return resultHandler.newResult(context, runtime, columns, resultRows);
|
820
1069
|
}
|
821
|
-
|
822
|
-
protected IRubyObject jdbcToRuby(Ruby runtime, int column,
|
823
|
-
|
1070
|
+
|
1071
|
+
protected IRubyObject jdbcToRuby(final Ruby runtime, final int column,
|
1072
|
+
final int type, final ResultSet resultSet) throws SQLException {
|
824
1073
|
try {
|
825
1074
|
switch (type) {
|
826
|
-
case Types.BINARY:
|
827
1075
|
case Types.BLOB:
|
828
|
-
case Types.
|
1076
|
+
case Types.BINARY:
|
829
1077
|
case Types.VARBINARY:
|
830
|
-
|
831
|
-
|
832
|
-
return runtime.is1_9() ?
|
833
|
-
readerToRuby(runtime, resultSet, resultSet.getCharacterStream(column)) :
|
834
|
-
streamToRuby(runtime, resultSet, resultSet.getBinaryStream(column));
|
1078
|
+
case Types.LONGVARBINARY:
|
1079
|
+
return streamToRuby(runtime, resultSet, column);
|
835
1080
|
case Types.CLOB:
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
case Types.
|
840
|
-
|
1081
|
+
case Types.NCLOB: // JDBC 4.0
|
1082
|
+
return readerToRuby(runtime, resultSet, column);
|
1083
|
+
case Types.LONGVARCHAR:
|
1084
|
+
case Types.LONGNVARCHAR: // JDBC 4.0
|
1085
|
+
if ( runtime.is1_9() ) {
|
1086
|
+
return readerToRuby(runtime, resultSet, column);
|
1087
|
+
}
|
1088
|
+
else {
|
1089
|
+
return streamToRuby(runtime, resultSet, column);
|
1090
|
+
}
|
841
1091
|
case Types.TINYINT:
|
842
|
-
|
1092
|
+
case Types.SMALLINT:
|
1093
|
+
case Types.INTEGER:
|
1094
|
+
return integerToRuby(runtime, resultSet, column);
|
843
1095
|
case Types.REAL:
|
844
|
-
|
1096
|
+
case Types.FLOAT:
|
1097
|
+
case Types.DOUBLE:
|
1098
|
+
return doubleToRuby(runtime, resultSet, column);
|
845
1099
|
case Types.BIGINT:
|
846
|
-
return bigIntegerToRuby(runtime, resultSet,
|
1100
|
+
return bigIntegerToRuby(runtime, resultSet, column);
|
1101
|
+
case Types.NUMERIC:
|
1102
|
+
case Types.DECIMAL:
|
1103
|
+
return decimalToRuby(runtime, resultSet, column);
|
1104
|
+
case Types.DATE:
|
1105
|
+
return dateToRuby(runtime, resultSet, column);
|
1106
|
+
case Types.TIME:
|
1107
|
+
return timeToRuby(runtime, resultSet, column);
|
1108
|
+
case Types.TIMESTAMP:
|
1109
|
+
return timestampToRuby(runtime, resultSet, column);
|
1110
|
+
case Types.BIT:
|
1111
|
+
case Types.BOOLEAN:
|
1112
|
+
return booleanToRuby(runtime, resultSet, column);
|
1113
|
+
case Types.SQLXML: // JDBC 4.0
|
1114
|
+
return xmlToRuby(runtime, resultSet, column);
|
1115
|
+
case Types.NULL:
|
1116
|
+
return runtime.getNil();
|
1117
|
+
// NOTE: (JDBC) exotic stuff just cause it's so easy with JRuby :)
|
1118
|
+
case Types.JAVA_OBJECT:
|
1119
|
+
case Types.OTHER:
|
1120
|
+
return objectToRuby(runtime, resultSet, column);
|
1121
|
+
case Types.ARRAY: // we handle JDBC Array into (Ruby) []
|
1122
|
+
return arrayToRuby(runtime, resultSet, column);
|
1123
|
+
// (default) String
|
1124
|
+
case Types.CHAR:
|
1125
|
+
case Types.VARCHAR:
|
1126
|
+
case Types.NCHAR: // JDBC 4.0
|
1127
|
+
case Types.NVARCHAR: // JDBC 4.0
|
847
1128
|
default:
|
848
|
-
return stringToRuby(runtime, resultSet,
|
1129
|
+
return stringToRuby(runtime, resultSet, column);
|
849
1130
|
}
|
850
|
-
|
851
|
-
|
1131
|
+
// NOTE: not mapped types :
|
1132
|
+
//case Types.DISTINCT:
|
1133
|
+
//case Types.STRUCT:
|
1134
|
+
//case Types.REF:
|
1135
|
+
//case Types.DATALINK:
|
1136
|
+
}
|
1137
|
+
catch (IOException e) {
|
1138
|
+
throw new SQLException(e.getMessage(), e);
|
852
1139
|
}
|
853
1140
|
}
|
854
1141
|
|
855
|
-
protected
|
856
|
-
|
857
|
-
|
1142
|
+
protected IRubyObject integerToRuby(
|
1143
|
+
final Ruby runtime, final ResultSet resultSet, final int column)
|
1144
|
+
throws SQLException {
|
1145
|
+
final long value = resultSet.getLong(column);
|
1146
|
+
if ( value == 0 && resultSet.wasNull() ) return runtime.getNil();
|
1147
|
+
return integerToRuby(runtime, resultSet, value);
|
1148
|
+
}
|
1149
|
+
|
1150
|
+
@Deprecated
|
1151
|
+
protected IRubyObject integerToRuby(
|
1152
|
+
final Ruby runtime, final ResultSet resultSet, final long longValue)
|
1153
|
+
throws SQLException {
|
1154
|
+
if ( longValue == 0 && resultSet.wasNull() ) return runtime.getNil();
|
858
1155
|
|
859
|
-
|
860
|
-
|
1156
|
+
return runtime.newFixnum(longValue);
|
1157
|
+
}
|
861
1158
|
|
862
|
-
|
863
|
-
|
864
|
-
|
1159
|
+
protected IRubyObject doubleToRuby(Ruby runtime, ResultSet resultSet, final int column)
|
1160
|
+
throws SQLException {
|
1161
|
+
final double value = resultSet.getDouble(column);
|
1162
|
+
if ( value == 0 && resultSet.wasNull() ) return runtime.getNil();
|
1163
|
+
return doubleToRuby(runtime, resultSet, value);
|
1164
|
+
}
|
1165
|
+
|
1166
|
+
@Deprecated
|
1167
|
+
protected IRubyObject doubleToRuby(Ruby runtime, ResultSet resultSet, double doubleValue)
|
1168
|
+
throws SQLException {
|
1169
|
+
if ( doubleValue == 0 && resultSet.wasNull() ) return runtime.getNil();
|
1170
|
+
return runtime.newFloat(doubleValue);
|
1171
|
+
}
|
865
1172
|
|
866
|
-
|
867
|
-
|
1173
|
+
protected IRubyObject stringToRuby(
|
1174
|
+
final Ruby runtime, final ResultSet resultSet, final int column)
|
1175
|
+
throws SQLException {
|
1176
|
+
final String value = resultSet.getString(column);
|
1177
|
+
if ( value == null && resultSet.wasNull() ) return runtime.getNil();
|
1178
|
+
return stringToRuby(runtime, resultSet, value);
|
868
1179
|
}
|
1180
|
+
|
1181
|
+
@Deprecated
|
1182
|
+
protected IRubyObject stringToRuby(
|
1183
|
+
final Ruby runtime, final ResultSet resultSet, final String string)
|
1184
|
+
throws SQLException {
|
1185
|
+
if ( string == null && resultSet.wasNull() ) return runtime.getNil();
|
869
1186
|
|
1187
|
+
return RubyString.newUnicodeString(runtime, string);
|
1188
|
+
}
|
870
1189
|
|
871
|
-
protected IRubyObject
|
872
|
-
|
873
|
-
|
1190
|
+
protected IRubyObject bigIntegerToRuby(
|
1191
|
+
final Ruby runtime, final ResultSet resultSet, final int column)
|
1192
|
+
throws SQLException {
|
1193
|
+
final String value = resultSet.getString(column);
|
1194
|
+
if ( value == null && resultSet.wasNull() ) return runtime.getNil();
|
1195
|
+
return bigIntegerToRuby(runtime, resultSet, value);
|
1196
|
+
}
|
1197
|
+
|
1198
|
+
@Deprecated
|
1199
|
+
protected IRubyObject bigIntegerToRuby(
|
1200
|
+
final Ruby runtime, final ResultSet resultSet, final String intValue)
|
1201
|
+
throws SQLException {
|
1202
|
+
if ( intValue == null && resultSet.wasNull() ) return runtime.getNil();
|
874
1203
|
|
875
|
-
|
1204
|
+
return RubyBignum.bignorm(runtime, new BigInteger(intValue));
|
1205
|
+
}
|
1206
|
+
|
1207
|
+
protected IRubyObject decimalToRuby(
|
1208
|
+
final Ruby runtime, final ResultSet resultSet, final int column)
|
1209
|
+
throws SQLException {
|
1210
|
+
final String value = resultSet.getString(column);
|
1211
|
+
if ( value == null && resultSet.wasNull() ) return runtime.getNil();
|
1212
|
+
// NOTE: JRuby 1.6 -> 1.7 API change : moved org.jruby.RubyBigDecimal
|
1213
|
+
return runtime.getKernel().callMethod("BigDecimal", runtime.newString(value));
|
1214
|
+
}
|
1215
|
+
|
1216
|
+
private static boolean parseDateTime = false; // TODO
|
1217
|
+
|
1218
|
+
protected IRubyObject dateToRuby( // TODO
|
1219
|
+
final Ruby runtime, final ResultSet resultSet, final int column)
|
1220
|
+
throws SQLException {
|
1221
|
+
final Date value = resultSet.getDate(column);
|
1222
|
+
if ( value == null && resultSet.wasNull() ) return runtime.getNil();
|
1223
|
+
return RubyString.newUnicodeString(runtime, value.toString());
|
1224
|
+
}
|
1225
|
+
|
1226
|
+
protected IRubyObject timeToRuby( // TODO
|
1227
|
+
final Ruby runtime, final ResultSet resultSet, final int column)
|
1228
|
+
throws SQLException {
|
1229
|
+
final Time value = resultSet.getTime(column);
|
1230
|
+
if ( value == null && resultSet.wasNull() ) return runtime.getNil();
|
1231
|
+
return RubyString.newUnicodeString(runtime, value.toString());
|
1232
|
+
}
|
1233
|
+
|
1234
|
+
protected IRubyObject timestampToRuby(
|
1235
|
+
final Ruby runtime, final ResultSet resultSet, final int column)
|
1236
|
+
throws SQLException {
|
1237
|
+
final Timestamp value = resultSet.getTimestamp(column);
|
1238
|
+
if ( value == null && resultSet.wasNull() ) return runtime.getNil();
|
1239
|
+
return timestampToRuby(runtime, resultSet, value);
|
1240
|
+
}
|
1241
|
+
|
1242
|
+
@Deprecated
|
1243
|
+
protected IRubyObject timestampToRuby(
|
1244
|
+
final Ruby runtime, final ResultSet resultSet, final Timestamp value)
|
1245
|
+
throws SQLException {
|
1246
|
+
if ( value == null && resultSet.wasNull() ) return runtime.getNil();
|
1247
|
+
|
1248
|
+
String format = value.toString(); // yyyy-mm-dd hh:mm:ss.fffffffff
|
1249
|
+
if ( format.endsWith(" 00:00:00.0") ) {
|
1250
|
+
format = format.substring(0, format.length() - (" 00:00:00.0".length()));
|
1251
|
+
}
|
1252
|
+
if ( format.endsWith(".0") ) {
|
1253
|
+
format = format.substring(0, format.length() - (".0".length()));
|
1254
|
+
}
|
1255
|
+
|
1256
|
+
return RubyString.newUnicodeString(runtime, format);
|
1257
|
+
}
|
1258
|
+
|
1259
|
+
protected IRubyObject booleanToRuby(
|
1260
|
+
final Ruby runtime, final ResultSet resultSet, final int column)
|
1261
|
+
throws SQLException {
|
1262
|
+
final boolean value = resultSet.getBoolean(column);
|
1263
|
+
if ( resultSet.wasNull() ) return runtime.getNil();
|
1264
|
+
return booleanToRuby(runtime, resultSet, value);
|
1265
|
+
}
|
1266
|
+
|
1267
|
+
@Deprecated
|
1268
|
+
protected IRubyObject booleanToRuby(
|
1269
|
+
final Ruby runtime, final ResultSet resultSet, final boolean value)
|
1270
|
+
throws SQLException {
|
1271
|
+
if ( value == false && resultSet.wasNull() ) return runtime.getNil();
|
1272
|
+
return runtime.newBoolean(value);
|
1273
|
+
}
|
1274
|
+
|
1275
|
+
protected static int streamBufferSize = 2048;
|
1276
|
+
|
1277
|
+
protected IRubyObject streamToRuby(
|
1278
|
+
final Ruby runtime, final ResultSet resultSet, final int column)
|
1279
|
+
throws SQLException, IOException {
|
1280
|
+
final InputStream stream = resultSet.getBinaryStream(column);
|
876
1281
|
try {
|
877
|
-
|
1282
|
+
if ( resultSet.wasNull() ) return runtime.getNil();
|
1283
|
+
return streamToRuby(runtime, resultSet, stream);
|
1284
|
+
}
|
1285
|
+
finally { if ( stream != null ) stream.close(); }
|
1286
|
+
}
|
1287
|
+
|
1288
|
+
@Deprecated
|
1289
|
+
protected IRubyObject streamToRuby(
|
1290
|
+
final Ruby runtime, final ResultSet resultSet, final InputStream stream)
|
1291
|
+
throws SQLException, IOException {
|
1292
|
+
if ( stream == null && resultSet.wasNull() ) return runtime.getNil();
|
878
1293
|
|
879
|
-
|
880
|
-
|
881
|
-
|
882
|
-
|
883
|
-
|
1294
|
+
final int bufSize = streamBufferSize;
|
1295
|
+
final ByteList string = new ByteList(bufSize);
|
1296
|
+
|
1297
|
+
final byte[] buf = new byte[bufSize];
|
1298
|
+
for (int len = stream.read(buf); len != -1; len = stream.read(buf)) {
|
1299
|
+
string.append(buf, 0, len);
|
884
1300
|
}
|
885
1301
|
|
886
|
-
return
|
1302
|
+
return runtime.newString(string);
|
887
1303
|
}
|
888
1304
|
|
1305
|
+
protected IRubyObject readerToRuby(
|
1306
|
+
final Ruby runtime, final ResultSet resultSet, final int column)
|
1307
|
+
throws SQLException, IOException {
|
1308
|
+
final Reader reader = resultSet.getCharacterStream(column);
|
1309
|
+
try {
|
1310
|
+
if ( resultSet.wasNull() ) return runtime.getNil();
|
1311
|
+
return readerToRuby(runtime, resultSet, reader);
|
1312
|
+
}
|
1313
|
+
finally { if ( reader != null ) reader.close(); }
|
1314
|
+
}
|
1315
|
+
|
1316
|
+
@Deprecated
|
1317
|
+
protected IRubyObject readerToRuby(
|
1318
|
+
final Ruby runtime, final ResultSet resultSet, final Reader reader)
|
1319
|
+
throws SQLException, IOException {
|
1320
|
+
if ( reader == null && resultSet.wasNull() ) return runtime.getNil();
|
1321
|
+
|
1322
|
+
final int bufSize = streamBufferSize;
|
1323
|
+
final StringBuilder string = new StringBuilder(bufSize);
|
1324
|
+
|
1325
|
+
final char[] buf = new char[bufSize];
|
1326
|
+
for (int len = reader.read(buf); len != -1; len = reader.read(buf)) {
|
1327
|
+
string.append(buf, 0, len);
|
1328
|
+
}
|
1329
|
+
|
1330
|
+
return RubyString.newUnicodeString(runtime, string.toString());
|
1331
|
+
}
|
1332
|
+
|
1333
|
+
protected IRubyObject objectToRuby(
|
1334
|
+
final Ruby runtime, final ResultSet resultSet, final int column)
|
1335
|
+
throws SQLException {
|
1336
|
+
final Object value = resultSet.getObject(column);
|
1337
|
+
|
1338
|
+
if ( value == null && resultSet.wasNull() ) return runtime.getNil();
|
1339
|
+
|
1340
|
+
return JavaUtil.convertJavaToRuby(runtime, value);
|
1341
|
+
}
|
1342
|
+
|
1343
|
+
protected IRubyObject arrayToRuby(
|
1344
|
+
final Ruby runtime, final ResultSet resultSet, final int column)
|
1345
|
+
throws SQLException {
|
1346
|
+
final Array value = resultSet.getArray(column);
|
1347
|
+
try {
|
1348
|
+
if ( value == null && resultSet.wasNull() ) return runtime.getNil();
|
1349
|
+
|
1350
|
+
final RubyArray array = runtime.newArray();
|
1351
|
+
|
1352
|
+
final ResultSet arrayResult = value.getResultSet(); // 1: index, 2: value
|
1353
|
+
final int baseType = value.getBaseType();
|
1354
|
+
while ( arrayResult.next() ) {
|
1355
|
+
IRubyObject element = jdbcToRuby(runtime, 2, baseType, arrayResult);
|
1356
|
+
array.append(element);
|
1357
|
+
}
|
1358
|
+
return array;
|
1359
|
+
}
|
1360
|
+
finally { value.free(); }
|
1361
|
+
}
|
1362
|
+
|
1363
|
+
protected IRubyObject xmlToRuby(
|
1364
|
+
final Ruby runtime, final ResultSet resultSet, final int column)
|
1365
|
+
throws SQLException {
|
1366
|
+
final SQLXML xml = resultSet.getSQLXML(column);
|
1367
|
+
try {
|
1368
|
+
return RubyString.newUnicodeString(runtime, xml.getString());
|
1369
|
+
}
|
1370
|
+
finally { xml.free(); }
|
1371
|
+
}
|
1372
|
+
|
889
1373
|
protected final Connection getConnection() {
|
890
1374
|
return getConnection(false);
|
891
1375
|
}
|
@@ -893,8 +1377,8 @@ public class RubyJdbcConnection extends RubyObject {
|
|
893
1377
|
protected Connection getConnection(boolean error) {
|
894
1378
|
final Connection connection = (Connection) dataGetStruct();
|
895
1379
|
if ( connection == null && error ) {
|
896
|
-
RubyClass
|
897
|
-
throw new RaiseException(getRuntime(),
|
1380
|
+
final RubyClass errorClass = getConnectionNotEstablished( getRuntime() );
|
1381
|
+
throw new RaiseException(getRuntime(), errorClass, "no connection available", false);
|
898
1382
|
}
|
899
1383
|
return connection;
|
900
1384
|
}
|
@@ -903,166 +1387,195 @@ public class RubyJdbcConnection extends RubyObject {
|
|
903
1387
|
close( getConnection(false) ); // close previously open connection if there is one
|
904
1388
|
|
905
1389
|
final IRubyObject rubyConnectionObject =
|
906
|
-
connection != null ?
|
1390
|
+
connection != null ? convertJavaToRuby(connection) : getRuntime().getNil();
|
907
1391
|
setInstanceVariable( "@connection", rubyConnectionObject );
|
908
1392
|
dataWrapStruct(connection);
|
909
1393
|
return this;
|
910
1394
|
}
|
911
1395
|
|
1396
|
+
private boolean isConnectionBroken(final ThreadContext context, final Connection connection) {
|
1397
|
+
Statement statement = null;
|
1398
|
+
try {
|
1399
|
+
final RubyString aliveSQL = getConfigValue(context, "connection_alive_sql").convertToString();
|
1400
|
+
if ( isSelect(aliveSQL) ) { // expect a SELECT/CALL SQL statement
|
1401
|
+
statement = connection.createStatement();
|
1402
|
+
statement.execute( aliveSQL.toString() );
|
1403
|
+
return false; // connection ain't broken
|
1404
|
+
}
|
1405
|
+
else { // alive_sql nil (or not a statement we can execute)
|
1406
|
+
return ! connection.isClosed(); // if closed than broken
|
1407
|
+
}
|
1408
|
+
}
|
1409
|
+
catch (Exception e) {
|
1410
|
+
debugMessage(context, "connection considered broken due: " + e.toString());
|
1411
|
+
return true;
|
1412
|
+
}
|
1413
|
+
finally { close(statement); }
|
1414
|
+
}
|
1415
|
+
|
912
1416
|
private final static DateFormat FORMAT = new SimpleDateFormat("%y-%M-%d %H:%m:%s");
|
913
1417
|
|
914
|
-
private static void setValue(
|
915
|
-
IRubyObject value, IRubyObject type
|
916
|
-
|
917
|
-
|
918
|
-
|
1418
|
+
private static void setValue(final ThreadContext context,
|
1419
|
+
final IRubyObject value, final IRubyObject type,
|
1420
|
+
final PreparedStatement statement, final int index) throws SQLException {
|
1421
|
+
|
1422
|
+
final int jdbcType = jdbcTypeFor(context, type);
|
1423
|
+
|
1424
|
+
if ( value.isNil() ) {
|
1425
|
+
statement.setNull(index, jdbcType);
|
919
1426
|
return;
|
920
1427
|
}
|
921
1428
|
|
922
|
-
switch(
|
1429
|
+
switch (jdbcType) {
|
923
1430
|
case Types.VARCHAR:
|
924
1431
|
case Types.CLOB:
|
925
|
-
|
1432
|
+
statement.setString(index, RubyString.objAsString(context, value).toString());
|
926
1433
|
break;
|
927
1434
|
case Types.INTEGER:
|
928
|
-
|
1435
|
+
statement.setLong(index, RubyNumeric.fix2long(value));
|
929
1436
|
break;
|
930
1437
|
case Types.FLOAT:
|
931
|
-
|
1438
|
+
statement.setDouble(index, ((RubyNumeric) value).getDoubleValue());
|
932
1439
|
break;
|
933
1440
|
case Types.TIMESTAMP:
|
934
1441
|
case Types.TIME:
|
935
1442
|
case Types.DATE:
|
936
|
-
if(!(value instanceof RubyTime)) {
|
1443
|
+
if ( ! ( value instanceof RubyTime ) ) {
|
1444
|
+
final String stringValue = RubyString.objAsString(context, value).toString();
|
937
1445
|
try {
|
938
|
-
|
939
|
-
|
940
|
-
}
|
941
|
-
|
1446
|
+
Timestamp timestamp = new Timestamp( FORMAT.parse( stringValue ).getTime() );
|
1447
|
+
statement.setTimestamp( index, timestamp, Calendar.getInstance() );
|
1448
|
+
}
|
1449
|
+
catch (Exception e) {
|
1450
|
+
statement.setString( index, stringValue );
|
942
1451
|
}
|
943
1452
|
} else {
|
944
|
-
RubyTime
|
945
|
-
java.util.Date
|
946
|
-
|
947
|
-
long
|
948
|
-
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
|
1453
|
+
final RubyTime timeValue = (RubyTime) value;
|
1454
|
+
final java.util.Date dateValue = timeValue.getJavaDate();
|
1455
|
+
|
1456
|
+
long millis = dateValue.getTime();
|
1457
|
+
Timestamp timestamp = new Timestamp(millis);
|
1458
|
+
Calendar calendar = Calendar.getInstance();
|
1459
|
+
calendar.setTime(dateValue);
|
1460
|
+
if ( jdbcType != Types.DATE ) {
|
1461
|
+
int micros = (int) timeValue.microseconds();
|
1462
|
+
timestamp.setNanos( micros * 1000 ); // time.nsec ~ time.usec * 1000
|
1463
|
+
}
|
1464
|
+
statement.setTimestamp( index, timestamp, calendar );
|
953
1465
|
}
|
954
1466
|
break;
|
955
1467
|
case Types.BOOLEAN:
|
956
|
-
|
1468
|
+
statement.setBoolean(index, value.isTrue());
|
957
1469
|
break;
|
958
|
-
default: throw new RuntimeException("type " +
|
1470
|
+
default: throw new RuntimeException("type " + jdbcType + " not supported in _bind (yet)");
|
959
1471
|
}
|
960
1472
|
}
|
961
1473
|
|
962
|
-
private static void
|
963
|
-
IRubyObject valuesArg, IRubyObject typesArg
|
964
|
-
|
965
|
-
RubyArray
|
966
|
-
|
967
|
-
for(int i=0, j=values.getLength(); i<j; i++) {
|
968
|
-
setValue(
|
1474
|
+
private static void setValues(final ThreadContext context,
|
1475
|
+
final IRubyObject valuesArg, final IRubyObject typesArg,
|
1476
|
+
final PreparedStatement statement) throws SQLException {
|
1477
|
+
final RubyArray values = (RubyArray) valuesArg;
|
1478
|
+
final RubyArray types = (RubyArray) typesArg;
|
1479
|
+
for( int i = 0, j = values.getLength(); i < j; i++ ) {
|
1480
|
+
setValue(context, values.eltInternal(i), types.eltInternal(i), statement, i + 1);
|
969
1481
|
}
|
970
1482
|
}
|
971
|
-
|
972
|
-
|
973
|
-
|
974
|
-
|
975
|
-
|
976
|
-
|
1483
|
+
|
1484
|
+
private boolean tableExists(final Ruby runtime,
|
1485
|
+
final Connection connection, final TableName tableName) throws SQLException {
|
1486
|
+
final IRubyObject matchedTables =
|
1487
|
+
matchTables(runtime, connection, tableName.catalog, tableName.schema, tableName.name, getTableTypes(), true);
|
1488
|
+
// NOTE: allow implementers to ignore checkExistsOnly paramater - empty array means does not exists
|
1489
|
+
return matchedTables != null && ! matchedTables.isNil() &&
|
1490
|
+
( ! (matchedTables instanceof RubyArray) || ! ((RubyArray) matchedTables).isEmpty() );
|
1491
|
+
}
|
1492
|
+
|
1493
|
+
/**
|
1494
|
+
* Match table names for given table name (pattern).
|
1495
|
+
* @param runtime
|
1496
|
+
* @param connection
|
1497
|
+
* @param catalog
|
1498
|
+
* @param schemaPattern
|
1499
|
+
* @param tablePattern
|
1500
|
+
* @param types table types
|
1501
|
+
* @param checkExistsOnly an optimization flag (that might be ignored by sub-classes)
|
1502
|
+
* whether the result really matters if true no need to map table names and a truth-y
|
1503
|
+
* value is sufficient (except for an empty array which is considered that the table
|
1504
|
+
* did not exists).
|
1505
|
+
* @return matched (and Ruby mapped) table names
|
1506
|
+
* @see #mapTables(Ruby, DatabaseMetaData, String, String, String, ResultSet)
|
1507
|
+
* @throws SQLException
|
1508
|
+
*/
|
1509
|
+
protected IRubyObject matchTables(final Ruby runtime,
|
1510
|
+
final Connection connection,
|
1511
|
+
final String catalog, final String schemaPattern,
|
1512
|
+
final String tablePattern, final String[] types,
|
1513
|
+
final boolean checkExistsOnly) throws SQLException {
|
1514
|
+
|
1515
|
+
final DatabaseMetaData metaData = connection.getMetaData();
|
1516
|
+
|
1517
|
+
final String _tablePattern = caseConvertIdentifierForJdbc(metaData, tablePattern);
|
1518
|
+
final String _schemaPattern = caseConvertIdentifierForJdbc(metaData, schemaPattern);
|
1519
|
+
|
1520
|
+
ResultSet tablesSet = null;
|
977
1521
|
try {
|
978
|
-
|
979
|
-
|
980
|
-
|
981
|
-
|
1522
|
+
tablesSet = metaData.getTables(catalog, _schemaPattern, _tablePattern, types);
|
1523
|
+
if ( checkExistsOnly ) { // only check if given table exists
|
1524
|
+
return tablesSet.next() ? runtime.getTrue() : null;
|
1525
|
+
}
|
1526
|
+
else {
|
1527
|
+
return mapTables(runtime, metaData, catalog, _schemaPattern, _tablePattern, tablesSet);
|
982
1528
|
}
|
983
|
-
} finally {
|
984
|
-
is.close();
|
985
1529
|
}
|
986
|
-
|
987
|
-
return runtime.newString(str);
|
1530
|
+
finally { close(tablesSet); }
|
988
1531
|
}
|
989
|
-
|
990
|
-
|
991
|
-
|
992
|
-
|
993
|
-
|
994
|
-
|
1532
|
+
|
1533
|
+
// NOTE java.sql.DatabaseMetaData.getTables :
|
1534
|
+
protected final static int TABLES_TABLE_CAT = 1;
|
1535
|
+
protected final static int TABLES_TABLE_SCHEM = 2;
|
1536
|
+
protected final static int TABLES_TABLE_NAME = 3;
|
1537
|
+
protected final static int TABLES_TABLE_TYPE = 4;
|
1538
|
+
|
1539
|
+
/**
|
1540
|
+
* @param runtime
|
1541
|
+
* @param metaData
|
1542
|
+
* @param catalog
|
1543
|
+
* @param schemaPattern
|
1544
|
+
* @param tablePattern
|
1545
|
+
* @param tablesSet
|
1546
|
+
* @return List<RubyString>
|
1547
|
+
* @throws SQLException
|
1548
|
+
*/
|
1549
|
+
protected RubyArray mapTables(final Ruby runtime, final DatabaseMetaData metaData,
|
1550
|
+
final String catalog, final String schemaPattern, final String tablePattern,
|
1551
|
+
final ResultSet tablesSet) throws SQLException {
|
1552
|
+
final RubyArray tables = runtime.newArray();
|
1553
|
+
while ( tablesSet.next() ) {
|
1554
|
+
String name = tablesSet.getString(TABLES_TABLE_NAME);
|
1555
|
+
name = caseConvertIdentifierForRails(metaData, name);
|
1556
|
+
tables.add(RubyString.newUnicodeString(runtime, name));
|
1557
|
+
}
|
1558
|
+
return tables;
|
995
1559
|
}
|
996
1560
|
|
997
|
-
|
1561
|
+
/**
|
1562
|
+
* NOTE: since 1.3.0 only present for binary compatibility (with extensions).
|
1563
|
+
*
|
1564
|
+
* @depreacated no longer used - replaced with
|
1565
|
+
* {@link #matchTables(Ruby, Connection, String, String, String, String[], boolean)}
|
1566
|
+
* please update your sub-class esp. if you're overriding this method !
|
1567
|
+
*/
|
1568
|
+
@Deprecated
|
998
1569
|
protected SQLBlock tableLookupBlock(final Ruby runtime,
|
999
|
-
final String catalog, final String
|
1000
|
-
final String
|
1001
|
-
final int TABLE_SCHEM = 2;
|
1002
|
-
final int TABLE_NAME = 3;
|
1003
|
-
final int TABLE_TYPE = 4;
|
1570
|
+
final String catalog, final String schemaPattern,
|
1571
|
+
final String tablePattern, final String[] types) {
|
1004
1572
|
return new SQLBlock() {
|
1005
|
-
public
|
1006
|
-
|
1007
|
-
try {
|
1008
|
-
DatabaseMetaData metadata = c.getMetaData();
|
1009
|
-
String clzName = metadata.getClass().getName().toLowerCase();
|
1010
|
-
boolean isOracle = clzName.indexOf("oracle") != -1 || clzName.indexOf("oci") != -1;
|
1011
|
-
boolean isDerby = clzName.indexOf("derby") != -1;
|
1012
|
-
boolean isMssql = clzName.indexOf("sqlserver") != -1 || clzName.indexOf("tds") != -1;
|
1013
|
-
|
1014
|
-
String realschema = schemapat;
|
1015
|
-
String realtablepat = tablepat;
|
1016
|
-
|
1017
|
-
if (isDerby && realschema != null && realschema.equals("")) realschema = null; // Derby doesn't like empty-string schema name
|
1018
|
-
if (realtablepat != null) realtablepat = caseConvertIdentifierForJdbc(metadata, realtablepat);
|
1019
|
-
if (realschema != null) realschema = caseConvertIdentifierForJdbc(metadata, realschema);
|
1020
|
-
|
1021
|
-
rs = metadata.getTables(catalog, realschema, realtablepat, types);
|
1022
|
-
List arr = new ArrayList();
|
1023
|
-
while (rs.next()) {
|
1024
|
-
String name;
|
1025
|
-
String schema = rs.getString(TABLE_SCHEM) != null ? rs.getString(TABLE_SCHEM).toLowerCase() : null;
|
1026
|
-
|
1027
|
-
if (downCase) {
|
1028
|
-
name = rs.getString(TABLE_NAME).toLowerCase();
|
1029
|
-
} else {
|
1030
|
-
name = caseConvertIdentifierForRails(metadata, rs.getString(TABLE_NAME));
|
1031
|
-
}
|
1032
|
-
// Handle stupid Oracle 10g RecycleBin feature
|
1033
|
-
if (isOracle && name.startsWith("bin$")) {
|
1034
|
-
continue;
|
1035
|
-
}
|
1036
|
-
// Under mssql, don't return system tables/views unless they're explicitly asked for.
|
1037
|
-
if (isMssql && realschema==null &&
|
1038
|
-
("sys".equals(schema) || "information_schema".equals(schema))) {
|
1039
|
-
continue;
|
1040
|
-
}
|
1041
|
-
arr.add(RubyString.newUnicodeString(runtime, name));
|
1042
|
-
}
|
1043
|
-
return runtime.newArray(arr);
|
1044
|
-
} finally {
|
1045
|
-
close(rs);
|
1046
|
-
}
|
1573
|
+
public IRubyObject call(final Connection connection) throws SQLException {
|
1574
|
+
return matchTables(runtime, connection, catalog, schemaPattern, tablePattern, types, false);
|
1047
1575
|
}
|
1048
1576
|
};
|
1049
1577
|
}
|
1050
|
-
|
1051
|
-
protected IRubyObject timestampToRuby(Ruby runtime, ResultSet resultSet, Timestamp time)
|
1052
|
-
throws SQLException {
|
1053
|
-
if (time == null && resultSet.wasNull()) return runtime.getNil();
|
1054
|
-
|
1055
|
-
String str = time.toString();
|
1056
|
-
if (str.endsWith(" 00:00:00.0")) {
|
1057
|
-
str = str.substring(0, str.length() - (" 00:00:00.0".length()));
|
1058
|
-
}
|
1059
|
-
if (str.endsWith(".0")) {
|
1060
|
-
str = str.substring(0, str.length() - (".0".length()));
|
1061
|
-
}
|
1062
|
-
|
1063
|
-
return RubyString.newUnicodeString(runtime, str);
|
1064
|
-
}
|
1065
|
-
|
1578
|
+
|
1066
1579
|
protected static final int COLUMN_NAME = 4;
|
1067
1580
|
protected static final int DATA_TYPE = 5;
|
1068
1581
|
protected static final int TYPE_NAME = 6;
|
@@ -1071,15 +1584,10 @@ public class RubyJdbcConnection extends RubyObject {
|
|
1071
1584
|
protected static final int COLUMN_DEF = 13;
|
1072
1585
|
protected static final int IS_NULLABLE = 18;
|
1073
1586
|
|
1074
|
-
protected int intFromResultSet(ResultSet resultSet, int column) throws SQLException {
|
1075
|
-
int precision = resultSet.getInt(column);
|
1076
|
-
|
1077
|
-
return precision == 0 && resultSet.wasNull() ? -1 : precision;
|
1078
|
-
}
|
1079
|
-
|
1080
1587
|
/**
|
1081
|
-
* Create a string which represents a
|
1082
|
-
*
|
1588
|
+
* Create a string which represents a SQL type usable by Rails from the
|
1589
|
+
* resultSet column meta-data
|
1590
|
+
* @param resultSet.
|
1083
1591
|
*/
|
1084
1592
|
protected String typeFromResultSet(final ResultSet resultSet) throws SQLException {
|
1085
1593
|
final int precision = intFromResultSet(resultSet, COLUMN_SIZE);
|
@@ -1088,8 +1596,16 @@ public class RubyJdbcConnection extends RubyObject {
|
|
1088
1596
|
final String type = resultSet.getString(TYPE_NAME);
|
1089
1597
|
return formatTypeWithPrecisionAndScale(type, precision, scale);
|
1090
1598
|
}
|
1599
|
+
|
1600
|
+
protected static int intFromResultSet(
|
1601
|
+
final ResultSet resultSet, final int column) throws SQLException {
|
1602
|
+
final int precision = resultSet.getInt(column);
|
1603
|
+
return precision == 0 && resultSet.wasNull() ? -1 : precision;
|
1604
|
+
}
|
1091
1605
|
|
1092
|
-
protected static String formatTypeWithPrecisionAndScale(
|
1606
|
+
protected static String formatTypeWithPrecisionAndScale(
|
1607
|
+
final String type, final int precision, final int scale) {
|
1608
|
+
|
1093
1609
|
if ( precision <= 0 ) return type;
|
1094
1610
|
|
1095
1611
|
final StringBuilder typeStr = new StringBuilder().append(type);
|
@@ -1098,156 +1614,272 @@ public class RubyJdbcConnection extends RubyObject {
|
|
1098
1614
|
return typeStr.append(')').toString(); // type += ")";
|
1099
1615
|
}
|
1100
1616
|
|
1101
|
-
private IRubyObject defaultValueFromResultSet(Ruby runtime, ResultSet resultSet)
|
1102
|
-
|
1103
|
-
String defaultValue = resultSet.getString(COLUMN_DEF);
|
1104
|
-
|
1617
|
+
private static IRubyObject defaultValueFromResultSet(final Ruby runtime, final ResultSet resultSet)
|
1618
|
+
throws SQLException {
|
1619
|
+
final String defaultValue = resultSet.getString(COLUMN_DEF);
|
1105
1620
|
return defaultValue == null ? runtime.getNil() : RubyString.newUnicodeString(runtime, defaultValue);
|
1106
1621
|
}
|
1107
1622
|
|
1108
|
-
private IRubyObject
|
1109
|
-
|
1110
|
-
|
1111
|
-
|
1112
|
-
|
1113
|
-
|
1114
|
-
|
1115
|
-
|
1116
|
-
RubyHash types = (RubyHash) native_database_types();
|
1117
|
-
IRubyObject jdbcCol = getJdbcColumnClass(context);
|
1623
|
+
private IRubyObject unmarshalColumns(final ThreadContext context,
|
1624
|
+
final DatabaseMetaData metaData, final ResultSet results, final ResultSet primaryKeys)
|
1625
|
+
throws SQLException {
|
1626
|
+
|
1627
|
+
final Ruby runtime = context.getRuntime();
|
1628
|
+
// RubyHash types = (RubyHash) native_database_types();
|
1629
|
+
final IRubyObject jdbcColumn = getJdbcColumnClass(context);
|
1118
1630
|
|
1119
|
-
|
1120
|
-
|
1121
|
-
|
1631
|
+
final List<String> primarykeyNames = new ArrayList<String>();
|
1632
|
+
while ( primaryKeys.next() ) {
|
1633
|
+
primarykeyNames.add( primaryKeys.getString(COLUMN_NAME) );
|
1634
|
+
}
|
1122
1635
|
|
1123
|
-
|
1124
|
-
|
1125
|
-
|
1126
|
-
|
1127
|
-
|
1128
|
-
|
1129
|
-
|
1130
|
-
|
1131
|
-
|
1132
|
-
|
1133
|
-
|
1134
|
-
|
1135
|
-
|
1136
|
-
|
1137
|
-
|
1138
|
-
}
|
1636
|
+
final List<IRubyObject> columns = new ArrayList<IRubyObject>();
|
1637
|
+
while ( results.next() ) {
|
1638
|
+
final String colName = results.getString(COLUMN_NAME);
|
1639
|
+
IRubyObject column = jdbcColumn.callMethod(context, "new",
|
1640
|
+
new IRubyObject[] {
|
1641
|
+
getInstanceVariable("@config"),
|
1642
|
+
RubyString.newUnicodeString( runtime, caseConvertIdentifierForRails(metaData, colName) ),
|
1643
|
+
defaultValueFromResultSet( runtime, results ),
|
1644
|
+
RubyString.newUnicodeString( runtime, typeFromResultSet(results) ),
|
1645
|
+
runtime.newBoolean( ! results.getString(IS_NULLABLE).trim().equals("NO") )
|
1646
|
+
});
|
1647
|
+
columns.add(column);
|
1648
|
+
|
1649
|
+
if ( primarykeyNames.contains(colName) ) {
|
1650
|
+
column.callMethod(context, "primary=", runtime.getTrue());
|
1139
1651
|
}
|
1140
|
-
return runtime.newArray(columns);
|
1141
|
-
} finally {
|
1142
|
-
close(rs);
|
1143
1652
|
}
|
1653
|
+
return runtime.newArray(columns);
|
1144
1654
|
}
|
1145
1655
|
|
1146
|
-
|
1147
|
-
|
1656
|
+
protected static IRubyObject unmarshalIdResult(
|
1657
|
+
final Ruby runtime, final Statement statement) throws SQLException {
|
1658
|
+
final ResultSet genKeys = statement.getGeneratedKeys();
|
1148
1659
|
try {
|
1149
|
-
if (
|
1150
|
-
return runtime.newFixnum(
|
1660
|
+
if (genKeys.next() && genKeys.getMetaData().getColumnCount() > 0) {
|
1661
|
+
return runtime.newFixnum( genKeys.getLong(1) );
|
1151
1662
|
}
|
1152
1663
|
return runtime.getNil();
|
1153
|
-
} finally {
|
1154
|
-
close(rs);
|
1155
1664
|
}
|
1665
|
+
finally { close(genKeys); }
|
1156
1666
|
}
|
1667
|
+
|
1668
|
+
/**
|
1669
|
+
* @deprecated no longer used - kept for binary compatibility, this method
|
1670
|
+
* is confusing since it closes the result set it receives and thus was
|
1671
|
+
* replaced with {@link #unmarshalIdResult(Ruby, Statement)}
|
1672
|
+
*/
|
1673
|
+
@Deprecated
|
1674
|
+
public static IRubyObject unmarshal_id_result(
|
1675
|
+
final Ruby runtime, final ResultSet genKeys) throws SQLException {
|
1676
|
+
try {
|
1677
|
+
if (genKeys.next() && genKeys.getMetaData().getColumnCount() > 0) {
|
1678
|
+
return runtime.newFixnum( genKeys.getLong(1) );
|
1679
|
+
}
|
1680
|
+
return runtime.getNil();
|
1681
|
+
}
|
1682
|
+
finally { close(genKeys); }
|
1683
|
+
}
|
1157
1684
|
|
1158
|
-
protected IRubyObject unmarshalResults(ThreadContext context,
|
1159
|
-
|
1685
|
+
protected IRubyObject unmarshalResults(final ThreadContext context,
|
1686
|
+
final DatabaseMetaData metaData, final Statement statement,
|
1687
|
+
final boolean downCase) throws SQLException {
|
1160
1688
|
|
1161
|
-
|
1689
|
+
final Ruby runtime = context.getRuntime();
|
1690
|
+
IRubyObject result;
|
1691
|
+
ResultSet resultSet = statement.getResultSet();
|
1692
|
+
try {
|
1693
|
+
result = mapToRawResult(context, runtime, metaData, resultSet, downCase);
|
1694
|
+
}
|
1695
|
+
finally { close(resultSet); }
|
1162
1696
|
|
1163
|
-
if ( !
|
1697
|
+
if ( ! statement.getMoreResults() ) return result;
|
1164
1698
|
|
1165
1699
|
final List<IRubyObject> results = new ArrayList<IRubyObject>();
|
1166
1700
|
results.add(result);
|
1701
|
+
|
1167
1702
|
do {
|
1168
|
-
|
1703
|
+
resultSet = statement.getResultSet();
|
1704
|
+
try {
|
1705
|
+
result = mapToRawResult(context, runtime, metaData, resultSet, downCase);
|
1706
|
+
}
|
1707
|
+
finally { close(resultSet); }
|
1708
|
+
|
1169
1709
|
results.add(result);
|
1170
1710
|
}
|
1171
|
-
while (
|
1711
|
+
while ( statement.getMoreResults() );
|
1172
1712
|
|
1173
|
-
return
|
1713
|
+
return runtime.newArray(results);
|
1174
1714
|
}
|
1175
1715
|
|
1176
1716
|
/**
|
1177
|
-
*
|
1717
|
+
* @deprecated no longer used but kept for binary compatibility
|
1718
|
+
*/
|
1719
|
+
@Deprecated
|
1720
|
+
protected IRubyObject unmarshalResult(final ThreadContext context,
|
1721
|
+
final DatabaseMetaData metaData, final ResultSet resultSet,
|
1722
|
+
final boolean downCase) throws SQLException {
|
1723
|
+
return mapToRawResult(context, context.getRuntime(), metaData, resultSet, downCase);
|
1724
|
+
}
|
1725
|
+
|
1726
|
+
/**
|
1727
|
+
* Converts a JDBC result set into an array (rows) of hashes (row).
|
1178
1728
|
*
|
1179
1729
|
* @param downCase should column names only be in lower case?
|
1180
1730
|
*/
|
1181
|
-
|
1182
|
-
|
1183
|
-
|
1184
|
-
|
1731
|
+
@SuppressWarnings("unchecked")
|
1732
|
+
private IRubyObject mapToRawResult(final ThreadContext context, final Ruby runtime,
|
1733
|
+
final DatabaseMetaData metaData, final ResultSet resultSet,
|
1734
|
+
final boolean downCase) throws SQLException {
|
1735
|
+
|
1736
|
+
ColumnData[] columns = extractColumns(runtime, metaData, resultSet, downCase);
|
1185
1737
|
|
1738
|
+
final RubyArray results = runtime.newArray();
|
1739
|
+
// [ { 'col1': 1, 'col2': 2 }, { 'col1': 3, 'col2': 4 } ]
|
1740
|
+
populateFromResultSet(context, runtime, (List<IRubyObject>) results, resultSet, columns);
|
1741
|
+
return results;
|
1742
|
+
}
|
1743
|
+
|
1744
|
+
/**
|
1745
|
+
* Extract columns from result set.
|
1746
|
+
* @param runtime
|
1747
|
+
* @param metaData
|
1748
|
+
* @param resultSet
|
1749
|
+
* @param downCase
|
1750
|
+
* @return columns data
|
1751
|
+
* @throws SQLException
|
1752
|
+
*/
|
1753
|
+
protected ColumnData[] extractColumns(final Ruby runtime,
|
1754
|
+
final DatabaseMetaData metaData, final ResultSet resultSet,
|
1755
|
+
final boolean downCase) throws SQLException {
|
1756
|
+
return setupColumns(runtime, metaData, resultSet.getMetaData(), downCase);
|
1757
|
+
}
|
1758
|
+
|
1759
|
+
/**
|
1760
|
+
* @deprecated renamed and parameterized to {@link #withConnection(ThreadContext, SQLBlock)}
|
1761
|
+
*/
|
1762
|
+
@Deprecated
|
1763
|
+
@SuppressWarnings("unchecked")
|
1764
|
+
protected Object withConnectionAndRetry(final ThreadContext context, final SQLBlock block)
|
1765
|
+
throws RaiseException {
|
1766
|
+
return withConnection(context, block);
|
1767
|
+
}
|
1768
|
+
|
1769
|
+
protected <T> T withConnection(final ThreadContext context, final Callable<T> block)
|
1770
|
+
throws RaiseException {
|
1186
1771
|
try {
|
1187
|
-
|
1188
|
-
|
1189
|
-
|
1190
|
-
|
1191
|
-
close(resultSet);
|
1772
|
+
return withConnection(context, true, block);
|
1773
|
+
}
|
1774
|
+
catch (final SQLException e) {
|
1775
|
+
return handleException(context, e); // should never happen
|
1192
1776
|
}
|
1193
|
-
|
1194
|
-
return runtime.newArray(results);
|
1195
1777
|
}
|
1196
|
-
|
1197
|
-
|
1198
|
-
|
1199
|
-
|
1200
|
-
Throwable
|
1201
|
-
|
1202
|
-
while (i < tries) {
|
1203
|
-
Connection
|
1778
|
+
|
1779
|
+
private <T> T withConnection(final ThreadContext context, final boolean handleException, final Callable<T> block)
|
1780
|
+
throws RaiseException, RuntimeException, SQLException {
|
1781
|
+
|
1782
|
+
Throwable exception = null; int tries = 1; int i = 0;
|
1783
|
+
|
1784
|
+
while ( i++ < tries ) {
|
1785
|
+
final Connection connection = getConnection(true);
|
1786
|
+
boolean autoCommit = true; // retry in-case getAutoCommit throws
|
1204
1787
|
try {
|
1205
|
-
autoCommit =
|
1206
|
-
return block.call(
|
1207
|
-
}
|
1208
|
-
|
1209
|
-
|
1210
|
-
|
1211
|
-
|
1212
|
-
|
1213
|
-
|
1214
|
-
|
1215
|
-
|
1216
|
-
|
1217
|
-
i++;
|
1218
|
-
if (autoCommit) {
|
1219
|
-
if (i == 1) {
|
1220
|
-
tries = (int) rubyApi.convertToRubyInteger(config_value(context, "retry_count")).getLongValue();
|
1221
|
-
if (tries <= 0) {
|
1222
|
-
tries = 1;
|
1223
|
-
}
|
1788
|
+
autoCommit = connection.getAutoCommit();
|
1789
|
+
return block.call(connection);
|
1790
|
+
}
|
1791
|
+
catch (final Exception e) { // SQLException or RuntimeException
|
1792
|
+
exception = e;
|
1793
|
+
|
1794
|
+
if ( autoCommit ) { // do not retry if (inside) transactions
|
1795
|
+
if ( i == 1 ) {
|
1796
|
+
IRubyObject retryCount = getConfigValue(context, "retry_count");
|
1797
|
+
tries = (int) retryCount.convertToInteger().getLongValue();
|
1798
|
+
if ( tries <= 0 ) tries = 1;
|
1224
1799
|
}
|
1225
|
-
if (isConnectionBroken(context,
|
1226
|
-
reconnect();
|
1227
|
-
} else {
|
1228
|
-
throw wrap(context, toWrap);
|
1800
|
+
if ( isConnectionBroken(context, connection) ) {
|
1801
|
+
reconnect(context); continue; // retry connection (block) again
|
1229
1802
|
}
|
1803
|
+
break; // connection not broken yet failed
|
1230
1804
|
}
|
1231
1805
|
}
|
1232
1806
|
}
|
1233
|
-
|
1807
|
+
// (retry) loop ended and we did not return ... exception != null
|
1808
|
+
if ( handleException ) {
|
1809
|
+
return handleException(context, getCause(exception)); // throws
|
1810
|
+
}
|
1811
|
+
else {
|
1812
|
+
if ( exception instanceof SQLException ) {
|
1813
|
+
throw (SQLException) exception;
|
1814
|
+
}
|
1815
|
+
if ( exception instanceof RuntimeException ) {
|
1816
|
+
throw (RuntimeException) exception;
|
1817
|
+
}
|
1818
|
+
// won't happen - our try block only throws SQL or Runtime exceptions
|
1819
|
+
throw new RuntimeException(exception);
|
1820
|
+
}
|
1821
|
+
}
|
1822
|
+
|
1823
|
+
private static Throwable getCause(Throwable exception) {
|
1824
|
+
Throwable cause = exception.getCause();
|
1825
|
+
while (cause != null && cause != exception) {
|
1826
|
+
// SQLException's cause might be DB specific (checked/unchecked) :
|
1827
|
+
if ( exception instanceof SQLException ) break;
|
1828
|
+
exception = cause; cause = exception.getCause();
|
1829
|
+
}
|
1830
|
+
return exception;
|
1234
1831
|
}
|
1235
1832
|
|
1236
|
-
protected
|
1237
|
-
|
1238
|
-
|
1239
|
-
|
1240
|
-
|
1241
|
-
if (exception instanceof
|
1242
|
-
|
1243
|
-
"errno=", runtime.newFixnum(((SQLException) exception).getErrorCode()));
|
1244
|
-
RuntimeHelpers.invoke(context, arError.getException(),
|
1245
|
-
"sql_exception=", JavaEmbedUtils.javaToRuby(runtime, exception));
|
1833
|
+
protected <T> T handleException(final ThreadContext context, Throwable exception)
|
1834
|
+
throws RaiseException {
|
1835
|
+
// NOTE: we shall not wrap unchecked (runtime) exceptions into AR::Error
|
1836
|
+
// if it's really a misbehavior of the driver throwing a RuntimeExcepion
|
1837
|
+
// instead of SQLException than this should be overriden for the adapter
|
1838
|
+
if ( exception instanceof RuntimeException ) {
|
1839
|
+
throw (RuntimeException) exception;
|
1246
1840
|
}
|
1247
|
-
|
1841
|
+
debugStackTrace(context, exception);
|
1842
|
+
throw wrapException(context, exception);
|
1843
|
+
}
|
1844
|
+
|
1845
|
+
/**
|
1846
|
+
* @deprecated use {@link #wrapException(ThreadContext, Throwable)} instead
|
1847
|
+
* for overriding how exceptions are handled use {@link #handleException(ThreadContext, Throwable)}
|
1848
|
+
*/
|
1849
|
+
@Deprecated
|
1850
|
+
protected RuntimeException wrap(final ThreadContext context, final Throwable exception) {
|
1851
|
+
return wrapException(context, exception);
|
1852
|
+
}
|
1853
|
+
|
1854
|
+
protected RaiseException wrapException(final ThreadContext context, final Throwable exception) {
|
1855
|
+
final Ruby runtime = context.getRuntime();
|
1856
|
+
if ( exception instanceof SQLException ) {
|
1857
|
+
final String message = SQLException.class == exception.getClass() ?
|
1858
|
+
exception.getMessage() : exception.toString(); // useful to easily see type on Ruby side
|
1859
|
+
final RaiseException error = wrapException(context, getJDBCError(runtime), exception, message);
|
1860
|
+
final int errorCode = ((SQLException) exception).getErrorCode();
|
1861
|
+
RuntimeHelpers.invoke( context, error.getException(),
|
1862
|
+
"errno=", runtime.newFixnum(errorCode) );
|
1863
|
+
RuntimeHelpers.invoke( context, error.getException(),
|
1864
|
+
"sql_exception=", JavaEmbedUtils.javaToRuby(runtime, exception) );
|
1865
|
+
return error;
|
1866
|
+
}
|
1867
|
+
return wrapException(context, getJDBCError(runtime), exception);
|
1868
|
+
}
|
1869
|
+
|
1870
|
+
protected static RaiseException wrapException(final ThreadContext context,
|
1871
|
+
final RubyClass errorClass, final Throwable exception) {
|
1872
|
+
return wrapException(context, errorClass, exception, exception.toString());
|
1248
1873
|
}
|
1249
1874
|
|
1250
|
-
|
1875
|
+
protected static RaiseException wrapException(final ThreadContext context,
|
1876
|
+
final RubyClass errorClass, final Throwable exception, final String message) {
|
1877
|
+
final RaiseException error = new RaiseException(context.getRuntime(), errorClass, message, true);
|
1878
|
+
error.initCause(exception);
|
1879
|
+
return error;
|
1880
|
+
}
|
1881
|
+
|
1882
|
+
private IRubyObject convertJavaToRuby(final Connection connection) {
|
1251
1883
|
return JavaUtil.convertJavaToRuby( getRuntime(), connection );
|
1252
1884
|
}
|
1253
1885
|
|
@@ -1259,110 +1891,319 @@ public class RubyJdbcConnection extends RubyObject {
|
|
1259
1891
|
return false;
|
1260
1892
|
}
|
1261
1893
|
|
1262
|
-
private static
|
1263
|
-
|
1894
|
+
private static final byte[] SELECT = new byte[] { 's','e','l','e','c','t' };
|
1895
|
+
private static final byte[] WITH = new byte[] { 'w','i','t','h' };
|
1896
|
+
private static final byte[] SHOW = new byte[] { 's','h','o','w' };
|
1897
|
+
private static final byte[] CALL = new byte[]{ 'c','a','l','l' };
|
1898
|
+
|
1899
|
+
@JRubyMethod(name = "select?", required = 1, meta = true, frame = false)
|
1900
|
+
public static IRubyObject select_p(final ThreadContext context,
|
1901
|
+
final IRubyObject self, final IRubyObject sql) {
|
1902
|
+
return context.getRuntime().newBoolean( isSelect(sql.convertToString()) );
|
1903
|
+
}
|
1904
|
+
|
1905
|
+
private static boolean isSelect(final RubyString sql) {
|
1906
|
+
final ByteList sqlBytes = sql.getByteList();
|
1907
|
+
return startsWithIgnoreCase(sqlBytes, SELECT) ||
|
1908
|
+
startsWithIgnoreCase(sqlBytes, WITH) ||
|
1909
|
+
startsWithIgnoreCase(sqlBytes, SHOW) ||
|
1910
|
+
startsWithIgnoreCase(sqlBytes, CALL);
|
1911
|
+
}
|
1912
|
+
|
1913
|
+
private static final byte[] INSERT = new byte[] { 'i','n','s','e','r','t' };
|
1914
|
+
|
1915
|
+
@JRubyMethod(name = "insert?", required = 1, meta = true, frame = false)
|
1916
|
+
public static IRubyObject insert_p(final ThreadContext context,
|
1917
|
+
final IRubyObject self, final IRubyObject sql) {
|
1918
|
+
final ByteList sqlBytes = sql.convertToString().getByteList();
|
1919
|
+
return context.getRuntime().newBoolean(startsWithIgnoreCase(sqlBytes, INSERT));
|
1920
|
+
}
|
1921
|
+
|
1922
|
+
protected static boolean startsWithIgnoreCase(final ByteList string, final byte[] start) {
|
1923
|
+
int p = skipWhitespace(string, string.getBegin());
|
1924
|
+
final byte[] stringBytes = string.unsafeBytes();
|
1925
|
+
if ( stringBytes[p] == '(' ) p = skipWhitespace(string, p + 1);
|
1264
1926
|
|
1265
|
-
for (int i =
|
1266
|
-
if (
|
1927
|
+
for ( int i = 0; i < string.getRealSize() && i < start.length; i++ ) {
|
1928
|
+
if ( Character.toLowerCase(stringBytes[p + i]) != start[i] ) return false;
|
1267
1929
|
}
|
1930
|
+
return true;
|
1931
|
+
}
|
1268
1932
|
|
1933
|
+
private static int skipWhitespace(final ByteList string, final int from) {
|
1934
|
+
final int end = string.getBegin() + string.getRealSize();
|
1935
|
+
final byte[] stringBytes = string.unsafeBytes();
|
1936
|
+
for ( int i = from; i < end; i++ ) {
|
1937
|
+
if ( ! Character.isWhitespace( stringBytes[i] ) ) return i;
|
1938
|
+
}
|
1269
1939
|
return end;
|
1270
1940
|
}
|
1941
|
+
|
1942
|
+
/**
|
1943
|
+
* JDBC connection helper that handles mapping results to
|
1944
|
+
* <code>ActiveRecord::Result</code> (available since AR-3.1).
|
1945
|
+
*
|
1946
|
+
* @see #populateFromResultSet(ThreadContext, Ruby, List, ResultSet, RubyJdbcConnection.ColumnData[])
|
1947
|
+
* @author kares
|
1948
|
+
*/
|
1949
|
+
protected static class ResultHandler {
|
1271
1950
|
|
1272
|
-
|
1273
|
-
private static byte[] INSERT = new byte[] {'i', 'n', 's', 'e', 'r', 't'};
|
1274
|
-
private static byte[] SELECT = new byte[] {'s', 'e', 'l', 'e', 'c', 't'};
|
1275
|
-
private static byte[] WITH = new byte[] {'w', 'i', 't', 'h'};
|
1276
|
-
private static byte[] SHOW = new byte[] {'s', 'h', 'o', 'w'};
|
1951
|
+
protected static Boolean USE_RESULT;
|
1277
1952
|
|
1278
|
-
|
1279
|
-
|
1953
|
+
// AR-3.2 : initialize(columns, rows)
|
1954
|
+
// AR-4.0 : initialize(columns, rows, column_types = {})
|
1955
|
+
protected static Boolean INIT_COLUMN_TYPES = Boolean.FALSE;
|
1280
1956
|
|
1281
|
-
|
1282
|
-
if (bytelist.bytes[p] == '(') p = whitespace(p, bytelist);
|
1957
|
+
protected static Boolean FORCE_HASH_ROWS = Boolean.FALSE;
|
1283
1958
|
|
1284
|
-
|
1285
|
-
|
1959
|
+
private static volatile ResultHandler instance;
|
1960
|
+
|
1961
|
+
public static ResultHandler getInstance(final ThreadContext context) {
|
1962
|
+
if ( instance == null ) {
|
1963
|
+
synchronized(ResultHandler.class) {
|
1964
|
+
if ( instance == null ) { // fine to initialize twice
|
1965
|
+
setInstance( new ResultHandler(context) );
|
1966
|
+
}
|
1967
|
+
}
|
1968
|
+
}
|
1969
|
+
return instance;
|
1286
1970
|
}
|
1287
1971
|
|
1288
|
-
|
1289
|
-
|
1972
|
+
protected static synchronized void setInstance(final ResultHandler instance) {
|
1973
|
+
ResultHandler.instance = instance;
|
1974
|
+
}
|
1290
1975
|
|
1291
|
-
|
1292
|
-
|
1976
|
+
protected ResultHandler(final ThreadContext context) {
|
1977
|
+
final Ruby runtime = context.getRuntime();
|
1978
|
+
final RubyClass result = getResult(runtime);
|
1979
|
+
USE_RESULT = result != null && result != runtime.getNilClass();
|
1980
|
+
}
|
1293
1981
|
|
1294
|
-
final
|
1295
|
-
|
1296
|
-
|
1982
|
+
public IRubyObject mapRow(final ThreadContext context, final Ruby runtime,
|
1983
|
+
final ColumnData[] columns, final ResultSet resultSet,
|
1984
|
+
final RubyJdbcConnection connection) throws SQLException {
|
1985
|
+
|
1986
|
+
if ( USE_RESULT ) { // maps a AR::Result row
|
1987
|
+
final RubyArray row = runtime.newArray(columns.length);
|
1988
|
+
|
1989
|
+
for ( int i = 0; i < columns.length; i++ ) {
|
1990
|
+
final ColumnData column = columns[i];
|
1991
|
+
row.append( connection.jdbcToRuby(runtime, column.index, column.type, resultSet) );
|
1992
|
+
}
|
1993
|
+
|
1994
|
+
return row;
|
1995
|
+
}
|
1996
|
+
else {
|
1997
|
+
return mapRawRow(context, runtime, columns, resultSet, connection);
|
1998
|
+
}
|
1297
1999
|
}
|
1298
2000
|
|
1299
|
-
|
1300
|
-
|
1301
|
-
|
2001
|
+
IRubyObject mapRawRow(final ThreadContext context, final Ruby runtime,
|
2002
|
+
final ColumnData[] columns, final ResultSet resultSet,
|
2003
|
+
final RubyJdbcConnection connection) throws SQLException {
|
2004
|
+
|
2005
|
+
final RubyHash row = RubyHash.newHash(runtime);
|
2006
|
+
|
2007
|
+
for ( int i = 0; i < columns.length; i++ ) {
|
2008
|
+
final ColumnData column = columns[i];
|
2009
|
+
row.op_aset( context, column.name, connection.jdbcToRuby(runtime, column.index, column.type, resultSet) );
|
2010
|
+
}
|
1302
2011
|
|
1303
|
-
|
1304
|
-
if (name_parts.length == 2) {
|
1305
|
-
schemaName = name_parts[0];
|
1306
|
-
tableName = name_parts[1];
|
1307
|
-
} else if (name_parts.length == 3) {
|
1308
|
-
catalog = name_parts[0];
|
1309
|
-
schemaName = name_parts[1];
|
1310
|
-
tableName = name_parts[2];
|
2012
|
+
return row;
|
1311
2013
|
}
|
2014
|
+
|
2015
|
+
public IRubyObject newResult(final ThreadContext context, final Ruby runtime,
|
2016
|
+
final ColumnData[] columns, final IRubyObject rows) { // rows array
|
2017
|
+
if ( USE_RESULT ) { // ActiveRecord::Result.new(columns, rows)
|
2018
|
+
final RubyClass result = getResult(runtime);
|
2019
|
+
return result.callMethod( context, "new", initArgs(runtime, columns, rows), Block.NULL_BLOCK );
|
2020
|
+
}
|
2021
|
+
return rows; // contains { 'col1' => 1, ... } Hash-es
|
2022
|
+
}
|
2023
|
+
|
2024
|
+
private IRubyObject[] initArgs(final Ruby runtime,
|
2025
|
+
final ColumnData[] columns, final IRubyObject rows) {
|
2026
|
+
|
2027
|
+
final IRubyObject[] args;
|
2028
|
+
|
2029
|
+
final RubyArray cols = runtime.newArray(columns.length);
|
2030
|
+
|
2031
|
+
if ( INIT_COLUMN_TYPES ) { // NOTE: NOT IMPLEMENTED
|
2032
|
+
for ( int i=0; i<columns.length; i++ ) {
|
2033
|
+
cols.add( columns[i].name );
|
2034
|
+
}
|
2035
|
+
args = new IRubyObject[] { cols, rows };
|
2036
|
+
}
|
2037
|
+
else {
|
2038
|
+
for ( int i=0; i<columns.length; i++ ) {
|
2039
|
+
cols.add( columns[i].name );
|
2040
|
+
}
|
2041
|
+
args = new IRubyObject[] { cols, rows };
|
2042
|
+
}
|
2043
|
+
return args;
|
2044
|
+
}
|
2045
|
+
|
2046
|
+
}
|
1312
2047
|
|
1313
|
-
|
2048
|
+
|
2049
|
+
protected static final class TableName {
|
2050
|
+
|
2051
|
+
public final String catalog, schema, name;
|
1314
2052
|
|
1315
|
-
|
1316
|
-
|
1317
|
-
|
1318
|
-
|
1319
|
-
|
2053
|
+
public TableName(String catalog, String schema, String table) {
|
2054
|
+
this.catalog = catalog;
|
2055
|
+
this.schema = schema;
|
2056
|
+
this.name = table;
|
2057
|
+
}
|
2058
|
+
|
2059
|
+
}
|
2060
|
+
|
2061
|
+
/**
|
2062
|
+
* Extract the table name components for the given name e.g. "mycat.sys.entries"
|
2063
|
+
*
|
2064
|
+
* @param connection
|
2065
|
+
* @param catalog (optional) catalog to use if table name does not contain
|
2066
|
+
* the catalog prefix
|
2067
|
+
* @param schema (optional) schema to use if table name does not have one
|
2068
|
+
* @param tableName the table name
|
2069
|
+
* @return (parsed) table name
|
2070
|
+
*
|
2071
|
+
* @throws IllegalArgumentException for invalid table name format
|
2072
|
+
* @throws SQLException
|
2073
|
+
*/
|
2074
|
+
protected TableName extractTableName(
|
2075
|
+
final Connection connection, String catalog, String schema,
|
2076
|
+
final String tableName) throws IllegalArgumentException, SQLException {
|
2077
|
+
|
2078
|
+
final String[] nameParts = tableName.split("\\.");
|
2079
|
+
if ( nameParts.length > 3 ) {
|
2080
|
+
throw new IllegalArgumentException("table name: " + tableName + " should not contain more than 2 '.'");
|
2081
|
+
}
|
2082
|
+
|
2083
|
+
String name = tableName;
|
2084
|
+
|
2085
|
+
if ( nameParts.length == 2 ) {
|
2086
|
+
schema = nameParts[0];
|
2087
|
+
name = nameParts[1];
|
2088
|
+
}
|
2089
|
+
else if ( nameParts.length == 3 ) {
|
2090
|
+
catalog = nameParts[0];
|
2091
|
+
schema = nameParts[1];
|
2092
|
+
name = nameParts[2];
|
2093
|
+
}
|
2094
|
+
|
2095
|
+
final DatabaseMetaData metaData = connection.getMetaData();
|
2096
|
+
|
2097
|
+
if (schema != null) {
|
2098
|
+
schema = caseConvertIdentifierForJdbc(metaData, schema);
|
2099
|
+
}
|
2100
|
+
name = caseConvertIdentifierForJdbc(metaData, name);
|
1320
2101
|
|
1321
|
-
if (
|
2102
|
+
if (schema != null && ! databaseSupportsSchemas()) {
|
2103
|
+
catalog = schema;
|
2104
|
+
}
|
2105
|
+
if (catalog == null) catalog = connection.getCatalog();
|
1322
2106
|
|
1323
|
-
return new
|
2107
|
+
return new TableName(catalog, schema, name);
|
2108
|
+
}
|
2109
|
+
|
2110
|
+
/**
|
2111
|
+
* @deprecated use {@link #extractTableName(Connection, String, String, String)}
|
2112
|
+
*/
|
2113
|
+
@Deprecated
|
2114
|
+
protected TableName extractTableName(
|
2115
|
+
final Connection connection, final String schema,
|
2116
|
+
final String tableName) throws IllegalArgumentException, SQLException {
|
2117
|
+
return extractTableName(connection, null, schema, tableName);
|
1324
2118
|
}
|
1325
2119
|
|
1326
|
-
|
1327
|
-
|
1328
|
-
public
|
1329
|
-
public int
|
2120
|
+
protected static final class ColumnData {
|
2121
|
+
|
2122
|
+
public final RubyString name;
|
2123
|
+
public final int index;
|
2124
|
+
public final int type;
|
1330
2125
|
|
1331
|
-
public ColumnData(
|
2126
|
+
public ColumnData(RubyString name, int type, int idx) {
|
1332
2127
|
this.name = name;
|
1333
2128
|
this.type = type;
|
1334
2129
|
this.index = idx;
|
1335
2130
|
}
|
1336
|
-
|
1337
|
-
|
1338
|
-
|
1339
|
-
|
1340
|
-
|
1341
|
-
|
1342
|
-
|
1343
|
-
|
1344
|
-
|
1345
|
-
|
1346
|
-
|
1347
|
-
|
1348
|
-
|
1349
|
-
|
1350
|
-
|
2131
|
+
|
2132
|
+
}
|
2133
|
+
|
2134
|
+
private static ColumnData[] setupColumns(
|
2135
|
+
final Ruby runtime,
|
2136
|
+
final DatabaseMetaData metaData,
|
2137
|
+
final ResultSetMetaData resultMetaData,
|
2138
|
+
final boolean downCase) throws SQLException {
|
2139
|
+
|
2140
|
+
final int columnCount = resultMetaData.getColumnCount();
|
2141
|
+
final ColumnData[] columns = new ColumnData[columnCount];
|
2142
|
+
|
2143
|
+
for ( int i = 1; i <= columnCount; i++ ) { // metadata is one-based
|
2144
|
+
final String name;
|
2145
|
+
if (downCase) {
|
2146
|
+
name = resultMetaData.getColumnLabel(i).toLowerCase();
|
2147
|
+
} else {
|
2148
|
+
name = caseConvertIdentifierForRails(metaData, resultMetaData.getColumnLabel(i));
|
1351
2149
|
}
|
2150
|
+
final int columnType = resultMetaData.getColumnType(i);
|
2151
|
+
final RubyString columnName = RubyString.newUnicodeString(runtime, name);
|
2152
|
+
columns[i - 1] = new ColumnData(columnName, columnType, i);
|
2153
|
+
}
|
1352
2154
|
|
1353
|
-
|
2155
|
+
return columns;
|
2156
|
+
}
|
2157
|
+
|
2158
|
+
// JDBC API Helpers :
|
2159
|
+
|
2160
|
+
protected static void close(final Connection connection) {
|
2161
|
+
if ( connection != null ) {
|
2162
|
+
try { connection.close(); }
|
2163
|
+
catch (final Exception e) { /* NOOP */ }
|
1354
2164
|
}
|
1355
2165
|
}
|
1356
2166
|
|
1357
|
-
|
1358
|
-
|
1359
|
-
|
1360
|
-
|
2167
|
+
public static void close(final ResultSet resultSet) {
|
2168
|
+
if (resultSet != null) {
|
2169
|
+
try { resultSet.close(); }
|
2170
|
+
catch (final Exception e) { /* NOOP */ }
|
2171
|
+
}
|
2172
|
+
}
|
1361
2173
|
|
1362
|
-
|
1363
|
-
|
1364
|
-
|
1365
|
-
|
2174
|
+
public static void close(final Statement statement) {
|
2175
|
+
if (statement != null) {
|
2176
|
+
try { statement.close(); }
|
2177
|
+
catch (final Exception e) { /* NOOP */ }
|
1366
2178
|
}
|
1367
2179
|
}
|
2180
|
+
|
2181
|
+
// DEBUG-ing helpers :
|
2182
|
+
|
2183
|
+
private static boolean debug = Boolean.getBoolean("arjdbc.debug");
|
2184
|
+
|
2185
|
+
public static boolean isDebug() { return debug; }
|
2186
|
+
|
2187
|
+
public static void setDebug(boolean debug) {
|
2188
|
+
RubyJdbcConnection.debug = debug;
|
2189
|
+
}
|
2190
|
+
|
2191
|
+
public static void debugMessage(final ThreadContext context, final String msg) {
|
2192
|
+
if ( debug || context.runtime.isDebug() ) {
|
2193
|
+
context.runtime.getOut().println(msg);
|
2194
|
+
}
|
2195
|
+
}
|
2196
|
+
|
2197
|
+
protected static void debugErrorSQL(final ThreadContext context, final String sql) {
|
2198
|
+
if ( debug || context.runtime.isDebug() ) {
|
2199
|
+
context.runtime.getOut().println("Error SQL: " + sql);
|
2200
|
+
}
|
2201
|
+
}
|
2202
|
+
|
2203
|
+
public static void debugStackTrace(final ThreadContext context, final Throwable e) {
|
2204
|
+
if ( debug || context.runtime.isDebug() ) {
|
2205
|
+
e.printStackTrace(context.runtime.getOut());
|
2206
|
+
}
|
2207
|
+
}
|
2208
|
+
|
1368
2209
|
}
|