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