The test schema had drifted from the real /etc/pihole/gravity.db:
- domainlist/adlist used single-column UNIQUE instead of composite
UNIQUE(domain,type) / UNIQUE(address,type), causing silent SQL
failures on INSERT ... ON CONFLICT
- adlist column order had `type` in wrong position
- client table had `NOL NULL` typo (should be `NOT NULL`)
- *_by_group tables were missing ON DELETE CASCADE and WITHOUT ROWID
- info table was missing WITHOUT ROWID
- vw_gravity had extra `AND adlist.type = 0` filter not in production
- vw_adlist was missing `type` column in SELECT
- domainlist views had ORDER BY clauses not present in production
- Three indexes were missing (idx_adlist_by_group_gid,
idx_domainlist_by_group_gid, idx_gravity)
Signed-off-by: Dominik <dl6er@dl6er.de>
The tr_adlist_update, tr_client_update, and tr_domainlist_update
triggers used WHERE address/ip/domain = NEW.* to locate the row being
updated. This requires a B-tree traversal of the UNIQUE text index on
every UPDATE. Replace with WHERE id = NEW.id which uses the INTEGER
PRIMARY KEY (rowid alias) for O(1) direct lookup.
The tr_group_update trigger already used WHERE id = NEW.id — this
makes all four update triggers consistent.
Signed-off-by: Dominik <dl6er@dl6er.de>
The client table's ip column was declared as "TEXT NOL NULL UNIQUE".
SQLite silently ignores the misspelled constraint, allowing NULL values
to be inserted despite the apparent intent. The UNIQUE constraint still
prevented duplicate NULLs (SQLite allows at most one NULL in a UNIQUE
column), but a single NULL IP row could cause client lookup failures.
Signed-off-by: Dominik <dl6er@dl6er.de>
Add bind_client_groups() helper that tracks the last-bound SHM groupspos
per statement and skips sqlite3_carray_bind() when the array is already
bound. Since addintarray() deduplicates, clients sharing the same group
set (the common case — most installations use only the default group)
share the same groupspos, so after the first DNS query all subsequent
queries skip the rebind on all four hot-path statements. Each skipped
rebind avoids an internal carray_bind struct malloc/free cycle.
Add WITHOUT ROWID to adlist_by_group, domainlist_by_group,
client_by_group, and info tables. These have composite or TEXT primary
keys and no extra data columns — ideal candidates. WITHOUT ROWID
eliminates the hidden rowid B-tree so the PK is the storage key
directly, reducing one level of indirection per JOIN through the
gravity views.
Signed-off-by: Dominik <dl6er@dl6er.de>
Replace pre-computed per-client adlist ID arrays with live view queries
for gravity and antigravity lookups. The cached approach bypassed view
JOINs for ~4x faster lookups, but produced stale results when users
modified groups, adlists, or their assignments at runtime without
triggering a full gravity reload — the info.updated timestamp only
changes on "pihole -g", not on individual table edits.
All four hot-path query types (gravity, antigravity, allowlist, denylist)
now uniformly query through their respective views with
group_id IN carray(), binding the client's group IDs from SHM. The
views' LEFT JOINs live-check adlist.enabled, group.enabled, and group
assignments on every query, ensuring changes take effect immediately.
Removed:
- adlist_ids_stmt shared prepared statement
- get_client_adlist_ids() pre-computation function
- gravity_adlistpos / antigravity_adlistpos fields from clientsData
- Associated fork handling, init, reset, and finalization code
Kept from prior optimization work:
- Shared prepared statements with carray() binding
- SHM intarray storage for client group IDs
- Junction table (group_id, ...) indexes for carray filter performance
Signed-off-by: Dominik <dl6er@dl6er.de>
- Add ANTIGRAVITY_TABLE enum, count query, getTable case, and SHM/API counter
- Add SELECT DISTINCT to regex group queries to eliminate duplicates when a
client belongs to multiple groups containing the same regex
- Add (group_id, ...) indexes on adlist_by_group and domainlist_by_group to
enable index seeks for carray()-filtered queries instead of full scans
- Remove unnecessary ORDER BY from domainlist views
- Restore LEFT JOINs in domainlist views (groups can be removed at runtime)
Signed-off-by: Dominik <dl6er@dl6er.de>
The real gravity.db created by pihole -g has idx_gravity ON
gravity(domain, adlist_id) — a covering index that enables O(log n)
domain lookups instead of full table scans. The test schema was missing
this index, making it out of sync with actual deployments. Also adds the
equivalent index for the antigravity table.
Signed-off-by: Dominik <dl6er@dl6er.de>