activerecord-oracle_enhanced-adapter-with-schema 0.0.1

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 (46) hide show
  1. data/.rspec +2 -0
  2. data/Gemfile +52 -0
  3. data/History.md +301 -0
  4. data/License.txt +20 -0
  5. data/README.md +123 -0
  6. data/RUNNING_TESTS.md +45 -0
  7. data/Rakefile +59 -0
  8. data/VERSION +1 -0
  9. data/activerecord-oracle_enhanced-adapter-with-schema.gemspec +130 -0
  10. data/lib/active_record/connection_adapters/emulation/oracle_adapter.rb +5 -0
  11. data/lib/active_record/connection_adapters/oracle_enhanced.rake +105 -0
  12. data/lib/active_record/connection_adapters/oracle_enhanced_activerecord_patches.rb +41 -0
  13. data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +1399 -0
  14. data/lib/active_record/connection_adapters/oracle_enhanced_base_ext.rb +121 -0
  15. data/lib/active_record/connection_adapters/oracle_enhanced_column.rb +146 -0
  16. data/lib/active_record/connection_adapters/oracle_enhanced_connection.rb +119 -0
  17. data/lib/active_record/connection_adapters/oracle_enhanced_context_index.rb +359 -0
  18. data/lib/active_record/connection_adapters/oracle_enhanced_core_ext.rb +25 -0
  19. data/lib/active_record/connection_adapters/oracle_enhanced_cpk.rb +21 -0
  20. data/lib/active_record/connection_adapters/oracle_enhanced_dirty.rb +46 -0
  21. data/lib/active_record/connection_adapters/oracle_enhanced_jdbc_connection.rb +565 -0
  22. data/lib/active_record/connection_adapters/oracle_enhanced_oci_connection.rb +494 -0
  23. data/lib/active_record/connection_adapters/oracle_enhanced_procedures.rb +260 -0
  24. data/lib/active_record/connection_adapters/oracle_enhanced_schema_definitions.rb +227 -0
  25. data/lib/active_record/connection_adapters/oracle_enhanced_schema_dumper.rb +260 -0
  26. data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements.rb +428 -0
  27. data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements_ext.rb +258 -0
  28. data/lib/active_record/connection_adapters/oracle_enhanced_structure_dump.rb +294 -0
  29. data/lib/active_record/connection_adapters/oracle_enhanced_tasks.rb +17 -0
  30. data/lib/active_record/connection_adapters/oracle_enhanced_version.rb +1 -0
  31. data/lib/activerecord-oracle_enhanced-adapter-with-schema.rb +25 -0
  32. data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +778 -0
  33. data/spec/active_record/connection_adapters/oracle_enhanced_connection_spec.rb +332 -0
  34. data/spec/active_record/connection_adapters/oracle_enhanced_context_index_spec.rb +427 -0
  35. data/spec/active_record/connection_adapters/oracle_enhanced_core_ext_spec.rb +19 -0
  36. data/spec/active_record/connection_adapters/oracle_enhanced_cpk_spec.rb +113 -0
  37. data/spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb +1388 -0
  38. data/spec/active_record/connection_adapters/oracle_enhanced_dbms_output_spec.rb +69 -0
  39. data/spec/active_record/connection_adapters/oracle_enhanced_dirty_spec.rb +141 -0
  40. data/spec/active_record/connection_adapters/oracle_enhanced_emulate_oracle_adapter_spec.rb +25 -0
  41. data/spec/active_record/connection_adapters/oracle_enhanced_procedures_spec.rb +378 -0
  42. data/spec/active_record/connection_adapters/oracle_enhanced_schema_dump_spec.rb +440 -0
  43. data/spec/active_record/connection_adapters/oracle_enhanced_schema_statements_spec.rb +1385 -0
  44. data/spec/active_record/connection_adapters/oracle_enhanced_structure_dump_spec.rb +339 -0
  45. data/spec/spec_helper.rb +189 -0
  46. metadata +260 -0
@@ -0,0 +1,25 @@
1
+ require "bigdecimal"
2
+
3
+ unless BigDecimal.method_defined?(:to_d)
4
+ BigDecimal.class_eval do
5
+ def to_d #:nodoc:
6
+ self
7
+ end
8
+ end
9
+ end
10
+
11
+ unless Bignum.method_defined?(:to_d)
12
+ Bignum.class_eval do
13
+ def to_d #:nodoc:
14
+ BigDecimal.new(self.to_s)
15
+ end
16
+ end
17
+ end
18
+
19
+ unless Fixnum.method_defined?(:to_d)
20
+ Fixnum.class_eval do
21
+ def to_d #:nodoc:
22
+ BigDecimal.new(self.to_s)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,21 @@
1
+ module ActiveRecord #:nodoc:
2
+ module ConnectionAdapters #:nodoc:
3
+ module OracleEnhancedCpk #:nodoc:
4
+
5
+ # This mightn't be in Core, but count(distinct x,y) doesn't work for me.
6
+ # Return that not supported if composite_primary_keys gem is required.
7
+ def supports_count_distinct? #:nodoc:
8
+ @supports_count_distinct ||= ! defined?(CompositePrimaryKeys)
9
+ end
10
+
11
+ def concat(*columns) #:nodoc:
12
+ "(#{columns.join('||')})"
13
+ end
14
+
15
+ end
16
+ end
17
+ end
18
+
19
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.class_eval do
20
+ include ActiveRecord::ConnectionAdapters::OracleEnhancedCpk
21
+ end
@@ -0,0 +1,46 @@
1
+ module ActiveRecord #:nodoc:
2
+ module ConnectionAdapters #:nodoc:
3
+ module OracleEnhancedDirty #:nodoc:
4
+
5
+ module InstanceMethods #:nodoc:
6
+ private
7
+
8
+ def _field_changed?(attr, old, value)
9
+ if column = column_for_attribute(attr)
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.
13
+ # Hence we don't record it as a change if the value changes from nil to ''.
14
+ # If an old value of 0 is set to '' we want this to get changed to nil as otherwise it'll
15
+ # be typecast back to 0 (''.to_i => 0)
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
+ elsif column.type == :string && column.null && old.nil?
20
+ value = nil if value == ''
21
+ elsif old == 0 && value.is_a?(String) && value.present? && value != '0'
22
+ value = nil
23
+ else
24
+ value = column.type_cast(value)
25
+ end
26
+ end
27
+
28
+ old != value
29
+ end
30
+
31
+ end
32
+
33
+ end
34
+ end
35
+ end
36
+
37
+ if ActiveRecord::Base.method_defined?(:changed?)
38
+ ActiveRecord::Base.class_eval do
39
+ include ActiveRecord::ConnectionAdapters::OracleEnhancedDirty::InstanceMethods
40
+ # Starting with rails 3.2.9 the method #field_changed?
41
+ # was renamed to #_field_changed?
42
+ if private_method_defined?(:field_changed?)
43
+ alias_method :field_changed?, :_field_changed?
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,565 @@
1
+ begin
2
+ require "java"
3
+ require "jruby"
4
+
5
+ # ojdbc6.jar or ojdbc5.jar file should be in JRUBY_HOME/lib or should be in ENV['PATH'] or load path
6
+
7
+ java_version = java.lang.System.getProperty("java.version")
8
+ ojdbc_jar = if java_version =~ /^1.5/
9
+ "ojdbc5.jar"
10
+ elsif java_version >= '1.6'
11
+ "ojdbc6.jar"
12
+ else
13
+ nil
14
+ end
15
+
16
+ unless ojdbc_jar.nil? || ENV_JAVA['java.class.path'] =~ Regexp.new(ojdbc_jar)
17
+ # On Unix environment variable should be PATH, on Windows it is sometimes Path
18
+ env_path = (ENV["PATH"] || ENV["Path"] || '').split(File::PATH_SEPARATOR)
19
+ # Look for JDBC driver at first in lib subdirectory (application specific JDBC file version)
20
+ # then in Ruby load path and finally in environment PATH
21
+ if ojdbc_jar_path = ['./lib'].concat($LOAD_PATH).concat(env_path).find{|d| File.exists?(File.join(d,ojdbc_jar))}
22
+ require File.join(ojdbc_jar_path,ojdbc_jar)
23
+ end
24
+ end
25
+
26
+ ORACLE_DRIVER = Java::oracle.jdbc.OracleDriver.new
27
+ java.sql.DriverManager.registerDriver ORACLE_DRIVER
28
+
29
+ # set tns_admin property from TNS_ADMIN environment variable
30
+ if !java.lang.System.get_property("oracle.net.tns_admin") && ENV["TNS_ADMIN"]
31
+ java.lang.System.set_property("oracle.net.tns_admin", ENV["TNS_ADMIN"])
32
+ end
33
+
34
+ rescue LoadError, NameError
35
+ # JDBC driver is unavailable.
36
+ raise LoadError, "ERROR: ActiveRecord oracle_enhanced adapter could not load Oracle JDBC driver. Please install #{ojdbc_jar || "Oracle JDBC"} library."
37
+ end
38
+
39
+
40
+ module ActiveRecord
41
+ module ConnectionAdapters
42
+
43
+ # JDBC database interface for JRuby
44
+ class OracleEnhancedJDBCConnection < OracleEnhancedConnection #:nodoc:
45
+
46
+ attr_accessor :active
47
+ alias :active? :active
48
+
49
+ attr_accessor :auto_retry
50
+ alias :auto_retry? :auto_retry
51
+ @auto_retry = false
52
+
53
+ def initialize(config)
54
+ @active = true
55
+ @config = config
56
+ new_connection(@config)
57
+ end
58
+
59
+ # modified method to support JNDI connections
60
+ def new_connection(config)
61
+ username = nil
62
+
63
+ if config[:jndi]
64
+ jndi = config[:jndi].to_s
65
+ ctx = javax.naming.InitialContext.new
66
+ ds = nil
67
+
68
+ # tomcat needs first lookup method, oc4j (and maybe other application servers) need second method
69
+ begin
70
+ env = ctx.lookup('java:/comp/env')
71
+ ds = env.lookup(jndi)
72
+ rescue
73
+ ds = ctx.lookup(jndi)
74
+ end
75
+
76
+ # check if datasource supports pooled connections, otherwise use default
77
+ if ds.respond_to?(:pooled_connection)
78
+ @raw_connection = ds.pooled_connection
79
+ else
80
+ @raw_connection = ds.connection
81
+ end
82
+
83
+ # get Oracle JDBC connection when using DBCP in Tomcat or jBoss
84
+ if @raw_connection.respond_to?(:getInnermostDelegate)
85
+ @pooled_connection = @raw_connection
86
+ @raw_connection = @raw_connection.innermost_delegate
87
+ elsif @raw_connection.respond_to?(:getUnderlyingConnection)
88
+ @pooled_connection = @raw_connection
89
+ @raw_connection = @raw_connection.underlying_connection
90
+ end
91
+
92
+ config[:driver] ||= @raw_connection.meta_data.connection.java_class.name
93
+ username = @raw_connection.meta_data.user_name
94
+ else
95
+ # to_s needed if username, password or database is specified as number in database.yml file
96
+ username = config[:username] && config[:username].to_s
97
+ password = config[:password] && config[:password].to_s
98
+ database = config[:database] && config[:database].to_s || 'XE'
99
+ host, port = config[:host], config[:port]
100
+ privilege = config[:privilege] && config[:privilege].to_s
101
+
102
+ # connection using TNS alias
103
+ if database && !host && !config[:url] && ENV['TNS_ADMIN']
104
+ url = "jdbc:oracle:thin:@#{database}"
105
+ else
106
+ unless database.match(/^(\:|\/)/)
107
+ # assume database is a SID if no colon or slash are supplied (backward-compatibility)
108
+ database = ":#{database}"
109
+ end
110
+ url = config[:url] || "jdbc:oracle:thin:@#{host || 'localhost'}:#{port || 1521}#{database}"
111
+ end
112
+
113
+ prefetch_rows = config[:prefetch_rows] || 100
114
+ # get session time_zone from configuration or from TZ environment variable
115
+ time_zone = config[:time_zone] || ENV['TZ'] || java.util.TimeZone.default.getID
116
+
117
+ properties = java.util.Properties.new
118
+ properties.put("user", username)
119
+ properties.put("password", password)
120
+ properties.put("defaultRowPrefetch", "#{prefetch_rows}") if prefetch_rows
121
+ properties.put("internal_logon", privilege) if privilege
122
+
123
+ begin
124
+ @raw_connection = java.sql.DriverManager.getConnection(url, properties)
125
+ rescue
126
+ # bypass DriverManager to work in cases where ojdbc*.jar
127
+ # is added to the load path at runtime and not on the
128
+ # system classpath
129
+ @raw_connection = ORACLE_DRIVER.connect(url, properties)
130
+ end
131
+
132
+ # Set session time zone to current time zone
133
+ @raw_connection.setSessionTimeZone(time_zone)
134
+
135
+ # Set default number of rows to prefetch
136
+ # @raw_connection.setDefaultRowPrefetch(prefetch_rows) if prefetch_rows
137
+ end
138
+
139
+ cursor_sharing = config[:cursor_sharing] || 'force'
140
+ exec "alter session set cursor_sharing = #{cursor_sharing}"
141
+
142
+ # Initialize NLS parameters
143
+ OracleEnhancedAdapter::DEFAULT_NLS_PARAMETERS.each do |key, default_value|
144
+ value = config[key] || ENV[key.to_s.upcase] || default_value
145
+ if value
146
+ exec "alter session set #{key} = '#{value}'"
147
+ end
148
+ end
149
+
150
+ self.autocommit = true
151
+
152
+ # default schema owner
153
+ @owner = username.upcase unless username.nil?
154
+
155
+ @raw_connection
156
+ end
157
+
158
+ def logoff
159
+ @active = false
160
+ if defined?(@pooled_connection)
161
+ @pooled_connection.close
162
+ else
163
+ @raw_connection.close
164
+ end
165
+ true
166
+ rescue
167
+ false
168
+ end
169
+
170
+ def commit
171
+ @raw_connection.commit
172
+ end
173
+
174
+ def rollback
175
+ @raw_connection.rollback
176
+ end
177
+
178
+ def autocommit?
179
+ @raw_connection.getAutoCommit
180
+ end
181
+
182
+ def autocommit=(value)
183
+ @raw_connection.setAutoCommit(value)
184
+ end
185
+
186
+ # Checks connection, returns true if active. Note that ping actively
187
+ # checks the connection, while #active? simply returns the last
188
+ # known state.
189
+ def ping
190
+ exec_no_retry("select 1 from dual")
191
+ @active = true
192
+ rescue NativeException => e
193
+ @active = false
194
+ if e.message =~ /^java\.sql\.SQL(Recoverable)?Exception/
195
+ raise OracleEnhancedConnectionException, e.message
196
+ else
197
+ raise
198
+ end
199
+ end
200
+
201
+ # Resets connection, by logging off and creating a new connection.
202
+ def reset!
203
+ logoff rescue nil
204
+ begin
205
+ new_connection(@config)
206
+ @active = true
207
+ rescue NativeException => e
208
+ @active = false
209
+ if e.message =~ /^java\.sql\.SQL(Recoverable)?Exception/
210
+ raise OracleEnhancedConnectionException, e.message
211
+ else
212
+ raise
213
+ end
214
+ end
215
+ end
216
+
217
+ # mark connection as dead if connection lost
218
+ def with_retry(&block)
219
+ should_retry = auto_retry? && autocommit?
220
+ begin
221
+ yield if block_given?
222
+ rescue NativeException => e
223
+ raise unless e.message =~ /^java\.sql\.SQL(Recoverable)?Exception: (Closed Connection|Io exception:|No more data to read from socket|IO Error:)/
224
+ @active = false
225
+ raise unless should_retry
226
+ should_retry = false
227
+ reset! rescue nil
228
+ retry
229
+ end
230
+ end
231
+
232
+ def exec(sql)
233
+ with_retry do
234
+ exec_no_retry(sql)
235
+ end
236
+ end
237
+
238
+ def exec_no_retry(sql)
239
+ case sql
240
+ when /\A\s*(UPDATE|INSERT|DELETE)/i
241
+ s = @raw_connection.prepareStatement(sql)
242
+ s.executeUpdate
243
+ # it is safer for CREATE and DROP statements not to use PreparedStatement
244
+ # as it does not allow creation of triggers with :NEW in their definition
245
+ when /\A\s*(CREATE|DROP)/i
246
+ s = @raw_connection.createStatement()
247
+ # this disables SQL92 syntax processing of {...} which can result in statement execution errors
248
+ # if sql contains {...} in strings or comments
249
+ s.setEscapeProcessing(false)
250
+ s.execute(sql)
251
+ true
252
+ else
253
+ s = @raw_connection.prepareStatement(sql)
254
+ s.execute
255
+ true
256
+ end
257
+ ensure
258
+ s.close rescue nil
259
+ end
260
+
261
+ def returning_clause(quoted_pk)
262
+ " RETURNING #{quoted_pk} INTO ?"
263
+ end
264
+
265
+ # execute sql with RETURNING ... INTO :insert_id
266
+ # and return :insert_id value
267
+ def exec_with_returning(sql)
268
+ with_retry do
269
+ begin
270
+ # it will always be INSERT statement
271
+
272
+ # TODO: need to investigate why PreparedStatement is giving strange exception "Protocol violation"
273
+ # s = @raw_connection.prepareStatement(sql)
274
+ # s.registerReturnParameter(1, ::Java::oracle.jdbc.OracleTypes::NUMBER)
275
+ # count = s.executeUpdate
276
+ # if count > 0
277
+ # rs = s.getReturnResultSet
278
+ # if rs.next
279
+ # # Assuming that primary key will not be larger as long max value
280
+ # insert_id = rs.getLong(1)
281
+ # rs.wasNull ? nil : insert_id
282
+ # else
283
+ # nil
284
+ # end
285
+ # else
286
+ # nil
287
+ # end
288
+
289
+ # Workaround with CallableStatement
290
+ s = @raw_connection.prepareCall("BEGIN #{sql}; END;")
291
+ s.registerOutParameter(1, java.sql.Types::BIGINT)
292
+ s.execute
293
+ insert_id = s.getLong(1)
294
+ s.wasNull ? nil : insert_id
295
+ ensure
296
+ # rs.close rescue nil
297
+ s.close rescue nil
298
+ end
299
+ end
300
+ end
301
+
302
+ def prepare(sql)
303
+ Cursor.new(self, @raw_connection.prepareStatement(sql))
304
+ end
305
+
306
+ class Cursor
307
+ def initialize(connection, raw_statement)
308
+ @connection = connection
309
+ @raw_statement = raw_statement
310
+ end
311
+
312
+ def bind_param(position, value, col_type = nil)
313
+ java_value = ruby_to_java_value(value, col_type)
314
+ case value
315
+ when Integer
316
+ @raw_statement.setLong(position, java_value)
317
+ when Float
318
+ @raw_statement.setFloat(position, java_value)
319
+ when BigDecimal
320
+ @raw_statement.setBigDecimal(position, java_value)
321
+ when String
322
+ case col_type
323
+ when :text
324
+ @raw_statement.setClob(position, java_value)
325
+ when :binary
326
+ @raw_statement.setBlob(position, java_value)
327
+ when :raw
328
+ @raw_statement.setString(position, OracleEnhancedAdapter.encode_raw(java_value))
329
+ when :decimal
330
+ @raw_statement.setBigDecimal(position, java_value)
331
+ else
332
+ @raw_statement.setString(position, java_value)
333
+ end
334
+ when Date, DateTime
335
+ @raw_statement.setDATE(position, java_value)
336
+ when Time
337
+ @raw_statement.setTimestamp(position, java_value)
338
+ when NilClass
339
+ # TODO: currently nil is always bound as NULL with VARCHAR type.
340
+ # When nils will actually be used by ActiveRecord as bound parameters
341
+ # then need to pass actual column type.
342
+ @raw_statement.setNull(position, java.sql.Types::VARCHAR)
343
+ else
344
+ raise ArgumentError, "Don't know how to bind variable with type #{value.class}"
345
+ end
346
+ end
347
+
348
+ def bind_returning_param(position, bind_type)
349
+ @returning_positions ||= []
350
+ @returning_positions << position
351
+ if bind_type == Integer
352
+ @raw_statement.registerReturnParameter(position, java.sql.Types::BIGINT)
353
+ end
354
+ end
355
+
356
+ def exec
357
+ @raw_result_set = @raw_statement.executeQuery
358
+ true
359
+ end
360
+
361
+ def exec_update
362
+ @raw_statement.executeUpdate
363
+ end
364
+
365
+ def metadata
366
+ @metadata ||= @raw_result_set.getMetaData
367
+ end
368
+
369
+ def column_types
370
+ @column_types ||= (1..metadata.getColumnCount).map{|i| metadata.getColumnTypeName(i).to_sym}
371
+ end
372
+
373
+ def column_names
374
+ @column_names ||= (1..metadata.getColumnCount).map{|i| metadata.getColumnName(i)}
375
+ end
376
+ alias :get_col_names :column_names
377
+
378
+ def fetch(options={})
379
+ if @raw_result_set.next
380
+ get_lob_value = options[:get_lob_value]
381
+ row_values = []
382
+ column_types.each_with_index do |column_type, i|
383
+ row_values <<
384
+ @connection.get_ruby_value_from_result_set(@raw_result_set, i+1, column_type, get_lob_value)
385
+ end
386
+ row_values
387
+ else
388
+ @raw_result_set.close
389
+ nil
390
+ end
391
+ end
392
+
393
+ def get_returning_param(position, type)
394
+ rs_position = @returning_positions.index(position) + 1
395
+ rs = @raw_statement.getReturnResultSet
396
+ if rs.next
397
+ # Assuming that primary key will not be larger as long max value
398
+ returning_id = rs.getLong(rs_position)
399
+ rs.wasNull ? nil : returning_id
400
+ else
401
+ nil
402
+ end
403
+ end
404
+
405
+ def close
406
+ @raw_statement.close
407
+ end
408
+
409
+ private
410
+
411
+ def ruby_to_java_value(value, col_type = nil)
412
+ case value
413
+ when Fixnum, Float
414
+ value
415
+ when String
416
+ case col_type
417
+ when :text
418
+ clob = Java::OracleSql::CLOB.createTemporary(@connection.raw_connection, false, Java::OracleSql::CLOB::DURATION_SESSION)
419
+ clob.setString(1, value)
420
+ clob
421
+ when :binary
422
+ blob = Java::OracleSql::BLOB.createTemporary(@connection.raw_connection, false, Java::OracleSql::BLOB::DURATION_SESSION)
423
+ blob.setBytes(1, value.to_java_bytes)
424
+ blob
425
+ when :decimal
426
+ java.math.BigDecimal.new(value.to_s)
427
+ else
428
+ value
429
+ end
430
+ when BigDecimal
431
+ java.math.BigDecimal.new(value.to_s)
432
+ when Date, DateTime
433
+ Java::oracle.sql.DATE.new(value.strftime("%Y-%m-%d %H:%M:%S"))
434
+ when Time
435
+ Java::java.sql.Timestamp.new(value.year-1900, value.month-1, value.day, value.hour, value.min, value.sec, value.usec * 1000)
436
+ else
437
+ value
438
+ end
439
+ end
440
+
441
+ end
442
+
443
+ def select(sql, name = nil, return_column_names = false)
444
+ with_retry do
445
+ select_no_retry(sql, name, return_column_names)
446
+ end
447
+ end
448
+
449
+ def select_no_retry(sql, name = nil, return_column_names = false)
450
+ stmt = @raw_connection.prepareStatement(sql)
451
+ rset = stmt.executeQuery
452
+
453
+ # Reuse the same hash for all rows
454
+ column_hash = {}
455
+
456
+ metadata = rset.getMetaData
457
+ column_count = metadata.getColumnCount
458
+
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)
466
+
467
+ rows = []
468
+ get_lob_value = !(name == 'Writable Large Object')
469
+
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)
474
+ end
475
+ rows << hash
476
+ end
477
+
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
483
+
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)
489
+ end
490
+ end
491
+
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
499
+ end
500
+ end
501
+
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)
510
+ else
511
+ BigDecimal.new(d.stringValue)
512
+ end
513
+ when :VARCHAR2, :CHAR, :LONG, :NVARCHAR2, :NCHAR
514
+ rset.getString(i)
515
+ when :DATE
516
+ if dt = rset.getDATE(i)
517
+ d = dt.dateValue
518
+ t = dt.timeValue
519
+ if OracleEnhancedAdapter.emulate_dates && t.hours == 0 && t.minutes == 0 && t.seconds == 0
520
+ Date.new(d.year + 1900, d.month + 1, d.date)
521
+ else
522
+ Time.send(Base.default_timezone, d.year + 1900, d.month + 1, d.date, t.hours, t.minutes, t.seconds)
523
+ end
524
+ else
525
+ nil
526
+ end
527
+ when :TIMESTAMP, :TIMESTAMPTZ, :TIMESTAMPLTZ, :"TIMESTAMP WITH TIME ZONE", :"TIMESTAMP WITH LOCAL TIME ZONE"
528
+ ts = rset.getTimestamp(i)
529
+ ts && Time.send(Base.default_timezone, ts.year + 1900, ts.month + 1, ts.date, ts.hours, ts.minutes, ts.seconds,
530
+ ts.nanos / 1000)
531
+ when :CLOB
532
+ get_lob_value ? lob_to_ruby_value(rset.getClob(i)) : rset.getClob(i)
533
+ when :BLOB
534
+ get_lob_value ? lob_to_ruby_value(rset.getBlob(i)) : rset.getBlob(i)
535
+ when :RAW
536
+ raw_value = rset.getRAW(i)
537
+ raw_value && raw_value.getBytes.to_a.pack('C*')
538
+ else
539
+ nil
540
+ end
541
+ end
542
+
543
+ private
544
+
545
+ def lob_to_ruby_value(val)
546
+ case val
547
+ when ::Java::OracleSql::CLOB
548
+ if val.isEmptyLob
549
+ nil
550
+ else
551
+ val.getSubString(1, val.length)
552
+ end
553
+ when ::Java::OracleSql::BLOB
554
+ if val.isEmptyLob
555
+ nil
556
+ else
557
+ String.from_java_bytes(val.getBytes(1, val.length))
558
+ end
559
+ end
560
+ end
561
+
562
+ end
563
+
564
+ end
565
+ end