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