aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/parser')
-rw-r--r--src/backend/parser/gram.y55
-rw-r--r--src/backend/parser/parse_coerce.c278
-rw-r--r--src/backend/parser/parse_expr.c7
-rw-r--r--src/backend/parser/parse_func.c12
-rw-r--r--src/backend/parser/parse_oper.c12
5 files changed, 218 insertions, 146 deletions
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 1994875bd1c..6951653ce8a 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.293 2002/03/19 12:52:20 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.294 2002/03/20 19:44:21 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -135,8 +135,7 @@ static void doNegateFloat(Value *v);
ClosePortalStmt, ClusterStmt, CommentStmt, ConstraintsSetStmt,
CopyStmt, CreateAsStmt, CreateDomainStmt, CreateGroupStmt, CreatePLangStmt,
CreateSchemaStmt, CreateSeqStmt, CreateStmt, CreateTrigStmt,
- CreateUserStmt, CreatedbStmt, CursorStmt,
- DefineStmt, DeleteStmt,
+ CreateUserStmt, CreatedbStmt, CursorStmt, DefineStmt, DeleteStmt,
DropGroupStmt, DropPLangStmt, DropSchemaStmt, DropStmt, DropTrigStmt,
DropUserStmt, DropdbStmt, ExplainStmt, FetchStmt,
GrantStmt, IndexStmt, InsertStmt, ListenStmt, LoadStmt, LockStmt,
@@ -151,7 +150,7 @@ static void doNegateFloat(Value *v);
simple_select
%type <node> alter_column_default
-%type <ival> drop_behavior
+%type <ival> drop_behavior, opt_drop_behavior
%type <list> createdb_opt_list, createdb_opt_item
%type <boolean> opt_equal
@@ -1181,6 +1180,10 @@ drop_behavior: CASCADE { $$ = CASCADE; }
| RESTRICT { $$ = RESTRICT; }
;
+opt_drop_behavior: CASCADE { $$ = CASCADE; }
+ | RESTRICT { $$ = RESTRICT; }
+ | /* EMPTY */ { $$ = RESTRICT; /* default */ }
+ ;
/*****************************************************************************
@@ -2030,22 +2033,13 @@ def_list: def_elem { $$ = makeList1($1); }
| def_list ',' def_elem { $$ = lappend($1, $3); }
;
-def_elem: DEFAULT '=' b_expr
- {
- $$ = makeNode(DefElem);
- $$->defname = "default";
- if (exprIsNullConstant($3))
- $$->arg = (Node *)NULL;
- else
- $$->arg = $3;
- }
- | ColId '=' def_arg
+def_elem: ColLabel '=' def_arg
{
$$ = makeNode(DefElem);
$$->defname = $1;
$$->arg = (Node *)$3;
}
- | ColId
+ | ColLabel
{
$$ = makeNode(DefElem);
$$->defname = $1;
@@ -2069,19 +2063,11 @@ def_arg: func_return { $$ = (Node *)$1; }
*
*****************************************************************************/
-DropStmt: DROP drop_type name_list
+DropStmt: DROP drop_type name_list opt_drop_behavior
{
DropStmt *n = makeNode(DropStmt);
n->removeType = $2;
n->names = $3;
- n->behavior = RESTRICT; /* Restricted by default */
- $$ = (Node *)n;
- }
- | DROP DOMAIN_P name_list drop_behavior
- {
- DropStmt *n = makeNode(DropStmt);
- n->removeType = DROP_DOMAIN_P;
- n->names = $3;
n->behavior = $4;
$$ = (Node *)n;
}
@@ -2092,7 +2078,8 @@ drop_type: TABLE { $$ = DROP_TABLE; }
| VIEW { $$ = DROP_VIEW; }
| INDEX { $$ = DROP_INDEX; }
| RULE { $$ = DROP_RULE; }
- | TYPE_P { $$ = DROP_TYPE_P; }
+ | TYPE_P { $$ = DROP_TYPE; }
+ | DOMAIN_P { $$ = DROP_DOMAIN; }
;
/*****************************************************************************
@@ -3194,12 +3181,19 @@ createdb_opt_item: LOCATION opt_equal Sconst
}
;
+/*
+ * Though the equals sign doesn't match other WITH options, pg_dump uses
+ * equals for backward compability, and it doesn't seem worth remove it.
+ */
+opt_equal: '=' { $$ = TRUE; }
+ | /*EMPTY*/ { $$ = FALSE; }
+ ;
+
/*****************************************************************************
*
* DROP DATABASE
*
- *
*****************************************************************************/
DropdbStmt: DROP DATABASE database_name
@@ -3210,20 +3204,11 @@ DropdbStmt: DROP DATABASE database_name
}
;
-/*
- * Though the equals sign doesn't match other WITH options, pg_dump uses
- * equals for backward compability, and it doesn't seem worth remove it.
- */
-opt_equal: '=' { $$ = TRUE; }
- | /*EMPTY*/ { $$ = FALSE; }
- ;
-
/*****************************************************************************
*
* ALTER DATABASE
*
- *
*****************************************************************************/
AlterDatabaseSetStmt: ALTER DATABASE database_name VariableSetStmt
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 7aaaa65c61b..690a047915a 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -8,25 +8,31 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.67 2002/03/19 02:18:20 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.68 2002/03/20 19:44:22 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "catalog/pg_proc.h"
+#include "nodes/makefuncs.h"
#include "optimizer/clauses.h"
#include "parser/parse_coerce.h"
#include "parser/parse_expr.h"
#include "parser/parse_func.h"
#include "parser/parse_type.h"
#include "utils/builtins.h"
+#include "utils/lsyscache.h"
#include "utils/syscache.h"
+
Oid DemoteType(Oid inType);
Oid PromoteTypeToNext(Oid inType);
static Oid PreferredType(CATEGORY category, Oid type);
+static Node *build_func_call(Oid funcid, Oid rettype, List *args);
+static Oid find_coercion_function(Oid targetTypeId, Oid inputTypeId,
+ Oid secondArgType);
/* coerce_type()
@@ -87,32 +93,28 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
result = (Node *) newcon;
}
- else if (IS_BINARY_COMPATIBLE(inputTypeId, targetTypeId))
+ else if (IsBinaryCompatible(inputTypeId, targetTypeId))
{
/*
* We don't really need to do a conversion, but we do need to
* attach a RelabelType node so that the expression will be seen
* to have the intended type when inspected by higher-level code.
- */
- RelabelType *relabel = makeNode(RelabelType);
-
- relabel->arg = node;
- relabel->resulttype = targetTypeId;
-
- /*
+ *
* XXX could we label result with exprTypmod(node) instead of
* default -1 typmod, to save a possible length-coercion later?
* Would work if both types have same interpretation of typmod,
* which is likely but not certain.
*/
- relabel->resulttypmod = -1;
-
- result = (Node *) relabel;
+ result = (Node *) makeRelabelType(node, targetTypeId, -1);
}
else if (typeInheritsFrom(inputTypeId, targetTypeId))
{
- /* Input class type is a subclass of target, so nothing to do */
- result = node;
+ /*
+ * Input class type is a subclass of target, so nothing to do
+ * --- except relabel the type. This is binary compatibility
+ * for complex types.
+ */
+ result = (Node *) makeRelabelType(node, targetTypeId, -1);
}
else
{
@@ -121,21 +123,24 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
* (caller should have determined that there is one), and generate
* an expression tree representing run-time application of the
* conversion function.
+ *
+ * For domains, we use the coercion function for the base type.
*/
- FuncCall *n = makeNode(FuncCall);
+ Oid baseTypeId = getBaseType(targetTypeId);
+ Oid funcId;
- n->funcname = typeidTypeName(targetTypeId);
- n->args = makeList1(node);
- n->agg_star = false;
- n->agg_distinct = false;
+ funcId = find_coercion_function(baseTypeId,
+ getBaseType(inputTypeId),
+ InvalidOid);
+ if (!OidIsValid(funcId))
+ elog(ERROR, "coerce_type: no conversion function from %s to %s",
+ format_type_be(inputTypeId), format_type_be(targetTypeId));
- result = transformExpr(pstate, (Node *) n, EXPR_COLUMN_FIRST);
+ result = build_func_call(funcId, baseTypeId, makeList1(node));
- /* safety check that we got the right thing */
- if (exprType(result) != targetTypeId)
- elog(ERROR, "coerce_type: conversion function %s produced %s",
- typeidTypeName(targetTypeId),
- typeidTypeName(exprType(result)));
+ /* if domain, relabel with domain type ID */
+ if (targetTypeId != baseTypeId)
+ result = (Node *) makeRelabelType(result, targetTypeId, -1);
/*
* If the input is a constant, apply the type conversion function
@@ -152,7 +157,8 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
* nodes that mustn't be collapsed. (It'd be a lot cleaner to
* make a separate node type for that purpose...)
*/
- if (IsA(node, Const) &&!((Const *) node)->constisnull)
+ if (IsA(node, Const) &&
+ !((Const *) node)->constisnull)
result = eval_const_expressions(result);
}
@@ -169,23 +175,18 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
*
* Notes:
* This uses the same mechanism as the CAST() SQL construct in gram.y.
- * We should also check the function return type on candidate conversion
- * routines just to be safe but we do not do that yet...
- * - thomas 1998-03-31
*/
bool
can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids)
{
int i;
- HeapTuple ftup;
- Form_pg_proc pform;
- Oid oid_array[FUNC_MAX_ARGS];
/* run through argument list... */
for (i = 0; i < nargs; i++)
{
Oid inputTypeId = input_typeids[i];
Oid targetTypeId = func_typeids[i];
+ Oid funcId;
/* no problem if same type */
if (inputTypeId == targetTypeId)
@@ -195,7 +196,7 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids)
* one of the known-good transparent conversions? then drop
* through...
*/
- if (IS_BINARY_COMPATIBLE(inputTypeId, targetTypeId))
+ if (IsBinaryCompatible(inputTypeId, targetTypeId))
continue;
/* don't know what to do for the output type? then quit... */
@@ -232,25 +233,14 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids)
* Else, try for explicit conversion using functions: look for a
* single-argument function named with the target type name and
* accepting the source type.
+ *
+ * If either type is a domain, use its base type instead.
*/
- MemSet(oid_array, 0, FUNC_MAX_ARGS * sizeof(Oid));
- oid_array[0] = inputTypeId;
-
- ftup = SearchSysCache(PROCNAME,
- PointerGetDatum(typeidTypeName(targetTypeId)),
- Int32GetDatum(1),
- PointerGetDatum(oid_array),
- 0);
- if (!HeapTupleIsValid(ftup))
- return false;
- /* Make sure the function's result type is as expected, too */
- pform = (Form_pg_proc) GETSTRUCT(ftup);
- if (pform->prorettype != targetTypeId)
- {
- ReleaseSysCache(ftup);
+ funcId = find_coercion_function(getBaseType(targetTypeId),
+ getBaseType(inputTypeId),
+ InvalidOid);
+ if (!OidIsValid(funcId))
return false;
- }
- ReleaseSysCache(ftup);
}
return true;
@@ -277,8 +267,8 @@ Node *
coerce_type_typmod(ParseState *pstate, Node *node,
Oid targetTypeId, int32 atttypmod)
{
- char *funcname;
- Oid oid_array[FUNC_MAX_ARGS];
+ Oid baseTypeId;
+ Oid funcId;
/*
* A negative typmod is assumed to mean that no coercion is wanted.
@@ -286,30 +276,28 @@ coerce_type_typmod(ParseState *pstate, Node *node,
if (atttypmod < 0 || atttypmod == exprTypmod(node))
return node;
- funcname = typeidTypeName(targetTypeId);
- MemSet(oid_array, 0, FUNC_MAX_ARGS * sizeof(Oid));
- oid_array[0] = targetTypeId;
- oid_array[1] = INT4OID;
-
- /* attempt to find with arguments exactly as specified... */
- if (SearchSysCacheExists(PROCNAME,
- PointerGetDatum(funcname),
- Int32GetDatum(2),
- PointerGetDatum(oid_array),
- 0))
+ /* If given type is a domain, use base type instead */
+ baseTypeId = getBaseType(targetTypeId);
+
+ funcId = find_coercion_function(baseTypeId, baseTypeId, INT4OID);
+
+ if (OidIsValid(funcId))
{
- A_Const *cons = makeNode(A_Const);
- FuncCall *func = makeNode(FuncCall);
+ Const *cons;
- cons->val.type = T_Integer;
- cons->val.val.ival = atttypmod;
+ cons = makeConst(INT4OID,
+ sizeof(int32),
+ Int32GetDatum(atttypmod),
+ false,
+ true,
+ false,
+ false);
- func->funcname = funcname;
- func->args = makeList2(node, cons);
- func->agg_star = false;
- func->agg_distinct = false;
+ node = build_func_call(funcId, baseTypeId, makeList2(node, cons));
- node = transformExpr(pstate, (Node *) func, EXPR_COLUMN_FIRST);
+ /* relabel if it's domain case */
+ if (targetTypeId != baseTypeId)
+ node = (Node *) makeRelabelType(node, targetTypeId, atttypmod);
}
return node;
@@ -532,6 +520,64 @@ TypeCategory(Oid inType)
} /* TypeCategory() */
+/* IsBinaryCompatible()
+ * Check if two types are binary-compatible.
+ *
+ * This notion allows us to cheat and directly exchange values without
+ * going through the trouble of calling a conversion function.
+ *
+ * XXX This should be moved to system catalog lookups
+ * to allow for better type extensibility.
+ */
+
+/*
+ * This macro describes hard-coded knowledge of binary compatibility
+ * for built-in types.
+ */
+#define IS_BINARY_COMPATIBLE(a,b) \
+ (((a) == BPCHAROID && (b) == TEXTOID) \
+ || ((a) == BPCHAROID && (b) == VARCHAROID) \
+ || ((a) == VARCHAROID && (b) == TEXTOID) \
+ || ((a) == VARCHAROID && (b) == BPCHAROID) \
+ || ((a) == TEXTOID && (b) == BPCHAROID) \
+ || ((a) == TEXTOID && (b) == VARCHAROID) \
+ || ((a) == OIDOID && (b) == INT4OID) \
+ || ((a) == OIDOID && (b) == REGPROCOID) \
+ || ((a) == INT4OID && (b) == OIDOID) \
+ || ((a) == INT4OID && (b) == REGPROCOID) \
+ || ((a) == REGPROCOID && (b) == OIDOID) \
+ || ((a) == REGPROCOID && (b) == INT4OID) \
+ || ((a) == ABSTIMEOID && (b) == INT4OID) \
+ || ((a) == INT4OID && (b) == ABSTIMEOID) \
+ || ((a) == RELTIMEOID && (b) == INT4OID) \
+ || ((a) == INT4OID && (b) == RELTIMEOID) \
+ || ((a) == INETOID && (b) == CIDROID) \
+ || ((a) == CIDROID && (b) == INETOID) \
+ || ((a) == BITOID && (b) == VARBITOID) \
+ || ((a) == VARBITOID && (b) == BITOID))
+
+bool
+IsBinaryCompatible(Oid type1, Oid type2)
+{
+ if (type1 == type2)
+ return true;
+ if (IS_BINARY_COMPATIBLE(type1, type2))
+ return true;
+ /*
+ * Perhaps the types are domains; if so, look at their base types
+ */
+ if (OidIsValid(type1))
+ type1 = getBaseType(type1);
+ if (OidIsValid(type2))
+ type2 = getBaseType(type2);
+ if (type1 == type2)
+ return true;
+ if (IS_BINARY_COMPATIBLE(type1, type2))
+ return true;
+ return false;
+}
+
+
/* IsPreferredType()
* Check if this type is a preferred type.
* XXX This should be moved to system catalog lookups
@@ -606,31 +652,81 @@ PreferredType(CATEGORY category, Oid type)
return result;
} /* PreferredType() */
-
/*
- * If the targetTypeId is a domain, we really want to coerce
- * the tuple to the domain type -- not the domain itself
+ * find_coercion_function
+ * Look for a coercion function between two types.
+ *
+ * A coercion function must be named after (the internal name of) its
+ * result type, and must accept exactly the specified input type.
+ *
+ * This routine is also used to look for length-coercion functions, which
+ * are similar but accept a second argument. secondArgType is the type
+ * of the second argument (normally INT4OID), or InvalidOid if we are
+ * looking for a regular coercion function.
+ *
+ * If a function is found, return its pg_proc OID; else return InvalidOid.
*/
-Oid
-getBaseType(Oid inType)
+static Oid
+find_coercion_function(Oid targetTypeId, Oid inputTypeId, Oid secondArgType)
{
- HeapTuple tup;
- Form_pg_type typTup;
+ char *funcname;
+ Oid oid_array[FUNC_MAX_ARGS];
+ int nargs;
+ HeapTuple ftup;
+ Form_pg_proc pform;
+ Oid funcid;
- tup = SearchSysCache(TYPEOID,
- ObjectIdGetDatum(inType),
- 0, 0, 0);
+ funcname = typeidTypeName(targetTypeId);
+ MemSet(oid_array, 0, FUNC_MAX_ARGS * sizeof(Oid));
+ oid_array[0] = inputTypeId;
+ if (OidIsValid(secondArgType))
+ {
+ oid_array[1] = secondArgType;
+ nargs = 2;
+ }
+ else
+ nargs = 1;
+
+ ftup = SearchSysCache(PROCNAME,
+ PointerGetDatum(funcname),
+ Int32GetDatum(nargs),
+ PointerGetDatum(oid_array),
+ 0);
+ if (!HeapTupleIsValid(ftup))
+ return InvalidOid;
+ /* Make sure the function's result type is as expected, too */
+ pform = (Form_pg_proc) GETSTRUCT(ftup);
+ if (pform->prorettype != targetTypeId)
+ {
+ ReleaseSysCache(ftup);
+ return InvalidOid;
+ }
+ funcid = ftup->t_data->t_oid;
+ ReleaseSysCache(ftup);
+ return funcid;
+}
- typTup = ((Form_pg_type) GETSTRUCT(tup));
+/*
+ * Build an expression tree representing a function call.
+ *
+ * The argument expressions must have been transformed already.
+ */
+static Node *
+build_func_call(Oid funcid, Oid rettype, List *args)
+{
+ Func *funcnode;
+ Expr *expr;
- /*
- * Assume that typbasetype exists and is a base type, where inType
- * was a domain
- */
- if (typTup->typtype == 'd')
- inType = typTup->typbasetype;
+ funcnode = makeNode(Func);
+ funcnode->funcid = funcid;
+ funcnode->functype = rettype;
+ funcnode->func_fcache = NULL;
- ReleaseSysCache(tup);
+ expr = makeNode(Expr);
+ expr->typeOid = rettype;
+ expr->opType = FUNC_EXPR;
+ expr->oper = (Node *) funcnode;
+ expr->args = args;
- return inType;
+ return (Node *) expr;
}
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 5cbc26f5cbe..6409ef3226a 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.109 2002/03/19 02:18:20 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.110 2002/03/20 19:44:25 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1027,8 +1027,7 @@ parser_typecast_expression(ParseState *pstate,
if (inputType != targetType)
{
expr = CoerceTargetExpr(pstate, expr, inputType,
- getBaseType(targetType),
- typename->typmod);
+ targetType, typename->typmod);
if (expr == NULL)
elog(ERROR, "Cannot cast type '%s' to '%s'",
format_type_be(inputType),
@@ -1040,7 +1039,7 @@ parser_typecast_expression(ParseState *pstate,
* as well as a type coercion.
*/
expr = coerce_type_typmod(pstate, expr,
- getBaseType(targetType), typename->typmod);
+ targetType, typename->typmod);
return expr;
}
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index ed39d6c1036..6dc7b440fa8 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.117 2002/03/12 00:51:55 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.118 2002/03/20 19:44:29 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -575,8 +575,7 @@ agg_select_candidate(Oid typeid, CandidateList candidates)
{
current_typeid = current_candidate->args[0];
- if (current_typeid == typeid
- || IS_BINARY_COMPATIBLE(current_typeid, typeid))
+ if (IsBinaryCompatible(current_typeid, typeid))
{
last_candidate = current_candidate;
ncandidates++;
@@ -815,9 +814,7 @@ func_select_candidate(int nargs,
{
if (input_typeids[i] != UNKNOWNOID)
{
- if (current_typeids[i] == input_typeids[i] ||
- IS_BINARY_COMPATIBLE(current_typeids[i],
- input_typeids[i]))
+ if (IsBinaryCompatible(current_typeids[i], input_typeids[i]))
nmatch++;
}
}
@@ -1115,8 +1112,7 @@ func_get_detail(char *funcname,
Node *arg1 = lfirst(fargs);
if ((sourceType == UNKNOWNOID && IsA(arg1, Const)) ||
- sourceType == targetType ||
- IS_BINARY_COMPATIBLE(sourceType, targetType))
+ IsBinaryCompatible(sourceType, targetType))
{
/* Yup, it's a type coercion */
*funcid = InvalidOid;
diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c
index 318f1b9eb7e..8495f9f9e65 100644
--- a/src/backend/parser/parse_oper.c
+++ b/src/backend/parser/parse_oper.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.52 2002/02/19 20:11:15 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.53 2002/03/20 19:44:31 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -354,9 +354,7 @@ oper_select_candidate(int nargs,
{
if (input_typeids[i] != UNKNOWNOID)
{
- if (current_typeids[i] == input_typeids[i] ||
- IS_BINARY_COMPATIBLE(current_typeids[i],
- input_typeids[i]))
+ if (IsBinaryCompatible(current_typeids[i], input_typeids[i]))
nmatch++;
}
}
@@ -736,10 +734,8 @@ compatible_oper(char *op, Oid arg1, Oid arg2, bool noError)
/* but is it good enough? */
opform = (Form_pg_operator) GETSTRUCT(optup);
- if ((opform->oprleft == arg1 ||
- IS_BINARY_COMPATIBLE(opform->oprleft, arg1)) &&
- (opform->oprright == arg2 ||
- IS_BINARY_COMPATIBLE(opform->oprright, arg2)))
+ if (IsBinaryCompatible(opform->oprleft, arg1) &&
+ IsBinaryCompatible(opform->oprright, arg2))
return optup;
/* nope... */