Issue explicit error messages for attempts to use "shell" operators in
authorTom Lane
Tue, 22 Apr 2008 01:34:34 +0000 (01:34 +0000)
committerTom Lane
Tue, 22 Apr 2008 01:34:34 +0000 (01:34 +0000)
ordinary expressions.  This probably doesn't catch every single case
where you might get "cache lookup failed for function 0" for use of a
shell operator, but it will catch most.  Per bug #4120 from Pedro Gimeno.

This patch incidentally folds make_op_expr() into its sole remaining
caller --- the alternative was to give it yet more arguments, which
didn't seem an improvement.

src/backend/parser/parse_oper.c

index 6b23fbb9e9a05af761d1b5262d40a1034be4ea3d..54fb63f959d014a7d03269f267d9b6ce8f2e7a0a 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/parser/parse_oper.c,v 1.101 2008/01/11 18:39:41 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/parser/parse_oper.c,v 1.102 2008/04/22 01:34:34 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -75,9 +75,6 @@ static const char *op_signature_string(List *op, char oprkind,
 static void op_error(ParseState *pstate, List *op, char oprkind,
         Oid arg1, Oid arg2,
         FuncDetailCode fdresult, int location);
-static Expr *make_op_expr(ParseState *pstate, Operator op,
-            Node *ltree, Node *rtree,
-            Oid ltypeId, Oid rtypeId);
 static bool make_oper_cache_key(OprCacheKey *key, List *opname,
                                Oid ltypeId, Oid rtypeId);
 static Oid find_oper_cache_entry(OprCacheKey *key);
@@ -913,7 +910,13 @@ make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree,
    Oid         ltypeId,
                rtypeId;
    Operator    tup;
-   Expr       *result;
+   Form_pg_operator opform;
+   Oid         actual_arg_types[2];
+   Oid         declared_arg_types[2];
+   int         nargs;
+   List       *args;
+   Oid         rettype;
+   OpExpr     *result;
 
    /* Select the operator */
    if (rtree == NULL)
@@ -938,12 +941,72 @@ make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree,
        tup = oper(pstate, opname, ltypeId, rtypeId, false, location);
    }
 
+   opform = (Form_pg_operator) GETSTRUCT(tup);
+
+   /* Check it's not a shell */
+   if (!RegProcedureIsValid(opform->oprcode))
+       ereport(ERROR,
+               (errcode(ERRCODE_UNDEFINED_FUNCTION),
+                errmsg("operator is only a shell: %s",
+                       op_signature_string(opname,
+                                           opform->oprkind,
+                                           opform->oprleft,
+                                           opform->oprright)),
+                parser_errposition(pstate, location)));
+
    /* Do typecasting and build the expression tree */
-   result = make_op_expr(pstate, tup, ltree, rtree, ltypeId, rtypeId);
+   if (rtree == NULL)
+   {
+       /* right operator */
+       args = list_make1(ltree);
+       actual_arg_types[0] = ltypeId;
+       declared_arg_types[0] = opform->oprleft;
+       nargs = 1;
+   }
+   else if (ltree == NULL)
+   {
+       /* left operator */
+       args = list_make1(rtree);
+       actual_arg_types[0] = rtypeId;
+       declared_arg_types[0] = opform->oprright;
+       nargs = 1;
+   }
+   else
+   {
+       /* otherwise, binary operator */
+       args = list_make2(ltree, rtree);
+       actual_arg_types[0] = ltypeId;
+       actual_arg_types[1] = rtypeId;
+       declared_arg_types[0] = opform->oprleft;
+       declared_arg_types[1] = opform->oprright;
+       nargs = 2;
+   }
+
+   /*
+    * enforce consistency with polymorphic argument and return types,
+    * possibly adjusting return type or declared_arg_types (which will be
+    * used as the cast destination by make_fn_arguments)
+    */
+   rettype = enforce_generic_type_consistency(actual_arg_types,
+                                              declared_arg_types,
+                                              nargs,
+                                              opform->oprresult,
+                                              false);
+
+   /* perform the necessary typecasting of arguments */
+   make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types);
+
+   /* and build the expression node */
+   result = makeNode(OpExpr);
+   result->opno = oprid(tup);
+   result->opfuncid = opform->oprcode;
+   result->opresulttype = rettype;
+   result->opretset = get_func_retset(opform->oprcode);
+   result->args = args;
 
    ReleaseSysCache(tup);
 
-   return result;
+   return (Expr *) result;
 }
 
 /*
@@ -992,6 +1055,17 @@ make_scalar_array_op(ParseState *pstate, List *opname,
    tup = oper(pstate, opname, ltypeId, rtypeId, false, location);
    opform = (Form_pg_operator) GETSTRUCT(tup);
 
+   /* Check it's not a shell */
+   if (!RegProcedureIsValid(opform->oprcode))
+       ereport(ERROR,
+               (errcode(ERRCODE_UNDEFINED_FUNCTION),
+                errmsg("operator is only a shell: %s",
+                       op_signature_string(opname,
+                                           opform->oprkind,
+                                           opform->oprleft,
+                                           opform->oprright)),
+                parser_errposition(pstate, location)));
+
    args = list_make2(ltree, rtree);
    actual_arg_types[0] = ltypeId;
    actual_arg_types[1] = rtypeId;
@@ -1062,78 +1136,6 @@ make_scalar_array_op(ParseState *pstate, List *opname,
    return (Expr *) result;
 }
 
-/*
- * make_op_expr()
- *     Build operator expression using an already-looked-up operator.
- *
- * As with coerce_type, pstate may be NULL if no special unknown-Param
- * processing is wanted.
- */
-static Expr *
-make_op_expr(ParseState *pstate, Operator op,
-            Node *ltree, Node *rtree,
-            Oid ltypeId, Oid rtypeId)
-{
-   Form_pg_operator opform = (Form_pg_operator) GETSTRUCT(op);
-   Oid         actual_arg_types[2];
-   Oid         declared_arg_types[2];
-   int         nargs;
-   List       *args;
-   Oid         rettype;
-   OpExpr     *result;
-
-   if (rtree == NULL)
-   {
-       /* right operator */
-       args = list_make1(ltree);
-       actual_arg_types[0] = ltypeId;
-       declared_arg_types[0] = opform->oprleft;
-       nargs = 1;
-   }
-   else if (ltree == NULL)
-   {
-       /* left operator */
-       args = list_make1(rtree);
-       actual_arg_types[0] = rtypeId;
-       declared_arg_types[0] = opform->oprright;
-       nargs = 1;
-   }
-   else
-   {
-       /* otherwise, binary operator */
-       args = list_make2(ltree, rtree);
-       actual_arg_types[0] = ltypeId;
-       actual_arg_types[1] = rtypeId;
-       declared_arg_types[0] = opform->oprleft;
-       declared_arg_types[1] = opform->oprright;
-       nargs = 2;
-   }
-
-   /*
-    * enforce consistency with polymorphic argument and return types,
-    * possibly adjusting return type or declared_arg_types (which will be
-    * used as the cast destination by make_fn_arguments)
-    */
-   rettype = enforce_generic_type_consistency(actual_arg_types,
-                                              declared_arg_types,
-                                              nargs,
-                                              opform->oprresult,
-                                              false);
-
-   /* perform the necessary typecasting of arguments */
-   make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types);
-
-   /* and build the expression node */
-   result = makeNode(OpExpr);
-   result->opno = oprid(op);
-   result->opfuncid = opform->oprcode;
-   result->opresulttype = rettype;
-   result->opretset = get_func_retset(opform->oprcode);
-   result->args = args;
-
-   return (Expr *) result;
-}
-
 
 /*
  * Lookaside cache to speed operator lookup.  Possibly this should be in