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/rel.h"
47 #include "utils/syscache.h"
48 #include "utils/tqual.h"
51 static void MakeConfigurationMapping(AlterTSConfigurationStmt
*stmt
,
52 HeapTuple tup
, Relation relMap
);
53 static void DropConfigurationMapping(AlterTSConfigurationStmt
*stmt
,
54 HeapTuple tup
, Relation relMap
);
57 /* --------------------- TS Parser commands ------------------------ */
60 * lookup a parser support function and return its OID (as a Datum)
62 * attnum is the pg_ts_parser column the function will go into
65 get_ts_parser_func(DefElem
*defel
, int attnum
)
67 List
*funcName
= defGetQualifiedName(defel
);
73 retTypeId
= INTERNALOID
; /* correct for most */
74 typeId
[0] = INTERNALOID
;
77 case Anum_pg_ts_parser_prsstart
:
81 case Anum_pg_ts_parser_prstoken
:
83 typeId
[1] = INTERNALOID
;
84 typeId
[2] = INTERNALOID
;
86 case Anum_pg_ts_parser_prsend
:
90 case Anum_pg_ts_parser_prsheadline
:
92 typeId
[1] = INTERNALOID
;
93 typeId
[2] = TSQUERYOID
;
95 case Anum_pg_ts_parser_prslextype
:
99 /* should not be here */
100 elog(ERROR
, "unrecognized attribute for text search parser: %d",
102 nargs
= 0; /* keep compiler quiet */
105 procOid
= LookupFuncName(funcName
, nargs
, typeId
, false);
106 if (get_func_rettype(procOid
) != retTypeId
)
108 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
109 errmsg("function %s should return type %s",
110 func_signature_string(funcName
, nargs
, typeId
),
111 format_type_be(retTypeId
))));
113 return ObjectIdGetDatum(procOid
);
117 * make pg_depend entries for a new pg_ts_parser entry
120 makeParserDependencies(HeapTuple tuple
)
122 Form_pg_ts_parser prs
= (Form_pg_ts_parser
) GETSTRUCT(tuple
);
123 ObjectAddress myself
,
126 myself
.classId
= TSParserRelationId
;
127 myself
.objectId
= HeapTupleGetOid(tuple
);
128 myself
.objectSubId
= 0;
130 /* dependency on namespace */
131 referenced
.classId
= NamespaceRelationId
;
132 referenced
.objectId
= prs
->prsnamespace
;
133 referenced
.objectSubId
= 0;
134 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
136 /* dependencies on functions */
137 referenced
.classId
= ProcedureRelationId
;
138 referenced
.objectSubId
= 0;
140 referenced
.objectId
= prs
->prsstart
;
141 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
143 referenced
.objectId
= prs
->prstoken
;
144 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
146 referenced
.objectId
= prs
->prsend
;
147 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
149 referenced
.objectId
= prs
->prslextype
;
150 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
152 if (OidIsValid(prs
->prsheadline
))
154 referenced
.objectId
= prs
->prsheadline
;
155 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
160 * CREATE TEXT SEARCH PARSER
163 DefineTSParser(List
*names
, List
*parameters
)
169 Datum values
[Natts_pg_ts_parser
];
170 char nulls
[Natts_pg_ts_parser
];
177 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
178 errmsg("must be superuser to create text search parsers")));
180 /* Convert list of names to a name and namespace */
181 namespaceoid
= QualifiedNameGetCreationNamespace(names
, &prsname
);
183 /* initialize tuple fields with name/namespace */
184 memset(values
, 0, sizeof(values
));
185 memset(nulls
, ' ', sizeof(nulls
));
187 namestrcpy(&pname
, prsname
);
188 values
[Anum_pg_ts_parser_prsname
- 1] = NameGetDatum(&pname
);
189 values
[Anum_pg_ts_parser_prsnamespace
- 1] = ObjectIdGetDatum(namespaceoid
);
192 * loop over the definition list and extract the information we need.
194 foreach(pl
, parameters
)
196 DefElem
*defel
= (DefElem
*) lfirst(pl
);
198 if (pg_strcasecmp(defel
->defname
, "start") == 0)
200 values
[Anum_pg_ts_parser_prsstart
- 1] =
201 get_ts_parser_func(defel
, Anum_pg_ts_parser_prsstart
);
203 else if (pg_strcasecmp(defel
->defname
, "gettoken") == 0)
205 values
[Anum_pg_ts_parser_prstoken
- 1] =
206 get_ts_parser_func(defel
, Anum_pg_ts_parser_prstoken
);
208 else if (pg_strcasecmp(defel
->defname
, "end") == 0)
210 values
[Anum_pg_ts_parser_prsend
- 1] =
211 get_ts_parser_func(defel
, Anum_pg_ts_parser_prsend
);
213 else if (pg_strcasecmp(defel
->defname
, "headline") == 0)
215 values
[Anum_pg_ts_parser_prsheadline
- 1] =
216 get_ts_parser_func(defel
, Anum_pg_ts_parser_prsheadline
);
218 else if (pg_strcasecmp(defel
->defname
, "lextypes") == 0)
220 values
[Anum_pg_ts_parser_prslextype
- 1] =
221 get_ts_parser_func(defel
, Anum_pg_ts_parser_prslextype
);
225 (errcode(ERRCODE_SYNTAX_ERROR
),
226 errmsg("text search parser parameter \"%s\" not recognized",
233 if (!OidIsValid(DatumGetObjectId(values
[Anum_pg_ts_parser_prsstart
- 1])))
235 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
236 errmsg("text search parser start method is required")));
238 if (!OidIsValid(DatumGetObjectId(values
[Anum_pg_ts_parser_prstoken
- 1])))
240 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
241 errmsg("text search parser gettoken method is required")));
243 if (!OidIsValid(DatumGetObjectId(values
[Anum_pg_ts_parser_prsend
- 1])))
245 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
246 errmsg("text search parser end method is required")));
248 if (!OidIsValid(DatumGetObjectId(values
[Anum_pg_ts_parser_prslextype
- 1])))
250 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
251 errmsg("text search parser lextypes method is required")));
256 prsRel
= heap_open(TSParserRelationId
, RowExclusiveLock
);
258 tup
= heap_formtuple(prsRel
->rd_att
, values
, nulls
);
260 prsOid
= simple_heap_insert(prsRel
, tup
);
262 CatalogUpdateIndexes(prsRel
, tup
);
264 makeParserDependencies(tup
);
268 heap_close(prsRel
, RowExclusiveLock
);
272 * DROP TEXT SEARCH PARSER
275 RemoveTSParsers(DropStmt
*drop
)
277 ObjectAddresses
*objects
;
282 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
283 errmsg("must be superuser to drop text search parsers")));
286 * First we identify all the objects, then we delete them in a single
287 * performMultipleDeletions() call. This is to avoid unwanted
288 * DROP RESTRICT errors if one of the objects depends on another.
290 objects
= new_object_addresses();
292 foreach(cell
, drop
->objects
)
294 List
*names
= (List
*) lfirst(cell
);
296 ObjectAddress object
;
298 prsOid
= TSParserGetPrsid(names
, true);
300 if (!OidIsValid(prsOid
))
302 if (!drop
->missing_ok
)
305 (errcode(ERRCODE_UNDEFINED_OBJECT
),
306 errmsg("text search parser \"%s\" does not exist",
307 NameListToString(names
))));
312 (errmsg("text search parser \"%s\" does not exist, skipping",
313 NameListToString(names
))));
318 object
.classId
= TSParserRelationId
;
319 object
.objectId
= prsOid
;
320 object
.objectSubId
= 0;
322 add_exact_object_address(&object
, objects
);
325 performMultipleDeletions(objects
, drop
->behavior
);
327 free_object_addresses(objects
);
331 * Guts of TS parser deletion.
334 RemoveTSParserById(Oid prsId
)
339 relation
= heap_open(TSParserRelationId
, RowExclusiveLock
);
341 tup
= SearchSysCache(TSPARSEROID
,
342 ObjectIdGetDatum(prsId
),
345 if (!HeapTupleIsValid(tup
))
346 elog(ERROR
, "cache lookup failed for text search parser %u", prsId
);
348 simple_heap_delete(relation
, &tup
->t_self
);
350 ReleaseSysCache(tup
);
352 heap_close(relation
, RowExclusiveLock
);
356 * ALTER TEXT SEARCH PARSER RENAME
359 RenameTSParser(List
*oldname
, const char *newname
)
368 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
369 errmsg("must be superuser to rename text search parsers")));
371 rel
= heap_open(TSParserRelationId
, RowExclusiveLock
);
373 prsId
= TSParserGetPrsid(oldname
, false);
375 tup
= SearchSysCacheCopy(TSPARSEROID
,
376 ObjectIdGetDatum(prsId
),
379 if (!HeapTupleIsValid(tup
)) /* should not happen */
380 elog(ERROR
, "cache lookup failed for text search parser %u", prsId
);
382 namespaceOid
= ((Form_pg_ts_parser
) GETSTRUCT(tup
))->prsnamespace
;
384 if (SearchSysCacheExists(TSPARSERNAMENSP
,
385 PointerGetDatum(newname
),
386 ObjectIdGetDatum(namespaceOid
),
389 (errcode(ERRCODE_DUPLICATE_OBJECT
),
390 errmsg("text search parser \"%s\" already exists",
393 namestrcpy(&(((Form_pg_ts_parser
) GETSTRUCT(tup
))->prsname
), newname
);
394 simple_heap_update(rel
, &tup
->t_self
, tup
);
395 CatalogUpdateIndexes(rel
, tup
);
397 heap_close(rel
, NoLock
);
401 /* ---------------------- TS Dictionary commands -----------------------*/
404 * make pg_depend entries for a new pg_ts_dict entry
407 makeDictionaryDependencies(HeapTuple tuple
)
409 Form_pg_ts_dict dict
= (Form_pg_ts_dict
) GETSTRUCT(tuple
);
410 ObjectAddress myself
,
413 myself
.classId
= TSDictionaryRelationId
;
414 myself
.objectId
= HeapTupleGetOid(tuple
);
415 myself
.objectSubId
= 0;
417 /* dependency on namespace */
418 referenced
.classId
= NamespaceRelationId
;
419 referenced
.objectId
= dict
->dictnamespace
;
420 referenced
.objectSubId
= 0;
421 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
423 /* dependency on owner */
424 recordDependencyOnOwner(myself
.classId
, myself
.objectId
, dict
->dictowner
);
426 /* dependency on template */
427 referenced
.classId
= TSTemplateRelationId
;
428 referenced
.objectId
= dict
->dicttemplate
;
429 referenced
.objectSubId
= 0;
430 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
434 * verify that a template's init method accepts a proposed option list
437 verify_dictoptions(Oid tmplId
, List
*dictoptions
)
440 Form_pg_ts_template tform
;
444 * Suppress this test when running in a standalone backend. This is a
445 * hack to allow initdb to create prefab dictionaries that might not
446 * actually be usable in template1's encoding (due to using external files
447 * that can't be translated into template1's encoding). We want to create
448 * them anyway, since they might be usable later in other databases.
450 if (!IsUnderPostmaster
)
453 tup
= SearchSysCache(TSTEMPLATEOID
,
454 ObjectIdGetDatum(tmplId
),
456 if (!HeapTupleIsValid(tup
)) /* should not happen */
457 elog(ERROR
, "cache lookup failed for text search template %u",
459 tform
= (Form_pg_ts_template
) GETSTRUCT(tup
);
461 initmethod
= tform
->tmplinit
;
463 if (!OidIsValid(initmethod
))
465 /* If there is no init method, disallow any options */
468 (errcode(ERRCODE_SYNTAX_ERROR
),
469 errmsg("text search template \"%s\" does not accept options",
470 NameStr(tform
->tmplname
))));
475 * Copy the options just in case init method thinks it can scribble on
478 dictoptions
= copyObject(dictoptions
);
481 * Call the init method and see if it complains. We don't worry about
482 * it leaking memory, since our command will soon be over anyway.
484 (void) OidFunctionCall1(initmethod
, PointerGetDatum(dictoptions
));
487 ReleaseSysCache(tup
);
491 * CREATE TEXT SEARCH DICTIONARY
494 DefineTSDictionary(List
*names
, List
*parameters
)
499 Datum values
[Natts_pg_ts_dict
];
500 char nulls
[Natts_pg_ts_dict
];
502 Oid templId
= InvalidOid
;
503 List
*dictoptions
= NIL
;
509 /* Convert list of names to a name and namespace */
510 namespaceoid
= QualifiedNameGetCreationNamespace(names
, &dictname
);
512 /* Check we have creation rights in target namespace */
513 aclresult
= pg_namespace_aclcheck(namespaceoid
, GetUserId(), ACL_CREATE
);
514 if (aclresult
!= ACLCHECK_OK
)
515 aclcheck_error(aclresult
, ACL_KIND_NAMESPACE
,
516 get_namespace_name(namespaceoid
));
519 * loop over the definition list and extract the information we need.
521 foreach(pl
, parameters
)
523 DefElem
*defel
= (DefElem
*) lfirst(pl
);
525 if (pg_strcasecmp(defel
->defname
, "template") == 0)
527 templId
= TSTemplateGetTmplid(defGetQualifiedName(defel
), false);
531 /* Assume it's an option for the dictionary itself */
532 dictoptions
= lappend(dictoptions
, defel
);
539 if (!OidIsValid(templId
))
541 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
542 errmsg("text search template is required")));
544 verify_dictoptions(templId
, dictoptions
);
549 memset(values
, 0, sizeof(values
));
550 memset(nulls
, ' ', sizeof(nulls
));
552 namestrcpy(&dname
, dictname
);
553 values
[Anum_pg_ts_dict_dictname
- 1] = NameGetDatum(&dname
);
554 values
[Anum_pg_ts_dict_dictnamespace
- 1] = ObjectIdGetDatum(namespaceoid
);
555 values
[Anum_pg_ts_dict_dictowner
- 1] = ObjectIdGetDatum(GetUserId());
556 values
[Anum_pg_ts_dict_dicttemplate
- 1] = ObjectIdGetDatum(templId
);
558 values
[Anum_pg_ts_dict_dictinitoption
- 1] =
559 PointerGetDatum(serialize_deflist(dictoptions
));
561 nulls
[Anum_pg_ts_dict_dictinitoption
- 1] = 'n';
563 dictRel
= heap_open(TSDictionaryRelationId
, RowExclusiveLock
);
565 tup
= heap_formtuple(dictRel
->rd_att
, values
, nulls
);
567 dictOid
= simple_heap_insert(dictRel
, tup
);
569 CatalogUpdateIndexes(dictRel
, tup
);
571 makeDictionaryDependencies(tup
);
575 heap_close(dictRel
, RowExclusiveLock
);
579 * ALTER TEXT SEARCH DICTIONARY RENAME
582 RenameTSDictionary(List
*oldname
, const char *newname
)
590 rel
= heap_open(TSDictionaryRelationId
, RowExclusiveLock
);
592 dictId
= TSDictionaryGetDictid(oldname
, false);
594 tup
= SearchSysCacheCopy(TSDICTOID
,
595 ObjectIdGetDatum(dictId
),
598 if (!HeapTupleIsValid(tup
)) /* should not happen */
599 elog(ERROR
, "cache lookup failed for text search dictionary %u",
602 namespaceOid
= ((Form_pg_ts_dict
) GETSTRUCT(tup
))->dictnamespace
;
604 if (SearchSysCacheExists(TSDICTNAMENSP
,
605 PointerGetDatum(newname
),
606 ObjectIdGetDatum(namespaceOid
),
609 (errcode(ERRCODE_DUPLICATE_OBJECT
),
610 errmsg("text search dictionary \"%s\" already exists",
614 if (!pg_ts_dict_ownercheck(dictId
, GetUserId()))
615 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_TSDICTIONARY
,
616 NameListToString(oldname
));
618 /* must have CREATE privilege on namespace */
619 aclresult
= pg_namespace_aclcheck(namespaceOid
, GetUserId(), ACL_CREATE
);
620 if (aclresult
!= ACLCHECK_OK
)
621 aclcheck_error(aclresult
, ACL_KIND_NAMESPACE
,
622 get_namespace_name(namespaceOid
));
624 namestrcpy(&(((Form_pg_ts_dict
) GETSTRUCT(tup
))->dictname
), newname
);
625 simple_heap_update(rel
, &tup
->t_self
, tup
);
626 CatalogUpdateIndexes(rel
, tup
);
628 heap_close(rel
, NoLock
);
633 * DROP TEXT SEARCH DICTIONARY
636 RemoveTSDictionaries(DropStmt
*drop
)
638 ObjectAddresses
*objects
;
642 * First we identify all the objects, then we delete them in a single
643 * performMultipleDeletions() call. This is to avoid unwanted
644 * DROP RESTRICT errors if one of the objects depends on another.
646 objects
= new_object_addresses();
648 foreach(cell
, drop
->objects
)
650 List
*names
= (List
*) lfirst(cell
);
652 ObjectAddress object
;
656 dictOid
= TSDictionaryGetDictid(names
, true);
658 if (!OidIsValid(dictOid
))
660 if (!drop
->missing_ok
)
663 (errcode(ERRCODE_UNDEFINED_OBJECT
),
664 errmsg("text search dictionary \"%s\" does not exist",
665 NameListToString(names
))));
670 (errmsg("text search dictionary \"%s\" does not exist, skipping",
671 NameListToString(names
))));
676 tup
= SearchSysCache(TSDICTOID
,
677 ObjectIdGetDatum(dictOid
),
679 if (!HeapTupleIsValid(tup
)) /* should not happen */
680 elog(ERROR
, "cache lookup failed for text search dictionary %u",
683 /* Permission check: must own dictionary or its namespace */
684 namespaceId
= ((Form_pg_ts_dict
) GETSTRUCT(tup
))->dictnamespace
;
685 if (!pg_ts_dict_ownercheck(dictOid
, GetUserId()) &&
686 !pg_namespace_ownercheck(namespaceId
, GetUserId()))
687 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_TSDICTIONARY
,
688 NameListToString(names
));
690 object
.classId
= TSDictionaryRelationId
;
691 object
.objectId
= dictOid
;
692 object
.objectSubId
= 0;
694 add_exact_object_address(&object
, objects
);
696 ReleaseSysCache(tup
);
699 performMultipleDeletions(objects
, drop
->behavior
);
701 free_object_addresses(objects
);
705 * Guts of TS dictionary deletion.
708 RemoveTSDictionaryById(Oid dictId
)
713 relation
= heap_open(TSDictionaryRelationId
, RowExclusiveLock
);
715 tup
= SearchSysCache(TSDICTOID
,
716 ObjectIdGetDatum(dictId
),
719 if (!HeapTupleIsValid(tup
))
720 elog(ERROR
, "cache lookup failed for text search dictionary %u",
723 simple_heap_delete(relation
, &tup
->t_self
);
725 ReleaseSysCache(tup
);
727 heap_close(relation
, RowExclusiveLock
);
731 * ALTER TEXT SEARCH DICTIONARY
734 AlterTSDictionary(AlterTSDictionaryStmt
*stmt
)
744 Datum repl_val
[Natts_pg_ts_dict
];
745 char repl_null
[Natts_pg_ts_dict
];
746 char repl_repl
[Natts_pg_ts_dict
];
748 dictId
= TSDictionaryGetDictid(stmt
->dictname
, false);
750 rel
= heap_open(TSDictionaryRelationId
, RowExclusiveLock
);
752 tup
= SearchSysCache(TSDICTOID
,
753 ObjectIdGetDatum(dictId
),
756 if (!HeapTupleIsValid(tup
))
757 elog(ERROR
, "cache lookup failed for text search dictionary %u",
761 if (!pg_ts_dict_ownercheck(dictId
, GetUserId()))
762 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_TSDICTIONARY
,
763 NameListToString(stmt
->dictname
));
765 /* deserialize the existing set of options */
766 opt
= SysCacheGetAttr(TSDICTOID
, tup
,
767 Anum_pg_ts_dict_dictinitoption
,
772 dictoptions
= deserialize_deflist(opt
);
775 * Modify the options list as per specified changes
777 foreach(pl
, stmt
->options
)
779 DefElem
*defel
= (DefElem
*) lfirst(pl
);
785 * Remove any matches ...
788 for (cell
= list_head(dictoptions
); cell
; cell
= next
)
790 DefElem
*oldel
= (DefElem
*) lfirst(cell
);
793 if (pg_strcasecmp(oldel
->defname
, defel
->defname
) == 0)
794 dictoptions
= list_delete_cell(dictoptions
, cell
, prev
);
800 * and add new value if it's got one
803 dictoptions
= lappend(dictoptions
, defel
);
809 verify_dictoptions(((Form_pg_ts_dict
) GETSTRUCT(tup
))->dicttemplate
,
815 memset(repl_val
, 0, sizeof(repl_val
));
816 memset(repl_null
, ' ', sizeof(repl_null
));
817 memset(repl_repl
, ' ', sizeof(repl_repl
));
820 repl_val
[Anum_pg_ts_dict_dictinitoption
- 1] =
821 PointerGetDatum(serialize_deflist(dictoptions
));
823 repl_null
[Anum_pg_ts_dict_dictinitoption
- 1] = 'n';
824 repl_repl
[Anum_pg_ts_dict_dictinitoption
- 1] = 'r';
826 newtup
= heap_modifytuple(tup
, RelationGetDescr(rel
),
827 repl_val
, repl_null
, repl_repl
);
829 simple_heap_update(rel
, &newtup
->t_self
, newtup
);
831 CatalogUpdateIndexes(rel
, newtup
);
834 * NOTE: because we only support altering the options, not the template,
835 * there is no need to update dependencies. This might have to change if
836 * the options ever reference inside-the-database objects.
839 heap_freetuple(newtup
);
840 ReleaseSysCache(tup
);
842 heap_close(rel
, RowExclusiveLock
);
846 * ALTER TEXT SEARCH DICTIONARY OWNER
849 AlterTSDictionaryOwner(List
*name
, Oid newOwnerId
)
856 Form_pg_ts_dict form
;
858 rel
= heap_open(TSDictionaryRelationId
, RowExclusiveLock
);
860 dictId
= TSDictionaryGetDictid(name
, false);
862 tup
= SearchSysCacheCopy(TSDICTOID
,
863 ObjectIdGetDatum(dictId
),
866 if (!HeapTupleIsValid(tup
)) /* should not happen */
867 elog(ERROR
, "cache lookup failed for text search dictionary %u",
870 form
= (Form_pg_ts_dict
) GETSTRUCT(tup
);
871 namespaceOid
= form
->dictnamespace
;
873 if (form
->dictowner
!= newOwnerId
)
875 /* Superusers can always do it */
879 if (!pg_ts_dict_ownercheck(dictId
, GetUserId()))
880 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_TSDICTIONARY
,
881 NameListToString(name
));
883 /* Must be able to become new owner */
884 check_is_member_of_role(GetUserId(), newOwnerId
);
886 /* New owner must have CREATE privilege on namespace */
887 aclresult
= pg_namespace_aclcheck(namespaceOid
, newOwnerId
, ACL_CREATE
);
888 if (aclresult
!= ACLCHECK_OK
)
889 aclcheck_error(aclresult
, ACL_KIND_NAMESPACE
,
890 get_namespace_name(namespaceOid
));
893 form
->dictowner
= newOwnerId
;
895 simple_heap_update(rel
, &tup
->t_self
, tup
);
896 CatalogUpdateIndexes(rel
, tup
);
898 /* Update owner dependency reference */
899 changeDependencyOnOwner(TSDictionaryRelationId
, HeapTupleGetOid(tup
),
903 heap_close(rel
, NoLock
);
907 /* ---------------------- TS Template commands -----------------------*/
910 * lookup a template support function and return its OID (as a Datum)
912 * attnum is the pg_ts_template column the function will go into
915 get_ts_template_func(DefElem
*defel
, int attnum
)
917 List
*funcName
= defGetQualifiedName(defel
);
923 retTypeId
= INTERNALOID
;
924 typeId
[0] = INTERNALOID
;
925 typeId
[1] = INTERNALOID
;
926 typeId
[2] = INTERNALOID
;
927 typeId
[3] = INTERNALOID
;
930 case Anum_pg_ts_template_tmplinit
:
933 case Anum_pg_ts_template_tmpllexize
:
937 /* should not be here */
938 elog(ERROR
, "unrecognized attribute for text search template: %d",
940 nargs
= 0; /* keep compiler quiet */
943 procOid
= LookupFuncName(funcName
, nargs
, typeId
, false);
944 if (get_func_rettype(procOid
) != retTypeId
)
946 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
947 errmsg("function %s should return type %s",
948 func_signature_string(funcName
, nargs
, typeId
),
949 format_type_be(retTypeId
))));
951 return ObjectIdGetDatum(procOid
);
955 * make pg_depend entries for a new pg_ts_template entry
958 makeTSTemplateDependencies(HeapTuple tuple
)
960 Form_pg_ts_template tmpl
= (Form_pg_ts_template
) GETSTRUCT(tuple
);
961 ObjectAddress myself
,
964 myself
.classId
= TSTemplateRelationId
;
965 myself
.objectId
= HeapTupleGetOid(tuple
);
966 myself
.objectSubId
= 0;
968 /* dependency on namespace */
969 referenced
.classId
= NamespaceRelationId
;
970 referenced
.objectId
= tmpl
->tmplnamespace
;
971 referenced
.objectSubId
= 0;
972 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
974 /* dependencies on functions */
975 referenced
.classId
= ProcedureRelationId
;
976 referenced
.objectSubId
= 0;
978 referenced
.objectId
= tmpl
->tmpllexize
;
979 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
981 if (OidIsValid(tmpl
->tmplinit
))
983 referenced
.objectId
= tmpl
->tmplinit
;
984 recordDependencyOn(&myself
, &referenced
, DEPENDENCY_NORMAL
);
989 * CREATE TEXT SEARCH TEMPLATE
992 DefineTSTemplate(List
*names
, List
*parameters
)
997 Datum values
[Natts_pg_ts_template
];
998 char nulls
[Natts_pg_ts_template
];
1007 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
1008 errmsg("must be superuser to create text search templates")));
1010 /* Convert list of names to a name and namespace */
1011 namespaceoid
= QualifiedNameGetCreationNamespace(names
, &tmplname
);
1013 for (i
= 0; i
< Natts_pg_ts_template
; i
++)
1016 values
[i
] = ObjectIdGetDatum(InvalidOid
);
1019 namestrcpy(&dname
, tmplname
);
1020 values
[Anum_pg_ts_template_tmplname
- 1] = NameGetDatum(&dname
);
1021 values
[Anum_pg_ts_template_tmplnamespace
- 1] = ObjectIdGetDatum(namespaceoid
);
1024 * loop over the definition list and extract the information we need.
1026 foreach(pl
, parameters
)
1028 DefElem
*defel
= (DefElem
*) lfirst(pl
);
1030 if (pg_strcasecmp(defel
->defname
, "init") == 0)
1032 values
[Anum_pg_ts_template_tmplinit
- 1] =
1033 get_ts_template_func(defel
, Anum_pg_ts_template_tmplinit
);
1034 nulls
[Anum_pg_ts_template_tmplinit
- 1] = ' ';
1036 else if (pg_strcasecmp(defel
->defname
, "lexize") == 0)
1038 values
[Anum_pg_ts_template_tmpllexize
- 1] =
1039 get_ts_template_func(defel
, Anum_pg_ts_template_tmpllexize
);
1040 nulls
[Anum_pg_ts_template_tmpllexize
- 1] = ' ';
1044 (errcode(ERRCODE_SYNTAX_ERROR
),
1045 errmsg("text search template parameter \"%s\" not recognized",
1052 if (!OidIsValid(DatumGetObjectId(values
[Anum_pg_ts_template_tmpllexize
- 1])))
1054 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
1055 errmsg("text search template lexize method is required")));
1058 * Looks good, insert
1061 tmplRel
= heap_open(TSTemplateRelationId
, RowExclusiveLock
);
1063 tup
= heap_formtuple(tmplRel
->rd_att
, values
, nulls
);
1065 dictOid
= simple_heap_insert(tmplRel
, tup
);
1067 CatalogUpdateIndexes(tmplRel
, tup
);
1069 makeTSTemplateDependencies(tup
);
1071 heap_freetuple(tup
);
1073 heap_close(tmplRel
, RowExclusiveLock
);
1077 * ALTER TEXT SEARCH TEMPLATE RENAME
1080 RenameTSTemplate(List
*oldname
, const char *newname
)
1089 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
1090 errmsg("must be superuser to rename text search templates")));
1092 rel
= heap_open(TSTemplateRelationId
, RowExclusiveLock
);
1094 tmplId
= TSTemplateGetTmplid(oldname
, false);
1096 tup
= SearchSysCacheCopy(TSTEMPLATEOID
,
1097 ObjectIdGetDatum(tmplId
),
1100 if (!HeapTupleIsValid(tup
)) /* should not happen */
1101 elog(ERROR
, "cache lookup failed for text search template %u",
1104 namespaceOid
= ((Form_pg_ts_template
) GETSTRUCT(tup
))->tmplnamespace
;
1106 if (SearchSysCacheExists(TSTEMPLATENAMENSP
,
1107 PointerGetDatum(newname
),
1108 ObjectIdGetDatum(namespaceOid
),
1111 (errcode(ERRCODE_DUPLICATE_OBJECT
),
1112 errmsg("text search template \"%s\" already exists",
1115 namestrcpy(&(((Form_pg_ts_template
) GETSTRUCT(tup
))->tmplname
), newname
);
1116 simple_heap_update(rel
, &tup
->t_self
, tup
);
1117 CatalogUpdateIndexes(rel
, tup
);
1119 heap_close(rel
, NoLock
);
1120 heap_freetuple(tup
);
1124 * DROP TEXT SEARCH TEMPLATE
1127 RemoveTSTemplates(DropStmt
*drop
)
1129 ObjectAddresses
*objects
;
1134 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE
),
1135 errmsg("must be superuser to drop text search templates")));
1138 * First we identify all the objects, then we delete them in a single
1139 * performMultipleDeletions() call. This is to avoid unwanted
1140 * DROP RESTRICT errors if one of the objects depends on another.
1142 objects
= new_object_addresses();
1144 foreach(cell
, drop
->objects
)
1146 List
*names
= (List
*) lfirst(cell
);
1148 ObjectAddress object
;
1150 tmplOid
= TSTemplateGetTmplid(names
, true);
1152 if (!OidIsValid(tmplOid
))
1154 if (!drop
->missing_ok
)
1157 (errcode(ERRCODE_UNDEFINED_OBJECT
),
1158 errmsg("text search template \"%s\" does not exist",
1159 NameListToString(names
))));
1164 (errmsg("text search template \"%s\" does not exist, skipping",
1165 NameListToString(names
))));
1170 object
.classId
= TSTemplateRelationId
;
1171 object
.objectId
= tmplOid
;
1172 object
.objectSubId
= 0;
1174 add_exact_object_address(&object
, objects
);
1177 performMultipleDeletions(objects
, drop
->behavior
);
1179 free_object_addresses(objects
);
1183 * Guts of TS template deletion.
1186 RemoveTSTemplateById(Oid tmplId
)
1191 relation
= heap_open(TSTemplateRelationId
, RowExclusiveLock
);
1193 tup
= SearchSysCache(TSTEMPLATEOID
,
1194 ObjectIdGetDatum(tmplId
),
1197 if (!HeapTupleIsValid(tup
))
1198 elog(ERROR
, "cache lookup failed for text search template %u",
1201 simple_heap_delete(relation
, &tup
->t_self
);
1203 ReleaseSysCache(tup
);
1205 heap_close(relation
, RowExclusiveLock
);
1208 /* ---------------------- TS Configuration commands -----------------------*/
1211 * Finds syscache tuple of configuration.
1212 * Returns NULL if no such cfg.
1215 GetTSConfigTuple(List
*names
)
1220 cfgId
= TSConfigGetCfgid(names
, true);
1221 if (!OidIsValid(cfgId
))
1224 tup
= SearchSysCache(TSCONFIGOID
,
1225 ObjectIdGetDatum(cfgId
),
1228 if (!HeapTupleIsValid(tup
)) /* should not happen */
1229 elog(ERROR
, "cache lookup failed for text search configuration %u",
1236 * make pg_depend entries for a new or updated pg_ts_config entry
1238 * Pass opened pg_ts_config_map relation if there might be any config map
1239 * entries for the config.
1242 makeConfigurationDependencies(HeapTuple tuple
, bool removeOld
,
1245 Form_pg_ts_config cfg
= (Form_pg_ts_config
) GETSTRUCT(tuple
);
1246 ObjectAddresses
*addrs
;
1247 ObjectAddress myself
,
1250 myself
.classId
= TSConfigRelationId
;
1251 myself
.objectId
= HeapTupleGetOid(tuple
);
1252 myself
.objectSubId
= 0;
1254 /* for ALTER case, first flush old dependencies */
1257 deleteDependencyRecordsFor(myself
.classId
, myself
.objectId
);
1258 deleteSharedDependencyRecordsFor(myself
.classId
, myself
.objectId
);
1262 * We use an ObjectAddresses list to remove possible duplicate
1263 * dependencies from the config map info. The pg_ts_config items
1264 * shouldn't be duplicates, but might as well fold them all into one call.
1266 addrs
= new_object_addresses();
1268 /* dependency on namespace */
1269 referenced
.classId
= NamespaceRelationId
;
1270 referenced
.objectId
= cfg
->cfgnamespace
;
1271 referenced
.objectSubId
= 0;
1272 add_exact_object_address(&referenced
, addrs
);
1274 /* dependency on owner */
1275 recordDependencyOnOwner(myself
.classId
, myself
.objectId
, cfg
->cfgowner
);
1277 /* dependency on parser */
1278 referenced
.classId
= TSParserRelationId
;
1279 referenced
.objectId
= cfg
->cfgparser
;
1280 referenced
.objectSubId
= 0;
1281 add_exact_object_address(&referenced
, addrs
);
1283 /* dependencies on dictionaries listed in config map */
1290 /* CCI to ensure we can see effects of caller's changes */
1291 CommandCounterIncrement();
1294 Anum_pg_ts_config_map_mapcfg
,
1295 BTEqualStrategyNumber
, F_OIDEQ
,
1296 ObjectIdGetDatum(myself
.objectId
));
1298 scan
= systable_beginscan(mapRel
, TSConfigMapIndexId
, true,
1299 SnapshotNow
, 1, &skey
);
1301 while (HeapTupleIsValid((maptup
= systable_getnext(scan
))))
1303 Form_pg_ts_config_map cfgmap
= (Form_pg_ts_config_map
) GETSTRUCT(maptup
);
1305 referenced
.classId
= TSDictionaryRelationId
;
1306 referenced
.objectId
= cfgmap
->mapdict
;
1307 referenced
.objectSubId
= 0;
1308 add_exact_object_address(&referenced
, addrs
);
1311 systable_endscan(scan
);
1314 /* Record 'em (this includes duplicate elimination) */
1315 record_object_address_dependencies(&myself
, addrs
, DEPENDENCY_NORMAL
);
1317 free_object_addresses(addrs
);
1321 * CREATE TEXT SEARCH CONFIGURATION
1324 DefineTSConfiguration(List
*names
, List
*parameters
)
1327 Relation mapRel
= NULL
;
1329 Datum values
[Natts_pg_ts_config
];
1330 char nulls
[Natts_pg_ts_config
];
1331 AclResult aclresult
;
1335 Oid sourceOid
= InvalidOid
;
1336 Oid prsOid
= InvalidOid
;
1340 /* Convert list of names to a name and namespace */
1341 namespaceoid
= QualifiedNameGetCreationNamespace(names
, &cfgname
);
1343 /* Check we have creation rights in target namespace */
1344 aclresult
= pg_namespace_aclcheck(namespaceoid
, GetUserId(), ACL_CREATE
);
1345 if (aclresult
!= ACLCHECK_OK
)
1346 aclcheck_error(aclresult
, ACL_KIND_NAMESPACE
,
1347 get_namespace_name(namespaceoid
));
1350 * loop over the definition list and extract the information we need.
1352 foreach(pl
, parameters
)
1354 DefElem
*defel
= (DefElem
*) lfirst(pl
);
1356 if (pg_strcasecmp(defel
->defname
, "parser") == 0)
1357 prsOid
= TSParserGetPrsid(defGetQualifiedName(defel
), false);
1358 else if (pg_strcasecmp(defel
->defname
, "copy") == 0)
1359 sourceOid
= TSConfigGetCfgid(defGetQualifiedName(defel
), false);
1362 (errcode(ERRCODE_SYNTAX_ERROR
),
1363 errmsg("text search configuration parameter \"%s\" not recognized",
1367 if (OidIsValid(sourceOid
) && OidIsValid(prsOid
))
1369 (errcode(ERRCODE_SYNTAX_ERROR
),
1370 errmsg("cannot specify both PARSER and COPY options")));
1373 * Look up source config if given.
1375 if (OidIsValid(sourceOid
))
1377 Form_pg_ts_config cfg
;
1379 tup
= SearchSysCache(TSCONFIGOID
,
1380 ObjectIdGetDatum(sourceOid
),
1382 if (!HeapTupleIsValid(tup
))
1383 elog(ERROR
, "cache lookup failed for text search configuration %u",
1386 cfg
= (Form_pg_ts_config
) GETSTRUCT(tup
);
1388 /* use source's parser */
1389 prsOid
= cfg
->cfgparser
;
1391 ReleaseSysCache(tup
);
1397 if (!OidIsValid(prsOid
))
1399 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION
),
1400 errmsg("text search parser is required")));
1403 * Looks good, build tuple and insert
1405 memset(values
, 0, sizeof(values
));
1406 memset(nulls
, ' ', sizeof(nulls
));
1408 namestrcpy(&cname
, cfgname
);
1409 values
[Anum_pg_ts_config_cfgname
- 1] = NameGetDatum(&cname
);
1410 values
[Anum_pg_ts_config_cfgnamespace
- 1] = ObjectIdGetDatum(namespaceoid
);
1411 values
[Anum_pg_ts_config_cfgowner
- 1] = ObjectIdGetDatum(GetUserId());
1412 values
[Anum_pg_ts_config_cfgparser
- 1] = ObjectIdGetDatum(prsOid
);
1414 cfgRel
= heap_open(TSConfigRelationId
, RowExclusiveLock
);
1416 tup
= heap_formtuple(cfgRel
->rd_att
, values
, nulls
);
1418 cfgOid
= simple_heap_insert(cfgRel
, tup
);
1420 CatalogUpdateIndexes(cfgRel
, tup
);
1422 if (OidIsValid(sourceOid
))
1425 * Copy token-dicts map from source config
1431 mapRel
= heap_open(TSConfigMapRelationId
, RowExclusiveLock
);
1434 Anum_pg_ts_config_map_mapcfg
,
1435 BTEqualStrategyNumber
, F_OIDEQ
,
1436 ObjectIdGetDatum(sourceOid
));
1438 scan
= systable_beginscan(mapRel
, TSConfigMapIndexId
, true,
1439 SnapshotNow
, 1, &skey
);
1441 while (HeapTupleIsValid((maptup
= systable_getnext(scan
))))
1443 Form_pg_ts_config_map cfgmap
= (Form_pg_ts_config_map
) GETSTRUCT(maptup
);
1444 HeapTuple newmaptup
;
1445 Datum mapvalues
[Natts_pg_ts_config_map
];
1446 char mapnulls
[Natts_pg_ts_config_map
];
1448 memset(mapvalues
, 0, sizeof(mapvalues
));
1449 memset(mapnulls
, ' ', sizeof(mapnulls
));
1451 mapvalues
[Anum_pg_ts_config_map_mapcfg
- 1] = cfgOid
;
1452 mapvalues
[Anum_pg_ts_config_map_maptokentype
- 1] = cfgmap
->maptokentype
;
1453 mapvalues
[Anum_pg_ts_config_map_mapseqno
- 1] = cfgmap
->mapseqno
;
1454 mapvalues
[Anum_pg_ts_config_map_mapdict
- 1] = cfgmap
->mapdict
;
1456 newmaptup
= heap_formtuple(mapRel
->rd_att
, mapvalues
, mapnulls
);
1458 simple_heap_insert(mapRel
, newmaptup
);
1460 CatalogUpdateIndexes(mapRel
, newmaptup
);
1462 heap_freetuple(newmaptup
);
1465 systable_endscan(scan
);
1468 makeConfigurationDependencies(tup
, false, mapRel
);
1470 heap_freetuple(tup
);
1473 heap_close(mapRel
, RowExclusiveLock
);
1474 heap_close(cfgRel
, RowExclusiveLock
);
1478 * ALTER TEXT SEARCH CONFIGURATION RENAME
1481 RenameTSConfiguration(List
*oldname
, const char *newname
)
1486 AclResult aclresult
;
1489 rel
= heap_open(TSConfigRelationId
, RowExclusiveLock
);
1491 cfgId
= TSConfigGetCfgid(oldname
, false);
1493 tup
= SearchSysCacheCopy(TSCONFIGOID
,
1494 ObjectIdGetDatum(cfgId
),
1497 if (!HeapTupleIsValid(tup
)) /* should not happen */
1498 elog(ERROR
, "cache lookup failed for text search configuration %u",
1501 namespaceOid
= ((Form_pg_ts_config
) GETSTRUCT(tup
))->cfgnamespace
;
1503 if (SearchSysCacheExists(TSCONFIGNAMENSP
,
1504 PointerGetDatum(newname
),
1505 ObjectIdGetDatum(namespaceOid
),
1508 (errcode(ERRCODE_DUPLICATE_OBJECT
),
1509 errmsg("text search configuration \"%s\" already exists",
1513 if (!pg_ts_config_ownercheck(cfgId
, GetUserId()))
1514 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_TSCONFIGURATION
,
1515 NameListToString(oldname
));
1517 /* must have CREATE privilege on namespace */
1518 aclresult
= pg_namespace_aclcheck(namespaceOid
, GetUserId(), ACL_CREATE
);
1519 aclcheck_error(aclresult
, ACL_KIND_NAMESPACE
,
1520 get_namespace_name(namespaceOid
));
1522 namestrcpy(&(((Form_pg_ts_config
) GETSTRUCT(tup
))->cfgname
), newname
);
1523 simple_heap_update(rel
, &tup
->t_self
, tup
);
1524 CatalogUpdateIndexes(rel
, tup
);
1526 heap_close(rel
, NoLock
);
1527 heap_freetuple(tup
);
1531 * DROP TEXT SEARCH CONFIGURATION
1534 RemoveTSConfigurations(DropStmt
*drop
)
1536 ObjectAddresses
*objects
;
1540 * First we identify all the objects, then we delete them in a single
1541 * performMultipleDeletions() call. This is to avoid unwanted
1542 * DROP RESTRICT errors if one of the objects depends on another.
1544 objects
= new_object_addresses();
1546 foreach(cell
, drop
->objects
)
1548 List
*names
= (List
*) lfirst(cell
);
1551 ObjectAddress object
;
1554 tup
= GetTSConfigTuple(names
);
1556 if (!HeapTupleIsValid(tup
))
1558 if (!drop
->missing_ok
)
1561 (errcode(ERRCODE_UNDEFINED_OBJECT
),
1562 errmsg("text search configuration \"%s\" does not exist",
1563 NameListToString(names
))));
1568 (errmsg("text search configuration \"%s\" does not exist, skipping",
1569 NameListToString(names
))));
1574 /* Permission check: must own configuration or its namespace */
1575 cfgOid
= HeapTupleGetOid(tup
);
1576 namespaceId
= ((Form_pg_ts_config
) GETSTRUCT(tup
))->cfgnamespace
;
1577 if (!pg_ts_config_ownercheck(cfgOid
, GetUserId()) &&
1578 !pg_namespace_ownercheck(namespaceId
, GetUserId()))
1579 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_TSCONFIGURATION
,
1580 NameListToString(names
));
1582 object
.classId
= TSConfigRelationId
;
1583 object
.objectId
= cfgOid
;
1584 object
.objectSubId
= 0;
1586 add_exact_object_address(&object
, objects
);
1588 ReleaseSysCache(tup
);
1591 performMultipleDeletions(objects
, drop
->behavior
);
1593 free_object_addresses(objects
);
1597 * Guts of TS configuration deletion.
1600 RemoveTSConfigurationById(Oid cfgId
)
1608 /* Remove the pg_ts_config entry */
1609 relCfg
= heap_open(TSConfigRelationId
, RowExclusiveLock
);
1611 tup
= SearchSysCache(TSCONFIGOID
,
1612 ObjectIdGetDatum(cfgId
),
1615 if (!HeapTupleIsValid(tup
))
1616 elog(ERROR
, "cache lookup failed for text search dictionary %u",
1619 simple_heap_delete(relCfg
, &tup
->t_self
);
1621 ReleaseSysCache(tup
);
1623 heap_close(relCfg
, RowExclusiveLock
);
1625 /* Remove any pg_ts_config_map entries */
1626 relMap
= heap_open(TSConfigMapRelationId
, RowExclusiveLock
);
1629 Anum_pg_ts_config_map_mapcfg
,
1630 BTEqualStrategyNumber
, F_OIDEQ
,
1631 ObjectIdGetDatum(cfgId
));
1633 scan
= systable_beginscan(relMap
, TSConfigMapIndexId
, true,
1634 SnapshotNow
, 1, &skey
);
1636 while (HeapTupleIsValid((tup
= systable_getnext(scan
))))
1638 simple_heap_delete(relMap
, &tup
->t_self
);
1641 systable_endscan(scan
);
1643 heap_close(relMap
, RowExclusiveLock
);
1647 * ALTER TEXT SEARCH CONFIGURATION OWNER
1650 AlterTSConfigurationOwner(List
*name
, Oid newOwnerId
)
1655 AclResult aclresult
;
1657 Form_pg_ts_config form
;
1659 rel
= heap_open(TSConfigRelationId
, RowExclusiveLock
);
1661 cfgId
= TSConfigGetCfgid(name
, false);
1663 tup
= SearchSysCacheCopy(TSCONFIGOID
,
1664 ObjectIdGetDatum(cfgId
),
1667 if (!HeapTupleIsValid(tup
)) /* should not happen */
1668 elog(ERROR
, "cache lookup failed for text search configuration %u",
1671 form
= (Form_pg_ts_config
) GETSTRUCT(tup
);
1672 namespaceOid
= form
->cfgnamespace
;
1674 if (form
->cfgowner
!= newOwnerId
)
1676 /* Superusers can always do it */
1680 if (!pg_ts_config_ownercheck(cfgId
, GetUserId()))
1681 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_TSCONFIGURATION
,
1682 NameListToString(name
));
1684 /* Must be able to become new owner */
1685 check_is_member_of_role(GetUserId(), newOwnerId
);
1687 /* New owner must have CREATE privilege on namespace */
1688 aclresult
= pg_namespace_aclcheck(namespaceOid
, newOwnerId
, ACL_CREATE
);
1689 if (aclresult
!= ACLCHECK_OK
)
1690 aclcheck_error(aclresult
, ACL_KIND_NAMESPACE
,
1691 get_namespace_name(namespaceOid
));
1694 form
->cfgowner
= newOwnerId
;
1696 simple_heap_update(rel
, &tup
->t_self
, tup
);
1697 CatalogUpdateIndexes(rel
, tup
);
1699 /* Update owner dependency reference */
1700 changeDependencyOnOwner(TSConfigRelationId
, HeapTupleGetOid(tup
),
1704 heap_close(rel
, NoLock
);
1705 heap_freetuple(tup
);
1709 * ALTER TEXT SEARCH CONFIGURATION - main entry point
1712 AlterTSConfiguration(AlterTSConfigurationStmt
*stmt
)
1717 /* Find the configuration */
1718 tup
= GetTSConfigTuple(stmt
->cfgname
);
1719 if (!HeapTupleIsValid(tup
))
1721 (errcode(ERRCODE_UNDEFINED_OBJECT
),
1722 errmsg("text search configuration \"%s\" does not exist",
1723 NameListToString(stmt
->cfgname
))));
1726 if (!pg_ts_config_ownercheck(HeapTupleGetOid(tup
), GetUserId()))
1727 aclcheck_error(ACLCHECK_NOT_OWNER
, ACL_KIND_TSCONFIGURATION
,
1728 NameListToString(stmt
->cfgname
));
1730 relMap
= heap_open(TSConfigMapRelationId
, RowExclusiveLock
);
1732 /* Add or drop mappings */
1734 MakeConfigurationMapping(stmt
, tup
, relMap
);
1735 else if (stmt
->tokentype
)
1736 DropConfigurationMapping(stmt
, tup
, relMap
);
1738 /* Update dependencies */
1739 makeConfigurationDependencies(tup
, true, relMap
);
1741 heap_close(relMap
, RowExclusiveLock
);
1743 ReleaseSysCache(tup
);
1747 * Translate a list of token type names to an array of token type numbers
1750 getTokenTypes(Oid prsId
, List
*tokennames
)
1752 TSParserCacheEntry
*prs
= lookup_ts_parser_cache(prsId
);
1759 ntoken
= list_length(tokennames
);
1762 res
= (int *) palloc(sizeof(int) * ntoken
);
1764 if (!OidIsValid(prs
->lextypeOid
))
1765 elog(ERROR
, "method lextype isn't defined for text search parser %u",
1768 /* OidFunctionCall0 is absent */
1769 list
= (LexDescr
*) DatumGetPointer(OidFunctionCall1(prs
->lextypeOid
,
1773 foreach(tn
, tokennames
)
1775 Value
*val
= (Value
*) lfirst(tn
);
1780 while (list
&& list
[j
].lexid
)
1782 /* XXX should we use pg_strcasecmp here? */
1783 if (strcmp(strVal(val
), list
[j
].alias
) == 0)
1785 res
[i
] = list
[j
].lexid
;
1793 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
1794 errmsg("token type \"%s\" does not exist",
1803 * ALTER TEXT SEARCH CONFIGURATION ADD/ALTER MAPPING
1806 MakeConfigurationMapping(AlterTSConfigurationStmt
*stmt
,
1807 HeapTuple tup
, Relation relMap
)
1809 Oid cfgId
= HeapTupleGetOid(tup
);
1810 ScanKeyData skey
[2];
1822 prsId
= ((Form_pg_ts_config
) GETSTRUCT(tup
))->cfgparser
;
1824 tokens
= getTokenTypes(prsId
, stmt
->tokentype
);
1825 ntoken
= list_length(stmt
->tokentype
);
1830 * delete maps for tokens if they exist and command was ALTER
1832 for (i
= 0; i
< ntoken
; i
++)
1834 ScanKeyInit(&skey
[0],
1835 Anum_pg_ts_config_map_mapcfg
,
1836 BTEqualStrategyNumber
, F_OIDEQ
,
1837 ObjectIdGetDatum(cfgId
));
1838 ScanKeyInit(&skey
[1],
1839 Anum_pg_ts_config_map_maptokentype
,
1840 BTEqualStrategyNumber
, F_INT4EQ
,
1841 Int32GetDatum(tokens
[i
]));
1843 scan
= systable_beginscan(relMap
, TSConfigMapIndexId
, true,
1844 SnapshotNow
, 2, skey
);
1846 while (HeapTupleIsValid((maptup
= systable_getnext(scan
))))
1848 simple_heap_delete(relMap
, &maptup
->t_self
);
1851 systable_endscan(scan
);
1856 * Convert list of dictionary names to array of dict OIDs
1858 ndict
= list_length(stmt
->dicts
);
1859 dictIds
= (Oid
*) palloc(sizeof(Oid
) * ndict
);
1861 foreach(c
, stmt
->dicts
)
1863 List
*names
= (List
*) lfirst(c
);
1865 dictIds
[i
] = TSDictionaryGetDictid(names
, false);
1872 * Replace a specific dictionary in existing entries
1874 Oid dictOld
= dictIds
[0],
1875 dictNew
= dictIds
[1];
1877 ScanKeyInit(&skey
[0],
1878 Anum_pg_ts_config_map_mapcfg
,
1879 BTEqualStrategyNumber
, F_OIDEQ
,
1880 ObjectIdGetDatum(cfgId
));
1882 scan
= systable_beginscan(relMap
, TSConfigMapIndexId
, true,
1883 SnapshotNow
, 1, skey
);
1885 while (HeapTupleIsValid((maptup
= systable_getnext(scan
))))
1887 Form_pg_ts_config_map cfgmap
= (Form_pg_ts_config_map
) GETSTRUCT(maptup
);
1890 * check if it's one of target token types
1894 bool tokmatch
= false;
1896 for (j
= 0; j
< ntoken
; j
++)
1898 if (cfgmap
->maptokentype
== tokens
[j
])
1909 * replace dictionary if match
1911 if (cfgmap
->mapdict
== dictOld
)
1913 Datum repl_val
[Natts_pg_ts_config_map
];
1914 char repl_null
[Natts_pg_ts_config_map
];
1915 char repl_repl
[Natts_pg_ts_config_map
];
1918 memset(repl_val
, 0, sizeof(repl_val
));
1919 memset(repl_null
, ' ', sizeof(repl_null
));
1920 memset(repl_repl
, ' ', sizeof(repl_repl
));
1922 repl_val
[Anum_pg_ts_config_map_mapdict
- 1] = ObjectIdGetDatum(dictNew
);
1923 repl_repl
[Anum_pg_ts_config_map_mapdict
- 1] = 'r';
1925 newtup
= heap_modifytuple(maptup
,
1926 RelationGetDescr(relMap
),
1927 repl_val
, repl_null
, repl_repl
);
1928 simple_heap_update(relMap
, &newtup
->t_self
, newtup
);
1930 CatalogUpdateIndexes(relMap
, newtup
);
1934 systable_endscan(scan
);
1939 * Insertion of new entries
1941 for (i
= 0; i
< ntoken
; i
++)
1943 for (j
= 0; j
< ndict
; j
++)
1945 Datum values
[Natts_pg_ts_config_map
];
1946 char nulls
[Natts_pg_ts_config_map
];
1948 memset(nulls
, ' ', sizeof(nulls
));
1949 values
[Anum_pg_ts_config_map_mapcfg
- 1] = ObjectIdGetDatum(cfgId
);
1950 values
[Anum_pg_ts_config_map_maptokentype
- 1] = Int32GetDatum(tokens
[i
]);
1951 values
[Anum_pg_ts_config_map_mapseqno
- 1] = Int32GetDatum(j
+ 1);
1952 values
[Anum_pg_ts_config_map_mapdict
- 1] = ObjectIdGetDatum(dictIds
[j
]);
1954 tup
= heap_formtuple(relMap
->rd_att
, values
, nulls
);
1955 simple_heap_insert(relMap
, tup
);
1956 CatalogUpdateIndexes(relMap
, tup
);
1958 heap_freetuple(tup
);
1965 * ALTER TEXT SEARCH CONFIGURATION DROP MAPPING
1968 DropConfigurationMapping(AlterTSConfigurationStmt
*stmt
,
1969 HeapTuple tup
, Relation relMap
)
1971 Oid cfgId
= HeapTupleGetOid(tup
);
1972 ScanKeyData skey
[2];
1981 prsId
= ((Form_pg_ts_config
) GETSTRUCT(tup
))->cfgparser
;
1983 tokens
= getTokenTypes(prsId
, stmt
->tokentype
);
1984 ntoken
= list_length(stmt
->tokentype
);
1987 foreach(c
, stmt
->tokentype
)
1989 Value
*val
= (Value
*) lfirst(c
);
1992 ScanKeyInit(&skey
[0],
1993 Anum_pg_ts_config_map_mapcfg
,
1994 BTEqualStrategyNumber
, F_OIDEQ
,
1995 ObjectIdGetDatum(cfgId
));
1996 ScanKeyInit(&skey
[1],
1997 Anum_pg_ts_config_map_maptokentype
,
1998 BTEqualStrategyNumber
, F_INT4EQ
,
1999 Int32GetDatum(tokens
[i
]));
2001 scan
= systable_beginscan(relMap
, TSConfigMapIndexId
, true,
2002 SnapshotNow
, 2, skey
);
2004 while (HeapTupleIsValid((maptup
= systable_getnext(scan
))))
2006 simple_heap_delete(relMap
, &maptup
->t_self
);
2010 systable_endscan(scan
);
2014 if (!stmt
->missing_ok
)
2017 (errcode(ERRCODE_UNDEFINED_OBJECT
),
2018 errmsg("mapping for token type \"%s\" does not exist",
2024 (errmsg("mapping for token type \"%s\" does not exist, skipping",
2035 * Serialize dictionary options, producing a TEXT datum from a List of DefElem
2037 * This is used to form the value stored in pg_ts_dict.dictinitoption.
2038 * For the convenience of pg_dump, the output is formatted exactly as it
2039 * would need to appear in CREATE TEXT SEARCH DICTIONARY to reproduce the
2042 * Note that we assume that only the textual representation of an option's
2043 * value is interesting --- hence, non-string DefElems get forced to strings.
2046 serialize_deflist(List
*deflist
)
2052 initStringInfo(&buf
);
2056 DefElem
*defel
= (DefElem
*) lfirst(l
);
2057 char *val
= defGetString(defel
);
2059 appendStringInfo(&buf
, "%s = ",
2060 quote_identifier(defel
->defname
));
2061 /* If backslashes appear, force E syntax to determine their handling */
2062 if (strchr(val
, '\\'))
2063 appendStringInfoChar(&buf
, ESCAPE_STRING_SYNTAX
);
2064 appendStringInfoChar(&buf
, '\'');
2069 if (SQL_STR_DOUBLE(ch
, true))
2070 appendStringInfoChar(&buf
, ch
);
2071 appendStringInfoChar(&buf
, ch
);
2073 appendStringInfoChar(&buf
, '\'');
2074 if (lnext(l
) != NULL
)
2075 appendStringInfo(&buf
, ", ");
2078 result
= cstring_to_text_with_len(buf
.data
, buf
.len
);
2084 * Deserialize dictionary options, reconstructing a List of DefElem from TEXT
2086 * This is also used for prsheadline options, so for backward compatibility
2087 * we need to accept a few things serialize_deflist() will never emit:
2088 * in particular, unquoted and double-quoted values.
2091 deserialize_deflist(Datum txt
)
2093 text
*in
= DatumGetTextP(txt
); /* in case it's toasted */
2095 int len
= VARSIZE(in
) - VARHDRSZ
;
2112 ds_state state
= CS_WAITKEY
;
2114 workspace
= (char *) palloc(len
+ 1); /* certainly enough room */
2117 for (; ptr
< endptr
; ptr
++)
2122 if (isspace((unsigned char) *ptr
) || *ptr
== ',')
2137 if (isspace((unsigned char) *ptr
))
2142 else if (*ptr
== '=')
2145 state
= CS_WAITVALUE
;
2155 if (ptr
+ 1 < endptr
&& ptr
[1] == '"')
2157 /* copy only one of the two quotes */
2173 state
= CS_WAITVALUE
;
2174 else if (!isspace((unsigned char) *ptr
))
2176 (errcode(ERRCODE_SYNTAX_ERROR
),
2177 errmsg("invalid parameter list format: \"%s\"",
2178 text_to_cstring(in
))));
2184 state
= CS_INSQVALUE
;
2186 else if (*ptr
== 'E' && ptr
+ 1 < endptr
&& ptr
[1] == '\'')
2190 state
= CS_INSQVALUE
;
2192 else if (*ptr
== '"')
2195 state
= CS_INDQVALUE
;
2197 else if (!isspace((unsigned char) *ptr
))
2201 state
= CS_INWVALUE
;
2207 if (ptr
+ 1 < endptr
&& ptr
[1] == '\'')
2209 /* copy only one of the two quotes */
2215 result
= lappend(result
,
2216 makeDefElem(pstrdup(workspace
),
2217 (Node
*) makeString(pstrdup(startvalue
))));
2221 else if (*ptr
== '\\')
2223 if (ptr
+ 1 < endptr
&& ptr
[1] == '\\')
2225 /* copy only one of the two backslashes */
2239 if (ptr
+ 1 < endptr
&& ptr
[1] == '"')
2241 /* copy only one of the two quotes */
2247 result
= lappend(result
,
2248 makeDefElem(pstrdup(workspace
),
2249 (Node
*) makeString(pstrdup(startvalue
))));
2259 if (*ptr
== ',' || isspace((unsigned char) *ptr
))
2262 result
= lappend(result
,
2263 makeDefElem(pstrdup(workspace
),
2264 (Node
*) makeString(pstrdup(startvalue
))));
2273 elog(ERROR
, "unrecognized deserialize_deflist state: %d",
2278 if (state
== CS_INWVALUE
)
2281 result
= lappend(result
,
2282 makeDefElem(pstrdup(workspace
),
2283 (Node
*) makeString(pstrdup(startvalue
))));
2285 else if (state
!= CS_WAITKEY
)
2287 (errcode(ERRCODE_SYNTAX_ERROR
),
2288 errmsg("invalid parameter list format: \"%s\"",
2289 text_to_cstring(in
))));