sequel 5.43.0 → 5.47.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +40 -0
- data/README.rdoc +1 -2
- data/doc/association_basics.rdoc +70 -11
- data/doc/migration.rdoc +11 -5
- data/doc/release_notes/5.44.0.txt +32 -0
- data/doc/release_notes/5.45.0.txt +34 -0
- data/doc/release_notes/5.46.0.txt +87 -0
- data/doc/release_notes/5.47.0.txt +59 -0
- data/doc/sql.rdoc +12 -0
- data/doc/testing.rdoc +5 -0
- data/doc/virtual_rows.rdoc +1 -1
- data/lib/sequel/adapters/odbc.rb +5 -1
- data/lib/sequel/adapters/shared/mysql.rb +17 -0
- data/lib/sequel/adapters/shared/postgres.rb +0 -12
- data/lib/sequel/adapters/shared/sqlite.rb +55 -9
- data/lib/sequel/core.rb +11 -0
- data/lib/sequel/database/schema_generator.rb +25 -46
- data/lib/sequel/database/schema_methods.rb +1 -1
- data/lib/sequel/dataset/query.rb +2 -4
- data/lib/sequel/dataset/sql.rb +7 -0
- data/lib/sequel/extensions/date_arithmetic.rb +29 -15
- data/lib/sequel/extensions/pg_enum.rb +1 -1
- data/lib/sequel/extensions/pg_loose_count.rb +3 -1
- data/lib/sequel/extensions/schema_dumper.rb +11 -0
- data/lib/sequel/model/associations.rb +275 -89
- data/lib/sequel/plugins/async_thread_pool.rb +1 -1
- data/lib/sequel/plugins/auto_validations_constraint_validations_presence_message.rb +68 -0
- data/lib/sequel/plugins/column_encryption.rb +20 -3
- data/lib/sequel/plugins/concurrent_eager_loading.rb +174 -0
- data/lib/sequel/plugins/many_through_many.rb +108 -9
- data/lib/sequel/plugins/pg_array_associations.rb +52 -38
- data/lib/sequel/plugins/prepared_statements.rb +10 -1
- data/lib/sequel/plugins/rcte_tree.rb +27 -19
- data/lib/sequel/plugins/timestamps.rb +1 -1
- data/lib/sequel/plugins/unused_associations.rb +520 -0
- data/lib/sequel/version.rb +1 -1
- metadata +14 -3
data/doc/sql.rdoc
CHANGED
@@ -428,6 +428,18 @@ As you can see, these literalize with ANDs by default. You can use the <tt>Sequ
|
|
428
428
|
|
429
429
|
Sequel.or(column1: 1, column2: 2) # (("column1" = 1) OR ("column2" = 2))
|
430
430
|
|
431
|
+
As you can see in the above examples, <tt>Sequel.|</tt> and <tt>Sequel.or</tt> work differently.
|
432
|
+
<tt>Sequel.|</tt> is for combining an arbitrary number of expressions using OR. If you pass a single
|
433
|
+
argument, <tt>Sequel.|</tt> will just convert it to a Sequel expression, similar to <tt>Sequel.expr</tt>.
|
434
|
+
<tt>Sequel.or</tt> is for taking a single hash or array of two element arrays and combining the
|
435
|
+
elements of that single argument using OR instead of AND:
|
436
|
+
|
437
|
+
Sequel.|(column1: 1, column2: 2) # (("column1" = 1) AND ("column2" = 2))
|
438
|
+
Sequel.or(column1: 1, column2: 2) # (("column1" = 1) OR ("column2" = 2))
|
439
|
+
|
440
|
+
Sequel.|({column1: 1}, {column2: 2}) # (("column1" = 1) OR ("column2" = 2))
|
441
|
+
Sequel.or({column1: 1}, {column2: 2}) # ArgumentError
|
442
|
+
|
431
443
|
You've already seen the <tt>Sequel.negate</tt> method, which will use ANDs if multiple entries are used:
|
432
444
|
|
433
445
|
Sequel.negate(column1: 1, column2: 2) # (("column1" != 1) AND ("column2" != 2))
|
data/doc/testing.rdoc
CHANGED
@@ -162,6 +162,7 @@ SEQUEL_ASYNC_THREAD_POOL_PREEMPT :: Use the async_thread_pool extension when run
|
|
162
162
|
SEQUEL_COLUMNS_INTROSPECTION :: Use the columns_introspection extension when running the specs
|
163
163
|
SEQUEL_CONNECTION_VALIDATOR :: Use the connection validator extension when running the specs
|
164
164
|
SEQUEL_DUPLICATE_COLUMNS_HANDLER :: Use the duplicate columns handler extension with value given when running the specs
|
165
|
+
SEQUEL_CONCURRENT_EAGER_LOADING :: Use the async_thread_pool extension and concurrent_eager_loading plugin when running the specs
|
165
166
|
SEQUEL_ERROR_SQL :: Use the error_sql extension when running the specs
|
166
167
|
SEQUEL_INDEX_CACHING :: Use the index_caching extension when running the specs
|
167
168
|
SEQUEL_FIBER_CONCURRENCY :: Use the fiber_concurrency extension when running the adapter and integration specs
|
@@ -174,6 +175,10 @@ SEQUEL_NO_CACHE_ASSOCIATIONS :: Don't cache association metadata when running th
|
|
174
175
|
SEQUEL_CHECK_PENDING :: Try running all specs (note, can cause lockups for some adapters), and raise errors for skipped specs that don't fail
|
175
176
|
SEQUEL_NO_PENDING :: Don't skip any specs, try running all specs (note, can cause lockups for some adapters)
|
176
177
|
SEQUEL_PG_TIMESTAMPTZ :: Use the pg_timestamptz extension when running the postgres specs
|
178
|
+
SEQUEL_QUERY_PER_ASSOCIATION_DB_0_URL :: Run query-per-association integration tests with multiple databases (all 4 must be set to run)
|
179
|
+
SEQUEL_QUERY_PER_ASSOCIATION_DB_1_URL :: Run query-per-association integration tests with multiple databases (all 4 must be set to run)
|
180
|
+
SEQUEL_QUERY_PER_ASSOCIATION_DB_2_URL :: Run query-per-association integration tests with multiple databases (all 4 must be set to run)
|
181
|
+
SEQUEL_QUERY_PER_ASSOCIATION_DB_3_URL :: Run query-per-association integration tests with multiple databases (all 4 must be set to run)
|
177
182
|
SEQUEL_SPLIT_SYMBOLS :: Turn on symbol splitting when running the adapter and integration specs
|
178
183
|
SEQUEL_SYNCHRONIZE_SQL :: Use the synchronize_sql extension when running the specs
|
179
184
|
SEQUEL_TZINFO_VERSION :: Force the given tzinfo version when running the specs (e.g. '>=2')
|
data/doc/virtual_rows.rdoc
CHANGED
data/lib/sequel/adapters/odbc.rb
CHANGED
@@ -94,7 +94,11 @@ module Sequel
|
|
94
94
|
self.columns = columns
|
95
95
|
s.each do |row|
|
96
96
|
hash = {}
|
97
|
-
cols.each
|
97
|
+
cols.each do |n,t,j|
|
98
|
+
v = row[j]
|
99
|
+
# We can assume v is not false, so this shouldn't convert false to nil.
|
100
|
+
hash[n] = (convert_odbc_value(v, t) if v)
|
101
|
+
end
|
98
102
|
yield hash
|
99
103
|
end
|
100
104
|
end
|
@@ -187,6 +187,15 @@ module Sequel
|
|
187
187
|
def views(opts=OPTS)
|
188
188
|
full_tables('VIEW', opts)
|
189
189
|
end
|
190
|
+
|
191
|
+
# Renames multiple tables in a single call.
|
192
|
+
#
|
193
|
+
# DB.rename_tables [:items, :old_items], [:other_items, :old_other_items]
|
194
|
+
# # RENAME TABLE items TO old_items, other_items TO old_other_items
|
195
|
+
def rename_tables(*renames)
|
196
|
+
execute_ddl(rename_tables_sql(renames))
|
197
|
+
renames.each{|from,| remove_cached_schema(from)}
|
198
|
+
end
|
190
199
|
|
191
200
|
private
|
192
201
|
|
@@ -473,6 +482,14 @@ module Sequel
|
|
473
482
|
schema(table).select{|a| a[1][:primary_key]}.map{|a| a[0]}
|
474
483
|
end
|
475
484
|
|
485
|
+
# SQL statement for renaming multiple tables.
|
486
|
+
def rename_tables_sql(renames)
|
487
|
+
rename_tos = renames.map do |from, to|
|
488
|
+
"#{quote_schema_table(from)} TO #{quote_schema_table(to)}"
|
489
|
+
end.join(', ')
|
490
|
+
"RENAME TABLE #{rename_tos}"
|
491
|
+
end
|
492
|
+
|
476
493
|
# Rollback the currently open XA transaction
|
477
494
|
def rollback_transaction(conn, opts=OPTS)
|
478
495
|
if (s = opts[:prepare]) && savepoint_level(conn) <= 1
|
@@ -2141,18 +2141,6 @@ module Sequel
|
|
2141
2141
|
opts[:with].any?{|w| w[:recursive]} ? "WITH RECURSIVE " : super
|
2142
2142
|
end
|
2143
2143
|
|
2144
|
-
# Support WITH AS [NOT] MATERIALIZED if :materialized option is used.
|
2145
|
-
def select_with_sql_prefix(sql, w)
|
2146
|
-
super
|
2147
|
-
|
2148
|
-
case w[:materialized]
|
2149
|
-
when true
|
2150
|
-
sql << "MATERIALIZED "
|
2151
|
-
when false
|
2152
|
-
sql << "NOT MATERIALIZED "
|
2153
|
-
end
|
2154
|
-
end
|
2155
|
-
|
2156
2144
|
# The version of the database server
|
2157
2145
|
def server_version
|
2158
2146
|
db.server_version(@opts[:server])
|
@@ -239,8 +239,12 @@ module Sequel
|
|
239
239
|
super
|
240
240
|
end
|
241
241
|
when :drop_column
|
242
|
-
|
243
|
-
|
242
|
+
if sqlite_version >= 33500
|
243
|
+
super
|
244
|
+
else
|
245
|
+
ocp = lambda{|oc| oc.delete_if{|c| c.to_s == op[:name].to_s}}
|
246
|
+
duplicate_table(table, :old_columns_proc=>ocp){|columns| columns.delete_if{|s| s[:name].to_s == op[:name].to_s}}
|
247
|
+
end
|
244
248
|
when :rename_column
|
245
249
|
if sqlite_version >= 32500
|
246
250
|
super
|
@@ -424,10 +428,10 @@ module Sequel
|
|
424
428
|
skip_indexes = []
|
425
429
|
indexes(table, :only_autocreated=>true).each do |name, h|
|
426
430
|
skip_indexes << name
|
427
|
-
if h[:unique]
|
431
|
+
if h[:unique] && !opts[:no_unique]
|
428
432
|
if h[:columns].length == 1
|
429
433
|
unique_columns.concat(h[:columns])
|
430
|
-
elsif h[:columns].map(&:to_s) != pks
|
434
|
+
elsif h[:columns].map(&:to_s) != pks
|
431
435
|
constraints << {:type=>:unique, :columns=>h[:columns]}
|
432
436
|
end
|
433
437
|
end
|
@@ -558,10 +562,10 @@ module Sequel
|
|
558
562
|
EXTRACT_MAP = {:year=>"'%Y'", :month=>"'%m'", :day=>"'%d'", :hour=>"'%H'", :minute=>"'%M'", :second=>"'%f'"}.freeze
|
559
563
|
EXTRACT_MAP.each_value(&:freeze)
|
560
564
|
|
561
|
-
Dataset.def_sql_method(self, :delete, [['if db.sqlite_version >= 30803', %w'with delete from where'], ["else", %w'delete from where']])
|
562
|
-
Dataset.def_sql_method(self, :insert, [['if db.sqlite_version >= 30803', %w'with insert conflict into columns values on_conflict'], ["else", %w'insert conflict into columns values']])
|
565
|
+
Dataset.def_sql_method(self, :delete, [['if db.sqlite_version >= 33500', %w'with delete from where returning'], ['elsif db.sqlite_version >= 30803', %w'with delete from where'], ["else", %w'delete from where']])
|
566
|
+
Dataset.def_sql_method(self, :insert, [['if db.sqlite_version >= 33500', %w'with insert conflict into columns values on_conflict returning'], ['elsif db.sqlite_version >= 30803', %w'with insert conflict into columns values on_conflict'], ["else", %w'insert conflict into columns values']])
|
563
567
|
Dataset.def_sql_method(self, :select, [['if opts[:values]', %w'with values compounds'], ['else', %w'with select distinct columns from join where group having window compounds order limit lock']])
|
564
|
-
Dataset.def_sql_method(self, :update, [['if db.sqlite_version >= 33300', %w'with update table set from where'], ['elsif db.sqlite_version >= 30803', %w'with update table set where'], ["else", %w'update table set where']])
|
568
|
+
Dataset.def_sql_method(self, :update, [['if db.sqlite_version >= 33500', %w'with update table set from where returning'], ['elsif db.sqlite_version >= 33300', %w'with update table set from where'], ['elsif db.sqlite_version >= 30803', %w'with update table set where'], ["else", %w'update table set where']])
|
565
569
|
|
566
570
|
def cast_sql_append(sql, expr, type)
|
567
571
|
if type == Time or type == DateTime
|
@@ -635,8 +639,8 @@ module Sequel
|
|
635
639
|
# SQLite performs a TRUNCATE style DELETE if no filter is specified.
|
636
640
|
# Since we want to always return the count of records, add a condition
|
637
641
|
# that is always true and then delete.
|
638
|
-
def delete
|
639
|
-
@opts[:where] ? super : where(1=>1).delete
|
642
|
+
def delete(&block)
|
643
|
+
@opts[:where] ? super : where(1=>1).delete(&block)
|
640
644
|
end
|
641
645
|
|
642
646
|
# Return an array of strings specifying a query explanation for a SELECT of the
|
@@ -657,6 +661,21 @@ module Sequel
|
|
657
661
|
super
|
658
662
|
end
|
659
663
|
|
664
|
+
# Support insert select for associations, so that the model code can use
|
665
|
+
# returning instead of a separate query.
|
666
|
+
def insert_select(*values)
|
667
|
+
return unless supports_insert_select?
|
668
|
+
# Handle case where query does not return a row
|
669
|
+
server?(:default).with_sql_first(insert_select_sql(*values)) || false
|
670
|
+
end
|
671
|
+
|
672
|
+
# The SQL to use for an insert_select, adds a RETURNING clause to the insert
|
673
|
+
# unless the RETURNING clause is already present.
|
674
|
+
def insert_select_sql(*values)
|
675
|
+
ds = opts[:returning] ? self : returning
|
676
|
+
ds.insert_sql(*values)
|
677
|
+
end
|
678
|
+
|
660
679
|
# SQLite uses the nonstandard ` (backtick) for quoting identifiers.
|
661
680
|
def quoted_identifier_append(sql, c)
|
662
681
|
sql << '`' << c.to_s.gsub('`', '``') << '`'
|
@@ -738,6 +757,13 @@ module Sequel
|
|
738
757
|
insert_conflict(:ignore)
|
739
758
|
end
|
740
759
|
|
760
|
+
# Automatically add aliases to RETURNING values to work around SQLite bug.
|
761
|
+
def returning(*values)
|
762
|
+
return super if values.empty?
|
763
|
+
raise Error, "RETURNING is not supported on #{db.database_type}" unless supports_returning?(:insert)
|
764
|
+
clone(:returning=>_returning_values(values).freeze)
|
765
|
+
end
|
766
|
+
|
741
767
|
# SQLite 3.8.3+ supports common table expressions.
|
742
768
|
def supports_cte?(type=:select)
|
743
769
|
db.sqlite_version >= 30803
|
@@ -778,6 +804,11 @@ module Sequel
|
|
778
804
|
false
|
779
805
|
end
|
780
806
|
|
807
|
+
# SQLite 3.35.0 supports RETURNING on INSERT/UPDATE/DELETE.
|
808
|
+
def supports_returning?(_)
|
809
|
+
db.sqlite_version >= 33500
|
810
|
+
end
|
811
|
+
|
781
812
|
# SQLite supports timezones in literal timestamps, since it stores them
|
782
813
|
# as text. But using timezones in timestamps breaks SQLite datetime
|
783
814
|
# functions, so we allow the user to override the default per database.
|
@@ -810,6 +841,21 @@ module Sequel
|
|
810
841
|
|
811
842
|
private
|
812
843
|
|
844
|
+
# Add aliases to symbols and identifiers to work around SQLite bug.
|
845
|
+
def _returning_values(values)
|
846
|
+
values.map do |v|
|
847
|
+
case v
|
848
|
+
when Symbol
|
849
|
+
_, c, a = split_symbol(v)
|
850
|
+
a ? v : Sequel.as(v, c)
|
851
|
+
when SQL::Identifier, SQL::QualifiedIdentifier
|
852
|
+
Sequel.as(v, unqualified_column_for(v))
|
853
|
+
else
|
854
|
+
v
|
855
|
+
end
|
856
|
+
end
|
857
|
+
end
|
858
|
+
|
813
859
|
# SQLite uses string literals instead of identifiers in AS clauses.
|
814
860
|
def as_sql_append(sql, aliaz, column_aliases=nil)
|
815
861
|
raise Error, "sqlite does not support derived column lists" if column_aliases
|
data/lib/sequel/core.rb
CHANGED
@@ -176,6 +176,17 @@ module Sequel
|
|
176
176
|
JSON.parse(json, :create_additions=>false)
|
177
177
|
end
|
178
178
|
|
179
|
+
# If a mutex is given, synchronize access using it. If nil is given, just
|
180
|
+
# yield to the block. This is designed for cases where a mutex may or may
|
181
|
+
# not be provided.
|
182
|
+
def synchronize_with(mutex)
|
183
|
+
if mutex
|
184
|
+
mutex.synchronize{yield}
|
185
|
+
else
|
186
|
+
yield
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
179
190
|
# Convert each item in the array to the correct type, handling multi-dimensional
|
180
191
|
# arrays. For each element in the array or subarrays, call the converter,
|
181
192
|
# unless the value is nil.
|
@@ -214,14 +214,12 @@ module Sequel
|
|
214
214
|
end
|
215
215
|
|
216
216
|
# Add a full text index on the given columns.
|
217
|
+
# See #index for additional options.
|
217
218
|
#
|
218
219
|
# PostgreSQL specific options:
|
219
220
|
# :index_type :: Can be set to :gist to use a GIST index instead of the
|
220
221
|
# default GIN index.
|
221
222
|
# :language :: Set a language to use for the index (default: simple).
|
222
|
-
#
|
223
|
-
# Microsoft SQL Server specific options:
|
224
|
-
# :key_index :: The KEY INDEX to use for the full text index.
|
225
223
|
def full_text_index(columns, opts = OPTS)
|
226
224
|
index(columns, opts.merge(:type => :full_text))
|
227
225
|
end
|
@@ -231,35 +229,43 @@ module Sequel
|
|
231
229
|
columns.any?{|c| c[:name] == name}
|
232
230
|
end
|
233
231
|
|
234
|
-
# Add an index on the given column(s) with the given options.
|
232
|
+
# Add an index on the given column(s) with the given options. Examples:
|
233
|
+
#
|
234
|
+
# index :name
|
235
|
+
# # CREATE INDEX table_name_index ON table (name)
|
236
|
+
#
|
237
|
+
# index [:artist_id, :name]
|
238
|
+
# # CREATE INDEX table_artist_id_name_index ON table (artist_id, name)
|
239
|
+
#
|
240
|
+
# index [:artist_id, :name], name: :foo
|
241
|
+
# # CREATE INDEX foo ON table (artist_id, name)
|
242
|
+
#
|
235
243
|
# General options:
|
236
244
|
#
|
245
|
+
# :include :: Include additional column values in the index, without
|
246
|
+
# actually indexing on those values (only supported by
|
247
|
+
# some databases).
|
237
248
|
# :name :: The name to use for the index. If not given, a default name
|
238
249
|
# based on the table and columns is used.
|
239
|
-
# :type :: The type of index to use (only supported by some databases
|
250
|
+
# :type :: The type of index to use (only supported by some databases,
|
251
|
+
# :full_text and :spatial values are handled specially).
|
240
252
|
# :unique :: Make the index unique, so duplicate values are not allowed.
|
241
|
-
# :where ::
|
253
|
+
# :where :: A filter expression, used to create a partial index (only
|
254
|
+
# supported by some databases).
|
242
255
|
#
|
243
256
|
# PostgreSQL specific options:
|
244
257
|
#
|
245
258
|
# :concurrently :: Create the index concurrently, so it doesn't block
|
246
259
|
# operations on the table while the index is being
|
247
260
|
# built.
|
248
|
-
# :
|
249
|
-
# :
|
250
|
-
#
|
261
|
+
# :if_not_exists :: Only create the index if an index of the same name doesn't already exist.
|
262
|
+
# :opclass :: Set an opclass to use for all columns (per-column opclasses require
|
263
|
+
# custom SQL).
|
251
264
|
# :tablespace :: Specify tablespace for index.
|
252
265
|
#
|
253
266
|
# Microsoft SQL Server specific options:
|
254
267
|
#
|
255
|
-
# :
|
256
|
-
# actually indexing on those values.
|
257
|
-
#
|
258
|
-
# index :name
|
259
|
-
# # CREATE INDEX table_name_index ON table (name)
|
260
|
-
#
|
261
|
-
# index [:artist_id, :name]
|
262
|
-
# # CREATE INDEX table_artist_id_name_index ON table (artist_id, name)
|
268
|
+
# :key_index :: Sets the KEY INDEX to the given value.
|
263
269
|
def index(columns, opts = OPTS)
|
264
270
|
indexes << {:columns => Array(columns)}.merge!(opts)
|
265
271
|
nil
|
@@ -325,6 +331,7 @@ module Sequel
|
|
325
331
|
end
|
326
332
|
|
327
333
|
# Add a spatial index on the given columns.
|
334
|
+
# See #index for additional options.
|
328
335
|
def spatial_index(columns, opts = OPTS)
|
329
336
|
index(columns, opts.merge(:type => :spatial))
|
330
337
|
end
|
@@ -451,7 +458,7 @@ module Sequel
|
|
451
458
|
end
|
452
459
|
|
453
460
|
# Add a full text index on the given columns.
|
454
|
-
# See CreateTableGenerator#
|
461
|
+
# See CreateTableGenerator#full_text_index for available options.
|
455
462
|
def add_full_text_index(columns, opts = OPTS)
|
456
463
|
add_index(columns, {:type=>:full_text}.merge!(opts))
|
457
464
|
end
|
@@ -460,34 +467,6 @@ module Sequel
|
|
460
467
|
# CreateTableGenerator#index for available options.
|
461
468
|
#
|
462
469
|
# add_index(:artist_id) # CREATE INDEX table_artist_id_index ON table (artist_id)
|
463
|
-
#
|
464
|
-
# Options:
|
465
|
-
#
|
466
|
-
# :name :: Give a specific name for the index. Highly recommended if you plan on
|
467
|
-
# dropping the index later.
|
468
|
-
# :where :: A filter expression, used to setup a partial index (if supported).
|
469
|
-
# :unique :: Create a unique index.
|
470
|
-
#
|
471
|
-
# PostgreSQL specific options:
|
472
|
-
#
|
473
|
-
# :concurrently :: Create the index concurrently, so it doesn't require an exclusive lock
|
474
|
-
# on the table.
|
475
|
-
# :index_type :: The underlying index type to use for a full_text index, gin by default).
|
476
|
-
# :language :: The language to use for a full text index (simple by default).
|
477
|
-
# :opclass :: Set an opclass to use for all columns (per-column opclasses require
|
478
|
-
# custom SQL).
|
479
|
-
# :type :: Set the index type (e.g. full_text, spatial, hash, gin, gist, btree).
|
480
|
-
# :if_not_exists :: Only create the index if an index of the same name doesn't already exists
|
481
|
-
#
|
482
|
-
# MySQL specific options:
|
483
|
-
#
|
484
|
-
# :type :: Set the index type, with full_text and spatial indexes handled specially.
|
485
|
-
#
|
486
|
-
# Microsoft SQL Server specific options:
|
487
|
-
#
|
488
|
-
# :include :: Includes additional columns in the index.
|
489
|
-
# :key_index :: Sets the KEY INDEX to the given value.
|
490
|
-
# :type :: clustered uses a clustered index, full_text uses a full text index.
|
491
470
|
def add_index(columns, opts = OPTS)
|
492
471
|
@operations << {:op => :add_index, :columns => Array(columns)}.merge!(opts)
|
493
472
|
nil
|
@@ -63,7 +63,7 @@ module Sequel
|
|
63
63
|
# definitions using <tt>create_table</tt>, and +add_index+ accepts all the options
|
64
64
|
# available for index definition.
|
65
65
|
#
|
66
|
-
# See <tt>Schema::AlterTableGenerator</tt> and the {
|
66
|
+
# See <tt>Schema::AlterTableGenerator</tt> and the {Migrations guide}[rdoc-ref:doc/migration.rdoc].
|
67
67
|
def alter_table(name, &block)
|
68
68
|
generator = alter_table_generator(&block)
|
69
69
|
remove_cached_schema(name)
|
data/lib/sequel/dataset/query.rb
CHANGED
@@ -699,7 +699,7 @@ module Sequel
|
|
699
699
|
end
|
700
700
|
|
701
701
|
# Returns a copy of the dataset with a specified order. Can be safely combined with limit.
|
702
|
-
# If you call limit with an offset, it will override
|
702
|
+
# If you call limit with an offset, it will override the offset if you've called
|
703
703
|
# offset first.
|
704
704
|
#
|
705
705
|
# DB[:items].offset(10) # SELECT * FROM items OFFSET 10
|
@@ -1062,10 +1062,8 @@ module Sequel
|
|
1062
1062
|
# Options:
|
1063
1063
|
# :args :: Specify the arguments/columns for the CTE, should be an array of symbols.
|
1064
1064
|
# :recursive :: Specify that this is a recursive CTE
|
1065
|
-
#
|
1066
|
-
# PostgreSQL Specific Options:
|
1067
1065
|
# :materialized :: Set to false to force inlining of the CTE, or true to force not inlining
|
1068
|
-
# the CTE (PostgreSQL 12+).
|
1066
|
+
# the CTE (PostgreSQL 12+/SQLite 3.35+).
|
1069
1067
|
#
|
1070
1068
|
# DB[:items].with(:items, DB[:syx].where(Sequel[:name].like('A%')))
|
1071
1069
|
# # WITH items AS (SELECT * FROM syx WHERE (name LIKE 'A%' ESCAPE '\')) SELECT * FROM items
|
data/lib/sequel/dataset/sql.rb
CHANGED
@@ -1567,6 +1567,13 @@ module Sequel
|
|
1567
1567
|
sql << ')'
|
1568
1568
|
end
|
1569
1569
|
sql << ' AS '
|
1570
|
+
|
1571
|
+
case w[:materialized]
|
1572
|
+
when true
|
1573
|
+
sql << "MATERIALIZED "
|
1574
|
+
when false
|
1575
|
+
sql << "NOT MATERIALIZED "
|
1576
|
+
end
|
1570
1577
|
end
|
1571
1578
|
|
1572
1579
|
# Whether the symbol cache should be skipped when literalizing the dataset
|
@@ -8,9 +8,10 @@
|
|
8
8
|
# DB.extension :date_arithmetic
|
9
9
|
#
|
10
10
|
# Then you can use the Sequel.date_add and Sequel.date_sub methods
|
11
|
-
# to return Sequel expressions
|
11
|
+
# to return Sequel expressions (this example shows the only supported
|
12
|
+
# keys for the second argument):
|
12
13
|
#
|
13
|
-
# add = Sequel.date_add(:date_column, years: 1, months: 2, days:
|
14
|
+
# add = Sequel.date_add(:date_column, years: 1, months: 2, weeks: 2, days: 1)
|
14
15
|
# sub = Sequel.date_sub(:date_column, hours: 1, minutes: 2, seconds: 3)
|
15
16
|
#
|
16
17
|
# In addition to specifying the interval as a hash, there is also
|
@@ -184,22 +185,35 @@ module Sequel
|
|
184
185
|
# ActiveSupport::Duration :: Converted to a hash using the interval's parts.
|
185
186
|
def initialize(expr, interval, opts=OPTS)
|
186
187
|
@expr = expr
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
188
|
+
|
189
|
+
h = Hash.new(0)
|
190
|
+
interval = interval.parts unless interval.is_a?(Hash)
|
191
|
+
interval.each do |unit, value|
|
192
|
+
# skip nil values
|
193
|
+
next unless value
|
194
|
+
|
195
|
+
# Convert weeks to days, as ActiveSupport::Duration can use weeks,
|
196
|
+
# but the database-specific literalizers only support days.
|
197
|
+
if unit == :weeks
|
198
|
+
unit = :days
|
199
|
+
value *= 7
|
200
|
+
end
|
201
|
+
|
202
|
+
unless DatasetMethods::DURATION_UNITS.include?(unit)
|
203
|
+
raise Sequel::Error, "Invalid key used in DateAdd interval hash: #{unit.inspect}"
|
194
204
|
end
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
205
|
+
|
206
|
+
# Attempt to prevent SQL injection by users who pass untrusted strings
|
207
|
+
# as interval values. It doesn't make sense to support literal strings,
|
208
|
+
# due to the numeric adding below.
|
209
|
+
if value.is_a?(String)
|
210
|
+
raise Sequel::InvalidValue, "cannot provide String value as interval part: #{value.inspect}"
|
211
|
+
end
|
212
|
+
|
213
|
+
h[unit] += value
|
200
214
|
end
|
201
215
|
|
202
|
-
@interval.freeze
|
216
|
+
@interval = Hash[h].freeze
|
203
217
|
@cast_type = opts[:cast] if opts[:cast]
|
204
218
|
freeze
|
205
219
|
end
|