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.
Files changed (64) hide show
  1. checksums.yaml +5 -5
  2. data/.codeclimate.yml +20 -0
  3. data/.gitattributes +1 -0
  4. data/.github/workflows/ci.yml +590 -0
  5. data/.gitignore +2 -0
  6. data/.rubocop.yml +31 -0
  7. data/{CHANGELOG → CHANGELOG.md} +133 -26
  8. data/Gemfile +1 -5
  9. data/ISSUE_TEMPLATE.md +36 -3
  10. data/README.md +147 -85
  11. data/Rakefile +51 -94
  12. data/VERSION +1 -1
  13. data/docker-compose.yml +34 -0
  14. data/ext/tiny_tds/client.c +149 -67
  15. data/ext/tiny_tds/client.h +11 -5
  16. data/ext/tiny_tds/extconf.rb +144 -283
  17. data/ext/tiny_tds/extconsts.rb +4 -11
  18. data/ext/tiny_tds/result.c +68 -50
  19. data/ext/tiny_tds/tiny_tds_ext.c +4 -1
  20. data/lib/tiny_tds/bin.rb +44 -40
  21. data/lib/tiny_tds/client.rb +63 -55
  22. data/lib/tiny_tds/error.rb +0 -3
  23. data/lib/tiny_tds/gem.rb +23 -0
  24. data/lib/tiny_tds/result.rb +0 -3
  25. data/lib/tiny_tds.rb +37 -32
  26. data/{ports/patches/freetds/1.00 → patches/freetds/1.00.27}/0001-mingw_missing_inet_pton.diff +4 -4
  27. data/patches/freetds/1.00.27/0002-Don-t-use-MSYS2-file-libws2_32.diff +28 -0
  28. data/patches/libiconv/1.14/1-avoid-gets-error.patch +17 -0
  29. data/setup_cimgruby_dev.sh +25 -0
  30. data/start_dev.sh +21 -0
  31. data/tasks/native_gem.rake +16 -0
  32. data/tasks/package.rake +6 -0
  33. data/tasks/ports.rake +24 -0
  34. data/tasks/test.rake +7 -0
  35. data/test/bin/install-freetds.sh +18 -0
  36. data/test/bin/install-mssql.ps1 +42 -0
  37. data/test/bin/install-mssqltools.sh +9 -0
  38. data/test/bin/install-openssl.sh +18 -0
  39. data/test/bin/restore-from-native-gem.ps1 +10 -0
  40. data/test/bin/setup_tinytds_db.sh +7 -0
  41. data/test/bin/setup_volume_permissions.sh +10 -0
  42. data/test/client_test.rb +161 -112
  43. data/test/gem_test.rb +100 -0
  44. data/test/result_test.rb +293 -313
  45. data/test/schema_test.rb +369 -395
  46. data/test/sql/db-create.sql +18 -0
  47. data/test/sql/db-login.sql +38 -0
  48. data/test/test_helper.rb +116 -85
  49. data/test/thread_test.rb +22 -31
  50. data/tiny_tds.gemspec +27 -24
  51. metadata +109 -56
  52. data/appveyor.yml +0 -51
  53. data/test/appveyor/dbsetup.ps1 +0 -27
  54. data/test/appveyor/dbsetup.sql +0 -9
  55. data/test/benchmark/query.rb +0 -77
  56. data/test/benchmark/query_odbc.rb +0 -106
  57. data/test/benchmark/query_tinytds.rb +0 -126
  58. data/test/schema/sqlserver_2000.sql +0 -140
  59. data/test/schema/sqlserver_2005.sql +0 -140
  60. data/test/schema/sqlserver_2014.sql +0 -140
  61. data/test/schema/sybase_ase.sql +0 -138
  62. /data/bin/{defncopy → defncopy-ttds} +0 -0
  63. /data/bin/{tsql → tsql-ttds} +0 -0
  64. /data/test/schema/{sqlserver_2008.sql → sqlserver_2017.sql} +0 -0
@@ -6,10 +6,10 @@
6
6
 
7
7
  VALUE cTinyTdsResult;
8
8
  extern VALUE mTinyTds, cTinyTdsClient, cTinyTdsError;
9
- VALUE cBigDecimal, cDate;
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
- if (userdata->nonblocking_error.is_set) {
99
- userdata->nonblocking_error.is_set = 0;
100
- rb_tinytds_raise_error(client,
101
- userdata->nonblocking_error.cancel,
102
- userdata->nonblocking_error.error,
103
- userdata->nonblocking_error.source,
104
- userdata->nonblocking_error.severity,
105
- userdata->nonblocking_error.dberr,
106
- userdata->nonblocking_error.oserr);
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(cBigDecimal, intern_new, 1, rb_str_new2((char *)converted_decimal));
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(cBigDecimal, intern_new, 2, rb_str_new2(converted_money), opt_four);
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(cBigDecimal, intern_new, 1, rb_str_new2(converted_money));
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 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
- 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
- #else
320
- val = ENCODED_STR_NEW(data, data_len);
321
- #endif
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
- cBigDecimal = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
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"));
@@ -1,6 +1,9 @@
1
-
2
1
  #include <tiny_tds_ext.h>
3
2
 
3
+ #ifndef DBVERSION_73
4
+ #error "DBVERSION_73 is not defined. Aborting compilation."
5
+ #endif
6
+
4
7
  VALUE mTinyTds, cTinyTdsError;
5
8
 
6
9
  void Init_tiny_tds() {
data/lib/tiny_tds/bin.rb CHANGED
@@ -1,86 +1,90 @@
1
- require_relative './version'
2
- require 'shellwords'
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
- return nil unless path
31
- Kernel.system Shellwords.join(args.unshift(path))
32
- $?.to_i
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
- return @path if defined?(@path)
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 ROOT, 'bin', name
64
+ File.join @root, "bin", name
49
65
  end
50
66
 
51
67
  def find_exe
52
- EXTS.each do |ext|
53
- f = File.join ROOT, 'exe', "#{name}#{ext}"
54
- return f if File.exists?(f)
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
- PATHS.each do |path|
61
- EXTS.each do |ext|
62
- exe = File.expand_path File.join(path, "#{name}#{ext}"), ROOT
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 if !File.executable?(exe)
65
- next if !binary?(exe)
82
+ next unless File.executable?(exe)
83
+
66
84
  return exe
67
85
  end
68
86
  end
69
- return nil
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
@@ -1,21 +1,18 @@
1
1
  module TinyTds
2
2
  class Client
3
-
4
- @@default_query_options = {
5
- :as => :hash,
6
- :symbolize_keys => false,
7
- :cache_rows => true,
8
- :timezone => :local,
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 = @@default_query_options.dup
35
- opts[:password] = opts[:password].to_s if opts[:password] && opts[:password].to_s.strip != ''
36
- opts[:appname] ||= 'TinyTds'
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].downcase == 'utf8') ? 'UTF-8' : opts[:encoding].upcase
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('.').first}"
82
+ "#{user}@#{domain.split(".").first}"
74
83
  end
75
84
 
76
- def tds_versions_setter(opts={})
77
- v = opts[:tds_version] || ENV['TDSVER'] || '7.3'
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
- 'unknown' => 0,
86
- '46' => 1,
87
- '100' => 2,
88
- '42' => 3,
89
- '70' => 4,
90
- '7.0' => 4,
91
- '71' => 5,
92
- '7.1' => 5,
93
- '80' => 5,
94
- '8.0' => 5,
95
- '72' => 6,
96
- '7.2' => 6,
97
- '90' => 6,
98
- '9.0' => 6,
99
- '73' => 7,
100
- '7.3' => 7
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 => {:name => 'DBTDS_UNKNOWN', :description => 'Unknown'},
109
- 1 => {:name => 'DBTDS_2_0', :description => 'Pre 4.0 SQL Server'},
110
- 2 => {:name => 'DBTDS_3_4', :description => 'Microsoft SQL Server (3.0)'},
111
- 3 => {:name => 'DBTDS_4_0', :description => '4.0 SQL Server'},
112
- 4 => {:name => 'DBTDS_4_2', :description => '4.2 SQL Server'},
113
- 5 => {:name => 'DBTDS_4_6', :description => '2.0 OpenServer and 4.6 SQL Server.'},
114
- 6 => {:name => 'DBTDS_4_9_5', :description => '4.9.5 (NCR) SQL Server'},
115
- 7 => {:name => 'DBTDS_5_0', :description => '5.0 SQL Server'},
116
- 8 => {:name => 'DBTDS_7_0', :description => 'Microsoft SQL Server 7.0'},
117
- 9 => {:name => 'DBTDS_7_1/DBTDS_8_0', :description => 'Microsoft SQL Server 2000'},
118
- 10 => {:name => 'DBTDS_7_2/DBTDS_9_0', :description => 'Microsoft SQL Server 2005'},
119
- 11 => {:name => 'DBTDS_7_3', :description => 'Microsoft SQL Server 2008'},
120
- 12 => {:name => 'DBTDS_7_4', :description => 'Microsoft SQL Server 2012/2014'}
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
@@ -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
@@ -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
@@ -1,8 +1,5 @@
1
1
  module TinyTds
2
2
  class Result
3
-
4
3
  include Enumerable
5
-
6
-
7
4
  end
8
5
  end
data/lib/tiny_tds.rb CHANGED
@@ -1,37 +1,42 @@
1
- # encoding: UTF-8
2
- require 'date'
3
- require 'bigdecimal'
4
- require 'rational'
1
+ require "date"
2
+ require "bigdecimal"
5
3
 
6
- require 'tiny_tds/version'
7
- require 'tiny_tds/error'
8
- require 'tiny_tds/client'
9
- require 'tiny_tds/result'
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
- # Support multiple ruby versions, fat binaries under Windows.
12
- if RUBY_PLATFORM =~ /mingw|mswin/ && RUBY_VERSION =~ /(\d+.\d+)/
13
- ver = $1
14
- # Set the PATH environment variable, so that the DLLs can be found.
15
- old_path = ENV['PATH']
16
- begin
17
- # Do the same host consolidation as in extconf.rb
18
- ports_dir = RbConfig::CONFIG["host"].gsub('i686-pc-mingw32', 'i686-w64-mingw32')
19
- ENV['PATH'] = "#{File.expand_path("../../ports/#{ports_dir}/bin", __FILE__)};#{old_path}"
20
- require "tiny_tds/#{ver}/tiny_tds"
21
- rescue LoadError
22
- require 'tiny_tds/tiny_tds'
23
- ensure
24
- ENV['PATH'] = old_path
25
- end
26
- else
27
- # Load dependent shared libraries into the process, so that they are already present,
28
- # when tiny_tds.so is loaded. This ensures, that shared libraries are loaded even when
29
- # the path is different between build and run time (e.g. Heroku).
30
- ports_libs = File.expand_path("../../ports/#{RbConfig::CONFIG["host"]}/lib/*.so", __FILE__)
31
- Dir[ports_libs].each do |lib|
32
- require "fiddle"
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
- require 'tiny_tds/tiny_tds'
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
@@ -1,16 +1,16 @@
1
1
  diff --git a/src/tds/tls.c b/src/tds/tls.c
2
- index 0d11a33..b8ab2ba 100644
2
+ index 09e7fa0..1da18f6 100644
3
3
  --- a/src/tds/tls.c
4
4
  +++ b/src/tds/tls.c
5
- @@ -72,6 +72,29 @@
6
- #define SSL_PTR bio->ptr
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(__MINGW64_VERSION_MAJOR) && !defined(InetPtonA)
13
+ +#if defined(__MINGW32__) && !defined(InetPtonA)
14
14
  + #include <windows.h>
15
15
  +
16
16
  + static HMODULE ws2_32 = NULL;