tiny_tds_vagas 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/CHANGELOG +198 -0
  4. data/CODE_OF_CONDUCT.md +31 -0
  5. data/Gemfile +6 -0
  6. data/ISSUE_TEMPLATE.md +5 -0
  7. data/MIT-LICENSE +23 -0
  8. data/README.md +427 -0
  9. data/Rakefile +110 -0
  10. data/VERSION +1 -0
  11. data/appveyor.yml +52 -0
  12. data/bin/defncopy +3 -0
  13. data/bin/tsql +3 -0
  14. data/ext/tiny_tds/client.c +410 -0
  15. data/ext/tiny_tds/client.h +49 -0
  16. data/ext/tiny_tds/extconf.rb +329 -0
  17. data/ext/tiny_tds/extconsts.rb +15 -0
  18. data/ext/tiny_tds/result.c +608 -0
  19. data/ext/tiny_tds/result.h +32 -0
  20. data/ext/tiny_tds/tiny_tds_ext.c +12 -0
  21. data/ext/tiny_tds/tiny_tds_ext.h +17 -0
  22. data/lib/tiny_tds.rb +37 -0
  23. data/lib/tiny_tds/bin.rb +86 -0
  24. data/lib/tiny_tds/client.rb +124 -0
  25. data/lib/tiny_tds/error.rb +15 -0
  26. data/lib/tiny_tds/result.rb +8 -0
  27. data/lib/tiny_tds/version.rb +3 -0
  28. data/ports/patches/freetds/1.00/0001-mingw_missing_inet_pton.diff +34 -0
  29. data/test/appveyor/dbsetup.ps1 +27 -0
  30. data/test/appveyor/dbsetup.sql +9 -0
  31. data/test/benchmark/query.rb +77 -0
  32. data/test/benchmark/query_odbc.rb +106 -0
  33. data/test/benchmark/query_tinytds.rb +126 -0
  34. data/test/client_test.rb +217 -0
  35. data/test/result_test.rb +728 -0
  36. data/test/schema/1px.gif +0 -0
  37. data/test/schema/sqlserver_2000.sql +140 -0
  38. data/test/schema/sqlserver_2005.sql +140 -0
  39. data/test/schema/sqlserver_2008.sql +140 -0
  40. data/test/schema/sqlserver_2014.sql +140 -0
  41. data/test/schema/sqlserver_azure.sql +140 -0
  42. data/test/schema/sybase_ase.sql +138 -0
  43. data/test/schema_test.rb +443 -0
  44. data/test/test_helper.rb +213 -0
  45. data/test/thread_test.rb +98 -0
  46. data/tiny_tds.gemspec +28 -0
  47. metadata +201 -0
data/Rakefile ADDED
@@ -0,0 +1,110 @@
1
+ # encoding: UTF-8
2
+ require 'rake'
3
+ require 'rake/clean'
4
+ require 'rbconfig'
5
+ require 'rake/testtask'
6
+ require 'rake/extensiontask'
7
+ require 'rubygems/package_task'
8
+ require_relative './ext/tiny_tds/extconsts'
9
+
10
+ def test_libs
11
+ ['lib','test']
12
+ end
13
+
14
+ def test_files
15
+ if ENV['TEST_FILES']
16
+ ENV['TEST_FILES'].split(',').map{ |f| f.strip }.sort
17
+ else
18
+ Dir.glob("test/**/*_test.rb").sort
19
+ end
20
+ end
21
+
22
+ def add_file_to_gem(spec, relative_path)
23
+ target_path = File.join gem_build_path(spec), relative_path
24
+ target_dir = File.dirname(target_path)
25
+ mkdir_p target_dir
26
+ rm_f target_path
27
+ safe_ln relative_path, target_path
28
+ spec.files += [relative_path]
29
+ end
30
+
31
+ def gem_build_path(spec)
32
+ File.join 'pkg', spec.full_name
33
+ end
34
+
35
+ gemspec = Gem::Specification::load(File.expand_path('../tiny_tds.gemspec', __FILE__))
36
+
37
+ Rake::TestTask.new do |t|
38
+ t.libs = test_libs
39
+ t.test_files = test_files
40
+ t.verbose = true
41
+ end
42
+
43
+ Gem::PackageTask.new(gemspec) do |pkg|
44
+ pkg.need_tar = false
45
+ pkg.need_zip = false
46
+ end
47
+
48
+ task :compile
49
+
50
+ task :build => [:clean, :compile]
51
+ task(:build_quietly) { capture_stds { Rake::Task[:build].invoke } }
52
+
53
+ task :default => [:build, :test]
54
+
55
+ Dir["tasks/*.rake"].sort.each { |f| load f }
56
+
57
+ Rake::ExtensionTask.new('tiny_tds', gemspec) do |ext|
58
+ ext.lib_dir = 'lib/tiny_tds'
59
+ ext.cross_compile = true
60
+ ext.cross_platform = ['x86-mingw32', 'x64-mingw32']
61
+ ext.cross_config_options += %w[ --disable-lookup --enable-cross-build ]
62
+ # Add dependent DLLs to the cross gems
63
+ ext.cross_compiling do |spec|
64
+ platform_host_map = {
65
+ 'x86-mingw32' => 'i686-w64-mingw32',
66
+ 'x64-mingw32' => 'x86_64-w64-mingw32'
67
+ }
68
+ gemplat = spec.platform.to_s
69
+ host = platform_host_map[gemplat]
70
+ dlls = [
71
+ "libeay32-1.0.2g-#{host}.dll",
72
+ "ssleay32-1.0.2g-#{host}.dll",
73
+ "libiconv-2.dll",
74
+ "libsybdb-5.dll",
75
+ ]
76
+ # We don't need the sources in a fat binary gem
77
+ spec.files = spec.files.reject { |f| f=~/^ports\/archives/ }
78
+ spec.files += dlls.map { |dll| "ports/#{host}/bin/#{File.basename(dll)}" }
79
+ spec.files += Dir.glob('exe/*')
80
+ dlls.each do |dll|
81
+ file "ports/#{host}/bin/#{dll}" do |t|
82
+ sh "x86_64-w64-mingw32-strip", t.name
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ # Bundle the freetds sources to avoid download while gem install.
89
+ task gem_build_path(gemspec) do
90
+ add_file_to_gem gemspec, "ports/archives/freetds-#{FREETDS_VERSION}.tar.bz2"
91
+ end
92
+
93
+ desc "Build the windows binary gems per rake-compiler-dock"
94
+ task 'gem:windows' do
95
+ require 'rake_compiler_dock'
96
+ RakeCompilerDock.sh <<-EOT
97
+ bundle && rake cross native gem RUBY_CC_VERSION=2.0.0:2.1.6:2.2.2:2.3.0 CFLAGS="-Wall"
98
+ EOT
99
+ end
100
+
101
+ def capture_stds
102
+ pstdout, $stdout = $stdout, StringIO.new
103
+ pstderr, $stderr = $stderr, StringIO.new
104
+ yield
105
+ $stdout.string
106
+ $stderr.string
107
+ ensure
108
+ $stdout = pstdout
109
+ $stderr = pstderr
110
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.5
data/appveyor.yml ADDED
@@ -0,0 +1,52 @@
1
+ init:
2
+ - SET PATH=C:\Ruby%ruby_version%\bin;%PATH%
3
+ - SET PATH=C:\MinGW\msys\1.0\bin;%PATH%
4
+ - SET RAKEOPT=-rdevkit
5
+ - SET TESTOPTS='-v'
6
+ clone_depth: 5
7
+ skip_tags: true
8
+ matrix:
9
+ fast_finish: true
10
+ install:
11
+ - ps: Update-AppveyorBuild -Version "$(Get-Content $env:appveyor_build_folder\VERSION).$env:appveyor_build_number"
12
+ - ruby --version
13
+ - gem --version
14
+ - bundle install
15
+ - bundle exec rake build
16
+ build: off
17
+ branches:
18
+ except:
19
+ - /dev.*/
20
+ test_script:
21
+ - timeout /t 4 /nobreak > NUL
22
+ - powershell -File "%APPVEYOR_BUILD_FOLDER%\test\appveyor\dbsetup.ps1"
23
+ - timeout /t 4 /nobreak > NUL
24
+ - ps: Start-Service 'MSSQL$SQL2014'
25
+ - timeout /t 4 /nobreak > NUL
26
+ - sqlcmd -S ".\SQL2014" -U sa -P Password12! -i %APPVEYOR_BUILD_FOLDER%\test\appveyor\dbsetup.sql
27
+ - bundle exec rake TINYTDS_UNIT_HOST_TEST=localhost TINYTDS_UNIT_DATASERVER="localhost\SQL2014" TINYTDS_SCHEMA=sqlserver_2014 TDSVER=7.1
28
+ - bundle exec rake TINYTDS_UNIT_HOST_TEST=localhost TINYTDS_UNIT_DATASERVER="localhost\SQL2014" TINYTDS_SCHEMA=sqlserver_2014
29
+ - ps: Stop-Service 'MSSQL$SQL2014'
30
+ - ps: Start-Service 'MSSQL$SQL2012SP1'
31
+ - timeout /t 4 /nobreak > NUL
32
+ - sqlcmd -S ".\SQL2012SP1" -U sa -P Password12! -i %APPVEYOR_BUILD_FOLDER%\test\appveyor\dbsetup.sql
33
+ - bundle exec rake TINYTDS_UNIT_HOST_TEST=localhost TINYTDS_UNIT_DATASERVER="localhost\SQL2012SP1" TINYTDS_SCHEMA=sqlserver_2014
34
+ - ps: Stop-Service 'MSSQL$SQL2012SP1'
35
+ - ps: Start-Service 'MSSQL$SQL2008R2SP2'
36
+ - timeout /t 4 /nobreak > NUL
37
+ - sqlcmd -S ".\SQL2008R2SP2" -U sa -P Password12! -i %APPVEYOR_BUILD_FOLDER%\test\appveyor\dbsetup.sql
38
+ - bundle exec rake TINYTDS_UNIT_HOST_TEST=localhost TINYTDS_UNIT_DATASERVER="localhost\SQL2008R2SP2" TINYTDS_SCHEMA=sqlserver_2008
39
+ - timeout /t 4 /nobreak > NUL
40
+ - bundle exec rake TINYTDS_UNIT_HOST_TEST=%CI_AZURE_HOST% TINYTDS_UNIT_HOST=%CI_AZURE_HOST% TINYTDS_SCHEMA=sqlserver_azure
41
+ environment:
42
+ CI_AZURE_HOST:
43
+ secure: 8ydpYysZYKEBKvp6plKlYfepH98/zAuT27FFCaJ9Sss=
44
+ TINYTDS_UNIT_AZURE_PASS:
45
+ secure: fYKSKV4v+36OFQp2nZdX4DfUpgmy5cm0wuR73cgdmEk=
46
+ matrix:
47
+ - ruby_version: "200"
48
+ - ruby_version: "23"
49
+ - ruby_version: "23-x64"
50
+ on_failure:
51
+ - find -name compile.log | xargs cat
52
+
data/bin/defncopy ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative '../lib/tiny_tds/bin'
3
+ Process.exit TinyTds::Bin.exe('defncopy', *ARGV)
data/bin/tsql ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative '../lib/tiny_tds/bin'
3
+ Process.exit TinyTds::Bin.exe('tsql', *ARGV)
@@ -0,0 +1,410 @@
1
+ #include <tiny_tds_ext.h>
2
+ #include <errno.h>
3
+
4
+ VALUE cTinyTdsClient;
5
+ extern VALUE mTinyTds, cTinyTdsError;
6
+ static ID sym_username, sym_password, sym_dataserver, sym_database, sym_appname, sym_tds_version, sym_login_timeout, sym_timeout, sym_encoding, sym_azure;
7
+ static ID intern_source_eql, intern_severity_eql, intern_db_error_number_eql, intern_os_error_number_eql;
8
+ static ID intern_new, intern_dup, intern_transpose_iconv_encoding, intern_local_offset, intern_gsub;
9
+ VALUE opt_escape_regex, opt_escape_dblquote;
10
+
11
+
12
+ // Lib Macros
13
+
14
+ #define GET_CLIENT_WRAPPER(self) \
15
+ tinytds_client_wrapper *cwrap; \
16
+ Data_Get_Struct(self, tinytds_client_wrapper, cwrap)
17
+
18
+ #define REQUIRE_OPEN_CLIENT(cwrap) \
19
+ if (cwrap->closed || cwrap->userdata->closed) { \
20
+ rb_raise(cTinyTdsError, "closed connection"); \
21
+ return Qnil; \
22
+ }
23
+
24
+
25
+ // Lib Backend (Helpers)
26
+
27
+ VALUE rb_tinytds_raise_error(DBPROCESS *dbproc, int cancel, const char *error, const char *source, int severity, int dberr, int oserr) {
28
+ VALUE e;
29
+ GET_CLIENT_USERDATA(dbproc);
30
+ if (cancel && !dbdead(dbproc) && userdata && !userdata->closed) {
31
+ userdata->dbsqlok_sent = 1;
32
+ dbsqlok(dbproc);
33
+ userdata->dbcancel_sent = 1;
34
+ dbcancel(dbproc);
35
+ }
36
+ e = rb_exc_new2(cTinyTdsError, error);
37
+ rb_funcall(e, intern_source_eql, 1, rb_str_new2(source));
38
+ if (severity)
39
+ rb_funcall(e, intern_severity_eql, 1, INT2FIX(severity));
40
+ if (dberr)
41
+ rb_funcall(e, intern_db_error_number_eql, 1, INT2FIX(dberr));
42
+ if (oserr)
43
+ rb_funcall(e, intern_os_error_number_eql, 1, INT2FIX(oserr));
44
+ rb_exc_raise(e);
45
+ return Qnil;
46
+ }
47
+
48
+
49
+ // Lib Backend (Memory Management & Handlers)
50
+
51
+ int tinytds_err_handler(DBPROCESS *dbproc, int severity, int dberr, int oserr, char *dberrstr, char *oserrstr) {
52
+ static const char *source = "error";
53
+ /* Everything should cancel by default */
54
+ int return_value = INT_CANCEL;
55
+ int cancel = 0;
56
+
57
+ GET_CLIENT_USERDATA(dbproc);
58
+
59
+ /* These error codes are documented in include/sybdb.h in FreeTDS */
60
+ switch(dberr) {
61
+
62
+ /* We don't want to raise these as a ruby exception for various reasons */
63
+ case 100: /* SYBEVERDOWN, indicating the connection can only be v7.1 */
64
+ case SYBESEOF: /* Usually accompanied by another more useful error */
65
+ case SYBESMSG: /* Generic "check messages from server" error */
66
+ case SYBEICONVI: /* Just return ?s to the client, as explained in readme */
67
+ return return_value;
68
+
69
+ case SYBEICONVO:
70
+ dbfreebuf(dbproc);
71
+ return return_value;
72
+
73
+ case SYBETIME:
74
+ /*
75
+ SYBETIME is the only error that can send INT_TIMEOUT or INT_CONTINUE,
76
+ but we don't ever want to automatically retry. Instead have the app
77
+ decide what to do.
78
+ */
79
+ return_value = INT_TIMEOUT;
80
+ cancel = 1;
81
+ break;
82
+
83
+ case SYBEWRIT:
84
+ /* Write errors may happen after we abort a statement */
85
+ if (userdata && (userdata->dbsqlok_sent || userdata->dbcancel_sent)) {
86
+ return return_value;
87
+ }
88
+ cancel = 1;
89
+ break;
90
+ }
91
+
92
+ /*
93
+ When in non-blocking mode we need to store the exception data to throw it
94
+ once the blocking call returns, otherwise we will segfault ruby since part
95
+ of the contract of the ruby non-blocking indicator is that you do not call
96
+ any of the ruby C API.
97
+ */
98
+ if (userdata && userdata->nonblocking) {
99
+ if (cancel && !dbdead(dbproc) && !userdata->closed) {
100
+ dbcancel(dbproc);
101
+ userdata->dbcancel_sent = 1;
102
+ }
103
+
104
+ /*
105
+ If we've already captured an error message, don't overwrite it. This is
106
+ here because FreeTDS sends a generic "General SQL Server error" message
107
+ that will overwrite the real message. This is not normally a problem
108
+ because a ruby exception is normally thrown and we bail before the
109
+ generic message can be sent.
110
+ */
111
+ if (!userdata->nonblocking_error.is_set) {
112
+ userdata->nonblocking_error.cancel = cancel;
113
+ strncpy(userdata->nonblocking_error.error, dberrstr, ERROR_MSG_SIZE);
114
+ strncpy(userdata->nonblocking_error.source, source, ERROR_MSG_SIZE);
115
+ userdata->nonblocking_error.severity = severity;
116
+ userdata->nonblocking_error.dberr = dberr;
117
+ userdata->nonblocking_error.oserr = oserr;
118
+ userdata->nonblocking_error.is_set = 1;
119
+ }
120
+
121
+ } else {
122
+ rb_tinytds_raise_error(dbproc, cancel, dberrstr, source, severity, dberr, oserr);
123
+ }
124
+
125
+ return return_value;
126
+ }
127
+
128
+ int tinytds_msg_handler(DBPROCESS *dbproc, DBINT msgno, int msgstate, int severity, char *msgtext, char *srvname, char *procname, int line) {
129
+ static const char *source = "message";
130
+ GET_CLIENT_USERDATA(dbproc);
131
+ if (severity > 10) {
132
+ // See tinytds_err_handler() for info about why we do this
133
+ if (userdata && userdata->nonblocking) {
134
+ if (!userdata->nonblocking_error.is_set) {
135
+ userdata->nonblocking_error.cancel = 1;
136
+ strncpy(userdata->nonblocking_error.error, msgtext, ERROR_MSG_SIZE);
137
+ strncpy(userdata->nonblocking_error.source, source, ERROR_MSG_SIZE);
138
+ userdata->nonblocking_error.severity = severity;
139
+ userdata->nonblocking_error.dberr = msgno;
140
+ userdata->nonblocking_error.oserr = msgstate;
141
+ userdata->nonblocking_error.is_set = 1;
142
+ }
143
+ if (!dbdead(dbproc) && !userdata->closed) {
144
+ dbcancel(dbproc);
145
+ userdata->dbcancel_sent = 1;
146
+ }
147
+ } else {
148
+ rb_tinytds_raise_error(dbproc, 1, msgtext, source, severity, msgno, msgstate);
149
+ }
150
+ }
151
+ return 0;
152
+ }
153
+
154
+ static void rb_tinytds_client_reset_userdata(tinytds_client_userdata *userdata) {
155
+ userdata->timing_out = 0;
156
+ userdata->dbsql_sent = 0;
157
+ userdata->dbsqlok_sent = 0;
158
+ userdata->dbcancel_sent = 0;
159
+ userdata->nonblocking = 0;
160
+ userdata->nonblocking_error.is_set = 0;
161
+ }
162
+
163
+ static void rb_tinytds_client_mark(void *ptr) {
164
+ tinytds_client_wrapper *cwrap = (tinytds_client_wrapper *)ptr;
165
+ if (cwrap) {
166
+ rb_gc_mark(cwrap->charset);
167
+ }
168
+ }
169
+
170
+ static void rb_tinytds_client_free(void *ptr) {
171
+ tinytds_client_wrapper *cwrap = (tinytds_client_wrapper *)ptr;
172
+ if (cwrap->login)
173
+ dbloginfree(cwrap->login);
174
+ if (cwrap->client && !cwrap->closed) {
175
+ dbclose(cwrap->client);
176
+ cwrap->closed = 1;
177
+ cwrap->userdata->closed = 1;
178
+ }
179
+ xfree(cwrap->userdata);
180
+ xfree(ptr);
181
+ }
182
+
183
+ static VALUE allocate(VALUE klass) {
184
+ VALUE obj;
185
+ tinytds_client_wrapper *cwrap;
186
+ obj = Data_Make_Struct(klass, tinytds_client_wrapper, rb_tinytds_client_mark, rb_tinytds_client_free, cwrap);
187
+ cwrap->closed = 1;
188
+ cwrap->charset = Qnil;
189
+ cwrap->userdata = malloc(sizeof(tinytds_client_userdata));
190
+ cwrap->userdata->closed = 1;
191
+ rb_tinytds_client_reset_userdata(cwrap->userdata);
192
+ return obj;
193
+ }
194
+
195
+
196
+ // TinyTds::Client (public)
197
+
198
+ static VALUE rb_tinytds_tds_version(VALUE self) {
199
+ GET_CLIENT_WRAPPER(self);
200
+ return INT2FIX(dbtds(cwrap->client));
201
+ }
202
+
203
+ static VALUE rb_tinytds_close(VALUE self) {
204
+ GET_CLIENT_WRAPPER(self);
205
+ if (cwrap->client && !cwrap->closed) {
206
+ dbclose(cwrap->client);
207
+ cwrap->closed = 1;
208
+ cwrap->userdata->closed = 1;
209
+ }
210
+ return Qtrue;
211
+ }
212
+
213
+ static VALUE rb_tinytds_dead(VALUE self) {
214
+ GET_CLIENT_WRAPPER(self);
215
+ return dbdead(cwrap->client) ? Qtrue : Qfalse;
216
+ }
217
+
218
+ static VALUE rb_tinytds_closed(VALUE self) {
219
+ GET_CLIENT_WRAPPER(self);
220
+ return (cwrap->closed || cwrap->userdata->closed) ? Qtrue : Qfalse;
221
+ }
222
+
223
+ static VALUE rb_tinytds_canceled(VALUE self) {
224
+ GET_CLIENT_WRAPPER(self);
225
+ return cwrap->userdata->dbcancel_sent ? Qtrue : Qfalse;
226
+ }
227
+
228
+ static VALUE rb_tinytds_sqlsent(VALUE self) {
229
+ GET_CLIENT_WRAPPER(self);
230
+ return cwrap->userdata->dbsql_sent ? Qtrue : Qfalse;
231
+ }
232
+
233
+ static VALUE rb_tinytds_execute(VALUE self, VALUE sql) {
234
+ VALUE result;
235
+
236
+ GET_CLIENT_WRAPPER(self);
237
+ rb_tinytds_client_reset_userdata(cwrap->userdata);
238
+ REQUIRE_OPEN_CLIENT(cwrap);
239
+ dbcmd(cwrap->client, StringValueCStr(sql));
240
+ if (dbsqlsend(cwrap->client) == FAIL) {
241
+ rb_warn("TinyTds: dbsqlsend() returned FAIL.\n");
242
+ return Qfalse;
243
+ }
244
+ cwrap->userdata->dbsql_sent = 1;
245
+ result = rb_tinytds_new_result_obj(cwrap);
246
+ rb_iv_set(result, "@query_options", rb_funcall(rb_iv_get(self, "@query_options"), intern_dup, 0));
247
+ {
248
+ GET_RESULT_WRAPPER(result);
249
+ rwrap->local_offset = rb_funcall(cTinyTdsClient, intern_local_offset, 0);
250
+ rwrap->encoding = cwrap->encoding;
251
+ return result;
252
+ }
253
+ }
254
+
255
+ static VALUE rb_tinytds_charset(VALUE self) {
256
+ GET_CLIENT_WRAPPER(self);
257
+ return cwrap->charset;
258
+ }
259
+
260
+ static VALUE rb_tinytds_encoding(VALUE self) {
261
+ GET_CLIENT_WRAPPER(self);
262
+ return rb_enc_from_encoding(cwrap->encoding);
263
+ }
264
+
265
+ static VALUE rb_tinytds_escape(VALUE self, VALUE string) {
266
+ VALUE new_string;
267
+ GET_CLIENT_WRAPPER(self);
268
+
269
+ Check_Type(string, T_STRING);
270
+ new_string = rb_funcall(string, intern_gsub, 2, opt_escape_regex, opt_escape_dblquote);
271
+ rb_enc_associate(new_string, cwrap->encoding);
272
+ return new_string;
273
+ }
274
+
275
+ /* Duplicated in result.c */
276
+ static VALUE rb_tinytds_return_code(VALUE self) {
277
+ GET_CLIENT_WRAPPER(self);
278
+ if (cwrap->client && dbhasretstat(cwrap->client)) {
279
+ return LONG2NUM((long)dbretstatus(cwrap->client));
280
+ } else {
281
+ return Qnil;
282
+ }
283
+ }
284
+
285
+ static VALUE rb_tinytds_identity_sql(VALUE self) {
286
+ GET_CLIENT_WRAPPER(self);
287
+ return rb_str_new2(cwrap->identity_insert_sql);
288
+ }
289
+
290
+
291
+
292
+ // TinyTds::Client (protected)
293
+
294
+ static VALUE rb_tinytds_connect(VALUE self, VALUE opts) {
295
+ /* Parsing options hash to local vars. */
296
+ VALUE user, pass, dataserver, database, app, version, ltimeout, timeout, charset, azure;
297
+ GET_CLIENT_WRAPPER(self);
298
+
299
+ user = rb_hash_aref(opts, sym_username);
300
+ pass = rb_hash_aref(opts, sym_password);
301
+ dataserver = rb_hash_aref(opts, sym_dataserver);
302
+ database = rb_hash_aref(opts, sym_database);
303
+ app = rb_hash_aref(opts, sym_appname);
304
+ version = rb_hash_aref(opts, sym_tds_version);
305
+ ltimeout = rb_hash_aref(opts, sym_login_timeout);
306
+ timeout = rb_hash_aref(opts, sym_timeout);
307
+ charset = rb_hash_aref(opts, sym_encoding);
308
+ azure = rb_hash_aref(opts, sym_azure);
309
+ /* Dealing with options. */
310
+ if (dbinit() == FAIL) {
311
+ rb_raise(cTinyTdsError, "failed dbinit() function");
312
+ return self;
313
+ }
314
+ dberrhandle(tinytds_err_handler);
315
+ dbmsghandle(tinytds_msg_handler);
316
+ cwrap->login = dblogin();
317
+ if (!NIL_P(version))
318
+ dbsetlversion(cwrap->login, NUM2INT(version));
319
+ if (!NIL_P(user))
320
+ dbsetluser(cwrap->login, StringValueCStr(user));
321
+ if (!NIL_P(pass))
322
+ dbsetlpwd(cwrap->login, StringValueCStr(pass));
323
+ if (!NIL_P(app))
324
+ dbsetlapp(cwrap->login, StringValueCStr(app));
325
+ if (!NIL_P(ltimeout))
326
+ dbsetlogintime(NUM2INT(ltimeout));
327
+ if (!NIL_P(timeout))
328
+ dbsettime(NUM2INT(timeout));
329
+ if (!NIL_P(charset))
330
+ DBSETLCHARSET(cwrap->login, StringValueCStr(charset));
331
+ if (!NIL_P(database) && (azure == Qtrue)) {
332
+ #ifdef DBSETLDBNAME
333
+ DBSETLDBNAME(cwrap->login, StringValueCStr(database));
334
+ #else
335
+ rb_warn("TinyTds: Azure connections not supported in this version of FreeTDS.\n");
336
+ #endif
337
+ }
338
+ cwrap->client = dbopen(cwrap->login, StringValueCStr(dataserver));
339
+ if (cwrap->client) {
340
+ VALUE transposed_encoding;
341
+
342
+ cwrap->closed = 0;
343
+ cwrap->charset = charset;
344
+ if (!NIL_P(version))
345
+ dbsetversion(NUM2INT(version));
346
+ dbsetuserdata(cwrap->client, (BYTE*)cwrap->userdata);
347
+ cwrap->userdata->closed = 0;
348
+ if (!NIL_P(database) && (azure != Qtrue)) {
349
+ dbuse(cwrap->client, StringValueCStr(database));
350
+ }
351
+ transposed_encoding = rb_funcall(cTinyTdsClient, intern_transpose_iconv_encoding, 1, charset);
352
+ cwrap->encoding = rb_enc_find(StringValueCStr(transposed_encoding));
353
+ if (dbtds(cwrap->client) <= 7) {
354
+ cwrap->identity_insert_sql = "SELECT CAST(@@IDENTITY AS bigint) AS Ident";
355
+ } else {
356
+ cwrap->identity_insert_sql = "SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident";
357
+ }
358
+ }
359
+ return self;
360
+ }
361
+
362
+
363
+ // Lib Init
364
+
365
+ void init_tinytds_client() {
366
+ cTinyTdsClient = rb_define_class_under(mTinyTds, "Client", rb_cObject);
367
+ rb_define_alloc_func(cTinyTdsClient, allocate);
368
+ /* Define TinyTds::Client Public Methods */
369
+ rb_define_method(cTinyTdsClient, "tds_version", rb_tinytds_tds_version, 0);
370
+ rb_define_method(cTinyTdsClient, "close", rb_tinytds_close, 0);
371
+ rb_define_method(cTinyTdsClient, "closed?", rb_tinytds_closed, 0);
372
+ rb_define_method(cTinyTdsClient, "canceled?", rb_tinytds_canceled, 0);
373
+ rb_define_method(cTinyTdsClient, "dead?", rb_tinytds_dead, 0);
374
+ rb_define_method(cTinyTdsClient, "sqlsent?", rb_tinytds_sqlsent, 0);
375
+ rb_define_method(cTinyTdsClient, "execute", rb_tinytds_execute, 1);
376
+ rb_define_method(cTinyTdsClient, "charset", rb_tinytds_charset, 0);
377
+ rb_define_method(cTinyTdsClient, "encoding", rb_tinytds_encoding, 0);
378
+ rb_define_method(cTinyTdsClient, "escape", rb_tinytds_escape, 1);
379
+ rb_define_method(cTinyTdsClient, "return_code", rb_tinytds_return_code, 0);
380
+ rb_define_method(cTinyTdsClient, "identity_sql", rb_tinytds_identity_sql, 0);
381
+ /* Define TinyTds::Client Protected Methods */
382
+ rb_define_protected_method(cTinyTdsClient, "connect", rb_tinytds_connect, 1);
383
+ /* Symbols For Connect */
384
+ sym_username = ID2SYM(rb_intern("username"));
385
+ sym_password = ID2SYM(rb_intern("password"));
386
+ sym_dataserver = ID2SYM(rb_intern("dataserver"));
387
+ sym_database = ID2SYM(rb_intern("database"));
388
+ sym_appname = ID2SYM(rb_intern("appname"));
389
+ sym_tds_version = ID2SYM(rb_intern("tds_version"));
390
+ sym_login_timeout = ID2SYM(rb_intern("login_timeout"));
391
+ sym_timeout = ID2SYM(rb_intern("timeout"));
392
+ sym_encoding = ID2SYM(rb_intern("encoding"));
393
+ sym_azure = ID2SYM(rb_intern("azure"));
394
+ /* Intern TinyTds::Error Accessors */
395
+ intern_source_eql = rb_intern("source=");
396
+ intern_severity_eql = rb_intern("severity=");
397
+ intern_db_error_number_eql = rb_intern("db_error_number=");
398
+ intern_os_error_number_eql = rb_intern("os_error_number=");
399
+ /* Intern Misc */
400
+ intern_new = rb_intern("new");
401
+ intern_dup = rb_intern("dup");
402
+ intern_transpose_iconv_encoding = rb_intern("transpose_iconv_encoding");
403
+ intern_local_offset = rb_intern("local_offset");
404
+ intern_gsub = rb_intern("gsub");
405
+ /* Escape Regexp Global */
406
+ opt_escape_regex = rb_funcall(rb_cRegexp, intern_new, 1, rb_str_new2("\\\'"));
407
+ opt_escape_dblquote = rb_str_new2("''");
408
+ rb_global_variable(&opt_escape_regex);
409
+ rb_global_variable(&opt_escape_dblquote);
410
+ }