Skip to content

Hide pgsqlite internal objects from client sqlite_master queries (result filtering)#83

Open
robertjbass wants to merge 1 commit into
erans:mainfrom
Layerbase-LLC:feat/result-filter
Open

Hide pgsqlite internal objects from client sqlite_master queries (result filtering)#83
robertjbass wants to merge 1 commit into
erans:mainfrom
Layerbase-LLC:feat/result-filter

Conversation

@robertjbass

Copy link
Copy Markdown

Problem

Client sqlite_master / sqlite_schema queries return pgsqlite's own internal objects alongside the user's tables: the __pgsqlite_* bookkeeping tables, the materialized pg_catalog tables (pg_attrdef, pg_constraint, pg_depend, pg_index), the materialized pg_catalog / information_schema views, and internal indexes on them. These leak into schema browsers, ORMs, and \dt-style tooling connected over the wire.

Approach: filter results, do not rewrite the query

This hides those internal objects by filtering the result rows, never by modifying the client's SQL. When intercept_query sees a single plain SELECT whose FROM references sqlite_master/sqlite_schema, it:

  1. executes the client's query byte-for-byte unchanged,
  2. builds the authoritative set of internal object names from a fixed SELECT name, tbl_name FROM sqlite_master probe (so internal-owned indexes/triggers are caught by tbl_name even when the client only projects name),
  3. drops rows whose name (or projected tbl_name) is an internal object: __pgsqlite_% prefix, the reserved pg_catalog table/view names (exact-name set, never a pg_% wildcard, so a user's own pg_myreport survives), or in the probe-derived denylist.

Because the query text is never altered, this cannot corrupt a query (an earlier query-rewriting attempt broke complex predicates - large NOT IN lists, ESCAPE clauses). Fail-open: any unrecognized shape (subquery-wrapped FROM, set ops, no name/tbl_name projection such as SELECT sql or SELECT count(*), or a parse failure) returns the original result unchanged.

Boundary

A query whose text itself trips an earlier catalog-interceptor branch (e.g. one embedding many pg_* names) is handled by that branch before this one and is out of scope here. Simple SELECT * FROM sqlite_master from external tools is handled cleanly.

Tests

Wire-level integration tests plus unit tests: internal tables/views/indexes hidden from SELECT *, type='table', type='index', type='view' scans; user tables/views/indexes returned; a user pg_myreport returned; a large non-pg NOT IN + ESCAPE returns correct rows (no corruption); SELECT sql FROM sqlite_master returned unfiltered. Verified against real psql.

…ltering

A client-issued sqlite_master / sqlite_schema SELECT should not expose pgsqlite's internal objects: the __pgsqlite_* bookkeeping tables, the pg_catalog tables/views pgsqlite materializes as real SQLite objects, or the indexes/triggers owned by them.

Rather than rewriting the query text (which can corrupt complex predicates such as a large NOT IN list or an ESCAPE clause), the catalog interceptor now runs the client's ORIGINAL, UNMODIFIED query and filters pgsqlite-internal rows out of the result. Because the query text is never changed, no client query can be corrupted by this path.

A small probe of sqlite_master builds the authoritative set of internal object names (including internal-owned indexes/triggers whose own names are not reserved) so they are hidden even when the client projects only the name column. The reserved pg_catalog sets are exact-name (never a pg_% wildcard) so a user-owned object like pg_indexes or pg_my_report is preserved. A projection with no name/tbl_name column (e.g. SELECT sql FROM sqlite_master) is returned unfiltered, and any unrecognized query shape fails open and runs unmodified.

Covered by unit tests for detection/filtering and wire-level integration tests, including a large non-matching NOT IN list that proves the client's query is not corrupted.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant