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.
- checksums.yaml +5 -5
- data/.codeclimate.yml +20 -0
- data/.gitattributes +1 -0
- data/.rubocop.yml +31 -0
- data/.travis.yml +25 -0
- data/{CHANGELOG → CHANGELOG.md} +102 -26
- data/Gemfile +4 -1
- data/ISSUE_TEMPLATE.md +35 -2
- data/README.md +131 -56
- data/Rakefile +31 -88
- data/VERSION +1 -1
- data/appveyor.yml +38 -17
- data/docker-compose.yml +22 -0
- data/ext/tiny_tds/client.c +147 -60
- data/ext/tiny_tds/client.h +11 -5
- data/ext/tiny_tds/extconf.rb +41 -297
- data/ext/tiny_tds/extconsts.rb +7 -7
- data/ext/tiny_tds/result.c +40 -15
- data/lib/tiny_tds/bin.rb +45 -27
- data/lib/tiny_tds/client.rb +46 -34
- data/lib/tiny_tds/error.rb +0 -1
- data/lib/tiny_tds/gem.rb +32 -0
- data/lib/tiny_tds/result.rb +2 -3
- data/lib/tiny_tds/version.rb +1 -1
- data/lib/tiny_tds.rb +38 -14
- 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/tasks/native_gem.rake +14 -0
- data/tasks/package.rake +8 -0
- data/tasks/ports/freetds.rb +37 -0
- data/tasks/ports/libiconv.rb +43 -0
- data/tasks/ports/openssl.rb +62 -0
- data/tasks/ports/recipe.rb +52 -0
- data/tasks/ports.rake +85 -0
- data/tasks/test.rake +9 -0
- data/test/appveyor/dbsetup.ps1 +1 -1
- data/test/bin/install-freetds.sh +20 -0
- data/test/bin/install-openssl.sh +18 -0
- data/test/bin/setup.sh +19 -0
- data/test/client_test.rb +124 -66
- data/test/gem_test.rb +179 -0
- data/test/result_test.rb +128 -42
- data/test/schema/sqlserver_2016.sql +140 -0
- data/test/schema_test.rb +23 -23
- data/test/test_helper.rb +65 -7
- data/test/thread_test.rb +1 -1
- data/tiny_tds.gemspec +9 -7
- metadata +60 -20
- /data/bin/{defncopy → defncopy-ttds} +0 -0
- /data/bin/{tsql → tsql-ttds} +0 -0
data/ext/tiny_tds/extconf.rb
CHANGED
|
@@ -3,327 +3,71 @@ ENV['RC_ARCHS'] = '' if RUBY_PLATFORM =~ /darwin/
|
|
|
3
3
|
# :stopdoc:
|
|
4
4
|
|
|
5
5
|
require 'mkmf'
|
|
6
|
-
require '
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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
|
-
|
|
250
|
-
|
|
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:
|
data/ext/tiny_tds/extconsts.rb
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
|
|
2
|
-
ICONV_VERSION = ENV['TINYTDS_ICONV_VERSION'] || "1.
|
|
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.
|
|
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.
|
|
8
|
+
FREETDS_VERSION = ENV['TINYTDS_FREETDS_VERSION'] || "1.1.24"
|
|
9
9
|
FREETDS_VERSION_INFO = Hash.new { |h,k|
|
|
10
|
-
h[k] = {files: "
|
|
10
|
+
h[k] = {files: "http://www.freetds.org/files/stable/freetds-#{k}.tar.bz2"}
|
|
11
11
|
}
|
|
12
|
-
FREETDS_VERSION_INFO['1.00'] = {files: '
|
|
13
|
-
FREETDS_VERSION_INFO['0.99'] = {files: '
|
|
14
|
-
FREETDS_VERSION_INFO['0.95'] = {files: '
|
|
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]
|
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:
|
|
@@ -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
|
-
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
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
|
|
65
|
+
File.join @root, 'bin', name
|
|
49
66
|
end
|
|
50
67
|
|
|
51
68
|
def find_exe
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
exe = File.expand_path File.join(path, "#{name}#{ext}"),
|
|
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
|
|
65
|
-
next
|
|
83
|
+
next unless File.executable?(exe)
|
|
84
|
+
next unless binary?(exe)
|
|
66
85
|
return exe
|
|
67
86
|
end
|
|
68
87
|
end
|
|
69
|
-
|
|
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', :
|
|
82
|
-
((s.size - s.grep(
|
|
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
|