activerecord-enhancedsqlite3-adapter 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f138b7e075e546691856c8e45f22413a1e381df3024c6ef30e467dbde830c896
|
4
|
+
data.tar.gz: 0e6bde9f85883cacc12893f8ab2b1ca5820deea6d6a1357a262fb24401ed4980
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dafd2a35c78565843b9aa384fb5988818c48bdc6852da55363c7e18e447a752b8194ef08ea4a40c9b482f30317cd7afc8e9501e22cb38abbab51dd16f3d6c7a7
|
7
|
+
data.tar.gz: 5c0d6b07eb0b3e90c4fd6f8859f90d944e33d90e100ee96981f24b4d1cb1212266045f81abe935b4ad1913caf22d43cb3386a73bdb0670b15f8686e680619a77
|
@@ -6,6 +6,7 @@
|
|
6
6
|
|
7
7
|
require "active_record/connection_adapters/sqlite3_adapter"
|
8
8
|
require "enhanced_sqlite3/supports_virtual_columns"
|
9
|
+
require "enhanced_sqlite3/supports_deferrable_constraints"
|
9
10
|
|
10
11
|
module EnhancedSQLite3
|
11
12
|
module Adapter
|
@@ -19,29 +20,17 @@ module EnhancedSQLite3
|
|
19
20
|
#
|
20
21
|
# extends https://github.com/rails/rails/blob/main/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb#L691
|
21
22
|
def configure_connection
|
22
|
-
configure_busy_handler
|
23
|
-
|
24
23
|
super
|
25
24
|
|
26
25
|
configure_pragmas
|
27
26
|
configure_extensions
|
28
27
|
|
29
28
|
EnhancedSQLite3::SupportsVirtualColumns.apply!
|
29
|
+
EnhancedSQLite3::SupportsDeferrableConstraints.apply!
|
30
30
|
end
|
31
31
|
|
32
32
|
private
|
33
33
|
|
34
|
-
def configure_busy_handler
|
35
|
-
if @config[:timeout] && @config[:retries]
|
36
|
-
raise ArgumentError, "Cannot specify both timeout and retries arguments"
|
37
|
-
elsif @config[:retries]
|
38
|
-
# see: https://www.sqlite.org/c3ref/busy_handler.html
|
39
|
-
@raw_connection.busy_handler do |count|
|
40
|
-
count <= @config[:retries]
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
34
|
def configure_pragmas
|
46
35
|
@config.fetch(:pragmas, []).each do |key, value|
|
47
36
|
execute("PRAGMA #{key} = #{value}", "SCHEMA")
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# see: https://github.com/rails/rails/pull/49376
|
4
|
+
module EnhancedSQLite3
|
5
|
+
module SupportsDeferrableConstraints
|
6
|
+
def self.apply!
|
7
|
+
EnhancedSQLite3::Adapter.include(Adapter)
|
8
|
+
ActiveRecord::ConnectionAdapters::SQLite3::SchemaCreation.include(SchemaCreation)
|
9
|
+
end
|
10
|
+
|
11
|
+
module Adapter
|
12
|
+
def supports_deferrable_constraints?
|
13
|
+
true
|
14
|
+
end
|
15
|
+
|
16
|
+
FK_REGEX = /.*FOREIGN KEY\s+\("(\w+)"\)\s+REFERENCES\s+"(\w+)"\s+\("(\w+)"\)/
|
17
|
+
DEFERRABLE_REGEX = /DEFERRABLE INITIALLY (\w+)/
|
18
|
+
def foreign_keys(table_name)
|
19
|
+
# SQLite returns 1 row for each column of composite foreign keys.
|
20
|
+
fk_info = internal_exec_query("PRAGMA foreign_key_list(#{quote(table_name)})", "SCHEMA")
|
21
|
+
# Deferred or immediate foreign keys can only be seen in the CREATE TABLE sql
|
22
|
+
fk_defs = table_structure_sql(table_name)
|
23
|
+
.select do |column_string|
|
24
|
+
column_string.start_with?("CONSTRAINT") &&
|
25
|
+
column_string.include?("FOREIGN KEY")
|
26
|
+
end
|
27
|
+
.to_h do |fk_string|
|
28
|
+
_, from, table, to = fk_string.match(FK_REGEX).to_a
|
29
|
+
_, mode = fk_string.match(DEFERRABLE_REGEX).to_a
|
30
|
+
deferred = mode&.downcase&.to_sym || false
|
31
|
+
[[table, from, to], deferred]
|
32
|
+
end
|
33
|
+
|
34
|
+
grouped_fk = fk_info.group_by { |row| row["id"] }.values.each { |group| group.sort_by! { |row| row["seq"] } }
|
35
|
+
grouped_fk.map do |group|
|
36
|
+
row = group.first
|
37
|
+
options = {
|
38
|
+
on_delete: extract_foreign_key_action(row["on_delete"]),
|
39
|
+
on_update: extract_foreign_key_action(row["on_update"]),
|
40
|
+
deferrable: fk_defs[[row["table"], row["from"], row["to"]]]
|
41
|
+
}
|
42
|
+
|
43
|
+
if group.one?
|
44
|
+
options[:column] = row["from"]
|
45
|
+
options[:primary_key] = row["to"]
|
46
|
+
else
|
47
|
+
options[:column] = group.map { |row| row["from"] }
|
48
|
+
options[:primary_key] = group.map { |row| row["to"] }
|
49
|
+
end
|
50
|
+
ActiveRecord::ConnectionAdapters::ForeignKeyDefinition.new(table_name, row["table"], options)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def add_foreign_key(from_table, to_table, **options)
|
55
|
+
if options[:deferrable] == true
|
56
|
+
ActiveRecord.deprecator.warn(<<~MSG)
|
57
|
+
`deferrable: true` is deprecated in favor of `deferrable: :immediate`, and will be removed in Rails 7.2.
|
58
|
+
MSG
|
59
|
+
|
60
|
+
options[:deferrable] = :immediate
|
61
|
+
end
|
62
|
+
|
63
|
+
assert_valid_deferrable(options[:deferrable])
|
64
|
+
|
65
|
+
super
|
66
|
+
end
|
67
|
+
|
68
|
+
def table_structure_sql(table_name)
|
69
|
+
sql = <<~SQL
|
70
|
+
SELECT sql FROM
|
71
|
+
(SELECT * FROM sqlite_master UNION ALL
|
72
|
+
SELECT * FROM sqlite_temp_master)
|
73
|
+
WHERE type = 'table' AND name = #{quote(table_name)}
|
74
|
+
SQL
|
75
|
+
|
76
|
+
# Result will have following sample string
|
77
|
+
# CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
78
|
+
# "password_digest" varchar COLLATE "NOCASE");
|
79
|
+
result = query_value(sql, "SCHEMA")
|
80
|
+
|
81
|
+
return [] unless result
|
82
|
+
|
83
|
+
# Splitting with left parentheses and discarding the first part will return all
|
84
|
+
# columns separated with comma(,).
|
85
|
+
columns_string = result.split("(", 2).last
|
86
|
+
|
87
|
+
columns_string.split(",").map(&:strip)
|
88
|
+
end
|
89
|
+
|
90
|
+
def assert_valid_deferrable(deferrable)
|
91
|
+
return if !deferrable || %i[immediate deferred].include?(deferrable)
|
92
|
+
|
93
|
+
raise ArgumentError, "deferrable must be `:immediate` or `:deferred`, got: `#{deferrable.inspect}`"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
module SchemaCreation
|
98
|
+
def visit_AddForeignKey(o)
|
99
|
+
super.dup.tap do |sql|
|
100
|
+
sql << " DEFERRABLE INITIALLY #{o.options[:deferrable].to_s.upcase}" if o.deferrable
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def visit_ForeignKeyDefinition(o)
|
105
|
+
super.dup.tap do |sql|
|
106
|
+
sql << " DEFERRABLE INITIALLY #{o.deferrable.to_s.upcase}" if o.deferrable
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord-enhancedsqlite3-adapter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stephen Margheim
|
@@ -111,6 +111,7 @@ files:
|
|
111
111
|
- lib/activerecord-enhancedsqlite3-adapter.rb
|
112
112
|
- lib/enhanced_sqlite3/adapter.rb
|
113
113
|
- lib/enhanced_sqlite3/railtie.rb
|
114
|
+
- lib/enhanced_sqlite3/supports_deferrable_constraints.rb
|
114
115
|
- lib/enhanced_sqlite3/supports_virtual_columns.rb
|
115
116
|
- lib/enhanced_sqlite3/version.rb
|
116
117
|
homepage: https://github.com/fractaledmind/activerecord-enhancedsqlite3-adapter
|