sequel 4.14.0 → 4.15.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +32 -0
- data/README.rdoc +3 -3
- data/Rakefile +1 -1
- data/doc/opening_databases.rdoc +20 -2
- data/doc/release_notes/4.15.0.txt +56 -0
- data/doc/testing.rdoc +10 -4
- data/lib/sequel/adapters/fdbsql.rb +285 -0
- data/lib/sequel/adapters/informix.rb +15 -0
- data/lib/sequel/adapters/jdbc/fdbsql.rb +65 -0
- data/lib/sequel/adapters/mock.rb +1 -0
- data/lib/sequel/adapters/shared/fdbsql.rb +550 -0
- data/lib/sequel/adapters/shared/postgres.rb +23 -10
- data/lib/sequel/database/connecting.rb +1 -1
- data/lib/sequel/database/schema_methods.rb +10 -3
- data/lib/sequel/dataset/placeholder_literalizer.rb +7 -0
- data/lib/sequel/extensions/date_arithmetic.rb +5 -0
- data/lib/sequel/extensions/migration.rb +2 -2
- data/lib/sequel/extensions/pg_array.rb +15 -1
- data/lib/sequel/extensions/pg_json.rb +3 -0
- data/lib/sequel/extensions/pg_json_ops.rb +4 -4
- data/lib/sequel/extensions/schema_dumper.rb +9 -1
- data/lib/sequel/model/associations.rb +70 -21
- data/lib/sequel/plugins/active_model.rb +7 -2
- data/lib/sequel/plugins/many_through_many.rb +1 -0
- data/lib/sequel/plugins/pg_array_associations.rb +2 -1
- data/lib/sequel/plugins/split_values.rb +64 -0
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/fdbsql_spec.rb +429 -0
- data/spec/adapters/informix_spec.rb +6 -0
- data/spec/adapters/postgres_spec.rb +49 -1
- data/spec/adapters/spec_helper.rb +6 -1
- data/spec/adapters/sqlite_spec.rb +1 -1
- data/spec/core/placeholder_literalizer_spec.rb +10 -0
- data/spec/extensions/date_arithmetic_spec.rb +7 -0
- data/spec/extensions/many_through_many_spec.rb +14 -0
- data/spec/extensions/migration_spec.rb +3 -3
- data/spec/extensions/pg_array_associations_spec.rb +9 -0
- data/spec/extensions/pg_json_ops_spec.rb +4 -8
- data/spec/extensions/schema_dumper_spec.rb +9 -0
- data/spec/extensions/spec_helper.rb +3 -0
- data/spec/extensions/split_values_spec.rb +22 -0
- data/spec/integration/database_test.rb +1 -1
- data/spec/integration/dataset_test.rb +1 -1
- data/spec/integration/eager_loader_test.rb +1 -1
- data/spec/integration/plugin_test.rb +3 -2
- data/spec/integration/prepared_statement_test.rb +3 -3
- data/spec/integration/schema_test.rb +3 -3
- data/spec/integration/spec_helper.rb +6 -1
- data/spec/integration/timezone_test.rb +1 -1
- data/spec/model/association_reflection_spec.rb +29 -0
- data/spec/model/associations_spec.rb +36 -0
- data/spec/model/eager_loading_spec.rb +14 -0
- data/spec/model/spec_helper.rb +3 -0
- data/spec/rspec_helper.rb +4 -0
- metadata +10 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bed2efa61dbcb53c3a0ceb08e9ca858368631d35
|
4
|
+
data.tar.gz: 11da3e14b94c21e9719cd0d8a700e7f0727bc33b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a132e45671dda78cb933d50f21942ace99b938c3133d54b8dd82babe8fb5657f8c4f63ebb28dc1cd4c13bc08ee5a9736514a3be5ceb2c3dbcc4c7eeb09a5177f
|
7
|
+
data.tar.gz: a1e4be8380d22afda924e1fec932c47fb5be3f258279010753c12fc5dca9220f38b18b657e84a03875f9108572614292c150d8963ee8b0249eceff06df627d1b
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,35 @@
|
|
1
|
+
=== 4.15.0 (2014-10-01)
|
2
|
+
|
3
|
+
* Make AssociationReflection#reciprocal not raise error if associated class contains association with invalid associated class (jeremyevans)
|
4
|
+
|
5
|
+
* Make create_view(:view_name, dataset, :materialized=>true) reversible on PostgreSQL (jeremyevans)
|
6
|
+
|
7
|
+
* Add support for creating foreign tables on PostgreSQL using :foreign and :options create_table options (jeremyevans)
|
8
|
+
|
9
|
+
* Raise Error if a primary key is necessary to use an association, but the model doesn't have a primary key (jeremyevans)
|
10
|
+
|
11
|
+
* Make tactical_eager_loading plugin work for limited associations (jeremyevans)
|
12
|
+
|
13
|
+
* Add PlaceholderLiteralizer#with_dataset, for returning a new literalizer using a modified dataset (jeremyevans)
|
14
|
+
|
15
|
+
* Support active_model 4.2.0beta1 in the active_model plugin (jeremyevans)
|
16
|
+
|
17
|
+
* Make Dataset#insert in the informix adapter return last inserted id (jihwans) (#887)
|
18
|
+
|
19
|
+
* Support :nolog option in the informix adapter to disable transactions (jihwans) (#887)
|
20
|
+
|
21
|
+
* Remove optional argument for Postgres::{JSON,JSONB}Op#to_record and #to_recordset (jeremyevans)
|
22
|
+
|
23
|
+
* Add support for FoundationDB SQL Layer, via fdbsql and jdbc/fdbsql adapters (ScottDugas, jeremyevans) (#884)
|
24
|
+
|
25
|
+
* Work around bug in old versions of MySQL when schema dumping a table with multiple timestamp columns (jeremyevans) (#882)
|
26
|
+
|
27
|
+
* Support more array types by default in the pg_array extension, such as xml[] and uuid[] (jeremyevans)
|
28
|
+
|
29
|
+
* Add Sequel::Model.cache_associations accessor, which can be set to false to not cache association metadata (jeremyevans)
|
30
|
+
|
31
|
+
* Add split_values plugin, for moving noncolumn entries from the values hash into a separate hash (jeremyevans) (#868)
|
32
|
+
|
1
33
|
=== 4.14.0 (2014-09-01)
|
2
34
|
|
3
35
|
* Raise original exception if there is an exception raised when rolling back transaction/savepoint (jeremyevans) (#875)
|
data/README.rdoc
CHANGED
@@ -12,9 +12,9 @@ toolkit for Ruby.
|
|
12
12
|
two-phase commit, transaction isolation, master/slave
|
13
13
|
configurations, and database sharding.
|
14
14
|
* Sequel currently has adapters for ADO, Amalgalite, CUBRID,
|
15
|
-
DataObjects, DB2, DBI, Firebird,
|
16
|
-
|
17
|
-
Swift, and TinyTDS.
|
15
|
+
DataObjects, DB2, DBI, Firebird, FoundationDB SQL Layer,
|
16
|
+
IBM_DB, Informix, JDBC, MySQL, Mysql2, ODBC, OpenBase, Oracle,
|
17
|
+
PostgreSQL, SQLAnywhere, SQLite3, Swift, and TinyTDS.
|
18
18
|
|
19
19
|
== Resources
|
20
20
|
|
data/Rakefile
CHANGED
@@ -144,7 +144,7 @@ begin
|
|
144
144
|
spec_with_cov.call("spec_plugin", Dir["spec/extensions/*_spec.rb"].sort_by{rand}, "Run extension/plugin specs"){|t| t.rcov_opts.concat(%w'--exclude "lib/sequel/([a-z_]+\.rb|adapters|connection_pool|database|dataset|model)"')}
|
145
145
|
spec_with_cov.call("spec_integration", Dir["spec/integration/*_test.rb"], "Run integration tests")
|
146
146
|
|
147
|
-
%w'postgres sqlite mysql informix oracle firebird mssql db2 sqlanywhere'.each do |adapter|
|
147
|
+
%w'postgres sqlite mysql informix oracle fdbsql firebird mssql db2 sqlanywhere'.each do |adapter|
|
148
148
|
spec_with_cov.call("spec_#{adapter}", ["spec/adapters/#{adapter}_spec.rb"] + Dir["spec/integration/*_test.rb"], "Run #{adapter} specs"){|t| t.rcov_opts.concat(%w'--exclude "lib/sequel/([a-z_]+\.rb|connection_pool|database|dataset|model|extensions|plugins)"')}
|
149
149
|
end
|
150
150
|
|
data/doc/opening_databases.rdoc
CHANGED
@@ -206,6 +206,18 @@ Connection string examples:
|
|
206
206
|
do:postgres://user:password@host/database
|
207
207
|
do:mysql://user:password@host/database
|
208
208
|
|
209
|
+
=== fdbsql
|
210
|
+
|
211
|
+
Requires: pg
|
212
|
+
|
213
|
+
The following additional options are supported:
|
214
|
+
|
215
|
+
:connect_timeout :: Set the number of seconds to wait for a connection (default 20).
|
216
|
+
:notice_receiver :: A proc that be called with the PGresult objects that have notice or warning messages.
|
217
|
+
The default notice receiver just prints the messages to stderr, but this can be used
|
218
|
+
to handle notice/warning messages differently.
|
219
|
+
:sslmode :: Set to 'disable', 'allow', 'prefer', 'require' to choose how to treat SSL.
|
220
|
+
|
209
221
|
=== firebird
|
210
222
|
|
211
223
|
Requires: fb (using code at http://github.com/wishdev/fb)
|
@@ -231,6 +243,10 @@ or set
|
|
231
243
|
export DELIMIDENT=y
|
232
244
|
|
233
245
|
in the scripts environment.
|
246
|
+
|
247
|
+
The following additional options are supported:
|
248
|
+
|
249
|
+
:nolog :: Disable transactions for the database.
|
234
250
|
|
235
251
|
=== jdbc
|
236
252
|
|
@@ -239,8 +255,9 @@ Requires: java
|
|
239
255
|
Houses Sequel's JDBC support when running on JRuby.
|
240
256
|
Support for individual database types is done using sub adapters.
|
241
257
|
There are currently subadapters for PostgreSQL, MySQL, SQLite, H2, HSQLDB, Derby,
|
242
|
-
Oracle, MSSQL, JTDS, AS400, Progress, Firebird, Informix, and DB2.
|
243
|
-
|
258
|
+
Oracle, MSSQL, JTDS, AS400, Progress, Fdbsql, Firebird, Informix, and DB2.
|
259
|
+
For PostgreSQL, MySQL, SQLite, H2, HSQLDB, Derby, and JTDS,
|
260
|
+
this can use the jdbc-* gem, for the others you need to have the .jar in your CLASSPATH
|
244
261
|
or load the Java class manually before calling Sequel.connect.
|
245
262
|
|
246
263
|
You just use the JDBC connection string directly, which can be specified
|
@@ -274,6 +291,7 @@ Example connection strings:
|
|
274
291
|
jdbc:jdbcprogress:T:hostname:port:database
|
275
292
|
jdbc:cubrid:hostname:port:database:::
|
276
293
|
jdbc:sqlanywhere://localhost?DBN=Test;UID=user;PWD=password
|
294
|
+
jdbc:fdbsql://localhost:15432/user?user=user&password=password
|
277
295
|
|
278
296
|
You can also use JNDI connection strings:
|
279
297
|
|
@@ -0,0 +1,56 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* fdbsql and jdbc/fdbsql adapters have been added, for connecting to
|
4
|
+
FoundationDB SQL Layer.
|
5
|
+
|
6
|
+
* A split_values plugin has been added, for moving non-column entries
|
7
|
+
from the values hash into a separate hash. This allows you to
|
8
|
+
select additional columns (e.g. computed columns) when retrieving
|
9
|
+
model instances, and be able to save those instances without
|
10
|
+
removing the additional columns.
|
11
|
+
|
12
|
+
* A Sequel::Model.cache_associations accessor has been added, which
|
13
|
+
can be set to false to not cache any association metadata. This
|
14
|
+
can fix issues in a development environment that uses code
|
15
|
+
reloading.
|
16
|
+
|
17
|
+
* The active_model plugin now supports activemodel 4.2.0beta1.
|
18
|
+
|
19
|
+
* More PostgreSQL array types are handled automatically by the
|
20
|
+
pg_array extension, such as xml[] and uuid[].
|
21
|
+
|
22
|
+
* Creating foreign tables is now supported on PostgreSQL via the
|
23
|
+
:foreign and :options create_table options.
|
24
|
+
|
25
|
+
* The :nolog Database option is now supported in the informix
|
26
|
+
adapter, where it disables the use of transactions.
|
27
|
+
|
28
|
+
* PlaceholderLiteralizer#with_dataset has been added, allowing you
|
29
|
+
to create another PlaceholderLiteralizer with a modified dataset,
|
30
|
+
useful if you want to change the row_proc or any non-SQL dataset
|
31
|
+
options.
|
32
|
+
|
33
|
+
= Other Improvements
|
34
|
+
|
35
|
+
* The tactical_eager_loading plugin once again works correctly with
|
36
|
+
limited associations.
|
37
|
+
|
38
|
+
* A bug in older versions of MySQL is now worked around when schema
|
39
|
+
dumping a table with multiple timestamp columns.
|
40
|
+
|
41
|
+
* On PostgreSQL, create_view(:view_name, dataset, :materialized=>true)
|
42
|
+
is now reversible.
|
43
|
+
|
44
|
+
* Postgres::{JSON,JSONB}Op#to_record and #to_recordset no longer take
|
45
|
+
an optional argument. This was supported in PostgreSQL 9.4beta1,
|
46
|
+
but removed before PostgreSQL 9.4beta2.
|
47
|
+
|
48
|
+
* Dataset#insert now returns the last inserted id in the informix
|
49
|
+
adapter.
|
50
|
+
|
51
|
+
* Sequel no longer raises an exception in
|
52
|
+
AssociationReflection#reciprocal if the associated class has an
|
53
|
+
association that does not have a valid associated class.
|
54
|
+
|
55
|
+
* Sequel now raises an exception if a primary key is necessary to use
|
56
|
+
an association, but the model does not have a primary key.
|
data/doc/testing.rdoc
CHANGED
@@ -128,9 +128,13 @@ The order in which you delete/truncate the tables is important if you are using
|
|
128
128
|
|
129
129
|
Sequel has multiple separate test suites. All test suites run under rspec >=1.3.
|
130
130
|
|
131
|
+
== rake
|
132
|
+
|
133
|
+
The default rake task runs Sequel's core, model, plugin, and extension specs, the same as <tt>rake spec spec_plugin</tt>.
|
134
|
+
|
131
135
|
== rake spec
|
132
136
|
|
133
|
-
The +spec+ rake task
|
137
|
+
The +spec+ rake task runs Sequel's core and model specs. These specs use a mocked database connection, and test for specific SQL used and for generally correct behavior.
|
134
138
|
|
135
139
|
== rake spec_plugin
|
136
140
|
|
@@ -152,7 +156,7 @@ These specs are broken down into two parts. For each database, there are specif
|
|
152
156
|
|
153
157
|
== Environment variables
|
154
158
|
|
155
|
-
Sequel
|
159
|
+
Sequel uses environment variables when testing to specify either the database to be tested or specify how testing should be done. You can also specify the databases to test by copying spec/spec_config.rb.example to spec/spec_config.rb and modifying it. See that file for details. It may be necessary to use spec_config.rb as opposed to an environment variable if your database connection cannot be specified by a connection string.
|
156
160
|
|
157
161
|
Sequel does not create test databases automatically, except for file-based databases such as SQLite/H2/HSQLDB/Derby. It's up to the user to create the test databases manually and give Sequel a valid connection string in an environment variable (or setup the connection object in spec_config.rb).
|
158
162
|
|
@@ -162,9 +166,11 @@ The SEQUEL_INTEGRATION_URL environment variable specifies the Database connectio
|
|
162
166
|
|
163
167
|
=== Other
|
164
168
|
|
165
|
-
SEQUEL_COLUMNS_INTROSPECTION ::
|
169
|
+
SEQUEL_COLUMNS_INTROSPECTION :: Use the columns_introspection extension when running the specs
|
166
170
|
SEQUEL_CONNECTION_VALIDATOR :: Use the connection validator extension when running the specs
|
167
171
|
SEQUEL_ERROR_SQL :: Use the error_sql extension when running the specs
|
172
|
+
SEQUEL_NO_CACHE_ASSOCIATIONS :: Don't cache association metadata when running the specs
|
168
173
|
SEQUEL_NO_CHECK_SQLS :: Don't check for specific SQL syntax when running the specs
|
169
|
-
SEQUEL_NO_PENDING :: Don't mark any specs as pending, try running all specs
|
174
|
+
SEQUEL_NO_PENDING :: Don't mark any specs as pending, try running all specs (note, can cause lockups for some adapters)
|
175
|
+
SEQUEL_NO_SKIP_PENDING :: On RSpec 3, run specs marked as pending, and fail the pending spec if it passes (note, can cause lockups for some adapters)
|
170
176
|
SKIPPED_TEST_WARN :: Warn when skipping any tests because libraries aren't available
|
@@ -0,0 +1,285 @@
|
|
1
|
+
require 'pg'
|
2
|
+
|
3
|
+
Sequel.require 'adapters/utils/pg_types'
|
4
|
+
Sequel.require 'adapters/shared/fdbsql'
|
5
|
+
|
6
|
+
module Sequel
|
7
|
+
module Fdbsql
|
8
|
+
CONVERTED_EXCEPTIONS << PGError
|
9
|
+
|
10
|
+
# Database class for the FoundationDB SQL Layer used with Sequel and the
|
11
|
+
# pg driver
|
12
|
+
class Database < Sequel::Database
|
13
|
+
include Sequel::Fdbsql::DatabaseMethods
|
14
|
+
|
15
|
+
set_adapter_scheme :fdbsql
|
16
|
+
|
17
|
+
# Connects to the database. In addition to the standard database options,
|
18
|
+
# :connect_timeout is a connection timeout in seconds,
|
19
|
+
# :sslmode sets whether to use ssl, and
|
20
|
+
# :notice_receiver handles server notices in a proc.
|
21
|
+
def connect(server)
|
22
|
+
opts = server_opts(server)
|
23
|
+
Connection.new(self, opts)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Execute the given SQL with the given args on an available connection.
|
27
|
+
def execute(sql, opts = {}, &block)
|
28
|
+
res = nil
|
29
|
+
synchronize(opts[:server]) do |conn|
|
30
|
+
res = check_database_errors do
|
31
|
+
if sql.is_a?(Symbol)
|
32
|
+
execute_prepared_statement(conn, sql, opts, &block)
|
33
|
+
else
|
34
|
+
log_yield(sql) do
|
35
|
+
conn.query(sql, opts[:arguments])
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
yield res if block_given?
|
40
|
+
res.cmd_tuples
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def server_version
|
45
|
+
return @server_version if @server_version
|
46
|
+
|
47
|
+
version = get{VERSION{}}
|
48
|
+
unless ver = version.match(/(\d+)\.(\d+)\.(\d+)/)
|
49
|
+
raise Error, "No match when checking FDB SQL Layer version: #{version}"
|
50
|
+
end
|
51
|
+
|
52
|
+
@server_version = (100 * ver[1].to_i + ver[2].to_i) * 100 + ver[3].to_i
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def database_exception_sqlstate(exception, opts)
|
58
|
+
if exception.respond_to?(:result) && (result = exception.result)
|
59
|
+
result.error_field(::PGresult::PG_DIAG_SQLSTATE)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def execute_prepared_statement(conn, name, opts=OPTS, &block)
|
64
|
+
statement = prepared_statement(name)
|
65
|
+
sql = statement.prepared_sql
|
66
|
+
ps_name = name.to_s
|
67
|
+
if args = opts[:arguments]
|
68
|
+
args = args.map{|arg| bound_variable_arg(arg, conn)}
|
69
|
+
end
|
70
|
+
begin
|
71
|
+
# create prepared statement if it doesn't exist, or has new sql
|
72
|
+
unless conn.prepared_statements[ps_name] == sql
|
73
|
+
conn.execute("DEALLOCATE #{ps_name}") if conn.prepared_statements.include?(ps_name)
|
74
|
+
log_yield("PREPARE #{ps_name} AS #{sql}"){conn.prepare(ps_name, sql)}
|
75
|
+
conn.prepared_statements[ps_name] = sql
|
76
|
+
end
|
77
|
+
|
78
|
+
log_sql = "EXECUTE #{ps_name}"
|
79
|
+
if statement.log_sql
|
80
|
+
log_sql << " ("
|
81
|
+
log_sql << sql
|
82
|
+
log_sql << ")"
|
83
|
+
end
|
84
|
+
log_yield(sql, args) do
|
85
|
+
conn.execute_prepared_statement(ps_name, args)
|
86
|
+
end
|
87
|
+
rescue PGError => e
|
88
|
+
if (database_exception_sqlstate(e, opts) == STALE_STATEMENT_SQLSTATE)
|
89
|
+
conn.prepared_statements[ps_name] = nil
|
90
|
+
retry
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Dataset class for the FoundationDB SQL Layer that uses the pg driver.
|
97
|
+
class Dataset < Sequel::Dataset
|
98
|
+
include Sequel::Fdbsql::DatasetMethods
|
99
|
+
|
100
|
+
Database::DatasetClass = self
|
101
|
+
|
102
|
+
# Allow use of bind arguments for FDBSQL using the pg driver.
|
103
|
+
module BindArgumentMethods
|
104
|
+
|
105
|
+
include Sequel::Dataset::UnnumberedArgumentMapper
|
106
|
+
include DatasetMethods::PreparedStatementMethods
|
107
|
+
|
108
|
+
private
|
109
|
+
|
110
|
+
# Execute the given SQL with the stored bind arguments.
|
111
|
+
def execute(sql, opts=OPTS, &block)
|
112
|
+
super(sql, {:arguments=>bind_arguments}.merge(opts), &block)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Same as execute, explicit due to intricacies of alias and super.
|
116
|
+
def execute_dui(sql, opts=OPTS, &block)
|
117
|
+
super(sql, {:arguments=>bind_arguments}.merge(opts), &block)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# Allow use of server side prepared statements for FDBSQL using the
|
122
|
+
# pg driver.
|
123
|
+
module PreparedStatementMethods
|
124
|
+
include BindArgumentMethods
|
125
|
+
|
126
|
+
# Raise a more obvious error if you attempt to call a unnamed prepared statement.
|
127
|
+
def call(*)
|
128
|
+
raise Error, "Cannot call prepared statement without a name" if prepared_statement_name.nil?
|
129
|
+
super
|
130
|
+
end
|
131
|
+
|
132
|
+
private
|
133
|
+
|
134
|
+
# Execute the stored prepared statement name and the stored bind
|
135
|
+
# arguments instead of the SQL given.
|
136
|
+
def execute(sql, opts=OPTS, &block)
|
137
|
+
super(prepared_statement_name, opts, &block)
|
138
|
+
end
|
139
|
+
|
140
|
+
# Same as execute, explicit due to intricacies of alias and super.
|
141
|
+
def execute_dui(sql, opts=OPTS, &block)
|
142
|
+
super(prepared_statement_name, opts, &block)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
|
147
|
+
# Execute the given type of statement with the hash of values.
|
148
|
+
def call(type, bind_vars=OPTS, *values, &block)
|
149
|
+
ps = to_prepared_statement(type, values)
|
150
|
+
ps.extend(BindArgumentMethods)
|
151
|
+
ps.call(bind_vars, &block)
|
152
|
+
end
|
153
|
+
|
154
|
+
# Yield all rows returned by executing the given SQL and converting
|
155
|
+
# the types.
|
156
|
+
def fetch_rows(sql)
|
157
|
+
execute(sql) do |res|
|
158
|
+
columns = set_columns(res)
|
159
|
+
yield_hash_rows(res, columns) {|h| yield h}
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
# Prepare the given type of statement with the given name, and store
|
164
|
+
# it in the database to be called later.
|
165
|
+
def prepare(type, name=nil, *values)
|
166
|
+
ps = to_prepared_statement(type, values)
|
167
|
+
ps.extend(PreparedStatementMethods)
|
168
|
+
if name
|
169
|
+
ps.prepared_statement_name = name
|
170
|
+
db.set_prepared_statement(name, ps)
|
171
|
+
end
|
172
|
+
ps
|
173
|
+
end
|
174
|
+
|
175
|
+
private
|
176
|
+
|
177
|
+
def set_columns(res)
|
178
|
+
cols = []
|
179
|
+
procs = db.conversion_procs
|
180
|
+
res.nfields.times do |fieldnum|
|
181
|
+
cols << [fieldnum, procs[res.ftype(fieldnum)], output_identifier(res.fname(fieldnum))]
|
182
|
+
end
|
183
|
+
@columns = cols.map{|c| c[2]}
|
184
|
+
cols
|
185
|
+
end
|
186
|
+
|
187
|
+
# For each row in the result set, yield a hash with column name symbol
|
188
|
+
# keys and typecasted values.
|
189
|
+
def yield_hash_rows(res, cols)
|
190
|
+
res.ntuples.times do |recnum|
|
191
|
+
converted_rec = {}
|
192
|
+
cols.each do |fieldnum, type_proc, fieldsym|
|
193
|
+
value = res.getvalue(recnum, fieldnum)
|
194
|
+
converted_rec[fieldsym] = (value && type_proc) ? type_proc.call(value) : value
|
195
|
+
end
|
196
|
+
yield converted_rec
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
end
|
201
|
+
|
202
|
+
# Connection specific methods for Fdbsql with pg
|
203
|
+
class Connection < PG::Connection
|
204
|
+
# Regular expression for error messages that note that the connection is closed.
|
205
|
+
DISCONNECT_ERROR_RE = /\A(?:could not receive data from server|no connection to the server|connection not open|connection is closed)/
|
206
|
+
|
207
|
+
# Hash of prepared statements for this connection. Keys are
|
208
|
+
# string names of the server side prepared statement, and values
|
209
|
+
# are SQL strings.
|
210
|
+
attr_accessor :prepared_statements
|
211
|
+
|
212
|
+
# Create a new connection to the FoundationDB SQL Layer. See Database#connect.
|
213
|
+
def initialize(db, opts)
|
214
|
+
connect_opts = {
|
215
|
+
:host => opts[:host] || 'localhost',
|
216
|
+
:port => opts[:port] || 15432,
|
217
|
+
:dbname => opts[:database],
|
218
|
+
:user => opts[:user],
|
219
|
+
:password => opts[:password],
|
220
|
+
:hostaddr => opts[:hostaddr],
|
221
|
+
:connect_timeout => opts[:connect_timeout] || 20,
|
222
|
+
:sslmode => opts[:sslmode]
|
223
|
+
}.delete_if{|key, value| value.nil? or (value.respond_to?(:empty?) and value.empty?)}
|
224
|
+
super(connect_opts)
|
225
|
+
|
226
|
+
@db = db
|
227
|
+
@prepared_statements = {}
|
228
|
+
|
229
|
+
if opts[:notice_receiver]
|
230
|
+
set_notice_receiver(opts[:notice_receiver])
|
231
|
+
else
|
232
|
+
# Swallow warnings
|
233
|
+
set_notice_receiver{|proc| }
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
# Close the connection.
|
238
|
+
def close
|
239
|
+
super
|
240
|
+
rescue PGError, IOError
|
241
|
+
end
|
242
|
+
|
243
|
+
# Execute the given SQL with this connection. If a block is given,
|
244
|
+
# yield the results, otherwise, return the number of changed rows.
|
245
|
+
def execute(sql, args=nil)
|
246
|
+
q = query(sql, args)
|
247
|
+
block_given? ? yield(q) : q.cmd_tuples
|
248
|
+
end
|
249
|
+
|
250
|
+
# Execute the prepared statement of the given name, binding the given
|
251
|
+
# args.
|
252
|
+
def execute_prepared_statement(name, args)
|
253
|
+
check_disconnect_errors{exec_prepared(name, args)}
|
254
|
+
end
|
255
|
+
|
256
|
+
# Prepare a statement for later use.
|
257
|
+
def prepare(name, sql)
|
258
|
+
check_disconnect_errors{super}
|
259
|
+
end
|
260
|
+
|
261
|
+
# Execute the given query and return the results.
|
262
|
+
def query(sql, args=nil)
|
263
|
+
args = args.map{|v| @db.bound_variable_arg(v, self)} if args
|
264
|
+
check_disconnect_errors{super}
|
265
|
+
end
|
266
|
+
|
267
|
+
private
|
268
|
+
|
269
|
+
# Raise a Sequel::DatabaseDisconnectError if a PGError is raised and
|
270
|
+
# the connection status cannot be determined or it is not OK.
|
271
|
+
def check_disconnect_errors
|
272
|
+
begin
|
273
|
+
yield
|
274
|
+
rescue PGError => e
|
275
|
+
disconnect = false
|
276
|
+
disconnect ||= e.message =~ DISCONNECT_ERROR_RE
|
277
|
+
disconnect ? raise(Sequel.convert_exception_class(e, Sequel::DatabaseDisconnectError)) : raise
|
278
|
+
rescue IOError, Errno::EPIPE, Errno::ECONNRESET => e
|
279
|
+
disconnect = true
|
280
|
+
raise(Sequel.convert_exception_class(e, Sequel::DatabaseDisconnectError))
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|