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-2023, 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 = object_aclcheck(NamespaceRelationId, 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 (!object_ownercheck(TSDictionaryRelationId, 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;
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 = object_aclcheck(NamespaceRelationId, 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  TupleDesc mapDesc;
1008  TupleTableSlot **slot;
1009  CatalogIndexState indstate;
1010  int max_slots,
1011  slot_init_count,
1012  slot_stored_count;
1013 
1014  mapRel = table_open(TSConfigMapRelationId, RowExclusiveLock);
1015  mapDesc = RelationGetDescr(mapRel);
1016 
1017  indstate = CatalogOpenIndexes(mapRel);
1018 
1019  /*
1020  * Allocate the slots to use, but delay costly initialization until we
1021  * know that they will be used.
1022  */
1024  slot = palloc(sizeof(TupleTableSlot *) * max_slots);
1025 
1026  ScanKeyInit(&skey,
1027  Anum_pg_ts_config_map_mapcfg,
1028  BTEqualStrategyNumber, F_OIDEQ,
1029  ObjectIdGetDatum(sourceOid));
1030 
1031  scan = systable_beginscan(mapRel, TSConfigMapIndexId, true,
1032  NULL, 1, &skey);
1033 
1034  /* number of slots currently storing tuples */
1035  slot_stored_count = 0;
1036  /* number of slots currently initialized */
1037  slot_init_count = 0;
1038 
1039  while (HeapTupleIsValid((maptup = systable_getnext(scan))))
1040  {
1042 
1043  if (slot_init_count < max_slots)
1044  {
1045  slot[slot_stored_count] = MakeSingleTupleTableSlot(mapDesc,
1046  &TTSOpsHeapTuple);
1047  slot_init_count++;
1048  }
1049 
1050  ExecClearTuple(slot[slot_stored_count]);
1051 
1052  memset(slot[slot_stored_count]->tts_isnull, false,
1053  slot[slot_stored_count]->tts_tupleDescriptor->natts * sizeof(bool));
1054 
1055  slot[slot_stored_count]->tts_values[Anum_pg_ts_config_map_mapcfg - 1] = cfgOid;
1056  slot[slot_stored_count]->tts_values[Anum_pg_ts_config_map_maptokentype - 1] = cfgmap->maptokentype;
1057  slot[slot_stored_count]->tts_values[Anum_pg_ts_config_map_mapseqno - 1] = cfgmap->mapseqno;
1058  slot[slot_stored_count]->tts_values[Anum_pg_ts_config_map_mapdict - 1] = cfgmap->mapdict;
1059 
1060  ExecStoreVirtualTuple(slot[slot_stored_count]);
1061  slot_stored_count++;
1062 
1063  /* If slots are full, insert a batch of tuples */
1064  if (slot_stored_count == max_slots)
1065  {
1066  CatalogTuplesMultiInsertWithInfo(mapRel, slot, slot_stored_count,
1067  indstate);
1068  slot_stored_count = 0;
1069  }
1070  }
1071 
1072  /* Insert any tuples left in the buffer */
1073  if (slot_stored_count > 0)
1074  CatalogTuplesMultiInsertWithInfo(mapRel, slot, slot_stored_count,
1075  indstate);
1076 
1077  for (int i = 0; i < slot_init_count; i++)
1079 
1080  systable_endscan(scan);
1081  CatalogCloseIndexes(indstate);
1082  }
1083 
1084  address = makeConfigurationDependencies(tup, false, mapRel);
1085 
1086  /* Post creation hook for new text search configuration */
1087  InvokeObjectPostCreateHook(TSConfigRelationId, cfgOid, 0);
1088 
1089  heap_freetuple(tup);
1090 
1091  if (mapRel)
1092  table_close(mapRel, RowExclusiveLock);
1093  table_close(cfgRel, RowExclusiveLock);
1094 
1095  return address;
1096 }
1097 
1098 /*
1099  * Guts of TS configuration deletion.
1100  */
1101 void
1103 {
1104  Relation relCfg,
1105  relMap;
1106  HeapTuple tup;
1107  ScanKeyData skey;
1108  SysScanDesc scan;
1109 
1110  /* Remove the pg_ts_config entry */
1111  relCfg = table_open(TSConfigRelationId, RowExclusiveLock);
1112 
1114 
1115  if (!HeapTupleIsValid(tup))
1116  elog(ERROR, "cache lookup failed for text search dictionary %u",
1117  cfgId);
1118 
1119  CatalogTupleDelete(relCfg, &tup->t_self);
1120 
1121  ReleaseSysCache(tup);
1122 
1123  table_close(relCfg, RowExclusiveLock);
1124 
1125  /* Remove any pg_ts_config_map entries */
1126  relMap = table_open(TSConfigMapRelationId, RowExclusiveLock);
1127 
1128  ScanKeyInit(&skey,
1129  Anum_pg_ts_config_map_mapcfg,
1130  BTEqualStrategyNumber, F_OIDEQ,
1131  ObjectIdGetDatum(cfgId));
1132 
1133  scan = systable_beginscan(relMap, TSConfigMapIndexId, true,
1134  NULL, 1, &skey);
1135 
1136  while (HeapTupleIsValid((tup = systable_getnext(scan))))
1137  {
1138  CatalogTupleDelete(relMap, &tup->t_self);
1139  }
1140 
1141  systable_endscan(scan);
1142 
1143  table_close(relMap, RowExclusiveLock);
1144 }
1145 
1146 /*
1147  * ALTER TEXT SEARCH CONFIGURATION - main entry point
1148  */
1151 {
1152  HeapTuple tup;
1153  Oid cfgId;
1154  Relation relMap;
1155  ObjectAddress address;
1156 
1157  /* Find the configuration */
1158  tup = GetTSConfigTuple(stmt->cfgname);
1159  if (!HeapTupleIsValid(tup))
1160  ereport(ERROR,
1161  (errcode(ERRCODE_UNDEFINED_OBJECT),
1162  errmsg("text search configuration \"%s\" does not exist",
1163  NameListToString(stmt->cfgname))));
1164 
1165  cfgId = ((Form_pg_ts_config) GETSTRUCT(tup))->oid;
1166 
1167  /* must be owner */
1168  if (!object_ownercheck(TSConfigRelationId, cfgId, GetUserId()))
1170  NameListToString(stmt->cfgname));
1171 
1172  relMap = table_open(TSConfigMapRelationId, RowExclusiveLock);
1173 
1174  /* Add or drop mappings */
1175  if (stmt->dicts)
1176  MakeConfigurationMapping(stmt, tup, relMap);
1177  else if (stmt->tokentype)
1178  DropConfigurationMapping(stmt, tup, relMap);
1179 
1180  /* Update dependencies */
1181  makeConfigurationDependencies(tup, true, relMap);
1182 
1183  InvokeObjectPostAlterHook(TSConfigRelationId, cfgId, 0);
1184 
1185  ObjectAddressSet(address, TSConfigRelationId, cfgId);
1186 
1187  table_close(relMap, RowExclusiveLock);
1188 
1189  ReleaseSysCache(tup);
1190 
1191  return address;
1192 }
1193 
1194 /*
1195  * Translate a list of token type names to an array of token type numbers
1196  */
1197 static int *
1198 getTokenTypes(Oid prsId, List *tokennames)
1199 {
1201  LexDescr *list;
1202  int *res,
1203  i,
1204  ntoken;
1205  ListCell *tn;
1206 
1207  ntoken = list_length(tokennames);
1208  if (ntoken == 0)
1209  return NULL;
1210  res = (int *) palloc(sizeof(int) * ntoken);
1211 
1212  if (!OidIsValid(prs->lextypeOid))
1213  elog(ERROR, "method lextype isn't defined for text search parser %u",
1214  prsId);
1215 
1216  /* lextype takes one dummy argument */
1218  (Datum) 0));
1219 
1220  i = 0;
1221  foreach(tn, tokennames)
1222  {
1223  String *val = lfirst_node(String, tn);
1224  bool found = false;
1225  int j;
1226 
1227  j = 0;
1228  while (list && list[j].lexid)
1229  {
1230  if (strcmp(strVal(val), list[j].alias) == 0)
1231  {
1232  res[i] = list[j].lexid;
1233  found = true;
1234  break;
1235  }
1236  j++;
1237  }
1238  if (!found)
1239  ereport(ERROR,
1240  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1241  errmsg("token type \"%s\" does not exist",
1242  strVal(val))));
1243  i++;
1244  }
1245 
1246  return res;
1247 }
1248 
1249 /*
1250  * ALTER TEXT SEARCH CONFIGURATION ADD/ALTER MAPPING
1251  */
1252 static void
1254  HeapTuple tup, Relation relMap)
1255 {
1256  Form_pg_ts_config tsform;
1257  Oid cfgId;
1258  ScanKeyData skey[2];
1259  SysScanDesc scan;
1260  HeapTuple maptup;
1261  int i;
1262  int j;
1263  Oid prsId;
1264  int *tokens,
1265  ntoken;
1266  Oid *dictIds;
1267  int ndict;
1268  ListCell *c;
1269  CatalogIndexState indstate;
1270 
1271  tsform = (Form_pg_ts_config) GETSTRUCT(tup);
1272  cfgId = tsform->oid;
1273  prsId = tsform->cfgparser;
1274 
1275  tokens = getTokenTypes(prsId, stmt->tokentype);
1276  ntoken = list_length(stmt->tokentype);
1277 
1278  if (stmt->override)
1279  {
1280  /*
1281  * delete maps for tokens if they exist and command was ALTER
1282  */
1283  for (i = 0; i < ntoken; i++)
1284  {
1285  ScanKeyInit(&skey[0],
1286  Anum_pg_ts_config_map_mapcfg,
1287  BTEqualStrategyNumber, F_OIDEQ,
1288  ObjectIdGetDatum(cfgId));
1289  ScanKeyInit(&skey[1],
1290  Anum_pg_ts_config_map_maptokentype,
1291  BTEqualStrategyNumber, F_INT4EQ,
1292  Int32GetDatum(tokens[i]));
1293 
1294  scan = systable_beginscan(relMap, TSConfigMapIndexId, true,
1295  NULL, 2, skey);
1296 
1297  while (HeapTupleIsValid((maptup = systable_getnext(scan))))
1298  {
1299  CatalogTupleDelete(relMap, &maptup->t_self);
1300  }
1301 
1302  systable_endscan(scan);
1303  }
1304  }
1305 
1306  /*
1307  * Convert list of dictionary names to array of dict OIDs
1308  */
1309  ndict = list_length(stmt->dicts);
1310  dictIds = (Oid *) palloc(sizeof(Oid) * ndict);
1311  i = 0;
1312  foreach(c, stmt->dicts)
1313  {
1314  List *names = (List *) lfirst(c);
1315 
1316  dictIds[i] = get_ts_dict_oid(names, false);
1317  i++;
1318  }
1319 
1320  indstate = CatalogOpenIndexes(relMap);
1321 
1322  if (stmt->replace)
1323  {
1324  /*
1325  * Replace a specific dictionary in existing entries
1326  */
1327  Oid dictOld = dictIds[0],
1328  dictNew = dictIds[1];
1329 
1330  ScanKeyInit(&skey[0],
1331  Anum_pg_ts_config_map_mapcfg,
1332  BTEqualStrategyNumber, F_OIDEQ,
1333  ObjectIdGetDatum(cfgId));
1334 
1335  scan = systable_beginscan(relMap, TSConfigMapIndexId, true,
1336  NULL, 1, skey);
1337 
1338  while (HeapTupleIsValid((maptup = systable_getnext(scan))))
1339  {
1341 
1342  /*
1343  * check if it's one of target token types
1344  */
1345  if (tokens)
1346  {
1347  bool tokmatch = false;
1348 
1349  for (j = 0; j < ntoken; j++)
1350  {
1351  if (cfgmap->maptokentype == tokens[j])
1352  {
1353  tokmatch = true;
1354  break;
1355  }
1356  }
1357  if (!tokmatch)
1358  continue;
1359  }
1360 
1361  /*
1362  * replace dictionary if match
1363  */
1364  if (cfgmap->mapdict == dictOld)
1365  {
1366  Datum repl_val[Natts_pg_ts_config_map];
1367  bool repl_null[Natts_pg_ts_config_map];
1368  bool repl_repl[Natts_pg_ts_config_map];
1369  HeapTuple newtup;
1370 
1371  memset(repl_val, 0, sizeof(repl_val));
1372  memset(repl_null, false, sizeof(repl_null));
1373  memset(repl_repl, false, sizeof(repl_repl));
1374 
1375  repl_val[Anum_pg_ts_config_map_mapdict - 1] = ObjectIdGetDatum(dictNew);
1376  repl_repl[Anum_pg_ts_config_map_mapdict - 1] = true;
1377 
1378  newtup = heap_modify_tuple(maptup,
1379  RelationGetDescr(relMap),
1380  repl_val, repl_null, repl_repl);
1381  CatalogTupleUpdateWithInfo(relMap, &newtup->t_self, newtup, indstate);
1382  }
1383  }
1384 
1385  systable_endscan(scan);
1386  }
1387  else
1388  {
1389  TupleTableSlot **slot;
1390  int slotCount = 0;
1391  int nslots;
1392 
1393  /* Allocate the slots to use and initialize them */
1394  nslots = Min(ntoken * ndict,
1396  slot = palloc(sizeof(TupleTableSlot *) * nslots);
1397  for (i = 0; i < nslots; i++)
1399  &TTSOpsHeapTuple);
1400 
1401  /*
1402  * Insertion of new entries
1403  */
1404  for (i = 0; i < ntoken; i++)
1405  {
1406  for (j = 0; j < ndict; j++)
1407  {
1408  ExecClearTuple(slot[slotCount]);
1409 
1410  memset(slot[slotCount]->tts_isnull, false,
1411  slot[slotCount]->tts_tupleDescriptor->natts * sizeof(bool));
1412 
1413  slot[slotCount]->tts_values[Anum_pg_ts_config_map_mapcfg - 1] = ObjectIdGetDatum(cfgId);
1414  slot[slotCount]->tts_values[Anum_pg_ts_config_map_maptokentype - 1] = Int32GetDatum(tokens[i]);
1415  slot[slotCount]->tts_values[Anum_pg_ts_config_map_mapseqno - 1] = Int32GetDatum(j + 1);
1416  slot[slotCount]->tts_values[Anum_pg_ts_config_map_mapdict - 1] = ObjectIdGetDatum(dictIds[j]);
1417 
1418  ExecStoreVirtualTuple(slot[slotCount]);
1419  slotCount++;
1420 
1421  /* If slots are full, insert a batch of tuples */
1422  if (slotCount == nslots)
1423  {
1424  CatalogTuplesMultiInsertWithInfo(relMap, slot, slotCount,
1425  indstate);
1426  slotCount = 0;
1427  }
1428  }
1429  }
1430 
1431  /* Insert any tuples left in the buffer */
1432  if (slotCount > 0)
1433  CatalogTuplesMultiInsertWithInfo(relMap, slot, slotCount,
1434  indstate);
1435 
1436  for (i = 0; i < nslots; i++)
1438  }
1439 
1440  /* clean up */
1441  CatalogCloseIndexes(indstate);
1442 
1443  EventTriggerCollectAlterTSConfig(stmt, cfgId, dictIds, ndict);
1444 }
1445 
1446 /*
1447  * ALTER TEXT SEARCH CONFIGURATION DROP MAPPING
1448  */
1449 static void
1451  HeapTuple tup, Relation relMap)
1452 {
1453  Form_pg_ts_config tsform;
1454  Oid cfgId;
1455  ScanKeyData skey[2];
1456  SysScanDesc scan;
1457  HeapTuple maptup;
1458  int i;
1459  Oid prsId;
1460  int *tokens;
1461  ListCell *c;
1462 
1463  tsform = (Form_pg_ts_config) GETSTRUCT(tup);
1464  cfgId = tsform->oid;
1465  prsId = tsform->cfgparser;
1466 
1467  tokens = getTokenTypes(prsId, stmt->tokentype);
1468 
1469  i = 0;
1470  foreach(c, stmt->tokentype)
1471  {
1472  String *val = lfirst_node(String, c);
1473  bool found = false;
1474 
1475  ScanKeyInit(&skey[0],
1476  Anum_pg_ts_config_map_mapcfg,
1477  BTEqualStrategyNumber, F_OIDEQ,
1478  ObjectIdGetDatum(cfgId));
1479  ScanKeyInit(&skey[1],
1480  Anum_pg_ts_config_map_maptokentype,
1481  BTEqualStrategyNumber, F_INT4EQ,
1482  Int32GetDatum(tokens[i]));
1483 
1484  scan = systable_beginscan(relMap, TSConfigMapIndexId, true,
1485  NULL, 2, skey);
1486 
1487  while (HeapTupleIsValid((maptup = systable_getnext(scan))))
1488  {
1489  CatalogTupleDelete(relMap, &maptup->t_self);
1490  found = true;
1491  }
1492 
1493  systable_endscan(scan);
1494 
1495  if (!found)
1496  {
1497  if (!stmt->missing_ok)
1498  {
1499  ereport(ERROR,
1500  (errcode(ERRCODE_UNDEFINED_OBJECT),
1501  errmsg("mapping for token type \"%s\" does not exist",
1502  strVal(val))));
1503  }
1504  else
1505  {
1506  ereport(NOTICE,
1507  (errmsg("mapping for token type \"%s\" does not exist, skipping",
1508  strVal(val))));
1509  }
1510  }
1511 
1512  i++;
1513  }
1514 
1515  EventTriggerCollectAlterTSConfig(stmt, cfgId, NULL, 0);
1516 }
1517 
1518 
1519 /*
1520  * Serialize dictionary options, producing a TEXT datum from a List of DefElem
1521  *
1522  * This is used to form the value stored in pg_ts_dict.dictinitoption.
1523  * For the convenience of pg_dump, the output is formatted exactly as it
1524  * would need to appear in CREATE TEXT SEARCH DICTIONARY to reproduce the
1525  * same options.
1526  */
1527 text *
1529 {
1530  text *result;
1532  ListCell *l;
1533 
1534  initStringInfo(&buf);
1535 
1536  foreach(l, deflist)
1537  {
1538  DefElem *defel = (DefElem *) lfirst(l);
1539  char *val = defGetString(defel);
1540 
1541  appendStringInfo(&buf, "%s = ",
1542  quote_identifier(defel->defname));
1543 
1544  /*
1545  * If the value is a T_Integer or T_Float, emit it without quotes,
1546  * otherwise with quotes. This is essential to allow correct
1547  * reconstruction of the node type as well as the value.
1548  */
1549  if (IsA(defel->arg, Integer) || IsA(defel->arg, Float))
1551  else
1552  {
1553  /* If backslashes appear, force E syntax to quote them safely */
1554  if (strchr(val, '\\'))
1556  appendStringInfoChar(&buf, '\'');
1557  while (*val)
1558  {
1559  char ch = *val++;
1560 
1561  if (SQL_STR_DOUBLE(ch, true))
1562  appendStringInfoChar(&buf, ch);
1563  appendStringInfoChar(&buf, ch);
1564  }
1565  appendStringInfoChar(&buf, '\'');
1566  }
1567  if (lnext(deflist, l) != NULL)
1568  appendStringInfoString(&buf, ", ");
1569  }
1570 
1571  result = cstring_to_text_with_len(buf.data, buf.len);
1572  pfree(buf.data);
1573  return result;
1574 }
1575 
1576 /*
1577  * Deserialize dictionary options, reconstructing a List of DefElem from TEXT
1578  *
1579  * This is also used for prsheadline options, so for backward compatibility
1580  * we need to accept a few things serialize_deflist() will never emit:
1581  * in particular, unquoted and double-quoted strings.
1582  */
1583 List *
1585 {
1586  text *in = DatumGetTextPP(txt); /* in case it's toasted */
1587  List *result = NIL;
1588  int len = VARSIZE_ANY_EXHDR(in);
1589  char *ptr,
1590  *endptr,
1591  *workspace,
1592  *wsptr = NULL,
1593  *startvalue = NULL;
1594  typedef enum
1595  {
1596  CS_WAITKEY,
1597  CS_INKEY,
1598  CS_INQKEY,
1599  CS_WAITEQ,
1600  CS_WAITVALUE,
1601  CS_INSQVALUE,
1602  CS_INDQVALUE,
1603  CS_INWVALUE
1604  } ds_state;
1605  ds_state state = CS_WAITKEY;
1606 
1607  workspace = (char *) palloc(len + 1); /* certainly enough room */
1608  ptr = VARDATA_ANY(in);
1609  endptr = ptr + len;
1610  for (; ptr < endptr; ptr++)
1611  {
1612  switch (state)
1613  {
1614  case CS_WAITKEY:
1615  if (isspace((unsigned char) *ptr) || *ptr == ',')
1616  continue;
1617  if (*ptr == '"')
1618  {
1619  wsptr = workspace;
1620  state = CS_INQKEY;
1621  }
1622  else
1623  {
1624  wsptr = workspace;
1625  *wsptr++ = *ptr;
1626  state = CS_INKEY;
1627  }
1628  break;
1629  case CS_INKEY:
1630  if (isspace((unsigned char) *ptr))
1631  {
1632  *wsptr++ = '\0';
1633  state = CS_WAITEQ;
1634  }
1635  else if (*ptr == '=')
1636  {
1637  *wsptr++ = '\0';
1638  state = CS_WAITVALUE;
1639  }
1640  else
1641  {
1642  *wsptr++ = *ptr;
1643  }
1644  break;
1645  case CS_INQKEY:
1646  if (*ptr == '"')
1647  {
1648  if (ptr + 1 < endptr && ptr[1] == '"')
1649  {
1650  /* copy only one of the two quotes */
1651  *wsptr++ = *ptr++;
1652  }
1653  else
1654  {
1655  *wsptr++ = '\0';
1656  state = CS_WAITEQ;
1657  }
1658  }
1659  else
1660  {
1661  *wsptr++ = *ptr;
1662  }
1663  break;
1664  case CS_WAITEQ:
1665  if (*ptr == '=')
1666  state = CS_WAITVALUE;
1667  else if (!isspace((unsigned char) *ptr))
1668  ereport(ERROR,
1669  (errcode(ERRCODE_SYNTAX_ERROR),
1670  errmsg("invalid parameter list format: \"%s\"",
1671  text_to_cstring(in))));
1672  break;
1673  case CS_WAITVALUE:
1674  if (*ptr == '\'')
1675  {
1676  startvalue = wsptr;
1677  state = CS_INSQVALUE;
1678  }
1679  else if (*ptr == 'E' && ptr + 1 < endptr && ptr[1] == '\'')
1680  {
1681  ptr++;
1682  startvalue = wsptr;
1683  state = CS_INSQVALUE;
1684  }
1685  else if (*ptr == '"')
1686  {
1687  startvalue = wsptr;
1688  state = CS_INDQVALUE;
1689  }
1690  else if (!isspace((unsigned char) *ptr))
1691  {
1692  startvalue = wsptr;
1693  *wsptr++ = *ptr;
1694  state = CS_INWVALUE;
1695  }
1696  break;
1697  case CS_INSQVALUE:
1698  if (*ptr == '\'')
1699  {
1700  if (ptr + 1 < endptr && ptr[1] == '\'')
1701  {
1702  /* copy only one of the two quotes */
1703  *wsptr++ = *ptr++;
1704  }
1705  else
1706  {
1707  *wsptr++ = '\0';
1708  result = lappend(result,
1709  buildDefItem(workspace,
1710  startvalue,
1711  true));
1712  state = CS_WAITKEY;
1713  }
1714  }
1715  else if (*ptr == '\\')
1716  {
1717  if (ptr + 1 < endptr && ptr[1] == '\\')
1718  {
1719  /* copy only one of the two backslashes */
1720  *wsptr++ = *ptr++;
1721  }
1722  else
1723  *wsptr++ = *ptr;
1724  }
1725  else
1726  {
1727  *wsptr++ = *ptr;
1728  }
1729  break;
1730  case CS_INDQVALUE:
1731  if (*ptr == '"')
1732  {
1733  if (ptr + 1 < endptr && ptr[1] == '"')
1734  {
1735  /* copy only one of the two quotes */
1736  *wsptr++ = *ptr++;
1737  }
1738  else
1739  {
1740  *wsptr++ = '\0';
1741  result = lappend(result,
1742  buildDefItem(workspace,
1743  startvalue,
1744  true));
1745  state = CS_WAITKEY;
1746  }
1747  }
1748  else
1749  {
1750  *wsptr++ = *ptr;
1751  }
1752  break;
1753  case CS_INWVALUE:
1754  if (*ptr == ',' || isspace((unsigned char) *ptr))
1755  {
1756  *wsptr++ = '\0';
1757  result = lappend(result,
1758  buildDefItem(workspace,
1759  startvalue,
1760  false));
1761  state = CS_WAITKEY;
1762  }
1763  else
1764  {
1765  *wsptr++ = *ptr;
1766  }
1767  break;
1768  default:
1769  elog(ERROR, "unrecognized deserialize_deflist state: %d",
1770  state);
1771  }
1772  }
1773 
1774  if (state == CS_INWVALUE)
1775  {
1776  *wsptr++ = '\0';
1777  result = lappend(result,
1778  buildDefItem(workspace,
1779  startvalue,
1780  false));
1781  }
1782  else if (state != CS_WAITKEY)
1783  ereport(ERROR,
1784  (errcode(ERRCODE_SYNTAX_ERROR),
1785  errmsg("invalid parameter list format: \"%s\"",
1786  text_to_cstring(in))));
1787 
1788  pfree(workspace);
1789 
1790  return result;
1791 }
1792 
1793 /*
1794  * Build one DefElem for deserialize_deflist
1795  */
1796 static DefElem *
1797 buildDefItem(const char *name, const char *val, bool was_quoted)
1798 {
1799  /* If input was quoted, always emit as string */
1800  if (!was_quoted && val[0] != '\0')
1801  {
1802  int v;
1803  char *endptr;
1804 
1805  /* Try to parse as an integer */
1806  errno = 0;
1807  v = strtoint(val, &endptr, 10);
1808  if (errno == 0 && *endptr == '\0')
1809  return makeDefElem(pstrdup(name),
1810  (Node *) makeInteger(v),
1811  -1);
1812  /* Nope, how about as a float? */
1813  errno = 0;
1814  (void) strtod(val, &endptr);
1815  if (errno == 0 && *endptr == '\0')
1816  return makeDefElem(pstrdup(name),
1817  (Node *) makeFloat(pstrdup(val)),
1818  -1);
1819 
1820  if (strcmp(val, "true") == 0)
1821  return makeDefElem(pstrdup(name),
1822  (Node *) makeBoolean(true),
1823  -1);
1824  if (strcmp(val, "false") == 0)
1825  return makeDefElem(pstrdup(name),
1826  (Node *) makeBoolean(false),
1827  -1);
1828  }
1829  /* Just make it a string */
1830  return makeDefElem(pstrdup(name),
1831  (Node *) makeString(pstrdup(val)),
1832  -1);
1833 }
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:2669
AclResult object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
Definition: aclchk.c:3760
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition: aclchk.c:3961
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:1179
#define SQL_STR_DOUBLE(ch, escape_backslash)
Definition: c.h:1176
#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:2790
ObjectAddresses * new_object_addresses(void)
Definition: dependency.c:2532
void add_exact_object_address(const ObjectAddress *object, ObjectAddresses *addrs)
Definition: dependency.c:2581
void free_object_addresses(ObjectAddresses *addrs)
Definition: dependency.c:2821
@ DEPENDENCY_NORMAL
Definition: dependency.h:33
int errcode(int sqlerrcode)
Definition: elog.c:858
int errmsg(const char *fmt,...)
Definition: elog.c:1069
#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:1553
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1255
const TupleTableSlotOps TTSOpsHeapTuple
Definition: execTuples.c:84
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1239
#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:113
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: heaptuple.c:1108
HeapTuple heap_modify_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *replValues, bool *replIsnull, bool *doReplace)
Definition: heaptuple.c:1201
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1426
#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:338
#define RowExclusiveLock
Definition: lockdefs.h:38
char * get_namespace_name(Oid nspid)
Definition: lsyscache.c:3348
Oid get_func_rettype(Oid funcid)
Definition: lsyscache.c:1659
DefElem * makeDefElem(char *name, Node *arg, int location)
Definition: makefuncs.c:549
char * pstrdup(const char *in)
Definition: mcxt.c:1644
void pfree(void *pointer)
Definition: mcxt.c:1456
void * palloc(Size size)
Definition: mcxt.c:1226
Oid GetUserId(void)
Definition: miscinit.c:509
void namestrcpy(Name name, const char *str)
Definition: name.c:233
Oid QualifiedNameGetCreationNamespace(const List *names, char **objname_p)
Definition: namespace.c:3020
Oid get_ts_dict_oid(List *names, bool missing_ok)
Definition: namespace.c:2451
Oid get_ts_parser_oid(List *names, bool missing_ok)
Definition: namespace.c:2325
char * NameListToString(const List *names)
Definition: namespace.c:3127
Oid get_ts_config_oid(List *names, bool missing_ok)
Definition: namespace.c:2704
Oid get_ts_template_oid(List *names, bool missing_ok)
Definition: namespace.c:2578
#define IsA(nodeptr, _type_)
Definition: nodes.h:179
#define copyObject(obj)
Definition: nodes.h:244
#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:2156
@ OBJECT_TSCONFIGURATION
Definition: parsenodes.h:2165
@ OBJECT_TSDICTIONARY
Definition: parsenodes.h:2166
#define ACL_CREATE
Definition: parsenodes.h:92
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
static ListCell * lnext(const List *l, const ListCell *c)
Definition: pg_list.h:343
#define foreach_delete_current(lst, cell)
Definition: pg_list.h:390
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:67
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:11965
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:91
void appendStringInfoString(StringInfo str, const char *s)
Definition: stringinfo.c:176
void appendStringInfoChar(StringInfo str, char ch)
Definition: stringinfo.c:188
void initStringInfo(StringInfo str)
Definition: stringinfo.c:59
char * defname
Definition: parsenodes.h:809
Node * arg
Definition: parsenodes.h:810
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:868
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:820
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:1081
@ TSTEMPLATEOID
Definition: syscache.h:112
@ TSDICTOID
Definition: syscache.h:108
@ TSCONFIGOID
Definition: syscache.h:106
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:1150
ObjectAddress DefineTSTemplate(List *names, List *parameters)
Definition: tsearchcmds.c:684
ObjectAddress DefineTSDictionary(List *names, List *parameters)
Definition: tsearchcmds.c:391
static ObjectAddress makeTSTemplateDependencies(HeapTuple tuple)
Definition: tsearchcmds.c:646
static ObjectAddress makeConfigurationDependencies(HeapTuple tuple, bool removeOld, Relation mapRel)
Definition: tsearchcmds.c:806
void RemoveTSConfigurationById(Oid cfgId)
Definition: tsearchcmds.c:1102
static HeapTuple GetTSConfigTuple(List *names)
Definition: tsearchcmds.c:781
static DefElem * buildDefItem(const char *name, const char *val, bool was_quoted)
Definition: tsearchcmds.c:1797
static void DropConfigurationMapping(AlterTSConfigurationStmt *stmt, HeapTuple tup, Relation relMap)
Definition: tsearchcmds.c:1450
static ObjectAddress makeDictionaryDependencies(HeapTuple tuple)
Definition: tsearchcmds.c:301
static int * getTokenTypes(Oid prsId, List *tokennames)
Definition: tsearchcmds.c:1198
static Datum get_ts_template_func(DefElem *defel, int attnum)
Definition: tsearchcmds.c:603
List * deserialize_deflist(Datum txt)
Definition: tsearchcmds.c:1584
ObjectAddress DefineTSConfiguration(List *names, List *parameters, ObjectAddress *copied)
Definition: tsearchcmds.c:893
static Datum get_ts_parser_func(DefElem *defel, int attnum)
Definition: tsearchcmds.c:68
ObjectAddress AlterTSDictionary(AlterTSDictionaryStmt *stmt)
Definition: tsearchcmds.c:487
static void verify_dictoptions(Oid tmplId, List *dictoptions)
Definition: tsearchcmds.c:336
ObjectAddress DefineTSParser(List *names, List *parameters)
Definition: tsearchcmds.c:178
static void MakeConfigurationMapping(AlterTSConfigurationStmt *stmt, HeapTuple tup, Relation relMap)
Definition: tsearchcmds.c:1253
static ObjectAddress makeParserDependencies(HeapTuple tuple)
Definition: tsearchcmds.c:131
text * serialize_deflist(List *deflist)
Definition: tsearchcmds.c:1528
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:432
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:215
text * cstring_to_text_with_len(const char *s, int len)
Definition: varlena.c:194
const char * name
void CommandCounterIncrement(void)
Definition: xact.c:1078