pmacs-activerecord-oracle_enhanced-adapter 1.4.2.rc1 → 1.5.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +11 -40
  3. data/History.md +170 -0
  4. data/README.md +61 -5
  5. data/Rakefile +1 -0
  6. data/VERSION +1 -1
  7. data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +330 -161
  8. data/lib/active_record/connection_adapters/oracle_enhanced_column.rb +48 -8
  9. data/lib/active_record/connection_adapters/oracle_enhanced_column_dumper.rb +77 -0
  10. data/lib/active_record/connection_adapters/oracle_enhanced_connection.rb +8 -24
  11. data/lib/active_record/connection_adapters/oracle_enhanced_context_index.rb +4 -13
  12. data/lib/active_record/connection_adapters/oracle_enhanced_database_tasks.rb +61 -0
  13. data/lib/active_record/connection_adapters/oracle_enhanced_dirty.rb +13 -12
  14. data/lib/active_record/connection_adapters/oracle_enhanced_jdbc_connection.rb +42 -19
  15. data/lib/active_record/connection_adapters/oracle_enhanced_oci_connection.rb +28 -74
  16. data/lib/active_record/connection_adapters/oracle_enhanced_procedures.rb +165 -231
  17. data/lib/active_record/connection_adapters/oracle_enhanced_schema_creation.rb +89 -0
  18. data/lib/active_record/connection_adapters/oracle_enhanced_schema_definitions.rb +16 -24
  19. data/lib/active_record/connection_adapters/oracle_enhanced_schema_dumper.rb +29 -38
  20. data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements.rb +93 -42
  21. data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements_ext.rb +5 -3
  22. data/lib/active_record/connection_adapters/oracle_enhanced_structure_dump.rb +7 -7
  23. data/lib/active_record/connection_adapters/oracle_enhanced_version.rb +1 -1
  24. data/lib/pmacs-activerecord-oracle_enhanced-adapter.rb +2 -2
  25. data/pmacs-activerecord-oracle_enhanced-adapter.gemspec +19 -17
  26. data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +35 -99
  27. data/spec/active_record/connection_adapters/oracle_enhanced_connection_spec.rb +17 -3
  28. data/spec/active_record/connection_adapters/oracle_enhanced_context_index_spec.rb +105 -98
  29. data/spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb +74 -44
  30. data/spec/active_record/connection_adapters/oracle_enhanced_database_tasks_spec.rb +89 -0
  31. data/spec/active_record/connection_adapters/oracle_enhanced_dbms_output_spec.rb +3 -3
  32. data/spec/active_record/connection_adapters/oracle_enhanced_dirty_spec.rb +13 -2
  33. data/spec/active_record/connection_adapters/oracle_enhanced_procedures_spec.rb +11 -12
  34. data/spec/active_record/connection_adapters/oracle_enhanced_schema_dump_spec.rb +252 -60
  35. data/spec/active_record/connection_adapters/oracle_enhanced_schema_statements_spec.rb +170 -40
  36. data/spec/active_record/connection_adapters/oracle_enhanced_structure_dump_spec.rb +14 -8
  37. data/spec/spec_helper.rb +25 -54
  38. metadata +41 -72
  39. data/lib/active_record/connection_adapters/oracle_enhanced.rake +0 -105
  40. data/lib/active_record/connection_adapters/oracle_enhanced_activerecord_patches.rb +0 -41
  41. data/lib/active_record/connection_adapters/oracle_enhanced_base_ext.rb +0 -118
  42. data/lib/active_record/connection_adapters/oracle_enhanced_core_ext.rb +0 -25
  43. data/lib/active_record/connection_adapters/oracle_enhanced_tasks.rb +0 -17
  44. data/spec/active_record/connection_adapters/oracle_enhanced_core_ext_spec.rb +0 -19
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 55fb63832be212bdf12fc8a8e9ef24b385d2dd71
4
+ data.tar.gz: bd81a44bc261832f358a0eb1ef297911f73110a1
5
+ SHA512:
6
+ metadata.gz: 8122b64d689f4abf8143f73fa141da3f7f461b3d31f56845f3d1a5916bc9c1db7b0d7f6a9b0a3aedc0af4deea95f1d08bf81b8814da356d37810f03438608ce1
7
+ data.tar.gz: 79beb44492fd15f7e122e91623fcb09c6bfef9b33038a92ef9599dd9b2b4783db375d1b2b0d6d11868ad64b48a1de35e3bd6d11133546a747c0a41a3de80d763
data/Gemfile CHANGED
@@ -1,52 +1,23 @@
1
1
  source 'http://rubygems.org'
2
2
 
3
3
  group :development do
4
- gem 'jeweler', '~> 1.5.1'
4
+ gem 'jeweler', '~> 1.8'
5
5
  gem 'rspec', '~> 2.4'
6
6
  gem 'rdoc'
7
7
 
8
- if ENV['RAILS_GEM_VERSION']
9
- gem 'activerecord', "=#{ENV['RAILS_GEM_VERSION']}"
10
- gem 'actionpack', "=#{ENV['RAILS_GEM_VERSION']}"
11
- gem 'activesupport', "=#{ENV['RAILS_GEM_VERSION']}"
12
- case ENV['RAILS_GEM_VERSION']
13
- when /^2.0/
14
- gem 'composite_primary_keys', '=0.9.93'
15
- when /^2.1/
16
- gem 'composite_primary_keys', '=1.0.8'
17
- when /^2.2/
18
- gem 'composite_primary_keys', '=2.2.2'
19
- when /^2.3.3/
20
- gem 'composite_primary_keys', '=2.3.2'
21
- when /^3/
22
- gem 'railties', "=#{ENV['RAILS_GEM_VERSION']}"
23
- end
24
- else
25
- %w(activerecord activemodel activesupport actionpack railties).each do |gem_name|
26
- if ENV['RAILS_GEM_PATH']
27
- gem gem_name, :path => File.join(ENV['RAILS_GEM_PATH'], gem_name)
28
- else
29
- gem gem_name, :git => "git://github.com/rails/rails"
30
- end
31
- end
8
+ gem 'activerecord', github: 'rails/rails'
9
+ gem 'activemodel', github: 'rails/rails'
10
+ gem 'activesupport', github: 'rails/rails'
11
+ gem 'actionpack', github: 'rails/rails'
12
+ gem 'railties', github: 'rails/rails'
32
13
 
33
- if ENV['AREL_GEM_PATH']
34
- gem 'arel', :path => ENV['AREL_GEM_PATH']
35
- else
36
- gem 'arel', :git => "git://github.com/rails/arel"
37
- end
14
+ gem 'arel', github: 'rails/arel'
15
+ gem 'journey', github: 'rails/journey'
38
16
 
39
- if ENV['JOURNEY_GEM_PATH']
40
- gem 'journey', :path => ENV['JOURNEY_GEM_PATH']
41
- else
42
- gem "journey", :git => "git://github.com/rails/journey"
43
- end
44
- end
45
-
46
- gem 'ruby-plsql', '>=0.4.4'
17
+ gem 'activerecord-deprecated_finders'
18
+ gem 'ruby-plsql', '>=0.5.0'
47
19
 
48
20
  platforms :ruby do
49
- gem 'ruby-oci8', '>=2.0.4'
21
+ gem 'ruby-oci8', github: 'kubo/ruby-oci8'
50
22
  end
51
-
52
23
  end
data/History.md CHANGED
@@ -1,3 +1,173 @@
1
+ ## 1.5.5 / 2014-05-23
2
+
3
+ * Enhancements
4
+ * Oracle NUMBER datatype can be handled as Rails :float datatype [#418]
5
+ - Default NUMBER datatype handled as :decimal to keep compatibility
6
+ - Configured by setting `self.number_datatype_coercion = :float`
7
+ * Add link to supported Oracle database version, JDK and Oracle JDBC Driver version [#438]
8
+ * Support `without_prepared_statements?` to handle `unprepared_statement` [#447]
9
+
10
+ * Bug Fix
11
+ * Associations with name `record` do not work correctly since Rails 4 [#435]
12
+ * Skip another Oracle Text test when Oracle 12c used [#437]
13
+ * Tag bind params with a bind param object [#444]
14
+
15
+ ## 1.5.4 / 2014-03-25
16
+
17
+ * Enhancements
18
+ * Support Rails 4.1.0.rc2
19
+ * Allow Java 8 to run with jruby [#383]
20
+
21
+ * Bug Fix
22
+ * Fix db:schema:dump when foreign key column name is not 'id' [#409]
23
+ * Fix schema dump works when non Oracle adapter used [#428]
24
+
25
+ ## 1.5.3 / 2014-03-04
26
+
27
+ * Enhancements
28
+ * Supports Rails 4.1.0.rc1
29
+ * Support rails/rails#13886 by chainging select_rows arguments [#415]
30
+
31
+ * Bug Fix
32
+ * Fix ORA-01008: not all variables bound [#422]
33
+
34
+ ## 1.5.2 / 2014-01-24
35
+
36
+ * Enhancements
37
+ * Supports Rails 4.1.0.beta1
38
+ * Support Rails 4 Database Tasks [#404]
39
+ * Create sequence when add primary_key column [#406]
40
+ * Move `SchemaCreation` to its own file [#381]
41
+ * Remove unused OracleEnhancedColumnDefinition [#382]
42
+ * Log bind variables after they were type casted [#385]
43
+ * Remove add_order_by_for_association_limiting! [#388]
44
+ * Support named savepoints [#389]
45
+ * Support self.extract_value_from_default [#395]
46
+ * Remove oracle_enhanced_core_ext.rb [#397]
47
+ * Remove unused to_sql_with_foreign_keys and lob_columns [#398]
48
+ * Remove ruby-oci8 v1 code [#405]
49
+
50
+ * Bug Fix
51
+ * Move add_column_options! into SchemaCreation class [#384]
52
+ * Add options_include_default! [#384]
53
+ * Use OCI8::Metadata::Base#obj_link [#399]
54
+
55
+ ## 1.5.1 / 2013-11-30
56
+
57
+ * Enhancements
58
+ * Removed set_table_name set_primary_key set_sequence_name from unit tests [#364]
59
+ * Update README to support assignment methods [#365]
60
+ * Remove add_limit_offset! method [#369]
61
+ * Update Gemfile to use `bundle config --local` [#370]
62
+ * `describe` does not try super when no datbase link and ORA-4043 returned [#375]
63
+ * Support `remove_columns` [#377]
64
+ * Dump views in alphabetical order and add `FORCE` option [#378]
65
+
66
+ * Bug Fix
67
+ * Fixed reverting add_column fails with v1.5.0 [#373]
68
+
69
+ ## 1.5.0 / 2013-11-01
70
+
71
+ * Enhancements
72
+ * Add license in gemspec and Rakefile [#361]
73
+
74
+ ## 1.5.0.rc1 / 2013-10-31
75
+
76
+ * Update README and HISTORY
77
+ * No other changes since 1.5.0.beta1
78
+
79
+ ## 1.5.0.beta1 / 2013-10-28
80
+
81
+ * Enhancements and major changes
82
+ * Support Rails 4.0
83
+ * Desupport Rails 3.2 and lower version. To support Rails 3.2, use Version 1.4.3
84
+ * Drop session store support [#219]
85
+ * Create indexes automatically for references and belongs_to [#183]
86
+ * Use the index name explicitly provided in a migration when reverting [#296]
87
+ * Rename indexes when a table or column is renamed [#286]
88
+ * Support refactored remove_column [#172]
89
+ * Support allowed_index_name_length method [#285]
90
+ * Remove schema prefix from sequence name if present before truncating [#155]
91
+ * Bumped jeweler, ruby-plsql and ruby-oci8 version [#176]
92
+ * Support also ojdbc6.jar for Java 1.7 [#350]
93
+ * Support "activerecord-deprecated_finders" [#210]
94
+ * Prepared statements can be disabled [#295]
95
+ * Ensure disconnecting or reconnecting resets the transaction state [#220]
96
+ * Support for specifying transaction isolation level [#226]
97
+ * Rename the partial_updates config to partial_writes [#234]
98
+ * Deprecate passing a string as third argument of add_index [#242]
99
+ * Rename update method to update_record, create method to create_record [#273]
100
+ * Deprecate #connection in favour of accessing it via the class [#297]
101
+ * Support SchemaCreation [#298]
102
+ * Add support for foreign key creation in create_table [#317]
103
+ * Add virtual columns support for rail4 branch [#329]
104
+ * Support columns_for_distinct method [#340]
105
+ * Clear index cache when any table dropped [#200]
106
+ * Clear index cache when remove_column executed [#269]
107
+ * Dump schema uses ruby 1.9 style hash [#229]
108
+ * Support _field_changed? and drop field_changed? [#182 #254]
109
+ * Use arel nodes instead of raw sql [#198]
110
+ * Raise an ArgumentError when passing an invalid option to add_index [#242]
111
+ * Split OracleEnhancedColumnDumper from OracleEnhancedSchemaDumper [#292]
112
+ * Unit test sets default_timezone = :local [#184]
113
+ * Support reset_pk_sequence! [#287]
114
+ * Remove unnecessary pendings in unit tests [#358]
115
+
116
+ * Bug Fix
117
+ * Address ArgumentError: wrong number of arguments (5 for 3) [#166]
118
+ * Address NoMethodError: undefined method `column_types' [#173]
119
+ * Schema dumper removes table_name_prefix and table_name_suffix [#191]
120
+ * Add clear_logger to address ArgumentError: wrong number of arguments (1 for 2) [#193]
121
+ * Use Relation#to_a as Relation#all is deprecated in Rails [#203]
122
+ * Address Address test_integer_zero_to_integer_zero_not_marked_as_changed failure [#207]
123
+ * Address NoMethodError undefined method `default_string' [#221]
124
+ * Address you can't redefine the primary key column 'id'. To define a custom primary key, pass { id: false } to create_table [#238]
125
+ * Remove unnecessary DEPRECATION WARNING [#255]
126
+ * Assigning "0.0" to a nullable numeric column does not make it dirty [#293]
127
+ * Address `rake spec` abort [#353]
128
+ * Correct activerecord-deprecated_finders not loaded if ENV['RAILS_GEM_VERSION'] set [#353]
129
+
130
+ * Known Issues
131
+ * Oracle Text features are not fully supported with Oracle 12c [#331]
132
+
133
+ ### 1.4.3 / 2013-10-24
134
+
135
+ * No changes since 1.4.3.rc2
136
+
137
+ ### 1.4.3.rc2 / 2013-10-23
138
+
139
+ * Change build procedures
140
+ * No other changes since 1.4.3.rc1
141
+
142
+ ### 1.4.3.rc1 / 2013-10-19
143
+
144
+ * Enhancements:
145
+ * Allow inserting NULL to Oracle Spatial Data Types such as MDSYS.SDO_GEOMETRY [#311]
146
+ * Support ojdbc7.jar JDBC Driver [#335]
147
+
148
+ * Bug fixes:
149
+ * Fixed Gemfile to bundle update work [#294]
150
+ * Fixed broken links in README.md and RUNNING_TESTS.md [#303 #306]
151
+ * Address rename_table works if the source table created with :id => false [#336]
152
+ * Use expand_path to show VERSION with Windows XP
153
+
154
+ ### 1.4.2 / 2013-03-18
155
+
156
+ * No changes since 1.4.2.rc2
157
+
158
+ ### 1.4.2.rc2 / 2013-03-01
159
+
160
+ * Bug fixes:
161
+ * Do not consider the numeric attribute as changed if the old value is zero and the new value is not a string [#247]
162
+ * Removed table_name_prefix and table_name_suffix when schema dumper executed [#248]
163
+ * Remove_column should raise an ArgumentError when no columns are passed [#246]
164
+ * Don't dump type for NUMBER virtual columns [#256]
165
+ * Address :returning_id column should be of type Column [#274]
166
+ * Migrated versions should be dumped in order [#277]
167
+ * Always write serialized LOB columns [#275]
168
+ * Truncate the schema_migrations index [#276]
169
+ * Split paths on windows machines in the right way [#231]
170
+
1
171
  ### 1.4.2.rc1 / 2012-11-13
2
172
 
3
173
  * Enhancements:
data/README.md CHANGED
@@ -6,11 +6,42 @@ Oracle enhanced adapter for ActiveRecord
6
6
  DESCRIPTION
7
7
  -----------
8
8
 
9
- Oracle enhanced ActiveRecord adapter provides Oracle database access from Ruby on Rails applications. Oracle enhanced adapter can be used from Ruby on Rails versions 2.3.x and 3.x and it is working with Oracle database versions 10g and 11g.
9
+ Oracle enhanced ActiveRecord adapter provides Oracle database access from Ruby on Rails applications. Oracle enhanced adapter can be used from Ruby on Rails versions between 2.3.x and 4.0 and it is working with Oracle database versions from 10g to 12c.
10
10
 
11
11
  INSTALLATION
12
12
  ------------
13
13
 
14
+ ### Rails 4
15
+
16
+ Oracle enhanced adapter version 1.5 just supports Rails 4 and does not support Rails 3.2 or lower version of Rails.
17
+
18
+ When using Ruby on Rails version 4 then in Gemfile include
19
+
20
+ gem "activerecord-oracle_enhanced-adapter", "~> 1.5.0"
21
+
22
+ where instead of 1.5.0 you can specify any other desired version. It is recommended to specify version with `~>` which means that use specified version or later patch versions (in this example any later 1.5.x version but not 1.6.x version). Oracle enhanced adapter maintains API backwards compatibility during patch version upgrades and therefore it is safe to always upgrade to latest patch version.
23
+
24
+ If you would like to use latest adapter version from github then specify
25
+
26
+ gem 'activerecord-oracle_enhanced-adapter', :git => 'git://github.com/rsim/oracle-enhanced.git'
27
+
28
+ If you are using CRuby 1.9.3 or 2.0 then you need to install ruby-oci8 gem as well as Oracle client, e.g. [Oracle Instant Client](http://www.oracle.com/technetwork/database/features/instant-client/index-097480.html). Include in Gemfile also ruby-oci8:
29
+
30
+ gem 'ruby-oci8', '~> 2.1.0'
31
+
32
+ If you are using JRuby then you need to download latest [Oracle JDBC driver](http://www.oracle.com/technetwork/database/enterprise-edition/jdbc-112010-090769.html) - either ojdbc7.jar or ojdbc6.jar for Java 7, ojdbc6.jar for Java 6 or ojdbc5.jar for Java 5. And copy this file to one of these locations:
33
+
34
+ * in `./lib` directory of Rails application
35
+ * in some directory which is in `PATH`
36
+ * in `JRUBY_HOME/lib` directory
37
+ * or include path to JDBC driver jar file in Java `CLASSPATH`
38
+
39
+ After specifying necessary gems in Gemfile run
40
+
41
+ bundle install
42
+
43
+ to install the adapter (or later run `bundle update` to force updating to latest version).
44
+
14
45
  ### Rails 3
15
46
 
16
47
  When using Ruby on Rails version 3 then in Gemfile include
@@ -113,7 +144,9 @@ If you deploy JRuby on Rails application in Java application server that support
113
144
  adapter: oracle_enhanced
114
145
  jndi: "jdbc/jndi_connection_name"
115
146
 
116
- You can find other available database.yml connection parameters in [oracle_enhanced_adapter.rb](/rsim/oracle-enhanced/blob/master/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb). There are many NLS settings as well as some other Oracle session settings.
147
+ To use jndi with Tomcat you need to set the accessToUnderlyingConnectionAllowed to true property on the pool. See the [Tomcat Documentation](http://tomcat.apache.org/tomcat-7.0-doc/jndi-resources-howto.html) for reference.
148
+
149
+ You can find other available database.yml connection parameters in [oracle_enhanced_adapter.rb](http://github.com/rsim/oracle-enhanced/blob/master/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb). There are many NLS settings as well as some other Oracle session settings.
117
150
 
118
151
  ### Adapter settings
119
152
 
@@ -138,12 +171,35 @@ If you want to change Oracle enhanced adapter default settings then create initi
138
171
 
139
172
  In case of Rails 2 application you do not need to use `ActiveSupport.on_load(:active_record) do ... end` around settings code block.
140
173
 
141
- See other adapter settings in [oracle_enhanced_adapter.rb](/rsim/oracle-enhanced/blob/master/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb).
174
+ See other adapter settings in [oracle_enhanced_adapter.rb](http://github.com/rsim/oracle-enhanced/blob/master/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb).
142
175
 
143
176
  ### Legacy schema support
144
177
 
145
178
  If you want to put Oracle enhanced adapter on top of existing schema tables then there are several methods how to override ActiveRecord defaults, see example:
146
179
 
180
+ class Employee < ActiveRecord::Base
181
+ # specify schema and table name
182
+ self.table_name "hr.hr_employees"
183
+ # specify primary key name
184
+ self.primary_key "employee_id"
185
+ # specify sequence name
186
+ self.sequence_name "hr.hr_employee_s"
187
+ # set which DATE columns should be converted to Ruby Date
188
+ set_date_columns :hired_on, :birth_date_on
189
+ # set which DATE columns should be converted to Ruby Time
190
+ set_datetime_columns :last_login_time
191
+ # set which VARCHAR2 columns should be converted to true and false
192
+ set_boolean_columns :manager, :active
193
+ # set which columns should be ignored in ActiveRecord
194
+ ignore_table_columns :attribute1, :attribute2
195
+ end
196
+
197
+ You can also access remote tables over database link using
198
+
199
+ self.table_name "hr_employees@db_link"
200
+
201
+ Examples for Rails 3.2 and lower version of Rails
202
+
147
203
  class Employee < ActiveRecord::Base
148
204
  # specify schema and table name
149
205
  set_table_name "hr.hr_employees"
@@ -257,7 +313,7 @@ And you can even create index on multiple tables by providing SELECT statements
257
313
  Post.contains(:all_text, "aaa within title")
258
314
  Post.contains(:all_text, "bbb within comment_author")
259
315
 
260
- ### Oracle virtual collumns support
316
+ ### Oracle virtual columns support
261
317
 
262
318
  Since version R11G1 Oracle database allows adding computed [Virtual Columns](http://www.oracle-base.com/articles/11g/virtual-columns-11gr1.php) to the table.
263
319
  They can be used as normal fields in the queries, in the foreign key contstraints and to partitioning data.
@@ -366,7 +422,7 @@ When Apache with Phusion Passenger (mod_passenger or previously mod_rails) is us
366
422
  RUNNING TESTS
367
423
  -------------
368
424
 
369
- See [RUNNING_TESTS.md](/rsim/oracle-enhanced/blob/master/RUNNING_TESTS.md) for information how to set up environment and run Oracle enhanced adapter unit tests.
425
+ See [RUNNING_TESTS.md](https://github.com/rsim/oracle-enhanced/blob/master/RUNNING_TESTS.md) for information how to set up environment and run Oracle enhanced adapter unit tests.
370
426
 
371
427
  LINKS
372
428
  -----
data/Rakefile CHANGED
@@ -22,6 +22,7 @@ EOS
22
22
  gem.homepage = "http://github.com/pmacs/oracle-enhanced"
23
23
  gem.authors = ["Charles Treatman", "Raimonds Simanovskis"]
24
24
  gem.extra_rdoc_files = ['README.md']
25
+ gem.license = 'MIT'
25
26
  end
26
27
  Jeweler::RubygemsDotOrgTasks.new
27
28
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.4.2.rc1
1
+ 1.5.5
@@ -29,24 +29,138 @@
29
29
  # contribution.
30
30
  # portions Copyright 2005 Graham Jenkins
31
31
 
32
- # ActiveRecord 2.2 does not load version file automatically
33
- require 'active_record/version' unless defined?(ActiveRecord::VERSION)
34
-
35
32
  require 'active_record/connection_adapters/abstract_adapter'
36
33
  require 'active_record/connection_adapters/oracle_enhanced_connection'
37
34
 
38
- require 'active_record/connection_adapters/oracle_enhanced_base_ext'
39
35
  require 'active_record/connection_adapters/oracle_enhanced_column'
40
36
 
41
37
  require 'digest/sha1'
42
38
 
39
+ require 'arel/visitors/bind_visitor'
40
+
41
+ ActiveRecord::Base.class_eval do
42
+ class_attribute :custom_create_method, :custom_update_method, :custom_delete_method
43
+ end
44
+
43
45
  module ActiveRecord
46
+ class Base
47
+
48
+ # Specify table columns which should be ignored by ActiveRecord, e.g.:
49
+ #
50
+ # ignore_table_columns :attribute1, :attribute2
51
+ def self.ignore_table_columns(*args)
52
+ connection.ignore_table_columns(table_name,*args)
53
+ end
54
+
55
+ # Specify which table columns should be typecasted to Date (without time), e.g.:
56
+ #
57
+ # set_date_columns :created_on, :updated_on
58
+ def self.set_date_columns(*args)
59
+ connection.set_type_for_columns(table_name,:date,*args)
60
+ end
61
+
62
+ # Specify which table columns should be typecasted to Time (or DateTime), e.g.:
63
+ #
64
+ # set_datetime_columns :created_date, :updated_date
65
+ def self.set_datetime_columns(*args)
66
+ connection.set_type_for_columns(table_name,:datetime,*args)
67
+ end
68
+
69
+ # Specify which table columns should be typecasted to boolean values +true+ or +false+, e.g.:
70
+ #
71
+ # set_boolean_columns :is_valid, :is_completed
72
+ def self.set_boolean_columns(*args)
73
+ connection.set_type_for_columns(table_name,:boolean,*args)
74
+ end
75
+
76
+ # Specify which table columns should be typecasted to integer values.
77
+ # Might be useful to force NUMBER(1) column to be integer and not boolean, or force NUMBER column without
78
+ # scale to be retrieved as integer and not decimal. Example:
79
+ #
80
+ # set_integer_columns :version_number, :object_identifier
81
+ def self.set_integer_columns(*args)
82
+ connection.set_type_for_columns(table_name,:integer,*args)
83
+ end
84
+
85
+ # Specify which table columns should be typecasted to string values.
86
+ # Might be useful to specify that columns should be string even if its name matches boolean column criteria.
87
+ #
88
+ # set_string_columns :active_flag
89
+ def self.set_string_columns(*args)
90
+ connection.set_type_for_columns(table_name,:string,*args)
91
+ end
92
+
93
+ # Get table comment from schema definition.
94
+ def self.table_comment
95
+ connection.table_comment(self.table_name)
96
+ end
97
+
98
+ def self.lob_columns
99
+ columns.select do |column|
100
+ column.respond_to?(:lob?) && column.lob?
101
+ end
102
+ end
103
+
104
+ def self.virtual_columns
105
+ columns.select do |column|
106
+ column.respond_to?(:virtual?) && column.virtual?
107
+ end
108
+ end
109
+
110
+ def arel_attributes_with_values(attribute_names)
111
+ virtual_column_names = self.class.virtual_columns.map(&:name)
112
+ super(attribute_names - virtual_column_names)
113
+ end
114
+
115
+ # After setting large objects to empty, select the OCI8::LOB
116
+ # and write back the data.
117
+ before_update :record_changed_lobs
118
+ after_update :enhanced_write_lobs
119
+
120
+ private
121
+
122
+ def enhanced_write_lobs
123
+ if self.class.connection.is_a?(ConnectionAdapters::OracleEnhancedAdapter) &&
124
+ !(
125
+ (self.class.custom_create_method || self.class.custom_create_method) ||
126
+ (self.class.custom_update_method || self.class.custom_update_method)
127
+ )
128
+ self.class.connection.write_lobs(self.class.table_name, self.class, attributes, @changed_lob_columns || self.class.lob_columns)
129
+ end
130
+ end
131
+
132
+ def record_changed_lobs
133
+ @changed_lob_columns = self.class.lob_columns.select do |col|
134
+ self.class.serialized_attributes.keys.include?(col.name) ||
135
+ (self.send(:"#{col.name}_changed?") && !self.class.readonly_attributes.to_a.include?(col.name))
136
+ end
137
+ end
138
+ end
139
+ end
140
+
141
+ module ActiveRecord
142
+ module ConnectionHandling #:nodoc:
143
+ # Establishes a connection to the database that's used by all Active Record objects.
144
+ def oracle_enhanced_connection(config) #:nodoc:
145
+ if config[:emulate_oracle_adapter] == true
146
+ # allows the enhanced adapter to look like the OracleAdapter. Useful to pick up
147
+ # conditionals in the rails activerecord test suite
148
+ require 'active_record/connection_adapters/emulation/oracle_adapter'
149
+ ConnectionAdapters::OracleAdapter.new(
150
+ ConnectionAdapters::OracleEnhancedConnection.create(config), logger, config)
151
+ else
152
+ ConnectionAdapters::OracleEnhancedAdapter.new(
153
+ ConnectionAdapters::OracleEnhancedConnection.create(config), logger, config)
154
+ end
155
+ end
156
+ end
157
+
44
158
  module ConnectionAdapters #:nodoc:
45
159
 
46
160
  # Oracle enhanced adapter will work with both
47
161
  # Ruby 1.8/1.9 ruby-oci8 gem (which provides interface to Oracle OCI client)
48
162
  # or with JRuby and Oracle JDBC driver.
49
- #
163
+ #
50
164
  # It should work with Oracle 9i, 10g and 11g databases.
51
165
  # Limited set of functionality should work on Oracle 8i as well but several features
52
166
  # rely on newer functionality in Oracle database.
@@ -71,9 +185,9 @@ module ActiveRecord
71
185
  # * <tt>:username</tt>
72
186
  # * <tt>:password</tt>
73
187
  # * <tt>:database</tt> - either TNS alias or connection string for OCI client or database name in JDBC connection string
74
- #
188
+ #
75
189
  # Optional parameters:
76
- #
190
+ #
77
191
  # * <tt>:host</tt> - host name for JDBC connection, defaults to "localhost"
78
192
  # * <tt>:port</tt> - port number for JDBC connection, defaults to 1521
79
193
  # * <tt>:privilege</tt> - set "SYSDBA" if you want to connect with this privilege
@@ -82,11 +196,10 @@ module ActiveRecord
82
196
  # * <tt>:cursor_sharing</tt> - cursor sharing mode to minimize amount of unique statements, defaults to "force"
83
197
  # * <tt>:time_zone</tt> - database session time zone
84
198
  # (it is recommended to set it using ENV['TZ'] which will be then also used for database session time zone)
85
- #
199
+ #
86
200
  # Optionals NLS parameters:
87
- #
201
+ #
88
202
  # * <tt>:nls_calendar</tt>
89
- # * <tt>:nls_characterset</tt>
90
203
  # * <tt>:nls_comp</tt>
91
204
  # * <tt>:nls_currency</tt>
92
205
  # * <tt>:nls_date_format</tt> - format for :date columns, defaults to <tt>YYYY-MM-DD HH24:MI:SS</tt>
@@ -96,7 +209,6 @@ module ActiveRecord
96
209
  # * <tt>:nls_language</tt>
97
210
  # * <tt>:nls_length_semantics</tt> - semantics of size of VARCHAR2 and CHAR columns, defaults to <tt>CHAR</tt>
98
211
  # (meaning that size specifies number of characters and not bytes)
99
- # * <tt>:nls_nchar_characterset</tt>
100
212
  # * <tt>:nls_nchar_conv_excp</tt>
101
213
  # * <tt>:nls_numeric_characters</tt>
102
214
  # * <tt>:nls_sort</tt>
@@ -105,7 +217,7 @@ module ActiveRecord
105
217
  # * <tt>:nls_timestamp_tz_format</tt>
106
218
  # * <tt>:nls_time_format</tt>
107
219
  # * <tt>:nls_time_tz_format</tt>
108
- #
220
+ #
109
221
  class OracleEnhancedAdapter < AbstractAdapter
110
222
 
111
223
  ##
@@ -126,12 +238,12 @@ module ActiveRecord
126
238
  # to Date then you can add the following line to your initializer file:
127
239
  #
128
240
  # ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.emulate_dates = true
129
- #
241
+ #
130
242
  # As this option can have side effects when unnecessary typecasting is done it is recommended
131
243
  # that Date columns are explicily defined with +set_date_columns+ method.
132
244
  cattr_accessor :emulate_dates
133
245
  self.emulate_dates = false
134
-
246
+
135
247
  ##
136
248
  # :singleton-method:
137
249
  # OracleEnhancedAdapter will use the default tablespace, but if you want specific types of
@@ -139,7 +251,7 @@ module ActiveRecord
139
251
  #
140
252
  # ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.default_tablespaces =
141
253
  # {:clob => 'TS_LOB', :blob => 'TS_LOB', :index => 'TS_INDEX', :table => 'TS_DATA'}
142
- #
254
+ #
143
255
  # Using the :tablespace option where available (e.g create_table) will take precedence
144
256
  # over these settings.
145
257
  cattr_accessor :default_tablespaces
@@ -153,12 +265,19 @@ module ActiveRecord
153
265
  # to Date then you can add the following line to your initializer file:
154
266
  #
155
267
  # ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.emulate_dates_by_column_name = true
156
- #
268
+ #
157
269
  # As this option can have side effects when unnecessary typecasting is done it is recommended
158
270
  # that Date columns are explicily defined with +set_date_columns+ method.
159
271
  cattr_accessor :emulate_dates_by_column_name
160
272
  self.emulate_dates_by_column_name = false
161
273
 
274
+ ##
275
+ # :singleton-method:
276
+ # Specify how `NUMBER` datatype columns, without precision and scale, are handled in Rails world.
277
+ # Default is :decimal and other valid option is :float. Be wary of setting it to other values.
278
+ cattr_accessor :number_datatype_coercion
279
+ self.number_datatype_coercion = :decimal
280
+
162
281
  # Check column name to identify if it is Date (and not Time) column.
163
282
  # Is used if +emulate_dates_by_column_name+ option is set to +true+.
164
283
  # Override this method definition in initializer file if different Date column recognition is needed.
@@ -193,7 +312,7 @@ module ActiveRecord
193
312
  # Is used if +emulate_integers_by_column_name+ option is set to +true+.
194
313
  # Override this method definition in initializer file if different Integer column recognition is needed.
195
314
  def self.is_integer_column?(name, table_name = nil)
196
- name =~ /(^|_)id$/i
315
+ !!(name =~ /(^|_)id$/i)
197
316
  end
198
317
 
199
318
  ##
@@ -212,7 +331,7 @@ module ActiveRecord
212
331
  return true if ["CHAR(1)","VARCHAR2(1)"].include?(field_type)
213
332
  field_type =~ /^VARCHAR2/ && (name =~ /_flag$/i || name =~ /_yn$/i)
214
333
  end
215
-
334
+
216
335
  # How boolean value should be quoted to String.
217
336
  # Used if +emulate_booleans_from_strings+ option is set to +true+.
218
337
  def self.boolean_to_string(bool)
@@ -222,15 +341,15 @@ module ActiveRecord
222
341
  ##
223
342
  # :singleton-method:
224
343
  # Specify non-default date format that should be used when assigning string values to :date columns, e.g.:
225
- #
344
+ #
226
345
  # ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.string_to_date_format = “%d.%m.%Y”
227
346
  cattr_accessor :string_to_date_format
228
347
  self.string_to_date_format = nil
229
-
348
+
230
349
  ##
231
350
  # :singleton-method:
232
351
  # Specify non-default time format that should be used when assigning string values to :datetime columns, e.g.:
233
- #
352
+ #
234
353
  # ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.string_to_time_format = “%d.%m.%Y %H:%M:%S”
235
354
  cattr_accessor :string_to_time_format
236
355
  self.string_to_time_format = nil
@@ -265,13 +384,22 @@ module ActiveRecord
265
384
  end
266
385
  end
267
386
 
387
+ class BindSubstitution < Arel::Visitors::Oracle #:nodoc:
388
+ include Arel::Visitors::BindVisitor
389
+ end
390
+
268
391
  def initialize(connection, logger, config) #:nodoc:
269
392
  super(connection, logger)
270
393
  @quoted_column_names, @quoted_table_names = {}, {}
271
394
  @config = config
272
395
  @statements = StatementPool.new(connection, config.fetch(:statement_limit) { 250 })
273
396
  @enable_dbms_output = false
274
- @visitor = Arel::Visitors::Oracle.new self if defined?(Arel::Visitors::Oracle)
397
+ if config.fetch(:prepared_statements) { true }
398
+ @visitor = Arel::Visitors::Oracle.new self
399
+ @prepared_statements = true
400
+ else
401
+ @visitor = unprepared_visitor
402
+ end
275
403
  end
276
404
 
277
405
  def self.visitor_for(pool) # :nodoc:
@@ -279,7 +407,7 @@ module ActiveRecord
279
407
  end
280
408
 
281
409
  ADAPTER_NAME = 'OracleEnhanced'.freeze
282
-
410
+
283
411
  def adapter_name #:nodoc:
284
412
  ADAPTER_NAME
285
413
  end
@@ -296,10 +424,15 @@ module ActiveRecord
296
424
  true
297
425
  end
298
426
 
427
+ def supports_transaction_isolation? #:nodoc:
428
+ true
429
+ end
430
+
431
+ NUMBER_MAX_PRECISION = 38
432
+
299
433
  #:stopdoc:
300
434
  DEFAULT_NLS_PARAMETERS = {
301
435
  :nls_calendar => nil,
302
- :nls_characterset => nil,
303
436
  :nls_comp => nil,
304
437
  :nls_currency => nil,
305
438
  :nls_date_format => 'YYYY-MM-DD HH24:MI:SS',
@@ -308,7 +441,6 @@ module ActiveRecord
308
441
  :nls_iso_currency => nil,
309
442
  :nls_language => nil,
310
443
  :nls_length_semantics => 'CHAR',
311
- :nls_nchar_characterset => nil,
312
444
  :nls_nchar_conv_excp => nil,
313
445
  :nls_numeric_characters => nil,
314
446
  :nls_sort => nil,
@@ -321,10 +453,10 @@ module ActiveRecord
321
453
 
322
454
  #:stopdoc:
323
455
  NATIVE_DATABASE_TYPES = {
324
- :primary_key => "NUMBER(38) NOT NULL PRIMARY KEY",
456
+ :primary_key => "NUMBER(#{NUMBER_MAX_PRECISION}) NOT NULL PRIMARY KEY",
325
457
  :string => { :name => "VARCHAR2", :limit => 255 },
326
458
  :text => { :name => "CLOB" },
327
- :integer => { :name => "NUMBER", :limit => 38 },
459
+ :integer => { :name => "NUMBER", :limit => NUMBER_MAX_PRECISION },
328
460
  :float => { :name => "NUMBER" },
329
461
  :decimal => { :name => "DECIMAL" },
330
462
  :datetime => { :name => "DATE" },
@@ -364,7 +496,17 @@ module ActiveRecord
364
496
  IDENTIFIER_MAX_LENGTH
365
497
  end
366
498
 
367
- # the maximum length of an index name
499
+ # Returns the maximum allowed length for an index name. This
500
+ # limit is enforced by rails and Is less than or equal to
501
+ # <tt>index_name_length</tt>. The gap between
502
+ # <tt>index_name_length</tt> is to allow internal rails
503
+ # opreations to use prefixes in temporary opreations.
504
+ def allowed_index_name_length
505
+ index_name_length
506
+ end
507
+
508
+ # the maximum length of an index name
509
+ # supported by this database
368
510
  def index_name_length
369
511
  IDENTIFIER_MAX_LENGTH
370
512
  end
@@ -418,12 +560,12 @@ module ActiveRecord
418
560
  # Names must be from 1 to 30 bytes long with these exceptions:
419
561
  # * Names of databases are limited to 8 bytes.
420
562
  # * Names of database links can be as long as 128 bytes.
421
- #
563
+ #
422
564
  # Nonquoted identifiers cannot be Oracle Database reserved words
423
- #
565
+ #
424
566
  # Nonquoted identifiers must begin with an alphabetic character from
425
567
  # your database character set
426
- #
568
+ #
427
569
  # Nonquoted identifiers can contain only alphanumeric characters from
428
570
  # your database character set and the underscore (_), dollar sign ($),
429
571
  # and pound sign (#). Database links can also contain periods (.) and
@@ -438,7 +580,7 @@ module ActiveRecord
438
580
  # can be prefixed with schema name
439
581
  # CamelCase table names should be quoted
440
582
  def self.valid_table_name?(name) #:nodoc:
441
- name = name.to_s
583
+ name = name.to_s
442
584
  name =~ VALID_TABLE_NAME && !(name =~ /[A-Z]/ && name =~ /[a-z]/) ? true : false
443
585
  end
444
586
 
@@ -446,7 +588,7 @@ module ActiveRecord
446
588
  name = name.to_s
447
589
  @quoted_table_names[name] ||= name.split('.').map{|n| n.split('@').map{|m| quote_column_name(m)}.join('@')}.join('.')
448
590
  end
449
-
591
+
450
592
  def quote_string(s) #:nodoc:
451
593
  s.gsub(/'/, "''")
452
594
  end
@@ -455,7 +597,7 @@ module ActiveRecord
455
597
  if value && column
456
598
  case column.type
457
599
  when :text, :binary
458
- %Q{empty_#{ column.sql_type.downcase rescue 'blob' }()}
600
+ %Q{empty_#{ type_to_sql(column.type.to_sym).downcase rescue 'blob' }()}
459
601
  # NLS_DATE_FORMAT independent TIMESTAMP support
460
602
  when :timestamp
461
603
  quote_timestamp_with_to_timestamp(value)
@@ -569,7 +711,7 @@ module ActiveRecord
569
711
 
570
712
  # Reconnects to the database.
571
713
  def reconnect! #:nodoc:
572
- clear_cache!
714
+ super
573
715
  @connection.reset!
574
716
  rescue OracleEnhancedConnectionException => e
575
717
  @logger.warn "#{adapter_name} automatic reconnection failed: #{e.message}" if @logger
@@ -582,7 +724,7 @@ module ActiveRecord
582
724
 
583
725
  # Disconnects from the database.
584
726
  def disconnect! #:nodoc:
585
- clear_cache!
727
+ super
586
728
  @connection.logoff rescue nil
587
729
  end
588
730
 
@@ -596,7 +738,7 @@ module ActiveRecord
596
738
  end
597
739
 
598
740
  def substitute_at(column, index)
599
- Arel.sql(":a#{index + 1}")
741
+ Arel::Nodes::BindParam.new (":a#{index + 1}")
600
742
  end
601
743
 
602
744
  def clear_cache!
@@ -604,10 +746,13 @@ module ActiveRecord
604
746
  end
605
747
 
606
748
  def exec_query(sql, name = 'SQL', binds = [])
607
- log(sql, name, binds) do
749
+ type_casted_binds = binds.map { |col, val|
750
+ [col, type_cast(val, col)]
751
+ }
752
+ log(sql, name, type_casted_binds) do
608
753
  cursor = nil
609
754
  cached = false
610
- if binds.empty?
755
+ if without_prepared_statement?(binds)
611
756
  cursor = @connection.prepare(sql)
612
757
  else
613
758
  unless @statements.key? sql
@@ -618,7 +763,7 @@ module ActiveRecord
618
763
 
619
764
  binds.each_with_index do |bind, i|
620
765
  col, val = bind
621
- cursor.bind_param(i + 1, type_cast(val, col), col && col.type)
766
+ cursor.bind_param(i + 1, type_cast(val, col), col)
622
767
  end
623
768
 
624
769
  cached = true
@@ -626,7 +771,7 @@ module ActiveRecord
626
771
 
627
772
  cursor.exec
628
773
 
629
- if name == 'EXPLAIN'
774
+ if name == 'EXPLAIN' and sql =~ /^EXPLAIN/
630
775
  res = true
631
776
  else
632
777
  columns = cursor.get_col_names.map do |col_name|
@@ -654,7 +799,7 @@ module ActiveRecord
654
799
  end
655
800
 
656
801
  def explain(arel, binds = [])
657
- sql = "EXPLAIN PLAN FOR #{to_sql(arel)}"
802
+ sql = "EXPLAIN PLAN FOR #{to_sql(arel, binds)}"
658
803
  return if sql =~ /FROM all_/
659
804
  if ORACLE_ENHANCED_CONNECTION == :jdbc
660
805
  exec_query(sql, 'EXPLAIN', binds)
@@ -666,13 +811,8 @@ module ActiveRecord
666
811
 
667
812
  # Returns an array of arrays containing the field values.
668
813
  # Order is the same as that returned by #columns.
669
- def select_rows(sql, name = nil)
670
- # last parameter indicates to return also column list
671
- result = columns = nil
672
- log(sql, name) do
673
- result, columns = @connection.select(sql, name, true)
674
- end
675
- result.map{ |v| columns.map{|c| v[c]} }
814
+ def select_rows(sql, name = nil, binds = [])
815
+ exec_query(sql, name, binds).rows
676
816
  end
677
817
 
678
818
  # Executes an INSERT statement and returns the new record's ID
@@ -696,30 +836,37 @@ module ActiveRecord
696
836
  def sql_for_insert(sql, pk, id_value, sequence_name, binds)
697
837
  unless id_value || pk.nil? || (defined?(CompositePrimaryKeys) && pk.kind_of?(CompositePrimaryKeys::CompositeKeys))
698
838
  sql = "#{sql} RETURNING #{quote_column_name(pk)} INTO :returning_id"
699
- (binds = binds.dup) << [:returning_id, nil]
839
+ returning_id_col = OracleEnhancedColumn.new("returning_id", nil, "number", true, "dual", :integer, true, true)
840
+ (binds = binds.dup) << [returning_id_col, nil]
700
841
  end
701
842
  [sql, binds]
702
843
  end
703
844
 
704
- EXEC_INSERT_RESULT_COLUMNS = %w(returning_id) #:nodoc:
705
-
706
845
  # New method in ActiveRecord 3.1
707
- def exec_insert(sql, name, binds)
708
- log(sql, name, binds) do
709
- returning_id_index = nil
710
- cursor = if @statements.key?(sql)
711
- @statements[sql]
846
+ def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
847
+ type_casted_binds = binds.map { |col, val|
848
+ [col, type_cast(val, col)]
849
+ }
850
+ log(sql, name, type_casted_binds) do
851
+ returning_id_col = returning_id_index = nil
852
+ if without_prepared_statement?(binds)
853
+ cursor = @connection.prepare(sql)
712
854
  else
713
- @statements[sql] = @connection.prepare(sql)
714
- end
855
+ unless @statements.key? (sql)
856
+ @statements[sql] = @connection.prepare(sql)
857
+ end
715
858
 
716
- binds.each_with_index do |bind, i|
717
- col, val = bind
718
- if col == :returning_id
719
- returning_id_index = i + 1
720
- cursor.bind_returning_param(returning_id_index, Integer)
721
- else
722
- cursor.bind_param(i + 1, type_cast(val, col), col && col.type)
859
+ cursor = @statements[sql]
860
+
861
+ binds.each_with_index do |bind, i|
862
+ col, val = bind
863
+ if col.returning_id?
864
+ returning_id_col = [col]
865
+ returning_id_index = i + 1
866
+ cursor.bind_returning_param(returning_id_index, Integer)
867
+ else
868
+ cursor.bind_param(i + 1, type_cast(val, col), col)
869
+ end
723
870
  end
724
871
  end
725
872
 
@@ -730,7 +877,7 @@ module ActiveRecord
730
877
  returning_id = cursor.get_returning_param(returning_id_index, Integer)
731
878
  rows << [returning_id]
732
879
  end
733
- ActiveRecord::Result.new(EXEC_INSERT_RESULT_COLUMNS, rows)
880
+ ActiveRecord::Result.new(returning_id_col || [], rows)
734
881
  end
735
882
  end
736
883
 
@@ -738,7 +885,7 @@ module ActiveRecord
738
885
  def exec_update(sql, name, binds)
739
886
  log(sql, name, binds) do
740
887
  cached = false
741
- if binds.empty?
888
+ if without_prepared_statement?(binds)
742
889
  cursor = @connection.prepare(sql)
743
890
  else
744
891
  cursor = if @statements.key?(sql)
@@ -749,7 +896,7 @@ module ActiveRecord
749
896
 
750
897
  binds.each_with_index do |bind, i|
751
898
  col, val = bind
752
- cursor.bind_param(i + 1, type_cast(val, col), col && col.type)
899
+ cursor.bind_param(i + 1, type_cast(val, col), col)
753
900
  end
754
901
  cached = true
755
902
  end
@@ -779,6 +926,21 @@ module ActiveRecord
779
926
  @connection.autocommit = false
780
927
  end
781
928
 
929
+ def transaction_isolation_levels
930
+ # Oracle database supports `READ COMMITTED` and `SERIALIZABLE`
931
+ # No read uncommitted nor repeatable read supppoted
932
+ # http://docs.oracle.com/cd/E11882_01/server.112/e26088/statements_10005.htm#SQLRF55422
933
+ {
934
+ read_committed: "READ COMMITTED",
935
+ serializable: "SERIALIZABLE"
936
+ }
937
+ end
938
+
939
+ def begin_isolated_db_transaction(isolation)
940
+ begin_db_transaction
941
+ execute "SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}"
942
+ end
943
+
782
944
  def commit_db_transaction #:nodoc:
783
945
  @connection.commit
784
946
  ensure
@@ -791,32 +953,18 @@ module ActiveRecord
791
953
  @connection.autocommit = true
792
954
  end
793
955
 
794
- def create_savepoint #:nodoc:
956
+ def create_savepoint(name = current_savepoint_name) #:nodoc:
795
957
  execute("SAVEPOINT #{current_savepoint_name}")
796
958
  end
797
959
 
798
- def rollback_to_savepoint #:nodoc:
960
+ def rollback_to_savepoint(name = current_savepoint_name) #:nodoc:
799
961
  execute("ROLLBACK TO #{current_savepoint_name}")
800
962
  end
801
963
 
802
- def release_savepoint #:nodoc:
964
+ def release_savepoint(name = current_savepoint_name) #:nodoc:
803
965
  # there is no RELEASE SAVEPOINT statement in Oracle
804
966
  end
805
967
 
806
- def add_limit_offset!(sql, options) #:nodoc:
807
- # added to_i for limit and offset to protect from SQL injection
808
- offset = (options[:offset] || 0).to_i
809
- limit = options[:limit]
810
- limit = limit.is_a?(String) && limit.blank? ? nil : limit && limit.to_i
811
- if limit && offset > 0
812
- sql.replace "SELECT * FROM (SELECT raw_sql_.*, ROWNUM raw_rnum_ FROM (#{sql}) raw_sql_ WHERE ROWNUM <= #{offset+limit}) WHERE raw_rnum_ > #{offset}"
813
- elsif limit
814
- sql.replace "SELECT * FROM (#{sql}) WHERE ROWNUM <= #{limit}"
815
- elsif offset > 0
816
- sql.replace "SELECT * FROM (SELECT raw_sql_.*, ROWNUM raw_rnum_ FROM (#{sql}) raw_sql_) WHERE raw_rnum_ > #{offset}"
817
- end
818
- end
819
-
820
968
  @@do_not_prefetch_primary_key = {}
821
969
 
822
970
  # Returns true for Oracle adapter (since Oracle requires primary key
@@ -842,9 +990,36 @@ module ActiveRecord
842
990
  # Returns default sequence name for table.
843
991
  # Will take all or first 26 characters of table name and append _seq suffix
844
992
  def default_sequence_name(table_name, primary_key = nil)
845
- # TODO: remove schema prefix if present before truncating
846
- # truncate table name if necessary to fit in max length of identifier
847
- "#{table_name.to_s[0,IDENTIFIER_MAX_LENGTH-4]}_seq"
993
+ table_name.to_s.gsub /(^|\.)([\w$-]{1,#{sequence_name_length-4}})([\w$-]*)$/, '\1\2_seq'
994
+ end
995
+
996
+ def reset_pk_sequence!(table_name, primary_key = nil, sequence_name = nil) #:nodoc:
997
+ return nil unless table_exists?(table_name)
998
+ unless primary_key and sequence_name
999
+ # *Note*: Only primary key is implemented - sequence will be nil.
1000
+ primary_key, sequence_name = pk_and_sequence_for(table_name)
1001
+ # TODO This sequence_name implemantation is just enough
1002
+ # to satisty fixures. To get correct sequence_name always
1003
+ # pk_and_sequence_for method needs some work.
1004
+ begin
1005
+ sequence_name = table_name.classify.constantize.sequence_name
1006
+ rescue
1007
+ sequence_name = default_sequence_name(table_name)
1008
+ end
1009
+ end
1010
+
1011
+ if @logger && primary_key && !sequence_name
1012
+ @logger.warn "#{table_name} has primary key #{primary_key} with no default sequence"
1013
+ end
1014
+
1015
+ if primary_key && sequence_name
1016
+ new_start_value = select_value("
1017
+ select NVL(max(#{quote_column_name(primary_key)}),0) + 1 from #{quote_table_name(table_name)}
1018
+ ", new_start_value)
1019
+
1020
+ execute ("DROP SEQUENCE #{quote_table_name(sequence_name)}")
1021
+ execute ("CREATE SEQUENCE #{quote_table_name(sequence_name)} START WITH #{new_start_value}")
1022
+ end
848
1023
  end
849
1024
 
850
1025
  # Inserts the given fixture into the table. Overridden to properly handle lobs.
@@ -852,9 +1027,9 @@ module ActiveRecord
852
1027
  super
853
1028
 
854
1029
  if ActiveRecord::Base.pluralize_table_names
855
- klass = table_name.singularize.camelize
1030
+ klass = table_name.to_s.singularize.camelize
856
1031
  else
857
- klass = table_name.camelize
1032
+ klass = table_name.to_s.camelize
858
1033
  end
859
1034
 
860
1035
  klass = klass.constantize rescue nil
@@ -982,7 +1157,7 @@ module ActiveRecord
982
1157
  current_index = row['index_name']
983
1158
  end
984
1159
 
985
- # Functional index columns and virtual columns both get stored as column expressions,
1160
+ # Functional index columns and virtual columns both get stored as column expressions,
986
1161
  # but re-creating a virtual column index as an expression (instead of using the virtual column's name)
987
1162
  # results in a ORA-54018 error. Thus, we only want the column expression value returned
988
1163
  # when the column is not virtual.
@@ -1008,12 +1183,12 @@ module ActiveRecord
1008
1183
  @@ignore_table_columns[table_name] += args.map{|a| a.to_s.downcase}
1009
1184
  @@ignore_table_columns[table_name].uniq!
1010
1185
  end
1011
-
1186
+
1012
1187
  def ignored_table_columns(table_name) #:nodoc:
1013
1188
  @@ignore_table_columns ||= {}
1014
1189
  @@ignore_table_columns[table_name]
1015
1190
  end
1016
-
1191
+
1017
1192
  # used just in tests to clear ignored table columns
1018
1193
  def clear_ignored_table_columns #:nodoc:
1019
1194
  @@ignore_table_columns = nil
@@ -1029,7 +1204,7 @@ module ActiveRecord
1029
1204
  @@table_column_type[table_name][col.to_s.downcase] = column_type
1030
1205
  end
1031
1206
  end
1032
-
1207
+
1033
1208
  def get_type_for_column(table_name, column_name) #:nodoc:
1034
1209
  @@table_column_type && @@table_column_type[table_name] && @@table_column_type[table_name][column_name.to_s.downcase]
1035
1210
  end
@@ -1086,7 +1261,7 @@ module ActiveRecord
1086
1261
  @@do_not_prefetch_primary_key[table_name] = nil
1087
1262
 
1088
1263
  table_cols = <<-SQL.strip.gsub(/\s+/, ' ')
1089
- SELECT column_name AS name, data_type AS sql_type, data_default, nullable, virtual_column, hidden_column,
1264
+ SELECT column_name AS name, data_type AS sql_type, data_default, nullable, virtual_column, hidden_column, data_type_owner AS sql_type_owner,
1090
1265
  DECODE(data_type, 'NUMBER', data_precision,
1091
1266
  'FLOAT', data_precision,
1092
1267
  'VARCHAR2', DECODE(char_used, 'C', char_length, data_length),
@@ -1102,12 +1277,16 @@ module ActiveRecord
1102
1277
  SQL
1103
1278
 
1104
1279
  # added deletion of ignored columns
1105
- select_all(table_cols, name).delete_if do |row|
1280
+ select_all(table_cols, name).to_a.delete_if do |row|
1106
1281
  ignored_columns && ignored_columns.include?(row['name'].downcase)
1107
1282
  end.map do |row|
1108
1283
  limit, scale = row['limit'], row['scale']
1109
1284
  if limit || scale
1110
- row['sql_type'] += "(#{(limit || 38).to_i}" + ((scale = scale.to_i) > 0 ? ",#{scale})" : ")")
1285
+ row['sql_type'] += "(#{(limit || NUMBER_MAX_PRECISION).to_i}" + ((scale = scale.to_i) > 0 ? ",#{scale})" : ")")
1286
+ end
1287
+
1288
+ if row['sql_type_owner']
1289
+ row['sql_type'] = row['sql_type_owner'] + '.' + row['sql_type']
1111
1290
  end
1112
1291
 
1113
1292
  is_virtual = row['virtual_column']=='YES'
@@ -1116,7 +1295,7 @@ module ActiveRecord
1116
1295
  if row['data_default'] && !is_virtual
1117
1296
  row['data_default'].sub!(/^(.*?)\s*$/, '\1')
1118
1297
 
1119
- # If a default contains a newline these cleanup regexes need to
1298
+ # If a default contains a newline these cleanup regexes need to
1120
1299
  # match newlines.
1121
1300
  row['data_default'].sub!(/^'(.*)'$/m, '\1')
1122
1301
  row['data_default'] = nil if row['data_default'] =~ /^(null|empty_[bc]lob\(\))$/i
@@ -1155,7 +1334,7 @@ module ActiveRecord
1155
1334
  cattr_accessor :default_sequence_start_value
1156
1335
  self.default_sequence_start_value = 10000
1157
1336
 
1158
- # Find a table's primary key and sequence.
1337
+ # Find a table's primary key and sequence.
1159
1338
  # *Note*: Only primary key is implemented - sequence will be nil.
1160
1339
  def pk_and_sequence_for(table_name, owner=nil, desc_table_name=nil, db_link=nil) #:nodoc:
1161
1340
  if @@cache_columns
@@ -1173,6 +1352,13 @@ module ActiveRecord
1173
1352
  def pk_and_sequence_for_without_cache(table_name, owner=nil, desc_table_name=nil, db_link=nil) #:nodoc:
1174
1353
  (owner, desc_table_name, db_link) = @connection.describe(table_name) unless owner
1175
1354
 
1355
+ seqs = select_values(<<-SQL.strip.gsub(/\s+/, ' '), 'Sequence')
1356
+ select us.sequence_name
1357
+ from all_sequences#{db_link} us
1358
+ where us.sequence_owner = '#{owner}'
1359
+ and us.sequence_name = '#{desc_table_name}_SEQ'
1360
+ SQL
1361
+
1176
1362
  # changed back from user_constraints to all_constraints for consistency
1177
1363
  pks = select_values(<<-SQL.strip.gsub(/\s+/, ' '), 'Primary Key')
1178
1364
  SELECT cc.column_name
@@ -1185,7 +1371,8 @@ module ActiveRecord
1185
1371
  SQL
1186
1372
 
1187
1373
  # only support single column keys
1188
- pks.size == 1 ? [oracle_downcase(pks.first), nil] : nil
1374
+ pks.size == 1 ? [oracle_downcase(pks.first),
1375
+ oracle_downcase(seqs.first)] : nil
1189
1376
  end
1190
1377
 
1191
1378
  # Returns just a table's primary key
@@ -1203,49 +1390,48 @@ module ActiveRecord
1203
1390
  # Oracle requires the ORDER BY columns to be in the SELECT list for DISTINCT
1204
1391
  # queries. However, with those columns included in the SELECT DISTINCT list, you
1205
1392
  # won't actually get a distinct list of the column you want (presuming the column
1206
- # has duplicates with multiple values for the ordered-by columns. So we use the
1393
+ # has duplicates with multiple values for the ordered-by columns. So we use the
1207
1394
  # FIRST_VALUE function to get a single (first) value for each column, effectively
1208
1395
  # making every row the same.
1209
1396
  #
1210
1397
  # distinct("posts.id", "posts.created_at desc")
1211
- def distinct(columns, order_by) #:nodoc:
1212
- return "DISTINCT #{columns}" if order_by.blank?
1213
-
1214
- # construct a valid DISTINCT clause, ie. one that includes the ORDER BY columns, using
1215
- # FIRST_VALUE such that the inclusion of these columns doesn't invalidate the DISTINCT
1216
- order_columns = if order_by.is_a?(String)
1217
- order_by.split(',').map { |s| s.strip }.reject(&:blank?)
1218
- else # in latest ActiveRecord versions order_by is already Array
1219
- order_by
1398
+ def distinct(columns, orders) #:nodoc:
1399
+ # To support Rails 4.0.0 and future releases
1400
+ # because `columns_for_distinct method introduced after Rails 4.0.0 released
1401
+ if super.respond_to?(:columns_for_distinct)
1402
+ super
1403
+ else
1404
+ order_columns = orders.map { |c|
1405
+ c = c.to_sql unless c.is_a?(String)
1406
+ # remove any ASC/DESC modifiers
1407
+ c.gsub(/\s+(ASC|DESC)\s*?/i, '')
1408
+ }.reject(&:blank?).map.with_index { |c,i|
1409
+ "FIRST_VALUE(#{c}) OVER (PARTITION BY #{columns} ORDER BY #{c}) AS alias_#{i}__"
1410
+ }
1411
+ [super].concat(order_columns).join(', ')
1220
1412
  end
1221
- order_columns = order_columns.zip((0...order_columns.size).to_a).map do |c, i|
1413
+ end
1414
+
1415
+ def columns_for_distinct(columns, orders) #:nodoc:
1416
+ # construct a valid columns name for DISTINCT clause,
1417
+ # ie. one that includes the ORDER BY columns, using FIRST_VALUE such that
1418
+ # the inclusion of these columns doesn't invalidate the DISTINCT
1419
+ #
1420
+ # It does not construct DISTINCT clause. Just return column names for distinct.
1421
+ order_columns = orders.reject(&:blank?).map{ |s|
1422
+ s = s.to_sql unless s.is_a?(String)
1222
1423
  # remove any ASC/DESC modifiers
1223
- value = c =~ /^(.+)\s+(ASC|DESC)\s*$/i ? $1 : c
1224
- "FIRST_VALUE(#{value}) OVER (PARTITION BY #{columns} ORDER BY #{c}) AS alias_#{i}__"
1225
- end
1226
- sql = "DISTINCT #{columns}, "
1227
- sql << order_columns * ", "
1424
+ s.gsub(/\s+(ASC|DESC)\s*?/i, '')
1425
+ }.reject(&:blank?).map.with_index { |column,i|
1426
+ "FIRST_VALUE(#{column}) OVER (PARTITION BY #{columns} ORDER BY #{column}) AS alias_#{i}__"
1427
+ }
1428
+ [super, *order_columns].join(', ')
1228
1429
  end
1229
1430
 
1230
1431
  def temporary_table?(table_name) #:nodoc:
1231
1432
  select_value("SELECT temporary FROM user_tables WHERE table_name = '#{table_name.upcase}'") == 'Y'
1232
1433
  end
1233
1434
 
1234
- # ORDER BY clause for the passed order option.
1235
- #
1236
- # Uses column aliases as defined by #distinct.
1237
- #
1238
- # In Rails 3.x this method is moved to Arel
1239
- def add_order_by_for_association_limiting!(sql, options) #:nodoc:
1240
- return sql if options[:order].blank?
1241
-
1242
- order = options[:order].split(',').collect { |s| s.strip }.reject(&:blank?)
1243
- order.map! {|s| $1 if s =~ / (.*)/}
1244
- order = order.zip((0...order.size).to_a).map { |s,i| "alias_#{i}__ #{s}" }.join(', ')
1245
-
1246
- sql << " ORDER BY #{order}"
1247
- end
1248
-
1249
1435
  # construct additional wrapper subquery if select.offset is used to avoid generation of invalid subquery
1250
1436
  # ... IN ( SELECT * FROM ( SELECT raw_sql_.*, rownum raw_rnum_ FROM ( ... ) raw_sql_ ) WHERE raw_rnum_ > ... )
1251
1437
  def join_to_update(update, select) #:nodoc:
@@ -1280,7 +1466,7 @@ module ActiveRecord
1280
1466
 
1281
1467
  def select(sql, name = nil, binds = [])
1282
1468
  if ActiveRecord.const_defined?(:Result)
1283
- exec_query(sql, name, binds).to_a
1469
+ exec_query(sql, name, binds)
1284
1470
  else
1285
1471
  log(sql, name) do
1286
1472
  @connection.select(sql, name, false)
@@ -1301,7 +1487,7 @@ module ActiveRecord
1301
1487
  #
1302
1488
  # PL/SQL in Oracle uses dbms_output for logging print statements
1303
1489
  # These methods stick that output into the Rails log so Ruby and PL/SQL
1304
- # code can can be debugged together in a single application
1490
+ # code can can be debugged together in a single application
1305
1491
 
1306
1492
  # Maximum DBMS_OUTPUT buffer size
1307
1493
  DBMS_OUTPUT_BUFFER_SIZE = 10000 # can be 1-1000000
@@ -1333,7 +1519,7 @@ module ActiveRecord
1333
1519
  ensure
1334
1520
  log_dbms_output if dbms_output_enabled?
1335
1521
  end
1336
-
1522
+
1337
1523
  private
1338
1524
 
1339
1525
  def set_dbms_output_plsql_connection
@@ -1356,19 +1542,6 @@ module ActiveRecord
1356
1542
  end
1357
1543
  end
1358
1544
 
1359
- # Added LOB writing callback for sessions stored in database
1360
- # Otherwise it is not working as Session class is defined before OracleAdapter is loaded in Rails 2.0
1361
- if defined?(CGI::Session::ActiveRecordStore::Session)
1362
- if !CGI::Session::ActiveRecordStore::Session.respond_to?(:after_save_callback_chain) ||
1363
- CGI::Session::ActiveRecordStore::Session.after_save_callback_chain.detect{|cb| cb.method == :enhanced_write_lobs}.nil?
1364
- #:stopdoc:
1365
- class CGI::Session::ActiveRecordStore::Session
1366
- after_save :enhanced_write_lobs
1367
- end
1368
- #:startdoc:
1369
- end
1370
- end
1371
-
1372
1545
  # Implementation of standard schema definition statements and extensions for schema definition
1373
1546
  require 'active_record/connection_adapters/oracle_enhanced_schema_statements'
1374
1547
  require 'active_record/connection_adapters/oracle_enhanced_schema_statements_ext'
@@ -1379,30 +1552,26 @@ require 'active_record/connection_adapters/oracle_enhanced_schema_definitions'
1379
1552
  # Extensions for context index definition
1380
1553
  require 'active_record/connection_adapters/oracle_enhanced_context_index'
1381
1554
 
1382
- # Load custom create, update, delete methods functionality
1383
- require 'active_record/connection_adapters/oracle_enhanced_procedures'
1384
-
1385
1555
  # Load additional methods for composite_primary_keys support
1386
1556
  require 'active_record/connection_adapters/oracle_enhanced_cpk'
1387
1557
 
1388
1558
  # Load patch for dirty tracking methods
1389
1559
  require 'active_record/connection_adapters/oracle_enhanced_dirty'
1390
1560
 
1391
- # Load rake tasks definitions
1392
- begin
1393
- require 'active_record/connection_adapters/oracle_enhanced_tasks'
1394
- rescue LoadError
1395
- end if defined?(Rails) || defined?(RAILS_ROOT)
1396
-
1397
1561
  # Patches and enhancements for schema dumper
1398
1562
  require 'active_record/connection_adapters/oracle_enhanced_schema_dumper'
1399
1563
 
1400
1564
  # Implementation of structure dump
1401
1565
  require 'active_record/connection_adapters/oracle_enhanced_structure_dump'
1402
1566
 
1403
- # Add BigDecimal#to_d, Fixnum#to_d and Bignum#to_d methods if not already present
1404
- require 'active_record/connection_adapters/oracle_enhanced_core_ext'
1567
+ require 'active_record/connection_adapters/oracle_enhanced_version'
1405
1568
 
1406
- require 'active_record/connection_adapters/oracle_enhanced_activerecord_patches'
1569
+ module ActiveRecord
1570
+ autoload :OracleEnhancedProcedures, 'active_record/connection_adapters/oracle_enhanced_procedures'
1571
+ end
1407
1572
 
1408
- require 'active_record/connection_adapters/oracle_enhanced_version'
1573
+ # Patches and enhancements for column dumper
1574
+ require 'active_record/connection_adapters/oracle_enhanced_column_dumper'
1575
+
1576
+ # Moved SchemaCreation class
1577
+ require 'active_record/connection_adapters/oracle_enhanced_schema_creation'