activerecord-oracle_enhanced-adapter 1.8.2 → 5.2.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +5 -5
  2. data/History.md +190 -5
  3. data/README.md +10 -10
  4. data/VERSION +1 -1
  5. data/lib/active_record/connection_adapters/emulation/oracle_adapter.rb +2 -0
  6. data/lib/active_record/connection_adapters/oracle_enhanced/column.rb +9 -71
  7. data/lib/active_record/connection_adapters/oracle_enhanced/connection.rb +84 -73
  8. data/lib/active_record/connection_adapters/oracle_enhanced/context_index.rb +12 -12
  9. data/lib/active_record/connection_adapters/oracle_enhanced/database_limits.rb +52 -0
  10. data/lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb +35 -7
  11. data/lib/active_record/connection_adapters/oracle_enhanced/database_tasks.rb +2 -0
  12. data/lib/active_record/connection_adapters/oracle_enhanced/dbms_output.rb +59 -0
  13. data/lib/active_record/connection_adapters/oracle_enhanced/jdbc_connection.rb +379 -402
  14. data/lib/active_record/connection_adapters/oracle_enhanced/jdbc_quoting.rb +7 -1
  15. data/lib/active_record/connection_adapters/oracle_enhanced/lob.rb +46 -0
  16. data/lib/active_record/connection_adapters/oracle_enhanced/oci_connection.rb +242 -247
  17. data/lib/active_record/connection_adapters/oracle_enhanced/oci_quoting.rb +9 -1
  18. data/lib/active_record/connection_adapters/oracle_enhanced/procedures.rb +3 -1
  19. data/lib/active_record/connection_adapters/oracle_enhanced/quoting.rb +25 -9
  20. data/lib/active_record/connection_adapters/oracle_enhanced/schema_creation.rb +9 -6
  21. data/lib/active_record/connection_adapters/oracle_enhanced/schema_definitions.rb +10 -5
  22. data/lib/active_record/connection_adapters/oracle_enhanced/schema_dumper.rb +48 -51
  23. data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb +261 -59
  24. data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements_ext.rb +2 -34
  25. data/lib/active_record/connection_adapters/oracle_enhanced/structure_dump.rb +267 -222
  26. data/lib/active_record/connection_adapters/oracle_enhanced/type_metadata.rb +33 -0
  27. data/lib/active_record/connection_adapters/oracle_enhanced/version.rb +2 -0
  28. data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +136 -547
  29. data/lib/active_record/{oracle_enhanced/type → type/oracle_enhanced}/boolean.rb +4 -2
  30. data/lib/active_record/{oracle_enhanced/type → type/oracle_enhanced}/integer.rb +4 -2
  31. data/lib/active_record/type/oracle_enhanced/json.rb +10 -0
  32. data/lib/active_record/{oracle_enhanced/type → type/oracle_enhanced}/national_character_string.rb +5 -3
  33. data/lib/active_record/type/oracle_enhanced/national_character_text.rb +36 -0
  34. data/lib/active_record/{oracle_enhanced/type → type/oracle_enhanced}/raw.rb +4 -2
  35. data/lib/active_record/{oracle_enhanced/type → type/oracle_enhanced}/string.rb +4 -2
  36. data/lib/active_record/{oracle_enhanced/type → type/oracle_enhanced}/text.rb +4 -2
  37. data/lib/active_record/type/oracle_enhanced/timestampltz.rb +25 -0
  38. data/lib/active_record/{oracle_enhanced/type → type/oracle_enhanced}/timestamptz.rb +4 -2
  39. data/lib/activerecord-oracle_enhanced-adapter.rb +2 -6
  40. data/spec/active_record/connection_adapters/{oracle_enhanced_emulate_oracle_adapter_spec.rb → emulation/oracle_adapter_spec.rb} +2 -0
  41. data/spec/active_record/connection_adapters/{oracle_enhanced_connection_spec.rb → oracle_enhanced/connection_spec.rb} +82 -38
  42. data/spec/active_record/connection_adapters/{oracle_enhanced_context_index_spec.rb → oracle_enhanced/context_index_spec.rb} +20 -16
  43. data/spec/active_record/connection_adapters/{oracle_enhanced_database_tasks_spec.rb → oracle_enhanced/database_tasks_spec.rb} +17 -5
  44. data/spec/active_record/connection_adapters/{oracle_enhanced_dbms_output_spec.rb → oracle_enhanced/dbms_output_spec.rb} +2 -0
  45. data/spec/active_record/connection_adapters/{oracle_enhanced_procedures_spec.rb → oracle_enhanced/procedures_spec.rb} +26 -33
  46. data/spec/active_record/connection_adapters/oracle_enhanced/quoting_spec.rb +196 -0
  47. data/spec/active_record/connection_adapters/{oracle_enhanced_schema_dump_spec.rb → oracle_enhanced/schema_dumper_spec.rb} +61 -90
  48. data/spec/active_record/connection_adapters/{oracle_enhanced_schema_statements_spec.rb → oracle_enhanced/schema_statements_spec.rb} +95 -28
  49. data/spec/active_record/connection_adapters/{oracle_enhanced_structure_dump_spec.rb → oracle_enhanced/structure_dump_spec.rb} +48 -2
  50. data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +202 -331
  51. data/spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb +15 -1106
  52. data/spec/active_record/oracle_enhanced/type/binary_spec.rb +119 -0
  53. data/spec/active_record/oracle_enhanced/type/boolean_spec.rb +207 -0
  54. data/spec/active_record/{connection_adapters/oracle_enhanced_dirty_spec.rb → oracle_enhanced/type/dirty_spec.rb} +3 -1
  55. data/spec/active_record/oracle_enhanced/type/float_spec.rb +48 -0
  56. data/spec/active_record/oracle_enhanced/type/integer_spec.rb +91 -0
  57. data/spec/active_record/oracle_enhanced/type/json_spec.rb +57 -0
  58. data/spec/active_record/oracle_enhanced/type/national_character_string_spec.rb +55 -0
  59. data/spec/active_record/oracle_enhanced/type/national_character_text_spec.rb +230 -0
  60. data/spec/active_record/oracle_enhanced/type/raw_spec.rb +122 -0
  61. data/spec/active_record/oracle_enhanced/type/text_spec.rb +229 -0
  62. data/spec/active_record/oracle_enhanced/type/timestamp_spec.rb +75 -0
  63. data/spec/spec_helper.rb +15 -1
  64. data/spec/support/alter_system_set_open_cursors.sql +1 -0
  65. metadata +63 -48
  66. data/lib/active_record/connection_adapters/oracle_enhanced/column_dumper.rb +0 -28
  67. data/lib/active_record/oracle_enhanced/type/json.rb +0 -8
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters
3
5
  module OracleEnhanced
@@ -72,7 +74,6 @@ module ActiveRecord
72
74
  # add_context_index :posts, :title, :transactional => true
73
75
  #
74
76
  def add_context_index(table_name, column_name, options = {})
75
- self.all_schema_indexes = nil
76
77
  column_names = Array(column_name)
77
78
  index_name = options[:name] || index_name(table_name, column: options[:index_column] || column_names,
78
79
  # CONEXT index name max length is 25
@@ -85,7 +86,7 @@ module ActiveRecord
85
86
  create_index_column_trigger(table_name, index_name, options[:index_column], options[:index_column_trigger_on])
86
87
  end
87
88
 
88
- sql = "CREATE INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
89
+ sql = "CREATE INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}".dup
89
90
  sql << " (#{quoted_column_name})"
90
91
  sql << " INDEXTYPE IS CTXSYS.CONTEXT"
91
92
  parameters = []
@@ -127,7 +128,6 @@ module ActiveRecord
127
128
 
128
129
  # Drop full text index with Oracle specific CONTEXT index type
129
130
  def remove_context_index(table_name, options = {})
130
- self.all_schema_indexes = nil
131
131
  unless Hash === options # if column names passed as argument
132
132
  options = { column: Array(options) }
133
133
  end
@@ -149,11 +149,11 @@ module ActiveRecord
149
149
  select_queries = select_queries.map { |s| s.strip.gsub(/\s+/, " ") }
150
150
  keys, selected_columns = parse_select_queries(select_queries)
151
151
  quoted_column_names = (column_names + keys).map { |col| quote_column_name(col) }
152
- execute compress_lines(<<-SQL)
152
+ execute <<-SQL
153
153
  CREATE OR REPLACE PROCEDURE #{quote_table_name(procedure_name)}
154
154
  (p_rowid IN ROWID,
155
155
  p_clob IN OUT NOCOPY CLOB) IS
156
- -- add_context_index_parameters #{(column_names + select_queries).inspect}#{!options.empty? ? ', ' << options.inspect[1..-2] : ''}
156
+ -- add_context_index_parameters #{(column_names + select_queries).inspect}#{!options.empty? ? ', '.dup << options.inspect[1..-2] : ''}
157
157
  #{
158
158
  selected_columns.map do |cols|
159
159
  cols.map do |col|
@@ -170,7 +170,7 @@ module ActiveRecord
170
170
  #{
171
171
  (column_names.map do |col|
172
172
  col = col.to_s
173
- "DBMS_LOB.WRITEAPPEND(p_clob, #{col.length + 2}, '<#{col}>');\n" <<
173
+ "DBMS_LOB.WRITEAPPEND(p_clob, #{col.length + 2}, '<#{col}>');\n".dup <<
174
174
  "IF LENGTH(r1.#{col}) > 0 THEN\n" <<
175
175
  "DBMS_LOB.WRITEAPPEND(p_clob, LENGTH(r1.#{col}), r1.#{col});\n" <<
176
176
  "END IF;\n" <<
@@ -188,7 +188,7 @@ module ActiveRecord
188
188
  "END LOOP;\n" <<
189
189
  (cols.map do |col|
190
190
  col = col.to_s
191
- "DBMS_LOB.WRITEAPPEND(p_clob, #{col.length + 2}, '<#{col}>');\n" <<
191
+ "DBMS_LOB.WRITEAPPEND(p_clob, #{col.length + 2}, '<#{col}>');\n".dup <<
192
192
  "IF LENGTH(l_#{col}) > 0 THEN\n" <<
193
193
  "DBMS_LOB.WRITEAPPEND(p_clob, LENGTH(l_#{col}), l_#{col});\n" <<
194
194
  "END IF;\n" <<
@@ -225,7 +225,7 @@ module ActiveRecord
225
225
 
226
226
  def create_storage_preference(storage_name, tablespace)
227
227
  drop_ctx_preference(storage_name)
228
- sql = "BEGIN\nCTX_DDL.CREATE_PREFERENCE('#{storage_name}', 'BASIC_STORAGE');\n"
228
+ sql = "BEGIN\nCTX_DDL.CREATE_PREFERENCE('#{storage_name}', 'BASIC_STORAGE');\n".dup
229
229
  ["I_TABLE_CLAUSE", "K_TABLE_CLAUSE", "R_TABLE_CLAUSE",
230
230
  "N_TABLE_CLAUSE", "I_INDEX_CLAUSE", "P_TABLE_CLAUSE"].each do |clause|
231
231
  default_clause = case clause
@@ -241,7 +241,7 @@ module ActiveRecord
241
241
 
242
242
  def create_lexer_preference(lexer_name, lexer_type, options)
243
243
  drop_ctx_preference(lexer_name)
244
- sql = "BEGIN\nCTX_DDL.CREATE_PREFERENCE('#{lexer_name}', '#{lexer_type}');\n"
244
+ sql = "BEGIN\nCTX_DDL.CREATE_PREFERENCE('#{lexer_name}', '#{lexer_type}');\n".dup
245
245
  options.each do |key, value|
246
246
  plsql_value = case value
247
247
  when String; "'#{value}'"
@@ -258,7 +258,7 @@ module ActiveRecord
258
258
 
259
259
  def create_wordlist_preference(wordlist_name, wordlist_type, options)
260
260
  drop_ctx_preference(wordlist_name)
261
- sql = "BEGIN\nCTX_DDL.CREATE_PREFERENCE('#{wordlist_name}', '#{wordlist_type}');\n"
261
+ sql = "BEGIN\nCTX_DDL.CREATE_PREFERENCE('#{wordlist_name}', '#{wordlist_type}');\n".dup
262
262
  options.each do |key, value|
263
263
  plsql_value = case value
264
264
  when String; "'#{value}'"
@@ -281,7 +281,7 @@ module ActiveRecord
281
281
  trigger_name = default_index_column_trigger_name(index_name)
282
282
  columns = Array(index_column_source)
283
283
  quoted_column_names = columns.map { |col| quote_column_name(col) }.join(", ")
284
- execute compress_lines(<<-SQL)
284
+ execute <<-SQL
285
285
  CREATE OR REPLACE TRIGGER #{quote_table_name(trigger_name)}
286
286
  BEFORE UPDATE OF #{quoted_column_names} ON #{quote_table_name(table_name)} FOR EACH ROW
287
287
  BEGIN
@@ -332,7 +332,7 @@ module ActiveRecord
332
332
  def contains(column, query, options = {})
333
333
  score_label = options[:label].to_i || 1
334
334
  where("CONTAINS(#{connection.quote_table_name(column)}, ?, #{score_label}) > 0", query).
335
- order("SCORE(#{score_label}) DESC")
335
+ order(Arel.sql("SCORE(#{score_label}) DESC"))
336
336
  end
337
337
  end
338
338
  end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module OracleEnhanced
6
+ module DatabaseLimits
7
+ # maximum length of Oracle identifiers
8
+ IDENTIFIER_MAX_LENGTH = 30
9
+
10
+ def table_alias_length #:nodoc:
11
+ IDENTIFIER_MAX_LENGTH
12
+ end
13
+
14
+ # the maximum length of a table name
15
+ def table_name_length
16
+ IDENTIFIER_MAX_LENGTH
17
+ end
18
+
19
+ # the maximum length of a column name
20
+ def column_name_length
21
+ IDENTIFIER_MAX_LENGTH
22
+ end
23
+
24
+ # Returns the maximum allowed length for an index name. This
25
+ # limit is enforced by rails and Is less than or equal to
26
+ # <tt>index_name_length</tt>. The gap between
27
+ # <tt>index_name_length</tt> is to allow internal rails
28
+ # opreations to use prefixes in temporary opreations.
29
+ def allowed_index_name_length
30
+ index_name_length
31
+ end
32
+
33
+ # the maximum length of an index name
34
+ # supported by this database
35
+ def index_name_length
36
+ IDENTIFIER_MAX_LENGTH
37
+ end
38
+
39
+ # the maximum length of a sequence name
40
+ def sequence_name_length
41
+ IDENTIFIER_MAX_LENGTH
42
+ end
43
+
44
+ # To avoid ORA-01795: maximum number of expressions in a list is 1000
45
+ # tell ActiveRecord to limit us to 1000 ids at a time
46
+ def in_clause_length
47
+ 1000
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters
3
5
  module OracleEnhanced
@@ -42,7 +44,7 @@ module ActiveRecord
42
44
  res = true
43
45
  else
44
46
  columns = cursor.get_col_names.map do |col_name|
45
- @connection.oracle_downcase(col_name)
47
+ oracle_downcase(col_name)
46
48
  end
47
49
  rows = []
48
50
  fetch_options = { get_lob_value: (name != "Writable Large Object") }
@@ -57,10 +59,6 @@ module ActiveRecord
57
59
  end
58
60
  end
59
61
 
60
- def supports_statement_cache?
61
- true
62
- end
63
-
64
62
  def supports_explain?
65
63
  true
66
64
  end
@@ -81,7 +79,7 @@ module ActiveRecord
81
79
  def sql_for_insert(sql, pk, id_value, sequence_name, binds)
82
80
  unless id_value || pk == false || pk.nil? || pk.is_a?(Array)
83
81
  sql = "#{sql} RETURNING #{quote_column_name(pk)} INTO :returning_id"
84
- (binds = binds.dup) << ActiveRecord::Relation::QueryAttribute.new("returning_id", nil, ActiveRecord::OracleEnhanced::Type::Integer.new)
82
+ (binds = binds.dup) << ActiveRecord::Relation::QueryAttribute.new("returning_id", nil, Type::OracleEnhanced::Integer.new)
85
83
  end
86
84
  super
87
85
  end
@@ -113,7 +111,7 @@ module ActiveRecord
113
111
  if sql =~ /:returning_id/
114
112
  # it currently expects that returning_id comes last part of binds
115
113
  returning_id_index = binds.size
116
- cursor.bind_returning_param(returning_id_index, Integer) if ORACLE_ENHANCED_CONNECTION == :jdbc
114
+ cursor.bind_returning_param(returning_id_index, Integer)
117
115
  end
118
116
 
119
117
  cached = true
@@ -224,12 +222,42 @@ module ActiveRecord
224
222
  end
225
223
  end
226
224
 
225
+ # fallback to non bulk fixture insert
226
+ def insert_fixtures(fixtures, table_name)
227
+ fixtures.each do |fixture|
228
+ insert_fixture(fixture, table_name)
229
+ end
230
+ end
231
+
227
232
  # Oracle Database does not support this feature
228
233
  # Refer https://community.oracle.com/ideas/13845 and consider to vote
229
234
  # if you need this feature.
230
235
  def empty_insert_statement_value
231
236
  raise NotImplementedError
232
237
  end
238
+
239
+ # Writes LOB values from attributes for specified columns
240
+ def write_lobs(table_name, klass, attributes, columns) #:nodoc:
241
+ id = quote(attributes[klass.primary_key])
242
+ columns.each do |col|
243
+ value = attributes[col.name]
244
+ # changed sequence of next two lines - should check if value is nil before converting to yaml
245
+ next if value.blank?
246
+ if klass.attribute_types[col.name].is_a? Type::Serialized
247
+ value = klass.attribute_types[col.name].serialize(value)
248
+ end
249
+ uncached do
250
+ unless lob_record = select_one(<<-SQL.strip.gsub(/\s+/, " "), "Writable Large Object")
251
+ SELECT #{quote_column_name(col.name)} FROM #{quote_table_name(table_name)}
252
+ WHERE #{quote_column_name(klass.primary_key)} = #{id} FOR UPDATE
253
+ SQL
254
+ raise ActiveRecord::RecordNotFound, "statement #{sql} returned no rows"
255
+ end
256
+ lob = lob_record[col.name]
257
+ @connection.write_lob(lob, value.to_s, col.type == :binary)
258
+ end
259
+ end
260
+ end
233
261
  end
234
262
  end
235
263
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_record/base"
2
4
 
3
5
  module ActiveRecord
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module OracleEnhanced
6
+ module DbmsOutput
7
+ # DBMS_OUTPUT =============================================
8
+ #
9
+ # PL/SQL in Oracle uses dbms_output for logging print statements
10
+ # These methods stick that output into the Rails log so Ruby and PL/SQL
11
+ # code can can be debugged together in a single application
12
+
13
+ # Maximum DBMS_OUTPUT buffer size
14
+ DBMS_OUTPUT_BUFFER_SIZE = 10000 # can be 1-1000000
15
+
16
+ # Turn DBMS_Output logging on
17
+ def enable_dbms_output
18
+ set_dbms_output_plsql_connection
19
+ @enable_dbms_output = true
20
+ plsql(:dbms_output).sys.dbms_output.enable(DBMS_OUTPUT_BUFFER_SIZE)
21
+ end
22
+ # Turn DBMS_Output logging off
23
+ def disable_dbms_output
24
+ set_dbms_output_plsql_connection
25
+ @enable_dbms_output = false
26
+ plsql(:dbms_output).sys.dbms_output.disable
27
+ end
28
+ # Is DBMS_Output logging enabled?
29
+ def dbms_output_enabled?
30
+ @enable_dbms_output
31
+ end
32
+
33
+ private
34
+
35
+ def log(sql, name = "SQL", binds = [], type_casted_binds = [], statement_name = nil)
36
+ super
37
+ ensure
38
+ log_dbms_output if dbms_output_enabled?
39
+ end
40
+
41
+ def set_dbms_output_plsql_connection
42
+ raise OracleEnhanced::ConnectionException, "ruby-plsql gem is required for logging DBMS output" unless self.respond_to?(:plsql)
43
+ # do not reset plsql connection if it is the same (as resetting will clear PL/SQL metadata cache)
44
+ unless plsql(:dbms_output).connection && plsql(:dbms_output).connection.raw_connection == raw_connection
45
+ plsql(:dbms_output).connection = raw_connection
46
+ end
47
+ end
48
+
49
+ def log_dbms_output
50
+ while true do
51
+ result = plsql(:dbms_output).sys.dbms_output.get_line(line: "", status: 0)
52
+ break unless result[:status] == 0
53
+ @logger.debug "DBMS_OUTPUT: #{result[:line]}" if @logger
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  begin
2
4
  require "java"
3
5
  require "jruby"
@@ -48,495 +50,463 @@ end
48
50
  module ActiveRecord
49
51
  module ConnectionAdapters
50
52
  # JDBC database interface for JRuby
51
- class OracleEnhancedJDBCConnection < OracleEnhancedConnection #:nodoc:
52
- attr_accessor :active
53
- alias :active? :active
54
-
55
- attr_accessor :auto_retry
56
- alias :auto_retry? :auto_retry
57
- @auto_retry = false
58
-
59
- def initialize(config)
60
- @active = true
61
- @config = config
62
- new_connection(@config)
63
- end
53
+ module OracleEnhanced
54
+ class JDBCConnection < OracleEnhanced::Connection #:nodoc:
55
+ attr_accessor :active
56
+ alias :active? :active
64
57
 
65
- # modified method to support JNDI connections
66
- def new_connection(config)
67
- username = nil
58
+ attr_accessor :auto_retry
59
+ alias :auto_retry? :auto_retry
60
+ @auto_retry = false
68
61
 
69
- if config[:jndi]
70
- jndi = config[:jndi].to_s
71
- ctx = javax.naming.InitialContext.new
72
- ds = nil
62
+ def initialize(config)
63
+ @active = true
64
+ @config = config
65
+ new_connection(@config)
66
+ end
73
67
 
74
- # tomcat needs first lookup method, oc4j (and maybe other application servers) need second method
75
- begin
76
- env = ctx.lookup("java:/comp/env")
77
- ds = env.lookup(jndi)
78
- rescue
79
- ds = ctx.lookup(jndi)
80
- end
68
+ # modified method to support JNDI connections
69
+ def new_connection(config)
70
+ username = nil
71
+
72
+ if config[:jndi]
73
+ jndi = config[:jndi].to_s
74
+ ctx = javax.naming.InitialContext.new
75
+ ds = nil
76
+
77
+ # tomcat needs first lookup method, oc4j (and maybe other application servers) need second method
78
+ begin
79
+ env = ctx.lookup("java:/comp/env")
80
+ ds = env.lookup(jndi)
81
+ rescue
82
+ ds = ctx.lookup(jndi)
83
+ end
81
84
 
82
- # check if datasource supports pooled connections, otherwise use default
83
- if ds.respond_to?(:pooled_connection)
84
- @raw_connection = ds.pooled_connection
85
- else
86
- @raw_connection = ds.connection
87
- end
85
+ # check if datasource supports pooled connections, otherwise use default
86
+ if ds.respond_to?(:pooled_connection)
87
+ @raw_connection = ds.pooled_connection
88
+ else
89
+ @raw_connection = ds.connection
90
+ end
88
91
 
89
- # get Oracle JDBC connection when using DBCP in Tomcat or jBoss
90
- if @raw_connection.respond_to?(:getInnermostDelegate)
91
- @pooled_connection = @raw_connection
92
- @raw_connection = @raw_connection.innermost_delegate
93
- elsif @raw_connection.respond_to?(:getUnderlyingConnection)
94
- @pooled_connection = @raw_connection
95
- @raw_connection = @raw_connection.underlying_connection
96
- end
92
+ # get Oracle JDBC connection when using DBCP in Tomcat or jBoss
93
+ if @raw_connection.respond_to?(:getInnermostDelegate)
94
+ @pooled_connection = @raw_connection
95
+ @raw_connection = @raw_connection.innermost_delegate
96
+ elsif @raw_connection.respond_to?(:getUnderlyingConnection)
97
+ @pooled_connection = @raw_connection
98
+ @raw_connection = @raw_connection.underlying_connection
99
+ end
97
100
 
98
- config[:driver] ||= @raw_connection.meta_data.connection.java_class.name
99
- username = @raw_connection.meta_data.user_name
100
- else
101
- # to_s needed if username, password or database is specified as number in database.yml file
102
- username = config[:username] && config[:username].to_s
103
- password = config[:password] && config[:password].to_s
104
- database = config[:database] && config[:database].to_s || "XE"
105
- host, port = config[:host], config[:port]
106
- privilege = config[:privilege] && config[:privilege].to_s
107
-
108
- # connection using TNS alias, or connection-string from DATABASE_URL
109
- using_tns_alias = !host && !config[:url] && ENV["TNS_ADMIN"]
110
- if database && (using_tns_alias || host == "connection-string")
111
- url = "jdbc:oracle:thin:@#{database}"
101
+ config[:driver] ||= @raw_connection.meta_data.connection.java_class.name
102
+ username = @raw_connection.meta_data.user_name
112
103
  else
113
- unless database.match(/^(\:|\/)/)
114
- # assume database is a SID if no colon or slash are supplied (backward-compatibility)
115
- database = ":#{database}"
104
+ # to_s needed if username, password or database is specified as number in database.yml file
105
+ username = config[:username] && config[:username].to_s
106
+ password = config[:password] && config[:password].to_s
107
+ database = config[:database] && config[:database].to_s || "XE"
108
+ host, port = config[:host], config[:port]
109
+ privilege = config[:privilege] && config[:privilege].to_s
110
+
111
+ # connection using TNS alias, or connection-string from DATABASE_URL
112
+ using_tns_alias = !host && !config[:url] && ENV["TNS_ADMIN"]
113
+ if database && (using_tns_alias || host == "connection-string")
114
+ url = "jdbc:oracle:thin:@#{database}"
115
+ else
116
+ unless database.match(/^(\:|\/)/)
117
+ # assume database is a SID if no colon or slash are supplied (backward-compatibility)
118
+ database = ":#{database}"
119
+ end
120
+ url = config[:url] || "jdbc:oracle:thin:@#{host || 'localhost'}:#{port || 1521}#{database}"
116
121
  end
117
- url = config[:url] || "jdbc:oracle:thin:@#{host || 'localhost'}:#{port || 1521}#{database}"
118
- end
119
122
 
120
- prefetch_rows = config[:prefetch_rows] || 100
121
- # get session time_zone from configuration or from TZ environment variable
122
- time_zone = config[:time_zone] || ENV["TZ"] || java.util.TimeZone.default.getID
123
+ prefetch_rows = config[:prefetch_rows] || 100
124
+ # get session time_zone from configuration or from TZ environment variable
125
+ time_zone = config[:time_zone] || ENV["TZ"] || java.util.TimeZone.default.getID
126
+
127
+ properties = java.util.Properties.new
128
+ properties.put("user", username)
129
+ properties.put("password", password)
130
+ properties.put("defaultRowPrefetch", "#{prefetch_rows}") if prefetch_rows
131
+ properties.put("internal_logon", privilege) if privilege
132
+
133
+ begin
134
+ @raw_connection = java.sql.DriverManager.getConnection(url, properties)
135
+ rescue
136
+ # bypass DriverManager to work in cases where ojdbc*.jar
137
+ # is added to the load path at runtime and not on the
138
+ # system classpath
139
+ @raw_connection = ORACLE_DRIVER.connect(url, properties)
140
+ end
123
141
 
124
- properties = java.util.Properties.new
125
- properties.put("user", username)
126
- properties.put("password", password)
127
- properties.put("defaultRowPrefetch", "#{prefetch_rows}") if prefetch_rows
128
- properties.put("internal_logon", privilege) if privilege
142
+ # Set session time zone to current time zone
143
+ if ActiveRecord::Base.default_timezone == :local
144
+ @raw_connection.setSessionTimeZone(time_zone)
145
+ elsif ActiveRecord::Base.default_timezone == :utc
146
+ @raw_connection.setSessionTimeZone("UTC")
147
+ end
129
148
 
130
- begin
131
- @raw_connection = java.sql.DriverManager.getConnection(url, properties)
132
- rescue
133
- # bypass DriverManager to work in cases where ojdbc*.jar
134
- # is added to the load path at runtime and not on the
135
- # system classpath
136
- @raw_connection = ORACLE_DRIVER.connect(url, properties)
149
+ # Set default number of rows to prefetch
150
+ # @raw_connection.setDefaultRowPrefetch(prefetch_rows) if prefetch_rows
137
151
  end
138
152
 
139
- # Set session time zone to current time zone
140
- if ActiveRecord::Base.default_timezone == :local
141
- @raw_connection.setSessionTimeZone(time_zone)
142
- elsif ActiveRecord::Base.default_timezone == :utc
143
- @raw_connection.setSessionTimeZone("UTC")
144
- end
153
+ cursor_sharing = config[:cursor_sharing]
154
+ exec "alter session set cursor_sharing = #{cursor_sharing}" if cursor_sharing
145
155
 
146
- # Set default number of rows to prefetch
147
- # @raw_connection.setDefaultRowPrefetch(prefetch_rows) if prefetch_rows
148
- end
156
+ # Initialize NLS parameters
157
+ OracleEnhancedAdapter::DEFAULT_NLS_PARAMETERS.each do |key, default_value|
158
+ value = config[key] || ENV[key.to_s.upcase] || default_value
159
+ if value
160
+ exec "alter session set #{key} = '#{value}'"
161
+ end
162
+ end
149
163
 
150
- cursor_sharing = config[:cursor_sharing] || "force"
151
- exec "alter session set cursor_sharing = #{cursor_sharing}"
164
+ self.autocommit = true
152
165
 
153
- # Initialize NLS parameters
154
- OracleEnhancedAdapter::DEFAULT_NLS_PARAMETERS.each do |key, default_value|
155
- value = config[key] || ENV[key.to_s.upcase] || default_value
156
- if value
157
- exec "alter session set #{key} = '#{value}'"
166
+ schema = config[:schema] && config[:schema].to_s
167
+ if schema.blank?
168
+ # default schema owner
169
+ @owner = username.upcase unless username.nil?
170
+ else
171
+ exec "alter session set current_schema = #{schema}"
172
+ @owner = schema
158
173
  end
159
- end
160
174
 
161
- self.autocommit = true
162
-
163
- schema = config[:schema] && config[:schema].to_s
164
- if schema.blank?
165
- # default schema owner
166
- @owner = username.upcase unless username.nil?
167
- else
168
- exec "alter session set current_schema = #{schema}"
169
- @owner = schema
175
+ @raw_connection
170
176
  end
171
177
 
172
- @raw_connection
173
- end
174
-
175
- def logoff
176
- @active = false
177
- if defined?(@pooled_connection)
178
- @pooled_connection.close
179
- else
180
- @raw_connection.close
178
+ def logoff
179
+ @active = false
180
+ if defined?(@pooled_connection)
181
+ @pooled_connection.close
182
+ else
183
+ @raw_connection.close
184
+ end
185
+ true
186
+ rescue
187
+ false
181
188
  end
182
- true
183
- rescue
184
- false
185
- end
186
189
 
187
- def commit
188
- @raw_connection.commit
189
- end
190
-
191
- def rollback
192
- @raw_connection.rollback
193
- end
190
+ def commit
191
+ @raw_connection.commit
192
+ end
194
193
 
195
- def autocommit?
196
- @raw_connection.getAutoCommit
197
- end
194
+ def rollback
195
+ @raw_connection.rollback
196
+ end
198
197
 
199
- def autocommit=(value)
200
- @raw_connection.setAutoCommit(value)
201
- end
198
+ def autocommit?
199
+ @raw_connection.getAutoCommit
200
+ end
202
201
 
203
- # Checks connection, returns true if active. Note that ping actively
204
- # checks the connection, while #active? simply returns the last
205
- # known state.
206
- def ping
207
- exec_no_retry("select 1 from dual")
208
- @active = true
209
- rescue NativeException => e
210
- @active = false
211
- if e.message =~ /^java\.sql\.SQL(Recoverable)?Exception/
212
- raise OracleEnhancedConnectionException, e.message
213
- else
214
- raise
202
+ def autocommit=(value)
203
+ @raw_connection.setAutoCommit(value)
215
204
  end
216
- end
217
205
 
218
- # Resets connection, by logging off and creating a new connection.
219
- def reset!
220
- logoff rescue nil
221
- begin
222
- new_connection(@config)
206
+ # Checks connection, returns true if active. Note that ping actively
207
+ # checks the connection, while #active? simply returns the last
208
+ # known state.
209
+ def ping
210
+ exec_no_retry("select 1 from dual")
223
211
  @active = true
224
212
  rescue NativeException => e
225
213
  @active = false
226
214
  if e.message =~ /^java\.sql\.SQL(Recoverable)?Exception/
227
- raise OracleEnhancedConnectionException, e.message
215
+ raise OracleEnhanced::ConnectionException, e.message
228
216
  else
229
217
  raise
230
218
  end
231
219
  end
232
- end
233
220
 
234
- # mark connection as dead if connection lost
235
- def with_retry(&block)
236
- should_retry = auto_retry? && autocommit?
237
- begin
238
- yield if block_given?
239
- rescue NativeException => e
240
- raise unless e.message =~ /^java\.sql\.SQL(Recoverable)?Exception: (Closed Connection|Io exception:|No more data to read from socket|IO Error:)/
241
- @active = false
242
- raise unless should_retry
243
- should_retry = false
244
- reset! rescue nil
245
- retry
221
+ # Resets connection, by logging off and creating a new connection.
222
+ def reset!
223
+ logoff rescue nil
224
+ begin
225
+ new_connection(@config)
226
+ @active = true
227
+ rescue NativeException => e
228
+ @active = false
229
+ if e.message =~ /^java\.sql\.SQL(Recoverable)?Exception/
230
+ raise OracleEnhanced::ConnectionException, e.message
231
+ else
232
+ raise
233
+ end
234
+ end
246
235
  end
247
- end
248
236
 
249
- def exec(sql)
250
- with_retry do
251
- exec_no_retry(sql)
237
+ # mark connection as dead if connection lost
238
+ def with_retry(&block)
239
+ should_retry = auto_retry? && autocommit?
240
+ begin
241
+ yield if block_given?
242
+ rescue NativeException => e
243
+ raise unless e.message =~ /^java\.sql\.SQL(Recoverable)?Exception: (Closed Connection|Io exception:|No more data to read from socket|IO Error:)/
244
+ @active = false
245
+ raise unless should_retry
246
+ should_retry = false
247
+ reset! rescue nil
248
+ retry
249
+ end
252
250
  end
253
- end
254
251
 
255
- def exec_no_retry(sql)
256
- case sql
257
- when /\A\s*(UPDATE|INSERT|DELETE)/i
258
- s = @raw_connection.prepareStatement(sql)
259
- s.executeUpdate
260
- # it is safer for CREATE and DROP statements not to use PreparedStatement
261
- # as it does not allow creation of triggers with :NEW in their definition
262
- when /\A\s*(CREATE|DROP)/i
263
- s = @raw_connection.createStatement()
264
- # this disables SQL92 syntax processing of {...} which can result in statement execution errors
265
- # if sql contains {...} in strings or comments
266
- s.setEscapeProcessing(false)
267
- s.execute(sql)
268
- true
269
- else
270
- s = @raw_connection.prepareStatement(sql)
271
- s.execute
272
- true
252
+ def exec(sql)
253
+ with_retry do
254
+ exec_no_retry(sql)
255
+ end
273
256
  end
274
- ensure
275
- s.close rescue nil
276
- end
277
-
278
- def returning_clause(quoted_pk)
279
- " RETURNING #{quoted_pk} INTO ?"
280
- end
281
257
 
282
- # execute sql with RETURNING ... INTO :insert_id
283
- # and return :insert_id value
284
- def exec_with_returning(sql)
285
- with_retry do
286
- begin
287
- # it will always be INSERT statement
288
-
289
- # TODO: need to investigate why PreparedStatement is giving strange exception "Protocol violation"
290
- # s = @raw_connection.prepareStatement(sql)
291
- # s.registerReturnParameter(1, ::Java::oracle.jdbc.OracleTypes::NUMBER)
292
- # count = s.executeUpdate
293
- # if count > 0
294
- # rs = s.getReturnResultSet
295
- # if rs.next
296
- # # Assuming that primary key will not be larger as long max value
297
- # insert_id = rs.getLong(1)
298
- # rs.wasNull ? nil : insert_id
299
- # else
300
- # nil
301
- # end
302
- # else
303
- # nil
304
- # end
305
-
306
- # Workaround with CallableStatement
307
- s = @raw_connection.prepareCall("BEGIN #{sql}; END;")
308
- s.registerOutParameter(1, java.sql.Types::BIGINT)
258
+ def exec_no_retry(sql)
259
+ case sql
260
+ when /\A\s*(UPDATE|INSERT|DELETE)/i
261
+ s = @raw_connection.prepareStatement(sql)
262
+ s.executeUpdate
263
+ # it is safer for CREATE and DROP statements not to use PreparedStatement
264
+ # as it does not allow creation of triggers with :NEW in their definition
265
+ when /\A\s*(CREATE|DROP)/i
266
+ s = @raw_connection.createStatement()
267
+ # this disables SQL92 syntax processing of {...} which can result in statement execution errors
268
+ # if sql contains {...} in strings or comments
269
+ s.setEscapeProcessing(false)
270
+ s.execute(sql)
271
+ true
272
+ else
273
+ s = @raw_connection.prepareStatement(sql)
309
274
  s.execute
310
- insert_id = s.getLong(1)
311
- s.wasNull ? nil : insert_id
312
- ensure
313
- # rs.close rescue nil
314
- s.close rescue nil
275
+ true
315
276
  end
277
+ ensure
278
+ s.close rescue nil
316
279
  end
317
- end
318
-
319
- def prepare(sql)
320
- Cursor.new(self, @raw_connection.prepareStatement(sql))
321
- end
322
280
 
323
- def database_version
324
- @database_version ||= (md = raw_connection.getMetaData) && [md.getDatabaseMajorVersion, md.getDatabaseMinorVersion]
325
- end
281
+ def prepare(sql)
282
+ Cursor.new(self, @raw_connection.prepareStatement(sql))
283
+ end
326
284
 
327
- class Cursor
328
- def initialize(connection, raw_statement)
329
- @connection = connection
330
- @raw_statement = raw_statement
285
+ def database_version
286
+ @database_version ||= (md = raw_connection.getMetaData) && [md.getDatabaseMajorVersion, md.getDatabaseMinorVersion]
331
287
  end
332
288
 
333
- def bind_params(*bind_vars)
334
- index = 1
335
- bind_vars.flatten.each do |var|
336
- if Hash === var
337
- var.each { |key, val| bind_param key, val }
338
- else
339
- bind_param index, var
340
- index += 1
289
+ class Cursor
290
+ def initialize(connection, raw_statement)
291
+ @connection = connection
292
+ @raw_statement = raw_statement
293
+ end
294
+
295
+ def bind_params(*bind_vars)
296
+ index = 1
297
+ bind_vars.flatten.each do |var|
298
+ if Hash === var
299
+ var.each { |key, val| bind_param key, val }
300
+ else
301
+ bind_param index, var
302
+ index += 1
303
+ end
341
304
  end
342
305
  end
343
- end
344
306
 
345
- def bind_param(position, value)
346
- case value
347
- when Integer
348
- @raw_statement.setLong(position, value)
349
- when Float
350
- @raw_statement.setFloat(position, value)
351
- when BigDecimal
352
- @raw_statement.setBigDecimal(position, value)
353
- when Java::OracleSql::BLOB
354
- @raw_statement.setBlob(position, value)
355
- when Java::OracleSql::CLOB
356
- @raw_statement.setClob(position, value)
357
- when ActiveRecord::OracleEnhanced::Type::Raw
358
- @raw_statement.setString(position, ActiveRecord::ConnectionAdapters::OracleEnhanced::Quoting.encode_raw(value))
359
- when String
360
- @raw_statement.setString(position, value)
361
- when Java::OracleSql::DATE
362
- @raw_statement.setDATE(position, value)
363
- when Date, DateTime
364
- # TODO: Really needed or not
365
- @raw_statement.setDATE(position, value)
366
- when Java::JavaSql::Timestamp
367
- @raw_statement.setTimestamp(position, value)
368
- when Time
369
- new_value = Java::java.sql.Timestamp.new(value.year - 1900, value.month - 1, value.day, value.hour, value.min, value.sec, value.usec * 1000)
370
- @raw_statement.setTimestamp(position, new_value)
371
- when NilClass
372
- # TODO: currently nil is always bound as NULL with VARCHAR type.
373
- # When nils will actually be used by ActiveRecord as bound parameters
374
- # then need to pass actual column type.
375
- @raw_statement.setNull(position, java.sql.Types::VARCHAR)
376
- else
377
- raise ArgumentError, "Don't know how to bind variable with type #{value.class}"
307
+ def bind_param(position, value)
308
+ case value
309
+ when Integer
310
+ @raw_statement.setLong(position, value)
311
+ when Float
312
+ @raw_statement.setFloat(position, value)
313
+ when BigDecimal
314
+ @raw_statement.setBigDecimal(position, value)
315
+ when Java::OracleSql::BLOB
316
+ @raw_statement.setBlob(position, value)
317
+ when Java::OracleSql::CLOB
318
+ @raw_statement.setClob(position, value)
319
+ when Java::OracleSql::NCLOB
320
+ @raw_statement.setClob(position, value)
321
+ when Type::OracleEnhanced::Raw
322
+ @raw_statement.setString(position, OracleEnhanced::Quoting.encode_raw(value))
323
+ when String
324
+ @raw_statement.setString(position, value)
325
+ when Java::OracleSql::DATE
326
+ @raw_statement.setDATE(position, value)
327
+ when Java::JavaSql::Timestamp
328
+ @raw_statement.setTimestamp(position, value)
329
+ when Time
330
+ new_value = Java::java.sql.Timestamp.new(value.year - 1900, value.month - 1, value.day, value.hour, value.min, value.sec, value.usec * 1000)
331
+ @raw_statement.setTimestamp(position, new_value)
332
+ when NilClass
333
+ # TODO: currently nil is always bound as NULL with VARCHAR type.
334
+ # When nils will actually be used by ActiveRecord as bound parameters
335
+ # then need to pass actual column type.
336
+ @raw_statement.setNull(position, java.sql.Types::VARCHAR)
337
+ else
338
+ raise ArgumentError, "Don't know how to bind variable with type #{value.class}"
339
+ end
378
340
  end
379
- end
380
341
 
381
- def bind_returning_param(position, bind_type)
382
- @returning_positions ||= []
383
- @returning_positions << position
384
- if bind_type == Integer
385
- @raw_statement.registerReturnParameter(position, java.sql.Types::BIGINT)
342
+ def bind_returning_param(position, bind_type)
343
+ @returning_positions ||= []
344
+ @returning_positions << position
345
+ if bind_type == Integer
346
+ @raw_statement.registerReturnParameter(position, java.sql.Types::BIGINT)
347
+ end
386
348
  end
387
- end
388
349
 
389
- def exec
390
- @raw_result_set = @raw_statement.executeQuery
391
- true
392
- end
350
+ def exec
351
+ @raw_result_set = @raw_statement.executeQuery
352
+ true
353
+ end
393
354
 
394
- def exec_update
395
- @raw_statement.executeUpdate
396
- end
355
+ def exec_update
356
+ @raw_statement.executeUpdate
357
+ end
397
358
 
398
- def metadata
399
- @metadata ||= @raw_result_set.getMetaData
400
- end
359
+ def metadata
360
+ @metadata ||= @raw_result_set.getMetaData
361
+ end
401
362
 
402
- def column_types
403
- @column_types ||= (1..metadata.getColumnCount).map { |i| metadata.getColumnTypeName(i).to_sym }
404
- end
363
+ def column_types
364
+ @column_types ||= (1..metadata.getColumnCount).map { |i| metadata.getColumnTypeName(i).to_sym }
365
+ end
405
366
 
406
- def column_names
407
- @column_names ||= (1..metadata.getColumnCount).map { |i| metadata.getColumnName(i) }
408
- end
409
- alias :get_col_names :column_names
410
-
411
- def fetch(options = {})
412
- if @raw_result_set.next
413
- get_lob_value = options[:get_lob_value]
414
- row_values = []
415
- column_types.each_with_index do |column_type, i|
416
- row_values <<
417
- @connection.get_ruby_value_from_result_set(@raw_result_set, i + 1, column_type, get_lob_value)
367
+ def column_names
368
+ @column_names ||= (1..metadata.getColumnCount).map { |i| metadata.getColumnName(i) }
369
+ end
370
+ alias :get_col_names :column_names
371
+
372
+ def fetch(options = {})
373
+ if @raw_result_set.next
374
+ get_lob_value = options[:get_lob_value]
375
+ row_values = []
376
+ column_types.each_with_index do |column_type, i|
377
+ row_values <<
378
+ @connection.get_ruby_value_from_result_set(@raw_result_set, i + 1, column_type, get_lob_value)
379
+ end
380
+ row_values
381
+ else
382
+ @raw_result_set.close
383
+ nil
418
384
  end
419
- row_values
420
- else
421
- @raw_result_set.close
422
- nil
423
385
  end
424
- end
425
386
 
426
- def get_returning_param(position, type)
427
- rs_position = @returning_positions.index(position) + 1
428
- rs = @raw_statement.getReturnResultSet
429
- if rs.next
430
- # Assuming that primary key will not be larger as long max value
431
- returning_id = rs.getLong(rs_position)
432
- rs.wasNull ? nil : returning_id
433
- else
434
- nil
387
+ def get_returning_param(position, type)
388
+ rs_position = @returning_positions.index(position) + 1
389
+ rs = @raw_statement.getReturnResultSet
390
+ if rs.next
391
+ # Assuming that primary key will not be larger as long max value
392
+ returning_id = rs.getLong(rs_position)
393
+ rs.wasNull ? nil : returning_id
394
+ else
395
+ nil
396
+ end
435
397
  end
436
- end
437
398
 
438
- def close
439
- @raw_statement.close
399
+ def close
400
+ @raw_statement.close
401
+ end
440
402
  end
441
- end
442
403
 
443
- def select(sql, name = nil, return_column_names = false)
444
- with_retry do
445
- select_no_retry(sql, name, return_column_names)
404
+ def select(sql, name = nil, return_column_names = false)
405
+ with_retry do
406
+ select_no_retry(sql, name, return_column_names)
407
+ end
446
408
  end
447
- end
448
409
 
449
- def select_no_retry(sql, name = nil, return_column_names = false)
450
- stmt = @raw_connection.prepareStatement(sql)
451
- rset = stmt.executeQuery
410
+ def select_no_retry(sql, name = nil, return_column_names = false)
411
+ stmt = @raw_connection.prepareStatement(sql)
412
+ rset = stmt.executeQuery
452
413
 
453
- # Reuse the same hash for all rows
454
- column_hash = {}
414
+ # Reuse the same hash for all rows
415
+ column_hash = {}
455
416
 
456
- metadata = rset.getMetaData
457
- column_count = metadata.getColumnCount
417
+ metadata = rset.getMetaData
418
+ column_count = metadata.getColumnCount
458
419
 
459
- cols_types_index = (1..column_count).map do |i|
460
- col_name = oracle_downcase(metadata.getColumnName(i))
461
- next if col_name == "raw_rnum_"
462
- column_hash[col_name] = nil
463
- [col_name, metadata.getColumnTypeName(i).to_sym, i]
464
- end
465
- cols_types_index.delete(nil)
420
+ cols_types_index = (1..column_count).map do |i|
421
+ col_name = _oracle_downcase(metadata.getColumnName(i))
422
+ next if col_name == "raw_rnum_"
423
+ column_hash[col_name] = nil
424
+ [col_name, metadata.getColumnTypeName(i).to_sym, i]
425
+ end
426
+ cols_types_index.delete(nil)
466
427
 
467
- rows = []
468
- get_lob_value = !(name == "Writable Large Object")
428
+ rows = []
429
+ get_lob_value = !(name == "Writable Large Object")
469
430
 
470
- while rset.next
471
- hash = column_hash.dup
472
- cols_types_index.each do |col, column_type, i|
473
- hash[col] = get_ruby_value_from_result_set(rset, i, column_type, get_lob_value)
431
+ while rset.next
432
+ hash = column_hash.dup
433
+ cols_types_index.each do |col, column_type, i|
434
+ hash[col] = get_ruby_value_from_result_set(rset, i, column_type, get_lob_value)
435
+ end
436
+ rows << hash
474
437
  end
475
- rows << hash
476
- end
477
438
 
478
- return_column_names ? [rows, cols_types_index.map(&:first)] : rows
479
- ensure
480
- rset.close rescue nil
481
- stmt.close rescue nil
482
- end
439
+ return_column_names ? [rows, cols_types_index.map(&:first)] : rows
440
+ ensure
441
+ rset.close rescue nil
442
+ stmt.close rescue nil
443
+ end
483
444
 
484
- def write_lob(lob, value, is_binary = false)
485
- if is_binary
486
- lob.setBytes(1, value.to_java_bytes)
487
- else
488
- lob.setString(1, value)
445
+ def write_lob(lob, value, is_binary = false)
446
+ if is_binary
447
+ lob.setBytes(1, value.to_java_bytes)
448
+ else
449
+ lob.setString(1, value)
450
+ end
489
451
  end
490
- end
491
452
 
492
- # Return NativeException / java.sql.SQLException error code
493
- def error_code(exception)
494
- case exception
495
- when NativeException
496
- exception.cause.getErrorCode
497
- else
498
- nil
453
+ # To allow private method called from `JDBCConnection`
454
+ def describe(name)
455
+ super
499
456
  end
500
- end
501
457
 
502
- def get_ruby_value_from_result_set(rset, i, type_name, get_lob_value = true)
503
- case type_name
504
- when :NUMBER
505
- d = rset.getNUMBER(i)
506
- if d.nil?
507
- nil
508
- elsif d.isInt
509
- Integer(d.stringValue)
458
+ # Return NativeException / java.sql.SQLException error code
459
+ def error_code(exception)
460
+ case exception
461
+ when NativeException
462
+ exception.cause.getErrorCode
463
+ when Java::JavaSql::SQLException
464
+ exception.getErrorCode
510
465
  else
511
- BigDecimal.new(d.stringValue)
466
+ nil
512
467
  end
513
- when :BINARY_FLOAT
514
- rset.getFloat(i)
515
- when :VARCHAR2, :CHAR, :LONG, :NVARCHAR2, :NCHAR
516
- rset.getString(i)
517
- when :DATE
518
- if dt = rset.getDATE(i)
519
- d = dt.dateValue
520
- t = dt.timeValue
521
- Time.send(Base.default_timezone, d.year + 1900, d.month + 1, d.date, t.hours, t.minutes, t.seconds)
468
+ end
469
+
470
+ def get_ruby_value_from_result_set(rset, i, type_name, get_lob_value = true)
471
+ case type_name
472
+ when :NUMBER
473
+ d = rset.getNUMBER(i)
474
+ if d.nil?
475
+ nil
476
+ elsif d.isInt
477
+ Integer(d.stringValue)
478
+ else
479
+ BigDecimal.new(d.stringValue)
480
+ end
481
+ when :BINARY_FLOAT
482
+ rset.getFloat(i)
483
+ when :VARCHAR2, :CHAR, :LONG, :NVARCHAR2, :NCHAR
484
+ rset.getString(i)
485
+ when :DATE
486
+ if dt = rset.getDATE(i)
487
+ d = dt.dateValue
488
+ t = dt.timeValue
489
+ Time.send(Base.default_timezone, d.year + 1900, d.month + 1, d.date, t.hours, t.minutes, t.seconds)
490
+ else
491
+ nil
492
+ end
493
+ when :TIMESTAMP, :TIMESTAMPTZ, :TIMESTAMPLTZ, :"TIMESTAMP WITH TIME ZONE", :"TIMESTAMP WITH LOCAL TIME ZONE"
494
+ ts = rset.getTimestamp(i)
495
+ ts && Time.send(Base.default_timezone, ts.year + 1900, ts.month + 1, ts.date, ts.hours, ts.minutes, ts.seconds,
496
+ ts.nanos / 1000)
497
+ when :CLOB
498
+ get_lob_value ? lob_to_ruby_value(rset.getClob(i)) : rset.getClob(i)
499
+ when :NCLOB
500
+ get_lob_value ? lob_to_ruby_value(rset.getClob(i)) : rset.getClob(i)
501
+ when :BLOB
502
+ get_lob_value ? lob_to_ruby_value(rset.getBlob(i)) : rset.getBlob(i)
503
+ when :RAW
504
+ raw_value = rset.getRAW(i)
505
+ raw_value && raw_value.getBytes.to_a.pack("C*")
522
506
  else
523
507
  nil
524
508
  end
525
- when :TIMESTAMP, :TIMESTAMPTZ, :TIMESTAMPLTZ, :"TIMESTAMP WITH TIME ZONE", :"TIMESTAMP WITH LOCAL TIME ZONE"
526
- ts = rset.getTimestamp(i)
527
- ts && Time.send(Base.default_timezone, ts.year + 1900, ts.month + 1, ts.date, ts.hours, ts.minutes, ts.seconds,
528
- ts.nanos / 1000)
529
- when :CLOB
530
- get_lob_value ? lob_to_ruby_value(rset.getClob(i)) : rset.getClob(i)
531
- when :BLOB
532
- get_lob_value ? lob_to_ruby_value(rset.getBlob(i)) : rset.getBlob(i)
533
- when :RAW
534
- raw_value = rset.getRAW(i)
535
- raw_value && raw_value.getBytes.to_a.pack("C*")
536
- else
537
- nil
538
509
  end
539
- end
540
510
 
541
511
  private
542
512
 
@@ -548,6 +518,12 @@ module ActiveRecord
548
518
  else
549
519
  val.getSubString(1, val.length)
550
520
  end
521
+ when ::Java::OracleSql::NCLOB
522
+ if val.isEmptyLob
523
+ nil
524
+ else
525
+ val.getSubString(1, val.length)
526
+ end
551
527
  when ::Java::OracleSql::BLOB
552
528
  if val.isEmptyLob
553
529
  nil
@@ -556,6 +532,7 @@ module ActiveRecord
556
532
  end
557
533
  end
558
534
  end
535
+ end
559
536
  end
560
537
  end
561
538
  end