tiny_tds 1.0.4 → 2.1.5

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 (51) hide show
  1. checksums.yaml +5 -5
  2. data/.codeclimate.yml +20 -0
  3. data/.gitattributes +1 -0
  4. data/.rubocop.yml +31 -0
  5. data/.travis.yml +25 -0
  6. data/{CHANGELOG → CHANGELOG.md} +102 -26
  7. data/Gemfile +4 -1
  8. data/ISSUE_TEMPLATE.md +35 -2
  9. data/README.md +131 -56
  10. data/Rakefile +31 -88
  11. data/VERSION +1 -1
  12. data/appveyor.yml +38 -17
  13. data/docker-compose.yml +22 -0
  14. data/ext/tiny_tds/client.c +147 -60
  15. data/ext/tiny_tds/client.h +11 -5
  16. data/ext/tiny_tds/extconf.rb +41 -297
  17. data/ext/tiny_tds/extconsts.rb +7 -7
  18. data/ext/tiny_tds/result.c +40 -15
  19. data/lib/tiny_tds/bin.rb +45 -27
  20. data/lib/tiny_tds/client.rb +46 -34
  21. data/lib/tiny_tds/error.rb +0 -1
  22. data/lib/tiny_tds/gem.rb +32 -0
  23. data/lib/tiny_tds/result.rb +2 -3
  24. data/lib/tiny_tds/version.rb +1 -1
  25. data/lib/tiny_tds.rb +38 -14
  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/tasks/native_gem.rake +14 -0
  30. data/tasks/package.rake +8 -0
  31. data/tasks/ports/freetds.rb +37 -0
  32. data/tasks/ports/libiconv.rb +43 -0
  33. data/tasks/ports/openssl.rb +62 -0
  34. data/tasks/ports/recipe.rb +52 -0
  35. data/tasks/ports.rake +85 -0
  36. data/tasks/test.rake +9 -0
  37. data/test/appveyor/dbsetup.ps1 +1 -1
  38. data/test/bin/install-freetds.sh +20 -0
  39. data/test/bin/install-openssl.sh +18 -0
  40. data/test/bin/setup.sh +19 -0
  41. data/test/client_test.rb +124 -66
  42. data/test/gem_test.rb +179 -0
  43. data/test/result_test.rb +128 -42
  44. data/test/schema/sqlserver_2016.sql +140 -0
  45. data/test/schema_test.rb +23 -23
  46. data/test/test_helper.rb +65 -7
  47. data/test/thread_test.rb +1 -1
  48. data/tiny_tds.gemspec +9 -7
  49. metadata +60 -20
  50. /data/bin/{defncopy → defncopy-ttds} +0 -0
  51. /data/bin/{tsql → tsql-ttds} +0 -0
@@ -3,327 +3,71 @@ ENV['RC_ARCHS'] = '' if RUBY_PLATFORM =~ /darwin/
3
3
  # :stopdoc:
4
4
 
5
5
  require 'mkmf'
6
- require 'fileutils'
7
-
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'
6
+ require 'rbconfig'
12
7
  require_relative './extconsts'
13
8
 
14
- OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE
15
-
16
9
  # Shamelessly copied from nokogiri
17
10
  #
18
11
 
19
12
  def do_help
20
13
  print <<HELP
21
14
  usage: ruby #{$0} [options]
22
-
23
- --enable-system-freetds / --disable-system-freetds
24
- --enable-system-iconv / --disable-system-iconv
25
- --enable-system-openssl / --disable-system-openssl
26
- Force use of system or builtin freetds/iconv/openssl library.
27
- Default is to prefer system libraries and fallback to builtin.
28
-
29
15
  --with-freetds-dir=DIR
30
16
  Use the freetds library placed under DIR.
31
-
32
- --enable-lookup
33
- Search for freetds through all paths in the PATH environment variable.
34
-
35
- --disable-openssl
36
- Disable OpenSSL for freetds build. No effect on system-freetds.
37
-
38
- --enable-gnutls
39
- Use GnuTLS instead of OpenSSL for freetds build.
40
-
41
- --enable-cross-build
42
- Do cross-build.
43
17
  HELP
44
18
  exit! 0
45
19
  end
46
20
 
47
21
  do_help if arg_config('--help')
48
22
 
49
- FREETDSDIR = ENV['FREETDS_DIR']
50
-
51
- if FREETDSDIR.nil? || FREETDSDIR.empty?
52
- LIBDIR = RbConfig::CONFIG['libdir']
53
- INCLUDEDIR = RbConfig::CONFIG['includedir']
54
- else
55
- puts "Will use #{FREETDSDIR}"
56
- LIBDIR = "#{FREETDSDIR}/lib"
57
- INCLUDEDIR = "#{FREETDSDIR}/include"
58
- end
59
-
60
- $CFLAGS << " #{ENV["CFLAGS"]}"
61
- $LDFLAGS << " #{ENV["LDFLAGS"]}"
62
- $LIBS << " #{ENV["LIBS"]}"
63
-
64
- SEARCHABLE_PATHS = begin
65
- eop_regexp = /#{File::SEPARATOR}bin$/
66
- paths = ENV['PATH']
67
- paths = paths.gsub(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR
68
- paths = paths.split(File::PATH_SEPARATOR)
69
- bin_paths = paths.select{ |p| p =~ eop_regexp }
70
- bin_paths.map{ |p| p.sub(eop_regexp,'') }.compact.reject{ |p| p.empty? }.uniq
71
- end
72
-
73
- def searchable_paths_with_directories(*directories)
74
- SEARCHABLE_PATHS.map do |path|
75
- directories.map do |paths|
76
- dir = File.join path, *paths
77
- File.directory?(dir) ? dir : nil
78
- end.flatten.compact
79
- end.flatten.compact
80
- end
81
-
82
- class BuildRecipe < MiniPortile
83
- def initialize(name, version, files)
84
- super(name, version)
85
- self.files = files
86
- self.target = File.expand_path('../../../ports', __FILE__)
87
- self.host = consolidated_host(RbConfig::CONFIG["host"])
88
- self.patch_files = Dir[File.join(self.target, "patches", self.name, self.version, "*.diff")].sort
89
- end
90
-
91
- def consolidated_host(name)
92
- # Host name and prefix of build tools are different on Windows 32 bit.
93
- name.gsub('i686-pc-mingw32', 'i686-w64-mingw32')
94
- end
95
-
96
- def configure_defaults
97
- [
98
- "--host=#{host}", # build for specific target (host)
99
- "--disable-static",
100
- "--enable-shared",
101
- ]
102
- end
103
-
104
- # Use the same path for all recipes, so that only one include/lib path is required.
105
- def port_path
106
- "#{target}/#{host}"
107
- end
108
-
109
- # We use the same port_path for all recipes. That breaks the standard installed? method.
110
- def installed?
111
- false
112
- end
113
-
114
- # When using rake-compiler-dock on Windows, the underlying Virtualbox shared
115
- # folders don't support symlinks, but libiconv expects it for a build on
116
- # Linux. We work around this limitation by using the temp dir for cooking.
117
- def chdir_for_build
118
- build_dir = ENV['RCD_HOST_RUBY_PLATFORM'].to_s =~ /mingw|mswin|cygwin/ ? '/tmp' : '.'
119
- Dir.chdir(build_dir) do
120
- yield
121
- end
122
- end
123
-
124
- def cook_and_activate
125
- checkpoint = File.join(self.target, "#{self.name}-#{self.version}-#{self.host}.installed")
126
- unless File.exist?(checkpoint)
127
- chdir_for_build do
128
- self.cook
129
- end
130
- FileUtils.touch checkpoint
131
- end
132
- self.activate
133
- self
134
- end
135
- end
136
-
137
- def define_libssl_recipe(host)
138
- BuildRecipe.new("openssl", OPENSSL_VERSION, [OPENSSL_SOURCE_URI]).tap do |recipe|
139
- class << recipe
140
- def extract_file(file, target)
141
- filename = File.basename(file)
142
- FileUtils.mkdir_p target
143
-
144
- message "Extracting #{filename} into #{target}... "
145
- result = `#{tar_exe} #{tar_compression_switch(filename)}xf "#{file}" -C "#{target}" 2>&1`
146
- if $?.success?
147
- output "OK"
148
- else
149
- # tar on windows returns error exit code, because it can not extract symlinks
150
- output "ERROR (ignored)"
151
- end
152
- end
153
-
154
- def configure
155
- config = if host=~/mingw/
156
- host=~/x86_64/ ? 'mingw64' : 'mingw'
157
- end
158
- args = [ "CFLAGS=-DDSO_WIN32",
159
- "./Configure",
160
- "no-shared",
161
- configure_prefix,
162
- config,
163
- ]
164
- args.unshift("CROSS_COMPILE=#{host}-") if enable_config("cross-build")
165
-
166
- execute "configure", "sh -c \"#{args.join(" ")}\""
167
- end
168
-
169
- def compile
170
- super
171
- # OpenSSL DLLs are called "libeay32.dll" and "ssleay32.dll" per default,
172
- # regardless to the version. This is best suited to meet the Windows DLL hell.
173
- # To avoid any conflicts we do a static build and build DLLs afterwards,
174
- # with our own naming scheme.
175
- execute "mkdef-libeay32", "(perl util/mkdef.pl 32 libeay >libeay32.def)"
176
- execute "mkdef-ssleay32", "(perl util/mkdef.pl 32 ssleay >ssleay32.def)"
177
- dllwrap = consolidated_host(RbConfig::CONFIG["DLLWRAP"])
178
- execute "dllwrap-libeay32", "#{dllwrap} --dllname libeay32-#{version}-#{host}.dll --output-lib libcrypto.dll.a --def libeay32.def libcrypto.a -lwsock32 -lgdi32 -lcrypt32"
179
- execute "dllwrap-ssleay32", "#{dllwrap} --dllname ssleay32-#{version}-#{host}.dll --output-lib libssl.dll.a --def ssleay32.def libssl.a libcrypto.dll.a"
180
- end
181
-
182
- def install
183
- super
184
- FileUtils.cp "#{work_path}/libeay32-#{version}-#{host}.dll", "#{path}/bin/"
185
- FileUtils.cp "#{work_path}/ssleay32-#{version}-#{host}.dll", "#{path}/bin/"
186
- FileUtils.cp "#{work_path}/libcrypto.dll.a", "#{path}/lib/"
187
- FileUtils.cp "#{work_path}/libssl.dll.a", "#{path}/lib/"
188
- end
189
- end
190
- end
23
+ # Make sure to check the ports path for the configured host
24
+ host = RbConfig::CONFIG['host']
25
+ project_dir = File.expand_path("../../..", __FILE__)
26
+ freetds_ports_dir = File.join(project_dir, 'ports', host, 'freetds', FREETDS_VERSION)
27
+ freetds_ports_dir = File.expand_path(freetds_ports_dir)
28
+
29
+ # Add all the special path searching from the original tiny_tds build
30
+ # order is important here! First in, first searched.
31
+ DIRS = %w(
32
+ /opt/local
33
+ /usr/local
34
+ )
35
+
36
+ # Add the ports directory if it exists for local developer builds
37
+ DIRS.unshift(freetds_ports_dir) if File.directory?(freetds_ports_dir)
38
+
39
+ # Grab freetds environment variable for use by people on services like
40
+ # Heroku who they can't easily use bundler config to set directories
41
+ DIRS.unshift(ENV['FREETDS_DIR']) if ENV.has_key?('FREETDS_DIR')
42
+
43
+ # Add the search paths for freetds configured above
44
+ ldirs = DIRS.flat_map do |path|
45
+ ldir = "#{path}/lib"
46
+ [ldir, "#{ldir}/freetds"]
191
47
  end
192
48
 
193
- def define_libiconv_recipe(host)
194
- BuildRecipe.new("libiconv", ICONV_VERSION, [ICONV_SOURCE_URI]).tap do |recipe|
195
- # always produce position independent code
196
- recipe.configure_options << "CFLAGS=-fPIC"
197
- end
49
+ idirs = DIRS.flat_map do |path|
50
+ idir = "#{path}/include"
51
+ [idir, "#{idir}/freetds"]
198
52
  end
199
53
 
200
- def define_freetds_recipe(host, libiconv, libssl, gnutls)
201
- BuildRecipe.new("freetds", FREETDS_VERSION, [FREETDS_SOURCE_URI]).tap do |recipe|
202
- with_tdsver = FREETDS_VERSION =~ /0\.91/ ? "--with-tdsver=7.1" : "--with-tdsver=7.3"
203
- for_windows = recipe.host =~ /mswin|mingw/i
204
- recipe.configure_options << '--with-pic'
205
- recipe.configure_options << "--with-libiconv-prefix=#{libiconv.path}" if libiconv
206
- if true == libssl
207
- recipe.configure_options << "--with-openssl"
208
- elsif libssl
209
- recipe.configure_options << "--with-openssl=#{libssl.path}"
210
- end
211
- recipe.configure_options << "--with-gnutls" if gnutls
212
- recipe.configure_options << '--sysconfdir=C:\Sites' if for_windows
213
- recipe.configure_options << '--enable-sspi' if for_windows
214
- recipe.configure_options << "--disable-odbc"
215
- recipe.configure_options << with_tdsver
216
- if libiconv
217
- # For some reason freetds doesn't honor --with-libiconv-prefix
218
- # so we have do add it by hand:
219
- recipe.configure_options << "CFLAGS=-I#{libiconv.path}/include"
220
- recipe.configure_options << "LDFLAGS=-L#{libiconv.path}/lib -liconv"
221
- end
222
-
223
- class << recipe
224
-
225
- def install
226
- super_value = super
227
- # Install binstub target binaries.
228
- if super_value
229
- bin_path = File.expand_path File.join(path, 'bin')
230
- exe_path = File.expand_path File.join(target, '..', 'exe')
231
- return unless File.directory?(bin_path)
232
- ['tsql', 'defncopy'].each do |bin|
233
- ['.exe', ''].each do |ext|
234
- exe = File.join bin_path, "#{bin}#{ext}"
235
- next unless File.exists?(exe)
236
- next unless File.executable?(exe)
237
- FileUtils.cp exe, exe_path
238
- end
239
- end
240
- end
241
- super_value
242
- end
243
-
244
- end
245
-
246
- end
54
+ puts "looking for freetds headers in the following directories:\n#{idirs.map{|a| " - #{a}\n"}.join}"
55
+ puts "looking for freetds library in the following directories:\n#{ldirs.map{|a| " - #{a}\n"}.join}"
56
+ dir_config('freetds', idirs, ldirs)
57
+
58
+ have_dependencies = [
59
+ find_header('sybfront.h'),
60
+ find_header('sybdb.h'),
61
+ find_library('sybdb', 'tdsdbopen'),
62
+ find_library('sybdb', 'dbanydatecrack')
63
+ ].inject(true) do |memo, current|
64
+ memo && current
247
65
  end
248
66
 
249
- if RbConfig::CONFIG['target_os'] =~ /mswin32|mingw32/
250
- lib_prefix = 'lib' unless RbConfig::CONFIG['target_os'] =~ /mingw32/
251
- # There's no default include/lib dir on Windows. Let's just add the Ruby ones
252
- # and resort on the search path specified by INCLUDE and LIB environment
253
- # variables
254
- HEADER_DIRS = [INCLUDEDIR]
255
- LIB_DIRS = [LIBDIR]
256
- else
257
- lib_prefix = ''
258
- HEADER_DIRS = [
259
- # First search /opt/local for macports
260
- '/opt/local/include',
261
- # Then search /usr/local for people that installed from source
262
- '/usr/local/include',
263
- # Check the ruby install locations
264
- INCLUDEDIR,
265
- # Finally fall back to /usr
266
- '/usr/include'
267
- ].reject{ |dir| !File.directory?(dir) }
268
- LIB_DIRS = [
269
- # First search /opt/local for macports
270
- '/opt/local/lib',
271
- # Then search /usr/local for people that installed from source
272
- '/usr/local/lib',
273
- # Check the ruby install locations
274
- LIBDIR,
275
- # Finally fall back to /usr
276
- '/usr/lib',
277
- ].reject{ |dir| !File.directory?(dir) }
67
+ unless have_dependencies
68
+ abort 'Failed! Do you have FreeTDS 0.95.80 or higher installed?'
278
69
  end
279
70
 
280
- FREETDS_HEADER_DIRS = (searchable_paths_with_directories(['include'],['include','freetds']) + HEADER_DIRS).uniq
281
- FREETDS_LIB_DIRS = (searchable_paths_with_directories(['lib'],['lib','freetds']) + LIB_DIRS).uniq
282
-
283
- # lookup over searchable paths is great for native compilation, however, when
284
- # cross compiling we need to specify our own paths.
285
- if enable_config("lookup", true)
286
- dir_config('freetds', FREETDS_HEADER_DIRS, FREETDS_LIB_DIRS)
287
- else
288
- dir_config('freetds')
289
-
290
- # remove LDFLAGS
291
- $LDFLAGS = ENV.fetch("LDFLAGS", "")
292
- end
293
-
294
- def asplode(lib)
295
- msg = "-----\n"
296
- msg << "#{lib} is missing.\n"
297
- msg << "Do you have FreeTDS 0.95.80 or higher installed?\n" if lib == 'freetds'
298
- msg << "-----"
299
- abort(msg)
300
- end
301
-
302
- def freetds_usable?(lib_prefix)
303
- have_header('sybfront.h') && have_header('sybdb.h') &&
304
- find_library("#{lib_prefix}sybdb", 'tdsdbopen') &&
305
- find_library("#{lib_prefix}sybdb", 'dbanydatecrack')
306
- end
307
-
308
- # We use freetds, when available already, and fallback to compilation of ports
309
- system_freetds = enable_config('system-freetds', ENV['TINYTDS_SKIP_PORTS'] || freetds_usable?(lib_prefix))
310
-
311
- # We expect to have iconv and OpenSSL available on non-Windows systems
312
- host = RbConfig::CONFIG["host"]
313
- system_iconv = enable_config('system-iconv', host =~ /mingw|mswin/ ? false : true)
314
- system_openssl = enable_config('system-openssl', host =~ /mingw|mswin/ ? false : true )
315
- enable_gnutls = enable_config('gnutls', false )
316
- enable_openssl = enable_config('openssl', !enable_gnutls )
317
-
318
- unless system_freetds
319
- libssl = define_libssl_recipe(host).cook_and_activate unless system_openssl
320
- libiconv = define_libiconv_recipe(host).cook_and_activate unless system_iconv
321
- freetds = define_freetds_recipe(host, libiconv, libssl || enable_openssl, enable_gnutls).cook_and_activate
322
- dir_config('freetds', freetds.path + "/include", freetds.path + "/lib")
323
- end
324
-
325
- asplode 'freetds' unless freetds_usable?(lib_prefix)
326
-
327
71
  create_makefile('tiny_tds/tiny_tds')
328
72
 
329
73
  # :startdoc:
@@ -1,15 +1,15 @@
1
1
 
2
- ICONV_VERSION = ENV['TINYTDS_ICONV_VERSION'] || "1.14"
2
+ ICONV_VERSION = ENV['TINYTDS_ICONV_VERSION'] || "1.15"
3
3
  ICONV_SOURCE_URI = "http://ftp.gnu.org/pub/gnu/libiconv/libiconv-#{ICONV_VERSION}.tar.gz"
4
4
 
5
- OPENSSL_VERSION = ENV['TINYTDS_OPENSSL_VERSION'] || '1.0.2g'
5
+ OPENSSL_VERSION = ENV['TINYTDS_OPENSSL_VERSION'] || '1.1.1d'
6
6
  OPENSSL_SOURCE_URI = "https://www.openssl.org/source/openssl-#{OPENSSL_VERSION}.tar.gz"
7
7
 
8
- FREETDS_VERSION = ENV['TINYTDS_FREETDS_VERSION'] || "1.00"
8
+ FREETDS_VERSION = ENV['TINYTDS_FREETDS_VERSION'] || "1.1.24"
9
9
  FREETDS_VERSION_INFO = Hash.new { |h,k|
10
- h[k] = {files: "ftp://ftp.freetds.org/pub/freetds/stable/freetds-#{k}.tar.bz2"}
10
+ h[k] = {files: "http://www.freetds.org/files/stable/freetds-#{k}.tar.bz2"}
11
11
  }
12
- FREETDS_VERSION_INFO['1.00'] = {files: 'ftp://ftp.freetds.org/pub/freetds/stable/freetds-1.00.tar.bz2'}
13
- FREETDS_VERSION_INFO['0.99'] = {files: 'ftp://ftp.freetds.org/pub/freetds/current/freetds-dev.0.99.678.tar.gz'}
14
- FREETDS_VERSION_INFO['0.95'] = {files: 'ftp://ftp.freetds.org/pub/freetds/stable/freetds-0.95.92.tar.gz'}
12
+ FREETDS_VERSION_INFO['1.00'] = {files: 'http://www.freetds.org/files/stable/freetds-1.00.tar.bz2'}
13
+ FREETDS_VERSION_INFO['0.99'] = {files: 'http://www.freetds.org/files/current/freetds-dev.0.99.678.tar.gz'}
14
+ FREETDS_VERSION_INFO['0.95'] = {files: 'http://www.freetds.org/files/stable/freetds-0.95.92.tar.gz'}
15
15
  FREETDS_SOURCE_URI = FREETDS_VERSION_INFO[FREETDS_VERSION][:files]
@@ -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:
@@ -325,6 +340,15 @@ static VALUE rb_tinytds_result_fetch_row(VALUE self, ID timezone, int symbolize_
325
340
  case SYBTEXT:
326
341
  val = ENCODED_STR_NEW(data, data_len);
327
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
+ }
328
352
  default:
329
353
  val = ENCODED_STR_NEW(data, data_len);
330
354
  break;
@@ -556,7 +580,7 @@ static VALUE rb_tinytds_result_insert(VALUE self) {
556
580
 
557
581
  void init_tinytds_result() {
558
582
  /* Data Classes */
559
- cBigDecimal = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
583
+ cKernel = rb_const_get(rb_cObject, rb_intern("Kernel"));
560
584
  cDate = rb_const_get(rb_cObject, rb_intern("Date"));
561
585
  /* Define TinyTds::Result */
562
586
  cTinyTdsResult = rb_define_class_under(mTinyTds, "Result", rb_cObject);
@@ -578,6 +602,7 @@ void init_tinytds_result() {
578
602
  intern_new_offset = rb_intern("new_offset");
579
603
  intern_plus = rb_intern("+");
580
604
  intern_divide = rb_intern("/");
605
+ intern_bigd = rb_intern("BigDecimal");
581
606
  /* Symbol Helpers */
582
607
  sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
583
608
  sym_as = ID2SYM(rb_intern("as"));
data/lib/tiny_tds/bin.rb CHANGED
@@ -1,72 +1,91 @@
1
1
  require_relative './version'
2
+ require_relative './gem'
2
3
  require 'shellwords'
3
4
 
4
5
  module TinyTds
5
6
  class Bin
6
7
 
7
- ROOT = File.expand_path '../../..', __FILE__
8
- PATHS = ENV['PATH'].split File::PATH_SEPARATOR
9
- EXTS = (ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']) | ['.exe']
10
-
11
8
  attr_reader :name
12
9
 
13
10
  class << self
14
-
15
11
  def exe(name, *args)
16
12
  bin = new(name)
17
- puts bin.info
13
+ puts bin.info unless args.any? { |x| x == '-q' }
18
14
  bin.run(*args)
19
15
  end
20
-
21
16
  end
22
17
 
23
18
  def initialize(name)
19
+ @root = Gem.root_path
20
+ @exts = (ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']) | ['.exe']
21
+
24
22
  @name = name
25
23
  @binstub = find_bin
26
24
  @exefile = find_exe
27
25
  end
28
26
 
29
27
  def run(*args)
30
- return nil unless path
31
- Kernel.system Shellwords.join(args.unshift(path))
32
- $?.to_i
28
+ with_ports_paths do
29
+ return nil unless path
30
+ Kernel.system Shellwords.join(args.unshift(path))
31
+ $CHILD_STATUS.to_i
32
+ end
33
33
  end
34
34
 
35
35
  def path
36
- return @path if defined?(@path)
37
- @path = @exefile && File.exists?(@exefile) ? @exefile : which
36
+ @path ||= @exefile && File.exist?(@exefile) ? @exefile : which
38
37
  end
39
38
 
40
39
  def info
41
40
  "[TinyTds][v#{TinyTds::VERSION}][#{name}]: #{path}"
42
41
  end
43
42
 
44
-
45
43
  private
46
44
 
45
+ def search_paths
46
+ ENV['PATH'].split File::PATH_SEPARATOR
47
+ end
48
+
49
+ def with_ports_paths
50
+ old_path = ENV['PATH']
51
+
52
+ begin
53
+ ENV['PATH'] = [
54
+ Gem.ports_bin_paths,
55
+ old_path
56
+ ].flatten.join File::PATH_SEPARATOR
57
+
58
+ yield if block_given?
59
+ ensure
60
+ ENV['PATH'] = old_path
61
+ end
62
+ end
63
+
47
64
  def find_bin
48
- File.join ROOT, 'bin', name
65
+ File.join @root, 'bin', name
49
66
  end
50
67
 
51
68
  def find_exe
52
- EXTS.each do |ext|
53
- f = File.join ROOT, 'exe', "#{name}#{ext}"
54
- return f if File.exists?(f)
69
+ Gem.ports_bin_paths.each do |bin|
70
+ @exts.each do |ext|
71
+ f = File.join bin, "#{name}#{ext}"
72
+ return f if File.exist?(f)
73
+ end
55
74
  end
56
75
  nil
57
76
  end
58
77
 
59
78
  def which
60
- PATHS.each do |path|
61
- EXTS.each do |ext|
62
- exe = File.expand_path File.join(path, "#{name}#{ext}"), ROOT
79
+ search_paths.each do |path|
80
+ @exts.each do |ext|
81
+ exe = File.expand_path File.join(path, "#{name}#{ext}"), @root
63
82
  next if exe == @binstub
64
- next if !File.executable?(exe)
65
- next if !binary?(exe)
83
+ next unless File.executable?(exe)
84
+ next unless binary?(exe)
66
85
  return exe
67
86
  end
68
87
  end
69
- return nil
88
+ nil
70
89
  end
71
90
 
72
91
  # Implementation directly copied from ptools.
@@ -77,10 +96,9 @@ module TinyTds
77
96
  bytes = File.stat(file).blksize
78
97
  return false unless bytes
79
98
  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
99
+ s = (File.read(file, bytes) || '')
100
+ s = s.encode('US-ASCII', undef: :replace).split(//)
101
+ ((s.size - s.grep(' '..'~').size) / s.size.to_f) > 0.30
83
102
  end
84
-
85
103
  end
86
104
  end