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,324 @@
1
+ begin
2
+ require "oci8"
3
+ rescue LoadError
4
+ # OCI8 driver is unavailable.
5
+ msg = $!.to_s
6
+ if /-- oci8$/ =~ msg
7
+ # ruby-oci8 is not installed.
8
+ # MRI <= 1.9.2, Rubinius, JRuby:
9
+ # no such file to load -- oci8
10
+ # MRI >= 1.9.3:
11
+ # cannot load such file -- oci8
12
+ msg = "Please install ruby-oci8 gem."
13
+ end
14
+ raise LoadError, "ERROR: ruby-plsql could not load ruby-oci8 library. #{msg}"
15
+ end
16
+
17
+ # check ruby-oci8 version
18
+ if Gem::Version.new(OCI8::VERSION) < Gem::Version.new("2.1.0")
19
+ raise LoadError, "ERROR: ruby-oci8 version #{OCI8::VERSION} is too old. Please install ruby-oci8 version 2.1.0 or later."
20
+ end
21
+
22
+ module PLSQL
23
+ class OCIConnection < Connection # :nodoc:
24
+ def self.create_raw(params)
25
+ connection_string = if params[:host]
26
+ "//#{params[:host]}:#{params[:port] || 1521}/#{params[:database]}"
27
+ else
28
+ params[:database]
29
+ end
30
+ new(OCI8.new(params[:username], params[:password], connection_string))
31
+ end
32
+
33
+ def logoff
34
+ super
35
+ raw_connection.logoff
36
+ end
37
+
38
+ def commit
39
+ raw_connection.commit
40
+ end
41
+
42
+ def rollback
43
+ raw_connection.rollback
44
+ end
45
+
46
+ def autocommit?
47
+ raw_connection.autocommit?
48
+ end
49
+
50
+ def autocommit=(value)
51
+ raw_connection.autocommit = value
52
+ end
53
+
54
+ def prefetch_rows=(value)
55
+ raw_connection.prefetch_rows = value
56
+ end
57
+
58
+ def exec(sql, *bindvars)
59
+ raw_connection.exec(sql, *bindvars)
60
+ true
61
+ end
62
+
63
+ class Cursor # :nodoc:
64
+ include Connection::CursorCommon
65
+
66
+ attr_reader :raw_cursor
67
+
68
+ # stack of open cursors per thread
69
+ def self.open_cursors
70
+ Thread.current[:plsql_oci_cursor_stack] ||= []
71
+ end
72
+
73
+ def initialize(conn, raw_cursor)
74
+ @connection = conn
75
+ @raw_cursor = raw_cursor
76
+ self.class.open_cursors.push self
77
+ end
78
+
79
+ def self.new_from_parse(conn, sql)
80
+ raw_cursor = conn.raw_connection.parse(sql)
81
+ self.new(conn, raw_cursor)
82
+ end
83
+
84
+ def self.new_from_query(conn, sql, bindvars = [], options = {})
85
+ cursor = new_from_parse(conn, sql)
86
+ if prefetch_rows = options[:prefetch_rows]
87
+ cursor.prefetch_rows = prefetch_rows
88
+ end
89
+ cursor.exec(*bindvars)
90
+ cursor
91
+ end
92
+
93
+ def prefetch_rows=(value)
94
+ @raw_cursor.prefetch_rows = value
95
+ end
96
+
97
+ def bind_param(arg, value, metadata)
98
+ type, length = @connection.plsql_to_ruby_data_type(metadata)
99
+ ora_value = @connection.ruby_value_to_ora_value(value, type)
100
+ @raw_cursor.bind_param(arg, ora_value, type, length)
101
+ end
102
+
103
+ def exec(*bindvars)
104
+ @raw_cursor.exec(*bindvars)
105
+ end
106
+
107
+ def [](key)
108
+ @connection.ora_value_to_ruby_value(@raw_cursor[key])
109
+ end
110
+
111
+ def fetch
112
+ row = @raw_cursor.fetch
113
+ row && row.map { |v| @connection.ora_value_to_ruby_value(v) }
114
+ end
115
+
116
+ def fields
117
+ @fields ||= @raw_cursor.get_col_names.map { |c| c.downcase.to_sym }
118
+ end
119
+
120
+ def close_raw_cursor
121
+ @raw_cursor.close
122
+ end
123
+
124
+ def close
125
+ # close all cursors that were created after this one
126
+ while (open_cursor = self.class.open_cursors.pop) && !open_cursor.equal?(self)
127
+ open_cursor.close_raw_cursor
128
+ end
129
+ close_raw_cursor
130
+ end
131
+ end
132
+
133
+ def parse(sql)
134
+ Cursor.new_from_parse(self, sql)
135
+ end
136
+
137
+ def cursor_from_query(sql, bindvars = [], options = {})
138
+ Cursor.new_from_query(self, sql, bindvars, options)
139
+ end
140
+
141
+ def plsql_to_ruby_data_type(metadata)
142
+ data_type, data_length = metadata[:data_type], metadata[:data_length]
143
+ case data_type
144
+ when "VARCHAR", "VARCHAR2", "CHAR", "NVARCHAR2", "NCHAR"
145
+ [String, data_length || 32767]
146
+ when "CLOB", "NCLOB"
147
+ [OCI8::CLOB, nil]
148
+ when "BLOB"
149
+ [OCI8::BLOB, nil]
150
+ when "NUMBER", "NATURAL", "NATURALN", "POSITIVE", "POSITIVEN", "SIGNTYPE", "SIMPLE_INTEGER", "PLS_INTEGER", "BINARY_INTEGER"
151
+ [OraNumber, nil]
152
+ when "DATE"
153
+ [DateTime, nil]
154
+ when "TIMESTAMP", "TIMESTAMP WITH TIME ZONE", "TIMESTAMP WITH LOCAL TIME ZONE"
155
+ [Time, nil]
156
+ when "TABLE", "VARRAY", "OBJECT", "XMLTYPE", "OPAQUE/XMLTYPE"
157
+ # create Ruby class for collection
158
+ klass = OCI8::Object::Base.get_class_by_typename(metadata[:sql_type_name])
159
+ unless klass
160
+ klass = Class.new(OCI8::Object::Base)
161
+ klass.set_typename metadata[:sql_type_name]
162
+ end
163
+ [klass, nil]
164
+ when "REF CURSOR"
165
+ [OCI8::Cursor]
166
+ else
167
+ [String, 32767]
168
+ end
169
+ end
170
+
171
+ def ruby_value_to_ora_value(value, type = nil)
172
+ type ||= value.class
173
+ case type.to_s.to_sym
174
+ when :Integer, :BigDecimal, :String
175
+ value
176
+ when :OraNumber
177
+ # pass parameters as OraNumber to avoid rounding errors
178
+ case value
179
+ when BigDecimal
180
+ OraNumber.new(value.to_s("F"))
181
+ when TrueClass
182
+ OraNumber.new(1)
183
+ when FalseClass
184
+ OraNumber.new(0)
185
+ else
186
+ value
187
+ end
188
+ when :DateTime
189
+ case value
190
+ when Time
191
+ ::DateTime.civil(value.year, value.month, value.day, value.hour, value.min, value.sec, Rational(value.utc_offset, 86400))
192
+ when DateTime
193
+ value
194
+ when Date
195
+ ::DateTime.civil(value.year, value.month, value.day, 0, 0, 0, 0)
196
+ else
197
+ value
198
+ end
199
+ when :"OCI8::CLOB", :"OCI8::BLOB"
200
+ # ruby-oci8 cannot create CLOB/BLOB from ''
201
+ value.to_s.length > 0 ? type.new(raw_oci_connection, value) : nil
202
+ when :"OCI8::Cursor"
203
+ value && value.raw_cursor
204
+ else
205
+ # collections and object types
206
+ if type.superclass == OCI8::Object::Base
207
+ return nil if value.nil?
208
+ tdo = raw_oci_connection.get_tdo_by_class(type)
209
+ if tdo.is_collection?
210
+ raise ArgumentError, "You should pass Array value for collection type parameter" unless value.is_a?(Array)
211
+ elem_list = value.map do |elem|
212
+ if (attr_tdo = tdo.coll_attr.typeinfo)
213
+ attr_type, _ = plsql_to_ruby_data_type(data_type: "OBJECT", sql_type_name: attr_tdo.typename)
214
+ else
215
+ attr_type = elem.class
216
+ end
217
+ ruby_value_to_ora_value(elem, attr_type)
218
+ end
219
+ # construct collection value
220
+ # TODO: change setting instance variable to appropriate ruby-oci8 method call when available
221
+ collection = type.new(raw_oci_connection)
222
+ collection.instance_variable_set("@attributes", elem_list)
223
+ collection
224
+ else # object type
225
+ raise ArgumentError, "You should pass Hash value for object type parameter" unless value.is_a?(Hash)
226
+ object_attrs = value.dup
227
+ object_attrs.keys.each do |key|
228
+ raise ArgumentError, "Wrong object type field passed to PL/SQL procedure" unless (attr = tdo.attr_getters[key])
229
+ case attr.datatype
230
+ when OCI8::TDO::ATTR_NAMED_TYPE, OCI8::TDO::ATTR_NAMED_COLLECTION
231
+ # nested object type or collection
232
+ attr_type, _ = plsql_to_ruby_data_type(data_type: "OBJECT", sql_type_name: attr.typeinfo.typename)
233
+ object_attrs[key] = ruby_value_to_ora_value(object_attrs[key], attr_type)
234
+ end
235
+ end
236
+ type.new(raw_oci_connection, object_attrs)
237
+ end
238
+ # all other cases
239
+ else
240
+ value
241
+ end
242
+ end
243
+ end
244
+
245
+ def ora_value_to_ruby_value(value)
246
+ case value
247
+ when Float, OraNumber, BigDecimal
248
+ ora_number_to_ruby_number(value)
249
+ when DateTime, OraDate
250
+ ora_date_to_ruby_date(value)
251
+ when OCI8::LOB
252
+ if value.available?
253
+ value.rewind
254
+ value.read
255
+ else
256
+ nil
257
+ end
258
+ when OCI8::Object::Base
259
+ tdo = raw_oci_connection.get_tdo_by_class(value.class)
260
+ if tdo.is_collection?
261
+ value.to_ary.map { |e| ora_value_to_ruby_value(e) }
262
+ else # object type
263
+ tdo.attributes.inject({}) do |hash, attr|
264
+ hash[attr.name] = ora_value_to_ruby_value(value.instance_variable_get(:@attributes)[attr.name])
265
+ hash
266
+ end
267
+ end
268
+ when OCI8::Cursor
269
+ Cursor.new(self, value)
270
+ else
271
+ value
272
+ end
273
+ end
274
+
275
+ def database_version
276
+ @database_version ||= (version = raw_connection.oracle_server_version) &&
277
+ [version.major, version.minor, version.update, version.patch]
278
+ end
279
+
280
+ private
281
+
282
+ def raw_oci_connection
283
+ if raw_connection.is_a? OCI8
284
+ raw_connection
285
+ # ActiveRecord Oracle enhanced adapter puts OCI8EnhancedAutoRecover wrapper around OCI8
286
+ # in this case we need to pass original OCI8 connection
287
+ else
288
+ if raw_connection.instance_variable_defined?(:@raw_connection)
289
+ raw_connection.instance_variable_get(:@raw_connection)
290
+ else
291
+ raw_connection.instance_variable_get(:@connection)
292
+ end
293
+ end
294
+ end
295
+
296
+ def ora_number_to_ruby_number(num)
297
+ # return BigDecimal instead of Float to avoid rounding errors
298
+ num == (num_to_i = num.to_i) ? num_to_i : (num.is_a?(BigDecimal) ? num : BigDecimal(num.to_s))
299
+ end
300
+
301
+ def ora_date_to_ruby_date(val)
302
+ case val
303
+ when DateTime
304
+ # similar implementation as in oracle_enhanced adapter
305
+ begin
306
+ Time.send(plsql.default_timezone, val.year, val.month, val.day, val.hour, val.min, val.sec)
307
+ rescue
308
+ offset = plsql.default_timezone.to_sym == :local ? plsql.local_timezone_offset : 0
309
+ DateTime.civil(val.year, val.month, val.day, val.hour, val.min, val.sec, offset)
310
+ end
311
+ when OraDate
312
+ # similar implementation as in oracle_enhanced adapter
313
+ begin
314
+ Time.send(plsql.default_timezone, val.year, val.month, val.day, val.hour, val.minute, val.second)
315
+ rescue
316
+ offset = plsql.default_timezone.to_sym == :local ? plsql.local_timezone_offset : 0
317
+ DateTime.civil(val.year, val.month, val.day, val.hour, val.minute, val.second, offset)
318
+ end
319
+ else
320
+ val
321
+ end
322
+ end
323
+ end
324
+ end
@@ -0,0 +1,87 @@
1
+ module PLSQL
2
+ module PackageClassMethods # :nodoc:
3
+ def find(schema, package)
4
+ package_name = package.to_s.upcase
5
+ find_in_schema(schema, package_name) || find_by_synonym(schema, package_name)
6
+ end
7
+
8
+ def find_in_schema(schema, package_name)
9
+ row = schema.select_first(<<-SQL, schema.schema_name, package_name)
10
+ SELECT object_name
11
+ FROM all_objects
12
+ WHERE owner = :owner
13
+ AND object_name = :package
14
+ AND object_type = 'PACKAGE'
15
+ SQL
16
+ new(schema, package_name) if row
17
+ end
18
+
19
+ def find_by_synonym(schema, package_name)
20
+ row = schema.select_first(<<-SQL, schema.schema_name, package_name)
21
+ SELECT o.object_name, o.owner
22
+ FROM all_synonyms s,
23
+ all_objects o
24
+ WHERE s.owner IN (:owner, 'PUBLIC')
25
+ AND s.synonym_name = :synonym_name
26
+ AND o.owner = s.table_owner
27
+ AND o.object_name = s.table_name
28
+ AND o.object_type = 'PACKAGE'
29
+ ORDER BY DECODE(s.owner, 'PUBLIC', 1, 0)
30
+ SQL
31
+ new(schema, row[0], row[1]) if row
32
+ end
33
+ end
34
+
35
+ class Package # :nodoc:
36
+ extend PackageClassMethods
37
+
38
+ def initialize(schema, package, override_schema_name = nil)
39
+ @schema = schema
40
+ @override_schema_name = override_schema_name
41
+ @package = package.to_s.upcase
42
+ @package_objects = {}
43
+ end
44
+
45
+ def procedure_defined?(name)
46
+ PLSQL::Procedure.find(@schema, name, @package) ? true : false
47
+ end
48
+
49
+ def [](object_name)
50
+ object_name = object_name.to_s.downcase
51
+ @package_objects[object_name] ||= [Procedure, Variable].inject(nil) do |res, object_type|
52
+ res || object_type.find(@schema, object_name, @package, @override_schema_name)
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ def method_missing(method, *args, &block)
59
+ method = +method.to_s
60
+ method.chop! if (assignment = method[/=$/])
61
+
62
+ case (object = self[method])
63
+ when Procedure
64
+ if assignment
65
+ raise ArgumentError, "Cannot assign value to package procedure '#{method.upcase}'"
66
+ end
67
+ object.exec(*args, &block)
68
+ when Variable
69
+ if assignment
70
+ unless args.size == 1 && block.nil?
71
+ raise ArgumentError, "Just one value can be assigned " \
72
+ "to package variable '#{method.upcase}'"
73
+ end
74
+ object.value = args[0]
75
+ else
76
+ unless args.size == 0 && block.nil?
77
+ raise ArgumentError, "Cannot pass arguments when getting " \
78
+ "package variable '#{method.upcase}' value"
79
+ end
80
+ object.value
81
+ end
82
+ else
83
+ raise ArgumentError, "No PL/SQL procedure or variable '#{method.upcase}' found"
84
+ end
85
+ end
86
+ end
87
+ end