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