LCOV - differential code coverage report
Current view: top level - src/backend/commands - tsearchcmds.c (source / functions) Coverage Total Hit UNC LBC UIC UBC GBC GIC GNC CBC EUB ECB DUB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 84.0 % 692 581 3 33 39 36 27 125 51 378 45 161 3 9
Current Date: 2023-04-08 15:15:32 Functions: 95.2 % 21 20 1 7 3 10 1 7
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

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

Generated by: LCOV version v1.16-55-g56c0a2a