litestack 0.1.6 → 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -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