activerecord-oracle_enhanced-adapter 5.2.8 → 7.0.3
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 +390 -21
- data/README.md +35 -8
- 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 +3 -3
- data/lib/active_record/connection_adapters/oracle_enhanced/connection.rb +42 -37
- data/lib/active_record/connection_adapters/oracle_enhanced/context_index.rb +59 -60
- data/lib/active_record/connection_adapters/oracle_enhanced/database_limits.rb +5 -10
- data/lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb +86 -81
- data/lib/active_record/connection_adapters/oracle_enhanced/database_tasks.rb +9 -10
- data/lib/active_record/connection_adapters/oracle_enhanced/dbms_output.rb +1 -2
- data/lib/active_record/connection_adapters/oracle_enhanced/jdbc_connection.rb +37 -16
- data/lib/active_record/connection_adapters/oracle_enhanced/jdbc_quoting.rb +1 -1
- data/lib/active_record/connection_adapters/oracle_enhanced/lob.rb +5 -6
- data/lib/active_record/connection_adapters/oracle_enhanced/oci_connection.rb +58 -49
- data/lib/active_record/connection_adapters/oracle_enhanced/oci_quoting.rb +1 -1
- data/lib/active_record/connection_adapters/oracle_enhanced/procedures.rb +6 -7
- data/lib/active_record/connection_adapters/oracle_enhanced/quoting.rb +75 -51
- data/lib/active_record/connection_adapters/oracle_enhanced/schema_creation.rb +13 -14
- data/lib/active_record/connection_adapters/oracle_enhanced/schema_definitions.rb +14 -4
- data/lib/active_record/connection_adapters/oracle_enhanced/schema_dumper.rb +27 -24
- data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb +156 -155
- data/lib/active_record/connection_adapters/oracle_enhanced/structure_dump.rb +103 -90
- data/lib/active_record/connection_adapters/oracle_enhanced/type_metadata.rb +3 -2
- data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +261 -161
- data/lib/active_record/type/oracle_enhanced/boolean.rb +0 -1
- data/lib/active_record/type/oracle_enhanced/character_string.rb +36 -0
- data/lib/active_record/type/oracle_enhanced/integer.rb +0 -1
- data/lib/arel/visitors/oracle.rb +221 -0
- data/lib/arel/visitors/oracle12.rb +128 -0
- data/spec/active_record/connection_adapters/emulation/oracle_adapter_spec.rb +0 -2
- data/spec/active_record/connection_adapters/oracle_enhanced/connection_spec.rb +78 -26
- data/spec/active_record/connection_adapters/oracle_enhanced/context_index_spec.rb +7 -15
- data/spec/active_record/connection_adapters/oracle_enhanced/database_tasks_spec.rb +5 -0
- data/spec/active_record/connection_adapters/oracle_enhanced/dbms_output_spec.rb +17 -17
- data/spec/active_record/connection_adapters/oracle_enhanced/procedures_spec.rb +7 -10
- data/spec/active_record/connection_adapters/oracle_enhanced/quoting_spec.rb +0 -15
- data/spec/active_record/connection_adapters/oracle_enhanced/schema_dumper_spec.rb +33 -36
- data/spec/active_record/connection_adapters/oracle_enhanced/schema_statements_spec.rb +77 -258
- data/spec/active_record/connection_adapters/oracle_enhanced/structure_dump_spec.rb +38 -39
- data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +273 -85
- data/spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb +7 -8
- data/spec/active_record/oracle_enhanced/type/boolean_spec.rb +2 -4
- data/spec/active_record/oracle_enhanced/type/character_string_spec.rb +43 -0
- data/spec/active_record/oracle_enhanced/type/custom_spec.rb +90 -0
- data/spec/active_record/oracle_enhanced/type/decimal_spec.rb +56 -0
- data/spec/active_record/oracle_enhanced/type/dirty_spec.rb +1 -1
- data/spec/active_record/oracle_enhanced/type/integer_spec.rb +2 -2
- data/spec/active_record/oracle_enhanced/type/json_spec.rb +0 -1
- data/spec/active_record/oracle_enhanced/type/national_character_string_spec.rb +6 -5
- data/spec/active_record/oracle_enhanced/type/timestamp_spec.rb +2 -4
- data/spec/spec_config.yaml.template +2 -2
- data/spec/spec_helper.rb +13 -2
- metadata +52 -30
- data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements_ext.rb +0 -28
@@ -17,21 +17,20 @@ module ActiveRecord
|
|
17
17
|
print "Please provide the SYSTEM password for your Oracle installation (set ORACLE_SYSTEM_PASSWORD to avoid this prompt)\n>"
|
18
18
|
$stdin.gets.strip
|
19
19
|
}
|
20
|
-
establish_connection(@config.merge(
|
20
|
+
establish_connection(@config.merge(username: "SYSTEM", password: system_password))
|
21
21
|
begin
|
22
|
-
connection.execute "CREATE USER #{@config[
|
22
|
+
connection.execute "CREATE USER #{@config[:username]} IDENTIFIED BY #{@config[:password]}"
|
23
23
|
rescue => e
|
24
|
-
if
|
25
|
-
connection.execute "ALTER USER #{@config[
|
24
|
+
if /ORA-01920/.match?(e.message) # user name conflicts with another user or role name
|
25
|
+
connection.execute "ALTER USER #{@config[:username]} IDENTIFIED BY #{@config[:password]}"
|
26
26
|
else
|
27
27
|
raise e
|
28
28
|
end
|
29
29
|
end
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
connection.execute "GRANT create sequence TO #{@config['username']}"
|
30
|
+
|
31
|
+
OracleEnhancedAdapter.permissions.each do |permission|
|
32
|
+
connection.execute "GRANT #{permission} TO #{@config[:username]}"
|
33
|
+
end
|
35
34
|
end
|
36
35
|
|
37
36
|
def drop
|
@@ -47,7 +46,7 @@ module ActiveRecord
|
|
47
46
|
def structure_dump(filename, extra_flags)
|
48
47
|
establish_connection(@config)
|
49
48
|
File.open(filename, "w:utf-8") { |f| f << connection.structure_dump }
|
50
|
-
if @config[
|
49
|
+
if @config[:structure_dump] == "db_stored_code"
|
51
50
|
File.open(filename, "a") { |f| f << connection.structure_dump_db_stored_code }
|
52
51
|
end
|
53
52
|
end
|
@@ -31,8 +31,7 @@ module ActiveRecord
|
|
31
31
|
end
|
32
32
|
|
33
33
|
private
|
34
|
-
|
35
|
-
def log(sql, name = "SQL", binds = [], type_casted_binds = [], statement_name = nil)
|
34
|
+
def log(sql, name = "SQL", binds = [], type_casted_binds = [], statement_name = nil, async: false)
|
36
35
|
super
|
37
36
|
ensure
|
38
37
|
log_dbms_output if dbms_output_enabled?
|
@@ -16,9 +16,10 @@ begin
|
|
16
16
|
# Oracle 11g client ojdbc6.jar is also compatible with Java 1.7
|
17
17
|
# Oracle 12c Release 1 client provides ojdbc7.jar
|
18
18
|
# Oracle 12c Release 2 client provides ojdbc8.jar
|
19
|
-
|
19
|
+
# Oracle 21c provides ojdbc11.jar for Java 11 and above
|
20
|
+
ojdbc_jars = %w(ojdbc11.jar ojdbc8.jar ojdbc7.jar ojdbc6.jar)
|
20
21
|
|
21
|
-
if ENV_JAVA["java.class.path"]
|
22
|
+
if !ENV_JAVA["java.class.path"]&.match?(Regexp.new(ojdbc_jars.join("|")))
|
22
23
|
# On Unix environment variable should be PATH, on Windows it is sometimes Path
|
23
24
|
env_path = (ENV["PATH"] || ENV["Path"] || "").split(File::PATH_SEPARATOR)
|
24
25
|
# Look for JDBC driver at first in lib subdirectory (application specific JDBC file version)
|
@@ -26,7 +27,7 @@ begin
|
|
26
27
|
["./lib"].concat($LOAD_PATH).concat(env_path).detect do |dir|
|
27
28
|
# check any compatible JDBC driver in the priority order
|
28
29
|
ojdbc_jars.any? do |ojdbc_jar|
|
29
|
-
if File.
|
30
|
+
if File.exist?(file_path = File.join(dir, ojdbc_jar))
|
30
31
|
require file_path
|
31
32
|
true
|
32
33
|
end
|
@@ -42,16 +43,16 @@ begin
|
|
42
43
|
java.lang.System.set_property("oracle.net.tns_admin", ENV["TNS_ADMIN"])
|
43
44
|
end
|
44
45
|
|
45
|
-
rescue LoadError, NameError
|
46
|
+
rescue LoadError, NameError => e
|
46
47
|
# JDBC driver is unavailable.
|
47
|
-
raise LoadError, "ERROR: ActiveRecord oracle_enhanced adapter could not load Oracle JDBC driver. Please install #{ojdbc_jars.join(' or ') } library."
|
48
|
+
raise LoadError, "ERROR: ActiveRecord oracle_enhanced adapter could not load Oracle JDBC driver. Please install #{ojdbc_jars.join(' or ') } library.\n#{e.class}:#{e.message}"
|
48
49
|
end
|
49
50
|
|
50
51
|
module ActiveRecord
|
51
52
|
module ConnectionAdapters
|
52
53
|
# JDBC database interface for JRuby
|
53
54
|
module OracleEnhanced
|
54
|
-
class JDBCConnection < OracleEnhanced::Connection
|
55
|
+
class JDBCConnection < OracleEnhanced::Connection # :nodoc:
|
55
56
|
attr_accessor :active
|
56
57
|
alias :active? :active
|
57
58
|
|
@@ -98,6 +99,8 @@ module ActiveRecord
|
|
98
99
|
@raw_connection = @raw_connection.underlying_connection
|
99
100
|
end
|
100
101
|
|
102
|
+
# Workaround FrozenError (can't modify frozen Hash):
|
103
|
+
config = config.dup
|
101
104
|
config[:driver] ||= @raw_connection.meta_data.connection.java_class.name
|
102
105
|
username = @raw_connection.meta_data.user_name
|
103
106
|
else
|
@@ -113,11 +116,11 @@ module ActiveRecord
|
|
113
116
|
if database && (using_tns_alias || host == "connection-string")
|
114
117
|
url = "jdbc:oracle:thin:@#{database}"
|
115
118
|
else
|
116
|
-
unless database.match(/^(
|
119
|
+
unless database.match?(/^(:|\/)/)
|
117
120
|
# assume database is a SID if no colon or slash are supplied (backward-compatibility)
|
118
|
-
database = "
|
121
|
+
database = "/#{database}"
|
119
122
|
end
|
120
|
-
url = config[:url] || "jdbc:oracle:thin
|
123
|
+
url = config[:url] || "jdbc:oracle:thin:@//#{host || 'localhost'}:#{port || 1521}#{database}"
|
121
124
|
end
|
122
125
|
|
123
126
|
prefetch_rows = config[:prefetch_rows] || 100
|
@@ -125,6 +128,8 @@ module ActiveRecord
|
|
125
128
|
time_zone = config[:time_zone] || ENV["TZ"] || java.util.TimeZone.default.getID
|
126
129
|
|
127
130
|
properties = java.util.Properties.new
|
131
|
+
raise "username not set" unless username
|
132
|
+
raise "password not set" unless password
|
128
133
|
properties.put("user", username)
|
129
134
|
properties.put("password", password)
|
130
135
|
properties.put("defaultRowPrefetch", "#{prefetch_rows}") if prefetch_rows
|
@@ -140,12 +145,18 @@ module ActiveRecord
|
|
140
145
|
end
|
141
146
|
|
142
147
|
# Set session time zone to current time zone
|
143
|
-
if ActiveRecord
|
148
|
+
if ActiveRecord.default_timezone == :local
|
144
149
|
@raw_connection.setSessionTimeZone(time_zone)
|
145
|
-
elsif ActiveRecord
|
150
|
+
elsif ActiveRecord.default_timezone == :utc
|
146
151
|
@raw_connection.setSessionTimeZone("UTC")
|
147
152
|
end
|
148
153
|
|
154
|
+
if config[:jdbc_statement_cache_size]
|
155
|
+
raise "Integer value expected for :jdbc_statement_cache_size" unless config[:jdbc_statement_cache_size].instance_of? Integer
|
156
|
+
@raw_connection.setImplicitCachingEnabled(true)
|
157
|
+
@raw_connection.setStatementCacheSize(config[:jdbc_statement_cache_size])
|
158
|
+
end
|
159
|
+
|
149
160
|
# Set default number of rows to prefetch
|
150
161
|
# @raw_connection.setDefaultRowPrefetch(prefetch_rows) if prefetch_rows
|
151
162
|
end
|
@@ -161,6 +172,10 @@ module ActiveRecord
|
|
161
172
|
end
|
162
173
|
end
|
163
174
|
|
175
|
+
OracleEnhancedAdapter::FIXED_NLS_PARAMETERS.each do |key, value|
|
176
|
+
exec "alter session set #{key} = '#{value}'"
|
177
|
+
end
|
178
|
+
|
164
179
|
self.autocommit = true
|
165
180
|
|
166
181
|
schema = config[:schema] && config[:schema].to_s
|
@@ -232,7 +247,7 @@ module ActiveRecord
|
|
232
247
|
begin
|
233
248
|
yield if block_given?
|
234
249
|
rescue Java::JavaSql::SQLException => e
|
235
|
-
raise unless
|
250
|
+
raise unless /^(Closed Connection|Io exception:|No more data to read from socket|IO Error:)/.match?(e.message)
|
236
251
|
@active = false
|
237
252
|
raise unless should_retry
|
238
253
|
should_retry = false
|
@@ -312,6 +327,8 @@ module ActiveRecord
|
|
312
327
|
@raw_statement.setClob(position, value)
|
313
328
|
when Type::OracleEnhanced::Raw
|
314
329
|
@raw_statement.setString(position, OracleEnhanced::Quoting.encode_raw(value))
|
330
|
+
when Type::OracleEnhanced::CharacterString::Data
|
331
|
+
@raw_statement.setFixedCHAR(position, value.to_s)
|
315
332
|
when String
|
316
333
|
@raw_statement.setString(position, value)
|
317
334
|
when Java::OracleSql::DATE
|
@@ -470,19 +487,24 @@ module ActiveRecord
|
|
470
487
|
end
|
471
488
|
when :BINARY_FLOAT
|
472
489
|
rset.getFloat(i)
|
473
|
-
when :VARCHAR2, :
|
490
|
+
when :VARCHAR2, :LONG, :NVARCHAR2
|
474
491
|
rset.getString(i)
|
492
|
+
when :CHAR, :NCHAR
|
493
|
+
char_str = rset.getString(i)
|
494
|
+
if !char_str.nil?
|
495
|
+
char_str.rstrip
|
496
|
+
end
|
475
497
|
when :DATE
|
476
498
|
if dt = rset.getDATE(i)
|
477
499
|
d = dt.dateValue
|
478
500
|
t = dt.timeValue
|
479
|
-
Time.send(
|
501
|
+
Time.send(ActiveRecord.default_timezone, d.year + 1900, d.month + 1, d.date, t.hours, t.minutes, t.seconds)
|
480
502
|
else
|
481
503
|
nil
|
482
504
|
end
|
483
505
|
when :TIMESTAMP, :TIMESTAMPTZ, :TIMESTAMPLTZ, :"TIMESTAMP WITH TIME ZONE", :"TIMESTAMP WITH LOCAL TIME ZONE"
|
484
506
|
ts = rset.getTimestamp(i)
|
485
|
-
ts && Time.send(
|
507
|
+
ts && Time.send(ActiveRecord.default_timezone, ts.year + 1900, ts.month + 1, ts.date, ts.hours, ts.minutes, ts.seconds,
|
486
508
|
ts.nanos / 1000)
|
487
509
|
when :CLOB
|
488
510
|
get_lob_value ? lob_to_ruby_value(rset.getClob(i)) : rset.getClob(i)
|
@@ -499,7 +521,6 @@ module ActiveRecord
|
|
499
521
|
end
|
500
522
|
|
501
523
|
private
|
502
|
-
|
503
524
|
def lob_to_ruby_value(val)
|
504
525
|
case val
|
505
526
|
when ::Java::OracleSql::CLOB
|
@@ -4,7 +4,7 @@ module ActiveRecord
|
|
4
4
|
module ConnectionAdapters
|
5
5
|
module OracleEnhanced
|
6
6
|
module JDBCQuoting
|
7
|
-
def
|
7
|
+
def type_cast(value)
|
8
8
|
case value
|
9
9
|
when ActiveModel::Type::Binary::Data
|
10
10
|
blob = Java::OracleSql::BLOB.createTemporary(@connection.raw_connection, false, Java::OracleSql::BLOB::DURATION_SESSION)
|
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module ActiveRecord
|
4
|
-
module ConnectionAdapters
|
5
|
-
module OracleEnhanced
|
6
|
-
module Lob
|
3
|
+
module ActiveRecord # :nodoc:
|
4
|
+
module ConnectionAdapters # :nodoc:
|
5
|
+
module OracleEnhanced # :nodoc:
|
6
|
+
module Lob # :nodoc:
|
7
7
|
extend ActiveSupport::Concern
|
8
8
|
|
9
9
|
included do
|
@@ -18,13 +18,12 @@ module ActiveRecord #:nodoc:
|
|
18
18
|
module ClassMethods
|
19
19
|
def lob_columns
|
20
20
|
columns.select do |column|
|
21
|
-
column.sql_type_metadata.sql_type
|
21
|
+
column.sql_type_metadata.sql_type.end_with?("LOB")
|
22
22
|
end
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
26
|
private
|
27
|
-
|
28
27
|
def enhanced_write_lobs
|
29
28
|
if self.class.connection.is_a?(ConnectionAdapters::OracleEnhancedAdapter) &&
|
30
29
|
!(self.class.custom_create_method || self.class.custom_update_method)
|
@@ -15,7 +15,7 @@ end
|
|
15
15
|
required_oci8_version = [2, 2, 4]
|
16
16
|
oci8_version_ints = OCI8::VERSION.scan(/\d+/).map { |s| s.to_i }
|
17
17
|
if (oci8_version_ints <=> required_oci8_version) < 0
|
18
|
-
$stderr.puts
|
18
|
+
$stderr.puts <<~EOS
|
19
19
|
"ERROR: ruby-oci8 version #{OCI8::VERSION} is too old. Please install ruby-oci8 version #{required_oci8_version.join('.')} or later."
|
20
20
|
EOS
|
21
21
|
|
@@ -26,7 +26,7 @@ module ActiveRecord
|
|
26
26
|
module ConnectionAdapters
|
27
27
|
# OCI database interface for MRI
|
28
28
|
module OracleEnhanced
|
29
|
-
class OCIConnection < OracleEnhanced::Connection
|
29
|
+
class OCIConnection < OracleEnhanced::Connection # :nodoc:
|
30
30
|
def initialize(config)
|
31
31
|
@raw_connection = OCI8EnhancedAutoRecover.new(config, OracleEnhancedOCIFactory)
|
32
32
|
# default schema owner
|
@@ -97,6 +97,10 @@ module ActiveRecord
|
|
97
97
|
@raw_connection.exec(sql, *bindvars, &block)
|
98
98
|
end
|
99
99
|
|
100
|
+
def with_retry(&block)
|
101
|
+
@raw_connection.with_retry(&block)
|
102
|
+
end
|
103
|
+
|
100
104
|
def prepare(sql)
|
101
105
|
Cursor.new(self, @raw_connection.parse(sql))
|
102
106
|
end
|
@@ -125,6 +129,8 @@ module ActiveRecord
|
|
125
129
|
@raw_cursor.bind_param(position, OracleEnhanced::Quoting.encode_raw(value))
|
126
130
|
when ActiveModel::Type::Decimal
|
127
131
|
@raw_cursor.bind_param(position, BigDecimal(value.to_s))
|
132
|
+
when Type::OracleEnhanced::CharacterString::Data
|
133
|
+
@raw_cursor.bind_param(position, value.to_character_str)
|
128
134
|
when NilClass
|
129
135
|
@raw_cursor.bind_param(position, nil, String)
|
130
136
|
else
|
@@ -151,8 +157,19 @@ module ActiveRecord
|
|
151
157
|
def fetch(options = {})
|
152
158
|
if row = @raw_cursor.fetch
|
153
159
|
get_lob_value = options[:get_lob_value]
|
160
|
+
col_index = 0
|
154
161
|
row.map do |col|
|
155
|
-
@connection.typecast_result_value(col, get_lob_value)
|
162
|
+
col_value = @connection.typecast_result_value(col, get_lob_value)
|
163
|
+
col_metadata = @raw_cursor.column_metadata.fetch(col_index)
|
164
|
+
if !col_metadata.nil?
|
165
|
+
key = col_metadata.data_type
|
166
|
+
case key.to_s.downcase
|
167
|
+
when "char"
|
168
|
+
col_value = col.to_s.rstrip
|
169
|
+
end
|
170
|
+
end
|
171
|
+
col_index = col_index + 1
|
172
|
+
col_value
|
156
173
|
end
|
157
174
|
end
|
158
175
|
end
|
@@ -184,7 +201,16 @@ module ActiveRecord
|
|
184
201
|
hash = column_hash.dup
|
185
202
|
|
186
203
|
cols.each_with_index do |col, i|
|
187
|
-
|
204
|
+
col_value = typecast_result_value(row[i], get_lob_value)
|
205
|
+
col_metadata = cursor.column_metadata.fetch(i)
|
206
|
+
if !col_metadata.nil?
|
207
|
+
key = col_metadata.data_type
|
208
|
+
case key.to_s.downcase
|
209
|
+
when "char"
|
210
|
+
col_value = col_value.to_s.rstrip
|
211
|
+
end
|
212
|
+
end
|
213
|
+
hash[col] = col_value
|
188
214
|
end
|
189
215
|
|
190
216
|
rows << hash
|
@@ -200,17 +226,7 @@ module ActiveRecord
|
|
200
226
|
end
|
201
227
|
|
202
228
|
def describe(name)
|
203
|
-
|
204
|
-
return super if name.to_s.include?("@")
|
205
|
-
quoted_name = OracleEnhanced::Quoting.valid_table_name?(name) ? name : "\"#{name}\""
|
206
|
-
@raw_connection.describe(quoted_name)
|
207
|
-
rescue OCIException => e
|
208
|
-
if e.code == 4043
|
209
|
-
raise OracleEnhanced::ConnectionException, %Q{"DESC #{name}" failed; does it exist?}
|
210
|
-
else
|
211
|
-
# fall back to SELECT which can handle synonyms to database links
|
212
|
-
super
|
213
|
-
end
|
229
|
+
super
|
214
230
|
end
|
215
231
|
|
216
232
|
# Return OCIError error code
|
@@ -253,7 +269,6 @@ module ActiveRecord
|
|
253
269
|
end
|
254
270
|
|
255
271
|
private
|
256
|
-
|
257
272
|
def date_without_time?(value)
|
258
273
|
case value
|
259
274
|
when OraDate
|
@@ -274,9 +289,9 @@ module ActiveRecord
|
|
274
289
|
end
|
275
290
|
# code from Time.time_with_datetime_fallback
|
276
291
|
begin
|
277
|
-
Time.send(
|
292
|
+
Time.send(ActiveRecord.default_timezone, year, month, day, hour, min, sec, usec)
|
278
293
|
rescue
|
279
|
-
offset =
|
294
|
+
offset = ActiveRecord.default_timezone.to_sym == :local ? ::DateTime.local_offset : 0
|
280
295
|
::DateTime.civil(year, month, day, hour, min, sec, offset)
|
281
296
|
end
|
282
297
|
end
|
@@ -284,7 +299,7 @@ module ActiveRecord
|
|
284
299
|
|
285
300
|
# The OracleEnhancedOCIFactory factors out the code necessary to connect and
|
286
301
|
# configure an Oracle/OCI connection.
|
287
|
-
class OracleEnhancedOCIFactory
|
302
|
+
class OracleEnhancedOCIFactory # :nodoc:
|
288
303
|
DEFAULT_TCP_KEEPALIVE_TIME = 600
|
289
304
|
|
290
305
|
def self.new_connection(config)
|
@@ -307,9 +322,9 @@ module ActiveRecord
|
|
307
322
|
# connection using host, port and database name
|
308
323
|
elsif host || port
|
309
324
|
host ||= "localhost"
|
310
|
-
host = "[#{host}]" if
|
325
|
+
host = "[#{host}]" if /^[^\[].*:/.match?(host) # IPv6
|
311
326
|
port ||= 1521
|
312
|
-
database = "/#{database}" unless database.
|
327
|
+
database = "/#{database}" unless database.start_with?("/")
|
313
328
|
"//#{host}:#{port}#{database}"
|
314
329
|
# if no host is specified then assume that
|
315
330
|
# database parameter is TNS alias or TNS connection string
|
@@ -328,9 +343,9 @@ module ActiveRecord
|
|
328
343
|
conn.non_blocking = true if async
|
329
344
|
conn.prefetch_rows = prefetch_rows
|
330
345
|
conn.exec "alter session set cursor_sharing = #{cursor_sharing}" rescue nil if cursor_sharing
|
331
|
-
if ActiveRecord
|
346
|
+
if ActiveRecord.default_timezone == :local
|
332
347
|
conn.exec "alter session set time_zone = '#{time_zone}'" unless time_zone.blank?
|
333
|
-
elsif ActiveRecord
|
348
|
+
elsif ActiveRecord.default_timezone == :utc
|
334
349
|
conn.exec "alter session set time_zone = '+00:00'"
|
335
350
|
end
|
336
351
|
conn.exec "alter session set current_schema = #{schema}" unless schema.blank?
|
@@ -342,6 +357,10 @@ module ActiveRecord
|
|
342
357
|
conn.exec "alter session set #{key} = '#{value}'"
|
343
358
|
end
|
344
359
|
end
|
360
|
+
|
361
|
+
OracleEnhancedAdapter::FIXED_NLS_PARAMETERS.each do |key, value|
|
362
|
+
conn.exec "alter session set #{key} = '#{value}'"
|
363
|
+
end
|
345
364
|
conn
|
346
365
|
end
|
347
366
|
end
|
@@ -349,18 +368,6 @@ module ActiveRecord
|
|
349
368
|
end
|
350
369
|
end
|
351
370
|
|
352
|
-
class OCI8 #:nodoc:
|
353
|
-
def describe(name)
|
354
|
-
info = describe_table(name.to_s)
|
355
|
-
raise %Q{"DESC #{name}" failed} if info.nil?
|
356
|
-
if info.respond_to?(:obj_link) && info.obj_link
|
357
|
-
[info.obj_schema, info.obj_name, "@" + info.obj_link]
|
358
|
-
else
|
359
|
-
[info.obj_schema, info.obj_name]
|
360
|
-
end
|
361
|
-
end
|
362
|
-
end
|
363
|
-
|
364
371
|
# The OCI8AutoRecover class enhances the OCI8 driver with auto-recover and
|
365
372
|
# reset functionality. If a call to #exec fails, and autocommit is turned on
|
366
373
|
# (ie., we're not in the middle of a longer transaction), it will
|
@@ -368,18 +375,18 @@ end
|
|
368
375
|
# this would be dangerous (as the earlier part of the implied transaction
|
369
376
|
# may have failed silently if the connection died) -- so instead the
|
370
377
|
# connection is marked as dead, to be reconnected on it's next use.
|
371
|
-
|
372
|
-
class OCI8EnhancedAutoRecover < DelegateClass(OCI8)
|
373
|
-
attr_accessor :active
|
374
|
-
alias :active? :active
|
378
|
+
# :stopdoc:
|
379
|
+
class OCI8EnhancedAutoRecover < DelegateClass(OCI8) # :nodoc:
|
380
|
+
attr_accessor :active # :nodoc:
|
381
|
+
alias :active? :active # :nodoc:
|
375
382
|
|
376
383
|
cattr_accessor :auto_retry
|
377
384
|
class << self
|
378
|
-
alias :auto_retry? :auto_retry
|
385
|
+
alias :auto_retry? :auto_retry # :nodoc:
|
379
386
|
end
|
380
387
|
@@auto_retry = false
|
381
388
|
|
382
|
-
def initialize(config, factory)
|
389
|
+
def initialize(config, factory) # :nodoc:
|
383
390
|
@active = true
|
384
391
|
@config = config
|
385
392
|
@factory = factory
|
@@ -390,7 +397,7 @@ class OCI8EnhancedAutoRecover < DelegateClass(OCI8) #:nodoc:
|
|
390
397
|
# Checks connection, returns true if active. Note that ping actively
|
391
398
|
# checks the connection, while #active? simply returns the last
|
392
399
|
# known state.
|
393
|
-
def ping
|
400
|
+
def ping # :nodoc:
|
394
401
|
@connection.exec("select 1 from dual") { |r| nil }
|
395
402
|
@active = true
|
396
403
|
rescue
|
@@ -399,7 +406,7 @@ class OCI8EnhancedAutoRecover < DelegateClass(OCI8) #:nodoc:
|
|
399
406
|
end
|
400
407
|
|
401
408
|
# Resets connection, by logging off and creating a new connection.
|
402
|
-
def reset!
|
409
|
+
def reset! # :nodoc:
|
403
410
|
logoff rescue nil
|
404
411
|
begin
|
405
412
|
@connection = @factory.new_connection @config
|
@@ -416,16 +423,14 @@ class OCI8EnhancedAutoRecover < DelegateClass(OCI8) #:nodoc:
|
|
416
423
|
# ORA-03113: end-of-file on communication channel
|
417
424
|
# ORA-03114: not connected to ORACLE
|
418
425
|
# ORA-03135: connection lost contact
|
419
|
-
LOST_CONNECTION_ERROR_CODES = [ 28, 1012, 3113, 3114, 3135 ]
|
426
|
+
LOST_CONNECTION_ERROR_CODES = [ 28, 1012, 3113, 3114, 3135 ] # :nodoc:
|
420
427
|
|
421
428
|
# Adds auto-recovery functionality.
|
422
|
-
#
|
423
|
-
# See: http://www.jiubao.org/ruby-oci8/api.en.html#label-11
|
424
|
-
def exec(sql, *bindvars, &block) #:nodoc:
|
429
|
+
def with_retry # :nodoc:
|
425
430
|
should_retry = self.class.auto_retry? && autocommit?
|
426
431
|
|
427
432
|
begin
|
428
|
-
|
433
|
+
yield
|
429
434
|
rescue OCIException => e
|
430
435
|
raise unless e.is_a?(OCIError) && LOST_CONNECTION_ERROR_CODES.include?(e.code)
|
431
436
|
@active = false
|
@@ -435,5 +440,9 @@ class OCI8EnhancedAutoRecover < DelegateClass(OCI8) #:nodoc:
|
|
435
440
|
retry
|
436
441
|
end
|
437
442
|
end
|
443
|
+
|
444
|
+
def exec(sql, *bindvars, &block) # :nodoc:
|
445
|
+
with_retry { @connection.exec(sql, *bindvars, &block) }
|
446
|
+
end
|
438
447
|
end
|
439
|
-
|
448
|
+
# :startdoc:
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
require "active_support"
|
4
4
|
|
5
|
-
module ActiveRecord
|
5
|
+
module ActiveRecord # :nodoc:
|
6
6
|
# Custom create, update, delete methods functionality.
|
7
7
|
#
|
8
8
|
# Example:
|
@@ -33,7 +33,7 @@ module ActiveRecord #:nodoc:
|
|
33
33
|
# end
|
34
34
|
# end
|
35
35
|
#
|
36
|
-
module OracleEnhancedProcedures
|
36
|
+
module OracleEnhancedProcedures # :nodoc:
|
37
37
|
module ClassMethods
|
38
38
|
# Specify custom create method which should be used instead of Rails generated INSERT statement.
|
39
39
|
# Provided block should return ID of new record.
|
@@ -83,7 +83,7 @@ module ActiveRecord #:nodoc:
|
|
83
83
|
end
|
84
84
|
end
|
85
85
|
|
86
|
-
def destroy
|
86
|
+
def destroy # :nodoc:
|
87
87
|
# check if class has custom delete method
|
88
88
|
if self.class.custom_delete_method
|
89
89
|
# wrap destroy in transaction
|
@@ -97,7 +97,6 @@ module ActiveRecord #:nodoc:
|
|
97
97
|
end
|
98
98
|
|
99
99
|
private
|
100
|
-
|
101
100
|
# Creates a record with custom create method
|
102
101
|
# and returns its id.
|
103
102
|
def _create_record
|
@@ -151,7 +150,7 @@ module ActiveRecord #:nodoc:
|
|
151
150
|
end
|
152
151
|
end
|
153
152
|
# update just dirty attributes
|
154
|
-
if
|
153
|
+
if partial_updates?
|
155
154
|
# Serialized attributes should always be written in case they've been
|
156
155
|
# changed in place.
|
157
156
|
update_using_custom_method(changed | (attributes.keys & self.class.columns.select { |column| column.is_a?(Type::Serialized) }))
|
@@ -186,8 +185,8 @@ module ActiveRecord #:nodoc:
|
|
186
185
|
freeze
|
187
186
|
end
|
188
187
|
|
189
|
-
def log_custom_method(*args)
|
190
|
-
self.class.connection.send(:log, *args)
|
188
|
+
def log_custom_method(*args, &block)
|
189
|
+
self.class.connection.send(:log, *args, &block)
|
191
190
|
end
|
192
191
|
|
193
192
|
alias_method :update_record, :_update_record if private_method_defined?(:_update_record)
|