Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1kQGLG-00079w-06 for pgsql-hackers@arkaria.postgresql.org; Wed, 07 Oct 2020 20:47:18 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1kQGLD-0001U3-VM for pgsql-hackers@arkaria.postgresql.org; Wed, 07 Oct 2020 20:47:15 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1kQGLD-0001Tw-9Y for pgsql-hackers@lists.postgresql.org; Wed, 07 Oct 2020 20:47:15 +0000 Received: from mail-ej1-x62a.google.com ([2a00:1450:4864:20::62a]) by makus.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.92) (envelope-from ) id 1kQGLA-0005RT-OV for pgsql-hackers@lists.postgresql.org; Wed, 07 Oct 2020 20:47:14 +0000 Received: by mail-ej1-x62a.google.com with SMTP id ce10so4905422ejc.5 for ; Wed, 07 Oct 2020 13:47:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=date:from:to:cc:subject:message-id:references:mime-version :content-disposition:in-reply-to; bh=/NozrEd4ClZ1wECGB0lwMhR5NypRudM2lkWFB1kUsfQ=; b=qMWg43P1gAMcb0vtsVVjmgL/css4lBeOaMdEgv7Kdhg1PNaSA+m/7YZz3boTmBc27t lNQmUqLQPGe+DbdsiIaHI7upkEAQC277LZ01PZrBU1kq30eVkn37abWgRn1/auqZuvN/ 3yd8AVO3cWModV6rJ9rm7JxePI3o8s/8QWNkkhES6+Kq42J+tOLPNQDduISU3yLwdoQY MaUFf6GfVsME4I+Vxod3N38OVc8h3B4a7EBgHz5/r+baqjEYqixvW+WWuZrltLxENs6S WTSQo9NzOE9IyW4FGJFPl0ww1btTicMdaQmI8lDpuOlTOUM2gqHllcEie00IghdQJ6DG AdbA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:cc:subject:message-id:references :mime-version:content-disposition:in-reply-to; bh=/NozrEd4ClZ1wECGB0lwMhR5NypRudM2lkWFB1kUsfQ=; b=lqSPUPRtfbt3VXzh8wHLDc8ishj0CvdGNIfGELg8M8xn2+mikxcpSL845e1svtYatb JA2ErP7RoCn5Rr2d1UY2tP3eoLqFbkhoLv46pky7ybGpUnvu7U2qYMNFoMB+hOwZ6q2W z67LF5F3CcTlwq0oI9JENVoGrdy3m4eiiWhGpEFTgzKmIVIgrEDXOLJq/nzVbqO9QslA C+Quhnhr4wneg3D1dL60qOEMMhCC7WpojBruzoAacTyxzMPa+I+lDueGzf1KFqD3kfnx NnUZ/T8ftpEhCRhRNdgRlNhx/2HDAGVcczscsjz+uLiI7TpJw23kUYi3P8PWYP4qFebU aN7Q== X-Gm-Message-State: AOAM532tsexv7I6Yy9HQ8KbCmr6qnlXYBYReF51tTX7n1R4w7BH/llNl dEwwhHtvbho8FwiIPO75qik= X-Google-Smtp-Source: ABdhPJyiwx6KFgYtc8pgrRijWo3+y7EAzYrkiA/i3N7kT4AaHgARHobGQA8YDmdjdtIuSvUOm/7Wrg== X-Received: by 2002:a17:906:4cd6:: with SMTP id q22mr5130434ejt.139.1602103630873; Wed, 07 Oct 2020 13:47:10 -0700 (PDT) Received: from yetti ([93.85.161.70]) by smtp.gmail.com with ESMTPSA id p12sm1911961ejz.76.2020.10.07.13.47.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 07 Oct 2020 13:47:10 -0700 (PDT) Date: Wed, 7 Oct 2020 23:47:07 +0300 From: Maksim Kita To: Tom Lane Cc: pgsql-hackers@lists.postgresql.org Subject: Re: Allow deleting enum value Message-ID: <20201007204707.GA210713@yetti> References: <20201007142656.GA181015@yetti> <2151250.1602084214@sss.pgh.pa.us> <2191224.1602086595@sss.pgh.pa.us> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="qDbXVdCdHGoSgWSk" Content-Disposition: inline In-Reply-To: <2191224.1602086595@sss.pgh.pa.us> List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Precedence: bulk --qDbXVdCdHGoSgWSk Content-Type: text/plain; charset=us-ascii Content-Disposition: inline I like the idea, during prototype I added additional column and modified enum_in method. But enum_in is called in contexts that can be important for user (like comparisons). Example: postgres=# CREATE TYPE test_enum AS enum ('1', '2', '3'); CREATE TYPE postgres=# CREATE TABLE test_table ( value test_enum ); postgres=# INSERT INTO test_table VALUES ('1'), ('2'), ('3'); INSERT 0 3 postgres=# ALTER TYPE test_enum DELETE VALUE '2'; ALTER TYPE postgres=# INSERT INTO test_table VALUES ('2'); ERROR: enum value is dropped test_enum: "2" LINE 1: INSERT INTO test_table VALUES ('2'); postgres=# SELECT * FROM test_table WHERE value = '2'; ERROR: enum value is dropped test_enum: "2" LINE 1: SELECT * FROM test_table WHERE value = '2'; postgres=# UPDATE test_table SET value = '3' WHERE value = '2'; ERROR: enum value is dropped test_enum: "2" LINE 1: UPDATE test_table SET value = '3' WHERE value = '2'; Probably we need to make more specific change for enum type to prevent using of dropped column in context of insert or update (where we creating dropped enum value), but not in others. Is that possible ? What places should I look into ? Thanks. Best regards, Maksim Kita --qDbXVdCdHGoSgWSk Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="0001-Allow-alter-type-delete-value-in-enum.patch" From f829ae210fc0f6a010cba9c9e4e7772a9b59ae37 Mon Sep 17 00:00:00 2001 From: Maksim Kita Date: Wed, 7 Oct 2020 23:35:56 +0300 Subject: [PATCH] Allow alter type delete value in enum --- src/backend/catalog/pg_enum.c | 69 +++++++++++++++++++++++++++++++++ src/backend/commands/typecmds.c | 8 +++- src/backend/parser/gram.y | 11 ++++++ src/backend/utils/adt/enum.c | 10 +++++ src/include/catalog/pg_enum.h | 2 + 5 files changed, 98 insertions(+), 2 deletions(-) diff --git a/src/backend/catalog/pg_enum.c b/src/backend/catalog/pg_enum.c index 27e4100a6f..907db93cf4 100644 --- a/src/backend/catalog/pg_enum.c +++ b/src/backend/catalog/pg_enum.c @@ -133,6 +133,7 @@ EnumValuesCreate(Oid enumTypeOid, List *vals) values[Anum_pg_enum_enumsortorder - 1] = Float4GetDatum(elemno + 1); namestrcpy(&enumlabel, lab); values[Anum_pg_enum_enumlabel - 1] = NameGetDatum(&enumlabel); + values[Anum_pg_enum_isdropped - 1] = false; tup = heap_form_tuple(RelationGetDescr(pg_enum), values, nulls); @@ -485,6 +486,7 @@ restart: values[Anum_pg_enum_enumsortorder - 1] = Float4GetDatum(newelemorder); namestrcpy(&enumlabel, newVal); values[Anum_pg_enum_enumlabel - 1] = NameGetDatum(&enumlabel); + values[Anum_pg_enum_isdropped - 1] = false; enum_tup = heap_form_tuple(RelationGetDescr(pg_enum), values, nulls); CatalogTupleInsert(pg_enum, enum_tup); heap_freetuple(enum_tup); @@ -583,6 +585,73 @@ RenameEnumLabel(Oid enumTypeOid, table_close(pg_enum, RowExclusiveLock); } +extern void RemoveEnumLabel(Oid enumTypeOid, const char *val) +{ + Relation pg_enum; + HeapTuple enum_tup; + Form_pg_enum en; + CatCList *list; + int nelems; + HeapTuple old_tup; + int i; + + /* check length of new label is ok */ + if (strlen(val) > (NAMEDATALEN - 1)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_NAME), + errmsg("invalid enum label \"%s\"", val), + errdetail("Labels must be %d characters or less.", + NAMEDATALEN - 1))); + + /* + * Acquire a lock on the enum type, which we won't release until commit. + * This ensures that two backends aren't concurrently modifying the same + * enum type. Since we are not changing the type's sort order, this is + * probably not really necessary, but there seems no reason not to take + * the lock to be sure. + */ + LockDatabaseObject(TypeRelationId, enumTypeOid, 0, ExclusiveLock); + + pg_enum = table_open(EnumRelationId, RowExclusiveLock); + + /* Get the list of existing members of the enum */ + list = SearchSysCacheList1(ENUMTYPOIDNAME, + ObjectIdGetDatum(enumTypeOid)); + nelems = list->n_members; + + /* + * Check if the label is in use. (The unique index on pg_enum would catch that anyway, but we + * prefer a friendlier error message.) + */ + for (i = 0; i < nelems; i++) + { + enum_tup = &(list->members[i]->tuple); + en = (Form_pg_enum) GETSTRUCT(enum_tup); + if (strcmp(NameStr(en->enumlabel), val) == 0) + { + old_tup = enum_tup; + break; + } + } + if (!old_tup) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("\"%s\" is not an existing enum label", + val))); + + /* OK, make a writable copy of old tuple */ + enum_tup = heap_copytuple(old_tup); + en = (Form_pg_enum) GETSTRUCT(enum_tup); + + ReleaseCatCacheList(list); + + /* Update the pg_enum entry */ + en->isdropped = true; + CatalogTupleUpdate(pg_enum, &enum_tup->t_self, enum_tup); + heap_freetuple(enum_tup); + + table_close(pg_enum, RowExclusiveLock); +} /* * Test if the given enum value is on the blacklist diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 483bb65ddc..b9b3e18a94 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -1232,13 +1232,17 @@ AlterEnum(AlterEnumStmt *stmt) ReleaseSysCache(tup); - if (stmt->oldVal) + if (stmt->oldVal && stmt->newVal) { /* Rename an existing label */ RenameEnumLabel(enum_type_oid, stmt->oldVal, stmt->newVal); } - else + else if (stmt->oldVal) { + /* Delete an existing label */ + RemoveEnumLabel(enum_type_oid, stmt->oldVal); + } + else { /* Add a new label */ AddEnumLabel(enum_type_oid, stmt->newVal, stmt->newValNeighbor, stmt->newValIsAfter, diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 0d101d8171..82dd5147fa 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -5792,6 +5792,17 @@ AlterEnumStmt: n->skipIfNewValExists = false; $$ = (Node *) n; } + | ALTER TYPE_P any_name DELETE_P VALUE_P Sconst + { + AlterEnumStmt *n = makeNode(AlterEnumStmt); + n->typeName = $3; + n->oldVal = $6; + n->newVal = NULL; + n->newValNeighbor = NULL; + n->newValIsAfter = false; + n->skipIfNewValExists = false; + $$ = (Node *) n; + } ; opt_if_not_exists: IF_P NOT EXISTS { $$ = true; } diff --git a/src/backend/utils/adt/enum.c b/src/backend/utils/adt/enum.c index 5ead794e34..397c02fa69 100644 --- a/src/backend/utils/adt/enum.c +++ b/src/backend/utils/adt/enum.c @@ -113,6 +113,7 @@ enum_in(PG_FUNCTION_ARGS) Oid enumtypoid = PG_GETARG_OID(1); Oid enumoid; HeapTuple tup; + Form_pg_enum en; /* must check length to prevent Assert failure within SearchSysCache */ if (strlen(name) >= NAMEDATALEN) @@ -132,6 +133,15 @@ enum_in(PG_FUNCTION_ARGS) format_type_be(enumtypoid), name))); + en = (Form_pg_enum) GETSTRUCT(tup); + + if (en->isdropped) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("enum value is dropped %s: \"%s\"", + format_type_be(enumtypoid), + name))); + /* check it's safe to use in SQL */ check_safe_enum_use(tup); diff --git a/src/include/catalog/pg_enum.h b/src/include/catalog/pg_enum.h index b28d441ba7..6a7b00ffbe 100644 --- a/src/include/catalog/pg_enum.h +++ b/src/include/catalog/pg_enum.h @@ -34,6 +34,7 @@ CATALOG(pg_enum,3501,EnumRelationId) Oid enumtypid; /* OID of owning enum type */ float4 enumsortorder; /* sort position of this enum value */ NameData enumlabel; /* text representation of enum value */ + bool isdropped; /* is enum value dropped */ } FormData_pg_enum; /* ---------------- @@ -53,6 +54,7 @@ extern void AddEnumLabel(Oid enumTypeOid, const char *newVal, bool skipIfExists); extern void RenameEnumLabel(Oid enumTypeOid, const char *oldVal, const char *newVal); +extern void RemoveEnumLabel(Oid enumTypeOid, const char *val); extern bool EnumBlacklisted(Oid enum_id); extern Size EstimateEnumBlacklistSpace(void); extern void SerializeEnumBlacklist(void *space, Size size); -- 2.25.1 --qDbXVdCdHGoSgWSk--