ruby-plsql 0.4.0 → 0.4.1
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 +19 -0
- data/License.txt +1 -1
- data/README.rdoc +9 -4
- data/VERSION +1 -1
- data/lib/plsql/connection.rb +24 -9
- data/lib/plsql/helpers.rb +9 -0
- data/lib/plsql/jdbc_connection.rb +38 -9
- data/lib/plsql/oci8_patches.rb +25 -0
- data/lib/plsql/oci_connection.rb +46 -12
- data/lib/plsql/package.rb +25 -12
- data/lib/plsql/procedure.rb +23 -29
- data/lib/plsql/procedure_call.rb +53 -4
- data/lib/plsql/schema.rb +114 -45
- data/lib/plsql/sequence.rb +1 -1
- data/lib/plsql/sql_statements.rb +7 -2
- data/lib/plsql/table.rb +80 -25
- data/lib/plsql/type.rb +87 -0
- data/lib/plsql/variable.rb +146 -0
- data/lib/plsql/version.rb +1 -1
- data/lib/plsql/view.rb +41 -0
- data/lib/ruby_plsql.rb +1 -1
- data/spec/plsql/connection_spec.rb +112 -50
- data/spec/plsql/package_spec.rb +19 -10
- data/spec/plsql/procedure_spec.rb +159 -126
- data/spec/plsql/schema_spec.rb +109 -1
- data/spec/plsql/sql_statements_spec.rb +14 -32
- data/spec/plsql/table_spec.rb +75 -9
- data/spec/plsql/type_spec.rb +133 -0
- data/spec/plsql/variable_spec.rb +458 -0
- data/spec/plsql/version_spec.rb +8 -0
- data/spec/plsql/view_spec.rb +259 -0
- data/spec/spec_helper.rb +1 -1
- metadata +15 -2
data/lib/plsql/procedure.rb
CHANGED
@@ -3,16 +3,16 @@ module PLSQL
|
|
3
3
|
module ProcedureClassMethods #:nodoc:
|
4
4
|
def find(schema, procedure, package = nil, override_schema_name = nil)
|
5
5
|
if package.nil?
|
6
|
-
if schema.select_first(
|
7
|
-
SELECT
|
6
|
+
if (row = schema.select_first(
|
7
|
+
"SELECT object_id FROM all_objects
|
8
8
|
WHERE owner = :owner
|
9
9
|
AND object_name = :object_name
|
10
10
|
AND object_type IN ('PROCEDURE','FUNCTION')",
|
11
|
-
schema.schema_name, procedure.to_s.upcase)
|
12
|
-
new(schema, procedure)
|
11
|
+
schema.schema_name, procedure.to_s.upcase))
|
12
|
+
new(schema, procedure, nil, nil, row[0])
|
13
13
|
# search for synonym
|
14
|
-
elsif (row = schema.select_first(
|
15
|
-
SELECT o.owner, o.object_name
|
14
|
+
elsif (row = schema.select_first(
|
15
|
+
"SELECT o.owner, o.object_name, o.object_id
|
16
16
|
FROM all_synonyms s, all_objects o
|
17
17
|
WHERE s.owner IN (:owner, 'PUBLIC')
|
18
18
|
AND s.synonym_name = :synonym_name
|
@@ -21,17 +21,21 @@ module PLSQL
|
|
21
21
|
AND o.object_type IN ('PROCEDURE','FUNCTION')
|
22
22
|
ORDER BY DECODE(s.owner, 'PUBLIC', 1, 0)",
|
23
23
|
schema.schema_name, procedure.to_s.upcase))
|
24
|
-
new(schema, row[1], nil, row[0])
|
24
|
+
new(schema, row[1], nil, row[0], row[2])
|
25
25
|
else
|
26
26
|
nil
|
27
27
|
end
|
28
|
-
elsif package && schema.select_first(
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
AND
|
33
|
-
|
34
|
-
|
28
|
+
elsif package && (row = schema.select_first(
|
29
|
+
# older Oracle versions do not have object_id column in all_procedures
|
30
|
+
"SELECT o.object_id FROM all_procedures p, all_objects o
|
31
|
+
WHERE p.owner = :owner
|
32
|
+
AND p.object_name = :object_name
|
33
|
+
AND p.procedure_name = :procedure_name
|
34
|
+
AND o.owner = p.owner
|
35
|
+
AND o.object_name = p.object_name
|
36
|
+
AND o.object_type = 'PACKAGE'",
|
37
|
+
override_schema_name || schema.schema_name, package, procedure.to_s.upcase))
|
38
|
+
new(schema, procedure, package, override_schema_name, row[0])
|
35
39
|
else
|
36
40
|
nil
|
37
41
|
end
|
@@ -44,7 +48,7 @@ module PLSQL
|
|
44
48
|
attr_reader :arguments, :argument_list, :out_list, :return
|
45
49
|
attr_reader :schema, :schema_name, :package, :procedure
|
46
50
|
|
47
|
-
def initialize(schema, procedure, package
|
51
|
+
def initialize(schema, procedure, package, override_schema_name, object_id)
|
48
52
|
@schema = schema
|
49
53
|
@schema_name = override_schema_name || schema.schema_name
|
50
54
|
@procedure = procedure.to_s.upcase
|
@@ -58,18 +62,8 @@ module PLSQL
|
|
58
62
|
# store reference to previous level record or collection metadata
|
59
63
|
previous_level_argument_metadata = {}
|
60
64
|
|
61
|
-
|
62
|
-
|
63
|
-
object_id = @schema.connection.select_first("
|
64
|
-
SELECT o.object_id
|
65
|
-
FROM all_objects o
|
66
|
-
WHERE o.owner = :owner
|
67
|
-
AND o.object_name = :object_name
|
68
|
-
AND o.object_type <> 'PACKAGE BODY'
|
69
|
-
", @schema_name, @package ? @package : @procedure
|
70
|
-
)[0] rescue nil
|
71
|
-
num_rows = @schema.connection.select_all("
|
72
|
-
SELECT overload, argument_name, position, data_level,
|
65
|
+
@schema.select_all(
|
66
|
+
"SELECT overload, argument_name, position, data_level,
|
73
67
|
data_type, in_out, data_length, data_precision, data_scale, char_used,
|
74
68
|
type_owner, type_name, type_subname
|
75
69
|
FROM all_arguments
|
@@ -77,8 +71,8 @@ module PLSQL
|
|
77
71
|
AND owner = :owner
|
78
72
|
AND object_name = :procedure_name
|
79
73
|
AND NVL(package_name,'nil') = :package
|
80
|
-
ORDER BY overload, sequence
|
81
|
-
|
74
|
+
ORDER BY overload, sequence",
|
75
|
+
object_id, @schema_name, @procedure, @package ? @package : 'nil'
|
82
76
|
) do |r|
|
83
77
|
|
84
78
|
overload, argument_name, position, data_level,
|
data/lib/plsql/procedure_call.rb
CHANGED
@@ -3,12 +3,14 @@ module PLSQL
|
|
3
3
|
|
4
4
|
def initialize(procedure, args = [])
|
5
5
|
@procedure = procedure
|
6
|
+
@schema = @procedure.schema
|
7
|
+
@dbms_output_stream = @schema.dbms_output_stream
|
6
8
|
@overload = get_overload_from_arguments_list(args)
|
7
9
|
construct_sql(args)
|
8
10
|
end
|
9
11
|
|
10
12
|
def exec
|
11
|
-
@cursor = @
|
13
|
+
@cursor = @schema.connection.parse(@sql)
|
12
14
|
|
13
15
|
@bind_values.each do |arg, value|
|
14
16
|
@cursor.bind_param(":#{arg}", value, @bind_metadata[arg])
|
@@ -20,6 +22,8 @@ module PLSQL
|
|
20
22
|
|
21
23
|
@cursor.exec
|
22
24
|
|
25
|
+
dbms_output_log
|
26
|
+
|
23
27
|
if block_given?
|
24
28
|
yield get_return_value
|
25
29
|
nil
|
@@ -66,10 +70,10 @@ module PLSQL
|
|
66
70
|
@return_vars = []
|
67
71
|
@return_vars_metadata = {}
|
68
72
|
|
73
|
+
@call_sql << add_return if return_metadata
|
69
74
|
# construct procedure call if procedure name is available
|
70
75
|
# otherwise will get surrounding call_sql from @procedure (used for table statements)
|
71
76
|
if procedure_name
|
72
|
-
@call_sql << add_return if return_metadata
|
73
77
|
@call_sql << "#{schema_name}." if schema_name
|
74
78
|
@call_sql << "#{package_name}." if package_name
|
75
79
|
@call_sql << "#{procedure_name}("
|
@@ -122,8 +126,53 @@ module PLSQL
|
|
122
126
|
@call_sql = @procedure.call_sql(@call_sql)
|
123
127
|
end
|
124
128
|
add_out_vars
|
125
|
-
|
126
|
-
|
129
|
+
|
130
|
+
dbms_output_enable_sql, dbms_output_get_sql = dbms_output_sql
|
131
|
+
|
132
|
+
@sql = "" << @declare_sql << @assignment_sql << dbms_output_enable_sql << @call_sql << dbms_output_get_sql << @return_sql << "END;\n"
|
133
|
+
end
|
134
|
+
|
135
|
+
def dbms_output_sql
|
136
|
+
if @dbms_output_stream
|
137
|
+
dbms_output_enable_sql = "DBMS_OUTPUT.ENABLE(#{@schema.dbms_output_buffer_size});\n"
|
138
|
+
# if database version is at least 10.2 then use DBMS_OUTPUT.GET_LINES with SYS.DBMSOUTPUT_LINESARRAY
|
139
|
+
if (@schema.connection.database_version <=> [10, 2]) >= 0
|
140
|
+
@declare_sql << "l_dbms_output_numlines INTEGER := #{Schema::DBMS_OUTPUT_MAX_LINES};\n"
|
141
|
+
dbms_output_get_sql = "DBMS_OUTPUT.GET_LINES(:dbms_output_lines, l_dbms_output_numlines);\n"
|
142
|
+
@bind_values[:dbms_output_lines] = nil
|
143
|
+
@bind_metadata[:dbms_output_lines] = {:data_type => 'TABLE', :data_length => nil,
|
144
|
+
:sql_type_name => "SYS.DBMSOUTPUT_LINESARRAY", :in_out => 'OUT'}
|
145
|
+
# if database version is less than 10.2 then use individual DBMS_OUTPUT.GET_LINE calls
|
146
|
+
else
|
147
|
+
dbms_output_get_sql = ""
|
148
|
+
end
|
149
|
+
[dbms_output_enable_sql, dbms_output_get_sql]
|
150
|
+
else
|
151
|
+
["", ""]
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def dbms_output_log
|
156
|
+
if @dbms_output_stream
|
157
|
+
# if database version is at least 10.2 then :dbms_output_lines output bind variable has dbms_output lines
|
158
|
+
if @bind_metadata[:dbms_output_lines]
|
159
|
+
@cursor[':dbms_output_lines'].each do |line|
|
160
|
+
@dbms_output_stream.puts "DBMS_OUTPUT: #{line}" if line
|
161
|
+
end
|
162
|
+
# if database version is less than 10.2 then use individual DBMS_OUTPUT.GET_LINE calls
|
163
|
+
else
|
164
|
+
cursor = @schema.connection.parse("BEGIN sys.dbms_output.get_line(:line, :status); END;")
|
165
|
+
while true do
|
166
|
+
cursor.bind_param(':line', nil, :data_type => 'VARCHAR2', :in_out => 'OUT')
|
167
|
+
cursor.bind_param(':status', nil, :data_type => 'NUMBER', :in_out => 'OUT')
|
168
|
+
cursor.exec
|
169
|
+
break unless cursor[':status'] == 0
|
170
|
+
@dbms_output_stream.puts "DBMS_OUTPUT: #{cursor[':line']}"
|
171
|
+
end
|
172
|
+
cursor.close
|
173
|
+
end
|
174
|
+
@dbms_output_stream.flush
|
175
|
+
end
|
127
176
|
end
|
128
177
|
|
129
178
|
def add_argument(argument, value)
|
data/lib/plsql/schema.rb
CHANGED
@@ -3,7 +3,7 @@ module PLSQL
|
|
3
3
|
include SQLStatements
|
4
4
|
|
5
5
|
@@schemas = {}
|
6
|
-
|
6
|
+
|
7
7
|
class <<self
|
8
8
|
def find_or_new(connection_alias) #:nodoc:
|
9
9
|
connection_alias ||= :default
|
@@ -15,18 +15,16 @@ module PLSQL
|
|
15
15
|
end
|
16
16
|
|
17
17
|
end
|
18
|
-
|
19
|
-
def initialize(raw_conn = nil, schema = nil,
|
18
|
+
|
19
|
+
def initialize(raw_conn = nil, schema = nil, original_schema = nil) #:nodoc:
|
20
20
|
self.connection = raw_conn
|
21
21
|
@schema_name = schema ? schema.to_s.upcase : nil
|
22
|
-
@
|
22
|
+
@original_schema = original_schema
|
23
23
|
end
|
24
|
-
|
24
|
+
|
25
25
|
# Returns connection wrapper object (this is not raw OCI8 or JDBC connection!)
|
26
|
-
|
27
|
-
|
28
|
-
end
|
29
|
-
|
26
|
+
attr_reader :connection
|
27
|
+
|
30
28
|
def raw_connection=(raw_conn) #:nodoc:
|
31
29
|
@connection = raw_conn ? Connection.create(raw_conn) : nil
|
32
30
|
reset_instance_variables
|
@@ -49,6 +47,7 @@ module PLSQL
|
|
49
47
|
else
|
50
48
|
self.raw_connection = conn
|
51
49
|
end
|
50
|
+
conn
|
52
51
|
end
|
53
52
|
|
54
53
|
# Set connection to current ActiveRecord connection (use in initializer file):
|
@@ -58,6 +57,7 @@ module PLSQL
|
|
58
57
|
def activerecord_class=(ar_class)
|
59
58
|
@connection = ar_class ? Connection.create(nil, ar_class) : nil
|
60
59
|
reset_instance_variables
|
60
|
+
ar_class
|
61
61
|
end
|
62
62
|
|
63
63
|
# Disconnect from Oracle
|
@@ -72,22 +72,23 @@ module PLSQL
|
|
72
72
|
@schema_name ||= select_first("SELECT SYS_CONTEXT('userenv','session_user') FROM dual")[0]
|
73
73
|
end
|
74
74
|
|
75
|
-
# Set to :local or :utc
|
76
|
-
@@default_timezone = nil
|
77
|
-
|
78
75
|
# Default timezone to which database values will be converted - :utc or :local
|
79
76
|
def default_timezone
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
77
|
+
if @original_schema
|
78
|
+
@original_schema.default_timezone
|
79
|
+
else
|
80
|
+
@default_timezone ||
|
81
|
+
# Use ActiveRecord class default_timezone when ActiveRecord connection is used
|
82
|
+
(@connection && (ar_class = @connection.activerecord_class) && ar_class.default_timezone) ||
|
83
|
+
# default to local timezone
|
84
|
+
:local
|
85
|
+
end
|
85
86
|
end
|
86
87
|
|
87
88
|
# Set default timezone to which database values will be converted - :utc or :local
|
88
89
|
def default_timezone=(value)
|
89
90
|
if [:local, :utc].include?(value)
|
90
|
-
|
91
|
+
@default_timezone = value
|
91
92
|
else
|
92
93
|
raise ArgumentError, "default timezone should be :local or :utc"
|
93
94
|
end
|
@@ -98,7 +99,47 @@ module PLSQL
|
|
98
99
|
def local_timezone_offset #:nodoc:
|
99
100
|
::Time.local(2007).utc_offset.to_r / 86400
|
100
101
|
end
|
101
|
-
|
102
|
+
|
103
|
+
# DBMS_OUTPUT buffer size (default is 20_000)
|
104
|
+
def dbms_output_buffer_size
|
105
|
+
if @original_schema
|
106
|
+
@original_schema.dbms_output_buffer_size
|
107
|
+
else
|
108
|
+
@dbms_output_buffer_size || 20_000
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Seet DBMS_OUTPUT buffer size (default is 20_000). Example:
|
113
|
+
#
|
114
|
+
# plsql.dbms_output_buffer_size = 100_000
|
115
|
+
#
|
116
|
+
def dbms_output_buffer_size=(value)
|
117
|
+
@dbms_output_buffer_size = value
|
118
|
+
end
|
119
|
+
|
120
|
+
# Maximum line numbers for DBMS_OUTPUT in one PL/SQL call (from DBMSOUTPUT_LINESARRAY type)
|
121
|
+
DBMS_OUTPUT_MAX_LINES = 2147483647
|
122
|
+
|
123
|
+
# Specify IO stream where to log DBMS_OUTPUT from PL/SQL procedures. Example:
|
124
|
+
#
|
125
|
+
# plsql.dbms_output_stream = STDOUT
|
126
|
+
#
|
127
|
+
def dbms_output_stream=(stream)
|
128
|
+
@dbms_output_stream = stream
|
129
|
+
if @dbms_output_stream.nil? && @connection
|
130
|
+
sys.dbms_output.disable
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# IO stream where to log DBMS_OUTPUT from PL/SQL procedures.
|
135
|
+
def dbms_output_stream
|
136
|
+
if @original_schema
|
137
|
+
@original_schema.dbms_output_stream
|
138
|
+
else
|
139
|
+
@dbms_output_stream
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
102
143
|
private
|
103
144
|
|
104
145
|
def reset_instance_variables
|
@@ -108,44 +149,72 @@ module PLSQL
|
|
108
149
|
@schema_objects = nil
|
109
150
|
end
|
110
151
|
@schema_name = nil
|
111
|
-
|
152
|
+
@default_timezone = nil
|
112
153
|
end
|
113
|
-
|
154
|
+
|
114
155
|
def method_missing(method, *args, &block)
|
115
|
-
raise ArgumentError, "No
|
116
|
-
#
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
elsif procedure = Procedure.find(self, method)
|
125
|
-
@schema_objects[method] = procedure
|
126
|
-
procedure.exec(*args, &block)
|
127
|
-
elsif package = Package.find(self, method)
|
128
|
-
@schema_objects[method] = package
|
129
|
-
elsif table = Table.find(self, method)
|
130
|
-
@schema_objects[method] = table
|
131
|
-
elsif sequence = Sequence.find(self, method)
|
132
|
-
@schema_objects[method] = sequence
|
133
|
-
elsif schema = find_other_schema(method)
|
134
|
-
@schema_objects[method] = schema
|
156
|
+
raise ArgumentError, "No database connection" unless connection
|
157
|
+
# search in database if not in cache at first
|
158
|
+
object = (@schema_objects[method] ||= find_database_object(method) || find_other_schema(method) ||
|
159
|
+
find_public_synonym(method)) || find_standard_procedure(method)
|
160
|
+
|
161
|
+
raise ArgumentError, "No database object '#{method.to_s.upcase}' found" unless object
|
162
|
+
|
163
|
+
if object.is_a?(Procedure)
|
164
|
+
object.exec(*args, &block)
|
135
165
|
else
|
136
|
-
|
166
|
+
object
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def find_database_object(name, override_schema_name = nil)
|
171
|
+
object_schema_name = override_schema_name || schema_name
|
172
|
+
object_name = name.to_s.upcase
|
173
|
+
if row = select_first(
|
174
|
+
"SELECT object_type, object_id FROM all_objects
|
175
|
+
WHERE owner = :owner AND object_name = :object_name
|
176
|
+
AND object_type IN ('PROCEDURE','FUNCTION','PACKAGE','TABLE','VIEW','SEQUENCE','TYPE','SYNONYM')",
|
177
|
+
object_schema_name, object_name)
|
178
|
+
case row[0]
|
179
|
+
when 'PROCEDURE', 'FUNCTION'
|
180
|
+
Procedure.new(self, name, nil, override_schema_name, row[1])
|
181
|
+
when 'PACKAGE', 'PACKAGE BODY'
|
182
|
+
Package.new(self, name, override_schema_name)
|
183
|
+
when 'TABLE'
|
184
|
+
Table.new(self, name, override_schema_name)
|
185
|
+
when 'VIEW'
|
186
|
+
View.new(self, name, override_schema_name)
|
187
|
+
when 'SEQUENCE'
|
188
|
+
Sequence.new(self, name, override_schema_name)
|
189
|
+
when 'TYPE'
|
190
|
+
Type.new(self, name, override_schema_name)
|
191
|
+
when 'SYNONYM'
|
192
|
+
target_schema_name, target_object_name = @connection.describe_synonym(object_schema_name, object_name)
|
193
|
+
find_database_object(target_object_name, target_schema_name)
|
194
|
+
end
|
137
195
|
end
|
138
196
|
end
|
139
197
|
|
140
198
|
def find_other_schema(name)
|
141
|
-
return nil
|
199
|
+
return nil if @original_schema
|
142
200
|
if select_first("SELECT username FROM all_users WHERE username = :username", name.to_s.upcase)
|
143
|
-
Schema.new(connection, name,
|
201
|
+
Schema.new(connection, name, self)
|
144
202
|
else
|
145
203
|
nil
|
146
204
|
end
|
147
205
|
end
|
148
|
-
|
206
|
+
|
207
|
+
def find_standard_procedure(name)
|
208
|
+
return nil if @original_schema
|
209
|
+
Procedure.find(self, name, 'STANDARD', 'SYS')
|
210
|
+
end
|
211
|
+
|
212
|
+
def find_public_synonym(name)
|
213
|
+
return nil if @original_schema
|
214
|
+
target_schema_name, target_object_name = @connection.describe_synonym('PUBLIC', name)
|
215
|
+
find_database_object(target_object_name, target_schema_name) if target_schema_name
|
216
|
+
end
|
217
|
+
|
149
218
|
end
|
150
219
|
end
|
151
220
|
|
data/lib/plsql/sequence.rb
CHANGED
@@ -39,7 +39,7 @@ module PLSQL
|
|
39
39
|
@schema.select_one "SELECT \"#{@schema_name}\".\"#{@sequence_name}\".NEXTVAL FROM dual"
|
40
40
|
end
|
41
41
|
|
42
|
-
# Get
|
42
|
+
# Get CURRVAL of sequence (can be called just after nextval)
|
43
43
|
def currval
|
44
44
|
@schema.select_one "SELECT \"#{@schema_name}\".\"#{@sequence_name}\".CURRVAL FROM dual"
|
45
45
|
end
|
data/lib/plsql/sql_statements.rb
CHANGED
@@ -5,6 +5,11 @@ module PLSQL
|
|
5
5
|
@connection.select_first(sql, *bindvars)
|
6
6
|
end
|
7
7
|
|
8
|
+
# Select all rows as array or values (without column names)
|
9
|
+
def select_all(sql, *bindvars, &block)
|
10
|
+
@connection.select_all(sql, *bindvars, &block)
|
11
|
+
end
|
12
|
+
|
8
13
|
# Select one value (use if only one row with one value is selected)
|
9
14
|
def select_one(sql, *bindvars)
|
10
15
|
(row = @connection.select_first(sql, *bindvars)) && row[0]
|
@@ -43,7 +48,7 @@ module PLSQL
|
|
43
48
|
#
|
44
49
|
# to turn off automatic commits after each statement.
|
45
50
|
def commit
|
46
|
-
connection.commit
|
51
|
+
@connection.commit
|
47
52
|
end
|
48
53
|
|
49
54
|
# Execute ROLLBACK in current database session.
|
@@ -53,7 +58,7 @@ module PLSQL
|
|
53
58
|
#
|
54
59
|
# to turn off automatic commits after each statement.
|
55
60
|
def rollback
|
56
|
-
connection.rollback
|
61
|
+
@connection.rollback
|
57
62
|
end
|
58
63
|
|
59
64
|
end
|