oracle_enhanced 1.3.0.pre → 1.3.0.pre2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. data/.gitignore +0 -1
  2. data/History.txt +1 -1
  3. data/Rakefile +1 -1
  4. data/VERSION +1 -1
  5. data/lib/active_record/connection_adapters/oracle_enhanced.rake +3 -2
  6. data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +223 -154
  7. data/lib/active_record/connection_adapters/oracle_enhanced_context_index.rb +227 -0
  8. data/lib/active_record/connection_adapters/oracle_enhanced_core_ext.rb +1 -1
  9. data/lib/active_record/connection_adapters/oracle_enhanced_dirty.rb +1 -1
  10. data/lib/active_record/connection_adapters/oracle_enhanced_jdbc_connection.rb +3 -3
  11. data/lib/active_record/connection_adapters/oracle_enhanced_oci_connection.rb +6 -1
  12. data/lib/active_record/connection_adapters/oracle_enhanced_procedures.rb +3 -3
  13. data/lib/active_record/connection_adapters/oracle_enhanced_schema_definitions.rb +2 -1
  14. data/lib/active_record/connection_adapters/oracle_enhanced_schema_dumper.rb +24 -9
  15. data/lib/active_record/connection_adapters/oracle_enhanced_version.rb +1 -1
  16. data/lib/{oracle_enhanced.rb → active_record/oracle_enhanced.rb} +0 -2
  17. data/oracle_enhanced.gemspec +7 -4
  18. data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +37 -10
  19. data/spec/active_record/connection_adapters/oracle_enhanced_adapter_structure_dumper_spec.rb +27 -20
  20. data/spec/active_record/connection_adapters/oracle_enhanced_context_index_spec.rb +292 -0
  21. data/spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb +24 -28
  22. data/spec/active_record/connection_adapters/oracle_enhanced_dbms_output_spec.rb +13 -11
  23. data/spec/active_record/connection_adapters/oracle_enhanced_dirty_spec.rb +1 -1
  24. data/spec/active_record/connection_adapters/oracle_enhanced_procedures_spec.rb +70 -68
  25. data/spec/active_record/connection_adapters/oracle_enhanced_schema_dump_spec.rb +14 -1
  26. data/spec/spec_helper.rb +97 -24
  27. metadata +8 -5
@@ -0,0 +1,227 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module OracleEnhancedContextIndex
4
+
5
+ # Define full text index with CONTEXT index type
6
+ def add_context_index(table_name, column_name, options = {})
7
+ self.all_schema_indexes = nil
8
+ column_names = Array(column_name)
9
+ index_name = options[:name] || index_name(table_name, :column => options[:index_column] || column_names,
10
+ # CONEXT index name max length is 25
11
+ :identifier_max_length => 25)
12
+
13
+ quoted_column_name = quote_column_name(options[:index_column] || column_names.first)
14
+ if options[:index_column_trigger_on]
15
+ raise ArgumentError, "Option :index_column should be specified together with :index_column_cource option" \
16
+ unless options[:index_column]
17
+ create_index_column_trigger(table_name, index_name, options[:index_column], options[:index_column_trigger_on])
18
+ end
19
+
20
+ sql = "CREATE INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
21
+ sql << " (#{quoted_column_name})"
22
+ sql << " INDEXTYPE IS CTXSYS.CONTEXT"
23
+ parameters = []
24
+ if column_names.size > 1
25
+ procedure_name = default_datastore_procedure(index_name)
26
+ datastore_name = default_datastore_name(index_name)
27
+ create_datastore_procedure(table_name, procedure_name, column_names, options)
28
+ create_datastore_preference(datastore_name, procedure_name)
29
+ parameters << "DATASTORE #{datastore_name} SECTION GROUP CTXSYS.AUTO_SECTION_GROUP"
30
+ end
31
+ if options[:tablespace]
32
+ storage_name = default_storage_name(index_name)
33
+ create_storage_preference(storage_name, options[:tablespace])
34
+ parameters << "STORAGE #{storage_name}"
35
+ end
36
+ if options[:sync]
37
+ parameters << "SYNC(#{options[:sync]})"
38
+ end
39
+ unless parameters.empty?
40
+ sql << " PARAMETERS ('#{parameters.join(' ')}')"
41
+ end
42
+ execute sql
43
+ end
44
+
45
+ # Drop full text index with CONTEXT index type
46
+ def remove_context_index(table_name, options = {})
47
+ self.all_schema_indexes = nil
48
+ unless Hash === options # if column names passed as argument
49
+ options = {:column => Array(options)}
50
+ end
51
+ index_name = options[:name] || index_name(table_name,
52
+ :column => options[:index_column] || options[:column], :identifier_max_length => 25)
53
+ execute "DROP INDEX #{index_name}"
54
+ drop_ctx_preference(default_datastore_name(index_name))
55
+ drop_ctx_preference(default_storage_name(index_name))
56
+ procedure_name = default_datastore_procedure(index_name)
57
+ execute "DROP PROCEDURE #{quote_table_name(procedure_name)}" rescue nil
58
+ drop_index_column_trigger(index_name)
59
+ end
60
+
61
+ private
62
+
63
+ def create_datastore_procedure(table_name, procedure_name, column_names, options)
64
+ quoted_table_name = quote_table_name(table_name)
65
+ select_queries = column_names.select{|c| c.to_s =~ /^SELECT /i}
66
+ column_names = column_names - select_queries
67
+ keys, selected_columns = parse_select_queries(select_queries)
68
+ quoted_column_names = (column_names+keys).map{|col| quote_column_name(col)}
69
+ execute compress_lines(<<-SQL)
70
+ CREATE OR REPLACE PROCEDURE #{quote_table_name(procedure_name)}
71
+ (p_rowid IN ROWID,
72
+ p_clob IN OUT NOCOPY CLOB) IS
73
+ -- add_context_index_parameters #{(column_names+select_queries).inspect}#{!options.empty? ? ', ' << options.inspect[1..-2] : ''}
74
+ #{
75
+ selected_columns.map do |cols|
76
+ cols.map do |col|
77
+ raise ArgumentError, "Alias #{col} too large, should be 28 or less characters long" unless col.length <= 28
78
+ "l_#{col} VARCHAR2(32767);\n"
79
+ end.join
80
+ end.join
81
+ } BEGIN
82
+ FOR r1 IN (
83
+ SELECT #{quoted_column_names.join(', ')}
84
+ FROM #{quoted_table_name}
85
+ WHERE #{quoted_table_name}.ROWID = p_rowid
86
+ ) LOOP
87
+ #{
88
+ (column_names.map do |col|
89
+ col = col.to_s
90
+ "DBMS_LOB.WRITEAPPEND(p_clob, #{col.length+2}, '<#{col}>');\n" <<
91
+ "DBMS_LOB.WRITEAPPEND(p_clob, LENGTH(r1.#{col}), r1.#{col});\n" <<
92
+ "DBMS_LOB.WRITEAPPEND(p_clob, #{col.length+3}, '</#{col}>');\n"
93
+ end.join) <<
94
+ (selected_columns.zip(select_queries).map do |cols, query|
95
+ (cols.map do |col|
96
+ "l_#{col} := '';\n"
97
+ end.join) <<
98
+ "FOR r2 IN (\n" <<
99
+ query.gsub(/:(\w+)/,"r1.\\1") << "\n) LOOP\n" <<
100
+ (cols.map do |col|
101
+ "l_#{col} := l_#{col} || r2.#{col} || CHR(10);\n"
102
+ end.join) <<
103
+ "END LOOP;\n" <<
104
+ (cols.map do |col|
105
+ col = col.to_s
106
+ "DBMS_LOB.WRITEAPPEND(p_clob, #{col.length+2}, '<#{col}>');\n" <<
107
+ "IF LENGTH(l_#{col}) > 0 THEN\n" <<
108
+ "DBMS_LOB.WRITEAPPEND(p_clob, LENGTH(l_#{col}), l_#{col});\n" <<
109
+ "END IF;\n" <<
110
+ "DBMS_LOB.WRITEAPPEND(p_clob, #{col.length+3}, '</#{col}>');\n"
111
+ end.join)
112
+ end.join)
113
+ }
114
+ END LOOP;
115
+ END;
116
+ SQL
117
+ end
118
+
119
+ def parse_select_queries(select_queries)
120
+ keys = []
121
+ selected_columns = []
122
+ select_queries.each do |query|
123
+ # get primary or foreign keys like :id or :something_id
124
+ keys << (query.scan(/:\w+/).map{|k| k[1..-1].downcase.to_sym})
125
+ select_part = query.scan(/^select\s.*\sfrom/i).first
126
+ selected_columns << select_part.scan(/\sas\s+(\w+)/i).map{|c| c.first}
127
+ end
128
+ [keys.flatten.uniq, selected_columns]
129
+ end
130
+
131
+ def create_datastore_preference(datastore_name, procedure_name)
132
+ drop_ctx_preference(datastore_name)
133
+ execute <<-SQL
134
+ BEGIN
135
+ CTX_DDL.CREATE_PREFERENCE('#{datastore_name}', 'USER_DATASTORE');
136
+ CTX_DDL.SET_ATTRIBUTE('#{datastore_name}', 'PROCEDURE', '#{procedure_name}');
137
+ END;
138
+ SQL
139
+ end
140
+
141
+ def create_storage_preference(storage_name, tablespace)
142
+ drop_ctx_preference(storage_name)
143
+ sql = "BEGIN\nCTX_DDL.CREATE_PREFERENCE('#{storage_name}', 'BASIC_STORAGE');\n"
144
+ ['I_TABLE_CLAUSE', 'K_TABLE_CLAUSE', 'R_TABLE_CLAUSE',
145
+ 'N_TABLE_CLAUSE', 'I_INDEX_CLAUSE', 'P_TABLE_CLAUSE'].each do |clause|
146
+ sql << "CTX_DDL.SET_ATTRIBUTE('#{storage_name}', '#{clause}', 'TABLESPACE #{tablespace}');\n"
147
+ end
148
+ sql << "END;\n"
149
+ execute sql
150
+ end
151
+
152
+ def drop_ctx_preference(preference_name)
153
+ execute "BEGIN CTX_DDL.DROP_PREFERENCE('#{preference_name}'); END;" rescue nil
154
+ end
155
+
156
+ def create_index_column_trigger(table_name, index_name, index_column, index_column_source)
157
+ trigger_name = default_index_column_trigger_name(index_name)
158
+ columns = Array(index_column_source)
159
+ quoted_column_names = columns.map{|col| quote_column_name(col)}.join(', ')
160
+ execute compress_lines(<<-SQL)
161
+ CREATE OR REPLACE TRIGGER #{quote_table_name(trigger_name)}
162
+ BEFORE UPDATE OF #{quoted_column_names} ON #{quote_table_name(table_name)} FOR EACH ROW
163
+ BEGIN
164
+ :new.#{quote_column_name(index_column)} := '1';
165
+ END;
166
+ SQL
167
+ end
168
+
169
+ def drop_index_column_trigger(index_name)
170
+ trigger_name = default_index_column_trigger_name(index_name)
171
+ execute "DROP TRIGGER #{quote_table_name(trigger_name)}" rescue nil
172
+ end
173
+
174
+ def default_datastore_procedure(index_name)
175
+ "#{index_name}_prc"
176
+ end
177
+
178
+ def default_datastore_name(index_name)
179
+ "#{index_name}_dst"
180
+ end
181
+
182
+ def default_storage_name(index_name)
183
+ "#{index_name}_sto"
184
+ end
185
+
186
+ def default_index_column_trigger_name(index_name)
187
+ "#{index_name}_trg"
188
+ end
189
+
190
+ module BaseClassMethods
191
+ # Declare that model table has context index defined.
192
+ # As a result <tt>contains</tt> class scope method is defined.
193
+ def has_context_index
194
+ extend ContextIndexClassMethods
195
+ end
196
+ end
197
+
198
+ module ContextIndexClassMethods
199
+ # Add context index condition.
200
+ case ::ActiveRecord::VERSION::MAJOR
201
+ when 3
202
+ def contains(column, query, options ={})
203
+ score_label = options[:label].to_i || 1
204
+ where("CONTAINS(#{connection.quote_column_name(column)}, ?, #{score_label}) > 0", query).
205
+ order("SCORE(#{score_label}) DESC")
206
+ end
207
+ when 2
208
+ def contains(column, query, options ={})
209
+ score_label = options[:label].to_i || 1
210
+ scoped(:conditions => ["CONTAINS(#{connection.quote_column_name(column)}, ?, #{score_label}) > 0", query],
211
+ :order => "SCORE(#{score_label}) DESC")
212
+ end
213
+ end
214
+ end
215
+
216
+ end
217
+
218
+ end
219
+ end
220
+
221
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.class_eval do
222
+ include ActiveRecord::ConnectionAdapters::OracleEnhancedContextIndex
223
+ end
224
+
225
+ ActiveRecord::Base.class_eval do
226
+ extend ActiveRecord::ConnectionAdapters::OracleEnhancedContextIndex::BaseClassMethods
227
+ end
@@ -53,7 +53,7 @@ if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'ruby' && RUBY_VERSION >= '1.9'
53
53
 
54
54
  rescue LoadError
55
55
  warning_message = "WARNING: Please install unicode_utils gem to support Unicode aware upcase and downcase for String#mb_chars"
56
- if !(Rails.logger.nil?)
56
+ if !Rails.logger.nil?
57
57
  Rails.logger.warn warning_message
58
58
  else
59
59
  STDERR.puts warning_message
@@ -32,7 +32,7 @@ module ActiveRecord #:nodoc:
32
32
  end
33
33
  end
34
34
 
35
- if ActiveRecord::Base.instance_methods.include?('changed?')
35
+ if ActiveRecord::Base.method_defined?(:changed?)
36
36
  ActiveRecord::Base.class_eval do
37
37
  include ActiveRecord::ConnectionAdapters::OracleEnhancedDirty::InstanceMethods
38
38
  end
@@ -154,7 +154,7 @@ module ActiveRecord
154
154
  @active = true
155
155
  rescue NativeException => e
156
156
  @active = false
157
- if e.message =~ /^java\.sql\.SQLException/
157
+ if e.message =~ /^java\.sql\.SQL(Recoverable)?Exception/
158
158
  raise OracleEnhancedConnectionException, e.message
159
159
  else
160
160
  raise
@@ -169,7 +169,7 @@ module ActiveRecord
169
169
  @active = true
170
170
  rescue NativeException => e
171
171
  @active = false
172
- if e.message =~ /^java\.sql\.SQLException/
172
+ if e.message =~ /^java\.sql\.SQL(Recoverable)?Exception/
173
173
  raise OracleEnhancedConnectionException, e.message
174
174
  else
175
175
  raise
@@ -183,7 +183,7 @@ module ActiveRecord
183
183
  begin
184
184
  yield if block_given?
185
185
  rescue NativeException => e
186
- raise unless e.message =~ /^java\.sql\.SQLException: (Closed Connection|Io exception:|No more data to read from socket)/
186
+ raise unless e.message =~ /^java\.sql\.SQL(Recoverable)?Exception: (Closed Connection|Io exception:|No more data to read from socket)/
187
187
  @active = false
188
188
  raise unless should_retry
189
189
  should_retry = false
@@ -138,7 +138,12 @@ module ActiveRecord
138
138
 
139
139
  # Return OCIError error code
140
140
  def error_code(exception)
141
- exception.code
141
+ case exception
142
+ when OCIError
143
+ exception.code
144
+ else
145
+ nil
146
+ end
142
147
  end
143
148
 
144
149
  private
@@ -112,7 +112,7 @@ module ActiveRecord #:nodoc:
112
112
  # check if class has custom create method
113
113
  return create_without_custom_method unless self.class.custom_create_method
114
114
  self.class.connection.log_custom_method("custom create method", "#{self.class.name} Create") do
115
- self.id = self.class.custom_create_method.bind(self).call
115
+ self.id = instance_eval &self.class.custom_create_method
116
116
  end
117
117
  @new_record = false
118
118
  id
@@ -125,7 +125,7 @@ module ActiveRecord #:nodoc:
125
125
  return update_without_custom_method unless self.class.custom_update_method
126
126
  return 0 if attribute_names.empty?
127
127
  self.class.connection.log_custom_method("custom update method with #{self.class.primary_key}=#{self.id}", "#{self.class.name} Update") do
128
- self.class.custom_update_method.bind(self).call
128
+ instance_eval &self.class.custom_update_method
129
129
  end
130
130
  1
131
131
  end
@@ -138,7 +138,7 @@ module ActiveRecord #:nodoc:
138
138
  return destroy_without_custom_method unless self.class.custom_delete_method
139
139
  unless new_record?
140
140
  self.class.connection.log_custom_method("custom delete method with #{self.class.primary_key}=#{self.id}", "#{self.class.name} Destroy") do
141
- self.class.custom_delete_method.bind(self).call
141
+ instance_eval &self.class.custom_delete_method
142
142
  end
143
143
  end
144
144
 
@@ -6,7 +6,8 @@ module ActiveRecord
6
6
  class OracleEnhancedSynonymDefinition < Struct.new(:name, :table_owner, :table_name, :db_link) #:nodoc:
7
7
  end
8
8
 
9
- class OracleEnhancedIndexDefinition < Struct.new(:table, :name, :unique, :tablespace, :columns) #:nodoc:
9
+ class OracleEnhancedIndexDefinition < Struct.new(:table, :name, :unique, :type, :parameters, :statement_parameters,
10
+ :tablespace, :columns) #:nodoc:
10
11
  end
11
12
 
12
13
  module OracleEnhancedSchemaDefinitions #:nodoc:
@@ -13,7 +13,8 @@ module ActiveRecord #:nodoc:
13
13
  private
14
14
 
15
15
  def tables_with_oracle_enhanced(stream)
16
- sorted_tables = @connection.tables.sort
16
+ # do not include materialized views in schema dump - they should be created separately after schema creation
17
+ sorted_tables = (@connection.tables - @connection.materialized_views).sort
17
18
  sorted_tables.each do |tbl|
18
19
  # add table prefix or suffix for schema_migrations
19
20
  next if [ActiveRecord::Migrator.proper_table_name('schema_migrations'), ignore_tables].flatten.any? do |ignored|
@@ -89,14 +90,28 @@ module ActiveRecord #:nodoc:
89
90
  def indexes_with_oracle_enhanced(table, stream)
90
91
  if (indexes = @connection.indexes(table)).any?
91
92
  add_index_statements = indexes.map do |index|
92
- # use table.inspect as it will remove prefix and suffix
93
- statment_parts = [ ('add_index ' + table.inspect) ]
94
- statment_parts << index.columns.inspect
95
- statment_parts << (':name => ' + index.name.inspect)
96
- statment_parts << ':unique => true' if index.unique
97
- statment_parts << ':tablespace => ' + index.tablespace.inspect if index.tablespace
98
-
99
- ' ' + statment_parts.join(', ')
93
+ case index.type
94
+ when nil
95
+ # use table.inspect as it will remove prefix and suffix
96
+ statement_parts = [ ('add_index ' + table.inspect) ]
97
+ statement_parts << index.columns.inspect
98
+ statement_parts << (':name => ' + index.name.inspect)
99
+ statement_parts << ':unique => true' if index.unique
100
+ statement_parts << ':tablespace => ' + index.tablespace.inspect if index.tablespace
101
+ when 'CTXSYS.CONTEXT'
102
+ if index.statement_parameters
103
+ statement_parts = [ ('add_context_index ' + table.inspect) ]
104
+ statement_parts << index.statement_parameters
105
+ else
106
+ statement_parts = [ ('add_context_index ' + table.inspect) ]
107
+ statement_parts << index.columns.inspect
108
+ statement_parts << (':name => ' + index.name.inspect)
109
+ end
110
+ else
111
+ # unrecognized index type
112
+ statement_parts = ["# unrecognized index #{index.name.inspect} with type #{index.type.inspect}"]
113
+ end
114
+ ' ' + statement_parts.join(', ')
100
115
  end
101
116
 
102
117
  stream.puts add_index_statements.sort.join("\n")
@@ -1 +1 @@
1
- ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter::VERSION = '1.2.3'
1
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter::VERSION = File.read(File.dirname(__FILE__)+'/../../../VERSION').chomp
@@ -1,3 +1 @@
1
- #dummy for easier require
2
-
3
1
  require 'active_record/connection_adapters/oracle_enhanced_adapter'
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{oracle_enhanced}
8
- s.version = "1.3.0.pre"
8
+ s.version = "1.3.0.pre2"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Raimonds Simanovskis"]
12
- s.date = %q{2010-03-03}
12
+ s.date = %q{2010-03-18}
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
  }
@@ -29,6 +29,7 @@ This adapter is superset of original ActiveRecord Oracle adapter.
29
29
  "lib/active_record/connection_adapters/oracle_enhanced.rake",
30
30
  "lib/active_record/connection_adapters/oracle_enhanced_adapter.rb",
31
31
  "lib/active_record/connection_adapters/oracle_enhanced_connection.rb",
32
+ "lib/active_record/connection_adapters/oracle_enhanced_context_index.rb",
32
33
  "lib/active_record/connection_adapters/oracle_enhanced_core_ext.rb",
33
34
  "lib/active_record/connection_adapters/oracle_enhanced_cpk.rb",
34
35
  "lib/active_record/connection_adapters/oracle_enhanced_dirty.rb",
@@ -41,11 +42,12 @@ This adapter is superset of original ActiveRecord Oracle adapter.
41
42
  "lib/active_record/connection_adapters/oracle_enhanced_schema_statements_ext.rb",
42
43
  "lib/active_record/connection_adapters/oracle_enhanced_tasks.rb",
43
44
  "lib/active_record/connection_adapters/oracle_enhanced_version.rb",
44
- "lib/oracle_enhanced.rb",
45
+ "lib/active_record/oracle_enhanced.rb",
45
46
  "oracle_enhanced.gemspec",
46
47
  "spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb",
47
48
  "spec/active_record/connection_adapters/oracle_enhanced_adapter_structure_dumper_spec.rb",
48
49
  "spec/active_record/connection_adapters/oracle_enhanced_connection_spec.rb",
50
+ "spec/active_record/connection_adapters/oracle_enhanced_context_index_spec.rb",
49
51
  "spec/active_record/connection_adapters/oracle_enhanced_core_ext_spec.rb",
50
52
  "spec/active_record/connection_adapters/oracle_enhanced_cpk_spec.rb",
51
53
  "spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb",
@@ -58,7 +60,7 @@ This adapter is superset of original ActiveRecord Oracle adapter.
58
60
  "spec/spec.opts",
59
61
  "spec/spec_helper.rb"
60
62
  ]
61
- s.homepage = %q{http://github.com/rsim/oracle-enhanced}
63
+ s.homepage = %q{http://github.com/plukevdh/oracle-enhanced}
62
64
  s.rdoc_options = ["--charset=UTF-8"]
63
65
  s.require_paths = ["lib"]
64
66
  s.rubygems_version = %q{1.3.6}
@@ -67,6 +69,7 @@ This adapter is superset of original ActiveRecord Oracle adapter.
67
69
  "spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb",
68
70
  "spec/active_record/connection_adapters/oracle_enhanced_adapter_structure_dumper_spec.rb",
69
71
  "spec/active_record/connection_adapters/oracle_enhanced_connection_spec.rb",
72
+ "spec/active_record/connection_adapters/oracle_enhanced_context_index_spec.rb",
70
73
  "spec/active_record/connection_adapters/oracle_enhanced_core_ext_spec.rb",
71
74
  "spec/active_record/connection_adapters/oracle_enhanced_cpk_spec.rb",
72
75
  "spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb",
@@ -112,7 +112,7 @@ describe "OracleEnhancedAdapter" do
112
112
  before(:all) do
113
113
  @conn.execute <<-SQL
114
114
  CREATE TABLE test_employees (
115
- id NUMBER,
115
+ id NUMBER PRIMARY KEY,
116
116
  first_name VARCHAR2(20),
117
117
  last_name VARCHAR2(25),
118
118
  email VARCHAR2(25),
@@ -180,7 +180,7 @@ describe "OracleEnhancedAdapter" do
180
180
  @conn.execute "DROP TABLE test_employees" rescue nil
181
181
  @conn.execute <<-SQL
182
182
  CREATE TABLE test_employees (
183
- id NUMBER,
183
+ id NUMBER PRIMARY KEY,
184
184
  first_name VARCHAR2(20),
185
185
  last_name VARCHAR2(25),
186
186
  hire_date DATE
@@ -198,12 +198,15 @@ describe "OracleEnhancedAdapter" do
198
198
  end
199
199
 
200
200
  before(:each) do
201
- @buffer = StringIO.new
202
- log_to @buffer
201
+ set_logger
203
202
  @conn = ActiveRecord::Base.connection
204
203
  @conn.clear_columns_cache
205
204
  end
206
205
 
206
+ after(:each) do
207
+ clear_logger
208
+ end
209
+
207
210
  describe "without column caching" do
208
211
 
209
212
  before(:each) do
@@ -212,14 +215,26 @@ describe "OracleEnhancedAdapter" do
212
215
 
213
216
  it "should get columns from database at first time" do
214
217
  TestEmployee.connection.columns('test_employees').map(&:name).should == @column_names
215
- @buffer.string.should =~ /select .* from all_tab_columns/im
218
+ @logger.logged(:debug).last.should =~ /select .* from all_tab_columns/im
216
219
  end
217
220
 
218
221
  it "should get columns from database at second time" do
219
222
  TestEmployee.connection.columns('test_employees')
220
- @buffer.truncate(0)
223
+ @logger.clear(:debug)
221
224
  TestEmployee.connection.columns('test_employees').map(&:name).should == @column_names
222
- @buffer.string.should =~ /select .* from all_tab_columns/im
225
+ @logger.logged(:debug).last.should =~ /select .* from all_tab_columns/im
226
+ end
227
+
228
+ it "should get primary key from database at first time" do
229
+ TestEmployee.connection.pk_and_sequence_for('test_employees').should == ['id', nil]
230
+ @logger.logged(:debug).last.should =~ /select .* from all_constraints/im
231
+ end
232
+
233
+ it "should get primary key from database at first time" do
234
+ TestEmployee.connection.pk_and_sequence_for('test_employees').should == ['id', nil]
235
+ @logger.clear(:debug)
236
+ TestEmployee.connection.pk_and_sequence_for('test_employees').should == ['id', nil]
237
+ @logger.logged(:debug).last.should =~ /select .* from all_constraints/im
223
238
  end
224
239
 
225
240
  end
@@ -232,14 +247,26 @@ describe "OracleEnhancedAdapter" do
232
247
 
233
248
  it "should get columns from database at first time" do
234
249
  TestEmployee.connection.columns('test_employees').map(&:name).should == @column_names
235
- @buffer.string.should =~ /select .* from all_tab_columns/im
250
+ @logger.logged(:debug).last.should =~ /select .* from all_tab_columns/im
236
251
  end
237
252
 
238
253
  it "should get columns from cache at second time" do
239
254
  TestEmployee.connection.columns('test_employees')
240
- @buffer.truncate(0)
255
+ @logger.clear(:debug)
241
256
  TestEmployee.connection.columns('test_employees').map(&:name).should == @column_names
242
- @buffer.string.should be_blank
257
+ @logger.logged(:debug).last.should be_blank
258
+ end
259
+
260
+ it "should get primary key from database at first time" do
261
+ TestEmployee.connection.pk_and_sequence_for('test_employees').should == ['id', nil]
262
+ @logger.logged(:debug).last.should =~ /select .* from all_constraints/im
263
+ end
264
+
265
+ it "should get primary key from cache at first time" do
266
+ TestEmployee.connection.pk_and_sequence_for('test_employees').should == ['id', nil]
267
+ @logger.clear(:debug)
268
+ TestEmployee.connection.pk_and_sequence_for('test_employees').should == ['id', nil]
269
+ @logger.logged(:debug).last.should be_blank
243
270
  end
244
271
 
245
272
  end
@@ -58,7 +58,7 @@ describe "OracleEnhancedAdapter structure dump" do
58
58
  SQL
59
59
  dump = ActiveRecord::Base.connection.structure_dump_fk_constraints
60
60
  dump.split('\n').length.should == 1
61
- dump.should =~ /ALTER TABLE TEST_POSTS ADD CONSTRAINT fk_test_post_foo FOREIGN KEY \(foo_id\) REFERENCES foos\(id\)/
61
+ dump.should =~ /ALTER TABLE \"?TEST_POSTS\"? ADD CONSTRAINT \"?FK_TEST_POST_FOO\"? FOREIGN KEY \(\"?FOO_ID\"?\) REFERENCES \"?FOOS\"?\(\"?ID\"?\)/i
62
62
  end
63
63
 
64
64
  it "should not error when no foreign keys are present" do
@@ -78,7 +78,7 @@ describe "OracleEnhancedAdapter structure dump" do
78
78
  END;
79
79
  SQL
80
80
  dump = ActiveRecord::Base.connection.structure_dump_db_stored_code.gsub(/\n|\s+/,' ')
81
- dump.should =~ /create or replace TRIGGER TEST_POST_TRIGGER/
81
+ dump.should =~ /CREATE OR REPLACE TRIGGER TEST_POST_TRIGGER/
82
82
  end
83
83
 
84
84
  it "should dump types" do
@@ -86,7 +86,7 @@ describe "OracleEnhancedAdapter structure dump" do
86
86
  create or replace TYPE TEST_TYPE AS TABLE OF VARCHAR2(10);
87
87
  SQL
88
88
  dump = ActiveRecord::Base.connection.structure_dump_db_stored_code.gsub(/\n|\s+/,' ')
89
- dump.should =~ /create or replace TYPE TEST_TYPE/
89
+ dump.should =~ /CREATE OR REPLACE TYPE TEST_TYPE/
90
90
  end
91
91
 
92
92
  it "should dump virtual columns" do
@@ -99,7 +99,7 @@ describe "OracleEnhancedAdapter structure dump" do
99
99
  )
100
100
  SQL
101
101
  dump = ActiveRecord::Base.connection.structure_dump
102
- dump.should =~ /id_plus number GENERATED ALWAYS AS \(ID\+2\) VIRTUAL/
102
+ dump.should =~ /ID_PLUS NUMBER GENERATED ALWAYS AS \(ID\+2\) VIRTUAL/
103
103
  end
104
104
 
105
105
  it "should dump unique keys" do
@@ -124,9 +124,9 @@ describe "OracleEnhancedAdapter structure dump" do
124
124
  SQL
125
125
 
126
126
  dump = ActiveRecord::Base.connection.structure_dump
127
- dump.should =~ /create unique index ix_test_posts_foo_id on test_posts \(foo_id\)/i
128
- dump.should =~ /create index ix_test_posts_foo on test_posts \(foo\)/i
129
- dump.should_not =~ /create unique index uk_test_posts_/i
127
+ dump.should =~ /CREATE UNIQUE INDEX "?IX_TEST_POSTS_FOO_ID"? ON "?TEST_POSTS"? \("?FOO_ID"?\)/i
128
+ dump.should =~ /CREATE INDEX "?IX_TEST_POSTS_FOO\"? ON "?TEST_POSTS"? \("?FOO"?\)/i
129
+ dump.should_not =~ /CREATE UNIQUE INDEX "?UK_TEST_POSTS_/i
130
130
  end
131
131
  end
132
132
  describe "temporary tables" do
@@ -138,7 +138,7 @@ describe "OracleEnhancedAdapter structure dump" do
138
138
  t.integer :post_id
139
139
  end
140
140
  dump = ActiveRecord::Base.connection.structure_dump
141
- dump.should =~ /create global temporary table test_comments/i
141
+ dump.should =~ /CREATE GLOBAL TEMPORARY TABLE "?TEST_COMMENTS"?/i
142
142
  end
143
143
  end
144
144
 
@@ -173,8 +173,8 @@ describe "OracleEnhancedAdapter structure dump" do
173
173
  end
174
174
  it "should dump drop sql for just temp tables" do
175
175
  dump = @conn.temp_table_drop
176
- dump.should =~ /drop table temp_tbl/i
177
- dump.should_not =~ /drop table not_temp_tbl/i
176
+ dump.should =~ /DROP TABLE "TEMP_TBL"/
177
+ dump.should_not =~ /DROP TABLE "?NOT_TEMP_TBL"?/i
178
178
  end
179
179
  after(:each) do
180
180
  @conn.drop_table :temp_tbl
@@ -194,6 +194,10 @@ describe "OracleEnhancedAdapter structure dump" do
194
194
  @conn.execute <<-SQL
195
195
  create or replace view full_drop_test_view (foo) as select id as "foo" from full_drop_test
196
196
  SQL
197
+ #materialized view
198
+ @conn.execute <<-SQL
199
+ create materialized view full_drop_test_mview (foo) as select id as "foo" from full_drop_test
200
+ SQL
197
201
  #package
198
202
  @conn.execute <<-SQL
199
203
  create or replace package full_drop_test_package as
@@ -241,6 +245,7 @@ describe "OracleEnhancedAdapter structure dump" do
241
245
  @conn.drop_table :full_drop_test
242
246
  @conn.drop_table :full_drop_test_temp
243
247
  @conn.execute "DROP VIEW FULL_DROP_TEST_VIEW" rescue nil
248
+ @conn.execute "DROP MATERIALIZED VIEW FULL_DROP_TEST_MVIEW" rescue nil
244
249
  @conn.execute "DROP SYNONYM FULL_DROP_TEST_SYNONYM" rescue nil
245
250
  @conn.execute "DROP PACKAGE FULL_DROP_TEST_PACKAGE" rescue nil
246
251
  @conn.execute "DROP FUNCTION FULL_DROP_TEST_FUNCTION" rescue nil
@@ -249,19 +254,21 @@ describe "OracleEnhancedAdapter structure dump" do
249
254
  end
250
255
  it "should contain correct sql" do
251
256
  drop = @conn.full_drop
252
- drop.should =~ /drop table full_drop_test cascade constraints/i
253
- drop.should =~ /drop sequence full_drop_test_seq/i
254
- drop.should =~ /drop view "full_drop_test_view"/i
255
- drop.should =~ /drop package full_drop_test_package/i
256
- drop.should =~ /drop function full_drop_test_function/i
257
- drop.should =~ /drop procedure full_drop_test_procedure/i
258
- drop.should =~ /drop synonym "full_drop_test_synonym"/i
259
- drop.should =~ /drop type "full_drop_test_type"/i
257
+ drop.should =~ /DROP TABLE "FULL_DROP_TEST" CASCADE CONSTRAINTS/
258
+ drop.should =~ /DROP SEQUENCE "FULL_DROP_TEST_SEQ"/
259
+ drop.should =~ /DROP VIEW "FULL_DROP_TEST_VIEW"/
260
+ drop.should_not =~ /DROP TABLE "?FULL_DROP_TEST_MVIEW"?/i
261
+ drop.should =~ /DROP MATERIALIZED VIEW "FULL_DROP_TEST_MVIEW"/
262
+ drop.should =~ /DROP PACKAGE "FULL_DROP_TEST_PACKAGE"/
263
+ drop.should =~ /DROP FUNCTION "FULL_DROP_TEST_FUNCTION"/
264
+ drop.should =~ /DROP PROCEDURE "FULL_DROP_TEST_PROCEDURE"/
265
+ drop.should =~ /DROP SYNONYM "FULL_DROP_TEST_SYNONYM"/
266
+ drop.should =~ /DROP TYPE "FULL_DROP_TEST_TYPE"/
260
267
  end
261
268
  it "should not drop tables when preserve_tables is true" do
262
269
  drop = @conn.full_drop(true)
263
- drop.should =~ /drop table full_drop_test_temp/i
264
- drop.should_not =~ /drop table full_drop_test cascade constraints/i
270
+ drop.should =~ /DROP TABLE "FULL_DROP_TEST_TEMP"/
271
+ drop.should_not =~ /DROP TABLE "?FULL_DROP_TEST"? CASCADE CONSTRAINTS/i
265
272
  end
266
273
  end
267
274
  end