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