sequel 4.14.0 → 4.15.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +32 -0
  3. data/README.rdoc +3 -3
  4. data/Rakefile +1 -1
  5. data/doc/opening_databases.rdoc +20 -2
  6. data/doc/release_notes/4.15.0.txt +56 -0
  7. data/doc/testing.rdoc +10 -4
  8. data/lib/sequel/adapters/fdbsql.rb +285 -0
  9. data/lib/sequel/adapters/informix.rb +15 -0
  10. data/lib/sequel/adapters/jdbc/fdbsql.rb +65 -0
  11. data/lib/sequel/adapters/mock.rb +1 -0
  12. data/lib/sequel/adapters/shared/fdbsql.rb +550 -0
  13. data/lib/sequel/adapters/shared/postgres.rb +23 -10
  14. data/lib/sequel/database/connecting.rb +1 -1
  15. data/lib/sequel/database/schema_methods.rb +10 -3
  16. data/lib/sequel/dataset/placeholder_literalizer.rb +7 -0
  17. data/lib/sequel/extensions/date_arithmetic.rb +5 -0
  18. data/lib/sequel/extensions/migration.rb +2 -2
  19. data/lib/sequel/extensions/pg_array.rb +15 -1
  20. data/lib/sequel/extensions/pg_json.rb +3 -0
  21. data/lib/sequel/extensions/pg_json_ops.rb +4 -4
  22. data/lib/sequel/extensions/schema_dumper.rb +9 -1
  23. data/lib/sequel/model/associations.rb +70 -21
  24. data/lib/sequel/plugins/active_model.rb +7 -2
  25. data/lib/sequel/plugins/many_through_many.rb +1 -0
  26. data/lib/sequel/plugins/pg_array_associations.rb +2 -1
  27. data/lib/sequel/plugins/split_values.rb +64 -0
  28. data/lib/sequel/version.rb +1 -1
  29. data/spec/adapters/fdbsql_spec.rb +429 -0
  30. data/spec/adapters/informix_spec.rb +6 -0
  31. data/spec/adapters/postgres_spec.rb +49 -1
  32. data/spec/adapters/spec_helper.rb +6 -1
  33. data/spec/adapters/sqlite_spec.rb +1 -1
  34. data/spec/core/placeholder_literalizer_spec.rb +10 -0
  35. data/spec/extensions/date_arithmetic_spec.rb +7 -0
  36. data/spec/extensions/many_through_many_spec.rb +14 -0
  37. data/spec/extensions/migration_spec.rb +3 -3
  38. data/spec/extensions/pg_array_associations_spec.rb +9 -0
  39. data/spec/extensions/pg_json_ops_spec.rb +4 -8
  40. data/spec/extensions/schema_dumper_spec.rb +9 -0
  41. data/spec/extensions/spec_helper.rb +3 -0
  42. data/spec/extensions/split_values_spec.rb +22 -0
  43. data/spec/integration/database_test.rb +1 -1
  44. data/spec/integration/dataset_test.rb +1 -1
  45. data/spec/integration/eager_loader_test.rb +1 -1
  46. data/spec/integration/plugin_test.rb +3 -2
  47. data/spec/integration/prepared_statement_test.rb +3 -3
  48. data/spec/integration/schema_test.rb +3 -3
  49. data/spec/integration/spec_helper.rb +6 -1
  50. data/spec/integration/timezone_test.rb +1 -1
  51. data/spec/model/association_reflection_spec.rb +29 -0
  52. data/spec/model/associations_spec.rb +36 -0
  53. data/spec/model/eager_loading_spec.rb +14 -0
  54. data/spec/model/spec_helper.rb +3 -0
  55. data/spec/rspec_helper.rb +4 -0
  56. metadata +10 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9b817d0bad633a1df7f0a331264d8ce67e0a2c40
4
- data.tar.gz: e46c816ef87e537d58a982e876011f14ab42c6a3
3
+ metadata.gz: bed2efa61dbcb53c3a0ceb08e9ca858368631d35
4
+ data.tar.gz: 11da3e14b94c21e9719cd0d8a700e7f0727bc33b
5
5
  SHA512:
6
- metadata.gz: d88876ee4814e172d6ed262b14a87f253e5864d03f8f3167420e10d6d3a873625a1fb797b406bc2438a28d7df846535f9af38f2dc90cc57c6167d6c333723345
7
- data.tar.gz: c0a61d9260147f6fa4d458242b7ebb6139eacee710e84eb1361d9527a15dea1ecbad377893b3a0dede352af92061308c4e5fd76d4741df60cdc1cf0f3819ddc3
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)
@@ -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, IBM_DB, Informix, JDBC, MySQL,
16
- Mysql2, ODBC, OpenBase, Oracle, PostgreSQL, SQLAnywhere, SQLite3,
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
 
@@ -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. For PostgreSQL, MySQL, SQLite, H2, HSQLDB, Derby,
243
- and JTDS, this can use the jdbc-* gem, for the others you need to have the .jar in your CLASSPATH
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.
@@ -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 (which is also the default 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.
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 often 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.
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 :: Whether to run the specs with the columns_introspection extension loaded by default
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