ruby-plsql 0.5.3 → 0.8.0
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.
- 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
|