1 /*-------------------------------------------------------------------------
5 * Routines for tsearch manipulation commands
7 * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
12 * src/backend/commands/tsearchcmds.c
14 *-------------------------------------------------------------------------
20 #include "access/heapam.h"
21 #include "access/genam.h"
22 #include "access/xact.h"
23 #include "catalog/dependency.h"
24 #include "catalog/indexing.h"
25 #include "catalog/objectaccess.h"
26 #include "catalog/pg_namespace.h"
27 #include "catalog/pg_proc.h"
28 #include "catalog/pg_ts_config.h"
29 #include "catalog/pg_ts_config_map.h"
30 #include "catalog/pg_ts_dict.h"
31 #include "catalog/pg_ts_parser.h"
32 #include "catalog/pg_ts_template.h"
33 #include "catalog/pg_type.h"
34 #include "commands/alter.h"
35 #include "commands/defrem.h"
36 #include "miscadmin.h"
37 #include "nodes/makefuncs.h"
38 #include "parser/parse_func.h"
39 #include "tsearch/ts_cache.h"
40 #include "tsearch/ts_utils.h"
41 #include "utils/builtins.h"
42 #include "utils/fmgroids.h"
43 #include "utils/lsyscache.h"
44 #include "utils/rel.h"
45 #include "utils/syscache.h"
46 #include "utils/tqual.h"
49 static void MakeConfigurationMapping(AlterTSConfigurationStmt *stmt,
50 HeapTuple tup, Relation relMap);
51 static void DropConfigurationMapping(AlterTSConfigurationStmt *stmt,
52 HeapTuple tup, Relation relMap);
55 /* --------------------- TS Parser commands ------------------------ */
58 * lookup a parser support function and return its OID (as a Datum)
60 * attnum is the pg_ts_parser column the function will go into
63 get_ts_parser_func(DefElem *defel, int attnum)
65 List *funcName = defGetQualifiedName(defel);
71 retTypeId = INTERNALOID; /* correct for most */
72 typeId[0] = INTERNALOID;
75 case Anum_pg_ts_parser_prsstart:
79 case Anum_pg_ts_parser_prstoken:
81 typeId[1] = INTERNALOID;
82 typeId[2] = INTERNALOID;
84 case Anum_pg_ts_parser_prsend:
88 case Anum_pg_ts_parser_prsheadline:
90 typeId[1] = INTERNALOID;
91 typeId[2] = TSQUERYOID;
93 case Anum_pg_ts_parser_prslextype:
97 * Note: because the lextype method returns type internal, it must
98 * have an internal-type argument for security reasons. The
99 * argument is not actually used, but is just passed as a zero.
103 /* should not be here */
104 elog(ERROR, "unrecognized attribute for text search parser: %d",
106 nargs = 0; /* keep compiler quiet */
109 procOid = LookupFuncName(funcName, nargs, typeId, false);
110 if (get_func_rettype(procOid) != retTypeId)
112 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
113 errmsg("function %s should return type %s",
114 func_signature_string(funcName, nargs, NIL, typeId),
115 format_type_be(retTypeId))));
117 return ObjectIdGetDatum(procOid);
121 * make pg_depend entries for a new pg_ts_parser entry
124 makeParserDependencies(HeapTuple tuple)
126 Form_pg_ts_parser prs = (Form_pg_ts_parser) GETSTRUCT(tuple);
127 ObjectAddress myself,
130 myself.classId = TSParserRelationId;
131 myself.objectId = HeapTupleGetOid(tuple);
132 myself.objectSubId = 0;
134 /* dependency on namespace */
135 referenced.classId = NamespaceRelationId;
136 referenced.objectId = prs->prsnamespace;
137 referenced.objectSubId = 0;
138 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
140 /* dependency on extension */
141 recordDependencyOnCurrentExtension(&myself, false);
143 /* dependencies on functions */
144 referenced.classId = ProcedureRelationId;
145 referenced.objectSubId = 0;
147 referenced.objectId = prs->prsstart;
148 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
150 referenced.objectId = prs->prstoken;
151 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
153 referenced.objectId = prs->prsend;
154 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
156 referenced.objectId = prs->prslextype;
157 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
159 if (OidIsValid(prs->prsheadline))
161 referenced.objectId = prs->prsheadline;
162 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
167 * CREATE TEXT SEARCH PARSER
170 DefineTSParser(List *names, List *parameters)
176 Datum values[Natts_pg_ts_parser];
177 bool nulls[Natts_pg_ts_parser];
184 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
185 errmsg("must be superuser to create text search parsers")));
187 /* Convert list of names to a name and namespace */
188 namespaceoid = QualifiedNameGetCreationNamespace(names, &prsname);
190 /* initialize tuple fields with name/namespace */
191 memset(values, 0, sizeof(values));
192 memset(nulls, false, sizeof(nulls));
194 namestrcpy(&pname, prsname);
195 values[Anum_pg_ts_parser_prsname - 1] = NameGetDatum(&pname);
196 values[Anum_pg_ts_parser_prsnamespace - 1] = ObjectIdGetDatum(namespaceoid);
199 * loop over the definition list and extract the information we need.
201 foreach(pl, parameters)
203 DefElem *defel = (DefElem *) lfirst(pl);
205 if (pg_strcasecmp(defel->defname, "start") == 0)
207 values[Anum_pg_ts_parser_prsstart - 1] =
208 get_ts_parser_func(defel, Anum_pg_ts_parser_prsstart);
210 else if (pg_strcasecmp(defel->defname, "gettoken") == 0)
212 values[Anum_pg_ts_parser_prstoken - 1] =
213 get_ts_parser_func(defel, Anum_pg_ts_parser_prstoken);
215 else if (pg_strcasecmp(defel->defname, "end") == 0)
217 values[Anum_pg_ts_parser_prsend - 1] =
218 get_ts_parser_func(defel, Anum_pg_ts_parser_prsend);
220 else if (pg_strcasecmp(defel->defname, "headline") == 0)
222 values[Anum_pg_ts_parser_prsheadline - 1] =
223 get_ts_parser_func(defel, Anum_pg_ts_parser_prsheadline);
225 else if (pg_strcasecmp(defel->defname, "lextypes") == 0)
227 values[Anum_pg_ts_parser_prslextype - 1] =
228 get_ts_parser_func(defel, Anum_pg_ts_parser_prslextype);
232 (errcode(ERRCODE_SYNTAX_ERROR),
233 errmsg("text search parser parameter \"%s\" not recognized",
240 if (!OidIsValid(DatumGetObjectId(values[Anum_pg_ts_parser_prsstart - 1])))
242 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
243 errmsg("text search parser start method is required")));
245 if (!OidIsValid(DatumGetObjectId(values[Anum_pg_ts_parser_prstoken - 1])))
247 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
248 errmsg("text search parser gettoken method is required")));
250 if (!OidIsValid(DatumGetObjectId(values[Anum_pg_ts_parser_prsend - 1])))
252 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
253 errmsg("text search parser end method is required")));
255 if (!OidIsValid(DatumGetObjectId(values[Anum_pg_ts_parser_prslextype - 1])))
257 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
258 errmsg("text search parser lextypes method is required")));
263 prsRel = heap_open(TSParserRelationId, RowExclusiveLock);
265 tup = heap_form_tuple(prsRel->rd_att, values, nulls);
267 prsOid = simple_heap_insert(prsRel, tup);
269 CatalogUpdateIndexes(prsRel, tup);
271 makeParserDependencies(tup);
273 /* Post creation hook for new text search parser */
274 InvokeObjectAccessHook(OAT_POST_CREATE, TSParserRelationId, prsOid, 0);
278 heap_close(prsRel, RowExclusiveLock);
282 * Guts of TS parser deletion.
285 RemoveTSParserById(Oid prsId)
290 relation = heap_open(TSParserRelationId, RowExclusiveLock);
292 tup = SearchSysCache1(TSPARSEROID, ObjectIdGetDatum(prsId));
294 if (!HeapTupleIsValid(tup))
295 elog(ERROR, "cache lookup failed for text search parser %u", prsId);
297 simple_heap_delete(relation, &tup->t_self);
299 ReleaseSysCache(tup);
301 heap_close(relation, RowExclusiveLock);
305 * ALTER TEXT SEARCH PARSER RENAME
308 RenameTSParser(List *oldname, const char *newname)
317 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
318 errmsg("must be superuser to rename text search parsers")));
320 rel = heap_open(TSParserRelationId, RowExclusiveLock);
322 prsId = get_ts_parser_oid(oldname, false);
324 tup = SearchSysCacheCopy1(TSPARSEROID, ObjectIdGetDatum(prsId));
326 if (!HeapTupleIsValid(tup)) /* should not happen */
327 elog(ERROR, "cache lookup failed for text search parser %u", prsId);
329 namespaceOid = ((Form_pg_ts_parser) GETSTRUCT(tup))->prsnamespace;
331 if (SearchSysCacheExists2(TSPARSERNAMENSP,
332 PointerGetDatum(newname),
333 ObjectIdGetDatum(namespaceOid)))
335 (errcode(ERRCODE_DUPLICATE_OBJECT),
336 errmsg("text search parser \"%s\" already exists",
339 namestrcpy(&(((Form_pg_ts_parser) GETSTRUCT(tup))->prsname), newname);
340 simple_heap_update(rel, &tup->t_self, tup);
341 CatalogUpdateIndexes(rel, tup);
343 heap_close(rel, NoLock);
348 * ALTER TEXT SEARCH PARSER any_name SET SCHEMA name
351 AlterTSParserNamespace(List *name, const char *newschema)
357 rel = heap_open(TSParserRelationId, RowExclusiveLock);
359 prsId = get_ts_parser_oid(name, false);
362 nspOid = LookupCreationNamespace(newschema);
364 AlterObjectNamespace(rel, TSPARSEROID, TSPARSERNAMENSP,
366 Anum_pg_ts_parser_prsname,
367 Anum_pg_ts_parser_prsnamespace,
370 heap_close(rel, RowExclusiveLock);
374 AlterTSParserNamespace_oid(Oid prsId, Oid newNspOid)
379 rel = heap_open(TSParserRelationId, RowExclusiveLock);
382 AlterObjectNamespace(rel, TSPARSEROID, TSPARSERNAMENSP,
384 Anum_pg_ts_parser_prsname,
385 Anum_pg_ts_parser_prsnamespace,
388 heap_close(rel, RowExclusiveLock);
393 /* ---------------------- TS Dictionary commands -----------------------*/
396 * make pg_depend entries for a new pg_ts_dict entry
399 makeDictionaryDependencies(HeapTuple tuple)
401 Form_pg_ts_dict dict = (Form_pg_ts_dict) GETSTRUCT(tuple);
402 ObjectAddress myself,
405 myself.classId = TSDictionaryRelationId;
406 myself.objectId = HeapTupleGetOid(tuple);
407 myself.objectSubId = 0;
409 /* dependency on namespace */
410 referenced.classId = NamespaceRelationId;
411 referenced.objectId = dict->dictnamespace;
412 referenced.objectSubId = 0;
413 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
415 /* dependency on owner */
416 recordDependencyOnOwner(myself.classId, myself.objectId, dict->dictowner);
418 /* dependency on extension */
419 recordDependencyOnCurrentExtension(&myself, false);
421 /* dependency on template */
422 referenced.classId = TSTemplateRelationId;
423 referenced.objectId = dict->dicttemplate;
424 referenced.objectSubId = 0;
425 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
429 * verify that a template's init method accepts a proposed option list
432 verify_dictoptions(Oid tmplId, List *dictoptions)
435 Form_pg_ts_template tform;
439 * Suppress this test when running in a standalone backend. This is a
440 * hack to allow initdb to create prefab dictionaries that might not
441 * actually be usable in template1's encoding (due to using external files
442 * that can't be translated into template1's encoding). We want to create
443 * them anyway, since they might be usable later in other databases.
445 if (!IsUnderPostmaster)
448 tup = SearchSysCache1(TSTEMPLATEOID, ObjectIdGetDatum(tmplId));
449 if (!HeapTupleIsValid(tup)) /* should not happen */
450 elog(ERROR, "cache lookup failed for text search template %u",
452 tform = (Form_pg_ts_template) GETSTRUCT(tup);
454 initmethod = tform->tmplinit;
456 if (!OidIsValid(initmethod))
458 /* If there is no init method, disallow any options */
461 (errcode(ERRCODE_SYNTAX_ERROR),
462 errmsg("text search template \"%s\" does not accept options",
463 NameStr(tform->tmplname))));
468 * Copy the options just in case init method thinks it can scribble on
471 dictoptions = copyObject(dictoptions);
474 * Call the init method and see if it complains. We don't worry about
475 * it leaking memory, since our command will soon be over anyway.
477 (void) OidFunctionCall1(initmethod, PointerGetDatum(dictoptions));
480 ReleaseSysCache(tup);
484 * CREATE TEXT SEARCH DICTIONARY
487 DefineTSDictionary(List *names, List *parameters)
492 Datum values[Natts_pg_ts_dict];
493 bool nulls[Natts_pg_ts_dict];
495 Oid templId = InvalidOid;
496 List *dictoptions = NIL;
502 /* Convert list of names to a name and namespace */
503 namespaceoid = QualifiedNameGetCreationNamespace(names, &dictname);
505 /* Check we have creation rights in target namespace */
506 aclresult = pg_namespace_aclcheck(namespaceoid, GetUserId(), ACL_CREATE);
507 if (aclresult != ACLCHECK_OK)
508 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
509 get_namespace_name(namespaceoid));
512 * loop over the definition list and extract the information we need.
514 foreach(pl, parameters)
516 DefElem *defel = (DefElem *) lfirst(pl);
518 if (pg_strcasecmp(defel->defname, "template") == 0)
520 templId = get_ts_template_oid(defGetQualifiedName(defel), false);
524 /* Assume it's an option for the dictionary itself */
525 dictoptions = lappend(dictoptions, defel);
532 if (!OidIsValid(templId))
534 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
535 errmsg("text search template is required")));
537 verify_dictoptions(templId, dictoptions);
542 memset(values, 0, sizeof(values));
543 memset(nulls, false, sizeof(nulls));
545 namestrcpy(&dname, dictname);
546 values[Anum_pg_ts_dict_dictname - 1] = NameGetDatum(&dname);
547 values[Anum_pg_ts_dict_dictnamespace - 1] = ObjectIdGetDatum(namespaceoid);
548 values[Anum_pg_ts_dict_dictowner - 1] = ObjectIdGetDatum(GetUserId());
549 values[Anum_pg_ts_dict_dicttemplate - 1] = ObjectIdGetDatum(templId);
551 values[Anum_pg_ts_dict_dictinitoption - 1] =
552 PointerGetDatum(serialize_deflist(dictoptions));
554 nulls[Anum_pg_ts_dict_dictinitoption - 1] = true;
556 dictRel = heap_open(TSDictionaryRelationId, RowExclusiveLock);
558 tup = heap_form_tuple(dictRel->rd_att, values, nulls);
560 dictOid = simple_heap_insert(dictRel, tup);
562 CatalogUpdateIndexes(dictRel, tup);
564 makeDictionaryDependencies(tup);
566 /* Post creation hook for new text search dictionary */
567 InvokeObjectAccessHook(OAT_POST_CREATE,
568 TSDictionaryRelationId, dictOid, 0);
572 heap_close(dictRel, RowExclusiveLock);
576 * ALTER TEXT SEARCH DICTIONARY RENAME
579 RenameTSDictionary(List *oldname, const char *newname)
587 rel = heap_open(TSDictionaryRelationId, RowExclusiveLock);
589 dictId = get_ts_dict_oid(oldname, false);
591 tup = SearchSysCacheCopy1(TSDICTOID, ObjectIdGetDatum(dictId));
593 if (!HeapTupleIsValid(tup)) /* should not happen */
594 elog(ERROR, "cache lookup failed for text search dictionary %u",
597 namespaceOid = ((Form_pg_ts_dict) GETSTRUCT(tup))->dictnamespace;
599 if (SearchSysCacheExists2(TSDICTNAMENSP,
600 PointerGetDatum(newname),
601 ObjectIdGetDatum(namespaceOid)))
603 (errcode(ERRCODE_DUPLICATE_OBJECT),
604 errmsg("text search dictionary \"%s\" already exists",
608 if (!pg_ts_dict_ownercheck(dictId, GetUserId()))
609 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSDICTIONARY,
610 NameListToString(oldname));
612 /* must have CREATE privilege on namespace */
613 aclresult = pg_namespace_aclcheck(namespaceOid, GetUserId(), ACL_CREATE);
614 if (aclresult != ACLCHECK_OK)
615 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
616 get_namespace_name(namespaceOid));
618 namestrcpy(&(((Form_pg_ts_dict) GETSTRUCT(tup))->dictname), newname);
619 simple_heap_update(rel, &tup->t_self, tup);
620 CatalogUpdateIndexes(rel, tup);
622 heap_close(rel, NoLock);
627 * ALTER TEXT SEARCH DICTIONARY any_name SET SCHEMA name
630 AlterTSDictionaryNamespace(List *name, const char *newschema)
636 rel = heap_open(TSDictionaryRelationId, RowExclusiveLock);
638 dictId = get_ts_dict_oid(name, false);
641 nspOid = LookupCreationNamespace(newschema);
643 AlterObjectNamespace(rel, TSDICTOID, TSDICTNAMENSP,
645 Anum_pg_ts_dict_dictname,
646 Anum_pg_ts_dict_dictnamespace,
647 Anum_pg_ts_dict_dictowner,
648 ACL_KIND_TSDICTIONARY);
650 heap_close(rel, RowExclusiveLock);
654 AlterTSDictionaryNamespace_oid(Oid dictId, Oid newNspOid)
659 rel = heap_open(TSDictionaryRelationId, RowExclusiveLock);
662 AlterObjectNamespace(rel, TSDICTOID, TSDICTNAMENSP,
664 Anum_pg_ts_dict_dictname,
665 Anum_pg_ts_dict_dictnamespace,
666 Anum_pg_ts_dict_dictowner,
667 ACL_KIND_TSDICTIONARY);
669 heap_close(rel, RowExclusiveLock);
675 * Guts of TS dictionary deletion.
678 RemoveTSDictionaryById(Oid dictId)
683 relation = heap_open(TSDictionaryRelationId, RowExclusiveLock);
685 tup = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(dictId));
687 if (!HeapTupleIsValid(tup))
688 elog(ERROR, "cache lookup failed for text search dictionary %u",
691 simple_heap_delete(relation, &tup->t_self);
693 ReleaseSysCache(tup);
695 heap_close(relation, RowExclusiveLock);
699 * ALTER TEXT SEARCH DICTIONARY
702 AlterTSDictionary(AlterTSDictionaryStmt *stmt)
712 Datum repl_val[Natts_pg_ts_dict];
713 bool repl_null[Natts_pg_ts_dict];
714 bool repl_repl[Natts_pg_ts_dict];
716 dictId = get_ts_dict_oid(stmt->dictname, false);
718 rel = heap_open(TSDictionaryRelationId, RowExclusiveLock);
720 tup = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(dictId));
722 if (!HeapTupleIsValid(tup))
723 elog(ERROR, "cache lookup failed for text search dictionary %u",
727 if (!pg_ts_dict_ownercheck(dictId, GetUserId()))
728 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSDICTIONARY,
729 NameListToString(stmt->dictname));
731 /* deserialize the existing set of options */
732 opt = SysCacheGetAttr(TSDICTOID, tup,
733 Anum_pg_ts_dict_dictinitoption,
738 dictoptions = deserialize_deflist(opt);
741 * Modify the options list as per specified changes
743 foreach(pl, stmt->options)
745 DefElem *defel = (DefElem *) lfirst(pl);
751 * Remove any matches ...
754 for (cell = list_head(dictoptions); cell; cell = next)
756 DefElem *oldel = (DefElem *) lfirst(cell);
759 if (pg_strcasecmp(oldel->defname, defel->defname) == 0)
760 dictoptions = list_delete_cell(dictoptions, cell, prev);
766 * and add new value if it's got one
769 dictoptions = lappend(dictoptions, defel);
775 verify_dictoptions(((Form_pg_ts_dict) GETSTRUCT(tup))->dicttemplate,
781 memset(repl_val, 0, sizeof(repl_val));
782 memset(repl_null, false, sizeof(repl_null));
783 memset(repl_repl, false, sizeof(repl_repl));
786 repl_val[Anum_pg_ts_dict_dictinitoption - 1] =
787 PointerGetDatum(serialize_deflist(dictoptions));
789 repl_null[Anum_pg_ts_dict_dictinitoption - 1] = true;
790 repl_repl[Anum_pg_ts_dict_dictinitoption - 1] = true;
792 newtup = heap_modify_tuple(tup, RelationGetDescr(rel),
793 repl_val, repl_null, repl_repl);
795 simple_heap_update(rel, &newtup->t_self, newtup);
797 CatalogUpdateIndexes(rel, newtup);
800 * NOTE: because we only support altering the options, not the template,
801 * there is no need to update dependencies. This might have to change if
802 * the options ever reference inside-the-database objects.
805 heap_freetuple(newtup);
806 ReleaseSysCache(tup);
808 heap_close(rel, RowExclusiveLock);
812 * ALTER TEXT SEARCH DICTIONARY OWNER
815 AlterTSDictionaryOwner(List *name, Oid newOwnerId)
822 Form_pg_ts_dict form;
824 rel = heap_open(TSDictionaryRelationId, RowExclusiveLock);
826 dictId = get_ts_dict_oid(name, false);
828 tup = SearchSysCacheCopy1(TSDICTOID, ObjectIdGetDatum(dictId));
830 if (!HeapTupleIsValid(tup)) /* should not happen */
831 elog(ERROR, "cache lookup failed for text search dictionary %u",
834 form = (Form_pg_ts_dict) GETSTRUCT(tup);
835 namespaceOid = form->dictnamespace;
837 if (form->dictowner != newOwnerId)
839 /* Superusers can always do it */
843 if (!pg_ts_dict_ownercheck(dictId, GetUserId()))
844 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSDICTIONARY,
845 NameListToString(name));
847 /* Must be able to become new owner */
848 check_is_member_of_role(GetUserId(), newOwnerId);
850 /* New owner must have CREATE privilege on namespace */
851 aclresult = pg_namespace_aclcheck(namespaceOid, newOwnerId, ACL_CREATE);
852 if (aclresult != ACLCHECK_OK)
853 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
854 get_namespace_name(namespaceOid));
857 form->dictowner = newOwnerId;
859 simple_heap_update(rel, &tup->t_self, tup);
860 CatalogUpdateIndexes(rel, tup);
862 /* Update owner dependency reference */
863 changeDependencyOnOwner(TSDictionaryRelationId, HeapTupleGetOid(tup),
867 heap_close(rel, NoLock);
871 /* ---------------------- TS Template commands -----------------------*/
874 * lookup a template support function and return its OID (as a Datum)
876 * attnum is the pg_ts_template column the function will go into
879 get_ts_template_func(DefElem *defel, int attnum)
881 List *funcName = defGetQualifiedName(defel);
887 retTypeId = INTERNALOID;
888 typeId[0] = INTERNALOID;
889 typeId[1] = INTERNALOID;
890 typeId[2] = INTERNALOID;
891 typeId[3] = INTERNALOID;
894 case Anum_pg_ts_template_tmplinit:
897 case Anum_pg_ts_template_tmpllexize:
901 /* should not be here */
902 elog(ERROR, "unrecognized attribute for text search template: %d",
904 nargs = 0; /* keep compiler quiet */
907 procOid = LookupFuncName(funcName, nargs, typeId, false);
908 if (get_func_rettype(procOid) != retTypeId)
910 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
911 errmsg("function %s should return type %s",
912 func_signature_string(funcName, nargs, NIL, typeId),
913 format_type_be(retTypeId))));
915 return ObjectIdGetDatum(procOid);
919 * make pg_depend entries for a new pg_ts_template entry
922 makeTSTemplateDependencies(HeapTuple tuple)
924 Form_pg_ts_template tmpl = (Form_pg_ts_template) GETSTRUCT(tuple);
925 ObjectAddress myself,
928 myself.classId = TSTemplateRelationId;
929 myself.objectId = HeapTupleGetOid(tuple);
930 myself.objectSubId = 0;
932 /* dependency on namespace */
933 referenced.classId = NamespaceRelationId;
934 referenced.objectId = tmpl->tmplnamespace;
935 referenced.objectSubId = 0;
936 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
938 /* dependency on extension */
939 recordDependencyOnCurrentExtension(&myself, false);
941 /* dependencies on functions */
942 referenced.classId = ProcedureRelationId;
943 referenced.objectSubId = 0;
945 referenced.objectId = tmpl->tmpllexize;
946 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
948 if (OidIsValid(tmpl->tmplinit))
950 referenced.objectId = tmpl->tmplinit;
951 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
956 * CREATE TEXT SEARCH TEMPLATE
959 DefineTSTemplate(List *names, List *parameters)
964 Datum values[Natts_pg_ts_template];
965 bool nulls[Natts_pg_ts_template];
974 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
975 errmsg("must be superuser to create text search templates")));
977 /* Convert list of names to a name and namespace */
978 namespaceoid = QualifiedNameGetCreationNamespace(names, &tmplname);
980 for (i = 0; i < Natts_pg_ts_template; i++)
983 values[i] = ObjectIdGetDatum(InvalidOid);
986 namestrcpy(&dname, tmplname);
987 values[Anum_pg_ts_template_tmplname - 1] = NameGetDatum(&dname);
988 values[Anum_pg_ts_template_tmplnamespace - 1] = ObjectIdGetDatum(namespaceoid);
991 * loop over the definition list and extract the information we need.
993 foreach(pl, parameters)
995 DefElem *defel = (DefElem *) lfirst(pl);
997 if (pg_strcasecmp(defel->defname, "init") == 0)
999 values[Anum_pg_ts_template_tmplinit - 1] =
1000 get_ts_template_func(defel, Anum_pg_ts_template_tmplinit);
1001 nulls[Anum_pg_ts_template_tmplinit - 1] = false;
1003 else if (pg_strcasecmp(defel->defname, "lexize") == 0)
1005 values[Anum_pg_ts_template_tmpllexize - 1] =
1006 get_ts_template_func(defel, Anum_pg_ts_template_tmpllexize);
1007 nulls[Anum_pg_ts_template_tmpllexize - 1] = false;
1011 (errcode(ERRCODE_SYNTAX_ERROR),
1012 errmsg("text search template parameter \"%s\" not recognized",
1019 if (!OidIsValid(DatumGetObjectId(values[Anum_pg_ts_template_tmpllexize - 1])))
1021 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1022 errmsg("text search template lexize method is required")));
1025 * Looks good, insert
1028 tmplRel = heap_open(TSTemplateRelationId, RowExclusiveLock);
1030 tup = heap_form_tuple(tmplRel->rd_att, values, nulls);
1032 dictOid = simple_heap_insert(tmplRel, tup);
1034 CatalogUpdateIndexes(tmplRel, tup);
1036 makeTSTemplateDependencies(tup);
1038 /* Post creation hook for new text search template */
1039 InvokeObjectAccessHook(OAT_POST_CREATE, TSTemplateRelationId, dictOid, 0);
1041 heap_freetuple(tup);
1043 heap_close(tmplRel, RowExclusiveLock);
1047 * ALTER TEXT SEARCH TEMPLATE RENAME
1050 RenameTSTemplate(List *oldname, const char *newname)
1059 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1060 errmsg("must be superuser to rename text search templates")));
1062 rel = heap_open(TSTemplateRelationId, RowExclusiveLock);
1064 tmplId = get_ts_template_oid(oldname, false);
1066 tup = SearchSysCacheCopy1(TSTEMPLATEOID, ObjectIdGetDatum(tmplId));
1068 if (!HeapTupleIsValid(tup)) /* should not happen */
1069 elog(ERROR, "cache lookup failed for text search template %u",
1072 namespaceOid = ((Form_pg_ts_template) GETSTRUCT(tup))->tmplnamespace;
1074 if (SearchSysCacheExists2(TSTEMPLATENAMENSP,
1075 PointerGetDatum(newname),
1076 ObjectIdGetDatum(namespaceOid)))
1078 (errcode(ERRCODE_DUPLICATE_OBJECT),
1079 errmsg("text search template \"%s\" already exists",
1082 namestrcpy(&(((Form_pg_ts_template) GETSTRUCT(tup))->tmplname), newname);
1083 simple_heap_update(rel, &tup->t_self, tup);
1084 CatalogUpdateIndexes(rel, tup);
1086 heap_close(rel, NoLock);
1087 heap_freetuple(tup);
1091 * ALTER TEXT SEARCH TEMPLATE any_name SET SCHEMA name
1094 AlterTSTemplateNamespace(List *name, const char *newschema)
1100 rel = heap_open(TSTemplateRelationId, RowExclusiveLock);
1102 tmplId = get_ts_template_oid(name, false);
1104 /* get schema OID */
1105 nspOid = LookupCreationNamespace(newschema);
1107 AlterObjectNamespace(rel, TSTEMPLATEOID, TSTEMPLATENAMENSP,
1109 Anum_pg_ts_template_tmplname,
1110 Anum_pg_ts_template_tmplnamespace,
1113 heap_close(rel, RowExclusiveLock);
1117 AlterTSTemplateNamespace_oid(Oid tmplId, Oid newNspOid)
1122 rel = heap_open(TSTemplateRelationId, RowExclusiveLock);
1125 AlterObjectNamespace(rel, TSTEMPLATEOID, TSTEMPLATENAMENSP,
1127 Anum_pg_ts_template_tmplname,
1128 Anum_pg_ts_template_tmplnamespace,
1131 heap_close(rel, RowExclusiveLock);
1137 * Guts of TS template deletion.
1140 RemoveTSTemplateById(Oid tmplId)
1145 relation = heap_open(TSTemplateRelationId, RowExclusiveLock);
1147 tup = SearchSysCache1(TSTEMPLATEOID, ObjectIdGetDatum(tmplId));
1149 if (!HeapTupleIsValid(tup))
1150 elog(ERROR, "cache lookup failed for text search template %u",
1153 simple_heap_delete(relation, &tup->t_self);
1155 ReleaseSysCache(tup);
1157 heap_close(relation, RowExclusiveLock);
1160 /* ---------------------- TS Configuration commands -----------------------*/
1163 * Finds syscache tuple of configuration.
1164 * Returns NULL if no such cfg.
1167 GetTSConfigTuple(List *names)
1172 cfgId = get_ts_config_oid(names, true);
1173 if (!OidIsValid(cfgId))
1176 tup = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(cfgId));
1178 if (!HeapTupleIsValid(tup)) /* should not happen */
1179 elog(ERROR, "cache lookup failed for text search configuration %u",
1186 * make pg_depend entries for a new or updated pg_ts_config entry
1188 * Pass opened pg_ts_config_map relation if there might be any config map
1189 * entries for the config.
1192 makeConfigurationDependencies(HeapTuple tuple, bool removeOld,
1195 Form_pg_ts_config cfg = (Form_pg_ts_config) GETSTRUCT(tuple);
1196 ObjectAddresses *addrs;
1197 ObjectAddress myself,
1200 myself.classId = TSConfigRelationId;
1201 myself.objectId = HeapTupleGetOid(tuple);
1202 myself.objectSubId = 0;
1204 /* for ALTER case, first flush old dependencies, except extension deps */
1207 deleteDependencyRecordsFor(myself.classId, myself.objectId, true);
1208 deleteSharedDependencyRecordsFor(myself.classId, myself.objectId, 0);
1212 * We use an ObjectAddresses list to remove possible duplicate
1213 * dependencies from the config map info. The pg_ts_config items
1214 * shouldn't be duplicates, but might as well fold them all into one call.
1216 addrs = new_object_addresses();
1218 /* dependency on namespace */
1219 referenced.classId = NamespaceRelationId;
1220 referenced.objectId = cfg->cfgnamespace;
1221 referenced.objectSubId = 0;
1222 add_exact_object_address(&referenced, addrs);
1224 /* dependency on owner */
1225 recordDependencyOnOwner(myself.classId, myself.objectId, cfg->cfgowner);
1227 /* dependency on extension */
1228 recordDependencyOnCurrentExtension(&myself, removeOld);
1230 /* dependency on parser */
1231 referenced.classId = TSParserRelationId;
1232 referenced.objectId = cfg->cfgparser;
1233 referenced.objectSubId = 0;
1234 add_exact_object_address(&referenced, addrs);
1236 /* dependencies on dictionaries listed in config map */
1243 /* CCI to ensure we can see effects of caller's changes */
1244 CommandCounterIncrement();
1247 Anum_pg_ts_config_map_mapcfg,
1248 BTEqualStrategyNumber, F_OIDEQ,
1249 ObjectIdGetDatum(myself.objectId));
1251 scan = systable_beginscan(mapRel, TSConfigMapIndexId, true,
1252 SnapshotNow, 1, &skey);
1254 while (HeapTupleIsValid((maptup = systable_getnext(scan))))
1256 Form_pg_ts_config_map cfgmap = (Form_pg_ts_config_map) GETSTRUCT(maptup);
1258 referenced.classId = TSDictionaryRelationId;
1259 referenced.objectId = cfgmap->mapdict;
1260 referenced.objectSubId = 0;
1261 add_exact_object_address(&referenced, addrs);
1264 systable_endscan(scan);
1267 /* Record 'em (this includes duplicate elimination) */
1268 record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
1270 free_object_addresses(addrs);
1274 * CREATE TEXT SEARCH CONFIGURATION
1277 DefineTSConfiguration(List *names, List *parameters)
1280 Relation mapRel = NULL;
1282 Datum values[Natts_pg_ts_config];
1283 bool nulls[Natts_pg_ts_config];
1284 AclResult aclresult;
1288 Oid sourceOid = InvalidOid;
1289 Oid prsOid = InvalidOid;
1293 /* Convert list of names to a name and namespace */
1294 namespaceoid = QualifiedNameGetCreationNamespace(names, &cfgname);
1296 /* Check we have creation rights in target namespace */
1297 aclresult = pg_namespace_aclcheck(namespaceoid, GetUserId(), ACL_CREATE);
1298 if (aclresult != ACLCHECK_OK)
1299 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
1300 get_namespace_name(namespaceoid));
1303 * loop over the definition list and extract the information we need.
1305 foreach(pl, parameters)
1307 DefElem *defel = (DefElem *) lfirst(pl);
1309 if (pg_strcasecmp(defel->defname, "parser") == 0)
1310 prsOid = get_ts_parser_oid(defGetQualifiedName(defel), false);
1311 else if (pg_strcasecmp(defel->defname, "copy") == 0)
1312 sourceOid = get_ts_config_oid(defGetQualifiedName(defel), false);
1315 (errcode(ERRCODE_SYNTAX_ERROR),
1316 errmsg("text search configuration parameter \"%s\" not recognized",
1320 if (OidIsValid(sourceOid) && OidIsValid(prsOid))
1322 (errcode(ERRCODE_SYNTAX_ERROR),
1323 errmsg("cannot specify both PARSER and COPY options")));
1326 * Look up source config if given.
1328 if (OidIsValid(sourceOid))
1330 Form_pg_ts_config cfg;
1332 tup = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(sourceOid));
1333 if (!HeapTupleIsValid(tup))
1334 elog(ERROR, "cache lookup failed for text search configuration %u",
1337 cfg = (Form_pg_ts_config) GETSTRUCT(tup);
1339 /* use source's parser */
1340 prsOid = cfg->cfgparser;
1342 ReleaseSysCache(tup);
1348 if (!OidIsValid(prsOid))
1350 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1351 errmsg("text search parser is required")));
1354 * Looks good, build tuple and insert
1356 memset(values, 0, sizeof(values));
1357 memset(nulls, false, sizeof(nulls));
1359 namestrcpy(&cname, cfgname);
1360 values[Anum_pg_ts_config_cfgname - 1] = NameGetDatum(&cname);
1361 values[Anum_pg_ts_config_cfgnamespace - 1] = ObjectIdGetDatum(namespaceoid);
1362 values[Anum_pg_ts_config_cfgowner - 1] = ObjectIdGetDatum(GetUserId());
1363 values[Anum_pg_ts_config_cfgparser - 1] = ObjectIdGetDatum(prsOid);
1365 cfgRel = heap_open(TSConfigRelationId, RowExclusiveLock);
1367 tup = heap_form_tuple(cfgRel->rd_att, values, nulls);
1369 cfgOid = simple_heap_insert(cfgRel, tup);
1371 CatalogUpdateIndexes(cfgRel, tup);
1373 if (OidIsValid(sourceOid))
1376 * Copy token-dicts map from source config
1382 mapRel = heap_open(TSConfigMapRelationId, RowExclusiveLock);
1385 Anum_pg_ts_config_map_mapcfg,
1386 BTEqualStrategyNumber, F_OIDEQ,
1387 ObjectIdGetDatum(sourceOid));
1389 scan = systable_beginscan(mapRel, TSConfigMapIndexId, true,
1390 SnapshotNow, 1, &skey);
1392 while (HeapTupleIsValid((maptup = systable_getnext(scan))))
1394 Form_pg_ts_config_map cfgmap = (Form_pg_ts_config_map) GETSTRUCT(maptup);
1395 HeapTuple newmaptup;
1396 Datum mapvalues[Natts_pg_ts_config_map];
1397 bool mapnulls[Natts_pg_ts_config_map];
1399 memset(mapvalues, 0, sizeof(mapvalues));
1400 memset(mapnulls, false, sizeof(mapnulls));
1402 mapvalues[Anum_pg_ts_config_map_mapcfg - 1] = cfgOid;
1403 mapvalues[Anum_pg_ts_config_map_maptokentype - 1] = cfgmap->maptokentype;
1404 mapvalues[Anum_pg_ts_config_map_mapseqno - 1] = cfgmap->mapseqno;
1405 mapvalues[Anum_pg_ts_config_map_mapdict - 1] = cfgmap->mapdict;
1407 newmaptup = heap_form_tuple(mapRel->rd_att, mapvalues, mapnulls);
1409 simple_heap_insert(mapRel, newmaptup);
1411 CatalogUpdateIndexes(mapRel, newmaptup);
1413 heap_freetuple(newmaptup);
1416 systable_endscan(scan);
1419 makeConfigurationDependencies(tup, false, mapRel);
1421 /* Post creation hook for new text search configuration */
1422 InvokeObjectAccessHook(OAT_POST_CREATE, TSConfigRelationId, cfgOid, 0);
1424 heap_freetuple(tup);
1427 heap_close(mapRel, RowExclusiveLock);
1428 heap_close(cfgRel, RowExclusiveLock);
1432 * ALTER TEXT SEARCH CONFIGURATION RENAME
1435 RenameTSConfiguration(List *oldname, const char *newname)
1440 AclResult aclresult;
1443 rel = heap_open(TSConfigRelationId, RowExclusiveLock);
1445 cfgId = get_ts_config_oid(oldname, false);
1447 tup = SearchSysCacheCopy1(TSCONFIGOID, ObjectIdGetDatum(cfgId));
1449 if (!HeapTupleIsValid(tup)) /* should not happen */
1450 elog(ERROR, "cache lookup failed for text search configuration %u",
1453 namespaceOid = ((Form_pg_ts_config) GETSTRUCT(tup))->cfgnamespace;
1455 if (SearchSysCacheExists2(TSCONFIGNAMENSP,
1456 PointerGetDatum(newname),
1457 ObjectIdGetDatum(namespaceOid)))
1459 (errcode(ERRCODE_DUPLICATE_OBJECT),
1460 errmsg("text search configuration \"%s\" already exists",
1464 if (!pg_ts_config_ownercheck(cfgId, GetUserId()))
1465 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSCONFIGURATION,
1466 NameListToString(oldname));
1468 /* must have CREATE privilege on namespace */
1469 aclresult = pg_namespace_aclcheck(namespaceOid, GetUserId(), ACL_CREATE);
1470 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
1471 get_namespace_name(namespaceOid));
1473 namestrcpy(&(((Form_pg_ts_config) GETSTRUCT(tup))->cfgname), newname);
1474 simple_heap_update(rel, &tup->t_self, tup);
1475 CatalogUpdateIndexes(rel, tup);
1477 heap_close(rel, NoLock);
1478 heap_freetuple(tup);
1482 * ALTER TEXT SEARCH CONFIGURATION any_name SET SCHEMA name
1485 AlterTSConfigurationNamespace(List *name, const char *newschema)
1491 rel = heap_open(TSConfigRelationId, RowExclusiveLock);
1493 cfgId = get_ts_config_oid(name, false);
1495 /* get schema OID */
1496 nspOid = LookupCreationNamespace(newschema);
1498 AlterObjectNamespace(rel, TSCONFIGOID, TSCONFIGNAMENSP,
1500 Anum_pg_ts_config_cfgname,
1501 Anum_pg_ts_config_cfgnamespace,
1502 Anum_pg_ts_config_cfgowner,
1503 ACL_KIND_TSCONFIGURATION);
1505 heap_close(rel, RowExclusiveLock);
1509 AlterTSConfigurationNamespace_oid(Oid cfgId, Oid newNspOid)
1514 rel = heap_open(TSConfigRelationId, RowExclusiveLock);
1517 AlterObjectNamespace(rel, TSCONFIGOID, TSCONFIGNAMENSP,
1519 Anum_pg_ts_config_cfgname,
1520 Anum_pg_ts_config_cfgnamespace,
1521 Anum_pg_ts_config_cfgowner,
1522 ACL_KIND_TSCONFIGURATION);
1524 heap_close(rel, RowExclusiveLock);
1530 * Guts of TS configuration deletion.
1533 RemoveTSConfigurationById(Oid cfgId)
1541 /* Remove the pg_ts_config entry */
1542 relCfg = heap_open(TSConfigRelationId, RowExclusiveLock);
1544 tup = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(cfgId));
1546 if (!HeapTupleIsValid(tup))
1547 elog(ERROR, "cache lookup failed for text search dictionary %u",
1550 simple_heap_delete(relCfg, &tup->t_self);
1552 ReleaseSysCache(tup);
1554 heap_close(relCfg, RowExclusiveLock);
1556 /* Remove any pg_ts_config_map entries */
1557 relMap = heap_open(TSConfigMapRelationId, RowExclusiveLock);
1560 Anum_pg_ts_config_map_mapcfg,
1561 BTEqualStrategyNumber, F_OIDEQ,
1562 ObjectIdGetDatum(cfgId));
1564 scan = systable_beginscan(relMap, TSConfigMapIndexId, true,
1565 SnapshotNow, 1, &skey);
1567 while (HeapTupleIsValid((tup = systable_getnext(scan))))
1569 simple_heap_delete(relMap, &tup->t_self);
1572 systable_endscan(scan);
1574 heap_close(relMap, RowExclusiveLock);
1578 * ALTER TEXT SEARCH CONFIGURATION OWNER
1581 AlterTSConfigurationOwner(List *name, Oid newOwnerId)
1586 AclResult aclresult;
1588 Form_pg_ts_config form;
1590 rel = heap_open(TSConfigRelationId, RowExclusiveLock);
1592 cfgId = get_ts_config_oid(name, false);
1594 tup = SearchSysCacheCopy1(TSCONFIGOID, ObjectIdGetDatum(cfgId));
1596 if (!HeapTupleIsValid(tup)) /* should not happen */
1597 elog(ERROR, "cache lookup failed for text search configuration %u",
1600 form = (Form_pg_ts_config) GETSTRUCT(tup);
1601 namespaceOid = form->cfgnamespace;
1603 if (form->cfgowner != newOwnerId)
1605 /* Superusers can always do it */
1609 if (!pg_ts_config_ownercheck(cfgId, GetUserId()))
1610 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSCONFIGURATION,
1611 NameListToString(name));
1613 /* Must be able to become new owner */
1614 check_is_member_of_role(GetUserId(), newOwnerId);
1616 /* New owner must have CREATE privilege on namespace */
1617 aclresult = pg_namespace_aclcheck(namespaceOid, newOwnerId, ACL_CREATE);
1618 if (aclresult != ACLCHECK_OK)
1619 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
1620 get_namespace_name(namespaceOid));
1623 form->cfgowner = newOwnerId;
1625 simple_heap_update(rel, &tup->t_self, tup);
1626 CatalogUpdateIndexes(rel, tup);
1628 /* Update owner dependency reference */
1629 changeDependencyOnOwner(TSConfigRelationId, HeapTupleGetOid(tup),
1633 heap_close(rel, NoLock);
1634 heap_freetuple(tup);
1638 * ALTER TEXT SEARCH CONFIGURATION - main entry point
1641 AlterTSConfiguration(AlterTSConfigurationStmt *stmt)
1646 /* Find the configuration */
1647 tup = GetTSConfigTuple(stmt->cfgname);
1648 if (!HeapTupleIsValid(tup))
1650 (errcode(ERRCODE_UNDEFINED_OBJECT),
1651 errmsg("text search configuration \"%s\" does not exist",
1652 NameListToString(stmt->cfgname))));
1655 if (!pg_ts_config_ownercheck(HeapTupleGetOid(tup), GetUserId()))
1656 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSCONFIGURATION,
1657 NameListToString(stmt->cfgname));
1659 relMap = heap_open(TSConfigMapRelationId, RowExclusiveLock);
1661 /* Add or drop mappings */
1663 MakeConfigurationMapping(stmt, tup, relMap);
1664 else if (stmt->tokentype)
1665 DropConfigurationMapping(stmt, tup, relMap);
1667 /* Update dependencies */
1668 makeConfigurationDependencies(tup, true, relMap);
1670 heap_close(relMap, RowExclusiveLock);
1672 ReleaseSysCache(tup);
1676 * Translate a list of token type names to an array of token type numbers
1679 getTokenTypes(Oid prsId, List *tokennames)
1681 TSParserCacheEntry *prs = lookup_ts_parser_cache(prsId);
1688 ntoken = list_length(tokennames);
1691 res = (int *) palloc(sizeof(int) * ntoken);
1693 if (!OidIsValid(prs->lextypeOid))
1694 elog(ERROR, "method lextype isn't defined for text search parser %u",
1697 /* lextype takes one dummy argument */
1698 list = (LexDescr *) DatumGetPointer(OidFunctionCall1(prs->lextypeOid,
1702 foreach(tn, tokennames)
1704 Value *val = (Value *) lfirst(tn);
1709 while (list && list[j].lexid)
1711 /* XXX should we use pg_strcasecmp here? */
1712 if (strcmp(strVal(val), list[j].alias) == 0)
1714 res[i] = list[j].lexid;
1722 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1723 errmsg("token type \"%s\" does not exist",
1732 * ALTER TEXT SEARCH CONFIGURATION ADD/ALTER MAPPING
1735 MakeConfigurationMapping(AlterTSConfigurationStmt *stmt,
1736 HeapTuple tup, Relation relMap)
1738 Oid cfgId = HeapTupleGetOid(tup);
1739 ScanKeyData skey[2];
1751 prsId = ((Form_pg_ts_config) GETSTRUCT(tup))->cfgparser;
1753 tokens = getTokenTypes(prsId, stmt->tokentype);
1754 ntoken = list_length(stmt->tokentype);
1759 * delete maps for tokens if they exist and command was ALTER
1761 for (i = 0; i < ntoken; i++)
1763 ScanKeyInit(&skey[0],
1764 Anum_pg_ts_config_map_mapcfg,
1765 BTEqualStrategyNumber, F_OIDEQ,
1766 ObjectIdGetDatum(cfgId));
1767 ScanKeyInit(&skey[1],
1768 Anum_pg_ts_config_map_maptokentype,
1769 BTEqualStrategyNumber, F_INT4EQ,
1770 Int32GetDatum(tokens[i]));
1772 scan = systable_beginscan(relMap, TSConfigMapIndexId, true,
1773 SnapshotNow, 2, skey);
1775 while (HeapTupleIsValid((maptup = systable_getnext(scan))))
1777 simple_heap_delete(relMap, &maptup->t_self);
1780 systable_endscan(scan);
1785 * Convert list of dictionary names to array of dict OIDs
1787 ndict = list_length(stmt->dicts);
1788 dictIds = (Oid *) palloc(sizeof(Oid) * ndict);
1790 foreach(c, stmt->dicts)
1792 List *names = (List *) lfirst(c);
1794 dictIds[i] = get_ts_dict_oid(names, false);
1801 * Replace a specific dictionary in existing entries
1803 Oid dictOld = dictIds[0],
1804 dictNew = dictIds[1];
1806 ScanKeyInit(&skey[0],
1807 Anum_pg_ts_config_map_mapcfg,
1808 BTEqualStrategyNumber, F_OIDEQ,
1809 ObjectIdGetDatum(cfgId));
1811 scan = systable_beginscan(relMap, TSConfigMapIndexId, true,
1812 SnapshotNow, 1, skey);
1814 while (HeapTupleIsValid((maptup = systable_getnext(scan))))
1816 Form_pg_ts_config_map cfgmap = (Form_pg_ts_config_map) GETSTRUCT(maptup);
1819 * check if it's one of target token types
1823 bool tokmatch = false;
1825 for (j = 0; j < ntoken; j++)
1827 if (cfgmap->maptokentype == tokens[j])
1838 * replace dictionary if match
1840 if (cfgmap->mapdict == dictOld)
1842 Datum repl_val[Natts_pg_ts_config_map];
1843 bool repl_null[Natts_pg_ts_config_map];
1844 bool repl_repl[Natts_pg_ts_config_map];
1847 memset(repl_val, 0, sizeof(repl_val));
1848 memset(repl_null, false, sizeof(repl_null));
1849 memset(repl_repl, false, sizeof(repl_repl));
1851 repl_val[Anum_pg_ts_config_map_mapdict - 1] = ObjectIdGetDatum(dictNew);
1852 repl_repl[Anum_pg_ts_config_map_mapdict - 1] = true;
1854 newtup = heap_modify_tuple(maptup,
1855 RelationGetDescr(relMap),
1856 repl_val, repl_null, repl_repl);
1857 simple_heap_update(relMap, &newtup->t_self, newtup);
1859 CatalogUpdateIndexes(relMap, newtup);
1863 systable_endscan(scan);
1868 * Insertion of new entries
1870 for (i = 0; i < ntoken; i++)
1872 for (j = 0; j < ndict; j++)
1874 Datum values[Natts_pg_ts_config_map];
1875 bool nulls[Natts_pg_ts_config_map];
1877 memset(nulls, false, sizeof(nulls));
1878 values[Anum_pg_ts_config_map_mapcfg - 1] = ObjectIdGetDatum(cfgId);
1879 values[Anum_pg_ts_config_map_maptokentype - 1] = Int32GetDatum(tokens[i]);
1880 values[Anum_pg_ts_config_map_mapseqno - 1] = Int32GetDatum(j + 1);
1881 values[Anum_pg_ts_config_map_mapdict - 1] = ObjectIdGetDatum(dictIds[j]);
1883 tup = heap_form_tuple(relMap->rd_att, values, nulls);
1884 simple_heap_insert(relMap, tup);
1885 CatalogUpdateIndexes(relMap, tup);
1887 heap_freetuple(tup);
1894 * ALTER TEXT SEARCH CONFIGURATION DROP MAPPING
1897 DropConfigurationMapping(AlterTSConfigurationStmt *stmt,
1898 HeapTuple tup, Relation relMap)
1900 Oid cfgId = HeapTupleGetOid(tup);
1901 ScanKeyData skey[2];
1909 prsId = ((Form_pg_ts_config) GETSTRUCT(tup))->cfgparser;
1911 tokens = getTokenTypes(prsId, stmt->tokentype);
1914 foreach(c, stmt->tokentype)
1916 Value *val = (Value *) lfirst(c);
1919 ScanKeyInit(&skey[0],
1920 Anum_pg_ts_config_map_mapcfg,
1921 BTEqualStrategyNumber, F_OIDEQ,
1922 ObjectIdGetDatum(cfgId));
1923 ScanKeyInit(&skey[1],
1924 Anum_pg_ts_config_map_maptokentype,
1925 BTEqualStrategyNumber, F_INT4EQ,
1926 Int32GetDatum(tokens[i]));
1928 scan = systable_beginscan(relMap, TSConfigMapIndexId, true,
1929 SnapshotNow, 2, skey);
1931 while (HeapTupleIsValid((maptup = systable_getnext(scan))))
1933 simple_heap_delete(relMap, &maptup->t_self);
1937 systable_endscan(scan);
1941 if (!stmt->missing_ok)
1944 (errcode(ERRCODE_UNDEFINED_OBJECT),
1945 errmsg("mapping for token type \"%s\" does not exist",
1951 (errmsg("mapping for token type \"%s\" does not exist, skipping",
1962 * Serialize dictionary options, producing a TEXT datum from a List of DefElem
1964 * This is used to form the value stored in pg_ts_dict.dictinitoption.
1965 * For the convenience of pg_dump, the output is formatted exactly as it
1966 * would need to appear in CREATE TEXT SEARCH DICTIONARY to reproduce the
1969 * Note that we assume that only the textual representation of an option's
1970 * value is interesting --- hence, non-string DefElems get forced to strings.
1973 serialize_deflist(List *deflist)
1979 initStringInfo(&buf);
1983 DefElem *defel = (DefElem *) lfirst(l);
1984 char *val = defGetString(defel);
1986 appendStringInfo(&buf, "%s = ",
1987 quote_identifier(defel->defname));
1988 /* If backslashes appear, force E syntax to determine their handling */
1989 if (strchr(val, '\\'))
1990 appendStringInfoChar(&buf, ESCAPE_STRING_SYNTAX);
1991 appendStringInfoChar(&buf, '\'');
1996 if (SQL_STR_DOUBLE(ch, true))
1997 appendStringInfoChar(&buf, ch);
1998 appendStringInfoChar(&buf, ch);
2000 appendStringInfoChar(&buf, '\'');
2001 if (lnext(l) != NULL)
2002 appendStringInfo(&buf, ", ");
2005 result = cstring_to_text_with_len(buf.data, buf.len);
2011 * Deserialize dictionary options, reconstructing a List of DefElem from TEXT
2013 * This is also used for prsheadline options, so for backward compatibility
2014 * we need to accept a few things serialize_deflist() will never emit:
2015 * in particular, unquoted and double-quoted values.
2018 deserialize_deflist(Datum txt)
2020 text *in = DatumGetTextP(txt); /* in case it's toasted */
2022 int len = VARSIZE(in) - VARHDRSZ;
2039 ds_state state = CS_WAITKEY;
2041 workspace = (char *) palloc(len + 1); /* certainly enough room */
2044 for (; ptr < endptr; ptr++)
2049 if (isspace((unsigned char) *ptr) || *ptr == ',')
2064 if (isspace((unsigned char) *ptr))
2069 else if (*ptr == '=')
2072 state = CS_WAITVALUE;
2082 if (ptr + 1 < endptr && ptr[1] == '"')
2084 /* copy only one of the two quotes */
2100 state = CS_WAITVALUE;
2101 else if (!isspace((unsigned char) *ptr))
2103 (errcode(ERRCODE_SYNTAX_ERROR),
2104 errmsg("invalid parameter list format: \"%s\"",
2105 text_to_cstring(in))));
2111 state = CS_INSQVALUE;
2113 else if (*ptr == 'E' && ptr + 1 < endptr && ptr[1] == '\'')
2117 state = CS_INSQVALUE;
2119 else if (*ptr == '"')
2122 state = CS_INDQVALUE;
2124 else if (!isspace((unsigned char) *ptr))
2128 state = CS_INWVALUE;
2134 if (ptr + 1 < endptr && ptr[1] == '\'')
2136 /* copy only one of the two quotes */
2142 result = lappend(result,
2143 makeDefElem(pstrdup(workspace),
2144 (Node *) makeString(pstrdup(startvalue))));
2148 else if (*ptr == '\\')
2150 if (ptr + 1 < endptr && ptr[1] == '\\')
2152 /* copy only one of the two backslashes */
2166 if (ptr + 1 < endptr && ptr[1] == '"')
2168 /* copy only one of the two quotes */
2174 result = lappend(result,
2175 makeDefElem(pstrdup(workspace),
2176 (Node *) makeString(pstrdup(startvalue))));
2186 if (*ptr == ',' || isspace((unsigned char) *ptr))
2189 result = lappend(result,
2190 makeDefElem(pstrdup(workspace),
2191 (Node *) makeString(pstrdup(startvalue))));
2200 elog(ERROR, "unrecognized deserialize_deflist state: %d",
2205 if (state == CS_INWVALUE)
2208 result = lappend(result,
2209 makeDefElem(pstrdup(workspace),
2210 (Node *) makeString(pstrdup(startvalue))));
2212 else if (state != CS_WAITKEY)
2214 (errcode(ERRCODE_SYNTAX_ERROR),
2215 errmsg("invalid parameter list format: \"%s\"",
2216 text_to_cstring(in))));