tiny_tds 2.1.6-x64-mingw-ucrt
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.circleci/config.yml +407 -0
- data/.codeclimate.yml +20 -0
- data/.gitattributes +1 -0
- data/.gitignore +22 -0
- data/.rubocop.yml +31 -0
- data/CHANGELOG.md +280 -0
- data/CODE_OF_CONDUCT.md +31 -0
- data/Gemfile +2 -0
- data/ISSUE_TEMPLATE.md +38 -0
- data/MIT-LICENSE +23 -0
- data/README.md +504 -0
- data/Rakefile +62 -0
- data/VERSION +1 -0
- data/bin/defncopy-ttds +3 -0
- data/bin/tsql-ttds +3 -0
- data/docker-compose.yml +34 -0
- data/exe/.keep +0 -0
- data/ext/tiny_tds/client.c +499 -0
- data/ext/tiny_tds/client.h +53 -0
- data/ext/tiny_tds/extconf.rb +92 -0
- data/ext/tiny_tds/extconsts.rb +15 -0
- data/ext/tiny_tds/result.c +634 -0
- data/ext/tiny_tds/result.h +32 -0
- data/ext/tiny_tds/tiny_tds_ext.c +12 -0
- data/ext/tiny_tds/tiny_tds_ext.h +17 -0
- data/lib/tiny_tds/3.1/tiny_tds.so +0 -0
- data/lib/tiny_tds/3.2/tiny_tds.so +0 -0
- data/lib/tiny_tds/bin.rb +104 -0
- data/lib/tiny_tds/client.rb +136 -0
- data/lib/tiny_tds/error.rb +14 -0
- data/lib/tiny_tds/gem.rb +27 -0
- data/lib/tiny_tds/result.rb +7 -0
- data/lib/tiny_tds/version.rb +3 -0
- data/lib/tiny_tds.rb +61 -0
- data/patches/freetds/1.00.27/0001-mingw_missing_inet_pton.diff +34 -0
- data/patches/freetds/1.00.27/0002-Don-t-use-MSYS2-file-libws2_32.diff +28 -0
- data/patches/libiconv/1.14/1-avoid-gets-error.patch +17 -0
- data/ports/x64-mingw-ucrt/freetds/1.1.24/bin/bsqldb.exe +0 -0
- data/ports/x64-mingw-ucrt/freetds/1.1.24/bin/datacopy.exe +0 -0
- data/ports/x64-mingw-ucrt/freetds/1.1.24/bin/defncopy.exe +0 -0
- data/ports/x64-mingw-ucrt/freetds/1.1.24/bin/freebcp.exe +0 -0
- data/ports/x64-mingw-ucrt/freetds/1.1.24/bin/libct-4.dll +0 -0
- data/ports/x64-mingw-ucrt/freetds/1.1.24/bin/libsybdb-5.dll +0 -0
- data/ports/x64-mingw-ucrt/freetds/1.1.24/bin/osql +388 -0
- data/ports/x64-mingw-ucrt/freetds/1.1.24/bin/tdspool.exe +0 -0
- data/ports/x64-mingw-ucrt/freetds/1.1.24/bin/tsql.exe +0 -0
- data/ports/x64-mingw-ucrt/freetds/1.1.24/lib/libct.dll.a +0 -0
- data/ports/x64-mingw-ucrt/freetds/1.1.24/lib/libct.la +41 -0
- data/ports/x64-mingw-ucrt/freetds/1.1.24/lib/libsybdb.dll.a +0 -0
- data/ports/x64-mingw-ucrt/freetds/1.1.24/lib/libsybdb.la +41 -0
- data/ports/x64-mingw-ucrt/libiconv/1.15/bin/iconv.exe +0 -0
- data/ports/x64-mingw-ucrt/libiconv/1.15/bin/libcharset-1.dll +0 -0
- data/ports/x64-mingw-ucrt/libiconv/1.15/bin/libiconv-2.dll +0 -0
- data/ports/x64-mingw-ucrt/libiconv/1.15/lib/charset.alias +4 -0
- data/ports/x64-mingw-ucrt/libiconv/1.15/lib/libcharset.dll.a +0 -0
- data/ports/x64-mingw-ucrt/libiconv/1.15/lib/libcharset.la +41 -0
- data/ports/x64-mingw-ucrt/libiconv/1.15/lib/libiconv.dll.a +0 -0
- data/ports/x64-mingw-ucrt/libiconv/1.15/lib/libiconv.la +41 -0
- data/ports/x64-mingw-ucrt/openssl/1.1.1s/bin/c_rehash +251 -0
- data/ports/x64-mingw-ucrt/openssl/1.1.1s/bin/libcrypto-1_1-x64.dll +0 -0
- data/ports/x64-mingw-ucrt/openssl/1.1.1s/bin/libssl-1_1-x64.dll +0 -0
- data/ports/x64-mingw-ucrt/openssl/1.1.1s/bin/openssl.exe +0 -0
- data/ports/x64-mingw-ucrt/openssl/1.1.1s/lib/libcrypto.a +0 -0
- data/ports/x64-mingw-ucrt/openssl/1.1.1s/lib/libcrypto.dll.a +0 -0
- data/ports/x64-mingw-ucrt/openssl/1.1.1s/lib/libssl.a +0 -0
- data/ports/x64-mingw-ucrt/openssl/1.1.1s/lib/libssl.dll.a +0 -0
- data/setup_cimgruby_dev.sh +25 -0
- data/start_dev.sh +21 -0
- data/tasks/native_gem.rake +23 -0
- data/tasks/package.rake +8 -0
- data/tasks/ports/freetds.rb +37 -0
- data/tasks/ports/libiconv.rb +26 -0
- data/tasks/ports/openssl.rb +62 -0
- data/tasks/ports/recipe.rb +64 -0
- data/tasks/ports.rake +108 -0
- data/tasks/test.rake +9 -0
- data/test/benchmark/query.rb +77 -0
- data/test/benchmark/query_odbc.rb +106 -0
- data/test/benchmark/query_tinytds.rb +126 -0
- data/test/bin/install-freetds.sh +20 -0
- data/test/bin/install-mssql.ps1 +31 -0
- data/test/bin/install-mssqltools.sh +9 -0
- data/test/bin/install-openssl.sh +18 -0
- data/test/bin/setup_tinytds_db.sh +7 -0
- data/test/bin/setup_volume_permissions.sh +10 -0
- data/test/client_test.rb +275 -0
- data/test/gem_test.rb +177 -0
- data/test/result_test.rb +814 -0
- data/test/schema/1px.gif +0 -0
- data/test/schema/sqlserver_2000.sql +140 -0
- data/test/schema/sqlserver_2005.sql +140 -0
- data/test/schema/sqlserver_2008.sql +140 -0
- data/test/schema/sqlserver_2014.sql +140 -0
- data/test/schema/sqlserver_2016.sql +140 -0
- data/test/schema/sqlserver_azure.sql +140 -0
- data/test/schema/sybase_ase.sql +138 -0
- data/test/schema_test.rb +443 -0
- data/test/sql/db-create.sql +18 -0
- data/test/sql/db-login.sql +38 -0
- data/test/test_helper.rb +280 -0
- data/test/thread_test.rb +98 -0
- data/tiny_tds.gemspec +31 -0
- metadata +267 -0
@@ -0,0 +1,634 @@
|
|
1
|
+
|
2
|
+
#include <tiny_tds_ext.h>
|
3
|
+
#include <stdint.h>
|
4
|
+
|
5
|
+
// File Types/Vars
|
6
|
+
|
7
|
+
VALUE cTinyTdsResult;
|
8
|
+
extern VALUE mTinyTds, cTinyTdsClient, cTinyTdsError;
|
9
|
+
VALUE cKernel, cDate;
|
10
|
+
VALUE opt_decimal_zero, opt_float_zero, opt_one, opt_zero, opt_four, opt_19hdr, opt_onek, opt_tenk, opt_onemil, opt_onebil;
|
11
|
+
static ID intern_new, intern_utc, intern_local, intern_localtime, intern_merge,
|
12
|
+
intern_civil, intern_new_offset, intern_plus, intern_divide, intern_bigd;
|
13
|
+
static ID sym_symbolize_keys, sym_as, sym_array, sym_cache_rows, sym_first, sym_timezone, sym_local, sym_utc, sym_empty_sets;
|
14
|
+
|
15
|
+
|
16
|
+
// Lib Macros
|
17
|
+
|
18
|
+
rb_encoding *binaryEncoding;
|
19
|
+
#define ENCODED_STR_NEW(_data, _len) ({ \
|
20
|
+
VALUE _val = rb_str_new((char *)_data, (long)_len); \
|
21
|
+
rb_enc_associate(_val, rwrap->encoding); \
|
22
|
+
_val; \
|
23
|
+
})
|
24
|
+
#define ENCODED_STR_NEW2(_data2) ({ \
|
25
|
+
VALUE _val = rb_str_new2((char *)_data2); \
|
26
|
+
rb_enc_associate(_val, rwrap->encoding); \
|
27
|
+
_val; \
|
28
|
+
})
|
29
|
+
|
30
|
+
#ifdef _WIN32
|
31
|
+
#define LONG_LONG_FORMAT "I64d"
|
32
|
+
#else
|
33
|
+
#define LONG_LONG_FORMAT "lld"
|
34
|
+
#endif
|
35
|
+
|
36
|
+
|
37
|
+
// Lib Backend (Memory Management)
|
38
|
+
|
39
|
+
static void rb_tinytds_result_mark(void *ptr) {
|
40
|
+
tinytds_result_wrapper *rwrap = (tinytds_result_wrapper *)ptr;
|
41
|
+
if (rwrap) {
|
42
|
+
rb_gc_mark(rwrap->local_offset);
|
43
|
+
rb_gc_mark(rwrap->fields);
|
44
|
+
rb_gc_mark(rwrap->fields_processed);
|
45
|
+
rb_gc_mark(rwrap->results);
|
46
|
+
rb_gc_mark(rwrap->dbresults_retcodes);
|
47
|
+
}
|
48
|
+
}
|
49
|
+
|
50
|
+
static void rb_tinytds_result_free(void *ptr) {
|
51
|
+
xfree(ptr);
|
52
|
+
}
|
53
|
+
|
54
|
+
VALUE rb_tinytds_new_result_obj(tinytds_client_wrapper *cwrap) {
|
55
|
+
VALUE obj;
|
56
|
+
tinytds_result_wrapper *rwrap;
|
57
|
+
obj = Data_Make_Struct(cTinyTdsResult, tinytds_result_wrapper, rb_tinytds_result_mark, rb_tinytds_result_free, rwrap);
|
58
|
+
rwrap->cwrap = cwrap;
|
59
|
+
rwrap->client = cwrap->client;
|
60
|
+
rwrap->local_offset = Qnil;
|
61
|
+
rwrap->fields = rb_ary_new();
|
62
|
+
rwrap->fields_processed = rb_ary_new();
|
63
|
+
rwrap->results = Qnil;
|
64
|
+
rwrap->dbresults_retcodes = rb_ary_new();
|
65
|
+
rwrap->number_of_results = 0;
|
66
|
+
rwrap->number_of_fields = 0;
|
67
|
+
rwrap->number_of_rows = 0;
|
68
|
+
rb_obj_call_init(obj, 0, NULL);
|
69
|
+
return obj;
|
70
|
+
}
|
71
|
+
|
72
|
+
// No GVL Helpers
|
73
|
+
|
74
|
+
#define NOGVL_DBCALL(_dbfunction, _client) ( \
|
75
|
+
(RETCODE)(intptr_t)rb_thread_call_without_gvl( \
|
76
|
+
(void *(*)(void *))_dbfunction, _client, \
|
77
|
+
(rb_unblock_function_t*)dbcancel_ubf, _client ) \
|
78
|
+
)
|
79
|
+
|
80
|
+
static void dbcancel_ubf(DBPROCESS *client) {
|
81
|
+
GET_CLIENT_USERDATA(client);
|
82
|
+
dbcancel(client);
|
83
|
+
userdata->dbcancel_sent = 1;
|
84
|
+
}
|
85
|
+
|
86
|
+
static void nogvl_setup(DBPROCESS *client) {
|
87
|
+
GET_CLIENT_USERDATA(client);
|
88
|
+
userdata->nonblocking = 1;
|
89
|
+
userdata->nonblocking_errors_length = 0;
|
90
|
+
userdata->nonblocking_errors = malloc(ERRORS_STACK_INIT_SIZE * sizeof(tinytds_errordata));
|
91
|
+
userdata->nonblocking_errors_size = ERRORS_STACK_INIT_SIZE;
|
92
|
+
}
|
93
|
+
|
94
|
+
static void nogvl_cleanup(DBPROCESS *client) {
|
95
|
+
GET_CLIENT_USERDATA(client);
|
96
|
+
userdata->nonblocking = 0;
|
97
|
+
userdata->timing_out = 0;
|
98
|
+
/*
|
99
|
+
Now that the blocking operation is done, we can finally throw any
|
100
|
+
exceptions based on errors from SQL Server.
|
101
|
+
*/
|
102
|
+
short int i;
|
103
|
+
for (i = 0; i < userdata->nonblocking_errors_length; i++) {
|
104
|
+
tinytds_errordata error = userdata->nonblocking_errors[i];
|
105
|
+
|
106
|
+
// lookahead to drain any info messages ahead of raising error
|
107
|
+
if (!error.is_message) {
|
108
|
+
short int j;
|
109
|
+
for (j = i; j < userdata->nonblocking_errors_length; j++) {
|
110
|
+
tinytds_errordata msg_error = userdata->nonblocking_errors[j];
|
111
|
+
if (msg_error.is_message) {
|
112
|
+
rb_tinytds_raise_error(client, msg_error);
|
113
|
+
}
|
114
|
+
}
|
115
|
+
}
|
116
|
+
|
117
|
+
rb_tinytds_raise_error(client, error);
|
118
|
+
}
|
119
|
+
|
120
|
+
free(userdata->nonblocking_errors);
|
121
|
+
userdata->nonblocking_errors_length = 0;
|
122
|
+
userdata->nonblocking_errors_size = 0;
|
123
|
+
}
|
124
|
+
|
125
|
+
static RETCODE nogvl_dbsqlok(DBPROCESS *client) {
|
126
|
+
int retcode = FAIL;
|
127
|
+
GET_CLIENT_USERDATA(client);
|
128
|
+
nogvl_setup(client);
|
129
|
+
retcode = NOGVL_DBCALL(dbsqlok, client);
|
130
|
+
nogvl_cleanup(client);
|
131
|
+
userdata->dbsqlok_sent = 1;
|
132
|
+
return retcode;
|
133
|
+
}
|
134
|
+
|
135
|
+
static RETCODE nogvl_dbsqlexec(DBPROCESS *client) {
|
136
|
+
int retcode = FAIL;
|
137
|
+
nogvl_setup(client);
|
138
|
+
retcode = NOGVL_DBCALL(dbsqlexec, client);
|
139
|
+
nogvl_cleanup(client);
|
140
|
+
return retcode;
|
141
|
+
}
|
142
|
+
|
143
|
+
static RETCODE nogvl_dbresults(DBPROCESS *client) {
|
144
|
+
int retcode = FAIL;
|
145
|
+
nogvl_setup(client);
|
146
|
+
retcode = NOGVL_DBCALL(dbresults, client);
|
147
|
+
nogvl_cleanup(client);
|
148
|
+
return retcode;
|
149
|
+
}
|
150
|
+
|
151
|
+
static RETCODE nogvl_dbnextrow(DBPROCESS * client) {
|
152
|
+
int retcode = FAIL;
|
153
|
+
nogvl_setup(client);
|
154
|
+
retcode = NOGVL_DBCALL(dbnextrow, client);
|
155
|
+
nogvl_cleanup(client);
|
156
|
+
return retcode;
|
157
|
+
}
|
158
|
+
|
159
|
+
// Lib Backend (Helpers)
|
160
|
+
|
161
|
+
static RETCODE rb_tinytds_result_dbresults_retcode(VALUE self) {
|
162
|
+
VALUE ruby_rc;
|
163
|
+
RETCODE db_rc;
|
164
|
+
GET_RESULT_WRAPPER(self);
|
165
|
+
ruby_rc = rb_ary_entry(rwrap->dbresults_retcodes, rwrap->number_of_results);
|
166
|
+
if (NIL_P(ruby_rc)) {
|
167
|
+
db_rc = nogvl_dbresults(rwrap->client);
|
168
|
+
ruby_rc = INT2FIX(db_rc);
|
169
|
+
rb_ary_store(rwrap->dbresults_retcodes, rwrap->number_of_results, ruby_rc);
|
170
|
+
} else {
|
171
|
+
db_rc = FIX2INT(ruby_rc);
|
172
|
+
}
|
173
|
+
return db_rc;
|
174
|
+
}
|
175
|
+
|
176
|
+
static RETCODE rb_tinytds_result_ok_helper(DBPROCESS *client) {
|
177
|
+
GET_CLIENT_USERDATA(client);
|
178
|
+
if (userdata->dbsqlok_sent == 0) {
|
179
|
+
userdata->dbsqlok_retcode = nogvl_dbsqlok(client);
|
180
|
+
}
|
181
|
+
return userdata->dbsqlok_retcode;
|
182
|
+
}
|
183
|
+
|
184
|
+
static void rb_tinytds_result_exec_helper(DBPROCESS *client) {
|
185
|
+
RETCODE dbsqlok_rc = rb_tinytds_result_ok_helper(client);
|
186
|
+
GET_CLIENT_USERDATA(client);
|
187
|
+
if (dbsqlok_rc == SUCCEED) {
|
188
|
+
/*
|
189
|
+
This is to just process each result set. Commands such as backup and
|
190
|
+
restore are not done when the first result set is returned, so we need to
|
191
|
+
exhaust the result sets before it is complete.
|
192
|
+
*/
|
193
|
+
while (nogvl_dbresults(client) == SUCCEED) {
|
194
|
+
/*
|
195
|
+
If we don't loop through each row for calls to TinyTds::Result.do that
|
196
|
+
actually do return result sets, we will trigger error 20019 about trying
|
197
|
+
to execute a new command with pending results. Oh well.
|
198
|
+
*/
|
199
|
+
while (dbnextrow(client) != NO_MORE_ROWS);
|
200
|
+
}
|
201
|
+
}
|
202
|
+
dbcancel(client);
|
203
|
+
userdata->dbcancel_sent = 1;
|
204
|
+
userdata->dbsql_sent = 0;
|
205
|
+
}
|
206
|
+
|
207
|
+
static VALUE rb_tinytds_result_fetch_row(VALUE self, ID timezone, int symbolize_keys, int as_array) {
|
208
|
+
VALUE row;
|
209
|
+
/* Storing Values */
|
210
|
+
unsigned int i;
|
211
|
+
/* Wrapper And Local Vars */
|
212
|
+
GET_RESULT_WRAPPER(self);
|
213
|
+
/* Create Empty Row */
|
214
|
+
row = as_array ? rb_ary_new2(rwrap->number_of_fields) : rb_hash_new();
|
215
|
+
for (i = 0; i < rwrap->number_of_fields; i++) {
|
216
|
+
VALUE val = Qnil;
|
217
|
+
int col = i+1;
|
218
|
+
int coltype = dbcoltype(rwrap->client, col);
|
219
|
+
BYTE *data = dbdata(rwrap->client, col);
|
220
|
+
DBINT data_len = dbdatlen(rwrap->client, col);
|
221
|
+
int null_val = ((data == NULL) && (data_len == 0));
|
222
|
+
if (!null_val) {
|
223
|
+
switch(coltype) {
|
224
|
+
case SYBINT1:
|
225
|
+
val = INT2FIX(*(DBTINYINT *)data);
|
226
|
+
break;
|
227
|
+
case SYBINT2:
|
228
|
+
val = INT2FIX(*(DBSMALLINT *)data);
|
229
|
+
break;
|
230
|
+
case SYBINT4:
|
231
|
+
val = INT2NUM(*(DBINT *)data);
|
232
|
+
break;
|
233
|
+
case SYBINT8:
|
234
|
+
val = LL2NUM(*(DBBIGINT *)data);
|
235
|
+
break;
|
236
|
+
case SYBBIT:
|
237
|
+
val = *(int *)data ? Qtrue : Qfalse;
|
238
|
+
break;
|
239
|
+
case SYBNUMERIC:
|
240
|
+
case SYBDECIMAL: {
|
241
|
+
DBTYPEINFO *data_info = dbcoltypeinfo(rwrap->client, col);
|
242
|
+
int data_slength = (int)data_info->precision + (int)data_info->scale + 1;
|
243
|
+
char converted_decimal[data_slength];
|
244
|
+
dbconvert(rwrap->client, coltype, data, data_len, SYBVARCHAR, (BYTE *)converted_decimal, -1);
|
245
|
+
val = rb_funcall(cKernel, intern_bigd, 1, rb_str_new2((char *)converted_decimal));
|
246
|
+
break;
|
247
|
+
}
|
248
|
+
case SYBFLT8: {
|
249
|
+
double col_to_double = *(double *)data;
|
250
|
+
val = (col_to_double == 0.000000) ? opt_float_zero : rb_float_new(col_to_double);
|
251
|
+
break;
|
252
|
+
}
|
253
|
+
case SYBREAL: {
|
254
|
+
float col_to_float = *(float *)data;
|
255
|
+
val = (col_to_float == 0.0) ? opt_float_zero : rb_float_new(col_to_float);
|
256
|
+
break;
|
257
|
+
}
|
258
|
+
case SYBMONEY: {
|
259
|
+
DBMONEY *money = (DBMONEY *)data;
|
260
|
+
char converted_money[25];
|
261
|
+
long long money_value = ((long long)money->mnyhigh << 32) | money->mnylow;
|
262
|
+
sprintf(converted_money, "%" LONG_LONG_FORMAT, money_value);
|
263
|
+
val = rb_funcall(cKernel, intern_bigd, 2, rb_str_new2(converted_money), opt_four);
|
264
|
+
val = rb_funcall(val, intern_divide, 1, opt_tenk);
|
265
|
+
break;
|
266
|
+
}
|
267
|
+
case SYBMONEY4: {
|
268
|
+
DBMONEY4 *money = (DBMONEY4 *)data;
|
269
|
+
char converted_money[20];
|
270
|
+
sprintf(converted_money, "%f", money->mny4 / 10000.0);
|
271
|
+
val = rb_funcall(cKernel, intern_bigd, 1, rb_str_new2(converted_money));
|
272
|
+
break;
|
273
|
+
}
|
274
|
+
case SYBBINARY:
|
275
|
+
case SYBIMAGE:
|
276
|
+
val = rb_str_new((char *)data, (long)data_len);
|
277
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
278
|
+
rb_enc_associate(val, binaryEncoding);
|
279
|
+
#endif
|
280
|
+
break;
|
281
|
+
case 36: { // SYBUNIQUE
|
282
|
+
char converted_unique[37];
|
283
|
+
dbconvert(rwrap->client, coltype, data, 37, SYBVARCHAR, (BYTE *)converted_unique, -1);
|
284
|
+
val = ENCODED_STR_NEW2(converted_unique);
|
285
|
+
break;
|
286
|
+
}
|
287
|
+
case SYBDATETIME4: {
|
288
|
+
DBDATETIME new_data;
|
289
|
+
dbconvert(rwrap->client, coltype, data, data_len, SYBDATETIME, (BYTE *)&new_data, sizeof(new_data));
|
290
|
+
data = (BYTE *)&new_data;
|
291
|
+
data_len = sizeof(new_data);
|
292
|
+
}
|
293
|
+
case SYBDATETIME: {
|
294
|
+
DBDATEREC dr;
|
295
|
+
dbdatecrack(rwrap->client, &dr, (DBDATETIME *)data);
|
296
|
+
if (dr.year + dr.month + dr.day + dr.hour + dr.minute + dr.second + dr.millisecond != 0) {
|
297
|
+
val = rb_funcall(rb_cTime, timezone, 7, INT2NUM(dr.year), INT2NUM(dr.month), INT2NUM(dr.day), INT2NUM(dr.hour), INT2NUM(dr.minute), INT2NUM(dr.second), INT2NUM(dr.millisecond*1000));
|
298
|
+
}
|
299
|
+
break;
|
300
|
+
}
|
301
|
+
case 40: // SYBMSDATE
|
302
|
+
case 41: // SYBMSTIME
|
303
|
+
case 42: // SYBMSDATETIME2
|
304
|
+
case 43: { // SYBMSDATETIMEOFFSET
|
305
|
+
#ifdef DBVERSION_73
|
306
|
+
if (dbtds(rwrap->client) >= DBTDS_7_3) {
|
307
|
+
DBDATEREC2 dr2;
|
308
|
+
dbanydatecrack(rwrap->client, &dr2, coltype, data);
|
309
|
+
switch(coltype) {
|
310
|
+
case 40: { // SYBMSDATE
|
311
|
+
val = rb_funcall(cDate, intern_new, 3, INT2NUM(dr2.year), INT2NUM(dr2.month), INT2NUM(dr2.day));
|
312
|
+
break;
|
313
|
+
}
|
314
|
+
case 41: { // SYBMSTIME
|
315
|
+
VALUE rational_nsec = rb_Rational(INT2NUM(dr2.nanosecond), opt_onek);
|
316
|
+
val = rb_funcall(rb_cTime, timezone, 7, INT2NUM(1900), INT2NUM(1), INT2NUM(1), INT2NUM(dr2.hour), INT2NUM(dr2.minute), INT2NUM(dr2.second), rational_nsec);
|
317
|
+
break;
|
318
|
+
}
|
319
|
+
case 42: { // SYBMSDATETIME2
|
320
|
+
VALUE rational_nsec = rb_Rational(INT2NUM(dr2.nanosecond), opt_onek);
|
321
|
+
val = rb_funcall(rb_cTime, timezone, 7, INT2NUM(dr2.year), INT2NUM(dr2.month), INT2NUM(dr2.day), INT2NUM(dr2.hour), INT2NUM(dr2.minute), INT2NUM(dr2.second), rational_nsec);
|
322
|
+
break;
|
323
|
+
}
|
324
|
+
case 43: { // SYBMSDATETIMEOFFSET
|
325
|
+
long long numerator = ((long)dr2.second * (long long)1000000000) + (long long)dr2.nanosecond;
|
326
|
+
VALUE rational_sec = rb_Rational(LL2NUM(numerator), opt_onebil);
|
327
|
+
val = rb_funcall(rb_cTime, intern_new, 7, INT2NUM(dr2.year), INT2NUM(dr2.month), INT2NUM(dr2.day), INT2NUM(dr2.hour), INT2NUM(dr2.minute), rational_sec, INT2NUM(dr2.tzone*60));
|
328
|
+
break;
|
329
|
+
}
|
330
|
+
}
|
331
|
+
} else {
|
332
|
+
val = ENCODED_STR_NEW(data, data_len);
|
333
|
+
}
|
334
|
+
#else
|
335
|
+
val = ENCODED_STR_NEW(data, data_len);
|
336
|
+
#endif
|
337
|
+
break;
|
338
|
+
}
|
339
|
+
case SYBCHAR:
|
340
|
+
case SYBTEXT:
|
341
|
+
val = ENCODED_STR_NEW(data, data_len);
|
342
|
+
break;
|
343
|
+
case 98: { // SYBVARIANT
|
344
|
+
if (data_len == 4) {
|
345
|
+
val = INT2NUM(*(DBINT *)data);
|
346
|
+
break;
|
347
|
+
} else {
|
348
|
+
val = ENCODED_STR_NEW(data, data_len);
|
349
|
+
break;
|
350
|
+
}
|
351
|
+
}
|
352
|
+
default:
|
353
|
+
val = ENCODED_STR_NEW(data, data_len);
|
354
|
+
break;
|
355
|
+
}
|
356
|
+
}
|
357
|
+
if (as_array) {
|
358
|
+
rb_ary_store(row, i, val);
|
359
|
+
} else {
|
360
|
+
VALUE key;
|
361
|
+
if (rwrap->number_of_results == 0) {
|
362
|
+
key = rb_ary_entry(rwrap->fields, i);
|
363
|
+
} else {
|
364
|
+
key = rb_ary_entry(rb_ary_entry(rwrap->fields, rwrap->number_of_results), i);
|
365
|
+
}
|
366
|
+
rb_hash_aset(row, key, val);
|
367
|
+
}
|
368
|
+
}
|
369
|
+
return row;
|
370
|
+
}
|
371
|
+
|
372
|
+
|
373
|
+
// TinyTds::Client (public)
|
374
|
+
|
375
|
+
static VALUE rb_tinytds_result_fields(VALUE self) {
|
376
|
+
RETCODE dbsqlok_rc, dbresults_rc;
|
377
|
+
VALUE fields_processed;
|
378
|
+
GET_RESULT_WRAPPER(self);
|
379
|
+
dbsqlok_rc = rb_tinytds_result_ok_helper(rwrap->client);
|
380
|
+
dbresults_rc = rb_tinytds_result_dbresults_retcode(self);
|
381
|
+
fields_processed = rb_ary_entry(rwrap->fields_processed, rwrap->number_of_results);
|
382
|
+
if ((dbsqlok_rc == SUCCEED) && (dbresults_rc == SUCCEED) && (fields_processed == Qnil)) {
|
383
|
+
/* Default query options. */
|
384
|
+
int symbolize_keys = 0;
|
385
|
+
VALUE qopts = rb_iv_get(self, "@query_options");
|
386
|
+
if (rb_hash_aref(qopts, sym_symbolize_keys) == Qtrue)
|
387
|
+
symbolize_keys = 1;
|
388
|
+
/* Set number_of_fields count for this result set. */
|
389
|
+
rwrap->number_of_fields = dbnumcols(rwrap->client);
|
390
|
+
if (rwrap->number_of_fields > 0) {
|
391
|
+
/* Create fields for this result set. */
|
392
|
+
unsigned int fldi = 0;
|
393
|
+
VALUE fields = rb_ary_new2(rwrap->number_of_fields);
|
394
|
+
for (fldi = 0; fldi < rwrap->number_of_fields; fldi++) {
|
395
|
+
char *colname = dbcolname(rwrap->client, fldi+1);
|
396
|
+
VALUE field = symbolize_keys ? rb_str_intern(ENCODED_STR_NEW2(colname)) : rb_obj_freeze(ENCODED_STR_NEW2(colname));
|
397
|
+
rb_ary_store(fields, fldi, field);
|
398
|
+
}
|
399
|
+
/* Store the fields. */
|
400
|
+
if (rwrap->number_of_results == 0) {
|
401
|
+
rwrap->fields = fields;
|
402
|
+
} else if (rwrap->number_of_results == 1) {
|
403
|
+
VALUE multi_rs_fields = rb_ary_new();
|
404
|
+
rb_ary_store(multi_rs_fields, 0, rwrap->fields);
|
405
|
+
rb_ary_store(multi_rs_fields, 1, fields);
|
406
|
+
rwrap->fields = multi_rs_fields;
|
407
|
+
} else {
|
408
|
+
rb_ary_store(rwrap->fields, rwrap->number_of_results, fields);
|
409
|
+
}
|
410
|
+
}
|
411
|
+
rb_ary_store(rwrap->fields_processed, rwrap->number_of_results, Qtrue);
|
412
|
+
}
|
413
|
+
return rwrap->fields;
|
414
|
+
}
|
415
|
+
|
416
|
+
static VALUE rb_tinytds_result_each(int argc, VALUE * argv, VALUE self) {
|
417
|
+
/* Local Vars */
|
418
|
+
VALUE qopts, opts, block;
|
419
|
+
ID timezone;
|
420
|
+
int symbolize_keys = 0, as_array = 0, cache_rows = 0, first = 0, empty_sets = 0;
|
421
|
+
tinytds_client_userdata *userdata;
|
422
|
+
GET_RESULT_WRAPPER(self);
|
423
|
+
userdata = (tinytds_client_userdata *)dbgetuserdata(rwrap->client);
|
424
|
+
/* Merge Options Hash To Query Options. Populate Opts & Block Var. */
|
425
|
+
qopts = rb_iv_get(self, "@query_options");
|
426
|
+
if (rb_scan_args(argc, argv, "01&", &opts, &block) == 1)
|
427
|
+
qopts = rb_funcall(qopts, intern_merge, 1, opts);
|
428
|
+
rb_iv_set(self, "@query_options", qopts);
|
429
|
+
/* Locals From Options */
|
430
|
+
if (rb_hash_aref(qopts, sym_first) == Qtrue)
|
431
|
+
first = 1;
|
432
|
+
if (rb_hash_aref(qopts, sym_symbolize_keys) == Qtrue)
|
433
|
+
symbolize_keys = 1;
|
434
|
+
if (rb_hash_aref(qopts, sym_as) == sym_array)
|
435
|
+
as_array = 1;
|
436
|
+
if (rb_hash_aref(qopts, sym_cache_rows) == Qtrue)
|
437
|
+
cache_rows = 1;
|
438
|
+
if (rb_hash_aref(qopts, sym_timezone) == sym_local) {
|
439
|
+
timezone = intern_local;
|
440
|
+
} else if (rb_hash_aref(qopts, sym_timezone) == sym_utc) {
|
441
|
+
timezone = intern_utc;
|
442
|
+
} else {
|
443
|
+
rb_warn(":timezone option must be :utc or :local - defaulting to :local");
|
444
|
+
timezone = intern_local;
|
445
|
+
}
|
446
|
+
if (rb_hash_aref(qopts, sym_empty_sets) == Qtrue)
|
447
|
+
empty_sets = 1;
|
448
|
+
/* Make The Results Or Yield Existing */
|
449
|
+
if (NIL_P(rwrap->results)) {
|
450
|
+
RETCODE dbsqlok_rc, dbresults_rc;
|
451
|
+
rwrap->results = rb_ary_new();
|
452
|
+
dbsqlok_rc = rb_tinytds_result_ok_helper(rwrap->client);
|
453
|
+
dbresults_rc = rb_tinytds_result_dbresults_retcode(self);
|
454
|
+
while ((dbsqlok_rc == SUCCEED) && (dbresults_rc == SUCCEED)) {
|
455
|
+
int has_rows = (DBROWS(rwrap->client) == SUCCEED) ? 1 : 0;
|
456
|
+
if (has_rows || empty_sets || (rwrap->number_of_results == 0))
|
457
|
+
rb_tinytds_result_fields(self);
|
458
|
+
if ((has_rows || empty_sets) && rwrap->number_of_fields > 0) {
|
459
|
+
/* Create rows for this result set. */
|
460
|
+
unsigned long rowi = 0;
|
461
|
+
VALUE result = rb_ary_new();
|
462
|
+
while (nogvl_dbnextrow(rwrap->client) != NO_MORE_ROWS) {
|
463
|
+
VALUE row = rb_tinytds_result_fetch_row(self, timezone, symbolize_keys, as_array);
|
464
|
+
if (cache_rows)
|
465
|
+
rb_ary_store(result, rowi, row);
|
466
|
+
if (!NIL_P(block))
|
467
|
+
rb_yield(row);
|
468
|
+
if (first) {
|
469
|
+
dbcanquery(rwrap->client);
|
470
|
+
userdata->dbcancel_sent = 1;
|
471
|
+
}
|
472
|
+
rowi++;
|
473
|
+
}
|
474
|
+
rwrap->number_of_rows = rowi;
|
475
|
+
/* Store the result. */
|
476
|
+
if (cache_rows) {
|
477
|
+
if (rwrap->number_of_results == 0) {
|
478
|
+
rwrap->results = result;
|
479
|
+
} else if (rwrap->number_of_results == 1) {
|
480
|
+
VALUE multi_resultsets = rb_ary_new();
|
481
|
+
rb_ary_store(multi_resultsets, 0, rwrap->results);
|
482
|
+
rb_ary_store(multi_resultsets, 1, result);
|
483
|
+
rwrap->results = multi_resultsets;
|
484
|
+
} else {
|
485
|
+
rb_ary_store(rwrap->results, rwrap->number_of_results, result);
|
486
|
+
}
|
487
|
+
}
|
488
|
+
// If we find results increment the counter that helpers use and setup the next loop.
|
489
|
+
rwrap->number_of_results = rwrap->number_of_results + 1;
|
490
|
+
dbresults_rc = rb_tinytds_result_dbresults_retcode(self);
|
491
|
+
rb_ary_store(rwrap->fields_processed, rwrap->number_of_results, Qnil);
|
492
|
+
} else {
|
493
|
+
// If we do not find results, side step the rb_tinytds_result_dbresults_retcode helper and
|
494
|
+
// manually populate its memoized array while nullifing any memoized fields too before loop.
|
495
|
+
dbresults_rc = nogvl_dbresults(rwrap->client);
|
496
|
+
rb_ary_store(rwrap->dbresults_retcodes, rwrap->number_of_results, INT2FIX(dbresults_rc));
|
497
|
+
rb_ary_store(rwrap->fields_processed, rwrap->number_of_results, Qnil);
|
498
|
+
}
|
499
|
+
}
|
500
|
+
if (dbresults_rc == FAIL)
|
501
|
+
rb_warn("TinyTDS: Something in the dbresults() while loop set the return code to FAIL.\n");
|
502
|
+
userdata->dbsql_sent = 0;
|
503
|
+
} else if (!NIL_P(block)) {
|
504
|
+
unsigned long i;
|
505
|
+
for (i = 0; i < rwrap->number_of_rows; i++) {
|
506
|
+
rb_yield(rb_ary_entry(rwrap->results, i));
|
507
|
+
}
|
508
|
+
}
|
509
|
+
return rwrap->results;
|
510
|
+
}
|
511
|
+
|
512
|
+
static VALUE rb_tinytds_result_cancel(VALUE self) {
|
513
|
+
tinytds_client_userdata *userdata;
|
514
|
+
GET_RESULT_WRAPPER(self);
|
515
|
+
userdata = (tinytds_client_userdata *)dbgetuserdata(rwrap->client);
|
516
|
+
if (rwrap->client && !userdata->dbcancel_sent) {
|
517
|
+
rb_tinytds_result_ok_helper(rwrap->client);
|
518
|
+
dbcancel(rwrap->client);
|
519
|
+
userdata->dbcancel_sent = 1;
|
520
|
+
userdata->dbsql_sent = 0;
|
521
|
+
}
|
522
|
+
return Qtrue;
|
523
|
+
}
|
524
|
+
|
525
|
+
static VALUE rb_tinytds_result_do(VALUE self) {
|
526
|
+
GET_RESULT_WRAPPER(self);
|
527
|
+
if (rwrap->client) {
|
528
|
+
rb_tinytds_result_exec_helper(rwrap->client);
|
529
|
+
return LONG2NUM((long)dbcount(rwrap->client));
|
530
|
+
} else {
|
531
|
+
return Qnil;
|
532
|
+
}
|
533
|
+
}
|
534
|
+
|
535
|
+
static VALUE rb_tinytds_result_affected_rows(VALUE self) {
|
536
|
+
GET_RESULT_WRAPPER(self);
|
537
|
+
if (rwrap->client) {
|
538
|
+
return LONG2NUM((long)dbcount(rwrap->client));
|
539
|
+
} else {
|
540
|
+
return Qnil;
|
541
|
+
}
|
542
|
+
}
|
543
|
+
|
544
|
+
/* Duplicated in client.c */
|
545
|
+
static VALUE rb_tinytds_result_return_code(VALUE self) {
|
546
|
+
GET_RESULT_WRAPPER(self);
|
547
|
+
if (rwrap->client && dbhasretstat(rwrap->client)) {
|
548
|
+
return LONG2NUM((long)dbretstatus(rwrap->client));
|
549
|
+
} else {
|
550
|
+
return Qnil;
|
551
|
+
}
|
552
|
+
}
|
553
|
+
|
554
|
+
static VALUE rb_tinytds_result_insert(VALUE self) {
|
555
|
+
GET_RESULT_WRAPPER(self);
|
556
|
+
if (rwrap->client) {
|
557
|
+
VALUE identity = Qnil;
|
558
|
+
rb_tinytds_result_exec_helper(rwrap->client);
|
559
|
+
dbcmd(rwrap->client, rwrap->cwrap->identity_insert_sql);
|
560
|
+
if (nogvl_dbsqlexec(rwrap->client) != FAIL
|
561
|
+
&& nogvl_dbresults(rwrap->client) != FAIL
|
562
|
+
&& DBROWS(rwrap->client) != FAIL) {
|
563
|
+
while (nogvl_dbnextrow(rwrap->client) != NO_MORE_ROWS) {
|
564
|
+
int col = 1;
|
565
|
+
BYTE *data = dbdata(rwrap->client, col);
|
566
|
+
DBINT data_len = dbdatlen(rwrap->client, col);
|
567
|
+
int null_val = ((data == NULL) && (data_len == 0));
|
568
|
+
if (!null_val)
|
569
|
+
identity = LL2NUM(*(DBBIGINT *)data);
|
570
|
+
}
|
571
|
+
}
|
572
|
+
return identity;
|
573
|
+
} else {
|
574
|
+
return Qnil;
|
575
|
+
}
|
576
|
+
}
|
577
|
+
|
578
|
+
|
579
|
+
// Lib Init
|
580
|
+
|
581
|
+
void init_tinytds_result() {
|
582
|
+
/* Data Classes */
|
583
|
+
cKernel = rb_const_get(rb_cObject, rb_intern("Kernel"));
|
584
|
+
cDate = rb_const_get(rb_cObject, rb_intern("Date"));
|
585
|
+
/* Define TinyTds::Result */
|
586
|
+
cTinyTdsResult = rb_define_class_under(mTinyTds, "Result", rb_cObject);
|
587
|
+
rb_undef_alloc_func(cTinyTdsResult);
|
588
|
+
/* Define TinyTds::Result Public Methods */
|
589
|
+
rb_define_method(cTinyTdsResult, "fields", rb_tinytds_result_fields, 0);
|
590
|
+
rb_define_method(cTinyTdsResult, "each", rb_tinytds_result_each, -1);
|
591
|
+
rb_define_method(cTinyTdsResult, "cancel", rb_tinytds_result_cancel, 0);
|
592
|
+
rb_define_method(cTinyTdsResult, "do", rb_tinytds_result_do, 0);
|
593
|
+
rb_define_method(cTinyTdsResult, "affected_rows", rb_tinytds_result_affected_rows, 0);
|
594
|
+
rb_define_method(cTinyTdsResult, "return_code", rb_tinytds_result_return_code, 0);
|
595
|
+
rb_define_method(cTinyTdsResult, "insert", rb_tinytds_result_insert, 0);
|
596
|
+
/* Intern String Helpers */
|
597
|
+
intern_new = rb_intern("new");
|
598
|
+
intern_utc = rb_intern("utc");
|
599
|
+
intern_local = rb_intern("local");
|
600
|
+
intern_merge = rb_intern("merge");
|
601
|
+
intern_localtime = rb_intern("localtime");
|
602
|
+
intern_civil = rb_intern("civil");
|
603
|
+
intern_new_offset = rb_intern("new_offset");
|
604
|
+
intern_plus = rb_intern("+");
|
605
|
+
intern_divide = rb_intern("/");
|
606
|
+
intern_bigd = rb_intern("BigDecimal");
|
607
|
+
/* Symbol Helpers */
|
608
|
+
sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
|
609
|
+
sym_as = ID2SYM(rb_intern("as"));
|
610
|
+
sym_array = ID2SYM(rb_intern("array"));
|
611
|
+
sym_cache_rows = ID2SYM(rb_intern("cache_rows"));
|
612
|
+
sym_first = ID2SYM(rb_intern("first"));
|
613
|
+
sym_local = ID2SYM(intern_local);
|
614
|
+
sym_utc = ID2SYM(intern_utc);
|
615
|
+
sym_timezone = ID2SYM(rb_intern("timezone"));
|
616
|
+
sym_empty_sets = ID2SYM(rb_intern("empty_sets"));
|
617
|
+
/* Data Conversion Options */
|
618
|
+
opt_decimal_zero = rb_str_new2("0.0");
|
619
|
+
rb_global_variable(&opt_decimal_zero);
|
620
|
+
opt_float_zero = rb_float_new((double)0);
|
621
|
+
rb_global_variable(&opt_float_zero);
|
622
|
+
opt_one = INT2NUM(1);
|
623
|
+
opt_zero = INT2NUM(0);
|
624
|
+
opt_four = INT2NUM(4);
|
625
|
+
opt_19hdr = INT2NUM(1900);
|
626
|
+
opt_onek = INT2NUM(1000);
|
627
|
+
opt_tenk = INT2NUM(10000);
|
628
|
+
opt_onemil = INT2NUM(1000000);
|
629
|
+
opt_onebil = INT2NUM(1000000000);
|
630
|
+
/* Encoding */
|
631
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
632
|
+
binaryEncoding = rb_enc_find("binary");
|
633
|
+
#endif
|
634
|
+
}
|
@@ -0,0 +1,32 @@
|
|
1
|
+
|
2
|
+
#ifndef TINYTDS_RESULT_H
|
3
|
+
#define TINYTDS_RESULT_H
|
4
|
+
|
5
|
+
void init_tinytds_result();
|
6
|
+
VALUE rb_tinytds_new_result_obj(tinytds_client_wrapper *cwrap);
|
7
|
+
|
8
|
+
typedef struct {
|
9
|
+
tinytds_client_wrapper *cwrap;
|
10
|
+
DBPROCESS *client;
|
11
|
+
VALUE local_offset;
|
12
|
+
VALUE fields;
|
13
|
+
VALUE fields_processed;
|
14
|
+
VALUE results;
|
15
|
+
rb_encoding *encoding;
|
16
|
+
VALUE dbresults_retcodes;
|
17
|
+
unsigned int number_of_results;
|
18
|
+
unsigned int number_of_fields;
|
19
|
+
unsigned long number_of_rows;
|
20
|
+
} tinytds_result_wrapper;
|
21
|
+
|
22
|
+
|
23
|
+
// Lib Macros
|
24
|
+
|
25
|
+
#define GET_RESULT_WRAPPER(self) \
|
26
|
+
tinytds_result_wrapper *rwrap; \
|
27
|
+
Data_Get_Struct(self, tinytds_result_wrapper, rwrap)
|
28
|
+
|
29
|
+
|
30
|
+
|
31
|
+
|
32
|
+
#endif
|
@@ -0,0 +1,17 @@
|
|
1
|
+
#ifndef TINYTDS_EXT
|
2
|
+
#define TINYTDS_EXT
|
3
|
+
|
4
|
+
#undef SYBDBLIB
|
5
|
+
#define MSDBLIB 1
|
6
|
+
|
7
|
+
#include <ruby.h>
|
8
|
+
#include <ruby/encoding.h>
|
9
|
+
#include <ruby/version.h>
|
10
|
+
#include <ruby/thread.h>
|
11
|
+
#include <sybfront.h>
|
12
|
+
#include <sybdb.h>
|
13
|
+
|
14
|
+
#include <client.h>
|
15
|
+
#include <result.h>
|
16
|
+
|
17
|
+
#endif
|
Binary file
|
Binary file
|