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