Add defenses against invalid operator names passed in CREATE OPERATOR

arguments (where the parser doesn't check them already).  Minor code
cleanups too.
This commit is contained in:
Tom Lane 2001-10-22 19:34:13 +00:00
parent 970a2d1c91
commit a19f2605ed
1 changed files with 106 additions and 73 deletions

View File

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_operator.c,v 1.61 2001/08/10 15:49:39 petere Exp $ * $Header: /cvsroot/pgsql/src/backend/catalog/pg_operator.c,v 1.62 2001/10/22 19:34:13 tgl Exp $
* *
* NOTES * NOTES
* these routines moved here from commands/define.c and somewhat cleaned up. * these routines moved here from commands/define.c and somewhat cleaned up.
@ -41,11 +41,6 @@ static Oid OperatorGet(char *operatorName,
char *rightTypeName, char *rightTypeName,
bool *defined); bool *defined);
static Oid OperatorShellMakeWithOpenRelation(Relation pg_operator_desc,
char *operatorName,
Oid leftObjectId,
Oid rightObjectId);
static Oid OperatorShellMake(char *operatorName, static Oid OperatorShellMake(char *operatorName,
char *leftTypeName, char *leftTypeName,
char *rightTypeName); char *rightTypeName);
@ -66,6 +61,65 @@ static void OperatorDef(char *operatorName,
static void OperatorUpd(Oid baseId, Oid commId, Oid negId); static void OperatorUpd(Oid baseId, Oid commId, Oid negId);
/*
* 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 bool
validOperatorName(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;
}
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* OperatorGetWithOpenRelation * OperatorGetWithOpenRelation
* *
@ -157,7 +211,6 @@ OperatorGet(char *operatorName,
bool *defined) bool *defined)
{ {
Relation pg_operator_desc; Relation pg_operator_desc;
Oid operatorObjectId; Oid operatorObjectId;
Oid leftObjectId = InvalidOid; Oid leftObjectId = InvalidOid;
Oid rightObjectId = InvalidOid; Oid rightObjectId = InvalidOid;
@ -215,24 +268,55 @@ OperatorGet(char *operatorName,
} }
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* OperatorShellMakeWithOpenRelation * OperatorShellMake
* *
* Specify operator name and left and right type names,
* fill an operator struct with this info and NULL's,
* call heap_insert and return the Oid to the caller.
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
static Oid static Oid
OperatorShellMakeWithOpenRelation(Relation pg_operator_desc, OperatorShellMake(char *operatorName,
char *operatorName, char *leftTypeName,
Oid leftObjectId, char *rightTypeName)
Oid rightObjectId)
{ {
Relation pg_operator_desc;
Oid operatorObjectId;
Oid leftObjectId = InvalidOid;
Oid rightObjectId = InvalidOid;
bool leftDefined = false;
bool rightDefined = false;
int i; int i;
HeapTuple tup; HeapTuple tup;
Datum values[Natts_pg_operator]; Datum values[Natts_pg_operator];
char nulls[Natts_pg_operator]; char nulls[Natts_pg_operator];
Oid operatorObjectId;
NameData oname; NameData oname;
TupleDesc tupDesc; TupleDesc tupDesc;
/*
* validate operator name
*/
if (!validOperatorName(operatorName))
elog(ERROR, "\"%s\" is not a valid operator name", operatorName);
/*
* get the left and right type oid's for this operator
*/
if (leftTypeName)
leftObjectId = TypeGet(leftTypeName, &leftDefined);
if (rightTypeName)
rightObjectId = TypeGet(rightTypeName, &rightDefined);
if (!((OidIsValid(leftObjectId) && leftDefined) ||
(OidIsValid(rightObjectId) && rightDefined)))
elog(ERROR, "OperatorShellMake: the operand types are not valid");
/*
* open pg_operator
*/
pg_operator_desc = heap_openr(OperatorRelationName, RowExclusiveLock);
/* /*
* initialize our *nulls and *values arrays * initialize our *nulls and *values arrays
*/ */
@ -243,7 +327,7 @@ OperatorShellMakeWithOpenRelation(Relation pg_operator_desc,
} }
/* /*
* initialize *values with the operator name and input data types. * initialize values[] with the operator name and input data types.
* Note that oprcode is set to InvalidOid, indicating it's a shell. * Note that oprcode is set to InvalidOid, indicating it's a shell.
*/ */
i = 0; i = 0;
@ -270,12 +354,10 @@ OperatorShellMakeWithOpenRelation(Relation pg_operator_desc,
*/ */
tupDesc = pg_operator_desc->rd_att; tupDesc = pg_operator_desc->rd_att;
tup = heap_formtuple(tupDesc, tup = heap_formtuple(tupDesc, values, nulls);
values,
nulls);
/* /*
* insert our "shell" operator tuple and close the relation * insert our "shell" operator tuple
*/ */
heap_insert(pg_operator_desc, tup); heap_insert(pg_operator_desc, tup);
operatorObjectId = tup->t_data->t_oid; operatorObjectId = tup->t_data->t_oid;
@ -289,63 +371,8 @@ OperatorShellMakeWithOpenRelation(Relation pg_operator_desc,
CatalogCloseIndices(Num_pg_operator_indices, idescs); CatalogCloseIndices(Num_pg_operator_indices, idescs);
} }
/*
* free the tuple and return the operator oid
*/
heap_freetuple(tup); heap_freetuple(tup);
return operatorObjectId;
}
/* ----------------------------------------------------------------
* OperatorShellMake
*
* Specify operator name and left and right type names,
* fill an operator struct with this info and NULL's,
* call heap_insert and return the Oid
* to the caller.
* ----------------------------------------------------------------
*/
static Oid
OperatorShellMake(char *operatorName,
char *leftTypeName,
char *rightTypeName)
{
Relation pg_operator_desc;
Oid operatorObjectId;
Oid leftObjectId = InvalidOid;
Oid rightObjectId = InvalidOid;
bool leftDefined = false;
bool rightDefined = false;
/*
* get the left and right type oid's for this operator
*/
if (leftTypeName)
leftObjectId = TypeGet(leftTypeName, &leftDefined);
if (rightTypeName)
rightObjectId = TypeGet(rightTypeName, &rightDefined);
if (!((OidIsValid(leftObjectId) && leftDefined) ||
(OidIsValid(rightObjectId) && rightDefined)))
elog(ERROR, "OperatorShellMake: the operand types are not valid");
/*
* open pg_operator
*/
pg_operator_desc = heap_openr(OperatorRelationName, RowExclusiveLock);
/*
* add a "shell" operator tuple to the operator relation and recover
* the shell tuple's oid.
*/
operatorObjectId = OperatorShellMakeWithOpenRelation(pg_operator_desc,
operatorName,
leftObjectId,
rightObjectId);
/* /*
* close the operator relation and return the oid. * close the operator relation and return the oid.
*/ */
@ -484,6 +511,12 @@ OperatorDef(char *operatorName,
* filling in a previously-created shell. * filling in a previously-created shell.
*/ */
/*
* validate operator name
*/
if (!validOperatorName(operatorName))
elog(ERROR, "\"%s\" is not a valid operator name", operatorName);
/* /*
* look up the operator data types. * look up the operator data types.
* *