ruby-informix 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (7) hide show
  1. data/COPYRIGHT +26 -0
  2. data/Changelog +84 -0
  3. data/README +80 -0
  4. data/extconf.rb +36 -0
  5. data/informix.c +4048 -0
  6. data/informix.ec +3093 -0
  7. metadata +50 -0
data/informix.ec ADDED
@@ -0,0 +1,3093 @@
1
+ /* $Id: informix.ec,v 1.56 2006/12/13 08:19:52 santana Exp $ */
2
+ /*
3
+ * Copyright (c) 2006, 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
+ #include "ruby.h"
32
+
33
+ #include <sqlstype.h>
34
+ #include <sqltypes.h>
35
+
36
+ static VALUE rb_cDate;
37
+
38
+ static VALUE rb_mInformix;
39
+ static VALUE rb_mSequentialCursor;
40
+ static VALUE rb_mScrollCursor;
41
+ static VALUE rb_mInsertCursor;
42
+
43
+ static VALUE rb_cSlob;
44
+ static VALUE rb_cDatabase;
45
+ static VALUE rb_cStatement;
46
+ static VALUE rb_cCursor;
47
+
48
+ static ID s_read, s_new, s_utc, s_day, s_month, s_year;
49
+ static ID s_hour, s_min, s_sec, s_usec, s_to_s, s_to_i;
50
+ static VALUE sym_name, sym_type, sym_nullable, sym_stype, sym_length;
51
+ static VALUE sym_precision, sym_scale, sym_default, sym_xid;
52
+ static VALUE sym_scroll, sym_hold;
53
+ static VALUE sym_col_info, sym_sbspace, sym_estbytes, sym_extsz;
54
+ static VALUE sym_createflags, sym_openflags;
55
+
56
+ #define IDSIZE 30
57
+
58
+ static char *currentdid = NULL;
59
+
60
+ typedef struct {
61
+ short is_select, is_open;
62
+ struct sqlda daInput, *daOutput;
63
+ short *indInput, *indOutput;
64
+ char *bfOutput;
65
+ char cursor_id[IDSIZE];
66
+ char stmt_id[IDSIZE];
67
+ VALUE db, array, hash, field_names;
68
+ char *database_id;
69
+ } cursor_t;
70
+
71
+ typedef struct {
72
+ mint fd;
73
+ ifx_lo_t lo;
74
+ ifx_lo_create_spec_t *spec;
75
+ short type; /* XID_CLOB/XID_BLOB */
76
+ VALUE db;
77
+ char *database_id;
78
+ } slob_t;
79
+
80
+ #define NUM2INT8(num, int8addr) do { \
81
+ VALUE str = rb_funcall(num, s_to_s, 0); \
82
+ char *c_str = StringValueCStr(str); \
83
+ mint ret = ifx_int8cvasc(c_str, strlen(c_str), (int8addr)); \
84
+ if (ret < 0) \
85
+ rb_raise(rb_eRuntimeError, "Could not convert %s to int8", c_str); \
86
+ }while(0)
87
+
88
+ #define INT82NUM(int8addr, num) do { \
89
+ char str[21]; \
90
+ mint ret = ifx_int8toasc((int8addr), str, sizeof(str) - 1); \
91
+ str[sizeof(str) - 1] = 0; \
92
+ num = rb_cstr2inum(str, 10); \
93
+ }while(0)
94
+
95
+ /* class Slob ------------------------------------------------------------ */
96
+
97
+ static void
98
+ slob_mark(slob_t *slob)
99
+ {
100
+ rb_gc_mark(slob->db);
101
+ }
102
+
103
+ static void
104
+ slob_free(slob_t *slob)
105
+ {
106
+ if (slob->fd != -1) {
107
+ EXEC SQL begin declare section;
108
+ char *did;
109
+ EXEC SQL end declare section;
110
+
111
+ did = slob->database_id;
112
+ if (currentdid != did) {
113
+ EXEC SQL set connection :did;
114
+ if (SQLCODE < 0)
115
+ goto exit;
116
+ currentdid = did;
117
+ }
118
+ ifx_lo_close(slob->fd);
119
+ }
120
+
121
+ exit:
122
+ if (slob->spec)
123
+ ifx_lo_spec_free(slob->spec);
124
+
125
+ xfree(slob);
126
+ }
127
+
128
+ static VALUE
129
+ slob_alloc(VALUE klass)
130
+ {
131
+ slob_t *slob;
132
+
133
+ slob = ALLOC(slob_t);
134
+ slob->spec = NULL;
135
+ slob->fd = -1;
136
+ slob->type = XID_CLOB;
137
+
138
+ return Data_Wrap_Struct(klass, slob_mark, slob_free, slob);
139
+ }
140
+
141
+ /*
142
+ * call-seq:
143
+ * Slob.new(database, type = Slob::CLOB, options = nil) => slob
144
+ *
145
+ * Creates a Smart Large Object of type <i>type</i> in <i>database</i>.
146
+ * Returns a <code>Slob</code> object pointing to it.
147
+ *
148
+ * <i>type</i> can be Slob::BLOB or Slob::CLOB
149
+ *
150
+ * <i>options</i> must be a hash with the following possible keys:
151
+ *
152
+ * :sbspace => Sbspace name
153
+ * :estbytes => Estimated size, in bytes
154
+ * :extsz => Allocation extent size
155
+ * :createflags => Create-time flags
156
+ * :openflags => Access mode
157
+ * :maxbytes => Maximum size
158
+ * :col_info => Get the previous values from the column-level storage
159
+ * characteristics for the specified database column
160
+ */
161
+ static VALUE
162
+ slob_initialize(int argc, VALUE *argv, VALUE self)
163
+ {
164
+ mint ret, error;
165
+ slob_t *slob;
166
+ VALUE db, type, options;
167
+ VALUE col_info, sbspace, estbytes, extsz, createflags, openflags, maxbytes;
168
+ EXEC SQL begin declare section;
169
+ char *did;
170
+ EXEC SQL end declare section;
171
+
172
+ rb_scan_args(argc, argv, "12", &db, &type, &options);
173
+ Data_Get_Struct(db, char, did);
174
+
175
+ if (currentdid != did) {
176
+ EXEC SQL set connection :did;
177
+ if (SQLCODE < 0)
178
+ rb_raise(rb_eRuntimeError, "Informix Error: %d", SQLCODE);
179
+ currentdid = did;
180
+ }
181
+
182
+ Data_Get_Struct(self, slob_t, slob);
183
+ slob->db = db;
184
+ slob->database_id = did;
185
+
186
+ if (RTEST(type)) {
187
+ int t = FIX2INT(type);
188
+ if (t != XID_CLOB && t != XID_BLOB)
189
+ rb_raise(rb_eRuntimeError, "Invalid type %d for an SLOB", t);
190
+ slob->type = t;
191
+ }
192
+
193
+ col_info = sbspace = estbytes = extsz = createflags = openflags = maxbytes = Qnil;
194
+
195
+ if (RTEST(options)) {
196
+ col_info = rb_hash_aref(options, sym_col_info);
197
+ sbspace = rb_hash_aref(options, sym_sbspace);
198
+ estbytes = rb_hash_aref(options, sym_estbytes);
199
+ extsz = rb_hash_aref(options, sym_extsz);
200
+ createflags = rb_hash_aref(options, sym_createflags);
201
+ openflags = rb_hash_aref(options, sym_openflags);
202
+ }
203
+
204
+ ret = ifx_lo_def_create_spec(&slob->spec);
205
+ if (ret < 0)
206
+ rb_raise(rb_eRuntimeError, "Informix Error: %d", ret);
207
+
208
+ if (RTEST(col_info)) {
209
+ ret = ifx_lo_col_info(StringValueCStr(col_info), slob->spec);
210
+
211
+ if (ret < 0)
212
+ rb_raise(rb_eRuntimeError, "Informix Error: %d", ret);
213
+ }
214
+ if (RTEST(sbspace)) {
215
+ char *c_sbspace = StringValueCStr(sbspace);
216
+ ret = ifx_lo_specset_sbspace(slob->spec, c_sbspace);
217
+ if (ret == -1)
218
+ rb_raise(rb_eRuntimeError, "Could not set sbspace name to %s", c_sbspace);
219
+ }
220
+ if (RTEST(estbytes)) {
221
+ ifx_int8_t estbytes8;
222
+
223
+ NUM2INT8(estbytes, &estbytes8);
224
+ ret = ifx_lo_specset_estbytes(slob->spec, &estbytes8);
225
+ if (ret == -1)
226
+ rb_raise(rb_eRuntimeError, "Could not set estbytes");
227
+ }
228
+ if (RTEST(extsz)) {
229
+ ret = ifx_lo_specset_extsz(slob->spec, FIX2LONG(extsz));
230
+ if (ret == -1)
231
+ rb_raise(rb_eRuntimeError, "Could not set extsz to %d", FIX2LONG(extsz));
232
+ }
233
+ if (RTEST(createflags)) {
234
+ ret = ifx_lo_specset_flags(slob->spec, FIX2LONG(createflags));
235
+ if (ret == -1)
236
+ rb_raise(rb_eRuntimeError, "Could not set crate-time flags to 0x%X", FIX2LONG(createflags));
237
+ }
238
+ if (RTEST(maxbytes)) {
239
+ ifx_int8_t maxbytes8;
240
+
241
+ NUM2INT8(maxbytes, (&maxbytes8));
242
+ ret = ifx_lo_specset_maxbytes(slob->spec, &maxbytes8);
243
+ if (ret == -1)
244
+ rb_raise(rb_eRuntimeError, "Could not set maxbytes");
245
+ }
246
+ slob->fd = ifx_lo_create(slob->spec, RTEST(openflags)? FIX2LONG(openflags): LO_RDWR, &slob->lo, &error);
247
+ if (slob->fd == -1) {
248
+ rb_raise(rb_eRuntimeError, "Informix Error: %d\n", error);
249
+ }
250
+ return self;
251
+ }
252
+
253
+ /*
254
+ * call-seq:
255
+ * slob.open(access = Slob::RDONLY) => slob
256
+ *
257
+ * Opens the Smart Large Object in <i>access</i> mode.
258
+ *
259
+ * access modes:
260
+ *
261
+ * Slob::RDONLY:: Read only
262
+ * Slob::DIRTY_READ:: Read uncommitted data
263
+ * Slob::WRONLY:: Write only
264
+ * Slob::APPEND:: Append data to the end, if combined with RDRW or WRONLY; read only otherwise
265
+ * Slob::RDRW:: Read/Write
266
+ * Slob::BUFFER:: Use standard database server buffer pool
267
+ * Slob::NOBUFFER:: Use private buffer from the session pool of the database server
268
+ * Slob::LOCKALL:: Lock the entire Smart Large Object
269
+ * Slob::LOCKRANGE:: Lock a range of bytes
270
+ *
271
+ * Returns __self__.
272
+ */
273
+ static VALUE
274
+ slob_open(int argc, VALUE *argv, VALUE self)
275
+ {
276
+ VALUE access;
277
+ slob_t *slob;
278
+ mint error;
279
+ EXEC SQL begin declare section;
280
+ char *did;
281
+ EXEC SQL end declare section;
282
+
283
+ Data_Get_Struct(self, slob_t, slob);
284
+
285
+ if (slob->fd != -1) /* Already open */
286
+ return self;
287
+
288
+ did = slob->database_id;
289
+ if (currentdid != did) {
290
+ EXEC SQL set connection :did;
291
+ if (SQLCODE < 0)
292
+ rb_raise(rb_eRuntimeError, "Informix Error: %d", SQLCODE);
293
+ currentdid = did;
294
+ }
295
+
296
+ rb_scan_args(argc, argv, "01", &access);
297
+
298
+ slob->fd = ifx_lo_open(&slob->lo, NIL_P(access)? LO_RDONLY: FIX2INT(access), &error);
299
+
300
+ if (slob->fd == -1)
301
+ rb_raise(rb_eRuntimeError, "Informix Error: %d", error);
302
+
303
+ return self;
304
+ }
305
+
306
+ /*
307
+ * call-seq:
308
+ * slob.close => slob
309
+ *
310
+ * Closes the Smart Large Object and returns __self__.
311
+ */
312
+ static VALUE
313
+ slob_close(VALUE self)
314
+ {
315
+ slob_t *slob;
316
+
317
+ Data_Get_Struct(self, slob_t, slob);
318
+ if (slob->fd != -1) {
319
+ EXEC SQL begin declare section;
320
+ char *did;
321
+ EXEC SQL end declare section;
322
+
323
+ did = slob->database_id;
324
+ if (currentdid != did) {
325
+ EXEC SQL set connection :did;
326
+ if (SQLCODE < 0)
327
+ return self;
328
+ currentdid = did;
329
+ }
330
+
331
+ ifx_lo_close(slob->fd);
332
+ slob->fd = -1;
333
+ }
334
+
335
+ return self;
336
+ }
337
+
338
+ /*
339
+ * call-seq:
340
+ * slob.read(nbytes) => string
341
+ *
342
+ * Reads at most <i>nbytes</i> bytes from the Smart Large Object.
343
+ *
344
+ * Returns the bytes read as a String object.
345
+ */
346
+ static VALUE
347
+ slob_read(VALUE self, VALUE nbytes)
348
+ {
349
+ slob_t *slob;
350
+ mint error, ret;
351
+ char *buffer;
352
+ long c_nbytes;
353
+ VALUE str;
354
+ EXEC SQL begin declare section;
355
+ char *did;
356
+ EXEC SQL end declare section;
357
+
358
+
359
+ Data_Get_Struct(self, slob_t, slob);
360
+
361
+ if (slob->fd == -1)
362
+ rb_raise(rb_eRuntimeError, "Open the Slob object before reading");
363
+
364
+ did = slob->database_id;
365
+ if (currentdid != did) {
366
+ EXEC SQL set connection :did;
367
+ if (SQLCODE < 0)
368
+ rb_raise(rb_eRuntimeError, "Informix Error: %d", SQLCODE);
369
+ currentdid = did;
370
+ }
371
+
372
+ c_nbytes = FIX2LONG(nbytes);
373
+ buffer = ALLOC_N(char, c_nbytes);
374
+ ret = ifx_lo_read(slob->fd, buffer, c_nbytes, &error);
375
+
376
+ if (ret == -1)
377
+ rb_raise(rb_eRuntimeError, "Informix Error: %d\n", error);
378
+
379
+ str = rb_str_new(buffer, ret);
380
+ xfree(buffer);
381
+
382
+ return str;
383
+ }
384
+
385
+ /*
386
+ * call-seq:
387
+ * slob.write(data) => fixnum or bignum
388
+ *
389
+ * Writes <i>data</i> to the Smart Large Object.
390
+ *
391
+ * Returns the number of bytes written.
392
+ */
393
+ static VALUE
394
+ slob_write(VALUE self, VALUE data)
395
+ {
396
+ slob_t *slob;
397
+ mint error, ret;
398
+ char *buffer;
399
+ long nbytes;
400
+ VALUE str;
401
+ EXEC SQL begin declare section;
402
+ char *did;
403
+ EXEC SQL end declare section;
404
+
405
+ Data_Get_Struct(self, slob_t, slob);
406
+
407
+ if (slob->fd == -1)
408
+ rb_raise(rb_eRuntimeError, "Open the Slob object before writing");
409
+
410
+ did = slob->database_id;
411
+ if (currentdid != did) {
412
+ EXEC SQL set connection :did;
413
+ if (SQLCODE < 0)
414
+ rb_raise(rb_eRuntimeError, "Informix Error: %d", SQLCODE);
415
+ currentdid = did;
416
+ }
417
+
418
+ str = StringValue(data);
419
+ buffer = RSTRING(str)->ptr;
420
+ nbytes = RSTRING(str)->len;
421
+
422
+ ret = ifx_lo_write(slob->fd, buffer, nbytes, &error);
423
+
424
+ if (ret == -1)
425
+ rb_raise(rb_eRuntimeError, "Informix Error: %d", error);
426
+
427
+ return LONG2NUM(ret);
428
+ }
429
+
430
+ /*
431
+ * call-seq:
432
+ * slob.seek(offset, whence) => fixnum or bignum
433
+ *
434
+ * Sets the file position for the next read or write
435
+ * operation on the open Smart Large Object.
436
+ *
437
+ *
438
+ * <i>offset</i> offset from the starting seek position
439
+ * <i>whence</i> identifies the starting seek position
440
+ *
441
+ * Values for <i>whence</i>:
442
+ *
443
+ * Slob::SEEK_SET:: The start of the Smart Large Object
444
+ * Slob::SEEK_CUR:: The current seek position in the Smart Large Object
445
+ * Slob::SEEK_END:: The end of the Smart Large Object
446
+ *
447
+ * Returns the new position.
448
+ */
449
+ static VALUE
450
+ slob_seek(VALUE self, VALUE offset, VALUE whence)
451
+ {
452
+ slob_t *slob;
453
+ mint ret;
454
+ VALUE seek_pos;
455
+ ifx_int8_t offset8, seek_pos8;
456
+ EXEC SQL begin declare section;
457
+ char *did;
458
+ EXEC SQL end declare section;
459
+
460
+ Data_Get_Struct(self, slob_t, slob);
461
+
462
+ if (slob->fd == -1)
463
+ rb_raise(rb_eRuntimeError, "Open the Slob object first");
464
+
465
+ did = slob->database_id;
466
+ if (currentdid != did) {
467
+ EXEC SQL set connection :did;
468
+ if (SQLCODE < 0)
469
+ rb_raise(rb_eRuntimeError, "Informix Error: %d", SQLCODE);
470
+ currentdid = did;
471
+ }
472
+
473
+ NUM2INT8(offset, &offset8);
474
+ ret = ifx_lo_seek(slob->fd, &offset8, FIX2INT(whence), &seek_pos8);
475
+ if (ret < 0)
476
+ rb_raise(rb_eRuntimeError, "Informix Error: %d", ret);
477
+
478
+ INT82NUM(&seek_pos8, seek_pos);
479
+
480
+ return seek_pos;
481
+ }
482
+
483
+ /*
484
+ * call-seq:
485
+ * slob.tell => fixnum or bignum
486
+ *
487
+ * Returns the current file or seek position for an
488
+ * open Smart Large Object
489
+ */
490
+ static VALUE
491
+ slob_tell(VALUE self)
492
+ {
493
+ slob_t *slob;
494
+ mint ret;
495
+ VALUE seek_pos;
496
+ ifx_int8_t seek_pos8;
497
+ EXEC SQL begin declare section;
498
+ char *did;
499
+ EXEC SQL end declare section;
500
+
501
+ Data_Get_Struct(self, slob_t, slob);
502
+
503
+ if (slob->fd == -1)
504
+ rb_raise(rb_eRuntimeError, "Open the Slob object first");
505
+
506
+ did = slob->database_id;
507
+ if (currentdid != did) {
508
+ EXEC SQL set connection :did;
509
+ if (SQLCODE < 0)
510
+ rb_raise(rb_eRuntimeError, "Informix Error: %d", SQLCODE);
511
+ currentdid = did;
512
+ }
513
+
514
+ ret = ifx_lo_tell(slob->fd, &seek_pos8);
515
+ if (ret < 0)
516
+ rb_raise(rb_eRuntimeError, "Informix Error: %d", ret);
517
+
518
+ INT82NUM(&seek_pos8, seek_pos);
519
+
520
+ return seek_pos;
521
+ }
522
+
523
+ /*
524
+ * call-seq:
525
+ * slob.truncate(offset) => slob
526
+ *
527
+ * Truncates a Smart Large Object at a specified byte position.
528
+ *
529
+ * Returns __self__.
530
+ */
531
+ static VALUE
532
+ slob_truncate(VALUE self, VALUE offset)
533
+ {
534
+ slob_t *slob;
535
+ mint ret;
536
+ ifx_int8_t offset8;
537
+ EXEC SQL begin declare section;
538
+ char *did;
539
+ EXEC SQL end declare section;
540
+
541
+ Data_Get_Struct(self, slob_t, slob);
542
+
543
+ if (slob->fd == -1)
544
+ rb_raise(rb_eRuntimeError, "Open the Slob object first");
545
+
546
+ did = slob->database_id;
547
+ if (currentdid != did) {
548
+ EXEC SQL set connection :did;
549
+ if (SQLCODE < 0)
550
+ rb_raise(rb_eRuntimeError, "Informix Error: %d", SQLCODE);
551
+ currentdid = did;
552
+ }
553
+
554
+ NUM2INT8(offset, &offset8);
555
+ ret = ifx_lo_truncate(slob->fd, &offset8);
556
+ if (ret < 0)
557
+ rb_raise(rb_eRuntimeError, "Informix Error: %d", ret);
558
+
559
+ return self;
560
+ }
561
+
562
+ /* Helper functions ------------------------------------------------------- */
563
+
564
+ /*
565
+ * Counts the number of markers '?' in the query
566
+ */
567
+ static int count_markers(const char *query)
568
+ {
569
+ register char c, quote = 0;
570
+ register int count = 0;
571
+
572
+ while((c = *query++)) {
573
+ if (quote && c != quote)
574
+ ;
575
+ else if (quote == c) {
576
+ quote = 0;
577
+ }
578
+ else if (c == '\'' || c == '"') {
579
+ quote = c;
580
+ }
581
+ else if (c == '?') {
582
+ ++count;
583
+ }
584
+ }
585
+ return count;
586
+ }
587
+
588
+ /*
589
+ * Allocates memory for the indicators array and slots for the input
590
+ * parameters, if any. Freed by free_input_slots.
591
+ */
592
+ static void
593
+ alloc_input_slots(cursor_t *c, const char *query)
594
+ {
595
+ register int n;
596
+
597
+ n = count_markers(query);
598
+ c->daInput.sqld = n;
599
+ if (n) {
600
+ c->daInput.sqlvar = ALLOC_N(struct sqlvar_struct, n);
601
+ memset(c->daInput.sqlvar, 0, n*sizeof(struct sqlvar_struct));
602
+ c->indInput = ALLOC_N(short, n);
603
+ while(n--)
604
+ c->daInput.sqlvar[n].sqlind = &c->indInput[n];
605
+ }
606
+ else {
607
+ c->daInput.sqlvar = NULL;
608
+ c->indInput = NULL;
609
+ }
610
+ }
611
+
612
+ /*
613
+ * Allocates memory for the output data slots and its indicators array.
614
+ * Freed by free_output_slots.
615
+ */
616
+ static void
617
+ alloc_output_slots(cursor_t *c)
618
+ {
619
+ register int i, count;
620
+ register short *ind;
621
+ struct sqlvar_struct *var;
622
+ register char *buffer;
623
+
624
+ c->field_names = rb_ary_new2(c->daOutput->sqld);
625
+
626
+ ind = c->indOutput = ALLOC_N(short, c->daOutput->sqld);
627
+
628
+ var = c->daOutput->sqlvar;
629
+ for (i = count = 0; i < c->daOutput->sqld; i++, ind++, var++) {
630
+ var->sqlind = ind;
631
+ rb_ary_store(c->field_names, i, rb_str_new2(var->sqlname));
632
+ if (ISSMARTBLOB(var->sqltype, var->sqlxid)) {
633
+ var->sqldata = (char *)ALLOC(ifx_lo_t);
634
+ continue;
635
+ }
636
+ var->sqllen = rtypmsize(var->sqltype, var->sqllen);
637
+ count = rtypalign(count, var->sqltype) + var->sqllen;
638
+ }
639
+
640
+ buffer = c->bfOutput = ALLOC_N(char, count);
641
+ memset(buffer, 0, count);
642
+
643
+ var = c->daOutput->sqlvar;
644
+ for (i = count = 0; i < c->daOutput->sqld; i++, var++) {
645
+ if (var->sqldata)
646
+ continue;
647
+ count = rtypalign(count, var->sqltype);
648
+ var->sqldata = buffer + count;
649
+ count += var->sqllen;
650
+ if (ISBYTESTYPE(var->sqltype) || ISTEXTTYPE(var->sqltype)) {
651
+ loc_t *p;
652
+ p = (loc_t *)var->sqldata;
653
+ byfill((char *)p, sizeof(loc_t), 0);
654
+ p->loc_loctype = LOCMEMORY;
655
+ p->loc_bufsize = -1;
656
+ }
657
+ if (var->sqltype == SQLDTIME) {
658
+ var->sqllen = 0;
659
+ }
660
+ }
661
+ }
662
+
663
+ /*
664
+ * Frees the allocated memory of the input parameters, but not the slots
665
+ * nor the indicators array. Allocated by bind_input_params.
666
+ */
667
+ static void
668
+ clean_input_slots(cursor_t *c)
669
+ {
670
+ register int count;
671
+ register struct sqlvar_struct *var;
672
+
673
+ if (c->daInput.sqlvar == NULL)
674
+ return;
675
+ var = c->daInput.sqlvar;
676
+ count = c->daInput.sqld;
677
+ while(count--) {
678
+ if (var->sqldata != NULL) {
679
+ if (var->sqltype == CLOCATORTYPE) {
680
+ loc_t *p = (loc_t *)var->sqldata;
681
+ if (p->loc_buffer != NULL) {
682
+ xfree(p->loc_buffer);
683
+ }
684
+ }
685
+ xfree(var->sqldata);
686
+ var->sqldata = NULL;
687
+ var++;
688
+ }
689
+ }
690
+ }
691
+
692
+ /*
693
+ * Frees the memory for the input parameters, their slots, and the indicators
694
+ * array. Allocated by alloc_input_slots and bind_input_params.
695
+ */
696
+ static void
697
+ free_input_slots(cursor_t *c)
698
+ {
699
+ clean_input_slots(c);
700
+ if (c->daInput.sqlvar) {
701
+ xfree(c->daInput.sqlvar);
702
+ c->daInput.sqlvar = NULL;
703
+ c->daInput.sqld = 0;
704
+ }
705
+ if (c->indInput) {
706
+ xfree(c->indInput);
707
+ c->indInput = NULL;
708
+ }
709
+ }
710
+
711
+ /*
712
+ * Frees the memory for the output parameters, their slots, and the indicators
713
+ * array. Allocated by alloc_output_slots.
714
+ */
715
+ static void
716
+ free_output_slots(cursor_t *c)
717
+ {
718
+ if (c->daOutput != NULL) {
719
+ struct sqlvar_struct *var = c->daOutput->sqlvar;
720
+ if (var) {
721
+ register int i;
722
+ for (i = 0; i < c->daOutput->sqld; i++, var++) {
723
+ if (ISBLOBTYPE(var->sqltype)) {
724
+ loc_t *p = (loc_t *) var->sqldata;
725
+ if(p -> loc_buffer)
726
+ xfree(p->loc_buffer);
727
+ }
728
+ if (ISSMARTBLOB(var->sqltype, var->sqlxid))
729
+ xfree(var->sqldata);
730
+ }
731
+ }
732
+ xfree(c->daOutput);
733
+ c->daOutput = NULL;
734
+ }
735
+ if (c->indOutput != NULL) {
736
+ xfree(c->indOutput);
737
+ c->indOutput = NULL;
738
+ }
739
+ if (c->bfOutput != NULL) {
740
+ xfree(c->bfOutput);
741
+ c->bfOutput = NULL;
742
+ }
743
+ }
744
+
745
+ /*
746
+ * Gets an array of Ruby objects as input parameters and place them in input
747
+ * slots, converting data types and allocating memory as needed.
748
+ */
749
+ static void
750
+ bind_input_params(cursor_t *c, VALUE *argv)
751
+ {
752
+ VALUE data, klass;
753
+ register int i;
754
+ register struct sqlvar_struct *var;
755
+
756
+ var = c->daInput.sqlvar;
757
+ for (i = 0; i < c->daInput.sqld; i++, var++) {
758
+ data = argv[i];
759
+
760
+ switch(TYPE(data)) {
761
+ case T_NIL:
762
+ var->sqltype = CSTRINGTYPE;
763
+ var->sqldata = NULL;
764
+ var->sqllen = 0;
765
+ *var->sqlind = -1;
766
+ break;
767
+ case T_FIXNUM:
768
+ var->sqldata = (char *)ALLOC(long);
769
+ *((long *)var->sqldata) = FIX2LONG(data);
770
+ var->sqltype = CLONGTYPE;
771
+ var->sqllen = sizeof(long);
772
+ *var->sqlind = 0;
773
+ break;
774
+ case T_FLOAT:
775
+ var->sqldata = (char *)ALLOC(double);
776
+ *((double *)var->sqldata) = NUM2DBL(data);
777
+ var->sqltype = CDOUBLETYPE;
778
+ var->sqllen = sizeof(double);
779
+ *var->sqlind = 0;
780
+ break;
781
+ case T_TRUE:
782
+ case T_FALSE:
783
+ var->sqldata = ALLOC(char);
784
+ *var->sqldata = TYPE(data) == T_TRUE? 't': 'f';
785
+ var->sqltype = CCHARTYPE;
786
+ var->sqllen = sizeof(char);
787
+ *var->sqlind = 0;
788
+ break;
789
+ default:
790
+ klass = rb_obj_class(data);
791
+ if (klass == rb_cDate) {
792
+ int2 mdy[3];
793
+ int4 date;
794
+
795
+ mdy[0] = FIX2INT(rb_funcall(data, s_month, 0));
796
+ mdy[1] = FIX2INT(rb_funcall(data, s_day, 0));
797
+ mdy[2] = FIX2INT(rb_funcall(data, s_year, 0));
798
+ rmdyjul(mdy, &date);
799
+
800
+ var->sqldata = (char *)ALLOC(int4);
801
+ *((int4 *)var->sqldata) = date;
802
+ var->sqltype = CDATETYPE;
803
+ var->sqllen = sizeof(int4);
804
+ *var->sqlind = 0;
805
+ break;
806
+ }
807
+ if (klass == rb_cTime) {
808
+ char buffer[30];
809
+ short year, month, day, hour, minute, second;
810
+ int usec;
811
+ dtime_t *dt;
812
+
813
+ year = FIX2INT(rb_funcall(data, s_year, 0));
814
+ month = FIX2INT(rb_funcall(data, s_month, 0));
815
+ day = FIX2INT(rb_funcall(data, s_day, 0));
816
+ hour = FIX2INT(rb_funcall(data, s_hour, 0));
817
+ minute = FIX2INT(rb_funcall(data, s_min, 0));
818
+ second = FIX2INT(rb_funcall(data, s_sec, 0));
819
+ usec = FIX2INT(rb_funcall(data, s_usec, 0));
820
+
821
+ dt = ALLOC(dtime_t);
822
+
823
+ dt->dt_qual = TU_DTENCODE(TU_YEAR, TU_F5);
824
+ snprintf(buffer, sizeof(buffer), "%d-%d-%d %d:%d:%d.%d",
825
+ year, month, day, hour, minute, second, usec/10);
826
+ dtcvasc(buffer, dt);
827
+
828
+ var->sqldata = (char *)dt;
829
+ var->sqltype = CDTIMETYPE;
830
+ var->sqllen = sizeof(dtime_t);
831
+ *var->sqlind = 0;
832
+ break;
833
+ }
834
+ if (klass == rb_cSlob) {
835
+ slob_t *slob;
836
+
837
+ Data_Get_Struct(data, slob_t, slob);
838
+
839
+ var->sqldata = (char *)ALLOC(ifx_lo_t);
840
+ memcpy(var->sqldata, &slob->lo, sizeof(slob->lo));
841
+ var->sqltype = SQLUDTFIXED;
842
+ var->sqlxid = slob->type;
843
+ var->sqllen = sizeof(ifx_lo_t);
844
+ *var->sqlind = 0;
845
+ break;
846
+ }
847
+ if (rb_respond_to(data, s_read)) {
848
+ char *str;
849
+ loc_t *loc;
850
+ long len;
851
+
852
+ data = rb_funcall(data, s_read, 0);
853
+ data = StringValue(data);
854
+ str = RSTRING(data)->ptr;
855
+ len = RSTRING(data)->len;
856
+
857
+ loc = (loc_t *)ALLOC(loc_t);
858
+ byfill((char *)loc, sizeof(loc_t), 0);
859
+ loc->loc_loctype = LOCMEMORY;
860
+ loc->loc_buffer = (char *)ALLOC_N(char, len);
861
+ memcpy(loc->loc_buffer, str, len);
862
+ loc->loc_bufsize = loc->loc_size = len;
863
+
864
+ var->sqldata = (char *)loc;
865
+ var->sqltype = CLOCATORTYPE;
866
+ var->sqllen = sizeof(loc_t);
867
+ *var->sqlind = 0;
868
+ break;
869
+ }
870
+ {
871
+ VALUE str;
872
+ str = rb_check_string_type(data);
873
+ if (NIL_P(str)) {
874
+ data = rb_obj_as_string(data);
875
+ }
876
+ else {
877
+ data = str;
878
+ }
879
+ }
880
+ case T_STRING: {
881
+ char *str;
882
+ long len;
883
+
884
+ str = RSTRING(data)->ptr;
885
+ len = RSTRING(data)->len;
886
+ var->sqldata = ALLOC_N(char, len + 1);
887
+ memcpy(var->sqldata, str, len);
888
+ var->sqldata[len] = 0;
889
+ var->sqltype = CSTRINGTYPE;
890
+ var->sqllen = len;
891
+ *var->sqlind = 0;
892
+ break;
893
+ }
894
+ }
895
+ }
896
+ }
897
+
898
+ /*
899
+ * Returns an array or a hash of Ruby objects containing the record fetched.
900
+ */
901
+ static VALUE
902
+ make_result(cursor_t *c, VALUE record)
903
+ {
904
+ VALUE item;
905
+ register int i;
906
+ register struct sqlvar_struct *var;
907
+
908
+ var = c->daOutput->sqlvar;
909
+ for (i = 0; i < c->daOutput->sqld; i++, var++) {
910
+ if (*var->sqlind == -1) {
911
+ item = Qnil;
912
+ } else {
913
+ switch(var->sqltype) {
914
+ case SQLCHAR:
915
+ case SQLVCHAR:
916
+ case SQLNCHAR:
917
+ case SQLNVCHAR:
918
+ item = rb_str_new2(var->sqldata);
919
+ break;
920
+ case SQLSMINT:
921
+ item = INT2FIX(*(int2 *)var->sqldata);
922
+ break;
923
+ case SQLINT:
924
+ case SQLSERIAL:
925
+ item = INT2NUM(*(int4 *)var->sqldata);
926
+ break;
927
+ case SQLINT8:
928
+ case SQLSERIAL8:
929
+ INT82NUM((ifx_int8_t *)var->sqldata, item);
930
+ break;
931
+ case SQLSMFLOAT:
932
+ item = rb_float_new(*(float *)var->sqldata);
933
+ break;
934
+ case SQLFLOAT:
935
+ item = rb_float_new(*(double *)var->sqldata);
936
+ break;
937
+ case SQLDATE: {
938
+ VALUE year, month, day;
939
+ int2 mdy[3];
940
+
941
+ rjulmdy(*(int4 *)var->sqldata, mdy);
942
+ year = INT2FIX(mdy[2]);
943
+ month = INT2FIX(mdy[0]);
944
+ day = INT2FIX(mdy[1]);
945
+ item = rb_funcall(rb_cDate, s_new, 3, year, month, day);
946
+ break;
947
+ }
948
+ case SQLDTIME: {
949
+ register short qual;
950
+ short year, month, day, hour, minute, second;
951
+ int usec;
952
+ dtime_t *dt;
953
+ register char *dgts;
954
+
955
+ month = day = 1;
956
+ year = hour = minute = second = usec = 0;
957
+ dt = (dtime_t *)var->sqldata;
958
+ dgts = dt->dt_dec.dec_dgts;
959
+
960
+ qual = TU_START(dt->dt_qual);
961
+ for (; qual <= TU_END(dt->dt_qual); qual++) {
962
+ switch(qual) {
963
+ case TU_YEAR:
964
+ year = 100**dgts++;
965
+ year += *dgts++;
966
+ break;
967
+ case TU_MONTH:
968
+ month = *dgts++;
969
+ break;
970
+ case TU_DAY:
971
+ day = *dgts++;
972
+ break;
973
+ case TU_HOUR:
974
+ hour = *dgts++;
975
+ break;
976
+ case TU_MINUTE:
977
+ minute = *dgts++;
978
+ break;
979
+ case TU_SECOND:
980
+ second = *dgts++;
981
+ break;
982
+ case TU_F1:
983
+ usec = 10000**dgts++;
984
+ break;
985
+ case TU_F3:
986
+ usec += 100**dgts++;
987
+ break;
988
+ case TU_F5:
989
+ usec += *dgts++;
990
+ break;
991
+ }
992
+ }
993
+
994
+ item = rb_funcall(rb_cTime, s_utc, 7,
995
+ INT2FIX(year), INT2FIX(month), INT2FIX(day),
996
+ INT2FIX(hour), INT2FIX(minute), INT2FIX(second),
997
+ INT2FIX(usec));
998
+
999
+ /* Clean the buffer for DATETIME columns because
1000
+ * ESQL/C leaves the previous content when a
1001
+ * a time field is zero.
1002
+ */
1003
+ memset(dt, 0, sizeof(dtime_t));
1004
+ break;
1005
+ }
1006
+ case SQLDECIMAL:
1007
+ case SQLMONEY: {
1008
+ double dblValue;
1009
+ dectodbl((dec_t *)var->sqldata, &dblValue);
1010
+ item = rb_float_new(dblValue);
1011
+ break;
1012
+ }
1013
+ case SQLBOOL:
1014
+ item = var->sqldata[0]? Qtrue: Qfalse;
1015
+ break;
1016
+ case SQLBYTES:
1017
+ case SQLTEXT: {
1018
+ loc_t *loc;
1019
+ loc = (loc_t *)var->sqldata;
1020
+ item = rb_str_new(loc->loc_buffer, loc->loc_size);
1021
+ break;
1022
+ }
1023
+ case SQLUDTFIXED:
1024
+ if (ISSMARTBLOB(var->sqltype, var->sqlxid)) {
1025
+ slob_t *slob;
1026
+
1027
+ item = slob_alloc(rb_cSlob);
1028
+ Data_Get_Struct(item, slob_t, slob);
1029
+ memcpy(&slob->lo, var->sqldata, sizeof(ifx_lo_t));
1030
+ slob->type = var->sqlxid;
1031
+ break;
1032
+ }
1033
+ case SQLSET:
1034
+ case SQLMULTISET:
1035
+ case SQLLIST:
1036
+ case SQLROW:
1037
+ case SQLCOLLECTION:
1038
+ case SQLROWREF:
1039
+ case SQLUDTVAR:
1040
+ case SQLREFSER8:
1041
+ case SQLLVARCHAR:
1042
+ case SQLSENDRECV:
1043
+ case SQLIMPEXP:
1044
+ case SQLIMPEXPBIN:
1045
+ case SQLUNKNOWN:
1046
+ default:
1047
+ item = Qnil;
1048
+ break;
1049
+ }
1050
+ }
1051
+ if (BUILTIN_TYPE(record) == T_ARRAY) {
1052
+ rb_ary_store(record, i, item);
1053
+ }
1054
+ else {
1055
+ rb_hash_aset(record, RARRAY(c->field_names)->ptr[i], item);
1056
+ }
1057
+ }
1058
+ return record;
1059
+ }
1060
+
1061
+ /* module Informix -------------------------------------------------------- */
1062
+
1063
+ /*
1064
+ * call-seq:
1065
+ * Informix.connect(dbname, user = nil, password = nil) => database
1066
+ *
1067
+ * Returns a <code>Database</code> object connected to <i>dbname</i> as
1068
+ * <i>user</i> with <i>password</i>. If these are not given, connects to
1069
+ * <i>dbname</i> as the current user.
1070
+ */
1071
+ static VALUE
1072
+ informix_connect(int argc, VALUE *argv, VALUE self)
1073
+ {
1074
+ return rb_class_new_instance(argc, argv, rb_cDatabase);
1075
+ }
1076
+
1077
+
1078
+ /* class Database --------------------------------------------------------- */
1079
+
1080
+ static void
1081
+ database_free(void *p)
1082
+ {
1083
+ EXEC SQL begin declare section;
1084
+ char *did;
1085
+ EXEC SQL end declare section;
1086
+
1087
+ did = p;
1088
+ EXEC SQL disconnect :did;
1089
+ if (currentdid == did)
1090
+ currentdid = NULL;
1091
+ xfree(p);
1092
+ }
1093
+
1094
+ static VALUE
1095
+ database_alloc(VALUE klass)
1096
+ {
1097
+ char *did;
1098
+
1099
+ did = ALLOC_N(char, IDSIZE);
1100
+ did[0] = 0;
1101
+ return Data_Wrap_Struct(klass, 0, database_free, did);
1102
+ }
1103
+
1104
+ /*
1105
+ * call-seq:
1106
+ * Database.new(dbname, user = nil, password = nil) => database
1107
+ *
1108
+ * Returns a <code>Database</code> object connected to <i>dbname</i> as
1109
+ * <i>user</i> with <i>password</i>. If these are not given, connects to
1110
+ * <i>dbname</i> as the current user.
1111
+ */
1112
+ static VALUE
1113
+ database_initialize(int argc, VALUE *argv, VALUE self)
1114
+ {
1115
+ VALUE arg[3];
1116
+
1117
+ EXEC SQL begin declare section;
1118
+ char *dbname, *user = NULL, *pass = NULL, *did;
1119
+ EXEC SQL end declare section;
1120
+
1121
+ rb_scan_args(argc, argv, "12", &arg[0], &arg[1], &arg[2]);
1122
+
1123
+ if (NIL_P(arg[0]))
1124
+ rb_raise(rb_eRuntimeError, "A database name must be specified");
1125
+
1126
+ Data_Get_Struct(self, char, did);
1127
+
1128
+ dbname = StringValueCStr(arg[0]);
1129
+ snprintf(did, IDSIZE, "DB%lX", self);
1130
+
1131
+ if (!NIL_P(arg[1]))
1132
+ user = StringValueCStr(arg[1]);
1133
+
1134
+ if (!NIL_P(arg[2]))
1135
+ pass = StringValueCStr(arg[2]);
1136
+
1137
+ if (user && pass)
1138
+ EXEC SQL connect to :dbname as :did user :user
1139
+ using :pass with concurrent transaction;
1140
+ else
1141
+ EXEC SQL connect to :dbname as :did with concurrent transaction;
1142
+
1143
+ if (SQLCODE < 0)
1144
+ rb_raise(rb_eRuntimeError, "Informix Error: %d", SQLCODE);
1145
+
1146
+ currentdid = did;
1147
+
1148
+ return self;
1149
+ }
1150
+
1151
+ /*
1152
+ * call-seq:
1153
+ * db.close => db
1154
+ *
1155
+ * Disconnects <i>db</i> and returns __self__
1156
+ */
1157
+ static VALUE
1158
+ database_close(VALUE self)
1159
+ {
1160
+ EXEC SQL begin declare section;
1161
+ char *did;
1162
+ EXEC SQL end declare section;
1163
+
1164
+ Data_Get_Struct(self, char, did);
1165
+ EXEC SQL disconnect :did;
1166
+ if (did == currentdid)
1167
+ currentdid = NULL;
1168
+
1169
+ return self;
1170
+ }
1171
+
1172
+ /*
1173
+ * call-seq:
1174
+ * db.immediate(query) => fixnum
1175
+ *
1176
+ * Executes <i>query</i> and returns the number of rows affected.
1177
+ * <i>query</i> must not return rows. Executes efficiently any
1178
+ * non-parameterized or DQL statement.
1179
+ */
1180
+
1181
+ static VALUE
1182
+ database_immediate(VALUE self, VALUE arg)
1183
+ {
1184
+ EXEC SQL begin declare section;
1185
+ char *query, *did;
1186
+ EXEC SQL end declare section;
1187
+
1188
+ Data_Get_Struct(self, char, did);
1189
+
1190
+ if (currentdid != did) {
1191
+ EXEC SQL set connection :did;
1192
+ if (SQLCODE < 0)
1193
+ rb_raise(rb_eRuntimeError, "Informix Error: %d", SQLCODE);
1194
+ currentdid = did;
1195
+ }
1196
+
1197
+ query = StringValueCStr(arg);
1198
+ EXEC SQL execute immediate :query;
1199
+ if (SQLCODE < 0)
1200
+ rb_raise(rb_eRuntimeError, "Informix Error: %d", SQLCODE);
1201
+
1202
+ return INT2FIX(sqlca.sqlerrd[2]);
1203
+ }
1204
+
1205
+ /*
1206
+ * call-seq:
1207
+ * db.rollback => db
1208
+ *
1209
+ * Rolls back a transaction and returns __self__.
1210
+ */
1211
+ static VALUE
1212
+ database_rollback(VALUE self)
1213
+ {
1214
+ EXEC SQL begin declare section;
1215
+ char *did;
1216
+ EXEC SQL end declare section;
1217
+
1218
+ Data_Get_Struct(self, char, did);
1219
+
1220
+ if (currentdid != did) {
1221
+ EXEC SQL set connection :did;
1222
+ if (SQLCODE < 0)
1223
+ rb_raise(rb_eRuntimeError, "Informix Error: %d", SQLCODE);
1224
+ currentdid = did;
1225
+ }
1226
+
1227
+ EXEC SQL rollback;
1228
+ return self;
1229
+ }
1230
+
1231
+ /*
1232
+ * call-seq:
1233
+ * db.commit => db
1234
+ *
1235
+ * Commits a transaction and returns __self__.
1236
+ */
1237
+ static VALUE
1238
+ database_commit(VALUE self)
1239
+ {
1240
+ EXEC SQL begin declare section;
1241
+ char *did;
1242
+ EXEC SQL end declare section;
1243
+
1244
+ Data_Get_Struct(self, char, did);
1245
+
1246
+ if (currentdid != did) {
1247
+ EXEC SQL set connection :did;
1248
+ if (SQLCODE < 0)
1249
+ rb_raise(rb_eRuntimeError, "Informix Error: %d", SQLCODE);
1250
+ currentdid = did;
1251
+ }
1252
+
1253
+ EXEC SQL commit;
1254
+ return self;
1255
+ }
1256
+
1257
+ static VALUE
1258
+ database_transfail(VALUE self)
1259
+ {
1260
+ database_rollback(self);
1261
+ return Qundef;
1262
+ }
1263
+
1264
+ /*
1265
+ * call-seq:
1266
+ * db.transaction {|db| block } => db
1267
+ *
1268
+ * Opens a transaction and executes <i>block</i>, passing __self__ as parameter.
1269
+ * If an exception is raised, the transaction is rolled back. It is commited
1270
+ * otherwise.
1271
+ *
1272
+ * Returns __self__.
1273
+ */
1274
+ static VALUE
1275
+ database_transaction(VALUE self)
1276
+ {
1277
+ VALUE ret;
1278
+ EXEC SQL begin declare section;
1279
+ char *did;
1280
+ EXEC SQL end declare section;
1281
+
1282
+ Data_Get_Struct(self, char, did);
1283
+
1284
+ if (currentdid != did) {
1285
+ EXEC SQL set connection :did;
1286
+ if (SQLCODE < 0)
1287
+ rb_raise(rb_eRuntimeError, "Informix Error: %d", SQLCODE);
1288
+ currentdid = did;
1289
+ }
1290
+
1291
+ EXEC SQL commit;
1292
+
1293
+ EXEC SQL begin work;
1294
+ ret = rb_rescue(rb_yield, self, database_transfail, self);
1295
+ if (ret == Qundef)
1296
+ rb_raise(rb_eRuntimeError, "Transaction rolled back");
1297
+ EXEC SQL commit;
1298
+ return self;
1299
+ }
1300
+
1301
+ /*
1302
+ * call-seq:
1303
+ * db.prepare(query) => statement
1304
+ *
1305
+ * Returns a <code>Statement</code> object based on <i>query</i>.
1306
+ * <i>query</i> may contain '?' placeholders for input parameters;
1307
+ * it must not be a query returning more than one row
1308
+ * (use <code>Database#cursor</code> instead.)
1309
+ */
1310
+ static VALUE
1311
+ database_prepare(VALUE self, VALUE query)
1312
+ {
1313
+ VALUE argv[2];
1314
+
1315
+ argv[0] = self; argv[1] = query;
1316
+ return rb_class_new_instance(2, argv, rb_cStatement);
1317
+ }
1318
+
1319
+ /*
1320
+ * call-seq:
1321
+ * db.cursor(query, options = nil) => cursor
1322
+ *
1323
+ * Returns a <code>Cursor</code> object based on <i>query</i>.
1324
+ * <i>query</i> may contain '?' placeholders for input parameters.
1325
+ *
1326
+ * <i>options</i> must be a hash with the following possible keys:
1327
+ *
1328
+ * :scroll => true or false
1329
+ * :hold => true or false
1330
+ *
1331
+ */
1332
+ static VALUE
1333
+ database_cursor(int argc, VALUE *argv, VALUE self)
1334
+ {
1335
+ VALUE arg[3];
1336
+
1337
+ arg[0] = self;
1338
+ rb_scan_args(argc, argv, "11", &arg[1], &arg[2]);
1339
+ return rb_class_new_instance(3, arg, rb_cCursor);
1340
+ }
1341
+
1342
+ /*
1343
+ * call-seq:
1344
+ * db.columns(tablename) => array
1345
+ *
1346
+ * Returns an array with information for every column of the given table.
1347
+ */
1348
+ static VALUE
1349
+ database_columns(VALUE self, VALUE tablename)
1350
+ {
1351
+ VALUE v, column, result;
1352
+ char *stype;
1353
+ static char *stypes[] = {
1354
+ "CHAR", "SMALLINT", "INTEGER", "FLOAT", "SMALLFLOAT", "DECIMAL",
1355
+ "SERIAL", "DATE", "MONEY", "NULL", "DATETIME", "BYTE",
1356
+ "TEXT", "VARCHAR", "INTERVAL", "NCHAR", "NVARCHAR", "INT8",
1357
+ "SERIAL8", "SET", "MULTISET", "LIST", "UNNAMED ROW", "NAMED ROW",
1358
+ "VARIABLE-LENGTH OPAQUE TYPE"
1359
+ };
1360
+
1361
+ static char *qualifiers[] = {
1362
+ "YEAR", "MONTH", "DAY", "HOUR", "MINUTE", "SECOND"
1363
+ };
1364
+
1365
+ EXEC SQL begin declare section;
1366
+ char *did;
1367
+ char *tabname;
1368
+ int tabid, xid;
1369
+ varchar colname[129];
1370
+ short coltype, collength;
1371
+ char deftype[2];
1372
+ varchar defvalue[257];
1373
+ EXEC SQL end declare section;
1374
+
1375
+ Data_Get_Struct(self, char, did);
1376
+
1377
+ if (currentdid != did) {
1378
+ EXEC SQL set connection :did;
1379
+ if (SQLCODE < 0)
1380
+ rb_raise(rb_eRuntimeError, "Informix Error: %d", SQLCODE);
1381
+ currentdid = did;
1382
+ }
1383
+
1384
+ tabname = StringValueCStr(tablename);
1385
+
1386
+ EXEC SQL select tabid into :tabid from systables where tabname = :tabname;
1387
+
1388
+ if (SQLCODE == SQLNOTFOUND)
1389
+ rb_raise(rb_eRuntimeError, "Table '%s' doesn't exist", tabname);
1390
+
1391
+ result = rb_ary_new();
1392
+
1393
+ EXEC SQL declare cur cursor for
1394
+ select colname, coltype, collength, extended_id, type, default, c.colno
1395
+ from syscolumns c, outer sysdefaults d
1396
+ where c.tabid = :tabid and c.tabid = d.tabid and c.colno = d.colno
1397
+ order by c.colno;
1398
+
1399
+ if (SQLCODE < 0)
1400
+ rb_raise(rb_eRuntimeError, "Informix Error: %d", SQLCODE);
1401
+
1402
+ EXEC SQL open cur;
1403
+ if (SQLCODE < 0)
1404
+ rb_raise(rb_eRuntimeError, "Informix Error: %d", SQLCODE);
1405
+
1406
+ for(;;) {
1407
+ EXEC SQL fetch cur into :colname, :coltype, :collength, :xid,
1408
+ :deftype, :defvalue;
1409
+ if (SQLCODE < 0)
1410
+ rb_raise(rb_eRuntimeError, "Informix Error: %d", SQLCODE);
1411
+
1412
+ if (SQLCODE == SQLNOTFOUND)
1413
+ break;
1414
+
1415
+ column = rb_hash_new();
1416
+ rb_hash_aset(column, sym_name, rb_str_new2(colname));
1417
+ rb_hash_aset(column, sym_type, INT2FIX(coltype));
1418
+ rb_hash_aset(column, sym_nullable, coltype&0x100? Qfalse: Qtrue);
1419
+ rb_hash_aset(column, sym_xid, INT2FIX(xid));
1420
+
1421
+ if ((coltype&0xFF) < 23) {
1422
+ stype = coltype == 4118? stypes[23]: stypes[coltype&0xFF];
1423
+ }
1424
+ else {
1425
+ stype = stypes[24];
1426
+ }
1427
+ rb_hash_aset(column, sym_stype, rb_str_new2(stype));
1428
+ rb_hash_aset(column, sym_length, INT2FIX(collength));
1429
+
1430
+ switch(coltype&0xFF) {
1431
+ case SQLVCHAR:
1432
+ case SQLNVCHAR:
1433
+ case SQLMONEY:
1434
+ case SQLDECIMAL:
1435
+ rb_hash_aset(column, sym_precision, INT2FIX(collength >> 8));
1436
+ rb_hash_aset(column, sym_scale, INT2FIX(collength&0xFF));
1437
+ break;
1438
+ case SQLDATE:
1439
+ case SQLDTIME:
1440
+ case SQLINTERVAL:
1441
+ rb_hash_aset(column, sym_length, INT2FIX(collength >> 8));
1442
+ rb_hash_aset(column, sym_precision, INT2FIX((collength&0xF0) >> 4));
1443
+ rb_hash_aset(column, sym_scale, INT2FIX(collength&0xF));
1444
+ break;
1445
+ default:
1446
+ rb_hash_aset(column, sym_precision, INT2FIX(0));
1447
+ rb_hash_aset(column, sym_scale, INT2FIX(0));
1448
+ }
1449
+
1450
+ if (!deftype[0]) {
1451
+ v = Qnil;
1452
+ }
1453
+ else {
1454
+ switch(deftype[0]) {
1455
+ case 'C': {
1456
+ char current[28];
1457
+ snprintf(current, sizeof(current), "CURRENT %s TO %s",
1458
+ qualifiers[(collength&0xF0) >> 5],
1459
+ qualifiers[(collength&0xF)>>1]);
1460
+ v = rb_str_new2(current);
1461
+ break;
1462
+ }
1463
+ case 'L':
1464
+ switch (coltype & 0xFF) {
1465
+ case SQLCHAR:
1466
+ case SQLNCHAR:
1467
+ case SQLVCHAR:
1468
+ case SQLNVCHAR:
1469
+ v = rb_str_new2(defvalue);
1470
+ break;
1471
+ default: {
1472
+ char *s = defvalue;
1473
+ while(*s++ != ' ');
1474
+ if ((coltype&0xFF) == SQLFLOAT ||
1475
+ (coltype&0xFF) == SQLSMFLOAT ||
1476
+ (coltype&0xFF) == SQLMONEY ||
1477
+ (coltype&0xFF) == SQLDECIMAL)
1478
+ v = rb_float_new(atof(s));
1479
+ else
1480
+ v = LONG2FIX(atol(s));
1481
+ }
1482
+ }
1483
+ break;
1484
+ case 'N':
1485
+ v = rb_str_new2("NULL");
1486
+ break;
1487
+ case 'T':
1488
+ v = rb_str_new2("today");
1489
+ break;
1490
+ case 'U':
1491
+ v = rb_str_new2("user");
1492
+ break;
1493
+ case 'S':
1494
+ default: /* XXX */
1495
+ v = Qnil;
1496
+ }
1497
+ }
1498
+ rb_hash_aset(column, sym_default, v);
1499
+ rb_ary_push(result, column);
1500
+ }
1501
+
1502
+ EXEC SQL close cur;
1503
+ EXEC SQL free cur;
1504
+
1505
+ return result;
1506
+ }
1507
+
1508
+ /* class Statement ------------------------------------------------------- */
1509
+
1510
+ static void
1511
+ statement_mark(cursor_t *c)
1512
+ {
1513
+ rb_gc_mark(c->db);
1514
+ if (c->array)
1515
+ rb_gc_mark(c->array);
1516
+ if (c->hash)
1517
+ rb_gc_mark(c->hash);
1518
+ if (c->field_names)
1519
+ rb_gc_mark(c->field_names);
1520
+ }
1521
+
1522
+ static void
1523
+ statement_free(void *p)
1524
+ {
1525
+ EXEC SQL begin declare section;
1526
+ char *sid, *did;
1527
+ EXEC SQL end declare section;
1528
+
1529
+ free_input_slots(p);
1530
+ free_output_slots(p);
1531
+
1532
+ did = ((cursor_t *)p)->database_id;
1533
+ if (currentdid != did) {
1534
+ EXEC SQL set connection :did;
1535
+ if (SQLCODE < 0)
1536
+ goto exit;
1537
+ currentdid = did;
1538
+ }
1539
+
1540
+ sid = ((cursor_t *)p)->stmt_id;
1541
+ EXEC SQL free :sid;
1542
+
1543
+ exit:
1544
+ xfree(p);
1545
+ }
1546
+
1547
+ static VALUE
1548
+ statement_alloc(VALUE klass)
1549
+ {
1550
+ cursor_t *c;
1551
+
1552
+ c = ALLOC(cursor_t);
1553
+ memset(c, 0, sizeof(cursor_t));
1554
+ return Data_Wrap_Struct(klass, statement_mark, statement_free, c);
1555
+ }
1556
+
1557
+ /*
1558
+ * call-seq:
1559
+ * Statement.new(database, query) => statement
1560
+ *
1561
+ * Prepares <i>query</i> in the context of <i>database</i> and returns
1562
+ * a <code>Statement</code> object.
1563
+ */
1564
+ static VALUE
1565
+ statement_initialize(VALUE self, VALUE db, VALUE query)
1566
+ {
1567
+ struct sqlda *output;
1568
+ cursor_t *c;
1569
+ EXEC SQL begin declare section;
1570
+ char *c_query, *sid, *did;
1571
+ EXEC SQL end declare section;
1572
+
1573
+ Data_Get_Struct(db, char, did);
1574
+ if (currentdid != did) {
1575
+ EXEC SQL set connection :did;
1576
+ if (SQLCODE < 0)
1577
+ rb_raise(rb_eRuntimeError, "Informix Error: %d", SQLCODE);
1578
+ currentdid = did;
1579
+ }
1580
+
1581
+ Data_Get_Struct(self, cursor_t, c);
1582
+ c->db = db;
1583
+ c->database_id = did;
1584
+ output = c->daOutput;
1585
+ snprintf(c->stmt_id, sizeof(c->stmt_id), "STMT%lX", self);
1586
+ sid = c->stmt_id;
1587
+ c_query = StringValueCStr(query);
1588
+
1589
+ EXEC SQL prepare :sid from :c_query;
1590
+ if (SQLCODE < 0)
1591
+ rb_raise(rb_eRuntimeError, "Informix Error: %d", SQLCODE);
1592
+
1593
+ alloc_input_slots(c, c_query);
1594
+ EXEC SQL describe :sid into output;
1595
+ c->daOutput = output;
1596
+
1597
+ c->is_select = (SQLCODE == 0 || SQLCODE == SQ_EXECPROC);
1598
+
1599
+ if (c->is_select)
1600
+ alloc_output_slots(c);
1601
+ else {
1602
+ xfree(c->daOutput);
1603
+ c->daOutput = NULL;
1604
+ }
1605
+
1606
+ return self;
1607
+ }
1608
+
1609
+
1610
+ /*
1611
+ * call-seq:
1612
+ * stmt[*params] => fixnum or hash
1613
+ *
1614
+ * Executes the previously prepared statement, binding <i>params</i> as
1615
+ * input parameters.
1616
+ *
1617
+ * Returns the record retrieved, in the case of a singleton select, or the
1618
+ * number of rows affected, in the case of any other statement.
1619
+ */
1620
+ static VALUE
1621
+ statement_call(int argc, VALUE *argv, VALUE self)
1622
+ {
1623
+ struct sqlda *input, *output;
1624
+ cursor_t *c;
1625
+ EXEC SQL begin declare section;
1626
+ char *sid, *did;
1627
+ EXEC SQL end declare section;
1628
+
1629
+ Data_Get_Struct(self, cursor_t, c);
1630
+
1631
+ did = c->database_id;
1632
+ if (currentdid != did) {
1633
+ EXEC SQL set connection :did;
1634
+ if (SQLCODE < 0)
1635
+ rb_raise(rb_eRuntimeError, "Informix Error: %d", SQLCODE);
1636
+ currentdid = did;
1637
+ }
1638
+
1639
+ output = c->daOutput;
1640
+ input = &c->daInput;
1641
+ sid = c->stmt_id;
1642
+
1643
+ if (argc != input->sqld)
1644
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)",
1645
+ argc, input->sqld);
1646
+
1647
+ if (c->is_select) {
1648
+ if (argc) {
1649
+ bind_input_params(c, argv);
1650
+ EXEC SQL execute :sid into descriptor output
1651
+ using descriptor input;
1652
+ clean_input_slots(c);
1653
+ }
1654
+ else
1655
+ EXEC SQL execute :sid into descriptor output;
1656
+
1657
+ if (SQLCODE < 0)
1658
+ rb_raise(rb_eRuntimeError, "Informix Error: %d", SQLCODE);
1659
+
1660
+ if (SQLCODE == SQLNOTFOUND)
1661
+ return Qnil;
1662
+ return make_result(c, rb_hash_new());
1663
+ }
1664
+ else {
1665
+ if (argc) {
1666
+ bind_input_params(c, argv);
1667
+ EXEC SQL execute :sid using descriptor input;
1668
+ clean_input_slots(c);
1669
+ }
1670
+ else
1671
+ EXEC SQL execute :sid;
1672
+ }
1673
+ if (SQLCODE < 0)
1674
+ rb_raise(rb_eRuntimeError, "Informix Error: %d", SQLCODE);
1675
+
1676
+ return INT2FIX(sqlca.sqlerrd[2]);
1677
+ }
1678
+
1679
+ /*
1680
+ * call-seq:
1681
+ * stmt.drop
1682
+ *
1683
+ * Frees the statement and the memory associated with it.
1684
+ */
1685
+ static VALUE
1686
+ statement_drop(VALUE self)
1687
+ {
1688
+ cursor_t *c;
1689
+ EXEC SQL begin declare section;
1690
+ char *sid, *did;
1691
+ EXEC SQL end declare section;
1692
+
1693
+ Data_Get_Struct(self, cursor_t, c);
1694
+ free_input_slots(c);
1695
+ free_output_slots(c);
1696
+
1697
+ did = c->database_id;
1698
+ if (currentdid != did) {
1699
+ EXEC SQL set connection :did;
1700
+ if (SQLCODE < 0)
1701
+ return Qnil;
1702
+ currentdid = did;
1703
+ }
1704
+ sid = c->stmt_id;
1705
+ EXEC SQL free :sid;
1706
+
1707
+ return Qnil;
1708
+ }
1709
+
1710
+
1711
+ /* module SequentialCursor ----------------------------------------------- */
1712
+
1713
+ /* Decides whether to use an Array or a Hash, and instantiate a new
1714
+ * object or reuse an existing one.
1715
+ */
1716
+ #define RECORD(c, type, bang, record) do {\
1717
+ if (type == T_ARRAY) {\
1718
+ if (bang) {\
1719
+ if (!c->array)\
1720
+ c->array = rb_ary_new2(c->daOutput->sqld);\
1721
+ record = c->array;\
1722
+ }\
1723
+ else\
1724
+ record = rb_ary_new2(c->daOutput->sqld);\
1725
+ }\
1726
+ else {\
1727
+ if (bang) {\
1728
+ if (!c->hash)\
1729
+ c->hash = rb_hash_new();\
1730
+ record = c->hash;\
1731
+ }\
1732
+ else\
1733
+ record = rb_hash_new();\
1734
+ }\
1735
+ }while(0)
1736
+
1737
+ /*
1738
+ * Base function for fetch* methods, except *_many
1739
+ */
1740
+ static VALUE
1741
+ fetch(VALUE self, VALUE type, int bang)
1742
+ {
1743
+ EXEC SQL begin declare section;
1744
+ char *cid, *did;
1745
+ EXEC SQL end declare section;
1746
+ cursor_t *c;
1747
+ struct sqlda *output;
1748
+ VALUE record;
1749
+
1750
+ Data_Get_Struct(self, cursor_t, c);
1751
+ if (!c->is_open)
1752
+ rb_raise(rb_eRuntimeError, "Open the cursor object first");
1753
+
1754
+ did = c->database_id;
1755
+ if (currentdid != did) {
1756
+ EXEC SQL set connection :did;
1757
+ if (SQLCODE < 0)
1758
+ rb_raise(rb_eRuntimeError, "Informix Error: %d", SQLCODE);
1759
+ currentdid = did;
1760
+ }
1761
+
1762
+ output = c->daOutput;
1763
+ cid = c->cursor_id;
1764
+
1765
+ EXEC SQL fetch :cid using descriptor output;
1766
+ if (SQLCODE < 0)
1767
+ rb_raise(rb_eRuntimeError, "Informix Error: %d", SQLCODE);
1768
+
1769
+ if (SQLCODE == SQLNOTFOUND)
1770
+ return Qnil;
1771
+
1772
+ RECORD(c, type, bang, record);
1773
+ return make_result(c, record);
1774
+ }
1775
+
1776
+ /*
1777
+ * call-seq:
1778
+ * cursor.fetch => array or nil
1779
+ *
1780
+ * Fetches the next record.
1781
+ *
1782
+ * Returns the record fetched as an array, or nil if there are no
1783
+ * records left.
1784
+ */
1785
+ static VALUE
1786
+ seqcur_fetch(VALUE self)
1787
+ {
1788
+ return fetch(self, T_ARRAY, 0);
1789
+ }
1790
+
1791
+ /*
1792
+ * call-seq:
1793
+ * cursor.fetch! => array or nil
1794
+ *
1795
+ * Fetches the next record, storing it in the same Array object every time
1796
+ * it is called.
1797
+ *
1798
+ * Returns the record fetched as an array, or nil if there are no
1799
+ * records left.
1800
+ */
1801
+ static VALUE
1802
+ seqcur_fetch_bang(VALUE self)
1803
+ {
1804
+ return fetch(self, T_ARRAY, 1);
1805
+ }
1806
+
1807
+ /*
1808
+ * call-seq:
1809
+ * cursor.fetch_hash => hash or nil
1810
+ *
1811
+ * Fetches the next record.
1812
+ *
1813
+ * Returns the record fetched as a hash, or nil if there are no
1814
+ * records left.
1815
+ */
1816
+ static VALUE
1817
+ seqcur_fetch_hash(VALUE self)
1818
+ {
1819
+ return fetch(self, T_HASH, 0);
1820
+ }
1821
+
1822
+ /*
1823
+ * call-seq:
1824
+ * cursor.fetch_hash! => hash or nil
1825
+ *
1826
+ * Fetches the next record, storing it in the same Hash object every time
1827
+ * it is called.
1828
+ *
1829
+ * Returns the record fetched as a hash, or nil if there are no
1830
+ * records left.
1831
+ */
1832
+ static VALUE
1833
+ seqcur_fetch_hash_bang(VALUE self)
1834
+ {
1835
+ return fetch(self, T_HASH, 1);
1836
+ }
1837
+
1838
+ /*
1839
+ * Base function for fetch*_many, fetch*_all and each_by methods
1840
+ */
1841
+ static VALUE
1842
+ fetch_many(VALUE self, VALUE n, VALUE type)
1843
+ {
1844
+ EXEC SQL begin declare section;
1845
+ char *cid, *did;
1846
+ EXEC SQL end declare section;
1847
+ cursor_t *c;
1848
+ struct sqlda *output;
1849
+ VALUE record, records;
1850
+ register long i, max;
1851
+ register int all = n == Qnil;
1852
+
1853
+ Data_Get_Struct(self, cursor_t, c);
1854
+ if (!c->is_open)
1855
+ rb_raise(rb_eRuntimeError, "Open the cursor object first");
1856
+
1857
+ did = c->database_id;
1858
+ if (currentdid != did) {
1859
+ EXEC SQL set connection :did;
1860
+ if (SQLCODE < 0)
1861
+ rb_raise(rb_eRuntimeError, "Informix Error: %d", SQLCODE);
1862
+ currentdid = did;
1863
+ }
1864
+
1865
+ output = c->daOutput;
1866
+ cid = c->cursor_id;
1867
+
1868
+ if (!all) {
1869
+ max = FIX2LONG(n);
1870
+ records = rb_ary_new2(max);
1871
+ }
1872
+ else {
1873
+ records = rb_ary_new();
1874
+ }
1875
+
1876
+ for(i = 0; all || i < max; i++) {
1877
+ EXEC SQL fetch :cid using descriptor output;
1878
+ if (SQLCODE < 0)
1879
+ rb_raise(rb_eRuntimeError, "Informix Error: %d", SQLCODE);
1880
+
1881
+ if (SQLCODE == SQLNOTFOUND)
1882
+ break;
1883
+
1884
+ if (type == T_ARRAY)
1885
+ record = rb_ary_new2(c->daOutput->sqld);
1886
+ else
1887
+ record = rb_hash_new();
1888
+ rb_ary_store(records, i, make_result(c, record));
1889
+ }
1890
+
1891
+ return records;
1892
+ }
1893
+
1894
+ /*
1895
+ * call-seq:
1896
+ * cursor.fetch_many(n) => array
1897
+ *
1898
+ * Reads at most <i>n</i> records.
1899
+ *
1900
+ * Returns the records read as an array of arrays
1901
+ */
1902
+ static VALUE
1903
+ seqcur_fetch_many(VALUE self, VALUE n)
1904
+ {
1905
+ return fetch_many(self, n, T_ARRAY);
1906
+ }
1907
+
1908
+ /*
1909
+ * call-seq:
1910
+ * cursor.fetch_hash_many(n) => array
1911
+ *
1912
+ * Reads at most <i>n</i> records.
1913
+ * Returns the records read as an array of hashes.
1914
+ */
1915
+ static VALUE
1916
+ seqcur_fetch_hash_many(VALUE self, VALUE n)
1917
+ {
1918
+ return fetch_many(self, n, T_HASH);
1919
+ }
1920
+
1921
+ /*
1922
+ * call-seq:
1923
+ * cursor.fetch_all => array
1924
+ *
1925
+ * Returns all the records left as an array of arrays
1926
+ */
1927
+ static VALUE
1928
+ seqcur_fetch_all(VALUE self)
1929
+ {
1930
+ return fetch_many(self, Qnil, T_ARRAY);
1931
+ }
1932
+
1933
+ /*
1934
+ * call-seq:
1935
+ * cursor.fetch_hash_all => array
1936
+ *
1937
+ * Returns all the records left as an array of hashes
1938
+ */
1939
+ static VALUE
1940
+ seqcur_fetch_hash_all(VALUE self)
1941
+ {
1942
+ return fetch_many(self, Qnil, T_HASH);
1943
+ }
1944
+
1945
+ /*
1946
+ * Base function for each* methods, except each*_by
1947
+ */
1948
+ static VALUE
1949
+ each(VALUE self, VALUE type, int bang)
1950
+ {
1951
+ cursor_t *c;
1952
+ EXEC SQL begin declare section;
1953
+ char *cid, *did;
1954
+ EXEC SQL end declare section;
1955
+ struct sqlda *output;
1956
+ VALUE record;
1957
+
1958
+ Data_Get_Struct(self, cursor_t, c);
1959
+ if (!c->is_open)
1960
+ rb_raise(rb_eRuntimeError, "Open the cursor object first");
1961
+
1962
+ did = c->database_id;
1963
+ if (currentdid != did) {
1964
+ EXEC SQL set connection :did;
1965
+ if (SQLCODE < 0)
1966
+ rb_raise(rb_eRuntimeError, "Informix Error: %d", SQLCODE);
1967
+ currentdid = did;
1968
+ }
1969
+
1970
+ output = c->daOutput;
1971
+ cid = c->cursor_id;
1972
+
1973
+ for(;;) {
1974
+ EXEC SQL fetch :cid using descriptor output;
1975
+ if (SQLCODE < 0)
1976
+ rb_raise(rb_eRuntimeError, "Informix Error: %d", SQLCODE);
1977
+
1978
+ if (SQLCODE == SQLNOTFOUND)
1979
+ return self;
1980
+ RECORD(c, type, bang, record);
1981
+ rb_yield(make_result(c, record));
1982
+ }
1983
+ }
1984
+
1985
+ /*
1986
+ * Base function for each*_by methods
1987
+ */
1988
+ static VALUE
1989
+ each_by(VALUE self, VALUE n, VALUE type)
1990
+ {
1991
+ VALUE records;
1992
+
1993
+ for(;;) {
1994
+ records = fetch_many(self, n, type);
1995
+ if (RARRAY(records)->len == 0)
1996
+ return self;
1997
+ rb_yield(records);
1998
+ }
1999
+ }
2000
+
2001
+ /*
2002
+ * call-seq:
2003
+ * cursor.each {|record| block } => cursor
2004
+ *
2005
+ * Iterates over the remaining records, passing each <i>record</i> to the
2006
+ * <i>block</i> as an array.
2007
+ *
2008
+ * Returns __self__.
2009
+ */
2010
+ static VALUE
2011
+ seqcur_each(VALUE self)
2012
+ {
2013
+ return each(self, T_ARRAY, 0);
2014
+ }
2015
+
2016
+ /*
2017
+ * call-seq:
2018
+ * cursor.each! {|record| block } => cursor
2019
+ *
2020
+ * Iterates over the remaining records, passing each <i>record</i> to the
2021
+ * <i>block</i> as an array. No new Array objects are created for each record.
2022
+ * The same Array object is reused in each call.
2023
+ *
2024
+ * Returns __self__.
2025
+ */
2026
+ static VALUE
2027
+ seqcur_each_bang(VALUE self)
2028
+ {
2029
+ return each(self, T_ARRAY, 1);
2030
+ }
2031
+
2032
+ /*
2033
+ * call-seq:
2034
+ * cursor.each_hash {|record| block } => cursor
2035
+ *
2036
+ * Iterates over the remaining records, passing each <i>record</i> to the
2037
+ * <i>block</i> as a hash.
2038
+ *
2039
+ * Returns __self__.
2040
+ */
2041
+ static VALUE
2042
+ seqcur_each_hash(VALUE self)
2043
+ {
2044
+ return each(self, T_HASH, 0);
2045
+ }
2046
+
2047
+ /*
2048
+ * call-seq:
2049
+ * cursor.each_hash! {|record| block } => cursor
2050
+ *
2051
+ * Iterates over the remaining records, passing each <i>record</i> to the
2052
+ * <i>block</i> as a hash. No new Hash objects are created for each record.
2053
+ * The same Hash object is reused in each call.
2054
+ *
2055
+ * Returns __self__.
2056
+ */
2057
+ static VALUE
2058
+ seqcur_each_hash_bang(VALUE self)
2059
+ {
2060
+ return each(self, T_HASH, 1);
2061
+ }
2062
+
2063
+ /*
2064
+ * call-seq:
2065
+ * cursor.each_by(n) {|records| block } => cursor
2066
+ *
2067
+ * Iterates over the remaining records, passing at most <i>n</i> <i>records</i>
2068
+ * to the <i>block</i> as arrays.
2069
+ *
2070
+ * Returns __self__.
2071
+ */
2072
+ static VALUE
2073
+ seqcur_each_by(VALUE self, VALUE n)
2074
+ {
2075
+ return each_by(self, n, T_ARRAY);
2076
+ }
2077
+
2078
+ /*
2079
+ * call-seq:
2080
+ * cursor.each_hash_by(n) {|records| block } => cursor
2081
+ *
2082
+ * Iterates over the remaining records, passing at most <i>n</i> <i>records</i>
2083
+ * to the <i>block</i> as hashes.
2084
+ *
2085
+ * Returns __self__.
2086
+ */
2087
+ static VALUE
2088
+ seqcur_each_hash_by(VALUE self, VALUE n)
2089
+ {
2090
+ return each_by(self, n, T_HASH);
2091
+ }
2092
+
2093
+ /* module InsertCursor --------------------------------------------------- */
2094
+
2095
+ /*
2096
+ * call-seq:
2097
+ * cursor.put(*params)
2098
+ *
2099
+ * Binds <i>params</i> as input parameters and executes the insert statement.
2100
+ * The records are not written immediatly to disk, unless the insert buffer
2101
+ * is full, the <code>flush</code> method is called, the cursor is closed or
2102
+ * the transaction is commited.
2103
+ */
2104
+ static VALUE
2105
+ inscur_put(int argc, VALUE *argv, VALUE self)
2106
+ {
2107
+ struct sqlda *input;
2108
+ cursor_t *c;
2109
+ EXEC SQL begin declare section;
2110
+ char *cid, *did;
2111
+ EXEC SQL end declare section;
2112
+
2113
+ Data_Get_Struct(self, cursor_t, c);
2114
+ if (!c->is_open)
2115
+ rb_raise(rb_eRuntimeError, "Open the cursor object first");
2116
+
2117
+ did = c->database_id;
2118
+ if (currentdid != did) {
2119
+ EXEC SQL set connection :did;
2120
+ if (SQLCODE < 0)
2121
+ rb_raise(rb_eRuntimeError, "Informix Error: %d", SQLCODE);
2122
+ currentdid = did;
2123
+ }
2124
+
2125
+ input = &c->daInput;
2126
+ cid = c->cursor_id;
2127
+
2128
+ bind_input_params(c, argv);
2129
+ if (argc != input->sqld)
2130
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)",
2131
+ argc, input->sqld);
2132
+
2133
+ EXEC SQL put :cid using descriptor input;
2134
+ clean_input_slots(c);
2135
+ if (SQLCODE < 0)
2136
+ rb_raise(rb_eRuntimeError, "Informix Error: %d", SQLCODE);
2137
+
2138
+ /* XXX 2-448, Guide to SQL: Sytax*/
2139
+ return INT2FIX(sqlca.sqlerrd[2]);
2140
+ }
2141
+
2142
+ /*
2143
+ * call-seq:
2144
+ * cursor.flush => cursor
2145
+ *
2146
+ * Flushes the insert buffer, writing data to disk.
2147
+ *
2148
+ * Returns __self__.
2149
+ */
2150
+ static VALUE
2151
+ inscur_flush(VALUE self)
2152
+ {
2153
+ cursor_t *c;
2154
+ EXEC SQL begin declare section;
2155
+ char *cid, *did;
2156
+ EXEC SQL end declare section;
2157
+
2158
+ Data_Get_Struct(self, cursor_t, c);
2159
+ if (!c->is_open)
2160
+ rb_raise(rb_eRuntimeError, "Open the cursor object first");
2161
+
2162
+ did = c->database_id;
2163
+ if (currentdid != did) {
2164
+ EXEC SQL set connection :did;
2165
+ if (SQLCODE < 0)
2166
+ rb_raise(rb_eRuntimeError, "Informix Error: %d", SQLCODE);
2167
+ currentdid = did;
2168
+ }
2169
+
2170
+ cid = c->cursor_id;
2171
+ EXEC SQL flush :cid;
2172
+ return self;
2173
+ }
2174
+
2175
+ /* module ScrollCursor --------------------------------------------------- */
2176
+
2177
+ /*
2178
+ * Provides the Array-like functionality for scroll cursors when using the
2179
+ * cursor[index] syntax
2180
+ */
2181
+ static VALUE
2182
+ scrollcur_entry(VALUE self, VALUE index, VALUE type, int bang)
2183
+ {
2184
+ cursor_t *c;
2185
+ struct sqlda *output;
2186
+ VALUE record;
2187
+ EXEC SQL begin declare section;
2188
+ char *cid, *did;
2189
+ long pos;
2190
+ EXEC SQL end declare section;
2191
+
2192
+ Data_Get_Struct(self, cursor_t, c);
2193
+ if (!c->is_open)
2194
+ rb_raise(rb_eRuntimeError, "Open the cursor object first");
2195
+
2196
+ did = c->database_id;
2197
+ if (currentdid != did) {
2198
+ EXEC SQL set connection :did;
2199
+ if (SQLCODE < 0)
2200
+ return Qnil;
2201
+ currentdid = did;
2202
+ }
2203
+
2204
+ output = c->daOutput;
2205
+ cid = c->cursor_id;
2206
+
2207
+ if (NIL_P(index))
2208
+ EXEC SQL fetch current :cid using descriptor output;
2209
+ else if ((pos = NUM2LONG(index) + 1) > 0)
2210
+ EXEC SQL fetch absolute :pos :cid using descriptor output;
2211
+ else {
2212
+ EXEC SQL fetch last :cid;
2213
+ EXEC SQL fetch relative :pos :cid using descriptor output;
2214
+ }
2215
+
2216
+ if (SQLCODE == SQLNOTFOUND)
2217
+ return Qnil;
2218
+
2219
+ if (SQLCODE < 0)
2220
+ rb_raise(rb_eRuntimeError, "Informix Error: %d", SQLCODE);
2221
+
2222
+ RECORD(c, type, bang, record);
2223
+ return make_result(c, record);
2224
+ }
2225
+
2226
+ /*
2227
+ * Provides the Array-like functionality for scroll cursors when using the
2228
+ * cursor[start, length] syntax
2229
+ */
2230
+ static VALUE
2231
+ scrollcur_subseq(VALUE self, VALUE start, VALUE length, VALUE type)
2232
+ {
2233
+ cursor_t *c;
2234
+ struct sqlda *output;
2235
+ VALUE first, records;
2236
+ EXEC SQL begin declare section;
2237
+ char *cid, *did;
2238
+ long pos;
2239
+ EXEC SQL end declare section;
2240
+
2241
+ first = scrollcur_entry(self, start, type, 0);
2242
+ if (NIL_P(first))
2243
+ return Qnil;
2244
+
2245
+ pos = NUM2LONG(length) - 1;
2246
+
2247
+ if (pos > 0) {
2248
+ length = LONG2NUM(pos);
2249
+ records = fetch_many(self, length, type);
2250
+ }
2251
+ else
2252
+ records = rb_ary_new();
2253
+
2254
+ rb_ary_unshift(records, first);
2255
+
2256
+ return records;
2257
+ }
2258
+
2259
+ /*
2260
+ * Base function for slice and slice_hash methods
2261
+ */
2262
+ static VALUE
2263
+ slice(int argc, VALUE *argv, VALUE self, VALUE type)
2264
+ {
2265
+ if (argc == 2) {
2266
+ if (NUM2LONG(argv[1]) <= 0)
2267
+ rb_raise(rb_eArgError, "length must be positive");
2268
+ return scrollcur_subseq(self, argv[0], argv[1], type);
2269
+ }
2270
+ if (argc != 1)
2271
+ rb_scan_args(argc, argv, "11", 0, 0);
2272
+
2273
+ return scrollcur_entry(self, argv[0], type, 0);
2274
+ }
2275
+
2276
+ /*
2277
+ * call-seq:
2278
+ * cursor[index] => array or nil
2279
+ * cursor[start, length] => array or nil
2280
+ * cursor.slice(index) => array or nil
2281
+ * cursor.slice(start, length) => array or nil
2282
+ *
2283
+ * Returns the record at _index_, or returns a subarray starting at _start_
2284
+ * and continuing for _length_ records. Negative indices count backward from
2285
+ * the end of the cursor (-1 is the last element). Returns nil if the
2286
+ * (starting) index is out of range.
2287
+ *
2288
+ * <b>Warning</b>: if the (starting) index is negative and out of range, the
2289
+ * position in the cursor is set to the last record. Otherwise the current
2290
+ * position in the cursor is preserved.
2291
+ */
2292
+ static VALUE
2293
+ scrollcur_slice(int argc, VALUE *argv, VALUE self)
2294
+ {
2295
+ return slice(argc, argv, self, T_ARRAY);
2296
+ }
2297
+
2298
+ /*
2299
+ * call-seq:
2300
+ * cursor.slice!(index) => array or nil
2301
+ *
2302
+ * Returns the record at _index_. Negative indices count backward from
2303
+ * the end of the cursor (-1 is the last element). Returns nil if the index
2304
+ * is out of range.
2305
+ *
2306
+ * Stores the record fetched always in the same Array object.
2307
+ *
2308
+ * <b>Warning</b>: if the index is negative and out of range, the
2309
+ * position in the cursor is set to the last record. Otherwise the current
2310
+ * position in the cursor is preserved.
2311
+ */
2312
+ static VALUE
2313
+ scrollcur_slice_bang(VALUE self, VALUE index)
2314
+ {
2315
+ return scrollcur_entry(self, index, T_ARRAY, 1);
2316
+ }
2317
+
2318
+ /*
2319
+ * call-seq:
2320
+ * cursor.slice_hash(index) => hash or nil
2321
+ * cursor.slice_hash(start, length) => array or nil
2322
+ *
2323
+ * Returns the record at _index_, or returns a subarray starting at _start_
2324
+ * and continuing for _length_ records. Negative indices count backward from
2325
+ * the end of the cursor (-1 is the last element). Returns nil if the
2326
+ * (starting) index is out of range.
2327
+ *
2328
+ * <b>Warning</b>: if the (starting) index is negative and out of range, the
2329
+ * position in the cursor is set to the last record. Otherwise the current
2330
+ * position in the cursor is preserved.
2331
+ */
2332
+ static VALUE
2333
+ scrollcur_slice_hash(int argc, VALUE *argv, VALUE self)
2334
+ {
2335
+ return slice(argc, argv, self, T_HASH);
2336
+ }
2337
+
2338
+ /*
2339
+ * call-seq:
2340
+ * cursor.slice_hash!(index) => hash or nil
2341
+ *
2342
+ * Returns the record at _index_. Negative indices count backward from
2343
+ * the end of the cursor (-1 is the last element). Returns nil if the index
2344
+ * is out of range.
2345
+ *
2346
+ * Stores the record fetched always in the same Hash object.
2347
+ *
2348
+ * <b>Warning</b>: if the index is negative and out of range, the
2349
+ * position in the cursor is set to the last record. Otherwise the current
2350
+ * position in the cursor is preserved.
2351
+ */
2352
+ static VALUE
2353
+ scrollcur_slice_hash_bang(VALUE self, VALUE index)
2354
+ {
2355
+ return scrollcur_entry(self, index, T_HASH, 1);
2356
+ }
2357
+
2358
+ /*
2359
+ * Base function for prev* and next* methods
2360
+ */
2361
+ static VALUE
2362
+ scrollcur_rel(int argc, VALUE *argv, VALUE self, int dir, VALUE type, int bang)
2363
+ {
2364
+ cursor_t *c;
2365
+ struct sqlda *output;
2366
+ VALUE offset, record;
2367
+ EXEC SQL begin declare section;
2368
+ char *cid, *did;
2369
+ long pos;
2370
+ EXEC SQL end declare section;
2371
+
2372
+ Data_Get_Struct(self, cursor_t, c);
2373
+ if (!c->is_open)
2374
+ rb_raise(rb_eRuntimeError, "Open the cursor object first");
2375
+
2376
+ did = c->database_id;
2377
+ if (currentdid != did) {
2378
+ EXEC SQL set connection :did;
2379
+ if (SQLCODE < 0)
2380
+ return Qnil;
2381
+ currentdid = did;
2382
+ }
2383
+
2384
+ rb_scan_args(argc, argv, "01", &offset);
2385
+ pos = dir*(NIL_P(offset)? 1: NUM2LONG(offset));
2386
+
2387
+ output = c->daOutput;
2388
+ cid = c->cursor_id;
2389
+ EXEC SQL fetch relative :pos :cid using descriptor output;
2390
+
2391
+ if (SQLCODE == SQLNOTFOUND)
2392
+ return Qnil;
2393
+
2394
+ if (SQLCODE < 0)
2395
+ rb_raise(rb_eRuntimeError, "Informix Error: %d", SQLCODE);
2396
+
2397
+ RECORD(c, type, bang, record);
2398
+ return make_result(c, record);
2399
+ }
2400
+
2401
+ /* call-seq:
2402
+ * cursor.prev(offset = 1) => array or nil
2403
+ *
2404
+ * Returns the previous _offset_ th record. Negative indices count
2405
+ * forward from the current position. Returns nil if the _offset_ is out of
2406
+ * range.
2407
+ */
2408
+ static VALUE
2409
+ scrollcur_prev(int argc, VALUE *argv, VALUE self)
2410
+ {
2411
+ return scrollcur_rel(argc, argv, self, -1, T_ARRAY, 0);
2412
+ }
2413
+
2414
+ /* call-seq:
2415
+ * cursor.prev!(offset = 1) => array or nil
2416
+ *
2417
+ * Returns the previous _offset_ th record. Negative indices count
2418
+ * forward from the current position. Returns nil if the _offset_ is out of
2419
+ * range.
2420
+ *
2421
+ * Stores the record fetched always in the same Array object.
2422
+ */
2423
+ static VALUE
2424
+ scrollcur_prev_bang(int argc, VALUE *argv, VALUE self)
2425
+ {
2426
+ return scrollcur_rel(argc, argv, self, -1, T_ARRAY, 1);
2427
+ }
2428
+
2429
+ /* call-seq:
2430
+ * cursor.prev_hash(offset = 1) => hash or nil
2431
+ *
2432
+ * Returns the previous _offset_ th record. Negative indices count
2433
+ * forward from the current position. Returns nil if the _offset_ is out of
2434
+ * range.
2435
+ */
2436
+ static VALUE
2437
+ scrollcur_prev_hash(int argc, VALUE *argv, VALUE self)
2438
+ {
2439
+ return scrollcur_rel(argc, argv, self, -1, T_HASH, 0);
2440
+ }
2441
+
2442
+ /* call-seq:
2443
+ * cursor.prev_hash!(offset = 1) => hash or nil
2444
+ *
2445
+ * Returns the previous _offset_ th record. Negative indices count
2446
+ * forward from the current position. Returns nil if the _offset_ is out of
2447
+ * range.
2448
+ *
2449
+ * Stores the record fetched always in the same Hash object.
2450
+ */
2451
+ static VALUE
2452
+ scrollcur_prev_hash_bang(int argc, VALUE *argv, VALUE self)
2453
+ {
2454
+ return scrollcur_rel(argc, argv, self, -1, T_HASH, 1);
2455
+ }
2456
+
2457
+ /* call-seq:
2458
+ * cursor.next(offset = 1) => array or nil
2459
+ *
2460
+ * Returns the next _offset_ th record. Negative indices count
2461
+ * backward from the current position. Returns nil if the _offset_ is out of
2462
+ * range.
2463
+ */
2464
+ static VALUE
2465
+ scrollcur_next(int argc, VALUE *argv, VALUE self)
2466
+ {
2467
+ return scrollcur_rel(argc, argv, self, 1, T_ARRAY, 0);
2468
+ }
2469
+
2470
+ /* call-seq:
2471
+ * cursor.next!(offset = 1) => array or nil
2472
+ *
2473
+ * Returns the next _offset_ th record. Negative indices count
2474
+ * backward from the current position. Returns nil if the _offset_ is out of
2475
+ * range.
2476
+ *
2477
+ * Stores the record fetched always in the same Array object.
2478
+ */
2479
+ static VALUE
2480
+ scrollcur_next_bang(int argc, VALUE *argv, VALUE self)
2481
+ {
2482
+ return scrollcur_rel(argc, argv, self, 1, T_ARRAY, 1);
2483
+ }
2484
+
2485
+ /* call-seq:
2486
+ * cursor.next_hash(offset = 1) => hash or nil
2487
+ *
2488
+ * Returns the next _offset_ th record. Negative indices count
2489
+ * backward from the current position. Returns nil if the _offset_ is out of
2490
+ * range.
2491
+ */
2492
+ static VALUE
2493
+ scrollcur_next_hash(int argc, VALUE *argv, VALUE self)
2494
+ {
2495
+ return scrollcur_rel(argc, argv, self, 1, T_HASH, 0);
2496
+ }
2497
+
2498
+ /* call-seq:
2499
+ * cursor.next_hash!(offset = 1) => hash or nil
2500
+ *
2501
+ * Returns the next _offset_ th record. Negative indices count
2502
+ * backward from the current position. Returns nil if the _offset_ is out of
2503
+ * range.
2504
+ *
2505
+ * Stores the record fetched always in the same Hash object.
2506
+ */
2507
+ static VALUE
2508
+ scrollcur_next_hash_bang(int argc, VALUE *argv, VALUE self)
2509
+ {
2510
+ return scrollcur_rel(argc, argv, self, 1, T_HASH, 1);
2511
+ }
2512
+
2513
+ /*
2514
+ * call-seq:
2515
+ * cursor.first => array or nil
2516
+ *
2517
+ * Returns the first record of the cursor. If the cursor is empty,
2518
+ * returns nil.
2519
+ */
2520
+ static VALUE
2521
+ scrollcur_first(VALUE self)
2522
+ {
2523
+ return scrollcur_entry(self, INT2FIX(0), T_ARRAY, 0);
2524
+ }
2525
+
2526
+ /*
2527
+ * call-seq:
2528
+ * cursor.first! => array or nil
2529
+ *
2530
+ * Returns the first record of the cursor. If the cursor is empty,
2531
+ * returns nil.
2532
+ *
2533
+ * Stores the record fetched always in the same Array object.
2534
+ */
2535
+ static VALUE
2536
+ scrollcur_first_bang(VALUE self)
2537
+ {
2538
+ return scrollcur_entry(self, INT2FIX(0), T_ARRAY, 1);
2539
+ }
2540
+
2541
+ /*
2542
+ * call-seq:
2543
+ * cursor.first_hash => hash or nil
2544
+ *
2545
+ * Returns the first record of the cursor. If the cursor is empty,
2546
+ * returns nil.
2547
+ */
2548
+ static VALUE
2549
+ scrollcur_first_hash(VALUE self)
2550
+ {
2551
+ return scrollcur_entry(self, INT2FIX(0), T_HASH, 0);
2552
+ }
2553
+
2554
+ /*
2555
+ * call-seq:
2556
+ * cursor.first_hash! => hash or nil
2557
+ *
2558
+ * Returns the first record of the cursor. If the cursor is empty,
2559
+ * returns nil.
2560
+ *
2561
+ * Stores the record fetched always in the same Hash object.
2562
+ */
2563
+ static VALUE
2564
+ scrollcur_first_hash_bang(VALUE self)
2565
+ {
2566
+ return scrollcur_entry(self, INT2FIX(0), T_HASH, 1);
2567
+ }
2568
+
2569
+ /*
2570
+ * call-seq:
2571
+ * cursor.last => array or nil
2572
+ *
2573
+ * Returns the last record of the cursor. If the cursor is empty,
2574
+ * returns nil.
2575
+ */
2576
+ static VALUE
2577
+ scrollcur_last(VALUE self)
2578
+ {
2579
+ return scrollcur_entry(self, INT2FIX(-1), T_ARRAY, 0);
2580
+ }
2581
+
2582
+ /*
2583
+ * call-seq:
2584
+ * cursor.last! => array or nil
2585
+ *
2586
+ * Returns the last record of the cursor. If the cursor is empty,
2587
+ * returns nil.
2588
+ *
2589
+ * Stores the record fetched always in the same Array object.
2590
+ */
2591
+ static VALUE
2592
+ scrollcur_last_bang(VALUE self)
2593
+ {
2594
+ return scrollcur_entry(self, INT2FIX(-1), T_ARRAY, 1);
2595
+ }
2596
+
2597
+ /*
2598
+ * call-seq:
2599
+ * cursor.last_hash => hash or nil
2600
+ *
2601
+ * Returns the last record of the cursor. If the cursor is empty,
2602
+ * returns nil.
2603
+ */
2604
+ static VALUE
2605
+ scrollcur_last_hash(VALUE self)
2606
+ {
2607
+ return scrollcur_entry(self, INT2FIX(-1), T_HASH, 0);
2608
+ }
2609
+
2610
+ /*
2611
+ * call-seq:
2612
+ * cursor.last_hash! => hash or nil
2613
+ *
2614
+ * Returns the last record of the cursor. If the cursor is empty,
2615
+ * returns nil.
2616
+ *
2617
+ * Stores the record fetched always in the same Hash object.
2618
+ */
2619
+ static VALUE
2620
+ scrollcur_last_hash_bang(VALUE self)
2621
+ {
2622
+ return scrollcur_entry(self, INT2FIX(-1), T_HASH, 1);
2623
+ }
2624
+
2625
+ /*
2626
+ * call-seq:
2627
+ * cursor.current => array or nil
2628
+ *
2629
+ * Returns the current record of the cursor. If the cursor is empty,
2630
+ * returns nil.
2631
+ */
2632
+ static VALUE
2633
+ scrollcur_current(VALUE self)
2634
+ {
2635
+ return scrollcur_entry(self, Qnil, T_ARRAY, 0);
2636
+ }
2637
+
2638
+ /*
2639
+ * call-seq:
2640
+ * cursor.current! => array or nil
2641
+ *
2642
+ * Returns the current record of the cursor. If the cursor is empty,
2643
+ * returns nil.
2644
+ *
2645
+ * Stores the record fetched always in the same Array object.
2646
+ */
2647
+ static VALUE
2648
+ scrollcur_current_bang(VALUE self)
2649
+ {
2650
+ return scrollcur_entry(self, Qnil, T_ARRAY, 1);
2651
+ }
2652
+
2653
+ /*
2654
+ * call-seq:
2655
+ * cursor.current_hash => hash or nil
2656
+ *
2657
+ * Returns the current record of the cursor. If the cursor is empty,
2658
+ * returns nil.
2659
+ */
2660
+ static VALUE
2661
+ scrollcur_current_hash(VALUE self)
2662
+ {
2663
+ return scrollcur_entry(self, Qnil, T_HASH, 0);
2664
+ }
2665
+
2666
+ /*
2667
+ * call-seq:
2668
+ * cursor.current_hash! => hash or nil
2669
+ *
2670
+ * Returns the current record of the cursor. If the cursor is empty,
2671
+ * returns nil.
2672
+ *
2673
+ * Stores the record fetched always in the same Hash object.
2674
+ */
2675
+ static VALUE
2676
+ scrollcur_current_hash_bang(VALUE self)
2677
+ {
2678
+ return scrollcur_entry(self, Qnil, T_HASH, 1);
2679
+ }
2680
+
2681
+ /* class Cursor ---------------------------------------------------------- */
2682
+ static void
2683
+ cursor_close_or_free(cursor_t *c, short op)
2684
+ {
2685
+ EXEC SQL begin declare section;
2686
+ char *cid, *sid, *did;
2687
+ EXEC SQL end declare section;
2688
+
2689
+ if (op == 1 && !c->is_open)
2690
+ return;
2691
+
2692
+ c->is_open = 0;
2693
+ if (op == 1)
2694
+ clean_input_slots(c);
2695
+ else {
2696
+ free_input_slots(c);
2697
+ free_output_slots(c);
2698
+ }
2699
+
2700
+ did = c->database_id;
2701
+ if (currentdid != did) {
2702
+ EXEC SQL set connection :did;
2703
+ if (SQLCODE < 0)
2704
+ return;
2705
+ currentdid = did;
2706
+ }
2707
+
2708
+ cid = c->cursor_id;
2709
+ EXEC SQL close :cid;
2710
+
2711
+ if (op == 2) {
2712
+ sid = c->stmt_id;
2713
+ EXEC SQL free :cid; EXEC SQL free :sid;
2714
+ }
2715
+ }
2716
+
2717
+ static void
2718
+ cursor_mark(cursor_t *c)
2719
+ {
2720
+ rb_gc_mark(c->db);
2721
+ if (c->array)
2722
+ rb_gc_mark(c->array);
2723
+ if (c->hash)
2724
+ rb_gc_mark(c->hash);
2725
+ if (c->field_names)
2726
+ rb_gc_mark(c->field_names);
2727
+ }
2728
+
2729
+ static void
2730
+ cursor_free(void *p)
2731
+ {
2732
+ cursor_close_or_free(p, 2);
2733
+ xfree(p);
2734
+ }
2735
+
2736
+ static VALUE
2737
+ cursor_alloc(VALUE klass)
2738
+ {
2739
+ cursor_t *c;
2740
+
2741
+ c = ALLOC(cursor_t);
2742
+ memset(c, 0, sizeof(cursor_t));
2743
+ return Data_Wrap_Struct(klass, cursor_mark, cursor_free, c);
2744
+ }
2745
+
2746
+ /*
2747
+ * call-seq:
2748
+ * Cursor.new(database, query, options) => cursor
2749
+ *
2750
+ * Prepares <i>query</i> in the context of <i>database</i> with <i>options</i>
2751
+ * and returns a <code>Cursor</code> object.
2752
+ *
2753
+ * <i>options</i> can be nil or a hash with the following possible keys:
2754
+ *
2755
+ * :scroll => true or false
2756
+ * :hold => true or false
2757
+ */
2758
+ static VALUE
2759
+ cursor_initialize(VALUE self, VALUE db, VALUE query, VALUE options)
2760
+ {
2761
+ VALUE scroll, hold;
2762
+ struct sqlda *output;
2763
+ cursor_t *c;
2764
+ EXEC SQL begin declare section;
2765
+ char *c_query;
2766
+ char *cid, *sid, *did;
2767
+ EXEC SQL end declare section;
2768
+
2769
+ Data_Get_Struct(db, char, did);
2770
+
2771
+ if (currentdid != did) {
2772
+ EXEC SQL set connection :did;
2773
+ if (SQLCODE < 0)
2774
+ rb_raise(rb_eRuntimeError, "Informix Error: %d", SQLCODE);
2775
+ currentdid = did;
2776
+ }
2777
+
2778
+ Data_Get_Struct(self, cursor_t, c);
2779
+ c->db = db;
2780
+ c->database_id = did;
2781
+ scroll = hold = Qfalse;
2782
+ snprintf(c->cursor_id, sizeof(c->cursor_id), "CUR%lX", self);
2783
+ snprintf(c->stmt_id, sizeof(c->stmt_id), "STMT%lX", self);
2784
+ cid = c->cursor_id; sid = c->stmt_id;
2785
+ c_query = StringValueCStr(query);
2786
+
2787
+ if (RTEST(options)) {
2788
+ scroll = rb_hash_aref(options, sym_scroll);
2789
+ hold = rb_hash_aref(options, sym_hold);
2790
+ }
2791
+
2792
+ EXEC SQL prepare :sid from :c_query;
2793
+ if (SQLCODE < 0)
2794
+ rb_raise(rb_eRuntimeError, "Informix Error: %d", SQLCODE);
2795
+
2796
+ if (RTEST(scroll) && RTEST(hold))
2797
+ EXEC SQL declare :cid scroll cursor with hold for :sid;
2798
+ else if (RTEST(hold))
2799
+ EXEC SQL declare :cid cursor with hold for :sid;
2800
+ else if (RTEST(scroll))
2801
+ EXEC SQL declare :cid scroll cursor for :sid;
2802
+ else
2803
+ EXEC SQL declare :cid cursor for :sid;
2804
+
2805
+ if (SQLCODE < 0)
2806
+ rb_raise(rb_eRuntimeError, "Informix Error: %d", SQLCODE);
2807
+
2808
+ alloc_input_slots(c, c_query);
2809
+ EXEC SQL describe :sid into output;
2810
+ c->daOutput = output;
2811
+
2812
+ c->is_select = (SQLCODE == 0 || SQLCODE == SQ_EXECPROC);
2813
+
2814
+ if (c->is_select) {
2815
+ alloc_output_slots(c);
2816
+ rb_extend_object(self, rb_mSequentialCursor);
2817
+ if (scroll)
2818
+ rb_extend_object(self, rb_mScrollCursor);
2819
+ }
2820
+ else {
2821
+ xfree(c->daOutput);
2822
+ c->daOutput = NULL;
2823
+ rb_extend_object(self, rb_mInsertCursor);
2824
+ }
2825
+ return self;
2826
+ }
2827
+
2828
+ /*
2829
+ * call-seq:
2830
+ * cursor.id => string
2831
+ *
2832
+ * Returns the cursor ID
2833
+ */
2834
+ static VALUE
2835
+ cursor_id(VALUE self)
2836
+ {
2837
+ cursor_t *c;
2838
+
2839
+ Data_Get_Struct(self, cursor_t, c);
2840
+ return rb_str_new2(c->cursor_id);
2841
+ }
2842
+
2843
+ /*
2844
+ * call-seq:
2845
+ * cursor.open(*params) => cursor
2846
+ *
2847
+ * Executes the previously prepared select statement, binding <i>params</i> as
2848
+ * input parameters.
2849
+ *
2850
+ * Returns __self__.
2851
+ */
2852
+ static VALUE
2853
+ cursor_open(int argc, VALUE *argv, VALUE self)
2854
+ {
2855
+ struct sqlda *input;
2856
+ cursor_t *c;
2857
+ EXEC SQL begin declare section;
2858
+ char *cid, *did;
2859
+ EXEC SQL end declare section;
2860
+
2861
+ Data_Get_Struct(self, cursor_t, c);
2862
+
2863
+ if (c->is_open)
2864
+ return self;
2865
+
2866
+ did = c->database_id;
2867
+ if (currentdid != did) {
2868
+ EXEC SQL set connection :did;
2869
+ if (SQLCODE < 0)
2870
+ rb_raise(rb_eRuntimeError, "Informix Error: %d", SQLCODE);
2871
+ currentdid = did;
2872
+ }
2873
+
2874
+ input = &c->daInput;
2875
+ cid = c->cursor_id;
2876
+
2877
+ if (c->is_select) {
2878
+ if (argc != input->sqld) {
2879
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)",
2880
+ argc, input->sqld);
2881
+ }
2882
+ if (argc) {
2883
+ bind_input_params(c, argv);
2884
+ EXEC SQL open :cid using descriptor input
2885
+ with reoptimization;
2886
+ clean_input_slots(c);
2887
+ }
2888
+ else
2889
+ EXEC SQL open :cid with reoptimization;
2890
+ }
2891
+ else
2892
+ EXEC SQL open :cid;
2893
+
2894
+ if (SQLCODE < 0)
2895
+ rb_raise(rb_eRuntimeError, "Informix Error: %d", SQLCODE);
2896
+
2897
+ c->is_open = 1;
2898
+ return self;
2899
+ }
2900
+
2901
+ /*
2902
+ * call-seq:
2903
+ * cursor.close => cursor
2904
+ *
2905
+ * Closes the cursor and returns __self__.
2906
+ */
2907
+ static VALUE
2908
+ cursor_close(VALUE self)
2909
+ {
2910
+ cursor_t *c;
2911
+
2912
+ Data_Get_Struct(self, cursor_t, c);
2913
+ cursor_close_or_free(c, 1);
2914
+ return self;
2915
+ }
2916
+
2917
+ /*
2918
+ * call-seq:
2919
+ * cursor.drop => nil
2920
+ *
2921
+ * Closes the cursor and frees the memory associated with it. The cursor
2922
+ * cannot be opened again.
2923
+ */
2924
+ static VALUE
2925
+ cursor_drop(VALUE self)
2926
+ {
2927
+ cursor_t *c;
2928
+
2929
+ Data_Get_Struct(self, cursor_t, c);
2930
+ cursor_close_or_free(c, 2);
2931
+
2932
+ return Qnil;
2933
+ }
2934
+
2935
+ /* Entry point ------------------------------------------------------------ */
2936
+
2937
+ void Init_informix(void)
2938
+ {
2939
+ /* module Informix ---------------------------------------------------- */
2940
+ rb_mInformix = rb_define_module("Informix");
2941
+ rb_mScrollCursor = rb_define_module_under(rb_mInformix, "ScrollCursor");
2942
+ rb_mInsertCursor = rb_define_module_under(rb_mInformix, "InsertCursor");
2943
+ rb_define_module_function(rb_mInformix, "connect", informix_connect, -1);
2944
+
2945
+ /* class Slob --------------------------------------------------------- */
2946
+ rb_cSlob = rb_define_class_under(rb_mInformix, "Slob", rb_cObject);
2947
+ rb_define_alloc_func(rb_cSlob, slob_alloc);
2948
+ rb_define_method(rb_cSlob, "initialize", slob_initialize, -1);
2949
+ rb_define_method(rb_cSlob, "open", slob_open, -1);
2950
+ rb_define_method(rb_cSlob, "close", slob_close, 0);
2951
+ rb_define_method(rb_cSlob, "read", slob_read, 1);
2952
+ rb_define_method(rb_cSlob, "write", slob_write, 1);
2953
+ rb_define_method(rb_cSlob, "seek", slob_seek, 2);
2954
+ rb_define_method(rb_cSlob, "tell", slob_tell, 0);
2955
+ rb_define_method(rb_cSlob, "truncate", slob_truncate, 1);
2956
+
2957
+ rb_define_const(rb_cSlob, "CLOB", INT2FIX(XID_CLOB));
2958
+ rb_define_const(rb_cSlob, "BLOB", INT2FIX(XID_BLOB));
2959
+
2960
+ #define DEF_SLOB_CONST(k) rb_define_const(rb_cSlob, #k, INT2FIX(LO_##k))
2961
+
2962
+ DEF_SLOB_CONST(RDONLY);
2963
+ DEF_SLOB_CONST(DIRTY_READ);
2964
+ DEF_SLOB_CONST(WRONLY);
2965
+ DEF_SLOB_CONST(APPEND);
2966
+ DEF_SLOB_CONST(RDWR);
2967
+ DEF_SLOB_CONST(BUFFER);
2968
+ DEF_SLOB_CONST(NOBUFFER);
2969
+ DEF_SLOB_CONST(LOCKALL);
2970
+ DEF_SLOB_CONST(LOCKRANGE);
2971
+ DEF_SLOB_CONST(SEEK_SET);
2972
+ DEF_SLOB_CONST(SEEK_CUR);
2973
+ DEF_SLOB_CONST(SEEK_END);
2974
+
2975
+ /* class Database ----------------------------------------------------- */
2976
+ rb_cDatabase = rb_define_class_under(rb_mInformix, "Database", rb_cObject);
2977
+ rb_define_alloc_func(rb_cDatabase, database_alloc);
2978
+ rb_define_method(rb_cDatabase, "initialize", database_initialize, -1);
2979
+ rb_define_alias(rb_cDatabase, "open", "initialize");
2980
+ rb_define_method(rb_cDatabase, "close", database_close, 0);
2981
+ rb_define_method(rb_cDatabase, "immediate", database_immediate, 1);
2982
+ rb_define_alias(rb_cDatabase, "do", "immediate");
2983
+ rb_define_method(rb_cDatabase, "rollback", database_rollback, 0);
2984
+ rb_define_method(rb_cDatabase, "commit", database_commit, 0);
2985
+ rb_define_method(rb_cDatabase, "transaction", database_transaction, 0);
2986
+ rb_define_method(rb_cDatabase, "prepare", database_prepare, 1);
2987
+ rb_define_method(rb_cDatabase, "columns", database_columns, 1);
2988
+ rb_define_method(rb_cDatabase, "cursor", database_cursor, -1);
2989
+
2990
+ /* class Statement ---------------------------------------------------- */
2991
+ rb_cStatement = rb_define_class_under(rb_mInformix, "Statement", rb_cObject);
2992
+ rb_define_alloc_func(rb_cStatement, statement_alloc);
2993
+ rb_define_method(rb_cStatement, "initialize", statement_initialize, 2);
2994
+ rb_define_method(rb_cStatement, "[]", statement_call, -1);
2995
+ rb_define_alias(rb_cStatement, "call", "[]");
2996
+ rb_define_alias(rb_cStatement, "execute", "[]");
2997
+ rb_define_method(rb_cStatement, "drop", statement_drop, 0);
2998
+
2999
+ /* module SequentialCursor -------------------------------------------- */
3000
+ rb_mSequentialCursor = rb_define_module_under(rb_mInformix, "SequentialCursor");
3001
+ rb_define_method(rb_mSequentialCursor, "fetch", seqcur_fetch, 0);
3002
+ rb_define_method(rb_mSequentialCursor, "fetch!", seqcur_fetch_bang, 0);
3003
+ rb_define_method(rb_mSequentialCursor, "fetch_hash", seqcur_fetch_hash, 0);
3004
+ rb_define_method(rb_mSequentialCursor, "fetch_hash!", seqcur_fetch_hash_bang, 0);
3005
+ rb_define_method(rb_mSequentialCursor, "fetch_many", seqcur_fetch_many, 1);
3006
+ rb_define_method(rb_mSequentialCursor, "fetch_hash_many", seqcur_fetch_hash_many, 1);
3007
+ rb_define_method(rb_mSequentialCursor, "fetch_all", seqcur_fetch_all, 0);
3008
+ rb_define_method(rb_mSequentialCursor, "fetch_hash_all", seqcur_fetch_hash_all, 0);
3009
+ rb_define_method(rb_mSequentialCursor, "each", seqcur_each, 0);
3010
+ rb_define_method(rb_mSequentialCursor, "each!", seqcur_each_bang, 0);
3011
+ rb_define_method(rb_mSequentialCursor, "each_hash", seqcur_each_hash, 0);
3012
+ rb_define_method(rb_mSequentialCursor, "each_hash!", seqcur_each_hash_bang, 0);
3013
+ rb_define_method(rb_mSequentialCursor, "each_by", seqcur_each_by, 1);
3014
+ rb_define_method(rb_mSequentialCursor, "each_hash_by", seqcur_each_hash_by, 1);
3015
+
3016
+ /* InsertCursor ------------------------------------------------------- */
3017
+ rb_define_method(rb_mInsertCursor, "put", inscur_put, -1);
3018
+ rb_define_method(rb_mInsertCursor, "flush", inscur_flush, 0);
3019
+
3020
+ /* ScrollCursor ------------------------------------------------------- */
3021
+ rb_define_method(rb_mScrollCursor, "[]", scrollcur_slice, -1);
3022
+ rb_define_alias(rb_mScrollCursor, "slice", "[]");
3023
+ rb_define_method(rb_mScrollCursor, "slice!", scrollcur_slice_bang, 1);
3024
+ rb_define_method(rb_mScrollCursor, "slice_hash", scrollcur_slice_hash, -1);
3025
+ rb_define_method(rb_mScrollCursor, "slice_hash!", scrollcur_slice_hash_bang, 1);
3026
+ rb_define_method(rb_mScrollCursor, "prev", scrollcur_prev, -1);
3027
+ rb_define_method(rb_mScrollCursor, "prev!", scrollcur_prev_bang, -1);
3028
+ rb_define_method(rb_mScrollCursor, "prev_hash", scrollcur_prev_hash, -1);
3029
+ rb_define_method(rb_mScrollCursor, "prev_hash!", scrollcur_prev_hash_bang, -1);
3030
+ rb_define_method(rb_mScrollCursor, "next", scrollcur_next, -1);
3031
+ rb_define_method(rb_mScrollCursor, "next!", scrollcur_next_bang, -1);
3032
+ rb_define_method(rb_mScrollCursor, "next_hash", scrollcur_next_hash, -1);
3033
+ rb_define_method(rb_mScrollCursor, "next_hash!", scrollcur_next_hash_bang, -1);
3034
+ rb_define_method(rb_mScrollCursor, "first", scrollcur_first, 0);
3035
+ rb_define_method(rb_mScrollCursor, "first!", scrollcur_first_bang, 0);
3036
+ rb_define_method(rb_mScrollCursor, "first_hash", scrollcur_first_hash, 0);
3037
+ rb_define_method(rb_mScrollCursor, "first_hash!", scrollcur_first_hash_bang, 0);
3038
+ rb_define_method(rb_mScrollCursor, "last", scrollcur_last, 0);
3039
+ rb_define_method(rb_mScrollCursor, "last!", scrollcur_last_bang, 0);
3040
+ rb_define_method(rb_mScrollCursor, "last_hash", scrollcur_last_hash, 0);
3041
+ rb_define_method(rb_mScrollCursor, "last_hash!", scrollcur_last_hash_bang, 0);
3042
+ rb_define_method(rb_mScrollCursor, "current", scrollcur_current, 0);
3043
+ rb_define_method(rb_mScrollCursor, "current!", scrollcur_current_bang, 0);
3044
+ rb_define_method(rb_mScrollCursor, "current_hash", scrollcur_current_hash, 0);
3045
+ rb_define_method(rb_mScrollCursor, "current_hash!", scrollcur_current_hash_bang, 0);
3046
+
3047
+ /* class Cursor ------------------------------------------------------- */
3048
+ rb_cCursor = rb_define_class_under(rb_mInformix, "Cursor", rb_cObject);
3049
+ rb_define_alloc_func(rb_cCursor, cursor_alloc);
3050
+ rb_define_method(rb_cCursor, "initialize", cursor_initialize, 3);
3051
+ rb_define_method(rb_cCursor, "id", cursor_id, 0);
3052
+ rb_define_method(rb_cCursor, "open", cursor_open, -1);
3053
+ rb_define_method(rb_cCursor, "close", cursor_close, 0);
3054
+ rb_define_method(rb_cCursor, "drop", cursor_drop, 0);
3055
+
3056
+ /* Global constants --------------------------------------------------- */
3057
+ rb_require("date");
3058
+ rb_cDate = rb_const_get(rb_cObject, rb_intern("Date"));
3059
+
3060
+ /* Global symbols ----------------------------------------------------- */
3061
+ s_read = rb_intern("read");
3062
+ s_new = rb_intern("new");
3063
+ s_utc = rb_intern("utc");
3064
+ s_day = rb_intern("day");
3065
+ s_month = rb_intern("month");
3066
+ s_year = rb_intern("year");
3067
+ s_hour = rb_intern("hour");
3068
+ s_min = rb_intern("min");
3069
+ s_sec = rb_intern("sec");
3070
+ s_usec = rb_intern("usec");
3071
+ s_to_s = rb_intern("to_s");
3072
+ s_to_i = rb_intern("to_i");
3073
+
3074
+ sym_name = ID2SYM(rb_intern("name"));
3075
+ sym_type = ID2SYM(rb_intern("type"));
3076
+ sym_nullable = ID2SYM(rb_intern("nullable"));
3077
+ sym_stype = ID2SYM(rb_intern("stype"));
3078
+ sym_length = ID2SYM(rb_intern("length"));
3079
+ sym_precision = ID2SYM(rb_intern("precision"));
3080
+ sym_scale = ID2SYM(rb_intern("scale"));
3081
+ sym_default = ID2SYM(rb_intern("default"));
3082
+ sym_xid = ID2SYM(rb_intern("xid"));
3083
+
3084
+ sym_scroll = ID2SYM(rb_intern("scroll"));
3085
+ sym_hold = ID2SYM(rb_intern("hold"));
3086
+
3087
+ sym_col_info = ID2SYM(rb_intern("col_info"));
3088
+ sym_sbspace = ID2SYM(rb_intern("sbspace"));
3089
+ sym_estbytes = ID2SYM(rb_intern("estbytes"));
3090
+ sym_extsz = ID2SYM(rb_intern("extsz"));
3091
+ sym_createflags = ID2SYM(rb_intern("createflags"));
3092
+ sym_openflags = ID2SYM(rb_intern("openflags"));
3093
+ }