sequel 3.21.0 → 3.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +294 -0
- data/README.rdoc +20 -6
- data/Rakefile +20 -15
- data/doc/association_basics.rdoc +210 -43
- data/doc/dataset_basics.rdoc +4 -4
- data/doc/mass_assignment.rdoc +54 -0
- data/doc/migration.rdoc +15 -538
- data/doc/model_hooks.rdoc +64 -27
- data/doc/opening_databases.rdoc +37 -10
- data/doc/prepared_statements.rdoc +16 -10
- data/doc/reflection.rdoc +8 -2
- data/doc/release_notes/3.22.0.txt +39 -0
- data/doc/release_notes/3.23.0.txt +172 -0
- data/doc/release_notes/3.24.0.txt +420 -0
- data/doc/release_notes/3.25.0.txt +88 -0
- data/doc/release_notes/3.26.0.txt +88 -0
- data/doc/release_notes/3.27.0.txt +82 -0
- data/doc/release_notes/3.28.0.txt +304 -0
- data/doc/schema_modification.rdoc +547 -0
- data/doc/testing.rdoc +106 -0
- data/doc/transactions.rdoc +97 -0
- data/doc/virtual_rows.rdoc +2 -2
- data/lib/sequel/adapters/ado.rb +12 -1
- data/lib/sequel/adapters/amalgalite.rb +4 -0
- data/lib/sequel/adapters/db2.rb +95 -58
- data/lib/sequel/adapters/do.rb +12 -0
- data/lib/sequel/adapters/firebird.rb +25 -203
- data/lib/sequel/adapters/ibmdb.rb +440 -0
- data/lib/sequel/adapters/informix.rb +4 -19
- data/lib/sequel/adapters/jdbc/as400.rb +0 -7
- data/lib/sequel/adapters/jdbc/db2.rb +49 -0
- data/lib/sequel/adapters/jdbc/firebird.rb +34 -0
- data/lib/sequel/adapters/jdbc/h2.rb +16 -5
- data/lib/sequel/adapters/jdbc/informix.rb +31 -0
- data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +0 -32
- data/lib/sequel/adapters/jdbc/mysql.rb +9 -0
- data/lib/sequel/adapters/jdbc/oracle.rb +2 -27
- data/lib/sequel/adapters/jdbc/postgresql.rb +6 -0
- data/lib/sequel/adapters/jdbc/sqlserver.rb +46 -0
- data/lib/sequel/adapters/jdbc/transactions.rb +34 -0
- data/lib/sequel/adapters/jdbc.rb +62 -29
- data/lib/sequel/adapters/mysql.rb +22 -139
- data/lib/sequel/adapters/mysql2.rb +9 -14
- data/lib/sequel/adapters/odbc/db2.rb +21 -0
- data/lib/sequel/adapters/odbc.rb +15 -3
- data/lib/sequel/adapters/oracle.rb +17 -1
- data/lib/sequel/adapters/postgres.rb +111 -16
- data/lib/sequel/adapters/shared/access.rb +21 -0
- data/lib/sequel/adapters/shared/db2.rb +290 -0
- data/lib/sequel/adapters/shared/firebird.rb +214 -0
- data/lib/sequel/adapters/shared/informix.rb +45 -0
- data/lib/sequel/adapters/shared/mssql.rb +85 -47
- data/lib/sequel/adapters/shared/mysql.rb +50 -7
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +143 -0
- data/lib/sequel/adapters/shared/oracle.rb +0 -4
- data/lib/sequel/adapters/shared/postgres.rb +75 -43
- data/lib/sequel/adapters/shared/sqlite.rb +56 -8
- data/lib/sequel/adapters/sqlite.rb +12 -11
- data/lib/sequel/adapters/swift/mysql.rb +9 -0
- data/lib/sequel/adapters/tinytds.rb +139 -7
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +55 -0
- data/lib/sequel/ast_transformer.rb +190 -0
- data/lib/sequel/connection_pool/threaded.rb +3 -2
- data/lib/sequel/connection_pool.rb +1 -1
- data/lib/sequel/core.rb +6 -5
- data/lib/sequel/database/connecting.rb +5 -5
- data/lib/sequel/database/dataset.rb +1 -1
- data/lib/sequel/database/dataset_defaults.rb +1 -1
- data/lib/sequel/database/logging.rb +1 -1
- data/lib/sequel/database/misc.rb +38 -17
- data/lib/sequel/database/query.rb +50 -19
- data/lib/sequel/database/schema_generator.rb +8 -5
- data/lib/sequel/database/schema_methods.rb +52 -27
- data/lib/sequel/dataset/actions.rb +167 -48
- data/lib/sequel/dataset/features.rb +57 -8
- data/lib/sequel/dataset/graph.rb +1 -1
- data/lib/sequel/dataset/misc.rb +39 -20
- data/lib/sequel/dataset/mutation.rb +3 -3
- data/lib/sequel/dataset/prepared_statements.rb +29 -14
- data/lib/sequel/dataset/query.rb +182 -32
- data/lib/sequel/dataset/sql.rb +31 -58
- data/lib/sequel/dataset.rb +8 -0
- data/lib/sequel/exceptions.rb +4 -0
- data/lib/sequel/extensions/columns_introspection.rb +61 -0
- data/lib/sequel/extensions/migration.rb +6 -4
- data/lib/sequel/extensions/to_dot.rb +95 -83
- data/lib/sequel/model/associations.rb +893 -309
- data/lib/sequel/model/base.rb +302 -105
- data/lib/sequel/model/errors.rb +1 -1
- data/lib/sequel/model/exceptions.rb +5 -1
- data/lib/sequel/model.rb +13 -7
- data/lib/sequel/plugins/association_pks.rb +22 -4
- data/lib/sequel/plugins/defaults_setter.rb +58 -0
- data/lib/sequel/plugins/identity_map.rb +113 -6
- data/lib/sequel/plugins/many_through_many.rb +67 -5
- data/lib/sequel/plugins/prepared_statements.rb +140 -0
- data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
- data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
- data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
- data/lib/sequel/plugins/serialization_modification_detection.rb +51 -0
- data/lib/sequel/plugins/sharding.rb +12 -20
- data/lib/sequel/plugins/single_table_inheritance.rb +2 -0
- data/lib/sequel/plugins/update_primary_key.rb +1 -1
- data/lib/sequel/plugins/xml_serializer.rb +3 -3
- data/lib/sequel/sql.rb +107 -51
- data/lib/sequel/timezones.rb +12 -3
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/db2_spec.rb +146 -0
- data/spec/adapters/mssql_spec.rb +36 -0
- data/spec/adapters/mysql_spec.rb +36 -19
- data/spec/adapters/postgres_spec.rb +115 -28
- data/spec/adapters/spec_helper.rb +6 -0
- data/spec/adapters/sqlite_spec.rb +11 -0
- data/spec/core/connection_pool_spec.rb +62 -77
- data/spec/core/database_spec.rb +244 -287
- data/spec/core/dataset_spec.rb +383 -34
- data/spec/core/expression_filters_spec.rb +159 -41
- data/spec/core/schema_spec.rb +326 -3
- data/spec/core/spec_helper.rb +45 -0
- data/spec/extensions/association_pks_spec.rb +38 -0
- data/spec/extensions/columns_introspection_spec.rb +91 -0
- data/spec/extensions/defaults_setter_spec.rb +64 -0
- data/spec/extensions/identity_map_spec.rb +162 -0
- data/spec/extensions/many_through_many_spec.rb +195 -20
- data/spec/extensions/migration_spec.rb +17 -17
- data/spec/extensions/nested_attributes_spec.rb +1 -0
- data/spec/extensions/prepared_statements_associations_spec.rb +126 -0
- data/spec/extensions/prepared_statements_safe_spec.rb +79 -0
- data/spec/extensions/prepared_statements_spec.rb +72 -0
- data/spec/extensions/prepared_statements_with_pk_spec.rb +38 -0
- data/spec/extensions/schema_dumper_spec.rb +2 -2
- data/spec/extensions/schema_spec.rb +12 -20
- data/spec/extensions/serialization_modification_detection_spec.rb +36 -0
- data/spec/extensions/single_table_inheritance_spec.rb +11 -0
- data/spec/extensions/spec_helper.rb +3 -1
- data/spec/extensions/to_dot_spec.rb +3 -5
- data/spec/extensions/xml_serializer_spec.rb +16 -4
- data/spec/integration/associations_test.rb +405 -15
- data/spec/integration/database_test.rb +4 -2
- data/spec/integration/dataset_test.rb +240 -20
- data/spec/integration/plugin_test.rb +142 -5
- data/spec/integration/prepared_statement_test.rb +174 -95
- data/spec/integration/schema_test.rb +128 -16
- data/spec/integration/spec_helper.rb +15 -0
- data/spec/integration/transaction_test.rb +40 -0
- data/spec/integration/type_test.rb +16 -2
- data/spec/model/association_reflection_spec.rb +91 -0
- data/spec/model/associations_spec.rb +476 -5
- data/spec/model/base_spec.rb +91 -1
- data/spec/model/eager_loading_spec.rb +519 -31
- data/spec/model/hooks_spec.rb +161 -0
- data/spec/model/model_spec.rb +89 -2
- data/spec/model/plugins_spec.rb +17 -0
- data/spec/model/record_spec.rb +184 -12
- data/spec/model/spec_helper.rb +5 -0
- data/spec/model/validations_spec.rb +11 -0
- metadata +85 -34
data/lib/sequel/dataset/misc.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module Sequel
|
|
2
2
|
class Dataset
|
|
3
3
|
# ---------------------
|
|
4
|
-
# :section: Miscellaneous methods
|
|
4
|
+
# :section: 6 - Miscellaneous methods
|
|
5
5
|
# These methods don't fit cleanly into another section.
|
|
6
6
|
# ---------------------
|
|
7
7
|
|
|
@@ -23,17 +23,13 @@ module Sequel
|
|
|
23
23
|
# DB[:posts]
|
|
24
24
|
#
|
|
25
25
|
# Sequel::Dataset is an abstract class that is not useful by itself. Each
|
|
26
|
-
# database
|
|
26
|
+
# database adapter provides a subclass of Sequel::Dataset, and has
|
|
27
27
|
# the Database#dataset method return an instance of that subclass.
|
|
28
28
|
def initialize(db, opts = nil)
|
|
29
29
|
@db = db
|
|
30
|
-
@quote_identifiers = db.quote_identifiers? if db.respond_to?(:quote_identifiers?)
|
|
31
|
-
@identifier_input_method = db.identifier_input_method if db.respond_to?(:identifier_input_method)
|
|
32
|
-
@identifier_output_method = db.identifier_output_method if db.respond_to?(:identifier_output_method)
|
|
33
30
|
@opts = opts || {}
|
|
34
|
-
@row_proc = nil
|
|
35
31
|
end
|
|
36
|
-
|
|
32
|
+
|
|
37
33
|
# Define a hash value such that datasets with the same DB, opts, and SQL
|
|
38
34
|
# will be consider equal.
|
|
39
35
|
def ==(o)
|
|
@@ -45,15 +41,6 @@ module Sequel
|
|
|
45
41
|
self == o
|
|
46
42
|
end
|
|
47
43
|
|
|
48
|
-
# Return the dataset as an aliased expression with the given alias. You can
|
|
49
|
-
# use this as a FROM or JOIN dataset, or as a column if this dataset
|
|
50
|
-
# returns a single row and column.
|
|
51
|
-
#
|
|
52
|
-
# DB.from(DB[:table].as(:b)) # SELECT * FROM (SELECT * FROM table) AS b
|
|
53
|
-
def as(aliaz)
|
|
54
|
-
::Sequel::SQL::AliasedExpression.new(self, aliaz)
|
|
55
|
-
end
|
|
56
|
-
|
|
57
44
|
# Yield a dataset for each server in the connection pool that is tied to that server.
|
|
58
45
|
# Intended for use in sharded environments where all servers need to be modified
|
|
59
46
|
# with the same data:
|
|
@@ -96,10 +83,10 @@ module Sequel
|
|
|
96
83
|
# have a table, raises an error. If the table is aliased, returns the original
|
|
97
84
|
# table, not the alias
|
|
98
85
|
#
|
|
99
|
-
# DB[:table].
|
|
86
|
+
# DB[:table].first_source_table
|
|
100
87
|
# # => :table
|
|
101
88
|
#
|
|
102
|
-
# DB[:table___t].
|
|
89
|
+
# DB[:table___t].first_source_table
|
|
103
90
|
# # => :table
|
|
104
91
|
def first_source_table
|
|
105
92
|
source = @opts[:from]
|
|
@@ -120,7 +107,31 @@ module Sequel
|
|
|
120
107
|
# Define a hash value such that datasets with the same DB, opts, and SQL
|
|
121
108
|
# will have the same hash value
|
|
122
109
|
def hash
|
|
123
|
-
[db, opts.sort_by{|k| k.to_s}, sql].hash
|
|
110
|
+
[db, opts.sort_by{|k, v| k.to_s}, sql].hash
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# The String instance method to call on identifiers before sending them to
|
|
114
|
+
# the database.
|
|
115
|
+
def identifier_input_method
|
|
116
|
+
if defined?(@identifier_input_method)
|
|
117
|
+
@identifier_input_method
|
|
118
|
+
elsif db.respond_to?(:identifier_input_method)
|
|
119
|
+
@identifier_input_method = db.identifier_input_method
|
|
120
|
+
else
|
|
121
|
+
@identifier_input_method = nil
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# The String instance method to call on identifiers before sending them to
|
|
126
|
+
# the database.
|
|
127
|
+
def identifier_output_method
|
|
128
|
+
if defined?(@identifier_output_method)
|
|
129
|
+
@identifier_output_method
|
|
130
|
+
elsif db.respond_to?(:identifier_output_method)
|
|
131
|
+
@identifier_output_method = db.identifier_output_method
|
|
132
|
+
else
|
|
133
|
+
@identifier_output_method = nil
|
|
134
|
+
end
|
|
124
135
|
end
|
|
125
136
|
|
|
126
137
|
# Returns a string representation of the dataset including the class name
|
|
@@ -129,7 +140,13 @@ module Sequel
|
|
|
129
140
|
"#<#{self.class}: #{sql.inspect}>"
|
|
130
141
|
end
|
|
131
142
|
|
|
132
|
-
#
|
|
143
|
+
# The alias to use for the row_number column, used when emulating OFFSET
|
|
144
|
+
# support and for eager limit strategies
|
|
145
|
+
def row_number_column
|
|
146
|
+
:x_sequel_row_number_x
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Splits a possible implicit alias in +c+, handling both SQL::AliasedExpressions
|
|
133
150
|
# and Symbols. Returns an array of two elements, with the first being the
|
|
134
151
|
# main expression, and the second being the alias.
|
|
135
152
|
def split_alias(c)
|
|
@@ -139,6 +156,8 @@ module Sequel
|
|
|
139
156
|
[c_table ? SQL::QualifiedIdentifier.new(c_table, column.to_sym) : column.to_sym, aliaz]
|
|
140
157
|
when SQL::AliasedExpression
|
|
141
158
|
[c.expression, c.aliaz]
|
|
159
|
+
when SQL::JoinClause
|
|
160
|
+
[c.table, c.table_alias]
|
|
142
161
|
else
|
|
143
162
|
[c, nil]
|
|
144
163
|
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module Sequel
|
|
2
2
|
class Dataset
|
|
3
3
|
# ---------------------
|
|
4
|
-
# :section: Mutation methods
|
|
4
|
+
# :section: 7 - Mutation methods
|
|
5
5
|
# These methods modify the receiving dataset and should be used with care.
|
|
6
6
|
# ---------------------
|
|
7
7
|
|
|
@@ -21,10 +21,10 @@ module Sequel
|
|
|
21
21
|
def_mutation_method(*MUTATION_METHODS)
|
|
22
22
|
|
|
23
23
|
# Set the method to call on identifiers going into the database for this dataset
|
|
24
|
-
|
|
24
|
+
attr_writer :identifier_input_method
|
|
25
25
|
|
|
26
26
|
# Set the method to call on identifiers coming the database for this dataset
|
|
27
|
-
|
|
27
|
+
attr_writer :identifier_output_method
|
|
28
28
|
|
|
29
29
|
# Whether to quote identifiers for this dataset
|
|
30
30
|
attr_writer :quote_identifiers
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module Sequel
|
|
2
2
|
class Dataset
|
|
3
3
|
# ---------------------
|
|
4
|
-
# :section: Methods related to prepared statements or bound variables
|
|
4
|
+
# :section: 8 - Methods related to prepared statements or bound variables
|
|
5
5
|
# On some adapters, these use native prepared statements and bound variables, on others
|
|
6
6
|
# support is emulated. For details, see the {"Prepared Statements/Bound Variables" guide}[link:files/doc/prepared_statements_rdoc.html].
|
|
7
7
|
# ---------------------
|
|
@@ -80,7 +80,9 @@ module Sequel
|
|
|
80
80
|
when :select, :all
|
|
81
81
|
select_sql
|
|
82
82
|
when :first
|
|
83
|
-
|
|
83
|
+
limit(1).select_sql
|
|
84
|
+
when :insert_select
|
|
85
|
+
returning.insert_sql(*@prepared_modify_values)
|
|
84
86
|
when :insert
|
|
85
87
|
insert_sql(*@prepared_modify_values)
|
|
86
88
|
when :update
|
|
@@ -95,8 +97,8 @@ module Sequel
|
|
|
95
97
|
# and they are substituted using prepared_arg.
|
|
96
98
|
def literal_symbol(v)
|
|
97
99
|
if @opts[:bind_vars] and match = PLACEHOLDER_RE.match(v.to_s)
|
|
98
|
-
|
|
99
|
-
|
|
100
|
+
s = match[1].to_sym
|
|
101
|
+
prepared_arg?(s) ? literal(prepared_arg(s)) : v
|
|
100
102
|
else
|
|
101
103
|
super
|
|
102
104
|
end
|
|
@@ -118,6 +120,9 @@ module Sequel
|
|
|
118
120
|
case @prepared_type
|
|
119
121
|
when :select, :all
|
|
120
122
|
all(&block)
|
|
123
|
+
when :insert_select
|
|
124
|
+
meta_def(:select_sql){prepared_sql}
|
|
125
|
+
first
|
|
121
126
|
when :first
|
|
122
127
|
first
|
|
123
128
|
when :insert
|
|
@@ -136,6 +141,11 @@ module Sequel
|
|
|
136
141
|
@opts[:bind_vars][k]
|
|
137
142
|
end
|
|
138
143
|
|
|
144
|
+
# Whether there is a bound value for the given key.
|
|
145
|
+
def prepared_arg?(k)
|
|
146
|
+
@opts[:bind_vars].has_key?(k)
|
|
147
|
+
end
|
|
148
|
+
|
|
139
149
|
# Use a clone of the dataset extended with prepared statement
|
|
140
150
|
# support and using the same argument hash so that you can use
|
|
141
151
|
# bind variables/prepared arguments in subselects.
|
|
@@ -171,6 +181,11 @@ module Sequel
|
|
|
171
181
|
prepared_args << k
|
|
172
182
|
prepared_arg_placeholder
|
|
173
183
|
end
|
|
184
|
+
|
|
185
|
+
# Always assume there is a prepared arg in the argument mapper.
|
|
186
|
+
def prepared_arg?(k)
|
|
187
|
+
true
|
|
188
|
+
end
|
|
174
189
|
end
|
|
175
190
|
|
|
176
191
|
# Set the bind variables to use for the call. If bind variables have
|
|
@@ -184,11 +199,9 @@ module Sequel
|
|
|
184
199
|
clone(:bind_vars=>@opts[:bind_vars] ? @opts[:bind_vars].merge(bind_vars) : bind_vars)
|
|
185
200
|
end
|
|
186
201
|
|
|
187
|
-
# For the given type (:select, :insert, :update, or :delete),
|
|
188
|
-
# run the sql with the bind variables
|
|
189
|
-
#
|
|
190
|
-
# insert or update (if one of those types is used),
|
|
191
|
-
# which may contain placeholders.
|
|
202
|
+
# For the given type (:select, :first, :insert, :insert_select, :update, or :delete),
|
|
203
|
+
# run the sql with the bind variables specified in the hash. +values+ is a hash passed to
|
|
204
|
+
# insert or update (if one of those types is used), which may contain placeholders.
|
|
192
205
|
#
|
|
193
206
|
# DB[:table].filter(:id=>:$id).call(:first, :id=>1)
|
|
194
207
|
# # SELECT * FROM table WHERE id = ? LIMIT 1 -- (1)
|
|
@@ -197,11 +210,13 @@ module Sequel
|
|
|
197
210
|
prepare(type, nil, *values).call(bind_variables, &block)
|
|
198
211
|
end
|
|
199
212
|
|
|
200
|
-
# Prepare an SQL statement for later execution.
|
|
201
|
-
#
|
|
202
|
-
#
|
|
203
|
-
#
|
|
204
|
-
#
|
|
213
|
+
# Prepare an SQL statement for later execution. Takes a type similar to #call,
|
|
214
|
+
# and the name symbol of the prepared statement.
|
|
215
|
+
# This returns a clone of the dataset extended with PreparedStatementMethods,
|
|
216
|
+
# which you can +call+ with the hash of bind variables to use.
|
|
217
|
+
# The prepared statement is also stored in
|
|
218
|
+
# the associated database, where it can be called by name.
|
|
219
|
+
# The following usage is identical:
|
|
205
220
|
#
|
|
206
221
|
# ps = DB[:table].filter(:name=>:$name).prepare(:first, :select_by_name)
|
|
207
222
|
#
|
data/lib/sequel/dataset/query.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module Sequel
|
|
2
2
|
class Dataset
|
|
3
3
|
# ---------------------
|
|
4
|
-
# :section: Methods that return modified datasets
|
|
4
|
+
# :section: 1 - Methods that return modified datasets
|
|
5
5
|
# These methods all return modified copies of the receiver.
|
|
6
6
|
# ---------------------
|
|
7
7
|
|
|
@@ -28,10 +28,10 @@ module Sequel
|
|
|
28
28
|
JOIN_METHODS = (CONDITIONED_JOIN_TYPES + UNCONDITIONED_JOIN_TYPES).map{|x| "#{x}_join".to_sym} + [:join, :join_table]
|
|
29
29
|
|
|
30
30
|
# Methods that return modified datasets
|
|
31
|
-
QUERY_METHODS = %w'add_graph_aliases and distinct except exclude
|
|
31
|
+
QUERY_METHODS = %w'add_graph_aliases and distinct except exclude exclude_having exclude_where
|
|
32
32
|
filter for_update from from_self graph grep group group_and_count group_by having intersect invert
|
|
33
33
|
limit lock_style naked or order order_append order_by order_more order_prepend paginate qualify query
|
|
34
|
-
reverse reverse_order select select_all select_append select_more server
|
|
34
|
+
reverse reverse_order select select_all select_append select_group select_more server
|
|
35
35
|
set_defaults set_graph_aliases set_overrides unfiltered ungraphed ungrouped union
|
|
36
36
|
unlimited unordered where with with_recursive with_sql'.collect{|x| x.to_sym} + JOIN_METHODS
|
|
37
37
|
|
|
@@ -80,7 +80,7 @@ module Sequel
|
|
|
80
80
|
# :from_self :: Set to false to not wrap the returned dataset in a from_self, use with care.
|
|
81
81
|
#
|
|
82
82
|
# DB[:items].except(DB[:other_items])
|
|
83
|
-
# # SELECT * FROM items EXCEPT SELECT * FROM other_items
|
|
83
|
+
# # SELECT * FROM (SELECT * FROM items EXCEPT SELECT * FROM other_items) AS t1
|
|
84
84
|
#
|
|
85
85
|
# DB[:items].except(DB[:other_items], :all=>true, :from_self=>false)
|
|
86
86
|
# # SELECT * FROM items EXCEPT ALL SELECT * FROM other_items
|
|
@@ -103,12 +103,29 @@ module Sequel
|
|
|
103
103
|
# DB[:items].exclude(:category => 'software', :id=>3)
|
|
104
104
|
# # SELECT * FROM items WHERE ((category != 'software') OR (id != 3))
|
|
105
105
|
def exclude(*cond, &block)
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
106
|
+
_filter_or_exclude(true, @opts[:having] ? :having : :where, *cond, &block)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Inverts the given conditions and adds them to the HAVING clause.
|
|
110
|
+
#
|
|
111
|
+
# DB[:items].select_group(:name).exclude_having{count(name) < 2}
|
|
112
|
+
# # SELECT name FROM items GROUP BY name HAVING (count(name) >= 2)
|
|
113
|
+
def exclude_having(*cond, &block)
|
|
114
|
+
_filter_or_exclude(true, :having, *cond, &block)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Inverts the given conditions and adds them to the WHERE clause.
|
|
118
|
+
#
|
|
119
|
+
# DB[:items].select_group(:name).exclude_where(:category => 'software')
|
|
120
|
+
# # SELECT * FROM items WHERE (category != 'software')
|
|
121
|
+
#
|
|
122
|
+
# DB[:items].select_group(:name).
|
|
123
|
+
# exclude_having{count(name) < 2}.
|
|
124
|
+
# exclude_where(:category => 'software')
|
|
125
|
+
# # SELECT name FROM items WHERE (category != 'software')
|
|
126
|
+
# # GROUP BY name HAVING (count(name) >= 2)
|
|
127
|
+
def exclude_where(*cond, &block)
|
|
128
|
+
_filter_or_exclude(true, :where, *cond, &block)
|
|
112
129
|
end
|
|
113
130
|
|
|
114
131
|
# Returns a copy of the dataset with the given conditions imposed upon it.
|
|
@@ -270,21 +287,25 @@ module Sequel
|
|
|
270
287
|
end
|
|
271
288
|
|
|
272
289
|
# Returns a copy of the dataset with the results grouped by the value of
|
|
273
|
-
# the given columns.
|
|
290
|
+
# the given columns. If a block is given, it is treated
|
|
291
|
+
# as a virtual row block, similar to +filter+.
|
|
274
292
|
#
|
|
275
293
|
# DB[:items].group(:id) # SELECT * FROM items GROUP BY id
|
|
276
294
|
# DB[:items].group(:id, :name) # SELECT * FROM items GROUP BY id, name
|
|
277
|
-
|
|
295
|
+
# DB[:items].group{[a, sum(b)]} # SELECT * FROM items GROUP BY a, sum(b)
|
|
296
|
+
def group(*columns, &block)
|
|
297
|
+
virtual_row_columns(columns, block)
|
|
278
298
|
clone(:group => (columns.compact.empty? ? nil : columns))
|
|
279
299
|
end
|
|
280
300
|
|
|
281
301
|
# Alias of group
|
|
282
|
-
def group_by(*columns)
|
|
283
|
-
group(*columns)
|
|
302
|
+
def group_by(*columns, &block)
|
|
303
|
+
group(*columns, &block)
|
|
284
304
|
end
|
|
285
305
|
|
|
286
306
|
# Returns a dataset grouped by the given column with count by group.
|
|
287
307
|
# Column aliases may be supplied, and will be included in the select clause.
|
|
308
|
+
# If a block is given, it is treated as a virtual row block, similar to +filter+.
|
|
288
309
|
#
|
|
289
310
|
# Examples:
|
|
290
311
|
#
|
|
@@ -299,8 +320,12 @@ module Sequel
|
|
|
299
320
|
# DB[:items].group_and_count(:first_name___name).all
|
|
300
321
|
# # SELECT first_name AS name, count(*) AS count FROM items GROUP BY first_name
|
|
301
322
|
# # => [{:name=>'a', :count=>1}, ...]
|
|
302
|
-
|
|
303
|
-
|
|
323
|
+
#
|
|
324
|
+
# DB[:items].group_and_count{substr(first_name, 1, 1).as(initial)}.all
|
|
325
|
+
# # SELECT substr(first_name, 1, 1) AS initial, count(*) AS count FROM items GROUP BY substr(first_name, 1, 1)
|
|
326
|
+
# # => [{:initial=>'a', :count=>1}, ...]
|
|
327
|
+
def group_and_count(*columns, &block)
|
|
328
|
+
select_group(*columns, &block).select_more(COUNT_OF_ALL_AS_COUNT)
|
|
304
329
|
end
|
|
305
330
|
|
|
306
331
|
# Returns a copy of the dataset with the HAVING conditions changed. See #filter for argument types.
|
|
@@ -356,7 +381,11 @@ module Sequel
|
|
|
356
381
|
inner_join(*args, &block)
|
|
357
382
|
end
|
|
358
383
|
|
|
359
|
-
# Returns a joined dataset.
|
|
384
|
+
# Returns a joined dataset. Not usually called directly, users should use the
|
|
385
|
+
# appropriate join method (e.g. join, left_join, natural_join, cross_join) which fills
|
|
386
|
+
# in the +type+ argument.
|
|
387
|
+
#
|
|
388
|
+
# Takes the following arguments:
|
|
360
389
|
#
|
|
361
390
|
# * type - The type of join to do (e.g. :inner)
|
|
362
391
|
# * table - Depends on type:
|
|
@@ -386,7 +415,29 @@ module Sequel
|
|
|
386
415
|
# in which case it yields the table alias/name for the table currently being joined,
|
|
387
416
|
# the table alias/name for the last joined (or first table), and an array of previous
|
|
388
417
|
# SQL::JoinClause. Unlike +filter+, this block is not treated as a virtual row block.
|
|
418
|
+
#
|
|
419
|
+
# Examples:
|
|
420
|
+
#
|
|
421
|
+
# DB[:a].join_table(:cross, :b)
|
|
422
|
+
# # SELECT * FROM a CROSS JOIN b
|
|
423
|
+
#
|
|
424
|
+
# DB[:a].join_table(:inner, DB[:b], :c=>d)
|
|
425
|
+
# # SELECT * FROM a INNER JOIN (SELECT * FROM b) AS t1 ON (t1.c = a.d)
|
|
426
|
+
#
|
|
427
|
+
# DB[:a].join_table(:left, :b___c, [:d])
|
|
428
|
+
# # SELECT * FROM a LEFT JOIN b AS c USING (d)
|
|
429
|
+
#
|
|
430
|
+
# DB[:a].natural_join(:b).join_table(:inner, :c) do |ta, jta, js|
|
|
431
|
+
# (:d.qualify(ta) > :e.qualify(jta)) & {:f.qualify(ta)=>DB.from(js.first.table).select(:g)}
|
|
432
|
+
# end
|
|
433
|
+
# # SELECT * FROM a NATURAL JOIN b INNER JOIN c
|
|
434
|
+
# # ON ((c.d > b.e) AND (c.f IN (SELECT g FROM b)))
|
|
389
435
|
def join_table(type, table, expr=nil, options={}, &block)
|
|
436
|
+
if table.is_a?(Dataset) && table.opts[:with] && !supports_cte_in_subqueries?
|
|
437
|
+
s, ds = hoist_cte(table)
|
|
438
|
+
return s.join_table(type, ds, expr, options, &block)
|
|
439
|
+
end
|
|
440
|
+
|
|
390
441
|
using_join = expr.is_a?(Array) && !expr.empty? && expr.all?{|x| x.is_a?(Symbol)}
|
|
391
442
|
if using_join && !supports_join_using?
|
|
392
443
|
h = {}
|
|
@@ -431,6 +482,7 @@ module Sequel
|
|
|
431
482
|
v = qualified_column_name(v, last_alias) if v.is_a?(Symbol)
|
|
432
483
|
[k,v]
|
|
433
484
|
end
|
|
485
|
+
expr = SQL::BooleanExpression.from_value_pairs(expr)
|
|
434
486
|
end
|
|
435
487
|
if block
|
|
436
488
|
expr2 = yield(table_name, last_alias, @opts[:join] || [])
|
|
@@ -531,7 +583,7 @@ module Sequel
|
|
|
531
583
|
# DB[:items].order{sum(name).desc} # SELECT * FROM items ORDER BY sum(name) DESC
|
|
532
584
|
# DB[:items].order(nil) # SELECT * FROM items
|
|
533
585
|
def order(*columns, &block)
|
|
534
|
-
columns
|
|
586
|
+
virtual_row_columns(columns, block)
|
|
535
587
|
clone(:order => (columns.compact.empty?) ? nil : columns)
|
|
536
588
|
end
|
|
537
589
|
|
|
@@ -565,7 +617,7 @@ module Sequel
|
|
|
565
617
|
@opts[:order] ? ds.order_more(*@opts[:order]) : ds
|
|
566
618
|
end
|
|
567
619
|
|
|
568
|
-
# Qualify to the given table, or first source if
|
|
620
|
+
# Qualify to the given table, or first source if no table is given.
|
|
569
621
|
#
|
|
570
622
|
# DB[:items].filter(:id=>1).qualify
|
|
571
623
|
# # SELECT items.* FROM items WHERE (items.id = 1)
|
|
@@ -606,6 +658,18 @@ module Sequel
|
|
|
606
658
|
qualify_to(first_source)
|
|
607
659
|
end
|
|
608
660
|
|
|
661
|
+
# Modify the RETURNING clause, only supported on a few databases. If returning
|
|
662
|
+
# is used, instead of insert returning the autogenerated primary key or
|
|
663
|
+
# update/delete returning the number of modified rows, results are
|
|
664
|
+
# returned using +fetch_rows+.
|
|
665
|
+
#
|
|
666
|
+
# DB[:items].returning # RETURNING *
|
|
667
|
+
# DB[:items].returning(nil) # RETURNING NULL
|
|
668
|
+
# DB[:items].returning(:id, :name) # RETURNING id, name
|
|
669
|
+
def returning(*values)
|
|
670
|
+
clone(:returning=>values)
|
|
671
|
+
end
|
|
672
|
+
|
|
609
673
|
# Returns a copy of the dataset with the order reversed. If no order is
|
|
610
674
|
# given, the existing order is inverted.
|
|
611
675
|
#
|
|
@@ -629,7 +693,7 @@ module Sequel
|
|
|
629
693
|
# DB[:items].select(:a, :b) # SELECT a, b FROM items
|
|
630
694
|
# DB[:items].select{[a, sum(b)]} # SELECT a, sum(b) FROM items
|
|
631
695
|
def select(*columns, &block)
|
|
632
|
-
columns
|
|
696
|
+
virtual_row_columns(columns, block)
|
|
633
697
|
m = []
|
|
634
698
|
columns.each do |i|
|
|
635
699
|
i.is_a?(Hash) ? m.concat(i.map{|k, v| SQL::AliasedExpression.new(k,v)}) : m << i
|
|
@@ -637,11 +701,19 @@ module Sequel
|
|
|
637
701
|
clone(:select => m)
|
|
638
702
|
end
|
|
639
703
|
|
|
640
|
-
# Returns a copy of the dataset selecting the wildcard
|
|
704
|
+
# Returns a copy of the dataset selecting the wildcard if no arguments
|
|
705
|
+
# are given. If arguments are given, treat them as tables and select
|
|
706
|
+
# all columns (using the wildcard) from each table.
|
|
641
707
|
#
|
|
642
708
|
# DB[:items].select(:a).select_all # SELECT * FROM items
|
|
643
|
-
|
|
644
|
-
|
|
709
|
+
# DB[:items].select_all(:items) # SELECT items.* FROM items
|
|
710
|
+
# DB[:items].select_all(:items, :foo) # SELECT items.*, foo.* FROM items
|
|
711
|
+
def select_all(*tables)
|
|
712
|
+
if tables.empty?
|
|
713
|
+
clone(:select => nil)
|
|
714
|
+
else
|
|
715
|
+
select(*tables.map{|t| i, a = split_alias(t); a || i}.map{|t| SQL::ColumnAll.new(t)})
|
|
716
|
+
end
|
|
645
717
|
end
|
|
646
718
|
|
|
647
719
|
# Returns a copy of the dataset with the given columns added
|
|
@@ -653,10 +725,29 @@ module Sequel
|
|
|
653
725
|
# DB[:items].select_append(:b) # SELECT *, b FROM items
|
|
654
726
|
def select_append(*columns, &block)
|
|
655
727
|
cur_sel = @opts[:select]
|
|
656
|
-
|
|
728
|
+
if !cur_sel || cur_sel.empty?
|
|
729
|
+
unless supports_select_all_and_column?
|
|
730
|
+
return select_all(*(Array(@opts[:from]) + Array(@opts[:join]))).select_more(*columns, &block)
|
|
731
|
+
end
|
|
732
|
+
cur_sel = [WILDCARD]
|
|
733
|
+
end
|
|
657
734
|
select(*(cur_sel + columns), &block)
|
|
658
735
|
end
|
|
659
736
|
|
|
737
|
+
# Set both the select and group clauses with the given +columns+.
|
|
738
|
+
# Column aliases may be supplied, and will be included in the select clause.
|
|
739
|
+
# This also takes a virtual row block similar to +filter+.
|
|
740
|
+
#
|
|
741
|
+
# DB[:items].select_group(:a, :b)
|
|
742
|
+
# # SELECT a, b FROM items GROUP BY a, b
|
|
743
|
+
#
|
|
744
|
+
# DB[:items].select_group(:c___a){f(c2)}
|
|
745
|
+
# # SELECT c AS a, f(c2) FROM items GROUP BY c, f(c2)
|
|
746
|
+
def select_group(*columns, &block)
|
|
747
|
+
virtual_row_columns(columns, block)
|
|
748
|
+
select(*columns).group(*columns.map{|c| unaliased_identifier(c)})
|
|
749
|
+
end
|
|
750
|
+
|
|
660
751
|
# Returns a copy of the dataset with the given columns added
|
|
661
752
|
# to the existing selected columns. If no columns are currently selected
|
|
662
753
|
# it will just select the columns given.
|
|
@@ -670,7 +761,7 @@ module Sequel
|
|
|
670
761
|
end
|
|
671
762
|
|
|
672
763
|
# Set the server for this dataset to use. Used to pick a specific database
|
|
673
|
-
# shard to run a query against, or to override the default (
|
|
764
|
+
# shard to run a query against, or to override the default (where SELECT uses
|
|
674
765
|
# :read_only database and all other queries use the :default database). This
|
|
675
766
|
# method is always available but is only useful when database sharding is being
|
|
676
767
|
# used.
|
|
@@ -702,6 +793,22 @@ module Sequel
|
|
|
702
793
|
clone(:overrides=>hash.merge(@opts[:overrides]||{}))
|
|
703
794
|
end
|
|
704
795
|
|
|
796
|
+
# Unbind bound variables from this dataset's filter and return an array of two
|
|
797
|
+
# objects. The first object is a modified dataset where the filter has been
|
|
798
|
+
# replaced with one that uses bound variable placeholders. The second object
|
|
799
|
+
# is the hash of unbound variables. You can then prepare and execute (or just
|
|
800
|
+
# call) the dataset with the bound variables to get results.
|
|
801
|
+
#
|
|
802
|
+
# ds, bv = DB[:items].filter(:a=>1).unbind
|
|
803
|
+
# ds # SELECT * FROM items WHERE (a = $a)
|
|
804
|
+
# bv # {:a => 1}
|
|
805
|
+
# ds.call(:select, bv)
|
|
806
|
+
def unbind
|
|
807
|
+
u = Unbinder.new
|
|
808
|
+
ds = clone(:where=>u.transform(opts[:where]), :join=>u.transform(opts[:join]))
|
|
809
|
+
[ds, u.binds]
|
|
810
|
+
end
|
|
811
|
+
|
|
705
812
|
# Returns a copy of the dataset with no filters (HAVING or WHERE clause) applied.
|
|
706
813
|
#
|
|
707
814
|
# DB[:items].group(:a).having(:a=>1).where(:b).unfiltered
|
|
@@ -726,8 +833,8 @@ module Sequel
|
|
|
726
833
|
# :all :: Set to true to use UNION ALL instead of UNION, so duplicate rows can occur
|
|
727
834
|
# :from_self :: Set to false to not wrap the returned dataset in a from_self, use with care.
|
|
728
835
|
#
|
|
729
|
-
# DB[:items].union(DB[:other_items])
|
|
730
|
-
#
|
|
836
|
+
# DB[:items].union(DB[:other_items])
|
|
837
|
+
# # SELECT * FROM (SELECT * FROM items UNION SELECT * FROM other_items) AS t1
|
|
731
838
|
#
|
|
732
839
|
# DB[:items].union(DB[:other_items], :all=>true, :from_self=>false)
|
|
733
840
|
# # SELECT * FROM items UNION ALL SELECT * FROM other_items
|
|
@@ -803,8 +910,20 @@ module Sequel
|
|
|
803
910
|
# to keep the same row_proc/graph, but change the SQL used to custom SQL.
|
|
804
911
|
#
|
|
805
912
|
# DB[:items].with_sql('SELECT * FROM foo') # SELECT * FROM foo
|
|
913
|
+
#
|
|
914
|
+
# You can use placeholders in your SQL and provide arguments for those placeholders:
|
|
915
|
+
#
|
|
916
|
+
# DB[:items].with_sql('SELECT ? FROM foo', 1) # SELECT 1 FROM foo
|
|
917
|
+
#
|
|
918
|
+
# You can also provide a method name and arguments to call to get the SQL:
|
|
919
|
+
#
|
|
920
|
+
# DB[:items].with_sql(:insert_sql, :b=>1) # INSERT INTO items (b) VALUES (1)
|
|
806
921
|
def with_sql(sql, *args)
|
|
807
|
-
sql
|
|
922
|
+
if sql.is_a?(Symbol)
|
|
923
|
+
sql = send(sql, *args)
|
|
924
|
+
else
|
|
925
|
+
sql = SQL::PlaceholderLiteralString.new(sql, args) unless args.empty?
|
|
926
|
+
end
|
|
808
927
|
clone(:sql=>sql)
|
|
809
928
|
end
|
|
810
929
|
|
|
@@ -823,18 +942,24 @@ module Sequel
|
|
|
823
942
|
|
|
824
943
|
private
|
|
825
944
|
|
|
826
|
-
# Internal filter method so it works on either the having or where clauses.
|
|
827
|
-
def
|
|
945
|
+
# Internal filter/exclude method so it works on either the having or where clauses.
|
|
946
|
+
def _filter_or_exclude(invert, clause, *cond, &block)
|
|
828
947
|
cond = cond.first if cond.size == 1
|
|
829
948
|
if cond.respond_to?(:empty?) && cond.empty? && !block
|
|
830
949
|
clone
|
|
831
950
|
else
|
|
832
951
|
cond = filter_expr(cond, &block)
|
|
952
|
+
cond = SQL::BooleanExpression.invert(cond) if invert
|
|
833
953
|
cond = SQL::BooleanExpression.new(:AND, @opts[clause], cond) if @opts[clause]
|
|
834
954
|
clone(clause => cond)
|
|
835
955
|
end
|
|
836
956
|
end
|
|
837
|
-
|
|
957
|
+
|
|
958
|
+
# Internal filter method so it works on either the having or where clauses.
|
|
959
|
+
def _filter(clause, *cond, &block)
|
|
960
|
+
_filter_or_exclude(false, clause, *cond, &block)
|
|
961
|
+
end
|
|
962
|
+
|
|
838
963
|
# Add the dataset to the list of compounds
|
|
839
964
|
def compound_clone(type, dataset, opts)
|
|
840
965
|
ds = compound_from_self.clone(:compounds=>Array(@opts[:compounds]).map{|x| x.dup} + [[type, dataset.compound_from_self, opts[:all]]])
|
|
@@ -867,7 +992,13 @@ module Sequel
|
|
|
867
992
|
when Symbol, SQL::Expression
|
|
868
993
|
expr
|
|
869
994
|
when TrueClass, FalseClass
|
|
870
|
-
|
|
995
|
+
if supports_where_true?
|
|
996
|
+
SQL::BooleanExpression.new(:NOOP, expr)
|
|
997
|
+
elsif expr
|
|
998
|
+
SQL::Constants::SQLTRUE
|
|
999
|
+
else
|
|
1000
|
+
SQL::Constants::SQLFALSE
|
|
1001
|
+
end
|
|
871
1002
|
when String
|
|
872
1003
|
LiteralString.new("(#{expr})")
|
|
873
1004
|
else
|
|
@@ -875,6 +1006,13 @@ module Sequel
|
|
|
875
1006
|
end
|
|
876
1007
|
end
|
|
877
1008
|
|
|
1009
|
+
# Return two datasets, the first a clone of the receiver with the WITH
|
|
1010
|
+
# clause from the given dataset added to it, and the second a clone of
|
|
1011
|
+
# the given dataset with the WITH clause removed.
|
|
1012
|
+
def hoist_cte(ds)
|
|
1013
|
+
[clone(:with => (opts[:with] || []) + ds.opts[:with]), ds.clone(:with => nil)]
|
|
1014
|
+
end
|
|
1015
|
+
|
|
878
1016
|
# Inverts the given order by breaking it into a list of column references
|
|
879
1017
|
# and inverting them.
|
|
880
1018
|
#
|
|
@@ -892,5 +1030,17 @@ module Sequel
|
|
|
892
1030
|
end
|
|
893
1031
|
end
|
|
894
1032
|
end
|
|
1033
|
+
|
|
1034
|
+
# Return self if the dataset already has a server, or a cloned dataset with the
|
|
1035
|
+
# default server otherwise.
|
|
1036
|
+
def default_server
|
|
1037
|
+
@opts[:server] ? self : clone(:server=>:default)
|
|
1038
|
+
end
|
|
1039
|
+
|
|
1040
|
+
# Treat the +block+ as a virtual_row block if not +nil+ and
|
|
1041
|
+
# add the resulting columns to the +columns+ array (modifies +columns+).
|
|
1042
|
+
def virtual_row_columns(columns, block)
|
|
1043
|
+
columns.concat(Array(Sequel.virtual_row(&block))) if block
|
|
1044
|
+
end
|
|
895
1045
|
end
|
|
896
1046
|
end
|