activerecord-jdbc-adapter 1.3.17 → 1.3.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +24 -5
  3. data/History.md +54 -0
  4. data/lib/arel/visitors/compat.rb +30 -2
  5. data/lib/arel/visitors/db2.rb +118 -29
  6. data/lib/arel/visitors/derby.rb +84 -29
  7. data/lib/arel/visitors/firebird.rb +66 -9
  8. data/lib/arel/visitors/h2.rb +16 -0
  9. data/lib/arel/visitors/hsqldb.rb +6 -3
  10. data/lib/arel/visitors/postgresql_jdbc.rb +6 -0
  11. data/lib/arel/visitors/sql_server.rb +121 -40
  12. data/lib/arel/visitors/sql_server/ng42.rb +293 -0
  13. data/lib/arjdbc.rb +1 -7
  14. data/lib/arjdbc/db2.rb +1 -0
  15. data/lib/arjdbc/db2/adapter.rb +118 -18
  16. data/lib/arjdbc/derby/adapter.rb +29 -8
  17. data/lib/arjdbc/firebird.rb +1 -0
  18. data/lib/arjdbc/firebird/adapter.rb +126 -11
  19. data/lib/arjdbc/hsqldb/adapter.rb +3 -0
  20. data/lib/arjdbc/informix.rb +1 -0
  21. data/lib/arjdbc/jdbc.rb +17 -0
  22. data/lib/arjdbc/jdbc/adapter.rb +28 -3
  23. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  24. data/lib/arjdbc/jdbc/column.rb +7 -3
  25. data/lib/arjdbc/jdbc/type_cast.rb +2 -0
  26. data/lib/arjdbc/jdbc/type_converter.rb +28 -15
  27. data/lib/arjdbc/mimer.rb +1 -0
  28. data/lib/arjdbc/mssql.rb +2 -1
  29. data/lib/arjdbc/mssql/adapter.rb +105 -30
  30. data/lib/arjdbc/mssql/column.rb +30 -7
  31. data/lib/arjdbc/mssql/limit_helpers.rb +22 -9
  32. data/lib/arjdbc/mssql/types.rb +343 -0
  33. data/lib/arjdbc/mssql/utils.rb +25 -2
  34. data/lib/arjdbc/mysql/adapter.rb +22 -21
  35. data/lib/arjdbc/oracle.rb +1 -0
  36. data/lib/arjdbc/oracle/adapter.rb +291 -19
  37. data/lib/arjdbc/oracle/column.rb +9 -5
  38. data/lib/arjdbc/oracle/connection_methods.rb +4 -1
  39. data/lib/arjdbc/postgresql/_bc_time_cast_patch.rb +21 -0
  40. data/lib/arjdbc/postgresql/adapter.rb +7 -1
  41. data/lib/arjdbc/postgresql/oid/bytea.rb +3 -0
  42. data/lib/arjdbc/postgresql/oid_types.rb +2 -1
  43. data/lib/arjdbc/tasks/database_tasks.rb +3 -0
  44. data/lib/arjdbc/util/quoted_cache.rb +2 -2
  45. data/lib/arjdbc/util/serialized_attributes.rb +11 -0
  46. data/lib/arjdbc/version.rb +1 -1
  47. data/rakelib/02-test.rake +1 -1
  48. data/rakelib/db.rake +3 -1
  49. data/src/java/arjdbc/firebird/FirebirdRubyJdbcConnection.java +190 -0
  50. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +259 -61
  51. data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +13 -2
  52. data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +192 -15
  53. data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +10 -2
  54. metadata +9 -4
@@ -1,12 +1,35 @@
1
+ # NOTE: file contains code adapted from **sqlserver** adapter, license follows
2
+ =begin
3
+ Copyright (c) 2008-2015
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
+
1
25
  module ArJdbc
2
26
  module MSSQL
3
- # @note Lot of code kindly borrowed from **activerecord-sqlserver-adapter**.
4
27
  module Utils
5
28
 
6
29
  module_function
7
30
 
8
31
  GET_TABLE_NAME_INSERT_UPDATE_RE =
9
- /^\s*(INSERT|EXEC sp_executesql N'INSERT)\s+INTO\s+([^\(\s,]+)\s*|^\s*update\s+([^\(\s,]+)\s*/i
32
+ /^\s*(INSERT|EXEC sp_executesql N'INSERT)(?:\s+INTO)?\s+([^\(\s]+)\s*|^\s*update\s+([^\(\s]+)\s*/i
10
33
 
11
34
  GET_TABLE_NAME_FROM_RE = /\bFROM\s+([^\(\)\s,]+)\s*/i
12
35
 
@@ -553,6 +553,7 @@ module ArJdbc
553
553
  # @private
554
554
  ForeignKeyDefinition = ::ActiveRecord::ConnectionAdapters::ForeignKeyDefinition if ::ActiveRecord::ConnectionAdapters.const_defined? :ForeignKeyDefinition
555
555
 
556
+ # @override
556
557
  def supports_foreign_keys?; true end
557
558
 
558
559
  def foreign_keys(table_name)
@@ -786,22 +787,22 @@ module ArJdbc
786
787
 
787
788
  register_class_with_limit m, %r(char)i, MysqlString
788
789
 
789
- m.register_type %r(tinytext)i, Type::Text.new(limit: 2**8 - 1)
790
- m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1)
791
- m.register_type %r(text)i, Type::Text.new(limit: 2**16 - 1)
792
- m.register_type %r(blob)i, Type::Binary.new(limit: 2**16 - 1)
793
- m.register_type %r(mediumtext)i, Type::Text.new(limit: 2**24 - 1)
794
- m.register_type %r(mediumblob)i, Type::Binary.new(limit: 2**24 - 1)
795
- m.register_type %r(longtext)i, Type::Text.new(limit: 2**32 - 1)
796
- m.register_type %r(longblob)i, Type::Binary.new(limit: 2**32 - 1)
797
- m.register_type %r(^float)i, Type::Float.new(limit: 24)
798
- m.register_type %r(^double)i, Type::Float.new(limit: 53)
799
-
800
- register_integer_type m, %r(^bigint)i, limit: 8
801
- register_integer_type m, %r(^int)i, limit: 4
802
- register_integer_type m, %r(^mediumint)i, limit: 3
803
- register_integer_type m, %r(^smallint)i, limit: 2
804
- register_integer_type m, %r(^tinyint)i, limit: 1
790
+ m.register_type %r(tinytext)i, Type::Text.new(:limit => 2**8 - 1)
791
+ m.register_type %r(tinyblob)i, Type::Binary.new(:limit => 2**8 - 1)
792
+ m.register_type %r(text)i, Type::Text.new(:limit => 2**16 - 1)
793
+ m.register_type %r(blob)i, Type::Binary.new(:limit => 2**16 - 1)
794
+ m.register_type %r(mediumtext)i, Type::Text.new(:limit => 2**24 - 1)
795
+ m.register_type %r(mediumblob)i, Type::Binary.new(:limit => 2**24 - 1)
796
+ m.register_type %r(longtext)i, Type::Text.new(:limit => 2**32 - 1)
797
+ m.register_type %r(longblob)i, Type::Binary.new(:limit => 2**32 - 1)
798
+ m.register_type %r(^float)i, Type::Float.new(:limit => 24)
799
+ m.register_type %r(^double)i, Type::Float.new(:limit => 53)
800
+
801
+ register_integer_type m, %r(^bigint)i, :limit => 8
802
+ register_integer_type m, %r(^int)i, :limit => 4
803
+ register_integer_type m, %r(^mediumint)i, :limit => 3
804
+ register_integer_type m, %r(^smallint)i, :limit => 2
805
+ register_integer_type m, %r(^tinyint)i, :limit => 1
805
806
 
806
807
  m.alias_type %r(tinyint\(1\))i, 'boolean' if emulate_booleans
807
808
  m.alias_type %r(set)i, 'varchar'
@@ -810,13 +811,13 @@ module ArJdbc
810
811
 
811
812
  m.register_type(%r(datetime)i) do |sql_type|
812
813
  precision = extract_precision(sql_type)
813
- MysqlDateTime.new(precision: precision)
814
+ MysqlDateTime.new(:precision => precision)
814
815
  end
815
816
 
816
817
  m.register_type(%r(enum)i) do |sql_type|
817
- limit = sql_type[/^enum\((.+)\)/i, 1]
818
- .split(',').map{|enum| enum.strip.length - 2}.max
819
- MysqlString.new(limit: limit)
818
+ limit = sql_type[/^enum\((.+)\)/i, 1].split(',').
819
+ map{|enum| enum.strip.length - 2}.max
820
+ MysqlString.new(:limit => limit)
820
821
  end
821
822
  end if AR42
822
823
 
@@ -890,7 +891,7 @@ module ArJdbc
890
891
  version << jdbc_connection.serverMinorVersion
891
892
  version << jdbc_connection.serverSubMinorVersion
892
893
  else
893
- if match = full_version.match(/^(\d)\.(\d+)\.(\d+)/)
894
+ if match = full_version.match(/^(\d+)\.(\d+)\.(\d+)/)
894
895
  version << match[1].to_i
895
896
  version << match[2].to_i
896
897
  version << match[3].to_i
@@ -1,3 +1,4 @@
1
1
  require 'arjdbc'
2
2
  require 'arjdbc/oracle/adapter'
3
3
  require 'arjdbc/oracle/connection_methods'
4
+ ArJdbc.warn_unsupported_adapter 'oracle', [4, 2] # warns on AR >= 4.2
@@ -1,3 +1,27 @@
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
+
1
25
  ArJdbc.load_java_part :Oracle
2
26
 
3
27
  module ArJdbc
@@ -70,18 +94,35 @@ module ArJdbc
70
94
  def raw(*args)
71
95
  options = args.extract_options!
72
96
  column(args[0], 'raw', options)
73
- end
97
+ end unless AR42
98
+
99
+ def raw(name, options={})
100
+ column(name, :raw, options)
101
+ end if AR42
74
102
 
75
103
  def xml(*args)
76
104
  options = args.extract_options!
77
105
  column(args[0], 'xml', options)
78
- end
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
79
116
  end
80
117
 
81
118
  def table_definition(*args)
82
119
  new_table_definition(TableDefinition, *args)
83
120
  end
84
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
+
85
126
  def self.arel_visitor_type(config = nil)
86
127
  ::Arel::Visitors::Oracle
87
128
  end
@@ -98,6 +139,63 @@ module ArJdbc
98
139
  ADAPTER_NAME
99
140
  end
100
141
 
142
+ def initialize_type_map(m)
143
+ super
144
+
145
+ m.register_type(%r(NUMBER)i) do |sql_type|
146
+ scale = extract_scale(sql_type)
147
+ precision = extract_precision(sql_type)
148
+ limit = extract_limit(sql_type)
149
+ if scale == 0
150
+ if Oracle.emulate_booleans? && limit == 1
151
+ ActiveRecord::Type::Boolean.new
152
+ else
153
+ ActiveRecord::Type::Integer.new(:precision => precision, :limit => limit)
154
+ end
155
+ else
156
+ ActiveRecord::Type::Decimal.new(:precision => precision, :scale => scale)
157
+ end
158
+ end
159
+
160
+ register_class_with_limit m, %r(date)i, ActiveRecord::Type::DateTime
161
+ register_class_with_limit m, %r(raw)i, RawType
162
+ register_class_with_limit m, %r(timestamp)i, TimestampType
163
+
164
+ m.register_type %r(xmltype)i, XmlType.new
165
+ end if AR42
166
+
167
+ def clear_cache!
168
+ super
169
+ reload_type_map
170
+ end if AR42
171
+
172
+ # @private
173
+ class RawType < ActiveRecord::Type::String
174
+ def type; :raw end
175
+ end if AR42
176
+
177
+ # @private
178
+ class TimestampType < ActiveRecord::Type::DateTime
179
+ def type; :timestamp end
180
+ end if AR42
181
+
182
+ # @private
183
+ class XmlType < ActiveRecord::Type::String
184
+ def type; :xml end
185
+
186
+ def type_cast_for_database(value)
187
+ return unless value
188
+ Data.new(super)
189
+ end
190
+
191
+ class Data
192
+ def initialize(value)
193
+ @value = value
194
+ end
195
+ def to_s; @value end
196
+ end
197
+ end if AR42
198
+
101
199
  NATIVE_DATABASE_TYPES = {
102
200
  :primary_key => "NUMBER(38) NOT NULL PRIMARY KEY",
103
201
  :string => { :name => "VARCHAR2", :limit => 255 },
@@ -142,9 +240,16 @@ module ArJdbc
142
240
  def column_name_length; IDENTIFIER_LENGTH; end
143
241
  def sequence_name_length; IDENTIFIER_LENGTH end
144
242
 
145
- def default_sequence_name(table_name, column = nil)
146
- # TODO: remove schema prefix if present (before truncating)
147
- "#{table_name.to_s[0, IDENTIFIER_LENGTH - 4]}_seq"
243
+ # @private
244
+ # Will take all or first 26 characters of table name and append _seq suffix
245
+ def default_sequence_name(table_name, primary_key = nil)
246
+ len = IDENTIFIER_LENGTH - 4
247
+ table_name.to_s.gsub (/(^|\.)([\w$-]{1,#{len}})([\w$-]*)$/), '\1\2_seq'
248
+ end
249
+
250
+ # @private
251
+ def default_trigger_name(table_name)
252
+ "#{table_name.to_s[0, IDENTIFIER_LENGTH - 4]}_pkt"
148
253
  end
149
254
 
150
255
  # @override
@@ -177,7 +282,7 @@ module ArJdbc
177
282
  seq_name = options.key?(:sequence_name) ? # pass nil/false - no sequence
178
283
  options[:sequence_name] : default_sequence_name(name)
179
284
  return outcome unless seq_name
180
- execute "DROP SEQUENCE #{quote_table_name(seq_name)}" rescue nil
285
+ execute_quietly "DROP SEQUENCE #{quote_table_name(seq_name)}"
181
286
  end
182
287
 
183
288
  # @override
@@ -206,7 +311,7 @@ module ArJdbc
206
311
  end
207
312
 
208
313
  def indexes(table, name = nil)
209
- @connection.indexes(table, name, @connection.connection.meta_data.user_name)
314
+ @connection.indexes(table, name, schema_owner)
210
315
  end
211
316
 
212
317
  # @note Only used with (non-AREL) ActiveRecord **2.3**.
@@ -245,13 +350,66 @@ module ArJdbc
245
350
  # no RELEASE SAVEPOINT statement in Oracle (JDBC driver throws "Unsupported feature")
246
351
  end
247
352
 
353
+ # @override
354
+ def add_index(table_name, column_name, options = {})
355
+ index_name, index_type, quoted_column_names, tablespace, index_options = add_index_options(table_name, column_name, options)
356
+ execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{quoted_column_names})#{tablespace} #{index_options}"
357
+ if index_type == 'UNIQUE'
358
+ unless quoted_column_names =~ /\(.*\)/
359
+ execute "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{quote_column_name(index_name)} #{index_type} (#{quoted_column_names})"
360
+ end
361
+ end
362
+ end if AR42
363
+
364
+ # @private
365
+ def add_index_options(table_name, column_name, options = {})
366
+ column_names = Array(column_name)
367
+ index_name = index_name(table_name, column: column_names)
368
+
369
+ options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :tablespace, :options, :using)
370
+
371
+ index_type = options[:unique] ? "UNIQUE" : ""
372
+ index_name = options[:name].to_s if options.key?(:name)
373
+ tablespace = '' # tablespace_for(:index, options[:tablespace])
374
+ max_index_length = options.fetch(:internal, false) ? index_name_length : allowed_index_name_length
375
+ index_options = '' # index_options = options[:options]
376
+
377
+ if index_name.to_s.length > max_index_length
378
+ raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{max_index_length} characters"
379
+ end
380
+ if index_name_exists?(table_name, index_name, false)
381
+ raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists"
382
+ end
383
+
384
+ quoted_column_names = column_names.map { |e| quote_column_name(e, true) }.join(", ")
385
+ [ index_name, index_type, quoted_column_names, tablespace, index_options ]
386
+ end if AR42
387
+
388
+ # @override
389
+ def remove_index(table_name, options = {})
390
+ index_name = index_name(table_name, options)
391
+ unless index_name_exists?(table_name, index_name, true)
392
+ # sometimes options can be String or Array with column names
393
+ options = {} unless options.is_a?(Hash)
394
+ if options.has_key? :name
395
+ options_without_column = options.dup
396
+ options_without_column.delete :column
397
+ index_name_without_column = index_name(table_name, options_without_column)
398
+ return index_name_without_column if index_name_exists?(table_name, index_name_without_column, false)
399
+ end
400
+ raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' does not exist"
401
+ end
402
+ execute "ALTER TABLE #{quote_table_name(table_name)} DROP CONSTRAINT #{quote_column_name(index_name)}" rescue nil
403
+ execute "DROP INDEX #{quote_column_name(index_name)}"
404
+ end if AR42
405
+
406
+ # @private
248
407
  def remove_index(table_name, options = {})
249
408
  execute "DROP INDEX #{index_name(table_name, options)}"
250
- end
409
+ end unless AR42
251
410
 
252
411
  def change_column_default(table_name, column_name, default)
253
- execute "ALTER TABLE #{quote_table_name(table_name)} " +
254
- "MODIFY #{quote_column_name(column_name)} DEFAULT #{quote(default)}"
412
+ execute "ALTER TABLE #{quote_table_name(table_name)} MODIFY #{quote_column_name(column_name)} DEFAULT #{quote(default)}"
255
413
  end
256
414
 
257
415
  # @override
@@ -396,17 +554,24 @@ module ArJdbc
396
554
  name.to_s.split('.').map{ |n| n.split('@').map{ |m| quote_column_name(m) }.join('@') }.join('.')
397
555
  end
398
556
 
557
+ # @private
558
+ LOWER_CASE_ONLY = /\A[a-z][a-z_0-9\$#]*\Z/
559
+
399
560
  # @override
400
- def quote_column_name(name)
561
+ def quote_column_name(name, handle_expression = false)
401
562
  # if only valid lowercase column characters in name
402
- if ( name = name.to_s ) =~ /\A[a-z][a-z_0-9\$#]*\Z/
563
+ if ( name = name.to_s ) =~ LOWER_CASE_ONLY
403
564
  # putting double-quotes around an identifier causes Oracle to treat the
404
565
  # identifier as case sensitive (otherwise assumes case-insensitivity) !
405
566
  # all upper case is an exception, where double-quotes are meaningless
406
567
  "\"#{name.upcase}\"" # name.upcase
407
568
  else
408
- # remove double quotes which cannot be used inside quoted identifier
409
- "\"#{name.gsub('"', '')}\""
569
+ if handle_expression
570
+ name =~ /^[a-z][a-z_0-9\$#\-]*$/i ? "\"#{name}\"" : name
571
+ else
572
+ # remove double quotes which cannot be used inside quoted identifier
573
+ "\"#{name.gsub('"', '')}\""
574
+ end
410
575
  end
411
576
  end
412
577
 
@@ -516,23 +681,110 @@ module ArJdbc
516
681
  result
517
682
  end
518
683
 
684
+ @@do_not_prefetch_primary_key = {}
685
+
519
686
  # Returns true for Oracle adapter (since Oracle requires primary key
520
687
  # values to be pre-fetched before insert).
521
688
  # @see #next_sequence_value
522
689
  # @override
523
690
  def prefetch_primary_key?(table_name = nil)
524
691
  return true if table_name.nil?
525
- table_name = table_name.to_s
526
- columns(table_name).count { |column| column.primary } == 1
692
+ do_not_prefetch_hash = @@do_not_prefetch_primary_key
693
+ do_not_prefetch = do_not_prefetch_hash[ table_name = table_name.to_s ]
694
+ if do_not_prefetch.nil?
695
+ owner, desc_table_name, db_link = describe(table_name)
696
+ do_not_prefetch_hash[table_name] = do_not_prefetch =
697
+ ! has_primary_key?(table_name, owner, desc_table_name, db_link) ||
698
+ has_primary_key_trigger?(table_name, owner, desc_table_name, db_link)
699
+ end
700
+ ! do_not_prefetch
527
701
  end
528
702
 
529
- # @override
703
+ # used to clear prefetch primary key flag for all tables
704
+ # @private
705
+ def clear_prefetch_primary_key; @@do_not_prefetch_primary_key = {} end
706
+
707
+ # @private
708
+ def has_primary_key?(table_name, owner = nil, desc_table_name = nil, db_link = nil)
709
+ ! pk_and_sequence_for(table_name, owner, desc_table_name, db_link).nil?
710
+ end
711
+
712
+ # @private check if table has primary key trigger with _pkt suffix
713
+ def has_primary_key_trigger?(table_name, owner = nil, desc_table_name = nil, db_link = nil)
714
+ (owner, desc_table_name, db_link) = describe(table_name) unless desc_table_name
715
+
716
+ trigger_name = default_trigger_name(table_name).upcase
717
+ pkt_sql = "SELECT trigger_name FROM all_triggers#{db_link} WHERE owner = '#{owner}'" <<
718
+ " AND trigger_name = '#{trigger_name}'" <<
719
+ " AND table_owner = '#{owner}'" <<
720
+ " AND table_name = '#{desc_table_name}'" <<
721
+ " AND status = 'ENABLED'"
722
+ select_value(pkt_sql, 'Primary Key Trigger') ? true : false
723
+ end
724
+
725
+ # use in set_sequence_name to avoid fetching primary key value from sequence
726
+ AUTOGENERATED_SEQUENCE_NAME = 'autogenerated'.freeze
727
+
728
+ # Returns the next sequence value from a sequence generator. Not generally
729
+ # called directly; used by ActiveRecord to get the next primary key value
730
+ # when inserting a new database record (see #prefetch_primary_key?).
530
731
  def next_sequence_value(sequence_name)
732
+ # if sequence_name is set to :autogenerated then it means that primary key will be populated by trigger
733
+ return nil if sequence_name == AUTOGENERATED_SEQUENCE_NAME
531
734
  sequence_name = quote_table_name(sequence_name)
532
735
  sql = "SELECT #{sequence_name}.NEXTVAL id FROM dual"
533
736
  log(sql, 'SQL') { @connection.next_sequence_value(sequence_name) }
534
737
  end
535
738
 
739
+ def pk_and_sequence_for(table_name, owner = nil, desc_table_name = nil, db_link = nil)
740
+ (owner, desc_table_name, db_link) = describe(table_name) unless desc_table_name
741
+
742
+ seqs = "SELECT us.sequence_name" <<
743
+ " FROM all_sequences#{db_link} us" <<
744
+ " WHERE us.sequence_owner = '#{owner}'" <<
745
+ " AND us.sequence_name = '#{desc_table_name}_SEQ'"
746
+ seqs = select_values(seqs, 'Sequence')
747
+
748
+ # changed back from user_constraints to all_constraints for consistency
749
+ pks = "SELECT cc.column_name" <<
750
+ " FROM all_constraints#{db_link} c, all_cons_columns#{db_link} cc" <<
751
+ " WHERE c.owner = '#{owner}'" <<
752
+ " AND c.table_name = '#{desc_table_name}'" <<
753
+ " AND c.constraint_type = 'P'" <<
754
+ " AND cc.owner = c.owner" <<
755
+ " AND cc.constraint_name = c.constraint_name"
756
+ pks = select_values(pks, 'Primary Key')
757
+
758
+ # only support single column keys
759
+ pks.size == 1 ? [oracle_downcase(pks.first), oracle_downcase(seqs.first)] : nil
760
+ end
761
+ private :pk_and_sequence_for
762
+
763
+ # Returns just a table's primary key
764
+ def primary_key(table_name)
765
+ pk_and_sequence = pk_and_sequence_for(table_name)
766
+ pk_and_sequence && pk_and_sequence.first
767
+ end
768
+
769
+ # @override
770
+ def supports_foreign_keys?; true end
771
+
772
+ # @private
773
+ def disable_referential_integrity
774
+ sql_constraints = "SELECT constraint_name, owner, table_name FROM user_constraints WHERE constraint_type = 'R' AND status = 'ENABLED'"
775
+ old_constraints = select_all(sql_constraints)
776
+ begin
777
+ old_constraints.each do |constraint|
778
+ execute "ALTER TABLE #{constraint["table_name"]} DISABLE CONSTRAINT #{constraint["constraint_name"]}"
779
+ end
780
+ yield
781
+ ensure
782
+ old_constraints.reverse_each do |constraint|
783
+ execute "ALTER TABLE #{constraint["table_name"]} ENABLE CONSTRAINT #{constraint["constraint_name"]}"
784
+ end
785
+ end
786
+ end
787
+
536
788
  # @override (for AR <= 3.0)
537
789
  def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
538
790
  # if PK is already pre-fetched from sequence or if there is no PK :
@@ -647,6 +899,26 @@ module ArJdbc
647
899
  end
648
900
  end
649
901
 
902
+ # default schema owner
903
+ def schema_owner(force = true)
904
+ unless defined? @schema_owner
905
+ username = config[:username] ? config[:username].to_s : nil
906
+ username = jdbc_connection.meta_data.user_name if force && username.nil?
907
+ @schema_owner = username.nil? ? nil : username.upcase
908
+ end
909
+ @schema_owner
910
+ end
911
+
912
+ # do not force reading schema_owner as we're read on our own ...
913
+ def describe(table_name, owner = schema_owner(false))
914
+ @connection.describe(table_name, owner)
915
+ end
916
+
917
+ def oracle_downcase(column_name)
918
+ return nil if column_name.nil?
919
+ column_name =~ /[a-z]/ ? column_name : column_name.downcase
920
+ end
921
+
650
922
  end
651
923
  end
652
924
 
@@ -660,8 +932,8 @@ module ActiveRecord::ConnectionAdapters
660
932
  include ::ArJdbc::Oracle
661
933
  include ::ArJdbc::Util::QuotedCache
662
934
 
663
- # By default, the MysqlAdapter will consider all columns of type
664
- # <tt>tinyint(1)</tt> as boolean. If you wish to disable this :
935
+ # By default, the OracleAdapter will consider all columns of type
936
+ # <tt>NUMBER(1)</tt> as boolean. If you wish to disable this :
665
937
  #
666
938
  # ActiveRecord::ConnectionAdapters::OracleAdapter.emulate_booleans = false
667
939
  #