00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 #include "postgres.h"
00017
00018 #include <ctype.h>
00019
00020 #include "access/genam.h"
00021 #include "access/heapam.h"
00022 #include "access/htup_details.h"
00023 #include "access/xact.h"
00024 #include "catalog/dependency.h"
00025 #include "catalog/indexing.h"
00026 #include "catalog/objectaccess.h"
00027 #include "catalog/pg_namespace.h"
00028 #include "catalog/pg_proc.h"
00029 #include "catalog/pg_ts_config.h"
00030 #include "catalog/pg_ts_config_map.h"
00031 #include "catalog/pg_ts_dict.h"
00032 #include "catalog/pg_ts_parser.h"
00033 #include "catalog/pg_ts_template.h"
00034 #include "catalog/pg_type.h"
00035 #include "commands/alter.h"
00036 #include "commands/defrem.h"
00037 #include "miscadmin.h"
00038 #include "nodes/makefuncs.h"
00039 #include "parser/parse_func.h"
00040 #include "tsearch/ts_cache.h"
00041 #include "tsearch/ts_utils.h"
00042 #include "utils/builtins.h"
00043 #include "utils/fmgroids.h"
00044 #include "utils/lsyscache.h"
00045 #include "utils/rel.h"
00046 #include "utils/syscache.h"
00047 #include "utils/tqual.h"
00048
00049
00050 static void MakeConfigurationMapping(AlterTSConfigurationStmt *stmt,
00051 HeapTuple tup, Relation relMap);
00052 static void DropConfigurationMapping(AlterTSConfigurationStmt *stmt,
00053 HeapTuple tup, Relation relMap);
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063 static Datum
00064 get_ts_parser_func(DefElem *defel, int attnum)
00065 {
00066 List *funcName = defGetQualifiedName(defel);
00067 Oid typeId[3];
00068 Oid retTypeId;
00069 int nargs;
00070 Oid procOid;
00071
00072 retTypeId = INTERNALOID;
00073 typeId[0] = INTERNALOID;
00074 switch (attnum)
00075 {
00076 case Anum_pg_ts_parser_prsstart:
00077 nargs = 2;
00078 typeId[1] = INT4OID;
00079 break;
00080 case Anum_pg_ts_parser_prstoken:
00081 nargs = 3;
00082 typeId[1] = INTERNALOID;
00083 typeId[2] = INTERNALOID;
00084 break;
00085 case Anum_pg_ts_parser_prsend:
00086 nargs = 1;
00087 retTypeId = VOIDOID;
00088 break;
00089 case Anum_pg_ts_parser_prsheadline:
00090 nargs = 3;
00091 typeId[1] = INTERNALOID;
00092 typeId[2] = TSQUERYOID;
00093 break;
00094 case Anum_pg_ts_parser_prslextype:
00095 nargs = 1;
00096
00097
00098
00099
00100
00101
00102 break;
00103 default:
00104
00105 elog(ERROR, "unrecognized attribute for text search parser: %d",
00106 attnum);
00107 nargs = 0;
00108 }
00109
00110 procOid = LookupFuncName(funcName, nargs, typeId, false);
00111 if (get_func_rettype(procOid) != retTypeId)
00112 ereport(ERROR,
00113 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
00114 errmsg("function %s should return type %s",
00115 func_signature_string(funcName, nargs, NIL, typeId),
00116 format_type_be(retTypeId))));
00117
00118 return ObjectIdGetDatum(procOid);
00119 }
00120
00121
00122
00123
00124 static void
00125 makeParserDependencies(HeapTuple tuple)
00126 {
00127 Form_pg_ts_parser prs = (Form_pg_ts_parser) GETSTRUCT(tuple);
00128 ObjectAddress myself,
00129 referenced;
00130
00131 myself.classId = TSParserRelationId;
00132 myself.objectId = HeapTupleGetOid(tuple);
00133 myself.objectSubId = 0;
00134
00135
00136 referenced.classId = NamespaceRelationId;
00137 referenced.objectId = prs->prsnamespace;
00138 referenced.objectSubId = 0;
00139 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
00140
00141
00142 recordDependencyOnCurrentExtension(&myself, false);
00143
00144
00145 referenced.classId = ProcedureRelationId;
00146 referenced.objectSubId = 0;
00147
00148 referenced.objectId = prs->prsstart;
00149 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
00150
00151 referenced.objectId = prs->prstoken;
00152 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
00153
00154 referenced.objectId = prs->prsend;
00155 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
00156
00157 referenced.objectId = prs->prslextype;
00158 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
00159
00160 if (OidIsValid(prs->prsheadline))
00161 {
00162 referenced.objectId = prs->prsheadline;
00163 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
00164 }
00165 }
00166
00167
00168
00169
00170 Oid
00171 DefineTSParser(List *names, List *parameters)
00172 {
00173 char *prsname;
00174 ListCell *pl;
00175 Relation prsRel;
00176 HeapTuple tup;
00177 Datum values[Natts_pg_ts_parser];
00178 bool nulls[Natts_pg_ts_parser];
00179 NameData pname;
00180 Oid prsOid;
00181 Oid namespaceoid;
00182
00183 if (!superuser())
00184 ereport(ERROR,
00185 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00186 errmsg("must be superuser to create text search parsers")));
00187
00188
00189 namespaceoid = QualifiedNameGetCreationNamespace(names, &prsname);
00190
00191
00192 memset(values, 0, sizeof(values));
00193 memset(nulls, false, sizeof(nulls));
00194
00195 namestrcpy(&pname, prsname);
00196 values[Anum_pg_ts_parser_prsname - 1] = NameGetDatum(&pname);
00197 values[Anum_pg_ts_parser_prsnamespace - 1] = ObjectIdGetDatum(namespaceoid);
00198
00199
00200
00201
00202 foreach(pl, parameters)
00203 {
00204 DefElem *defel = (DefElem *) lfirst(pl);
00205
00206 if (pg_strcasecmp(defel->defname, "start") == 0)
00207 {
00208 values[Anum_pg_ts_parser_prsstart - 1] =
00209 get_ts_parser_func(defel, Anum_pg_ts_parser_prsstart);
00210 }
00211 else if (pg_strcasecmp(defel->defname, "gettoken") == 0)
00212 {
00213 values[Anum_pg_ts_parser_prstoken - 1] =
00214 get_ts_parser_func(defel, Anum_pg_ts_parser_prstoken);
00215 }
00216 else if (pg_strcasecmp(defel->defname, "end") == 0)
00217 {
00218 values[Anum_pg_ts_parser_prsend - 1] =
00219 get_ts_parser_func(defel, Anum_pg_ts_parser_prsend);
00220 }
00221 else if (pg_strcasecmp(defel->defname, "headline") == 0)
00222 {
00223 values[Anum_pg_ts_parser_prsheadline - 1] =
00224 get_ts_parser_func(defel, Anum_pg_ts_parser_prsheadline);
00225 }
00226 else if (pg_strcasecmp(defel->defname, "lextypes") == 0)
00227 {
00228 values[Anum_pg_ts_parser_prslextype - 1] =
00229 get_ts_parser_func(defel, Anum_pg_ts_parser_prslextype);
00230 }
00231 else
00232 ereport(ERROR,
00233 (errcode(ERRCODE_SYNTAX_ERROR),
00234 errmsg("text search parser parameter \"%s\" not recognized",
00235 defel->defname)));
00236 }
00237
00238
00239
00240
00241 if (!OidIsValid(DatumGetObjectId(values[Anum_pg_ts_parser_prsstart - 1])))
00242 ereport(ERROR,
00243 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
00244 errmsg("text search parser start method is required")));
00245
00246 if (!OidIsValid(DatumGetObjectId(values[Anum_pg_ts_parser_prstoken - 1])))
00247 ereport(ERROR,
00248 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
00249 errmsg("text search parser gettoken method is required")));
00250
00251 if (!OidIsValid(DatumGetObjectId(values[Anum_pg_ts_parser_prsend - 1])))
00252 ereport(ERROR,
00253 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
00254 errmsg("text search parser end method is required")));
00255
00256 if (!OidIsValid(DatumGetObjectId(values[Anum_pg_ts_parser_prslextype - 1])))
00257 ereport(ERROR,
00258 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
00259 errmsg("text search parser lextypes method is required")));
00260
00261
00262
00263
00264 prsRel = heap_open(TSParserRelationId, RowExclusiveLock);
00265
00266 tup = heap_form_tuple(prsRel->rd_att, values, nulls);
00267
00268 prsOid = simple_heap_insert(prsRel, tup);
00269
00270 CatalogUpdateIndexes(prsRel, tup);
00271
00272 makeParserDependencies(tup);
00273
00274
00275 InvokeObjectPostCreateHook(TSParserRelationId, prsOid, 0);
00276
00277 heap_freetuple(tup);
00278
00279 heap_close(prsRel, RowExclusiveLock);
00280
00281 return prsOid;
00282 }
00283
00284
00285
00286
00287 void
00288 RemoveTSParserById(Oid prsId)
00289 {
00290 Relation relation;
00291 HeapTuple tup;
00292
00293 relation = heap_open(TSParserRelationId, RowExclusiveLock);
00294
00295 tup = SearchSysCache1(TSPARSEROID, ObjectIdGetDatum(prsId));
00296
00297 if (!HeapTupleIsValid(tup))
00298 elog(ERROR, "cache lookup failed for text search parser %u", prsId);
00299
00300 simple_heap_delete(relation, &tup->t_self);
00301
00302 ReleaseSysCache(tup);
00303
00304 heap_close(relation, RowExclusiveLock);
00305 }
00306
00307
00308
00309
00310
00311
00312 static void
00313 makeDictionaryDependencies(HeapTuple tuple)
00314 {
00315 Form_pg_ts_dict dict = (Form_pg_ts_dict) GETSTRUCT(tuple);
00316 ObjectAddress myself,
00317 referenced;
00318
00319 myself.classId = TSDictionaryRelationId;
00320 myself.objectId = HeapTupleGetOid(tuple);
00321 myself.objectSubId = 0;
00322
00323
00324 referenced.classId = NamespaceRelationId;
00325 referenced.objectId = dict->dictnamespace;
00326 referenced.objectSubId = 0;
00327 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
00328
00329
00330 recordDependencyOnOwner(myself.classId, myself.objectId, dict->dictowner);
00331
00332
00333 recordDependencyOnCurrentExtension(&myself, false);
00334
00335
00336 referenced.classId = TSTemplateRelationId;
00337 referenced.objectId = dict->dicttemplate;
00338 referenced.objectSubId = 0;
00339 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
00340 }
00341
00342
00343
00344
00345 static void
00346 verify_dictoptions(Oid tmplId, List *dictoptions)
00347 {
00348 HeapTuple tup;
00349 Form_pg_ts_template tform;
00350 Oid initmethod;
00351
00352
00353
00354
00355
00356
00357
00358
00359 if (!IsUnderPostmaster)
00360 return;
00361
00362 tup = SearchSysCache1(TSTEMPLATEOID, ObjectIdGetDatum(tmplId));
00363 if (!HeapTupleIsValid(tup))
00364 elog(ERROR, "cache lookup failed for text search template %u",
00365 tmplId);
00366 tform = (Form_pg_ts_template) GETSTRUCT(tup);
00367
00368 initmethod = tform->tmplinit;
00369
00370 if (!OidIsValid(initmethod))
00371 {
00372
00373 if (dictoptions)
00374 ereport(ERROR,
00375 (errcode(ERRCODE_SYNTAX_ERROR),
00376 errmsg("text search template \"%s\" does not accept options",
00377 NameStr(tform->tmplname))));
00378 }
00379 else
00380 {
00381
00382
00383
00384
00385 dictoptions = copyObject(dictoptions);
00386
00387
00388
00389
00390
00391 (void) OidFunctionCall1(initmethod, PointerGetDatum(dictoptions));
00392 }
00393
00394 ReleaseSysCache(tup);
00395 }
00396
00397
00398
00399
00400 Oid
00401 DefineTSDictionary(List *names, List *parameters)
00402 {
00403 ListCell *pl;
00404 Relation dictRel;
00405 HeapTuple tup;
00406 Datum values[Natts_pg_ts_dict];
00407 bool nulls[Natts_pg_ts_dict];
00408 NameData dname;
00409 Oid templId = InvalidOid;
00410 List *dictoptions = NIL;
00411 Oid dictOid;
00412 Oid namespaceoid;
00413 AclResult aclresult;
00414 char *dictname;
00415
00416
00417 namespaceoid = QualifiedNameGetCreationNamespace(names, &dictname);
00418
00419
00420 aclresult = pg_namespace_aclcheck(namespaceoid, GetUserId(), ACL_CREATE);
00421 if (aclresult != ACLCHECK_OK)
00422 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
00423 get_namespace_name(namespaceoid));
00424
00425
00426
00427
00428 foreach(pl, parameters)
00429 {
00430 DefElem *defel = (DefElem *) lfirst(pl);
00431
00432 if (pg_strcasecmp(defel->defname, "template") == 0)
00433 {
00434 templId = get_ts_template_oid(defGetQualifiedName(defel), false);
00435 }
00436 else
00437 {
00438
00439 dictoptions = lappend(dictoptions, defel);
00440 }
00441 }
00442
00443
00444
00445
00446 if (!OidIsValid(templId))
00447 ereport(ERROR,
00448 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
00449 errmsg("text search template is required")));
00450
00451 verify_dictoptions(templId, dictoptions);
00452
00453
00454
00455
00456 memset(values, 0, sizeof(values));
00457 memset(nulls, false, sizeof(nulls));
00458
00459 namestrcpy(&dname, dictname);
00460 values[Anum_pg_ts_dict_dictname - 1] = NameGetDatum(&dname);
00461 values[Anum_pg_ts_dict_dictnamespace - 1] = ObjectIdGetDatum(namespaceoid);
00462 values[Anum_pg_ts_dict_dictowner - 1] = ObjectIdGetDatum(GetUserId());
00463 values[Anum_pg_ts_dict_dicttemplate - 1] = ObjectIdGetDatum(templId);
00464 if (dictoptions)
00465 values[Anum_pg_ts_dict_dictinitoption - 1] =
00466 PointerGetDatum(serialize_deflist(dictoptions));
00467 else
00468 nulls[Anum_pg_ts_dict_dictinitoption - 1] = true;
00469
00470 dictRel = heap_open(TSDictionaryRelationId, RowExclusiveLock);
00471
00472 tup = heap_form_tuple(dictRel->rd_att, values, nulls);
00473
00474 dictOid = simple_heap_insert(dictRel, tup);
00475
00476 CatalogUpdateIndexes(dictRel, tup);
00477
00478 makeDictionaryDependencies(tup);
00479
00480
00481 InvokeObjectPostCreateHook(TSDictionaryRelationId, dictOid, 0);
00482
00483 heap_freetuple(tup);
00484
00485 heap_close(dictRel, RowExclusiveLock);
00486
00487 return dictOid;
00488 }
00489
00490
00491
00492
00493 void
00494 RemoveTSDictionaryById(Oid dictId)
00495 {
00496 Relation relation;
00497 HeapTuple tup;
00498
00499 relation = heap_open(TSDictionaryRelationId, RowExclusiveLock);
00500
00501 tup = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(dictId));
00502
00503 if (!HeapTupleIsValid(tup))
00504 elog(ERROR, "cache lookup failed for text search dictionary %u",
00505 dictId);
00506
00507 simple_heap_delete(relation, &tup->t_self);
00508
00509 ReleaseSysCache(tup);
00510
00511 heap_close(relation, RowExclusiveLock);
00512 }
00513
00514
00515
00516
00517 Oid
00518 AlterTSDictionary(AlterTSDictionaryStmt *stmt)
00519 {
00520 HeapTuple tup,
00521 newtup;
00522 Relation rel;
00523 Oid dictId;
00524 ListCell *pl;
00525 List *dictoptions;
00526 Datum opt;
00527 bool isnull;
00528 Datum repl_val[Natts_pg_ts_dict];
00529 bool repl_null[Natts_pg_ts_dict];
00530 bool repl_repl[Natts_pg_ts_dict];
00531
00532 dictId = get_ts_dict_oid(stmt->dictname, false);
00533
00534 rel = heap_open(TSDictionaryRelationId, RowExclusiveLock);
00535
00536 tup = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(dictId));
00537
00538 if (!HeapTupleIsValid(tup))
00539 elog(ERROR, "cache lookup failed for text search dictionary %u",
00540 dictId);
00541
00542
00543 if (!pg_ts_dict_ownercheck(dictId, GetUserId()))
00544 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSDICTIONARY,
00545 NameListToString(stmt->dictname));
00546
00547
00548 opt = SysCacheGetAttr(TSDICTOID, tup,
00549 Anum_pg_ts_dict_dictinitoption,
00550 &isnull);
00551 if (isnull)
00552 dictoptions = NIL;
00553 else
00554 dictoptions = deserialize_deflist(opt);
00555
00556
00557
00558
00559 foreach(pl, stmt->options)
00560 {
00561 DefElem *defel = (DefElem *) lfirst(pl);
00562 ListCell *cell;
00563 ListCell *prev;
00564 ListCell *next;
00565
00566
00567
00568
00569 prev = NULL;
00570 for (cell = list_head(dictoptions); cell; cell = next)
00571 {
00572 DefElem *oldel = (DefElem *) lfirst(cell);
00573
00574 next = lnext(cell);
00575 if (pg_strcasecmp(oldel->defname, defel->defname) == 0)
00576 dictoptions = list_delete_cell(dictoptions, cell, prev);
00577 else
00578 prev = cell;
00579 }
00580
00581
00582
00583
00584 if (defel->arg)
00585 dictoptions = lappend(dictoptions, defel);
00586 }
00587
00588
00589
00590
00591 verify_dictoptions(((Form_pg_ts_dict) GETSTRUCT(tup))->dicttemplate,
00592 dictoptions);
00593
00594
00595
00596
00597 memset(repl_val, 0, sizeof(repl_val));
00598 memset(repl_null, false, sizeof(repl_null));
00599 memset(repl_repl, false, sizeof(repl_repl));
00600
00601 if (dictoptions)
00602 repl_val[Anum_pg_ts_dict_dictinitoption - 1] =
00603 PointerGetDatum(serialize_deflist(dictoptions));
00604 else
00605 repl_null[Anum_pg_ts_dict_dictinitoption - 1] = true;
00606 repl_repl[Anum_pg_ts_dict_dictinitoption - 1] = true;
00607
00608 newtup = heap_modify_tuple(tup, RelationGetDescr(rel),
00609 repl_val, repl_null, repl_repl);
00610
00611 simple_heap_update(rel, &newtup->t_self, newtup);
00612
00613 CatalogUpdateIndexes(rel, newtup);
00614
00615 InvokeObjectPostAlterHook(TSDictionaryRelationId, dictId, 0);
00616
00617
00618
00619
00620
00621
00622
00623 heap_freetuple(newtup);
00624 ReleaseSysCache(tup);
00625
00626 heap_close(rel, RowExclusiveLock);
00627
00628 return dictId;
00629 }
00630
00631
00632
00633
00634
00635
00636
00637
00638 static Datum
00639 get_ts_template_func(DefElem *defel, int attnum)
00640 {
00641 List *funcName = defGetQualifiedName(defel);
00642 Oid typeId[4];
00643 Oid retTypeId;
00644 int nargs;
00645 Oid procOid;
00646
00647 retTypeId = INTERNALOID;
00648 typeId[0] = INTERNALOID;
00649 typeId[1] = INTERNALOID;
00650 typeId[2] = INTERNALOID;
00651 typeId[3] = INTERNALOID;
00652 switch (attnum)
00653 {
00654 case Anum_pg_ts_template_tmplinit:
00655 nargs = 1;
00656 break;
00657 case Anum_pg_ts_template_tmpllexize:
00658 nargs = 4;
00659 break;
00660 default:
00661
00662 elog(ERROR, "unrecognized attribute for text search template: %d",
00663 attnum);
00664 nargs = 0;
00665 }
00666
00667 procOid = LookupFuncName(funcName, nargs, typeId, false);
00668 if (get_func_rettype(procOid) != retTypeId)
00669 ereport(ERROR,
00670 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
00671 errmsg("function %s should return type %s",
00672 func_signature_string(funcName, nargs, NIL, typeId),
00673 format_type_be(retTypeId))));
00674
00675 return ObjectIdGetDatum(procOid);
00676 }
00677
00678
00679
00680
00681 static void
00682 makeTSTemplateDependencies(HeapTuple tuple)
00683 {
00684 Form_pg_ts_template tmpl = (Form_pg_ts_template) GETSTRUCT(tuple);
00685 ObjectAddress myself,
00686 referenced;
00687
00688 myself.classId = TSTemplateRelationId;
00689 myself.objectId = HeapTupleGetOid(tuple);
00690 myself.objectSubId = 0;
00691
00692
00693 referenced.classId = NamespaceRelationId;
00694 referenced.objectId = tmpl->tmplnamespace;
00695 referenced.objectSubId = 0;
00696 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
00697
00698
00699 recordDependencyOnCurrentExtension(&myself, false);
00700
00701
00702 referenced.classId = ProcedureRelationId;
00703 referenced.objectSubId = 0;
00704
00705 referenced.objectId = tmpl->tmpllexize;
00706 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
00707
00708 if (OidIsValid(tmpl->tmplinit))
00709 {
00710 referenced.objectId = tmpl->tmplinit;
00711 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
00712 }
00713 }
00714
00715
00716
00717
00718 Oid
00719 DefineTSTemplate(List *names, List *parameters)
00720 {
00721 ListCell *pl;
00722 Relation tmplRel;
00723 HeapTuple tup;
00724 Datum values[Natts_pg_ts_template];
00725 bool nulls[Natts_pg_ts_template];
00726 NameData dname;
00727 int i;
00728 Oid tmplOid;
00729 Oid namespaceoid;
00730 char *tmplname;
00731
00732 if (!superuser())
00733 ereport(ERROR,
00734 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00735 errmsg("must be superuser to create text search templates")));
00736
00737
00738 namespaceoid = QualifiedNameGetCreationNamespace(names, &tmplname);
00739
00740 for (i = 0; i < Natts_pg_ts_template; i++)
00741 {
00742 nulls[i] = false;
00743 values[i] = ObjectIdGetDatum(InvalidOid);
00744 }
00745
00746 namestrcpy(&dname, tmplname);
00747 values[Anum_pg_ts_template_tmplname - 1] = NameGetDatum(&dname);
00748 values[Anum_pg_ts_template_tmplnamespace - 1] = ObjectIdGetDatum(namespaceoid);
00749
00750
00751
00752
00753 foreach(pl, parameters)
00754 {
00755 DefElem *defel = (DefElem *) lfirst(pl);
00756
00757 if (pg_strcasecmp(defel->defname, "init") == 0)
00758 {
00759 values[Anum_pg_ts_template_tmplinit - 1] =
00760 get_ts_template_func(defel, Anum_pg_ts_template_tmplinit);
00761 nulls[Anum_pg_ts_template_tmplinit - 1] = false;
00762 }
00763 else if (pg_strcasecmp(defel->defname, "lexize") == 0)
00764 {
00765 values[Anum_pg_ts_template_tmpllexize - 1] =
00766 get_ts_template_func(defel, Anum_pg_ts_template_tmpllexize);
00767 nulls[Anum_pg_ts_template_tmpllexize - 1] = false;
00768 }
00769 else
00770 ereport(ERROR,
00771 (errcode(ERRCODE_SYNTAX_ERROR),
00772 errmsg("text search template parameter \"%s\" not recognized",
00773 defel->defname)));
00774 }
00775
00776
00777
00778
00779 if (!OidIsValid(DatumGetObjectId(values[Anum_pg_ts_template_tmpllexize - 1])))
00780 ereport(ERROR,
00781 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
00782 errmsg("text search template lexize method is required")));
00783
00784
00785
00786
00787
00788 tmplRel = heap_open(TSTemplateRelationId, RowExclusiveLock);
00789
00790 tup = heap_form_tuple(tmplRel->rd_att, values, nulls);
00791
00792 tmplOid = simple_heap_insert(tmplRel, tup);
00793
00794 CatalogUpdateIndexes(tmplRel, tup);
00795
00796 makeTSTemplateDependencies(tup);
00797
00798
00799 InvokeObjectPostCreateHook(TSTemplateRelationId, tmplOid, 0);
00800
00801 heap_freetuple(tup);
00802
00803 heap_close(tmplRel, RowExclusiveLock);
00804
00805 return tmplOid;
00806 }
00807
00808
00809
00810
00811 void
00812 RemoveTSTemplateById(Oid tmplId)
00813 {
00814 Relation relation;
00815 HeapTuple tup;
00816
00817 relation = heap_open(TSTemplateRelationId, RowExclusiveLock);
00818
00819 tup = SearchSysCache1(TSTEMPLATEOID, ObjectIdGetDatum(tmplId));
00820
00821 if (!HeapTupleIsValid(tup))
00822 elog(ERROR, "cache lookup failed for text search template %u",
00823 tmplId);
00824
00825 simple_heap_delete(relation, &tup->t_self);
00826
00827 ReleaseSysCache(tup);
00828
00829 heap_close(relation, RowExclusiveLock);
00830 }
00831
00832
00833
00834
00835
00836
00837
00838 static HeapTuple
00839 GetTSConfigTuple(List *names)
00840 {
00841 HeapTuple tup;
00842 Oid cfgId;
00843
00844 cfgId = get_ts_config_oid(names, true);
00845 if (!OidIsValid(cfgId))
00846 return NULL;
00847
00848 tup = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(cfgId));
00849
00850 if (!HeapTupleIsValid(tup))
00851 elog(ERROR, "cache lookup failed for text search configuration %u",
00852 cfgId);
00853
00854 return tup;
00855 }
00856
00857
00858
00859
00860
00861
00862
00863 static void
00864 makeConfigurationDependencies(HeapTuple tuple, bool removeOld,
00865 Relation mapRel)
00866 {
00867 Form_pg_ts_config cfg = (Form_pg_ts_config) GETSTRUCT(tuple);
00868 ObjectAddresses *addrs;
00869 ObjectAddress myself,
00870 referenced;
00871
00872 myself.classId = TSConfigRelationId;
00873 myself.objectId = HeapTupleGetOid(tuple);
00874 myself.objectSubId = 0;
00875
00876
00877 if (removeOld)
00878 {
00879 deleteDependencyRecordsFor(myself.classId, myself.objectId, true);
00880 deleteSharedDependencyRecordsFor(myself.classId, myself.objectId, 0);
00881 }
00882
00883
00884
00885
00886
00887
00888 addrs = new_object_addresses();
00889
00890
00891 referenced.classId = NamespaceRelationId;
00892 referenced.objectId = cfg->cfgnamespace;
00893 referenced.objectSubId = 0;
00894 add_exact_object_address(&referenced, addrs);
00895
00896
00897 recordDependencyOnOwner(myself.classId, myself.objectId, cfg->cfgowner);
00898
00899
00900 recordDependencyOnCurrentExtension(&myself, removeOld);
00901
00902
00903 referenced.classId = TSParserRelationId;
00904 referenced.objectId = cfg->cfgparser;
00905 referenced.objectSubId = 0;
00906 add_exact_object_address(&referenced, addrs);
00907
00908
00909 if (mapRel)
00910 {
00911 ScanKeyData skey;
00912 SysScanDesc scan;
00913 HeapTuple maptup;
00914
00915
00916 CommandCounterIncrement();
00917
00918 ScanKeyInit(&skey,
00919 Anum_pg_ts_config_map_mapcfg,
00920 BTEqualStrategyNumber, F_OIDEQ,
00921 ObjectIdGetDatum(myself.objectId));
00922
00923 scan = systable_beginscan(mapRel, TSConfigMapIndexId, true,
00924 SnapshotNow, 1, &skey);
00925
00926 while (HeapTupleIsValid((maptup = systable_getnext(scan))))
00927 {
00928 Form_pg_ts_config_map cfgmap = (Form_pg_ts_config_map) GETSTRUCT(maptup);
00929
00930 referenced.classId = TSDictionaryRelationId;
00931 referenced.objectId = cfgmap->mapdict;
00932 referenced.objectSubId = 0;
00933 add_exact_object_address(&referenced, addrs);
00934 }
00935
00936 systable_endscan(scan);
00937 }
00938
00939
00940 record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
00941
00942 free_object_addresses(addrs);
00943 }
00944
00945
00946
00947
00948 Oid
00949 DefineTSConfiguration(List *names, List *parameters)
00950 {
00951 Relation cfgRel;
00952 Relation mapRel = NULL;
00953 HeapTuple tup;
00954 Datum values[Natts_pg_ts_config];
00955 bool nulls[Natts_pg_ts_config];
00956 AclResult aclresult;
00957 Oid namespaceoid;
00958 char *cfgname;
00959 NameData cname;
00960 Oid sourceOid = InvalidOid;
00961 Oid prsOid = InvalidOid;
00962 Oid cfgOid;
00963 ListCell *pl;
00964
00965
00966 namespaceoid = QualifiedNameGetCreationNamespace(names, &cfgname);
00967
00968
00969 aclresult = pg_namespace_aclcheck(namespaceoid, GetUserId(), ACL_CREATE);
00970 if (aclresult != ACLCHECK_OK)
00971 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
00972 get_namespace_name(namespaceoid));
00973
00974
00975
00976
00977 foreach(pl, parameters)
00978 {
00979 DefElem *defel = (DefElem *) lfirst(pl);
00980
00981 if (pg_strcasecmp(defel->defname, "parser") == 0)
00982 prsOid = get_ts_parser_oid(defGetQualifiedName(defel), false);
00983 else if (pg_strcasecmp(defel->defname, "copy") == 0)
00984 sourceOid = get_ts_config_oid(defGetQualifiedName(defel), false);
00985 else
00986 ereport(ERROR,
00987 (errcode(ERRCODE_SYNTAX_ERROR),
00988 errmsg("text search configuration parameter \"%s\" not recognized",
00989 defel->defname)));
00990 }
00991
00992 if (OidIsValid(sourceOid) && OidIsValid(prsOid))
00993 ereport(ERROR,
00994 (errcode(ERRCODE_SYNTAX_ERROR),
00995 errmsg("cannot specify both PARSER and COPY options")));
00996
00997
00998
00999
01000 if (OidIsValid(sourceOid))
01001 {
01002 Form_pg_ts_config cfg;
01003
01004 tup = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(sourceOid));
01005 if (!HeapTupleIsValid(tup))
01006 elog(ERROR, "cache lookup failed for text search configuration %u",
01007 sourceOid);
01008
01009 cfg = (Form_pg_ts_config) GETSTRUCT(tup);
01010
01011
01012 prsOid = cfg->cfgparser;
01013
01014 ReleaseSysCache(tup);
01015 }
01016
01017
01018
01019
01020 if (!OidIsValid(prsOid))
01021 ereport(ERROR,
01022 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
01023 errmsg("text search parser is required")));
01024
01025
01026
01027
01028 memset(values, 0, sizeof(values));
01029 memset(nulls, false, sizeof(nulls));
01030
01031 namestrcpy(&cname, cfgname);
01032 values[Anum_pg_ts_config_cfgname - 1] = NameGetDatum(&cname);
01033 values[Anum_pg_ts_config_cfgnamespace - 1] = ObjectIdGetDatum(namespaceoid);
01034 values[Anum_pg_ts_config_cfgowner - 1] = ObjectIdGetDatum(GetUserId());
01035 values[Anum_pg_ts_config_cfgparser - 1] = ObjectIdGetDatum(prsOid);
01036
01037 cfgRel = heap_open(TSConfigRelationId, RowExclusiveLock);
01038
01039 tup = heap_form_tuple(cfgRel->rd_att, values, nulls);
01040
01041 cfgOid = simple_heap_insert(cfgRel, tup);
01042
01043 CatalogUpdateIndexes(cfgRel, tup);
01044
01045 if (OidIsValid(sourceOid))
01046 {
01047
01048
01049
01050 ScanKeyData skey;
01051 SysScanDesc scan;
01052 HeapTuple maptup;
01053
01054 mapRel = heap_open(TSConfigMapRelationId, RowExclusiveLock);
01055
01056 ScanKeyInit(&skey,
01057 Anum_pg_ts_config_map_mapcfg,
01058 BTEqualStrategyNumber, F_OIDEQ,
01059 ObjectIdGetDatum(sourceOid));
01060
01061 scan = systable_beginscan(mapRel, TSConfigMapIndexId, true,
01062 SnapshotNow, 1, &skey);
01063
01064 while (HeapTupleIsValid((maptup = systable_getnext(scan))))
01065 {
01066 Form_pg_ts_config_map cfgmap = (Form_pg_ts_config_map) GETSTRUCT(maptup);
01067 HeapTuple newmaptup;
01068 Datum mapvalues[Natts_pg_ts_config_map];
01069 bool mapnulls[Natts_pg_ts_config_map];
01070
01071 memset(mapvalues, 0, sizeof(mapvalues));
01072 memset(mapnulls, false, sizeof(mapnulls));
01073
01074 mapvalues[Anum_pg_ts_config_map_mapcfg - 1] = cfgOid;
01075 mapvalues[Anum_pg_ts_config_map_maptokentype - 1] = cfgmap->maptokentype;
01076 mapvalues[Anum_pg_ts_config_map_mapseqno - 1] = cfgmap->mapseqno;
01077 mapvalues[Anum_pg_ts_config_map_mapdict - 1] = cfgmap->mapdict;
01078
01079 newmaptup = heap_form_tuple(mapRel->rd_att, mapvalues, mapnulls);
01080
01081 simple_heap_insert(mapRel, newmaptup);
01082
01083 CatalogUpdateIndexes(mapRel, newmaptup);
01084
01085 heap_freetuple(newmaptup);
01086 }
01087
01088 systable_endscan(scan);
01089 }
01090
01091 makeConfigurationDependencies(tup, false, mapRel);
01092
01093
01094 InvokeObjectPostCreateHook(TSConfigRelationId, cfgOid, 0);
01095
01096 heap_freetuple(tup);
01097
01098 if (mapRel)
01099 heap_close(mapRel, RowExclusiveLock);
01100 heap_close(cfgRel, RowExclusiveLock);
01101
01102 return cfgOid;
01103 }
01104
01105
01106
01107
01108 void
01109 RemoveTSConfigurationById(Oid cfgId)
01110 {
01111 Relation relCfg,
01112 relMap;
01113 HeapTuple tup;
01114 ScanKeyData skey;
01115 SysScanDesc scan;
01116
01117
01118 relCfg = heap_open(TSConfigRelationId, RowExclusiveLock);
01119
01120 tup = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(cfgId));
01121
01122 if (!HeapTupleIsValid(tup))
01123 elog(ERROR, "cache lookup failed for text search dictionary %u",
01124 cfgId);
01125
01126 simple_heap_delete(relCfg, &tup->t_self);
01127
01128 ReleaseSysCache(tup);
01129
01130 heap_close(relCfg, RowExclusiveLock);
01131
01132
01133 relMap = heap_open(TSConfigMapRelationId, RowExclusiveLock);
01134
01135 ScanKeyInit(&skey,
01136 Anum_pg_ts_config_map_mapcfg,
01137 BTEqualStrategyNumber, F_OIDEQ,
01138 ObjectIdGetDatum(cfgId));
01139
01140 scan = systable_beginscan(relMap, TSConfigMapIndexId, true,
01141 SnapshotNow, 1, &skey);
01142
01143 while (HeapTupleIsValid((tup = systable_getnext(scan))))
01144 {
01145 simple_heap_delete(relMap, &tup->t_self);
01146 }
01147
01148 systable_endscan(scan);
01149
01150 heap_close(relMap, RowExclusiveLock);
01151 }
01152
01153
01154
01155
01156 Oid
01157 AlterTSConfiguration(AlterTSConfigurationStmt *stmt)
01158 {
01159 HeapTuple tup;
01160 Oid cfgId;
01161 Relation relMap;
01162
01163
01164 tup = GetTSConfigTuple(stmt->cfgname);
01165 if (!HeapTupleIsValid(tup))
01166 ereport(ERROR,
01167 (errcode(ERRCODE_UNDEFINED_OBJECT),
01168 errmsg("text search configuration \"%s\" does not exist",
01169 NameListToString(stmt->cfgname))));
01170
01171 cfgId = HeapTupleGetOid(tup);
01172
01173
01174 if (!pg_ts_config_ownercheck(HeapTupleGetOid(tup), GetUserId()))
01175 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSCONFIGURATION,
01176 NameListToString(stmt->cfgname));
01177
01178 relMap = heap_open(TSConfigMapRelationId, RowExclusiveLock);
01179
01180
01181 if (stmt->dicts)
01182 MakeConfigurationMapping(stmt, tup, relMap);
01183 else if (stmt->tokentype)
01184 DropConfigurationMapping(stmt, tup, relMap);
01185
01186
01187 makeConfigurationDependencies(tup, true, relMap);
01188
01189 InvokeObjectPostAlterHook(TSConfigMapRelationId,
01190 HeapTupleGetOid(tup), 0);
01191
01192 heap_close(relMap, RowExclusiveLock);
01193
01194 ReleaseSysCache(tup);
01195
01196 return cfgId;
01197 }
01198
01199
01200
01201
01202 static int *
01203 getTokenTypes(Oid prsId, List *tokennames)
01204 {
01205 TSParserCacheEntry *prs = lookup_ts_parser_cache(prsId);
01206 LexDescr *list;
01207 int *res,
01208 i,
01209 ntoken;
01210 ListCell *tn;
01211
01212 ntoken = list_length(tokennames);
01213 if (ntoken == 0)
01214 return NULL;
01215 res = (int *) palloc(sizeof(int) * ntoken);
01216
01217 if (!OidIsValid(prs->lextypeOid))
01218 elog(ERROR, "method lextype isn't defined for text search parser %u",
01219 prsId);
01220
01221
01222 list = (LexDescr *) DatumGetPointer(OidFunctionCall1(prs->lextypeOid,
01223 (Datum) 0));
01224
01225 i = 0;
01226 foreach(tn, tokennames)
01227 {
01228 Value *val = (Value *) lfirst(tn);
01229 bool found = false;
01230 int j;
01231
01232 j = 0;
01233 while (list && list[j].lexid)
01234 {
01235
01236 if (strcmp(strVal(val), list[j].alias) == 0)
01237 {
01238 res[i] = list[j].lexid;
01239 found = true;
01240 break;
01241 }
01242 j++;
01243 }
01244 if (!found)
01245 ereport(ERROR,
01246 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
01247 errmsg("token type \"%s\" does not exist",
01248 strVal(val))));
01249 i++;
01250 }
01251
01252 return res;
01253 }
01254
01255
01256
01257
01258 static void
01259 MakeConfigurationMapping(AlterTSConfigurationStmt *stmt,
01260 HeapTuple tup, Relation relMap)
01261 {
01262 Oid cfgId = HeapTupleGetOid(tup);
01263 ScanKeyData skey[2];
01264 SysScanDesc scan;
01265 HeapTuple maptup;
01266 int i;
01267 int j;
01268 Oid prsId;
01269 int *tokens,
01270 ntoken;
01271 Oid *dictIds;
01272 int ndict;
01273 ListCell *c;
01274
01275 prsId = ((Form_pg_ts_config) GETSTRUCT(tup))->cfgparser;
01276
01277 tokens = getTokenTypes(prsId, stmt->tokentype);
01278 ntoken = list_length(stmt->tokentype);
01279
01280 if (stmt->override)
01281 {
01282
01283
01284
01285 for (i = 0; i < ntoken; i++)
01286 {
01287 ScanKeyInit(&skey[0],
01288 Anum_pg_ts_config_map_mapcfg,
01289 BTEqualStrategyNumber, F_OIDEQ,
01290 ObjectIdGetDatum(cfgId));
01291 ScanKeyInit(&skey[1],
01292 Anum_pg_ts_config_map_maptokentype,
01293 BTEqualStrategyNumber, F_INT4EQ,
01294 Int32GetDatum(tokens[i]));
01295
01296 scan = systable_beginscan(relMap, TSConfigMapIndexId, true,
01297 SnapshotNow, 2, skey);
01298
01299 while (HeapTupleIsValid((maptup = systable_getnext(scan))))
01300 {
01301 simple_heap_delete(relMap, &maptup->t_self);
01302 }
01303
01304 systable_endscan(scan);
01305 }
01306 }
01307
01308
01309
01310
01311 ndict = list_length(stmt->dicts);
01312 dictIds = (Oid *) palloc(sizeof(Oid) * ndict);
01313 i = 0;
01314 foreach(c, stmt->dicts)
01315 {
01316 List *names = (List *) lfirst(c);
01317
01318 dictIds[i] = get_ts_dict_oid(names, false);
01319 i++;
01320 }
01321
01322 if (stmt->replace)
01323 {
01324
01325
01326
01327 Oid dictOld = dictIds[0],
01328 dictNew = dictIds[1];
01329
01330 ScanKeyInit(&skey[0],
01331 Anum_pg_ts_config_map_mapcfg,
01332 BTEqualStrategyNumber, F_OIDEQ,
01333 ObjectIdGetDatum(cfgId));
01334
01335 scan = systable_beginscan(relMap, TSConfigMapIndexId, true,
01336 SnapshotNow, 1, skey);
01337
01338 while (HeapTupleIsValid((maptup = systable_getnext(scan))))
01339 {
01340 Form_pg_ts_config_map cfgmap = (Form_pg_ts_config_map) GETSTRUCT(maptup);
01341
01342
01343
01344
01345 if (tokens)
01346 {
01347 bool tokmatch = false;
01348
01349 for (j = 0; j < ntoken; j++)
01350 {
01351 if (cfgmap->maptokentype == tokens[j])
01352 {
01353 tokmatch = true;
01354 break;
01355 }
01356 }
01357 if (!tokmatch)
01358 continue;
01359 }
01360
01361
01362
01363
01364 if (cfgmap->mapdict == dictOld)
01365 {
01366 Datum repl_val[Natts_pg_ts_config_map];
01367 bool repl_null[Natts_pg_ts_config_map];
01368 bool repl_repl[Natts_pg_ts_config_map];
01369 HeapTuple newtup;
01370
01371 memset(repl_val, 0, sizeof(repl_val));
01372 memset(repl_null, false, sizeof(repl_null));
01373 memset(repl_repl, false, sizeof(repl_repl));
01374
01375 repl_val[Anum_pg_ts_config_map_mapdict - 1] = ObjectIdGetDatum(dictNew);
01376 repl_repl[Anum_pg_ts_config_map_mapdict - 1] = true;
01377
01378 newtup = heap_modify_tuple(maptup,
01379 RelationGetDescr(relMap),
01380 repl_val, repl_null, repl_repl);
01381 simple_heap_update(relMap, &newtup->t_self, newtup);
01382
01383 CatalogUpdateIndexes(relMap, newtup);
01384 }
01385 }
01386
01387 systable_endscan(scan);
01388 }
01389 else
01390 {
01391
01392
01393
01394 for (i = 0; i < ntoken; i++)
01395 {
01396 for (j = 0; j < ndict; j++)
01397 {
01398 Datum values[Natts_pg_ts_config_map];
01399 bool nulls[Natts_pg_ts_config_map];
01400
01401 memset(nulls, false, sizeof(nulls));
01402 values[Anum_pg_ts_config_map_mapcfg - 1] = ObjectIdGetDatum(cfgId);
01403 values[Anum_pg_ts_config_map_maptokentype - 1] = Int32GetDatum(tokens[i]);
01404 values[Anum_pg_ts_config_map_mapseqno - 1] = Int32GetDatum(j + 1);
01405 values[Anum_pg_ts_config_map_mapdict - 1] = ObjectIdGetDatum(dictIds[j]);
01406
01407 tup = heap_form_tuple(relMap->rd_att, values, nulls);
01408 simple_heap_insert(relMap, tup);
01409 CatalogUpdateIndexes(relMap, tup);
01410
01411 heap_freetuple(tup);
01412 }
01413 }
01414 }
01415 }
01416
01417
01418
01419
01420 static void
01421 DropConfigurationMapping(AlterTSConfigurationStmt *stmt,
01422 HeapTuple tup, Relation relMap)
01423 {
01424 Oid cfgId = HeapTupleGetOid(tup);
01425 ScanKeyData skey[2];
01426 SysScanDesc scan;
01427 HeapTuple maptup;
01428 int i;
01429 Oid prsId;
01430 int *tokens;
01431 ListCell *c;
01432
01433 prsId = ((Form_pg_ts_config) GETSTRUCT(tup))->cfgparser;
01434
01435 tokens = getTokenTypes(prsId, stmt->tokentype);
01436
01437 i = 0;
01438 foreach(c, stmt->tokentype)
01439 {
01440 Value *val = (Value *) lfirst(c);
01441 bool found = false;
01442
01443 ScanKeyInit(&skey[0],
01444 Anum_pg_ts_config_map_mapcfg,
01445 BTEqualStrategyNumber, F_OIDEQ,
01446 ObjectIdGetDatum(cfgId));
01447 ScanKeyInit(&skey[1],
01448 Anum_pg_ts_config_map_maptokentype,
01449 BTEqualStrategyNumber, F_INT4EQ,
01450 Int32GetDatum(tokens[i]));
01451
01452 scan = systable_beginscan(relMap, TSConfigMapIndexId, true,
01453 SnapshotNow, 2, skey);
01454
01455 while (HeapTupleIsValid((maptup = systable_getnext(scan))))
01456 {
01457 simple_heap_delete(relMap, &maptup->t_self);
01458 found = true;
01459 }
01460
01461 systable_endscan(scan);
01462
01463 if (!found)
01464 {
01465 if (!stmt->missing_ok)
01466 {
01467 ereport(ERROR,
01468 (errcode(ERRCODE_UNDEFINED_OBJECT),
01469 errmsg("mapping for token type \"%s\" does not exist",
01470 strVal(val))));
01471 }
01472 else
01473 {
01474 ereport(NOTICE,
01475 (errmsg("mapping for token type \"%s\" does not exist, skipping",
01476 strVal(val))));
01477 }
01478 }
01479
01480 i++;
01481 }
01482 }
01483
01484
01485
01486
01487
01488
01489
01490
01491
01492
01493
01494
01495
01496 text *
01497 serialize_deflist(List *deflist)
01498 {
01499 text *result;
01500 StringInfoData buf;
01501 ListCell *l;
01502
01503 initStringInfo(&buf);
01504
01505 foreach(l, deflist)
01506 {
01507 DefElem *defel = (DefElem *) lfirst(l);
01508 char *val = defGetString(defel);
01509
01510 appendStringInfo(&buf, "%s = ",
01511 quote_identifier(defel->defname));
01512
01513 if (strchr(val, '\\'))
01514 appendStringInfoChar(&buf, ESCAPE_STRING_SYNTAX);
01515 appendStringInfoChar(&buf, '\'');
01516 while (*val)
01517 {
01518 char ch = *val++;
01519
01520 if (SQL_STR_DOUBLE(ch, true))
01521 appendStringInfoChar(&buf, ch);
01522 appendStringInfoChar(&buf, ch);
01523 }
01524 appendStringInfoChar(&buf, '\'');
01525 if (lnext(l) != NULL)
01526 appendStringInfo(&buf, ", ");
01527 }
01528
01529 result = cstring_to_text_with_len(buf.data, buf.len);
01530 pfree(buf.data);
01531 return result;
01532 }
01533
01534
01535
01536
01537
01538
01539
01540
01541 List *
01542 deserialize_deflist(Datum txt)
01543 {
01544 text *in = DatumGetTextP(txt);
01545 List *result = NIL;
01546 int len = VARSIZE(in) - VARHDRSZ;
01547 char *ptr,
01548 *endptr,
01549 *workspace,
01550 *wsptr = NULL,
01551 *startvalue = NULL;
01552 typedef enum
01553 {
01554 CS_WAITKEY,
01555 CS_INKEY,
01556 CS_INQKEY,
01557 CS_WAITEQ,
01558 CS_WAITVALUE,
01559 CS_INSQVALUE,
01560 CS_INDQVALUE,
01561 CS_INWVALUE
01562 } ds_state;
01563 ds_state state = CS_WAITKEY;
01564
01565 workspace = (char *) palloc(len + 1);
01566 ptr = VARDATA(in);
01567 endptr = ptr + len;
01568 for (; ptr < endptr; ptr++)
01569 {
01570 switch (state)
01571 {
01572 case CS_WAITKEY:
01573 if (isspace((unsigned char) *ptr) || *ptr == ',')
01574 continue;
01575 if (*ptr == '"')
01576 {
01577 wsptr = workspace;
01578 state = CS_INQKEY;
01579 }
01580 else
01581 {
01582 wsptr = workspace;
01583 *wsptr++ = *ptr;
01584 state = CS_INKEY;
01585 }
01586 break;
01587 case CS_INKEY:
01588 if (isspace((unsigned char) *ptr))
01589 {
01590 *wsptr++ = '\0';
01591 state = CS_WAITEQ;
01592 }
01593 else if (*ptr == '=')
01594 {
01595 *wsptr++ = '\0';
01596 state = CS_WAITVALUE;
01597 }
01598 else
01599 {
01600 *wsptr++ = *ptr;
01601 }
01602 break;
01603 case CS_INQKEY:
01604 if (*ptr == '"')
01605 {
01606 if (ptr + 1 < endptr && ptr[1] == '"')
01607 {
01608
01609 *wsptr++ = *ptr++;
01610 }
01611 else
01612 {
01613 *wsptr++ = '\0';
01614 state = CS_WAITEQ;
01615 }
01616 }
01617 else
01618 {
01619 *wsptr++ = *ptr;
01620 }
01621 break;
01622 case CS_WAITEQ:
01623 if (*ptr == '=')
01624 state = CS_WAITVALUE;
01625 else if (!isspace((unsigned char) *ptr))
01626 ereport(ERROR,
01627 (errcode(ERRCODE_SYNTAX_ERROR),
01628 errmsg("invalid parameter list format: \"%s\"",
01629 text_to_cstring(in))));
01630 break;
01631 case CS_WAITVALUE:
01632 if (*ptr == '\'')
01633 {
01634 startvalue = wsptr;
01635 state = CS_INSQVALUE;
01636 }
01637 else if (*ptr == 'E' && ptr + 1 < endptr && ptr[1] == '\'')
01638 {
01639 ptr++;
01640 startvalue = wsptr;
01641 state = CS_INSQVALUE;
01642 }
01643 else if (*ptr == '"')
01644 {
01645 startvalue = wsptr;
01646 state = CS_INDQVALUE;
01647 }
01648 else if (!isspace((unsigned char) *ptr))
01649 {
01650 startvalue = wsptr;
01651 *wsptr++ = *ptr;
01652 state = CS_INWVALUE;
01653 }
01654 break;
01655 case CS_INSQVALUE:
01656 if (*ptr == '\'')
01657 {
01658 if (ptr + 1 < endptr && ptr[1] == '\'')
01659 {
01660
01661 *wsptr++ = *ptr++;
01662 }
01663 else
01664 {
01665 *wsptr++ = '\0';
01666 result = lappend(result,
01667 makeDefElem(pstrdup(workspace),
01668 (Node *) makeString(pstrdup(startvalue))));
01669 state = CS_WAITKEY;
01670 }
01671 }
01672 else if (*ptr == '\\')
01673 {
01674 if (ptr + 1 < endptr && ptr[1] == '\\')
01675 {
01676
01677 *wsptr++ = *ptr++;
01678 }
01679 else
01680 *wsptr++ = *ptr;
01681 }
01682 else
01683 {
01684 *wsptr++ = *ptr;
01685 }
01686 break;
01687 case CS_INDQVALUE:
01688 if (*ptr == '"')
01689 {
01690 if (ptr + 1 < endptr && ptr[1] == '"')
01691 {
01692
01693 *wsptr++ = *ptr++;
01694 }
01695 else
01696 {
01697 *wsptr++ = '\0';
01698 result = lappend(result,
01699 makeDefElem(pstrdup(workspace),
01700 (Node *) makeString(pstrdup(startvalue))));
01701 state = CS_WAITKEY;
01702 }
01703 }
01704 else
01705 {
01706 *wsptr++ = *ptr;
01707 }
01708 break;
01709 case CS_INWVALUE:
01710 if (*ptr == ',' || isspace((unsigned char) *ptr))
01711 {
01712 *wsptr++ = '\0';
01713 result = lappend(result,
01714 makeDefElem(pstrdup(workspace),
01715 (Node *) makeString(pstrdup(startvalue))));
01716 state = CS_WAITKEY;
01717 }
01718 else
01719 {
01720 *wsptr++ = *ptr;
01721 }
01722 break;
01723 default:
01724 elog(ERROR, "unrecognized deserialize_deflist state: %d",
01725 state);
01726 }
01727 }
01728
01729 if (state == CS_INWVALUE)
01730 {
01731 *wsptr++ = '\0';
01732 result = lappend(result,
01733 makeDefElem(pstrdup(workspace),
01734 (Node *) makeString(pstrdup(startvalue))));
01735 }
01736 else if (state != CS_WAITKEY)
01737 ereport(ERROR,
01738 (errcode(ERRCODE_SYNTAX_ERROR),
01739 errmsg("invalid parameter list format: \"%s\"",
01740 text_to_cstring(in))));
01741
01742 pfree(workspace);
01743
01744 return result;
01745 }