rsim-activerecord-oracle_enhanced-adapter 1.2.0.2 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,7 +1,13 @@
1
- == master development branch
1
+ == 1.2.1 2009-06-07
2
2
 
3
+ * Enhancements
4
+ * caching of table indexes query which makes schema dump much faster
3
5
  * Bug fixes:
4
6
  * return Date (and not DateTime) values for :date column value before year 1970
7
+ * fixed after_create/update/destroy callbacks with plsql custom methods
8
+ * fixed creation of large integers in JRuby
9
+ * Made test tasks respect RAILS_ENV
10
+ * fixed support for composite primary keys for tables with LOBs
5
11
 
6
12
  == 1.2.0 2009-03-22
7
13
 
data/Manifest.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  History.txt
2
2
  License.txt
3
- README.txt
3
+ README.rdoc
4
4
  lib/active_record/connection_adapters/emulation/oracle_adapter.rb
5
5
  lib/active_record/connection_adapters/oracle_enhanced.rake
6
6
  lib/active_record/connection_adapters/oracle_enhanced_adapter.rb
@@ -29,6 +29,17 @@ Bugs and enhancement requests can be reported at http://rsim.lighthouseapp.com/p
29
29
 
30
30
  * sudo gem install activerecord-oracle_enhanced-adapter
31
31
 
32
+ == CONTRIBUTORS:
33
+
34
+ * Raimonds Simanovskis
35
+ * Jorge Dias
36
+ * James Wylder
37
+ * Rob Christie
38
+ * Nate Wieger
39
+ * Edgars Beigarts
40
+ * Lachlan Laycock
41
+ * toddwf
42
+
32
43
  == LICENSE:
33
44
 
34
45
  (The MIT License)
@@ -24,17 +24,19 @@ namespace :db do
24
24
  end
25
25
 
26
26
  namespace :test do
27
- redefine_task :clone_structure => [ "db:structure:dump", "db:test:purge" ] do
27
+ redefine_task :clone_structure => [ :rails_env, "db:structure:dump", "db:test:purge" ] do
28
+ env = RAILS_ENV ||= 'test'
28
29
  abcs = ActiveRecord::Base.configurations
29
- ActiveRecord::Base.establish_connection(:test)
30
+ ActiveRecord::Base.establish_connection(env.to_sym)
30
31
  IO.readlines("db/#{RAILS_ENV}_structure.sql").join.split("\n\n").each do |ddl|
31
32
  ActiveRecord::Base.connection.execute(ddl.chop)
32
33
  end
33
34
  end
34
35
 
35
- redefine_task :purge => :environment do
36
+ redefine_task :purge => [:rails_env, :environment] do
37
+ env = RAILS_ENV ||= 'test'
36
38
  abcs = ActiveRecord::Base.configurations
37
- ActiveRecord::Base.establish_connection(:test)
39
+ ActiveRecord::Base.establish_connection(env.to_sym)
38
40
  ActiveRecord::Base.connection.structure_drop.split("\n\n").each do |ddl|
39
41
  ActiveRecord::Base.connection.execute(ddl.chop)
40
42
  end
@@ -518,15 +518,26 @@ module ActiveRecord
518
518
 
519
519
  # Writes LOB values from attributes, as indicated by the LOB columns of klass.
520
520
  def write_lobs(table_name, klass, attributes)
521
- id = quote(attributes[klass.primary_key])
521
+ # is class with composite primary key>
522
+ is_with_cpk = klass.respond_to?(:composite?) && klass.composite?
523
+ if is_with_cpk
524
+ id = klass.primary_key.map {|pk| attributes[pk.to_s] }
525
+ else
526
+ id = quote(attributes[klass.primary_key])
527
+ end
522
528
  klass.columns.select { |col| col.sql_type =~ /LOB$/i }.each do |col|
523
529
  value = attributes[col.name]
524
530
  # RSI: changed sequence of next two lines - should check if value is nil before converting to yaml
525
531
  next if value.nil? || (value == '')
526
532
  value = value.to_yaml if col.text? && klass.serialized_attributes[col.name]
527
533
  uncached do
528
- lob = select_one("SELECT #{col.name} FROM #{table_name} WHERE #{klass.primary_key} = #{id} FOR UPDATE",
534
+ if is_with_cpk
535
+ lob = select_one("SELECT #{col.name} FROM #{table_name} WHERE #{klass.composite_where_clause(id)} FOR UPDATE",
536
+ 'Writable Large Object')[col.name]
537
+ else
538
+ lob = select_one("SELECT #{col.name} FROM #{table_name} WHERE #{klass.primary_key} = #{id} FOR UPDATE",
529
539
  'Writable Large Object')[col.name]
540
+ end
530
541
  @connection.write_lob(lob, value, col.type == :binary)
531
542
  end
532
543
  end
@@ -562,33 +573,42 @@ module ActiveRecord
562
573
  select_all("select decode(table_name,upper(table_name),lower(table_name),table_name) name from all_tables where owner = sys_context('userenv','session_user')").map {|t| t['name']}
563
574
  end
564
575
 
565
- def indexes(table_name, name = nil) #:nodoc:
566
- (owner, table_name) = @connection.describe(table_name)
567
- result = select_all(<<-SQL, name)
568
- SELECT lower(i.index_name) as index_name, i.uniqueness, lower(c.column_name) as column_name
569
- FROM all_indexes i, all_ind_columns c
570
- WHERE i.table_name = '#{table_name}'
571
- AND i.owner = '#{owner}'
572
- AND i.table_owner = '#{owner}'
573
- AND c.index_name = i.index_name
574
- AND c.index_owner = i.owner
575
- AND NOT EXISTS (SELECT uc.index_name FROM all_constraints uc WHERE uc.index_name = i.index_name AND uc.owner = i.owner AND uc.constraint_type = 'P')
576
- ORDER BY i.index_name, c.column_position
577
- SQL
578
-
579
- current_index = nil
580
- indexes = []
576
+ cattr_accessor :all_schema_indexes
581
577
 
582
- result.each do |row|
583
- if current_index != row['index_name']
584
- indexes << IndexDefinition.new(table_name.downcase, row['index_name'], row['uniqueness'] == "UNIQUE", [])
585
- current_index = row['index_name']
578
+ # This method selects all indexes at once, and caches them in a class variable.
579
+ # Subsequent index calls get them from the variable, without going to the DB.
580
+ def indexes(table_name, name = nil)
581
+ (owner, table_name) = @connection.describe(table_name)
582
+ unless all_schema_indexes
583
+ result = select_all(<<-SQL)
584
+ SELECT lower(i.table_name) as table_name, lower(i.index_name) as index_name, i.uniqueness, lower(c.column_name) as column_name
585
+ FROM all_indexes i, all_ind_columns c
586
+ WHERE i.owner = '#{owner}'
587
+ AND i.table_owner = '#{owner}'
588
+ AND c.index_name = i.index_name
589
+ AND c.index_owner = i.owner
590
+ AND NOT EXISTS (SELECT uc.index_name FROM all_constraints uc WHERE uc.index_name = i.index_name AND uc.owner = i.owner AND uc.constraint_type = 'P')
591
+ ORDER BY i.index_name, c.column_position
592
+ SQL
593
+
594
+ current_index = nil
595
+ self.all_schema_indexes = []
596
+
597
+ result.each do |row|
598
+ # have to keep track of indexes because above query returns dups
599
+ # there is probably a better query we could figure out
600
+ if current_index != row['index_name']
601
+ self.all_schema_indexes << ::ActiveRecord::ConnectionAdapters::IndexDefinition.new(row['table_name'], row['index_name'], row['uniqueness'] == "UNIQUE", [])
602
+ current_index = row['index_name']
603
+ end
604
+
605
+ self.all_schema_indexes.last.columns << row['column_name']
586
606
  end
587
-
588
- indexes.last.columns << row['column_name']
589
607
  end
590
-
591
- indexes
608
+
609
+ # Return the indexes just for the requested table, since AR is structured that way
610
+ table_name = table_name.downcase
611
+ all_schema_indexes.select{|i| i.table == table_name}
592
612
  end
593
613
 
594
614
  # RSI: set ignored columns for table
@@ -726,7 +746,15 @@ module ActiveRecord
726
746
  execute "DROP SEQUENCE #{seq_name}" rescue nil
727
747
  end
728
748
 
749
+ # clear cached indexes when adding new index
750
+ def add_index(table_name, column_name, options = {})
751
+ self.all_schema_indexes = nil
752
+ super
753
+ end
754
+
755
+ # clear cached indexes when removing index
729
756
  def remove_index(table_name, options = {}) #:nodoc:
757
+ self.all_schema_indexes = nil
730
758
  execute "DROP INDEX #{index_name(table_name, options)}"
731
759
  end
732
760
 
@@ -72,6 +72,9 @@ module ActiveRecord
72
72
  # Set session time zone to current time zone
73
73
  @raw_connection.setSessionTimeZone(java.util.TimeZone.default.getID)
74
74
 
75
+ # Set default number of rows to prefetch
76
+ # @raw_connection.setDefaultRowPrefetch(prefetch_rows) if prefetch_rows
77
+
75
78
  # default schema owner
76
79
  @owner = username.upcase
77
80
 
@@ -156,7 +159,7 @@ module ActiveRecord
156
159
  end
157
160
 
158
161
  def exec_no_retry(sql)
159
- cs = prepare_call(sql)
162
+ cs = @raw_connection.prepareCall(sql)
160
163
  case sql
161
164
  when /\A\s*UPDATE/i, /\A\s*INSERT/i, /\A\s*DELETE/i
162
165
  cs.executeUpdate
@@ -175,59 +178,35 @@ module ActiveRecord
175
178
  end
176
179
 
177
180
  def select_no_retry(sql, name = nil, return_column_names = false)
178
- stmt = prepare_statement(sql)
181
+ stmt = @raw_connection.prepareStatement(sql)
179
182
  rset = stmt.executeQuery
183
+
184
+ # Reuse the same hash for all rows
185
+ column_hash = {}
186
+
180
187
  metadata = rset.getMetaData
181
188
  column_count = metadata.getColumnCount
182
- cols = (1..column_count).map do |i|
183
- oracle_downcase(metadata.getColumnName(i))
184
- end
185
- col_types = (1..column_count).map do |i|
186
- metadata.getColumnTypeName(i)
189
+
190
+ cols_types_index = (1..column_count).map do |i|
191
+ col_name = oracle_downcase(metadata.getColumnName(i))
192
+ next if col_name == 'raw_rnum_'
193
+ column_hash[col_name] = nil
194
+ [col_name, metadata.getColumnTypeName(i).to_sym, i]
187
195
  end
196
+ cols_types_index.delete(nil)
188
197
 
189
198
  rows = []
190
-
199
+ get_lob_value = !(name == 'Writable Large Object')
200
+
191
201
  while rset.next
192
- hash = Hash.new
193
-
194
- cols.each_with_index do |col, i0|
195
- i = i0 + 1
196
- hash[col] =
197
- case column_type = col_types[i0]
198
- when /CLOB/
199
- name == 'Writable Large Object' ? rset.getClob(i) : get_ruby_value_from_result_set(rset, i, column_type)
200
- when /BLOB/
201
- name == 'Writable Large Object' ? rset.getBlob(i) : get_ruby_value_from_result_set(rset, i, column_type)
202
- when 'DATE'
203
- t = get_ruby_value_from_result_set(rset, i, column_type)
204
- # RSI: added emulate_dates_by_column_name functionality
205
- # if emulate_dates_by_column_name && self.class.is_date_column?(col)
206
- # d.to_date
207
- # elsif
208
- if t && OracleEnhancedAdapter.emulate_dates && (t.hour == 0 && t.min == 0 && t.sec == 0)
209
- t.to_date
210
- else
211
- # JRuby Time supports time before year 1900 therefore now need to fall back to DateTime
212
- t
213
- end
214
- # RSI: added emulate_integers_by_column_name functionality
215
- when "NUMBER"
216
- n = get_ruby_value_from_result_set(rset, i, column_type)
217
- if n && n.is_a?(Float) && OracleEnhancedAdapter.emulate_integers_by_column_name && OracleEnhancedAdapter.is_integer_column?(col)
218
- n.to_i
219
- else
220
- n
221
- end
222
- else
223
- get_ruby_value_from_result_set(rset, i, column_type)
224
- end unless col == 'raw_rnum_'
202
+ hash = column_hash.dup
203
+ cols_types_index.each do |col, column_type, i|
204
+ hash[col] = get_ruby_value_from_result_set(rset, i, column_type, get_lob_value)
225
205
  end
226
-
227
206
  rows << hash
228
207
  end
229
208
 
230
- return_column_names ? [rows, cols] : rows
209
+ return_column_names ? [rows, cols_types_index.map(&:first)] : rows
231
210
  ensure
232
211
  rset.close rescue nil
233
212
  stmt.close rescue nil
@@ -284,50 +263,62 @@ module ActiveRecord
284
263
 
285
264
  private
286
265
 
287
- def prepare_statement(sql)
288
- @raw_connection.prepareStatement(sql)
289
- end
266
+ # def prepare_statement(sql)
267
+ # @raw_connection.prepareStatement(sql)
268
+ # end
290
269
 
291
- def prepare_call(sql, *bindvars)
292
- @raw_connection.prepareCall(sql)
293
- end
270
+ # def prepare_call(sql, *bindvars)
271
+ # @raw_connection.prepareCall(sql)
272
+ # end
294
273
 
295
- def get_ruby_value_from_result_set(rset, i, type_name)
274
+ def get_ruby_value_from_result_set(rset, i, type_name, get_lob_value = true)
296
275
  case type_name
297
- when "CHAR", "VARCHAR2", "LONG"
298
- rset.getString(i)
299
- when "CLOB"
300
- ora_value_to_ruby_value(rset.getClob(i))
301
- when "BLOB"
302
- ora_value_to_ruby_value(rset.getBlob(i))
303
- when "NUMBER"
304
- d = rset.getBigDecimal(i)
276
+ when :NUMBER
277
+ # d = rset.getBigDecimal(i)
278
+ # if d.nil?
279
+ # nil
280
+ # elsif d.scale == 0
281
+ # d.toBigInteger+0
282
+ # else
283
+ # # Is there better way how to convert Java BigDecimal to Ruby BigDecimal?
284
+ # d.toString.to_d
285
+ # end
286
+ d = rset.getNUMBER(i)
305
287
  if d.nil?
306
288
  nil
307
- elsif d.scale == 0
308
- d.toBigInteger+0
289
+ elsif d.isInt
290
+ Integer(d.stringValue)
309
291
  else
310
- # Is there better way how to convert Java BigDecimal to Ruby BigDecimal?
311
- d.toString.to_d
292
+ BigDecimal.new(d.stringValue)
312
293
  end
313
- when "DATE"
294
+ when :VARCHAR2, :CHAR, :LONG
295
+ rset.getString(i)
296
+ when :DATE
314
297
  if dt = rset.getDATE(i)
315
298
  d = dt.dateValue
316
299
  t = dt.timeValue
317
- Time.send(Base.default_timezone, d.year + 1900, d.month + 1, d.date, t.hours, t.minutes, t.seconds)
300
+ if OracleEnhancedAdapter.emulate_dates && t.hours == 0 && t.minutes == 0 && t.seconds == 0
301
+ Date.new(d.year + 1900, d.month + 1, d.date)
302
+ else
303
+ Time.send(Base.default_timezone, d.year + 1900, d.month + 1, d.date, t.hours, t.minutes, t.seconds)
304
+ end
318
305
  else
319
306
  nil
320
307
  end
321
- when /^TIMESTAMP/
308
+ when :TIMESTAMP, :TIMESTAMPTZ, :TIMESTAMPLTZ
322
309
  ts = rset.getTimestamp(i)
323
310
  ts && Time.send(Base.default_timezone, ts.year + 1900, ts.month + 1, ts.date, ts.hours, ts.minutes, ts.seconds,
324
311
  ts.nanos / 1000)
312
+ when :CLOB
313
+ get_lob_value ? lob_to_ruby_value(rset.getClob(i)) : rset.getClob(i)
314
+ when :BLOB
315
+ get_lob_value ? lob_to_ruby_value(rset.getBlob(i)) : rset.getBlob(i)
325
316
  else
326
317
  nil
327
318
  end
328
319
  end
329
320
 
330
- def ora_value_to_ruby_value(val)
321
+ def lob_to_ruby_value(val)
331
322
  case val
332
323
  when ::Java::OracleSql::CLOB
333
324
  if val.isEmptyLob
@@ -341,8 +332,6 @@ module ActiveRecord
341
332
  else
342
333
  String.from_java_bytes(val.getBytes(1, val.length))
343
334
  end
344
- else
345
- val
346
335
  end
347
336
  end
348
337
 
@@ -84,70 +84,75 @@ module ActiveRecord
84
84
 
85
85
  def select(sql, name = nil, return_column_names = false)
86
86
  cursor = @raw_connection.exec(sql)
87
- cols = cursor.get_col_names.map { |x| oracle_downcase(x) }
87
+ cols = []
88
+ # Ignore raw_rnum_ which is used to simulate LIMIT and OFFSET
89
+ cursor.get_col_names.each do |col_name|
90
+ col_name = oracle_downcase(col_name)
91
+ cols << col_name unless col_name == 'raw_rnum_'
92
+ end
93
+ # Reuse the same hash for all rows
94
+ column_hash = {}
95
+ cols.each {|c| column_hash[c] = nil}
88
96
  rows = []
97
+ get_lob_value = !(name == 'Writable Large Object')
89
98
 
90
99
  while row = cursor.fetch
91
- hash = Hash.new
100
+ hash = column_hash.dup
92
101
 
93
102
  cols.each_with_index do |col, i|
94
103
  hash[col] =
95
- case row[i]
104
+ case v = row[i]
105
+ # RSI: added emulate_integers_by_column_name functionality
106
+ when Float
107
+ # if OracleEnhancedAdapter.emulate_integers_by_column_name && OracleEnhancedAdapter.is_integer_column?(col)
108
+ # v.to_i
109
+ # else
110
+ # v
111
+ # end
112
+ v == (v_to_i = v.to_i) ? v_to_i : v
113
+ # ruby-oci8 2.0 returns OraNumber - convert it to Integer or BigDecimal
114
+ when OraNumber
115
+ v == (v_to_i = v.to_i) ? v_to_i : BigDecimal.new(v.to_s)
116
+ when String
117
+ v
96
118
  when OCI8::LOB
97
- if name == 'Writable Large Object'
98
- row[i]
99
- else
100
- data = row[i].read
119
+ if get_lob_value
120
+ data = v.read
101
121
  # In Ruby 1.9.1 always change encoding to ASCII-8BIT for binaries
102
- data.force_encoding('ASCII-8BIT') if data.respond_to?(:force_encoding) && row[i].is_a?(OCI8::BLOB)
122
+ data.force_encoding('ASCII-8BIT') if data.respond_to?(:force_encoding) && v.is_a?(OCI8::BLOB)
103
123
  data
124
+ else
125
+ v
104
126
  end
105
127
  # ruby-oci8 1.0 returns OraDate
106
128
  when OraDate
107
- d = row[i]
108
129
  # RSI: added emulate_dates_by_column_name functionality
109
- # if emulate_dates_by_column_name && self.class.is_date_column?(col)
110
- # d.to_date
111
- # elsif
112
- if OracleEnhancedAdapter.emulate_dates && (d.hour == 0 && d.minute == 0 && d.second == 0)
113
- d.to_date
130
+ if OracleEnhancedAdapter.emulate_dates && (v.hour == 0 && v.minute == 0 && v.second == 0)
131
+ v.to_date
114
132
  else
115
133
  # code from Time.time_with_datetime_fallback
116
134
  begin
117
- Time.send(Base.default_timezone, d.year, d.month, d.day, d.hour, d.minute, d.second)
135
+ Time.send(Base.default_timezone, v.year, v.month, v.day, v.hour, v.minute, v.second)
118
136
  rescue
119
137
  offset = Base.default_timezone.to_sym == :local ? ::DateTime.local_offset : 0
120
- ::DateTime.civil(d.year, d.month, d.day, d.hour, d.minute, d.second, offset)
138
+ ::DateTime.civil(v.year, v.month, v.day, v.hour, v.minute, v.second, offset)
121
139
  end
122
140
  end
123
141
  # ruby-oci8 2.0 returns Time or DateTime
124
142
  when Time, DateTime
125
- d = row[i]
126
- if OracleEnhancedAdapter.emulate_dates && (d.hour == 0 && d.min == 0 && d.sec == 0)
127
- d.to_date
143
+ if OracleEnhancedAdapter.emulate_dates && (v.hour == 0 && v.min == 0 && v.sec == 0)
144
+ v.to_date
128
145
  else
129
146
  # recreate Time or DateTime using Base.default_timezone
130
147
  begin
131
- Time.send(Base.default_timezone, d.year, d.month, d.day, d.hour, d.min, d.sec)
148
+ Time.send(Base.default_timezone, v.year, v.month, v.day, v.hour, v.min, v.sec)
132
149
  rescue
133
150
  offset = Base.default_timezone.to_sym == :local ? ::DateTime.local_offset : 0
134
- ::DateTime.civil(d.year, d.month, d.day, d.hour, d.min, d.sec, offset)
151
+ ::DateTime.civil(v.year, v.month, v.day, v.hour, v.min, v.sec, offset)
135
152
  end
136
153
  end
137
- # RSI: added emulate_integers_by_column_name functionality
138
- when Float
139
- n = row[i]
140
- if OracleEnhancedAdapter.emulate_integers_by_column_name && OracleEnhancedAdapter.is_integer_column?(col)
141
- n.to_i
142
- else
143
- n
144
- end
145
- # ruby-oci8 2.0 returns OraNumber - convert it to Integer or BigDecimal
146
- when OraNumber
147
- n = row[i]
148
- n == (n_to_i = n.to_i) ? n_to_i : BigDecimal.new(n.to_s)
149
- else row[i]
150
- end unless col == 'raw_rnum_'
154
+ else v
155
+ end
151
156
  end
152
157
 
153
158
  rows << hash
@@ -38,17 +38,30 @@ module ActiveRecord #:nodoc:
38
38
  module InstanceMethods
39
39
  def self.included(base)
40
40
  base.instance_eval do
41
- alias_method_chain :create, :custom_method
41
+ if private_instance_methods.include?('create_without_callbacks') || private_instance_methods.include?(:create_without_callbacks)
42
+ alias_method :create_without_custom_method, :create_without_callbacks
43
+ alias_method :create_without_callbacks, :create_with_custom_method
44
+ else
45
+ alias_method_chain :create, :custom_method
46
+ end
42
47
  # insert after dirty checking in Rails 2.1
43
48
  # in Ruby 1.9 methods names are returned as symbols
44
49
  if private_instance_methods.include?('update_without_dirty') || private_instance_methods.include?(:update_without_dirty)
45
50
  alias_method :update_without_custom_method, :update_without_dirty
46
51
  alias_method :update_without_dirty, :update_with_custom_method
52
+ elsif private_instance_methods.include?('update_without_callbacks') || private_instance_methods.include?(:update_without_callbacks)
53
+ alias_method :update_without_custom_method, :update_without_callbacks
54
+ alias_method :update_without_callbacks, :update_with_custom_method
47
55
  else
48
56
  alias_method_chain :update, :custom_method
49
57
  end
50
58
  private :create, :update
51
- alias_method_chain :destroy, :custom_method
59
+ if public_instance_methods.include?('destroy_without_callbacks') || public_instance_methods.include?(:destroy_without_callbacks)
60
+ alias_method :destroy_without_custom_method, :destroy_without_callbacks
61
+ alias_method :destroy_without_callbacks, :destroy_with_custom_method
62
+ else
63
+ alias_method_chain :destroy, :custom_method
64
+ end
52
65
  public :destroy
53
66
  end
54
67
  end
@@ -1,7 +1,7 @@
1
1
  module ActiveRecord #:nodoc:
2
2
  module ConnectionAdapters #:nodoc:
3
3
  module OracleEnhancedVersion #:nodoc:
4
- VERSION = '1.2.0'
4
+ VERSION = '1.2.1'
5
5
  end
6
6
  end
7
7
  end
@@ -2,15 +2,15 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{activerecord-oracle_enhanced-adapter}
5
- s.version = "1.2.0.2"
5
+ s.version = "1.2.1"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Raimonds Simanovskis"]
9
- s.date = %q{2009-04-07}
9
+ s.date = %q{2009-06-07}
10
10
  s.description = %q{Oracle enhanced adapter for ActiveRecord}
11
11
  s.email = ["raimonds.simanovskis@gmail.com"]
12
- s.extra_rdoc_files = ["History.txt", "License.txt", "Manifest.txt", "README.txt"]
13
- s.files = ["History.txt", "License.txt", "Manifest.txt", "README.txt",
12
+ s.extra_rdoc_files = ["History.txt", "License.txt", "Manifest.txt", "README.rdoc"]
13
+ s.files = ["History.txt", "License.txt", "Manifest.txt", "README.rdoc",
14
14
  "lib/active_record/connection_adapters/emulation/oracle_adapter.rb",
15
15
  "lib/active_record/connection_adapters/oracle_enhanced.rake",
16
16
  "lib/active_record/connection_adapters/oracle_enhanced_adapter.rb",
@@ -3,35 +3,101 @@ require File.dirname(__FILE__) + '/../../spec_helper.rb'
3
3
  describe "OracleEnhancedAdapter composite_primary_keys support" do
4
4
 
5
5
  before(:all) do
6
- if defined?(ActiveRecord::ConnectionAdapters::OracleAdapter)
7
- @old_oracle_adapter = ActiveRecord::ConnectionAdapters::OracleAdapter
8
- ActiveRecord::ConnectionAdapters.send(:remove_const, :OracleAdapter)
6
+ if defined?(::ActiveRecord::ConnectionAdapters::OracleAdapter)
7
+ @old_oracle_adapter = ::ActiveRecord::ConnectionAdapters::OracleAdapter
8
+ ::ActiveRecord::ConnectionAdapters.send(:remove_const, :OracleAdapter)
9
9
  end
10
10
  ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
11
- require 'composite_primary_keys'
12
- class ::JobHistory < ActiveRecord::Base
13
- set_table_name "job_history"
14
- set_primary_keys :employee_id, :start_date
11
+ if $cpk_oracle_adapter
12
+ ::ActiveRecord::ConnectionAdapters::OracleAdapter = $cpk_oracle_adapter
13
+ $cpk_oracle_adapter = nil
15
14
  end
15
+ require 'composite_primary_keys'
16
16
  end
17
17
 
18
18
  after(:all) do
19
- Object.send(:remove_const, 'CompositePrimaryKeys') if defined?(CompositePrimaryKeys)
20
- Object.send(:remove_const, 'JobHistory') if defined?(JobHistory)
19
+ # Object.send(:remove_const, 'CompositePrimaryKeys') if defined?(CompositePrimaryKeys)
20
+ if defined?(::ActiveRecord::ConnectionAdapters::OracleAdapter)
21
+ $cpk_oracle_adapter = ::ActiveRecord::ConnectionAdapters::OracleAdapter
22
+ ::ActiveRecord::ConnectionAdapters.send(:remove_const, :OracleAdapter)
23
+ end
21
24
  if @old_oracle_adapter
22
- ActiveRecord::ConnectionAdapters.send(:remove_const, :OracleAdapter)
23
- ActiveRecord::ConnectionAdapters::OracleAdapter = @old_oracle_adapter
25
+ ::ActiveRecord::ConnectionAdapters::OracleAdapter = @old_oracle_adapter
26
+ @old_oracle_adapter = nil
24
27
  end
25
28
  end
26
29
 
27
- it "should tell ActiveRecord that count distinct is not supported" do
28
- ActiveRecord::Base.connection.supports_count_distinct?.should be_false
29
- end
30
+ describe "do not use count distinct" do
31
+ before(:all) do
32
+ class ::JobHistory < ActiveRecord::Base
33
+ set_table_name "job_history"
34
+ set_primary_keys :employee_id, :start_date
35
+ end
36
+ end
37
+
38
+ after(:all) do
39
+ Object.send(:remove_const, 'JobHistory') if defined?(JobHistory)
40
+ end
41
+
42
+ it "should tell ActiveRecord that count distinct is not supported" do
43
+ ActiveRecord::Base.connection.supports_count_distinct?.should be_false
44
+ end
30
45
 
31
- it "should execute correct SQL COUNT DISTINCT statement on table with composite primary keys" do
32
- lambda { JobHistory.count(:distinct => true) }.should_not raise_error
46
+ it "should execute correct SQL COUNT DISTINCT statement on table with composite primary keys" do
47
+ lambda { JobHistory.count(:distinct => true) }.should_not raise_error
48
+ end
33
49
  end
34
50
 
51
+ describe "table with LOB" do
52
+ before(:all) do
53
+ ActiveRecord::Schema.define do
54
+ suppress_messages do
55
+ create_table :cpk_write_lobs_test, :primary_key => [:type_category, :date_value], :force => true do |t|
56
+ t.string :type_category, :limit => 15, :null => false
57
+ t.date :date_value, :null => false
58
+ t.text :results, :null => false
59
+ t.timestamps
60
+ end
61
+ create_table :non_cpk_write_lobs_test, :force => true do |t|
62
+ t.date :date_value, :null => false
63
+ t.text :results, :null => false
64
+ t.timestamps
65
+ end
66
+ end
67
+ end
68
+ class ::CpkWriteLobsTest < ActiveRecord::Base
69
+ set_table_name 'cpk_write_lobs_test'
70
+ set_primary_keys :type_category, :date_value
71
+ end
72
+ class ::NonCpkWriteLobsTest < ActiveRecord::Base
73
+ set_table_name 'non_cpk_write_lobs_test'
74
+ end
75
+ end
76
+
77
+ after(:all) do
78
+ ActiveRecord::Schema.define do
79
+ suppress_messages do
80
+ drop_table :cpk_write_lobs_test
81
+ drop_table :non_cpk_write_lobs_test
82
+ end
83
+ end
84
+ Object.send(:remove_const, "CpkWriteLobsTest")
85
+ Object.send(:remove_const, "NonCpkWriteLobsTest")
86
+ end
87
+
88
+ it "should create new record in table with CPK and LOB" do
89
+ lambda {
90
+ CpkWriteLobsTest.create(:type_category => 'AAA', :date_value => Date.today, :results => 'DATA '*10)
91
+ }.should_not raise_error
92
+ end
93
+
94
+ it "should create new record in table without CPK and with LOB" do
95
+ lambda {
96
+ NonCpkWriteLobsTest.create(:date_value => Date.today, :results => 'DATA '*10)
97
+ }.should_not raise_error
98
+ end
99
+ end
100
+
35
101
  # Other testing was done based on composite_primary_keys tests
36
102
 
37
103
  end
@@ -159,6 +159,26 @@ describe "OracleEnhancedAdapter custom methods for create, update and destroy" d
159
159
  @employee.update_time.should_not be_nil
160
160
  end
161
161
 
162
+ it "should rollback record when exception is raised in after_create callback" do
163
+ @employee = TestEmployee.new(
164
+ :first_name => "First",
165
+ :last_name => "Last",
166
+ :hire_date => @today
167
+ )
168
+ TestEmployee.class_eval { def after_create() raise "Make the transaction rollback" end }
169
+ begin
170
+ employees_count = TestEmployee.count
171
+ @employee.save
172
+ fail "Did not raise exception"
173
+ rescue => e
174
+ e.message.should == "Make the transaction rollback"
175
+ @employee.id.should == nil
176
+ TestEmployee.count.should == employees_count
177
+ ensure
178
+ TestEmployee.class_eval { remove_method :after_create }
179
+ end
180
+ end
181
+
162
182
  it "should update record" do
163
183
  @employee = TestEmployee.create(
164
184
  :first_name => "First",
@@ -173,6 +193,29 @@ describe "OracleEnhancedAdapter custom methods for create, update and destroy" d
173
193
  @employee.description.should == "Second Last"
174
194
  end
175
195
 
196
+ it "should rollback record when exception is raised in after_update callback" do
197
+ TestEmployee.class_eval { def after_update() raise "Make the transaction rollback" end }
198
+ begin
199
+ @employee = TestEmployee.create(
200
+ :first_name => "First",
201
+ :last_name => "Last",
202
+ :hire_date => @today,
203
+ :description => "description"
204
+ )
205
+ empl_id = @employee.id
206
+ @employee.reload
207
+ @employee.first_name = "Second"
208
+ @employee.save!
209
+ fail "Did not raise exception"
210
+ rescue => e
211
+ e.message.should == "Make the transaction rollback"
212
+ @employee.reload
213
+ @employee.first_name.should == "First"
214
+ ensure
215
+ TestEmployee.class_eval { remove_method :after_update }
216
+ end
217
+ end
218
+
176
219
  it "should not update record if nothing is changed and partial updates are enabled" do
177
220
  return pending("Not in this ActiveRecord version") unless TestEmployee.respond_to?(:partial_updates=)
178
221
  TestEmployee.partial_updates = true
@@ -214,6 +257,27 @@ describe "OracleEnhancedAdapter custom methods for create, update and destroy" d
214
257
  TestEmployee.find_by_employee_id(empl_id).should be_nil
215
258
  end
216
259
 
260
+ it "should rollback record when exception is raised in after_desotry callback" do
261
+ TestEmployee.class_eval { def after_destroy() raise "Make the transaction rollback" end }
262
+ @employee = TestEmployee.create(
263
+ :first_name => "First",
264
+ :last_name => "Last",
265
+ :hire_date => @today
266
+ )
267
+ @employee.reload
268
+ empl_id = @employee.id
269
+ begin
270
+ @employee.destroy
271
+ fail "Did not raise exception"
272
+ rescue => e
273
+ e.message.should == "Make the transaction rollback"
274
+ @employee.id.should == empl_id
275
+ TestEmployee.find_by_employee_id(empl_id).should_not be_nil
276
+ ensure
277
+ TestEmployee.class_eval { remove_method :after_destroy }
278
+ end
279
+ end
280
+
217
281
  it "should log create record" do
218
282
  log_to @buffer
219
283
  # reestablish plsql.connection as log_to might reset existing connection
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rsim-activerecord-oracle_enhanced-adapter
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0.2
4
+ version: 1.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Raimonds Simanovskis
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-04-07 00:00:00 -07:00
12
+ date: 2009-06-07 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -33,12 +33,12 @@ extra_rdoc_files:
33
33
  - History.txt
34
34
  - License.txt
35
35
  - Manifest.txt
36
- - README.txt
36
+ - README.rdoc
37
37
  files:
38
38
  - History.txt
39
39
  - License.txt
40
40
  - Manifest.txt
41
- - README.txt
41
+ - README.rdoc
42
42
  - lib/active_record/connection_adapters/emulation/oracle_adapter.rb
43
43
  - lib/active_record/connection_adapters/oracle_enhanced.rake
44
44
  - lib/active_record/connection_adapters/oracle_enhanced_adapter.rb