sequel 3.21.0 → 3.32.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 +539 -3
- data/MIT-LICENSE +1 -1
- data/README.rdoc +20 -6
- data/Rakefile +37 -19
- data/bin/sequel +1 -5
- data/doc/advanced_associations.rdoc +17 -0
- data/doc/association_basics.rdoc +293 -43
- data/doc/dataset_basics.rdoc +4 -4
- data/doc/dataset_filtering.rdoc +8 -0
- data/doc/mass_assignment.rdoc +54 -0
- data/doc/migration.rdoc +15 -538
- data/doc/model_hooks.rdoc +72 -27
- data/doc/opening_databases.rdoc +89 -50
- data/doc/prepared_statements.rdoc +40 -13
- data/doc/reflection.rdoc +8 -2
- data/doc/release_notes/3.22.0.txt +39 -0
- data/doc/release_notes/3.23.0.txt +172 -0
- data/doc/release_notes/3.24.0.txt +420 -0
- data/doc/release_notes/3.25.0.txt +88 -0
- data/doc/release_notes/3.26.0.txt +88 -0
- data/doc/release_notes/3.27.0.txt +82 -0
- data/doc/release_notes/3.28.0.txt +304 -0
- data/doc/release_notes/3.29.0.txt +459 -0
- data/doc/release_notes/3.30.0.txt +135 -0
- data/doc/release_notes/3.31.0.txt +146 -0
- data/doc/release_notes/3.32.0.txt +202 -0
- data/doc/schema_modification.rdoc +547 -0
- data/doc/sharding.rdoc +7 -1
- data/doc/testing.rdoc +129 -0
- data/doc/transactions.rdoc +137 -0
- data/doc/virtual_rows.rdoc +2 -2
- data/lib/sequel/adapters/ado/mssql.rb +18 -6
- data/lib/sequel/adapters/ado.rb +40 -18
- data/lib/sequel/adapters/amalgalite.rb +17 -9
- data/lib/sequel/adapters/db2.rb +175 -86
- data/lib/sequel/adapters/dbi.rb +15 -15
- data/lib/sequel/adapters/do/mysql.rb +5 -7
- data/lib/sequel/adapters/do/postgres.rb +0 -5
- data/lib/sequel/adapters/do/sqlite.rb +0 -5
- data/lib/sequel/adapters/do.rb +17 -36
- data/lib/sequel/adapters/firebird.rb +27 -208
- data/lib/sequel/adapters/ibmdb.rb +448 -0
- data/lib/sequel/adapters/informix.rb +6 -23
- data/lib/sequel/adapters/jdbc/as400.rb +16 -34
- data/lib/sequel/adapters/jdbc/db2.rb +52 -0
- data/lib/sequel/adapters/jdbc/derby.rb +260 -0
- data/lib/sequel/adapters/jdbc/firebird.rb +29 -0
- data/lib/sequel/adapters/jdbc/h2.rb +66 -30
- data/lib/sequel/adapters/jdbc/hsqldb.rb +177 -0
- data/lib/sequel/adapters/jdbc/informix.rb +26 -0
- data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
- data/lib/sequel/adapters/jdbc/mysql.rb +11 -11
- data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
- data/lib/sequel/adapters/jdbc/postgresql.rb +9 -13
- data/lib/sequel/adapters/jdbc/sqlite.rb +2 -6
- data/lib/sequel/adapters/jdbc/sqlserver.rb +71 -0
- data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
- data/lib/sequel/adapters/jdbc.rb +231 -78
- data/lib/sequel/adapters/mock.rb +339 -0
- data/lib/sequel/adapters/mysql.rb +85 -184
- data/lib/sequel/adapters/mysql2.rb +26 -25
- data/lib/sequel/adapters/odbc/db2.rb +17 -0
- data/lib/sequel/adapters/odbc/mssql.rb +1 -6
- data/lib/sequel/adapters/odbc.rb +28 -9
- data/lib/sequel/adapters/openbase.rb +3 -5
- data/lib/sequel/adapters/oracle.rb +355 -53
- data/lib/sequel/adapters/postgres.rb +194 -33
- data/lib/sequel/adapters/shared/access.rb +32 -9
- data/lib/sequel/adapters/shared/db2.rb +328 -0
- data/lib/sequel/adapters/shared/firebird.rb +221 -0
- data/lib/sequel/adapters/shared/informix.rb +53 -0
- data/lib/sequel/adapters/shared/mssql.rb +278 -107
- data/lib/sequel/adapters/shared/mysql.rb +203 -65
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +149 -0
- data/lib/sequel/adapters/shared/oracle.rb +237 -46
- data/lib/sequel/adapters/shared/postgres.rb +214 -82
- data/lib/sequel/adapters/shared/progress.rb +5 -8
- data/lib/sequel/adapters/shared/sqlite.rb +200 -50
- data/lib/sequel/adapters/sqlite.rb +29 -21
- data/lib/sequel/adapters/swift/mysql.rb +12 -7
- data/lib/sequel/adapters/swift/postgres.rb +0 -5
- data/lib/sequel/adapters/swift/sqlite.rb +8 -6
- data/lib/sequel/adapters/swift.rb +5 -5
- data/lib/sequel/adapters/tinytds.rb +160 -19
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +66 -0
- data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
- data/lib/sequel/ast_transformer.rb +190 -0
- data/lib/sequel/connection_pool/threaded.rb +3 -2
- data/lib/sequel/connection_pool.rb +1 -1
- data/lib/sequel/core.rb +68 -5
- data/lib/sequel/database/connecting.rb +5 -6
- data/lib/sequel/database/dataset.rb +4 -4
- data/lib/sequel/database/dataset_defaults.rb +59 -1
- data/lib/sequel/database/logging.rb +1 -1
- data/lib/sequel/database/misc.rb +104 -19
- data/lib/sequel/database/query.rb +163 -59
- data/lib/sequel/database/schema_generator.rb +17 -7
- data/lib/sequel/database/schema_methods.rb +59 -29
- data/lib/sequel/dataset/actions.rb +240 -76
- data/lib/sequel/dataset/features.rb +91 -8
- data/lib/sequel/dataset/graph.rb +38 -11
- data/lib/sequel/dataset/misc.rb +50 -23
- data/lib/sequel/dataset/mutation.rb +12 -7
- data/lib/sequel/dataset/prepared_statements.rb +59 -27
- data/lib/sequel/dataset/query.rb +236 -43
- data/lib/sequel/dataset/sql.rb +676 -267
- data/lib/sequel/dataset.rb +8 -0
- data/lib/sequel/exceptions.rb +4 -0
- data/lib/sequel/extensions/columns_introspection.rb +61 -0
- data/lib/sequel/extensions/migration.rb +6 -4
- data/lib/sequel/extensions/named_timezones.rb +5 -0
- data/lib/sequel/extensions/query.rb +1 -1
- data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
- data/lib/sequel/extensions/to_dot.rb +95 -83
- data/lib/sequel/model/associations.rb +1081 -344
- data/lib/sequel/model/base.rb +420 -133
- data/lib/sequel/model/errors.rb +1 -1
- data/lib/sequel/model/exceptions.rb +5 -1
- data/lib/sequel/model.rb +20 -10
- data/lib/sequel/plugins/active_model.rb +13 -1
- data/lib/sequel/plugins/association_pks.rb +22 -4
- data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
- data/lib/sequel/plugins/composition.rb +43 -10
- data/lib/sequel/plugins/dataset_associations.rb +100 -0
- data/lib/sequel/plugins/defaults_setter.rb +58 -0
- data/lib/sequel/plugins/force_encoding.rb +6 -6
- data/lib/sequel/plugins/identity_map.rb +114 -7
- data/lib/sequel/plugins/many_through_many.rb +68 -7
- data/lib/sequel/plugins/nested_attributes.rb +65 -10
- data/lib/sequel/plugins/prepared_statements.rb +151 -0
- data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
- data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
- data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
- data/lib/sequel/plugins/rcte_tree.rb +29 -15
- data/lib/sequel/plugins/serialization.rb +98 -49
- data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
- data/lib/sequel/plugins/sharding.rb +21 -54
- data/lib/sequel/plugins/single_table_inheritance.rb +3 -1
- data/lib/sequel/plugins/typecast_on_load.rb +9 -12
- data/lib/sequel/plugins/update_primary_key.rb +2 -2
- data/lib/sequel/plugins/xml_serializer.rb +3 -3
- data/lib/sequel/sql.rb +109 -52
- data/lib/sequel/timezones.rb +57 -38
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/db2_spec.rb +146 -0
- data/spec/adapters/mssql_spec.rb +135 -30
- data/spec/adapters/mysql_spec.rb +244 -212
- data/spec/adapters/oracle_spec.rb +67 -77
- data/spec/adapters/postgres_spec.rb +267 -78
- data/spec/adapters/spec_helper.rb +6 -5
- data/spec/adapters/sqlite_spec.rb +35 -21
- data/spec/core/connection_pool_spec.rb +71 -92
- data/spec/core/core_sql_spec.rb +21 -30
- data/spec/core/database_spec.rb +789 -512
- data/spec/core/dataset_spec.rb +1176 -1061
- data/spec/core/expression_filters_spec.rb +159 -42
- data/spec/core/mock_adapter_spec.rb +419 -0
- data/spec/core/object_graph_spec.rb +86 -114
- data/spec/core/schema_generator_spec.rb +3 -3
- data/spec/core/schema_spec.rb +298 -38
- data/spec/core/spec_helper.rb +6 -48
- data/spec/extensions/active_model_spec.rb +13 -0
- data/spec/extensions/association_autoreloading_spec.rb +1 -10
- data/spec/extensions/association_dependencies_spec.rb +2 -12
- data/spec/extensions/association_pks_spec.rb +66 -32
- data/spec/extensions/caching_spec.rb +23 -50
- data/spec/extensions/class_table_inheritance_spec.rb +31 -83
- data/spec/extensions/columns_introspection_spec.rb +91 -0
- data/spec/extensions/composition_spec.rb +18 -13
- data/spec/extensions/dataset_associations_spec.rb +199 -0
- data/spec/extensions/defaults_setter_spec.rb +64 -0
- data/spec/extensions/hook_class_methods_spec.rb +65 -91
- data/spec/extensions/identity_map_spec.rb +114 -22
- data/spec/extensions/instance_filters_spec.rb +11 -21
- data/spec/extensions/instance_hooks_spec.rb +53 -0
- data/spec/extensions/json_serializer_spec.rb +4 -5
- data/spec/extensions/lazy_attributes_spec.rb +16 -20
- data/spec/extensions/list_spec.rb +17 -39
- data/spec/extensions/many_through_many_spec.rb +283 -250
- data/spec/extensions/migration_spec.rb +35 -32
- data/spec/extensions/named_timezones_spec.rb +23 -3
- data/spec/extensions/nested_attributes_spec.rb +97 -88
- data/spec/extensions/optimistic_locking_spec.rb +9 -20
- data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
- data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
- data/spec/extensions/prepared_statements_spec.rb +53 -0
- data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
- data/spec/extensions/pretty_table_spec.rb +1 -6
- data/spec/extensions/query_spec.rb +6 -0
- data/spec/extensions/rcte_tree_spec.rb +41 -43
- data/spec/extensions/schema_dumper_spec.rb +5 -8
- data/spec/extensions/schema_spec.rb +13 -21
- data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
- data/spec/extensions/serialization_spec.rb +81 -43
- data/spec/extensions/sharding_spec.rb +66 -140
- data/spec/extensions/single_table_inheritance_spec.rb +18 -29
- data/spec/extensions/spec_helper.rb +13 -61
- data/spec/extensions/sql_expr_spec.rb +20 -60
- data/spec/extensions/tactical_eager_loading_spec.rb +9 -19
- data/spec/extensions/thread_local_timezones_spec.rb +22 -2
- data/spec/extensions/timestamps_spec.rb +6 -6
- data/spec/extensions/to_dot_spec.rb +4 -7
- data/spec/extensions/touch_spec.rb +13 -14
- data/spec/extensions/tree_spec.rb +11 -26
- data/spec/extensions/typecast_on_load_spec.rb +1 -6
- data/spec/extensions/update_primary_key_spec.rb +30 -24
- data/spec/extensions/validation_class_methods_spec.rb +30 -51
- data/spec/extensions/validation_helpers_spec.rb +16 -35
- data/spec/extensions/xml_serializer_spec.rb +16 -4
- data/spec/integration/associations_test.rb +661 -21
- data/spec/integration/database_test.rb +4 -2
- data/spec/integration/dataset_test.rb +500 -67
- data/spec/integration/eager_loader_test.rb +19 -21
- data/spec/integration/model_test.rb +80 -1
- data/spec/integration/plugin_test.rb +367 -119
- data/spec/integration/prepared_statement_test.rb +222 -125
- data/spec/integration/schema_test.rb +217 -39
- data/spec/integration/spec_helper.rb +39 -30
- data/spec/integration/timezone_test.rb +38 -12
- data/spec/integration/transaction_test.rb +182 -5
- data/spec/integration/type_test.rb +17 -3
- data/spec/model/association_reflection_spec.rb +201 -7
- data/spec/model/associations_spec.rb +955 -662
- data/spec/model/base_spec.rb +275 -95
- data/spec/model/dataset_methods_spec.rb +7 -27
- data/spec/model/eager_loading_spec.rb +677 -676
- data/spec/model/hooks_spec.rb +349 -67
- data/spec/model/model_spec.rb +222 -168
- data/spec/model/plugins_spec.rb +24 -13
- data/spec/model/record_spec.rb +381 -218
- data/spec/model/spec_helper.rb +13 -71
- data/spec/model/validations_spec.rb +11 -0
- metadata +117 -73
|
@@ -93,13 +93,14 @@ module Sequel
|
|
|
93
93
|
def bytea(s) ::Sequel::SQL::Blob.new(Adapter.unescape_bytea(s)) end
|
|
94
94
|
def integer(s) s.to_i end
|
|
95
95
|
def float(s) s.to_f end
|
|
96
|
-
def
|
|
97
|
-
def date(s) ::Sequel.string_to_date(s) end
|
|
98
|
-
def date_iso(s) ::Date.new(*s.split("-").map{|x| x.to_i}) end
|
|
99
|
-
def time(s) ::Sequel.string_to_time(s) end
|
|
100
|
-
def timestamp(s) ::Sequel.database_to_application_timestamp(s) end
|
|
96
|
+
def date(s) ::Date.new(*s.split("-").map{|x| x.to_i}) end
|
|
101
97
|
end.new
|
|
102
98
|
|
|
99
|
+
# Hash with type name symbols and callable values for converting PostgreSQL types.
|
|
100
|
+
# Non-builtin types that don't have fixed numbers should use this to register
|
|
101
|
+
# conversion procs.
|
|
102
|
+
PG_NAMED_TYPES = {}
|
|
103
|
+
|
|
103
104
|
# Hash with integer keys and callable values for converting PostgreSQL types.
|
|
104
105
|
PG_TYPES = {}
|
|
105
106
|
{
|
|
@@ -107,9 +108,8 @@ module Sequel
|
|
|
107
108
|
[17] => tt.method(:bytea),
|
|
108
109
|
[20, 21, 22, 23, 26] => tt.method(:integer),
|
|
109
110
|
[700, 701] => tt.method(:float),
|
|
110
|
-
[790, 1700] =>
|
|
111
|
-
[1083, 1266] =>
|
|
112
|
-
[1114, 1184] => tt.method(:timestamp)
|
|
111
|
+
[790, 1700] => ::BigDecimal.method(:new),
|
|
112
|
+
[1083, 1266] => ::Sequel.method(:string_to_time),
|
|
113
113
|
}.each do |k,v|
|
|
114
114
|
k.each{|n| PG_TYPES[n] = v}
|
|
115
115
|
end
|
|
@@ -123,7 +123,7 @@ module Sequel
|
|
|
123
123
|
|
|
124
124
|
# Modify the type translator for the date type depending on the value given.
|
|
125
125
|
def self.use_iso_date_format=(v)
|
|
126
|
-
PG_TYPES[1082] = TYPE_TRANSLATOR.method(
|
|
126
|
+
PG_TYPES[1082] = v ? TYPE_TRANSLATOR.method(:date) : Sequel.method(:string_to_date)
|
|
127
127
|
@use_iso_date_format = v
|
|
128
128
|
end
|
|
129
129
|
self.use_iso_date_format = true
|
|
@@ -131,7 +131,10 @@ module Sequel
|
|
|
131
131
|
# PGconn subclass for connection specific methods used with the
|
|
132
132
|
# pg, postgres, or postgres-pr driver.
|
|
133
133
|
class Adapter < ::PGconn
|
|
134
|
+
DISCONNECT_ERROR_RE = /\Acould not receive data from server: Software caused connection abort/
|
|
135
|
+
|
|
134
136
|
include Sequel::Postgres::AdapterMethods
|
|
137
|
+
|
|
135
138
|
self.translate_results = false if respond_to?(:translate_results=)
|
|
136
139
|
|
|
137
140
|
# Hash of prepared statements for this connection. Keys are
|
|
@@ -150,20 +153,23 @@ module Sequel
|
|
|
150
153
|
end
|
|
151
154
|
@prepared_statements = {} if SEQUEL_POSTGRES_USES_PG
|
|
152
155
|
end
|
|
153
|
-
|
|
156
|
+
|
|
154
157
|
# Raise a Sequel::DatabaseDisconnectError if a PGError is raised and
|
|
155
158
|
# the connection status cannot be determined or it is not OK.
|
|
156
159
|
def check_disconnect_errors
|
|
157
160
|
begin
|
|
158
161
|
yield
|
|
159
162
|
rescue PGError =>e
|
|
163
|
+
disconnect = false
|
|
160
164
|
begin
|
|
161
165
|
s = status
|
|
162
166
|
rescue PGError
|
|
163
|
-
|
|
167
|
+
disconnect = true
|
|
164
168
|
end
|
|
165
169
|
status_ok = (s == Adapter::CONNECTION_OK)
|
|
166
|
-
|
|
170
|
+
disconnect ||= !status_ok
|
|
171
|
+
disconnect ||= e.message =~ DISCONNECT_ERROR_RE
|
|
172
|
+
disconnect ? raise(Sequel.convert_exception_class(e, Sequel::DatabaseDisconnectError)) : raise
|
|
167
173
|
ensure
|
|
168
174
|
block if status_ok
|
|
169
175
|
end
|
|
@@ -176,7 +182,7 @@ module Sequel
|
|
|
176
182
|
begin
|
|
177
183
|
block_given? ? yield(q) : q.cmd_tuples
|
|
178
184
|
ensure
|
|
179
|
-
q.clear
|
|
185
|
+
q.clear if q
|
|
180
186
|
end
|
|
181
187
|
end
|
|
182
188
|
|
|
@@ -194,6 +200,10 @@ module Sequel
|
|
|
194
200
|
include Sequel::Postgres::DatabaseMethods
|
|
195
201
|
|
|
196
202
|
set_adapter_scheme :postgres
|
|
203
|
+
|
|
204
|
+
# A hash of conversion procs, keyed by type integer (oid) and
|
|
205
|
+
# having callable values for the conversion proc for that type.
|
|
206
|
+
attr_reader :conversion_procs
|
|
197
207
|
|
|
198
208
|
# Add the primary_keys and primary_key_sequences instance variables,
|
|
199
209
|
# so we can get the correct return values for inserted rows.
|
|
@@ -208,14 +218,27 @@ module Sequel
|
|
|
208
218
|
# client encoding for the connection.
|
|
209
219
|
def connect(server)
|
|
210
220
|
opts = server_opts(server)
|
|
211
|
-
conn =
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
221
|
+
conn = if SEQUEL_POSTGRES_USES_PG
|
|
222
|
+
connection_params = {
|
|
223
|
+
:host => opts[:host],
|
|
224
|
+
:port => opts[:port] || 5432,
|
|
225
|
+
:tty => '',
|
|
226
|
+
:dbname => opts[:database],
|
|
227
|
+
:user => opts[:user],
|
|
228
|
+
:password => opts[:password],
|
|
229
|
+
:connect_timeout => opts[:connect_timeout] || 20
|
|
230
|
+
}.delete_if { |key, value| blank_object?(value) }
|
|
231
|
+
Adapter.connect(connection_params)
|
|
232
|
+
else
|
|
233
|
+
Adapter.connect(
|
|
234
|
+
(opts[:host] unless blank_object?(opts[:host])),
|
|
235
|
+
opts[:port] || 5432,
|
|
236
|
+
nil, '',
|
|
237
|
+
opts[:database],
|
|
238
|
+
opts[:user],
|
|
239
|
+
opts[:password]
|
|
240
|
+
)
|
|
241
|
+
end
|
|
219
242
|
if encoding = opts[:encoding] || opts[:charset]
|
|
220
243
|
if conn.respond_to?(:set_client_encoding)
|
|
221
244
|
conn.set_client_encoding(encoding)
|
|
@@ -225,14 +248,10 @@ module Sequel
|
|
|
225
248
|
end
|
|
226
249
|
conn.db = self
|
|
227
250
|
conn.apply_connection_settings
|
|
251
|
+
@conversion_procs ||= get_conversion_procs(conn)
|
|
228
252
|
conn
|
|
229
253
|
end
|
|
230
254
|
|
|
231
|
-
# Return instance of Sequel::Postgres::Dataset with the given options.
|
|
232
|
-
def dataset(opts = nil)
|
|
233
|
-
Postgres::Dataset.new(self, opts)
|
|
234
|
-
end
|
|
235
|
-
|
|
236
255
|
# Execute the given SQL with the given args on an available connection.
|
|
237
256
|
def execute(sql, opts={}, &block)
|
|
238
257
|
check_database_errors do
|
|
@@ -253,6 +272,119 @@ module Sequel
|
|
|
253
272
|
end
|
|
254
273
|
end
|
|
255
274
|
|
|
275
|
+
if SEQUEL_POSTGRES_USES_PG
|
|
276
|
+
# +copy_table+ uses PostgreSQL's +COPY+ SQL statement to return formatted
|
|
277
|
+
# results directly to the caller. This method is only supported if pg is the
|
|
278
|
+
# underlying ruby driver. This method should only be called if you want
|
|
279
|
+
# results returned to the client. If you are using +COPY FROM+ or +COPY TO+
|
|
280
|
+
# with a filename, you should just use +run+ instead of this method. This
|
|
281
|
+
# method does not currently support +COPY FROM STDIN+, but that may be supported
|
|
282
|
+
# in the future.
|
|
283
|
+
#
|
|
284
|
+
# The table argument supports the following types:
|
|
285
|
+
#
|
|
286
|
+
# String :: Uses the first argument directly as literal SQL. If you are using
|
|
287
|
+
# a version of PostgreSQL before 9.0, you will probably want to
|
|
288
|
+
# use a string if you are using any options at all, as the syntax
|
|
289
|
+
# Sequel uses for options is only compatible with PostgreSQL 9.0+.
|
|
290
|
+
# Dataset :: Uses a query instead of a table name when copying.
|
|
291
|
+
# other :: Uses a table name (usually a symbol) when copying.
|
|
292
|
+
#
|
|
293
|
+
# The following options are respected:
|
|
294
|
+
#
|
|
295
|
+
# :format :: The format to use. text is the default, so this should be :csv or :binary.
|
|
296
|
+
# :options :: An options SQL string to use, which should contain comma separated options.
|
|
297
|
+
# :server :: The server on which to run the query.
|
|
298
|
+
#
|
|
299
|
+
# If a block is provided, the method continually yields to the block, one yield
|
|
300
|
+
# per row. If a block is not provided, a single string is returned with all
|
|
301
|
+
# of the data.
|
|
302
|
+
def copy_table(table, opts={})
|
|
303
|
+
sql = if table.is_a?(String)
|
|
304
|
+
sql = table
|
|
305
|
+
else
|
|
306
|
+
if opts[:options] || opts[:format]
|
|
307
|
+
options = " ("
|
|
308
|
+
options << "FORMAT #{opts[:format]}" if opts[:format]
|
|
309
|
+
options << "#{', ' if opts[:format]}#{opts[:options]}" if opts[:options]
|
|
310
|
+
options << ')'
|
|
311
|
+
end
|
|
312
|
+
table = if table.is_a?(::Sequel::Dataset)
|
|
313
|
+
"(#{table.sql})"
|
|
314
|
+
else
|
|
315
|
+
literal(table)
|
|
316
|
+
end
|
|
317
|
+
sql = "COPY #{table} TO STDOUT#{options}"
|
|
318
|
+
end
|
|
319
|
+
synchronize(opts[:server]) do |conn|
|
|
320
|
+
conn.execute(sql)
|
|
321
|
+
begin
|
|
322
|
+
if block_given?
|
|
323
|
+
while buf = conn.get_copy_data
|
|
324
|
+
yield buf
|
|
325
|
+
end
|
|
326
|
+
nil
|
|
327
|
+
else
|
|
328
|
+
b = ''
|
|
329
|
+
b << buf while buf = conn.get_copy_data
|
|
330
|
+
b
|
|
331
|
+
end
|
|
332
|
+
ensure
|
|
333
|
+
raise DatabaseDisconnectError, "disconnecting as a partial COPY may leave the connection in an unusable state" if buf
|
|
334
|
+
end
|
|
335
|
+
end
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
# Listens on the given channel (or multiple channels if channel is an array), waiting for notifications.
|
|
339
|
+
# After a notification is received, or the timeout has passed, stops listening to the channel. Options:
|
|
340
|
+
#
|
|
341
|
+
# :after_listen :: An object that responds to +call+ that is called with the underlying connection after the LISTEN
|
|
342
|
+
# statement is sent, but before the connection starts waiting for notifications.
|
|
343
|
+
# :loop :: Whether to continually wait for notifications, instead of just waiting for a single
|
|
344
|
+
# notification. If this option is given, a block must be provided. If this object responds to call, it is
|
|
345
|
+
# called with the underlying connection after each notification is received (after the block is called).
|
|
346
|
+
# If a :timeout option is used, and a callable object is given, the object will also be called if the
|
|
347
|
+
# timeout expires. If :loop is used and you want to stop listening, you can either break from inside the
|
|
348
|
+
# block given to #listen, or you can throw :stop from inside the :loop object's call method or the block.
|
|
349
|
+
# :server :: The server on which to listen, if the sharding support is being used.
|
|
350
|
+
# :timeout :: How long to wait for a notification, in seconds (can provide a float value for
|
|
351
|
+
# fractional seconds). If not given or nil, waits indefinitely.
|
|
352
|
+
#
|
|
353
|
+
# This method is only supported if pg is used as the underlying ruby driver. It returns the
|
|
354
|
+
# channel the notification was sent to (as a string), unless :loop was used, in which case it returns nil.
|
|
355
|
+
# If a block is given, it is yielded 3 arguments:
|
|
356
|
+
# * the channel the notification was sent to (as a string)
|
|
357
|
+
# * the backend pid of the notifier (as an integer),
|
|
358
|
+
# * and the payload of the notification (as a string or nil).
|
|
359
|
+
def listen(channels, opts={}, &block)
|
|
360
|
+
check_database_errors do
|
|
361
|
+
synchronize(opts[:server]) do |conn|
|
|
362
|
+
begin
|
|
363
|
+
channels = Array(channels)
|
|
364
|
+
channels.each{|channel| conn.execute("LISTEN #{channel}")}
|
|
365
|
+
opts[:after_listen].call(conn) if opts[:after_listen]
|
|
366
|
+
timeout = opts[:timeout] ? [opts[:timeout]] : []
|
|
367
|
+
if l = opts[:loop]
|
|
368
|
+
raise Error, 'calling #listen with :loop requires a block' unless block
|
|
369
|
+
loop_call = l.respond_to?(:call)
|
|
370
|
+
catch(:stop) do
|
|
371
|
+
loop do
|
|
372
|
+
conn.wait_for_notify(*timeout, &block)
|
|
373
|
+
l.call(conn) if loop_call
|
|
374
|
+
end
|
|
375
|
+
end
|
|
376
|
+
nil
|
|
377
|
+
else
|
|
378
|
+
conn.wait_for_notify(*timeout, &block)
|
|
379
|
+
end
|
|
380
|
+
ensure
|
|
381
|
+
conn.execute("UNLISTEN *")
|
|
382
|
+
end
|
|
383
|
+
end
|
|
384
|
+
end
|
|
385
|
+
end
|
|
386
|
+
end
|
|
387
|
+
|
|
256
388
|
private
|
|
257
389
|
|
|
258
390
|
# Convert exceptions raised from the block into DatabaseErrors.
|
|
@@ -305,12 +437,29 @@ module Sequel
|
|
|
305
437
|
end
|
|
306
438
|
end
|
|
307
439
|
end
|
|
440
|
+
|
|
441
|
+
# Return the conversion procs hash to use for this database
|
|
442
|
+
def get_conversion_procs(conn)
|
|
443
|
+
procs = PG_TYPES.dup
|
|
444
|
+
procs[1114] = method(:to_application_timestamp)
|
|
445
|
+
procs[1184] = method(:to_application_timestamp)
|
|
446
|
+
conn.execute("SELECT oid, typname FROM pg_type where typtype = 'b'") do |res|
|
|
447
|
+
res.ntuples.times do |recnum|
|
|
448
|
+
if pr = PG_NAMED_TYPES[res.getvalue(recnum, 1).untaint.to_sym]
|
|
449
|
+
procs[res.getvalue(recnum, 0).to_i] ||= pr
|
|
450
|
+
end
|
|
451
|
+
end
|
|
452
|
+
end
|
|
453
|
+
procs
|
|
454
|
+
end
|
|
308
455
|
end
|
|
309
456
|
|
|
310
457
|
# Dataset class for PostgreSQL datasets that use the pg, postgres, or
|
|
311
458
|
# postgres-pr driver.
|
|
312
459
|
class Dataset < Sequel::Dataset
|
|
313
460
|
include Sequel::Postgres::DatasetMethods
|
|
461
|
+
|
|
462
|
+
Database::DatasetClass = self
|
|
314
463
|
|
|
315
464
|
# Yield all rows returned by executing the given SQL and converting
|
|
316
465
|
# the types.
|
|
@@ -372,11 +521,17 @@ module Sequel
|
|
|
372
521
|
end
|
|
373
522
|
LiteralString.new("#{prepared_arg_placeholder}#{i}#{"::#{type}" if type}")
|
|
374
523
|
end
|
|
524
|
+
|
|
525
|
+
# Always assume a prepared argument.
|
|
526
|
+
def prepared_arg?(k)
|
|
527
|
+
true
|
|
528
|
+
end
|
|
375
529
|
end
|
|
376
530
|
|
|
377
531
|
# Allow use of bind arguments for PostgreSQL using the pg driver.
|
|
378
532
|
module BindArgumentMethods
|
|
379
533
|
include ArgumentMapper
|
|
534
|
+
include ::Sequel::Postgres::DatasetMethods::PreparedStatementMethods
|
|
380
535
|
|
|
381
536
|
private
|
|
382
537
|
|
|
@@ -400,7 +555,12 @@ module Sequel
|
|
|
400
555
|
# pg driver.
|
|
401
556
|
module PreparedStatementMethods
|
|
402
557
|
include BindArgumentMethods
|
|
403
|
-
|
|
558
|
+
|
|
559
|
+
# Raise a more obvious error if you attempt to call a unnamed prepared statement.
|
|
560
|
+
def call(*)
|
|
561
|
+
raise Error, "Cannot call prepared statement without a name" if prepared_statement_name.nil?
|
|
562
|
+
super
|
|
563
|
+
end
|
|
404
564
|
|
|
405
565
|
private
|
|
406
566
|
|
|
@@ -427,7 +587,7 @@ module Sequel
|
|
|
427
587
|
ps.extend(BindArgumentMethods)
|
|
428
588
|
ps.call(bind_vars, &block)
|
|
429
589
|
end
|
|
430
|
-
|
|
590
|
+
|
|
431
591
|
# Prepare the given type of statement with the given name, and store
|
|
432
592
|
# it in the database to be called later.
|
|
433
593
|
def prepare(type, name=nil, *values)
|
|
@@ -483,21 +643,22 @@ module Sequel
|
|
|
483
643
|
# field numers, type conversion procs, and name symbol arrays.
|
|
484
644
|
def fetch_rows_set_cols(res)
|
|
485
645
|
cols = []
|
|
646
|
+
procs = db.conversion_procs
|
|
486
647
|
res.nfields.times do |fieldnum|
|
|
487
|
-
cols << [fieldnum,
|
|
648
|
+
cols << [fieldnum, procs[res.ftype(fieldnum)], output_identifier(res.fname(fieldnum))]
|
|
488
649
|
end
|
|
489
650
|
@columns = cols.map{|c| c.at(2)}
|
|
490
651
|
cols
|
|
491
652
|
end
|
|
492
653
|
|
|
493
654
|
# Use the driver's escape_bytea
|
|
494
|
-
def
|
|
495
|
-
db.synchronize{|c|
|
|
655
|
+
def literal_blob_append(sql, v)
|
|
656
|
+
sql << "'" << db.synchronize{|c| c.escape_bytea(v)} << "'"
|
|
496
657
|
end
|
|
497
658
|
|
|
498
659
|
# Use the driver's escape_string
|
|
499
|
-
def
|
|
500
|
-
db.synchronize{|c|
|
|
660
|
+
def literal_string_append(sql, v)
|
|
661
|
+
sql << "'" << db.synchronize{|c| c.escape_string(v)} << "'"
|
|
501
662
|
end
|
|
502
663
|
|
|
503
664
|
# For each row in the result set, yield a hash with column name symbol
|
|
@@ -1,21 +1,17 @@
|
|
|
1
1
|
module Sequel
|
|
2
2
|
module Access
|
|
3
3
|
module DatabaseMethods
|
|
4
|
+
# Access uses type :access as the database_type
|
|
4
5
|
def database_type
|
|
5
6
|
:access
|
|
6
7
|
end
|
|
7
8
|
|
|
8
|
-
def dataset(opts = nil)
|
|
9
|
-
ds = super
|
|
10
|
-
ds.extend(DatasetMethods)
|
|
11
|
-
ds
|
|
12
|
-
end
|
|
13
|
-
|
|
14
9
|
# Doesn't work, due to security restrictions on MSysObjects
|
|
15
10
|
def tables
|
|
16
11
|
from(:MSysObjects).filter(:Type=>1, :Flags=>0).select_map(:Name).map{|x| x.to_sym}
|
|
17
12
|
end
|
|
18
13
|
|
|
14
|
+
# Access uses type Counter for an autoincrementing keys
|
|
19
15
|
def serial_primary_key_options
|
|
20
16
|
{:primary_key => true, :type=>:Counter}
|
|
21
17
|
end
|
|
@@ -32,18 +28,45 @@ module Sequel
|
|
|
32
28
|
end
|
|
33
29
|
|
|
34
30
|
module DatasetMethods
|
|
35
|
-
SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'limit distinct columns from join where group order having compounds')
|
|
31
|
+
SELECT_CLAUSE_METHODS = Dataset.clause_methods(:select, %w'select limit distinct columns from join where group order having compounds')
|
|
32
|
+
DATE_FORMAT = '#%Y-%m-%d#'.freeze
|
|
33
|
+
TIMESTAMP_FORMAT = '#%Y-%m-%d %H:%M:%S#'.freeze
|
|
34
|
+
TOP = " TOP ".freeze
|
|
35
|
+
BRACKET_CLOSE = Dataset::BRACKET_CLOSE
|
|
36
|
+
BRACKET_OPEN = Dataset::BRACKET_OPEN
|
|
36
37
|
|
|
38
|
+
# Access doesn't support INTERSECT or EXCEPT
|
|
37
39
|
def supports_intersect_except?
|
|
38
40
|
false
|
|
39
41
|
end
|
|
40
42
|
|
|
41
43
|
private
|
|
42
44
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
+
# Access uses # to quote dates
|
|
46
|
+
def literal_date(d)
|
|
47
|
+
d.strftime(DATE_FORMAT)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Access uses # to quote datetimes
|
|
51
|
+
def literal_datetime(t)
|
|
52
|
+
t.strftime(TIMESTAMP_FORMAT)
|
|
53
|
+
end
|
|
54
|
+
alias literal_time literal_datetime
|
|
55
|
+
|
|
56
|
+
# Access uses TOP for limits
|
|
57
|
+
def select_limit_sql(sql)
|
|
58
|
+
if l = @opts[:limit]
|
|
59
|
+
sql << TOP
|
|
60
|
+
literal_append(sql, l)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Access uses [] for quoting identifiers
|
|
65
|
+
def quoted_identifier_append(sql, v)
|
|
66
|
+
sql << BRACKET_OPEN << v.to_s << BRACKET_CLOSE
|
|
45
67
|
end
|
|
46
68
|
|
|
69
|
+
# Access requires the limit clause come before other clauses
|
|
47
70
|
def select_clause_methods
|
|
48
71
|
SELECT_CLAUSE_METHODS
|
|
49
72
|
end
|