Add comparison operators and btree indexing support for type bytea.
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 13 Aug 2001 18:45:36 +0000 (18:45 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 13 Aug 2001 18:45:36 +0000 (18:45 +0000)
From Joe Conway.

src/backend/utils/adt/selfuncs.c
src/backend/utils/adt/varlena.c
src/include/catalog/catversion.h
src/include/catalog/pg_amop.h
src/include/catalog/pg_amproc.h
src/include/catalog/pg_opclass.h
src/include/catalog/pg_operator.h
src/include/catalog/pg_proc.h
src/include/utils/builtins.h

index e0cfeefaee41b627309de8129dc5572fbef25af3..406916c88735cd1750ce7c2f188b71769fe6886f 100644 (file)
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.95 2001/07/16 05:06:59 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.96 2001/08/13 18:45:35 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -134,8 +134,16 @@ static void convert_string_to_scalar(unsigned char *value,
                         double *scaledlobound,
                         unsigned char *hibound,
                         double *scaledhibound);
+static void convert_bytea_to_scalar(Datum value,
+                        double *scaledvalue,
+                        Datum lobound,
+                        double *scaledlobound,
+                        Datum hibound,
+                        double *scaledhibound);
 static double convert_one_string_to_scalar(unsigned char *value,
                             int rangelo, int rangehi);
+static double convert_one_bytea_to_scalar(unsigned char *value, int valuelen,
+                            int rangelo, int rangehi);
 static unsigned char *convert_string_datum(Datum value, Oid typid);
 static double convert_timevalue_to_scalar(Datum value, Oid typid);
 static double get_att_numdistinct(Query *root, Var *var,
@@ -1664,6 +1672,9 @@ icnlikejoinsel(PG_FUNCTION_ARGS)
  * which is explained below.  The reason why this routine deals with
  * three values at a time, not just one, is that we need it for strings.
  *
+ * The bytea datatype is just enough different from strings that it has
+ * to be treated separately.
+ *
  * The several datatypes representing absolute times are all converted
  * to Timestamp, which is actually a double, and then we just use that
  * double value.  Note this will give bad results for the various "special"
@@ -1718,6 +1729,17 @@ convert_to_scalar(Datum value, Oid valuetypid, double *scaledvalue,
                return true;
            }
 
+       /*
+        * Built-in bytea type
+        */
+       case BYTEAOID:
+           {
+               convert_bytea_to_scalar(value, scaledvalue,
+                                       lobound, scaledlobound,
+                                       hibound, scaledhibound);
+               return true;
+           }
+
        /*
         * Built-in time types
         */
@@ -1996,6 +2018,99 @@ convert_string_datum(Datum value, Oid typid)
    return (unsigned char *) val;
 }
 
+/*
+ * Do convert_to_scalar()'s work for any bytea data type.
+ *
+ * Very similar to convert_string_to_scalar except we can't assume
+ * null-termination and therefore pass explicit lengths around.
+ *
+ * Also, assumptions about likely "normal" ranges of characters have been
+ * removed - a data range of 0..255 is always used, for now.  (Perhaps
+ * someday we will add information about actual byte data range to
+ * pg_statistic.)
+ */
+static void
+convert_bytea_to_scalar(Datum value,
+                       double *scaledvalue,
+                       Datum lobound,
+                       double *scaledlobound,
+                       Datum hibound,
+                       double *scaledhibound)
+{
+   int         rangelo,
+               rangehi,
+               valuelen = VARSIZE(DatumGetPointer(value)) - VARHDRSZ,
+               loboundlen = VARSIZE(DatumGetPointer(lobound)) - VARHDRSZ,
+               hiboundlen = VARSIZE(DatumGetPointer(hibound)) - VARHDRSZ,
+               i,
+               minlen;
+   unsigned char *valstr = (unsigned char *) VARDATA(DatumGetPointer(value)),
+               *lostr = (unsigned char *) VARDATA(DatumGetPointer(lobound)),
+               *histr = (unsigned char *) VARDATA(DatumGetPointer(hibound));
+
+   /*
+    * Assume bytea data is uniformly distributed across all byte values.
+    */
+   rangelo = 0;
+   rangehi = 255;
+
+   /*
+    * Now strip any common prefix of the three strings.
+    */
+   minlen = Min(Min(valuelen, loboundlen), hiboundlen);
+   for (i = 0; i < minlen; i++)
+   {
+       if (*lostr != *histr || *lostr != *valstr)
+           break;
+       lostr++, histr++, valstr++;
+       loboundlen--, hiboundlen--, valuelen--;
+   }
+
+   /*
+    * Now we can do the conversions.
+    */
+   *scaledvalue = convert_one_bytea_to_scalar(valstr, valuelen, rangelo, rangehi);
+   *scaledlobound = convert_one_bytea_to_scalar(lostr, loboundlen, rangelo, rangehi);
+   *scaledhibound = convert_one_bytea_to_scalar(histr, hiboundlen, rangelo, rangehi);
+}
+
+static double
+convert_one_bytea_to_scalar(unsigned char *value, int valuelen,
+                           int rangelo, int rangehi)
+{
+   double      num,
+               denom,
+               base;
+
+   if (valuelen <= 0)
+       return 0.0;             /* empty string has scalar value 0 */
+
+   /*
+    * Since base is 256, need not consider more than about 10
+    * chars (even this many seems like overkill)
+    */
+   if (valuelen > 10)
+       valuelen = 10;
+
+   /* Convert initial characters to fraction */
+   base = rangehi - rangelo + 1;
+   num = 0.0;
+   denom = base;
+   while (valuelen-- > 0)
+   {
+       int         ch = *value++;
+
+       if (ch < rangelo)
+           ch = rangelo - 1;
+       else if (ch > rangehi)
+           ch = rangehi + 1;
+       num += ((double) (ch - rangelo)) / denom;
+       denom *= base;
+   }
+
+   return num;
+}
+
 /*
  * Do convert_to_scalar()'s work for any timevalue data type.
  */
index c534c7d92e52573efcae887c2af84706cb3d973d..9bc42cf05e702639afcb26b0cd3f0ad354ed7f45 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/utils/adt/varlena.c,v 1.70 2001/05/03 19:00:36 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/utils/adt/varlena.c,v 1.71 2001/08/13 18:45:35 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -875,3 +875,162 @@ name_text(PG_FUNCTION_ARGS)
 
    PG_RETURN_TEXT_P(result);
 }
+
+
+/*****************************************************************************
+ * Comparison Functions used for bytea
+ *
+ * Note: btree indexes need these routines not to leak memory; therefore,
+ * be careful to free working copies of toasted datums.  Most places don't
+ * need to be so careful.
+ *****************************************************************************/
+
+Datum
+byteaeq(PG_FUNCTION_ARGS)
+{
+   bytea       *arg1 = PG_GETARG_BYTEA_P(0);
+   bytea       *arg2 = PG_GETARG_BYTEA_P(1);
+   int         len1,
+               len2;
+   bool        result;
+
+   len1 = VARSIZE(arg1) - VARHDRSZ;
+   len2 = VARSIZE(arg2) - VARHDRSZ;
+
+   /* fast path for different-length inputs */
+   if (len1 != len2)
+       result = false;
+   else
+       result = (memcmp(VARDATA(arg1), VARDATA(arg2), len1) == 0);
+
+   PG_FREE_IF_COPY(arg1, 0);
+   PG_FREE_IF_COPY(arg2, 1);
+
+   PG_RETURN_BOOL(result);
+}
+
+Datum
+byteane(PG_FUNCTION_ARGS)
+{
+   bytea       *arg1 = PG_GETARG_BYTEA_P(0);
+   bytea       *arg2 = PG_GETARG_BYTEA_P(1);
+   int         len1,
+               len2;
+   bool        result;
+
+   len1 = VARSIZE(arg1) - VARHDRSZ;
+   len2 = VARSIZE(arg2) - VARHDRSZ;
+
+   /* fast path for different-length inputs */
+   if (len1 != len2)
+       result = true;
+   else
+       result = (memcmp(VARDATA(arg1), VARDATA(arg2), len1) != 0);
+
+   PG_FREE_IF_COPY(arg1, 0);
+   PG_FREE_IF_COPY(arg2, 1);
+
+   PG_RETURN_BOOL(result);
+}
+
+Datum
+bytealt(PG_FUNCTION_ARGS)
+{
+   bytea       *arg1 = PG_GETARG_BYTEA_P(0);
+   bytea       *arg2 = PG_GETARG_BYTEA_P(1);
+   int         len1,
+               len2;
+   int         cmp;
+
+   len1 = VARSIZE(arg1) - VARHDRSZ;
+   len2 = VARSIZE(arg2) - VARHDRSZ;
+
+   cmp = memcmp(VARDATA(arg1), VARDATA(arg2), Min(len1, len2));
+
+   PG_FREE_IF_COPY(arg1, 0);
+   PG_FREE_IF_COPY(arg2, 1);
+
+   PG_RETURN_BOOL((cmp < 0) || ((cmp == 0) && (len1 < len2)));
+}
+
+Datum
+byteale(PG_FUNCTION_ARGS)
+{
+   bytea       *arg1 = PG_GETARG_BYTEA_P(0);
+   bytea       *arg2 = PG_GETARG_BYTEA_P(1);
+   int         len1,
+               len2;
+   int         cmp;
+
+   len1 = VARSIZE(arg1) - VARHDRSZ;
+   len2 = VARSIZE(arg2) - VARHDRSZ;
+
+   cmp = memcmp(VARDATA(arg1), VARDATA(arg2), Min(len1, len2));
+
+   PG_FREE_IF_COPY(arg1, 0);
+   PG_FREE_IF_COPY(arg2, 1);
+
+   PG_RETURN_BOOL((cmp < 0) || ((cmp == 0) && (len1 <= len2)));
+}
+
+Datum
+byteagt(PG_FUNCTION_ARGS)
+{
+   bytea       *arg1 = PG_GETARG_BYTEA_P(0);
+   bytea       *arg2 = PG_GETARG_BYTEA_P(1);
+   int         len1,
+               len2;
+   int         cmp;
+
+   len1 = VARSIZE(arg1) - VARHDRSZ;
+   len2 = VARSIZE(arg2) - VARHDRSZ;
+
+   cmp = memcmp(VARDATA(arg1), VARDATA(arg2), Min(len1, len2));
+
+   PG_FREE_IF_COPY(arg1, 0);
+   PG_FREE_IF_COPY(arg2, 1);
+
+   PG_RETURN_BOOL((cmp > 0) || ((cmp == 0) && (len1 > len2)));
+}
+
+Datum
+byteage(PG_FUNCTION_ARGS)
+{
+   bytea       *arg1 = PG_GETARG_BYTEA_P(0);
+   bytea       *arg2 = PG_GETARG_BYTEA_P(1);
+   int         len1,
+               len2;
+   int         cmp;
+
+   len1 = VARSIZE(arg1) - VARHDRSZ;
+   len2 = VARSIZE(arg2) - VARHDRSZ;
+
+   cmp = memcmp(VARDATA(arg1), VARDATA(arg2), Min(len1, len2));
+
+   PG_FREE_IF_COPY(arg1, 0);
+   PG_FREE_IF_COPY(arg2, 1);
+
+   PG_RETURN_BOOL((cmp > 0) || ((cmp == 0) && (len1 >= len2)));
+}
+
+Datum
+byteacmp(PG_FUNCTION_ARGS)
+{
+   bytea       *arg1 = PG_GETARG_BYTEA_P(0);
+   bytea       *arg2 = PG_GETARG_BYTEA_P(1);
+   int         len1,
+               len2;
+   int         cmp;
+
+   len1 = VARSIZE(arg1) - VARHDRSZ;
+   len2 = VARSIZE(arg2) - VARHDRSZ;
+
+   cmp = memcmp(VARDATA(arg1), VARDATA(arg2), Min(len1, len2));
+   if ((cmp == 0) && (len1 != len2))
+       cmp = (len1 < len2) ? -1 : 1;
+
+   PG_FREE_IF_COPY(arg1, 0);
+   PG_FREE_IF_COPY(arg2, 1);
+
+   PG_RETURN_INT32(cmp);
+}
index 5827c15f77ff216f6f61ec6a211d325b0083a454..9d635597414126871d7f2f0c42eebf49cde7f799 100644 (file)
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: catversion.h,v 1.87 2001/08/10 18:57:39 tgl Exp $
+ * $Id: catversion.h,v 1.88 2001/08/13 18:45:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 200108101
+#define CATALOG_VERSION_NO 200108131
 
 #endif
index 48f2c3bd83d62dda7609eb557c0af109314d1ab2..42adef9ad65e35b6552486ed2b0c1c811032fcab 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_amop.h,v 1.39 2001/08/10 18:57:39 tgl Exp $
+ * $Id: pg_amop.h,v 1.40 2001/08/13 18:45:36 tgl Exp $
  *
  * NOTES
  *  the genbki.sh script reads this file and generates .bki
@@ -249,6 +249,16 @@ DATA(insert (  403 1077 1062 3 ));
 DATA(insert (  403 1077 1069 4 ));
 DATA(insert (  403 1077 1068 5 ));
 
+/*
+ * nbtree bytea_ops
+ */
+
+DATA(insert (  403 1961 1957 1 ));
+DATA(insert (  403 1961 1958 2 ));
+DATA(insert (  403 1961 1955 3 ));
+DATA(insert (  403 1961 1960 4 ));
+DATA(insert (  403 1961 1959 5 ));
+
 /*
  * nbtree date_ops
  */
index a8aadd4518ac6fd66ced4d06b1847fdcea9a914d..d3639becc5f4f2ea91779e7020870639ce440a97 100644 (file)
@@ -10,7 +10,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_amproc.h,v 1.28 2001/08/10 18:57:39 tgl Exp $
+ * $Id: pg_amproc.h,v 1.29 2001/08/13 18:45:36 tgl Exp $
  *
  * NOTES
  *   the genbki.sh script reads this file and generates .bki
@@ -101,6 +101,7 @@ DATA(insert (403 1690 1693 1));
 DATA(insert (403 1399 1358 1));
 DATA(insert (403  424 1596 1));
 DATA(insert (403  425 1672 1));
+DATA(insert (403 1961 1954 1));
 
 
 /* hash */
index eca88f5068cf4c9743eaeef757efb81d10b0ed44..77adfc91214fb0bb6ff56c7972159c99c18284de 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_opclass.h,v 1.37 2001/01/24 19:43:21 momjian Exp $
+ * $Id: pg_opclass.h,v 1.38 2001/08/13 18:45:36 tgl Exp $
  *
  * NOTES
  *   the genbki.sh script reads this file and generates .bki
@@ -123,5 +123,7 @@ DATA(insert OID =  424 (    bit_ops        1560   ));
 DESCR("");
 DATA(insert OID =  425 (   varbit_ops     1562   ));
 DESCR("");
+DATA(insert OID = 1961 (   bytea_ops        17   ));
+DESCR("");
 
 #endif  /* PG_OPCLASS_H */
index 171ab3dc79ece697c936762a0fb470e1667a2089..ef4806f17ab0d9ff84faf6aee4c9d61db9f1bb17 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_operator.h,v 1.91 2001/06/17 02:05:20 tgl Exp $
+ * $Id: pg_operator.h,v 1.92 2001/08/13 18:45:36 tgl Exp $
  *
  * NOTES
  *   the genbki.sh script reads this file and generates .bki
@@ -809,6 +809,14 @@ DATA(insert OID = 1919 (  "+"     PGUID 0 l t f   0  700 700   0   0   0   0 float4
 DATA(insert OID = 1920 (  "+"     PGUID 0 l t f   0  701 701   0   0   0   0 float8up - - ));
 DATA(insert OID = 1921 (  "+"     PGUID 0 l t f   0 1700 1700  0   0   0   0 numeric_uplus - - ));
 
+/* bytea operators */
+DATA(insert OID = 1955 ( "="      PGUID 0 b t t 17 17  16 1955 1956 1957 1957 byteaeq eqsel eqjoinsel ));
+DATA(insert OID = 1956 ( "<>"     PGUID 0 b t f 17 17  16 1956 1955  0 0 byteane neqsel neqjoinsel ));
+DATA(insert OID = 1957 ( "<"      PGUID 0 b t f 17 17  16 1959 1960  0 0 bytealt scalarltsel scalarltjoinsel ));
+DATA(insert OID = 1958 ( "<="     PGUID 0 b t f 17 17  16 1960 1959  0 0 byteale scalarltsel scalarltjoinsel ));
+DATA(insert OID = 1959 ( ">"      PGUID 0 b t f 17 17  16 1957 1958  0 0 byteagt scalargtsel scalargtjoinsel ));
+DATA(insert OID = 1960 ( ">="     PGUID 0 b t f 17 17  16 1958 1957  0 0 byteage scalargtsel scalargtjoinsel ));
+
 /*
  * function prototypes
  */
index 63480d4965e44d8dc76ab93e5f57e3238eb05bdd..2bb41d49b6608c553407639c8721556d84a37ce6 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_proc.h,v 1.202 2001/08/10 20:52:25 tgl Exp $
+ * $Id: pg_proc.h,v 1.203 2001/08/13 18:45:36 tgl Exp $
  *
  * NOTES
  *   The script catalog/genbki.sh reads this file and generates .bki
@@ -2699,6 +2699,22 @@ DATA(insert OID = 1946 (  encode                     PGUID 12 f t t t 2 f 25 "17 25" 100 0 0 10
 DESCR("Convert bytea value into some ascii-only text string");
 DATA(insert OID = 1947 (  decode                       PGUID 12 f t t t 2 f 17 "25 25" 100 0 0 100 binary_decode - ));
 DESCR("Convert ascii-encoded text string into bytea value");
+
+DATA(insert OID = 1948 (  byteaeq         PGUID 12 f t t t 2 f 16 "17 17" 100 0 0 100  byteaeq - ));
+DESCR("equal");
+DATA(insert OID = 1949 (  bytealt         PGUID 12 f t t t 2 f 16 "17 17" 100 0 0 100  bytealt - ));
+DESCR("less-than");
+DATA(insert OID = 1950 (  byteale         PGUID 12 f t t t 2 f 16 "17 17" 100 0 0 100  byteale - ));
+DESCR("less-than-or-equal");
+DATA(insert OID = 1951 (  byteagt         PGUID 12 f t t t 2 f 16 "17 17" 100 0 0 100  byteagt - ));
+DESCR("greater-than");
+DATA(insert OID = 1952 (  byteage         PGUID 12 f t t t 2 f 16 "17 17" 100 0 0 100  byteage - ));
+DESCR("greater-than-or-equal");
+DATA(insert OID = 1953 (  byteane         PGUID 12 f t t t 2 f 16 "17 17" 100 0 0 100  byteane - ));
+DESCR("not equal");
+DATA(insert OID = 1954 (  byteacmp        PGUID 12 f t t t 2 f 23 "17 17" 100 0 0 100  byteacmp - ));
+DESCR("less-equal-greater");
+
  
 /*
  * prototypes for functions pg_proc.c
index 5fbb2cd755d8d3c763c1e84c921853c51ce7e68b..a774309aed8bd901c7cd2ee420de7f20cb615344 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: builtins.h,v 1.159 2001/08/09 18:28:18 petere Exp $
+ * $Id: builtins.h,v 1.160 2001/08/13 18:45:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -412,6 +412,13 @@ extern Datum byteaSetByte(PG_FUNCTION_ARGS);
 extern Datum byteaSetBit(PG_FUNCTION_ARGS);
 extern Datum binary_encode(PG_FUNCTION_ARGS);
 extern Datum binary_decode(PG_FUNCTION_ARGS);
+extern Datum byteaeq(PG_FUNCTION_ARGS);
+extern Datum byteane(PG_FUNCTION_ARGS);
+extern Datum bytealt(PG_FUNCTION_ARGS);
+extern Datum byteale(PG_FUNCTION_ARGS);
+extern Datum byteagt(PG_FUNCTION_ARGS);
+extern Datum byteage(PG_FUNCTION_ARGS);
+extern Datum byteacmp(PG_FUNCTION_ARGS);
 
 /* version.c */
 extern Datum pgsql_version(PG_FUNCTION_ARGS);