sequel 2.3.0 → 2.4.0
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.
- 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)
|