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/lib/plsql/table.rb CHANGED
@@ -12,12 +12,18 @@ module PLSQL
12
12
  elsif (row = schema.select_first(
13
13
  "SELECT t.owner, t.table_name
14
14
  FROM all_synonyms s, all_tables t
15
- WHERE s.owner IN (:owner, 'PUBLIC')
15
+ WHERE s.owner = :owner
16
16
  AND s.synonym_name = :synonym_name
17
17
  AND t.owner = s.table_owner
18
18
  AND t.table_name = s.table_name
19
- ORDER BY DECODE(s.owner, 'PUBLIC', 1, 0)",
20
- schema.schema_name, table.to_s.upcase))
19
+ UNION ALL
20
+ SELECT t.owner, t.table_name
21
+ FROM all_synonyms s, all_tables t
22
+ WHERE s.owner = 'PUBLIC'
23
+ AND s.synonym_name = :synonym_name
24
+ AND t.owner = s.table_owner
25
+ AND t.table_name = s.table_name",
26
+ schema.schema_name, table.to_s.upcase, table.to_s.upcase))
21
27
  new(schema, row[1], row[0])
22
28
  else
23
29
  nil
@@ -36,16 +42,17 @@ module PLSQL
36
42
  @table_name = table.to_s.upcase
37
43
  @columns = {}
38
44
 
39
- @schema.connection.select_all("
40
- SELECT c.column_name, c.column_id position,
45
+ @schema.select_all(
46
+ "SELECT c.column_name, c.column_id position,
41
47
  c.data_type, c.data_length, c.data_precision, c.data_scale, c.char_used,
42
- c.data_type_owner, c.data_type_mod, t.typecode
43
- FROM all_tab_columns c, all_types t
48
+ c.data_type_owner, c.data_type_mod,
49
+ CASE WHEN c.data_type_owner IS NULL THEN NULL
50
+ ELSE (SELECT t.typecode FROM all_types t
51
+ WHERE t.owner = c.data_type_owner
52
+ AND t.type_name = c.data_type) END typecode
53
+ FROM all_tab_columns c
44
54
  WHERE c.owner = :owner
45
- AND c.table_name = :table_name
46
- AND t.owner(+) = c.data_type_owner
47
- AND t.type_name(+) = c.data_type
48
- ORDER BY c.column_id",
55
+ AND c.table_name = :table_name",
49
56
  @schema_name, @table_name
50
57
  ) do |r|
51
58
  column_name, position,
@@ -65,6 +72,11 @@ module PLSQL
65
72
  end
66
73
  end
67
74
 
75
+ # list of table column names
76
+ def column_names
77
+ @column_names ||= @columns.keys.sort_by{|k| columns[k][:position]}
78
+ end
79
+
68
80
  # General select method with :first, :all or :count as first parameter.
69
81
  # It is recommended to use #first, #all or #count method instead of this one.
70
82
  def select(first_or_all, sql_params='', *bindvars)
@@ -87,6 +99,8 @@ module PLSQL
87
99
  sql_params.each do |k,v|
88
100
  if k == :order_by
89
101
  order_by_sql = "ORDER BY #{v} "
102
+ elsif v.nil?
103
+ where_sqls << "#{k} IS NULL"
90
104
  else
91
105
  where_sqls << "#{k} = :#{k}"
92
106
  bindvars << v
@@ -150,10 +164,44 @@ module PLSQL
150
164
  return nil
151
165
  end
152
166
 
153
- call = ProcedureCall.new(TableProcedure.new(@schema, self, :insert), [record])
167
+ table_proc = TableProcedure.new(@schema, self, :insert)
168
+ table_proc.add_insert_arguments(record)
169
+
170
+ call = ProcedureCall.new(table_proc, table_proc.argument_values)
154
171
  call.exec
155
172
  end
156
173
 
174
+ # Insert record or records in table using array of values. Examples:
175
+ #
176
+ # # with values for all columns
177
+ # plsql.employees.insert_values [1, 'First', 'Last', Time.local(2000,01,31)]
178
+ # # => INSERT INTO employees VALUES (1, 'First', 'Last', ...)
179
+ #
180
+ # # with values for specified columns
181
+ # plsql.employees.insert_values [:employee_id, :first_name, :last_name], [1, 'First', 'Last']
182
+ # # => INSERT INTO employees (employee_id, first_name, last_name) VALUES (1, 'First', 'Last')
183
+ #
184
+ # # with values for many records
185
+ # plsql.employees.insert_values [:employee_id, :first_name, :last_name], [1, 'First', 'Last'], [2, 'Second', 'Last']
186
+ # # => INSERT INTO employees (employee_id, first_name, last_name) VALUES (1, 'First', 'Last')
187
+ # # => INSERT INTO employees (employee_id, first_name, last_name) VALUES (2, 'Second', 'Last')
188
+ #
189
+ def insert_values(*args)
190
+ raise ArgumentError, "no arguments given" unless args.first
191
+ # if first argument is array of symbols then use it as list of fields
192
+ if args.first.all?{|a| a.instance_of?(Symbol)}
193
+ fields = args.shift
194
+ # otherwise use all columns as list of fields
195
+ else
196
+ fields = column_names
197
+ end
198
+ args.each do |record|
199
+ raise ArgumentError, "record should be Array of values" unless record.is_a?(Array)
200
+ raise ArgumentError, "wrong number of column values" unless record.size == fields.size
201
+ insert(ArrayHelpers::to_hash(fields, record))
202
+ end
203
+ end
204
+
157
205
  # Update table records using optional conditions. Example:
158
206
  #
159
207
  # plsql.employees.update(:first_name => 'Second', :where => {:employee_id => 1})
@@ -194,12 +242,6 @@ module PLSQL
194
242
  @schema.execute(delete_sql, *bindvars)
195
243
  end
196
244
 
197
- private
198
-
199
- def get_typecode(owner, type_name)
200
-
201
- end
202
-
203
245
  # wrapper class to simulate Procedure class for ProcedureClass#exec
204
246
  class TableProcedure #:nodoc:
205
247
  attr_reader :arguments, :argument_list, :return, :out_list, :schema
@@ -214,11 +256,10 @@ module PLSQL
214
256
 
215
257
  case @operation
216
258
  when :insert
217
- @argument_list = [[:p_record]]
218
- @arguments = [{:p_record => {
219
- :data_type => 'PL/SQL RECORD',
220
- :fields => @table.columns
221
- }}]
259
+ @argument_list = [[]]
260
+ @arguments = [{}]
261
+ @insert_columns = []
262
+ @insert_values = []
222
263
  when :update
223
264
  @argument_list = [[]]
224
265
  @arguments = [{}]
@@ -237,6 +278,15 @@ module PLSQL
237
278
  nil
238
279
  end
239
280
 
281
+ def add_insert_arguments(params)
282
+ params.each do |k,v|
283
+ raise ArgumentError, "Invalid column name #{k.inspect} specified as argument" unless (column_metadata = @table.columns[k])
284
+ @argument_list[0] << k
285
+ @arguments[0][k] = column_metadata
286
+ @insert_values << v
287
+ end
288
+ end
289
+
240
290
  def add_set_arguments(params)
241
291
  params.each do |k,v|
242
292
  raise ArgumentError, "Invalid column name #{k.inspect} specified as argument" unless (column_metadata = @table.columns[k])
@@ -263,13 +313,18 @@ module PLSQL
263
313
  end
264
314
 
265
315
  def argument_values
266
- @set_values + @where_values
316
+ case @operation
317
+ when :insert
318
+ @insert_values
319
+ when :update
320
+ @set_values + @where_values
321
+ end
267
322
  end
268
323
 
269
324
  def call_sql(params_string)
270
325
  case @operation
271
326
  when :insert
272
- "INSERT INTO \"#{@table.schema_name}\".\"#{@table.table_name}\" VALUES #{params_string};\n"
327
+ "INSERT INTO \"#{@table.schema_name}\".\"#{@table.table_name}\"(#{@argument_list[0].map{|a| a.to_s}.join(', ')}) VALUES (#{params_string});\n"
273
328
  when :update
274
329
  update_sql = "UPDATE \"#{@table.schema_name}\".\"#{@table.table_name}\" SET #{@set_sqls.join(', ')}"
275
330
  update_sql << " WHERE #{@where_sqls.join(' AND ')}" unless @where_sqls.empty?
data/lib/plsql/type.rb ADDED
@@ -0,0 +1,87 @@
1
+ module PLSQL
2
+
3
+ module TypeClassMethods #:nodoc:
4
+ def find(schema, type)
5
+ if schema.select_first(
6
+ "SELECT type_name FROM all_types
7
+ WHERE owner = :owner
8
+ AND type_name = :table_name",
9
+ schema.schema_name, type.to_s.upcase)
10
+ new(schema, type)
11
+ # search for synonym
12
+ elsif (row = schema.select_first(
13
+ "SELECT t.owner, t.type_name
14
+ FROM all_synonyms s, all_types t
15
+ WHERE s.owner = :owner
16
+ AND s.synonym_name = :synonym_name
17
+ AND t.owner = s.table_owner
18
+ AND t.type_name = s.table_name
19
+ UNION ALL
20
+ SELECT t.owner, t.type_name
21
+ FROM all_synonyms s, all_types t
22
+ WHERE s.owner = 'PUBLIC'
23
+ AND s.synonym_name = :synonym_name
24
+ AND t.owner = s.table_owner
25
+ AND t.type_name = s.table_name",
26
+ schema.schema_name, type.to_s.upcase, type.to_s.upcase))
27
+ new(schema, row[1], row[0])
28
+ else
29
+ nil
30
+ end
31
+ end
32
+ end
33
+
34
+ class Type
35
+ extend TypeClassMethods
36
+
37
+ attr_reader :typecode, :attributes, :schema_name, :type_name #:nodoc:
38
+
39
+ def initialize(schema, type, override_schema_name = nil) #:nodoc:
40
+ @schema = schema
41
+ @schema_name = override_schema_name || schema.schema_name
42
+ @type_name = type.to_s.upcase
43
+ @attributes = {}
44
+
45
+ @typecode = @schema.select_first(
46
+ "SELECT typecode FROM all_types
47
+ WHERE owner = :owner
48
+ AND type_name = :type_name",
49
+ @schema_name, @type_name)[0]
50
+
51
+ @schema.select_all(
52
+ "SELECT attr_name, attr_no,
53
+ attr_type_name, length, precision, scale,
54
+ attr_type_owner, attr_type_mod,
55
+ (SELECT t.typecode FROM all_types t
56
+ WHERE t.owner = attr_type_owner
57
+ AND t.type_name = attr_type_name) typecode
58
+ FROM all_type_attrs
59
+ WHERE owner = :owner
60
+ AND type_name = :type_name
61
+ ORDER BY attr_no",
62
+ @schema_name, @type_name
63
+ ) do |r|
64
+ attr_name, position,
65
+ data_type, data_length, data_precision, data_scale,
66
+ data_type_owner, data_type_mod, typecode = r
67
+ @attributes[attr_name.downcase.to_sym] = {
68
+ :position => position && position.to_i,
69
+ :data_type => data_type_owner && (typecode == 'COLLECTION' ? 'TABLE' : 'OBJECT' ) || data_type,
70
+ :data_length => data_type_owner ? nil : data_length && data_length.to_i,
71
+ :data_precision => data_precision && data_precision.to_i,
72
+ :data_scale => data_scale && data_scale.to_i,
73
+ :type_owner => data_type_owner,
74
+ :type_name => data_type_owner && data_type,
75
+ :sql_type_name => data_type_owner && "#{data_type_owner}.#{data_type}"
76
+ }
77
+ end
78
+ end
79
+
80
+ # list of object type attribute names
81
+ def attribute_names
82
+ @attribute_names ||= @attributes.keys.sort_by{|k| @attributes[k][:position]}
83
+ end
84
+
85
+ end
86
+
87
+ end
@@ -0,0 +1,146 @@
1
+ module PLSQL
2
+
3
+ module VariableClassMethods #:nodoc:
4
+ def find(schema, variable, package, override_schema_name = nil)
5
+ variable_upcase = variable.to_s.upcase
6
+ schema.select_all(
7
+ "SELECT text FROM all_source
8
+ WHERE owner = :owner
9
+ AND name = :object_name
10
+ AND type = 'PACKAGE'
11
+ AND UPPER(text) LIKE :variable_name",
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_. %]+(\([0-9,]+\))?)\s*(NOT\s+NULL)?\s*((:=|DEFAULT).*)?;\s*(--.*)?$/i
14
+ return new(schema, variable, package, $2.strip, override_schema_name)
15
+ end
16
+ end
17
+ nil
18
+ end
19
+ end
20
+
21
+ class Variable #:nodoc:
22
+ extend VariableClassMethods
23
+
24
+ attr_reader :schema_name, :package_name, :variable_name #:nodoc:
25
+
26
+ def initialize(schema, variable, package, variable_type, override_schema_name = nil)
27
+ @schema = schema
28
+ @schema_name = override_schema_name || schema.schema_name
29
+ @variable_name = variable.to_s.upcase
30
+ @package_name = package
31
+ @variable_type = variable_type.upcase
32
+ @metadata = metadata(@variable_type)
33
+ end
34
+
35
+ def value
36
+ @variable_get_proc ||= VariableProcedure.new(@schema, self, :get, @metadata)
37
+ ProcedureCall.new(@variable_get_proc).exec
38
+ end
39
+
40
+ def value=(new_value)
41
+ @variable_set_proc ||= VariableProcedure.new(@schema, self, :set, @metadata)
42
+ ProcedureCall.new(@variable_set_proc, [new_value]).exec
43
+ new_value
44
+ end
45
+
46
+ private
47
+
48
+ def metadata(type_string)
49
+ case type_string
50
+ when /^(VARCHAR2|CHAR|NVARCHAR2|NCHAR)(\((\d+)\))?$/
51
+ {:data_type => $1, :data_length => $3.to_i, :in_out => 'IN/OUT'}
52
+ when /^(CLOB|NCLOB|BLOB)$/,
53
+ /^(NUMBER)(\(.*\))?$/, /^(PLS_INTEGER|BINARY_INTEGER)$/,
54
+ /^(DATE|TIMESTAMP|TIMESTAMP WITH TIME ZONE|TIMESTAMP WITH LOCAL TIME ZONE)$/
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 ? plsql.send($1.chop) : plsql
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"
77
+ end
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, column|
87
+ record_metadata[:fields][name] =
88
+ {:data_type => column[:data_type], :data_length => column[:data_length], :sql_type_name => column[:sql_type_name],
89
+ :position => column[:position], :in_out => 'IN/OUT'}
90
+ end
91
+ record_metadata
92
+ else
93
+ raise ArgumentError, "Package variable data type #{type_string} is not supported"
94
+ 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
+
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
119
+
120
+ end
121
+
122
+ def overloaded?
123
+ false
124
+ end
125
+
126
+ def procedure
127
+ nil
128
+ end
129
+
130
+ def call_sql(params_string)
131
+ sql = (schema_name = @variable.schema_name) ? "#{schema_name}." : ""
132
+ sql << "#{@variable.package_name}.#{@variable.variable_name}"
133
+ case @operation
134
+ when :get
135
+ # params string contains assignment to return variable
136
+ "#{params_string} #{sql};\n"
137
+ when :set
138
+ "#{sql} := #{params_string};\n"
139
+ end
140
+ end
141
+
142
+ end
143
+
144
+ end
145
+
146
+ end
data/lib/plsql/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module PLSQL #:nodoc:
2
- VERSION = '0.4.0'
2
+ VERSION = File.read(File.dirname(__FILE__)+'/../../VERSION').chomp
3
3
  end
data/lib/plsql/view.rb ADDED
@@ -0,0 +1,41 @@
1
+ module PLSQL
2
+
3
+ module ViewClassMethods #:nodoc:
4
+ def find(schema, view)
5
+ if schema.select_first(
6
+ "SELECT view_name FROM all_views
7
+ WHERE owner = :owner
8
+ AND view_name = :view_name",
9
+ schema.schema_name, view.to_s.upcase)
10
+ new(schema, view)
11
+ # search for synonym
12
+ elsif (row = schema.select_first(
13
+ "SELECT v.owner, v.view_name
14
+ FROM all_synonyms s, all_views v
15
+ WHERE s.owner = :owner
16
+ AND s.synonym_name = :synonym_name
17
+ AND v.owner = s.table_owner
18
+ AND v.view_name = s.table_name
19
+ UNION ALL
20
+ SELECT v.owner, v.view_name
21
+ FROM all_synonyms s, all_views v
22
+ WHERE s.owner = 'PUBLIC'
23
+ AND s.synonym_name = :synonym_name
24
+ AND v.owner = s.table_owner
25
+ AND v.view_name = s.table_name",
26
+ schema.schema_name, view.to_s.upcase, view.to_s.upcase))
27
+ new(schema, row[1], row[0])
28
+ else
29
+ nil
30
+ end
31
+ end
32
+ end
33
+
34
+ class View < Table
35
+ extend ViewClassMethods
36
+
37
+ alias :view_name :table_name #:nodoc:
38
+
39
+ end
40
+
41
+ end