PostgreSQL Source Code git master
Loading...
Searching...
No Matches
pg_operator.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * pg_operator.c
4 * routines to support manipulation of the pg_operator relation
5 *
6 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
8 *
9 *
10 * IDENTIFICATION
11 * src/backend/catalog/pg_operator.c
12 *
13 * NOTES
14 * these routines moved here from commands/define.c and somewhat cleaned up.
15 *
16 *-------------------------------------------------------------------------
17 */
18#include "postgres.h"
19
20#include "access/htup_details.h"
21#include "access/table.h"
22#include "access/xact.h"
23#include "catalog/catalog.h"
24#include "catalog/dependency.h"
25#include "catalog/indexing.h"
26#include "catalog/namespace.h"
29#include "catalog/pg_operator.h"
30#include "catalog/pg_proc.h"
31#include "catalog/pg_type.h"
32#include "miscadmin.h"
33#include "parser/parse_oper.h"
34#include "utils/acl.h"
35#include "utils/builtins.h"
36#include "utils/lsyscache.h"
37#include "utils/rel.h"
38#include "utils/syscache.h"
39
40
41static Oid OperatorGet(const char *operatorName,
45 bool *defined);
46
47static Oid OperatorShellMake(const char *operatorName,
51
56
57
58/*
59 * Check whether a proposed operator name is legal
60 *
61 * This had better match the behavior of parser/scan.l!
62 *
63 * We need this because the parser is not smart enough to check that
64 * the arguments of CREATE OPERATOR's COMMUTATOR, NEGATOR, etc clauses
65 * are operator names rather than some other lexical entity.
66 */
67static bool
69{
70 size_t len = strlen(name);
71
72 /* Can't be empty or too long */
73 if (len == 0 || len >= NAMEDATALEN)
74 return false;
75
76 /* Can't contain any invalid characters */
77 /* Test string here should match op_chars in scan.l */
78 if (strspn(name, "~!@#^&|`?+-*/%<>=") != len)
79 return false;
80
81 /* Can't contain slash-star or dash-dash (comment starts) */
82 if (strstr(name, "/*") || strstr(name, "--"))
83 return false;
84
85 /*
86 * For SQL standard compatibility, '+' and '-' cannot be the last char of
87 * a multi-char operator unless the operator contains chars that are not
88 * in SQL operators. The idea is to lex '=-' as two operators, but not to
89 * forbid operator names like '?-' that could not be sequences of standard
90 * SQL operators.
91 */
92 if (len > 1 &&
93 (name[len - 1] == '+' ||
94 name[len - 1] == '-'))
95 {
96 int ic;
97
98 for (ic = len - 2; ic >= 0; ic--)
99 {
100 if (strchr("~!@#^&|`?%", name[ic]))
101 break;
102 }
103 if (ic < 0)
104 return false; /* nope, not valid */
105 }
106
107 /* != isn't valid either, because parser will convert it to <> */
108 if (strcmp(name, "!=") == 0)
109 return false;
110
111 return true;
112}
113
114
115/*
116 * OperatorGet
117 *
118 * finds an operator given an exact specification (name, namespace,
119 * left and right type IDs).
120 *
121 * *defined is set true if defined (not a shell)
122 */
123static Oid
154
155/*
156 * OperatorLookup
157 *
158 * looks up an operator given a possibly-qualified name and
159 * left and right type IDs.
160 *
161 * *defined is set true if defined (not a shell)
162 */
163Oid
167 bool *defined)
168{
170 RegProcedure oprcode;
171
174 true, -1);
176 {
177 *defined = false;
178 return InvalidOid;
179 }
180
181 oprcode = get_opcode(operatorObjectId);
182 *defined = RegProcedureIsValid(oprcode);
183
184 return operatorObjectId;
185}
186
187
188/*
189 * OperatorShellMake
190 * Make a "shell" entry for a not-yet-existing operator.
191 */
192static Oid
197{
200 int i;
203 bool nulls[Natts_pg_operator];
205 TupleDesc tupDesc;
206
207 /*
208 * validate operator name
209 */
213 errmsg("\"%s\" is not a valid operator name",
214 operatorName)));
215
216 /*
217 * open pg_operator
218 */
220 tupDesc = pg_operator_desc->rd_att;
221
222 /*
223 * initialize our *nulls and *values arrays
224 */
225 for (i = 0; i < Natts_pg_operator; ++i)
226 {
227 nulls[i] = false;
228 values[i] = (Datum) 0; /* redundant, but safe */
229 }
230
231 /*
232 * initialize values[] with the operator name and input data types. Note
233 * that oprcode is set to InvalidOid, indicating it's a shell.
234 */
253
254 /*
255 * create a new operator tuple
256 */
257 tup = heap_form_tuple(tupDesc, values, nulls);
258
259 /*
260 * insert our "shell" operator tuple
261 */
263
264 /* Add dependencies for the entry */
265 makeOperatorDependencies(tup, true, false);
266
268
269 /* Post creation hook for new shell operator */
271
272 /*
273 * Make sure the tuple is visible for subsequent lookups/updates.
274 */
276
277 /*
278 * close the operator relation and return the oid.
279 */
281
282 return operatorObjectId;
283}
284
285/*
286 * OperatorCreate
287 *
288 * "X" indicates an optional argument (i.e. one that can be NULL or 0)
289 * operatorName name for new operator
290 * operatorNamespace namespace for new operator
291 * leftTypeId X left type ID
292 * rightTypeId X right type ID
293 * procedureId procedure ID for operator
294 * commutatorName X commutator operator
295 * negatorName X negator operator
296 * restrictionId X restriction selectivity procedure ID
297 * joinId X join selectivity procedure ID
298 * canMerge merge join can be used with this operator
299 * canHash hash join can be used with this operator
300 *
301 * The caller should have validated properties and permissions for the
302 * objects passed as OID references. We must handle the commutator and
303 * negator operator references specially, however, since those need not
304 * exist beforehand.
305 *
306 * This routine gets complicated because it allows the user to
307 * specify operators that do not exist. For example, if operator
308 * "op" is being defined, the negator operator "negop" and the
309 * commutator "commop" can also be defined without specifying
310 * any information other than their names. Since in order to
311 * add "op" to the PG_OPERATOR catalog, all the Oid's for these
312 * operators must be placed in the fields of "op", a forward
313 * declaration is done on the commutator and negator operators.
314 * This is called creating a shell, and its main effect is to
315 * create a tuple in the PG_OPERATOR catalog with minimal
316 * information about the operator (just its name and types).
317 * Forward declaration is used only for this purpose, it is
318 * not available to the user as it is for type definition.
319 */
329 Oid joinId,
330 bool canMerge,
331 bool canHash)
332{
335 bool isUpdate;
336 bool nulls[Natts_pg_operator];
343 negatorId;
344 bool selfCommutator = false;
346 int i;
347 ObjectAddress address;
348
349 /*
350 * Sanity checks
351 */
355 errmsg("\"%s\" is not a valid operator name",
356 operatorName)));
357
359
364 negatorName != NIL,
367 canMerge,
368 canHash);
369
375
379 errmsg("operator %s already exists",
380 operatorName)));
381
382 /*
383 * At this point, if operatorObjectId is not InvalidOid then we are
384 * filling in a previously-created shell. Insist that the user own any
385 * such shell.
386 */
391
392 /*
393 * Set up the other operators. If they do not currently exist, create
394 * shells in order to get ObjectId's.
395 */
396
397 if (commutatorName)
398 {
399 /* commutator has reversed arg types */
404
405 /* Permission check: must own other operator */
410
411 /*
412 * If self-linkage to the new operator is requested, we'll fix it
413 * below. (In case of self-linkage to an existing shell operator, we
414 * need do nothing special.)
415 */
417 selfCommutator = true;
418 }
419 else
421
422 if (negatorName)
423 {
424 /* negator has same arg types */
429
430 /* Permission check: must own other operator */
431 if (OidIsValid(negatorId) &&
435
436 /*
437 * Prevent self negation, as it doesn't make sense. It's self
438 * negation if result is InvalidOid (negator would be the same
439 * operator but it doesn't exist yet) or operatorObjectId (we are
440 * replacing a shell that would need to be its own negator).
441 */
445 errmsg("operator cannot be its own negator")));
446 }
447 else
449
450 /*
451 * set up values in the operator tuple
452 */
453
454 for (i = 0; i < Natts_pg_operator; ++i)
455 {
456 values[i] = (Datum) 0;
457 replaces[i] = true;
458 nulls[i] = false;
459 }
460
476
478
479 /*
480 * If we are replacing an operator shell, update; else insert
481 */
483 {
484 isUpdate = true;
485
488 if (!HeapTupleIsValid(tup))
489 elog(ERROR, "cache lookup failed for operator %u",
491
492 replaces[Anum_pg_operator_oid - 1] = false;
495 values,
496 nulls,
497 replaces);
498
500 }
501 else
502 {
503 isUpdate = false;
504
509
511 values, nulls);
512
514 }
515
516 /* Add dependencies for the entry */
517 address = makeOperatorDependencies(tup, true, isUpdate);
518
519 /*
520 * If a commutator and/or negator link is provided, update the other
521 * operator(s) to point at this one, if they don't already have a link.
522 * This supports an alternative style of operator definition wherein the
523 * user first defines one operator without giving negator or commutator,
524 * then defines the other operator of the pair with the proper commutator
525 * or negator attribute. That style doesn't require creation of a shell,
526 * and it's the only style that worked right before Postgres version 6.5.
527 * This code also takes care of the situation where the new operator is
528 * its own commutator.
529 */
530 if (selfCommutator)
532
535
536 /* Post creation hook for new operator */
538
540
541 return address;
542}
543
544/*
545 * OperatorValidateParams
546 *
547 * Check that an operator with argument types leftTypeId and rightTypeId,
548 * returning operResultType, can have the attributes that are set to true.
549 * Raise an error for any disallowed attribute.
550 *
551 * Note: in ALTER OPERATOR, we only bother to pass "true" for attributes
552 * the command is trying to set, not those that may already be set.
553 * This is OK as long as the attribute checks are independent.
554 */
555void
559 bool hasCommutator,
560 bool hasNegator,
563 bool canMerge,
564 bool canHash)
565{
567 {
568 /* If it's not a binary op, these things mustn't be set: */
569 if (hasCommutator)
572 errmsg("only binary operators can have commutators")));
576 errmsg("only binary operators can have join selectivity")));
577 if (canMerge)
580 errmsg("only binary operators can merge join")));
581 if (canHash)
584 errmsg("only binary operators can hash")));
585 }
586
587 if (operResultType != BOOLOID)
588 {
589 /* If it's not a boolean op, these things mustn't be set: */
590 if (hasNegator)
593 errmsg("only boolean operators can have negators")));
597 errmsg("only boolean operators can have restriction selectivity")));
601 errmsg("only boolean operators can have join selectivity")));
602 if (canMerge)
605 errmsg("only boolean operators can merge join")));
606 if (canHash)
609 errmsg("only boolean operators can hash")));
610 }
611}
612
613/*
614 * Try to lookup another operator (commutator, etc); return its OID
615 *
616 * If not found, check to see if it would be the same operator we are trying
617 * to define; if so, return InvalidOid. (Caller must decide whether
618 * that is sensible.) If it is not the same operator, create a shell
619 * operator.
620 */
621static Oid
625{
627 bool otherDefined;
628 char *otherName;
631
635 &otherDefined);
636
638 {
639 /* other op already in catalogs */
640 return other_oid;
641 }
642
644 &otherName);
645
646 if (strcmp(otherName, operatorName) == 0 &&
650 {
651 /* self-linkage to new operator; caller must handle this */
652 return InvalidOid;
653 }
654
655 /* not in catalogs, different from operator, so make shell */
656
658 ACL_CREATE);
659 if (aclresult != ACLCHECK_OK)
662
667 return other_oid;
668}
669
670/*
671 * OperatorUpd
672 *
673 * For a given operator, look up its negator and commutator operators.
674 * When isDelete is false, update their negator and commutator fields to
675 * point back to the given operator; when isDelete is true, update those
676 * fields to be InvalidOid.
677 *
678 * The !isDelete case solves a problem for users who need to insert two new
679 * operators that are the negator or commutator of each other, while the
680 * isDelete case is needed so as not to leave dangling OID links behind
681 * after dropping an operator.
682 */
683void
685{
688
689 /*
690 * If we're making an operator into its own commutator, then we need a
691 * command-counter increment here, since we've just inserted the tuple
692 * we're about to update. But when we're dropping an operator, we can
693 * skip this because we're at the beginning of the command.
694 */
695 if (!isDelete)
697
698 /* Open the relation. */
700
701 /* Get a writable copy of the commutator's tuple. */
702 if (OidIsValid(commId))
704 else
705 tup = NULL;
706
707 /* Update the commutator's tuple if need be. */
709 {
711 bool update_commutator = false;
712
713 /*
714 * We can skip doing anything if the commutator's oprcom field is
715 * already what we want. While that's not expected in the isDelete
716 * case, it's perfectly possible when filling in a shell operator.
717 */
718 if (isDelete && OidIsValid(t->oprcom))
719 {
720 t->oprcom = InvalidOid;
721 update_commutator = true;
722 }
723 else if (!isDelete && t->oprcom != baseId)
724 {
725 /*
726 * If commutator's oprcom field is already set to point to some
727 * third operator, it's an error. Changing its link would be
728 * unsafe, and letting the inconsistency stand would not be good
729 * either. This might be indicative of catalog corruption, so
730 * don't assume t->oprcom is necessarily a valid operator.
731 */
732 if (OidIsValid(t->oprcom))
733 {
734 char *thirdop = get_opname(t->oprcom);
735
736 if (thirdop != NULL)
739 errmsg("commutator operator %s is already the commutator of operator %s",
740 NameStr(t->oprname), thirdop)));
741 else
744 errmsg("commutator operator %s is already the commutator of operator %u",
745 NameStr(t->oprname), t->oprcom)));
746 }
747
748 t->oprcom = baseId;
749 update_commutator = true;
750 }
751
752 /* If any columns were found to need modification, update tuple. */
754 {
756
757 /*
758 * Do CCI to make the updated tuple visible. We must do this in
759 * case the commutator is also the negator. (Which would be a
760 * logic error on the operator definer's part, but that's not a
761 * good reason to fail here.) We would need a CCI anyway in the
762 * deletion case for a self-commutator with no negator.
763 */
765 }
766 }
767
768 /*
769 * Similarly find and update the negator, if any.
770 */
771 if (OidIsValid(negId))
773 else
774 tup = NULL;
775
777 {
779 bool update_negator = false;
780
781 /*
782 * We can skip doing anything if the negator's oprnegate field is
783 * already what we want. While that's not expected in the isDelete
784 * case, it's perfectly possible when filling in a shell operator.
785 */
786 if (isDelete && OidIsValid(t->oprnegate))
787 {
788 t->oprnegate = InvalidOid;
789 update_negator = true;
790 }
791 else if (!isDelete && t->oprnegate != baseId)
792 {
793 /*
794 * If negator's oprnegate field is already set to point to some
795 * third operator, it's an error. Changing its link would be
796 * unsafe, and letting the inconsistency stand would not be good
797 * either. This might be indicative of catalog corruption, so
798 * don't assume t->oprnegate is necessarily a valid operator.
799 */
800 if (OidIsValid(t->oprnegate))
801 {
802 char *thirdop = get_opname(t->oprnegate);
803
804 if (thirdop != NULL)
807 errmsg("negator operator %s is already the negator of operator %s",
808 NameStr(t->oprname), thirdop)));
809 else
812 errmsg("negator operator %s is already the negator of operator %u",
813 NameStr(t->oprname), t->oprnegate)));
814 }
815
816 t->oprnegate = baseId;
817 update_negator = true;
818 }
819
820 /* If any columns were found to need modification, update tuple. */
821 if (update_negator)
822 {
824
825 /*
826 * In the deletion case, do CCI to make the updated tuple visible.
827 * We must do this in case the operator is its own negator. (Which
828 * would be a logic error on the operator definer's part, but
829 * that's not a good reason to fail here.)
830 */
831 if (isDelete)
833 }
834 }
835
836 /* Close relation and release catalog lock. */
838}
839
840/*
841 * Create dependencies for an operator (either a freshly inserted
842 * complete operator, a new shell operator, a just-updated shell,
843 * or an operator that's being modified by ALTER OPERATOR).
844 *
845 * makeExtensionDep should be true when making a new operator or
846 * replacing a shell, false for ALTER OPERATOR. Passing false
847 * will prevent any change in the operator's extension membership.
848 *
849 * NB: the OidIsValid tests in this routine are necessary, in case
850 * the given operator is a shell.
851 */
854 bool makeExtensionDep,
855 bool isUpdate)
856{
860 ObjectAddresses *addrs;
861
863
864 /*
865 * If we are updating the operator, delete any existing entries, except
866 * for extension membership which should remain the same.
867 */
868 if (isUpdate)
869 {
870 deleteDependencyRecordsFor(myself.classId, myself.objectId, true);
871 deleteSharedDependencyRecordsFor(myself.classId, myself.objectId, 0);
872 }
873
874 addrs = new_object_addresses();
875
876 /* Dependency on namespace */
877 if (OidIsValid(oper->oprnamespace))
878 {
881 }
882
883 /* Dependency on left type */
884 if (OidIsValid(oper->oprleft))
885 {
888 }
889
890 /* Dependency on right type */
891 if (OidIsValid(oper->oprright))
892 {
895 }
896
897 /* Dependency on result type */
898 if (OidIsValid(oper->oprresult))
899 {
902 }
903
904 /*
905 * NOTE: we do not consider the operator to depend on the associated
906 * operators oprcom and oprnegate. We do not want to delete this operator
907 * if those go away, but only reset the link fields; which is not a
908 * function that the dependency logic can handle. (It's taken care of
909 * manually within RemoveOperatorById, instead.)
910 */
911
912 /* Dependency on implementation function */
913 if (OidIsValid(oper->oprcode))
914 {
917 }
918
919 /* Dependency on restriction selectivity function */
920 if (OidIsValid(oper->oprrest))
921 {
924 }
925
926 /* Dependency on join selectivity function */
927 if (OidIsValid(oper->oprjoin))
928 {
931 }
932
935
936 /* Dependency on owner */
938 oper->oprowner);
939
940 /* Dependency on extension */
943
944 return myself;
945}
AclResult
Definition acl.h:182
@ ACLCHECK_OK
Definition acl.h:183
@ ACLCHECK_NOT_OWNER
Definition acl.h:185
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition aclchk.c:2654
AclResult object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
Definition aclchk.c:3836
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition aclchk.c:4090
static Datum values[MAXATTR]
Definition bootstrap.c:155
#define NameStr(name)
Definition c.h:765
#define RegProcedureIsValid(p)
Definition c.h:792
regproc RegProcedure
Definition c.h:664
#define OidIsValid(objectId)
Definition c.h:788
Oid GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn)
Definition catalog.c:448
void record_object_address_dependencies(const ObjectAddress *depender, ObjectAddresses *referenced, DependencyType behavior)
void add_exact_object_address(const ObjectAddress *object, ObjectAddresses *addrs)
ObjectAddresses * new_object_addresses(void)
void free_object_addresses(ObjectAddresses *addrs)
@ DEPENDENCY_NORMAL
Definition dependency.h:33
int errcode(int sqlerrcode)
Definition elog.c:863
int errmsg(const char *fmt,...)
Definition elog.c:1080
#define ERROR
Definition elog.h:39
#define elog(elevel,...)
Definition elog.h:226
#define ereport(elevel,...)
Definition elog.h:150
HeapTuple heap_modify_tuple(HeapTuple tuple, TupleDesc tupleDesc, const Datum *replValues, const bool *replIsnull, const bool *doReplace)
Definition heaptuple.c:1210
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
Definition heaptuple.c:1117
void heap_freetuple(HeapTuple htup)
Definition heaptuple.c:1435
#define HeapTupleIsValid(tuple)
Definition htup.h:78
static void * GETSTRUCT(const HeapTupleData *tuple)
void CatalogTupleUpdate(Relation heapRel, const ItemPointerData *otid, HeapTuple tup)
Definition indexing.c:313
void CatalogTupleInsert(Relation heapRel, HeapTuple tup)
Definition indexing.c:233
int i
Definition isn.c:77
#define RowExclusiveLock
Definition lockdefs.h:38
char * get_opname(Oid opno)
Definition lsyscache.c:1460
RegProcedure get_opcode(Oid opno)
Definition lsyscache.c:1435
char * get_namespace_name(Oid nspid)
Definition lsyscache.c:3516
Oid get_func_rettype(Oid funcid)
Definition lsyscache.c:1805
Oid GetUserId(void)
Definition miscinit.c:469
void namestrcpy(Name name, const char *str)
Definition name.c:233
char * NameListToString(const List *names)
Definition namespace.c:3664
Oid QualifiedNameGetCreationNamespace(const List *names, char **objname_p)
Definition namespace.c:3557
#define InvokeObjectPostCreateHook(classId, objectId, subId)
#define ObjectAddressSet(addr, class_id, object_id)
Oid LookupOperName(ParseState *pstate, List *opername, Oid oprleft, Oid oprright, bool noError, int location)
Definition parse_oper.c:100
Operator oper(ParseState *pstate, List *opname, Oid ltypeId, Oid rtypeId, bool noError, int location)
Definition parse_oper.c:371
@ OBJECT_SCHEMA
@ OBJECT_OPERATOR
#define ACL_CREATE
Definition parsenodes.h:85
#define NAMEDATALEN
const void size_t len
long deleteDependencyRecordsFor(Oid classId, Oid objectId, bool skipExtensionDeps)
Definition pg_depend.c:301
void recordDependencyOnCurrentExtension(const ObjectAddress *object, bool isReplace)
Definition pg_depend.c:193
#define NIL
Definition pg_list.h:68
static Oid get_other_operator(List *otherOp, Oid otherLeftTypeId, Oid otherRightTypeId, const char *operatorName, Oid operatorNamespace, Oid leftTypeId, Oid rightTypeId)
static Oid OperatorShellMake(const char *operatorName, Oid operatorNamespace, Oid leftTypeId, Oid rightTypeId)
static Oid OperatorGet(const char *operatorName, Oid operatorNamespace, Oid leftObjectId, Oid rightObjectId, bool *defined)
static bool validOperatorName(const char *name)
Definition pg_operator.c:68
ObjectAddress OperatorCreate(const char *operatorName, Oid operatorNamespace, Oid leftTypeId, Oid rightTypeId, Oid procedureId, List *commutatorName, List *negatorName, Oid restrictionId, Oid joinId, bool canMerge, bool canHash)
void OperatorValidateParams(Oid leftTypeId, Oid rightTypeId, Oid operResultType, bool hasCommutator, bool hasNegator, bool hasRestrictionSelectivity, bool hasJoinSelectivity, bool canMerge, bool canHash)
Oid OperatorLookup(List *operatorName, Oid leftObjectId, Oid rightObjectId, bool *defined)
ObjectAddress makeOperatorDependencies(HeapTuple tuple, bool makeExtensionDep, bool isUpdate)
void OperatorUpd(Oid baseId, Oid commId, Oid negId, bool isDelete)
FormData_pg_operator * Form_pg_operator
Definition pg_operator.h:83
void deleteSharedDependencyRecordsFor(Oid classId, Oid objectId, int32 objectSubId)
void recordDependencyOnOwner(Oid classId, Oid objectId, Oid owner)
static Datum PointerGetDatum(const void *X)
Definition postgres.h:352
static Datum BoolGetDatum(bool X)
Definition postgres.h:112
static Datum ObjectIdGetDatum(Oid X)
Definition postgres.h:262
static Datum NameGetDatum(const NameData *X)
Definition postgres.h:403
uint64_t Datum
Definition postgres.h:70
static Datum CharGetDatum(char X)
Definition postgres.h:132
#define InvalidOid
unsigned int Oid
static int fb(int x)
#define RelationGetDescr(relation)
Definition rel.h:540
Definition pg_list.h:54
Definition c.h:760
void ReleaseSysCache(HeapTuple tuple)
Definition syscache.c:264
HeapTuple SearchSysCache4(int cacheId, Datum key1, Datum key2, Datum key3, Datum key4)
Definition syscache.c:250
#define SearchSysCacheCopy1(cacheId, key1)
Definition syscache.h:91
void table_close(Relation relation, LOCKMODE lockmode)
Definition table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition table.c:40
const char * name
void CommandCounterIncrement(void)
Definition xact.c:1101