/usr/lib/ruby/vendor_ruby/sequel/adapters/tinytds.rb is in ruby-sequel 3.33.0-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
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 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 | 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
# 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
if (args = opts[:arguments]) && !args.empty?
types = []
values = []
args.each_with_index do |(k, v), i|
v, type = ps_arg_type(v)
types << "@#{k} #{type}"
values << "@#{k} = #{v}"
end
case m
when :do
sql = "#{sql}; SELECT @@ROWCOUNT AS AffectedRows"
single_value = true
when :insert
sql = "#{sql}; SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident"
single_value = true
end
sql = "EXEC sp_executesql N'#{c.escape(sql)}', N'#{c.escape(types.join(', '))}', #{values.join(', ')}"
log_yield(sql) do
r = c.execute(sql)
r.each{|row| return row.values.first} if single_value
end
else
log_yield(sql) do
r = c.execute(sql)
return r.send(m) if m
end
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
# tiny_tds uses TinyTds::Error as the base error class.
def database_error_classes
[TinyTds::Error]
end
# Close the TinyTds::Client object.
def disconnect_connection(c)
c.close
end
# Return true if the :conn argument is present and not active.
def disconnect_error?(e, opts)
super || (opts[:conn] && !opts[:conn].active?)
end
# Return a 2 element array with the literal value and type to use
# in the prepared statement call for the given value and connection.
def ps_arg_type(v)
case v
when Fixnum
[v, 'int']
when Bignum
[v, 'bigint']
when Float
[v, 'double precision']
when Numeric
[v, 'numeric']
when Time
if v.is_a?(SQLTime)
[literal(v), 'time']
else
[literal(v), 'datetime']
end
when DateTime
[literal(v), 'datetime']
when Date
[literal(v), 'date']
when nil
['NULL', 'nvarchar(max)']
when true
['1', 'int']
when false
['0', 'int']
when SQL::Blob
[literal(v), 'varbinary(max)']
else
[literal(v), 'nvarchar(max)']
end
end
end
class Dataset < Sequel::Dataset
include Sequel::MSSQL::DatasetMethods
Database::DatasetClass = self
# SQLite already supports named bind arguments, so use directly.
module ArgumentMapper
include Sequel::Dataset::ArgumentMapper
protected
# Return a hash with the same values as the given hash,
# but with the keys converted to strings.
def map_to_prepared_args(hash)
args = {}
hash.each{|k,v| args[k.to_s.gsub('.', '__')] = v}
args
end
private
# SQLite uses a : before the name of the argument for named
# arguments.
def prepared_arg(k)
LiteralString.new("@#{k.to_s.gsub('.', '__')}")
end
# Always assume a prepared argument.
def prepared_arg?(k)
true
end
end
# SQLite prepared statement uses a new prepared statement each time
# it is called, but it does use the bind arguments.
module PreparedStatementMethods
include ArgumentMapper
private
# Run execute_select on the database with the given SQL and the stored
# bind arguments.
def execute(sql, opts={}, &block)
super(prepared_sql, {:arguments=>bind_arguments}.merge(opts), &block)
end
# Same as execute, explicit due to intricacies of alias and super.
def execute_dui(sql, opts={}, &block)
super(prepared_sql, {:arguments=>bind_arguments}.merge(opts), &block)
end
# Same as execute, explicit due to intricacies of alias and super.
def execute_insert(sql, opts={}, &block)
super(prepared_sql, {:arguments=>bind_arguments}.merge(opts), &block)
end
end
# 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 db.timezone == :utc
rn = row_number_column if offset = @opts[:offset]
columns = cols = result.fields.map{|c| output_identifier(c)}
if offset
rn = row_number_column
columns = columns.dup
columns.delete(rn)
end
@columns = columns
#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(rn) if rn
yield h
end
=begin
# Temporarily disable this optimization, as tiny_tds uses string keys
# if result.fields is called before result.each(:symbolize_keys=>true).
# See https://github.com/rails-sqlserver/tiny_tds/issues/57
else
each_opts[:symbolize_keys] = true
if offset
result.each(each_opts) do |r|
r.delete(rn) if rn
yield r
end
else
result.each(each_opts, &Proc.new)
end
end
=end
end
self
end
# Create a named prepared statement that is stored in the
# database (and connection) for reuse.
def prepare(type, name=nil, *values)
ps = to_prepared_statement(type, values)
ps.extend(PreparedStatementMethods)
if name
ps.prepared_statement_name = name
db.prepared_statements[name] = ps
end
ps
end
private
# Properly escape the given string +v+.
def literal_string_append(sql, v)
sql << 'N' if mssql_unicode_strings
sql << "'" << db.synchronize{|c| c.escape(v)} << "'"
end
end
end
end
|