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.
Files changed (32) hide show
  1. data/.gitignore +3 -1
  2. data/Gemfile +37 -0
  3. data/History.txt +17 -0
  4. data/README.rdoc +9 -4
  5. data/RUNNING_TESTS.rdoc +28 -0
  6. data/VERSION +1 -1
  7. data/activerecord-oracle_enhanced-adapter.gemspec +13 -6
  8. data/lib/active_record/connection_adapters/oracle_enhanced.rake +32 -24
  9. data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +19 -793
  10. data/lib/active_record/connection_adapters/oracle_enhanced_base_ext.rb +79 -0
  11. data/lib/active_record/connection_adapters/oracle_enhanced_column.rb +106 -0
  12. data/lib/active_record/connection_adapters/oracle_enhanced_context_index.rb +32 -2
  13. data/lib/active_record/connection_adapters/oracle_enhanced_core_ext.rb +3 -44
  14. data/lib/active_record/connection_adapters/oracle_enhanced_dirty.rb +2 -2
  15. data/lib/active_record/connection_adapters/oracle_enhanced_jdbc_connection.rb +4 -2
  16. data/lib/active_record/connection_adapters/oracle_enhanced_oci_connection.rb +19 -3
  17. data/lib/active_record/connection_adapters/oracle_enhanced_schema_dumper.rb +14 -6
  18. data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements.rb +331 -0
  19. data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements_ext.rb +46 -14
  20. data/lib/active_record/connection_adapters/oracle_enhanced_structure_dump.rb +290 -0
  21. data/lib/active_record/connection_adapters/oracle_enhanced_tasks.rb +15 -10
  22. data/lib/activerecord-oracle_enhanced-adapter.rb +25 -0
  23. data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +51 -19
  24. data/spec/active_record/connection_adapters/oracle_enhanced_connection_spec.rb +1 -1
  25. data/spec/active_record/connection_adapters/oracle_enhanced_context_index_spec.rb +31 -2
  26. data/spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb +152 -11
  27. data/spec/active_record/connection_adapters/oracle_enhanced_dirty_spec.rb +27 -0
  28. data/spec/active_record/connection_adapters/oracle_enhanced_schema_dump_spec.rb +19 -1
  29. data/spec/active_record/connection_adapters/{oracle_enhanced_schema_spec.rb → oracle_enhanced_schema_statements_spec.rb} +44 -1
  30. data/spec/active_record/connection_adapters/{oracle_enhanced_adapter_structure_dumper_spec.rb → oracle_enhanced_structure_dump_spec.rb} +49 -1
  31. data/spec/spec_helper.rb +60 -53
  32. metadata +15 -8
data/.gitignore CHANGED
@@ -1,3 +1,5 @@
1
+ .bundle
2
+ .rvmrc
1
3
  .svn
2
4
  .DS_Store
3
5
  coverage
@@ -6,4 +8,4 @@ pkg
6
8
  log
7
9
  tmp
8
10
  sqlnet.log
9
-
11
+ Gemfile.lock
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 oracle-enahnced can be found at http://blog.rayapps.com/category/oracle-enhanced
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.1 or 1.9.2 - requires ruby-oci8 2.0 library to connect to Oracle
22
- unicode_utils gem is recommended for Unicode aware string upcase and downcase
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
 
@@ -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.0
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.0"
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-06-21}
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/oracle_enhanced_schema_spec.rb",
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/oracle_enhanced_schema_spec.rb",
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
- # RSI: implementation idea taken from JDBC adapter
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, &block)
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
- ActiveRecord::Base.establish_connection(abcs[rails_env])
20
- File.open("db/#{rails_env}_structure.sql", "w+") { |f| f << ActiveRecord::Base.connection.structure_dump }
21
- if ActiveRecord::Base.connection.supports_migrations?
22
- File.open("db/#{rails_env}_structure.sql", "a") { |f| f << ActiveRecord::Base.connection.dump_schema_information }
23
- end
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 }
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
- ActiveRecord::Base.establish_connection(:test)
36
- File.read("db/#{rails_env}_structure.sql").
37
- split(ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter::STATEMENT_TOKEN).each do |ddl|
38
- ddl.chop! if ddl.last == ";"
39
- ActiveRecord::Base.connection.execute(ddl) unless ddl.blank?
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
- ActiveRecord::Base.establish_connection(:test)
46
- ActiveRecord::Base.connection.full_drop.
47
- split(ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter::STATEMENT_TOKEN).each do |ddl|
48
- ddl.chop! if ddl.last == ";"
49
- ActiveRecord::Base.connection.execute(ddl) unless ddl.blank?
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'] << "(#{(limit || 38).to_i}" + ((scale = scale.to_i) > 0 ? ",#{scale})" : ")")
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.root) || defined?(RAILS_ROOT)
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
- # Extensions for schema definition statements
1785
- require 'active_record/connection_adapters/oracle_enhanced_schema_statements_ext'
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'