activerecord-oracle_enhanced-adapter 1.5.4 → 1.5.5

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.
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