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.
- 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
|