activerecord-oracle_enhanced-adapter 1.7.3 → 1.8.0
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.
- checksums.yaml +4 -4
- data/History.md +288 -0
- data/README.md +44 -0
- data/VERSION +1 -1
- data/lib/active_record/connection_adapters/emulation/oracle_adapter.rb +1 -1
- data/lib/active_record/connection_adapters/oracle_enhanced/column.rb +7 -63
- data/lib/active_record/connection_adapters/oracle_enhanced/column_dumper.rb +6 -52
- data/lib/active_record/connection_adapters/oracle_enhanced/connection.rb +13 -16
- data/lib/active_record/connection_adapters/oracle_enhanced/context_index.rb +117 -119
- data/lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb +33 -27
- data/lib/active_record/connection_adapters/oracle_enhanced/database_tasks.rb +10 -10
- data/lib/active_record/connection_adapters/oracle_enhanced/jdbc_connection.rb +59 -77
- data/lib/active_record/connection_adapters/oracle_enhanced/jdbc_quoting.rb +0 -4
- data/lib/active_record/connection_adapters/oracle_enhanced/oci_connection.rb +56 -82
- data/lib/active_record/connection_adapters/oracle_enhanced/oci_quoting.rb +4 -4
- data/lib/active_record/connection_adapters/oracle_enhanced/procedures.rb +77 -77
- data/lib/active_record/connection_adapters/oracle_enhanced/quoting.rb +16 -45
- data/lib/active_record/connection_adapters/oracle_enhanced/schema_creation.rb +60 -65
- data/lib/active_record/connection_adapters/oracle_enhanced/schema_definitions.rb +33 -51
- data/lib/active_record/connection_adapters/oracle_enhanced/schema_dumper.rb +149 -162
- data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb +119 -122
- data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements_ext.rb +3 -4
- data/lib/active_record/connection_adapters/oracle_enhanced/structure_dump.rb +69 -107
- data/lib/active_record/connection_adapters/oracle_enhanced/version.rb +1 -1
- data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +315 -538
- data/lib/active_record/oracle_enhanced/type/boolean.rb +7 -11
- data/lib/active_record/oracle_enhanced/type/integer.rb +4 -5
- data/lib/active_record/oracle_enhanced/type/json.rb +8 -0
- data/lib/active_record/oracle_enhanced/type/national_character_string.rb +1 -2
- data/lib/active_record/oracle_enhanced/type/raw.rb +2 -4
- data/lib/active_record/oracle_enhanced/type/string.rb +2 -3
- data/lib/active_record/oracle_enhanced/type/text.rb +2 -4
- data/lib/active_record/oracle_enhanced/type/timestamptz.rb +23 -0
- data/lib/activerecord-oracle_enhanced-adapter.rb +14 -13
- data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +129 -166
- data/spec/active_record/connection_adapters/oracle_enhanced_connection_spec.rb +50 -33
- data/spec/active_record/connection_adapters/oracle_enhanced_context_index_spec.rb +44 -42
- data/spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb +283 -378
- data/spec/active_record/connection_adapters/oracle_enhanced_database_tasks_spec.rb +14 -7
- data/spec/active_record/connection_adapters/oracle_enhanced_dbms_output_spec.rb +5 -7
- data/spec/active_record/connection_adapters/oracle_enhanced_dirty_spec.rb +118 -127
- data/spec/active_record/connection_adapters/oracle_enhanced_emulate_oracle_adapter_spec.rb +2 -3
- data/spec/active_record/connection_adapters/oracle_enhanced_procedures_spec.rb +74 -78
- data/spec/active_record/connection_adapters/oracle_enhanced_schema_dump_spec.rb +68 -83
- data/spec/active_record/connection_adapters/oracle_enhanced_schema_statements_spec.rb +308 -321
- data/spec/active_record/connection_adapters/oracle_enhanced_structure_dump_spec.rb +103 -48
- data/spec/spec_config.yaml.template +11 -0
- data/spec/spec_helper.rb +59 -60
- data/spec/support/alter_system_user_password.sql +2 -0
- data/spec/support/create_oracle_enhanced_users.sql +31 -0
- metadata +25 -25
- data/.rspec +0 -2
- data/Gemfile +0 -22
- data/RUNNING_TESTS.md +0 -50
- data/Rakefile +0 -45
- data/activerecord-oracle_enhanced-adapter.gemspec +0 -95
- data/lib/active_record/connection_adapters/oracle_enhanced/cpk.rb +0 -21
- data/spec/active_record/connection_adapters/oracle_enhanced_cpk_spec.rb +0 -113
|
@@ -28,17 +28,18 @@
|
|
|
28
28
|
# contribution.
|
|
29
29
|
# portions Copyright 2005 Graham Jenkins
|
|
30
30
|
|
|
31
|
-
require
|
|
32
|
-
require
|
|
33
|
-
require
|
|
34
|
-
require
|
|
35
|
-
require
|
|
36
|
-
require
|
|
37
|
-
require
|
|
38
|
-
require
|
|
39
|
-
require
|
|
40
|
-
|
|
41
|
-
|
|
31
|
+
require "active_record/connection_adapters/abstract_adapter"
|
|
32
|
+
require "active_record/connection_adapters/statement_pool"
|
|
33
|
+
require "active_record/connection_adapters/oracle_enhanced/connection"
|
|
34
|
+
require "active_record/connection_adapters/oracle_enhanced/database_statements"
|
|
35
|
+
require "active_record/connection_adapters/oracle_enhanced/schema_statements"
|
|
36
|
+
require "active_record/connection_adapters/oracle_enhanced/schema_statements_ext"
|
|
37
|
+
require "active_record/connection_adapters/oracle_enhanced/column_dumper"
|
|
38
|
+
require "active_record/connection_adapters/oracle_enhanced/context_index"
|
|
39
|
+
require "active_record/connection_adapters/oracle_enhanced/column"
|
|
40
|
+
require "active_record/connection_adapters/oracle_enhanced/quoting"
|
|
41
|
+
|
|
42
|
+
require "digest/sha1"
|
|
42
43
|
|
|
43
44
|
ActiveRecord::Base.class_eval do
|
|
44
45
|
class_attribute :custom_create_method, :custom_update_method, :custom_delete_method
|
|
@@ -46,90 +47,12 @@ end
|
|
|
46
47
|
|
|
47
48
|
module ActiveRecord
|
|
48
49
|
class Base
|
|
49
|
-
|
|
50
|
-
# Specify table columns which should be ignored by ActiveRecord, e.g.:
|
|
51
|
-
#
|
|
52
|
-
# ignore_table_columns :attribute1, :attribute2
|
|
53
|
-
def self.ignore_table_columns(*args)
|
|
54
|
-
connection.ignore_table_columns(table_name,*args)
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
# Specify which table columns should be typecasted to Date (without time), e.g.:
|
|
58
|
-
#
|
|
59
|
-
# set_date_columns :created_on, :updated_on
|
|
60
|
-
def self.set_date_columns(*args)
|
|
61
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
|
62
|
-
'set_date_columns` has been deprecated. Please use Rails attribute API.
|
|
63
|
-
MSG
|
|
64
|
-
# connection.set_type_for_columns(table_name,:date,*args)
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
# Specify which table columns should be typecasted to Time (or DateTime), e.g.:
|
|
68
|
-
#
|
|
69
|
-
# set_datetime_columns :created_date, :updated_date
|
|
70
|
-
def self.set_datetime_columns(*args)
|
|
71
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
|
72
|
-
'set_datetime_columns` has been deprecated. Please use Rails attribute API.
|
|
73
|
-
MSG
|
|
74
|
-
# connection.set_type_for_columns(table_name,:datetime,*args)
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
# Specify which table columns should be typecasted to boolean values +true+ or +false+, e.g.:
|
|
78
|
-
#
|
|
79
|
-
# set_boolean_columns :is_valid, :is_completed
|
|
80
|
-
def self.set_boolean_columns(*args)
|
|
81
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
|
82
|
-
'set_boolean_columns` has been deprecated. Please use Rails attribute API.
|
|
83
|
-
MSG
|
|
84
|
-
# connection.set_type_for_columns(table_name,:boolean,*args)
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
# Specify which table columns should be typecasted to integer values.
|
|
88
|
-
# Might be useful to force NUMBER(1) column to be integer and not boolean, or force NUMBER column without
|
|
89
|
-
# scale to be retrieved as integer and not decimal. Example:
|
|
90
|
-
#
|
|
91
|
-
# set_integer_columns :version_number, :object_identifier
|
|
92
|
-
def self.set_integer_columns(*args)
|
|
93
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
|
94
|
-
'set_integer_columns` has been deprecated. Please use Rails attribute API.
|
|
95
|
-
MSG
|
|
96
|
-
# connection.set_type_for_columns(table_name,:integer,*args)
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
# Specify which table columns should be typecasted to string values.
|
|
100
|
-
# Might be useful to specify that columns should be string even if its name matches boolean column criteria.
|
|
101
|
-
#
|
|
102
|
-
# set_string_columns :active_flag
|
|
103
|
-
def self.set_string_columns(*args)
|
|
104
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
|
105
|
-
'set_string_columns` has been deprecated. Please use Rails attribute API.
|
|
106
|
-
MSG
|
|
107
|
-
# connection.set_type_for_columns(table_name,:string,*args)
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
# Get table comment from schema definition.
|
|
111
|
-
def self.table_comment
|
|
112
|
-
#TODO: may be deprecated
|
|
113
|
-
connection.table_comment(self.table_name)
|
|
114
|
-
end
|
|
115
|
-
|
|
116
50
|
def self.lob_columns
|
|
117
51
|
columns.select do |column|
|
|
118
52
|
column.respond_to?(:lob?) && column.lob?
|
|
119
53
|
end
|
|
120
54
|
end
|
|
121
55
|
|
|
122
|
-
def self.virtual_columns
|
|
123
|
-
columns.select do |column|
|
|
124
|
-
column.respond_to?(:virtual?) && column.virtual?
|
|
125
|
-
end
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
def arel_attributes_with_values(attribute_names)
|
|
129
|
-
virtual_column_names = self.class.virtual_columns.map(&:name)
|
|
130
|
-
super(attribute_names - virtual_column_names)
|
|
131
|
-
end
|
|
132
|
-
|
|
133
56
|
# After setting large objects to empty, select the OCI8::LOB
|
|
134
57
|
# and write back the data.
|
|
135
58
|
before_update :record_changed_lobs
|
|
@@ -137,21 +60,21 @@ module ActiveRecord
|
|
|
137
60
|
|
|
138
61
|
private
|
|
139
62
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
63
|
+
def enhanced_write_lobs
|
|
64
|
+
if self.class.connection.is_a?(ConnectionAdapters::OracleEnhancedAdapter) &&
|
|
65
|
+
!(
|
|
66
|
+
(self.class.custom_create_method || self.class.custom_create_method) ||
|
|
67
|
+
(self.class.custom_update_method || self.class.custom_update_method)
|
|
68
|
+
)
|
|
69
|
+
self.class.connection.write_lobs(self.class.table_name, self.class, attributes, @changed_lob_columns)
|
|
70
|
+
end
|
|
147
71
|
end
|
|
148
|
-
end
|
|
149
72
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
73
|
+
def record_changed_lobs
|
|
74
|
+
@changed_lob_columns = self.class.lob_columns.select do |col|
|
|
75
|
+
self.attribute_changed?(col.name) && !self.class.readonly_attributes.to_a.include?(col.name)
|
|
76
|
+
end
|
|
153
77
|
end
|
|
154
|
-
end
|
|
155
78
|
end
|
|
156
79
|
end
|
|
157
80
|
|
|
@@ -162,7 +85,7 @@ module ActiveRecord
|
|
|
162
85
|
if config[:emulate_oracle_adapter] == true
|
|
163
86
|
# allows the enhanced adapter to look like the OracleAdapter. Useful to pick up
|
|
164
87
|
# conditionals in the rails activerecord test suite
|
|
165
|
-
require
|
|
88
|
+
require "active_record/connection_adapters/emulation/oracle_adapter"
|
|
166
89
|
ConnectionAdapters::OracleAdapter.new(
|
|
167
90
|
ConnectionAdapters::OracleEnhancedConnection.create(config), logger, config)
|
|
168
91
|
else
|
|
@@ -173,7 +96,6 @@ module ActiveRecord
|
|
|
173
96
|
end
|
|
174
97
|
|
|
175
98
|
module ConnectionAdapters #:nodoc:
|
|
176
|
-
|
|
177
99
|
# Oracle enhanced adapter will work with both
|
|
178
100
|
# Ruby 1.8/1.9 ruby-oci8 gem (which provides interface to Oracle OCI client)
|
|
179
101
|
# or with JRuby and Oracle JDBC driver.
|
|
@@ -260,92 +182,16 @@ module ActiveRecord
|
|
|
260
182
|
|
|
261
183
|
##
|
|
262
184
|
# :singleton-method:
|
|
263
|
-
#
|
|
264
|
-
# to
|
|
265
|
-
# If you wish that DATE values with hour, minutes and seconds equal to 0 are typecasted
|
|
266
|
-
# to Date then you can add the following line to your initializer file:
|
|
185
|
+
# OracleEnhancedAdapter will use the default tablespace, but if you want specific types of
|
|
186
|
+
# objects to go into specific tablespaces, specify them like this in an initializer:
|
|
267
187
|
#
|
|
268
|
-
# ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.
|
|
188
|
+
# ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.default_tablespaces =
|
|
189
|
+
# {:clob => 'TS_LOB', :blob => 'TS_LOB', :index => 'TS_INDEX', :table => 'TS_DATA'}
|
|
269
190
|
#
|
|
270
|
-
#
|
|
271
|
-
#
|
|
272
|
-
cattr_accessor :emulate_dates
|
|
273
|
-
self.emulate_dates = false
|
|
274
|
-
|
|
275
|
-
##
|
|
276
|
-
# :singleton-method:
|
|
277
|
-
# OracleEnhancedAdapter will use the default tablespace, but if you want specific types of
|
|
278
|
-
# objects to go into specific tablespaces, specify them like this in an initializer:
|
|
279
|
-
#
|
|
280
|
-
# ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.default_tablespaces =
|
|
281
|
-
# {:clob => 'TS_LOB', :blob => 'TS_LOB', :index => 'TS_INDEX', :table => 'TS_DATA'}
|
|
282
|
-
#
|
|
283
|
-
# Using the :tablespace option where available (e.g create_table) will take precedence
|
|
284
|
-
# over these settings.
|
|
191
|
+
# Using the :tablespace option where available (e.g create_table) will take precedence
|
|
192
|
+
# over these settings.
|
|
285
193
|
cattr_accessor :default_tablespaces
|
|
286
|
-
self.default_tablespaces={}
|
|
287
|
-
|
|
288
|
-
##
|
|
289
|
-
# :singleton-method:
|
|
290
|
-
# By default, the OracleEnhancedAdapter will typecast all columns of type <tt>DATE</tt>
|
|
291
|
-
# to Time or DateTime (if value is out of Time value range) value.
|
|
292
|
-
# If you wish that DATE columns with "date" in their name (e.g. "creation_date") are typecasted
|
|
293
|
-
# to Date then you can add the following line to your initializer file:
|
|
294
|
-
#
|
|
295
|
-
# ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.emulate_dates_by_column_name = true
|
|
296
|
-
#
|
|
297
|
-
# As this option can have side effects when unnecessary typecasting is done it is recommended
|
|
298
|
-
# that Date columns are explicily defined with +set_date_columns+ method.
|
|
299
|
-
cattr_accessor :emulate_dates_by_column_name
|
|
300
|
-
self.emulate_dates_by_column_name = false
|
|
301
|
-
|
|
302
|
-
# Check column name to identify if it is Date (and not Time) column.
|
|
303
|
-
# Is used if +emulate_dates_by_column_name+ option is set to +true+.
|
|
304
|
-
# Override this method definition in initializer file if different Date column recognition is needed.
|
|
305
|
-
def self.is_date_column?(name, table_name = nil)
|
|
306
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
|
307
|
-
`self.is_date_column?` has been deprecated. Please use Rails attribute API.
|
|
308
|
-
MSG
|
|
309
|
-
return false
|
|
310
|
-
# name =~ /(^|_)date(_|$)/i
|
|
311
|
-
end
|
|
312
|
-
|
|
313
|
-
# instance method uses at first check if column type defined at class level
|
|
314
|
-
def is_date_column?(name, table_name = nil) #:nodoc:
|
|
315
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
|
316
|
-
`is_date_column?` has been deprecated. Please use Rails attribute API.
|
|
317
|
-
MSG
|
|
318
|
-
return false
|
|
319
|
-
#case get_type_for_column(table_name, name)
|
|
320
|
-
#when nil
|
|
321
|
-
# self.class.is_date_column?(name, table_name)
|
|
322
|
-
#when :date
|
|
323
|
-
# true
|
|
324
|
-
#else
|
|
325
|
-
# false
|
|
326
|
-
#end
|
|
327
|
-
end
|
|
328
|
-
|
|
329
|
-
##
|
|
330
|
-
# :singleton-method:
|
|
331
|
-
# By default, the OracleEnhancedAdapter will typecast all columns of type <tt>NUMBER</tt>
|
|
332
|
-
# (without precision or scale) to Float or BigDecimal value.
|
|
333
|
-
# If you wish that NUMBER columns with name "id" or that end with "_id" are typecasted
|
|
334
|
-
# to Integer then you can add the following line to your initializer file:
|
|
335
|
-
#
|
|
336
|
-
# ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.emulate_integers_by_column_name = true
|
|
337
|
-
cattr_accessor :emulate_integers_by_column_name
|
|
338
|
-
self.emulate_integers_by_column_name = false
|
|
339
|
-
|
|
340
|
-
# Check column name to identify if it is Integer (and not Float or BigDecimal) column.
|
|
341
|
-
# Is used if +emulate_integers_by_column_name+ option is set to +true+.
|
|
342
|
-
# Override this method definition in initializer file if different Integer column recognition is needed.
|
|
343
|
-
def self.is_integer_column?(name, table_name = nil)
|
|
344
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
|
345
|
-
`is_integer_column?` has been deprecated. Please use Rails attribute API.
|
|
346
|
-
MSG
|
|
347
|
-
return false
|
|
348
|
-
end
|
|
194
|
+
self.default_tablespaces = {}
|
|
349
195
|
|
|
350
196
|
##
|
|
351
197
|
# :singleton-method:
|
|
@@ -356,78 +202,32 @@ module ActiveRecord
|
|
|
356
202
|
cattr_accessor :emulate_booleans_from_strings
|
|
357
203
|
self.emulate_booleans_from_strings = false
|
|
358
204
|
|
|
359
|
-
# Check column name to identify if it is boolean (and not String) column.
|
|
360
|
-
# Is used if +emulate_booleans_from_strings+ option is set to +true+.
|
|
361
|
-
# Override this method definition in initializer file if different boolean column recognition is needed.
|
|
362
|
-
def self.is_boolean_column?(name, sql_type, table_name = nil)
|
|
363
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
|
364
|
-
`is_boolean_column?` has been deprecated. Please use Rails attribute API.
|
|
365
|
-
MSG
|
|
366
|
-
return false
|
|
367
|
-
# return true if ["CHAR(1)","VARCHAR2(1)"].include?(sql_type)
|
|
368
|
-
# sql_type =~ /^VARCHAR2/ && (name =~ /_flag$/i || name =~ /_yn$/i)
|
|
369
|
-
end
|
|
370
|
-
|
|
371
|
-
# How boolean value should be quoted to String.
|
|
372
|
-
# Used if +emulate_booleans_from_strings+ option is set to +true+.
|
|
373
|
-
def self.boolean_to_string(bool)
|
|
374
|
-
bool ? "Y" : "N"
|
|
375
|
-
end
|
|
376
|
-
|
|
377
205
|
##
|
|
378
206
|
# :singleton-method:
|
|
379
|
-
#
|
|
207
|
+
# By default, OracleEnhanced adapter will use Oracle12 visitor
|
|
208
|
+
# if database version is Oracle 12.1.
|
|
209
|
+
# If you wish to use Oracle visitor which is intended to work with Oracle 11.2 or lower
|
|
210
|
+
# for Oracle 12.1 database you can add the following line to your initializer file:
|
|
380
211
|
#
|
|
381
|
-
# ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.
|
|
382
|
-
cattr_accessor :
|
|
383
|
-
self.
|
|
212
|
+
# ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.use_old_oracle_visitor = true
|
|
213
|
+
cattr_accessor :use_old_oracle_visitor
|
|
214
|
+
self.use_old_oracle_visitor = false
|
|
384
215
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
# Specify non-default time format that should be used when assigning string values to :datetime columns, e.g.:
|
|
388
|
-
#
|
|
389
|
-
# ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.string_to_time_format = “%d.%m.%Y %H:%M:%S”
|
|
390
|
-
cattr_accessor :string_to_time_format
|
|
391
|
-
self.string_to_time_format = nil
|
|
392
|
-
|
|
393
|
-
class StatementPool
|
|
394
|
-
include Enumerable
|
|
395
|
-
|
|
396
|
-
def initialize(connection, max = 300)
|
|
397
|
-
@connection = connection
|
|
398
|
-
@max = max
|
|
399
|
-
@cache = {}
|
|
400
|
-
end
|
|
401
|
-
|
|
402
|
-
def each(&block); @cache.each(&block); end
|
|
403
|
-
def key?(key); @cache.key?(key); end
|
|
404
|
-
def [](key); @cache[key]; end
|
|
405
|
-
def length; @cache.length; end
|
|
406
|
-
def delete(key); @cache.delete(key); end
|
|
407
|
-
|
|
408
|
-
def []=(sql, key)
|
|
409
|
-
while @max <= @cache.size
|
|
410
|
-
@cache.shift.last.close
|
|
411
|
-
end
|
|
412
|
-
@cache[sql] = key
|
|
413
|
-
end
|
|
216
|
+
class StatementPool < ConnectionAdapters::StatementPool
|
|
217
|
+
private
|
|
414
218
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
cursor.close
|
|
219
|
+
def dealloc(stmt)
|
|
220
|
+
stmt.close
|
|
418
221
|
end
|
|
419
|
-
@cache.clear
|
|
420
|
-
end
|
|
421
222
|
end
|
|
422
223
|
|
|
423
224
|
def initialize(connection, logger = nil, config = {}) # :nodoc:
|
|
424
225
|
super(connection, logger, config)
|
|
425
|
-
@
|
|
426
|
-
@statements = StatementPool.new(connection, config.fetch(:statement_limit) { 250 })
|
|
226
|
+
@statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit]))
|
|
427
227
|
@enable_dbms_output = false
|
|
428
228
|
end
|
|
429
229
|
|
|
430
|
-
ADAPTER_NAME =
|
|
230
|
+
ADAPTER_NAME = "OracleEnhanced".freeze
|
|
431
231
|
|
|
432
232
|
def adapter_name #:nodoc:
|
|
433
233
|
ADAPTER_NAME
|
|
@@ -441,14 +241,6 @@ module ActiveRecord
|
|
|
441
241
|
end
|
|
442
242
|
end
|
|
443
243
|
|
|
444
|
-
def supports_migrations? #:nodoc:
|
|
445
|
-
true
|
|
446
|
-
end
|
|
447
|
-
|
|
448
|
-
def supports_primary_key? #:nodoc:
|
|
449
|
-
true
|
|
450
|
-
end
|
|
451
|
-
|
|
452
244
|
def supports_savepoints? #:nodoc:
|
|
453
245
|
true
|
|
454
246
|
end
|
|
@@ -461,12 +253,16 @@ module ActiveRecord
|
|
|
461
253
|
true
|
|
462
254
|
end
|
|
463
255
|
|
|
256
|
+
def supports_foreign_keys_in_create?
|
|
257
|
+
supports_foreign_keys?
|
|
258
|
+
end
|
|
259
|
+
|
|
464
260
|
def supports_views?
|
|
465
261
|
true
|
|
466
262
|
end
|
|
467
263
|
|
|
468
264
|
def supports_fetch_first_n_rows_and_offset?
|
|
469
|
-
if @connection.database_version
|
|
265
|
+
if !use_old_oracle_visitor && @connection.database_version.first >= 12
|
|
470
266
|
true
|
|
471
267
|
else
|
|
472
268
|
false
|
|
@@ -474,56 +270,88 @@ module ActiveRecord
|
|
|
474
270
|
end
|
|
475
271
|
|
|
476
272
|
def supports_datetime_with_precision?
|
|
477
|
-
|
|
478
|
-
#to map :datetime as DATE
|
|
479
|
-
@connection.database_version.first >= 9
|
|
273
|
+
true
|
|
480
274
|
end
|
|
481
275
|
|
|
482
276
|
def supports_comments?
|
|
483
277
|
true
|
|
484
278
|
end
|
|
485
279
|
|
|
280
|
+
def supports_multi_insert?
|
|
281
|
+
@connection.database_version.to_s >= [11, 2].to_s
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
def supports_virtual_columns?
|
|
285
|
+
@connection.database_version.first >= 11
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
def supports_json?
|
|
289
|
+
# No migration supported for :json type due to there is no `JSON` data type
|
|
290
|
+
# in Oracle Database itself.
|
|
291
|
+
#
|
|
292
|
+
# 1.Define :string or :text in migration
|
|
293
|
+
#
|
|
294
|
+
# create_table :test_posts, force: true do |t|
|
|
295
|
+
# t.string :title
|
|
296
|
+
# t.text :article
|
|
297
|
+
# end
|
|
298
|
+
#
|
|
299
|
+
# 2. Set :json attributes
|
|
300
|
+
#
|
|
301
|
+
# class TestPost < ActiveRecord::Base
|
|
302
|
+
# attribute :title, :json
|
|
303
|
+
# attribute :article, :json
|
|
304
|
+
# end
|
|
305
|
+
#
|
|
306
|
+
# 3. Add `is json` database constraints by running sql statements
|
|
307
|
+
#
|
|
308
|
+
# alter table test_posts add constraint test_posts_title_is_json check (title is json)
|
|
309
|
+
# alter table test_posts add constraint test_posts_article_is_json check (article is json)
|
|
310
|
+
#
|
|
311
|
+
@connection.database_version.first >= 12
|
|
312
|
+
end
|
|
313
|
+
|
|
486
314
|
#:stopdoc:
|
|
487
315
|
DEFAULT_NLS_PARAMETERS = {
|
|
488
|
-
:
|
|
489
|
-
:
|
|
490
|
-
:
|
|
491
|
-
:
|
|
492
|
-
:
|
|
493
|
-
:
|
|
494
|
-
:
|
|
495
|
-
:
|
|
496
|
-
:
|
|
497
|
-
:
|
|
498
|
-
:
|
|
499
|
-
:
|
|
500
|
-
:
|
|
501
|
-
:
|
|
502
|
-
:
|
|
503
|
-
:
|
|
504
|
-
:
|
|
316
|
+
nls_calendar: nil,
|
|
317
|
+
nls_comp: nil,
|
|
318
|
+
nls_currency: nil,
|
|
319
|
+
nls_date_format: "YYYY-MM-DD HH24:MI:SS",
|
|
320
|
+
nls_date_language: nil,
|
|
321
|
+
nls_dual_currency: nil,
|
|
322
|
+
nls_iso_currency: nil,
|
|
323
|
+
nls_language: nil,
|
|
324
|
+
nls_length_semantics: "CHAR",
|
|
325
|
+
nls_nchar_conv_excp: nil,
|
|
326
|
+
nls_numeric_characters: nil,
|
|
327
|
+
nls_sort: nil,
|
|
328
|
+
nls_territory: nil,
|
|
329
|
+
nls_timestamp_format: "YYYY-MM-DD HH24:MI:SS:FF6",
|
|
330
|
+
nls_timestamp_tz_format: nil,
|
|
331
|
+
nls_time_format: nil,
|
|
332
|
+
nls_time_tz_format: nil
|
|
505
333
|
}
|
|
506
334
|
|
|
507
335
|
#:stopdoc:
|
|
508
336
|
NATIVE_DATABASE_TYPES = {
|
|
509
|
-
:
|
|
510
|
-
:
|
|
511
|
-
:
|
|
512
|
-
:
|
|
513
|
-
:
|
|
514
|
-
:
|
|
515
|
-
:
|
|
516
|
-
:
|
|
517
|
-
:
|
|
518
|
-
:
|
|
519
|
-
:
|
|
520
|
-
:
|
|
521
|
-
:
|
|
522
|
-
:
|
|
337
|
+
primary_key: "NUMBER(38) NOT NULL PRIMARY KEY",
|
|
338
|
+
string: { name: "VARCHAR2", limit: 255 },
|
|
339
|
+
text: { name: "CLOB" },
|
|
340
|
+
integer: { name: "NUMBER", limit: 38 },
|
|
341
|
+
float: { name: "BINARY_FLOAT" },
|
|
342
|
+
decimal: { name: "DECIMAL" },
|
|
343
|
+
datetime: { name: "TIMESTAMP" },
|
|
344
|
+
timestamp: { name: "TIMESTAMP" },
|
|
345
|
+
time: { name: "TIMESTAMP" },
|
|
346
|
+
date: { name: "DATE" },
|
|
347
|
+
binary: { name: "BLOB" },
|
|
348
|
+
boolean: { name: "NUMBER", limit: 1 },
|
|
349
|
+
raw: { name: "RAW", limit: 2000 },
|
|
350
|
+
bigint: { name: "NUMBER", limit: 19 }
|
|
523
351
|
}
|
|
524
352
|
# if emulate_booleans_from_strings then store booleans in VARCHAR2
|
|
525
353
|
NATIVE_DATABASE_TYPES_BOOLEAN_STRINGS = NATIVE_DATABASE_TYPES.dup.merge(
|
|
526
|
-
:
|
|
354
|
+
boolean: { name: "VARCHAR2", limit: 1 }
|
|
527
355
|
)
|
|
528
356
|
#:startdoc:
|
|
529
357
|
|
|
@@ -573,7 +401,6 @@ module ActiveRecord
|
|
|
573
401
|
def in_clause_length
|
|
574
402
|
1000
|
|
575
403
|
end
|
|
576
|
-
alias ids_in_list_limit in_clause_length
|
|
577
404
|
|
|
578
405
|
# CONNECTION MANAGEMENT ====================================
|
|
579
406
|
#
|
|
@@ -625,7 +452,7 @@ module ActiveRecord
|
|
|
625
452
|
end
|
|
626
453
|
|
|
627
454
|
# use in set_sequence_name to avoid fetching primary key value from sequence
|
|
628
|
-
AUTOGENERATED_SEQUENCE_NAME =
|
|
455
|
+
AUTOGENERATED_SEQUENCE_NAME = "autogenerated".freeze
|
|
629
456
|
|
|
630
457
|
# Returns the next sequence value from a sequence generator. Not generally
|
|
631
458
|
# called directly; used by ActiveRecord to get the next primary key value
|
|
@@ -661,8 +488,8 @@ module ActiveRecord
|
|
|
661
488
|
|
|
662
489
|
def reset_pk_sequence!(table_name, primary_key = nil, sequence_name = nil) #:nodoc:
|
|
663
490
|
return nil unless data_source_exists?(table_name)
|
|
664
|
-
unless primary_key
|
|
665
|
-
|
|
491
|
+
unless primary_key && sequence_name
|
|
492
|
+
# *Note*: Only primary key is implemented - sequence will be nil.
|
|
666
493
|
primary_key, sequence_name = pk_and_sequence_for(table_name)
|
|
667
494
|
# TODO This sequence_name implemantation is just enough
|
|
668
495
|
# to satisty fixures. To get correct sequence_name always
|
|
@@ -690,24 +517,17 @@ module ActiveRecord
|
|
|
690
517
|
|
|
691
518
|
# Writes LOB values from attributes for specified columns
|
|
692
519
|
def write_lobs(table_name, klass, attributes, columns) #:nodoc:
|
|
693
|
-
|
|
694
|
-
is_with_cpk = klass.respond_to?(:composite?) && klass.composite?
|
|
695
|
-
if is_with_cpk
|
|
696
|
-
id = klass.primary_key.map {|pk| attributes[pk.to_s] }
|
|
697
|
-
else
|
|
698
|
-
id = quote(attributes[klass.primary_key])
|
|
699
|
-
end
|
|
520
|
+
id = quote(attributes[klass.primary_key])
|
|
700
521
|
columns.each do |col|
|
|
701
522
|
value = attributes[col.name]
|
|
702
523
|
# changed sequence of next two lines - should check if value is nil before converting to yaml
|
|
703
|
-
next if value.
|
|
524
|
+
next if value.blank?
|
|
704
525
|
if klass.attribute_types[col.name].is_a? ActiveRecord::Type::Serialized
|
|
705
526
|
value = klass.attribute_types[col.name].serialize(value)
|
|
706
527
|
end
|
|
707
528
|
uncached do
|
|
708
|
-
sql =
|
|
709
|
-
|
|
710
|
-
unless lob_record = select_one(sql, 'Writable Large Object')
|
|
529
|
+
sql = "SELECT #{quote_column_name(col.name)} FROM #{quote_table_name(table_name)} WHERE #{quote_column_name(klass.primary_key)} = #{id} FOR UPDATE"
|
|
530
|
+
unless lob_record = select_one(sql, "Writable Large Object")
|
|
711
531
|
raise ActiveRecord::RecordNotFound, "statement #{sql} returned no rows"
|
|
712
532
|
end
|
|
713
533
|
lob = lob_record[col.name]
|
|
@@ -718,6 +538,8 @@ module ActiveRecord
|
|
|
718
538
|
|
|
719
539
|
# Current database name
|
|
720
540
|
def current_database
|
|
541
|
+
select_value("SELECT SYS_CONTEXT('userenv', 'con_name') FROM dual")
|
|
542
|
+
rescue ActiveRecord::StatementInvalid
|
|
721
543
|
select_value("SELECT SYS_CONTEXT('userenv', 'db_name') FROM dual")
|
|
722
544
|
end
|
|
723
545
|
|
|
@@ -736,40 +558,40 @@ module ActiveRecord
|
|
|
736
558
|
select_value("SELECT LOWER(default_tablespace) FROM user_users WHERE username = SYS_CONTEXT('userenv', 'current_schema')")
|
|
737
559
|
end
|
|
738
560
|
|
|
739
|
-
def tables
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
MSG
|
|
745
|
-
|
|
746
|
-
if name
|
|
747
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
|
748
|
-
Passing arguments to #tables is deprecated without replacement.
|
|
749
|
-
MSG
|
|
750
|
-
end
|
|
751
|
-
|
|
752
|
-
data_sources
|
|
561
|
+
def tables #:nodoc:
|
|
562
|
+
select_values(<<-SQL, "SCHEMA")
|
|
563
|
+
SELECT DECODE(table_name, UPPER(table_name), LOWER(table_name), table_name)
|
|
564
|
+
FROM all_tables WHERE owner = SYS_CONTEXT('userenv', 'current_schema') AND secondary = 'N'
|
|
565
|
+
SQL
|
|
753
566
|
end
|
|
754
567
|
|
|
755
|
-
|
|
756
568
|
def data_sources
|
|
757
|
-
|
|
758
|
-
"SELECT DECODE(table_name, UPPER(table_name), LOWER(table_name), table_name) FROM all_tables WHERE owner = SYS_CONTEXT('userenv', 'current_schema') AND secondary = 'N'",
|
|
759
|
-
'SCHEMA')
|
|
569
|
+
super
|
|
760
570
|
end
|
|
761
571
|
|
|
762
572
|
def table_exists?(table_name)
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
573
|
+
table_name = table_name.to_s
|
|
574
|
+
if table_name.include?("@")
|
|
575
|
+
# db link is not table
|
|
576
|
+
false
|
|
577
|
+
else
|
|
578
|
+
default_owner = current_schema
|
|
579
|
+
end
|
|
580
|
+
real_name = ActiveRecord::ConnectionAdapters::OracleEnhanced::Quoting.valid_table_name?(table_name) ?
|
|
581
|
+
table_name.upcase : table_name
|
|
582
|
+
if real_name.include?(".")
|
|
583
|
+
table_owner, table_name = real_name.split(".")
|
|
584
|
+
else
|
|
585
|
+
table_owner, table_name = default_owner, real_name
|
|
586
|
+
end
|
|
587
|
+
select_values(<<-SQL, "SCHEMA").any?
|
|
588
|
+
SELECT owner, table_name
|
|
589
|
+
FROM all_tables
|
|
590
|
+
WHERE owner = '#{table_owner}'
|
|
591
|
+
AND table_name = q'[#{table_name}]'
|
|
592
|
+
SQL
|
|
770
593
|
end
|
|
771
594
|
|
|
772
|
-
# Will return true if database object exists (to be able to use also views and synonyms for ActiveRecord models)
|
|
773
595
|
# Needs to consider how to support synonyms in Rails 5.1
|
|
774
596
|
def data_source_exists?(table_name)
|
|
775
597
|
(_owner, table_name, _db_link) = @connection.describe(table_name)
|
|
@@ -779,7 +601,7 @@ module ActiveRecord
|
|
|
779
601
|
end
|
|
780
602
|
|
|
781
603
|
def views # :nodoc:
|
|
782
|
-
select_values("SELECT LOWER(view_name) FROM all_views WHERE owner = SYS_CONTEXT('userenv', '
|
|
604
|
+
select_values("SELECT LOWER(view_name) FROM all_views WHERE owner = SYS_CONTEXT('userenv', 'current_schema')")
|
|
783
605
|
end
|
|
784
606
|
|
|
785
607
|
def materialized_views #:nodoc:
|
|
@@ -794,7 +616,7 @@ module ActiveRecord
|
|
|
794
616
|
(owner, table_name, db_link) = @connection.describe(table_name)
|
|
795
617
|
unless all_schema_indexes
|
|
796
618
|
default_tablespace_name = default_tablespace
|
|
797
|
-
result = select_all(<<-SQL.strip.gsub(/\s+/,
|
|
619
|
+
result = select_all(<<-SQL.strip.gsub(/\s+/, " "))
|
|
798
620
|
SELECT LOWER(i.table_name) AS table_name, LOWER(i.index_name) AS index_name, i.uniqueness,
|
|
799
621
|
i.index_type, i.ityp_owner, i.ityp_name, i.parameters,
|
|
800
622
|
LOWER(i.tablespace_name) AS tablespace_name,
|
|
@@ -819,10 +641,10 @@ module ActiveRecord
|
|
|
819
641
|
result.each do |row|
|
|
820
642
|
# have to keep track of indexes because above query returns dups
|
|
821
643
|
# there is probably a better query we could figure out
|
|
822
|
-
if current_index != row[
|
|
644
|
+
if current_index != row["index_name"]
|
|
823
645
|
statement_parameters = nil
|
|
824
|
-
if row[
|
|
825
|
-
procedure_name = default_datastore_procedure(row[
|
|
646
|
+
if row["index_type"] == "DOMAIN" && row["ityp_owner"] == "CTXSYS" && row["ityp_name"] == "CONTEXT"
|
|
647
|
+
procedure_name = default_datastore_procedure(row["index_name"])
|
|
826
648
|
source = select_values(<<-SQL).join
|
|
827
649
|
SELECT text
|
|
828
650
|
FROM all_source#{db_link}
|
|
@@ -835,82 +657,36 @@ module ActiveRecord
|
|
|
835
657
|
end
|
|
836
658
|
end
|
|
837
659
|
all_schema_indexes << OracleEnhanced::IndexDefinition.new(
|
|
838
|
-
row[
|
|
839
|
-
row[
|
|
840
|
-
row[
|
|
660
|
+
row["table_name"],
|
|
661
|
+
row["index_name"],
|
|
662
|
+
row["uniqueness"] == "UNIQUE",
|
|
841
663
|
[],
|
|
842
664
|
nil,
|
|
843
665
|
nil,
|
|
844
666
|
nil,
|
|
845
|
-
row[
|
|
667
|
+
row["index_type"] == "DOMAIN" ? "#{row['ityp_owner']}.#{row['ityp_name']}" : nil,
|
|
846
668
|
nil,
|
|
847
|
-
row[
|
|
669
|
+
row["parameters"],
|
|
848
670
|
statement_parameters,
|
|
849
|
-
row[
|
|
850
|
-
current_index = row[
|
|
671
|
+
row["tablespace_name"] == default_tablespace_name ? nil : row["tablespace_name"])
|
|
672
|
+
current_index = row["index_name"]
|
|
851
673
|
end
|
|
852
674
|
|
|
853
675
|
# Functional index columns and virtual columns both get stored as column expressions,
|
|
854
676
|
# but re-creating a virtual column index as an expression (instead of using the virtual column's name)
|
|
855
677
|
# results in a ORA-54018 error. Thus, we only want the column expression value returned
|
|
856
678
|
# when the column is not virtual.
|
|
857
|
-
if row[
|
|
858
|
-
all_schema_indexes.last.columns << row[
|
|
679
|
+
if row["column_expression"] && row["virtual_column"] != "YES"
|
|
680
|
+
all_schema_indexes.last.columns << row["column_expression"]
|
|
859
681
|
else
|
|
860
|
-
all_schema_indexes.last.columns << row[
|
|
682
|
+
all_schema_indexes.last.columns << row["column_name"].downcase
|
|
861
683
|
end
|
|
862
684
|
end
|
|
863
685
|
end
|
|
864
686
|
|
|
865
687
|
# Return the indexes just for the requested table, since AR is structured that way
|
|
866
688
|
table_name = table_name.downcase
|
|
867
|
-
all_schema_indexes.select{|i| i.table == table_name}
|
|
868
|
-
end
|
|
869
|
-
|
|
870
|
-
@@ignore_table_columns = nil #:nodoc:
|
|
871
|
-
|
|
872
|
-
# set ignored columns for table
|
|
873
|
-
def ignore_table_columns(table_name, *args) #:nodoc:
|
|
874
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
|
875
|
-
`ignore_table_columns` will be deprecated in next version of Oracle enhanced adapter
|
|
876
|
-
since Rails 5 introduces `ignored_columns`. Use `ignored_columns` instead of `ignore_table_columns`.
|
|
877
|
-
MSG
|
|
878
|
-
@@ignore_table_columns ||= {}
|
|
879
|
-
@@ignore_table_columns[table_name] ||= []
|
|
880
|
-
@@ignore_table_columns[table_name] += args.map{|a| a.to_s.downcase}
|
|
881
|
-
@@ignore_table_columns[table_name].uniq!
|
|
882
|
-
end
|
|
883
|
-
|
|
884
|
-
def ignored_table_columns(table_name) #:nodoc:
|
|
885
|
-
@@ignore_table_columns ||= {}
|
|
886
|
-
@@ignore_table_columns[table_name]
|
|
887
|
-
end
|
|
888
|
-
|
|
889
|
-
# used just in tests to clear ignored table columns
|
|
890
|
-
def clear_ignored_table_columns #:nodoc:
|
|
891
|
-
@@ignore_table_columns = nil
|
|
892
|
-
end
|
|
893
|
-
|
|
894
|
-
@@table_column_type = nil #:nodoc:
|
|
895
|
-
|
|
896
|
-
# set explicit type for specified table columns
|
|
897
|
-
def set_type_for_columns(table_name, column_type, *args) #:nodoc:
|
|
898
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
|
899
|
-
`set_type_for_columns` has been deprecated. Please use Rails attribute API.
|
|
900
|
-
MSG
|
|
901
|
-
end
|
|
902
|
-
|
|
903
|
-
def get_type_for_column(table_name, column_name) #:nodoc:
|
|
904
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
|
905
|
-
`get_type_for_columns` has been deprecated. Please use Rails attribute API.
|
|
906
|
-
MSG
|
|
907
|
-
end
|
|
908
|
-
|
|
909
|
-
# used just in tests to clear column data type definitions
|
|
910
|
-
def clear_types_for_columns #:nodoc:
|
|
911
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
|
912
|
-
`clear_types_for_columns` has been deprecated. Please use Rails attribute API.
|
|
913
|
-
MSG
|
|
689
|
+
all_schema_indexes.select { |i| i.table == table_name }
|
|
914
690
|
end
|
|
915
691
|
|
|
916
692
|
# check if table has primary key trigger with _pkt suffix
|
|
@@ -922,12 +698,12 @@ module ActiveRecord
|
|
|
922
698
|
SELECT trigger_name
|
|
923
699
|
FROM all_triggers#{db_link}
|
|
924
700
|
WHERE owner = '#{owner}'
|
|
925
|
-
AND trigger_name = '#{trigger_name}'
|
|
701
|
+
AND trigger_name = q'[#{trigger_name}]'
|
|
926
702
|
AND table_owner = '#{owner}'
|
|
927
|
-
AND table_name = '#{desc_table_name}'
|
|
703
|
+
AND table_name = q'[#{desc_table_name}]'
|
|
928
704
|
AND status = 'ENABLED'
|
|
929
705
|
SQL
|
|
930
|
-
select_value(pkt_sql,
|
|
706
|
+
select_value(pkt_sql, "Primary Key Trigger") ? true : false
|
|
931
707
|
end
|
|
932
708
|
|
|
933
709
|
##
|
|
@@ -951,17 +727,15 @@ module ActiveRecord
|
|
|
951
727
|
|
|
952
728
|
def columns_without_cache(table_name, name = nil) #:nodoc:
|
|
953
729
|
table_name = table_name.to_s
|
|
954
|
-
# get ignored_columns by original table name
|
|
955
|
-
ignored_columns = ignored_table_columns(table_name)
|
|
956
730
|
|
|
957
731
|
(owner, desc_table_name, db_link) = @connection.describe(table_name)
|
|
958
732
|
|
|
959
733
|
# reset do_not_prefetch_primary_key cache for this table
|
|
960
734
|
@@do_not_prefetch_primary_key[table_name] = nil
|
|
961
735
|
|
|
962
|
-
table_cols = <<-SQL.strip.gsub(/\s+/,
|
|
963
|
-
SELECT cols.column_name AS name, cols.data_type AS sql_type,
|
|
964
|
-
cols.data_default, cols.nullable, cols.virtual_column, cols.hidden_column,
|
|
736
|
+
table_cols = <<-SQL.strip.gsub(/\s+/, " ")
|
|
737
|
+
SELECT cols.column_name AS name, cols.data_type AS sql_type,
|
|
738
|
+
cols.data_default, cols.nullable, cols.virtual_column, cols.hidden_column,
|
|
965
739
|
cols.data_type_owner AS sql_type_owner,
|
|
966
740
|
DECODE(cols.data_type, 'NUMBER', data_precision,
|
|
967
741
|
'FLOAT', data_precision,
|
|
@@ -973,7 +747,7 @@ module ActiveRecord
|
|
|
973
747
|
comments.comments as column_comment
|
|
974
748
|
FROM all_tab_cols#{db_link} cols, all_col_comments#{db_link} comments
|
|
975
749
|
WHERE cols.owner = '#{owner}'
|
|
976
|
-
AND cols.table_name =
|
|
750
|
+
AND cols.table_name = #{quote(desc_table_name)}
|
|
977
751
|
AND cols.hidden_column = 'NO'
|
|
978
752
|
AND cols.owner = comments.owner
|
|
979
753
|
AND cols.table_name = comments.table_name
|
|
@@ -982,46 +756,44 @@ module ActiveRecord
|
|
|
982
756
|
SQL
|
|
983
757
|
|
|
984
758
|
# added deletion of ignored columns
|
|
985
|
-
select_all(table_cols, name).to_a.
|
|
986
|
-
|
|
987
|
-
end.map do |row|
|
|
988
|
-
limit, scale = row['limit'], row['scale']
|
|
759
|
+
select_all(table_cols, name).to_a.map do |row|
|
|
760
|
+
limit, scale = row["limit"], row["scale"]
|
|
989
761
|
if limit || scale
|
|
990
|
-
row[
|
|
762
|
+
row["sql_type"] += "(#{(limit || 38).to_i}" + ((scale = scale.to_i) > 0 ? ",#{scale})" : ")")
|
|
991
763
|
end
|
|
992
764
|
|
|
993
|
-
if row[
|
|
994
|
-
row[
|
|
765
|
+
if row["sql_type_owner"]
|
|
766
|
+
row["sql_type"] = row["sql_type_owner"] + "." + row["sql_type"]
|
|
995
767
|
end
|
|
996
768
|
|
|
997
|
-
is_virtual = row[
|
|
769
|
+
is_virtual = row["virtual_column"] == "YES"
|
|
998
770
|
|
|
999
771
|
# clean up odd default spacing from Oracle
|
|
1000
|
-
if row[
|
|
1001
|
-
row[
|
|
772
|
+
if row["data_default"] && !is_virtual
|
|
773
|
+
row["data_default"].sub!(/^(.*?)\s*$/, '\1')
|
|
1002
774
|
|
|
1003
775
|
# If a default contains a newline these cleanup regexes need to
|
|
1004
776
|
# match newlines.
|
|
1005
|
-
row[
|
|
1006
|
-
row[
|
|
777
|
+
row["data_default"].sub!(/^'(.*)'$/m, '\1')
|
|
778
|
+
row["data_default"] = nil if row["data_default"] =~ /^(null|empty_[bc]lob\(\))$/i
|
|
1007
779
|
# TODO: Needs better fix to fallback "N" to false
|
|
1008
|
-
row[
|
|
780
|
+
row["data_default"] = false if (row["data_default"] == "N" && OracleEnhancedAdapter.emulate_booleans_from_strings)
|
|
1009
781
|
end
|
|
1010
782
|
|
|
1011
|
-
type_metadata = fetch_type_metadata(row[
|
|
1012
|
-
new_column(oracle_downcase(row[
|
|
1013
|
-
row[
|
|
783
|
+
type_metadata = fetch_type_metadata(row["sql_type"])
|
|
784
|
+
new_column(oracle_downcase(row["name"]),
|
|
785
|
+
row["data_default"],
|
|
1014
786
|
type_metadata,
|
|
1015
|
-
row[
|
|
787
|
+
row["nullable"] == "Y",
|
|
1016
788
|
table_name,
|
|
1017
789
|
is_virtual,
|
|
1018
790
|
false,
|
|
1019
|
-
row[
|
|
791
|
+
row["column_comment"]
|
|
1020
792
|
)
|
|
1021
793
|
end
|
|
1022
794
|
end
|
|
1023
795
|
|
|
1024
|
-
def new_column(name, default, sql_type_metadata = nil, null = true, table_name = nil, virtual = false, returning_id = false,comment = nil) # :nodoc:
|
|
796
|
+
def new_column(name, default, sql_type_metadata = nil, null = true, table_name = nil, virtual = false, returning_id = false, comment = nil) # :nodoc:
|
|
1025
797
|
OracleEnhancedColumn.new(name, default, sql_type_metadata, null, table_name, virtual, returning_id, comment)
|
|
1026
798
|
end
|
|
1027
799
|
|
|
@@ -1049,7 +821,7 @@ module ActiveRecord
|
|
|
1049
821
|
|
|
1050
822
|
# Find a table's primary key and sequence.
|
|
1051
823
|
# *Note*: Only primary key is implemented - sequence will be nil.
|
|
1052
|
-
def pk_and_sequence_for(table_name, owner=nil, desc_table_name=nil, db_link=nil) #:nodoc:
|
|
824
|
+
def pk_and_sequence_for(table_name, owner = nil, desc_table_name = nil, db_link = nil) #:nodoc:
|
|
1053
825
|
if @@cache_columns
|
|
1054
826
|
@@pk_and_sequence_for_cache ||= {}
|
|
1055
827
|
if @@pk_and_sequence_for_cache.key?(table_name)
|
|
@@ -1062,22 +834,22 @@ module ActiveRecord
|
|
|
1062
834
|
end
|
|
1063
835
|
end
|
|
1064
836
|
|
|
1065
|
-
def pk_and_sequence_for_without_cache(table_name, owner=nil, desc_table_name=nil, db_link=nil) #:nodoc:
|
|
837
|
+
def pk_and_sequence_for_without_cache(table_name, owner = nil, desc_table_name = nil, db_link = nil) #:nodoc:
|
|
1066
838
|
(owner, desc_table_name, db_link) = @connection.describe(table_name) unless owner
|
|
1067
839
|
|
|
1068
|
-
seqs = select_values(<<-SQL.strip.gsub(/\s+/,
|
|
840
|
+
seqs = select_values(<<-SQL.strip.gsub(/\s+/, " "), "Sequence")
|
|
1069
841
|
select us.sequence_name
|
|
1070
842
|
from all_sequences#{db_link} us
|
|
1071
843
|
where us.sequence_owner = '#{owner}'
|
|
1072
|
-
and us.sequence_name =
|
|
844
|
+
and us.sequence_name = upper(#{quote(default_sequence_name(desc_table_name))})
|
|
1073
845
|
SQL
|
|
1074
846
|
|
|
1075
847
|
# changed back from user_constraints to all_constraints for consistency
|
|
1076
|
-
pks = select_values(<<-SQL.strip.gsub(/\s+/,
|
|
848
|
+
pks = select_values(<<-SQL.strip.gsub(/\s+/, " "), "Primary Key")
|
|
1077
849
|
SELECT cc.column_name
|
|
1078
850
|
FROM all_constraints#{db_link} c, all_cons_columns#{db_link} cc
|
|
1079
851
|
WHERE c.owner = '#{owner}'
|
|
1080
|
-
AND c.table_name =
|
|
852
|
+
AND c.table_name = #{quote(desc_table_name)}
|
|
1081
853
|
AND c.constraint_type = 'P'
|
|
1082
854
|
AND cc.owner = c.owner
|
|
1083
855
|
AND cc.constraint_name = c.constraint_name
|
|
@@ -1100,14 +872,14 @@ module ActiveRecord
|
|
|
1100
872
|
pk_and_sequence && pk_and_sequence.first
|
|
1101
873
|
end
|
|
1102
874
|
|
|
1103
|
-
def has_primary_key?(table_name, owner=nil, desc_table_name=nil, db_link=nil) #:nodoc:
|
|
875
|
+
def has_primary_key?(table_name, owner = nil, desc_table_name = nil, db_link = nil) #:nodoc:
|
|
1104
876
|
!pk_and_sequence_for(table_name, owner, desc_table_name, db_link).nil?
|
|
1105
877
|
end
|
|
1106
878
|
|
|
1107
879
|
def primary_keys(table_name) # :nodoc:
|
|
1108
880
|
(owner, desc_table_name, db_link) = @connection.describe(table_name) unless owner
|
|
1109
881
|
|
|
1110
|
-
pks = select_values(<<-SQL.strip_heredoc,
|
|
882
|
+
pks = select_values(<<-SQL.strip_heredoc, "Primary Keys")
|
|
1111
883
|
SELECT cc.column_name
|
|
1112
884
|
FROM all_constraints#{db_link} c, all_cons_columns#{db_link} cc
|
|
1113
885
|
WHERE c.owner = '#{owner}'
|
|
@@ -1117,7 +889,7 @@ module ActiveRecord
|
|
|
1117
889
|
AND cc.constraint_name = c.constraint_name
|
|
1118
890
|
order by cc.position
|
|
1119
891
|
SQL
|
|
1120
|
-
pks.map {|pk| oracle_downcase(pk)}
|
|
892
|
+
pks.map { |pk| oracle_downcase(pk) }
|
|
1121
893
|
end
|
|
1122
894
|
|
|
1123
895
|
def columns_for_distinct(columns, orders) #:nodoc:
|
|
@@ -1126,30 +898,18 @@ module ActiveRecord
|
|
|
1126
898
|
# the inclusion of these columns doesn't invalidate the DISTINCT
|
|
1127
899
|
#
|
|
1128
900
|
# It does not construct DISTINCT clause. Just return column names for distinct.
|
|
1129
|
-
order_columns = orders.reject(&:blank?).map{ |s|
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
}.reject(&:blank?).map.with_index { |column,i|
|
|
901
|
+
order_columns = orders.reject(&:blank?).map { |s|
|
|
902
|
+
s = s.to_sql unless s.is_a?(String)
|
|
903
|
+
# remove any ASC/DESC modifiers
|
|
904
|
+
s.gsub(/\s+(ASC|DESC)\s*?/i, "")
|
|
905
|
+
}.reject(&:blank?).map.with_index { |column, i|
|
|
1134
906
|
"FIRST_VALUE(#{column}) OVER (PARTITION BY #{columns} ORDER BY #{column}) AS alias_#{i}__"
|
|
1135
907
|
}
|
|
1136
|
-
|
|
908
|
+
[super, *order_columns].join(", ")
|
|
1137
909
|
end
|
|
1138
910
|
|
|
1139
911
|
def temporary_table?(table_name) #:nodoc:
|
|
1140
|
-
select_value("SELECT temporary FROM all_tables WHERE table_name = '#{table_name.upcase}' and owner = SYS_CONTEXT('userenv', 'session_user')") ==
|
|
1141
|
-
end
|
|
1142
|
-
|
|
1143
|
-
# construct additional wrapper subquery if select.offset is used to avoid generation of invalid subquery
|
|
1144
|
-
# ... IN ( SELECT * FROM ( SELECT raw_sql_.*, rownum raw_rnum_ FROM ( ... ) raw_sql_ ) WHERE raw_rnum_ > ... )
|
|
1145
|
-
def join_to_update(update, select, key) #:nodoc:
|
|
1146
|
-
#TODO: Need to validate if we can remove join_to_update from Oracle enhanced adapter after testing
|
|
1147
|
-
# older version of Oracle 11gR2
|
|
1148
|
-
super
|
|
1149
|
-
end
|
|
1150
|
-
|
|
1151
|
-
def valid_type?(type)
|
|
1152
|
-
!native_database_types[type].nil?
|
|
912
|
+
select_value("SELECT temporary FROM all_tables WHERE table_name = '#{table_name.upcase}' and owner = SYS_CONTEXT('userenv', 'session_user')") == "Y"
|
|
1153
913
|
end
|
|
1154
914
|
|
|
1155
915
|
def combine_bind_parameters(
|
|
@@ -1161,78 +921,89 @@ module ActiveRecord
|
|
|
1161
921
|
offset: nil
|
|
1162
922
|
) # :nodoc:
|
|
1163
923
|
result = from_clause + join_clause + where_clause + having_clause
|
|
1164
|
-
if offset
|
|
924
|
+
if RUBY_ENGINE == "jruby" && !supports_fetch_first_n_rows_and_offset? && offset && limit
|
|
1165
925
|
result << offset
|
|
1166
|
-
end
|
|
1167
|
-
if limit
|
|
1168
926
|
result << limit
|
|
927
|
+
result << offset
|
|
928
|
+
else
|
|
929
|
+
if offset
|
|
930
|
+
result << offset
|
|
931
|
+
end
|
|
932
|
+
if limit
|
|
933
|
+
result << limit
|
|
934
|
+
end
|
|
1169
935
|
end
|
|
1170
936
|
result
|
|
1171
937
|
end
|
|
1172
938
|
|
|
1173
939
|
protected
|
|
1174
940
|
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
941
|
+
def initialize_type_map(m)
|
|
942
|
+
super
|
|
943
|
+
# oracle
|
|
944
|
+
register_class_with_precision m, %r(ZONE)i, ActiveRecord::OracleEnhanced::Type::TimestampTz
|
|
945
|
+
register_class_with_limit m, %r(raw)i, ActiveRecord::OracleEnhanced::Type::Raw
|
|
946
|
+
register_class_with_limit m, %r(char)i, ActiveRecord::OracleEnhanced::Type::String
|
|
947
|
+
register_class_with_limit m, %r(clob)i, ActiveRecord::OracleEnhanced::Type::Text
|
|
948
|
+
|
|
949
|
+
m.register_type "NCHAR", ActiveRecord::OracleEnhanced::Type::NationalCharacterString.new
|
|
950
|
+
m.alias_type %r(NVARCHAR2)i, "NCHAR"
|
|
951
|
+
|
|
952
|
+
m.register_type(%r(NUMBER)i) do |sql_type|
|
|
953
|
+
scale = extract_scale(sql_type)
|
|
954
|
+
precision = extract_precision(sql_type)
|
|
955
|
+
limit = extract_limit(sql_type)
|
|
956
|
+
if scale == 0
|
|
957
|
+
ActiveRecord::OracleEnhanced::Type::Integer.new(precision: precision, limit: limit)
|
|
958
|
+
else
|
|
959
|
+
Type::Decimal.new(precision: precision, scale: scale)
|
|
960
|
+
end
|
|
1193
961
|
end
|
|
1194
|
-
end
|
|
1195
962
|
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
963
|
+
if OracleEnhancedAdapter.emulate_booleans
|
|
964
|
+
if OracleEnhancedAdapter.emulate_booleans_from_strings
|
|
965
|
+
m.register_type %r(^VARCHAR2\(1\))i, ActiveRecord::OracleEnhanced::Type::Boolean.new
|
|
966
|
+
else
|
|
967
|
+
m.register_type %r(^NUMBER\(1\))i, Type::Boolean.new
|
|
968
|
+
end
|
|
1201
969
|
end
|
|
1202
970
|
end
|
|
1203
|
-
end
|
|
1204
971
|
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
972
|
+
def extract_limit(sql_type) #:nodoc:
|
|
973
|
+
case sql_type
|
|
974
|
+
when /^bigint/i
|
|
975
|
+
19
|
|
976
|
+
when /\((.*)\)/
|
|
977
|
+
$1.to_i
|
|
978
|
+
end
|
|
1211
979
|
end
|
|
1212
|
-
end
|
|
1213
980
|
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
981
|
+
def translate_exception(exception, message) #:nodoc:
|
|
982
|
+
case @connection.error_code(exception)
|
|
983
|
+
when 1
|
|
984
|
+
RecordNotUnique.new(message)
|
|
985
|
+
when 942, 955, 1418
|
|
986
|
+
ActiveRecord::StatementInvalid.new(message)
|
|
987
|
+
when 1400
|
|
988
|
+
ActiveRecord::NotNullViolation.new(message)
|
|
989
|
+
when 2291
|
|
990
|
+
InvalidForeignKey.new(message)
|
|
991
|
+
when 12899
|
|
992
|
+
ValueTooLong.new(message)
|
|
993
|
+
else
|
|
994
|
+
super
|
|
995
|
+
end
|
|
1224
996
|
end
|
|
1225
|
-
end
|
|
1226
997
|
|
|
1227
998
|
private
|
|
1228
999
|
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1000
|
+
def oracle_downcase(column_name)
|
|
1001
|
+
@connection.oracle_downcase(column_name)
|
|
1002
|
+
end
|
|
1232
1003
|
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1004
|
+
def compress_lines(string, join_with = "\n")
|
|
1005
|
+
string.split($/).map { |line| line.strip }.join(join_with)
|
|
1006
|
+
end
|
|
1236
1007
|
|
|
1237
1008
|
public
|
|
1238
1009
|
# DBMS_OUTPUT =============================================
|
|
@@ -1262,82 +1033,88 @@ module ActiveRecord
|
|
|
1262
1033
|
end
|
|
1263
1034
|
|
|
1264
1035
|
protected
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1036
|
+
def log(sql, name = "SQL", binds = [], type_casted_binds = [], statement_name = nil)
|
|
1037
|
+
super
|
|
1038
|
+
ensure
|
|
1039
|
+
log_dbms_output if dbms_output_enabled?
|
|
1040
|
+
end
|
|
1270
1041
|
|
|
1271
1042
|
private
|
|
1272
1043
|
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1044
|
+
def set_dbms_output_plsql_connection
|
|
1045
|
+
raise OracleEnhancedConnectionException, "ruby-plsql gem is required for logging DBMS output" unless self.respond_to?(:plsql)
|
|
1046
|
+
# do not reset plsql connection if it is the same (as resetting will clear PL/SQL metadata cache)
|
|
1047
|
+
unless plsql(:dbms_output).connection && plsql(:dbms_output).connection.raw_connection == raw_connection
|
|
1048
|
+
plsql(:dbms_output).connection = raw_connection
|
|
1049
|
+
end
|
|
1278
1050
|
end
|
|
1279
|
-
end
|
|
1280
1051
|
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1052
|
+
def log_dbms_output
|
|
1053
|
+
while true do
|
|
1054
|
+
result = plsql(:dbms_output).sys.dbms_output.get_line(line: "", status: 0)
|
|
1055
|
+
break unless result[:status] == 0
|
|
1056
|
+
@logger.debug "DBMS_OUTPUT: #{result[:line]}" if @logger
|
|
1057
|
+
end
|
|
1286
1058
|
end
|
|
1287
|
-
end
|
|
1288
|
-
|
|
1289
1059
|
end
|
|
1290
1060
|
end
|
|
1291
1061
|
end
|
|
1292
1062
|
|
|
1293
1063
|
# Implementation of standard schema definition statements and extensions for schema definition
|
|
1294
|
-
require
|
|
1295
|
-
require
|
|
1064
|
+
require "active_record/connection_adapters/oracle_enhanced/schema_statements"
|
|
1065
|
+
require "active_record/connection_adapters/oracle_enhanced/schema_statements_ext"
|
|
1296
1066
|
|
|
1297
1067
|
# Extensions for schema definition
|
|
1298
|
-
require
|
|
1068
|
+
require "active_record/connection_adapters/oracle_enhanced/schema_definitions"
|
|
1299
1069
|
|
|
1300
1070
|
# Extensions for context index definition
|
|
1301
|
-
require
|
|
1302
|
-
|
|
1303
|
-
# Load additional methods for composite_primary_keys support
|
|
1304
|
-
require 'active_record/connection_adapters/oracle_enhanced/cpk'
|
|
1071
|
+
require "active_record/connection_adapters/oracle_enhanced/context_index"
|
|
1305
1072
|
|
|
1306
1073
|
# Patches and enhancements for schema dumper
|
|
1307
|
-
require
|
|
1074
|
+
require "active_record/connection_adapters/oracle_enhanced/schema_dumper"
|
|
1308
1075
|
|
|
1309
1076
|
# Implementation of structure dump
|
|
1310
|
-
require
|
|
1077
|
+
require "active_record/connection_adapters/oracle_enhanced/structure_dump"
|
|
1311
1078
|
|
|
1312
|
-
require
|
|
1079
|
+
require "active_record/connection_adapters/oracle_enhanced/version"
|
|
1313
1080
|
|
|
1314
1081
|
module ActiveRecord
|
|
1315
|
-
autoload :OracleEnhancedProcedures,
|
|
1082
|
+
autoload :OracleEnhancedProcedures, "active_record/connection_adapters/oracle_enhanced/procedures"
|
|
1316
1083
|
end
|
|
1317
1084
|
|
|
1318
1085
|
# Patches and enhancements for column dumper
|
|
1319
|
-
require
|
|
1086
|
+
require "active_record/connection_adapters/oracle_enhanced/column_dumper"
|
|
1320
1087
|
|
|
1321
1088
|
# Moved SchemaCreation class
|
|
1322
|
-
require
|
|
1089
|
+
require "active_record/connection_adapters/oracle_enhanced/schema_creation"
|
|
1323
1090
|
|
|
1324
1091
|
# Moved DatabaseStetements
|
|
1325
|
-
require
|
|
1092
|
+
require "active_record/connection_adapters/oracle_enhanced/database_statements"
|
|
1326
1093
|
|
|
1327
1094
|
# Add Type:Raw
|
|
1328
|
-
require
|
|
1095
|
+
require "active_record/oracle_enhanced/type/raw"
|
|
1329
1096
|
|
|
1330
1097
|
# Add OracleEnhanced::Type::Integer
|
|
1331
|
-
require
|
|
1098
|
+
require "active_record/oracle_enhanced/type/integer"
|
|
1332
1099
|
|
|
1333
1100
|
# Add OracleEnhanced::Type::String
|
|
1334
|
-
require
|
|
1101
|
+
require "active_record/oracle_enhanced/type/string"
|
|
1335
1102
|
|
|
1336
1103
|
# Add OracleEnhanced::Type::NationalCharacterString
|
|
1337
|
-
require
|
|
1104
|
+
require "active_record/oracle_enhanced/type/national_character_string"
|
|
1338
1105
|
|
|
1339
1106
|
# Add OracleEnhanced::Type::Text
|
|
1340
|
-
require
|
|
1107
|
+
require "active_record/oracle_enhanced/type/text"
|
|
1341
1108
|
|
|
1342
1109
|
# Add OracleEnhanced::Type::Boolean
|
|
1343
|
-
require
|
|
1110
|
+
require "active_record/oracle_enhanced/type/boolean"
|
|
1111
|
+
|
|
1112
|
+
# To use :boolean type for Attribute API, each type needs registered explicitly.
|
|
1113
|
+
ActiveRecord::Type.register(:boolean, ActiveRecord::OracleEnhanced::Type::Boolean, adapter: :oracleenhanced)
|
|
1114
|
+
|
|
1115
|
+
# Add JSON attribute support
|
|
1116
|
+
require "active_record/oracle_enhanced/type/json"
|
|
1117
|
+
ActiveRecord::Type.register(:json, ActiveRecord::OracleEnhanced::Type::Json, adapter: :oracleenhanced)
|
|
1118
|
+
|
|
1119
|
+
# Add Type:TimestampTz
|
|
1120
|
+
require "active_record/oracle_enhanced/type/timestamptz"
|