pmacs-activerecord-oracle_enhanced-adapter 1.4.2.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/.rspec +2 -0
  2. data/Gemfile +52 -0
  3. data/History.md +284 -0
  4. data/License.txt +20 -0
  5. data/README.md +403 -0
  6. data/RUNNING_TESTS.md +45 -0
  7. data/Rakefile +59 -0
  8. data/VERSION +1 -0
  9. data/lib/active_record/connection_adapters/emulation/oracle_adapter.rb +5 -0
  10. data/lib/active_record/connection_adapters/oracle_enhanced.rake +105 -0
  11. data/lib/active_record/connection_adapters/oracle_enhanced_activerecord_patches.rb +41 -0
  12. data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +1408 -0
  13. data/lib/active_record/connection_adapters/oracle_enhanced_base_ext.rb +118 -0
  14. data/lib/active_record/connection_adapters/oracle_enhanced_column.rb +141 -0
  15. data/lib/active_record/connection_adapters/oracle_enhanced_connection.rb +135 -0
  16. data/lib/active_record/connection_adapters/oracle_enhanced_context_index.rb +359 -0
  17. data/lib/active_record/connection_adapters/oracle_enhanced_core_ext.rb +25 -0
  18. data/lib/active_record/connection_adapters/oracle_enhanced_cpk.rb +21 -0
  19. data/lib/active_record/connection_adapters/oracle_enhanced_dirty.rb +44 -0
  20. data/lib/active_record/connection_adapters/oracle_enhanced_jdbc_connection.rb +565 -0
  21. data/lib/active_record/connection_adapters/oracle_enhanced_oci_connection.rb +491 -0
  22. data/lib/active_record/connection_adapters/oracle_enhanced_procedures.rb +260 -0
  23. data/lib/active_record/connection_adapters/oracle_enhanced_schema_definitions.rb +231 -0
  24. data/lib/active_record/connection_adapters/oracle_enhanced_schema_dumper.rb +257 -0
  25. data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements.rb +397 -0
  26. data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements_ext.rb +265 -0
  27. data/lib/active_record/connection_adapters/oracle_enhanced_structure_dump.rb +294 -0
  28. data/lib/active_record/connection_adapters/oracle_enhanced_tasks.rb +17 -0
  29. data/lib/active_record/connection_adapters/oracle_enhanced_version.rb +1 -0
  30. data/lib/pmacs-activerecord-oracle_enhanced-adapter.rb +25 -0
  31. data/pmacs-activerecord-oracle_enhanced-adapter.gemspec +131 -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 +1376 -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 +438 -0
  43. data/spec/active_record/connection_adapters/oracle_enhanced_schema_statements_spec.rb +1280 -0
  44. data/spec/active_record/connection_adapters/oracle_enhanced_structure_dump_spec.rb +339 -0
  45. data/spec/spec_helper.rb +187 -0
  46. metadata +302 -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,44 @@
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
+ else
22
+ value = column.type_cast(value)
23
+ end
24
+ end
25
+
26
+ old != value
27
+ end
28
+
29
+ end
30
+
31
+ end
32
+ end
33
+ end
34
+
35
+ if ActiveRecord::Base.method_defined?(:changed?)
36
+ ActiveRecord::Base.class_eval do
37
+ include ActiveRecord::ConnectionAdapters::OracleEnhancedDirty::InstanceMethods
38
+ # Starting with rails 3.2.9 the method #field_changed?
39
+ # was renamed to #_field_changed?
40
+ if private_method_defined?(:field_changed?)
41
+ alias_method :field_changed?, :_field_changed?
42
+ end
43
+ end
44
+ 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(/[:;]/)
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