tiny_tds 1.0.4 → 3.2.0
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.
- checksums.yaml +5 -5
- data/.codeclimate.yml +20 -0
- data/.gitattributes +1 -0
- data/.github/workflows/ci.yml +590 -0
- data/.gitignore +2 -0
- data/.rubocop.yml +31 -0
- data/{CHANGELOG → CHANGELOG.md} +133 -26
- data/Gemfile +1 -5
- data/ISSUE_TEMPLATE.md +36 -3
- data/README.md +147 -85
- data/Rakefile +51 -94
- data/VERSION +1 -1
- data/docker-compose.yml +34 -0
- data/ext/tiny_tds/client.c +149 -67
- data/ext/tiny_tds/client.h +11 -5
- data/ext/tiny_tds/extconf.rb +144 -283
- data/ext/tiny_tds/extconsts.rb +4 -11
- data/ext/tiny_tds/result.c +68 -50
- data/ext/tiny_tds/tiny_tds_ext.c +4 -1
- data/lib/tiny_tds/bin.rb +44 -40
- data/lib/tiny_tds/client.rb +63 -55
- data/lib/tiny_tds/error.rb +0 -3
- data/lib/tiny_tds/gem.rb +23 -0
- data/lib/tiny_tds/result.rb +0 -3
- data/lib/tiny_tds.rb +37 -32
- data/{ports/patches/freetds/1.00 → patches/freetds/1.00.27}/0001-mingw_missing_inet_pton.diff +4 -4
- 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/setup_cimgruby_dev.sh +25 -0
- data/start_dev.sh +21 -0
- data/tasks/native_gem.rake +16 -0
- data/tasks/package.rake +6 -0
- data/tasks/ports.rake +24 -0
- data/tasks/test.rake +7 -0
- data/test/bin/install-freetds.sh +18 -0
- data/test/bin/install-mssql.ps1 +42 -0
- data/test/bin/install-mssqltools.sh +9 -0
- data/test/bin/install-openssl.sh +18 -0
- data/test/bin/restore-from-native-gem.ps1 +10 -0
- data/test/bin/setup_tinytds_db.sh +7 -0
- data/test/bin/setup_volume_permissions.sh +10 -0
- data/test/client_test.rb +161 -112
- data/test/gem_test.rb +100 -0
- data/test/result_test.rb +293 -313
- data/test/schema_test.rb +369 -395
- data/test/sql/db-create.sql +18 -0
- data/test/sql/db-login.sql +38 -0
- data/test/test_helper.rb +116 -85
- data/test/thread_test.rb +22 -31
- data/tiny_tds.gemspec +27 -24
- metadata +109 -56
- data/appveyor.yml +0 -51
- data/test/appveyor/dbsetup.ps1 +0 -27
- data/test/appveyor/dbsetup.sql +0 -9
- data/test/benchmark/query.rb +0 -77
- data/test/benchmark/query_odbc.rb +0 -106
- data/test/benchmark/query_tinytds.rb +0 -126
- data/test/schema/sqlserver_2000.sql +0 -140
- data/test/schema/sqlserver_2005.sql +0 -140
- data/test/schema/sqlserver_2014.sql +0 -140
- data/test/schema/sybase_ase.sql +0 -138
- /data/bin/{defncopy → defncopy-ttds} +0 -0
- /data/bin/{tsql → tsql-ttds} +0 -0
- /data/test/schema/{sqlserver_2008.sql → sqlserver_2017.sql} +0 -0
data/ext/tiny_tds/result.c
CHANGED
@@ -6,10 +6,10 @@
|
|
6
6
|
|
7
7
|
VALUE cTinyTdsResult;
|
8
8
|
extern VALUE mTinyTds, cTinyTdsClient, cTinyTdsError;
|
9
|
-
VALUE
|
9
|
+
VALUE cKernel, cDate;
|
10
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, intern_bigd;
|
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
|
|
@@ -86,25 +86,40 @@ static void dbcancel_ubf(DBPROCESS *client) {
|
|
86
86
|
static void nogvl_setup(DBPROCESS *client) {
|
87
87
|
GET_CLIENT_USERDATA(client);
|
88
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;
|
89
92
|
}
|
90
93
|
|
91
94
|
static void nogvl_cleanup(DBPROCESS *client) {
|
92
95
|
GET_CLIENT_USERDATA(client);
|
93
96
|
userdata->nonblocking = 0;
|
97
|
+
userdata->timing_out = 0;
|
94
98
|
/*
|
95
99
|
Now that the blocking operation is done, we can finally throw any
|
96
100
|
exceptions based on errors from SQL Server.
|
97
101
|
*/
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
userdata->
|
106
|
-
|
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);
|
107
118
|
}
|
119
|
+
|
120
|
+
free(userdata->nonblocking_errors);
|
121
|
+
userdata->nonblocking_errors_length = 0;
|
122
|
+
userdata->nonblocking_errors_size = 0;
|
108
123
|
}
|
109
124
|
|
110
125
|
static RETCODE nogvl_dbsqlok(DBPROCESS *client) {
|
@@ -227,7 +242,7 @@ static VALUE rb_tinytds_result_fetch_row(VALUE self, ID timezone, int symbolize_
|
|
227
242
|
int data_slength = (int)data_info->precision + (int)data_info->scale + 1;
|
228
243
|
char converted_decimal[data_slength];
|
229
244
|
dbconvert(rwrap->client, coltype, data, data_len, SYBVARCHAR, (BYTE *)converted_decimal, -1);
|
230
|
-
val = rb_funcall(
|
245
|
+
val = rb_funcall(cKernel, intern_bigd, 1, rb_str_new2((char *)converted_decimal));
|
231
246
|
break;
|
232
247
|
}
|
233
248
|
case SYBFLT8: {
|
@@ -245,7 +260,7 @@ static VALUE rb_tinytds_result_fetch_row(VALUE self, ID timezone, int symbolize_
|
|
245
260
|
char converted_money[25];
|
246
261
|
long long money_value = ((long long)money->mnyhigh << 32) | money->mnylow;
|
247
262
|
sprintf(converted_money, "%" LONG_LONG_FORMAT, money_value);
|
248
|
-
val = rb_funcall(
|
263
|
+
val = rb_funcall(cKernel, intern_bigd, 2, rb_str_new2(converted_money), opt_four);
|
249
264
|
val = rb_funcall(val, intern_divide, 1, opt_tenk);
|
250
265
|
break;
|
251
266
|
}
|
@@ -253,7 +268,7 @@ static VALUE rb_tinytds_result_fetch_row(VALUE self, ID timezone, int symbolize_
|
|
253
268
|
DBMONEY4 *money = (DBMONEY4 *)data;
|
254
269
|
char converted_money[20];
|
255
270
|
sprintf(converted_money, "%f", money->mny4 / 10000.0);
|
256
|
-
val = rb_funcall(
|
271
|
+
val = rb_funcall(cKernel, intern_bigd, 1, rb_str_new2(converted_money));
|
257
272
|
break;
|
258
273
|
}
|
259
274
|
case SYBBINARY:
|
@@ -283,48 +298,49 @@ static VALUE rb_tinytds_result_fetch_row(VALUE self, ID timezone, int symbolize_
|
|
283
298
|
}
|
284
299
|
break;
|
285
300
|
}
|
286
|
-
case
|
287
|
-
case
|
288
|
-
case
|
289
|
-
case
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
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
|
-
long long numerator = ((long)dr2.second * (long long)1000000000) + (long long)dr2.nanosecond;
|
311
|
-
VALUE rational_sec = rb_Rational(LL2NUM(numerator), opt_onebil);
|
312
|
-
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));
|
313
|
-
break;
|
314
|
-
}
|
315
|
-
}
|
316
|
-
} else {
|
317
|
-
val = ENCODED_STR_NEW(data, data_len);
|
301
|
+
case SYBMSDATE:
|
302
|
+
case SYBMSTIME:
|
303
|
+
case SYBMSDATETIME2:
|
304
|
+
case SYBMSDATETIMEOFFSET: {
|
305
|
+
DBDATEREC2 dr2;
|
306
|
+
dbanydatecrack(rwrap->client, &dr2, coltype, data);
|
307
|
+
switch(coltype) {
|
308
|
+
case SYBMSDATE: {
|
309
|
+
val = rb_funcall(cDate, intern_new, 3, INT2NUM(dr2.year), INT2NUM(dr2.month), INT2NUM(dr2.day));
|
310
|
+
break;
|
318
311
|
}
|
319
|
-
|
320
|
-
|
321
|
-
|
312
|
+
case SYBMSTIME: {
|
313
|
+
VALUE rational_nsec = rb_Rational(INT2NUM(dr2.nanosecond), opt_onek);
|
314
|
+
val = rb_funcall(rb_cTime, timezone, 7, INT2NUM(1900), INT2NUM(1), INT2NUM(1), INT2NUM(dr2.hour), INT2NUM(dr2.minute), INT2NUM(dr2.second), rational_nsec);
|
315
|
+
break;
|
316
|
+
}
|
317
|
+
case SYBMSDATETIME2: {
|
318
|
+
VALUE rational_nsec = rb_Rational(INT2NUM(dr2.nanosecond), opt_onek);
|
319
|
+
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);
|
320
|
+
break;
|
321
|
+
}
|
322
|
+
case SYBMSDATETIMEOFFSET: {
|
323
|
+
long long numerator = ((long)dr2.second * (long long)1000000000) + (long long)dr2.nanosecond;
|
324
|
+
VALUE rational_sec = rb_Rational(LL2NUM(numerator), opt_onebil);
|
325
|
+
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));
|
326
|
+
break;
|
327
|
+
}
|
328
|
+
}
|
322
329
|
break;
|
323
330
|
}
|
324
331
|
case SYBCHAR:
|
325
332
|
case SYBTEXT:
|
326
333
|
val = ENCODED_STR_NEW(data, data_len);
|
327
334
|
break;
|
335
|
+
case 98: { // SYBVARIANT
|
336
|
+
if (data_len == 4) {
|
337
|
+
val = INT2NUM(*(DBINT *)data);
|
338
|
+
break;
|
339
|
+
} else {
|
340
|
+
val = ENCODED_STR_NEW(data, data_len);
|
341
|
+
break;
|
342
|
+
}
|
343
|
+
}
|
328
344
|
default:
|
329
345
|
val = ENCODED_STR_NEW(data, data_len);
|
330
346
|
break;
|
@@ -556,10 +572,11 @@ static VALUE rb_tinytds_result_insert(VALUE self) {
|
|
556
572
|
|
557
573
|
void init_tinytds_result() {
|
558
574
|
/* Data Classes */
|
559
|
-
|
575
|
+
cKernel = rb_const_get(rb_cObject, rb_intern("Kernel"));
|
560
576
|
cDate = rb_const_get(rb_cObject, rb_intern("Date"));
|
561
577
|
/* Define TinyTds::Result */
|
562
578
|
cTinyTdsResult = rb_define_class_under(mTinyTds, "Result", rb_cObject);
|
579
|
+
rb_undef_alloc_func(cTinyTdsResult);
|
563
580
|
/* Define TinyTds::Result Public Methods */
|
564
581
|
rb_define_method(cTinyTdsResult, "fields", rb_tinytds_result_fields, 0);
|
565
582
|
rb_define_method(cTinyTdsResult, "each", rb_tinytds_result_each, -1);
|
@@ -578,6 +595,7 @@ void init_tinytds_result() {
|
|
578
595
|
intern_new_offset = rb_intern("new_offset");
|
579
596
|
intern_plus = rb_intern("+");
|
580
597
|
intern_divide = rb_intern("/");
|
598
|
+
intern_bigd = rb_intern("BigDecimal");
|
581
599
|
/* Symbol Helpers */
|
582
600
|
sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
|
583
601
|
sym_as = ID2SYM(rb_intern("as"));
|
data/ext/tiny_tds/tiny_tds_ext.c
CHANGED
data/lib/tiny_tds/bin.rb
CHANGED
@@ -1,86 +1,90 @@
|
|
1
|
-
require_relative
|
2
|
-
|
1
|
+
require_relative "version"
|
2
|
+
require_relative "gem"
|
3
|
+
require "shellwords"
|
3
4
|
|
4
5
|
module TinyTds
|
5
6
|
class Bin
|
6
|
-
|
7
|
-
ROOT = File.expand_path '../../..', __FILE__
|
8
|
-
PATHS = ENV['PATH'].split File::PATH_SEPARATOR
|
9
|
-
EXTS = (ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']) | ['.exe']
|
10
|
-
|
11
7
|
attr_reader :name
|
12
8
|
|
13
9
|
class << self
|
14
|
-
|
15
10
|
def exe(name, *args)
|
16
11
|
bin = new(name)
|
17
|
-
puts bin.info
|
12
|
+
puts bin.info unless args.any? { |x| x == "-q" }
|
18
13
|
bin.run(*args)
|
19
14
|
end
|
20
|
-
|
21
15
|
end
|
22
16
|
|
23
17
|
def initialize(name)
|
18
|
+
@root = Gem.root_path
|
19
|
+
@exts = (ENV["PATHEXT"] ? ENV["PATHEXT"].split(";") : [""]) | [".exe"]
|
20
|
+
|
24
21
|
@name = name
|
25
22
|
@binstub = find_bin
|
26
23
|
@exefile = find_exe
|
27
24
|
end
|
28
25
|
|
29
26
|
def run(*args)
|
30
|
-
|
31
|
-
|
32
|
-
|
27
|
+
with_ports_paths do
|
28
|
+
return nil unless path
|
29
|
+
Kernel.system Shellwords.join(args.unshift(path))
|
30
|
+
$CHILD_STATUS.to_i
|
31
|
+
end
|
33
32
|
end
|
34
33
|
|
35
34
|
def path
|
36
|
-
|
37
|
-
@path = @exefile && File.exists?(@exefile) ? @exefile : which
|
35
|
+
@path ||= (@exefile && File.exist?(@exefile)) ? @exefile : which
|
38
36
|
end
|
39
37
|
|
40
38
|
def info
|
41
39
|
"[TinyTds][v#{TinyTds::VERSION}][#{name}]: #{path}"
|
42
40
|
end
|
43
41
|
|
44
|
-
|
45
42
|
private
|
46
43
|
|
44
|
+
def search_paths
|
45
|
+
ENV["PATH"].split File::PATH_SEPARATOR
|
46
|
+
end
|
47
|
+
|
48
|
+
def with_ports_paths
|
49
|
+
old_path = ENV["PATH"]
|
50
|
+
|
51
|
+
begin
|
52
|
+
ENV["PATH"] = [
|
53
|
+
Gem.ports_bin_paths,
|
54
|
+
old_path
|
55
|
+
].flatten.join File::PATH_SEPARATOR
|
56
|
+
|
57
|
+
yield if block_given?
|
58
|
+
ensure
|
59
|
+
ENV["PATH"] = old_path
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
47
63
|
def find_bin
|
48
|
-
File.join
|
64
|
+
File.join @root, "bin", name
|
49
65
|
end
|
50
66
|
|
51
67
|
def find_exe
|
52
|
-
|
53
|
-
|
54
|
-
|
68
|
+
Gem.ports_bin_paths.each do |bin|
|
69
|
+
@exts.each do |ext|
|
70
|
+
f = File.join bin, "#{name}#{ext}"
|
71
|
+
return f if File.exist?(f)
|
72
|
+
end
|
55
73
|
end
|
56
74
|
nil
|
57
75
|
end
|
58
76
|
|
59
77
|
def which
|
60
|
-
|
61
|
-
|
62
|
-
exe = File.expand_path File.join(path, "#{name}#{ext}"),
|
78
|
+
search_paths.each do |path|
|
79
|
+
@exts.each do |ext|
|
80
|
+
exe = File.expand_path File.join(path, "#{name}#{ext}"), @root
|
63
81
|
next if exe == @binstub
|
64
|
-
next
|
65
|
-
|
82
|
+
next unless File.executable?(exe)
|
83
|
+
|
66
84
|
return exe
|
67
85
|
end
|
68
86
|
end
|
69
|
-
|
70
|
-
end
|
71
|
-
|
72
|
-
# Implementation directly copied from ptools.
|
73
|
-
# https://github.com/djberg96/ptools
|
74
|
-
# https://opensource.org/licenses/Artistic-2.0
|
75
|
-
#
|
76
|
-
def binary?(file)
|
77
|
-
bytes = File.stat(file).blksize
|
78
|
-
return false unless bytes
|
79
|
-
bytes = 4096 if bytes > 4096
|
80
|
-
s = (File.read(file, bytes) || "")
|
81
|
-
s = s.encode('US-ASCII', :undef => :replace).split(//)
|
82
|
-
((s.size - s.grep(" ".."~").size) / s.size.to_f) > 0.30
|
87
|
+
nil
|
83
88
|
end
|
84
|
-
|
85
89
|
end
|
86
90
|
end
|
data/lib/tiny_tds/client.rb
CHANGED
@@ -1,21 +1,18 @@
|
|
1
1
|
module TinyTds
|
2
2
|
class Client
|
3
|
-
|
4
|
-
|
5
|
-
:
|
6
|
-
:
|
7
|
-
:
|
8
|
-
:
|
9
|
-
:empty_sets => true
|
3
|
+
@default_query_options = {
|
4
|
+
as: :hash,
|
5
|
+
symbolize_keys: false,
|
6
|
+
cache_rows: true,
|
7
|
+
timezone: :local,
|
8
|
+
empty_sets: true
|
10
9
|
}
|
11
10
|
|
12
11
|
attr_reader :query_options
|
12
|
+
attr_reader :message_handler
|
13
13
|
|
14
14
|
class << self
|
15
|
-
|
16
|
-
def default_query_options
|
17
|
-
@@default_query_options
|
18
|
-
end
|
15
|
+
attr_reader :default_query_options
|
19
16
|
|
20
17
|
# Most, if not all, iconv encoding names can be found by ruby. Just in case, you can
|
21
18
|
# overide this method to return a string name that Encoding.find would work with. Default
|
@@ -25,21 +22,38 @@ module TinyTds
|
|
25
22
|
encoding
|
26
23
|
end
|
27
24
|
|
25
|
+
def local_offset
|
26
|
+
::Time.local(2010).utc_offset.to_r / 86_400
|
27
|
+
end
|
28
28
|
end
|
29
29
|
|
30
|
+
# rubocop:disable Metrics/AbcSize
|
31
|
+
# rubocop:disable Metrics/MethodLength
|
32
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
33
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
34
|
+
def initialize(opts = {})
|
35
|
+
if opts[:dataserver].to_s.empty? && opts[:host].to_s.empty?
|
36
|
+
raise ArgumentError, "missing :host option if no :dataserver given"
|
37
|
+
end
|
38
|
+
|
39
|
+
@message_handler = opts[:message_handler]
|
40
|
+
if @message_handler && !@message_handler.respond_to?(:call)
|
41
|
+
raise ArgumentError, ":message_handler must implement `call` (eg, a Proc or a Method)"
|
42
|
+
end
|
30
43
|
|
31
|
-
def initialize(opts={})
|
32
|
-
raise ArgumentError, 'missing :host option if no :dataserver given' if opts[:dataserver].to_s.empty? && opts[:host].to_s.empty?
|
33
44
|
opts[:username] = parse_username(opts)
|
34
|
-
@query_options =
|
35
|
-
opts[:password] = opts[:password].to_s if opts[:password] && opts[:password].to_s.strip !=
|
36
|
-
opts[:appname] ||=
|
45
|
+
@query_options = self.class.default_query_options.dup
|
46
|
+
opts[:password] = opts[:password].to_s if opts[:password] && opts[:password].to_s.strip != ""
|
47
|
+
opts[:appname] ||= "TinyTds"
|
37
48
|
opts[:tds_version] = tds_versions_setter(opts)
|
49
|
+
opts[:use_utf16] = opts[:use_utf16].nil? || ["true", "1", "yes"].include?(opts[:use_utf16].to_s)
|
38
50
|
opts[:login_timeout] ||= 60
|
39
51
|
opts[:timeout] ||= 5
|
40
|
-
opts[:encoding] = (opts[:encoding].nil? || opts[:encoding].
|
52
|
+
opts[:encoding] = (opts[:encoding].nil? || opts[:encoding].casecmp("utf8").zero?) ? "UTF-8" : opts[:encoding].upcase
|
41
53
|
opts[:port] ||= 1433
|
42
54
|
opts[:dataserver] = "#{opts[:host]}:#{opts[:port]}" if opts[:dataserver].to_s.empty?
|
55
|
+
forced_integer_keys = [:login_timeout, :port, :timeout]
|
56
|
+
forced_integer_keys.each { |k| opts[k] = opts[k].to_i if opts[k] }
|
43
57
|
connect(opts)
|
44
58
|
end
|
45
59
|
|
@@ -56,13 +70,8 @@ module TinyTds
|
|
56
70
|
!closed? && !dead?
|
57
71
|
end
|
58
72
|
|
59
|
-
|
60
73
|
private
|
61
74
|
|
62
|
-
def self.local_offset
|
63
|
-
::Time.local(2010).utc_offset.to_r / 86400
|
64
|
-
end
|
65
|
-
|
66
75
|
def parse_username(opts)
|
67
76
|
host = opts[:host]
|
68
77
|
username = opts[:username]
|
@@ -70,11 +79,11 @@ module TinyTds
|
|
70
79
|
return username if username.include?("@") && !username.include?("database.windows.net")
|
71
80
|
user, domain = username.split("@")
|
72
81
|
domain ||= host
|
73
|
-
"#{user}@#{domain.split(
|
82
|
+
"#{user}@#{domain.split(".").first}"
|
74
83
|
end
|
75
84
|
|
76
|
-
def tds_versions_setter(opts={})
|
77
|
-
v = opts[:tds_version] || ENV[
|
85
|
+
def tds_versions_setter(opts = {})
|
86
|
+
v = opts[:tds_version] || ENV["TDSVER"] || "7.3"
|
78
87
|
TDS_VERSIONS_SETTERS[v.to_s]
|
79
88
|
end
|
80
89
|
|
@@ -82,22 +91,22 @@ module TinyTds
|
|
82
91
|
# DBVERSION_xxx are used with dbsetversion()
|
83
92
|
#
|
84
93
|
TDS_VERSIONS_SETTERS = {
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
94
|
+
"unknown" => 0,
|
95
|
+
"46" => 1,
|
96
|
+
"100" => 2,
|
97
|
+
"42" => 3,
|
98
|
+
"70" => 4,
|
99
|
+
"7.0" => 4,
|
100
|
+
"71" => 5,
|
101
|
+
"7.1" => 5,
|
102
|
+
"80" => 5,
|
103
|
+
"8.0" => 5,
|
104
|
+
"72" => 6,
|
105
|
+
"7.2" => 6,
|
106
|
+
"90" => 6,
|
107
|
+
"9.0" => 6,
|
108
|
+
"73" => 7,
|
109
|
+
"7.3" => 7
|
101
110
|
}.freeze
|
102
111
|
|
103
112
|
# From sybdb.h comments:
|
@@ -105,20 +114,19 @@ module TinyTds
|
|
105
114
|
# The integer values of the constants are poorly chosen.
|
106
115
|
#
|
107
116
|
TDS_VERSIONS_GETTERS = {
|
108
|
-
0
|
109
|
-
1
|
110
|
-
2
|
111
|
-
3
|
112
|
-
4
|
113
|
-
5
|
114
|
-
6
|
115
|
-
7
|
116
|
-
8
|
117
|
-
9
|
118
|
-
10 => {:
|
119
|
-
11 => {:
|
120
|
-
12 => {:
|
117
|
+
0 => {name: "DBTDS_UNKNOWN", description: "Unknown"},
|
118
|
+
1 => {name: "DBTDS_2_0", description: "Pre 4.0 SQL Server"},
|
119
|
+
2 => {name: "DBTDS_3_4", description: "Microsoft SQL Server (3.0)"},
|
120
|
+
3 => {name: "DBTDS_4_0", description: "4.0 SQL Server"},
|
121
|
+
4 => {name: "DBTDS_4_2", description: "4.2 SQL Server"},
|
122
|
+
5 => {name: "DBTDS_4_6", description: "2.0 OpenServer and 4.6 SQL Server."},
|
123
|
+
6 => {name: "DBTDS_4_9_5", description: "4.9.5 (NCR) SQL Server"},
|
124
|
+
7 => {name: "DBTDS_5_0", description: "5.0 SQL Server"},
|
125
|
+
8 => {name: "DBTDS_7_0", description: "Microsoft SQL Server 7.0"},
|
126
|
+
9 => {name: "DBTDS_7_1/DBTDS_8_0", description: "Microsoft SQL Server 2000"},
|
127
|
+
10 => {name: "DBTDS_7_2/DBTDS_9_0", description: "Microsoft SQL Server 2005"},
|
128
|
+
11 => {name: "DBTDS_7_3", description: "Microsoft SQL Server 2008"},
|
129
|
+
12 => {name: "DBTDS_7_4", description: "Microsoft SQL Server 2012/2014"}
|
121
130
|
}.freeze
|
122
|
-
|
123
131
|
end
|
124
132
|
end
|
data/lib/tiny_tds/error.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
module TinyTds
|
2
2
|
class Error < StandardError
|
3
|
-
|
4
3
|
attr_accessor :source, :severity, :db_error_number, :os_error_number
|
5
4
|
|
6
5
|
def initialize(message)
|
@@ -9,7 +8,5 @@ module TinyTds
|
|
9
8
|
@db_error_number = nil
|
10
9
|
@os_error_number = nil
|
11
10
|
end
|
12
|
-
|
13
|
-
|
14
11
|
end
|
15
12
|
end
|
data/lib/tiny_tds/gem.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require "rbconfig"
|
2
|
+
|
3
|
+
module TinyTds
|
4
|
+
module Gem
|
5
|
+
class << self
|
6
|
+
def root_path
|
7
|
+
File.expand_path "../../..", __FILE__
|
8
|
+
end
|
9
|
+
|
10
|
+
def ports_root_path
|
11
|
+
File.join(root_path, "ports")
|
12
|
+
end
|
13
|
+
|
14
|
+
def ports_bin_paths
|
15
|
+
Dir.glob(File.join(ports_root_path, "**", "bin"))
|
16
|
+
end
|
17
|
+
|
18
|
+
def ports_lib_paths
|
19
|
+
Dir.glob(File.join(ports_root_path, "**", "lib"))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/tiny_tds/result.rb
CHANGED
data/lib/tiny_tds.rb
CHANGED
@@ -1,37 +1,42 @@
|
|
1
|
-
|
2
|
-
require
|
3
|
-
require 'bigdecimal'
|
4
|
-
require 'rational'
|
1
|
+
require "date"
|
2
|
+
require "bigdecimal"
|
5
3
|
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
4
|
+
require "tiny_tds/version"
|
5
|
+
require "tiny_tds/error"
|
6
|
+
require "tiny_tds/client"
|
7
|
+
require "tiny_tds/result"
|
8
|
+
require "tiny_tds/gem"
|
10
9
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
Fiddle.dlopen(lib)
|
10
|
+
module TinyTds
|
11
|
+
# Is this file part of a fat binary gem with bundled freetds?
|
12
|
+
# This path must be enabled by add_dll_directory on Windows.
|
13
|
+
gplat = ::Gem::Platform.local
|
14
|
+
FREETDS_LIB_PATH = Dir[File.expand_path("../ports/#{gplat.cpu}-#{gplat.os}*/lib", __dir__)].first
|
15
|
+
|
16
|
+
add_dll_path = proc do |path, &block|
|
17
|
+
if RUBY_PLATFORM =~ /(mswin|mingw)/i && path
|
18
|
+
begin
|
19
|
+
require "ruby_installer/runtime"
|
20
|
+
RubyInstaller::Runtime.add_dll_directory(path, &block)
|
21
|
+
rescue LoadError
|
22
|
+
old_path = ENV["PATH"]
|
23
|
+
ENV["PATH"] = "#{path};#{old_path}"
|
24
|
+
block.call
|
25
|
+
ENV["PATH"] = old_path
|
26
|
+
end
|
27
|
+
else
|
28
|
+
# libsybdb is found by a relative rpath in the cross compiled extension dll
|
29
|
+
# or by the system library loader
|
30
|
+
block.call
|
31
|
+
end
|
34
32
|
end
|
35
33
|
|
36
|
-
|
34
|
+
add_dll_path.call(FREETDS_LIB_PATH) do
|
35
|
+
# Try the <major>.<minor> subdirectory for fat binary gems
|
36
|
+
major_minor = RUBY_VERSION[/^(\d+\.\d+)/] or
|
37
|
+
raise "Oops, can't extract the major/minor version from #{RUBY_VERSION.dump}"
|
38
|
+
require "tiny_tds/#{major_minor}/tiny_tds"
|
39
|
+
rescue LoadError
|
40
|
+
require "tiny_tds/tiny_tds"
|
41
|
+
end
|
37
42
|
end
|
data/{ports/patches/freetds/1.00 → patches/freetds/1.00.27}/0001-mingw_missing_inet_pton.diff
RENAMED
@@ -1,16 +1,16 @@
|
|
1
1
|
diff --git a/src/tds/tls.c b/src/tds/tls.c
|
2
|
-
index
|
2
|
+
index 09e7fa0..1da18f6 100644
|
3
3
|
--- a/src/tds/tls.c
|
4
4
|
+++ b/src/tds/tls.c
|
5
|
-
@@ -
|
6
|
-
#define SSL_PTR bio
|
5
|
+
@@ -101,6 +101,29 @@
|
6
|
+
#define SSL_PTR BIO_get_data(bio)
|
7
7
|
#endif
|
8
8
|
|
9
9
|
+/*
|
10
10
|
+ * Add a workaround for older Mingw versions without inet_pton().
|
11
11
|
+ * This means RubyInstallers DevKit-4.7.2 in particular.
|
12
12
|
+ */
|
13
|
-
+#if defined(
|
13
|
+
+#if defined(__MINGW32__) && !defined(InetPtonA)
|
14
14
|
+ #include <windows.h>
|
15
15
|
+
|
16
16
|
+ static HMODULE ws2_32 = NULL;
|