activerecord-jdbc-adapter 1.0.3-java → 50.1-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (268) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +33 -0
  3. data/.travis.yml +79 -0
  4. data/.yardopts +4 -0
  5. data/CONTRIBUTING.md +50 -0
  6. data/Gemfile +91 -0
  7. data/History.md +1191 -0
  8. data/LICENSE.txt +22 -17
  9. data/README.md +169 -0
  10. data/RUNNING_TESTS.md +127 -0
  11. data/Rakefile +294 -5
  12. data/Rakefile.jdbc +20 -0
  13. data/activerecord-jdbc-adapter.gemspec +55 -0
  14. data/lib/active_record/connection_adapters/as400_adapter.rb +2 -0
  15. data/lib/active_record/connection_adapters/db2_adapter.rb +1 -0
  16. data/lib/active_record/connection_adapters/firebird_adapter.rb +1 -0
  17. data/lib/active_record/connection_adapters/mariadb_adapter.rb +1 -0
  18. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +1 -0
  19. data/lib/activerecord-jdbc-adapter.rb +0 -5
  20. data/lib/arel/visitors/compat.rb +60 -0
  21. data/lib/arel/visitors/db2.rb +128 -6
  22. data/lib/arel/visitors/derby.rb +103 -10
  23. data/lib/arel/visitors/firebird.rb +79 -0
  24. data/lib/arel/visitors/h2.rb +25 -0
  25. data/lib/arel/visitors/hsqldb.rb +18 -10
  26. data/lib/arel/visitors/postgresql_jdbc.rb +6 -0
  27. data/lib/arel/visitors/sql_server.rb +225 -0
  28. data/lib/arel/visitors/sql_server/ng42.rb +293 -0
  29. data/lib/arjdbc.rb +11 -21
  30. data/lib/arjdbc/abstract/connection_management.rb +35 -0
  31. data/lib/arjdbc/abstract/core.rb +64 -0
  32. data/lib/arjdbc/abstract/database_statements.rb +64 -0
  33. data/lib/arjdbc/abstract/statement_cache.rb +58 -0
  34. data/lib/arjdbc/abstract/transaction_support.rb +86 -0
  35. data/lib/arjdbc/db2.rb +3 -1
  36. data/lib/arjdbc/db2/adapter.rb +630 -250
  37. data/lib/arjdbc/db2/as400.rb +130 -0
  38. data/lib/arjdbc/db2/column.rb +167 -0
  39. data/lib/arjdbc/db2/connection_methods.rb +44 -0
  40. data/lib/arjdbc/derby.rb +1 -5
  41. data/lib/arjdbc/derby/active_record_patch.rb +13 -0
  42. data/lib/arjdbc/derby/adapter.rb +409 -217
  43. data/lib/arjdbc/derby/connection_methods.rb +16 -14
  44. data/lib/arjdbc/derby/schema_creation.rb +15 -0
  45. data/lib/arjdbc/discover.rb +62 -50
  46. data/lib/arjdbc/firebird.rb +3 -1
  47. data/lib/arjdbc/firebird/adapter.rb +365 -62
  48. data/lib/arjdbc/firebird/connection_methods.rb +23 -0
  49. data/lib/arjdbc/h2.rb +2 -3
  50. data/lib/arjdbc/h2/adapter.rb +273 -6
  51. data/lib/arjdbc/h2/connection_methods.rb +23 -8
  52. data/lib/arjdbc/hsqldb.rb +2 -3
  53. data/lib/arjdbc/hsqldb/adapter.rb +204 -77
  54. data/lib/arjdbc/hsqldb/connection_methods.rb +24 -10
  55. data/lib/arjdbc/hsqldb/explain_support.rb +35 -0
  56. data/lib/arjdbc/hsqldb/schema_creation.rb +11 -0
  57. data/lib/arjdbc/informix.rb +4 -2
  58. data/lib/arjdbc/informix/adapter.rb +78 -54
  59. data/lib/arjdbc/informix/connection_methods.rb +8 -9
  60. data/lib/arjdbc/jdbc.rb +59 -2
  61. data/lib/arjdbc/jdbc/adapter.rb +356 -166
  62. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  63. data/lib/arjdbc/jdbc/adapter_require.rb +46 -0
  64. data/lib/arjdbc/jdbc/base_ext.rb +15 -0
  65. data/lib/arjdbc/jdbc/callbacks.rb +27 -18
  66. data/lib/arjdbc/jdbc/column.rb +79 -20
  67. data/lib/arjdbc/jdbc/connection.rb +5 -119
  68. data/lib/arjdbc/jdbc/connection_methods.rb +32 -4
  69. data/lib/arjdbc/jdbc/error.rb +65 -0
  70. data/lib/arjdbc/jdbc/extension.rb +41 -29
  71. data/lib/arjdbc/jdbc/java.rb +5 -6
  72. data/lib/arjdbc/jdbc/jdbc.rake +3 -126
  73. data/lib/arjdbc/jdbc/railtie.rb +2 -9
  74. data/lib/arjdbc/jdbc/rake_tasks.rb +3 -10
  75. data/lib/arjdbc/jdbc/serialized_attributes_helper.rb +3 -0
  76. data/lib/arjdbc/jdbc/type_cast.rb +166 -0
  77. data/lib/arjdbc/jdbc/type_converter.rb +35 -19
  78. data/lib/arjdbc/mssql.rb +6 -3
  79. data/lib/arjdbc/mssql/adapter.rb +630 -298
  80. data/lib/arjdbc/mssql/column.rb +200 -0
  81. data/lib/arjdbc/mssql/connection_methods.rb +66 -17
  82. data/lib/arjdbc/mssql/explain_support.rb +99 -0
  83. data/lib/arjdbc/mssql/limit_helpers.rb +189 -50
  84. data/lib/arjdbc/mssql/lock_methods.rb +77 -0
  85. data/lib/arjdbc/mssql/types.rb +343 -0
  86. data/lib/arjdbc/mssql/utils.rb +82 -0
  87. data/lib/arjdbc/mysql.rb +2 -3
  88. data/lib/arjdbc/mysql/adapter.rb +86 -356
  89. data/lib/arjdbc/mysql/connection_methods.rb +159 -23
  90. data/lib/arjdbc/oracle/adapter.rb +714 -263
  91. data/lib/arjdbc/postgresql.rb +2 -3
  92. data/lib/arjdbc/postgresql/_bc_time_cast_patch.rb +24 -0
  93. data/lib/arjdbc/postgresql/adapter.rb +570 -400
  94. data/lib/arjdbc/postgresql/base/array_decoder.rb +26 -0
  95. data/lib/arjdbc/postgresql/base/array_encoder.rb +25 -0
  96. data/lib/arjdbc/postgresql/base/array_parser.rb +95 -0
  97. data/lib/arjdbc/postgresql/base/pgconn.rb +11 -0
  98. data/lib/arjdbc/postgresql/column.rb +51 -0
  99. data/lib/arjdbc/postgresql/connection_methods.rb +57 -18
  100. data/lib/arjdbc/postgresql/name.rb +24 -0
  101. data/lib/arjdbc/postgresql/oid_types.rb +192 -0
  102. data/lib/arjdbc/railtie.rb +11 -0
  103. data/lib/arjdbc/sqlite3.rb +2 -3
  104. data/lib/arjdbc/sqlite3/adapter.rb +518 -198
  105. data/lib/arjdbc/sqlite3/connection_methods.rb +49 -24
  106. data/lib/arjdbc/sybase.rb +2 -2
  107. data/lib/arjdbc/sybase/adapter.rb +7 -6
  108. data/lib/arjdbc/tasks.rb +13 -0
  109. data/lib/arjdbc/tasks/database_tasks.rb +52 -0
  110. data/lib/arjdbc/tasks/databases.rake +91 -0
  111. data/lib/arjdbc/tasks/databases3.rake +215 -0
  112. data/lib/arjdbc/tasks/databases4.rake +39 -0
  113. data/lib/arjdbc/tasks/db2_database_tasks.rb +104 -0
  114. data/lib/arjdbc/tasks/derby_database_tasks.rb +95 -0
  115. data/lib/arjdbc/tasks/h2_database_tasks.rb +31 -0
  116. data/lib/arjdbc/tasks/hsqldb_database_tasks.rb +70 -0
  117. data/lib/arjdbc/tasks/jdbc_database_tasks.rb +169 -0
  118. data/lib/arjdbc/tasks/mssql_database_tasks.rb +46 -0
  119. data/lib/arjdbc/util/quoted_cache.rb +60 -0
  120. data/lib/arjdbc/util/serialized_attributes.rb +98 -0
  121. data/lib/arjdbc/util/table_copier.rb +110 -0
  122. data/lib/arjdbc/version.rb +1 -6
  123. data/lib/generators/jdbc/USAGE +9 -0
  124. data/lib/generators/jdbc/jdbc_generator.rb +8 -0
  125. data/lib/jdbc_adapter.rb +1 -1
  126. data/lib/jdbc_adapter/rake_tasks.rb +3 -2
  127. data/lib/jdbc_adapter/version.rb +2 -1
  128. data/pom.xml +114 -0
  129. data/rails_generators/jdbc_generator.rb +1 -1
  130. data/rails_generators/templates/config/initializers/jdbc.rb +8 -5
  131. data/rails_generators/templates/lib/tasks/jdbc.rake +7 -4
  132. data/rakelib/01-tomcat.rake +51 -0
  133. data/rakelib/02-test.rake +132 -0
  134. data/rakelib/bundler_ext.rb +11 -0
  135. data/rakelib/compile.rake +67 -22
  136. data/rakelib/db.rake +61 -0
  137. data/rakelib/rails.rake +204 -29
  138. data/src/java/arjdbc/ArJdbcModule.java +286 -0
  139. data/src/java/arjdbc/db2/DB2Module.java +76 -0
  140. data/src/java/arjdbc/db2/DB2RubyJdbcConnection.java +126 -0
  141. data/src/java/arjdbc/derby/DerbyModule.java +99 -243
  142. data/src/java/arjdbc/derby/DerbyRubyJdbcConnection.java +152 -0
  143. data/src/java/arjdbc/firebird/FirebirdRubyJdbcConnection.java +174 -0
  144. data/src/java/arjdbc/{jdbc/JdbcConnectionFactory.java → h2/H2Module.java} +20 -6
  145. data/src/java/arjdbc/h2/H2RubyJdbcConnection.java +27 -12
  146. data/src/java/arjdbc/hsqldb/HSQLDBModule.java +73 -0
  147. data/src/java/arjdbc/informix/InformixRubyJdbcConnection.java +7 -6
  148. data/src/java/arjdbc/jdbc/AdapterJavaService.java +7 -29
  149. data/src/java/arjdbc/jdbc/Callable.java +44 -0
  150. data/src/java/arjdbc/jdbc/ConnectionFactory.java +132 -0
  151. data/src/java/arjdbc/jdbc/DataSourceConnectionFactory.java +157 -0
  152. data/src/java/arjdbc/jdbc/DriverConnectionFactory.java +63 -0
  153. data/src/java/arjdbc/jdbc/DriverWrapper.java +119 -0
  154. data/src/java/arjdbc/jdbc/JdbcResult.java +130 -0
  155. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +3622 -948
  156. data/src/java/arjdbc/mssql/MSSQLModule.java +90 -0
  157. data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +181 -0
  158. data/src/java/arjdbc/mysql/MySQLModule.java +99 -81
  159. data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +294 -0
  160. data/src/java/arjdbc/oracle/OracleModule.java +80 -0
  161. data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +387 -17
  162. data/src/java/arjdbc/postgresql/ByteaUtils.java +157 -0
  163. data/src/java/arjdbc/postgresql/PgResultSetMetaDataWrapper.java +23 -0
  164. data/src/java/arjdbc/postgresql/PostgreSQLModule.java +77 -0
  165. data/src/java/arjdbc/postgresql/PostgreSQLResult.java +184 -0
  166. data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +952 -0
  167. data/src/java/arjdbc/sqlite3/SQLite3Module.java +73 -0
  168. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +525 -0
  169. data/src/java/arjdbc/util/CallResultSet.java +826 -0
  170. data/src/java/arjdbc/util/DateTimeUtils.java +580 -0
  171. data/src/java/arjdbc/util/ObjectSupport.java +65 -0
  172. data/src/java/arjdbc/util/QuotingUtils.java +138 -0
  173. data/src/java/arjdbc/util/StringCache.java +63 -0
  174. data/src/java/arjdbc/util/StringHelper.java +159 -0
  175. metadata +245 -268
  176. data/History.txt +0 -369
  177. data/Manifest.txt +0 -180
  178. data/README.txt +0 -181
  179. data/lib/active_record/connection_adapters/oracle_adapter.rb +0 -1
  180. data/lib/arel/engines/sql/compilers/db2_compiler.rb +0 -9
  181. data/lib/arel/engines/sql/compilers/derby_compiler.rb +0 -6
  182. data/lib/arel/engines/sql/compilers/h2_compiler.rb +0 -6
  183. data/lib/arel/engines/sql/compilers/hsqldb_compiler.rb +0 -15
  184. data/lib/arel/engines/sql/compilers/jdbc_compiler.rb +0 -6
  185. data/lib/arel/engines/sql/compilers/mssql_compiler.rb +0 -46
  186. data/lib/arel/visitors/mssql.rb +0 -44
  187. data/lib/arjdbc/jdbc/compatibility.rb +0 -51
  188. data/lib/arjdbc/jdbc/core_ext.rb +0 -24
  189. data/lib/arjdbc/jdbc/discover.rb +0 -18
  190. data/lib/arjdbc/jdbc/driver.rb +0 -44
  191. data/lib/arjdbc/jdbc/missing_functionality_helper.rb +0 -87
  192. data/lib/arjdbc/jdbc/quoted_primary_key.rb +0 -28
  193. data/lib/arjdbc/jdbc/require_driver.rb +0 -16
  194. data/lib/arjdbc/mimer.rb +0 -2
  195. data/lib/arjdbc/mimer/adapter.rb +0 -142
  196. data/lib/arjdbc/mssql/tsql_helper.rb +0 -61
  197. data/lib/arjdbc/oracle.rb +0 -3
  198. data/lib/arjdbc/oracle/connection_methods.rb +0 -11
  199. data/lib/pg.rb +0 -4
  200. data/rakelib/package.rake +0 -92
  201. data/rakelib/test.rake +0 -81
  202. data/src/java/arjdbc/jdbc/SQLBlock.java +0 -48
  203. data/src/java/arjdbc/mssql/MssqlRubyJdbcConnection.java +0 -127
  204. data/src/java/arjdbc/postgresql/PostgresqlRubyJdbcConnection.java +0 -57
  205. data/src/java/arjdbc/sqlite3/Sqlite3RubyJdbcConnection.java +0 -64
  206. data/test/abstract_db_create.rb +0 -117
  207. data/test/activerecord/connection_adapters/type_conversion_test.rb +0 -31
  208. data/test/activerecord/connections/native_jdbc_mysql/connection.rb +0 -25
  209. data/test/db/db2.rb +0 -11
  210. data/test/db/derby.rb +0 -12
  211. data/test/db/h2.rb +0 -11
  212. data/test/db/hsqldb.rb +0 -13
  213. data/test/db/informix.rb +0 -11
  214. data/test/db/jdbc.rb +0 -11
  215. data/test/db/jndi_config.rb +0 -40
  216. data/test/db/logger.rb +0 -3
  217. data/test/db/mssql.rb +0 -9
  218. data/test/db/mysql.rb +0 -10
  219. data/test/db/oracle.rb +0 -34
  220. data/test/db/postgres.rb +0 -9
  221. data/test/db/sqlite3.rb +0 -11
  222. data/test/db2_simple_test.rb +0 -66
  223. data/test/derby_migration_test.rb +0 -68
  224. data/test/derby_multibyte_test.rb +0 -12
  225. data/test/derby_simple_test.rb +0 -99
  226. data/test/generic_jdbc_connection_test.rb +0 -29
  227. data/test/h2_simple_test.rb +0 -41
  228. data/test/has_many_through.rb +0 -79
  229. data/test/helper.rb +0 -5
  230. data/test/hsqldb_simple_test.rb +0 -6
  231. data/test/informix_simple_test.rb +0 -48
  232. data/test/jdbc_common.rb +0 -25
  233. data/test/jndi_callbacks_test.rb +0 -40
  234. data/test/jndi_test.rb +0 -25
  235. data/test/manualTestDatabase.rb +0 -191
  236. data/test/models/add_not_null_column_to_table.rb +0 -12
  237. data/test/models/auto_id.rb +0 -18
  238. data/test/models/data_types.rb +0 -28
  239. data/test/models/entry.rb +0 -43
  240. data/test/models/mixed_case.rb +0 -25
  241. data/test/models/reserved_word.rb +0 -18
  242. data/test/models/string_id.rb +0 -18
  243. data/test/models/validates_uniqueness_of_string.rb +0 -19
  244. data/test/mssql_db_create_test.rb +0 -26
  245. data/test/mssql_identity_insert_test.rb +0 -19
  246. data/test/mssql_legacy_types_test.rb +0 -58
  247. data/test/mssql_limit_offset_test.rb +0 -136
  248. data/test/mssql_multibyte_test.rb +0 -18
  249. data/test/mssql_simple_test.rb +0 -55
  250. data/test/mysql_db_create_test.rb +0 -27
  251. data/test/mysql_info_test.rb +0 -113
  252. data/test/mysql_multibyte_test.rb +0 -10
  253. data/test/mysql_nonstandard_primary_key_test.rb +0 -42
  254. data/test/mysql_simple_test.rb +0 -49
  255. data/test/oracle_simple_test.rb +0 -18
  256. data/test/oracle_specific_test.rb +0 -83
  257. data/test/pick_rails_version.rb +0 -3
  258. data/test/postgres_db_create_test.rb +0 -32
  259. data/test/postgres_drop_db_test.rb +0 -16
  260. data/test/postgres_mixed_case_test.rb +0 -29
  261. data/test/postgres_nonseq_pkey_test.rb +0 -38
  262. data/test/postgres_reserved_test.rb +0 -22
  263. data/test/postgres_schema_search_path_test.rb +0 -44
  264. data/test/postgres_simple_test.rb +0 -51
  265. data/test/postgres_table_alias_length_test.rb +0 -15
  266. data/test/simple.rb +0 -546
  267. data/test/sqlite3_simple_test.rb +0 -233
  268. data/test/sybase_jtds_simple_test.rb +0 -28
@@ -1,27 +1,163 @@
1
- # Don't need to load native mysql adapter
2
- $LOADED_FEATURES << "active_record/connection_adapters/mysql_adapter.rb"
3
- $LOADED_FEATURES << "active_record/connection_adapters/mysql2_adapter.rb"
4
-
5
- class ActiveRecord::Base
6
- class << self
7
- def mysql_connection(config)
8
- require "arjdbc/mysql"
9
- config[:port] ||= 3306
10
- options = (config[:options] ||= {})
11
- options['zeroDateTimeBehavior'] ||= 'convertToNull'
12
- options['jdbcCompliantTruncation'] ||= 'false'
13
- options['useUnicode'] ||= 'true'
14
- options['characterEncoding'] = config[:encoding] || 'utf8'
15
- config[:url] ||= "jdbc:mysql://#{config[:host]}:#{config[:port]}/#{config[:database]}"
16
- config[:driver] ||= "com.mysql.jdbc.Driver"
17
- config[:adapter_class] = ActiveRecord::ConnectionAdapters::MysqlAdapter
18
- connection = jdbc_connection(config)
19
- ::ArJdbc::MySQL.kill_cancel_timer(connection.raw_connection)
20
- connection
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
- alias_method :jdbcmysql_connection, :mysql_connection
23
- alias_method :mysql2_connection, :mysql_connection
107
+
108
+ config[:driver] ||= 'org.mariadb.jdbc.Driver'
109
+
110
+ mysql_connection(config)
24
111
  end
25
- end
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
- module ActiveRecord::ConnectionAdapters
2
- OracleAdapter = Class.new(AbstractAdapter) unless const_defined?(:OracleAdapter)
3
- end
4
-
5
- module ::ArJdbc
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
- ActiveRecord::Base.after_save :after_save_with_oracle_lob
22
- @lob_callback_added = true
23
- end
24
- require 'arjdbc/jdbc/quoted_primary_key'
25
- ActiveRecord::Base.extend ArJdbc::QuotedPrimaryKeyExtension
26
- mod.class.class_eval do
27
- alias_chained_method :insert, :query_dirty, :insert
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
- def self.column_selector
33
- [/oracle/i, lambda {|cfg,col| col.extend(::ArJdbc::Oracle::Column)}]
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
- module Column
41
- def primary=(val)
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
- def type_cast(value)
49
- return nil if value.nil?
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
- def type_cast_code(var_name)
58
- case type
59
- when :datetime then "ArJdbc::Oracle::Column.string_to_time(#{var_name}, self.class)"
60
- else
61
- super
62
- end
63
- end
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
- def self.string_to_time(string, klass)
66
- time = klass.string_to_time(string)
67
- guess_date_or_time(time)
68
- end
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
- def self.guess_date_or_time(value)
71
- (value && value.hour == 0 && value.min == 0 && value.sec == 0) ?
72
- Date.new(value.year, value.month, value.day) : value
73
- end
79
+ # @private
80
+ @@emulate_booleans = true
74
81
 
75
- private
76
- def simplified_type(field_type)
77
- case field_type
78
- when /^number\(1\)$/i then :boolean
79
- when /char/i then :string
80
- when /float|double/i then :float
81
- when /int/i then :integer
82
- when /num|dec|real/i then extract_scale(field_type) == 0 ? :integer : :decimal
83
- when /date|time/i then :datetime
84
- when /clob/i then :text
85
- when /blob/i then :binary
86
- end
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
- # Post process default value from JDBC into a Rails-friendly format (columns{-internal})
90
- def default_value(value)
91
- return nil unless value
99
+ def xml(*args)
100
+ options = args.extract_options!
101
+ column(args[0], 'xml', options)
102
+ end
103
+ end
92
104
 
93
- # Not sure why we need this for Oracle?
94
- value = value.strip
105
+ def table_definition(*args)
106
+ new_table_definition(TableDefinition, *args)
107
+ end
95
108
 
96
- return nil if value == "null"
109
+ def self.arel_visitor_type(config = nil)
110
+ ::Arel::Visitors::Oracle
111
+ end
97
112
 
98
- # sysdate default should be treated like a null value
99
- return nil if value.downcase == "sysdate"
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
- # jdbc returns column default strings with actual single quotes around the value.
102
- return $1 if value =~ /^'(.*)'$/
119
+ ADAPTER_NAME = 'Oracle'.freeze
103
120
 
104
- value
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
- def adapter_name
109
- 'Oracle'
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
- def arel2_visitors
113
- { 'oracle' => Arel::Visitors::Oracle }
114
- end
160
+ IDENTIFIER_LENGTH = 30
115
161
 
116
- # TODO: use this instead of the QuotedPrimaryKey logic and execute_id_insert?
117
- # def prefetch_primary_key?(table_name = nil)
118
- # columns(table_name).detect {|c| c.primary } if table_name
119
- # end
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
- def table_alias_length
122
- 30
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
- def default_sequence_name(table, column = nil) #:nodoc:
126
- "#{table}_seq"
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
- def create_table(name, options = {}) #:nodoc:
181
+ # @override
182
+ def create_table(name, options = {})
130
183
  super(name, options)
131
- seq_name = options[:sequence_name] || "#{name}_seq"
132
- start_value = options[:sequence_start_value] || 10000
133
- raise ActiveRecord::StatementInvalid.new("name #{seq_name} too long") if seq_name.length > table_alias_length
134
- execute "CREATE SEQUENCE #{seq_name} START WITH #{start_value}" unless options[:id] == false
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
- def rename_table(name, new_name) #:nodoc:
138
- execute "RENAME #{name} TO #{new_name}"
139
- execute "RENAME #{name}_seq TO #{new_name}_seq" rescue nil
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 drop_table(name, options = {}) #:nodoc:
143
- super(name)
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
- def recreate_database(name)
149
- tables.each{ |table| drop_table(table) }
150
- end
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 drop_database(name)
153
- recreate_database(name)
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 next_sequence_value(sequence_name)
157
- # avoid #select or #select_one so that the sequence values aren't cached
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 sql_literal?(value)
162
- defined?(::Arel::SqlLiteral) && ::Arel::SqlLiteral === value
266
+ def current_schema
267
+ execute("SELECT sys_context('userenv', 'current_schema') schema FROM dual").first['schema']
163
268
  end
164
269
 
165
- def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
166
- if (id_value && !sql_literal?(id_value)) || pk.nil?
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
- def indexes(table, name = nil)
185
- @connection.indexes(table, name, @connection.connection.meta_data.user_name)
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
- def _execute(sql, name = nil)
189
- case sql.strip
190
- when /\A\(?\s*(select|show)/i then
191
- @connection.execute_query(sql)
192
- else
193
- @connection.execute_update(sql)
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
- end
287
+ end
288
+ end if AR42
196
289
 
197
- def modify_types(tp)
198
- tp[:primary_key] = "NUMBER(38) NOT NULL PRIMARY KEY"
199
- tp[:integer] = { :name => "NUMBER", :limit => 38 }
200
- tp[:datetime] = { :name => "DATE" }
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
- def add_limit_offset!(sql, options) #:nodoc:
208
- offset = options[:offset] || 0
295
+ options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :tablespace, :options, :using)
209
296
 
210
- if limit = options[:limit]
211
- sql.replace "select * from (select raw_sql_.*, rownum raw_rnum_ from (#{sql}) raw_sql_ where rownum <= #{offset+limit}) where raw_rnum_ > #{offset}"
212
- elsif offset > 0
213
- sql.replace "select * from (select raw_sql_.*, rownum raw_rnum_ from (#{sql}) raw_sql_) where raw_rnum_ > #{offset}"
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
- def current_database #:nodoc:
218
- select_one("select sys_context('userenv','db_name') db from dual")["db"]
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
- def remove_index(table_name, options = {}) #:nodoc:
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) #:nodoc:
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
- def add_column_options!(sql, options) #:nodoc:
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
- def change_column(table_name, column_name, type, options = {}) #:nodoc:
238
- change_column_sql = "ALTER TABLE #{table_name} MODIFY #{column_name} #{type_to_sql(type, options[:limit])}"
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
- def rename_column(table_name, column_name, new_column_name) #:nodoc:
244
- execute "ALTER TABLE #{table_name} RENAME COLUMN #{column_name} to #{new_column_name}"
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
- def remove_column(table_name, column_name) #:nodoc:
248
- execute "ALTER TABLE #{table_name} DROP COLUMN #{column_name}"
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
- def structure_dump #:nodoc:
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
- select_all("select table_name from user_tables").inject(s) do |structure, table|
257
- ddl = "create table #{table.to_a.first.last} (\n "
258
- cols = select_all(%Q{
259
- select column_name, data_type, data_length, data_precision, data_scale, data_default, nullable
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
- def structure_drop #:nodoc:
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
- select_all("select table_name from user_tables").inject(s) do |drop, table|
292
- drop << "drop table #{table.to_a.first.last} cascade constraints;\n\n"
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
- return "DISTINCT #{columns}" if order_by.blank?
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 = order_by.split(',').map { |s| s.strip }.reject(&:blank?)
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
- sql = "DISTINCT #{columns}, "
316
- sql << order_columns * ", "
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
- order = options[:order].split(',').collect { |s| s.strip }.reject(&:blank?)
326
- order.map! {|s| $1 if s =~ / (.*)/}
327
- order = order.zip((0...order.size).to_a).map { |s,i| "alias_#{i}__ #{s}" }.join(', ')
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
- sql << "ORDER BY #{order}"
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
- def columns(table_name, name=nil)
337
- @connection.columns_internal(table_name, name, oracle_schema)
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
- # See ACTIVERECORD_JDBC-33 for details -- better to not quote
345
- # table names, esp. if they have schemas.
346
- def quote_table_name(name) #:nodoc:
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
- # Camelcase column names need to be quoted.
351
- # Nonquoted identifiers can contain only alphanumeric characters from your
352
- # database character set and the underscore (_), dollar sign ($), and pound sign (#).
353
- # Database links can also contain periods (.) and "at" signs (@).
354
- # Oracle strongly discourages you from using $ and # in nonquoted identifiers.
355
- # Source: http://download.oracle.com/docs/cd/B28359_01/server.111/b28286/sql_elements008.htm
356
- def quote_column_name(name) #:nodoc:
357
- name.to_s =~ /^[a-z0-9_$#]+$/ ? name.to_s : "\"#{name}\""
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 quote_string(string) #:nodoc:
361
- string.gsub(/'/, "''")
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
- def quote(value, column = nil) #:nodoc:
365
- # Arel 2 passes SqlLiterals through
502
+ # @override
503
+ def quote(value, column = nil)
366
504
  return value if sql_literal?(value)
367
505
 
368
- if column && [:text, :binary].include?(column.type)
369
- if /(.*?)\([0-9]+\)/ =~ column.sql_type
370
- %Q{empty_#{ $1.downcase }()}
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
- %Q{empty_#{ column.sql_type.downcase rescue 'blob' }()}
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
- quoted = super
379
- if value.acts_like?(:date)
380
- quoted = %Q{DATE'#{quoted_date(value)}'}
381
- elsif value.acts_like?(:time)
382
- quoted = %Q{TIMESTAMP'#{quoted_date(value)}'}
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
- def quoted_true #:nodoc:
389
- '1'
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 quoted_false #:nodoc:
393
- '0'
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
- # In Oracle, schemas are created under your username:
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
- @config[:username].to_s if @config[:username]
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
- def select(sql, name=nil)
404
- records = execute(sql,name)
405
- records.each do |col|
406
- col.delete('raw_rnum_')
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
- records
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