From 4a0e73a36073d5d6d3381a257387441195516e49 Mon Sep 17 00:00:00 2001 From: "tender.wang" Date: Tue, 30 Jan 2024 19:20:00 +0800 Subject: [PATCH] Fix "tuple already updated by self" error when duplicate token type name in ALTER TEXT SEARCH CONFIGURATION xxx ALTER MAPPING FOR. Currently, we don't remove duplicated token type name in getTokenTypes(). Above error will report in MakeConfigurationMapping() when meets duplicated token type name. We remove duplicated token type name in getTokenTypes() and return List * not int *. DropConfigurationMapping() need token type name when reports error, so the returned List point to a structue, which not only has token type numbuer, but has the token type name. --- src/backend/commands/tsearchcmds.c | 98 +++++++++++++++++++-------- src/test/regress/expected/tsdicts.out | 3 + src/test/regress/sql/tsdicts.sql | 5 ++ 3 files changed, 76 insertions(+), 30 deletions(-) diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c index 07671720ea..3905dd2897 100644 --- a/src/backend/commands/tsearchcmds.c +++ b/src/backend/commands/tsearchcmds.c @@ -48,6 +48,12 @@ #include "utils/rel.h" #include "utils/syscache.h" +/* result structure for getTokenTypes() */ +typedef struct +{ + int num; /* token type number */ + char *name; /* token type name */ +} TSTokenTypes; static void MakeConfigurationMapping(AlterTSConfigurationStmt *stmt, HeapTuple tup, Relation relMap); @@ -1192,22 +1198,47 @@ AlterTSConfiguration(AlterTSConfigurationStmt *stmt) } /* - * Translate a list of token type names to an array of token type numbers + * Test whether a token type name is present in a TSTokenTypes list. + * + * We return "true" if a name is already in the list. + */ +static bool +token_type_name_present(char *token_name, List *tokens) +{ + ListCell *c; + bool found = false; + + foreach(c, tokens) + { + TSTokenTypes *ts = (TSTokenTypes *) lfirst(c); + + if (strcmp(token_name, ts->name) == 0) + { + found = true; + break; + } + } + + return found; +} + +/* + * Translate a list of token type names to an array of token type numbers. + * + * We remove duplicated entry in tokennames list, and return a list of structure. */ -static int * +static List * getTokenTypes(Oid prsId, List *tokennames) { TSParserCacheEntry *prs = lookup_ts_parser_cache(prsId); LexDescr *list; - int *res, - i, - ntoken; + List *result = NIL; + int ntoken; ListCell *tn; ntoken = list_length(tokennames); if (ntoken == 0) - return NULL; - res = (int *) palloc(sizeof(int) * ntoken); + return NIL; if (!OidIsValid(prs->lextypeOid)) elog(ERROR, "method lextype isn't defined for text search parser %u", @@ -1217,19 +1248,25 @@ getTokenTypes(Oid prsId, List *tokennames) list = (LexDescr *) DatumGetPointer(OidFunctionCall1(prs->lextypeOid, (Datum) 0)); - i = 0; foreach(tn, tokennames) { String *val = lfirst_node(String, tn); bool found = false; int j; + /* Skip if the token type name is present in the list */ + if (token_type_name_present(strVal(val), result)) + continue; + j = 0; while (list && list[j].lexid) { if (strcmp(strVal(val), list[j].alias) == 0) { - res[i] = list[j].lexid; + TSTokenTypes *ts = (TSTokenTypes *) palloc(sizeof(TSTokenTypes)); + ts->num = list[j].lexid; + ts->name = pstrdup(strVal(val)); + result = lappend(result, ts); found = true; break; } @@ -1240,10 +1277,9 @@ getTokenTypes(Oid prsId, List *tokennames) (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("token type \"%s\" does not exist", strVal(val)))); - i++; } - return res; + return result; } /* @@ -1261,8 +1297,8 @@ MakeConfigurationMapping(AlterTSConfigurationStmt *stmt, int i; int j; Oid prsId; - int *tokens, - ntoken; + List *tokens = NIL; + int ntoken; Oid *dictIds; int ndict; ListCell *c; @@ -1273,15 +1309,17 @@ MakeConfigurationMapping(AlterTSConfigurationStmt *stmt, prsId = tsform->cfgparser; tokens = getTokenTypes(prsId, stmt->tokentype); - ntoken = list_length(stmt->tokentype); + ntoken = list_length(tokens); if (stmt->override) { /* * delete maps for tokens if they exist and command was ALTER */ - for (i = 0; i < ntoken; i++) + foreach(c, tokens) { + TSTokenTypes *ts = (TSTokenTypes *) lfirst(c); + ScanKeyInit(&skey[0], Anum_pg_ts_config_map_mapcfg, BTEqualStrategyNumber, F_OIDEQ, @@ -1289,7 +1327,7 @@ MakeConfigurationMapping(AlterTSConfigurationStmt *stmt, ScanKeyInit(&skey[1], Anum_pg_ts_config_map_maptokentype, BTEqualStrategyNumber, F_INT4EQ, - Int32GetDatum(tokens[i])); + Int32GetDatum(ts->num)); scan = systable_beginscan(relMap, TSConfigMapIndexId, true, NULL, 2, skey); @@ -1346,9 +1384,11 @@ MakeConfigurationMapping(AlterTSConfigurationStmt *stmt, { bool tokmatch = false; - for (j = 0; j < ntoken; j++) + foreach(c, tokens) { - if (cfgmap->maptokentype == tokens[j]) + TSTokenTypes *ts = (TSTokenTypes *) lfirst(c); + + if (cfgmap->maptokentype == ts->num) { tokmatch = true; break; @@ -1401,8 +1441,10 @@ MakeConfigurationMapping(AlterTSConfigurationStmt *stmt, /* * Insertion of new entries */ - for (i = 0; i < ntoken; i++) + foreach(c, tokens) { + TSTokenTypes *ts = (TSTokenTypes *) lfirst(c); + for (j = 0; j < ndict; j++) { ExecClearTuple(slot[slotCount]); @@ -1411,7 +1453,7 @@ MakeConfigurationMapping(AlterTSConfigurationStmt *stmt, slot[slotCount]->tts_tupleDescriptor->natts * sizeof(bool)); slot[slotCount]->tts_values[Anum_pg_ts_config_map_mapcfg - 1] = ObjectIdGetDatum(cfgId); - slot[slotCount]->tts_values[Anum_pg_ts_config_map_maptokentype - 1] = Int32GetDatum(tokens[i]); + slot[slotCount]->tts_values[Anum_pg_ts_config_map_maptokentype - 1] = Int32GetDatum(ts->num); slot[slotCount]->tts_values[Anum_pg_ts_config_map_mapseqno - 1] = Int32GetDatum(j + 1); slot[slotCount]->tts_values[Anum_pg_ts_config_map_mapdict - 1] = ObjectIdGetDatum(dictIds[j]); @@ -1455,9 +1497,8 @@ DropConfigurationMapping(AlterTSConfigurationStmt *stmt, ScanKeyData skey[2]; SysScanDesc scan; HeapTuple maptup; - int i; Oid prsId; - int *tokens; + List *tokens = NIL; ListCell *c; tsform = (Form_pg_ts_config) GETSTRUCT(tup); @@ -1466,10 +1507,9 @@ DropConfigurationMapping(AlterTSConfigurationStmt *stmt, tokens = getTokenTypes(prsId, stmt->tokentype); - i = 0; - foreach(c, stmt->tokentype) + foreach(c, tokens) { - String *val = lfirst_node(String, c); + TSTokenTypes *ts = (TSTokenTypes *) lfirst(c); bool found = false; ScanKeyInit(&skey[0], @@ -1479,7 +1519,7 @@ DropConfigurationMapping(AlterTSConfigurationStmt *stmt, ScanKeyInit(&skey[1], Anum_pg_ts_config_map_maptokentype, BTEqualStrategyNumber, F_INT4EQ, - Int32GetDatum(tokens[i])); + Int32GetDatum(ts->num)); scan = systable_beginscan(relMap, TSConfigMapIndexId, true, NULL, 2, skey); @@ -1499,17 +1539,15 @@ DropConfigurationMapping(AlterTSConfigurationStmt *stmt, ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("mapping for token type \"%s\" does not exist", - strVal(val)))); + ts->name))); } else { ereport(NOTICE, (errmsg("mapping for token type \"%s\" does not exist, skipping", - strVal(val)))); + ts->name))); } } - - i++; } EventTriggerCollectAlterTSConfig(stmt, cfgId, NULL, 0); diff --git a/src/test/regress/expected/tsdicts.out b/src/test/regress/expected/tsdicts.out index c804293142..94aed1164b 100644 --- a/src/test/regress/expected/tsdicts.out +++ b/src/test/regress/expected/tsdicts.out @@ -687,3 +687,6 @@ CREATE TEXT SEARCH DICTIONARY tsdict_case "AffFile" = ispell_sample ); ERROR: unrecognized Ispell parameter: "DictFile" +-- test duplicate token type name +CREATE TEXT SEARCH CONFIGURATION dup_ispell_tst (COPY=english); +ALTER TEXT SEARCH CONFIGURATION dup_ispell_tst ALTER MAPPING FOR word, word WITH ispell; diff --git a/src/test/regress/sql/tsdicts.sql b/src/test/regress/sql/tsdicts.sql index ddc6c7f445..88db4f4516 100644 --- a/src/test/regress/sql/tsdicts.sql +++ b/src/test/regress/sql/tsdicts.sql @@ -251,3 +251,8 @@ CREATE TEXT SEARCH DICTIONARY tsdict_case "DictFile" = ispell_sample, "AffFile" = ispell_sample ); + +-- test duplicate token type name +CREATE TEXT SEARCH CONFIGURATION dup_ispell_tst (COPY=english); + +ALTER TEXT SEARCH CONFIGURATION dup_ispell_tst ALTER MAPPING FOR word, word WITH ispell; -- 2.25.1