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