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

Generated by: LCOV version 1.11