activerecord-jdbc-adapter 5.0.pre1 → 51.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -2
  3. data/.travis.yml +15 -416
  4. data/Gemfile +35 -37
  5. data/README.md +23 -118
  6. data/RUNNING_TESTS.md +31 -26
  7. data/Rakefile +2 -3
  8. data/activerecord-jdbc-adapter.gemspec +1 -2
  9. data/lib/arjdbc/abstract/connection_management.rb +21 -0
  10. data/lib/arjdbc/abstract/core.rb +62 -0
  11. data/lib/arjdbc/abstract/database_statements.rb +46 -0
  12. data/lib/arjdbc/abstract/statement_cache.rb +58 -0
  13. data/lib/arjdbc/abstract/transaction_support.rb +86 -0
  14. data/lib/arjdbc/derby/adapter.rb +6 -1
  15. data/lib/arjdbc/discover.rb +0 -7
  16. data/lib/arjdbc/firebird/adapter.rb +2 -2
  17. data/lib/arjdbc/jdbc/adapter.rb +10 -252
  18. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  19. data/lib/arjdbc/jdbc/connection.rb +6 -0
  20. data/lib/arjdbc/jdbc.rb +2 -2
  21. data/lib/arjdbc/mysql/adapter.rb +87 -944
  22. data/lib/arjdbc/mysql/connection_methods.rb +4 -2
  23. data/lib/arjdbc/postgresql/adapter.rb +288 -1023
  24. data/lib/arjdbc/postgresql/base/array_decoder.rb +26 -0
  25. data/lib/arjdbc/postgresql/base/array_encoder.rb +25 -0
  26. data/lib/arjdbc/postgresql/base/pgconn.rb +8 -5
  27. data/lib/arjdbc/postgresql/column.rb +10 -599
  28. data/lib/arjdbc/postgresql/connection_methods.rb +9 -0
  29. data/lib/arjdbc/postgresql/name.rb +24 -0
  30. data/lib/arjdbc/postgresql/oid_types.rb +25 -110
  31. data/lib/arjdbc/sqlite3/adapter.rb +171 -170
  32. data/lib/arjdbc/tasks/database_tasks.rb +1 -3
  33. data/lib/arjdbc/tasks/db2_database_tasks.rb +2 -2
  34. data/lib/arjdbc/version.rb +1 -1
  35. data/pom.xml +3 -3
  36. data/rakelib/02-test.rake +0 -12
  37. data/rakelib/compile.rake +1 -1
  38. data/rakelib/db.rake +7 -5
  39. data/rakelib/rails.rake +63 -64
  40. data/src/java/arjdbc/firebird/FirebirdRubyJdbcConnection.java +1 -17
  41. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +518 -1260
  42. data/src/java/arjdbc/mysql/MySQLModule.java +3 -3
  43. data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +53 -134
  44. data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +214 -240
  45. data/src/java/arjdbc/sqlite3/SQLite3Module.java +0 -20
  46. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +85 -10
  47. metadata +20 -34
  48. data/Appraisals +0 -41
  49. data/lib/active_record/connection_adapters/oracle_adapter.rb +0 -1
  50. data/lib/arjdbc/common_jdbc_methods.rb +0 -89
  51. data/lib/arjdbc/mysql/bulk_change_table.rb +0 -150
  52. data/lib/arjdbc/mysql/column.rb +0 -162
  53. data/lib/arjdbc/mysql/explain_support.rb +0 -82
  54. data/lib/arjdbc/mysql/schema_creation.rb +0 -58
  55. data/lib/arjdbc/oracle/adapter.rb +0 -952
  56. data/lib/arjdbc/oracle/column.rb +0 -126
  57. data/lib/arjdbc/oracle/connection_methods.rb +0 -21
  58. data/lib/arjdbc/oracle.rb +0 -4
  59. data/lib/arjdbc/postgresql/_bc_time_cast_patch.rb +0 -21
  60. data/lib/arjdbc/postgresql/base/oid.rb +0 -412
  61. data/lib/arjdbc/postgresql/base/schema_definitions.rb +0 -131
  62. data/lib/arjdbc/postgresql/explain_support.rb +0 -53
  63. data/lib/arjdbc/postgresql/oid/bytea.rb +0 -2
  64. data/lib/arjdbc/postgresql/schema_creation.rb +0 -60
  65. data/lib/arjdbc/tasks/oracle/enhanced_structure_dump.rb +0 -297
  66. data/lib/arjdbc/tasks/oracle_database_tasks.rb +0 -65
  67. data/src/java/arjdbc/oracle/OracleModule.java +0 -75
  68. data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +0 -465
@@ -1,952 +0,0 @@
1
- # NOTE: file contains code adapted from **oracle-enhanced** adapter, license follows
2
- =begin
3
- Copyright (c) 2008-2011 Graham Jenkins, Michael Schoen, Raimonds Simanovskis
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining
6
- a copy of this software and associated documentation files (the
7
- "Software"), to deal in the Software without restriction, including
8
- without limitation the rights to use, copy, modify, merge, publish,
9
- distribute, sublicense, and/or sell copies of the Software, and to
10
- permit persons to whom the Software is furnished to do so, subject to
11
- the following conditions:
12
-
13
- The above copyright notice and this permission notice shall be
14
- included in all copies or substantial portions of the Software.
15
-
16
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
- =end
24
-
25
- ArJdbc.load_java_part :Oracle
26
-
27
- module ArJdbc
28
- module Oracle
29
-
30
- require 'arjdbc/oracle/column'
31
-
32
- # @private
33
- def self.extended(adapter); initialize!; end
34
-
35
- # @private
36
- @@_initialized = nil
37
-
38
- # @private
39
- def self.initialize!
40
- return if @@_initialized; @@_initialized = true
41
-
42
- require 'arjdbc/util/serialized_attributes'
43
- Util::SerializedAttributes.setup /LOB\(|LOB$/i, 'after_save_with_oracle_lob'
44
-
45
- unless ActiveRecord::ConnectionAdapters::AbstractAdapter.
46
- instance_methods(false).detect { |m| m.to_s == "prefetch_primary_key?" }
47
- require 'arjdbc/jdbc/quoted_primary_key'
48
- ActiveRecord::Base.extend ArJdbc::QuotedPrimaryKeyExtension
49
- end
50
- end
51
-
52
- # @see ActiveRecord::ConnectionAdapters::JdbcAdapter#jdbc_connection_class
53
- def self.jdbc_connection_class
54
- ::ActiveRecord::ConnectionAdapters::OracleJdbcConnection
55
- end
56
-
57
- # @see ActiveRecord::ConnectionAdapters::JdbcAdapter#jdbc_column_class
58
- def jdbc_column_class; ::ActiveRecord::ConnectionAdapters::OracleColumn end
59
-
60
- # @private
61
- @@update_lob_values = true
62
-
63
- # Updating records with LOB values (binary/text columns) in a separate
64
- # statement can be disabled using :
65
- #
66
- # ArJdbc::Oracle.update_lob_values = false
67
- #
68
- # @note This only applies when prepared statements are not used.
69
- def self.update_lob_values?; @@update_lob_values; end
70
- # @see #update_lob_values?
71
- def self.update_lob_values=(update); @@update_lob_values = update; end
72
-
73
- # @see #update_lob_values?
74
- # @see ArJdbc::Util::SerializedAttributes#update_lob_columns
75
- def update_lob_value?(value, column = nil)
76
- Oracle.update_lob_values? && ! prepared_statements? && ! ( value.nil? || value == '' )
77
- end
78
-
79
- # @private
80
- @@emulate_booleans = true
81
-
82
- # Boolean emulation can be disabled using :
83
- #
84
- # ArJdbc::Oracle.emulate_booleans = false
85
- #
86
- # @see ActiveRecord::ConnectionAdapters::OracleAdapter#emulate_booleans
87
- def self.emulate_booleans?; @@emulate_booleans; end
88
- # @deprecated Use {#emulate_booleans?} instead.
89
- def self.emulate_booleans; @@emulate_booleans; end
90
- # @see #emulate_booleans?
91
- def self.emulate_booleans=(emulate); @@emulate_booleans = emulate; end
92
-
93
- class TableDefinition < ::ActiveRecord::ConnectionAdapters::TableDefinition
94
- def raw(*args)
95
- options = args.extract_options!
96
- column(args[0], 'raw', options)
97
- end unless AR42
98
-
99
- def raw(name, options={})
100
- column(name, :raw, options)
101
- end if AR42
102
-
103
- def xml(*args)
104
- options = args.extract_options!
105
- column(args[0], 'xml', options)
106
- end unless AR42
107
-
108
- def raw(name, options={})
109
- column(name, :xml, options)
110
- end if AR42
111
-
112
- def aliased_types(name, fallback)
113
- # NOTE: disable aliasing :timestamp as :datetime :
114
- fallback # 'timestamp' == name ? :datetime : fallback
115
- end if AR42
116
- end
117
-
118
- def table_definition(*args)
119
- new_table_definition(TableDefinition, *args)
120
- end
121
-
122
- def create_table_definition(name, temporary, options, as = nil)
123
- TableDefinition.new native_database_types, name, temporary, options, as
124
- end if AR42
125
-
126
- ADAPTER_NAME = 'Oracle'.freeze
127
-
128
- def adapter_name
129
- ADAPTER_NAME
130
- end
131
-
132
- def initialize_type_map(m)
133
- super
134
-
135
- m.register_type(%r(NUMBER)i) do |sql_type|
136
- scale = extract_scale(sql_type)
137
- precision = extract_precision(sql_type)
138
- limit = extract_limit(sql_type)
139
- if scale == 0
140
- if Oracle.emulate_booleans? && limit == 1
141
- ActiveRecord::Type::Boolean.new
142
- else
143
- ActiveRecord::Type::Integer.new(:precision => precision, :limit => limit)
144
- end
145
- else
146
- ActiveRecord::Type::Decimal.new(:precision => precision, :scale => scale)
147
- end
148
- end
149
-
150
- register_class_with_limit m, %r(date)i, ActiveRecord::Type::DateTime
151
- register_class_with_limit m, %r(raw)i, RawType
152
- register_class_with_limit m, %r(timestamp)i, TimestampType
153
-
154
- m.register_type %r(xmltype)i, XmlType.new
155
- end if AR42
156
-
157
- def clear_cache!
158
- super
159
- reload_type_map
160
- end if AR42
161
-
162
- # @private
163
- class RawType < ActiveRecord::Type::String
164
- def type; :raw end
165
- end if AR42
166
-
167
- # @private
168
- class TimestampType < ActiveRecord::Type::DateTime
169
- def type; :timestamp end
170
- end if AR42
171
-
172
- # @private
173
- class XmlType < ActiveRecord::Type::String
174
- def type; :xml end
175
-
176
- def type_cast_for_database(value)
177
- return unless value
178
- Data.new(super)
179
- end
180
-
181
- class Data
182
- def initialize(value)
183
- @value = value
184
- end
185
- def to_s; @value end
186
- end
187
- end if AR42
188
-
189
- NATIVE_DATABASE_TYPES = {
190
- :primary_key => "NUMBER(38) NOT NULL PRIMARY KEY",
191
- :string => { :name => "VARCHAR2", :limit => 255 },
192
- :text => { :name => "CLOB" },
193
- :integer => { :name => "NUMBER", :limit => 38 },
194
- :float => { :name => "NUMBER" },
195
- :decimal => { :name => "DECIMAL" },
196
- :datetime => { :name => "DATE" },
197
- :timestamp => { :name => "TIMESTAMP" },
198
- :time => { :name => "DATE" },
199
- :date => { :name => "DATE" },
200
- :binary => { :name => "BLOB" },
201
- :boolean => { :name => "NUMBER", :limit => 1 },
202
- :raw => { :name => "RAW", :limit => 2000 },
203
- :xml => { :name => 'XMLTYPE' }
204
- }
205
-
206
- def native_database_types
207
- super.merge(NATIVE_DATABASE_TYPES)
208
- end
209
-
210
- def modify_types(types)
211
- super(types)
212
- NATIVE_DATABASE_TYPES.each do |key, value|
213
- types[key] = value.dup
214
- end
215
- types
216
- end
217
-
218
- # Prevent ORA-01795 for in clauses with more than 1000
219
- def in_clause_length
220
- 1000
221
- end
222
- alias_method :ids_in_list_limit, :in_clause_length
223
-
224
- IDENTIFIER_LENGTH = 30
225
-
226
- # maximum length of Oracle identifiers is 30
227
- def table_alias_length; IDENTIFIER_LENGTH; end
228
- def table_name_length; IDENTIFIER_LENGTH; end
229
- def index_name_length; IDENTIFIER_LENGTH; end
230
- def column_name_length; IDENTIFIER_LENGTH; end
231
- def sequence_name_length; IDENTIFIER_LENGTH end
232
-
233
- # @private
234
- # Will take all or first 26 characters of table name and append _seq suffix
235
- def default_sequence_name(table_name, primary_key = nil)
236
- len = IDENTIFIER_LENGTH - 4
237
- table_name.to_s.gsub (/(^|\.)([\w$-]{1,#{len}})([\w$-]*)$/), '\1\2_seq'
238
- end
239
-
240
- # @private
241
- def default_trigger_name(table_name)
242
- "#{table_name.to_s[0, IDENTIFIER_LENGTH - 4]}_pkt"
243
- end
244
-
245
- # @override
246
- def create_table(name, options = {})
247
- super(name, options)
248
- unless options[:id] == false
249
- seq_name = options[:sequence_name] || default_sequence_name(name)
250
- start_value = options[:sequence_start_value] || 10000
251
- raise ActiveRecord::StatementInvalid.new("name #{seq_name} too long") if seq_name.length > table_alias_length
252
- execute "CREATE SEQUENCE #{quote_table_name(seq_name)} START WITH #{start_value}"
253
- end
254
- end
255
-
256
- # @override
257
- def rename_table(name, new_name)
258
- if new_name.to_s.length > table_name_length
259
- raise ArgumentError, "New table name '#{new_name}' is too long; the limit is #{table_name_length} characters"
260
- end
261
- if "#{new_name}_seq".to_s.length > sequence_name_length
262
- raise ArgumentError, "New sequence name '#{new_name}_seq' is too long; the limit is #{sequence_name_length} characters"
263
- end
264
- execute "RENAME #{quote_table_name(name)} TO #{quote_table_name(new_name)}"
265
- execute "RENAME #{quote_table_name("#{name}_seq")} TO #{quote_table_name("#{new_name}_seq")}" rescue nil
266
- end
267
-
268
- # @override
269
- def drop_table(name, options = {})
270
- outcome = super(name)
271
- return outcome if name == 'schema_migrations'
272
- seq_name = options.key?(:sequence_name) ? # pass nil/false - no sequence
273
- options[:sequence_name] : default_sequence_name(name)
274
- return outcome unless seq_name
275
- execute_quietly "DROP SEQUENCE #{quote_table_name(seq_name)}"
276
- end
277
-
278
- # @override
279
- def type_to_sql(type, limit = nil, precision = nil, scale = nil)
280
- case type.to_sym
281
- when :binary
282
- # { BLOB | BINARY LARGE OBJECT } [ ( length [{K |M |G }] ) ]
283
- # although Oracle does not like limit (length) with BLOB (or CLOB) :
284
- #
285
- # CREATE TABLE binaries (data BLOB, short_data BLOB(1024));
286
- # ORA-00907: missing right parenthesis *
287
- #
288
- # TODO do we need to worry about NORMAL vs. non IN-TABLE BLOBs ?!
289
- # http://dba.stackexchange.com/questions/8770/improve-blob-writing-performance-in-oracle-11g
290
- # - if the LOB is smaller than 3900 bytes it can be stored inside the
291
- # table row; by default this is enabled,
292
- # unless you specify DISABLE STORAGE IN ROW
293
- # - normal LOB - stored in a separate segment, outside of table,
294
- # you may even put it in another tablespace;
295
- super(type, nil, nil, nil)
296
- when :text
297
- super(type, nil, nil, nil)
298
- else
299
- super
300
- end
301
- end
302
-
303
- def indexes(table, name = nil)
304
- @connection.indexes(table, name, schema_owner)
305
- end
306
-
307
- # @note Only used with (non-AREL) ActiveRecord **2.3**.
308
- # @see Arel::Visitors::Oracle
309
- def add_limit_offset!(sql, options)
310
- offset = options[:offset] || 0
311
- if limit = options[:limit]
312
- sql.replace "SELECT * FROM " <<
313
- "(select raw_sql_.*, rownum raw_rnum_ from (#{sql}) raw_sql_ where rownum <= #{offset + limit})" <<
314
- " WHERE raw_rnum_ > #{offset}"
315
- elsif offset > 0
316
- sql.replace "SELECT * FROM " <<
317
- "(select raw_sql_.*, rownum raw_rnum_ from (#{sql}) raw_sql_)" <<
318
- " WHERE raw_rnum_ > #{offset}"
319
- end
320
- end if ::ActiveRecord::VERSION::MAJOR < 3
321
-
322
- def current_user
323
- @current_user ||= execute("SELECT sys_context('userenv', 'session_user') su FROM dual").first['su']
324
- end
325
-
326
- def current_database
327
- @current_database ||= execute("SELECT sys_context('userenv', 'db_name') db FROM dual").first['db']
328
- end
329
-
330
- def current_schema
331
- execute("SELECT sys_context('userenv', 'current_schema') schema FROM dual").first['schema']
332
- end
333
-
334
- def current_schema=(schema_owner)
335
- execute("ALTER SESSION SET current_schema=#{schema_owner}")
336
- end
337
-
338
- # @override
339
- def release_savepoint(name = nil)
340
- # no RELEASE SAVEPOINT statement in Oracle (JDBC driver throws "Unsupported feature")
341
- end
342
-
343
- # @override
344
- def add_index(table_name, column_name, options = {})
345
- index_name, index_type, quoted_column_names, tablespace, index_options = add_index_options(table_name, column_name, options)
346
- execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{quoted_column_names})#{tablespace} #{index_options}"
347
- if index_type == 'UNIQUE'
348
- unless quoted_column_names =~ /\(.*\)/
349
- execute "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{quote_column_name(index_name)} #{index_type} (#{quoted_column_names})"
350
- end
351
- end
352
- end if AR42
353
-
354
- # @private
355
- def add_index_options(table_name, column_name, options = {})
356
- column_names = Array(column_name)
357
- index_name = index_name(table_name, column: column_names)
358
-
359
- options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :tablespace, :options, :using)
360
-
361
- index_type = options[:unique] ? "UNIQUE" : ""
362
- index_name = options[:name].to_s if options.key?(:name)
363
- tablespace = '' # tablespace_for(:index, options[:tablespace])
364
- max_index_length = options.fetch(:internal, false) ? index_name_length : allowed_index_name_length
365
- index_options = '' # index_options = options[:options]
366
-
367
- if index_name.to_s.length > max_index_length
368
- raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{max_index_length} characters"
369
- end
370
- if index_name_exists?(table_name, index_name, false)
371
- raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists"
372
- end
373
-
374
- quoted_column_names = column_names.map { |e| quote_column_name(e, true) }.join(", ")
375
- [ index_name, index_type, quoted_column_names, tablespace, index_options ]
376
- end if AR42
377
-
378
- # @override
379
- def remove_index(table_name, options = {})
380
- index_name = index_name(table_name, options)
381
- unless index_name_exists?(table_name, index_name, true)
382
- # sometimes options can be String or Array with column names
383
- options = {} unless options.is_a?(Hash)
384
- if options.has_key? :name
385
- options_without_column = options.dup
386
- options_without_column.delete :column
387
- index_name_without_column = index_name(table_name, options_without_column)
388
- return index_name_without_column if index_name_exists?(table_name, index_name_without_column, false)
389
- end
390
- raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' does not exist"
391
- end
392
- execute "ALTER TABLE #{quote_table_name(table_name)} DROP CONSTRAINT #{quote_column_name(index_name)}" rescue nil
393
- execute "DROP INDEX #{quote_column_name(index_name)}"
394
- end if AR42
395
-
396
- # @private
397
- def remove_index(table_name, options = {})
398
- execute "DROP INDEX #{index_name(table_name, options)}"
399
- end unless AR42
400
-
401
- def change_column_default(table_name, column_name, default)
402
- execute "ALTER TABLE #{quote_table_name(table_name)} MODIFY #{quote_column_name(column_name)} DEFAULT #{quote(default)}"
403
- end
404
-
405
- # @override
406
- def add_column_options!(sql, options)
407
- # handle case of defaults for CLOB columns, which would otherwise get "quoted" incorrectly
408
- if options_include_default?(options) && (column = options[:column]) && column.type == :text
409
- sql << " DEFAULT #{quote(options.delete(:default))}"
410
- end
411
- super
412
- end
413
-
414
- # @override
415
- def change_column(table_name, column_name, type, options = {})
416
- change_column_sql = "ALTER TABLE #{quote_table_name(table_name)} " <<
417
- "MODIFY #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit])}"
418
- add_column_options!(change_column_sql, options)
419
- execute(change_column_sql)
420
- end
421
-
422
- # @override
423
- def rename_column(table_name, column_name, new_column_name)
424
- execute "ALTER TABLE #{quote_table_name(table_name)} " <<
425
- "RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
426
- end
427
-
428
- if ActiveRecord::VERSION::MAJOR >= 4
429
-
430
- # @override
431
- def remove_column(table_name, column_name, type = nil, options = {})
432
- do_remove_column(table_name, column_name)
433
- end
434
-
435
- else
436
-
437
- # @override
438
- def remove_column(table_name, *column_names)
439
- for column_name in column_names.flatten
440
- do_remove_column(table_name, column_name)
441
- end
442
- end
443
- alias remove_columns remove_column
444
-
445
- end
446
-
447
- def do_remove_column(table_name, column_name)
448
- execute "ALTER TABLE #{quote_table_name(table_name)} DROP COLUMN #{quote_column_name(column_name)}"
449
- end
450
- private :do_remove_column
451
-
452
- # SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
453
- #
454
- # Oracle requires the ORDER BY columns to be in the SELECT list for DISTINCT
455
- # queries. However, with those columns included in the SELECT DISTINCT list, you
456
- # won't actually get a distinct list of the column you want (presuming the column
457
- # has duplicates with multiple values for the ordered-by columns. So we use the
458
- # FIRST_VALUE function to get a single (first) value for each column, effectively
459
- # making every row the same.
460
- #
461
- # distinct("posts.id", "posts.created_at desc")
462
- #
463
- # @override
464
- def distinct(columns, order_by)
465
- "DISTINCT #{columns_for_distinct(columns, order_by)}"
466
- end
467
-
468
- # @override Since AR 4.0 (on 4.1 {#distinct} is gone and won't be called).
469
- def columns_for_distinct(columns, orders)
470
- return columns if orders.blank?
471
- if orders.is_a?(Array) # AR 3.x vs 4.x
472
- orders = orders.map { |column| column.is_a?(String) ? column : column.to_sql }
473
- else
474
- orders = extract_order_columns(orders)
475
- end
476
- # construct a valid DISTINCT clause, ie. one that includes the ORDER BY columns, using
477
- # FIRST_VALUE such that the inclusion of these columns doesn't invalidate the DISTINCT
478
- order_columns = orders.map do |c, i|
479
- "FIRST_VALUE(#{c.split.first}) OVER (PARTITION BY #{columns} ORDER BY #{c}) AS alias_#{i}__"
480
- end
481
- columns = [ columns ]; columns.flatten!
482
- columns.push( *order_columns ).join(', ')
483
- end
484
-
485
- # ORDER BY clause for the passed order option.
486
- #
487
- # Uses column aliases as defined by {#distinct}.
488
- def add_order_by_for_association_limiting!(sql, options)
489
- return sql if options[:order].blank?
490
-
491
- order_columns = extract_order_columns(options[:order]) do |columns|
492
- columns.map! { |s| $1 if s =~ / (.*)/ }; columns
493
- end
494
- order = order_columns.map { |s, i| "alias_#{i}__ #{s}" } # @see {#distinct}
495
-
496
- sql << "ORDER BY #{order.join(', ')}"
497
- end
498
-
499
- def extract_order_columns(order_by)
500
- columns = order_by.split(',')
501
- columns.map!(&:strip); columns.reject!(&:blank?)
502
- columns = yield(columns) if block_given?
503
- columns.zip( (0...columns.size).to_a )
504
- end
505
- private :extract_order_columns
506
-
507
- def temporary_table?(table_name)
508
- select_value("SELECT temporary FROM user_tables WHERE table_name = '#{table_name.upcase}'") == 'Y'
509
- end
510
-
511
- def tables
512
- @connection.tables(nil, oracle_schema)
513
- end
514
-
515
- # NOTE: better to use current_schema instead of the configured one ?!
516
- def columns(table_name, name = nil)
517
- @connection.columns_internal(table_name.to_s, nil, oracle_schema)
518
- end
519
-
520
- def tablespace(table_name)
521
- select_value "SELECT tablespace_name FROM user_tables WHERE table_name='#{table_name.to_s.upcase}'"
522
- end
523
-
524
- def charset
525
- database_parameters['NLS_CHARACTERSET']
526
- end
527
-
528
- def collation
529
- database_parameters['NLS_COMP']
530
- end
531
-
532
- def database_parameters
533
- return @database_parameters unless ( @database_parameters ||= {} ).empty?
534
- @connection.execute_query_raw("SELECT * FROM NLS_DATABASE_PARAMETERS") do
535
- |name, value| @database_parameters[name] = value
536
- end
537
- @database_parameters
538
- end
539
-
540
- # QUOTING ==================================================
541
-
542
- # @override
543
- def quote_table_name(name)
544
- name.to_s.split('.').map{ |n| n.split('@').map{ |m| quote_column_name(m) }.join('@') }.join('.')
545
- end
546
-
547
- # @private
548
- LOWER_CASE_ONLY = /\A[a-z][a-z_0-9\$#]*\Z/
549
-
550
- # @override
551
- def quote_column_name(name, handle_expression = false)
552
- # if only valid lowercase column characters in name
553
- if ( name = name.to_s ) =~ LOWER_CASE_ONLY
554
- # putting double-quotes around an identifier causes Oracle to treat the
555
- # identifier as case sensitive (otherwise assumes case-insensitivity) !
556
- # all upper case is an exception, where double-quotes are meaningless
557
- "\"#{name.upcase}\"" # name.upcase
558
- else
559
- if handle_expression
560
- name =~ /^[a-z][a-z_0-9\$#\-]*$/i ? "\"#{name}\"" : name
561
- else
562
- # remove double quotes which cannot be used inside quoted identifier
563
- "\"#{name.gsub('"', '')}\""
564
- end
565
- end
566
- end
567
-
568
- def unquote_table_name(name)
569
- name = name[1...-1] if name[0, 1] == '"'
570
- name.upcase == name ? name.downcase : name
571
- end
572
-
573
- # @override
574
- def quote(value, column = nil)
575
- return value if sql_literal?(value)
576
-
577
- column_type = column && column.type
578
- if column_type == :text || column_type == :binary
579
- return 'NULL' if value.nil? || value == ''
580
- if update_lob_value?(value, column)
581
- if /(.*?)\([0-9]+\)/ =~ ( sql_type = column.sql_type )
582
- %Q{empty_#{ $1.downcase }()}
583
- else
584
- %Q{empty_#{ sql_type.respond_to?(:downcase) ? sql_type.downcase : 'blob' }()}
585
- end
586
- else
587
- "'#{quote_string(value.to_s)}'"
588
- end
589
- elsif column_type == :xml
590
- "XMLTYPE('#{quote_string(value)}')" # XMLTYPE ?
591
- elsif column_type == :raw
592
- quote_raw(value)
593
- else
594
- if column.respond_to?(:primary) && column.primary && column.klass != String
595
- return value.to_i.to_s
596
- end
597
-
598
- if column_type == :datetime || column_type == :time
599
- if value.acts_like?(:time)
600
- %Q{TO_DATE('#{get_time(value).strftime("%Y-%m-%d %H:%M:%S")}','YYYY-MM-DD HH24:MI:SS')}
601
- else
602
- value.blank? ? 'NULL' : %Q{DATE'#{value}'} # assume correctly formated DATE (string)
603
- end
604
- elsif ( like_date = value.acts_like?(:date) ) || column_type == :date
605
- if value.acts_like?(:time) # value.respond_to?(:strftime)
606
- %Q{DATE'#{get_time(value).strftime("%Y-%m-%d")}'}
607
- elsif like_date
608
- %Q{DATE'#{quoted_date(value)}'} # DATE 'YYYY-MM-DD'
609
- else
610
- value.blank? ? 'NULL' : %Q{DATE'#{value}'} # assume correctly formated DATE (string)
611
- end
612
- elsif ( like_time = value.acts_like?(:time) ) || column_type == :timestamp
613
- if like_time
614
- %Q{TIMESTAMP'#{quoted_date(value, true)}'} # TIMESTAMP 'YYYY-MM-DD HH24:MI:SS.FF'
615
- else
616
- value.blank? ? 'NULL' : %Q{TIMESTAMP'#{value}'} # assume correctly formated TIMESTAMP (string)
617
- end
618
- else
619
- super
620
- end
621
- end
622
- end
623
-
624
- # Quote date/time values for use in SQL input.
625
- # Includes milliseconds if the value is a Time responding to usec.
626
- # @override
627
- def quoted_date(value, time = nil)
628
- if time || ( time.nil? && value.acts_like?(:time) )
629
- usec = value.respond_to?(:usec) && (value.usec / 10000.0).round # .428000 -> .43
630
- return "#{get_time(value).to_s(:db)}.#{sprintf("%02d", usec)}" if usec
631
- # value.strftime("%Y-%m-%d %H:%M:%S")
632
- end
633
- value.to_s(:db)
634
- end
635
-
636
- def quote_raw(value)
637
- value = value.unpack('C*') if value.is_a?(String)
638
- "'#{value.map { |x| "%02X" % x }.join}'"
639
- end
640
-
641
- # @override
642
- def supports_migrations?; true end
643
-
644
- # @override
645
- def supports_primary_key?; true end
646
-
647
- # @override
648
- def supports_savepoints?; true end
649
-
650
- # @override
651
- def supports_explain?; true end
652
-
653
- # @override
654
- def supports_views?; true end
655
-
656
- def truncate(table_name, name = nil)
657
- execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
658
- end
659
-
660
- def explain(arel, binds = [])
661
- sql = "EXPLAIN PLAN FOR #{to_sql(arel, binds)}"
662
- return if sql =~ /FROM all_/
663
- exec_update(sql, 'EXPLAIN', binds)
664
- select_values("SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY)", 'EXPLAIN').join("\n")
665
- end
666
-
667
- def select(sql, name = nil, binds = [])
668
- result = super # AR::Result (4.0) or Array (<= 3.2)
669
- result.columns.delete('raw_rnum_') if result.respond_to?(:columns)
670
- result.each { |row| row.delete('raw_rnum_') } # Hash rows even for AR::Result
671
- result
672
- end
673
-
674
- @@do_not_prefetch_primary_key = {}
675
-
676
- # Returns true for Oracle adapter (since Oracle requires primary key
677
- # values to be pre-fetched before insert).
678
- # @see #next_sequence_value
679
- # @override
680
- def prefetch_primary_key?(table_name = nil)
681
- return true if table_name.nil?
682
- do_not_prefetch_hash = @@do_not_prefetch_primary_key
683
- do_not_prefetch = do_not_prefetch_hash[ table_name = table_name.to_s ]
684
- if do_not_prefetch.nil?
685
- owner, desc_table_name, db_link = describe(table_name)
686
- do_not_prefetch_hash[table_name] = do_not_prefetch =
687
- ! has_primary_key?(table_name, owner, desc_table_name, db_link) ||
688
- has_primary_key_trigger?(table_name, owner, desc_table_name, db_link)
689
- end
690
- ! do_not_prefetch
691
- end
692
-
693
- # used to clear prefetch primary key flag for all tables
694
- # @private
695
- def clear_prefetch_primary_key; @@do_not_prefetch_primary_key = {} end
696
-
697
- # @private
698
- def has_primary_key?(table_name, owner = nil, desc_table_name = nil, db_link = nil)
699
- ! pk_and_sequence_for(table_name, owner, desc_table_name, db_link).nil?
700
- end
701
-
702
- # @private check if table has primary key trigger with _pkt suffix
703
- def has_primary_key_trigger?(table_name, owner = nil, desc_table_name = nil, db_link = nil)
704
- (owner, desc_table_name, db_link) = describe(table_name) unless desc_table_name
705
-
706
- trigger_name = default_trigger_name(table_name).upcase
707
- pkt_sql = "SELECT trigger_name FROM all_triggers#{db_link} WHERE owner = '#{owner}'" <<
708
- " AND trigger_name = '#{trigger_name}'" <<
709
- " AND table_owner = '#{owner}'" <<
710
- " AND table_name = '#{desc_table_name}'" <<
711
- " AND status = 'ENABLED'"
712
- select_value(pkt_sql, 'Primary Key Trigger') ? true : false
713
- end
714
-
715
- # use in set_sequence_name to avoid fetching primary key value from sequence
716
- AUTOGENERATED_SEQUENCE_NAME = 'autogenerated'.freeze
717
-
718
- # Returns the next sequence value from a sequence generator. Not generally
719
- # called directly; used by ActiveRecord to get the next primary key value
720
- # when inserting a new database record (see #prefetch_primary_key?).
721
- def next_sequence_value(sequence_name)
722
- # if sequence_name is set to :autogenerated then it means that primary key will be populated by trigger
723
- return nil if sequence_name == AUTOGENERATED_SEQUENCE_NAME
724
- sequence_name = quote_table_name(sequence_name)
725
- sql = "SELECT #{sequence_name}.NEXTVAL id FROM dual"
726
- log(sql, 'SQL') { @connection.next_sequence_value(sequence_name) }
727
- end
728
-
729
- def pk_and_sequence_for(table_name, owner = nil, desc_table_name = nil, db_link = nil)
730
- (owner, desc_table_name, db_link) = describe(table_name) unless desc_table_name
731
-
732
- seqs = "SELECT us.sequence_name" <<
733
- " FROM all_sequences#{db_link} us" <<
734
- " WHERE us.sequence_owner = '#{owner}'" <<
735
- " AND us.sequence_name = '#{desc_table_name}_SEQ'"
736
- seqs = select_values(seqs, 'Sequence')
737
-
738
- # changed back from user_constraints to all_constraints for consistency
739
- pks = "SELECT cc.column_name" <<
740
- " FROM all_constraints#{db_link} c, all_cons_columns#{db_link} cc" <<
741
- " WHERE c.owner = '#{owner}'" <<
742
- " AND c.table_name = '#{desc_table_name}'" <<
743
- " AND c.constraint_type = 'P'" <<
744
- " AND cc.owner = c.owner" <<
745
- " AND cc.constraint_name = c.constraint_name"
746
- pks = select_values(pks, 'Primary Key')
747
-
748
- # only support single column keys
749
- pks.size == 1 ? [oracle_downcase(pks.first), oracle_downcase(seqs.first)] : nil
750
- end
751
- private :pk_and_sequence_for
752
-
753
- # Returns just a table's primary key
754
- def primary_key(table_name)
755
- pk_and_sequence = pk_and_sequence_for(table_name)
756
- pk_and_sequence && pk_and_sequence.first
757
- end
758
-
759
- # @override
760
- def supports_foreign_keys?; true end
761
-
762
- # @private
763
- def disable_referential_integrity
764
- sql_constraints = "SELECT constraint_name, owner, table_name FROM user_constraints WHERE constraint_type = 'R' AND status = 'ENABLED'"
765
- old_constraints = select_all(sql_constraints)
766
- begin
767
- old_constraints.each do |constraint|
768
- execute "ALTER TABLE #{constraint["table_name"]} DISABLE CONSTRAINT #{constraint["constraint_name"]}"
769
- end
770
- yield
771
- ensure
772
- old_constraints.reverse_each do |constraint|
773
- execute "ALTER TABLE #{constraint["table_name"]} ENABLE CONSTRAINT #{constraint["constraint_name"]}"
774
- end
775
- end
776
- end
777
-
778
- # @override (for AR <= 3.0)
779
- def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
780
- # if PK is already pre-fetched from sequence or if there is no PK :
781
- if id_value || pk.nil?
782
- execute(sql, name)
783
- return id_value
784
- end
785
-
786
- if pk && use_insert_returning? # true by default on AR <= 3.0
787
- sql = "#{sql} RETURNING #{quote_column_name(pk)} INTO ?"
788
- exec_insert_returning(sql, name, nil, pk)
789
- else
790
- execute(sql, name)
791
- end
792
- end
793
- protected :insert_sql
794
-
795
- # @override
796
- def sql_for_insert(sql, pk, id_value, sequence_name, binds)
797
- unless id_value || pk.nil?
798
- if pk && use_insert_returning?
799
- sql = "#{sql} RETURNING #{quote_column_name(pk)} INTO ?"
800
- end
801
- end
802
- [ sql, binds ]
803
- end
804
-
805
- # @override
806
- def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
807
- # NOTE: ActiveRecord::Relation calls our {#next_sequence_value}
808
- # (from its `insert`) and passes the returned id_value here ...
809
- sql, binds = sql_for_insert(to_sql(arel, binds), pk, id_value, sequence_name, binds)
810
- if id_value
811
- exec_update(sql, name, binds)
812
- return id_value
813
- else
814
- value = exec_insert(sql, name, binds, pk, sequence_name)
815
- id_value || last_inserted_id(value)
816
- end
817
- end
818
-
819
- # @override
820
- def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
821
- if pk && use_insert_returning?
822
- if sql.is_a?(String) && sql.index('RETURNING')
823
- return exec_insert_returning(sql, name, binds, pk)
824
- end
825
- end
826
- super(sql, name, binds) # assume no generated id for table
827
- end
828
-
829
- def exec_insert_returning(sql, name, binds, pk = nil)
830
- sql = to_sql(sql, binds) if sql.respond_to?(:to_sql)
831
- if prepared_statements?
832
- log(sql, name, binds) { @connection.execute_insert_returning(sql, binds) }
833
- else
834
- log(sql, name) { @connection.execute_insert_returning(sql, nil) }
835
- end
836
- end
837
- # private :exec_insert_returning
838
-
839
- def next_id_value(sql, sequence_name = nil)
840
- # Assume the SQL contains a bind-variable for the ID
841
- sequence_name ||= begin
842
- # Extract the table from the insert SQL. Yuck.
843
- table = extract_table_ref_from_insert_sql(sql)
844
- default_sequence_name(table)
845
- end
846
- next_sequence_value(sequence_name)
847
- end
848
- private :next_id_value
849
-
850
- def use_insert_returning?
851
- if @use_insert_returning.nil?
852
- @use_insert_returning = false
853
- end
854
- @use_insert_returning
855
- end
856
-
857
- private
858
-
859
- def _execute(sql, name = nil)
860
- if self.class.select?(sql)
861
- @connection.execute_query_raw(sql)
862
- elsif self.class.insert?(sql)
863
- @connection.execute_insert(sql)
864
- else
865
- @connection.execute_update(sql)
866
- end
867
- end
868
-
869
- def extract_table_ref_from_insert_sql(sql)
870
- table = sql.split(" ", 4)[2]
871
- if idx = table.index('(')
872
- table = table[0...idx] # INTO table(col1, col2) ...
873
- end
874
- unquote_table_name(table)
875
- end
876
-
877
- # In Oracle, schemas are usually created under your username :
878
- # http://www.oracle.com/technology/obe/2day_dba/schema/schema.htm
879
- #
880
- # A schema is the set of objects (tables, views, indexes, etc) that belongs
881
- # to an user, often used as another way to refer to an Oracle user account.
882
- #
883
- # But allow separate configuration as "schema:" anyway (see #53)
884
- def oracle_schema
885
- if @config[:schema]
886
- @config[:schema].to_s
887
- elsif @config[:username]
888
- @config[:username].to_s
889
- end
890
- end
891
-
892
- # default schema owner
893
- def schema_owner(force = true)
894
- unless defined? @schema_owner
895
- username = config[:username] ? config[:username].to_s : nil
896
- username = jdbc_connection.meta_data.user_name if force && username.nil?
897
- @schema_owner = username.nil? ? nil : username.upcase
898
- end
899
- @schema_owner
900
- end
901
-
902
- # do not force reading schema_owner as we're read on our own ...
903
- def describe(table_name, owner = schema_owner(false))
904
- @connection.describe(table_name, owner)
905
- end
906
-
907
- def oracle_downcase(column_name)
908
- return nil if column_name.nil?
909
- column_name =~ /[a-z]/ ? column_name : column_name.downcase
910
- end
911
-
912
- end
913
- end
914
-
915
- require 'arjdbc/util/quoted_cache'
916
-
917
- module ActiveRecord::ConnectionAdapters
918
-
919
- remove_const(:OracleAdapter) if const_defined?(:OracleAdapter)
920
-
921
- class OracleAdapter < JdbcAdapter
922
- include ::ArJdbc::Oracle
923
- include ::ArJdbc::Util::QuotedCache
924
-
925
- # By default, the OracleAdapter will consider all columns of type
926
- # <tt>NUMBER(1)</tt> as boolean. If you wish to disable this :
927
- #
928
- # ActiveRecord::ConnectionAdapters::OracleAdapter.emulate_booleans = false
929
- #
930
- def self.emulate_booleans?; ::ArJdbc::Oracle.emulate_booleans?; end
931
- def self.emulate_booleans; ::ArJdbc::Oracle.emulate_booleans?; end # oracle-enhanced
932
- def self.emulate_booleans=(emulate); ::ArJdbc::Oracle.emulate_booleans = emulate; end
933
-
934
- def initialize(*args)
935
- ::ArJdbc::Oracle.initialize!
936
- super # configure_connection happens in super
937
-
938
- @use_insert_returning = config.key?(:insert_returning) ?
939
- self.class.type_cast_config_to_boolean(config[:insert_returning]) : nil
940
- end
941
-
942
- end
943
-
944
- class OracleColumn < JdbcColumn
945
- include ::ArJdbc::Oracle::Column
946
-
947
- # def returning_id?; @returning_id ||= nil end
948
- # def returning_id!; @returning_id = true end
949
-
950
- end
951
-
952
- end