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

Generated by: LCOV version 1.14