tiny_tds 0.4.5 → 0.5.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -11,6 +11,7 @@ tmp
11
11
  vendor
12
12
  lib/tiny_tds/tiny_tds.rb
13
13
  .rvmrc
14
+ .rbenv-version
14
15
  Gemfile.lock
15
16
  misc
16
17
  *.gem
data/CHANGELOG CHANGED
@@ -1,4 +1,24 @@
1
1
 
2
+ * 0.5.0 *
3
+
4
+ * Copy mysql2s handling of Time and Datetime so 64bit systems are leveraged. Fixes #46 and #47. Thanks @lsylvester!
5
+
6
+ * Add CFLAGS='-fPIC' for libtool. Fix TDS version configs in our ports file. Document. Fixes #45
7
+
8
+ * Update our TDS version constants to reflect changed 8.0/9.0 to 7.1/7.2 DBLIB versions in FreeTDS
9
+ while making it backward compatible, again like FreeTDS. Even tho you can not configure FreeTDS with
10
+ TDS version 7.2 or technically even use it, I added tests to prove that we correctly handle both
11
+ varchar(max) and nvarchar(max) with large amounts of data.
12
+
13
+ * FreeTDS 0.91 has been released. Update our port scripts.
14
+
15
+ * Add test for 0.91 and higher to handle incorrect syntax in sp_executesql.
16
+
17
+ * Returning empty result sets with a command batch that has multiple statements is now the default. Use :empty_sets => false to override.
18
+
19
+ * Do not raise a TinyTds::Error with our message handler unless the severity is greater than 10.
20
+
21
+
2
22
  * 0.4.5 *
3
23
 
4
24
  * Includes precompiled Windows binaries for FreeTDS 0.91rc2 & LibIconv. No precompiled OpenSSL yet for Windows to SQL Azure.
@@ -28,7 +48,8 @@
28
48
 
29
49
  * 0.4.2 *
30
50
 
31
- * Iconv is a dep only when compiling locally. However, left in the ability to configure it for native gem installation but you must use --enable-iconv before using --with-iconv-dir=/some/dir
51
+ * Iconv is a dep only when compiling locally. However, left in the ability to configure it for native gem installation
52
+ but you must use --enable-iconv before using --with-iconv-dir=/some/dir
32
53
 
33
54
  * Really fix what 0.4.1 was supposed to do, force SYBDBLIB compile.
34
55
 
data/Gemfile CHANGED
@@ -4,18 +4,11 @@ source :rubygems
4
4
  group :development do
5
5
  gem 'rake', '0.8.7'
6
6
  gem 'mini_portile', '0.2.2'
7
- gem 'rake-compiler', '0.7.8'
7
+ gem 'rake-compiler', '0.7.9'
8
8
  end
9
9
 
10
10
  group :test do
11
11
  gem 'mini_shoulda', '0.3.0'
12
12
  gem 'activesupport', '2.3.5'
13
13
  gem 'bench_press', '0.3.1'
14
- platforms :mri_18 do
15
- gem 'ruby-prof', '0.9.1'
16
- gem 'ruby-debug', '0.10.3'
17
- end
18
- platforms :mri_19 do
19
- gem 'ruby-debug19', '0.11.6'
20
- end
21
14
  end
@@ -1,7 +1,6 @@
1
-
2
1
  = TinyTDS - A modern, simple and fast FreeTDS library for Ruby using DB-Library.
3
2
 
4
- The TinyTDS gem is meant to serve the extremely common use-case of connecting, querying and iterating over results to Microsoft SQL Server databases from ruby. Even though it uses FreeTDS's DB-Library, it is NOT meant to serve as direct 1:1 mapping of that complex C API.
3
+ The TinyTDS gem is meant to serve the extremely common use-case of connecting, querying and iterating over results to Microsoft SQL Server databases from ruby. Even though it uses FreeTDS's DB-Library, it is NOT meant to serve as direct 1:1 mapping of that C API.
5
4
 
6
5
  The benefits are speed, automatic casting to ruby primitives, and proper encoding support. It converts all SQL Server datatypes to native ruby objects supporting :utc or :local time zones for time-like types. To date it is the only ruby client library that allows client encoding options, defaulting to UTF-8, while connecting to SQL Server. It also properly encodes all string and binary data. The motivation for TinyTDS is to become the de-facto low level connection mode for the SQL Server adapter for ActiveRecord. For further details see the special thanks section at the bottom
7
6
 
@@ -12,6 +11,7 @@ The API is simple and consists of these classes:
12
11
  * TinyTds::Error - A wrapper for all FreeTDS exceptions.
13
12
 
14
13
 
14
+
15
15
  == New & Noteworthy
16
16
 
17
17
  * Works with FreeTDS 0.91
@@ -19,9 +19,10 @@ The API is simple and consists of these classes:
19
19
  * New :host/:port connection options. Removes need for freetds.conf file.
20
20
 
21
21
 
22
+
22
23
  == Install
23
24
 
24
- Installing with rubygems should just work. TinyTDS is tested on ruby version 1.8.6, 1.8.7, 1.9.1, 1.9.2 as well as REE & JRuby.
25
+ Installing with rubygems should just work. TinyTDS is tested on ruby version 1.8.6, 1.8.7, 1.9.1, 1.9.2, 1.9.3 as well as REE & JRuby.
25
26
 
26
27
  $ gem install tiny_tds
27
28
 
@@ -29,9 +30,19 @@ Although we search for FreeTDS's libraries and headers, you may have to specify
29
30
 
30
31
 
31
32
 
32
- == FreeTDS Compatibility
33
+ == FreeTDS Compatibility & Configuration
34
+
35
+ TinyTDS is developed against FreeTDS 0.82 & 0.91, the latest is recommended. It is tested with SQL Server 2000, 2005, 2008 and Azure. Below are a few QA style notes about installing FreeTDS.
36
+
37
+ * <b>Do I need to install FreeTDS?</b> Yes! Somehow, someway, your are going to need FreeTDS for TinyTDS to compile against. You can avoid installing FreeTDS on your system by using our projects usage of rake-compiler and mini_portile to compile and package a native gem just for you. See the "Using MiniPortile" section below.
38
+
39
+ * <b>OK, I am installing FreeTDS, how do I configure it?</b> Contrary to what most people think, you do not need to specially configure FreeTDS in any way for client libraries like TinyTDS to use it. About the only requirement is that you compile it with libiconv for proper encoding support. FreeTDS must also be compiled with OpenSSL (or the like) to use it with Azure. See the "Using TinyTDS with Azure" section below for more info.
33
40
 
34
- TinyTDS is developed for FreeTDS 0.82 & 0.91. It is tested with SQL Server 2000, 2005, 2008 and Azure using TDS Version 8.0. We utilize FreeTDS's db-lib client library. We compile against sybdb.h, undefine MSDBLIB and define SYBDBLIB which means that internally we conform to sybase API to db-lib. TinyTDS will work for both Sybase & Miscrosoft SQL Server. You do NOT need to compile FreeTDS with the "--enable-msdblib" option for our client to work properly. However, please make sure to compile FreeTDS with libiconv support for encodings to work at their best. Run "tsql -C" in your console and check for "iconv library: yes".
41
+ * <b>Do I need to configure "--with-tdsver" equal to anything?</b> Maybe! Technically you should not have too. This is only a default for clients/configs that do not specify what TDS version they want to use. We are currently having issues with passing down a TDS version with the login bit. Till we get that fixed, if you are not using a freetds.conf or a TDSVER environment variable, the make sure to use 7.1 for FreeTDS 0.91 and 8.0 for FreeTDS 0.82.
42
+
43
+ * <b>But I want to use TDS version 7.2 for SQL Server 2005 and up!</b> TinyTDS uses TDS version 7.1 (previously named 8.0) and fully supports all the data types supported by FreeTDS, this includes varchar(max) and nvarchar(max). Technically compiling and using TDS version 7.2 with FreeTDS is not supported. But this does not mean those data types will not work. I know, it's confusing If you want to learn more, read this thread. http://lists.ibiblio.org/pipermail/freetds/2011q3/027306.html
44
+
45
+ * <b>I want to configure FreeTDS using "--enable-msdblib" and/or "--enable-sybase-compat" so it works for my database. Cool?</b> It's a waste of time and totally moot! Client libraries like TinyTDS define their own C structure names where they diverge from Sybase to SQL Server. Technically we use the Sybase structures which does not mean we only work with that database vs SQL Server. These configs are just a low level default for C libraries that do not define what they want. So I repeat, you do not NEED to use any of these, nor will they hurt anything since we control what C structure names we use and this has no affect on what database you use!
35
46
 
36
47
 
37
48
 
@@ -63,7 +74,7 @@ Creating a new client takes a hash of options. For valid iconv encoding options,
63
74
  * :port - Defaults to 1433. Only used if :host is used.
64
75
  * :database - The default database to use.
65
76
  * :appname - Short string seen in SQL Servers process/activity window.
66
- * :tds_version - TDS version. Defaults to 80, not recommended to change.
77
+ * :tds_version - TDS version. Defaults to 71 (7.1) and is not recommended to change!
67
78
  * :login_timeout - Seconds to wait for login. Default to 60 seconds.
68
79
  * :timeout - Seconds to wait for a response to a SQL command. Default 5 seconds.
69
80
  * :encoding - Any valid iconv value like CP1251 or ISO-8859-1. Default UTF-8.
@@ -202,6 +213,7 @@ Every TinyTds::Result object can pass query options to the #each method. The def
202
213
  * :symbolize_keys => false - Row hash keys. Defaults to shared/frozen string keys.
203
214
  * :cache_rows => true - Successive calls to #each returns the cached rows.
204
215
  * :timezone => :local - Local to the ruby client or :utc for UTC.
216
+ * :empty_sets => true - Include empty results set in queries that return multiple result sets.
205
217
 
206
218
  Each result gets a copy of the default options you specify at the client level and can be overridden by passing an options hash to the #each method. For example
207
219
 
@@ -227,7 +239,8 @@ By default row caching is turned on because the SQL Server adapter for ActiveRec
227
239
 
228
240
  As of version 2.3.11 & 3.0.3 of the adapter, you can specify a :dblib mode in database.yml and use TinyTDS as the low level connection mode. The SQL Server adapter can be found using the link below. Also included is a direct link to the wiki article covering common questions when using TinyTDS as the low level connection mode for the adapter.
229
241
 
230
- http://github.com/rails-sqlserver/activerecord-sqlserver-adapter/wiki/Using-TinyTds
242
+ * ActiveRecord SQL Server Adapter: http://github.com/rails-sqlserver/activerecord-sqlserver-adapter
243
+ * Using TinyTDS: http://github.com/rails-sqlserver/activerecord-sqlserver-adapter/wiki/Using-TinyTds
231
244
 
232
245
 
233
246
 
@@ -237,6 +250,20 @@ TinyTDS is fully tested with the Azure platform. You must set the :azure => true
237
250
 
238
251
 
239
252
 
253
+ == Using MiniPortile
254
+
255
+ MiniPortile is a minimalistic, simplistic and stupid implementation of a port/recipe system for developers. <https://github.com/luislavena/mini_portile>
256
+
257
+ The TinyTDS project uses MiniPortile so that we can easily install a local "project specific" version of FreeTDS and supporting libraries to link against when building a test version of TinyTDS. MiniPortile is a great tool that even allows us to build statically linked components that TinyTDS relies on. Hence this allows us to publish native gems for any platform. We use this feature for gems targeted at Windows.
258
+
259
+ You too can use MiniPortile to build TinyTDS and build your own gems for your own package management needs. Here is a few simple steps that assume you have cloned a fresh copy of this repository. 1) Bundling will install all the development dependencies. 2) Running "rake compile" will basically download and install a supported version of FreeTDS in our "ports.rake" file and supporting libraries. These will all be installed into the projects tmp directory. 3) The final "rake native gem" command will build a native gem for your specific platform.
260
+
261
+ $ bundle install
262
+ $ rake compile
263
+ $ rake native gem
264
+
265
+
266
+
240
267
  == Development & Testing
241
268
 
242
269
  We use bundler for development. Simply run "bundle install" then "rake" to build the gem and run the unit tests. The tests assume you have created a database named "tinytdstest" accessible by a database owner named "tinytds". Before running the test rake task, you may need to define a pair of environment variables that help the client connect to your specific FreeTDS database server name and which schema (2000, 2005, 2008 or azure) to use. For example:
@@ -245,23 +272,26 @@ We use bundler for development. Simply run "bundle install" then "rake" to build
245
272
  or
246
273
  $ rake TINYTDS_UNIT_HOST=mydb.host.net TINYTDS_SCHEMA=sqlserver_azure
247
274
 
248
- If you do not want to use MiniPortile to compile a local project version of FreeTDS, use the TINYTDS_SKIP_PORTS environment variable. This will ignore any port tasks and will instead look for your systems FreeTDS installation as a normal gem install would.
275
+ If you do not want to use MiniPortile to compile a local project version of FreeTDS and instead use your local system version, use the TINYTDS_SKIP_PORTS environment variable. This will ignore any port tasks and will instead build and link to your system's FreeTDS installation as a normal gem install would.
249
276
 
250
277
  $ rake TINYTDS_SKIP_PORTS=true
251
278
 
252
- For help and support.
279
+
280
+
281
+ == Help & Support
253
282
 
254
283
  * Github Source: http://github.com/rails-sqlserver/tiny_tds
255
284
  * Github Issues: http://github.com/rails-sqlserver/tiny_tds/issues
256
285
  * Google Group: http://groups.google.com/group/rails-sqlserver-adapter
257
286
  * IRC Room: #rails-sqlserver on irc.freenode.net
258
287
 
259
- Current to do list.
288
+
289
+
290
+ == TODO List
260
291
 
261
292
  * Include OpenSSL with Windows binaries for SQL Azure.
262
293
  * Install an interrupt handler.
263
294
  * Allow #escape to accept all ruby primitives.
264
- * Get bug reports!
265
295
 
266
296
 
267
297
 
@@ -278,3 +308,10 @@ My name is Ken Collins and I currently maintain the SQL Server adapter for Activ
278
308
  * Yehuda Katz for articulating ruby's need for proper encoding support. Especially in database drivers - http://yehudakatz.com/2010/05/05/ruby-1-9-encodings-a-primer-and-the-solution-for-rails/
279
309
  * Josh Clayton of Thoughtbot for writing about ruby C extensions. - http://robots.thoughtbot.com/post/1037240922/get-your-c-on
280
310
 
311
+
312
+
313
+ == License
314
+
315
+ TinyTDS is Copyright (c) 2010-2011 Ken Collins, <ken@metaskills.net> and is distributed under the MIT license. Windows binaries contain precompiled versions of FreeTDS <http://www.freetds.org/> which is licensed under the GNU LGPL license at <http://www.gnu.org/licenses/lgpl-2.0.html>
316
+
317
+
data/Rakefile CHANGED
@@ -6,6 +6,17 @@ require 'rake/testtask'
6
6
  require 'rake/extensiontask'
7
7
  require "rubygems/package_task"
8
8
 
9
+ # My notes for cross compile native Windows gem.
10
+ #
11
+ # $ rake-compiler cross-ruby VERSION=1.8.7-p352
12
+ # $ rake-compiler cross-ruby VERSION=1.9.2-p290
13
+ #
14
+ # $ git clean -x -d -f
15
+ # $ bundle install
16
+ # $ ~/.rbenv/shims/rake compile
17
+ # $ ~/.rbenv/shims/rake cross compile RUBY_CC_VERSION=1.8.7:1.9.2
18
+ # $ ~/.rbenv/shims/rake cross native gem RUBY_CC_VERSION=1.8.7:1.9.2
19
+
9
20
  def test_libs
10
21
  ['lib','test']
11
22
  end
@@ -54,6 +54,8 @@ int tinytds_err_handler(DBPROCESS *dbproc, int severity, int dberr, int oserr, c
54
54
  int return_value = INT_CONTINUE;
55
55
  int cancel = 0;
56
56
  switch(dberr) {
57
+ case 100: /* SYBEVERDOWN */
58
+ return INT_CANCEL;
57
59
  case SYBESMSG:
58
60
  return return_value;
59
61
  case SYBEICONVI:
@@ -96,7 +98,7 @@ int tinytds_err_handler(DBPROCESS *dbproc, int severity, int dberr, int oserr, c
96
98
 
97
99
  int tinytds_msg_handler(DBPROCESS *dbproc, DBINT msgno, int msgstate, int severity, char *msgtext, char *srvname, char *procname, int line) {
98
100
  static char *source = "message";
99
- if (severity)
101
+ if (severity > 10)
100
102
  rb_tinytds_raise_error(dbproc, 1, msgtext, source, severity, msgno, msgstate);
101
103
  return 0;
102
104
  }
@@ -232,6 +234,14 @@ static VALUE rb_tinytds_return_code(VALUE self) {
232
234
  }
233
235
  }
234
236
 
237
+ static VALUE rb_tinytds_freetds_nine_one_or_higher(VALUE self) {
238
+ #ifdef DBSETLDBNAME
239
+ return Qtrue;
240
+ #else
241
+ return Qfalse;
242
+ #endif
243
+ }
244
+
235
245
 
236
246
  // TinyTds::Client (protected)
237
247
 
@@ -313,6 +323,7 @@ void init_tinytds_client() {
313
323
  rb_define_method(cTinyTdsClient, "encoding", rb_tinytds_encoding, 0);
314
324
  rb_define_method(cTinyTdsClient, "escape", rb_tinytds_escape, 1);
315
325
  rb_define_method(cTinyTdsClient, "return_code", rb_tinytds_return_code, 0);
326
+ rb_define_method(cTinyTdsClient, "freetds_091_or_higer?", rb_tinytds_freetds_nine_one_or_higher, 0);
316
327
  /* Define TinyTds::Client Protected Methods */
317
328
  rb_define_protected_method(cTinyTdsClient, "connect", rb_tinytds_connect, 1);
318
329
  /* Symbols For Connect */
@@ -1,6 +1,45 @@
1
1
 
2
2
  #include <tiny_tds_ext.h>
3
3
 
4
+ // TINY_TDS_MAX_TIME
5
+
6
+ #if (SIZEOF_INT < SIZEOF_LONG) || defined(HAVE_RUBY_ENCODING_H)
7
+ /* On 64bit platforms we can handle dates way outside 2038-01-19T03:14:07 */
8
+ /* (10000*31557600) + (12*2592000) + (31*86400) + (11*3600) + (59*60) + 59 */
9
+ #define TINY_TDS_MAX_TIME 315607276799ULL
10
+ #else
11
+ /* On 32bit platforms the maximum date the Time class can handle is 2038-01-19T03:14:07 */
12
+ /* 2038 years + 1 month + 19 days + 3 hours + 14 minutes + 7 seconds = 64318634047 seconds */
13
+ /* (2038*31557600) + (1*2592000) + (19*86400) + (3*3600) + (14*60) + 7 */
14
+ #define TINY_TDS_MAX_TIME 64318634047ULL
15
+ #endif
16
+
17
+
18
+ // TINY_TDS_MIN_TIME
19
+
20
+ #if defined(HAVE_RUBY_ENCODING_H)
21
+ /* Ruby 1.9 */
22
+ /* 0000-1-1 00:00:00 UTC */
23
+ /* (0*31557600) + (1*2592000) + (1*86400) + (0*3600) + (0*60) + 0 */
24
+ #define TINY_TDS_MIN_TIME 2678400ULL
25
+ #elif SIZEOF_INT < SIZEOF_LONG
26
+ /* 64bit Ruby 1.8 */
27
+ /* 0139-1-1 00:00:00 UTC */
28
+ /* (139*31557600) + (1*2592000) + (1*86400) + (0*3600) + (0*60) + 0 */
29
+ #define TINY_TDS_MIN_TIME 4389184800ULL
30
+ #elif defined(NEGATIVE_TIME_T)
31
+ /* 1901-12-13 20:45:52 UTC : The oldest time in 32-bit signed time_t. */
32
+ /* (1901*31557600) + (12*2592000) + (13*86400) + (20*3600) + (45*60) + 52 */
33
+ #define TINY_TDS_MIN_TIME 60023299552ULL
34
+ #else
35
+ /* 1970-01-01 00:00:01 UTC : The Unix epoch - the oldest time in portable time_t. */
36
+ /* (1970*31557600) + (1*2592000) + (1*86400) + (0*3600) + (0*60) + 1 */
37
+ #define TINY_TDS_MIN_TIME 62171150401ULL
38
+ #endif
39
+
40
+
41
+ // File Types/Vars
42
+
4
43
  VALUE cTinyTdsResult;
5
44
  extern VALUE mTinyTds, cTinyTdsClient, cTinyTdsError;
6
45
  VALUE cBigDecimal, cDate, cDateTime;
@@ -8,7 +47,7 @@ VALUE opt_decimal_zero, opt_float_zero, opt_one, opt_zero, opt_four, opt_19hdr,
8
47
  int opt_ruby_186;
9
48
  static ID intern_new, intern_utc, intern_local, intern_localtime, intern_merge,
10
49
  intern_civil, intern_new_offset, intern_plus, intern_divide, intern_Rational;
11
- static ID sym_symbolize_keys, sym_as, sym_array, sym_cache_rows, sym_first, sym_timezone, sym_local, sym_utc;
50
+ static ID sym_symbolize_keys, sym_as, sym_array, sym_cache_rows, sym_first, sym_timezone, sym_local, sym_utc, sym_empty_sets;
12
51
 
13
52
 
14
53
  // Lib Macros
@@ -200,8 +239,9 @@ static VALUE rb_tinytds_result_fetch_row(VALUE self, ID timezone, int symbolize_
200
239
  msec = date_rec.datemsecond;
201
240
  if (year+month+day+hour+min+sec+msec != 0) {
202
241
  VALUE offset = (timezone == intern_local) ? rwrap->local_offset : opt_zero;
242
+ uint64_t seconds = (year*31557600ULL) + (month*2592000ULL) + (day*86400ULL) + (hour*3600ULL) + (min*60ULL) + sec;
203
243
  /* Use DateTime */
204
- if (year < 1902 || year+month+day > 2058) {
244
+ if (seconds < TINY_TDS_MIN_TIME || seconds > TINY_TDS_MAX_TIME) {
205
245
  VALUE datetime_sec = INT2NUM(sec);
206
246
  if (msec != 0) {
207
247
  if ((opt_ruby_186 == 1 && sec < 59) || (opt_ruby_186 != 1)) {
@@ -256,8 +296,12 @@ static VALUE rb_tinytds_result_fields(VALUE self) {
256
296
  VALUE fields_processed = rb_ary_entry(rwrap->fields_processed, rwrap->number_of_results);
257
297
  if ((dbsqlok_rc == SUCCEED) && (dbresults_rc == SUCCEED) && (fields_processed == Qnil)) {
258
298
  /* Default query options. */
299
+ int symbolize_keys = 0, empty_sets = 1;
259
300
  VALUE qopts = rb_iv_get(self, "@query_options");
260
- int symbolize_keys = (rb_hash_aref(qopts, sym_symbolize_keys) == Qtrue) ? 1 : 0;
301
+ if (rb_hash_aref(qopts, sym_symbolize_keys) == Qtrue)
302
+ symbolize_keys = 1;
303
+ if (rb_hash_aref(qopts, sym_empty_sets) == Qfalse)
304
+ empty_sets = 0;
261
305
  /* Set number_of_fields count for this result set. */
262
306
  rwrap->number_of_fields = dbnumcols(rwrap->client);
263
307
  if (rwrap->number_of_fields > 0) {
@@ -292,7 +336,7 @@ static VALUE rb_tinytds_result_each(int argc, VALUE * argv, VALUE self) {
292
336
  /* Local Vars */
293
337
  VALUE qopts, opts, block;
294
338
  ID timezone;
295
- int symbolize_keys = 0, as_array = 0, cache_rows = 0, first = 0;
339
+ int symbolize_keys = 0, as_array = 0, cache_rows = 0, first = 0, empty_sets = 0;
296
340
  /* Merge Options Hash To Query Options. Populate Opts & Block Var. */
297
341
  qopts = rb_iv_get(self, "@query_options");
298
342
  if (rb_scan_args(argc, argv, "01&", &opts, &block) == 1)
@@ -315,6 +359,8 @@ static VALUE rb_tinytds_result_each(int argc, VALUE * argv, VALUE self) {
315
359
  rb_warn(":timezone option must be :utc or :local - defaulting to :local");
316
360
  timezone = intern_local;
317
361
  }
362
+ if (rb_hash_aref(qopts, sym_empty_sets) == Qtrue)
363
+ empty_sets = 1;
318
364
  /* Make The Results Or Yield Existing */
319
365
  if (NIL_P(rwrap->results)) {
320
366
  rwrap->results = rb_ary_new();
@@ -322,8 +368,9 @@ static VALUE rb_tinytds_result_each(int argc, VALUE * argv, VALUE self) {
322
368
  RETCODE dbresults_rc = rb_tinytds_result_dbresults_retcode(self);
323
369
  while ((dbsqlok_rc == SUCCEED) && (dbresults_rc == SUCCEED)) {
324
370
  int has_rows = (DBROWS(rwrap->client) == SUCCEED) ? 1 : 0;
325
- rb_tinytds_result_fields(self);
326
- if (has_rows && rwrap->number_of_fields > 0) {
371
+ if (has_rows || empty_sets || (rwrap->number_of_results == 0))
372
+ rb_tinytds_result_fields(self);
373
+ if ((has_rows || empty_sets) && rwrap->number_of_fields > 0) {
327
374
  /* Create rows for this result set. */
328
375
  unsigned long rowi = 0;
329
376
  VALUE result = rb_ary_new();
@@ -356,6 +403,7 @@ static VALUE rb_tinytds_result_each(int argc, VALUE * argv, VALUE self) {
356
403
  // If we find results increment the counter that helpers use and setup the next loop.
357
404
  rwrap->number_of_results = rwrap->number_of_results + 1;
358
405
  dbresults_rc = rb_tinytds_result_dbresults_retcode(self);
406
+ rb_ary_store(rwrap->fields_processed, rwrap->number_of_results, Qnil);
359
407
  } else {
360
408
  // If we do not find results, side step the rb_tinytds_result_dbresults_retcode helper and
361
409
  // manually populate its memoized array while nullifing any memoized fields too before loop.
@@ -473,6 +521,7 @@ void init_tinytds_result() {
473
521
  sym_local = ID2SYM(intern_local);
474
522
  sym_utc = ID2SYM(intern_utc);
475
523
  sym_timezone = ID2SYM(rb_intern("timezone"));
524
+ sym_empty_sets = ID2SYM(rb_intern("empty_sets"));
476
525
  /* Data Conversion Options */
477
526
  opt_decimal_zero = rb_str_new2("0.0");
478
527
  rb_global_variable(&opt_decimal_zero);
@@ -2,8 +2,9 @@
2
2
  #ifndef TINYTDS_RESULT_H
3
3
  #define TINYTDS_RESULT_H
4
4
 
5
- // TODO: Is this needed?
6
- typedef tds_sysdep_int64_type DBBIGINT; /* Missing in sybdb.h ?!?! */
5
+ #ifndef DBSETLDBNAME
6
+ typedef tds_sysdep_int64_type DBBIGINT; /* For FreeTDS 0.82 */
7
+ #endif
7
8
 
8
9
  void init_tinytds_result();
9
10
  VALUE rb_tinytds_new_result_obj(DBPROCESS *c);
@@ -7,8 +7,10 @@ module TinyTds
7
7
  '100' => 2,
8
8
  '42' => 3,
9
9
  '70' => 4,
10
+ '71' => 5,
10
11
  '80' => 5,
11
- '90' => 6 # TODO: untested
12
+ '72' => 6,
13
+ '90' => 6
12
14
  }.freeze
13
15
 
14
16
  TDS_VERSIONS_GETTERS = {
@@ -21,28 +23,33 @@ module TinyTds
21
23
  6 => {:name => 'DBTDS_4_9_5', :description => '4.9.5 (NCR) SQL Server'},
22
24
  7 => {:name => 'DBTDS_5_0', :description => '5.0 SQL Server'},
23
25
  8 => {:name => 'DBTDS_7_0', :description => 'Microsoft SQL Server 7.0'},
24
- 9 => {:name => 'DBTDS_8_0', :description => 'Microsoft SQL Server 2000'},
25
- 10 => {:name => 'DBTDS_9_0', :description => 'Microsoft SQL Server 2005'}
26
+ 9 => {:name => 'DBTDS_7_1', :description => 'Microsoft SQL Server 2000'},
27
+ 10 => {:name => 'DBTDS_7_2', :description => 'Microsoft SQL Server 2005'}
26
28
  }.freeze
27
29
 
28
30
  @@default_query_options = {
29
31
  :as => :hash,
30
32
  :symbolize_keys => false,
31
33
  :cache_rows => true,
32
- :timezone => :local
34
+ :timezone => :local,
35
+ :empty_sets => true
33
36
  }
34
37
 
35
38
  attr_reader :query_options
36
39
 
37
- def self.default_query_options
38
- @@default_query_options
39
- end
40
-
41
- # Most, if not all, iconv encoding names can be found by ruby. Just in case, you can
42
- # overide this method to return a string name that Encoding.find would work with. Default
43
- # is to return the passed encoding.
44
- def self.transpose_iconv_encoding(encoding)
45
- encoding
40
+ class << self
41
+
42
+ def default_query_options
43
+ @@default_query_options
44
+ end
45
+
46
+ # Most, if not all, iconv encoding names can be found by ruby. Just in case, you can
47
+ # overide this method to return a string name that Encoding.find would work with. Default
48
+ # is to return the passed encoding.
49
+ def transpose_iconv_encoding(encoding)
50
+ encoding
51
+ end
52
+
46
53
  end
47
54
 
48
55
 
@@ -51,7 +58,7 @@ module TinyTds
51
58
  raise ArgumentError, 'missing :host option if no :dataserver given' if opts[:dataserver].to_s.empty? && opts[:host].to_s.empty?
52
59
  @query_options = @@default_query_options.dup
53
60
  opts[:appname] ||= 'TinyTds'
54
- opts[:tds_version] = TDS_VERSIONS_SETTERS[opts[:tds_version].to_s] || TDS_VERSIONS_SETTERS['80']
61
+ opts[:tds_version] = TDS_VERSIONS_SETTERS[opts[:tds_version].to_s] || TDS_VERSIONS_SETTERS['71']
55
62
  opts[:login_timeout] ||= 60
56
63
  opts[:timeout] ||= 5
57
64
  opts[:encoding] = (opts[:encoding].nil? || opts[:encoding].downcase == 'utf8') ? 'UTF-8' : opts[:encoding].upcase
@@ -1,3 +1,3 @@
1
1
  module TinyTds
2
- VERSION = '0.4.5'
2
+ VERSION = '0.5.0.rc1'
3
3
  end
@@ -2,12 +2,15 @@ require "mini_portile"
2
2
  require "rake/extensioncompiler"
3
3
 
4
4
  namespace :ports do
5
-
5
+
6
+ # If your using 0.82, you may have to make a conf file to get it to work. For example:
7
+ # $ export FREETDSCONF='/opt/local/etc/freetds/freetds.conf'
6
8
  ICONV_VERSION = "1.13.1"
7
9
  FREETDS_VERSION = ENV['TINYTDS_FREETDS_082'] ? "0.82" : "0.91"
8
10
  FREETDS_VERSION_INFO = {
9
- "0.82" => {:files => "http://ibiblio.org/pub/Linux/ALPHA/freetds/stable/freetds-stable.tgz"},
10
- "0.91" => {:files => "http://ibiblio.org/pub/Linux/ALPHA/freetds/stable/release_candidates/freetds-0.91RC2.tar.gz"} }
11
+ "0.82" => {:files => "http://ibiblio.org/pub/Linux/ALPHA/freetds/old/0.82/freetds-0.82.tar.gz"},
12
+ # "0.82" => {:files => "http://ibiblio.org/pub/Linux/ALPHA/freetds/old/0.82/freetds-patched.tgz"},
13
+ "0.91" => {:files => "http://ibiblio.org/pub/Linux/ALPHA/freetds/stable/freetds-0.91.tar.gz"} }
11
14
 
12
15
  ORIGINAL_HOST = RbConfig::CONFIG["arch"]
13
16
 
@@ -25,6 +28,7 @@ namespace :ports do
25
28
  recipe = $recipes[:libiconv]
26
29
  checkpoint = "ports/.#{recipe.name}.#{recipe.version}.#{recipe.host}.timestamp"
27
30
  unless File.exist?(checkpoint)
31
+ recipe.configure_options << "CFLAGS='-fPIC'"
28
32
  recipe.cook
29
33
  touch checkpoint
30
34
  end
@@ -39,7 +43,12 @@ namespace :ports do
39
43
  # recipe.configure_options << "--disable-debug"
40
44
  recipe.configure_options << '--sysconfdir="C:/Sites"' if recipe.host != ORIGINAL_HOST
41
45
  recipe.configure_options << "--disable-odbc"
42
- recipe.configure_options << "--with-tdsver=7.1"
46
+ if ENV['TINYTDS_FREETDS_082']
47
+ recipe.configure_options << "--with-tdsver=8.0"
48
+ else
49
+ recipe.configure_options << "--with-tdsver=7.1"
50
+ end
51
+ recipe.configure_options << "CFLAGS='-fPIC'"
43
52
  recipe.cook
44
53
  touch checkpoint
45
54
  end
@@ -84,28 +84,43 @@ end
84
84
 
85
85
  =begin
86
86
 
87
+ Query Tinytds
88
+ =============
89
+ Author: Ken Collins
90
+ Date: September 11, 2011
91
+ Summary: Benchmark TinyTds Querys
92
+
87
93
  System Information
88
94
  ------------------
89
- Operating System: Mac OS X 10.6.4 (10F569)
90
- CPU: Intel Core 2 Duo 2.4 GHz
91
- Processor Count: 2
92
- Memory: 4 GB
93
- ruby 1.8.7 (2010-08-16 patchlevel 302) [i686-darwin10.4.0]
95
+ Operating System: Mac OS X 10.7.1 (11B26)
96
+ CPU: Quad-Core Intel Xeon 2.66 GHz
97
+ Processor Count: 4
98
+ Memory: 24 GB
99
+ ruby 1.8.7 (2011-02-18 patchlevel 334) [i686-darwin11.1.0], MBARI 0x6770, Ruby Enterprise Edition 2011.03
94
100
 
95
- "Nothing" is up to 89% faster over 1,000 repetitions
96
101
  ----------------------------------------------------
97
-
98
- Nothing 0.357741832733154 secs Fastest
99
- Guids 0.460683107376099 secs 22% Slower
100
- Bits 0.483309984207153 secs 25% Slower
101
- Floats 0.505340099334717 secs 29% Slower
102
- Moneys 0.523844003677368 secs 31% Slower
103
- Integers 0.616975069046021 secs 42% Slower
104
- Binaries 0.639773845672607 secs 44% Slower
105
- Decimals 0.670897960662842 secs 46% Slower
106
- Chars 0.800287008285522 secs 55% Slower
107
- Dates 0.950634956359863 secs 62% Slower
108
- All 2.91044211387634 secs 87% Slower
102
+ (before 64bit times) (after 64bit times)
103
+ Nothing 0.287657022476196 secs Nothing 0.289273977279663 secs
104
+ Bits 0.406533002853394 secs Bits 0.424988031387329 secs
105
+ Guids 0.419962882995605 secs Guids 0.427381992340088 secs
106
+ Floats 0.452103137969971 secs Floats 0.455377101898193 secs
107
+ Moneys 0.481696844100952 secs Moneys 0.485175132751465 secs
108
+ Integers 0.496185064315796 secs Integers 0.525003910064697 secs
109
+ Binaries 0.538873195648193 secs Decimals 0.541536808013916 secs
110
+ Decimals 0.540570974349976 secs Binaries 0.542865991592407 secs
111
+ Dates 0.761389970779419 secs Dates 1.51440119743347 secs
112
+ Chars 0.793163061141968 secs Chars 0.666505098342896 secs
113
+ All 4.4630811214447 secs All 5.17242312431335 secs
109
114
 
110
115
  =end
111
116
 
117
+
118
+
119
+
120
+
121
+
122
+
123
+
124
+
125
+
126
+
@@ -25,7 +25,7 @@ class ClientTest < TinyTds::TestCase
25
25
 
26
26
  should 'have a getters for the tds version information (brittle since conf takes precedence)' do
27
27
  assert_equal 9, @client.tds_version
28
- assert_equal 'DBTDS_8_0 - Microsoft SQL Server 2000', @client.tds_version_info
28
+ assert_equal 'DBTDS_7_1 - Microsoft SQL Server 2000', @client.tds_version_info
29
29
  end
30
30
 
31
31
  should 'use UTF-8 client charset/encoding by default' do
@@ -4,7 +4,7 @@ require 'test_helper'
4
4
  class ResultTest < TinyTds::TestCase
5
5
 
6
6
  context 'Basic query and result' do
7
-
7
+
8
8
  setup do
9
9
  @@current_schema_loaded ||= load_current_schema
10
10
  @client = new_connection
@@ -110,7 +110,7 @@ class ResultTest < TinyTds::TestCase
110
110
  assert_equal text, row['varchar_50']
111
111
  end
112
112
  end
113
-
113
+
114
114
  should 'insert and find unicode data' do
115
115
  rollback_transaction(@client) do
116
116
  text = '✓'
@@ -174,7 +174,7 @@ class ResultTest < TinyTds::TestCase
174
174
  assert_equal sql_identity+1, native_identity
175
175
  end
176
176
  end
177
-
177
+
178
178
  should 'return bigint for #insert when needed' do
179
179
  rollback_transaction(@client) do
180
180
  seed = 9223372036854775805
@@ -326,9 +326,20 @@ class ResultTest < TinyTds::TestCase
326
326
  end
327
327
 
328
328
  context 'with multiple result sets' do
329
-
329
+
330
330
  setup do
331
- @double_select = "SELECT 1 AS [rs1]\nSELECT 2 AS [rs2]"
331
+ @empty_select = "SELECT 1 AS [rs1] WHERE 1 = 0"
332
+ @double_select = "SELECT 1 AS [rs1]
333
+ SELECT 2 AS [rs2]"
334
+ @triple_select_1st_empty = "SELECT 1 AS [rs1] WHERE 1 = 0
335
+ SELECT 2 AS [rs2]
336
+ SELECT 3 AS [rs3]"
337
+ @triple_select_2nd_empty = "SELECT 1 AS [rs1]
338
+ SELECT 2 AS [rs2] WHERE 1 = 0
339
+ SELECT 3 AS [rs3]"
340
+ @triple_select_3rd_empty = "SELECT 1 AS [rs1]
341
+ SELECT 2 AS [rs2]
342
+ SELECT 3 AS [rs3] WHERE 1 = 0"
332
343
  end
333
344
 
334
345
  should 'handle a command buffer with double selects' do
@@ -337,7 +348,7 @@ class ResultTest < TinyTds::TestCase
337
348
  assert_equal 2, result_sets.size
338
349
  assert_equal [{'rs1' => 1}], result_sets.first
339
350
  assert_equal [{'rs2' => 2}], result_sets.last
340
- assert_equal [['rs1'],['rs2']], result.fields
351
+ assert_equal [['rs1'], ['rs2']], result.fields
341
352
  assert_equal result.each.object_id, result.each.object_id, 'same cached rows'
342
353
  # As array
343
354
  result = @client.execute(@double_select)
@@ -345,7 +356,7 @@ class ResultTest < TinyTds::TestCase
345
356
  assert_equal 2, result_sets.size
346
357
  assert_equal [[1]], result_sets.first
347
358
  assert_equal [[2]], result_sets.last
348
- assert_equal [['rs1'],['rs2']], result.fields
359
+ assert_equal [['rs1'], ['rs2']], result.fields
349
360
  assert_equal result.each.object_id, result.each.object_id, 'same cached rows'
350
361
  end
351
362
 
@@ -365,23 +376,160 @@ class ResultTest < TinyTds::TestCase
365
376
  assert constraint_info.key?("constraint_name")
366
377
  end
367
378
 
368
- should 'ignore empty result sets' do
369
- rollback_transaction(@client) do
370
- @client.execute("DELETE FROM [datatypes]").do
371
- id = @client.execute("INSERT INTO [datatypes] ([varchar_50]) VALUES ('test empty result sets')").insert
372
- sql = %|
373
- SET NOCOUNT ON
374
- DECLARE @row_number TABLE (row int identity(1,1), id int)
375
- INSERT INTO @row_number (id)
376
- SELECT [datatypes].[id] FROM [datatypes]
377
- SET NOCOUNT OFF
378
- SELECT id FROM @row_number|
379
- result = @client.execute(sql)
380
- result.each.must_equal [{"id"=>id}]
381
- result.fields.must_equal ['id']
379
+ context 'using :empty_sets TRUE' do
380
+
381
+ setup do
382
+ @old_query_option_value = TinyTds::Client.default_query_options[:empty_sets]
383
+ TinyTds::Client.default_query_options[:empty_sets] = true
384
+ @client = new_connection
382
385
  end
386
+
387
+ teardown do
388
+ TinyTds::Client.default_query_options[:empty_sets] = @old_query_option_value
389
+ end
390
+
391
+ should 'handle a basic empty result set' do
392
+ result = @client.execute(@empty_select)
393
+ assert_equal [], result.each
394
+ assert_equal ['rs1'], result.fields
395
+ end
396
+
397
+ should 'include empty result sets by default - using 1st empty buffer' do
398
+ result = @client.execute(@triple_select_1st_empty)
399
+ result_sets = result.each
400
+ assert_equal 3, result_sets.size
401
+ assert_equal [], result_sets[0]
402
+ assert_equal [{'rs2' => 2}], result_sets[1]
403
+ assert_equal [{'rs3' => 3}], result_sets[2]
404
+ assert_equal [['rs1'], ['rs2'], ['rs3']], result.fields
405
+ assert_equal result.each.object_id, result.each.object_id, 'same cached rows'
406
+ # As array
407
+ result = @client.execute(@triple_select_1st_empty)
408
+ result_sets = result.each(:as => :array)
409
+ assert_equal 3, result_sets.size
410
+ assert_equal [], result_sets[0]
411
+ assert_equal [[2]], result_sets[1]
412
+ assert_equal [[3]], result_sets[2]
413
+ assert_equal [['rs1'], ['rs2'], ['rs3']], result.fields
414
+ assert_equal result.each.object_id, result.each.object_id, 'same cached rows'
415
+ end
416
+
417
+ should 'include empty result sets by default - using 2nd empty buffer' do
418
+ result = @client.execute(@triple_select_2nd_empty)
419
+ result_sets = result.each
420
+ assert_equal 3, result_sets.size
421
+ assert_equal [{'rs1' => 1}], result_sets[0]
422
+ assert_equal [], result_sets[1]
423
+ assert_equal [{'rs3' => 3}], result_sets[2]
424
+ assert_equal [['rs1'], ['rs2'], ['rs3']], result.fields
425
+ assert_equal result.each.object_id, result.each.object_id, 'same cached rows'
426
+ # As array
427
+ result = @client.execute(@triple_select_2nd_empty)
428
+ result_sets = result.each(:as => :array)
429
+ assert_equal 3, result_sets.size
430
+ assert_equal [[1]], result_sets[0]
431
+ assert_equal [], result_sets[1]
432
+ assert_equal [[3]], result_sets[2]
433
+ assert_equal [['rs1'], ['rs2'], ['rs3']], result.fields
434
+ assert_equal result.each.object_id, result.each.object_id, 'same cached rows'
435
+ end
436
+
437
+ should 'include empty result sets by default - using 3rd empty buffer' do
438
+ result = @client.execute(@triple_select_3rd_empty)
439
+ result_sets = result.each
440
+ assert_equal 3, result_sets.size
441
+ assert_equal [{'rs1' => 1}], result_sets[0]
442
+ assert_equal [{'rs2' => 2}], result_sets[1]
443
+ assert_equal [], result_sets[2]
444
+ assert_equal [['rs1'], ['rs2'], ['rs3']], result.fields
445
+ assert_equal result.each.object_id, result.each.object_id, 'same cached rows'
446
+ # As array
447
+ result = @client.execute(@triple_select_3rd_empty)
448
+ result_sets = result.each(:as => :array)
449
+ assert_equal 3, result_sets.size
450
+ assert_equal [[1]], result_sets[0]
451
+ assert_equal [[2]], result_sets[1]
452
+ assert_equal [], result_sets[2]
453
+ assert_equal [['rs1'], ['rs2'], ['rs3']], result.fields
454
+ assert_equal result.each.object_id, result.each.object_id, 'same cached rows'
455
+ end
456
+
383
457
  end
384
-
458
+
459
+ context 'using :empty_sets FALSE' do
460
+
461
+ setup do
462
+ @old_query_option_value = TinyTds::Client.default_query_options[:empty_sets]
463
+ TinyTds::Client.default_query_options[:empty_sets] = false
464
+ @client = new_connection
465
+ end
466
+
467
+ teardown do
468
+ TinyTds::Client.default_query_options[:empty_sets] = @old_query_option_value
469
+ end
470
+
471
+ should 'handle a basic empty result set' do
472
+ result = @client.execute(@empty_select)
473
+ assert_equal [], result.each
474
+ assert_equal ['rs1'], result.fields
475
+ end
476
+
477
+ should 'not include empty result sets by default - using 1st empty buffer' do
478
+ result = @client.execute(@triple_select_1st_empty)
479
+ result_sets = result.each
480
+ assert_equal 2, result_sets.size
481
+ assert_equal [{'rs2' => 2}], result_sets[0]
482
+ assert_equal [{'rs3' => 3}], result_sets[1]
483
+ assert_equal [['rs2'], ['rs3']], result.fields
484
+ assert_equal result.each.object_id, result.each.object_id, 'same cached rows'
485
+ # As array
486
+ result = @client.execute(@triple_select_1st_empty)
487
+ result_sets = result.each(:as => :array)
488
+ assert_equal 2, result_sets.size
489
+ assert_equal [[2]], result_sets[0]
490
+ assert_equal [[3]], result_sets[1]
491
+ assert_equal [['rs2'], ['rs3']], result.fields
492
+ assert_equal result.each.object_id, result.each.object_id, 'same cached rows'
493
+ end
494
+
495
+ should 'not include empty result sets by default - using 2nd empty buffer' do
496
+ result = @client.execute(@triple_select_2nd_empty)
497
+ result_sets = result.each
498
+ assert_equal 2, result_sets.size
499
+ assert_equal [{'rs1' => 1}], result_sets[0]
500
+ assert_equal [{'rs3' => 3}], result_sets[1]
501
+ assert_equal [['rs1'], ['rs3']], result.fields
502
+ assert_equal result.each.object_id, result.each.object_id, 'same cached rows'
503
+ # As array
504
+ result = @client.execute(@triple_select_2nd_empty)
505
+ result_sets = result.each(:as => :array)
506
+ assert_equal 2, result_sets.size
507
+ assert_equal [[1]], result_sets[0]
508
+ assert_equal [[3]], result_sets[1]
509
+ assert_equal [['rs1'], ['rs3']], result.fields
510
+ assert_equal result.each.object_id, result.each.object_id, 'same cached rows'
511
+ end
512
+
513
+ should 'not include empty result sets by default - using 3rd empty buffer' do
514
+ result = @client.execute(@triple_select_3rd_empty)
515
+ result_sets = result.each
516
+ assert_equal 2, result_sets.size
517
+ assert_equal [{'rs1' => 1}], result_sets[0]
518
+ assert_equal [{'rs2' => 2}], result_sets[1]
519
+ assert_equal [['rs1'], ['rs2']], result.fields
520
+ assert_equal result.each.object_id, result.each.object_id, 'same cached rows'
521
+ # As array
522
+ result = @client.execute(@triple_select_3rd_empty)
523
+ result_sets = result.each(:as => :array)
524
+ assert_equal 2, result_sets.size
525
+ assert_equal [[1]], result_sets[0]
526
+ assert_equal [[2]], result_sets[1]
527
+ assert_equal [['rs1'], ['rs2']], result.fields
528
+ assert_equal result.each.object_id, result.each.object_id, 'same cached rows'
529
+ end
530
+
531
+ end
532
+
385
533
  end
386
534
 
387
535
  context 'when casting to native ruby values' do
@@ -398,6 +546,28 @@ class ResultTest < TinyTds::TestCase
398
546
 
399
547
  end
400
548
 
549
+ context 'with data type' do
550
+
551
+ context 'char max' do
552
+
553
+ setup do
554
+ @big_text = 'x' * 2_000_000
555
+ @old_textsize = @client.execute("SELECT @@TEXTSIZE AS [textsize]").each.first['textsize'].inspect
556
+ @client.execute("SET TEXTSIZE #{(@big_text.length*2)+1}").do
557
+ end
558
+
559
+ should 'insert and select large varchar_max' do
560
+ insert_and_select_datatype :varchar_max
561
+ end
562
+
563
+ should 'insert and select large nvarchar_max' do
564
+ insert_and_select_datatype :nvarchar_max
565
+ end
566
+
567
+ end unless sqlserver_2000?
568
+
569
+ end
570
+
401
571
  context 'when shit happens' do
402
572
 
403
573
  should 'cope with nil or empty buffer' do
@@ -405,6 +575,21 @@ class ResultTest < TinyTds::TestCase
405
575
  assert_equal [], @client.execute('').each
406
576
  end
407
577
 
578
+ should 'not raise an error when severity is 10 or less' do
579
+ (1..10).to_a.each do |severity|
580
+ @client.execute("RAISERROR(N'Test #{severity} severity', #{severity}, 1)").do
581
+ end
582
+ end
583
+
584
+ should 'raise an error when severity is greater than 10' do
585
+ action = lambda { @client.execute("RAISERROR(N'Test 11 severity', 11, 1)").do }
586
+ assert_raise_tinytds_error(action) do |e|
587
+ assert_equal "Test 11 severity", e.message
588
+ assert_equal 11, e.severity
589
+ assert_equal 50000, e.db_error_number
590
+ end
591
+ end
592
+
408
593
  should 'throw an error when you execute another query with other results pending' do
409
594
  result1 = @client.execute(@query1)
410
595
  action = lambda { @client.execute(@query1) }
@@ -414,7 +599,7 @@ class ResultTest < TinyTds::TestCase
414
599
  assert_equal 20019, e.db_error_number
415
600
  end
416
601
  end
417
-
602
+
418
603
  should 'error gracefully with bad table name' do
419
604
  action = lambda { @client.execute('SELECT * FROM [foobar]').each }
420
605
  assert_raise_tinytds_error(action) do |e|
@@ -425,7 +610,7 @@ class ResultTest < TinyTds::TestCase
425
610
  assert_followup_query
426
611
  end
427
612
 
428
- should 'error gracefully with invalid syntax' do
613
+ should 'error gracefully with incorrect syntax' do
429
614
  action = lambda { @client.execute('this will not work').each }
430
615
  assert_raise_tinytds_error(action) do |e|
431
616
  assert_match %r|incorrect syntax|i, e.message
@@ -434,6 +619,20 @@ class ResultTest < TinyTds::TestCase
434
619
  end
435
620
  assert_followup_query
436
621
  end
622
+
623
+ should 'error gracefully with incorrect syntax in sp_executesql' do
624
+ if @client.freetds_091_or_higer?
625
+ action = lambda { @client.execute("EXEC sp_executesql N'this will not work'").each }
626
+ assert_raise_tinytds_error(action) do |e|
627
+ assert_match %r|incorrect syntax|i, e.message
628
+ assert_equal 15, e.severity
629
+ assert_equal 156, e.db_error_number
630
+ end
631
+ assert_followup_query
632
+ else
633
+ skip 'FreeTDS 0.91 and higher can only pass this test.'
634
+ end
635
+ end
437
636
 
438
637
  end
439
638
 
@@ -447,5 +646,14 @@ class ResultTest < TinyTds::TestCase
447
646
  assert_equal 1, result.each.first['one']
448
647
  end
449
648
 
649
+ def insert_and_select_datatype(datatype)
650
+ rollback_transaction(@client) do
651
+ @client.execute("DELETE FROM [datatypes] WHERE [#{datatype}] IS NOT NULL").do
652
+ id = @client.execute("INSERT INTO [datatypes] ([#{datatype}]) VALUES (N'#{@big_text}')").insert
653
+ found_text = find_value id, datatype
654
+ flunk "Large #{datatype} data with a length of #{@big_text.length} did not match found text with length of #{found_text.length}" unless @big_text == found_text
655
+ end
656
+ end
657
+
450
658
  end
451
659
 
@@ -39,40 +39,77 @@ class SchemaTest < TinyTds::TestCase
39
39
  end
40
40
 
41
41
  should 'cast datetime' do
42
- # 1753-01-01T00:00:00.000
43
- v = find_value 61, :datetime
44
- assert_instance_of DateTime, v, 'not in range of Time class'
45
- assert_equal 1753, v.year
46
- assert_equal 01, v.month
47
- assert_equal 01, v.day
48
- assert_equal 0, v.hour
49
- assert_equal 0, v.min
50
- assert_equal 0, v.sec
51
- assert_equal 0, v.usec
52
- # 9999-12-31T23:59:59.997
53
- v = find_value 62, :datetime
54
- assert_instance_of DateTime, v, 'not in range of Time class'
55
- assert_equal 9999, v.year
56
- assert_equal 12, v.month
57
- assert_equal 31, v.day
58
- assert_equal 23, v.hour
59
- assert_equal 59, v.min
60
- assert_equal 59, v.sec
61
- assert_equal 997000, v.usec unless ruby186?
62
- assert_equal local_offset, find_value(61, :datetime, :timezone => :local).offset
63
- assert_equal 0, find_value(61, :datetime, :timezone => :utc).offset
64
- # 2010-01-01T12:34:56.123
65
- v = find_value 63, :datetime
66
- assert_instance_of Time, v, 'in range of Time class'
67
- assert_equal 2010, v.year
68
- assert_equal 01, v.month
69
- assert_equal 01, v.day
70
- assert_equal 12, v.hour
71
- assert_equal 34, v.min
72
- assert_equal 56, v.sec
73
- assert_equal 123000, v.usec
74
- assert_equal utc_offset, find_value(63, :datetime, :timezone => :local).utc_offset
75
- assert_equal 0, find_value(63, :datetime, :timezone => :utc).utc_offset
42
+ if ruby18? && ruby32bit?
43
+ # 1753-01-01T00:00:00.000
44
+ v = find_value 61, :datetime
45
+ assert_instance_of DateTime, v, 'not in range of Time class'
46
+ assert_equal 1753, v.year
47
+ assert_equal 01, v.month
48
+ assert_equal 01, v.day
49
+ assert_equal 0, v.hour
50
+ assert_equal 0, v.min
51
+ assert_equal 0, v.sec
52
+ assert_equal 0, v.usec
53
+ # 9999-12-31T23:59:59.997
54
+ v = find_value 62, :datetime
55
+ assert_instance_of DateTime, v, 'not in range of Time class'
56
+ assert_equal 9999, v.year
57
+ assert_equal 12, v.month
58
+ assert_equal 31, v.day
59
+ assert_equal 23, v.hour
60
+ assert_equal 59, v.min
61
+ assert_equal 59, v.sec
62
+ assert_equal 997000, v.usec unless ruby186?
63
+ assert_equal local_offset, find_value(62, :datetime, :timezone => :local).offset
64
+ assert_equal 0, find_value(62, :datetime, :timezone => :utc).offset
65
+ # 2010-01-01T12:34:56.123
66
+ v = find_value 63, :datetime
67
+ assert_instance_of Time, v, 'in range of Time class'
68
+ assert_equal 2010, v.year
69
+ assert_equal 01, v.month
70
+ assert_equal 01, v.day
71
+ assert_equal 12, v.hour
72
+ assert_equal 34, v.min
73
+ assert_equal 56, v.sec
74
+ assert_equal 123000, v.usec
75
+ assert_equal utc_offset, find_value(63, :datetime, :timezone => :local).utc_offset
76
+ assert_equal 0, find_value(63, :datetime, :timezone => :utc).utc_offset
77
+ else
78
+ # 1753-01-01T00:00:00.000
79
+ v = find_value 61, :datetime
80
+ assert_instance_of Time, v, 'not in range of Time class'
81
+ assert_equal 1753, v.year
82
+ assert_equal 01, v.month
83
+ assert_equal 01, v.day
84
+ assert_equal 0, v.hour
85
+ assert_equal 0, v.min
86
+ assert_equal 0, v.sec
87
+ assert_equal 0, v.usec
88
+ # 9999-12-31T23:59:59.997
89
+ v = find_value 62, :datetime
90
+ assert_instance_of Time, v, 'not in range of Time class'
91
+ assert_equal 9999, v.year
92
+ assert_equal 12, v.month
93
+ assert_equal 31, v.day
94
+ assert_equal 23, v.hour
95
+ assert_equal 59, v.min
96
+ assert_equal 59, v.sec
97
+ assert_equal 997000, v.usec unless ruby186?
98
+ assert_equal utc_offset, find_value(62, :datetime, :timezone => :local).utc_offset
99
+ assert_equal 0, find_value(62, :datetime, :timezone => :utc).utc_offset
100
+ # 2010-01-01T12:34:56.123
101
+ v = find_value 63, :datetime
102
+ assert_instance_of Time, v, 'in range of Time class'
103
+ assert_equal 2010, v.year
104
+ assert_equal 01, v.month
105
+ assert_equal 01, v.day
106
+ assert_equal 12, v.hour
107
+ assert_equal 34, v.min
108
+ assert_equal 56, v.sec
109
+ assert_equal 123000, v.usec
110
+ assert_equal utc_offset, find_value(63, :datetime, :timezone => :local).utc_offset
111
+ assert_equal 0, find_value(63, :datetime, :timezone => :utc).utc_offset
112
+ end
76
113
  end
77
114
 
78
115
  should 'cast decimal' do
@@ -146,28 +183,53 @@ class SchemaTest < TinyTds::TestCase
146
183
  end
147
184
 
148
185
  should 'cast smalldatetime' do
149
- # 1901-01-01 15:45:00
150
- v = find_value 231, :smalldatetime
151
- assert_instance_of DateTime, v
152
- assert_equal 1901, v.year
153
- assert_equal 01, v.month
154
- assert_equal 01, v.day
155
- assert_equal 15, v.hour
156
- assert_equal 45, v.min
157
- assert_equal 00, v.sec
158
- assert_equal local_offset, find_value(231, :smalldatetime, :timezone => :local).offset
159
- assert_equal 0, find_value(231, :smalldatetime, :timezone => :utc).offset
160
- # 2078-06-05 04:20:00
161
- v = find_value 232, :smalldatetime
162
- assert_instance_of DateTime, v
163
- assert_equal 2078, v.year
164
- assert_equal 06, v.month
165
- assert_equal 05, v.day
166
- assert_equal 04, v.hour
167
- assert_equal 20, v.min
168
- assert_equal 00, v.sec
169
- assert_equal local_offset, find_value(232, :smalldatetime, :timezone => :local).offset
170
- assert_equal 0, find_value(232, :smalldatetime, :timezone => :utc).offset
186
+ if ruby18? && ruby32bit?
187
+ # 1901-01-01 15:45:00
188
+ v = find_value 231, :smalldatetime
189
+ assert_instance_of DateTime, v
190
+ assert_equal 1901, v.year
191
+ assert_equal 01, v.month
192
+ assert_equal 01, v.day
193
+ assert_equal 15, v.hour
194
+ assert_equal 45, v.min
195
+ assert_equal 00, v.sec
196
+ assert_equal local_offset, find_value(231, :smalldatetime, :timezone => :local).offset
197
+ assert_equal 0, find_value(231, :smalldatetime, :timezone => :utc).offset
198
+ # 2078-06-05 04:20:00
199
+ v = find_value 232, :smalldatetime
200
+ assert_instance_of DateTime, v
201
+ assert_equal 2078, v.year
202
+ assert_equal 06, v.month
203
+ assert_equal 05, v.day
204
+ assert_equal 04, v.hour
205
+ assert_equal 20, v.min
206
+ assert_equal 00, v.sec
207
+ assert_equal local_offset, find_value(232, :smalldatetime, :timezone => :local).offset
208
+ assert_equal 0, find_value(232, :smalldatetime, :timezone => :utc).offset
209
+ else
210
+ # 1901-01-01 15:45:00
211
+ v = find_value 231, :smalldatetime
212
+ assert_instance_of Time, v
213
+ assert_equal 1901, v.year
214
+ assert_equal 01, v.month
215
+ assert_equal 01, v.day
216
+ assert_equal 15, v.hour
217
+ assert_equal 45, v.min
218
+ assert_equal 00, v.sec
219
+ assert_equal Time.local(1901).utc_offset, find_value(231, :smalldatetime, :timezone => :local).utc_offset
220
+ assert_equal 0, find_value(231, :smalldatetime, :timezone => :utc).utc_offset
221
+ # 2078-06-05 04:20:00
222
+ v = find_value 232, :smalldatetime
223
+ assert_instance_of Time, v
224
+ assert_equal 2078, v.year
225
+ assert_equal 06, v.month
226
+ assert_equal 05, v.day
227
+ assert_equal 04, v.hour
228
+ assert_equal 20, v.min
229
+ assert_equal 00, v.sec
230
+ assert_equal Time.local(2078,6).utc_offset, find_value(232, :smalldatetime, :timezone => :local).utc_offset
231
+ assert_equal 0, find_value(232, :smalldatetime, :timezone => :utc).utc_offset
232
+ end
171
233
  end
172
234
 
173
235
  should 'cast smallint' do
@@ -242,33 +304,54 @@ class SchemaTest < TinyTds::TestCase
242
304
 
243
305
  context 'for 2008 and up' do
244
306
 
245
- should 'cast date' do
246
-
247
- end
248
-
249
- should 'cast datetime2' do
250
-
251
- end
252
-
253
- should 'cast datetimeoffset' do
254
-
255
- end
256
-
257
- should 'cast geography' do
258
-
259
- end
260
-
261
- should 'cast geometry' do
262
-
263
- end
264
-
265
- should 'cast hierarchyid' do
266
-
267
- end
268
-
269
- should 'cast time' do
270
-
271
- end
307
+ # These data types always come back as SYBTEXT and there is no way I can
308
+ # find out the column's human readable name.
309
+ #
310
+ # * [date]
311
+ # * [datetime2]
312
+ # * [datetimeoffset]
313
+ # * [time]
314
+ #
315
+ # I have tried the following and I only get back either "char" or 0/null.
316
+ #
317
+ # rb_warn("SYBTEXT: dbprtype: %s", dbprtype(coltype));
318
+ # rb_warn("SYBTEXT: dbcolutype: %s", dbcolutype(rwrap->client, col));
319
+ # rb_warn("SYBTEXT: dbcolutype: %ld", dbcolutype(rwrap->client, col));
320
+
321
+ # should 'cast date' do
322
+ # value = find_value 51, :date
323
+ # assert_equal '', value
324
+ # end
325
+ #
326
+ # should 'cast datetime2' do
327
+ # value = find_value 72, :datetime2_7
328
+ # assert_equal '', value
329
+ # end
330
+ #
331
+ # should 'cast datetimeoffset' do
332
+ # value = find_value 81, :datetimeoffset_2
333
+ # assert_equal '', value
334
+ # end
335
+ #
336
+ # should 'cast geography' do
337
+ # value = find_value 111, :geography
338
+ # assert_equal '', value
339
+ # end
340
+ #
341
+ # should 'cast geometry' do
342
+ # value = find_value 121, :geometry
343
+ # assert_equal '', value
344
+ # end
345
+ #
346
+ # should 'cast hierarchyid' do
347
+ # value = find_value 131, :hierarchyid
348
+ # assert_equal '', value
349
+ # end
350
+ #
351
+ # should 'cast time' do
352
+ # value = find_value 283, :time_7
353
+ # assert_equal '', value
354
+ # end
272
355
 
273
356
  end if sqlserver_2008? || sqlserver_azure?
274
357
 
@@ -127,6 +127,10 @@ module TinyTds
127
127
  RUBY_VERSION == '1.9.2'
128
128
  end
129
129
 
130
+ def ruby32bit?
131
+ 1.size == 4
132
+ end
133
+
130
134
  def rubyRbx?
131
135
  RUBY_DESCRIPTION =~ /rubinius/i
132
136
  end
metadata CHANGED
@@ -1,13 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tiny_tds
3
3
  version: !ruby/object:Gem::Version
4
- hash: 5
5
- prerelease:
4
+ hash: 15424071
5
+ prerelease: 6
6
6
  segments:
7
7
  - 0
8
- - 4
9
8
  - 5
10
- version: 0.4.5
9
+ - 0
10
+ - rc
11
+ - 1
12
+ version: 0.5.0.rc1
11
13
  platform: ruby
12
14
  authors:
13
15
  - Ken Collins
@@ -16,7 +18,7 @@ autorequire:
16
18
  bindir: bin
17
19
  cert_chain: []
18
20
 
19
- date: 2011-05-24 00:00:00 -04:00
21
+ date: 2011-09-12 00:00:00 -04:00
20
22
  default_executable:
21
23
  dependencies: []
22
24
 
@@ -83,12 +85,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
83
85
  required_rubygems_version: !ruby/object:Gem::Requirement
84
86
  none: false
85
87
  requirements:
86
- - - ">="
88
+ - - ">"
87
89
  - !ruby/object:Gem::Version
88
- hash: 3
90
+ hash: 25
89
91
  segments:
90
- - 0
91
- version: "0"
92
+ - 1
93
+ - 3
94
+ - 1
95
+ version: 1.3.1
92
96
  requirements: []
93
97
 
94
98
  rubyforge_project: