litestack 0.4.1 → 0.4.2
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/.standard.yml +3 -0
- data/BENCHMARKS.md +23 -7
- data/CHANGELOG.md +11 -0
- data/Gemfile +1 -7
- data/Gemfile.lock +92 -0
- data/README.md +120 -6
- data/ROADMAP.md +45 -0
- data/Rakefile +3 -1
- data/WHYLITESTACK.md +1 -1
- data/assets/litecache_metrics.png +0 -0
- data/assets/litedb_metrics.png +0 -0
- data/assets/litemetric_logo_teal.png +0 -0
- data/assets/litesearch_logo_teal.png +0 -0
- data/bench/bench.rb +17 -10
- data/bench/bench_cache_rails.rb +10 -13
- data/bench/bench_cache_raw.rb +17 -22
- data/bench/bench_jobs_rails.rb +18 -12
- data/bench/bench_jobs_raw.rb +17 -10
- data/bench/bench_queue.rb +4 -6
- data/bench/rails_job.rb +5 -7
- data/bench/skjob.rb +4 -4
- data/bench/uljob.rb +6 -6
- data/lib/action_cable/subscription_adapter/litecable.rb +5 -8
- data/lib/active_job/queue_adapters/litejob_adapter.rb +6 -8
- data/lib/active_record/connection_adapters/litedb_adapter.rb +65 -75
- data/lib/active_support/cache/litecache.rb +38 -41
- data/lib/generators/litestack/install/install_generator.rb +3 -3
- data/lib/generators/litestack/install/templates/database.yml +7 -1
- data/lib/litestack/liteboard/liteboard.rb +269 -149
- data/lib/litestack/litecable.rb +41 -37
- data/lib/litestack/litecable.sql.yml +22 -11
- data/lib/litestack/litecache.rb +79 -88
- data/lib/litestack/litecache.sql.yml +81 -22
- data/lib/litestack/litecache.yml +1 -1
- data/lib/litestack/litedb.rb +35 -40
- data/lib/litestack/litejob.rb +30 -29
- data/lib/litestack/litejobqueue.rb +63 -65
- data/lib/litestack/litemetric.rb +80 -92
- data/lib/litestack/litemetric.sql.yml +244 -234
- data/lib/litestack/litemetric_collector.sql.yml +38 -41
- data/lib/litestack/litequeue.rb +39 -41
- data/lib/litestack/litequeue.sql.yml +39 -31
- data/lib/litestack/litescheduler.rb +15 -15
- data/lib/litestack/litesearch/index.rb +93 -63
- data/lib/litestack/litesearch/model.rb +66 -65
- data/lib/litestack/litesearch/schema.rb +53 -56
- data/lib/litestack/litesearch/schema_adapters/backed_adapter.rb +46 -50
- data/lib/litestack/litesearch/schema_adapters/basic_adapter.rb +44 -35
- data/lib/litestack/litesearch/schema_adapters/contentless_adapter.rb +3 -6
- data/lib/litestack/litesearch/schema_adapters/standalone_adapter.rb +7 -9
- data/lib/litestack/litesearch/schema_adapters.rb +4 -9
- data/lib/litestack/litesearch.rb +6 -9
- data/lib/litestack/litesupport.rb +76 -86
- data/lib/litestack/railtie.rb +1 -1
- data/lib/litestack/version.rb +2 -2
- data/lib/litestack.rb +6 -4
- data/lib/railties/rails/commands/dbconsole.rb +11 -15
- data/lib/sequel/adapters/litedb.rb +16 -21
- data/lib/sequel/adapters/shared/litedb.rb +168 -168
- data/scripts/build_metrics.rb +91 -0
- data/scripts/test_cable.rb +30 -0
- data/scripts/test_job_retry.rb +33 -0
- data/scripts/test_metrics.rb +60 -0
- data/template.rb +2 -2
- metadata +101 -6
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen-string-literal: true
|
2
2
|
|
3
|
-
require_relative
|
4
|
-
require_relative
|
3
|
+
require_relative "../utils/replace"
|
4
|
+
require_relative "../utils/unmodified_identifiers"
|
5
5
|
|
6
6
|
module Sequel
|
7
7
|
module Litedb
|
@@ -17,7 +17,7 @@ module Sequel
|
|
17
17
|
singleton_class.send(:private, :schema_parse_table)
|
18
18
|
end
|
19
19
|
end
|
20
|
-
|
20
|
+
|
21
21
|
# No matter how you connect to SQLite, the following Database options
|
22
22
|
# can be used to set PRAGMAs on connections in a thread-safe manner:
|
23
23
|
# :auto_vacuum, :foreign_keys, :synchronous, and :temp_store.
|
@@ -28,10 +28,10 @@ module Sequel
|
|
28
28
|
SYNCHRONOUS = [:off, :normal, :full].freeze
|
29
29
|
TEMP_STORE = [:default, :file, :memory].freeze
|
30
30
|
TRANSACTION_MODE = {
|
31
|
-
:deferred => "BEGIN DEFERRED TRANSACTION"
|
32
|
-
:immediate => "BEGIN IMMEDIATE TRANSACTION"
|
33
|
-
:exclusive => "BEGIN EXCLUSIVE TRANSACTION"
|
34
|
-
nil => "BEGIN"
|
31
|
+
:deferred => "BEGIN DEFERRED TRANSACTION",
|
32
|
+
:immediate => "BEGIN IMMEDIATE TRANSACTION",
|
33
|
+
:exclusive => "BEGIN EXCLUSIVE TRANSACTION",
|
34
|
+
nil => "BEGIN"
|
35
35
|
}.freeze
|
36
36
|
|
37
37
|
# Whether to use integers for booleans in the database. SQLite recommends
|
@@ -58,7 +58,7 @@ module Sequel
|
|
58
58
|
def database_type
|
59
59
|
:litedb
|
60
60
|
end
|
61
|
-
|
61
|
+
|
62
62
|
# Set the integer_booleans option using the passed in :integer_boolean option.
|
63
63
|
def set_integer_booleans
|
64
64
|
@integer_booleans = @opts.has_key?(:integer_booleans) ? typecast_value_boolean(@opts[:integer_booleans]) : true
|
@@ -66,15 +66,15 @@ module Sequel
|
|
66
66
|
|
67
67
|
# Return the array of foreign key info hashes using the foreign_key_list PRAGMA,
|
68
68
|
# including information for the :on_update and :on_delete entries.
|
69
|
-
def foreign_key_list(table, opts=OPTS)
|
69
|
+
def foreign_key_list(table, opts = OPTS)
|
70
70
|
m = output_identifier_meth
|
71
71
|
h = {}
|
72
72
|
_foreign_key_list_ds(table).each do |row|
|
73
|
-
if r = h[row[:id]]
|
73
|
+
if (r = h[row[:id]])
|
74
74
|
r[:columns] << m.call(row[:from])
|
75
75
|
r[:key] << m.call(row[:to]) if r[:key]
|
76
76
|
else
|
77
|
-
h[row[:id]] = {:
|
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
78
|
end
|
79
79
|
end
|
80
80
|
h.values
|
@@ -87,7 +87,7 @@ module Sequel
|
|
87
87
|
end
|
88
88
|
|
89
89
|
# Use the index_list and index_info PRAGMAs to determine the indexes on the table.
|
90
|
-
def indexes(table, opts=OPTS)
|
90
|
+
def indexes(table, opts = OPTS)
|
91
91
|
m = output_identifier_meth
|
92
92
|
im = input_identifier_meth
|
93
93
|
indexes = {}
|
@@ -95,20 +95,20 @@ module Sequel
|
|
95
95
|
metadata_dataset.with_sql("PRAGMA index_list(?)", im.call(table)).each do |r|
|
96
96
|
if opts[:only_autocreated]
|
97
97
|
# If specifically asked for only autocreated indexes, then return those an only those
|
98
|
-
next unless r[:name]
|
98
|
+
next unless r[:name].start_with?("sqlite_autoindex_")
|
99
99
|
elsif r.has_key?(:origin)
|
100
100
|
# If origin is set, then only exclude primary key indexes and partial indexes
|
101
|
-
next if r[:origin] ==
|
101
|
+
next if r[:origin] == "pk"
|
102
102
|
next if r[:partial].to_i == 1
|
103
|
-
|
104
|
-
|
105
|
-
next if r[:name] =~ /\Asqlite_autoindex_/
|
103
|
+
elsif r[:name].start_with?("sqlite_autoindex_")
|
104
|
+
next
|
106
105
|
end
|
106
|
+
# When :origin key not present, assume any autoindex could be a primary key one and exclude it
|
107
107
|
|
108
|
-
indexes[m.call(r[:name])] = {:
|
108
|
+
indexes[m.call(r[:name])] = {unique: r[:unique].to_i == 1}
|
109
109
|
end
|
110
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)}
|
111
|
+
v[:columns] = metadata_dataset.with_sql("PRAGMA index_info(?)", im.call(k)).map(:name).map { |x| m.call(x) }
|
112
112
|
end
|
113
113
|
indexes
|
114
114
|
end
|
@@ -118,18 +118,18 @@ module Sequel
|
|
118
118
|
def sqlite_version
|
119
119
|
return @sqlite_version if defined?(@sqlite_version)
|
120
120
|
@sqlite_version = begin
|
121
|
-
v = fetch(
|
122
|
-
[10000, 100, 1].zip(v.split(
|
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
123
|
rescue
|
124
124
|
0
|
125
125
|
end
|
126
126
|
end
|
127
|
-
|
127
|
+
|
128
128
|
# SQLite supports CREATE TABLE IF NOT EXISTS syntax since 3.3.0.
|
129
129
|
def supports_create_table_if_not_exists?
|
130
130
|
sqlite_version >= 30300
|
131
131
|
end
|
132
|
-
|
132
|
+
|
133
133
|
# SQLite 3.6.19+ supports deferrable foreign key constraints.
|
134
134
|
def supports_deferrable_foreign_key_constraints?
|
135
135
|
sqlite_version >= 30619
|
@@ -140,7 +140,7 @@ module Sequel
|
|
140
140
|
sqlite_version >= 30800
|
141
141
|
end
|
142
142
|
|
143
|
-
# SQLite 3.6.8+ supports savepoints.
|
143
|
+
# SQLite 3.6.8+ supports savepoints.
|
144
144
|
def supports_savepoints?
|
145
145
|
sqlite_version >= 30608
|
146
146
|
end
|
@@ -160,25 +160,25 @@ module Sequel
|
|
160
160
|
#
|
161
161
|
# Options:
|
162
162
|
# :server :: Set the server to use.
|
163
|
-
def tables(opts=OPTS)
|
164
|
-
tables_and_views(Sequel.~(:
|
163
|
+
def tables(opts = OPTS)
|
164
|
+
tables_and_views(Sequel.~(name: "sqlite_sequence") & {type: "table"}, opts)
|
165
165
|
end
|
166
|
-
|
166
|
+
|
167
167
|
# Creates a dataset that uses the VALUES clause:
|
168
168
|
#
|
169
169
|
# DB.values([[1, 2], [3, 4]])
|
170
170
|
# # VALUES ((1, 2), (3, 4))
|
171
171
|
def values(v)
|
172
172
|
raise Error, "Cannot provide an empty array for values" if v.empty?
|
173
|
-
@default_dataset.clone(:
|
173
|
+
@default_dataset.clone(values: v)
|
174
174
|
end
|
175
175
|
|
176
176
|
# Array of symbols specifying the view names in the current database.
|
177
177
|
#
|
178
178
|
# Options:
|
179
179
|
# :server :: Set the server to use.
|
180
|
-
def views(opts=OPTS)
|
181
|
-
tables_and_views({:
|
180
|
+
def views(opts = OPTS)
|
181
|
+
tables_and_views({type: "view"}, opts)
|
182
182
|
end
|
183
183
|
|
184
184
|
private
|
@@ -190,7 +190,7 @@ module Sequel
|
|
190
190
|
|
191
191
|
# Dataset used for parsing schema
|
192
192
|
def _parse_pragma_ds(table_name, opts)
|
193
|
-
metadata_dataset.with_sql("PRAGMA table_#{
|
193
|
+
metadata_dataset.with_sql("PRAGMA table_#{"x" if sqlite_version > 33100}info(?)", input_identifier_meth(opts[:dataset]).call(table_name))
|
194
194
|
end
|
195
195
|
|
196
196
|
# Run all alter_table commands in a transaction. This is technically only
|
@@ -201,20 +201,20 @@ module Sequel
|
|
201
201
|
run "PRAGMA foreign_keys = 0"
|
202
202
|
run "PRAGMA legacy_alter_table = 1" if sqlite_version >= 32600
|
203
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}
|
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
207
|
|
208
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)}}
|
209
|
+
null_ops.each { |op| alter_table_sql_list(table, [op]).flatten.each { |sql| execute_ddl(sql) } }
|
210
210
|
|
211
211
|
# If you are just doing constraints, apply all of them at the same time,
|
212
212
|
# as otherwise all but the last one get lost.
|
213
|
-
alter_table_sql_list(table, [{:
|
213
|
+
alter_table_sql_list(table, [{op: :add_constraints, ops: ops}]).flatten.each { |sql| execute_ddl(sql) }
|
214
214
|
else
|
215
215
|
# Run each operation separately, as later operations may depend on the
|
216
216
|
# results of earlier operations.
|
217
|
-
ops.each{|op| alter_table_sql_list(table, [op]).flatten.each{|sql| execute_ddl(sql)}}
|
217
|
+
ops.each { |op| alter_table_sql_list(table, [op]).flatten.each { |sql| execute_ddl(sql) } }
|
218
218
|
end
|
219
219
|
end
|
220
220
|
remove_cached_schema(table)
|
@@ -235,7 +235,7 @@ module Sequel
|
|
235
235
|
super
|
236
236
|
when :add_column
|
237
237
|
if op[:unique] || op[:primary_key]
|
238
|
-
duplicate_table(table){|columns| columns.push(op)}
|
238
|
+
duplicate_table(table) { |columns| columns.push(op) }
|
239
239
|
else
|
240
240
|
super
|
241
241
|
end
|
@@ -243,22 +243,22 @@ module Sequel
|
|
243
243
|
if sqlite_version >= 33500
|
244
244
|
super
|
245
245
|
else
|
246
|
-
ocp = lambda{|oc| oc.delete_if{|c| c.to_s == op[:name].to_s}}
|
247
|
-
duplicate_table(table, :
|
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
248
|
end
|
249
249
|
when :rename_column
|
250
250
|
if sqlite_version >= 32500
|
251
251
|
super
|
252
252
|
else
|
253
|
-
ncp = lambda{|nc| nc.map!{|c| c.to_s == op[:name].to_s ? op[:new_name] : c}}
|
254
|
-
duplicate_table(table, :
|
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
255
|
end
|
256
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}}
|
257
|
+
duplicate_table(table) { |columns| columns.each { |s| s[:default] = op[:default] if s[:name].to_s == op[:name].to_s } }
|
258
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}}
|
259
|
+
duplicate_table(table) { |columns| columns.each { |s| s[:null] = op[:null] if s[:name].to_s == op[:name].to_s } }
|
260
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}}
|
261
|
+
duplicate_table(table) { |columns| columns.each { |s| s.merge!(op) if s[:name].to_s == op[:name].to_s } }
|
262
262
|
when :drop_constraint
|
263
263
|
case op[:type]
|
264
264
|
when :primary_key
|
@@ -270,19 +270,19 @@ module Sequel
|
|
270
270
|
end
|
271
271
|
when :foreign_key
|
272
272
|
if op[:columns]
|
273
|
-
duplicate_table(table, :
|
273
|
+
duplicate_table(table, skip_foreign_key_columns: op[:columns])
|
274
274
|
else
|
275
|
-
duplicate_table(table, :
|
275
|
+
duplicate_table(table, no_foreign_keys: true)
|
276
276
|
end
|
277
277
|
when :unique
|
278
|
-
duplicate_table(table, :
|
278
|
+
duplicate_table(table, no_unique: true)
|
279
279
|
else
|
280
280
|
duplicate_table(table)
|
281
281
|
end
|
282
282
|
when :add_constraint
|
283
|
-
duplicate_table(table, :
|
283
|
+
duplicate_table(table, constraints: [op])
|
284
284
|
when :add_constraints
|
285
|
-
duplicate_table(table, :
|
285
|
+
duplicate_table(table, constraints: op[:ops])
|
286
286
|
else
|
287
287
|
raise Error, "Unsupported ALTER TABLE operation: #{op[:op].inspect}"
|
288
288
|
end
|
@@ -296,9 +296,9 @@ module Sequel
|
|
296
296
|
end
|
297
297
|
|
298
298
|
# A name to use for the backup table
|
299
|
-
def backup_table_name(table, opts=OPTS)
|
300
|
-
table = table.
|
301
|
-
(opts[:times]||1000).times do |i|
|
299
|
+
def backup_table_name(table, opts = OPTS)
|
300
|
+
table = table.delete("`")
|
301
|
+
(opts[:times] || 1000).times do |i|
|
302
302
|
table_name = "#{table}_backup#{i}"
|
303
303
|
return table_name unless table_exists?(table_name)
|
304
304
|
end
|
@@ -320,12 +320,12 @@ module Sequel
|
|
320
320
|
sql << " GENERATED ALWAYS AS (#{literal(generated)}) #{generated_type}"
|
321
321
|
end
|
322
322
|
end
|
323
|
-
|
323
|
+
|
324
324
|
# SQLite does not restrict the integer or decimal type to a specific range.
|
325
325
|
def column_schema_integer_min_max_values(column)
|
326
326
|
nil
|
327
327
|
end
|
328
|
-
|
328
|
+
alias_method :column_schema_decimal_min_max_values, :column_schema_integer_min_max_values
|
329
329
|
|
330
330
|
# Array of PRAGMA SQL statements based on the Database options that should be applied to
|
331
331
|
# new connections.
|
@@ -336,8 +336,8 @@ module Sequel
|
|
336
336
|
v = typecast_value_boolean(opts.fetch(:case_sensitive_like, 1))
|
337
337
|
ps << "PRAGMA case_sensitive_like = #{v ? 1 : 0}"
|
338
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(
|
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
341
|
ps << "PRAGMA #{prag} = #{v}"
|
342
342
|
end
|
343
343
|
end
|
@@ -346,12 +346,12 @@ module Sequel
|
|
346
346
|
|
347
347
|
# Support creating STRICT tables via :strict option
|
348
348
|
def create_table_sql(name, generator, options)
|
349
|
-
"#{super}#{
|
349
|
+
"#{super}#{" STRICT" if options[:strict]}"
|
350
350
|
end
|
351
351
|
|
352
352
|
# SQLite support creating temporary views.
|
353
353
|
def create_view_prefix_sql(name, options)
|
354
|
-
create_view_sql_append_columns("CREATE #{
|
354
|
+
create_view_sql_append_columns("CREATE #{"TEMPORARY " if options[:temp]}VIEW #{quote_schema_table(name)}", options[:columns])
|
355
355
|
end
|
356
356
|
|
357
357
|
DATABASE_ERROR_REGEXPS = {
|
@@ -401,20 +401,20 @@ module Sequel
|
|
401
401
|
# Duplicate an existing table by creating a new table, copying all records
|
402
402
|
# from the existing table into the new table, deleting the existing table
|
403
403
|
# and renaming the new table to the existing table's name.
|
404
|
-
def duplicate_table(table, opts=OPTS)
|
404
|
+
def duplicate_table(table, opts = OPTS)
|
405
405
|
remove_cached_schema(table)
|
406
406
|
def_columns = defined_columns_for(table)
|
407
|
-
old_columns = def_columns.map{|c| c[:name]}
|
408
|
-
opts[:old_columns_proc]
|
407
|
+
old_columns = def_columns.map { |c| c[:name] }
|
408
|
+
opts[:old_columns_proc]&.call(old_columns)
|
409
409
|
|
410
410
|
yield def_columns if defined?(yield)
|
411
411
|
|
412
412
|
constraints = (opts[:constraints] || []).dup
|
413
413
|
pks = []
|
414
|
-
def_columns.each{|c| pks << c[:name] if c[:primary_key]}
|
414
|
+
def_columns.each { |c| pks << c[:name] if c[:primary_key] }
|
415
415
|
if pks.length > 1
|
416
|
-
constraints << {:
|
417
|
-
def_columns.each{|c| c[:primary_key] = false if c[:primary_key]}
|
416
|
+
constraints << {type: :primary_key, columns: pks}
|
417
|
+
def_columns.each { |c| c[:primary_key] = false if c[:primary_key] }
|
418
418
|
end
|
419
419
|
|
420
420
|
# If dropping a foreign key constraint, drop all foreign key constraints,
|
@@ -424,56 +424,56 @@ module Sequel
|
|
424
424
|
|
425
425
|
# If dropping a column, if there is a foreign key with that
|
426
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]}
|
427
|
+
if (ocp = opts[:old_columns_proc])
|
428
|
+
fks.delete_if { |c| ocp.call(c[:columns].dup) != c[:columns] }
|
429
429
|
end
|
430
|
-
|
430
|
+
|
431
431
|
# Skip any foreign key columns where a constraint for those
|
432
432
|
# foreign keys is being dropped.
|
433
|
-
if sfkc = opts[:skip_foreign_key_columns]
|
434
|
-
fks.delete_if{|c| c[:columns] == sfkc}
|
433
|
+
if (sfkc = opts[:skip_foreign_key_columns])
|
434
|
+
fks.delete_if { |c| c[:columns] == sfkc }
|
435
435
|
end
|
436
436
|
|
437
|
-
constraints.concat(fks.each{|h| h[:type] = :foreign_key})
|
437
|
+
constraints.concat(fks.each { |h| h[:type] = :foreign_key })
|
438
438
|
end
|
439
439
|
|
440
440
|
# Determine unique constraints and make sure the new columns have them
|
441
441
|
unique_columns = []
|
442
442
|
skip_indexes = []
|
443
|
-
indexes(table, :
|
443
|
+
indexes(table, only_autocreated: true).each do |name, h|
|
444
444
|
skip_indexes << name
|
445
445
|
if h[:unique] && !opts[:no_unique]
|
446
446
|
if h[:columns].length == 1
|
447
447
|
unique_columns.concat(h[:columns])
|
448
448
|
elsif h[:columns].map(&:to_s) != pks
|
449
|
-
constraints << {:
|
449
|
+
constraints << {type: :unique, columns: h[:columns]}
|
450
450
|
end
|
451
451
|
end
|
452
452
|
end
|
453
453
|
unique_columns -= pks
|
454
454
|
unless unique_columns.empty?
|
455
|
-
unique_columns.map!{|c| quote_identifier(c)}
|
455
|
+
unique_columns.map! { |c| quote_identifier(c) }
|
456
456
|
def_columns.each do |c|
|
457
457
|
c[:unique] = true if unique_columns.include?(quote_identifier(c[:name])) && c[:unique] != false
|
458
458
|
end
|
459
459
|
end
|
460
|
-
|
461
|
-
def_columns_str = (def_columns.map{|c| column_definition_sql(c)} + constraints.map{|c| constraint_definition_sql(c)}).join(
|
460
|
+
|
461
|
+
def_columns_str = (def_columns.map { |c| column_definition_sql(c) } + constraints.map { |c| constraint_definition_sql(c) }).join(", ")
|
462
462
|
new_columns = old_columns.dup
|
463
|
-
opts[:new_columns_proc]
|
463
|
+
opts[:new_columns_proc]&.call(new_columns)
|
464
464
|
|
465
465
|
qt = quote_schema_table(table)
|
466
466
|
bt = quote_identifier(backup_table_name(qt))
|
467
467
|
a = [
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
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
472
|
]
|
473
473
|
indexes(table).each do |name, h|
|
474
474
|
next if skip_indexes.include?(name)
|
475
475
|
if (h[:columns].map(&:to_s) - new_columns).empty?
|
476
|
-
a << alter_table_sql(table, h.merge(:
|
476
|
+
a << alter_table_sql(table, h.merge(op: :add_index, name: name))
|
477
477
|
end
|
478
478
|
end
|
479
479
|
a
|
@@ -483,15 +483,15 @@ module Sequel
|
|
483
483
|
# to symbols +:set_null+.
|
484
484
|
def on_delete_sql_to_sym(str)
|
485
485
|
case str
|
486
|
-
when
|
486
|
+
when "RESTRICT"
|
487
487
|
:restrict
|
488
|
-
when
|
488
|
+
when "CASCADE"
|
489
489
|
:cascade
|
490
|
-
when
|
490
|
+
when "SET NULL"
|
491
491
|
:set_null
|
492
|
-
when
|
492
|
+
when "SET DEFAULT"
|
493
493
|
:set_default
|
494
|
-
when
|
494
|
+
when "NO ACTION"
|
495
495
|
:no_action
|
496
496
|
end
|
497
497
|
end
|
@@ -503,23 +503,23 @@ module Sequel
|
|
503
503
|
if sqlite_version > 33100
|
504
504
|
# table_xinfo PRAGMA used, remove hidden columns
|
505
505
|
# that are not generated columns
|
506
|
-
if row[:generated]
|
507
|
-
next unless row[:type].end_with?(
|
508
|
-
row[:type] = row[:type].sub(
|
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
509
|
end
|
510
510
|
end
|
511
511
|
|
512
512
|
row.delete(:cid)
|
513
513
|
row[:allow_null] = row.delete(:notnull).to_i == 0
|
514
514
|
row[:default] = row.delete(:dflt_value)
|
515
|
-
row[:default] = nil if blank_object?(row[:default]) || row[:default] ==
|
515
|
+
row[:default] = nil if blank_object?(row[:default]) || row[:default] == "NULL"
|
516
516
|
row[:db_type] = row.delete(:type)
|
517
|
-
if row[:primary_key]
|
517
|
+
if row[:primary_key] == row.delete(:pk).to_i > 0
|
518
518
|
pks += 1
|
519
519
|
# Guess that an integer primary key uses auto increment,
|
520
520
|
# since that is Sequel's default and SQLite does not provide
|
521
521
|
# a way to introspect whether it is actually autoincrementing.
|
522
|
-
row[:auto_increment] = row[:db_type].downcase ==
|
522
|
+
row[:auto_increment] = row[:db_type].downcase == "integer"
|
523
523
|
end
|
524
524
|
row[:type] = schema_column_type(row[:db_type])
|
525
525
|
row
|
@@ -531,12 +531,12 @@ module Sequel
|
|
531
531
|
# SQLite does not allow use of auto increment for tables
|
532
532
|
# with composite primary keys, so remove auto_increment
|
533
533
|
# if composite primary keys are detected.
|
534
|
-
sch.each{|r| r.delete(:auto_increment)}
|
534
|
+
sch.each { |r| r.delete(:auto_increment) }
|
535
535
|
end
|
536
536
|
|
537
537
|
sch
|
538
538
|
end
|
539
|
-
|
539
|
+
|
540
540
|
# SQLite supports schema parsing using the table_info PRAGMA, so
|
541
541
|
# parse the output of that into the format Sequel expects.
|
542
542
|
def schema_parse_table(table_name, opts)
|
@@ -545,7 +545,7 @@ module Sequel
|
|
545
545
|
[m.call(row.delete(:name)), row]
|
546
546
|
end
|
547
547
|
end
|
548
|
-
|
548
|
+
|
549
549
|
# Don't support SQLite error codes for exceptions by default.
|
550
550
|
def sqlite_error_code(exception)
|
551
551
|
nil
|
@@ -554,7 +554,7 @@ module Sequel
|
|
554
554
|
# Backbone of the tables and views support.
|
555
555
|
def tables_and_views(filter, opts)
|
556
556
|
m = output_identifier_meth
|
557
|
-
metadata_dataset.from(:sqlite_master).server(opts[:server]).where(filter).map{|r| m.call(r[:name])}
|
557
|
+
metadata_dataset.from(:sqlite_master).server(opts[:server]).where(filter).map { |r| m.call(r[:name]) }
|
558
558
|
end
|
559
559
|
|
560
560
|
# SQLite only supports AUTOINCREMENT on integer columns, not
|
@@ -564,32 +564,32 @@ module Sequel
|
|
564
564
|
column[:auto_increment] ? :integer : super
|
565
565
|
end
|
566
566
|
end
|
567
|
-
|
567
|
+
|
568
568
|
module DatasetMethods
|
569
569
|
include Dataset::Replace
|
570
570
|
include UnmodifiedIdentifiers::DatasetMethods
|
571
571
|
|
572
572
|
# The allowed values for insert_conflict
|
573
|
-
INSERT_CONFLICT_RESOLUTIONS = %w
|
573
|
+
INSERT_CONFLICT_RESOLUTIONS = %w[ROLLBACK ABORT FAIL IGNORE REPLACE].each(&:freeze).freeze
|
574
574
|
|
575
|
-
CONSTANT_MAP = {:
|
576
|
-
EXTRACT_MAP = {:
|
575
|
+
CONSTANT_MAP = {CURRENT_DATE: "date(CURRENT_TIMESTAMP, 'localtime')", CURRENT_TIMESTAMP: "datetime(CURRENT_TIMESTAMP, 'localtime')", CURRENT_TIME: "time(CURRENT_TIMESTAMP, 'localtime')"}.freeze
|
576
|
+
EXTRACT_MAP = {year: "'%Y'", month: "'%m'", day: "'%d'", hour: "'%H'", minute: "'%M'", second: "'%f'"}.freeze
|
577
577
|
EXTRACT_MAP.each_value(&:freeze)
|
578
578
|
|
579
|
-
Dataset.def_sql_method(self, :delete, [[
|
580
|
-
Dataset.def_sql_method(self, :insert, [[
|
581
|
-
Dataset.def_sql_method(self, :select, [[
|
582
|
-
Dataset.def_sql_method(self, :update, [[
|
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
583
|
|
584
584
|
def cast_sql_append(sql, expr, type)
|
585
|
-
if type == Time
|
585
|
+
if type == Time || type == DateTime
|
586
586
|
sql << "datetime("
|
587
587
|
literal_append(sql, expr)
|
588
|
-
sql <<
|
588
|
+
sql << ")"
|
589
589
|
elsif type == Date
|
590
590
|
sql << "date("
|
591
591
|
literal_append(sql, expr)
|
592
|
-
sql <<
|
592
|
+
sql << ")"
|
593
593
|
else
|
594
594
|
super
|
595
595
|
end
|
@@ -600,46 +600,46 @@ module Sequel
|
|
600
600
|
def complex_expression_sql_append(sql, op, args)
|
601
601
|
case op
|
602
602
|
when :"NOT LIKE", :"NOT ILIKE"
|
603
|
-
sql <<
|
604
|
-
complex_expression_sql_append(sql, (op == :"NOT ILIKE" ? :ILIKE : :LIKE), args)
|
603
|
+
sql << "NOT "
|
604
|
+
complex_expression_sql_append(sql, ((op == :"NOT ILIKE") ? :ILIKE : :LIKE), args)
|
605
605
|
when :^
|
606
|
-
complex_expression_arg_pairs_append(sql, args){|a, b| Sequel.lit(["((~(", " & ", ")) & (", " | ", "))"], a, b, a, b)}
|
606
|
+
complex_expression_arg_pairs_append(sql, args) { |a, b| Sequel.lit(["((~(", " & ", ")) & (", " | ", "))"], a, b, a, b) }
|
607
607
|
when :**
|
608
608
|
unless (exp = args[1]).is_a?(Integer)
|
609
609
|
raise(Sequel::Error, "can only emulate exponentiation on SQLite if exponent is an integer, given #{exp.inspect}")
|
610
610
|
end
|
611
611
|
case exp
|
612
612
|
when 0
|
613
|
-
sql <<
|
613
|
+
sql << "1"
|
614
614
|
else
|
615
|
-
sql <<
|
615
|
+
sql << "("
|
616
616
|
arg = args[0]
|
617
617
|
if exp < 0
|
618
618
|
invert = true
|
619
619
|
exp = exp.abs
|
620
|
-
sql <<
|
620
|
+
sql << "(1.0 / ("
|
621
621
|
end
|
622
|
-
(exp - 1).times do
|
622
|
+
(exp - 1).times do
|
623
623
|
literal_append(sql, arg)
|
624
624
|
sql << " * "
|
625
625
|
end
|
626
626
|
literal_append(sql, arg)
|
627
|
-
sql <<
|
627
|
+
sql << ")"
|
628
628
|
if invert
|
629
629
|
sql << "))"
|
630
630
|
end
|
631
631
|
end
|
632
632
|
when :extract
|
633
633
|
part = args[0]
|
634
|
-
raise(Sequel::Error, "unsupported extract argument: #{part.inspect}") unless format = EXTRACT_MAP[part]
|
635
|
-
sql << "CAST(strftime(" << format <<
|
634
|
+
raise(Sequel::Error, "unsupported extract argument: #{part.inspect}") unless (format = EXTRACT_MAP[part])
|
635
|
+
sql << "CAST(strftime(" << format << ", "
|
636
636
|
literal_append(sql, args[1])
|
637
|
-
sql <<
|
637
|
+
sql << ") AS " << ((part == :second) ? "NUMERIC" : "INTEGER") << ")"
|
638
638
|
else
|
639
639
|
super
|
640
640
|
end
|
641
641
|
end
|
642
|
-
|
642
|
+
|
643
643
|
# SQLite has CURRENT_TIMESTAMP and related constants in UTC instead
|
644
644
|
# of in localtime, so convert those constants to local time.
|
645
645
|
def constant_sql_append(sql, constant)
|
@@ -649,14 +649,14 @@ module Sequel
|
|
649
649
|
super
|
650
650
|
end
|
651
651
|
end
|
652
|
-
|
652
|
+
|
653
653
|
# SQLite performs a TRUNCATE style DELETE if no filter is specified.
|
654
654
|
# Since we want to always return the count of records, add a condition
|
655
655
|
# that is always true and then delete.
|
656
656
|
def delete(&block)
|
657
|
-
@opts[:where] ? super : where(1=>1).delete(&block)
|
657
|
+
@opts[:where] ? super : where(1 => 1).delete(&block)
|
658
658
|
end
|
659
|
-
|
659
|
+
|
660
660
|
# Always return false when using VALUES
|
661
661
|
def empty?
|
662
662
|
return false if @opts[:values]
|
@@ -666,21 +666,21 @@ module Sequel
|
|
666
666
|
# Return an array of strings specifying a query explanation for a SELECT of the
|
667
667
|
# current dataset. Currently, the options are ignored, but it accepts options
|
668
668
|
# to be compatible with other adapters.
|
669
|
-
def explain(opts=nil)
|
669
|
+
def explain(opts = nil)
|
670
670
|
# Load the PrettyTable class, needed for explain output
|
671
671
|
Sequel.extension(:_pretty_table) unless defined?(Sequel::PrettyTable)
|
672
672
|
|
673
|
-
ds = db.send(:metadata_dataset).clone(:
|
673
|
+
ds = db.send(:metadata_dataset).clone(sql: "EXPLAIN #{select_sql}")
|
674
674
|
rows = ds.all
|
675
675
|
Sequel::PrettyTable.string(rows, ds.columns)
|
676
676
|
end
|
677
|
-
|
677
|
+
|
678
678
|
# HAVING requires GROUP BY on SQLite
|
679
679
|
def having(*cond)
|
680
680
|
raise(InvalidOperation, "Can only specify a HAVING clause on a grouped dataset") if !@opts[:group] && db.sqlite_version < 33900
|
681
681
|
super
|
682
682
|
end
|
683
|
-
|
683
|
+
|
684
684
|
# Support insert select for associations, so that the model code can use
|
685
685
|
# returning instead of a separate query.
|
686
686
|
def insert_select(*values)
|
@@ -698,16 +698,16 @@ module Sequel
|
|
698
698
|
|
699
699
|
# SQLite uses the nonstandard ` (backtick) for quoting identifiers.
|
700
700
|
def quoted_identifier_append(sql, c)
|
701
|
-
sql <<
|
701
|
+
sql << "`" << c.to_s.gsub("`", "``") << "`"
|
702
702
|
end
|
703
|
-
|
703
|
+
|
704
704
|
# When a qualified column is selected on SQLite and the qualifier
|
705
705
|
# is a subselect, the column name used is the full qualified name
|
706
706
|
# (including the qualifier) instead of just the column name. To
|
707
707
|
# get correct column names, you must use an alias.
|
708
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)})
|
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
711
|
else
|
712
712
|
super
|
713
713
|
end
|
@@ -737,7 +737,7 @@ module Sequel
|
|
737
737
|
# DB[:table].insert_conflict({}).insert(a: 1, b: 2)
|
738
738
|
# # INSERT INTO TABLE (a, b) VALUES (1, 2)
|
739
739
|
# # ON CONFLICT DO NOTHING
|
740
|
-
#
|
740
|
+
#
|
741
741
|
# DB[:table].insert_conflict(target: :a).insert(a: 1, b: 2)
|
742
742
|
# # INSERT INTO TABLE (a, b) VALUES (1, 2)
|
743
743
|
# # ON CONFLICT (a) DO NOTHING
|
@@ -745,11 +745,11 @@ module Sequel
|
|
745
745
|
# DB[:table].insert_conflict(target: :a, conflict_where: {c: true}).insert(a: 1, b: 2)
|
746
746
|
# # INSERT INTO TABLE (a, b) VALUES (1, 2)
|
747
747
|
# # ON CONFLICT (a) WHERE (c IS TRUE) DO NOTHING
|
748
|
-
#
|
748
|
+
#
|
749
749
|
# DB[:table].insert_conflict(target: :a, update: {b: Sequel[:excluded][:b]}).insert(a: 1, b: 2)
|
750
750
|
# # INSERT INTO TABLE (a, b) VALUES (1, 2)
|
751
751
|
# # ON CONFLICT (a) DO UPDATE SET b = excluded.b
|
752
|
-
#
|
752
|
+
#
|
753
753
|
# DB[:table].insert_conflict(target: :a,
|
754
754
|
# update: {b: Sequel[:excluded][:b]}, update_where: {Sequel[:table][:status_id] => 1}).insert(a: 1, b: 2)
|
755
755
|
# # INSERT INTO TABLE (a, b) VALUES (1, 2)
|
@@ -760,9 +760,9 @@ module Sequel
|
|
760
760
|
unless INSERT_CONFLICT_RESOLUTIONS.include?(opts.to_s.upcase)
|
761
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
762
|
end
|
763
|
-
clone(:
|
763
|
+
clone(insert_conflict: opts)
|
764
764
|
when Hash
|
765
|
-
clone(:
|
765
|
+
clone(insert_on_conflict: opts)
|
766
766
|
else
|
767
767
|
raise Error, "Invalid value passed to Dataset#insert_conflict: #{opts.inspect}, should use a symbol or a hash"
|
768
768
|
end
|
@@ -781,11 +781,11 @@ module Sequel
|
|
781
781
|
def returning(*values)
|
782
782
|
return super if values.empty?
|
783
783
|
raise Error, "RETURNING is not supported on #{db.database_type}" unless supports_returning?(:insert)
|
784
|
-
clone(:
|
784
|
+
clone(returning: _returning_values(values).freeze)
|
785
785
|
end
|
786
786
|
|
787
787
|
# SQLite 3.8.3+ supports common table expressions.
|
788
|
-
def supports_cte?(type
|
788
|
+
def supports_cte?(type = :select)
|
789
789
|
db.sqlite_version >= 30803
|
790
790
|
end
|
791
791
|
|
@@ -813,7 +813,7 @@ module Sequel
|
|
813
813
|
def supports_is_true?
|
814
814
|
false
|
815
815
|
end
|
816
|
-
|
816
|
+
|
817
817
|
# SQLite 3.33.0 supports modifying joined datasets
|
818
818
|
def supports_modifying_joins?
|
819
819
|
db.sqlite_version >= 33300
|
@@ -823,7 +823,7 @@ module Sequel
|
|
823
823
|
def supports_multiple_column_in?
|
824
824
|
false
|
825
825
|
end
|
826
|
-
|
826
|
+
|
827
827
|
# SQLite 3.35.0 supports RETURNING on INSERT/UPDATE/DELETE.
|
828
828
|
def supports_returning?(_)
|
829
829
|
db.sqlite_version >= 33500
|
@@ -845,7 +845,7 @@ module Sequel
|
|
845
845
|
def supports_window_clause?
|
846
846
|
db.sqlite_version >= 32800
|
847
847
|
end
|
848
|
-
|
848
|
+
|
849
849
|
# SQLite 3.25+ supports window functions. However, support is only enabled
|
850
850
|
# on SQLite 3.26.0+ because internal Sequel usage of window functions
|
851
851
|
# to implement eager loading of limited associations triggers
|
@@ -853,14 +853,14 @@ module Sequel
|
|
853
853
|
def supports_window_functions?
|
854
854
|
db.sqlite_version >= 32600
|
855
855
|
end
|
856
|
-
|
856
|
+
|
857
857
|
# SQLite 3.28.0+ supports all window frame options that Sequel supports
|
858
858
|
def supports_window_function_frame_option?(option)
|
859
|
-
db.sqlite_version >= 32800 ? true : super
|
859
|
+
(db.sqlite_version >= 32800) ? true : super
|
860
860
|
end
|
861
861
|
|
862
862
|
private
|
863
|
-
|
863
|
+
|
864
864
|
# Add aliases to symbols and identifiers to work around SQLite bug.
|
865
865
|
def _returning_values(values)
|
866
866
|
values.map do |v|
|
@@ -880,12 +880,12 @@ module Sequel
|
|
880
880
|
def aggreate_dataset_use_from_self?
|
881
881
|
super || @opts[:values]
|
882
882
|
end
|
883
|
-
|
883
|
+
|
884
884
|
# SQLite uses string literals instead of identifiers in AS clauses.
|
885
|
-
def as_sql_append(sql, aliaz, column_aliases=nil)
|
885
|
+
def as_sql_append(sql, aliaz, column_aliases = nil)
|
886
886
|
raise Error, "sqlite does not support derived column lists" if column_aliases
|
887
887
|
aliaz = aliaz.value if aliaz.is_a?(SQL::Identifier)
|
888
|
-
sql <<
|
888
|
+
sql << " AS "
|
889
889
|
literal_append(sql, aliaz.to_s)
|
890
890
|
end
|
891
891
|
|
@@ -911,7 +911,7 @@ module Sequel
|
|
911
911
|
raise(InvalidOperation, "Grouped datasets cannot be modified") if opts[:group]
|
912
912
|
raise(InvalidOperation, "Joined datasets cannot be modified") if joined_dataset?
|
913
913
|
end
|
914
|
-
|
914
|
+
alias_method :check_delete_allowed!, :check_insert_allowed!
|
915
915
|
|
916
916
|
# SQLite supports a maximum of 500 rows in a VALUES clause.
|
917
917
|
def default_import_slice
|
@@ -920,37 +920,37 @@ module Sequel
|
|
920
920
|
|
921
921
|
# SQL fragment specifying a list of identifiers
|
922
922
|
def identifier_list(columns)
|
923
|
-
columns.map{|i| quote_identifier(i)}.join(
|
923
|
+
columns.map { |i| quote_identifier(i) }.join(", ")
|
924
924
|
end
|
925
|
-
|
925
|
+
|
926
926
|
# Add OR clauses to SQLite INSERT statements
|
927
927
|
def insert_conflict_sql(sql)
|
928
|
-
if resolution = @opts[:insert_conflict]
|
928
|
+
if (resolution = @opts[:insert_conflict])
|
929
929
|
sql << " OR " << resolution.to_s.upcase
|
930
930
|
end
|
931
931
|
end
|
932
932
|
|
933
933
|
# Add ON CONFLICT clause if it should be used
|
934
934
|
def insert_on_conflict_sql(sql)
|
935
|
-
if opts = @opts[:insert_on_conflict]
|
935
|
+
if (opts = @opts[:insert_on_conflict])
|
936
936
|
sql << " ON CONFLICT"
|
937
937
|
|
938
|
-
if target = opts[:constraint]
|
938
|
+
if (target = opts[:constraint])
|
939
939
|
sql << " ON CONSTRAINT "
|
940
940
|
identifier_append(sql, target)
|
941
|
-
elsif target = opts[:target]
|
942
|
-
sql <<
|
941
|
+
elsif (target = opts[:target])
|
942
|
+
sql << " "
|
943
943
|
identifier_append(sql, Array(target))
|
944
|
-
if conflict_where = opts[:conflict_where]
|
944
|
+
if (conflict_where = opts[:conflict_where])
|
945
945
|
sql << " WHERE "
|
946
946
|
literal_append(sql, conflict_where)
|
947
947
|
end
|
948
948
|
end
|
949
949
|
|
950
|
-
if values = opts[:update]
|
950
|
+
if (values = opts[:update])
|
951
951
|
sql << " DO UPDATE SET "
|
952
952
|
update_sql_values_hash(sql, values)
|
953
|
-
if update_where = opts[:update_where]
|
953
|
+
if (update_where = opts[:update_where])
|
954
954
|
sql << " WHERE "
|
955
955
|
literal_append(sql, update_where)
|
956
956
|
end
|
@@ -962,29 +962,29 @@ module Sequel
|
|
962
962
|
|
963
963
|
# SQLite uses a preceding X for hex escaping strings
|
964
964
|
def literal_blob_append(sql, v)
|
965
|
-
sql <<
|
965
|
+
sql << "X'" << v.unpack1("H*") << "'"
|
966
966
|
end
|
967
967
|
|
968
968
|
# Respect the database integer_booleans setting, using 0 or 'f'.
|
969
969
|
def literal_false
|
970
|
-
@db.integer_booleans ?
|
970
|
+
@db.integer_booleans ? "0" : "'f'"
|
971
971
|
end
|
972
972
|
|
973
973
|
# Respect the database integer_booleans setting, using 1 or 't'.
|
974
974
|
def literal_true
|
975
|
-
@db.integer_booleans ?
|
975
|
+
@db.integer_booleans ? "1" : "'t'"
|
976
976
|
end
|
977
977
|
|
978
978
|
# SQLite only supporting multiple rows in the VALUES clause
|
979
979
|
# starting in 3.7.11. On older versions, fallback to using a UNION.
|
980
980
|
def multi_insert_sql_strategy
|
981
|
-
db.sqlite_version >= 30711 ? :values : :union
|
981
|
+
(db.sqlite_version >= 30711) ? :values : :union
|
982
982
|
end
|
983
983
|
|
984
984
|
# Emulate the char_length function with length
|
985
985
|
def native_function_name(emulated_function)
|
986
986
|
if emulated_function == :char_length
|
987
|
-
|
987
|
+
"length"
|
988
988
|
else
|
989
989
|
super
|
990
990
|
end
|
@@ -1006,7 +1006,7 @@ module Sequel
|
|
1006
1006
|
sql << " LIMIT -1 OFFSET "
|
1007
1007
|
literal_append(sql, @opts[:offset])
|
1008
1008
|
end
|
1009
|
-
|
1009
|
+
|
1010
1010
|
# Support VALUES clause instead of the SELECT clause to return rows.
|
1011
1011
|
def select_values_sql(sql)
|
1012
1012
|
sql << "VALUES "
|
@@ -1035,10 +1035,10 @@ module Sequel
|
|
1035
1035
|
|
1036
1036
|
# Use FROM to specify additional tables in an update query
|
1037
1037
|
def update_from_sql(sql)
|
1038
|
-
if(from = @opts[:from][1
|
1039
|
-
raise(Error,
|
1038
|
+
if (from = @opts[:from][1..]).empty?
|
1039
|
+
raise(Error, "Need multiple FROM tables if updating/deleting a dataset with JOINs") if @opts[:join]
|
1040
1040
|
else
|
1041
|
-
sql <<
|
1041
|
+
sql << " FROM "
|
1042
1042
|
source_list_append(sql, from)
|
1043
1043
|
select_join_sql(sql)
|
1044
1044
|
end
|
@@ -1046,7 +1046,7 @@ module Sequel
|
|
1046
1046
|
|
1047
1047
|
# Only include the primary table in the main update clause
|
1048
1048
|
def update_table_sql(sql)
|
1049
|
-
sql <<
|
1049
|
+
sql << " "
|
1050
1050
|
source_list_append(sql, @opts[:from][0..0])
|
1051
1051
|
end
|
1052
1052
|
end
|