sequel 5.45.0 → 5.77.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG +434 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +59 -27
- data/bin/sequel +11 -3
- data/doc/advanced_associations.rdoc +16 -14
- data/doc/association_basics.rdoc +119 -24
- data/doc/cheat_sheet.rdoc +11 -3
- data/doc/mass_assignment.rdoc +1 -1
- data/doc/migration.rdoc +27 -6
- data/doc/model_hooks.rdoc +1 -1
- data/doc/object_model.rdoc +8 -8
- data/doc/opening_databases.rdoc +28 -12
- data/doc/postgresql.rdoc +16 -8
- data/doc/querying.rdoc +5 -3
- data/doc/release_notes/5.46.0.txt +87 -0
- data/doc/release_notes/5.47.0.txt +59 -0
- data/doc/release_notes/5.48.0.txt +14 -0
- data/doc/release_notes/5.49.0.txt +59 -0
- data/doc/release_notes/5.50.0.txt +78 -0
- data/doc/release_notes/5.51.0.txt +47 -0
- data/doc/release_notes/5.52.0.txt +87 -0
- data/doc/release_notes/5.53.0.txt +23 -0
- data/doc/release_notes/5.54.0.txt +27 -0
- data/doc/release_notes/5.55.0.txt +21 -0
- data/doc/release_notes/5.56.0.txt +51 -0
- data/doc/release_notes/5.57.0.txt +23 -0
- data/doc/release_notes/5.58.0.txt +31 -0
- data/doc/release_notes/5.59.0.txt +73 -0
- data/doc/release_notes/5.60.0.txt +22 -0
- data/doc/release_notes/5.61.0.txt +43 -0
- data/doc/release_notes/5.62.0.txt +132 -0
- data/doc/release_notes/5.63.0.txt +33 -0
- data/doc/release_notes/5.64.0.txt +50 -0
- data/doc/release_notes/5.65.0.txt +21 -0
- data/doc/release_notes/5.66.0.txt +24 -0
- data/doc/release_notes/5.67.0.txt +32 -0
- data/doc/release_notes/5.68.0.txt +61 -0
- data/doc/release_notes/5.69.0.txt +26 -0
- data/doc/release_notes/5.70.0.txt +35 -0
- data/doc/release_notes/5.71.0.txt +21 -0
- data/doc/release_notes/5.72.0.txt +33 -0
- data/doc/release_notes/5.73.0.txt +66 -0
- data/doc/release_notes/5.74.0.txt +45 -0
- data/doc/release_notes/5.75.0.txt +35 -0
- data/doc/release_notes/5.76.0.txt +86 -0
- data/doc/release_notes/5.77.0.txt +63 -0
- data/doc/schema_modification.rdoc +1 -1
- data/doc/security.rdoc +9 -9
- data/doc/sharding.rdoc +3 -1
- data/doc/sql.rdoc +27 -15
- data/doc/testing.rdoc +23 -13
- data/doc/transactions.rdoc +6 -6
- data/doc/virtual_rows.rdoc +1 -1
- data/lib/sequel/adapters/ado/access.rb +1 -1
- data/lib/sequel/adapters/ado.rb +1 -1
- data/lib/sequel/adapters/amalgalite.rb +3 -5
- data/lib/sequel/adapters/ibmdb.rb +3 -3
- data/lib/sequel/adapters/jdbc/derby.rb +8 -0
- data/lib/sequel/adapters/jdbc/h2.rb +63 -10
- data/lib/sequel/adapters/jdbc/hsqldb.rb +8 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +7 -4
- data/lib/sequel/adapters/jdbc/sqlanywhere.rb +15 -0
- data/lib/sequel/adapters/jdbc/sqlserver.rb +4 -0
- data/lib/sequel/adapters/jdbc.rb +24 -22
- data/lib/sequel/adapters/mysql.rb +92 -67
- data/lib/sequel/adapters/mysql2.rb +56 -51
- data/lib/sequel/adapters/odbc/mssql.rb +1 -1
- data/lib/sequel/adapters/odbc.rb +1 -1
- data/lib/sequel/adapters/oracle.rb +4 -3
- data/lib/sequel/adapters/postgres.rb +89 -45
- data/lib/sequel/adapters/shared/access.rb +11 -1
- data/lib/sequel/adapters/shared/db2.rb +42 -0
- data/lib/sequel/adapters/shared/mssql.rb +91 -10
- data/lib/sequel/adapters/shared/mysql.rb +78 -3
- data/lib/sequel/adapters/shared/oracle.rb +86 -7
- data/lib/sequel/adapters/shared/postgres.rb +576 -171
- data/lib/sequel/adapters/shared/sqlanywhere.rb +21 -5
- data/lib/sequel/adapters/shared/sqlite.rb +92 -8
- data/lib/sequel/adapters/sqlanywhere.rb +1 -1
- data/lib/sequel/adapters/sqlite.rb +99 -18
- data/lib/sequel/adapters/tinytds.rb +1 -1
- data/lib/sequel/adapters/trilogy.rb +117 -0
- data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
- data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -1
- data/lib/sequel/ast_transformer.rb +6 -0
- data/lib/sequel/connection_pool/sharded_single.rb +5 -7
- data/lib/sequel/connection_pool/sharded_threaded.rb +16 -11
- data/lib/sequel/connection_pool/sharded_timed_queue.rb +374 -0
- data/lib/sequel/connection_pool/single.rb +6 -8
- data/lib/sequel/connection_pool/threaded.rb +14 -8
- data/lib/sequel/connection_pool/timed_queue.rb +270 -0
- data/lib/sequel/connection_pool.rb +57 -31
- data/lib/sequel/core.rb +17 -18
- data/lib/sequel/database/connecting.rb +27 -3
- data/lib/sequel/database/dataset.rb +16 -6
- data/lib/sequel/database/misc.rb +70 -14
- data/lib/sequel/database/query.rb +73 -2
- data/lib/sequel/database/schema_generator.rb +11 -6
- data/lib/sequel/database/schema_methods.rb +23 -4
- data/lib/sequel/database/transactions.rb +6 -0
- data/lib/sequel/dataset/actions.rb +111 -15
- data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +42 -0
- data/lib/sequel/dataset/features.rb +20 -1
- data/lib/sequel/dataset/misc.rb +12 -2
- data/lib/sequel/dataset/placeholder_literalizer.rb +20 -9
- data/lib/sequel/dataset/query.rb +170 -41
- data/lib/sequel/dataset/sql.rb +190 -71
- data/lib/sequel/dataset.rb +4 -0
- data/lib/sequel/extensions/_model_pg_row.rb +0 -12
- data/lib/sequel/extensions/_pretty_table.rb +1 -1
- data/lib/sequel/extensions/any_not_empty.rb +2 -2
- data/lib/sequel/extensions/async_thread_pool.rb +14 -13
- data/lib/sequel/extensions/auto_cast_date_and_time.rb +94 -0
- data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
- data/lib/sequel/extensions/connection_expiration.rb +15 -9
- data/lib/sequel/extensions/connection_validator.rb +16 -11
- data/lib/sequel/extensions/constraint_validations.rb +1 -1
- data/lib/sequel/extensions/core_refinements.rb +36 -11
- data/lib/sequel/extensions/date_arithmetic.rb +36 -8
- data/lib/sequel/extensions/date_parse_input_handler.rb +67 -0
- data/lib/sequel/extensions/datetime_parse_to_time.rb +5 -1
- data/lib/sequel/extensions/duplicate_columns_handler.rb +11 -10
- data/lib/sequel/extensions/index_caching.rb +5 -1
- data/lib/sequel/extensions/inflector.rb +1 -1
- data/lib/sequel/extensions/is_distinct_from.rb +141 -0
- data/lib/sequel/extensions/looser_typecasting.rb +3 -0
- data/lib/sequel/extensions/migration.rb +57 -15
- data/lib/sequel/extensions/named_timezones.rb +22 -6
- data/lib/sequel/extensions/pagination.rb +1 -1
- data/lib/sequel/extensions/pg_array.rb +33 -4
- data/lib/sequel/extensions/pg_array_ops.rb +2 -2
- data/lib/sequel/extensions/pg_auto_parameterize.rb +509 -0
- data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +110 -0
- data/lib/sequel/extensions/pg_enum.rb +1 -2
- data/lib/sequel/extensions/pg_extended_date_support.rb +39 -28
- data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
- data/lib/sequel/extensions/pg_hstore.rb +6 -1
- data/lib/sequel/extensions/pg_hstore_ops.rb +53 -3
- data/lib/sequel/extensions/pg_inet.rb +10 -11
- data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
- data/lib/sequel/extensions/pg_interval.rb +11 -11
- data/lib/sequel/extensions/pg_json.rb +13 -15
- data/lib/sequel/extensions/pg_json_ops.rb +125 -2
- data/lib/sequel/extensions/pg_multirange.rb +367 -0
- data/lib/sequel/extensions/pg_range.rb +13 -26
- data/lib/sequel/extensions/pg_range_ops.rb +37 -9
- data/lib/sequel/extensions/pg_row.rb +20 -19
- data/lib/sequel/extensions/pg_row_ops.rb +1 -1
- data/lib/sequel/extensions/pg_timestamptz.rb +27 -3
- data/lib/sequel/extensions/round_timestamps.rb +1 -1
- data/lib/sequel/extensions/s.rb +2 -1
- data/lib/sequel/extensions/schema_caching.rb +1 -1
- data/lib/sequel/extensions/schema_dumper.rb +45 -11
- data/lib/sequel/extensions/server_block.rb +10 -13
- data/lib/sequel/extensions/set_literalizer.rb +58 -0
- data/lib/sequel/extensions/sql_comments.rb +110 -3
- data/lib/sequel/extensions/sql_log_normalizer.rb +108 -0
- data/lib/sequel/extensions/sqlite_json_ops.rb +255 -0
- data/lib/sequel/extensions/string_agg.rb +1 -1
- data/lib/sequel/extensions/string_date_time.rb +19 -23
- data/lib/sequel/extensions/symbol_aref.rb +2 -0
- data/lib/sequel/extensions/transaction_connection_validator.rb +78 -0
- data/lib/sequel/model/associations.rb +286 -92
- data/lib/sequel/model/base.rb +53 -33
- data/lib/sequel/model/dataset_module.rb +3 -0
- data/lib/sequel/model/errors.rb +10 -1
- data/lib/sequel/model/exceptions.rb +15 -3
- data/lib/sequel/model/inflections.rb +1 -1
- data/lib/sequel/plugins/auto_restrict_eager_graph.rb +62 -0
- data/lib/sequel/plugins/auto_validations.rb +74 -16
- data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
- data/lib/sequel/plugins/column_encryption.rb +29 -8
- data/lib/sequel/plugins/composition.rb +3 -2
- data/lib/sequel/plugins/concurrent_eager_loading.rb +4 -4
- data/lib/sequel/plugins/constraint_validations.rb +8 -5
- data/lib/sequel/plugins/defaults_setter.rb +16 -0
- data/lib/sequel/plugins/dirty.rb +1 -1
- data/lib/sequel/plugins/enum.rb +124 -0
- data/lib/sequel/plugins/finder.rb +4 -2
- data/lib/sequel/plugins/insert_conflict.rb +4 -0
- data/lib/sequel/plugins/instance_specific_default.rb +1 -1
- data/lib/sequel/plugins/json_serializer.rb +2 -2
- data/lib/sequel/plugins/lazy_attributes.rb +3 -0
- data/lib/sequel/plugins/list.rb +8 -3
- data/lib/sequel/plugins/many_through_many.rb +109 -10
- data/lib/sequel/plugins/mssql_optimistic_locking.rb +8 -38
- data/lib/sequel/plugins/nested_attributes.rb +4 -4
- data/lib/sequel/plugins/optimistic_locking.rb +9 -42
- data/lib/sequel/plugins/optimistic_locking_base.rb +55 -0
- data/lib/sequel/plugins/paged_operations.rb +181 -0
- data/lib/sequel/plugins/pg_array_associations.rb +46 -34
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +9 -3
- data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +109 -0
- data/lib/sequel/plugins/prepared_statements.rb +12 -2
- data/lib/sequel/plugins/prepared_statements_safe.rb +2 -1
- data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
- data/lib/sequel/plugins/rcte_tree.rb +7 -4
- data/lib/sequel/plugins/require_valid_schema.rb +67 -0
- data/lib/sequel/plugins/serialization.rb +1 -0
- data/lib/sequel/plugins/serialization_modification_detection.rb +1 -0
- data/lib/sequel/plugins/single_table_inheritance.rb +8 -0
- data/lib/sequel/plugins/sql_comments.rb +189 -0
- data/lib/sequel/plugins/static_cache.rb +39 -1
- data/lib/sequel/plugins/static_cache_cache.rb +5 -1
- data/lib/sequel/plugins/subclasses.rb +28 -11
- data/lib/sequel/plugins/tactical_eager_loading.rb +23 -10
- data/lib/sequel/plugins/timestamps.rb +1 -1
- data/lib/sequel/plugins/unused_associations.rb +521 -0
- data/lib/sequel/plugins/update_or_create.rb +1 -1
- data/lib/sequel/plugins/validate_associated.rb +22 -12
- data/lib/sequel/plugins/validation_helpers.rb +41 -11
- data/lib/sequel/plugins/validation_helpers_generic_type_messages.rb +73 -0
- data/lib/sequel/plugins/xml_serializer.rb +1 -1
- data/lib/sequel/sql.rb +1 -1
- data/lib/sequel/timezones.rb +12 -14
- data/lib/sequel/version.rb +1 -1
- metadata +109 -19
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen-string-literal: true
|
2
2
|
|
3
|
+
require_relative '../utils/columns_limit_1'
|
4
|
+
|
3
5
|
module Sequel
|
4
6
|
module SqlAnywhere
|
5
7
|
Sequel::Database.set_shared_adapter_scheme(:sqlanywhere, self)
|
@@ -35,7 +37,7 @@ module Sequel
|
|
35
37
|
row[:auto_increment] = auto_increment == 1 || auto_increment == true
|
36
38
|
row[:primary_key] = row.delete(:pkey) == 'Y'
|
37
39
|
row[:allow_null] = row[:nulls_allowed].is_a?(Integer) ? row.delete(:nulls_allowed) == 1 : row.delete(:nulls_allowed)
|
38
|
-
row[:db_type] = row.delete(:
|
40
|
+
row[:db_type] = row.delete(:domain_name_with_size)
|
39
41
|
row[:type] = if row[:db_type] =~ /numeric/i and (row[:scale].is_a?(Integer) ? row[:scale] == 0 : !row[:scale])
|
40
42
|
:integer
|
41
43
|
else
|
@@ -193,6 +195,11 @@ module Sequel
|
|
193
195
|
end
|
194
196
|
end
|
195
197
|
|
198
|
+
# SQLAnywhere tinyint types are unsigned.
|
199
|
+
def column_schema_tinyint_type_is_unsigned?
|
200
|
+
true
|
201
|
+
end
|
202
|
+
|
196
203
|
# SqlAnywhere doesn't support CREATE TABLE AS, it only supports SELECT INTO.
|
197
204
|
# Emulating CREATE TABLE AS using SELECT INTO is only possible if a dataset
|
198
205
|
# is given as the argument, it can't work with a string, so raise an
|
@@ -211,6 +218,8 @@ module Sequel
|
|
211
218
|
def schema_column_type(db_type)
|
212
219
|
if convert_smallint_to_bool && db_type =~ /smallint/i
|
213
220
|
:boolean
|
221
|
+
elsif db_type =~ /unsigned (big)?int/i
|
222
|
+
:integer
|
214
223
|
else
|
215
224
|
super
|
216
225
|
end
|
@@ -234,6 +243,7 @@ module Sequel
|
|
234
243
|
module DatasetMethods
|
235
244
|
Dataset.def_sql_method(self, :insert, %w'insert into columns values')
|
236
245
|
Dataset.def_sql_method(self, :select, %w'with select distinct limit columns into from join where group having window compounds order lock')
|
246
|
+
include ::Sequel::Dataset::ColumnsLimit1
|
237
247
|
|
238
248
|
# Whether to convert smallint to boolean arguments for this dataset.
|
239
249
|
# Defaults to the IBMDB module setting.
|
@@ -271,10 +281,6 @@ module Sequel
|
|
271
281
|
false
|
272
282
|
end
|
273
283
|
|
274
|
-
def supports_timestamp_usecs?
|
275
|
-
false
|
276
|
-
end
|
277
|
-
|
278
284
|
def supports_window_clause?
|
279
285
|
true
|
280
286
|
end
|
@@ -368,6 +374,16 @@ module Sequel
|
|
368
374
|
|
369
375
|
private
|
370
376
|
|
377
|
+
# SQLAnywhere only supports 3 digits after the decimal point for times.
|
378
|
+
def default_time_format
|
379
|
+
"'%H:%M:%S.%3N'"
|
380
|
+
end
|
381
|
+
|
382
|
+
# SQLAnywhere only supports 3 digits after the decimal point for timestamps.
|
383
|
+
def default_timestamp_format
|
384
|
+
"'%Y-%m-%d %H:%M:%S.%3N'"
|
385
|
+
end
|
386
|
+
|
371
387
|
# Use 1 for true on Sybase
|
372
388
|
def literal_true
|
373
389
|
'1'
|
@@ -145,6 +145,11 @@ module Sequel
|
|
145
145
|
sqlite_version >= 30608
|
146
146
|
end
|
147
147
|
|
148
|
+
# SQLite 3.8.2+ supports the without rowid table constraint
|
149
|
+
def support_without_rowid?
|
150
|
+
sqlite_version >= 30802
|
151
|
+
end
|
152
|
+
|
148
153
|
# Override the default setting for whether to use timezones in timestamps.
|
149
154
|
# It is set to +false+ by default, as SQLite's date/time methods do not
|
150
155
|
# support timezones in timestamps.
|
@@ -169,6 +174,7 @@ module Sequel
|
|
169
174
|
# DB.values([[1, 2], [3, 4]])
|
170
175
|
# # VALUES ((1, 2), (3, 4))
|
171
176
|
def values(v)
|
177
|
+
raise Error, "Cannot provide an empty array for values" if v.empty?
|
172
178
|
@default_dataset.clone(:values=>v)
|
173
179
|
end
|
174
180
|
|
@@ -320,6 +326,12 @@ module Sequel
|
|
320
326
|
end
|
321
327
|
end
|
322
328
|
|
329
|
+
# SQLite does not restrict the integer or decimal type to a specific range.
|
330
|
+
def column_schema_integer_min_max_values(column)
|
331
|
+
nil
|
332
|
+
end
|
333
|
+
alias column_schema_decimal_min_max_values column_schema_integer_min_max_values
|
334
|
+
|
323
335
|
# Array of PRAGMA SQL statements based on the Database options that should be applied to
|
324
336
|
# new connections.
|
325
337
|
def connection_pragmas
|
@@ -337,6 +349,19 @@ module Sequel
|
|
337
349
|
ps
|
338
350
|
end
|
339
351
|
|
352
|
+
# Support creating STRICT AND/OR WITHOUT ROWID tables via :strict and :without_rowid options
|
353
|
+
def create_table_sql(name, generator, options)
|
354
|
+
if options[:strict] && options[:without_rowid]
|
355
|
+
"#{super} STRICT, WITHOUT ROWID"
|
356
|
+
elsif options[:strict]
|
357
|
+
"#{super} STRICT"
|
358
|
+
elsif options[:without_rowid]
|
359
|
+
"#{super} WITHOUT ROWID"
|
360
|
+
else
|
361
|
+
super
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
340
365
|
# SQLite support creating temporary views.
|
341
366
|
def create_view_prefix_sql(name, options)
|
342
367
|
create_view_sql_append_columns("CREATE #{'TEMPORARY 'if options[:temp]}VIEW #{quote_schema_table(name)}", options[:columns])
|
@@ -345,8 +370,10 @@ module Sequel
|
|
345
370
|
DATABASE_ERROR_REGEXPS = {
|
346
371
|
/(is|are) not unique\z|PRIMARY KEY must be unique\z|UNIQUE constraint failed: .+\z/ => UniqueConstraintViolation,
|
347
372
|
/foreign key constraint failed\z/i => ForeignKeyConstraintViolation,
|
373
|
+
/\ASQLITE ERROR 3091/ => CheckConstraintViolation,
|
348
374
|
/\A(SQLITE ERROR 275 \(CONSTRAINT_CHECK\) : )?CHECK constraint failed/ => CheckConstraintViolation,
|
349
375
|
/\A(SQLITE ERROR 19 \(CONSTRAINT\) : )?constraint failed\z/ => ConstraintViolation,
|
376
|
+
/\Acannot store [A-Z]+ value in [A-Z]+ column / => ConstraintViolation,
|
350
377
|
/may not be NULL\z|NOT NULL constraint failed: .+\z/ => NotNullConstraintViolation,
|
351
378
|
/\ASQLITE ERROR \d+ \(\) : CHECK constraint failed: / => CheckConstraintViolation
|
352
379
|
}.freeze
|
@@ -393,7 +420,7 @@ module Sequel
|
|
393
420
|
old_columns = def_columns.map{|c| c[:name]}
|
394
421
|
opts[:old_columns_proc].call(old_columns) if opts[:old_columns_proc]
|
395
422
|
|
396
|
-
yield def_columns if
|
423
|
+
yield def_columns if defined?(yield)
|
397
424
|
|
398
425
|
constraints = (opts[:constraints] || []).dup
|
399
426
|
pks = []
|
@@ -490,7 +517,6 @@ module Sequel
|
|
490
517
|
# table_xinfo PRAGMA used, remove hidden columns
|
491
518
|
# that are not generated columns
|
492
519
|
if row[:generated] = (row.delete(:hidden) != 0)
|
493
|
-
next unless row[:type].end_with?(' GENERATED ALWAYS')
|
494
520
|
row[:type] = row[:type].sub(' GENERATED ALWAYS', '')
|
495
521
|
end
|
496
522
|
end
|
@@ -562,10 +588,10 @@ module Sequel
|
|
562
588
|
EXTRACT_MAP = {:year=>"'%Y'", :month=>"'%m'", :day=>"'%d'", :hour=>"'%H'", :minute=>"'%M'", :second=>"'%f'"}.freeze
|
563
589
|
EXTRACT_MAP.each_value(&:freeze)
|
564
590
|
|
565
|
-
Dataset.def_sql_method(self, :delete, [['if 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 >= 30803', %w'with insert conflict into columns values on_conflict'], ["else", %w'insert conflict into columns values']])
|
591
|
+
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']])
|
592
|
+
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']])
|
567
593
|
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']])
|
568
|
-
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']])
|
594
|
+
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']])
|
569
595
|
|
570
596
|
def cast_sql_append(sql, expr, type)
|
571
597
|
if type == Time or type == DateTime
|
@@ -639,10 +665,16 @@ module Sequel
|
|
639
665
|
# SQLite performs a TRUNCATE style DELETE if no filter is specified.
|
640
666
|
# Since we want to always return the count of records, add a condition
|
641
667
|
# that is always true and then delete.
|
642
|
-
def delete
|
643
|
-
@opts[:where] ? super : where(1=>1).delete
|
668
|
+
def delete(&block)
|
669
|
+
@opts[:where] ? super : where(1=>1).delete(&block)
|
644
670
|
end
|
645
671
|
|
672
|
+
# Always return false when using VALUES
|
673
|
+
def empty?
|
674
|
+
return false if @opts[:values]
|
675
|
+
super
|
676
|
+
end
|
677
|
+
|
646
678
|
# Return an array of strings specifying a query explanation for a SELECT of the
|
647
679
|
# current dataset. Currently, the options are ignored, but it accepts options
|
648
680
|
# to be compatible with other adapters.
|
@@ -657,10 +689,25 @@ module Sequel
|
|
657
689
|
|
658
690
|
# HAVING requires GROUP BY on SQLite
|
659
691
|
def having(*cond)
|
660
|
-
raise(InvalidOperation, "Can only specify a HAVING clause on a grouped dataset")
|
692
|
+
raise(InvalidOperation, "Can only specify a HAVING clause on a grouped dataset") if !@opts[:group] && db.sqlite_version < 33900
|
661
693
|
super
|
662
694
|
end
|
663
695
|
|
696
|
+
# Support insert select for associations, so that the model code can use
|
697
|
+
# returning instead of a separate query.
|
698
|
+
def insert_select(*values)
|
699
|
+
return unless supports_insert_select?
|
700
|
+
# Handle case where query does not return a row
|
701
|
+
server?(:default).with_sql_first(insert_select_sql(*values)) || false
|
702
|
+
end
|
703
|
+
|
704
|
+
# The SQL to use for an insert_select, adds a RETURNING clause to the insert
|
705
|
+
# unless the RETURNING clause is already present.
|
706
|
+
def insert_select_sql(*values)
|
707
|
+
ds = opts[:returning] ? self : returning
|
708
|
+
ds.insert_sql(*values)
|
709
|
+
end
|
710
|
+
|
664
711
|
# SQLite uses the nonstandard ` (backtick) for quoting identifiers.
|
665
712
|
def quoted_identifier_append(sql, c)
|
666
713
|
sql << '`' << c.to_s.gsub('`', '``') << '`'
|
@@ -742,6 +789,13 @@ module Sequel
|
|
742
789
|
insert_conflict(:ignore)
|
743
790
|
end
|
744
791
|
|
792
|
+
# Automatically add aliases to RETURNING values to work around SQLite bug.
|
793
|
+
def returning(*values)
|
794
|
+
return super if values.empty?
|
795
|
+
raise Error, "RETURNING is not supported on #{db.database_type}" unless supports_returning?(:insert)
|
796
|
+
clone(:returning=>_returning_values(values).freeze)
|
797
|
+
end
|
798
|
+
|
745
799
|
# SQLite 3.8.3+ supports common table expressions.
|
746
800
|
def supports_cte?(type=:select)
|
747
801
|
db.sqlite_version >= 30803
|
@@ -782,6 +836,11 @@ module Sequel
|
|
782
836
|
false
|
783
837
|
end
|
784
838
|
|
839
|
+
# SQLite 3.35.0 supports RETURNING on INSERT/UPDATE/DELETE.
|
840
|
+
def supports_returning?(_)
|
841
|
+
db.sqlite_version >= 33500
|
842
|
+
end
|
843
|
+
|
785
844
|
# SQLite supports timezones in literal timestamps, since it stores them
|
786
845
|
# as text. But using timezones in timestamps breaks SQLite datetime
|
787
846
|
# functions, so we allow the user to override the default per database.
|
@@ -814,6 +873,26 @@ module Sequel
|
|
814
873
|
|
815
874
|
private
|
816
875
|
|
876
|
+
# Add aliases to symbols and identifiers to work around SQLite bug.
|
877
|
+
def _returning_values(values)
|
878
|
+
values.map do |v|
|
879
|
+
case v
|
880
|
+
when Symbol
|
881
|
+
_, c, a = split_symbol(v)
|
882
|
+
a ? v : Sequel.as(v, c)
|
883
|
+
when SQL::Identifier, SQL::QualifiedIdentifier
|
884
|
+
Sequel.as(v, unqualified_column_for(v))
|
885
|
+
else
|
886
|
+
v
|
887
|
+
end
|
888
|
+
end
|
889
|
+
end
|
890
|
+
|
891
|
+
# Use from_self for aggregate dataset using VALUES.
|
892
|
+
def aggreate_dataset_use_from_self?
|
893
|
+
super || @opts[:values]
|
894
|
+
end
|
895
|
+
|
817
896
|
# SQLite uses string literals instead of identifiers in AS clauses.
|
818
897
|
def as_sql_append(sql, aliaz, column_aliases=nil)
|
819
898
|
raise Error, "sqlite does not support derived column lists" if column_aliases
|
@@ -851,6 +930,11 @@ module Sequel
|
|
851
930
|
500
|
852
931
|
end
|
853
932
|
|
933
|
+
# The strftime format to use when literalizing the time.
|
934
|
+
def default_timestamp_format
|
935
|
+
db.use_timestamp_timezones? ? "'%Y-%m-%d %H:%M:%S.%6N%z'" : super
|
936
|
+
end
|
937
|
+
|
854
938
|
# SQL fragment specifying a list of identifiers
|
855
939
|
def identifier_list(columns)
|
856
940
|
columns.map{|i| quote_identifier(i)}.join(', ')
|
@@ -98,6 +98,11 @@ module Sequel
|
|
98
98
|
# The conversion procs to use for this database
|
99
99
|
attr_reader :conversion_procs
|
100
100
|
|
101
|
+
def initialize(opts = OPTS)
|
102
|
+
super
|
103
|
+
@allow_regexp = typecast_value_boolean(opts[:setup_regexp_function])
|
104
|
+
end
|
105
|
+
|
101
106
|
# Connect to the database. Since SQLite is a file based database,
|
102
107
|
# available options are limited:
|
103
108
|
#
|
@@ -106,6 +111,18 @@ module Sequel
|
|
106
111
|
# static data that you do not want to modify
|
107
112
|
# :timeout :: how long to wait for the database to be available if it
|
108
113
|
# is locked, given in milliseconds (default is 5000)
|
114
|
+
# :setup_regexp_function :: enable use of Regexp objects with SQL
|
115
|
+
# 'REGEXP' operator. If the value is :cached or "cached",
|
116
|
+
# caches the generated regexps, which can result in a memory
|
117
|
+
# leak if dynamic regexps are used. If the value is a Proc,
|
118
|
+
# it will be called with a string for the regexp and a string
|
119
|
+
# for the value to compare, and should return whether the regexp
|
120
|
+
# matches.
|
121
|
+
# :regexp_function_cache :: If setting +setup_regexp_function+ to +cached+, this
|
122
|
+
# determines the cache to use. It should either be a proc or a class, and it
|
123
|
+
# defaults to +Hash+. You can use +ObjectSpace::WeakKeyMap+ on Ruby 3.3+ to
|
124
|
+
# have the VM automatically remove regexps from the cache after they
|
125
|
+
# are no longer used.
|
109
126
|
def connect(server)
|
110
127
|
opts = server_opts(server)
|
111
128
|
opts[:database] = ':memory:' if blank_object?(opts[:database])
|
@@ -119,6 +136,10 @@ module Sequel
|
|
119
136
|
end
|
120
137
|
|
121
138
|
connection_pragmas.each{|s| log_connection_yield(s, db){db.execute_batch(s)}}
|
139
|
+
|
140
|
+
if typecast_value_boolean(opts[:setup_regexp_function])
|
141
|
+
setup_regexp_function(db, opts[:setup_regexp_function])
|
142
|
+
end
|
122
143
|
|
123
144
|
class << db
|
124
145
|
attr_reader :prepared_statements
|
@@ -128,6 +149,12 @@ module Sequel
|
|
128
149
|
db
|
129
150
|
end
|
130
151
|
|
152
|
+
# Whether this Database instance is setup to allow regexp matching.
|
153
|
+
# True if the :setup_regexp_function option was passed when creating the Database.
|
154
|
+
def allow_regexp?
|
155
|
+
@allow_regexp
|
156
|
+
end
|
157
|
+
|
131
158
|
# Disconnect given connections from the database.
|
132
159
|
def disconnect_connection(c)
|
133
160
|
c.prepared_statements.each_value{|v| v.first.close}
|
@@ -185,30 +212,57 @@ module Sequel
|
|
185
212
|
@conversion_procs['datetime'] = @conversion_procs['timestamp'] = method(:to_application_timestamp)
|
186
213
|
set_integer_booleans
|
187
214
|
end
|
215
|
+
|
216
|
+
def setup_regexp_function(db, how)
|
217
|
+
case how
|
218
|
+
when Proc
|
219
|
+
# nothing
|
220
|
+
when :cached, "cached"
|
221
|
+
cache = @opts[:regexp_function_cache] || Hash
|
222
|
+
cache = cache.is_a?(Proc) ? cache.call : cache.new
|
223
|
+
how = if RUBY_VERSION >= '2.4'
|
224
|
+
lambda do |regexp_str, str|
|
225
|
+
(cache[regexp_str] ||= Regexp.new(regexp_str)).match?(str)
|
226
|
+
end
|
227
|
+
else
|
228
|
+
lambda do |regexp_str, str|
|
229
|
+
(cache[regexp_str] ||= Regexp.new(regexp_str)).match(str)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
else
|
233
|
+
how = if RUBY_VERSION >= '2.4'
|
234
|
+
lambda{|regexp_str, str| Regexp.new(regexp_str).match?(str)}
|
235
|
+
else
|
236
|
+
lambda{|regexp_str, str| Regexp.new(regexp_str).match(str)}
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
db.create_function("regexp", 2) do |func, regexp_str, str|
|
241
|
+
func.result = how.call(regexp_str, str) ? 1 : 0
|
242
|
+
end
|
243
|
+
end
|
188
244
|
|
189
245
|
# Yield an available connection. Rescue
|
190
246
|
# any SQLite3::Exceptions and turn them into DatabaseErrors.
|
191
247
|
def _execute(type, sql, opts, &block)
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
conn.changes
|
207
|
-
end
|
248
|
+
synchronize(opts[:server]) do |conn|
|
249
|
+
return execute_prepared_statement(conn, type, sql, opts, &block) if sql.is_a?(Symbol)
|
250
|
+
log_args = opts[:arguments]
|
251
|
+
args = {}
|
252
|
+
opts.fetch(:arguments, OPTS).each{|k, v| args[k] = prepared_statement_argument(v)}
|
253
|
+
case type
|
254
|
+
when :select
|
255
|
+
log_connection_yield(sql, conn, log_args){conn.query(sql, args, &block)}
|
256
|
+
when :insert
|
257
|
+
log_connection_yield(sql, conn, log_args){conn.execute(sql, args)}
|
258
|
+
conn.last_insert_row_id
|
259
|
+
when :update
|
260
|
+
log_connection_yield(sql, conn, log_args){conn.execute_batch(sql, args)}
|
261
|
+
conn.changes
|
208
262
|
end
|
209
|
-
rescue SQLite3::Exception => e
|
210
|
-
raise_error(e)
|
211
263
|
end
|
264
|
+
rescue SQLite3::Exception => e
|
265
|
+
raise_error(e)
|
212
266
|
end
|
213
267
|
|
214
268
|
# The SQLite adapter does not need the pool to convert exceptions.
|
@@ -323,6 +377,28 @@ module Sequel
|
|
323
377
|
BindArgumentMethods = prepared_statements_module(:bind, ArgumentMapper)
|
324
378
|
PreparedStatementMethods = prepared_statements_module(:prepare, BindArgumentMethods)
|
325
379
|
|
380
|
+
# Support regexp functions if using :setup_regexp_function Database option.
|
381
|
+
def complex_expression_sql_append(sql, op, args)
|
382
|
+
case op
|
383
|
+
when :~, :'!~', :'~*', :'!~*'
|
384
|
+
return super unless supports_regexp?
|
385
|
+
|
386
|
+
case_insensitive = [:'~*', :'!~*'].include?(op)
|
387
|
+
sql << 'NOT ' if [:'!~', :'!~*'].include?(op)
|
388
|
+
sql << '('
|
389
|
+
sql << 'LOWER(' if case_insensitive
|
390
|
+
literal_append(sql, args[0])
|
391
|
+
sql << ')' if case_insensitive
|
392
|
+
sql << ' REGEXP '
|
393
|
+
sql << 'LOWER(' if case_insensitive
|
394
|
+
literal_append(sql, args[1])
|
395
|
+
sql << ')' if case_insensitive
|
396
|
+
sql << ')'
|
397
|
+
else
|
398
|
+
super
|
399
|
+
end
|
400
|
+
end
|
401
|
+
|
326
402
|
def fetch_rows(sql)
|
327
403
|
execute(sql) do |result|
|
328
404
|
cps = db.conversion_procs
|
@@ -346,6 +422,11 @@ module Sequel
|
|
346
422
|
end
|
347
423
|
end
|
348
424
|
end
|
425
|
+
|
426
|
+
# Support regexp if using :setup_regexp_function Database option.
|
427
|
+
def supports_regexp?
|
428
|
+
db.allow_regexp?
|
429
|
+
end
|
349
430
|
|
350
431
|
private
|
351
432
|
|
@@ -0,0 +1,117 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
require 'trilogy'
|
4
|
+
require_relative 'shared/mysql'
|
5
|
+
|
6
|
+
module Sequel
|
7
|
+
module Trilogy
|
8
|
+
class Database < Sequel::Database
|
9
|
+
include Sequel::MySQL::DatabaseMethods
|
10
|
+
|
11
|
+
QUERY_FLAGS = ::Trilogy::QUERY_FLAGS_CAST | ::Trilogy::QUERY_FLAGS_CAST_BOOLEANS
|
12
|
+
LOCAL_TIME_QUERY_FLAGS = QUERY_FLAGS | ::Trilogy::QUERY_FLAGS_LOCAL_TIMEZONE
|
13
|
+
|
14
|
+
set_adapter_scheme :trilogy
|
15
|
+
|
16
|
+
# Connect to the database. See Trilogy documentation for options.
|
17
|
+
def connect(server)
|
18
|
+
opts = server_opts(server)
|
19
|
+
opts[:username] ||= opts.delete(:user)
|
20
|
+
opts[:found_rows] = true
|
21
|
+
conn = ::Trilogy.new(opts)
|
22
|
+
mysql_connection_setting_sqls.each{|sql| log_connection_yield(sql, conn){conn.query(sql)}}
|
23
|
+
conn
|
24
|
+
end
|
25
|
+
|
26
|
+
def disconnect_connection(c)
|
27
|
+
c.discard!
|
28
|
+
rescue ::Trilogy::Error
|
29
|
+
nil
|
30
|
+
end
|
31
|
+
|
32
|
+
# Execute the given SQL on the given connection and yield the result.
|
33
|
+
def execute(sql, opts)
|
34
|
+
r = synchronize(opts[:server]) do |conn|
|
35
|
+
log_connection_yield((log_sql = opts[:log_sql]) ? sql + log_sql : sql, conn) do
|
36
|
+
conn.query_with_flags(sql, timezone.nil? || timezone == :local ? LOCAL_TIME_QUERY_FLAGS : QUERY_FLAGS)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
yield r
|
40
|
+
rescue ::Trilogy::Error => e
|
41
|
+
raise_error(e)
|
42
|
+
end
|
43
|
+
|
44
|
+
def execute_dui(sql, opts=OPTS)
|
45
|
+
execute(sql, opts, &:affected_rows)
|
46
|
+
end
|
47
|
+
|
48
|
+
def execute_insert(sql, opts=OPTS)
|
49
|
+
execute(sql, opts, &:last_insert_id)
|
50
|
+
end
|
51
|
+
|
52
|
+
def freeze
|
53
|
+
server_version
|
54
|
+
super
|
55
|
+
end
|
56
|
+
|
57
|
+
# Return the version of the MySQL server to which we are connecting.
|
58
|
+
def server_version(_server=nil)
|
59
|
+
@server_version ||= super()
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def database_specific_error_class(exception, opts)
|
65
|
+
case exception.message
|
66
|
+
when /1205 - Lock wait timeout exceeded; try restarting transaction\z/
|
67
|
+
DatabaseLockTimeout
|
68
|
+
else
|
69
|
+
super
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def connection_execute_method
|
74
|
+
:query
|
75
|
+
end
|
76
|
+
|
77
|
+
def database_error_classes
|
78
|
+
[::Trilogy::Error]
|
79
|
+
end
|
80
|
+
|
81
|
+
def dataset_class_default
|
82
|
+
Dataset
|
83
|
+
end
|
84
|
+
|
85
|
+
# Convert tinyint(1) type to boolean if convert_tinyint_to_bool is true
|
86
|
+
def schema_column_type(db_type)
|
87
|
+
db_type =~ /\Atinyint\(1\)/ ? :boolean : super
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
class Dataset < Sequel::Dataset
|
92
|
+
include Sequel::MySQL::DatasetMethods
|
93
|
+
|
94
|
+
def fetch_rows(sql)
|
95
|
+
execute(sql) do |r|
|
96
|
+
self.columns = r.fields.map!{|c| output_identifier(c.to_s)}
|
97
|
+
r.each_hash{|h| yield h}
|
98
|
+
end
|
99
|
+
self
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
def execute(sql, opts=OPTS)
|
105
|
+
opts = Hash[opts]
|
106
|
+
opts[:type] = :select
|
107
|
+
super
|
108
|
+
end
|
109
|
+
|
110
|
+
# Handle correct quoting of strings using ::Trilogy#escape.
|
111
|
+
def literal_string_append(sql, v)
|
112
|
+
sql << "'" << db.synchronize(@opts[:server]){|c| c.escape(v)} << "'"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
module Sequel
|
4
|
+
class Dataset
|
5
|
+
module ColumnsLimit1
|
6
|
+
COLUMNS_CLONE_OPTIONS = {:distinct => nil, :limit => 1, :offset=>nil, :where=>nil, :having=>nil, :order=>nil, :row_proc=>nil, :graph=>nil, :eager_graph=>nil}.freeze
|
7
|
+
|
8
|
+
# Use a limit of 1 instead of a limit of 0 when
|
9
|
+
# getting the columns.
|
10
|
+
def columns!
|
11
|
+
ds = clone(COLUMNS_CLONE_OPTIONS)
|
12
|
+
ds.each{break}
|
13
|
+
|
14
|
+
if cols = ds.cache[:_columns]
|
15
|
+
self.columns = cols
|
16
|
+
else
|
17
|
+
[]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -34,7 +34,7 @@ module Sequel
|
|
34
34
|
def execute(sql, opts=OPTS, &block)
|
35
35
|
if opts[:sproc]
|
36
36
|
call_sproc(sql, opts, &block)
|
37
|
-
elsif sql.is_a?(Symbol)
|
37
|
+
elsif sql.is_a?(Symbol) || sql.is_a?(Sequel::Dataset::ArgumentMapper)
|
38
38
|
execute_prepared_statement(sql, opts, &block)
|
39
39
|
else
|
40
40
|
synchronize(opts[:server]){|conn| _execute(conn, sql, opts, &block)}
|
@@ -80,6 +80,12 @@ module Sequel
|
|
80
80
|
SQL::DelayedEvaluation.new(lambda{|ds| v(o.call(ds))})
|
81
81
|
when SQL::Wrapper
|
82
82
|
SQL::Wrapper.new(v(o.value))
|
83
|
+
when SQL::Expression
|
84
|
+
if o.respond_to?(:sequel_ast_transform)
|
85
|
+
o.sequel_ast_transform(method(:v))
|
86
|
+
else
|
87
|
+
o
|
88
|
+
end
|
83
89
|
else
|
84
90
|
o
|
85
91
|
end
|
@@ -55,13 +55,11 @@ class Sequel::ShardedSingleConnectionPool < Sequel::ConnectionPool
|
|
55
55
|
# Yields the connection to the supplied block for the given server.
|
56
56
|
# This method simulates the ConnectionPool#hold API.
|
57
57
|
def hold(server=:default)
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
raise
|
64
|
-
end
|
58
|
+
server = pick_server(server)
|
59
|
+
yield(@conns[server] ||= make_new(server))
|
60
|
+
rescue Sequel::DatabaseDisconnectError, *@error_classes => e
|
61
|
+
disconnect_server(server) if disconnect_error?(e)
|
62
|
+
raise
|
65
63
|
end
|
66
64
|
|
67
65
|
# The ShardedSingleConnectionPool always has a maximum size of 1.
|