Support TYPE 'object_store' servers; make read_only a server option#320
Draft
sfc-gh-npuka wants to merge 27 commits into
Draft
Support TYPE 'object_store' servers; make read_only a server option#320sfc-gh-npuka wants to merge 27 commits into
sfc-gh-npuka wants to merge 27 commits into
Conversation
…pport Introduce the iceberg_catalog foreign data wrapper as a configuration framework for Iceberg catalogs. This allows defining named catalog servers via CREATE SERVER, removing the limitation of a single global REST catalog configured through GUCs. Key changes: - Add iceberg_catalog_validator for server option validation - Add RestCatalogConnectionInfo struct to unify connection parameters - Add GetRestCatalogConnectionFromGUCs/FromServer resolution functions - Refactor all REST catalog functions to accept RestCatalogConnectionInfo - Refactor token cache from single global to per-server hash table - Add extension upgrade SQL (3.2--3.3) with FDW, validator, and pre-created postgres/object_store servers Signed-off-by: sfc-gh-npuka <naisila.puka@snowflake.com>
Update IsServerBasedRestCatalog and HasRestCatalogTableOption to check whether a catalog option value refers to an iceberg_catalog foreign server. Servers without an explicit TYPE default to rest. Signed-off-by: sfc-gh-npuka <naisila.puka@snowflake.com>
Update pg_lake_iceberg_validator to accept server names as valid catalog option values (in addition to the existing postgres/object_store/rest literals). Update ProcessCreateIcebergTableFromForeignTableStmt to resolve REST catalog connection info from the named server when the catalog option refers to an iceberg_catalog server. Signed-off-by: sfc-gh-npuka <naisila.puka@snowflake.com>
Refactor transaction-level REST catalog request tracking to resolve and store a RestCatalogConnectionInfo per table. Batch commit requests are now grouped by REST catalog host, so tables using different catalog servers within the same transaction are committed to the correct endpoints independently. Signed-off-by: sfc-gh-npuka <naisila.puka@snowflake.com>
Comprehensive test suite covering: - FDW existence and pre-created postgres/object_store servers - CREATE SERVER with valid and invalid options - Foreign table creation on handler-less FDW (query-time error) - ALTER/DROP SERVER operations - Server-based catalog references in CREATE TABLE - Backward compatibility with catalog='rest'/'postgres'/'object_store' Signed-off-by: sfc-gh-npuka <naisila.puka@snowflake.com>
1 - Add extension owned rest catalog of type 'rest'
2 - Disallow creation of further 'postgres' or 'object_store'
type catalogs unless we are creating_extension
3 - Disallow renaming/dropping extension owned catalogs.
4 - Disallow altering the options of extension-owned postgres
and object_store catalogs.
Signed-off-by: sfc-gh-npuka <naisila.puka@snowflake.com>
- Add helper functions:
- IsRestCatalogOwnedByExtension ('rest' name)
- IsCatalogOwnedByExtension ('postgres', 'object_store', 'rest')
- Rename IsServerBasedRestCatalog to IsRestCatalogOwnedByUsers
- Use servername per server token cache
- Add tests where we modify tables from different catalogs(rest_endpoints)
in the same transaction.
- Keep some comments I accidentally removed.
Signed-off-by: sfc-gh-npuka <naisila.puka@snowflake.com>
This means any type 'rest' server (extension or user-owned) will use GUC options as default, and change any option to the ones set on the server. This eliminates the need of the function IsRestCatalogOwnedByUsers Instead we just have IsRestCatalog function. This also eliminates the need of GetRestCatalogConnectionFromGUCs Instead we just have GetRestCatalogConnectionFromServer - this function uses GUC values as defaults for all type 'rest' servers. Signed-off-by: sfc-gh-npuka <naisila.puka@snowflake.com>
- Block CREATE FOREIGN TABLE on iceberg_catalog servers. The iceberg_catalog FDW has no handler, so foreign tables created on it would fail at query time with "has no handler". The check is added to ErrorUnsupportedCreatePgLakeTableHandler in pg_lake_table, which already runs first for all CREATE FOREIGN TABLE statements. - Block ALTER SERVER ... OWNER TO on extension-owned catalog servers (postgres, object_store, rest). - Move ICEBERG_CATALOG_FDW_NAME from rest_catalog.h to catalog_type.h alongside the other catalog name constants, since it is referenced by both pg_lake_iceberg and pg_lake_table. - Rename rest_auth_type value "default" to "oauth2" to better describe the standard OAuth2 client_credentials grant with Basic auth. "default" value is also kept. - Rename ProtectExtensionCatalogServersHandler to BlockDDLOnExtensionCatalogs for clarity. - Move BlockDDLOnExtensionCatalogs registration from pg_lake_iceberg init to pg_lake_table init, where all other ProcessUtility hooks are registered. Signed-off-by: sfc-gh-npuka <naisila.puka@snowflake.com>
Token cache improvements: - Allocate the token cache hash table and token strings in a dedicated RestTokenCacheCtx memory context (under TopMemoryContext) instead of directly in TopMemoryContext, keeping the cache memory isolated. - On a 419 (token expired) retry, invalidate only the affected server cached token instead of clearing the entire cache. The server name is passed through SendRequestToRestCatalog and stored in a file-scoped static so the retry callback can target the right entry. Transaction commit batching fix: - Group batches by (host, catalogName) instead of host alone. The transaction commit URL includes the catalog prefix, so two servers pointing to the same host but with different catalog_name values need separate commits. Previously, all tables on the same host were batched together using only the first table catalogName. Other improvements: - Replace the ereport(ERROR) FDW name check in GetRestCatalogConnectionFromServer with an Assert, since the catalog option validator already ensures only iceberg_catalog servers are accepted. - Add errhint to credential/host error messages in FetchRestCatalogAccessToken, pointing users to both the server option and the corresponding GUC. - Add test_precreated_rest_server test. Signed-off-by: sfc-gh-npuka <naisila.puka@snowflake.com>
Block CREATE SERVER with a name that case-insensitively matches the extension-owned catalog names (postgres, object_store, rest) on the iceberg_catalog FDW. Also block ALTER SERVER ... RENAME TO a reserved name. Fix a latent bug across multiple files where pg_strncasecmp was used with a partial length, causing prefix-only matching: e.g. "rest_1" would match "rest". Replaced all instances with pg_strcasecmp for exact case-insensitive comparison. Signed-off-by: sfc-gh-npuka <naisila.puka@snowflake.com>
REST catalog options and retry mechanism: - Rename RestCatalogConnectionInfo to RestCatalogOptions throughout - Eliminate CurrentRetryServerName static; pass opts directly through HttpRetryFn callback (void *context + List *headers) so the 419 token-expired handler can force-refresh and patch the Authorization header in-place - Fix double-free in GetRestCatalogAccessToken: null out entry fields before calling FetchRestCatalogAccessToken Transaction-scoped state: - Reject transactions that touch tables from different REST catalog servers - Replace per-table rest catalog opts deep-copy with a single PgLakeXactRestCatalogOpts static, deep-copied into TopTransactionContext on first use - This avoids syscache lookups at XACT_EVENT_COMMIT time, which are forbidden (AssertCouldGetRelation fires during TRANS_COMMIT state) Server configuration enforcement: - Require TYPE 'rest' on CREATE SERVER ... FOREIGN DATA WRAPPER iceberg_catalog (reject NULL or non-rest types) - Make FDW option names and auth type values case-insensitive (pg_strcasecmp), while keeping server names case-sensitive - Make reserved catalog name checks (postgres, object_store, rest) case-insensitive via IsCatalogOwnedByExtension - Support location_prefix server option, overriding the GUC default - Accept user-created iceberg_catalog servers in default_catalog GUC Tests: - Add test_reject_modify_different_rest_catalogs_in_single_transaction - Add test_server_location_prefix_overrides_guc - Add tests for TYPE enforcement, case-sensitive server names, and default_catalog GUC with user-created servers - Remove obsolete no-TYPE-defaults-to-rest tests Signed-off-by: sfc-gh-npuka <naisila.puka@snowflake.com>
Don't create foreign servers for the reserved catalog names in the
extension install script. These names ('postgres', 'object_store',
'rest') could already be in use in the target database (especially
'postgres'), causing the extension script to fail.
Instead, treat them as special built-in identifiers whose configuration
comes entirely from GUC settings. Only user-created catalogs (via
CREATE SERVER ... FOREIGN DATA WRAPPER iceberg_catalog) result in
actual foreign server objects.
Key changes:
- Remove CREATE SERVER postgres/object_store/rest from the upgrade SQL.
- GetRestCatalogOptionsFromCatalog now returns GUC-only opts for the
built-in 'rest' name without looking up a foreign server.
- Simplify BlockDDLOnExtensionCatalogs: only guard CREATE SERVER
(reserved names, TYPE postgres/object_store) and RENAME TO reserved
names. ALTER/DROP/OWNER on built-in names fail naturally since no
server object exists.
- Remove IsIcebergCatalogServer (no longer needed).
- Rename RestCatalogOptions.serverName to .catalog and
GetRestCatalogOptionsFromServer to GetRestCatalogOptionsFromCatalog
to reflect the new semantics.
- Update tests
Signed-off-by: sfc-gh-npuka <naisila.puka@snowflake.com>
The catalog_name option was accepted on iceberg_catalog servers but never consumed. Now GetRestCatalogOptionsFromCatalog reads it and stores it in RestCatalogOptions.catalogName. GetRestCatalogName uses a three-level fallback for the REST API catalog prefix: table option > server option > database name. This lets a server define a default catalog_name for all its tables while still allowing per-table overrides. - Add catalogName field to RestCatalogOptions. - Populate it from the server catalog_name option in GetRestCatalogOptionsFromCatalog. - Rewrite GetRestCatalogName to check table option first, then opts->catalogName. - Writable REST catalog tables cannot use a server with catalog_name set, since for now they inherit from the database name, schema name, and table name - Add tests Signed-off-by: sfc-gh-npuka <naisila.puka@snowflake.com>
Last 3 commits added/removed enough tests in other files to shift the pytest-split group boundaries, moving test_create_extension_table_access_method away from test_create_drop_query_engine (which previously cleaned up dependent extensions). The test was always fragile as it assumed no dependent extensions were installed. Adding CASCADE makes it work regardless of ordering of tests. Signed-off-by: sfc-gh-npuka <naisila.puka@snowflake.com>
Signed-off-by: sfc-gh-npuka <naisila.puka@snowflake.com>
Consolidate the three per-transaction REST catalog statics (RestCatalogRequestsHash, PgLakeXactCommitContext, PgLakeXactRestCatalogOpts) into a single RestCatalogXactState struct to reduce global state and make lifetime management clearer. Merge BlockDDLOnExtensionCatalogs and RequireRestTypeForIcebergCatalogServer into a single ValidateIcebergCatalogServerDDL hook, eliminating duplicate hook guard logic for CREATE SERVER validation. Grant USAGE on iceberg_catalog FDW to lake_write so non-superusers with write permissions can create catalog servers. Additional cleanups: - Convert unreachable host check in FetchRestCatalogAccessToken to Assert - Rename FDW validator parameter from 'catalog' to 'catalogRelId' - Replace two separate override tests with a single parameterized test_server_option_overrides_guc covering rest_endpoint, client_id, client_secret, location_prefix, and catalog_name - Add tests for no-option server creation and lake_write permissions Signed-off-by: sfc-gh-npuka <naisila.puka@snowflake.com>
…og validator Move secrets (client_id, client_secret) to CREATE USER MAPPING OPTIONS. - Split iceberg_catalog_server_options and iceberg_catalog_user_mapping_options - Add UserMappingRelationId handling to iceberg_catalog_validator Signed-off-by: sfc-gh-npuka <naisila.puka@snowflake.com>
…C fallback Implement the credential resolution chain for server-based REST catalogs: 1. CREATE USER MAPPING (per-user, cached by relcache) 2. $PGDATA/catalogs.conf (platform-provided, re-read each time) 3. GUC variables (backward compatibility) New functions: - LookupUserMappingOptions: queries pg_user_mapping syscache without erroring when no mapping exists (unlike GetUserMapping) - ReadCatalogsConfCredentials: parses $PGDATA/catalogs.conf using ParseConfigFp, matching dotted keys like horizon.client_id Rewrite GetRestCatalogOptionsFromCatalog with two phases: Phase 1: non-secret config from server options Phase 2: credentials from user mapping > catalogs.conf > GUCs Update extension upgrade SQL with revised documentation showing user-defined (CREATE USER MAPPING) and platform-provided (catalogs.conf) catalog examples. - scope is accepted in both server and user mapping (user mapping wins) Signed-off-by: sfc-gh-npuka <naisila.puka@snowflake.com>
- Move client_id/client_secret from server OPTIONS to CREATE USER MAPPING in all tests that previously used server-level credentials - Add test_reject_secrets_in_server_options: verifies client_id in server OPTIONS is now rejected - Add user mapping tests: valid options, PUBLIC mapping, rejection of unknown and server-level options in user mapping context - Add catalogs.conf tests with pg_data_dir and catalogs_conf fixtures: credentials from config file, user mapping override, missing credentials error, and scope from config file - Update ALTER SERVER test to use scope instead of client_id Signed-off-by: sfc-gh-npuka <naisila.puka@snowflake.com>
When CREATE USER MAPPING or ALTER USER MAPPING targets an iceberg_catalog server, overwrite client_id and client_secret values with asterisks directly in the queryString buffer. This prevents credential leakage in pg_stat_statements and similar. Implementation: - iceberg_catalog_secret_options: separates secret names (client_id, client_secret) from the full user mapping option list so scope remains visible. - ScrubUserMappingSecrets: walks DefElem options, uses DefElem.location to find each secret quoted value in the query string, and overwrites value characters with * while preserving quotes and string length. - ScrubIcebergUserMappingHandler: detects user mapping DDL targeting iceberg_catalog servers, calls ScrubUserMappingSecrets, returns false so normal processing continues. Signed-off-by: sfc-gh-npuka <naisila.puka@snowflake.com>
Rename ScrubIcebergUserMappingHandler to RedactRestCatalogUserMappingSecrets and move its registration from pg_lake_iceberg to pg_lake_table, alongside BlockDDLOnExtensionCatalogs. Reorder credential resolution in GetRestCatalogConnectionFromServer so the code reads in ascending priority: GUCs (defaults) → catalogs.conf → user mapping. Previously catalogs.conf was checked after user mapping with NULL guards to avoid overriding it. Guard catalogs.conf parser against malformed entries like "rest." (dot with no key after it) by checking item->name[serverNameLen + 1] != '\0'. Add test for catalogs.conf on the extension-owned rest server. Add DDL-level test for per-user scope isolation: two roles with different scopes on the same server, verified via pg_user_mapping. Signed-off-by: sfc-gh-npuka <naisila.puka@snowflake.com>
New GUC pg_lake_iceberg.catalogs_conf_path controls the location of the catalog credentials file. Defaults to "catalogs.conf", which resolves to $PGDATA/catalogs.conf. Absolute paths are used as-is. The GUC is PGC_SIGHUP (reloadable) and superuser-only. Signed-off-by: sfc-gh-npuka <naisila.puka@snowflake.com>
Signed-off-by: sfc-gh-npuka <naisila.puka@snowflake.com>
Signed-off-by: sfc-gh-npuka <naisila.puka@snowflake.com>
Allow CREATE SERVER ... TYPE 'object_store' FOREIGN DATA WRAPPER iceberg_catalog, mirroring the existing TYPE 'rest' pattern. Each server can carry its own location_prefix and read_only options, enabling shared writer/reader topologies where multiple Postgres instances point at the same S3 path. Key changes: - ValidateIcebergCatalogServerDDL now accepts TYPE 'object_store' (TYPE 'postgres' remains rejected). - Add IsObjectStoreCatalog() in catalog_type.c, paralleling IsRestCatalog(); update HasObjectStoreCatalogTableOption to use it so that tables with catalog='<named_server>' resolve correctly. - Add ObjectStoreCatalogOptions struct and resolution function that reads server options with GUC fallback. - Add read_only as a server-level option (valid for both REST and object_store servers). Server read_only propagates to all tables unless overridden; table read_only='false' on a read_only server is an error. - Update create_table.c to resolve location_prefix from named object_store servers instead of requiring the GUC. - Update IcebergDefaultCatalogCheckHook to accept named object_store servers. - Update catalog export SPI query to include tables on named object_store servers. Signed-off-by: sfc-gh-npuka <naisila.puka@snowflake.com>
5d0b9ef to
fb0da6f
Compare
507450b to
8ed0b8e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
NOTE: not ready for review yet
Allow CREATE SERVER ... TYPE 'object_store' FOREIGN DATA WRAPPER iceberg_catalog, mirroring the existing TYPE 'rest' pattern. Each server can carry its own location_prefix and read_only options, enabling shared writer/reader topologies where multiple Postgres instances point at the same S3 path.
Key changes:
Checklist