📄 pg_operator.c
字号:
/*------------------------------------------------------------------------- * * pg_operator.c * routines to support manipulation of the pg_operator relation * * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $PostgreSQL: pgsql/src/backend/catalog/pg_operator.c,v 1.94 2005/10/15 02:49:14 momjian Exp $ * * NOTES * these routines moved here from commands/define.c and somewhat cleaned up. * *------------------------------------------------------------------------- */#include "postgres.h"#include "access/heapam.h"#include "catalog/dependency.h"#include "catalog/indexing.h"#include "catalog/namespace.h"#include "catalog/pg_namespace.h"#include "catalog/pg_operator.h"#include "catalog/pg_proc.h"#include "catalog/pg_type.h"#include "miscadmin.h"#include "parser/parse_func.h"#include "parser/parse_oper.h"#include "utils/acl.h"#include "utils/builtins.h"#include "utils/lsyscache.h"#include "utils/syscache.h"static Oid OperatorGet(const char *operatorName, Oid operatorNamespace, Oid leftObjectId, Oid rightObjectId, bool *defined);static Oid OperatorLookup(List *operatorName, Oid leftObjectId, Oid rightObjectId, bool *defined);static Oid OperatorShellMake(const char *operatorName, Oid operatorNamespace, Oid leftTypeId, Oid rightTypeId);static void OperatorUpd(Oid baseId, Oid commId, Oid negId);static Oid get_other_operator(List *otherOp, Oid otherLeftTypeId, Oid otherRightTypeId, const char *operatorName, Oid operatorNamespace, Oid leftTypeId, Oid rightTypeId, bool isCommutator);static void makeOperatorDependencies(HeapTuple tuple);/* * Check whether a proposed operator name is legal * * This had better match the behavior of parser/scan.l! * * We need this because the parser is not smart enough to check that * the arguments of CREATE OPERATOR's COMMUTATOR, NEGATOR, etc clauses * are operator names rather than some other lexical entity. */static boolvalidOperatorName(const char *name){ size_t len = strlen(name); /* Can't be empty or too long */ if (len == 0 || len >= NAMEDATALEN) return false; /* Can't contain any invalid characters */ /* Test string here should match op_chars in scan.l */ if (strspn(name, "~!@#^&|`?+-*/%<>=") != len) return false; /* Can't contain slash-star or dash-dash (comment starts) */ if (strstr(name, "/*") || strstr(name, "--")) return false; /* * For SQL92 compatibility, '+' and '-' cannot be the last char of a * multi-char operator unless the operator contains chars that are not in * SQL92 operators. The idea is to lex '=-' as two operators, but not to * forbid operator names like '?-' that could not be sequences of SQL92 * operators. */ if (len > 1 && (name[len - 1] == '+' || name[len - 1] == '-')) { int ic; for (ic = len - 2; ic >= 0; ic--) { if (strchr("~!@#^&|`?%", name[ic])) break; } if (ic < 0) return false; /* nope, not valid */ } /* != isn't valid either, because parser will convert it to <> */ if (strcmp(name, "!=") == 0) return false; return true;}/* * OperatorGet * * finds an operator given an exact specification (name, namespace, * left and right type IDs). * * *defined is set TRUE if defined (not a shell) */static OidOperatorGet(const char *operatorName, Oid operatorNamespace, Oid leftObjectId, Oid rightObjectId, bool *defined){ HeapTuple tup; Oid operatorObjectId; tup = SearchSysCache(OPERNAMENSP, PointerGetDatum(operatorName), ObjectIdGetDatum(leftObjectId), ObjectIdGetDatum(rightObjectId), ObjectIdGetDatum(operatorNamespace)); if (HeapTupleIsValid(tup)) { RegProcedure oprcode = ((Form_pg_operator) GETSTRUCT(tup))->oprcode; operatorObjectId = HeapTupleGetOid(tup); *defined = RegProcedureIsValid(oprcode); ReleaseSysCache(tup); } else { operatorObjectId = InvalidOid; *defined = false; } return operatorObjectId;}/* * OperatorLookup * * looks up an operator given a possibly-qualified name and * left and right type IDs. * * *defined is set TRUE if defined (not a shell) */static OidOperatorLookup(List *operatorName, Oid leftObjectId, Oid rightObjectId, bool *defined){ Oid operatorObjectId; RegProcedure oprcode; operatorObjectId = LookupOperName(operatorName, leftObjectId, rightObjectId, true); if (!OidIsValid(operatorObjectId)) { *defined = false; return InvalidOid; } oprcode = get_opcode(operatorObjectId); *defined = RegProcedureIsValid(oprcode); return operatorObjectId;}/* * OperatorShellMake * Make a "shell" entry for a not-yet-existing operator. */static OidOperatorShellMake(const char *operatorName, Oid operatorNamespace, Oid leftTypeId, Oid rightTypeId){ Relation pg_operator_desc; Oid operatorObjectId; int i; HeapTuple tup; Datum values[Natts_pg_operator]; char nulls[Natts_pg_operator]; NameData oname; TupleDesc tupDesc; /* * validate operator name */ if (!validOperatorName(operatorName)) ereport(ERROR, (errcode(ERRCODE_INVALID_NAME), errmsg("\"%s\" is not a valid operator name", operatorName))); /* * initialize our *nulls and *values arrays */ for (i = 0; i < Natts_pg_operator; ++i) { nulls[i] = ' '; values[i] = (Datum) NULL; /* redundant, but safe */ } /* * initialize values[] with the operator name and input data types. Note * that oprcode is set to InvalidOid, indicating it's a shell. */ i = 0; namestrcpy(&oname, operatorName); values[i++] = NameGetDatum(&oname); /* oprname */ values[i++] = ObjectIdGetDatum(operatorNamespace); /* oprnamespace */ values[i++] = ObjectIdGetDatum(GetUserId()); /* oprowner */ values[i++] = CharGetDatum(leftTypeId ? (rightTypeId ? 'b' : 'r') : 'l'); /* oprkind */ values[i++] = BoolGetDatum(false); /* oprcanhash */ values[i++] = ObjectIdGetDatum(leftTypeId); /* oprleft */ values[i++] = ObjectIdGetDatum(rightTypeId); /* oprright */ values[i++] = ObjectIdGetDatum(InvalidOid); /* oprresult */ values[i++] = ObjectIdGetDatum(InvalidOid); /* oprcom */ values[i++] = ObjectIdGetDatum(InvalidOid); /* oprnegate */ values[i++] = ObjectIdGetDatum(InvalidOid); /* oprlsortop */ values[i++] = ObjectIdGetDatum(InvalidOid); /* oprrsortop */ values[i++] = ObjectIdGetDatum(InvalidOid); /* oprltcmpop */ values[i++] = ObjectIdGetDatum(InvalidOid); /* oprgtcmpop */ values[i++] = ObjectIdGetDatum(InvalidOid); /* oprcode */ values[i++] = ObjectIdGetDatum(InvalidOid); /* oprrest */ values[i++] = ObjectIdGetDatum(InvalidOid); /* oprjoin */ /* * open pg_operator */ pg_operator_desc = heap_open(OperatorRelationId, RowExclusiveLock); tupDesc = pg_operator_desc->rd_att; /* * create a new operator tuple */ tup = heap_formtuple(tupDesc, values, nulls); /* * insert our "shell" operator tuple */ operatorObjectId = simple_heap_insert(pg_operator_desc, tup); CatalogUpdateIndexes(pg_operator_desc, tup); /* Add dependencies for the entry */ makeOperatorDependencies(tup); heap_freetuple(tup); /* * close the operator relation and return the oid. */ heap_close(pg_operator_desc, RowExclusiveLock); return operatorObjectId;}/* * OperatorCreate * * "X" indicates an optional argument (i.e. one that can be NULL or 0) * operatorName name for new operator * operatorNamespace namespace for new operator * leftTypeId X left type ID * rightTypeId X right type ID * procedureName procedure for operator * commutatorName X commutator operator * negatorName X negator operator * restrictionName X restriction sel. procedure * joinName X join sel. procedure * canHash hash join can be used with this operator * leftSortName X left sort operator (for merge join) * rightSortName X right sort operator (for merge join) * ltCompareName X L<R compare operator (for merge join) * gtCompareName X L>R compare operator (for merge join) * * This routine gets complicated because it allows the user to * specify operators that do not exist. For example, if operator * "op" is being defined, the negator operator "negop" and the * commutator "commop" can also be defined without specifying * any information other than their names. Since in order to * add "op" to the PG_OPERATOR catalog, all the Oid's for these * operators must be placed in the fields of "op", a forward * declaration is done on the commutator and negator operators. * This is called creating a shell, and its main effect is to * create a tuple in the PG_OPERATOR catalog with minimal * information about the operator (just its name and types). * Forward declaration is used only for this purpose, it is * not available to the user as it is for type definition. * * Algorithm: * * check if operator already defined * if so, but oprcode is null, save the Oid -- we are filling in a shell * otherwise error * get the attribute types from relation descriptor for pg_operator * assign values to the fields of the operator: * operatorName * owner id (simply the user id of the caller) * operator "kind" either "b" for binary or "l" for left unary * canHash boolean * leftTypeObjectId -- type must already be defined * rightTypeObjectId -- this is optional, enter ObjectId=0 if none specified * resultType -- defer this, since it must be determined from * the pg_procedure catalog * commutatorObjectId -- if this is NULL, enter ObjectId=0 * else if this already exists, enter its ObjectId * else if this does not yet exist, and is not * the same as the main operatorName, then create * a shell and enter the new ObjectId * else if this does not exist but IS the same * name & types as the main operator, set the ObjectId=0. * (We are creating a self-commutating operator.) * The link will be fixed later by OperatorUpd. * negatorObjectId -- same as for commutatorObjectId * leftSortObjectId -- same as for commutatorObjectId * rightSortObjectId -- same as for commutatorObjectId * operatorProcedure -- must access the pg_procedure catalog to get the * ObjectId of the procedure that actually does the operator * actions this is required. Do a lookup to find out the * return type of the procedure * restrictionProcedure -- must access the pg_procedure catalog to get * the ObjectId but this is optional * joinProcedure -- same as restrictionProcedure * now either insert or replace the operator into the pg_operator catalog * if the operator shell is being filled in * access the catalog in order to get a valid buffer * create a tuple using ModifyHeapTuple * get the t_self from the modified tuple and call RelationReplaceHeapTuple * else if a new operator is being created * create a tuple using heap_formtuple * call simple_heap_insert */voidOperatorCreate(const char *operatorName, Oid operatorNamespace, Oid leftTypeId, Oid rightTypeId, List *procedureName, List *commutatorName, List *negatorName, List *restrictionName, List *joinName, bool canHash, List *leftSortName, List *rightSortName, List *ltCompareName, List *gtCompareName){ Relation pg_operator_desc; HeapTuple tup; char nulls[Natts_pg_operator]; char replaces[Natts_pg_operator]; Datum values[Natts_pg_operator]; Oid operatorObjectId; bool operatorAlreadyDefined; Oid procOid; Oid operResultType; Oid commutatorId, negatorId, leftSortId, rightSortId, ltCompareId, gtCompareId, restOid, joinOid; bool selfCommutator = false; Oid typeId[4]; /* only need up to 4 args here */ int nargs; NameData oname; TupleDesc tupDesc; int i; /* * Sanity checks */ if (!validOperatorName(operatorName)) ereport(ERROR, (errcode(ERRCODE_INVALID_NAME), errmsg("\"%s\" is not a valid operator name", operatorName))); if (!OidIsValid(leftTypeId) && !OidIsValid(rightTypeId)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("at least one of leftarg or rightarg must be specified"))); if (!(OidIsValid(leftTypeId) && OidIsValid(rightTypeId))) { /* If it's not a binary op, these things mustn't be set: */ if (commutatorName) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("only binary operators can have commutators"))); if (joinName) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("only binary operators can have join selectivity"))); if (canHash) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("only binary operators can hash"))); if (leftSortName || rightSortName || ltCompareName || gtCompareName) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("only binary operators can merge join"))); } operatorObjectId = OperatorGet(operatorName, operatorNamespace, leftTypeId, rightTypeId, &operatorAlreadyDefined); if (operatorAlreadyDefined) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_FUNCTION), errmsg("operator %s already exists", operatorName))); /* * At this point, if operatorObjectId is not InvalidOid then we are * filling in a previously-created shell. */ /* * Look up registered procedures -- find the return type of procedureName * to place in "result" field. Do this before shells are created so we * don't have to worry about deleting them later. */ if (!OidIsValid(leftTypeId)) { typeId[0] = rightTypeId; nargs = 1; } else if (!OidIsValid(rightTypeId)) { typeId[0] = leftTypeId; nargs = 1; } else { typeId[0] = leftTypeId; typeId[1] = rightTypeId; nargs = 2; } procOid = LookupFuncName(procedureName, nargs, typeId, false); operResultType = get_func_rettype(procOid); /* * find restriction estimator */ if (restrictionName) { typeId[0] = INTERNALOID; /* Query */ typeId[1] = OIDOID; /* operator OID */ typeId[2] = INTERNALOID; /* args list */ typeId[3] = INT4OID; /* varRelid */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -