GISTENTRY *
gbt_num_compress(GISTENTRY *retval, GISTENTRY *entry, const gbtree_ninfo *tinfo)
{
	if (entry->leafkey)
	{
		union
		{
			int16		i2;
			int32		i4;
			int64		i8;
			float4		f4;
			float8		f8;
			DateADT		dt;
			TimeADT		tm;
			Timestamp	ts;
			Cash		ch;
		}			v;

		GBT_NUMKEY *r = (GBT_NUMKEY *) palloc0(2 * tinfo->size);
		void	   *leaf = NULL;

		switch (tinfo->t)
		{
			case gbt_t_int2:
				v.i2 = DatumGetInt16(entry->key);
				leaf = &v.i2;
				break;
			case gbt_t_int4:
				v.i4 = DatumGetInt32(entry->key);
				leaf = &v.i4;
				break;
			case gbt_t_int8:
				v.i8 = DatumGetInt64(entry->key);
				leaf = &v.i8;
				break;
			case gbt_t_oid:
				v.i4 = DatumGetObjectId(entry->key);
				leaf = &v.i4;
				break;
			case gbt_t_float4:
				v.f4 = DatumGetFloat4(entry->key);
				leaf = &v.f4;
				break;
			case gbt_t_float8:
				v.f8 = DatumGetFloat8(entry->key);
				leaf = &v.f8;
				break;
			case gbt_t_date:
				v.dt = DatumGetDateADT(entry->key);
				leaf = &v.dt;
				break;
			case gbt_t_time:
				v.tm = DatumGetTimeADT(entry->key);
				leaf = &v.tm;
				break;
			case gbt_t_ts:
				v.ts = DatumGetTimestamp(entry->key);
				leaf = &v.ts;
				break;
			case gbt_t_cash:
				v.ch = DatumGetCash(entry->key);
				leaf = &v.ch;
				break;
			default:
				leaf = DatumGetPointer(entry->key);
		}

		memcpy((void *) &r[0], leaf, tinfo->size);
		memcpy((void *) &r[tinfo->size], leaf, tinfo->size);
		retval = palloc(sizeof(GISTENTRY));
		gistentryinit(*retval, PointerGetDatum(r), entry->rel, entry->page,
					  entry->offset, FALSE);
	}
	else
		retval = entry;

	return retval;
}
Beispiel #2
0
/*
 * Add an attribute to the hash calculation.
 * **IMPORTANT: any new hard coded support for a data type in here
 * must be added to isGreenplumDbHashable() below!
 *
 * Note that the caller should provide the base type if the datum is
 * of a domain type. It is quite expensive to call get_typtype() and
 * getBaseType() here since this function gets called a lot for the
 * same set of Datums.
 *
 * @param hashFn called to update the hash value.
 * @param clientData passed to hashFn.
 */
void
hashDatum(Datum datum, Oid type, datumHashFunction hashFn, void *clientData)
{
	void	   *buf = NULL;		/* pointer to the data */
	size_t		len = 0;		/* length for the data buffer */

	int64		intbuf;			/* an 8 byte buffer for all integer sizes */

	float4		buf_f4;
	float8		buf_f8;
	Timestamp	tsbuf;			/* timestamp data dype is either a double or
								 * int8 (determined in compile time) */
	TimestampTz tstzbuf;
	DateADT		datebuf;
	TimeADT		timebuf;
	TimeTzADT  *timetzptr;
	Interval   *intervalptr;
	AbsoluteTime abstime_buf;
	RelativeTime reltime_buf;
	TimeInterval tinterval;
	AbsoluteTime tinterval_len;

	Numeric		num;
	bool		bool_buf;
	char		char_buf;
	Name		namebuf;

	ArrayType  *arrbuf;
	inet	   *inetptr;		/* inet/cidr */
	unsigned char inet_hkey[sizeof(inet_struct)];
	macaddr    *macptr;			/* MAC address */

	VarBit	   *vbitptr;

	oidvector  *oidvec_buf;
	Complex    *complex_ptr;
	Complex		complex_buf;
	double		complex_real;
	double		complex_imag;

	Cash		cash_buf;
	pg_uuid_t  *uuid_buf;

	/*
	 * special case buffers
	 */
	uint32		nanbuf;
	uint32		invalidbuf;

	void	   *tofree = NULL;

	if (typeIsEnumType(type))
		type = ANYENUMOID;

	/*
	 * Select the hash to be performed according to the field type we are
	 * adding to the hash.
	 */
	switch (type)
	{
			/*
			 * ======= NUMERIC TYPES ========
			 */
		case INT2OID:			/* -32 thousand to 32 thousand, 2-byte storage */
			intbuf = (int64) DatumGetInt16(datum);	/* cast to 8 byte before
													 * hashing */
			buf = &intbuf;
			len = sizeof(intbuf);
			break;

		case INT4OID:			/* -2 billion to 2 billion integer, 4-byte
								 * storage */
			intbuf = (int64) DatumGetInt32(datum);	/* cast to 8 byte before
													 * hashing */
			buf = &intbuf;
			len = sizeof(intbuf);
			break;

		case INT8OID:			/* ~18 digit integer, 8-byte storage */
			intbuf = DatumGetInt64(datum);	/* cast to 8 byte before hashing */
			buf = &intbuf;
			len = sizeof(intbuf);
			break;

		case FLOAT4OID:			/* single-precision floating point number,
								 * 4-byte storage */
			buf_f4 = DatumGetFloat4(datum);

			/*
			 * On IEEE-float machines, minus zero and zero have different bit
			 * patterns but should compare as equal.  We must ensure that they
			 * have the same hash value, which is most easily done this way:
			 */
			if (buf_f4 == (float4) 0)
				buf_f4 = 0.0;

			buf = &buf_f4;
			len = sizeof(buf_f4);
			break;

		case FLOAT8OID:			/* double-precision floating point number,
								 * 8-byte storage */
			buf_f8 = DatumGetFloat8(datum);

			/*
			 * On IEEE-float machines, minus zero and zero have different bit
			 * patterns but should compare as equal.  We must ensure that they
			 * have the same hash value, which is most easily done this way:
			 */
			if (buf_f8 == (float8) 0)
				buf_f8 = 0.0;

			buf = &buf_f8;
			len = sizeof(buf_f8);
			break;

		case NUMERICOID:

			num = DatumGetNumeric(datum);

			if (NUMERIC_IS_NAN(num))
			{
				nanbuf = NAN_VAL;
				buf = &nanbuf;
				len = sizeof(nanbuf);
			}
			else
				/* not a nan */
			{
				buf = num->n_data;
				len = (VARSIZE(num) - NUMERIC_HDRSZ);
			}

			/*
			 * If we did a pg_detoast_datum, we need to remember to pfree, or
			 * we will leak memory.  Because of the 1-byte varlena header
			 * stuff.
			 */
			if (num != (Numeric) DatumGetPointer(datum))
				tofree = num;

			break;

			/*
			 * ====== CHARACTER TYPES =======
			 */
		case CHAROID:			/* char(1), single character */
			char_buf = DatumGetChar(datum);
			buf = &char_buf;
			len = 1;
			break;

		case BPCHAROID:			/* char(n), blank-padded string, fixed storage */
		case TEXTOID:			/* text */
		case VARCHAROID:		/* varchar */
		case BYTEAOID:			/* bytea */
			{
				int			tmplen;

				varattrib_untoast_ptr_len(datum, (char **) &buf, &tmplen, &tofree);
				/* adjust length to not include trailing blanks */
				if (type != BYTEAOID && tmplen > 1)
					tmplen = ignoreblanks((char *) buf, tmplen);

				len = tmplen;
				break;
			}

		case NAMEOID:
			namebuf = DatumGetName(datum);
			len = NAMEDATALEN;
			buf = NameStr(*namebuf);

			/* adjust length to not include trailing blanks */
			if (len > 1)
				len = ignoreblanks((char *) buf, len);
			break;

			/*
			 * ====== OBJECT IDENTIFIER TYPES ======
			 */
		case OIDOID:			/* object identifier(oid), maximum 4 billion */
		case REGPROCOID:		/* function name */
		case REGPROCEDUREOID:	/* function name with argument types */
		case REGOPEROID:		/* operator name */
		case REGOPERATOROID:	/* operator with argument types */
		case REGCLASSOID:		/* relation name */
		case REGTYPEOID:		/* data type name */
		case ANYENUMOID:		/* enum type name */
			intbuf = (int64) DatumGetUInt32(datum); /* cast to 8 byte before
													 * hashing */
			buf = &intbuf;
			len = sizeof(intbuf);
			break;

		case TIDOID:			/* tuple id (6 bytes) */
			buf = DatumGetPointer(datum);
			len = SizeOfIptrData;
			break;

			/*
			 * ====== DATE/TIME TYPES ======
			 */
		case TIMESTAMPOID:		/* date and time */
			tsbuf = DatumGetTimestamp(datum);
			buf = &tsbuf;
			len = sizeof(tsbuf);
			break;

		case TIMESTAMPTZOID:	/* date and time with time zone */
			tstzbuf = DatumGetTimestampTz(datum);
			buf = &tstzbuf;
			len = sizeof(tstzbuf);
			break;

		case DATEOID:			/* ANSI SQL date */
			datebuf = DatumGetDateADT(datum);
			buf = &datebuf;
			len = sizeof(datebuf);
			break;

		case TIMEOID:			/* hh:mm:ss, ANSI SQL time */
			timebuf = DatumGetTimeADT(datum);
			buf = &timebuf;
			len = sizeof(timebuf);
			break;

		case TIMETZOID:			/* time with time zone */

			/*
			 * will not compare to TIMEOID on equal values. Postgres never
			 * attempts to compare the two as well.
			 */
			timetzptr = DatumGetTimeTzADTP(datum);
			buf = (unsigned char *) timetzptr;

			/*
			 * Specify hash length as sizeof(double) + sizeof(int4), not as
			 * sizeof(TimeTzADT), so that any garbage pad bytes in the
			 * structure won't be included in the hash!
			 */
			len = sizeof(timetzptr->time) + sizeof(timetzptr->zone);
			break;

		case INTERVALOID:		/* @ <number> <units>, time interval */
			intervalptr = DatumGetIntervalP(datum);
			buf = (unsigned char *) intervalptr;

			/*
			 * Specify hash length as sizeof(double) + sizeof(int4), not as
			 * sizeof(Interval), so that any garbage pad bytes in the
			 * structure won't be included in the hash!
			 */
			len = sizeof(intervalptr->time) + sizeof(intervalptr->month);
			break;

		case ABSTIMEOID:
			abstime_buf = DatumGetAbsoluteTime(datum);

			if (abstime_buf == INVALID_ABSTIME)
			{
				/* hash to a constant value */
				invalidbuf = INVALID_VAL;
				len = sizeof(invalidbuf);
				buf = &invalidbuf;
			}
			else
			{
				len = sizeof(abstime_buf);
				buf = &abstime_buf;
			}

			break;

		case RELTIMEOID:
			reltime_buf = DatumGetRelativeTime(datum);

			if (reltime_buf == INVALID_RELTIME)
			{
				/* hash to a constant value */
				invalidbuf = INVALID_VAL;
				len = sizeof(invalidbuf);
				buf = &invalidbuf;
			}
			else
			{
				len = sizeof(reltime_buf);
				buf = &reltime_buf;
			}

			break;

		case TINTERVALOID:
			tinterval = DatumGetTimeInterval(datum);

			/*
			 * check if a valid interval. the '0' status code stands for
			 * T_INTERVAL_INVAL which is defined in nabstime.c. We use the
			 * actual value instead of defining it again here.
			 */
			if (tinterval->status == 0 ||
				tinterval->data[0] == INVALID_ABSTIME ||
				tinterval->data[1] == INVALID_ABSTIME)
			{
				/* hash to a constant value */
				invalidbuf = INVALID_VAL;
				len = sizeof(invalidbuf);
				buf = &invalidbuf;
			}
			else
			{
				/* normalize on length of the time interval */
				tinterval_len = tinterval->data[1] - tinterval->data[0];
				len = sizeof(tinterval_len);
				buf = &tinterval_len;
			}

			break;

			/*
			 * ======= NETWORK TYPES ========
			 */
		case INETOID:
		case CIDROID:

			inetptr = DatumGetInetP(datum);
			len = inet_getkey(inetptr, inet_hkey, sizeof(inet_hkey));	/* fill-in inet_key &
																		 * get len */
			buf = inet_hkey;
			break;

		case MACADDROID:

			macptr = DatumGetMacaddrP(datum);
			len = sizeof(macaddr);
			buf = (unsigned char *) macptr;
			break;

			/*
			 * ======== BIT STRINGS ========
			 */
		case BITOID:
		case VARBITOID:

			/*
			 * Note that these are essentially strings. we don't need to worry
			 * about '10' and '010' to compare, b/c they will not, by design.
			 * (see SQL standard, and varbit.c)
			 */
			vbitptr = DatumGetVarBitP(datum);
			len = VARBITBYTES(vbitptr);
			buf = (char *) VARBITS(vbitptr);
			break;

			/*
			 * ======= other types =======
			 */
		case BOOLOID:			/* boolean, 'true'/'false' */
			bool_buf = DatumGetBool(datum);
			buf = &bool_buf;
			len = sizeof(bool_buf);
			break;

			/*
			 * ANYARRAY is a pseudo-type. We use it to include any of the
			 * array types (OIDs 1007-1033 in pg_type.h). caller needs to be
			 * sure the type is ANYARRAYOID before calling cdbhash on an array
			 * (INSERT and COPY do so).
			 */
		case ANYARRAYOID:

			arrbuf = DatumGetArrayTypeP(datum);
			len = VARSIZE(arrbuf) - VARHDRSZ;
			buf = VARDATA(arrbuf);
			break;

		case OIDVECTOROID:
			oidvec_buf = (oidvector *) DatumGetPointer(datum);
			len = oidvec_buf->dim1 * sizeof(Oid);
			buf = oidvec_buf->values;
			break;

		case CASHOID:			/* cash is stored in int64 internally */
			cash_buf = DatumGetCash(datum);
			len = sizeof(Cash);
			buf = &cash_buf;
			break;

			/* pg_uuid_t is defined as a char array of size UUID_LEN in uuid.c */
		case UUIDOID:
			uuid_buf = DatumGetUUIDP(datum);
			len = UUID_LEN;
			buf = (char *) uuid_buf;
			break;

		case COMPLEXOID:
			complex_ptr = DatumGetComplexP(datum);
			complex_real = re(complex_ptr);
			complex_imag = im(complex_ptr);

			/*
			 * On IEEE-float machines, minus zero and zero have different bit
			 * patterns but should compare as equal.  We must ensure that they
			 * have the same hash value, which is most easily done this way:
			 */
			if (complex_real == (float8) 0)
			{
				complex_real = 0.0;
			}
			if (complex_imag == (float8) 0)
			{
				complex_imag = 0.0;
			}

			INIT_COMPLEX(&complex_buf, complex_real, complex_imag);
			len = sizeof(Complex);
			buf = (unsigned char *) &complex_buf;
			break;

		default:
			ereport(ERROR,
					(errcode(ERRCODE_GP_FEATURE_NOT_YET),
					 errmsg("Type %u is not hashable.", type)));

	}							/* switch(type) */

	/* do the hash using the selected algorithm */
	hashFn(clientData, buf, len);
	if (tofree)
		pfree(tofree);
}