ruby-plsql 0.3.1 → 0.4.0

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