do_sqlserver-tinytds 0.10.17.alpha

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/.gitignore +3 -0
  5. data/.rspec +2 -0
  6. data/CONNECTING.markdown +40 -0
  7. data/ChangeLog.markdown +56 -0
  8. data/FAQS.markdown +8 -0
  9. data/Gemfile +3 -0
  10. data/Gemfile.lock +30 -0
  11. data/INSTALL.markdown +76 -0
  12. data/LICENSE +21 -0
  13. data/README.md +95 -0
  14. data/Rakefile +7 -0
  15. data/buildfile +27 -0
  16. data/do_sqlserver_tinytds.gemspec +31 -0
  17. data/lib/do_sqlserver.rb +1 -0
  18. data/lib/do_sqlserver_tinytds.rb +360 -0
  19. data/lib/do_sqlserver_tinytds/addressable_extension.rb +69 -0
  20. data/lib/do_sqlserver_tinytds/tiny_tds_extension.rb +123 -0
  21. data/lib/do_sqlserver_tinytds/transaction.rb +36 -0
  22. data/lib/do_sqlserver_tinytds/version.rb +5 -0
  23. data/spec/command_spec.rb +9 -0
  24. data/spec/connection_spec.rb +21 -0
  25. data/spec/encoding_spec.rb +8 -0
  26. data/spec/reader_spec.rb +8 -0
  27. data/spec/result_spec.rb +16 -0
  28. data/spec/spec_helper.rb +155 -0
  29. data/spec/typecast/array_spec.rb +8 -0
  30. data/spec/typecast/bigdecimal_spec.rb +9 -0
  31. data/spec/typecast/boolean_spec.rb +9 -0
  32. data/spec/typecast/byte_array_spec.rb +31 -0
  33. data/spec/typecast/class_spec.rb +8 -0
  34. data/spec/typecast/date_spec.rb +11 -0
  35. data/spec/typecast/datetime_spec.rb +9 -0
  36. data/spec/typecast/float_spec.rb +9 -0
  37. data/spec/typecast/integer_spec.rb +8 -0
  38. data/spec/typecast/nil_spec.rb +10 -0
  39. data/spec/typecast/other_spec.rb +8 -0
  40. data/spec/typecast/range_spec.rb +8 -0
  41. data/spec/typecast/string_spec.rb +8 -0
  42. data/spec/typecast/time_spec.rb +8 -0
  43. metadata +169 -0
  44. metadata.gz.sig +3 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e4350cba1497e5dedf23f8f292ade5f41f37f0c5
4
+ data.tar.gz: 0d713306670222a6eebe2bc2776f41bd11fd8dcb
5
+ SHA512:
6
+ metadata.gz: 26a8cb0d7faa67fffdd71e27ba35a1d2eaf96adcc1697a1cb7c873215229a966c015974b11ebd83ff3fa65bb7b2d56d2003c6a2486b63aa99be9d987f06f2d7e
7
+ data.tar.gz: f41621cfb2ec8ed5424484d0d17b8022bf07e941adc4beaf66caa0c5c3a4c331fc3c59aaa0990c26063bc1be936e93e4f1ed73cc11e2869bafdef1176f60450f
Binary file
Binary file
@@ -0,0 +1,3 @@
1
+ .idea/
2
+ .bundle/
3
+ pkg/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --colour
@@ -0,0 +1,40 @@
1
+ CONNECTING
2
+ ==========
3
+
4
+ Various notes (needs to be cleaned-up).
5
+
6
+ See:
7
+ * <http://blogs.msdn.com/sqlexpress/archive/2004/07/23/192044.aspx>
8
+ * <http://blogs.msdn.com/b/bethmassi/archive/2008/09/17/enabling-remote-sql-express-2008-network-connections-on-vista.aspx>
9
+
10
+
11
+ Example Setup
12
+ -------------
13
+
14
+ * In Visual Studio, click Server Explorer
15
+ * Right click Data Connections
16
+ * Create New SQL Server Database
17
+ * Server Name: YOURPCNAME\SQLEXPRESS
18
+ * Use Windows Authentication
19
+ * #Use SQL Server Authentication
20
+ * #User name: do_test
21
+ * #Password: do_test
22
+ * New database name: do_test
23
+
24
+ See:
25
+ <http://social.msdn.microsoft.com/Forums/en-US/Vsexpressinstall/thread/aaf2f68c-4a40-44c8-b7ee-b2f5d94e23c3>
26
+
27
+ ---
28
+
29
+
30
+ Tips
31
+ ----
32
+
33
+ * Check the password is not required to be set on first connect.
34
+ * Test you can connect locally. Either through Visual Studio SQL Server tools,
35
+ SQL Server Management Studio, or simply using telnet: `telnet localhost 4322`.
36
+ * Configure Firewall correctly: http://support.microsoft.com/kb/287932
37
+ * Specify an instance: in the DO URL append `INSTANCE=SQLEXPRESS`.
38
+ * You can not add the instance to the hostname, i.e. `192.168.2.110\SQLEXPRESS`
39
+ as the underlying jTDS driver needs a URL that looks like this:
40
+ `jdbc:jtds:sqlserver://192.168.2.110:1433/do_test;instance=SQLEXPRESS`.
@@ -0,0 +1,56 @@
1
+ ## 0.10.7 2011-10-13
2
+
3
+ No changes
4
+
5
+ ## 0.10.6 2011-05-22
6
+
7
+ No changes
8
+
9
+ ## 0.10.5 2011-05-03
10
+
11
+ No changes
12
+
13
+ ## 0.10.4 2011-04-28
14
+
15
+ New features
16
+ * Add save point to transactions (all)
17
+ * JRuby 1.9 mode support (encodings etc.)
18
+
19
+ Bugfixes
20
+ * Fix bug when using nested transactions in concurrent scenarios (all)
21
+ * Use column aliases instead of names (jruby)
22
+
23
+ Other
24
+ * Switch back to RSpec
25
+
26
+ ## 0.10.3 2011-01-30
27
+ * No changes
28
+
29
+ ## 0.10.2 2010-05-19
30
+ * No changes
31
+
32
+ ## 0.10.1 2010-01-08
33
+
34
+ Initial release as part of mainline DataObjects project.
35
+
36
+ * Switch to Jeweler for Gem building tasks (this change may be temporary).
37
+ * Switch to using Bacon for running specs: This should make specs friendlier to
38
+ new Ruby implementations that are not yet 100% MRI-compatible, and in turn,
39
+ pave the road for our own IronRuby and MacRuby support.
40
+ * Switch to the newly added rake-compiler `JavaExtensionTask` for compiling
41
+ JRuby extensions, instead of our (broken) home-grown solution.
42
+
43
+ * Known Issues:
44
+ * Writing Extlib::ByteArray is not currently supported.
45
+
46
+ ## 0.10.0 2009-09-15
47
+
48
+ (NOT RELEASED)
49
+
50
+ * Improvements
51
+ * JRuby Support (using *do_jdbc*)
52
+
53
+ ## 0.0.1 2009-05-11
54
+
55
+ * 1 major enhancement:
56
+ * Initial release
@@ -0,0 +1,8 @@
1
+ FAQS
2
+ ====
3
+
4
+ * Can I use Microsoft's SQL Server driver instead of jTDS?
5
+
6
+ No, not currently. Currently the DataObjects Driver implementation is tied not
7
+ only to a relational database, but to its JDBC driver implementation. You could
8
+ create an alternate database implementation.
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
@@ -0,0 +1,30 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ do_sqlserver-tinytds (0.10.15)
5
+ data_objects (~> 0.10.13)
6
+ tiny_tds (~> 0.5)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ addressable (2.3.4)
12
+ data_objects (0.10.13)
13
+ addressable (~> 2.1)
14
+ diff-lcs (1.1.3)
15
+ rspec (2.7.0)
16
+ rspec-core (~> 2.7.0)
17
+ rspec-expectations (~> 2.7.0)
18
+ rspec-mocks (~> 2.7.0)
19
+ rspec-core (2.7.1)
20
+ rspec-expectations (2.7.0)
21
+ diff-lcs (~> 1.1.2)
22
+ rspec-mocks (2.7.0)
23
+ tiny_tds (0.5.1)
24
+
25
+ PLATFORMS
26
+ ruby
27
+
28
+ DEPENDENCIES
29
+ do_sqlserver-tinytds!
30
+ rspec (~> 2.5)
@@ -0,0 +1,76 @@
1
+ INSTALLING
2
+ ==========
3
+
4
+ JRuby variant driver
5
+ --------------------
6
+
7
+ * Install the jTDS JDBC Driver. For your convenience, DO packages it as a Ruby
8
+ Gem. If installing from source, `cd` to `../jdbc_drivers/sqlserver/` and run
9
+ `rake install`.
10
+ * There are currently no other prerequisites for installation.
11
+
12
+ 1.8.6/7 (MRI) and 1.9.x (YARV) variant
13
+ --------------------------------------
14
+
15
+ 1. Install the DBI and DBD::ODBC dependencies:
16
+
17
+ sudo gem install dbi
18
+ sudo gem install dbd-odbc
19
+
20
+ 2. Install [ODBC Binding for Ruby][rubyodbc]. It it *not* currently available
21
+ via RubyGems, so you'll have to download the source tarball:
22
+
23
+ wget http://www.ch-werner.de/rubyodbc/ruby-odbc-0.9997.tar.gz
24
+ tar xvfz ruby-odbc-0.9997.tar.gz
25
+ cd ruby-odbc-0.9997
26
+
27
+
28
+ 3. Ensure you read the accompanying `COPYING` file, as the ODBC Binding for Ruby
29
+ is licensed under the GPL, unlike DataObjects. To install read the
30
+ accompanying `INSTALL` file. On a recent variant of OS X, installation
31
+ should look like this:
32
+
33
+ ruby extconf.rb
34
+ make
35
+ sudo make install
36
+
37
+ 4. You must also have FreeTDS or [unixODBC][unixodbc] (SQL Server license for
38
+ unixODBC is commercially licensed though) installed.
39
+
40
+ 5. To install FreeTDS on OS X, you can use MacPorts:
41
+
42
+ sudo port install freetds
43
+
44
+ ****************************************************************
45
+ Configuration file freetds.conf does not exist and has been created using
46
+ /opt/local/etc/freetds/freetds.conf.sample
47
+ Configuration file locales.conf does not exist and has been created using
48
+ /opt/local/etc/freetds/locales.conf.sample
49
+ Configuration file pool.conf does not exist and has been created using
50
+ /opt/local/etc/freetds/pool.conf.sample
51
+ ****************************************************************
52
+
53
+
54
+ * Then edit your ODBC configuration and add the FreeTDS driver.
55
+
56
+ * Using a text editor: On OS X, open `/Library/ODBC/odbcinst.ini` and add
57
+ the following entries:
58
+
59
+ [FreeTDS]
60
+ Driver = /opt/local/lib/libtdsodbc.so
61
+ Setup = /opt/local/lib/libtdsodbc.so
62
+
63
+ * You can also use a GUI for this (provided in Mac OS X 10.3 - 10.5;
64
+ [ODBCManager][odbcmanager] available for OS X 10.6)..
65
+ * Start ODBC Manager
66
+ * Go to *Drivers*, *Add...*
67
+ * Enter _FreeTDS_ as Driver Name.
68
+ * Enter `/usr/local/freetds/lib/libtdsodbc.so` as Driver File
69
+ * Enter `/usr/local/freetds/lib/libtdsodbc.so` as Setup File
70
+ * Select *System*
71
+ * Click *OK*.
72
+
73
+
74
+ [rubyodbc]:http://www.ch-werner.de/rubyodbc/README
75
+ [unixodbc]:http://www.unixodbc.org/
76
+ [odbcmanager]:http://www.odbcmanager.net/index.php
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2009 Clifford Heath
2
+ Copyright (c) 2009 - 2011 Alex Coles
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining
5
+ a copy of this software and associated documentation files (the
6
+ "Software"), to deal in the Software without restriction, including
7
+ without limitation the rights to use, copy, modify, merge, publish,
8
+ distribute, sublicense, and/or sell copies of the Software, and to
9
+ permit persons to whom the Software is furnished to do so, subject to
10
+ the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,95 @@
1
+ # do_sqlserver
2
+
3
+ * <http://dataobjects.info>
4
+
5
+ ## Description
6
+
7
+ A Microsoft SQL Server adapter for DataObjects,
8
+
9
+ ## Features/Problems
10
+
11
+ This driver implements the DataObjects API for the Microsoft SQL Server
12
+ relational database.
13
+
14
+ Problems with previous MRI implementation (unreleased):
15
+
16
+ * Relies on DBI's support for either ADO or ODBC with FreeTDS
17
+ * Has no tests and no data type conversion yet
18
+
19
+ This version uses TinyTDS instead. Is still alpha, and not production ready.
20
+
21
+ ## Synopsis
22
+
23
+ Examples of usage:
24
+
25
+ # default port (using SQL Server Express Edition)
26
+ DataObjects::Connection.new('sqlserver://user:pass@host/database;instance=SQLEXPRESS')
27
+ # port specified (using SQL Server Express Edition)
28
+ DataObjects::Connection.new('sqlserver://user:pass@host:1433/database;instance=SQLEXPRESS')
29
+
30
+ @connection = DataObjects::Connection.new("sqlserver://john:p3$$@localhost:1433/userinfo")
31
+ @reader = @connection.create_command('SELECT * FROM users').execute_reader
32
+ @reader.next!
33
+
34
+ * See also the accompanying `CONNECTING.markdown`.
35
+
36
+ ## Requirements
37
+
38
+ This driver is provided for the following platforms:
39
+ * JRuby 1.3.1 + (1.4+ recommended).
40
+
41
+ Code for the following platform is in the repository, but is still under EARLY
42
+ DEVELOPMENT and is neither RELEASED or SUPPORTED:
43
+ * Ruby MRI (1.8.6/7), 1.9: tested on Linux, Mac OS X and Windows platforms.
44
+
45
+ Additionally you should have the following prerequisites:
46
+ * `data_objects` gem
47
+ * `do_jdbc` gem (shared library), if running on JRuby.
48
+ * `dbi` gem, if running on MRI.
49
+ * On non-Windows platforms, unixODBC and FreeTDS libraries.
50
+
51
+ ## Install
52
+
53
+ To install the gem:
54
+
55
+ gem install do_sqlserver
56
+
57
+ To compile and install from source:
58
+
59
+ * For MRI:
60
+ * Installation of do_sqlserver is significantly more involved than for other
61
+ drivers. Please see the accompanying `INSTALL.markdown`.
62
+
63
+ Then:
64
+
65
+ sudo gem install do_sqlserver
66
+
67
+ For more information, see the SQL Server driver wiki page:
68
+ <http://wiki.github.com/datamapper/do/sql-server>.
69
+
70
+ ## Developers
71
+
72
+ Follow the above installation instructions. Additionally, you'll need:
73
+ * `rspec` gem for running specs.
74
+ * `YARD` gem for generating documentation.
75
+
76
+ See the DataObjects wiki for more comprehensive information on installing and
77
+ contributing to the JRuby-variant of this driver:
78
+ <http://wiki.github.com/datamapper/do/jruby>.
79
+
80
+ To run specs:
81
+
82
+ rake spec
83
+
84
+ To run specs without compiling extensions first:
85
+
86
+ rake spec_no_compile
87
+
88
+ To run individual specs:
89
+
90
+ rake spec SPEC=spec/connection_spec.rb
91
+
92
+ ## License
93
+
94
+ This code is licensed under an **MIT (X11) License**. Please see the
95
+ accompanying `LICENSE` file.
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new
5
+
6
+ task :default => :spec
7
+ task :test => :spec
@@ -0,0 +1,27 @@
1
+ # Apache Buildr buildfile for do_sqlserver
2
+ # see http://incubator.apache.org/buildr/ for more information on Apache Buildr
3
+ require 'buildr'
4
+ require 'pathname'
5
+
6
+ VERSION_NUMBER = '1.0'
7
+ JDBC_SUPPORT = ['data_objects:jdbc:jar:1.0']
8
+ TARGET_DIR = 'pkg/classes'
9
+ repositories.remote << 'http://www.ibiblio.org/maven2/'
10
+
11
+ define 'do_sqlserver' do
12
+ project.version = VERSION_NUMBER
13
+ project.group = 'data_objects.rb'
14
+
15
+ manifest['Copyright'] = 'Raimonds Simanovskis (C) 2009'
16
+
17
+ compile.using :target => '1.5', :lint => 'all', :deprecation => 'true'
18
+
19
+ define 'ext-java' do
20
+ package(:jar).clean.include(TARGET_DIR)
21
+
22
+ jdbc_support_jar = file('../../do_jdbc/lib/do_jdbc_internal.jar')
23
+ jdbc_support = artifact('data_objects:jdbc:jar:1.0').from(jdbc_support_jar)
24
+
25
+ compile.into(TARGET_DIR).with(JDBC_SUPPORT)
26
+ end
27
+ end
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'do_sqlserver_tinytds/version'
5
+
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = %q{do_sqlserver-tinytds}
9
+ spec.version = DataObjects::SqlServer::VERSION
10
+
11
+ spec.authors = ["Juan Leal", "Caleb Tutty"]
12
+ spec.description = %q{Implements the DataObjects API for Microsoft SQL Server using TinyTDS}
13
+ spec.email = %q{sellingangle@hotmail.com caleb@prettymint.co.nz}
14
+ spec.summary = %q{DataObjects SQL Server Driver using TinyTDS}
15
+
16
+ spec.homepage = ""
17
+ spec.license = "MIT"
18
+
19
+ spec.files = `git ls-files`.split($/)
20
+ spec.require_paths = ["lib"]
21
+
22
+
23
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
24
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
25
+
26
+
27
+ spec.add_dependency 'data_objects', '~> 0.10.13'
28
+ spec.add_dependency 'tiny_tds', '~> 0.5'
29
+ spec.add_development_dependency 'rspec', '~> 2.5'
30
+
31
+ end
@@ -0,0 +1 @@
1
+ require 'do_sqlserver_tinytds'
@@ -0,0 +1,360 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+
3
+ require 'data_objects'
4
+ require 'bigdecimal'
5
+ require 'date'
6
+ require 'base64'
7
+ require 'do_sqlserver_tinytds/version'
8
+ require 'do_sqlserver_tinytds/transaction'
9
+ require 'do_sqlserver_tinytds/tiny_tds_extension'
10
+ require 'do_sqlserver_tinytds/addressable_extension'
11
+
12
+
13
+ module DataObjects
14
+ module SqlServer
15
+
16
+ class Connection < DataObjects::Connection
17
+ def method_missing(method, *args)
18
+ if @connection.respond_to?(method)
19
+ begin
20
+ @connection.send(method , *args)
21
+ rescue TinyTds::Error => te
22
+ case te.db_error_number
23
+ when 20019
24
+ #Got a pending transcation there, lets make it let go
25
+ @connection.close
26
+ set_tiny_tds_connection
27
+ #give it another try
28
+ @connection.send(method, *args)
29
+ else
30
+ raise te
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ def set_tiny_tds_connection
37
+ @connection = TinyTds::Client.new(@options).tap do |client|
38
+ client.execute("SET ANSI_NULLS ON").do
39
+ client.execute("SET QUOTED_IDENTIFIER ON").do
40
+ end
41
+ end
42
+
43
+ def initialize uri
44
+ host = uri.host
45
+ user = uri.user || "sa"
46
+ password = uri.password || ""
47
+ path = uri.path.sub(%r{^/*}, '')
48
+ port = uri.port || "1433"
49
+ azure = uri.query['azure'] || false
50
+
51
+ begin
52
+ _encoding = uri.query && uri.query["encoding"] || "UTF-8"
53
+ @encoding = Encoding.find(_encoding) ? _encoding : "UTF-8"
54
+ rescue
55
+ @encoding = "UTF-8"
56
+ end
57
+
58
+ @options = {:username => user,
59
+ :password => password,
60
+ :port => port ,
61
+ :encoding => @encoding,
62
+ :timeout => 5000,
63
+ :dataserver => host,
64
+ :database => path,
65
+ :azure => azure
66
+ }
67
+
68
+ @options[:dataserver] = host
69
+
70
+ begin
71
+ set_tiny_tds_connection
72
+ #@connection = DBI.connect(connection_string, user, password)
73
+ rescue Exception => e
74
+ raise
75
+ end
76
+
77
+ set_date_format = create_command("SET DATEFORMAT YMD").execute_non_query
78
+ options_reader = create_command("DBCC USEROPTIONS").execute_reader
79
+ while options_reader.next!
80
+ key, value = *options_reader.values
81
+ value = options_reader.values
82
+ case key
83
+ when "textsize" # "64512"
84
+ when "language" # "us_english", "select * from master..syslanguages" for info
85
+ when "dateformat" # "ymd"
86
+ when "datefirst" # "7" = Sunday, first day of the week, change with "SET DATEFIRST"
87
+ when "quoted_identifier" # "SET"
88
+ when "ansi_null_dflt_on" # "SET"
89
+ when "ansi_defaults" # "SET"
90
+ when "ansi_warnings" # "SET"
91
+ when "ansi_padding" # "SET"
92
+ when "ansi_nulls" # "SET"
93
+ when "concat_null_yields_null" # "SET"
94
+ else
95
+ end
96
+ end
97
+ end
98
+
99
+ def using_socket?
100
+ # This might be an unnecessary feature dragged from the mysql driver
101
+ raise "Not yet implemented"
102
+ end
103
+
104
+ def character_set
105
+ @encoding
106
+ end
107
+
108
+ def dispose
109
+ return false if @connection.closed?
110
+ @connection.close
111
+ true
112
+ rescue
113
+ false
114
+ end
115
+
116
+ def raw
117
+ @connection
118
+ end
119
+
120
+ :private
121
+
122
+ #debugger_on - used only for development when debugging things
123
+ def debugger_on=(value)
124
+ $debugger_on = value
125
+ end
126
+ end
127
+
128
+ class Command < DataObjects::Command
129
+ IDENTITY_ROWCOUNT_QUERY = 'SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident, @@ROWCOUNT AS AffectedRows'
130
+
131
+ attr_reader :types
132
+
133
+ def debugger_on(value)
134
+ $debugger_on = value
135
+ end
136
+
137
+ def set_types *t
138
+ @types = t.flatten
139
+ end
140
+
141
+ def execute_non_query *args
142
+ DataObjects::SqlServer.check_params @text, args
143
+
144
+ begin
145
+ handle = @connection.raw_execute(@text , *args)
146
+ handle.do
147
+ rescue TinyTds::Error => te
148
+
149
+ handle.cancel if handle && handle.respond_to?(:cancel) && !@connection.sqlsent?
150
+
151
+ DataObjects::SqlServer.raise_db_error(te, @text, args)
152
+ rescue RuntimeError => re
153
+ case re.message
154
+ when "closed connection"
155
+ raise DataObjects::ConnectionError.new(re.message)
156
+ end
157
+ rescue Exception => e
158
+ raise e
159
+ end
160
+
161
+ # Get the inserted ID and the count of affected rows:
162
+ inserted_id = @connection.execute("SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident").each.first['Ident']
163
+ row_count = handle.affected_rows
164
+ Result.new(self, row_count, inserted_id)
165
+ end
166
+
167
+ def execute_reader *args
168
+ DataObjects::SqlServer.check_params @text, args
169
+ massage_limit_and_offset args
170
+ begin
171
+ handle = @connection.raw_execute(@text, *args)
172
+
173
+ rescue Exception => e
174
+
175
+ handle.cancel if handle && handle.respond_to?(:cancel) && !@connection.sqlsent?
176
+ raise
177
+ end
178
+
179
+ Reader.new(self, handle)
180
+ end
181
+
182
+ private
183
+ def massage_limit_and_offset args
184
+ @text.sub!(%r{SELECT (.*) ORDER BY (.*) LIMIT ([?0-9]*)( OFFSET ([?0-9]*))?}) {
185
+ what, order, limit, offset = $1, $2, $3, $5
186
+
187
+ # LIMIT and OFFSET will probably be set by args. We need exact values, so must
188
+ # do substitution here, and remove those args from the array. This is made easier
189
+ # because LIMIT and OFFSET are always the last args in the array.
190
+ offset = args.pop if offset == '?'
191
+ limit = args.pop if limit == '?'
192
+ offset = offset.to_i
193
+ limit = limit.to_i
194
+
195
+ #Reverse the sort direction of each field in the ORDER BY:
196
+ rev_order = order.split(/, */).map{ |f|
197
+ f =~ /(.*) DESC *$/ ? $1 : f+" DESC"
198
+ }*", "
199
+
200
+ "SELECT TOP #{limit} * FROM (SELECT TOP #{offset+limit} #{what} ORDER BY #{rev_order}) ORDER BY #{order}"
201
+ }
202
+ end
203
+ end
204
+
205
+ class Result < DataObjects::Result
206
+ end
207
+
208
+ class Reader < DataObjects::Reader
209
+ def initialize command, handle
210
+ @command, @handle = command, handle
211
+ return unless @handle
212
+
213
+ @fields = handle.fields
214
+
215
+ @rows = []
216
+ types = @command.types
217
+ if types && types.size != @fields.size
218
+ @handle.cancel if @handle && @handle.respond_to?(:cancel)
219
+ raise ArgumentError, "Field-count mismatch. Expected #{types.size} fields, but the query yielded #{@fields.size}"
220
+ end
221
+
222
+
223
+ @handle.each(:as => :array) do |row|
224
+ field = -1
225
+ @rows << row.map do |value|
226
+ field += 1
227
+
228
+ next value if !types && !value.is_a?(Time)
229
+
230
+ field_type = types && types[field]
231
+ value_class = value.class
232
+
233
+ r_value = case
234
+ when field_type == NilClass || value.nil?
235
+ nil
236
+ when field_type.nil? && value.is_a?(Time)
237
+ #sql small dates have zeros for hr, min, sec
238
+ # and needs to be cast as Date, else cast as DateTime
239
+ if (value.hour + value.min + value.sec) == 0
240
+ time_to_date(value)
241
+ else
242
+ time_to_date_time(value)
243
+ end
244
+ when value.is_a?(field_type) || value_class.kind_of?(field_type) || field_type == TrueClass
245
+ value
246
+ when field_type == Integer
247
+ Integer(value)
248
+ when field_type == Float
249
+ Float(value)
250
+ when field_type == String
251
+ raise "Value '#{value.inspect}' does not respond to #to_s" unless value.respond_to?(:to_s)
252
+ value.to_s
253
+ when field_type == DateTime
254
+ case
255
+ when value.is_a?(Time)
256
+ time_to_date_time(value)
257
+ when value.is_a?(String)
258
+ DateTime.parse(value)
259
+ else
260
+ DateTime.parse(value.to_s)
261
+ end
262
+ when field_type == Date
263
+ case
264
+ when value.is_a?(Time) || value.is_a?(DateTime)
265
+ Date.parse(value.strftime('%Y/%m/%d'))
266
+ else
267
+ Date.parse(value)
268
+ end
269
+ when field_type == Time
270
+ Time.parse(value)
271
+ when field_type == BigDecimal
272
+ BigDecimal.new(value.to_s)
273
+ else
274
+ if value.respond_to?(:to_s)
275
+ value.to_s
276
+ else
277
+ value
278
+ end
279
+ end
280
+
281
+ r_value
282
+
283
+ end
284
+ end
285
+ @handle.cancel if @handle && @handle.respond_to?(:cancel)
286
+ @current_row = -1
287
+ end
288
+
289
+ def close
290
+ if @handle
291
+ @handle.finish if @handle.respond_to?(:finish) && !@handle.finished?
292
+ @handle = nil
293
+ true
294
+ else
295
+ false
296
+ end
297
+ end
298
+
299
+ def next!
300
+ (@current_row += 1) < @rows.size
301
+ end
302
+
303
+ def values
304
+ raise DataObjects::DataError.new("First row has not been fetched") if @current_row < 0
305
+ raise DataObjects::DataError.new("Last row has been processed") if @current_row >= @rows.size
306
+ @rows[@current_row]
307
+ end
308
+
309
+ def fields
310
+ @fields
311
+ end
312
+
313
+ def field_count
314
+ @fields.size
315
+ end
316
+
317
+ def row_count
318
+ @rows.size
319
+ end
320
+
321
+ private
322
+
323
+ def time_to_date_time(value)
324
+ DateTime.new(value.year, value.month, value.day,
325
+ value.hour, value.min, value.sec,
326
+ Rational(value.gmt_offset / 3600, 24))
327
+ end
328
+
329
+ def time_to_date(value)
330
+ Date.parse(time_to_date_time(value).strftime('%Y/%m/%d'))
331
+ end
332
+ end
333
+
334
+ private
335
+
336
+ def self.check_params cmd, args
337
+ actual = args.size
338
+ expected = param_count(cmd)
339
+ raise ArgumentError.new("Binding mismatch: #{actual} for #{expected}") if actual != expected
340
+ end
341
+
342
+ def self.raise_db_error(e, cmd, args)
343
+ msg = e.to_str
344
+ case msg
345
+ when /Too much parameters/, /No data found/
346
+ check_params(cmd, args)
347
+ else
348
+ e.errstr << " running '#{cmd}'"
349
+ end
350
+ raise DataObjects::SQLError.new(e.errstr)
351
+ end
352
+
353
+ def self.param_count cmd
354
+ cmd.gsub(/'[^']*'/,'').gsub(/"[_|A-Z|a-z|0-9|?]+"/,'').scan(/\?/).size
355
+ end
356
+
357
+ end
358
+
359
+ end
360
+