1 /*-------------------------------------------------------------------------
5 * Routines for tsearch manipulation commands
7 * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
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/namespace.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/defrem.h"
35 #include "miscadmin.h"
36 #include "nodes/makefuncs.h"
37 #include "parser/parse_func.h"
38 #include "tsearch/ts_cache.h"
39 #include "tsearch/ts_public.h"
40 #include "tsearch/ts_utils.h"
41 #include "utils/acl.h"
42 #include "utils/builtins.h"
43 #include "utils/catcache.h"
44 #include "utils/fmgroids.h"
45 #include "utils/lsyscache.h"
46 #include "utils/syscache.h"
47 #include "utils/tqual.h"
50 static void MakeConfigurationMapping(AlterTSConfigurationStmt
*stmt
,
51 HeapTuple tup
, Relation relMap
);
52 static void DropConfigurationMapping(AlterTSConfigurationStmt
*stmt
,
53 HeapTuple tup
, Relation relMap
);
56 /* --------------------- TS Parser commands ------------------------ */
59 * lookup a parser support function and return its OID (as a Datum)
61 * attnum is the pg_ts_parser column the function will go into
64 get_ts_parser_func(DefElem
*defel
, int attnum
)
66 List
*funcName
= defGetQualifiedName(defel
);
72 retTypeId
= INTERNALOID
; /* correct for most */
73 typeId
[0] = INTERNALOID
;
76 case Anum_pg_ts_parser_prsstart
:
80 case Anum_pg_ts_parser_prstoken
:
82 typeId
[1] = INTERNALOID
;
83 typeId
[2] = INTERNALOID
;
85 case Anum_pg_ts_parser_prsend
:
89 case Anum_pg_ts_parser_prsheadline
:
91 typeId
[1] = INTERNALOID
;
92 typeId
[2] = TSQUERYOID
;
94 case Anum_pg_ts_parser_prslextype
:
98 /* should not be here */
99 elog(ERROR
, "unrecognized attribute for text search parser: %d",
101 nargs
= 0; /* keep compiler quiet */
104 procOid
= LookupFuncName(funcName
, nargs
, typeId
, false);
105 if (get_func_rettype(procOid
) != retTypeId
)
107 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
108 errmsg("function %s should return type %s",
109 func_signature_string(funcName
, nargs
, typeId
),
110 format_type_be(retTypeId
))));
112 return ObjectIdGetDatum(procOid
);
116 * make pg_depend entries for a new pg_ts_parser entry
119 makeParserDependencies(HeapTuple tuple
)
121 Form_pg_ts_parser prs
= (Form_pg_ts_parser
) GETSTRUCT(tuple
);
122 ObjectAddress myself
,
125 myself
.classId
= TSParserRelationId
;
126 myself
.objectId
= HeapTupleGetOid(tuple
);
127 myself
.objectSubId
= 0;
129 /* dependency on namespace */
130 referenced
.classId
= NamespaceRelationId
;
131 referenced
.objectId
= prs
->prsnamespace
;
132 referenced
.objectSubId
= 0;
133 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
135 /* dependencies on functions */
136 referenced
.classId
= ProcedureRelationId
;
137 referenced
.objectSubId
= 0;
139 referenced
.objectId
= prs
->prsstart
;
140 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
142 referenced
.objectId
= prs
->prstoken
;
143 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
145 referenced
.objectId
= prs
->prsend
;
146 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
148 referenced
.objectId
= prs
->prslextype
;
149 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
151 if (OidIsValid(prs
->prsheadline
))
153 referenced
.objectId
= prs
->prsheadline
;
154 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
159 * CREATE TEXT SEARCH PARSER
162 DefineTSParser(List
*names
, List
*parameters
)
168 Datum values
[Natts_pg_ts_parser
];
169 char nulls
[Natts_pg_ts_parser
];
176 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
177 errmsg("must be superuser to create text search parsers")));
179 /* Convert list of names to a name and namespace */
180 namespaceoid
= QualifiedNameGetCreationNamespace(names
, &prsname
);
182 /* initialize tuple fields with name/namespace */
183 memset(values
, 0, sizeof(values
));
184 memset(nulls
, ' ', sizeof(nulls
));
186 namestrcpy(&pname
, prsname
);
187 values
[Anum_pg_ts_parser_prsname
- 1] = NameGetDatum(&pname
);
188 values
[Anum_pg_ts_parser_prsnamespace
- 1] = ObjectIdGetDatum(namespaceoid
);
191 * loop over the definition list and extract the information we need.
193 foreach(pl
, parameters
)
195 DefElem
*defel
= (DefElem
*) lfirst(pl
);
197 if (pg_strcasecmp(defel
->defname
, "start") == 0)
199 values
[Anum_pg_ts_parser_prsstart
- 1] =
200 get_ts_parser_func(defel
, Anum_pg_ts_parser_prsstart
);
202 else if (pg_strcasecmp(defel
->defname
, "gettoken") == 0)
204 values
[Anum_pg_ts_parser_prstoken
- 1] =
205 get_ts_parser_func(defel
, Anum_pg_ts_parser_prstoken
);
207 else if (pg_strcasecmp(defel
->defname
, "end") == 0)
209 values
[Anum_pg_ts_parser_prsend
- 1] =
210 get_ts_parser_func(defel
, Anum_pg_ts_parser_prsend
);
212 else if (pg_strcasecmp(defel
->defname
, "headline") == 0)
214 values
[Anum_pg_ts_parser_prsheadline
- 1] =
215 get_ts_parser_func(defel
, Anum_pg_ts_parser_prsheadline
);
217 else if (pg_strcasecmp(defel
->defname
, "lextypes") == 0)
219 values
[Anum_pg_ts_parser_prslextype
- 1] =
220 get_ts_parser_func(defel
, Anum_pg_ts_parser_prslextype
);
224 (errcode(ERRCODE_SYNTAX_ERROR
),
225 errmsg("text search parser parameter \"%s\" not recognized",
232 if (!OidIsValid(DatumGetObjectId(values
[Anum_pg_ts_parser_prsstart
- 1])))
234 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
235 errmsg("text search parser start method is required")));
237 if (!OidIsValid(DatumGetObjectId(values
[Anum_pg_ts_parser_prstoken
- 1])))
239 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
240 errmsg("text search parser gettoken method is required")));
242 if (!OidIsValid(DatumGetObjectId(values
[Anum_pg_ts_parser_prsend
- 1])))
244 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
245 errmsg("text search parser end method is required")));
247 if (!OidIsValid(DatumGetObjectId(values
[Anum_pg_ts_parser_prslextype
- 1])))
249 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
250 errmsg("text search parser lextypes method is required")));
255 prsRel
= heap_open(TSParserRelationId
, RowExclusiveLock
);
257 tup
= heap_formtuple(prsRel
->rd_att
, values
, nulls
);
259 prsOid
= simple_heap_insert(prsRel
, tup
);
261 CatalogUpdateIndexes(prsRel
, tup
);
263 makeParserDependencies(tup
);
267 heap_close(prsRel
, RowExclusiveLock
);
271 * DROP TEXT SEARCH PARSER
274 RemoveTSParsers(DropStmt
*drop
)
276 ObjectAddresses
*objects
;
281 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
282 errmsg("must be superuser to drop text search parsers")));
285 * First we identify all the objects, then we delete them in a single
286 * performMultipleDeletions() call. This is to avoid unwanted
287 * DROP RESTRICT errors if one of the objects depends on another.
289 objects
= new_object_addresses();
291 foreach(cell
, drop
->objects
)
293 List
*names
= (List
*) lfirst(cell
);
295 ObjectAddress object
;
297 prsOid
= TSParserGetPrsid(names
, true);
299 if (!OidIsValid(prsOid
))
301 if (!drop
->missing_ok
)
304 (errcode(ERRCODE_UNDEFINED_OBJECT
),
305 errmsg("text search parser \"%s\" does not exist",
306 NameListToString(names
))));
311 (errmsg("text search parser \"%s\" does not exist, skipping",
312 NameListToString(names
))));
317 object
.classId
= TSParserRelationId
;
318 object
.objectId
= prsOid
;
319 object
.objectSubId
= 0;
321 add_exact_object_address(&object
, objects
);
324 performMultipleDeletions(objects
, drop
->behavior
);
326 free_object_addresses(objects
);
330 * Guts of TS parser deletion.
333 RemoveTSParserById(Oid prsId
)
338 relation
= heap_open(TSParserRelationId
, RowExclusiveLock
);
340 tup
= SearchSysCache(TSPARSEROID
,
341 ObjectIdGetDatum(prsId
),
344 if (!HeapTupleIsValid(tup
))
345 elog(ERROR
, "cache lookup failed for text search parser %u", prsId
);
347 simple_heap_delete(relation
, &tup
->t_self
);
349 ReleaseSysCache(tup
);
351 heap_close(relation
, RowExclusiveLock
);
355 * ALTER TEXT SEARCH PARSER RENAME
358 RenameTSParser(List
*oldname
, const char *newname
)
367 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
368 errmsg("must be superuser to rename text search parsers")));
370 rel
= heap_open(TSParserRelationId
, RowExclusiveLock
);
372 prsId
= TSParserGetPrsid(oldname
, false);
374 tup
= SearchSysCacheCopy(TSPARSEROID
,
375 ObjectIdGetDatum(prsId
),
378 if (!HeapTupleIsValid(tup
)) /* should not happen */
379 elog(ERROR
, "cache lookup failed for text search parser %u", prsId
);
381 namespaceOid
= ((Form_pg_ts_parser
) GETSTRUCT(tup
))->prsnamespace
;
383 if (SearchSysCacheExists(TSPARSERNAMENSP
,
384 PointerGetDatum(newname
),
385 ObjectIdGetDatum(namespaceOid
),
388 (errcode(ERRCODE_DUPLICATE_OBJECT
),
389 errmsg("text search parser \"%s\" already exists",
392 namestrcpy(&(((Form_pg_ts_parser
) GETSTRUCT(tup
))->prsname
), newname
);
393 simple_heap_update(rel
, &tup
->t_self
, tup
);
394 CatalogUpdateIndexes(rel
, tup
);
396 heap_close(rel
, NoLock
);
400 /* ---------------------- TS Dictionary commands -----------------------*/
403 * make pg_depend entries for a new pg_ts_dict entry
406 makeDictionaryDependencies(HeapTuple tuple
)
408 Form_pg_ts_dict dict
= (Form_pg_ts_dict
) GETSTRUCT(tuple
);
409 ObjectAddress myself
,
412 myself
.classId
= TSDictionaryRelationId
;
413 myself
.objectId
= HeapTupleGetOid(tuple
);
414 myself
.objectSubId
= 0;
416 /* dependency on namespace */
417 referenced
.classId
= NamespaceRelationId
;
418 referenced
.objectId
= dict
->dictnamespace
;
419 referenced
.objectSubId
= 0;
420 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
422 /* dependency on owner */
423 recordDependencyOnOwner(myself
.classId
, myself
.objectId
, dict
->dictowner
);
425 /* dependency on template */
426 referenced
.classId
= TSTemplateRelationId
;
427 referenced
.objectId
= dict
->dicttemplate
;
428 referenced
.objectSubId
= 0;
429 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
433 * verify that a template's init method accepts a proposed option list
436 verify_dictoptions(Oid tmplId
, List
*dictoptions
)
439 Form_pg_ts_template tform
;
443 * Suppress this test when running in a standalone backend. This is a
444 * hack to allow initdb to create prefab dictionaries that might not
445 * actually be usable in template1's encoding (due to using external files
446 * that can't be translated into template1's encoding). We want to create
447 * them anyway, since they might be usable later in other databases.
449 if (!IsUnderPostmaster
)
452 tup
= SearchSysCache(TSTEMPLATEOID
,
453 ObjectIdGetDatum(tmplId
),
455 if (!HeapTupleIsValid(tup
)) /* should not happen */
456 elog(ERROR
, "cache lookup failed for text search template %u",
458 tform
= (Form_pg_ts_template
) GETSTRUCT(tup
);
460 initmethod
= tform
->tmplinit
;
462 if (!OidIsValid(initmethod
))
464 /* If there is no init method, disallow any options */
467 (errcode(ERRCODE_SYNTAX_ERROR
),
468 errmsg("text search template \"%s\" does not accept options",
469 NameStr(tform
->tmplname
))));
474 * Copy the options just in case init method thinks it can scribble on
477 dictoptions
= copyObject(dictoptions
);
480 * Call the init method and see if it complains. We don't worry about
481 * it leaking memory, since our command will soon be over anyway.
483 (void) OidFunctionCall1(initmethod
, PointerGetDatum(dictoptions
));
486 ReleaseSysCache(tup
);
490 * CREATE TEXT SEARCH DICTIONARY
493 DefineTSDictionary(List
*names
, List
*parameters
)
498 Datum values
[Natts_pg_ts_dict
];
499 char nulls
[Natts_pg_ts_dict
];
501 Oid templId
= InvalidOid
;
502 List
*dictoptions
= NIL
;
508 /* Convert list of names to a name and namespace */
509 namespaceoid
= QualifiedNameGetCreationNamespace(names
, &dictname
);
511 /* Check we have creation rights in target namespace */
512 aclresult
= pg_namespace_aclcheck(namespaceoid
, GetUserId(), ACL_CREATE
);
513 if (aclresult
!= ACLCHECK_OK
)
514 aclcheck_error(aclresult
, ACL_KIND_NAMESPACE
,
515 get_namespace_name(namespaceoid
));
518 * loop over the definition list and extract the information we need.
520 foreach(pl
, parameters
)
522 DefElem
*defel
= (DefElem
*) lfirst(pl
);
524 if (pg_strcasecmp(defel
->defname
, "template") == 0)
526 templId
= TSTemplateGetTmplid(defGetQualifiedName(defel
), false);
530 /* Assume it's an option for the dictionary itself */
531 dictoptions
= lappend(dictoptions
, defel
);
538 if (!OidIsValid(templId
))
540 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
541 errmsg("text search template is required")));
543 verify_dictoptions(templId
, dictoptions
);
548 memset(values
, 0, sizeof(values
));
549 memset(nulls
, ' ', sizeof(nulls
));
551 namestrcpy(&dname
, dictname
);
552 values
[Anum_pg_ts_dict_dictname
- 1] = NameGetDatum(&dname
);
553 values
[Anum_pg_ts_dict_dictnamespace
- 1] = ObjectIdGetDatum(namespaceoid
);
554 values
[Anum_pg_ts_dict_dictowner
- 1] = ObjectIdGetDatum(GetUserId());
555 values
[Anum_pg_ts_dict_dicttemplate
- 1] = ObjectIdGetDatum(templId
);
557 values
[Anum_pg_ts_dict_dictinitoption
- 1] =
558 PointerGetDatum(serialize_deflist(dictoptions
));
560 nulls
[Anum_pg_ts_dict_dictinitoption
- 1] = 'n';
562 dictRel
= heap_open(TSDictionaryRelationId
, RowExclusiveLock
);
564 tup
= heap_formtuple(dictRel
->rd_att
, values
, nulls
);
566 dictOid
= simple_heap_insert(dictRel
, tup
);
568 CatalogUpdateIndexes(dictRel
, tup
);
570 makeDictionaryDependencies(tup
);
574 heap_close(dictRel
, RowExclusiveLock
);
578 * ALTER TEXT SEARCH DICTIONARY RENAME
581 RenameTSDictionary(List
*oldname
, const char *newname
)
589 rel
= heap_open(TSDictionaryRelationId
, RowExclusiveLock
);
591 dictId
= TSDictionaryGetDictid(oldname
, false);
593 tup
= SearchSysCacheCopy(TSDICTOID
,
594 ObjectIdGetDatum(dictId
),
597 if (!HeapTupleIsValid(tup
)) /* should not happen */
598 elog(ERROR
, "cache lookup failed for text search dictionary %u",
601 namespaceOid
= ((Form_pg_ts_dict
) GETSTRUCT(tup
))->dictnamespace
;
603 if (SearchSysCacheExists(TSDICTNAMENSP
,
604 PointerGetDatum(newname
),
605 ObjectIdGetDatum(namespaceOid
),
608 (errcode(ERRCODE_DUPLICATE_OBJECT
),
609 errmsg("text search dictionary \"%s\" already exists",
613 if (!pg_ts_dict_ownercheck(dictId
, GetUserId()))
614 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_TSDICTIONARY
,
615 NameListToString(oldname
));
617 /* must have CREATE privilege on namespace */
618 aclresult
= pg_namespace_aclcheck(namespaceOid
, GetUserId(), ACL_CREATE
);
619 if (aclresult
!= ACLCHECK_OK
)
620 aclcheck_error(aclresult
, ACL_KIND_NAMESPACE
,
621 get_namespace_name(namespaceOid
));
623 namestrcpy(&(((Form_pg_ts_dict
) GETSTRUCT(tup
))->dictname
), newname
);
624 simple_heap_update(rel
, &tup
->t_self
, tup
);
625 CatalogUpdateIndexes(rel
, tup
);
627 heap_close(rel
, NoLock
);
632 * DROP TEXT SEARCH DICTIONARY
635 RemoveTSDictionaries(DropStmt
*drop
)
637 ObjectAddresses
*objects
;
641 * First we identify all the objects, then we delete them in a single
642 * performMultipleDeletions() call. This is to avoid unwanted
643 * DROP RESTRICT errors if one of the objects depends on another.
645 objects
= new_object_addresses();
647 foreach(cell
, drop
->objects
)
649 List
*names
= (List
*) lfirst(cell
);
651 ObjectAddress object
;
655 dictOid
= TSDictionaryGetDictid(names
, true);
657 if (!OidIsValid(dictOid
))
659 if (!drop
->missing_ok
)
662 (errcode(ERRCODE_UNDEFINED_OBJECT
),
663 errmsg("text search dictionary \"%s\" does not exist",
664 NameListToString(names
))));
669 (errmsg("text search dictionary \"%s\" does not exist, skipping",
670 NameListToString(names
))));
675 tup
= SearchSysCache(TSDICTOID
,
676 ObjectIdGetDatum(dictOid
),
678 if (!HeapTupleIsValid(tup
)) /* should not happen */
679 elog(ERROR
, "cache lookup failed for text search dictionary %u",
682 /* Permission check: must own dictionary or its namespace */
683 namespaceId
= ((Form_pg_ts_dict
) GETSTRUCT(tup
))->dictnamespace
;
684 if (!pg_ts_dict_ownercheck(dictOid
, GetUserId()) &&
685 !pg_namespace_ownercheck(namespaceId
, GetUserId()))
686 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_TSDICTIONARY
,
687 NameListToString(names
));
689 object
.classId
= TSDictionaryRelationId
;
690 object
.objectId
= dictOid
;
691 object
.objectSubId
= 0;
693 add_exact_object_address(&object
, objects
);
695 ReleaseSysCache(tup
);
698 performMultipleDeletions(objects
, drop
->behavior
);
700 free_object_addresses(objects
);
704 * Guts of TS dictionary deletion.
707 RemoveTSDictionaryById(Oid dictId
)
712 relation
= heap_open(TSDictionaryRelationId
, RowExclusiveLock
);
714 tup
= SearchSysCache(TSDICTOID
,
715 ObjectIdGetDatum(dictId
),
718 if (!HeapTupleIsValid(tup
))
719 elog(ERROR
, "cache lookup failed for text search dictionary %u",
722 simple_heap_delete(relation
, &tup
->t_self
);
724 ReleaseSysCache(tup
);
726 heap_close(relation
, RowExclusiveLock
);
730 * ALTER TEXT SEARCH DICTIONARY
733 AlterTSDictionary(AlterTSDictionaryStmt
*stmt
)
743 Datum repl_val
[Natts_pg_ts_dict
];
744 char repl_null
[Natts_pg_ts_dict
];
745 char repl_repl
[Natts_pg_ts_dict
];
747 dictId
= TSDictionaryGetDictid(stmt
->dictname
, false);
749 rel
= heap_open(TSDictionaryRelationId
, RowExclusiveLock
);
751 tup
= SearchSysCache(TSDICTOID
,
752 ObjectIdGetDatum(dictId
),
755 if (!HeapTupleIsValid(tup
))
756 elog(ERROR
, "cache lookup failed for text search dictionary %u",
760 if (!pg_ts_dict_ownercheck(dictId
, GetUserId()))
761 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_TSDICTIONARY
,
762 NameListToString(stmt
->dictname
));
764 /* deserialize the existing set of options */
765 opt
= SysCacheGetAttr(TSDICTOID
, tup
,
766 Anum_pg_ts_dict_dictinitoption
,
771 dictoptions
= deserialize_deflist(opt
);
774 * Modify the options list as per specified changes
776 foreach(pl
, stmt
->options
)
778 DefElem
*defel
= (DefElem
*) lfirst(pl
);
784 * Remove any matches ...
787 for (cell
= list_head(dictoptions
); cell
; cell
= next
)
789 DefElem
*oldel
= (DefElem
*) lfirst(cell
);
792 if (pg_strcasecmp(oldel
->defname
, defel
->defname
) == 0)
793 dictoptions
= list_delete_cell(dictoptions
, cell
, prev
);
799 * and add new value if it's got one
802 dictoptions
= lappend(dictoptions
, defel
);
808 verify_dictoptions(((Form_pg_ts_dict
) GETSTRUCT(tup
))->dicttemplate
,
814 memset(repl_val
, 0, sizeof(repl_val
));
815 memset(repl_null
, ' ', sizeof(repl_null
));
816 memset(repl_repl
, ' ', sizeof(repl_repl
));
819 repl_val
[Anum_pg_ts_dict_dictinitoption
- 1] =
820 PointerGetDatum(serialize_deflist(dictoptions
));
822 repl_null
[Anum_pg_ts_dict_dictinitoption
- 1] = 'n';
823 repl_repl
[Anum_pg_ts_dict_dictinitoption
- 1] = 'r';
825 newtup
= heap_modifytuple(tup
, RelationGetDescr(rel
),
826 repl_val
, repl_null
, repl_repl
);
828 simple_heap_update(rel
, &newtup
->t_self
, newtup
);
830 CatalogUpdateIndexes(rel
, newtup
);
833 * NOTE: because we only support altering the options, not the template,
834 * there is no need to update dependencies. This might have to change if
835 * the options ever reference inside-the-database objects.
838 heap_freetuple(newtup
);
839 ReleaseSysCache(tup
);
841 heap_close(rel
, RowExclusiveLock
);
845 * ALTER TEXT SEARCH DICTIONARY OWNER
848 AlterTSDictionaryOwner(List
*name
, Oid newOwnerId
)
855 Form_pg_ts_dict form
;
857 rel
= heap_open(TSDictionaryRelationId
, RowExclusiveLock
);
859 dictId
= TSDictionaryGetDictid(name
, false);
861 tup
= SearchSysCacheCopy(TSDICTOID
,
862 ObjectIdGetDatum(dictId
),
865 if (!HeapTupleIsValid(tup
)) /* should not happen */
866 elog(ERROR
, "cache lookup failed for text search dictionary %u",
869 form
= (Form_pg_ts_dict
) GETSTRUCT(tup
);
870 namespaceOid
= form
->dictnamespace
;
872 if (form
->dictowner
!= newOwnerId
)
874 /* Superusers can always do it */
878 if (!pg_ts_dict_ownercheck(dictId
, GetUserId()))
879 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_TSDICTIONARY
,
880 NameListToString(name
));
882 /* Must be able to become new owner */
883 check_is_member_of_role(GetUserId(), newOwnerId
);
885 /* New owner must have CREATE privilege on namespace */
886 aclresult
= pg_namespace_aclcheck(namespaceOid
, newOwnerId
, ACL_CREATE
);
887 if (aclresult
!= ACLCHECK_OK
)
888 aclcheck_error(aclresult
, ACL_KIND_NAMESPACE
,
889 get_namespace_name(namespaceOid
));
892 form
->dictowner
= newOwnerId
;
894 simple_heap_update(rel
, &tup
->t_self
, tup
);
895 CatalogUpdateIndexes(rel
, tup
);
897 /* Update owner dependency reference */
898 changeDependencyOnOwner(TSDictionaryRelationId
, HeapTupleGetOid(tup
),
902 heap_close(rel
, NoLock
);
906 /* ---------------------- TS Template commands -----------------------*/
909 * lookup a template support function and return its OID (as a Datum)
911 * attnum is the pg_ts_template column the function will go into
914 get_ts_template_func(DefElem
*defel
, int attnum
)
916 List
*funcName
= defGetQualifiedName(defel
);
922 retTypeId
= INTERNALOID
;
923 typeId
[0] = INTERNALOID
;
924 typeId
[1] = INTERNALOID
;
925 typeId
[2] = INTERNALOID
;
926 typeId
[3] = INTERNALOID
;
929 case Anum_pg_ts_template_tmplinit
:
932 case Anum_pg_ts_template_tmpllexize
:
936 /* should not be here */
937 elog(ERROR
, "unrecognized attribute for text search template: %d",
939 nargs
= 0; /* keep compiler quiet */
942 procOid
= LookupFuncName(funcName
, nargs
, typeId
, false);
943 if (get_func_rettype(procOid
) != retTypeId
)
945 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
946 errmsg("function %s should return type %s",
947 func_signature_string(funcName
, nargs
, typeId
),
948 format_type_be(retTypeId
))));
950 return ObjectIdGetDatum(procOid
);
954 * make pg_depend entries for a new pg_ts_template entry
957 makeTSTemplateDependencies(HeapTuple tuple
)
959 Form_pg_ts_template tmpl
= (Form_pg_ts_template
) GETSTRUCT(tuple
);
960 ObjectAddress myself
,
963 myself
.classId
= TSTemplateRelationId
;
964 myself
.objectId
= HeapTupleGetOid(tuple
);
965 myself
.objectSubId
= 0;
967 /* dependency on namespace */
968 referenced
.classId
= NamespaceRelationId
;
969 referenced
.objectId
= tmpl
->tmplnamespace
;
970 referenced
.objectSubId
= 0;
971 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
973 /* dependencies on functions */
974 referenced
.classId
= ProcedureRelationId
;
975 referenced
.objectSubId
= 0;
977 referenced
.objectId
= tmpl
->tmpllexize
;
978 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
980 if (OidIsValid(tmpl
->tmplinit
))
982 referenced
.objectId
= tmpl
->tmplinit
;
983 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
988 * CREATE TEXT SEARCH TEMPLATE
991 DefineTSTemplate(List
*names
, List
*parameters
)
996 Datum values
[Natts_pg_ts_template
];
997 char nulls
[Natts_pg_ts_template
];
1006 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
1007 errmsg("must be superuser to create text search templates")));
1009 /* Convert list of names to a name and namespace */
1010 namespaceoid
= QualifiedNameGetCreationNamespace(names
, &tmplname
);
1012 for (i
= 0; i
< Natts_pg_ts_template
; i
++)
1015 values
[i
] = ObjectIdGetDatum(InvalidOid
);
1018 namestrcpy(&dname
, tmplname
);
1019 values
[Anum_pg_ts_template_tmplname
- 1] = NameGetDatum(&dname
);
1020 values
[Anum_pg_ts_template_tmplnamespace
- 1] = ObjectIdGetDatum(namespaceoid
);
1023 * loop over the definition list and extract the information we need.
1025 foreach(pl
, parameters
)
1027 DefElem
*defel
= (DefElem
*) lfirst(pl
);
1029 if (pg_strcasecmp(defel
->defname
, "init") == 0)
1031 values
[Anum_pg_ts_template_tmplinit
- 1] =
1032 get_ts_template_func(defel
, Anum_pg_ts_template_tmplinit
);
1033 nulls
[Anum_pg_ts_template_tmplinit
- 1] = ' ';
1035 else if (pg_strcasecmp(defel
->defname
, "lexize") == 0)
1037 values
[Anum_pg_ts_template_tmpllexize
- 1] =
1038 get_ts_template_func(defel
, Anum_pg_ts_template_tmpllexize
);
1039 nulls
[Anum_pg_ts_template_tmpllexize
- 1] = ' ';
1043 (errcode(ERRCODE_SYNTAX_ERROR
),
1044 errmsg("text search template parameter \"%s\" not recognized",
1051 if (!OidIsValid(DatumGetObjectId(values
[Anum_pg_ts_template_tmpllexize
- 1])))
1053 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
1054 errmsg("text search template lexize method is required")));
1057 * Looks good, insert
1060 tmplRel
= heap_open(TSTemplateRelationId
, RowExclusiveLock
);
1062 tup
= heap_formtuple(tmplRel
->rd_att
, values
, nulls
);
1064 dictOid
= simple_heap_insert(tmplRel
, tup
);
1066 CatalogUpdateIndexes(tmplRel
, tup
);
1068 makeTSTemplateDependencies(tup
);
1070 heap_freetuple(tup
);
1072 heap_close(tmplRel
, RowExclusiveLock
);
1076 * ALTER TEXT SEARCH TEMPLATE RENAME
1079 RenameTSTemplate(List
*oldname
, const char *newname
)
1088 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
1089 errmsg("must be superuser to rename text search templates")));
1091 rel
= heap_open(TSTemplateRelationId
, RowExclusiveLock
);
1093 tmplId
= TSTemplateGetTmplid(oldname
, false);
1095 tup
= SearchSysCacheCopy(TSTEMPLATEOID
,
1096 ObjectIdGetDatum(tmplId
),
1099 if (!HeapTupleIsValid(tup
)) /* should not happen */
1100 elog(ERROR
, "cache lookup failed for text search template %u",
1103 namespaceOid
= ((Form_pg_ts_template
) GETSTRUCT(tup
))->tmplnamespace
;
1105 if (SearchSysCacheExists(TSTEMPLATENAMENSP
,
1106 PointerGetDatum(newname
),
1107 ObjectIdGetDatum(namespaceOid
),
1110 (errcode(ERRCODE_DUPLICATE_OBJECT
),
1111 errmsg("text search template \"%s\" already exists",
1114 namestrcpy(&(((Form_pg_ts_template
) GETSTRUCT(tup
))->tmplname
), newname
);
1115 simple_heap_update(rel
, &tup
->t_self
, tup
);
1116 CatalogUpdateIndexes(rel
, tup
);
1118 heap_close(rel
, NoLock
);
1119 heap_freetuple(tup
);
1123 * DROP TEXT SEARCH TEMPLATE
1126 RemoveTSTemplates(DropStmt
*drop
)
1128 ObjectAddresses
*objects
;
1133 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
1134 errmsg("must be superuser to drop text search templates")));
1137 * First we identify all the objects, then we delete them in a single
1138 * performMultipleDeletions() call. This is to avoid unwanted
1139 * DROP RESTRICT errors if one of the objects depends on another.
1141 objects
= new_object_addresses();
1143 foreach(cell
, drop
->objects
)
1145 List
*names
= (List
*) lfirst(cell
);
1147 ObjectAddress object
;
1149 tmplOid
= TSTemplateGetTmplid(names
, true);
1151 if (!OidIsValid(tmplOid
))
1153 if (!drop
->missing_ok
)
1156 (errcode(ERRCODE_UNDEFINED_OBJECT
),
1157 errmsg("text search template \"%s\" does not exist",
1158 NameListToString(names
))));
1163 (errmsg("text search template \"%s\" does not exist, skipping",
1164 NameListToString(names
))));
1169 object
.classId
= TSTemplateRelationId
;
1170 object
.objectId
= tmplOid
;
1171 object
.objectSubId
= 0;
1173 add_exact_object_address(&object
, objects
);
1176 performMultipleDeletions(objects
, drop
->behavior
);
1178 free_object_addresses(objects
);
1182 * Guts of TS template deletion.
1185 RemoveTSTemplateById(Oid tmplId
)
1190 relation
= heap_open(TSTemplateRelationId
, RowExclusiveLock
);
1192 tup
= SearchSysCache(TSTEMPLATEOID
,
1193 ObjectIdGetDatum(tmplId
),
1196 if (!HeapTupleIsValid(tup
))
1197 elog(ERROR
, "cache lookup failed for text search template %u",
1200 simple_heap_delete(relation
, &tup
->t_self
);
1202 ReleaseSysCache(tup
);
1204 heap_close(relation
, RowExclusiveLock
);
1207 /* ---------------------- TS Configuration commands -----------------------*/
1210 * Finds syscache tuple of configuration.
1211 * Returns NULL if no such cfg.
1214 GetTSConfigTuple(List
*names
)
1219 cfgId
= TSConfigGetCfgid(names
, true);
1220 if (!OidIsValid(cfgId
))
1223 tup
= SearchSysCache(TSCONFIGOID
,
1224 ObjectIdGetDatum(cfgId
),
1227 if (!HeapTupleIsValid(tup
)) /* should not happen */
1228 elog(ERROR
, "cache lookup failed for text search configuration %u",
1235 * make pg_depend entries for a new or updated pg_ts_config entry
1237 * Pass opened pg_ts_config_map relation if there might be any config map
1238 * entries for the config.
1241 makeConfigurationDependencies(HeapTuple tuple
, bool removeOld
,
1244 Form_pg_ts_config cfg
= (Form_pg_ts_config
) GETSTRUCT(tuple
);
1245 ObjectAddresses
*addrs
;
1246 ObjectAddress myself
,
1249 myself
.classId
= TSConfigRelationId
;
1250 myself
.objectId
= HeapTupleGetOid(tuple
);
1251 myself
.objectSubId
= 0;
1253 /* for ALTER case, first flush old dependencies */
1256 deleteDependencyRecordsFor(myself
.classId
, myself
.objectId
);
1257 deleteSharedDependencyRecordsFor(myself
.classId
, myself
.objectId
);
1261 * We use an ObjectAddresses list to remove possible duplicate
1262 * dependencies from the config map info. The pg_ts_config items
1263 * shouldn't be duplicates, but might as well fold them all into one call.
1265 addrs
= new_object_addresses();
1267 /* dependency on namespace */
1268 referenced
.classId
= NamespaceRelationId
;
1269 referenced
.objectId
= cfg
->cfgnamespace
;
1270 referenced
.objectSubId
= 0;
1271 add_exact_object_address(&referenced
, addrs
);
1273 /* dependency on owner */
1274 recordDependencyOnOwner(myself
.classId
, myself
.objectId
, cfg
->cfgowner
);
1276 /* dependency on parser */
1277 referenced
.classId
= TSParserRelationId
;
1278 referenced
.objectId
= cfg
->cfgparser
;
1279 referenced
.objectSubId
= 0;
1280 add_exact_object_address(&referenced
, addrs
);
1282 /* dependencies on dictionaries listed in config map */
1289 /* CCI to ensure we can see effects of caller's changes */
1290 CommandCounterIncrement();
1293 Anum_pg_ts_config_map_mapcfg
,
1294 BTEqualStrategyNumber
, F_OIDEQ
,
1295 ObjectIdGetDatum(myself
.objectId
));
1297 scan
= systable_beginscan(mapRel
, TSConfigMapIndexId
, true,
1298 SnapshotNow
, 1, &skey
);
1300 while (HeapTupleIsValid((maptup
= systable_getnext(scan
))))
1302 Form_pg_ts_config_map cfgmap
= (Form_pg_ts_config_map
) GETSTRUCT(maptup
);
1304 referenced
.classId
= TSDictionaryRelationId
;
1305 referenced
.objectId
= cfgmap
->mapdict
;
1306 referenced
.objectSubId
= 0;
1307 add_exact_object_address(&referenced
, addrs
);
1310 systable_endscan(scan
);
1313 /* Record 'em (this includes duplicate elimination) */
1314 record_object_address_dependencies(&myself
, addrs
, DEPENDENCY_NORMAL
);
1316 free_object_addresses(addrs
);
1320 * CREATE TEXT SEARCH CONFIGURATION
1323 DefineTSConfiguration(List
*names
, List
*parameters
)
1326 Relation mapRel
= NULL
;
1328 Datum values
[Natts_pg_ts_config
];
1329 char nulls
[Natts_pg_ts_config
];
1330 AclResult aclresult
;
1334 Oid sourceOid
= InvalidOid
;
1335 Oid prsOid
= InvalidOid
;
1339 /* Convert list of names to a name and namespace */
1340 namespaceoid
= QualifiedNameGetCreationNamespace(names
, &cfgname
);
1342 /* Check we have creation rights in target namespace */
1343 aclresult
= pg_namespace_aclcheck(namespaceoid
, GetUserId(), ACL_CREATE
);
1344 if (aclresult
!= ACLCHECK_OK
)
1345 aclcheck_error(aclresult
, ACL_KIND_NAMESPACE
,
1346 get_namespace_name(namespaceoid
));
1349 * loop over the definition list and extract the information we need.
1351 foreach(pl
, parameters
)
1353 DefElem
*defel
= (DefElem
*) lfirst(pl
);
1355 if (pg_strcasecmp(defel
->defname
, "parser") == 0)
1356 prsOid
= TSParserGetPrsid(defGetQualifiedName(defel
), false);
1357 else if (pg_strcasecmp(defel
->defname
, "copy") == 0)
1358 sourceOid
= TSConfigGetCfgid(defGetQualifiedName(defel
), false);
1361 (errcode(ERRCODE_SYNTAX_ERROR
),
1362 errmsg("text search configuration parameter \"%s\" not recognized",
1366 if (OidIsValid(sourceOid
) && OidIsValid(prsOid
))
1368 (errcode(ERRCODE_SYNTAX_ERROR
),
1369 errmsg("cannot specify both PARSER and COPY options")));
1372 * Look up source config if given.
1374 if (OidIsValid(sourceOid
))
1376 Form_pg_ts_config cfg
;
1378 tup
= SearchSysCache(TSCONFIGOID
,
1379 ObjectIdGetDatum(sourceOid
),
1381 if (!HeapTupleIsValid(tup
))
1382 elog(ERROR
, "cache lookup failed for text search configuration %u",
1385 cfg
= (Form_pg_ts_config
) GETSTRUCT(tup
);
1387 /* use source's parser */
1388 prsOid
= cfg
->cfgparser
;
1390 ReleaseSysCache(tup
);
1396 if (!OidIsValid(prsOid
))
1398 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
1399 errmsg("text search parser is required")));
1402 * Looks good, build tuple and insert
1404 memset(values
, 0, sizeof(values
));
1405 memset(nulls
, ' ', sizeof(nulls
));
1407 namestrcpy(&cname
, cfgname
);
1408 values
[Anum_pg_ts_config_cfgname
- 1] = NameGetDatum(&cname
);
1409 values
[Anum_pg_ts_config_cfgnamespace
- 1] = ObjectIdGetDatum(namespaceoid
);
1410 values
[Anum_pg_ts_config_cfgowner
- 1] = ObjectIdGetDatum(GetUserId());
1411 values
[Anum_pg_ts_config_cfgparser
- 1] = ObjectIdGetDatum(prsOid
);
1413 cfgRel
= heap_open(TSConfigRelationId
, RowExclusiveLock
);
1415 tup
= heap_formtuple(cfgRel
->rd_att
, values
, nulls
);
1417 cfgOid
= simple_heap_insert(cfgRel
, tup
);
1419 CatalogUpdateIndexes(cfgRel
, tup
);
1421 if (OidIsValid(sourceOid
))
1424 * Copy token-dicts map from source config
1430 mapRel
= heap_open(TSConfigMapRelationId
, RowExclusiveLock
);
1433 Anum_pg_ts_config_map_mapcfg
,
1434 BTEqualStrategyNumber
, F_OIDEQ
,
1435 ObjectIdGetDatum(sourceOid
));
1437 scan
= systable_beginscan(mapRel
, TSConfigMapIndexId
, true,
1438 SnapshotNow
, 1, &skey
);
1440 while (HeapTupleIsValid((maptup
= systable_getnext(scan
))))
1442 Form_pg_ts_config_map cfgmap
= (Form_pg_ts_config_map
) GETSTRUCT(maptup
);
1443 HeapTuple newmaptup
;
1444 Datum mapvalues
[Natts_pg_ts_config_map
];
1445 char mapnulls
[Natts_pg_ts_config_map
];
1447 memset(mapvalues
, 0, sizeof(mapvalues
));
1448 memset(mapnulls
, ' ', sizeof(mapnulls
));
1450 mapvalues
[Anum_pg_ts_config_map_mapcfg
- 1] = cfgOid
;
1451 mapvalues
[Anum_pg_ts_config_map_maptokentype
- 1] = cfgmap
->maptokentype
;
1452 mapvalues
[Anum_pg_ts_config_map_mapseqno
- 1] = cfgmap
->mapseqno
;
1453 mapvalues
[Anum_pg_ts_config_map_mapdict
- 1] = cfgmap
->mapdict
;
1455 newmaptup
= heap_formtuple(mapRel
->rd_att
, mapvalues
, mapnulls
);
1457 simple_heap_insert(mapRel
, newmaptup
);
1459 CatalogUpdateIndexes(mapRel
, newmaptup
);
1461 heap_freetuple(newmaptup
);
1464 systable_endscan(scan
);
1467 makeConfigurationDependencies(tup
, false, mapRel
);
1469 heap_freetuple(tup
);
1472 heap_close(mapRel
, RowExclusiveLock
);
1473 heap_close(cfgRel
, RowExclusiveLock
);
1477 * ALTER TEXT SEARCH CONFIGURATION RENAME
1480 RenameTSConfiguration(List
*oldname
, const char *newname
)
1485 AclResult aclresult
;
1488 rel
= heap_open(TSConfigRelationId
, RowExclusiveLock
);
1490 cfgId
= TSConfigGetCfgid(oldname
, false);
1492 tup
= SearchSysCacheCopy(TSCONFIGOID
,
1493 ObjectIdGetDatum(cfgId
),
1496 if (!HeapTupleIsValid(tup
)) /* should not happen */
1497 elog(ERROR
, "cache lookup failed for text search configuration %u",
1500 namespaceOid
= ((Form_pg_ts_config
) GETSTRUCT(tup
))->cfgnamespace
;
1502 if (SearchSysCacheExists(TSCONFIGNAMENSP
,
1503 PointerGetDatum(newname
),
1504 ObjectIdGetDatum(namespaceOid
),
1507 (errcode(ERRCODE_DUPLICATE_OBJECT
),
1508 errmsg("text search configuration \"%s\" already exists",
1512 if (!pg_ts_config_ownercheck(cfgId
, GetUserId()))
1513 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_TSCONFIGURATION
,
1514 NameListToString(oldname
));
1516 /* must have CREATE privilege on namespace */
1517 aclresult
= pg_namespace_aclcheck(namespaceOid
, GetUserId(), ACL_CREATE
);
1518 aclcheck_error(aclresult
, ACL_KIND_NAMESPACE
,
1519 get_namespace_name(namespaceOid
));
1521 namestrcpy(&(((Form_pg_ts_config
) GETSTRUCT(tup
))->cfgname
), newname
);
1522 simple_heap_update(rel
, &tup
->t_self
, tup
);
1523 CatalogUpdateIndexes(rel
, tup
);
1525 heap_close(rel
, NoLock
);
1526 heap_freetuple(tup
);
1530 * DROP TEXT SEARCH CONFIGURATION
1533 RemoveTSConfigurations(DropStmt
*drop
)
1535 ObjectAddresses
*objects
;
1539 * First we identify all the objects, then we delete them in a single
1540 * performMultipleDeletions() call. This is to avoid unwanted
1541 * DROP RESTRICT errors if one of the objects depends on another.
1543 objects
= new_object_addresses();
1545 foreach(cell
, drop
->objects
)
1547 List
*names
= (List
*) lfirst(cell
);
1550 ObjectAddress object
;
1553 tup
= GetTSConfigTuple(names
);
1555 if (!HeapTupleIsValid(tup
))
1557 if (!drop
->missing_ok
)
1560 (errcode(ERRCODE_UNDEFINED_OBJECT
),
1561 errmsg("text search configuration \"%s\" does not exist",
1562 NameListToString(names
))));
1567 (errmsg("text search configuration \"%s\" does not exist, skipping",
1568 NameListToString(names
))));
1573 /* Permission check: must own configuration or its namespace */
1574 cfgOid
= HeapTupleGetOid(tup
);
1575 namespaceId
= ((Form_pg_ts_config
) GETSTRUCT(tup
))->cfgnamespace
;
1576 if (!pg_ts_config_ownercheck(cfgOid
, GetUserId()) &&
1577 !pg_namespace_ownercheck(namespaceId
, GetUserId()))
1578 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_TSCONFIGURATION
,
1579 NameListToString(names
));
1581 object
.classId
= TSConfigRelationId
;
1582 object
.objectId
= cfgOid
;
1583 object
.objectSubId
= 0;
1585 add_exact_object_address(&object
, objects
);
1587 ReleaseSysCache(tup
);
1590 performMultipleDeletions(objects
, drop
->behavior
);
1592 free_object_addresses(objects
);
1596 * Guts of TS configuration deletion.
1599 RemoveTSConfigurationById(Oid cfgId
)
1607 /* Remove the pg_ts_config entry */
1608 relCfg
= heap_open(TSConfigRelationId
, RowExclusiveLock
);
1610 tup
= SearchSysCache(TSCONFIGOID
,
1611 ObjectIdGetDatum(cfgId
),
1614 if (!HeapTupleIsValid(tup
))
1615 elog(ERROR
, "cache lookup failed for text search dictionary %u",
1618 simple_heap_delete(relCfg
, &tup
->t_self
);
1620 ReleaseSysCache(tup
);
1622 heap_close(relCfg
, RowExclusiveLock
);
1624 /* Remove any pg_ts_config_map entries */
1625 relMap
= heap_open(TSConfigMapRelationId
, RowExclusiveLock
);
1628 Anum_pg_ts_config_map_mapcfg
,
1629 BTEqualStrategyNumber
, F_OIDEQ
,
1630 ObjectIdGetDatum(cfgId
));
1632 scan
= systable_beginscan(relMap
, TSConfigMapIndexId
, true,
1633 SnapshotNow
, 1, &skey
);
1635 while (HeapTupleIsValid((tup
= systable_getnext(scan
))))
1637 simple_heap_delete(relMap
, &tup
->t_self
);
1640 systable_endscan(scan
);
1642 heap_close(relMap
, RowExclusiveLock
);
1646 * ALTER TEXT SEARCH CONFIGURATION OWNER
1649 AlterTSConfigurationOwner(List
*name
, Oid newOwnerId
)
1654 AclResult aclresult
;
1656 Form_pg_ts_config form
;
1658 rel
= heap_open(TSConfigRelationId
, RowExclusiveLock
);
1660 cfgId
= TSConfigGetCfgid(name
, false);
1662 tup
= SearchSysCacheCopy(TSCONFIGOID
,
1663 ObjectIdGetDatum(cfgId
),
1666 if (!HeapTupleIsValid(tup
)) /* should not happen */
1667 elog(ERROR
, "cache lookup failed for text search configuration %u",
1670 form
= (Form_pg_ts_config
) GETSTRUCT(tup
);
1671 namespaceOid
= form
->cfgnamespace
;
1673 if (form
->cfgowner
!= newOwnerId
)
1675 /* Superusers can always do it */
1679 if (!pg_ts_config_ownercheck(cfgId
, GetUserId()))
1680 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_TSCONFIGURATION
,
1681 NameListToString(name
));
1683 /* Must be able to become new owner */
1684 check_is_member_of_role(GetUserId(), newOwnerId
);
1686 /* New owner must have CREATE privilege on namespace */
1687 aclresult
= pg_namespace_aclcheck(namespaceOid
, newOwnerId
, ACL_CREATE
);
1688 if (aclresult
!= ACLCHECK_OK
)
1689 aclcheck_error(aclresult
, ACL_KIND_NAMESPACE
,
1690 get_namespace_name(namespaceOid
));
1693 form
->cfgowner
= newOwnerId
;
1695 simple_heap_update(rel
, &tup
->t_self
, tup
);
1696 CatalogUpdateIndexes(rel
, tup
);
1698 /* Update owner dependency reference */
1699 changeDependencyOnOwner(TSConfigRelationId
, HeapTupleGetOid(tup
),
1703 heap_close(rel
, NoLock
);
1704 heap_freetuple(tup
);
1708 * ALTER TEXT SEARCH CONFIGURATION - main entry point
1711 AlterTSConfiguration(AlterTSConfigurationStmt
*stmt
)
1716 /* Find the configuration */
1717 tup
= GetTSConfigTuple(stmt
->cfgname
);
1718 if (!HeapTupleIsValid(tup
))
1720 (errcode(ERRCODE_UNDEFINED_OBJECT
),
1721 errmsg("text search configuration \"%s\" does not exist",
1722 NameListToString(stmt
->cfgname
))));
1725 if (!pg_ts_config_ownercheck(HeapTupleGetOid(tup
), GetUserId()))
1726 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_TSCONFIGURATION
,
1727 NameListToString(stmt
->cfgname
));
1729 relMap
= heap_open(TSConfigMapRelationId
, RowExclusiveLock
);
1731 /* Add or drop mappings */
1733 MakeConfigurationMapping(stmt
, tup
, relMap
);
1734 else if (stmt
->tokentype
)
1735 DropConfigurationMapping(stmt
, tup
, relMap
);
1737 /* Update dependencies */
1738 makeConfigurationDependencies(tup
, true, relMap
);
1740 heap_close(relMap
, RowExclusiveLock
);
1742 ReleaseSysCache(tup
);
1746 * Translate a list of token type names to an array of token type numbers
1749 getTokenTypes(Oid prsId
, List
*tokennames
)
1751 TSParserCacheEntry
*prs
= lookup_ts_parser_cache(prsId
);
1758 ntoken
= list_length(tokennames
);
1761 res
= (int *) palloc(sizeof(int) * ntoken
);
1763 if (!OidIsValid(prs
->lextypeOid
))
1764 elog(ERROR
, "method lextype isn't defined for text search parser %u",
1767 /* OidFunctionCall0 is absent */
1768 list
= (LexDescr
*) DatumGetPointer(OidFunctionCall1(prs
->lextypeOid
,
1772 foreach(tn
, tokennames
)
1774 Value
*val
= (Value
*) lfirst(tn
);
1779 while (list
&& list
[j
].lexid
)
1781 /* XXX should we use pg_strcasecmp here? */
1782 if (strcmp(strVal(val
), list
[j
].alias
) == 0)
1784 res
[i
] = list
[j
].lexid
;
1792 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
1793 errmsg("token type \"%s\" does not exist",
1802 * ALTER TEXT SEARCH CONFIGURATION ADD/ALTER MAPPING
1805 MakeConfigurationMapping(AlterTSConfigurationStmt
*stmt
,
1806 HeapTuple tup
, Relation relMap
)
1808 Oid cfgId
= HeapTupleGetOid(tup
);
1809 ScanKeyData skey
[2];
1821 prsId
= ((Form_pg_ts_config
) GETSTRUCT(tup
))->cfgparser
;
1823 tokens
= getTokenTypes(prsId
, stmt
->tokentype
);
1824 ntoken
= list_length(stmt
->tokentype
);
1829 * delete maps for tokens if they exist and command was ALTER
1831 for (i
= 0; i
< ntoken
; i
++)
1833 ScanKeyInit(&skey
[0],
1834 Anum_pg_ts_config_map_mapcfg
,
1835 BTEqualStrategyNumber
, F_OIDEQ
,
1836 ObjectIdGetDatum(cfgId
));
1837 ScanKeyInit(&skey
[1],
1838 Anum_pg_ts_config_map_maptokentype
,
1839 BTEqualStrategyNumber
, F_INT4EQ
,
1840 Int32GetDatum(tokens
[i
]));
1842 scan
= systable_beginscan(relMap
, TSConfigMapIndexId
, true,
1843 SnapshotNow
, 2, skey
);
1845 while (HeapTupleIsValid((maptup
= systable_getnext(scan
))))
1847 simple_heap_delete(relMap
, &maptup
->t_self
);
1850 systable_endscan(scan
);
1855 * Convert list of dictionary names to array of dict OIDs
1857 ndict
= list_length(stmt
->dicts
);
1858 dictIds
= (Oid
*) palloc(sizeof(Oid
) * ndict
);
1860 foreach(c
, stmt
->dicts
)
1862 List
*names
= (List
*) lfirst(c
);
1864 dictIds
[i
] = TSDictionaryGetDictid(names
, false);
1871 * Replace a specific dictionary in existing entries
1873 Oid dictOld
= dictIds
[0],
1874 dictNew
= dictIds
[1];
1876 ScanKeyInit(&skey
[0],
1877 Anum_pg_ts_config_map_mapcfg
,
1878 BTEqualStrategyNumber
, F_OIDEQ
,
1879 ObjectIdGetDatum(cfgId
));
1881 scan
= systable_beginscan(relMap
, TSConfigMapIndexId
, true,
1882 SnapshotNow
, 1, skey
);
1884 while (HeapTupleIsValid((maptup
= systable_getnext(scan
))))
1886 Form_pg_ts_config_map cfgmap
= (Form_pg_ts_config_map
) GETSTRUCT(maptup
);
1889 * check if it's one of target token types
1893 bool tokmatch
= false;
1895 for (j
= 0; j
< ntoken
; j
++)
1897 if (cfgmap
->maptokentype
== tokens
[j
])
1908 * replace dictionary if match
1910 if (cfgmap
->mapdict
== dictOld
)
1912 Datum repl_val
[Natts_pg_ts_config_map
];
1913 char repl_null
[Natts_pg_ts_config_map
];
1914 char repl_repl
[Natts_pg_ts_config_map
];
1917 memset(repl_val
, 0, sizeof(repl_val
));
1918 memset(repl_null
, ' ', sizeof(repl_null
));
1919 memset(repl_repl
, ' ', sizeof(repl_repl
));
1921 repl_val
[Anum_pg_ts_config_map_mapdict
- 1] = ObjectIdGetDatum(dictNew
);
1922 repl_repl
[Anum_pg_ts_config_map_mapdict
- 1] = 'r';
1924 newtup
= heap_modifytuple(maptup
,
1925 RelationGetDescr(relMap
),
1926 repl_val
, repl_null
, repl_repl
);
1927 simple_heap_update(relMap
, &newtup
->t_self
, newtup
);
1929 CatalogUpdateIndexes(relMap
, newtup
);
1933 systable_endscan(scan
);
1938 * Insertion of new entries
1940 for (i
= 0; i
< ntoken
; i
++)
1942 for (j
= 0; j
< ndict
; j
++)
1944 Datum values
[Natts_pg_ts_config_map
];
1945 char nulls
[Natts_pg_ts_config_map
];
1947 memset(nulls
, ' ', sizeof(nulls
));
1948 values
[Anum_pg_ts_config_map_mapcfg
- 1] = ObjectIdGetDatum(cfgId
);
1949 values
[Anum_pg_ts_config_map_maptokentype
- 1] = Int32GetDatum(tokens
[i
]);
1950 values
[Anum_pg_ts_config_map_mapseqno
- 1] = Int32GetDatum(j
+ 1);
1951 values
[Anum_pg_ts_config_map_mapdict
- 1] = ObjectIdGetDatum(dictIds
[j
]);
1953 tup
= heap_formtuple(relMap
->rd_att
, values
, nulls
);
1954 simple_heap_insert(relMap
, tup
);
1955 CatalogUpdateIndexes(relMap
, tup
);
1957 heap_freetuple(tup
);
1964 * ALTER TEXT SEARCH CONFIGURATION DROP MAPPING
1967 DropConfigurationMapping(AlterTSConfigurationStmt
*stmt
,
1968 HeapTuple tup
, Relation relMap
)
1970 Oid cfgId
= HeapTupleGetOid(tup
);
1971 ScanKeyData skey
[2];
1980 prsId
= ((Form_pg_ts_config
) GETSTRUCT(tup
))->cfgparser
;
1982 tokens
= getTokenTypes(prsId
, stmt
->tokentype
);
1983 ntoken
= list_length(stmt
->tokentype
);
1986 foreach(c
, stmt
->tokentype
)
1988 Value
*val
= (Value
*) lfirst(c
);
1991 ScanKeyInit(&skey
[0],
1992 Anum_pg_ts_config_map_mapcfg
,
1993 BTEqualStrategyNumber
, F_OIDEQ
,
1994 ObjectIdGetDatum(cfgId
));
1995 ScanKeyInit(&skey
[1],
1996 Anum_pg_ts_config_map_maptokentype
,
1997 BTEqualStrategyNumber
, F_INT4EQ
,
1998 Int32GetDatum(tokens
[i
]));
2000 scan
= systable_beginscan(relMap
, TSConfigMapIndexId
, true,
2001 SnapshotNow
, 2, skey
);
2003 while (HeapTupleIsValid((maptup
= systable_getnext(scan
))))
2005 simple_heap_delete(relMap
, &maptup
->t_self
);
2009 systable_endscan(scan
);
2013 if (!stmt
->missing_ok
)
2016 (errcode(ERRCODE_UNDEFINED_OBJECT
),
2017 errmsg("mapping for token type \"%s\" does not exist",
2023 (errmsg("mapping for token type \"%s\" does not exist, skipping",
2034 * Serialize dictionary options, producing a TEXT datum from a List of DefElem
2036 * This is used to form the value stored in pg_ts_dict.dictinitoption.
2037 * For the convenience of pg_dump, the output is formatted exactly as it
2038 * would need to appear in CREATE TEXT SEARCH DICTIONARY to reproduce the
2041 * Note that we assume that only the textual representation of an option's
2042 * value is interesting --- hence, non-string DefElems get forced to strings.
2045 serialize_deflist(List
*deflist
)
2051 initStringInfo(&buf
);
2055 DefElem
*defel
= (DefElem
*) lfirst(l
);
2056 char *val
= defGetString(defel
);
2058 appendStringInfo(&buf
, "%s = ",
2059 quote_identifier(defel
->defname
));
2060 /* If backslashes appear, force E syntax to determine their handling */
2061 if (strchr(val
, '\\'))
2062 appendStringInfoChar(&buf
, ESCAPE_STRING_SYNTAX
);
2063 appendStringInfoChar(&buf
, '\'');
2068 if (SQL_STR_DOUBLE(ch
, true))
2069 appendStringInfoChar(&buf
, ch
);
2070 appendStringInfoChar(&buf
, ch
);
2072 appendStringInfoChar(&buf
, '\'');
2073 if (lnext(l
) != NULL
)
2074 appendStringInfo(&buf
, ", ");
2077 result
= cstring_to_text_with_len(buf
.data
, buf
.len
);
2083 * Deserialize dictionary options, reconstructing a List of DefElem from TEXT
2085 * This is also used for prsheadline options, so for backward compatibility
2086 * we need to accept a few things serialize_deflist() will never emit:
2087 * in particular, unquoted and double-quoted values.
2090 deserialize_deflist(Datum txt
)
2092 text
*in
= DatumGetTextP(txt
); /* in case it's toasted */
2094 int len
= VARSIZE(in
) - VARHDRSZ
;
2111 ds_state state
= CS_WAITKEY
;
2113 workspace
= (char *) palloc(len
+ 1); /* certainly enough room */
2116 for (; ptr
< endptr
; ptr
++)
2121 if (isspace((unsigned char) *ptr
) || *ptr
== ',')
2136 if (isspace((unsigned char) *ptr
))
2141 else if (*ptr
== '=')
2144 state
= CS_WAITVALUE
;
2154 if (ptr
+ 1 < endptr
&& ptr
[1] == '"')
2156 /* copy only one of the two quotes */
2172 state
= CS_WAITVALUE
;
2173 else if (!isspace((unsigned char) *ptr
))
2175 (errcode(ERRCODE_SYNTAX_ERROR
),
2176 errmsg("invalid parameter list format: \"%s\"",
2177 text_to_cstring(in
))));
2183 state
= CS_INSQVALUE
;
2185 else if (*ptr
== 'E' && ptr
+ 1 < endptr
&& ptr
[1] == '\'')
2189 state
= CS_INSQVALUE
;
2191 else if (*ptr
== '"')
2194 state
= CS_INDQVALUE
;
2196 else if (!isspace((unsigned char) *ptr
))
2200 state
= CS_INWVALUE
;
2206 if (ptr
+ 1 < endptr
&& ptr
[1] == '\'')
2208 /* copy only one of the two quotes */
2214 result
= lappend(result
,
2215 makeDefElem(pstrdup(workspace
),
2216 (Node
*) makeString(pstrdup(startvalue
))));
2220 else if (*ptr
== '\\')
2222 if (ptr
+ 1 < endptr
&& ptr
[1] == '\\')
2224 /* copy only one of the two backslashes */
2238 if (ptr
+ 1 < endptr
&& ptr
[1] == '"')
2240 /* copy only one of the two quotes */
2246 result
= lappend(result
,
2247 makeDefElem(pstrdup(workspace
),
2248 (Node
*) makeString(pstrdup(startvalue
))));
2258 if (*ptr
== ',' || isspace((unsigned char) *ptr
))
2261 result
= lappend(result
,
2262 makeDefElem(pstrdup(workspace
),
2263 (Node
*) makeString(pstrdup(startvalue
))));
2272 elog(ERROR
, "unrecognized deserialize_deflist state: %d",
2277 if (state
== CS_INWVALUE
)
2280 result
= lappend(result
,
2281 makeDefElem(pstrdup(workspace
),
2282 (Node
*) makeString(pstrdup(startvalue
))));
2284 else if (state
!= CS_WAITKEY
)
2286 (errcode(ERRCODE_SYNTAX_ERROR
),
2287 errmsg("invalid parameter list format: \"%s\"",
2288 text_to_cstring(in
))));