sequel 2.3.0 → 2.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +16 -0
- data/README +4 -1
- data/Rakefile +17 -19
- data/doc/prepared_statements.rdoc +104 -0
- data/doc/sharding.rdoc +113 -0
- data/lib/sequel_core/adapters/ado.rb +24 -17
- data/lib/sequel_core/adapters/db2.rb +30 -33
- data/lib/sequel_core/adapters/dbi.rb +15 -13
- data/lib/sequel_core/adapters/informix.rb +13 -14
- data/lib/sequel_core/adapters/jdbc.rb +243 -60
- data/lib/sequel_core/adapters/jdbc/mysql.rb +32 -24
- data/lib/sequel_core/adapters/jdbc/postgresql.rb +32 -2
- data/lib/sequel_core/adapters/jdbc/sqlite.rb +16 -20
- data/lib/sequel_core/adapters/mysql.rb +164 -76
- data/lib/sequel_core/adapters/odbc.rb +21 -34
- data/lib/sequel_core/adapters/openbase.rb +10 -7
- data/lib/sequel_core/adapters/oracle.rb +17 -23
- data/lib/sequel_core/adapters/postgres.rb +246 -35
- data/lib/sequel_core/adapters/shared/mssql.rb +106 -0
- data/lib/sequel_core/adapters/shared/mysql.rb +34 -26
- data/lib/sequel_core/adapters/shared/postgres.rb +82 -38
- data/lib/sequel_core/adapters/shared/sqlite.rb +48 -16
- data/lib/sequel_core/adapters/sqlite.rb +141 -44
- data/lib/sequel_core/connection_pool.rb +85 -63
- data/lib/sequel_core/database.rb +46 -17
- data/lib/sequel_core/dataset.rb +21 -40
- data/lib/sequel_core/dataset/convenience.rb +3 -3
- data/lib/sequel_core/dataset/prepared_statements.rb +218 -0
- data/lib/sequel_core/exceptions.rb +0 -12
- data/lib/sequel_model/base.rb +1 -2
- data/lib/sequel_model/plugins.rb +1 -1
- data/spec/adapters/ado_spec.rb +32 -3
- data/spec/adapters/mysql_spec.rb +7 -8
- data/spec/integration/prepared_statement_test.rb +106 -0
- data/spec/sequel_core/connection_pool_spec.rb +105 -3
- data/spec/sequel_core/database_spec.rb +41 -3
- data/spec/sequel_core/dataset_spec.rb +117 -7
- data/spec/sequel_core/spec_helper.rb +2 -2
- data/spec/sequel_model/model_spec.rb +0 -6
- data/spec/sequel_model/spec_helper.rb +1 -1
- metadata +11 -6
- data/lib/sequel_core/adapters/adapter_skeleton.rb +0 -54
- data/lib/sequel_core/adapters/odbc_mssql.rb +0 -106
data/CHANGELOG
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
=== 2.4.0 (2008-08-06)
|
2
|
+
|
3
|
+
* Handle Java::JavaSql::Date type in the JDBC adapter (jeremyevans)
|
4
|
+
|
5
|
+
* Add support for read-only slave/writable master databases and database sharding (jeremyevans)
|
6
|
+
|
7
|
+
* Remove InvalidExpression, InvalidFilter, InvalidJoinType, and WorkerStop exceptions (jeremyevans)
|
8
|
+
|
9
|
+
* Add prepared statement/bound variable support (jeremyevans)
|
10
|
+
|
11
|
+
* Fix anonymous column names in the ADO adapter (nusco)
|
12
|
+
|
13
|
+
* Remove odbc_mssql adapter, use :db_type=>'mssql' option instead (jeremyevans)
|
14
|
+
|
15
|
+
* Split MSSQL specific syntax into separate file, usable by ADO and ODBC adapters (nusco, jeremyevans)
|
16
|
+
|
1
17
|
=== 2.3.0 (2008-07-25)
|
2
18
|
|
3
19
|
* Enable almost full support for MySQL using JDBC (jeremyevans)
|
data/README
CHANGED
@@ -6,6 +6,8 @@ Sequel is a lightweight database access toolkit for Ruby.
|
|
6
6
|
for constructing database queries and table schemas.
|
7
7
|
* Sequel also includes a lightweight but comprehensive ORM layer for
|
8
8
|
mapping records to Ruby objects and handling associated records.
|
9
|
+
* Sequel supports advanced database features such as prepared statements,
|
10
|
+
bound variables, master/slave configurations, and database sharding.
|
9
11
|
* Sequel makes it easy to deal with multiple records without having
|
10
12
|
to break your teeth on SQL.
|
11
13
|
* Sequel currently has adapters for ADO, DB2, DBI, Informix, JDBC,
|
@@ -13,10 +15,11 @@ Sequel is a lightweight database access toolkit for Ruby.
|
|
13
15
|
|
14
16
|
== Resources
|
15
17
|
|
18
|
+
* {Website}[http://sequel.rubyforge.org]
|
16
19
|
* {Source code}[http://github.com/jeremyevans/sequel]
|
17
20
|
* {Bug tracking}[http://code.google.com/p/ruby-sequel/issues/list]
|
18
21
|
* {Google group}[http://groups.google.com/group/sequel-talk]
|
19
|
-
* {RDoc}[http://sequel.rubyforge.org]
|
22
|
+
* {RDoc}[http://sequel.rubyforge.org/rdoc]
|
20
23
|
|
21
24
|
To check out the source code:
|
22
25
|
|
data/Rakefile
CHANGED
@@ -8,14 +8,13 @@ require "spec/rake/spectask"
|
|
8
8
|
include FileUtils
|
9
9
|
|
10
10
|
NAME = 'sequel'
|
11
|
-
VERS = '2.
|
12
|
-
CLEAN.include ["**/.*.sw?", "pkg", ".config", "rdoc", "coverage"]
|
11
|
+
VERS = '2.4.0'
|
12
|
+
CLEAN.include ["**/.*.sw?", "pkg", ".config", "rdoc", "coverage", "www/public/*.html"]
|
13
13
|
RDOC_OPTS = ["--quiet", "--line-numbers", "--inline-source", '--title', \
|
14
14
|
'Sequel: The Database Toolkit for Ruby', '--main', 'README']
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
##############################################################################
|
16
|
+
# Gem Packaging and Release
|
17
|
+
|
19
18
|
desc "Packages sequel"
|
20
19
|
task :package=>[:clean]
|
21
20
|
spec = Gem::Specification.new do |s|
|
@@ -64,25 +63,26 @@ task :release=>[:package] do
|
|
64
63
|
sh %{rubyforge add_file sequel #{NAME} #{VERS} pkg/#{NAME}-#{VERS}.gem}
|
65
64
|
end
|
66
65
|
|
67
|
-
|
68
|
-
|
69
|
-
##############################################################################
|
66
|
+
### RDoc
|
67
|
+
|
70
68
|
Rake::RDocTask.new do |rdoc|
|
71
69
|
rdoc.rdoc_dir = "rdoc"
|
72
70
|
rdoc.options += RDOC_OPTS
|
73
71
|
rdoc.rdoc_files.add %w"README CHANGELOG COPYING lib/**/*.rb doc/*.rdoc"
|
74
72
|
end
|
75
73
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
74
|
+
### Website
|
75
|
+
|
76
|
+
desc "Update sequel.rubyforge.org"
|
77
|
+
task :website => [:rdoc]
|
78
|
+
task :website do
|
79
|
+
sh %{www/make_www.rb}
|
80
|
+
sh %{scp -r www/public/* rubyforge.org:/var/www/gforge-projects/sequel/}
|
81
|
+
sh %{scp -r rdoc/* rubyforge.org:/var/www/gforge-projects/sequel/rdoc/}
|
81
82
|
end
|
82
83
|
|
83
|
-
|
84
|
-
|
85
|
-
##############################################################################
|
84
|
+
### Specs
|
85
|
+
|
86
86
|
lib_dir = File.join(File.dirname(__FILE__), 'lib')
|
87
87
|
fixRUBYLIB = Proc.new{ENV['RUBYLIB'] ? (ENV['RUBYLIB'] += ":#{lib_dir}") : (ENV['RUBYLIB'] = lib_dir)}
|
88
88
|
sequel_core_specs = "spec/sequel_core/*_spec.rb"
|
@@ -141,9 +141,7 @@ task :dcov do
|
|
141
141
|
sh %{find lib -name '*.rb' | xargs dcov}
|
142
142
|
end
|
143
143
|
|
144
|
-
|
145
|
-
# Statistics
|
146
|
-
##############################################################################
|
144
|
+
### Statistics
|
147
145
|
|
148
146
|
STATS_DIRECTORIES = [
|
149
147
|
%w(Code lib/),
|
@@ -0,0 +1,104 @@
|
|
1
|
+
= Prepared Statements and Bound Variables
|
2
|
+
|
3
|
+
Starting with version 2.4.0, Sequel has support for prepared statements and
|
4
|
+
bound variables. No matter which database you are using, the Sequel prepared
|
5
|
+
statement/bound variable API remains exactly the same. There is native support
|
6
|
+
for prepared statements/bound variables on the following databases:
|
7
|
+
|
8
|
+
* PostgreSQL (using the pg driver, requires type specifiers)
|
9
|
+
* MySQL (prepared statements only, as the ruby mysql driver doesn't support
|
10
|
+
bound variables)
|
11
|
+
* SQLite (a new native prepared statement is used for each call, though)
|
12
|
+
* JDBC (using the postgresql, mysql, or sqlite databases, and possibly others)
|
13
|
+
|
14
|
+
Support on other databases is emulated via the usual string interpolation.
|
15
|
+
|
16
|
+
== Placeholders
|
17
|
+
|
18
|
+
Generally, when using prepared statements (and certainly when using bound
|
19
|
+
variables), you need to put placeholders in your SQL to indicate where you
|
20
|
+
want your bound arguments to appear. Database support and syntax vary
|
21
|
+
significantly for placeholders (e.g. :name, $1, ?). Sequel abstracts all of
|
22
|
+
that and allows you to specify placeholders by using the :$name format for
|
23
|
+
placeholders, e.g.:
|
24
|
+
|
25
|
+
ds = DB[:items].filter(:name=>:$name)
|
26
|
+
|
27
|
+
== Bound Variables
|
28
|
+
|
29
|
+
Using bound variables for this query is simple:
|
30
|
+
|
31
|
+
ds.call(:select, :name=>'Jim')
|
32
|
+
|
33
|
+
This will do the equivalent of selecting records that have the name 'Jim'. It
|
34
|
+
returns all records, and can take a block that is passed to Dataset#all.
|
35
|
+
|
36
|
+
Deleting or returning the first record works similarly:
|
37
|
+
|
38
|
+
ds.call(:first, :name=>'Jim') # First record with name 'Jim'
|
39
|
+
ds.call(:delete, :name=>'Jim') # Delete records with name 'Jim'
|
40
|
+
|
41
|
+
For inserting/updating records, you should also specify a value hash, which
|
42
|
+
may itself contain placeholders:
|
43
|
+
|
44
|
+
# Insert record with 'Jim', note that the previous filter is ignored
|
45
|
+
ds.call(:insert, {:name=>'Jim'}, :name=>:$name)
|
46
|
+
# Change name to 'Bob' for all records with name of 'Jim'
|
47
|
+
ds.call(:update, {:name=>'Jim', :new_name=>'Bob'}, :name=>$:new_name)
|
48
|
+
|
49
|
+
== Prepared Statements
|
50
|
+
|
51
|
+
Prepared statement support is similar to bound variable support, but you
|
52
|
+
use Dataset#prepare with a name, and Dataset#call later with the values:
|
53
|
+
|
54
|
+
ds = DB[:items].filter(:name=>:$name)
|
55
|
+
ps = ds.prepare(:select, :select_by_name)
|
56
|
+
ps.call(:name=>'Jim')
|
57
|
+
DB.call(:select_by_name, :name=>'Jim') # same as above
|
58
|
+
|
59
|
+
The Dataset#prepare method returns a prepared statement, and also stores a
|
60
|
+
copy of the prepared statement in the database for later use. For insert
|
61
|
+
and update queries, the hash to insert/update is passed to prepare:
|
62
|
+
|
63
|
+
ps1 = DB[:items].prepare(:insert, :insert_with_name, :name=>:$name)
|
64
|
+
ps1.call(:name=>'Jim')
|
65
|
+
DB.call(:insert_with_name, :name=>'Jim') # same as above
|
66
|
+
ds = DB[:items].filter(:name=>:$name)
|
67
|
+
ps2 = ds.prepare(:update, :update_name, :name=>:$new_name)
|
68
|
+
ps2.call(:name=>'Jim', :new_name=>'Bob')
|
69
|
+
DB.call(:update_name, :name=>'Jim', :new_name=>'Bob') # same as above
|
70
|
+
|
71
|
+
== Database support
|
72
|
+
|
73
|
+
=== PostgreSQL
|
74
|
+
|
75
|
+
If you are using the ruby-postgres or postgres-pr driver, PostgreSQL uses the
|
76
|
+
default emulated support. If you are using ruby-pg, there is native support,
|
77
|
+
but it requires type specifiers most of the time. This is easy if you have
|
78
|
+
direct control over the SQL string, but since Sequel abstracts that, the types
|
79
|
+
have to be specified another way. This is done by adding a __* suffix to the
|
80
|
+
placeholder symbol (e.g. :$name__text, which will be compiled to "$1::text"
|
81
|
+
in the SQL). Prepared statements are always server side.
|
82
|
+
|
83
|
+
=== SQLite
|
84
|
+
|
85
|
+
SQLite supports bound variables and prepared statements exactly the same, since
|
86
|
+
a new native prepared statement is created and executed for each call.
|
87
|
+
|
88
|
+
=== MySQL
|
89
|
+
|
90
|
+
The MySQL ruby driver does not support bound variables, so the the bound
|
91
|
+
variable methods fall back to string interpolation. It uses server side
|
92
|
+
prepared statements.
|
93
|
+
|
94
|
+
=== JDBC
|
95
|
+
|
96
|
+
JDBC supports both prepared statements and bound variables. Whether these
|
97
|
+
are server side or client side depends on the JDBC driver. For PostgreSQL
|
98
|
+
over JDBC, you can add the prepareThreshold=N parameter to the connection
|
99
|
+
string, which will use a server side prepared statement after N calls to
|
100
|
+
the prepared statement.
|
101
|
+
|
102
|
+
=== All Others
|
103
|
+
|
104
|
+
Support is emulated using interpolation.
|
data/doc/sharding.rdoc
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
= Read-Only Slaves/Writable Master and Database Sharding
|
2
|
+
|
3
|
+
Starting with version 2.4.0, Sequel has support for read only slave databases
|
4
|
+
with a writable master database, as well as database sharding (where you can
|
5
|
+
pick a database connection to use for a given dataset). Support for both
|
6
|
+
features is database independent, and should work for all database adapters
|
7
|
+
included with Sequel.
|
8
|
+
|
9
|
+
== The :servers Database option
|
10
|
+
|
11
|
+
Both features use the new :servers Database option. The :servers option should
|
12
|
+
be a hash with symbol keys and values that are either hashes or procs that
|
13
|
+
return hashes. Note that all servers should have the same schema, unless you
|
14
|
+
really know what you are doing.
|
15
|
+
|
16
|
+
== Master and Slave Database Configurations
|
17
|
+
|
18
|
+
=== Single Read-Only Slave, Single Master
|
19
|
+
|
20
|
+
To use a single, read-only slave that handles SELECT queries, the following
|
21
|
+
is the simplest configuration:
|
22
|
+
|
23
|
+
DB=Sequel.connect('postgres://master_server/database', \
|
24
|
+
:servers=>{:read_only=>{:host=>'slave_server'}})
|
25
|
+
|
26
|
+
This will use the host slave_server for SELECT queries and master_server for
|
27
|
+
other queries.
|
28
|
+
|
29
|
+
=== Multiple Read-Only Slaves, Single Master
|
30
|
+
|
31
|
+
Let's say you have 4 slave database servers with names slave_server0,
|
32
|
+
slave_server1, slave_server2, and slave_server3.
|
33
|
+
|
34
|
+
DB=Sequel.connect('postgres://master_server/database', \
|
35
|
+
:servers=>{:read_only=>proc{|db| :host=>db.get_slave_host}})
|
36
|
+
def DB.get_slave_host
|
37
|
+
@current_host ||= -1
|
38
|
+
"slave_server#{(@current_host+=1)%4}"
|
39
|
+
end
|
40
|
+
|
41
|
+
This will use one of the slave servers for SELECT queries and use the
|
42
|
+
master_server for other queries. It's also possible to pick a random host
|
43
|
+
instead of using the round robin approach presented above, but that can result
|
44
|
+
in less optimal resource usage.
|
45
|
+
|
46
|
+
=== Multiple Read-Only Slaves, Multiple Masters
|
47
|
+
|
48
|
+
This involves the same basic idea as the multiple slaves, single master, but
|
49
|
+
it shows that the master database is named :default. So for 4 masters and
|
50
|
+
4 slaves:
|
51
|
+
|
52
|
+
DB=Sequel.connect('postgres://master_server/database', \
|
53
|
+
:servers=>{:read_only=>proc{|db| :host=>db.get_slave_host}, \
|
54
|
+
:default=>proc{|db| :host=>db.get_master_host}})
|
55
|
+
def DB.get_slave_host
|
56
|
+
@current_slave_host ||= -1
|
57
|
+
"slave_server#{(@current_slave_host+=1)%4}"
|
58
|
+
end
|
59
|
+
def DB.get_master_host
|
60
|
+
@current_master_host ||= -1
|
61
|
+
"master_server#{(@current_master_host+=1)%4}"
|
62
|
+
end
|
63
|
+
|
64
|
+
== Sharding
|
65
|
+
|
66
|
+
There is specific support in Sequel for handling master/slave database
|
67
|
+
combinations, with the only necessary setup being the database configuration.
|
68
|
+
However, since sharding is always going to be implementation dependent, Sequel
|
69
|
+
supplies the basic infrastructure, but you have to tell it which server to use
|
70
|
+
for each dataset. Let's assume the simple scenario, a distributed rainbow
|
71
|
+
table for SHA-1 hashes, sharding based on the first hex character (for a total
|
72
|
+
of 16 shards). First, you need to configure the database:
|
73
|
+
|
74
|
+
servers = {}
|
75
|
+
(('0'..'9').to_a + ('a'..'f').to_a).each do |hex|
|
76
|
+
servers[hex.to_sym] = {:host=>"hash_host_#{hex}"}
|
77
|
+
end
|
78
|
+
DB=Sequel.connect('postgres://hash_host/hashes', :servers=>servers)
|
79
|
+
|
80
|
+
This configures 17 servers, the 16 shard servers (/hash_host_[0-9a-f]/), and 1
|
81
|
+
default server which will be used if no shard is specified ("hash_host"). If
|
82
|
+
you want the default server to be one of the shard servers (e.g. hash_host_a),
|
83
|
+
it's easiest to do:
|
84
|
+
|
85
|
+
DB=Sequel.connect('postgres://hash_host_a/hashes', :servers=>servers)
|
86
|
+
|
87
|
+
That will still set up a second pool of connections for the default server,
|
88
|
+
since it considers the default server and shard servers independent. Note that
|
89
|
+
if you always set the shard on a dataset before using it in queries, it will
|
90
|
+
not attempt to connect to the default server. Sequel may use the default
|
91
|
+
server in queries it generates itself, such as to get column names or table
|
92
|
+
schemas, so it is always good to have a default server that works.
|
93
|
+
|
94
|
+
To set the shard for a given query, you use the Dataset#server method:
|
95
|
+
|
96
|
+
DB[:hashes].server(:a).filter(:hash=>/31337/)
|
97
|
+
|
98
|
+
That will return all matching rows on the hash_host_a shard that have a hash
|
99
|
+
column that contains 31337.
|
100
|
+
|
101
|
+
Rainbow tables are generally used to find specific hashes, so to save some
|
102
|
+
work, you might want to add a method to the dataset that automatically sets
|
103
|
+
the shard to use. This is fairly easy using a Sequel::Model:
|
104
|
+
|
105
|
+
class Rainbow < Sequel::Model(:hashes)
|
106
|
+
def_dataset_method(:plaintext_for_hash) do |hash|
|
107
|
+
raise(ArgumentError, 'Invalid SHA-1 Hash') unless /\A[0-9a-f]{40}\z/.match(hash)
|
108
|
+
row = self.server(hash[0...1].to_sym).first(:hash=>hash)
|
109
|
+
row[:plaintext] if row
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
Rainbow.plaintext_for_hash("e580726d31f6e1ad216ffd87279e536d1f74e606")
|
@@ -13,15 +13,16 @@ module Sequel
|
|
13
13
|
module ADO
|
14
14
|
class Database < Sequel::Database
|
15
15
|
set_adapter_scheme :ado
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
16
|
+
|
17
|
+
def connect(server)
|
18
|
+
opts = server_opts(server)
|
19
|
+
opts[:driver] ||= 'SQL Server'
|
20
|
+
case opts[:driver]
|
21
|
+
when 'SQL Server'
|
22
|
+
require 'sequel_core/adapters/shared/mssql'
|
23
|
+
extend Sequel::MSSQL::DatabaseMethods
|
24
|
+
end
|
25
|
+
s = "driver=#{opts[:driver]};server=#{opts[:host]};database=#{opts[:database]}#{";uid=#{opts[:user]};pwd=#{opts[:password]}" if opts[:user]}"
|
25
26
|
handle = WIN32OLE.new('ADODB.Connection')
|
26
27
|
handle.Open(s)
|
27
28
|
handle
|
@@ -35,11 +36,14 @@ module Sequel
|
|
35
36
|
ADO::Dataset.new(self, opts)
|
36
37
|
end
|
37
38
|
|
38
|
-
def execute(sql)
|
39
|
+
def execute(sql, opts={})
|
39
40
|
log_info(sql)
|
40
|
-
|
41
|
+
synchronize(opts[:server]) do |conn|
|
42
|
+
r = conn.Execute(sql)
|
43
|
+
yield(r) if block_given?
|
44
|
+
r
|
45
|
+
end
|
41
46
|
end
|
42
|
-
|
43
47
|
alias_method :do, :execute
|
44
48
|
end
|
45
49
|
|
@@ -55,11 +59,12 @@ module Sequel
|
|
55
59
|
end
|
56
60
|
end
|
57
61
|
|
58
|
-
def fetch_rows(sql
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
62
|
+
def fetch_rows(sql)
|
63
|
+
execute(sql) do |s|
|
64
|
+
@columns = s.Fields.extend(Enumerable).map do |column|
|
65
|
+
name = column.Name.empty? ? '(no column name)' : column.Name
|
66
|
+
name.to_sym
|
67
|
+
end
|
63
68
|
|
64
69
|
unless s.eof
|
65
70
|
s.moveFirst
|
@@ -69,6 +74,8 @@ module Sequel
|
|
69
74
|
self
|
70
75
|
end
|
71
76
|
|
77
|
+
private
|
78
|
+
|
72
79
|
def hash_row(row)
|
73
80
|
@columns.inject({}) do |m, c|
|
74
81
|
m[c] = row.shift
|
@@ -6,29 +6,15 @@ module Sequel
|
|
6
6
|
set_adapter_scheme :db2
|
7
7
|
include DB2CLI
|
8
8
|
|
9
|
-
# AUTO_INCREMENT = 'IDENTITY(1,1)'.freeze
|
10
|
-
#
|
11
|
-
# def auto_increment_sql
|
12
|
-
# AUTO_INCREMENT
|
13
|
-
# end
|
14
|
-
|
15
|
-
def check_error(rc, msg)
|
16
|
-
case rc
|
17
|
-
when SQL_SUCCESS, SQL_SUCCESS_WITH_INFO
|
18
|
-
nil
|
19
|
-
else
|
20
|
-
raise Error, msg
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
9
|
rc, @@env = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE)
|
25
|
-
check_error(rc, "Could not allocate DB2 environment")
|
10
|
+
#check_error(rc, "Could not allocate DB2 environment")
|
26
11
|
|
27
|
-
def connect
|
12
|
+
def connect(server)
|
13
|
+
opts = server_opts(server)
|
28
14
|
rc, dbc = SQLAllocHandle(SQL_HANDLE_DBC, @@env)
|
29
15
|
check_error(rc, "Could not allocate database connection")
|
30
16
|
|
31
|
-
rc = SQLConnect(dbc,
|
17
|
+
rc = SQLConnect(dbc, opts[:database], opts[:user], opts[:password])
|
32
18
|
check_error(rc, "Could not connect to database")
|
33
19
|
|
34
20
|
dbc
|
@@ -44,8 +30,8 @@ module Sequel
|
|
44
30
|
end
|
45
31
|
end
|
46
32
|
|
47
|
-
def test_connection
|
48
|
-
|
33
|
+
def test_connection(server=nil)
|
34
|
+
synchronize(server){|conn|}
|
49
35
|
true
|
50
36
|
end
|
51
37
|
|
@@ -53,9 +39,9 @@ module Sequel
|
|
53
39
|
DB2::Dataset.new(self, opts)
|
54
40
|
end
|
55
41
|
|
56
|
-
def execute(sql,
|
42
|
+
def execute(sql, opts={})
|
57
43
|
log_info(sql)
|
58
|
-
|
44
|
+
synchronize(opts[:server]) do |conn|
|
59
45
|
rc, sth = SQLAllocHandle(SQL_HANDLE_STMT, @handle)
|
60
46
|
check_error(rc, "Could not allocate statement")
|
61
47
|
|
@@ -63,7 +49,7 @@ module Sequel
|
|
63
49
|
rc = SQLExecDirect(sth, sql)
|
64
50
|
check_error(rc, "Could not execute statement")
|
65
51
|
|
66
|
-
|
52
|
+
yield(sth) if block_given?
|
67
53
|
|
68
54
|
rc, rpc = SQLRowCount(sth)
|
69
55
|
check_error(rc, "Could not get RPC")
|
@@ -75,9 +61,22 @@ module Sequel
|
|
75
61
|
end
|
76
62
|
end
|
77
63
|
alias_method :do, :execute
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def check_error(rc, msg)
|
68
|
+
case rc
|
69
|
+
when SQL_SUCCESS, SQL_SUCCESS_WITH_INFO
|
70
|
+
nil
|
71
|
+
else
|
72
|
+
raise Error, msg
|
73
|
+
end
|
74
|
+
end
|
78
75
|
end
|
79
76
|
|
80
77
|
class Dataset < Sequel::Dataset
|
78
|
+
MAX_COL_SIZE = 256
|
79
|
+
|
81
80
|
def literal(v)
|
82
81
|
case v
|
83
82
|
when Time
|
@@ -89,21 +88,19 @@ module Sequel
|
|
89
88
|
end
|
90
89
|
end
|
91
90
|
|
92
|
-
def fetch_rows(sql
|
93
|
-
|
94
|
-
@
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
yield hash_row(sth)
|
100
|
-
end
|
91
|
+
def fetch_rows(sql)
|
92
|
+
execute(sql) do |sth|
|
93
|
+
@column_info = get_column_info(sth)
|
94
|
+
@columns = @column_info.map {|c| c[:name]}
|
95
|
+
while (rc = SQLFetch(@handle)) != SQL_NO_DATA_FOUND
|
96
|
+
@db.check_error(rc, "Could not fetch row")
|
97
|
+
yield hash_row(sth)
|
101
98
|
end
|
102
99
|
end
|
103
100
|
self
|
104
101
|
end
|
105
102
|
|
106
|
-
|
103
|
+
private
|
107
104
|
|
108
105
|
def get_column_info(sth)
|
109
106
|
rc, column_count = SQLNumResultCols(sth)
|