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.
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