tiny_tds 0.7.0 → 0.9.5.beta.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +3 -1
- data/CHANGELOG +17 -46
- data/Gemfile +4 -0
- data/README.md +20 -14
- data/Rakefile +18 -13
- data/VERSION +1 -0
- data/appveyor.yml +17 -6
- data/bin/tsql +25 -0
- data/exe/.keep +0 -0
- data/ext/tiny_tds/client.c +26 -24
- data/ext/tiny_tds/client.h +1 -0
- data/ext/tiny_tds/extconf.rb +57 -36
- data/ext/tiny_tds/extconsts.rb +14 -0
- data/ext/tiny_tds/result.c +76 -36
- data/ext/tiny_tds/result.h +0 -4
- data/ext/tiny_tds/tiny_tds_ext.h +4 -2
- data/lib/tiny_tds.rb +3 -9
- data/lib/tiny_tds/client.rb +61 -34
- data/lib/tiny_tds/version.rb +1 -1
- data/ports/patches/freetds/0.91.112/Makefile.in.diff +29 -0
- data/ports/patches/freetds/0.91.112/dblib-30-char-username.diff +11 -0
- data/ports/patches/freetds/0.95.75/0001-mingw_missing_inet_pton.diff +34 -0
- data/test/client_test.rb +32 -12
- data/test/result_test.rb +13 -13
- data/test/schema/sqlserver_2000.sql +13 -13
- data/test/schema/sqlserver_2005.sql +13 -13
- data/test/schema/sqlserver_2008.sql +13 -13
- data/test/schema/sqlserver_2014.sql +9 -9
- data/test/schema/sqlserver_azure.sql +13 -13
- data/test/schema/sybase_ase.sql +7 -7
- data/test/schema_test.rb +157 -33
- data/test/test_helper.rb +13 -8
- data/test/thread_test.rb +6 -4
- metadata +37 -15
data/exe/.keep
ADDED
File without changes
|
data/ext/tiny_tds/client.c
CHANGED
@@ -24,7 +24,8 @@ VALUE opt_escape_regex, opt_escape_dblquote;
|
|
24
24
|
|
25
25
|
// Lib Backend (Helpers)
|
26
26
|
|
27
|
-
VALUE rb_tinytds_raise_error(DBPROCESS *dbproc, int cancel, char *error, char *source, int severity, int dberr, int oserr) {
|
27
|
+
VALUE rb_tinytds_raise_error(DBPROCESS *dbproc, int cancel, const char *error, const char *source, int severity, int dberr, int oserr) {
|
28
|
+
VALUE e;
|
28
29
|
GET_CLIENT_USERDATA(dbproc);
|
29
30
|
if (cancel && !dbdead(dbproc) && userdata && !userdata->closed) {
|
30
31
|
userdata->dbsqlok_sent = 1;
|
@@ -32,7 +33,7 @@ VALUE rb_tinytds_raise_error(DBPROCESS *dbproc, int cancel, char *error, char *s
|
|
32
33
|
userdata->dbcancel_sent = 1;
|
33
34
|
dbcancel(dbproc);
|
34
35
|
}
|
35
|
-
|
36
|
+
e = rb_exc_new2(cTinyTdsError, error);
|
36
37
|
rb_funcall(e, intern_source_eql, 1, rb_str_new2(source));
|
37
38
|
if (severity)
|
38
39
|
rb_funcall(e, intern_severity_eql, 1, INT2FIX(severity));
|
@@ -48,13 +49,13 @@ VALUE rb_tinytds_raise_error(DBPROCESS *dbproc, int cancel, char *error, char *s
|
|
48
49
|
// Lib Backend (Memory Management & Handlers)
|
49
50
|
|
50
51
|
int tinytds_err_handler(DBPROCESS *dbproc, int severity, int dberr, int oserr, char *dberrstr, char *oserrstr) {
|
51
|
-
static char *source = "error";
|
52
|
-
GET_CLIENT_USERDATA(dbproc);
|
53
|
-
|
52
|
+
static const char *source = "error";
|
54
53
|
/* Everything should cancel by default */
|
55
54
|
int return_value = INT_CANCEL;
|
56
55
|
int cancel = 0;
|
57
56
|
|
57
|
+
GET_CLIENT_USERDATA(dbproc);
|
58
|
+
|
58
59
|
/* These error codes are documented in include/sybdb.h in FreeTDS */
|
59
60
|
switch(dberr) {
|
60
61
|
|
@@ -67,7 +68,7 @@ int tinytds_err_handler(DBPROCESS *dbproc, int severity, int dberr, int oserr, c
|
|
67
68
|
|
68
69
|
case SYBEICONVO:
|
69
70
|
dbfreebuf(dbproc);
|
70
|
-
|
71
|
+
return return_value;
|
71
72
|
|
72
73
|
case SYBETIME:
|
73
74
|
/*
|
@@ -125,7 +126,7 @@ int tinytds_err_handler(DBPROCESS *dbproc, int severity, int dberr, int oserr, c
|
|
125
126
|
}
|
126
127
|
|
127
128
|
int tinytds_msg_handler(DBPROCESS *dbproc, DBINT msgno, int msgstate, int severity, char *msgtext, char *srvname, char *procname, int line) {
|
128
|
-
static char *source = "message";
|
129
|
+
static const char *source = "message";
|
129
130
|
GET_CLIENT_USERDATA(dbproc);
|
130
131
|
if (severity > 10) {
|
131
132
|
// See tinytds_err_handler() for info about why we do this
|
@@ -230,6 +231,8 @@ static VALUE rb_tinytds_sqlsent(VALUE self) {
|
|
230
231
|
}
|
231
232
|
|
232
233
|
static VALUE rb_tinytds_execute(VALUE self, VALUE sql) {
|
234
|
+
VALUE result;
|
235
|
+
|
233
236
|
GET_CLIENT_WRAPPER(self);
|
234
237
|
rb_tinytds_client_reset_userdata(cwrap->userdata);
|
235
238
|
REQUIRE_OPEN_CLIENT(cwrap);
|
@@ -239,12 +242,14 @@ static VALUE rb_tinytds_execute(VALUE self, VALUE sql) {
|
|
239
242
|
return Qfalse;
|
240
243
|
}
|
241
244
|
cwrap->userdata->dbsql_sent = 1;
|
242
|
-
|
245
|
+
result = rb_tinytds_new_result_obj(cwrap);
|
243
246
|
rb_iv_set(result, "@query_options", rb_funcall(rb_iv_get(self, "@query_options"), intern_dup, 0));
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
247
|
+
{
|
248
|
+
GET_RESULT_WRAPPER(result);
|
249
|
+
rwrap->local_offset = rb_funcall(cTinyTdsClient, intern_local_offset, 0);
|
250
|
+
rwrap->encoding = cwrap->encoding;
|
251
|
+
return result;
|
252
|
+
}
|
248
253
|
}
|
249
254
|
|
250
255
|
static VALUE rb_tinytds_charset(VALUE self) {
|
@@ -258,9 +263,11 @@ static VALUE rb_tinytds_encoding(VALUE self) {
|
|
258
263
|
}
|
259
264
|
|
260
265
|
static VALUE rb_tinytds_escape(VALUE self, VALUE string) {
|
261
|
-
|
266
|
+
VALUE new_string;
|
262
267
|
GET_CLIENT_WRAPPER(self);
|
263
|
-
|
268
|
+
|
269
|
+
Check_Type(string, T_STRING);
|
270
|
+
new_string = rb_funcall(string, intern_gsub, 2, opt_escape_regex, opt_escape_dblquote);
|
264
271
|
rb_enc_associate(new_string, cwrap->encoding);
|
265
272
|
return new_string;
|
266
273
|
}
|
@@ -280,13 +287,6 @@ static VALUE rb_tinytds_identity_sql(VALUE self) {
|
|
280
287
|
return rb_str_new2(cwrap->identity_insert_sql);
|
281
288
|
}
|
282
289
|
|
283
|
-
static VALUE rb_tinytds_freetds_nine_one_or_higher(VALUE self) {
|
284
|
-
#ifdef DBSETLDBNAME
|
285
|
-
return Qtrue;
|
286
|
-
#else
|
287
|
-
return Qfalse;
|
288
|
-
#endif
|
289
|
-
}
|
290
290
|
|
291
291
|
|
292
292
|
// TinyTds::Client (protected)
|
@@ -294,6 +294,8 @@ static VALUE rb_tinytds_freetds_nine_one_or_higher(VALUE self) {
|
|
294
294
|
static VALUE rb_tinytds_connect(VALUE self, VALUE opts) {
|
295
295
|
/* Parsing options hash to local vars. */
|
296
296
|
VALUE user, pass, dataserver, database, app, version, ltimeout, timeout, charset, azure;
|
297
|
+
GET_CLIENT_WRAPPER(self);
|
298
|
+
|
297
299
|
user = rb_hash_aref(opts, sym_username);
|
298
300
|
pass = rb_hash_aref(opts, sym_password);
|
299
301
|
dataserver = rb_hash_aref(opts, sym_dataserver);
|
@@ -311,7 +313,6 @@ static VALUE rb_tinytds_connect(VALUE self, VALUE opts) {
|
|
311
313
|
}
|
312
314
|
dberrhandle(tinytds_err_handler);
|
313
315
|
dbmsghandle(tinytds_msg_handler);
|
314
|
-
GET_CLIENT_WRAPPER(self);
|
315
316
|
cwrap->login = dblogin();
|
316
317
|
if (!NIL_P(version))
|
317
318
|
dbsetlversion(cwrap->login, NUM2INT(version));
|
@@ -336,6 +337,8 @@ static VALUE rb_tinytds_connect(VALUE self, VALUE opts) {
|
|
336
337
|
}
|
337
338
|
cwrap->client = dbopen(cwrap->login, StringValueCStr(dataserver));
|
338
339
|
if (cwrap->client) {
|
340
|
+
VALUE transposed_encoding;
|
341
|
+
|
339
342
|
cwrap->closed = 0;
|
340
343
|
cwrap->charset = charset;
|
341
344
|
if (!NIL_P(version))
|
@@ -345,7 +348,7 @@ static VALUE rb_tinytds_connect(VALUE self, VALUE opts) {
|
|
345
348
|
if (!NIL_P(database) && (azure != Qtrue)) {
|
346
349
|
dbuse(cwrap->client, StringValueCStr(database));
|
347
350
|
}
|
348
|
-
|
351
|
+
transposed_encoding = rb_funcall(cTinyTdsClient, intern_transpose_iconv_encoding, 1, charset);
|
349
352
|
cwrap->encoding = rb_enc_find(StringValueCStr(transposed_encoding));
|
350
353
|
if (dbtds(cwrap->client) <= 7) {
|
351
354
|
cwrap->identity_insert_sql = "SELECT CAST(@@IDENTITY AS bigint) AS Ident";
|
@@ -375,7 +378,6 @@ void init_tinytds_client() {
|
|
375
378
|
rb_define_method(cTinyTdsClient, "escape", rb_tinytds_escape, 1);
|
376
379
|
rb_define_method(cTinyTdsClient, "return_code", rb_tinytds_return_code, 0);
|
377
380
|
rb_define_method(cTinyTdsClient, "identity_sql", rb_tinytds_identity_sql, 0);
|
378
|
-
rb_define_method(cTinyTdsClient, "freetds_091_or_higer?", rb_tinytds_freetds_nine_one_or_higher, 0);
|
379
381
|
/* Define TinyTds::Client Protected Methods */
|
380
382
|
rb_define_protected_method(cTinyTdsClient, "connect", rb_tinytds_connect, 1);
|
381
383
|
/* Symbols For Connect */
|
data/ext/tiny_tds/client.h
CHANGED
data/ext/tiny_tds/extconf.rb
CHANGED
@@ -3,27 +3,13 @@ ENV['RC_ARCHS'] = '' if RUBY_PLATFORM =~ /darwin/
|
|
3
3
|
# :stopdoc:
|
4
4
|
|
5
5
|
require 'mkmf'
|
6
|
-
require 'mini_portile'
|
7
6
|
require 'fileutils'
|
8
7
|
|
9
|
-
#
|
10
|
-
#
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
OPENSSL_VERSION = ENV['TINYTDS_OPENSSL_VERSION'] || '1.0.2d'
|
15
|
-
OPENSSL_SOURCE_URI = "http://www.openssl.org/source/openssl-#{OPENSSL_VERSION}.tar.gz"
|
16
|
-
|
17
|
-
FREETDS_VERSION = ENV['TINYTDS_FREETDS_VERSION'] || "0.91"
|
18
|
-
FREETDS_VERSION_INFO = Hash.new { |h,k|
|
19
|
-
h[k] = {:files => "ftp://ftp.freetds.org/pub/freetds/stable/freetds-#{k}.tar.gz"}
|
20
|
-
}.merge({
|
21
|
-
"0.82" => {:files => "ftp://ftp.freetds.org/pub/freetds/old/0.82/freetds-0.82.tar.gz"},
|
22
|
-
"0.91" => {:files => "ftp://ftp.freetds.org/pub/freetds/stable/freetds-0.91.112.tar.gz"},
|
23
|
-
"0.92" => {:files => "ftp://ftp.freetds.org/pub/freetds/stable/freetds-0.92.405.tar.gz"},
|
24
|
-
"current" => {:files => "ftp://ftp.freetds.org/pub/freetds/current/freetds-current.tar.gz"}
|
25
|
-
})
|
26
|
-
FREETDS_SOURCE_URI = FREETDS_VERSION_INFO[FREETDS_VERSION][:files]
|
8
|
+
# The gem version constraint in the gemspec is not respected at install time.
|
9
|
+
# Keep this version in sync with the one in the gemspec !
|
10
|
+
gem 'mini_portile2', '~> 2.0'
|
11
|
+
require 'mini_portile2'
|
12
|
+
require_relative './extconsts'
|
27
13
|
|
28
14
|
# Shamelessly copied from nokogiri
|
29
15
|
#
|
@@ -44,6 +30,12 @@ usage: ruby #{$0} [options]
|
|
44
30
|
--enable-lookup
|
45
31
|
Search for freetds through all paths in the PATH environment variable.
|
46
32
|
|
33
|
+
--disable-openssl
|
34
|
+
Disable OpenSSL for freetds build. No effect on system-freetds.
|
35
|
+
|
36
|
+
--enable-gnutls
|
37
|
+
Use GnuTLS instead of OpenSSL for freetds build.
|
38
|
+
|
47
39
|
--enable-cross-build
|
48
40
|
Do cross-build.
|
49
41
|
HELP
|
@@ -90,17 +82,13 @@ class BuildRecipe < MiniPortile
|
|
90
82
|
super(name, version)
|
91
83
|
self.files = files
|
92
84
|
self.target = File.expand_path('../../../ports', __FILE__)
|
93
|
-
|
94
|
-
# correct compiler prefix for cross build, but use host if not set.
|
95
|
-
self.host = consolidated_host(RbConfig::CONFIG["host_alias"].empty? ? RbConfig::CONFIG["host"] : RbConfig::CONFIG["host_alias"])
|
85
|
+
self.host = consolidated_host(RbConfig::CONFIG["host"])
|
96
86
|
self.patch_files = Dir[File.join(self.target, "patches", self.name, self.version, "*.diff")].sort
|
97
87
|
end
|
98
88
|
|
99
89
|
def consolidated_host(name)
|
100
|
-
|
101
|
-
|
102
|
-
name.gsub('i586-mingw32msvc', 'i686-w64-mingw32').
|
103
|
-
gsub('i686-pc-mingw32', 'i686-w64-mingw32')
|
90
|
+
# Host name and prefix of build tools are different on Windows 32 bit.
|
91
|
+
name.gsub('i686-pc-mingw32', 'i686-w64-mingw32')
|
104
92
|
end
|
105
93
|
|
106
94
|
def configure_defaults
|
@@ -111,10 +99,16 @@ class BuildRecipe < MiniPortile
|
|
111
99
|
]
|
112
100
|
end
|
113
101
|
|
102
|
+
# Use the same path for all recipes, so that only one include/lib path is required.
|
114
103
|
def port_path
|
115
104
|
"#{target}/#{host}"
|
116
105
|
end
|
117
106
|
|
107
|
+
# We use the same port_path for all recipes. That breaks the standard installed? method.
|
108
|
+
def installed?
|
109
|
+
false
|
110
|
+
end
|
111
|
+
|
118
112
|
# When using rake-compiler-dock on Windows, the underlying Virtualbox shared
|
119
113
|
# folders don't support symlinks, but libiconv expects it for a build on
|
120
114
|
# Linux. We work around this limitation by using the temp dir for cooking.
|
@@ -195,21 +189,24 @@ def define_libssl_recipe(host)
|
|
195
189
|
end
|
196
190
|
|
197
191
|
def define_libiconv_recipe(host)
|
198
|
-
BuildRecipe.new("libiconv", ICONV_VERSION, [ICONV_SOURCE_URI])
|
199
|
-
.tap do |recipe|
|
192
|
+
BuildRecipe.new("libiconv", ICONV_VERSION, [ICONV_SOURCE_URI]).tap do |recipe|
|
200
193
|
# always produce position independent code
|
201
194
|
recipe.configure_options << "CFLAGS=-fPIC"
|
202
195
|
end
|
203
196
|
end
|
204
197
|
|
205
|
-
def define_freetds_recipe(host, libiconv, libssl)
|
206
|
-
BuildRecipe.new("freetds", FREETDS_VERSION, [FREETDS_SOURCE_URI])
|
207
|
-
|
208
|
-
with_tdsver = FREETDS_VERSION =~ /0\.8/ ? "--with-tdsver=8.0" : "--with-tdsver=7.1"
|
198
|
+
def define_freetds_recipe(host, libiconv, libssl, gnutls)
|
199
|
+
BuildRecipe.new("freetds", FREETDS_VERSION, [FREETDS_SOURCE_URI]).tap do |recipe|
|
200
|
+
with_tdsver = FREETDS_VERSION =~ /0\.91/ ? "--with-tdsver=7.1" : "--with-tdsver=7.3"
|
209
201
|
for_windows = recipe.host =~ /mswin|mingw/i
|
210
202
|
recipe.configure_options << '--with-pic'
|
211
203
|
recipe.configure_options << "--with-libiconv-prefix=#{libiconv.path}" if libiconv
|
212
|
-
|
204
|
+
if true == libssl
|
205
|
+
recipe.configure_options << "--with-openssl"
|
206
|
+
elsif libssl
|
207
|
+
recipe.configure_options << "--with-openssl=#{libssl.path}"
|
208
|
+
end
|
209
|
+
recipe.configure_options << "--with-gnutls" if gnutls
|
213
210
|
recipe.configure_options << '--sysconfdir=C:/Sites' if for_windows
|
214
211
|
recipe.configure_options << '--enable-sspi' if for_windows
|
215
212
|
recipe.configure_options << "--disable-odbc"
|
@@ -217,9 +214,31 @@ def define_freetds_recipe(host, libiconv, libssl)
|
|
217
214
|
if libiconv
|
218
215
|
# For some reason freetds doesn't honor --with-libiconv-prefix
|
219
216
|
# so we have do add it by hand:
|
220
|
-
recipe.configure_options << "
|
221
|
-
recipe.configure_options << "
|
217
|
+
recipe.configure_options << "CFLAGS=-I#{libiconv.path}/include"
|
218
|
+
recipe.configure_options << "LDFLAGS=-L#{libiconv.path}/lib -liconv"
|
219
|
+
end
|
220
|
+
|
221
|
+
class << recipe
|
222
|
+
|
223
|
+
def install
|
224
|
+
super_value = super
|
225
|
+
# Install binstub target binaries.
|
226
|
+
if super_value
|
227
|
+
bin_path = File.expand_path File.join(path, 'bin')
|
228
|
+
exe_path = File.expand_path File.join(target, '..', 'exe')
|
229
|
+
return unless File.directory?(bin_path)
|
230
|
+
ENV['PATH'] = "#{bin_path}#{File::PATH_SEPARATOR}#{ENV['PATH']}" unless ENV['PATH'].include?(bin_path)
|
231
|
+
['tsql'].each do |bin|
|
232
|
+
path = which(bin)
|
233
|
+
next unless path.include?(bin_path)
|
234
|
+
FileUtils.cp path, exe_path
|
235
|
+
end
|
236
|
+
end
|
237
|
+
super_value
|
238
|
+
end
|
239
|
+
|
222
240
|
end
|
241
|
+
|
223
242
|
end
|
224
243
|
end
|
225
244
|
|
@@ -285,11 +304,13 @@ system_freetds = enable_config('system-freetds', ENV['TINYTDS_SKIP_PORTS'] || fr
|
|
285
304
|
host = RbConfig::CONFIG["host"]
|
286
305
|
system_iconv = enable_config('system-iconv', host =~ /mingw|mswin/ ? false : true)
|
287
306
|
system_openssl = enable_config('system-openssl', host =~ /mingw|mswin/ ? false : true )
|
307
|
+
enable_gnutls = enable_config('gnutls', false )
|
308
|
+
enable_openssl = enable_config('openssl', !enable_gnutls )
|
288
309
|
|
289
310
|
unless system_freetds
|
290
311
|
libssl = define_libssl_recipe(host).cook_and_activate unless system_openssl
|
291
312
|
libiconv = define_libiconv_recipe(host).cook_and_activate unless system_iconv
|
292
|
-
freetds = define_freetds_recipe(host, libiconv, libssl).cook_and_activate
|
313
|
+
freetds = define_freetds_recipe(host, libiconv, libssl || enable_openssl, enable_gnutls).cook_and_activate
|
293
314
|
dir_config('freetds', freetds.path + "/include", freetds.path + "/lib")
|
294
315
|
end
|
295
316
|
|
@@ -0,0 +1,14 @@
|
|
1
|
+
|
2
|
+
ICONV_VERSION = ENV['TINYTDS_ICONV_VERSION'] || "1.14"
|
3
|
+
ICONV_SOURCE_URI = "http://ftp.gnu.org/pub/gnu/libiconv/libiconv-#{ICONV_VERSION}.tar.gz"
|
4
|
+
|
5
|
+
OPENSSL_VERSION = ENV['TINYTDS_OPENSSL_VERSION'] || '1.0.2e'
|
6
|
+
OPENSSL_SOURCE_URI = "http://www.openssl.org/source/openssl-#{OPENSSL_VERSION}.tar.gz"
|
7
|
+
|
8
|
+
FREETDS_VERSION = ENV['TINYTDS_FREETDS_VERSION'] || "0.95.75"
|
9
|
+
FREETDS_VERSION_INFO = Hash.new { |h,k|
|
10
|
+
h[k] = {files: "ftp://ftp.freetds.org/pub/freetds/stable/freetds-#{k}.tar.bz2"}
|
11
|
+
}
|
12
|
+
FREETDS_VERSION_INFO['0.99'] = {files: 'ftp://ftp.freetds.org/pub/freetds/current/freetds-dev.0.99.479.tar.bz2'}
|
13
|
+
FREETDS_VERSION_INFO['current'] = {files: 'https://github.com/FreeTDS/freetds/archive/master.zip'}
|
14
|
+
FREETDS_SOURCE_URI = FREETDS_VERSION_INFO[FREETDS_VERSION][:files]
|
data/ext/tiny_tds/result.c
CHANGED
@@ -7,9 +7,9 @@
|
|
7
7
|
VALUE cTinyTdsResult;
|
8
8
|
extern VALUE mTinyTds, cTinyTdsClient, cTinyTdsError;
|
9
9
|
VALUE cBigDecimal, cDate;
|
10
|
-
VALUE opt_decimal_zero, opt_float_zero, opt_one, opt_zero, opt_four, opt_19hdr, opt_tenk, opt_onemil;
|
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
11
|
static ID intern_new, intern_utc, intern_local, intern_localtime, intern_merge,
|
12
|
-
intern_civil, intern_new_offset, intern_plus, intern_divide
|
12
|
+
intern_civil, intern_new_offset, intern_plus, intern_divide;
|
13
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
14
|
|
15
15
|
|
@@ -27,6 +27,12 @@ rb_encoding *binaryEncoding;
|
|
27
27
|
_val; \
|
28
28
|
})
|
29
29
|
|
30
|
+
#ifdef _WIN32
|
31
|
+
#define LONG_LONG_FORMAT "I64d"
|
32
|
+
#else
|
33
|
+
#define LONG_LONG_FORMAT "lld"
|
34
|
+
#endif
|
35
|
+
|
30
36
|
|
31
37
|
// Lib Backend (Memory Management)
|
32
38
|
|
@@ -42,7 +48,6 @@ static void rb_tinytds_result_mark(void *ptr) {
|
|
42
48
|
}
|
43
49
|
|
44
50
|
static void rb_tinytds_result_free(void *ptr) {
|
45
|
-
tinytds_result_wrapper *rwrap = (tinytds_result_wrapper *)ptr;
|
46
51
|
xfree(ptr);
|
47
52
|
}
|
48
53
|
|
@@ -67,8 +72,8 @@ VALUE rb_tinytds_new_result_obj(tinytds_client_wrapper *cwrap) {
|
|
67
72
|
// No GVL Helpers
|
68
73
|
|
69
74
|
#define NOGVL_DBCALL(_dbfunction, _client) ( \
|
70
|
-
(RETCODE)rb_thread_call_without_gvl( \
|
71
|
-
(
|
75
|
+
(RETCODE)(intptr_t)rb_thread_call_without_gvl( \
|
76
|
+
(void *(*)(void *))_dbfunction, _client, \
|
72
77
|
(rb_unblock_function_t*)dbcancel_ubf, _client ) \
|
73
78
|
)
|
74
79
|
|
@@ -94,8 +99,8 @@ static void nogvl_cleanup(DBPROCESS *client) {
|
|
94
99
|
userdata->nonblocking_error.is_set = 0;
|
95
100
|
rb_tinytds_raise_error(client,
|
96
101
|
userdata->nonblocking_error.cancel,
|
97
|
-
|
98
|
-
|
102
|
+
userdata->nonblocking_error.error,
|
103
|
+
userdata->nonblocking_error.source,
|
99
104
|
userdata->nonblocking_error.severity,
|
100
105
|
userdata->nonblocking_error.dberr,
|
101
106
|
userdata->nonblocking_error.oserr);
|
@@ -139,9 +144,9 @@ static RETCODE nogvl_dbnextrow(DBPROCESS * client) {
|
|
139
144
|
// Lib Backend (Helpers)
|
140
145
|
|
141
146
|
static RETCODE rb_tinytds_result_dbresults_retcode(VALUE self) {
|
142
|
-
GET_RESULT_WRAPPER(self);
|
143
147
|
VALUE ruby_rc;
|
144
148
|
RETCODE db_rc;
|
149
|
+
GET_RESULT_WRAPPER(self);
|
145
150
|
ruby_rc = rb_ary_entry(rwrap->dbresults_retcodes, rwrap->number_of_results);
|
146
151
|
if (NIL_P(ruby_rc)) {
|
147
152
|
db_rc = nogvl_dbresults(rwrap->client);
|
@@ -162,8 +167,8 @@ static RETCODE rb_tinytds_result_ok_helper(DBPROCESS *client) {
|
|
162
167
|
}
|
163
168
|
|
164
169
|
static void rb_tinytds_result_exec_helper(DBPROCESS *client) {
|
165
|
-
GET_CLIENT_USERDATA(client);
|
166
170
|
RETCODE dbsqlok_rc = rb_tinytds_result_ok_helper(client);
|
171
|
+
GET_CLIENT_USERDATA(client);
|
167
172
|
if (dbsqlok_rc == SUCCEED) {
|
168
173
|
/*
|
169
174
|
This is to just process each result set. Commands such as backup and
|
@@ -185,12 +190,13 @@ static void rb_tinytds_result_exec_helper(DBPROCESS *client) {
|
|
185
190
|
}
|
186
191
|
|
187
192
|
static VALUE rb_tinytds_result_fetch_row(VALUE self, ID timezone, int symbolize_keys, int as_array) {
|
193
|
+
VALUE row;
|
194
|
+
/* Storing Values */
|
195
|
+
unsigned int i;
|
188
196
|
/* Wrapper And Local Vars */
|
189
197
|
GET_RESULT_WRAPPER(self);
|
190
198
|
/* Create Empty Row */
|
191
|
-
|
192
|
-
/* Storing Values */
|
193
|
-
unsigned int i = 0;
|
199
|
+
row = as_array ? rb_ary_new2(rwrap->number_of_fields) : rb_hash_new();
|
194
200
|
for (i = 0; i < rwrap->number_of_fields; i++) {
|
195
201
|
VALUE val = Qnil;
|
196
202
|
int col = i+1;
|
@@ -238,7 +244,7 @@ static VALUE rb_tinytds_result_fetch_row(VALUE self, ID timezone, int symbolize_
|
|
238
244
|
DBMONEY *money = (DBMONEY *)data;
|
239
245
|
char converted_money[25];
|
240
246
|
long long money_value = ((long long)money->mnyhigh << 32) | money->mnylow;
|
241
|
-
sprintf(converted_money, "%
|
247
|
+
sprintf(converted_money, "%" LONG_LONG_FORMAT, money_value);
|
242
248
|
val = rb_funcall(cBigDecimal, intern_new, 2, rb_str_new2(converted_money), opt_four);
|
243
249
|
val = rb_funcall(val, intern_divide, 1, opt_tenk);
|
244
250
|
break;
|
@@ -270,22 +276,50 @@ static VALUE rb_tinytds_result_fetch_row(VALUE self, ID timezone, int symbolize_
|
|
270
276
|
data_len = sizeof(new_data);
|
271
277
|
}
|
272
278
|
case SYBDATETIME: {
|
273
|
-
DBDATEREC
|
274
|
-
dbdatecrack(rwrap->client, &
|
275
|
-
|
276
|
-
|
277
|
-
day = date_rec.datedmonth,
|
278
|
-
hour = date_rec.datehour,
|
279
|
-
min = date_rec.dateminute,
|
280
|
-
sec = date_rec.datesecond,
|
281
|
-
msec = date_rec.datemsecond;
|
282
|
-
if (year+month+day+hour+min+sec+msec != 0) {
|
283
|
-
VALUE offset = (timezone == intern_local) ? rwrap->local_offset : opt_zero;
|
284
|
-
uint64_t seconds = (year*31557600ULL) + (month*2592000ULL) + (day*86400ULL) + (hour*3600ULL) + (min*60ULL) + sec;
|
285
|
-
val = rb_funcall(rb_cTime, timezone, 7, INT2NUM(year), INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), INT2NUM(sec), INT2NUM(msec*1000));
|
279
|
+
DBDATEREC dr;
|
280
|
+
dbdatecrack(rwrap->client, &dr, (DBDATETIME *)data);
|
281
|
+
if (dr.year + dr.month + dr.day + dr.hour + dr.minute + dr.second + dr.millisecond != 0) {
|
282
|
+
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));
|
286
283
|
}
|
287
284
|
break;
|
288
285
|
}
|
286
|
+
case 40: // SYBMSDATE
|
287
|
+
case 41: // SYBMSTIME
|
288
|
+
case 42: // SYBMSDATETIME2
|
289
|
+
case 43: { // SYBMSDATETIMEOFFSET
|
290
|
+
#ifdef DBVERSION_73
|
291
|
+
if (dbtds(rwrap->client) >= DBTDS_7_3) {
|
292
|
+
DBDATEREC2 dr2;
|
293
|
+
dbanydatecrack(rwrap->client, &dr2, coltype, data);
|
294
|
+
switch(coltype) {
|
295
|
+
case 40: { // SYBMSDATE
|
296
|
+
val = rb_funcall(cDate, intern_new, 3, INT2NUM(dr2.year), INT2NUM(dr2.month), INT2NUM(dr2.day));
|
297
|
+
break;
|
298
|
+
}
|
299
|
+
case 41: { // SYBMSTIME
|
300
|
+
VALUE rational_nsec = rb_Rational(INT2NUM(dr2.nanosecond), opt_onek);
|
301
|
+
val = rb_funcall(rb_cTime, timezone, 7, INT2NUM(1900), INT2NUM(1), INT2NUM(1), INT2NUM(dr2.hour), INT2NUM(dr2.minute), INT2NUM(dr2.second), rational_nsec);
|
302
|
+
break;
|
303
|
+
}
|
304
|
+
case 42: { // SYBMSDATETIME2
|
305
|
+
VALUE rational_nsec = rb_Rational(INT2NUM(dr2.nanosecond), opt_onek);
|
306
|
+
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);
|
307
|
+
break;
|
308
|
+
}
|
309
|
+
case 43: { // SYBMSDATETIMEOFFSET
|
310
|
+
VALUE rational_sec = rb_Rational(INT2NUM(dr2.nanosecond), opt_onebil);
|
311
|
+
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));
|
312
|
+
break;
|
313
|
+
}
|
314
|
+
}
|
315
|
+
} else {
|
316
|
+
val = ENCODED_STR_NEW(data, data_len);
|
317
|
+
}
|
318
|
+
#else
|
319
|
+
val = ENCODED_STR_NEW(data, data_len);
|
320
|
+
#endif
|
321
|
+
break;
|
322
|
+
}
|
289
323
|
case SYBCHAR:
|
290
324
|
case SYBTEXT:
|
291
325
|
val = ENCODED_STR_NEW(data, data_len);
|
@@ -314,10 +348,12 @@ static VALUE rb_tinytds_result_fetch_row(VALUE self, ID timezone, int symbolize_
|
|
314
348
|
// TinyTds::Client (public)
|
315
349
|
|
316
350
|
static VALUE rb_tinytds_result_fields(VALUE self) {
|
351
|
+
RETCODE dbsqlok_rc, dbresults_rc;
|
352
|
+
VALUE fields_processed;
|
317
353
|
GET_RESULT_WRAPPER(self);
|
318
|
-
|
319
|
-
|
320
|
-
|
354
|
+
dbsqlok_rc = rb_tinytds_result_ok_helper(rwrap->client);
|
355
|
+
dbresults_rc = rb_tinytds_result_dbresults_retcode(self);
|
356
|
+
fields_processed = rb_ary_entry(rwrap->fields_processed, rwrap->number_of_results);
|
321
357
|
if ((dbsqlok_rc == SUCCEED) && (dbresults_rc == SUCCEED) && (fields_processed == Qnil)) {
|
322
358
|
/* Default query options. */
|
323
359
|
int symbolize_keys = 0;
|
@@ -353,12 +389,13 @@ static VALUE rb_tinytds_result_fields(VALUE self) {
|
|
353
389
|
}
|
354
390
|
|
355
391
|
static VALUE rb_tinytds_result_each(int argc, VALUE * argv, VALUE self) {
|
356
|
-
GET_RESULT_WRAPPER(self);
|
357
|
-
GET_CLIENT_USERDATA(rwrap->client);
|
358
392
|
/* Local Vars */
|
359
393
|
VALUE qopts, opts, block;
|
360
394
|
ID timezone;
|
361
395
|
int symbolize_keys = 0, as_array = 0, cache_rows = 0, first = 0, empty_sets = 0;
|
396
|
+
tinytds_client_userdata *userdata;
|
397
|
+
GET_RESULT_WRAPPER(self);
|
398
|
+
userdata = (tinytds_client_userdata *)dbgetuserdata(rwrap->client);
|
362
399
|
/* Merge Options Hash To Query Options. Populate Opts & Block Var. */
|
363
400
|
qopts = rb_iv_get(self, "@query_options");
|
364
401
|
if (rb_scan_args(argc, argv, "01&", &opts, &block) == 1)
|
@@ -385,9 +422,10 @@ static VALUE rb_tinytds_result_each(int argc, VALUE * argv, VALUE self) {
|
|
385
422
|
empty_sets = 1;
|
386
423
|
/* Make The Results Or Yield Existing */
|
387
424
|
if (NIL_P(rwrap->results)) {
|
425
|
+
RETCODE dbsqlok_rc, dbresults_rc;
|
388
426
|
rwrap->results = rb_ary_new();
|
389
|
-
|
390
|
-
|
427
|
+
dbsqlok_rc = rb_tinytds_result_ok_helper(rwrap->client);
|
428
|
+
dbresults_rc = rb_tinytds_result_dbresults_retcode(self);
|
391
429
|
while ((dbsqlok_rc == SUCCEED) && (dbresults_rc == SUCCEED)) {
|
392
430
|
int has_rows = (DBROWS(rwrap->client) == SUCCEED) ? 1 : 0;
|
393
431
|
if (has_rows || empty_sets || (rwrap->number_of_results == 0))
|
@@ -447,8 +485,9 @@ static VALUE rb_tinytds_result_each(int argc, VALUE * argv, VALUE self) {
|
|
447
485
|
}
|
448
486
|
|
449
487
|
static VALUE rb_tinytds_result_cancel(VALUE self) {
|
488
|
+
tinytds_client_userdata *userdata;
|
450
489
|
GET_RESULT_WRAPPER(self);
|
451
|
-
|
490
|
+
userdata = (tinytds_client_userdata *)dbgetuserdata(rwrap->client);
|
452
491
|
if (rwrap->client && !userdata->dbcancel_sent) {
|
453
492
|
RETCODE dbsqlok_rc = rb_tinytds_result_ok_helper(rwrap->client);
|
454
493
|
dbcancel(rwrap->client);
|
@@ -490,8 +529,8 @@ static VALUE rb_tinytds_result_return_code(VALUE self) {
|
|
490
529
|
static VALUE rb_tinytds_result_insert(VALUE self) {
|
491
530
|
GET_RESULT_WRAPPER(self);
|
492
531
|
if (rwrap->client) {
|
493
|
-
rb_tinytds_result_exec_helper(rwrap->client);
|
494
532
|
VALUE identity = Qnil;
|
533
|
+
rb_tinytds_result_exec_helper(rwrap->client);
|
495
534
|
dbcmd(rwrap->client, rwrap->cwrap->identity_insert_sql);
|
496
535
|
if (nogvl_dbsqlexec(rwrap->client) != FAIL
|
497
536
|
&& nogvl_dbresults(rwrap->client) != FAIL
|
@@ -538,7 +577,6 @@ void init_tinytds_result() {
|
|
538
577
|
intern_new_offset = rb_intern("new_offset");
|
539
578
|
intern_plus = rb_intern("+");
|
540
579
|
intern_divide = rb_intern("/");
|
541
|
-
intern_Rational = rb_intern("Rational");
|
542
580
|
/* Symbol Helpers */
|
543
581
|
sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
|
544
582
|
sym_as = ID2SYM(rb_intern("as"));
|
@@ -558,8 +596,10 @@ void init_tinytds_result() {
|
|
558
596
|
opt_zero = INT2NUM(0);
|
559
597
|
opt_four = INT2NUM(4);
|
560
598
|
opt_19hdr = INT2NUM(1900);
|
599
|
+
opt_onek = INT2NUM(1000);
|
561
600
|
opt_tenk = INT2NUM(10000);
|
562
601
|
opt_onemil = INT2NUM(1000000);
|
602
|
+
opt_onebil = INT2NUM(1000000000);
|
563
603
|
/* Encoding */
|
564
604
|
#ifdef HAVE_RUBY_ENCODING_H
|
565
605
|
binaryEncoding = rb_enc_find("binary");
|