LCOV - code coverage report
Current view: top level - src/backend/commands - tsearchcmds.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 87.3 % 912 796
Test Date: 2026-01-26 10:56:24 Functions: 100.0 % 22 22
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 54.4 % 517 281

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

Generated by: LCOV version 2.3.2-1