sequel 5.31.0 → 5.32.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +16 -0
- data/doc/advanced_associations.rdoc +4 -4
- data/doc/association_basics.rdoc +3 -3
- data/doc/code_order.rdoc +12 -2
- data/doc/model_dataset_method_design.rdoc +1 -1
- data/doc/release_notes/5.32.0.txt +46 -0
- data/doc/testing.rdoc +1 -0
- 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 +18 -8
- data/lib/sequel/adapters/shared/sqlanywhere.rb +9 -9
- data/lib/sequel/adapters/shared/sqlite.rb +1 -0
- data/lib/sequel/connection_pool/sharded_threaded.rb +2 -2
- data/lib/sequel/connection_pool/threaded.rb +1 -1
- data/lib/sequel/core.rb +318 -314
- data/lib/sequel/database/query.rb +1 -1
- data/lib/sequel/extensions/connection_expiration.rb +2 -2
- data/lib/sequel/extensions/connection_validator.rb +2 -2
- 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/pg_inet.rb +13 -5
- data/lib/sequel/extensions/pg_interval.rb +2 -0
- data/lib/sequel/extensions/pg_range.rb +2 -0
- data/lib/sequel/extensions/pg_timestamptz.rb +2 -0
- data/lib/sequel/extensions/run_transaction_hooks.rb +72 -0
- data/lib/sequel/extensions/server_block.rb +3 -3
- data/lib/sequel/model/base.rb +48 -47
- data/lib/sequel/model/plugins.rb +1 -0
- data/lib/sequel/plugins/association_lazy_eager_option.rb +2 -0
- data/lib/sequel/plugins/association_multi_add_remove.rb +2 -0
- data/lib/sequel/plugins/association_proxies.rb +2 -0
- data/lib/sequel/plugins/boolean_subsets.rb +4 -1
- data/lib/sequel/plugins/class_table_inheritance.rb +26 -26
- data/lib/sequel/plugins/dirty.rb +13 -13
- data/lib/sequel/plugins/json_serializer.rb +3 -7
- data/lib/sequel/plugins/single_table_inheritance.rb +15 -15
- data/lib/sequel/plugins/subclasses.rb +2 -0
- data/lib/sequel/timezones.rb +6 -4
- data/lib/sequel/version.rb +1 -1
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5d15527276e1c30bbe39a7260589f3a1e027908dfa51b01e0c365e4162850605
|
4
|
+
data.tar.gz: 7ff61ff4f1af3ef3700c8a8bc15cda941f68eb42f19f28fc376126ebb1f9f9ed
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 154fd4039919acb1185ad57c2671eb3cd7162dcc6652f803f2f64ce3acc6204ea99b95a485fab1f003590199f4a1e0213fee1c779bec7852c08db6e17f5995c4
|
7
|
+
data.tar.gz: 398dcdb11a158c49ba270ffecfe6a330085784b28448c367eb4678c1fb394c968377193a06d01db6d6d938788afaad45e2ac56fcfe6688e2c6a8211b42a958cd
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
=== 5.32.0 (2020-05-01)
|
2
|
+
|
3
|
+
* Allow Database#create_table? work with :partition_of option on PostgreSQL (jeremyevans) (#1690)
|
4
|
+
|
5
|
+
* Add fiber_concurrency extension, for using Fiber.current instead of Thread.current for checking out connections (jeremyevans)
|
6
|
+
|
7
|
+
* Move most Sequel singleton methods into a module that extends Sequel for easier overriding (jeremyevans)
|
8
|
+
|
9
|
+
* Fix method visibility issues in model, plugin, extension, and adapter code (jeremyevans)
|
10
|
+
|
11
|
+
* Avoid defining conversion procs for PostgreSQL inet/cidr types in pg_inet extension when using sequel_pg 1.13.0+ (jeremyevans)
|
12
|
+
|
13
|
+
* Add run_transaction_hooks Database extension, allowing for running the transaction hooks before commit/rollback, for use with transactional testing (jeremyevans)
|
14
|
+
|
15
|
+
* Recognize timestamp(N) with time zone type (isc) (#1684)
|
16
|
+
|
1
17
|
=== 5.31.0 (2020-04-01)
|
2
18
|
|
3
19
|
* Fix alter_table drop_constraint :primary_key option on SQLite for non-integer primary keys (jeremyevans)
|
@@ -731,7 +731,7 @@ associations:
|
|
731
731
|
one_to_one :tracks, key: [:disc_number, :number, :album_id], primary_key: [:disc_number, :number, :album_id]
|
732
732
|
end
|
733
733
|
|
734
|
-
=== Tree - All Ancestors and
|
734
|
+
=== Tree - All Ancestors and Descendants
|
735
735
|
|
736
736
|
Let's say you want to store a tree relationship in your database, it's pretty
|
737
737
|
simple:
|
@@ -749,7 +749,7 @@ node.children. You can even eager load the relationship up to a certain depth:
|
|
749
749
|
# Load parents and grandparents for a group of nodes
|
750
750
|
Node.where{id < 10}.eager(parent: :parent).all
|
751
751
|
|
752
|
-
What if you want to get all ancestors up to the root node, or all
|
752
|
+
What if you want to get all ancestors up to the root node, or all descendants,
|
753
753
|
without knowing the depth of the tree?
|
754
754
|
|
755
755
|
class Node < Sequel::Model
|
@@ -770,7 +770,7 @@ without knowing the depth of the tree?
|
|
770
770
|
id_map = {}
|
771
771
|
# Create an map of parent_ids to nodes that have that parent id
|
772
772
|
non_root_nodes.each{|n| (id_map[n.parent_id] ||= []) << n}
|
773
|
-
# Doesn't cause an
|
773
|
+
# Doesn't cause an infinite loop, because when only the root node
|
774
774
|
# is left, this is not called.
|
775
775
|
Node.where(id: id_map.keys).eager(:ancestors).all do |node|
|
776
776
|
# Populate the parent association for each node
|
@@ -879,4 +879,4 @@ associated tickets.
|
|
879
879
|
end
|
880
880
|
|
881
881
|
Note that it is often better to use a sum cache instead of this approach. You can implement
|
882
|
-
a sum cache using +after_create+, +after_update+, and +after_delete+ hooks, or
|
882
|
+
a sum cache using +after_create+, +after_update+, and +after_delete+ hooks, or preferably using a database trigger.
|
data/doc/association_basics.rdoc
CHANGED
@@ -113,7 +113,7 @@ many rows in the current table, by using a join table to associate the two table
|
|
113
113
|
The one_through_one association can be thought of as a subset of the many_to_many
|
114
114
|
association, but where there can only be 0 or 1 records in the associated table.
|
115
115
|
This is useful if there is a unique constraint on the foreign key in the join table
|
116
|
-
that
|
116
|
+
that references the current table. It's also useful if you want to impose an order
|
117
117
|
on the association and just want the first record returned. The one_through_one
|
118
118
|
association is so named because it sets up a one-to-one association through a
|
119
119
|
single join table.
|
@@ -781,7 +781,7 @@ Sequel is designed to be very flexible. If the default behavior of the
|
|
781
781
|
association modification methods isn't what you desire, you can override
|
782
782
|
the methods in your classes. However, you should be aware that for each
|
783
783
|
of the association modification methods described, there is a private
|
784
|
-
method that is
|
784
|
+
method that is preceded by an underscore that does the actual
|
785
785
|
modification. The public method without the underscore handles caching
|
786
786
|
and callbacks, and shouldn't be overridden by the user.
|
787
787
|
|
@@ -1069,7 +1069,7 @@ option of the first association, it doesn't attempt to merge them.
|
|
1069
1069
|
In addition to the options hash, the :clone option will copy a block argument
|
1070
1070
|
from the existing situation. If you want a cloned association to not have the
|
1071
1071
|
same block as the association you are cloning from, specify the block: nil option
|
1072
|
-
in
|
1072
|
+
in addition to the :clone option.
|
1073
1073
|
|
1074
1074
|
==== :dataset
|
1075
1075
|
|
data/doc/code_order.rdoc
CHANGED
@@ -7,7 +7,7 @@ this guide will be specific about which are strictly necessary.
|
|
7
7
|
|
8
8
|
== Require Sequel
|
9
9
|
|
10
|
-
This is sort of a no
|
10
|
+
This is sort of a no-brainer, but you need to require the library
|
11
11
|
first. This is a strict requirement, none of the other code can
|
12
12
|
be executed unless the library has been required first. Example:
|
13
13
|
|
@@ -70,7 +70,7 @@ copied into the subclass when model subclasses are created. Example:
|
|
70
70
|
== Load Model Classes
|
71
71
|
|
72
72
|
After you have established a database connection, and configured your
|
73
|
-
global model
|
73
|
+
global model configuration and global plugins, you can load your model
|
74
74
|
classes. It's recommended to have a separate file for each model class,
|
75
75
|
unless the model classes are very simple. Example:
|
76
76
|
|
@@ -91,6 +91,16 @@ unsafe runtime modification of the configuration:
|
|
91
91
|
model_classes.each(&:freeze)
|
92
92
|
DB.freeze
|
93
93
|
|
94
|
+
The `subclasses` plugin can be used to keep track of all model classes
|
95
|
+
that have been setup in your application. Finalizing their associations
|
96
|
+
and freezing them can easily be achieved through the plugin:
|
97
|
+
|
98
|
+
# Register the plugin before setting up the models
|
99
|
+
Sequel::Model.plugin :subclasses
|
100
|
+
# ... setup models
|
101
|
+
# Now finalize associations & freeze models by calling the plugin:
|
102
|
+
Sequel::Model.freeze_descendents
|
103
|
+
|
94
104
|
== Disconnect If Using Forking Webserver with Code Preloading
|
95
105
|
|
96
106
|
If you are using a forking webserver such as unicorn or passenger, with
|
@@ -4,7 +4,7 @@ How you design your model dataset methods can significantly affect the flexibili
|
|
4
4
|
|
5
5
|
== Flexibility: Use Single Method Per Task
|
6
6
|
|
7
|
-
In general, it is recommended that you have a single method per task for maximum
|
7
|
+
In general, it is recommended that you have a single method per task for maximum flexibility. For example, let's say you need to retrieve all albums released in a given year, ordered by number of units sold descending, and only care about the id, name and number of units sold. One way to do this is in your application code (outside the model), you can
|
8
8
|
call the dataset methods directly:
|
9
9
|
|
10
10
|
Album.
|
@@ -0,0 +1,46 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* A fiber_concurrency extension has been added, for using
|
4
|
+
Fiber.current instead of Thread.current when checking out a
|
5
|
+
connection. This allows separate fibers of the same thread
|
6
|
+
to use separate connections. In addition to allowing direct use
|
7
|
+
of fibers, this also allows concurrent use of multiple enumerators
|
8
|
+
that use database connections in the same thread.
|
9
|
+
|
10
|
+
When using this extension, you must be careful and ensure that you
|
11
|
+
are not using more concurrent fibers than your connection pool size.
|
12
|
+
Otherwise, all fibers will block while one fiber waits until a
|
13
|
+
connection is available. It is possible this issue will be
|
14
|
+
addressed when Ruby implements a fiber scheduler (currently
|
15
|
+
being discussed for inclusion in Ruby 3).
|
16
|
+
|
17
|
+
* A run_transaction_hooks Database extension has been added,
|
18
|
+
allowing for running the transaction hooks before commit/rollback,
|
19
|
+
which can be helpful for testing the hooks when using transactional
|
20
|
+
testing.
|
21
|
+
|
22
|
+
= Other Improvements
|
23
|
+
|
24
|
+
* Database#create_table? now works correctly with the :partition_of
|
25
|
+
option on PostgreSQL.
|
26
|
+
|
27
|
+
* The timestamp(N) with time zone type is now recognized by the
|
28
|
+
schema parser.
|
29
|
+
|
30
|
+
* Singleton methods of the Sequel module have now been moved into a
|
31
|
+
Sequel::SequelMethods module. This allows you to extend Sequel
|
32
|
+
with a module that overrides the methods and call super to get
|
33
|
+
the default behavior.
|
34
|
+
|
35
|
+
* The pg_inet extension no longer defines inet/cidr conversion procs
|
36
|
+
if sequel_pg 1.13.0+ is in use. This is because sequel_pg 1.13.0+
|
37
|
+
will respect the conversion procs and defining them makes things
|
38
|
+
slower. sequel_pg 1.13.0+ handles the same conversion by default
|
39
|
+
without needing a conversion proc.
|
40
|
+
|
41
|
+
* Method visibility issues in the model, plugin, extension, and adapter
|
42
|
+
code have been fixed. Most cases fixed were private methods being
|
43
|
+
accidentally made public when they were overridden.
|
44
|
+
|
45
|
+
During this change, Model#_insert_values was changed from public to
|
46
|
+
private, since it was originally intended to be private.
|
data/doc/testing.rdoc
CHANGED
@@ -162,6 +162,7 @@ SEQUEL_CONNECTION_VALIDATOR :: Use the connection validator extension when runni
|
|
162
162
|
SEQUEL_DUPLICATE_COLUMNS_HANDLER :: Use the duplicate columns handler extension with value given when running the specs
|
163
163
|
SEQUEL_ERROR_SQL :: Use the error_sql extension when running the specs
|
164
164
|
SEQUEL_INDEX_CACHING :: Use the index_caching extension when running the specs
|
165
|
+
SEQUEL_FIBER_CONCURRENCY :: Use the fiber_concurrency extension when running the adapter and integration specs
|
165
166
|
SEQUEL_FREEZE_DATABASE :: Freeze the database before running the integration specs
|
166
167
|
SEQUEL_IDENTIFIER_MANGLING :: Use the identifier_mangling extension when running the specs
|
167
168
|
SEQUEL_INTEGER64 :: Use the integer64 extension when running the adapter or integration specs
|
@@ -171,6 +171,12 @@ module Sequel
|
|
171
171
|
clone(:into => table)
|
172
172
|
end
|
173
173
|
|
174
|
+
# Access uses [] for quoting identifiers, and can't handle
|
175
|
+
# ] inside identifiers.
|
176
|
+
def quoted_identifier_append(sql, v)
|
177
|
+
sql << '[' << v.to_s << ']'
|
178
|
+
end
|
179
|
+
|
174
180
|
# Access does not support derived column lists.
|
175
181
|
def supports_derived_column_lists?
|
176
182
|
false
|
@@ -279,12 +285,6 @@ module Sequel
|
|
279
285
|
literal_append(sql, l)
|
280
286
|
end
|
281
287
|
end
|
282
|
-
|
283
|
-
# Access uses [] for quoting identifiers, and can't handle
|
284
|
-
# ] inside identifiers.
|
285
|
-
def quoted_identifier_append(sql, v)
|
286
|
-
sql << '[' << v.to_s << ']'
|
287
|
-
end
|
288
288
|
end
|
289
289
|
end
|
290
290
|
end
|
@@ -775,11 +775,6 @@ module Sequel
|
|
775
775
|
end
|
776
776
|
end
|
777
777
|
|
778
|
-
# MSSQL does not allow ordering in sub-clauses unless TOP (limit) is specified
|
779
|
-
def aggregate_dataset
|
780
|
-
(options_overlap(Sequel::Dataset::COUNT_FROM_SELF_OPTS) && !options_overlap([:limit])) ? unordered.from_self : super
|
781
|
-
end
|
782
|
-
|
783
778
|
# If the dataset using a order without a limit or offset or custom SQL,
|
784
779
|
# remove the order. Compounds on Microsoft SQL Server have undefined
|
785
780
|
# order unless the result is specifically ordered. Applying the current
|
@@ -799,6 +794,11 @@ module Sequel
|
|
799
794
|
|
800
795
|
private
|
801
796
|
|
797
|
+
# MSSQL does not allow ordering in sub-clauses unless TOP (limit) is specified
|
798
|
+
def aggregate_dataset
|
799
|
+
(options_overlap(Sequel::Dataset::COUNT_FROM_SELF_OPTS) && !options_overlap([:limit])) ? unordered.from_self : super
|
800
|
+
end
|
801
|
+
|
802
802
|
# Allow update and delete for unordered, limited datasets only.
|
803
803
|
def check_not_limited!(type)
|
804
804
|
return if @opts[:skip_limit_check] && type != :truncate
|
@@ -717,15 +717,6 @@ module Sequel
|
|
717
717
|
SQL::PlaceholderLiteralString.new((opts[:boolean] ? MATCH_AGAINST_BOOLEAN : MATCH_AGAINST), [Array(cols), terms])
|
718
718
|
end
|
719
719
|
|
720
|
-
# Transforms :straight to STRAIGHT_JOIN.
|
721
|
-
def join_type_sql(join_type)
|
722
|
-
if join_type == :straight
|
723
|
-
'STRAIGHT_JOIN'
|
724
|
-
else
|
725
|
-
super
|
726
|
-
end
|
727
|
-
end
|
728
|
-
|
729
720
|
# Sets up the insert methods to use INSERT IGNORE.
|
730
721
|
# Useful if you have a unique key and want to just skip
|
731
722
|
# inserting rows that violate the unique key restriction.
|
@@ -956,6 +947,15 @@ module Sequel
|
|
956
947
|
end
|
957
948
|
end
|
958
949
|
|
950
|
+
# Transforms :straight to STRAIGHT_JOIN.
|
951
|
+
def join_type_sql(join_type)
|
952
|
+
if join_type == :straight
|
953
|
+
'STRAIGHT_JOIN'
|
954
|
+
else
|
955
|
+
super
|
956
|
+
end
|
957
|
+
end
|
958
|
+
|
959
959
|
# MySQL allows a LIMIT in DELETE and UPDATE statements.
|
960
960
|
def limit_sql(sql)
|
961
961
|
if l = @opts[:limit]
|
@@ -422,22 +422,6 @@ module Sequel
|
|
422
422
|
end
|
423
423
|
end
|
424
424
|
|
425
|
-
def select_limit_sql(sql)
|
426
|
-
return unless supports_fetch_next_rows?
|
427
|
-
|
428
|
-
if offset = @opts[:offset]
|
429
|
-
sql << " OFFSET "
|
430
|
-
literal_append(sql, offset)
|
431
|
-
sql << " ROWS"
|
432
|
-
end
|
433
|
-
|
434
|
-
if limit = @opts[:limit]
|
435
|
-
sql << " FETCH NEXT "
|
436
|
-
literal_append(sql, limit)
|
437
|
-
sql << " ROWS ONLY"
|
438
|
-
end
|
439
|
-
end
|
440
|
-
|
441
425
|
# Oracle requires recursive CTEs to have column aliases.
|
442
426
|
def recursive_cte_requires_column_aliases?
|
443
427
|
true
|
@@ -624,6 +608,22 @@ module Sequel
|
|
624
608
|
:union
|
625
609
|
end
|
626
610
|
|
611
|
+
def select_limit_sql(sql)
|
612
|
+
return unless supports_fetch_next_rows?
|
613
|
+
|
614
|
+
if offset = @opts[:offset]
|
615
|
+
sql << " OFFSET "
|
616
|
+
literal_append(sql, offset)
|
617
|
+
sql << " ROWS"
|
618
|
+
end
|
619
|
+
|
620
|
+
if limit = @opts[:limit]
|
621
|
+
sql << " FETCH NEXT "
|
622
|
+
literal_append(sql, limit)
|
623
|
+
sql << " ROWS ONLY"
|
624
|
+
end
|
625
|
+
end
|
626
|
+
|
627
627
|
# Use SKIP LOCKED if skipping locked rows.
|
628
628
|
def select_lock_sql(sql)
|
629
629
|
super
|
@@ -459,6 +459,16 @@ module Sequel
|
|
459
459
|
super
|
460
460
|
end
|
461
461
|
|
462
|
+
# Support partitions of tables using the :partition_of option.
|
463
|
+
def create_table?(name, options=OPTS, &block)
|
464
|
+
if options[:partition_of]
|
465
|
+
create_table(name, options.merge!(:if_not_exists=>true), &block)
|
466
|
+
return
|
467
|
+
end
|
468
|
+
|
469
|
+
super
|
470
|
+
end
|
471
|
+
|
462
472
|
# Create a trigger in the database. Arguments:
|
463
473
|
# table :: the table on which this trigger operates
|
464
474
|
# name :: the name of this trigger
|
@@ -1896,6 +1906,14 @@ module Sequel
|
|
1896
1906
|
end
|
1897
1907
|
end
|
1898
1908
|
|
1909
|
+
def to_prepared_statement(type, *a)
|
1910
|
+
if type == :insert && !@opts.has_key?(:returning)
|
1911
|
+
returning(insert_pk).send(:to_prepared_statement, :insert_pk, *a)
|
1912
|
+
else
|
1913
|
+
super
|
1914
|
+
end
|
1915
|
+
end
|
1916
|
+
|
1899
1917
|
private
|
1900
1918
|
|
1901
1919
|
# Format TRUNCATE statement with PostgreSQL specific options.
|
@@ -2110,14 +2128,6 @@ module Sequel
|
|
2110
2128
|
true
|
2111
2129
|
end
|
2112
2130
|
|
2113
|
-
def to_prepared_statement(type, *a)
|
2114
|
-
if type == :insert && !@opts.has_key?(:returning)
|
2115
|
-
returning(insert_pk).send(:to_prepared_statement, :insert_pk, *a)
|
2116
|
-
else
|
2117
|
-
super
|
2118
|
-
end
|
2119
|
-
end
|
2120
|
-
|
2121
2131
|
# Concatenate the expressions with a space in between
|
2122
2132
|
def full_text_string_join(cols)
|
2123
2133
|
cols = Array(cols).map{|x| SQL::Function.new(:COALESCE, x, '')}
|
@@ -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.
|
@@ -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|
|
@@ -121,7 +121,7 @@ class Sequel::ShardedThreadedConnectionPool < Sequel::ThreadedConnectionPool
|
|
121
121
|
# connection can be acquired, a Sequel::PoolTimeout is raised.
|
122
122
|
def hold(server=:default)
|
123
123
|
server = pick_server(server)
|
124
|
-
t =
|
124
|
+
t = Sequel.current
|
125
125
|
if conn = owned_connection(t, server)
|
126
126
|
return yield(conn)
|
127
127
|
end
|
@@ -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
|
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:
|