litestack 0.1.5 → 0.1.7

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.
@@ -0,0 +1,1054 @@
1
+ # frozen-string-literal: true
2
+
3
+ require_relative '../utils/replace'
4
+ require_relative '../utils/unmodified_identifiers'
5
+
6
+ module Sequel
7
+ module Litedb
8
+ Sequel::Database.set_shared_adapter_scheme(:litedb, self)
9
+
10
+ def self.mock_adapter_setup(db)
11
+ db.instance_exec do
12
+ @sqlite_version = 30903
13
+
14
+ def schema_parse_table(*)
15
+ []
16
+ end
17
+ singleton_class.send(:private, :schema_parse_table)
18
+ end
19
+ end
20
+
21
+ # No matter how you connect to SQLite, the following Database options
22
+ # can be used to set PRAGMAs on connections in a thread-safe manner:
23
+ # :auto_vacuum, :foreign_keys, :synchronous, and :temp_store.
24
+ module DatabaseMethods
25
+ include UnmodifiedIdentifiers::DatabaseMethods
26
+
27
+ AUTO_VACUUM = [:none, :full, :incremental].freeze
28
+ SYNCHRONOUS = [:off, :normal, :full].freeze
29
+ TEMP_STORE = [:default, :file, :memory].freeze
30
+ TRANSACTION_MODE = {
31
+ :deferred => "BEGIN DEFERRED TRANSACTION".freeze,
32
+ :immediate => "BEGIN IMMEDIATE TRANSACTION".freeze,
33
+ :exclusive => "BEGIN EXCLUSIVE TRANSACTION".freeze,
34
+ nil => "BEGIN".freeze
35
+ }.freeze
36
+
37
+ # Whether to use integers for booleans in the database. SQLite recommends
38
+ # booleans be stored as integers, but historically Sequel has used 't'/'f'.
39
+ attr_accessor :integer_booleans
40
+
41
+ # Whether to keep CURRENT_TIMESTAMP and similar expressions in UTC. By
42
+ # default, the expressions are converted to localtime.
43
+ attr_accessor :current_timestamp_utc
44
+
45
+ # A symbol signifying the value of the default transaction mode
46
+ attr_reader :transaction_mode
47
+
48
+ # Set the default transaction mode.
49
+ def transaction_mode=(value)
50
+ if TRANSACTION_MODE.include?(value)
51
+ @transaction_mode = value
52
+ else
53
+ raise Error, "Invalid value for transaction_mode. Please specify one of :deferred, :immediate, :exclusive, nil"
54
+ end
55
+ end
56
+
57
+ # SQLite uses the :sqlite database type.
58
+ def database_type
59
+ :litedb
60
+ end
61
+
62
+ # Set the integer_booleans option using the passed in :integer_boolean option.
63
+ def set_integer_booleans
64
+ @integer_booleans = @opts.has_key?(:integer_booleans) ? typecast_value_boolean(@opts[:integer_booleans]) : true
65
+ end
66
+
67
+ # Return the array of foreign key info hashes using the foreign_key_list PRAGMA,
68
+ # including information for the :on_update and :on_delete entries.
69
+ def foreign_key_list(table, opts=OPTS)
70
+ m = output_identifier_meth
71
+ h = {}
72
+ _foreign_key_list_ds(table).each do |row|
73
+ if r = h[row[:id]]
74
+ r[:columns] << m.call(row[:from])
75
+ r[:key] << m.call(row[:to]) if r[:key]
76
+ else
77
+ h[row[:id]] = {:columns=>[m.call(row[:from])], :table=>m.call(row[:table]), :key=>([m.call(row[:to])] if row[:to]), :on_update=>on_delete_sql_to_sym(row[:on_update]), :on_delete=>on_delete_sql_to_sym(row[:on_delete])}
78
+ end
79
+ end
80
+ h.values
81
+ end
82
+
83
+ def freeze
84
+ sqlite_version
85
+ use_timestamp_timezones?
86
+ super
87
+ end
88
+
89
+ # Use the index_list and index_info PRAGMAs to determine the indexes on the table.
90
+ def indexes(table, opts=OPTS)
91
+ m = output_identifier_meth
92
+ im = input_identifier_meth
93
+ indexes = {}
94
+ table = table.value if table.is_a?(Sequel::SQL::Identifier)
95
+ metadata_dataset.with_sql("PRAGMA index_list(?)", im.call(table)).each do |r|
96
+ if opts[:only_autocreated]
97
+ # If specifically asked for only autocreated indexes, then return those an only those
98
+ next unless r[:name] =~ /\Asqlite_autoindex_/
99
+ elsif r.has_key?(:origin)
100
+ # If origin is set, then only exclude primary key indexes and partial indexes
101
+ next if r[:origin] == 'pk'
102
+ next if r[:partial].to_i == 1
103
+ else
104
+ # When :origin key not present, assume any autoindex could be a primary key one and exclude it
105
+ next if r[:name] =~ /\Asqlite_autoindex_/
106
+ end
107
+
108
+ indexes[m.call(r[:name])] = {:unique=>r[:unique].to_i==1}
109
+ end
110
+ indexes.each do |k, v|
111
+ v[:columns] = metadata_dataset.with_sql("PRAGMA index_info(?)", im.call(k)).map(:name).map{|x| m.call(x)}
112
+ end
113
+ indexes
114
+ end
115
+
116
+ # The version of the server as an integer, where 3.6.19 = 30619.
117
+ # If the server version can't be determined, 0 is used.
118
+ def sqlite_version
119
+ return @sqlite_version if defined?(@sqlite_version)
120
+ @sqlite_version = begin
121
+ v = fetch('SELECT sqlite_version()').single_value
122
+ [10000, 100, 1].zip(v.split('.')).inject(0){|a, m| a + m[0] * Integer(m[1])}
123
+ rescue
124
+ 0
125
+ end
126
+ end
127
+
128
+ # SQLite supports CREATE TABLE IF NOT EXISTS syntax since 3.3.0.
129
+ def supports_create_table_if_not_exists?
130
+ sqlite_version >= 30300
131
+ end
132
+
133
+ # SQLite 3.6.19+ supports deferrable foreign key constraints.
134
+ def supports_deferrable_foreign_key_constraints?
135
+ sqlite_version >= 30619
136
+ end
137
+
138
+ # SQLite 3.8.0+ supports partial indexes.
139
+ def supports_partial_indexes?
140
+ sqlite_version >= 30800
141
+ end
142
+
143
+ # SQLite 3.6.8+ supports savepoints.
144
+ def supports_savepoints?
145
+ sqlite_version >= 30608
146
+ end
147
+
148
+ # Override the default setting for whether to use timezones in timestamps.
149
+ # It is set to +false+ by default, as SQLite's date/time methods do not
150
+ # support timezones in timestamps.
151
+ attr_writer :use_timestamp_timezones
152
+
153
+ # SQLite supports timezones in timestamps, since it just stores them as strings,
154
+ # but it breaks the usage of SQLite's datetime functions.
155
+ def use_timestamp_timezones?
156
+ defined?(@use_timestamp_timezones) ? @use_timestamp_timezones : (@use_timestamp_timezones = false)
157
+ end
158
+
159
+ # Array of symbols specifying the table names in the current database.
160
+ #
161
+ # Options:
162
+ # :server :: Set the server to use.
163
+ def tables(opts=OPTS)
164
+ tables_and_views(Sequel.~(:name=>'sqlite_sequence') & {:type => 'table'}, opts)
165
+ end
166
+
167
+ # Creates a dataset that uses the VALUES clause:
168
+ #
169
+ # DB.values([[1, 2], [3, 4]])
170
+ # # VALUES ((1, 2), (3, 4))
171
+ def values(v)
172
+ raise Error, "Cannot provide an empty array for values" if v.empty?
173
+ @default_dataset.clone(:values=>v)
174
+ end
175
+
176
+ # Array of symbols specifying the view names in the current database.
177
+ #
178
+ # Options:
179
+ # :server :: Set the server to use.
180
+ def views(opts=OPTS)
181
+ tables_and_views({:type => 'view'}, opts)
182
+ end
183
+
184
+ private
185
+
186
+ # Dataset used for parsing foreign key lists
187
+ def _foreign_key_list_ds(table)
188
+ metadata_dataset.with_sql("PRAGMA foreign_key_list(?)", input_identifier_meth.call(table))
189
+ end
190
+
191
+ # Dataset used for parsing schema
192
+ def _parse_pragma_ds(table_name, opts)
193
+ metadata_dataset.with_sql("PRAGMA table_#{'x' if sqlite_version > 33100}info(?)", input_identifier_meth(opts[:dataset]).call(table_name))
194
+ end
195
+
196
+ # Run all alter_table commands in a transaction. This is technically only
197
+ # needed for drop column.
198
+ def apply_alter_table(table, ops)
199
+ fks = fetch("PRAGMA foreign_keys")
200
+ if fks
201
+ run "PRAGMA foreign_keys = 0"
202
+ run "PRAGMA legacy_alter_table = 1" if sqlite_version >= 32600
203
+ end
204
+ transaction do
205
+ if ops.length > 1 && ops.all?{|op| op[:op] == :add_constraint || op[:op] == :set_column_null}
206
+ null_ops, ops = ops.partition{|op| op[:op] == :set_column_null}
207
+
208
+ # Apply NULL/NOT NULL ops first, since those should be purely idependent of the constraints.
209
+ null_ops.each{|op| alter_table_sql_list(table, [op]).flatten.each{|sql| execute_ddl(sql)}}
210
+
211
+ # If you are just doing constraints, apply all of them at the same time,
212
+ # as otherwise all but the last one get lost.
213
+ alter_table_sql_list(table, [{:op=>:add_constraints, :ops=>ops}]).flatten.each{|sql| execute_ddl(sql)}
214
+ else
215
+ # Run each operation separately, as later operations may depend on the
216
+ # results of earlier operations.
217
+ ops.each{|op| alter_table_sql_list(table, [op]).flatten.each{|sql| execute_ddl(sql)}}
218
+ end
219
+ end
220
+ remove_cached_schema(table)
221
+ ensure
222
+ if fks
223
+ run "PRAGMA foreign_keys = 1"
224
+ run "PRAGMA legacy_alter_table = 0" if sqlite_version >= 32600
225
+ end
226
+ end
227
+
228
+ # SQLite supports limited table modification. You can add a column
229
+ # or an index. Dropping columns is supported by copying the table into
230
+ # a temporary table, dropping the table, and creating a new table without
231
+ # the column inside of a transaction.
232
+ def alter_table_sql(table, op)
233
+ case op[:op]
234
+ when :add_index, :drop_index
235
+ super
236
+ when :add_column
237
+ if op[:unique] || op[:primary_key]
238
+ duplicate_table(table){|columns| columns.push(op)}
239
+ else
240
+ super
241
+ end
242
+ when :drop_column
243
+ if sqlite_version >= 33500
244
+ super
245
+ else
246
+ ocp = lambda{|oc| oc.delete_if{|c| c.to_s == op[:name].to_s}}
247
+ duplicate_table(table, :old_columns_proc=>ocp){|columns| columns.delete_if{|s| s[:name].to_s == op[:name].to_s}}
248
+ end
249
+ when :rename_column
250
+ if sqlite_version >= 32500
251
+ super
252
+ else
253
+ ncp = lambda{|nc| nc.map!{|c| c.to_s == op[:name].to_s ? op[:new_name] : c}}
254
+ duplicate_table(table, :new_columns_proc=>ncp){|columns| columns.each{|s| s[:name] = op[:new_name] if s[:name].to_s == op[:name].to_s}}
255
+ end
256
+ when :set_column_default
257
+ duplicate_table(table){|columns| columns.each{|s| s[:default] = op[:default] if s[:name].to_s == op[:name].to_s}}
258
+ when :set_column_null
259
+ duplicate_table(table){|columns| columns.each{|s| s[:null] = op[:null] if s[:name].to_s == op[:name].to_s}}
260
+ when :set_column_type
261
+ duplicate_table(table){|columns| columns.each{|s| s.merge!(op) if s[:name].to_s == op[:name].to_s}}
262
+ when :drop_constraint
263
+ case op[:type]
264
+ when :primary_key
265
+ duplicate_table(table) do |columns|
266
+ columns.each do |s|
267
+ s[:unique] = false if s[:primary_key]
268
+ s[:primary_key] = s[:auto_increment] = nil
269
+ end
270
+ end
271
+ when :foreign_key
272
+ if op[:columns]
273
+ duplicate_table(table, :skip_foreign_key_columns=>op[:columns])
274
+ else
275
+ duplicate_table(table, :no_foreign_keys=>true)
276
+ end
277
+ when :unique
278
+ duplicate_table(table, :no_unique=>true)
279
+ else
280
+ duplicate_table(table)
281
+ end
282
+ when :add_constraint
283
+ duplicate_table(table, :constraints=>[op])
284
+ when :add_constraints
285
+ duplicate_table(table, :constraints=>op[:ops])
286
+ else
287
+ raise Error, "Unsupported ALTER TABLE operation: #{op[:op].inspect}"
288
+ end
289
+ end
290
+
291
+ def begin_new_transaction(conn, opts)
292
+ mode = opts[:mode] || @transaction_mode
293
+ sql = TRANSACTION_MODE[mode] or raise Error, "transaction :mode must be one of: :deferred, :immediate, :exclusive, nil"
294
+ log_connection_execute(conn, sql)
295
+ set_transaction_isolation(conn, opts)
296
+ end
297
+
298
+ # A name to use for the backup table
299
+ def backup_table_name(table, opts=OPTS)
300
+ table = table.gsub('`', '')
301
+ (opts[:times]||1000).times do |i|
302
+ table_name = "#{table}_backup#{i}"
303
+ return table_name unless table_exists?(table_name)
304
+ end
305
+ end
306
+
307
+ # SQLite allows adding primary key constraints on NULLABLE columns, but then
308
+ # does not enforce NOT NULL for such columns, so force setting the columns NOT NULL.
309
+ def can_add_primary_key_constraint_on_nullable_columns?
310
+ false
311
+ end
312
+
313
+ # Surround default with parens to appease SQLite. Add support for GENERATED ALWAYS AS.
314
+ def column_definition_default_sql(sql, column)
315
+ sql << " DEFAULT (#{literal(column[:default])})" if column.include?(:default)
316
+ if (generated = column[:generated_always_as])
317
+ if (generated_type = column[:generated_type]) && (generated_type == :stored || generated_type == :virtual)
318
+ generated_type = generated_type.to_s.upcase
319
+ end
320
+ sql << " GENERATED ALWAYS AS (#{literal(generated)}) #{generated_type}"
321
+ end
322
+ end
323
+
324
+ # SQLite does not restrict the integer or decimal type to a specific range.
325
+ def column_schema_integer_min_max_values(column)
326
+ nil
327
+ end
328
+ alias column_schema_decimal_min_max_values column_schema_integer_min_max_values
329
+
330
+ # Array of PRAGMA SQL statements based on the Database options that should be applied to
331
+ # new connections.
332
+ def connection_pragmas
333
+ ps = []
334
+ v = typecast_value_boolean(opts.fetch(:foreign_keys, 1))
335
+ ps << "PRAGMA foreign_keys = #{v ? 1 : 0}"
336
+ v = typecast_value_boolean(opts.fetch(:case_sensitive_like, 1))
337
+ ps << "PRAGMA case_sensitive_like = #{v ? 1 : 0}"
338
+ [[:auto_vacuum, AUTO_VACUUM], [:synchronous, SYNCHRONOUS], [:temp_store, TEMP_STORE]].each do |prag, con|
339
+ if v = opts[prag]
340
+ raise(Error, "Value for PRAGMA #{prag} not supported, should be one of #{con.join(', ')}") unless v = con.index(v.to_sym)
341
+ ps << "PRAGMA #{prag} = #{v}"
342
+ end
343
+ end
344
+ ps
345
+ end
346
+
347
+ # Support creating STRICT tables via :strict option
348
+ def create_table_sql(name, generator, options)
349
+ "#{super}#{' STRICT' if options[:strict]}"
350
+ end
351
+
352
+ # SQLite support creating temporary views.
353
+ def create_view_prefix_sql(name, options)
354
+ create_view_sql_append_columns("CREATE #{'TEMPORARY 'if options[:temp]}VIEW #{quote_schema_table(name)}", options[:columns])
355
+ end
356
+
357
+ DATABASE_ERROR_REGEXPS = {
358
+ /(is|are) not unique\z|PRIMARY KEY must be unique\z|UNIQUE constraint failed: .+\z/ => UniqueConstraintViolation,
359
+ /foreign key constraint failed\z/i => ForeignKeyConstraintViolation,
360
+ /\ASQLITE ERROR 3091/ => CheckConstraintViolation,
361
+ /\A(SQLITE ERROR 275 \(CONSTRAINT_CHECK\) : )?CHECK constraint failed/ => CheckConstraintViolation,
362
+ /\A(SQLITE ERROR 19 \(CONSTRAINT\) : )?constraint failed\z/ => ConstraintViolation,
363
+ /\Acannot store [A-Z]+ value in [A-Z]+ column / => ConstraintViolation,
364
+ /may not be NULL\z|NOT NULL constraint failed: .+\z/ => NotNullConstraintViolation,
365
+ /\ASQLITE ERROR \d+ \(\) : CHECK constraint failed: / => CheckConstraintViolation
366
+ }.freeze
367
+ def database_error_regexps
368
+ DATABASE_ERROR_REGEXPS
369
+ end
370
+
371
+ # Recognize SQLite error codes if the exception provides access to them.
372
+ def database_specific_error_class(exception, opts)
373
+ case sqlite_error_code(exception)
374
+ when 1299
375
+ NotNullConstraintViolation
376
+ when 1555, 2067, 2579
377
+ UniqueConstraintViolation
378
+ when 787
379
+ ForeignKeyConstraintViolation
380
+ when 275
381
+ CheckConstraintViolation
382
+ when 19
383
+ ConstraintViolation
384
+ when 517
385
+ SerializationFailure
386
+ else
387
+ super
388
+ end
389
+ end
390
+
391
+ # The array of column schema hashes for the current columns in the table
392
+ def defined_columns_for(table)
393
+ cols = parse_pragma(table, OPTS)
394
+ cols.each do |c|
395
+ c[:default] = LiteralString.new(c[:default]) if c[:default]
396
+ c[:type] = c[:db_type]
397
+ end
398
+ cols
399
+ end
400
+
401
+ # Duplicate an existing table by creating a new table, copying all records
402
+ # from the existing table into the new table, deleting the existing table
403
+ # and renaming the new table to the existing table's name.
404
+ def duplicate_table(table, opts=OPTS)
405
+ remove_cached_schema(table)
406
+ def_columns = defined_columns_for(table)
407
+ old_columns = def_columns.map{|c| c[:name]}
408
+ opts[:old_columns_proc].call(old_columns) if opts[:old_columns_proc]
409
+
410
+ yield def_columns if defined?(yield)
411
+
412
+ constraints = (opts[:constraints] || []).dup
413
+ pks = []
414
+ def_columns.each{|c| pks << c[:name] if c[:primary_key]}
415
+ if pks.length > 1
416
+ constraints << {:type=>:primary_key, :columns=>pks}
417
+ def_columns.each{|c| c[:primary_key] = false if c[:primary_key]}
418
+ end
419
+
420
+ # If dropping a foreign key constraint, drop all foreign key constraints,
421
+ # as there is no way to determine which one to drop.
422
+ unless opts[:no_foreign_keys]
423
+ fks = foreign_key_list(table)
424
+
425
+ # If dropping a column, if there is a foreign key with that
426
+ # column, don't include it when building a copy of the table.
427
+ if ocp = opts[:old_columns_proc]
428
+ fks.delete_if{|c| ocp.call(c[:columns].dup) != c[:columns]}
429
+ end
430
+
431
+ # Skip any foreign key columns where a constraint for those
432
+ # foreign keys is being dropped.
433
+ if sfkc = opts[:skip_foreign_key_columns]
434
+ fks.delete_if{|c| c[:columns] == sfkc}
435
+ end
436
+
437
+ constraints.concat(fks.each{|h| h[:type] = :foreign_key})
438
+ end
439
+
440
+ # Determine unique constraints and make sure the new columns have them
441
+ unique_columns = []
442
+ skip_indexes = []
443
+ indexes(table, :only_autocreated=>true).each do |name, h|
444
+ skip_indexes << name
445
+ if h[:unique] && !opts[:no_unique]
446
+ if h[:columns].length == 1
447
+ unique_columns.concat(h[:columns])
448
+ elsif h[:columns].map(&:to_s) != pks
449
+ constraints << {:type=>:unique, :columns=>h[:columns]}
450
+ end
451
+ end
452
+ end
453
+ unique_columns -= pks
454
+ unless unique_columns.empty?
455
+ unique_columns.map!{|c| quote_identifier(c)}
456
+ def_columns.each do |c|
457
+ c[:unique] = true if unique_columns.include?(quote_identifier(c[:name])) && c[:unique] != false
458
+ end
459
+ end
460
+
461
+ def_columns_str = (def_columns.map{|c| column_definition_sql(c)} + constraints.map{|c| constraint_definition_sql(c)}).join(', ')
462
+ new_columns = old_columns.dup
463
+ opts[:new_columns_proc].call(new_columns) if opts[:new_columns_proc]
464
+
465
+ qt = quote_schema_table(table)
466
+ bt = quote_identifier(backup_table_name(qt))
467
+ a = [
468
+ "ALTER TABLE #{qt} RENAME TO #{bt}",
469
+ "CREATE TABLE #{qt}(#{def_columns_str})",
470
+ "INSERT INTO #{qt}(#{dataset.send(:identifier_list, new_columns)}) SELECT #{dataset.send(:identifier_list, old_columns)} FROM #{bt}",
471
+ "DROP TABLE #{bt}"
472
+ ]
473
+ indexes(table).each do |name, h|
474
+ next if skip_indexes.include?(name)
475
+ if (h[:columns].map(&:to_s) - new_columns).empty?
476
+ a << alter_table_sql(table, h.merge(:op=>:add_index, :name=>name))
477
+ end
478
+ end
479
+ a
480
+ end
481
+
482
+ # Does the reverse of on_delete_clause, eg. converts strings like +'SET NULL'+
483
+ # to symbols +:set_null+.
484
+ def on_delete_sql_to_sym(str)
485
+ case str
486
+ when 'RESTRICT'
487
+ :restrict
488
+ when 'CASCADE'
489
+ :cascade
490
+ when 'SET NULL'
491
+ :set_null
492
+ when 'SET DEFAULT'
493
+ :set_default
494
+ when 'NO ACTION'
495
+ :no_action
496
+ end
497
+ end
498
+
499
+ # Parse the output of the table_info pragma
500
+ def parse_pragma(table_name, opts)
501
+ pks = 0
502
+ sch = _parse_pragma_ds(table_name, opts).map do |row|
503
+ if sqlite_version > 33100
504
+ # table_xinfo PRAGMA used, remove hidden columns
505
+ # that are not generated columns
506
+ if row[:generated] = (row.delete(:hidden) != 0)
507
+ next unless row[:type].end_with?(' GENERATED ALWAYS')
508
+ row[:type] = row[:type].sub(' GENERATED ALWAYS', '')
509
+ end
510
+ end
511
+
512
+ row.delete(:cid)
513
+ row[:allow_null] = row.delete(:notnull).to_i == 0
514
+ row[:default] = row.delete(:dflt_value)
515
+ row[:default] = nil if blank_object?(row[:default]) || row[:default] == 'NULL'
516
+ row[:db_type] = row.delete(:type)
517
+ if row[:primary_key] = row.delete(:pk).to_i > 0
518
+ pks += 1
519
+ # Guess that an integer primary key uses auto increment,
520
+ # since that is Sequel's default and SQLite does not provide
521
+ # a way to introspect whether it is actually autoincrementing.
522
+ row[:auto_increment] = row[:db_type].downcase == 'integer'
523
+ end
524
+ row[:type] = schema_column_type(row[:db_type])
525
+ row
526
+ end
527
+
528
+ sch.compact!
529
+
530
+ if pks > 1
531
+ # SQLite does not allow use of auto increment for tables
532
+ # with composite primary keys, so remove auto_increment
533
+ # if composite primary keys are detected.
534
+ sch.each{|r| r.delete(:auto_increment)}
535
+ end
536
+
537
+ sch
538
+ end
539
+
540
+ # SQLite supports schema parsing using the table_info PRAGMA, so
541
+ # parse the output of that into the format Sequel expects.
542
+ def schema_parse_table(table_name, opts)
543
+ m = output_identifier_meth(opts[:dataset])
544
+ parse_pragma(table_name, opts).map do |row|
545
+ [m.call(row.delete(:name)), row]
546
+ end
547
+ end
548
+
549
+ # Don't support SQLite error codes for exceptions by default.
550
+ def sqlite_error_code(exception)
551
+ nil
552
+ end
553
+
554
+ # Backbone of the tables and views support.
555
+ def tables_and_views(filter, opts)
556
+ m = output_identifier_meth
557
+ metadata_dataset.from(:sqlite_master).server(opts[:server]).where(filter).map{|r| m.call(r[:name])}
558
+ end
559
+
560
+ # SQLite only supports AUTOINCREMENT on integer columns, not
561
+ # bigint columns, so use integer instead of bigint for those
562
+ # columns.
563
+ def type_literal_generic_bignum_symbol(column)
564
+ column[:auto_increment] ? :integer : super
565
+ end
566
+ end
567
+
568
+ module DatasetMethods
569
+ include Dataset::Replace
570
+ include UnmodifiedIdentifiers::DatasetMethods
571
+
572
+ # The allowed values for insert_conflict
573
+ INSERT_CONFLICT_RESOLUTIONS = %w'ROLLBACK ABORT FAIL IGNORE REPLACE'.each(&:freeze).freeze
574
+
575
+ CONSTANT_MAP = {:CURRENT_DATE=>"date(CURRENT_TIMESTAMP, 'localtime')".freeze, :CURRENT_TIMESTAMP=>"datetime(CURRENT_TIMESTAMP, 'localtime')".freeze, :CURRENT_TIME=>"time(CURRENT_TIMESTAMP, 'localtime')".freeze}.freeze
576
+ EXTRACT_MAP = {:year=>"'%Y'", :month=>"'%m'", :day=>"'%d'", :hour=>"'%H'", :minute=>"'%M'", :second=>"'%f'"}.freeze
577
+ EXTRACT_MAP.each_value(&:freeze)
578
+
579
+ 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']])
580
+ 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']])
581
+ 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']])
582
+ 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']])
583
+
584
+ def cast_sql_append(sql, expr, type)
585
+ if type == Time or type == DateTime
586
+ sql << "datetime("
587
+ literal_append(sql, expr)
588
+ sql << ')'
589
+ elsif type == Date
590
+ sql << "date("
591
+ literal_append(sql, expr)
592
+ sql << ')'
593
+ else
594
+ super
595
+ end
596
+ end
597
+
598
+ # SQLite doesn't support a NOT LIKE b, you need to use NOT (a LIKE b).
599
+ # It doesn't support xor, power, or the extract function natively, so those have to be emulated.
600
+ def complex_expression_sql_append(sql, op, args)
601
+ case op
602
+ when :"NOT LIKE", :"NOT ILIKE"
603
+ sql << 'NOT '
604
+ complex_expression_sql_append(sql, (op == :"NOT ILIKE" ? :ILIKE : :LIKE), args)
605
+ when :^
606
+ complex_expression_arg_pairs_append(sql, args){|a, b| Sequel.lit(["((~(", " & ", ")) & (", " | ", "))"], a, b, a, b)}
607
+ when :**
608
+ unless (exp = args[1]).is_a?(Integer)
609
+ raise(Sequel::Error, "can only emulate exponentiation on SQLite if exponent is an integer, given #{exp.inspect}")
610
+ end
611
+ case exp
612
+ when 0
613
+ sql << '1'
614
+ else
615
+ sql << '('
616
+ arg = args[0]
617
+ if exp < 0
618
+ invert = true
619
+ exp = exp.abs
620
+ sql << '(1.0 / ('
621
+ end
622
+ (exp - 1).times do
623
+ literal_append(sql, arg)
624
+ sql << " * "
625
+ end
626
+ literal_append(sql, arg)
627
+ sql << ')'
628
+ if invert
629
+ sql << "))"
630
+ end
631
+ end
632
+ when :extract
633
+ part = args[0]
634
+ raise(Sequel::Error, "unsupported extract argument: #{part.inspect}") unless format = EXTRACT_MAP[part]
635
+ sql << "CAST(strftime(" << format << ', '
636
+ literal_append(sql, args[1])
637
+ sql << ') AS ' << (part == :second ? 'NUMERIC' : 'INTEGER') << ')'
638
+ else
639
+ super
640
+ end
641
+ end
642
+
643
+ # SQLite has CURRENT_TIMESTAMP and related constants in UTC instead
644
+ # of in localtime, so convert those constants to local time.
645
+ def constant_sql_append(sql, constant)
646
+ if (c = CONSTANT_MAP[constant]) && !db.current_timestamp_utc
647
+ sql << c
648
+ else
649
+ super
650
+ end
651
+ end
652
+
653
+ # SQLite performs a TRUNCATE style DELETE if no filter is specified.
654
+ # Since we want to always return the count of records, add a condition
655
+ # that is always true and then delete.
656
+ def delete(&block)
657
+ @opts[:where] ? super : where(1=>1).delete(&block)
658
+ end
659
+
660
+ # Always return false when using VALUES
661
+ def empty?
662
+ return false if @opts[:values]
663
+ super
664
+ end
665
+
666
+ # Return an array of strings specifying a query explanation for a SELECT of the
667
+ # current dataset. Currently, the options are ignored, but it accepts options
668
+ # to be compatible with other adapters.
669
+ def explain(opts=nil)
670
+ # Load the PrettyTable class, needed for explain output
671
+ Sequel.extension(:_pretty_table) unless defined?(Sequel::PrettyTable)
672
+
673
+ ds = db.send(:metadata_dataset).clone(:sql=>"EXPLAIN #{select_sql}")
674
+ rows = ds.all
675
+ Sequel::PrettyTable.string(rows, ds.columns)
676
+ end
677
+
678
+ # HAVING requires GROUP BY on SQLite
679
+ def having(*cond)
680
+ raise(InvalidOperation, "Can only specify a HAVING clause on a grouped dataset") if !@opts[:group] && db.sqlite_version < 33900
681
+ super
682
+ end
683
+
684
+ # Support insert select for associations, so that the model code can use
685
+ # returning instead of a separate query.
686
+ def insert_select(*values)
687
+ return unless supports_insert_select?
688
+ # Handle case where query does not return a row
689
+ server?(:default).with_sql_first(insert_select_sql(*values)) || false
690
+ end
691
+
692
+ # The SQL to use for an insert_select, adds a RETURNING clause to the insert
693
+ # unless the RETURNING clause is already present.
694
+ def insert_select_sql(*values)
695
+ ds = opts[:returning] ? self : returning
696
+ ds.insert_sql(*values)
697
+ end
698
+
699
+ # SQLite uses the nonstandard ` (backtick) for quoting identifiers.
700
+ def quoted_identifier_append(sql, c)
701
+ sql << '`' << c.to_s.gsub('`', '``') << '`'
702
+ end
703
+
704
+ # When a qualified column is selected on SQLite and the qualifier
705
+ # is a subselect, the column name used is the full qualified name
706
+ # (including the qualifier) instead of just the column name. To
707
+ # get correct column names, you must use an alias.
708
+ def select(*cols)
709
+ if ((f = @opts[:from]) && f.any?{|t| t.is_a?(Dataset) || (t.is_a?(SQL::AliasedExpression) && t.expression.is_a?(Dataset))}) || ((j = @opts[:join]) && j.any?{|t| t.table.is_a?(Dataset)})
710
+ super(*cols.map{|c| alias_qualified_column(c)})
711
+ else
712
+ super
713
+ end
714
+ end
715
+
716
+ # Handle uniqueness violations when inserting, by using a specified
717
+ # resolution algorithm. With no options, uses INSERT OR REPLACE. SQLite
718
+ # supports the following conflict resolution algoriths: ROLLBACK, ABORT,
719
+ # FAIL, IGNORE and REPLACE.
720
+ #
721
+ # On SQLite 3.24.0+, you can pass a hash to use an ON CONFLICT clause.
722
+ # With out :update option, uses ON CONFLICT DO NOTHING. Options:
723
+ #
724
+ # :conflict_where :: The index filter, when using a partial index to determine uniqueness.
725
+ # :target :: The column name or expression to handle uniqueness violations on.
726
+ # :update :: A hash of columns and values to set. Uses ON CONFLICT DO UPDATE.
727
+ # :update_where :: A WHERE condition to use for the update.
728
+ #
729
+ # Examples:
730
+ #
731
+ # DB[:table].insert_conflict.insert(a: 1, b: 2)
732
+ # # INSERT OR IGNORE INTO TABLE (a, b) VALUES (1, 2)
733
+ #
734
+ # DB[:table].insert_conflict(:replace).insert(a: 1, b: 2)
735
+ # # INSERT OR REPLACE INTO TABLE (a, b) VALUES (1, 2)
736
+ #
737
+ # DB[:table].insert_conflict({}).insert(a: 1, b: 2)
738
+ # # INSERT INTO TABLE (a, b) VALUES (1, 2)
739
+ # # ON CONFLICT DO NOTHING
740
+ #
741
+ # DB[:table].insert_conflict(target: :a).insert(a: 1, b: 2)
742
+ # # INSERT INTO TABLE (a, b) VALUES (1, 2)
743
+ # # ON CONFLICT (a) DO NOTHING
744
+ #
745
+ # DB[:table].insert_conflict(target: :a, conflict_where: {c: true}).insert(a: 1, b: 2)
746
+ # # INSERT INTO TABLE (a, b) VALUES (1, 2)
747
+ # # ON CONFLICT (a) WHERE (c IS TRUE) DO NOTHING
748
+ #
749
+ # DB[:table].insert_conflict(target: :a, update: {b: Sequel[:excluded][:b]}).insert(a: 1, b: 2)
750
+ # # INSERT INTO TABLE (a, b) VALUES (1, 2)
751
+ # # ON CONFLICT (a) DO UPDATE SET b = excluded.b
752
+ #
753
+ # DB[:table].insert_conflict(target: :a,
754
+ # update: {b: Sequel[:excluded][:b]}, update_where: {Sequel[:table][:status_id] => 1}).insert(a: 1, b: 2)
755
+ # # INSERT INTO TABLE (a, b) VALUES (1, 2)
756
+ # # ON CONFLICT (a) DO UPDATE SET b = excluded.b WHERE (table.status_id = 1)
757
+ def insert_conflict(opts = :ignore)
758
+ case opts
759
+ when Symbol, String
760
+ unless INSERT_CONFLICT_RESOLUTIONS.include?(opts.to_s.upcase)
761
+ raise Error, "Invalid symbol or string passed to Dataset#insert_conflict: #{opts.inspect}. The allowed values are: :rollback, :abort, :fail, :ignore, or :replace"
762
+ end
763
+ clone(:insert_conflict => opts)
764
+ when Hash
765
+ clone(:insert_on_conflict => opts)
766
+ else
767
+ raise Error, "Invalid value passed to Dataset#insert_conflict: #{opts.inspect}, should use a symbol or a hash"
768
+ end
769
+ end
770
+
771
+ # Ignore uniqueness/exclusion violations when inserting, using INSERT OR IGNORE.
772
+ # Exists mostly for compatibility to MySQL's insert_ignore. Example:
773
+ #
774
+ # DB[:table].insert_ignore.insert(a: 1, b: 2)
775
+ # # INSERT OR IGNORE INTO TABLE (a, b) VALUES (1, 2)
776
+ def insert_ignore
777
+ insert_conflict(:ignore)
778
+ end
779
+
780
+ # Automatically add aliases to RETURNING values to work around SQLite bug.
781
+ def returning(*values)
782
+ return super if values.empty?
783
+ raise Error, "RETURNING is not supported on #{db.database_type}" unless supports_returning?(:insert)
784
+ clone(:returning=>_returning_values(values).freeze)
785
+ end
786
+
787
+ # SQLite 3.8.3+ supports common table expressions.
788
+ def supports_cte?(type=:select)
789
+ db.sqlite_version >= 30803
790
+ end
791
+
792
+ # SQLite supports CTEs in subqueries if it supports CTEs.
793
+ def supports_cte_in_subqueries?
794
+ supports_cte?
795
+ end
796
+
797
+ # SQLite does not support table aliases with column aliases
798
+ def supports_derived_column_lists?
799
+ false
800
+ end
801
+
802
+ # SQLite does not support deleting from a joined dataset
803
+ def supports_deleting_joins?
804
+ false
805
+ end
806
+
807
+ # SQLite does not support INTERSECT ALL or EXCEPT ALL
808
+ def supports_intersect_except_all?
809
+ false
810
+ end
811
+
812
+ # SQLite does not support IS TRUE
813
+ def supports_is_true?
814
+ false
815
+ end
816
+
817
+ # SQLite 3.33.0 supports modifying joined datasets
818
+ def supports_modifying_joins?
819
+ db.sqlite_version >= 33300
820
+ end
821
+
822
+ # SQLite does not support multiple columns for the IN/NOT IN operators
823
+ def supports_multiple_column_in?
824
+ false
825
+ end
826
+
827
+ # SQLite 3.35.0 supports RETURNING on INSERT/UPDATE/DELETE.
828
+ def supports_returning?(_)
829
+ db.sqlite_version >= 33500
830
+ end
831
+
832
+ # SQLite supports timezones in literal timestamps, since it stores them
833
+ # as text. But using timezones in timestamps breaks SQLite datetime
834
+ # functions, so we allow the user to override the default per database.
835
+ def supports_timestamp_timezones?
836
+ db.use_timestamp_timezones?
837
+ end
838
+
839
+ # SQLite cannot use WHERE 't'.
840
+ def supports_where_true?
841
+ false
842
+ end
843
+
844
+ # SQLite 3.28+ supports the WINDOW clause.
845
+ def supports_window_clause?
846
+ db.sqlite_version >= 32800
847
+ end
848
+
849
+ # SQLite 3.25+ supports window functions. However, support is only enabled
850
+ # on SQLite 3.26.0+ because internal Sequel usage of window functions
851
+ # to implement eager loading of limited associations triggers
852
+ # an SQLite crash bug in versions 3.25.0-3.25.3.
853
+ def supports_window_functions?
854
+ db.sqlite_version >= 32600
855
+ end
856
+
857
+ # SQLite 3.28.0+ supports all window frame options that Sequel supports
858
+ def supports_window_function_frame_option?(option)
859
+ db.sqlite_version >= 32800 ? true : super
860
+ end
861
+
862
+ private
863
+
864
+ # Add aliases to symbols and identifiers to work around SQLite bug.
865
+ def _returning_values(values)
866
+ values.map do |v|
867
+ case v
868
+ when Symbol
869
+ _, c, a = split_symbol(v)
870
+ a ? v : Sequel.as(v, c)
871
+ when SQL::Identifier, SQL::QualifiedIdentifier
872
+ Sequel.as(v, unqualified_column_for(v))
873
+ else
874
+ v
875
+ end
876
+ end
877
+ end
878
+
879
+ # Use from_self for aggregate dataset using VALUES.
880
+ def aggreate_dataset_use_from_self?
881
+ super || @opts[:values]
882
+ end
883
+
884
+ # SQLite uses string literals instead of identifiers in AS clauses.
885
+ def as_sql_append(sql, aliaz, column_aliases=nil)
886
+ raise Error, "sqlite does not support derived column lists" if column_aliases
887
+ aliaz = aliaz.value if aliaz.is_a?(SQL::Identifier)
888
+ sql << ' AS '
889
+ literal_append(sql, aliaz.to_s)
890
+ end
891
+
892
+ # If col is a qualified column, alias it to the same as the column name
893
+ def alias_qualified_column(col)
894
+ case col
895
+ when Symbol
896
+ t, c, a = split_symbol(col)
897
+ if t && !a
898
+ alias_qualified_column(SQL::QualifiedIdentifier.new(t, c))
899
+ else
900
+ col
901
+ end
902
+ when SQL::QualifiedIdentifier
903
+ SQL::AliasedExpression.new(col, col.column)
904
+ else
905
+ col
906
+ end
907
+ end
908
+
909
+ # Raise an InvalidOperation exception if insert is not allowed for this dataset.
910
+ def check_insert_allowed!
911
+ raise(InvalidOperation, "Grouped datasets cannot be modified") if opts[:group]
912
+ raise(InvalidOperation, "Joined datasets cannot be modified") if joined_dataset?
913
+ end
914
+ alias check_delete_allowed! check_insert_allowed!
915
+
916
+ # SQLite supports a maximum of 500 rows in a VALUES clause.
917
+ def default_import_slice
918
+ 500
919
+ end
920
+
921
+ # SQL fragment specifying a list of identifiers
922
+ def identifier_list(columns)
923
+ columns.map{|i| quote_identifier(i)}.join(', ')
924
+ end
925
+
926
+ # Add OR clauses to SQLite INSERT statements
927
+ def insert_conflict_sql(sql)
928
+ if resolution = @opts[:insert_conflict]
929
+ sql << " OR " << resolution.to_s.upcase
930
+ end
931
+ end
932
+
933
+ # Add ON CONFLICT clause if it should be used
934
+ def insert_on_conflict_sql(sql)
935
+ if opts = @opts[:insert_on_conflict]
936
+ sql << " ON CONFLICT"
937
+
938
+ if target = opts[:constraint]
939
+ sql << " ON CONSTRAINT "
940
+ identifier_append(sql, target)
941
+ elsif target = opts[:target]
942
+ sql << ' '
943
+ identifier_append(sql, Array(target))
944
+ if conflict_where = opts[:conflict_where]
945
+ sql << " WHERE "
946
+ literal_append(sql, conflict_where)
947
+ end
948
+ end
949
+
950
+ if values = opts[:update]
951
+ sql << " DO UPDATE SET "
952
+ update_sql_values_hash(sql, values)
953
+ if update_where = opts[:update_where]
954
+ sql << " WHERE "
955
+ literal_append(sql, update_where)
956
+ end
957
+ else
958
+ sql << " DO NOTHING"
959
+ end
960
+ end
961
+ end
962
+
963
+ # SQLite uses a preceding X for hex escaping strings
964
+ def literal_blob_append(sql, v)
965
+ sql << "X'" << v.unpack("H*").first << "'"
966
+ end
967
+
968
+ # Respect the database integer_booleans setting, using 0 or 'f'.
969
+ def literal_false
970
+ @db.integer_booleans ? '0' : "'f'"
971
+ end
972
+
973
+ # Respect the database integer_booleans setting, using 1 or 't'.
974
+ def literal_true
975
+ @db.integer_booleans ? '1' : "'t'"
976
+ end
977
+
978
+ # SQLite only supporting multiple rows in the VALUES clause
979
+ # starting in 3.7.11. On older versions, fallback to using a UNION.
980
+ def multi_insert_sql_strategy
981
+ db.sqlite_version >= 30711 ? :values : :union
982
+ end
983
+
984
+ # Emulate the char_length function with length
985
+ def native_function_name(emulated_function)
986
+ if emulated_function == :char_length
987
+ 'length'
988
+ else
989
+ super
990
+ end
991
+ end
992
+
993
+ # SQLite supports NULLS FIRST/LAST natively in 3.30+.
994
+ def requires_emulating_nulls_first?
995
+ db.sqlite_version < 33000
996
+ end
997
+
998
+ # SQLite does not support FOR UPDATE, but silently ignore it
999
+ # instead of raising an error for compatibility with other
1000
+ # databases.
1001
+ def select_lock_sql(sql)
1002
+ super unless @opts[:lock] == :update
1003
+ end
1004
+
1005
+ def select_only_offset_sql(sql)
1006
+ sql << " LIMIT -1 OFFSET "
1007
+ literal_append(sql, @opts[:offset])
1008
+ end
1009
+
1010
+ # Support VALUES clause instead of the SELECT clause to return rows.
1011
+ def select_values_sql(sql)
1012
+ sql << "VALUES "
1013
+ expression_list_append(sql, opts[:values])
1014
+ end
1015
+
1016
+ # SQLite does not support CTEs directly inside UNION/INTERSECT/EXCEPT.
1017
+ def supports_cte_in_compounds?
1018
+ false
1019
+ end
1020
+
1021
+ # SQLite 3.30 supports the FILTER clause for aggregate functions.
1022
+ def supports_filtered_aggregates?
1023
+ db.sqlite_version >= 33000
1024
+ end
1025
+
1026
+ # SQLite supports quoted function names.
1027
+ def supports_quoted_function_names?
1028
+ true
1029
+ end
1030
+
1031
+ # SQLite treats a DELETE with no WHERE clause as a TRUNCATE
1032
+ def _truncate_sql(table)
1033
+ "DELETE FROM #{table}"
1034
+ end
1035
+
1036
+ # Use FROM to specify additional tables in an update query
1037
+ def update_from_sql(sql)
1038
+ if(from = @opts[:from][1..-1]).empty?
1039
+ raise(Error, 'Need multiple FROM tables if updating/deleting a dataset with JOINs') if @opts[:join]
1040
+ else
1041
+ sql << ' FROM '
1042
+ source_list_append(sql, from)
1043
+ select_join_sql(sql)
1044
+ end
1045
+ end
1046
+
1047
+ # Only include the primary table in the main update clause
1048
+ def update_table_sql(sql)
1049
+ sql << ' '
1050
+ source_list_append(sql, @opts[:from][0..0])
1051
+ end
1052
+ end
1053
+ end
1054
+ end