do_sqlserver-tinytds 0.10.17.alpha
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/.gitignore +3 -0
- data/.rspec +2 -0
- data/CONNECTING.markdown +40 -0
- data/ChangeLog.markdown +56 -0
- data/FAQS.markdown +8 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +30 -0
- data/INSTALL.markdown +76 -0
- data/LICENSE +21 -0
- data/README.md +95 -0
- data/Rakefile +7 -0
- data/buildfile +27 -0
- data/do_sqlserver_tinytds.gemspec +31 -0
- data/lib/do_sqlserver.rb +1 -0
- data/lib/do_sqlserver_tinytds.rb +360 -0
- data/lib/do_sqlserver_tinytds/addressable_extension.rb +69 -0
- data/lib/do_sqlserver_tinytds/tiny_tds_extension.rb +123 -0
- data/lib/do_sqlserver_tinytds/transaction.rb +36 -0
- data/lib/do_sqlserver_tinytds/version.rb +5 -0
- data/spec/command_spec.rb +9 -0
- data/spec/connection_spec.rb +21 -0
- data/spec/encoding_spec.rb +8 -0
- data/spec/reader_spec.rb +8 -0
- data/spec/result_spec.rb +16 -0
- data/spec/spec_helper.rb +155 -0
- data/spec/typecast/array_spec.rb +8 -0
- data/spec/typecast/bigdecimal_spec.rb +9 -0
- data/spec/typecast/boolean_spec.rb +9 -0
- data/spec/typecast/byte_array_spec.rb +31 -0
- data/spec/typecast/class_spec.rb +8 -0
- data/spec/typecast/date_spec.rb +11 -0
- data/spec/typecast/datetime_spec.rb +9 -0
- data/spec/typecast/float_spec.rb +9 -0
- data/spec/typecast/integer_spec.rb +8 -0
- data/spec/typecast/nil_spec.rb +10 -0
- data/spec/typecast/other_spec.rb +8 -0
- data/spec/typecast/range_spec.rb +8 -0
- data/spec/typecast/string_spec.rb +8 -0
- data/spec/typecast/time_spec.rb +8 -0
- metadata +169 -0
- metadata.gz.sig +3 -0
@@ -0,0 +1,69 @@
|
|
1
|
+
#require "addressable/uri"
|
2
|
+
#
|
3
|
+
#module Addressable
|
4
|
+
# class URI
|
5
|
+
# def self.parse(uri)
|
6
|
+
#
|
7
|
+
# # If we were given nil, return nil.
|
8
|
+
# return nil unless uri
|
9
|
+
# # If a URI object is passed, just return itself.
|
10
|
+
# return uri.dup if uri.kind_of?(self)
|
11
|
+
#
|
12
|
+
# # If a URI object of the Ruby standard library variety is passed,
|
13
|
+
# # convert it to a string, then parse the string.
|
14
|
+
# # We do the check this way because we don't want to accidentally
|
15
|
+
# # cause a missing constant exception to be thrown.
|
16
|
+
# if uri.class.name =~ /^URI\b/
|
17
|
+
# uri = uri.to_s
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# # Otherwise, convert to a String
|
21
|
+
# begin
|
22
|
+
# uri = uri.to_str
|
23
|
+
# rescue TypeError, NoMethodError
|
24
|
+
# raise TypeError, "Can't convert #{uri.class} into String."
|
25
|
+
# end if not uri.is_a? String
|
26
|
+
#
|
27
|
+
# # This Regexp supplied as an example in RFC 3986, and it works great.
|
28
|
+
# scan = uri.scan(URIREGEX)
|
29
|
+
# fragments = scan[0]
|
30
|
+
# scheme = fragments[1]
|
31
|
+
# authority = fragments[3]
|
32
|
+
# path = fragments[4]
|
33
|
+
# query = fragments[6]
|
34
|
+
# fragment = fragments[8]
|
35
|
+
# user = nil
|
36
|
+
# password = nil
|
37
|
+
# host = nil
|
38
|
+
# port = nil
|
39
|
+
# database = nil
|
40
|
+
# if authority != nil
|
41
|
+
# # The Regexp above doesn't split apart the authority.
|
42
|
+
# userinfo = authority[/^([^\[\]]*)@/, 1]
|
43
|
+
# if userinfo != nil
|
44
|
+
# user = userinfo.strip[/^([^:]*):?/, 1]
|
45
|
+
# password = userinfo.strip[/:(.*)$/, 1]
|
46
|
+
# end
|
47
|
+
# host = authority.gsub(/^([^\[\]]*)@/, EMPTYSTR).gsub(/:([^:@\[\]]*?)$/, EMPTYSTR)
|
48
|
+
# port = authority[/:(\d+).*$/ , 1]#authority[/:([^:@\[\]]*?)$/, 1]
|
49
|
+
# database = authority[/:\d+(.*)$/ , 1]
|
50
|
+
# end
|
51
|
+
# if port == EMPTYSTR
|
52
|
+
# port = nil
|
53
|
+
# end
|
54
|
+
#
|
55
|
+
# return Addressable::URI.new(
|
56
|
+
# :scheme => scheme,
|
57
|
+
# :user => user,
|
58
|
+
# :password => password,
|
59
|
+
# :host => host,
|
60
|
+
# :port => port,
|
61
|
+
# :path => path,
|
62
|
+
# :query => query,
|
63
|
+
# :fragment => fragment,
|
64
|
+
# :database => database
|
65
|
+
# )
|
66
|
+
#
|
67
|
+
# end
|
68
|
+
# end
|
69
|
+
#end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'tiny_tds'
|
2
|
+
|
3
|
+
module TinyTds
|
4
|
+
class Client
|
5
|
+
#raw_execute - freetds does not support dynamic sql so
|
6
|
+
#we're going to translate dynamic sql statements into a plain
|
7
|
+
#sql statement
|
8
|
+
def raw_execute(text , *var_list)
|
9
|
+
|
10
|
+
dynamic_sql = text.clone
|
11
|
+
flat_sql = var_list.empty? ? dynamic_sql : process_questioned_sql(dynamic_sql , *var_list)
|
12
|
+
execute(flat_sql)
|
13
|
+
end
|
14
|
+
|
15
|
+
:private
|
16
|
+
|
17
|
+
def process_questioned_sql(sql , *vars)
|
18
|
+
sql_type = classify_sql(sql)
|
19
|
+
|
20
|
+
result = substitute_quoted_questions(sql)
|
21
|
+
_sql = result[:sql]
|
22
|
+
|
23
|
+
vars.each do |var|
|
24
|
+
_sql.sub!("?" , convert_type_to_s(var , sql_type))
|
25
|
+
end
|
26
|
+
|
27
|
+
#place back strings in the sql that contained "?"
|
28
|
+
result[:container].each do |k,v|
|
29
|
+
_sql.gsub!(k , v)
|
30
|
+
end
|
31
|
+
|
32
|
+
_sql
|
33
|
+
end
|
34
|
+
|
35
|
+
def substitute_quoted_questions(sql)
|
36
|
+
container = {}
|
37
|
+
#collect strings in the sql that contains a question_mark
|
38
|
+
qstrings = sql.scan(/"[^"]*"/).select{|s| s.include?("?")}
|
39
|
+
|
40
|
+
#temporarily replace quoted values
|
41
|
+
counter = 0
|
42
|
+
qstrings.each do |qstring|
|
43
|
+
key = "(((####{counter}###)))"
|
44
|
+
sql.gsub!(qstring, key)
|
45
|
+
|
46
|
+
container[key] = qstring
|
47
|
+
counter += 1
|
48
|
+
end
|
49
|
+
|
50
|
+
{:sql => sql, :container => container}
|
51
|
+
end
|
52
|
+
|
53
|
+
#convert_type_to_s - convert Ruby collection objects fed into the query into
|
54
|
+
#sql strings
|
55
|
+
def convert_type_to_s(arg , sql_type)
|
56
|
+
|
57
|
+
case sql_type
|
58
|
+
when :between
|
59
|
+
case arg
|
60
|
+
when Range , Array
|
61
|
+
"#{sql_stringify_value(arg.first)} AND #{sql_stringify_value(arg.last)}"
|
62
|
+
else
|
63
|
+
raise "Type not found..."
|
64
|
+
end
|
65
|
+
when :in
|
66
|
+
case arg
|
67
|
+
when Range , Array
|
68
|
+
" (#{arg.collect{|e| "#{sql_stringify_value(e)}"}.join(" , ")}) "
|
69
|
+
else
|
70
|
+
raise "Type not found..."
|
71
|
+
end
|
72
|
+
else
|
73
|
+
case arg
|
74
|
+
when Range , Array
|
75
|
+
arg.collect{|e| "#{sql_stringify_value(e)}"}.join(" , ")
|
76
|
+
else
|
77
|
+
sql_stringify_value(arg)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
#convert_type_to_s - convert Ruby object fed into the query into
|
83
|
+
#sql string
|
84
|
+
def sql_stringify_value(value)
|
85
|
+
case
|
86
|
+
when value.is_a?(String)
|
87
|
+
"N'#{value}'"
|
88
|
+
when value.is_a?(Numeric)
|
89
|
+
value.to_s
|
90
|
+
when value.is_a?(NilClass)
|
91
|
+
"NULL"
|
92
|
+
when value.is_a?(DateTime)
|
93
|
+
value.strftime("'%m/%d/%Y %I:%M:%S %p'")
|
94
|
+
when value.is_a?(Time)
|
95
|
+
DateTime.new(value.year, value.month, value.day,
|
96
|
+
value.hour, value.min, value.sec,
|
97
|
+
Rational(value.gmt_offset / 3600, 24)).strftime("'%m/%d/%Y %I:%M:%S %p'")
|
98
|
+
else
|
99
|
+
"N'#{value.to_s}'"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
#classify_sql - determine if this sql has a between or in
|
104
|
+
def classify_sql(sql)
|
105
|
+
case
|
106
|
+
when sql[/between.+\?/i] #var should be an array
|
107
|
+
:between
|
108
|
+
when sql[/\sin\s+\?/i]
|
109
|
+
:in
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
|
115
|
+
class Error
|
116
|
+
alias :to_str :to_s
|
117
|
+
|
118
|
+
def errstr
|
119
|
+
@errstr ||= []
|
120
|
+
@errstr
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module DataObjects
|
2
|
+
|
3
|
+
module SqlServer
|
4
|
+
|
5
|
+
class Transaction < DataObjects::Transaction
|
6
|
+
|
7
|
+
def begin
|
8
|
+
connection.instance_variable_get("@connection").autocommit = false
|
9
|
+
end
|
10
|
+
|
11
|
+
def commit
|
12
|
+
connection.instance_variable_get("@connection").commit
|
13
|
+
ensure
|
14
|
+
connection.instance_variable_get("@connection").autocommit = true
|
15
|
+
end
|
16
|
+
|
17
|
+
def rollback
|
18
|
+
connection.instance_variable_get("@connection").rollback
|
19
|
+
ensure
|
20
|
+
connection.instance_variable_get("@connection").autocommit = true
|
21
|
+
end
|
22
|
+
|
23
|
+
def rollback_prepared
|
24
|
+
# TODO: what should be done differently?
|
25
|
+
rollback
|
26
|
+
end
|
27
|
+
|
28
|
+
def prepare
|
29
|
+
# TODO: what should be done here?
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
|
4
|
+
require 'data_objects/spec/shared/command_spec'
|
5
|
+
|
6
|
+
describe DataObjects::SqlServer::Command do
|
7
|
+
it_should_behave_like 'a Command'
|
8
|
+
it_should_behave_like 'a Command with async'
|
9
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
|
4
|
+
require 'data_objects/spec/shared/connection_spec'
|
5
|
+
|
6
|
+
describe DataObjects::SqlServer::Connection do
|
7
|
+
|
8
|
+
before :all do
|
9
|
+
@driver = CONFIG.scheme
|
10
|
+
@user = CONFIG.user
|
11
|
+
@password = CONFIG.pass
|
12
|
+
@host = CONFIG.host
|
13
|
+
@port = CONFIG.port
|
14
|
+
@database = CONFIG.database
|
15
|
+
end
|
16
|
+
|
17
|
+
it_should_behave_like 'a Connection'
|
18
|
+
it_should_behave_like 'a Connection with authentication support'
|
19
|
+
it_should_behave_like 'a Connection with JDBC URL support' if JRUBY
|
20
|
+
it_should_behave_like 'a Connection via JDNI' if JRUBY
|
21
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
|
4
|
+
require 'data_objects/spec/shared/encoding_spec'
|
5
|
+
|
6
|
+
describe DataObjects::SqlServer::Connection do
|
7
|
+
it_should_behave_like 'a driver supporting different encodings'
|
8
|
+
end
|
data/spec/reader_spec.rb
ADDED
data/spec/result_spec.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
|
4
|
+
require 'data_objects/spec/shared/result_spec'
|
5
|
+
|
6
|
+
# splitting the descibe into two separate declaration avoids
|
7
|
+
# concurrent execution of the "it_should_behave_like ....."
|
8
|
+
# needed by some databases (sqlite3)
|
9
|
+
|
10
|
+
describe DataObjects::SqlServer::Result do
|
11
|
+
it_should_behave_like 'a Result'
|
12
|
+
end
|
13
|
+
|
14
|
+
describe DataObjects::SqlServer::Result do
|
15
|
+
it_should_behave_like 'a Result which returns inserted key with sequences'
|
16
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
$TESTING=true
|
2
|
+
JRUBY = nil
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'rspec'
|
6
|
+
require 'date'
|
7
|
+
require 'ostruct'
|
8
|
+
require 'fileutils'
|
9
|
+
|
10
|
+
driver_lib = File.expand_path('../../lib', __FILE__)
|
11
|
+
$LOAD_PATH.unshift(driver_lib) unless $LOAD_PATH.include?(driver_lib)
|
12
|
+
|
13
|
+
|
14
|
+
repo_root = File.expand_path('../../..', __FILE__)
|
15
|
+
|
16
|
+
|
17
|
+
require 'data_objects'
|
18
|
+
require 'data_objects/spec/setup'
|
19
|
+
require 'data_objects/spec/lib/pending_helpers'
|
20
|
+
require 'do_sqlserver_tinytds'
|
21
|
+
require 'ruby-debug'
|
22
|
+
|
23
|
+
DataObjects::SqlServer.logger = DataObjects::Logger.new(STDOUT, :off)
|
24
|
+
at_exit { DataObjects.logger.flush }
|
25
|
+
|
26
|
+
|
27
|
+
CONFIG = OpenStruct.new
|
28
|
+
CONFIG.scheme = 'sqlserver'
|
29
|
+
CONFIG.user = ENV['DO_SQLSERVER_USER'] || 'sa'
|
30
|
+
CONFIG.pass = ENV['DO_SQLSERVER_PASS'] || 'p@$$word'
|
31
|
+
CONFIG.host = ENV['DO_SQLSERVER_HOST'] || 'MSSQLLOCAL'
|
32
|
+
CONFIG.port = ENV['DO_SQLSERVER_PORT'] || '1433'
|
33
|
+
CONFIG.instance = ENV['DO_SQLSERVER_INSTANCE'] || 'SQLEXPRESS'
|
34
|
+
CONFIG.database = ENV['DO_SQLSERVER_DATABASE'] || "/datamapper_test"
|
35
|
+
|
36
|
+
CONFIG.driver = 'sqlserver'
|
37
|
+
CONFIG.uri = ENV["DO_SQLSERVER_SPEC_URI"] ||"#{CONFIG.scheme}://#{CONFIG.user}:#{CONFIG.pass}@#{CONFIG.host}:#{CONFIG.port}#{CONFIG.database}"
|
38
|
+
CONFIG.sleep = "WAITFOR DELAY '00:00:00:10'"
|
39
|
+
|
40
|
+
module DataObjectsSpecHelpers
|
41
|
+
|
42
|
+
def setup_test_environment
|
43
|
+
conn = DataObjects::Connection.new(CONFIG.uri)
|
44
|
+
|
45
|
+
conn.create_command(<<-EOF).execute_non_query
|
46
|
+
IF OBJECT_ID('invoices') IS NOT NULL DROP TABLE invoices
|
47
|
+
EOF
|
48
|
+
|
49
|
+
conn.create_command(<<-EOF).execute_non_query
|
50
|
+
IF OBJECT_ID('users') IS NOT NULL DROP TABLE users
|
51
|
+
EOF
|
52
|
+
|
53
|
+
conn.create_command(<<-EOF).execute_non_query
|
54
|
+
IF OBJECT_ID('widgets') IS NOT NULL DROP TABLE widgets
|
55
|
+
EOF
|
56
|
+
|
57
|
+
conn.create_command(<<-EOF).execute_non_query
|
58
|
+
CREATE TABLE [users] (
|
59
|
+
[id] int NOT NULL IDENTITY,
|
60
|
+
[name] nvarchar(200) default 'Billy' NULL,
|
61
|
+
[fired_at] timestamp,
|
62
|
+
PRIMARY KEY ([id])
|
63
|
+
);
|
64
|
+
EOF
|
65
|
+
|
66
|
+
conn.create_command(<<-EOF).execute_non_query
|
67
|
+
CREATE TABLE [invoices] (
|
68
|
+
[id] int NOT NULL IDENTITY,
|
69
|
+
[invoice_number] varchar(50) NOT NULL,
|
70
|
+
PRIMARY KEY ([id])
|
71
|
+
);
|
72
|
+
EOF
|
73
|
+
|
74
|
+
conn.create_command(<<-EOF).execute_non_query
|
75
|
+
CREATE TABLE [widgets] (
|
76
|
+
[id] int NOT NULL IDENTITY,
|
77
|
+
[code] char(8) default 'A14' NULL,
|
78
|
+
[name] varchar(200) default 'Super Widget' NULL,
|
79
|
+
[shelf_location] nvarchar(max) NULL,
|
80
|
+
[description] nvarchar(max) NULL,
|
81
|
+
[image_data] image NULL,
|
82
|
+
[ad_description] varchar(8000) NULL,
|
83
|
+
[ad_image] image NULL,
|
84
|
+
[whitepaper_text] nvarchar(max) NULL,
|
85
|
+
[cad_drawing] image NULL,
|
86
|
+
[flags] bit default 0 NULL,
|
87
|
+
[number_in_stock] smallint default 500,
|
88
|
+
[number_sold] int default 0,
|
89
|
+
[super_number] bigint default 9223372036854775807,
|
90
|
+
[weight] float default 1.23,
|
91
|
+
[cost1] real default 10.23 NULL,
|
92
|
+
[cost2] decimal(8,2) default 50.23 NULL,
|
93
|
+
[release_date] smalldatetime default '2008-02-14' NULL, -- date type is SQL Server 2008 only
|
94
|
+
[release_datetime] datetime default '2008-02-14 00:31:12' NULL,
|
95
|
+
[release_timestamp] smalldatetime /* default '2008-02-14 00:31:31' */ NULL,
|
96
|
+
-- [status] enum('active','out of stock') NOT NULL default 'active',
|
97
|
+
PRIMARY KEY ([id])
|
98
|
+
);
|
99
|
+
EOF
|
100
|
+
|
101
|
+
1.upto(16) do |n|
|
102
|
+
conn.create_command(<<-EOF).execute_non_query #(::Extlib::ByteArray.new("CAD \001 \000 DRAWING"))
|
103
|
+
insert into widgets(code, name, shelf_location, description, image_data, ad_description, ad_image, whitepaper_text, cad_drawing, super_number, weight)
|
104
|
+
VALUES ('W#{n.to_s.rjust(7,"0")}', 'Widget #{n}', 'A14', 'This is a description', 'IMAGE DATA', 'Buy this product now!', 'AD IMAGE DATA', 'String', 0x2145475754578785 , 1234, 13.4);
|
105
|
+
EOF
|
106
|
+
end
|
107
|
+
|
108
|
+
conn.create_command(<<-EOF).execute_non_query
|
109
|
+
update widgets set flags = 1 where id = 2
|
110
|
+
EOF
|
111
|
+
|
112
|
+
conn.create_command(<<-EOF).execute_non_query
|
113
|
+
update widgets set ad_description = NULL where id = 3
|
114
|
+
EOF
|
115
|
+
|
116
|
+
conn.create_command(<<-EOF).execute_non_query
|
117
|
+
update widgets set flags = NULL where id = 4
|
118
|
+
EOF
|
119
|
+
|
120
|
+
conn.create_command(<<-EOF).execute_non_query
|
121
|
+
update widgets set cost1 = NULL where id = 5
|
122
|
+
EOF
|
123
|
+
|
124
|
+
conn.create_command(<<-EOF).execute_non_query
|
125
|
+
update widgets set cost2 = NULL where id = 6
|
126
|
+
EOF
|
127
|
+
|
128
|
+
conn.create_command(<<-EOF).execute_non_query
|
129
|
+
update widgets set release_date = NULL where id = 7
|
130
|
+
EOF
|
131
|
+
|
132
|
+
conn.create_command(<<-EOF).execute_non_query
|
133
|
+
update widgets set release_datetime = NULL where id = 8
|
134
|
+
EOF
|
135
|
+
|
136
|
+
# (cannot update a Timestamp column w/MSSQL)
|
137
|
+
# so we use a smalldatetime
|
138
|
+
conn.create_command(<<-EOF).execute_non_query
|
139
|
+
update widgets set release_timestamp = NULL where id = 9
|
140
|
+
EOF
|
141
|
+
|
142
|
+
conn.create_command(<<-EOF).execute_non_query
|
143
|
+
update widgets set release_datetime = '2008-07-14 00:31:12' where id = 10
|
144
|
+
EOF
|
145
|
+
|
146
|
+
conn.close
|
147
|
+
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
151
|
+
|
152
|
+
RSpec.configure do |config|
|
153
|
+
config.include(DataObjectsSpecHelpers)
|
154
|
+
config.include(DataObjects::Spec::PendingHelpers)
|
155
|
+
end
|