ruby-plsql 0.4.0 → 0.4.1

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