ruby-plsql 0.9.9-java

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,296 @@
1
+ module PLSQL
2
+ class Schema
3
+ include SQLStatements
4
+
5
+ @@schemas = {}
6
+
7
+ class << self
8
+ def find_or_new(connection_alias) # :nodoc:
9
+ connection_alias ||= :default
10
+ if @@schemas[connection_alias]
11
+ @@schemas[connection_alias]
12
+ else
13
+ @@schemas[connection_alias] = self.new
14
+ end
15
+ end
16
+ end
17
+
18
+ def initialize(raw_conn = nil, schema = nil, original_schema = nil) # :nodoc:
19
+ self.connection = raw_conn
20
+ @schema_name = schema ? schema.to_s.upcase : nil
21
+ @original_schema = original_schema
22
+ @dbms_output_stream = nil
23
+ end
24
+
25
+ # Returns connection wrapper object (this is not raw OCI8 or JDBC connection!)
26
+ attr_reader :connection
27
+
28
+ def root_schema # :nodoc:
29
+ @original_schema || self
30
+ end
31
+
32
+ def raw_connection=(raw_conn) # :nodoc:
33
+ @connection = raw_conn ? Connection.create(raw_conn) : nil
34
+ reset_instance_variables
35
+ end
36
+
37
+ # Set connection to OCI8 or JDBC connection:
38
+ #
39
+ # plsql.connection = OCI8.new(database_user, database_password, database_name)
40
+ #
41
+ # or
42
+ #
43
+ # plsql.connection = java.sql.DriverManager.getConnection(
44
+ # "jdbc:oracle:thin:@//#{database_host}:#{database_port}/#{database_service_name}",
45
+ # database_user, database_password)
46
+ #
47
+ def connection=(conn)
48
+ if conn.is_a?(Connection)
49
+ @connection = conn
50
+ reset_instance_variables
51
+ else
52
+ self.raw_connection = conn
53
+ end
54
+ conn
55
+ end
56
+
57
+ # Create new OCI8 or JDBC connection using one of the following ways:
58
+ #
59
+ # plsql.connect! username, password, database_tns_alias
60
+ # plsql.connect! username, password, :host => host, :port => port, :database => database
61
+ # plsql.connect! :username => username, :password => password, :database => database_tns_alias
62
+ # plsql.connect! :username => username, :password => password, :host => host, :port => port, :database => database
63
+ #
64
+ def connect!(*args)
65
+ params = {}
66
+ params[:username] = args.shift if args[0].is_a?(String)
67
+ params[:password] = args.shift if args[0].is_a?(String)
68
+ params[:database] = args.shift if args[0].is_a?(String)
69
+ params.merge!(args.shift) if args[0].is_a?(Hash)
70
+ raise ArgumentError, "Wrong number of arguments" unless args.empty?
71
+ self.connection = Connection.create_new(params)
72
+ end
73
+
74
+ # Set connection to current ActiveRecord connection (use in initializer file):
75
+ #
76
+ # plsql.activerecord_class = ActiveRecord::Base
77
+ #
78
+ def activerecord_class=(ar_class)
79
+ @connection = ar_class ? Connection.create(nil, ar_class) : nil
80
+ reset_instance_variables
81
+ ar_class
82
+ end
83
+
84
+ # Disconnect from Oracle
85
+ def logoff
86
+ @connection.logoff
87
+ self.connection = nil
88
+ end
89
+
90
+ # Current Oracle schema name
91
+ def schema_name
92
+ return nil unless connection
93
+ @schema_name ||= select_first("SELECT SYS_CONTEXT('userenv','current_schema') FROM dual")[0]
94
+ end
95
+
96
+ # Default timezone to which database values will be converted - :utc or :local
97
+ def default_timezone
98
+ if @original_schema
99
+ @original_schema.default_timezone
100
+ else
101
+ @default_timezone ||
102
+ # Use ActiveRecord default_timezone when ActiveRecord connection is used.
103
+ # Prefer the module-level accessor (AR 7.0+; the only one in AR 8.0+) so
104
+ # that AR 7.0/7.1's deprecation warning for ActiveRecord::Base.default_timezone
105
+ # is not emitted. Fall back to the per-class accessor only on pre-7.0 AR,
106
+ # where ActiveRecord.default_timezone does not exist.
107
+ (@connection && @connection.activerecord_class &&
108
+ (ActiveRecord.respond_to?(:default_timezone) ?
109
+ ActiveRecord.default_timezone :
110
+ @connection.activerecord_class.default_timezone)) ||
111
+ # default to local timezone
112
+ :local
113
+ end
114
+ end
115
+
116
+ # Set default timezone to which database values will be converted - :utc or :local
117
+ def default_timezone=(value)
118
+ if [:local, :utc].include?(value)
119
+ @default_timezone = value
120
+ else
121
+ raise ArgumentError, "default timezone should be :local or :utc"
122
+ end
123
+ end
124
+
125
+ # Same implementation as for ActiveRecord
126
+ # DateTimes aren't aware of DST rules, so use a consistent non-DST offset when creating a DateTime with an offset in the local zone
127
+ def local_timezone_offset # :nodoc:
128
+ ::Time.local(2007).utc_offset.to_r / 86400
129
+ end
130
+
131
+ # DBMS_OUTPUT buffer size (default is 20_000)
132
+ def dbms_output_buffer_size
133
+ if @original_schema
134
+ @original_schema.dbms_output_buffer_size
135
+ else
136
+ @dbms_output_buffer_size || 20_000
137
+ end
138
+ end
139
+
140
+ # Seet DBMS_OUTPUT buffer size (default is 20_000). Example:
141
+ #
142
+ # plsql.dbms_output_buffer_size = 100_000
143
+ #
144
+ def dbms_output_buffer_size=(value)
145
+ @dbms_output_buffer_size = value
146
+ end
147
+
148
+ # Maximum line numbers for DBMS_OUTPUT in one PL/SQL call (from DBMSOUTPUT_LINESARRAY type)
149
+ DBMS_OUTPUT_MAX_LINES = 2147483647
150
+
151
+ # Specify IO stream where to log DBMS_OUTPUT from PL/SQL procedures. Example:
152
+ #
153
+ # plsql.dbms_output_stream = STDOUT
154
+ #
155
+ def dbms_output_stream=(stream)
156
+ @dbms_output_stream = stream
157
+ if @dbms_output_stream.nil? && @connection
158
+ sys.dbms_output.disable
159
+ end
160
+ end
161
+
162
+ # IO stream where to log DBMS_OUTPUT from PL/SQL procedures.
163
+ def dbms_output_stream
164
+ if @original_schema
165
+ @original_schema.dbms_output_stream
166
+ else
167
+ @dbms_output_stream
168
+ end
169
+ end
170
+
171
+ private
172
+
173
+ def reset_instance_variables
174
+ if @connection
175
+ @schema_objects = {}
176
+ else
177
+ @schema_objects = nil
178
+ end
179
+ @schema_name = nil
180
+ @default_timezone = nil
181
+ end
182
+
183
+ def method_missing(method, *args, &block)
184
+ raise ArgumentError, "No database connection" unless connection
185
+ # search in database if not in cache at first
186
+ object = (@schema_objects[method] ||= find_database_object(method) || find_other_schema(method) ||
187
+ find_public_synonym(method) || find_standard_procedure(method))
188
+
189
+ raise ArgumentError, "No database object '#{method.to_s.upcase}' found" unless object
190
+
191
+ if object.is_a?(Procedure)
192
+ object.exec(*args, &block)
193
+ elsif object.is_a?(Type) && !args.empty?
194
+ object.new(*args, &block)
195
+ else
196
+ object
197
+ end
198
+ end
199
+
200
+ def find_database_object(name, override_schema_name = nil)
201
+ object_schema_name = override_schema_name || schema_name
202
+ object_name = name.to_s.upcase
203
+ if row = select_first(
204
+ "SELECT o.object_type, o.object_id
205
+ FROM all_objects o
206
+ WHERE owner = :owner AND object_name = :object_name
207
+ AND object_type IN ('PROCEDURE','FUNCTION','PACKAGE','TABLE','VIEW','SEQUENCE','TYPE','SYNONYM')",
208
+ object_schema_name, object_name)
209
+ object_type, object_id = row
210
+ case object_type
211
+ when "PROCEDURE", "FUNCTION"
212
+ if (connection.database_version <=> [11, 1, 0, 0]) >= 0
213
+ if row = select_first(
214
+ "SELECT p.object_id FROM all_procedures p
215
+ WHERE p.owner = :owner
216
+ AND p.object_name = :object_name
217
+ AND p.object_type = :object_type",
218
+ object_schema_name, object_name, object_type)
219
+ object_id = row[0]
220
+ else
221
+ raise ArgumentError, "Database object '#{object_schema_name}.#{object_name}' is not in valid status\n#{
222
+ _errors(object_schema_name, object_name, object_type)}"
223
+ end
224
+ end
225
+ Procedure.new(self, name, nil, override_schema_name, object_id)
226
+ when "PACKAGE"
227
+ Package.new(self, name, override_schema_name)
228
+ when "TABLE"
229
+ Table.new(self, name, override_schema_name)
230
+ when "VIEW"
231
+ View.new(self, name, override_schema_name)
232
+ when "SEQUENCE"
233
+ Sequence.new(self, name, override_schema_name)
234
+ when "TYPE"
235
+ Type.new(self, name, override_schema_name)
236
+ when "SYNONYM"
237
+ target_schema_name, target_object_name = @connection.describe_synonym(object_schema_name, object_name)
238
+ find_database_object(target_object_name, target_schema_name)
239
+ end
240
+ end
241
+ end
242
+
243
+ def _errors(object_schema_name, object_name, object_type)
244
+ result = +""
245
+ previous_line = 0
246
+ select_all(
247
+ "SELECT e.line, e.position, e.text error_text, s.text source_text
248
+ FROM all_errors e, all_source s
249
+ WHERE e.owner = :owner AND e.name = :name AND e.type = :type
250
+ AND s.owner = e.owner AND s.name = e.name AND s.type = e.type AND s.line = e.line
251
+ ORDER BY e.sequence",
252
+ object_schema_name, object_name, object_type
253
+ ).each do |line, position, error_text, source_text|
254
+ result << "Error on line #{'%4d' % line}: #{source_text}" if line > previous_line
255
+ result << " position #{'%4d' % position}: #{error_text}\n"
256
+ previous_line = line
257
+ end
258
+ result unless result.empty?
259
+ end
260
+
261
+ def find_other_schema(name)
262
+ return nil if @original_schema
263
+ if select_first("SELECT username FROM all_users WHERE username = :username", name.to_s.upcase)
264
+ Schema.new(connection, name, self)
265
+ else
266
+ nil
267
+ end
268
+ end
269
+
270
+ def find_standard_procedure(name)
271
+ return nil if @original_schema
272
+ Procedure.find(self, name, "STANDARD", "SYS")
273
+ end
274
+
275
+ def find_public_synonym(name)
276
+ return nil if @original_schema
277
+ target_schema_name, target_object_name = @connection.describe_synonym("PUBLIC", name)
278
+ find_database_object(target_object_name, target_schema_name) if target_schema_name
279
+ end
280
+ end
281
+ end
282
+
283
+ module Kernel
284
+ # Returns current schema object. You can now chain either database object (packages, procedures, tables, sequences)
285
+ # in current schema or specify different schema name. Examples:
286
+ #
287
+ # plsql.test_function('some parameter')
288
+ # plsql.test_package.test_function('some parameter')
289
+ # plsql.other_schema.test_package.test_function('some parameter')
290
+ # plsql.table_name.all
291
+ # plsql.other_schema.table_name.all
292
+ #
293
+ def plsql(connection_alias = nil)
294
+ PLSQL::Schema.find_or_new(connection_alias)
295
+ end
296
+ end
@@ -0,0 +1,46 @@
1
+ module PLSQL
2
+ module SequenceClassMethods # :nodoc:
3
+ def find(schema, sequence)
4
+ if schema.select_first(
5
+ "SELECT sequence_name FROM all_sequences
6
+ WHERE sequence_owner = :owner
7
+ AND sequence_name = :sequence_name",
8
+ schema.schema_name, sequence.to_s.upcase)
9
+ new(schema, sequence)
10
+ # search for synonym
11
+ elsif (row = schema.select_first(
12
+ "SELECT t.sequence_owner, t.sequence_name
13
+ FROM all_synonyms s, all_sequences t
14
+ WHERE s.owner IN (:owner, 'PUBLIC')
15
+ AND s.synonym_name = :synonym_name
16
+ AND t.sequence_owner = s.table_owner
17
+ AND t.sequence_name = s.table_name
18
+ ORDER BY DECODE(s.owner, 'PUBLIC', 1, 0)",
19
+ schema.schema_name, sequence.to_s.upcase))
20
+ new(schema, row[1], row[0])
21
+ else
22
+ nil
23
+ end
24
+ end
25
+ end
26
+
27
+ class Sequence
28
+ extend SequenceClassMethods
29
+
30
+ def initialize(schema, sequence, override_schema_name = nil) # :nodoc:
31
+ @schema = schema
32
+ @schema_name = override_schema_name || schema.schema_name
33
+ @sequence_name = sequence.to_s.upcase
34
+ end
35
+
36
+ # Get NEXTVAL of sequence
37
+ def nextval
38
+ @schema.select_one "SELECT \"#{@schema_name}\".\"#{@sequence_name}\".NEXTVAL FROM dual"
39
+ end
40
+
41
+ # Get CURRVAL of sequence (can be called just after nextval)
42
+ def currval
43
+ @schema.select_one "SELECT \"#{@schema_name}\".\"#{@sequence_name}\".CURRVAL FROM dual"
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,85 @@
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 all rows as array or values (without column names)
9
+ def select_all(sql, *bindvars, &block)
10
+ @connection.select_all(sql, *bindvars, &block)
11
+ end
12
+
13
+ # Select one value (use if only one row with one value is selected)
14
+ def select_one(sql, *bindvars)
15
+ (row = @connection.select_first(sql, *bindvars)) && row[0]
16
+ end
17
+
18
+ # Select :first or :all values. Examples:
19
+ #
20
+ # plsql.select :first, "SELECT * FROM employees WHERE employee_id = :1", 1
21
+ # plsql.select :all, "SELECT * FROM employees ORDER BY employee_id"
22
+ def select(*args)
23
+ case args[0]
24
+ when nil
25
+ raise ArgumentError, "Not enough arguments"
26
+ when :first
27
+ args.shift
28
+ @connection.select_hash_first(*args)
29
+ when :all
30
+ args.shift
31
+ @connection.select_hash_all(*args)
32
+ else
33
+ @connection.select_hash_all(*args)
34
+ end
35
+ end
36
+
37
+ # Execute SQL statement. Example:
38
+ #
39
+ # plsql.execute "DROP TABLE employees"
40
+ def execute(*args)
41
+ @connection.exec(*args)
42
+ end
43
+
44
+ # Execute COMMIT in current database session.
45
+ # Use beforehand
46
+ #
47
+ # plsql.connection.autocommit = false
48
+ #
49
+ # to turn off automatic commits after each statement.
50
+ def commit
51
+ @connection.commit
52
+ end
53
+
54
+ # Execute ROLLBACK in current database session.
55
+ # Use beforehand
56
+ #
57
+ # plsql.connection.autocommit = false
58
+ #
59
+ # to turn off automatic commits after each statement.
60
+ def rollback
61
+ @connection.rollback
62
+ end
63
+
64
+ # Create SAVEPOINT with specified name. Later use +rollback_to+ method to roll changes back
65
+ # to specified savepoint.
66
+ # Use beforehand
67
+ #
68
+ # plsql.connection.autocommit = false
69
+ #
70
+ # to turn off automatic commits after each statement.
71
+ def savepoint(name)
72
+ execute "SAVEPOINT #{name}"
73
+ end
74
+
75
+ # Roll back changes to specified savepoint (that was created using +savepoint+ method)
76
+ # Use beforehand
77
+ #
78
+ # plsql.connection.autocommit = false
79
+ #
80
+ # to turn off automatic commits after each statement.
81
+ def rollback_to(name)
82
+ execute "ROLLBACK TO #{name}"
83
+ end
84
+ end
85
+ end