activerecord-oracle_enhanced-adapter 1.7.7 → 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 +225 -0
- data/README.md +14 -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 +58 -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 +51 -79
- 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 +112 -132
- 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 +292 -539
- 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 +2 -3
- data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +82 -162
- data/spec/active_record/connection_adapters/oracle_enhanced_connection_spec.rb +35 -35
- 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 +270 -365
- 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 +73 -77
- 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 +263 -329
- 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 -83
- 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
|
@@ -2,7 +2,6 @@ module ActiveRecord
|
|
|
2
2
|
module ConnectionAdapters
|
|
3
3
|
module OracleEnhanced
|
|
4
4
|
module ContextIndex
|
|
5
|
-
|
|
6
5
|
# Define full text index with Oracle specific CONTEXT index type
|
|
7
6
|
#
|
|
8
7
|
# Oracle CONTEXT index by default supports full text indexing of one column.
|
|
@@ -75,9 +74,9 @@ module ActiveRecord
|
|
|
75
74
|
def add_context_index(table_name, column_name, options = {})
|
|
76
75
|
self.all_schema_indexes = nil
|
|
77
76
|
column_names = Array(column_name)
|
|
78
|
-
index_name = options[:name] || index_name(table_name, :
|
|
77
|
+
index_name = options[:name] || index_name(table_name, column: options[:index_column] || column_names,
|
|
79
78
|
# CONEXT index name max length is 25
|
|
80
|
-
:
|
|
79
|
+
identifier_max_length: 25)
|
|
81
80
|
|
|
82
81
|
quoted_column_name = quote_column_name(options[:index_column] || column_names.first)
|
|
83
82
|
if options[:index_column_trigger_on]
|
|
@@ -130,10 +129,10 @@ module ActiveRecord
|
|
|
130
129
|
def remove_context_index(table_name, options = {})
|
|
131
130
|
self.all_schema_indexes = nil
|
|
132
131
|
unless Hash === options # if column names passed as argument
|
|
133
|
-
options = {:
|
|
132
|
+
options = { column: Array(options) }
|
|
134
133
|
end
|
|
135
134
|
index_name = options[:name] || index_name(table_name,
|
|
136
|
-
:
|
|
135
|
+
column: options[:index_column] || options[:column], identifier_max_length: 25)
|
|
137
136
|
execute "DROP INDEX #{index_name}"
|
|
138
137
|
drop_ctx_preference(default_datastore_name(index_name))
|
|
139
138
|
drop_ctx_preference(default_storage_name(index_name))
|
|
@@ -144,17 +143,17 @@ module ActiveRecord
|
|
|
144
143
|
|
|
145
144
|
private
|
|
146
145
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
146
|
+
def create_datastore_procedure(table_name, procedure_name, column_names, options)
|
|
147
|
+
quoted_table_name = quote_table_name(table_name)
|
|
148
|
+
select_queries, column_names = column_names.partition { |c| c.to_s =~ /^\s*SELECT\s+/i }
|
|
149
|
+
select_queries = select_queries.map { |s| s.strip.gsub(/\s+/, " ") }
|
|
150
|
+
keys, selected_columns = parse_select_queries(select_queries)
|
|
151
|
+
quoted_column_names = (column_names + keys).map { |col| quote_column_name(col) }
|
|
152
|
+
execute compress_lines(<<-SQL)
|
|
154
153
|
CREATE OR REPLACE PROCEDURE #{quote_table_name(procedure_name)}
|
|
155
154
|
(p_rowid IN ROWID,
|
|
156
155
|
p_clob IN OUT NOCOPY CLOB) IS
|
|
157
|
-
-- add_context_index_parameters #{(column_names+select_queries).inspect}#{!options.empty? ? ', ' << options.inspect[1..-2] : ''}
|
|
156
|
+
-- add_context_index_parameters #{(column_names + select_queries).inspect}#{!options.empty? ? ', ' << options.inspect[1..-2] : ''}
|
|
158
157
|
#{
|
|
159
158
|
selected_columns.map do |cols|
|
|
160
159
|
cols.map do |col|
|
|
@@ -171,172 +170,171 @@ module ActiveRecord
|
|
|
171
170
|
#{
|
|
172
171
|
(column_names.map do |col|
|
|
173
172
|
col = col.to_s
|
|
174
|
-
"DBMS_LOB.WRITEAPPEND(p_clob, #{col.length+2}, '<#{col}>');\n" <<
|
|
173
|
+
"DBMS_LOB.WRITEAPPEND(p_clob, #{col.length + 2}, '<#{col}>');\n" <<
|
|
175
174
|
"IF LENGTH(r1.#{col}) > 0 THEN\n" <<
|
|
176
175
|
"DBMS_LOB.WRITEAPPEND(p_clob, LENGTH(r1.#{col}), r1.#{col});\n" <<
|
|
177
176
|
"END IF;\n" <<
|
|
178
|
-
"DBMS_LOB.WRITEAPPEND(p_clob, #{col.length+3}, '</#{col}>');\n"
|
|
177
|
+
"DBMS_LOB.WRITEAPPEND(p_clob, #{col.length + 3}, '</#{col}>');\n"
|
|
179
178
|
end.join) <<
|
|
180
179
|
(selected_columns.zip(select_queries).map do |cols, query|
|
|
181
180
|
(cols.map do |col|
|
|
182
181
|
"l_#{col} := '';\n"
|
|
183
182
|
end.join) <<
|
|
184
183
|
"FOR r2 IN (\n" <<
|
|
185
|
-
query.gsub(/:(\w+)/,"r1.\\1") << "\n) LOOP\n" <<
|
|
184
|
+
query.gsub(/:(\w+)/, "r1.\\1") << "\n) LOOP\n" <<
|
|
186
185
|
(cols.map do |col|
|
|
187
186
|
"l_#{col} := l_#{col} || r2.#{col} || CHR(10);\n"
|
|
188
187
|
end.join) <<
|
|
189
188
|
"END LOOP;\n" <<
|
|
190
189
|
(cols.map do |col|
|
|
191
190
|
col = col.to_s
|
|
192
|
-
"DBMS_LOB.WRITEAPPEND(p_clob, #{col.length+2}, '<#{col}>');\n" <<
|
|
191
|
+
"DBMS_LOB.WRITEAPPEND(p_clob, #{col.length + 2}, '<#{col}>');\n" <<
|
|
193
192
|
"IF LENGTH(l_#{col}) > 0 THEN\n" <<
|
|
194
193
|
"DBMS_LOB.WRITEAPPEND(p_clob, LENGTH(l_#{col}), l_#{col});\n" <<
|
|
195
194
|
"END IF;\n" <<
|
|
196
|
-
"DBMS_LOB.WRITEAPPEND(p_clob, #{col.length+3}, '</#{col}>');\n"
|
|
195
|
+
"DBMS_LOB.WRITEAPPEND(p_clob, #{col.length + 3}, '</#{col}>');\n"
|
|
197
196
|
end.join)
|
|
198
197
|
end.join)
|
|
199
198
|
}
|
|
200
199
|
END LOOP;
|
|
201
200
|
END;
|
|
202
201
|
SQL
|
|
203
|
-
|
|
202
|
+
end
|
|
204
203
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
204
|
+
def parse_select_queries(select_queries)
|
|
205
|
+
keys = []
|
|
206
|
+
selected_columns = []
|
|
207
|
+
select_queries.each do |query|
|
|
208
|
+
# get primary or foreign keys like :id or :something_id
|
|
209
|
+
keys << (query.scan(/:\w+/).map { |k| k[1..-1].downcase.to_sym })
|
|
210
|
+
select_part = query.scan(/^select\s.*\sfrom/i).first
|
|
211
|
+
selected_columns << select_part.scan(/\sas\s+(\w+)/i).map { |c| c.first }
|
|
212
|
+
end
|
|
213
|
+
[keys.flatten.uniq, selected_columns]
|
|
213
214
|
end
|
|
214
|
-
[keys.flatten.uniq, selected_columns]
|
|
215
|
-
end
|
|
216
215
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
216
|
+
def create_datastore_preference(datastore_name, procedure_name)
|
|
217
|
+
drop_ctx_preference(datastore_name)
|
|
218
|
+
execute <<-SQL
|
|
220
219
|
BEGIN
|
|
221
220
|
CTX_DDL.CREATE_PREFERENCE('#{datastore_name}', 'USER_DATASTORE');
|
|
222
221
|
CTX_DDL.SET_ATTRIBUTE('#{datastore_name}', 'PROCEDURE', '#{procedure_name}');
|
|
223
222
|
END;
|
|
224
223
|
SQL
|
|
225
|
-
|
|
224
|
+
end
|
|
226
225
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
226
|
+
def create_storage_preference(storage_name, tablespace)
|
|
227
|
+
drop_ctx_preference(storage_name)
|
|
228
|
+
sql = "BEGIN\nCTX_DDL.CREATE_PREFERENCE('#{storage_name}', 'BASIC_STORAGE');\n"
|
|
229
|
+
["I_TABLE_CLAUSE", "K_TABLE_CLAUSE", "R_TABLE_CLAUSE",
|
|
230
|
+
"N_TABLE_CLAUSE", "I_INDEX_CLAUSE", "P_TABLE_CLAUSE"].each do |clause|
|
|
231
|
+
default_clause = case clause
|
|
232
|
+
when "R_TABLE_CLAUSE"; "LOB(DATA) STORE AS (CACHE) "
|
|
233
|
+
when "I_INDEX_CLAUSE"; "COMPRESS 2 "
|
|
234
|
+
else ""
|
|
235
|
+
end
|
|
236
|
+
sql << "CTX_DDL.SET_ATTRIBUTE('#{storage_name}', '#{clause}', '#{default_clause}TABLESPACE #{tablespace}');\n"
|
|
236
237
|
end
|
|
237
|
-
sql << "
|
|
238
|
+
sql << "END;\n"
|
|
239
|
+
execute sql
|
|
238
240
|
end
|
|
239
|
-
sql << "END;\n"
|
|
240
|
-
execute sql
|
|
241
|
-
end
|
|
242
241
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
242
|
+
def create_lexer_preference(lexer_name, lexer_type, options)
|
|
243
|
+
drop_ctx_preference(lexer_name)
|
|
244
|
+
sql = "BEGIN\nCTX_DDL.CREATE_PREFERENCE('#{lexer_name}', '#{lexer_type}');\n"
|
|
245
|
+
options.each do |key, value|
|
|
246
|
+
plsql_value = case value
|
|
247
|
+
when String; "'#{value}'"
|
|
248
|
+
when true; "'YES'"
|
|
249
|
+
when false; "'NO'"
|
|
250
|
+
when nil; "NULL"
|
|
251
|
+
else value
|
|
252
|
+
end
|
|
253
|
+
sql << "CTX_DDL.SET_ATTRIBUTE('#{lexer_name}', '#{key}', #{plsql_value});\n"
|
|
253
254
|
end
|
|
254
|
-
sql << "
|
|
255
|
+
sql << "END;\n"
|
|
256
|
+
execute sql
|
|
255
257
|
end
|
|
256
|
-
sql << "END;\n"
|
|
257
|
-
execute sql
|
|
258
|
-
end
|
|
259
258
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
259
|
+
def create_wordlist_preference(wordlist_name, wordlist_type, options)
|
|
260
|
+
drop_ctx_preference(wordlist_name)
|
|
261
|
+
sql = "BEGIN\nCTX_DDL.CREATE_PREFERENCE('#{wordlist_name}', '#{wordlist_type}');\n"
|
|
262
|
+
options.each do |key, value|
|
|
263
|
+
plsql_value = case value
|
|
264
|
+
when String; "'#{value}'"
|
|
265
|
+
when true; "'YES'"
|
|
266
|
+
when false; "'NO'"
|
|
267
|
+
when nil; "NULL"
|
|
268
|
+
else value
|
|
269
|
+
end
|
|
270
|
+
sql << "CTX_DDL.SET_ATTRIBUTE('#{wordlist_name}', '#{key}', #{plsql_value});\n"
|
|
270
271
|
end
|
|
271
|
-
sql << "
|
|
272
|
+
sql << "END;\n"
|
|
273
|
+
execute sql
|
|
272
274
|
end
|
|
273
|
-
sql << "END;\n"
|
|
274
|
-
execute sql
|
|
275
|
-
end
|
|
276
275
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
276
|
+
def drop_ctx_preference(preference_name)
|
|
277
|
+
execute "BEGIN CTX_DDL.DROP_PREFERENCE('#{preference_name}'); END;" rescue nil
|
|
278
|
+
end
|
|
280
279
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
280
|
+
def create_index_column_trigger(table_name, index_name, index_column, index_column_source)
|
|
281
|
+
trigger_name = default_index_column_trigger_name(index_name)
|
|
282
|
+
columns = Array(index_column_source)
|
|
283
|
+
quoted_column_names = columns.map { |col| quote_column_name(col) }.join(", ")
|
|
284
|
+
execute compress_lines(<<-SQL)
|
|
286
285
|
CREATE OR REPLACE TRIGGER #{quote_table_name(trigger_name)}
|
|
287
286
|
BEFORE UPDATE OF #{quoted_column_names} ON #{quote_table_name(table_name)} FOR EACH ROW
|
|
288
287
|
BEGIN
|
|
289
288
|
:new.#{quote_column_name(index_column)} := '1';
|
|
290
289
|
END;
|
|
291
290
|
SQL
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
def drop_index_column_trigger(index_name)
|
|
295
|
-
trigger_name = default_index_column_trigger_name(index_name)
|
|
296
|
-
execute "DROP TRIGGER #{quote_table_name(trigger_name)}" rescue nil
|
|
297
|
-
end
|
|
291
|
+
end
|
|
298
292
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
293
|
+
def drop_index_column_trigger(index_name)
|
|
294
|
+
trigger_name = default_index_column_trigger_name(index_name)
|
|
295
|
+
execute "DROP TRIGGER #{quote_table_name(trigger_name)}" rescue nil
|
|
296
|
+
end
|
|
302
297
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
298
|
+
def default_datastore_procedure(index_name)
|
|
299
|
+
"#{index_name}_prc"
|
|
300
|
+
end
|
|
306
301
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
302
|
+
def default_datastore_name(index_name)
|
|
303
|
+
"#{index_name}_dst"
|
|
304
|
+
end
|
|
310
305
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
306
|
+
def default_storage_name(index_name)
|
|
307
|
+
"#{index_name}_sto"
|
|
308
|
+
end
|
|
314
309
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
310
|
+
def default_index_column_trigger_name(index_name)
|
|
311
|
+
"#{index_name}_trg"
|
|
312
|
+
end
|
|
318
313
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
314
|
+
def default_lexer_name(index_name)
|
|
315
|
+
"#{index_name}_lex"
|
|
316
|
+
end
|
|
322
317
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
# As a result <tt>contains</tt> class scope method is defined.
|
|
326
|
-
def has_context_index
|
|
327
|
-
extend ContextIndexClassMethods
|
|
318
|
+
def default_wordlist_name(index_name)
|
|
319
|
+
"#{index_name}_wl"
|
|
328
320
|
end
|
|
329
|
-
end
|
|
330
321
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
322
|
+
module BaseClassMethods
|
|
323
|
+
# Declare that model table has context index defined.
|
|
324
|
+
# As a result <tt>contains</tt> class scope method is defined.
|
|
325
|
+
def has_context_index
|
|
326
|
+
extend ContextIndexClassMethods
|
|
327
|
+
end
|
|
337
328
|
end
|
|
338
|
-
end
|
|
339
329
|
|
|
330
|
+
module ContextIndexClassMethods
|
|
331
|
+
# Add context index condition.
|
|
332
|
+
def contains(column, query, options = {})
|
|
333
|
+
score_label = options[:label].to_i || 1
|
|
334
|
+
where("CONTAINS(#{connection.quote_table_name(column)}, ?, #{score_label}) > 0", query).
|
|
335
|
+
order("SCORE(#{score_label}) DESC")
|
|
336
|
+
end
|
|
337
|
+
end
|
|
340
338
|
end
|
|
341
339
|
end
|
|
342
340
|
end
|
|
@@ -16,10 +16,10 @@ module ActiveRecord
|
|
|
16
16
|
reload_type_map
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
def exec_query(sql, name =
|
|
20
|
-
type_casted_binds = binds
|
|
19
|
+
def exec_query(sql, name = "SQL", binds = [], prepare: false)
|
|
20
|
+
type_casted_binds = type_casted_binds(binds)
|
|
21
21
|
|
|
22
|
-
log(sql, name, binds) do
|
|
22
|
+
log(sql, name, binds, type_casted_binds) do
|
|
23
23
|
cursor = nil
|
|
24
24
|
cached = false
|
|
25
25
|
if without_prepared_statement?(binds)
|
|
@@ -38,14 +38,14 @@ module ActiveRecord
|
|
|
38
38
|
|
|
39
39
|
cursor.exec
|
|
40
40
|
|
|
41
|
-
if name ==
|
|
41
|
+
if (name == "EXPLAIN") && sql =~ /^EXPLAIN/
|
|
42
42
|
res = true
|
|
43
43
|
else
|
|
44
44
|
columns = cursor.get_col_names.map do |col_name|
|
|
45
45
|
@connection.oracle_downcase(col_name)
|
|
46
46
|
end
|
|
47
47
|
rows = []
|
|
48
|
-
fetch_options = {:
|
|
48
|
+
fetch_options = { get_lob_value: (name != "Writable Large Object") }
|
|
49
49
|
while row = cursor.fetch(fetch_options)
|
|
50
50
|
rows << row
|
|
51
51
|
end
|
|
@@ -69,34 +69,34 @@ module ActiveRecord
|
|
|
69
69
|
sql = "EXPLAIN PLAN FOR #{to_sql(arel, binds)}"
|
|
70
70
|
return if sql =~ /FROM all_/
|
|
71
71
|
if ORACLE_ENHANCED_CONNECTION == :jdbc
|
|
72
|
-
exec_query(sql,
|
|
72
|
+
exec_query(sql, "EXPLAIN", binds)
|
|
73
73
|
else
|
|
74
|
-
exec_query(sql,
|
|
74
|
+
exec_query(sql, "EXPLAIN")
|
|
75
75
|
end
|
|
76
|
-
select_values("SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY)",
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
# Returns an array of arrays containing the field values.
|
|
80
|
-
# Order is the same as that returned by #columns.
|
|
81
|
-
def select_rows(sql, name = nil, binds = [])
|
|
82
|
-
exec_query(sql, name, binds).rows
|
|
76
|
+
select_values("SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY)", "EXPLAIN").join("\n")
|
|
83
77
|
end
|
|
84
78
|
|
|
85
79
|
# New method in ActiveRecord 3.1
|
|
86
80
|
# Will add RETURNING clause in case of trigger generated primary keys
|
|
87
81
|
def sql_for_insert(sql, pk, id_value, sequence_name, binds)
|
|
88
|
-
unless id_value || pk == false || pk.nil? ||
|
|
82
|
+
unless id_value || pk == false || pk.nil? || pk.is_a?(Array)
|
|
89
83
|
sql = "#{sql} RETURNING #{quote_column_name(pk)} INTO :returning_id"
|
|
90
84
|
(binds = binds.dup) << ActiveRecord::Relation::QueryAttribute.new("returning_id", nil, ActiveRecord::OracleEnhanced::Type::Integer.new)
|
|
91
85
|
end
|
|
92
86
|
super
|
|
93
87
|
end
|
|
94
88
|
|
|
89
|
+
def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
|
|
90
|
+
pk = nil if id_value
|
|
91
|
+
super
|
|
92
|
+
end
|
|
93
|
+
|
|
95
94
|
# New method in ActiveRecord 3.1
|
|
96
|
-
def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
|
|
97
|
-
|
|
95
|
+
def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil)
|
|
96
|
+
sql, binds = sql_for_insert(sql, pk, nil, sequence_name, binds)
|
|
97
|
+
type_casted_binds = type_casted_binds(binds)
|
|
98
98
|
|
|
99
|
-
log(sql, name, binds) do
|
|
99
|
+
log(sql, name, binds, type_casted_binds) do
|
|
100
100
|
returning_id_col = returning_id_index = nil
|
|
101
101
|
if without_prepared_statement?(binds)
|
|
102
102
|
cursor = @connection.prepare(sql)
|
|
@@ -129,19 +129,19 @@ module ActiveRecord
|
|
|
129
129
|
end
|
|
130
130
|
|
|
131
131
|
# New method in ActiveRecord 3.1
|
|
132
|
-
def exec_update(sql, name, binds)
|
|
133
|
-
type_casted_binds = binds
|
|
132
|
+
def exec_update(sql, name = nil, binds = [])
|
|
133
|
+
type_casted_binds = type_casted_binds(binds)
|
|
134
134
|
|
|
135
|
-
log(sql, name, binds) do
|
|
135
|
+
log(sql, name, binds, type_casted_binds) do
|
|
136
136
|
cached = false
|
|
137
137
|
if without_prepared_statement?(binds)
|
|
138
138
|
cursor = @connection.prepare(sql)
|
|
139
139
|
else
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
140
|
+
if @statements.key?(sql)
|
|
141
|
+
cursor = @statements[sql]
|
|
142
|
+
else
|
|
143
|
+
cursor = @statements[sql] = @connection.prepare(sql)
|
|
144
|
+
end
|
|
145
145
|
|
|
146
146
|
cursor.bind_params(type_casted_binds)
|
|
147
147
|
|
|
@@ -202,7 +202,7 @@ module ActiveRecord
|
|
|
202
202
|
# Returns default sequence name for table.
|
|
203
203
|
# Will take all or first 26 characters of table name and append _seq suffix
|
|
204
204
|
def default_sequence_name(table_name, primary_key = nil)
|
|
205
|
-
table_name.to_s.gsub((/(^|\.)([\w$-]{1,#{sequence_name_length-4}})([\w$-]*)$/), '\1\2_seq')
|
|
205
|
+
table_name.to_s.gsub((/(^|\.)([\w$-]{1,#{sequence_name_length - 4}})([\w$-]*)$/), '\1\2_seq')
|
|
206
206
|
end
|
|
207
207
|
|
|
208
208
|
# Inserts the given fixture into the table. Overridden to properly handle lobs.
|
|
@@ -221,6 +221,12 @@ module ActiveRecord
|
|
|
221
221
|
end
|
|
222
222
|
end
|
|
223
223
|
|
|
224
|
+
# Oracle Database does not support this feature
|
|
225
|
+
# Refer https://community.oracle.com/ideas/13845 and consider to vote
|
|
226
|
+
# if you need this feature.
|
|
227
|
+
def empty_insert_statement_value
|
|
228
|
+
raise NotImplementedError
|
|
229
|
+
end
|
|
224
230
|
end
|
|
225
231
|
end
|
|
226
232
|
end
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
require
|
|
1
|
+
require "active_record/base"
|
|
2
2
|
|
|
3
3
|
module ActiveRecord
|
|
4
4
|
module ConnectionAdapters
|
|
5
5
|
class OracleEnhancedAdapter
|
|
6
6
|
class DatabaseTasks
|
|
7
|
-
delegate :connection, :establish_connection, :
|
|
7
|
+
delegate :connection, :establish_connection, to: ActiveRecord::Base
|
|
8
8
|
|
|
9
9
|
def initialize(config)
|
|
10
10
|
@config = config
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def create
|
|
14
|
-
system_password = ENV.fetch(
|
|
14
|
+
system_password = ENV.fetch("ORACLE_SYSTEM_PASSWORD") {
|
|
15
15
|
print "Please provide the SYSTEM password for your Oracle installation (set ORACLE_SYSTEM_PASSWORD to avoid this prompt)\n>"
|
|
16
16
|
$stdin.gets.strip
|
|
17
17
|
}
|
|
18
|
-
establish_connection(@config.merge(
|
|
18
|
+
establish_connection(@config.merge("username" => "SYSTEM", "password" => system_password))
|
|
19
19
|
begin
|
|
20
20
|
connection.execute "CREATE USER #{@config['username']} IDENTIFIED BY #{@config['password']}"
|
|
21
21
|
rescue => e
|
|
@@ -39,18 +39,18 @@ module ActiveRecord
|
|
|
39
39
|
|
|
40
40
|
def purge
|
|
41
41
|
drop
|
|
42
|
-
connection.execute(
|
|
42
|
+
connection.execute("PURGE RECYCLEBIN") rescue nil
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
-
def structure_dump(filename)
|
|
45
|
+
def structure_dump(filename, extra_flags)
|
|
46
46
|
establish_connection(@config)
|
|
47
|
-
File.open(filename,
|
|
48
|
-
if @config[
|
|
49
|
-
|
|
47
|
+
File.open(filename, "w:utf-8") { |f| f << connection.structure_dump }
|
|
48
|
+
if @config["structure_dump"] == "db_stored_code"
|
|
49
|
+
File.open(filename, "a") { |f| f << connection.structure_dump_db_stored_code }
|
|
50
50
|
end
|
|
51
51
|
end
|
|
52
52
|
|
|
53
|
-
def structure_load(filename)
|
|
53
|
+
def structure_load(filename, extra_flags)
|
|
54
54
|
establish_connection(@config)
|
|
55
55
|
connection.execute_structure_dump(File.read(filename))
|
|
56
56
|
end
|