activerecord-oracle_enhanced-adapter 5.2.8 → 7.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +390 -21
  3. data/README.md +35 -8
  4. data/VERSION +1 -1
  5. data/lib/active_record/connection_adapters/emulation/oracle_adapter.rb +1 -1
  6. data/lib/active_record/connection_adapters/oracle_enhanced/column.rb +3 -3
  7. data/lib/active_record/connection_adapters/oracle_enhanced/connection.rb +42 -37
  8. data/lib/active_record/connection_adapters/oracle_enhanced/context_index.rb +59 -60
  9. data/lib/active_record/connection_adapters/oracle_enhanced/database_limits.rb +5 -10
  10. data/lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb +86 -81
  11. data/lib/active_record/connection_adapters/oracle_enhanced/database_tasks.rb +9 -10
  12. data/lib/active_record/connection_adapters/oracle_enhanced/dbms_output.rb +1 -2
  13. data/lib/active_record/connection_adapters/oracle_enhanced/jdbc_connection.rb +37 -16
  14. data/lib/active_record/connection_adapters/oracle_enhanced/jdbc_quoting.rb +1 -1
  15. data/lib/active_record/connection_adapters/oracle_enhanced/lob.rb +5 -6
  16. data/lib/active_record/connection_adapters/oracle_enhanced/oci_connection.rb +58 -49
  17. data/lib/active_record/connection_adapters/oracle_enhanced/oci_quoting.rb +1 -1
  18. data/lib/active_record/connection_adapters/oracle_enhanced/procedures.rb +6 -7
  19. data/lib/active_record/connection_adapters/oracle_enhanced/quoting.rb +75 -51
  20. data/lib/active_record/connection_adapters/oracle_enhanced/schema_creation.rb +13 -14
  21. data/lib/active_record/connection_adapters/oracle_enhanced/schema_definitions.rb +14 -4
  22. data/lib/active_record/connection_adapters/oracle_enhanced/schema_dumper.rb +27 -24
  23. data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb +156 -155
  24. data/lib/active_record/connection_adapters/oracle_enhanced/structure_dump.rb +103 -90
  25. data/lib/active_record/connection_adapters/oracle_enhanced/type_metadata.rb +3 -2
  26. data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +261 -161
  27. data/lib/active_record/type/oracle_enhanced/boolean.rb +0 -1
  28. data/lib/active_record/type/oracle_enhanced/character_string.rb +36 -0
  29. data/lib/active_record/type/oracle_enhanced/integer.rb +0 -1
  30. data/lib/arel/visitors/oracle.rb +221 -0
  31. data/lib/arel/visitors/oracle12.rb +128 -0
  32. data/spec/active_record/connection_adapters/emulation/oracle_adapter_spec.rb +0 -2
  33. data/spec/active_record/connection_adapters/oracle_enhanced/connection_spec.rb +78 -26
  34. data/spec/active_record/connection_adapters/oracle_enhanced/context_index_spec.rb +7 -15
  35. data/spec/active_record/connection_adapters/oracle_enhanced/database_tasks_spec.rb +5 -0
  36. data/spec/active_record/connection_adapters/oracle_enhanced/dbms_output_spec.rb +17 -17
  37. data/spec/active_record/connection_adapters/oracle_enhanced/procedures_spec.rb +7 -10
  38. data/spec/active_record/connection_adapters/oracle_enhanced/quoting_spec.rb +0 -15
  39. data/spec/active_record/connection_adapters/oracle_enhanced/schema_dumper_spec.rb +33 -36
  40. data/spec/active_record/connection_adapters/oracle_enhanced/schema_statements_spec.rb +77 -258
  41. data/spec/active_record/connection_adapters/oracle_enhanced/structure_dump_spec.rb +38 -39
  42. data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +273 -85
  43. data/spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb +7 -8
  44. data/spec/active_record/oracle_enhanced/type/boolean_spec.rb +2 -4
  45. data/spec/active_record/oracle_enhanced/type/character_string_spec.rb +43 -0
  46. data/spec/active_record/oracle_enhanced/type/custom_spec.rb +90 -0
  47. data/spec/active_record/oracle_enhanced/type/decimal_spec.rb +56 -0
  48. data/spec/active_record/oracle_enhanced/type/dirty_spec.rb +1 -1
  49. data/spec/active_record/oracle_enhanced/type/integer_spec.rb +2 -2
  50. data/spec/active_record/oracle_enhanced/type/json_spec.rb +0 -1
  51. data/spec/active_record/oracle_enhanced/type/national_character_string_spec.rb +6 -5
  52. data/spec/active_record/oracle_enhanced/type/timestamp_spec.rb +2 -4
  53. data/spec/spec_config.yaml.template +2 -2
  54. data/spec/spec_helper.rb +13 -2
  55. metadata +52 -30
  56. data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements_ext.rb +0 -28
@@ -5,7 +5,6 @@ module ActiveRecord
5
5
  module OracleEnhanced
6
6
  class Boolean < ActiveModel::Type::Boolean # :nodoc:
7
7
  private
8
-
9
8
  def cast_value(value)
10
9
  # Kind of adding 'n' and 'N' to `FALSE_VALUES`
11
10
  if ["n", "N"].include?(value)
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_model/type/string"
4
+
5
+ module ActiveRecord
6
+ module Type
7
+ module OracleEnhanced
8
+ class CharacterString < ActiveRecord::Type::OracleEnhanced::String # :nodoc:
9
+ def serialize(value)
10
+ return unless value
11
+ Data.new(super, self.limit)
12
+ end
13
+
14
+ class Data # :nodoc:
15
+ def initialize(value, limit)
16
+ @value = value
17
+ @limit = limit
18
+ end
19
+
20
+ def to_s
21
+ @value
22
+ end
23
+
24
+ def to_character_str
25
+ len = @value.to_s.length
26
+ if len < @limit
27
+ "%-#{@limit}s" % @value
28
+ else
29
+ @value
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -5,7 +5,6 @@ module ActiveRecord
5
5
  module OracleEnhanced
6
6
  class Integer < ActiveModel::Type::Integer # :nodoc:
7
7
  private
8
-
9
8
  def max_value
10
9
  ("9" * 38).to_i
11
10
  end
@@ -0,0 +1,221 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arel # :nodoc: all
4
+ module Visitors
5
+ class Oracle < Arel::Visitors::ToSql
6
+ private
7
+ def visit_Arel_Nodes_SelectStatement(o, collector)
8
+ o = order_hacks(o)
9
+
10
+ # if need to select first records without ORDER BY and GROUP BY and without DISTINCT
11
+ # then can use simple ROWNUM in WHERE clause
12
+ if o.limit && o.orders.empty? && o.cores.first.groups.empty? && !o.offset && !o.cores.first.set_quantifier.class.to_s.match?(/Distinct/)
13
+ o.cores.last.wheres.push Nodes::LessThanOrEqual.new(
14
+ Nodes::SqlLiteral.new("ROWNUM"), o.limit.expr
15
+ )
16
+ return super
17
+ end
18
+
19
+ if o.limit && o.offset
20
+ o = o.dup
21
+ limit = o.limit.expr
22
+ offset = o.offset
23
+ o.offset = nil
24
+ collector << "
25
+ SELECT * FROM (
26
+ SELECT raw_sql_.*, rownum raw_rnum_
27
+ FROM ("
28
+
29
+ collector = super(o, collector)
30
+
31
+ if offset.expr.type.is_a? ActiveModel::Type::Value
32
+ collector << ") raw_sql_ WHERE rownum <= ("
33
+ collector = visit offset.expr, collector
34
+ collector << " + "
35
+ collector = visit limit, collector
36
+ collector << ") ) WHERE raw_rnum_ > "
37
+ collector = visit offset.expr, collector
38
+ return collector
39
+ else
40
+ collector << ") raw_sql_
41
+ WHERE rownum <= #{offset.expr.value_before_type_cast + limit.value_before_type_cast}
42
+ )
43
+ WHERE "
44
+ return visit(offset, collector)
45
+ end
46
+ end
47
+
48
+ if o.limit
49
+ o = o.dup
50
+ limit = o.limit.expr
51
+ collector << "SELECT * FROM ("
52
+ collector = super(o, collector)
53
+ collector << ") WHERE ROWNUM <= "
54
+ return visit limit, collector
55
+ end
56
+
57
+ if o.offset
58
+ o = o.dup
59
+ offset = o.offset
60
+ o.offset = nil
61
+ collector << "SELECT * FROM (
62
+ SELECT raw_sql_.*, rownum raw_rnum_
63
+ FROM ("
64
+ collector = super(o, collector)
65
+ collector << ") raw_sql_
66
+ )
67
+ WHERE "
68
+ return visit offset, collector
69
+ end
70
+
71
+ super
72
+ end
73
+
74
+ def visit_Arel_Nodes_Limit(o, collector)
75
+ collector
76
+ end
77
+
78
+ def visit_Arel_Nodes_Offset(o, collector)
79
+ collector << "raw_rnum_ > "
80
+ visit o.expr, collector
81
+ end
82
+
83
+ def visit_Arel_Nodes_Except(o, collector)
84
+ collector << "( "
85
+ collector = infix_value o, collector, " MINUS "
86
+ collector << " )"
87
+ end
88
+
89
+ ##
90
+ # To avoid ORA-01795: maximum number of expressions in a list is 1000
91
+ # tell ActiveRecord to limit us to 1000 ids at a time
92
+ def visit_Arel_Nodes_HomogeneousIn(o, collector)
93
+ in_clause_length = @connection.in_clause_length
94
+ values = o.casted_values.map { |v| @connection.quote(v) }
95
+ column_name = quote_table_name(o.table_name) + "." + quote_column_name(o.column_name)
96
+ operator =
97
+ if o.type == :in
98
+ " IN ("
99
+ else
100
+ " NOT IN ("
101
+ end
102
+
103
+ if !Array === values || values.length <= in_clause_length
104
+ collector << column_name
105
+ collector << operator
106
+
107
+ expr =
108
+ if values.empty?
109
+ @connection.quote(nil)
110
+ else
111
+ values.join(",")
112
+ end
113
+
114
+ collector << expr
115
+ collector << ")"
116
+ else
117
+ separator =
118
+ if o.type == :in
119
+ " OR "
120
+ else
121
+ " AND "
122
+ end
123
+ collector << "("
124
+ values.each_slice(in_clause_length).each_with_index do |valuez, i|
125
+ collector << separator unless i == 0
126
+ collector << column_name
127
+ collector << operator
128
+ collector << valuez.join(",")
129
+ collector << ")"
130
+ end
131
+ collector << ")"
132
+ end
133
+
134
+ collector
135
+ end
136
+
137
+ def visit_Arel_Nodes_UpdateStatement(o, collector)
138
+ # Oracle does not allow ORDER BY/LIMIT in UPDATEs.
139
+ if o.orders.any? && o.limit.nil?
140
+ # However, there is no harm in silently eating the ORDER BY clause if no LIMIT has been provided,
141
+ # otherwise let the user deal with the error
142
+ o = o.dup
143
+ o.orders = []
144
+ end
145
+
146
+ super
147
+ end
148
+
149
+ ###
150
+ # Hacks for the order clauses specific to Oracle
151
+ def order_hacks(o)
152
+ return o if o.orders.empty?
153
+ return o unless o.cores.any? do |core|
154
+ core.projections.any? do |projection|
155
+ /FIRST_VALUE/ === projection
156
+ end
157
+ end
158
+ # Previous version with join and split broke ORDER BY clause
159
+ # if it contained functions with several arguments (separated by ',').
160
+ #
161
+ # orders = o.orders.map { |x| visit x }.join(', ').split(',')
162
+ orders = o.orders.map do |x|
163
+ string = visit(x, Arel::Collectors::SQLString.new).value
164
+ if string.include?(",")
165
+ split_order_string(string)
166
+ else
167
+ string
168
+ end
169
+ end.flatten
170
+ o.orders = []
171
+ orders.each_with_index do |order, i|
172
+ o.orders <<
173
+ Nodes::SqlLiteral.new("alias_#{i}__#{' DESC' if /\bdesc$/i.match?(order)}")
174
+ end
175
+ o
176
+ end
177
+
178
+ # Split string by commas but count opening and closing brackets
179
+ # and ignore commas inside brackets.
180
+ def split_order_string(string)
181
+ array = []
182
+ i = 0
183
+ string.split(",").each do |part|
184
+ if array[i]
185
+ array[i] << "," << part
186
+ else
187
+ # to ensure that array[i] will be String and not Arel::Nodes::SqlLiteral
188
+ array[i] = part.to_s
189
+ end
190
+ i += 1 if array[i].count("(") == array[i].count(")")
191
+ end
192
+ array
193
+ end
194
+
195
+ def visit_ActiveModel_Attribute(o, collector)
196
+ collector.add_bind(o) { |i| ":a#{i}" }
197
+ end
198
+
199
+ def visit_Arel_Nodes_BindParam(o, collector)
200
+ collector.add_bind(o.value) { |i| ":a#{i}" }
201
+ end
202
+
203
+ def is_distinct_from(o, collector)
204
+ collector << "DECODE("
205
+ collector = visit [o.left, o.right, 0, 1], collector
206
+ collector << ")"
207
+ end
208
+
209
+ # Oracle will occur an error `ORA-00907: missing right parenthesis`
210
+ # when using `ORDER BY` in `UPDATE` or `DELETE`'s subquery.
211
+ #
212
+ # This method has been overridden based on the following code.
213
+ # https://github.com/rails/rails/blob/v6.1.0.rc1/activerecord/lib/arel/visitors/to_sql.rb#L815-L825
214
+ def build_subselect(key, o)
215
+ stmt = super
216
+ stmt.orders = [] # `orders` will never be set to prevent `ORA-00907`.
217
+ stmt
218
+ end
219
+ end
220
+ end
221
+ end
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arel # :nodoc: all
4
+ module Visitors
5
+ class Oracle12 < Arel::Visitors::ToSql
6
+ private
7
+ def visit_Arel_Nodes_SelectStatement(o, collector)
8
+ # Oracle does not allow LIMIT clause with select for update
9
+ if o.limit && o.lock
10
+ raise ArgumentError, <<~MSG
11
+ Combination of limit and lock is not supported. Because generated SQL statements
12
+ `SELECT FOR UPDATE and FETCH FIRST n ROWS` generates ORA-02014.
13
+ MSG
14
+ end
15
+ super
16
+ end
17
+
18
+ def visit_Arel_Nodes_SelectOptions(o, collector)
19
+ collector = maybe_visit o.offset, collector
20
+ collector = maybe_visit o.limit, collector
21
+ maybe_visit o.lock, collector
22
+ end
23
+
24
+ def visit_Arel_Nodes_Limit(o, collector)
25
+ collector << "FETCH FIRST "
26
+ collector = visit o.expr, collector
27
+ collector << " ROWS ONLY"
28
+ end
29
+
30
+ def visit_Arel_Nodes_Offset(o, collector)
31
+ collector << "OFFSET "
32
+ visit o.expr, collector
33
+ collector << " ROWS"
34
+ end
35
+
36
+ def visit_Arel_Nodes_Except(o, collector)
37
+ collector << "( "
38
+ collector = infix_value o, collector, " MINUS "
39
+ collector << " )"
40
+ end
41
+
42
+ ##
43
+ # To avoid ORA-01795: maximum number of expressions in a list is 1000
44
+ # tell ActiveRecord to limit us to 1000 ids at a time
45
+ def visit_Arel_Nodes_HomogeneousIn(o, collector)
46
+ in_clause_length = @connection.in_clause_length
47
+ values = o.casted_values.map { |v| @connection.quote(v) }
48
+ column_name = quote_table_name(o.table_name) + "." + quote_column_name(o.column_name)
49
+ operator =
50
+ if o.type == :in
51
+ " IN ("
52
+ else
53
+ " NOT IN ("
54
+ end
55
+
56
+ if !Array === values || values.length <= in_clause_length
57
+ collector << column_name
58
+ collector << operator
59
+
60
+ expr =
61
+ if values.empty?
62
+ @connection.quote(nil)
63
+ else
64
+ values.join(",")
65
+ end
66
+
67
+ collector << expr
68
+ collector << ")"
69
+ else
70
+ separator =
71
+ if o.type == :in
72
+ " OR "
73
+ else
74
+ " AND "
75
+ end
76
+ collector << "("
77
+ values.each_slice(in_clause_length).each_with_index do |valuez, i|
78
+ collector << separator unless i == 0
79
+ collector << column_name
80
+ collector << operator
81
+ collector << valuez.join(",")
82
+ collector << ")"
83
+ end
84
+ collector << ")"
85
+ end
86
+
87
+ collector
88
+ end
89
+
90
+ def visit_Arel_Nodes_UpdateStatement(o, collector)
91
+ # Oracle does not allow ORDER BY/LIMIT in UPDATEs.
92
+ if o.orders.any? && o.limit.nil?
93
+ # However, there is no harm in silently eating the ORDER BY clause if no LIMIT has been provided,
94
+ # otherwise let the user deal with the error
95
+ o = o.dup
96
+ o.orders = []
97
+ end
98
+
99
+ super
100
+ end
101
+
102
+ def visit_ActiveModel_Attribute(o, collector)
103
+ collector.add_bind(o) { |i| ":a#{i}" }
104
+ end
105
+
106
+ def visit_Arel_Nodes_BindParam(o, collector)
107
+ collector.add_bind(o.value) { |i| ":a#{i}" }
108
+ end
109
+
110
+ def is_distinct_from(o, collector)
111
+ collector << "DECODE("
112
+ collector = visit [o.left, o.right, 0, 1], collector
113
+ collector << ")"
114
+ end
115
+
116
+ # Oracle will occur an error `ORA-00907: missing right parenthesis`
117
+ # when using `ORDER BY` in `UPDATE` or `DELETE`'s subquery.
118
+ #
119
+ # This method has been overridden based on the following code.
120
+ # https://github.com/rails/rails/blob/v6.1.0.rc1/activerecord/lib/arel/visitors/to_sql.rb#L815-L825
121
+ def build_subselect(key, o)
122
+ stmt = super
123
+ stmt.orders = [] # `orders` will never be set to prevent `ORA-00907`.
124
+ stmt
125
+ end
126
+ end
127
+ end
128
+ end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  describe "OracleEnhancedAdapter emulate OracleAdapter" do
4
-
5
4
  before(:all) do
6
5
  @old_oracle_adapter = nil
7
6
  if defined?(ActiveRecord::ConnectionAdapters::OracleAdapter)
@@ -22,5 +21,4 @@ describe "OracleEnhancedAdapter emulate OracleAdapter" do
22
21
  ActiveRecord::ConnectionAdapters::OracleAdapter = @old_oracle_adapter
23
22
  end
24
23
  end
25
-
26
24
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  describe "OracleEnhancedAdapter establish connection" do
4
-
5
4
  it "should connect to database" do
6
5
  ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
7
6
  expect(ActiveRecord::Base.connection).not_to be_nil
@@ -41,10 +40,32 @@ describe "OracleEnhancedAdapter establish connection" do
41
40
  ActiveRecord::Base.establish_connection(SYSTEM_CONNECTION_PARAMS.merge(cursor_sharing: :exact))
42
41
  expect(ActiveRecord::Base.connection.select_value("select value from v$parameter where name = 'cursor_sharing'")).to eq("EXACT")
43
42
  end
43
+
44
+ it "should not use JDBC statement caching" do
45
+ if ORACLE_ENHANCED_CONNECTION == :jdbc
46
+ ActiveRecord::Base.establish_connection(SYSTEM_CONNECTION_PARAMS)
47
+ expect(ActiveRecord::Base.connection.raw_connection.getImplicitCachingEnabled).to eq(false)
48
+ expect(ActiveRecord::Base.connection.raw_connection.getStatementCacheSize).to eq(-1)
49
+ end
50
+ end
51
+
52
+ it "should use JDBC statement caching" do
53
+ if ORACLE_ENHANCED_CONNECTION == :jdbc
54
+ ActiveRecord::Base.establish_connection(SYSTEM_CONNECTION_PARAMS.merge(jdbc_statement_cache_size: 100))
55
+ expect(ActiveRecord::Base.connection.raw_connection.getImplicitCachingEnabled).to eq(true)
56
+ expect(ActiveRecord::Base.connection.raw_connection.getStatementCacheSize).to eq(100)
57
+ # else: don't raise error if OCI connection has parameter "jdbc_statement_cache_size", still ignore it
58
+ end
59
+ end
60
+
61
+ it "should connect to database using service_name" do
62
+ ActiveRecord::Base.establish_connection(SERVICE_NAME_CONNECTION_PARAMS)
63
+ expect(ActiveRecord::Base.connection).not_to be_nil
64
+ expect(ActiveRecord::Base.connection.class).to eq(ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter)
65
+ end
44
66
  end
45
67
 
46
68
  describe "OracleEnhancedConnection" do
47
-
48
69
  describe "create connection" do
49
70
  before(:all) do
50
71
  @conn = ActiveRecord::ConnectionAdapters::OracleEnhanced::Connection.create(CONNECTION_PARAMS)
@@ -75,49 +96,64 @@ describe "OracleEnhancedConnection" do
75
96
  it "should be in autocommit mode after connection" do
76
97
  expect(@conn).to be_autocommit
77
98
  end
78
-
79
99
  end
80
100
 
81
101
  describe "create connection with schema option" do
82
-
83
102
  it "should create new connection" do
84
103
  ActiveRecord::Base.establish_connection(CONNECTION_WITH_SCHEMA_PARAMS)
85
104
  expect(ActiveRecord::Base.connection).to be_active
86
105
  end
87
106
 
88
- it "should swith to specified schema" do
107
+ it "should switch to specified schema" do
89
108
  ActiveRecord::Base.establish_connection(CONNECTION_WITH_SCHEMA_PARAMS)
90
109
  expect(ActiveRecord::Base.connection.current_schema).to eq(CONNECTION_WITH_SCHEMA_PARAMS[:schema].upcase)
110
+ expect(ActiveRecord::Base.connection.current_user).to eq(CONNECTION_WITH_SCHEMA_PARAMS[:username].upcase)
91
111
  end
92
112
 
93
- it "should swith to specified schema after reset" do
113
+ it "should switch to specified schema after reset" do
94
114
  ActiveRecord::Base.connection.reset!
95
115
  expect(ActiveRecord::Base.connection.current_schema).to eq(CONNECTION_WITH_SCHEMA_PARAMS[:schema].upcase)
96
116
  end
97
-
98
117
  end
99
118
 
100
119
  describe "create connection with NLS parameters" do
120
+ after do
121
+ ENV["NLS_TERRITORY"] = nil
122
+ end
123
+
124
+ it "should use NLS_TERRITORY environment variable" do
125
+ ENV["NLS_TERRITORY"] = "JAPAN"
126
+ @conn = ActiveRecord::ConnectionAdapters::OracleEnhanced::Connection.create(CONNECTION_PARAMS)
127
+ expect(@conn.select("select SYS_CONTEXT('userenv', 'NLS_TERRITORY') as value from dual")).to eq([{ "value" => "JAPAN" }])
128
+ end
129
+
130
+ it "should use configuration value and ignore NLS_TERRITORY environment variable" do
131
+ ENV["NLS_TERRITORY"] = "AMERICA"
132
+ @conn = ActiveRecord::ConnectionAdapters::OracleEnhanced::Connection.create(CONNECTION_PARAMS.merge(nls_territory: "INDONESIA"))
133
+ expect(@conn.select("select SYS_CONTEXT('userenv', 'NLS_TERRITORY') as value from dual")).to eq([{ "value" => "INDONESIA" }])
134
+ end
135
+ end
136
+
137
+ describe "Fixed NLS parameters" do
101
138
  after do
102
139
  ENV["NLS_DATE_FORMAT"] = nil
103
140
  end
104
141
 
105
- it "should use NLS_DATE_FORMAT environment variable" do
142
+ it "should ignore NLS_DATE_FORMAT environment variable" do
106
143
  ENV["NLS_DATE_FORMAT"] = "YYYY-MM-DD"
107
144
  @conn = ActiveRecord::ConnectionAdapters::OracleEnhanced::Connection.create(CONNECTION_PARAMS)
108
- expect(@conn.select("select SYS_CONTEXT('userenv', 'NLS_DATE_FORMAT') as value from dual")).to eq([{ "value" => "YYYY-MM-DD" }])
145
+ expect(@conn.select("select SYS_CONTEXT('userenv', 'NLS_DATE_FORMAT') as value from dual")).to eq([{ "value" => "YYYY-MM-DD HH24:MI:SS" }])
109
146
  end
110
147
 
111
- it "should use configuration value and ignore NLS_DATE_FORMAT environment variable" do
112
- ENV["NLS_DATE_FORMAT"] = "YYYY-MM-DD"
148
+ it "should ignore NLS_DATE_FORMAT configuration value" do
113
149
  @conn = ActiveRecord::ConnectionAdapters::OracleEnhanced::Connection.create(CONNECTION_PARAMS.merge(nls_date_format: "YYYY-MM-DD HH24:MI"))
114
- expect(@conn.select("select SYS_CONTEXT('userenv', 'NLS_DATE_FORMAT') as value from dual")).to eq([{ "value" => "YYYY-MM-DD HH24:MI" }])
150
+ expect(@conn.select("select SYS_CONTEXT('userenv', 'NLS_DATE_FORMAT') as value from dual")).to eq([{ "value" => "YYYY-MM-DD HH24:MI:SS" }])
115
151
  end
116
152
 
117
153
  it "should use default value when NLS_DATE_FORMAT environment variable is not set" do
118
154
  ENV["NLS_DATE_FORMAT"] = nil
119
155
  @conn = ActiveRecord::ConnectionAdapters::OracleEnhanced::Connection.create(CONNECTION_PARAMS)
120
- default = ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter::DEFAULT_NLS_PARAMETERS[:nls_date_format]
156
+ default = ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter::FIXED_NLS_PARAMETERS[:nls_date_format]
121
157
  expect(@conn.select("select SYS_CONTEXT('userenv', 'NLS_DATE_FORMAT') as value from dual")).to eq([{ "value" => default }])
122
158
  end
123
159
  end
@@ -167,7 +203,7 @@ describe "OracleEnhancedConnection" do
167
203
  describe "with slash-prefixed database name (service name)" do
168
204
  before(:all) do
169
205
  params = CONNECTION_PARAMS.dup
170
- params[:database] = "/#{params[:database]}" unless params[:database].match(/^\//)
206
+ params[:database] = "/#{params[:database]}" unless params[:database].start_with?("/")
171
207
  @conn = ActiveRecord::ConnectionAdapters::OracleEnhanced::Connection.create(params)
172
208
  end
173
209
 
@@ -196,13 +232,12 @@ describe "OracleEnhancedConnection" do
196
232
  end
197
233
 
198
234
  it "should respect default_timezone = :utc than time_zone setting" do
199
- # it expects that ActiveRecord::Base.default_timezone = :utc
235
+ # it expects that ActiveRecord.default_timezone = :utc
200
236
  ActiveRecord::ConnectionAdapters::OracleEnhanced::Connection.create(CONNECTION_WITH_TIMEZONE_PARAMS)
201
237
  post = Post.create!
202
238
  created_at = post.created_at
203
239
  expect(post).to eq(Post.find_by!(created_at: created_at))
204
240
  end
205
-
206
241
  end
207
242
 
208
243
  describe 'with host="connection-string"' do
@@ -225,7 +260,6 @@ describe "OracleEnhancedConnection" do
225
260
  if defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby"
226
261
 
227
262
  describe "create JDBC connection" do
228
-
229
263
  it "should create new connection using :url" do
230
264
  params = CONNECTION_PARAMS.dup
231
265
  params[:url] = "jdbc:oracle:thin:@#{DATABASE_HOST && "//#{DATABASE_HOST}#{DATABASE_PORT && ":#{DATABASE_PORT}"}/"}#{DATABASE_NAME}"
@@ -253,7 +287,6 @@ describe "OracleEnhancedConnection" do
253
287
  end
254
288
 
255
289
  it "should create a new connection using JNDI" do
256
-
257
290
  begin
258
291
  import "oracle.jdbc.driver.OracleDriver"
259
292
  import "org.apache.commons.pool.impl.GenericObjectPool"
@@ -274,7 +307,7 @@ describe "OracleEnhancedConnection" do
274
307
  @data_source.access_to_underlying_connection_allowed = true
275
308
  end
276
309
  def lookup(path)
277
- if (path == "java:/comp/env")
310
+ if path == "java:/comp/env"
278
311
  self
279
312
  else
280
313
  @data_source
@@ -289,7 +322,6 @@ describe "OracleEnhancedConnection" do
289
322
  @conn = ActiveRecord::ConnectionAdapters::OracleEnhanced::Connection.create(params)
290
323
  expect(@conn).to be_active
291
324
  end
292
-
293
325
  end
294
326
 
295
327
  it "should fall back to directly instantiating OracleDriver" do
@@ -320,7 +352,6 @@ describe "OracleEnhancedConnection" do
320
352
  it "should execute SQL select and return also columns" do
321
353
  expect(@conn.select("SELECT * FROM dual", nil, true)).to eq([ [{ "dummy" => "X" }], ["dummy"] ])
322
354
  end
323
-
324
355
  end
325
356
 
326
357
  describe "SQL with bind parameters" do
@@ -364,7 +395,7 @@ describe "OracleEnhancedConnection" do
364
395
  it "should execute prepared statement with decimal bind parameter " do
365
396
  cursor = @conn.prepare("INSERT INTO test_employees VALUES(:1)")
366
397
  type_metadata = ActiveRecord::ConnectionAdapters::SqlTypeMetadata.new(sql_type: "NUMBER", type: :decimal, limit: 10, precision: nil, scale: 2)
367
- column = ActiveRecord::ConnectionAdapters::OracleEnhanced::Column.new("age", nil, type_metadata, false, "test_employees", nil)
398
+ column = ActiveRecord::ConnectionAdapters::OracleEnhanced::Column.new("age", nil, type_metadata, false, comment: nil)
368
399
  expect(column.type).to eq(:decimal)
369
400
  # Here 1.5 expects that this value has been type casted already
370
401
  # it should use bind_params in the long term.
@@ -379,10 +410,22 @@ describe "OracleEnhancedConnection" do
379
410
  end
380
411
 
381
412
  describe "auto reconnection" do
413
+ include SchemaSpecHelper
414
+
382
415
  before(:all) do
383
416
  ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
384
417
  @conn = ActiveRecord::Base.connection.instance_variable_get("@connection")
385
418
  @sys_conn = ActiveRecord::ConnectionAdapters::OracleEnhanced::Connection.create(SYS_CONNECTION_PARAMS)
419
+ schema_define do
420
+ create_table :posts, force: true
421
+ end
422
+ class ::Post < ActiveRecord::Base
423
+ end
424
+ end
425
+
426
+ after(:all) do
427
+ Object.send(:remove_const, "Post")
428
+ ActiveRecord::Base.clear_cache!
386
429
  end
387
430
 
388
431
  before(:each) do
@@ -433,6 +476,19 @@ describe "OracleEnhancedConnection" do
433
476
  end
434
477
  end
435
478
 
479
+ it "should reconnect and execute query if connection is lost and auto retry is enabled" do
480
+ Post.create!
481
+ ActiveRecord::Base.connection.auto_retry = true
482
+ kill_current_session
483
+ expect(Post.take).not_to be_nil
484
+ end
485
+
486
+ it "should not reconnect and execute query if connection is lost and auto retry is disabled" do
487
+ Post.create!
488
+ ActiveRecord::Base.connection.auto_retry = false
489
+ kill_current_session
490
+ expect { Post.take }.to raise_error(ActiveRecord::StatementInvalid)
491
+ end
436
492
  end
437
493
 
438
494
  describe "describe table" do
@@ -483,15 +539,11 @@ describe "OracleEnhancedConnection" do
483
539
 
484
540
  if defined?(OCI8)
485
541
  context "OCI8 adapter" do
486
-
487
542
  it "should not fallback to SELECT-based logic when querying non-existent table information" do
488
543
  expect(@conn).not_to receive(:select_one)
489
544
  @conn.describe("non_existent") rescue ActiveRecord::ConnectionAdapters::OracleEnhanced::ConnectionException
490
545
  end
491
-
492
546
  end
493
547
  end
494
-
495
548
  end
496
-
497
549
  end