-
Notifications
You must be signed in to change notification settings - Fork 0
Description
Background
Flow.bi requires the ability to show custom constraint definitions instead of actual PostgreSQL constraints to provide a curated database experience for our users. The constraints tab should display data from our intf_studio.pgweb_constraints table rather than querying PostgreSQL's system catalogs.
Initial Approach and Challenges
What We Tried First: SQL Conditional Logic
I initially attempted to solve this by implementing conditional SQL logic directly within the table_constraints.sql query using Common Table Expressions (CTEs). The approach involved:
WITH flowbi_constraints AS (
SELECT CASE
WHEN current_setting('pgweb.custom_constraints', true) = 'true'
AND EXISTS (SELECT 1 FROM information_schema.tables
WHERE table_schema = 'intf_studio'
AND table_name = 'pgweb_constraints')
THEN 'use_flowbi'
ELSE 'use_standard'
END AS mode
)
-- ... conditional UNION query based on modeWhy This Failed
This approach encountered a fundamental PostgreSQL limitation:
- Parse-time validation: PostgreSQL validates table existence during query planning phase, not execution
- UNION query constraints: Both sides of UNION operations are parsed regardless of WHERE conditions
- Test environment failures: GitHub Actions tests failed with
pq: relation "intf_studio.pgweb_constraints" does not exist
Even with comprehensive safety checks and conditional logic, PostgreSQL would still attempt to validate the intf_studio.pgweb_constraints table reference before any runtime conditions could be evaluated.
New Approach: Runtime File Replacement
Architecture Decision
After researching pgweb's query loading system, I discovered that pgweb uses Go's //go:embed directive to embed SQL files at compile time. The solution involved modifying this architecture to support runtime file replacement while maintaining backward compatibility.
Implementation Approach
-
Modified SQL Loading Architecture (
pkg/statements/sql.go):- Converted
TableConstraintsfrom static embed to dynamic loading - Added
loadTableConstraintsSQL()function that checks for external files first - Maintained graceful fallback to embedded SQL when external files are unavailable
- Converted
-
Runtime Query Generation:
SELECT conname AS name, definition FROM intf_studio.pgweb_constraints WHERE nspname = $1 AND relname = $2 ORDER BY contype DESC;
Benefits of This Solution
- Clean separation: Custom logic is completely separate from core pgweb functionality
- Test compatibility: All existing tests continue to pass without modification
- Runtime flexibility: Easy to enable/disable per deployment environment
- No performance impact: Standard mode operates with zero overhead
- Maintainable: Clear boundary between standard and custom behavior
Technical Details
How It Works
Custome Constraints Mode (PGWEB_CUSTOM_CONSTRAINTS=true):
- Docker entrypoint generates
/tmp/queries/table_constraints.sqlwith custom query - pgweb starts and
init()function runsloadTableConstraintsSQL() - External file is found and loaded, replacing the embedded SQL
- All constraint requests now query
intf_studio.pgweb_constraints
Standard Mode (PGWEB_CUSTOM_CONSTRAINTS=false or unset):
- No external file is generated
loadTableConstraintsSQL()falls back to embedded SQL- Standard PostgreSQL constraint queries work unchanged
Backward compatibility
This approach overcomes PostgreSQL's parse-time validation limitations while maintaining clean architecture and full backward compatibility. The solution provides Flow.bi with the custom constraints display functionality needed while ensuring that standard pgweb deployments continue to work exactly as before.
Integration Test
Up next.
Current status - Pending