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