cubrid 0.6-x86-mswin32-60

Sign up to get free protection for your applications and to get access to all the features.
Files changed (9) hide show
  1. data/ext/conn.c +405 -0
  2. data/ext/cubrid.c +235 -0
  3. data/ext/cubrid.h +98 -0
  4. data/ext/error.c +144 -0
  5. data/ext/extconf.rb +53 -0
  6. data/ext/oid.c +727 -0
  7. data/ext/stmt.c +1074 -0
  8. data/lib/cubrid.so +0 -0
  9. metadata +62 -0
data/ext/stmt.c ADDED
@@ -0,0 +1,1074 @@
1
+ /*
2
+ * Copyright (C) 2008 Search Solution Corporation. All rights reserved by Search Solution.
3
+ *
4
+ * Redistribution and use in source and binary forms, with or without modification,
5
+ * are permitted provided that the following conditions are met:
6
+ *
7
+ * - Redistributions of source code must retain the above copyright notice,
8
+ * this list of conditions and the following disclaimer.
9
+ *
10
+ * - Redistributions in binary form must reproduce the above copyright notice,
11
+ * this list of conditions and the following disclaimer in the documentation
12
+ * and/or other materials provided with the distribution.
13
+ *
14
+ * - Neither the name of the <ORGANIZATION> nor the names of its contributors
15
+ * may be used to endorse or promote products derived from this software without
16
+ * specific prior written permission.
17
+ *
18
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
22
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
23
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
24
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
27
+ * OF SUCH DAMAGE.
28
+ *
29
+ */
30
+
31
+ #include "cubrid.h"
32
+
33
+ extern VALUE cubrid_conn_end_tran(Connection *con, int type);
34
+ extern VALUE cubrid_oid_new(Connection *con, char *oid_str);
35
+
36
+ extern VALUE cStatement, cOid;
37
+
38
+ void
39
+ cubrid_stmt_free(void *p)
40
+ {
41
+ free(p);
42
+ }
43
+
44
+ VALUE
45
+ cubrid_stmt_new(Connection *con, char *sql, int option)
46
+ {
47
+ VALUE cursor;
48
+ Statement *stmt;
49
+ int handle, param_cnt;
50
+ T_CCI_ERROR error;
51
+
52
+ /*printf("%s\n", sql);*/
53
+
54
+ handle = cci_prepare(con->handle, sql, option, &error);
55
+ if (handle < 0) {
56
+ cubrid_handle_error(handle, &error);
57
+ return Qnil;
58
+ }
59
+
60
+ param_cnt = cci_get_bind_num(handle);
61
+ if (param_cnt < 0) {
62
+ cubrid_handle_error(param_cnt, NULL);
63
+ return Qnil;
64
+ }
65
+
66
+ cursor = Data_Make_Struct(cStatement, Statement, 0, cubrid_stmt_free, stmt);
67
+ stmt->con = con;
68
+ stmt->handle = handle;
69
+ stmt->param_cnt = param_cnt;
70
+ stmt->bound = 0;
71
+
72
+ return cursor;
73
+ }
74
+
75
+ /* call-seq:
76
+ * close() -> nil
77
+ *
78
+ * Statement를 종료합니다.
79
+ */
80
+ VALUE
81
+ cubrid_stmt_close(VALUE self)
82
+ {
83
+ Statement *stmt;
84
+
85
+ GET_STMT_STRUCT(self, stmt);
86
+
87
+ if (stmt->handle) {
88
+ cci_close_req_handle(stmt->handle);
89
+ stmt->handle = 0;
90
+ }
91
+
92
+ return Qnil;
93
+ }
94
+
95
+ T_CCI_SET
96
+ cubrid_stmt_make_set(VALUE data, int u_type) /* TODO: check if all item has same type */
97
+ {
98
+ int i, arr_size, res;
99
+ T_CCI_SET set = NULL;
100
+ void *val = NULL;
101
+ int *ind;
102
+
103
+ arr_size = RARRAY(data)->len;
104
+ ind = ALLOCA_N(int, arr_size);
105
+ if (ind == NULL) {
106
+ rb_raise(rb_eNoMemError, "Not enough memory");
107
+ return NULL;
108
+ }
109
+
110
+ switch (TYPE(rb_ary_entry(data, 0))) {
111
+ case T_FIXNUM:
112
+ case T_BIGNUM:
113
+ {
114
+ int *int_ary = ALLOCA_N(int, arr_size);
115
+ if (int_ary == NULL) {
116
+ rb_raise(rb_eNoMemError, "Not enough memory");
117
+ return NULL;
118
+ }
119
+
120
+ for(i = 0; i < arr_size; i++) {
121
+ if (NIL_P(rb_ary_entry(data, i))) {
122
+ ind[i] = 1;
123
+ }
124
+ else {
125
+ int_ary[i] = NUM2INT(rb_ary_entry(data, i));
126
+ ind[i] = 0;
127
+ }
128
+ }
129
+
130
+ if (u_type == CCI_U_TYPE_UNKNOWN) {
131
+ u_type = CCI_U_TYPE_INT;
132
+ }
133
+ val = int_ary;
134
+ }
135
+ break;
136
+ case T_FLOAT:
137
+ {
138
+ double *dbl_ary;
139
+
140
+ dbl_ary = ALLOCA_N(double, arr_size);
141
+ if (dbl_ary == NULL) {
142
+ rb_raise(rb_eNoMemError, "Not enough memory");
143
+ return NULL;
144
+ }
145
+
146
+ for(i = 0; i < arr_size; i++) {
147
+ if (NIL_P(rb_ary_entry(data, i))) {
148
+ ind[i] = 1;
149
+ }
150
+ else {
151
+ dbl_ary[i] = NUM2DBL(rb_ary_entry(data, i));
152
+ ind[i] = 0;
153
+ }
154
+ }
155
+
156
+ if (u_type == CCI_U_TYPE_UNKNOWN) {
157
+ u_type = CCI_U_TYPE_DOUBLE;
158
+ }
159
+ val = dbl_ary;
160
+ }
161
+ break;
162
+ case T_STRING:
163
+ {
164
+ if (u_type == CCI_U_TYPE_BIT || u_type == CCI_U_TYPE_VARBIT) {
165
+ T_CCI_BIT *bit_ary;
166
+
167
+ bit_ary = ALLOCA_N(T_CCI_BIT, arr_size);
168
+ if (bit_ary == NULL) {
169
+ rb_raise(rb_eNoMemError, "Not enough memory");
170
+ return NULL;
171
+ }
172
+
173
+ for(i = 0; i < arr_size; i++) {
174
+ if (NIL_P(rb_ary_entry(data, i))) {
175
+ ind[i] = 1;
176
+ }
177
+ else {
178
+ bit_ary[i].size = RSTRING(rb_ary_entry(data, i))->len;
179
+ bit_ary[i].buf = RSTRING(rb_ary_entry(data, i))->ptr;
180
+ ind[i] = 0;
181
+ }
182
+ }
183
+
184
+ val = bit_ary;
185
+ } else {
186
+ char **str_ary;
187
+
188
+ str_ary = ALLOCA_N(char*, arr_size);
189
+ if (str_ary == NULL) {
190
+ rb_raise(rb_eNoMemError, "Not enough memory");
191
+ return NULL;
192
+ }
193
+
194
+ for(i = 0; i < arr_size; i++) {
195
+ if (NIL_P(rb_ary_entry(data, i))) {
196
+ ind[i] = 1;
197
+ }
198
+ else {
199
+ str_ary[i] = RSTRING(rb_ary_entry(data, i))->ptr;
200
+ ind[i] = 0;
201
+ }
202
+ }
203
+
204
+ if (u_type == CCI_U_TYPE_UNKNOWN) {
205
+ u_type = CCI_U_TYPE_STRING;
206
+ }
207
+ val = str_ary;
208
+ }
209
+ }
210
+ break;
211
+ case T_DATA:
212
+ if (CLASS_OF(rb_ary_entry(data, 0)) == rb_cTime) {
213
+ VALUE a;
214
+ T_CCI_DATE *date_ary;
215
+
216
+ date_ary = ALLOCA_N(T_CCI_DATE, arr_size);
217
+ if (date_ary == NULL) {
218
+ rb_raise(rb_eNoMemError, "Not enough memory");
219
+ return NULL;
220
+ }
221
+
222
+ for(i = 0; i < arr_size; i++) {
223
+ if (NIL_P(rb_ary_entry(data, i))) {
224
+ ind[i] = 1;
225
+ }
226
+ else {
227
+ a = rb_funcall(rb_ary_entry(data, i), rb_intern("to_a"), 0);
228
+ date_ary[i].ss = FIX2INT(RARRAY(a)->ptr[0]);
229
+ date_ary[i].mm = FIX2INT(RARRAY(a)->ptr[1]);
230
+ date_ary[i].hh = FIX2INT(RARRAY(a)->ptr[2]);
231
+ date_ary[i].day = FIX2INT(RARRAY(a)->ptr[3]);
232
+ date_ary[i].mon = FIX2INT(RARRAY(a)->ptr[4]);
233
+ date_ary[i].yr = FIX2INT(RARRAY(a)->ptr[5]);
234
+
235
+ ind[i] = 0;
236
+ }
237
+ }
238
+
239
+ if (u_type == CCI_U_TYPE_UNKNOWN) {
240
+ u_type = CCI_U_TYPE_TIMESTAMP;
241
+ }
242
+ val = date_ary;
243
+ } else if (CLASS_OF(rb_ary_entry(data, 0)) == cOid) {
244
+ char **str_ary;
245
+ Oid *oid;
246
+
247
+ str_ary = ALLOCA_N(char*, arr_size);
248
+ if (str_ary == NULL) {
249
+ rb_raise(rb_eNoMemError, "Not enough memory");
250
+ return NULL;
251
+ }
252
+
253
+ for(i = 0; i < arr_size; i++) {
254
+ if (NIL_P(rb_ary_entry(data, i))) {
255
+ ind[i] = 1;
256
+ }
257
+ else {
258
+ Data_Get_Struct(rb_ary_entry(data, i), Oid, oid);
259
+ str_ary[i] = oid->oid_str;
260
+ ind[i] = 0;
261
+ }
262
+ }
263
+
264
+ if (u_type == CCI_U_TYPE_UNKNOWN) {
265
+ u_type = CCI_U_TYPE_OBJECT;
266
+ }
267
+ val = str_ary;
268
+ }
269
+ break;
270
+ default:
271
+ rb_raise(rb_eArgError, "Wrong data type");
272
+ break;
273
+ }
274
+
275
+ res = cci_set_make(&set, u_type, arr_size, val, ind);
276
+ if (res < 0) {
277
+ cubrid_handle_error(res, NULL);
278
+ return NULL;
279
+ }
280
+
281
+ return set;
282
+ }
283
+
284
+ static void
285
+ cubrid_stmt_bind_internal(Statement *stmt, int index, VALUE data, int u_type, int set_type)
286
+ {
287
+ int res, int_val, a_type = CCI_A_TYPE_STR;
288
+ char *str_val;
289
+ double dbl_val;
290
+ void *val = NULL;
291
+ T_CCI_SET set = NULL;
292
+ T_CCI_DATE date;
293
+ T_CCI_BIT bit;
294
+
295
+ switch (TYPE(data)) {
296
+ case T_NIL:
297
+ a_type = CCI_A_TYPE_STR;
298
+ val = NULL;
299
+ u_type = CCI_U_TYPE_NULL;
300
+ break;
301
+
302
+ case T_FIXNUM:
303
+ case T_BIGNUM:
304
+ int_val = NUM2INT(data);
305
+ a_type = CCI_A_TYPE_INT;
306
+ val = &int_val;
307
+ if (u_type == CCI_U_TYPE_UNKNOWN) {
308
+ u_type = CCI_U_TYPE_INT;
309
+ }
310
+ break;
311
+
312
+ case T_FLOAT:
313
+ dbl_val = NUM2DBL(data);
314
+ a_type = CCI_A_TYPE_DOUBLE;
315
+ val = &dbl_val;
316
+ if (u_type == CCI_U_TYPE_UNKNOWN) {
317
+ u_type = CCI_U_TYPE_DOUBLE;
318
+ }
319
+ break;
320
+
321
+ case T_STRING:
322
+ str_val = RSTRING(data)->ptr;
323
+ a_type = CCI_A_TYPE_STR;
324
+ val = str_val;
325
+ if (u_type == CCI_U_TYPE_UNKNOWN) {
326
+ u_type = CCI_U_TYPE_STRING;
327
+ } else if (u_type == CCI_U_TYPE_BIT || u_type == CCI_U_TYPE_VARBIT) {
328
+ bit.size = RSTRING(data)->len;
329
+ bit.buf = str_val;
330
+ a_type = CCI_A_TYPE_BIT;
331
+ val = &bit;
332
+ }
333
+ break;
334
+
335
+ case T_DATA:
336
+ if (CLASS_OF(data) == rb_cTime) {
337
+ VALUE a;
338
+
339
+ a = rb_funcall(data, rb_intern("to_a"), 0);
340
+ date.ss = FIX2INT(RARRAY(a)->ptr[0]);
341
+ date.mm = FIX2INT(RARRAY(a)->ptr[1]);
342
+ date.hh = FIX2INT(RARRAY(a)->ptr[2]);
343
+ date.day = FIX2INT(RARRAY(a)->ptr[3]);
344
+ date.mon = FIX2INT(RARRAY(a)->ptr[4]);
345
+ date.yr = FIX2INT(RARRAY(a)->ptr[5]);
346
+
347
+ a_type = CCI_A_TYPE_DATE;
348
+ val = &date;
349
+ if (u_type == CCI_U_TYPE_UNKNOWN) {
350
+ u_type = CCI_U_TYPE_TIMESTAMP;
351
+ }
352
+ } else if (CLASS_OF(data) == cOid) {
353
+ Oid *oid;
354
+
355
+ Data_Get_Struct(data, Oid, oid);
356
+ a_type = CCI_A_TYPE_STR;
357
+ val = oid->oid_str;
358
+ if (u_type == CCI_U_TYPE_UNKNOWN) {
359
+ u_type = CCI_U_TYPE_OBJECT;
360
+ }
361
+ }
362
+ break;
363
+
364
+ case T_ARRAY:
365
+ set = cubrid_stmt_make_set(data, set_type);
366
+ a_type = CCI_A_TYPE_SET;
367
+ val = set;
368
+ if (u_type == CCI_U_TYPE_UNKNOWN) {
369
+ u_type = CCI_U_TYPE_SET;
370
+ }
371
+ break;
372
+
373
+ default:
374
+ rb_raise(rb_eArgError, "Wrong data type");
375
+ return;
376
+ }
377
+
378
+ res = cci_bind_param(stmt->handle, index, a_type, val, u_type, 0);
379
+
380
+ if (TYPE(data) == T_ARRAY && set) {
381
+ cci_set_free(set);
382
+ }
383
+
384
+ if (res < 0) {
385
+ cubrid_handle_error(res, NULL);
386
+ return;
387
+ }
388
+
389
+ return;
390
+ }
391
+
392
+ /* call-seq:
393
+ * bind(index, data <, db_type, set_type>) -> nil
394
+ *
395
+ * prepare된 Statement의 호스트변수에 데이터를 바인딩합니다.
396
+ * db_type은 ruby의 데이터가 바인딩될 때 적용될 데이터베이스의 타입을 지정합니다.
397
+ * db_type이 주어지지 않으면 아래와 같이 기본 타입으로 적용됩니다.
398
+ *
399
+ * *fixnum, bignum -> integer
400
+ * *float -> double
401
+ * *string -> string(varchar)
402
+ * *Time -> timestamp
403
+ * *Oid -> object
404
+ * *array -> collection
405
+ *
406
+ * set_type은 data가 배열인 경우 배열의 원소에 적용될 데이터베이스 타입을 지정합니다.
407
+ * 배열의 모든 원소들은 동일한 타입이어야 합니다.
408
+ * set_type이 주어지지 않으면 배열의 원소들은 기본 타입으로 바인딩됩니다.
409
+ *
410
+ * con = Cubrid.connect('demodb')
411
+ * con.auto_commit = true
412
+ * con.query('create table a (a int, b double, c string, d date)')
413
+ * con.prepare('insert into a values (?, ?, ?, ?)') { |stmt|
414
+ * stmt.bind(1, 10)
415
+ * stmt.bind(2, 3.141592)
416
+ * stmt.bind(3, 'hello')
417
+ * stmt.bind(4, Time.local(2007, 12, 25, 10, 10, 10), CUBRID::DATE)
418
+ * stmt.execute
419
+ * }
420
+ * con.close
421
+ */
422
+ VALUE
423
+ cubrid_stmt_bind(int argc, VALUE* argv, VALUE self)
424
+ {
425
+ Statement *stmt;
426
+ VALUE index, u_type, data, set_type;
427
+
428
+ GET_STMT_STRUCT(self, stmt);
429
+ CHECK_HANDLE(stmt, self);
430
+
431
+ rb_scan_args(argc, argv, "22", &index, &data, &u_type, &set_type);
432
+
433
+ if (NIL_P(u_type)) {
434
+ u_type = INT2NUM(CCI_U_TYPE_UNKNOWN);
435
+ }
436
+
437
+ if (NIL_P(set_type)) {
438
+ set_type = INT2NUM(CCI_U_TYPE_UNKNOWN);
439
+ }
440
+
441
+ cubrid_stmt_bind_internal(stmt, NUM2INT(index), data, NUM2INT(u_type), NUM2INT(set_type));
442
+ stmt->bound = 1;
443
+
444
+ return Qnil;
445
+ }
446
+
447
+ static int
448
+ cubrid_stmt_is_auto_commitable(T_CCI_SQLX_CMD cmd)
449
+ {
450
+ switch(cmd) {
451
+ case SQLX_CMD_SELECT:
452
+ case SQLX_CMD_CALL:
453
+ case SQLX_CMD_CALL_SP:
454
+ case SQLX_CMD_COMMIT_WORK:
455
+ case SQLX_CMD_ROLLBACK_WORK:
456
+ case SQLX_CMD_GET_ISO_LVL:
457
+ case SQLX_CMD_GET_TIMEOUT:
458
+ case SQLX_CMD_GET_OPT_LVL:
459
+ case SQLX_CMD_GET_TRIGGER:
460
+ case SQLX_CMD_SAVEPOINT:
461
+ case SQLX_CMD_GET_LDB:
462
+ case SQLX_CMD_GET_STATS:
463
+ return 0;
464
+ }
465
+
466
+ return 1;
467
+ }
468
+
469
+ /* call-seq:
470
+ * execute() -> int
471
+ * execute(...) -> int
472
+ *
473
+ * prepare된 Statement를 실행하고 검색되거나 영향을 받은 row의 개수를 반환합니다.
474
+ *
475
+ * 인수가 주어지면 prepare된 Statement의 호스트 변수에 데이터를 바인딩합니다.
476
+ * 인수는 순서대로 호스트 변수에 바인딩 되기 때문에 인수의 개수와 호스트 변수의 개수가 일치해야 합니다.
477
+ * 인수는 묵시적으로 기본 타입으로 바인딩 됩니다.
478
+ *
479
+ * connection의 auto commit 모드가 true이면 commit이 곧바로 수행됩니다.
480
+ *
481
+ * con = Cubrid.connect('demodb')
482
+ * con.prepare('insert into a values (?, ?, ?, ?)') { |stmt|
483
+ * stmt.execute (10, 3.141592, 'hello', Time.local(2007, 12, 25))
484
+ * }
485
+ * con.close
486
+ */
487
+ VALUE
488
+ cubrid_stmt_execute(int argc, VALUE* argv, VALUE self)
489
+ {
490
+ T_CCI_ERROR error;
491
+ T_CCI_COL_INFO *res_col_info;
492
+ T_CCI_SQLX_CMD res_sql_type;
493
+ int res_col_count, row_count;
494
+ Statement *stmt;
495
+
496
+ GET_STMT_STRUCT(self, stmt);
497
+ CHECK_HANDLE(stmt, self);
498
+
499
+ if (!stmt->bound && stmt->param_cnt != argc) {
500
+ rb_raise(rb_eStandardError, "execute: param_count(%d) != number of argument(%d)",
501
+ stmt->param_cnt, argc);
502
+ return INT2NUM(0);
503
+ }
504
+
505
+ if (argc > 0) {
506
+ int i;
507
+ for (i = 0; i < argc; i++) {
508
+ cubrid_stmt_bind_internal(stmt, i + 1, argv[i], CCI_U_TYPE_UNKNOWN, CCI_U_TYPE_UNKNOWN);
509
+ }
510
+ }
511
+
512
+ row_count = cci_execute(stmt->handle, 0, 0, &error);
513
+ if (row_count < 0) {
514
+ cubrid_handle_error(row_count, &error);
515
+ return INT2NUM(0);
516
+ }
517
+
518
+ res_col_info = cci_get_result_info(stmt->handle, &res_sql_type, &res_col_count);
519
+ if (res_sql_type == SQLX_CMD_SELECT && !res_col_info) {
520
+ cubrid_handle_error(CUBRID_ER_CANNOT_GET_COLUMN_INFO, &error);
521
+ return INT2NUM(0);
522
+ }
523
+
524
+ stmt->col_info = res_col_info;
525
+ stmt->sql_type = res_sql_type;
526
+ stmt->col_count = res_col_count;
527
+ stmt->affected_rows = row_count;
528
+
529
+ if(stmt->con->auto_commit == Qtrue && cubrid_stmt_is_auto_commitable(stmt->sql_type)) {
530
+ cubrid_stmt_close(self);
531
+ cubrid_conn_end_tran(stmt->con, CCI_TRAN_COMMIT);
532
+ }
533
+
534
+ stmt->bound = 0;
535
+ return INT2NUM(row_count);
536
+ }
537
+
538
+ /* call-seq:
539
+ * affected_rows() -> int
540
+ *
541
+ * 수행된 SQL로 영향을 받았거나 검색된 row의 개수를 반환합니다.
542
+ */
543
+ VALUE
544
+ cubrid_stmt_affected_rows(VALUE self)
545
+ {
546
+ Statement *stmt;
547
+
548
+ GET_STMT_STRUCT(self, stmt);
549
+ CHECK_HANDLE(stmt, self);
550
+
551
+ return INT2NUM(stmt->affected_rows);
552
+ }
553
+
554
+ static VALUE
555
+ cubrid_stmt_dbval_to_ruby_value(int req_handle, int type, int index, Connection *con)
556
+ {
557
+ int res, ind;
558
+ VALUE val;
559
+ char *res_buf;
560
+ int int_val;
561
+ double double_val;
562
+ T_CCI_DATE date;
563
+ T_CCI_BIT bit;
564
+
565
+ switch (type) {
566
+ case CCI_U_TYPE_INT:
567
+ case CCI_U_TYPE_SHORT:
568
+ res = cci_get_data(req_handle, index, CCI_A_TYPE_INT, &int_val, &ind);
569
+ if (res < 0) {
570
+ cubrid_handle_error(res, NULL);
571
+ return Qnil;
572
+ }
573
+ if (ind < 0) {
574
+ val = Qnil;
575
+ } else {
576
+ val = INT2NUM(int_val);
577
+ }
578
+ break;
579
+
580
+ case CCI_U_TYPE_FLOAT:
581
+ case CCI_U_TYPE_DOUBLE:
582
+ case CCI_U_TYPE_NUMERIC:
583
+ case CCI_U_TYPE_MONETARY:
584
+ res = cci_get_data(req_handle, index, CCI_A_TYPE_STR, &res_buf, &ind);
585
+ if (res < 0) {
586
+ cubrid_handle_error(res, NULL);
587
+ return Qnil;
588
+ }
589
+ if (ind < 0) {
590
+ val = Qnil;
591
+ } else {
592
+ double_val = atof(res_buf);
593
+ val = rb_float_new(double_val);
594
+ }
595
+ break;
596
+
597
+ case CCI_U_TYPE_OBJECT:
598
+ res = cci_get_data(req_handle, index, CCI_A_TYPE_STR, &res_buf, &ind);
599
+ if (res < 0) {
600
+ cubrid_handle_error(res, NULL);
601
+ return Qnil;
602
+ }
603
+ if (ind < 0) {
604
+ val = Qnil;
605
+ } else {
606
+ val = cubrid_oid_new(con, res_buf);
607
+ }
608
+ break;
609
+
610
+ case CCI_U_TYPE_DATE:
611
+ case CCI_U_TYPE_TIME:
612
+ case CCI_U_TYPE_TIMESTAMP:
613
+ res = cci_get_data(req_handle, index, CCI_A_TYPE_DATE, &date, &ind);
614
+ if (res < 0) {
615
+ cubrid_handle_error(res, NULL);
616
+ return Qnil;
617
+ }
618
+ if (ind < 0) {
619
+ val = Qnil;
620
+ } else {
621
+ if (type == CCI_U_TYPE_DATE) {
622
+ val = rb_funcall(rb_cTime, rb_intern("mktime"), 3,
623
+ INT2NUM(date.yr), INT2NUM(date.mon), INT2NUM(date.day));
624
+ } else if (type == CCI_U_TYPE_TIME) {
625
+ val = rb_funcall(rb_cTime, rb_intern("mktime"), 7,
626
+ INT2NUM(1970), INT2NUM(1), INT2NUM(1),
627
+ INT2NUM(date.hh), INT2NUM(date.mm), INT2NUM(date.ss), INT2NUM(0));
628
+ } else {
629
+ val = rb_funcall(rb_cTime, rb_intern("mktime"), 7,
630
+ INT2NUM(date.yr), INT2NUM(date.mon), INT2NUM(date.day),
631
+ INT2NUM(date.hh), INT2NUM(date.mm), INT2NUM(date.ss), INT2NUM(0));
632
+ }
633
+ }
634
+ break;
635
+
636
+ case CCI_U_TYPE_BIT:
637
+ case CCI_U_TYPE_VARBIT:
638
+ res = cci_get_data(req_handle, index, CCI_A_TYPE_BIT, &bit, &ind);
639
+ if (res < 0) {
640
+ cubrid_handle_error(res, NULL);
641
+ return Qnil;
642
+ }
643
+ if (ind < 0) {
644
+ val = Qnil;
645
+ } else {
646
+ val = rb_tainted_str_new(bit.buf, bit.size);
647
+ }
648
+ break;
649
+
650
+ default:
651
+ res = cci_get_data(req_handle, index, CCI_A_TYPE_STR, &res_buf, &ind);
652
+ if (res < 0) {
653
+ cubrid_handle_error(res, NULL);
654
+ return Qnil;
655
+ }
656
+ if (ind < 0) {
657
+ val = Qnil;
658
+ } else {
659
+ val = rb_str_new2(res_buf);
660
+ }
661
+ break;
662
+ }
663
+
664
+ return val;
665
+ }
666
+
667
+ static VALUE
668
+ cubrid_stmt_dbval_to_ruby_value_from_set(T_CCI_SET set, int type, int index, Connection *con)
669
+ {
670
+ int res, ind;
671
+ VALUE val;
672
+ char *res_buf;
673
+ int int_val;
674
+ double double_val;
675
+ T_CCI_DATE date;
676
+ T_CCI_BIT bit;
677
+
678
+ switch (type) {
679
+ case CCI_U_TYPE_INT:
680
+ case CCI_U_TYPE_SHORT:
681
+ res = cci_set_get(set, index, CCI_A_TYPE_INT, &int_val, &ind);
682
+ if (res < 0) {
683
+ cubrid_handle_error(res, NULL);
684
+ return Qnil;
685
+ }
686
+ if (ind < 0) {
687
+ val = Qnil;
688
+ } else {
689
+ val = INT2NUM(int_val);
690
+ }
691
+ break;
692
+
693
+ case CCI_U_TYPE_FLOAT:
694
+ case CCI_U_TYPE_DOUBLE:
695
+ case CCI_U_TYPE_NUMERIC:
696
+ case CCI_U_TYPE_MONETARY:
697
+ res = cci_set_get(set, index, CCI_A_TYPE_STR, &res_buf, &ind);
698
+ if (res < 0) {
699
+ cubrid_handle_error(res, NULL);
700
+ return Qnil;
701
+ }
702
+ if (ind < 0) {
703
+ val = Qnil;
704
+ } else {
705
+ double_val = atof(res_buf);
706
+ val = rb_float_new(double_val);
707
+ }
708
+ break;
709
+
710
+ case CCI_U_TYPE_OBJECT:
711
+ res = cci_set_get(set, index, CCI_A_TYPE_STR, &res_buf, &ind);
712
+ if (res < 0) {
713
+ cubrid_handle_error(res, NULL);
714
+ return Qnil;
715
+ }
716
+ if (ind < 0) {
717
+ val = Qnil;
718
+ } else {
719
+ val = cubrid_oid_new(con, res_buf);
720
+ }
721
+ break;
722
+
723
+ case CCI_U_TYPE_DATE:
724
+ case CCI_U_TYPE_TIME:
725
+ case CCI_U_TYPE_TIMESTAMP:
726
+ res = cci_set_get(set, index, CCI_A_TYPE_DATE, &date, &ind);
727
+ if (res < 0) {
728
+ cubrid_handle_error(res, NULL);
729
+ return Qnil;
730
+ }
731
+ if (ind < 0) {
732
+ val = Qnil;
733
+ } else {
734
+ if (type == CCI_U_TYPE_DATE) {
735
+ val = rb_funcall(rb_cTime, rb_intern("mktime"), 3,
736
+ INT2NUM(date.yr), INT2NUM(date.mon), INT2NUM(date.day));
737
+ } else if (type == CCI_U_TYPE_TIME) {
738
+ val = rb_funcall(rb_cTime, rb_intern("mktime"), 7,
739
+ INT2NUM(1970), INT2NUM(1), INT2NUM(1),
740
+ INT2NUM(date.hh), INT2NUM(date.mm), INT2NUM(date.ss), INT2NUM(0));
741
+ } else {
742
+ val = rb_funcall(rb_cTime, rb_intern("mktime"), 7,
743
+ INT2NUM(date.yr), INT2NUM(date.mon), INT2NUM(date.day),
744
+ INT2NUM(date.hh), INT2NUM(date.mm), INT2NUM(date.ss), INT2NUM(0));
745
+ }
746
+ }
747
+ break;
748
+
749
+ case CCI_U_TYPE_BIT:
750
+ case CCI_U_TYPE_VARBIT:
751
+ res = cci_set_get(set, index, CCI_A_TYPE_BIT, &bit, &ind);
752
+ if (res < 0) {
753
+ cubrid_handle_error(res, NULL);
754
+ return Qnil;
755
+ }
756
+ if (ind < 0) {
757
+ val = Qnil;
758
+ } else {
759
+ val = rb_tainted_str_new(bit.buf, bit.size);
760
+ }
761
+ break;
762
+
763
+ default:
764
+ res = cci_set_get(set, index, CCI_A_TYPE_STR, &res_buf, &ind);
765
+ if (res < 0) {
766
+ cubrid_handle_error(res, NULL);
767
+ return Qnil;
768
+ }
769
+ if (ind < 0) {
770
+ val = Qnil;
771
+ } else {
772
+ val = rb_str_new2(res_buf);
773
+ }
774
+ break;
775
+ }
776
+
777
+ return val;
778
+ }
779
+
780
+ static VALUE
781
+ cubrid_stmt_dbset_to_ruby_value(int req_handle, int index, Connection *con)
782
+ {
783
+ int i, res, ind, e_type;
784
+ VALUE val, e;
785
+ T_CCI_SET set = NULL;
786
+ int set_size;
787
+
788
+ res = cci_get_data(req_handle, index, CCI_A_TYPE_SET, &set, &ind);
789
+ if (res < 0) {
790
+ cubrid_handle_error(res, NULL);
791
+ return Qnil;
792
+ }
793
+
794
+ if (set == NULL)
795
+ return Qnil;
796
+
797
+ set_size = cci_set_size(set);
798
+ val = rb_ary_new2(set_size);
799
+
800
+ e_type = cci_set_element_type(set);
801
+
802
+ for (i = 0; i < set_size; i++) {
803
+ e = cubrid_stmt_dbval_to_ruby_value_from_set(set, e_type, i + 1, con);
804
+ rb_ary_push(val, e);
805
+ }
806
+ cci_set_free(set);
807
+
808
+ return val;
809
+ }
810
+
811
+ VALUE
812
+ cubrid_stmt_fetch_one_row(int req_handle, int col_count, T_CCI_COL_INFO *col_info, Connection *con)
813
+ {
814
+ int i, type;
815
+ VALUE row, val;
816
+
817
+ row = rb_ary_new();
818
+
819
+ for (i = 0; i < col_count; i++) {
820
+ type = CCI_GET_RESULT_INFO_TYPE(col_info, i + 1);
821
+
822
+ if (CCI_IS_COLLECTION_TYPE(type)) {
823
+ val = cubrid_stmt_dbset_to_ruby_value(req_handle, i + 1, con);
824
+ } else {
825
+ val = cubrid_stmt_dbval_to_ruby_value(req_handle, type, i + 1, con);
826
+ }
827
+
828
+ rb_ary_push(row, val);
829
+ }
830
+
831
+ return row;
832
+ }
833
+
834
+ /* call-seq:
835
+ * fetch() -> array or nil
836
+ *
837
+ * 검색된 결과에서 현재 커서가 위치한 row를 반환하고 커서의 위치를 다음으로 옮깁니다.
838
+ * row의 column들은 배열에 저장되어 반환됩니다.
839
+ * 더 이상 fetch할 row가 없으면 nil을 반환합니다.
840
+ *
841
+ * con = Cubrid.connect('demodb')
842
+ * con.prepare('SELECT * FROM db_user') { |stmt|
843
+ * stmt.execute
844
+ * r = stmt.fetch
845
+ * print r[0]
846
+ * }
847
+ * con.close
848
+ *
849
+ * 데이터베이스의 타입은 아래와 같이 루비의 타입으로 대응됩니다.
850
+ *
851
+ * *int, short -> fixnum, bignum
852
+ * *float, double, numeric, monetary -> float
853
+ * *char, varchar, ncahr, varnchar -> string
854
+ * *bit, varbit -> string
855
+ * *date, time, timestamp -> Time
856
+ * *object -> Oid
857
+ * *collection -> array
858
+ *
859
+ */
860
+ VALUE
861
+ cubrid_stmt_fetch(VALUE self)
862
+ {
863
+ int res;
864
+ T_CCI_ERROR error;
865
+ Statement *stmt;
866
+
867
+ GET_STMT_STRUCT(self, stmt);
868
+ CHECK_HANDLE(stmt, self);
869
+
870
+ res = cci_cursor(stmt->handle, 1, CCI_CURSOR_CURRENT, &error);
871
+ if (res == CCI_ER_NO_MORE_DATA) {
872
+ return Qnil;
873
+ } else if (res < 0) {
874
+ cubrid_handle_error(res, &error);
875
+ return Qnil;
876
+ }
877
+
878
+ res = cci_fetch(stmt->handle, &error);
879
+ if (res < 0) {
880
+ cubrid_handle_error(res, &error);
881
+ return Qnil;
882
+ }
883
+
884
+ return cubrid_stmt_fetch_one_row(stmt->handle, stmt->col_count, stmt->col_info, stmt->con);
885
+ }
886
+
887
+ /* call-seq:
888
+ * fetch_hash() -> hash or nil
889
+ *
890
+ * 검색된 결과에서 현재 커서가 위치한 row를 반환하고 커서의 위치를 다음으로 옮깁니다.
891
+ * row의 column들은 hash에 저장되어 반환됩니다.
892
+ * hash의 key는 column 이름이거나 SELECT 문에 주어진 alias 입니다.
893
+ * 더 이상 fetch할 row가 없으면 nil을 반환합니다.
894
+ *
895
+ * con = Cubrid.connect('demodb')
896
+ * con.prepare('SELECT * FROM db_user') { |stmt|
897
+ * stmt.execute
898
+ * r = stmt.fetch_hash
899
+ * print r['name']
900
+ * }
901
+ * con.close
902
+ */
903
+ VALUE
904
+ cubrid_stmt_fetch_hash(VALUE self)
905
+ {
906
+ VALUE row, col, hash;
907
+ int i;
908
+ char colName[128];
909
+ Statement *stmt;
910
+
911
+ GET_STMT_STRUCT(self, stmt);
912
+ CHECK_HANDLE(stmt, self);
913
+
914
+ row = cubrid_stmt_fetch(self);
915
+ if (NIL_P(row))
916
+ return Qnil;
917
+
918
+ hash = rb_hash_new();
919
+ for(i = 0; i < stmt->col_count; i++) {
920
+ col = RARRAY(row)->ptr[i];
921
+ strcpy(colName, CCI_GET_RESULT_INFO_NAME(stmt->col_info, i+1));
922
+ rb_hash_aset(hash, rb_str_new2(colName), col);
923
+ }
924
+
925
+ return hash;
926
+ }
927
+
928
+ /* call-seq:
929
+ * each() { |row| block } -> nil
930
+ *
931
+ * row를 fetch하여 주어진 block에 전달하여 수행시킵니다. 전달되는 row는 array입니다.
932
+ * block은 row가 fetch될 때마다 수행됩니다.
933
+ *
934
+ * con = Cubrid.connect('demodb')
935
+ * con.prepare('SELECT * FROM db_user') { |stmt|
936
+ * stmt.execute
937
+ * stmt.each { |r|
938
+ * print r[0]
939
+ * }
940
+ * }
941
+ * con.close
942
+ */
943
+ VALUE
944
+ cubrid_stmt_each(VALUE self)
945
+ {
946
+ VALUE row;
947
+
948
+ while(1) {
949
+ row = cubrid_stmt_fetch(self);
950
+ if (NIL_P(row)) {
951
+ break;
952
+ }
953
+ rb_yield(row);
954
+ }
955
+
956
+ return Qnil;
957
+ }
958
+
959
+ /* call-seq:
960
+ * each_hash() { |hash| block } -> nil
961
+ *
962
+ * row를 fetch하여 주어진 block에 전달하여 수행시킵니다. 전달되는 row는 hash입니다.
963
+ * block은 row가 fetch될 때마다 수행됩니다.
964
+ *
965
+ * con = Cubrid.connect('demodb')
966
+ * con.prepare('SELECT * FROM db_user') { |stmt|
967
+ * stmt.execute
968
+ * stmt.each_hash { |r|
969
+ * print r['name']
970
+ * }
971
+ * }
972
+ * con.close
973
+ */
974
+ VALUE
975
+ cubrid_stmt_each_hash(VALUE self)
976
+ {
977
+ VALUE row;
978
+
979
+ while(1) {
980
+ row = cubrid_stmt_fetch_hash(self);
981
+ if (NIL_P(row)) {
982
+ break;
983
+ }
984
+ rb_yield(row);
985
+ }
986
+
987
+ return Qnil;
988
+ }
989
+
990
+ /* call-seq:
991
+ * column_info() -> array
992
+ *
993
+ * fetch 될 row의 column 정보를 반환합니다.
994
+ * 한 컬럼의 정보가 hash로 구성되기 때문에, 결국 전체 컬럼 정보는 hash를 저장하고 있는 array로 반환됩니다.
995
+ * hash의 key는 name, type_name, precision, scale, nullable 이며,
996
+ * 각각 컬럼 이름, 컬럼의 데이터 타입, 정밀도, 스케일, 널가능 여부를 의미합니다.
997
+ *
998
+ * con = Cubrid.connect('demodb')
999
+ * con.prepare('SELECT * FROM db_user') { |stmt|
1000
+ * stmt.column_info.each { |col|
1001
+ * print col['name']
1002
+ * print col['type_name']
1003
+ * print col['precision']
1004
+ * print col['scale']
1005
+ * print col['nullable']
1006
+ * }
1007
+ * }
1008
+ * con.close
1009
+ */
1010
+ VALUE
1011
+ cubrid_stmt_column_info(VALUE self)
1012
+ {
1013
+ VALUE desc;
1014
+ int i;
1015
+ char col_name[MAX_STR_LEN];
1016
+ int datatype, precision, scale, nullable;
1017
+ Statement *stmt;
1018
+
1019
+ GET_STMT_STRUCT(self, stmt);
1020
+ CHECK_HANDLE(stmt, self);
1021
+
1022
+ desc = rb_ary_new2(stmt->col_count);
1023
+
1024
+ for (i = 0; i < stmt->col_count; i++) {
1025
+ VALUE item;
1026
+
1027
+ item = rb_hash_new();
1028
+
1029
+ strcpy(col_name, CCI_GET_RESULT_INFO_NAME(stmt->col_info, i+1));
1030
+ precision = CCI_GET_RESULT_INFO_PRECISION(stmt->col_info, i+1);
1031
+ scale = CCI_GET_RESULT_INFO_SCALE(stmt->col_info, i+1);
1032
+ nullable = CCI_GET_RESULT_INFO_IS_NON_NULL(stmt->col_info, i+1);
1033
+ datatype = CCI_GET_RESULT_INFO_TYPE(stmt->col_info, i+1);
1034
+
1035
+ rb_hash_aset(item, rb_str_new2("name"), rb_str_new2(col_name));
1036
+ rb_hash_aset(item, rb_str_new2("type_name"), INT2NUM(datatype));
1037
+ rb_hash_aset(item, rb_str_new2("precision"), INT2NUM(precision));
1038
+ rb_hash_aset(item, rb_str_new2("scale"), INT2NUM(scale));
1039
+ rb_hash_aset(item, rb_str_new2("nullable"), INT2NUM(nullable));
1040
+
1041
+ rb_ary_push(desc, item);
1042
+ }
1043
+
1044
+ return desc;
1045
+ }
1046
+
1047
+ /* call-seq:
1048
+ * get_oid() -> Oid
1049
+ *
1050
+ * Cubrid::INCLUDE_OID 옵션으로 prepare된 Statement인 경우 실행 결과에 OID가 포함되어 있습니다.
1051
+ * get_oid()는 이 OID를 다룰 수 있도록 Oid 객체를 생성하여 반환합니다.
1052
+ *
1053
+ * con = Cubrid.connect('demodb')
1054
+ * con.prepare('SELECT * FROM db_user', CUBRID::INCLUDE_OID) { |stmt|
1055
+ * stmt.execute
1056
+ * stmt.fetch
1057
+ * oid = stmt.get_oid
1058
+ * print oid.table
1059
+ * }
1060
+ * con.close
1061
+ */
1062
+ VALUE
1063
+ cubrid_stmt_get_oid(VALUE self)
1064
+ {
1065
+ Statement *stmt;
1066
+ char oid_str[MAX_STR_LEN];
1067
+
1068
+ GET_STMT_STRUCT(self, stmt);
1069
+ CHECK_HANDLE(stmt, self);
1070
+
1071
+ cci_get_cur_oid(stmt->handle, oid_str);
1072
+ return cubrid_oid_new(stmt->con, oid_str);
1073
+ }
1074
+