flash-gordons-ruby-plsql 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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