Check that user-specified opclass in CREATE INDEX corresponds to operators
authorTom Lane
Tue, 25 Apr 2000 02:45:54 +0000 (02:45 +0000)
committerTom Lane
Tue, 25 Apr 2000 02:45:54 +0000 (02:45 +0000)
that will actually work on the column datatype.

src/backend/commands/indexcmds.c

index c1c16a844d4737a93246241b98cac6a54509828f..19ff9597d5297129ce761b5161a77e8214fc4bdc 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.24 2000/04/23 01:44:55 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.25 2000/04/25 02:45:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -24,6 +24,7 @@
 #include "catalog/pg_database.h"
 #include "catalog/pg_index.h"
 #include "catalog/pg_opclass.h"
+#include "catalog/pg_operator.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_shadow.h"
 #include "catalog/pg_type.h"
@@ -32,6 +33,7 @@
 #include "optimizer/planmain.h"
 #include "optimizer/prep.h"
 #include "parser/parsetree.h"
+#include "parser/parse_coerce.h"
 #include "parser/parse_func.h"
 #include "parser/parse_type.h"
 #include "utils/builtins.h"
@@ -556,7 +558,9 @@ GetAttrOpClass(IndexElem *attribute, Oid attrType,
    HeapScanDesc scan;
    ScanKeyData entry[2];
    HeapTuple   tuple;
-   Oid         opClassId;
+   Oid         opClassId,
+               oprId;
+   bool        doTypeCheck = true;
 
    if (attribute->class == NULL)
    {
@@ -565,6 +569,8 @@ GetAttrOpClass(IndexElem *attribute, Oid attrType,
        if (attribute->class == NULL)
            elog(ERROR, "DefineIndex: type %s has no default operator class",
                 typeidTypeName(attrType));
+       /* assume we need not check type compatibility */
+       doTypeCheck = false;
    }
 
    tuple = SearchSysCacheTuple(CLANAME,
@@ -597,9 +603,42 @@ GetAttrOpClass(IndexElem *attribute, Oid attrType,
             attribute->class, accessMethodName);
    }
 
+   oprId = ((Form_pg_amop) GETSTRUCT(tuple))->amopopr;
+
    heap_endscan(scan);
    heap_close(relation, AccessShareLock);
 
+   /*
+    * Make sure the operators associated with this opclass actually accept
+    * the column data type.  This prevents possible coredumps caused by
+    * user errors like applying text_ops to an int4 column.  We will accept
+    * an opclass as OK if the operator's input datatype is binary-compatible
+    * with the actual column datatype.  Note we assume that all the operators
+    * associated with an opclass accept the same datatypes, so checking the
+    * first one we happened to find in the table is sufficient.
+    *
+    * If the opclass was the default for the datatype, assume we can skip
+    * this check --- that saves a few cycles in the most common case.
+    * If pg_opclass is messed up then we're probably screwed anyway...
+    */
+   if (doTypeCheck)
+   {
+       tuple = SearchSysCacheTuple(OPEROID,
+                                   ObjectIdGetDatum(oprId),
+                                   0, 0, 0);
+       if (HeapTupleIsValid(tuple))
+       {
+           Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tuple);
+           Oid     opInputType = (optup->oprkind == 'l') ?
+               optup->oprright : optup->oprleft;
+
+           if (attrType != opInputType &&
+               ! IS_BINARY_COMPATIBLE(attrType, opInputType))
+               elog(ERROR, "DefineIndex: opclass \"%s\" does not accept datatype \"%s\"",
+                    attribute->class, typeidTypeName(attrType));
+       }
+   }
+
    return opClassId;
 }