do_sqlserver-tinytds 0.10.17.alpha

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 (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
+