activerecord-oracle_enhanced-adapter 1.4.3 → 5.2.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 +5 -5
- data/History.md +1162 -2
- data/README.md +567 -155
- data/VERSION +1 -1
- data/lib/active_record/connection_adapters/emulation/oracle_adapter.rb +3 -1
- data/lib/active_record/connection_adapters/oracle_enhanced/column.rb +19 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/connection.rb +132 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/context_index.rb +345 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/database_limits.rb +52 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb +280 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/database_tasks.rb +64 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/dbms_output.rb +59 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/jdbc_connection.rb +538 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/jdbc_quoting.rb +38 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/lob.rb +46 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/oci_connection.rb +435 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/oci_quoting.rb +44 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/procedures.rb +196 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/quoting.rb +164 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/schema_creation.rb +95 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/schema_definitions.rb +79 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/schema_dumper.rb +194 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb +709 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements_ext.rb +28 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/structure_dump.rb +353 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/type_metadata.rb +33 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/version.rb +3 -0
- data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +385 -1083
- data/lib/active_record/type/oracle_enhanced/boolean.rb +20 -0
- data/lib/active_record/type/oracle_enhanced/integer.rb +15 -0
- data/lib/active_record/type/oracle_enhanced/json.rb +10 -0
- data/lib/active_record/type/oracle_enhanced/national_character_string.rb +26 -0
- data/lib/active_record/type/oracle_enhanced/national_character_text.rb +36 -0
- data/lib/active_record/type/oracle_enhanced/raw.rb +25 -0
- data/lib/active_record/type/oracle_enhanced/string.rb +29 -0
- data/lib/active_record/type/oracle_enhanced/text.rb +32 -0
- data/lib/active_record/type/oracle_enhanced/timestampltz.rb +25 -0
- data/lib/active_record/type/oracle_enhanced/timestamptz.rb +25 -0
- data/lib/activerecord-oracle_enhanced-adapter.rb +5 -13
- data/spec/active_record/connection_adapters/{oracle_enhanced_emulate_oracle_adapter_spec.rb → emulation/oracle_adapter_spec.rb} +5 -4
- data/spec/active_record/connection_adapters/oracle_enhanced/connection_spec.rb +469 -0
- data/spec/active_record/connection_adapters/{oracle_enhanced_context_index_spec.rb → oracle_enhanced/context_index_spec.rb} +140 -128
- data/spec/active_record/connection_adapters/oracle_enhanced/database_tasks_spec.rb +112 -0
- data/spec/active_record/connection_adapters/{oracle_enhanced_dbms_output_spec.rb → oracle_enhanced/dbms_output_spec.rb} +13 -13
- data/spec/active_record/connection_adapters/oracle_enhanced/procedures_spec.rb +365 -0
- data/spec/active_record/connection_adapters/oracle_enhanced/quoting_spec.rb +196 -0
- data/spec/active_record/connection_adapters/oracle_enhanced/schema_dumper_spec.rb +492 -0
- data/spec/active_record/connection_adapters/oracle_enhanced/schema_statements_spec.rb +1433 -0
- data/spec/active_record/connection_adapters/oracle_enhanced/structure_dump_spec.rb +478 -0
- data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +385 -550
- data/spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb +92 -1249
- data/spec/active_record/oracle_enhanced/type/binary_spec.rb +119 -0
- data/spec/active_record/oracle_enhanced/type/boolean_spec.rb +208 -0
- data/spec/active_record/oracle_enhanced/type/dirty_spec.rb +139 -0
- data/spec/active_record/oracle_enhanced/type/float_spec.rb +48 -0
- data/spec/active_record/oracle_enhanced/type/integer_spec.rb +91 -0
- data/spec/active_record/oracle_enhanced/type/json_spec.rb +57 -0
- data/spec/active_record/oracle_enhanced/type/national_character_string_spec.rb +55 -0
- data/spec/active_record/oracle_enhanced/type/national_character_text_spec.rb +230 -0
- data/spec/active_record/oracle_enhanced/type/raw_spec.rb +122 -0
- data/spec/active_record/oracle_enhanced/type/text_spec.rb +229 -0
- data/spec/active_record/oracle_enhanced/type/timestamp_spec.rb +75 -0
- data/spec/spec_config.yaml.template +11 -0
- data/spec/spec_helper.rb +100 -93
- data/spec/support/alter_system_set_open_cursors.sql +1 -0
- data/spec/support/alter_system_user_password.sql +2 -0
- data/spec/support/create_oracle_enhanced_users.sql +31 -0
- metadata +105 -152
- data/.rspec +0 -2
- data/Gemfile +0 -52
- data/RUNNING_TESTS.md +0 -45
- data/Rakefile +0 -59
- data/activerecord-oracle_enhanced-adapter.gemspec +0 -130
- data/lib/active_record/connection_adapters/oracle_enhanced.rake +0 -105
- data/lib/active_record/connection_adapters/oracle_enhanced_activerecord_patches.rb +0 -41
- data/lib/active_record/connection_adapters/oracle_enhanced_base_ext.rb +0 -121
- data/lib/active_record/connection_adapters/oracle_enhanced_column.rb +0 -151
- data/lib/active_record/connection_adapters/oracle_enhanced_connection.rb +0 -119
- data/lib/active_record/connection_adapters/oracle_enhanced_context_index.rb +0 -359
- data/lib/active_record/connection_adapters/oracle_enhanced_core_ext.rb +0 -25
- data/lib/active_record/connection_adapters/oracle_enhanced_cpk.rb +0 -21
- data/lib/active_record/connection_adapters/oracle_enhanced_dirty.rb +0 -46
- data/lib/active_record/connection_adapters/oracle_enhanced_jdbc_connection.rb +0 -572
- data/lib/active_record/connection_adapters/oracle_enhanced_oci_connection.rb +0 -497
- data/lib/active_record/connection_adapters/oracle_enhanced_procedures.rb +0 -260
- data/lib/active_record/connection_adapters/oracle_enhanced_schema_definitions.rb +0 -227
- data/lib/active_record/connection_adapters/oracle_enhanced_schema_dumper.rb +0 -260
- data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements.rb +0 -428
- data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements_ext.rb +0 -258
- data/lib/active_record/connection_adapters/oracle_enhanced_structure_dump.rb +0 -294
- data/lib/active_record/connection_adapters/oracle_enhanced_tasks.rb +0 -17
- data/lib/active_record/connection_adapters/oracle_enhanced_version.rb +0 -1
- data/spec/active_record/connection_adapters/oracle_enhanced_connection_spec.rb +0 -334
- data/spec/active_record/connection_adapters/oracle_enhanced_core_ext_spec.rb +0 -19
- data/spec/active_record/connection_adapters/oracle_enhanced_cpk_spec.rb +0 -113
- data/spec/active_record/connection_adapters/oracle_enhanced_dirty_spec.rb +0 -141
- data/spec/active_record/connection_adapters/oracle_enhanced_procedures_spec.rb +0 -378
- data/spec/active_record/connection_adapters/oracle_enhanced_schema_dump_spec.rb +0 -440
- data/spec/active_record/connection_adapters/oracle_enhanced_schema_statements_spec.rb +0 -1400
- data/spec/active_record/connection_adapters/oracle_enhanced_structure_dump_spec.rb +0 -339
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module ConnectionAdapters
|
|
5
|
+
module OracleEnhanced
|
|
6
|
+
module SchemaStatementsExt
|
|
7
|
+
# Create primary key trigger (so that you can skip primary key value in INSERT statement).
|
|
8
|
+
# By default trigger name will be "table_name_pkt", you can override the name with
|
|
9
|
+
# :trigger_name option (but it is not recommended to override it as then this trigger will
|
|
10
|
+
# not be detected by ActiveRecord model and it will still do prefetching of sequence value).
|
|
11
|
+
#
|
|
12
|
+
# add_primary_key_trigger :users
|
|
13
|
+
#
|
|
14
|
+
# You can also create primary key trigger using +create_table+ with :primary_key_trigger
|
|
15
|
+
# option:
|
|
16
|
+
#
|
|
17
|
+
# create_table :users, :primary_key_trigger => true do |t|
|
|
18
|
+
# # ...
|
|
19
|
+
# end
|
|
20
|
+
#
|
|
21
|
+
def add_primary_key_trigger(table_name, options = {})
|
|
22
|
+
# call the same private method that is used for create_table :primary_key_trigger => true
|
|
23
|
+
create_primary_key_trigger(table_name, options)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord #:nodoc:
|
|
4
|
+
module ConnectionAdapters #:nodoc:
|
|
5
|
+
module OracleEnhanced #:nodoc:
|
|
6
|
+
module StructureDump #:nodoc:
|
|
7
|
+
# Statements separator used in structure dump to allow loading of structure dump also with SQL*Plus
|
|
8
|
+
STATEMENT_TOKEN = "\n\n/\n\n"
|
|
9
|
+
|
|
10
|
+
def structure_dump #:nodoc:
|
|
11
|
+
sequences = select(<<-SQL.strip.gsub(/\s+/, " "), "sequences to dump at structure dump")
|
|
12
|
+
SELECT sequence_name, min_value, max_value, increment_by, order_flag, cycle_flag
|
|
13
|
+
FROM all_sequences
|
|
14
|
+
where sequence_owner = SYS_CONTEXT('userenv', 'session_user') ORDER BY 1
|
|
15
|
+
SQL
|
|
16
|
+
|
|
17
|
+
structure = sequences.map do |result|
|
|
18
|
+
"CREATE SEQUENCE #{quote_table_name(result["sequence_name"])} MINVALUE #{result["min_value"]} MAXVALUE #{result["max_value"]} INCREMENT BY #{result["increment_by"]} #{result["order_flag"] == 'Y' ? "ORDER" : "NOORDER"} #{result["cycle_flag"] == 'Y' ? "CYCLE" : "NOCYCLE"}"
|
|
19
|
+
end
|
|
20
|
+
tables = select_values(<<-SQL.strip.gsub(/\s+/, " "), "tables at structure dump")
|
|
21
|
+
SELECT table_name FROM all_tables t
|
|
22
|
+
WHERE owner = SYS_CONTEXT('userenv', 'current_schema') AND secondary = 'N'
|
|
23
|
+
AND NOT EXISTS (SELECT mv.mview_name FROM all_mviews mv
|
|
24
|
+
WHERE mv.owner = t.owner AND mv.mview_name = t.table_name)
|
|
25
|
+
AND NOT EXISTS (SELECT mvl.log_table FROM all_mview_logs mvl
|
|
26
|
+
WHERE mvl.log_owner = t.owner AND mvl.log_table = t.table_name)
|
|
27
|
+
ORDER BY 1
|
|
28
|
+
SQL
|
|
29
|
+
tables.each do |table_name|
|
|
30
|
+
virtual_columns = virtual_columns_for(table_name) if supports_virtual_columns?
|
|
31
|
+
ddl = "CREATE#{ ' GLOBAL TEMPORARY' if temporary_table?(table_name)} TABLE \"#{table_name}\" (\n".dup
|
|
32
|
+
columns = select_all(<<-SQL.strip.gsub(/\s+/, " "), "columns at structure dump")
|
|
33
|
+
SELECT column_name, data_type, data_length, char_used, char_length,
|
|
34
|
+
data_precision, data_scale, data_default, nullable
|
|
35
|
+
FROM all_tab_columns
|
|
36
|
+
WHERE table_name = '#{table_name}'
|
|
37
|
+
AND owner = SYS_CONTEXT('userenv', 'session_user')
|
|
38
|
+
ORDER BY column_id
|
|
39
|
+
SQL
|
|
40
|
+
cols = columns.map do |row|
|
|
41
|
+
if (v = virtual_columns.find { |col| col["column_name"] == row["column_name"] })
|
|
42
|
+
structure_dump_virtual_column(row, v["data_default"])
|
|
43
|
+
else
|
|
44
|
+
structure_dump_column(row)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
ddl << cols.map { |col| " #{col}" }.join(",\n")
|
|
48
|
+
ddl << structure_dump_primary_key(table_name)
|
|
49
|
+
ddl << "\n)"
|
|
50
|
+
structure << ddl
|
|
51
|
+
structure << structure_dump_indexes(table_name)
|
|
52
|
+
structure << structure_dump_unique_keys(table_name)
|
|
53
|
+
structure << structure_dump_table_comments(table_name)
|
|
54
|
+
structure << structure_dump_column_comments(table_name)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
join_with_statement_token(structure) << structure_dump_fk_constraints
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def structure_dump_column(column) #:nodoc:
|
|
61
|
+
col = "\"#{column['column_name']}\" #{column['data_type']}".dup
|
|
62
|
+
if (column["data_type"] == "NUMBER") && !column["data_precision"].nil?
|
|
63
|
+
col << "(#{column['data_precision'].to_i}"
|
|
64
|
+
col << ",#{column['data_scale'].to_i}" if !column["data_scale"].nil?
|
|
65
|
+
col << ")"
|
|
66
|
+
elsif column["data_type"].include?("CHAR") || column["data_type"] == "RAW"
|
|
67
|
+
length = column["char_used"] == "C" ? column["char_length"].to_i : column["data_length"].to_i
|
|
68
|
+
col << "(#{length})"
|
|
69
|
+
end
|
|
70
|
+
col << " DEFAULT #{column['data_default']}" if !column["data_default"].nil?
|
|
71
|
+
col << " NOT NULL" if column["nullable"] == "N"
|
|
72
|
+
col
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def structure_dump_virtual_column(column, data_default) #:nodoc:
|
|
76
|
+
data_default = data_default.gsub(/"/, "")
|
|
77
|
+
col = "\"#{column['column_name']}\" #{column['data_type']}".dup
|
|
78
|
+
if (column["data_type"] == "NUMBER") && !column["data_precision"].nil?
|
|
79
|
+
col << "(#{column['data_precision'].to_i}"
|
|
80
|
+
col << ",#{column['data_scale'].to_i}" if !column["data_scale"].nil?
|
|
81
|
+
col << ")"
|
|
82
|
+
elsif column["data_type"].include?("CHAR") || column["data_type"] == "RAW"
|
|
83
|
+
length = column["char_used"] == "C" ? column["char_length"].to_i : column["data_length"].to_i
|
|
84
|
+
col << "(#{length})"
|
|
85
|
+
end
|
|
86
|
+
col << " GENERATED ALWAYS AS (#{data_default}) VIRTUAL"
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def structure_dump_primary_key(table) #:nodoc:
|
|
90
|
+
opts = { name: "", cols: [] }
|
|
91
|
+
pks = select_all(<<-SQL.strip.gsub(/\s+/, " "), "Primary Keys")
|
|
92
|
+
SELECT a.constraint_name, a.column_name, a.position
|
|
93
|
+
FROM all_cons_columns a
|
|
94
|
+
JOIN all_constraints c
|
|
95
|
+
ON a.constraint_name = c.constraint_name
|
|
96
|
+
WHERE c.table_name = '#{table.upcase}'
|
|
97
|
+
AND c.constraint_type = 'P'
|
|
98
|
+
AND a.owner = c.owner
|
|
99
|
+
AND c.owner = SYS_CONTEXT('userenv', 'current_schema')
|
|
100
|
+
SQL
|
|
101
|
+
pks.each do |row|
|
|
102
|
+
opts[:name] = row["constraint_name"]
|
|
103
|
+
opts[:cols][row["position"] - 1] = row["column_name"]
|
|
104
|
+
end
|
|
105
|
+
opts[:cols].length > 0 ? ",\n CONSTRAINT #{opts[:name]} PRIMARY KEY (#{opts[:cols].join(',')})" : ""
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def structure_dump_unique_keys(table) #:nodoc:
|
|
109
|
+
keys = {}
|
|
110
|
+
uks = select_all(<<-SQL.strip.gsub(/\s+/, " "), "Primary Keys")
|
|
111
|
+
SELECT a.constraint_name, a.column_name, a.position
|
|
112
|
+
FROM all_cons_columns a
|
|
113
|
+
JOIN all_constraints c
|
|
114
|
+
ON a.constraint_name = c.constraint_name
|
|
115
|
+
WHERE c.table_name = '#{table.upcase}'
|
|
116
|
+
AND c.constraint_type = 'U'
|
|
117
|
+
AND a.owner = c.owner
|
|
118
|
+
AND c.owner = SYS_CONTEXT('userenv', 'current_schema')
|
|
119
|
+
SQL
|
|
120
|
+
uks.each do |uk|
|
|
121
|
+
keys[uk["constraint_name"]] ||= []
|
|
122
|
+
keys[uk["constraint_name"]][uk["position"] - 1] = uk["column_name"]
|
|
123
|
+
end
|
|
124
|
+
keys.map do |k, v|
|
|
125
|
+
"ALTER TABLE #{table.upcase} ADD CONSTRAINT #{k} UNIQUE (#{v.join(',')})"
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def structure_dump_indexes(table_name) #:nodoc:
|
|
130
|
+
indexes(table_name).map do |options|
|
|
131
|
+
column_names = options.columns
|
|
132
|
+
options = { name: options.name, unique: options.unique }
|
|
133
|
+
index_name = index_name(table_name, column: column_names)
|
|
134
|
+
if Hash === options # legacy support, since this param was a string
|
|
135
|
+
index_type = options[:unique] ? "UNIQUE" : ""
|
|
136
|
+
index_name = options[:name] || index_name
|
|
137
|
+
else
|
|
138
|
+
index_type = options
|
|
139
|
+
end
|
|
140
|
+
quoted_column_names = column_names.map { |e| quote_column_name_or_expression(e) }.join(", ")
|
|
141
|
+
"CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{quoted_column_names})"
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def structure_dump_fk_constraints #:nodoc:
|
|
146
|
+
foreign_keys = select_all(<<-SQL.strip.gsub(/\s+/, " "), "foreign keys at structure dump")
|
|
147
|
+
SELECT table_name FROM all_tables
|
|
148
|
+
WHERE owner = SYS_CONTEXT('userenv', 'current_schema') ORDER BY 1
|
|
149
|
+
SQL
|
|
150
|
+
fks = foreign_keys.map do |table|
|
|
151
|
+
if respond_to?(:foreign_keys) && (foreign_keys = foreign_keys(table["table_name"])).any?
|
|
152
|
+
foreign_keys.map do |fk|
|
|
153
|
+
sql = "ALTER TABLE #{quote_table_name(fk.from_table)} ADD CONSTRAINT #{quote_column_name(fk.options[:name])} ".dup
|
|
154
|
+
sql << "#{foreign_key_definition(fk.to_table, fk.options)}"
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end.flatten.compact
|
|
158
|
+
join_with_statement_token(fks)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def structure_dump_table_comments(table_name)
|
|
162
|
+
comments = []
|
|
163
|
+
comment = table_comment(table_name)
|
|
164
|
+
|
|
165
|
+
unless comment.nil?
|
|
166
|
+
comments << "COMMENT ON TABLE #{quote_table_name(table_name)} IS '#{quote_string(comment)}'"
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
join_with_statement_token(comments)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def structure_dump_column_comments(table_name)
|
|
173
|
+
comments = []
|
|
174
|
+
columns = select_values(<<-SQL.strip.gsub(/\s+/, " "), "column comments at structure dump")
|
|
175
|
+
SELECT column_name FROM user_tab_columns
|
|
176
|
+
WHERE table_name = '#{table_name}' ORDER BY column_id
|
|
177
|
+
SQL
|
|
178
|
+
|
|
179
|
+
columns.each do |column|
|
|
180
|
+
comment = column_comment(table_name, column)
|
|
181
|
+
unless comment.nil?
|
|
182
|
+
comments << "COMMENT ON COLUMN #{quote_table_name(table_name)}.#{quote_column_name(column)} IS '#{quote_string(comment)}'"
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
join_with_statement_token(comments)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def foreign_key_definition(to_table, options = {}) #:nodoc:
|
|
190
|
+
column_sql = quote_column_name(options[:column] || "#{to_table.to_s.singularize}_id")
|
|
191
|
+
references = options[:references] ? options[:references].first : nil
|
|
192
|
+
references_sql = quote_column_name(options[:primary_key] || references || "id")
|
|
193
|
+
|
|
194
|
+
sql = "FOREIGN KEY (#{column_sql}) REFERENCES #{quote_table_name(to_table)}(#{references_sql})"
|
|
195
|
+
|
|
196
|
+
case options[:dependent]
|
|
197
|
+
when :nullify
|
|
198
|
+
sql << " ON DELETE SET NULL"
|
|
199
|
+
when :delete
|
|
200
|
+
sql << " ON DELETE CASCADE"
|
|
201
|
+
end
|
|
202
|
+
sql
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
# Extract all stored procedures, packages, synonyms and views.
|
|
206
|
+
def structure_dump_db_stored_code #:nodoc:
|
|
207
|
+
structure = []
|
|
208
|
+
all_source = select_all(<<-SQL.strip.gsub(/\s+/, " "), "stored program at structure dump")
|
|
209
|
+
SELECT DISTINCT name, type
|
|
210
|
+
FROM all_source
|
|
211
|
+
WHERE type IN ('PROCEDURE', 'PACKAGE', 'PACKAGE BODY', 'FUNCTION', 'TRIGGER', 'TYPE')
|
|
212
|
+
AND name NOT LIKE 'BIN$%'
|
|
213
|
+
AND owner = SYS_CONTEXT('userenv', 'current_schema') ORDER BY type
|
|
214
|
+
SQL
|
|
215
|
+
all_source.each do |source|
|
|
216
|
+
ddl = "CREATE OR REPLACE \n".dup
|
|
217
|
+
texts = select_all(<<-SQL.strip.gsub(/\s+/, " "), "all source at structure dump")
|
|
218
|
+
SELECT text
|
|
219
|
+
FROM all_source
|
|
220
|
+
WHERE name = '#{source['name']}'
|
|
221
|
+
AND type = '#{source['type']}'
|
|
222
|
+
AND owner = SYS_CONTEXT('userenv', 'current_schema')
|
|
223
|
+
ORDER BY line
|
|
224
|
+
SQL
|
|
225
|
+
texts.each do |row|
|
|
226
|
+
ddl << row["text"]
|
|
227
|
+
end
|
|
228
|
+
ddl << ";" unless ddl.strip[-1, 1] == ";"
|
|
229
|
+
structure << ddl
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# export views
|
|
233
|
+
views = select_all(<<-SQL.strip.gsub(/\s+/, " "), "views at structure dump")
|
|
234
|
+
SELECT view_name, text FROM all_views
|
|
235
|
+
WHERE owner = SYS_CONTEXT('userenv', 'session_user') ORDER BY view_name ASC
|
|
236
|
+
SQL
|
|
237
|
+
views.each do |view|
|
|
238
|
+
structure << "CREATE OR REPLACE FORCE VIEW #{view['view_name']} AS\n #{view['text']}"
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
# export synonyms
|
|
242
|
+
synonyms = select_all(<<-SQL.strip.gsub(/\s+/, " "), "synonyms at structure dump")
|
|
243
|
+
SELECT owner, synonym_name, table_name, table_owner
|
|
244
|
+
FROM all_synonyms
|
|
245
|
+
WHERE owner = SYS_CONTEXT('userenv', 'current_schema')
|
|
246
|
+
SQL
|
|
247
|
+
synonyms.each do |synonym|
|
|
248
|
+
structure << "CREATE OR REPLACE #{synonym['owner'] == 'PUBLIC' ? 'PUBLIC' : '' } SYNONYM #{synonym['synonym_name']}
|
|
249
|
+
FOR #{synonym['table_owner']}.#{synonym['table_name']}"
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
join_with_statement_token(structure)
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
def structure_drop #:nodoc:
|
|
256
|
+
sequences = select_values(<<-SQL.strip.gsub(/\s+/, " "), "sequences to drop at structure dump")
|
|
257
|
+
SELECT sequence_name FROM all_sequences where sequence_owner = SYS_CONTEXT('userenv', 'session_user') ORDER BY 1
|
|
258
|
+
SQL
|
|
259
|
+
statements = sequences.map do |seq|
|
|
260
|
+
"DROP SEQUENCE \"#{seq}\""
|
|
261
|
+
end
|
|
262
|
+
tables = select_values(<<-SQL.strip.gsub(/\s+/, " "), "tables to drop at structure dump")
|
|
263
|
+
SELECT table_name from all_tables t
|
|
264
|
+
WHERE owner = SYS_CONTEXT('userenv', 'current_schema') AND secondary = 'N'
|
|
265
|
+
AND NOT EXISTS (SELECT mv.mview_name FROM all_mviews mv
|
|
266
|
+
WHERE mv.owner = t.owner AND mv.mview_name = t.table_name)
|
|
267
|
+
AND NOT EXISTS (SELECT mvl.log_table FROM all_mview_logs mvl
|
|
268
|
+
WHERE mvl.log_owner = t.owner AND mvl.log_table = t.table_name)
|
|
269
|
+
ORDER BY 1
|
|
270
|
+
SQL
|
|
271
|
+
tables.each do |table|
|
|
272
|
+
statements << "DROP TABLE \"#{table}\" CASCADE CONSTRAINTS"
|
|
273
|
+
end
|
|
274
|
+
join_with_statement_token(statements)
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
def temp_table_drop #:nodoc:
|
|
278
|
+
temporary_tables = select_values(<<-SQL.strip.gsub(/\s+/, " "), "temporary tables to drop at structure dump")
|
|
279
|
+
SELECT table_name FROM all_tables
|
|
280
|
+
WHERE owner = SYS_CONTEXT('userenv', 'current_schema')
|
|
281
|
+
AND secondary = 'N' AND temporary = 'Y' ORDER BY 1
|
|
282
|
+
SQL
|
|
283
|
+
statements = temporary_tables.map do |table|
|
|
284
|
+
"DROP TABLE \"#{table}\" CASCADE CONSTRAINTS"
|
|
285
|
+
end
|
|
286
|
+
join_with_statement_token(statements)
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def full_drop(preserve_tables = false) #:nodoc:
|
|
290
|
+
s = preserve_tables ? [] : [structure_drop]
|
|
291
|
+
s << temp_table_drop if preserve_tables
|
|
292
|
+
s << drop_sql_for_feature("view")
|
|
293
|
+
s << drop_sql_for_feature("materialized view")
|
|
294
|
+
s << drop_sql_for_feature("synonym")
|
|
295
|
+
s << drop_sql_for_feature("type")
|
|
296
|
+
s << drop_sql_for_object("package")
|
|
297
|
+
s << drop_sql_for_object("function")
|
|
298
|
+
s << drop_sql_for_object("procedure")
|
|
299
|
+
s.join
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
def execute_structure_dump(string)
|
|
303
|
+
string.split(STATEMENT_TOKEN).each do |ddl|
|
|
304
|
+
execute(ddl) unless ddl.blank?
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
private
|
|
309
|
+
|
|
310
|
+
# Called only if `supports_virtual_columns?` returns true
|
|
311
|
+
# return [{'column_name' => 'FOOS', 'data_default' => '...'}, ...]
|
|
312
|
+
def virtual_columns_for(table)
|
|
313
|
+
select_all(<<-SQL.strip.gsub(/\s+/, " "), "virtual columns for")
|
|
314
|
+
SELECT column_name, data_default
|
|
315
|
+
FROM all_tab_cols
|
|
316
|
+
WHERE virtual_column = 'YES'
|
|
317
|
+
AND owner = SYS_CONTEXT('userenv', 'session_user')
|
|
318
|
+
AND table_name = '#{table.upcase}'
|
|
319
|
+
SQL
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
def drop_sql_for_feature(type)
|
|
323
|
+
short_type = type == "materialized view" ? "mview" : type
|
|
324
|
+
features = select_values(<<-SQL.strip.gsub(/\s+/, " "), "features to drop")
|
|
325
|
+
SELECT #{short_type}_name FROM all_#{short_type.tableize}
|
|
326
|
+
where owner = SYS_CONTEXT('userenv', 'session_user')
|
|
327
|
+
SQL
|
|
328
|
+
statements = features.map do |name|
|
|
329
|
+
"DROP #{type.upcase} \"#{name}\""
|
|
330
|
+
end
|
|
331
|
+
join_with_statement_token(statements)
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
def drop_sql_for_object(type)
|
|
335
|
+
objects = select_values(<<-SQL.strip.gsub(/\s+/, " "), "objects to drop")
|
|
336
|
+
SELECT object_name FROM all_objects
|
|
337
|
+
WHERE object_type = '#{type.upcase}' and owner = SYS_CONTEXT('userenv', 'session_user')
|
|
338
|
+
SQL
|
|
339
|
+
statements = objects.map do |name|
|
|
340
|
+
"DROP #{type.upcase} \"#{name}\""
|
|
341
|
+
end
|
|
342
|
+
join_with_statement_token(statements)
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
def join_with_statement_token(array)
|
|
346
|
+
string = array.join(STATEMENT_TOKEN)
|
|
347
|
+
string << STATEMENT_TOKEN unless string.blank?
|
|
348
|
+
string
|
|
349
|
+
end
|
|
350
|
+
end
|
|
351
|
+
end
|
|
352
|
+
end
|
|
353
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module ConnectionAdapters #:nodoc:
|
|
5
|
+
module OracleEnhanced
|
|
6
|
+
class TypeMetadata < DelegateClass(ActiveRecord::ConnectionAdapters::SqlTypeMetadata) # :nodoc:
|
|
7
|
+
attr_reader :virtual
|
|
8
|
+
|
|
9
|
+
def initialize(type_metadata, virtual: nil)
|
|
10
|
+
super(type_metadata)
|
|
11
|
+
@type_metadata = type_metadata
|
|
12
|
+
@virtual = virtual
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def ==(other)
|
|
16
|
+
other.is_a?(OracleEnhanced::TypeMetadata) &&
|
|
17
|
+
attributes_for_hash == other.attributes_for_hash
|
|
18
|
+
end
|
|
19
|
+
alias eql? ==
|
|
20
|
+
|
|
21
|
+
def hash
|
|
22
|
+
attributes_for_hash.hash
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
protected
|
|
26
|
+
|
|
27
|
+
def attributes_for_hash
|
|
28
|
+
[self.class, @type_metadata, virtual]
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# oracle_enhanced_adapter.rb -- ActiveRecord adapter for Oracle 10g, 11g and 12c
|
|
3
4
|
#
|
|
4
5
|
# Authors or original oracle_adapter: Graham Jenkins, Michael Schoen
|
|
5
6
|
#
|
|
@@ -29,27 +30,58 @@
|
|
|
29
30
|
# contribution.
|
|
30
31
|
# portions Copyright 2005 Graham Jenkins
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
require
|
|
34
|
-
|
|
35
|
-
require
|
|
36
|
-
require
|
|
37
|
-
|
|
38
|
-
require
|
|
39
|
-
require
|
|
40
|
-
|
|
41
|
-
require
|
|
33
|
+
require "active_record/connection_adapters/abstract_adapter"
|
|
34
|
+
require "active_record/connection_adapters/statement_pool"
|
|
35
|
+
require "active_record/connection_adapters/oracle_enhanced/connection"
|
|
36
|
+
require "active_record/connection_adapters/oracle_enhanced/database_statements"
|
|
37
|
+
require "active_record/connection_adapters/oracle_enhanced/schema_creation"
|
|
38
|
+
require "active_record/connection_adapters/oracle_enhanced/schema_definitions"
|
|
39
|
+
require "active_record/connection_adapters/oracle_enhanced/schema_dumper"
|
|
40
|
+
require "active_record/connection_adapters/oracle_enhanced/schema_statements"
|
|
41
|
+
require "active_record/connection_adapters/oracle_enhanced/schema_statements_ext"
|
|
42
|
+
require "active_record/connection_adapters/oracle_enhanced/context_index"
|
|
43
|
+
require "active_record/connection_adapters/oracle_enhanced/column"
|
|
44
|
+
require "active_record/connection_adapters/oracle_enhanced/quoting"
|
|
45
|
+
require "active_record/connection_adapters/oracle_enhanced/database_limits"
|
|
46
|
+
require "active_record/connection_adapters/oracle_enhanced/dbms_output"
|
|
47
|
+
require "active_record/connection_adapters/oracle_enhanced/type_metadata"
|
|
48
|
+
require "active_record/connection_adapters/oracle_enhanced/structure_dump"
|
|
49
|
+
require "active_record/connection_adapters/oracle_enhanced/lob"
|
|
50
|
+
|
|
51
|
+
require "active_record/type/oracle_enhanced/raw"
|
|
52
|
+
require "active_record/type/oracle_enhanced/integer"
|
|
53
|
+
require "active_record/type/oracle_enhanced/string"
|
|
54
|
+
require "active_record/type/oracle_enhanced/national_character_string"
|
|
55
|
+
require "active_record/type/oracle_enhanced/text"
|
|
56
|
+
require "active_record/type/oracle_enhanced/national_character_text"
|
|
57
|
+
require "active_record/type/oracle_enhanced/boolean"
|
|
58
|
+
require "active_record/type/oracle_enhanced/json"
|
|
59
|
+
require "active_record/type/oracle_enhanced/timestamptz"
|
|
60
|
+
require "active_record/type/oracle_enhanced/timestampltz"
|
|
42
61
|
|
|
43
62
|
module ActiveRecord
|
|
44
|
-
module
|
|
63
|
+
module ConnectionHandling #:nodoc:
|
|
64
|
+
# Establishes a connection to the database that's used by all Active Record objects.
|
|
65
|
+
def oracle_enhanced_connection(config) #:nodoc:
|
|
66
|
+
if config[:emulate_oracle_adapter] == true
|
|
67
|
+
# allows the enhanced adapter to look like the OracleAdapter. Useful to pick up
|
|
68
|
+
# conditionals in the rails activerecord test suite
|
|
69
|
+
require "active_record/connection_adapters/emulation/oracle_adapter"
|
|
70
|
+
ConnectionAdapters::OracleAdapter.new(
|
|
71
|
+
ConnectionAdapters::OracleEnhanced::Connection.create(config), logger, config)
|
|
72
|
+
else
|
|
73
|
+
ConnectionAdapters::OracleEnhancedAdapter.new(
|
|
74
|
+
ConnectionAdapters::OracleEnhanced::Connection.create(config), logger, config)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
45
78
|
|
|
79
|
+
module ConnectionAdapters #:nodoc:
|
|
46
80
|
# Oracle enhanced adapter will work with both
|
|
47
|
-
#
|
|
81
|
+
# CRuby ruby-oci8 gem (which provides interface to Oracle OCI client)
|
|
48
82
|
# or with JRuby and Oracle JDBC driver.
|
|
49
83
|
#
|
|
50
|
-
# It should work with Oracle
|
|
51
|
-
# Limited set of functionality should work on Oracle 8i as well but several features
|
|
52
|
-
# rely on newer functionality in Oracle database.
|
|
84
|
+
# It should work with Oracle 10g, 11g and 12c databases.
|
|
53
85
|
#
|
|
54
86
|
# Usage notes:
|
|
55
87
|
# * Key generation assumes a "${table_name}_seq" sequence is available
|
|
@@ -79,9 +111,10 @@ module ActiveRecord
|
|
|
79
111
|
# * <tt>:privilege</tt> - set "SYSDBA" if you want to connect with this privilege
|
|
80
112
|
# * <tt>:allow_concurrency</tt> - set to "true" if non-blocking mode should be enabled (just for OCI client)
|
|
81
113
|
# * <tt>:prefetch_rows</tt> - how many rows should be fetched at one time to increase performance, defaults to 100
|
|
82
|
-
# * <tt>:cursor_sharing</tt> - cursor sharing mode to minimize amount of unique statements,
|
|
114
|
+
# * <tt>:cursor_sharing</tt> - cursor sharing mode to minimize amount of unique statements, no default value
|
|
83
115
|
# * <tt>:time_zone</tt> - database session time zone
|
|
84
116
|
# (it is recommended to set it using ENV['TZ'] which will be then also used for database session time zone)
|
|
117
|
+
# * <tt>:schema</tt> - database schema which holds schema objects.
|
|
85
118
|
#
|
|
86
119
|
# Optionals NLS parameters:
|
|
87
120
|
#
|
|
@@ -89,6 +122,7 @@ module ActiveRecord
|
|
|
89
122
|
# * <tt>:nls_comp</tt>
|
|
90
123
|
# * <tt>:nls_currency</tt>
|
|
91
124
|
# * <tt>:nls_date_format</tt> - format for :date columns, defaults to <tt>YYYY-MM-DD HH24:MI:SS</tt>
|
|
125
|
+
# (changing `nls_date_format` parameter value is not supported. Use the default value.)
|
|
92
126
|
# * <tt>:nls_date_language</tt>
|
|
93
127
|
# * <tt>:nls_dual_currency</tt>
|
|
94
128
|
# * <tt>:nls_iso_currency</tt>
|
|
@@ -100,11 +134,20 @@ module ActiveRecord
|
|
|
100
134
|
# * <tt>:nls_sort</tt>
|
|
101
135
|
# * <tt>:nls_territory</tt>
|
|
102
136
|
# * <tt>:nls_timestamp_format</tt> - format for :timestamp columns, defaults to <tt>YYYY-MM-DD HH24:MI:SS:FF6</tt>
|
|
137
|
+
# (changing `nls_timestamp_format` parameter value is not supported. Use the default value.)
|
|
103
138
|
# * <tt>:nls_timestamp_tz_format</tt>
|
|
104
139
|
# * <tt>:nls_time_format</tt>
|
|
105
140
|
# * <tt>:nls_time_tz_format</tt>
|
|
106
141
|
#
|
|
107
142
|
class OracleEnhancedAdapter < AbstractAdapter
|
|
143
|
+
include OracleEnhanced::DatabaseStatements
|
|
144
|
+
include OracleEnhanced::SchemaStatements
|
|
145
|
+
include OracleEnhanced::SchemaStatementsExt
|
|
146
|
+
include OracleEnhanced::ContextIndex
|
|
147
|
+
include OracleEnhanced::Quoting
|
|
148
|
+
include OracleEnhanced::DatabaseLimits
|
|
149
|
+
include OracleEnhanced::DbmsOutput
|
|
150
|
+
include OracleEnhanced::StructureDump
|
|
108
151
|
|
|
109
152
|
##
|
|
110
153
|
# :singleton-method:
|
|
@@ -118,419 +161,197 @@ module ActiveRecord
|
|
|
118
161
|
|
|
119
162
|
##
|
|
120
163
|
# :singleton-method:
|
|
121
|
-
#
|
|
122
|
-
# to
|
|
123
|
-
# If you wish that DATE values with hour, minutes and seconds equal to 0 are typecasted
|
|
124
|
-
# to Date then you can add the following line to your initializer file:
|
|
164
|
+
# OracleEnhancedAdapter will use the default tablespace, but if you want specific types of
|
|
165
|
+
# objects to go into specific tablespaces, specify them like this in an initializer:
|
|
125
166
|
#
|
|
126
|
-
# ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.
|
|
167
|
+
# ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.default_tablespaces =
|
|
168
|
+
# {:clob => 'TS_LOB', :blob => 'TS_LOB', :index => 'TS_INDEX', :table => 'TS_DATA'}
|
|
127
169
|
#
|
|
128
|
-
#
|
|
129
|
-
#
|
|
130
|
-
cattr_accessor :emulate_dates
|
|
131
|
-
self.emulate_dates = false
|
|
132
|
-
|
|
133
|
-
##
|
|
134
|
-
# :singleton-method:
|
|
135
|
-
# OracleEnhancedAdapter will use the default tablespace, but if you want specific types of
|
|
136
|
-
# objects to go into specific tablespaces, specify them like this in an initializer:
|
|
137
|
-
#
|
|
138
|
-
# ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.default_tablespaces =
|
|
139
|
-
# {:clob => 'TS_LOB', :blob => 'TS_LOB', :index => 'TS_INDEX', :table => 'TS_DATA'}
|
|
140
|
-
#
|
|
141
|
-
# Using the :tablespace option where available (e.g create_table) will take precedence
|
|
142
|
-
# over these settings.
|
|
170
|
+
# Using the :tablespace option where available (e.g create_table) will take precedence
|
|
171
|
+
# over these settings.
|
|
143
172
|
cattr_accessor :default_tablespaces
|
|
144
|
-
self.default_tablespaces={}
|
|
173
|
+
self.default_tablespaces = {}
|
|
145
174
|
|
|
146
175
|
##
|
|
147
176
|
# :singleton-method:
|
|
148
|
-
#
|
|
149
|
-
#
|
|
150
|
-
# If you wish that DATE columns with "date" in their name (e.g. "creation_date") are typecasted
|
|
151
|
-
# to Date then you can add the following line to your initializer file:
|
|
152
|
-
#
|
|
153
|
-
# ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.emulate_dates_by_column_name = true
|
|
154
|
-
#
|
|
155
|
-
# As this option can have side effects when unnecessary typecasting is done it is recommended
|
|
156
|
-
# that Date columns are explicily defined with +set_date_columns+ method.
|
|
157
|
-
cattr_accessor :emulate_dates_by_column_name
|
|
158
|
-
self.emulate_dates_by_column_name = false
|
|
159
|
-
|
|
160
|
-
# Check column name to identify if it is Date (and not Time) column.
|
|
161
|
-
# Is used if +emulate_dates_by_column_name+ option is set to +true+.
|
|
162
|
-
# Override this method definition in initializer file if different Date column recognition is needed.
|
|
163
|
-
def self.is_date_column?(name, table_name = nil)
|
|
164
|
-
name =~ /(^|_)date(_|$)/i
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
# instance method uses at first check if column type defined at class level
|
|
168
|
-
def is_date_column?(name, table_name = nil) #:nodoc:
|
|
169
|
-
case get_type_for_column(table_name, name)
|
|
170
|
-
when nil
|
|
171
|
-
self.class.is_date_column?(name, table_name)
|
|
172
|
-
when :date
|
|
173
|
-
true
|
|
174
|
-
else
|
|
175
|
-
false
|
|
176
|
-
end
|
|
177
|
-
end
|
|
178
|
-
|
|
179
|
-
##
|
|
180
|
-
# :singleton-method:
|
|
181
|
-
# By default, the OracleEnhancedAdapter will typecast all columns of type <tt>NUMBER</tt>
|
|
182
|
-
# (without precision or scale) to Float or BigDecimal value.
|
|
183
|
-
# If you wish that NUMBER columns with name "id" or that end with "_id" are typecasted
|
|
184
|
-
# to Integer then you can add the following line to your initializer file:
|
|
185
|
-
#
|
|
186
|
-
# ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.emulate_integers_by_column_name = true
|
|
187
|
-
cattr_accessor :emulate_integers_by_column_name
|
|
188
|
-
self.emulate_integers_by_column_name = false
|
|
189
|
-
|
|
190
|
-
# Check column name to identify if it is Integer (and not Float or BigDecimal) column.
|
|
191
|
-
# Is used if +emulate_integers_by_column_name+ option is set to +true+.
|
|
192
|
-
# Override this method definition in initializer file if different Integer column recognition is needed.
|
|
193
|
-
def self.is_integer_column?(name, table_name = nil)
|
|
194
|
-
name =~ /(^|_)id$/i
|
|
195
|
-
end
|
|
196
|
-
|
|
197
|
-
##
|
|
198
|
-
# :singleton-method:
|
|
199
|
-
# If you wish that CHAR(1), VARCHAR2(1) columns or VARCHAR2 columns with FLAG or YN at the end of their name
|
|
200
|
-
# are typecasted to booleans then you can add the following line to your initializer file:
|
|
177
|
+
# If you wish that CHAR(1), VARCHAR2(1) columns are typecasted to booleans
|
|
178
|
+
# then you can add the following line to your initializer file:
|
|
201
179
|
#
|
|
202
180
|
# ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.emulate_booleans_from_strings = true
|
|
203
181
|
cattr_accessor :emulate_booleans_from_strings
|
|
204
182
|
self.emulate_booleans_from_strings = false
|
|
205
183
|
|
|
206
|
-
# Check column name to identify if it is boolean (and not String) column.
|
|
207
|
-
# Is used if +emulate_booleans_from_strings+ option is set to +true+.
|
|
208
|
-
# Override this method definition in initializer file if different boolean column recognition is needed.
|
|
209
|
-
def self.is_boolean_column?(name, field_type, table_name = nil)
|
|
210
|
-
return true if ["CHAR(1)","VARCHAR2(1)"].include?(field_type)
|
|
211
|
-
field_type =~ /^VARCHAR2/ && (name =~ /_flag$/i || name =~ /_yn$/i)
|
|
212
|
-
end
|
|
213
|
-
|
|
214
|
-
# How boolean value should be quoted to String.
|
|
215
|
-
# Used if +emulate_booleans_from_strings+ option is set to +true+.
|
|
216
|
-
def self.boolean_to_string(bool)
|
|
217
|
-
bool ? "Y" : "N"
|
|
218
|
-
end
|
|
219
|
-
|
|
220
184
|
##
|
|
221
185
|
# :singleton-method:
|
|
222
|
-
#
|
|
186
|
+
# By default, OracleEnhanced adapter will use Oracle12 visitor
|
|
187
|
+
# if database version is Oracle 12.1.
|
|
188
|
+
# If you wish to use Oracle visitor which is intended to work with Oracle 11.2 or lower
|
|
189
|
+
# for Oracle 12.1 database you can add the following line to your initializer file:
|
|
223
190
|
#
|
|
224
|
-
# ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.
|
|
225
|
-
cattr_accessor :
|
|
226
|
-
self.
|
|
191
|
+
# ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.use_old_oracle_visitor = true
|
|
192
|
+
cattr_accessor :use_old_oracle_visitor
|
|
193
|
+
self.use_old_oracle_visitor = false
|
|
227
194
|
|
|
228
195
|
##
|
|
229
196
|
# :singleton-method:
|
|
230
|
-
# Specify
|
|
197
|
+
# Specify default sequence start with value (by default 10000 if not explicitly set), e.g.:
|
|
231
198
|
#
|
|
232
|
-
# ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.
|
|
233
|
-
cattr_accessor :
|
|
234
|
-
self.
|
|
235
|
-
|
|
236
|
-
class StatementPool
|
|
237
|
-
include Enumerable
|
|
238
|
-
|
|
239
|
-
def initialize(connection, max = 300)
|
|
240
|
-
@connection = connection
|
|
241
|
-
@max = max
|
|
242
|
-
@cache = {}
|
|
243
|
-
end
|
|
244
|
-
|
|
245
|
-
def each(&block); @cache.each(&block); end
|
|
246
|
-
def key?(key); @cache.key?(key); end
|
|
247
|
-
def [](key); @cache[key]; end
|
|
248
|
-
def length; @cache.length; end
|
|
249
|
-
def delete(key); @cache.delete(key); end
|
|
199
|
+
# ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.default_sequence_start_value = 1
|
|
200
|
+
cattr_accessor :default_sequence_start_value
|
|
201
|
+
self.default_sequence_start_value = 10000
|
|
250
202
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
@cache.shift.last.close
|
|
254
|
-
end
|
|
255
|
-
@cache[sql] = key
|
|
256
|
-
end
|
|
203
|
+
class StatementPool < ConnectionAdapters::StatementPool
|
|
204
|
+
private
|
|
257
205
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
cursor.close
|
|
206
|
+
def dealloc(stmt)
|
|
207
|
+
stmt.close
|
|
261
208
|
end
|
|
262
|
-
@cache.clear
|
|
263
|
-
end
|
|
264
209
|
end
|
|
265
210
|
|
|
266
|
-
def initialize(connection, logger, config)
|
|
267
|
-
super(connection, logger)
|
|
268
|
-
@
|
|
269
|
-
@config = config
|
|
270
|
-
@statements = StatementPool.new(connection, config.fetch(:statement_limit) { 250 })
|
|
211
|
+
def initialize(connection, logger = nil, config = {}) # :nodoc:
|
|
212
|
+
super(connection, logger, config)
|
|
213
|
+
@statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit]))
|
|
271
214
|
@enable_dbms_output = false
|
|
272
|
-
@visitor = Arel::Visitors::Oracle.new self if defined?(Arel::Visitors::Oracle)
|
|
273
215
|
end
|
|
274
216
|
|
|
275
|
-
|
|
276
|
-
Arel::Visitors::Oracle.new(pool)
|
|
277
|
-
end
|
|
278
|
-
|
|
279
|
-
ADAPTER_NAME = 'OracleEnhanced'.freeze
|
|
217
|
+
ADAPTER_NAME = "OracleEnhanced".freeze
|
|
280
218
|
|
|
281
219
|
def adapter_name #:nodoc:
|
|
282
220
|
ADAPTER_NAME
|
|
283
221
|
end
|
|
284
222
|
|
|
285
|
-
def
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
223
|
+
def arel_visitor # :nodoc:
|
|
224
|
+
if supports_fetch_first_n_rows_and_offset?
|
|
225
|
+
Arel::Visitors::Oracle12.new(self)
|
|
226
|
+
else
|
|
227
|
+
Arel::Visitors::Oracle.new(self)
|
|
228
|
+
end
|
|
291
229
|
end
|
|
292
230
|
|
|
293
231
|
def supports_savepoints? #:nodoc:
|
|
294
232
|
true
|
|
295
233
|
end
|
|
296
234
|
|
|
297
|
-
#:
|
|
298
|
-
|
|
299
|
-
:nls_calendar => nil,
|
|
300
|
-
:nls_comp => nil,
|
|
301
|
-
:nls_currency => nil,
|
|
302
|
-
:nls_date_format => 'YYYY-MM-DD HH24:MI:SS',
|
|
303
|
-
:nls_date_language => nil,
|
|
304
|
-
:nls_dual_currency => nil,
|
|
305
|
-
:nls_iso_currency => nil,
|
|
306
|
-
:nls_language => nil,
|
|
307
|
-
:nls_length_semantics => 'CHAR',
|
|
308
|
-
:nls_nchar_conv_excp => nil,
|
|
309
|
-
:nls_numeric_characters => nil,
|
|
310
|
-
:nls_sort => nil,
|
|
311
|
-
:nls_territory => nil,
|
|
312
|
-
:nls_timestamp_format => 'YYYY-MM-DD HH24:MI:SS:FF6',
|
|
313
|
-
:nls_timestamp_tz_format => nil,
|
|
314
|
-
:nls_time_format => nil,
|
|
315
|
-
:nls_time_tz_format => nil
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
#:stopdoc:
|
|
319
|
-
NATIVE_DATABASE_TYPES = {
|
|
320
|
-
:primary_key => "NUMBER(38) NOT NULL PRIMARY KEY",
|
|
321
|
-
:string => { :name => "VARCHAR2", :limit => 255 },
|
|
322
|
-
:text => { :name => "CLOB" },
|
|
323
|
-
:integer => { :name => "NUMBER", :limit => 38 },
|
|
324
|
-
:float => { :name => "NUMBER" },
|
|
325
|
-
:decimal => { :name => "DECIMAL" },
|
|
326
|
-
:datetime => { :name => "DATE" },
|
|
327
|
-
# changed to native TIMESTAMP type
|
|
328
|
-
# :timestamp => { :name => "DATE" },
|
|
329
|
-
:timestamp => { :name => "TIMESTAMP" },
|
|
330
|
-
:time => { :name => "DATE" },
|
|
331
|
-
:date => { :name => "DATE" },
|
|
332
|
-
:binary => { :name => "BLOB" },
|
|
333
|
-
:boolean => { :name => "NUMBER", :limit => 1 },
|
|
334
|
-
:raw => { :name => "RAW", :limit => 2000 }
|
|
335
|
-
}
|
|
336
|
-
# if emulate_booleans_from_strings then store booleans in VARCHAR2
|
|
337
|
-
NATIVE_DATABASE_TYPES_BOOLEAN_STRINGS = NATIVE_DATABASE_TYPES.dup.merge(
|
|
338
|
-
:boolean => { :name => "VARCHAR2", :limit => 1 }
|
|
339
|
-
)
|
|
340
|
-
#:startdoc:
|
|
341
|
-
|
|
342
|
-
def native_database_types #:nodoc:
|
|
343
|
-
emulate_booleans_from_strings ? NATIVE_DATABASE_TYPES_BOOLEAN_STRINGS : NATIVE_DATABASE_TYPES
|
|
344
|
-
end
|
|
345
|
-
|
|
346
|
-
# maximum length of Oracle identifiers
|
|
347
|
-
IDENTIFIER_MAX_LENGTH = 30
|
|
348
|
-
|
|
349
|
-
def table_alias_length #:nodoc:
|
|
350
|
-
IDENTIFIER_MAX_LENGTH
|
|
351
|
-
end
|
|
352
|
-
|
|
353
|
-
# the maximum length of a table name
|
|
354
|
-
def table_name_length
|
|
355
|
-
IDENTIFIER_MAX_LENGTH
|
|
356
|
-
end
|
|
357
|
-
|
|
358
|
-
# the maximum length of a column name
|
|
359
|
-
def column_name_length
|
|
360
|
-
IDENTIFIER_MAX_LENGTH
|
|
361
|
-
end
|
|
362
|
-
|
|
363
|
-
# the maximum length of an index name
|
|
364
|
-
def index_name_length
|
|
365
|
-
IDENTIFIER_MAX_LENGTH
|
|
235
|
+
def supports_transaction_isolation? #:nodoc:
|
|
236
|
+
true
|
|
366
237
|
end
|
|
367
238
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
IDENTIFIER_MAX_LENGTH
|
|
239
|
+
def supports_foreign_keys?
|
|
240
|
+
true
|
|
371
241
|
end
|
|
372
242
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
def in_clause_length
|
|
376
|
-
1000
|
|
243
|
+
def supports_foreign_keys_in_create?
|
|
244
|
+
supports_foreign_keys?
|
|
377
245
|
end
|
|
378
|
-
alias ids_in_list_limit in_clause_length
|
|
379
246
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
# see: abstract/quoting.rb
|
|
383
|
-
|
|
384
|
-
def quote_column_name(name) #:nodoc:
|
|
385
|
-
name = name.to_s
|
|
386
|
-
@quoted_column_names[name] ||= begin
|
|
387
|
-
# if only valid lowercase column characters in name
|
|
388
|
-
if name =~ /\A[a-z][a-z_0-9\$#]*\Z/
|
|
389
|
-
"\"#{name.upcase}\""
|
|
390
|
-
else
|
|
391
|
-
# remove double quotes which cannot be used inside quoted identifier
|
|
392
|
-
"\"#{name.gsub('"', '')}\""
|
|
393
|
-
end
|
|
394
|
-
end
|
|
247
|
+
def supports_views?
|
|
248
|
+
true
|
|
395
249
|
end
|
|
396
250
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
name = name.to_s
|
|
401
|
-
case name
|
|
402
|
-
# if only valid lowercase column characters in name
|
|
403
|
-
when /^[a-z][a-z_0-9\$#]*$/
|
|
404
|
-
"\"#{name.upcase}\""
|
|
405
|
-
when /^[a-z][a-z_0-9\$#\-]*$/i
|
|
406
|
-
"\"#{name}\""
|
|
407
|
-
# if other characters present then assume that it is expression
|
|
408
|
-
# which should not be quoted
|
|
251
|
+
def supports_fetch_first_n_rows_and_offset?
|
|
252
|
+
if !use_old_oracle_visitor && @connection.database_version.first >= 12
|
|
253
|
+
true
|
|
409
254
|
else
|
|
410
|
-
|
|
255
|
+
false
|
|
411
256
|
end
|
|
412
257
|
end
|
|
413
258
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
# * Names of database links can be as long as 128 bytes.
|
|
417
|
-
#
|
|
418
|
-
# Nonquoted identifiers cannot be Oracle Database reserved words
|
|
419
|
-
#
|
|
420
|
-
# Nonquoted identifiers must begin with an alphabetic character from
|
|
421
|
-
# your database character set
|
|
422
|
-
#
|
|
423
|
-
# Nonquoted identifiers can contain only alphanumeric characters from
|
|
424
|
-
# your database character set and the underscore (_), dollar sign ($),
|
|
425
|
-
# and pound sign (#). Database links can also contain periods (.) and
|
|
426
|
-
# "at" signs (@). Oracle strongly discourages you from using $ and # in
|
|
427
|
-
# nonquoted identifiers.
|
|
428
|
-
NONQUOTED_OBJECT_NAME = /[A-Za-z][A-z0-9$#]{0,29}/
|
|
429
|
-
NONQUOTED_DATABASE_LINK = /[A-Za-z][A-z0-9$#\.@]{0,127}/
|
|
430
|
-
VALID_TABLE_NAME = /\A(?:#{NONQUOTED_OBJECT_NAME}\.)?#{NONQUOTED_OBJECT_NAME}(?:@#{NONQUOTED_DATABASE_LINK})?\Z/
|
|
431
|
-
|
|
432
|
-
# unescaped table name should start with letter and
|
|
433
|
-
# contain letters, digits, _, $ or #
|
|
434
|
-
# can be prefixed with schema name
|
|
435
|
-
# CamelCase table names should be quoted
|
|
436
|
-
def self.valid_table_name?(name) #:nodoc:
|
|
437
|
-
name = name.to_s
|
|
438
|
-
name =~ VALID_TABLE_NAME && !(name =~ /[A-Z]/ && name =~ /[a-z]/) ? true : false
|
|
439
|
-
end
|
|
440
|
-
|
|
441
|
-
def quote_table_name(name) #:nodoc:
|
|
442
|
-
name = name.to_s
|
|
443
|
-
@quoted_table_names[name] ||= name.split('.').map{|n| n.split('@').map{|m| quote_column_name(m)}.join('@')}.join('.')
|
|
444
|
-
end
|
|
445
|
-
|
|
446
|
-
def quote_string(s) #:nodoc:
|
|
447
|
-
s.gsub(/'/, "''")
|
|
448
|
-
end
|
|
449
|
-
|
|
450
|
-
def quote(value, column = nil) #:nodoc:
|
|
451
|
-
if value && column
|
|
452
|
-
case column.type
|
|
453
|
-
when :text, :binary
|
|
454
|
-
%Q{empty_#{ column.sql_type.downcase rescue 'blob' }()}
|
|
455
|
-
# NLS_DATE_FORMAT independent TIMESTAMP support
|
|
456
|
-
when :timestamp
|
|
457
|
-
quote_timestamp_with_to_timestamp(value)
|
|
458
|
-
# NLS_DATE_FORMAT independent DATE support
|
|
459
|
-
when :date, :time, :datetime
|
|
460
|
-
quote_date_with_to_date(value)
|
|
461
|
-
when :raw
|
|
462
|
-
quote_raw(value)
|
|
463
|
-
when :string
|
|
464
|
-
# NCHAR and NVARCHAR2 literals should be quoted with N'...'.
|
|
465
|
-
# Read directly instance variable as otherwise migrations with table column default values are failing
|
|
466
|
-
# as migrations pass ColumnDefinition object to this method.
|
|
467
|
-
# Check if instance variable is defined to avoid warnings about accessing undefined instance variable.
|
|
468
|
-
column.instance_variable_defined?('@nchar') && column.instance_variable_get('@nchar') ? 'N' << super : super
|
|
469
|
-
else
|
|
470
|
-
super
|
|
471
|
-
end
|
|
472
|
-
elsif value.acts_like?(:date)
|
|
473
|
-
quote_date_with_to_date(value)
|
|
474
|
-
elsif value.acts_like?(:time)
|
|
475
|
-
value.to_i == value.to_f ? quote_date_with_to_date(value) : quote_timestamp_with_to_timestamp(value)
|
|
476
|
-
else
|
|
477
|
-
super
|
|
478
|
-
end
|
|
259
|
+
def supports_datetime_with_precision?
|
|
260
|
+
true
|
|
479
261
|
end
|
|
480
262
|
|
|
481
|
-
def
|
|
482
|
-
|
|
483
|
-
"1"
|
|
263
|
+
def supports_comments?
|
|
264
|
+
true
|
|
484
265
|
end
|
|
485
266
|
|
|
486
|
-
def
|
|
487
|
-
|
|
488
|
-
"0"
|
|
267
|
+
def supports_multi_insert?
|
|
268
|
+
@connection.database_version.to_s >= [11, 2].to_s
|
|
489
269
|
end
|
|
490
270
|
|
|
491
|
-
def
|
|
492
|
-
|
|
493
|
-
value = quoted_date(value) if value.acts_like?(:date) || value.acts_like?(:time)
|
|
494
|
-
"TO_DATE('#{value}','YYYY-MM-DD HH24:MI:SS')"
|
|
271
|
+
def supports_virtual_columns?
|
|
272
|
+
@connection.database_version.first >= 11
|
|
495
273
|
end
|
|
496
274
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
#
|
|
500
|
-
|
|
501
|
-
|
|
275
|
+
def supports_json?
|
|
276
|
+
# Oracle Database 12.1 or higher version supports JSON.
|
|
277
|
+
# However, Oracle enhanced adapter has limited support for JSON data type.
|
|
278
|
+
# which does not pass many of ActiveRecord JSON tests.
|
|
279
|
+
#
|
|
280
|
+
# No migration supported for :json type due to there is no `JSON` data type
|
|
281
|
+
# in Oracle Database itself.
|
|
282
|
+
#
|
|
283
|
+
# If you want to use JSON data type, here are steps
|
|
284
|
+
# 1.Define :string or :text in migration
|
|
285
|
+
#
|
|
286
|
+
# create_table :test_posts, force: true do |t|
|
|
287
|
+
# t.string :title
|
|
288
|
+
# t.text :article
|
|
289
|
+
# end
|
|
290
|
+
#
|
|
291
|
+
# 2. Set :json attributes
|
|
292
|
+
#
|
|
293
|
+
# class TestPost < ActiveRecord::Base
|
|
294
|
+
# attribute :title, :json
|
|
295
|
+
# attribute :article, :json
|
|
296
|
+
# end
|
|
297
|
+
#
|
|
298
|
+
# 3. Add `is json` database constraints by running sql statements
|
|
299
|
+
#
|
|
300
|
+
# alter table test_posts add constraint test_posts_title_is_json check (title is json)
|
|
301
|
+
# alter table test_posts add constraint test_posts_article_is_json check (article is json)
|
|
302
|
+
#
|
|
303
|
+
false
|
|
502
304
|
end
|
|
503
305
|
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
306
|
+
#:stopdoc:
|
|
307
|
+
DEFAULT_NLS_PARAMETERS = {
|
|
308
|
+
nls_calendar: nil,
|
|
309
|
+
nls_comp: nil,
|
|
310
|
+
nls_currency: nil,
|
|
311
|
+
nls_date_format: "YYYY-MM-DD HH24:MI:SS",
|
|
312
|
+
nls_date_language: nil,
|
|
313
|
+
nls_dual_currency: nil,
|
|
314
|
+
nls_iso_currency: nil,
|
|
315
|
+
nls_language: nil,
|
|
316
|
+
nls_length_semantics: "CHAR",
|
|
317
|
+
nls_nchar_conv_excp: nil,
|
|
318
|
+
nls_numeric_characters: nil,
|
|
319
|
+
nls_sort: nil,
|
|
320
|
+
nls_territory: nil,
|
|
321
|
+
nls_timestamp_format: "YYYY-MM-DD HH24:MI:SS:FF6",
|
|
322
|
+
nls_timestamp_tz_format: nil,
|
|
323
|
+
nls_time_format: nil,
|
|
324
|
+
nls_time_tz_format: nil
|
|
325
|
+
}
|
|
508
326
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
"
|
|
513
|
-
|
|
327
|
+
#:stopdoc:
|
|
328
|
+
NATIVE_DATABASE_TYPES = {
|
|
329
|
+
primary_key: "NUMBER(38) NOT NULL PRIMARY KEY",
|
|
330
|
+
string: { name: "VARCHAR2", limit: 255 },
|
|
331
|
+
text: { name: "CLOB" },
|
|
332
|
+
ntext: { name: "NCLOB" },
|
|
333
|
+
integer: { name: "NUMBER", limit: 38 },
|
|
334
|
+
float: { name: "BINARY_FLOAT" },
|
|
335
|
+
decimal: { name: "DECIMAL" },
|
|
336
|
+
datetime: { name: "TIMESTAMP" },
|
|
337
|
+
timestamp: { name: "TIMESTAMP" },
|
|
338
|
+
timestamptz: { name: "TIMESTAMP WITH TIME ZONE" },
|
|
339
|
+
timestampltz: { name: "TIMESTAMP WITH LOCAL TIME ZONE" },
|
|
340
|
+
time: { name: "TIMESTAMP" },
|
|
341
|
+
date: { name: "DATE" },
|
|
342
|
+
binary: { name: "BLOB" },
|
|
343
|
+
boolean: { name: "NUMBER", limit: 1 },
|
|
344
|
+
raw: { name: "RAW", limit: 2000 },
|
|
345
|
+
bigint: { name: "NUMBER", limit: 19 }
|
|
346
|
+
}
|
|
347
|
+
# if emulate_booleans_from_strings then store booleans in VARCHAR2
|
|
348
|
+
NATIVE_DATABASE_TYPES_BOOLEAN_STRINGS = NATIVE_DATABASE_TYPES.dup.merge(
|
|
349
|
+
boolean: { name: "VARCHAR2", limit: 1 }
|
|
350
|
+
)
|
|
351
|
+
#:startdoc:
|
|
514
352
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
case value
|
|
518
|
-
when true, false
|
|
519
|
-
if emulate_booleans_from_strings || column && column.type == :string
|
|
520
|
-
self.class.boolean_to_string(value)
|
|
521
|
-
else
|
|
522
|
-
value ? 1 : 0
|
|
523
|
-
end
|
|
524
|
-
when Date, Time
|
|
525
|
-
if value.acts_like?(:time)
|
|
526
|
-
zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
|
|
527
|
-
value.respond_to?(zone_conversion_method) ? value.send(zone_conversion_method) : value
|
|
528
|
-
else
|
|
529
|
-
value
|
|
530
|
-
end
|
|
531
|
-
else
|
|
532
|
-
super
|
|
533
|
-
end
|
|
353
|
+
def native_database_types #:nodoc:
|
|
354
|
+
emulate_booleans_from_strings ? NATIVE_DATABASE_TYPES_BOOLEAN_STRINGS : NATIVE_DATABASE_TYPES
|
|
534
355
|
end
|
|
535
356
|
|
|
536
357
|
# CONNECTION MANAGEMENT ====================================
|
|
@@ -559,15 +380,15 @@ module ActiveRecord
|
|
|
559
380
|
# last known state, which isn't good enough if the connection has
|
|
560
381
|
# gone stale since the last use.
|
|
561
382
|
@connection.ping
|
|
562
|
-
rescue
|
|
383
|
+
rescue OracleEnhanced::ConnectionException
|
|
563
384
|
false
|
|
564
385
|
end
|
|
565
386
|
|
|
566
387
|
# Reconnects to the database.
|
|
567
388
|
def reconnect! #:nodoc:
|
|
568
|
-
|
|
389
|
+
super
|
|
569
390
|
@connection.reset!
|
|
570
|
-
rescue
|
|
391
|
+
rescue OracleEnhanced::ConnectionException => e
|
|
571
392
|
@logger.warn "#{adapter_name} automatic reconnection failed: #{e.message}" if @logger
|
|
572
393
|
end
|
|
573
394
|
|
|
@@ -578,188 +399,16 @@ module ActiveRecord
|
|
|
578
399
|
|
|
579
400
|
# Disconnects from the database.
|
|
580
401
|
def disconnect! #:nodoc:
|
|
581
|
-
|
|
402
|
+
super
|
|
582
403
|
@connection.logoff rescue nil
|
|
583
404
|
end
|
|
584
405
|
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
# see: abstract/database_statements.rb
|
|
588
|
-
|
|
589
|
-
# Executes a SQL statement
|
|
590
|
-
def execute(sql, name = nil)
|
|
591
|
-
log(sql, name) { @connection.exec(sql) }
|
|
592
|
-
end
|
|
593
|
-
|
|
594
|
-
def substitute_at(column, index)
|
|
595
|
-
Arel.sql(":a#{index + 1}")
|
|
596
|
-
end
|
|
597
|
-
|
|
598
|
-
def clear_cache!
|
|
599
|
-
@statements.clear
|
|
600
|
-
end
|
|
601
|
-
|
|
602
|
-
def exec_query(sql, name = 'SQL', binds = [])
|
|
603
|
-
log(sql, name, binds) do
|
|
604
|
-
cursor = nil
|
|
605
|
-
cached = false
|
|
606
|
-
if binds.empty?
|
|
607
|
-
cursor = @connection.prepare(sql)
|
|
608
|
-
else
|
|
609
|
-
unless @statements.key? sql
|
|
610
|
-
@statements[sql] = @connection.prepare(sql)
|
|
611
|
-
end
|
|
612
|
-
|
|
613
|
-
cursor = @statements[sql]
|
|
614
|
-
|
|
615
|
-
binds.each_with_index do |bind, i|
|
|
616
|
-
col, val = bind
|
|
617
|
-
cursor.bind_param(i + 1, type_cast(val, col), col)
|
|
618
|
-
end
|
|
619
|
-
|
|
620
|
-
cached = true
|
|
621
|
-
end
|
|
622
|
-
|
|
623
|
-
cursor.exec
|
|
624
|
-
|
|
625
|
-
if name == 'EXPLAIN'
|
|
626
|
-
res = true
|
|
627
|
-
else
|
|
628
|
-
columns = cursor.get_col_names.map do |col_name|
|
|
629
|
-
@connection.oracle_downcase(col_name)
|
|
630
|
-
end
|
|
631
|
-
rows = []
|
|
632
|
-
fetch_options = {:get_lob_value => (name != 'Writable Large Object')}
|
|
633
|
-
while row = cursor.fetch(fetch_options)
|
|
634
|
-
rows << row
|
|
635
|
-
end
|
|
636
|
-
res = ActiveRecord::Result.new(columns, rows)
|
|
637
|
-
end
|
|
638
|
-
|
|
639
|
-
cursor.close unless cached
|
|
640
|
-
res
|
|
641
|
-
end
|
|
642
|
-
end
|
|
643
|
-
|
|
644
|
-
def supports_statement_cache?
|
|
645
|
-
true
|
|
646
|
-
end
|
|
647
|
-
|
|
648
|
-
def supports_explain?
|
|
649
|
-
true
|
|
650
|
-
end
|
|
651
|
-
|
|
652
|
-
def explain(arel, binds = [])
|
|
653
|
-
sql = "EXPLAIN PLAN FOR #{to_sql(arel)}"
|
|
654
|
-
return if sql =~ /FROM all_/
|
|
655
|
-
if ORACLE_ENHANCED_CONNECTION == :jdbc
|
|
656
|
-
exec_query(sql, 'EXPLAIN', binds)
|
|
657
|
-
else
|
|
658
|
-
exec_query(sql, 'EXPLAIN')
|
|
659
|
-
end
|
|
660
|
-
select_values("SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY)", 'EXPLAIN').join("\n")
|
|
661
|
-
end
|
|
662
|
-
|
|
663
|
-
# Returns an array of arrays containing the field values.
|
|
664
|
-
# Order is the same as that returned by #columns.
|
|
665
|
-
def select_rows(sql, name = nil)
|
|
666
|
-
# last parameter indicates to return also column list
|
|
667
|
-
result = columns = nil
|
|
668
|
-
log(sql, name) do
|
|
669
|
-
result, columns = @connection.select(sql, name, true)
|
|
670
|
-
end
|
|
671
|
-
result.map{ |v| columns.map{|c| v[c]} }
|
|
672
|
-
end
|
|
673
|
-
|
|
674
|
-
# Executes an INSERT statement and returns the new record's ID
|
|
675
|
-
def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
|
|
676
|
-
# if primary key value is already prefetched from sequence
|
|
677
|
-
# or if there is no primary key
|
|
678
|
-
if id_value || pk.nil?
|
|
679
|
-
execute(sql, name)
|
|
680
|
-
return id_value
|
|
681
|
-
end
|
|
682
|
-
|
|
683
|
-
sql_with_returning = sql + @connection.returning_clause(quote_column_name(pk))
|
|
684
|
-
log(sql, name) do
|
|
685
|
-
@connection.exec_with_returning(sql_with_returning)
|
|
686
|
-
end
|
|
687
|
-
end
|
|
688
|
-
protected :insert_sql
|
|
689
|
-
|
|
690
|
-
# New method in ActiveRecord 3.1
|
|
691
|
-
# Will add RETURNING clause in case of trigger generated primary keys
|
|
692
|
-
def sql_for_insert(sql, pk, id_value, sequence_name, binds)
|
|
693
|
-
unless id_value || pk.nil? || (defined?(CompositePrimaryKeys) && pk.kind_of?(CompositePrimaryKeys::CompositeKeys))
|
|
694
|
-
sql = "#{sql} RETURNING #{quote_column_name(pk)} INTO :returning_id"
|
|
695
|
-
returning_id_col = OracleEnhancedColumn.new("returning_id", nil, "number", true, "dual", :integer, true, true)
|
|
696
|
-
(binds = binds.dup) << [returning_id_col, nil]
|
|
697
|
-
end
|
|
698
|
-
[sql, binds]
|
|
699
|
-
end
|
|
700
|
-
|
|
701
|
-
# New method in ActiveRecord 3.1
|
|
702
|
-
def exec_insert(sql, name, binds)
|
|
703
|
-
log(sql, name, binds) do
|
|
704
|
-
returning_id_col = returning_id_index = nil
|
|
705
|
-
cursor = if @statements.key?(sql)
|
|
706
|
-
@statements[sql]
|
|
707
|
-
else
|
|
708
|
-
@statements[sql] = @connection.prepare(sql)
|
|
709
|
-
end
|
|
710
|
-
|
|
711
|
-
binds.each_with_index do |bind, i|
|
|
712
|
-
col, val = bind
|
|
713
|
-
if col.returning_id?
|
|
714
|
-
returning_id_col = [col]
|
|
715
|
-
returning_id_index = i + 1
|
|
716
|
-
cursor.bind_returning_param(returning_id_index, Integer)
|
|
717
|
-
else
|
|
718
|
-
cursor.bind_param(i + 1, type_cast(val, col), col)
|
|
719
|
-
end
|
|
720
|
-
end
|
|
721
|
-
|
|
722
|
-
cursor.exec_update
|
|
723
|
-
|
|
724
|
-
rows = []
|
|
725
|
-
if returning_id_index
|
|
726
|
-
returning_id = cursor.get_returning_param(returning_id_index, Integer)
|
|
727
|
-
rows << [returning_id]
|
|
728
|
-
end
|
|
729
|
-
ActiveRecord::Result.new(returning_id_col || [], rows)
|
|
730
|
-
end
|
|
731
|
-
end
|
|
732
|
-
|
|
733
|
-
# New method in ActiveRecord 3.1
|
|
734
|
-
def exec_update(sql, name, binds)
|
|
735
|
-
log(sql, name, binds) do
|
|
736
|
-
cached = false
|
|
737
|
-
if binds.empty?
|
|
738
|
-
cursor = @connection.prepare(sql)
|
|
739
|
-
else
|
|
740
|
-
cursor = if @statements.key?(sql)
|
|
741
|
-
@statements[sql]
|
|
742
|
-
else
|
|
743
|
-
@statements[sql] = @connection.prepare(sql)
|
|
744
|
-
end
|
|
745
|
-
|
|
746
|
-
binds.each_with_index do |bind, i|
|
|
747
|
-
col, val = bind
|
|
748
|
-
cursor.bind_param(i + 1, type_cast(val, col), col)
|
|
749
|
-
end
|
|
750
|
-
cached = true
|
|
751
|
-
end
|
|
752
|
-
|
|
753
|
-
res = cursor.exec_update
|
|
754
|
-
cursor.close unless cached
|
|
755
|
-
res
|
|
756
|
-
end
|
|
406
|
+
def discard!
|
|
407
|
+
@connection = nil
|
|
757
408
|
end
|
|
758
409
|
|
|
759
|
-
alias :exec_delete :exec_update
|
|
760
|
-
|
|
761
410
|
# use in set_sequence_name to avoid fetching primary key value from sequence
|
|
762
|
-
AUTOGENERATED_SEQUENCE_NAME =
|
|
411
|
+
AUTOGENERATED_SEQUENCE_NAME = "autogenerated".freeze
|
|
763
412
|
|
|
764
413
|
# Returns the next sequence value from a sequence generator. Not generally
|
|
765
414
|
# called directly; used by ActiveRecord to get the next primary key value
|
|
@@ -768,266 +417,81 @@ module ActiveRecord
|
|
|
768
417
|
# if sequence_name is set to :autogenerated then it means that primary key will be populated by trigger
|
|
769
418
|
return nil if sequence_name == AUTOGENERATED_SEQUENCE_NAME
|
|
770
419
|
# call directly connection method to avoid prepared statement which causes fetching of next sequence value twice
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
def begin_db_transaction #:nodoc:
|
|
775
|
-
@connection.autocommit = false
|
|
776
|
-
end
|
|
777
|
-
|
|
778
|
-
def commit_db_transaction #:nodoc:
|
|
779
|
-
@connection.commit
|
|
780
|
-
ensure
|
|
781
|
-
@connection.autocommit = true
|
|
782
|
-
end
|
|
783
|
-
|
|
784
|
-
def rollback_db_transaction #:nodoc:
|
|
785
|
-
@connection.rollback
|
|
786
|
-
ensure
|
|
787
|
-
@connection.autocommit = true
|
|
788
|
-
end
|
|
789
|
-
|
|
790
|
-
def create_savepoint #:nodoc:
|
|
791
|
-
execute("SAVEPOINT #{current_savepoint_name}")
|
|
792
|
-
end
|
|
793
|
-
|
|
794
|
-
def rollback_to_savepoint #:nodoc:
|
|
795
|
-
execute("ROLLBACK TO #{current_savepoint_name}")
|
|
796
|
-
end
|
|
797
|
-
|
|
798
|
-
def release_savepoint #:nodoc:
|
|
799
|
-
# there is no RELEASE SAVEPOINT statement in Oracle
|
|
800
|
-
end
|
|
801
|
-
|
|
802
|
-
def add_limit_offset!(sql, options) #:nodoc:
|
|
803
|
-
# added to_i for limit and offset to protect from SQL injection
|
|
804
|
-
offset = (options[:offset] || 0).to_i
|
|
805
|
-
limit = options[:limit]
|
|
806
|
-
limit = limit.is_a?(String) && limit.blank? ? nil : limit && limit.to_i
|
|
807
|
-
if limit && offset > 0
|
|
808
|
-
sql.replace "SELECT * FROM (SELECT raw_sql_.*, ROWNUM raw_rnum_ FROM (#{sql}) raw_sql_ WHERE ROWNUM <= #{offset+limit}) WHERE raw_rnum_ > #{offset}"
|
|
809
|
-
elsif limit
|
|
810
|
-
sql.replace "SELECT * FROM (#{sql}) WHERE ROWNUM <= #{limit}"
|
|
811
|
-
elsif offset > 0
|
|
812
|
-
sql.replace "SELECT * FROM (SELECT raw_sql_.*, ROWNUM raw_rnum_ FROM (#{sql}) raw_sql_) WHERE raw_rnum_ > #{offset}"
|
|
813
|
-
end
|
|
420
|
+
select_value(<<-SQL.strip.gsub(/\s+/, " "), "next sequence value")
|
|
421
|
+
SELECT #{quote_table_name(sequence_name)}.NEXTVAL FROM dual
|
|
422
|
+
SQL
|
|
814
423
|
end
|
|
815
424
|
|
|
816
|
-
@@do_not_prefetch_primary_key = {}
|
|
817
|
-
|
|
818
425
|
# Returns true for Oracle adapter (since Oracle requires primary key
|
|
819
426
|
# values to be pre-fetched before insert). See also #next_sequence_value.
|
|
820
427
|
def prefetch_primary_key?(table_name = nil)
|
|
821
428
|
return true if table_name.nil?
|
|
822
429
|
table_name = table_name.to_s
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
owner, desc_table_name, db_link = @connection.describe(table_name)
|
|
826
|
-
@@do_not_prefetch_primary_key[table_name] = do_not_prefetch =
|
|
827
|
-
!has_primary_key?(table_name, owner, desc_table_name, db_link) ||
|
|
828
|
-
has_primary_key_trigger?(table_name, owner, desc_table_name, db_link)
|
|
829
|
-
end
|
|
430
|
+
owner, desc_table_name, db_link = @connection.describe(table_name)
|
|
431
|
+
do_not_prefetch = !has_primary_key?(table_name, owner, desc_table_name, db_link) || has_primary_key_trigger?(table_name, owner, desc_table_name, db_link)
|
|
830
432
|
!do_not_prefetch
|
|
831
433
|
end
|
|
832
434
|
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
# Inserts the given fixture into the table. Overridden to properly handle lobs.
|
|
847
|
-
def insert_fixture(fixture, table_name) #:nodoc:
|
|
848
|
-
super
|
|
849
|
-
|
|
850
|
-
if ActiveRecord::Base.pluralize_table_names
|
|
851
|
-
klass = table_name.singularize.camelize
|
|
852
|
-
else
|
|
853
|
-
klass = table_name.camelize
|
|
435
|
+
def reset_pk_sequence!(table_name, primary_key = nil, sequence_name = nil) #:nodoc:
|
|
436
|
+
return nil unless data_source_exists?(table_name)
|
|
437
|
+
unless primary_key && sequence_name
|
|
438
|
+
# *Note*: Only primary key is implemented - sequence will be nil.
|
|
439
|
+
primary_key, sequence_name = pk_and_sequence_for(table_name)
|
|
440
|
+
# TODO This sequence_name implemantation is just enough
|
|
441
|
+
# to satisty fixures. To get correct sequence_name always
|
|
442
|
+
# pk_and_sequence_for method needs some work.
|
|
443
|
+
begin
|
|
444
|
+
sequence_name = table_name.classify.constantize.sequence_name
|
|
445
|
+
rescue
|
|
446
|
+
sequence_name = default_sequence_name(table_name)
|
|
447
|
+
end
|
|
854
448
|
end
|
|
855
449
|
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
write_lobs(table_name, klass, fixture, klass.lob_columns)
|
|
450
|
+
if @logger && primary_key && !sequence_name
|
|
451
|
+
@logger.warn "#{table_name} has primary key #{primary_key} with no default sequence"
|
|
859
452
|
end
|
|
860
|
-
end
|
|
861
453
|
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
id = quote(attributes[klass.primary_key])
|
|
870
|
-
end
|
|
871
|
-
columns.each do |col|
|
|
872
|
-
value = attributes[col.name]
|
|
873
|
-
# changed sequence of next two lines - should check if value is nil before converting to yaml
|
|
874
|
-
next if value.nil? || (value == '')
|
|
875
|
-
value = value.to_yaml if col.text? && klass.serialized_attributes[col.name]
|
|
876
|
-
uncached do
|
|
877
|
-
sql = is_with_cpk ? "SELECT #{quote_column_name(col.name)} FROM #{quote_table_name(table_name)} WHERE #{klass.composite_where_clause(id)} FOR UPDATE" :
|
|
878
|
-
"SELECT #{quote_column_name(col.name)} FROM #{quote_table_name(table_name)} WHERE #{quote_column_name(klass.primary_key)} = #{id} FOR UPDATE"
|
|
879
|
-
unless lob_record = select_one(sql, 'Writable Large Object')
|
|
880
|
-
raise ActiveRecord::RecordNotFound, "statement #{sql} returned no rows"
|
|
881
|
-
end
|
|
882
|
-
lob = lob_record[col.name]
|
|
883
|
-
@connection.write_lob(lob, value.to_s, col.type == :binary)
|
|
884
|
-
end
|
|
454
|
+
if primary_key && sequence_name
|
|
455
|
+
new_start_value = select_value(<<-SQL.strip.gsub(/\s+/, " "), "new start value")
|
|
456
|
+
select NVL(max(#{quote_column_name(primary_key)}),0) + 1 from #{quote_table_name(table_name)}
|
|
457
|
+
SQL
|
|
458
|
+
|
|
459
|
+
execute "DROP SEQUENCE #{quote_table_name(sequence_name)}"
|
|
460
|
+
execute "CREATE SEQUENCE #{quote_table_name(sequence_name)} START WITH #{new_start_value}"
|
|
885
461
|
end
|
|
886
462
|
end
|
|
887
463
|
|
|
888
464
|
# Current database name
|
|
889
465
|
def current_database
|
|
890
|
-
select_value("
|
|
466
|
+
select_value(<<-SQL.strip.gsub(/\s+/, " "), "current database on CDB ")
|
|
467
|
+
SELECT SYS_CONTEXT('userenv', 'con_name') FROM dual
|
|
468
|
+
SQL
|
|
469
|
+
rescue ActiveRecord::StatementInvalid
|
|
470
|
+
select_value(<<-SQL.strip.gsub(/\s+/, " "), "current database")
|
|
471
|
+
SELECT SYS_CONTEXT('userenv', 'db_name') FROM dual
|
|
472
|
+
SQL
|
|
891
473
|
end
|
|
892
474
|
|
|
893
475
|
# Current database session user
|
|
894
476
|
def current_user
|
|
895
|
-
select_value("
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
# Default tablespace name of current user
|
|
899
|
-
def default_tablespace
|
|
900
|
-
select_value("SELECT LOWER(default_tablespace) FROM user_users WHERE username = SYS_CONTEXT('userenv', 'session_user')")
|
|
901
|
-
end
|
|
902
|
-
|
|
903
|
-
def tables(name = nil) #:nodoc:
|
|
904
|
-
select_values(
|
|
905
|
-
"SELECT DECODE(table_name, UPPER(table_name), LOWER(table_name), table_name) FROM all_tables WHERE owner = SYS_CONTEXT('userenv', 'session_user') AND secondary = 'N'",
|
|
906
|
-
name)
|
|
907
|
-
end
|
|
908
|
-
|
|
909
|
-
# Will return true if database object exists (to be able to use also views and synonyms for ActiveRecord models)
|
|
910
|
-
def table_exists?(table_name)
|
|
911
|
-
(owner, table_name, db_link) = @connection.describe(table_name)
|
|
912
|
-
true
|
|
913
|
-
rescue
|
|
914
|
-
false
|
|
915
|
-
end
|
|
916
|
-
|
|
917
|
-
def materialized_views #:nodoc:
|
|
918
|
-
select_values("SELECT LOWER(mview_name) FROM all_mviews WHERE owner = SYS_CONTEXT('userenv', 'session_user')")
|
|
919
|
-
end
|
|
920
|
-
|
|
921
|
-
cattr_accessor :all_schema_indexes #:nodoc:
|
|
922
|
-
|
|
923
|
-
# This method selects all indexes at once, and caches them in a class variable.
|
|
924
|
-
# Subsequent index calls get them from the variable, without going to the DB.
|
|
925
|
-
def indexes(table_name, name = nil) #:nodoc:
|
|
926
|
-
(owner, table_name, db_link) = @connection.describe(table_name)
|
|
927
|
-
unless all_schema_indexes
|
|
928
|
-
default_tablespace_name = default_tablespace
|
|
929
|
-
result = select_all(<<-SQL.strip.gsub(/\s+/, ' '))
|
|
930
|
-
SELECT LOWER(i.table_name) AS table_name, LOWER(i.index_name) AS index_name, i.uniqueness,
|
|
931
|
-
i.index_type, i.ityp_owner, i.ityp_name, i.parameters,
|
|
932
|
-
LOWER(i.tablespace_name) AS tablespace_name,
|
|
933
|
-
LOWER(c.column_name) AS column_name, e.column_expression,
|
|
934
|
-
atc.virtual_column
|
|
935
|
-
FROM all_indexes#{db_link} i
|
|
936
|
-
JOIN all_ind_columns#{db_link} c ON c.index_name = i.index_name AND c.index_owner = i.owner
|
|
937
|
-
LEFT OUTER JOIN all_ind_expressions#{db_link} e ON e.index_name = i.index_name AND
|
|
938
|
-
e.index_owner = i.owner AND e.column_position = c.column_position
|
|
939
|
-
LEFT OUTER JOIN all_tab_cols#{db_link} atc ON i.table_name = atc.table_name AND
|
|
940
|
-
c.column_name = atc.column_name AND i.owner = atc.owner AND atc.hidden_column = 'NO'
|
|
941
|
-
WHERE i.owner = '#{owner}'
|
|
942
|
-
AND i.table_owner = '#{owner}'
|
|
943
|
-
AND NOT EXISTS (SELECT uc.index_name FROM all_constraints uc
|
|
944
|
-
WHERE uc.index_name = i.index_name AND uc.owner = i.owner AND uc.constraint_type = 'P')
|
|
945
|
-
ORDER BY i.index_name, c.column_position
|
|
946
|
-
SQL
|
|
947
|
-
|
|
948
|
-
current_index = nil
|
|
949
|
-
self.all_schema_indexes = []
|
|
950
|
-
|
|
951
|
-
result.each do |row|
|
|
952
|
-
# have to keep track of indexes because above query returns dups
|
|
953
|
-
# there is probably a better query we could figure out
|
|
954
|
-
if current_index != row['index_name']
|
|
955
|
-
statement_parameters = nil
|
|
956
|
-
if row['index_type'] == 'DOMAIN' && row['ityp_owner'] == 'CTXSYS' && row['ityp_name'] == 'CONTEXT'
|
|
957
|
-
procedure_name = default_datastore_procedure(row['index_name'])
|
|
958
|
-
source = select_values(<<-SQL).join
|
|
959
|
-
SELECT text
|
|
960
|
-
FROM all_source#{db_link}
|
|
961
|
-
WHERE owner = '#{owner}'
|
|
962
|
-
AND name = '#{procedure_name.upcase}'
|
|
963
|
-
ORDER BY line
|
|
964
|
-
SQL
|
|
965
|
-
if source =~ /-- add_context_index_parameters (.+)\n/
|
|
966
|
-
statement_parameters = $1
|
|
967
|
-
end
|
|
968
|
-
end
|
|
969
|
-
all_schema_indexes << OracleEnhancedIndexDefinition.new(row['table_name'], row['index_name'],
|
|
970
|
-
row['uniqueness'] == "UNIQUE", row['index_type'] == 'DOMAIN' ? "#{row['ityp_owner']}.#{row['ityp_name']}" : nil,
|
|
971
|
-
row['parameters'], statement_parameters,
|
|
972
|
-
row['tablespace_name'] == default_tablespace_name ? nil : row['tablespace_name'], [])
|
|
973
|
-
current_index = row['index_name']
|
|
974
|
-
end
|
|
975
|
-
|
|
976
|
-
# Functional index columns and virtual columns both get stored as column expressions,
|
|
977
|
-
# but re-creating a virtual column index as an expression (instead of using the virtual column's name)
|
|
978
|
-
# results in a ORA-54018 error. Thus, we only want the column expression value returned
|
|
979
|
-
# when the column is not virtual.
|
|
980
|
-
if row['column_expression'] && row['virtual_column'] != 'YES'
|
|
981
|
-
all_schema_indexes.last.columns << row['column_expression']
|
|
982
|
-
else
|
|
983
|
-
all_schema_indexes.last.columns << row['column_name'].downcase
|
|
984
|
-
end
|
|
985
|
-
end
|
|
986
|
-
end
|
|
987
|
-
|
|
988
|
-
# Return the indexes just for the requested table, since AR is structured that way
|
|
989
|
-
table_name = table_name.downcase
|
|
990
|
-
all_schema_indexes.select{|i| i.table == table_name}
|
|
991
|
-
end
|
|
992
|
-
|
|
993
|
-
@@ignore_table_columns = nil #:nodoc:
|
|
994
|
-
|
|
995
|
-
# set ignored columns for table
|
|
996
|
-
def ignore_table_columns(table_name, *args) #:nodoc:
|
|
997
|
-
@@ignore_table_columns ||= {}
|
|
998
|
-
@@ignore_table_columns[table_name] ||= []
|
|
999
|
-
@@ignore_table_columns[table_name] += args.map{|a| a.to_s.downcase}
|
|
1000
|
-
@@ignore_table_columns[table_name].uniq!
|
|
1001
|
-
end
|
|
1002
|
-
|
|
1003
|
-
def ignored_table_columns(table_name) #:nodoc:
|
|
1004
|
-
@@ignore_table_columns ||= {}
|
|
1005
|
-
@@ignore_table_columns[table_name]
|
|
1006
|
-
end
|
|
1007
|
-
|
|
1008
|
-
# used just in tests to clear ignored table columns
|
|
1009
|
-
def clear_ignored_table_columns #:nodoc:
|
|
1010
|
-
@@ignore_table_columns = nil
|
|
1011
|
-
end
|
|
1012
|
-
|
|
1013
|
-
@@table_column_type = nil #:nodoc:
|
|
1014
|
-
|
|
1015
|
-
# set explicit type for specified table columns
|
|
1016
|
-
def set_type_for_columns(table_name, column_type, *args) #:nodoc:
|
|
1017
|
-
@@table_column_type ||= {}
|
|
1018
|
-
@@table_column_type[table_name] ||= {}
|
|
1019
|
-
args.each do |col|
|
|
1020
|
-
@@table_column_type[table_name][col.to_s.downcase] = column_type
|
|
1021
|
-
end
|
|
477
|
+
select_value(<<-SQL.strip.gsub(/\s+/, " "), "current user")
|
|
478
|
+
SELECT SYS_CONTEXT('userenv', 'session_user') FROM dual
|
|
479
|
+
SQL
|
|
1022
480
|
end
|
|
1023
481
|
|
|
1024
|
-
|
|
1025
|
-
|
|
482
|
+
# Current database session schema
|
|
483
|
+
def current_schema
|
|
484
|
+
select_value(<<-SQL.strip.gsub(/\s+/, " "), "current schema")
|
|
485
|
+
SELECT SYS_CONTEXT('userenv', 'current_schema') FROM dual
|
|
486
|
+
SQL
|
|
1026
487
|
end
|
|
1027
488
|
|
|
1028
|
-
#
|
|
1029
|
-
def
|
|
1030
|
-
|
|
489
|
+
# Default tablespace name of current user
|
|
490
|
+
def default_tablespace
|
|
491
|
+
select_value(<<-SQL.strip.gsub(/\s+/, " "), "default tablespace")
|
|
492
|
+
SELECT LOWER(default_tablespace) FROM user_users
|
|
493
|
+
WHERE username = SYS_CONTEXT('userenv', 'current_schema')
|
|
494
|
+
SQL
|
|
1031
495
|
end
|
|
1032
496
|
|
|
1033
497
|
# check if table has primary key trigger with _pkt suffix
|
|
@@ -1035,152 +499,76 @@ module ActiveRecord
|
|
|
1035
499
|
(owner, desc_table_name, db_link) = @connection.describe(table_name) unless owner
|
|
1036
500
|
|
|
1037
501
|
trigger_name = default_trigger_name(table_name).upcase
|
|
1038
|
-
|
|
502
|
+
|
|
503
|
+
!!select_value(<<-SQL.strip.gsub(/\s+/, " "), "Primary Key Trigger", [bind_string("owner", owner), bind_string("trigger_name", trigger_name), bind_string("owner", owner), bind_string("table_name", desc_table_name)])
|
|
1039
504
|
SELECT trigger_name
|
|
1040
505
|
FROM all_triggers#{db_link}
|
|
1041
|
-
WHERE owner =
|
|
1042
|
-
AND trigger_name =
|
|
1043
|
-
AND table_owner =
|
|
1044
|
-
AND table_name =
|
|
506
|
+
WHERE owner = :owner
|
|
507
|
+
AND trigger_name = :trigger_name
|
|
508
|
+
AND table_owner = :owner
|
|
509
|
+
AND table_name = :table_name
|
|
1045
510
|
AND status = 'ENABLED'
|
|
1046
511
|
SQL
|
|
1047
|
-
select_value(pkt_sql, 'Primary Key Trigger') ? true : false
|
|
1048
|
-
end
|
|
1049
|
-
|
|
1050
|
-
##
|
|
1051
|
-
# :singleton-method:
|
|
1052
|
-
# Cache column description between requests.
|
|
1053
|
-
# Could be used in development environment to avoid selecting table columns from data dictionary tables for each request.
|
|
1054
|
-
# This can speed up request processing in development mode if development database is not on local computer.
|
|
1055
|
-
#
|
|
1056
|
-
# ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.cache_columns = true
|
|
1057
|
-
cattr_accessor :cache_columns
|
|
1058
|
-
self.cache_columns = false
|
|
1059
|
-
|
|
1060
|
-
def columns(table_name, name = nil) #:nodoc:
|
|
1061
|
-
if @@cache_columns
|
|
1062
|
-
@@columns_cache ||= {}
|
|
1063
|
-
@@columns_cache[table_name] ||= columns_without_cache(table_name, name)
|
|
1064
|
-
else
|
|
1065
|
-
columns_without_cache(table_name, name)
|
|
1066
|
-
end
|
|
1067
512
|
end
|
|
1068
513
|
|
|
1069
|
-
def
|
|
1070
|
-
table_name = table_name.to_s
|
|
1071
|
-
# get ignored_columns by original table name
|
|
1072
|
-
ignored_columns = ignored_table_columns(table_name)
|
|
1073
|
-
|
|
514
|
+
def column_definitions(table_name)
|
|
1074
515
|
(owner, desc_table_name, db_link) = @connection.describe(table_name)
|
|
1075
516
|
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
DECODE(data_type, 'NUMBER', data_precision,
|
|
517
|
+
select_all(<<-SQL.strip.gsub(/\s+/, " "), "Column definitions", [bind_string("owner", owner), bind_string("table_name", desc_table_name)])
|
|
518
|
+
SELECT cols.column_name AS name, cols.data_type AS sql_type,
|
|
519
|
+
cols.data_default, cols.nullable, cols.virtual_column, cols.hidden_column,
|
|
520
|
+
cols.data_type_owner AS sql_type_owner,
|
|
521
|
+
DECODE(cols.data_type, 'NUMBER', data_precision,
|
|
1082
522
|
'FLOAT', data_precision,
|
|
1083
523
|
'VARCHAR2', DECODE(char_used, 'C', char_length, data_length),
|
|
1084
524
|
'RAW', DECODE(char_used, 'C', char_length, data_length),
|
|
1085
525
|
'CHAR', DECODE(char_used, 'C', char_length, data_length),
|
|
1086
526
|
NULL) AS limit,
|
|
1087
|
-
DECODE(data_type, 'NUMBER', data_scale, NULL) AS scale
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
AND
|
|
1092
|
-
|
|
527
|
+
DECODE(data_type, 'NUMBER', data_scale, NULL) AS scale,
|
|
528
|
+
comments.comments as column_comment
|
|
529
|
+
FROM all_tab_cols#{db_link} cols, all_col_comments#{db_link} comments
|
|
530
|
+
WHERE cols.owner = :owner
|
|
531
|
+
AND cols.table_name = :table_name
|
|
532
|
+
AND cols.hidden_column = 'NO'
|
|
533
|
+
AND cols.owner = comments.owner
|
|
534
|
+
AND cols.table_name = comments.table_name
|
|
535
|
+
AND cols.column_name = comments.column_name
|
|
536
|
+
ORDER BY cols.column_id
|
|
1093
537
|
SQL
|
|
1094
|
-
|
|
1095
|
-
# added deletion of ignored columns
|
|
1096
|
-
select_all(table_cols, name).delete_if do |row|
|
|
1097
|
-
ignored_columns && ignored_columns.include?(row['name'].downcase)
|
|
1098
|
-
end.map do |row|
|
|
1099
|
-
limit, scale = row['limit'], row['scale']
|
|
1100
|
-
if limit || scale
|
|
1101
|
-
row['sql_type'] += "(#{(limit || 38).to_i}" + ((scale = scale.to_i) > 0 ? ",#{scale})" : ")")
|
|
1102
|
-
end
|
|
1103
|
-
|
|
1104
|
-
if row['sql_type_owner']
|
|
1105
|
-
row['sql_type'] = row['sql_type_owner'] + '.' + row['sql_type']
|
|
1106
|
-
end
|
|
1107
|
-
|
|
1108
|
-
is_virtual = row['virtual_column']=='YES'
|
|
1109
|
-
|
|
1110
|
-
# clean up odd default spacing from Oracle
|
|
1111
|
-
if row['data_default'] && !is_virtual
|
|
1112
|
-
row['data_default'].sub!(/^(.*?)\s*$/, '\1')
|
|
1113
|
-
|
|
1114
|
-
# If a default contains a newline these cleanup regexes need to
|
|
1115
|
-
# match newlines.
|
|
1116
|
-
row['data_default'].sub!(/^'(.*)'$/m, '\1')
|
|
1117
|
-
row['data_default'] = nil if row['data_default'] =~ /^(null|empty_[bc]lob\(\))$/i
|
|
1118
|
-
end
|
|
1119
|
-
|
|
1120
|
-
OracleEnhancedColumn.new(oracle_downcase(row['name']),
|
|
1121
|
-
row['data_default'],
|
|
1122
|
-
row['sql_type'],
|
|
1123
|
-
row['nullable'] == 'Y',
|
|
1124
|
-
# pass table name for table specific column definitions
|
|
1125
|
-
table_name,
|
|
1126
|
-
# pass column type if specified in class definition
|
|
1127
|
-
get_type_for_column(table_name, oracle_downcase(row['name'])), is_virtual)
|
|
1128
|
-
end
|
|
1129
|
-
end
|
|
1130
|
-
|
|
1131
|
-
# used just in tests to clear column cache
|
|
1132
|
-
def clear_columns_cache #:nodoc:
|
|
1133
|
-
@@columns_cache = nil
|
|
1134
|
-
@@pk_and_sequence_for_cache = nil
|
|
1135
538
|
end
|
|
1136
539
|
|
|
1137
|
-
# used in migrations to clear column cache for specified table
|
|
1138
|
-
def clear_table_columns_cache(table_name)
|
|
1139
|
-
if @@cache_columns
|
|
1140
|
-
@@columns_cache ||= {}
|
|
1141
|
-
@@columns_cache[table_name.to_s] = nil
|
|
1142
|
-
end
|
|
1143
|
-
end
|
|
1144
|
-
|
|
1145
|
-
##
|
|
1146
|
-
# :singleton-method:
|
|
1147
|
-
# Specify default sequence start with value (by default 10000 if not explicitly set), e.g.:
|
|
1148
|
-
#
|
|
1149
|
-
# ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.default_sequence_start_value = 1
|
|
1150
|
-
cattr_accessor :default_sequence_start_value
|
|
1151
|
-
self.default_sequence_start_value = 10000
|
|
1152
|
-
|
|
1153
540
|
# Find a table's primary key and sequence.
|
|
1154
541
|
# *Note*: Only primary key is implemented - sequence will be nil.
|
|
1155
|
-
def pk_and_sequence_for(table_name, owner=nil, desc_table_name=nil, db_link=nil) #:nodoc:
|
|
1156
|
-
if @@cache_columns
|
|
1157
|
-
@@pk_and_sequence_for_cache ||= {}
|
|
1158
|
-
if @@pk_and_sequence_for_cache.key?(table_name)
|
|
1159
|
-
@@pk_and_sequence_for_cache[table_name]
|
|
1160
|
-
else
|
|
1161
|
-
@@pk_and_sequence_for_cache[table_name] = pk_and_sequence_for_without_cache(table_name, owner, desc_table_name, db_link)
|
|
1162
|
-
end
|
|
1163
|
-
else
|
|
1164
|
-
pk_and_sequence_for_without_cache(table_name, owner, desc_table_name, db_link)
|
|
1165
|
-
end
|
|
1166
|
-
end
|
|
1167
|
-
|
|
1168
|
-
def pk_and_sequence_for_without_cache(table_name, owner=nil, desc_table_name=nil, db_link=nil) #:nodoc:
|
|
542
|
+
def pk_and_sequence_for(table_name, owner = nil, desc_table_name = nil, db_link = nil) #:nodoc:
|
|
1169
543
|
(owner, desc_table_name, db_link) = @connection.describe(table_name) unless owner
|
|
1170
544
|
|
|
545
|
+
seqs = select_values(<<-SQL.strip.gsub(/\s+/, " "), "Sequence", [bind_string("owner", owner), bind_string("sequence_name", default_sequence_name(desc_table_name).upcase)])
|
|
546
|
+
select us.sequence_name
|
|
547
|
+
from all_sequences#{db_link} us
|
|
548
|
+
where us.sequence_owner = :owner
|
|
549
|
+
and us.sequence_name = :sequence_name
|
|
550
|
+
SQL
|
|
551
|
+
|
|
1171
552
|
# changed back from user_constraints to all_constraints for consistency
|
|
1172
|
-
pks = select_values(<<-SQL.strip.gsub(/\s+/,
|
|
553
|
+
pks = select_values(<<-SQL.strip.gsub(/\s+/, " "), "Primary Key", [bind_string("owner", owner), bind_string("table_name", desc_table_name)])
|
|
1173
554
|
SELECT cc.column_name
|
|
1174
555
|
FROM all_constraints#{db_link} c, all_cons_columns#{db_link} cc
|
|
1175
|
-
WHERE c.owner =
|
|
1176
|
-
AND c.table_name =
|
|
556
|
+
WHERE c.owner = :owner
|
|
557
|
+
AND c.table_name = :table_name
|
|
1177
558
|
AND c.constraint_type = 'P'
|
|
1178
559
|
AND cc.owner = c.owner
|
|
1179
560
|
AND cc.constraint_name = c.constraint_name
|
|
1180
561
|
SQL
|
|
1181
562
|
|
|
563
|
+
warn <<-WARNING.strip_heredoc if pks.count > 1
|
|
564
|
+
WARNING: Active Record does not support composite primary key.
|
|
565
|
+
|
|
566
|
+
#{table_name} has composite primary key. Composite primary key is ignored.
|
|
567
|
+
WARNING
|
|
568
|
+
|
|
1182
569
|
# only support single column keys
|
|
1183
|
-
pks.size == 1 ? [oracle_downcase(pks.first),
|
|
570
|
+
pks.size == 1 ? [oracle_downcase(pks.first),
|
|
571
|
+
oracle_downcase(seqs.first)] : nil
|
|
1184
572
|
end
|
|
1185
573
|
|
|
1186
574
|
# Returns just a table's primary key
|
|
@@ -1189,215 +577,129 @@ module ActiveRecord
|
|
|
1189
577
|
pk_and_sequence && pk_and_sequence.first
|
|
1190
578
|
end
|
|
1191
579
|
|
|
1192
|
-
def has_primary_key?(table_name, owner=nil, desc_table_name=nil, db_link=nil) #:nodoc:
|
|
580
|
+
def has_primary_key?(table_name, owner = nil, desc_table_name = nil, db_link = nil) #:nodoc:
|
|
1193
581
|
!pk_and_sequence_for(table_name, owner, desc_table_name, db_link).nil?
|
|
1194
582
|
end
|
|
1195
583
|
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
# Oracle requires the ORDER BY columns to be in the SELECT list for DISTINCT
|
|
1199
|
-
# queries. However, with those columns included in the SELECT DISTINCT list, you
|
|
1200
|
-
# won't actually get a distinct list of the column you want (presuming the column
|
|
1201
|
-
# has duplicates with multiple values for the ordered-by columns. So we use the
|
|
1202
|
-
# FIRST_VALUE function to get a single (first) value for each column, effectively
|
|
1203
|
-
# making every row the same.
|
|
1204
|
-
#
|
|
1205
|
-
# distinct("posts.id", "posts.created_at desc")
|
|
1206
|
-
def distinct(columns, order_by) #:nodoc:
|
|
1207
|
-
return "DISTINCT #{columns}" if order_by.blank?
|
|
1208
|
-
|
|
1209
|
-
# construct a valid DISTINCT clause, ie. one that includes the ORDER BY columns, using
|
|
1210
|
-
# FIRST_VALUE such that the inclusion of these columns doesn't invalidate the DISTINCT
|
|
1211
|
-
order_columns = if order_by.is_a?(String)
|
|
1212
|
-
order_by.split(',').map { |s| s.strip }.reject(&:blank?)
|
|
1213
|
-
else # in latest ActiveRecord versions order_by is already Array
|
|
1214
|
-
order_by
|
|
1215
|
-
end
|
|
1216
|
-
order_columns = order_columns.zip((0...order_columns.size).to_a).map do |c, i|
|
|
1217
|
-
# remove any ASC/DESC modifiers
|
|
1218
|
-
value = c =~ /^(.+)\s+(ASC|DESC)\s*$/i ? $1 : c
|
|
1219
|
-
"FIRST_VALUE(#{value}) OVER (PARTITION BY #{columns} ORDER BY #{c}) AS alias_#{i}__"
|
|
1220
|
-
end
|
|
1221
|
-
sql = "DISTINCT #{columns}, "
|
|
1222
|
-
sql << order_columns * ", "
|
|
1223
|
-
end
|
|
584
|
+
def primary_keys(table_name) # :nodoc:
|
|
585
|
+
(owner, desc_table_name, db_link) = @connection.describe(table_name) unless owner
|
|
1224
586
|
|
|
1225
|
-
|
|
1226
|
-
|
|
587
|
+
pks = select_values(<<-SQL.strip.gsub(/\s+/, " "), "Primary Keys", [bind_string("owner", owner), bind_string("table_name", desc_table_name)])
|
|
588
|
+
SELECT cc.column_name
|
|
589
|
+
FROM all_constraints#{db_link} c, all_cons_columns#{db_link} cc
|
|
590
|
+
WHERE c.owner = :owner
|
|
591
|
+
AND c.table_name = :table_name
|
|
592
|
+
AND c.constraint_type = 'P'
|
|
593
|
+
AND cc.owner = c.owner
|
|
594
|
+
AND cc.constraint_name = c.constraint_name
|
|
595
|
+
order by cc.position
|
|
596
|
+
SQL
|
|
597
|
+
pks.map { |pk| oracle_downcase(pk) }
|
|
1227
598
|
end
|
|
1228
599
|
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
600
|
+
def columns_for_distinct(columns, orders) #:nodoc:
|
|
601
|
+
# construct a valid columns name for DISTINCT clause,
|
|
602
|
+
# ie. one that includes the ORDER BY columns, using FIRST_VALUE such that
|
|
603
|
+
# the inclusion of these columns doesn't invalidate the DISTINCT
|
|
604
|
+
#
|
|
605
|
+
# It does not construct DISTINCT clause. Just return column names for distinct.
|
|
606
|
+
order_columns = orders.reject(&:blank?).map { |s|
|
|
607
|
+
s = s.to_sql unless s.is_a?(String)
|
|
608
|
+
# remove any ASC/DESC modifiers
|
|
609
|
+
s.gsub(/\s+(ASC|DESC)\s*?/i, "")
|
|
610
|
+
}.reject(&:blank?).map.with_index { |column, i|
|
|
611
|
+
"FIRST_VALUE(#{column}) OVER (PARTITION BY #{columns} ORDER BY #{column}) AS alias_#{i}__"
|
|
612
|
+
}
|
|
613
|
+
[super, *order_columns].join(", ")
|
|
1242
614
|
end
|
|
1243
615
|
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
subsubselect = select.clone
|
|
1249
|
-
subsubselect.projections = [update.key]
|
|
1250
|
-
|
|
1251
|
-
subselect = Arel::SelectManager.new(select.engine)
|
|
1252
|
-
subselect.project Arel.sql(quote_column_name update.key.name)
|
|
1253
|
-
subselect.from subsubselect.as('alias_join_to_update')
|
|
1254
|
-
|
|
1255
|
-
update.where update.key.in(subselect)
|
|
1256
|
-
else
|
|
1257
|
-
super
|
|
1258
|
-
end
|
|
616
|
+
def temporary_table?(table_name) #:nodoc:
|
|
617
|
+
select_value(<<-SQL.strip.gsub(/\s+/, " "), "temporary table", [bind_string("table_name", table_name.upcase)]) == "Y"
|
|
618
|
+
SELECT temporary FROM all_tables WHERE table_name = :table_name and owner = SYS_CONTEXT('userenv', 'session_user')
|
|
619
|
+
SQL
|
|
1259
620
|
end
|
|
1260
621
|
|
|
1261
|
-
|
|
622
|
+
private
|
|
1262
623
|
|
|
1263
|
-
|
|
1264
|
-
case @connection.error_code(exception)
|
|
1265
|
-
when 1
|
|
1266
|
-
RecordNotUnique.new(message, exception)
|
|
1267
|
-
when 2291
|
|
1268
|
-
InvalidForeignKey.new(message, exception)
|
|
1269
|
-
else
|
|
624
|
+
def initialize_type_map(m = type_map)
|
|
1270
625
|
super
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
626
|
+
# oracle
|
|
627
|
+
register_class_with_precision m, %r(WITH TIME ZONE)i, Type::OracleEnhanced::TimestampTz
|
|
628
|
+
register_class_with_precision m, %r(WITH LOCAL TIME ZONE)i, Type::OracleEnhanced::TimestampLtz
|
|
629
|
+
register_class_with_limit m, %r(raw)i, Type::OracleEnhanced::Raw
|
|
630
|
+
register_class_with_limit m, %r(char)i, Type::OracleEnhanced::String
|
|
631
|
+
register_class_with_limit m, %r(clob)i, Type::OracleEnhanced::Text
|
|
632
|
+
register_class_with_limit m, %r(nclob)i, Type::OracleEnhanced::NationalCharacterText
|
|
633
|
+
|
|
634
|
+
m.register_type "NCHAR", Type::OracleEnhanced::NationalCharacterString.new
|
|
635
|
+
m.alias_type %r(NVARCHAR2)i, "NCHAR"
|
|
636
|
+
|
|
637
|
+
m.register_type(%r(NUMBER)i) do |sql_type|
|
|
638
|
+
scale = extract_scale(sql_type)
|
|
639
|
+
precision = extract_precision(sql_type)
|
|
640
|
+
limit = extract_limit(sql_type)
|
|
641
|
+
if scale == 0
|
|
642
|
+
Type::OracleEnhanced::Integer.new(precision: precision, limit: limit)
|
|
643
|
+
else
|
|
644
|
+
Type::Decimal.new(precision: precision, scale: scale)
|
|
645
|
+
end
|
|
646
|
+
end
|
|
1275
647
|
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
exec_query(sql, name, binds).to_a
|
|
1279
|
-
else
|
|
1280
|
-
log(sql, name) do
|
|
1281
|
-
@connection.select(sql, name, false)
|
|
648
|
+
if OracleEnhancedAdapter.emulate_booleans
|
|
649
|
+
m.register_type %r(^NUMBER\(1\))i, Type::Boolean.new
|
|
1282
650
|
end
|
|
1283
651
|
end
|
|
1284
|
-
end
|
|
1285
|
-
|
|
1286
|
-
def oracle_downcase(column_name)
|
|
1287
|
-
@connection.oracle_downcase(column_name)
|
|
1288
|
-
end
|
|
1289
652
|
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
# PL/SQL in Oracle uses dbms_output for logging print statements
|
|
1298
|
-
# These methods stick that output into the Rails log so Ruby and PL/SQL
|
|
1299
|
-
# code can can be debugged together in a single application
|
|
1300
|
-
|
|
1301
|
-
# Maximum DBMS_OUTPUT buffer size
|
|
1302
|
-
DBMS_OUTPUT_BUFFER_SIZE = 10000 # can be 1-1000000
|
|
1303
|
-
|
|
1304
|
-
# Turn DBMS_Output logging on
|
|
1305
|
-
def enable_dbms_output
|
|
1306
|
-
set_dbms_output_plsql_connection
|
|
1307
|
-
@enable_dbms_output = true
|
|
1308
|
-
plsql(:dbms_output).sys.dbms_output.enable(DBMS_OUTPUT_BUFFER_SIZE)
|
|
1309
|
-
end
|
|
1310
|
-
# Turn DBMS_Output logging off
|
|
1311
|
-
def disable_dbms_output
|
|
1312
|
-
set_dbms_output_plsql_connection
|
|
1313
|
-
@enable_dbms_output = false
|
|
1314
|
-
plsql(:dbms_output).sys.dbms_output.disable
|
|
1315
|
-
end
|
|
1316
|
-
# Is DBMS_Output logging enabled?
|
|
1317
|
-
def dbms_output_enabled?
|
|
1318
|
-
@enable_dbms_output
|
|
1319
|
-
end
|
|
1320
|
-
|
|
1321
|
-
protected
|
|
1322
|
-
def log(sql, name, binds = nil) #:nodoc:
|
|
1323
|
-
if binds
|
|
1324
|
-
super sql, name, binds
|
|
1325
|
-
else
|
|
1326
|
-
super sql, name
|
|
653
|
+
def extract_value_from_default(default)
|
|
654
|
+
case default
|
|
655
|
+
when String
|
|
656
|
+
default.gsub("''", "'")
|
|
657
|
+
else
|
|
658
|
+
default
|
|
659
|
+
end
|
|
1327
660
|
end
|
|
1328
|
-
ensure
|
|
1329
|
-
log_dbms_output if dbms_output_enabled?
|
|
1330
|
-
end
|
|
1331
661
|
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
662
|
+
def extract_limit(sql_type) #:nodoc:
|
|
663
|
+
case sql_type
|
|
664
|
+
when /^bigint/i
|
|
665
|
+
19
|
|
666
|
+
when /\((.*)\)/
|
|
667
|
+
$1.to_i
|
|
668
|
+
end
|
|
1339
669
|
end
|
|
1340
|
-
end
|
|
1341
670
|
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
671
|
+
def translate_exception(exception, message) #:nodoc:
|
|
672
|
+
case @connection.error_code(exception)
|
|
673
|
+
when 1
|
|
674
|
+
RecordNotUnique.new(message)
|
|
675
|
+
when 60
|
|
676
|
+
Deadlocked.new(message)
|
|
677
|
+
when 900, 904, 942, 955, 1418, 2289, 2449, 17008
|
|
678
|
+
ActiveRecord::StatementInvalid.new(message)
|
|
679
|
+
when 1400
|
|
680
|
+
ActiveRecord::NotNullViolation.new(message)
|
|
681
|
+
when 2291
|
|
682
|
+
InvalidForeignKey.new(message)
|
|
683
|
+
when 12899
|
|
684
|
+
ValueTooLong.new(message)
|
|
685
|
+
else
|
|
686
|
+
super
|
|
687
|
+
end
|
|
1347
688
|
end
|
|
1348
|
-
end
|
|
1349
689
|
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
690
|
+
# create bind object for type String
|
|
691
|
+
def bind_string(name, value)
|
|
692
|
+
ActiveRecord::Relation::QueryAttribute.new(name, value, Type::OracleEnhanced::String.new)
|
|
693
|
+
end
|
|
1353
694
|
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
if defined?(CGI::Session::ActiveRecordStore::Session)
|
|
1357
|
-
if !CGI::Session::ActiveRecordStore::Session.respond_to?(:after_save_callback_chain) ||
|
|
1358
|
-
CGI::Session::ActiveRecordStore::Session.after_save_callback_chain.detect{|cb| cb.method == :enhanced_write_lobs}.nil?
|
|
1359
|
-
#:stopdoc:
|
|
1360
|
-
class CGI::Session::ActiveRecordStore::Session
|
|
1361
|
-
after_save :enhanced_write_lobs
|
|
695
|
+
ActiveRecord::Type.register(:boolean, Type::OracleEnhanced::Boolean, adapter: :oracleenhanced)
|
|
696
|
+
ActiveRecord::Type.register(:json, Type::OracleEnhanced::Json, adapter: :oracleenhanced)
|
|
1362
697
|
end
|
|
1363
|
-
#:startdoc:
|
|
1364
698
|
end
|
|
1365
699
|
end
|
|
1366
700
|
|
|
1367
|
-
|
|
1368
|
-
require 'active_record/connection_adapters/oracle_enhanced_schema_statements'
|
|
1369
|
-
require 'active_record/connection_adapters/oracle_enhanced_schema_statements_ext'
|
|
701
|
+
require "active_record/connection_adapters/oracle_enhanced/version"
|
|
1370
702
|
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
# Extensions for context index definition
|
|
1375
|
-
require 'active_record/connection_adapters/oracle_enhanced_context_index'
|
|
1376
|
-
|
|
1377
|
-
# Load custom create, update, delete methods functionality
|
|
1378
|
-
require 'active_record/connection_adapters/oracle_enhanced_procedures'
|
|
1379
|
-
|
|
1380
|
-
# Load additional methods for composite_primary_keys support
|
|
1381
|
-
require 'active_record/connection_adapters/oracle_enhanced_cpk'
|
|
1382
|
-
|
|
1383
|
-
# Load patch for dirty tracking methods
|
|
1384
|
-
require 'active_record/connection_adapters/oracle_enhanced_dirty'
|
|
1385
|
-
|
|
1386
|
-
# Load rake tasks definitions
|
|
1387
|
-
begin
|
|
1388
|
-
require 'active_record/connection_adapters/oracle_enhanced_tasks'
|
|
1389
|
-
rescue LoadError
|
|
1390
|
-
end if defined?(Rails) || defined?(RAILS_ROOT)
|
|
1391
|
-
|
|
1392
|
-
# Patches and enhancements for schema dumper
|
|
1393
|
-
require 'active_record/connection_adapters/oracle_enhanced_schema_dumper'
|
|
1394
|
-
|
|
1395
|
-
# Implementation of structure dump
|
|
1396
|
-
require 'active_record/connection_adapters/oracle_enhanced_structure_dump'
|
|
1397
|
-
|
|
1398
|
-
# Add BigDecimal#to_d, Fixnum#to_d and Bignum#to_d methods if not already present
|
|
1399
|
-
require 'active_record/connection_adapters/oracle_enhanced_core_ext'
|
|
1400
|
-
|
|
1401
|
-
require 'active_record/connection_adapters/oracle_enhanced_activerecord_patches'
|
|
1402
|
-
|
|
1403
|
-
require 'active_record/connection_adapters/oracle_enhanced_version'
|
|
703
|
+
module ActiveRecord
|
|
704
|
+
autoload :OracleEnhancedProcedures, "active_record/connection_adapters/oracle_enhanced/procedures"
|
|
705
|
+
end
|