activerecord-jdbc-adapter 1.3.17 → 1.3.18

Sign up to get free protection for your applications and to get access to all the features.
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
  #