Header And Logo

PostgreSQL
| The world's most advanced open source database.

tsearchcmds.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * tsearchcmds.c
00004  *
00005  *    Routines for tsearch manipulation commands
00006  *
00007  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00008  * Portions Copyright (c) 1994, Regents of the University of California
00009  *
00010  *
00011  * IDENTIFICATION
00012  *    src/backend/commands/tsearchcmds.c
00013  *
00014  *-------------------------------------------------------------------------
00015  */
00016 #include "postgres.h"
00017 
00018 #include <ctype.h>
00019 
00020 #include "access/genam.h"
00021 #include "access/heapam.h"
00022 #include "access/htup_details.h"
00023 #include "access/xact.h"
00024 #include "catalog/dependency.h"
00025 #include "catalog/indexing.h"
00026 #include "catalog/objectaccess.h"
00027 #include "catalog/pg_namespace.h"
00028 #include "catalog/pg_proc.h"
00029 #include "catalog/pg_ts_config.h"
00030 #include "catalog/pg_ts_config_map.h"
00031 #include "catalog/pg_ts_dict.h"
00032 #include "catalog/pg_ts_parser.h"
00033 #include "catalog/pg_ts_template.h"
00034 #include "catalog/pg_type.h"
00035 #include "commands/alter.h"
00036 #include "commands/defrem.h"
00037 #include "miscadmin.h"
00038 #include "nodes/makefuncs.h"
00039 #include "parser/parse_func.h"
00040 #include "tsearch/ts_cache.h"
00041 #include "tsearch/ts_utils.h"
00042 #include "utils/builtins.h"
00043 #include "utils/fmgroids.h"
00044 #include "utils/lsyscache.h"
00045 #include "utils/rel.h"
00046 #include "utils/syscache.h"
00047 #include "utils/tqual.h"
00048 
00049 
00050 static void MakeConfigurationMapping(AlterTSConfigurationStmt *stmt,
00051                          HeapTuple tup, Relation relMap);
00052 static void DropConfigurationMapping(AlterTSConfigurationStmt *stmt,
00053                          HeapTuple tup, Relation relMap);
00054 
00055 
00056 /* --------------------- TS Parser commands ------------------------ */
00057 
00058 /*
00059  * lookup a parser support function and return its OID (as a Datum)
00060  *
00061  * attnum is the pg_ts_parser column the function will go into
00062  */
00063 static Datum
00064 get_ts_parser_func(DefElem *defel, int attnum)
00065 {
00066     List       *funcName = defGetQualifiedName(defel);
00067     Oid         typeId[3];
00068     Oid         retTypeId;
00069     int         nargs;
00070     Oid         procOid;
00071 
00072     retTypeId = INTERNALOID;    /* correct for most */
00073     typeId[0] = INTERNALOID;
00074     switch (attnum)
00075     {
00076         case Anum_pg_ts_parser_prsstart:
00077             nargs = 2;
00078             typeId[1] = INT4OID;
00079             break;
00080         case Anum_pg_ts_parser_prstoken:
00081             nargs = 3;
00082             typeId[1] = INTERNALOID;
00083             typeId[2] = INTERNALOID;
00084             break;
00085         case Anum_pg_ts_parser_prsend:
00086             nargs = 1;
00087             retTypeId = VOIDOID;
00088             break;
00089         case Anum_pg_ts_parser_prsheadline:
00090             nargs = 3;
00091             typeId[1] = INTERNALOID;
00092             typeId[2] = TSQUERYOID;
00093             break;
00094         case Anum_pg_ts_parser_prslextype:
00095             nargs = 1;
00096 
00097             /*
00098              * Note: because the lextype method returns type internal, it must
00099              * have an internal-type argument for security reasons.  The
00100              * argument is not actually used, but is just passed as a zero.
00101              */
00102             break;
00103         default:
00104             /* should not be here */
00105             elog(ERROR, "unrecognized attribute for text search parser: %d",
00106                  attnum);
00107             nargs = 0;          /* keep compiler quiet */
00108     }
00109 
00110     procOid = LookupFuncName(funcName, nargs, typeId, false);
00111     if (get_func_rettype(procOid) != retTypeId)
00112         ereport(ERROR,
00113                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
00114                  errmsg("function %s should return type %s",
00115                         func_signature_string(funcName, nargs, NIL, typeId),
00116                         format_type_be(retTypeId))));
00117 
00118     return ObjectIdGetDatum(procOid);
00119 }
00120 
00121 /*
00122  * make pg_depend entries for a new pg_ts_parser entry
00123  */
00124 static void
00125 makeParserDependencies(HeapTuple tuple)
00126 {
00127     Form_pg_ts_parser prs = (Form_pg_ts_parser) GETSTRUCT(tuple);
00128     ObjectAddress myself,
00129                 referenced;
00130 
00131     myself.classId = TSParserRelationId;
00132     myself.objectId = HeapTupleGetOid(tuple);
00133     myself.objectSubId = 0;
00134 
00135     /* dependency on namespace */
00136     referenced.classId = NamespaceRelationId;
00137     referenced.objectId = prs->prsnamespace;
00138     referenced.objectSubId = 0;
00139     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
00140 
00141     /* dependency on extension */
00142     recordDependencyOnCurrentExtension(&myself, false);
00143 
00144     /* dependencies on functions */
00145     referenced.classId = ProcedureRelationId;
00146     referenced.objectSubId = 0;
00147 
00148     referenced.objectId = prs->prsstart;
00149     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
00150 
00151     referenced.objectId = prs->prstoken;
00152     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
00153 
00154     referenced.objectId = prs->prsend;
00155     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
00156 
00157     referenced.objectId = prs->prslextype;
00158     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
00159 
00160     if (OidIsValid(prs->prsheadline))
00161     {
00162         referenced.objectId = prs->prsheadline;
00163         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
00164     }
00165 }
00166 
00167 /*
00168  * CREATE TEXT SEARCH PARSER
00169  */
00170 Oid
00171 DefineTSParser(List *names, List *parameters)
00172 {
00173     char       *prsname;
00174     ListCell   *pl;
00175     Relation    prsRel;
00176     HeapTuple   tup;
00177     Datum       values[Natts_pg_ts_parser];
00178     bool        nulls[Natts_pg_ts_parser];
00179     NameData    pname;
00180     Oid         prsOid;
00181     Oid         namespaceoid;
00182 
00183     if (!superuser())
00184         ereport(ERROR,
00185                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00186                  errmsg("must be superuser to create text search parsers")));
00187 
00188     /* Convert list of names to a name and namespace */
00189     namespaceoid = QualifiedNameGetCreationNamespace(names, &prsname);
00190 
00191     /* initialize tuple fields with name/namespace */
00192     memset(values, 0, sizeof(values));
00193     memset(nulls, false, sizeof(nulls));
00194 
00195     namestrcpy(&pname, prsname);
00196     values[Anum_pg_ts_parser_prsname - 1] = NameGetDatum(&pname);
00197     values[Anum_pg_ts_parser_prsnamespace - 1] = ObjectIdGetDatum(namespaceoid);
00198 
00199     /*
00200      * loop over the definition list and extract the information we need.
00201      */
00202     foreach(pl, parameters)
00203     {
00204         DefElem    *defel = (DefElem *) lfirst(pl);
00205 
00206         if (pg_strcasecmp(defel->defname, "start") == 0)
00207         {
00208             values[Anum_pg_ts_parser_prsstart - 1] =
00209                 get_ts_parser_func(defel, Anum_pg_ts_parser_prsstart);
00210         }
00211         else if (pg_strcasecmp(defel->defname, "gettoken") == 0)
00212         {
00213             values[Anum_pg_ts_parser_prstoken - 1] =
00214                 get_ts_parser_func(defel, Anum_pg_ts_parser_prstoken);
00215         }
00216         else if (pg_strcasecmp(defel->defname, "end") == 0)
00217         {
00218             values[Anum_pg_ts_parser_prsend - 1] =
00219                 get_ts_parser_func(defel, Anum_pg_ts_parser_prsend);
00220         }
00221         else if (pg_strcasecmp(defel->defname, "headline") == 0)
00222         {
00223             values[Anum_pg_ts_parser_prsheadline - 1] =
00224                 get_ts_parser_func(defel, Anum_pg_ts_parser_prsheadline);
00225         }
00226         else if (pg_strcasecmp(defel->defname, "lextypes") == 0)
00227         {
00228             values[Anum_pg_ts_parser_prslextype - 1] =
00229                 get_ts_parser_func(defel, Anum_pg_ts_parser_prslextype);
00230         }
00231         else
00232             ereport(ERROR,
00233                     (errcode(ERRCODE_SYNTAX_ERROR),
00234                  errmsg("text search parser parameter \"%s\" not recognized",
00235                         defel->defname)));
00236     }
00237 
00238     /*
00239      * Validation
00240      */
00241     if (!OidIsValid(DatumGetObjectId(values[Anum_pg_ts_parser_prsstart - 1])))
00242         ereport(ERROR,
00243                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
00244                  errmsg("text search parser start method is required")));
00245 
00246     if (!OidIsValid(DatumGetObjectId(values[Anum_pg_ts_parser_prstoken - 1])))
00247         ereport(ERROR,
00248                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
00249                  errmsg("text search parser gettoken method is required")));
00250 
00251     if (!OidIsValid(DatumGetObjectId(values[Anum_pg_ts_parser_prsend - 1])))
00252         ereport(ERROR,
00253                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
00254                  errmsg("text search parser end method is required")));
00255 
00256     if (!OidIsValid(DatumGetObjectId(values[Anum_pg_ts_parser_prslextype - 1])))
00257         ereport(ERROR,
00258                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
00259                  errmsg("text search parser lextypes method is required")));
00260 
00261     /*
00262      * Looks good, insert
00263      */
00264     prsRel = heap_open(TSParserRelationId, RowExclusiveLock);
00265 
00266     tup = heap_form_tuple(prsRel->rd_att, values, nulls);
00267 
00268     prsOid = simple_heap_insert(prsRel, tup);
00269 
00270     CatalogUpdateIndexes(prsRel, tup);
00271 
00272     makeParserDependencies(tup);
00273 
00274     /* Post creation hook for new text search parser */
00275     InvokeObjectPostCreateHook(TSParserRelationId, prsOid, 0);
00276 
00277     heap_freetuple(tup);
00278 
00279     heap_close(prsRel, RowExclusiveLock);
00280 
00281     return prsOid;
00282 }
00283 
00284 /*
00285  * Guts of TS parser deletion.
00286  */
00287 void
00288 RemoveTSParserById(Oid prsId)
00289 {
00290     Relation    relation;
00291     HeapTuple   tup;
00292 
00293     relation = heap_open(TSParserRelationId, RowExclusiveLock);
00294 
00295     tup = SearchSysCache1(TSPARSEROID, ObjectIdGetDatum(prsId));
00296 
00297     if (!HeapTupleIsValid(tup))
00298         elog(ERROR, "cache lookup failed for text search parser %u", prsId);
00299 
00300     simple_heap_delete(relation, &tup->t_self);
00301 
00302     ReleaseSysCache(tup);
00303 
00304     heap_close(relation, RowExclusiveLock);
00305 }
00306 
00307 /* ---------------------- TS Dictionary commands -----------------------*/
00308 
00309 /*
00310  * make pg_depend entries for a new pg_ts_dict entry
00311  */
00312 static void
00313 makeDictionaryDependencies(HeapTuple tuple)
00314 {
00315     Form_pg_ts_dict dict = (Form_pg_ts_dict) GETSTRUCT(tuple);
00316     ObjectAddress myself,
00317                 referenced;
00318 
00319     myself.classId = TSDictionaryRelationId;
00320     myself.objectId = HeapTupleGetOid(tuple);
00321     myself.objectSubId = 0;
00322 
00323     /* dependency on namespace */
00324     referenced.classId = NamespaceRelationId;
00325     referenced.objectId = dict->dictnamespace;
00326     referenced.objectSubId = 0;
00327     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
00328 
00329     /* dependency on owner */
00330     recordDependencyOnOwner(myself.classId, myself.objectId, dict->dictowner);
00331 
00332     /* dependency on extension */
00333     recordDependencyOnCurrentExtension(&myself, false);
00334 
00335     /* dependency on template */
00336     referenced.classId = TSTemplateRelationId;
00337     referenced.objectId = dict->dicttemplate;
00338     referenced.objectSubId = 0;
00339     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
00340 }
00341 
00342 /*
00343  * verify that a template's init method accepts a proposed option list
00344  */
00345 static void
00346 verify_dictoptions(Oid tmplId, List *dictoptions)
00347 {
00348     HeapTuple   tup;
00349     Form_pg_ts_template tform;
00350     Oid         initmethod;
00351 
00352     /*
00353      * Suppress this test when running in a standalone backend.  This is a
00354      * hack to allow initdb to create prefab dictionaries that might not
00355      * actually be usable in template1's encoding (due to using external files
00356      * that can't be translated into template1's encoding).  We want to create
00357      * them anyway, since they might be usable later in other databases.
00358      */
00359     if (!IsUnderPostmaster)
00360         return;
00361 
00362     tup = SearchSysCache1(TSTEMPLATEOID, ObjectIdGetDatum(tmplId));
00363     if (!HeapTupleIsValid(tup)) /* should not happen */
00364         elog(ERROR, "cache lookup failed for text search template %u",
00365              tmplId);
00366     tform = (Form_pg_ts_template) GETSTRUCT(tup);
00367 
00368     initmethod = tform->tmplinit;
00369 
00370     if (!OidIsValid(initmethod))
00371     {
00372         /* If there is no init method, disallow any options */
00373         if (dictoptions)
00374             ereport(ERROR,
00375                     (errcode(ERRCODE_SYNTAX_ERROR),
00376                 errmsg("text search template \"%s\" does not accept options",
00377                        NameStr(tform->tmplname))));
00378     }
00379     else
00380     {
00381         /*
00382          * Copy the options just in case init method thinks it can scribble on
00383          * them ...
00384          */
00385         dictoptions = copyObject(dictoptions);
00386 
00387         /*
00388          * Call the init method and see if it complains.  We don't worry about
00389          * it leaking memory, since our command will soon be over anyway.
00390          */
00391         (void) OidFunctionCall1(initmethod, PointerGetDatum(dictoptions));
00392     }
00393 
00394     ReleaseSysCache(tup);
00395 }
00396 
00397 /*
00398  * CREATE TEXT SEARCH DICTIONARY
00399  */
00400 Oid
00401 DefineTSDictionary(List *names, List *parameters)
00402 {
00403     ListCell   *pl;
00404     Relation    dictRel;
00405     HeapTuple   tup;
00406     Datum       values[Natts_pg_ts_dict];
00407     bool        nulls[Natts_pg_ts_dict];
00408     NameData    dname;
00409     Oid         templId = InvalidOid;
00410     List       *dictoptions = NIL;
00411     Oid         dictOid;
00412     Oid         namespaceoid;
00413     AclResult   aclresult;
00414     char       *dictname;
00415 
00416     /* Convert list of names to a name and namespace */
00417     namespaceoid = QualifiedNameGetCreationNamespace(names, &dictname);
00418 
00419     /* Check we have creation rights in target namespace */
00420     aclresult = pg_namespace_aclcheck(namespaceoid, GetUserId(), ACL_CREATE);
00421     if (aclresult != ACLCHECK_OK)
00422         aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
00423                        get_namespace_name(namespaceoid));
00424 
00425     /*
00426      * loop over the definition list and extract the information we need.
00427      */
00428     foreach(pl, parameters)
00429     {
00430         DefElem    *defel = (DefElem *) lfirst(pl);
00431 
00432         if (pg_strcasecmp(defel->defname, "template") == 0)
00433         {
00434             templId = get_ts_template_oid(defGetQualifiedName(defel), false);
00435         }
00436         else
00437         {
00438             /* Assume it's an option for the dictionary itself */
00439             dictoptions = lappend(dictoptions, defel);
00440         }
00441     }
00442 
00443     /*
00444      * Validation
00445      */
00446     if (!OidIsValid(templId))
00447         ereport(ERROR,
00448                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
00449                  errmsg("text search template is required")));
00450 
00451     verify_dictoptions(templId, dictoptions);
00452 
00453     /*
00454      * Looks good, insert
00455      */
00456     memset(values, 0, sizeof(values));
00457     memset(nulls, false, sizeof(nulls));
00458 
00459     namestrcpy(&dname, dictname);
00460     values[Anum_pg_ts_dict_dictname - 1] = NameGetDatum(&dname);
00461     values[Anum_pg_ts_dict_dictnamespace - 1] = ObjectIdGetDatum(namespaceoid);
00462     values[Anum_pg_ts_dict_dictowner - 1] = ObjectIdGetDatum(GetUserId());
00463     values[Anum_pg_ts_dict_dicttemplate - 1] = ObjectIdGetDatum(templId);
00464     if (dictoptions)
00465         values[Anum_pg_ts_dict_dictinitoption - 1] =
00466             PointerGetDatum(serialize_deflist(dictoptions));
00467     else
00468         nulls[Anum_pg_ts_dict_dictinitoption - 1] = true;
00469 
00470     dictRel = heap_open(TSDictionaryRelationId, RowExclusiveLock);
00471 
00472     tup = heap_form_tuple(dictRel->rd_att, values, nulls);
00473 
00474     dictOid = simple_heap_insert(dictRel, tup);
00475 
00476     CatalogUpdateIndexes(dictRel, tup);
00477 
00478     makeDictionaryDependencies(tup);
00479 
00480     /* Post creation hook for new text search dictionary */
00481     InvokeObjectPostCreateHook(TSDictionaryRelationId, dictOid, 0);
00482 
00483     heap_freetuple(tup);
00484 
00485     heap_close(dictRel, RowExclusiveLock);
00486 
00487     return dictOid;
00488 }
00489 
00490 /*
00491  * Guts of TS dictionary deletion.
00492  */
00493 void
00494 RemoveTSDictionaryById(Oid dictId)
00495 {
00496     Relation    relation;
00497     HeapTuple   tup;
00498 
00499     relation = heap_open(TSDictionaryRelationId, RowExclusiveLock);
00500 
00501     tup = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(dictId));
00502 
00503     if (!HeapTupleIsValid(tup))
00504         elog(ERROR, "cache lookup failed for text search dictionary %u",
00505              dictId);
00506 
00507     simple_heap_delete(relation, &tup->t_self);
00508 
00509     ReleaseSysCache(tup);
00510 
00511     heap_close(relation, RowExclusiveLock);
00512 }
00513 
00514 /*
00515  * ALTER TEXT SEARCH DICTIONARY
00516  */
00517 Oid
00518 AlterTSDictionary(AlterTSDictionaryStmt *stmt)
00519 {
00520     HeapTuple   tup,
00521                 newtup;
00522     Relation    rel;
00523     Oid         dictId;
00524     ListCell   *pl;
00525     List       *dictoptions;
00526     Datum       opt;
00527     bool        isnull;
00528     Datum       repl_val[Natts_pg_ts_dict];
00529     bool        repl_null[Natts_pg_ts_dict];
00530     bool        repl_repl[Natts_pg_ts_dict];
00531 
00532     dictId = get_ts_dict_oid(stmt->dictname, false);
00533 
00534     rel = heap_open(TSDictionaryRelationId, RowExclusiveLock);
00535 
00536     tup = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(dictId));
00537 
00538     if (!HeapTupleIsValid(tup))
00539         elog(ERROR, "cache lookup failed for text search dictionary %u",
00540              dictId);
00541 
00542     /* must be owner */
00543     if (!pg_ts_dict_ownercheck(dictId, GetUserId()))
00544         aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSDICTIONARY,
00545                        NameListToString(stmt->dictname));
00546 
00547     /* deserialize the existing set of options */
00548     opt = SysCacheGetAttr(TSDICTOID, tup,
00549                           Anum_pg_ts_dict_dictinitoption,
00550                           &isnull);
00551     if (isnull)
00552         dictoptions = NIL;
00553     else
00554         dictoptions = deserialize_deflist(opt);
00555 
00556     /*
00557      * Modify the options list as per specified changes
00558      */
00559     foreach(pl, stmt->options)
00560     {
00561         DefElem    *defel = (DefElem *) lfirst(pl);
00562         ListCell   *cell;
00563         ListCell   *prev;
00564         ListCell   *next;
00565 
00566         /*
00567          * Remove any matches ...
00568          */
00569         prev = NULL;
00570         for (cell = list_head(dictoptions); cell; cell = next)
00571         {
00572             DefElem    *oldel = (DefElem *) lfirst(cell);
00573 
00574             next = lnext(cell);
00575             if (pg_strcasecmp(oldel->defname, defel->defname) == 0)
00576                 dictoptions = list_delete_cell(dictoptions, cell, prev);
00577             else
00578                 prev = cell;
00579         }
00580 
00581         /*
00582          * and add new value if it's got one
00583          */
00584         if (defel->arg)
00585             dictoptions = lappend(dictoptions, defel);
00586     }
00587 
00588     /*
00589      * Validate
00590      */
00591     verify_dictoptions(((Form_pg_ts_dict) GETSTRUCT(tup))->dicttemplate,
00592                        dictoptions);
00593 
00594     /*
00595      * Looks good, update
00596      */
00597     memset(repl_val, 0, sizeof(repl_val));
00598     memset(repl_null, false, sizeof(repl_null));
00599     memset(repl_repl, false, sizeof(repl_repl));
00600 
00601     if (dictoptions)
00602         repl_val[Anum_pg_ts_dict_dictinitoption - 1] =
00603             PointerGetDatum(serialize_deflist(dictoptions));
00604     else
00605         repl_null[Anum_pg_ts_dict_dictinitoption - 1] = true;
00606     repl_repl[Anum_pg_ts_dict_dictinitoption - 1] = true;
00607 
00608     newtup = heap_modify_tuple(tup, RelationGetDescr(rel),
00609                                repl_val, repl_null, repl_repl);
00610 
00611     simple_heap_update(rel, &newtup->t_self, newtup);
00612 
00613     CatalogUpdateIndexes(rel, newtup);
00614 
00615     InvokeObjectPostAlterHook(TSDictionaryRelationId, dictId, 0);
00616 
00617     /*
00618      * NOTE: because we only support altering the options, not the template,
00619      * there is no need to update dependencies.  This might have to change if
00620      * the options ever reference inside-the-database objects.
00621      */
00622 
00623     heap_freetuple(newtup);
00624     ReleaseSysCache(tup);
00625 
00626     heap_close(rel, RowExclusiveLock);
00627 
00628     return dictId;
00629 }
00630 
00631 /* ---------------------- TS Template commands -----------------------*/
00632 
00633 /*
00634  * lookup a template support function and return its OID (as a Datum)
00635  *
00636  * attnum is the pg_ts_template column the function will go into
00637  */
00638 static Datum
00639 get_ts_template_func(DefElem *defel, int attnum)
00640 {
00641     List       *funcName = defGetQualifiedName(defel);
00642     Oid         typeId[4];
00643     Oid         retTypeId;
00644     int         nargs;
00645     Oid         procOid;
00646 
00647     retTypeId = INTERNALOID;
00648     typeId[0] = INTERNALOID;
00649     typeId[1] = INTERNALOID;
00650     typeId[2] = INTERNALOID;
00651     typeId[3] = INTERNALOID;
00652     switch (attnum)
00653     {
00654         case Anum_pg_ts_template_tmplinit:
00655             nargs = 1;
00656             break;
00657         case Anum_pg_ts_template_tmpllexize:
00658             nargs = 4;
00659             break;
00660         default:
00661             /* should not be here */
00662             elog(ERROR, "unrecognized attribute for text search template: %d",
00663                  attnum);
00664             nargs = 0;          /* keep compiler quiet */
00665     }
00666 
00667     procOid = LookupFuncName(funcName, nargs, typeId, false);
00668     if (get_func_rettype(procOid) != retTypeId)
00669         ereport(ERROR,
00670                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
00671                  errmsg("function %s should return type %s",
00672                         func_signature_string(funcName, nargs, NIL, typeId),
00673                         format_type_be(retTypeId))));
00674 
00675     return ObjectIdGetDatum(procOid);
00676 }
00677 
00678 /*
00679  * make pg_depend entries for a new pg_ts_template entry
00680  */
00681 static void
00682 makeTSTemplateDependencies(HeapTuple tuple)
00683 {
00684     Form_pg_ts_template tmpl = (Form_pg_ts_template) GETSTRUCT(tuple);
00685     ObjectAddress myself,
00686                 referenced;
00687 
00688     myself.classId = TSTemplateRelationId;
00689     myself.objectId = HeapTupleGetOid(tuple);
00690     myself.objectSubId = 0;
00691 
00692     /* dependency on namespace */
00693     referenced.classId = NamespaceRelationId;
00694     referenced.objectId = tmpl->tmplnamespace;
00695     referenced.objectSubId = 0;
00696     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
00697 
00698     /* dependency on extension */
00699     recordDependencyOnCurrentExtension(&myself, false);
00700 
00701     /* dependencies on functions */
00702     referenced.classId = ProcedureRelationId;
00703     referenced.objectSubId = 0;
00704 
00705     referenced.objectId = tmpl->tmpllexize;
00706     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
00707 
00708     if (OidIsValid(tmpl->tmplinit))
00709     {
00710         referenced.objectId = tmpl->tmplinit;
00711         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
00712     }
00713 }
00714 
00715 /*
00716  * CREATE TEXT SEARCH TEMPLATE
00717  */
00718 Oid
00719 DefineTSTemplate(List *names, List *parameters)
00720 {
00721     ListCell   *pl;
00722     Relation    tmplRel;
00723     HeapTuple   tup;
00724     Datum       values[Natts_pg_ts_template];
00725     bool        nulls[Natts_pg_ts_template];
00726     NameData    dname;
00727     int         i;
00728     Oid         tmplOid;
00729     Oid         namespaceoid;
00730     char       *tmplname;
00731 
00732     if (!superuser())
00733         ereport(ERROR,
00734                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00735                errmsg("must be superuser to create text search templates")));
00736 
00737     /* Convert list of names to a name and namespace */
00738     namespaceoid = QualifiedNameGetCreationNamespace(names, &tmplname);
00739 
00740     for (i = 0; i < Natts_pg_ts_template; i++)
00741     {
00742         nulls[i] = false;
00743         values[i] = ObjectIdGetDatum(InvalidOid);
00744     }
00745 
00746     namestrcpy(&dname, tmplname);
00747     values[Anum_pg_ts_template_tmplname - 1] = NameGetDatum(&dname);
00748     values[Anum_pg_ts_template_tmplnamespace - 1] = ObjectIdGetDatum(namespaceoid);
00749 
00750     /*
00751      * loop over the definition list and extract the information we need.
00752      */
00753     foreach(pl, parameters)
00754     {
00755         DefElem    *defel = (DefElem *) lfirst(pl);
00756 
00757         if (pg_strcasecmp(defel->defname, "init") == 0)
00758         {
00759             values[Anum_pg_ts_template_tmplinit - 1] =
00760                 get_ts_template_func(defel, Anum_pg_ts_template_tmplinit);
00761             nulls[Anum_pg_ts_template_tmplinit - 1] = false;
00762         }
00763         else if (pg_strcasecmp(defel->defname, "lexize") == 0)
00764         {
00765             values[Anum_pg_ts_template_tmpllexize - 1] =
00766                 get_ts_template_func(defel, Anum_pg_ts_template_tmpllexize);
00767             nulls[Anum_pg_ts_template_tmpllexize - 1] = false;
00768         }
00769         else
00770             ereport(ERROR,
00771                     (errcode(ERRCODE_SYNTAX_ERROR),
00772                errmsg("text search template parameter \"%s\" not recognized",
00773                       defel->defname)));
00774     }
00775 
00776     /*
00777      * Validation
00778      */
00779     if (!OidIsValid(DatumGetObjectId(values[Anum_pg_ts_template_tmpllexize - 1])))
00780         ereport(ERROR,
00781                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
00782                  errmsg("text search template lexize method is required")));
00783 
00784     /*
00785      * Looks good, insert
00786      */
00787 
00788     tmplRel = heap_open(TSTemplateRelationId, RowExclusiveLock);
00789 
00790     tup = heap_form_tuple(tmplRel->rd_att, values, nulls);
00791 
00792     tmplOid = simple_heap_insert(tmplRel, tup);
00793 
00794     CatalogUpdateIndexes(tmplRel, tup);
00795 
00796     makeTSTemplateDependencies(tup);
00797 
00798     /* Post creation hook for new text search template */
00799     InvokeObjectPostCreateHook(TSTemplateRelationId, tmplOid, 0);
00800 
00801     heap_freetuple(tup);
00802 
00803     heap_close(tmplRel, RowExclusiveLock);
00804 
00805     return tmplOid;
00806 }
00807 
00808 /*
00809  * Guts of TS template deletion.
00810  */
00811 void
00812 RemoveTSTemplateById(Oid tmplId)
00813 {
00814     Relation    relation;
00815     HeapTuple   tup;
00816 
00817     relation = heap_open(TSTemplateRelationId, RowExclusiveLock);
00818 
00819     tup = SearchSysCache1(TSTEMPLATEOID, ObjectIdGetDatum(tmplId));
00820 
00821     if (!HeapTupleIsValid(tup))
00822         elog(ERROR, "cache lookup failed for text search template %u",
00823              tmplId);
00824 
00825     simple_heap_delete(relation, &tup->t_self);
00826 
00827     ReleaseSysCache(tup);
00828 
00829     heap_close(relation, RowExclusiveLock);
00830 }
00831 
00832 /* ---------------------- TS Configuration commands -----------------------*/
00833 
00834 /*
00835  * Finds syscache tuple of configuration.
00836  * Returns NULL if no such cfg.
00837  */
00838 static HeapTuple
00839 GetTSConfigTuple(List *names)
00840 {
00841     HeapTuple   tup;
00842     Oid         cfgId;
00843 
00844     cfgId = get_ts_config_oid(names, true);
00845     if (!OidIsValid(cfgId))
00846         return NULL;
00847 
00848     tup = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(cfgId));
00849 
00850     if (!HeapTupleIsValid(tup)) /* should not happen */
00851         elog(ERROR, "cache lookup failed for text search configuration %u",
00852              cfgId);
00853 
00854     return tup;
00855 }
00856 
00857 /*
00858  * make pg_depend entries for a new or updated pg_ts_config entry
00859  *
00860  * Pass opened pg_ts_config_map relation if there might be any config map
00861  * entries for the config.
00862  */
00863 static void
00864 makeConfigurationDependencies(HeapTuple tuple, bool removeOld,
00865                               Relation mapRel)
00866 {
00867     Form_pg_ts_config cfg = (Form_pg_ts_config) GETSTRUCT(tuple);
00868     ObjectAddresses *addrs;
00869     ObjectAddress myself,
00870                 referenced;
00871 
00872     myself.classId = TSConfigRelationId;
00873     myself.objectId = HeapTupleGetOid(tuple);
00874     myself.objectSubId = 0;
00875 
00876     /* for ALTER case, first flush old dependencies, except extension deps */
00877     if (removeOld)
00878     {
00879         deleteDependencyRecordsFor(myself.classId, myself.objectId, true);
00880         deleteSharedDependencyRecordsFor(myself.classId, myself.objectId, 0);
00881     }
00882 
00883     /*
00884      * We use an ObjectAddresses list to remove possible duplicate
00885      * dependencies from the config map info.  The pg_ts_config items
00886      * shouldn't be duplicates, but might as well fold them all into one call.
00887      */
00888     addrs = new_object_addresses();
00889 
00890     /* dependency on namespace */
00891     referenced.classId = NamespaceRelationId;
00892     referenced.objectId = cfg->cfgnamespace;
00893     referenced.objectSubId = 0;
00894     add_exact_object_address(&referenced, addrs);
00895 
00896     /* dependency on owner */
00897     recordDependencyOnOwner(myself.classId, myself.objectId, cfg->cfgowner);
00898 
00899     /* dependency on extension */
00900     recordDependencyOnCurrentExtension(&myself, removeOld);
00901 
00902     /* dependency on parser */
00903     referenced.classId = TSParserRelationId;
00904     referenced.objectId = cfg->cfgparser;
00905     referenced.objectSubId = 0;
00906     add_exact_object_address(&referenced, addrs);
00907 
00908     /* dependencies on dictionaries listed in config map */
00909     if (mapRel)
00910     {
00911         ScanKeyData skey;
00912         SysScanDesc scan;
00913         HeapTuple   maptup;
00914 
00915         /* CCI to ensure we can see effects of caller's changes */
00916         CommandCounterIncrement();
00917 
00918         ScanKeyInit(&skey,
00919                     Anum_pg_ts_config_map_mapcfg,
00920                     BTEqualStrategyNumber, F_OIDEQ,
00921                     ObjectIdGetDatum(myself.objectId));
00922 
00923         scan = systable_beginscan(mapRel, TSConfigMapIndexId, true,
00924                                   SnapshotNow, 1, &skey);
00925 
00926         while (HeapTupleIsValid((maptup = systable_getnext(scan))))
00927         {
00928             Form_pg_ts_config_map cfgmap = (Form_pg_ts_config_map) GETSTRUCT(maptup);
00929 
00930             referenced.classId = TSDictionaryRelationId;
00931             referenced.objectId = cfgmap->mapdict;
00932             referenced.objectSubId = 0;
00933             add_exact_object_address(&referenced, addrs);
00934         }
00935 
00936         systable_endscan(scan);
00937     }
00938 
00939     /* Record 'em (this includes duplicate elimination) */
00940     record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
00941 
00942     free_object_addresses(addrs);
00943 }
00944 
00945 /*
00946  * CREATE TEXT SEARCH CONFIGURATION
00947  */
00948 Oid
00949 DefineTSConfiguration(List *names, List *parameters)
00950 {
00951     Relation    cfgRel;
00952     Relation    mapRel = NULL;
00953     HeapTuple   tup;
00954     Datum       values[Natts_pg_ts_config];
00955     bool        nulls[Natts_pg_ts_config];
00956     AclResult   aclresult;
00957     Oid         namespaceoid;
00958     char       *cfgname;
00959     NameData    cname;
00960     Oid         sourceOid = InvalidOid;
00961     Oid         prsOid = InvalidOid;
00962     Oid         cfgOid;
00963     ListCell   *pl;
00964 
00965     /* Convert list of names to a name and namespace */
00966     namespaceoid = QualifiedNameGetCreationNamespace(names, &cfgname);
00967 
00968     /* Check we have creation rights in target namespace */
00969     aclresult = pg_namespace_aclcheck(namespaceoid, GetUserId(), ACL_CREATE);
00970     if (aclresult != ACLCHECK_OK)
00971         aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
00972                        get_namespace_name(namespaceoid));
00973 
00974     /*
00975      * loop over the definition list and extract the information we need.
00976      */
00977     foreach(pl, parameters)
00978     {
00979         DefElem    *defel = (DefElem *) lfirst(pl);
00980 
00981         if (pg_strcasecmp(defel->defname, "parser") == 0)
00982             prsOid = get_ts_parser_oid(defGetQualifiedName(defel), false);
00983         else if (pg_strcasecmp(defel->defname, "copy") == 0)
00984             sourceOid = get_ts_config_oid(defGetQualifiedName(defel), false);
00985         else
00986             ereport(ERROR,
00987                     (errcode(ERRCODE_SYNTAX_ERROR),
00988                      errmsg("text search configuration parameter \"%s\" not recognized",
00989                             defel->defname)));
00990     }
00991 
00992     if (OidIsValid(sourceOid) && OidIsValid(prsOid))
00993         ereport(ERROR,
00994                 (errcode(ERRCODE_SYNTAX_ERROR),
00995                  errmsg("cannot specify both PARSER and COPY options")));
00996 
00997     /*
00998      * Look up source config if given.
00999      */
01000     if (OidIsValid(sourceOid))
01001     {
01002         Form_pg_ts_config cfg;
01003 
01004         tup = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(sourceOid));
01005         if (!HeapTupleIsValid(tup))
01006             elog(ERROR, "cache lookup failed for text search configuration %u",
01007                  sourceOid);
01008 
01009         cfg = (Form_pg_ts_config) GETSTRUCT(tup);
01010 
01011         /* use source's parser */
01012         prsOid = cfg->cfgparser;
01013 
01014         ReleaseSysCache(tup);
01015     }
01016 
01017     /*
01018      * Validation
01019      */
01020     if (!OidIsValid(prsOid))
01021         ereport(ERROR,
01022                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
01023                  errmsg("text search parser is required")));
01024 
01025     /*
01026      * Looks good, build tuple and insert
01027      */
01028     memset(values, 0, sizeof(values));
01029     memset(nulls, false, sizeof(nulls));
01030 
01031     namestrcpy(&cname, cfgname);
01032     values[Anum_pg_ts_config_cfgname - 1] = NameGetDatum(&cname);
01033     values[Anum_pg_ts_config_cfgnamespace - 1] = ObjectIdGetDatum(namespaceoid);
01034     values[Anum_pg_ts_config_cfgowner - 1] = ObjectIdGetDatum(GetUserId());
01035     values[Anum_pg_ts_config_cfgparser - 1] = ObjectIdGetDatum(prsOid);
01036 
01037     cfgRel = heap_open(TSConfigRelationId, RowExclusiveLock);
01038 
01039     tup = heap_form_tuple(cfgRel->rd_att, values, nulls);
01040 
01041     cfgOid = simple_heap_insert(cfgRel, tup);
01042 
01043     CatalogUpdateIndexes(cfgRel, tup);
01044 
01045     if (OidIsValid(sourceOid))
01046     {
01047         /*
01048          * Copy token-dicts map from source config
01049          */
01050         ScanKeyData skey;
01051         SysScanDesc scan;
01052         HeapTuple   maptup;
01053 
01054         mapRel = heap_open(TSConfigMapRelationId, RowExclusiveLock);
01055 
01056         ScanKeyInit(&skey,
01057                     Anum_pg_ts_config_map_mapcfg,
01058                     BTEqualStrategyNumber, F_OIDEQ,
01059                     ObjectIdGetDatum(sourceOid));
01060 
01061         scan = systable_beginscan(mapRel, TSConfigMapIndexId, true,
01062                                   SnapshotNow, 1, &skey);
01063 
01064         while (HeapTupleIsValid((maptup = systable_getnext(scan))))
01065         {
01066             Form_pg_ts_config_map cfgmap = (Form_pg_ts_config_map) GETSTRUCT(maptup);
01067             HeapTuple   newmaptup;
01068             Datum       mapvalues[Natts_pg_ts_config_map];
01069             bool        mapnulls[Natts_pg_ts_config_map];
01070 
01071             memset(mapvalues, 0, sizeof(mapvalues));
01072             memset(mapnulls, false, sizeof(mapnulls));
01073 
01074             mapvalues[Anum_pg_ts_config_map_mapcfg - 1] = cfgOid;
01075             mapvalues[Anum_pg_ts_config_map_maptokentype - 1] = cfgmap->maptokentype;
01076             mapvalues[Anum_pg_ts_config_map_mapseqno - 1] = cfgmap->mapseqno;
01077             mapvalues[Anum_pg_ts_config_map_mapdict - 1] = cfgmap->mapdict;
01078 
01079             newmaptup = heap_form_tuple(mapRel->rd_att, mapvalues, mapnulls);
01080 
01081             simple_heap_insert(mapRel, newmaptup);
01082 
01083             CatalogUpdateIndexes(mapRel, newmaptup);
01084 
01085             heap_freetuple(newmaptup);
01086         }
01087 
01088         systable_endscan(scan);
01089     }
01090 
01091     makeConfigurationDependencies(tup, false, mapRel);
01092 
01093     /* Post creation hook for new text search configuration */
01094     InvokeObjectPostCreateHook(TSConfigRelationId, cfgOid, 0);
01095 
01096     heap_freetuple(tup);
01097 
01098     if (mapRel)
01099         heap_close(mapRel, RowExclusiveLock);
01100     heap_close(cfgRel, RowExclusiveLock);
01101 
01102     return cfgOid;
01103 }
01104 
01105 /*
01106  * Guts of TS configuration deletion.
01107  */
01108 void
01109 RemoveTSConfigurationById(Oid cfgId)
01110 {
01111     Relation    relCfg,
01112                 relMap;
01113     HeapTuple   tup;
01114     ScanKeyData skey;
01115     SysScanDesc scan;
01116 
01117     /* Remove the pg_ts_config entry */
01118     relCfg = heap_open(TSConfigRelationId, RowExclusiveLock);
01119 
01120     tup = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(cfgId));
01121 
01122     if (!HeapTupleIsValid(tup))
01123         elog(ERROR, "cache lookup failed for text search dictionary %u",
01124              cfgId);
01125 
01126     simple_heap_delete(relCfg, &tup->t_self);
01127 
01128     ReleaseSysCache(tup);
01129 
01130     heap_close(relCfg, RowExclusiveLock);
01131 
01132     /* Remove any pg_ts_config_map entries */
01133     relMap = heap_open(TSConfigMapRelationId, RowExclusiveLock);
01134 
01135     ScanKeyInit(&skey,
01136                 Anum_pg_ts_config_map_mapcfg,
01137                 BTEqualStrategyNumber, F_OIDEQ,
01138                 ObjectIdGetDatum(cfgId));
01139 
01140     scan = systable_beginscan(relMap, TSConfigMapIndexId, true,
01141                               SnapshotNow, 1, &skey);
01142 
01143     while (HeapTupleIsValid((tup = systable_getnext(scan))))
01144     {
01145         simple_heap_delete(relMap, &tup->t_self);
01146     }
01147 
01148     systable_endscan(scan);
01149 
01150     heap_close(relMap, RowExclusiveLock);
01151 }
01152 
01153 /*
01154  * ALTER TEXT SEARCH CONFIGURATION - main entry point
01155  */
01156 Oid
01157 AlterTSConfiguration(AlterTSConfigurationStmt *stmt)
01158 {
01159     HeapTuple   tup;
01160     Oid         cfgId;
01161     Relation    relMap;
01162 
01163     /* Find the configuration */
01164     tup = GetTSConfigTuple(stmt->cfgname);
01165     if (!HeapTupleIsValid(tup))
01166         ereport(ERROR,
01167                 (errcode(ERRCODE_UNDEFINED_OBJECT),
01168                  errmsg("text search configuration \"%s\" does not exist",
01169                         NameListToString(stmt->cfgname))));
01170 
01171     cfgId = HeapTupleGetOid(tup);
01172 
01173     /* must be owner */
01174     if (!pg_ts_config_ownercheck(HeapTupleGetOid(tup), GetUserId()))
01175         aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSCONFIGURATION,
01176                        NameListToString(stmt->cfgname));
01177 
01178     relMap = heap_open(TSConfigMapRelationId, RowExclusiveLock);
01179 
01180     /* Add or drop mappings */
01181     if (stmt->dicts)
01182         MakeConfigurationMapping(stmt, tup, relMap);
01183     else if (stmt->tokentype)
01184         DropConfigurationMapping(stmt, tup, relMap);
01185 
01186     /* Update dependencies */
01187     makeConfigurationDependencies(tup, true, relMap);
01188 
01189     InvokeObjectPostAlterHook(TSConfigMapRelationId,
01190                               HeapTupleGetOid(tup), 0);
01191 
01192     heap_close(relMap, RowExclusiveLock);
01193 
01194     ReleaseSysCache(tup);
01195 
01196     return cfgId;
01197 }
01198 
01199 /*
01200  * Translate a list of token type names to an array of token type numbers
01201  */
01202 static int *
01203 getTokenTypes(Oid prsId, List *tokennames)
01204 {
01205     TSParserCacheEntry *prs = lookup_ts_parser_cache(prsId);
01206     LexDescr   *list;
01207     int        *res,
01208                 i,
01209                 ntoken;
01210     ListCell   *tn;
01211 
01212     ntoken = list_length(tokennames);
01213     if (ntoken == 0)
01214         return NULL;
01215     res = (int *) palloc(sizeof(int) * ntoken);
01216 
01217     if (!OidIsValid(prs->lextypeOid))
01218         elog(ERROR, "method lextype isn't defined for text search parser %u",
01219              prsId);
01220 
01221     /* lextype takes one dummy argument */
01222     list = (LexDescr *) DatumGetPointer(OidFunctionCall1(prs->lextypeOid,
01223                                                          (Datum) 0));
01224 
01225     i = 0;
01226     foreach(tn, tokennames)
01227     {
01228         Value      *val = (Value *) lfirst(tn);
01229         bool        found = false;
01230         int         j;
01231 
01232         j = 0;
01233         while (list && list[j].lexid)
01234         {
01235             /* XXX should we use pg_strcasecmp here? */
01236             if (strcmp(strVal(val), list[j].alias) == 0)
01237             {
01238                 res[i] = list[j].lexid;
01239                 found = true;
01240                 break;
01241             }
01242             j++;
01243         }
01244         if (!found)
01245             ereport(ERROR,
01246                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
01247                      errmsg("token type \"%s\" does not exist",
01248                             strVal(val))));
01249         i++;
01250     }
01251 
01252     return res;
01253 }
01254 
01255 /*
01256  * ALTER TEXT SEARCH CONFIGURATION ADD/ALTER MAPPING
01257  */
01258 static void
01259 MakeConfigurationMapping(AlterTSConfigurationStmt *stmt,
01260                          HeapTuple tup, Relation relMap)
01261 {
01262     Oid         cfgId = HeapTupleGetOid(tup);
01263     ScanKeyData skey[2];
01264     SysScanDesc scan;
01265     HeapTuple   maptup;
01266     int         i;
01267     int         j;
01268     Oid         prsId;
01269     int        *tokens,
01270                 ntoken;
01271     Oid        *dictIds;
01272     int         ndict;
01273     ListCell   *c;
01274 
01275     prsId = ((Form_pg_ts_config) GETSTRUCT(tup))->cfgparser;
01276 
01277     tokens = getTokenTypes(prsId, stmt->tokentype);
01278     ntoken = list_length(stmt->tokentype);
01279 
01280     if (stmt->override)
01281     {
01282         /*
01283          * delete maps for tokens if they exist and command was ALTER
01284          */
01285         for (i = 0; i < ntoken; i++)
01286         {
01287             ScanKeyInit(&skey[0],
01288                         Anum_pg_ts_config_map_mapcfg,
01289                         BTEqualStrategyNumber, F_OIDEQ,
01290                         ObjectIdGetDatum(cfgId));
01291             ScanKeyInit(&skey[1],
01292                         Anum_pg_ts_config_map_maptokentype,
01293                         BTEqualStrategyNumber, F_INT4EQ,
01294                         Int32GetDatum(tokens[i]));
01295 
01296             scan = systable_beginscan(relMap, TSConfigMapIndexId, true,
01297                                       SnapshotNow, 2, skey);
01298 
01299             while (HeapTupleIsValid((maptup = systable_getnext(scan))))
01300             {
01301                 simple_heap_delete(relMap, &maptup->t_self);
01302             }
01303 
01304             systable_endscan(scan);
01305         }
01306     }
01307 
01308     /*
01309      * Convert list of dictionary names to array of dict OIDs
01310      */
01311     ndict = list_length(stmt->dicts);
01312     dictIds = (Oid *) palloc(sizeof(Oid) * ndict);
01313     i = 0;
01314     foreach(c, stmt->dicts)
01315     {
01316         List       *names = (List *) lfirst(c);
01317 
01318         dictIds[i] = get_ts_dict_oid(names, false);
01319         i++;
01320     }
01321 
01322     if (stmt->replace)
01323     {
01324         /*
01325          * Replace a specific dictionary in existing entries
01326          */
01327         Oid         dictOld = dictIds[0],
01328                     dictNew = dictIds[1];
01329 
01330         ScanKeyInit(&skey[0],
01331                     Anum_pg_ts_config_map_mapcfg,
01332                     BTEqualStrategyNumber, F_OIDEQ,
01333                     ObjectIdGetDatum(cfgId));
01334 
01335         scan = systable_beginscan(relMap, TSConfigMapIndexId, true,
01336                                   SnapshotNow, 1, skey);
01337 
01338         while (HeapTupleIsValid((maptup = systable_getnext(scan))))
01339         {
01340             Form_pg_ts_config_map cfgmap = (Form_pg_ts_config_map) GETSTRUCT(maptup);
01341 
01342             /*
01343              * check if it's one of target token types
01344              */
01345             if (tokens)
01346             {
01347                 bool        tokmatch = false;
01348 
01349                 for (j = 0; j < ntoken; j++)
01350                 {
01351                     if (cfgmap->maptokentype == tokens[j])
01352                     {
01353                         tokmatch = true;
01354                         break;
01355                     }
01356                 }
01357                 if (!tokmatch)
01358                     continue;
01359             }
01360 
01361             /*
01362              * replace dictionary if match
01363              */
01364             if (cfgmap->mapdict == dictOld)
01365             {
01366                 Datum       repl_val[Natts_pg_ts_config_map];
01367                 bool        repl_null[Natts_pg_ts_config_map];
01368                 bool        repl_repl[Natts_pg_ts_config_map];
01369                 HeapTuple   newtup;
01370 
01371                 memset(repl_val, 0, sizeof(repl_val));
01372                 memset(repl_null, false, sizeof(repl_null));
01373                 memset(repl_repl, false, sizeof(repl_repl));
01374 
01375                 repl_val[Anum_pg_ts_config_map_mapdict - 1] = ObjectIdGetDatum(dictNew);
01376                 repl_repl[Anum_pg_ts_config_map_mapdict - 1] = true;
01377 
01378                 newtup = heap_modify_tuple(maptup,
01379                                            RelationGetDescr(relMap),
01380                                            repl_val, repl_null, repl_repl);
01381                 simple_heap_update(relMap, &newtup->t_self, newtup);
01382 
01383                 CatalogUpdateIndexes(relMap, newtup);
01384             }
01385         }
01386 
01387         systable_endscan(scan);
01388     }
01389     else
01390     {
01391         /*
01392          * Insertion of new entries
01393          */
01394         for (i = 0; i < ntoken; i++)
01395         {
01396             for (j = 0; j < ndict; j++)
01397             {
01398                 Datum       values[Natts_pg_ts_config_map];
01399                 bool        nulls[Natts_pg_ts_config_map];
01400 
01401                 memset(nulls, false, sizeof(nulls));
01402                 values[Anum_pg_ts_config_map_mapcfg - 1] = ObjectIdGetDatum(cfgId);
01403                 values[Anum_pg_ts_config_map_maptokentype - 1] = Int32GetDatum(tokens[i]);
01404                 values[Anum_pg_ts_config_map_mapseqno - 1] = Int32GetDatum(j + 1);
01405                 values[Anum_pg_ts_config_map_mapdict - 1] = ObjectIdGetDatum(dictIds[j]);
01406 
01407                 tup = heap_form_tuple(relMap->rd_att, values, nulls);
01408                 simple_heap_insert(relMap, tup);
01409                 CatalogUpdateIndexes(relMap, tup);
01410 
01411                 heap_freetuple(tup);
01412             }
01413         }
01414     }
01415 }
01416 
01417 /*
01418  * ALTER TEXT SEARCH CONFIGURATION DROP MAPPING
01419  */
01420 static void
01421 DropConfigurationMapping(AlterTSConfigurationStmt *stmt,
01422                          HeapTuple tup, Relation relMap)
01423 {
01424     Oid         cfgId = HeapTupleGetOid(tup);
01425     ScanKeyData skey[2];
01426     SysScanDesc scan;
01427     HeapTuple   maptup;
01428     int         i;
01429     Oid         prsId;
01430     int        *tokens;
01431     ListCell   *c;
01432 
01433     prsId = ((Form_pg_ts_config) GETSTRUCT(tup))->cfgparser;
01434 
01435     tokens = getTokenTypes(prsId, stmt->tokentype);
01436 
01437     i = 0;
01438     foreach(c, stmt->tokentype)
01439     {
01440         Value      *val = (Value *) lfirst(c);
01441         bool        found = false;
01442 
01443         ScanKeyInit(&skey[0],
01444                     Anum_pg_ts_config_map_mapcfg,
01445                     BTEqualStrategyNumber, F_OIDEQ,
01446                     ObjectIdGetDatum(cfgId));
01447         ScanKeyInit(&skey[1],
01448                     Anum_pg_ts_config_map_maptokentype,
01449                     BTEqualStrategyNumber, F_INT4EQ,
01450                     Int32GetDatum(tokens[i]));
01451 
01452         scan = systable_beginscan(relMap, TSConfigMapIndexId, true,
01453                                   SnapshotNow, 2, skey);
01454 
01455         while (HeapTupleIsValid((maptup = systable_getnext(scan))))
01456         {
01457             simple_heap_delete(relMap, &maptup->t_self);
01458             found = true;
01459         }
01460 
01461         systable_endscan(scan);
01462 
01463         if (!found)
01464         {
01465             if (!stmt->missing_ok)
01466             {
01467                 ereport(ERROR,
01468                         (errcode(ERRCODE_UNDEFINED_OBJECT),
01469                        errmsg("mapping for token type \"%s\" does not exist",
01470                               strVal(val))));
01471             }
01472             else
01473             {
01474                 ereport(NOTICE,
01475                         (errmsg("mapping for token type \"%s\" does not exist, skipping",
01476                                 strVal(val))));
01477             }
01478         }
01479 
01480         i++;
01481     }
01482 }
01483 
01484 
01485 /*
01486  * Serialize dictionary options, producing a TEXT datum from a List of DefElem
01487  *
01488  * This is used to form the value stored in pg_ts_dict.dictinitoption.
01489  * For the convenience of pg_dump, the output is formatted exactly as it
01490  * would need to appear in CREATE TEXT SEARCH DICTIONARY to reproduce the
01491  * same options.
01492  *
01493  * Note that we assume that only the textual representation of an option's
01494  * value is interesting --- hence, non-string DefElems get forced to strings.
01495  */
01496 text *
01497 serialize_deflist(List *deflist)
01498 {
01499     text       *result;
01500     StringInfoData buf;
01501     ListCell   *l;
01502 
01503     initStringInfo(&buf);
01504 
01505     foreach(l, deflist)
01506     {
01507         DefElem    *defel = (DefElem *) lfirst(l);
01508         char       *val = defGetString(defel);
01509 
01510         appendStringInfo(&buf, "%s = ",
01511                          quote_identifier(defel->defname));
01512         /* If backslashes appear, force E syntax to determine their handling */
01513         if (strchr(val, '\\'))
01514             appendStringInfoChar(&buf, ESCAPE_STRING_SYNTAX);
01515         appendStringInfoChar(&buf, '\'');
01516         while (*val)
01517         {
01518             char        ch = *val++;
01519 
01520             if (SQL_STR_DOUBLE(ch, true))
01521                 appendStringInfoChar(&buf, ch);
01522             appendStringInfoChar(&buf, ch);
01523         }
01524         appendStringInfoChar(&buf, '\'');
01525         if (lnext(l) != NULL)
01526             appendStringInfo(&buf, ", ");
01527     }
01528 
01529     result = cstring_to_text_with_len(buf.data, buf.len);
01530     pfree(buf.data);
01531     return result;
01532 }
01533 
01534 /*
01535  * Deserialize dictionary options, reconstructing a List of DefElem from TEXT
01536  *
01537  * This is also used for prsheadline options, so for backward compatibility
01538  * we need to accept a few things serialize_deflist() will never emit:
01539  * in particular, unquoted and double-quoted values.
01540  */
01541 List *
01542 deserialize_deflist(Datum txt)
01543 {
01544     text       *in = DatumGetTextP(txt);        /* in case it's toasted */
01545     List       *result = NIL;
01546     int         len = VARSIZE(in) - VARHDRSZ;
01547     char       *ptr,
01548                *endptr,
01549                *workspace,
01550                *wsptr = NULL,
01551                *startvalue = NULL;
01552     typedef enum
01553     {
01554         CS_WAITKEY,
01555         CS_INKEY,
01556         CS_INQKEY,
01557         CS_WAITEQ,
01558         CS_WAITVALUE,
01559         CS_INSQVALUE,
01560         CS_INDQVALUE,
01561         CS_INWVALUE
01562     } ds_state;
01563     ds_state    state = CS_WAITKEY;
01564 
01565     workspace = (char *) palloc(len + 1);       /* certainly enough room */
01566     ptr = VARDATA(in);
01567     endptr = ptr + len;
01568     for (; ptr < endptr; ptr++)
01569     {
01570         switch (state)
01571         {
01572             case CS_WAITKEY:
01573                 if (isspace((unsigned char) *ptr) || *ptr == ',')
01574                     continue;
01575                 if (*ptr == '"')
01576                 {
01577                     wsptr = workspace;
01578                     state = CS_INQKEY;
01579                 }
01580                 else
01581                 {
01582                     wsptr = workspace;
01583                     *wsptr++ = *ptr;
01584                     state = CS_INKEY;
01585                 }
01586                 break;
01587             case CS_INKEY:
01588                 if (isspace((unsigned char) *ptr))
01589                 {
01590                     *wsptr++ = '\0';
01591                     state = CS_WAITEQ;
01592                 }
01593                 else if (*ptr == '=')
01594                 {
01595                     *wsptr++ = '\0';
01596                     state = CS_WAITVALUE;
01597                 }
01598                 else
01599                 {
01600                     *wsptr++ = *ptr;
01601                 }
01602                 break;
01603             case CS_INQKEY:
01604                 if (*ptr == '"')
01605                 {
01606                     if (ptr + 1 < endptr && ptr[1] == '"')
01607                     {
01608                         /* copy only one of the two quotes */
01609                         *wsptr++ = *ptr++;
01610                     }
01611                     else
01612                     {
01613                         *wsptr++ = '\0';
01614                         state = CS_WAITEQ;
01615                     }
01616                 }
01617                 else
01618                 {
01619                     *wsptr++ = *ptr;
01620                 }
01621                 break;
01622             case CS_WAITEQ:
01623                 if (*ptr == '=')
01624                     state = CS_WAITVALUE;
01625                 else if (!isspace((unsigned char) *ptr))
01626                     ereport(ERROR,
01627                             (errcode(ERRCODE_SYNTAX_ERROR),
01628                              errmsg("invalid parameter list format: \"%s\"",
01629                                     text_to_cstring(in))));
01630                 break;
01631             case CS_WAITVALUE:
01632                 if (*ptr == '\'')
01633                 {
01634                     startvalue = wsptr;
01635                     state = CS_INSQVALUE;
01636                 }
01637                 else if (*ptr == 'E' && ptr + 1 < endptr && ptr[1] == '\'')
01638                 {
01639                     ptr++;
01640                     startvalue = wsptr;
01641                     state = CS_INSQVALUE;
01642                 }
01643                 else if (*ptr == '"')
01644                 {
01645                     startvalue = wsptr;
01646                     state = CS_INDQVALUE;
01647                 }
01648                 else if (!isspace((unsigned char) *ptr))
01649                 {
01650                     startvalue = wsptr;
01651                     *wsptr++ = *ptr;
01652                     state = CS_INWVALUE;
01653                 }
01654                 break;
01655             case CS_INSQVALUE:
01656                 if (*ptr == '\'')
01657                 {
01658                     if (ptr + 1 < endptr && ptr[1] == '\'')
01659                     {
01660                         /* copy only one of the two quotes */
01661                         *wsptr++ = *ptr++;
01662                     }
01663                     else
01664                     {
01665                         *wsptr++ = '\0';
01666                         result = lappend(result,
01667                                          makeDefElem(pstrdup(workspace),
01668                                   (Node *) makeString(pstrdup(startvalue))));
01669                         state = CS_WAITKEY;
01670                     }
01671                 }
01672                 else if (*ptr == '\\')
01673                 {
01674                     if (ptr + 1 < endptr && ptr[1] == '\\')
01675                     {
01676                         /* copy only one of the two backslashes */
01677                         *wsptr++ = *ptr++;
01678                     }
01679                     else
01680                         *wsptr++ = *ptr;
01681                 }
01682                 else
01683                 {
01684                     *wsptr++ = *ptr;
01685                 }
01686                 break;
01687             case CS_INDQVALUE:
01688                 if (*ptr == '"')
01689                 {
01690                     if (ptr + 1 < endptr && ptr[1] == '"')
01691                     {
01692                         /* copy only one of the two quotes */
01693                         *wsptr++ = *ptr++;
01694                     }
01695                     else
01696                     {
01697                         *wsptr++ = '\0';
01698                         result = lappend(result,
01699                                          makeDefElem(pstrdup(workspace),
01700                                   (Node *) makeString(pstrdup(startvalue))));
01701                         state = CS_WAITKEY;
01702                     }
01703                 }
01704                 else
01705                 {
01706                     *wsptr++ = *ptr;
01707                 }
01708                 break;
01709             case CS_INWVALUE:
01710                 if (*ptr == ',' || isspace((unsigned char) *ptr))
01711                 {
01712                     *wsptr++ = '\0';
01713                     result = lappend(result,
01714                                      makeDefElem(pstrdup(workspace),
01715                                   (Node *) makeString(pstrdup(startvalue))));
01716                     state = CS_WAITKEY;
01717                 }
01718                 else
01719                 {
01720                     *wsptr++ = *ptr;
01721                 }
01722                 break;
01723             default:
01724                 elog(ERROR, "unrecognized deserialize_deflist state: %d",
01725                      state);
01726         }
01727     }
01728 
01729     if (state == CS_INWVALUE)
01730     {
01731         *wsptr++ = '\0';
01732         result = lappend(result,
01733                          makeDefElem(pstrdup(workspace),
01734                                   (Node *) makeString(pstrdup(startvalue))));
01735     }
01736     else if (state != CS_WAITKEY)
01737         ereport(ERROR,
01738                 (errcode(ERRCODE_SYNTAX_ERROR),
01739                  errmsg("invalid parameter list format: \"%s\"",
01740                         text_to_cstring(in))));
01741 
01742     pfree(workspace);
01743 
01744     return result;
01745 }