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
|
@@ -1,27 +1,163 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
ArJdbc::ConnectionMethods.module_eval do
|
|
3
|
+
def mysql_connection(config)
|
|
4
|
+
# NOTE: this isn't "really" necessary but Rails (in tests) assumes being able to :
|
|
5
|
+
# ActiveRecord::Base.mysql2_connection ActiveRecord::Base.configurations['arunit'].merge(database: ...)
|
|
6
|
+
config = symbolize_keys_if_necessary(config)
|
|
7
|
+
|
|
8
|
+
config[:adapter_spec] ||= ::ArJdbc::MySQL
|
|
9
|
+
config[:adapter_class] = ActiveRecord::ConnectionAdapters::Mysql2Adapter unless config.key?(:adapter_class)
|
|
10
|
+
|
|
11
|
+
return jndi_connection(config) if jndi_config?(config)
|
|
12
|
+
|
|
13
|
+
driver = config[:driver] ||=
|
|
14
|
+
defined?(::Jdbc::MySQL.driver_name) ? ::Jdbc::MySQL.driver_name : 'com.mysql.jdbc.Driver'
|
|
15
|
+
|
|
16
|
+
begin
|
|
17
|
+
require 'jdbc/mysql'
|
|
18
|
+
::Jdbc::MySQL.load_driver(:require) if defined?(::Jdbc::MySQL.load_driver)
|
|
19
|
+
rescue LoadError # assuming driver.jar is on the class-path
|
|
20
|
+
end if mysql_driver = driver[0, 10] == 'com.mysql.'
|
|
21
|
+
|
|
22
|
+
config[:username] = 'root' unless config.key?(:username)
|
|
23
|
+
# jdbc:mysql://[host][,failoverhost...][:port]/[database]
|
|
24
|
+
# - if the host name is not specified, it defaults to 127.0.0.1
|
|
25
|
+
# - if the port is not specified, it defaults to 3306
|
|
26
|
+
# - alternate fail-over syntax: [host:port],[host:port]/[database]
|
|
27
|
+
unless config[:url]
|
|
28
|
+
host = config[:host]; host = host.join(',') if host.respond_to?(:join)
|
|
29
|
+
config[:url] = "jdbc:mysql://#{host}#{ config[:port] ? ":#{config[:port]}" : nil }/#{config[:database]}"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
mariadb_driver = ! mysql_driver && driver.start_with?('org.mariadb.')
|
|
33
|
+
|
|
34
|
+
properties = ( config[:properties] ||= {} )
|
|
35
|
+
if mysql_driver
|
|
36
|
+
properties['zeroDateTimeBehavior'] ||= 'convertToNull'
|
|
37
|
+
properties['jdbcCompliantTruncation'] ||= false
|
|
38
|
+
# NOTE: this is "better" than passing what users are used to set on MRI
|
|
39
|
+
# e.g. 'utf8mb4' will fail cause the driver will check for a Java charset
|
|
40
|
+
# ... it's smart enough to detect utf8mb4 from server variables :
|
|
41
|
+
# "character_set_client" && "character_set_connection" (thus UTF-8)
|
|
42
|
+
if encoding = config.key?(:encoding) ? config[:encoding] : 'utf8'
|
|
43
|
+
charset_name = convert_mysql_encoding(encoding)
|
|
44
|
+
if charset_name.eql?(false) # do not set characterEncoding
|
|
45
|
+
properties['character_set_server'] = encoding
|
|
46
|
+
else
|
|
47
|
+
properties['characterEncoding'] = charset_name || encoding
|
|
48
|
+
end
|
|
49
|
+
# driver also executes: "SET NAMES " + (useutf8mb4 ? "utf8mb4" : "utf8")
|
|
50
|
+
# thus no need to do it on configure_connection :
|
|
51
|
+
config[:encoding] = nil if config.key?(:encoding)
|
|
52
|
+
end
|
|
53
|
+
# properties['useUnicode'] is true by default
|
|
54
|
+
if collation = config[:collation]
|
|
55
|
+
properties['connectionCollation'] = collation
|
|
56
|
+
end
|
|
57
|
+
if ! ( reconnect = config[:reconnect] ).nil?
|
|
58
|
+
properties['autoReconnect'] ||= reconnect.to_s
|
|
59
|
+
# properties['maxReconnects'] ||= '3'
|
|
60
|
+
# with reconnect fail-over sets connection read-only (by default)
|
|
61
|
+
# properties['failOverReadOnly'] ||= 'false'
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
if config[:sslkey] || sslcert = config[:sslcert] # || config[:use_ssl]
|
|
65
|
+
properties['useSSL'] ||= true # supported by MariaDB as well
|
|
66
|
+
properties['requireSSL'] ||= true if mysql_driver
|
|
67
|
+
if mysql_driver
|
|
68
|
+
properties['clientCertificateKeyStoreUrl'] ||= java.io.File.new(sslcert).to_url.to_s if sslcert
|
|
69
|
+
if sslca = config[:sslca]
|
|
70
|
+
properties['trustCertificateKeyStoreUrl'] ||= java.io.File.new(sslca).to_url.to_s
|
|
71
|
+
else
|
|
72
|
+
properties['verifyServerCertificate'] ||= false
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
properties['verifyServerCertificate'] ||= false if mariadb_driver
|
|
76
|
+
else
|
|
77
|
+
# According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection
|
|
78
|
+
# must be established by default if explicit option isn't set :
|
|
79
|
+
properties[mariadb_driver ? 'useSsl' : 'useSSL'] ||= false
|
|
80
|
+
end
|
|
81
|
+
if socket = config[:socket]
|
|
82
|
+
properties['localSocket'] ||= socket if mariadb_driver
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# for the Connector/J 5.1 line this is true by default - but it requires some really nasty
|
|
86
|
+
# quirks to get casted Time values extracted properly according for AR's default_timezone
|
|
87
|
+
# - thus we're turning it off (should be off in newer driver versions >= 6 anyway)
|
|
88
|
+
# + also MariaDB driver is compilant and we would need to branch out based on driver
|
|
89
|
+
properties['useLegacyDatetimeCode'] = false
|
|
90
|
+
|
|
91
|
+
jdbc_connection(config)
|
|
92
|
+
end
|
|
93
|
+
alias_method :jdbcmysql_connection, :mysql_connection
|
|
94
|
+
alias_method :mysql2_connection, :mysql_connection
|
|
95
|
+
|
|
96
|
+
def mariadb_connection(config)
|
|
97
|
+
config[:adapter_spec] ||= ::ArJdbc::MySQL
|
|
98
|
+
config[:adapter_class] = ActiveRecord::ConnectionAdapters::Mysql2Adapter unless config.key?(:adapter_class)
|
|
99
|
+
|
|
100
|
+
return jndi_connection(config) if jndi_config?(config)
|
|
101
|
+
|
|
102
|
+
begin
|
|
103
|
+
require 'jdbc/mariadb'
|
|
104
|
+
::Jdbc::MariaDB.load_driver(:require) if defined?(::Jdbc::MariaDB.load_driver)
|
|
105
|
+
rescue LoadError # assuming driver.jar is on the class-path
|
|
21
106
|
end
|
|
22
|
-
|
|
23
|
-
|
|
107
|
+
|
|
108
|
+
config[:driver] ||= 'org.mariadb.jdbc.Driver'
|
|
109
|
+
|
|
110
|
+
mysql_connection(config)
|
|
24
111
|
end
|
|
25
|
-
|
|
112
|
+
alias_method :jdbcmariadb_connection, :mariadb_connection
|
|
113
|
+
|
|
114
|
+
private
|
|
26
115
|
|
|
116
|
+
@@mysql_encodings = nil
|
|
27
117
|
|
|
118
|
+
# @see https://dev.mysql.com/doc/connector-j/5.1/en/connector-j-reference-charsets.html
|
|
119
|
+
def convert_mysql_encoding(encoding) # to charset-name (characterEncoding=...)
|
|
120
|
+
( @@mysql_encodings ||= {
|
|
121
|
+
"big5" => "Big5",
|
|
122
|
+
"dec8" => nil,
|
|
123
|
+
#"cp850" => "Cp850",
|
|
124
|
+
"hp8" => nil,
|
|
125
|
+
#"koi8r" => "KOI8-R",
|
|
126
|
+
"latin1" => "Cp1252",
|
|
127
|
+
"latin2" => "ISO8859_2",
|
|
128
|
+
"swe7" => nil,
|
|
129
|
+
"ascii" => "US-ASCII",
|
|
130
|
+
"ujis" => "EUC_JP",
|
|
131
|
+
"sjis" => "SJIS",
|
|
132
|
+
"hebrew" => "ISO8859_8",
|
|
133
|
+
"tis620" => "TIS620",
|
|
134
|
+
"euckr" => "EUC_KR",
|
|
135
|
+
#"koi8u" => "KOI8-R",
|
|
136
|
+
"gb2312" => "EUC_CN",
|
|
137
|
+
"greek" => "ISO8859_7",
|
|
138
|
+
"cp1250" => "Cp1250",
|
|
139
|
+
"gbk" => "GBK",
|
|
140
|
+
#"latin5" => "ISO-8859-9",
|
|
141
|
+
"armscii8" => nil,
|
|
142
|
+
"ucs2" => "UnicodeBig",
|
|
143
|
+
"cp866" => "Cp866",
|
|
144
|
+
"keybcs2" => nil,
|
|
145
|
+
"macce" => "MacCentralEurope",
|
|
146
|
+
"macroman" => "MacRoman",
|
|
147
|
+
#"cp852" => "CP852",
|
|
148
|
+
#"latin7" => "ISO-8859-13",
|
|
149
|
+
"cp1251" => "Cp1251",
|
|
150
|
+
"cp1256" => "Cp1256",
|
|
151
|
+
"cp1257" => "Cp1257",
|
|
152
|
+
"binary" => false,
|
|
153
|
+
"geostd8" => nil,
|
|
154
|
+
"cp932" => "Cp932",
|
|
155
|
+
#"eucjpms" => "eucJP-ms"
|
|
156
|
+
"utf8" => "UTF-8",
|
|
157
|
+
"utf8mb4" => false,
|
|
158
|
+
"utf16" => false,
|
|
159
|
+
"utf32" => false,
|
|
160
|
+
} )[ encoding ]
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
end
|
|
@@ -1,232 +1,345 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
# NOTE: file contains code adapted from **oracle-enhanced** adapter, license follows
|
|
2
|
+
=begin
|
|
3
|
+
Copyright (c) 2008-2011 Graham Jenkins, Michael Schoen, Raimonds Simanovskis
|
|
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 :Oracle
|
|
26
|
+
|
|
27
|
+
module ArJdbc
|
|
6
28
|
module Oracle
|
|
7
|
-
def self.extended(mod)
|
|
8
|
-
unless @lob_callback_added
|
|
9
|
-
ActiveRecord::Base.class_eval do
|
|
10
|
-
def after_save_with_oracle_lob
|
|
11
|
-
self.class.columns.select { |c| c.sql_type =~ /LOB\(|LOB$/i }.each do |c|
|
|
12
|
-
value = self[c.name]
|
|
13
|
-
value = value.to_yaml if unserializable_attribute?(c.name, c)
|
|
14
|
-
next if value.nil? || (value == '')
|
|
15
|
-
|
|
16
|
-
connection.write_large_object(c.type == :binary, c.name, self.class.table_name, self.class.primary_key, quote_value(id), value)
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
29
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
alias_chained_method :columns, :query_cache, :columns
|
|
29
|
-
end
|
|
30
|
-
end
|
|
30
|
+
require 'arjdbc/oracle/column'
|
|
31
|
+
|
|
32
|
+
# @private
|
|
33
|
+
def self.extended(adapter); initialize!; end
|
|
34
|
+
|
|
35
|
+
# @private
|
|
36
|
+
@@_initialized = nil
|
|
31
37
|
|
|
32
|
-
|
|
33
|
-
|
|
38
|
+
# @private
|
|
39
|
+
def self.initialize!
|
|
40
|
+
return if @@_initialized; @@_initialized = true
|
|
41
|
+
|
|
42
|
+
require 'arjdbc/util/serialized_attributes'
|
|
43
|
+
Util::SerializedAttributes.setup /LOB\(|LOB$/i, 'after_save_with_oracle_lob'
|
|
44
|
+
|
|
45
|
+
unless ActiveRecord::ConnectionAdapters::AbstractAdapter.
|
|
46
|
+
instance_methods(false).detect { |m| m.to_s == "prefetch_primary_key?" }
|
|
47
|
+
require 'arjdbc/jdbc/quoted_primary_key'
|
|
48
|
+
ActiveRecord::Base.extend ArJdbc::QuotedPrimaryKeyExtension
|
|
49
|
+
end
|
|
34
50
|
end
|
|
35
51
|
|
|
52
|
+
# @see ActiveRecord::ConnectionAdapters::JdbcAdapter#jdbc_connection_class
|
|
36
53
|
def self.jdbc_connection_class
|
|
37
54
|
::ActiveRecord::ConnectionAdapters::OracleJdbcConnection
|
|
38
55
|
end
|
|
39
56
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
super
|
|
43
|
-
if val && @sql_type =~ /^NUMBER$/i
|
|
44
|
-
@type = :integer
|
|
45
|
-
end
|
|
46
|
-
end
|
|
57
|
+
# @see ActiveRecord::ConnectionAdapters::JdbcAdapter#jdbc_column_class
|
|
58
|
+
def jdbc_column_class; ::ActiveRecord::ConnectionAdapters::OracleColumn end
|
|
47
59
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
case type
|
|
51
|
-
when :datetime then ArJdbc::Oracle::Column.string_to_time(value, self.class)
|
|
52
|
-
else
|
|
53
|
-
super
|
|
54
|
-
end
|
|
55
|
-
end
|
|
60
|
+
# @private
|
|
61
|
+
@@update_lob_values = true
|
|
56
62
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
63
|
+
# Updating records with LOB values (binary/text columns) in a separate
|
|
64
|
+
# statement can be disabled using :
|
|
65
|
+
#
|
|
66
|
+
# ArJdbc::Oracle.update_lob_values = false
|
|
67
|
+
#
|
|
68
|
+
# @note This only applies when prepared statements are not used.
|
|
69
|
+
def self.update_lob_values?; @@update_lob_values; end
|
|
70
|
+
# @see #update_lob_values?
|
|
71
|
+
def self.update_lob_values=(update); @@update_lob_values = update; end
|
|
64
72
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
73
|
+
# @see #update_lob_values?
|
|
74
|
+
# @see ArJdbc::Util::SerializedAttributes#update_lob_columns
|
|
75
|
+
def update_lob_value?(value, column = nil)
|
|
76
|
+
Oracle.update_lob_values? && ! prepared_statements? && ! ( value.nil? || value == '' )
|
|
77
|
+
end
|
|
69
78
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
Date.new(value.year, value.month, value.day) : value
|
|
73
|
-
end
|
|
79
|
+
# @private
|
|
80
|
+
@@emulate_booleans = true
|
|
74
81
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
82
|
+
# Boolean emulation can be disabled using :
|
|
83
|
+
#
|
|
84
|
+
# ArJdbc::Oracle.emulate_booleans = false
|
|
85
|
+
#
|
|
86
|
+
# @see ActiveRecord::ConnectionAdapters::OracleAdapter#emulate_booleans
|
|
87
|
+
def self.emulate_booleans?; @@emulate_booleans; end
|
|
88
|
+
# @deprecated Use {#emulate_booleans?} instead.
|
|
89
|
+
def self.emulate_booleans; @@emulate_booleans; end
|
|
90
|
+
# @see #emulate_booleans?
|
|
91
|
+
def self.emulate_booleans=(emulate); @@emulate_booleans = emulate; end
|
|
92
|
+
|
|
93
|
+
class TableDefinition < ::ActiveRecord::ConnectionAdapters::TableDefinition
|
|
94
|
+
def raw(*args)
|
|
95
|
+
options = args.extract_options!
|
|
96
|
+
column(args[0], 'raw', options)
|
|
87
97
|
end
|
|
88
98
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
99
|
+
def xml(*args)
|
|
100
|
+
options = args.extract_options!
|
|
101
|
+
column(args[0], 'xml', options)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
92
104
|
|
|
93
|
-
|
|
94
|
-
|
|
105
|
+
def table_definition(*args)
|
|
106
|
+
new_table_definition(TableDefinition, *args)
|
|
107
|
+
end
|
|
95
108
|
|
|
96
|
-
|
|
109
|
+
def self.arel_visitor_type(config = nil)
|
|
110
|
+
::Arel::Visitors::Oracle
|
|
111
|
+
end
|
|
97
112
|
|
|
98
|
-
|
|
99
|
-
|
|
113
|
+
# @see ActiveRecord::ConnectionAdapters::JdbcAdapter#bind_substitution
|
|
114
|
+
# @private
|
|
115
|
+
class BindSubstitution < ::Arel::Visitors::Oracle
|
|
116
|
+
include ::Arel::Visitors::BindVisitor
|
|
117
|
+
end if defined? ::Arel::Visitors::BindVisitor
|
|
100
118
|
|
|
101
|
-
|
|
102
|
-
return $1 if value =~ /^'(.*)'$/
|
|
119
|
+
ADAPTER_NAME = 'Oracle'.freeze
|
|
103
120
|
|
|
104
|
-
|
|
121
|
+
def adapter_name
|
|
122
|
+
ADAPTER_NAME
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
NATIVE_DATABASE_TYPES = {
|
|
126
|
+
:primary_key => "NUMBER(38) NOT NULL PRIMARY KEY",
|
|
127
|
+
:string => { :name => "VARCHAR2", :limit => 255 },
|
|
128
|
+
:text => { :name => "CLOB" },
|
|
129
|
+
:integer => { :name => "NUMBER", :limit => 38 },
|
|
130
|
+
:float => { :name => "NUMBER" },
|
|
131
|
+
:decimal => { :name => "DECIMAL" },
|
|
132
|
+
:datetime => { :name => "DATE" },
|
|
133
|
+
:timestamp => { :name => "TIMESTAMP" },
|
|
134
|
+
:time => { :name => "DATE" },
|
|
135
|
+
:date => { :name => "DATE" },
|
|
136
|
+
:binary => { :name => "BLOB" },
|
|
137
|
+
:boolean => { :name => "NUMBER", :limit => 1 },
|
|
138
|
+
:raw => { :name => "RAW", :limit => 2000 },
|
|
139
|
+
:xml => { :name => 'XMLTYPE' }
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
def native_database_types
|
|
143
|
+
super.merge(NATIVE_DATABASE_TYPES)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def modify_types(types)
|
|
147
|
+
super(types)
|
|
148
|
+
NATIVE_DATABASE_TYPES.each do |key, value|
|
|
149
|
+
types[key] = value.dup
|
|
105
150
|
end
|
|
151
|
+
types
|
|
106
152
|
end
|
|
107
153
|
|
|
108
|
-
|
|
109
|
-
|
|
154
|
+
# Prevent ORA-01795 for in clauses with more than 1000
|
|
155
|
+
def in_clause_length
|
|
156
|
+
1000
|
|
110
157
|
end
|
|
158
|
+
alias_method :ids_in_list_limit, :in_clause_length
|
|
111
159
|
|
|
112
|
-
|
|
113
|
-
{ 'oracle' => Arel::Visitors::Oracle }
|
|
114
|
-
end
|
|
160
|
+
IDENTIFIER_LENGTH = 30
|
|
115
161
|
|
|
116
|
-
#
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
162
|
+
# maximum length of Oracle identifiers is 30
|
|
163
|
+
def table_alias_length; IDENTIFIER_LENGTH; end
|
|
164
|
+
def table_name_length; IDENTIFIER_LENGTH; end
|
|
165
|
+
def index_name_length; IDENTIFIER_LENGTH; end
|
|
166
|
+
def column_name_length; IDENTIFIER_LENGTH; end
|
|
167
|
+
def sequence_name_length; IDENTIFIER_LENGTH end
|
|
120
168
|
|
|
121
|
-
|
|
122
|
-
|
|
169
|
+
# @private
|
|
170
|
+
# Will take all or first 26 characters of table name and append _seq suffix
|
|
171
|
+
def default_sequence_name(table_name, primary_key = nil)
|
|
172
|
+
len = IDENTIFIER_LENGTH - 4
|
|
173
|
+
table_name.to_s.gsub (/(^|\.)([\w$-]{1,#{len}})([\w$-]*)$/), '\1\2_seq'
|
|
123
174
|
end
|
|
124
175
|
|
|
125
|
-
|
|
126
|
-
|
|
176
|
+
# @private
|
|
177
|
+
def default_trigger_name(table_name)
|
|
178
|
+
"#{table_name.to_s[0, IDENTIFIER_LENGTH - 4]}_pkt"
|
|
127
179
|
end
|
|
128
180
|
|
|
129
|
-
|
|
181
|
+
# @override
|
|
182
|
+
def create_table(name, options = {})
|
|
130
183
|
super(name, options)
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
184
|
+
unless options[:id] == false
|
|
185
|
+
seq_name = options[:sequence_name] || default_sequence_name(name)
|
|
186
|
+
start_value = options[:sequence_start_value] || 10000
|
|
187
|
+
raise ActiveRecord::StatementInvalid.new("name #{seq_name} too long") if seq_name.length > table_alias_length
|
|
188
|
+
execute "CREATE SEQUENCE #{quote_table_name(seq_name)} START WITH #{start_value}"
|
|
189
|
+
end
|
|
135
190
|
end
|
|
136
191
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
192
|
+
# @override
|
|
193
|
+
def rename_table(name, new_name)
|
|
194
|
+
if new_name.to_s.length > table_name_length
|
|
195
|
+
raise ArgumentError, "New table name '#{new_name}' is too long; the limit is #{table_name_length} characters"
|
|
196
|
+
end
|
|
197
|
+
if "#{new_name}_seq".to_s.length > sequence_name_length
|
|
198
|
+
raise ArgumentError, "New sequence name '#{new_name}_seq' is too long; the limit is #{sequence_name_length} characters"
|
|
199
|
+
end
|
|
200
|
+
execute "RENAME #{quote_table_name(name)} TO #{quote_table_name(new_name)}"
|
|
201
|
+
execute "RENAME #{quote_table_name("#{name}_seq")} TO #{quote_table_name("#{new_name}_seq")}" rescue nil
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# @override
|
|
205
|
+
def drop_table(name, options = {})
|
|
206
|
+
outcome = super(name)
|
|
207
|
+
return outcome if name == 'schema_migrations'
|
|
208
|
+
seq_name = options.key?(:sequence_name) ? # pass nil/false - no sequence
|
|
209
|
+
options[:sequence_name] : default_sequence_name(name)
|
|
210
|
+
return outcome unless seq_name
|
|
211
|
+
execute "DROP SEQUENCE #{quote_table_name(seq_name)}" rescue nil
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# @override
|
|
215
|
+
def type_to_sql(type, limit = nil, precision = nil, scale = nil)
|
|
216
|
+
case type.to_sym
|
|
217
|
+
when :binary
|
|
218
|
+
# { BLOB | BINARY LARGE OBJECT } [ ( length [{K |M |G }] ) ]
|
|
219
|
+
# although Oracle does not like limit (length) with BLOB (or CLOB) :
|
|
220
|
+
#
|
|
221
|
+
# CREATE TABLE binaries (data BLOB, short_data BLOB(1024));
|
|
222
|
+
# ORA-00907: missing right parenthesis *
|
|
223
|
+
#
|
|
224
|
+
# TODO do we need to worry about NORMAL vs. non IN-TABLE BLOBs ?!
|
|
225
|
+
# http://dba.stackexchange.com/questions/8770/improve-blob-writing-performance-in-oracle-11g
|
|
226
|
+
# - if the LOB is smaller than 3900 bytes it can be stored inside the
|
|
227
|
+
# table row; by default this is enabled,
|
|
228
|
+
# unless you specify DISABLE STORAGE IN ROW
|
|
229
|
+
# - normal LOB - stored in a separate segment, outside of table,
|
|
230
|
+
# you may even put it in another tablespace;
|
|
231
|
+
super(type, nil, nil, nil)
|
|
232
|
+
when :text
|
|
233
|
+
super(type, nil, nil, nil)
|
|
234
|
+
else
|
|
235
|
+
super
|
|
236
|
+
end
|
|
140
237
|
end
|
|
141
238
|
|
|
142
|
-
def
|
|
143
|
-
|
|
144
|
-
seq_name = options[:sequence_name] || "#{name}_seq"
|
|
145
|
-
execute "DROP SEQUENCE #{seq_name}" rescue nil
|
|
239
|
+
def indexes(table, name = nil)
|
|
240
|
+
@connection.indexes(table, name, @connection.connection.meta_data.user_name)
|
|
146
241
|
end
|
|
147
242
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
243
|
+
# @note Only used with (non-AREL) ActiveRecord **2.3**.
|
|
244
|
+
# @see Arel::Visitors::Oracle
|
|
245
|
+
def add_limit_offset!(sql, options)
|
|
246
|
+
offset = options[:offset] || 0
|
|
247
|
+
if limit = options[:limit]
|
|
248
|
+
sql.replace "SELECT * FROM " <<
|
|
249
|
+
"(select raw_sql_.*, rownum raw_rnum_ from (#{sql}) raw_sql_ where rownum <= #{offset + limit})" <<
|
|
250
|
+
" WHERE raw_rnum_ > #{offset}"
|
|
251
|
+
elsif offset > 0
|
|
252
|
+
sql.replace "SELECT * FROM " <<
|
|
253
|
+
"(select raw_sql_.*, rownum raw_rnum_ from (#{sql}) raw_sql_)" <<
|
|
254
|
+
" WHERE raw_rnum_ > #{offset}"
|
|
255
|
+
end
|
|
256
|
+
end if ::ActiveRecord::VERSION::MAJOR < 3
|
|
151
257
|
|
|
152
|
-
def
|
|
153
|
-
|
|
258
|
+
def current_user
|
|
259
|
+
@current_user ||= execute("SELECT sys_context('userenv', 'session_user') su FROM dual").first['su']
|
|
154
260
|
end
|
|
155
261
|
|
|
156
|
-
def
|
|
157
|
-
|
|
158
|
-
execute("select #{sequence_name}.nextval id from dual").first['id'].to_i
|
|
262
|
+
def current_database
|
|
263
|
+
@current_database ||= execute("SELECT sys_context('userenv', 'db_name') db FROM dual").first['db']
|
|
159
264
|
end
|
|
160
265
|
|
|
161
|
-
def
|
|
162
|
-
|
|
266
|
+
def current_schema
|
|
267
|
+
execute("SELECT sys_context('userenv', 'current_schema') schema FROM dual").first['schema']
|
|
163
268
|
end
|
|
164
269
|
|
|
165
|
-
def
|
|
166
|
-
|
|
167
|
-
# Pre-assigned id or table without a primary key
|
|
168
|
-
# Presence of #to_sql means an Arel literal bind variable
|
|
169
|
-
# that should use #execute_id_insert below
|
|
170
|
-
execute sql, name
|
|
171
|
-
else
|
|
172
|
-
# Assume the sql contains a bind-variable for the id
|
|
173
|
-
# Extract the table from the insert sql. Yuck.
|
|
174
|
-
table = sql.split(" ", 4)[2].gsub('"', '')
|
|
175
|
-
sequence_name ||= default_sequence_name(table)
|
|
176
|
-
id_value = next_sequence_value(sequence_name)
|
|
177
|
-
log(sql, name) do
|
|
178
|
-
@connection.execute_id_insert(sql,id_value)
|
|
179
|
-
end
|
|
180
|
-
end
|
|
181
|
-
id_value
|
|
270
|
+
def current_schema=(schema_owner)
|
|
271
|
+
execute("ALTER SESSION SET current_schema=#{schema_owner}")
|
|
182
272
|
end
|
|
183
273
|
|
|
184
|
-
|
|
185
|
-
|
|
274
|
+
# @override
|
|
275
|
+
def release_savepoint(name = nil)
|
|
276
|
+
# no RELEASE SAVEPOINT statement in Oracle (JDBC driver throws "Unsupported feature")
|
|
186
277
|
end
|
|
187
278
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
279
|
+
# @override
|
|
280
|
+
def add_index(table_name, column_name, options = {})
|
|
281
|
+
index_name, index_type, quoted_column_names, tablespace, index_options = add_index_options(table_name, column_name, options)
|
|
282
|
+
execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{quoted_column_names})#{tablespace} #{index_options}"
|
|
283
|
+
if index_type == 'UNIQUE'
|
|
284
|
+
unless quoted_column_names =~ /\(.*\)/
|
|
285
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{quote_column_name(index_name)} #{index_type} (#{quoted_column_names})"
|
|
194
286
|
end
|
|
195
|
-
|
|
287
|
+
end
|
|
288
|
+
end if AR42
|
|
196
289
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
tp[:timestamp] = { :name => "DATE" }
|
|
202
|
-
tp[:time] = { :name => "DATE" }
|
|
203
|
-
tp[:date] = { :name => "DATE" }
|
|
204
|
-
tp
|
|
205
|
-
end
|
|
290
|
+
# @private
|
|
291
|
+
def add_index_options(table_name, column_name, options = {})
|
|
292
|
+
column_names = Array(column_name)
|
|
293
|
+
index_name = index_name(table_name, column: column_names)
|
|
206
294
|
|
|
207
|
-
|
|
208
|
-
offset = options[:offset] || 0
|
|
295
|
+
options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :tablespace, :options, :using)
|
|
209
296
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
297
|
+
index_type = options[:unique] ? "UNIQUE" : ""
|
|
298
|
+
index_name = options[:name].to_s if options.key?(:name)
|
|
299
|
+
tablespace = '' # tablespace_for(:index, options[:tablespace])
|
|
300
|
+
max_index_length = options.fetch(:internal, false) ? index_name_length : allowed_index_name_length
|
|
301
|
+
index_options = '' # index_options = options[:options]
|
|
302
|
+
|
|
303
|
+
if index_name.to_s.length > max_index_length
|
|
304
|
+
raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{max_index_length} characters"
|
|
305
|
+
end
|
|
306
|
+
if index_name_exists?(table_name, index_name, false)
|
|
307
|
+
raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists"
|
|
214
308
|
end
|
|
215
|
-
end
|
|
216
309
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
end
|
|
310
|
+
quoted_column_names = column_names.map { |e| quote_column_name_or_expression(e) }.join(", ")
|
|
311
|
+
[ index_name, index_type, quoted_column_names, tablespace, index_options ]
|
|
312
|
+
end if AR42
|
|
313
|
+
|
|
314
|
+
# @override
|
|
315
|
+
def remove_index(table_name, options = {})
|
|
316
|
+
index_name = index_name(table_name, options)
|
|
317
|
+
unless index_name_exists?(table_name, index_name, true)
|
|
318
|
+
# sometimes options can be String or Array with column names
|
|
319
|
+
options = {} unless options.is_a?(Hash)
|
|
320
|
+
if options.has_key? :name
|
|
321
|
+
options_without_column = options.dup
|
|
322
|
+
options_without_column.delete :column
|
|
323
|
+
index_name_without_column = index_name(table_name, options_without_column)
|
|
324
|
+
return index_name_without_column if index_name_exists?(table_name, index_name_without_column, false)
|
|
325
|
+
end
|
|
326
|
+
raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' does not exist"
|
|
327
|
+
end
|
|
328
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} DROP CONSTRAINT #{quote_column_name(index_name)}" rescue nil
|
|
329
|
+
execute "DROP INDEX #{quote_column_name(index_name)}"
|
|
330
|
+
end if AR42
|
|
220
331
|
|
|
221
|
-
|
|
332
|
+
# @private
|
|
333
|
+
def remove_index(table_name, options = {})
|
|
222
334
|
execute "DROP INDEX #{index_name(table_name, options)}"
|
|
223
|
-
end
|
|
335
|
+
end unless AR42
|
|
224
336
|
|
|
225
|
-
def change_column_default(table_name, column_name, default)
|
|
226
|
-
execute "ALTER TABLE #{table_name} MODIFY #{column_name} DEFAULT #{quote(default)}"
|
|
337
|
+
def change_column_default(table_name, column_name, default)
|
|
338
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} MODIFY #{quote_column_name(column_name)} DEFAULT #{quote(default)}"
|
|
227
339
|
end
|
|
228
340
|
|
|
229
|
-
|
|
341
|
+
# @override
|
|
342
|
+
def add_column_options!(sql, options)
|
|
230
343
|
# handle case of defaults for CLOB columns, which would otherwise get "quoted" incorrectly
|
|
231
344
|
if options_include_default?(options) && (column = options[:column]) && column.type == :text
|
|
232
345
|
sql << " DEFAULT #{quote(options.delete(:default))}"
|
|
@@ -234,64 +347,43 @@ module ::ArJdbc
|
|
|
234
347
|
super
|
|
235
348
|
end
|
|
236
349
|
|
|
237
|
-
|
|
238
|
-
|
|
350
|
+
# @override
|
|
351
|
+
def change_column(table_name, column_name, type, options = {})
|
|
352
|
+
change_column_sql = "ALTER TABLE #{quote_table_name(table_name)} " <<
|
|
353
|
+
"MODIFY #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit])}"
|
|
239
354
|
add_column_options!(change_column_sql, options)
|
|
240
355
|
execute(change_column_sql)
|
|
241
356
|
end
|
|
242
357
|
|
|
243
|
-
|
|
244
|
-
|
|
358
|
+
# @override
|
|
359
|
+
def rename_column(table_name, column_name, new_column_name)
|
|
360
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} " <<
|
|
361
|
+
"RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
|
|
245
362
|
end
|
|
246
363
|
|
|
247
|
-
|
|
248
|
-
|
|
364
|
+
if ActiveRecord::VERSION::MAJOR >= 4
|
|
365
|
+
|
|
366
|
+
# @override
|
|
367
|
+
def remove_column(table_name, column_name, type = nil, options = {})
|
|
368
|
+
do_remove_column(table_name, column_name)
|
|
249
369
|
end
|
|
250
370
|
|
|
251
|
-
|
|
252
|
-
s = select_all("select sequence_name from user_sequences").inject("") do |structure, seq|
|
|
253
|
-
structure << "create sequence #{seq.to_a.first.last};\n\n"
|
|
254
|
-
end
|
|
371
|
+
else
|
|
255
372
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
from user_tab_columns
|
|
261
|
-
where table_name = '#{table.to_a.first.last}'
|
|
262
|
-
order by column_id
|
|
263
|
-
}).map do |row|
|
|
264
|
-
row = row.inject({}) do |h,args|
|
|
265
|
-
h[args[0].downcase] = args[1]
|
|
266
|
-
h
|
|
267
|
-
end
|
|
268
|
-
col = "#{row['column_name'].downcase} #{row['data_type'].downcase}"
|
|
269
|
-
if row['data_type'] =='NUMBER' and !row['data_precision'].nil?
|
|
270
|
-
col << "(#{row['data_precision'].to_i}"
|
|
271
|
-
col << ",#{row['data_scale'].to_i}" if !row['data_scale'].nil?
|
|
272
|
-
col << ')'
|
|
273
|
-
elsif row['data_type'].include?('CHAR')
|
|
274
|
-
col << "(#{row['data_length'].to_i})"
|
|
275
|
-
end
|
|
276
|
-
col << " default #{row['data_default']}" if !row['data_default'].nil?
|
|
277
|
-
col << ' not null' if row['nullable'] == 'N'
|
|
278
|
-
col
|
|
279
|
-
end
|
|
280
|
-
ddl << cols.join(",\n ")
|
|
281
|
-
ddl << ");\n\n"
|
|
282
|
-
structure << ddl
|
|
373
|
+
# @override
|
|
374
|
+
def remove_column(table_name, *column_names)
|
|
375
|
+
for column_name in column_names.flatten
|
|
376
|
+
do_remove_column(table_name, column_name)
|
|
283
377
|
end
|
|
284
378
|
end
|
|
379
|
+
alias remove_columns remove_column
|
|
285
380
|
|
|
286
|
-
|
|
287
|
-
s = select_all("select sequence_name from user_sequences").inject("") do |drop, seq|
|
|
288
|
-
drop << "drop sequence #{seq.to_a.first.last};\n\n"
|
|
289
|
-
end
|
|
381
|
+
end
|
|
290
382
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
end
|
|
383
|
+
def do_remove_column(table_name, column_name)
|
|
384
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} DROP COLUMN #{quote_column_name(column_name)}"
|
|
294
385
|
end
|
|
386
|
+
private :do_remove_column
|
|
295
387
|
|
|
296
388
|
# SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
|
|
297
389
|
#
|
|
@@ -303,110 +395,469 @@ module ::ArJdbc
|
|
|
303
395
|
# making every row the same.
|
|
304
396
|
#
|
|
305
397
|
# distinct("posts.id", "posts.created_at desc")
|
|
398
|
+
#
|
|
399
|
+
# @override
|
|
306
400
|
def distinct(columns, order_by)
|
|
307
|
-
|
|
401
|
+
"DISTINCT #{columns_for_distinct(columns, order_by)}"
|
|
402
|
+
end
|
|
308
403
|
|
|
404
|
+
# @override Since AR 4.0 (on 4.1 {#distinct} is gone and won't be called).
|
|
405
|
+
def columns_for_distinct(columns, orders)
|
|
406
|
+
return columns if orders.blank?
|
|
407
|
+
if orders.is_a?(Array) # AR 3.x vs 4.x
|
|
408
|
+
orders = orders.map { |column| column.is_a?(String) ? column : column.to_sql }
|
|
409
|
+
else
|
|
410
|
+
orders = extract_order_columns(orders)
|
|
411
|
+
end
|
|
309
412
|
# construct a valid DISTINCT clause, ie. one that includes the ORDER BY columns, using
|
|
310
413
|
# FIRST_VALUE such that the inclusion of these columns doesn't invalidate the DISTINCT
|
|
311
|
-
order_columns =
|
|
312
|
-
order_columns = order_columns.zip((0...order_columns.size).to_a).map do |c, i|
|
|
414
|
+
order_columns = orders.map do |c, i|
|
|
313
415
|
"FIRST_VALUE(#{c.split.first}) OVER (PARTITION BY #{columns} ORDER BY #{c}) AS alias_#{i}__"
|
|
314
416
|
end
|
|
315
|
-
|
|
316
|
-
|
|
417
|
+
columns = [ columns ]; columns.flatten!
|
|
418
|
+
columns.push( *order_columns ).join(', ')
|
|
317
419
|
end
|
|
318
420
|
|
|
319
421
|
# ORDER BY clause for the passed order option.
|
|
320
422
|
#
|
|
321
|
-
# Uses column aliases as defined by #distinct.
|
|
423
|
+
# Uses column aliases as defined by {#distinct}.
|
|
322
424
|
def add_order_by_for_association_limiting!(sql, options)
|
|
323
425
|
return sql if options[:order].blank?
|
|
324
426
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
427
|
+
order_columns = extract_order_columns(options[:order]) do |columns|
|
|
428
|
+
columns.map! { |s| $1 if s =~ / (.*)/ }; columns
|
|
429
|
+
end
|
|
430
|
+
order = order_columns.map { |s, i| "alias_#{i}__ #{s}" } # @see {#distinct}
|
|
431
|
+
|
|
432
|
+
sql << "ORDER BY #{order.join(', ')}"
|
|
433
|
+
end
|
|
328
434
|
|
|
329
|
-
|
|
435
|
+
def extract_order_columns(order_by)
|
|
436
|
+
columns = order_by.split(',')
|
|
437
|
+
columns.map!(&:strip); columns.reject!(&:blank?)
|
|
438
|
+
columns = yield(columns) if block_given?
|
|
439
|
+
columns.zip( (0...columns.size).to_a )
|
|
440
|
+
end
|
|
441
|
+
private :extract_order_columns
|
|
442
|
+
|
|
443
|
+
def temporary_table?(table_name)
|
|
444
|
+
select_value("SELECT temporary FROM user_tables WHERE table_name = '#{table_name.upcase}'") == 'Y'
|
|
330
445
|
end
|
|
331
446
|
|
|
332
447
|
def tables
|
|
333
448
|
@connection.tables(nil, oracle_schema)
|
|
334
449
|
end
|
|
335
450
|
|
|
336
|
-
|
|
337
|
-
|
|
451
|
+
# NOTE: better to use current_schema instead of the configured one ?!
|
|
452
|
+
def columns(table_name, name = nil)
|
|
453
|
+
@connection.columns_internal(table_name.to_s, nil, oracle_schema)
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
def tablespace(table_name)
|
|
457
|
+
select_value "SELECT tablespace_name FROM user_tables WHERE table_name='#{table_name.to_s.upcase}'"
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
def charset
|
|
461
|
+
database_parameters['NLS_CHARACTERSET']
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
def collation
|
|
465
|
+
database_parameters['NLS_COMP']
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
def database_parameters
|
|
469
|
+
return @database_parameters unless ( @database_parameters ||= {} ).empty?
|
|
470
|
+
@connection.execute_query_raw("SELECT * FROM NLS_DATABASE_PARAMETERS") do
|
|
471
|
+
|name, value| @database_parameters[name] = value
|
|
472
|
+
end
|
|
473
|
+
@database_parameters
|
|
338
474
|
end
|
|
339
475
|
|
|
340
476
|
# QUOTING ==================================================
|
|
341
|
-
#
|
|
342
|
-
# see: abstract/quoting.rb
|
|
343
477
|
|
|
344
|
-
#
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
name.to_s
|
|
478
|
+
# @override
|
|
479
|
+
def quote_table_name(name)
|
|
480
|
+
name.to_s.split('.').map{ |n| n.split('@').map{ |m| quote_column_name(m) }.join('@') }.join('.')
|
|
348
481
|
end
|
|
349
482
|
|
|
350
|
-
#
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
483
|
+
# @override
|
|
484
|
+
def quote_column_name(name)
|
|
485
|
+
# if only valid lowercase column characters in name
|
|
486
|
+
if ( name = name.to_s ) =~ /\A[a-z][a-z_0-9\$#]*\Z/
|
|
487
|
+
# putting double-quotes around an identifier causes Oracle to treat the
|
|
488
|
+
# identifier as case sensitive (otherwise assumes case-insensitivity) !
|
|
489
|
+
# all upper case is an exception, where double-quotes are meaningless
|
|
490
|
+
"\"#{name.upcase}\"" # name.upcase
|
|
491
|
+
else
|
|
492
|
+
# remove double quotes which cannot be used inside quoted identifier
|
|
493
|
+
"\"#{name.gsub('"', '')}\""
|
|
494
|
+
end
|
|
358
495
|
end
|
|
359
496
|
|
|
360
|
-
def
|
|
361
|
-
|
|
497
|
+
def unquote_table_name(name)
|
|
498
|
+
name = name[1...-1] if name[0, 1] == '"'
|
|
499
|
+
name.upcase == name ? name.downcase : name
|
|
362
500
|
end
|
|
363
501
|
|
|
364
|
-
|
|
365
|
-
|
|
502
|
+
# @override
|
|
503
|
+
def quote(value, column = nil)
|
|
366
504
|
return value if sql_literal?(value)
|
|
367
505
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
506
|
+
column_type = column && column.type
|
|
507
|
+
if column_type == :text || column_type == :binary
|
|
508
|
+
return 'NULL' if value.nil? || value == ''
|
|
509
|
+
if update_lob_value?(value, column)
|
|
510
|
+
if /(.*?)\([0-9]+\)/ =~ ( sql_type = column.sql_type )
|
|
511
|
+
%Q{empty_#{ $1.downcase }()}
|
|
512
|
+
else
|
|
513
|
+
%Q{empty_#{ sql_type.respond_to?(:downcase) ? sql_type.downcase : 'blob' }()}
|
|
514
|
+
end
|
|
371
515
|
else
|
|
372
|
-
|
|
516
|
+
"'#{quote_string(value.to_s)}'"
|
|
373
517
|
end
|
|
518
|
+
elsif column_type == :xml
|
|
519
|
+
"XMLTYPE('#{quote_string(value)}')" # XMLTYPE ?
|
|
520
|
+
elsif column_type == :raw
|
|
521
|
+
quote_raw(value)
|
|
374
522
|
else
|
|
375
523
|
if column.respond_to?(:primary) && column.primary && column.klass != String
|
|
376
524
|
return value.to_i.to_s
|
|
377
525
|
end
|
|
378
|
-
|
|
379
|
-
if
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
526
|
+
|
|
527
|
+
if column_type == :datetime || column_type == :time
|
|
528
|
+
if value.acts_like?(:time)
|
|
529
|
+
%Q{TO_DATE('#{get_time(value).strftime("%Y-%m-%d %H:%M:%S")}','YYYY-MM-DD HH24:MI:SS')}
|
|
530
|
+
else
|
|
531
|
+
value.blank? ? 'NULL' : %Q{DATE'#{value}'} # assume correctly formated DATE (string)
|
|
532
|
+
end
|
|
533
|
+
elsif ( like_date = value.acts_like?(:date) ) || column_type == :date
|
|
534
|
+
if value.acts_like?(:time) # value.respond_to?(:strftime)
|
|
535
|
+
%Q{DATE'#{get_time(value).strftime("%Y-%m-%d")}'}
|
|
536
|
+
elsif like_date
|
|
537
|
+
%Q{DATE'#{quoted_date(value)}'} # DATE 'YYYY-MM-DD'
|
|
538
|
+
else
|
|
539
|
+
value.blank? ? 'NULL' : %Q{DATE'#{value}'} # assume correctly formated DATE (string)
|
|
540
|
+
end
|
|
541
|
+
elsif ( like_time = value.acts_like?(:time) ) || column_type == :timestamp
|
|
542
|
+
if like_time
|
|
543
|
+
%Q{TIMESTAMP'#{quoted_date(value, true)}'} # TIMESTAMP 'YYYY-MM-DD HH24:MI:SS.FF'
|
|
544
|
+
else
|
|
545
|
+
value.blank? ? 'NULL' : %Q{TIMESTAMP'#{value}'} # assume correctly formated TIMESTAMP (string)
|
|
546
|
+
end
|
|
547
|
+
else
|
|
548
|
+
super
|
|
383
549
|
end
|
|
384
|
-
quoted
|
|
385
550
|
end
|
|
386
551
|
end
|
|
387
552
|
|
|
388
|
-
|
|
389
|
-
|
|
553
|
+
# Quote date/time values for use in SQL input.
|
|
554
|
+
# Includes milliseconds if the value is a Time responding to usec.
|
|
555
|
+
# @override
|
|
556
|
+
def quoted_date(value, time = nil)
|
|
557
|
+
if time || ( time.nil? && value.acts_like?(:time) )
|
|
558
|
+
usec = value.respond_to?(:usec) && (value.usec / 10000.0).round # .428000 -> .43
|
|
559
|
+
return "#{get_time(value).to_s(:db)}.#{sprintf("%02d", usec)}" if usec
|
|
560
|
+
# value.strftime("%Y-%m-%d %H:%M:%S")
|
|
561
|
+
end
|
|
562
|
+
value.to_s(:db)
|
|
390
563
|
end
|
|
391
564
|
|
|
392
|
-
def
|
|
393
|
-
'
|
|
565
|
+
def quote_raw(value)
|
|
566
|
+
value = value.unpack('C*') if value.is_a?(String)
|
|
567
|
+
"'#{value.map { |x| "%02X" % x }.join}'"
|
|
568
|
+
end
|
|
569
|
+
|
|
570
|
+
# @override
|
|
571
|
+
def supports_migrations?; true end
|
|
572
|
+
|
|
573
|
+
# @override
|
|
574
|
+
def supports_primary_key?; true end
|
|
575
|
+
|
|
576
|
+
# @override
|
|
577
|
+
def supports_savepoints?; true end
|
|
578
|
+
|
|
579
|
+
# @override
|
|
580
|
+
def supports_explain?; true end
|
|
581
|
+
|
|
582
|
+
# @override
|
|
583
|
+
def supports_views?; true end
|
|
584
|
+
|
|
585
|
+
def truncate(table_name, name = nil)
|
|
586
|
+
execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
|
|
587
|
+
end
|
|
588
|
+
|
|
589
|
+
def explain(arel, binds = [])
|
|
590
|
+
sql = "EXPLAIN PLAN FOR #{to_sql(arel, binds)}"
|
|
591
|
+
return if sql =~ /FROM all_/
|
|
592
|
+
exec_update(sql, 'EXPLAIN', binds)
|
|
593
|
+
select_values("SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY)", 'EXPLAIN').join("\n")
|
|
594
|
+
end
|
|
595
|
+
|
|
596
|
+
def select(sql, name = nil, binds = [])
|
|
597
|
+
result = super # AR::Result (4.0) or Array (<= 3.2)
|
|
598
|
+
result.columns.delete('raw_rnum_') if result.respond_to?(:columns)
|
|
599
|
+
result.each { |row| row.delete('raw_rnum_') } # Hash rows even for AR::Result
|
|
600
|
+
result
|
|
601
|
+
end
|
|
602
|
+
|
|
603
|
+
@@do_not_prefetch_primary_key = {}
|
|
604
|
+
|
|
605
|
+
# Returns true for Oracle adapter (since Oracle requires primary key
|
|
606
|
+
# values to be pre-fetched before insert).
|
|
607
|
+
# @see #next_sequence_value
|
|
608
|
+
# @override
|
|
609
|
+
def prefetch_primary_key?(table_name = nil)
|
|
610
|
+
return true if table_name.nil?
|
|
611
|
+
do_not_prefetch_hash = @@do_not_prefetch_primary_key
|
|
612
|
+
do_not_prefetch = do_not_prefetch_hash[ table_name = table_name.to_s ]
|
|
613
|
+
if do_not_prefetch.nil?
|
|
614
|
+
owner, desc_table_name, db_link = @connection.describe(table_name, default_owner)
|
|
615
|
+
do_not_prefetch_hash[table_name] = do_not_prefetch =
|
|
616
|
+
! has_primary_key?(table_name, owner, desc_table_name, db_link) ||
|
|
617
|
+
has_primary_key_trigger?(table_name, owner, desc_table_name, db_link)
|
|
618
|
+
end
|
|
619
|
+
! do_not_prefetch
|
|
620
|
+
end
|
|
621
|
+
|
|
622
|
+
# used to clear prefetch primary key flag for all tables
|
|
623
|
+
# @private
|
|
624
|
+
def clear_prefetch_primary_key; @@do_not_prefetch_primary_key = {} end
|
|
625
|
+
|
|
626
|
+
# @private
|
|
627
|
+
def has_primary_key?(table_name, owner = nil, desc_table_name = nil, db_link = nil)
|
|
628
|
+
! pk_and_sequence_for(table_name, owner, desc_table_name, db_link).nil?
|
|
629
|
+
end
|
|
630
|
+
|
|
631
|
+
# @private check if table has primary key trigger with _pkt suffix
|
|
632
|
+
def has_primary_key_trigger?(table_name, owner = nil, desc_table_name = nil, db_link = nil)
|
|
633
|
+
(owner, desc_table_name, db_link) = @connection.describe(table_name, default_owner) unless desc_table_name
|
|
634
|
+
|
|
635
|
+
trigger_name = default_trigger_name(table_name).upcase
|
|
636
|
+
pkt_sql = <<-SQL
|
|
637
|
+
SELECT trigger_name
|
|
638
|
+
FROM all_triggers#{db_link}
|
|
639
|
+
WHERE owner = '#{owner}'
|
|
640
|
+
AND trigger_name = '#{trigger_name}'
|
|
641
|
+
AND table_owner = '#{owner}'
|
|
642
|
+
AND table_name = '#{desc_table_name}'
|
|
643
|
+
AND status = 'ENABLED'
|
|
644
|
+
SQL
|
|
645
|
+
select_value(pkt_sql, 'Primary Key Trigger') ? true : false
|
|
646
|
+
end
|
|
647
|
+
|
|
648
|
+
# use in set_sequence_name to avoid fetching primary key value from sequence
|
|
649
|
+
AUTOGENERATED_SEQUENCE_NAME = 'autogenerated'.freeze
|
|
650
|
+
|
|
651
|
+
# Returns the next sequence value from a sequence generator. Not generally
|
|
652
|
+
# called directly; used by ActiveRecord to get the next primary key value
|
|
653
|
+
# when inserting a new database record (see #prefetch_primary_key?).
|
|
654
|
+
def next_sequence_value(sequence_name)
|
|
655
|
+
# if sequence_name is set to :autogenerated then it means that primary key will be populated by trigger
|
|
656
|
+
return nil if sequence_name == AUTOGENERATED_SEQUENCE_NAME
|
|
657
|
+
sequence_name = quote_table_name(sequence_name)
|
|
658
|
+
sql = "SELECT #{sequence_name}.NEXTVAL id FROM dual"
|
|
659
|
+
log(sql, 'SQL') { @connection.next_sequence_value(sequence_name) }
|
|
660
|
+
end
|
|
661
|
+
|
|
662
|
+
def pk_and_sequence_for(table_name, owner = nil, desc_table_name = nil, db_link = nil)
|
|
663
|
+
(owner, desc_table_name, db_link) = @connection.describe(table_name, default_owner) unless desc_table_name
|
|
664
|
+
|
|
665
|
+
seqs = select_values(<<-SQL.strip.gsub(/\s+/, ' '), 'Sequence')
|
|
666
|
+
SELECT us.sequence_name
|
|
667
|
+
FROM all_sequences#{db_link} us
|
|
668
|
+
WHERE us.sequence_owner = '#{owner}'
|
|
669
|
+
AND us.sequence_name = '#{desc_table_name}_SEQ'
|
|
670
|
+
SQL
|
|
671
|
+
|
|
672
|
+
# changed back from user_constraints to all_constraints for consistency
|
|
673
|
+
pks = select_values(<<-SQL.strip.gsub(/\s+/, ' '), 'Primary Key')
|
|
674
|
+
SELECT cc.column_name
|
|
675
|
+
FROM all_constraints#{db_link} c, all_cons_columns#{db_link} cc
|
|
676
|
+
WHERE c.owner = '#{owner}'
|
|
677
|
+
AND c.table_name = '#{desc_table_name}'
|
|
678
|
+
AND c.constraint_type = 'P'
|
|
679
|
+
AND cc.owner = c.owner
|
|
680
|
+
AND cc.constraint_name = c.constraint_name
|
|
681
|
+
SQL
|
|
682
|
+
|
|
683
|
+
# only support single column keys
|
|
684
|
+
pks.size == 1 ? [oracle_downcase(pks.first),
|
|
685
|
+
oracle_downcase(seqs.first)] : nil
|
|
686
|
+
end
|
|
687
|
+
private :pk_and_sequence_for
|
|
688
|
+
|
|
689
|
+
# Returns just a table's primary key
|
|
690
|
+
def primary_key(table_name)
|
|
691
|
+
pk_and_sequence = pk_and_sequence_for(table_name)
|
|
692
|
+
pk_and_sequence && pk_and_sequence.first
|
|
693
|
+
end
|
|
694
|
+
|
|
695
|
+
# @override (for AR <= 3.0)
|
|
696
|
+
def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
|
|
697
|
+
# if PK is already pre-fetched from sequence or if there is no PK :
|
|
698
|
+
if id_value || pk.nil?
|
|
699
|
+
execute(sql, name)
|
|
700
|
+
return id_value
|
|
701
|
+
end
|
|
702
|
+
|
|
703
|
+
if pk && use_insert_returning? # true by default on AR <= 3.0
|
|
704
|
+
sql = "#{sql} RETURNING #{quote_column_name(pk)} INTO ?"
|
|
705
|
+
exec_insert_returning(sql, name, nil, pk)
|
|
706
|
+
else
|
|
707
|
+
execute(sql, name)
|
|
708
|
+
end
|
|
709
|
+
end
|
|
710
|
+
protected :insert_sql
|
|
711
|
+
|
|
712
|
+
# @override
|
|
713
|
+
def sql_for_insert(sql, pk, id_value, sequence_name, binds)
|
|
714
|
+
unless id_value || pk.nil?
|
|
715
|
+
if pk && use_insert_returning?
|
|
716
|
+
sql = "#{sql} RETURNING #{quote_column_name(pk)} INTO ?"
|
|
717
|
+
end
|
|
718
|
+
end
|
|
719
|
+
[ sql, binds ]
|
|
720
|
+
end
|
|
721
|
+
|
|
722
|
+
# @override
|
|
723
|
+
def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
|
|
724
|
+
# NOTE: ActiveRecord::Relation calls our {#next_sequence_value}
|
|
725
|
+
# (from its `insert`) and passes the returned id_value here ...
|
|
726
|
+
sql, binds = sql_for_insert(to_sql(arel, binds), pk, id_value, sequence_name, binds)
|
|
727
|
+
if id_value
|
|
728
|
+
exec_update(sql, name, binds)
|
|
729
|
+
return id_value
|
|
730
|
+
else
|
|
731
|
+
value = exec_insert(sql, name, binds, pk, sequence_name)
|
|
732
|
+
id_value || last_inserted_id(value)
|
|
733
|
+
end
|
|
734
|
+
end
|
|
735
|
+
|
|
736
|
+
# @override
|
|
737
|
+
def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
|
|
738
|
+
if pk && use_insert_returning?
|
|
739
|
+
if sql.is_a?(String) && sql.index('RETURNING')
|
|
740
|
+
return exec_insert_returning(sql, name, binds, pk)
|
|
741
|
+
end
|
|
742
|
+
end
|
|
743
|
+
super(sql, name, binds) # assume no generated id for table
|
|
744
|
+
end
|
|
745
|
+
|
|
746
|
+
def exec_insert_returning(sql, name, binds, pk = nil)
|
|
747
|
+
sql = to_sql(sql, binds) if sql.respond_to?(:to_sql)
|
|
748
|
+
if prepared_statements?
|
|
749
|
+
log(sql, name, binds) { @connection.execute_insert_returning(sql, binds) }
|
|
750
|
+
else
|
|
751
|
+
log(sql, name) { @connection.execute_insert_returning(sql, nil) }
|
|
752
|
+
end
|
|
753
|
+
end
|
|
754
|
+
# private :exec_insert_returning
|
|
755
|
+
|
|
756
|
+
def next_id_value(sql, sequence_name = nil)
|
|
757
|
+
# Assume the SQL contains a bind-variable for the ID
|
|
758
|
+
sequence_name ||= begin
|
|
759
|
+
# Extract the table from the insert SQL. Yuck.
|
|
760
|
+
table = extract_table_ref_from_insert_sql(sql)
|
|
761
|
+
default_sequence_name(table)
|
|
762
|
+
end
|
|
763
|
+
next_sequence_value(sequence_name)
|
|
764
|
+
end
|
|
765
|
+
private :next_id_value
|
|
766
|
+
|
|
767
|
+
def use_insert_returning?
|
|
768
|
+
if @use_insert_returning.nil?
|
|
769
|
+
@use_insert_returning = false
|
|
770
|
+
end
|
|
771
|
+
@use_insert_returning
|
|
394
772
|
end
|
|
395
773
|
|
|
396
774
|
private
|
|
397
|
-
|
|
775
|
+
|
|
776
|
+
def _execute(sql, name = nil)
|
|
777
|
+
if self.class.select?(sql)
|
|
778
|
+
@connection.execute_query_raw(sql)
|
|
779
|
+
elsif self.class.insert?(sql)
|
|
780
|
+
@connection.execute_insert(sql)
|
|
781
|
+
else
|
|
782
|
+
@connection.execute_update(sql)
|
|
783
|
+
end
|
|
784
|
+
end
|
|
785
|
+
|
|
786
|
+
def extract_table_ref_from_insert_sql(sql)
|
|
787
|
+
table = sql.split(" ", 4)[2]
|
|
788
|
+
if idx = table.index('(')
|
|
789
|
+
table = table[0...idx] # INTO table(col1, col2) ...
|
|
790
|
+
end
|
|
791
|
+
unquote_table_name(table)
|
|
792
|
+
end
|
|
793
|
+
|
|
794
|
+
# In Oracle, schemas are usually created under your username :
|
|
398
795
|
# http://www.oracle.com/technology/obe/2day_dba/schema/schema.htm
|
|
796
|
+
#
|
|
797
|
+
# A schema is the set of objects (tables, views, indexes, etc) that belongs
|
|
798
|
+
# to an user, often used as another way to refer to an Oracle user account.
|
|
799
|
+
#
|
|
800
|
+
# But allow separate configuration as "schema:" anyway (see #53)
|
|
399
801
|
def oracle_schema
|
|
400
|
-
|
|
802
|
+
if @config[:schema]
|
|
803
|
+
@config[:schema].to_s
|
|
804
|
+
elsif @config[:username]
|
|
805
|
+
@config[:username].to_s
|
|
806
|
+
end
|
|
401
807
|
end
|
|
402
808
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
809
|
+
# default schema owner
|
|
810
|
+
def default_owner
|
|
811
|
+
unless defined? @default_owner
|
|
812
|
+
username = config[:username] ? config[:username].to_s : jdbc_connection.meta_data.user_name
|
|
813
|
+
@default_owner = username.nil? ? nil : username.upcase
|
|
407
814
|
end
|
|
408
|
-
|
|
815
|
+
@default_owner
|
|
409
816
|
end
|
|
817
|
+
|
|
818
|
+
def oracle_downcase(column_name)
|
|
819
|
+
return nil if column_name.nil?
|
|
820
|
+
column_name =~ /[a-z]/ ? column_name : column_name.downcase
|
|
821
|
+
end
|
|
822
|
+
|
|
410
823
|
end
|
|
411
824
|
end
|
|
412
825
|
|
|
826
|
+
require 'arjdbc/util/quoted_cache'
|
|
827
|
+
|
|
828
|
+
module ActiveRecord::ConnectionAdapters
|
|
829
|
+
|
|
830
|
+
remove_const(:OracleAdapter) if const_defined?(:OracleAdapter)
|
|
831
|
+
|
|
832
|
+
class OracleAdapter < JdbcAdapter
|
|
833
|
+
include ::ArJdbc::Oracle
|
|
834
|
+
include ::ArJdbc::Util::QuotedCache
|
|
835
|
+
|
|
836
|
+
# By default, the MysqlAdapter will consider all columns of type
|
|
837
|
+
# <tt>tinyint(1)</tt> as boolean. If you wish to disable this :
|
|
838
|
+
#
|
|
839
|
+
# ActiveRecord::ConnectionAdapters::OracleAdapter.emulate_booleans = false
|
|
840
|
+
#
|
|
841
|
+
def self.emulate_booleans?; ::ArJdbc::Oracle.emulate_booleans?; end
|
|
842
|
+
def self.emulate_booleans; ::ArJdbc::Oracle.emulate_booleans?; end # oracle-enhanced
|
|
843
|
+
def self.emulate_booleans=(emulate); ::ArJdbc::Oracle.emulate_booleans = emulate; end
|
|
844
|
+
|
|
845
|
+
def initialize(*args)
|
|
846
|
+
::ArJdbc::Oracle.initialize!
|
|
847
|
+
super # configure_connection happens in super
|
|
848
|
+
|
|
849
|
+
@use_insert_returning = config.key?(:insert_returning) ?
|
|
850
|
+
self.class.type_cast_config_to_boolean(config[:insert_returning]) : nil
|
|
851
|
+
end
|
|
852
|
+
|
|
853
|
+
end
|
|
854
|
+
|
|
855
|
+
class OracleColumn < JdbcColumn
|
|
856
|
+
include ::ArJdbc::Oracle::Column
|
|
857
|
+
|
|
858
|
+
# def returning_id?; @returning_id ||= nil end
|
|
859
|
+
# def returning_id!; @returning_id = true end
|
|
860
|
+
|
|
861
|
+
end
|
|
862
|
+
|
|
863
|
+
end
|