Skip to content

Commit 1e5f0b7

Browse files
Refactor read-only iceberg table errors
Signed-off-by: Aykut Bozkurt <[email protected]>
1 parent 0d01cfc commit 1e5f0b7

File tree

14 files changed

+137
-194
lines changed

14 files changed

+137
-194
lines changed

pg_lake_copy/src/copy/copy.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -389,8 +389,6 @@ ProcessPgLakeCopyFrom(CopyStmt *copyStmt, ParseState *pstate, Relation relation,
389389
if (PgLakeCopyValidityCheckHook)
390390
PgLakeCopyValidityCheckHook(relationId);
391391

392-
ErrorIfReadOnlyIcebergTable(relationId);
393-
394392
/* check read-only transaction and parallel mode */
395393
if (XactReadOnly)
396394
{

pg_lake_engine/include/pg_lake/util/rel_utils.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ extern PGDLLEXPORT PgLakeTableType GetPgLakeTableTypeViaServerName(char *serverN
3434
extern PGDLLEXPORT bool IsPgLakeForeignTableById(Oid foreignTableId);
3535
extern PGDLLEXPORT bool IsPgLakeIcebergForeignTableById(Oid foreignTableId);
3636
extern PGDLLEXPORT bool IsPgLakeServerName(const char *serverName);
37-
extern PGDLLEXPORT bool IsAnyWritableLakeTable(Oid foreignTableId);
3837
extern PGDLLEXPORT bool IsPgLakeIcebergServerName(const char *serverName);
3938
extern PGDLLEXPORT char *GetWritableTableLocation(Oid relationId, char **queryArguments);
4039
extern PGDLLEXPORT void EnsureTableOwner(Oid relationId);

pg_lake_engine/src/utils/rel_utils.c

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -80,22 +80,6 @@ GetPgLakeForeignServerName(Oid foreignTableId)
8080
}
8181

8282

83-
/*
84-
* IsAnyWritableLakeTable - check if the table is writable.
85-
*/
86-
bool
87-
IsAnyWritableLakeTable(Oid foreignTableId)
88-
{
89-
ForeignTable *foreignTable = GetForeignTable(foreignTableId);
90-
List *options = foreignTable->options;
91-
DefElem *writableOption = GetOption(options, "writable");
92-
PgLakeTableType tableType = GetPgLakeTableType(foreignTableId);
93-
94-
return tableType == PG_LAKE_ICEBERG_TABLE_TYPE ||
95-
(writableOption != NULL ? defGetBoolean(writableOption) : false);
96-
}
97-
98-
9983
/*
10084
* IsAnyLakeForeignTableById - check if the table is a lake table.
10185
*/

pg_lake_iceberg/include/pg_lake/iceberg/catalog.h

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,7 @@ extern PGDLLEXPORT void UpdateInternalCatalogMetadataLocation(Oid relationId, co
6262
extern PGDLLEXPORT void UpdateAllInternalIcebergTablesToReadOnly(void);
6363
extern PGDLLEXPORT const char *GetIcebergDefaultLocationPrefix(void);
6464
extern PGDLLEXPORT bool IcebergTablesCatalogExists(void);
65-
extern PGDLLEXPORT void ErrorIfReadOnlyIcebergTable(Oid relationId);
66-
extern PGDLLEXPORT bool WarnIfReadOnlyIcebergTable(Oid relationId);
67-
extern PGDLLEXPORT void ErrorIfReadOnlyExternalCatalogIcebergTable(Oid relationId);
68-
extern PGDLLEXPORT bool IsReadOnlyIcebergTable(Oid relationId);
6965
extern PGDLLEXPORT bool RelationExistsInTheIcebergCatalog(Oid relationId);
7066
extern PGDLLEXPORT bool HasCustomLocation(Oid relationId);
7167
extern PGDLLEXPORT bool IsWritableIcebergTable(Oid relationId);
68+
extern PGDLLEXPORT bool IsReadOnlyIcebergTable(Oid relationId);

pg_lake_iceberg/src/iceberg/catalog.c

Lines changed: 10 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ static char *GetIcebergExternalMetadataLocation(Oid relationId);
3838
static char *GetIcebergCatalogMetadataLocationInternal(Oid relationId, bool isPrevMetadata, bool forUpdate);
3939
static char *GetIcebergCatalogColumnInternal(Oid relationId, char *columnName, bool forUpdate, bool errorIfNotFound);
4040
static void ErrorIfSameTableExistsInExternalCatalog(Oid relationId);
41-
static bool ReportIfReadOnlyIcebergTable(Oid relationId, int logLevel);
4241

4342
/*
4443
* InsertExternalIcebergCatalogTable inserts a record into the Iceberg
@@ -436,65 +435,6 @@ GetIcebergCatalogPreviousMetadataLocation(Oid relationId, bool forUpdate)
436435
return GetIcebergCatalogMetadataLocationInternal(relationId, true, forUpdate);
437436
}
438437

439-
/*
440-
* ErrorIfReadOnlyIcebergTable checks if the iceberg table is read-only and
441-
* throws an error if it is.
442-
*/
443-
void
444-
ErrorIfReadOnlyIcebergTable(Oid relationId)
445-
{
446-
ReportIfReadOnlyIcebergTable(relationId, ERROR);
447-
448-
ErrorIfReadOnlyExternalCatalogIcebergTable(relationId);
449-
}
450-
451-
/*
452-
* WarnIfReadOnlyIcebergTable checks if the iceberg table is read-only and
453-
* throws a warning if it is.
454-
*/
455-
bool
456-
WarnIfReadOnlyIcebergTable(Oid relationId)
457-
{
458-
return ReportIfReadOnlyIcebergTable(relationId, WARNING);
459-
}
460-
461-
/*
462-
* Similar to ErrorIfReadOnlyExternalCatalogIcebergTable, but for external
463-
* catalog iceberg tables, namely rest catalog and object catalog tables.
464-
*/
465-
void
466-
ErrorIfReadOnlyExternalCatalogIcebergTable(Oid relationId)
467-
{
468-
IcebergCatalogType icebergCatalogType = GetIcebergCatalogType(relationId);
469-
470-
if (icebergCatalogType == REST_CATALOG_READ_ONLY ||
471-
icebergCatalogType == OBJECT_STORE_READ_ONLY)
472-
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
473-
errmsg("modifications on read-only external catalog iceberg tables are not supported")));
474-
}
475-
476-
477-
/*
478-
* ReportIfReadOnlyIcebergTable checks if the iceberg table is read-only and
479-
* reports an logLevel if it is.
480-
*
481-
* For non-error cases, it returns true if the table is read-only.
482-
*/
483-
static bool
484-
ReportIfReadOnlyIcebergTable(Oid relationId, int logLevel)
485-
{
486-
bool readOnly = IsReadOnlyIcebergTable(relationId);
487-
488-
if (readOnly)
489-
{
490-
ereport(logLevel,
491-
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
492-
errmsg("iceberg table \"%s\" is read-only", get_rel_name(relationId))));
493-
}
494-
495-
return readOnly;
496-
}
497-
498438
/*
499439
* GetIcebergCatalogMetadataLocationInternal returns the metadata or previous metadata
500440
* location for a table in the iceberg catalog table.
@@ -509,36 +449,6 @@ GetIcebergCatalogMetadataLocationInternal(Oid relationId, bool isPrevMetadata, b
509449
}
510450

511451

512-
/*
513-
* IsReadOnlyIcebergTable checks if the iceberg table is read-only and
514-
* returns true if it is.
515-
*/
516-
bool
517-
IsReadOnlyIcebergTable(Oid relationId)
518-
{
519-
if (GetPgLakeTableType(relationId) != PG_LAKE_ICEBERG_TABLE_TYPE)
520-
{
521-
/* read-only feature is only applicable for pg_lake_iceberg tables */
522-
return false;
523-
}
524-
525-
bool forUpdate = false;
526-
char *columnName = "read_only";
527-
bool errorIfNotFound = false;
528-
529-
530-
char *readOnlyValue =
531-
GetIcebergCatalogColumnInternal(relationId, columnName, forUpdate, errorIfNotFound);
532-
533-
if (readOnlyValue != NULL && pg_strcasecmp(readOnlyValue, "t") == 0)
534-
{
535-
/* let the caller know that this is a read-only table for non-errors */
536-
return true;
537-
}
538-
539-
return false;
540-
}
541-
542452

543453
/*
544454
* RelationExistsInTheIcebergCatalog checks if the relation exists in the iceberg
@@ -808,3 +718,13 @@ IsWritableIcebergTable(Oid relationId)
808718

809719
return (pg_strcasecmp(readOnlyValue, "f") == 0);
810720
}
721+
722+
723+
/*
724+
* IsReadOnlyIcebergTable - check if the iceberg table is read-only.
725+
*/
726+
bool
727+
IsReadOnlyIcebergTable(Oid relationId)
728+
{
729+
return IsIcebergTable(relationId) && !IsWritableIcebergTable(relationId);
730+
}

pg_lake_table/src/ddl/alter_table.c

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ static bool HasPartitionByAdded(AlterTableStmt *alterStmt);
205205
static bool HasPartitionByDropped(AlterTableStmt *alterStmt);
206206
static bool HasOnlyCatalogAlterTableOptions(AlterTableStmt *alterStmt);
207207
static void ErrorIfUnsupportedTableOptionChange(AlterTableStmt *alterStmt, List *allowedOptions);
208+
static void ErrorIfAlterReadOnlyIcebergTable(Oid relationId);
208209

209210
/*
210211
* ProcessAlterTable is used in cases where we want to preempt the error
@@ -228,8 +229,7 @@ ProcessAlterTable(ProcessUtilityParams * processUtilityParams, void *arg)
228229

229230
Oid relationId = AlterTableLookupRelation(alterStmt, NoLock);
230231

231-
if (!IsWritablePgLakeTable(relationId) &&
232-
!IsPgLakeIcebergForeignTableById(relationId))
232+
if (!IsWritablePgLakeTable(relationId) && !IsIcebergTable(relationId))
233233
{
234234
/* for non-pg_lake tables, error when using SET ACCESS METHOD iceberg */
235235
ErrorIfUnsupportedSetAccessMethod(alterStmt);
@@ -272,8 +272,7 @@ ProcessAlterTable(ProcessUtilityParams * processUtilityParams, void *arg)
272272

273273
}
274274

275-
/* check whether we are accepting writes for this table */
276-
ErrorIfReadOnlyIcebergTable(relationId);
275+
ErrorIfAlterReadOnlyIcebergTable(relationId);
277276

278277
ErrorIfUnsupportedTypeAddedForIcebergTables(alterStmt);
279278

@@ -634,13 +633,12 @@ PostProcessRenameWritablePgLakeTable(ProcessUtilityParams * params, void *arg)
634633

635634
Oid relationId = get_relname_relid(relationName, namespaceId);
636635

637-
if (!IsWritablePgLakeTable(relationId) &&
638-
!IsPgLakeIcebergForeignTableById(relationId))
636+
if (!IsWritablePgLakeTable(relationId) && !IsIcebergTable(relationId))
639637
{
640638
return;
641639
}
642640

643-
ErrorIfReadOnlyIcebergTable(relationId);
641+
ErrorIfAlterReadOnlyIcebergTable(relationId);
644642

645643
PgLakeTableType tableType = GetPgLakeTableType(relationId);
646644

@@ -706,13 +704,12 @@ PostProcessAlterWritablePgLakeTableSchema(ProcessUtilityParams * params, void *a
706704

707705
Oid relationId = get_relname_relid(alterSchemaStmt->relation->relname, namespaceId);
708706

709-
if (!IsWritablePgLakeTable(relationId) &&
710-
!IsPgLakeIcebergForeignTableById(relationId))
707+
if (!IsWritablePgLakeTable(relationId) && !IsIcebergTable(relationId))
711708
{
712709
return;
713710
}
714711

715-
ErrorIfReadOnlyIcebergTable(relationId);
712+
ErrorIfAlterReadOnlyIcebergTable(relationId);
716713

717714
PgLakeTableType tableType = GetPgLakeTableType(relationId);
718715

@@ -1368,6 +1365,20 @@ ErrorIfUnsupportedTableOptionChange(AlterTableStmt *alterStmt, List *allowedOpti
13681365
}
13691366

13701367

1368+
/*
1369+
* ErrorIfAlterReadOnlyIcebergTable throws an error if an attempt is made to
1370+
* alter a read-only lake table.
1371+
*/
1372+
static void
1373+
ErrorIfAlterReadOnlyIcebergTable(Oid relationId)
1374+
{
1375+
if (IsReadOnlyIcebergTable(relationId))
1376+
{
1377+
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1378+
errmsg("ALTER TABLE is not supported for read-only iceberg tables")));
1379+
}
1380+
}
1381+
13711382

13721383
/*
13731384
* HasPartitionByAdded checks if new partition_by option is set or added.

pg_lake_table/src/ddl/ddl_changes.c

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -134,15 +134,14 @@ ApplyDDLCatalogChanges(Oid relationId, List *ddlOperations,
134134
}
135135

136136
/*
137-
* We intentionally do not call ErrorIfReadOnlyIcebergTable here
138-
* because users might want to get rid-of the iceberg table on
139-
* forks. If we ever remove the remote files with DROP TABLE, we
140-
* should call ErrorIfReadOnlyIcebergTable here.
137+
* We intentionally do not throw error here because users might
138+
* want to get rid-of the iceberg table on forks. If we ever
139+
* remove the remote files with DROP TABLE, we should throw error.
141140
*
142141
* Also, for read-only tables, we will not mark the files for
143142
* deletion.
144143
*/
145-
if (!IsReadOnlyIcebergTable(relationId))
144+
if (IsWritableIcebergTable(relationId))
146145
{
147146
/*
148147
* metadata is not pushed yet if table is created in current

pg_lake_table/src/ddl/vacuum.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -526,8 +526,6 @@ VacuumLakeTables(ProcessUtilityParams * utilityParams)
526526
get_rel_name(relationId))));
527527
}
528528

529-
ErrorIfReadOnlyExternalCatalogIcebergTable(relationId);
530-
531529
continue;
532530
}
533531

@@ -563,8 +561,13 @@ VacuumLakeTables(ProcessUtilityParams * utilityParams)
563561
{
564562
Oid relationId = lfirst_oid(relationIdCell);
565563

566-
if (WarnIfReadOnlyIcebergTable(relationId))
564+
/* skip read only lake tables */
565+
if (!IsWritablePgLakeTable(relationId) && !IsWritableIcebergTable(relationId))
567566
{
567+
ereport(WARNING,
568+
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
569+
errmsg("lake table \"%s\" is read-only", get_rel_name(relationId))));
570+
568571
/* let other tables VACUUMed */
569572
continue;
570573
}

pg_lake_table/src/fdw/pg_lake_table.c

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,7 @@ static void ErrorIfSystemColumnUsed(Oid relationId, AttrNumber attnum);
515515
static bool AdjustUniqueRelationIdentifiersViaAlias(Node *node, void *context);
516516
static bool IsSimpleDelete(ModifyTableState *mtstate, Relation resultRel);
517517
static void ShutdownPgLakeScan(ForeignScanState *node);
518+
static void ErrorIfTruncateReadOnlyLakeTable(Oid relationId);
518519

519520

520521
/*
@@ -2551,7 +2552,7 @@ postgresIsForeignRelUpdatable(Relation rel)
25512552
* By default, all pg_lake foreign tables are assumed not writable. This
25522553
* can be overridden by a per-table setting.
25532554
*/
2554-
if (!IsAnyWritableLakeTable(relationId))
2555+
if (!IsWritablePgLakeTable(relationId) && !IsWritableIcebergTable(relationId))
25552556
return 0;
25562557

25572558
int writeFlags = (1 << CMD_INSERT);
@@ -3360,8 +3361,6 @@ create_foreign_modify(Relation rel,
33603361
CmdType operation = mtstate->operation;
33613362
Oid relationId = RelationGetRelid(rel);
33623363

3363-
ErrorIfReadOnlyIcebergTable(relationId);
3364-
33653364
/* extra checks */
33663365
if (PgLakeModifyValidityCheckHook)
33673366
PgLakeModifyValidityCheckHook(relationId);
@@ -4412,7 +4411,7 @@ postgresExecForeignTruncate(List *relations,
44124411
Relation relation = lfirst(relationCell);
44134412
Oid relationId = RelationGetRelid(relation);
44144413

4415-
ErrorIfReadOnlyIcebergTable(relationId);
4414+
ErrorIfTruncateReadOnlyLakeTable(relationId);
44164415

44174416
/* extra checks */
44184417
if (PgLakeModifyValidityCheckHook)
@@ -5789,3 +5788,17 @@ ShutdownPgLakeScan(ForeignScanState *node)
57895788

57905789
estate->es_processed += fsstate->skippableRows;
57915790
}
5791+
5792+
5793+
/*
5794+
* ErrorIfTruncateReadOnlyLakeTable
5795+
* Raise an error if trying to TRUNCATE a read-only lake table.
5796+
*/
5797+
static void
5798+
ErrorIfTruncateReadOnlyLakeTable(Oid relationId)
5799+
{
5800+
if (!IsWritablePgLakeTable(relationId) && !IsWritableIcebergTable(relationId))
5801+
ereport(ERROR,
5802+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5803+
errmsg("TRUNCATE on read-only lake tables is not supported")));
5804+
}

pg_lake_table/src/planner/insert_select.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#include "access/table.h"
2121
#include "pg_lake/extensions/postgis.h"
22+
#include "pg_lake/iceberg/catalog.h"
2223
#include "pg_lake/planner/insert_select.h"
2324
#include "pg_lake/planner/query_pushdown.h"
2425
#include "pg_lake/util/numeric.h"
@@ -84,10 +85,10 @@ IsPushdownableInsertSelectQuery(Query *query)
8485

8586
Oid insertIntoRelid = GetInsertRelidFromInsertSelect(query);
8687

87-
if (!IsPgLakeIcebergForeignTableById(insertIntoRelid))
88+
if (!IsWritablePgLakeTable(insertIntoRelid) && !IsWritableIcebergTable(insertIntoRelid))
8889
{
8990
ereport(DEBUG4,
90-
(errmsg("INSERT..SELECT into non-pg_lake table is not pushdownable")));
91+
(errmsg("INSERT..SELECT into read-only pg_lake table is not pushdownable")));
9192

9293
return false;
9394
}

0 commit comments

Comments
 (0)