activerecord-enhancedsqlite3-adapter 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml
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
|