sequel 5.31.0 → 5.32.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +16 -0
  3. data/doc/advanced_associations.rdoc +4 -4
  4. data/doc/association_basics.rdoc +3 -3
  5. data/doc/code_order.rdoc +12 -2
  6. data/doc/model_dataset_method_design.rdoc +1 -1
  7. data/doc/release_notes/5.32.0.txt +46 -0
  8. data/doc/testing.rdoc +1 -0
  9. data/lib/sequel/adapters/shared/access.rb +6 -6
  10. data/lib/sequel/adapters/shared/mssql.rb +5 -5
  11. data/lib/sequel/adapters/shared/mysql.rb +9 -9
  12. data/lib/sequel/adapters/shared/oracle.rb +16 -16
  13. data/lib/sequel/adapters/shared/postgres.rb +18 -8
  14. data/lib/sequel/adapters/shared/sqlanywhere.rb +9 -9
  15. data/lib/sequel/adapters/shared/sqlite.rb +1 -0
  16. data/lib/sequel/connection_pool/sharded_threaded.rb +2 -2
  17. data/lib/sequel/connection_pool/threaded.rb +1 -1
  18. data/lib/sequel/core.rb +318 -314
  19. data/lib/sequel/database/query.rb +1 -1
  20. data/lib/sequel/extensions/connection_expiration.rb +2 -2
  21. data/lib/sequel/extensions/connection_validator.rb +2 -2
  22. data/lib/sequel/extensions/fiber_concurrency.rb +24 -0
  23. data/lib/sequel/extensions/index_caching.rb +9 -7
  24. data/lib/sequel/extensions/integer64.rb +2 -0
  25. data/lib/sequel/extensions/pg_inet.rb +13 -5
  26. data/lib/sequel/extensions/pg_interval.rb +2 -0
  27. data/lib/sequel/extensions/pg_range.rb +2 -0
  28. data/lib/sequel/extensions/pg_timestamptz.rb +2 -0
  29. data/lib/sequel/extensions/run_transaction_hooks.rb +72 -0
  30. data/lib/sequel/extensions/server_block.rb +3 -3
  31. data/lib/sequel/model/base.rb +48 -47
  32. data/lib/sequel/model/plugins.rb +1 -0
  33. data/lib/sequel/plugins/association_lazy_eager_option.rb +2 -0
  34. data/lib/sequel/plugins/association_multi_add_remove.rb +2 -0
  35. data/lib/sequel/plugins/association_proxies.rb +2 -0
  36. data/lib/sequel/plugins/boolean_subsets.rb +4 -1
  37. data/lib/sequel/plugins/class_table_inheritance.rb +26 -26
  38. data/lib/sequel/plugins/dirty.rb +13 -13
  39. data/lib/sequel/plugins/json_serializer.rb +3 -7
  40. data/lib/sequel/plugins/single_table_inheritance.rb +15 -15
  41. data/lib/sequel/plugins/subclasses.rb +2 -0
  42. data/lib/sequel/timezones.rb +6 -4
  43. data/lib/sequel/version.rb +1 -1
  44. metadata +6 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f7419480f1c01043fb35c490c25cfbbcfaa9b5694ad41462f7af1c5e5e57bdf5
4
- data.tar.gz: 7fe799521a27993775f197f15cc38b780c04a3d8d4a9a8ef931da1bea2308779
3
+ metadata.gz: 5d15527276e1c30bbe39a7260589f3a1e027908dfa51b01e0c365e4162850605
4
+ data.tar.gz: 7ff61ff4f1af3ef3700c8a8bc15cda941f68eb42f19f28fc376126ebb1f9f9ed
5
5
  SHA512:
6
- metadata.gz: c3c37683caeb5390eaf614d481be330af8685a07250f4464487d45f0cc0758b94afc28c7008f6ba62f2945f92caf7dc83f2e43907d534e502d16c62e8b0d1c4d
7
- data.tar.gz: 49eb980c909fb2490b7e0ec808c0594e84f80593baed607917cd40ed99abab69fc42daf8e7fc5d4691785a0681fa1689c9e1c5130813c0d89cb5e5d638e60cd8
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 Descendents
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 descendents,
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 infinte loop, because when only the root node
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 preferrably using a database trigger.
882
+ a sum cache using +after_create+, +after_update+, and +after_delete+ hooks, or preferably using a database trigger.
@@ -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 refrences the current table. It's also useful if you want to impose an order
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 preceeded by an underscore that does the actual
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 additon to the :clone option.
1072
+ in addition to the :clone option.
1073
1073
 
1074
1074
  ==== :dataset
1075
1075
 
@@ -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 brainer, but you need to require the library
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 configration and global plugins, you can load your 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 flexibilty. 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
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.
@@ -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.
@@ -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
 
@@ -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 = Thread.current
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 = Thread.current
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 = Thread.current
86
+ t = Sequel.current
87
87
  if conn = owned_connection(t)
88
88
  return yield(conn)
89
89
  end
@@ -30,7 +30,15 @@ module Sequel
30
30
  @split_symbols = false
31
31
  @single_threaded = false
32
32
 
33
- class << self
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
- # Returns true if the passed object could be a specifier of conditions, false otherwise.
69
- # Currently, Sequel considers hashes and arrays of two element arrays as
70
- # condition specifiers.
71
- #
72
- # Sequel.condition_specifier?({}) # => true
73
- # Sequel.condition_specifier?([[1, 2]]) # => true
74
- # Sequel.condition_specifier?([]) # => false
75
- # Sequel.condition_specifier?([1]) # => false
76
- # Sequel.condition_specifier?(1) # => false
77
- def self.condition_specifier?(obj)
78
- case obj
79
- when Hash
80
- true
81
- when Array
82
- !obj.empty? && !obj.is_a?(SQL::ValueList) && obj.all?{|i| i.is_a?(Array) && (i.length == 2)}
83
- else
84
- false
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
- # Frozen hash used as the default options hash for most options.
89
- OPTS = {}.freeze
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
- # Creates a new database object based on the supplied connection string
92
- # and optional arguments. The specified scheme determines the database
93
- # class used, and the rest of the string specifies the connection options.
94
- # For example:
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
- # Assume the core extensions are not loaded by default, if the core_extensions
125
- # extension is loaded, this will be overridden.
126
- def self.core_extensions?
127
- false
128
- end
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
- # Convert the +exception+ to the given class. The given class should be
131
- # <tt>Sequel::Error</tt> or a subclass. Returns an instance of +klass+ with
132
- # the message and backtrace of +exception+.
133
- def self.convert_exception_class(exception, klass)
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
- # Load all Sequel extensions given. Extensions are just files that exist under
142
- # <tt>sequel/extensions</tt> in the load path, and are just required.
143
- # In some cases, requiring an extension modifies classes directly, and in others,
144
- # it just loads a module that you can extend other classes with. Consult the documentation
145
- # for each extension you plan on using for usage.
146
- #
147
- # Sequel.extension(:blank)
148
- # Sequel.extension(:core_extensions, :named_timezones)
149
- def self.extension(*extensions)
150
- extensions.each{|e| orig_require("sequel/extensions/#{e}")}
151
- end
152
-
153
- # The exception classed raised if there is an error parsing JSON.
154
- # This can be overridden to use an alternative json implementation.
155
- def self.json_parser_error_class
156
- JSON::ParserError
157
- end
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
- # Convert given object to json and return the result.
160
- # This can be overridden to use an alternative json implementation.
161
- def self.object_to_json(obj, *args, &block)
162
- obj.to_json(*args, &block)
163
- end
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
- # Parse the string as JSON and return the result.
166
- # This can be overridden to use an alternative json implementation.
167
- def self.parse_json(json)
168
- JSON.parse(json, :create_additions=>false)
169
- end
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
- # Convert each item in the array to the correct type, handling multi-dimensional
172
- # arrays. For each element in the array or subarrays, call the converter,
173
- # unless the value is nil.
174
- def self.recursive_map(array, converter)
175
- array.map do |i|
176
- if i.is_a?(Array)
177
- recursive_map(i, converter)
178
- elsif !i.nil?
179
- converter.call(i)
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
- SPLIT_SYMBOL_CACHE = {}
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
- # Splits the symbol into three parts, if symbol splitting is enabled (not the default).
193
- # Each part will either be a string or nil. If symbol splitting
194
- # is disabled, returns an array with the first and third parts
195
- # being nil, and the second part beind a string version of the symbol.
196
- #
197
- # For columns, these parts are the table, column, and alias.
198
- # For tables, these parts are the schema, table, and alias.
199
- def self.split_symbol(sym)
200
- unless v = Sequel.synchronize{SPLIT_SYMBOL_CACHE[sym]}
201
- if split_symbols?
202
- v = case s = sym.to_s
203
- when /\A((?:(?!__).)+)__((?:(?!___).)+)___(.+)\z/
204
- [$1.freeze, $2.freeze, $3.freeze].freeze
205
- when /\A((?:(?!___).)+)___(.+)\z/
206
- [nil, $1.freeze, $2.freeze].freeze
207
- when /\A((?:(?!__).)+)__(.+)\z/
208
- [$1.freeze, $2.freeze, nil].freeze
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, s.freeze, nil].freeze
220
+ v = [nil,sym.to_s.freeze,nil].freeze
211
221
  end
212
- else
213
- v = [nil,sym.to_s.freeze,nil].freeze
222
+ Sequel.synchronize{SPLIT_SYMBOL_CACHE[sym] = v}
214
223
  end
215
- Sequel.synchronize{SPLIT_SYMBOL_CACHE[sym] = v}
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
- # Whether Sequel currently splits symbols into qualified/aliased identifiers.
256
- def self.split_symbols?
257
- @split_symbols
258
- end
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
- # Converts the given +string+ into a +Date+ object.
261
- #
262
- # Sequel.string_to_date('2010-09-10') # Date.civil(2010, 09, 10)
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
- # Converts the given +string+ into a +Time+ or +DateTime+ object, depending on the
272
- # value of <tt>Sequel.datetime_class</tt>.
273
- #
274
- # Sequel.string_to_datetime('2010-09-10 10:20:30') # Time.local(2010, 09, 10, 10, 20, 30)
275
- def self.string_to_datetime(string)
276
- begin
277
- if datetime_class == DateTime
278
- DateTime.parse(string, convert_two_digit_years)
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
- # Converts the given +string+ into a <tt>Sequel::SQLTime</tt> object.
288
- #
289
- # v = Sequel.string_to_time('10:20:30') # Sequel::SQLTime.parse('10:20:30')
290
- # DB.literal(v) # => '10:20:30'
291
- def self.string_to_time(string)
292
- begin
293
- SQLTime.parse(string)
294
- rescue => e
295
- raise convert_exception_class(e, InvalidValue)
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
- if RUBY_VERSION >= '2.1'
312
- # A timer object that can be passed to Sequel.elapsed_seconds_since
313
- # to return the number of seconds elapsed.
314
- def self.start_timer
315
- Process.clock_gettime(Process::CLOCK_MONOTONIC)
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
- else
318
- # :nocov:
319
- def self.start_timer # :nodoc:
320
- Time.now
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
- # The elapsed seconds since the given timer object was created. The
326
- # timer object should have been created via Sequel.start_timer.
327
- def self.elapsed_seconds_since(timer)
328
- start_timer - timer
329
- end
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
- # Uses a transaction on all given databases with the given options. This:
332
- #
333
- # Sequel.transaction([DB1, DB2, DB3]){}
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
- pr = dbs.reverse.inject(block){|bl, db| proc{db.transaction(opts, &bl)}}
359
- if rescue_rollback
360
- begin
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
- # If the supplied block takes a single argument,
371
- # yield an <tt>SQL::VirtualRow</tt> instance to the block
372
- # argument. Otherwise, evaluate the block in the context of a
373
- # <tt>SQL::VirtualRow</tt> instance.
374
- #
375
- # Sequel.virtual_row{a} # Sequel::SQL::Identifier.new(:a)
376
- # Sequel.virtual_row{|o| o.a} # Sequel::SQL::Function.new(:a)
377
- def self.virtual_row(&block)
378
- vr = VIRTUAL_ROW
379
- case block.arity
380
- when -1, 0
381
- vr.instance_exec(&block)
382
- else
383
- block.call(vr)
384
- end
385
- end
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
- ### Private Class Methods ###
391
+ private
388
392
 
389
- # Helper method that the database adapter class methods that are added to Sequel via
390
- # metaprogramming use to parse arguments.
391
- def self.adapter_method(adapter, *args, &block)
392
- options = args.last.is_a?(Hash) ? args.pop : OPTS
393
- opts = {:adapter => adapter.to_sym}
394
- opts[:database] = args.shift if args.first.is_a?(String)
395
- if args.any?
396
- raise ::Sequel::Error, "Wrong format of arguments, either use (), (String), (Hash), or (String, Hash)"
397
- end
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
- connect(opts.merge(options), &block)
400
- end
403
+ connect(opts.merge(options), &block)
404
+ end
401
405
 
402
- # Method that adds a database adapter class method to Sequel that calls
403
- # Sequel.adapter_method.
404
- def self.def_adapter_method(*adapters) # :nodoc:
405
- adapters.each do |adapter|
406
- define_singleton_method(adapter){|*args, &block| adapter_method(adapter, *args, &block)}
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
- private_class_method :adapter_method, :def_adapter_method
411
-
412
- require_relative "deprecated"
413
- require_relative "sql"
414
- require_relative "connection_pool"
415
- require_relative "exceptions"
416
- require_relative "dataset"
417
- require_relative "database"
418
- require_relative "timezones"
419
- require_relative "ast_transformer"
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: