activerecord-oracle_enhanced-adapter 1.5.4 → 1.5.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: de6010dec5446b296a5ad3d372f6aaad1ac71549
4
- data.tar.gz: c82a83f8d18e75b367925eb029f786b567e1f6e4
3
+ metadata.gz: b1f1f3ee668e111001b017dd37db4d5e70bb315f
4
+ data.tar.gz: 10e4fdff8ab461b4e48ac248e8a61ccaa4fd1870
5
5
  SHA512:
6
- metadata.gz: 8887e65c6d08ede8097bdeb8951227edd05c92c943074853fb6caf16009033af74407d24d03da3ca9184aa0b37633939fec5174f07aac38d85ef84abdbd620f6
7
- data.tar.gz: 0f3843a406fbe8945785b85ee849debb10636e82161b7423da0643bb6c2c3ee8f92b2bf0e5e46b5ec577ebdeda0d9020c63b8fa1624f7a325b5d9c110f72372e
6
+ metadata.gz: 58dddb4992f8ae81c79ee379d0c26a0dcfe869df5e052e1892679a9f03ba440b623fe7b6b2d6c4a85e589bc80989b47e1285d8ad9139e19a64e249872701d566
7
+ data.tar.gz: d42acd789d7733e82e7e70d8c81c453e9dbd0a3a3dc0b196901ed9552ad09580540559ee5dbb0ec30102ef83d389b313ba932bfe22dc5c7ef0cefda1347ff1db
data/History.md CHANGED
@@ -1,3 +1,17 @@
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
+
1
15
  ## 1.5.4 / 2014-03-25
2
16
 
3
17
  * Enhancements
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.5.4
1
+ 1.5.5
@@ -5,12 +5,12 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{activerecord-oracle_enhanced-adapter}
8
- s.version = "1.5.4"
8
+ s.version = "1.5.5"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.license = 'MIT'
12
12
  s.authors = [%q{Raimonds Simanovskis}]
13
- s.date = %q{2014-03-25}
13
+ s.date = %q{2014-05-23}
14
14
  s.description = %q{Oracle "enhanced" ActiveRecord adapter contains useful additional methods for working with new and legacy Oracle databases.
15
15
  This adapter is superset of original ActiveRecord Oracle adapter.
16
16
  }
@@ -271,6 +271,13 @@ module ActiveRecord
271
271
  cattr_accessor :emulate_dates_by_column_name
272
272
  self.emulate_dates_by_column_name = false
273
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
+
274
281
  # Check column name to identify if it is Date (and not Time) column.
275
282
  # Is used if +emulate_dates_by_column_name+ option is set to +true+.
276
283
  # Override this method definition in initializer file if different Date column recognition is needed.
@@ -305,7 +312,7 @@ module ActiveRecord
305
312
  # Is used if +emulate_integers_by_column_name+ option is set to +true+.
306
313
  # Override this method definition in initializer file if different Integer column recognition is needed.
307
314
  def self.is_integer_column?(name, table_name = nil)
308
- name =~ /(^|_)id$/i
315
+ !!(name =~ /(^|_)id$/i)
309
316
  end
310
317
 
311
318
  ##
@@ -389,6 +396,7 @@ module ActiveRecord
389
396
  @enable_dbms_output = false
390
397
  if config.fetch(:prepared_statements) { true }
391
398
  @visitor = Arel::Visitors::Oracle.new self
399
+ @prepared_statements = true
392
400
  else
393
401
  @visitor = unprepared_visitor
394
402
  end
@@ -420,6 +428,8 @@ module ActiveRecord
420
428
  true
421
429
  end
422
430
 
431
+ NUMBER_MAX_PRECISION = 38
432
+
423
433
  #:stopdoc:
424
434
  DEFAULT_NLS_PARAMETERS = {
425
435
  :nls_calendar => nil,
@@ -443,10 +453,10 @@ module ActiveRecord
443
453
 
444
454
  #:stopdoc:
445
455
  NATIVE_DATABASE_TYPES = {
446
- :primary_key => "NUMBER(38) NOT NULL PRIMARY KEY",
456
+ :primary_key => "NUMBER(#{NUMBER_MAX_PRECISION}) NOT NULL PRIMARY KEY",
447
457
  :string => { :name => "VARCHAR2", :limit => 255 },
448
458
  :text => { :name => "CLOB" },
449
- :integer => { :name => "NUMBER", :limit => 38 },
459
+ :integer => { :name => "NUMBER", :limit => NUMBER_MAX_PRECISION },
450
460
  :float => { :name => "NUMBER" },
451
461
  :decimal => { :name => "DECIMAL" },
452
462
  :datetime => { :name => "DATE" },
@@ -728,7 +738,7 @@ module ActiveRecord
728
738
  end
729
739
 
730
740
  def substitute_at(column, index)
731
- Arel.sql(":a#{index + 1}")
741
+ Arel::Nodes::BindParam.new (":a#{index + 1}")
732
742
  end
733
743
 
734
744
  def clear_cache!
@@ -742,7 +752,7 @@ module ActiveRecord
742
752
  log(sql, name, type_casted_binds) do
743
753
  cursor = nil
744
754
  cached = false
745
- if binds.empty?
755
+ if without_prepared_statement?(binds)
746
756
  cursor = @connection.prepare(sql)
747
757
  else
748
758
  unless @statements.key? sql
@@ -834,22 +844,29 @@ module ActiveRecord
834
844
 
835
845
  # New method in ActiveRecord 3.1
836
846
  def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
837
- log(sql, name, binds) do
847
+ type_casted_binds = binds.map { |col, val|
848
+ [col, type_cast(val, col)]
849
+ }
850
+ log(sql, name, type_casted_binds) do
838
851
  returning_id_col = returning_id_index = nil
839
- cursor = if @statements.key?(sql)
840
- @statements[sql]
852
+ if without_prepared_statement?(binds)
853
+ cursor = @connection.prepare(sql)
841
854
  else
842
- @statements[sql] = @connection.prepare(sql)
843
- end
855
+ unless @statements.key? (sql)
856
+ @statements[sql] = @connection.prepare(sql)
857
+ end
844
858
 
845
- binds.each_with_index do |bind, i|
846
- col, val = bind
847
- if col.returning_id?
848
- returning_id_col = [col]
849
- returning_id_index = i + 1
850
- cursor.bind_returning_param(returning_id_index, Integer)
851
- else
852
- cursor.bind_param(i + 1, type_cast(val, col), col)
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
853
870
  end
854
871
  end
855
872
 
@@ -868,7 +885,7 @@ module ActiveRecord
868
885
  def exec_update(sql, name, binds)
869
886
  log(sql, name, binds) do
870
887
  cached = false
871
- if binds.empty?
888
+ if without_prepared_statement?(binds)
872
889
  cursor = @connection.prepare(sql)
873
890
  else
874
891
  cursor = if @statements.key?(sql)
@@ -1260,7 +1277,7 @@ module ActiveRecord
1260
1277
  end.map do |row|
1261
1278
  limit, scale = row['limit'], row['scale']
1262
1279
  if limit || scale
1263
- row['sql_type'] += "(#{(limit || 38).to_i}" + ((scale = scale.to_i) > 0 ? ",#{scale})" : ")")
1280
+ row['sql_type'] += "(#{(limit || NUMBER_MAX_PRECISION).to_i}" + ((scale = scale.to_i) > 0 ? ",#{scale})" : ")")
1264
1281
  end
1265
1282
 
1266
1283
  if row['sql_type_owner']
@@ -23,9 +23,24 @@ module ActiveRecord
23
23
  end
24
24
 
25
25
  def type_cast(value) #:nodoc:
26
- return OracleEnhancedColumn::string_to_raw(value) if type == :raw
27
- return guess_date_or_time(value) if type == :datetime && OracleEnhancedAdapter.emulate_dates
28
- super
26
+ case type
27
+ when :raw
28
+ OracleEnhancedColumn.string_to_raw(value)
29
+ when :datetime
30
+ OracleEnhancedAdapter.emulate_dates ? guess_date_or_time(value) : super
31
+ when :float
32
+ !value.nil? ? self.class.value_to_decimal(value) : super
33
+ else
34
+ super
35
+ end
36
+ end
37
+
38
+ def type_cast_code(var_name)
39
+ type == :float ? "#{self.class.name}.value_to_decimal(#{var_name})" : super
40
+ end
41
+
42
+ def klass
43
+ type == :float ? BigDecimal : super
29
44
  end
30
45
 
31
46
  def virtual?
@@ -85,12 +100,14 @@ module ActiveRecord
85
100
  forced_column_type ||
86
101
  case field_type
87
102
  when /decimal|numeric|number/i
88
- if OracleEnhancedAdapter.emulate_booleans && field_type == 'NUMBER(1)'
103
+ if OracleEnhancedAdapter.emulate_booleans && field_type.upcase == "NUMBER(1)"
89
104
  :boolean
90
105
  elsif extract_scale(field_type) == 0 ||
91
106
  # if column name is ID or ends with _ID
92
107
  OracleEnhancedAdapter.emulate_integers_by_column_name && OracleEnhancedAdapter.is_integer_column?(name, table_name)
93
108
  :integer
109
+ elsif field_type.upcase == "NUMBER"
110
+ OracleEnhancedAdapter.number_datatype_coercion
94
111
  else
95
112
  :decimal
96
113
  end
@@ -39,15 +39,23 @@ module ActiveRecord #:nodoc:
39
39
  return prepare_column_options_without_oracle_enhanced(column, types) unless oracle_enhanced_adapter?
40
40
 
41
41
  spec = {}
42
+
42
43
  spec[:name] = column.name.inspect
43
44
  spec[:type] = column.virtual? ? 'virtual' : column.type.to_s
44
- spec[:virtual_type] = column.type.inspect if column.virtual? && column.sql_type != 'NUMBER'
45
45
  spec[:limit] = column.limit.inspect if column.limit != types[column.type][:limit] && column.type != :decimal
46
46
  spec[:precision] = column.precision.inspect if !column.precision.nil?
47
47
  spec[:scale] = column.scale.inspect if !column.scale.nil?
48
48
  spec[:null] = 'false' if !column.null
49
49
  spec[:as] = column.virtual_column_data_default if column.virtual?
50
50
  spec[:default] = default_string(column.default) if column.has_default? && !column.virtual?
51
+
52
+ if column.virtual?
53
+ # Supports backwards compatibility with older OracleEnhancedAdapter versions where 'NUMBER' virtual column type is not included in dump
54
+ if column.sql_type != "NUMBER" || ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.number_datatype_coercion != :decimal
55
+ spec[:virtual_type] = column.type.inspect
56
+ end
57
+ end
58
+
51
59
  spec
52
60
  end
53
61
 
@@ -4,19 +4,19 @@ module ActiveRecord #:nodoc:
4
4
 
5
5
  module InstanceMethods #:nodoc:
6
6
  private
7
-
7
+
8
8
  def _field_changed?(attr, old, value)
9
9
  if column = column_for_attribute(attr)
10
10
  # Added also :decimal type
11
- if (column.type == :integer || column.type == :decimal) && column.null && (old.nil? || old == 0) && value.blank?
12
- # For nullable integer columns, NULL gets stored in database for blank (i.e. '') values.
11
+ if ([:integer, :decimal, :float].include? column.type) && column.null && (old.nil? || old == 0) && value.blank?
12
+ # For nullable integer/decimal/float columns, NULL gets stored in database for blank (i.e. '') values.
13
13
  # Hence we don't record it as a change if the value changes from nil to ''.
14
14
  # If an old value of 0 is set to '' we want this to get changed to nil as otherwise it'll
15
15
  # be typecast back to 0 (''.to_i => 0)
16
16
  value = nil
17
- # Oracle stores empty string '' as NULL
18
- # therefore need to convert empty string value to nil if old value is nil
19
17
  elsif column.type == :string && column.null && old.nil?
18
+ # Oracle stores empty string '' as NULL
19
+ # therefore need to convert empty string value to nil if old value is nil
20
20
  value = nil if value == ''
21
21
  elsif old == 0 && value.is_a?(String) && value.present? && non_zero?(value)
22
22
  value = nil
@@ -30,8 +30,8 @@ module ActiveRecord #:nodoc:
30
30
 
31
31
  def non_zero?(value)
32
32
  value !~ /\A0+(\.0+)?\z/
33
- end
34
-
33
+ end
34
+
35
35
  end
36
36
 
37
37
  end
@@ -27,6 +27,7 @@ begin
27
27
  ojdbc_jars.any? do |ojdbc_jar|
28
28
  if File.exists?(file_path = File.join(dir, ojdbc_jar))
29
29
  puts "WARNING: JDK #{java_version} is not officially supported by #{ojdbc_jar}" if java_version >= '1.8'
30
+ puts "See http://www.oracle.com/technetwork/database/enterprise-edition/jdbc-faq-090281.html#01_03 for supported versions"
30
31
  require file_path
31
32
  true
32
33
  end
@@ -98,7 +98,7 @@ module ActiveRecord #:nodoc:
98
98
 
99
99
  # Creates a record with custom create method
100
100
  # and returns its id.
101
- def create_record
101
+ def _create_record
102
102
  # check if class has custom create method
103
103
  if self.class.custom_create_method
104
104
  # run before/after callbacks defined in model
@@ -133,7 +133,7 @@ module ActiveRecord #:nodoc:
133
133
 
134
134
  # Updates the associated record with custom update method
135
135
  # Returns the number of affected rows.
136
- def update_record(attribute_names = @attributes.keys)
136
+ def _update_record(attribute_names = @attributes.keys)
137
137
  # check if class has custom update method
138
138
  if self.class.custom_update_method
139
139
  # run before/after callbacks defined in model
@@ -187,5 +187,8 @@ module ActiveRecord #:nodoc:
187
187
  def log_custom_method(*args)
188
188
  self.class.connection.send(:log, *args) { yield }
189
189
  end
190
+
191
+ alias_method :update_record, :_update_record if private_method_defined?(:_update_record)
192
+ alias_method :create_record, :_create_record if private_method_defined?(:_create_record)
190
193
  end
191
194
  end
@@ -686,4 +686,29 @@ describe "OracleEnhancedAdapter" do
686
686
  explain.should include("INDEX UNIQUE SCAN")
687
687
  end
688
688
  end if ENV['RAILS_GEM_VERSION'] >= '3.2'
689
+
690
+ describe ".is_integer_column?" do
691
+ before(:all) do
692
+ @adapter = ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter
693
+ end
694
+
695
+ it "should return TrueClass or FalseClass" do
696
+ @adapter.is_integer_column?("adapter_id").should be_a TrueClass
697
+ @adapter.is_integer_column?("").should be_a FalseClass
698
+ end
699
+
700
+ it "should return true if name is 'id'" do
701
+ @adapter.is_integer_column?("id").should be_true
702
+ end
703
+
704
+ it "should return true if name ends with '_id'" do
705
+ @adapter.is_integer_column?("_id").should be_true
706
+ @adapter.is_integer_column?("foo_id").should be_true
707
+ end
708
+
709
+ it "should return false if name is 'something_else'" do
710
+ @adapter.is_integer_column?("something_else").should be_false
711
+ end
712
+ end
713
+
689
714
  end
@@ -381,6 +381,9 @@ describe "OracleEnhancedAdapter context index" do
381
381
 
382
382
  describe "with table prefix and suffix" do
383
383
  before(:all) do
384
+ @conn = ActiveRecord::Base.connection
385
+ @oracle12c = !! @conn.select_value(
386
+ "select * from product_component_version where product like 'Oracle%' and to_number(substr(version,1,2)) = 12")
384
387
  ActiveRecord::Base.table_name_prefix = 'xxx_'
385
388
  ActiveRecord::Base.table_name_suffix = '_xxx'
386
389
  create_tables
@@ -405,6 +408,7 @@ describe "OracleEnhancedAdapter context index" do
405
408
  end
406
409
 
407
410
  it "should dump definition of multiple table index with options" do
411
+ pending "It always fails when Oracle 12c 12.1.0 used." if @oracle12c
408
412
  options = {
409
413
  name: 'xxx_post_and_comments_i',
410
414
  index_column: :all_text, index_column_trigger_on: :updated_at,
@@ -206,6 +206,7 @@ describe "OracleEnhancedAdapter integer type detection based on column names" do
206
206
  job_id NUMBER,
207
207
  salary NUMBER,
208
208
  commission_pct NUMBER(2,2),
209
+ unwise_name_id NUMBER(2,2),
209
210
  manager_id NUMBER(6),
210
211
  is_manager NUMBER(1),
211
212
  department_id NUMBER(4,0),
@@ -218,17 +219,46 @@ describe "OracleEnhancedAdapter integer type detection based on column names" do
218
219
  INCREMENT BY 1 START WITH 10040 CACHE 20 NOORDER NOCYCLE
219
220
  SQL
220
221
  end
221
-
222
+
222
223
  after(:all) do
223
224
  @conn.execute "DROP TABLE test2_employees"
224
225
  @conn.execute "DROP SEQUENCE test2_employees_seq"
225
226
  end
226
227
 
227
- it "should set NUMBER column type as decimal if emulate_integers_by_column_name is false" do
228
- ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.emulate_integers_by_column_name = false
229
- columns = @conn.columns('test2_employees')
230
- column = columns.detect{|c| c.name == "job_id"}
231
- column.type.should == :decimal
228
+ context "when number_datatype_coercion is :decimal" do
229
+ before { ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.stub(:number_datatype_coercion).and_return(:decimal) }
230
+
231
+ it "should set NUMBER column type as decimal if emulate_integers_by_column_name is false" do
232
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.emulate_integers_by_column_name = false
233
+ columns = @conn.columns('test2_employees')
234
+ column = columns.detect{|c| c.name == "job_id"}
235
+ column.type.should == :decimal
236
+ end
237
+
238
+ it "should set NUMBER column type as decimal if column name is not 'id' and does not ends with '_id' and emulate_integers_by_column_name is true" do
239
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.emulate_integers_by_column_name = true
240
+ columns = @conn.columns('test2_employees')
241
+ column = columns.detect{|c| c.name == "salary"}
242
+ column.type.should == :decimal
243
+ end
244
+ end
245
+
246
+ context "when number_datatype_coercion is :float" do
247
+ before { ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.stub(:number_datatype_coercion).and_return(:float) }
248
+
249
+ it "should set NUMBER column type as float if emulate_integers_by_column_name is false" do
250
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.emulate_integers_by_column_name = false
251
+ columns = @conn.columns('test2_employees')
252
+ column = columns.detect{|c| c.name == "job_id"}
253
+ column.type.should == :float
254
+ end
255
+
256
+ it "should set NUMBER column type as float if column name is not 'id' and does not ends with '_id' and emulate_integers_by_column_name is true" do
257
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.emulate_integers_by_column_name = true
258
+ columns = @conn.columns('test2_employees')
259
+ column = columns.detect{|c| c.name == "salary"}
260
+ column.type.should == :float
261
+ end
232
262
  end
233
263
 
234
264
  it "should set NUMBER column type as integer if emulate_integers_by_column_name is true" do
@@ -240,10 +270,24 @@ describe "OracleEnhancedAdapter integer type detection based on column names" do
240
270
  column.type.should == :integer
241
271
  end
242
272
 
243
- it "should set NUMBER column type as decimal if column name does not contain 'id' and emulate_integers_by_column_name is true" do
273
+ it "should set NUMBER(p,0) column type as integer" do
244
274
  ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.emulate_integers_by_column_name = true
245
275
  columns = @conn.columns('test2_employees')
246
- column = columns.detect{|c| c.name == "salary"}
276
+ column = columns.detect{|c| c.name == "department_id"}
277
+ column.type.should == :integer
278
+ end
279
+
280
+ it "should set NUMBER(p,s) column type as integer if column name ends with '_id' and emulate_integers_by_column_name is true" do
281
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.emulate_integers_by_column_name = true
282
+ columns = @conn.columns('test2_employees')
283
+ column = columns.detect{|c| c.name == "unwise_name_id"}
284
+ column.type.should == :integer
285
+ end
286
+
287
+ it "should set NUMBER(p,s) column type as decimal if column name ends with '_id' and emulate_integers_by_column_name is false" do
288
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.emulate_integers_by_column_name = false
289
+ columns = @conn.columns('test2_employees')
290
+ column = columns.detect{|c| c.name == "unwise_name_id"}
247
291
  column.type.should == :decimal
248
292
  end
249
293
 
@@ -254,7 +298,7 @@ describe "OracleEnhancedAdapter integer type detection based on column names" do
254
298
  column.type_cast(1.0).class.should == BigDecimal
255
299
  end
256
300
 
257
- it "should return Fixnum value from NUMBER column if column name contains 'id' and emulate_integers_by_column_name is true" do
301
+ it "should return Fixnum value from NUMBER column if column name ends with '_id' and emulate_integers_by_column_name is true" do
258
302
  ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.emulate_integers_by_column_name = true
259
303
  columns = @conn.columns('test2_employees')
260
304
  column = columns.detect{|c| c.name == "job_id"}
@@ -266,7 +310,7 @@ describe "OracleEnhancedAdapter integer type detection based on column names" do
266
310
  class ::Test2Employee < ActiveRecord::Base
267
311
  end
268
312
  end
269
-
313
+
270
314
  after(:each) do
271
315
  Object.send(:remove_const, "Test2Employee")
272
316
  @conn.clear_types_for_columns
@@ -606,7 +650,6 @@ describe "OracleEnhancedAdapter timestamp with timezone support" do
606
650
 
607
651
  end
608
652
 
609
-
610
653
  describe "OracleEnhancedAdapter date and timestamp with different NLS date formats" do
611
654
  before(:all) do
612
655
  ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
@@ -863,7 +906,7 @@ describe "OracleEnhancedAdapter assign string to :date and :datetime columns" do
863
906
  @employee.reload
864
907
  @employee.last_login_at.should == @today.to_time
865
908
  end
866
-
909
+
867
910
  end
868
911
 
869
912
  describe "OracleEnhancedAdapter handling of CLOB columns" do
@@ -1301,7 +1344,6 @@ describe "OracleEnhancedAdapter handling of RAW columns" do
1301
1344
  end
1302
1345
  end
1303
1346
 
1304
-
1305
1347
  describe "OracleEnhancedAdapter quoting of NCHAR and NVARCHAR2 columns" do
1306
1348
  before(:all) do
1307
1349
  ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
@@ -16,6 +16,7 @@ if ActiveRecord::Base.method_defined?(:changed?)
16
16
  last_name VARCHAR2(25),
17
17
  job_id NUMBER(6,0) NULL,
18
18
  salary NUMBER(8,2),
19
+ pto_per_hour NUMBER,
19
20
  comments CLOB,
20
21
  hire_date DATE
21
22
  )
@@ -27,7 +28,7 @@ if ActiveRecord::Base.method_defined?(:changed?)
27
28
  class TestEmployee < ActiveRecord::Base
28
29
  end
29
30
  end
30
-
31
+
31
32
  after(:all) do
32
33
  Object.send(:remove_const, "TestEmployee")
33
34
  @conn.execute "DROP TABLE test_employees"
@@ -62,6 +63,16 @@ if ActiveRecord::Base.method_defined?(:changed?)
62
63
  @employee.should_not be_changed
63
64
  end
64
65
 
66
+ it "should not mark empty float (stored as NULL) as changed when reassigning it" do
67
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.stub(:number_datatype_coercion) { :float }
68
+ @employee = TestEmployee.create!(:pto_per_hour => '')
69
+ @employee.pto_per_hour = ''
70
+ @employee.should_not be_changed
71
+ @employee.reload
72
+ @employee.pto_per_hour = ''
73
+ @employee.should_not be_changed
74
+ end
75
+
65
76
  it "should not mark empty text (stored as NULL) as changed when reassigning it" do
66
77
  @employee = TestEmployee.create!(:comments => nil)
67
78
  @employee.comments = nil
@@ -111,7 +122,7 @@ if ActiveRecord::Base.method_defined?(:changed?)
111
122
  @employee = TestEmployee.new
112
123
  @employee.job_id = 0
113
124
  @employee.save!.should be_true
114
-
125
+
115
126
  @employee.should_not be_changed
116
127
 
117
128
  @employee.job_id = '0'
@@ -376,7 +376,7 @@ describe "OracleEnhancedAdapter schema dump" do
376
376
  t.virtual :full_name, :as => "first_name || ', ' || last_name"
377
377
  t.virtual :short_name, :as => "COALESCE(first_name, last_name)", :type => :string, :limit => 300
378
378
  t.virtual :abbrev_name, :as => "SUBSTR(first_name,1,50) || ' ' || SUBSTR(last_name,1,1) || '.'", :type => "VARCHAR(100)"
379
- t.virtual :name_ratio, :as=>'(LENGTH(first_name)*10/LENGTH(last_name)*10)'
379
+ t.virtual :name_ratio, :as=>'(LENGTH(first_name)/LENGTH(last_name))'
380
380
  t.column :full_name_length, :virtual, :as => "length(first_name || ', ' || last_name)", :type => :integer
381
381
  t.virtual :field_with_leading_space, :as => "' ' || first_name || ' '", :limit => 300, :type => :string
382
382
  end
@@ -402,13 +402,30 @@ describe "OracleEnhancedAdapter schema dump" do
402
402
  end
403
403
  end
404
404
 
405
- it 'should dump correctly' do
406
- standard_dump.should =~ /t\.virtual "full_name",(\s*)limit: 512,(\s*)as: "\\"FIRST_NAME\\"\|\|', '\|\|\\"LAST_NAME\\"",(\s*)type: :string/
407
- standard_dump.should =~ /t\.virtual "short_name",(\s*)limit: 300,(\s*)as:(.*),(\s*)type: :string/
408
- standard_dump.should =~ /t\.virtual "full_name_length",(\s*)precision: 38,(\s*)scale: 0,(\s*)as:(.*),(\s*)type: :integer/
409
- standard_dump.should =~ /t\.virtual "name_ratio",(\s*)as:(.*)\"$/ # no :type
410
- standard_dump.should =~ /t\.virtual "abbrev_name",(\s*)limit: 100,(\s*)as:(.*),(\s*)type: :string/
411
- standard_dump.should =~ /t\.virtual "field_with_leading_space",(\s*)limit: 300,(\s*)as: "' '\|\|\\"FIRST_NAME\\"\|\|' '",(\s*)type: :string/
405
+ context "when number_datatype_coercion is :float" do
406
+ before { ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.stub(:number_datatype_coercion).and_return(:float) }
407
+
408
+ it 'should dump correctly' do
409
+ standard_dump.should =~ /t\.virtual "full_name",(\s*)limit: 512,(\s*)as: "\\"FIRST_NAME\\"\|\|', '\|\|\\"LAST_NAME\\"",(\s*)type: :string/
410
+ standard_dump.should =~ /t\.virtual "short_name",(\s*)limit: 300,(\s*)as:(.*),(\s*)type: :string/
411
+ standard_dump.should =~ /t\.virtual "full_name_length",(\s*)precision: 38,(\s*)scale: 0,(\s*)as:(.*),(\s*)type: :integer/
412
+ standard_dump.should =~ /t\.virtual "name_ratio",(\s*)as:(.*),(\s*)type: :float$/
413
+ standard_dump.should =~ /t\.virtual "abbrev_name",(\s*)limit: 100,(\s*)as:(.*),(\s*)type: :string/
414
+ standard_dump.should =~ /t\.virtual "field_with_leading_space",(\s*)limit: 300,(\s*)as: "' '\|\|\\"FIRST_NAME\\"\|\|' '",(\s*)type: :string/
415
+ end
416
+ end
417
+
418
+ context "when number_datatype_coercion is :decimal" do
419
+ before { ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.stub(:number_datatype_coercion).and_return(:decimal) }
420
+
421
+ it 'should dump correctly' do
422
+ standard_dump.should =~ /t\.virtual "full_name",(\s*)limit: 512,(\s*)as: "\\"FIRST_NAME\\"\|\|', '\|\|\\"LAST_NAME\\"",(\s*)type: :string/
423
+ standard_dump.should =~ /t\.virtual "short_name",(\s*)limit: 300,(\s*)as:(.*),(\s*)type: :string/
424
+ standard_dump.should =~ /t\.virtual "full_name_length",(\s*)precision: 38,(\s*)scale: 0,(\s*)as:(.*),(\s*)type: :integer/
425
+ standard_dump.should =~ /t\.virtual "name_ratio",(\s*)as:(.*)\"$/
426
+ standard_dump.should =~ /t\.virtual "abbrev_name",(\s*)limit: 100,(\s*)as:(.*),(\s*)type: :string/
427
+ standard_dump.should =~ /t\.virtual "field_with_leading_space",(\s*)limit: 300,(\s*)as: "' '\|\|\\"FIRST_NAME\\"\|\|' '",(\s*)type: :string/
428
+ end
412
429
  end
413
430
 
414
431
  context 'with column cache' do
@@ -454,4 +471,160 @@ describe "OracleEnhancedAdapter schema dump" do
454
471
  end
455
472
  end
456
473
 
474
+ describe "NUMBER columns" do
475
+ after(:each) do
476
+ schema_define do
477
+ drop_table "test_numbers"
478
+ end
479
+ end
480
+
481
+ let(:value_within_max_precision) { (10 ** @conn.class::NUMBER_MAX_PRECISION) - 1 }
482
+ let(:value_exceeding_max_precision) { (10 ** @conn.class::NUMBER_MAX_PRECISION) + 1 }
483
+
484
+ context "when using ActiveRecord::Schema.define and ActiveRecord::ConnectionAdapters::TableDefinition#float" do
485
+ before :each do
486
+ schema_define do
487
+ create_table :test_numbers, :force => true do |t|
488
+ t.float :value
489
+ end
490
+ end
491
+ end
492
+
493
+ context "when number_datatype_coercion is :float" do
494
+ before { ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.stub(:number_datatype_coercion).and_return(:float) }
495
+
496
+ it "should dump correctly" do
497
+ standard_dump.should =~ /t\.float "value"$/
498
+ end
499
+ end
500
+
501
+ context "when number_datatype_coercion is :decimal" do
502
+ before { ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.stub(:number_datatype_coercion).and_return(:decimal) }
503
+
504
+ it "should dump correctly" do
505
+ standard_dump.should =~ /t\.decimal "value"$/
506
+ end
507
+ end
508
+ end
509
+
510
+ context "when using handwritten 'CREATE_TABLE' SQL" do
511
+ before :each do
512
+ ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
513
+ @conn = ActiveRecord::Base.connection
514
+ @conn.execute <<-SQL
515
+ CREATE TABLE test_numbers (
516
+ id NUMBER(#{@conn.class::NUMBER_MAX_PRECISION},0) PRIMARY KEY,
517
+ value NUMBER
518
+ )
519
+ SQL
520
+ @conn.execute <<-SQL
521
+ CREATE SEQUENCE test_numbers_seq MINVALUE 1
522
+ INCREMENT BY 1 START WITH 1 CACHE 20 NOORDER NOCYCLE
523
+ SQL
524
+ end
525
+
526
+ context "when number_datatype_coercion is :float" do
527
+ before { ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.stub(:number_datatype_coercion).and_return(:float) }
528
+
529
+ it "should dump correctly" do
530
+ standard_dump.should =~ /t\.float "value"$/
531
+ end
532
+
533
+ describe "ActiveRecord saving" do
534
+ before :each do
535
+ class ::TestNumber < ActiveRecord::Base
536
+ self.table_name = "test_numbers"
537
+ end
538
+ end
539
+
540
+ it "should allow saving of values within NUMBER_MAX_PRECISION" do
541
+ number = TestNumber.new(value: value_within_max_precision)
542
+ number.save!
543
+ number.reload
544
+ number.value.should eq(value_within_max_precision)
545
+ end
546
+
547
+ it "should allow saving of values larger than NUMBER_MAX_PRECISION" do
548
+ number = TestNumber.new(value: value_exceeding_max_precision)
549
+ number.save!
550
+ number.reload
551
+ number.value.should eq(value_exceeding_max_precision)
552
+ end
553
+
554
+ it "should be recreatable from dump and have same properties" do
555
+ # Simulating db:schema:dump & db:test:load
556
+ 2.times do
557
+ create_table_dump = standard_dump[/(create_table.+?end)/m]
558
+
559
+ schema_define do
560
+ drop_table "test_numbers"
561
+ end
562
+
563
+ schema_define(&eval("-> * { #{create_table_dump} }"))
564
+ end
565
+
566
+ number = TestNumber.new(value: value_within_max_precision)
567
+ number.save!
568
+
569
+ number2 = TestNumber.new(value: value_exceeding_max_precision)
570
+ number2.save!
571
+ end
572
+ end
573
+ end
574
+
575
+ context "when number_datatype_coercion is :decimal" do
576
+ before { ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.stub(:number_datatype_coercion).and_return(:decimal) }
577
+
578
+ it "should dump correctly" do
579
+ standard_dump.should =~ /t\.decimal "value"$/
580
+ end
581
+
582
+ describe "ActiveRecord saving" do
583
+ before :each do
584
+ class ::TestNumber < ActiveRecord::Base
585
+ self.table_name = "test_numbers"
586
+ end
587
+ end
588
+
589
+ it "should allow saving of values within NUMBER_MAX_PRECISION" do
590
+ number = TestNumber.new(value: value_within_max_precision)
591
+ number.save!
592
+ number.reload
593
+ number.value.should eq(value_within_max_precision)
594
+ end
595
+
596
+ it "should allow saving of values larger than NUMBER_MAX_PRECISION" do
597
+ number = TestNumber.new(value: value_exceeding_max_precision)
598
+ number.save!
599
+ number.reload
600
+ number.value.should eq(value_exceeding_max_precision)
601
+ end
602
+
603
+ it "should be recreatable from dump and have same properties" do
604
+ # Simulating db:schema:dump & db:test:load
605
+ 2.times do |i|
606
+ create_table_dump = standard_dump[/(create_table.+?end)/m]
607
+
608
+ schema_define do
609
+ drop_table "test_numbers"
610
+ end
611
+
612
+ schema_define(&eval("-> * { #{create_table_dump} }"))
613
+ end
614
+
615
+ number = TestNumber.new(value: value_within_max_precision)
616
+ number.save!
617
+
618
+ # Raises 'ORA-01438' as :value column type isn't FLOAT'ish
619
+ number2 = TestNumber.new(value: value_exceeding_max_precision)
620
+ lambda do
621
+ number2.save!
622
+ end.should raise_error() { |e| e.message.should =~ /ORA-01438/ }
623
+ end
624
+ end
625
+ end # context (:decimal)
626
+
627
+ end # context (handwritten)
628
+ end # describe (NUMBER columns)
629
+
457
630
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-oracle_enhanced-adapter
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.4
4
+ version: 1.5.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Raimonds Simanovskis
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-03-26 00:00:00.000000000 Z
11
+ date: 2014-05-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jeweler