activerecord-jdbc-adapter 1.3.0.beta2 → 1.3.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (282) hide show
  1. data/.gitignore +14 -8
  2. data/.travis.yml +40 -31
  3. data/.yardopts +4 -0
  4. data/Appraisals +2 -5
  5. data/CONTRIBUTING.md +46 -0
  6. data/Gemfile +21 -4
  7. data/Gemfile.lock +42 -17
  8. data/{History.txt → History.md} +142 -75
  9. data/README.md +102 -104
  10. data/RUNNING_TESTS.md +76 -0
  11. data/Rakefile.jdbc +20 -0
  12. data/activerecord-jdbc-adapter.gemspec +35 -18
  13. data/gemfiles/rails23.gemfile +4 -3
  14. data/gemfiles/rails23.gemfile.lock +9 -6
  15. data/gemfiles/rails30.gemfile +4 -3
  16. data/gemfiles/rails30.gemfile.lock +9 -6
  17. data/gemfiles/rails31.gemfile +4 -3
  18. data/gemfiles/rails31.gemfile.lock +9 -6
  19. data/gemfiles/rails32.gemfile +4 -3
  20. data/gemfiles/rails32.gemfile.lock +17 -14
  21. data/gemfiles/rails40.gemfile +5 -5
  22. data/gemfiles/rails40.gemfile.lock +17 -69
  23. data/lib/active_record/connection_adapters/firebird_adapter.rb +1 -0
  24. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +1 -0
  25. data/lib/arel/visitors/compat.rb +22 -3
  26. data/lib/arel/visitors/db2.rb +8 -4
  27. data/lib/arel/visitors/derby.rb +14 -13
  28. data/lib/arel/visitors/firebird.rb +5 -4
  29. data/lib/arel/visitors/hsqldb.rb +11 -9
  30. data/lib/arel/visitors/sql_server.rb +89 -61
  31. data/lib/arjdbc.rb +1 -1
  32. data/lib/arjdbc/db2/adapter.rb +181 -212
  33. data/lib/arjdbc/db2/as400.rb +31 -18
  34. data/lib/arjdbc/db2/column.rb +167 -0
  35. data/lib/arjdbc/db2/connection_methods.rb +2 -0
  36. data/lib/arjdbc/derby/adapter.rb +206 -107
  37. data/lib/arjdbc/derby/connection_methods.rb +4 -9
  38. data/lib/arjdbc/firebird.rb +1 -0
  39. data/lib/arjdbc/firebird/adapter.rb +202 -64
  40. data/lib/arjdbc/firebird/connection_methods.rb +20 -0
  41. data/lib/arjdbc/h2/adapter.rb +56 -36
  42. data/lib/arjdbc/hsqldb/adapter.rb +99 -68
  43. data/lib/arjdbc/jdbc/adapter.rb +474 -265
  44. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  45. data/lib/arjdbc/jdbc/adapter_require.rb +8 -7
  46. data/lib/arjdbc/jdbc/arel_support.rb +132 -0
  47. data/lib/arjdbc/jdbc/base_ext.rb +8 -7
  48. data/lib/arjdbc/jdbc/callbacks.rb +16 -10
  49. data/lib/arjdbc/jdbc/column.rb +25 -3
  50. data/lib/arjdbc/jdbc/connection.rb +28 -55
  51. data/lib/arjdbc/jdbc/extension.rb +14 -14
  52. data/lib/arjdbc/jdbc/java.rb +6 -3
  53. data/lib/arjdbc/jdbc/jdbc.rake +1 -1
  54. data/lib/arjdbc/jdbc/quoted_primary_key.rb +2 -2
  55. data/lib/arjdbc/jdbc/rake_tasks.rb +1 -1
  56. data/lib/arjdbc/jdbc/type_converter.rb +5 -2
  57. data/lib/arjdbc/mssql/adapter.rb +160 -280
  58. data/lib/arjdbc/mssql/column.rb +182 -0
  59. data/lib/arjdbc/mssql/connection_methods.rb +37 -4
  60. data/lib/arjdbc/mssql/explain_support.rb +13 -21
  61. data/lib/arjdbc/mssql/limit_helpers.rb +79 -42
  62. data/lib/arjdbc/mssql/lock_methods.rb +77 -0
  63. data/lib/arjdbc/mssql/utils.rb +11 -11
  64. data/lib/arjdbc/mysql/adapter.rb +165 -247
  65. data/lib/arjdbc/mysql/column.rb +123 -0
  66. data/lib/arjdbc/mysql/connection_methods.rb +3 -6
  67. data/lib/arjdbc/oracle/adapter.rb +282 -288
  68. data/lib/arjdbc/oracle/column.rb +122 -0
  69. data/lib/arjdbc/oracle/connection_methods.rb +3 -0
  70. data/lib/arjdbc/postgresql/adapter.rb +336 -574
  71. data/lib/arjdbc/postgresql/column.rb +458 -0
  72. data/lib/arjdbc/postgresql/connection_methods.rb +1 -2
  73. data/lib/arjdbc/postgresql/schema_creation.rb +38 -0
  74. data/lib/arjdbc/sqlite3/adapter.rb +189 -145
  75. data/lib/arjdbc/sqlite3/explain_support.rb +1 -1
  76. data/lib/arjdbc/tasks/oracle/enhanced_structure_dump.rb +8 -8
  77. data/lib/arjdbc/util/quoted_cache.rb +60 -0
  78. data/lib/arjdbc/util/table_copier.rb +110 -0
  79. data/lib/arjdbc/version.rb +6 -7
  80. data/pom.xml +56 -2
  81. data/rakelib/02-test.rake +72 -83
  82. data/rakelib/db.rake +29 -17
  83. data/src/java/arjdbc/ArJdbcModule.java +21 -18
  84. data/src/java/arjdbc/db2/DB2RubyJdbcConnection.java +84 -12
  85. data/src/java/arjdbc/derby/DerbyModule.java +140 -143
  86. data/src/java/arjdbc/derby/DerbyRubyJdbcConnection.java +58 -7
  87. data/src/java/arjdbc/h2/H2Module.java +43 -0
  88. data/src/java/arjdbc/informix/InformixRubyJdbcConnection.java +7 -6
  89. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +1223 -648
  90. data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +24 -23
  91. data/src/java/arjdbc/mysql/MySQLModule.java +33 -32
  92. data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +147 -30
  93. data/src/java/arjdbc/oracle/OracleModule.java +13 -13
  94. data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +114 -6
  95. data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +166 -36
  96. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +101 -19
  97. data/src/java/arjdbc/util/QuotingUtils.java +19 -19
  98. metadata +240 -394
  99. data/bench/bench_attributes.rb +0 -13
  100. data/bench/bench_attributes_new.rb +0 -14
  101. data/bench/bench_create.rb +0 -12
  102. data/bench/bench_find_all.rb +0 -12
  103. data/bench/bench_find_all_mt.rb +0 -25
  104. data/bench/bench_model.rb +0 -85
  105. data/bench/bench_new.rb +0 -12
  106. data/bench/bench_new_valid.rb +0 -12
  107. data/bench/bench_valid.rb +0 -13
  108. data/lib/arel/engines/sql/compilers/db2_compiler.rb +0 -9
  109. data/lib/arel/engines/sql/compilers/derby_compiler.rb +0 -6
  110. data/lib/arel/engines/sql/compilers/h2_compiler.rb +0 -6
  111. data/lib/arel/engines/sql/compilers/hsqldb_compiler.rb +0 -15
  112. data/lib/arel/engines/sql/compilers/jdbc_compiler.rb +0 -6
  113. data/lib/arel/engines/sql/compilers/mssql_compiler.rb +0 -46
  114. data/lib/arjdbc/jdbc/missing_functionality_helper.rb +0 -98
  115. data/lib/arjdbc/mssql/lock_helpers.rb +0 -76
  116. data/lib/arjdbc/mssql/tsql_methods.rb +0 -58
  117. data/lib/arjdbc/postgresql/column_cast.rb +0 -134
  118. data/test/activerecord/connections/native_jdbc_mysql/connection.rb +0 -25
  119. data/test/activerecord/jall.sh +0 -7
  120. data/test/activerecord/jtest.sh +0 -3
  121. data/test/assets/flowers.jpg +0 -0
  122. data/test/binary.rb +0 -67
  123. data/test/db/db2.rb +0 -43
  124. data/test/db/db2/binary_test.rb +0 -6
  125. data/test/db/db2/has_many_through_test.rb +0 -6
  126. data/test/db/db2/rake_test.rb +0 -82
  127. data/test/db/db2/rake_test_data.sql +0 -35
  128. data/test/db/db2/reset_column_information_test.rb +0 -5
  129. data/test/db/db2/serialize_test.rb +0 -6
  130. data/test/db/db2/simple_test.rb +0 -81
  131. data/test/db/db2/test_helper.rb +0 -6
  132. data/test/db/db2/unit_test.rb +0 -73
  133. data/test/db/derby.rb +0 -12
  134. data/test/db/derby/binary_test.rb +0 -6
  135. data/test/db/derby/migration_test.rb +0 -74
  136. data/test/db/derby/rake_test.rb +0 -96
  137. data/test/db/derby/reset_column_information_test.rb +0 -6
  138. data/test/db/derby/row_locking_test.rb +0 -20
  139. data/test/db/derby/schema_dump_test.rb +0 -5
  140. data/test/db/derby/serialize_test.rb +0 -6
  141. data/test/db/derby/simple_test.rb +0 -173
  142. data/test/db/derby/test_helper.rb +0 -6
  143. data/test/db/derby/unit_test.rb +0 -32
  144. data/test/db/derby/xml_column_test.rb +0 -17
  145. data/test/db/h2.rb +0 -11
  146. data/test/db/h2/binary_test.rb +0 -6
  147. data/test/db/h2/change_column_test.rb +0 -68
  148. data/test/db/h2/identity_column_test.rb +0 -35
  149. data/test/db/h2/offset_test.rb +0 -49
  150. data/test/db/h2/rake_test.rb +0 -98
  151. data/test/db/h2/schema_dump_test.rb +0 -29
  152. data/test/db/h2/serialize_test.rb +0 -6
  153. data/test/db/h2/simple_test.rb +0 -56
  154. data/test/db/hsqldb.rb +0 -11
  155. data/test/db/hsqldb/binary_test.rb +0 -6
  156. data/test/db/hsqldb/rake_test.rb +0 -101
  157. data/test/db/hsqldb/schema_dump_test.rb +0 -19
  158. data/test/db/hsqldb/serialize_test.rb +0 -6
  159. data/test/db/hsqldb/simple_test.rb +0 -17
  160. data/test/db/informix.rb +0 -13
  161. data/test/db/jdbc.rb +0 -16
  162. data/test/db/jdbc_derby.rb +0 -14
  163. data/test/db/jdbc_h2.rb +0 -17
  164. data/test/db/jdbc_mysql.rb +0 -13
  165. data/test/db/jdbc_postgres.rb +0 -23
  166. data/test/db/jndi_config.rb +0 -32
  167. data/test/db/jndi_pooled_config.rb +0 -32
  168. data/test/db/mssql.rb +0 -11
  169. data/test/db/mssql/binary_test.rb +0 -6
  170. data/test/db/mssql/exec_proc_test.rb +0 -46
  171. data/test/db/mssql/identity_insert_test.rb +0 -18
  172. data/test/db/mssql/ignore_system_views_test.rb +0 -40
  173. data/test/db/mssql/limit_offset_test.rb +0 -190
  174. data/test/db/mssql/multibyte_test.rb +0 -16
  175. data/test/db/mssql/multiple_connections_test.rb +0 -71
  176. data/test/db/mssql/rake_test.rb +0 -143
  177. data/test/db/mssql/reset_column_information_test.rb +0 -6
  178. data/test/db/mssql/row_locking_test.rb +0 -7
  179. data/test/db/mssql/serialize_test.rb +0 -6
  180. data/test/db/mssql/simple_test.rb +0 -140
  181. data/test/db/mssql/transaction_test.rb +0 -6
  182. data/test/db/mssql/types_test.rb +0 -205
  183. data/test/db/mssql/unit_test.rb +0 -249
  184. data/test/db/mysql.rb +0 -4
  185. data/test/db/mysql/_rails_test_mysql.32.out +0 -6585
  186. data/test/db/mysql/binary_test.rb +0 -6
  187. data/test/db/mysql/connection_test.rb +0 -51
  188. data/test/db/mysql/index_length_test.rb +0 -58
  189. data/test/db/mysql/multibyte_test.rb +0 -10
  190. data/test/db/mysql/nonstandard_primary_key_test.rb +0 -39
  191. data/test/db/mysql/rake_test.rb +0 -97
  192. data/test/db/mysql/reset_column_information_test.rb +0 -6
  193. data/test/db/mysql/schema_dump_test.rb +0 -228
  194. data/test/db/mysql/serialize_test.rb +0 -6
  195. data/test/db/mysql/simple_test.rb +0 -187
  196. data/test/db/mysql/statement_escaping_test.rb +0 -46
  197. data/test/db/mysql/transaction_test.rb +0 -6
  198. data/test/db/mysql/types_test.rb +0 -30
  199. data/test/db/mysql/unit_test.rb +0 -93
  200. data/test/db/mysql_config.rb +0 -7
  201. data/test/db/oracle.rb +0 -27
  202. data/test/db/oracle/binary_test.rb +0 -6
  203. data/test/db/oracle/limit_test.rb +0 -24
  204. data/test/db/oracle/multibyte_test.rb +0 -22
  205. data/test/db/oracle/rake_test.rb +0 -100
  206. data/test/db/oracle/reset_column_information_test.rb +0 -6
  207. data/test/db/oracle/serialize_test.rb +0 -6
  208. data/test/db/oracle/simple_test.rb +0 -140
  209. data/test/db/oracle/specific_test.rb +0 -180
  210. data/test/db/oracle/transaction_test.rb +0 -31
  211. data/test/db/oracle/unit_test.rb +0 -31
  212. data/test/db/postgres.rb +0 -11
  213. data/test/db/postgres/_rails_test_postgres.32.out +0 -6405
  214. data/test/db/postgres/a_custom_primary_key_test.rb +0 -50
  215. data/test/db/postgres/active_schema_unit_test.rb +0 -68
  216. data/test/db/postgres/array_type_test.rb +0 -101
  217. data/test/db/postgres/binary_test.rb +0 -6
  218. data/test/db/postgres/connection_test.rb +0 -63
  219. data/test/db/postgres/data_types_test.rb +0 -703
  220. data/test/db/postgres/hstore_test.rb +0 -200
  221. data/test/db/postgres/information_schema_leak_test.rb +0 -30
  222. data/test/db/postgres/json_test.rb +0 -86
  223. data/test/db/postgres/ltree_test.rb +0 -51
  224. data/test/db/postgres/mixed_case_test.rb +0 -29
  225. data/test/db/postgres/native_types_test.rb +0 -124
  226. data/test/db/postgres/rake_test.rb +0 -117
  227. data/test/db/postgres/reserved_test.rb +0 -22
  228. data/test/db/postgres/reset_column_information_test.rb +0 -6
  229. data/test/db/postgres/row_locking_test.rb +0 -21
  230. data/test/db/postgres/schema_dump_test.rb +0 -95
  231. data/test/db/postgres/schema_test.rb +0 -115
  232. data/test/db/postgres/simple_test.rb +0 -260
  233. data/test/db/postgres/table_alias_length_test.rb +0 -16
  234. data/test/db/postgres/transaction_test.rb +0 -6
  235. data/test/db/postgres/unit_test.rb +0 -31
  236. data/test/db/postgres_config.rb +0 -10
  237. data/test/db/sqlite3.rb +0 -6
  238. data/test/db/sqlite3/_rails_test_sqlite3.32.out +0 -6274
  239. data/test/db/sqlite3/has_many_though_test.rb +0 -6
  240. data/test/db/sqlite3/rake_test.rb +0 -71
  241. data/test/db/sqlite3/reset_column_information_test.rb +0 -6
  242. data/test/db/sqlite3/schema_dump_test.rb +0 -6
  243. data/test/db/sqlite3/serialize_test.rb +0 -6
  244. data/test/db/sqlite3/simple_test.rb +0 -268
  245. data/test/db/sqlite3/transaction_test.rb +0 -32
  246. data/test/db/sqlite3/type_conversion_test.rb +0 -104
  247. data/test/has_many_through.rb +0 -61
  248. data/test/informix_simple_test.rb +0 -48
  249. data/test/jdbc/db2.rb +0 -36
  250. data/test/jdbc/oracle.rb +0 -34
  251. data/test/jdbc_column_test.rb +0 -23
  252. data/test/jdbc_common.rb +0 -16
  253. data/test/jdbc_connection_test.rb +0 -196
  254. data/test/jndi_callbacks_test.rb +0 -33
  255. data/test/jndi_test.rb +0 -55
  256. data/test/manualTestDatabase.rb +0 -191
  257. data/test/models/add_not_null_column_to_table.rb +0 -9
  258. data/test/models/auto_id.rb +0 -15
  259. data/test/models/binary.rb +0 -18
  260. data/test/models/custom_pk_name.rb +0 -15
  261. data/test/models/data_types.rb +0 -40
  262. data/test/models/entry.rb +0 -41
  263. data/test/models/mixed_case.rb +0 -22
  264. data/test/models/reserved_word.rb +0 -15
  265. data/test/models/rights_and_roles.rb +0 -57
  266. data/test/models/string_id.rb +0 -17
  267. data/test/models/thing.rb +0 -17
  268. data/test/models/topic.rb +0 -32
  269. data/test/models/validates_uniqueness_of_string.rb +0 -19
  270. data/test/rails/mysql.rb +0 -13
  271. data/test/rails/sqlite3/version.rb +0 -6
  272. data/test/rails_stub.rb +0 -31
  273. data/test/rake_test_support.rb +0 -298
  274. data/test/row_locking.rb +0 -102
  275. data/test/schema_dump.rb +0 -182
  276. data/test/serialize.rb +0 -275
  277. data/test/shared_helper.rb +0 -35
  278. data/test/simple.rb +0 -1317
  279. data/test/sybase_jtds_simple_test.rb +0 -28
  280. data/test/sybase_reset_column_information_test.rb +0 -6
  281. data/test/test_helper.rb +0 -304
  282. data/test/transaction.rb +0 -109
@@ -0,0 +1,123 @@
1
+ module ArJdbc
2
+ module MySQL
3
+
4
+ # @see ActiveRecord::ConnectionAdapters::JdbcColumn#column_types
5
+ def self.column_selector
6
+ [ /mysql/i, lambda { |config, column| column.extend(Column) } ]
7
+ end
8
+
9
+ # Column behavior based on (abstract) MySQL adapter in Rails.
10
+ # @see ActiveRecord::ConnectionAdapters::JdbcColumn
11
+ module Column
12
+
13
+ def extract_default(default)
14
+ if sql_type =~ /blob/i || type == :text
15
+ if default.blank?
16
+ return null ? nil : ''
17
+ else
18
+ raise ArgumentError, "#{type} columns cannot have a default value: #{default.inspect}"
19
+ end
20
+ elsif missing_default_forged_as_empty_string?(default)
21
+ nil
22
+ else
23
+ super
24
+ end
25
+ end
26
+
27
+ def has_default?
28
+ return false if sql_type =~ /blob/i || type == :text #mysql forbids defaults on blob and text columns
29
+ super
30
+ end
31
+
32
+ def simplified_type(field_type)
33
+ if adapter.respond_to?(:emulate_booleans) && adapter.emulate_booleans
34
+ return :boolean if field_type.downcase.index('tinyint(1)')
35
+ end
36
+
37
+ case field_type
38
+ when /enum/i, /set/i then :string
39
+ when /year/i then :integer
40
+ # :tinyint : {:name=>"tinyint", :limit=>3}
41
+ # :"tinyint unsigned" : {:name=>"tinyint unsigned", :limit=>3}
42
+ # :bigint : {:name=>"bigint", :limit=>19}
43
+ # :"bigint unsigned" : {:name=>"bigint unsigned", :limit=>20}
44
+ # :integer : {:name=>"integer", :limit=>10}
45
+ # :"integer unsigned" : {:name=>"integer unsigned", :limit=>10}
46
+ # :int : {:name=>"int", :limit=>10}
47
+ # :"int unsigned" : {:name=>"int unsigned", :limit=>10}
48
+ # :mediumint : {:name=>"mediumint", :limit=>7}
49
+ # :"mediumint unsigned" : {:name=>"mediumint unsigned", :limit=>8}
50
+ # :smallint : {:name=>"smallint", :limit=>5}
51
+ # :"smallint unsigned" : {:name=>"smallint unsigned", :limit=>5}
52
+ when /int/i then :integer
53
+ when /double/i then :float # double precision (alias)
54
+ when 'bool' then :boolean
55
+ when 'char' then :string
56
+ # :mediumtext => {:name=>"mediumtext", :limit=>16777215}
57
+ # :longtext => {:name=>"longtext", :limit=>2147483647}
58
+ # :text => {:name=>"text"}
59
+ # :tinytext => {:name=>"tinytext", :limit=>255}
60
+ when /text/i then :text
61
+ when 'long varchar' then :text
62
+ # :"long varbinary" => {:name=>"long varbinary", :limit=>16777215}
63
+ # :varbinary => {:name=>"varbinary", :limit=>255}
64
+ when /binary/i then :binary
65
+ # :mediumblob => {:name=>"mediumblob", :limit=>16777215}
66
+ # :longblob => {:name=>"longblob", :limit=>2147483647}
67
+ # :blob => {:name=>"blob", :limit=>65535}
68
+ # :tinyblob => {:name=>"tinyblob", :limit=>255}
69
+ when /blob/i then :binary
70
+ when /^bit/i then :binary
71
+ else
72
+ super
73
+ end
74
+ end
75
+
76
+ def extract_limit(sql_type)
77
+ case sql_type
78
+ when /blob|text/i
79
+ case sql_type
80
+ when /tiny/i
81
+ 255
82
+ when /medium/i
83
+ 16777215
84
+ when /long/i
85
+ 2147483647 # mysql only allows 2^31-1, not 2^32-1, somewhat inconsistently with the tiny/medium/normal cases
86
+ else
87
+ super # we could return 65535 here, but we leave it undecorated by default
88
+ end
89
+ when /^bigint/i; 8
90
+ when /^int/i; 4
91
+ when /^mediumint/i; 3
92
+ when /^smallint/i; 2
93
+ when /^tinyint/i; 1
94
+ when /^enum\((.+)\)/i # 255
95
+ $1.split(',').map{ |enum| enum.strip.length - 2 }.max
96
+ when /^(bool|date|float|int|time)/i
97
+ nil
98
+ else
99
+ super
100
+ end
101
+ end
102
+
103
+ # MySQL misreports NOT NULL column default when none is given.
104
+ # We can't detect this for columns which may have a legitimate ''
105
+ # default (string) but we can for others (integer, datetime, boolean,
106
+ # and the rest).
107
+ #
108
+ # Test whether the column has default '', is not null, and is not
109
+ # a type allowing default ''.
110
+ def missing_default_forged_as_empty_string?(default)
111
+ type != :string && !null && default == ''
112
+ end
113
+
114
+ def adapter; end
115
+ private :adapter
116
+
117
+ end
118
+
119
+ # @private backwards-compatibility
120
+ ColumnExtensions = Column
121
+
122
+ end
123
+ end
@@ -11,17 +11,14 @@ ArJdbc::ConnectionMethods.module_eval do
11
11
  config[:driver] ||= defined?(::Jdbc::MySQL.driver_name) ? ::Jdbc::MySQL.driver_name : 'com.mysql.jdbc.Driver'
12
12
  config[:adapter_spec] ||= ::ArJdbc::MySQL
13
13
  config[:adapter_class] = ActiveRecord::ConnectionAdapters::MysqlAdapter unless config.key?(:adapter_class)
14
- # config[:connection_alive_sql] ||= 'SELECT 1'
15
-
14
+
16
15
  options = (config[:options] ||= {})
17
16
  options['zeroDateTimeBehavior'] ||= 'convertToNull'
18
17
  options['jdbcCompliantTruncation'] ||= 'false'
19
18
  options['useUnicode'] ||= 'true'
20
19
  options['characterEncoding'] = config[:encoding] || 'utf8'
21
-
22
- connection = jdbc_connection(config)
23
- ::ArJdbc::MySQL.kill_cancel_timer(connection.raw_connection)
24
- connection
20
+
21
+ jdbc_connection(config)
25
22
  end
26
23
  alias_method :jdbcmysql_connection, :mysql_connection
27
24
  alias_method :mysql2_connection, :mysql_connection
@@ -1,15 +1,20 @@
1
1
  ArJdbc.load_java_part :Oracle
2
2
 
3
+ require 'arjdbc/oracle/column'
4
+
3
5
  module ArJdbc
4
6
  module Oracle
5
-
7
+
8
+ # @private
6
9
  def self.extended(adapter); initialize!; end
7
-
10
+
11
+ # @private
8
12
  @@_initialized = nil
9
-
13
+
14
+ # @private
10
15
  def self.initialize!
11
16
  return if @@_initialized; @@_initialized = true
12
-
17
+
13
18
  require 'arjdbc/jdbc/serialized_attributes_helper'
14
19
  ActiveRecord::Base.class_eval do
15
20
  def after_save_with_oracle_lob
@@ -17,11 +22,7 @@ module ArJdbc
17
22
  value = ::ArJdbc::SerializedAttributesHelper.dump_column_value(self, column)
18
23
  next if value.nil? || (value == '')
19
24
 
20
- self.class.connection.write_large_object(
21
- column.type == :binary, column.name,
22
- self.class.table_name, self.class.primary_key,
23
- self.class.connection.quote(id), value
24
- )
25
+ self.class.connection.update_lob_value(self, column, value)
25
26
  end
26
27
  end
27
28
  end
@@ -33,160 +34,60 @@ module ArJdbc
33
34
  ActiveRecord::Base.extend ArJdbc::QuotedPrimaryKeyExtension
34
35
  end
35
36
  end
36
-
37
- def self.column_selector
38
- [ /oracle/i, lambda { |cfg, column| column.extend(::ArJdbc::Oracle::Column) } ]
39
- end
40
37
 
38
+ # @see ActiveRecord::ConnectionAdapters::JdbcAdapter#jdbc_connection_class
41
39
  def self.jdbc_connection_class
42
40
  ::ActiveRecord::ConnectionAdapters::OracleJdbcConnection
43
41
  end
44
42
 
43
+ # @see ActiveRecord::ConnectionAdapters::JdbcAdapter#jdbc_column_class
45
44
  def jdbc_column_class
46
45
  ::ActiveRecord::ConnectionAdapters::OracleColumn
47
46
  end
48
47
 
48
+ # @private
49
49
  @@emulate_booleans = true
50
-
50
+
51
51
  # Boolean emulation can be disabled using :
52
- #
52
+ #
53
53
  # ArJdbc::Oracle.emulate_booleans = false
54
- #
54
+ #
55
55
  # @see ActiveRecord::ConnectionAdapters::OracleAdapter#emulate_booleans
56
56
  def self.emulate_booleans; @@emulate_booleans; end
57
57
  def self.emulate_booleans=(emulate); @@emulate_booleans = emulate; end
58
-
59
- module Column
60
-
61
- def primary=(value)
62
- super
63
- @type = :integer if value && @sql_type =~ /^NUMBER$/i
64
- end
65
-
66
- def type_cast(value)
67
- return nil if value.nil?
68
- case type
69
- when :datetime then Column.string_to_time(value)
70
- when :timestamp then Column.string_to_time(value)
71
- when :boolean then Column.value_to_boolean(value)
72
- else
73
- super
74
- end
75
- end
76
-
77
- def type_cast_code(var_name)
78
- case type
79
- when :datetime then "ArJdbc::Oracle::Column.string_to_time(#{var_name})"
80
- when :timestamp then "ArJdbc::Oracle::Column.string_to_time(#{var_name})"
81
- when :boolean then "ArJdbc::Oracle::Column.value_to_boolean(#{var_name})"
82
- else
83
- super
84
- end
85
- end
86
-
87
- # convert a value to a boolean
88
- def self.value_to_boolean(value)
89
- # NOTE: Oracle JDBC meta-data gets us DECIMAL for NUMBER(1) values
90
- # thus we're likely to get a column back as BigDecimal (e.g. 1.0)
91
- if value.is_a?(String)
92
- value.blank? ? nil : value == '1'
93
- elsif value.is_a?(Numeric)
94
- value.to_i == 1 # <BigDecimal:7b5bfe,'0.1E1',1(4)>
95
- else
96
- !! value
97
- end
98
- end
99
-
100
- def self.string_to_time(string)
101
- return string unless string.is_a?(String)
102
- return nil if string.empty?
103
- return Time.now if string.index('CURRENT') == 0 # TODO seems very wrong
104
-
105
- ::ActiveRecord::ConnectionAdapters::JdbcColumn.string_to_time(string)
106
- end
107
-
108
- def self.string_to_dummy_time(string)
109
- ::ActiveRecord::ConnectionAdapters::JdbcColumn.string_to_dummy_time(string)
110
- end
111
-
112
- def self.guess_date_or_time(value)
113
- return value if value.is_a? Date
114
- ( value && value.hour == 0 && value.min == 0 && value.sec == 0 ) ?
115
- Date.new(value.year, value.month, value.day) : value
116
- end
117
-
118
- private
119
-
120
- def extract_limit(sql_type)
121
- case sql_type
122
- when /^(clob|date)/i then nil
123
- when /^xml/i then @sql_type = 'XMLTYPE'; nil
124
- else super
125
- end
126
- end
127
-
128
- def simplified_type(field_type)
129
- case field_type
130
- when /char/i then :string
131
- when /float|double/i then :float
132
- when /int/i then :integer
133
- when /^number\(1\)$/i then Oracle.emulate_booleans ? :boolean : :integer
134
- when /^num|dec|real/i then extract_scale(field_type) == 0 ? :integer : :decimal
135
- # Oracle TIMESTAMP stores the date and time to up to 9 digits of sub-second precision
136
- when /TIMESTAMP/i then :timestamp
137
- # Oracle DATE stores the date and time to the second
138
- when /DATE|TIME/i then :datetime
139
- when /CLOB/i then :text
140
- when /BLOB/i then :binary
141
- when /XML/i then :xml
142
- else
143
- super
144
- end
145
- end
146
-
147
- # Post process default value from JDBC into a Rails-friendly format (columns{-internal})
148
- def default_value(value)
149
- return nil unless value
150
- value = value.strip # Not sure why we need this for Oracle?
151
- upcase = value.upcase
152
58
 
153
- return nil if upcase == "NULL"
154
- # SYSDATE default should be treated like a NULL value
155
- return nil if upcase == "SYSDATE"
156
- # jdbc returns column default strings with actual single quotes around the value.
157
- return $1 if value =~ /^'(.*)'$/
158
-
159
- value
160
- end
161
-
162
- end
163
-
164
- class TableDefinition < ::ActiveRecord::ConnectionAdapters::TableDefinition # :nodoc:
59
+ class TableDefinition < ::ActiveRecord::ConnectionAdapters::TableDefinition
165
60
  def raw(*args)
166
61
  options = args.extract_options!
167
62
  column(args[0], 'raw', options)
168
63
  end
169
-
64
+
170
65
  def xml(*args)
171
66
  options = args.extract_options!
172
67
  column(args[0], 'xml', options)
173
68
  end
174
69
  end
175
-
70
+
176
71
  def table_definition(*args)
177
72
  new_table_definition(TableDefinition, *args)
178
73
  end
179
74
 
180
- def self.arel2_visitors(config)
181
- { 'oracle' => Arel::Visitors::Oracle }
75
+ def self.arel_visitor_type(config = nil)
76
+ ::Arel::Visitors::Oracle
182
77
  end
183
78
 
184
- ADAPTER_NAME = 'Oracle'
185
-
79
+ # @see ActiveRecord::ConnectionAdapters::JdbcAdapter#bind_substitution
80
+ # @private
81
+ class BindSubstitution < ::Arel::Visitors::Oracle
82
+ include ::Arel::Visitors::BindVisitor
83
+ end if defined? ::Arel::Visitors::BindVisitor
84
+
85
+ ADAPTER_NAME = 'Oracle'.freeze
86
+
186
87
  def adapter_name
187
88
  ADAPTER_NAME
188
89
  end
189
-
90
+
190
91
  NATIVE_DATABASE_TYPES = {
191
92
  :primary_key => "NUMBER(38) NOT NULL PRIMARY KEY",
192
93
  :string => { :name => "VARCHAR2", :limit => 255 },
@@ -207,7 +108,7 @@ module ArJdbc
207
108
  def native_database_types
208
109
  super.merge(NATIVE_DATABASE_TYPES)
209
110
  end
210
-
111
+
211
112
  def modify_types(types)
212
113
  super(types)
213
114
  NATIVE_DATABASE_TYPES.each do |key, value|
@@ -215,63 +116,72 @@ module ArJdbc
215
116
  end
216
117
  types
217
118
  end
218
-
219
- def prefetch_primary_key?(table_name = nil)
220
- columns(table_name).detect {|c| c.primary } if table_name
221
- end
222
119
 
223
120
  # Prevent ORA-01795 for in clauses with more than 1000
224
- def in_clause_length # :nodoc:
121
+ def in_clause_length
225
122
  1000
226
123
  end
227
124
  alias_method :ids_in_list_limit, :in_clause_length
228
-
229
- IDENTIFIER_LENGTH = 30 # :nodoc:
230
-
125
+
126
+ IDENTIFIER_LENGTH = 30
127
+
231
128
  # maximum length of Oracle identifiers is 30
232
- def table_alias_length; IDENTIFIER_LENGTH; end # :nodoc:
233
- def table_name_length; IDENTIFIER_LENGTH; end # :nodoc:
234
- def index_name_length; IDENTIFIER_LENGTH; end # :nodoc:
235
- def column_name_length; IDENTIFIER_LENGTH; end # :nodoc:
129
+ def table_alias_length; IDENTIFIER_LENGTH; end
130
+ def table_name_length; IDENTIFIER_LENGTH; end
131
+ def index_name_length; IDENTIFIER_LENGTH; end
132
+ def column_name_length; IDENTIFIER_LENGTH; end
236
133
 
237
- def default_sequence_name(table_name, column = nil) # :nodoc:
134
+ def default_sequence_name(table_name, column = nil)
135
+ # TODO: remove schema prefix if present (before truncating)
238
136
  "#{table_name.to_s[0, IDENTIFIER_LENGTH - 4]}_seq"
239
137
  end
240
138
 
241
- def create_table(name, options = {}) #:nodoc:
139
+ # @override
140
+ def create_table(name, options = {})
242
141
  super(name, options)
243
- seq_name = options[:sequence_name] || default_sequence_name(name)
244
- start_value = options[:sequence_start_value] || 10000
245
- raise ActiveRecord::StatementInvalid.new("name #{seq_name} too long") if seq_name.length > table_alias_length
246
- execute "CREATE SEQUENCE #{seq_name} START WITH #{start_value}" unless options[:id] == false
142
+ unless options[:id] == false
143
+ seq_name = options[:sequence_name] || default_sequence_name(name)
144
+ start_value = options[:sequence_start_value] || 10000
145
+ raise ActiveRecord::StatementInvalid.new("name #{seq_name} too long") if seq_name.length > table_alias_length
146
+ execute "CREATE SEQUENCE #{quote_table_name(seq_name)} START WITH #{start_value}"
147
+ end
247
148
  end
248
149
 
249
- def rename_table(name, new_name) #:nodoc:
250
- execute "RENAME #{name} TO #{new_name}"
251
- execute "RENAME #{name}_seq TO #{new_name}_seq" rescue nil
150
+ # @override
151
+ def rename_table(name, new_name)
152
+ if new_name.to_s.length > table_name_length
153
+ raise ArgumentError, "New table name '#{new_name}' is too long; the limit is #{table_name_length} characters"
154
+ end
155
+ if "#{new_name}_seq".to_s.length > sequence_name_length
156
+ raise ArgumentError, "New sequence name '#{new_name}_seq' is too long; the limit is #{sequence_name_length} characters"
157
+ end
158
+ execute "RENAME #{quote_table_name(name)} TO #{quote_table_name(new_name)}"
159
+ execute "RENAME #{quote_table_name("#{name}_seq")} TO #{quote_table_name("#{new_name}_seq")}" rescue nil
252
160
  end
253
161
 
254
- def drop_table(name, options = {}) #:nodoc:
255
- super(name) rescue nil
162
+ # @override
163
+ def drop_table(name, options = {})
164
+ super(name)
256
165
  seq_name = options[:sequence_name] || default_sequence_name(name)
257
- execute "DROP SEQUENCE #{seq_name}" rescue nil
166
+ execute "DROP SEQUENCE #{quote_table_name(seq_name)}" rescue nil
258
167
  end
259
-
260
- def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
168
+
169
+ # @override
170
+ def type_to_sql(type, limit = nil, precision = nil, scale = nil)
261
171
  case type.to_sym
262
172
  when :binary
263
173
  # { BLOB | BINARY LARGE OBJECT } [ ( length [{K |M |G }] ) ]
264
174
  # although Oracle does not like limit (length) with BLOB (or CLOB) :
265
- #
175
+ #
266
176
  # CREATE TABLE binaries (data BLOB, short_data BLOB(1024));
267
177
  # ORA-00907: missing right parenthesis *
268
178
  #
269
179
  # TODO do we need to worry about NORMAL vs. non IN-TABLE BLOBs ?!
270
180
  # http://dba.stackexchange.com/questions/8770/improve-blob-writing-performance-in-oracle-11g
271
- # - if the LOB is smaller than 3900 bytes it can be stored inside the
272
- # table row; by default this is enabled,
181
+ # - if the LOB is smaller than 3900 bytes it can be stored inside the
182
+ # table row; by default this is enabled,
273
183
  # unless you specify DISABLE STORAGE IN ROW
274
- # - normal LOB - stored in a separate segment, outside of table,
184
+ # - normal LOB - stored in a separate segment, outside of table,
275
185
  # you may even put it in another tablespace;
276
186
  super(type, nil, nil, nil)
277
187
  when :text
@@ -280,88 +190,63 @@ module ArJdbc
280
190
  super
281
191
  end
282
192
  end
283
-
284
- def next_sequence_value(sequence_name)
285
- # avoid #select or #select_one so that the sequence values aren't cached
286
- execute("SELECT #{quote_table_name(sequence_name)}.nextval id FROM dual").first['id'].to_i
287
- end
288
193
 
289
194
  def sql_literal?(value)
290
195
  defined?(::Arel::SqlLiteral) && ::Arel::SqlLiteral === value
291
196
  end
197
+ private :sql_literal?
292
198
 
293
- def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = []) # :nodoc:
294
- if (id_value && ! sql_literal?(id_value)) || pk.nil?
295
- # Pre-assigned id or table without a primary key
296
- # Presence of #to_sql means an Arel literal bind variable
297
- # that should use #execute_id_insert below
298
- value = exec_insert(to_sql(sql, binds), name, binds)
299
- id_value || last_inserted_id(value) # super
300
- else
301
- # Assume the sql contains a bind-variable for the id
302
- # Extract the table from the insert sql. Yuck.
303
- sequence_name ||= begin
304
- table = extract_table_ref_from_insert_sql(sql)
305
- default_sequence_name(table)
306
- end
307
- id_value = next_sequence_value(sequence_name)
308
- log(sql, name) { @connection.execute_id_insert(sql, id_value) }
309
- id_value
310
- end
311
- end
312
-
313
199
  def indexes(table, name = nil)
314
200
  @connection.indexes(table, name, @connection.connection.meta_data.user_name)
315
201
  end
316
202
 
317
- def add_limit_offset!(sql, options) #:nodoc:
203
+ # @note Only used with (non-AREL) ActiveRecord **2.3**.
204
+ # @see Arel::Visitors::Oracle
205
+ def add_limit_offset!(sql, options)
318
206
  offset = options[:offset] || 0
319
-
320
207
  if limit = options[:limit]
321
- sql.replace "select * from (select raw_sql_.*, rownum raw_rnum_ from (#{sql}) raw_sql_ where rownum <= #{offset+limit}) where raw_rnum_ > #{offset}"
208
+ sql.replace "SELECT * FROM " <<
209
+ "(select raw_sql_.*, rownum raw_rnum_ from (#{sql}) raw_sql_ where rownum <= #{offset + limit})" <<
210
+ " WHERE raw_rnum_ > #{offset}"
322
211
  elsif offset > 0
323
- sql.replace "select * from (select raw_sql_.*, rownum raw_rnum_ from (#{sql}) raw_sql_) where raw_rnum_ > #{offset}"
212
+ sql.replace "SELECT * FROM " <<
213
+ "(select raw_sql_.*, rownum raw_rnum_ from (#{sql}) raw_sql_)" <<
214
+ " WHERE raw_rnum_ > #{offset}"
324
215
  end
325
- end
216
+ end if ::ActiveRecord::VERSION::MAJOR < 3
326
217
 
327
- def current_user # :nodoc:
218
+ def current_user
328
219
  @current_user ||= execute("SELECT sys_context('userenv', 'session_user') su FROM dual").first['su']
329
220
  end
330
-
331
- def current_database # :nodoc:
221
+
222
+ def current_database
332
223
  @current_database ||= execute("SELECT sys_context('userenv', 'db_name') db FROM dual").first['db']
333
224
  end
334
225
 
335
- def current_schema # :nodoc:
226
+ def current_schema
336
227
  execute("SELECT sys_context('userenv', 'current_schema') schema FROM dual").first['schema']
337
228
  end
338
229
 
339
230
  def current_schema=(schema_owner)
340
231
  execute("ALTER SESSION SET current_schema=#{schema_owner}")
341
232
  end
342
-
343
- def create_savepoint # :nodoc:
344
- execute("SAVEPOINT #{current_savepoint_name}")
345
- end
346
233
 
347
- def rollback_to_savepoint # :nodoc:
348
- execute("ROLLBACK TO #{current_savepoint_name}")
234
+ # @override
235
+ def release_savepoint(name = nil)
236
+ # no RELEASE SAVEPOINT statement in Oracle (JDBC driver throws "Unsupported feature")
349
237
  end
350
238
 
351
- def release_savepoint # :nodoc:
352
- # no RELEASE SAVEPOINT statement in Oracle
353
- end
354
-
355
- def remove_index(table_name, options = {}) #:nodoc:
239
+ def remove_index(table_name, options = {})
356
240
  execute "DROP INDEX #{index_name(table_name, options)}"
357
241
  end
358
242
 
359
- def change_column_default(table_name, column_name, default) #:nodoc:
360
- execute "ALTER TABLE #{quote_table_name(table_name)} " +
243
+ def change_column_default(table_name, column_name, default)
244
+ execute "ALTER TABLE #{quote_table_name(table_name)} " +
361
245
  "MODIFY #{quote_column_name(column_name)} DEFAULT #{quote(default)}"
362
246
  end
363
247
 
364
- def add_column_options!(sql, options) #:nodoc:
248
+ # @override
249
+ def add_column_options!(sql, options)
365
250
  # handle case of defaults for CLOB columns, which would otherwise get "quoted" incorrectly
366
251
  if options_include_default?(options) && (column = options[:column]) && column.type == :text
367
252
  sql << " DEFAULT #{quote(options.delete(:default))}"
@@ -369,24 +254,27 @@ module ArJdbc
369
254
  super
370
255
  end
371
256
 
372
- def change_column(table_name, column_name, type, options = {}) #:nodoc:
257
+ # @override
258
+ def change_column(table_name, column_name, type, options = {})
373
259
  change_column_sql = "ALTER TABLE #{quote_table_name(table_name)} " <<
374
260
  "MODIFY #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit])}"
375
261
  add_column_options!(change_column_sql, options)
376
262
  execute(change_column_sql)
377
263
  end
378
264
 
379
- def rename_column(table_name, column_name, new_column_name) #:nodoc:
265
+ # @override
266
+ def rename_column(table_name, column_name, new_column_name)
380
267
  execute "ALTER TABLE #{quote_table_name(table_name)} " <<
381
268
  "RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
382
269
  end
383
-
384
- def remove_column(table_name, *column_names) #:nodoc:
270
+
271
+ # @override
272
+ def remove_column(table_name, *column_names)
385
273
  for column_name in column_names.flatten
386
274
  execute "ALTER TABLE #{quote_table_name(table_name)} DROP COLUMN #{quote_column_name(column_name)}"
387
275
  end
388
276
  end
389
-
277
+
390
278
  # SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
391
279
  #
392
280
  # Oracle requires the ORDER BY columns to be in the SELECT list for DISTINCT
@@ -422,7 +310,7 @@ module ArJdbc
422
310
 
423
311
  sql << "ORDER BY #{order.join(', ')}"
424
312
  end
425
-
313
+
426
314
  def extract_order_columns(order_by)
427
315
  columns = order_by.split(',')
428
316
  columns.map!(&:strip); columns.reject!(&:blank?)
@@ -430,24 +318,24 @@ module ArJdbc
430
318
  columns.zip( (0...columns.size).to_a )
431
319
  end
432
320
  private :extract_order_columns
433
-
434
- def temporary_table?(table_name) # :nodoc:
321
+
322
+ def temporary_table?(table_name)
435
323
  select_value("SELECT temporary FROM user_tables WHERE table_name = '#{table_name.upcase}'") == 'Y'
436
324
  end
437
-
438
- def tables # :nodoc:
325
+
326
+ def tables
439
327
  @connection.tables(nil, oracle_schema)
440
328
  end
441
-
329
+
442
330
  # NOTE: better to use current_schema instead of the configured one ?!
443
- def columns(table_name, name = nil) # :nodoc:
331
+ def columns(table_name, name = nil)
444
332
  @connection.columns_internal(table_name.to_s, nil, oracle_schema)
445
333
  end
446
-
334
+
447
335
  def tablespace(table_name)
448
336
  select_value "SELECT tablespace_name FROM user_tables WHERE table_name='#{table_name.to_s.upcase}'"
449
337
  end
450
-
338
+
451
339
  def charset
452
340
  database_parameters['NLS_CHARACTERSET']
453
341
  end
@@ -455,25 +343,27 @@ module ArJdbc
455
343
  def collation
456
344
  database_parameters['NLS_COMP']
457
345
  end
458
-
346
+
459
347
  def database_parameters
460
348
  return @database_parameters unless ( @database_parameters ||= {} ).empty?
461
- @connection.execute_query_raw("SELECT * FROM NLS_DATABASE_PARAMETERS") do
349
+ @connection.execute_query_raw("SELECT * FROM NLS_DATABASE_PARAMETERS") do
462
350
  |name, value| @database_parameters[name] = value
463
351
  end
464
352
  @database_parameters
465
353
  end
466
-
354
+
467
355
  # QUOTING ==================================================
468
-
469
- def quote_table_name(name) # :nodoc:
356
+
357
+ # @override
358
+ def quote_table_name(name)
470
359
  name.to_s.split('.').map{ |n| n.split('@').map{ |m| quote_column_name(m) }.join('@') }.join('.')
471
360
  end
472
-
473
- def quote_column_name(name) #:nodoc:
361
+
362
+ # @override
363
+ def quote_column_name(name)
474
364
  # if only valid lowercase column characters in name
475
365
  if ( name = name.to_s ) =~ /\A[a-z][a-z_0-9\$#]*\Z/
476
- # putting double-quotes around an identifier causes Oracle to treat the
366
+ # putting double-quotes around an identifier causes Oracle to treat the
477
367
  # identifier as case sensitive (otherwise assumes case-insensitivity) !
478
368
  # all upper case is an exception, where double-quotes are meaningless
479
369
  "\"#{name.upcase}\"" # name.upcase
@@ -482,10 +372,16 @@ module ArJdbc
482
372
  "\"#{name.gsub('"', '')}\""
483
373
  end
484
374
  end
485
-
486
- def quote(value, column = nil) # :nodoc:
375
+
376
+ def unquote_table_name(name)
377
+ name = name[1...-1] if name[0, 1] == '"'
378
+ name.upcase == name ? name.downcase : name
379
+ end
380
+
381
+ # @override
382
+ def quote(value, column = nil)
487
383
  return value if sql_literal?(value) # Arel 2 passes SqlLiterals through
488
-
384
+
489
385
  column_type = column && column.type
490
386
  if column_type == :text || column_type == :binary
491
387
  if /(.*?)\([0-9]+\)/ =~ column.sql_type
@@ -501,34 +397,67 @@ module ArJdbc
501
397
  if column.respond_to?(:primary) && column.primary && column.klass != String
502
398
  return value.to_i.to_s
503
399
  end
504
- quoted = super
505
- if value.acts_like?(:date)
506
- quoted = %Q{DATE'#{quoted_date(value)}'}
507
- elsif value.acts_like?(:time)
508
- quoted = %Q{TIMESTAMP'#{quoted_date(value)}'}
400
+
401
+ if column_type == :datetime || column_type == :time
402
+ if value.acts_like?(:time)
403
+ %Q{TO_DATE('#{get_time(value).strftime("%Y-%m-%d %H:%M:%S")}','YYYY-MM-DD HH24:MI:SS')}
404
+ else
405
+ value.blank? ? 'NULL' : %Q{DATE'#{value}'} # assume correctly formated DATE (string)
406
+ end
407
+ elsif ( like_date = value.acts_like?(:date) ) || column_type == :date
408
+ if value.acts_like?(:time) # value.respond_to?(:strftime)
409
+ %Q{DATE'#{get_time(value).strftime("%Y-%m-%d")}'}
410
+ elsif like_date
411
+ %Q{DATE'#{quoted_date(value)}'} # DATE 'YYYY-MM-DD'
412
+ else
413
+ value.blank? ? 'NULL' : %Q{DATE'#{value}'} # assume correctly formated DATE (string)
414
+ end
415
+ elsif ( like_time = value.acts_like?(:time) ) || column_type == :timestamp
416
+ if like_time
417
+ %Q{TIMESTAMP'#{quoted_date(value, true)}'} # TIMESTAMP 'YYYY-MM-DD HH24:MI:SS.FF'
418
+ else
419
+ value.blank? ? 'NULL' : %Q{TIMESTAMP'#{value}'} # assume correctly formated TIMESTAMP (string)
420
+ end
421
+ else
422
+ super
509
423
  end
510
- quoted
511
424
  end
512
425
  end
513
-
514
- def quote_raw(value) # :nodoc:
426
+
427
+ # Quote date/time values for use in SQL input.
428
+ # Includes milliseconds if the value is a Time responding to usec.
429
+ # @override
430
+ def quoted_date(value, time = nil)
431
+ if time || ( time.nil? && value.acts_like?(:time) )
432
+ usec = value.respond_to?(:usec) && (value.usec / 10000.0).round # .428000 -> .43
433
+ return "#{get_time(value).to_s(:db)}.#{sprintf("%02d", usec)}" if usec
434
+ # value.strftime("%Y-%m-%d %H:%M:%S")
435
+ end
436
+ value.to_s(:db)
437
+ end
438
+
439
+ def quote_raw(value)
515
440
  value = value.unpack('C*') if value.is_a?(String)
516
441
  "'#{value.map { |x| "%02X" % x }.join}'"
517
442
  end
518
-
519
- def supports_migrations? # :nodoc:
443
+
444
+ # @override
445
+ def supports_migrations?
520
446
  true
521
447
  end
522
448
 
523
- def supports_primary_key? # :nodoc:
449
+ # @override
450
+ def supports_primary_key?
524
451
  true
525
452
  end
526
453
 
527
- def supports_savepoints? # :nodoc:
454
+ # @override
455
+ def supports_savepoints?
528
456
  true
529
457
  end
530
-
531
- def supports_explain? # :nodoc:
458
+
459
+ # @override
460
+ def supports_explain?
532
461
  true
533
462
  end
534
463
 
@@ -545,35 +474,116 @@ module ArJdbc
545
474
  result.each { |row| row.delete('raw_rnum_') } # Hash rows even for AR::Result
546
475
  result
547
476
  end
548
-
549
- # @override as <code>#execute_insert</code> not working for Oracle e.g.
550
- # getLong not implemented for class oracle.jdbc.driver.T4CRowidAccessor:
551
- # INSERT INTO binaries (data, id, name, short_data) VALUES (?, ?, ?, ?)
552
- def exec_insert(sql, name, binds, pk = nil, sequence_name = nil) # :nodoc:
553
- execute(sql, name, binds)
477
+
478
+ # Returns true for Oracle adapter (since Oracle requires primary key
479
+ # values to be pre-fetched before insert).
480
+ # @see #next_sequence_value
481
+ # @override
482
+ def prefetch_primary_key?(table_name = nil)
483
+ return true if table_name.nil?
484
+ table_name = table_name.to_s
485
+ columns(table_name).detect { |column| column.primary }
486
+ end
487
+
488
+ # @override
489
+ def next_sequence_value(sequence_name)
490
+ sequence_name = quote_table_name(sequence_name)
491
+ sql = "SELECT #{sequence_name}.NEXTVAL id FROM dual"
492
+ log(sql, 'SQL') { @connection.next_sequence_value(sequence_name) }
493
+ end
494
+
495
+ # @override (for AR <= 3.0)
496
+ def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
497
+ # if PK is already pre-fetched from sequence or if there is no PK :
498
+ if id_value || pk.nil?
499
+ execute(sql, name)
500
+ return id_value
501
+ end
502
+
503
+ if pk && use_insert_returning? # true by default on AR <= 3.0
504
+ sql = "#{sql} RETURNING #{quote_column_name(pk)}"
505
+ end
506
+ execute(sql, name)
507
+ end
508
+ protected :insert_sql
509
+
510
+ # @override
511
+ def sql_for_insert(sql, pk, id_value, sequence_name, binds)
512
+ unless id_value || pk.nil?
513
+ if pk && use_insert_returning?
514
+ sql = "#{sql} RETURNING #{quote_column_name(pk)}"
515
+ end
516
+ end
517
+ [ sql, binds ]
518
+ end
519
+
520
+ # @override
521
+ def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
522
+ # NOTE: ActiveRecord::Relation calls our {#next_sequence_value}
523
+ # (from its `insert`) and passes the returned id_value here ...
524
+ sql, binds = sql_for_insert(to_sql(arel, binds), pk, id_value, sequence_name, binds)
525
+ if id_value
526
+ exec_update(sql, name, binds)
527
+ return id_value
528
+ else
529
+ value = exec_insert(sql, name, binds, pk, sequence_name)
530
+ id_value || last_inserted_id(value)
531
+ end
532
+ end
533
+
534
+ # @override
535
+ def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
536
+ if pk && use_insert_returning?
537
+ exec_query(sql, name, binds) # due RETURNING clause
538
+ else
539
+ super(sql, name, binds) # assume no generated id for table
540
+ end
541
+ end
542
+
543
+ def next_id_value(sql, sequence_name = nil)
544
+ # Assume the SQL contains a bind-variable for the ID
545
+ sequence_name ||= begin
546
+ # Extract the table from the insert SQL. Yuck.
547
+ table = extract_table_ref_from_insert_sql(sql)
548
+ default_sequence_name(table)
549
+ end
550
+ next_sequence_value(sequence_name)
554
551
  end
555
-
552
+ private :next_id_value
553
+
554
+ def use_insert_returning?
555
+ if ( @use_insert_returning ||= nil ).nil?
556
+ @use_insert_returning = false
557
+ end
558
+ @use_insert_returning
559
+ end
560
+
556
561
  private
557
-
562
+
558
563
  def _execute(sql, name = nil)
559
564
  if self.class.select?(sql)
560
565
  @connection.execute_query_raw(sql)
566
+ elsif self.class.insert?(sql)
567
+ @connection.execute_insert(sql)
561
568
  else
562
569
  @connection.execute_update(sql)
563
570
  end
564
571
  end
565
-
566
- def extract_table_ref_from_insert_sql(sql) # :nodoc:
567
- table = sql.split(" ", 4)[2].gsub('"', '')
568
- ( idx = table.index('(') ) ? table[0...idx] : table # INTO table(col1, col2) ...
572
+
573
+ def extract_table_ref_from_insert_sql(sql)
574
+ table = sql.split(" ", 4)[2]
575
+ if idx = table.index('(')
576
+ table = table[0...idx] # INTO table(col1, col2) ...
577
+ end
578
+ unquote_table_name(table)
569
579
  end
570
-
580
+
571
581
  # In Oracle, schemas are usually created under your username :
572
582
  # http://www.oracle.com/technology/obe/2day_dba/schema/schema.htm
573
- #
583
+ #
574
584
  # A schema is the set of objects (tables, views, indexes, etc) that belongs
575
585
  # to an user, often used as another way to refer to an Oracle user account.
576
- #
586
+ #
577
587
  # But allow separate configuration as "schema:" anyway (see #53)
578
588
  def oracle_schema
579
589
  if @config[:schema]
@@ -586,52 +596,36 @@ module ArJdbc
586
596
  end
587
597
  end
588
598
 
599
+ require 'arjdbc/util/quoted_cache'
600
+
589
601
  module ActiveRecord::ConnectionAdapters
590
-
602
+
591
603
  remove_const(:OracleAdapter) if const_defined?(:OracleAdapter)
592
604
 
593
605
  class OracleAdapter < JdbcAdapter
594
606
  include ::ArJdbc::Oracle
595
-
596
- # By default, the MysqlAdapter will consider all columns of type
607
+ include ::ArJdbc::Util::QuotedCache
608
+
609
+ # By default, the MysqlAdapter will consider all columns of type
597
610
  # <tt>tinyint(1)</tt> as boolean. If you wish to disable this :
598
611
  #
599
612
  # ActiveRecord::ConnectionAdapters::OracleAdapter.emulate_booleans = false
600
613
  #
601
614
  def self.emulate_booleans; ::ArJdbc::Oracle.emulate_booleans; end
602
615
  def self.emulate_booleans=(emulate); ::ArJdbc::Oracle.emulate_booleans = emulate; end
603
-
616
+
604
617
  def initialize(*args)
605
618
  ::ArJdbc::Oracle.initialize!
606
619
  super # configure_connection happens in super
607
- end
608
-
609
- # some QUOTING caching :
610
-
611
- @@quoted_table_names = {}
612
620
 
613
- def quote_table_name(name)
614
- unless quoted = @@quoted_table_names[name]
615
- quoted = super
616
- @@quoted_table_names[name] = quoted.freeze
617
- end
618
- quoted
621
+ @use_insert_returning = config.key?(:insert_returning) ?
622
+ self.class.type_cast_config_to_boolean(config[:insert_returning]) : nil
619
623
  end
620
624
 
621
- @@quoted_column_names = {}
622
-
623
- def quote_column_name(name)
624
- unless quoted = @@quoted_column_names[name]
625
- quoted = super
626
- @@quoted_column_names[name] = quoted.freeze
627
- end
628
- quoted
629
- end
630
-
631
625
  end
632
626
 
633
627
  class OracleColumn < JdbcColumn
634
628
  include ::ArJdbc::Oracle::Column
635
629
  end
636
-
630
+
637
631
  end