dmRuby 1.0.3 → 1.0.4

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.
data/ext/client.c CHANGED
@@ -1,540 +1,548 @@
1
- #include <dm_ext.h>
2
-
3
- #include <time.h>
4
- #include <errno.h>
5
- #ifndef _WIN32
6
- #include <sys/types.h>
7
- #include <sys/socket.h>
8
- #endif
9
- #ifndef _MSC_VER
10
- #include <unistd.h>
11
- #endif
12
- #include <fcntl.h>
13
- #include "dm_enc_name_to_ruby.h"
14
-
15
- VALUE cdmClient;
16
- extern VALUE mdm, cdmError;
17
- static VALUE sym_id, sym_version, sym_header_version, sym_async, sym_symbolize_keys, sym_as, sym_array, sym_stream;
18
- static VALUE sym_no_good_index_used, sym_no_index_used, sym_query_was_slow;
19
- static ID intern_brackets, intern_merge, intern_merge_bang, intern_new_with_args,
20
- intern_current_query_options, intern_read_timeout;
21
-
22
- #define REQUIRE_INITIALIZED(wrapper) \
23
- if (!wrapper->initialized) { \
24
- rb_raise(cdmError, "DM client is not initialized"); \
25
- }
26
-
27
- #define CONNECTED(wrapper) (wrapper->active == 1 && wrapper->closed != 1)
28
-
29
- #define REQUIRE_CONNECTED(wrapper) \
30
- REQUIRE_INITIALIZED(wrapper) \
31
- if ((wrapper->closed) && !wrapper->reconnect_enabled) { \
32
- rb_raise(cdmError, "DM client is not connected"); \
33
- }
34
-
35
- #define REQUIRE_NOT_CONNECTED(wrapper) \
36
- REQUIRE_INITIALIZED(wrapper) \
37
- if ((wrapper->active)) { \
38
- rb_raise(cdmError, "DM connection is already open"); \
39
- }
40
-
41
- #define DM_LINK_VERSION 8
42
-
43
- /*
44
- * used to pass all arguments to dm_real_connect while inside
45
- * rb_thread_call_without_gvl
46
- */
47
- struct nogvl_connect_args {
48
- dm_client_wrapper *wrapper;
49
- const char *server;
50
- const char *user;
51
- const char *passwd;
52
- };
53
-
54
- /*
55
- * used to pass all arguments to dm_send_query while inside
56
- * rb_thread_call_without_gvl
57
- */
58
- struct nogvl_send_query_args {
59
- dhcon *con;
60
- VALUE sql;
61
- const char *sql_ptr;
62
- long sql_len;
63
- dm_client_wrapper *wrapper;
64
- };
65
-
66
-
67
- /*
68
- * non-blocking dm_*() functions that we won't be wrapping since
69
- * they do not appear to hit the network nor issue any interruptible
70
- * or blocking system calls.
71
- *
72
- * - dm_affected_rows()
73
- * - dm_error()
74
- * - dm_fetch_fields()
75
- * - dm_fetch_lengths() - calls cli_fetch_lengths or emb_fetch_lengths
76
- * - dm_field_count()
77
- * - dm_get_client_info()
78
- * - dm_get_client_version()
79
- * - dm_get_server_info()
80
- * - dm_get_server_version()
81
- * - dm_insert_id()
82
- * - dm_num_fields()
83
- * - dm_num_rows()
84
- * - dm_options()
85
- * - dm_real_escape_string()
86
- * - dm_ssl_set()
87
- */
88
-
89
- static void rb_dm_client_mark(void * wrapper)
90
- {
91
- dm_client_wrapper * w = wrapper;
92
- if (w) {
93
- rb_gc_mark_movable(w->encoding);
94
- rb_gc_mark_movable(w->active_fiber);
95
- }
96
- }
97
-
98
- /* this is called during GC */
99
- static void rb_dm_client_free(void *ptr) {
100
- dm_client_wrapper *wrapper = ptr;
101
- decr_dm_client(wrapper);
102
- }
103
-
104
- static size_t rb_dm_client_memsize(const void * wrapper) {
105
- const dm_client_wrapper * w = wrapper;
106
- return sizeof(*w);
107
- }
108
-
109
- static void rb_dm_client_compact(void * wrapper) {
110
- dm_client_wrapper * w = wrapper;
111
- if (w) {
112
- rb_dm_gc_location(w->encoding);
113
- rb_dm_gc_location(w->active_fiber);
114
- }
115
- }
116
-
117
- const rb_data_type_t rb_dm_client_type = {
118
- "rb_dm_client",
119
- {
120
- rb_dm_client_mark,
121
- rb_dm_client_free,
122
- rb_dm_client_memsize,
123
- #ifdef HAVE_RB_GC_MARK_MOVABLE
124
- rb_dm_client_compact,
125
- #endif
126
- },
127
- 0,
128
- 0,
129
- #ifdef RUBY_TYPED_FREE_IMMEDIATELY
130
- RUBY_TYPED_FREE_IMMEDIATELY,
131
- #endif
132
- };
133
-
134
- struct nogvl_errors_args {
135
- void* hndl;
136
- sdbyte errormsg[4096];
137
- sdint4 errorCode;
138
- sdint2 hndl_type;
139
- };
140
-
141
- static void *nogvl_get_error(void *ptr) {
142
- struct nogvl_errors_args *args = ptr;
143
- sdbyte error_buf[4096];
144
- dpi_get_diag_rec(args->hndl_type, args->hndl, 1, &args->errorCode, error_buf, sizeof(error_buf), NULL);
145
- sprintf(args->errormsg,"[CODE:%d]%s",args->errorCode, error_buf);
146
- return (void*)Qtrue;
147
- }
148
-
149
-
150
- static VALUE rb_raise_dm_error(dm_client_wrapper *wrapper, void* hndl, sdint2 hndl_type) {
151
- VALUE rb_error_msg;
152
- VALUE e;
153
- const struct dm_enc_name_to_rb_map *dmrb;
154
- struct nogvl_errors_args err;
155
-
156
- err.hndl = hndl;
157
- err.hndl_type = hndl_type;
158
-
159
- rb_thread_call_without_gvl(nogvl_get_error, &err, RUBY_UBF_IO, 0);
160
- rb_error_msg = rb_str_new2(err.errormsg);
161
-
162
- dmrb = dm_enc_name_to_rb(wrapper->encode_code);
163
- rb_enc_associate(rb_error_msg, rb_enc_find(dmrb->rb_name));
164
- e = rb_funcall(cdmError, intern_new_with_args, 2,
165
- rb_error_msg,
166
- UINT2NUM(err.errorCode));
167
- rb_exc_raise(e);
168
- }
169
-
170
- static void *nogvl_connect(void *ptr) {
171
- struct nogvl_connect_args *args = ptr;
172
- DPIRETURN rt;
173
- rt = dpi_alloc_env(&args->wrapper->env);
174
- if(!(DSQL_SUCCEEDED(rt)))
175
- return (void*)Qfalse;
176
- rt = dpi_alloc_con(args->wrapper->env,&args->wrapper->client);
177
- if(!(DSQL_SUCCEEDED(rt)))
178
- return (void*)Qfalse;
179
- rt = dpi_set_con_attr(args->wrapper->client,DSQL_ATTR_LOCAL_CODE,(dpointer)args->wrapper->encode_code,0);
180
- if(!(DSQL_SUCCEEDED(rt)))
181
- return (void*)Qfalse;
182
- rt = dpi_login(args->wrapper->client,args->server,args->user,args->passwd);
183
- if(!(DSQL_SUCCEEDED(rt)))
184
- return (void*)Qfalse;
185
- rt = dpi_set_con_attr(args->wrapper->client,DSQL_ATTR_AUTOCOMMIT,(dpointer)1,0);
186
- if(!(DSQL_SUCCEEDED(rt)))
187
- return (void*)Qfalse;
188
-
189
- return(void*)Qtrue;
190
- }
191
-
192
- static void *nogvl_close(void *ptr) {
193
- dm_client_wrapper *wrapper = ptr;
194
-
195
- if (wrapper->initialized && !wrapper->closed) {
196
- dpi_logout(wrapper->client);
197
- dpi_free_con(wrapper->client);
198
- dpi_free_env(wrapper->env);
199
- wrapper->closed = 1;
200
- wrapper->active = 0;
201
- wrapper->reconnect_enabled = 0;
202
- wrapper->active_fiber = Qnil;
203
- }
204
-
205
- return NULL;
206
- }
207
-
208
- void decr_dm_client(dm_client_wrapper *wrapper)
209
- {
210
- wrapper->refcount--;
211
-
212
- if (wrapper->refcount == 0) {
213
- xfree(wrapper);
214
- }
215
- }
216
-
217
- static VALUE allocate(VALUE klass) {
218
- VALUE obj;
219
- dm_client_wrapper * wrapper;
220
- #ifdef NEW_TYPEDDATA_WRAPPER
221
- obj = TypedData_Make_Struct(klass, dm_client_wrapper, &rb_dm_client_type, wrapper);
222
- #else
223
- obj = Data_Make_Struct(klass, dm_client_wrapper, rb_dm_client_mark, rb_dm_client_free, wrapper);
224
- #endif
225
- wrapper->encoding = Qnil;
226
- wrapper->active_fiber = Qnil;
227
- wrapper->server_version = 0;
228
- wrapper->reconnect_enabled = 0;
229
- wrapper->connect_timeout = 0;
230
- wrapper->initialized = 0; /* will be set true after calling dm_init */
231
- wrapper->closed = 1; /* will be set false after calling dm_real_connect */
232
- wrapper->refcount = 1;
233
- wrapper->active = 0;
234
-
235
- return obj;
236
- }
237
-
238
- static VALUE rb_dm_connect(VALUE self, VALUE user, VALUE pass, VALUE servers) {
239
- struct nogvl_connect_args args;
240
- time_t start_time, end_time, elapsed_time, connect_timeout;
241
- VALUE rv;
242
- GET_CLIENT(self);
243
-
244
- args.server = NIL_P(servers) ? NULL : StringValueCStr(servers);
245
- args.user = NIL_P(user) ? NULL : StringValueCStr(user);
246
- args.passwd = NIL_P(pass) ? NULL : StringValueCStr(pass);
247
- args.wrapper = wrapper;
248
-
249
- rv = (VALUE) rb_thread_call_without_gvl(nogvl_connect, &args, RUBY_UBF_IO, 0);
250
-
251
- if (rv == Qfalse)
252
- rb_raise_dm_error(wrapper, wrapper->client, DSQL_HANDLE_DBC);
253
- wrapper->closed = 0;
254
- wrapper->server_version = 8;
255
- wrapper->active = 1;
256
- wrapper->initialized = 1;
257
- return self;
258
- }
259
-
260
- /* call-seq:
261
- * client.closed
262
- *
263
- * @return [Boolean]
264
- */
265
- static VALUE rb_dm_client_close(VALUE self) {
266
- GET_CLIENT(self);
267
-
268
- if (wrapper->client) {
269
- rb_thread_call_without_gvl(nogvl_close, wrapper, RUBY_UBF_IO, 0);
270
- }
271
-
272
- return Qnil;
273
- }
274
-
275
- /* call-seq:
276
- * client.closed?
277
- *
278
- * @return [Boolean]
279
- */
280
- static VALUE rb_dm_client_closed(VALUE self) {
281
- GET_CLIENT(self);
282
- return CONNECTED(wrapper) ? Qfalse : Qtrue;
283
- }
284
-
285
-
286
- static void *nogvl_send_query(void *ptr) {
287
- struct nogvl_send_query_args *args = ptr;
288
- DPIRETURN rt;
289
- dhstmt hstmt;
290
- udint2 col_num;
291
- sdint4 nStmtType;
292
- sdbyte lastrowid[12];
293
- udint4 len;
294
-
295
- args->wrapper->stmt = NULL;
296
- rt = dpi_alloc_stmt(args->con,&hstmt);
297
- if(!DSQL_SUCCEEDED(rt))
298
- {
299
- return (void*)Qfalse;
300
- }
301
- args->wrapper->stmt = hstmt;
302
-
303
- rt = dpi_set_stmt_attr(hstmt,DSQL_ATTR_CURSOR_TYPE,(dpointer)DSQL_CURSOR_DYNAMIC,0);
304
- if(!DSQL_SUCCEEDED(rt))
305
- {
306
- return (void*)Qfalse;
307
- }
308
- rt = dpi_exec_direct(hstmt,args->sql_ptr);
309
- if(!DSQL_SUCCEEDED(rt))
310
- {
311
- return (void*)Qfalse;
312
- }
313
- rt = dpi_get_diag_field(DSQL_HANDLE_STMT, hstmt, 0,
314
- DSQL_DIAG_DYNAMIC_FUNCTION_CODE,
315
- (dpointer)&nStmtType, 0, NULL);
316
- if(!DSQL_SUCCEEDED(rt))
317
- {
318
- return (void*)Qfalse;
319
- }
320
- //lastrowid
321
- if(nStmtType == DSQL_DIAG_FUNC_CODE_INSERT ||
322
- nStmtType == DSQL_DIAG_FUNC_CODE_UPDATE ||
323
- nStmtType == DSQL_DIAG_FUNC_CODE_DELETE)
324
- {
325
- rt = dpi_get_diag_field(DSQL_HANDLE_STMT, hstmt, 0, DSQL_DIAG_ROWID, &lastrowid, sizeof(lastrowid), NULL);
326
- rt = dpi_rowid_to_char(args->con,lastrowid,sizeof(lastrowid),args->wrapper->lastrowid,sizeof(args->wrapper->lastrowid),&len);
327
- }
328
- else
329
- {
330
- strncpy(args->wrapper->lastrowid, "", 20);
331
- }
332
-
333
- // affected_rows
334
- if(nStmtType == DSQL_DIAG_FUNC_CODE_INSERT ||
335
- nStmtType == DSQL_DIAG_FUNC_CODE_UPDATE ||
336
- nStmtType == DSQL_DIAG_FUNC_CODE_DELETE ||
337
- nStmtType == DSQL_DIAG_FUNC_CODE_CALL)
338
- {
339
- rt = dpi_row_count(hstmt,&args->wrapper->affected_rows);
340
- }
341
- else{
342
- args->wrapper->affected_rows = 0;
343
- }
344
- //if have a result
345
- if(nStmtType == DSQL_DIAG_FUNC_CODE_SELECT ||
346
- nStmtType == DSQL_DIAG_FUNC_CODE_CALL)
347
- {
348
- args->wrapper->is_select = 1;
349
- }
350
-
351
- return (void*)Qtrue;
352
- }
353
-
354
- static VALUE do_send_query(VALUE args) {
355
- struct nogvl_send_query_args *query_args = (void *)args;
356
- dm_client_wrapper *wrapper = query_args->wrapper;
357
- wrapper->is_select = 0;
358
- if ((VALUE)rb_thread_call_without_gvl(nogvl_send_query, query_args, RUBY_UBF_IO, 0) == Qfalse) {
359
- if(wrapper->stmt == NULL)
360
- rb_raise(cdmError, "failed to alloc statement");
361
- else
362
- rb_raise_dm_error(wrapper, wrapper->stmt, DSQL_HANDLE_STMT);
363
- }
364
- return Qnil;
365
- }
366
-
367
- /* call-seq:
368
- * client.query(sql)
369
- *
370
- * Query the database with +sql+, with optional +options+. For the possible
371
- * options, see default_query_options on the dm::Client class.
372
- */
373
- static VALUE rb_dm_query(VALUE self, VALUE sql, VALUE options) {
374
- struct nogvl_send_query_args args;
375
- VALUE resultobj;
376
- GET_CLIENT(self);
377
-
378
- REQUIRE_CONNECTED(wrapper);
379
- args.con = wrapper->client;
380
-
381
- Check_Type(sql, T_STRING);
382
- /* ensure the string is in the encoding the connection is expecting */
383
- args.sql = rb_str_export_to_enc(sql, rb_to_encoding(wrapper->encoding));
384
- args.sql_ptr = RSTRING_PTR(args.sql);
385
- args.sql_len = RSTRING_LEN(args.sql);
386
- args.wrapper = wrapper;
387
-
388
- do_send_query((VALUE)&args);
389
- (void)RB_GC_GUARD(sql);
390
-
391
- if(wrapper->is_select == 1)
392
- {
393
- resultobj = rb_dm_result_to_obj(self, wrapper->encoding, wrapper->stmt, 0 , options);
394
- wrapper->stmt = NULL;
395
- return resultobj;
396
- }
397
- else
398
- {
399
- dpi_free_stmt(wrapper->stmt);
400
- wrapper->stmt = NULL;
401
- }
402
-
403
- /* this will just block until the result is ready */
404
- return Qnil;
405
- }
406
-
407
-
408
- /* call-seq:
409
- * client.last_id
410
- *
411
- * Returns the value generated for an AUTO_INCREMENT column by the previous INSERT or UPDATE
412
- * statement.
413
- */
414
- static VALUE rb_dm_client_last_id(VALUE self) {
415
- GET_CLIENT(self);
416
- REQUIRE_CONNECTED(wrapper);
417
- return rb_str_new2(wrapper->lastrowid);
418
- }
419
-
420
-
421
- /* call-seq:
422
- * client.affected_rows
423
- *
424
- * returns the number of rows changed, deleted, or inserted by the last statement
425
- * if it was an UPDATE, DELETE, or INSERT.
426
- */
427
- static VALUE rb_dm_client_affected_rows(VALUE self) {
428
- GET_CLIENT(self);
429
- REQUIRE_CONNECTED(wrapper);
430
- return ULL2NUM(wrapper->affected_rows);
431
- }
432
-
433
-
434
- /* call-seq:
435
- * client.encoding
436
- *
437
- * Returns the encoding set on the client.
438
- */
439
- static VALUE rb_dm_client_encoding(VALUE self) {
440
- GET_CLIENT(self);
441
- return wrapper->encoding;
442
- }
443
-
444
- static void *nogvl_ping(void *ptr) {
445
- dhcon client = ptr;
446
- sdint4 is_dead = 1;
447
-
448
- dpi_get_con_attr(client, DSQL_ATTR_CONNECTION_DEAD, &is_dead, sizeof(sdint4), NULL);
449
-
450
- return (void *)( is_dead == 0 ? Qtrue : Qfalse);
451
- }
452
-
453
- static VALUE rb_dm_client_ping(VALUE self) {
454
- GET_CLIENT(self);
455
-
456
- if (!CONNECTED(wrapper)) {
457
- return Qfalse;
458
- } else {
459
- return (VALUE)rb_thread_call_without_gvl(nogvl_ping, wrapper->client, RUBY_UBF_IO, 0);
460
- }
461
- }
462
-
463
- static VALUE set_charset_name(VALUE self, VALUE value) {
464
- int code;
465
- DPIRETURN rt;
466
- const struct dm_enc_name_to_rb_map *dmrb;
467
- rb_encoding *enc;
468
- VALUE rb_enc;
469
- GET_CLIENT(self);
470
-
471
- code = NUM2INT(value);
472
- if(code<1 || code>11)
473
- code = 1;
474
- dmrb = dm_enc_name_to_rb(code);
475
- if (dmrb == NULL || dmrb->rb_name == NULL) {
476
- rb_raise(cdmError, "Unsupported charset");
477
- } else {
478
- enc = rb_enc_find(dmrb->rb_name);
479
- rb_enc = rb_enc_from_encoding(enc);
480
- wrapper->encoding = rb_enc;
481
- }
482
-
483
- wrapper->encode_code = code;
484
- return value;
485
- }
486
-
487
- /* call-seq: client.prepare # => dm::Statement
488
- *
489
- * Create a new prepared statement.
490
- */
491
- static VALUE rb_dm_client_prepare_statement(VALUE self, VALUE sql) {
492
- GET_CLIENT(self);
493
- REQUIRE_CONNECTED(wrapper);
494
- VALUE obj = rb_dm_stmt_new(self,sql);
495
- return obj;
496
- }
497
-
498
- void init_dm_client() {
499
- #if 0
500
- mdm = rb_define_module("dm"); Teach RDoc about dm constant.
501
- #endif
502
- cdmClient = rb_define_class_under(mdm, "Client", rb_cObject);
503
- rb_global_variable(&cdmClient);
504
- rb_define_alloc_func(cdmClient, allocate);
505
-
506
- rb_define_method(cdmClient, "close", rb_dm_client_close, 0);
507
- rb_define_method(cdmClient, "closed?", rb_dm_client_closed, 0);
508
- rb_define_method(cdmClient, "last_id", rb_dm_client_last_id, 0);
509
- rb_define_method(cdmClient, "affected_rows", rb_dm_client_affected_rows, 0);
510
- rb_define_method(cdmClient, "prepare", rb_dm_client_prepare_statement, 1);
511
- rb_define_method(cdmClient, "encoding", rb_dm_client_encoding, 0);
512
- rb_define_method(cdmClient, "ping", rb_dm_client_ping, 0);
513
-
514
- rb_define_private_method(cdmClient, "charset_name=", set_charset_name, 1);
515
- rb_define_private_method(cdmClient, "connect", rb_dm_connect, 3);
516
- rb_define_private_method(cdmClient, "_query", rb_dm_query, 2);
517
-
518
- sym_id = ID2SYM(rb_intern("id"));
519
- sym_version = ID2SYM(rb_intern("version"));
520
- sym_header_version = ID2SYM(rb_intern("header_version"));
521
- sym_async = ID2SYM(rb_intern("async"));
522
- sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
523
- sym_as = ID2SYM(rb_intern("as"));
524
- sym_array = ID2SYM(rb_intern("array"));
525
- sym_stream = ID2SYM(rb_intern("stream"));
526
-
527
- sym_no_good_index_used = ID2SYM(rb_intern("no_good_index_used"));
528
- sym_no_index_used = ID2SYM(rb_intern("no_index_used"));
529
- sym_query_was_slow = ID2SYM(rb_intern("query_was_slow"));
530
-
531
- intern_brackets = rb_intern("[]");
532
- intern_merge = rb_intern("merge");
533
- intern_merge_bang = rb_intern("merge!");
534
- intern_new_with_args = rb_intern("new_with_args");
535
- intern_current_query_options = rb_intern("@current_query_options");
536
- intern_read_timeout = rb_intern("@read_timeout");
537
-
538
- rb_const_set(cdmClient, rb_intern("SECURE_CONNECTION"), LONG2NUM(0));
539
-
540
- }
1
+ #include <dm_ext.h>
2
+
3
+ #include <time.h>
4
+ #include <errno.h>
5
+ #ifndef _WIN32
6
+ #include <sys/types.h>
7
+ #include <sys/socket.h>
8
+ #endif
9
+ #ifndef _MSC_VER
10
+ #include <unistd.h>
11
+ #endif
12
+ #include <fcntl.h>
13
+ #include "dm_enc_name_to_ruby.h"
14
+
15
+ VALUE cdmClient;
16
+ extern VALUE mdm, cdmError;
17
+ static VALUE sym_id, sym_version, sym_header_version, sym_async, sym_symbolize_keys, sym_as, sym_array, sym_stream;
18
+ static VALUE sym_no_good_index_used, sym_no_index_used, sym_query_was_slow;
19
+ static ID intern_brackets, intern_merge, intern_merge_bang, intern_new_with_args,
20
+ intern_current_query_options, intern_read_timeout;
21
+
22
+ #define REQUIRE_INITIALIZED(wrapper) \
23
+ if (!wrapper->initialized) { \
24
+ rb_raise(cdmError, "DM client is not initialized"); \
25
+ }
26
+
27
+ #define CONNECTED(wrapper) (wrapper->active == 1 && wrapper->closed != 1)
28
+
29
+ #define REQUIRE_CONNECTED(wrapper) \
30
+ REQUIRE_INITIALIZED(wrapper) \
31
+ if ((wrapper->closed) && !wrapper->reconnect_enabled) { \
32
+ rb_raise(cdmError, "DM client is not connected"); \
33
+ }
34
+
35
+ #define REQUIRE_NOT_CONNECTED(wrapper) \
36
+ REQUIRE_INITIALIZED(wrapper) \
37
+ if ((wrapper->active)) { \
38
+ rb_raise(cdmError, "DM connection is already open"); \
39
+ }
40
+
41
+ #define DM_LINK_VERSION 8
42
+
43
+ /*
44
+ * used to pass all arguments to dm_real_connect while inside
45
+ * rb_thread_call_without_gvl
46
+ */
47
+ struct nogvl_connect_args {
48
+ dm_client_wrapper *wrapper;
49
+ const char *server;
50
+ const char *user;
51
+ const char *passwd;
52
+ const char *schema;
53
+ };
54
+
55
+ /*
56
+ * used to pass all arguments to dm_send_query while inside
57
+ * rb_thread_call_without_gvl
58
+ */
59
+ struct nogvl_send_query_args {
60
+ dhcon *con;
61
+ VALUE sql;
62
+ const char *sql_ptr;
63
+ long sql_len;
64
+ dm_client_wrapper *wrapper;
65
+ };
66
+
67
+
68
+ /*
69
+ * non-blocking dm_*() functions that we won't be wrapping since
70
+ * they do not appear to hit the network nor issue any interruptible
71
+ * or blocking system calls.
72
+ *
73
+ * - dm_affected_rows()
74
+ * - dm_error()
75
+ * - dm_fetch_fields()
76
+ * - dm_fetch_lengths() - calls cli_fetch_lengths or emb_fetch_lengths
77
+ * - dm_field_count()
78
+ * - dm_get_client_info()
79
+ * - dm_get_client_version()
80
+ * - dm_get_server_info()
81
+ * - dm_get_server_version()
82
+ * - dm_insert_id()
83
+ * - dm_num_fields()
84
+ * - dm_num_rows()
85
+ * - dm_options()
86
+ * - dm_real_escape_string()
87
+ * - dm_ssl_set()
88
+ */
89
+
90
+ static void rb_dm_client_mark(void * wrapper)
91
+ {
92
+ dm_client_wrapper * w = wrapper;
93
+ if (w) {
94
+ rb_gc_mark_movable(w->encoding);
95
+ rb_gc_mark_movable(w->active_fiber);
96
+ }
97
+ }
98
+
99
+ /* this is called during GC */
100
+ static void rb_dm_client_free(void *ptr) {
101
+ dm_client_wrapper *wrapper = ptr;
102
+ decr_dm_client(wrapper);
103
+ }
104
+
105
+ static size_t rb_dm_client_memsize(const void * wrapper) {
106
+ const dm_client_wrapper * w = wrapper;
107
+ return sizeof(*w);
108
+ }
109
+
110
+ static void rb_dm_client_compact(void * wrapper) {
111
+ dm_client_wrapper * w = wrapper;
112
+ if (w) {
113
+ rb_dm_gc_location(w->encoding);
114
+ rb_dm_gc_location(w->active_fiber);
115
+ }
116
+ }
117
+
118
+ const rb_data_type_t rb_dm_client_type = {
119
+ "rb_dm_client",
120
+ {
121
+ rb_dm_client_mark,
122
+ rb_dm_client_free,
123
+ rb_dm_client_memsize,
124
+ #ifdef HAVE_RB_GC_MARK_MOVABLE
125
+ rb_dm_client_compact,
126
+ #endif
127
+ },
128
+ 0,
129
+ 0,
130
+ #ifdef RUBY_TYPED_FREE_IMMEDIATELY
131
+ RUBY_TYPED_FREE_IMMEDIATELY,
132
+ #endif
133
+ };
134
+
135
+ struct nogvl_errors_args {
136
+ void* hndl;
137
+ sdbyte errormsg[4096];
138
+ sdint4 errorCode;
139
+ sdint2 hndl_type;
140
+ };
141
+
142
+ static void *nogvl_get_error(void *ptr) {
143
+ struct nogvl_errors_args *args = ptr;
144
+ sdbyte error_buf[4096];
145
+ dpi_get_diag_rec(args->hndl_type, args->hndl, 1, &args->errorCode, error_buf, sizeof(error_buf), NULL);
146
+ sprintf(args->errormsg,"[CODE:%d]%s",args->errorCode, error_buf);
147
+ return (void*)Qtrue;
148
+ }
149
+
150
+
151
+ static VALUE rb_raise_dm_error(dm_client_wrapper *wrapper, void* hndl, sdint2 hndl_type) {
152
+ VALUE rb_error_msg;
153
+ VALUE e;
154
+ const struct dm_enc_name_to_rb_map *dmrb;
155
+ struct nogvl_errors_args err;
156
+
157
+ err.hndl = hndl;
158
+ err.hndl_type = hndl_type;
159
+
160
+ rb_thread_call_without_gvl(nogvl_get_error, &err, RUBY_UBF_IO, 0);
161
+ rb_error_msg = rb_str_new2(err.errormsg);
162
+
163
+ dmrb = dm_enc_name_to_rb(wrapper->encode_code);
164
+ rb_enc_associate(rb_error_msg, rb_enc_find(dmrb->rb_name));
165
+ e = rb_funcall(cdmError, intern_new_with_args, 2,
166
+ rb_error_msg,
167
+ UINT2NUM(err.errorCode));
168
+ rb_exc_raise(e);
169
+ }
170
+
171
+ static void *nogvl_connect(void *ptr) {
172
+ struct nogvl_connect_args *args = ptr;
173
+ DPIRETURN rt;
174
+ rt = dpi_alloc_env(&args->wrapper->env);
175
+ if(!(DSQL_SUCCEEDED(rt)))
176
+ return (void*)Qfalse;
177
+ rt = dpi_alloc_con(args->wrapper->env,&args->wrapper->client);
178
+ if(!(DSQL_SUCCEEDED(rt)))
179
+ return (void*)Qfalse;
180
+ if(args->schema != NULL)
181
+ {
182
+ rt = dpi_set_con_attr(args->wrapper->client,DSQL_ATTR_CURRENT_SCHEMA,(dpointer)args->schema,(sdint4)strlen(args->schema));
183
+ if(!(DSQL_SUCCEEDED(rt)))
184
+ return (void*)Qfalse;
185
+ }
186
+ rt = dpi_set_con_attr(args->wrapper->client,DSQL_ATTR_LOCAL_CODE,(dpointer)args->wrapper->encode_code,0);
187
+ if(!(DSQL_SUCCEEDED(rt)))
188
+ return (void*)Qfalse;
189
+ rt = dpi_login(args->wrapper->client,args->server,args->user,args->passwd);
190
+ if(!(DSQL_SUCCEEDED(rt)))
191
+ return (void*)Qfalse;
192
+ rt = dpi_set_con_attr(args->wrapper->client,DSQL_ATTR_AUTOCOMMIT,(dpointer)1,0);
193
+ if(!(DSQL_SUCCEEDED(rt)))
194
+ return (void*)Qfalse;
195
+
196
+ return(void*)Qtrue;
197
+ }
198
+
199
+ static void *nogvl_close(void *ptr) {
200
+ dm_client_wrapper *wrapper = ptr;
201
+
202
+ if (wrapper->initialized && !wrapper->closed) {
203
+ dpi_logout(wrapper->client);
204
+ dpi_free_con(wrapper->client);
205
+ dpi_free_env(wrapper->env);
206
+ wrapper->closed = 1;
207
+ wrapper->active = 0;
208
+ wrapper->reconnect_enabled = 0;
209
+ wrapper->active_fiber = Qnil;
210
+ }
211
+
212
+ return NULL;
213
+ }
214
+
215
+ void decr_dm_client(dm_client_wrapper *wrapper)
216
+ {
217
+ wrapper->refcount--;
218
+
219
+ if (wrapper->refcount == 0) {
220
+ xfree(wrapper);
221
+ }
222
+ }
223
+
224
+ static VALUE allocate(VALUE klass) {
225
+ VALUE obj;
226
+ dm_client_wrapper * wrapper;
227
+ #ifdef NEW_TYPEDDATA_WRAPPER
228
+ obj = TypedData_Make_Struct(klass, dm_client_wrapper, &rb_dm_client_type, wrapper);
229
+ #else
230
+ obj = Data_Make_Struct(klass, dm_client_wrapper, rb_dm_client_mark, rb_dm_client_free, wrapper);
231
+ #endif
232
+ wrapper->encoding = Qnil;
233
+ wrapper->active_fiber = Qnil;
234
+ wrapper->server_version = 0;
235
+ wrapper->reconnect_enabled = 0;
236
+ wrapper->connect_timeout = 0;
237
+ wrapper->initialized = 0; /* will be set true after calling dm_init */
238
+ wrapper->closed = 1; /* will be set false after calling dm_real_connect */
239
+ wrapper->refcount = 1;
240
+ wrapper->active = 0;
241
+
242
+ return obj;
243
+ }
244
+
245
+ static VALUE rb_dm_connect(VALUE self, VALUE user, VALUE pass, VALUE servers, VALUE schema) {
246
+ struct nogvl_connect_args args;
247
+ time_t start_time, end_time, elapsed_time, connect_timeout;
248
+ VALUE rv;
249
+ GET_CLIENT(self);
250
+
251
+ args.server = NIL_P(servers) ? NULL : StringValueCStr(servers);
252
+ args.user = NIL_P(user) ? NULL : StringValueCStr(user);
253
+ args.passwd = NIL_P(pass) ? NULL : StringValueCStr(pass);
254
+ args.schema = NIL_P(schema) ? NULL : StringValueCStr(schema);
255
+ args.wrapper = wrapper;
256
+
257
+ rv = (VALUE) rb_thread_call_without_gvl(nogvl_connect, &args, RUBY_UBF_IO, 0);
258
+
259
+ if (rv == Qfalse)
260
+ rb_raise_dm_error(wrapper, wrapper->client, DSQL_HANDLE_DBC);
261
+ wrapper->closed = 0;
262
+ wrapper->server_version = 8;
263
+ wrapper->active = 1;
264
+ wrapper->initialized = 1;
265
+ return self;
266
+ }
267
+
268
+ /* call-seq:
269
+ * client.closed
270
+ *
271
+ * @return [Boolean]
272
+ */
273
+ static VALUE rb_dm_client_close(VALUE self) {
274
+ GET_CLIENT(self);
275
+
276
+ if (wrapper->client) {
277
+ rb_thread_call_without_gvl(nogvl_close, wrapper, RUBY_UBF_IO, 0);
278
+ }
279
+
280
+ return Qnil;
281
+ }
282
+
283
+ /* call-seq:
284
+ * client.closed?
285
+ *
286
+ * @return [Boolean]
287
+ */
288
+ static VALUE rb_dm_client_closed(VALUE self) {
289
+ GET_CLIENT(self);
290
+ return CONNECTED(wrapper) ? Qfalse : Qtrue;
291
+ }
292
+
293
+
294
+ static void *nogvl_send_query(void *ptr) {
295
+ struct nogvl_send_query_args *args = ptr;
296
+ DPIRETURN rt;
297
+ dhstmt hstmt;
298
+ udint2 col_num;
299
+ sdint4 nStmtType;
300
+ sdbyte lastrowid[12];
301
+ udint4 len;
302
+
303
+ args->wrapper->stmt = NULL;
304
+ rt = dpi_alloc_stmt(args->con,&hstmt);
305
+ if(!DSQL_SUCCEEDED(rt))
306
+ {
307
+ return (void*)Qfalse;
308
+ }
309
+ args->wrapper->stmt = hstmt;
310
+
311
+ rt = dpi_set_stmt_attr(hstmt,DSQL_ATTR_CURSOR_TYPE,(dpointer)DSQL_CURSOR_DYNAMIC,0);
312
+ if(!DSQL_SUCCEEDED(rt))
313
+ {
314
+ return (void*)Qfalse;
315
+ }
316
+ rt = dpi_exec_direct(hstmt,args->sql_ptr);
317
+ if(!DSQL_SUCCEEDED(rt))
318
+ {
319
+ return (void*)Qfalse;
320
+ }
321
+ rt = dpi_get_diag_field(DSQL_HANDLE_STMT, hstmt, 0,
322
+ DSQL_DIAG_DYNAMIC_FUNCTION_CODE,
323
+ (dpointer)&nStmtType, 0, NULL);
324
+ if(!DSQL_SUCCEEDED(rt))
325
+ {
326
+ return (void*)Qfalse;
327
+ }
328
+ //lastrowid
329
+ if(nStmtType == DSQL_DIAG_FUNC_CODE_INSERT ||
330
+ nStmtType == DSQL_DIAG_FUNC_CODE_UPDATE ||
331
+ nStmtType == DSQL_DIAG_FUNC_CODE_DELETE)
332
+ {
333
+ rt = dpi_get_diag_field(DSQL_HANDLE_STMT, hstmt, 0, DSQL_DIAG_ROWID, &lastrowid, sizeof(lastrowid), NULL);
334
+ rt = dpi_rowid_to_char(args->con,lastrowid,sizeof(lastrowid),args->wrapper->lastrowid,sizeof(args->wrapper->lastrowid),&len);
335
+ }
336
+ else
337
+ {
338
+ strncpy(args->wrapper->lastrowid, "", 20);
339
+ }
340
+
341
+ // affected_rows
342
+ if(nStmtType == DSQL_DIAG_FUNC_CODE_INSERT ||
343
+ nStmtType == DSQL_DIAG_FUNC_CODE_UPDATE ||
344
+ nStmtType == DSQL_DIAG_FUNC_CODE_DELETE ||
345
+ nStmtType == DSQL_DIAG_FUNC_CODE_CALL)
346
+ {
347
+ rt = dpi_row_count(hstmt,&args->wrapper->affected_rows);
348
+ }
349
+ else{
350
+ args->wrapper->affected_rows = 0;
351
+ }
352
+ //if have a result
353
+ if(nStmtType == DSQL_DIAG_FUNC_CODE_SELECT ||
354
+ nStmtType == DSQL_DIAG_FUNC_CODE_CALL)
355
+ {
356
+ args->wrapper->is_select = 1;
357
+ }
358
+
359
+ return (void*)Qtrue;
360
+ }
361
+
362
+ static VALUE do_send_query(VALUE args) {
363
+ struct nogvl_send_query_args *query_args = (void *)args;
364
+ dm_client_wrapper *wrapper = query_args->wrapper;
365
+ wrapper->is_select = 0;
366
+ if ((VALUE)rb_thread_call_without_gvl(nogvl_send_query, query_args, RUBY_UBF_IO, 0) == Qfalse) {
367
+ if(wrapper->stmt == NULL)
368
+ rb_raise(cdmError, "failed to alloc statement");
369
+ else
370
+ rb_raise_dm_error(wrapper, wrapper->stmt, DSQL_HANDLE_STMT);
371
+ }
372
+ return Qnil;
373
+ }
374
+
375
+ /* call-seq:
376
+ * client.query(sql)
377
+ *
378
+ * Query the database with +sql+, with optional +options+. For the possible
379
+ * options, see default_query_options on the dm::Client class.
380
+ */
381
+ static VALUE rb_dm_query(VALUE self, VALUE sql, VALUE options) {
382
+ struct nogvl_send_query_args args;
383
+ VALUE resultobj;
384
+ GET_CLIENT(self);
385
+
386
+ REQUIRE_CONNECTED(wrapper);
387
+ args.con = wrapper->client;
388
+
389
+ Check_Type(sql, T_STRING);
390
+ /* ensure the string is in the encoding the connection is expecting */
391
+ args.sql = rb_str_export_to_enc(sql, rb_to_encoding(wrapper->encoding));
392
+ args.sql_ptr = RSTRING_PTR(args.sql);
393
+ args.sql_len = RSTRING_LEN(args.sql);
394
+ args.wrapper = wrapper;
395
+
396
+ do_send_query((VALUE)&args);
397
+ (void)RB_GC_GUARD(sql);
398
+
399
+ if(wrapper->is_select == 1)
400
+ {
401
+ resultobj = rb_dm_result_to_obj(self, wrapper->encoding, wrapper->stmt, 0 , options);
402
+ wrapper->stmt = NULL;
403
+ return resultobj;
404
+ }
405
+ else
406
+ {
407
+ dpi_free_stmt(wrapper->stmt);
408
+ wrapper->stmt = NULL;
409
+ }
410
+
411
+ /* this will just block until the result is ready */
412
+ return Qnil;
413
+ }
414
+
415
+
416
+ /* call-seq:
417
+ * client.last_id
418
+ *
419
+ * Returns the value generated for an AUTO_INCREMENT column by the previous INSERT or UPDATE
420
+ * statement.
421
+ */
422
+ static VALUE rb_dm_client_last_id(VALUE self) {
423
+ GET_CLIENT(self);
424
+ REQUIRE_CONNECTED(wrapper);
425
+ return rb_str_new2(wrapper->lastrowid);
426
+ }
427
+
428
+
429
+ /* call-seq:
430
+ * client.affected_rows
431
+ *
432
+ * returns the number of rows changed, deleted, or inserted by the last statement
433
+ * if it was an UPDATE, DELETE, or INSERT.
434
+ */
435
+ static VALUE rb_dm_client_affected_rows(VALUE self) {
436
+ GET_CLIENT(self);
437
+ REQUIRE_CONNECTED(wrapper);
438
+ return ULL2NUM(wrapper->affected_rows);
439
+ }
440
+
441
+
442
+ /* call-seq:
443
+ * client.encoding
444
+ *
445
+ * Returns the encoding set on the client.
446
+ */
447
+ static VALUE rb_dm_client_encoding(VALUE self) {
448
+ GET_CLIENT(self);
449
+ return wrapper->encoding;
450
+ }
451
+
452
+ static void *nogvl_ping(void *ptr) {
453
+ dhcon client = ptr;
454
+ sdint4 is_dead = 1;
455
+
456
+ dpi_get_con_attr(client, DSQL_ATTR_CONNECTION_DEAD, &is_dead, sizeof(sdint4), NULL);
457
+
458
+ return (void *)( is_dead == 0 ? Qtrue : Qfalse);
459
+ }
460
+
461
+ static VALUE rb_dm_client_ping(VALUE self) {
462
+ GET_CLIENT(self);
463
+
464
+ if (!CONNECTED(wrapper)) {
465
+ return Qfalse;
466
+ } else {
467
+ return (VALUE)rb_thread_call_without_gvl(nogvl_ping, wrapper->client, RUBY_UBF_IO, 0);
468
+ }
469
+ }
470
+
471
+ static VALUE set_charset_name(VALUE self, VALUE value) {
472
+ int code;
473
+ DPIRETURN rt;
474
+ const struct dm_enc_name_to_rb_map *dmrb;
475
+ rb_encoding *enc;
476
+ VALUE rb_enc;
477
+ GET_CLIENT(self);
478
+
479
+ code = NUM2INT(value);
480
+ if(code<1 || code>11)
481
+ code = 1;
482
+ dmrb = dm_enc_name_to_rb(code);
483
+ if (dmrb == NULL || dmrb->rb_name == NULL) {
484
+ rb_raise(cdmError, "Unsupported charset");
485
+ } else {
486
+ enc = rb_enc_find(dmrb->rb_name);
487
+ rb_enc = rb_enc_from_encoding(enc);
488
+ wrapper->encoding = rb_enc;
489
+ }
490
+
491
+ wrapper->encode_code = code;
492
+ return value;
493
+ }
494
+
495
+ /* call-seq: client.prepare # => dm::Statement
496
+ *
497
+ * Create a new prepared statement.
498
+ */
499
+ static VALUE rb_dm_client_prepare_statement(VALUE self, VALUE sql) {
500
+ GET_CLIENT(self);
501
+ REQUIRE_CONNECTED(wrapper);
502
+ VALUE obj = rb_dm_stmt_new(self,sql);
503
+ return obj;
504
+ }
505
+
506
+ void init_dm_client() {
507
+ #if 0
508
+ mdm = rb_define_module("dm"); Teach RDoc about dm constant.
509
+ #endif
510
+ cdmClient = rb_define_class_under(mdm, "Client", rb_cObject);
511
+ rb_global_variable(&cdmClient);
512
+ rb_define_alloc_func(cdmClient, allocate);
513
+
514
+ rb_define_method(cdmClient, "close", rb_dm_client_close, 0);
515
+ rb_define_method(cdmClient, "closed?", rb_dm_client_closed, 0);
516
+ rb_define_method(cdmClient, "last_id", rb_dm_client_last_id, 0);
517
+ rb_define_method(cdmClient, "affected_rows", rb_dm_client_affected_rows, 0);
518
+ rb_define_method(cdmClient, "prepare", rb_dm_client_prepare_statement, 1);
519
+ rb_define_method(cdmClient, "encoding", rb_dm_client_encoding, 0);
520
+ rb_define_method(cdmClient, "ping", rb_dm_client_ping, 0);
521
+
522
+ rb_define_private_method(cdmClient, "charset_name=", set_charset_name, 1);
523
+ rb_define_private_method(cdmClient, "connect", rb_dm_connect, 4);
524
+ rb_define_private_method(cdmClient, "_query", rb_dm_query, 2);
525
+
526
+ sym_id = ID2SYM(rb_intern("id"));
527
+ sym_version = ID2SYM(rb_intern("version"));
528
+ sym_header_version = ID2SYM(rb_intern("header_version"));
529
+ sym_async = ID2SYM(rb_intern("async"));
530
+ sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
531
+ sym_as = ID2SYM(rb_intern("as"));
532
+ sym_array = ID2SYM(rb_intern("array"));
533
+ sym_stream = ID2SYM(rb_intern("stream"));
534
+
535
+ sym_no_good_index_used = ID2SYM(rb_intern("no_good_index_used"));
536
+ sym_no_index_used = ID2SYM(rb_intern("no_index_used"));
537
+ sym_query_was_slow = ID2SYM(rb_intern("query_was_slow"));
538
+
539
+ intern_brackets = rb_intern("[]");
540
+ intern_merge = rb_intern("merge");
541
+ intern_merge_bang = rb_intern("merge!");
542
+ intern_new_with_args = rb_intern("new_with_args");
543
+ intern_current_query_options = rb_intern("@current_query_options");
544
+ intern_read_timeout = rb_intern("@read_timeout");
545
+
546
+ rb_const_set(cdmClient, rb_intern("SECURE_CONNECTION"), LONG2NUM(0));
547
+
548
+ }