sequel 4.10.0 → 4.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG +58 -0
- data/doc/association_basics.rdoc +1 -1
- data/doc/cheat_sheet.rdoc +0 -1
- data/doc/core_extensions.rdoc +2 -2
- data/doc/dataset_filtering.rdoc +5 -5
- data/doc/model_hooks.rdoc +9 -0
- data/doc/object_model.rdoc +7 -13
- data/doc/opening_databases.rdoc +3 -1
- data/doc/querying.rdoc +8 -8
- data/doc/release_notes/4.11.0.txt +147 -0
- data/doc/sql.rdoc +11 -7
- data/doc/virtual_rows.rdoc +4 -5
- data/lib/sequel/adapters/ibmdb.rb +24 -16
- data/lib/sequel/adapters/jdbc/h2.rb +5 -0
- data/lib/sequel/adapters/jdbc/hsqldb.rb +5 -0
- data/lib/sequel/adapters/mock.rb +14 -2
- data/lib/sequel/adapters/shared/access.rb +6 -9
- data/lib/sequel/adapters/shared/cubrid.rb +5 -0
- data/lib/sequel/adapters/shared/db2.rb +5 -0
- data/lib/sequel/adapters/shared/firebird.rb +5 -0
- data/lib/sequel/adapters/shared/mssql.rb +23 -16
- data/lib/sequel/adapters/shared/mysql.rb +12 -2
- data/lib/sequel/adapters/shared/oracle.rb +31 -15
- data/lib/sequel/adapters/shared/postgres.rb +28 -4
- data/lib/sequel/adapters/shared/sqlanywhere.rb +5 -0
- data/lib/sequel/adapters/shared/sqlite.rb +12 -1
- data/lib/sequel/ast_transformer.rb +9 -7
- data/lib/sequel/connection_pool.rb +10 -4
- data/lib/sequel/database/features.rb +15 -0
- data/lib/sequel/database/schema_generator.rb +2 -2
- data/lib/sequel/database/schema_methods.rb +21 -3
- data/lib/sequel/database/transactions.rb +8 -4
- data/lib/sequel/dataset/actions.rb +13 -7
- data/lib/sequel/dataset/features.rb +7 -0
- data/lib/sequel/dataset/query.rb +28 -11
- data/lib/sequel/dataset/sql.rb +90 -14
- data/lib/sequel/extensions/constraint_validations.rb +2 -2
- data/lib/sequel/extensions/date_arithmetic.rb +1 -1
- data/lib/sequel/extensions/eval_inspect.rb +12 -6
- data/lib/sequel/extensions/pg_array_ops.rb +11 -2
- data/lib/sequel/extensions/pg_json.rb +130 -23
- data/lib/sequel/extensions/pg_json_ops.rb +196 -28
- data/lib/sequel/extensions/to_dot.rb +5 -7
- data/lib/sequel/model/associations.rb +0 -50
- data/lib/sequel/plugins/class_table_inheritance.rb +49 -21
- data/lib/sequel/plugins/many_through_many.rb +10 -11
- data/lib/sequel/plugins/serialization.rb +4 -1
- data/lib/sequel/plugins/sharding.rb +0 -9
- data/lib/sequel/plugins/single_table_inheritance.rb +4 -2
- data/lib/sequel/plugins/timestamps.rb +2 -2
- data/lib/sequel/sql.rb +166 -44
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +199 -133
- data/spec/core/connection_pool_spec.rb +6 -0
- data/spec/core/database_spec.rb +12 -0
- data/spec/core/dataset_spec.rb +58 -3
- data/spec/core/expression_filters_spec.rb +67 -5
- data/spec/core/mock_adapter_spec.rb +8 -4
- data/spec/core/schema_spec.rb +7 -0
- data/spec/core_extensions_spec.rb +14 -0
- data/spec/extensions/class_table_inheritance_spec.rb +23 -3
- data/spec/extensions/core_refinements_spec.rb +14 -0
- data/spec/extensions/eval_inspect_spec.rb +8 -4
- data/spec/extensions/pg_array_ops_spec.rb +6 -0
- data/spec/extensions/pg_json_ops_spec.rb +99 -0
- data/spec/extensions/pg_json_spec.rb +104 -4
- data/spec/extensions/serialization_spec.rb +19 -0
- data/spec/extensions/single_table_inheritance_spec.rb +11 -3
- data/spec/extensions/timestamps_spec.rb +10 -0
- data/spec/extensions/to_dot_spec.rb +8 -4
- data/spec/integration/database_test.rb +1 -1
- data/spec/integration/dataset_test.rb +9 -0
- data/spec/integration/schema_test.rb +27 -0
- metadata +4 -2
@@ -92,6 +92,7 @@ module Sequel
|
|
92
92
|
dot "AliasedExpression"
|
93
93
|
v(e.expression, :expression)
|
94
94
|
v(e.alias, :alias)
|
95
|
+
v(e.columns, :columns) if e.columns
|
95
96
|
when SQL::CaseExpression
|
96
97
|
dot "CaseExpression"
|
97
98
|
v(e.expression, :expression) if e.expression
|
@@ -102,18 +103,16 @@ module Sequel
|
|
102
103
|
v(e.expr, :expr)
|
103
104
|
v(e.type, :type)
|
104
105
|
when SQL::Function
|
105
|
-
dot "Function: #{e.
|
106
|
+
dot "Function: #{e.name}"
|
106
107
|
e.args.each_with_index do |val, j|
|
107
108
|
v(val, j)
|
108
109
|
end
|
110
|
+
v(e.args, :args)
|
111
|
+
v(e.opts, :opts)
|
109
112
|
when SQL::Subscript
|
110
113
|
dot "Subscript"
|
111
114
|
v(e.f, :f)
|
112
115
|
v(e.sub, :sub)
|
113
|
-
when SQL::WindowFunction
|
114
|
-
dot "WindowFunction"
|
115
|
-
v(e.function, :function)
|
116
|
-
v(e.window, :window)
|
117
116
|
when SQL::Window
|
118
117
|
dot "Window"
|
119
118
|
v(e.opts, :opts)
|
@@ -130,8 +129,7 @@ module Sequel
|
|
130
129
|
str << " USING"
|
131
130
|
end
|
132
131
|
dot str
|
133
|
-
v(e.
|
134
|
-
v(e.table_alias, :alias) if e.table_alias
|
132
|
+
v(e.table_expr, :table)
|
135
133
|
if e.is_a?(SQL::JoinOnClause)
|
136
134
|
v(e.on, :on)
|
137
135
|
elsif e.is_a?(SQL::JoinUsingClause)
|
@@ -1350,20 +1350,6 @@ module Sequel
|
|
1350
1350
|
association_reflections.values
|
1351
1351
|
end
|
1352
1352
|
|
1353
|
-
# REMOVE410
|
1354
|
-
def apply_association_dataset_opts(opts, ds)
|
1355
|
-
Deprecation.deprecate("Model.apply_association_dataset_opts/Model.eager_loading_dataset", "Use AssociationReflection#apply_dataset_changes/Association#reflection#apply_eager_dataset_changes instead.")
|
1356
|
-
ds = ds.select(*opts.select) if opts.select
|
1357
|
-
if c = opts[:conditions]
|
1358
|
-
ds = (c.is_a?(Array) && !Sequel.condition_specifier?(c)) ? ds.where(*c) : ds.where(c)
|
1359
|
-
end
|
1360
|
-
ds = ds.order(*opts[:order]) if opts[:order]
|
1361
|
-
ds = ds.eager(opts[:eager]) if opts[:eager]
|
1362
|
-
ds = ds.distinct if opts[:distinct]
|
1363
|
-
ds = opts[:eager_block].call(ds) if opts[:eager_block]
|
1364
|
-
ds
|
1365
|
-
end
|
1366
|
-
|
1367
1353
|
# Associates a related model with the current model. The following types are
|
1368
1354
|
# supported:
|
1369
1355
|
#
|
@@ -1646,22 +1632,6 @@ module Sequel
|
|
1646
1632
|
opts.eager_load_results(eo, &block)
|
1647
1633
|
end
|
1648
1634
|
|
1649
|
-
# REMOVE410
|
1650
|
-
def eager_loading_dataset(opts, ds, select, associations, eager_options=OPTS)
|
1651
|
-
ds = apply_association_dataset_opts(opts, ds)
|
1652
|
-
ds = ds.select(*select) if select
|
1653
|
-
if opts[:eager_graph]
|
1654
|
-
raise(Error, "cannot eagerly load a #{opts[:type]} association that uses :eager_graph") if opts.eager_loading_use_associated_key?
|
1655
|
-
ds = ds.eager_graph(opts[:eager_graph])
|
1656
|
-
end
|
1657
|
-
ds = ds.eager(associations) unless Array(associations).empty?
|
1658
|
-
ds = eager_options[:eager_block].call(ds) if eager_options[:eager_block]
|
1659
|
-
if opts.eager_loading_use_associated_key?
|
1660
|
-
ds = ds.select_append(*opts.associated_key_array)
|
1661
|
-
end
|
1662
|
-
ds
|
1663
|
-
end
|
1664
|
-
|
1665
1635
|
# Shortcut for adding a many_to_many association, see #associate
|
1666
1636
|
def many_to_many(name, opts=OPTS, &block)
|
1667
1637
|
associate(:many_to_many, name, opts, &block)
|
@@ -1711,19 +1681,6 @@ module Sequel
|
|
1711
1681
|
association_module(opts).send(:private, name)
|
1712
1682
|
end
|
1713
1683
|
|
1714
|
-
# REMOVE410
|
1715
|
-
def def_add_method(opts)
|
1716
|
-
Deprecation.deprecate("Model.def_add_method", "The Model.associate method now sets up the add method you if an :adder association reflection entry is present.")
|
1717
|
-
association_module_def(opts.add_method, opts){|o,*args| add_associated_object(opts, o, *args)}
|
1718
|
-
end
|
1719
|
-
|
1720
|
-
# REMOVE410
|
1721
|
-
def def_association_dataset_methods(opts)
|
1722
|
-
Deprecation.deprecate("Model.def_association_dataset_methods", "The Model.associate method now sets up the association dataset methods.")
|
1723
|
-
association_module_def(opts.dataset_method, opts){_dataset(opts)}
|
1724
|
-
def_association_method(opts)
|
1725
|
-
end
|
1726
|
-
|
1727
1684
|
# Adds the association method to the association methods module.
|
1728
1685
|
def def_association_method(opts)
|
1729
1686
|
association_module_def(opts.association_method, opts){|*dynamic_opts, &block| load_associated_objects(opts, dynamic_opts[0], &block)}
|
@@ -1996,13 +1953,6 @@ module Sequel
|
|
1996
1953
|
def_one_to_many(opts)
|
1997
1954
|
end
|
1998
1955
|
|
1999
|
-
# REMOVE410
|
2000
|
-
def def_remove_methods(opts)
|
2001
|
-
Deprecation.deprecate("Model.def_remove_methods", "The Model.associate method now sets up the remove/remove_all methods for you if a :remover or :clearer association reflection entry is present.")
|
2002
|
-
association_module_def(opts.remove_method, opts){|o,*args| remove_associated_object(opts, o, *args)}
|
2003
|
-
association_module_def(opts.remove_all_method, opts){|*args| remove_all_associated_objects(opts, *args)}
|
2004
|
-
end
|
2005
|
-
|
2006
1956
|
# Return dataset to graph into given the association reflection, applying the :callback option if set.
|
2007
1957
|
def eager_graph_dataset(opts, eager_options)
|
2008
1958
|
ds = opts.associated_class.dataset
|
@@ -71,7 +71,9 @@ module Sequel
|
|
71
71
|
# # You can also set options when loading the plugin:
|
72
72
|
# # :kind :: column to hold the class name
|
73
73
|
# # :table_map :: map of class name symbols to table name symbols
|
74
|
-
#
|
74
|
+
# # :model_map :: map of column values to class name symbols
|
75
|
+
# Employee.plugin :class_table_inheritance, :key=>:kind, :table_map=>{:Staff=>:staff},
|
76
|
+
# :model_map=>{1=>:Employee, 2=>:Manager, 3=>:Executive, 4=>:Staff}
|
75
77
|
module ClassTableInheritance
|
76
78
|
# The class_table_inheritance plugin requires the lazy_attributes plugin
|
77
79
|
# to handle lazily-loaded attributes for subclass instances returned
|
@@ -83,25 +85,26 @@ module Sequel
|
|
83
85
|
# Initialize the per-model data structures and set the dataset's row_proc
|
84
86
|
# to check for the :key option column for the type of class when loading objects.
|
85
87
|
# Options:
|
86
|
-
#
|
87
|
-
#
|
88
|
-
#
|
89
|
-
#
|
90
|
-
#
|
91
|
-
#
|
88
|
+
# :key :: The column symbol holding the name of the model class this
|
89
|
+
# is an instance of. Necessary if you want to call model methods
|
90
|
+
# using the superclass, but have them return subclass instances.
|
91
|
+
# :table_map :: Hash with class name symbol keys and table name symbol
|
92
|
+
# values. Necessary if the implicit table name for the model class
|
93
|
+
# does not match the database table name
|
94
|
+
# :model_map :: Hash with keys being values of the cti_key column, and values
|
95
|
+
# being class name strings or symbols. Used if you don't want to
|
96
|
+
# store class names in the database. If you use this option, you
|
97
|
+
# are responsible for setting the values of the cti_key column
|
98
|
+
# manually (usually in a before_create hook).
|
92
99
|
def self.configure(model, opts=OPTS)
|
93
100
|
model.instance_eval do
|
94
|
-
m = method(:constantize)
|
95
101
|
@cti_base_model = self
|
96
102
|
@cti_key = key = opts[:key]
|
97
103
|
@cti_tables = [table_name]
|
98
104
|
@cti_columns = {table_name=>columns}
|
99
105
|
@cti_table_map = opts[:table_map] || {}
|
100
|
-
|
101
|
-
|
102
|
-
else
|
103
|
-
model
|
104
|
-
end
|
106
|
+
@cti_model_map = opts[:model_map]
|
107
|
+
set_dataset_cti_row_proc
|
105
108
|
end
|
106
109
|
end
|
107
110
|
|
@@ -120,6 +123,11 @@ module Sequel
|
|
120
123
|
# load method.
|
121
124
|
attr_reader :cti_key
|
122
125
|
|
126
|
+
# A hash with keys being values of the cti_key column, and values
|
127
|
+
# being class name strings or symbols. Used if you don't want to
|
128
|
+
# store class names in the database.
|
129
|
+
attr_reader :cti_model_map
|
130
|
+
|
123
131
|
# An array of table symbols that back this model. The first is
|
124
132
|
# cti_base_model table symbol, and the last is the current model
|
125
133
|
# table symbol.
|
@@ -139,6 +147,7 @@ module Sequel
|
|
139
147
|
ct = cti_tables.dup
|
140
148
|
ctm = cti_table_map.dup
|
141
149
|
cbm = cti_base_model
|
150
|
+
cmm = cti_model_map
|
142
151
|
pk = primary_key
|
143
152
|
ds = dataset
|
144
153
|
subclass.instance_eval do
|
@@ -150,6 +159,7 @@ module Sequel
|
|
150
159
|
@cti_columns = cc.merge(table=>columns)
|
151
160
|
@cti_table_map = ctm
|
152
161
|
@cti_base_model = cbm
|
162
|
+
@cti_model_map = cmm
|
153
163
|
# Need to set dataset and columns before calling super so that
|
154
164
|
# the main column accessor module is included in the class before any
|
155
165
|
# plugin accessor modules (such as the lazy attributes accessor module).
|
@@ -158,12 +168,7 @@ module Sequel
|
|
158
168
|
end
|
159
169
|
super
|
160
170
|
subclass.instance_eval do
|
161
|
-
|
162
|
-
dataset.row_proc = if cti_key
|
163
|
-
lambda{|r| (m.call(r[ck]) rescue subclass).call(r)}
|
164
|
-
else
|
165
|
-
subclass
|
166
|
-
end
|
171
|
+
set_dataset_cti_row_proc
|
167
172
|
(columns - [cbm.primary_key]).each{|a| define_lazy_attribute_getter(a)}
|
168
173
|
cti_tables.reverse.each do |table|
|
169
174
|
db.schema(table).each{|k,v| db_schema[k] = v}
|
@@ -192,12 +197,35 @@ module Sequel
|
|
192
197
|
ds.row_proc = @dataset.row_proc if @dataset
|
193
198
|
end
|
194
199
|
|
200
|
+
# Set the row_proc for the model's dataset appropriately
|
201
|
+
# based on the cti key and model map.
|
202
|
+
def set_dataset_cti_row_proc
|
203
|
+
m = method(:constantize)
|
204
|
+
dataset.row_proc = if ck = cti_key
|
205
|
+
if model_map = cti_model_map
|
206
|
+
lambda do |r|
|
207
|
+
mod = if name = model_map[r[ck]]
|
208
|
+
m.call(name)
|
209
|
+
else
|
210
|
+
self
|
211
|
+
end
|
212
|
+
mod.call(r)
|
213
|
+
end
|
214
|
+
else
|
215
|
+
lambda{|r| (m.call(r[ck]) rescue self).call(r)}
|
216
|
+
end
|
217
|
+
else
|
218
|
+
self
|
219
|
+
end
|
220
|
+
end
|
195
221
|
end
|
196
222
|
|
197
223
|
module InstanceMethods
|
198
224
|
# Set the cti_key column to the name of the model.
|
199
|
-
def
|
200
|
-
|
225
|
+
def before_validation
|
226
|
+
if new? && model.cti_key && !model.cti_model_map
|
227
|
+
send("#{model.cti_key}=", model.name.to_s)
|
228
|
+
end
|
201
229
|
super
|
202
230
|
end
|
203
231
|
|
@@ -9,14 +9,13 @@ module Sequel
|
|
9
9
|
# The many_through_many plugin would allow this:
|
10
10
|
#
|
11
11
|
# Artist.plugin :many_through_many
|
12
|
-
# Artist.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:
|
12
|
+
# Artist.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums_tags, :album_id, :tag_id]]
|
13
13
|
#
|
14
14
|
# Which will give you the tags for all of the artist's albums.
|
15
15
|
#
|
16
16
|
# Let's break down the 2nd argument of the many_through_many call:
|
17
17
|
#
|
18
18
|
# [[:albums_artists, :artist_id, :album_id],
|
19
|
-
# [:albums, :id, :id],
|
20
19
|
# [:albums_tags, :album_id, :tag_id]]
|
21
20
|
#
|
22
21
|
# This argument is an array of arrays with three elements. Each entry in the main array represents a JOIN in SQL:
|
@@ -29,12 +28,12 @@ module Sequel
|
|
29
28
|
#
|
30
29
|
# FROM artists
|
31
30
|
# JOIN albums_artists ON (artists.id = albums_artists.artist_id)
|
32
|
-
# JOIN
|
33
|
-
# JOIN albums_tags ON (albums.id = albums_tag.album_id)
|
31
|
+
# JOIN albums_tags ON (albums_artists.album_id = albums_tag.album_id)
|
34
32
|
# JOIN tags ON (albums_tags.tag_id = tags.id)
|
35
33
|
#
|
36
34
|
# The "artists.id" and "tags.id" criteria come from other association options (defaulting to the primary keys of the current and
|
37
|
-
# associated tables), but hopefully you can see how each argument in the array is used in the JOIN clauses.
|
35
|
+
# associated tables), but hopefully you can see how each argument in the array is used in the JOIN clauses. Note that you do
|
36
|
+
# not need to add an entry for the final table (tags in this example), as that comes from the associated class.
|
38
37
|
#
|
39
38
|
# Here are some more examples:
|
40
39
|
#
|
@@ -42,20 +41,20 @@ module Sequel
|
|
42
41
|
# Artist.many_through_many :albums, [[:albums_artists, :artist_id, :album_id]]
|
43
42
|
#
|
44
43
|
# # All artists that are associated to any album that this artist is associated to
|
45
|
-
# Artist.many_through_many :artists, [[:albums_artists, :artist_id, :album_id], [:
|
44
|
+
# Artist.many_through_many :artists, [[:albums_artists, :artist_id, :album_id], [:albums_artists, :album_id, :artist_id]]
|
46
45
|
#
|
47
46
|
# # All albums by artists that are associated to any album that this artist is associated to
|
48
|
-
# Artist.many_through_many :artist_albums, [[:albums_artists, :artist_id, :album_id],
|
49
|
-
# [:albums_artists, :album_id, :artist_id], [:
|
47
|
+
# Artist.many_through_many :artist_albums, [[:albums_artists, :artist_id, :album_id], \
|
48
|
+
# [:albums_artists, :album_id, :artist_id], [:albums_artists, :artist_id, :album_id]], \
|
50
49
|
# :class=>:Album
|
51
50
|
#
|
52
|
-
# # All tracks on albums by this artist
|
53
|
-
# Artist.many_through_many :tracks, [[:albums_artists, :artist_id, :album_id]
|
51
|
+
# # All tracks on albums by this artist (also could be a many_to_many)
|
52
|
+
# Artist.many_through_many :tracks, [[:albums_artists, :artist_id, :album_id]], \
|
54
53
|
# :right_primary_key=>:album_id
|
55
54
|
#
|
56
55
|
# Often you don't want the current object to appear in the array of associated objects. This is easiest to handle via an :after_load hook:
|
57
56
|
#
|
58
|
-
# Artist.many_through_many :artists, [[:albums_artists, :artist_id, :album_id], [:
|
57
|
+
# Artist.many_through_many :artists, [[:albums_artists, :artist_id, :album_id], [:albums_artists, :album_id, :artist_id]],
|
59
58
|
# :after_load=>proc{|artist, associated_artists| associated_artists.delete(artist)}
|
60
59
|
#
|
61
60
|
# You can also handle it by adding a dataset block that excludes the current record (so it won't be retrieved at all), but
|
@@ -163,7 +163,10 @@ module Sequel
|
|
163
163
|
end
|
164
164
|
end
|
165
165
|
define_method("#{column}=") do |v|
|
166
|
-
|
166
|
+
if !changed_columns.include?(column) && (new? || send(column) != v)
|
167
|
+
changed_columns << column
|
168
|
+
end
|
169
|
+
|
167
170
|
deserialized_values[column] = v
|
168
171
|
end
|
169
172
|
end
|
@@ -39,15 +39,6 @@ module Sequel
|
|
39
39
|
super
|
40
40
|
end
|
41
41
|
|
42
|
-
# REMOVE410
|
43
|
-
def eager_loading_dataset(opts, ds, select, associations, eager_options=OPTS)
|
44
|
-
ds = super(opts, ds, select, associations, eager_options)
|
45
|
-
if !ds.opts[:server] and s = eager_options[:self] and server = s.opts[:server]
|
46
|
-
ds = ds.server(server)
|
47
|
-
end
|
48
|
-
ds
|
49
|
-
end
|
50
|
-
|
51
42
|
# Return a newly instantiated object that is tied to the given
|
52
43
|
# shard s. When the object is saved, a record will be inserted
|
53
44
|
# on shard s.
|
@@ -216,8 +216,10 @@ module Sequel
|
|
216
216
|
|
217
217
|
module InstanceMethods
|
218
218
|
# Set the sti_key column based on the sti_key_map.
|
219
|
-
def
|
220
|
-
|
219
|
+
def before_validation
|
220
|
+
if new? && !self[model.sti_key]
|
221
|
+
send("#{model.sti_key}=", model.sti_key_chooser.call(self))
|
222
|
+
end
|
221
223
|
super
|
222
224
|
end
|
223
225
|
end
|
data/lib/sequel/sql.rb
CHANGED
@@ -251,8 +251,9 @@ module Sequel
|
|
251
251
|
# Create an SQL alias (+AliasedExpression+) of the receiving column or expression to the given alias.
|
252
252
|
#
|
253
253
|
# Sequel.function(:func).as(:alias) # func() AS "alias"
|
254
|
-
|
255
|
-
|
254
|
+
# Sequel.function(:func).as(:alias, [:col_alias1, :col_alias2]) # func() AS "alias"("col_alias1", "col_alias2")
|
255
|
+
def as(aliaz, columns=nil)
|
256
|
+
AliasedExpression.new(self, aliaz, columns)
|
256
257
|
end
|
257
258
|
end
|
258
259
|
|
@@ -320,8 +321,9 @@ module Sequel
|
|
320
321
|
# Create an SQL::AliasedExpression for the given expression and alias.
|
321
322
|
#
|
322
323
|
# Sequel.as(:column, :alias) # "column" AS "alias"
|
323
|
-
|
324
|
-
|
324
|
+
# Sequel.as(:column, :alias, [:col_alias1, :col_alias2]) # "column" AS "alias"("col_alias1", "col_alias2")
|
325
|
+
def as(exp, aliaz, columns=nil)
|
326
|
+
SQL::AliasedExpression.new(exp, aliaz, columns)
|
325
327
|
end
|
326
328
|
|
327
329
|
# Order the given argument ascending.
|
@@ -391,7 +393,7 @@ module Sequel
|
|
391
393
|
# Sequel.char_length(:a) # char_length(a) -- Most databases
|
392
394
|
# Sequel.char_length(:a) # length(a) -- SQLite
|
393
395
|
def char_length(arg)
|
394
|
-
SQL::
|
396
|
+
SQL::Function.new!(:char_length, [arg], :emulate=>true)
|
395
397
|
end
|
396
398
|
|
397
399
|
# Do a deep qualification of the argument using the qualifier. This recurses into
|
@@ -555,7 +557,7 @@ module Sequel
|
|
555
557
|
# Create a <tt>BooleanExpression</tt> case insensitive (if the database supports it) pattern match of the receiver with
|
556
558
|
# the given patterns. See <tt>SQL::StringExpression.like</tt>.
|
557
559
|
#
|
558
|
-
# Sequel.ilike(:a, 'A%') # "a" ILIKE 'A%'
|
560
|
+
# Sequel.ilike(:a, 'A%') # "a" ILIKE 'A%' ESCAPE '\'
|
559
561
|
def ilike(*args)
|
560
562
|
SQL::StringExpression.like(*(args << {:case_insensitive=>true}))
|
561
563
|
end
|
@@ -563,7 +565,7 @@ module Sequel
|
|
563
565
|
# Create a <tt>SQL::BooleanExpression</tt> case sensitive (if the database supports it) pattern match of the receiver with
|
564
566
|
# the given patterns. See <tt>SQL::StringExpression.like</tt>.
|
565
567
|
#
|
566
|
-
# Sequel.like(:a, 'A%') # "a" LIKE 'A%'
|
568
|
+
# Sequel.like(:a, 'A%') # "a" LIKE 'A%' ESCAPE '\'
|
567
569
|
def like(*args)
|
568
570
|
SQL::StringExpression.like(*args)
|
569
571
|
end
|
@@ -648,7 +650,7 @@ module Sequel
|
|
648
650
|
# Sequel.trim(:a) # trim(a) -- Most databases
|
649
651
|
# Sequel.trim(:a) # ltrim(rtrim(a)) -- Microsoft SQL Server
|
650
652
|
def trim(arg)
|
651
|
-
SQL::
|
653
|
+
SQL::Function.new!(:trim, [arg], :emulate=>true)
|
652
654
|
end
|
653
655
|
|
654
656
|
# Return a <tt>SQL::ValueList</tt> created from the given array. Used if the array contains
|
@@ -887,7 +889,7 @@ module Sequel
|
|
887
889
|
# Create a +BooleanExpression+ case insensitive pattern match of the receiver
|
888
890
|
# with the given patterns. See <tt>StringExpression.like</tt>.
|
889
891
|
#
|
890
|
-
# :a.ilike('A%') # "a" ILIKE 'A%'
|
892
|
+
# :a.ilike('A%') # "a" ILIKE 'A%' ESCAPE '\'
|
891
893
|
def ilike(*ces)
|
892
894
|
StringExpression.like(self, *(ces << {:case_insensitive=>true}))
|
893
895
|
end
|
@@ -895,7 +897,7 @@ module Sequel
|
|
895
897
|
# Create a +BooleanExpression+ case sensitive (if the database supports it) pattern match of the receiver with
|
896
898
|
# the given patterns. See <tt>StringExpression.like</tt>.
|
897
899
|
#
|
898
|
-
# :a.like('A%') # "a" LIKE 'A%'
|
900
|
+
# :a.like('A%') # "a" LIKE 'A%' ESCAPE '\'
|
899
901
|
def like(*ces)
|
900
902
|
StringExpression.like(self, *ces)
|
901
903
|
end
|
@@ -940,9 +942,15 @@ module Sequel
|
|
940
942
|
attr_reader :aliaz
|
941
943
|
alias_method :alias, :aliaz
|
942
944
|
|
945
|
+
# The columns aliases to use, for when the aliased expression is
|
946
|
+
# a record or set of records (such as a dataset).
|
947
|
+
attr_reader :columns
|
948
|
+
|
943
949
|
# Create an object with the given expression and alias.
|
944
|
-
def initialize(expression, aliaz)
|
945
|
-
@expression
|
950
|
+
def initialize(expression, aliaz, columns=nil)
|
951
|
+
@expression = expression
|
952
|
+
@aliaz = aliaz
|
953
|
+
@columns = columns
|
946
954
|
end
|
947
955
|
|
948
956
|
to_s_method :aliased_expression_sql
|
@@ -1222,44 +1230,122 @@ module Sequel
|
|
1222
1230
|
COMMA_ARRAY = [LiteralString.new(', ').freeze].freeze
|
1223
1231
|
|
1224
1232
|
# The SQL function to call
|
1225
|
-
attr_reader :
|
1233
|
+
attr_reader :name
|
1234
|
+
alias f name
|
1226
1235
|
|
1227
1236
|
# The array of arguments to pass to the function (may be blank)
|
1228
1237
|
attr_reader :args
|
1229
1238
|
|
1230
|
-
#
|
1231
|
-
|
1232
|
-
|
1239
|
+
# Options for this function
|
1240
|
+
attr_reader :opts
|
1241
|
+
|
1242
|
+
# Set the name and args for the function
|
1243
|
+
def initialize(name, *args)
|
1244
|
+
@name = name
|
1245
|
+
@args = args
|
1246
|
+
@opts = OPTS
|
1247
|
+
end
|
1248
|
+
|
1249
|
+
def self.new!(name, args, opts)
|
1250
|
+
f = new(name, *args)
|
1251
|
+
f.instance_variable_set(:@opts, opts)
|
1252
|
+
f
|
1233
1253
|
end
|
1234
1254
|
|
1235
1255
|
# If no arguments are given, return a new function with the wildcard prepended to the arguments.
|
1236
1256
|
#
|
1237
1257
|
# Sequel.function(:count).* # count(*)
|
1238
|
-
# Sequel.function(:count, 1).* # count(*, 1)
|
1239
1258
|
def *(ce=(arg=false;nil))
|
1240
1259
|
if arg == false
|
1241
|
-
|
1260
|
+
raise Error, "Cannot apply * to functions with arguments" unless args.empty?
|
1261
|
+
with_opts(:"*"=>true)
|
1242
1262
|
else
|
1243
1263
|
super(ce)
|
1244
1264
|
end
|
1245
1265
|
end
|
1246
1266
|
|
1247
1267
|
# Return a new function with DISTINCT before the method arguments.
|
1268
|
+
#
|
1269
|
+
# Sequel.function(:count, :col).distinct # count(DISTINCT col)
|
1248
1270
|
def distinct
|
1249
|
-
|
1271
|
+
with_opts(:distinct=>true)
|
1272
|
+
end
|
1273
|
+
|
1274
|
+
# Return a new function with FILTER added to it, for filtered
|
1275
|
+
# aggregate functions:
|
1276
|
+
#
|
1277
|
+
# Sequel.function(:foo, :col).filter(:a=>1) # foo(col) FILTER (WHERE a = 1)
|
1278
|
+
def filter(*args, &block)
|
1279
|
+
args = args.first if args.length == 1
|
1280
|
+
with_opts(:filter=>args, :filter_block=>block)
|
1281
|
+
end
|
1282
|
+
|
1283
|
+
# Return a function which will use LATERAL when literalized:
|
1284
|
+
#
|
1285
|
+
# Sequel.function(:foo, :col).lateral # LATERAL foo(col)
|
1286
|
+
def lateral
|
1287
|
+
with_opts(:lateral=>true)
|
1288
|
+
end
|
1289
|
+
|
1290
|
+
# Return a new function with an OVER clause (making it a window function).
|
1291
|
+
#
|
1292
|
+
# Sequel.function(:row_number).over(:partition=>:col) # row_number() OVER (PARTITION BY col)
|
1293
|
+
def over(window=OPTS)
|
1294
|
+
raise Error, "function already has a window applied to it" if opts[:over]
|
1295
|
+
window = Window.new(window) unless window.is_a?(Window)
|
1296
|
+
with_opts(:over=>window)
|
1297
|
+
end
|
1298
|
+
|
1299
|
+
# Return a new function where the function name will be quoted if the database supports
|
1300
|
+
# quoted functions:
|
1301
|
+
#
|
1302
|
+
# Sequel.function(:foo).quoted # "foo"()
|
1303
|
+
def quoted
|
1304
|
+
with_opts(:quoted=>true)
|
1305
|
+
end
|
1306
|
+
|
1307
|
+
# Return a new function where the function name will not be quoted even
|
1308
|
+
# if the database supports quoted functions:
|
1309
|
+
#
|
1310
|
+
# Sequel.expr(:foo).function.unquoted # foo()
|
1311
|
+
def unquoted
|
1312
|
+
with_opts(:quoted=>false)
|
1313
|
+
end
|
1314
|
+
|
1315
|
+
# Return a new function that will use WITH ORDINALITY to also return
|
1316
|
+
# a row number for every row the function returns:
|
1317
|
+
#
|
1318
|
+
# Sequel.function(:foo).with_ordinality # foo() WITH ORDINALITY
|
1319
|
+
def with_ordinality
|
1320
|
+
with_opts(:with_ordinality=>true)
|
1250
1321
|
end
|
1251
1322
|
|
1252
|
-
#
|
1253
|
-
|
1254
|
-
|
1323
|
+
# Return a new function that uses WITHIN GROUP ordered by the given expression,
|
1324
|
+
# useful for ordered-set and hypothetical-set aggregate functions:
|
1325
|
+
#
|
1326
|
+
# Sequel.function(:rank, :a).within_group(:b, :c)
|
1327
|
+
# # rank(a) WITHIN GROUP (ORDER BY b, c)
|
1328
|
+
def within_group(*expressions)
|
1329
|
+
with_opts(:within_group=>expressions)
|
1255
1330
|
end
|
1256
1331
|
|
1257
1332
|
to_s_method :function_sql
|
1333
|
+
|
1334
|
+
private
|
1335
|
+
|
1336
|
+
# Return a new function call with the given opts merged into the current opts.
|
1337
|
+
def with_opts(opts)
|
1338
|
+
self.class.new!(name, args, @opts.merge(opts))
|
1339
|
+
end
|
1258
1340
|
end
|
1259
1341
|
|
1260
|
-
#
|
1261
|
-
# on databases that lack support for such a function.
|
1342
|
+
# REMOVE411
|
1262
1343
|
class EmulatedFunction < Function
|
1344
|
+
def self.new(name, *args)
|
1345
|
+
Deprecation.deprecate("Sequel::SQL::EmulatedFunction", "Please use Sequel::SQL::Function.new!(name, args, :emulate=>true) to create an emulated SQL function")
|
1346
|
+
Function.new!(name, args, :emulate=>true)
|
1347
|
+
end
|
1348
|
+
|
1263
1349
|
to_s_method :emulated_function_sql
|
1264
1350
|
end
|
1265
1351
|
|
@@ -1303,15 +1389,48 @@ module Sequel
|
|
1303
1389
|
# The type of join to do
|
1304
1390
|
attr_reader :join_type
|
1305
1391
|
|
1306
|
-
# The
|
1307
|
-
|
1308
|
-
|
1309
|
-
# The table alias to use for the join, if any
|
1310
|
-
attr_reader :table_alias
|
1392
|
+
# The expression representing the table/set related to the JOIN.
|
1393
|
+
# Is an AliasedExpression if the JOIN uses an alias.
|
1394
|
+
attr_reader :table_expr
|
1311
1395
|
|
1312
|
-
# Create an object with the given join_type
|
1396
|
+
# Create an object with the given join_type and table expression.
|
1313
1397
|
def initialize(join_type, table, table_alias = nil)
|
1314
|
-
@join_type
|
1398
|
+
@join_type = join_type
|
1399
|
+
|
1400
|
+
@table_expr = if table.is_a?(AliasedExpression)
|
1401
|
+
table
|
1402
|
+
# REMOVE411
|
1403
|
+
elsif table_alias
|
1404
|
+
Deprecation.deprecate("The table_alias argument to Sequel::SQL::JoinClause#initialize", "Please use a Sequel::SQL::AliasedExpression as the table argument instead.")
|
1405
|
+
AliasedExpression.new(table, table_alias)
|
1406
|
+
else
|
1407
|
+
table
|
1408
|
+
end
|
1409
|
+
end
|
1410
|
+
|
1411
|
+
# The table/set related to the JOIN, without any alias.
|
1412
|
+
def table
|
1413
|
+
if @table_expr.is_a?(AliasedExpression)
|
1414
|
+
@table_expr.expression
|
1415
|
+
else
|
1416
|
+
@table_expr
|
1417
|
+
end
|
1418
|
+
end
|
1419
|
+
|
1420
|
+
# The table alias to use for the JOIN , or nil if the
|
1421
|
+
# JOIN does not alias the table.
|
1422
|
+
def table_alias
|
1423
|
+
if @table_expr.is_a?(AliasedExpression)
|
1424
|
+
@table_expr.alias
|
1425
|
+
end
|
1426
|
+
end
|
1427
|
+
|
1428
|
+
# The column aliases to use for the JOIN , or nil if the
|
1429
|
+
# JOIN does not use a derived column list.
|
1430
|
+
def column_aliases
|
1431
|
+
if @table_expr.is_a?(AliasedExpression)
|
1432
|
+
@table_expr.columns
|
1433
|
+
end
|
1315
1434
|
end
|
1316
1435
|
|
1317
1436
|
to_s_method :join_clause_sql
|
@@ -1486,9 +1605,9 @@ module Sequel
|
|
1486
1605
|
# if a case insensitive regular expression is used (//i), that particular
|
1487
1606
|
# pattern which will always be case insensitive.
|
1488
1607
|
#
|
1489
|
-
# StringExpression.like(:a, 'a%') # "a" LIKE 'a%'
|
1490
|
-
# StringExpression.like(:a, 'a%', :case_insensitive=>true) # "a" ILIKE 'a%'
|
1491
|
-
# StringExpression.like(:a, 'a%', /^a/i) # "a" LIKE 'a%' OR "a" ~* '^a'
|
1608
|
+
# StringExpression.like(:a, 'a%') # "a" LIKE 'a%' ESCAPE '\'
|
1609
|
+
# StringExpression.like(:a, 'a%', :case_insensitive=>true) # "a" ILIKE 'a%' ESCAPE '\'
|
1610
|
+
# StringExpression.like(:a, 'a%', /^a/i) # "a" LIKE 'a%' ESCAPE '\' OR "a" ~* '^a'
|
1492
1611
|
def self.like(l, *ces)
|
1493
1612
|
l, lre, lci = like_element(l)
|
1494
1613
|
lci = (ces.last.is_a?(Hash) ? ces.pop : {})[:case_insensitive] ? true : lci
|
@@ -1568,7 +1687,7 @@ module Sequel
|
|
1568
1687
|
# If the block doesn't take an argument, the block is instance_execed in the context of
|
1569
1688
|
# an instance of this class.
|
1570
1689
|
#
|
1571
|
-
# +VirtualRow+ uses +method_missing+ to return either an +Identifier+, +QualifiedIdentifier+,
|
1690
|
+
# +VirtualRow+ uses +method_missing+ to return either an +Identifier+, +QualifiedIdentifier+, or +Function+
|
1572
1691
|
# depending on how it is called.
|
1573
1692
|
#
|
1574
1693
|
# If a block is _not_ given, creates one of the following objects:
|
@@ -1579,16 +1698,15 @@ module Sequel
|
|
1579
1698
|
# table being the part before __, and the column being the part after.
|
1580
1699
|
# +Identifier+ :: Returned otherwise, using the method name.
|
1581
1700
|
#
|
1582
|
-
# If a block is given, it returns
|
1583
|
-
# argument to the method. Note that the block is currently not called by the code, though
|
1701
|
+
# If a block is given, it returns a +Function+. Note that the block is currently not called by the code, though
|
1584
1702
|
# this may change in a future version. If the first argument is:
|
1585
1703
|
#
|
1586
1704
|
# no arguments given :: creates a +Function+ with no arguments.
|
1587
1705
|
# :* :: creates a +Function+ with a literal wildcard argument (*), mostly useful for COUNT.
|
1588
1706
|
# :distinct :: creates a +Function+ that prepends DISTINCT to the rest of the arguments, mostly
|
1589
1707
|
# useful for aggregate functions.
|
1590
|
-
# :over :: creates a +
|
1591
|
-
# of options which are
|
1708
|
+
# :over :: creates a +Function+ with a window. If a second argument is provided, it should be a hash
|
1709
|
+
# of options which are used to create the +Window+ (with possible keys :window, :partition, :order, and :frame). The
|
1592
1710
|
# arguments to the function itself should be specified as <tt>:*=>true</tt> for a wildcard, or via
|
1593
1711
|
# the <tt>:args</tt> option.
|
1594
1712
|
#
|
@@ -1657,7 +1775,7 @@ module Sequel
|
|
1657
1775
|
Sequel::LiteralString.new(s)
|
1658
1776
|
end
|
1659
1777
|
|
1660
|
-
# Return an +Identifier+, +QualifiedIdentifier+,
|
1778
|
+
# Return an +Identifier+, +QualifiedIdentifier+, or +Function+, depending
|
1661
1779
|
# on arguments and whether a block is provided. Does not currently call the block.
|
1662
1780
|
# See the class level documentation.
|
1663
1781
|
def method_missing(m, *args, &block)
|
@@ -1690,9 +1808,7 @@ module Sequel
|
|
1690
1808
|
Sequel::VIRTUAL_ROW = new
|
1691
1809
|
end
|
1692
1810
|
|
1693
|
-
# A +Window+ is part of a window function specifying the window over which
|
1694
|
-
# It is separated from the +WindowFunction+ class because it also can be used separately on
|
1695
|
-
# some databases.
|
1811
|
+
# A +Window+ is part of a window function specifying the window over which a window function operates.
|
1696
1812
|
class Window < Expression
|
1697
1813
|
# The options for this window. Options currently supported:
|
1698
1814
|
# :frame :: if specified, should be :all, :rows, or a String that is used literally. :all always operates over all rows in the
|
@@ -1711,7 +1827,7 @@ module Sequel
|
|
1711
1827
|
to_s_method :window_sql, '@opts'
|
1712
1828
|
end
|
1713
1829
|
|
1714
|
-
#
|
1830
|
+
# REMOVE411
|
1715
1831
|
class WindowFunction < GenericExpression
|
1716
1832
|
# The function to use, should be an <tt>SQL::Function</tt>.
|
1717
1833
|
attr_reader :function
|
@@ -1719,8 +1835,14 @@ module Sequel
|
|
1719
1835
|
# The window to use, should be an <tt>SQL::Window</tt>.
|
1720
1836
|
attr_reader :window
|
1721
1837
|
|
1838
|
+
def self.new(function, window)
|
1839
|
+
Deprecation.deprecate("Sequel::SQL::WindowFunction", "Please use Sequel::SQL::Function.new(name, *args).over(...) to create an SQL window function")
|
1840
|
+
function.over(window)
|
1841
|
+
end
|
1842
|
+
|
1722
1843
|
# Set the function and window.
|
1723
1844
|
def initialize(function, window)
|
1845
|
+
Deprecation.deprecate("Sequel::SQL::WindowFunction", "Please use Sequel::SQL::Function.new(name, *args).over(...) to create an SQL window function")
|
1724
1846
|
@function, @window = function, window
|
1725
1847
|
end
|
1726
1848
|
|