tiny_tds 2.1.2 → 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/.github/workflows/ci.yml +590 -0
  3. data/.gitignore +2 -0
  4. data/CHANGELOG.md +51 -1
  5. data/Gemfile +1 -8
  6. data/ISSUE_TEMPLATE.md +1 -1
  7. data/README.md +89 -89
  8. data/Rakefile +44 -30
  9. data/VERSION +1 -1
  10. data/docker-compose.yml +34 -0
  11. data/ext/tiny_tds/client.c +100 -59
  12. data/ext/tiny_tds/client.h +5 -3
  13. data/ext/tiny_tds/extconf.rb +173 -52
  14. data/ext/tiny_tds/extconsts.rb +4 -11
  15. data/ext/tiny_tds/result.c +52 -45
  16. data/ext/tiny_tds/tiny_tds_ext.c +4 -1
  17. data/lib/tiny_tds/bin.rb +12 -26
  18. data/lib/tiny_tds/client.rb +38 -42
  19. data/lib/tiny_tds/error.rb +0 -2
  20. data/lib/tiny_tds/gem.rb +5 -14
  21. data/lib/tiny_tds/result.rb +0 -2
  22. data/lib/tiny_tds/version.rb +1 -1
  23. data/lib/tiny_tds.rb +28 -47
  24. data/setup_cimgruby_dev.sh +25 -0
  25. data/start_dev.sh +21 -0
  26. data/tasks/native_gem.rake +12 -10
  27. data/tasks/package.rake +1 -3
  28. data/tasks/ports.rake +14 -77
  29. data/tasks/test.rake +3 -5
  30. data/test/bin/install-freetds.sh +2 -4
  31. data/test/bin/install-mssql.ps1 +42 -0
  32. data/test/bin/install-mssqltools.sh +9 -0
  33. data/test/bin/restore-from-native-gem.ps1 +10 -0
  34. data/test/bin/setup_tinytds_db.sh +7 -0
  35. data/test/bin/setup_volume_permissions.sh +10 -0
  36. data/test/client_test.rb +152 -116
  37. data/test/gem_test.rb +39 -118
  38. data/test/result_test.rb +285 -350
  39. data/test/schema_test.rb +369 -395
  40. data/test/sql/db-create.sql +18 -0
  41. data/test/sql/db-login.sql +38 -0
  42. data/test/test_helper.rb +112 -85
  43. data/test/thread_test.rb +22 -31
  44. data/tiny_tds.gemspec +28 -26
  45. metadata +85 -59
  46. data/.travis.yml +0 -24
  47. data/BACKERS.md +0 -32
  48. data/appveyor.yml +0 -51
  49. data/tasks/ports/freetds.rb +0 -37
  50. data/tasks/ports/libiconv.rb +0 -43
  51. data/tasks/ports/openssl.rb +0 -78
  52. data/tasks/ports/recipe.rb +0 -52
  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/bin/setup.sh +0 -19
  59. data/test/schema/sqlserver_2000.sql +0 -140
  60. data/test/schema/sqlserver_2005.sql +0 -140
  61. data/test/schema/sqlserver_2014.sql +0 -140
  62. data/test/schema/sqlserver_2016.sql +0 -140
  63. data/test/schema/sybase_ase.sql +0 -138
  64. /data/test/schema/{sqlserver_2008.sql → sqlserver_2017.sql} +0 -0
@@ -24,25 +24,25 @@ VALUE opt_escape_regex, opt_escape_dblquote;
24
24
 
25
25
  // Lib Backend (Helpers)
26
26
 
27
- VALUE rb_tinytds_raise_error(DBPROCESS *dbproc, int is_message, int cancel, const char *error, const char *source, int severity, int dberr, int oserr) {
27
+ VALUE rb_tinytds_raise_error(DBPROCESS *dbproc, tinytds_errordata error) {
28
28
  VALUE e;
29
29
  GET_CLIENT_USERDATA(dbproc);
30
- if (cancel && !dbdead(dbproc) && userdata && !userdata->closed) {
30
+ if (error.cancel && !dbdead(dbproc) && userdata && !userdata->closed) {
31
31
  userdata->dbsqlok_sent = 1;
32
32
  dbsqlok(dbproc);
33
33
  userdata->dbcancel_sent = 1;
34
34
  dbcancel(dbproc);
35
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
-
45
- if (severity <= 10 && is_message) {
36
+ e = rb_exc_new2(cTinyTdsError, error.error);
37
+ rb_funcall(e, intern_source_eql, 1, rb_str_new2(error.source));
38
+ if (error.severity)
39
+ rb_funcall(e, intern_severity_eql, 1, INT2FIX(error.severity));
40
+ if (error.dberr)
41
+ rb_funcall(e, intern_db_error_number_eql, 1, INT2FIX(error.dberr));
42
+ if (error.oserr)
43
+ rb_funcall(e, intern_os_error_number_eql, 1, INT2FIX(error.oserr));
44
+
45
+ if (error.severity <= 10 && error.is_message) {
46
46
  VALUE message_handler = userdata && userdata->message_handler ? userdata->message_handler : Qnil;
47
47
  if (message_handler && message_handler != Qnil && rb_respond_to(message_handler, intern_call) != 0) {
48
48
  rb_funcall(message_handler, intern_call, 1, e);
@@ -57,6 +57,16 @@ VALUE rb_tinytds_raise_error(DBPROCESS *dbproc, int is_message, int cancel, cons
57
57
 
58
58
 
59
59
  // Lib Backend (Memory Management & Handlers)
60
+ static void push_userdata_error(tinytds_client_userdata *userdata, tinytds_errordata error) {
61
+ // reallocate memory for the array as needed
62
+ if (userdata->nonblocking_errors_size == userdata->nonblocking_errors_length) {
63
+ userdata->nonblocking_errors_size *= 2;
64
+ userdata->nonblocking_errors = realloc(userdata->nonblocking_errors, userdata->nonblocking_errors_size * sizeof(tinytds_errordata));
65
+ }
66
+
67
+ userdata->nonblocking_errors[userdata->nonblocking_errors_length] = error;
68
+ userdata->nonblocking_errors_length++;
69
+ }
60
70
 
61
71
  int tinytds_err_handler(DBPROCESS *dbproc, int severity, int dberr, int oserr, char *dberrstr, char *oserrstr) {
62
72
  static const char *source = "error";
@@ -86,6 +96,13 @@ int tinytds_err_handler(DBPROCESS *dbproc, int severity, int dberr, int oserr, c
86
96
  but we don't ever want to automatically retry. Instead have the app
87
97
  decide what to do.
88
98
  */
99
+ if (userdata && userdata->timing_out) {
100
+ return INT_CANCEL;
101
+ }
102
+ // userdata will not be set if hitting timeout during login so check for it first
103
+ if (userdata) {
104
+ userdata->timing_out = 1;
105
+ }
89
106
  return_value = INT_TIMEOUT;
90
107
  cancel = 1;
91
108
  break;
@@ -99,6 +116,16 @@ int tinytds_err_handler(DBPROCESS *dbproc, int severity, int dberr, int oserr, c
99
116
  break;
100
117
  }
101
118
 
119
+ tinytds_errordata error_data = {
120
+ .is_message = 0,
121
+ .cancel = cancel,
122
+ .severity = severity,
123
+ .dberr = dberr,
124
+ .oserr = oserr
125
+ };
126
+ strncpy(error_data.error, dberrstr, ERROR_MSG_SIZE);
127
+ strncpy(error_data.source, source, ERROR_MSG_SIZE);
128
+
102
129
  /*
103
130
  When in non-blocking mode we need to store the exception data to throw it
104
131
  once the blocking call returns, otherwise we will segfault ruby since part
@@ -110,27 +137,9 @@ int tinytds_err_handler(DBPROCESS *dbproc, int severity, int dberr, int oserr, c
110
137
  dbcancel(dbproc);
111
138
  userdata->dbcancel_sent = 1;
112
139
  }
113
-
114
- /*
115
- If we've already captured an error message, don't overwrite it. This is
116
- here because FreeTDS sends a generic "General SQL Server error" message
117
- that will overwrite the real message. This is not normally a problem
118
- because a ruby exception is normally thrown and we bail before the
119
- generic message can be sent.
120
- */
121
- if (!userdata->nonblocking_error.is_set) {
122
- userdata->nonblocking_error.is_message = 0;
123
- userdata->nonblocking_error.cancel = cancel;
124
- strncpy(userdata->nonblocking_error.error, dberrstr, ERROR_MSG_SIZE);
125
- strncpy(userdata->nonblocking_error.source, source, ERROR_MSG_SIZE);
126
- userdata->nonblocking_error.severity = severity;
127
- userdata->nonblocking_error.dberr = dberr;
128
- userdata->nonblocking_error.oserr = oserr;
129
- userdata->nonblocking_error.is_set = 1;
130
- }
131
-
140
+ push_userdata_error(userdata, error_data);
132
141
  } else {
133
- rb_tinytds_raise_error(dbproc, 0, cancel, dberrstr, source, severity, dberr, oserr);
142
+ rb_tinytds_raise_error(dbproc, error_data);
134
143
  }
135
144
 
136
145
  return return_value;
@@ -142,36 +151,72 @@ int tinytds_msg_handler(DBPROCESS *dbproc, DBINT msgno, int msgstate, int severi
142
151
 
143
152
  int is_message_an_error = severity > 10 ? 1 : 0;
144
153
 
154
+ tinytds_errordata error_data = {
155
+ .is_message = !is_message_an_error,
156
+ .cancel = is_message_an_error,
157
+ .severity = severity,
158
+ .dberr = msgno,
159
+ .oserr = msgstate
160
+ };
161
+ strncpy(error_data.error, msgtext, ERROR_MSG_SIZE);
162
+ strncpy(error_data.source, source, ERROR_MSG_SIZE);
163
+
145
164
  // See tinytds_err_handler() for info about why we do this
146
165
  if (userdata && userdata->nonblocking) {
147
- if (!userdata->nonblocking_error.is_set) {
148
- userdata->nonblocking_error.is_message = !is_message_an_error;
149
- userdata->nonblocking_error.cancel = is_message_an_error;
150
- strncpy(userdata->nonblocking_error.error, msgtext, ERROR_MSG_SIZE);
151
- strncpy(userdata->nonblocking_error.source, source, ERROR_MSG_SIZE);
152
- userdata->nonblocking_error.severity = severity;
153
- userdata->nonblocking_error.dberr = msgno;
154
- userdata->nonblocking_error.oserr = msgstate;
155
- userdata->nonblocking_error.is_set = 1;
156
- }
166
+ /*
167
+ In the case of non-blocking command batch execution we can receive multiple messages
168
+ (including errors). We keep track of those here so they can be processed once the
169
+ non-blocking call returns.
170
+ */
171
+ push_userdata_error(userdata, error_data);
157
172
 
158
173
  if (is_message_an_error && !dbdead(dbproc) && !userdata->closed) {
159
174
  dbcancel(dbproc);
160
175
  userdata->dbcancel_sent = 1;
161
176
  }
162
177
  } else {
163
- rb_tinytds_raise_error(dbproc, !is_message_an_error, is_message_an_error, msgtext, source, severity, msgno, msgstate);
178
+ rb_tinytds_raise_error(dbproc, error_data);
164
179
  }
165
180
  return 0;
166
181
  }
167
182
 
183
+ /*
184
+ Used by dbsetinterrupt -
185
+ This gets called periodically while waiting on a read from the server
186
+ Right now, we only care about cases where a read from the server is
187
+ taking longer than the specified timeout and dbcancel is not working.
188
+ In these cases we decide that we actually want to handle the interrupt
189
+ */
190
+ static int check_interrupt(void *ptr) {
191
+ GET_CLIENT_USERDATA((DBPROCESS *)ptr);
192
+ return userdata->timing_out;
193
+ }
194
+
195
+ /*
196
+ Used by dbsetinterrupt -
197
+ This gets called if check_interrupt returns TRUE.
198
+ Right now, this is only used in cases where a read from the server is
199
+ taking longer than the specified timeout and dbcancel is not working.
200
+ Return INT_CANCEL to abort the current command batch.
201
+ */
202
+ static int handle_interrupt(void *ptr) {
203
+ GET_CLIENT_USERDATA((DBPROCESS *)ptr);
204
+ if (userdata->timing_out) {
205
+ return INT_CANCEL;
206
+ }
207
+ return INT_CONTINUE;
208
+ }
209
+
168
210
  static void rb_tinytds_client_reset_userdata(tinytds_client_userdata *userdata) {
169
211
  userdata->timing_out = 0;
170
212
  userdata->dbsql_sent = 0;
171
213
  userdata->dbsqlok_sent = 0;
172
214
  userdata->dbcancel_sent = 0;
173
215
  userdata->nonblocking = 0;
174
- userdata->nonblocking_error.is_set = 0;
216
+ // the following is mainly done for consistency since the values are reset accordingly in nogvl_setup/cleanup.
217
+ // the nonblocking_errors array does not need to be freed here. That is done as part of nogvl_cleanup.
218
+ userdata->nonblocking_errors_length = 0;
219
+ userdata->nonblocking_errors_size = 0;
175
220
  }
176
221
 
177
222
  static void rb_tinytds_client_mark(void *ptr) {
@@ -187,6 +232,7 @@ static void rb_tinytds_client_free(void *ptr) {
187
232
  dbloginfree(cwrap->login);
188
233
  if (cwrap->client && !cwrap->closed) {
189
234
  dbclose(cwrap->client);
235
+ cwrap->client = NULL;
190
236
  cwrap->closed = 1;
191
237
  cwrap->userdata->closed = 1;
192
238
  }
@@ -218,6 +264,7 @@ static VALUE rb_tinytds_close(VALUE self) {
218
264
  GET_CLIENT_WRAPPER(self);
219
265
  if (cwrap->client && !cwrap->closed) {
220
266
  dbclose(cwrap->client);
267
+ cwrap->client = NULL;
221
268
  cwrap->closed = 1;
222
269
  cwrap->userdata->closed = 1;
223
270
  }
@@ -252,8 +299,7 @@ static VALUE rb_tinytds_execute(VALUE self, VALUE sql) {
252
299
  REQUIRE_OPEN_CLIENT(cwrap);
253
300
  dbcmd(cwrap->client, StringValueCStr(sql));
254
301
  if (dbsqlsend(cwrap->client) == FAIL) {
255
- rb_warn("TinyTds: dbsqlsend() returned FAIL.\n");
256
- return Qfalse;
302
+ rb_raise(cTinyTdsError, "failed dbsqlsend() function");
257
303
  }
258
304
  cwrap->userdata->dbsql_sent = 1;
259
305
  result = rb_tinytds_new_result_obj(cwrap);
@@ -357,17 +403,15 @@ static VALUE rb_tinytds_connect(VALUE self, VALUE opts) {
357
403
  #endif
358
404
  }
359
405
  }
360
- #ifdef DBSETUTF16
361
- if (use_utf16 == Qtrue) { DBSETLUTF16(cwrap->login, 1); }
362
- if (use_utf16 == Qfalse) { DBSETLUTF16(cwrap->login, 0); }
363
- #else
364
- if (use_utf16 == Qtrue || use_utf16 == Qfalse) {
365
- rb_warning("TinyTds: Please consider upgrading to FreeTDS 0.99 or higher for better unicode support.\n");
366
- }
367
- #endif
406
+ if (use_utf16 == Qtrue) { DBSETLUTF16(cwrap->login, 1); }
407
+ if (use_utf16 == Qfalse) { DBSETLUTF16(cwrap->login, 0); }
368
408
 
369
409
  cwrap->client = dbopen(cwrap->login, StringValueCStr(dataserver));
370
410
  if (cwrap->client) {
411
+ if (dbtds(cwrap->client) < 11) {
412
+ rb_raise(cTinyTdsError, "connecting with a TDS version older than 7.3!");
413
+ }
414
+
371
415
  VALUE transposed_encoding, timeout_string;
372
416
 
373
417
  cwrap->closed = 0;
@@ -381,17 +425,14 @@ static VALUE rb_tinytds_connect(VALUE self, VALUE opts) {
381
425
  }
382
426
  }
383
427
  dbsetuserdata(cwrap->client, (BYTE*)cwrap->userdata);
428
+ dbsetinterrupt(cwrap->client, check_interrupt, handle_interrupt);
384
429
  cwrap->userdata->closed = 0;
385
430
  if (!NIL_P(database) && (azure != Qtrue)) {
386
431
  dbuse(cwrap->client, StringValueCStr(database));
387
432
  }
388
433
  transposed_encoding = rb_funcall(cTinyTdsClient, intern_transpose_iconv_encoding, 1, charset);
389
434
  cwrap->encoding = rb_enc_find(StringValueCStr(transposed_encoding));
390
- if (dbtds(cwrap->client) <= 7) {
391
- cwrap->identity_insert_sql = "SELECT CAST(@@IDENTITY AS bigint) AS Ident";
392
- } else {
393
- cwrap->identity_insert_sql = "SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident";
394
- }
435
+ cwrap->identity_insert_sql = "SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident";
395
436
  }
396
437
  return self;
397
438
  }
@@ -5,9 +5,9 @@
5
5
  void init_tinytds_client();
6
6
 
7
7
  #define ERROR_MSG_SIZE 1024
8
+ #define ERRORS_STACK_INIT_SIZE 2
8
9
 
9
10
  typedef struct {
10
- short int is_set;
11
11
  int is_message;
12
12
  int cancel;
13
13
  char error[ERROR_MSG_SIZE];
@@ -25,7 +25,9 @@ typedef struct {
25
25
  RETCODE dbsqlok_retcode;
26
26
  short int dbcancel_sent;
27
27
  short int nonblocking;
28
- tinytds_errordata nonblocking_error;
28
+ short int nonblocking_errors_length;
29
+ short int nonblocking_errors_size;
30
+ tinytds_errordata *nonblocking_errors;
29
31
  VALUE message_handler;
30
32
  } tinytds_client_userdata;
31
33
 
@@ -40,7 +42,7 @@ typedef struct {
40
42
  rb_encoding *encoding;
41
43
  } tinytds_client_wrapper;
42
44
 
43
- VALUE rb_tinytds_raise_error(DBPROCESS *dbproc, int is_message, int cancel, const char *error, const char *source, int severity, int dberr, int oserr);
45
+ VALUE rb_tinytds_raise_error(DBPROCESS *dbproc, tinytds_errordata error);
44
46
 
45
47
  // Lib Macros
46
48
 
@@ -1,69 +1,190 @@
1
- ENV['RC_ARCHS'] = '' if RUBY_PLATFORM =~ /darwin/
1
+ require "mkmf"
2
+ require_relative "extconsts"
2
3
 
3
- # :stopdoc:
4
+ if ENV["MAINTAINER_MODE"]
5
+ warn "Maintainer mode enabled."
6
+ $CFLAGS << # standard:disable Style/GlobalVars
7
+ " -Wall" \
8
+ " -ggdb" \
9
+ " -DDEBUG" \
10
+ " -pedantic"
11
+ $LDFLAGS << # standard:disable Style/GlobalVars
12
+ " -ggdb"
13
+ end
4
14
 
5
- require 'mkmf'
6
- require 'rbconfig'
7
- require_relative './extconsts'
15
+ if (gem_platform = with_config("cross-build"))
16
+ require "mini_portile2"
8
17
 
9
- # Shamelessly copied from nokogiri
10
- #
18
+ openssl_platform = with_config("openssl-platform")
11
19
 
12
- def do_help
13
- print <<HELP
14
- usage: ruby #{$0} [options]
15
- --with-freetds-dir=DIR
16
- Use the freetds library placed under DIR.
17
- HELP
18
- exit! 0
19
- end
20
+ class BuildRecipe < MiniPortile
21
+ attr_accessor :gem_platform
22
+
23
+ def initialize(name, version, files)
24
+ super(name, version)
25
+ self.files = files
26
+ rootdir = File.expand_path("../../..", __FILE__)
27
+ self.target = File.join(rootdir, "ports")
28
+ self.patch_files = Dir[File.join("patches", self.name, self.version, "*.patch")].sort
29
+ end
30
+
31
+ # this will yield all ports into the same directory, making our path configuration for the linker easier
32
+ def port_path
33
+ "#{@target}/#{gem_platform}"
34
+ end
35
+
36
+ def cook_and_activate
37
+ checkpoint = File.join(target, "#{name}-#{version}-#{gem_platform}.installed")
38
+
39
+ unless File.exist?(checkpoint)
40
+ cook
41
+ FileUtils.touch checkpoint
42
+ end
43
+
44
+ activate
45
+ self
46
+ end
47
+ end
48
+
49
+ openssl_recipe = BuildRecipe.new("openssl", OPENSSL_VERSION, [OPENSSL_SOURCE_URI]).tap do |recipe|
50
+ class << recipe
51
+ attr_accessor :openssl_platform
52
+
53
+ def configure
54
+ envs = []
55
+ envs << "CFLAGS=-DDSO_WIN32 -DOPENSSL_THREADS" if MiniPortile.windows?
56
+ envs << "CFLAGS=-fPIC -DOPENSSL_THREADS" if MiniPortile.linux?
57
+ execute("configure", ["env", *envs, "./Configure", openssl_platform, "threads", "-static", "CROSS_COMPILE=#{host}-", configure_prefix, "--libdir=lib"], altlog: "config.log")
58
+ end
59
+
60
+ def compile
61
+ execute("compile", "#{make_cmd} build_libs")
62
+ end
63
+
64
+ def install
65
+ execute("install", "#{make_cmd} install_dev")
66
+ end
67
+ end
68
+
69
+ recipe.gem_platform = gem_platform
70
+ recipe.openssl_platform = openssl_platform
71
+ recipe.cook_and_activate
72
+ end
73
+
74
+ libiconv_recipe = BuildRecipe.new("libiconv", ICONV_VERSION, [ICONV_SOURCE_URI]).tap do |recipe|
75
+ recipe.configure_options << "CFLAGS=-fPIC" if MiniPortile.linux?
76
+ recipe.gem_platform = gem_platform
77
+
78
+ recipe.cook_and_activate
79
+ end
20
80
 
21
- do_help if arg_config('--help')
81
+ # remove the ".la" files, otherwise libtool starts to complain when linking into FreeTDS
82
+ Dir.glob(File.join(libiconv_recipe.path, "lib", "**", "*.la")).each do |la_file|
83
+ File.delete(la_file)
84
+ end
22
85
 
23
- # Make sure to check the ports path for the configured host
24
- host = RbConfig::CONFIG['host']
25
- project_dir = File.join(['..']*4)
26
- freetds_ports_dir = File.join(project_dir, 'ports', host, 'freetds', FREETDS_VERSION)
27
- freetds_ports_dir = File.expand_path(freetds_ports_dir)
86
+ freetds_recipe = BuildRecipe.new("freetds", FREETDS_VERSION, [FREETDS_SOURCE_URI]).tap do |recipe|
87
+ class << recipe
88
+ def configure_defaults
89
+ [
90
+ "--host=#{@host}",
91
+ "--enable-shared",
92
+ "--disable-static",
93
+ "--disable-odbc"
94
+ ]
95
+ end
96
+ end
28
97
 
29
- # Add all the special path searching from the original tiny_tds build
30
- # order is important here! First in, last searched.
31
- DIRS = %w(
32
- /usr/local
33
- /opt/local
34
- )
98
+ # i am not 100% what is going on behind the scenes
99
+ # it seems that FreeTDS build system prefers OPENSSL_CFLAGS and OPENSSL_LIBS
100
+ # but the linker still relies on LIBS and CPPFLAGS
101
+ # removing one or the other leads to build failures in any case of FreeTDS
102
+ if MiniPortile.linux?
103
+ recipe.configure_options << "CFLAGS=-fPIC"
104
+ elsif MiniPortile.windows?
105
+ recipe.configure_options << "--enable-sspi"
106
+ end
35
107
 
36
- # Grab freetds environment variable for use by people on services like
37
- # Heroku who they can't easily use bundler config to set directories
38
- DIRS.push(ENV['FREETDS_DIR']) if ENV.has_key?('FREETDS_DIR')
108
+ # pass an additional runtime path to the linker so defncopy and tsql can find our shared sybdb
109
+ recipe.configure_options << "LDFLAGS=-L#{openssl_recipe.path}/lib #{"-Wl,-rpath='$$ORIGIN/../lib'" if MiniPortile.linux?}"
110
+ recipe.configure_options << "LIBS=-liconv -lssl -lcrypto #{"-lwsock32 -lgdi32 -lws2_32 -lcrypt32" if MiniPortile.windows?} #{"-ldl -lpthread" if MiniPortile.linux?}"
111
+ recipe.configure_options << "CPPFLAGS=-I#{openssl_recipe.path}/include"
39
112
 
40
- # Add the ports directory if it exists for local developer builds
41
- DIRS.push(freetds_ports_dir) if File.directory?(freetds_ports_dir)
113
+ recipe.configure_options << "OPENSSL_CFLAGS=-L#{openssl_recipe.path}/lib"
114
+ recipe.configure_options << "OPENSSL_LIBS=-lssl -lcrypto #{"-lwsock32 -lgdi32 -lws2_32 -lcrypt32" if MiniPortile.windows?} #{"-ldl -lpthread" if MiniPortile.linux?}"
42
115
 
43
- # Add the search paths for freetds configured above
44
- DIRS.each do |path|
45
- idir = "#{path}/include"
46
- ldir = "#{path}/lib"
116
+ recipe.configure_options << "--with-openssl=#{openssl_recipe.path}"
117
+ recipe.configure_options << "--with-libiconv-prefix=#{libiconv_recipe.path}"
118
+ recipe.configure_options << "--sysconfdir=C:/Sites" if MiniPortile.windows?
47
119
 
48
- dir_config('freetds',
49
- [idir, "#{idir}/freetds"],
120
+ recipe.gem_platform = gem_platform
121
+ recipe.cook_and_activate
122
+ end
123
+
124
+ # enable relative path to later load the FreeTDS shared library
125
+ $LDFLAGS << " '-Wl,-rpath=$$ORIGIN/../../../ports/#{gem_platform}/lib'" # standard:disable Style/GlobalVars
126
+
127
+ dir_config("freetds", "#{freetds_recipe.path}/include", "#{freetds_recipe.path}/lib")
128
+ else
129
+ # Make sure to check the ports path for the configured host
130
+ architecture = RbConfig::CONFIG["arch"]
131
+
132
+ project_dir = File.expand_path("../../..", __FILE__)
133
+ freetds_ports_dir = File.join(project_dir, "ports", architecture, "freetds", FREETDS_VERSION)
134
+ freetds_ports_dir = File.expand_path(freetds_ports_dir)
135
+
136
+ # Add all the special path searching from the original tiny_tds build
137
+ # order is important here! First in, first searched.
138
+ DIRS = %w[
139
+ /opt/local
140
+ /usr/local
141
+ ]
142
+
143
+ if /darwin/i.match?(RbConfig::CONFIG["host_os"])
144
+ # Ruby below 2.7 seems to label the host CPU on Apple Silicon as aarch64
145
+ # 2.7 and above print is as ARM64
146
+ target_host_cpu = (Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.7")) ? "aarch64" : "arm64"
147
+
148
+ if RbConfig::CONFIG["host_cpu"] == target_host_cpu
149
+ # Homebrew on Apple Silicon installs into /opt/hombrew
150
+ # https://docs.brew.sh/Installation
151
+ # On Intel Macs, it is /usr/local, so no changes necessary to DIRS
152
+ DIRS.unshift("/opt/homebrew")
153
+ end
154
+ end
155
+
156
+ if ENV["RI_DEVKIT"] && ENV["MINGW_PREFIX"] # RubyInstaller Support
157
+ DIRS.unshift(File.join(ENV["RI_DEVKIT"], ENV["MINGW_PREFIX"]))
158
+ end
159
+
160
+ # Add the ports directory if it exists for local developer builds
161
+ DIRS.unshift(freetds_ports_dir) if File.directory?(freetds_ports_dir)
162
+
163
+ # Grab freetds environment variable for use by people on services like
164
+ # Heroku who they can't easily use bundler config to set directories
165
+ DIRS.unshift(ENV["FREETDS_DIR"]) if ENV.has_key?("FREETDS_DIR")
166
+
167
+ # Add the search paths for freetds configured above
168
+ ldirs = DIRS.flat_map do |path|
169
+ ldir = "#{path}/lib"
50
170
  [ldir, "#{ldir}/freetds"]
51
- )
52
- end
171
+ end
53
172
 
54
- have_dependencies = [
55
- find_header('sybfront.h'),
56
- find_header('sybdb.h'),
57
- find_library('sybdb', 'tdsdbopen'),
58
- find_library('sybdb', 'dbanydatecrack')
59
- ].inject(true) do |memo, current|
60
- memo && current
61
- end
173
+ idirs = DIRS.flat_map do |path|
174
+ idir = "#{path}/include"
175
+ [idir, "#{idir}/freetds"]
176
+ end
62
177
 
63
- unless have_dependencies
64
- abort 'Failed! Do you have FreeTDS 0.95.80 or higher installed?'
178
+ puts "looking for freetds headers in the following directories:\n#{idirs.map { |a| " - #{a}\n" }.join}"
179
+ puts "looking for freetds library in the following directories:\n#{ldirs.map { |a| " - #{a}\n" }.join}"
180
+ dir_config("freetds", idirs, ldirs)
65
181
  end
66
182
 
67
- create_makefile('tiny_tds/tiny_tds')
183
+ find_header("sybfront.h") or abort "Can't find the 'sybfront.h' header"
184
+ find_header("sybdb.h") or abort "Can't find the 'sybdb.h' header"
185
+
186
+ unless have_library("sybdb", "dbanydatecrack")
187
+ abort "Failed! Do you have FreeTDS 1.0.0 or higher installed?"
188
+ end
68
189
 
69
- # :startdoc:
190
+ create_makefile("tiny_tds/tiny_tds")
@@ -1,15 +1,8 @@
1
-
2
- ICONV_VERSION = ENV['TINYTDS_ICONV_VERSION'] || "1.15"
1
+ ICONV_VERSION = ENV["TINYTDS_ICONV_VERSION"] || "1.18"
3
2
  ICONV_SOURCE_URI = "http://ftp.gnu.org/pub/gnu/libiconv/libiconv-#{ICONV_VERSION}.tar.gz"
4
3
 
5
- OPENSSL_VERSION = ENV['TINYTDS_OPENSSL_VERSION'] || '1.1.0e'
4
+ OPENSSL_VERSION = ENV["TINYTDS_OPENSSL_VERSION"] || "3.4.0"
6
5
  OPENSSL_SOURCE_URI = "https://www.openssl.org/source/openssl-#{OPENSSL_VERSION}.tar.gz"
7
6
 
8
- FREETDS_VERSION = ENV['TINYTDS_FREETDS_VERSION'] || "1.00.27"
9
- FREETDS_VERSION_INFO = Hash.new { |h,k|
10
- h[k] = {files: "http://www.freetds.org/files/stable/freetds-#{k}.tar.bz2"}
11
- }
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
- FREETDS_SOURCE_URI = FREETDS_VERSION_INFO[FREETDS_VERSION][:files]
7
+ FREETDS_VERSION = ENV["TINYTDS_FREETDS_VERSION"] || "1.4.26"
8
+ FREETDS_SOURCE_URI = "http://www.freetds.org/files/stable/freetds-#{FREETDS_VERSION}.tar.bz2"