sequel 5.30.0 → 5.35.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.
- checksums.yaml +4 -4
- data/CHANGELOG +86 -0
- data/README.rdoc +1 -1
- data/doc/advanced_associations.rdoc +4 -4
- data/doc/association_basics.rdoc +10 -5
- data/doc/code_order.rdoc +12 -2
- data/doc/dataset_filtering.rdoc +2 -2
- data/doc/model_dataset_method_design.rdoc +1 -1
- data/doc/postgresql.rdoc +71 -0
- data/doc/release_notes/5.31.0.txt +148 -0
- data/doc/release_notes/5.32.0.txt +46 -0
- data/doc/release_notes/5.33.0.txt +24 -0
- data/doc/release_notes/5.34.0.txt +40 -0
- data/doc/release_notes/5.35.0.txt +56 -0
- data/doc/testing.rdoc +1 -1
- data/lib/sequel/adapters/oracle.rb +2 -1
- data/lib/sequel/adapters/shared/access.rb +6 -6
- data/lib/sequel/adapters/shared/mssql.rb +5 -5
- data/lib/sequel/adapters/shared/mysql.rb +9 -9
- data/lib/sequel/adapters/shared/oracle.rb +16 -16
- data/lib/sequel/adapters/shared/postgres.rb +169 -14
- data/lib/sequel/adapters/shared/sqlanywhere.rb +9 -9
- data/lib/sequel/adapters/shared/sqlite.rb +33 -6
- data/lib/sequel/adapters/tinytds.rb +1 -0
- data/lib/sequel/connection_pool/sharded_single.rb +4 -1
- data/lib/sequel/connection_pool/sharded_threaded.rb +12 -12
- data/lib/sequel/connection_pool/single.rb +1 -1
- data/lib/sequel/connection_pool/threaded.rb +2 -2
- data/lib/sequel/core.rb +318 -314
- data/lib/sequel/database/connecting.rb +1 -1
- data/lib/sequel/database/misc.rb +16 -10
- data/lib/sequel/database/query.rb +3 -1
- data/lib/sequel/database/schema_generator.rb +0 -1
- data/lib/sequel/database/schema_methods.rb +15 -16
- data/lib/sequel/database/transactions.rb +7 -4
- data/lib/sequel/dataset/placeholder_literalizer.rb +3 -7
- data/lib/sequel/dataset/query.rb +5 -4
- data/lib/sequel/deprecated.rb +3 -1
- data/lib/sequel/exceptions.rb +2 -0
- data/lib/sequel/extensions/_pretty_table.rb +1 -2
- data/lib/sequel/extensions/columns_introspection.rb +1 -2
- data/lib/sequel/extensions/connection_expiration.rb +2 -2
- data/lib/sequel/extensions/connection_validator.rb +2 -2
- data/lib/sequel/extensions/core_refinements.rb +2 -0
- data/lib/sequel/extensions/duplicate_columns_handler.rb +2 -0
- data/lib/sequel/extensions/fiber_concurrency.rb +24 -0
- data/lib/sequel/extensions/index_caching.rb +9 -7
- data/lib/sequel/extensions/integer64.rb +2 -0
- data/lib/sequel/extensions/migration.rb +1 -2
- data/lib/sequel/extensions/pg_array_ops.rb +4 -0
- data/lib/sequel/extensions/pg_enum.rb +7 -2
- data/lib/sequel/extensions/pg_extended_date_support.rb +1 -1
- data/lib/sequel/extensions/pg_hstore.rb +6 -0
- data/lib/sequel/extensions/pg_hstore_ops.rb +2 -0
- data/lib/sequel/extensions/pg_inet.rb +15 -5
- data/lib/sequel/extensions/pg_interval.rb +2 -0
- data/lib/sequel/extensions/pg_json_ops.rb +2 -0
- data/lib/sequel/extensions/pg_range.rb +5 -7
- data/lib/sequel/extensions/pg_range_ops.rb +2 -0
- data/lib/sequel/extensions/pg_row.rb +0 -1
- data/lib/sequel/extensions/pg_timestamptz.rb +2 -0
- data/lib/sequel/extensions/run_transaction_hooks.rb +72 -0
- data/lib/sequel/extensions/s.rb +2 -0
- data/lib/sequel/extensions/schema_dumper.rb +10 -4
- data/lib/sequel/extensions/server_block.rb +3 -3
- data/lib/sequel/extensions/symbol_aref_refinement.rb +2 -0
- data/lib/sequel/extensions/symbol_as_refinement.rb +2 -0
- data/lib/sequel/extensions/to_dot.rb +9 -3
- data/lib/sequel/model.rb +2 -0
- data/lib/sequel/model/associations.rb +54 -25
- data/lib/sequel/model/base.rb +70 -57
- data/lib/sequel/model/plugins.rb +3 -3
- data/lib/sequel/plugins/association_lazy_eager_option.rb +66 -0
- data/lib/sequel/plugins/association_multi_add_remove.rb +2 -0
- data/lib/sequel/plugins/association_pks.rb +60 -18
- data/lib/sequel/plugins/association_proxies.rb +2 -0
- data/lib/sequel/plugins/blacklist_security.rb +1 -2
- data/lib/sequel/plugins/boolean_subsets.rb +4 -1
- data/lib/sequel/plugins/class_table_inheritance.rb +28 -28
- data/lib/sequel/plugins/csv_serializer.rb +2 -0
- data/lib/sequel/plugins/dirty.rb +13 -13
- data/lib/sequel/plugins/forbid_lazy_load.rb +216 -0
- data/lib/sequel/plugins/instance_specific_default.rb +113 -0
- data/lib/sequel/plugins/json_serializer.rb +3 -7
- data/lib/sequel/plugins/lazy_attributes.rb +1 -1
- data/lib/sequel/plugins/pg_array_associations.rb +2 -3
- data/lib/sequel/plugins/prepared_statements.rb +5 -11
- data/lib/sequel/plugins/prepared_statements_safe.rb +1 -3
- data/lib/sequel/plugins/rcte_tree.rb +10 -16
- data/lib/sequel/plugins/single_table_inheritance.rb +15 -15
- data/lib/sequel/plugins/skip_saving_columns.rb +108 -0
- data/lib/sequel/plugins/string_stripper.rb +1 -1
- data/lib/sequel/plugins/subclasses.rb +2 -0
- data/lib/sequel/plugins/validation_class_methods.rb +5 -1
- data/lib/sequel/timezones.rb +6 -4
- data/lib/sequel/version.rb +1 -1
- metadata +18 -2
|
@@ -23,15 +23,6 @@ module Sequel
|
|
|
23
23
|
to_application_timestamp(v.to_s) if v
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
-
# Convert smallint type to boolean if convert_smallint_to_bool is true
|
|
27
|
-
def schema_column_type(db_type)
|
|
28
|
-
if convert_smallint_to_bool && db_type =~ /smallint/i
|
|
29
|
-
:boolean
|
|
30
|
-
else
|
|
31
|
-
super
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
|
|
35
26
|
def schema_parse_table(table, opts)
|
|
36
27
|
m = output_identifier_meth(opts[:dataset])
|
|
37
28
|
im = input_identifier_meth(opts[:dataset])
|
|
@@ -216,6 +207,15 @@ module Sequel
|
|
|
216
207
|
"ALTER TABLE #{quote_schema_table(name)} RENAME #{quote_schema_table(new_name)}"
|
|
217
208
|
end
|
|
218
209
|
|
|
210
|
+
# Convert smallint type to boolean if convert_smallint_to_bool is true
|
|
211
|
+
def schema_column_type(db_type)
|
|
212
|
+
if convert_smallint_to_bool && db_type =~ /smallint/i
|
|
213
|
+
:boolean
|
|
214
|
+
else
|
|
215
|
+
super
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
|
|
219
219
|
def tables_and_views(type, opts=OPTS)
|
|
220
220
|
m = output_identifier_meth
|
|
221
221
|
metadata_dataset.
|
|
@@ -14,6 +14,7 @@ module Sequel
|
|
|
14
14
|
def schema_parse_table(*)
|
|
15
15
|
[]
|
|
16
16
|
end
|
|
17
|
+
singleton_class.send(:private, :schema_parse_table)
|
|
17
18
|
end
|
|
18
19
|
end
|
|
19
20
|
|
|
@@ -37,6 +38,10 @@ module Sequel
|
|
|
37
38
|
# booleans be stored as integers, but historically Sequel has used 't'/'f'.
|
|
38
39
|
attr_accessor :integer_booleans
|
|
39
40
|
|
|
41
|
+
# Whether to keep CURRENT_TIMESTAMP and similar expressions in UTC. By
|
|
42
|
+
# default, the expressions are converted to localtime.
|
|
43
|
+
attr_accessor :current_timestamp_utc
|
|
44
|
+
|
|
40
45
|
# A symbol signifying the value of the default transaction mode
|
|
41
46
|
attr_reader :transaction_mode
|
|
42
47
|
|
|
@@ -184,7 +189,7 @@ module Sequel
|
|
|
184
189
|
|
|
185
190
|
# Dataset used for parsing schema
|
|
186
191
|
def _parse_pragma_ds(table_name, opts)
|
|
187
|
-
metadata_dataset.with_sql("PRAGMA
|
|
192
|
+
metadata_dataset.with_sql("PRAGMA table_#{'x' if sqlite_version > 33100}info(?)", input_identifier_meth(opts[:dataset]).call(table_name))
|
|
188
193
|
end
|
|
189
194
|
|
|
190
195
|
# Run all alter_table commands in a transaction. This is technically only
|
|
@@ -252,13 +257,20 @@ module Sequel
|
|
|
252
257
|
when :drop_constraint
|
|
253
258
|
case op[:type]
|
|
254
259
|
when :primary_key
|
|
255
|
-
duplicate_table(table)
|
|
260
|
+
duplicate_table(table) do |columns|
|
|
261
|
+
columns.each do |s|
|
|
262
|
+
s[:unique] = false if s[:primary_key]
|
|
263
|
+
s[:primary_key] = s[:auto_increment] = nil
|
|
264
|
+
end
|
|
265
|
+
end
|
|
256
266
|
when :foreign_key
|
|
257
267
|
if op[:columns]
|
|
258
268
|
duplicate_table(table, :skip_foreign_key_columns=>op[:columns])
|
|
259
269
|
else
|
|
260
270
|
duplicate_table(table, :no_foreign_keys=>true)
|
|
261
271
|
end
|
|
272
|
+
when :unique
|
|
273
|
+
duplicate_table(table, :no_unique=>true)
|
|
262
274
|
else
|
|
263
275
|
duplicate_table(table)
|
|
264
276
|
end
|
|
@@ -412,15 +424,19 @@ module Sequel
|
|
|
412
424
|
skip_indexes = []
|
|
413
425
|
indexes(table, :only_autocreated=>true).each do |name, h|
|
|
414
426
|
skip_indexes << name
|
|
415
|
-
if h[:
|
|
416
|
-
|
|
427
|
+
if h[:unique]
|
|
428
|
+
if h[:columns].length == 1
|
|
429
|
+
unique_columns.concat(h[:columns])
|
|
430
|
+
elsif h[:columns].map(&:to_s) != pks && !opts[:no_unique]
|
|
431
|
+
constraints << {:type=>:unique, :columns=>h[:columns]}
|
|
432
|
+
end
|
|
417
433
|
end
|
|
418
434
|
end
|
|
419
435
|
unique_columns -= pks
|
|
420
436
|
unless unique_columns.empty?
|
|
421
437
|
unique_columns.map!{|c| quote_identifier(c)}
|
|
422
438
|
def_columns.each do |c|
|
|
423
|
-
c[:unique] = true if unique_columns.include?(quote_identifier(c[:name]))
|
|
439
|
+
c[:unique] = true if unique_columns.include?(quote_identifier(c[:name])) && c[:unique] != false
|
|
424
440
|
end
|
|
425
441
|
end
|
|
426
442
|
|
|
@@ -466,6 +482,15 @@ module Sequel
|
|
|
466
482
|
def parse_pragma(table_name, opts)
|
|
467
483
|
pks = 0
|
|
468
484
|
sch = _parse_pragma_ds(table_name, opts).map do |row|
|
|
485
|
+
if sqlite_version > 33100
|
|
486
|
+
# table_xinfo PRAGMA used, remove hidden columns
|
|
487
|
+
# that are not generated columns
|
|
488
|
+
if row[:generated] = (row.delete(:hidden) != 0)
|
|
489
|
+
next unless row[:type].end_with?(' GENERATED ALWAYS')
|
|
490
|
+
row[:type] = row[:type].sub(' GENERATED ALWAYS', '')
|
|
491
|
+
end
|
|
492
|
+
end
|
|
493
|
+
|
|
469
494
|
row.delete(:cid)
|
|
470
495
|
row[:allow_null] = row.delete(:notnull).to_i == 0
|
|
471
496
|
row[:default] = row.delete(:dflt_value)
|
|
@@ -482,6 +507,8 @@ module Sequel
|
|
|
482
507
|
row
|
|
483
508
|
end
|
|
484
509
|
|
|
510
|
+
sch.compact!
|
|
511
|
+
|
|
485
512
|
if pks > 1
|
|
486
513
|
# SQLite does not allow use of auto increment for tables
|
|
487
514
|
# with composite primary keys, so remove auto_increment
|
|
@@ -598,7 +625,7 @@ module Sequel
|
|
|
598
625
|
# SQLite has CURRENT_TIMESTAMP and related constants in UTC instead
|
|
599
626
|
# of in localtime, so convert those constants to local time.
|
|
600
627
|
def constant_sql_append(sql, constant)
|
|
601
|
-
if c = CONSTANT_MAP[constant]
|
|
628
|
+
if (c = CONSTANT_MAP[constant]) && !db.current_timestamp_utc
|
|
602
629
|
sql << c
|
|
603
630
|
else
|
|
604
631
|
super
|
|
@@ -41,7 +41,10 @@ class Sequel::ShardedSingleConnectionPool < Sequel::ConnectionPool
|
|
|
41
41
|
# :server :: Should be a symbol specifing the server to disconnect from,
|
|
42
42
|
# or an array of symbols to specify multiple servers.
|
|
43
43
|
def disconnect(opts=OPTS)
|
|
44
|
-
(opts[:server] ? Array(opts[:server]) : servers).each
|
|
44
|
+
(opts[:server] ? Array(opts[:server]) : servers).each do |s|
|
|
45
|
+
raise Sequel::Error, "invalid server: #{s}" unless @servers.has_key?(s)
|
|
46
|
+
disconnect_server(s)
|
|
47
|
+
end
|
|
45
48
|
end
|
|
46
49
|
|
|
47
50
|
def freeze
|
|
@@ -57,7 +57,7 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
|
|
|
57
57
|
# it is yielding all of the connections, which means that until
|
|
58
58
|
# the method's block returns, the pool is locked.
|
|
59
59
|
def all_connections
|
|
60
|
-
t =
|
|
60
|
+
t = Sequel.current
|
|
61
61
|
sync do
|
|
62
62
|
@allocated.values.each do |threads|
|
|
63
63
|
threads.each do |thread, conn|
|
|
@@ -95,9 +95,7 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
|
|
|
95
95
|
# or an array of symbols to specify multiple servers.
|
|
96
96
|
def disconnect(opts=OPTS)
|
|
97
97
|
(opts[:server] ? Array(opts[:server]) : sync{@servers.keys}).each do |s|
|
|
98
|
-
|
|
99
|
-
disconnect_connections(conns)
|
|
100
|
-
end
|
|
98
|
+
disconnect_connections(sync{disconnect_server_connections(s)})
|
|
101
99
|
end
|
|
102
100
|
end
|
|
103
101
|
|
|
@@ -121,7 +119,7 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
|
|
|
121
119
|
# connection can be acquired, a Sequel::PoolTimeout is raised.
|
|
122
120
|
def hold(server=:default)
|
|
123
121
|
server = pick_server(server)
|
|
124
|
-
t =
|
|
122
|
+
t = Sequel.current
|
|
125
123
|
if conn = owned_connection(t, server)
|
|
126
124
|
return yield(conn)
|
|
127
125
|
end
|
|
@@ -203,9 +201,9 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
|
|
|
203
201
|
|
|
204
202
|
until conn = assign_connection(thread, server)
|
|
205
203
|
elapsed = Sequel.elapsed_seconds_since(timer)
|
|
204
|
+
# :nocov:
|
|
206
205
|
raise_pool_timeout(elapsed, server) if elapsed > timeout
|
|
207
206
|
|
|
208
|
-
# :nocov:
|
|
209
207
|
# It's difficult to get to this point, it can only happen if there is a race condition
|
|
210
208
|
# where a connection cannot be acquired even after the thread is signalled by the condition variable
|
|
211
209
|
sync do
|
|
@@ -278,13 +276,15 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
|
|
|
278
276
|
# Mark any allocated connections to be removed when they are checked back in. The calling
|
|
279
277
|
# code should already have the mutex before calling this.
|
|
280
278
|
def disconnect_server_connections(server)
|
|
281
|
-
|
|
279
|
+
remove_conns = allocated(server)
|
|
280
|
+
dis_conns = available_connections(server)
|
|
281
|
+
raise Sequel::Error, "invalid server: #{server}" unless remove_conns && dis_conns
|
|
282
282
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
283
|
+
@connections_to_remove.concat(remove_conns.values)
|
|
284
|
+
|
|
285
|
+
conns = dis_conns.dup
|
|
286
|
+
dis_conns.clear
|
|
287
|
+
@waiters[server].signal
|
|
288
288
|
conns
|
|
289
289
|
end
|
|
290
290
|
|
|
@@ -11,7 +11,7 @@ class Sequel::SingleConnectionPool < Sequel::ConnectionPool
|
|
|
11
11
|
|
|
12
12
|
# Yield the connection if one has been made.
|
|
13
13
|
def all_connections
|
|
14
|
-
yield @conn.first
|
|
14
|
+
yield @conn.first unless @conn.empty?
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
# Disconnect the connection from the database.
|
|
@@ -83,7 +83,7 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
|
|
|
83
83
|
# is available or the timeout expires. If the timeout expires before a
|
|
84
84
|
# connection can be acquired, a Sequel::PoolTimeout is raised.
|
|
85
85
|
def hold(server=nil)
|
|
86
|
-
t =
|
|
86
|
+
t = Sequel.current
|
|
87
87
|
if conn = owned_connection(t)
|
|
88
88
|
return yield(conn)
|
|
89
89
|
end
|
|
@@ -152,9 +152,9 @@ class Sequel::ThreadedConnectionPool < Sequel::ConnectionPool
|
|
|
152
152
|
|
|
153
153
|
until conn = assign_connection(thread)
|
|
154
154
|
elapsed = Sequel.elapsed_seconds_since(timer)
|
|
155
|
+
# :nocov:
|
|
155
156
|
raise_pool_timeout(elapsed) if elapsed > timeout
|
|
156
157
|
|
|
157
|
-
# :nocov:
|
|
158
158
|
# It's difficult to get to this point, it can only happen if there is a race condition
|
|
159
159
|
# where a connection cannot be acquired even after the thread is signalled by the condition variable
|
|
160
160
|
sync do
|
data/lib/sequel/core.rb
CHANGED
|
@@ -30,7 +30,15 @@ module Sequel
|
|
|
30
30
|
@split_symbols = false
|
|
31
31
|
@single_threaded = false
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
# Mutex used to protect mutable data structures
|
|
34
|
+
@data_mutex = Mutex.new
|
|
35
|
+
|
|
36
|
+
# Frozen hash used as the default options hash for most options.
|
|
37
|
+
OPTS = {}.freeze
|
|
38
|
+
|
|
39
|
+
SPLIT_SYMBOL_CACHE = {}
|
|
40
|
+
|
|
41
|
+
module SequelMethods
|
|
34
42
|
# Sequel converts two digit years in <tt>Date</tt>s and <tt>DateTime</tt>s by default,
|
|
35
43
|
# so 01/02/03 is interpreted at January 2nd, 2003, and 12/13/99 is interpreted
|
|
36
44
|
# as December 13, 1999. You can override this to treat those dates as
|
|
@@ -63,361 +71,357 @@ module Sequel
|
|
|
63
71
|
# require for backwards compatibility.
|
|
64
72
|
alias orig_require require
|
|
65
73
|
private :orig_require
|
|
66
|
-
end
|
|
67
74
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
75
|
+
# Returns true if the passed object could be a specifier of conditions, false otherwise.
|
|
76
|
+
# Currently, Sequel considers hashes and arrays of two element arrays as
|
|
77
|
+
# condition specifiers.
|
|
78
|
+
#
|
|
79
|
+
# Sequel.condition_specifier?({}) # => true
|
|
80
|
+
# Sequel.condition_specifier?([[1, 2]]) # => true
|
|
81
|
+
# Sequel.condition_specifier?([]) # => false
|
|
82
|
+
# Sequel.condition_specifier?([1]) # => false
|
|
83
|
+
# Sequel.condition_specifier?(1) # => false
|
|
84
|
+
def condition_specifier?(obj)
|
|
85
|
+
case obj
|
|
86
|
+
when Hash
|
|
87
|
+
true
|
|
88
|
+
when Array
|
|
89
|
+
!obj.empty? && !obj.is_a?(SQL::ValueList) && obj.all?{|i| i.is_a?(Array) && (i.length == 2)}
|
|
90
|
+
else
|
|
91
|
+
false
|
|
92
|
+
end
|
|
85
93
|
end
|
|
86
|
-
end
|
|
87
94
|
|
|
88
|
-
|
|
89
|
-
|
|
95
|
+
# Creates a new database object based on the supplied connection string
|
|
96
|
+
# and optional arguments. The specified scheme determines the database
|
|
97
|
+
# class used, and the rest of the string specifies the connection options.
|
|
98
|
+
# For example:
|
|
99
|
+
#
|
|
100
|
+
# DB = Sequel.connect('sqlite:/') # Memory database
|
|
101
|
+
# DB = Sequel.connect('sqlite://blog.db') # ./blog.db
|
|
102
|
+
# DB = Sequel.connect('sqlite:///blog.db') # /blog.db
|
|
103
|
+
# DB = Sequel.connect('postgres://user:password@host:port/database_name')
|
|
104
|
+
# DB = Sequel.connect('sqlite:///blog.db', max_connections: 10)
|
|
105
|
+
#
|
|
106
|
+
# You can also pass a single options hash:
|
|
107
|
+
#
|
|
108
|
+
# DB = Sequel.connect(adapter: 'sqlite', database: './blog.db')
|
|
109
|
+
#
|
|
110
|
+
# If a block is given, it is passed the opened +Database+ object, which is
|
|
111
|
+
# closed when the block exits. For example:
|
|
112
|
+
#
|
|
113
|
+
# Sequel.connect('sqlite://blog.db'){|db| puts db[:users].count}
|
|
114
|
+
#
|
|
115
|
+
# If a block is not given, a reference to this database will be held in
|
|
116
|
+
# <tt>Sequel::DATABASES</tt> until it is removed manually. This is by
|
|
117
|
+
# design, and used by <tt>Sequel::Model</tt> to pick the default
|
|
118
|
+
# database. It is recommended to pass a block if you do not want the
|
|
119
|
+
# resulting Database object to remain in memory until the process
|
|
120
|
+
# terminates, or use the <tt>keep_reference: false</tt> Database option.
|
|
121
|
+
#
|
|
122
|
+
# For details, see the {"Connecting to a Database" guide}[rdoc-ref:doc/opening_databases.rdoc].
|
|
123
|
+
# To set up a primary/replica or sharded database connection, see the {"Primary/Replica Database Configurations and Sharding" guide}[rdoc-ref:doc/sharding.rdoc].
|
|
124
|
+
def connect(*args, &block)
|
|
125
|
+
Database.connect(*args, &block)
|
|
126
|
+
end
|
|
90
127
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
# DB = Sequel.connect('sqlite:/') # Memory database
|
|
97
|
-
# DB = Sequel.connect('sqlite://blog.db') # ./blog.db
|
|
98
|
-
# DB = Sequel.connect('sqlite:///blog.db') # /blog.db
|
|
99
|
-
# DB = Sequel.connect('postgres://user:password@host:port/database_name')
|
|
100
|
-
# DB = Sequel.connect('sqlite:///blog.db', max_connections: 10)
|
|
101
|
-
#
|
|
102
|
-
# You can also pass a single options hash:
|
|
103
|
-
#
|
|
104
|
-
# DB = Sequel.connect(adapter: 'sqlite', database: './blog.db')
|
|
105
|
-
#
|
|
106
|
-
# If a block is given, it is passed the opened +Database+ object, which is
|
|
107
|
-
# closed when the block exits. For example:
|
|
108
|
-
#
|
|
109
|
-
# Sequel.connect('sqlite://blog.db'){|db| puts db[:users].count}
|
|
110
|
-
#
|
|
111
|
-
# If a block is not given, a reference to this database will be held in
|
|
112
|
-
# <tt>Sequel::DATABASES</tt> until it is removed manually. This is by
|
|
113
|
-
# design, and used by <tt>Sequel::Model</tt> to pick the default
|
|
114
|
-
# database. It is recommended to pass a block if you do not want the
|
|
115
|
-
# resulting Database object to remain in memory until the process
|
|
116
|
-
# terminates, or use the <tt>keep_reference: false</tt> Database option.
|
|
117
|
-
#
|
|
118
|
-
# For details, see the {"Connecting to a Database" guide}[rdoc-ref:doc/opening_databases.rdoc].
|
|
119
|
-
# To set up a primary/replica or sharded database connection, see the {"Primary/Replica Database Configurations and Sharding" guide}[rdoc-ref:doc/sharding.rdoc].
|
|
120
|
-
def self.connect(*args, &block)
|
|
121
|
-
Database.connect(*args, &block)
|
|
122
|
-
end
|
|
128
|
+
# Assume the core extensions are not loaded by default, if the core_extensions
|
|
129
|
+
# extension is loaded, this will be overridden.
|
|
130
|
+
def core_extensions?
|
|
131
|
+
false
|
|
132
|
+
end
|
|
123
133
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
134
|
+
# Convert the +exception+ to the given class. The given class should be
|
|
135
|
+
# <tt>Sequel::Error</tt> or a subclass. Returns an instance of +klass+ with
|
|
136
|
+
# the message and backtrace of +exception+.
|
|
137
|
+
def convert_exception_class(exception, klass)
|
|
138
|
+
return exception if exception.is_a?(klass)
|
|
139
|
+
e = klass.new("#{exception.class}: #{exception.message}")
|
|
140
|
+
e.wrapped_exception = exception
|
|
141
|
+
e.set_backtrace(exception.backtrace)
|
|
142
|
+
e
|
|
143
|
+
end
|
|
129
144
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
return exception if exception.is_a?(klass)
|
|
135
|
-
e = klass.new("#{exception.class}: #{exception.message}")
|
|
136
|
-
e.wrapped_exception = exception
|
|
137
|
-
e.set_backtrace(exception.backtrace)
|
|
138
|
-
e
|
|
139
|
-
end
|
|
145
|
+
# The current concurrency primitive, Thread.current by default.
|
|
146
|
+
def current
|
|
147
|
+
Thread.current
|
|
148
|
+
end
|
|
140
149
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
150
|
+
# Load all Sequel extensions given. Extensions are just files that exist under
|
|
151
|
+
# <tt>sequel/extensions</tt> in the load path, and are just required.
|
|
152
|
+
# In some cases, requiring an extension modifies classes directly, and in others,
|
|
153
|
+
# it just loads a module that you can extend other classes with. Consult the documentation
|
|
154
|
+
# for each extension you plan on using for usage.
|
|
155
|
+
#
|
|
156
|
+
# Sequel.extension(:blank)
|
|
157
|
+
# Sequel.extension(:core_extensions, :named_timezones)
|
|
158
|
+
def extension(*extensions)
|
|
159
|
+
extensions.each{|e| orig_require("sequel/extensions/#{e}")}
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# The exception classed raised if there is an error parsing JSON.
|
|
163
|
+
# This can be overridden to use an alternative json implementation.
|
|
164
|
+
def json_parser_error_class
|
|
165
|
+
JSON::ParserError
|
|
166
|
+
end
|
|
158
167
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
168
|
+
# Convert given object to json and return the result.
|
|
169
|
+
# This can be overridden to use an alternative json implementation.
|
|
170
|
+
def object_to_json(obj, *args, &block)
|
|
171
|
+
obj.to_json(*args, &block)
|
|
172
|
+
end
|
|
164
173
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
174
|
+
# Parse the string as JSON and return the result.
|
|
175
|
+
# This can be overridden to use an alternative json implementation.
|
|
176
|
+
def parse_json(json)
|
|
177
|
+
JSON.parse(json, :create_additions=>false)
|
|
178
|
+
end
|
|
170
179
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
+
# Convert each item in the array to the correct type, handling multi-dimensional
|
|
181
|
+
# arrays. For each element in the array or subarrays, call the converter,
|
|
182
|
+
# unless the value is nil.
|
|
183
|
+
def recursive_map(array, converter)
|
|
184
|
+
array.map do |i|
|
|
185
|
+
if i.is_a?(Array)
|
|
186
|
+
recursive_map(i, converter)
|
|
187
|
+
elsif !i.nil?
|
|
188
|
+
converter.call(i)
|
|
189
|
+
end
|
|
180
190
|
end
|
|
181
191
|
end
|
|
182
|
-
end
|
|
183
|
-
|
|
184
|
-
# For backwards compatibility only. require_relative should be used instead.
|
|
185
|
-
def self.require(files, subdir=nil)
|
|
186
|
-
# Use Kernel.require_relative to work around JRuby 9.0 bug
|
|
187
|
-
Array(files).each{|f| Kernel.require_relative "#{"#{subdir}/" if subdir}#{f}"}
|
|
188
|
-
end
|
|
189
192
|
|
|
190
|
-
|
|
193
|
+
# For backwards compatibility only. require_relative should be used instead.
|
|
194
|
+
def require(files, subdir=nil)
|
|
195
|
+
# Use Kernel.require_relative to work around JRuby 9.0 bug
|
|
196
|
+
Array(files).each{|f| Kernel.require_relative "#{"#{subdir}/" if subdir}#{f}"}
|
|
197
|
+
end
|
|
191
198
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
199
|
+
# Splits the symbol into three parts, if symbol splitting is enabled (not the default).
|
|
200
|
+
# Each part will either be a string or nil. If symbol splitting
|
|
201
|
+
# is disabled, returns an array with the first and third parts
|
|
202
|
+
# being nil, and the second part beind a string version of the symbol.
|
|
203
|
+
#
|
|
204
|
+
# For columns, these parts are the table, column, and alias.
|
|
205
|
+
# For tables, these parts are the schema, table, and alias.
|
|
206
|
+
def split_symbol(sym)
|
|
207
|
+
unless v = Sequel.synchronize{SPLIT_SYMBOL_CACHE[sym]}
|
|
208
|
+
if split_symbols?
|
|
209
|
+
v = case s = sym.to_s
|
|
210
|
+
when /\A((?:(?!__).)+)__((?:(?!___).)+)___(.+)\z/
|
|
211
|
+
[$1.freeze, $2.freeze, $3.freeze].freeze
|
|
212
|
+
when /\A((?:(?!___).)+)___(.+)\z/
|
|
213
|
+
[nil, $1.freeze, $2.freeze].freeze
|
|
214
|
+
when /\A((?:(?!__).)+)__(.+)\z/
|
|
215
|
+
[$1.freeze, $2.freeze, nil].freeze
|
|
216
|
+
else
|
|
217
|
+
[nil, s.freeze, nil].freeze
|
|
218
|
+
end
|
|
209
219
|
else
|
|
210
|
-
[nil,
|
|
220
|
+
v = [nil,sym.to_s.freeze,nil].freeze
|
|
211
221
|
end
|
|
212
|
-
|
|
213
|
-
v = [nil,sym.to_s.freeze,nil].freeze
|
|
222
|
+
Sequel.synchronize{SPLIT_SYMBOL_CACHE[sym] = v}
|
|
214
223
|
end
|
|
215
|
-
|
|
224
|
+
v
|
|
216
225
|
end
|
|
217
|
-
v
|
|
218
|
-
end
|
|
219
|
-
|
|
220
|
-
# Setting this to true enables Sequel's historical behavior of splitting
|
|
221
|
-
# symbols on double or triple underscores:
|
|
222
|
-
#
|
|
223
|
-
# :table__column # table.column
|
|
224
|
-
# :column___alias # column AS alias
|
|
225
|
-
# :table__column___alias # table.column AS alias
|
|
226
|
-
#
|
|
227
|
-
# It is only recommended to turn this on for backwards compatibility until
|
|
228
|
-
# such symbols have been converted to use newer Sequel APIs such as:
|
|
229
|
-
#
|
|
230
|
-
# Sequel[:table][:column] # table.column
|
|
231
|
-
# Sequel[:column].as(:alias) # column AS alias
|
|
232
|
-
# Sequel[:table][:column].as(:alias) # table.column AS alias
|
|
233
|
-
#
|
|
234
|
-
# Sequel::Database instances do their own caching of literalized
|
|
235
|
-
# symbols, and changing this setting does not affect those caches. It is
|
|
236
|
-
# recommended that if you want to change this setting, you do so directly
|
|
237
|
-
# after requiring Sequel, before creating any Sequel::Database instances.
|
|
238
|
-
#
|
|
239
|
-
# Disabling symbol splitting will also disable the handling
|
|
240
|
-
# of double underscores in virtual row methods, causing such methods to
|
|
241
|
-
# yield regular identifers instead of qualified identifiers:
|
|
242
|
-
#
|
|
243
|
-
# # Sequel.split_symbols = true
|
|
244
|
-
# Sequel.expr{table__column} # table.column
|
|
245
|
-
# Sequel.expr{table[:column]} # table.column
|
|
246
|
-
#
|
|
247
|
-
# # Sequel.split_symbols = false
|
|
248
|
-
# Sequel.expr{table__column} # table__column
|
|
249
|
-
# Sequel.expr{table[:column]} # table.column
|
|
250
|
-
def self.split_symbols=(v)
|
|
251
|
-
Sequel.synchronize{SPLIT_SYMBOL_CACHE.clear}
|
|
252
|
-
@split_symbols = v
|
|
253
|
-
end
|
|
254
226
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
227
|
+
# Setting this to true enables Sequel's historical behavior of splitting
|
|
228
|
+
# symbols on double or triple underscores:
|
|
229
|
+
#
|
|
230
|
+
# :table__column # table.column
|
|
231
|
+
# :column___alias # column AS alias
|
|
232
|
+
# :table__column___alias # table.column AS alias
|
|
233
|
+
#
|
|
234
|
+
# It is only recommended to turn this on for backwards compatibility until
|
|
235
|
+
# such symbols have been converted to use newer Sequel APIs such as:
|
|
236
|
+
#
|
|
237
|
+
# Sequel[:table][:column] # table.column
|
|
238
|
+
# Sequel[:column].as(:alias) # column AS alias
|
|
239
|
+
# Sequel[:table][:column].as(:alias) # table.column AS alias
|
|
240
|
+
#
|
|
241
|
+
# Sequel::Database instances do their own caching of literalized
|
|
242
|
+
# symbols, and changing this setting does not affect those caches. It is
|
|
243
|
+
# recommended that if you want to change this setting, you do so directly
|
|
244
|
+
# after requiring Sequel, before creating any Sequel::Database instances.
|
|
245
|
+
#
|
|
246
|
+
# Disabling symbol splitting will also disable the handling
|
|
247
|
+
# of double underscores in virtual row methods, causing such methods to
|
|
248
|
+
# yield regular identifers instead of qualified identifiers:
|
|
249
|
+
#
|
|
250
|
+
# # Sequel.split_symbols = true
|
|
251
|
+
# Sequel.expr{table__column} # table.column
|
|
252
|
+
# Sequel.expr{table[:column]} # table.column
|
|
253
|
+
#
|
|
254
|
+
# # Sequel.split_symbols = false
|
|
255
|
+
# Sequel.expr{table__column} # table__column
|
|
256
|
+
# Sequel.expr{table[:column]} # table.column
|
|
257
|
+
def split_symbols=(v)
|
|
258
|
+
Sequel.synchronize{SPLIT_SYMBOL_CACHE.clear}
|
|
259
|
+
@split_symbols = v
|
|
260
|
+
end
|
|
259
261
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
def self.string_to_date(string)
|
|
264
|
-
begin
|
|
265
|
-
Date.parse(string, Sequel.convert_two_digit_years)
|
|
266
|
-
rescue => e
|
|
267
|
-
raise convert_exception_class(e, InvalidValue)
|
|
262
|
+
# Whether Sequel currently splits symbols into qualified/aliased identifiers.
|
|
263
|
+
def split_symbols?
|
|
264
|
+
@split_symbols
|
|
268
265
|
end
|
|
269
|
-
end
|
|
270
266
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
else
|
|
280
|
-
datetime_class.parse(string)
|
|
267
|
+
# Converts the given +string+ into a +Date+ object.
|
|
268
|
+
#
|
|
269
|
+
# Sequel.string_to_date('2010-09-10') # Date.civil(2010, 09, 10)
|
|
270
|
+
def string_to_date(string)
|
|
271
|
+
begin
|
|
272
|
+
Date.parse(string, Sequel.convert_two_digit_years)
|
|
273
|
+
rescue => e
|
|
274
|
+
raise convert_exception_class(e, InvalidValue)
|
|
281
275
|
end
|
|
282
|
-
rescue => e
|
|
283
|
-
raise convert_exception_class(e, InvalidValue)
|
|
284
276
|
end
|
|
285
|
-
end
|
|
286
277
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
278
|
+
# Converts the given +string+ into a +Time+ or +DateTime+ object, depending on the
|
|
279
|
+
# value of <tt>Sequel.datetime_class</tt>.
|
|
280
|
+
#
|
|
281
|
+
# Sequel.string_to_datetime('2010-09-10 10:20:30') # Time.local(2010, 09, 10, 10, 20, 30)
|
|
282
|
+
def string_to_datetime(string)
|
|
283
|
+
begin
|
|
284
|
+
if datetime_class == DateTime
|
|
285
|
+
DateTime.parse(string, convert_two_digit_years)
|
|
286
|
+
else
|
|
287
|
+
datetime_class.parse(string)
|
|
288
|
+
end
|
|
289
|
+
rescue => e
|
|
290
|
+
raise convert_exception_class(e, InvalidValue)
|
|
291
|
+
end
|
|
296
292
|
end
|
|
297
|
-
end
|
|
298
|
-
|
|
299
|
-
# Mutex used to protect mutable data structures
|
|
300
|
-
@data_mutex = Mutex.new
|
|
301
|
-
|
|
302
|
-
# Unless in single threaded mode, protects access to any mutable
|
|
303
|
-
# global data structure in Sequel.
|
|
304
|
-
# Uses a non-reentrant mutex, so calling code should be careful.
|
|
305
|
-
# In general, this should only be used around the minimal possible code
|
|
306
|
-
# such as Hash#[], Hash#[]=, Hash#delete, Array#<<, and Array#delete.
|
|
307
|
-
def self.synchronize(&block)
|
|
308
|
-
@single_threaded ? yield : @data_mutex.synchronize(&block)
|
|
309
|
-
end
|
|
310
293
|
|
|
311
|
-
|
|
312
|
-
#
|
|
313
|
-
#
|
|
314
|
-
|
|
315
|
-
|
|
294
|
+
# Converts the given +string+ into a <tt>Sequel::SQLTime</tt> object.
|
|
295
|
+
#
|
|
296
|
+
# v = Sequel.string_to_time('10:20:30') # Sequel::SQLTime.parse('10:20:30')
|
|
297
|
+
# DB.literal(v) # => '10:20:30'
|
|
298
|
+
def string_to_time(string)
|
|
299
|
+
begin
|
|
300
|
+
SQLTime.parse(string)
|
|
301
|
+
rescue => e
|
|
302
|
+
raise convert_exception_class(e, InvalidValue)
|
|
303
|
+
end
|
|
316
304
|
end
|
|
317
|
-
|
|
318
|
-
#
|
|
319
|
-
|
|
320
|
-
|
|
305
|
+
|
|
306
|
+
# Unless in single threaded mode, protects access to any mutable
|
|
307
|
+
# global data structure in Sequel.
|
|
308
|
+
# Uses a non-reentrant mutex, so calling code should be careful.
|
|
309
|
+
# In general, this should only be used around the minimal possible code
|
|
310
|
+
# such as Hash#[], Hash#[]=, Hash#delete, Array#<<, and Array#delete.
|
|
311
|
+
def synchronize(&block)
|
|
312
|
+
@single_threaded ? yield : @data_mutex.synchronize(&block)
|
|
321
313
|
end
|
|
322
|
-
# :nocov:
|
|
323
|
-
end
|
|
324
314
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
315
|
+
if RUBY_VERSION >= '2.1'
|
|
316
|
+
# A timer object that can be passed to Sequel.elapsed_seconds_since
|
|
317
|
+
# to return the number of seconds elapsed.
|
|
318
|
+
def start_timer
|
|
319
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
320
|
+
end
|
|
321
|
+
else
|
|
322
|
+
# :nocov:
|
|
323
|
+
def start_timer # :nodoc:
|
|
324
|
+
Time.now
|
|
325
|
+
end
|
|
326
|
+
# :nocov:
|
|
327
|
+
end
|
|
330
328
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
# is equivalent to:
|
|
336
|
-
#
|
|
337
|
-
# DB1.transaction do
|
|
338
|
-
# DB2.transaction do
|
|
339
|
-
# DB3.transaction do
|
|
340
|
-
# end
|
|
341
|
-
# end
|
|
342
|
-
# end
|
|
343
|
-
#
|
|
344
|
-
# except that if Sequel::Rollback is raised by the block, the transaction is
|
|
345
|
-
# rolled back on all databases instead of just the last one.
|
|
346
|
-
#
|
|
347
|
-
# Note that this method cannot guarantee that all databases will commit or
|
|
348
|
-
# rollback. For example, if DB3 commits but attempting to commit on DB2
|
|
349
|
-
# fails (maybe because foreign key checks are deferred), there is no way
|
|
350
|
-
# to uncommit the changes on DB3. For that kind of support, you need to
|
|
351
|
-
# have two-phase commit/prepared transactions (which Sequel supports on
|
|
352
|
-
# some databases).
|
|
353
|
-
def self.transaction(dbs, opts=OPTS, &block)
|
|
354
|
-
unless opts[:rollback]
|
|
355
|
-
rescue_rollback = true
|
|
356
|
-
opts = Hash[opts].merge!(:rollback=>:reraise)
|
|
329
|
+
# The elapsed seconds since the given timer object was created. The
|
|
330
|
+
# timer object should have been created via Sequel.start_timer.
|
|
331
|
+
def elapsed_seconds_since(timer)
|
|
332
|
+
start_timer - timer
|
|
357
333
|
end
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
334
|
+
|
|
335
|
+
# Uses a transaction on all given databases with the given options. This:
|
|
336
|
+
#
|
|
337
|
+
# Sequel.transaction([DB1, DB2, DB3]){}
|
|
338
|
+
#
|
|
339
|
+
# is equivalent to:
|
|
340
|
+
#
|
|
341
|
+
# DB1.transaction do
|
|
342
|
+
# DB2.transaction do
|
|
343
|
+
# DB3.transaction do
|
|
344
|
+
# end
|
|
345
|
+
# end
|
|
346
|
+
# end
|
|
347
|
+
#
|
|
348
|
+
# except that if Sequel::Rollback is raised by the block, the transaction is
|
|
349
|
+
# rolled back on all databases instead of just the last one.
|
|
350
|
+
#
|
|
351
|
+
# Note that this method cannot guarantee that all databases will commit or
|
|
352
|
+
# rollback. For example, if DB3 commits but attempting to commit on DB2
|
|
353
|
+
# fails (maybe because foreign key checks are deferred), there is no way
|
|
354
|
+
# to uncommit the changes on DB3. For that kind of support, you need to
|
|
355
|
+
# have two-phase commit/prepared transactions (which Sequel supports on
|
|
356
|
+
# some databases).
|
|
357
|
+
def transaction(dbs, opts=OPTS, &block)
|
|
358
|
+
unless opts[:rollback]
|
|
359
|
+
rescue_rollback = true
|
|
360
|
+
opts = Hash[opts].merge!(:rollback=>:reraise)
|
|
361
|
+
end
|
|
362
|
+
pr = dbs.reverse.inject(block){|bl, db| proc{db.transaction(opts, &bl)}}
|
|
363
|
+
if rescue_rollback
|
|
364
|
+
begin
|
|
365
|
+
pr.call
|
|
366
|
+
rescue Sequel::Rollback
|
|
367
|
+
nil
|
|
368
|
+
end
|
|
369
|
+
else
|
|
361
370
|
pr.call
|
|
362
|
-
rescue Sequel::Rollback
|
|
363
|
-
nil
|
|
364
371
|
end
|
|
365
|
-
else
|
|
366
|
-
pr.call
|
|
367
372
|
end
|
|
368
|
-
end
|
|
369
373
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
374
|
+
# If the supplied block takes a single argument,
|
|
375
|
+
# yield an <tt>SQL::VirtualRow</tt> instance to the block
|
|
376
|
+
# argument. Otherwise, evaluate the block in the context of a
|
|
377
|
+
# <tt>SQL::VirtualRow</tt> instance.
|
|
378
|
+
#
|
|
379
|
+
# Sequel.virtual_row{a} # Sequel::SQL::Identifier.new(:a)
|
|
380
|
+
# Sequel.virtual_row{|o| o.a} # Sequel::SQL::Function.new(:a)
|
|
381
|
+
def virtual_row(&block)
|
|
382
|
+
vr = VIRTUAL_ROW
|
|
383
|
+
case block.arity
|
|
384
|
+
when -1, 0
|
|
385
|
+
vr.instance_exec(&block)
|
|
386
|
+
else
|
|
387
|
+
block.call(vr)
|
|
388
|
+
end
|
|
389
|
+
end
|
|
386
390
|
|
|
387
|
-
|
|
391
|
+
private
|
|
388
392
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
393
|
+
# Helper method that the database adapter class methods that are added to Sequel via
|
|
394
|
+
# metaprogramming use to parse arguments.
|
|
395
|
+
def adapter_method(adapter, *args, &block)
|
|
396
|
+
options = args.last.is_a?(Hash) ? args.pop : OPTS
|
|
397
|
+
opts = {:adapter => adapter.to_sym}
|
|
398
|
+
opts[:database] = args.shift if args.first.is_a?(String)
|
|
399
|
+
if args.any?
|
|
400
|
+
raise ::Sequel::Error, "Wrong format of arguments, either use (), (String), (Hash), or (String, Hash)"
|
|
401
|
+
end
|
|
398
402
|
|
|
399
|
-
|
|
400
|
-
|
|
403
|
+
connect(opts.merge(options), &block)
|
|
404
|
+
end
|
|
401
405
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
406
|
+
# Method that adds a database adapter class method to Sequel that calls
|
|
407
|
+
# Sequel.adapter_method.
|
|
408
|
+
def def_adapter_method(*adapters) # :nodoc:
|
|
409
|
+
adapters.each do |adapter|
|
|
410
|
+
define_singleton_method(adapter){|*args, &block| adapter_method(adapter, *args, &block)}
|
|
411
|
+
end
|
|
407
412
|
end
|
|
408
413
|
end
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
require_relative "version"
|
|
414
|
+
extend SequelMethods
|
|
415
|
+
|
|
416
|
+
require_relative "deprecated"
|
|
417
|
+
require_relative "sql"
|
|
418
|
+
require_relative "connection_pool"
|
|
419
|
+
require_relative "exceptions"
|
|
420
|
+
require_relative "dataset"
|
|
421
|
+
require_relative "database"
|
|
422
|
+
require_relative "timezones"
|
|
423
|
+
require_relative "ast_transformer"
|
|
424
|
+
require_relative "version"
|
|
421
425
|
|
|
422
426
|
class << self
|
|
423
427
|
# Allow nicer syntax for creating Sequel expressions:
|