PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
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-2017, 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
65 get_ts_parser_func(DefElem *defel, int attnum)
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  {
78  nargs = 2;
79  typeId[1] = INT4OID;
80  break;
82  nargs = 3;
83  typeId[1] = INTERNALOID;
84  typeId[2] = INTERNALOID;
85  break;
87  nargs = 1;
88  retTypeId = VOIDOID;
89  break;
91  nargs = 3;
92  typeId[1] = INTERNALOID;
93  typeId[2] = TSQUERYOID;
94  break;
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;
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 (pg_strcasecmp(defel->defname, "start") == 0)
213  {
214  values[Anum_pg_ts_parser_prsstart - 1] =
216  }
217  else if (pg_strcasecmp(defel->defname, "gettoken") == 0)
218  {
219  values[Anum_pg_ts_parser_prstoken - 1] =
221  }
222  else if (pg_strcasecmp(defel->defname, "end") == 0)
223  {
224  values[Anum_pg_ts_parser_prsend - 1] =
226  }
227  else if (pg_strcasecmp(defel->defname, "headline") == 0)
228  {
229  values[Anum_pg_ts_parser_prsheadline - 1] =
231  }
232  else if (pg_strcasecmp(defel->defname, "lextypes") == 0)
233  {
234  values[Anum_pg_ts_parser_prslextype - 1] =
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  */
248  ereport(ERROR,
249  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
250  errmsg("text search parser start method is required")));
251 
253  ereport(ERROR,
254  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
255  errmsg("text search parser gettoken method is required")));
256 
258  ereport(ERROR,
259  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
260  errmsg("text search parser end method is required")));
261 
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  */
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 */
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 
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 
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;
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)
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 (pg_strcasecmp(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);
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 
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 */
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 
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 
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,
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 (pg_strcasecmp(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 
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
647 get_ts_template_func(DefElem *defel, int attnum)
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  {
663  nargs = 1;
664  break;
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;
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 (pg_strcasecmp(defel->defname, "init") == 0)
769  {
770  values[Anum_pg_ts_template_tmplinit - 1] =
772  nulls[Anum_pg_ts_template_tmplinit - 1] = false;
773  }
774  else if (pg_strcasecmp(defel->defname, "lexize") == 0)
775  {
776  values[Anum_pg_ts_template_tmpllexize - 1] =
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  */
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 
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 */
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 
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,
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;
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)
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 (pg_strcasecmp(defel->defname, "parser") == 0)
994  prsOid = get_ts_parser_oid(defGetQualifiedName(defel), false);
995  else if (pg_strcasecmp(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,
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);
1055  values[Anum_pg_ts_config_cfgparser - 1] = ObjectIdGetDatum(prsOid);
1056 
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 
1073 
1074  ScanKeyInit(&skey,
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 */
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 */
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 */
1150 
1151  ScanKeyInit(&skey,
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 
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 
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  /* XXX should we use pg_strcasecmp here? */
1255  if (strcmp(strVal(val), list[j].alias) == 0)
1256  {
1257  res[i] = list[j].lexid;
1258  found = true;
1259  break;
1260  }
1261  j++;
1262  }
1263  if (!found)
1264  ereport(ERROR,
1265  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1266  errmsg("token type \"%s\" does not exist",
1267  strVal(val))));
1268  i++;
1269  }
1270 
1271  return res;
1272 }
1273 
1274 /*
1275  * ALTER TEXT SEARCH CONFIGURATION ADD/ALTER MAPPING
1276  */
1277 static void
1279  HeapTuple tup, Relation relMap)
1280 {
1281  Oid cfgId = HeapTupleGetOid(tup);
1282  ScanKeyData skey[2];
1283  SysScanDesc scan;
1284  HeapTuple maptup;
1285  int i;
1286  int j;
1287  Oid prsId;
1288  int *tokens,
1289  ntoken;
1290  Oid *dictIds;
1291  int ndict;
1292  ListCell *c;
1293 
1294  prsId = ((Form_pg_ts_config) GETSTRUCT(tup))->cfgparser;
1295 
1296  tokens = getTokenTypes(prsId, stmt->tokentype);
1297  ntoken = list_length(stmt->tokentype);
1298 
1299  if (stmt->override)
1300  {
1301  /*
1302  * delete maps for tokens if they exist and command was ALTER
1303  */
1304  for (i = 0; i < ntoken; i++)
1305  {
1306  ScanKeyInit(&skey[0],
1308  BTEqualStrategyNumber, F_OIDEQ,
1309  ObjectIdGetDatum(cfgId));
1310  ScanKeyInit(&skey[1],
1312  BTEqualStrategyNumber, F_INT4EQ,
1313  Int32GetDatum(tokens[i]));
1314 
1315  scan = systable_beginscan(relMap, TSConfigMapIndexId, true,
1316  NULL, 2, skey);
1317 
1318  while (HeapTupleIsValid((maptup = systable_getnext(scan))))
1319  {
1320  CatalogTupleDelete(relMap, &maptup->t_self);
1321  }
1322 
1323  systable_endscan(scan);
1324  }
1325  }
1326 
1327  /*
1328  * Convert list of dictionary names to array of dict OIDs
1329  */
1330  ndict = list_length(stmt->dicts);
1331  dictIds = (Oid *) palloc(sizeof(Oid) * ndict);
1332  i = 0;
1333  foreach(c, stmt->dicts)
1334  {
1335  List *names = (List *) lfirst(c);
1336 
1337  dictIds[i] = get_ts_dict_oid(names, false);
1338  i++;
1339  }
1340 
1341  if (stmt->replace)
1342  {
1343  /*
1344  * Replace a specific dictionary in existing entries
1345  */
1346  Oid dictOld = dictIds[0],
1347  dictNew = dictIds[1];
1348 
1349  ScanKeyInit(&skey[0],
1351  BTEqualStrategyNumber, F_OIDEQ,
1352  ObjectIdGetDatum(cfgId));
1353 
1354  scan = systable_beginscan(relMap, TSConfigMapIndexId, true,
1355  NULL, 1, skey);
1356 
1357  while (HeapTupleIsValid((maptup = systable_getnext(scan))))
1358  {
1360 
1361  /*
1362  * check if it's one of target token types
1363  */
1364  if (tokens)
1365  {
1366  bool tokmatch = false;
1367 
1368  for (j = 0; j < ntoken; j++)
1369  {
1370  if (cfgmap->maptokentype == tokens[j])
1371  {
1372  tokmatch = true;
1373  break;
1374  }
1375  }
1376  if (!tokmatch)
1377  continue;
1378  }
1379 
1380  /*
1381  * replace dictionary if match
1382  */
1383  if (cfgmap->mapdict == dictOld)
1384  {
1385  Datum repl_val[Natts_pg_ts_config_map];
1386  bool repl_null[Natts_pg_ts_config_map];
1387  bool repl_repl[Natts_pg_ts_config_map];
1388  HeapTuple newtup;
1389 
1390  memset(repl_val, 0, sizeof(repl_val));
1391  memset(repl_null, false, sizeof(repl_null));
1392  memset(repl_repl, false, sizeof(repl_repl));
1393 
1394  repl_val[Anum_pg_ts_config_map_mapdict - 1] = ObjectIdGetDatum(dictNew);
1395  repl_repl[Anum_pg_ts_config_map_mapdict - 1] = true;
1396 
1397  newtup = heap_modify_tuple(maptup,
1398  RelationGetDescr(relMap),
1399  repl_val, repl_null, repl_repl);
1400  CatalogTupleUpdate(relMap, &newtup->t_self, newtup);
1401  }
1402  }
1403 
1404  systable_endscan(scan);
1405  }
1406  else
1407  {
1408  /*
1409  * Insertion of new entries
1410  */
1411  for (i = 0; i < ntoken; i++)
1412  {
1413  for (j = 0; j < ndict; j++)
1414  {
1416  bool nulls[Natts_pg_ts_config_map];
1417 
1418  memset(nulls, false, sizeof(nulls));
1419  values[Anum_pg_ts_config_map_mapcfg - 1] = ObjectIdGetDatum(cfgId);
1420  values[Anum_pg_ts_config_map_maptokentype - 1] = Int32GetDatum(tokens[i]);
1421  values[Anum_pg_ts_config_map_mapseqno - 1] = Int32GetDatum(j + 1);
1422  values[Anum_pg_ts_config_map_mapdict - 1] = ObjectIdGetDatum(dictIds[j]);
1423 
1424  tup = heap_form_tuple(relMap->rd_att, values, nulls);
1425  CatalogTupleInsert(relMap, tup);
1426 
1427  heap_freetuple(tup);
1428  }
1429  }
1430  }
1431 
1432  EventTriggerCollectAlterTSConfig(stmt, cfgId, dictIds, ndict);
1433 }
1434 
1435 /*
1436  * ALTER TEXT SEARCH CONFIGURATION DROP MAPPING
1437  */
1438 static void
1440  HeapTuple tup, Relation relMap)
1441 {
1442  Oid cfgId = HeapTupleGetOid(tup);
1443  ScanKeyData skey[2];
1444  SysScanDesc scan;
1445  HeapTuple maptup;
1446  int i;
1447  Oid prsId;
1448  int *tokens;
1449  ListCell *c;
1450 
1451  prsId = ((Form_pg_ts_config) GETSTRUCT(tup))->cfgparser;
1452 
1453  tokens = getTokenTypes(prsId, stmt->tokentype);
1454 
1455  i = 0;
1456  foreach(c, stmt->tokentype)
1457  {
1458  Value *val = (Value *) lfirst(c);
1459  bool found = false;
1460 
1461  ScanKeyInit(&skey[0],
1463  BTEqualStrategyNumber, F_OIDEQ,
1464  ObjectIdGetDatum(cfgId));
1465  ScanKeyInit(&skey[1],
1467  BTEqualStrategyNumber, F_INT4EQ,
1468  Int32GetDatum(tokens[i]));
1469 
1470  scan = systable_beginscan(relMap, TSConfigMapIndexId, true,
1471  NULL, 2, skey);
1472 
1473  while (HeapTupleIsValid((maptup = systable_getnext(scan))))
1474  {
1475  CatalogTupleDelete(relMap, &maptup->t_self);
1476  found = true;
1477  }
1478 
1479  systable_endscan(scan);
1480 
1481  if (!found)
1482  {
1483  if (!stmt->missing_ok)
1484  {
1485  ereport(ERROR,
1486  (errcode(ERRCODE_UNDEFINED_OBJECT),
1487  errmsg("mapping for token type \"%s\" does not exist",
1488  strVal(val))));
1489  }
1490  else
1491  {
1492  ereport(NOTICE,
1493  (errmsg("mapping for token type \"%s\" does not exist, skipping",
1494  strVal(val))));
1495  }
1496  }
1497 
1498  i++;
1499  }
1500 
1501  EventTriggerCollectAlterTSConfig(stmt, cfgId, NULL, 0);
1502 }
1503 
1504 
1505 /*
1506  * Serialize dictionary options, producing a TEXT datum from a List of DefElem
1507  *
1508  * This is used to form the value stored in pg_ts_dict.dictinitoption.
1509  * For the convenience of pg_dump, the output is formatted exactly as it
1510  * would need to appear in CREATE TEXT SEARCH DICTIONARY to reproduce the
1511  * same options.
1512  *
1513  * Note that we assume that only the textual representation of an option's
1514  * value is interesting --- hence, non-string DefElems get forced to strings.
1515  */
1516 text *
1518 {
1519  text *result;
1521  ListCell *l;
1522 
1523  initStringInfo(&buf);
1524 
1525  foreach(l, deflist)
1526  {
1527  DefElem *defel = (DefElem *) lfirst(l);
1528  char *val = defGetString(defel);
1529 
1530  appendStringInfo(&buf, "%s = ",
1531  quote_identifier(defel->defname));
1532  /* If backslashes appear, force E syntax to determine their handling */
1533  if (strchr(val, '\\'))
1535  appendStringInfoChar(&buf, '\'');
1536  while (*val)
1537  {
1538  char ch = *val++;
1539 
1540  if (SQL_STR_DOUBLE(ch, true))
1541  appendStringInfoChar(&buf, ch);
1542  appendStringInfoChar(&buf, ch);
1543  }
1544  appendStringInfoChar(&buf, '\'');
1545  if (lnext(l) != NULL)
1546  appendStringInfoString(&buf, ", ");
1547  }
1548 
1549  result = cstring_to_text_with_len(buf.data, buf.len);
1550  pfree(buf.data);
1551  return result;
1552 }
1553 
1554 /*
1555  * Deserialize dictionary options, reconstructing a List of DefElem from TEXT
1556  *
1557  * This is also used for prsheadline options, so for backward compatibility
1558  * we need to accept a few things serialize_deflist() will never emit:
1559  * in particular, unquoted and double-quoted values.
1560  */
1561 List *
1563 {
1564  text *in = DatumGetTextPP(txt); /* in case it's toasted */
1565  List *result = NIL;
1566  int len = VARSIZE_ANY_EXHDR(in);
1567  char *ptr,
1568  *endptr,
1569  *workspace,
1570  *wsptr = NULL,
1571  *startvalue = NULL;
1572  typedef enum
1573  {
1574  CS_WAITKEY,
1575  CS_INKEY,
1576  CS_INQKEY,
1577  CS_WAITEQ,
1578  CS_WAITVALUE,
1579  CS_INSQVALUE,
1580  CS_INDQVALUE,
1581  CS_INWVALUE
1582  } ds_state;
1583  ds_state state = CS_WAITKEY;
1584 
1585  workspace = (char *) palloc(len + 1); /* certainly enough room */
1586  ptr = VARDATA_ANY(in);
1587  endptr = ptr + len;
1588  for (; ptr < endptr; ptr++)
1589  {
1590  switch (state)
1591  {
1592  case CS_WAITKEY:
1593  if (isspace((unsigned char) *ptr) || *ptr == ',')
1594  continue;
1595  if (*ptr == '"')
1596  {
1597  wsptr = workspace;
1598  state = CS_INQKEY;
1599  }
1600  else
1601  {
1602  wsptr = workspace;
1603  *wsptr++ = *ptr;
1604  state = CS_INKEY;
1605  }
1606  break;
1607  case CS_INKEY:
1608  if (isspace((unsigned char) *ptr))
1609  {
1610  *wsptr++ = '\0';
1611  state = CS_WAITEQ;
1612  }
1613  else if (*ptr == '=')
1614  {
1615  *wsptr++ = '\0';
1616  state = CS_WAITVALUE;
1617  }
1618  else
1619  {
1620  *wsptr++ = *ptr;
1621  }
1622  break;
1623  case CS_INQKEY:
1624  if (*ptr == '"')
1625  {
1626  if (ptr + 1 < endptr && ptr[1] == '"')
1627  {
1628  /* copy only one of the two quotes */
1629  *wsptr++ = *ptr++;
1630  }
1631  else
1632  {
1633  *wsptr++ = '\0';
1634  state = CS_WAITEQ;
1635  }
1636  }
1637  else
1638  {
1639  *wsptr++ = *ptr;
1640  }
1641  break;
1642  case CS_WAITEQ:
1643  if (*ptr == '=')
1644  state = CS_WAITVALUE;
1645  else if (!isspace((unsigned char) *ptr))
1646  ereport(ERROR,
1647  (errcode(ERRCODE_SYNTAX_ERROR),
1648  errmsg("invalid parameter list format: \"%s\"",
1649  text_to_cstring(in))));
1650  break;
1651  case CS_WAITVALUE:
1652  if (*ptr == '\'')
1653  {
1654  startvalue = wsptr;
1655  state = CS_INSQVALUE;
1656  }
1657  else if (*ptr == 'E' && ptr + 1 < endptr && ptr[1] == '\'')
1658  {
1659  ptr++;
1660  startvalue = wsptr;
1661  state = CS_INSQVALUE;
1662  }
1663  else if (*ptr == '"')
1664  {
1665  startvalue = wsptr;
1666  state = CS_INDQVALUE;
1667  }
1668  else if (!isspace((unsigned char) *ptr))
1669  {
1670  startvalue = wsptr;
1671  *wsptr++ = *ptr;
1672  state = CS_INWVALUE;
1673  }
1674  break;
1675  case CS_INSQVALUE:
1676  if (*ptr == '\'')
1677  {
1678  if (ptr + 1 < endptr && ptr[1] == '\'')
1679  {
1680  /* copy only one of the two quotes */
1681  *wsptr++ = *ptr++;
1682  }
1683  else
1684  {
1685  *wsptr++ = '\0';
1686  result = lappend(result,
1687  makeDefElem(pstrdup(workspace),
1688  (Node *) makeString(pstrdup(startvalue)), -1));
1689  state = CS_WAITKEY;
1690  }
1691  }
1692  else if (*ptr == '\\')
1693  {
1694  if (ptr + 1 < endptr && ptr[1] == '\\')
1695  {
1696  /* copy only one of the two backslashes */
1697  *wsptr++ = *ptr++;
1698  }
1699  else
1700  *wsptr++ = *ptr;
1701  }
1702  else
1703  {
1704  *wsptr++ = *ptr;
1705  }
1706  break;
1707  case CS_INDQVALUE:
1708  if (*ptr == '"')
1709  {
1710  if (ptr + 1 < endptr && ptr[1] == '"')
1711  {
1712  /* copy only one of the two quotes */
1713  *wsptr++ = *ptr++;
1714  }
1715  else
1716  {
1717  *wsptr++ = '\0';
1718  result = lappend(result,
1719  makeDefElem(pstrdup(workspace),
1720  (Node *) makeString(pstrdup(startvalue)), -1));
1721  state = CS_WAITKEY;
1722  }
1723  }
1724  else
1725  {
1726  *wsptr++ = *ptr;
1727  }
1728  break;
1729  case CS_INWVALUE:
1730  if (*ptr == ',' || isspace((unsigned char) *ptr))
1731  {
1732  *wsptr++ = '\0';
1733  result = lappend(result,
1734  makeDefElem(pstrdup(workspace),
1735  (Node *) makeString(pstrdup(startvalue)), -1));
1736  state = CS_WAITKEY;
1737  }
1738  else
1739  {
1740  *wsptr++ = *ptr;
1741  }
1742  break;
1743  default:
1744  elog(ERROR, "unrecognized deserialize_deflist state: %d",
1745  state);
1746  }
1747  }
1748 
1749  if (state == CS_INWVALUE)
1750  {
1751  *wsptr++ = '\0';
1752  result = lappend(result,
1753  makeDefElem(pstrdup(workspace),
1754  (Node *) makeString(pstrdup(startvalue)), -1));
1755  }
1756  else if (state != CS_WAITKEY)
1757  ereport(ERROR,
1758  (errcode(ERRCODE_SYNTAX_ERROR),
1759  errmsg("invalid parameter list format: \"%s\"",
1760  text_to_cstring(in))));
1761 
1762  pfree(workspace);
1763 
1764  return result;
1765 }
Value * makeString(char *str)
Definition: value.c:53
ObjectAddress DefineTSParser(List *names, List *parameters)
Definition: tsearchcmds.c:176
#define Anum_pg_ts_config_cfgparser
Definition: pg_ts_config.h:51
#define NIL
Definition: pg_list.h:69
#define Anum_pg_ts_dict_dicttemplate
Definition: pg_ts_dict.h:55
#define Natts_pg_ts_config_map
#define NamespaceRelationId
Definition: pg_namespace.h:34
#define TSQUERYOID
Definition: pg_type.h:621
#define Natts_pg_ts_dict
Definition: pg_ts_dict.h:51
ObjectAddress DefineTSConfiguration(List *names, List *parameters, ObjectAddress *copied)
Definition: tsearchcmds.c:960
#define NameGetDatum(X)
Definition: postgres.h:601
#define Anum_pg_ts_config_map_mapseqno
#define VARDATA_ANY(PTR)
Definition: postgres.h:347
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:499
#define GETSTRUCT(TUP)
Definition: htup_details.h:656
static int32 next
Definition: blutils.c:210
#define InvokeObjectPostCreateHook(classId, objectId, subId)
Definition: objectaccess.h:145
const char * quote_identifier(const char *ident)
Definition: ruleutils.c:10390
#define Natts_pg_ts_parser
Definition: pg_ts_parser.h:50
bool pg_ts_dict_ownercheck(Oid dict_oid, Oid roleid)
Definition: aclchk.c:4829
FormData_pg_ts_config * Form_pg_ts_config
Definition: pg_ts_config.h:41
#define RelationGetDescr(relation)
Definition: rel.h:428
char * alias
Definition: ts_public.h:28
Oid GetUserId(void)
Definition: miscinit.c:284
Oid QualifiedNameGetCreationNamespace(List *names, char **objname_p)
Definition: namespace.c:2956
FormData_pg_ts_config_map * Form_pg_ts_config_map
#define Anum_pg_ts_template_tmpllexize
#define PointerGetDatum(X)
Definition: postgres.h:562
bool pg_ts_config_ownercheck(Oid cfg_oid, Oid roleid)
Definition: aclchk.c:4856
#define ProcedureRelationId
Definition: pg_proc.h:33
List * deserialize_deflist(Datum txt)
Definition: tsearchcmds.c:1562
#define DatumGetObjectId(X)
Definition: postgres.h:506
char * pstrdup(const char *in)
Definition: mcxt.c:1076
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:2312
#define DatumGetTextPP(X)
Definition: fmgr.h:256
static ObjectAddress makeConfigurationDependencies(HeapTuple tuple, bool removeOld, Relation mapRel)
Definition: tsearchcmds.c:873
#define Anum_pg_ts_parser_prsend
Definition: pg_ts_parser.h:55
long deleteDependencyRecordsFor(Oid classId, Oid objectId, bool skipExtensionDeps)
Definition: pg_depend.c:191
#define INT4OID
Definition: pg_type.h:316
Definition: nodes.h:510
#define Natts_pg_ts_template
#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:2639
bool superuser(void)
Definition: superuser.c:47
char * format_type_be(Oid type_oid)
Definition: format_type.c:94
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:255
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:2110
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: heaptuple.c:695
#define heap_close(r, l)
Definition: heapam.h:97
int pg_strcasecmp(const char *s1, const char *s2)
Definition: pgstrcasecmp.c:36
void recordDependencyOnOwner(Oid classId, Oid objectId, Oid owner)
Definition: pg_shdepend.c:159
ObjectAddresses * new_object_addresses(void)
Definition: dependency.c:2055
void free_object_addresses(ObjectAddresses *addrs)
Definition: dependency.c:2326
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1373
unsigned int Oid
Definition: postgres_ext.h:31
#define ESCAPE_STRING_SYNTAX
Definition: c.h:504
int namestrcpy(Name name, const char *str)
Definition: name.c:216
#define Anum_pg_ts_config_map_maptokentype
static void DropConfigurationMapping(AlterTSConfigurationStmt *stmt, HeapTuple tup, Relation relMap)
Definition: tsearchcmds.c:1439
#define OidIsValid(objectId)
Definition: c.h:532
static ObjectAddress makeDictionaryDependencies(HeapTuple tuple)
Definition: tsearchcmds.c:319
DefElem * makeDefElem(char *name, Node *arg, int location)
Definition: makefuncs.c:544
AclResult pg_namespace_aclcheck(Oid nsp_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4484
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:328
Oid get_func_rettype(Oid funcid)
Definition: lsyscache.c:1459
#define Anum_pg_ts_dict_dictowner
Definition: pg_ts_dict.h:54
#define Natts_pg_ts_config
Definition: pg_ts_config.h:47
#define TSConfigRelationId
Definition: pg_ts_config.h:31
Oid get_ts_dict_oid(List *names, bool missing_ok)
Definition: namespace.c:2386
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:416
void pfree(void *pointer)
Definition: mcxt.c:949
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:78
#define VOIDOID
Definition: pg_type.h:690
#define TSConfigMapRelationId
#define ObjectIdGetDatum(X)
Definition: postgres.h:513
#define ERROR
Definition: elog.h:43
#define ACL_CREATE
Definition: parsenodes.h:82
Oid CatalogTupleInsert(Relation heapRel, HeapTuple tup)
Definition: indexing.c:162
#define TSDictionaryRelationId
Definition: pg_ts_dict.h:31
void RemoveTSConfigurationById(Oid cfgId)
Definition: tsearchcmds.c:1125
#define Anum_pg_ts_config_map_mapcfg
char * defGetString(DefElem *def)
Definition: define.c:49
ItemPointerData t_self
Definition: htup.h:65
#define OidFunctionCall1(functionId, arg1)
Definition: fmgr.h:623
ObjectAddress AlterTSConfiguration(AlterTSConfigurationStmt *stmt)
Definition: tsearchcmds.c:1173
Definition: c.h:487
FormData_pg_ts_dict * Form_pg_ts_dict
Definition: pg_ts_dict.h:45
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:2513
char * get_namespace_name(Oid nspid)
Definition: lsyscache.c:3033
char * c
#define Anum_pg_ts_config_cfgnamespace
Definition: pg_ts_config.h:49
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:101
void aclcheck_error(AclResult aclerr, AclObjectKind objectkind, const char *objectname)
Definition: aclchk.c:3399
#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
#define Anum_pg_ts_parser_prsheadline
Definition: pg_ts_parser.h:56
void EventTriggerCollectAlterTSConfig(AlterTSConfigurationStmt *stmt, Oid cfgId, Oid *dictIds, int ndicts)
#define Anum_pg_ts_template_tmplnamespace
#define Anum_pg_ts_config_cfgowner
Definition: pg_ts_config.h:50
#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:1908
Node * arg
Definition: parsenodes.h:720
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
#define Anum_pg_ts_template_tmplinit
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1112
char * NameListToString(List *names)
Definition: namespace.c:3063
#define Anum_pg_ts_config_map_mapdict
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:1517
uintptr_t Datum
Definition: postgres.h:372
void CommandCounterIncrement(void)
Definition: xact.c:915
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1160
FormData_pg_ts_parser * Form_pg_ts_parser
Definition: pg_ts_parser.h:44
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:1368
Relation heap_open(Oid relationId, LOCKMODE lockmode)
Definition: heapam.c:1290
#define TSParserRelationId
Definition: pg_ts_parser.h:31
TupleDesc rd_att
Definition: rel.h:115
#define InvalidOid
Definition: postgres_ext.h:36
#define INTERNALOID
Definition: pg_type.h:698
#define NOTICE
Definition: elog.h:37
static ObjectAddress makeTSTemplateDependencies(HeapTuple tuple)
Definition: tsearchcmds.c:690
#define Anum_pg_ts_parser_prsstart
Definition: pg_ts_parser.h:53
#define HeapTupleIsValid(tuple)
Definition: htup.h:77
Oid get_ts_parser_oid(List *names, bool missing_ok)
Definition: namespace.c:2260
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
#define Anum_pg_ts_dict_dictinitoption
Definition: pg_ts_dict.h:56
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:210
static int list_length(const List *l)
Definition: pg_list.h:89
#define Anum_pg_ts_parser_prsnamespace
Definition: pg_ts_parser.h:52
#define ObjectAddressSet(addr, class_id, object_id)
Definition: objectaddress.h:40
#define DatumGetPointer(X)
Definition: postgres.h:555
Oid LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
Definition: parse_func.c:1929
static Datum values[MAXATTR]
Definition: bootstrap.c:164
#define Anum_pg_ts_dict_dictnamespace
Definition: pg_ts_dict.h:53
char * text_to_cstring(const text *t)
Definition: varlena.c:182
#define Anum_pg_ts_parser_prstoken
Definition: pg_ts_parser.h:54
#define Anum_pg_ts_parser_prsname
Definition: pg_ts_parser.h:51
#define Int32GetDatum(X)
Definition: postgres.h:485
tuple list
Definition: sort-test.py:11
#define VARSIZE_ANY_EXHDR(PTR)
Definition: postgres.h:340
void * palloc(Size size)
Definition: mcxt.c:848
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:493
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
#define TSConfigMapIndexId
Definition: indexing.h:264
#define SQL_STR_DOUBLE(ch, escape_backslash)
Definition: c.h:501
Definition: c.h:433
char * defname
Definition: parsenodes.h:719
#define Anum_pg_ts_template_tmplname
#define Anum_pg_ts_dict_dictname
Definition: pg_ts_dict.h:52
#define elog
Definition: elog.h:219
#define HeapTupleGetOid(tuple)
Definition: htup_details.h:695
#define copyObject(obj)
Definition: nodes.h:623
#define Anum_pg_ts_config_cfgname
Definition: pg_ts_config.h:48
HeapTuple heap_modify_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *replValues, bool *replIsnull, bool *doReplace)
Definition: heaptuple.c:794
Definition: pg_list.h:45
long val
Definition: informix.c:689
static void MakeConfigurationMapping(AlterTSConfigurationStmt *stmt, HeapTuple tup, Relation relMap)
Definition: tsearchcmds.c:1278
#define BTEqualStrategyNumber
Definition: stratnum.h:31
#define Anum_pg_ts_parser_prslextype
Definition: pg_ts_parser.h:57
#define TSTemplateRelationId
void RemoveTSDictionaryById(Oid dictId)
Definition: tsearchcmds.c:501