do_sqlserver 0.10.1-java

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,41 @@
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/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
+
31
+ Tips
32
+ ----
33
+
34
+ * Check the password is not required to be set on first connect.
35
+ * Test you can connect locally. Either through Visual Studio SQL Server tools,
36
+ SQL Server Management Studio, or simply using telnet: `telnet localhost 4322`.
37
+ * Configure Firewall correctly: http://support.microsoft.com/kb/287932
38
+ * Specify an instance: in the DO URL append `INSTANCE=SQLEXPRESS`.
39
+ * You can not add the instance to the hostname, i.e. `192.168.2.110\SQLEXPRESS`
40
+ as the underlying jTDS driver needs a URL that looks like this:
41
+ `jdbc:jtds:sqlserver://192.168.2.110:1433/do_test;instance=SQLEXPRESS`.
@@ -0,0 +1,25 @@
1
+ ## 0.10.1 (unreleased, in git)
2
+
3
+ Initial release as part of mainline DataObjects project.
4
+
5
+ * Switch to Jeweler for Gem building tasks (this change may be temporary).
6
+ * Switch to using Bacon for running specs: This should make specs friendlier to
7
+ new Ruby implementations that are not yet 100% MRI-compatible, and in turn,
8
+ prepared the road for our own IronRuby and MacRuby support.
9
+ * Switch to the newly added rake-compiler `JavaExtensionTask` for compiling
10
+ JRuby extensions, instead of our (broken) home-grown solution.
11
+
12
+ * Known Issues:
13
+ * Writing Extlib::ByteArray is not currently supported.
14
+
15
+ ## 0.10.0 2009-09-15
16
+
17
+ (NOT RELEASED)
18
+
19
+ * Improvements
20
+ * JRuby Support (using *do_jdbc*)
21
+
22
+ ## 0.0.1 2009-05-11
23
+
24
+ * 1 major enhancement:
25
+ * 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.
@@ -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, 2010 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,110 @@
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 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
+ ## Synopsis
20
+
21
+ Examples of usage:
22
+
23
+ # default port (using SQL Server Express Edition)
24
+ DataObjects::Connection.new('sqlserver://user:pass@host/database;instance=SQLEXPRESS')
25
+ # port specified (using SQL Server Express Edition)
26
+ DataObjects::Connection.new('sqlserver://user:pass@host:1433/database;instance=SQLEXPRESS')
27
+
28
+ @connection = DataObjects::Connection.new("sqlserver://john:p3$$@localhost:1433/userinfo")
29
+ @reader = @connection.create_command('SELECT * FROM users').execute_reader
30
+ @reader.next!
31
+
32
+ In the future, the `Connection` constructor will be able to be passed either a
33
+ DataObjects-style URL or JDBC style URL, when using do\_sqlserver on JRuby.
34
+ However, this feature is not currently working reliably and is a known issue.
35
+
36
+ * See also the accompanying `CONNECTING.markdown`.
37
+
38
+ ## Requirements
39
+
40
+ This driver is provided for the following platforms:
41
+ * JRuby 1.3.1 + (1.4+ recommended).
42
+
43
+ Code for the following platform is in the repository, but is still under EARLY
44
+ DEVELOPMENT and is neither RELEASED or SUPPORTED:
45
+ * Ruby MRI (1.8.6/7), 1.9: tested on Linux, Mac OS X and Windows platforms.
46
+
47
+ Additionally you should have the following prerequisites:
48
+ * `data_objects` gem
49
+ * `do_jdbc` gem (shared library), if running on JRuby.
50
+ * `dbi` gem, if running on MRI.
51
+ * On non-Windows platforms, unixODBC and FreeTDS libraries.
52
+
53
+ ## Install
54
+
55
+ To install the gem:
56
+
57
+ gem install do_sqlserver
58
+
59
+ To compile and install from source:
60
+
61
+ * For MRI:
62
+ * Installation of do_sqlserver is significantly more involved than for other
63
+ drivers. Please see the accompanying `INSTALL.markdown`.
64
+
65
+ * For JRuby extensions:
66
+ * Install the Java Development Kit (provided if you are
67
+ on a recent version of Mac OS X) from <http://java.sun.com>.
68
+ * Install a recent version of JRuby. Ensure `jruby` is in your `PATH` and/or
69
+ you have configured the `JRUBY_HOME` environment variable to point to your
70
+ JRuby installation.
71
+ * Install `data_objects` and `do_jdbc` with `jruby -S rake install`.
72
+
73
+ * Then, install this driver with `(jruby -S) rake install`.
74
+
75
+ Then:
76
+
77
+ sudo gem install do_sqlserver
78
+
79
+ For more information, see the SQL Server driver wiki page:
80
+ <http://wiki.github.com/datamapper/do/sql-server>.
81
+
82
+ ## Developers
83
+
84
+ Follow the above installation instructions. Additionally, you'll need:
85
+ * `bacon` gem for running specs.
86
+ * `YARD` gem for generating documentation.
87
+
88
+ See the DataObjects wiki for more comprehensive information on installing and
89
+ contributing to the JRuby-variant of this driver:
90
+ <http://wiki.github.com/datamapper/do/jruby>.
91
+
92
+ To run specs:
93
+
94
+ rake spec
95
+
96
+ To run specs without compiling extensions first:
97
+
98
+ rake spec_no_compile
99
+
100
+ To run individual specs:
101
+
102
+ rake spec TEST=spec/connection_spec.rb
103
+
104
+ (Note that the `rake` task uses a `TEST` parameter, not `SPEC`. This is because
105
+ the `Rake::TestTask` is used for executing the Bacon specs).
106
+
107
+ ## License
108
+
109
+ This code is licensed under an **MIT (X11) License**. Please see the
110
+ accompanying `LICENSE` file.
@@ -0,0 +1,56 @@
1
+ require 'pathname'
2
+ require 'rubygems'
3
+ require 'rake'
4
+ require 'rake/clean'
5
+
6
+ ROOT = Pathname(__FILE__).dirname.expand_path
7
+
8
+ require ROOT + 'lib/do_sqlserver/version'
9
+
10
+ JRUBY = RUBY_PLATFORM =~ /java/
11
+ IRONRUBY = defined?(RUBY_ENGINE) && RUBY_ENGINE == 'ironruby'
12
+ WINDOWS = Gem.win_platform? || (JRUBY && ENV_JAVA['os.name'] =~ /windows/i)
13
+ SUDO = WINDOWS ? '' : ('sudo' unless ENV['SUDOLESS'])
14
+
15
+ CLEAN.include(%w[ {tmp,pkg}/ **/*.{o,so,bundle,jar,log,a,gem,dSYM,obj,pdb,exp,DS_Store,rbc,db} ext-java/target ])
16
+
17
+ begin
18
+ gem 'jeweler', '~> 1.4'
19
+ require 'jeweler'
20
+
21
+ Jeweler::Tasks.new do |gem|
22
+ gem.name = 'do_sqlserver'
23
+ gem.version = DataObjects::SqlServer::VERSION
24
+ gem.summary = 'DataObjects SQL Server Driver'
25
+ gem.description = 'Implements the DataObjects API for Microsoft SQL Server'
26
+ gem.platform = 'java'
27
+ gem.files = FileList['lib/**/*.rb', 'spec/**/*.rb', 'tasks/**/*.rake',
28
+ 'LICENSE', 'Rakefile', '*.{markdown,rdoc,txt,yml}',
29
+ 'lib/*.jar']
30
+ gem.extra_rdoc_files = FileList['README*', 'ChangeLog*', 'INSTALL.markdown',
31
+ 'FAQS.markdown', 'LICENSE']
32
+ gem.test_files = FileList['spec/**/*.rb']
33
+
34
+ gem.add_dependency 'data_objects', DataObjects::SqlServer::VERSION
35
+ gem.add_dependency 'do_jdbc', DataObjects::SqlServer::VERSION
36
+ gem.add_dependency 'do-jdbc_sqlserver', '~>1.2.4'
37
+
38
+ gem.add_development_dependency 'bacon', '~>1.1'
39
+ gem.add_development_dependency 'rake-compiler', '~>0.7'
40
+
41
+ gem.has_rdoc = false
42
+
43
+ gem.rubyforge_project = 'dorb'
44
+ gem.authors = [ 'Alex Coles' ]
45
+ gem.email = 'alex@alexcolesportfolio.com'
46
+ end
47
+
48
+ Rake::Task['build'].clear_actions if Rake::Task.task_defined?('build')
49
+ task :build => [ :java, :gem ]
50
+
51
+ Jeweler::GemcutterTasks.new
52
+
53
+ FileList['tasks/**/*.rake'].each { |task| import task }
54
+ rescue LoadError
55
+ puts 'Jeweler (or a dependency) not available. Install it with: gem install jeweler'
56
+ end
@@ -0,0 +1,50 @@
1
+ #
2
+ # Monkey patch DBD::ODBC to pass usernames and passwords along
3
+ # to the DB driver correctly
4
+ #
5
+
6
+ # Are you running a new version of DBI?
7
+ begin
8
+ require 'dbd/ODBC'
9
+ rescue Exception
10
+ end
11
+
12
+ # Or an old version?
13
+ begin
14
+ require 'DBD/ODBC/ODBC'
15
+ rescue Exception
16
+ end
17
+
18
+ class DBI::DBD::ODBC::Driver < DBI::BaseDriver
19
+
20
+ def connect(dbname, user, auth, attr)
21
+ driver_attrs = dbname.split(';')
22
+
23
+ if driver_attrs.size > 1
24
+ # DNS-less connection
25
+ drv = ::ODBC::Driver.new
26
+ drv.name = 'Driver1'
27
+ driver_attrs.each do |param|
28
+ pv = param.split('=')
29
+ next if pv.size < 2
30
+ drv.attrs[pv[0]] = pv[1]
31
+ end
32
+ #
33
+ # These next two lines are new
34
+ #
35
+ drv.attrs['UID'] = user unless user.nil?
36
+ drv.attrs['PWD'] = auth unless auth.nil?
37
+
38
+ db = ::ODBC::Database.new
39
+ handle = db.drvconnect(drv)
40
+ else
41
+ # DNS given
42
+ handle = ::ODBC.connect(dbname, user, auth)
43
+ end
44
+
45
+ return DBI::DBD::ODBC::Database.new(handle, attr)
46
+ rescue DBI::DBD::ODBC::ODBCErr => err
47
+ raise DBI::DatabaseError.new(err.message)
48
+ end
49
+
50
+ end
@@ -0,0 +1,296 @@
1
+ require 'data_objects'
2
+ if RUBY_PLATFORM =~ /java/
3
+ require 'do_jdbc'
4
+ require 'java'
5
+ require 'do_jdbc/sqlserver' # the JDBC driver, packaged as a gem
6
+ else # MRI and Ruby 1.9
7
+ require 'dbi' unless defined?(DBI)
8
+ require 'dbd_odbc_patch' # a monkey patch for DNS-less connections
9
+ #require 'core_ext/dbi' # a hack to work around ODBC millisecond handling in Timestamps
10
+ end
11
+
12
+ require 'bigdecimal'
13
+ require 'date'
14
+ require 'base64'
15
+ require 'do_sqlserver/do_sqlserver' if RUBY_PLATFORM =~ /java/
16
+ require 'do_sqlserver/version'
17
+ # JDBC driver has transactions implementation in Java
18
+ require 'do_sqlserver/transaction' if RUBY_PLATFORM !~ /java/
19
+
20
+ if RUBY_PLATFORM !~ /java/
21
+ module DataObjects
22
+ module SqlServer
23
+ Mode = :odbc
24
+ # The ADO mode requires a very old DBI to work with the unmaintained DBI::ADO adapter. Don't enable this unless you fix that.
25
+ # Mode = begin
26
+ # require "ADO"
27
+ # :ado
28
+ # rescue LoadError => e
29
+ # :odbc
30
+ # end
31
+
32
+ class Connection < DataObjects::Connection
33
+ def initialize uri
34
+ # REVISIT: Allow uri.query to modify this connection's mode?
35
+ #host = uri.host.blank? ? "localhost" : uri.host
36
+ host = uri.host.blank? ? nil : uri.host
37
+ user = uri.user || "sa"
38
+ password = uri.password || ""
39
+ path = uri.path.sub(%r{^/*}, '')
40
+ port = uri.port || "1433"
41
+ if Mode == :ado
42
+ connection_string = "DBI:ADO:Provider=SQLOLEDB;Data Source=#{host};Initial Catalog=#{path};User ID=#{user};Password=#{password};"
43
+ else
44
+ # FIXME: Cannot get a DNS-less configuration without freetds.conf to
45
+ # connect successfully, i.e.:
46
+ # connection_string = "DBI:ODBC:DRIVER=FreeTDS;SERVER=#{host};DATABASE=#{path};TDS_Version=5.0;Port=#{port}"
47
+ #
48
+ # Currently need to setup a dataserver entry in freetds.conf (if
49
+ # using MacPorts, full path is /opt/local/etc/freetds/freetds.conf):
50
+ #
51
+ # [sqlserver]
52
+ # host = hostname
53
+ # port = 1433
54
+ # instance = SQLEXPRESS
55
+ # tds version = 8.0
56
+ #
57
+ connection_string = "DBI:ODBC:DRIVER=FreeTDS;SERVERNAME=sqlserver;DATABASE=#{path};"
58
+ end
59
+
60
+ begin
61
+ @connection = DBI.connect(connection_string, user, password)
62
+ rescue DBI::DatabaseError => e
63
+ # Place to debug connection failures
64
+ raise
65
+ end
66
+
67
+ @encoding = uri.query && uri.query["encoding"] || "utf8"
68
+
69
+ set_date_format = create_command("SET DATEFORMAT YMD").execute_non_query
70
+ options_reader = create_command("DBCC USEROPTIONS").execute_reader
71
+ while options_reader.next!
72
+ key, value = *options_reader.values
73
+ value = options_reader.values
74
+ case key
75
+ when "textsize" # "64512"
76
+ when "language" # "us_english", "select * from master..syslanguages" for info
77
+ when "dateformat" # "ymd"
78
+ when "datefirst" # "7" = Sunday, first day of the week, change with "SET DATEFIRST"
79
+ when "quoted_identifier" # "SET"
80
+ when "ansi_null_dflt_on" # "SET"
81
+ when "ansi_defaults" # "SET"
82
+ when "ansi_warnings" # "SET"
83
+ when "ansi_padding" # "SET"
84
+ when "ansi_nulls" # "SET"
85
+ when "concat_null_yields_null" # "SET"
86
+ else
87
+ end
88
+ end
89
+ end
90
+
91
+ def using_socket?
92
+ # This might be an unnecessary feature dragged from the mysql driver
93
+ raise "Not yet implemented"
94
+ end
95
+
96
+ def character_set
97
+ @encoding
98
+ end
99
+
100
+ def dispose
101
+ @connection.disconnect
102
+ true
103
+ rescue
104
+ false
105
+ end
106
+
107
+ def raw
108
+ @connection
109
+ end
110
+ end
111
+
112
+ class Command < DataObjects::Command
113
+ # Theoretically, SCOPE_IDENTIY should be preferred, but there are cases where it returns a stale ID, and I don't know why.
114
+ #IDENTITY_ROWCOUNT_QUERY = 'SELECT SCOPE_IDENTITY(), @@ROWCOUNT'
115
+ IDENTITY_ROWCOUNT_QUERY = 'SELECT @@IDENTITY, @@ROWCOUNT'
116
+
117
+ attr_reader :types
118
+
119
+ def set_types *t
120
+ @types = t.flatten
121
+ end
122
+
123
+ def execute_non_query *args
124
+ DataObjects::SqlServer.check_params @text, args
125
+ begin
126
+ handle = @connection.raw.execute(@text, *args)
127
+ rescue DBI::DatabaseError => e
128
+ handle = @connection.raw.handle
129
+ handle.finish if handle && handle.respond_to?(:finish) && !handle.finished?
130
+ DataObjects::SqlServer.raise_db_error(e, @text, args)
131
+ end
132
+ handle.finish if handle && handle.respond_to?(:finish) && !handle.finished?
133
+
134
+ # Get the inserted ID and the count of affected rows:
135
+ inserted_id, row_count = nil, nil
136
+ if (handle = @connection.raw.execute(IDENTITY_ROWCOUNT_QUERY))
137
+ row1 = Array(Array(handle)[0])
138
+ inserted_id, row_count = row1[0].to_i, row1[1].to_i
139
+ handle.finish
140
+ end
141
+ Result.new(self, row_count, inserted_id)
142
+ end
143
+
144
+ def execute_reader *args
145
+ DataObjects::SqlServer.check_params @text, args
146
+ massage_limit_and_offset args
147
+ begin
148
+ handle = @connection.raw.execute(@text, *args)
149
+ rescue DBI::DatabaseError => e
150
+ handle = @connection.raw.handle
151
+ DataObjects::SqlServer.raise_db_error(e, @text, args)
152
+ handle.finish if handle && handle.respond_to?(:finish) && !handle.finished?
153
+ rescue
154
+ handle = @connection.raw.handle
155
+ handle.finish if handle && handle.respond_to?(:finish) && !handle.finished?
156
+ raise
157
+ end
158
+ Reader.new(self, handle)
159
+ end
160
+
161
+ private
162
+ def massage_limit_and_offset args
163
+ @text.sub!(%r{SELECT (.*) ORDER BY (.*) LIMIT ([?0-9]*)( OFFSET ([?0-9]*))?}) {
164
+ what, order, limit, offset = $1, $2, $3, $5
165
+
166
+ # LIMIT and OFFSET will probably be set by args. We need exact values, so must
167
+ # do substitution here, and remove those args from the array. This is made easier
168
+ # because LIMIT and OFFSET are always the last args in the array.
169
+ offset = args.pop if offset == '?'
170
+ limit = args.pop if limit == '?'
171
+ offset = offset.to_i
172
+ limit = limit.to_i
173
+
174
+ # Reverse the sort direction of each field in the ORDER BY:
175
+ rev_order = order.split(/, */).map{ |f|
176
+ f =~ /(.*) DESC *$/ ? $1 : f+" DESC"
177
+ }*", "
178
+
179
+ "SELECT TOP #{limit} * FROM (SELECT TOP #{offset+limit} #{what} ORDER BY #{rev_order}) ORDER BY #{order}"
180
+ }
181
+ end
182
+ end
183
+
184
+ class Result < DataObjects::Result
185
+ end
186
+
187
+ # REVISIT: There is no data type conversion happening here. That will make DataObjects sad.
188
+ class Reader < DataObjects::Reader
189
+ def initialize command, handle
190
+ @command, @handle = command, handle
191
+ return unless @handle
192
+
193
+ @fields = handle.column_names
194
+
195
+ # REVISIT: Prefetch results like AR's adapter does. ADO is a bit strange about handle lifetimes, don't move this until you can test it.
196
+ @rows = []
197
+ types = @command.types
198
+ if types && types.size != @fields.size
199
+ @handle.finish if @handle && @handle.respond_to?(:finish) && !@handle.finished?
200
+ raise ArgumentError, "Field-count mismatch. Expected #{types.size} fields, but the query yielded #{@fields.size}"
201
+ end
202
+ @handle.each do |row|
203
+ field = -1
204
+ @rows << row.map do |value|
205
+ field += 1
206
+ next value unless types
207
+ if (t = types[field]) == Integer
208
+ Integer(value)
209
+ elsif t == Float
210
+ Float(value)
211
+ else
212
+ t.new(value)
213
+ end
214
+ end
215
+ end
216
+ @handle.finish if @handle && @handle.respond_to?(:finish) && !@handle.finished?
217
+ @current_row = -1
218
+ end
219
+
220
+ def close
221
+ if @handle
222
+ @handle.finish if @handle.respond_to?(:finish) && !@handle.finished?
223
+ @handle = nil
224
+ true
225
+ else
226
+ false
227
+ end
228
+ end
229
+
230
+ def next!
231
+ (@current_row += 1) < @rows.size
232
+ end
233
+
234
+ def values
235
+ raise StandardError.new("First row has not been fetched") if @current_row < 0
236
+ raise StandardError.new("Last row has been processed") if @current_row >= @rows.size
237
+ @rows[@current_row]
238
+ end
239
+
240
+ def fields
241
+ @fields
242
+ end
243
+
244
+ def field_count
245
+ @fields.size
246
+ end
247
+
248
+ # REVISIT: This is being deprecated
249
+ def row_count
250
+ @rows.size
251
+ end
252
+ end
253
+
254
+ private
255
+ def self.check_params cmd, args
256
+ actual = args.size
257
+ expected = param_count(cmd)
258
+ raise ArgumentError.new("Binding mismatch: #{actual} for #{expected}") if actual != expected
259
+ end
260
+
261
+ def self.raise_db_error(e, cmd, args)
262
+ msg = e.to_str
263
+ case msg
264
+ when /Too much parameters/, /No data found/
265
+ #puts "'#{cmd}' (#{args.map{|a| a.inspect}*", "}): #{e.to_str}"
266
+ check_params(cmd, args)
267
+ else
268
+ e.errstr << " running '#{cmd}'"
269
+ #puts "'#{cmd}' (#{args.map{|a| a.inspect}*", "}): #{e.to_str}"
270
+ #debugger
271
+ end
272
+ raise
273
+ end
274
+
275
+ def self.param_count cmd
276
+ cmd.gsub(/'[^']*'/,'').scan(/\?/).size
277
+ end
278
+
279
+ end
280
+
281
+ end
282
+
283
+ else
284
+
285
+ # Register SqlServer JDBC driver
286
+ java.sql.DriverManager.registerDriver Java::net.sourceforge.jtds.jdbc.Driver.new
287
+
288
+ DataObjects::SqlServer::Connection.class_eval do
289
+
290
+ def quote_boolean(value)
291
+ value ? 1 : 0
292
+ end
293
+
294
+ end
295
+
296
+ end