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