Skip to content

Commit b08defb

Browse files
Fix misleading iceberg catalog option error messages
The FDW validator in pg_lake_iceberg used error messages that said options were "only valid for writable catalog=\"rest\"" when they are actually valid for read-only external catalog tables (both catalog=rest and catalog=object_store). Similarly, "required for catalog=\"rest\"" omitted that the same options are required for catalog=object_store read-only tables. Changes: - Reword all "only valid for" / "required for" error messages for catalog_name, catalog_namespace, catalog_table_name, and read_only to accurately describe which catalog types they apply to. - Remove a dead "catalog_namespace required" check: it only applied to REST read-only (object_store excluded), but the REST ALTER whitelist never allows touching catalog_namespace and CREATE auto-fills a default, so the path was unreachable. - Clean up stale copy-pasted comments in ProcessAlterTable that referenced "catalog_table_name only" in the object_store branch and add a comment explaining the validator interaction. - Add a comment in option.c noting that the remaining "required" checks act as a safety net for ALTER DROP scenarios. Tests: - test_iceberg_catalog_option_validation_errors (test_writable_iceberg.py): exercises the four "only valid for read-only external catalog tables" rejection paths without needing external infrastructure. - test_object_store_read_only_alter_drop_required_options (test_object_store_catalog.py): exercises the two "required" paths via ALTER FOREIGN TABLE OPTIONS (DROP ...) on an object_store read-only table. Co-authored-by: Cursor <cursoragent@cursor.com> Signed-off-by: sfc-gh-npuka <naisila.puka@snowflake.com>
1 parent 9e7f568 commit b08defb

4 files changed

Lines changed: 107 additions & 24 deletions

File tree

pg_lake_table/src/ddl/alter_table.c

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -248,20 +248,18 @@ ProcessAlterTable(ProcessUtilityParams * processUtilityParams, void *arg)
248248
}
249249

250250
/*
251-
* For read-only rest catalog iceberg tables, we only allow changing
252-
* catalog_table_name option. All other operations or any other DDLs are
253-
* disallowed.
251+
* For read-only external catalog iceberg tables, we only allow changing
252+
* a subset of catalog options. All other operations are disallowed.
253+
* After the whitelist check passes, we return false so PostgreSQL
254+
* processes the ALTER normally (which invokes the FDW validator on
255+
* the merged option set).
254256
*/
255257
if (HasOnlyCatalogAlterTableOptions(alterStmt))
256258
{
257259
IcebergCatalogType icebergCatalogType = GetIcebergCatalogType(relationId);
258260

259261
if (icebergCatalogType == REST_CATALOG_READ_ONLY)
260262
{
261-
/*
262-
* We currently only allow changing catalog_table_name option for
263-
* read-only rest catalog iceberg tables.
264-
*/
265263
List *allowedOptions = list_make1("catalog_table_name");
266264

267265
ErrorIfUnsupportedTableOptionChange(alterStmt, allowedOptions);
@@ -270,10 +268,6 @@ ProcessAlterTable(ProcessUtilityParams * processUtilityParams, void *arg)
270268
}
271269
else if (icebergCatalogType == OBJECT_STORE_READ_ONLY)
272270
{
273-
/*
274-
* We currently only allow changing catalog_table_name option for
275-
* read-only rest catalog iceberg tables.
276-
*/
277271
List *allowedOptions = list_make3("catalog_table_name", "catalog_namespace", "catalog_name");
278272

279273
ErrorIfUnsupportedTableOptionChange(alterStmt, allowedOptions);

pg_lake_table/src/fdw/option.c

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -908,30 +908,29 @@ pg_lake_iceberg_validator(PG_FUNCTION_ARGS)
908908
!(icebergCatalogType == REST_CATALOG_READ_ONLY || icebergCatalogType == OBJECT_STORE_READ_ONLY))
909909
ereport(ERROR,
910910
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
911-
errmsg("\"read_only\" option is only valid for catalog=\"rest\"")));
911+
errmsg("\"read_only\" option is only valid for catalog=\"rest\" or catalog=\"object_store\"")));
912912

913913
if (catalog == ForeignTableRelationId)
914914
{
915915
if (icebergCatalogType == REST_CATALOG_READ_ONLY || icebergCatalogType == OBJECT_STORE_READ_ONLY)
916916
{
917917
/*
918-
* catalog_namespace, catalog_table_name and catalog_name is
919-
* required for catalog=rest
918+
* catalog_name and catalog_table_name are required for
919+
* read-only external catalog tables, i.e. catalog=rest and catalog=object_store
920+
*
921+
* On CREATE, ProcessCreateIcebergTableFromForeignTableStmt
922+
* auto-fills defaults for these options, so these checks
923+
* act as a safety net for ALTER DROP scenarios.
920924
*/
921925
if (!catalogName)
922926
ereport(ERROR,
923927
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
924-
errmsg("\"catalog_name\" option is required for catalog=\"rest\"")));
925-
926-
if (!catalogNamespace && icebergCatalogType != OBJECT_STORE_READ_ONLY)
927-
ereport(ERROR,
928-
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
929-
errmsg("\"catalog_namespace\" option is required for catalog=\"rest\"")));
928+
errmsg("\"catalog_name\" option is required for read-only external catalog tables")));
930929

931930
if (!catalogTableName)
932931
ereport(ERROR,
933932
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
934-
errmsg("\"catalog_table_name\" option is required for catalog=\"rest\"")));
933+
errmsg("\"catalog_table_name\" option is required for read-only external catalog tables")));
935934
}
936935
else
937936
{
@@ -941,17 +940,17 @@ pg_lake_iceberg_validator(PG_FUNCTION_ARGS)
941940
if (catalogNamespace)
942941
ereport(ERROR,
943942
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
944-
errmsg("\"catalog_namespace\" option is only valid for writable catalog=\"rest\"")));
943+
errmsg("\"catalog_namespace\" option is only valid for read-only external catalog tables")));
945944

946945
if (catalogTableName)
947946
ereport(ERROR,
948947
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
949-
errmsg("\"catalog_table_name\" option is only valid for writable catalog=\"rest\"")));
948+
errmsg("\"catalog_table_name\" option is only valid for read-only external catalog tables")));
950949

951950
if (catalogName)
952951
ereport(ERROR,
953952
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
954-
errmsg("\"catalog_name\" option is only valid for writable catalog=\"rest\"")));
953+
errmsg("\"catalog_name\" option is only valid for read-only external catalog tables")));
955954

956955
}
957956
}

pg_lake_table/tests/pytests/test_object_store_catalog.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,47 @@ def test_read_only_object_store_with_non_existing_options(
127127
pg_conn.commit()
128128

129129

130+
def test_object_store_read_only_alter_drop_required_options(
131+
pg_conn, s3, extension, with_default_location, adjust_object_store_settings
132+
):
133+
schema = "test_alter_drop_required"
134+
run_command(f"CREATE SCHEMA {schema}", pg_conn)
135+
136+
run_command(
137+
f"CREATE TABLE {schema}.wrt_tbl(a INT) USING iceberg WITH (catalog='object_store')",
138+
pg_conn,
139+
)
140+
pg_conn.commit()
141+
wait_until_object_store_writable_table_pushed(pg_conn, schema, "wrt_tbl")
142+
143+
run_command(
144+
f"CREATE TABLE {schema}.ro_tbl() USING iceberg WITH (catalog='object_store', read_only=True, catalog_table_name='wrt_tbl')",
145+
pg_conn,
146+
)
147+
pg_conn.commit()
148+
149+
# dropping catalog_name should fail — it is required
150+
error = run_command(
151+
f"ALTER FOREIGN TABLE {schema}.ro_tbl OPTIONS (DROP catalog_name)",
152+
pg_conn,
153+
raise_error=False,
154+
)
155+
assert '"catalog_name" option is required for read-only external catalog tables' in str(error)
156+
pg_conn.rollback()
157+
158+
# dropping catalog_table_name should fail — it is required
159+
error = run_command(
160+
f"ALTER FOREIGN TABLE {schema}.ro_tbl OPTIONS (DROP catalog_table_name)",
161+
pg_conn,
162+
raise_error=False,
163+
)
164+
assert '"catalog_table_name" option is required for read-only external catalog tables' in str(error)
165+
pg_conn.rollback()
166+
167+
run_command(f"DROP SCHEMA {schema} CASCADE", pg_conn)
168+
pg_conn.commit()
169+
170+
130171
# basic flow for object store catalog tables
131172
# changes on the source is reflected on the target
132173
def test_read_only_object_store_read_write(

pg_lake_table/tests/pytests/test_writable_iceberg.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,55 @@ def test_writable_iceberg_create_wrong_option(pg_conn, extension):
120120
pg_conn.commit()
121121

122122

123+
def test_iceberg_catalog_option_validation_errors(pg_conn, extension):
124+
schema = "test_catalog_option_validation"
125+
run_command(f"CREATE SCHEMA {schema}", pg_conn)
126+
pg_conn.commit()
127+
128+
# read_only is only valid for catalog="rest" or catalog="object_store"
129+
error = run_command(
130+
f"""CREATE FOREIGN TABLE {schema}.ft (id int) SERVER pg_lake_iceberg
131+
OPTIONS (location 's3://bucket/path', read_only 'true')""",
132+
pg_conn,
133+
raise_error=False,
134+
)
135+
assert '"read_only" option is only valid for catalog="rest" or catalog="object_store"' in str(error)
136+
pg_conn.rollback()
137+
138+
# catalog_name is only valid for read-only external catalog tables
139+
error = run_command(
140+
f"""CREATE FOREIGN TABLE {schema}.ft (id int) SERVER pg_lake_iceberg
141+
OPTIONS (location 's3://bucket/path', catalog_name 'cat')""",
142+
pg_conn,
143+
raise_error=False,
144+
)
145+
assert '"catalog_name" option is only valid for read-only external catalog tables' in str(error)
146+
pg_conn.rollback()
147+
148+
# catalog_namespace is only valid for read-only external catalog tables
149+
error = run_command(
150+
f"""CREATE FOREIGN TABLE {schema}.ft (id int) SERVER pg_lake_iceberg
151+
OPTIONS (location 's3://bucket/path', catalog_namespace 'ns')""",
152+
pg_conn,
153+
raise_error=False,
154+
)
155+
assert '"catalog_namespace" option is only valid for read-only external catalog tables' in str(error)
156+
pg_conn.rollback()
157+
158+
# catalog_table_name is only valid for read-only external catalog tables
159+
error = run_command(
160+
f"""CREATE FOREIGN TABLE {schema}.ft (id int) SERVER pg_lake_iceberg
161+
OPTIONS (location 's3://bucket/path', catalog_table_name 'tbl')""",
162+
pg_conn,
163+
raise_error=False,
164+
)
165+
assert '"catalog_table_name" option is only valid for read-only external catalog tables' in str(error)
166+
pg_conn.rollback()
167+
168+
run_command(f"DROP SCHEMA {schema} CASCADE", pg_conn)
169+
pg_conn.commit()
170+
171+
123172
def test_writable_iceberg_table_failure(
124173
installcheck,
125174
install_iceberg_to_duckdb,

0 commit comments

Comments
 (0)