ruby-plsql 0.5.3 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/stale.yml +37 -0
- data/.github/workflows/rubocop.yml +37 -0
- data/.github/workflows/test.yml +69 -0
- data/.rubocop.yml +147 -0
- data/.travis.yml +88 -0
- data/.travis/oracle/download.sh +15 -0
- data/.travis/oracle/install.sh +32 -0
- data/.travis/setup_accounts.sh +9 -0
- data/Gemfile +17 -9
- data/History.txt +76 -0
- data/README.md +29 -6
- data/Rakefile +31 -26
- data/VERSION +1 -1
- data/Vagrantfile +4 -4
- data/ci/network/admin/tnsnames.ora +7 -0
- data/ci/setup_accounts.sh +9 -0
- data/gemfiles/Gemfile.activerecord-5.0 +21 -0
- data/gemfiles/Gemfile.activerecord-5.1 +21 -0
- data/gemfiles/Gemfile.activerecord-5.2 +21 -0
- data/gemfiles/Gemfile.activerecord-6.0 +21 -0
- data/gemfiles/Gemfile.activerecord-6.1 +21 -0
- data/gemfiles/Gemfile.activerecord-main +21 -0
- data/lib/plsql/connection.rb +19 -22
- data/lib/plsql/helpers.rb +1 -3
- data/lib/plsql/jdbc_connection.rb +70 -68
- data/lib/plsql/oci8_patches.rb +2 -2
- data/lib/plsql/oci_connection.rb +62 -77
- data/lib/plsql/package.rb +61 -46
- data/lib/plsql/procedure.rb +358 -78
- data/lib/plsql/procedure_call.rb +508 -463
- data/lib/plsql/schema.rb +96 -101
- data/lib/plsql/sequence.rb +10 -13
- data/lib/plsql/sql_statements.rb +9 -11
- data/lib/plsql/table.rb +60 -63
- data/lib/plsql/type.rb +71 -76
- data/lib/plsql/variable.rb +90 -94
- data/lib/plsql/version.rb +1 -1
- data/lib/plsql/view.rb +16 -19
- data/ruby-plsql.gemspec +55 -35
- data/spec/plsql/connection_spec.rb +72 -66
- data/spec/plsql/package_spec.rb +63 -14
- data/spec/plsql/procedure_spec.rb +603 -261
- data/spec/plsql/schema_spec.rb +47 -23
- data/spec/plsql/sequence_spec.rb +2 -2
- data/spec/plsql/sql_statements_spec.rb +6 -6
- data/spec/plsql/table_spec.rb +84 -79
- data/spec/plsql/type_spec.rb +24 -30
- data/spec/plsql/variable_spec.rb +80 -88
- data/spec/plsql/version_spec.rb +4 -4
- data/spec/plsql/view_spec.rb +42 -42
- data/spec/spec_helper.rb +38 -35
- data/spec/support/create_arunit_user.sql +2 -0
- data/spec/support/custom_config.rb.sample +14 -0
- data/spec/support/test_db.rb +12 -13
- data/spec/support/unlock_and_setup_hr_user.sql +2 -0
- metadata +111 -34
data/lib/plsql/type.rb
CHANGED
@@ -1,28 +1,27 @@
|
|
1
1
|
module PLSQL
|
2
|
-
|
3
2
|
module TypeClassMethods #:nodoc:
|
4
3
|
def find(schema, type)
|
5
4
|
if schema.select_first(
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
"SELECT type_name FROM all_types
|
6
|
+
WHERE owner = :owner
|
7
|
+
AND type_name = :table_name",
|
9
8
|
schema.schema_name, type.to_s.upcase)
|
10
9
|
new(schema, type)
|
11
10
|
# search for synonym
|
12
11
|
elsif (row = schema.select_first(
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
12
|
+
"SELECT t.owner, t.type_name
|
13
|
+
FROM all_synonyms s, all_types t
|
14
|
+
WHERE s.owner = :owner
|
15
|
+
AND s.synonym_name = :synonym_name
|
16
|
+
AND t.owner = s.table_owner
|
17
|
+
AND t.type_name = s.table_name
|
18
|
+
UNION ALL
|
19
|
+
SELECT t.owner, t.type_name
|
20
|
+
FROM all_synonyms s, all_types t
|
21
|
+
WHERE s.owner = 'PUBLIC'
|
22
|
+
AND s.synonym_name = :synonym_name
|
23
|
+
AND t.owner = s.table_owner
|
24
|
+
AND t.type_name = s.table_name",
|
26
25
|
schema.schema_name, type.to_s.upcase, type.to_s.upcase))
|
27
26
|
new(schema, row[1], row[0])
|
28
27
|
else
|
@@ -67,28 +66,28 @@ module PLSQL
|
|
67
66
|
) do |r|
|
68
67
|
attr_name, position,
|
69
68
|
data_type, data_length, data_precision, data_scale,
|
70
|
-
data_type_owner,
|
69
|
+
data_type_owner, _, typecode = r
|
71
70
|
@attributes[attr_name.downcase.to_sym] = {
|
72
|
-
:
|
73
|
-
:
|
74
|
-
:
|
75
|
-
:
|
76
|
-
:
|
77
|
-
:
|
78
|
-
:
|
79
|
-
:
|
71
|
+
position: position && position.to_i,
|
72
|
+
data_type: data_type_owner && (typecode == "COLLECTION" ? "TABLE" : "OBJECT") || data_type,
|
73
|
+
data_length: data_type_owner ? nil : data_length && data_length.to_i,
|
74
|
+
data_precision: data_precision && data_precision.to_i,
|
75
|
+
data_scale: data_scale && data_scale.to_i,
|
76
|
+
type_owner: data_type_owner,
|
77
|
+
type_name: data_type_owner && data_type,
|
78
|
+
sql_type_name: data_type_owner && "#{data_type_owner}.#{data_type}"
|
80
79
|
}
|
81
80
|
end
|
82
81
|
end
|
83
82
|
|
84
83
|
# is type collection?
|
85
84
|
def collection?
|
86
|
-
@is_collection ||= @typecode ==
|
85
|
+
@is_collection ||= @typecode == "COLLECTION"
|
87
86
|
end
|
88
87
|
|
89
88
|
# list of object type attribute names
|
90
89
|
def attribute_names
|
91
|
-
@attribute_names ||= @attributes.keys.sort_by{|k| @attributes[k][:position]}
|
90
|
+
@attribute_names ||= @attributes.keys.sort_by { |k| @attributes[k][:position] }
|
92
91
|
end
|
93
92
|
|
94
93
|
# create new PL/SQL object instance
|
@@ -98,7 +97,7 @@ module PLSQL
|
|
98
97
|
if collection? && !(args.size == 1 && args[0].is_a?(Array))
|
99
98
|
args = [args]
|
100
99
|
end
|
101
|
-
result = procedure.exec_with_options(args, {:
|
100
|
+
result = procedure.exec_with_options(args, { skip_self: true }, &block)
|
102
101
|
# TODO: collection constructor should return Array of ObhjectInstance objects
|
103
102
|
if collection?
|
104
103
|
result
|
@@ -121,10 +120,10 @@ module PLSQL
|
|
121
120
|
procedure_name = new_or_procedure == :new ? @type_name : new_or_procedure
|
122
121
|
# find defined procedure for type
|
123
122
|
if @schema.select_first(
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
123
|
+
"SELECT procedure_name FROM all_procedures
|
124
|
+
WHERE owner = :owner
|
125
|
+
AND object_name = :object_name
|
126
|
+
AND procedure_name = :procedure_name",
|
128
127
|
@schema_name, @type_name, procedure_name.to_s.upcase)
|
129
128
|
TypeProcedure.new(@schema, self, procedure_name)
|
130
129
|
# call default constructor
|
@@ -171,7 +170,7 @@ module PLSQL
|
|
171
170
|
@arguments_without_self ||= begin
|
172
171
|
hash = {}
|
173
172
|
@arguments.each do |ov, args|
|
174
|
-
hash[ov] = args.reject{|key, value| key == :self}
|
173
|
+
hash[ov] = args.reject { |key, value| key == :self }
|
175
174
|
end
|
176
175
|
hash
|
177
176
|
end
|
@@ -181,7 +180,7 @@ module PLSQL
|
|
181
180
|
@argument_list_without_self ||= begin
|
182
181
|
hash = {}
|
183
182
|
@argument_list.each do |ov, arg_list|
|
184
|
-
hash[ov] = arg_list.select{|arg| arg != :self}
|
183
|
+
hash[ov] = arg_list.select { |arg| arg != :self }
|
185
184
|
end
|
186
185
|
hash
|
187
186
|
end
|
@@ -191,13 +190,13 @@ module PLSQL
|
|
191
190
|
@out_list_without_self ||= begin
|
192
191
|
hash = {}
|
193
192
|
@out_list.each do |ov, out_list|
|
194
|
-
hash[ov] = out_list.select{|arg| arg != :self}
|
193
|
+
hash[ov] = out_list.select { |arg| arg != :self }
|
195
194
|
end
|
196
195
|
hash
|
197
196
|
end
|
198
197
|
end
|
199
198
|
|
200
|
-
def exec_with_options(args, options={}, &block)
|
199
|
+
def exec_with_options(args, options = {}, &block)
|
201
200
|
call = ProcedureCall.new(self, args, options)
|
202
201
|
result = call.exec(&block)
|
203
202
|
# if procedure was called then modified object is returned in SELF output parameter
|
@@ -211,46 +210,44 @@ module PLSQL
|
|
211
210
|
|
212
211
|
private
|
213
212
|
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
213
|
+
def set_default_constructor_arguments
|
214
|
+
@arguments ||= {}
|
215
|
+
@argument_list ||= {}
|
216
|
+
@out_list ||= {}
|
217
|
+
@return ||= {}
|
218
|
+
# either this will be the only overload or it will be additional
|
219
|
+
overload = @arguments.keys.size
|
220
|
+
# if type is collection then expect array of objects as argument
|
221
|
+
if @type.collection?
|
222
|
+
@arguments[overload] = {
|
223
|
+
value: {
|
224
|
+
position: 1,
|
225
|
+
data_type: "TABLE",
|
226
|
+
in_out: "IN",
|
227
|
+
type_owner: @schema_name,
|
228
|
+
type_name: @type_name,
|
229
|
+
sql_type_name: "#{@schema_name}.#{@type_name}"
|
230
|
+
}
|
231
231
|
}
|
232
|
+
# otherwise if type is object type then expect object attributes as argument list
|
233
|
+
else
|
234
|
+
@arguments[overload] = @type.attributes
|
235
|
+
end
|
236
|
+
attributes = @arguments[overload]
|
237
|
+
@argument_list[overload] = attributes.keys.sort { |k1, k2| attributes[k1][:position] <=> attributes[k2][:position] }
|
238
|
+
# returns object or collection
|
239
|
+
@return[overload] = {
|
240
|
+
position: 0,
|
241
|
+
data_type: @type.collection? ? "TABLE" : "OBJECT",
|
242
|
+
in_out: "OUT",
|
243
|
+
type_owner: @schema_name,
|
244
|
+
type_name: @type_name,
|
245
|
+
sql_type_name: "#{@schema_name}.#{@type_name}"
|
232
246
|
}
|
233
|
-
|
234
|
-
|
235
|
-
@arguments[overload] = @type.attributes
|
247
|
+
@out_list[overload] = []
|
248
|
+
@overloaded = overload > 0
|
236
249
|
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
250
|
end
|
253
|
-
|
254
251
|
end
|
255
252
|
|
256
253
|
class ObjectInstance < Hash #:nodoc:
|
@@ -264,12 +261,10 @@ module PLSQL
|
|
264
261
|
|
265
262
|
def method_missing(method, *args, &block)
|
266
263
|
if procedure = @plsql_type.find_procedure(method)
|
267
|
-
procedure.exec_with_options(args, :
|
264
|
+
procedure.exec_with_options(args, self: self, &block)
|
268
265
|
else
|
269
266
|
raise ArgumentError, "No PL/SQL procedure '#{method.to_s.upcase}' found for type '#{@plsql_type.type_name}' object"
|
270
267
|
end
|
271
268
|
end
|
272
|
-
|
273
269
|
end
|
274
|
-
|
275
270
|
end
|
data/lib/plsql/variable.rb
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
module PLSQL
|
2
|
-
|
3
2
|
module VariableClassMethods #:nodoc:
|
4
3
|
def find(schema, variable, package, override_schema_name = nil)
|
5
4
|
variable_upcase = variable.to_s.upcase
|
6
5
|
schema.select_all(
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
6
|
+
"SELECT text FROM all_source
|
7
|
+
WHERE owner = :owner
|
8
|
+
AND name = :object_name
|
9
|
+
AND type = 'PACKAGE'
|
10
|
+
AND UPPER(text) LIKE :variable_name",
|
12
11
|
override_schema_name || schema.schema_name, package, "%#{variable_upcase}%").each do |row|
|
13
12
|
if row[0] =~ /^\s*#{variable_upcase}\s+(CONSTANT\s+)?([A-Z0-9_. %]+(\([\w\s,]+\))?)\s*(NOT\s+NULL)?\s*((:=|DEFAULT).*)?;\s*(--.*)?$/i
|
14
13
|
return new(schema, variable, package, $2.strip, override_schema_name)
|
@@ -45,102 +44,99 @@ module PLSQL
|
|
45
44
|
|
46
45
|
private
|
47
46
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
47
|
+
def metadata(type_string)
|
48
|
+
case type_string
|
49
|
+
when /^(VARCHAR|VARCHAR2|CHAR|NVARCHAR2|NCHAR)(\((\d+)[\s\w]*\))?$/
|
50
|
+
{ data_type: $1, data_length: $3.to_i, in_out: "IN/OUT" }
|
51
|
+
when /^(CLOB|NCLOB|BLOB)$/,
|
52
|
+
/^(NUMBER)(\(.*\))?$/, /^(NATURAL|NATURALN|POSITIVE|POSITIVEN|SIGNTYPE|SIMPLE_INTEGER|PLS_INTEGER|BINARY_INTEGER)$/,
|
53
|
+
/^(DATE|TIMESTAMP|TIMESTAMP WITH TIME ZONE|TIMESTAMP WITH LOCAL TIME ZONE)$/,
|
54
|
+
/^(XMLTYPE)$/
|
55
|
+
{ data_type: $1, in_out: "IN/OUT" }
|
56
|
+
when /^INTEGER$/
|
57
|
+
{ data_type: "NUMBER", in_out: "IN/OUT" }
|
58
|
+
when /^BOOLEAN$/
|
59
|
+
{ data_type: "PL/SQL BOOLEAN", in_out: "IN/OUT" }
|
60
|
+
when /^(\w+\.)?(\w+)\.(\w+)%TYPE$/
|
61
|
+
schema = $1 ? plsql.send($1.chop) : plsql
|
62
|
+
table = schema.send($2.downcase.to_sym)
|
63
|
+
column = table.columns[$3.downcase.to_sym]
|
64
|
+
{ data_type: column[:data_type], data_length: column[:data_length], sql_type_name: column[:sql_type_name], in_out: "IN/OUT" }
|
65
|
+
when /^(\w+\.)?(\w+)$/
|
66
|
+
schema = $1 ? @schema.root_schema.send($1.chop) : @schema
|
67
|
+
begin
|
68
|
+
type = schema.send($2.downcase.to_sym)
|
69
|
+
raise ArgumentError unless type.is_a?(PLSQL::Type)
|
70
|
+
typecode = case type.typecode
|
71
|
+
when "COLLECTION" then "TABLE"
|
72
|
+
else "OBJECT"
|
73
|
+
end
|
74
|
+
{ data_type: typecode, data_length: nil, sql_type_name: "#{type.schema_name}.#{type.type_name}", in_out: "IN/OUT" }
|
75
|
+
rescue ArgumentError
|
76
|
+
raise ArgumentError, "Package variable data type #{type_string} is not object type defined in schema"
|
73
77
|
end
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
record_metadata
|
88
|
-
|
89
|
-
|
78
|
+
when /^(\w+\.)?(\w+)%ROWTYPE$/
|
79
|
+
schema = $1 ? plsql.send($1.chop) : plsql
|
80
|
+
table = schema.send($2.downcase.to_sym)
|
81
|
+
record_metadata = {
|
82
|
+
data_type: "PL/SQL RECORD",
|
83
|
+
in_out: "IN/OUT",
|
84
|
+
fields: {}
|
85
|
+
}
|
86
|
+
table.columns.each do |name, col|
|
87
|
+
record_metadata[:fields][name] =
|
88
|
+
{ data_type: col[:data_type], data_length: col[:data_length], sql_type_name: col[:sql_type_name],
|
89
|
+
position: col[:position], in_out: "IN/OUT" }
|
90
|
+
end
|
91
|
+
record_metadata
|
92
|
+
else
|
93
|
+
raise ArgumentError, "Package variable data type #{type_string} is not supported"
|
90
94
|
end
|
91
|
-
record_metadata
|
92
|
-
else
|
93
|
-
raise ArgumentError, "Package variable data type #{type_string} is not supported"
|
94
95
|
end
|
95
|
-
end
|
96
|
-
|
97
|
-
# wrapper class to simulate Procedure class for ProcedureClass#exec
|
98
|
-
class VariableProcedure #:nodoc:
|
99
|
-
attr_reader :arguments, :argument_list, :return, :out_list, :schema
|
100
96
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
@
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
97
|
+
# wrapper class to simulate Procedure class for ProcedureClass#exec
|
98
|
+
class VariableProcedure #:nodoc:
|
99
|
+
attr_reader :arguments, :argument_list, :return, :out_list, :schema
|
100
|
+
|
101
|
+
def initialize(schema, variable, operation, metadata)
|
102
|
+
@schema = schema
|
103
|
+
@variable = variable
|
104
|
+
@operation = operation
|
105
|
+
@metadata = metadata
|
106
|
+
|
107
|
+
@out_list = [[]]
|
108
|
+
|
109
|
+
case @operation
|
110
|
+
when :get
|
111
|
+
@argument_list = [[]]
|
112
|
+
@arguments = [{}]
|
113
|
+
@return = [@metadata]
|
114
|
+
when :set
|
115
|
+
@argument_list = [[:value]]
|
116
|
+
@arguments = [{ value: @metadata }]
|
117
|
+
@return = [nil]
|
118
|
+
end
|
118
119
|
end
|
119
120
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
false
|
124
|
-
end
|
121
|
+
def overloaded?
|
122
|
+
false
|
123
|
+
end
|
125
124
|
|
126
|
-
|
127
|
-
|
128
|
-
|
125
|
+
def procedure
|
126
|
+
nil
|
127
|
+
end
|
129
128
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
129
|
+
def call_sql(params_string)
|
130
|
+
sql = (schema_name = @variable.schema_name) ? "#{schema_name}." : ""
|
131
|
+
sql << "#{@variable.package_name}.#{@variable.variable_name}"
|
132
|
+
case @operation
|
133
|
+
when :get
|
134
|
+
# params string contains assignment to return variable
|
135
|
+
"#{params_string} #{sql};\n"
|
136
|
+
when :set
|
137
|
+
"#{sql} := #{params_string};\n"
|
138
|
+
end
|
139
139
|
end
|
140
140
|
end
|
141
|
-
|
142
|
-
end
|
143
|
-
|
144
141
|
end
|
145
|
-
|
146
|
-
end
|
142
|
+
end
|
data/lib/plsql/version.rb
CHANGED
data/lib/plsql/view.rb
CHANGED
@@ -1,28 +1,27 @@
|
|
1
1
|
module PLSQL
|
2
|
-
|
3
2
|
module ViewClassMethods #:nodoc:
|
4
3
|
def find(schema, view)
|
5
4
|
if schema.select_first(
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
"SELECT view_name FROM all_views
|
6
|
+
WHERE owner = :owner
|
7
|
+
AND view_name = :view_name",
|
9
8
|
schema.schema_name, view.to_s.upcase)
|
10
9
|
new(schema, view)
|
11
10
|
# search for synonym
|
12
11
|
elsif (row = schema.select_first(
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
12
|
+
"SELECT v.owner, v.view_name
|
13
|
+
FROM all_synonyms s, all_views v
|
14
|
+
WHERE s.owner = :owner
|
15
|
+
AND s.synonym_name = :synonym_name
|
16
|
+
AND v.owner = s.table_owner
|
17
|
+
AND v.view_name = s.table_name
|
18
|
+
UNION ALL
|
19
|
+
SELECT v.owner, v.view_name
|
20
|
+
FROM all_synonyms s, all_views v
|
21
|
+
WHERE s.owner = 'PUBLIC'
|
22
|
+
AND s.synonym_name = :synonym_name
|
23
|
+
AND v.owner = s.table_owner
|
24
|
+
AND v.view_name = s.table_name",
|
26
25
|
schema.schema_name, view.to_s.upcase, view.to_s.upcase))
|
27
26
|
new(schema, row[1], row[0])
|
28
27
|
else
|
@@ -35,7 +34,5 @@ module PLSQL
|
|
35
34
|
extend ViewClassMethods
|
36
35
|
|
37
36
|
alias :view_name :table_name #:nodoc:
|
38
|
-
|
39
37
|
end
|
40
|
-
|
41
38
|
end
|