cubrid 0.6-x86-mswin32-60

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 (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
+