LCOV - code coverage report
Current view: top level - src/backend/commands - tsearchcmds.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13beta1 Lines: 582 691 84.2 %
Date: 2020-06-01 10:07:15 Functions: 23 24 95.8 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * tsearchcmds.c
       4             :  *
       5             :  *    Routines for tsearch manipulation commands
       6             :  *
       7             :  * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
       8             :  * Portions Copyright (c) 1994, Regents of the University of California
       9             :  *
      10             :  *
      11             :  * IDENTIFICATION
      12             :  *    src/backend/commands/tsearchcmds.c
      13             :  *
      14             :  *-------------------------------------------------------------------------
      15             :  */
      16             : #include "postgres.h"
      17             : 
      18             : #include <ctype.h>
      19             : 
      20             : #include "access/genam.h"
      21             : #include "access/htup_details.h"
      22             : #include "access/table.h"
      23             : #include "access/xact.h"
      24             : #include "catalog/catalog.h"
      25             : #include "catalog/dependency.h"
      26             : #include "catalog/indexing.h"
      27             : #include "catalog/objectaccess.h"
      28             : #include "catalog/pg_namespace.h"
      29             : #include "catalog/pg_proc.h"
      30             : #include "catalog/pg_ts_config.h"
      31             : #include "catalog/pg_ts_config_map.h"
      32             : #include "catalog/pg_ts_dict.h"
      33             : #include "catalog/pg_ts_parser.h"
      34             : #include "catalog/pg_ts_template.h"
      35             : #include "catalog/pg_type.h"
      36             : #include "commands/alter.h"
      37             : #include "commands/defrem.h"
      38             : #include "commands/event_trigger.h"
      39             : #include "common/string.h"
      40             : #include "miscadmin.h"
      41             : #include "nodes/makefuncs.h"
      42             : #include "parser/parse_func.h"
      43             : #include "tsearch/ts_cache.h"
      44             : #include "tsearch/ts_utils.h"
      45             : #include "utils/builtins.h"
      46             : #include "utils/fmgroids.h"
      47             : #include "utils/lsyscache.h"
      48             : #include "utils/rel.h"
      49             : #include "utils/syscache.h"
      50             : 
      51             : 
      52             : static void MakeConfigurationMapping(AlterTSConfigurationStmt *stmt,
      53             :                                      HeapTuple tup, Relation relMap);
      54             : static void DropConfigurationMapping(AlterTSConfigurationStmt *stmt,
      55             :                                      HeapTuple tup, Relation relMap);
      56             : static DefElem *buildDefItem(const char *name, const char *val,
      57             :                              bool was_quoted);
      58             : 
      59             : 
      60             : /* --------------------- TS Parser commands ------------------------ */
      61             : 
      62             : /*
      63             :  * lookup a parser support function and return its OID (as a Datum)
      64             :  *
      65             :  * attnum is the pg_ts_parser column the function will go into
      66             :  */
      67             : static Datum
      68          98 : get_ts_parser_func(DefElem *defel, int attnum)
      69             : {
      70          98 :     List       *funcName = defGetQualifiedName(defel);
      71             :     Oid         typeId[3];
      72             :     Oid         retTypeId;
      73             :     int         nargs;
      74             :     Oid         procOid;
      75             : 
      76          98 :     retTypeId = INTERNALOID;    /* correct for most */
      77          98 :     typeId[0] = INTERNALOID;
      78          98 :     switch (attnum)
      79             :     {
      80          24 :         case Anum_pg_ts_parser_prsstart:
      81          24 :             nargs = 2;
      82          24 :             typeId[1] = INT4OID;
      83          24 :             break;
      84          24 :         case Anum_pg_ts_parser_prstoken:
      85          24 :             nargs = 3;
      86          24 :             typeId[1] = INTERNALOID;
      87          24 :             typeId[2] = INTERNALOID;
      88          24 :             break;
      89          24 :         case Anum_pg_ts_parser_prsend:
      90          24 :             nargs = 1;
      91          24 :             retTypeId = VOIDOID;
      92          24 :             break;
      93           2 :         case Anum_pg_ts_parser_prsheadline:
      94           2 :             nargs = 3;
      95           2 :             typeId[1] = INTERNALOID;
      96           2 :             typeId[2] = TSQUERYOID;
      97           2 :             break;
      98          24 :         case Anum_pg_ts_parser_prslextype:
      99          24 :             nargs = 1;
     100             : 
     101             :             /*
     102             :              * Note: because the lextype method returns type internal, it must
     103             :              * have an internal-type argument for security reasons.  The
     104             :              * argument is not actually used, but is just passed as a zero.
     105             :              */
     106          24 :             break;
     107           0 :         default:
     108             :             /* should not be here */
     109           0 :             elog(ERROR, "unrecognized attribute for text search parser: %d",
     110             :                  attnum);
     111             :             nargs = 0;          /* keep compiler quiet */
     112             :     }
     113             : 
     114          98 :     procOid = LookupFuncName(funcName, nargs, typeId, false);
     115          98 :     if (get_func_rettype(procOid) != retTypeId)
     116           0 :         ereport(ERROR,
     117             :                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     118             :                  errmsg("function %s should return type %s",
     119             :                         func_signature_string(funcName, nargs, NIL, typeId),
     120             :                         format_type_be(retTypeId))));
     121             : 
     122          98 :     return ObjectIdGetDatum(procOid);
     123             : }
     124             : 
     125             : /*
     126             :  * make pg_depend entries for a new pg_ts_parser entry
     127             :  *
     128             :  * Return value is the address of said new entry.
     129             :  */
     130             : static ObjectAddress
     131          24 : makeParserDependencies(HeapTuple tuple)
     132             : {
     133          24 :     Form_pg_ts_parser prs = (Form_pg_ts_parser) GETSTRUCT(tuple);
     134             :     ObjectAddress myself,
     135             :                 referenced;
     136             : 
     137          24 :     myself.classId = TSParserRelationId;
     138          24 :     myself.objectId = prs->oid;
     139          24 :     myself.objectSubId = 0;
     140             : 
     141             :     /* dependency on namespace */
     142          24 :     referenced.classId = NamespaceRelationId;
     143          24 :     referenced.objectId = prs->prsnamespace;
     144          24 :     referenced.objectSubId = 0;
     145          24 :     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     146             : 
     147             :     /* dependency on extension */
     148          24 :     recordDependencyOnCurrentExtension(&myself, false);
     149             : 
     150             :     /* dependencies on functions */
     151          24 :     referenced.classId = ProcedureRelationId;
     152          24 :     referenced.objectSubId = 0;
     153             : 
     154          24 :     referenced.objectId = prs->prsstart;
     155          24 :     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     156             : 
     157          24 :     referenced.objectId = prs->prstoken;
     158          24 :     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     159             : 
     160          24 :     referenced.objectId = prs->prsend;
     161          24 :     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     162             : 
     163          24 :     referenced.objectId = prs->prslextype;
     164          24 :     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     165             : 
     166          24 :     if (OidIsValid(prs->prsheadline))
     167             :     {
     168           2 :         referenced.objectId = prs->prsheadline;
     169           2 :         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     170             :     }
     171             : 
     172          24 :     return myself;
     173             : }
     174             : 
     175             : /*
     176             :  * CREATE TEXT SEARCH PARSER
     177             :  */
     178             : ObjectAddress
     179          28 : DefineTSParser(List *names, List *parameters)
     180             : {
     181             :     char       *prsname;
     182             :     ListCell   *pl;
     183             :     Relation    prsRel;
     184             :     HeapTuple   tup;
     185             :     Datum       values[Natts_pg_ts_parser];
     186             :     bool        nulls[Natts_pg_ts_parser];
     187             :     NameData    pname;
     188             :     Oid         prsOid;
     189             :     Oid         namespaceoid;
     190             :     ObjectAddress address;
     191             : 
     192          28 :     if (!superuser())
     193           0 :         ereport(ERROR,
     194             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     195             :                  errmsg("must be superuser to create text search parsers")));
     196             : 
     197          28 :     prsRel = table_open(TSParserRelationId, RowExclusiveLock);
     198             : 
     199             :     /* Convert list of names to a name and namespace */
     200          28 :     namespaceoid = QualifiedNameGetCreationNamespace(names, &prsname);
     201             : 
     202             :     /* initialize tuple fields with name/namespace */
     203          28 :     memset(values, 0, sizeof(values));
     204          28 :     memset(nulls, false, sizeof(nulls));
     205             : 
     206          28 :     prsOid = GetNewOidWithIndex(prsRel, TSParserOidIndexId,
     207             :                                 Anum_pg_ts_parser_oid);
     208          28 :     values[Anum_pg_ts_parser_oid - 1] = ObjectIdGetDatum(prsOid);
     209          28 :     namestrcpy(&pname, prsname);
     210          28 :     values[Anum_pg_ts_parser_prsname - 1] = NameGetDatum(&pname);
     211          28 :     values[Anum_pg_ts_parser_prsnamespace - 1] = ObjectIdGetDatum(namespaceoid);
     212             : 
     213             :     /*
     214             :      * loop over the definition list and extract the information we need.
     215             :      */
     216         126 :     foreach(pl, parameters)
     217             :     {
     218         102 :         DefElem    *defel = (DefElem *) lfirst(pl);
     219             : 
     220         102 :         if (strcmp(defel->defname, "start") == 0)
     221             :         {
     222          24 :             values[Anum_pg_ts_parser_prsstart - 1] =
     223          24 :                 get_ts_parser_func(defel, Anum_pg_ts_parser_prsstart);
     224             :         }
     225          78 :         else if (strcmp(defel->defname, "gettoken") == 0)
     226             :         {
     227          24 :             values[Anum_pg_ts_parser_prstoken - 1] =
     228          24 :                 get_ts_parser_func(defel, Anum_pg_ts_parser_prstoken);
     229             :         }
     230          54 :         else if (strcmp(defel->defname, "end") == 0)
     231             :         {
     232          24 :             values[Anum_pg_ts_parser_prsend - 1] =
     233          24 :                 get_ts_parser_func(defel, Anum_pg_ts_parser_prsend);
     234             :         }
     235          30 :         else if (strcmp(defel->defname, "headline") == 0)
     236             :         {
     237           2 :             values[Anum_pg_ts_parser_prsheadline - 1] =
     238           2 :                 get_ts_parser_func(defel, Anum_pg_ts_parser_prsheadline);
     239             :         }
     240          28 :         else if (strcmp(defel->defname, "lextypes") == 0)
     241             :         {
     242          24 :             values[Anum_pg_ts_parser_prslextype - 1] =
     243          24 :                 get_ts_parser_func(defel, Anum_pg_ts_parser_prslextype);
     244             :         }
     245             :         else
     246           4 :             ereport(ERROR,
     247             :                     (errcode(ERRCODE_SYNTAX_ERROR),
     248             :                      errmsg("text search parser parameter \"%s\" not recognized",
     249             :                             defel->defname)));
     250             :     }
     251             : 
     252             :     /*
     253             :      * Validation
     254             :      */
     255          24 :     if (!OidIsValid(DatumGetObjectId(values[Anum_pg_ts_parser_prsstart - 1])))
     256           0 :         ereport(ERROR,
     257             :                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     258             :                  errmsg("text search parser start method is required")));
     259             : 
     260          24 :     if (!OidIsValid(DatumGetObjectId(values[Anum_pg_ts_parser_prstoken - 1])))
     261           0 :         ereport(ERROR,
     262             :                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     263             :                  errmsg("text search parser gettoken method is required")));
     264             : 
     265          24 :     if (!OidIsValid(DatumGetObjectId(values[Anum_pg_ts_parser_prsend - 1])))
     266           0 :         ereport(ERROR,
     267             :                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     268             :                  errmsg("text search parser end method is required")));
     269             : 
     270          24 :     if (!OidIsValid(DatumGetObjectId(values[Anum_pg_ts_parser_prslextype - 1])))
     271           0 :         ereport(ERROR,
     272             :                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     273             :                  errmsg("text search parser lextypes method is required")));
     274             : 
     275             :     /*
     276             :      * Looks good, insert
     277             :      */
     278          24 :     tup = heap_form_tuple(prsRel->rd_att, values, nulls);
     279             : 
     280          24 :     CatalogTupleInsert(prsRel, tup);
     281             : 
     282          24 :     address = makeParserDependencies(tup);
     283             : 
     284             :     /* Post creation hook for new text search parser */
     285          24 :     InvokeObjectPostCreateHook(TSParserRelationId, prsOid, 0);
     286             : 
     287          24 :     heap_freetuple(tup);
     288             : 
     289          24 :     table_close(prsRel, RowExclusiveLock);
     290             : 
     291          24 :     return address;
     292             : }
     293             : 
     294             : /*
     295             :  * Guts of TS parser deletion.
     296             :  */
     297             : void
     298          20 : RemoveTSParserById(Oid prsId)
     299             : {
     300             :     Relation    relation;
     301             :     HeapTuple   tup;
     302             : 
     303          20 :     relation = table_open(TSParserRelationId, RowExclusiveLock);
     304             : 
     305          20 :     tup = SearchSysCache1(TSPARSEROID, ObjectIdGetDatum(prsId));
     306             : 
     307          20 :     if (!HeapTupleIsValid(tup))
     308           0 :         elog(ERROR, "cache lookup failed for text search parser %u", prsId);
     309             : 
     310          20 :     CatalogTupleDelete(relation, &tup->t_self);
     311             : 
     312          20 :     ReleaseSysCache(tup);
     313             : 
     314          20 :     table_close(relation, RowExclusiveLock);
     315          20 : }
     316             : 
     317             : /* ---------------------- TS Dictionary commands -----------------------*/
     318             : 
     319             : /*
     320             :  * make pg_depend entries for a new pg_ts_dict entry
     321             :  *
     322             :  * Return value is address of the new entry
     323             :  */
     324             : static ObjectAddress
     325        7966 : makeDictionaryDependencies(HeapTuple tuple)
     326             : {
     327        7966 :     Form_pg_ts_dict dict = (Form_pg_ts_dict) GETSTRUCT(tuple);
     328             :     ObjectAddress myself,
     329             :                 referenced;
     330             : 
     331        7966 :     myself.classId = TSDictionaryRelationId;
     332        7966 :     myself.objectId = dict->oid;
     333        7966 :     myself.objectSubId = 0;
     334             : 
     335             :     /* dependency on namespace */
     336        7966 :     referenced.classId = NamespaceRelationId;
     337        7966 :     referenced.objectId = dict->dictnamespace;
     338        7966 :     referenced.objectSubId = 0;
     339        7966 :     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     340             : 
     341             :     /* dependency on owner */
     342        7966 :     recordDependencyOnOwner(myself.classId, myself.objectId, dict->dictowner);
     343             : 
     344             :     /* dependency on extension */
     345        7966 :     recordDependencyOnCurrentExtension(&myself, false);
     346             : 
     347             :     /* dependency on template */
     348        7966 :     referenced.classId = TSTemplateRelationId;
     349        7966 :     referenced.objectId = dict->dicttemplate;
     350        7966 :     referenced.objectSubId = 0;
     351        7966 :     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     352             : 
     353        7966 :     return myself;
     354             : }
     355             : 
     356             : /*
     357             :  * verify that a template's init method accepts a proposed option list
     358             :  */
     359             : static void
     360        8016 : verify_dictoptions(Oid tmplId, List *dictoptions)
     361             : {
     362             :     HeapTuple   tup;
     363             :     Form_pg_ts_template tform;
     364             :     Oid         initmethod;
     365             : 
     366             :     /*
     367             :      * Suppress this test when running in a standalone backend.  This is a
     368             :      * hack to allow initdb to create prefab dictionaries that might not
     369             :      * actually be usable in template1's encoding (due to using external files
     370             :      * that can't be translated into template1's encoding).  We want to create
     371             :      * them anyway, since they might be usable later in other databases.
     372             :      */
     373        8016 :     if (!IsUnderPostmaster)
     374        7876 :         return;
     375             : 
     376         140 :     tup = SearchSysCache1(TSTEMPLATEOID, ObjectIdGetDatum(tmplId));
     377         140 :     if (!HeapTupleIsValid(tup)) /* should not happen */
     378           0 :         elog(ERROR, "cache lookup failed for text search template %u",
     379             :              tmplId);
     380         140 :     tform = (Form_pg_ts_template) GETSTRUCT(tup);
     381             : 
     382         140 :     initmethod = tform->tmplinit;
     383             : 
     384         140 :     if (!OidIsValid(initmethod))
     385             :     {
     386             :         /* If there is no init method, disallow any options */
     387           0 :         if (dictoptions)
     388           0 :             ereport(ERROR,
     389             :                     (errcode(ERRCODE_SYNTAX_ERROR),
     390             :                      errmsg("text search template \"%s\" does not accept options",
     391             :                             NameStr(tform->tmplname))));
     392             :     }
     393             :     else
     394             :     {
     395             :         /*
     396             :          * Copy the options just in case init method thinks it can scribble on
     397             :          * them ...
     398             :          */
     399         140 :         dictoptions = copyObject(dictoptions);
     400             : 
     401             :         /*
     402             :          * Call the init method and see if it complains.  We don't worry about
     403             :          * it leaking memory, since our command will soon be over anyway.
     404             :          */
     405         140 :         (void) OidFunctionCall1(initmethod, PointerGetDatum(dictoptions));
     406             :     }
     407             : 
     408         118 :     ReleaseSysCache(tup);
     409             : }
     410             : 
     411             : /*
     412             :  * CREATE TEXT SEARCH DICTIONARY
     413             :  */
     414             : ObjectAddress
     415        7982 : DefineTSDictionary(List *names, List *parameters)
     416             : {
     417             :     ListCell   *pl;
     418             :     Relation    dictRel;
     419             :     HeapTuple   tup;
     420             :     Datum       values[Natts_pg_ts_dict];
     421             :     bool        nulls[Natts_pg_ts_dict];
     422             :     NameData    dname;
     423        7982 :     Oid         templId = InvalidOid;
     424        7982 :     List       *dictoptions = NIL;
     425             :     Oid         dictOid;
     426             :     Oid         namespaceoid;
     427             :     AclResult   aclresult;
     428             :     char       *dictname;
     429             :     ObjectAddress address;
     430             : 
     431             :     /* Convert list of names to a name and namespace */
     432        7982 :     namespaceoid = QualifiedNameGetCreationNamespace(names, &dictname);
     433             : 
     434             :     /* Check we have creation rights in target namespace */
     435        7982 :     aclresult = pg_namespace_aclcheck(namespaceoid, GetUserId(), ACL_CREATE);
     436        7982 :     if (aclresult != ACLCHECK_OK)
     437           0 :         aclcheck_error(aclresult, OBJECT_SCHEMA,
     438           0 :                        get_namespace_name(namespaceoid));
     439             : 
     440             :     /*
     441             :      * loop over the definition list and extract the information we need.
     442             :      */
     443       29356 :     foreach(pl, parameters)
     444             :     {
     445       21374 :         DefElem    *defel = (DefElem *) lfirst(pl);
     446             : 
     447       21374 :         if (strcmp(defel->defname, "template") == 0)
     448             :         {
     449        7982 :             templId = get_ts_template_oid(defGetQualifiedName(defel), false);
     450             :         }
     451             :         else
     452             :         {
     453             :             /* Assume it's an option for the dictionary itself */
     454       13392 :             dictoptions = lappend(dictoptions, defel);
     455             :         }
     456             :     }
     457             : 
     458             :     /*
     459             :      * Validation
     460             :      */
     461        7982 :     if (!OidIsValid(templId))
     462           0 :         ereport(ERROR,
     463             :                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     464             :                  errmsg("text search template is required")));
     465             : 
     466        7982 :     verify_dictoptions(templId, dictoptions);
     467             : 
     468             : 
     469        7966 :     dictRel = table_open(TSDictionaryRelationId, RowExclusiveLock);
     470             : 
     471             :     /*
     472             :      * Looks good, insert
     473             :      */
     474        7966 :     memset(values, 0, sizeof(values));
     475        7966 :     memset(nulls, false, sizeof(nulls));
     476             : 
     477        7966 :     dictOid = GetNewOidWithIndex(dictRel, TSDictionaryOidIndexId,
     478             :                                  Anum_pg_ts_dict_oid);
     479        7966 :     values[Anum_pg_ts_dict_oid - 1] = ObjectIdGetDatum(dictOid);
     480        7966 :     namestrcpy(&dname, dictname);
     481        7966 :     values[Anum_pg_ts_dict_dictname - 1] = NameGetDatum(&dname);
     482        7966 :     values[Anum_pg_ts_dict_dictnamespace - 1] = ObjectIdGetDatum(namespaceoid);
     483        7966 :     values[Anum_pg_ts_dict_dictowner - 1] = ObjectIdGetDatum(GetUserId());
     484        7966 :     values[Anum_pg_ts_dict_dicttemplate - 1] = ObjectIdGetDatum(templId);
     485        7966 :     if (dictoptions)
     486        7936 :         values[Anum_pg_ts_dict_dictinitoption - 1] =
     487        7936 :             PointerGetDatum(serialize_deflist(dictoptions));
     488             :     else
     489          30 :         nulls[Anum_pg_ts_dict_dictinitoption - 1] = true;
     490             : 
     491        7966 :     tup = heap_form_tuple(dictRel->rd_att, values, nulls);
     492             : 
     493        7966 :     CatalogTupleInsert(dictRel, tup);
     494             : 
     495        7966 :     address = makeDictionaryDependencies(tup);
     496             : 
     497             :     /* Post creation hook for new text search dictionary */
     498        7966 :     InvokeObjectPostCreateHook(TSDictionaryRelationId, dictOid, 0);
     499             : 
     500        7966 :     heap_freetuple(tup);
     501             : 
     502        7966 :     table_close(dictRel, RowExclusiveLock);
     503             : 
     504        7966 :     return address;
     505             : }
     506             : 
     507             : /*
     508             :  * Guts of TS dictionary deletion.
     509             :  */
     510             : void
     511          28 : RemoveTSDictionaryById(Oid dictId)
     512             : {
     513             :     Relation    relation;
     514             :     HeapTuple   tup;
     515             : 
     516          28 :     relation = table_open(TSDictionaryRelationId, RowExclusiveLock);
     517             : 
     518          28 :     tup = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(dictId));
     519             : 
     520          28 :     if (!HeapTupleIsValid(tup))
     521           0 :         elog(ERROR, "cache lookup failed for text search dictionary %u",
     522             :              dictId);
     523             : 
     524          28 :     CatalogTupleDelete(relation, &tup->t_self);
     525             : 
     526          28 :     ReleaseSysCache(tup);
     527             : 
     528          28 :     table_close(relation, RowExclusiveLock);
     529          28 : }
     530             : 
     531             : /*
     532             :  * ALTER TEXT SEARCH DICTIONARY
     533             :  */
     534             : ObjectAddress
     535          34 : AlterTSDictionary(AlterTSDictionaryStmt *stmt)
     536             : {
     537             :     HeapTuple   tup,
     538             :                 newtup;
     539             :     Relation    rel;
     540             :     Oid         dictId;
     541             :     ListCell   *pl;
     542             :     List       *dictoptions;
     543             :     Datum       opt;
     544             :     bool        isnull;
     545             :     Datum       repl_val[Natts_pg_ts_dict];
     546             :     bool        repl_null[Natts_pg_ts_dict];
     547             :     bool        repl_repl[Natts_pg_ts_dict];
     548             :     ObjectAddress address;
     549             : 
     550          34 :     dictId = get_ts_dict_oid(stmt->dictname, false);
     551             : 
     552          34 :     rel = table_open(TSDictionaryRelationId, RowExclusiveLock);
     553             : 
     554          34 :     tup = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(dictId));
     555             : 
     556          34 :     if (!HeapTupleIsValid(tup))
     557           0 :         elog(ERROR, "cache lookup failed for text search dictionary %u",
     558             :              dictId);
     559             : 
     560             :     /* must be owner */
     561          34 :     if (!pg_ts_dict_ownercheck(dictId, GetUserId()))
     562           0 :         aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_TSDICTIONARY,
     563           0 :                        NameListToString(stmt->dictname));
     564             : 
     565             :     /* deserialize the existing set of options */
     566          34 :     opt = SysCacheGetAttr(TSDICTOID, tup,
     567             :                           Anum_pg_ts_dict_dictinitoption,
     568             :                           &isnull);
     569          34 :     if (isnull)
     570           6 :         dictoptions = NIL;
     571             :     else
     572          28 :         dictoptions = deserialize_deflist(opt);
     573             : 
     574             :     /*
     575             :      * Modify the options list as per specified changes
     576             :      */
     577         124 :     foreach(pl, stmt->options)
     578             :     {
     579          90 :         DefElem    *defel = (DefElem *) lfirst(pl);
     580             :         ListCell   *cell;
     581             : 
     582             :         /*
     583             :          * Remove any matches ...
     584             :          */
     585         436 :         foreach(cell, dictoptions)
     586             :         {
     587         346 :             DefElem    *oldel = (DefElem *) lfirst(cell);
     588             : 
     589         346 :             if (strcmp(oldel->defname, defel->defname) == 0)
     590          68 :                 dictoptions = foreach_delete_current(dictoptions, cell);
     591             :         }
     592             : 
     593             :         /*
     594             :          * and add new value if it's got one
     595             :          */
     596          90 :         if (defel->arg)
     597          90 :             dictoptions = lappend(dictoptions, defel);
     598             :     }
     599             : 
     600             :     /*
     601             :      * Validate
     602             :      */
     603          34 :     verify_dictoptions(((Form_pg_ts_dict) GETSTRUCT(tup))->dicttemplate,
     604             :                        dictoptions);
     605             : 
     606             :     /*
     607             :      * Looks good, update
     608             :      */
     609          28 :     memset(repl_val, 0, sizeof(repl_val));
     610          28 :     memset(repl_null, false, sizeof(repl_null));
     611          28 :     memset(repl_repl, false, sizeof(repl_repl));
     612             : 
     613          28 :     if (dictoptions)
     614          28 :         repl_val[Anum_pg_ts_dict_dictinitoption - 1] =
     615          28 :             PointerGetDatum(serialize_deflist(dictoptions));
     616             :     else
     617           0 :         repl_null[Anum_pg_ts_dict_dictinitoption - 1] = true;
     618          28 :     repl_repl[Anum_pg_ts_dict_dictinitoption - 1] = true;
     619             : 
     620          28 :     newtup = heap_modify_tuple(tup, RelationGetDescr(rel),
     621             :                                repl_val, repl_null, repl_repl);
     622             : 
     623          28 :     CatalogTupleUpdate(rel, &newtup->t_self, newtup);
     624             : 
     625          28 :     InvokeObjectPostAlterHook(TSDictionaryRelationId, dictId, 0);
     626             : 
     627          28 :     ObjectAddressSet(address, TSDictionaryRelationId, dictId);
     628             : 
     629             :     /*
     630             :      * NOTE: because we only support altering the options, not the template,
     631             :      * there is no need to update dependencies.  This might have to change if
     632             :      * the options ever reference inside-the-database objects.
     633             :      */
     634             : 
     635          28 :     heap_freetuple(newtup);
     636          28 :     ReleaseSysCache(tup);
     637             : 
     638          28 :     table_close(rel, RowExclusiveLock);
     639             : 
     640          28 :     return address;
     641             : }
     642             : 
     643             : /* ---------------------- TS Template commands -----------------------*/
     644             : 
     645             : /*
     646             :  * lookup a template support function and return its OID (as a Datum)
     647             :  *
     648             :  * attnum is the pg_ts_template column the function will go into
     649             :  */
     650             : static Datum
     651         754 : get_ts_template_func(DefElem *defel, int attnum)
     652             : {
     653         754 :     List       *funcName = defGetQualifiedName(defel);
     654             :     Oid         typeId[4];
     655             :     Oid         retTypeId;
     656             :     int         nargs;
     657             :     Oid         procOid;
     658             : 
     659         754 :     retTypeId = INTERNALOID;
     660         754 :     typeId[0] = INTERNALOID;
     661         754 :     typeId[1] = INTERNALOID;
     662         754 :     typeId[2] = INTERNALOID;
     663         754 :     typeId[3] = INTERNALOID;
     664         754 :     switch (attnum)
     665             :     {
     666         368 :         case Anum_pg_ts_template_tmplinit:
     667         368 :             nargs = 1;
     668         368 :             break;
     669         386 :         case Anum_pg_ts_template_tmpllexize:
     670         386 :             nargs = 4;
     671         386 :             break;
     672           0 :         default:
     673             :             /* should not be here */
     674           0 :             elog(ERROR, "unrecognized attribute for text search template: %d",
     675             :                  attnum);
     676             :             nargs = 0;          /* keep compiler quiet */
     677             :     }
     678             : 
     679         754 :     procOid = LookupFuncName(funcName, nargs, typeId, false);
     680         754 :     if (get_func_rettype(procOid) != retTypeId)
     681           0 :         ereport(ERROR,
     682             :                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     683             :                  errmsg("function %s should return type %s",
     684             :                         func_signature_string(funcName, nargs, NIL, typeId),
     685             :                         format_type_be(retTypeId))));
     686             : 
     687         754 :     return ObjectIdGetDatum(procOid);
     688             : }
     689             : 
     690             : /*
     691             :  * make pg_depend entries for a new pg_ts_template entry
     692             :  */
     693             : static ObjectAddress
     694         386 : makeTSTemplateDependencies(HeapTuple tuple)
     695             : {
     696         386 :     Form_pg_ts_template tmpl = (Form_pg_ts_template) GETSTRUCT(tuple);
     697             :     ObjectAddress myself,
     698             :                 referenced;
     699             : 
     700         386 :     myself.classId = TSTemplateRelationId;
     701         386 :     myself.objectId = tmpl->oid;
     702         386 :     myself.objectSubId = 0;
     703             : 
     704             :     /* dependency on namespace */
     705         386 :     referenced.classId = NamespaceRelationId;
     706         386 :     referenced.objectId = tmpl->tmplnamespace;
     707         386 :     referenced.objectSubId = 0;
     708         386 :     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     709             : 
     710             :     /* dependency on extension */
     711         386 :     recordDependencyOnCurrentExtension(&myself, false);
     712             : 
     713             :     /* dependencies on functions */
     714         386 :     referenced.classId = ProcedureRelationId;
     715         386 :     referenced.objectSubId = 0;
     716             : 
     717         386 :     referenced.objectId = tmpl->tmpllexize;
     718         386 :     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     719             : 
     720         386 :     if (OidIsValid(tmpl->tmplinit))
     721             :     {
     722         368 :         referenced.objectId = tmpl->tmplinit;
     723         368 :         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     724             :     }
     725             : 
     726         386 :     return myself;
     727             : }
     728             : 
     729             : /*
     730             :  * CREATE TEXT SEARCH TEMPLATE
     731             :  */
     732             : ObjectAddress
     733         390 : DefineTSTemplate(List *names, List *parameters)
     734             : {
     735             :     ListCell   *pl;
     736             :     Relation    tmplRel;
     737             :     HeapTuple   tup;
     738             :     Datum       values[Natts_pg_ts_template];
     739             :     bool        nulls[Natts_pg_ts_template];
     740             :     NameData    dname;
     741             :     int         i;
     742             :     Oid         tmplOid;
     743             :     Oid         namespaceoid;
     744             :     char       *tmplname;
     745             :     ObjectAddress address;
     746             : 
     747         390 :     if (!superuser())
     748           0 :         ereport(ERROR,
     749             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     750             :                  errmsg("must be superuser to create text search templates")));
     751             : 
     752             :     /* Convert list of names to a name and namespace */
     753         390 :     namespaceoid = QualifiedNameGetCreationNamespace(names, &tmplname);
     754             : 
     755         390 :     tmplRel = table_open(TSTemplateRelationId, RowExclusiveLock);
     756             : 
     757        2340 :     for (i = 0; i < Natts_pg_ts_template; i++)
     758             :     {
     759        1950 :         nulls[i] = false;
     760        1950 :         values[i] = ObjectIdGetDatum(InvalidOid);
     761             :     }
     762             : 
     763         390 :     tmplOid = GetNewOidWithIndex(tmplRel, TSTemplateOidIndexId,
     764             :                                  Anum_pg_ts_dict_oid);
     765         390 :     values[Anum_pg_ts_template_oid - 1] = ObjectIdGetDatum(tmplOid);
     766         390 :     namestrcpy(&dname, tmplname);
     767         390 :     values[Anum_pg_ts_template_tmplname - 1] = NameGetDatum(&dname);
     768         390 :     values[Anum_pg_ts_template_tmplnamespace - 1] = ObjectIdGetDatum(namespaceoid);
     769             : 
     770             :     /*
     771             :      * loop over the definition list and extract the information we need.
     772             :      */
     773        1144 :     foreach(pl, parameters)
     774             :     {
     775         758 :         DefElem    *defel = (DefElem *) lfirst(pl);
     776             : 
     777         758 :         if (strcmp(defel->defname, "init") == 0)
     778             :         {
     779         368 :             values[Anum_pg_ts_template_tmplinit - 1] =
     780         368 :                 get_ts_template_func(defel, Anum_pg_ts_template_tmplinit);
     781         368 :             nulls[Anum_pg_ts_template_tmplinit - 1] = false;
     782             :         }
     783         390 :         else if (strcmp(defel->defname, "lexize") == 0)
     784             :         {
     785         386 :             values[Anum_pg_ts_template_tmpllexize - 1] =
     786         386 :                 get_ts_template_func(defel, Anum_pg_ts_template_tmpllexize);
     787         386 :             nulls[Anum_pg_ts_template_tmpllexize - 1] = false;
     788             :         }
     789             :         else
     790           4 :             ereport(ERROR,
     791             :                     (errcode(ERRCODE_SYNTAX_ERROR),
     792             :                      errmsg("text search template parameter \"%s\" not recognized",
     793             :                             defel->defname)));
     794             :     }
     795             : 
     796             :     /*
     797             :      * Validation
     798             :      */
     799         386 :     if (!OidIsValid(DatumGetObjectId(values[Anum_pg_ts_template_tmpllexize - 1])))
     800           0 :         ereport(ERROR,
     801             :                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     802             :                  errmsg("text search template lexize method is required")));
     803             : 
     804             :     /*
     805             :      * Looks good, insert
     806             :      */
     807         386 :     tup = heap_form_tuple(tmplRel->rd_att, values, nulls);
     808             : 
     809         386 :     CatalogTupleInsert(tmplRel, tup);
     810             : 
     811         386 :     address = makeTSTemplateDependencies(tup);
     812             : 
     813             :     /* Post creation hook for new text search template */
     814         386 :     InvokeObjectPostCreateHook(TSTemplateRelationId, tmplOid, 0);
     815             : 
     816         386 :     heap_freetuple(tup);
     817             : 
     818         386 :     table_close(tmplRel, RowExclusiveLock);
     819             : 
     820         386 :     return address;
     821             : }
     822             : 
     823             : /*
     824             :  * Guts of TS template deletion.
     825             :  */
     826             : void
     827          20 : RemoveTSTemplateById(Oid tmplId)
     828             : {
     829             :     Relation    relation;
     830             :     HeapTuple   tup;
     831             : 
     832          20 :     relation = table_open(TSTemplateRelationId, RowExclusiveLock);
     833             : 
     834          20 :     tup = SearchSysCache1(TSTEMPLATEOID, ObjectIdGetDatum(tmplId));
     835             : 
     836          20 :     if (!HeapTupleIsValid(tup))
     837           0 :         elog(ERROR, "cache lookup failed for text search template %u",
     838             :              tmplId);
     839             : 
     840          20 :     CatalogTupleDelete(relation, &tup->t_self);
     841             : 
     842          20 :     ReleaseSysCache(tup);
     843             : 
     844          20 :     table_close(relation, RowExclusiveLock);
     845          20 : }
     846             : 
     847             : /* ---------------------- TS Configuration commands -----------------------*/
     848             : 
     849             : /*
     850             :  * Finds syscache tuple of configuration.
     851             :  * Returns NULL if no such cfg.
     852             :  */
     853             : static HeapTuple
     854       23808 : GetTSConfigTuple(List *names)
     855             : {
     856             :     HeapTuple   tup;
     857             :     Oid         cfgId;
     858             : 
     859       23808 :     cfgId = get_ts_config_oid(names, true);
     860       23808 :     if (!OidIsValid(cfgId))
     861           0 :         return NULL;
     862             : 
     863       23808 :     tup = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(cfgId));
     864             : 
     865       23808 :     if (!HeapTupleIsValid(tup)) /* should not happen */
     866           0 :         elog(ERROR, "cache lookup failed for text search configuration %u",
     867             :              cfgId);
     868             : 
     869       23808 :     return tup;
     870             : }
     871             : 
     872             : /*
     873             :  * make pg_depend entries for a new or updated pg_ts_config entry
     874             :  *
     875             :  * Pass opened pg_ts_config_map relation if there might be any config map
     876             :  * entries for the config.
     877             :  */
     878             : static ObjectAddress
     879       31742 : makeConfigurationDependencies(HeapTuple tuple, bool removeOld,
     880             :                               Relation mapRel)
     881             : {
     882       31742 :     Form_pg_ts_config cfg = (Form_pg_ts_config) GETSTRUCT(tuple);
     883             :     ObjectAddresses *addrs;
     884             :     ObjectAddress myself,
     885             :                 referenced;
     886             : 
     887       31742 :     myself.classId = TSConfigRelationId;
     888       31742 :     myself.objectId = cfg->oid;
     889       31742 :     myself.objectSubId = 0;
     890             : 
     891             :     /* for ALTER case, first flush old dependencies, except extension deps */
     892       31742 :     if (removeOld)
     893             :     {
     894       23808 :         deleteDependencyRecordsFor(myself.classId, myself.objectId, true);
     895       23808 :         deleteSharedDependencyRecordsFor(myself.classId, myself.objectId, 0);
     896             :     }
     897             : 
     898             :     /*
     899             :      * We use an ObjectAddresses list to remove possible duplicate
     900             :      * dependencies from the config map info.  The pg_ts_config items
     901             :      * shouldn't be duplicates, but might as well fold them all into one call.
     902             :      */
     903       31742 :     addrs = new_object_addresses();
     904             : 
     905             :     /* dependency on namespace */
     906       31742 :     referenced.classId = NamespaceRelationId;
     907       31742 :     referenced.objectId = cfg->cfgnamespace;
     908       31742 :     referenced.objectSubId = 0;
     909       31742 :     add_exact_object_address(&referenced, addrs);
     910             : 
     911             :     /* dependency on owner */
     912       31742 :     recordDependencyOnOwner(myself.classId, myself.objectId, cfg->cfgowner);
     913             : 
     914             :     /* dependency on extension */
     915       31742 :     recordDependencyOnCurrentExtension(&myself, removeOld);
     916             : 
     917             :     /* dependency on parser */
     918       31742 :     referenced.classId = TSParserRelationId;
     919       31742 :     referenced.objectId = cfg->cfgparser;
     920       31742 :     referenced.objectSubId = 0;
     921       31742 :     add_exact_object_address(&referenced, addrs);
     922             : 
     923             :     /* dependencies on dictionaries listed in config map */
     924       31742 :     if (mapRel)
     925             :     {
     926             :         ScanKeyData skey;
     927             :         SysScanDesc scan;
     928             :         HeapTuple   maptup;
     929             : 
     930             :         /* CCI to ensure we can see effects of caller's changes */
     931       23852 :         CommandCounterIncrement();
     932             : 
     933       23852 :         ScanKeyInit(&skey,
     934             :                     Anum_pg_ts_config_map_mapcfg,
     935             :                     BTEqualStrategyNumber, F_OIDEQ,
     936       23852 :                     ObjectIdGetDatum(myself.objectId));
     937             : 
     938       23852 :         scan = systable_beginscan(mapRel, TSConfigMapIndexId, true,
     939             :                                   NULL, 1, &skey);
     940             : 
     941      405610 :         while (HeapTupleIsValid((maptup = systable_getnext(scan))))
     942             :         {
     943      381758 :             Form_pg_ts_config_map cfgmap = (Form_pg_ts_config_map) GETSTRUCT(maptup);
     944             : 
     945      381758 :             referenced.classId = TSDictionaryRelationId;
     946      381758 :             referenced.objectId = cfgmap->mapdict;
     947      381758 :             referenced.objectSubId = 0;
     948      381758 :             add_exact_object_address(&referenced, addrs);
     949             :         }
     950             : 
     951       23852 :         systable_endscan(scan);
     952             :     }
     953             : 
     954             :     /* Record 'em (this includes duplicate elimination) */
     955       31742 :     record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
     956             : 
     957       31742 :     free_object_addresses(addrs);
     958             : 
     959       31742 :     return myself;
     960             : }
     961             : 
     962             : /*
     963             :  * CREATE TEXT SEARCH CONFIGURATION
     964             :  */
     965             : ObjectAddress
     966        7934 : DefineTSConfiguration(List *names, List *parameters, ObjectAddress *copied)
     967             : {
     968             :     Relation    cfgRel;
     969        7934 :     Relation    mapRel = NULL;
     970             :     HeapTuple   tup;
     971             :     Datum       values[Natts_pg_ts_config];
     972             :     bool        nulls[Natts_pg_ts_config];
     973             :     AclResult   aclresult;
     974             :     Oid         namespaceoid;
     975             :     char       *cfgname;
     976             :     NameData    cname;
     977        7934 :     Oid         sourceOid = InvalidOid;
     978        7934 :     Oid         prsOid = InvalidOid;
     979             :     Oid         cfgOid;
     980             :     ListCell   *pl;
     981             :     ObjectAddress address;
     982             : 
     983             :     /* Convert list of names to a name and namespace */
     984        7934 :     namespaceoid = QualifiedNameGetCreationNamespace(names, &cfgname);
     985             : 
     986             :     /* Check we have creation rights in target namespace */
     987        7934 :     aclresult = pg_namespace_aclcheck(namespaceoid, GetUserId(), ACL_CREATE);
     988        7934 :     if (aclresult != ACLCHECK_OK)
     989           0 :         aclcheck_error(aclresult, OBJECT_SCHEMA,
     990           0 :                        get_namespace_name(namespaceoid));
     991             : 
     992             :     /*
     993             :      * loop over the definition list and extract the information we need.
     994             :      */
     995       15868 :     foreach(pl, parameters)
     996             :     {
     997        7934 :         DefElem    *defel = (DefElem *) lfirst(pl);
     998             : 
     999        7934 :         if (strcmp(defel->defname, "parser") == 0)
    1000        7890 :             prsOid = get_ts_parser_oid(defGetQualifiedName(defel), false);
    1001          44 :         else if (strcmp(defel->defname, "copy") == 0)
    1002          44 :             sourceOid = get_ts_config_oid(defGetQualifiedName(defel), false);
    1003             :         else
    1004           0 :             ereport(ERROR,
    1005             :                     (errcode(ERRCODE_SYNTAX_ERROR),
    1006             :                      errmsg("text search configuration parameter \"%s\" not recognized",
    1007             :                             defel->defname)));
    1008             :     }
    1009             : 
    1010        7934 :     if (OidIsValid(sourceOid) && OidIsValid(prsOid))
    1011           0 :         ereport(ERROR,
    1012             :                 (errcode(ERRCODE_SYNTAX_ERROR),
    1013             :                  errmsg("cannot specify both PARSER and COPY options")));
    1014             : 
    1015             :     /* make copied tsconfig available to callers */
    1016        7934 :     if (copied && OidIsValid(sourceOid))
    1017             :     {
    1018          44 :         ObjectAddressSet(*copied,
    1019             :                          TSConfigRelationId,
    1020             :                          sourceOid);
    1021             :     }
    1022             : 
    1023             :     /*
    1024             :      * Look up source config if given.
    1025             :      */
    1026        7934 :     if (OidIsValid(sourceOid))
    1027             :     {
    1028             :         Form_pg_ts_config cfg;
    1029             : 
    1030          44 :         tup = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(sourceOid));
    1031          44 :         if (!HeapTupleIsValid(tup))
    1032           0 :             elog(ERROR, "cache lookup failed for text search configuration %u",
    1033             :                  sourceOid);
    1034             : 
    1035          44 :         cfg = (Form_pg_ts_config) GETSTRUCT(tup);
    1036             : 
    1037             :         /* use source's parser */
    1038          44 :         prsOid = cfg->cfgparser;
    1039             : 
    1040          44 :         ReleaseSysCache(tup);
    1041             :     }
    1042             : 
    1043             :     /*
    1044             :      * Validation
    1045             :      */
    1046        7934 :     if (!OidIsValid(prsOid))
    1047           0 :         ereport(ERROR,
    1048             :                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1049             :                  errmsg("text search parser is required")));
    1050             : 
    1051        7934 :     cfgRel = table_open(TSConfigRelationId, RowExclusiveLock);
    1052             : 
    1053             :     /*
    1054             :      * Looks good, build tuple and insert
    1055             :      */
    1056        7934 :     memset(values, 0, sizeof(values));
    1057        7934 :     memset(nulls, false, sizeof(nulls));
    1058             : 
    1059        7934 :     cfgOid = GetNewOidWithIndex(cfgRel, TSConfigOidIndexId,
    1060             :                                 Anum_pg_ts_config_oid);
    1061        7934 :     values[Anum_pg_ts_config_oid - 1] = ObjectIdGetDatum(cfgOid);
    1062        7934 :     namestrcpy(&cname, cfgname);
    1063        7934 :     values[Anum_pg_ts_config_cfgname - 1] = NameGetDatum(&cname);
    1064        7934 :     values[Anum_pg_ts_config_cfgnamespace - 1] = ObjectIdGetDatum(namespaceoid);
    1065        7934 :     values[Anum_pg_ts_config_cfgowner - 1] = ObjectIdGetDatum(GetUserId());
    1066        7934 :     values[Anum_pg_ts_config_cfgparser - 1] = ObjectIdGetDatum(prsOid);
    1067             : 
    1068        7934 :     tup = heap_form_tuple(cfgRel->rd_att, values, nulls);
    1069             : 
    1070        7934 :     CatalogTupleInsert(cfgRel, tup);
    1071             : 
    1072        7934 :     if (OidIsValid(sourceOid))
    1073             :     {
    1074             :         /*
    1075             :          * Copy token-dicts map from source config
    1076             :          */
    1077             :         ScanKeyData skey;
    1078             :         SysScanDesc scan;
    1079             :         HeapTuple   maptup;
    1080             : 
    1081          44 :         mapRel = table_open(TSConfigMapRelationId, RowExclusiveLock);
    1082             : 
    1083          44 :         ScanKeyInit(&skey,
    1084             :                     Anum_pg_ts_config_map_mapcfg,
    1085             :                     BTEqualStrategyNumber, F_OIDEQ,
    1086             :                     ObjectIdGetDatum(sourceOid));
    1087             : 
    1088          44 :         scan = systable_beginscan(mapRel, TSConfigMapIndexId, true,
    1089             :                                   NULL, 1, &skey);
    1090             : 
    1091         928 :         while (HeapTupleIsValid((maptup = systable_getnext(scan))))
    1092             :         {
    1093         884 :             Form_pg_ts_config_map cfgmap = (Form_pg_ts_config_map) GETSTRUCT(maptup);
    1094             :             HeapTuple   newmaptup;
    1095             :             Datum       mapvalues[Natts_pg_ts_config_map];
    1096             :             bool        mapnulls[Natts_pg_ts_config_map];
    1097             : 
    1098         884 :             memset(mapvalues, 0, sizeof(mapvalues));
    1099         884 :             memset(mapnulls, false, sizeof(mapnulls));
    1100             : 
    1101         884 :             mapvalues[Anum_pg_ts_config_map_mapcfg - 1] = cfgOid;
    1102         884 :             mapvalues[Anum_pg_ts_config_map_maptokentype - 1] = cfgmap->maptokentype;
    1103         884 :             mapvalues[Anum_pg_ts_config_map_mapseqno - 1] = cfgmap->mapseqno;
    1104         884 :             mapvalues[Anum_pg_ts_config_map_mapdict - 1] = cfgmap->mapdict;
    1105             : 
    1106         884 :             newmaptup = heap_form_tuple(mapRel->rd_att, mapvalues, mapnulls);
    1107             : 
    1108         884 :             CatalogTupleInsert(mapRel, newmaptup);
    1109             : 
    1110         884 :             heap_freetuple(newmaptup);
    1111             :         }
    1112             : 
    1113          44 :         systable_endscan(scan);
    1114             :     }
    1115             : 
    1116        7934 :     address = makeConfigurationDependencies(tup, false, mapRel);
    1117             : 
    1118             :     /* Post creation hook for new text search configuration */
    1119        7934 :     InvokeObjectPostCreateHook(TSConfigRelationId, cfgOid, 0);
    1120             : 
    1121        7934 :     heap_freetuple(tup);
    1122             : 
    1123        7934 :     if (mapRel)
    1124          44 :         table_close(mapRel, RowExclusiveLock);
    1125        7934 :     table_close(cfgRel, RowExclusiveLock);
    1126             : 
    1127        7934 :     return address;
    1128             : }
    1129             : 
    1130             : /*
    1131             :  * Guts of TS configuration deletion.
    1132             :  */
    1133             : void
    1134          28 : RemoveTSConfigurationById(Oid cfgId)
    1135             : {
    1136             :     Relation    relCfg,
    1137             :                 relMap;
    1138             :     HeapTuple   tup;
    1139             :     ScanKeyData skey;
    1140             :     SysScanDesc scan;
    1141             : 
    1142             :     /* Remove the pg_ts_config entry */
    1143          28 :     relCfg = table_open(TSConfigRelationId, RowExclusiveLock);
    1144             : 
    1145          28 :     tup = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(cfgId));
    1146             : 
    1147          28 :     if (!HeapTupleIsValid(tup))
    1148           0 :         elog(ERROR, "cache lookup failed for text search dictionary %u",
    1149             :              cfgId);
    1150             : 
    1151          28 :     CatalogTupleDelete(relCfg, &tup->t_self);
    1152             : 
    1153          28 :     ReleaseSysCache(tup);
    1154             : 
    1155          28 :     table_close(relCfg, RowExclusiveLock);
    1156             : 
    1157             :     /* Remove any pg_ts_config_map entries */
    1158          28 :     relMap = table_open(TSConfigMapRelationId, RowExclusiveLock);
    1159             : 
    1160          28 :     ScanKeyInit(&skey,
    1161             :                 Anum_pg_ts_config_map_mapcfg,
    1162             :                 BTEqualStrategyNumber, F_OIDEQ,
    1163             :                 ObjectIdGetDatum(cfgId));
    1164             : 
    1165          28 :     scan = systable_beginscan(relMap, TSConfigMapIndexId, true,
    1166             :                               NULL, 1, &skey);
    1167             : 
    1168         484 :     while (HeapTupleIsValid((tup = systable_getnext(scan))))
    1169             :     {
    1170         456 :         CatalogTupleDelete(relMap, &tup->t_self);
    1171             :     }
    1172             : 
    1173          28 :     systable_endscan(scan);
    1174             : 
    1175          28 :     table_close(relMap, RowExclusiveLock);
    1176          28 : }
    1177             : 
    1178             : /*
    1179             :  * ALTER TEXT SEARCH CONFIGURATION - main entry point
    1180             :  */
    1181             : ObjectAddress
    1182       23808 : AlterTSConfiguration(AlterTSConfigurationStmt *stmt)
    1183             : {
    1184             :     HeapTuple   tup;
    1185             :     Oid         cfgId;
    1186             :     Relation    relMap;
    1187             :     ObjectAddress address;
    1188             : 
    1189             :     /* Find the configuration */
    1190       23808 :     tup = GetTSConfigTuple(stmt->cfgname);
    1191       23808 :     if (!HeapTupleIsValid(tup))
    1192           0 :         ereport(ERROR,
    1193             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
    1194             :                  errmsg("text search configuration \"%s\" does not exist",
    1195             :                         NameListToString(stmt->cfgname))));
    1196             : 
    1197       23808 :     cfgId = ((Form_pg_ts_config) GETSTRUCT(tup))->oid;
    1198             : 
    1199             :     /* must be owner */
    1200       23808 :     if (!pg_ts_config_ownercheck(cfgId, GetUserId()))
    1201           0 :         aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_TSCONFIGURATION,
    1202           0 :                        NameListToString(stmt->cfgname));
    1203             : 
    1204       23808 :     relMap = table_open(TSConfigMapRelationId, RowExclusiveLock);
    1205             : 
    1206             :     /* Add or drop mappings */
    1207       23808 :     if (stmt->dicts)
    1208       23808 :         MakeConfigurationMapping(stmt, tup, relMap);
    1209           0 :     else if (stmt->tokentype)
    1210           0 :         DropConfigurationMapping(stmt, tup, relMap);
    1211             : 
    1212             :     /* Update dependencies */
    1213       23808 :     makeConfigurationDependencies(tup, true, relMap);
    1214             : 
    1215       23808 :     InvokeObjectPostAlterHook(TSConfigRelationId, cfgId, 0);
    1216             : 
    1217       23808 :     ObjectAddressSet(address, TSConfigRelationId, cfgId);
    1218             : 
    1219       23808 :     table_close(relMap, RowExclusiveLock);
    1220             : 
    1221       23808 :     ReleaseSysCache(tup);
    1222             : 
    1223       23808 :     return address;
    1224             : }
    1225             : 
    1226             : /*
    1227             :  * Translate a list of token type names to an array of token type numbers
    1228             :  */
    1229             : static int *
    1230       23808 : getTokenTypes(Oid prsId, List *tokennames)
    1231             : {
    1232       23808 :     TSParserCacheEntry *prs = lookup_ts_parser_cache(prsId);
    1233             :     LexDescr   *list;
    1234             :     int        *res,
    1235             :                 i,
    1236             :                 ntoken;
    1237             :     ListCell   *tn;
    1238             : 
    1239       23808 :     ntoken = list_length(tokennames);
    1240       23808 :     if (ntoken == 0)
    1241          12 :         return NULL;
    1242       23796 :     res = (int *) palloc(sizeof(int) * ntoken);
    1243             : 
    1244       23796 :     if (!OidIsValid(prs->lextypeOid))
    1245           0 :         elog(ERROR, "method lextype isn't defined for text search parser %u",
    1246             :              prsId);
    1247             : 
    1248             :     /* lextype takes one dummy argument */
    1249       23796 :     list = (LexDescr *) DatumGetPointer(OidFunctionCall1(prs->lextypeOid,
    1250             :                                                          (Datum) 0));
    1251             : 
    1252       23796 :     i = 0;
    1253      173662 :     foreach(tn, tokennames)
    1254             :     {
    1255      149866 :         Value      *val = (Value *) lfirst(tn);
    1256      149866 :         bool        found = false;
    1257             :         int         j;
    1258             : 
    1259      149866 :         j = 0;
    1260     1687782 :         while (list && list[j].lexid)
    1261             :         {
    1262     1687782 :             if (strcmp(strVal(val), list[j].alias) == 0)
    1263             :             {
    1264      149866 :                 res[i] = list[j].lexid;
    1265      149866 :                 found = true;
    1266      149866 :                 break;
    1267             :             }
    1268     1537916 :             j++;
    1269             :         }
    1270      149866 :         if (!found)
    1271           0 :             ereport(ERROR,
    1272             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1273             :                      errmsg("token type \"%s\" does not exist",
    1274             :                             strVal(val))));
    1275      149866 :         i++;
    1276             :     }
    1277             : 
    1278       23796 :     return res;
    1279             : }
    1280             : 
    1281             : /*
    1282             :  * ALTER TEXT SEARCH CONFIGURATION ADD/ALTER MAPPING
    1283             :  */
    1284             : static void
    1285       23808 : MakeConfigurationMapping(AlterTSConfigurationStmt *stmt,
    1286             :                          HeapTuple tup, Relation relMap)
    1287             : {
    1288             :     Form_pg_ts_config tsform;
    1289             :     Oid         cfgId;
    1290             :     ScanKeyData skey[2];
    1291             :     SysScanDesc scan;
    1292             :     HeapTuple   maptup;
    1293             :     int         i;
    1294             :     int         j;
    1295             :     Oid         prsId;
    1296             :     int        *tokens,
    1297             :                 ntoken;
    1298             :     Oid        *dictIds;
    1299             :     int         ndict;
    1300             :     ListCell   *c;
    1301             : 
    1302       23808 :     tsform = (Form_pg_ts_config) GETSTRUCT(tup);
    1303       23808 :     cfgId = tsform->oid;
    1304       23808 :     prsId = tsform->cfgparser;
    1305             : 
    1306       23808 :     tokens = getTokenTypes(prsId, stmt->tokentype);
    1307       23808 :     ntoken = list_length(stmt->tokentype);
    1308             : 
    1309       23808 :     if (stmt->override)
    1310             :     {
    1311             :         /*
    1312             :          * delete maps for tokens if they exist and command was ALTER
    1313             :          */
    1314          82 :         for (i = 0; i < ntoken; i++)
    1315             :         {
    1316          68 :             ScanKeyInit(&skey[0],
    1317             :                         Anum_pg_ts_config_map_mapcfg,
    1318             :                         BTEqualStrategyNumber, F_OIDEQ,
    1319             :                         ObjectIdGetDatum(cfgId));
    1320          68 :             ScanKeyInit(&skey[1],
    1321             :                         Anum_pg_ts_config_map_maptokentype,
    1322             :                         BTEqualStrategyNumber, F_INT4EQ,
    1323          68 :                         Int32GetDatum(tokens[i]));
    1324             : 
    1325          68 :             scan = systable_beginscan(relMap, TSConfigMapIndexId, true,
    1326             :                                       NULL, 2, skey);
    1327             : 
    1328         148 :             while (HeapTupleIsValid((maptup = systable_getnext(scan))))
    1329             :             {
    1330          80 :                 CatalogTupleDelete(relMap, &maptup->t_self);
    1331             :             }
    1332             : 
    1333          68 :             systable_endscan(scan);
    1334             :         }
    1335             :     }
    1336             : 
    1337             :     /*
    1338             :      * Convert list of dictionary names to array of dict OIDs
    1339             :      */
    1340       23808 :     ndict = list_length(stmt->dicts);
    1341       23808 :     dictIds = (Oid *) palloc(sizeof(Oid) * ndict);
    1342       23808 :     i = 0;
    1343       47698 :     foreach(c, stmt->dicts)
    1344             :     {
    1345       23890 :         List       *names = (List *) lfirst(c);
    1346             : 
    1347       23890 :         dictIds[i] = get_ts_dict_oid(names, false);
    1348       23890 :         i++;
    1349             :     }
    1350             : 
    1351       23808 :     if (stmt->replace)
    1352             :     {
    1353             :         /*
    1354             :          * Replace a specific dictionary in existing entries
    1355             :          */
    1356          12 :         Oid         dictOld = dictIds[0],
    1357          12 :                     dictNew = dictIds[1];
    1358             : 
    1359          12 :         ScanKeyInit(&skey[0],
    1360             :                     Anum_pg_ts_config_map_mapcfg,
    1361             :                     BTEqualStrategyNumber, F_OIDEQ,
    1362             :                     ObjectIdGetDatum(cfgId));
    1363             : 
    1364          12 :         scan = systable_beginscan(relMap, TSConfigMapIndexId, true,
    1365             :                                   NULL, 1, skey);
    1366             : 
    1367         348 :         while (HeapTupleIsValid((maptup = systable_getnext(scan))))
    1368             :         {
    1369         336 :             Form_pg_ts_config_map cfgmap = (Form_pg_ts_config_map) GETSTRUCT(maptup);
    1370             : 
    1371             :             /*
    1372             :              * check if it's one of target token types
    1373             :              */
    1374         336 :             if (tokens)
    1375             :             {
    1376           0 :                 bool        tokmatch = false;
    1377             : 
    1378           0 :                 for (j = 0; j < ntoken; j++)
    1379             :                 {
    1380           0 :                     if (cfgmap->maptokentype == tokens[j])
    1381             :                     {
    1382           0 :                         tokmatch = true;
    1383           0 :                         break;
    1384             :                     }
    1385             :                 }
    1386           0 :                 if (!tokmatch)
    1387           0 :                     continue;
    1388             :             }
    1389             : 
    1390             :             /*
    1391             :              * replace dictionary if match
    1392             :              */
    1393         336 :             if (cfgmap->mapdict == dictOld)
    1394             :             {
    1395             :                 Datum       repl_val[Natts_pg_ts_config_map];
    1396             :                 bool        repl_null[Natts_pg_ts_config_map];
    1397             :                 bool        repl_repl[Natts_pg_ts_config_map];
    1398             :                 HeapTuple   newtup;
    1399             : 
    1400         108 :                 memset(repl_val, 0, sizeof(repl_val));
    1401         108 :                 memset(repl_null, false, sizeof(repl_null));
    1402         108 :                 memset(repl_repl, false, sizeof(repl_repl));
    1403             : 
    1404         108 :                 repl_val[Anum_pg_ts_config_map_mapdict - 1] = ObjectIdGetDatum(dictNew);
    1405         108 :                 repl_repl[Anum_pg_ts_config_map_mapdict - 1] = true;
    1406             : 
    1407         108 :                 newtup = heap_modify_tuple(maptup,
    1408             :                                            RelationGetDescr(relMap),
    1409             :                                            repl_val, repl_null, repl_repl);
    1410         108 :                 CatalogTupleUpdate(relMap, &newtup->t_self, newtup);
    1411             :             }
    1412             :         }
    1413             : 
    1414          12 :         systable_endscan(scan);
    1415             :     }
    1416             :     else
    1417             :     {
    1418             :         /*
    1419             :          * Insertion of new entries
    1420             :          */
    1421      173662 :         for (i = 0; i < ntoken; i++)
    1422             :         {
    1423      299858 :             for (j = 0; j < ndict; j++)
    1424             :             {
    1425             :                 Datum       values[Natts_pg_ts_config_map];
    1426             :                 bool        nulls[Natts_pg_ts_config_map];
    1427             : 
    1428      149992 :                 memset(nulls, false, sizeof(nulls));
    1429      149992 :                 values[Anum_pg_ts_config_map_mapcfg - 1] = ObjectIdGetDatum(cfgId);
    1430      149992 :                 values[Anum_pg_ts_config_map_maptokentype - 1] = Int32GetDatum(tokens[i]);
    1431      149992 :                 values[Anum_pg_ts_config_map_mapseqno - 1] = Int32GetDatum(j + 1);
    1432      149992 :                 values[Anum_pg_ts_config_map_mapdict - 1] = ObjectIdGetDatum(dictIds[j]);
    1433             : 
    1434      149992 :                 tup = heap_form_tuple(relMap->rd_att, values, nulls);
    1435      149992 :                 CatalogTupleInsert(relMap, tup);
    1436             : 
    1437      149992 :                 heap_freetuple(tup);
    1438             :             }
    1439             :         }
    1440             :     }
    1441             : 
    1442       23808 :     EventTriggerCollectAlterTSConfig(stmt, cfgId, dictIds, ndict);
    1443       23808 : }
    1444             : 
    1445             : /*
    1446             :  * ALTER TEXT SEARCH CONFIGURATION DROP MAPPING
    1447             :  */
    1448             : static void
    1449           0 : DropConfigurationMapping(AlterTSConfigurationStmt *stmt,
    1450             :                          HeapTuple tup, Relation relMap)
    1451             : {
    1452             :     Form_pg_ts_config tsform;
    1453             :     Oid         cfgId;
    1454             :     ScanKeyData skey[2];
    1455             :     SysScanDesc scan;
    1456             :     HeapTuple   maptup;
    1457             :     int         i;
    1458             :     Oid         prsId;
    1459             :     int        *tokens;
    1460             :     ListCell   *c;
    1461             : 
    1462           0 :     tsform = (Form_pg_ts_config) GETSTRUCT(tup);
    1463           0 :     cfgId = tsform->oid;
    1464           0 :     prsId = tsform->cfgparser;
    1465             : 
    1466           0 :     tokens = getTokenTypes(prsId, stmt->tokentype);
    1467             : 
    1468           0 :     i = 0;
    1469           0 :     foreach(c, stmt->tokentype)
    1470             :     {
    1471           0 :         Value      *val = (Value *) lfirst(c);
    1472           0 :         bool        found = false;
    1473             : 
    1474           0 :         ScanKeyInit(&skey[0],
    1475             :                     Anum_pg_ts_config_map_mapcfg,
    1476             :                     BTEqualStrategyNumber, F_OIDEQ,
    1477             :                     ObjectIdGetDatum(cfgId));
    1478           0 :         ScanKeyInit(&skey[1],
    1479             :                     Anum_pg_ts_config_map_maptokentype,
    1480             :                     BTEqualStrategyNumber, F_INT4EQ,
    1481           0 :                     Int32GetDatum(tokens[i]));
    1482             : 
    1483           0 :         scan = systable_beginscan(relMap, TSConfigMapIndexId, true,
    1484             :                                   NULL, 2, skey);
    1485             : 
    1486           0 :         while (HeapTupleIsValid((maptup = systable_getnext(scan))))
    1487             :         {
    1488           0 :             CatalogTupleDelete(relMap, &maptup->t_self);
    1489           0 :             found = true;
    1490             :         }
    1491             : 
    1492           0 :         systable_endscan(scan);
    1493             : 
    1494           0 :         if (!found)
    1495             :         {
    1496           0 :             if (!stmt->missing_ok)
    1497             :             {
    1498           0 :                 ereport(ERROR,
    1499             :                         (errcode(ERRCODE_UNDEFINED_OBJECT),
    1500             :                          errmsg("mapping for token type \"%s\" does not exist",
    1501             :                                 strVal(val))));
    1502             :             }
    1503             :             else
    1504             :             {
    1505           0 :                 ereport(NOTICE,
    1506             :                         (errmsg("mapping for token type \"%s\" does not exist, skipping",
    1507             :                                 strVal(val))));
    1508             :             }
    1509             :         }
    1510             : 
    1511           0 :         i++;
    1512             :     }
    1513             : 
    1514           0 :     EventTriggerCollectAlterTSConfig(stmt, cfgId, NULL, 0);
    1515           0 : }
    1516             : 
    1517             : 
    1518             : /*
    1519             :  * Serialize dictionary options, producing a TEXT datum from a List of DefElem
    1520             :  *
    1521             :  * This is used to form the value stored in pg_ts_dict.dictinitoption.
    1522             :  * For the convenience of pg_dump, the output is formatted exactly as it
    1523             :  * would need to appear in CREATE TEXT SEARCH DICTIONARY to reproduce the
    1524             :  * same options.
    1525             :  */
    1526             : text *
    1527        7964 : serialize_deflist(List *deflist)
    1528             : {
    1529             :     text       *result;
    1530             :     StringInfoData buf;
    1531             :     ListCell   *l;
    1532             : 
    1533        7964 :     initStringInfo(&buf);
    1534             : 
    1535       21422 :     foreach(l, deflist)
    1536             :     {
    1537       13458 :         DefElem    *defel = (DefElem *) lfirst(l);
    1538       13458 :         char       *val = defGetString(defel);
    1539             : 
    1540       13458 :         appendStringInfo(&buf, "%s = ",
    1541       13458 :                          quote_identifier(defel->defname));
    1542             : 
    1543             :         /*
    1544             :          * If the value is a T_Integer or T_Float, emit it without quotes,
    1545             :          * otherwise with quotes.  This is essential to allow correct
    1546             :          * reconstruction of the node type as well as the value.
    1547             :          */
    1548       13458 :         if (IsA(defel->arg, Integer) || IsA(defel->arg, Float))
    1549          12 :             appendStringInfoString(&buf, val);
    1550             :         else
    1551             :         {
    1552             :             /* If backslashes appear, force E syntax to quote them safely */
    1553       13446 :             if (strchr(val, '\\'))
    1554           0 :                 appendStringInfoChar(&buf, ESCAPE_STRING_SYNTAX);
    1555       13446 :             appendStringInfoChar(&buf, '\'');
    1556      109220 :             while (*val)
    1557             :             {
    1558       95774 :                 char        ch = *val++;
    1559             : 
    1560       95774 :                 if (SQL_STR_DOUBLE(ch, true))
    1561           0 :                     appendStringInfoChar(&buf, ch);
    1562       95774 :                 appendStringInfoChar(&buf, ch);
    1563             :             }
    1564       13446 :             appendStringInfoChar(&buf, '\'');
    1565             :         }
    1566       13458 :         if (lnext(deflist, l) != NULL)
    1567        5494 :             appendStringInfoString(&buf, ", ");
    1568             :     }
    1569             : 
    1570        7964 :     result = cstring_to_text_with_len(buf.data, buf.len);
    1571        7964 :     pfree(buf.data);
    1572        7964 :     return result;
    1573             : }
    1574             : 
    1575             : /*
    1576             :  * Deserialize dictionary options, reconstructing a List of DefElem from TEXT
    1577             :  *
    1578             :  * This is also used for prsheadline options, so for backward compatibility
    1579             :  * we need to accept a few things serialize_deflist() will never emit:
    1580             :  * in particular, unquoted and double-quoted strings.
    1581             :  */
    1582             : List *
    1583         184 : deserialize_deflist(Datum txt)
    1584             : {
    1585         184 :     text       *in = DatumGetTextPP(txt);   /* in case it's toasted */
    1586         184 :     List       *result = NIL;
    1587         184 :     int         len = VARSIZE_ANY_EXHDR(in);
    1588             :     char       *ptr,
    1589             :                *endptr,
    1590             :                *workspace,
    1591         184 :                *wsptr = NULL,
    1592         184 :                *startvalue = NULL;
    1593             :     typedef enum
    1594             :     {
    1595             :         CS_WAITKEY,
    1596             :         CS_INKEY,
    1597             :         CS_INQKEY,
    1598             :         CS_WAITEQ,
    1599             :         CS_WAITVALUE,
    1600             :         CS_INSQVALUE,
    1601             :         CS_INDQVALUE,
    1602             :         CS_INWVALUE
    1603             :     } ds_state;
    1604         184 :     ds_state    state = CS_WAITKEY;
    1605             : 
    1606         184 :     workspace = (char *) palloc(len + 1);   /* certainly enough room */
    1607         184 :     ptr = VARDATA_ANY(in);
    1608         184 :     endptr = ptr + len;
    1609        9046 :     for (; ptr < endptr; ptr++)
    1610             :     {
    1611        8862 :         switch (state)
    1612             :         {
    1613         844 :             case CS_WAITKEY:
    1614         844 :                 if (isspace((unsigned char) *ptr) || *ptr == ',')
    1615         422 :                     continue;
    1616         422 :                 if (*ptr == '"')
    1617             :                 {
    1618           0 :                     wsptr = workspace;
    1619           0 :                     state = CS_INQKEY;
    1620             :                 }
    1621             :                 else
    1622             :                 {
    1623         422 :                     wsptr = workspace;
    1624         422 :                     *wsptr++ = *ptr;
    1625         422 :                     state = CS_INKEY;
    1626             :                 }
    1627         422 :                 break;
    1628        3742 :             case CS_INKEY:
    1629        3742 :                 if (isspace((unsigned char) *ptr))
    1630             :                 {
    1631         354 :                     *wsptr++ = '\0';
    1632         354 :                     state = CS_WAITEQ;
    1633             :                 }
    1634        3388 :                 else if (*ptr == '=')
    1635             :                 {
    1636          68 :                     *wsptr++ = '\0';
    1637          68 :                     state = CS_WAITVALUE;
    1638             :                 }
    1639             :                 else
    1640             :                 {
    1641        3320 :                     *wsptr++ = *ptr;
    1642             :                 }
    1643        3742 :                 break;
    1644           0 :             case CS_INQKEY:
    1645           0 :                 if (*ptr == '"')
    1646             :                 {
    1647           0 :                     if (ptr + 1 < endptr && ptr[1] == '"')
    1648             :                     {
    1649             :                         /* copy only one of the two quotes */
    1650           0 :                         *wsptr++ = *ptr++;
    1651             :                     }
    1652             :                     else
    1653             :                     {
    1654           0 :                         *wsptr++ = '\0';
    1655           0 :                         state = CS_WAITEQ;
    1656             :                     }
    1657             :                 }
    1658             :                 else
    1659             :                 {
    1660           0 :                     *wsptr++ = *ptr;
    1661             :                 }
    1662           0 :                 break;
    1663         354 :             case CS_WAITEQ:
    1664         354 :                 if (*ptr == '=')
    1665         354 :                     state = CS_WAITVALUE;
    1666           0 :                 else if (!isspace((unsigned char) *ptr))
    1667           0 :                     ereport(ERROR,
    1668             :                             (errcode(ERRCODE_SYNTAX_ERROR),
    1669             :                              errmsg("invalid parameter list format: \"%s\"",
    1670             :                                     text_to_cstring(in))));
    1671         354 :                 break;
    1672         776 :             case CS_WAITVALUE:
    1673         776 :                 if (*ptr == '\'')
    1674             :                 {
    1675         298 :                     startvalue = wsptr;
    1676         298 :                     state = CS_INSQVALUE;
    1677             :                 }
    1678         478 :                 else if (*ptr == 'E' && ptr + 1 < endptr && ptr[1] == '\'')
    1679             :                 {
    1680           0 :                     ptr++;
    1681           0 :                     startvalue = wsptr;
    1682           0 :                     state = CS_INSQVALUE;
    1683             :                 }
    1684         478 :                 else if (*ptr == '"')
    1685             :                 {
    1686           0 :                     startvalue = wsptr;
    1687           0 :                     state = CS_INDQVALUE;
    1688             :                 }
    1689         478 :                 else if (!isspace((unsigned char) *ptr))
    1690             :                 {
    1691         124 :                     startvalue = wsptr;
    1692         124 :                     *wsptr++ = *ptr;
    1693         124 :                     state = CS_INWVALUE;
    1694             :                 }
    1695         776 :                 break;
    1696        3032 :             case CS_INSQVALUE:
    1697        3032 :                 if (*ptr == '\'')
    1698             :                 {
    1699         298 :                     if (ptr + 1 < endptr && ptr[1] == '\'')
    1700             :                     {
    1701             :                         /* copy only one of the two quotes */
    1702           0 :                         *wsptr++ = *ptr++;
    1703             :                     }
    1704             :                     else
    1705             :                     {
    1706         298 :                         *wsptr++ = '\0';
    1707         298 :                         result = lappend(result,
    1708         298 :                                          buildDefItem(workspace,
    1709             :                                                       startvalue,
    1710             :                                                       true));
    1711         298 :                         state = CS_WAITKEY;
    1712             :                     }
    1713             :                 }
    1714        2734 :                 else if (*ptr == '\\')
    1715             :                 {
    1716           0 :                     if (ptr + 1 < endptr && ptr[1] == '\\')
    1717             :                     {
    1718             :                         /* copy only one of the two backslashes */
    1719           0 :                         *wsptr++ = *ptr++;
    1720             :                     }
    1721             :                     else
    1722           0 :                         *wsptr++ = *ptr;
    1723             :                 }
    1724             :                 else
    1725             :                 {
    1726        2734 :                     *wsptr++ = *ptr;
    1727             :                 }
    1728        3032 :                 break;
    1729           0 :             case CS_INDQVALUE:
    1730           0 :                 if (*ptr == '"')
    1731             :                 {
    1732           0 :                     if (ptr + 1 < endptr && ptr[1] == '"')
    1733             :                     {
    1734             :                         /* copy only one of the two quotes */
    1735           0 :                         *wsptr++ = *ptr++;
    1736             :                     }
    1737             :                     else
    1738             :                     {
    1739           0 :                         *wsptr++ = '\0';
    1740           0 :                         result = lappend(result,
    1741           0 :                                          buildDefItem(workspace,
    1742             :                                                       startvalue,
    1743             :                                                       true));
    1744           0 :                         state = CS_WAITKEY;
    1745             :                     }
    1746             :                 }
    1747             :                 else
    1748             :                 {
    1749           0 :                     *wsptr++ = *ptr;
    1750             :                 }
    1751           0 :                 break;
    1752         114 :             case CS_INWVALUE:
    1753         114 :                 if (*ptr == ',' || isspace((unsigned char) *ptr))
    1754             :                 {
    1755          50 :                     *wsptr++ = '\0';
    1756          50 :                     result = lappend(result,
    1757          50 :                                      buildDefItem(workspace,
    1758             :                                                   startvalue,
    1759             :                                                   false));
    1760          50 :                     state = CS_WAITKEY;
    1761             :                 }
    1762             :                 else
    1763             :                 {
    1764          64 :                     *wsptr++ = *ptr;
    1765             :                 }
    1766         114 :                 break;
    1767           0 :             default:
    1768           0 :                 elog(ERROR, "unrecognized deserialize_deflist state: %d",
    1769             :                      state);
    1770             :         }
    1771             :     }
    1772             : 
    1773         184 :     if (state == CS_INWVALUE)
    1774             :     {
    1775          74 :         *wsptr++ = '\0';
    1776          74 :         result = lappend(result,
    1777          74 :                          buildDefItem(workspace,
    1778             :                                       startvalue,
    1779             :                                       false));
    1780             :     }
    1781         110 :     else if (state != CS_WAITKEY)
    1782           0 :         ereport(ERROR,
    1783             :                 (errcode(ERRCODE_SYNTAX_ERROR),
    1784             :                  errmsg("invalid parameter list format: \"%s\"",
    1785             :                         text_to_cstring(in))));
    1786             : 
    1787         184 :     pfree(workspace);
    1788             : 
    1789         184 :     return result;
    1790             : }
    1791             : 
    1792             : /*
    1793             :  * Build one DefElem for deserialize_deflist
    1794             :  */
    1795             : static DefElem *
    1796         422 : buildDefItem(const char *name, const char *val, bool was_quoted)
    1797             : {
    1798             :     /* If input was quoted, always emit as string */
    1799         422 :     if (!was_quoted && val[0] != '\0')
    1800             :     {
    1801             :         int         v;
    1802             :         char       *endptr;
    1803             : 
    1804             :         /* Try to parse as an integer */
    1805         124 :         errno = 0;
    1806         124 :         v = strtoint(val, &endptr, 10);
    1807         124 :         if (errno == 0 && *endptr == '\0')
    1808          84 :             return makeDefElem(pstrdup(name),
    1809          74 :                                (Node *) makeInteger(v),
    1810             :                                -1);
    1811             :         /* Nope, how about as a float? */
    1812          50 :         errno = 0;
    1813          50 :         (void) strtod(val, &endptr);
    1814          50 :         if (errno == 0 && *endptr == '\0')
    1815          10 :             return makeDefElem(pstrdup(name),
    1816          10 :                                (Node *) makeFloat(pstrdup(val)),
    1817             :                                -1);
    1818             :     }
    1819             :     /* Just make it a string */
    1820         338 :     return makeDefElem(pstrdup(name),
    1821         338 :                        (Node *) makeString(pstrdup(val)),
    1822             :                        -1);
    1823             : }

Generated by: LCOV version 1.13