flash-gordons-ruby-plsql 0.5.0

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.
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.5.0
@@ -0,0 +1,233 @@
1
+ module PLSQL
2
+ class Connection
3
+ attr_reader :raw_driver
4
+ attr_reader :activerecord_class
5
+
6
+ def initialize(raw_conn, ar_class = nil) #:nodoc:
7
+ @raw_driver = self.class.driver_type
8
+ @raw_connection = raw_conn
9
+ @activerecord_class = ar_class
10
+ end
11
+
12
+ def self.create(raw_conn, ar_class = nil) #:nodoc:
13
+ if ar_class && !(defined?(::ActiveRecord) && ar_class.ancestors.include?(::ActiveRecord::Base))
14
+ raise ArgumentError, "Wrong ActiveRecord class"
15
+ end
16
+ case driver_type
17
+ when :oci
18
+ OCIConnection.new(raw_conn, ar_class)
19
+ when :jdbc
20
+ JDBCConnection.new(raw_conn, ar_class)
21
+ else
22
+ raise ArgumentError, "Unknown raw driver"
23
+ end
24
+ end
25
+
26
+ def self.create_new(params) #:nodoc:
27
+ conn = case driver_type
28
+ when :oci
29
+ OCIConnection.create_raw(params)
30
+ when :jdbc
31
+ JDBCConnection.create_raw(params)
32
+ else
33
+ raise ArgumentError, "Unknown raw driver"
34
+ end
35
+ conn.set_time_zone(params[:time_zone])
36
+ conn
37
+ end
38
+
39
+ def self.driver_type #:nodoc:
40
+ # MRI 1.8.6 or YARV 1.9.1
41
+ @driver_type ||= if (!defined?(RUBY_ENGINE) || RUBY_ENGINE == "ruby") && defined?(OCI8)
42
+ :oci
43
+ # JRuby
44
+ elsif (defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby")
45
+ :jdbc
46
+ else
47
+ nil
48
+ end
49
+ end
50
+
51
+ # Returns OCI8 or JDBC connection
52
+ def raw_connection
53
+ if @activerecord_class
54
+ @activerecord_class.connection.raw_connection
55
+ else
56
+ @raw_connection
57
+ end
58
+ end
59
+
60
+ # Is it OCI8 connection
61
+ def oci?
62
+ @raw_driver == :oci
63
+ end
64
+
65
+ # Is it JDBC connection
66
+ def jdbc?
67
+ @raw_driver == :jdbc
68
+ end
69
+
70
+ def logoff #:nodoc:
71
+ # Rollback any uncommited transactions
72
+ rollback
73
+ # Common cleanup activities before logoff, should be called from particular driver method
74
+ drop_session_ruby_temporary_tables
75
+ end
76
+
77
+ def commit #:nodoc:
78
+ raise NoMethodError, "Not implemented for this raw driver"
79
+ end
80
+
81
+ def rollback #:nodoc:
82
+ raise NoMethodError, "Not implemented for this raw driver"
83
+ end
84
+
85
+ # Current autocommit mode (true or false)
86
+ def autocommit?
87
+ raise NoMethodError, "Not implemented for this raw driver"
88
+ end
89
+
90
+ # Set autocommit mode (true or false)
91
+ def autocommit=(value)
92
+ raise NoMethodError, "Not implemented for this raw driver"
93
+ end
94
+
95
+ # Set number of rows to be prefetched. This can reduce the number of network round trips when fetching many rows.
96
+ # The default value is one. (If ActiveRecord oracle_enhanced connection is used then default is 100)
97
+ def prefetch_rows=(value)
98
+ raise NoMethodError, "Not implemented for this raw driver"
99
+ end
100
+
101
+ def select_first(sql, *bindvars) #:nodoc:
102
+ cursor = cursor_from_query(sql, bindvars, :prefetch_rows => 1)
103
+ cursor.fetch
104
+ ensure
105
+ cursor.close rescue nil
106
+ end
107
+
108
+ def select_hash_first(sql, *bindvars) #:nodoc:
109
+ cursor = cursor_from_query(sql, bindvars, :prefetch_rows => 1)
110
+ cursor.fetch_hash
111
+ ensure
112
+ cursor.close rescue nil
113
+ end
114
+
115
+ def select_all(sql, *bindvars, &block) #:nodoc:
116
+ cursor = cursor_from_query(sql, bindvars)
117
+ results = []
118
+ row_count = 0
119
+ while row = cursor.fetch
120
+ if block_given?
121
+ yield(row)
122
+ row_count += 1
123
+ else
124
+ results << row
125
+ end
126
+ end
127
+ block_given? ? row_count : results
128
+ ensure
129
+ cursor.close rescue nil
130
+ end
131
+
132
+ def select_hash_all(sql, *bindvars, &block) #:nodoc:
133
+ cursor = cursor_from_query(sql, bindvars)
134
+ results = []
135
+ row_count = 0
136
+ while row = cursor.fetch_hash
137
+ if block_given?
138
+ yield(row)
139
+ row_count += 1
140
+ else
141
+ results << row
142
+ end
143
+ end
144
+ block_given? ? row_count : results
145
+ ensure
146
+ cursor.close rescue nil
147
+ end
148
+
149
+ def exec(sql, *bindvars) #:nodoc:
150
+ raise NoMethodError, "Not implemented for this raw driver"
151
+ end
152
+
153
+ def parse(sql) #:nodoc:
154
+ raise NoMethodError, "Not implemented for this raw driver"
155
+ end
156
+
157
+ module CursorCommon
158
+ # Fetch all rows from cursor, each row as array of values
159
+ def fetch_all
160
+ rows = []
161
+ while (row = fetch)
162
+ rows << row
163
+ end
164
+ rows
165
+ end
166
+
167
+ # Fetch all rows from cursor, each row as hash {:column => value, ...}
168
+ def fetch_hash_all
169
+ rows = []
170
+ while (row = fetch_hash)
171
+ rows << row
172
+ end
173
+ rows
174
+ end
175
+
176
+ # Fetch row from cursor as hash {:column => value, ...}
177
+ def fetch_hash
178
+ (row = fetch) && ArrayHelpers::to_hash(fields, row)
179
+ end
180
+ end
181
+
182
+ # all_synonyms view is quite slow therefore
183
+ # this implementation is overriden in OCI connection with faster native OCI method
184
+ def describe_synonym(schema_name, synonym_name) #:nodoc:
185
+ select_first(
186
+ "SELECT table_owner, table_name FROM all_synonyms WHERE owner = :owner AND synonym_name = :synonym_name",
187
+ schema_name.to_s.upcase, synonym_name.to_s.upcase)
188
+ end
189
+
190
+ # Returns array with major and minor version of database (e.g. [10, 2])
191
+ def database_version
192
+ raise NoMethodError, "Not implemented for this raw driver"
193
+ end
194
+
195
+ # Returns session ID
196
+ def session_id
197
+ @session_id ||= select_first("SELECT TO_NUMBER(USERENV('SESSIONID')) FROM dual")[0]
198
+ end
199
+
200
+ # Set time zone (default taken from TZ environment variable)
201
+ def set_time_zone(time_zone=nil)
202
+ time_zone ||= ENV['TZ']
203
+ exec("alter session set time_zone = '#{time_zone}'") if time_zone
204
+ end
205
+
206
+ # Returns session time zone
207
+ def time_zone
208
+ select_first("SELECT SESSIONTIMEZONE FROM dual")[0]
209
+ end
210
+
211
+ RUBY_TEMP_TABLE_PREFIX = 'ruby_'
212
+
213
+ # Drop all ruby temporary tables that are used for calling packages with table parameter types defined in packages
214
+ def drop_all_ruby_temporary_tables
215
+ select_all("SELECT table_name FROM user_tables WHERE temporary='Y' AND table_name LIKE :table_name",
216
+ RUBY_TEMP_TABLE_PREFIX.upcase+'%').each do |row|
217
+ exec "TRUNCATE TABLE #{row[0]}"
218
+ exec "DROP TABLE #{row[0]}"
219
+ end
220
+ end
221
+
222
+ # Drop ruby temporary tables created in current session that are used for calling packages with table parameter types defined in packages
223
+ def drop_session_ruby_temporary_tables
224
+ select_all("SELECT table_name FROM user_tables WHERE temporary='Y' AND table_name LIKE :table_name",
225
+ RUBY_TEMP_TABLE_PREFIX.upcase+"#{session_id}_%").each do |row|
226
+ exec "TRUNCATE TABLE #{row[0]}"
227
+ exec "DROP TABLE #{row[0]}"
228
+ end
229
+ end
230
+
231
+ end
232
+
233
+ end
@@ -0,0 +1,9 @@
1
+ module PLSQL #:nodoc:
2
+ module ArrayHelpers #:nodoc:
3
+
4
+ def self.to_hash(keys, values) #:nodoc:
5
+ (0...keys.size).inject({}) { |hash, i| hash[keys[i]] = values[i]; hash }
6
+ end
7
+
8
+ end
9
+ end
@@ -0,0 +1,542 @@
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 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
+ java.sql.DriverManager.registerDriver Java::oracle.jdbc.OracleDriver.new
27
+
28
+ # set tns_admin property from TNS_ADMIN environment variable
29
+ if !java.lang.System.get_property("oracle.net.tns_admin") && ENV["TNS_ADMIN"]
30
+ java.lang.System.set_property("oracle.net.tns_admin", ENV["TNS_ADMIN"])
31
+ end
32
+
33
+ rescue LoadError, NameError
34
+ # JDBC driver is unavailable.
35
+ raise LoadError, "ERROR: ruby-plsql could not load Oracle JDBC driver. Please install #{ojdbc_jar || "Oracle JDBC"} library."
36
+ end
37
+
38
+ module PLSQL
39
+ class JDBCConnection < Connection #:nodoc:
40
+
41
+ def self.create_raw(params)
42
+ url = if ENV['TNS_ADMIN'] && params[:database] && !params[:host] && !params[:url]
43
+ "jdbc:oracle:thin:@#{params[:database]}"
44
+ else
45
+ params[:url] || "jdbc:oracle:thin:@#{params[:host] || 'localhost'}:#{params[:port] || 1521}:#{params[:database]}"
46
+ end
47
+ new(java.sql.DriverManager.getConnection(url, params[:username], params[:password]))
48
+ end
49
+
50
+ def set_time_zone(time_zone=nil)
51
+ time_zone ||= ENV['TZ']
52
+ raw_connection.setSessionTimeZone(time_zone)
53
+ end
54
+
55
+ def logoff
56
+ super
57
+ raw_connection.close
58
+ true
59
+ rescue
60
+ false
61
+ end
62
+
63
+ def commit
64
+ raw_connection.commit
65
+ end
66
+
67
+ def rollback
68
+ raw_connection.rollback
69
+ end
70
+
71
+ def autocommit?
72
+ raw_connection.getAutoCommit
73
+ end
74
+
75
+ def autocommit=(value)
76
+ raw_connection.setAutoCommit(value)
77
+ end
78
+
79
+ def prefetch_rows=(value)
80
+ raw_connection.setDefaultRowPrefetch(value)
81
+ end
82
+
83
+ def exec(sql, *bindvars)
84
+ cs = prepare_call(sql, *bindvars)
85
+ cs.execute
86
+ true
87
+ ensure
88
+ cs.close rescue nil
89
+ end
90
+
91
+ class CallableStatement #:nodoc:
92
+
93
+ def initialize(conn, sql)
94
+ @sql = sql
95
+ @connection = conn
96
+ @params = sql.scan(/\:\w+/)
97
+ @out_types = {}
98
+ @out_index = {}
99
+ @statement = @connection.prepare_call(sql)
100
+ end
101
+
102
+ def bind_param(arg, value, metadata)
103
+ type, length = @connection.plsql_to_ruby_data_type(metadata)
104
+ ora_value = @connection.ruby_value_to_ora_value(value, type, metadata)
105
+ @connection.set_bind_variable(@statement, arg, ora_value, type, length, metadata)
106
+ if metadata[:in_out] =~ /OUT/
107
+ @out_types[arg] = type || ora_value.class
108
+ @out_index[arg] = bind_param_index(arg)
109
+ if ['TABLE','VARRAY','OBJECT'].include?(metadata[:data_type])
110
+ @statement.registerOutParameter(@out_index[arg], @connection.get_java_sql_type(ora_value,type),
111
+ metadata[:sql_type_name])
112
+ else
113
+ @statement.registerOutParameter(@out_index[arg],@connection.get_java_sql_type(ora_value,type))
114
+ end
115
+ end
116
+ end
117
+
118
+ def exec
119
+ @statement.execute
120
+ end
121
+
122
+ def [](key)
123
+ @connection.ora_value_to_ruby_value(@connection.get_bind_variable(@statement, @out_index[key], @out_types[key]))
124
+ end
125
+
126
+ def close
127
+ @statement.close
128
+ end
129
+
130
+ private
131
+
132
+ def bind_param_index(key)
133
+ return key if key.kind_of? Integer
134
+ key = ":#{key.to_s}" unless key.to_s =~ /^:/
135
+ @params.index(key)+1
136
+ end
137
+ end
138
+
139
+ class Cursor #:nodoc:
140
+ include Connection::CursorCommon
141
+
142
+ attr_reader :result_set
143
+ attr_accessor :statement
144
+
145
+ def initialize(conn, result_set)
146
+ @connection = conn
147
+ @result_set = result_set
148
+ @metadata = @result_set.getMetaData
149
+ @column_count = @metadata.getColumnCount
150
+ @column_type_names = [nil] # column numbering starts at 1
151
+ (1..@column_count).each do |i|
152
+ @column_type_names << {:type_name => @metadata.getColumnTypeName(i), :sql_type => @metadata.getColumnType(i)}
153
+ end
154
+ end
155
+
156
+ def self.new_from_query(conn, sql, bindvars=[], options={})
157
+ stmt = conn.prepare_statement(sql, *bindvars)
158
+ if prefetch_rows = options[:prefetch_rows]
159
+ stmt.setRowPrefetch(prefetch_rows)
160
+ end
161
+ cursor = Cursor.new(conn, stmt.executeQuery)
162
+ cursor.statement = stmt
163
+ cursor
164
+ rescue
165
+ # in case of any error close statement
166
+ stmt.close rescue nil
167
+ raise
168
+ end
169
+
170
+ def fetch
171
+ if @result_set.next
172
+ (1..@column_count).map do |i|
173
+ @connection.get_ruby_value_from_result_set(@result_set, i, @column_type_names[i])
174
+ end
175
+ else
176
+ nil
177
+ end
178
+ end
179
+
180
+ def fields
181
+ @fields ||= (1..@column_count).map do |i|
182
+ @metadata.getColumnName(i).downcase.to_sym
183
+ end
184
+ end
185
+
186
+ def close
187
+ @result_set.close
188
+ @statement.close if @statement
189
+ end
190
+ end
191
+
192
+ def parse(sql)
193
+ CallableStatement.new(self, sql)
194
+ end
195
+
196
+ def cursor_from_query(sql, bindvars=[], options={})
197
+ Cursor.new_from_query(self, sql, bindvars, options)
198
+ end
199
+
200
+ def prepare_statement(sql, *bindvars)
201
+ stmt = raw_connection.prepareStatement(sql)
202
+ bindvars.each_with_index do |bv, i|
203
+ set_bind_variable(stmt, i+1, ruby_value_to_ora_value(bv))
204
+ end
205
+ stmt
206
+ end
207
+
208
+ def prepare_call(sql, *bindvars)
209
+ stmt = raw_connection.prepareCall(sql)
210
+ bindvars.each_with_index do |bv, i|
211
+ set_bind_variable(stmt, i+1, bv)
212
+ end
213
+ stmt
214
+ end
215
+
216
+ RUBY_CLASS_TO_SQL_TYPE = {
217
+ Fixnum => java.sql.Types::INTEGER,
218
+ Bignum => java.sql.Types::INTEGER,
219
+ Integer => java.sql.Types::INTEGER,
220
+ Float => java.sql.Types::FLOAT,
221
+ BigDecimal => java.sql.Types::NUMERIC,
222
+ String => java.sql.Types::VARCHAR,
223
+ Java::OracleSql::CLOB => Java::oracle.jdbc.OracleTypes::CLOB,
224
+ Java::OracleSql::BLOB => Java::oracle.jdbc.OracleTypes::BLOB,
225
+ Date => java.sql.Types::DATE,
226
+ Time => java.sql.Types::TIMESTAMP,
227
+ DateTime => java.sql.Types::DATE,
228
+ Java::OracleSql::ARRAY => Java::oracle.jdbc.OracleTypes::ARRAY,
229
+ Array => Java::oracle.jdbc.OracleTypes::ARRAY,
230
+ Java::OracleSql::STRUCT => Java::oracle.jdbc.OracleTypes::STRUCT,
231
+ Hash => Java::oracle.jdbc.OracleTypes::STRUCT,
232
+ java.sql.ResultSet => Java::oracle.jdbc.OracleTypes::CURSOR,
233
+ }
234
+
235
+ SQL_TYPE_TO_RUBY_CLASS = {
236
+ java.sql.Types::CHAR => String,
237
+ java.sql.Types::NCHAR => String,
238
+ java.sql.Types::VARCHAR => String,
239
+ java.sql.Types::NVARCHAR => String,
240
+ java.sql.Types::LONGVARCHAR => String,
241
+ java.sql.Types::NUMERIC => BigDecimal,
242
+ java.sql.Types::INTEGER => Fixnum,
243
+ java.sql.Types::DATE => Time,
244
+ java.sql.Types::TIMESTAMP => Time,
245
+ Java::oracle.jdbc.OracleTypes::TIMESTAMPTZ => Time,
246
+ Java::oracle.jdbc.OracleTypes::TIMESTAMPLTZ => Time,
247
+ java.sql.Types::BLOB => String,
248
+ java.sql.Types::CLOB => String,
249
+ java.sql.Types::ARRAY => Java::OracleSql::ARRAY,
250
+ java.sql.Types::STRUCT => Java::OracleSql::STRUCT,
251
+ Java::oracle.jdbc.OracleTypes::CURSOR => java.sql.ResultSet
252
+ }
253
+
254
+ def get_java_sql_type(value, type)
255
+ RUBY_CLASS_TO_SQL_TYPE[type || value.class] || java.sql.Types::VARCHAR
256
+ end
257
+
258
+ def set_bind_variable(stmt, i, value, type=nil, length=nil, metadata={})
259
+ key = i.kind_of?(Integer) ? nil : i.to_s.gsub(':','')
260
+ type_symbol = (!value.nil? && type ? type : value.class).to_s.to_sym
261
+ case type_symbol
262
+ when :Fixnum, :Bignum, :Integer
263
+ stmt.send("setInt#{key && "AtName"}", key || i, value)
264
+ when :Float
265
+ stmt.send("setFloat#{key && "AtName"}", key || i, value)
266
+ when :BigDecimal, :'Java::JavaMath::BigDecimal'
267
+ stmt.send("setBigDecimal#{key && "AtName"}", key || i, value)
268
+ when :String
269
+ stmt.send("setString#{key && "AtName"}", key || i, value)
270
+ when :'Java::OracleSql::CLOB'
271
+ stmt.send("setClob#{key && "AtName"}", key || i, value)
272
+ when :'Java::OracleSql::BLOB'
273
+ stmt.send("setBlob#{key && "AtName"}", key || i, value)
274
+ when :Date, :DateTime, :'Java::OracleSql::DATE'
275
+ stmt.send("setDATE#{key && "AtName"}", key || i, value)
276
+ when :Time, :'Java::JavaSql::Timestamp'
277
+ stmt.send("setTimestamp#{key && "AtName"}", key || i, value)
278
+ when :NilClass
279
+ if ['TABLE', 'VARRAY', 'OBJECT'].include?(metadata[:data_type])
280
+ stmt.send("setNull#{key && "AtName"}", key || i, get_java_sql_type(value, type),
281
+ metadata[:sql_type_name])
282
+ elsif metadata[:data_type] == 'REF CURSOR'
283
+ # TODO: cannot bind NULL value to cursor parameter, getting error
284
+ # java.sql.SQLException: Unsupported feature: sqlType=-10
285
+ # Currently do nothing and assume that NULL values will not be passed to IN parameters
286
+ # If cursor is IN/OUT or OUT parameter then it should work
287
+ else
288
+ stmt.send("setNull#{key && "AtName"}", key || i, get_java_sql_type(value, type))
289
+ end
290
+ when :'Java::OracleSql::ARRAY'
291
+ stmt.send("setARRAY#{key && "AtName"}", key || i, value)
292
+ when :'Java::OracleSql::STRUCT'
293
+ stmt.send("setSTRUCT#{key && "AtName"}", key || i, value)
294
+ when :'Java::JavaSql::ResultSet'
295
+ # TODO: cannot find how to pass cursor parameter from JDBC
296
+ # setCursor is giving exception java.sql.SQLException: Unsupported feature
297
+ stmt.send("setCursor#{key && "AtName"}", key || i, value)
298
+ else
299
+ raise ArgumentError, "Don't know how to bind variable with type #{type_symbol}"
300
+ end
301
+ end
302
+
303
+ def get_bind_variable(stmt, i, type)
304
+ case type.to_s.to_sym
305
+ when :Fixnum, :Bignum, :Integer
306
+ stmt.getInt(i)
307
+ when :Float
308
+ stmt.getFloat(i)
309
+ when :BigDecimal
310
+ bd = stmt.getBigDecimal(i)
311
+ bd && BigDecimal.new(bd.to_s)
312
+ when :String
313
+ stmt.getString(i)
314
+ when :'Java::OracleSql::CLOB'
315
+ stmt.getClob(i)
316
+ when :'Java::OracleSql::BLOB'
317
+ stmt.getBlob(i)
318
+ when :Date, :DateTime
319
+ stmt.getDATE(i)
320
+ when :Time
321
+ stmt.getTimestamp(i)
322
+ when :'Java::OracleSql::ARRAY'
323
+ stmt.getArray(i)
324
+ when :'Java::OracleSql::STRUCT'
325
+ stmt.getSTRUCT(i)
326
+ when :'Java::JavaSql::ResultSet'
327
+ stmt.getCursor(i)
328
+ end
329
+ end
330
+
331
+ def get_ruby_value_from_result_set(rset, i, metadata)
332
+ ruby_type = SQL_TYPE_TO_RUBY_CLASS[metadata[:sql_type]]
333
+ ora_value = get_bind_variable(rset, i, ruby_type)
334
+ result_new = ora_value_to_ruby_value(ora_value)
335
+ end
336
+
337
+ def result_set_to_ruby_data_type(column_type, column_type_name)
338
+
339
+ end
340
+
341
+ def plsql_to_ruby_data_type(metadata)
342
+ data_type, data_length = metadata[:data_type], metadata[:data_length]
343
+ case data_type
344
+ when "VARCHAR2", "CHAR", "NVARCHAR2", "NCHAR"
345
+ [String, data_length || 32767]
346
+ when "CLOB", "NCLOB"
347
+ [Java::OracleSql::CLOB, nil]
348
+ when "BLOB"
349
+ [Java::OracleSql::BLOB, nil]
350
+ when "NUMBER"
351
+ [BigDecimal, nil]
352
+ when "PLS_INTEGER", "BINARY_INTEGER"
353
+ [Fixnum, nil]
354
+ when "DATE"
355
+ [DateTime, nil]
356
+ when "TIMESTAMP", "TIMESTAMP WITH TIME ZONE", "TIMESTAMP WITH LOCAL TIME ZONE"
357
+ [Time, nil]
358
+ when "TABLE", "VARRAY"
359
+ [Java::OracleSql::ARRAY, nil]
360
+ when "OBJECT"
361
+ [Java::OracleSql::STRUCT, nil]
362
+ when "REF CURSOR"
363
+ [java.sql.ResultSet, nil]
364
+ else
365
+ [String, 32767]
366
+ end
367
+ end
368
+
369
+ def ruby_value_to_ora_value(value, type=nil, metadata={})
370
+ type ||= value.class
371
+ case type.to_s.to_sym
372
+ when :Fixnum, :String
373
+ value
374
+ when :BigDecimal
375
+ case value
376
+ when TrueClass
377
+ java_bigdecimal(1)
378
+ when FalseClass
379
+ java_bigdecimal(0)
380
+ else
381
+ java_bigdecimal(value)
382
+ end
383
+ when :Date, :DateTime
384
+ case value
385
+ when DateTime
386
+ java_date(Time.send(plsql.default_timezone, value.year, value.month, value.day, value.hour, value.min, value.sec))
387
+ when Date
388
+ java_date(Time.send(plsql.default_timezone, value.year, value.month, value.day, 0, 0, 0))
389
+ else
390
+ java_date(value)
391
+ end
392
+ when :Time
393
+ java_timestamp(value)
394
+ when :'Java::OracleSql::CLOB'
395
+ if value
396
+ clob = Java::OracleSql::CLOB.createTemporary(raw_connection, false, Java::OracleSql::CLOB::DURATION_SESSION)
397
+ clob.setString(1, value)
398
+ clob
399
+ else
400
+ nil
401
+ end
402
+ when :'Java::OracleSql::BLOB'
403
+ if value
404
+ blob = Java::OracleSql::BLOB.createTemporary(raw_connection, false, Java::OracleSql::BLOB::DURATION_SESSION)
405
+ blob.setBytes(1, value.to_java_bytes)
406
+ blob
407
+ else
408
+ nil
409
+ end
410
+ when :'Java::OracleSql::ARRAY'
411
+ if value
412
+ raise ArgumentError, "You should pass Array value for collection type parameter" unless value.is_a?(Array)
413
+ descriptor = Java::OracleSql::ArrayDescriptor.createDescriptor(metadata[:sql_type_name], raw_connection)
414
+ elem_type = descriptor.getBaseType
415
+ elem_type_name = descriptor.getBaseName
416
+ elem_list = value.map do |elem|
417
+ case elem_type
418
+ when Java::oracle.jdbc.OracleTypes::ARRAY
419
+ ruby_value_to_ora_value(elem, Java::OracleSql::ARRAY, :sql_type_name => elem_type_name)
420
+ when Java::oracle.jdbc.OracleTypes::STRUCT
421
+ ruby_value_to_ora_value(elem, Java::OracleSql::STRUCT, :sql_type_name => elem_type_name)
422
+ else
423
+ ruby_value_to_ora_value(elem)
424
+ end
425
+ end
426
+ Java::OracleSql::ARRAY.new(descriptor, raw_connection, elem_list.to_java)
427
+ end
428
+ when :'Java::OracleSql::STRUCT'
429
+ if value
430
+ raise ArgumentError, "You should pass Hash value for object type parameter" unless value.is_a?(Hash)
431
+ descriptor = Java::OracleSql::StructDescriptor.createDescriptor(metadata[:sql_type_name], raw_connection)
432
+ struct_metadata = descriptor.getMetaData
433
+ struct_fields = (1..descriptor.getLength).inject({}) do |hash, i|
434
+ hash[struct_metadata.getColumnName(i).downcase.to_sym] =
435
+ {:type => struct_metadata.getColumnType(i), :type_name => struct_metadata.getColumnTypeName(i)}
436
+ hash
437
+ end
438
+ object_attrs = java.util.HashMap.new
439
+ value.each do |key, attr_value|
440
+ raise ArgumentError, "Wrong object type field passed to PL/SQL procedure" unless (field = struct_fields[key])
441
+ case field[:type]
442
+ when Java::oracle.jdbc.OracleTypes::ARRAY
443
+ # nested collection
444
+ object_attrs.put(key.to_s.upcase, ruby_value_to_ora_value(attr_value, Java::OracleSql::ARRAY, :sql_type_name => field[:type_name]))
445
+ when Java::oracle.jdbc.OracleTypes::STRUCT
446
+ # nested object type
447
+ object_attrs.put(key.to_s.upcase, ruby_value_to_ora_value(attr_value, Java::OracleSql::STRUCT, :sql_type_name => field[:type_name]))
448
+ else
449
+ object_attrs.put(key.to_s.upcase, ruby_value_to_ora_value(attr_value))
450
+ end
451
+ end
452
+ Java::OracleSql::STRUCT.new(descriptor, raw_connection, object_attrs)
453
+ end
454
+ when :'Java::JavaSql::ResultSet'
455
+ if value
456
+ value.result_set
457
+ end
458
+ else
459
+ value
460
+ end
461
+ end
462
+
463
+ def ora_value_to_ruby_value(value)
464
+ case value
465
+ when Float, BigDecimal
466
+ ora_number_to_ruby_number(value)
467
+ when Java::JavaMath::BigDecimal
468
+ value && ora_number_to_ruby_number(BigDecimal.new(value.to_s))
469
+ when Java::OracleSql::DATE
470
+ if value
471
+ d = value.dateValue
472
+ t = value.timeValue
473
+ Time.send(plsql.default_timezone, d.year + 1900, d.month + 1, d.date, t.hours, t.minutes, t.seconds)
474
+ end
475
+ when Java::JavaSql::Timestamp
476
+ if value
477
+ Time.send(plsql.default_timezone, value.year + 1900, value.month + 1, value.date, value.hours, value.minutes, value.seconds,
478
+ value.nanos / 1000)
479
+ end
480
+ when Java::OracleSql::CLOB
481
+ if value.isEmptyLob
482
+ nil
483
+ else
484
+ value.getSubString(1, value.length)
485
+ end
486
+ when Java::OracleSql::BLOB
487
+ if value.isEmptyLob
488
+ nil
489
+ else
490
+ String.from_java_bytes(value.getBytes(1, value.length))
491
+ end
492
+ when Java::OracleSql::ARRAY
493
+ value.getArray.map{|e| ora_value_to_ruby_value(e)}
494
+ when Java::OracleSql::STRUCT
495
+ descriptor = value.getDescriptor
496
+ struct_metadata = descriptor.getMetaData
497
+ field_names = (1..descriptor.getLength).map {|i| struct_metadata.getColumnName(i).downcase.to_sym}
498
+ field_values = value.getAttributes.map{|e| ora_value_to_ruby_value(e)}
499
+ ArrayHelpers::to_hash(field_names, field_values)
500
+ when Java::java.sql.ResultSet
501
+ Cursor.new(self, value)
502
+ else
503
+ value
504
+ end
505
+ end
506
+
507
+ def database_version
508
+ @database_version ||= if md = raw_connection.getMetaData
509
+ major = md.getDatabaseMajorVersion
510
+ minor = md.getDatabaseMinorVersion
511
+ if md.getDatabaseProductVersion =~ /#{major}\.#{minor}\.(\d+)\.(\d+)/
512
+ update = $1.to_i
513
+ patch = $2.to_i
514
+ else
515
+ update = patch = 0
516
+ end
517
+ [major, minor, update, patch]
518
+ end
519
+ end
520
+
521
+ private
522
+
523
+ def java_date(value)
524
+ value && Java::oracle.sql.DATE.new(value.strftime("%Y-%m-%d %H:%M:%S"))
525
+ end
526
+
527
+ def java_timestamp(value)
528
+ value && Java::java.sql.Timestamp.new(value.year-1900, value.month-1, value.day, value.hour, value.min, value.sec, value.usec * 1000)
529
+ end
530
+
531
+ def java_bigdecimal(value)
532
+ value && java.math.BigDecimal.new(value.to_s)
533
+ end
534
+
535
+ def ora_number_to_ruby_number(num)
536
+ # return BigDecimal instead of Float to avoid rounding errors
537
+ num == (num_to_i = num.to_i) ? num_to_i : (num.is_a?(BigDecimal) ? num : BigDecimal.new(num.to_s))
538
+ end
539
+
540
+ end
541
+
542
+ end