sequel 4.41.0 → 4.42.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +98 -0
- data/README.rdoc +23 -10
- data/doc/active_record.rdoc +4 -4
- data/doc/advanced_associations.rdoc +2 -2
- data/doc/association_basics.rdoc +5 -2
- data/doc/cheat_sheet.rdoc +3 -3
- data/doc/core_extensions.rdoc +2 -2
- data/doc/dataset_basics.rdoc +4 -4
- data/doc/dataset_filtering.rdoc +1 -1
- data/doc/migration.rdoc +19 -1
- data/doc/prepared_statements.rdoc +2 -2
- data/doc/release_notes/4.42.0.txt +221 -0
- data/doc/testing.rdoc +3 -1
- data/lib/sequel/adapters/ado/access.rb +0 -1
- data/lib/sequel/adapters/ado/mssql.rb +0 -1
- data/lib/sequel/adapters/do/mysql.rb +0 -1
- data/lib/sequel/adapters/do/postgres.rb +0 -1
- data/lib/sequel/adapters/do/sqlite3.rb +0 -1
- data/lib/sequel/adapters/ibmdb.rb +21 -25
- data/lib/sequel/adapters/jdbc.rb +8 -16
- data/lib/sequel/adapters/jdbc/as400.rb +0 -1
- data/lib/sequel/adapters/jdbc/cubrid.rb +0 -1
- data/lib/sequel/adapters/jdbc/db2.rb +0 -1
- data/lib/sequel/adapters/jdbc/derby.rb +0 -1
- data/lib/sequel/adapters/jdbc/firebirdsql.rb +0 -1
- data/lib/sequel/adapters/jdbc/h2.rb +0 -1
- data/lib/sequel/adapters/jdbc/hsqldb.rb +0 -1
- data/lib/sequel/adapters/jdbc/informix-sqli.rb +0 -1
- data/lib/sequel/adapters/jdbc/jdbcprogress.rb +0 -1
- data/lib/sequel/adapters/jdbc/jtds.rb +0 -1
- data/lib/sequel/adapters/jdbc/mssql.rb +0 -1
- data/lib/sequel/adapters/jdbc/mysql.rb +0 -1
- data/lib/sequel/adapters/jdbc/oracle.rb +0 -1
- data/lib/sequel/adapters/jdbc/postgresql.rb +0 -13
- data/lib/sequel/adapters/jdbc/sqlanywhere.rb +0 -1
- data/lib/sequel/adapters/jdbc/sqlite.rb +0 -1
- data/lib/sequel/adapters/jdbc/sqlserver.rb +3 -4
- data/lib/sequel/adapters/mock.rb +54 -12
- data/lib/sequel/adapters/mysql.rb +1 -1
- data/lib/sequel/adapters/mysql2.rb +11 -17
- data/lib/sequel/adapters/odbc/mssql.rb +0 -1
- data/lib/sequel/adapters/oracle.rb +8 -20
- data/lib/sequel/adapters/postgres.rb +11 -29
- data/lib/sequel/adapters/shared/access.rb +5 -12
- data/lib/sequel/adapters/shared/cubrid.rb +4 -13
- data/lib/sequel/adapters/shared/db2.rb +4 -2
- data/lib/sequel/adapters/shared/firebird.rb +2 -4
- data/lib/sequel/adapters/shared/informix.rb +4 -2
- data/lib/sequel/adapters/shared/mssql.rb +3 -5
- data/lib/sequel/adapters/shared/mysql.rb +4 -14
- data/lib/sequel/adapters/shared/oracle.rb +1 -3
- data/lib/sequel/adapters/shared/postgres.rb +16 -38
- data/lib/sequel/adapters/shared/progress.rb +0 -2
- data/lib/sequel/adapters/shared/sqlanywhere.rb +0 -2
- data/lib/sequel/adapters/shared/sqlite.rb +20 -16
- data/lib/sequel/adapters/sqlite.rb +8 -20
- data/lib/sequel/adapters/swift/mysql.rb +0 -1
- data/lib/sequel/adapters/swift/postgres.rb +0 -1
- data/lib/sequel/adapters/swift/sqlite.rb +0 -1
- data/lib/sequel/adapters/tinytds.rb +4 -12
- data/lib/sequel/adapters/utils/emulate_offset_with_reverse_and_count.rb +1 -1
- data/lib/sequel/adapters/utils/mysql_mysql2.rb +2 -2
- data/lib/sequel/adapters/utils/mysql_prepared_statements.rb +11 -34
- data/lib/sequel/adapters/utils/stored_procedures.rb +9 -22
- data/lib/sequel/adapters/utils/unmodified_identifiers.rb +26 -0
- data/lib/sequel/ast_transformer.rb +2 -2
- data/lib/sequel/database/dataset.rb +1 -1
- data/lib/sequel/database/dataset_defaults.rb +0 -66
- data/lib/sequel/database/features.rb +6 -0
- data/lib/sequel/database/misc.rb +31 -17
- data/lib/sequel/database/query.rb +7 -4
- data/lib/sequel/database/schema_methods.rb +1 -1
- data/lib/sequel/dataset.rb +8 -8
- data/lib/sequel/dataset/actions.rb +140 -46
- data/lib/sequel/dataset/features.rb +1 -5
- data/lib/sequel/dataset/graph.rb +7 -8
- data/lib/sequel/dataset/misc.rb +127 -56
- data/lib/sequel/dataset/mutation.rb +9 -20
- data/lib/sequel/dataset/placeholder_literalizer.rb +10 -1
- data/lib/sequel/dataset/prepared_statements.rb +102 -46
- data/lib/sequel/dataset/query.rb +155 -72
- data/lib/sequel/dataset/sql.rb +26 -9
- data/lib/sequel/extensions/columns_introspection.rb +3 -1
- data/lib/sequel/extensions/core_extensions.rb +5 -5
- data/lib/sequel/extensions/core_refinements.rb +5 -5
- data/lib/sequel/extensions/duplicate_columns_handler.rb +4 -2
- data/lib/sequel/extensions/freeze_datasets.rb +69 -0
- data/lib/sequel/extensions/identifier_mangling.rb +196 -0
- data/lib/sequel/extensions/looser_typecasting.rb +11 -7
- data/lib/sequel/extensions/migration.rb +1 -1
- data/lib/sequel/extensions/null_dataset.rb +5 -2
- data/lib/sequel/extensions/pagination.rb +42 -23
- data/lib/sequel/extensions/pg_enum.rb +3 -3
- data/lib/sequel/extensions/query.rb +3 -3
- data/lib/sequel/extensions/sequel_3_dataset_methods.rb +15 -8
- data/lib/sequel/model/associations.rb +25 -8
- data/lib/sequel/model/base.rb +88 -29
- data/lib/sequel/model/dataset_module.rb +37 -0
- data/lib/sequel/plugins/association_pks.rb +4 -4
- data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
- data/lib/sequel/plugins/constraint_validations.rb +1 -2
- data/lib/sequel/plugins/csv_serializer.rb +2 -2
- data/lib/sequel/plugins/dataset_associations.rb +8 -8
- data/lib/sequel/plugins/eager_each.rb +2 -2
- data/lib/sequel/plugins/instance_filters.rb +1 -1
- data/lib/sequel/plugins/json_serializer.rb +2 -2
- data/lib/sequel/plugins/lazy_attributes.rb +1 -1
- data/lib/sequel/plugins/list.rb +4 -4
- data/lib/sequel/plugins/prepared_statements.rb +2 -4
- data/lib/sequel/plugins/prepared_statements_associations.rb +1 -3
- data/lib/sequel/plugins/prepared_statements_with_pk.rb +1 -1
- data/lib/sequel/plugins/rcte_tree.rb +13 -13
- data/lib/sequel/plugins/sharding.rb +1 -1
- data/lib/sequel/plugins/single_table_inheritance.rb +9 -4
- data/lib/sequel/plugins/tactical_eager_loading.rb +4 -4
- data/lib/sequel/plugins/validation_class_methods.rb +1 -1
- data/lib/sequel/plugins/validation_helpers.rb +1 -1
- data/lib/sequel/plugins/xml_serializer.rb +2 -2
- data/lib/sequel/sql.rb +69 -36
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/db2_spec.rb +10 -0
- data/spec/adapters/firebird_spec.rb +1 -1
- data/spec/adapters/mssql_spec.rb +4 -5
- data/spec/adapters/mysql_spec.rb +9 -9
- data/spec/adapters/postgres_spec.rb +67 -68
- data/spec/adapters/spec_helper.rb +6 -1
- data/spec/adapters/sqlite_spec.rb +29 -15
- data/spec/core/connection_pool_spec.rb +14 -14
- data/spec/core/database_spec.rb +38 -180
- data/spec/core/dataset_mutation_spec.rb +253 -0
- data/spec/core/dataset_spec.rb +394 -537
- data/spec/core/expression_filters_spec.rb +34 -32
- data/spec/core/mock_adapter_spec.rb +27 -35
- data/spec/core/placeholder_literalizer_spec.rb +2 -4
- data/spec/core/schema_generator_spec.rb +4 -4
- data/spec/core/schema_spec.rb +1 -2
- data/spec/core_extensions_spec.rb +22 -29
- data/spec/extensions/active_model_spec.rb +6 -6
- data/spec/extensions/association_dependencies_spec.rb +2 -2
- data/spec/extensions/blacklist_security_spec.rb +3 -3
- data/spec/extensions/boolean_readers_spec.rb +12 -12
- data/spec/extensions/caching_spec.rb +13 -10
- data/spec/extensions/class_table_inheritance_spec.rb +38 -43
- data/spec/extensions/column_conflicts_spec.rb +1 -3
- data/spec/extensions/columns_introspection_spec.rb +2 -3
- data/spec/extensions/composition_spec.rb +5 -3
- data/spec/extensions/constraint_validations_plugin_spec.rb +5 -5
- data/spec/extensions/constraint_validations_spec.rb +14 -8
- data/spec/extensions/core_refinements_spec.rb +22 -29
- data/spec/extensions/csv_serializer_spec.rb +7 -6
- data/spec/extensions/date_arithmetic_spec.rb +15 -15
- data/spec/extensions/defaults_setter_spec.rb +2 -2
- data/spec/extensions/delay_add_association_spec.rb +1 -1
- data/spec/extensions/dirty_spec.rb +19 -10
- data/spec/extensions/duplicate_columns_handler_spec.rb +12 -18
- data/spec/extensions/eager_each_spec.rb +12 -16
- data/spec/extensions/empty_array_consider_nulls_spec.rb +1 -1
- data/spec/extensions/eval_inspect_spec.rb +4 -3
- data/spec/extensions/force_encoding_spec.rb +12 -12
- data/spec/extensions/freeze_datasets_spec.rb +31 -0
- data/spec/extensions/graph_each_spec.rb +6 -18
- data/spec/extensions/hook_class_methods_spec.rb +7 -7
- data/spec/extensions/identifier_mangling_spec.rb +307 -0
- data/spec/extensions/instance_filters_spec.rb +5 -6
- data/spec/extensions/instance_hooks_spec.rb +12 -12
- data/spec/extensions/json_serializer_spec.rb +12 -15
- data/spec/extensions/lazy_attributes_spec.rb +4 -4
- data/spec/extensions/list_spec.rb +19 -21
- data/spec/extensions/many_through_many_spec.rb +108 -163
- data/spec/extensions/meta_def_spec.rb +7 -2
- data/spec/extensions/migration_spec.rb +10 -12
- data/spec/extensions/mssql_optimistic_locking_spec.rb +4 -3
- data/spec/extensions/named_timezones_spec.rb +4 -3
- data/spec/extensions/nested_attributes_spec.rb +2 -2
- data/spec/extensions/null_dataset_spec.rb +17 -12
- data/spec/extensions/optimistic_locking_spec.rb +4 -5
- data/spec/extensions/pagination_spec.rb +8 -10
- data/spec/extensions/pg_array_associations_spec.rb +28 -27
- data/spec/extensions/pg_array_ops_spec.rb +2 -1
- data/spec/extensions/pg_array_spec.rb +6 -2
- data/spec/extensions/pg_enum_spec.rb +5 -3
- data/spec/extensions/pg_hstore_ops_spec.rb +3 -1
- data/spec/extensions/pg_hstore_spec.rb +7 -6
- data/spec/extensions/pg_inet_ops_spec.rb +2 -1
- data/spec/extensions/pg_inet_spec.rb +2 -1
- data/spec/extensions/pg_interval_spec.rb +2 -1
- data/spec/extensions/pg_json_ops_spec.rb +2 -1
- data/spec/extensions/pg_json_spec.rb +6 -3
- data/spec/extensions/pg_loose_count_spec.rb +1 -0
- data/spec/extensions/pg_range_ops_spec.rb +3 -1
- data/spec/extensions/pg_range_spec.rb +9 -5
- data/spec/extensions/pg_row_ops_spec.rb +2 -1
- data/spec/extensions/pg_row_plugin_spec.rb +4 -6
- data/spec/extensions/pg_row_spec.rb +5 -3
- data/spec/extensions/pg_static_cache_updater_spec.rb +2 -1
- data/spec/extensions/pg_typecast_on_load_spec.rb +1 -1
- data/spec/extensions/prepared_statements_associations_spec.rb +1 -1
- data/spec/extensions/prepared_statements_spec.rb +12 -11
- data/spec/extensions/pretty_table_spec.rb +1 -1
- data/spec/extensions/query_spec.rb +8 -5
- data/spec/extensions/rcte_tree_spec.rb +39 -39
- data/spec/extensions/round_timestamps_spec.rb +2 -2
- data/spec/extensions/schema_dumper_spec.rb +3 -2
- data/spec/extensions/schema_spec.rb +2 -2
- data/spec/extensions/scissors_spec.rb +1 -2
- data/spec/extensions/sequel_3_dataset_methods_spec.rb +30 -17
- data/spec/extensions/serialization_modification_detection_spec.rb +2 -2
- data/spec/extensions/serialization_spec.rb +15 -13
- data/spec/extensions/set_overrides_spec.rb +14 -8
- data/spec/extensions/sharding_spec.rb +9 -18
- data/spec/extensions/shared_caching_spec.rb +3 -4
- data/spec/extensions/single_table_inheritance_spec.rb +11 -11
- data/spec/extensions/skip_create_refresh_spec.rb +2 -1
- data/spec/extensions/spec_helper.rb +1 -1
- data/spec/extensions/split_values_spec.rb +2 -2
- data/spec/extensions/sql_comments_spec.rb +6 -0
- data/spec/extensions/static_cache_spec.rb +7 -9
- data/spec/extensions/string_agg_spec.rb +30 -29
- data/spec/extensions/tactical_eager_loading_spec.rb +4 -5
- data/spec/extensions/thread_local_timezones_spec.rb +2 -2
- data/spec/extensions/timestamps_spec.rb +28 -3
- data/spec/extensions/to_dot_spec.rb +1 -2
- data/spec/extensions/tree_spec.rb +33 -29
- data/spec/extensions/typecast_on_load_spec.rb +1 -1
- data/spec/extensions/unlimited_update_spec.rb +1 -0
- data/spec/extensions/update_primary_key_spec.rb +11 -7
- data/spec/extensions/update_refresh_spec.rb +1 -1
- data/spec/extensions/uuid_spec.rb +0 -1
- data/spec/extensions/validate_associated_spec.rb +1 -1
- data/spec/extensions/validation_class_methods_spec.rb +10 -10
- data/spec/extensions/validation_helpers_spec.rb +10 -10
- data/spec/extensions/xml_serializer_spec.rb +7 -3
- data/spec/integration/associations_test.rb +31 -31
- data/spec/integration/dataset_test.rb +17 -19
- data/spec/integration/eager_loader_test.rb +24 -24
- data/spec/integration/model_test.rb +6 -6
- data/spec/integration/plugin_test.rb +43 -43
- data/spec/integration/prepared_statement_test.rb +6 -6
- data/spec/integration/schema_test.rb +63 -52
- data/spec/integration/spec_helper.rb +6 -1
- data/spec/integration/transaction_test.rb +13 -13
- data/spec/model/association_reflection_spec.rb +17 -17
- data/spec/model/associations_spec.rb +101 -96
- data/spec/model/base_spec.rb +175 -49
- data/spec/model/class_dataset_methods_spec.rb +5 -9
- data/spec/model/dataset_methods_spec.rb +5 -5
- data/spec/model/eager_loading_spec.rb +209 -235
- data/spec/model/hooks_spec.rb +15 -15
- data/spec/model/model_spec.rb +28 -21
- data/spec/model/plugins_spec.rb +4 -5
- data/spec/model/record_spec.rb +59 -57
- data/spec/model/spec_helper.rb +1 -1
- data/spec/model/validations_spec.rb +6 -6
- data/spec/spec_config.rb +1 -1
- metadata +10 -2
data/lib/sequel/dataset/query.rb
CHANGED
@@ -18,7 +18,7 @@ module Sequel
|
|
18
18
|
|
19
19
|
# Which options don't affect the SQL generation. Used by simple_select_all?
|
20
20
|
# to determine if this is a simple SELECT * FROM table.
|
21
|
-
NON_SQL_OPTIONS = [:server, :graph, :eager, :eager_graph, :graph_aliases].freeze
|
21
|
+
NON_SQL_OPTIONS = [:server, :graph, :eager, :eager_graph, :graph_aliases, :row_proc, :quote_identifiers, :identifier_input_method, :identifier_output_method, :skip_symbol_cache, :model, :model_object, :association_reflection, :fetch, :numrows, :autoid].freeze
|
22
22
|
|
23
23
|
# These symbols have _join methods created (e.g. inner_join) that
|
24
24
|
# call join_table with the symbol, passing along the arguments and
|
@@ -69,19 +69,44 @@ module Sequel
|
|
69
69
|
where(*cond, &block)
|
70
70
|
end
|
71
71
|
|
72
|
-
#
|
73
|
-
#
|
74
|
-
#
|
75
|
-
#
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
72
|
+
# On Ruby 2.4+, use clone(:freeze=>false) to create clones, because
|
73
|
+
# we use true freezing in that case, and we need to modify the opts
|
74
|
+
# in the frozen copy.
|
75
|
+
#
|
76
|
+
# On Ruby <2.4, just use Object#clone directly, since we don't
|
77
|
+
# use true freezing as it isn't possible.
|
78
|
+
if TRUE_FREEZE
|
79
|
+
# Save original clone implementation, as some other methods need
|
80
|
+
# to call it internally.
|
81
|
+
alias _clone clone
|
82
|
+
private :_clone
|
83
|
+
|
84
|
+
# Returns a new clone of the dataset with the given options merged.
|
85
|
+
# If the options changed include options in COLUMN_CHANGE_OPTS, the cached
|
86
|
+
# columns are deleted. This method should generally not be called
|
87
|
+
# directly by user code.
|
88
|
+
def clone(opts = OPTS)
|
89
|
+
c = super(:freeze=>false)
|
90
|
+
c.opts.merge!(opts)
|
91
|
+
unless opts.each_key{|o| break if COLUMN_CHANGE_OPTS.include?(o)}
|
92
|
+
c.clear_columns_cache
|
93
|
+
end
|
94
|
+
c.freeze if frozen?
|
95
|
+
c
|
83
96
|
end
|
84
|
-
|
97
|
+
else
|
98
|
+
# :nocov:
|
99
|
+
# :nodoc:
|
100
|
+
def clone(opts = OPTS)
|
101
|
+
c = super()
|
102
|
+
c.opts.merge!(opts)
|
103
|
+
unless opts.each_key{|o| break if COLUMN_CHANGE_OPTS.include?(o)}
|
104
|
+
c.clear_columns_cache
|
105
|
+
end
|
106
|
+
c.freeze if frozen?
|
107
|
+
c
|
108
|
+
end
|
109
|
+
# :nocov:
|
85
110
|
end
|
86
111
|
|
87
112
|
# Returns a copy of the dataset with the SQL DISTINCT clause. The DISTINCT
|
@@ -97,7 +122,7 @@ module Sequel
|
|
97
122
|
def distinct(*args, &block)
|
98
123
|
virtual_row_columns(args, block)
|
99
124
|
raise(InvalidOperation, "DISTINCT ON not supported") if !args.empty? && !supports_distinct_on?
|
100
|
-
clone(:distinct => args)
|
125
|
+
clone(:distinct => args.freeze)
|
101
126
|
end
|
102
127
|
|
103
128
|
# Adds an EXCEPT clause using a second dataset object.
|
@@ -131,6 +156,22 @@ module Sequel
|
|
131
156
|
#
|
132
157
|
# DB[:items].exclude(:category => 'software', :id=>3)
|
133
158
|
# # SELECT * FROM items WHERE ((category != 'software') OR (id != 3))
|
159
|
+
#
|
160
|
+
# Also note that SQL uses 3-valued boolean logic (+true+, +false+, +NULL+), so
|
161
|
+
# the inverse of a true condition is a false condition, and will still
|
162
|
+
# not match rows that were NULL originally. If you take the earlier
|
163
|
+
# example:
|
164
|
+
#
|
165
|
+
# DB[:items].exclude(:category => 'software')
|
166
|
+
# # SELECT * FROM items WHERE (category != 'software')
|
167
|
+
#
|
168
|
+
# Note that this does not match rows where +category+ is +NULL+. This
|
169
|
+
# is because +NULL+ is an unknown value, and you do not know whether
|
170
|
+
# or not the +NULL+ category is +software+. You can explicitly
|
171
|
+
# specify how to handle +NULL+ values if you want:
|
172
|
+
#
|
173
|
+
# DB[:items].exclude(Sequel.~(:category=>nil) & {:category => 'software'})
|
174
|
+
# # SELECT * FROM items WHERE ((category IS NULL) OR (category != 'software'))
|
134
175
|
def exclude(*cond, &block)
|
135
176
|
_filter_or_exclude(true, :where, *cond, &block)
|
136
177
|
end
|
@@ -139,6 +180,9 @@ module Sequel
|
|
139
180
|
#
|
140
181
|
# DB[:items].select_group(:name).exclude_having{count(name) < 2}
|
141
182
|
# # SELECT name FROM items GROUP BY name HAVING (count(name) >= 2)
|
183
|
+
#
|
184
|
+
# See documentation for exclude for how inversion is handled in regards
|
185
|
+
# to SQL 3-valued boolean logic.
|
142
186
|
def exclude_having(*cond, &block)
|
143
187
|
_filter_or_exclude(true, :having, *cond, &block)
|
144
188
|
end
|
@@ -148,13 +192,25 @@ module Sequel
|
|
148
192
|
exclude(*cond, &block)
|
149
193
|
end
|
150
194
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
195
|
+
if TRUE_FREEZE
|
196
|
+
# Return a clone of the dataset loaded with the given dataset extensions.
|
197
|
+
# If no related extension file exists or the extension does not have
|
198
|
+
# specific support for Dataset objects, an Error will be raised.
|
199
|
+
def extension(*a)
|
200
|
+
c = _clone(:freeze=>false)
|
201
|
+
c.send(:_extension!, a)
|
202
|
+
c.freeze if frozen?
|
203
|
+
c
|
204
|
+
end
|
205
|
+
else
|
206
|
+
# :nocov:
|
207
|
+
# :nodoc:
|
208
|
+
def extension(*exts)
|
209
|
+
c = clone
|
210
|
+
c.send(:_extension!, exts)
|
211
|
+
c
|
212
|
+
end
|
213
|
+
# :nocov:
|
158
214
|
end
|
159
215
|
|
160
216
|
# Alias for where.
|
@@ -166,7 +222,7 @@ module Sequel
|
|
166
222
|
#
|
167
223
|
# DB[:table].for_update # SELECT * FROM table FOR UPDATE
|
168
224
|
def for_update
|
169
|
-
lock_style(:update)
|
225
|
+
cached_dataset(:_for_update_ds){lock_style(:update)}
|
170
226
|
end
|
171
227
|
|
172
228
|
# Returns a copy of the dataset with the source changed. If no
|
@@ -203,8 +259,8 @@ module Sequel
|
|
203
259
|
s
|
204
260
|
end
|
205
261
|
end
|
206
|
-
o = {:from=>source.empty? ? nil : source}
|
207
|
-
o[:with] = (opts[:with] || []) + ctes if ctes
|
262
|
+
o = {:from=>source.empty? ? nil : source.freeze}
|
263
|
+
o[:with] = ((opts[:with] || []) + ctes).freeze if ctes
|
208
264
|
o[:num_dataset_sources] = table_alias_num if table_alias_num > 0
|
209
265
|
clone(o)
|
210
266
|
end
|
@@ -288,7 +344,7 @@ module Sequel
|
|
288
344
|
# DB[:items].group{[a, sum(b)]} # SELECT * FROM items GROUP BY a, sum(b)
|
289
345
|
def group(*columns, &block)
|
290
346
|
virtual_row_columns(columns, block)
|
291
|
-
clone(:group => (columns.compact.empty? ? nil : columns))
|
347
|
+
clone(:group => (columns.compact.empty? ? nil : columns.freeze))
|
292
348
|
end
|
293
349
|
|
294
350
|
# Alias of group
|
@@ -318,7 +374,7 @@ module Sequel
|
|
318
374
|
# # SELECT substr(first_name, 1, 1) AS initial, count(*) AS count FROM items GROUP BY substr(first_name, 1, 1)
|
319
375
|
# # => [{:initial=>'a', :count=>1}, ...]
|
320
376
|
def group_and_count(*columns, &block)
|
321
|
-
select_group(*columns, &block).
|
377
|
+
select_group(*columns, &block).select_append(COUNT_OF_ALL_AS_COUNT)
|
322
378
|
end
|
323
379
|
|
324
380
|
# Returns a copy of the dataset with the given columns added to the list of
|
@@ -389,15 +445,20 @@ module Sequel
|
|
389
445
|
#
|
390
446
|
# DB[:items].where(:category => 'software', :id=>3).invert
|
391
447
|
# # SELECT * FROM items WHERE ((category != 'software') OR (id != 3))
|
448
|
+
#
|
449
|
+
# See documentation for exclude for how inversion is handled in regards
|
450
|
+
# to SQL 3-valued boolean logic.
|
392
451
|
def invert
|
393
|
-
|
394
|
-
|
395
|
-
where
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
452
|
+
cached_dataset(:_invert_ds) do
|
453
|
+
having, where = @opts.values_at(:having, :where)
|
454
|
+
if having.nil? && where.nil?
|
455
|
+
where(false)
|
456
|
+
else
|
457
|
+
o = {}
|
458
|
+
o[:having] = SQL::BooleanExpression.invert(having) if having
|
459
|
+
o[:where] = SQL::BooleanExpression.invert(where) if where
|
460
|
+
clone(o)
|
461
|
+
end
|
401
462
|
end
|
402
463
|
end
|
403
464
|
|
@@ -535,7 +596,7 @@ module Sequel
|
|
535
596
|
SQL::JoinOnClause.new(expr, type, table_expr)
|
536
597
|
end
|
537
598
|
|
538
|
-
opts = {:join => (@opts[:join] || []) + [join]}
|
599
|
+
opts = {:join => ((@opts[:join] || []) + [join]).freeze}
|
539
600
|
opts[:last_joined_table] = table_name unless options[:reset_implicit_qualifier] == false
|
540
601
|
opts[:num_dataset_sources] = table_alias_num if table_alias_num
|
541
602
|
clone(opts)
|
@@ -615,7 +676,7 @@ module Sequel
|
|
615
676
|
# ds.all # => [{2=>:id}]
|
616
677
|
# ds.naked.all # => [{:id=>2}]
|
617
678
|
def naked
|
618
|
-
with_row_proc(nil)
|
679
|
+
cached_dataset(:_naked_ds){with_row_proc(nil)}
|
619
680
|
end
|
620
681
|
|
621
682
|
# Returns a copy of the dataset with a specified order. Can be safely combined with limit.
|
@@ -661,12 +722,17 @@ module Sequel
|
|
661
722
|
# DB[:items].order(nil) # SELECT * FROM items
|
662
723
|
def order(*columns, &block)
|
663
724
|
virtual_row_columns(columns, block)
|
664
|
-
clone(:order => (columns.compact.empty?) ? nil : columns)
|
725
|
+
clone(:order => (columns.compact.empty?) ? nil : columns.freeze)
|
665
726
|
end
|
666
727
|
|
667
|
-
#
|
728
|
+
# Returns a copy of the dataset with the order columns added
|
729
|
+
# to the end of the existing order.
|
730
|
+
#
|
731
|
+
# DB[:items].order(:a).order(:b) # SELECT * FROM items ORDER BY b
|
732
|
+
# DB[:items].order(:a).order_append(:b) # SELECT * FROM items ORDER BY a, b
|
668
733
|
def order_append(*columns, &block)
|
669
|
-
|
734
|
+
columns = @opts[:order] + columns if @opts[:order]
|
735
|
+
order(*columns, &block)
|
670
736
|
end
|
671
737
|
|
672
738
|
# Alias of order
|
@@ -674,14 +740,9 @@ module Sequel
|
|
674
740
|
order(*columns, &block)
|
675
741
|
end
|
676
742
|
|
677
|
-
#
|
678
|
-
# to the end of the existing order.
|
679
|
-
#
|
680
|
-
# DB[:items].order(:a).order(:b) # SELECT * FROM items ORDER BY b
|
681
|
-
# DB[:items].order(:a).order_more(:b) # SELECT * FROM items ORDER BY a, b
|
743
|
+
# Alias of order_append.
|
682
744
|
def order_more(*columns, &block)
|
683
|
-
columns
|
684
|
-
order(*columns, &block)
|
745
|
+
order_append(*columns, &block)
|
685
746
|
end
|
686
747
|
|
687
748
|
# Returns a copy of the dataset with the order columns added
|
@@ -691,7 +752,7 @@ module Sequel
|
|
691
752
|
# DB[:items].order(:a).order_prepend(:b) # SELECT * FROM items ORDER BY b, a
|
692
753
|
def order_prepend(*columns, &block)
|
693
754
|
ds = order(*columns, &block)
|
694
|
-
@opts[:order] ? ds.
|
755
|
+
@opts[:order] ? ds.order_append(*@opts[:order]) : ds
|
695
756
|
end
|
696
757
|
|
697
758
|
# Qualify to the given table, or first source if no table is given.
|
@@ -708,7 +769,7 @@ module Sequel
|
|
708
769
|
(o.keys & QUALIFY_KEYS).each do |k|
|
709
770
|
h[k] = qualified_expression(o[k], table)
|
710
771
|
end
|
711
|
-
h[:select] = [SQL::ColumnAll.new(table)] if !o[:select] || o[:select].empty?
|
772
|
+
h[:select] = [SQL::ColumnAll.new(table)].freeze if !o[:select] || o[:select].empty?
|
712
773
|
clone(h)
|
713
774
|
end
|
714
775
|
|
@@ -722,7 +783,7 @@ module Sequel
|
|
722
783
|
# DB[:items].returning(:id, :name) # RETURNING id, name
|
723
784
|
def returning(*values)
|
724
785
|
raise Error, "RETURNING is not supported on #{db.database_type}" unless supports_returning?(:insert)
|
725
|
-
clone(:returning=>values)
|
786
|
+
clone(:returning=>values.freeze)
|
726
787
|
end
|
727
788
|
|
728
789
|
# Returns a copy of the dataset with the order reversed. If no order is
|
@@ -733,8 +794,12 @@ module Sequel
|
|
733
794
|
# DB[:items].order(:id).reverse # SELECT * FROM items ORDER BY id DESC
|
734
795
|
# DB[:items].order(:id).reverse(Sequel.desc(:name)) # SELECT * FROM items ORDER BY name ASC
|
735
796
|
def reverse(*order, &block)
|
736
|
-
|
737
|
-
|
797
|
+
if order.empty? && !block
|
798
|
+
cached_dataset(:_reverse_ds){order(*invert_order(@opts[:order]))}
|
799
|
+
else
|
800
|
+
virtual_row_columns(order, block)
|
801
|
+
order(*invert_order(order.empty? ? @opts[:order] : order.freeze))
|
802
|
+
end
|
738
803
|
end
|
739
804
|
|
740
805
|
# Alias of +reverse+
|
@@ -751,7 +816,7 @@ module Sequel
|
|
751
816
|
# DB[:items].select{[a, sum(b)]} # SELECT a, sum(b) FROM items
|
752
817
|
def select(*columns, &block)
|
753
818
|
virtual_row_columns(columns, block)
|
754
|
-
clone(:select => columns)
|
819
|
+
clone(:select => columns.freeze)
|
755
820
|
end
|
756
821
|
|
757
822
|
# Returns a copy of the dataset selecting the wildcard if no arguments
|
@@ -765,7 +830,7 @@ module Sequel
|
|
765
830
|
if tables.empty?
|
766
831
|
clone(:select => nil)
|
767
832
|
else
|
768
|
-
select(*tables.map{|t| i, a = split_alias(t); a || i}.map{|t| SQL::ColumnAll.new(t)})
|
833
|
+
select(*tables.map{|t| i, a = split_alias(t); a || i}.map!{|t| SQL::ColumnAll.new(t)}.freeze)
|
769
834
|
end
|
770
835
|
end
|
771
836
|
|
@@ -780,7 +845,7 @@ module Sequel
|
|
780
845
|
cur_sel = @opts[:select]
|
781
846
|
if !cur_sel || cur_sel.empty?
|
782
847
|
unless supports_select_all_and_column?
|
783
|
-
return select_all(*(Array(@opts[:from]) + Array(@opts[:join]))).
|
848
|
+
return select_all(*(Array(@opts[:from]) + Array(@opts[:join]))).select_append(*columns, &block)
|
784
849
|
end
|
785
850
|
cur_sel = [WILDCARD]
|
786
851
|
end
|
@@ -832,8 +897,10 @@ module Sequel
|
|
832
897
|
|
833
898
|
# Skip locked rows when returning results from this dataset.
|
834
899
|
def skip_locked
|
835
|
-
|
836
|
-
|
900
|
+
cached_dataset(:_skip_locked_ds) do
|
901
|
+
raise(Error, 'This dataset does not support skipping locked rows') unless supports_skip_locked?
|
902
|
+
clone(:skip_locked=>true)
|
903
|
+
end
|
837
904
|
end
|
838
905
|
|
839
906
|
# Unbind bound variables from this dataset's filter and return an array of two
|
@@ -857,7 +924,7 @@ module Sequel
|
|
857
924
|
# DB[:items].group(:a).having(:a=>1).where(:b).unfiltered
|
858
925
|
# # SELECT * FROM items GROUP BY a
|
859
926
|
def unfiltered
|
860
|
-
clone(:where => nil, :having => nil)
|
927
|
+
cached_dataset(:_unfiltered_ds){clone(:where => nil, :having => nil)}
|
861
928
|
end
|
862
929
|
|
863
930
|
# Returns a copy of the dataset with no grouping (GROUP or HAVING clause) applied.
|
@@ -865,7 +932,7 @@ module Sequel
|
|
865
932
|
# DB[:items].group(:a).having(:a=>1).where(:b).ungrouped
|
866
933
|
# # SELECT * FROM items WHERE b
|
867
934
|
def ungrouped
|
868
|
-
clone(:group => nil, :having => nil)
|
935
|
+
cached_dataset(:_ungrouped_ds){clone(:group => nil, :having => nil)}
|
869
936
|
end
|
870
937
|
|
871
938
|
# Adds a UNION clause using a second dataset object.
|
@@ -892,14 +959,14 @@ module Sequel
|
|
892
959
|
#
|
893
960
|
# DB[:items].limit(10, 20).unlimited # SELECT * FROM items
|
894
961
|
def unlimited
|
895
|
-
clone(:limit=>nil, :offset=>nil)
|
962
|
+
cached_dataset(:_unlimited_ds){clone(:limit=>nil, :offset=>nil)}
|
896
963
|
end
|
897
964
|
|
898
965
|
# Returns a copy of the dataset with no order.
|
899
966
|
#
|
900
967
|
# DB[:items].order(:a).unordered # SELECT * FROM items
|
901
968
|
def unordered
|
902
|
-
order
|
969
|
+
cached_dataset(:_unordered_ds){clone(:order=>nil)}
|
903
970
|
end
|
904
971
|
|
905
972
|
# Returns a copy of the dataset with the given WHERE conditions imposed upon it.
|
@@ -970,7 +1037,7 @@ module Sequel
|
|
970
1037
|
s, ds = hoist_cte(dataset)
|
971
1038
|
s.with(name, ds, opts)
|
972
1039
|
else
|
973
|
-
clone(:with=>(@opts[:with]||[]) + [Hash[opts].merge!(:name=>name, :dataset=>dataset)])
|
1040
|
+
clone(:with=>((@opts[:with]||[]) + [Hash[opts].merge!(:name=>name, :dataset=>dataset)]).freeze)
|
974
1041
|
end
|
975
1042
|
end
|
976
1043
|
|
@@ -999,15 +1066,33 @@ module Sequel
|
|
999
1066
|
s, ds = hoist_cte(recursive)
|
1000
1067
|
s.with_recursive(name, nonrecursive, ds, opts)
|
1001
1068
|
else
|
1002
|
-
clone(:with=>(@opts[:with]||[]) + [Hash[opts].merge!(:recursive=>true, :name=>name, :dataset=>nonrecursive.union(recursive, {:all=>opts[:union_all] != false, :from_self=>false}))])
|
1069
|
+
clone(:with=>((@opts[:with]||[]) + [Hash[opts].merge!(:recursive=>true, :name=>name, :dataset=>nonrecursive.union(recursive, {:all=>opts[:union_all] != false, :from_self=>false}))]).freeze)
|
1003
1070
|
end
|
1004
1071
|
end
|
1005
1072
|
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1073
|
+
if TRUE_FREEZE
|
1074
|
+
# Return a clone of the dataset extended with the given modules.
|
1075
|
+
# Note that like Object#extend, when multiple modules are provided
|
1076
|
+
# as arguments the cloned dataset is extended with the modules in reverse
|
1077
|
+
# order. If a block is provided, a module is created using the block and
|
1078
|
+
# the clone is extended with that module after any modules given as arguments.
|
1079
|
+
def with_extend(*mods, &block)
|
1080
|
+
c = _clone(:freeze=>false)
|
1081
|
+
c.extend(*mods) unless mods.empty?
|
1082
|
+
c.extend(Module.new(&block)) if block
|
1083
|
+
c.freeze if frozen?
|
1084
|
+
c
|
1085
|
+
end
|
1086
|
+
else
|
1087
|
+
# :nocov:
|
1088
|
+
# :nodoc:
|
1089
|
+
def with_extend(*mods, &block)
|
1090
|
+
c = clone
|
1091
|
+
c.extend(*mods) unless mods.empty?
|
1092
|
+
c.extend(Module.new(&block)) if block
|
1093
|
+
c
|
1094
|
+
end
|
1095
|
+
# :nocov:
|
1011
1096
|
end
|
1012
1097
|
|
1013
1098
|
# Returns a cloned dataset with the given row_proc.
|
@@ -1016,9 +1101,7 @@ module Sequel
|
|
1016
1101
|
# ds.all # => [{:id=>2}]
|
1017
1102
|
# ds.with_row_proc(proc(&:invert)).all # => [{2=>:id}]
|
1018
1103
|
def with_row_proc(callable)
|
1019
|
-
|
1020
|
-
c.instance_variable_set(:@row_proc, callable)
|
1021
|
-
c
|
1104
|
+
clone(:row_proc=>callable)
|
1022
1105
|
end
|
1023
1106
|
|
1024
1107
|
# Returns a copy of the dataset with the static SQL used. This is useful if you want
|
@@ -1050,7 +1133,7 @@ module Sequel
|
|
1050
1133
|
s, ds = hoist_cte(dataset)
|
1051
1134
|
return s.compound_clone(type, ds, opts)
|
1052
1135
|
end
|
1053
|
-
ds = compound_from_self.clone(:compounds=>Array(@opts[:compounds]).map(&:dup) + [[type, dataset.compound_from_self, opts[:all]]])
|
1136
|
+
ds = compound_from_self.clone(:compounds=>(Array(@opts[:compounds]).map(&:dup) + [[type, dataset.compound_from_self, opts[:all]].freeze]).freeze)
|
1054
1137
|
opts[:from_self] == false ? ds : ds.from_self(opts)
|
1055
1138
|
end
|
1056
1139
|
|
@@ -1152,7 +1235,7 @@ module Sequel
|
|
1152
1235
|
# clause from the given dataset added to it, and the second a clone of
|
1153
1236
|
# the given dataset with the WITH clause removed.
|
1154
1237
|
def hoist_cte(ds)
|
1155
|
-
[clone(:with => (opts[:with] || []) + ds.opts[:with]), ds.clone(:with => nil)]
|
1238
|
+
[clone(:with => ((opts[:with] || []) + ds.opts[:with]).freeze), ds.clone(:with => nil)]
|
1156
1239
|
end
|
1157
1240
|
|
1158
1241
|
# Whether CTEs need to be hoisted from the given ds into the current ds.
|
data/lib/sequel/dataset/sql.rb
CHANGED
@@ -198,11 +198,13 @@ module Sequel
|
|
198
198
|
# being an array of symbol/strings for the appropriate branch.
|
199
199
|
def self.def_sql_method(mod, type, clauses)
|
200
200
|
priv = type == :update || type == :insert
|
201
|
+
cacheable = type == :select || type == :delete
|
201
202
|
|
202
203
|
lines = []
|
203
204
|
lines << 'private' if priv
|
204
205
|
lines << "def #{'_' if priv}#{type}_sql"
|
205
206
|
lines << 'if sql = opts[:sql]; return static_sql(sql) end' unless priv
|
207
|
+
lines << "if sql = cache_get(:_#{type}_sql); return sql end" if cacheable
|
206
208
|
lines << 'check_modification_allowed!' if type == :delete
|
207
209
|
lines << 'sql = @opts[:append_sql] || sql_string_origin'
|
208
210
|
|
@@ -216,6 +218,7 @@ module Sequel
|
|
216
218
|
lines.concat(clause_methods(type, clauses).map{|x| "#{x}(sql)"})
|
217
219
|
end
|
218
220
|
|
221
|
+
lines << "cache_set(:_#{type}_sql, sql) if cache_sql?" if cacheable
|
219
222
|
lines << 'sql'
|
220
223
|
lines << 'end'
|
221
224
|
|
@@ -522,6 +525,11 @@ module Sequel
|
|
522
525
|
# Append literalization of delayed evaluation to SQL string,
|
523
526
|
# causing the delayed evaluation proc to be evaluated.
|
524
527
|
def delayed_evaluation_sql_append(sql, delay)
|
528
|
+
# Delayed evaluations are used specifically so the SQL
|
529
|
+
# can differ in subsequent calls, so we definitely don't
|
530
|
+
# want to cache the sql in this case.
|
531
|
+
disable_sql_caching!
|
532
|
+
|
525
533
|
if recorder = @opts[:placeholder_literalizer]
|
526
534
|
recorder.use(sql, lambda{delay.call(self)}, nil)
|
527
535
|
else
|
@@ -901,6 +909,12 @@ module Sequel
|
|
901
909
|
end
|
902
910
|
end
|
903
911
|
|
912
|
+
# Only allow caching the select SQL if the dataset is frozen and hasn't
|
913
|
+
# specifically been marked as not allowing SQL caching.
|
914
|
+
def cache_sql?
|
915
|
+
frozen? && !@opts[:no_cache_sql] && !cache_get(:_no_cache_sql)
|
916
|
+
end
|
917
|
+
|
904
918
|
# Raise an InvalidOperation exception if deletion is not allowed
|
905
919
|
# for this dataset
|
906
920
|
def check_modification_allowed!
|
@@ -993,6 +1007,11 @@ module Sequel
|
|
993
1007
|
end
|
994
1008
|
end
|
995
1009
|
|
1010
|
+
# Disable caching of SQL for the current dataset
|
1011
|
+
def disable_sql_caching!
|
1012
|
+
cache_set(:_no_cache_sql, true)
|
1013
|
+
end
|
1014
|
+
|
996
1015
|
# An SQL FROM clause to use in SELECT statements where the dataset has
|
997
1016
|
# no from tables.
|
998
1017
|
def empty_from_sql
|
@@ -1099,10 +1118,9 @@ module Sequel
|
|
1099
1118
|
end
|
1100
1119
|
end
|
1101
1120
|
|
1102
|
-
#
|
1103
|
-
# identifier_output_method.
|
1121
|
+
# Upcase identifiers by default when inputting them into the database.
|
1104
1122
|
def input_identifier(v)
|
1105
|
-
|
1123
|
+
v.to_s.upcase
|
1106
1124
|
end
|
1107
1125
|
|
1108
1126
|
def insert_into_sql(sql)
|
@@ -1250,6 +1268,10 @@ module Sequel
|
|
1250
1268
|
# calls +sql_literal+ if object responds to it, otherwise raises an error.
|
1251
1269
|
# If a database specific type is allowed, this should be overriden in a subclass.
|
1252
1270
|
def literal_other_append(sql, v)
|
1271
|
+
# We can't be sure if v will always literalize to the same SQL, so
|
1272
|
+
# don't cache SQL for a dataset that uses this.
|
1273
|
+
disable_sql_caching!
|
1274
|
+
|
1253
1275
|
if v.respond_to?(:sql_literal_append)
|
1254
1276
|
v.sql_literal_append(self, sql)
|
1255
1277
|
elsif v.respond_to?(:sql_literal)
|
@@ -1496,12 +1518,7 @@ module Sequel
|
|
1496
1518
|
|
1497
1519
|
# Whether the symbol cache should be skipped when literalizing the dataset
|
1498
1520
|
def skip_symbol_cache?
|
1499
|
-
@skip_symbol_cache
|
1500
|
-
end
|
1501
|
-
|
1502
|
-
# Set the dataset to skip the symbol cache
|
1503
|
-
def skip_symbol_cache!
|
1504
|
-
@skip_symbol_cache = true
|
1521
|
+
@opts[:skip_symbol_cache]
|
1505
1522
|
end
|
1506
1523
|
|
1507
1524
|
# Append literalization of array of sources/tables to SQL string, raising an Error if there
|