-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
tinytds.rb
129 lines (114 loc) · 4 KB
/
tinytds.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
require 'tiny_tds'
Sequel.require 'adapters/shared/mssql'
module Sequel
module TinyTDS
class Database < Sequel::Database
include Sequel::MSSQL::DatabaseMethods
set_adapter_scheme :tinytds
# Transfer the :host and :user options to the
# :dataserver and :username options.
def connect(server)
opts = server_opts(server)
opts[:username] = opts[:user]
set_mssql_unicode_strings
TinyTds::Client.new(opts)
end
# Return instance of Sequel::TinyTDS::Dataset with the given options.
def dataset(opts = nil)
TinyTDS::Dataset.new(self, opts)
end
# Execute the given +sql+ on the server. If the :return option
# is present, its value should be a method symbol that is called
# on the TinyTds::Result object returned from executing the
# +sql+. The value of such a method is returned to the caller.
# Otherwise, if a block is given, it is yielded the result object.
# If no block is given and a :return is not present, +nil+ is returned.
def execute(sql, opts={})
synchronize(opts[:server]) do |c|
begin
m = opts[:return]
r = nil
log_yield(sql) do
r = c.execute(sql)
return r.send(m) if m
end
yield(r) if block_given?
rescue TinyTds::Error => e
raise_error(e, :disconnect=>!c.active?)
ensure
r.cancel if r && c.sqlsent?
end
end
end
# Return the number of rows modified by the given +sql+.
def execute_dui(sql, opts={})
execute(sql, opts.merge(:return=>:do))
end
# Return the value of the autogenerated primary key (if any)
# for the row inserted by the given +sql+.
def execute_insert(sql, opts={})
execute(sql, opts.merge(:return=>:insert))
end
# Execute the DDL +sql+ on the database and return nil.
def execute_ddl(sql, opts={})
execute(sql, opts.merge(:return=>:each))
nil
end
private
# For some reason, unless you specify a column can be
# NULL, it assumes NOT NULL, so turn NULL on by default unless
# the column is a primary key column.
def column_list_sql(g)
pks = []
g.constraints.each{|c| pks = c[:columns] if c[:type] == :primary_key}
g.columns.each{|c| c[:null] = true if !pks.include?(c[:name]) && !c[:primary_key] && !c.has_key?(:null) && !c.has_key?(:allow_null)}
super
end
# Close the TinyTds::Client object.
def disconnect_connection(c)
c.close
end
def disconnect_error(e, opts)
super || (opts[:conn] && !opts[:conn].active?)
end
end
class Dataset < Sequel::Dataset
include Sequel::MSSQL::DatasetMethods
# Yield hashes with symbol keys, attempting to optimize for
# various cases.
def fetch_rows(sql)
execute(sql) do |result|
each_opts = {:cache_rows=>false}
each_opts[:timezone] = :utc if Sequel.database_timezone == :utc
offset = @opts[:offset]
@columns = cols = result.fields.map{|c| output_identifier(c)}
if identifier_output_method
each_opts[:as] = :array
result.each(each_opts) do |r|
h = {}
cols.zip(r).each{|k, v| h[k] = v}
h.delete(row_number_column) if offset
yield h
end
else
each_opts[:symbolize_keys] = true
if offset
result.each(each_opts) do |r|
r.delete(row_number_column) if offset
yield r
end
else
result.each(each_opts, &Proc.new)
end
end
end
self
end
private
# Properly escape the given string +v+.
def literal_string(v)
s = db.synchronize{|c| "#{'N' if mssql_unicode_strings}'#{c.escape(v)}'"}
end
end
end
end