Add defenses against invalid operator names passed in CREATE OPERATOR
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 22 Oct 2001 19:34:13 +0000 (19:34 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 22 Oct 2001 19:34:13 +0000 (19:34 +0000)
arguments (where the parser doesn't check them already).  Minor code
cleanups too.

src/backend/catalog/pg_operator.c

index a72565e9709d284ed723a72a69cabe149ba8276b..97e0b87b9c9e9d46fc06b245c627c0d7b865bd7d 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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
  *   these routines moved here from commands/define.c and somewhat cleaned up.
@@ -41,11 +41,6 @@ static Oid OperatorGet(char *operatorName,
            char *rightTypeName,
            bool *defined);
 
-static Oid OperatorShellMakeWithOpenRelation(Relation pg_operator_desc,
-                                 char *operatorName,
-                                 Oid leftObjectId,
-                                 Oid rightObjectId);
-
 static Oid OperatorShellMake(char *operatorName,
                  char *leftTypeName,
                  char *rightTypeName);
@@ -66,6 +61,65 @@ static void OperatorDef(char *operatorName,
 
 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
  *
@@ -157,7 +211,6 @@ OperatorGet(char *operatorName,
            bool *defined)
 {
    Relation    pg_operator_desc;
-
    Oid         operatorObjectId;
    Oid         leftObjectId = 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
-OperatorShellMakeWithOpenRelation(Relation pg_operator_desc,
-                                 char *operatorName,
-                                 Oid leftObjectId,
-                                 Oid rightObjectId)
+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;
    int         i;
    HeapTuple   tup;
    Datum       values[Natts_pg_operator];
    char        nulls[Natts_pg_operator];
-   Oid         operatorObjectId;
    NameData    oname;
    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
     */
@@ -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.
     */
    i = 0;
@@ -270,12 +354,10 @@ OperatorShellMakeWithOpenRelation(Relation pg_operator_desc,
     */
    tupDesc = pg_operator_desc->rd_att;
 
-   tup = heap_formtuple(tupDesc,
-                        values,
-                        nulls);
+   tup = heap_formtuple(tupDesc, values, nulls);
 
    /*
-    * insert our "shell" operator tuple and close the relation
+    * insert our "shell" operator tuple
     */
    heap_insert(pg_operator_desc, tup);
    operatorObjectId = tup->t_data->t_oid;
@@ -289,63 +371,8 @@ OperatorShellMakeWithOpenRelation(Relation pg_operator_desc,
        CatalogCloseIndices(Num_pg_operator_indices, idescs);
    }
 
-   /*
-    * free the tuple and return the operator oid
-    */
    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.
     */
@@ -484,6 +511,12 @@ OperatorDef(char *operatorName,
     * 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.
     *