ruby-informix 0.6.2 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,380 @@
1
+ # $Id: informix.rb,v 1.13 2008/04/01 01:17:21 santana Exp $
2
+ #
3
+ # Copyright (c) 2008, Gerardo Santana Gomez Garrido <gerardo.santana@gmail.com>
4
+ # All rights reserved.
5
+ #
6
+ # Redistribution and use in source and binary forms, with or without
7
+ # modification, are permitted provided that the following conditions
8
+ # are met:
9
+ #
10
+ # 1. Redistributions of source code must retain the above copyright
11
+ # notice, this list of conditions and the following disclaimer.
12
+ # 2. Redistributions in binary form must reproduce the above copyright
13
+ # notice, this list of conditions and the following disclaimer in the
14
+ # documentation and/or other materials provided with the distribution.
15
+ # 3. The name of the author may not be used to endorse or promote products
16
+ # derived from this software without specific prior written permission.
17
+ #
18
+ # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19
+ # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
+ # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ # DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
22
+ # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23
+ # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25
+ # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26
+ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27
+ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28
+ # POSSIBILITY OF SUCH DAMAGE.
29
+
30
+ require 'informixc'
31
+ require 'informix/seqcursor'
32
+ require 'informix/scrollcursor'
33
+
34
+ module Informix
35
+ VERSION = "0.7.0"
36
+ VERSION.freeze
37
+
38
+ # Shortcut to create a +Database+ object connected to +dbname+ as
39
+ # +user+ with +password+. If these are not given, connects to
40
+ # +dbname+ as the current user.
41
+ #
42
+ # The +Database+ object is passed to the block if it's given, and
43
+ # automatically closes the connection when the block terminates,
44
+ # returning the value of the block.
45
+ #
46
+ # Examples:
47
+ #
48
+ # Connecting to stores with our current credentials:
49
+ # db = Informix.connect('stores')
50
+ #
51
+ # Same thing, using a block and using a different server. The connection is
52
+ # closed automatically when the block terminates.
53
+ # result = Informix.connect('stores@server_shm') do |db|
54
+ # # do something with db
55
+ # # the last expression evaluated will be returned
56
+ # done
57
+ def self.connect(dbname, user = nil, password = nil, &block)
58
+ Database.open(dbname, user, password, &block)
59
+ end
60
+
61
+ # Returns the version of this Ruby/Informix driver.
62
+ # Note that this is NOT the Informix database version.
63
+ #
64
+ # Informix.version => string
65
+ def self.version
66
+ VERSION
67
+ end
68
+
69
+ class Database
70
+ private_class_method :new
71
+
72
+ alias disconnect close
73
+
74
+ # Exact version of the database server to which a Database object is
75
+ # connected
76
+ attr_reader :version
77
+
78
+ # The +IfxVersion+ struct provides the exact version of the database server
79
+ # to which a Database object is connected.
80
+ #
81
+ # Examples:
82
+ #
83
+ # db.version.server_type #=> "IBM Informix Dynamic Server"
84
+ # db.version.level #=> "C6"
85
+ # db.version.to_s #=> "IBM Informix Dynamic Server Version 9.40.FC6"
86
+ class IfxVersion
87
+ # Returns the complete version string
88
+ def to_s
89
+ full.rstrip
90
+ end
91
+ end
92
+
93
+ # Creates a +Database+ object connected to +dbname+ as
94
+ # +user+ with +password+. If these are not given, connects to
95
+ # +dbname+ as the current user.
96
+ #
97
+ # The +Database+ object is passed to the block if it's given, and
98
+ # automatically closes the connection when the block terminates, returning
99
+ # the value of the block.
100
+ def self.open(dbname, user = nil, password = nil)
101
+ db = new(dbname, user, password)
102
+ return db unless block_given?
103
+ begin
104
+ yield db
105
+ ensure
106
+ db.close
107
+ end
108
+ end
109
+
110
+ # Shortcut to create a +Statement+ object from +query+.
111
+ #
112
+ # The +Statement+ object is passed to the block if it's given, and
113
+ # automatically dropped when the block terminates, returning
114
+ # the value of the block.
115
+ #
116
+ # +query+ may contain '?' placeholders for input parameters;
117
+ # it must <b>NOT</b> be a query returning more than one row
118
+ # (use <tt>Database#cursor</tt> instead.)
119
+ #
120
+ # Examples:
121
+ #
122
+ # Preparing a statement:
123
+ # st = db.prepare('delete from orders where order_date = ?')
124
+ #
125
+ # Using a block:
126
+ # query 'update items set quantity = ? where item_num = ?'
127
+ # db.prepare(query) do |st|
128
+ # # do something with st
129
+ # # the last expression evaluated will be returned
130
+ # end
131
+ def prepare(query, &block)
132
+ Statement.new(self, query, &block)
133
+ end
134
+
135
+ # Shortcut to create, <b>execute and drop</b> a +Statement+ object from
136
+ # +query+.
137
+ #
138
+ # +query+ may contain '?' placeholders for input parameters;
139
+ # it <b>cannot</b> be a query returning <b>more than one</b> row
140
+ # (use <tt>Database#cursor</tt> instead.)
141
+ #
142
+ # Returns the record retrieved, in the case of a singleton select, or the
143
+ # number of rows affected, in the case of any other statement.
144
+ #
145
+ # Examples:
146
+ #
147
+ # Deleting records:
148
+ # db.execute('delete from orders where order_date = ?', Date.today)
149
+ #
150
+ # Updating a record:
151
+ # db.execute('update items set quantity = ? where item_num = ?', 10, 101)
152
+ def execute(query, *args)
153
+ Statement.new(self, query) {|stmt| stmt.execute(*args) }
154
+ end
155
+
156
+ # Shortcut to create a cursor object based on +query+ using +options+.
157
+ #
158
+ # The cursor object is passed to the block if it's given, and
159
+ # automatically dropped when the block terminates, returning
160
+ # the value of the block.
161
+ #
162
+ # +query+ may contain '?' placeholders for input parameters.
163
+ #
164
+ # +options+ can be a Hash object with the following possible keys:
165
+ #
166
+ # :scroll => true or false
167
+ # :hold => true or false
168
+ #
169
+ # Examples:
170
+ #
171
+ # This creates a +SequentialCursor+
172
+ # cur = db.cursor('select * from orders where order_date > ?')
173
+ # This creates a +ScrollCursor+
174
+ # cur = db.cursor('select * from customer', :scroll => true)
175
+ # This creates an +InsertCursor+
176
+ # cur = db.cursor('insert into stock values(?, ?, ?, ?, ?, ?)')
177
+ def cursor(query, options = nil, &block)
178
+ Cursor.new(self, query, options, &block)
179
+ end
180
+
181
+ # Shortcut to create, <b>open and iterate</b> a cursor object based on
182
+ # +query+ using +options+. The records are retrieved as arrays.
183
+ #
184
+ # The cursor object is passed to the block and
185
+ # automatically dropped when the block terminates. Returns __self__.
186
+ #
187
+ # +query+ may contain '?' placeholders for input parameters.
188
+ #
189
+ # +options+ can be a Hash object with the following possible keys:
190
+ #
191
+ # :scroll => true or false
192
+ # :hold => true or false
193
+ # :params => input parameters as an Array or nil
194
+ #
195
+ # Examples:
196
+ #
197
+ # Iterating over a table:
198
+ # db.each('select * from customer') do |cust|
199
+ # # do something with cust
200
+ # puts "#{cust[0] cust[1]}"
201
+ # end
202
+ # Same thing, using input parameters:
203
+ # query = 'select * from orders where order_date = ?'
204
+ # db.each(query, :params => [Date.today]) do |order|
205
+ # # do something with order
206
+ # end
207
+ def each(query, options = nil, &block)
208
+ Cursor.open(self, query, options) {|cur| cur.each(&block)}
209
+ self
210
+ end
211
+
212
+ # Similar to +Database#each+, except that retrieves records as hashes
213
+ # instead of arrays.
214
+ #
215
+ # Examples:
216
+ #
217
+ # Iterating over a table:
218
+ # db.each_hash('select * from customer') do |cust|
219
+ # # do something with cust
220
+ # puts "#{cust['fname'] cust['lname']}"
221
+ # end
222
+ def each_hash(query, options = nil, &block)
223
+ Cursor.open(self, query, options) {|cur| cur.each_hash(&block)}
224
+ self
225
+ end
226
+
227
+ # Shortcut to create a +Slob+ object.
228
+ #
229
+ # The +Slob+ object is passed to the block if it's given, and
230
+ # automatically closes it when the block terminates, returning
231
+ # the value of the block.
232
+ #
233
+ # +type+ can be Slob::BLOB or Slob::CLOB
234
+ #
235
+ # +options+ can be a Hash object with the following possible
236
+ # keys:
237
+ #
238
+ # :sbspace => Sbspace name
239
+ # :estbytes => Estimated size, in bytes
240
+ # :extsz => Allocation extent size
241
+ # :createflags => Create-time flags
242
+ # :openflags => Access mode
243
+ # :maxbytes => Maximum size
244
+ # :col_info => Get the previous values from the column-level storage
245
+ # characteristics for the specified database column
246
+ #
247
+ # Examples:
248
+ #
249
+ # Creating a CLOB:
250
+ # slob = db.slob
251
+ # Creating a BLOB without log and passing it to a block:
252
+ # slob = db.slob(Slob::BLOB, :createflags=>Slob:NOLOG) do |slob|
253
+ # # do something with slob
254
+ # end
255
+ def slob(type = Slob::CLOB, options = nil, &block)
256
+ Slob.new(self, type, options, &block)
257
+ end
258
+ end # class Database
259
+
260
+ class Statement
261
+ alias call []
262
+ alias execute []
263
+
264
+ class << self
265
+ alias _new new
266
+ end
267
+
268
+ # Creates a +Statement+ object from +query+.
269
+ #
270
+ # The +Statement+ object is passed to the block if it's given, and
271
+ # automatically dropped when the block terminates, returning
272
+ # the value of the block.
273
+ #
274
+ # +query+ may contain '?' placeholders for input parameters;
275
+ # it must <b>NOT</b> be a query returning more than one row
276
+ # (use +Cursor+ instead.)
277
+ def self.new(dbname, query)
278
+ stmt = _new(dbname, query)
279
+ return stmt if !block_given?
280
+ begin
281
+ yield stmt
282
+ ensure
283
+ stmt.drop
284
+ end
285
+ end
286
+ end # class Statement
287
+
288
+ class Slob
289
+ alias pos tell
290
+
291
+ class << self
292
+ alias _new new
293
+ end
294
+
295
+ # Creates a +Slob+ object.
296
+ #
297
+ # The +Slob+ object is passed to the block if it's given, and
298
+ # automatically closes it when the block terminates, returning
299
+ # the value of the block.
300
+ #
301
+ # +type+ can be Slob::BLOB or Slob::CLOB
302
+ #
303
+ # +options+ can be a Hash object with the following possible
304
+ # keys:
305
+ #
306
+ # :sbspace => Sbspace name
307
+ # :estbytes => Estimated size, in bytes
308
+ # :extsz => Allocation extent size
309
+ # :createflags => Create-time flags
310
+ # :openflags => Access mode
311
+ # :maxbytes => Maximum size
312
+ # :col_info => Get the previous values from the column-level storage
313
+ # characteristics for the specified database column
314
+ def self.new(dbname, query, options = nil)
315
+ slob = _new(dbname, query, options)
316
+ return slob if !block_given?
317
+ begin
318
+ yield slob
319
+ ensure
320
+ slob.close
321
+ end
322
+ end
323
+ end # class Slob
324
+
325
+ module Cursor
326
+ private_class_method :new0
327
+
328
+ # Shortcut to create a cursor object based on +query+ using +options+.
329
+ #
330
+ # The cursor object is passed to the block if it's given, and
331
+ # automatically dropped when the block terminates, returning
332
+ # the value of the block.
333
+ #
334
+ # +options+ can be a Hash object with the following possible keys:
335
+ #
336
+ # :scroll => true or false
337
+ # :hold => true or false
338
+ def self.new(db, query, options = nil)
339
+ if options
340
+ Hash === options||raise(TypeError,"options must be supplied as a Hash")
341
+ end
342
+ cur = new0(db, query, options)
343
+ return cur unless block_given?
344
+ begin
345
+ yield cur
346
+ ensure
347
+ cur.drop
348
+ end
349
+ end
350
+
351
+ # Shortcut to create <b>and open</b> a cursor object based on +query+
352
+ # using +options+ in a single step.
353
+ #
354
+ # The cursor object is passed to the block if it's given, and
355
+ # automatically dropped when the block terminates, returning
356
+ # the value of the block.
357
+ #
358
+ # +options+ can be a Hash object with the following possible keys:
359
+ #
360
+ # :scroll => true or false
361
+ # :hold => true or false
362
+ # :params => input parameters as an Array or nil
363
+ def self.open(db, query, options = nil)
364
+ params = nil
365
+ if options
366
+ Hash === options||raise(TypeError,"options must be supplied as a Hash")
367
+ (params = options[:params]) && (Array === params ||
368
+ raise(TypeError,"params must be supplied as an Array"))
369
+ end
370
+ cur = new(db, query, options)
371
+ params ? cur.open(*params) : cur.open
372
+ return cur unless block_given?
373
+ begin
374
+ yield cur
375
+ ensure
376
+ cur.drop
377
+ end
378
+ end
379
+ end # module Cursor
380
+ end # module Informix
@@ -0,0 +1,161 @@
1
+ # $Id: exceptions.rb,v 1.2 2008/03/29 01:38:35 santana Exp $
2
+ #
3
+ # Copyright (c) 2008, Gerardo Santana Gomez Garrido <gerardo.santana@gmail.com>
4
+ # All rights reserved.
5
+ #
6
+ # Redistribution and use in source and binary forms, with or without
7
+ # modification, are permitted provided that the following conditions
8
+ # are met:
9
+ #
10
+ # 1. Redistributions of source code must retain the above copyright
11
+ # notice, this list of conditions and the following disclaimer.
12
+ # 2. Redistributions in binary form must reproduce the above copyright
13
+ # notice, this list of conditions and the following disclaimer in the
14
+ # documentation and/or other materials provided with the distribution.
15
+ # 3. The name of the author may not be used to endorse or promote products
16
+ # derived from this software without specific prior written permission.
17
+ #
18
+ # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19
+ # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
+ # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ # DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
22
+ # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23
+ # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25
+ # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26
+ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27
+ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28
+ # POSSIBILITY OF SUCH DAMAGE.
29
+
30
+ #--
31
+ # The following code is a translation of the original C code written
32
+ # by Edwin M. Fine <emofine at finecomputerconsultants dot com>
33
+ #++
34
+
35
+ module Informix
36
+ ExcInfo = Struct.new(:sql_code, :sql_state, :class_origin, :subclass_origin,
37
+ :message, :server_name, :connection_name)
38
+
39
+ # The +ExcInfo+ class works as an object representation of an Informix
40
+ # error state
41
+ class ExcInfo
42
+ FORMAT = "%-15s: %s\n".freeze
43
+
44
+ # excinfo.to_s => string
45
+ #
46
+ # Returns a string representation of the error.
47
+ def to_s
48
+ ret = "\n"
49
+ each_pair do |member, value|
50
+ ret += sprintf(FORMAT, member.to_s, value)
51
+ end
52
+ ret
53
+ end
54
+ end # class ExcInfo
55
+
56
+ # The +Error+ class is the base class for the rest of the exception classes
57
+ # used in this extension. It works as a collection of +ExcInfo+ objects
58
+ # when an error condition occurs.
59
+ class Error < StandardError
60
+ include Enumerable
61
+
62
+ # Informix::Error.new([string|array]) => obj
63
+ #
64
+ # Optional string is the exception message.
65
+ # Optional array must contain only instances of Informix::ExcInfo structs.
66
+ #
67
+ # Examples:
68
+ # exc = Informix::Error.new
69
+ # arr = [ExcInfo.new(x,y,z...), ExcInfo.new(a,b,c...)]
70
+ # exc = Informix::Error.new(arr)
71
+ def initialize(v = nil)
72
+ case v
73
+ when NilClass
74
+ @info = []
75
+ when String
76
+ @info = []
77
+ super
78
+ when Array
79
+ return @info = v if v.all? {|e| ExcInfo === e}
80
+ raise(TypeError, "Array may contain only Informix::ExcInfo structs")
81
+ else
82
+ raise(TypeError,
83
+ "Expected string, or array of Informix::ExcInfo, as argument")
84
+ end
85
+ end
86
+
87
+ # exc.add_info(sql_code, sql_state, class_origin, subclass_origin,
88
+ # message, server_name, connection_name) => self
89
+ #
90
+ # Appends the given information to the exception.
91
+ def add_info(*v)
92
+ v.flatten!
93
+ raise(ArgumentError,
94
+ "Invalid number of arguments (got %d, need %d)", v.size, 7) \
95
+ if v.size != 7
96
+ @info.push ExcInfo.new(*v)
97
+ end
98
+
99
+ # exc.size => num
100
+ #
101
+ # Returns the number of Informix exception messages in the exception.
102
+ def size
103
+ @info.size
104
+ end
105
+
106
+ alias length size
107
+
108
+ # exc.each {|exc_info| block } => exc_info
109
+ #
110
+ # Calls block once for each Informix::ExcInfo object in the exception.
111
+ def each(&block)
112
+ @info.each(&block)
113
+ end
114
+
115
+ # exc[index] => info
116
+ #
117
+ # Returns the ExcInfo object at index.
118
+ def [](index)
119
+ @info[index]
120
+ end
121
+
122
+ # exc.to_s => string
123
+ #
124
+ # Returns a string representation of self.
125
+ def to_s
126
+ return super if @info.size == 0
127
+ ret = ""
128
+ @info.each do |info|
129
+ ret += info.to_s
130
+ end
131
+ ret
132
+ end
133
+
134
+ # exc.message => string
135
+ #
136
+ # Overrides Exception#message. Returns first message in ExcInfo array,
137
+ # or if the array is empty, delegates back to the parent class.
138
+ def message
139
+ @info.size > 0 ? @info[0].message : super
140
+ end
141
+
142
+ # exc.sqlcode => fixnum
143
+ #
144
+ # Returns the SQLCODE for the first stored ExcInfo struct, or 0
145
+ # if none are stored.
146
+ def sql_code
147
+ @info.size > 0 ? @info[0].sql_code : 0
148
+ end
149
+ end # class Error
150
+
151
+ class Warning < StandardError; end
152
+
153
+ class InterfaceError < Error; end
154
+ class DatabaseError < Error; end
155
+ class DataError < Error; end
156
+ class OperationalError < Error; end
157
+ class IntegrityError < Error; end
158
+ class InternalError < Error; end
159
+ class ProgrammingError < Error; end
160
+ class NotSupportedError < Error; end
161
+ end # module Informix