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.
- checksums.yaml +7 -0
- data/History.txt +282 -0
- data/License.txt +20 -0
- data/README.md +263 -0
- data/VERSION +1 -0
- data/lib/plsql/connection.rb +230 -0
- data/lib/plsql/helpers.rb +7 -0
- data/lib/plsql/jdbc_connection.rb +588 -0
- data/lib/plsql/oci_connection.rb +324 -0
- data/lib/plsql/package.rb +87 -0
- data/lib/plsql/procedure.rb +584 -0
- data/lib/plsql/procedure_call.rb +626 -0
- data/lib/plsql/schema.rb +296 -0
- data/lib/plsql/sequence.rb +46 -0
- data/lib/plsql/sql_statements.rb +85 -0
- data/lib/plsql/table.rb +345 -0
- data/lib/plsql/type.rb +270 -0
- data/lib/plsql/variable.rb +143 -0
- data/lib/plsql/version.rb +3 -0
- data/lib/plsql/view.rb +38 -0
- data/lib/ruby-plsql.rb +1 -0
- data/lib/ruby_plsql.rb +13 -0
- metadata +120 -0
|
@@ -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
|