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