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
@@ -4,7 +4,7 @@ module ActiveRecord
|
|
4
4
|
module ConnectionAdapters
|
5
5
|
# interface independent methods
|
6
6
|
module OracleEnhanced
|
7
|
-
class Connection
|
7
|
+
class Connection # :nodoc:
|
8
8
|
def self.create(config)
|
9
9
|
case ORACLE_ENHANCED_CONNECTION
|
10
10
|
when :oci
|
@@ -19,16 +19,12 @@ module ActiveRecord
|
|
19
19
|
attr_reader :raw_connection
|
20
20
|
|
21
21
|
private
|
22
|
-
|
23
22
|
# Used always by JDBC connection as well by OCI connection when describing tables over database link
|
24
23
|
def describe(name)
|
25
24
|
name = name.to_s
|
26
25
|
if name.include?("@")
|
27
|
-
|
28
|
-
default_owner = _select_value("SELECT username FROM all_db_links WHERE db_link = '#{db_link.upcase}'")
|
29
|
-
db_link = "@#{db_link}"
|
26
|
+
raise ArgumentError "db link is not supported"
|
30
27
|
else
|
31
|
-
db_link = nil
|
32
28
|
default_owner = @owner
|
33
29
|
end
|
34
30
|
real_name = OracleEnhanced::Quoting.valid_table_name?(name) ? name.upcase : name
|
@@ -37,33 +33,33 @@ module ActiveRecord
|
|
37
33
|
else
|
38
34
|
table_owner, table_name = default_owner, real_name
|
39
35
|
end
|
40
|
-
sql =
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
if result = _select_one(sql)
|
36
|
+
sql = <<~SQL.squish
|
37
|
+
SELECT owner, table_name, 'TABLE' name_type
|
38
|
+
FROM all_tables
|
39
|
+
WHERE owner = :table_owner
|
40
|
+
AND table_name = :table_name
|
41
|
+
UNION ALL
|
42
|
+
SELECT owner, view_name table_name, 'VIEW' name_type
|
43
|
+
FROM all_views
|
44
|
+
WHERE owner = :table_owner
|
45
|
+
AND view_name = :table_name
|
46
|
+
UNION ALL
|
47
|
+
SELECT table_owner, table_name, 'SYNONYM' name_type
|
48
|
+
FROM all_synonyms
|
49
|
+
WHERE owner = :table_owner
|
50
|
+
AND synonym_name = :table_name
|
51
|
+
UNION ALL
|
52
|
+
SELECT table_owner, table_name, 'SYNONYM' name_type
|
53
|
+
FROM all_synonyms
|
54
|
+
WHERE owner = 'PUBLIC'
|
55
|
+
AND synonym_name = :real_name
|
56
|
+
SQL
|
57
|
+
if result = _select_one(sql, "CONNECTION", [table_owner, table_name, table_owner, table_name, table_owner, table_name, real_name])
|
62
58
|
case result["name_type"]
|
63
59
|
when "SYNONYM"
|
64
|
-
describe("#{result['owner'] && "#{result['owner']}."}#{result['table_name']}
|
60
|
+
describe("#{result['owner'] && "#{result['owner']}."}#{result['table_name']}")
|
65
61
|
else
|
66
|
-
|
62
|
+
[result["owner"], result["table_name"]]
|
67
63
|
end
|
68
64
|
else
|
69
65
|
raise OracleEnhanced::ConnectionException, %Q{"DESC #{name}" failed; does it exist?}
|
@@ -85,7 +81,7 @@ module ActiveRecord
|
|
85
81
|
# To avoid it is called from anywhere else, added _ at the beginning of the method name.
|
86
82
|
def _oracle_downcase(column_name)
|
87
83
|
return nil if column_name.nil?
|
88
|
-
|
84
|
+
/[a-z]/.match?(column_name) ? column_name : column_name.downcase
|
89
85
|
end
|
90
86
|
|
91
87
|
# _select_one and _select_value methods are expected to be called
|
@@ -96,14 +92,23 @@ module ActiveRecord
|
|
96
92
|
|
97
93
|
# Returns a record hash with the column names as keys and column values
|
98
94
|
# as values.
|
95
|
+
# binds is a array of native values in contrast to ActiveRecord::Relation::QueryAttribute
|
99
96
|
def _select_one(arel, name = nil, binds = [])
|
100
|
-
|
101
|
-
|
97
|
+
cursor = prepare(arel)
|
98
|
+
cursor.bind_params(binds)
|
99
|
+
cursor.exec
|
100
|
+
columns = cursor.get_col_names.map do |col_name|
|
101
|
+
_oracle_downcase(col_name)
|
102
|
+
end
|
103
|
+
row = cursor.fetch
|
104
|
+
columns.each_with_index.map { |x, i| [x, row[i]] }.to_h if row
|
105
|
+
ensure
|
106
|
+
cursor.close
|
102
107
|
end
|
103
108
|
|
104
109
|
# Returns a single value from a record
|
105
110
|
def _select_value(arel, name = nil, binds = [])
|
106
|
-
if result = _select_one(arel)
|
111
|
+
if result = _select_one(arel, name, binds)
|
107
112
|
result.values.first
|
108
113
|
end
|
109
114
|
end
|
@@ -113,14 +118,14 @@ module ActiveRecord
|
|
113
118
|
def database_version
|
114
119
|
raise NoMethodError, "Not implemented for this raw driver"
|
115
120
|
end
|
116
|
-
class ConnectionException < StandardError
|
121
|
+
class ConnectionException < StandardError # :nodoc:
|
117
122
|
end
|
118
123
|
end
|
119
124
|
end
|
120
125
|
end
|
121
126
|
|
122
|
-
# if MRI or YARV
|
123
|
-
if !defined?(RUBY_ENGINE) || RUBY_ENGINE == "ruby"
|
127
|
+
# if MRI or YARV or TruffleRuby
|
128
|
+
if !defined?(RUBY_ENGINE) || RUBY_ENGINE == "ruby" || RUBY_ENGINE == "truffleruby"
|
124
129
|
ORACLE_ENHANCED_CONNECTION = :oci
|
125
130
|
require "active_record/connection_adapters/oracle_enhanced/oci_connection"
|
126
131
|
# if JRuby
|
@@ -86,7 +86,7 @@ module ActiveRecord
|
|
86
86
|
create_index_column_trigger(table_name, index_name, options[:index_column], options[:index_column_trigger_on])
|
87
87
|
end
|
88
88
|
|
89
|
-
sql = "CREATE INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
|
89
|
+
sql = +"CREATE INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
|
90
90
|
sql << " (#{quoted_column_name})"
|
91
91
|
sql << " INDEXTYPE IS CTXSYS.CONTEXT"
|
92
92
|
parameters = []
|
@@ -142,63 +142,62 @@ module ActiveRecord
|
|
142
142
|
end
|
143
143
|
|
144
144
|
private
|
145
|
-
|
146
145
|
def create_datastore_procedure(table_name, procedure_name, column_names, options)
|
147
146
|
quoted_table_name = quote_table_name(table_name)
|
148
147
|
select_queries, column_names = column_names.partition { |c| c.to_s =~ /^\s*SELECT\s+/i }
|
149
148
|
select_queries = select_queries.map { |s| s.strip.gsub(/\s+/, " ") }
|
150
149
|
keys, selected_columns = parse_select_queries(select_queries)
|
151
150
|
quoted_column_names = (column_names + keys).map { |col| quote_column_name(col) }
|
152
|
-
execute
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
151
|
+
execute <<~SQL
|
152
|
+
CREATE OR REPLACE PROCEDURE #{quote_table_name(procedure_name)}
|
153
|
+
(p_rowid IN ROWID,
|
154
|
+
p_clob IN OUT NOCOPY CLOB) IS
|
155
|
+
-- add_context_index_parameters #{(column_names + select_queries).inspect}#{!options.empty? ? +', ' << options.inspect[1..-2] : ''}
|
157
156
|
#{
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
157
|
+
selected_columns.map do |cols|
|
158
|
+
cols.map do |col|
|
159
|
+
raise ArgumentError, "Alias #{col} too large, should be 28 or less characters long" unless col.length <= 28
|
160
|
+
"l_#{col} VARCHAR2(32767);\n"
|
161
|
+
end.join
|
162
162
|
end.join
|
163
|
-
end.join
|
164
163
|
} BEGIN
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
col = col.to_s
|
173
|
-
"DBMS_LOB.WRITEAPPEND(p_clob, #{col.length + 2}, '<#{col}>');\n".dup <<
|
174
|
-
"IF LENGTH(r1.#{col}) > 0 THEN\n" <<
|
175
|
-
"DBMS_LOB.WRITEAPPEND(p_clob, LENGTH(r1.#{col}), r1.#{col});\n" <<
|
176
|
-
"END IF;\n" <<
|
177
|
-
"DBMS_LOB.WRITEAPPEND(p_clob, #{col.length + 3}, '</#{col}>');\n"
|
178
|
-
end.join) <<
|
179
|
-
(selected_columns.zip(select_queries).map do |cols, query|
|
180
|
-
(cols.map do |col|
|
181
|
-
"l_#{col} := '';\n"
|
182
|
-
end.join) <<
|
183
|
-
"FOR r2 IN (\n" <<
|
184
|
-
query.gsub(/:(\w+)/, "r1.\\1") << "\n) LOOP\n" <<
|
185
|
-
(cols.map do |col|
|
186
|
-
"l_#{col} := l_#{col} || r2.#{col} || CHR(10);\n"
|
187
|
-
end.join) <<
|
188
|
-
"END LOOP;\n" <<
|
189
|
-
(cols.map do |col|
|
164
|
+
FOR r1 IN (
|
165
|
+
SELECT #{quoted_column_names.join(', ')}
|
166
|
+
FROM #{quoted_table_name}
|
167
|
+
WHERE #{quoted_table_name}.ROWID = p_rowid
|
168
|
+
) LOOP
|
169
|
+
#{
|
170
|
+
(column_names.map do |col|
|
190
171
|
col = col.to_s
|
191
|
-
"DBMS_LOB.WRITEAPPEND(p_clob, #{col.length + 2}, '<#{col}>');\n"
|
192
|
-
"IF LENGTH(
|
193
|
-
"DBMS_LOB.WRITEAPPEND(p_clob, LENGTH(
|
172
|
+
+"DBMS_LOB.WRITEAPPEND(p_clob, #{col.length + 2}, '<#{col}>');\n" <<
|
173
|
+
"IF LENGTH(r1.#{col}) > 0 THEN\n" <<
|
174
|
+
"DBMS_LOB.WRITEAPPEND(p_clob, LENGTH(r1.#{col}), r1.#{col});\n" <<
|
194
175
|
"END IF;\n" <<
|
195
176
|
"DBMS_LOB.WRITEAPPEND(p_clob, #{col.length + 3}, '</#{col}>');\n"
|
177
|
+
end.join) <<
|
178
|
+
(selected_columns.zip(select_queries).map do |cols, query|
|
179
|
+
(cols.map do |col|
|
180
|
+
"l_#{col} := '';\n"
|
181
|
+
end.join) <<
|
182
|
+
"FOR r2 IN (\n" <<
|
183
|
+
query.gsub(/:(\w+)/, "r1.\\1") << "\n) LOOP\n" <<
|
184
|
+
(cols.map do |col|
|
185
|
+
"l_#{col} := l_#{col} || r2.#{col} || CHR(10);\n"
|
186
|
+
end.join) <<
|
187
|
+
"END LOOP;\n" <<
|
188
|
+
(cols.map do |col|
|
189
|
+
col = col.to_s
|
190
|
+
+"DBMS_LOB.WRITEAPPEND(p_clob, #{col.length + 2}, '<#{col}>');\n" <<
|
191
|
+
"IF LENGTH(l_#{col}) > 0 THEN\n" <<
|
192
|
+
"DBMS_LOB.WRITEAPPEND(p_clob, LENGTH(l_#{col}), l_#{col});\n" <<
|
193
|
+
"END IF;\n" <<
|
194
|
+
"DBMS_LOB.WRITEAPPEND(p_clob, #{col.length + 3}, '</#{col}>');\n"
|
195
|
+
end.join)
|
196
196
|
end.join)
|
197
|
-
end.join)
|
198
197
|
}
|
199
|
-
|
200
|
-
|
201
|
-
|
198
|
+
END LOOP;
|
199
|
+
END;
|
200
|
+
SQL
|
202
201
|
end
|
203
202
|
|
204
203
|
def parse_select_queries(select_queries)
|
@@ -215,17 +214,17 @@ module ActiveRecord
|
|
215
214
|
|
216
215
|
def create_datastore_preference(datastore_name, procedure_name)
|
217
216
|
drop_ctx_preference(datastore_name)
|
218
|
-
execute
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
217
|
+
execute <<~SQL
|
218
|
+
BEGIN
|
219
|
+
CTX_DDL.CREATE_PREFERENCE('#{datastore_name}', 'USER_DATASTORE');
|
220
|
+
CTX_DDL.SET_ATTRIBUTE('#{datastore_name}', 'PROCEDURE', '#{procedure_name}');
|
221
|
+
END;
|
222
|
+
SQL
|
224
223
|
end
|
225
224
|
|
226
225
|
def create_storage_preference(storage_name, tablespace)
|
227
226
|
drop_ctx_preference(storage_name)
|
228
|
-
sql = "BEGIN\nCTX_DDL.CREATE_PREFERENCE('#{storage_name}', 'BASIC_STORAGE');\n"
|
227
|
+
sql = +"BEGIN\nCTX_DDL.CREATE_PREFERENCE('#{storage_name}', 'BASIC_STORAGE');\n"
|
229
228
|
["I_TABLE_CLAUSE", "K_TABLE_CLAUSE", "R_TABLE_CLAUSE",
|
230
229
|
"N_TABLE_CLAUSE", "I_INDEX_CLAUSE", "P_TABLE_CLAUSE"].each do |clause|
|
231
230
|
default_clause = case clause
|
@@ -241,7 +240,7 @@ module ActiveRecord
|
|
241
240
|
|
242
241
|
def create_lexer_preference(lexer_name, lexer_type, options)
|
243
242
|
drop_ctx_preference(lexer_name)
|
244
|
-
sql = "BEGIN\nCTX_DDL.CREATE_PREFERENCE('#{lexer_name}', '#{lexer_type}');\n"
|
243
|
+
sql = +"BEGIN\nCTX_DDL.CREATE_PREFERENCE('#{lexer_name}', '#{lexer_type}');\n"
|
245
244
|
options.each do |key, value|
|
246
245
|
plsql_value = case value
|
247
246
|
when String; "'#{value}'"
|
@@ -258,7 +257,7 @@ module ActiveRecord
|
|
258
257
|
|
259
258
|
def create_wordlist_preference(wordlist_name, wordlist_type, options)
|
260
259
|
drop_ctx_preference(wordlist_name)
|
261
|
-
sql = "BEGIN\nCTX_DDL.CREATE_PREFERENCE('#{wordlist_name}', '#{wordlist_type}');\n"
|
260
|
+
sql = +"BEGIN\nCTX_DDL.CREATE_PREFERENCE('#{wordlist_name}', '#{wordlist_type}');\n"
|
262
261
|
options.each do |key, value|
|
263
262
|
plsql_value = case value
|
264
263
|
when String; "'#{value}'"
|
@@ -281,13 +280,13 @@ module ActiveRecord
|
|
281
280
|
trigger_name = default_index_column_trigger_name(index_name)
|
282
281
|
columns = Array(index_column_source)
|
283
282
|
quoted_column_names = columns.map { |col| quote_column_name(col) }.join(", ")
|
284
|
-
execute
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
283
|
+
execute <<~SQL
|
284
|
+
CREATE OR REPLACE TRIGGER #{quote_table_name(trigger_name)}
|
285
|
+
BEFORE UPDATE OF #{quoted_column_names} ON #{quote_table_name(table_name)} FOR EACH ROW
|
286
|
+
BEGIN
|
287
|
+
:new.#{quote_column_name(index_column)} := '1';
|
288
|
+
END;
|
289
|
+
SQL
|
291
290
|
end
|
292
291
|
|
293
292
|
def drop_index_column_trigger(index_name)
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/deprecation"
|
4
|
+
|
3
5
|
module ActiveRecord
|
4
6
|
module ConnectionAdapters
|
5
7
|
module OracleEnhanced
|
@@ -7,7 +9,7 @@ module ActiveRecord
|
|
7
9
|
# maximum length of Oracle identifiers
|
8
10
|
IDENTIFIER_MAX_LENGTH = 30
|
9
11
|
|
10
|
-
def table_alias_length
|
12
|
+
def table_alias_length # :nodoc:
|
11
13
|
IDENTIFIER_MAX_LENGTH
|
12
14
|
end
|
13
15
|
|
@@ -15,20 +17,13 @@ module ActiveRecord
|
|
15
17
|
def table_name_length
|
16
18
|
IDENTIFIER_MAX_LENGTH
|
17
19
|
end
|
20
|
+
deprecate :table_name_length
|
18
21
|
|
19
22
|
# the maximum length of a column name
|
20
23
|
def column_name_length
|
21
24
|
IDENTIFIER_MAX_LENGTH
|
22
25
|
end
|
23
|
-
|
24
|
-
# Returns the maximum allowed length for an index name. This
|
25
|
-
# limit is enforced by rails and Is less than or equal to
|
26
|
-
# <tt>index_name_length</tt>. The gap between
|
27
|
-
# <tt>index_name_length</tt> is to allow internal rails
|
28
|
-
# opreations to use prefixes in temporary opreations.
|
29
|
-
def allowed_index_name_length
|
30
|
-
index_name_length
|
31
|
-
end
|
26
|
+
deprecate :column_name_length
|
32
27
|
|
33
28
|
# the maximum length of an index name
|
34
29
|
# supported by this database
|
@@ -9,38 +9,39 @@ module ActiveRecord
|
|
9
9
|
# see: abstract/database_statements.rb
|
10
10
|
|
11
11
|
# Executes a SQL statement
|
12
|
-
def execute(sql, name = nil)
|
13
|
-
|
14
|
-
end
|
12
|
+
def execute(sql, name = nil, async: false)
|
13
|
+
sql = transform_query(sql)
|
15
14
|
|
16
|
-
|
17
|
-
@statements.clear
|
18
|
-
reload_type_map
|
15
|
+
log(sql, name, async: async) { @connection.exec(sql) }
|
19
16
|
end
|
20
17
|
|
21
|
-
def exec_query(sql, name = "SQL", binds = [], prepare: false)
|
18
|
+
def exec_query(sql, name = "SQL", binds = [], prepare: false, async: false)
|
19
|
+
sql = transform_query(sql)
|
20
|
+
|
22
21
|
type_casted_binds = type_casted_binds(binds)
|
23
22
|
|
24
|
-
log(sql, name, binds, type_casted_binds) do
|
23
|
+
log(sql, name, binds, type_casted_binds, async: async) do
|
25
24
|
cursor = nil
|
26
25
|
cached = false
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
@statements
|
32
|
-
|
26
|
+
with_retry do
|
27
|
+
if without_prepared_statement?(binds)
|
28
|
+
cursor = @connection.prepare(sql)
|
29
|
+
else
|
30
|
+
unless @statements.key? sql
|
31
|
+
@statements[sql] = @connection.prepare(sql)
|
32
|
+
end
|
33
33
|
|
34
|
-
|
34
|
+
cursor = @statements[sql]
|
35
35
|
|
36
|
-
|
36
|
+
cursor.bind_params(type_casted_binds)
|
37
37
|
|
38
|
-
|
39
|
-
|
38
|
+
cached = true
|
39
|
+
end
|
40
40
|
|
41
|
-
|
41
|
+
cursor.exec
|
42
|
+
end
|
42
43
|
|
43
|
-
if (name == "EXPLAIN") && sql
|
44
|
+
if (name == "EXPLAIN") && sql.start_with?("EXPLAIN")
|
44
45
|
res = true
|
45
46
|
else
|
46
47
|
columns = cursor.get_col_names.map do |col_name|
|
@@ -51,7 +52,7 @@ module ActiveRecord
|
|
51
52
|
while row = cursor.fetch(fetch_options)
|
52
53
|
rows << row
|
53
54
|
end
|
54
|
-
res =
|
55
|
+
res = build_result(columns: columns, rows: rows)
|
55
56
|
end
|
56
57
|
|
57
58
|
cursor.close unless cached
|
@@ -65,7 +66,7 @@ module ActiveRecord
|
|
65
66
|
|
66
67
|
def explain(arel, binds = [])
|
67
68
|
sql = "EXPLAIN PLAN FOR #{to_sql(arel, binds)}"
|
68
|
-
return if
|
69
|
+
return if /FROM all_/.match?(sql)
|
69
70
|
if ORACLE_ENHANCED_CONNECTION == :jdbc
|
70
71
|
exec_query(sql, "EXPLAIN", binds)
|
71
72
|
else
|
@@ -76,8 +77,8 @@ module ActiveRecord
|
|
76
77
|
|
77
78
|
# New method in ActiveRecord 3.1
|
78
79
|
# Will add RETURNING clause in case of trigger generated primary keys
|
79
|
-
def sql_for_insert(sql, pk,
|
80
|
-
unless
|
80
|
+
def sql_for_insert(sql, pk, binds)
|
81
|
+
unless pk == false || pk.nil? || pk.is_a?(Array) || pk.is_a?(String)
|
81
82
|
sql = "#{sql} RETURNING #{quote_column_name(pk)} INTO :returning_id"
|
82
83
|
(binds = binds.dup) << ActiveRecord::Relation::QueryAttribute.new("returning_id", nil, Type::OracleEnhanced::Integer.new)
|
83
84
|
end
|
@@ -91,41 +92,44 @@ module ActiveRecord
|
|
91
92
|
|
92
93
|
# New method in ActiveRecord 3.1
|
93
94
|
def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil)
|
94
|
-
sql, binds = sql_for_insert(sql, pk,
|
95
|
+
sql, binds = sql_for_insert(sql, pk, binds)
|
95
96
|
type_casted_binds = type_casted_binds(binds)
|
96
97
|
|
97
98
|
log(sql, name, binds, type_casted_binds) do
|
98
99
|
cached = false
|
100
|
+
cursor = nil
|
99
101
|
returning_id_col = returning_id_index = nil
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
@statements
|
105
|
-
|
102
|
+
with_retry do
|
103
|
+
if without_prepared_statement?(binds)
|
104
|
+
cursor = @connection.prepare(sql)
|
105
|
+
else
|
106
|
+
unless @statements.key?(sql)
|
107
|
+
@statements[sql] = @connection.prepare(sql)
|
108
|
+
end
|
109
|
+
|
110
|
+
cursor = @statements[sql]
|
106
111
|
|
107
|
-
|
112
|
+
cursor.bind_params(type_casted_binds)
|
108
113
|
|
109
|
-
|
114
|
+
if /:returning_id/.match?(sql)
|
115
|
+
# it currently expects that returning_id comes last part of binds
|
116
|
+
returning_id_index = binds.size
|
117
|
+
cursor.bind_returning_param(returning_id_index, Integer)
|
118
|
+
end
|
110
119
|
|
111
|
-
|
112
|
-
# it currently expects that returning_id comes last part of binds
|
113
|
-
returning_id_index = binds.size
|
114
|
-
cursor.bind_returning_param(returning_id_index, Integer)
|
120
|
+
cached = true
|
115
121
|
end
|
116
122
|
|
117
|
-
|
123
|
+
cursor.exec_update
|
118
124
|
end
|
119
125
|
|
120
|
-
cursor.exec_update
|
121
|
-
|
122
126
|
rows = []
|
123
127
|
if returning_id_index
|
124
128
|
returning_id = cursor.get_returning_param(returning_id_index, Integer).to_i
|
125
129
|
rows << [returning_id]
|
126
130
|
end
|
127
131
|
cursor.close unless cached
|
128
|
-
|
132
|
+
build_result(columns: returning_id_col || [], rows: rows)
|
129
133
|
end
|
130
134
|
end
|
131
135
|
|
@@ -134,30 +138,32 @@ module ActiveRecord
|
|
134
138
|
type_casted_binds = type_casted_binds(binds)
|
135
139
|
|
136
140
|
log(sql, name, binds, type_casted_binds) do
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
if @statements.key?(sql)
|
142
|
-
cursor = @statements[sql]
|
141
|
+
with_retry do
|
142
|
+
cached = false
|
143
|
+
if without_prepared_statement?(binds)
|
144
|
+
cursor = @connection.prepare(sql)
|
143
145
|
else
|
144
|
-
|
145
|
-
|
146
|
+
if @statements.key?(sql)
|
147
|
+
cursor = @statements[sql]
|
148
|
+
else
|
149
|
+
cursor = @statements[sql] = @connection.prepare(sql)
|
150
|
+
end
|
146
151
|
|
147
|
-
|
152
|
+
cursor.bind_params(type_casted_binds)
|
148
153
|
|
149
|
-
|
150
|
-
|
154
|
+
cached = true
|
155
|
+
end
|
151
156
|
|
152
|
-
|
153
|
-
|
154
|
-
|
157
|
+
res = cursor.exec_update
|
158
|
+
cursor.close unless cached
|
159
|
+
res
|
160
|
+
end
|
155
161
|
end
|
156
162
|
end
|
157
163
|
|
158
164
|
alias :exec_delete :exec_update
|
159
165
|
|
160
|
-
def begin_db_transaction
|
166
|
+
def begin_db_transaction # :nodoc:
|
161
167
|
@connection.autocommit = false
|
162
168
|
end
|
163
169
|
|
@@ -176,27 +182,27 @@ module ActiveRecord
|
|
176
182
|
execute "SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}"
|
177
183
|
end
|
178
184
|
|
179
|
-
def commit_db_transaction
|
185
|
+
def commit_db_transaction # :nodoc:
|
180
186
|
@connection.commit
|
181
187
|
ensure
|
182
188
|
@connection.autocommit = true
|
183
189
|
end
|
184
190
|
|
185
|
-
def exec_rollback_db_transaction
|
191
|
+
def exec_rollback_db_transaction # :nodoc:
|
186
192
|
@connection.rollback
|
187
193
|
ensure
|
188
194
|
@connection.autocommit = true
|
189
195
|
end
|
190
196
|
|
191
|
-
def create_savepoint(name = current_savepoint_name)
|
192
|
-
execute("SAVEPOINT #{name}")
|
197
|
+
def create_savepoint(name = current_savepoint_name) # :nodoc:
|
198
|
+
execute("SAVEPOINT #{name}", "TRANSACTION")
|
193
199
|
end
|
194
200
|
|
195
|
-
def exec_rollback_to_savepoint(name = current_savepoint_name)
|
196
|
-
execute("ROLLBACK TO #{name}")
|
201
|
+
def exec_rollback_to_savepoint(name = current_savepoint_name) # :nodoc:
|
202
|
+
execute("ROLLBACK TO #{name}", "TRANSACTION")
|
197
203
|
end
|
198
204
|
|
199
|
-
def release_savepoint(name = current_savepoint_name)
|
205
|
+
def release_savepoint(name = current_savepoint_name) # :nodoc:
|
200
206
|
# there is no RELEASE SAVEPOINT statement in Oracle
|
201
207
|
end
|
202
208
|
|
@@ -207,7 +213,7 @@ module ActiveRecord
|
|
207
213
|
end
|
208
214
|
|
209
215
|
# Inserts the given fixture into the table. Overridden to properly handle lobs.
|
210
|
-
def insert_fixture(fixture, table_name)
|
216
|
+
def insert_fixture(fixture, table_name) # :nodoc:
|
211
217
|
super
|
212
218
|
|
213
219
|
if ActiveRecord::Base.pluralize_table_names
|
@@ -222,17 +228,6 @@ module ActiveRecord
|
|
222
228
|
end
|
223
229
|
end
|
224
230
|
|
225
|
-
# fallback to non bulk fixture insert
|
226
|
-
def insert_fixtures(fixtures, table_name)
|
227
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
228
|
-
`insert_fixtures` is deprecated and will be removed in the next version of Rails.
|
229
|
-
Consider using `insert_fixtures_set` for performance improvement.
|
230
|
-
MSG
|
231
|
-
fixtures.each do |fixture|
|
232
|
-
insert_fixture(fixture, table_name)
|
233
|
-
end
|
234
|
-
end
|
235
|
-
|
236
231
|
def insert_fixtures_set(fixture_set, tables_to_delete = [])
|
237
232
|
disable_referential_integrity do
|
238
233
|
transaction(requires_new: true) do
|
@@ -253,17 +248,17 @@ module ActiveRecord
|
|
253
248
|
end
|
254
249
|
|
255
250
|
# Writes LOB values from attributes for specified columns
|
256
|
-
def write_lobs(table_name, klass, attributes, columns)
|
251
|
+
def write_lobs(table_name, klass, attributes, columns) # :nodoc:
|
257
252
|
id = quote(attributes[klass.primary_key])
|
258
253
|
columns.each do |col|
|
259
254
|
value = attributes[col.name]
|
260
255
|
# changed sequence of next two lines - should check if value is nil before converting to yaml
|
261
|
-
next
|
262
|
-
|
263
|
-
|
264
|
-
|
256
|
+
next unless value
|
257
|
+
value = klass.attribute_types[col.name].serialize(value)
|
258
|
+
# value can be nil after serialization because ActiveRecord serializes [] and {} as nil
|
259
|
+
next unless value
|
265
260
|
uncached do
|
266
|
-
unless lob_record = select_one(sql =
|
261
|
+
unless lob_record = select_one(sql = <<~SQL.squish, "Writable Large Object")
|
267
262
|
SELECT #{quote_column_name(col.name)} FROM #{quote_table_name(table_name)}
|
268
263
|
WHERE #{quote_column_name(klass.primary_key)} = #{id} FOR UPDATE
|
269
264
|
SQL
|
@@ -274,6 +269,16 @@ module ActiveRecord
|
|
274
269
|
end
|
275
270
|
end
|
276
271
|
end
|
272
|
+
|
273
|
+
private
|
274
|
+
def with_retry
|
275
|
+
@connection.with_retry do
|
276
|
+
yield
|
277
|
+
rescue
|
278
|
+
@statements.clear
|
279
|
+
raise
|
280
|
+
end
|
281
|
+
end
|
277
282
|
end
|
278
283
|
end
|
279
284
|
end
|