ruby-informix 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+ }