activerecord-oracle_enhanced-adapter 1.3.0 → 1.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -1
- data/Gemfile +37 -0
- data/History.txt +17 -0
- data/README.rdoc +9 -4
- data/RUNNING_TESTS.rdoc +28 -0
- data/VERSION +1 -1
- data/activerecord-oracle_enhanced-adapter.gemspec +13 -6
- data/lib/active_record/connection_adapters/oracle_enhanced.rake +32 -24
- data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +19 -793
- data/lib/active_record/connection_adapters/oracle_enhanced_base_ext.rb +79 -0
- data/lib/active_record/connection_adapters/oracle_enhanced_column.rb +106 -0
- data/lib/active_record/connection_adapters/oracle_enhanced_context_index.rb +32 -2
- data/lib/active_record/connection_adapters/oracle_enhanced_core_ext.rb +3 -44
- data/lib/active_record/connection_adapters/oracle_enhanced_dirty.rb +2 -2
- data/lib/active_record/connection_adapters/oracle_enhanced_jdbc_connection.rb +4 -2
- data/lib/active_record/connection_adapters/oracle_enhanced_oci_connection.rb +19 -3
- data/lib/active_record/connection_adapters/oracle_enhanced_schema_dumper.rb +14 -6
- data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements.rb +331 -0
- data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements_ext.rb +46 -14
- data/lib/active_record/connection_adapters/oracle_enhanced_structure_dump.rb +290 -0
- data/lib/active_record/connection_adapters/oracle_enhanced_tasks.rb +15 -10
- data/lib/activerecord-oracle_enhanced-adapter.rb +25 -0
- data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +51 -19
- data/spec/active_record/connection_adapters/oracle_enhanced_connection_spec.rb +1 -1
- data/spec/active_record/connection_adapters/oracle_enhanced_context_index_spec.rb +31 -2
- data/spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb +152 -11
- data/spec/active_record/connection_adapters/oracle_enhanced_dirty_spec.rb +27 -0
- data/spec/active_record/connection_adapters/oracle_enhanced_schema_dump_spec.rb +19 -1
- data/spec/active_record/connection_adapters/{oracle_enhanced_schema_spec.rb → oracle_enhanced_schema_statements_spec.rb} +44 -1
- data/spec/active_record/connection_adapters/{oracle_enhanced_adapter_structure_dumper_spec.rb → oracle_enhanced_structure_dump_spec.rb} +49 -1
- data/spec/spec_helper.rb +60 -53
- metadata +15 -8
data/.gitignore
CHANGED
data/Gemfile
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
source 'http://rubygems.org'
|
2
|
+
|
3
|
+
gem 'jeweler'
|
4
|
+
gem 'rspec', "~> 1.3.0"
|
5
|
+
|
6
|
+
if ENV['RAILS_GEM_VERSION']
|
7
|
+
gem 'activerecord', "=#{ENV['RAILS_GEM_VERSION']}"
|
8
|
+
gem 'actionpack', "=#{ENV['RAILS_GEM_VERSION']}"
|
9
|
+
gem 'activesupport', "=#{ENV['RAILS_GEM_VERSION']}"
|
10
|
+
case ENV['RAILS_GEM_VERSION']
|
11
|
+
when /^2.0/
|
12
|
+
gem 'composite_primary_keys', '=0.9.93'
|
13
|
+
when /^2.1/
|
14
|
+
gem 'composite_primary_keys', '=1.0.8'
|
15
|
+
when /^2.2/
|
16
|
+
gem 'composite_primary_keys', '=2.2.2'
|
17
|
+
when /^2.3.3/
|
18
|
+
gem 'composite_primary_keys', '=2.3.2'
|
19
|
+
when /^3/
|
20
|
+
gem 'railties', "=#{ENV['RAILS_GEM_VERSION']}"
|
21
|
+
end
|
22
|
+
else
|
23
|
+
# uses local copy of Rails 3 and Arel gems
|
24
|
+
ENV['RAILS_GEM_PATH'] ||= '../rails'
|
25
|
+
%w(activerecord activemodel activesupport actionpack railties).each do |gem_name|
|
26
|
+
gem gem_name, :path => File.join(ENV['RAILS_GEM_PATH'], gem_name)
|
27
|
+
end
|
28
|
+
|
29
|
+
ENV['AREL_GEM_PATH'] ||= '../arel'
|
30
|
+
gem 'arel', :path => ENV['AREL_GEM_PATH']
|
31
|
+
end
|
32
|
+
|
33
|
+
if !defined?(RUBY_ENGINE) || RUBY_ENGINE == 'ruby'
|
34
|
+
gem 'ruby-oci8', '>=2.0.4'
|
35
|
+
end
|
36
|
+
|
37
|
+
gem 'ruby-plsql', '>=0.4.3'
|
data/History.txt
CHANGED
@@ -1,3 +1,20 @@
|
|
1
|
+
== 1.3.1 2010-09-09
|
2
|
+
|
3
|
+
* Enhancements:
|
4
|
+
* Tested with Rails 3.0.0 release
|
5
|
+
* Lexer options for context index creation
|
6
|
+
* Added Bundler for running adapter specs, added RUNNING_TESTS.rdoc with description how to run specs
|
7
|
+
* Connection to database using :host, :port and :database options
|
8
|
+
* Improved loading of adapter in Rails 3 using railtie
|
9
|
+
* Bug fixes:
|
10
|
+
* Fix for custom context index procedure when indexing records with null values
|
11
|
+
* Quote table and column names in write_lobs callback
|
12
|
+
* Fix for incorrect column SQL types when two models use the same table and AR query cache is enabled
|
13
|
+
* Fixes for schema and scructure dump tasks
|
14
|
+
* Fix for handling of zero-length strings in BLOB and CLOB columns
|
15
|
+
* removed String.mb_chars upcase and downcase methods for Ruby 1.9 as Rails 3.0.0 already includes Unicode aware upcase and downcase methods for Ruby 1.9
|
16
|
+
* Fixes for latest ActiveRecord unit tests
|
17
|
+
|
1
18
|
== 1.3.0 2010-06-21
|
2
19
|
|
3
20
|
* Enhancements:
|
data/README.rdoc
CHANGED
@@ -11,16 +11,15 @@ See http://wiki.github.com/rsim/oracle-enhanced for usage information.
|
|
11
11
|
|
12
12
|
For questions and feature discussion please use http://groups.google.com/group/oracle-enhanced
|
13
13
|
|
14
|
-
Blog posts about
|
14
|
+
Blog posts about oracle_enhanced can be found at http://blog.rayapps.com/category/oracle_enhanced
|
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
20
|
* MRI - requires ruby-oci8 2.0 gem to connect to Oracle (2.0.4 or later recommended)
|
21
|
-
* Ruby/YARV 1.9.
|
22
|
-
|
23
|
-
* JRuby - uses JDBC driver ojdbc14.jar to connect to Oracle (should be in JRUBY_HOME/lib or in Java class path)
|
21
|
+
* Ruby/YARV 1.9.2 - requires ruby-oci8 2.0 library to connect to Oracle
|
22
|
+
* JRuby 1.5 - uses JDBC driver ojdbc14.jar to connect to Oracle (should be in JRUBY_HOME/lib or in Java class path)
|
24
23
|
* 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)
|
25
24
|
|
26
25
|
== INSTALL:
|
@@ -29,6 +28,10 @@ Blog posts about oracle-enahnced can be found at http://blog.rayapps.com/categor
|
|
29
28
|
|
30
29
|
In addition install either ruby-oci8 (for MRI/YARV) or copy Oracle JDBC driver to $JRUBY_HOME/lib (for JRuby).
|
31
30
|
|
31
|
+
== RUNNING TESTS:
|
32
|
+
|
33
|
+
See RUNNING_TESTS.rdoc
|
34
|
+
|
32
35
|
== LINKS
|
33
36
|
|
34
37
|
* Source code: http://github.com/rsim/oracle-enhanced
|
@@ -54,6 +57,8 @@ In addition install either ruby-oci8 (for MRI/YARV) or copy Oracle JDBC driver t
|
|
54
57
|
* Edvard Majakari
|
55
58
|
* Beau Fabry
|
56
59
|
* Simon Chiang
|
60
|
+
* Peter Nyberg
|
61
|
+
* Dwayne Litzenberger
|
57
62
|
|
58
63
|
== LICENSE:
|
59
64
|
|
data/RUNNING_TESTS.rdoc
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
== Creating the test database
|
2
|
+
|
3
|
+
You need Oracle database (version 10.2 or later) with SYS and SYSTEM use access.
|
4
|
+
|
5
|
+
If you are on a Mac OS X 10.6 then use these instructions http://blog.rayapps.com/2009/09/14/how-to-install-oracle-database-10g-on-mac-os-x-snow-leopard to install local Oracle DB 10.2.0.4. Other option is to use Linux VM and install Oracle DB on it.
|
6
|
+
|
7
|
+
If you are on Linux (or will use Linux virtual machine) and need Oracle DB just for running tests then Oracle DB XE edition is enough. See http://www.oracle.com/technetwork/database/express-edition/downloads/index.html for download links and instructions.
|
8
|
+
|
9
|
+
If you are getting ORA-12520 errors when running tests then it means that Oracle cannot create enough processes to handle many connections (as during tests many connections are created and destroyed). In this case you need to log in as SYSTEM user and execute e.g.
|
10
|
+
alter system set processes=200 scope=spfile;
|
11
|
+
to increase process limit and then restart the database (this will be necessary if Oracle XE will be used as default processes limit is 40).
|
12
|
+
|
13
|
+
== Ruby versions
|
14
|
+
|
15
|
+
It is recommended to use RVM (http://rvm.beginrescueend.com) to run tests with different Ruby implementations. oracle_enhanced is mainly tested with MRI 1.8.7 (all Rails versions) and 1.9.2 (Rails 3) and JRuby 1.5.
|
16
|
+
|
17
|
+
== Running tests
|
18
|
+
|
19
|
+
* Create Oracle database schema for test purposes. Review spec/spec_helper.rb to see default schema/user names and database names (use environment variables to override defaults)
|
20
|
+
* If you use RVM then switch to corresponding Ruby (1.8.7, 1.9.2 or JRuby) and it is recommended to create isolated gemset for test purposes (e.g. rvm create gemset oracle_enhanced)
|
21
|
+
* Install bundler with
|
22
|
+
gem install bundler
|
23
|
+
* Set RAILS_GEM_VERSION to Rails version that you would like to use in oracle_enhanced tests, e.g.
|
24
|
+
export RAILS_GEM_VERSION=3.0.0
|
25
|
+
* Install necessary gems with
|
26
|
+
bundle install
|
27
|
+
* Run tests with
|
28
|
+
rake spec
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.3.
|
1
|
+
1.3.1
|
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{activerecord-oracle_enhanced-adapter}
|
8
|
-
s.version = "1.3.
|
8
|
+
s.version = "1.3.1"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Raimonds Simanovskis"]
|
12
|
-
s.date = %q{2010-
|
12
|
+
s.date = %q{2010-09-09}
|
13
13
|
s.description = %q{Oracle "enhanced" ActiveRecord adapter contains useful additional methods for working with new and legacy Oracle databases.
|
14
14
|
This adapter is superset of original ActiveRecord Oracle adapter.
|
15
15
|
}
|
@@ -19,9 +19,11 @@ This adapter is superset of original ActiveRecord Oracle adapter.
|
|
19
19
|
]
|
20
20
|
s.files = [
|
21
21
|
".gitignore",
|
22
|
+
"Gemfile",
|
22
23
|
"History.txt",
|
23
24
|
"License.txt",
|
24
25
|
"README.rdoc",
|
26
|
+
"RUNNING_TESTS.rdoc",
|
25
27
|
"Rakefile",
|
26
28
|
"VERSION",
|
27
29
|
"activerecord-oracle_enhanced-adapter.gemspec",
|
@@ -29,6 +31,8 @@ This adapter is superset of original ActiveRecord Oracle adapter.
|
|
29
31
|
"lib/active_record/connection_adapters/oracle_enhanced.rake",
|
30
32
|
"lib/active_record/connection_adapters/oracle_enhanced_activerecord_patches.rb",
|
31
33
|
"lib/active_record/connection_adapters/oracle_enhanced_adapter.rb",
|
34
|
+
"lib/active_record/connection_adapters/oracle_enhanced_base_ext.rb",
|
35
|
+
"lib/active_record/connection_adapters/oracle_enhanced_column.rb",
|
32
36
|
"lib/active_record/connection_adapters/oracle_enhanced_connection.rb",
|
33
37
|
"lib/active_record/connection_adapters/oracle_enhanced_context_index.rb",
|
34
38
|
"lib/active_record/connection_adapters/oracle_enhanced_core_ext.rb",
|
@@ -39,11 +43,13 @@ This adapter is superset of original ActiveRecord Oracle adapter.
|
|
39
43
|
"lib/active_record/connection_adapters/oracle_enhanced_procedures.rb",
|
40
44
|
"lib/active_record/connection_adapters/oracle_enhanced_schema_definitions.rb",
|
41
45
|
"lib/active_record/connection_adapters/oracle_enhanced_schema_dumper.rb",
|
46
|
+
"lib/active_record/connection_adapters/oracle_enhanced_schema_statements.rb",
|
42
47
|
"lib/active_record/connection_adapters/oracle_enhanced_schema_statements_ext.rb",
|
48
|
+
"lib/active_record/connection_adapters/oracle_enhanced_structure_dump.rb",
|
43
49
|
"lib/active_record/connection_adapters/oracle_enhanced_tasks.rb",
|
44
50
|
"lib/active_record/connection_adapters/oracle_enhanced_version.rb",
|
51
|
+
"lib/activerecord-oracle_enhanced-adapter.rb",
|
45
52
|
"spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb",
|
46
|
-
"spec/active_record/connection_adapters/oracle_enhanced_adapter_structure_dumper_spec.rb",
|
47
53
|
"spec/active_record/connection_adapters/oracle_enhanced_connection_spec.rb",
|
48
54
|
"spec/active_record/connection_adapters/oracle_enhanced_context_index_spec.rb",
|
49
55
|
"spec/active_record/connection_adapters/oracle_enhanced_core_ext_spec.rb",
|
@@ -54,7 +60,8 @@ This adapter is superset of original ActiveRecord Oracle adapter.
|
|
54
60
|
"spec/active_record/connection_adapters/oracle_enhanced_emulate_oracle_adapter_spec.rb",
|
55
61
|
"spec/active_record/connection_adapters/oracle_enhanced_procedures_spec.rb",
|
56
62
|
"spec/active_record/connection_adapters/oracle_enhanced_schema_dump_spec.rb",
|
57
|
-
"spec/active_record/connection_adapters/
|
63
|
+
"spec/active_record/connection_adapters/oracle_enhanced_schema_statements_spec.rb",
|
64
|
+
"spec/active_record/connection_adapters/oracle_enhanced_structure_dump_spec.rb",
|
58
65
|
"spec/spec.opts",
|
59
66
|
"spec/spec_helper.rb"
|
60
67
|
]
|
@@ -65,7 +72,6 @@ This adapter is superset of original ActiveRecord Oracle adapter.
|
|
65
72
|
s.summary = %q{Oracle enhanced adapter for ActiveRecord}
|
66
73
|
s.test_files = [
|
67
74
|
"spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb",
|
68
|
-
"spec/active_record/connection_adapters/oracle_enhanced_adapter_structure_dumper_spec.rb",
|
69
75
|
"spec/active_record/connection_adapters/oracle_enhanced_connection_spec.rb",
|
70
76
|
"spec/active_record/connection_adapters/oracle_enhanced_context_index_spec.rb",
|
71
77
|
"spec/active_record/connection_adapters/oracle_enhanced_core_ext_spec.rb",
|
@@ -76,7 +82,8 @@ This adapter is superset of original ActiveRecord Oracle adapter.
|
|
76
82
|
"spec/active_record/connection_adapters/oracle_enhanced_emulate_oracle_adapter_spec.rb",
|
77
83
|
"spec/active_record/connection_adapters/oracle_enhanced_procedures_spec.rb",
|
78
84
|
"spec/active_record/connection_adapters/oracle_enhanced_schema_dump_spec.rb",
|
79
|
-
"spec/active_record/connection_adapters/
|
85
|
+
"spec/active_record/connection_adapters/oracle_enhanced_schema_statements_spec.rb",
|
86
|
+
"spec/active_record/connection_adapters/oracle_enhanced_structure_dump_spec.rb",
|
80
87
|
"spec/spec_helper.rb"
|
81
88
|
]
|
82
89
|
|
@@ -1,52 +1,60 @@
|
|
1
|
-
#
|
1
|
+
# implementation idea taken from JDBC adapter
|
2
|
+
# added possibility to execute previously defined task (passed as argument to task block)
|
2
3
|
def redefine_task(*args, &block)
|
3
4
|
task_name = Hash === args.first ? args.first.keys[0] : args.first
|
4
5
|
existing_task = Rake.application.lookup task_name
|
6
|
+
existing_actions = nil
|
5
7
|
if existing_task
|
6
|
-
class << existing_task; public :instance_variable_set; end
|
8
|
+
class << existing_task; public :instance_variable_set, :instance_variable_get; end
|
7
9
|
existing_task.instance_variable_set "@prerequisites", FileList[]
|
10
|
+
existing_actions = existing_task.instance_variable_get "@actions"
|
8
11
|
existing_task.instance_variable_set "@actions", []
|
9
12
|
end
|
10
|
-
task(*args
|
13
|
+
task(*args) do
|
14
|
+
block.call(existing_actions)
|
15
|
+
end
|
11
16
|
end
|
12
17
|
|
13
18
|
namespace :db do
|
14
19
|
|
15
20
|
namespace :structure do
|
16
|
-
redefine_task :dump => :environment do
|
21
|
+
redefine_task :dump => :environment do |existing_actions|
|
17
22
|
abcs = ActiveRecord::Base.configurations
|
18
23
|
rails_env = defined?(Rails.env) ? Rails.env : RAILS_ENV
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
24
|
+
if abcs[rails_env]['adapter'] == 'oracle_enhanced'
|
25
|
+
ActiveRecord::Base.establish_connection(abcs[rails_env])
|
26
|
+
File.open("db/#{rails_env}_structure.sql", "w+") { |f| f << ActiveRecord::Base.connection.structure_dump }
|
27
|
+
if ActiveRecord::Base.connection.supports_migrations?
|
28
|
+
File.open("db/#{rails_env}_structure.sql", "a") { |f| f << ActiveRecord::Base.connection.dump_schema_information }
|
29
|
+
end
|
30
|
+
if abcs[rails_env]['structure_dump'] == "db_stored_code"
|
31
|
+
File.open("db/#{rails_env}_structure.sql", "a") { |f| f << ActiveRecord::Base.connection.structure_dump_db_stored_code }
|
32
|
+
end
|
33
|
+
else
|
34
|
+
Array(existing_actions).each{|action| action.call}
|
26
35
|
end
|
27
|
-
|
28
36
|
end
|
29
37
|
end
|
30
38
|
|
31
39
|
namespace :test do
|
32
|
-
redefine_task :clone_structure => [ "db:structure:dump", "db:test:purge" ] do
|
40
|
+
redefine_task :clone_structure => [ "db:structure:dump", "db:test:purge" ] do |existing_actions|
|
33
41
|
abcs = ActiveRecord::Base.configurations
|
34
42
|
rails_env = defined?(Rails.env) ? Rails.env : RAILS_ENV
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
43
|
+
if abcs[rails_env]['adapter'] == 'oracle_enhanced' && abcs['test']['adapter'] == 'oracle_enhanced'
|
44
|
+
ActiveRecord::Base.establish_connection(:test)
|
45
|
+
ActiveRecord::Base.connection.execute_structure_dump(File.read("db/#{rails_env}_structure.sql"))
|
46
|
+
else
|
47
|
+
Array(existing_actions).each{|action| action.call}
|
40
48
|
end
|
41
49
|
end
|
42
50
|
|
43
|
-
redefine_task :purge => :environment do
|
51
|
+
redefine_task :purge => :environment do |existing_actions|
|
44
52
|
abcs = ActiveRecord::Base.configurations
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
53
|
+
if abcs['test']['adapter'] == 'oracle_enhanced'
|
54
|
+
ActiveRecord::Base.establish_connection(:test)
|
55
|
+
ActiveRecord::Base.connection.execute_structure_dump(ActiveRecord::Base.connection.full_drop)
|
56
|
+
else
|
57
|
+
Array(existing_actions).each{|action| action.call}
|
50
58
|
end
|
51
59
|
end
|
52
60
|
|
@@ -35,189 +35,13 @@ require 'active_record/version' unless defined?(ActiveRecord::VERSION)
|
|
35
35
|
require 'active_record/connection_adapters/abstract_adapter'
|
36
36
|
require 'active_record/connection_adapters/oracle_enhanced_connection'
|
37
37
|
|
38
|
+
require 'active_record/connection_adapters/oracle_enhanced_base_ext'
|
39
|
+
require 'active_record/connection_adapters/oracle_enhanced_column'
|
40
|
+
|
38
41
|
require 'digest/sha1'
|
39
42
|
|
40
43
|
module ActiveRecord
|
41
|
-
class Base
|
42
|
-
# Establishes a connection to the database that's used by all Active Record objects.
|
43
|
-
def self.oracle_enhanced_connection(config) #:nodoc:
|
44
|
-
if config[:emulate_oracle_adapter] == true
|
45
|
-
# allows the enhanced adapter to look like the OracleAdapter. Useful to pick up
|
46
|
-
# conditionals in the rails activerecord test suite
|
47
|
-
require 'active_record/connection_adapters/emulation/oracle_adapter'
|
48
|
-
ConnectionAdapters::OracleAdapter.new(
|
49
|
-
ConnectionAdapters::OracleEnhancedConnection.create(config), logger)
|
50
|
-
else
|
51
|
-
ConnectionAdapters::OracleEnhancedAdapter.new(
|
52
|
-
ConnectionAdapters::OracleEnhancedConnection.create(config), logger)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
# Specify table columns which should be ignored by ActiveRecord, e.g.:
|
57
|
-
#
|
58
|
-
# ignore_table_columns :attribute1, :attribute2
|
59
|
-
def self.ignore_table_columns(*args)
|
60
|
-
connection.ignore_table_columns(table_name,*args)
|
61
|
-
end
|
62
|
-
|
63
|
-
# Specify which table columns should be typecasted to Date (without time), e.g.:
|
64
|
-
#
|
65
|
-
# set_date_columns :created_on, :updated_on
|
66
|
-
def self.set_date_columns(*args)
|
67
|
-
connection.set_type_for_columns(table_name,:date,*args)
|
68
|
-
end
|
69
|
-
|
70
|
-
# Specify which table columns should be typecasted to Time (or DateTime), e.g.:
|
71
|
-
#
|
72
|
-
# set_datetime_columns :created_date, :updated_date
|
73
|
-
def self.set_datetime_columns(*args)
|
74
|
-
connection.set_type_for_columns(table_name,:datetime,*args)
|
75
|
-
end
|
76
|
-
|
77
|
-
# Specify which table columns should be typecasted to boolean values +true+ or +false+, e.g.:
|
78
|
-
#
|
79
|
-
# set_boolean_columns :is_valid, :is_completed
|
80
|
-
def self.set_boolean_columns(*args)
|
81
|
-
connection.set_type_for_columns(table_name,:boolean,*args)
|
82
|
-
end
|
83
|
-
|
84
|
-
# Specify which table columns should be typecasted to integer values.
|
85
|
-
# Might be useful to force NUMBER(1) column to be integer and not boolean, or force NUMBER column without
|
86
|
-
# scale to be retrieved as integer and not decimal. Example:
|
87
|
-
#
|
88
|
-
# set_integer_columns :version_number, :object_identifier
|
89
|
-
def self.set_integer_columns(*args)
|
90
|
-
connection.set_type_for_columns(table_name,:integer,*args)
|
91
|
-
end
|
92
|
-
|
93
|
-
# Specify which table columns should be typecasted to string values.
|
94
|
-
# Might be useful to specify that columns should be string even if its name matches boolean column criteria.
|
95
|
-
#
|
96
|
-
# set_string_columns :active_flag
|
97
|
-
def self.set_string_columns(*args)
|
98
|
-
connection.set_type_for_columns(table_name,:string,*args)
|
99
|
-
end
|
100
|
-
|
101
|
-
# After setting large objects to empty, select the OCI8::LOB
|
102
|
-
# and write back the data.
|
103
|
-
after_save :enhanced_write_lobs
|
104
|
-
def enhanced_write_lobs #:nodoc:
|
105
|
-
if connection.is_a?(ConnectionAdapters::OracleEnhancedAdapter) &&
|
106
|
-
!(self.class.custom_create_method || self.class.custom_update_method)
|
107
|
-
connection.write_lobs(self.class.table_name, self.class, attributes)
|
108
|
-
end
|
109
|
-
end
|
110
|
-
private :enhanced_write_lobs
|
111
|
-
|
112
|
-
# Get table comment from schema definition.
|
113
|
-
def self.table_comment
|
114
|
-
connection.table_comment(self.table_name)
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
|
119
44
|
module ConnectionAdapters #:nodoc:
|
120
|
-
class OracleEnhancedColumn < Column
|
121
|
-
|
122
|
-
attr_reader :table_name, :forced_column_type #:nodoc:
|
123
|
-
|
124
|
-
def initialize(name, default, sql_type = nil, null = true, table_name = nil, forced_column_type = nil) #:nodoc:
|
125
|
-
@table_name = table_name
|
126
|
-
@forced_column_type = forced_column_type
|
127
|
-
super(name, default, sql_type, null)
|
128
|
-
end
|
129
|
-
|
130
|
-
def type_cast(value) #:nodoc:
|
131
|
-
return guess_date_or_time(value) if type == :datetime && OracleEnhancedAdapter.emulate_dates
|
132
|
-
super
|
133
|
-
end
|
134
|
-
|
135
|
-
# convert something to a boolean
|
136
|
-
# added y as boolean value
|
137
|
-
def self.value_to_boolean(value) #:nodoc:
|
138
|
-
if value == true || value == false
|
139
|
-
value
|
140
|
-
elsif value.is_a?(String) && value.blank?
|
141
|
-
nil
|
142
|
-
else
|
143
|
-
%w(true t 1 y +).include?(value.to_s.downcase)
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
# convert Time or DateTime value to Date for :date columns
|
148
|
-
def self.string_to_date(string) #:nodoc:
|
149
|
-
return string.to_date if string.is_a?(Time) || string.is_a?(DateTime)
|
150
|
-
super
|
151
|
-
end
|
152
|
-
|
153
|
-
# convert Date value to Time for :datetime columns
|
154
|
-
def self.string_to_time(string) #:nodoc:
|
155
|
-
return string.to_time if string.is_a?(Date) && !OracleEnhancedAdapter.emulate_dates
|
156
|
-
super
|
157
|
-
end
|
158
|
-
|
159
|
-
# Get column comment from schema definition.
|
160
|
-
# Will work only if using default ActiveRecord connection.
|
161
|
-
def comment
|
162
|
-
ActiveRecord::Base.connection.column_comment(@table_name, name)
|
163
|
-
end
|
164
|
-
|
165
|
-
private
|
166
|
-
def simplified_type(field_type)
|
167
|
-
forced_column_type ||
|
168
|
-
case field_type
|
169
|
-
when /decimal|numeric|number/i
|
170
|
-
return :boolean if OracleEnhancedAdapter.emulate_booleans && field_type == 'NUMBER(1)'
|
171
|
-
return :integer if extract_scale(field_type) == 0
|
172
|
-
# if column name is ID or ends with _ID
|
173
|
-
return :integer if OracleEnhancedAdapter.emulate_integers_by_column_name && OracleEnhancedAdapter.is_integer_column?(name, table_name)
|
174
|
-
:decimal
|
175
|
-
when /char/i
|
176
|
-
return :boolean if OracleEnhancedAdapter.emulate_booleans_from_strings &&
|
177
|
-
OracleEnhancedAdapter.is_boolean_column?(name, field_type, table_name)
|
178
|
-
:string
|
179
|
-
when /date/i
|
180
|
-
forced_column_type ||
|
181
|
-
(:date if OracleEnhancedAdapter.emulate_dates_by_column_name && OracleEnhancedAdapter.is_date_column?(name, table_name)) ||
|
182
|
-
:datetime
|
183
|
-
when /timestamp/i then :timestamp
|
184
|
-
when /time/i then :datetime
|
185
|
-
else super
|
186
|
-
end
|
187
|
-
end
|
188
|
-
|
189
|
-
def guess_date_or_time(value)
|
190
|
-
value.respond_to?(:hour) && (value.hour == 0 and value.min == 0 and value.sec == 0) ?
|
191
|
-
Date.new(value.year, value.month, value.day) : value
|
192
|
-
end
|
193
|
-
|
194
|
-
class << self
|
195
|
-
protected
|
196
|
-
|
197
|
-
def fallback_string_to_date(string) #:nodoc:
|
198
|
-
if OracleEnhancedAdapter.string_to_date_format || OracleEnhancedAdapter.string_to_time_format
|
199
|
-
return (string_to_date_or_time_using_format(string).to_date rescue super)
|
200
|
-
end
|
201
|
-
super
|
202
|
-
end
|
203
|
-
|
204
|
-
def fallback_string_to_time(string) #:nodoc:
|
205
|
-
if OracleEnhancedAdapter.string_to_time_format || OracleEnhancedAdapter.string_to_date_format
|
206
|
-
return (string_to_date_or_time_using_format(string).to_time rescue super)
|
207
|
-
end
|
208
|
-
super
|
209
|
-
end
|
210
|
-
|
211
|
-
def string_to_date_or_time_using_format(string) #:nodoc:
|
212
|
-
if OracleEnhancedAdapter.string_to_time_format && dt=Date._strptime(string, OracleEnhancedAdapter.string_to_time_format)
|
213
|
-
return Time.mktime(*dt.values_at(:year, :mon, :mday, :hour, :min, :sec, :zone, :wday))
|
214
|
-
end
|
215
|
-
DateTime.strptime(string, OracleEnhancedAdapter.string_to_date_format).to_date
|
216
|
-
end
|
217
|
-
|
218
|
-
end
|
219
|
-
end
|
220
|
-
|
221
45
|
|
222
46
|
# Oracle enhanced adapter will work with both
|
223
47
|
# Ruby 1.8/1.9 ruby-oci8 gem (which provides interface to Oracle OCI client)
|
@@ -730,8 +554,8 @@ module ActiveRecord
|
|
730
554
|
next if value.nil? || (value == '')
|
731
555
|
value = value.to_yaml if col.text? && klass.serialized_attributes[col.name]
|
732
556
|
uncached do
|
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"
|
557
|
+
sql = is_with_cpk ? "SELECT #{quote_column_name(col.name)} FROM #{quote_table_name(table_name)} WHERE #{klass.composite_where_clause(id)} FOR UPDATE" :
|
558
|
+
"SELECT #{quote_column_name(col.name)} FROM #{quote_table_name(table_name)} WHERE #{quote_column_name(klass.primary_key)} = #{id} FOR UPDATE"
|
735
559
|
unless lob_record = select_one(sql, 'Writable Large Object')
|
736
560
|
raise ActiveRecord::RecordNotFound, "statement #{sql} returned no rows"
|
737
561
|
end
|
@@ -741,27 +565,6 @@ module ActiveRecord
|
|
741
565
|
end
|
742
566
|
end
|
743
567
|
|
744
|
-
# change LOB column for ORDER BY clause
|
745
|
-
# just first 100 characters are taken for ordering
|
746
|
-
def lob_order_by_expression(klass, order) #:nodoc:
|
747
|
-
return order if order.nil?
|
748
|
-
changed = false
|
749
|
-
new_order = order.to_s.strip.split(/, */).map do |order_by_col|
|
750
|
-
column_name, asc_desc = order_by_col.split(/ +/)
|
751
|
-
if column = klass.columns.detect { |col| col.name == column_name && col.sql_type =~ /LOB$/i}
|
752
|
-
changed = true
|
753
|
-
"DBMS_LOB.SUBSTR(#{column_name},100,1) #{asc_desc}"
|
754
|
-
else
|
755
|
-
order_by_col
|
756
|
-
end
|
757
|
-
end.join(', ')
|
758
|
-
changed ? new_order : order
|
759
|
-
end
|
760
|
-
|
761
|
-
# SCHEMA STATEMENTS ========================================
|
762
|
-
#
|
763
|
-
# see: abstract/schema_statements.rb
|
764
|
-
|
765
568
|
# Current database name
|
766
569
|
def current_database
|
767
570
|
select_value("select sys_context('userenv','db_name') from dual")
|
@@ -959,7 +762,7 @@ module ActiveRecord
|
|
959
762
|
end.map do |row|
|
960
763
|
limit, scale = row['limit'], row['scale']
|
961
764
|
if limit || scale
|
962
|
-
row['sql_type']
|
765
|
+
row['sql_type'] += "(#{(limit || 38).to_i}" + ((scale = scale.to_i) > 0 ? ",#{scale})" : ")")
|
963
766
|
end
|
964
767
|
|
965
768
|
# clean up odd default spacing from Oracle
|
@@ -1005,277 +808,6 @@ module ActiveRecord
|
|
1005
808
|
cattr_accessor :default_sequence_start_value
|
1006
809
|
self.default_sequence_start_value = 10000
|
1007
810
|
|
1008
|
-
# Additional options for +create_table+ method in migration files.
|
1009
|
-
#
|
1010
|
-
# You can specify individual starting value in table creation migration file, e.g.:
|
1011
|
-
#
|
1012
|
-
# create_table :users, :sequence_start_value => 100 do |t|
|
1013
|
-
# # ...
|
1014
|
-
# end
|
1015
|
-
#
|
1016
|
-
# You can also specify other sequence definition additional parameters, e.g.:
|
1017
|
-
#
|
1018
|
-
# create_table :users, :sequence_start_value => “100 NOCACHE INCREMENT BY 10” do |t|
|
1019
|
-
# # ...
|
1020
|
-
# end
|
1021
|
-
#
|
1022
|
-
# Create primary key trigger (so that you can skip primary key value in INSERT statement).
|
1023
|
-
# By default trigger name will be "table_name_pkt", you can override the name with
|
1024
|
-
# :trigger_name option (but it is not recommended to override it as then this trigger will
|
1025
|
-
# not be detected by ActiveRecord model and it will still do prefetching of sequence value).
|
1026
|
-
# Example:
|
1027
|
-
#
|
1028
|
-
# create_table :users, :primary_key_trigger => true do |t|
|
1029
|
-
# # ...
|
1030
|
-
# end
|
1031
|
-
#
|
1032
|
-
# It is possible to add table and column comments in table creation migration files:
|
1033
|
-
#
|
1034
|
-
# create_table :employees, :comment => “Employees and contractors” do |t|
|
1035
|
-
# t.string :first_name, :comment => “Given name”
|
1036
|
-
# t.string :last_name, :comment => “Surname”
|
1037
|
-
# end
|
1038
|
-
|
1039
|
-
def create_table(name, options = {}, &block)
|
1040
|
-
create_sequence = options[:id] != false
|
1041
|
-
column_comments = {}
|
1042
|
-
|
1043
|
-
table_definition = TableDefinition.new(self)
|
1044
|
-
table_definition.primary_key(options[:primary_key] || Base.get_primary_key(name.to_s.singularize)) unless options[:id] == false
|
1045
|
-
|
1046
|
-
# store that primary key was defined in create_table block
|
1047
|
-
unless create_sequence
|
1048
|
-
class << table_definition
|
1049
|
-
attr_accessor :create_sequence
|
1050
|
-
def primary_key(*args)
|
1051
|
-
self.create_sequence = true
|
1052
|
-
super(*args)
|
1053
|
-
end
|
1054
|
-
end
|
1055
|
-
end
|
1056
|
-
|
1057
|
-
# store column comments
|
1058
|
-
class << table_definition
|
1059
|
-
attr_accessor :column_comments
|
1060
|
-
def column(name, type, options = {})
|
1061
|
-
if options[:comment]
|
1062
|
-
self.column_comments ||= {}
|
1063
|
-
self.column_comments[name] = options[:comment]
|
1064
|
-
end
|
1065
|
-
super(name, type, options)
|
1066
|
-
end
|
1067
|
-
end
|
1068
|
-
|
1069
|
-
result = block.call(table_definition) if block
|
1070
|
-
create_sequence = create_sequence || table_definition.create_sequence
|
1071
|
-
column_comments = table_definition.column_comments if table_definition.column_comments
|
1072
|
-
tablespace = options[:tablespace] ? " TABLESPACE #{options[:tablespace]}" : ""
|
1073
|
-
|
1074
|
-
if options[:force] && table_exists?(name)
|
1075
|
-
drop_table(name, options)
|
1076
|
-
end
|
1077
|
-
|
1078
|
-
create_sql = "CREATE#{' GLOBAL TEMPORARY' if options[:temporary]} TABLE "
|
1079
|
-
create_sql << "#{quote_table_name(name)} ("
|
1080
|
-
create_sql << table_definition.to_sql
|
1081
|
-
create_sql << ")#{tablespace} #{options[:options]}"
|
1082
|
-
execute create_sql
|
1083
|
-
|
1084
|
-
create_sequence_and_trigger(name, options) if create_sequence
|
1085
|
-
|
1086
|
-
add_table_comment name, options[:comment]
|
1087
|
-
column_comments.each do |column_name, comment|
|
1088
|
-
add_comment name, column_name, comment
|
1089
|
-
end
|
1090
|
-
|
1091
|
-
end
|
1092
|
-
|
1093
|
-
def rename_table(name, new_name) #:nodoc:
|
1094
|
-
execute "RENAME #{quote_table_name(name)} TO #{quote_table_name(new_name)}"
|
1095
|
-
execute "RENAME #{quote_table_name("#{name}_seq")} TO #{quote_table_name("#{new_name}_seq")}" rescue nil
|
1096
|
-
end
|
1097
|
-
|
1098
|
-
def drop_table(name, options = {}) #:nodoc:
|
1099
|
-
super(name)
|
1100
|
-
seq_name = options[:sequence_name] || default_sequence_name(name)
|
1101
|
-
execute "DROP SEQUENCE #{quote_table_name(seq_name)}" rescue nil
|
1102
|
-
ensure
|
1103
|
-
clear_table_columns_cache(name)
|
1104
|
-
end
|
1105
|
-
|
1106
|
-
# clear cached indexes when adding new index
|
1107
|
-
def add_index(table_name, column_name, options = {}) #:nodoc:
|
1108
|
-
column_names = Array(column_name)
|
1109
|
-
index_name = index_name(table_name, :column => column_names)
|
1110
|
-
|
1111
|
-
if Hash === options # legacy support, since this param was a string
|
1112
|
-
index_type = options[:unique] ? "UNIQUE" : ""
|
1113
|
-
index_name = options[:name] || index_name
|
1114
|
-
tablespace = options[:tablespace] ? " TABLESPACE #{options[:tablespace]}" : ""
|
1115
|
-
else
|
1116
|
-
index_type = options
|
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
|
1127
|
-
quoted_column_names = column_names.map { |e| quote_column_name(e) }.join(", ")
|
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
|
1132
|
-
end
|
1133
|
-
|
1134
|
-
# Remove the given index from the table.
|
1135
|
-
# Gives warning if index does not exist
|
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
|
1149
|
-
self.all_schema_indexes = nil
|
1150
|
-
end
|
1151
|
-
|
1152
|
-
# returned shortened index name if default is too large
|
1153
|
-
def index_name(table_name, options) #:nodoc:
|
1154
|
-
default_name = super(table_name, options)
|
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
|
1159
|
-
|
1160
|
-
# remove 'index', 'on' and 'and' keywords
|
1161
|
-
shortened_name = "i_#{table_name}_#{Array(options[:column]) * '_'}"
|
1162
|
-
|
1163
|
-
# leave just first three letters from each word
|
1164
|
-
if shortened_name.length > identifier_max_length
|
1165
|
-
shortened_name = shortened_name.split('_').map{|w| w[0,3]}.join('_')
|
1166
|
-
end
|
1167
|
-
# generate unique name using hash function
|
1168
|
-
if shortened_name.length > identifier_max_length
|
1169
|
-
shortened_name = 'i'+Digest::SHA1.hexdigest(default_name)[0,identifier_max_length-1]
|
1170
|
-
end
|
1171
|
-
@logger.warn "#{adapter_name} shortened default index name #{default_name} to #{shortened_name}" if @logger
|
1172
|
-
shortened_name
|
1173
|
-
end
|
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
|
-
|
1188
|
-
def add_column(table_name, column_name, type, options = {}) #:nodoc:
|
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])}"
|
1190
|
-
options[:type] = type
|
1191
|
-
add_column_options!(add_column_sql, options)
|
1192
|
-
execute(add_column_sql)
|
1193
|
-
ensure
|
1194
|
-
clear_table_columns_cache(table_name)
|
1195
|
-
end
|
1196
|
-
|
1197
|
-
def change_column_default(table_name, column_name, default) #:nodoc:
|
1198
|
-
execute "ALTER TABLE #{quote_table_name(table_name)} MODIFY #{quote_column_name(column_name)} DEFAULT #{quote(default)}"
|
1199
|
-
ensure
|
1200
|
-
clear_table_columns_cache(table_name)
|
1201
|
-
end
|
1202
|
-
|
1203
|
-
def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
|
1204
|
-
column = column_for(table_name, column_name)
|
1205
|
-
|
1206
|
-
unless null || default.nil?
|
1207
|
-
execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
|
1208
|
-
end
|
1209
|
-
|
1210
|
-
change_column table_name, column_name, column.sql_type, :null => null
|
1211
|
-
end
|
1212
|
-
|
1213
|
-
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
1214
|
-
column = column_for(table_name, column_name)
|
1215
|
-
|
1216
|
-
# remove :null option if its value is the same as current column definition
|
1217
|
-
# otherwise Oracle will raise error
|
1218
|
-
if options.has_key?(:null) && options[:null] == column.null
|
1219
|
-
options[:null] = nil
|
1220
|
-
end
|
1221
|
-
|
1222
|
-
change_column_sql = "ALTER TABLE #{quote_table_name(table_name)} MODIFY #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
|
1223
|
-
options[:type] = type
|
1224
|
-
add_column_options!(change_column_sql, options)
|
1225
|
-
execute(change_column_sql)
|
1226
|
-
ensure
|
1227
|
-
clear_table_columns_cache(table_name)
|
1228
|
-
end
|
1229
|
-
|
1230
|
-
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
1231
|
-
execute "ALTER TABLE #{quote_table_name(table_name)} RENAME COLUMN #{quote_column_name(column_name)} to #{quote_column_name(new_column_name)}"
|
1232
|
-
ensure
|
1233
|
-
clear_table_columns_cache(table_name)
|
1234
|
-
end
|
1235
|
-
|
1236
|
-
def remove_column(table_name, column_name) #:nodoc:
|
1237
|
-
execute "ALTER TABLE #{quote_table_name(table_name)} DROP COLUMN #{quote_column_name(column_name)}"
|
1238
|
-
ensure
|
1239
|
-
clear_table_columns_cache(table_name)
|
1240
|
-
end
|
1241
|
-
|
1242
|
-
def add_comment(table_name, column_name, comment) #:nodoc:
|
1243
|
-
return if comment.blank?
|
1244
|
-
execute "COMMENT ON COLUMN #{quote_table_name(table_name)}.#{column_name} IS '#{comment}'"
|
1245
|
-
end
|
1246
|
-
|
1247
|
-
def add_table_comment(table_name, comment) #:nodoc:
|
1248
|
-
return if comment.blank?
|
1249
|
-
execute "COMMENT ON TABLE #{quote_table_name(table_name)} IS '#{comment}'"
|
1250
|
-
end
|
1251
|
-
|
1252
|
-
def table_comment(table_name) #:nodoc:
|
1253
|
-
(owner, table_name, db_link) = @connection.describe(table_name)
|
1254
|
-
select_value <<-SQL
|
1255
|
-
SELECT comments FROM all_tab_comments#{db_link}
|
1256
|
-
WHERE owner = '#{owner}'
|
1257
|
-
AND table_name = '#{table_name}'
|
1258
|
-
SQL
|
1259
|
-
end
|
1260
|
-
|
1261
|
-
def column_comment(table_name, column_name) #:nodoc:
|
1262
|
-
(owner, table_name, db_link) = @connection.describe(table_name)
|
1263
|
-
select_value <<-SQL
|
1264
|
-
SELECT comments FROM all_col_comments#{db_link}
|
1265
|
-
WHERE owner = '#{owner}'
|
1266
|
-
AND table_name = '#{table_name}'
|
1267
|
-
AND column_name = '#{column_name.upcase}'
|
1268
|
-
SQL
|
1269
|
-
end
|
1270
|
-
|
1271
|
-
# Maps logical Rails types to Oracle-specific data types.
|
1272
|
-
def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
|
1273
|
-
# Ignore options for :text and :binary columns
|
1274
|
-
return super(type, nil, nil, nil) if ['text', 'binary'].include?(type.to_s)
|
1275
|
-
|
1276
|
-
super
|
1277
|
-
end
|
1278
|
-
|
1279
811
|
# Find a table's primary key and sequence.
|
1280
812
|
# *Note*: Only primary key is implemented - sequence will be nil.
|
1281
813
|
def pk_and_sequence_for(table_name, owner=nil, desc_table_name=nil, db_link=nil) #:nodoc:
|
@@ -1315,241 +847,6 @@ module ActiveRecord
|
|
1315
847
|
!pk_and_sequence_for(table_name, owner, desc_table_name, db_link).nil?
|
1316
848
|
end
|
1317
849
|
|
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
|
-
|
1321
|
-
def structure_dump #:nodoc:
|
1322
|
-
structure = select_values("select sequence_name from user_sequences order by 1").map do |seq|
|
1323
|
-
"CREATE SEQUENCE \"#{seq}\""
|
1324
|
-
end
|
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|
|
1329
|
-
virtual_columns = virtual_columns_for(table_name)
|
1330
|
-
ddl = "CREATE#{ ' GLOBAL TEMPORARY' if temporary_table?(table_name)} TABLE \"#{table_name}\" (\n"
|
1331
|
-
cols = select_all(%Q{
|
1332
|
-
select column_name, data_type, data_length, char_used, char_length, data_precision, data_scale, data_default, nullable
|
1333
|
-
from user_tab_columns
|
1334
|
-
where table_name = '#{table_name}'
|
1335
|
-
order by column_id
|
1336
|
-
}).map do |row|
|
1337
|
-
if(v = virtual_columns.find {|col| col['column_name'] == row['column_name']})
|
1338
|
-
structure_dump_virtual_column(row, v['data_default'])
|
1339
|
-
else
|
1340
|
-
structure_dump_column(row)
|
1341
|
-
end
|
1342
|
-
end
|
1343
|
-
ddl << cols.join(",\n ")
|
1344
|
-
ddl << structure_dump_constraints(table_name)
|
1345
|
-
ddl << "\n)"
|
1346
|
-
structure << ddl
|
1347
|
-
structure << structure_dump_indexes(table_name)
|
1348
|
-
end
|
1349
|
-
|
1350
|
-
join_with_statement_token(structure) << structure_dump_fk_constraints
|
1351
|
-
end
|
1352
|
-
|
1353
|
-
def structure_dump_column(column) #:nodoc:
|
1354
|
-
col = "\"#{column['column_name']}\" #{column['data_type']}"
|
1355
|
-
if column['data_type'] =='NUMBER' and !column['data_precision'].nil?
|
1356
|
-
col << "(#{column['data_precision'].to_i}"
|
1357
|
-
col << ",#{column['data_scale'].to_i}" if !column['data_scale'].nil?
|
1358
|
-
col << ')'
|
1359
|
-
elsif column['data_type'].include?('CHAR')
|
1360
|
-
length = column['char_used'] == 'C' ? column['char_length'].to_i : column['data_length'].to_i
|
1361
|
-
col << "(#{length})"
|
1362
|
-
end
|
1363
|
-
col << " DEFAULT #{column['data_default']}" if !column['data_default'].nil?
|
1364
|
-
col << ' NOT NULL' if column['nullable'] == 'N'
|
1365
|
-
col
|
1366
|
-
end
|
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']}"
|
1371
|
-
if column['data_type'] =='NUMBER' and !column['data_precision'].nil?
|
1372
|
-
col << "(#{column['data_precision'].to_i}"
|
1373
|
-
col << ",#{column['data_scale'].to_i}" if !column['data_scale'].nil?
|
1374
|
-
col << ')'
|
1375
|
-
elsif column['data_type'].include?('CHAR')
|
1376
|
-
length = column['char_used'] == 'C' ? column['char_length'].to_i : column['data_length'].to_i
|
1377
|
-
col << "(#{length})"
|
1378
|
-
end
|
1379
|
-
col << " GENERATED ALWAYS AS (#{data_default}) VIRTUAL"
|
1380
|
-
end
|
1381
|
-
|
1382
|
-
def structure_dump_constraints(table) #:nodoc:
|
1383
|
-
out = [structure_dump_primary_key(table), structure_dump_unique_keys(table)].flatten.compact
|
1384
|
-
out.length > 0 ? ",\n#{out.join(",\n")}" : ''
|
1385
|
-
end
|
1386
|
-
|
1387
|
-
def structure_dump_primary_key(table) #:nodoc:
|
1388
|
-
opts = {:name => '', :cols => []}
|
1389
|
-
pks = select_all(<<-SQL, "Primary Keys")
|
1390
|
-
select a.constraint_name, a.column_name, a.position
|
1391
|
-
from user_cons_columns a
|
1392
|
-
join user_constraints c
|
1393
|
-
on a.constraint_name = c.constraint_name
|
1394
|
-
where c.table_name = '#{table.upcase}'
|
1395
|
-
and c.constraint_type = 'P'
|
1396
|
-
and c.owner = sys_context('userenv', 'session_user')
|
1397
|
-
SQL
|
1398
|
-
pks.each do |row|
|
1399
|
-
opts[:name] = row['constraint_name']
|
1400
|
-
opts[:cols][row['position']-1] = row['column_name']
|
1401
|
-
end
|
1402
|
-
opts[:cols].length > 0 ? " CONSTRAINT #{opts[:name]} PRIMARY KEY (#{opts[:cols].join(',')})" : nil
|
1403
|
-
end
|
1404
|
-
|
1405
|
-
def structure_dump_unique_keys(table) #:nodoc:
|
1406
|
-
keys = {}
|
1407
|
-
uks = select_all(<<-SQL, "Primary Keys")
|
1408
|
-
select a.constraint_name, a.column_name, a.position
|
1409
|
-
from user_cons_columns a
|
1410
|
-
join user_constraints c
|
1411
|
-
on a.constraint_name = c.constraint_name
|
1412
|
-
where c.table_name = '#{table.upcase}'
|
1413
|
-
and c.constraint_type = 'U'
|
1414
|
-
and c.owner = sys_context('userenv', 'session_user')
|
1415
|
-
SQL
|
1416
|
-
uks.each do |uk|
|
1417
|
-
keys[uk['constraint_name']] ||= []
|
1418
|
-
keys[uk['constraint_name']][uk['position']-1] = uk['column_name']
|
1419
|
-
end
|
1420
|
-
keys.map do |k,v|
|
1421
|
-
" CONSTRAINT #{k} UNIQUE (#{v.join(',')})"
|
1422
|
-
end
|
1423
|
-
end
|
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
|
-
|
1441
|
-
def structure_dump_fk_constraints #:nodoc:
|
1442
|
-
fks = select_all("select table_name from all_tables where owner = sys_context('userenv','session_user') order by 1").map do |table|
|
1443
|
-
if respond_to?(:foreign_keys) && (foreign_keys = foreign_keys(table["table_name"])).any?
|
1444
|
-
foreign_keys.map do |fk|
|
1445
|
-
column = fk.options[:column] || "#{fk.to_table.to_s.singularize}_id"
|
1446
|
-
constraint_name = foreign_key_constraint_name(fk.from_table, column, fk.options)
|
1447
|
-
sql = "ALTER TABLE #{quote_table_name(fk.from_table)} ADD CONSTRAINT #{quote_column_name(constraint_name)} "
|
1448
|
-
sql << "#{foreign_key_definition(fk.to_table, fk.options)}"
|
1449
|
-
end
|
1450
|
-
end
|
1451
|
-
end.flatten.compact
|
1452
|
-
join_with_statement_token(fks)
|
1453
|
-
end
|
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
|
-
|
1461
|
-
# Extract all stored procedures, packages, synonyms and views.
|
1462
|
-
def structure_dump_db_stored_code #:nodoc:
|
1463
|
-
structure = []
|
1464
|
-
select_all("select distinct name, type
|
1465
|
-
from all_source
|
1466
|
-
where type in ('PROCEDURE', 'PACKAGE', 'PACKAGE BODY', 'FUNCTION', 'TRIGGER', 'TYPE')
|
1467
|
-
and owner = sys_context('userenv','session_user') order by type").each do |source|
|
1468
|
-
ddl = "CREATE OR REPLACE \n"
|
1469
|
-
lines = select_all(%Q{
|
1470
|
-
select text
|
1471
|
-
from all_source
|
1472
|
-
where name = '#{source['name']}'
|
1473
|
-
and type = '#{source['type']}'
|
1474
|
-
and owner = sys_context('userenv','session_user')
|
1475
|
-
order by line
|
1476
|
-
}).map do |row|
|
1477
|
-
ddl << row['text']
|
1478
|
-
end
|
1479
|
-
ddl << ";" unless ddl.strip[-1,1] == ";"
|
1480
|
-
structure << ddl
|
1481
|
-
end
|
1482
|
-
|
1483
|
-
# export views
|
1484
|
-
select_all("select view_name, text from user_views").each do |view|
|
1485
|
-
structure << "CREATE OR REPLACE VIEW #{view['view_name']} AS\n #{view['text']}"
|
1486
|
-
end
|
1487
|
-
|
1488
|
-
# export synonyms
|
1489
|
-
select_all("select owner, synonym_name, table_name, table_owner
|
1490
|
-
from all_synonyms
|
1491
|
-
where owner = sys_context('userenv','session_user') ").each do |synonym|
|
1492
|
-
structure << "CREATE OR REPLACE #{synonym['owner'] == 'PUBLIC' ? 'PUBLIC' : '' } SYNONYM #{synonym['synonym_name']}"
|
1493
|
-
structure << " FOR #{synonym['table_owner']}.#{synonym['table_name']}"
|
1494
|
-
end
|
1495
|
-
|
1496
|
-
join_with_statement_token(structure)
|
1497
|
-
end
|
1498
|
-
|
1499
|
-
def structure_drop #:nodoc:
|
1500
|
-
statements = select_values("select sequence_name from user_sequences order by 1").map do |seq|
|
1501
|
-
"DROP SEQUENCE \"#{seq}\""
|
1502
|
-
end
|
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"
|
1508
|
-
end
|
1509
|
-
join_with_statement_token(statements)
|
1510
|
-
end
|
1511
|
-
|
1512
|
-
def temp_table_drop #:nodoc:
|
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)
|
1518
|
-
end
|
1519
|
-
|
1520
|
-
def full_drop(preserve_tables=false) #:nodoc:
|
1521
|
-
s = preserve_tables ? [] : [structure_drop]
|
1522
|
-
s << temp_table_drop if preserve_tables
|
1523
|
-
s << drop_sql_for_feature("view")
|
1524
|
-
s << drop_sql_for_feature("materialized view")
|
1525
|
-
s << drop_sql_for_feature("synonym")
|
1526
|
-
s << drop_sql_for_feature("type")
|
1527
|
-
s << drop_sql_for_object("package")
|
1528
|
-
s << drop_sql_for_object("function")
|
1529
|
-
s << drop_sql_for_object("procedure")
|
1530
|
-
s.join
|
1531
|
-
end
|
1532
|
-
|
1533
|
-
def add_column_options!(sql, options) #:nodoc:
|
1534
|
-
type = options[:type] || ((column = options[:column]) && column.type)
|
1535
|
-
type = type && type.to_sym
|
1536
|
-
# handle case of defaults for CLOB columns, which would otherwise get "quoted" incorrectly
|
1537
|
-
if options_include_default?(options)
|
1538
|
-
if type == :text
|
1539
|
-
sql << " DEFAULT #{quote(options[:default])}"
|
1540
|
-
else
|
1541
|
-
# from abstract adapter
|
1542
|
-
sql << " DEFAULT #{quote(options[:default], options[:column])}"
|
1543
|
-
end
|
1544
|
-
end
|
1545
|
-
# must explicitly add NULL or NOT NULL to allow change_column to work on migrations
|
1546
|
-
if options[:null] == false
|
1547
|
-
sql << " NOT NULL"
|
1548
|
-
elsif options[:null] == true
|
1549
|
-
sql << " NULL" unless type == :primary_key
|
1550
|
-
end
|
1551
|
-
end
|
1552
|
-
|
1553
850
|
# SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
|
1554
851
|
#
|
1555
852
|
# Oracle requires the ORDER BY columns to be in the SELECT list for DISTINCT
|
@@ -1615,85 +912,10 @@ module ActiveRecord
|
|
1615
912
|
@connection.oracle_downcase(column_name)
|
1616
913
|
end
|
1617
914
|
|
1618
|
-
def column_for(table_name, column_name)
|
1619
|
-
unless column = columns(table_name).find { |c| c.name == column_name.to_s }
|
1620
|
-
raise "No such column: #{table_name}.#{column_name}"
|
1621
|
-
end
|
1622
|
-
column
|
1623
|
-
end
|
1624
|
-
|
1625
|
-
def create_sequence_and_trigger(table_name, options)
|
1626
|
-
seq_name = options[:sequence_name] || default_sequence_name(table_name)
|
1627
|
-
seq_start_value = options[:sequence_start_value] || default_sequence_start_value
|
1628
|
-
execute "CREATE SEQUENCE #{quote_table_name(seq_name)} START WITH #{seq_start_value}"
|
1629
|
-
|
1630
|
-
create_primary_key_trigger(table_name, options) if options[:primary_key_trigger]
|
1631
|
-
end
|
1632
|
-
|
1633
|
-
def create_primary_key_trigger(table_name, options)
|
1634
|
-
seq_name = options[:sequence_name] || default_sequence_name(table_name)
|
1635
|
-
trigger_name = options[:trigger_name] || default_trigger_name(table_name)
|
1636
|
-
primary_key = options[:primary_key] || Base.get_primary_key(table_name.to_s.singularize)
|
1637
|
-
execute compress_lines(<<-SQL)
|
1638
|
-
CREATE OR REPLACE TRIGGER #{quote_table_name(trigger_name)}
|
1639
|
-
BEFORE INSERT ON #{quote_table_name(table_name)} FOR EACH ROW
|
1640
|
-
BEGIN
|
1641
|
-
IF inserting THEN
|
1642
|
-
IF :new.#{quote_column_name(primary_key)} IS NULL THEN
|
1643
|
-
SELECT #{quote_table_name(seq_name)}.NEXTVAL INTO :new.#{quote_column_name(primary_key)} FROM dual;
|
1644
|
-
END IF;
|
1645
|
-
END IF;
|
1646
|
-
END;
|
1647
|
-
SQL
|
1648
|
-
end
|
1649
|
-
|
1650
|
-
def default_trigger_name(table_name)
|
1651
|
-
# truncate table name if necessary to fit in max length of identifier
|
1652
|
-
"#{table_name.to_s[0,IDENTIFIER_MAX_LENGTH-4]}_pkt"
|
1653
|
-
end
|
1654
|
-
|
1655
915
|
def compress_lines(string, join_with = "\n")
|
1656
916
|
string.split($/).map { |line| line.strip }.join(join_with)
|
1657
917
|
end
|
1658
918
|
|
1659
|
-
# virtual columns are an 11g feature. This returns [] if feature is not
|
1660
|
-
# present or none are found.
|
1661
|
-
# return [{'column_name' => 'FOOS', 'data_default' => '...'}, ...]
|
1662
|
-
def virtual_columns_for(table)
|
1663
|
-
begin
|
1664
|
-
select_all <<-SQL
|
1665
|
-
select column_name, data_default
|
1666
|
-
from user_tab_cols
|
1667
|
-
where virtual_column='YES'
|
1668
|
-
and table_name='#{table.upcase}'
|
1669
|
-
SQL
|
1670
|
-
# feature not supported previous to 11g
|
1671
|
-
rescue ActiveRecord::StatementInvalid => e
|
1672
|
-
[]
|
1673
|
-
end
|
1674
|
-
end
|
1675
|
-
|
1676
|
-
def drop_sql_for_feature(type)
|
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)
|
1682
|
-
end
|
1683
|
-
|
1684
|
-
def drop_sql_for_object(type)
|
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)
|
1689
|
-
end
|
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
|
-
|
1697
919
|
public
|
1698
920
|
# DBMS_OUTPUT =============================================
|
1699
921
|
#
|
@@ -1763,6 +985,16 @@ if defined?(CGI::Session::ActiveRecordStore::Session)
|
|
1763
985
|
end
|
1764
986
|
end
|
1765
987
|
|
988
|
+
# Implementation of standard schema definition statements and extensions for schema definition
|
989
|
+
require 'active_record/connection_adapters/oracle_enhanced_schema_statements'
|
990
|
+
require 'active_record/connection_adapters/oracle_enhanced_schema_statements_ext'
|
991
|
+
|
992
|
+
# Extensions for schema definition
|
993
|
+
require 'active_record/connection_adapters/oracle_enhanced_schema_definitions'
|
994
|
+
|
995
|
+
# Extensions for context index definition
|
996
|
+
require 'active_record/connection_adapters/oracle_enhanced_context_index'
|
997
|
+
|
1766
998
|
# Load custom create, update, delete methods functionality
|
1767
999
|
require 'active_record/connection_adapters/oracle_enhanced_procedures'
|
1768
1000
|
|
@@ -1776,19 +1008,13 @@ require 'active_record/connection_adapters/oracle_enhanced_dirty'
|
|
1776
1008
|
begin
|
1777
1009
|
require 'active_record/connection_adapters/oracle_enhanced_tasks'
|
1778
1010
|
rescue LoadError
|
1779
|
-
end if defined?(Rails
|
1011
|
+
end if defined?(Rails) || defined?(RAILS_ROOT)
|
1780
1012
|
|
1781
1013
|
# Patches and enhancements for schema dumper
|
1782
1014
|
require 'active_record/connection_adapters/oracle_enhanced_schema_dumper'
|
1783
1015
|
|
1784
|
-
#
|
1785
|
-
require 'active_record/connection_adapters/
|
1786
|
-
|
1787
|
-
# Extensions for schema definition
|
1788
|
-
require 'active_record/connection_adapters/oracle_enhanced_schema_definitions'
|
1789
|
-
|
1790
|
-
# Extensions for context index definition
|
1791
|
-
require 'active_record/connection_adapters/oracle_enhanced_context_index'
|
1016
|
+
# Implementation of structure dump
|
1017
|
+
require 'active_record/connection_adapters/oracle_enhanced_structure_dump'
|
1792
1018
|
|
1793
1019
|
# Add BigDecimal#to_d, Fixnum#to_d and Bignum#to_d methods if not already present
|
1794
1020
|
require 'active_record/connection_adapters/oracle_enhanced_core_ext'
|