activerecord-oracle_enhanced-adapter 1.2.4 → 1.3.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.
- data/.gitignore +0 -1
- data/History.txt +20 -0
- data/README.rdoc +7 -3
- data/Rakefile +1 -2
- data/VERSION +1 -1
- data/activerecord-oracle_enhanced-adapter.gemspec +96 -0
- data/lib/active_record/connection_adapters/oracle_enhanced.rake +11 -8
- data/lib/active_record/connection_adapters/oracle_enhanced_activerecord_patches.rb +37 -0
- data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +317 -180
- data/lib/active_record/connection_adapters/oracle_enhanced_context_index.rb +282 -0
- data/lib/active_record/connection_adapters/oracle_enhanced_core_ext.rb +3 -2
- data/lib/active_record/connection_adapters/oracle_enhanced_dirty.rb +1 -1
- data/lib/active_record/connection_adapters/oracle_enhanced_jdbc_connection.rb +3 -3
- data/lib/active_record/connection_adapters/oracle_enhanced_oci_connection.rb +6 -1
- data/lib/active_record/connection_adapters/oracle_enhanced_procedures.rb +143 -52
- data/lib/active_record/connection_adapters/oracle_enhanced_schema_definitions.rb +2 -1
- data/lib/active_record/connection_adapters/oracle_enhanced_schema_dumper.rb +39 -20
- data/lib/active_record/connection_adapters/oracle_enhanced_tasks.rb +2 -1
- data/lib/active_record/connection_adapters/oracle_enhanced_version.rb +1 -1
- data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +70 -11
- data/spec/active_record/connection_adapters/oracle_enhanced_adapter_structure_dumper_spec.rb +27 -20
- data/spec/active_record/connection_adapters/oracle_enhanced_context_index_spec.rb +334 -0
- data/spec/active_record/connection_adapters/oracle_enhanced_cpk_spec.rb +28 -22
- data/spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb +24 -28
- data/spec/active_record/connection_adapters/oracle_enhanced_dbms_output_spec.rb +13 -11
- data/spec/active_record/connection_adapters/oracle_enhanced_dirty_spec.rb +1 -1
- data/spec/active_record/connection_adapters/oracle_enhanced_procedures_spec.rb +72 -69
- data/spec/active_record/connection_adapters/oracle_enhanced_schema_dump_spec.rb +112 -6
- data/spec/active_record/connection_adapters/oracle_enhanced_schema_spec.rb +49 -1
- data/spec/spec_helper.rb +97 -19
- metadata +33 -22
- data/Manifest.txt +0 -32
- data/lib/active_record/connection_adapters/oracle_enhanced_reserved_words.rb +0 -126
data/.gitignore
CHANGED
data/History.txt
CHANGED
@@ -1,3 +1,23 @@
|
|
1
|
+
== 1.3.0 2010-06-21
|
2
|
+
|
3
|
+
* Enhancements:
|
4
|
+
* Rails 3.0.0.beta4 and Rails 2.3.x compatible
|
5
|
+
* When used with Rails 3 then works together with Oracle SQL compiler included in Arel gem (http://github.com/rails/arel)
|
6
|
+
* Rails 3: Better support for limit and offset (when possible adds just ROWNUM condition in WHERE clause without using subqueries)
|
7
|
+
* Table and column names are always quoted and in uppercase to avoid the need for checking Oracle reserved words
|
8
|
+
* Full text search index creation (add_context_index and remove_context_index methods in migrations and #contains method in ActiveRecord models)
|
9
|
+
* add_index and remove_index give just warnings on wrong index names (new expected behavior in Rails 2.3.8 and 3.0.0)
|
10
|
+
* :tablespace and :options options for create_table and add_index
|
11
|
+
* Workarounds:
|
12
|
+
* Rails 3: set ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.cache_columns = true in initializer file for all environments
|
13
|
+
(to avoid too many data dictionary queries from Arel)
|
14
|
+
* Rails 2.3: patch several ActiveRecord methods to work correctly with quoted table names in uppercase (see oracle_enhanced_activerecord_patches.rb).
|
15
|
+
These patches are already included in Rails 3.0.0.beta4.
|
16
|
+
* Bug fixes:
|
17
|
+
* Fixes for schema purge (drop correctly materialized views)
|
18
|
+
* Fixes for schema dump and structure dump (use correct statement separator)
|
19
|
+
* Only use Oracle specific schema dump for Oracle connections
|
20
|
+
|
1
21
|
== 1.2.4 2010-02-23
|
2
22
|
|
3
23
|
* Enhancements:
|
data/README.rdoc
CHANGED
@@ -15,10 +15,10 @@ Blog posts about oracle-enahnced can be found at http://blog.rayapps.com/categor
|
|
15
15
|
|
16
16
|
== REQUIREMENTS:
|
17
17
|
|
18
|
-
*
|
18
|
+
* Latest version works (has been tested) with ActiveRecord version 2.3 and 3.0 (these are the same as Rails versions)
|
19
19
|
* Can be used on the following Ruby platforms:
|
20
|
-
* MRI - requires ruby-oci8
|
21
|
-
* Ruby/YARV 1.9.1 - requires ruby-oci8 2.
|
20
|
+
* MRI - requires ruby-oci8 2.0 gem to connect to Oracle (2.0.4 or later recommended)
|
21
|
+
* Ruby/YARV 1.9.1 or 1.9.2 - requires ruby-oci8 2.0 library to connect to Oracle
|
22
22
|
unicode_utils gem is recommended for Unicode aware string upcase and downcase
|
23
23
|
* JRuby - uses JDBC driver ojdbc14.jar to connect to Oracle (should be in JRUBY_HOME/lib or in Java class path)
|
24
24
|
* Requires ruby-plsql gem to support custom create, update and delete methods (but can be used without ruby-plsql if this functionality is not needed)
|
@@ -50,6 +50,10 @@ In addition install either ruby-oci8 (for MRI/YARV) or copy Oracle JDBC driver t
|
|
50
50
|
* Alex Rothenberg
|
51
51
|
* Billy Reisinger
|
52
52
|
* David Blain
|
53
|
+
* Joe Khoobyar
|
54
|
+
* Edvard Majakari
|
55
|
+
* Beau Fabry
|
56
|
+
* Simon Chiang
|
53
57
|
|
54
58
|
== LICENSE:
|
55
59
|
|
data/Rakefile
CHANGED
@@ -13,8 +13,7 @@ EOS
|
|
13
13
|
gem.email = "raimonds.simanovskis@gmail.com"
|
14
14
|
gem.homepage = "http://github.com/rsim/oracle-enhanced"
|
15
15
|
gem.authors = ["Raimonds Simanovskis"]
|
16
|
-
gem.
|
17
|
-
gem.add_development_dependency "rspec", ">= 1.2.9"
|
16
|
+
gem.add_development_dependency "rspec", ">= 1.3.0"
|
18
17
|
gem.extra_rdoc_files = ['README.rdoc']
|
19
18
|
end
|
20
19
|
Jeweler::GemcutterTasks.new
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.3.0
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{activerecord-oracle_enhanced-adapter}
|
8
|
+
s.version = "1.3.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Raimonds Simanovskis"]
|
12
|
+
s.date = %q{2010-06-21}
|
13
|
+
s.description = %q{Oracle "enhanced" ActiveRecord adapter contains useful additional methods for working with new and legacy Oracle databases.
|
14
|
+
This adapter is superset of original ActiveRecord Oracle adapter.
|
15
|
+
}
|
16
|
+
s.email = %q{raimonds.simanovskis@gmail.com}
|
17
|
+
s.extra_rdoc_files = [
|
18
|
+
"README.rdoc"
|
19
|
+
]
|
20
|
+
s.files = [
|
21
|
+
".gitignore",
|
22
|
+
"History.txt",
|
23
|
+
"License.txt",
|
24
|
+
"README.rdoc",
|
25
|
+
"Rakefile",
|
26
|
+
"VERSION",
|
27
|
+
"activerecord-oracle_enhanced-adapter.gemspec",
|
28
|
+
"lib/active_record/connection_adapters/emulation/oracle_adapter.rb",
|
29
|
+
"lib/active_record/connection_adapters/oracle_enhanced.rake",
|
30
|
+
"lib/active_record/connection_adapters/oracle_enhanced_activerecord_patches.rb",
|
31
|
+
"lib/active_record/connection_adapters/oracle_enhanced_adapter.rb",
|
32
|
+
"lib/active_record/connection_adapters/oracle_enhanced_connection.rb",
|
33
|
+
"lib/active_record/connection_adapters/oracle_enhanced_context_index.rb",
|
34
|
+
"lib/active_record/connection_adapters/oracle_enhanced_core_ext.rb",
|
35
|
+
"lib/active_record/connection_adapters/oracle_enhanced_cpk.rb",
|
36
|
+
"lib/active_record/connection_adapters/oracle_enhanced_dirty.rb",
|
37
|
+
"lib/active_record/connection_adapters/oracle_enhanced_jdbc_connection.rb",
|
38
|
+
"lib/active_record/connection_adapters/oracle_enhanced_oci_connection.rb",
|
39
|
+
"lib/active_record/connection_adapters/oracle_enhanced_procedures.rb",
|
40
|
+
"lib/active_record/connection_adapters/oracle_enhanced_schema_definitions.rb",
|
41
|
+
"lib/active_record/connection_adapters/oracle_enhanced_schema_dumper.rb",
|
42
|
+
"lib/active_record/connection_adapters/oracle_enhanced_schema_statements_ext.rb",
|
43
|
+
"lib/active_record/connection_adapters/oracle_enhanced_tasks.rb",
|
44
|
+
"lib/active_record/connection_adapters/oracle_enhanced_version.rb",
|
45
|
+
"spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb",
|
46
|
+
"spec/active_record/connection_adapters/oracle_enhanced_adapter_structure_dumper_spec.rb",
|
47
|
+
"spec/active_record/connection_adapters/oracle_enhanced_connection_spec.rb",
|
48
|
+
"spec/active_record/connection_adapters/oracle_enhanced_context_index_spec.rb",
|
49
|
+
"spec/active_record/connection_adapters/oracle_enhanced_core_ext_spec.rb",
|
50
|
+
"spec/active_record/connection_adapters/oracle_enhanced_cpk_spec.rb",
|
51
|
+
"spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb",
|
52
|
+
"spec/active_record/connection_adapters/oracle_enhanced_dbms_output_spec.rb",
|
53
|
+
"spec/active_record/connection_adapters/oracle_enhanced_dirty_spec.rb",
|
54
|
+
"spec/active_record/connection_adapters/oracle_enhanced_emulate_oracle_adapter_spec.rb",
|
55
|
+
"spec/active_record/connection_adapters/oracle_enhanced_procedures_spec.rb",
|
56
|
+
"spec/active_record/connection_adapters/oracle_enhanced_schema_dump_spec.rb",
|
57
|
+
"spec/active_record/connection_adapters/oracle_enhanced_schema_spec.rb",
|
58
|
+
"spec/spec.opts",
|
59
|
+
"spec/spec_helper.rb"
|
60
|
+
]
|
61
|
+
s.homepage = %q{http://github.com/rsim/oracle-enhanced}
|
62
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
63
|
+
s.require_paths = ["lib"]
|
64
|
+
s.rubygems_version = %q{1.3.7}
|
65
|
+
s.summary = %q{Oracle enhanced adapter for ActiveRecord}
|
66
|
+
s.test_files = [
|
67
|
+
"spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb",
|
68
|
+
"spec/active_record/connection_adapters/oracle_enhanced_adapter_structure_dumper_spec.rb",
|
69
|
+
"spec/active_record/connection_adapters/oracle_enhanced_connection_spec.rb",
|
70
|
+
"spec/active_record/connection_adapters/oracle_enhanced_context_index_spec.rb",
|
71
|
+
"spec/active_record/connection_adapters/oracle_enhanced_core_ext_spec.rb",
|
72
|
+
"spec/active_record/connection_adapters/oracle_enhanced_cpk_spec.rb",
|
73
|
+
"spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb",
|
74
|
+
"spec/active_record/connection_adapters/oracle_enhanced_dbms_output_spec.rb",
|
75
|
+
"spec/active_record/connection_adapters/oracle_enhanced_dirty_spec.rb",
|
76
|
+
"spec/active_record/connection_adapters/oracle_enhanced_emulate_oracle_adapter_spec.rb",
|
77
|
+
"spec/active_record/connection_adapters/oracle_enhanced_procedures_spec.rb",
|
78
|
+
"spec/active_record/connection_adapters/oracle_enhanced_schema_dump_spec.rb",
|
79
|
+
"spec/active_record/connection_adapters/oracle_enhanced_schema_spec.rb",
|
80
|
+
"spec/spec_helper.rb"
|
81
|
+
]
|
82
|
+
|
83
|
+
if s.respond_to? :specification_version then
|
84
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
85
|
+
s.specification_version = 3
|
86
|
+
|
87
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
88
|
+
s.add_development_dependency(%q<rspec>, [">= 1.3.0"])
|
89
|
+
else
|
90
|
+
s.add_dependency(%q<rspec>, [">= 1.3.0"])
|
91
|
+
end
|
92
|
+
else
|
93
|
+
s.add_dependency(%q<rspec>, [">= 1.3.0"])
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
@@ -15,14 +15,14 @@ namespace :db do
|
|
15
15
|
namespace :structure do
|
16
16
|
redefine_task :dump => :environment do
|
17
17
|
abcs = ActiveRecord::Base.configurations
|
18
|
-
|
19
|
-
|
20
|
-
File.open("db/#{
|
18
|
+
rails_env = defined?(Rails.env) ? Rails.env : RAILS_ENV
|
19
|
+
ActiveRecord::Base.establish_connection(abcs[rails_env])
|
20
|
+
File.open("db/#{rails_env}_structure.sql", "w+") { |f| f << ActiveRecord::Base.connection.structure_dump }
|
21
21
|
if ActiveRecord::Base.connection.supports_migrations?
|
22
|
-
File.open("db/#{
|
22
|
+
File.open("db/#{rails_env}_structure.sql", "a") { |f| f << ActiveRecord::Base.connection.dump_schema_information }
|
23
23
|
end
|
24
|
-
if abcs[
|
25
|
-
File.open("db/#{
|
24
|
+
if abcs[rails_env]['structure_dump'] == "db_stored_code"
|
25
|
+
File.open("db/#{rails_env}_structure.sql", "a") { |f| f << ActiveRecord::Base.connection.structure_dump_db_stored_code }
|
26
26
|
end
|
27
27
|
|
28
28
|
end
|
@@ -31,8 +31,10 @@ namespace :db do
|
|
31
31
|
namespace :test do
|
32
32
|
redefine_task :clone_structure => [ "db:structure:dump", "db:test:purge" ] do
|
33
33
|
abcs = ActiveRecord::Base.configurations
|
34
|
+
rails_env = defined?(Rails.env) ? Rails.env : RAILS_ENV
|
34
35
|
ActiveRecord::Base.establish_connection(:test)
|
35
|
-
|
36
|
+
File.read("db/#{rails_env}_structure.sql").
|
37
|
+
split(ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter::STATEMENT_TOKEN).each do |ddl|
|
36
38
|
ddl.chop! if ddl.last == ";"
|
37
39
|
ActiveRecord::Base.connection.execute(ddl) unless ddl.blank?
|
38
40
|
end
|
@@ -41,7 +43,8 @@ namespace :db do
|
|
41
43
|
redefine_task :purge => :environment do
|
42
44
|
abcs = ActiveRecord::Base.configurations
|
43
45
|
ActiveRecord::Base.establish_connection(:test)
|
44
|
-
ActiveRecord::Base.connection.full_drop.
|
46
|
+
ActiveRecord::Base.connection.full_drop.
|
47
|
+
split(ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter::STATEMENT_TOKEN).each do |ddl|
|
45
48
|
ddl.chop! if ddl.last == ";"
|
46
49
|
ActiveRecord::Base.connection.execute(ddl) unless ddl.blank?
|
47
50
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# ActiveRecord 2.3 patches
|
2
|
+
if ActiveRecord::VERSION::MAJOR == 2 && ActiveRecord::VERSION::MINOR == 3
|
3
|
+
require "active_record/associations"
|
4
|
+
|
5
|
+
ActiveRecord::Associations::ClassMethods.module_eval do
|
6
|
+
private
|
7
|
+
def tables_in_string(string)
|
8
|
+
return [] if string.blank?
|
9
|
+
# always convert table names to downcase as in Oracle quoted table names are in uppercase
|
10
|
+
# ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
|
11
|
+
string.scan(/([a-zA-Z_][\.\w]+).?\./).flatten.map(&:downcase).uniq - ['raw_sql_']
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation.class_eval do
|
16
|
+
protected
|
17
|
+
def aliased_table_name_for(name, suffix = nil)
|
18
|
+
# always downcase quoted table name as Oracle quoted table names are in uppercase
|
19
|
+
if !parent.table_joins.blank? && parent.table_joins.to_s.downcase =~ %r{join(\s+\w+)?\s+#{active_record.connection.quote_table_name(name).downcase}\son}
|
20
|
+
@join_dependency.table_aliases[name] += 1
|
21
|
+
end
|
22
|
+
|
23
|
+
unless @join_dependency.table_aliases[name].zero?
|
24
|
+
# if the table name has been used, then use an alias
|
25
|
+
name = active_record.connection.table_alias_for "#{pluralize(reflection.name)}_#{parent_table_name}#{suffix}"
|
26
|
+
table_index = @join_dependency.table_aliases[name]
|
27
|
+
@join_dependency.table_aliases[name] += 1
|
28
|
+
name = name[0..active_record.connection.table_alias_length-3] + "_#{table_index+1}" if table_index > 0
|
29
|
+
else
|
30
|
+
@join_dependency.table_aliases[name] += 1
|
31
|
+
end
|
32
|
+
|
33
|
+
name
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -29,8 +29,10 @@
|
|
29
29
|
# contribution.
|
30
30
|
# portions Copyright 2005 Graham Jenkins
|
31
31
|
|
32
|
-
|
32
|
+
# ActiveRecord 2.2 does not load version file automatically
|
33
|
+
require 'active_record/version' unless defined?(ActiveRecord::VERSION)
|
33
34
|
|
35
|
+
require 'active_record/connection_adapters/abstract_adapter'
|
34
36
|
require 'active_record/connection_adapters/oracle_enhanced_connection'
|
35
37
|
|
36
38
|
require 'digest/sha1'
|
@@ -91,7 +93,7 @@ module ActiveRecord
|
|
91
93
|
# Specify which table columns should be typecasted to string values.
|
92
94
|
# Might be useful to specify that columns should be string even if its name matches boolean column criteria.
|
93
95
|
#
|
94
|
-
#
|
96
|
+
# set_string_columns :active_flag
|
95
97
|
def self.set_string_columns(*args)
|
96
98
|
connection.set_type_for_columns(table_name,:string,*args)
|
97
99
|
end
|
@@ -106,33 +108,7 @@ module ActiveRecord
|
|
106
108
|
end
|
107
109
|
end
|
108
110
|
private :enhanced_write_lobs
|
109
|
-
|
110
|
-
class << self
|
111
|
-
# patch ORDER BY to work with LOBs
|
112
|
-
def add_order_with_lobs!(sql, order, scope = :auto)
|
113
|
-
if connection.is_a?(ConnectionAdapters::OracleEnhancedAdapter)
|
114
|
-
order = connection.lob_order_by_expression(self, order) if order
|
115
|
-
|
116
|
-
orig_scope = scope
|
117
|
-
scope = scope(:find) if :auto == scope
|
118
|
-
if scope
|
119
|
-
new_scope_order = connection.lob_order_by_expression(self, scope[:order])
|
120
|
-
if new_scope_order != scope[:order]
|
121
|
-
scope = scope.merge(:order => new_scope_order)
|
122
|
-
else
|
123
|
-
scope = orig_scope
|
124
|
-
end
|
125
|
-
end
|
126
|
-
end
|
127
|
-
add_order_without_lobs!(sql, order, scope = :auto)
|
128
|
-
end
|
129
|
-
private :add_order_with_lobs!
|
130
|
-
#:stopdoc:
|
131
|
-
alias_method :add_order_without_lobs!, :add_order!
|
132
|
-
alias_method :add_order!, :add_order_with_lobs!
|
133
|
-
#:startdoc:
|
134
|
-
end
|
135
|
-
|
111
|
+
|
136
112
|
# Get table comment from schema definition.
|
137
113
|
def self.table_comment
|
138
114
|
connection.table_comment(self.table_name)
|
@@ -236,7 +212,7 @@ module ActiveRecord
|
|
236
212
|
if OracleEnhancedAdapter.string_to_time_format && dt=Date._strptime(string, OracleEnhancedAdapter.string_to_time_format)
|
237
213
|
return Time.mktime(*dt.values_at(:year, :mon, :mday, :hour, :min, :sec, :zone, :wday))
|
238
214
|
end
|
239
|
-
DateTime.strptime(string, OracleEnhancedAdapter.string_to_date_format)
|
215
|
+
DateTime.strptime(string, OracleEnhancedAdapter.string_to_date_format).to_date
|
240
216
|
end
|
241
217
|
|
242
218
|
end
|
@@ -403,6 +379,7 @@ module ActiveRecord
|
|
403
379
|
def initialize(connection, logger = nil) #:nodoc:
|
404
380
|
super
|
405
381
|
@quoted_column_names, @quoted_table_names = {}, {}
|
382
|
+
@enable_dbms_output = false
|
406
383
|
end
|
407
384
|
|
408
385
|
ADAPTER_NAME = 'OracleEnhanced'.freeze
|
@@ -415,6 +392,10 @@ module ActiveRecord
|
|
415
392
|
true
|
416
393
|
end
|
417
394
|
|
395
|
+
def supports_primary_key? #:nodoc:
|
396
|
+
true
|
397
|
+
end
|
398
|
+
|
418
399
|
def supports_savepoints? #:nodoc:
|
419
400
|
true
|
420
401
|
end
|
@@ -453,6 +434,21 @@ module ActiveRecord
|
|
453
434
|
IDENTIFIER_MAX_LENGTH
|
454
435
|
end
|
455
436
|
|
437
|
+
# the maximum length of a table name
|
438
|
+
def table_name_length
|
439
|
+
IDENTIFIER_MAX_LENGTH
|
440
|
+
end
|
441
|
+
|
442
|
+
# the maximum length of a column name
|
443
|
+
def column_name_length
|
444
|
+
IDENTIFIER_MAX_LENGTH
|
445
|
+
end
|
446
|
+
|
447
|
+
# the maximum length of an index name
|
448
|
+
def index_name_length
|
449
|
+
IDENTIFIER_MAX_LENGTH
|
450
|
+
end
|
451
|
+
|
456
452
|
# QUOTING ==================================================
|
457
453
|
#
|
458
454
|
# see: abstract/quoting.rb
|
@@ -460,26 +456,52 @@ module ActiveRecord
|
|
460
456
|
def quote_column_name(name) #:nodoc:
|
461
457
|
# camelCase column names need to be quoted; not that anyone using Oracle
|
462
458
|
# would really do this, but handling this case means we pass the test...
|
463
|
-
|
459
|
+
name = name.to_s
|
460
|
+
@quoted_column_names[name] ||= begin
|
461
|
+
case name
|
462
|
+
# if only valid column characters in name
|
463
|
+
when /^[a-z][a-z_0-9\$#]*$/
|
464
|
+
"\"#{name.upcase}\""
|
465
|
+
when /^[a-z][a-z_0-9\$#\-]*$/i
|
466
|
+
"\"#{name}\""
|
467
|
+
# if other characters present then assume that it is expression
|
468
|
+
# which should not be quoted
|
469
|
+
else
|
470
|
+
name
|
471
|
+
end
|
472
|
+
end
|
464
473
|
end
|
465
474
|
|
475
|
+
# Names must be from 1 to 30 bytes long with these exceptions:
|
476
|
+
# * Names of databases are limited to 8 bytes.
|
477
|
+
# * Names of database links can be as long as 128 bytes.
|
478
|
+
#
|
479
|
+
# Nonquoted identifiers cannot be Oracle Database reserved words
|
480
|
+
#
|
481
|
+
# Nonquoted identifiers must begin with an alphabetic character from
|
482
|
+
# your database character set
|
483
|
+
#
|
484
|
+
# Nonquoted identifiers can contain only alphanumeric characters from
|
485
|
+
# your database character set and the underscore (_), dollar sign ($),
|
486
|
+
# and pound sign (#). Database links can also contain periods (.) and
|
487
|
+
# "at" signs (@). Oracle strongly discourages you from using $ and # in
|
488
|
+
# nonquoted identifiers.
|
489
|
+
NONQUOTED_OBJECT_NAME = /[A-Za-z][A-z0-9$#]{0,29}/
|
490
|
+
NONQUOTED_DATABASE_LINK = /[A-Za-z][A-z0-9$#\.@]{0,127}/
|
491
|
+
VALID_TABLE_NAME = /\A(?:#{NONQUOTED_OBJECT_NAME}\.)?#{NONQUOTED_OBJECT_NAME}(?:@#{NONQUOTED_DATABASE_LINK})?\Z/
|
492
|
+
|
466
493
|
# unescaped table name should start with letter and
|
467
494
|
# contain letters, digits, _, $ or #
|
468
495
|
# can be prefixed with schema name
|
469
496
|
# CamelCase table names should be quoted
|
470
497
|
def self.valid_table_name?(name) #:nodoc:
|
471
|
-
name = name.to_s
|
472
|
-
name =~
|
473
|
-
name =~ /\A([A-Za-z_0-9]+\.)?[A-Z][A-Z_0-9\$#]*(@[A-Za-z_0-9\.]+)?\Z/ ? true : false
|
498
|
+
name = name.to_s
|
499
|
+
name =~ VALID_TABLE_NAME && !(name =~ /[A-Z]/ && name =~ /[a-z]/) ? true : false
|
474
500
|
end
|
475
501
|
|
476
502
|
def quote_table_name(name) #:nodoc:
|
477
|
-
|
478
|
-
@quoted_table_names[name] ||=
|
479
|
-
name
|
480
|
-
else
|
481
|
-
"\"#{name}\""
|
482
|
-
end
|
503
|
+
name = name.to_s
|
504
|
+
@quoted_table_names[name] ||= name.split('.').map{|n| n.split('@').map{|m| quote_column_name(m)}.join('@')}.join('.')
|
483
505
|
end
|
484
506
|
|
485
507
|
def quote_string(s) #:nodoc:
|
@@ -580,7 +602,8 @@ module ActiveRecord
|
|
580
602
|
# Executes a SQL statement
|
581
603
|
def execute(sql, name = nil)
|
582
604
|
# hack to pass additional "with_returning" option without changing argument list
|
583
|
-
log(sql, name) { sql.
|
605
|
+
log(sql, name) { sql.instance_variable_defined?(:@with_returning) && sql.instance_variable_get(:@with_returning) ?
|
606
|
+
@connection.exec_with_returning(sql) : @connection.exec(sql) }
|
584
607
|
end
|
585
608
|
|
586
609
|
# Returns an array of arrays containing the field values.
|
@@ -603,7 +626,6 @@ module ActiveRecord
|
|
603
626
|
sql_with_returning = sql.dup << @connection.returning_clause(quote_column_name(pk))
|
604
627
|
# hack to pass additional "with_returning" option without changing argument list
|
605
628
|
sql_with_returning.instance_variable_set(:@with_returning, true)
|
606
|
-
clear_query_cache
|
607
629
|
execute(sql_with_returning, name)
|
608
630
|
end
|
609
631
|
protected :insert_sql
|
@@ -651,10 +673,12 @@ module ActiveRecord
|
|
651
673
|
def add_limit_offset!(sql, options) #:nodoc:
|
652
674
|
# added to_i for limit and offset to protect from SQL injection
|
653
675
|
offset = (options[:offset] || 0).to_i
|
654
|
-
|
655
|
-
|
656
|
-
|
676
|
+
limit = options[:limit]
|
677
|
+
limit = limit.is_a?(String) && limit.blank? ? nil : limit && limit.to_i
|
678
|
+
if limit && offset > 0
|
657
679
|
sql.replace "select * from (select raw_sql_.*, rownum raw_rnum_ from (#{sql}) raw_sql_ where rownum <= #{offset+limit}) where raw_rnum_ > #{offset}"
|
680
|
+
elsif limit
|
681
|
+
sql.replace "select * from (#{sql}) where rownum <= #{limit}"
|
658
682
|
elsif offset > 0
|
659
683
|
sql.replace "select * from (select raw_sql_.*, rownum raw_rnum_ from (#{sql}) raw_sql_) where raw_rnum_ > #{offset}"
|
660
684
|
end
|
@@ -706,13 +730,12 @@ module ActiveRecord
|
|
706
730
|
next if value.nil? || (value == '')
|
707
731
|
value = value.to_yaml if col.text? && klass.serialized_attributes[col.name]
|
708
732
|
uncached do
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
lob = select_one("SELECT #{col.name} FROM #{table_name} WHERE #{klass.primary_key} = #{id} FOR UPDATE",
|
714
|
-
'Writable Large Object')[col.name]
|
733
|
+
sql = is_with_cpk ? "SELECT #{col.name} FROM #{table_name} WHERE #{klass.composite_where_clause(id)} FOR UPDATE" :
|
734
|
+
"SELECT #{col.name} FROM #{table_name} WHERE #{klass.primary_key} = #{id} FOR UPDATE"
|
735
|
+
unless lob_record = select_one(sql, 'Writable Large Object')
|
736
|
+
raise ActiveRecord::RecordNotFound, "statement #{sql} returned no rows"
|
715
737
|
end
|
738
|
+
lob = lob_record[col.name]
|
716
739
|
@connection.write_lob(lob, value.to_s, col.type == :binary)
|
717
740
|
end
|
718
741
|
end
|
@@ -755,8 +778,21 @@ module ActiveRecord
|
|
755
778
|
end
|
756
779
|
|
757
780
|
def tables(name = nil) #:nodoc:
|
758
|
-
|
759
|
-
|
781
|
+
select_values(
|
782
|
+
"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'",
|
783
|
+
name)
|
784
|
+
end
|
785
|
+
|
786
|
+
# Will return true if database object exists (to be able to use also views and synonyms for ActiveRecord models)
|
787
|
+
def table_exists?(table_name)
|
788
|
+
(owner, table_name, db_link) = @connection.describe(table_name)
|
789
|
+
true
|
790
|
+
rescue
|
791
|
+
false
|
792
|
+
end
|
793
|
+
|
794
|
+
def materialized_views #:nodoc:
|
795
|
+
select_values("select lower(mview_name) from all_mviews where owner = sys_context('userenv','session_user')")
|
760
796
|
end
|
761
797
|
|
762
798
|
cattr_accessor :all_schema_indexes #:nodoc:
|
@@ -768,14 +804,19 @@ module ActiveRecord
|
|
768
804
|
unless all_schema_indexes
|
769
805
|
default_tablespace_name = default_tablespace
|
770
806
|
result = select_all(<<-SQL)
|
771
|
-
SELECT
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
807
|
+
SELECT LOWER(i.table_name) AS table_name, LOWER(i.index_name) AS index_name, i.uniqueness,
|
808
|
+
i.index_type, i.ityp_owner, i.ityp_name, i.parameters,
|
809
|
+
LOWER(i.tablespace_name) AS tablespace_name,
|
810
|
+
LOWER(c.column_name) AS column_name, e.column_expression
|
811
|
+
FROM all_indexes#{db_link} i
|
812
|
+
JOIN all_ind_columns#{db_link} c ON c.index_name = i.index_name AND c.index_owner = i.owner
|
813
|
+
LEFT OUTER JOIN all_ind_expressions#{db_link} e ON e.index_name = i.index_name AND
|
814
|
+
e.index_owner = i.owner AND e.column_position = c.column_position
|
815
|
+
WHERE i.owner = '#{owner}'
|
776
816
|
AND i.table_owner = '#{owner}'
|
777
|
-
AND NOT EXISTS (SELECT uc.index_name FROM all_constraints uc
|
778
|
-
|
817
|
+
AND NOT EXISTS (SELECT uc.index_name FROM all_constraints uc
|
818
|
+
WHERE uc.index_name = i.index_name AND uc.owner = i.owner AND uc.constraint_type = 'P')
|
819
|
+
ORDER BY i.index_name, c.column_position
|
779
820
|
SQL
|
780
821
|
|
781
822
|
current_index = nil
|
@@ -785,11 +826,24 @@ module ActiveRecord
|
|
785
826
|
# have to keep track of indexes because above query returns dups
|
786
827
|
# there is probably a better query we could figure out
|
787
828
|
if current_index != row['index_name']
|
788
|
-
|
829
|
+
statement_parameters = nil
|
830
|
+
if row['index_type'] == 'DOMAIN' && row['ityp_owner'] == 'CTXSYS' && row['ityp_name'] == 'CONTEXT'
|
831
|
+
procedure_name = default_datastore_procedure(row['index_name'])
|
832
|
+
statement_parameters = select_value(<<-SQL)
|
833
|
+
SELECT SUBSTR(text,LENGTH('-- add_context_index_parameters ')+1)
|
834
|
+
FROM all_source#{db_link}
|
835
|
+
WHERE owner = '#{owner}'
|
836
|
+
AND name = '#{procedure_name.upcase}'
|
837
|
+
AND text LIKE '-- add_context_index_parameters %'
|
838
|
+
SQL
|
839
|
+
end
|
840
|
+
all_schema_indexes << OracleEnhancedIndexDefinition.new(row['table_name'], row['index_name'],
|
841
|
+
row['uniqueness'] == "UNIQUE", row['index_type'] == 'DOMAIN' ? "#{row['ityp_owner']}.#{row['ityp_name']}" : nil,
|
842
|
+
row['parameters'], statement_parameters,
|
789
843
|
row['tablespace_name'] == default_tablespace_name ? nil : row['tablespace_name'], [])
|
790
844
|
current_index = row['index_name']
|
791
845
|
end
|
792
|
-
all_schema_indexes.last.columns << (row['column_expression']
|
846
|
+
all_schema_indexes.last.columns << (row['column_expression'] || row['column_name'].downcase)
|
793
847
|
end
|
794
848
|
end
|
795
849
|
|
@@ -852,7 +906,7 @@ module ActiveRecord
|
|
852
906
|
AND table_name = '#{desc_table_name}'
|
853
907
|
AND status = 'ENABLED'
|
854
908
|
SQL
|
855
|
-
select_value(pkt_sql) ? true : false
|
909
|
+
select_value(pkt_sql, 'Primary Key Trigger') ? true : false
|
856
910
|
end
|
857
911
|
|
858
912
|
##
|
@@ -866,8 +920,7 @@ module ActiveRecord
|
|
866
920
|
self.cache_columns = false
|
867
921
|
|
868
922
|
def columns(table_name, name = nil) #:nodoc:
|
869
|
-
|
870
|
-
if @@cache_columns && !(defined?(Rails) && Rails.configuration.cache_classes)
|
923
|
+
if @@cache_columns
|
871
924
|
@@columns_cache ||= {}
|
872
925
|
@@columns_cache[table_name] ||= columns_without_cache(table_name, name)
|
873
926
|
else
|
@@ -876,14 +929,15 @@ module ActiveRecord
|
|
876
929
|
end
|
877
930
|
|
878
931
|
def columns_without_cache(table_name, name = nil) #:nodoc:
|
932
|
+
table_name = table_name.to_s
|
879
933
|
# get ignored_columns by original table name
|
880
934
|
ignored_columns = ignored_table_columns(table_name)
|
881
935
|
|
882
936
|
(owner, desc_table_name, db_link) = @connection.describe(table_name)
|
883
937
|
|
884
|
-
|
885
|
-
|
886
|
-
|
938
|
+
@@do_not_prefetch_primary_key[table_name] =
|
939
|
+
!has_primary_key?(table_name, owner, desc_table_name, db_link) ||
|
940
|
+
has_primary_key_trigger?(table_name, owner, desc_table_name, db_link)
|
887
941
|
|
888
942
|
table_cols = <<-SQL
|
889
943
|
select column_name as name, data_type as sql_type, data_default, nullable,
|
@@ -911,7 +965,10 @@ module ActiveRecord
|
|
911
965
|
# clean up odd default spacing from Oracle
|
912
966
|
if row['data_default']
|
913
967
|
row['data_default'].sub!(/^(.*?)\s*$/, '\1')
|
914
|
-
|
968
|
+
|
969
|
+
# If a default contains a newline these cleanup regexes need to
|
970
|
+
# match newlines.
|
971
|
+
row['data_default'].sub!(/^'(.*)'$/m, '\1')
|
915
972
|
row['data_default'] = nil if row['data_default'] =~ /^(null|empty_[bc]lob\(\))$/i
|
916
973
|
end
|
917
974
|
|
@@ -929,11 +986,15 @@ module ActiveRecord
|
|
929
986
|
# used just in tests to clear column cache
|
930
987
|
def clear_columns_cache #:nodoc:
|
931
988
|
@@columns_cache = nil
|
989
|
+
@@pk_and_sequence_for_cache = nil
|
932
990
|
end
|
933
991
|
|
934
992
|
# used in migrations to clear column cache for specified table
|
935
993
|
def clear_table_columns_cache(table_name)
|
936
|
-
|
994
|
+
if @@cache_columns
|
995
|
+
@@columns_cache ||= {}
|
996
|
+
@@columns_cache[table_name.to_s] = nil
|
997
|
+
end
|
937
998
|
end
|
938
999
|
|
939
1000
|
##
|
@@ -1008,7 +1069,7 @@ module ActiveRecord
|
|
1008
1069
|
result = block.call(table_definition) if block
|
1009
1070
|
create_sequence = create_sequence || table_definition.create_sequence
|
1010
1071
|
column_comments = table_definition.column_comments if table_definition.column_comments
|
1011
|
-
|
1072
|
+
tablespace = options[:tablespace] ? " TABLESPACE #{options[:tablespace]}" : ""
|
1012
1073
|
|
1013
1074
|
if options[:force] && table_exists?(name)
|
1014
1075
|
drop_table(name, options)
|
@@ -1017,7 +1078,7 @@ module ActiveRecord
|
|
1017
1078
|
create_sql = "CREATE#{' GLOBAL TEMPORARY' if options[:temporary]} TABLE "
|
1018
1079
|
create_sql << "#{quote_table_name(name)} ("
|
1019
1080
|
create_sql << table_definition.to_sql
|
1020
|
-
create_sql << ") #{options[:options]}"
|
1081
|
+
create_sql << ")#{tablespace} #{options[:options]}"
|
1021
1082
|
execute create_sql
|
1022
1083
|
|
1023
1084
|
create_sequence_and_trigger(name, options) if create_sequence
|
@@ -1044,51 +1105,86 @@ module ActiveRecord
|
|
1044
1105
|
|
1045
1106
|
# clear cached indexes when adding new index
|
1046
1107
|
def add_index(table_name, column_name, options = {}) #:nodoc:
|
1047
|
-
self.all_schema_indexes = nil
|
1048
1108
|
column_names = Array(column_name)
|
1049
1109
|
index_name = index_name(table_name, :column => column_names)
|
1050
1110
|
|
1051
1111
|
if Hash === options # legacy support, since this param was a string
|
1052
1112
|
index_type = options[:unique] ? "UNIQUE" : ""
|
1053
1113
|
index_name = options[:name] || index_name
|
1054
|
-
tablespace =
|
1055
|
-
" TABLESPACE #{options[:tablespace]}"
|
1056
|
-
else
|
1057
|
-
""
|
1058
|
-
end
|
1114
|
+
tablespace = options[:tablespace] ? " TABLESPACE #{options[:tablespace]}" : ""
|
1059
1115
|
else
|
1060
1116
|
index_type = options
|
1061
1117
|
end
|
1118
|
+
|
1119
|
+
if index_name.to_s.length > index_name_length
|
1120
|
+
@logger.warn("Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{index_name_length} characters. Skipping.") if @logger
|
1121
|
+
return
|
1122
|
+
end
|
1123
|
+
if index_exists?(table_name, index_name, false)
|
1124
|
+
@logger.warn("Index name '#{index_name}' on table '#{table_name}' already exists. Skipping.") if @logger
|
1125
|
+
return
|
1126
|
+
end
|
1062
1127
|
quoted_column_names = column_names.map { |e| quote_column_name(e) }.join(", ")
|
1063
|
-
|
1128
|
+
|
1129
|
+
execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{quoted_column_names})#{tablespace} #{options[:options]}"
|
1130
|
+
ensure
|
1131
|
+
self.all_schema_indexes = nil
|
1064
1132
|
end
|
1065
1133
|
|
1066
|
-
#
|
1134
|
+
# Remove the given index from the table.
|
1135
|
+
# Gives warning if index does not exist
|
1067
1136
|
def remove_index(table_name, options = {}) #:nodoc:
|
1137
|
+
index_name = index_name(table_name, options)
|
1138
|
+
unless index_exists?(table_name, index_name, true)
|
1139
|
+
@logger.warn("Index name '#{index_name}' on table '#{table_name}' does not exist. Skipping.") if @logger
|
1140
|
+
return
|
1141
|
+
end
|
1142
|
+
remove_index!(table_name, index_name)
|
1143
|
+
end
|
1144
|
+
|
1145
|
+
# clear cached indexes when removing index
|
1146
|
+
def remove_index!(table_name, index_name) #:nodoc:
|
1147
|
+
execute "DROP INDEX #{quote_column_name(index_name)}"
|
1148
|
+
ensure
|
1068
1149
|
self.all_schema_indexes = nil
|
1069
|
-
execute "DROP INDEX #{index_name(table_name, options)}"
|
1070
1150
|
end
|
1071
|
-
|
1151
|
+
|
1072
1152
|
# returned shortened index name if default is too large
|
1073
1153
|
def index_name(table_name, options) #:nodoc:
|
1074
1154
|
default_name = super(table_name, options)
|
1075
|
-
|
1155
|
+
# sometimes options can be String or Array with column names
|
1156
|
+
options = {} unless options.is_a?(Hash)
|
1157
|
+
identifier_max_length = options[:identifier_max_length] || IDENTIFIER_MAX_LENGTH
|
1158
|
+
return default_name if default_name.length <= identifier_max_length
|
1076
1159
|
|
1077
1160
|
# remove 'index', 'on' and 'and' keywords
|
1078
1161
|
shortened_name = "i_#{table_name}_#{Array(options[:column]) * '_'}"
|
1079
1162
|
|
1080
1163
|
# leave just first three letters from each word
|
1081
|
-
if shortened_name.length >
|
1164
|
+
if shortened_name.length > identifier_max_length
|
1082
1165
|
shortened_name = shortened_name.split('_').map{|w| w[0,3]}.join('_')
|
1083
1166
|
end
|
1084
1167
|
# generate unique name using hash function
|
1085
|
-
if shortened_name.length >
|
1086
|
-
shortened_name = 'i'+Digest::SHA1.hexdigest(default_name)[0,
|
1168
|
+
if shortened_name.length > identifier_max_length
|
1169
|
+
shortened_name = 'i'+Digest::SHA1.hexdigest(default_name)[0,identifier_max_length-1]
|
1087
1170
|
end
|
1088
1171
|
@logger.warn "#{adapter_name} shortened default index name #{default_name} to #{shortened_name}" if @logger
|
1089
1172
|
shortened_name
|
1090
1173
|
end
|
1091
1174
|
|
1175
|
+
# Verify the existence of an index (always query database).
|
1176
|
+
def index_exists?(table_name, index_name, default) #:nodoc:
|
1177
|
+
(owner, table_name, db_link) = @connection.describe(table_name)
|
1178
|
+
result = select_value(<<-SQL)
|
1179
|
+
SELECT 1 FROM all_indexes#{db_link} i
|
1180
|
+
WHERE i.owner = '#{owner}'
|
1181
|
+
AND i.table_owner = '#{owner}'
|
1182
|
+
AND i.table_name = '#{table_name}'
|
1183
|
+
AND i.index_name = '#{index_name.to_s.upcase}'
|
1184
|
+
SQL
|
1185
|
+
result == 1 ? true : false
|
1186
|
+
end
|
1187
|
+
|
1092
1188
|
def add_column(table_name, column_name, type, options = {}) #:nodoc:
|
1093
1189
|
add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
|
1094
1190
|
options[:type] = type
|
@@ -1182,15 +1278,24 @@ module ActiveRecord
|
|
1182
1278
|
|
1183
1279
|
# Find a table's primary key and sequence.
|
1184
1280
|
# *Note*: Only primary key is implemented - sequence will be nil.
|
1185
|
-
def pk_and_sequence_for(table_name) #:nodoc:
|
1186
|
-
|
1281
|
+
def pk_and_sequence_for(table_name, owner=nil, desc_table_name=nil, db_link=nil) #:nodoc:
|
1282
|
+
if @@cache_columns
|
1283
|
+
@@pk_and_sequence_for_cache ||= {}
|
1284
|
+
@@pk_and_sequence_for_cache[table_name] ||= pk_and_sequence_for_without_cache(table_name, owner, desc_table_name, db_link)
|
1285
|
+
else
|
1286
|
+
pk_and_sequence_for_without_cache(table_name, owner, desc_table_name, db_link)
|
1287
|
+
end
|
1288
|
+
end
|
1187
1289
|
|
1188
|
-
|
1290
|
+
def pk_and_sequence_for_without_cache(table_name, owner=nil, desc_table_name=nil, db_link=nil) #:nodoc:
|
1291
|
+
(owner, desc_table_name, db_link) = @connection.describe(table_name) unless owner
|
1292
|
+
|
1293
|
+
# changed back from user_constraints to all_constraints for consistency
|
1189
1294
|
pks = select_values(<<-SQL, 'Primary Key')
|
1190
1295
|
select cc.column_name
|
1191
|
-
from
|
1296
|
+
from all_constraints#{db_link} c, all_cons_columns#{db_link} cc
|
1192
1297
|
where c.owner = '#{owner}'
|
1193
|
-
and c.table_name = '#{
|
1298
|
+
and c.table_name = '#{desc_table_name}'
|
1194
1299
|
and c.constraint_type = 'P'
|
1195
1300
|
and cc.owner = c.owner
|
1196
1301
|
and cc.constraint_name = c.constraint_name
|
@@ -1200,16 +1305,29 @@ module ActiveRecord
|
|
1200
1305
|
pks.size == 1 ? [oracle_downcase(pks.first), nil] : nil
|
1201
1306
|
end
|
1202
1307
|
|
1308
|
+
# Returns just a table's primary key
|
1309
|
+
def primary_key(table_name)
|
1310
|
+
pk_and_sequence = pk_and_sequence_for(table_name)
|
1311
|
+
pk_and_sequence && pk_and_sequence.first
|
1312
|
+
end
|
1313
|
+
|
1314
|
+
def has_primary_key?(table_name, owner=nil, desc_table_name=nil, db_link=nil) #:nodoc:
|
1315
|
+
!pk_and_sequence_for(table_name, owner, desc_table_name, db_link).nil?
|
1316
|
+
end
|
1317
|
+
|
1318
|
+
# Statements separator used in structure dump to allow loading of structure dump also with SQL*Plus
|
1319
|
+
STATEMENT_TOKEN = "\n\n/\n\n"
|
1320
|
+
|
1203
1321
|
def structure_dump #:nodoc:
|
1204
|
-
|
1205
|
-
|
1322
|
+
structure = select_values("select sequence_name from user_sequences order by 1").map do |seq|
|
1323
|
+
"CREATE SEQUENCE \"#{seq}\""
|
1206
1324
|
end
|
1207
|
-
|
1208
|
-
|
1209
|
-
|
1210
|
-
|
1325
|
+
select_values("select table_name from all_tables t
|
1326
|
+
where owner = sys_context('userenv','session_user') and secondary='N'
|
1327
|
+
and not exists (select mv.mview_name from all_mviews mv where mv.owner = t.owner and mv.mview_name = t.table_name)
|
1328
|
+
order by 1").each do |table_name|
|
1211
1329
|
virtual_columns = virtual_columns_for(table_name)
|
1212
|
-
ddl = "
|
1330
|
+
ddl = "CREATE#{ ' GLOBAL TEMPORARY' if temporary_table?(table_name)} TABLE \"#{table_name}\" (\n"
|
1213
1331
|
cols = select_all(%Q{
|
1214
1332
|
select column_name, data_type, data_length, char_used, char_length, data_precision, data_scale, data_default, nullable
|
1215
1333
|
from user_tab_columns
|
@@ -1224,15 +1342,16 @@ module ActiveRecord
|
|
1224
1342
|
end
|
1225
1343
|
ddl << cols.join(",\n ")
|
1226
1344
|
ddl << structure_dump_constraints(table_name)
|
1227
|
-
ddl << "\n)
|
1345
|
+
ddl << "\n)"
|
1228
1346
|
structure << ddl
|
1229
1347
|
structure << structure_dump_indexes(table_name)
|
1230
1348
|
end
|
1349
|
+
|
1350
|
+
join_with_statement_token(structure) << structure_dump_fk_constraints
|
1231
1351
|
end
|
1232
|
-
|
1233
|
-
def
|
1234
|
-
|
1235
|
-
col = "#{column['column_name'].downcase} #{column['data_type'].downcase}"
|
1352
|
+
|
1353
|
+
def structure_dump_column(column) #:nodoc:
|
1354
|
+
col = "\"#{column['column_name']}\" #{column['data_type']}"
|
1236
1355
|
if column['data_type'] =='NUMBER' and !column['data_precision'].nil?
|
1237
1356
|
col << "(#{column['data_precision'].to_i}"
|
1238
1357
|
col << ",#{column['data_scale'].to_i}" if !column['data_scale'].nil?
|
@@ -1241,11 +1360,14 @@ module ActiveRecord
|
|
1241
1360
|
length = column['char_used'] == 'C' ? column['char_length'].to_i : column['data_length'].to_i
|
1242
1361
|
col << "(#{length})"
|
1243
1362
|
end
|
1244
|
-
col << "
|
1363
|
+
col << " DEFAULT #{column['data_default']}" if !column['data_default'].nil?
|
1364
|
+
col << ' NOT NULL' if column['nullable'] == 'N'
|
1365
|
+
col
|
1245
1366
|
end
|
1246
|
-
|
1247
|
-
def
|
1248
|
-
|
1367
|
+
|
1368
|
+
def structure_dump_virtual_column(column, data_default) #:nodoc:
|
1369
|
+
data_default = data_default.gsub(/"/, '')
|
1370
|
+
col = "\"#{column['column_name']}\" #{column['data_type']}"
|
1249
1371
|
if column['data_type'] =='NUMBER' and !column['data_precision'].nil?
|
1250
1372
|
col << "(#{column['data_precision'].to_i}"
|
1251
1373
|
col << ",#{column['data_scale'].to_i}" if !column['data_scale'].nil?
|
@@ -1254,16 +1376,14 @@ module ActiveRecord
|
|
1254
1376
|
length = column['char_used'] == 'C' ? column['char_length'].to_i : column['data_length'].to_i
|
1255
1377
|
col << "(#{length})"
|
1256
1378
|
end
|
1257
|
-
col << "
|
1258
|
-
col << ' not null' if column['nullable'] == 'N'
|
1259
|
-
col
|
1379
|
+
col << " GENERATED ALWAYS AS (#{data_default}) VIRTUAL"
|
1260
1380
|
end
|
1261
|
-
|
1381
|
+
|
1262
1382
|
def structure_dump_constraints(table) #:nodoc:
|
1263
1383
|
out = [structure_dump_primary_key(table), structure_dump_unique_keys(table)].flatten.compact
|
1264
1384
|
out.length > 0 ? ",\n#{out.join(",\n")}" : ''
|
1265
1385
|
end
|
1266
|
-
|
1386
|
+
|
1267
1387
|
def structure_dump_primary_key(table) #:nodoc:
|
1268
1388
|
opts = {:name => '', :cols => []}
|
1269
1389
|
pks = select_all(<<-SQL, "Primary Keys")
|
@@ -1281,7 +1401,7 @@ module ActiveRecord
|
|
1281
1401
|
end
|
1282
1402
|
opts[:cols].length > 0 ? " CONSTRAINT #{opts[:name]} PRIMARY KEY (#{opts[:cols].join(',')})" : nil
|
1283
1403
|
end
|
1284
|
-
|
1404
|
+
|
1285
1405
|
def structure_dump_unique_keys(table) #:nodoc:
|
1286
1406
|
keys = {}
|
1287
1407
|
uks = select_all(<<-SQL, "Primary Keys")
|
@@ -1301,7 +1421,23 @@ module ActiveRecord
|
|
1301
1421
|
" CONSTRAINT #{k} UNIQUE (#{v.join(',')})"
|
1302
1422
|
end
|
1303
1423
|
end
|
1304
|
-
|
1424
|
+
|
1425
|
+
def structure_dump_indexes(table_name) #:nodoc:
|
1426
|
+
indexes(table_name).map do |options|
|
1427
|
+
column_names = options[:columns]
|
1428
|
+
options = {:name => options[:name], :unique => options[:unique]}
|
1429
|
+
index_name = index_name(table_name, :column => column_names)
|
1430
|
+
if Hash === options # legacy support, since this param was a string
|
1431
|
+
index_type = options[:unique] ? "UNIQUE" : ""
|
1432
|
+
index_name = options[:name] || index_name
|
1433
|
+
else
|
1434
|
+
index_type = options
|
1435
|
+
end
|
1436
|
+
quoted_column_names = column_names.map { |e| quote_column_name(e) }.join(", ")
|
1437
|
+
"CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{quoted_column_names})"
|
1438
|
+
end
|
1439
|
+
end
|
1440
|
+
|
1305
1441
|
def structure_dump_fk_constraints #:nodoc:
|
1306
1442
|
fks = select_all("select table_name from all_tables where owner = sys_context('userenv','session_user') order by 1").map do |table|
|
1307
1443
|
if respond_to?(:foreign_keys) && (foreign_keys = foreign_keys(table["table_name"])).any?
|
@@ -1312,18 +1448,24 @@ module ActiveRecord
|
|
1312
1448
|
sql << "#{foreign_key_definition(fk.to_table, fk.options)}"
|
1313
1449
|
end
|
1314
1450
|
end
|
1315
|
-
end.flatten.compact
|
1316
|
-
fks
|
1451
|
+
end.flatten.compact
|
1452
|
+
join_with_statement_token(fks)
|
1317
1453
|
end
|
1318
|
-
|
1454
|
+
|
1455
|
+
def dump_schema_information #:nodoc:
|
1456
|
+
sm_table = ActiveRecord::Migrator.schema_migrations_table_name
|
1457
|
+
migrated = select_values("SELECT version FROM #{sm_table}")
|
1458
|
+
join_with_statement_token(migrated.map{|v| "INSERT INTO #{sm_table} (version) VALUES ('#{v}')" })
|
1459
|
+
end
|
1460
|
+
|
1319
1461
|
# Extract all stored procedures, packages, synonyms and views.
|
1320
1462
|
def structure_dump_db_stored_code #:nodoc:
|
1321
|
-
structure =
|
1322
|
-
select_all("select distinct name, type
|
1323
|
-
from all_source
|
1324
|
-
where type in ('PROCEDURE', 'PACKAGE', 'PACKAGE BODY', 'FUNCTION', 'TRIGGER', 'TYPE')
|
1463
|
+
structure = []
|
1464
|
+
select_all("select distinct name, type
|
1465
|
+
from all_source
|
1466
|
+
where type in ('PROCEDURE', 'PACKAGE', 'PACKAGE BODY', 'FUNCTION', 'TRIGGER', 'TYPE')
|
1325
1467
|
and owner = sys_context('userenv','session_user') order by type").each do |source|
|
1326
|
-
ddl = "
|
1468
|
+
ddl = "CREATE OR REPLACE \n"
|
1327
1469
|
lines = select_all(%Q{
|
1328
1470
|
select text
|
1329
1471
|
from all_source
|
@@ -1332,79 +1474,62 @@ module ActiveRecord
|
|
1332
1474
|
and owner = sys_context('userenv','session_user')
|
1333
1475
|
order by line
|
1334
1476
|
}).map do |row|
|
1335
|
-
ddl << row['text']
|
1477
|
+
ddl << row['text']
|
1336
1478
|
end
|
1337
|
-
ddl << ";" unless ddl.strip
|
1338
|
-
structure << ddl
|
1479
|
+
ddl << ";" unless ddl.strip[-1,1] == ";"
|
1480
|
+
structure << ddl
|
1339
1481
|
end
|
1340
1482
|
|
1341
1483
|
# export views
|
1342
1484
|
select_all("select view_name, text from user_views").each do |view|
|
1343
|
-
|
1344
|
-
# any views with empty lines will cause OCI to barf when loading. remove blank lines =/
|
1345
|
-
ddl << view['text'].gsub(/^\n/, '')
|
1346
|
-
structure << ddl << STATEMENT_TOKEN
|
1485
|
+
structure << "CREATE OR REPLACE VIEW #{view['view_name']} AS\n #{view['text']}"
|
1347
1486
|
end
|
1348
1487
|
|
1349
|
-
# export synonyms
|
1488
|
+
# export synonyms
|
1350
1489
|
select_all("select owner, synonym_name, table_name, table_owner
|
1351
1490
|
from all_synonyms
|
1352
1491
|
where owner = sys_context('userenv','session_user') ").each do |synonym|
|
1353
|
-
|
1354
|
-
structure <<
|
1492
|
+
structure << "CREATE OR REPLACE #{synonym['owner'] == 'PUBLIC' ? 'PUBLIC' : '' } SYNONYM #{synonym['synonym_name']}"
|
1493
|
+
structure << " FOR #{synonym['table_owner']}.#{synonym['table_name']}"
|
1355
1494
|
end
|
1356
1495
|
|
1357
|
-
structure
|
1496
|
+
join_with_statement_token(structure)
|
1358
1497
|
end
|
1359
1498
|
|
1360
|
-
def structure_dump_indexes(table_name) #:nodoc:
|
1361
|
-
statements = indexes(table_name).map do |options|
|
1362
|
-
#def add_index(table_name, column_name, options = {})
|
1363
|
-
column_names = options[:columns]
|
1364
|
-
options = {:name => options[:name], :unique => options[:unique]}
|
1365
|
-
index_name = index_name(table_name, :column => column_names)
|
1366
|
-
if Hash === options # legacy support, since this param was a string
|
1367
|
-
index_type = options[:unique] ? "UNIQUE" : ""
|
1368
|
-
index_name = options[:name] || index_name
|
1369
|
-
else
|
1370
|
-
index_type = options
|
1371
|
-
end
|
1372
|
-
quoted_column_names = column_names.map { |e| quote_column_name(e) }.join(", ")
|
1373
|
-
"CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{quoted_column_names})"
|
1374
|
-
end
|
1375
|
-
statements.length > 0 ? "#{statements.join(STATEMENT_TOKEN)}#{STATEMENT_TOKEN}" : ''
|
1376
|
-
end
|
1377
|
-
|
1378
1499
|
def structure_drop #:nodoc:
|
1379
|
-
|
1380
|
-
|
1500
|
+
statements = select_values("select sequence_name from user_sequences order by 1").map do |seq|
|
1501
|
+
"DROP SEQUENCE \"#{seq}\""
|
1381
1502
|
end
|
1382
|
-
|
1383
|
-
|
1384
|
-
|
1385
|
-
|
1503
|
+
select_values("select table_name from all_tables t
|
1504
|
+
where owner = sys_context('userenv','session_user') and secondary='N'
|
1505
|
+
and not exists (select mv.mview_name from all_mviews mv where mv.owner = t.owner and mv.mview_name = t.table_name)
|
1506
|
+
order by 1").each do |table|
|
1507
|
+
statements << "DROP TABLE \"#{table}\" CASCADE CONSTRAINTS"
|
1386
1508
|
end
|
1509
|
+
join_with_statement_token(statements)
|
1387
1510
|
end
|
1388
|
-
|
1511
|
+
|
1389
1512
|
def temp_table_drop #:nodoc:
|
1390
|
-
|
1391
|
-
|
1392
|
-
|
1393
|
-
|
1513
|
+
join_with_statement_token(select_values(
|
1514
|
+
"select table_name from all_tables
|
1515
|
+
where owner = sys_context('userenv','session_user') and secondary='N' and temporary = 'Y' order by 1").map do |table|
|
1516
|
+
"DROP TABLE \"#{table}\" CASCADE CONSTRAINTS"
|
1517
|
+
end)
|
1394
1518
|
end
|
1395
|
-
|
1519
|
+
|
1396
1520
|
def full_drop(preserve_tables=false) #:nodoc:
|
1397
1521
|
s = preserve_tables ? [] : [structure_drop]
|
1398
1522
|
s << temp_table_drop if preserve_tables
|
1399
1523
|
s << drop_sql_for_feature("view")
|
1524
|
+
s << drop_sql_for_feature("materialized view")
|
1400
1525
|
s << drop_sql_for_feature("synonym")
|
1401
1526
|
s << drop_sql_for_feature("type")
|
1402
1527
|
s << drop_sql_for_object("package")
|
1403
1528
|
s << drop_sql_for_object("function")
|
1404
1529
|
s << drop_sql_for_object("procedure")
|
1405
|
-
s.join
|
1530
|
+
s.join
|
1406
1531
|
end
|
1407
|
-
|
1532
|
+
|
1408
1533
|
def add_column_options!(sql, options) #:nodoc:
|
1409
1534
|
type = options[:type] || ((column = options[:column]) && column.type)
|
1410
1535
|
type = type && type.to_sym
|
@@ -1452,9 +1577,6 @@ module ActiveRecord
|
|
1452
1577
|
select_value("select temporary from user_tables where table_name = '#{table_name.upcase}'") == 'Y'
|
1453
1578
|
end
|
1454
1579
|
|
1455
|
-
# statements separator used in structure dump
|
1456
|
-
STATEMENT_TOKEN = "\n\n--@@@--\n\n"
|
1457
|
-
|
1458
1580
|
# ORDER BY clause for the passed order option.
|
1459
1581
|
#
|
1460
1582
|
# Uses column aliases as defined by #distinct.
|
@@ -1530,8 +1652,8 @@ module ActiveRecord
|
|
1530
1652
|
"#{table_name.to_s[0,IDENTIFIER_MAX_LENGTH-4]}_pkt"
|
1531
1653
|
end
|
1532
1654
|
|
1533
|
-
def compress_lines(string,
|
1534
|
-
string.split($/).map { |line| line.strip }.join(
|
1655
|
+
def compress_lines(string, join_with = "\n")
|
1656
|
+
string.split($/).map { |line| line.strip }.join(join_with)
|
1535
1657
|
end
|
1536
1658
|
|
1537
1659
|
# virtual columns are an 11g feature. This returns [] if feature is not
|
@@ -1550,15 +1672,28 @@ module ActiveRecord
|
|
1550
1672
|
[]
|
1551
1673
|
end
|
1552
1674
|
end
|
1553
|
-
|
1675
|
+
|
1554
1676
|
def drop_sql_for_feature(type)
|
1555
|
-
|
1677
|
+
short_type = type == 'materialized view' ? 'mview' : type
|
1678
|
+
join_with_statement_token(
|
1679
|
+
select_values("select #{short_type}_name from user_#{short_type.tableize}").map do |name|
|
1680
|
+
"DROP #{type.upcase} \"#{name}\""
|
1681
|
+
end)
|
1556
1682
|
end
|
1557
|
-
|
1683
|
+
|
1558
1684
|
def drop_sql_for_object(type)
|
1559
|
-
|
1685
|
+
join_with_statement_token(
|
1686
|
+
select_values("select object_name from user_objects where object_type = '#{type.upcase}'").map do |name|
|
1687
|
+
"DROP #{type.upcase} \"#{name}\""
|
1688
|
+
end)
|
1560
1689
|
end
|
1561
|
-
|
1690
|
+
|
1691
|
+
def join_with_statement_token(array)
|
1692
|
+
string = array.join(STATEMENT_TOKEN)
|
1693
|
+
string << STATEMENT_TOKEN unless string.blank?
|
1694
|
+
string
|
1695
|
+
end
|
1696
|
+
|
1562
1697
|
public
|
1563
1698
|
# DBMS_OUTPUT =============================================
|
1564
1699
|
#
|
@@ -1607,7 +1742,7 @@ module ActiveRecord
|
|
1607
1742
|
while true do
|
1608
1743
|
result = plsql(:dbms_output).sys.dbms_output.get_line(:line => '', :status => 0)
|
1609
1744
|
break unless result[:status] == 0
|
1610
|
-
@logger.debug "DBMS_OUTPUT: #{result[:line]}"
|
1745
|
+
@logger.debug "DBMS_OUTPUT: #{result[:line]}" if @logger
|
1611
1746
|
end
|
1612
1747
|
end
|
1613
1748
|
|
@@ -1641,10 +1776,7 @@ require 'active_record/connection_adapters/oracle_enhanced_dirty'
|
|
1641
1776
|
begin
|
1642
1777
|
require 'active_record/connection_adapters/oracle_enhanced_tasks'
|
1643
1778
|
rescue LoadError
|
1644
|
-
end if defined?(RAILS_ROOT)
|
1645
|
-
|
1646
|
-
# Handles quoting of oracle reserved words
|
1647
|
-
require 'active_record/connection_adapters/oracle_enhanced_reserved_words'
|
1779
|
+
end if defined?(Rails.root) || defined?(RAILS_ROOT)
|
1648
1780
|
|
1649
1781
|
# Patches and enhancements for schema dumper
|
1650
1782
|
require 'active_record/connection_adapters/oracle_enhanced_schema_dumper'
|
@@ -1655,7 +1787,12 @@ require 'active_record/connection_adapters/oracle_enhanced_schema_statements_ext
|
|
1655
1787
|
# Extensions for schema definition
|
1656
1788
|
require 'active_record/connection_adapters/oracle_enhanced_schema_definitions'
|
1657
1789
|
|
1790
|
+
# Extensions for context index definition
|
1791
|
+
require 'active_record/connection_adapters/oracle_enhanced_context_index'
|
1792
|
+
|
1658
1793
|
# Add BigDecimal#to_d, Fixnum#to_d and Bignum#to_d methods if not already present
|
1659
1794
|
require 'active_record/connection_adapters/oracle_enhanced_core_ext'
|
1660
1795
|
|
1796
|
+
require 'active_record/connection_adapters/oracle_enhanced_activerecord_patches'
|
1797
|
+
|
1661
1798
|
require 'active_record/connection_adapters/oracle_enhanced_version'
|