ruby-plsql 0.3.1 → 0.4.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.
@@ -0,0 +1,49 @@
1
+ module PLSQL
2
+
3
+ module SequenceClassMethods #:nodoc:
4
+ def find(schema, sequence)
5
+ if schema.select_first(
6
+ "SELECT sequence_name FROM all_sequences
7
+ WHERE sequence_owner = :owner
8
+ AND sequence_name = :sequence_name",
9
+ schema.schema_name, sequence.to_s.upcase)
10
+ new(schema, sequence)
11
+ # search for synonym
12
+ elsif (row = schema.select_first(
13
+ "SELECT t.sequence_owner, t.sequence_name
14
+ FROM all_synonyms s, all_sequences t
15
+ WHERE s.owner IN (:owner, 'PUBLIC')
16
+ AND s.synonym_name = :synonym_name
17
+ AND t.sequence_owner = s.table_owner
18
+ AND t.sequence_name = s.table_name
19
+ ORDER BY DECODE(s.owner, 'PUBLIC', 1, 0)",
20
+ schema.schema_name, sequence.to_s.upcase))
21
+ new(schema, row[1], row[0])
22
+ else
23
+ nil
24
+ end
25
+ end
26
+ end
27
+
28
+ class Sequence
29
+ extend SequenceClassMethods
30
+
31
+ def initialize(schema, sequence, override_schema_name = nil) #:nodoc:
32
+ @schema = schema
33
+ @schema_name = override_schema_name || schema.schema_name
34
+ @sequence_name = sequence.to_s.upcase
35
+ end
36
+
37
+ # Get NEXTVAL of sequence
38
+ def nextval
39
+ @schema.select_one "SELECT \"#{@schema_name}\".\"#{@sequence_name}\".NEXTVAL FROM dual"
40
+ end
41
+
42
+ # Get CURRTVAL of sequence (can be called just after nextval)
43
+ def currval
44
+ @schema.select_one "SELECT \"#{@schema_name}\".\"#{@sequence_name}\".CURRVAL FROM dual"
45
+ end
46
+
47
+ end
48
+
49
+ end
@@ -0,0 +1,61 @@
1
+ module PLSQL
2
+ module SQLStatements
3
+ # Select first row as array or values (without column names)
4
+ def select_first(sql, *bindvars)
5
+ @connection.select_first(sql, *bindvars)
6
+ end
7
+
8
+ # Select one value (use if only one row with one value is selected)
9
+ def select_one(sql, *bindvars)
10
+ (row = @connection.select_first(sql, *bindvars)) && row[0]
11
+ end
12
+
13
+ # Select :first or :all values. Examples:
14
+ #
15
+ # plsql.select :first, "SELECT * FROM employees WHERE employee_id = :1", 1
16
+ # plsql.select :all, "SELECT * FROM employees ORDER BY employee_id"
17
+ def select(*args)
18
+ case args[0]
19
+ when nil
20
+ raise ArgumentError, "Not enough arguments"
21
+ when :first
22
+ args.shift
23
+ @connection.select_hash_first(*args)
24
+ when :all
25
+ args.shift
26
+ @connection.select_hash_all(*args)
27
+ else
28
+ @connection.select_hash_all(*args)
29
+ end
30
+ end
31
+
32
+ # Execute SQL statement. Example:
33
+ #
34
+ # plsql.execute "DROP TABLE employees"
35
+ def execute(*args)
36
+ @connection.exec(*args)
37
+ end
38
+
39
+ # Execute COMMIT in current database session.
40
+ # Use beforehand
41
+ #
42
+ # plsql.connection.autocommit = false
43
+ #
44
+ # to turn off automatic commits after each statement.
45
+ def commit
46
+ connection.commit
47
+ end
48
+
49
+ # Execute ROLLBACK in current database session.
50
+ # Use beforehand
51
+ #
52
+ # plsql.connection.autocommit = false
53
+ #
54
+ # to turn off automatic commits after each statement.
55
+ def rollback
56
+ connection.rollback
57
+ end
58
+
59
+ end
60
+ end
61
+
@@ -0,0 +1,285 @@
1
+ module PLSQL
2
+
3
+ module TableClassMethods #:nodoc:
4
+ def find(schema, table)
5
+ if schema.select_first(
6
+ "SELECT table_name FROM all_tables
7
+ WHERE owner = :owner
8
+ AND table_name = :table_name",
9
+ schema.schema_name, table.to_s.upcase)
10
+ new(schema, table)
11
+ # search for synonym
12
+ elsif (row = schema.select_first(
13
+ "SELECT t.owner, t.table_name
14
+ FROM all_synonyms s, all_tables t
15
+ WHERE s.owner IN (:owner, 'PUBLIC')
16
+ AND s.synonym_name = :synonym_name
17
+ AND t.owner = s.table_owner
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))
21
+ new(schema, row[1], row[0])
22
+ else
23
+ nil
24
+ end
25
+ end
26
+ end
27
+
28
+ class Table
29
+ extend TableClassMethods
30
+
31
+ attr_reader :columns, :schema_name, :table_name #:nodoc:
32
+
33
+ def initialize(schema, table, override_schema_name = nil) #:nodoc:
34
+ @schema = schema
35
+ @schema_name = override_schema_name || schema.schema_name
36
+ @table_name = table.to_s.upcase
37
+ @columns = {}
38
+
39
+ @schema.connection.select_all("
40
+ SELECT c.column_name, c.column_id position,
41
+ 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
44
+ 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",
49
+ @schema_name, @table_name
50
+ ) do |r|
51
+ column_name, position,
52
+ data_type, data_length, data_precision, data_scale, char_used,
53
+ data_type_owner, data_type_mod, typecode = r
54
+ @columns[column_name.downcase.to_sym] = {
55
+ :position => position && position.to_i,
56
+ :data_type => data_type_owner && (typecode == 'COLLECTION' ? 'TABLE' : 'OBJECT' ) || data_type,
57
+ :data_length => data_type_owner ? nil : data_length && data_length.to_i,
58
+ :data_precision => data_precision && data_precision.to_i,
59
+ :data_scale => data_scale && data_scale.to_i,
60
+ :char_used => char_used,
61
+ :type_owner => data_type_owner,
62
+ :type_name => data_type_owner && data_type,
63
+ :sql_type_name => data_type_owner && "#{data_type_owner}.#{data_type}"
64
+ }
65
+ end
66
+ end
67
+
68
+ # General select method with :first, :all or :count as first parameter.
69
+ # It is recommended to use #first, #all or #count method instead of this one.
70
+ def select(first_or_all, sql_params='', *bindvars)
71
+ case first_or_all
72
+ when :first, :all
73
+ select_sql = "SELECT * "
74
+ when :count
75
+ select_sql = "SELECT COUNT(*) "
76
+ else
77
+ raise ArgumentError, "Only :first, :all or :count are supported"
78
+ end
79
+ select_sql << "FROM \"#{@schema_name}\".\"#{@table_name}\" "
80
+ case sql_params
81
+ when String
82
+ select_sql << sql_params
83
+ when Hash
84
+ raise ArgumentError, "Cannot specify bind variables when passing WHERE conditions as Hash" unless bindvars.empty?
85
+ where_sqls = []
86
+ order_by_sql = nil
87
+ sql_params.each do |k,v|
88
+ if k == :order_by
89
+ order_by_sql = "ORDER BY #{v} "
90
+ else
91
+ where_sqls << "#{k} = :#{k}"
92
+ bindvars << v
93
+ end
94
+ end
95
+ select_sql << "WHERE " << where_sqls.join(' AND ') unless where_sqls.empty?
96
+ select_sql << order_by_sql if order_by_sql
97
+ else
98
+ raise ArgumentError, "Only String or Hash can be provided as SQL condition argument"
99
+ end
100
+ if first_or_all == :count
101
+ @schema.select_one(select_sql, *bindvars)
102
+ else
103
+ @schema.select(first_or_all, select_sql, *bindvars)
104
+ end
105
+ end
106
+
107
+ # Select all table records using optional conditions. Examples:
108
+ #
109
+ # plsql.employees.all
110
+ # plsql.employees.all(:order_by => :employee_id)
111
+ # plsql.employees.all("WHERE employee_id > :employee_id", 5)
112
+ #
113
+ def all(sql='', *bindvars)
114
+ select(:all, sql, *bindvars)
115
+ end
116
+
117
+ # Select first table record using optional conditions. Examples:
118
+ #
119
+ # plsql.employees.first
120
+ # plsql.employees.first(:employee_id => 1)
121
+ # plsql.employees.first("WHERE employee_id = 1")
122
+ # plsql.employees.first("WHERE employee_id = :employee_id", 1)
123
+ #
124
+ def first(sql='', *bindvars)
125
+ select(:first, sql, *bindvars)
126
+ end
127
+
128
+ # Count table records using optional conditions. Examples:
129
+ #
130
+ # plsql.employees.count
131
+ # plsql.employees.count("WHERE employee_id > :employee_id", 5)
132
+ #
133
+ def count(sql='', *bindvars)
134
+ select(:count, sql, *bindvars)
135
+ end
136
+
137
+ # Insert record or records in table. Examples:
138
+ #
139
+ # employee = { :employee_id => 1, :first_name => 'First', :last_name => 'Last', :hire_date => Time.local(2000,01,31) }
140
+ # plsql.employees.insert employee
141
+ # # => INSERT INTO employees VALUES (1, 'First', 'Last', ...)
142
+ #
143
+ # employees = [employee1, employee2, ... ] # array of many Hashes
144
+ # plsql.employees.insert employees
145
+ #
146
+ def insert(record)
147
+ # if Array of records is passed then insert each individually
148
+ if record.is_a?(Array)
149
+ record.each {|r| insert(r)}
150
+ return nil
151
+ end
152
+
153
+ call = ProcedureCall.new(TableProcedure.new(@schema, self, :insert), [record])
154
+ call.exec
155
+ end
156
+
157
+ # Update table records using optional conditions. Example:
158
+ #
159
+ # plsql.employees.update(:first_name => 'Second', :where => {:employee_id => 1})
160
+ # # => UPDATE employees SET first_name = 'Second' WHERE employee_id = 1
161
+ #
162
+ def update(params)
163
+ raise ArgumentError, "Only Hash parameter can be passed to table update method" unless params.is_a?(Hash)
164
+ where = params.delete(:where)
165
+
166
+ table_proc = TableProcedure.new(@schema, self, :update)
167
+ table_proc.add_set_arguments(params)
168
+ table_proc.add_where_arguments(where) if where
169
+ call = ProcedureCall.new(table_proc, table_proc.argument_values)
170
+ call.exec
171
+ end
172
+
173
+ # Delete table records using optional conditions. Example:
174
+ #
175
+ # plsql.employees.delete(:employee_id => 1)
176
+ # # => DELETE FROM employees WHERE employee_id = 1
177
+ #
178
+ def delete(sql_params='', *bindvars)
179
+ delete_sql = "DELETE FROM \"#{@schema_name}\".\"#{@table_name}\" "
180
+ case sql_params
181
+ when String
182
+ delete_sql << sql_params
183
+ when Hash
184
+ raise ArgumentError, "Cannot specify bind variables when passing WHERE conditions as Hash" unless bindvars.empty?
185
+ where_sqls = []
186
+ sql_params.each do |k,v|
187
+ where_sqls << "#{k} = :#{k}"
188
+ bindvars << v
189
+ end
190
+ delete_sql << "WHERE " << where_sqls.join(' AND ') unless where_sqls.empty?
191
+ else
192
+ raise ArgumentError, "Only String or Hash can be provided as SQL condition argument"
193
+ end
194
+ @schema.execute(delete_sql, *bindvars)
195
+ end
196
+
197
+ private
198
+
199
+ def get_typecode(owner, type_name)
200
+
201
+ end
202
+
203
+ # wrapper class to simulate Procedure class for ProcedureClass#exec
204
+ class TableProcedure #:nodoc:
205
+ attr_reader :arguments, :argument_list, :return, :out_list, :schema
206
+
207
+ def initialize(schema, table, operation)
208
+ @schema = schema
209
+ @table = table
210
+ @operation = operation
211
+
212
+ @return = [nil]
213
+ @out_list = [[]]
214
+
215
+ case @operation
216
+ when :insert
217
+ @argument_list = [[:p_record]]
218
+ @arguments = [{:p_record => {
219
+ :data_type => 'PL/SQL RECORD',
220
+ :fields => @table.columns
221
+ }}]
222
+ when :update
223
+ @argument_list = [[]]
224
+ @arguments = [{}]
225
+ @set_sqls = []
226
+ @set_values = []
227
+ @where_sqls = []
228
+ @where_values = []
229
+ end
230
+ end
231
+
232
+ def overloaded?
233
+ false
234
+ end
235
+
236
+ def procedure
237
+ nil
238
+ end
239
+
240
+ def add_set_arguments(params)
241
+ params.each do |k,v|
242
+ raise ArgumentError, "Invalid column name #{k.inspect} specified as argument" unless (column_metadata = @table.columns[k])
243
+ @argument_list[0] << k
244
+ @arguments[0][k] = column_metadata
245
+ @set_sqls << "#{k}=:#{k}"
246
+ @set_values << v
247
+ end
248
+ end
249
+
250
+ def add_where_arguments(params)
251
+ case params
252
+ when Hash
253
+ params.each do |k,v|
254
+ raise ArgumentError, "Invalid column name #{k.inspect} specified as argument" unless (column_metadata = @table.columns[k])
255
+ @argument_list[0] << :"w_#{k}"
256
+ @arguments[0][:"w_#{k}"] = column_metadata
257
+ @where_sqls << "#{k}=:w_#{k}"
258
+ @where_values << v
259
+ end
260
+ when String
261
+ @where_sqls << params
262
+ end
263
+ end
264
+
265
+ def argument_values
266
+ @set_values + @where_values
267
+ end
268
+
269
+ def call_sql(params_string)
270
+ case @operation
271
+ when :insert
272
+ "INSERT INTO \"#{@table.schema_name}\".\"#{@table.table_name}\" VALUES #{params_string};\n"
273
+ when :update
274
+ update_sql = "UPDATE \"#{@table.schema_name}\".\"#{@table.table_name}\" SET #{@set_sqls.join(', ')}"
275
+ update_sql << " WHERE #{@where_sqls.join(' AND ')}" unless @where_sqls.empty?
276
+ update_sql << ";\n"
277
+ update_sql
278
+ end
279
+ end
280
+
281
+ end
282
+
283
+ end
284
+
285
+ end
@@ -0,0 +1,3 @@
1
+ module PLSQL #:nodoc:
2
+ VERSION = '0.4.0'
3
+ end
@@ -0,0 +1 @@
1
+ require "ruby_plsql"
@@ -1,46 +1,13 @@
1
- $:.unshift File.dirname(__FILE__)
1
+ require "time"
2
+ require "date"
3
+ require "bigdecimal"
2
4
 
3
- module RubyPlsql #:nodoc:
5
+ %w(connection sql_statements schema procedure procedure_call package table sequence).each do |file|
6
+ require "plsql/#{file}"
4
7
  end
5
8
 
6
9
  unless defined?(JRUBY_VERSION)
7
- begin
8
- require "oci8"
9
- rescue LoadError
10
- puts <<-EOS
11
- To use ruby_plsql you must install ruby-oci8 library.
12
- EOS
13
- end
10
+ require "plsql/oci_connection"
14
11
  else
15
- begin
16
- require "java"
17
- require "jruby"
18
- # Adds JRuby classloader to current thread classloader - as a result ojdbc14.jar should not be in $JRUBY_HOME/lib
19
- java.lang.Thread.currentThread.setContextClassLoader(JRuby.runtime.jruby_class_loader)
20
-
21
- ojdbc_jar = "ojdbc14.jar"
22
- if ojdbc_jar_path = ENV["PATH"].split(/[:;]/).find{|d| File.exists?(File.join(d,ojdbc_jar))}
23
- require File.join(ojdbc_jar_path,ojdbc_jar)
24
- else
25
- require ojdbc_jar
26
- end
27
- # import java.sql.Statement
28
- # import java.sql.Connection
29
- # import java.sql.SQLException
30
- # import java.sql.Types
31
- # import java.sql.DriverManager
32
- java.sql.DriverManager.registerDriver Java::oracle.jdbc.driver.OracleDriver.new
33
- rescue LoadError
34
- puts <<-EOS
35
- To use ruby_plsql you must have Oracle JDBC driver installed.
36
- EOS
37
- end
38
- end
39
-
40
- require "time"
41
- require "date"
42
- require "bigdecimal"
43
-
44
- %w(connection oci_connection jdbc_connection schema procedure package).each do |file|
45
- require File.dirname(__FILE__) + "/plsql/#{file}"
12
+ require "plsql/jdbc_connection"
46
13
  end