Follow-up from #80 / #85.
--hide-internal-tables rewrites sqlite_master references only in read contexts — Statement::Query and the source of Statement::Insert (src/translator/sqlite_master_filter.rs). CREATE VIEW ... AS SELECT and CREATE TABLE ... AS SELECT are not rewritten.
So with the flag on:
CREATE VIEW v AS SELECT name FROM sqlite_master;
SELECT * FROM v; -- returns the unfiltered catalog, including __pgsqlite_* rows
The second query never mentions sqlite_master, so nothing filters it — the view is materialized against the real catalog once and reads through it thereafter.
Why it is scoped this way
The restriction to read contexts was added deliberately in #85. Before it, DELETE FROM sqlite_master WHERE name='zzz' was rewritten into syntactically invalid SQL that pasted the internal prefix into the client-facing error:
SQLite error: near "(": syntax error in DELETE FROM (SELECT * FROM sqlite_master WHERE SUBSTR(name, 1, 11) <> '__pgsqlite_' ...
Narrowing to read contexts fixed that but dropped CTAS/CREATE VIEW along with the write statements.
Severity
Not a security hole. The design (docs/superpowers/specs/2026-08-05-hide-internal-tables-design.md) states hiding is listing-only and SELECT * FROM __pgsqlite_schema keeps working by name, so nothing is exposed that was otherwise unreachable. But the flag exists to stop end users stumbling onto internal tables and concluding their data is corrupt, and a schema browser that creates views would defeat that.
Possible fix
Extend the rewrite to the query of Statement::CreateView and Statement::CreateTable's AS SELECT source, keeping Update/Delete excluded. EXPLAIN and DECLARE ... CURSOR are also currently excluded and do not matter — pgsqlite has no handler for either.
Follow-up from #80 / #85.
--hide-internal-tablesrewritessqlite_masterreferences only in read contexts —Statement::Queryand thesourceofStatement::Insert(src/translator/sqlite_master_filter.rs).CREATE VIEW ... AS SELECTandCREATE TABLE ... AS SELECTare not rewritten.So with the flag on:
The second query never mentions
sqlite_master, so nothing filters it — the view is materialized against the real catalog once and reads through it thereafter.Why it is scoped this way
The restriction to read contexts was added deliberately in #85. Before it,
DELETE FROM sqlite_master WHERE name='zzz'was rewritten into syntactically invalid SQL that pasted the internal prefix into the client-facing error:Narrowing to read contexts fixed that but dropped CTAS/CREATE VIEW along with the write statements.
Severity
Not a security hole. The design (
docs/superpowers/specs/2026-08-05-hide-internal-tables-design.md) states hiding is listing-only andSELECT * FROM __pgsqlite_schemakeeps working by name, so nothing is exposed that was otherwise unreachable. But the flag exists to stop end users stumbling onto internal tables and concluding their data is corrupt, and a schema browser that creates views would defeat that.Possible fix
Extend the rewrite to the
queryofStatement::CreateViewandStatement::CreateTable'sAS SELECTsource, keepingUpdate/Deleteexcluded.EXPLAINandDECLARE ... CURSORare also currently excluded and do not matter — pgsqlite has no handler for either.