activerecord-jdbc-adapter 1.0.3-java → 50.1-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +33 -0
- data/.travis.yml +79 -0
- data/.yardopts +4 -0
- data/CONTRIBUTING.md +50 -0
- data/Gemfile +91 -0
- data/History.md +1191 -0
- data/LICENSE.txt +22 -17
- data/README.md +169 -0
- data/RUNNING_TESTS.md +127 -0
- data/Rakefile +294 -5
- data/Rakefile.jdbc +20 -0
- data/activerecord-jdbc-adapter.gemspec +55 -0
- data/lib/active_record/connection_adapters/as400_adapter.rb +2 -0
- data/lib/active_record/connection_adapters/db2_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/firebird_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/mariadb_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +1 -0
- data/lib/activerecord-jdbc-adapter.rb +0 -5
- data/lib/arel/visitors/compat.rb +60 -0
- data/lib/arel/visitors/db2.rb +128 -6
- data/lib/arel/visitors/derby.rb +103 -10
- data/lib/arel/visitors/firebird.rb +79 -0
- data/lib/arel/visitors/h2.rb +25 -0
- data/lib/arel/visitors/hsqldb.rb +18 -10
- data/lib/arel/visitors/postgresql_jdbc.rb +6 -0
- data/lib/arel/visitors/sql_server.rb +225 -0
- data/lib/arel/visitors/sql_server/ng42.rb +293 -0
- data/lib/arjdbc.rb +11 -21
- data/lib/arjdbc/abstract/connection_management.rb +35 -0
- data/lib/arjdbc/abstract/core.rb +64 -0
- data/lib/arjdbc/abstract/database_statements.rb +64 -0
- data/lib/arjdbc/abstract/statement_cache.rb +58 -0
- data/lib/arjdbc/abstract/transaction_support.rb +86 -0
- data/lib/arjdbc/db2.rb +3 -1
- data/lib/arjdbc/db2/adapter.rb +630 -250
- data/lib/arjdbc/db2/as400.rb +130 -0
- data/lib/arjdbc/db2/column.rb +167 -0
- data/lib/arjdbc/db2/connection_methods.rb +44 -0
- data/lib/arjdbc/derby.rb +1 -5
- data/lib/arjdbc/derby/active_record_patch.rb +13 -0
- data/lib/arjdbc/derby/adapter.rb +409 -217
- data/lib/arjdbc/derby/connection_methods.rb +16 -14
- data/lib/arjdbc/derby/schema_creation.rb +15 -0
- data/lib/arjdbc/discover.rb +62 -50
- data/lib/arjdbc/firebird.rb +3 -1
- data/lib/arjdbc/firebird/adapter.rb +365 -62
- data/lib/arjdbc/firebird/connection_methods.rb +23 -0
- data/lib/arjdbc/h2.rb +2 -3
- data/lib/arjdbc/h2/adapter.rb +273 -6
- data/lib/arjdbc/h2/connection_methods.rb +23 -8
- data/lib/arjdbc/hsqldb.rb +2 -3
- data/lib/arjdbc/hsqldb/adapter.rb +204 -77
- data/lib/arjdbc/hsqldb/connection_methods.rb +24 -10
- data/lib/arjdbc/hsqldb/explain_support.rb +35 -0
- data/lib/arjdbc/hsqldb/schema_creation.rb +11 -0
- data/lib/arjdbc/informix.rb +4 -2
- data/lib/arjdbc/informix/adapter.rb +78 -54
- data/lib/arjdbc/informix/connection_methods.rb +8 -9
- data/lib/arjdbc/jdbc.rb +59 -2
- data/lib/arjdbc/jdbc/adapter.rb +356 -166
- data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
- data/lib/arjdbc/jdbc/adapter_require.rb +46 -0
- data/lib/arjdbc/jdbc/base_ext.rb +15 -0
- data/lib/arjdbc/jdbc/callbacks.rb +27 -18
- data/lib/arjdbc/jdbc/column.rb +79 -20
- data/lib/arjdbc/jdbc/connection.rb +5 -119
- data/lib/arjdbc/jdbc/connection_methods.rb +32 -4
- data/lib/arjdbc/jdbc/error.rb +65 -0
- data/lib/arjdbc/jdbc/extension.rb +41 -29
- data/lib/arjdbc/jdbc/java.rb +5 -6
- data/lib/arjdbc/jdbc/jdbc.rake +3 -126
- data/lib/arjdbc/jdbc/railtie.rb +2 -9
- data/lib/arjdbc/jdbc/rake_tasks.rb +3 -10
- data/lib/arjdbc/jdbc/serialized_attributes_helper.rb +3 -0
- data/lib/arjdbc/jdbc/type_cast.rb +166 -0
- data/lib/arjdbc/jdbc/type_converter.rb +35 -19
- data/lib/arjdbc/mssql.rb +6 -3
- data/lib/arjdbc/mssql/adapter.rb +630 -298
- data/lib/arjdbc/mssql/column.rb +200 -0
- data/lib/arjdbc/mssql/connection_methods.rb +66 -17
- data/lib/arjdbc/mssql/explain_support.rb +99 -0
- data/lib/arjdbc/mssql/limit_helpers.rb +189 -50
- data/lib/arjdbc/mssql/lock_methods.rb +77 -0
- data/lib/arjdbc/mssql/types.rb +343 -0
- data/lib/arjdbc/mssql/utils.rb +82 -0
- data/lib/arjdbc/mysql.rb +2 -3
- data/lib/arjdbc/mysql/adapter.rb +86 -356
- data/lib/arjdbc/mysql/connection_methods.rb +159 -23
- data/lib/arjdbc/oracle/adapter.rb +714 -263
- data/lib/arjdbc/postgresql.rb +2 -3
- data/lib/arjdbc/postgresql/_bc_time_cast_patch.rb +24 -0
- data/lib/arjdbc/postgresql/adapter.rb +570 -400
- data/lib/arjdbc/postgresql/base/array_decoder.rb +26 -0
- data/lib/arjdbc/postgresql/base/array_encoder.rb +25 -0
- data/lib/arjdbc/postgresql/base/array_parser.rb +95 -0
- data/lib/arjdbc/postgresql/base/pgconn.rb +11 -0
- data/lib/arjdbc/postgresql/column.rb +51 -0
- data/lib/arjdbc/postgresql/connection_methods.rb +57 -18
- data/lib/arjdbc/postgresql/name.rb +24 -0
- data/lib/arjdbc/postgresql/oid_types.rb +192 -0
- data/lib/arjdbc/railtie.rb +11 -0
- data/lib/arjdbc/sqlite3.rb +2 -3
- data/lib/arjdbc/sqlite3/adapter.rb +518 -198
- data/lib/arjdbc/sqlite3/connection_methods.rb +49 -24
- data/lib/arjdbc/sybase.rb +2 -2
- data/lib/arjdbc/sybase/adapter.rb +7 -6
- data/lib/arjdbc/tasks.rb +13 -0
- data/lib/arjdbc/tasks/database_tasks.rb +52 -0
- data/lib/arjdbc/tasks/databases.rake +91 -0
- data/lib/arjdbc/tasks/databases3.rake +215 -0
- data/lib/arjdbc/tasks/databases4.rake +39 -0
- data/lib/arjdbc/tasks/db2_database_tasks.rb +104 -0
- data/lib/arjdbc/tasks/derby_database_tasks.rb +95 -0
- data/lib/arjdbc/tasks/h2_database_tasks.rb +31 -0
- data/lib/arjdbc/tasks/hsqldb_database_tasks.rb +70 -0
- data/lib/arjdbc/tasks/jdbc_database_tasks.rb +169 -0
- data/lib/arjdbc/tasks/mssql_database_tasks.rb +46 -0
- data/lib/arjdbc/util/quoted_cache.rb +60 -0
- data/lib/arjdbc/util/serialized_attributes.rb +98 -0
- data/lib/arjdbc/util/table_copier.rb +110 -0
- data/lib/arjdbc/version.rb +1 -6
- data/lib/generators/jdbc/USAGE +9 -0
- data/lib/generators/jdbc/jdbc_generator.rb +8 -0
- data/lib/jdbc_adapter.rb +1 -1
- data/lib/jdbc_adapter/rake_tasks.rb +3 -2
- data/lib/jdbc_adapter/version.rb +2 -1
- data/pom.xml +114 -0
- data/rails_generators/jdbc_generator.rb +1 -1
- data/rails_generators/templates/config/initializers/jdbc.rb +8 -5
- data/rails_generators/templates/lib/tasks/jdbc.rake +7 -4
- data/rakelib/01-tomcat.rake +51 -0
- data/rakelib/02-test.rake +132 -0
- data/rakelib/bundler_ext.rb +11 -0
- data/rakelib/compile.rake +67 -22
- data/rakelib/db.rake +61 -0
- data/rakelib/rails.rake +204 -29
- data/src/java/arjdbc/ArJdbcModule.java +286 -0
- data/src/java/arjdbc/db2/DB2Module.java +76 -0
- data/src/java/arjdbc/db2/DB2RubyJdbcConnection.java +126 -0
- data/src/java/arjdbc/derby/DerbyModule.java +99 -243
- data/src/java/arjdbc/derby/DerbyRubyJdbcConnection.java +152 -0
- data/src/java/arjdbc/firebird/FirebirdRubyJdbcConnection.java +174 -0
- data/src/java/arjdbc/{jdbc/JdbcConnectionFactory.java → h2/H2Module.java} +20 -6
- data/src/java/arjdbc/h2/H2RubyJdbcConnection.java +27 -12
- data/src/java/arjdbc/hsqldb/HSQLDBModule.java +73 -0
- data/src/java/arjdbc/informix/InformixRubyJdbcConnection.java +7 -6
- data/src/java/arjdbc/jdbc/AdapterJavaService.java +7 -29
- data/src/java/arjdbc/jdbc/Callable.java +44 -0
- data/src/java/arjdbc/jdbc/ConnectionFactory.java +132 -0
- data/src/java/arjdbc/jdbc/DataSourceConnectionFactory.java +157 -0
- data/src/java/arjdbc/jdbc/DriverConnectionFactory.java +63 -0
- data/src/java/arjdbc/jdbc/DriverWrapper.java +119 -0
- data/src/java/arjdbc/jdbc/JdbcResult.java +130 -0
- data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +3622 -948
- data/src/java/arjdbc/mssql/MSSQLModule.java +90 -0
- data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +181 -0
- data/src/java/arjdbc/mysql/MySQLModule.java +99 -81
- data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +294 -0
- data/src/java/arjdbc/oracle/OracleModule.java +80 -0
- data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +387 -17
- data/src/java/arjdbc/postgresql/ByteaUtils.java +157 -0
- data/src/java/arjdbc/postgresql/PgResultSetMetaDataWrapper.java +23 -0
- data/src/java/arjdbc/postgresql/PostgreSQLModule.java +77 -0
- data/src/java/arjdbc/postgresql/PostgreSQLResult.java +184 -0
- data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +952 -0
- data/src/java/arjdbc/sqlite3/SQLite3Module.java +73 -0
- data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +525 -0
- data/src/java/arjdbc/util/CallResultSet.java +826 -0
- data/src/java/arjdbc/util/DateTimeUtils.java +580 -0
- data/src/java/arjdbc/util/ObjectSupport.java +65 -0
- data/src/java/arjdbc/util/QuotingUtils.java +138 -0
- data/src/java/arjdbc/util/StringCache.java +63 -0
- data/src/java/arjdbc/util/StringHelper.java +159 -0
- metadata +245 -268
- data/History.txt +0 -369
- data/Manifest.txt +0 -180
- data/README.txt +0 -181
- data/lib/active_record/connection_adapters/oracle_adapter.rb +0 -1
- data/lib/arel/engines/sql/compilers/db2_compiler.rb +0 -9
- data/lib/arel/engines/sql/compilers/derby_compiler.rb +0 -6
- data/lib/arel/engines/sql/compilers/h2_compiler.rb +0 -6
- data/lib/arel/engines/sql/compilers/hsqldb_compiler.rb +0 -15
- data/lib/arel/engines/sql/compilers/jdbc_compiler.rb +0 -6
- data/lib/arel/engines/sql/compilers/mssql_compiler.rb +0 -46
- data/lib/arel/visitors/mssql.rb +0 -44
- 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/lib/arjdbc/jdbc/driver.rb +0 -44
- data/lib/arjdbc/jdbc/missing_functionality_helper.rb +0 -87
- data/lib/arjdbc/jdbc/quoted_primary_key.rb +0 -28
- data/lib/arjdbc/jdbc/require_driver.rb +0 -16
- data/lib/arjdbc/mimer.rb +0 -2
- data/lib/arjdbc/mimer/adapter.rb +0 -142
- data/lib/arjdbc/mssql/tsql_helper.rb +0 -61
- data/lib/arjdbc/oracle.rb +0 -3
- data/lib/arjdbc/oracle/connection_methods.rb +0 -11
- data/lib/pg.rb +0 -4
- data/rakelib/package.rake +0 -92
- data/rakelib/test.rake +0 -81
- data/src/java/arjdbc/jdbc/SQLBlock.java +0 -48
- data/src/java/arjdbc/mssql/MssqlRubyJdbcConnection.java +0 -127
- data/src/java/arjdbc/postgresql/PostgresqlRubyJdbcConnection.java +0 -57
- data/src/java/arjdbc/sqlite3/Sqlite3RubyJdbcConnection.java +0 -64
- data/test/abstract_db_create.rb +0 -117
- data/test/activerecord/connection_adapters/type_conversion_test.rb +0 -31
- data/test/activerecord/connections/native_jdbc_mysql/connection.rb +0 -25
- data/test/db/db2.rb +0 -11
- data/test/db/derby.rb +0 -12
- data/test/db/h2.rb +0 -11
- data/test/db/hsqldb.rb +0 -13
- data/test/db/informix.rb +0 -11
- data/test/db/jdbc.rb +0 -11
- data/test/db/jndi_config.rb +0 -40
- data/test/db/logger.rb +0 -3
- data/test/db/mssql.rb +0 -9
- data/test/db/mysql.rb +0 -10
- data/test/db/oracle.rb +0 -34
- data/test/db/postgres.rb +0 -9
- data/test/db/sqlite3.rb +0 -11
- data/test/db2_simple_test.rb +0 -66
- data/test/derby_migration_test.rb +0 -68
- data/test/derby_multibyte_test.rb +0 -12
- data/test/derby_simple_test.rb +0 -99
- data/test/generic_jdbc_connection_test.rb +0 -29
- data/test/h2_simple_test.rb +0 -41
- data/test/has_many_through.rb +0 -79
- data/test/helper.rb +0 -5
- data/test/hsqldb_simple_test.rb +0 -6
- data/test/informix_simple_test.rb +0 -48
- data/test/jdbc_common.rb +0 -25
- data/test/jndi_callbacks_test.rb +0 -40
- data/test/jndi_test.rb +0 -25
- data/test/manualTestDatabase.rb +0 -191
- data/test/models/add_not_null_column_to_table.rb +0 -12
- data/test/models/auto_id.rb +0 -18
- data/test/models/data_types.rb +0 -28
- data/test/models/entry.rb +0 -43
- data/test/models/mixed_case.rb +0 -25
- data/test/models/reserved_word.rb +0 -18
- data/test/models/string_id.rb +0 -18
- data/test/models/validates_uniqueness_of_string.rb +0 -19
- data/test/mssql_db_create_test.rb +0 -26
- data/test/mssql_identity_insert_test.rb +0 -19
- data/test/mssql_legacy_types_test.rb +0 -58
- data/test/mssql_limit_offset_test.rb +0 -136
- data/test/mssql_multibyte_test.rb +0 -18
- data/test/mssql_simple_test.rb +0 -55
- data/test/mysql_db_create_test.rb +0 -27
- data/test/mysql_info_test.rb +0 -113
- data/test/mysql_multibyte_test.rb +0 -10
- data/test/mysql_nonstandard_primary_key_test.rb +0 -42
- data/test/mysql_simple_test.rb +0 -49
- data/test/oracle_simple_test.rb +0 -18
- data/test/oracle_specific_test.rb +0 -83
- data/test/pick_rails_version.rb +0 -3
- data/test/postgres_db_create_test.rb +0 -32
- data/test/postgres_drop_db_test.rb +0 -16
- data/test/postgres_mixed_case_test.rb +0 -29
- data/test/postgres_nonseq_pkey_test.rb +0 -38
- data/test/postgres_reserved_test.rb +0 -22
- data/test/postgres_schema_search_path_test.rb +0 -44
- data/test/postgres_simple_test.rb +0 -51
- data/test/postgres_table_alias_length_test.rb +0 -15
- data/test/simple.rb +0 -546
- data/test/sqlite3_simple_test.rb +0 -233
- data/test/sybase_jtds_simple_test.rb +0 -28
data/lib/arjdbc.rb
CHANGED
|
@@ -1,29 +1,19 @@
|
|
|
1
1
|
if defined?(JRUBY_VERSION)
|
|
2
2
|
begin
|
|
3
|
-
tried_gem ||= false
|
|
4
3
|
require 'active_record/version'
|
|
5
|
-
rescue LoadError
|
|
6
|
-
raise if tried_gem
|
|
7
|
-
require 'rubygems'
|
|
8
|
-
gem 'activerecord'
|
|
9
|
-
tried_gem = true
|
|
10
|
-
retry
|
|
11
|
-
end
|
|
12
|
-
if ActiveRecord::VERSION::MAJOR < 2
|
|
13
|
-
if defined?(RAILS_CONNECTION_ADAPTERS)
|
|
14
|
-
RAILS_CONNECTION_ADAPTERS << %q(jdbc)
|
|
15
|
-
else
|
|
16
|
-
RAILS_CONNECTION_ADAPTERS = %w(jdbc)
|
|
17
|
-
end
|
|
18
|
-
if ActiveRecord::VERSION::MAJOR == 1 && ActiveRecord::VERSION::MINOR == 14
|
|
19
|
-
require 'arjdbc/jdbc'
|
|
20
|
-
end
|
|
21
|
-
else
|
|
22
4
|
require 'active_record'
|
|
23
|
-
|
|
5
|
+
rescue LoadError => e
|
|
6
|
+
warn "activerecord-jdbc-adapter requires the activerecord gem at runtime"
|
|
7
|
+
raise e
|
|
24
8
|
end
|
|
9
|
+
require 'arjdbc/jdbc'
|
|
10
|
+
begin
|
|
11
|
+
require 'arjdbc/railtie'
|
|
12
|
+
rescue LoadError => e
|
|
13
|
+
warn "activerecord-jdbc-adapter failed to load railtie: #{e.inspect}"
|
|
14
|
+
end if defined?(Rails) && ActiveRecord::VERSION::MAJOR >= 3
|
|
25
15
|
else
|
|
26
|
-
warn "
|
|
16
|
+
warn "activerecord-jdbc-adapter is for use with JRuby only"
|
|
27
17
|
end
|
|
28
18
|
|
|
29
|
-
require 'arjdbc/version'
|
|
19
|
+
require 'arjdbc/version'
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module ArJdbc
|
|
2
|
+
module Abstract
|
|
3
|
+
module ConnectionManagement
|
|
4
|
+
|
|
5
|
+
# @override
|
|
6
|
+
def active?
|
|
7
|
+
return unless @connection
|
|
8
|
+
@connection.active?
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# @override
|
|
12
|
+
def reconnect!
|
|
13
|
+
super # clear_cache! && reset_transaction
|
|
14
|
+
@connection.reconnect! # handles adapter.configure_connection
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# @override
|
|
18
|
+
def disconnect!
|
|
19
|
+
super # clear_cache! && reset_transaction
|
|
20
|
+
return unless @connection
|
|
21
|
+
@connection.disconnect!
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# @override
|
|
25
|
+
# def verify!(*ignored)
|
|
26
|
+
# if @connection && @connection.jndi?
|
|
27
|
+
# # checkout call-back does #reconnect!
|
|
28
|
+
# else
|
|
29
|
+
# reconnect! unless active? # super
|
|
30
|
+
# end
|
|
31
|
+
# end
|
|
32
|
+
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
module ArJdbc
|
|
2
|
+
module Abstract
|
|
3
|
+
|
|
4
|
+
# This is minimum amount of code needed from base JDBC Adapter class to make common adapters
|
|
5
|
+
# work. This replaces using jdbc/adapter as a base class for all adapters.
|
|
6
|
+
module Core
|
|
7
|
+
|
|
8
|
+
attr_reader :config
|
|
9
|
+
|
|
10
|
+
def initialize(connection, logger = nil, config = {})
|
|
11
|
+
@config = config
|
|
12
|
+
|
|
13
|
+
if self.class.equal? ActiveRecord::ConnectionAdapters::JdbcAdapter
|
|
14
|
+
spec = @config.key?(:adapter_spec) ? @config[:adapter_spec] :
|
|
15
|
+
( @config[:adapter_spec] = adapter_spec(@config) ) # due resolving visitor
|
|
16
|
+
extend spec if spec
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
connection ||= jdbc_connection_class(config[:adapter_spec]).new(config, self)
|
|
20
|
+
|
|
21
|
+
super(connection, logger, config) # AbstractAdapter
|
|
22
|
+
|
|
23
|
+
connection.configure_connection # will call us (maybe)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Retrieve the raw `java.sql.Connection` object.
|
|
27
|
+
# The unwrap parameter is useful if an attempt to unwrap a pooled (JNDI)
|
|
28
|
+
# connection should be made - to really return the 'native' JDBC object.
|
|
29
|
+
# @param unwrap [true, false] whether to unwrap the connection object
|
|
30
|
+
# @return [Java::JavaSql::Connection] the JDBC connection
|
|
31
|
+
def jdbc_connection(unwrap = nil)
|
|
32
|
+
raw_connection.jdbc_connection(unwrap)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
protected
|
|
36
|
+
|
|
37
|
+
def translate_exception_class(e, sql)
|
|
38
|
+
begin
|
|
39
|
+
message = "#{e.class.name}: #{e.message}: #{sql}"
|
|
40
|
+
rescue Encoding::CompatibilityError
|
|
41
|
+
message = "#{e.class.name}: #{e.message.force_encoding sql.encoding}: #{sql}"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
exception = translate_exception(e, message)
|
|
45
|
+
exception.set_backtrace e.backtrace unless e.equal?(exception)
|
|
46
|
+
exception
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def translate_exception(e, message)
|
|
50
|
+
# we shall not translate native "Java" exceptions as they might
|
|
51
|
+
# swallow an ArJdbc / driver bug into an AR::StatementInvalid !
|
|
52
|
+
return e if e.is_a?(Java::JavaLang::Throwable)
|
|
53
|
+
|
|
54
|
+
case e
|
|
55
|
+
when SystemExit, SignalException, NoMemoryError then e
|
|
56
|
+
when ActiveModel::RangeError, TypeError, RuntimeError then e
|
|
57
|
+
else ActiveRecord::StatementInvalid.new(message)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ArJdbc
|
|
4
|
+
module Abstract
|
|
5
|
+
|
|
6
|
+
# This provides the basic interface for interacting with the
|
|
7
|
+
# database for JDBC based adapters
|
|
8
|
+
module DatabaseStatements
|
|
9
|
+
|
|
10
|
+
NO_BINDS = [].freeze
|
|
11
|
+
|
|
12
|
+
def exec_insert(sql, name = nil, binds = NO_BINDS, pk = nil, sequence_name = nil)
|
|
13
|
+
if without_prepared_statement?(binds)
|
|
14
|
+
log(sql, name) { @connection.execute_insert(sql) }
|
|
15
|
+
else
|
|
16
|
+
log(sql, name, binds) do
|
|
17
|
+
@connection.execute_insert(sql, binds)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# It appears that at this point (AR 5.0) "prepare" should only ever be true
|
|
23
|
+
# if prepared statements are enabled
|
|
24
|
+
def exec_query(sql, name = nil, binds = NO_BINDS, prepare: false)
|
|
25
|
+
if without_prepared_statement?(binds)
|
|
26
|
+
log(sql, name) { @connection.execute_query(sql) }
|
|
27
|
+
else
|
|
28
|
+
log(sql, name, binds) do
|
|
29
|
+
# It seems that #supports_statement_cache? is defined but isn't checked before setting "prepare" (AR 5.0)
|
|
30
|
+
cached_statement = fetch_cached_statement(sql) if prepare && supports_statement_cache?
|
|
31
|
+
@connection.execute_prepared_query(sql, binds, cached_statement)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def exec_update(sql, name = nil, binds = NO_BINDS)
|
|
37
|
+
if without_prepared_statement?(binds)
|
|
38
|
+
log(sql, name) { @connection.execute_update(sql) }
|
|
39
|
+
else
|
|
40
|
+
log(sql, name, binds) { @connection.execute_prepared_update(sql, binds) }
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
alias :exec_delete :exec_update
|
|
44
|
+
|
|
45
|
+
def execute(sql, name = nil)
|
|
46
|
+
log(sql, name) { @connection.execute(sql) }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def select_all(arel, name = nil, binds = NO_BINDS, preparable: nil)
|
|
50
|
+
binds = convert_legacy_binds_to_attributes(binds) if binds.first.is_a?(Array)
|
|
51
|
+
super
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
def convert_legacy_binds_to_attributes(binds)
|
|
57
|
+
binds.map do |column, value|
|
|
58
|
+
ActiveRecord::Relation::QueryAttribute.new(nil, type_cast(value, column), ActiveModel::Type::Value.new)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
require 'active_record/connection_adapters/statement_pool'
|
|
2
|
+
|
|
3
|
+
module ArJdbc
|
|
4
|
+
module Abstract
|
|
5
|
+
module StatementCache
|
|
6
|
+
|
|
7
|
+
# This works a little differently than the AR implementation in that
|
|
8
|
+
# we are storing an actual PreparedStatement object instead of just
|
|
9
|
+
# the name of the prepared statement
|
|
10
|
+
class StatementPool < ActiveRecord::ConnectionAdapters::StatementPool
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def dealloc(statement)
|
|
15
|
+
statement.close
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def initialize(*args) # (connection, logger, config)
|
|
21
|
+
super
|
|
22
|
+
|
|
23
|
+
# Only say we support the statement cache if we are using prepared statements
|
|
24
|
+
# and have a max number of statements defined
|
|
25
|
+
statement_limit = self.class.type_cast_config_to_integer(config[:statement_limit])
|
|
26
|
+
@jdbc_statement_cache_enabled = config[:prepared_statements] && (statement_limit.nil? || statement_limit > 0)
|
|
27
|
+
|
|
28
|
+
@statements = StatementPool.new(statement_limit) # AR (5.0) expects this to be stored as @statements
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Clears the prepared statements cache.
|
|
32
|
+
def clear_cache!
|
|
33
|
+
@statements.clear
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def delete_cached_statement(sql)
|
|
37
|
+
@statements.delete(cached_statement_key(sql))
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def fetch_cached_statement(sql)
|
|
41
|
+
@statements[cached_statement_key(sql)] ||= @connection.connection.prepare_statement(sql)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def supports_statement_cache?
|
|
45
|
+
@jdbc_statement_cache_enabled
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
# This should be overridden by the adapter if the sql itself
|
|
51
|
+
# is not enough to make the key unique
|
|
52
|
+
def cached_statement_key(sql)
|
|
53
|
+
sql
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
module ArJdbc
|
|
2
|
+
module Abstract
|
|
3
|
+
|
|
4
|
+
# Provides the basic interface needed to support transactions for JDBC based adapters
|
|
5
|
+
module TransactionSupport
|
|
6
|
+
|
|
7
|
+
########################## Support Checks #################################
|
|
8
|
+
|
|
9
|
+
# Does our database (+ its JDBC driver) support save-points?
|
|
10
|
+
# @since 1.3.0
|
|
11
|
+
# @override
|
|
12
|
+
def supports_savepoints?
|
|
13
|
+
@connection.supports_savepoints?
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Does this adapter support setting the isolation level for a transaction?
|
|
17
|
+
# Unlike 'plain' `ActiveRecord` we allow checking for concrete transaction
|
|
18
|
+
# isolation level support by the database.
|
|
19
|
+
# @param level optional to check if we support a specific isolation level
|
|
20
|
+
# @since 1.3.0
|
|
21
|
+
# @extension added optional level parameter
|
|
22
|
+
def supports_transaction_isolation?(level = nil)
|
|
23
|
+
return false unless level
|
|
24
|
+
@connection.supports_transaction_isolation?(level)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
########################## Transaction Interface ##########################
|
|
28
|
+
|
|
29
|
+
# Starts a database transaction.
|
|
30
|
+
# @override
|
|
31
|
+
def begin_db_transaction
|
|
32
|
+
log('BEGIN TRANSACTION'.freeze, nil) { @connection.begin }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Starts a database transaction.
|
|
36
|
+
# @param isolation the transaction isolation to use
|
|
37
|
+
def begin_isolated_db_transaction(isolation)
|
|
38
|
+
log("BEGIN ISOLATED TRANSACTION - #{isolation}", nil) { @connection.begin(isolation) }
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Commits the current database transaction.
|
|
42
|
+
# @override
|
|
43
|
+
def commit_db_transaction
|
|
44
|
+
log('COMMIT TRANSACTION'.freeze, nil) { @connection.commit }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Rolls back the current database transaction.
|
|
48
|
+
# Called from 'rollback_db_transaction' in the AbstractAdapter
|
|
49
|
+
# @override
|
|
50
|
+
def exec_rollback_db_transaction
|
|
51
|
+
log('ROLLBACK TRANSACTION'.freeze, nil) { @connection.rollback }
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
########################## Savepoint Interface ############################
|
|
55
|
+
|
|
56
|
+
# Creates a (transactional) save-point one can rollback to.
|
|
57
|
+
# Unlike 'plain' `ActiveRecord` it is allowed to pass a save-point name.
|
|
58
|
+
# @param name the save-point name
|
|
59
|
+
# @return save-point name (even if nil passed will be generated)
|
|
60
|
+
# @since 1.3.0
|
|
61
|
+
# @extension added optional name parameter
|
|
62
|
+
def create_savepoint(name = current_savepoint_name)
|
|
63
|
+
log("SAVEPOINT #{name}", 'Savepoint') { @connection.create_savepoint(name) }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Transaction rollback to a given (previously created) save-point.
|
|
67
|
+
# If no save-point name given rollback to the last created one.
|
|
68
|
+
# Called from 'rollback_to_savepoint' in AbstractAdapter
|
|
69
|
+
# @param name the save-point name
|
|
70
|
+
# @extension added optional name parameter
|
|
71
|
+
def exec_rollback_to_savepoint(name = current_savepoint_name)
|
|
72
|
+
log("ROLLBACK TO SAVEPOINT #{name}", 'Savepoint') { @connection.rollback_savepoint(name) }
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Release a previously created save-point.
|
|
76
|
+
# @note Save-points are auto-released with the transaction they're created
|
|
77
|
+
# in (on transaction commit or roll-back).
|
|
78
|
+
# @param name the save-point name
|
|
79
|
+
# @extension added optional name parameter
|
|
80
|
+
def release_savepoint(name = current_savepoint_name)
|
|
81
|
+
log("RELEASE SAVEPOINT #{name}", 'Savepoint') { @connection.release_savepoint(name) }
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
data/lib/arjdbc/db2.rb
CHANGED
data/lib/arjdbc/db2/adapter.rb
CHANGED
|
@@ -1,170 +1,334 @@
|
|
|
1
|
+
# NOTE: file contains code adapted from **ruby-ibmdb** adapter, license follows
|
|
2
|
+
=begin
|
|
3
|
+
Copyright (c) 2006 - 2015 IBM Corporation
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
a copy of this software and associated documentation files (the
|
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+
the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be
|
|
14
|
+
included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
23
|
+
=end
|
|
24
|
+
|
|
25
|
+
ArJdbc.load_java_part :DB2
|
|
26
|
+
|
|
27
|
+
require 'arjdbc/db2/column'
|
|
28
|
+
|
|
1
29
|
module ArJdbc
|
|
30
|
+
# @note This adapter doesn't support explain `config.active_record.auto_explain_threshold_in_seconds` should be commented (Rails < 4.0)
|
|
2
31
|
module DB2
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
32
|
+
|
|
33
|
+
# @private
|
|
34
|
+
def self.extended(adapter); initialize!; end
|
|
35
|
+
|
|
36
|
+
# @private
|
|
37
|
+
@@_initialized = nil
|
|
38
|
+
|
|
39
|
+
# @private
|
|
40
|
+
def self.initialize!
|
|
41
|
+
return if @@_initialized; @@_initialized = true
|
|
42
|
+
|
|
43
|
+
require 'arjdbc/util/serialized_attributes'
|
|
44
|
+
Util::SerializedAttributes.setup /blob|clob/i, 'after_save_with_db2_lob'
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# @see ActiveRecord::ConnectionAdapters::JdbcAdapter#jdbc_connection_class
|
|
48
|
+
def self.jdbc_connection_class
|
|
49
|
+
::ActiveRecord::ConnectionAdapters::DB2JdbcConnection
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# @see ActiveRecord::ConnectionAdapters::JdbcAdapter#jdbc_column_class
|
|
53
|
+
def jdbc_column_class
|
|
54
|
+
::ActiveRecord::ConnectionAdapters::DB2Column
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# @private
|
|
58
|
+
@@emulate_booleans = true
|
|
59
|
+
|
|
60
|
+
# Boolean emulation can be disabled using :
|
|
61
|
+
#
|
|
62
|
+
# ArJdbc::DB2.emulate_booleans = false
|
|
63
|
+
#
|
|
64
|
+
def self.emulate_booleans?; @@emulate_booleans; end
|
|
65
|
+
# @deprecated Use {#emulate_booleans?} instead.
|
|
66
|
+
def self.emulate_booleans; @@emulate_booleans; end
|
|
67
|
+
# @see #emulate_booleans?
|
|
68
|
+
def self.emulate_booleans=(emulate); @@emulate_booleans = emulate; end
|
|
69
|
+
|
|
70
|
+
# @private
|
|
71
|
+
@@update_lob_values = true
|
|
72
|
+
|
|
73
|
+
# Updating records with LOB values (binary/text columns) in a separate
|
|
74
|
+
# statement can be disabled using :
|
|
75
|
+
#
|
|
76
|
+
# ArJdbc::DB2.update_lob_values = false
|
|
77
|
+
#
|
|
78
|
+
# @note This only applies when prepared statements are not used.
|
|
79
|
+
def self.update_lob_values?; @@update_lob_values; end
|
|
80
|
+
# @see #update_lob_values?
|
|
81
|
+
def self.update_lob_values=(update); @@update_lob_values = update; end
|
|
82
|
+
|
|
83
|
+
# @see #update_lob_values?
|
|
84
|
+
# @see ArJdbc::Util::SerializedAttributes#update_lob_columns
|
|
85
|
+
def update_lob_value?(value, column = nil)
|
|
86
|
+
DB2.update_lob_values? && ! prepared_statements? # && value
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# @see #quote
|
|
90
|
+
# @private
|
|
91
|
+
BLOB_VALUE_MARKER = "BLOB('')"
|
|
92
|
+
# @see #quote
|
|
93
|
+
# @private
|
|
94
|
+
CLOB_VALUE_MARKER = "''"
|
|
95
|
+
|
|
96
|
+
def configure_connection
|
|
97
|
+
schema = self.schema
|
|
98
|
+
set_schema(schema) if schema && schema != config[:username]
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
ADAPTER_NAME = 'DB2'.freeze
|
|
102
|
+
|
|
103
|
+
def adapter_name
|
|
104
|
+
ADAPTER_NAME
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
NATIVE_DATABASE_TYPES = {
|
|
108
|
+
:string => { :name => "varchar", :limit => 255 },
|
|
109
|
+
:integer => { :name => "integer" },
|
|
110
|
+
:bigint => { :name => 'bigint' },
|
|
111
|
+
:float => { :name => "real" }, # :limit => 24
|
|
112
|
+
:double => { :name => "double" }, # :limit => 53
|
|
113
|
+
:text => { :name => "clob" },
|
|
114
|
+
:binary => { :name => "blob" },
|
|
115
|
+
:xml => { :name => "xml" },
|
|
116
|
+
:decimal => { :name => "decimal" }, # :limit => 31
|
|
117
|
+
:char => { :name => "char" }, # :limit => 254
|
|
118
|
+
:date => { :name => "date" },
|
|
119
|
+
:datetime => { :name => "timestamp" },
|
|
120
|
+
:timestamp => { :name => "timestamp" },
|
|
121
|
+
:time => { :name => "time" },
|
|
122
|
+
:boolean => { :name => "smallint" }, # no native boolean type
|
|
123
|
+
#:rowid => { :name => "rowid" }, # rowid is a supported datatype on z/OS and i/5
|
|
124
|
+
#:serial => { :name => "serial" }, # supported datatype on Informix Dynamic Server
|
|
125
|
+
#:graphic => { :name => "graphic", :limit => 1 }, # :limit => 127
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
# @override
|
|
129
|
+
def initialize_type_map(m)
|
|
130
|
+
register_class_with_limit m, %r(boolean)i, ActiveRecord::Type::Boolean
|
|
131
|
+
register_class_with_limit m, %r(char)i, ActiveRecord::Type::String
|
|
132
|
+
register_class_with_limit m, %r(binary)i, ActiveRecord::Type::Binary
|
|
133
|
+
register_class_with_limit m, %r(text)i, ActiveRecord::Type::Text
|
|
134
|
+
register_class_with_limit m, %r(date)i, ActiveRecord::Type::Date
|
|
135
|
+
register_class_with_limit m, %r(time)i, ActiveRecord::Type::Time
|
|
136
|
+
register_class_with_limit m, %r(datetime)i, ActiveRecord::Type::DateTime
|
|
137
|
+
register_class_with_limit m, %r(float)i, ActiveRecord::Type::Float
|
|
138
|
+
register_class_with_limit m, %r(int)i, ActiveRecord::Type::Integer
|
|
139
|
+
|
|
140
|
+
m.alias_type %r(blob)i, 'binary'
|
|
141
|
+
m.alias_type %r(clob)i, 'text'
|
|
142
|
+
m.alias_type %r(timestamp)i, 'datetime'
|
|
143
|
+
m.alias_type %r(numeric)i, 'decimal'
|
|
144
|
+
m.alias_type %r(number)i, 'decimal'
|
|
145
|
+
m.alias_type %r(double)i, 'float'
|
|
146
|
+
m.alias_type %r(real)i, 'float'
|
|
147
|
+
|
|
148
|
+
m.register_type(%r(decimal)i) do |sql_type|
|
|
149
|
+
scale = extract_scale(sql_type)
|
|
150
|
+
precision = extract_precision(sql_type)
|
|
151
|
+
limit = extract_limit(sql_type)
|
|
152
|
+
if scale == 0
|
|
153
|
+
ActiveRecord::Type::BigInteger.new(:precision => precision, :limit => limit)
|
|
21
154
|
else
|
|
22
|
-
|
|
155
|
+
ActiveRecord::Type::Decimal.new(:precision => precision, :scale => scale)
|
|
23
156
|
end
|
|
24
157
|
end
|
|
25
158
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
159
|
+
m.alias_type %r(for bit data)i, 'binary'
|
|
160
|
+
m.alias_type %r(smallint)i, 'boolean'
|
|
161
|
+
m.alias_type %r(serial)i, 'int'
|
|
162
|
+
m.alias_type %r(decfloat)i, 'decimal'
|
|
163
|
+
#m.alias_type %r(real)i, 'decimal'
|
|
164
|
+
m.alias_type %r(graphic)i, 'binary'
|
|
165
|
+
m.alias_type %r(rowid)i, 'int'
|
|
166
|
+
|
|
167
|
+
m.register_type(%r(smallint)i) do
|
|
168
|
+
if DB2.emulate_booleans?
|
|
169
|
+
ActiveRecord::Type::Boolean.new
|
|
32
170
|
else
|
|
33
|
-
|
|
171
|
+
ActiveRecord::Type::Integer.new(:limit => 1)
|
|
34
172
|
end
|
|
35
173
|
end
|
|
36
174
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
return nil if value.blank?
|
|
40
|
-
guess_date_or_time((value.is_a? Time) ? value : cast_to_time(value))
|
|
41
|
-
end
|
|
175
|
+
m.register_type %r(xml)i, XmlType.new
|
|
176
|
+
end if AR42
|
|
42
177
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
time_array = ParseDate.parsedate(value, true)
|
|
47
|
-
time_array[0] ||= 2000; time_array[1] ||= 1; time_array[2] ||= 1;
|
|
48
|
-
Time.send(ActiveRecord::Base.default_timezone, *time_array) rescue nil
|
|
49
|
-
end
|
|
178
|
+
# @private
|
|
179
|
+
class XmlType < ActiveRecord::Type::String
|
|
180
|
+
def type; :xml end
|
|
50
181
|
|
|
51
|
-
def
|
|
52
|
-
|
|
53
|
-
|
|
182
|
+
def type_cast_for_database(value)
|
|
183
|
+
return unless value
|
|
184
|
+
Data.new(super)
|
|
54
185
|
end
|
|
55
186
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
:boolean
|
|
187
|
+
class Data
|
|
188
|
+
def initialize(value)
|
|
189
|
+
@value = value
|
|
190
|
+
end
|
|
191
|
+
def to_s; @value end
|
|
62
192
|
end
|
|
193
|
+
end if AR42
|
|
63
194
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
195
|
+
# @override
|
|
196
|
+
def reset_column_information
|
|
197
|
+
initialize_type_map(type_map)
|
|
198
|
+
end if AR42
|
|
199
|
+
|
|
200
|
+
# @override
|
|
201
|
+
def native_database_types
|
|
202
|
+
# NOTE: currently merging with what JDBC gives us since there's a lot
|
|
203
|
+
# of DB2-like stuff we could be connecting e.g. "classic", Z/OS etc.
|
|
204
|
+
# types = super
|
|
205
|
+
types = super.merge(NATIVE_DATABASE_TYPES)
|
|
206
|
+
types
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# @private
|
|
210
|
+
class TableDefinition < ::ActiveRecord::ConnectionAdapters::TableDefinition
|
|
211
|
+
|
|
212
|
+
def xml(*args)
|
|
213
|
+
options = args.extract_options!
|
|
214
|
+
column(args[0], 'xml', options)
|
|
75
215
|
end
|
|
76
216
|
|
|
77
|
-
#
|
|
78
|
-
def default_value(value)
|
|
79
|
-
# IBM i (AS400) will return an empty string instead of null for no default
|
|
80
|
-
return nil if value.blank?
|
|
217
|
+
# IBM DB adapter (MRI) compatibility :
|
|
81
218
|
|
|
82
|
-
|
|
83
|
-
|
|
219
|
+
# @private
|
|
220
|
+
# @deprecated
|
|
221
|
+
def double(*args)
|
|
222
|
+
options = args.extract_options!
|
|
223
|
+
column(args[0], 'double', options)
|
|
224
|
+
end
|
|
84
225
|
|
|
85
|
-
|
|
226
|
+
# @private
|
|
227
|
+
def decfloat(*args)
|
|
228
|
+
options = args.extract_options!
|
|
229
|
+
column(args[0], 'decfloat', options)
|
|
86
230
|
end
|
|
87
|
-
end
|
|
88
231
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
elsif ActiveRecord::ConnectionAdapters::JdbcConnection::insert?(sql)
|
|
93
|
-
(@connection.execute_insert(sql) or last_insert_id(sql)).to_i
|
|
94
|
-
else
|
|
95
|
-
@connection.execute_update(sql)
|
|
232
|
+
def graphic(*args)
|
|
233
|
+
options = args.extract_options!
|
|
234
|
+
column(args[0], 'graphic', options)
|
|
96
235
|
end
|
|
97
|
-
end
|
|
98
236
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
rescue Exception => e
|
|
105
|
-
raise "Could not call CHGJOB INQMSGRPY(*SYSRPYL) and ADDRPYLE SEQNBR(9876) MSGID(CPA32B2) RPY('I').\n" +
|
|
106
|
-
"Do you have authority to do this?\n\n" + e.to_s
|
|
237
|
+
# @private
|
|
238
|
+
# @deprecated
|
|
239
|
+
def vargraphic(*args)
|
|
240
|
+
options = args.extract_options!
|
|
241
|
+
column(args[0], 'vargraphic', options)
|
|
107
242
|
end
|
|
108
243
|
|
|
109
|
-
|
|
244
|
+
# @private
|
|
245
|
+
# @deprecated
|
|
246
|
+
def bigint(*args)
|
|
247
|
+
options = args.extract_options!
|
|
248
|
+
column(args[0], 'bigint', options)
|
|
249
|
+
end
|
|
110
250
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
rescue Exception => e
|
|
115
|
-
raise "Could not call CHGJOB INQMSGRPY(*DFT) and RMVRPYLE SEQNBR(9876).\n" +
|
|
116
|
-
"Do you have authority to do this?\n\n" + e.to_s
|
|
251
|
+
def char(*args)
|
|
252
|
+
options = args.extract_options!
|
|
253
|
+
column(args[0], 'char', options)
|
|
117
254
|
end
|
|
118
|
-
|
|
119
|
-
end
|
|
255
|
+
# alias_method :character, :char
|
|
120
256
|
|
|
121
|
-
def last_insert_id(sql)
|
|
122
|
-
table_name = sql.split(/\s/)[2]
|
|
123
|
-
result = select(ActiveRecord::Base.send(:sanitize_sql,
|
|
124
|
-
%[select IDENTITY_VAL_LOCAL() as last_insert_id from #{table_name}],
|
|
125
|
-
nil))
|
|
126
|
-
result.last['last_insert_id']
|
|
127
257
|
end
|
|
128
258
|
|
|
129
|
-
def
|
|
130
|
-
|
|
131
|
-
tp[:string][:limit] = 255
|
|
132
|
-
tp[:integer][:limit] = nil
|
|
133
|
-
tp[:boolean] = {:name => "smallint"}
|
|
134
|
-
tp
|
|
259
|
+
def table_definition(*args)
|
|
260
|
+
new_table_definition(TableDefinition, *args)
|
|
135
261
|
end
|
|
136
262
|
|
|
137
|
-
def
|
|
138
|
-
|
|
139
|
-
|
|
263
|
+
def prefetch_primary_key?(table_name = nil)
|
|
264
|
+
# TRUE if the table has no identity column
|
|
265
|
+
names = table_name.upcase.split(".")
|
|
266
|
+
sql = "SELECT 1 FROM SYSCAT.COLUMNS WHERE IDENTITY = 'Y' "
|
|
267
|
+
sql << "AND TABSCHEMA = '#{names.first}' " if names.size == 2
|
|
268
|
+
sql << "AND TABNAME = '#{names.last}'"
|
|
269
|
+
select_one(sql).nil?
|
|
140
270
|
end
|
|
141
271
|
|
|
142
|
-
def
|
|
143
|
-
|
|
272
|
+
def next_sequence_value(sequence_name)
|
|
273
|
+
select_value("SELECT NEXT VALUE FOR #{sequence_name} FROM sysibm.sysdummy1")
|
|
144
274
|
end
|
|
145
275
|
|
|
146
|
-
def
|
|
147
|
-
|
|
148
|
-
|
|
276
|
+
def create_table(name, options = {}, &block)
|
|
277
|
+
if zos?
|
|
278
|
+
zos_create_table(name, options, &block)
|
|
279
|
+
else
|
|
280
|
+
super
|
|
281
|
+
end
|
|
149
282
|
end
|
|
150
283
|
|
|
151
|
-
def
|
|
152
|
-
|
|
153
|
-
end
|
|
284
|
+
def zos_create_table(name, options = {})
|
|
285
|
+
table_definition = new_table_definition TableDefinition, name, options[:temporary], options[:options], options[:as]
|
|
154
286
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
287
|
+
unless options[:id] == false
|
|
288
|
+
table_definition.primary_key(options[:primary_key] || primary_key(name))
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
yield table_definition if block_given?
|
|
292
|
+
|
|
293
|
+
# Clobs in DB2 Host have to be created after the Table with an auxiliary Table.
|
|
294
|
+
clob_columns = []
|
|
295
|
+
table_definition.columns.delete_if do |column|
|
|
296
|
+
if column.type && column.type.to_sym == :text
|
|
297
|
+
clob_columns << column; true
|
|
161
298
|
end
|
|
162
|
-
elsif limit && offset
|
|
163
|
-
sql.gsub!(/SELECT/i, 'SELECT B.* FROM (SELECT A.*, row_number() over () AS internal$rownum FROM (SELECT')
|
|
164
|
-
sql << ") A ) B WHERE B.internal$rownum > #{offset} AND B.internal$rownum <= #{sanitize_limit(limit) + offset}"
|
|
165
299
|
end
|
|
166
|
-
|
|
300
|
+
|
|
301
|
+
drop_table(name, options) if options[:force] && table_exists?(name)
|
|
302
|
+
|
|
303
|
+
create_sql = "CREATE#{' TEMPORARY' if options[:temporary]} TABLE "
|
|
304
|
+
create_sql << "#{quote_table_name(name)} ("
|
|
305
|
+
create_sql << table_definition.to_sql
|
|
306
|
+
create_sql << ") #{options[:options]}"
|
|
307
|
+
if @config[:database] && @config[:tablespace]
|
|
308
|
+
create_sql << " IN #{@config[:database]}.#{@config[:tablespace]}"
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
execute create_sql
|
|
312
|
+
|
|
313
|
+
# Table definition is complete only when a unique index is created on the primary_key column for DB2 V8 on zOS
|
|
314
|
+
# create index on id column if options[:id] is nil or id ==true
|
|
315
|
+
# else check if options[:primary_key]is not nil then create an unique index on that column
|
|
316
|
+
# TODO someone on Z/OS should test this out - also not needed for V9 ?
|
|
317
|
+
#primary_column = options[:id] == true ? 'id' : options[:primary_key]
|
|
318
|
+
#add_index(name, (primary_column || 'id').to_s, :unique => true)
|
|
319
|
+
|
|
320
|
+
clob_columns.each do |clob_column|
|
|
321
|
+
column_name = clob_column.name.to_s
|
|
322
|
+
execute "ALTER TABLE #{name} ADD COLUMN #{column_name} clob"
|
|
323
|
+
clob_table_name = "#{name}_#{column_name}_CD_"
|
|
324
|
+
if @config[:database] && @config[:lob_tablespaces]
|
|
325
|
+
in_lob_table_space = " IN #{@config[:database]}.#{@config[:lob_tablespaces][name.split(".")[1]]}"
|
|
326
|
+
end
|
|
327
|
+
execute "CREATE AUXILIARY TABLE #{clob_table_name} #{in_lob_table_space} STORES #{name} COLUMN #{column_name}"
|
|
328
|
+
execute "CREATE UNIQUE INDEX #{clob_table_name} ON #{clob_table_name};"
|
|
329
|
+
end
|
|
167
330
|
end
|
|
331
|
+
private :zos_create_table
|
|
168
332
|
|
|
169
333
|
def pk_and_sequence_for(table)
|
|
170
334
|
# In JDBC/DB2 side, only upcase names of table and column are handled.
|
|
@@ -176,88 +340,255 @@ module ArJdbc
|
|
|
176
340
|
keys
|
|
177
341
|
end
|
|
178
342
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
343
|
+
# Properly quotes the various data types.
|
|
344
|
+
# @param value contains the data
|
|
345
|
+
# @param column (optional) contains info on the field
|
|
346
|
+
# @override
|
|
347
|
+
def quote(value, column = nil)
|
|
348
|
+
return value.quoted_id if value.respond_to?(:quoted_id)
|
|
349
|
+
return value if sql_literal?(value)
|
|
182
350
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
351
|
+
if column
|
|
352
|
+
if column.respond_to?(:primary) && column.primary && column.klass != String
|
|
353
|
+
return value.to_i.to_s
|
|
354
|
+
end
|
|
355
|
+
if value && (column.type.to_sym == :decimal || column.type.to_sym == :integer)
|
|
356
|
+
return value.to_s
|
|
357
|
+
end
|
|
189
358
|
end
|
|
359
|
+
|
|
360
|
+
column_type = column && column.type.to_sym
|
|
361
|
+
|
|
190
362
|
case value
|
|
191
|
-
when
|
|
192
|
-
|
|
193
|
-
|
|
363
|
+
when nil then 'NULL'
|
|
364
|
+
when Numeric # IBM_DB doesn't accept quotes on numeric types
|
|
365
|
+
# if the column type is text or string, return the quote value
|
|
366
|
+
if column_type == :text || column_type == :string
|
|
367
|
+
"'#{value}'"
|
|
368
|
+
else
|
|
369
|
+
value.to_s
|
|
370
|
+
end
|
|
371
|
+
when String, ActiveSupport::Multibyte::Chars
|
|
372
|
+
if column_type == :binary && column.sql_type !~ /for bit data/i
|
|
373
|
+
if update_lob_value?(value, column)
|
|
374
|
+
value.nil? ? 'NULL' : BLOB_VALUE_MARKER # '@@@IBMBINARY@@@'"
|
|
375
|
+
else
|
|
376
|
+
"BLOB('#{quote_string(value)}')"
|
|
377
|
+
end
|
|
378
|
+
elsif column && column.sql_type =~ /clob/ # :text
|
|
379
|
+
if update_lob_value?(value, column)
|
|
380
|
+
value.nil? ? 'NULL' : CLOB_VALUE_MARKER # "'@@@IBMTEXT@@@'"
|
|
381
|
+
else
|
|
382
|
+
"'#{quote_string(value)}'"
|
|
383
|
+
end
|
|
384
|
+
elsif column_type == :xml
|
|
385
|
+
value.nil? ? 'NULL' : "'#{quote_string(value)}'" # "'<ibm>@@@IBMXML@@@</ibm>'"
|
|
194
386
|
else
|
|
195
387
|
"'#{quote_string(value)}'"
|
|
196
388
|
end
|
|
389
|
+
when Symbol then "'#{quote_string(value.to_s)}'"
|
|
390
|
+
when Time
|
|
391
|
+
# AS400 doesn't support date in time column
|
|
392
|
+
if column_type == :time
|
|
393
|
+
quote_time(value)
|
|
394
|
+
else
|
|
395
|
+
super
|
|
396
|
+
end
|
|
197
397
|
else super
|
|
198
398
|
end
|
|
199
399
|
end
|
|
200
400
|
|
|
201
|
-
|
|
202
|
-
|
|
401
|
+
# @override
|
|
402
|
+
def quoted_date(value)
|
|
403
|
+
if value.acts_like?(:time) && value.respond_to?(:usec)
|
|
404
|
+
usec = sprintf("%06d", value.usec)
|
|
405
|
+
value = ::ActiveRecord::Base.default_timezone == :utc ? value.getutc : value.getlocal
|
|
406
|
+
"#{value.strftime("%Y-%m-%d %H:%M:%S")}.#{usec}"
|
|
407
|
+
else
|
|
408
|
+
super
|
|
409
|
+
end
|
|
410
|
+
end if ::ActiveRecord::VERSION::MAJOR >= 3
|
|
411
|
+
|
|
412
|
+
def quote_time(value)
|
|
413
|
+
value = ::ActiveRecord::Base.default_timezone == :utc ? value.getutc : value.getlocal
|
|
414
|
+
# AS400 doesn't support date in time column
|
|
415
|
+
"'#{value.strftime("%H:%M:%S")}'"
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
def quote_column_name(column_name)
|
|
419
|
+
column_name.to_s
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
def modify_types(types)
|
|
423
|
+
super(types)
|
|
424
|
+
types[:primary_key] = 'int not null generated by default as identity (start with 1) primary key'
|
|
425
|
+
types[:string][:limit] = 255
|
|
426
|
+
types[:integer][:limit] = nil
|
|
427
|
+
types[:boolean] = {:name => "decimal(1)"}
|
|
428
|
+
types
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
def type_to_sql(type, limit = nil, precision = nil, scale = nil)
|
|
432
|
+
limit = nil if type.to_sym == :integer
|
|
433
|
+
super(type, limit, precision, scale)
|
|
203
434
|
end
|
|
204
435
|
|
|
205
|
-
|
|
206
|
-
|
|
436
|
+
# @private
|
|
437
|
+
VALUES_DEFAULT = 'VALUES ( DEFAULT )' # NOTE: Arel::Visitors::DB2 uses this
|
|
438
|
+
|
|
439
|
+
# @override
|
|
440
|
+
def empty_insert_statement_value
|
|
441
|
+
VALUES_DEFAULT # won't work as DB2 needs to know the column count
|
|
207
442
|
end
|
|
208
443
|
|
|
209
|
-
def
|
|
210
|
-
|
|
444
|
+
def add_column(table_name, column_name, type, options = {})
|
|
445
|
+
# The keyword COLUMN allows to use reserved names for columns (ex: date)
|
|
446
|
+
add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ADD COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
|
|
447
|
+
add_column_options!(add_column_sql, options)
|
|
448
|
+
execute(add_column_sql)
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
def add_column_options!(sql, options)
|
|
452
|
+
# handle case of defaults for CLOB columns,
|
|
453
|
+
# which might get incorrect if we write LOBs in the after_save callback
|
|
454
|
+
if options_include_default?(options)
|
|
455
|
+
column = options[:column]
|
|
456
|
+
if column && column.type == :text
|
|
457
|
+
sql << " DEFAULT #{quote(options.delete(:default))}"
|
|
458
|
+
end
|
|
459
|
+
if column && column.type == :binary
|
|
460
|
+
# quoting required for the default value of a column :
|
|
461
|
+
value = options.delete(:default)
|
|
462
|
+
# DB2 z/OS only allows NULL or "" (empty) string as DEFAULT value
|
|
463
|
+
# for a BLOB column. non-empty string and non-NULL, return error!
|
|
464
|
+
if value.nil?
|
|
465
|
+
sql_value = "NULL"
|
|
466
|
+
else
|
|
467
|
+
sql_value = zos? ? "#{value}" : "BLOB('#{quote_string(value)}'"
|
|
468
|
+
end
|
|
469
|
+
sql << " DEFAULT #{sql_value}"
|
|
470
|
+
end
|
|
471
|
+
end
|
|
472
|
+
super
|
|
211
473
|
end
|
|
212
474
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
475
|
+
# @note Only used with (non-AREL) ActiveRecord **2.3**.
|
|
476
|
+
# @see Arel::Visitors::DB2
|
|
477
|
+
def add_limit_offset!(sql, options)
|
|
478
|
+
limit = options[:limit]
|
|
479
|
+
replace_limit_offset!(sql, limit, options[:offset]) if limit
|
|
480
|
+
end if ::ActiveRecord::VERSION::MAJOR < 3
|
|
481
|
+
|
|
482
|
+
# @private shared with {Arel::Visitors::DB2}
|
|
483
|
+
def replace_limit_offset!(sql, limit, offset, orders = nil)
|
|
484
|
+
limit = limit.to_i
|
|
485
|
+
|
|
486
|
+
if offset # && limit
|
|
487
|
+
over_order_by = nil # NOTE: orders matching got reverted as it was not complete and there were no case covering it ...
|
|
488
|
+
|
|
489
|
+
start_sql = "SELECT B.* FROM (SELECT A.*, row_number() OVER (#{over_order_by}) AS internal$rownum FROM (SELECT"
|
|
490
|
+
end_sql = ") A ) B WHERE B.internal$rownum > #{offset} AND B.internal$rownum <= #{limit + offset.to_i}"
|
|
491
|
+
|
|
492
|
+
if sql.is_a?(String)
|
|
493
|
+
sql.sub!(/SELECT/i, start_sql)
|
|
494
|
+
sql << end_sql
|
|
495
|
+
else # AR 4.2 sql.class ... Arel::Collectors::Bind
|
|
496
|
+
sql.parts[0] = start_sql # sql.sub! /SELECT/i
|
|
497
|
+
sql.parts[ sql.parts.length ] = end_sql
|
|
498
|
+
end
|
|
499
|
+
else
|
|
500
|
+
limit_sql = limit == 1 ? " FETCH FIRST ROW ONLY" : " FETCH FIRST #{limit} ROWS ONLY"
|
|
501
|
+
if sql.is_a?(String)
|
|
502
|
+
sql << limit_sql
|
|
503
|
+
else # AR 4.2 sql.class ... Arel::Collectors::Bind
|
|
504
|
+
sql.parts[ sql.parts.length ] = limit_sql
|
|
505
|
+
end
|
|
216
506
|
end
|
|
507
|
+
sql
|
|
508
|
+
end
|
|
509
|
+
|
|
510
|
+
# @deprecated seems not sued nor tested ?!
|
|
511
|
+
def runstats_for_table(tablename, priority = 10)
|
|
512
|
+
@connection.execute_update "call sysproc.admin_cmd('RUNSTATS ON TABLE #{tablename} WITH DISTRIBUTION AND DETAILED INDEXES ALL UTIL_IMPACT_PRIORITY #{priority}')"
|
|
513
|
+
end
|
|
514
|
+
|
|
515
|
+
if ::ActiveRecord::VERSION::MAJOR >= 4
|
|
516
|
+
|
|
517
|
+
def select(sql, name = nil, binds = [])
|
|
518
|
+
exec_query(to_sql(suble_null_test(sql), binds), name, binds)
|
|
519
|
+
end
|
|
520
|
+
|
|
521
|
+
else
|
|
522
|
+
|
|
523
|
+
def select(sql, name = nil, binds = [])
|
|
524
|
+
exec_query_raw(to_sql(suble_null_test(sql), binds), name, binds)
|
|
525
|
+
end
|
|
526
|
+
|
|
217
527
|
end
|
|
218
528
|
|
|
219
|
-
|
|
220
|
-
|
|
529
|
+
# @private
|
|
530
|
+
IS_NOT_NULL = /(!=|<>)\s*NULL/i
|
|
531
|
+
# @private
|
|
532
|
+
IS_NULL = /=\s*NULL/i
|
|
533
|
+
|
|
534
|
+
def suble_null_test(sql)
|
|
535
|
+
return sql unless sql.is_a?(String)
|
|
536
|
+
# DB2 does not like "= NULL", "!= NULL", or "<> NULL" :
|
|
537
|
+
sql = sql.dup
|
|
538
|
+
sql.gsub! IS_NOT_NULL, 'IS NOT NULL'
|
|
539
|
+
sql.gsub! IS_NULL, 'IS NULL'
|
|
540
|
+
sql
|
|
541
|
+
end
|
|
542
|
+
private :suble_null_test
|
|
543
|
+
|
|
544
|
+
def add_index(table_name, column_name, options = {})
|
|
545
|
+
if ! zos? || ( table_name.to_s == ActiveRecord::Migrator.schema_migrations_table_name.to_s )
|
|
546
|
+
column_name = column_name.to_s if column_name.is_a?(Symbol)
|
|
547
|
+
super
|
|
548
|
+
else
|
|
549
|
+
statement = 'CREATE'
|
|
550
|
+
statement << ' UNIQUE ' if options[:unique]
|
|
551
|
+
statement << " INDEX #{ActiveRecord::Base.table_name_prefix}#{options[:name]} "
|
|
552
|
+
statement << " ON #{table_name}(#{column_name})"
|
|
553
|
+
|
|
554
|
+
execute statement
|
|
555
|
+
end
|
|
221
556
|
end
|
|
222
557
|
|
|
223
|
-
|
|
224
|
-
|
|
558
|
+
# @override
|
|
559
|
+
def remove_index!(table_name, index_name)
|
|
560
|
+
execute "DROP INDEX #{quote_column_name(index_name)}"
|
|
225
561
|
end
|
|
226
562
|
|
|
227
563
|
# http://publib.boulder.ibm.com/infocenter/db2luw/v9r7/topic/com.ibm.db2.luw.admin.dbobj.doc/doc/t0020130.html
|
|
228
564
|
# ...not supported on IBM i, so we raise in this case
|
|
229
565
|
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
else
|
|
233
|
-
execute "ALTER TABLE #{table_name} RENAME COLUMN #{column_name} TO #{new_column_name}"
|
|
234
|
-
reorg_table(table_name)
|
|
235
|
-
end
|
|
566
|
+
sql = "ALTER TABLE #{table_name} RENAME COLUMN #{column_name} TO #{new_column_name}"
|
|
567
|
+
execute_table_change(sql, table_name, 'Rename Column')
|
|
236
568
|
end
|
|
237
569
|
|
|
238
570
|
def change_column_null(table_name, column_name, null)
|
|
239
571
|
if null
|
|
240
|
-
|
|
572
|
+
sql = "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} DROP NOT NULL"
|
|
241
573
|
else
|
|
242
|
-
|
|
574
|
+
sql = "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} SET NOT NULL"
|
|
243
575
|
end
|
|
244
|
-
|
|
576
|
+
execute_table_change(sql, table_name, 'Change Column')
|
|
245
577
|
end
|
|
246
578
|
|
|
247
579
|
def change_column_default(table_name, column_name, default)
|
|
248
580
|
if default.nil?
|
|
249
|
-
|
|
581
|
+
sql = "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} DROP DEFAULT"
|
|
250
582
|
else
|
|
251
|
-
|
|
583
|
+
sql = "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} SET WITH DEFAULT #{quote(default)}"
|
|
252
584
|
end
|
|
253
|
-
|
|
585
|
+
execute_table_change(sql, table_name, 'Change Column')
|
|
254
586
|
end
|
|
255
587
|
|
|
256
588
|
def change_column(table_name, column_name, type, options = {})
|
|
257
589
|
data_type = type_to_sql(type, options[:limit], options[:precision], options[:scale])
|
|
258
590
|
sql = "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} SET DATA TYPE #{data_type}"
|
|
259
|
-
|
|
260
|
-
reorg_table(table_name)
|
|
591
|
+
execute_table_change(sql, table_name, 'Change Column')
|
|
261
592
|
|
|
262
593
|
if options.include?(:default) and options.include?(:null)
|
|
263
594
|
# which to run first?
|
|
@@ -275,135 +606,184 @@ module ArJdbc
|
|
|
275
606
|
end
|
|
276
607
|
end
|
|
277
608
|
|
|
278
|
-
|
|
279
|
-
def remove_column(table_name, column_name) #:nodoc:
|
|
280
|
-
sql = "ALTER TABLE #{table_name} DROP COLUMN #{column_name}"
|
|
609
|
+
if ActiveRecord::VERSION::MAJOR >= 4
|
|
281
610
|
|
|
282
|
-
|
|
283
|
-
|
|
611
|
+
def remove_column(table_name, column_name, type = nil, options = {})
|
|
612
|
+
db2_remove_column(table_name, column_name)
|
|
284
613
|
end
|
|
285
614
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
615
|
+
else
|
|
616
|
+
|
|
617
|
+
def remove_column(table_name, *column_names)
|
|
618
|
+
# http://publib.boulder.ibm.com/infocenter/db2luw/v9r7/topic/com.ibm.db2.luw.admin.dbobj.doc/doc/t0020132.html
|
|
619
|
+
outcome = nil
|
|
620
|
+
column_names = column_names.flatten
|
|
621
|
+
for column_name in column_names
|
|
622
|
+
outcome = db2_remove_column(table_name, column_name)
|
|
623
|
+
end
|
|
624
|
+
column_names.size == 1 ? outcome : nil
|
|
625
|
+
end
|
|
626
|
+
|
|
627
|
+
end
|
|
628
|
+
|
|
629
|
+
def rename_table(name, new_name)
|
|
630
|
+
# http://publib.boulder.ibm.com/infocenter/db2luw/v9r7/topic/com.ibm.db2.luw.sql.ref.doc/doc/r0000980.html
|
|
631
|
+
execute_table_change("RENAME TABLE #{name} TO #{new_name}", new_name, 'Rename Table')
|
|
290
632
|
end
|
|
291
633
|
|
|
292
634
|
def tables
|
|
293
|
-
@connection.tables(nil,
|
|
635
|
+
@connection.tables(nil, schema)
|
|
294
636
|
end
|
|
295
637
|
|
|
296
|
-
# only record precision and scale for types that can set
|
|
297
|
-
# them via CREATE TABLE:
|
|
638
|
+
# only record precision and scale for types that can set them via CREATE TABLE:
|
|
298
639
|
# http://publib.boulder.ibm.com/infocenter/db2luw/v9r7/topic/com.ibm.db2.luw.sql.ref.doc/doc/r0000927.html
|
|
299
|
-
|
|
640
|
+
|
|
641
|
+
HAVE_LIMIT = %w(FLOAT DECFLOAT CHAR VARCHAR CLOB BLOB NCHAR NCLOB DBCLOB GRAPHIC VARGRAPHIC) # TIMESTAMP
|
|
300
642
|
HAVE_PRECISION = %w(DECIMAL NUMERIC)
|
|
301
643
|
HAVE_SCALE = %w(DECIMAL NUMERIC)
|
|
302
644
|
|
|
303
645
|
def columns(table_name, name = nil)
|
|
304
|
-
|
|
646
|
+
columns = @connection.columns_internal(table_name.to_s, nil, schema) # catalog == nil
|
|
305
647
|
|
|
648
|
+
if zos?
|
|
649
|
+
# Remove the mighty db2_generated_rowid_for_lobs from the list of columns
|
|
650
|
+
columns = columns.reject { |col| "db2_generated_rowid_for_lobs" == col.name }
|
|
651
|
+
end
|
|
306
652
|
# scrub out sizing info when CREATE TABLE doesn't support it
|
|
307
653
|
# but JDBC reports it (doh!)
|
|
308
|
-
for
|
|
309
|
-
base_sql_type =
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
#
|
|
654
|
+
for column in columns
|
|
655
|
+
base_sql_type = column.sql_type.sub(/\(.*/, "").upcase
|
|
656
|
+
column.limit = nil unless HAVE_LIMIT.include?(base_sql_type)
|
|
657
|
+
column.precision = nil unless HAVE_PRECISION.include?(base_sql_type)
|
|
658
|
+
#column.scale = nil unless HAVE_SCALE.include?(base_sql_type)
|
|
313
659
|
end
|
|
314
660
|
|
|
315
|
-
|
|
661
|
+
columns
|
|
316
662
|
end
|
|
317
663
|
|
|
318
664
|
def indexes(table_name, name = nil)
|
|
319
|
-
@connection.indexes(table_name, name,
|
|
665
|
+
@connection.indexes(table_name, name, schema)
|
|
320
666
|
end
|
|
321
667
|
|
|
322
|
-
def
|
|
323
|
-
|
|
324
|
-
%Q{"#{name}"}
|
|
668
|
+
def recreate_database(name = nil, options = {})
|
|
669
|
+
drop_database(name)
|
|
325
670
|
end
|
|
326
671
|
|
|
327
|
-
def
|
|
328
|
-
|
|
329
|
-
return str unless /^(["']).*\1$/ =~ str
|
|
330
|
-
str[1..-2]
|
|
672
|
+
def drop_database(name = nil)
|
|
673
|
+
tables.each { |table| drop_table("#{table}") }
|
|
331
674
|
end
|
|
332
675
|
|
|
333
|
-
def
|
|
334
|
-
|
|
335
|
-
name.gsub(/"/,'""')
|
|
676
|
+
def truncate(table_name, name = nil)
|
|
677
|
+
execute "TRUNCATE TABLE #{quote_table_name(table_name)} IMMEDIATE", name
|
|
336
678
|
end
|
|
337
679
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
rs = @connection.connection.meta_data.getTables(nil,db2_schema.upcase,nil,["TABLE"].to_java(:string))
|
|
341
|
-
while rs.next
|
|
342
|
-
tname = rs.getString(3)
|
|
343
|
-
definition << "CREATE TABLE #{tname} (\n"
|
|
344
|
-
rs2 = @connection.connection.meta_data.getColumns(nil,db2_schema.upcase,tname,nil)
|
|
345
|
-
first_col = true
|
|
346
|
-
while rs2.next
|
|
347
|
-
col_name = add_quotes(rs2.getString(4));
|
|
348
|
-
default = ""
|
|
349
|
-
d1 = rs2.getString(13)
|
|
350
|
-
# IBM i (as400 toolbox driver) will return an empty string if there is no default
|
|
351
|
-
if @config[:url] =~ /^jdbc:as400:/
|
|
352
|
-
default = !d1.blank? ? " DEFAULT #{d1}" : ""
|
|
353
|
-
else
|
|
354
|
-
default = d1 ? " DEFAULT #{d1}" : ""
|
|
355
|
-
end
|
|
680
|
+
# @override
|
|
681
|
+
def supports_views?; true end
|
|
356
682
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
elsif (HAVE_LIMIT + HAVE_PRECISION).include?(type) and col_precision
|
|
364
|
-
col_size = "(#{col_precision})"
|
|
365
|
-
end
|
|
366
|
-
nulling = (rs2.getString(18) == 'NO' ? " NOT NULL" : "")
|
|
367
|
-
create_col_string = add_quotes(expand_double_quotes(strip_quotes(col_name))) +
|
|
368
|
-
" " +
|
|
369
|
-
type +
|
|
370
|
-
col_size +
|
|
371
|
-
"" +
|
|
372
|
-
nulling +
|
|
373
|
-
default
|
|
374
|
-
if !first_col
|
|
375
|
-
create_col_string = ",\n #{create_col_string}"
|
|
376
|
-
else
|
|
377
|
-
create_col_string = " #{create_col_string}"
|
|
378
|
-
end
|
|
683
|
+
def execute_table_change(sql, table_name, name = nil)
|
|
684
|
+
outcome = execute(sql, name)
|
|
685
|
+
reorg_table(table_name, name)
|
|
686
|
+
outcome
|
|
687
|
+
end
|
|
688
|
+
protected :execute_table_change
|
|
379
689
|
|
|
380
|
-
|
|
690
|
+
def reorg_table(table_name, name = nil)
|
|
691
|
+
exec_update "call sysproc.admin_cmd ('REORG TABLE #{table_name}')", name, []
|
|
692
|
+
end
|
|
693
|
+
private :reorg_table
|
|
381
694
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
695
|
+
# alias_method :execute_and_auto_confirm, :execute
|
|
696
|
+
|
|
697
|
+
# Returns the value of an identity column of the last *INSERT* statement
|
|
698
|
+
# made over this connection.
|
|
699
|
+
# @note Check the *IDENTITY_VAL_LOCAL* function for documentation.
|
|
700
|
+
# @return [Fixnum]
|
|
701
|
+
def last_insert_id
|
|
702
|
+
@connection.identity_val_local
|
|
703
|
+
end
|
|
704
|
+
|
|
705
|
+
# NOTE: only setup query analysis on AR <= 3.0 since on 3.1 {#exec_query},
|
|
706
|
+
# {#exec_insert} will be used for AR generated queries/inserts etc.
|
|
707
|
+
# Also there's prepared statement support and {#execute} is meant to stay
|
|
708
|
+
# as a way of running non-prepared SQL statements (returning raw results).
|
|
709
|
+
if ActiveRecord::VERSION::MAJOR < 3 ||
|
|
710
|
+
( ActiveRecord::VERSION::MAJOR == 3 && ActiveRecord::VERSION::MINOR < 1 )
|
|
711
|
+
|
|
712
|
+
def _execute(sql, name = nil)
|
|
713
|
+
if self.class.select?(sql)
|
|
714
|
+
@connection.execute_query_raw(sql)
|
|
715
|
+
elsif self.class.insert?(sql)
|
|
716
|
+
@connection.execute_insert(sql) || last_insert_id
|
|
717
|
+
else
|
|
718
|
+
@connection.execute_update(sql)
|
|
385
719
|
end
|
|
386
|
-
definition
|
|
387
720
|
end
|
|
721
|
+
private :_execute
|
|
388
722
|
|
|
389
|
-
|
|
723
|
+
end
|
|
724
|
+
|
|
725
|
+
DRIVER_NAME = 'com.ibm.db2.jcc.DB2Driver'.freeze
|
|
726
|
+
|
|
727
|
+
# @private
|
|
728
|
+
def zos?
|
|
729
|
+
@zos = nil unless defined? @zos
|
|
730
|
+
return @zos unless @zos.nil?
|
|
731
|
+
@zos =
|
|
732
|
+
if url = config[:url]
|
|
733
|
+
!!( url =~ /^jdbc:db2j:net:/ && config[:driver] == DRIVER_NAME )
|
|
734
|
+
else
|
|
735
|
+
nil
|
|
736
|
+
end
|
|
737
|
+
end
|
|
738
|
+
|
|
739
|
+
# @private
|
|
740
|
+
# @deprecated no longer used
|
|
390
741
|
def as400?
|
|
391
|
-
|
|
742
|
+
false
|
|
743
|
+
end
|
|
744
|
+
|
|
745
|
+
def schema
|
|
746
|
+
db2_schema
|
|
747
|
+
end
|
|
748
|
+
|
|
749
|
+
def schema=(schema)
|
|
750
|
+
set_schema(@db2_schema = schema) if db2_schema != schema
|
|
751
|
+
end
|
|
752
|
+
|
|
753
|
+
private
|
|
754
|
+
|
|
755
|
+
def set_schema(schema)
|
|
756
|
+
execute("SET SCHEMA #{schema}")
|
|
392
757
|
end
|
|
393
758
|
|
|
394
759
|
def db2_schema
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
760
|
+
@db2_schema = false unless defined? @db2_schema
|
|
761
|
+
return @db2_schema if @db2_schema != false
|
|
762
|
+
schema = config[:schema]
|
|
763
|
+
@db2_schema =
|
|
764
|
+
if schema then schema
|
|
765
|
+
elsif config[:jndi] || config[:data_source]
|
|
766
|
+
nil # let JNDI worry about schema
|
|
400
767
|
else
|
|
401
768
|
# LUW implementation uses schema name of username by default
|
|
402
|
-
|
|
769
|
+
config[:username] || ENV['USER']
|
|
403
770
|
end
|
|
404
|
-
else
|
|
405
|
-
@config[:schema]
|
|
406
|
-
end
|
|
407
771
|
end
|
|
772
|
+
|
|
773
|
+
def db2_remove_column(table_name, column_name)
|
|
774
|
+
sql = "ALTER TABLE #{quote_table_name(table_name)} DROP COLUMN #{quote_column_name(column_name)}"
|
|
775
|
+
execute_table_change(sql, table_name, 'Remove Column')
|
|
776
|
+
end
|
|
777
|
+
|
|
778
|
+
end
|
|
779
|
+
end
|
|
780
|
+
|
|
781
|
+
module ActiveRecord::ConnectionAdapters
|
|
782
|
+
|
|
783
|
+
remove_const(:DB2Column) if const_defined?(:DB2Column)
|
|
784
|
+
|
|
785
|
+
class DB2Column < JdbcColumn
|
|
786
|
+
include ::ArJdbc::DB2::Column
|
|
408
787
|
end
|
|
788
|
+
|
|
409
789
|
end
|