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