ruby-plsql 0.9.9-java

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.
@@ -0,0 +1,588 @@
1
+ ojdbc_jars = []
2
+
3
+ begin
4
+ require "java"
5
+ require "jruby"
6
+
7
+ # Oracle JDBC driver jar should be in JRUBY_HOME/lib or should be in ENV['PATH'] or load path
8
+
9
+ java_version = java.lang.System.getProperty("java.version")
10
+ java_major = if java_version =~ /^1\.(\d+)/
11
+ $1.to_i
12
+ else
13
+ java_version.to_i
14
+ end
15
+
16
+ ojdbc_jars << "ojdbc17.jar" if java_major >= 17
17
+ ojdbc_jars << "ojdbc11.jar" if java_major >= 11
18
+ ojdbc_jars << "ojdbc8.jar" if java_major >= 8
19
+ ojdbc_jars << "ojdbc7.jar" if java_major >= 7
20
+ ojdbc_jars << "ojdbc6.jar" if java_major >= 6
21
+ ojdbc_jars << "ojdbc5.jar" if java_major == 5
22
+
23
+ if ENV_JAVA["java.class.path"] !~ Regexp.union(ojdbc_jars)
24
+ # On Unix environment variable should be PATH, on Windows it is sometimes Path
25
+ env_path = (ENV["PATH"] || ENV["Path"] || "").split(File::PATH_SEPARATOR)
26
+ # Look for JDBC driver at first in lib subdirectory (application specific JDBC file version)
27
+ # then in Ruby load path and finally in environment PATH
28
+ ["./lib"].concat($LOAD_PATH).concat(env_path).detect do |dir|
29
+ # check any compatible JDBC driver in the priority order
30
+ ojdbc_jars.any? do |ojdbc_jar|
31
+ if File.exist?(file_path = File.join(dir, ojdbc_jar))
32
+ require file_path
33
+ true
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ # set tns_admin property from TNS_ADMIN environment variable
40
+ if !java.lang.System.get_property("oracle.net.tns_admin") && ENV["TNS_ADMIN"]
41
+ java.lang.System.set_property("oracle.net.tns_admin", ENV["TNS_ADMIN"])
42
+ end
43
+
44
+ rescue LoadError
45
+ # JDBC driver is unavailable.
46
+ raise LoadError, "ERROR: ruby-plsql could not load Oracle JDBC driver. Please install #{ojdbc_jars.empty? ? "Oracle JDBC" : ojdbc_jars.join(' or ') } library."
47
+ end
48
+
49
+ module PLSQL
50
+ class JDBCConnection < Connection # :nodoc:
51
+ begin
52
+ ORACLE_DRIVER = Java::oracle.jdbc.OracleDriver.new
53
+ java.sql.DriverManager.registerDriver ORACLE_DRIVER
54
+ rescue NameError
55
+ raise LoadError, "ERROR: ruby-plsql could not load Oracle JDBC driver. " \
56
+ "Please install the appropriate Oracle JDBC driver. " \
57
+ "See https://www.oracle.com/database/technologies/appdev/jdbc-downloads.html"
58
+ end
59
+
60
+ def self.create_raw(params)
61
+ url = jdbc_connection_url(params)
62
+ conn = begin
63
+ java.sql.DriverManager.getConnection(url, params[:username], params[:password])
64
+ rescue Java::JavaSql::SQLException => e
65
+ raise unless e.message =~ /no suitable driver/i
66
+ # bypass DriverManager to work in cases where ojdbc*.jar
67
+ # is added to the load path at runtime and not on the
68
+ # system classpath
69
+ ORACLE_DRIVER.connect(url, java.util.Properties.new.tap do |props|
70
+ props.setProperty("user", params[:username])
71
+ props.setProperty("password", params[:password])
72
+ end)
73
+ end
74
+ conn.setAutoCommit(false)
75
+ new(conn)
76
+ end
77
+
78
+ def self.jdbc_connection_url(params)
79
+ database = params[:database]
80
+ if ENV["TNS_ADMIN"] && database && database !~ %r{\A[:/]} && !params[:host] && !params[:url]
81
+ "jdbc:oracle:thin:@#{database}"
82
+ else
83
+ return params[:url] if params[:url]
84
+
85
+ raise ArgumentError, "database or url option is required" if database.nil? || database.empty?
86
+
87
+ host = params[:host] || "localhost"
88
+ port = params[:port] || 1521
89
+
90
+ if database =~ /^:/
91
+ # SID syntax: jdbc:oracle:thin:@host:port:SID
92
+ "jdbc:oracle:thin:@#{host}:#{port}#{database}"
93
+ else
94
+ # service name syntax: jdbc:oracle:thin:@//host:port/service_name
95
+ database = "/#{database}" unless database =~ /^\//
96
+ "jdbc:oracle:thin:@//#{host}:#{port}#{database}"
97
+ end
98
+ end
99
+ end
100
+
101
+ def set_time_zone(time_zone = nil)
102
+ raw_connection.setSessionTimeZone(time_zone) if time_zone
103
+ end
104
+
105
+ def logoff
106
+ super
107
+ raw_connection.close
108
+ true
109
+ rescue
110
+ false
111
+ end
112
+
113
+ def commit
114
+ raw_connection.commit
115
+ end
116
+
117
+ def rollback
118
+ raw_connection.rollback
119
+ end
120
+
121
+ def autocommit?
122
+ raw_connection.getAutoCommit
123
+ end
124
+
125
+ def autocommit=(value)
126
+ raw_connection.setAutoCommit(value)
127
+ end
128
+
129
+ def prefetch_rows=(value)
130
+ raw_connection.setDefaultRowPrefetch(value)
131
+ end
132
+
133
+ def exec(sql, *bindvars)
134
+ cs = prepare_call(sql, *bindvars)
135
+ cs.execute
136
+ true
137
+ ensure
138
+ cs.close rescue nil
139
+ end
140
+
141
+ class CallableStatement # :nodoc:
142
+ def initialize(conn, sql)
143
+ @sql = sql
144
+ @connection = conn
145
+ @params = sql.scan(/\:\w+/)
146
+ @out_types = {}
147
+ @out_index = {}
148
+ @statement = @connection.prepare_call(sql)
149
+ end
150
+
151
+ def bind_param(arg, value, metadata)
152
+ type, length = @connection.plsql_to_ruby_data_type(metadata)
153
+ ora_value = @connection.ruby_value_to_ora_value(value, type, metadata)
154
+ @connection.set_bind_variable(@statement, arg, ora_value, type, length, metadata)
155
+ if metadata[:in_out] =~ /OUT/
156
+ @out_types[arg] = type || ora_value.class
157
+ @out_index[arg] = bind_param_index(arg)
158
+ if ["TABLE", "VARRAY", "OBJECT", "XMLTYPE"].include?(metadata[:data_type])
159
+ @statement.registerOutParameter(@out_index[arg], @connection.get_java_sql_type(ora_value, type),
160
+ metadata[:sql_type_name])
161
+ else
162
+ @statement.registerOutParameter(@out_index[arg], @connection.get_java_sql_type(ora_value, type))
163
+ end
164
+ end
165
+ end
166
+
167
+ def exec
168
+ @statement.execute
169
+ end
170
+
171
+ def [](key)
172
+ @connection.ora_value_to_ruby_value(@connection.get_bind_variable(@statement, @out_index[key], @out_types[key]))
173
+ end
174
+
175
+ def close
176
+ @statement.close
177
+ end
178
+
179
+ private
180
+
181
+ def bind_param_index(key)
182
+ return key if key.kind_of? Integer
183
+ key = ":#{key.to_s}" unless key.to_s =~ /^:/
184
+ @params.index(key) + 1
185
+ end
186
+ end
187
+
188
+ class Cursor # :nodoc:
189
+ include Connection::CursorCommon
190
+
191
+ attr_reader :result_set
192
+ attr_accessor :statement
193
+
194
+ def initialize(conn, result_set)
195
+ @connection = conn
196
+ @result_set = result_set
197
+ @metadata = @result_set.getMetaData
198
+ @column_count = @metadata.getColumnCount
199
+ @column_type_names = [nil] # column numbering starts at 1
200
+ (1..@column_count).each do |i|
201
+ @column_type_names << { type_name: @metadata.getColumnTypeName(i), sql_type: @metadata.getColumnType(i) }
202
+ end
203
+ end
204
+
205
+ def self.new_from_query(conn, sql, bindvars = [], options = {})
206
+ stmt = conn.prepare_statement(sql, *bindvars)
207
+ if prefetch_rows = options[:prefetch_rows]
208
+ stmt.setRowPrefetch(prefetch_rows)
209
+ end
210
+ cursor = Cursor.new(conn, stmt.executeQuery)
211
+ cursor.statement = stmt
212
+ cursor
213
+ rescue
214
+ # in case of any error close statement
215
+ stmt.close rescue nil
216
+ raise
217
+ end
218
+
219
+ def fetch
220
+ if @result_set.next
221
+ (1..@column_count).map do |i|
222
+ @connection.get_ruby_value_from_result_set(@result_set, i, @column_type_names[i])
223
+ end
224
+ else
225
+ nil
226
+ end
227
+ end
228
+
229
+ def fields
230
+ @fields ||= (1..@column_count).map do |i|
231
+ @metadata.getColumnName(i).downcase.to_sym
232
+ end
233
+ end
234
+
235
+ def close
236
+ @result_set.close
237
+ @statement.close if @statement
238
+ end
239
+ end
240
+
241
+ def parse(sql)
242
+ CallableStatement.new(self, sql)
243
+ end
244
+
245
+ def cursor_from_query(sql, bindvars = [], options = {})
246
+ Cursor.new_from_query(self, sql, bindvars, options)
247
+ end
248
+
249
+ def prepare_statement(sql, *bindvars)
250
+ stmt = raw_connection.prepareStatement(sql)
251
+ bindvars.each_with_index do |bv, i|
252
+ set_bind_variable(stmt, i + 1, ruby_value_to_ora_value(bv))
253
+ end
254
+ stmt
255
+ end
256
+
257
+ def prepare_call(sql, *bindvars)
258
+ stmt = raw_connection.prepareCall(sql)
259
+ bindvars.each_with_index do |bv, i|
260
+ set_bind_variable(stmt, i + 1, bv)
261
+ end
262
+ stmt
263
+ end
264
+
265
+ RUBY_CLASS_TO_SQL_TYPE = {
266
+ Integer => java.sql.Types::INTEGER,
267
+ Float => java.sql.Types::FLOAT,
268
+ BigDecimal => java.sql.Types::NUMERIC,
269
+ String => java.sql.Types::VARCHAR,
270
+ Java::OracleSql::CLOB => Java::oracle.jdbc.OracleTypes::CLOB,
271
+ Java::OracleSql::BLOB => Java::oracle.jdbc.OracleTypes::BLOB,
272
+ Date => java.sql.Types::DATE,
273
+ Time => java.sql.Types::TIMESTAMP,
274
+ DateTime => java.sql.Types::DATE,
275
+ Java::OracleSql::ARRAY => Java::oracle.jdbc.OracleTypes::ARRAY,
276
+ Array => Java::oracle.jdbc.OracleTypes::ARRAY,
277
+ Java::OracleSql::STRUCT => Java::oracle.jdbc.OracleTypes::STRUCT,
278
+ Hash => Java::oracle.jdbc.OracleTypes::STRUCT,
279
+ java.sql.ResultSet => Java::oracle.jdbc.OracleTypes::CURSOR,
280
+ }
281
+
282
+ SQL_TYPE_TO_RUBY_CLASS = {
283
+ java.sql.Types::CHAR => String,
284
+ java.sql.Types::NCHAR => String,
285
+ java.sql.Types::VARCHAR => String,
286
+ java.sql.Types::NVARCHAR => String,
287
+ java.sql.Types::LONGVARCHAR => String,
288
+ java.sql.Types::NUMERIC => BigDecimal,
289
+ java.sql.Types::INTEGER => Integer,
290
+ java.sql.Types::DATE => Time,
291
+ java.sql.Types::TIMESTAMP => Time,
292
+ Java::oracle.jdbc.OracleTypes::TIMESTAMPTZ => Time,
293
+ Java::oracle.jdbc.OracleTypes::TIMESTAMPLTZ => Time,
294
+ java.sql.Types::BLOB => String,
295
+ java.sql.Types::CLOB => String,
296
+ java.sql.Types::ARRAY => Java::OracleSql::ARRAY,
297
+ java.sql.Types::STRUCT => Java::OracleSql::STRUCT,
298
+ Java::oracle.jdbc.OracleTypes::CURSOR => java.sql.ResultSet
299
+ }
300
+
301
+ def get_java_sql_type(value, type)
302
+ RUBY_CLASS_TO_SQL_TYPE[type || value.class] || java.sql.Types::VARCHAR
303
+ end
304
+
305
+ def set_bind_variable(stmt, i, value, type = nil, length = nil, metadata = {})
306
+ key = i.kind_of?(Integer) ? nil : i.to_s.gsub(":", "")
307
+ type_symbol = (!value.nil? && type ? type : value.class).to_s.to_sym
308
+ case type_symbol
309
+ when :Fixnum, :Bignum, :Integer
310
+ stmt.send("setInt#{key && "AtName"}", key || i, value)
311
+ when :Float
312
+ stmt.send("setFloat#{key && "AtName"}", key || i, value)
313
+ when :BigDecimal, :'Java::JavaMath::BigDecimal'
314
+ stmt.send("setBigDecimal#{key && "AtName"}", key || i, value)
315
+ when :String
316
+ stmt.send("setString#{key && "AtName"}", key || i, value)
317
+ when :'Java::OracleSql::CLOB'
318
+ stmt.send("setClob#{key && "AtName"}", key || i, value)
319
+ when :'Java::OracleSql::BLOB'
320
+ stmt.send("setBlob#{key && "AtName"}", key || i, value)
321
+ when :Date, :DateTime, :'Java::OracleSql::DATE'
322
+ stmt.send("setDATE#{key && "AtName"}", key || i, value)
323
+ when :Time, :'Java::JavaSql::Timestamp'
324
+ stmt.send("setTimestamp#{key && "AtName"}", key || i, value)
325
+ when :NilClass
326
+ if ["TABLE", "VARRAY", "OBJECT", "XMLTYPE"].include?(metadata[:data_type])
327
+ stmt.send("setNull#{key && "AtName"}", key || i, get_java_sql_type(value, type),
328
+ metadata[:sql_type_name])
329
+ elsif metadata[:data_type] == "REF CURSOR"
330
+ # TODO: cannot bind NULL value to cursor parameter, getting error
331
+ # java.sql.SQLException: Unsupported feature: sqlType=-10
332
+ # Currently do nothing and assume that NULL values will not be passed to IN parameters
333
+ # If cursor is IN/OUT or OUT parameter then it should work
334
+ else
335
+ stmt.send("setNull#{key && "AtName"}", key || i, get_java_sql_type(value, type))
336
+ end
337
+ when :'Java::OracleSql::ARRAY'
338
+ stmt.send("setARRAY#{key && "AtName"}", key || i, value)
339
+ when :'Java::OracleSql::STRUCT'
340
+ stmt.send("setSTRUCT#{key && "AtName"}", key || i, value)
341
+ when :'Java::JavaSql::ResultSet'
342
+ # TODO: cannot find how to pass cursor parameter from JDBC
343
+ # setCursor is giving exception java.sql.SQLException: Unsupported feature
344
+ stmt.send("setCursor#{key && "AtName"}", key || i, value)
345
+ else
346
+ raise ArgumentError, "Don't know how to bind variable with type #{type_symbol}"
347
+ end
348
+ end
349
+
350
+ def get_bind_variable(stmt, i, type)
351
+ case type.to_s.to_sym
352
+ when :Fixnum, :Bignum, :Integer
353
+ stmt.getObject(i)
354
+ when :Float
355
+ stmt.getFloat(i)
356
+ when :BigDecimal
357
+ bd = stmt.getBigDecimal(i)
358
+ bd && BigDecimal(bd.to_s)
359
+ when :String
360
+ stmt.getString(i)
361
+ when :'Java::OracleSql::CLOB'
362
+ stmt.getClob(i)
363
+ when :'Java::OracleSql::BLOB'
364
+ stmt.getBlob(i)
365
+ when :Date, :DateTime
366
+ stmt.getDATE(i)
367
+ when :Time
368
+ stmt.getTimestamp(i)
369
+ when :'Java::OracleSql::ARRAY'
370
+ stmt.getArray(i)
371
+ when :'Java::OracleSql::STRUCT'
372
+ stmt.getSTRUCT(i)
373
+ when :'Java::JavaSql::ResultSet'
374
+ stmt.getCursor(i)
375
+ end
376
+ end
377
+
378
+ def get_ruby_value_from_result_set(rset, i, metadata)
379
+ ruby_type = SQL_TYPE_TO_RUBY_CLASS[metadata[:sql_type]]
380
+ ora_value = get_bind_variable(rset, i, ruby_type)
381
+ result_new = ora_value_to_ruby_value(ora_value)
382
+ end
383
+
384
+ def result_set_to_ruby_data_type(column_type, column_type_name)
385
+ end
386
+
387
+ def plsql_to_ruby_data_type(metadata)
388
+ data_type, data_length = metadata[:data_type], metadata[:data_length]
389
+ case data_type
390
+ when "VARCHAR", "VARCHAR2", "CHAR", "NVARCHAR2", "NCHAR"
391
+ [String, data_length || 32767]
392
+ when "CLOB", "NCLOB"
393
+ [Java::OracleSql::CLOB, nil]
394
+ when "BLOB"
395
+ [Java::OracleSql::BLOB, nil]
396
+ when "NUMBER"
397
+ [BigDecimal, nil]
398
+ when "NATURAL", "NATURALN", "POSITIVE", "POSITIVEN", "SIGNTYPE", "SIMPLE_INTEGER", "PLS_INTEGER", "BINARY_INTEGER"
399
+ [Integer, nil]
400
+ when "DATE"
401
+ [DateTime, nil]
402
+ when "TIMESTAMP", "TIMESTAMP WITH TIME ZONE", "TIMESTAMP WITH LOCAL TIME ZONE"
403
+ [Time, nil]
404
+ when "TABLE", "VARRAY"
405
+ [Java::OracleSql::ARRAY, nil]
406
+ when "OBJECT"
407
+ [Java::OracleSql::STRUCT, nil]
408
+ when "REF CURSOR"
409
+ [java.sql.ResultSet, nil]
410
+ else
411
+ [String, 32767]
412
+ end
413
+ end
414
+
415
+ def ruby_value_to_ora_value(value, type = nil, metadata = {})
416
+ type ||= value.class
417
+ case type.to_s.to_sym
418
+ when :Integer
419
+ value
420
+ when :String
421
+ value.to_s
422
+ when :BigDecimal
423
+ case value
424
+ when TrueClass
425
+ java_bigdecimal(1)
426
+ when FalseClass
427
+ java_bigdecimal(0)
428
+ else
429
+ java_bigdecimal(value)
430
+ end
431
+ when :Date, :DateTime
432
+ case value
433
+ when DateTime
434
+ java_date(Time.send(plsql.default_timezone, value.year, value.month, value.day, value.hour, value.min, value.sec))
435
+ when Date
436
+ java_date(Time.send(plsql.default_timezone, value.year, value.month, value.day, 0, 0, 0))
437
+ else
438
+ java_date(value)
439
+ end
440
+ when :Time
441
+ java_timestamp(value)
442
+ when :'Java::OracleSql::CLOB'
443
+ if value
444
+ clob = Java::OracleSql::CLOB.createTemporary(raw_connection, false, Java::OracleSql::CLOB::DURATION_SESSION)
445
+ clob.setString(1, value)
446
+ clob
447
+ else
448
+ nil
449
+ end
450
+ when :'Java::OracleSql::BLOB'
451
+ if value
452
+ blob = Java::OracleSql::BLOB.createTemporary(raw_connection, false, Java::OracleSql::BLOB::DURATION_SESSION)
453
+ blob.setBytes(1, value.to_java_bytes)
454
+ blob
455
+ else
456
+ nil
457
+ end
458
+ when :'Java::OracleSql::ARRAY'
459
+ if value
460
+ raise ArgumentError, "You should pass Array value for collection type parameter" unless value.is_a?(Array)
461
+ descriptor = Java::OracleSql::ArrayDescriptor.createDescriptor(metadata[:sql_type_name], raw_connection)
462
+ elem_type = descriptor.getBaseType
463
+ elem_type_name = descriptor.getBaseName
464
+ elem_list = value.map do |elem|
465
+ case elem_type
466
+ when Java::oracle.jdbc.OracleTypes::ARRAY
467
+ ruby_value_to_ora_value(elem, Java::OracleSql::ARRAY, sql_type_name: elem_type_name)
468
+ when Java::oracle.jdbc.OracleTypes::STRUCT
469
+ ruby_value_to_ora_value(elem, Java::OracleSql::STRUCT, sql_type_name: elem_type_name)
470
+ else
471
+ ruby_value_to_ora_value(elem)
472
+ end
473
+ end
474
+ Java::OracleSql::ARRAY.new(descriptor, raw_connection, elem_list.to_java)
475
+ end
476
+ when :'Java::OracleSql::STRUCT'
477
+ if value
478
+ raise ArgumentError, "You should pass Hash value for object type parameter" unless value.is_a?(Hash)
479
+ descriptor = Java::OracleSql::StructDescriptor.createDescriptor(metadata[:sql_type_name], raw_connection)
480
+ struct_metadata = descriptor.getMetaData
481
+ struct_fields = (1..descriptor.getLength).inject({}) do |hash, i|
482
+ hash[struct_metadata.getColumnName(i).downcase.to_sym] =
483
+ { type: struct_metadata.getColumnType(i), type_name: struct_metadata.getColumnTypeName(i) }
484
+ hash
485
+ end
486
+ object_attrs = java.util.HashMap.new
487
+ value.each do |key, attr_value|
488
+ raise ArgumentError, "Wrong object type field passed to PL/SQL procedure" unless (field = struct_fields[key])
489
+ case field[:type]
490
+ when Java::oracle.jdbc.OracleTypes::ARRAY
491
+ # nested collection
492
+ object_attrs.put(key.to_s.upcase, ruby_value_to_ora_value(attr_value, Java::OracleSql::ARRAY, sql_type_name: field[:type_name]))
493
+ when Java::oracle.jdbc.OracleTypes::STRUCT
494
+ # nested object type
495
+ object_attrs.put(key.to_s.upcase, ruby_value_to_ora_value(attr_value, Java::OracleSql::STRUCT, sql_type_name: field[:type_name]))
496
+ else
497
+ object_attrs.put(key.to_s.upcase, ruby_value_to_ora_value(attr_value))
498
+ end
499
+ end
500
+ Java::OracleSql::STRUCT.new(descriptor, raw_connection, object_attrs)
501
+ end
502
+ when :'Java::JavaSql::ResultSet'
503
+ if value
504
+ value.result_set
505
+ end
506
+ else
507
+ value
508
+ end
509
+ end
510
+
511
+ def ora_value_to_ruby_value(value)
512
+ case value
513
+ when Float, BigDecimal
514
+ ora_number_to_ruby_number(value)
515
+ when Java::JavaMath::BigDecimal
516
+ value && ora_number_to_ruby_number(BigDecimal(value.to_s))
517
+ when Java::OracleSql::DATE
518
+ if value
519
+ d = value.dateValue
520
+ t = value.timeValue
521
+ Time.send(plsql.default_timezone, d.year + 1900, d.month + 1, d.date, t.hours, t.minutes, t.seconds)
522
+ end
523
+ when Java::JavaSql::Timestamp
524
+ if value
525
+ Time.send(plsql.default_timezone, value.year + 1900, value.month + 1, value.date, value.hours, value.minutes, value.seconds,
526
+ value.nanos / 1000)
527
+ end
528
+ when Java::OracleSql::CLOB
529
+ if value.isEmptyLob
530
+ nil
531
+ else
532
+ value.getSubString(1, value.length)
533
+ end
534
+ when Java::OracleSql::BLOB
535
+ if value.isEmptyLob
536
+ nil
537
+ else
538
+ String.from_java_bytes(value.getBytes(1, value.length))
539
+ end
540
+ when Java::OracleSql::ARRAY
541
+ value.getArray.map { |e| ora_value_to_ruby_value(e) }
542
+ when Java::OracleSql::STRUCT
543
+ descriptor = value.getDescriptor
544
+ struct_metadata = descriptor.getMetaData
545
+ field_names = (1..descriptor.getLength).map { |i| struct_metadata.getColumnName(i).downcase.to_sym }
546
+ field_values = value.getAttributes.map { |e| ora_value_to_ruby_value(e) }
547
+ ArrayHelpers::to_hash(field_names, field_values)
548
+ when Java::java.sql.ResultSet
549
+ Cursor.new(self, value)
550
+ else
551
+ value
552
+ end
553
+ end
554
+
555
+ def database_version
556
+ @database_version ||= if md = raw_connection.getMetaData
557
+ major = md.getDatabaseMajorVersion
558
+ minor = md.getDatabaseMinorVersion
559
+ if md.getDatabaseProductVersion =~ /#{major}\.#{minor}\.(\d+)\.(\d+)/
560
+ update = $1.to_i
561
+ patch = $2.to_i
562
+ else
563
+ update = patch = 0
564
+ end
565
+ [major, minor, update, patch]
566
+ end
567
+ end
568
+
569
+ private
570
+
571
+ def java_date(value)
572
+ value && Java::oracle.sql.DATE.new(value.strftime("%Y-%m-%d %H:%M:%S"))
573
+ end
574
+
575
+ def java_timestamp(value)
576
+ value && Java::java.sql.Timestamp.new(value.year - 1900, value.month - 1, value.day, value.hour, value.min, value.sec, value.usec * 1000)
577
+ end
578
+
579
+ def java_bigdecimal(value)
580
+ value && java.math.BigDecimal.new(value.to_s)
581
+ end
582
+
583
+ def ora_number_to_ruby_number(num)
584
+ # return BigDecimal instead of Float to avoid rounding errors
585
+ num == (num_to_i = num.to_i) ? num_to_i : (num.is_a?(BigDecimal) ? num : BigDecimal(num.to_s))
586
+ end
587
+ end
588
+ end