ruby-plsql 0.4.1 → 0.4.2
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/History.txt +18 -0
- data/Rakefile +2 -0
- data/VERSION +1 -1
- data/lib/plsql/connection.rb +60 -10
- data/lib/plsql/jdbc_connection.rb +14 -2
- data/lib/plsql/oci_connection.rb +11 -1
- data/lib/plsql/procedure.rb +120 -25
- data/lib/plsql/procedure_call.rb +349 -165
- data/lib/plsql/schema.rb +59 -8
- data/lib/plsql/sql_statements.rb +21 -0
- data/lib/plsql/table.rb +10 -4
- data/lib/plsql/type.rb +194 -6
- data/lib/plsql/variable.rb +3 -3
- data/spec/plsql/connection_spec.rb +76 -1
- data/spec/plsql/procedure_spec.rb +336 -12
- data/spec/plsql/schema_spec.rb +80 -9
- data/spec/plsql/table_spec.rb +48 -28
- data/spec/plsql/type_spec.rb +173 -2
- data/spec/plsql/variable_spec.rb +21 -0
- data/spec/plsql/view_spec.rb +16 -11
- data/spec/spec_helper.rb +19 -5
- metadata +22 -2
data/lib/plsql/schema.rb
CHANGED
@@ -25,6 +25,10 @@ module PLSQL
|
|
25
25
|
# Returns connection wrapper object (this is not raw OCI8 or JDBC connection!)
|
26
26
|
attr_reader :connection
|
27
27
|
|
28
|
+
def root_schema #:nodoc:
|
29
|
+
@original_schema || self
|
30
|
+
end
|
31
|
+
|
28
32
|
def raw_connection=(raw_conn) #:nodoc:
|
29
33
|
@connection = raw_conn ? Connection.create(raw_conn) : nil
|
30
34
|
reset_instance_variables
|
@@ -32,16 +36,16 @@ module PLSQL
|
|
32
36
|
|
33
37
|
# Set connection to OCI8 or JDBC connection:
|
34
38
|
#
|
35
|
-
# plsql.connection = OCI8.new(database_user, database_password,
|
39
|
+
# plsql.connection = OCI8.new(database_user, database_password, database_name)
|
36
40
|
#
|
37
41
|
# or
|
38
42
|
#
|
39
43
|
# plsql.connection = java.sql.DriverManager.getConnection(
|
40
|
-
# "jdbc:oracle:thin:@#{
|
44
|
+
# "jdbc:oracle:thin:@#{database_host}:#{database_port}:#{database_name}",
|
41
45
|
# database_user, database_password)
|
42
46
|
#
|
43
47
|
def connection=(conn)
|
44
|
-
if conn.is_a?(
|
48
|
+
if conn.is_a?(Connection)
|
45
49
|
@connection = conn
|
46
50
|
reset_instance_variables
|
47
51
|
else
|
@@ -50,6 +54,23 @@ module PLSQL
|
|
50
54
|
conn
|
51
55
|
end
|
52
56
|
|
57
|
+
# Create new OCI8 or JDBC connection using one of the following ways:
|
58
|
+
#
|
59
|
+
# plsql.connect! username, password, database_tns_alias
|
60
|
+
# plsql.connect! username, password, :host => host, :port => port, :database => database
|
61
|
+
# plsql.connect! :username => username, :password => password, :database => database_tns_alias
|
62
|
+
# plsql.connect! :username => username, :password => password, :host => host, :port => port, :database => database
|
63
|
+
#
|
64
|
+
def connect!(*args)
|
65
|
+
params = {}
|
66
|
+
params[:username] = args.shift if args[0].is_a?(String)
|
67
|
+
params[:password] = args.shift if args[0].is_a?(String)
|
68
|
+
params[:database] = args.shift if args[0].is_a?(String)
|
69
|
+
params.merge!(args.shift) if args[0].is_a?(Hash)
|
70
|
+
raise ArgumentError, "Wrong number of arguments" unless args.empty?
|
71
|
+
self.connection = Connection.create_new(params)
|
72
|
+
end
|
73
|
+
|
53
74
|
# Set connection to current ActiveRecord connection (use in initializer file):
|
54
75
|
#
|
55
76
|
# plsql.activerecord_class = ActiveRecord::Base
|
@@ -156,12 +177,14 @@ module PLSQL
|
|
156
177
|
raise ArgumentError, "No database connection" unless connection
|
157
178
|
# search in database if not in cache at first
|
158
179
|
object = (@schema_objects[method] ||= find_database_object(method) || find_other_schema(method) ||
|
159
|
-
find_public_synonym(method)
|
180
|
+
find_public_synonym(method) || find_standard_procedure(method))
|
160
181
|
|
161
182
|
raise ArgumentError, "No database object '#{method.to_s.upcase}' found" unless object
|
162
183
|
|
163
184
|
if object.is_a?(Procedure)
|
164
185
|
object.exec(*args, &block)
|
186
|
+
elsif object.is_a?(Type) && !args.empty?
|
187
|
+
object.new(*args, &block)
|
165
188
|
else
|
166
189
|
object
|
167
190
|
end
|
@@ -171,14 +194,24 @@ module PLSQL
|
|
171
194
|
object_schema_name = override_schema_name || schema_name
|
172
195
|
object_name = name.to_s.upcase
|
173
196
|
if row = select_first(
|
174
|
-
"SELECT object_type, object_id
|
197
|
+
"SELECT o.object_type, o.object_id, o.status,
|
198
|
+
(CASE WHEN o.object_type = 'PACKAGE'
|
199
|
+
THEN (SELECT ob.status FROM all_objects ob
|
200
|
+
WHERE ob.owner = o.owner AND ob.object_name = o.object_name AND ob.object_type = 'PACKAGE BODY')
|
201
|
+
ELSE NULL END) body_status
|
202
|
+
FROM all_objects o
|
175
203
|
WHERE owner = :owner AND object_name = :object_name
|
176
204
|
AND object_type IN ('PROCEDURE','FUNCTION','PACKAGE','TABLE','VIEW','SEQUENCE','TYPE','SYNONYM')",
|
177
205
|
object_schema_name, object_name)
|
178
|
-
|
206
|
+
object_type, object_id, status, body_status = row
|
207
|
+
raise ArgumentError, "Database object '#{object_schema_name}.#{object_name}' is not in valid status\n" <<
|
208
|
+
_errors(object_schema_name, object_name, object_type) if status == 'INVALID'
|
209
|
+
raise ArgumentError, "Package '#{object_schema_name}.#{object_name}' body is not in valid status\n" <<
|
210
|
+
_errors(object_schema_name, object_name, 'PACKAGE BODY') if body_status == 'INVALID'
|
211
|
+
case object_type
|
179
212
|
when 'PROCEDURE', 'FUNCTION'
|
180
|
-
Procedure.new(self, name, nil, override_schema_name,
|
181
|
-
when 'PACKAGE'
|
213
|
+
Procedure.new(self, name, nil, override_schema_name, object_id)
|
214
|
+
when 'PACKAGE'
|
182
215
|
Package.new(self, name, override_schema_name)
|
183
216
|
when 'TABLE'
|
184
217
|
Table.new(self, name, override_schema_name)
|
@@ -195,6 +228,24 @@ module PLSQL
|
|
195
228
|
end
|
196
229
|
end
|
197
230
|
|
231
|
+
def _errors(object_schema_name, object_name, object_type)
|
232
|
+
result = ""
|
233
|
+
previous_line = 0
|
234
|
+
select_all(
|
235
|
+
"SELECT e.line, e.position, e.text error_text, s.text source_text
|
236
|
+
FROM all_errors e, all_source s
|
237
|
+
WHERE e.owner = :owner AND e.name = :name AND e.type = :type
|
238
|
+
AND s.owner = e.owner AND s.name = e.name AND s.type = e.type AND s.line = e.line
|
239
|
+
ORDER BY e.sequence",
|
240
|
+
object_schema_name, object_name, object_type
|
241
|
+
).each do |line, position, error_text, source_text|
|
242
|
+
result << "Error on line #{'%4d' % line}: #{source_text}" if line > previous_line
|
243
|
+
result << " position #{'%4d' % position}: #{error_text}\n"
|
244
|
+
previous_line = line
|
245
|
+
end
|
246
|
+
result unless result.empty?
|
247
|
+
end
|
248
|
+
|
198
249
|
def find_other_schema(name)
|
199
250
|
return nil if @original_schema
|
200
251
|
if select_first("SELECT username FROM all_users WHERE username = :username", name.to_s.upcase)
|
data/lib/plsql/sql_statements.rb
CHANGED
@@ -61,6 +61,27 @@ module PLSQL
|
|
61
61
|
@connection.rollback
|
62
62
|
end
|
63
63
|
|
64
|
+
# Create SAVEPOINT with specified name. Later use +rollback_to+ method to roll changes back
|
65
|
+
# to specified savepoint.
|
66
|
+
# Use beforehand
|
67
|
+
#
|
68
|
+
# plsql.connection.autocommit = false
|
69
|
+
#
|
70
|
+
# to turn off automatic commits after each statement.
|
71
|
+
def savepoint(name)
|
72
|
+
execute "SAVEPOINT #{name}"
|
73
|
+
end
|
74
|
+
|
75
|
+
# Roll back changes to specified savepoint (that was created using +savepoint+ method)
|
76
|
+
# Use beforehand
|
77
|
+
#
|
78
|
+
# plsql.connection.autocommit = false
|
79
|
+
#
|
80
|
+
# to turn off automatic commits after each statement.
|
81
|
+
def rollback_to(name)
|
82
|
+
execute "ROLLBACK TO #{name}"
|
83
|
+
end
|
84
|
+
|
64
85
|
end
|
65
86
|
end
|
66
87
|
|
data/lib/plsql/table.rb
CHANGED
@@ -49,7 +49,8 @@ module PLSQL
|
|
49
49
|
CASE WHEN c.data_type_owner IS NULL THEN NULL
|
50
50
|
ELSE (SELECT t.typecode FROM all_types t
|
51
51
|
WHERE t.owner = c.data_type_owner
|
52
|
-
AND t.type_name = c.data_type) END typecode
|
52
|
+
AND t.type_name = c.data_type) END typecode,
|
53
|
+
c.nullable, c.data_default
|
53
54
|
FROM all_tab_columns c
|
54
55
|
WHERE c.owner = :owner
|
55
56
|
AND c.table_name = :table_name",
|
@@ -57,7 +58,10 @@ module PLSQL
|
|
57
58
|
) do |r|
|
58
59
|
column_name, position,
|
59
60
|
data_type, data_length, data_precision, data_scale, char_used,
|
60
|
-
data_type_owner, data_type_mod, typecode = r
|
61
|
+
data_type_owner, data_type_mod, typecode, nullable, data_default = r
|
62
|
+
# remove scale (n) from data_type (returned for TIMESTAMPs and INTERVALs)
|
63
|
+
data_type.sub!(/\(\d+\)/,'')
|
64
|
+
# store column metadata
|
61
65
|
@columns[column_name.downcase.to_sym] = {
|
62
66
|
:position => position && position.to_i,
|
63
67
|
:data_type => data_type_owner && (typecode == 'COLLECTION' ? 'TABLE' : 'OBJECT' ) || data_type,
|
@@ -67,7 +71,9 @@ module PLSQL
|
|
67
71
|
:char_used => char_used,
|
68
72
|
:type_owner => data_type_owner,
|
69
73
|
:type_name => data_type_owner && data_type,
|
70
|
-
:sql_type_name => data_type_owner && "#{data_type_owner}.#{data_type}"
|
74
|
+
:sql_type_name => data_type_owner && "#{data_type_owner}.#{data_type}",
|
75
|
+
:nullable => nullable == 'Y', # store as true or false
|
76
|
+
:data_default => data_default && data_default.strip # remove leading and trailing whitespace
|
71
77
|
}
|
72
78
|
end
|
73
79
|
end
|
@@ -98,7 +104,7 @@ module PLSQL
|
|
98
104
|
order_by_sql = nil
|
99
105
|
sql_params.each do |k,v|
|
100
106
|
if k == :order_by
|
101
|
-
order_by_sql = "ORDER BY #{v} "
|
107
|
+
order_by_sql = " ORDER BY #{v} "
|
102
108
|
elsif v.nil?
|
103
109
|
where_sqls << "#{k} IS NULL"
|
104
110
|
else
|
data/lib/plsql/type.rb
CHANGED
@@ -34,19 +34,23 @@ module PLSQL
|
|
34
34
|
class Type
|
35
35
|
extend TypeClassMethods
|
36
36
|
|
37
|
-
attr_reader :typecode, :attributes, :schema_name, :type_name #:nodoc:
|
37
|
+
attr_reader :typecode, :attributes, :schema_name, :type_name, :type_object_id #:nodoc:
|
38
38
|
|
39
39
|
def initialize(schema, type, override_schema_name = nil) #:nodoc:
|
40
40
|
@schema = schema
|
41
41
|
@schema_name = override_schema_name || schema.schema_name
|
42
42
|
@type_name = type.to_s.upcase
|
43
43
|
@attributes = {}
|
44
|
+
@type_procedures = {}
|
44
45
|
|
45
|
-
@typecode = @schema.select_first(
|
46
|
-
"SELECT typecode FROM all_types
|
47
|
-
WHERE owner = :owner
|
48
|
-
AND type_name = :type_name
|
49
|
-
|
46
|
+
@typecode, @type_object_id = @schema.select_first(
|
47
|
+
"SELECT t.typecode, o.object_id FROM all_types t, all_objects o
|
48
|
+
WHERE t.owner = :owner
|
49
|
+
AND t.type_name = :type_name
|
50
|
+
AND o.owner = t.owner
|
51
|
+
AND o.object_name = t.type_name
|
52
|
+
AND o.object_type = 'TYPE'",
|
53
|
+
@schema_name, @type_name)
|
50
54
|
|
51
55
|
@schema.select_all(
|
52
56
|
"SELECT attr_name, attr_no,
|
@@ -77,11 +81,195 @@ module PLSQL
|
|
77
81
|
end
|
78
82
|
end
|
79
83
|
|
84
|
+
# is type collection?
|
85
|
+
def collection?
|
86
|
+
@is_collection ||= @typecode == 'COLLECTION'
|
87
|
+
end
|
88
|
+
|
80
89
|
# list of object type attribute names
|
81
90
|
def attribute_names
|
82
91
|
@attribute_names ||= @attributes.keys.sort_by{|k| @attributes[k][:position]}
|
83
92
|
end
|
84
93
|
|
94
|
+
# create new PL/SQL object instance
|
95
|
+
def new(*args, &block)
|
96
|
+
procedure = find_procedure(:new)
|
97
|
+
# in case of collections pass array of elements as one argument for constructor
|
98
|
+
if collection? && !(args.size == 1 && args[0].is_a?(Array))
|
99
|
+
args = [args]
|
100
|
+
end
|
101
|
+
result = procedure.exec_with_options(args, {:skip_self => true}, &block)
|
102
|
+
# TODO: collection constructor should return Array of ObhjectInstance objects
|
103
|
+
if collection?
|
104
|
+
result
|
105
|
+
else
|
106
|
+
# TODO: what to do if block is passed to constructor?
|
107
|
+
ObjectInstance.create(self, result)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def method_missing(method, *args, &block) #:nodoc:
|
112
|
+
if procedure = find_procedure(method)
|
113
|
+
procedure.exec_with_options(args, {}, &block)
|
114
|
+
else
|
115
|
+
raise ArgumentError, "No PL/SQL procedure '#{method.to_s.upcase}' found for type '#{@type_name}'"
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def find_procedure(new_or_procedure) #:nodoc:
|
120
|
+
@type_procedures[new_or_procedure] ||= begin
|
121
|
+
procedure_name = new_or_procedure == :new ? @type_name : new_or_procedure
|
122
|
+
# find defined procedure for type
|
123
|
+
if @schema.select_first(
|
124
|
+
"SELECT procedure_name FROM all_procedures
|
125
|
+
WHERE owner = :owner
|
126
|
+
AND object_name = :object_name
|
127
|
+
AND procedure_name = :procedure_name",
|
128
|
+
@schema_name, @type_name, procedure_name.to_s.upcase)
|
129
|
+
TypeProcedure.new(@schema, self, procedure_name)
|
130
|
+
# call default constructor
|
131
|
+
elsif new_or_procedure == :new
|
132
|
+
TypeProcedure.new(@schema, self, :new)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# wrapper class to simulate Procedure class for ProcedureClass#exec
|
138
|
+
class TypeProcedure #:nodoc:
|
139
|
+
include ProcedureCommon
|
140
|
+
|
141
|
+
def initialize(schema, type, procedure)
|
142
|
+
@schema = schema
|
143
|
+
@type = type
|
144
|
+
@schema_name = @type.schema_name
|
145
|
+
@type_name = @type.type_name
|
146
|
+
@object_id = @type.type_object_id
|
147
|
+
|
148
|
+
# if default constructor
|
149
|
+
if @default_constructor = (procedure == :new)
|
150
|
+
@procedure = @type.collection? ? nil : @type_name
|
151
|
+
set_default_constructor_arguments
|
152
|
+
# if defined type procedure
|
153
|
+
else
|
154
|
+
@procedure = procedure.to_s.upcase
|
155
|
+
get_argument_metadata
|
156
|
+
# add also definition for default constructor in case of custom constructor
|
157
|
+
set_default_constructor_arguments if @procedure == @type_name
|
158
|
+
end
|
159
|
+
|
160
|
+
# constructors do not need type prefix in call
|
161
|
+
@package = @procedure == @type_name ? nil : @type_name
|
162
|
+
end
|
163
|
+
|
164
|
+
# will be called for collection constructor
|
165
|
+
def call_sql(params_string)
|
166
|
+
"#{params_string};\n"
|
167
|
+
end
|
168
|
+
|
169
|
+
attr_reader :arguments, :argument_list, :out_list
|
170
|
+
def arguments_without_self
|
171
|
+
@arguments_without_self ||= begin
|
172
|
+
hash = {}
|
173
|
+
@arguments.each do |ov, args|
|
174
|
+
hash[ov] = args.reject{|key, value| key == :self}
|
175
|
+
end
|
176
|
+
hash
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def argument_list_without_self
|
181
|
+
@argument_list_without_self ||= begin
|
182
|
+
hash = {}
|
183
|
+
@argument_list.each do |ov, arg_list|
|
184
|
+
hash[ov] = arg_list.select{|arg| arg != :self}
|
185
|
+
end
|
186
|
+
hash
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
def out_list_without_self
|
191
|
+
@out_list_without_self ||= begin
|
192
|
+
hash = {}
|
193
|
+
@out_list.each do |ov, out_list|
|
194
|
+
hash[ov] = out_list.select{|arg| arg != :self}
|
195
|
+
end
|
196
|
+
hash
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def exec_with_options(args, options={}, &block)
|
201
|
+
call = ProcedureCall.new(self, args, options)
|
202
|
+
result = call.exec(&block)
|
203
|
+
# if procedure was called then modified object is returned in SELF output parameter
|
204
|
+
if result.is_a?(Hash) && result[:self]
|
205
|
+
object = result.delete(:self)
|
206
|
+
result.empty? ? object : [object, result]
|
207
|
+
else
|
208
|
+
result
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
private
|
213
|
+
|
214
|
+
def set_default_constructor_arguments
|
215
|
+
@arguments ||= {}
|
216
|
+
@argument_list ||= {}
|
217
|
+
@out_list ||= {}
|
218
|
+
@return ||= {}
|
219
|
+
# either this will be the only overload or it will be additional
|
220
|
+
overload = @arguments.keys.size
|
221
|
+
# if type is collection then expect array of objects as argument
|
222
|
+
if @type.collection?
|
223
|
+
@arguments[overload] = {
|
224
|
+
:value => {
|
225
|
+
:position => 1,
|
226
|
+
:data_type => 'TABLE',
|
227
|
+
:in_out => 'IN',
|
228
|
+
:type_owner => @schema_name,
|
229
|
+
:type_name => @type_name,
|
230
|
+
:sql_type_name => "#{@schema_name}.#{@type_name}"
|
231
|
+
}
|
232
|
+
}
|
233
|
+
# otherwise if type is object type then expect object attributes as argument list
|
234
|
+
else
|
235
|
+
@arguments[overload] = @type.attributes
|
236
|
+
end
|
237
|
+
attributes = @arguments[overload]
|
238
|
+
@argument_list[overload] = attributes.keys.sort {|k1, k2| attributes[k1][:position] <=> attributes[k2][:position]}
|
239
|
+
# returns object or collection
|
240
|
+
@return[overload] = {
|
241
|
+
:position => 0,
|
242
|
+
:data_type => @type.collection? ? 'TABLE' : 'OBJECT',
|
243
|
+
:in_out => 'OUT',
|
244
|
+
:type_owner => @schema_name,
|
245
|
+
:type_name => @type_name,
|
246
|
+
:sql_type_name => "#{@schema_name}.#{@type_name}"
|
247
|
+
}
|
248
|
+
@out_list[overload] = []
|
249
|
+
@overloaded = overload > 0
|
250
|
+
end
|
251
|
+
|
252
|
+
end
|
253
|
+
|
254
|
+
end
|
255
|
+
|
256
|
+
class ObjectInstance < Hash #:nodoc:
|
257
|
+
attr_accessor :plsql_type
|
258
|
+
|
259
|
+
def self.create(type, attributes)
|
260
|
+
object = self.new.merge!(attributes)
|
261
|
+
object.plsql_type = type
|
262
|
+
object
|
263
|
+
end
|
264
|
+
|
265
|
+
def method_missing(method, *args, &block)
|
266
|
+
if procedure = @plsql_type.find_procedure(method)
|
267
|
+
procedure.exec_with_options(args, :self => self, &block)
|
268
|
+
else
|
269
|
+
raise ArgumentError, "No PL/SQL procedure '#{method.to_s.upcase}' found for type '#{@plsql_type.type_name}' object"
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
85
273
|
end
|
86
274
|
|
87
275
|
end
|
data/lib/plsql/variable.rb
CHANGED
@@ -10,7 +10,7 @@ module PLSQL
|
|
10
10
|
AND type = 'PACKAGE'
|
11
11
|
AND UPPER(text) LIKE :variable_name",
|
12
12
|
override_schema_name || schema.schema_name, package, "%#{variable_upcase}%").each do |row|
|
13
|
-
if row[0] =~ /^\s*#{variable_upcase}\s+(CONSTANT\s+)?([A-Z0-9_. %]+(\([
|
13
|
+
if row[0] =~ /^\s*#{variable_upcase}\s+(CONSTANT\s+)?([A-Z0-9_. %]+(\([\w\s,]+\))?)\s*(NOT\s+NULL)?\s*((:=|DEFAULT).*)?;\s*(--.*)?$/i
|
14
14
|
return new(schema, variable, package, $2.strip, override_schema_name)
|
15
15
|
end
|
16
16
|
end
|
@@ -47,7 +47,7 @@ module PLSQL
|
|
47
47
|
|
48
48
|
def metadata(type_string)
|
49
49
|
case type_string
|
50
|
-
when /^(VARCHAR2|CHAR|NVARCHAR2|NCHAR)(\((\d+)\))?$/
|
50
|
+
when /^(VARCHAR2|CHAR|NVARCHAR2|NCHAR)(\((\d+)[\s\w]*\))?$/
|
51
51
|
{:data_type => $1, :data_length => $3.to_i, :in_out => 'IN/OUT'}
|
52
52
|
when /^(CLOB|NCLOB|BLOB)$/,
|
53
53
|
/^(NUMBER)(\(.*\))?$/, /^(PLS_INTEGER|BINARY_INTEGER)$/,
|
@@ -63,7 +63,7 @@ module PLSQL
|
|
63
63
|
column = table.columns[$3.downcase.to_sym]
|
64
64
|
{:data_type => column[:data_type], :data_length => column[:data_length], :sql_type_name => column[:sql_type_name], :in_out => 'IN/OUT'}
|
65
65
|
when /^(\w+\.)?(\w+)$/
|
66
|
-
schema = $1 ?
|
66
|
+
schema = $1 ? @schema.root_schema.send($1.chop) : @schema
|
67
67
|
begin
|
68
68
|
type = schema.send($2.downcase.to_sym)
|
69
69
|
raise ArgumentError unless type.is_a?(PLSQL::Type)
|
@@ -404,11 +404,86 @@ describe "Connection" do
|
|
404
404
|
|
405
405
|
end
|
406
406
|
|
407
|
-
describe "
|
407
|
+
describe "session information" do
|
408
408
|
it "should get database version" do
|
409
409
|
# using Oracle version 10.2.0.4 for unit tests
|
410
410
|
@conn.database_version.should == [10, 2]
|
411
411
|
end
|
412
|
+
|
413
|
+
it "should get session ID" do
|
414
|
+
@conn.session_id.should == @conn.select_first("SELECT USERENV('SESSIONID') FROM dual")[0].to_i
|
415
|
+
end
|
416
|
+
end
|
417
|
+
|
418
|
+
describe "drop ruby temporary tables" do
|
419
|
+
after(:all) do
|
420
|
+
@conn.drop_all_ruby_temporary_tables
|
421
|
+
end
|
422
|
+
|
423
|
+
it "should drop all ruby temporary tables" do
|
424
|
+
tmp_table = "ruby_111_222_333"
|
425
|
+
@conn.exec "CREATE GLOBAL TEMPORARY TABLE #{tmp_table} (dummy CHAR(1))"
|
426
|
+
lambda { @conn.select_first("SELECT * FROM #{tmp_table}") }.should_not raise_error
|
427
|
+
@conn.drop_all_ruby_temporary_tables
|
428
|
+
lambda { @conn.select_first("SELECT * FROM #{tmp_table}") }.should raise_error(/table or view does not exist/)
|
429
|
+
end
|
430
|
+
|
431
|
+
it "should drop current session ruby temporary tables" do
|
432
|
+
tmp_table = "ruby_#{@conn.session_id}_222_333"
|
433
|
+
@conn.exec "CREATE GLOBAL TEMPORARY TABLE #{tmp_table} (dummy CHAR(1))"
|
434
|
+
lambda { @conn.select_first("SELECT * FROM #{tmp_table}") }.should_not raise_error
|
435
|
+
@conn.drop_session_ruby_temporary_tables
|
436
|
+
lambda { @conn.select_first("SELECT * FROM #{tmp_table}") }.should raise_error(/table or view does not exist/)
|
437
|
+
end
|
438
|
+
|
439
|
+
it "should not drop other session ruby temporary tables" do
|
440
|
+
tmp_table = "ruby_#{@conn.session_id+1}_222_333"
|
441
|
+
@conn.exec "CREATE GLOBAL TEMPORARY TABLE #{tmp_table} (dummy CHAR(1))"
|
442
|
+
lambda { @conn.select_first("SELECT * FROM #{tmp_table}") }.should_not raise_error
|
443
|
+
@conn.drop_session_ruby_temporary_tables
|
444
|
+
lambda { @conn.select_first("SELECT * FROM #{tmp_table}") }.should_not raise_error
|
445
|
+
end
|
446
|
+
|
447
|
+
end
|
448
|
+
|
449
|
+
describe "logoff" do
|
450
|
+
before(:each) do
|
451
|
+
# restore connection before each test
|
452
|
+
reconnect_connection
|
453
|
+
end
|
454
|
+
|
455
|
+
after(:all) do
|
456
|
+
@conn.exec "DROP TABLE test_dummy_table" rescue nil
|
457
|
+
end
|
458
|
+
|
459
|
+
def reconnect_connection
|
460
|
+
@raw_conn = get_connection
|
461
|
+
@conn = PLSQL::Connection.create( @raw_conn )
|
462
|
+
end
|
463
|
+
|
464
|
+
it "should drop current session ruby temporary tables" do
|
465
|
+
tmp_table = "ruby_#{@conn.session_id}_222_333"
|
466
|
+
@conn.exec "CREATE GLOBAL TEMPORARY TABLE #{tmp_table} (dummy CHAR(1))"
|
467
|
+
lambda { @conn.select_first("SELECT * FROM #{tmp_table}") }.should_not raise_error
|
468
|
+
@conn.logoff
|
469
|
+
reconnect_connection
|
470
|
+
lambda { @conn.select_first("SELECT * FROM #{tmp_table}") }.should raise_error(/table or view does not exist/)
|
471
|
+
end
|
472
|
+
|
473
|
+
it "should rollback any uncommited transactions" do
|
474
|
+
tmp_table = "ruby_#{@conn.session_id}_222_333"
|
475
|
+
old_autocommit = @conn.autocommit?
|
476
|
+
@conn.autocommit = false
|
477
|
+
@conn.exec "CREATE GLOBAL TEMPORARY TABLE #{tmp_table} (dummy CHAR(1))"
|
478
|
+
@conn.exec "CREATE TABLE test_dummy_table (dummy CHAR(1))"
|
479
|
+
@conn.exec "INSERT INTO test_dummy_table VALUES ('1')"
|
480
|
+
# logoff will drop ruby temporary tables, it should do rollback before drop table
|
481
|
+
@conn.logoff
|
482
|
+
reconnect_connection
|
483
|
+
@conn.select_first("SELECT * FROM test_dummy_table").should == nil
|
484
|
+
@conn.autocommit = old_autocommit
|
485
|
+
end
|
486
|
+
|
412
487
|
end
|
413
488
|
|
414
489
|
end
|