mirror of
				https://github.com/postgres/postgres.git
				synced 2025-10-29 22:49:41 +03:00 
			
		
		
		
	Some data types under adt/ have separate header files, but most simple
ones do not, and their public functions are defined in builtins.h.  As
the patches improving geometric types will require making additional
functions public, this seems like a good opportunity to create a header
for floats types.
Commit 1acf757255 made _cmp functions public to solve NaN issues locally
for GiST indexes.  This patch reworks it in favour of a more widely
applicable API.  The API uses inline functions, as they are easier to
use compared to macros, and avoid double-evaluation hazards.
Author: Emre Hasegeli
Reviewed-by: Kyotaro Horiguchi
Discussion: https://www.postgresql.org/message-id/CAE2gYzxF7-5djV6-cEvqQu-fNsnt%3DEqbOURx7ZDg%2BVv6ZMTWbg%40mail.gmail.com
		
	
		
			
				
	
	
		
			407 lines
		
	
	
		
			9.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			407 lines
		
	
	
		
			9.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * contrib/btree_gist/btree_ts.c
 | |
|  */
 | |
| #include "postgres.h"
 | |
| 
 | |
| #include <limits.h>
 | |
| 
 | |
| #include "btree_gist.h"
 | |
| #include "btree_utils_num.h"
 | |
| #include "utils/builtins.h"
 | |
| #include "utils/datetime.h"
 | |
| #include "utils/float.h"
 | |
| 
 | |
| typedef struct
 | |
| {
 | |
| 	Timestamp	lower;
 | |
| 	Timestamp	upper;
 | |
| } tsKEY;
 | |
| 
 | |
| /*
 | |
| ** timestamp ops
 | |
| */
 | |
| PG_FUNCTION_INFO_V1(gbt_ts_compress);
 | |
| PG_FUNCTION_INFO_V1(gbt_tstz_compress);
 | |
| PG_FUNCTION_INFO_V1(gbt_ts_fetch);
 | |
| PG_FUNCTION_INFO_V1(gbt_ts_union);
 | |
| PG_FUNCTION_INFO_V1(gbt_ts_picksplit);
 | |
| PG_FUNCTION_INFO_V1(gbt_ts_consistent);
 | |
| PG_FUNCTION_INFO_V1(gbt_ts_distance);
 | |
| PG_FUNCTION_INFO_V1(gbt_tstz_consistent);
 | |
| PG_FUNCTION_INFO_V1(gbt_tstz_distance);
 | |
| PG_FUNCTION_INFO_V1(gbt_ts_penalty);
 | |
| PG_FUNCTION_INFO_V1(gbt_ts_same);
 | |
| 
 | |
| 
 | |
| #ifdef USE_FLOAT8_BYVAL
 | |
| #define TimestampGetDatumFast(X) TimestampGetDatum(X)
 | |
| #else
 | |
| #define TimestampGetDatumFast(X) PointerGetDatum(&(X))
 | |
| #endif
 | |
| 
 | |
| 
 | |
| static bool
 | |
| gbt_tsgt(const void *a, const void *b, FmgrInfo *flinfo)
 | |
| {
 | |
| 	const Timestamp *aa = (const Timestamp *) a;
 | |
| 	const Timestamp *bb = (const Timestamp *) b;
 | |
| 
 | |
| 	return DatumGetBool(DirectFunctionCall2(timestamp_gt,
 | |
| 											TimestampGetDatumFast(*aa),
 | |
| 											TimestampGetDatumFast(*bb)));
 | |
| }
 | |
| 
 | |
| static bool
 | |
| gbt_tsge(const void *a, const void *b, FmgrInfo *flinfo)
 | |
| {
 | |
| 	const Timestamp *aa = (const Timestamp *) a;
 | |
| 	const Timestamp *bb = (const Timestamp *) b;
 | |
| 
 | |
| 	return DatumGetBool(DirectFunctionCall2(timestamp_ge,
 | |
| 											TimestampGetDatumFast(*aa),
 | |
| 											TimestampGetDatumFast(*bb)));
 | |
| }
 | |
| 
 | |
| static bool
 | |
| gbt_tseq(const void *a, const void *b, FmgrInfo *flinfo)
 | |
| {
 | |
| 	const Timestamp *aa = (const Timestamp *) a;
 | |
| 	const Timestamp *bb = (const Timestamp *) b;
 | |
| 
 | |
| 	return DatumGetBool(DirectFunctionCall2(timestamp_eq,
 | |
| 											TimestampGetDatumFast(*aa),
 | |
| 											TimestampGetDatumFast(*bb)));
 | |
| }
 | |
| 
 | |
| static bool
 | |
| gbt_tsle(const void *a, const void *b, FmgrInfo *flinfo)
 | |
| {
 | |
| 	const Timestamp *aa = (const Timestamp *) a;
 | |
| 	const Timestamp *bb = (const Timestamp *) b;
 | |
| 
 | |
| 	return DatumGetBool(DirectFunctionCall2(timestamp_le,
 | |
| 											TimestampGetDatumFast(*aa),
 | |
| 											TimestampGetDatumFast(*bb)));
 | |
| }
 | |
| 
 | |
| static bool
 | |
| gbt_tslt(const void *a, const void *b, FmgrInfo *flinfo)
 | |
| {
 | |
| 	const Timestamp *aa = (const Timestamp *) a;
 | |
| 	const Timestamp *bb = (const Timestamp *) b;
 | |
| 
 | |
| 	return DatumGetBool(DirectFunctionCall2(timestamp_lt,
 | |
| 											TimestampGetDatumFast(*aa),
 | |
| 											TimestampGetDatumFast(*bb)));
 | |
| }
 | |
| 
 | |
| 
 | |
| static int
 | |
| gbt_tskey_cmp(const void *a, const void *b, FmgrInfo *flinfo)
 | |
| {
 | |
| 	tsKEY	   *ia = (tsKEY *) (((const Nsrt *) a)->t);
 | |
| 	tsKEY	   *ib = (tsKEY *) (((const Nsrt *) b)->t);
 | |
| 	int			res;
 | |
| 
 | |
| 	res = DatumGetInt32(DirectFunctionCall2(timestamp_cmp, TimestampGetDatumFast(ia->lower), TimestampGetDatumFast(ib->lower)));
 | |
| 	if (res == 0)
 | |
| 		return DatumGetInt32(DirectFunctionCall2(timestamp_cmp, TimestampGetDatumFast(ia->upper), TimestampGetDatumFast(ib->upper)));
 | |
| 
 | |
| 	return res;
 | |
| }
 | |
| 
 | |
| static float8
 | |
| gbt_ts_dist(const void *a, const void *b, FmgrInfo *flinfo)
 | |
| {
 | |
| 	const Timestamp *aa = (const Timestamp *) a;
 | |
| 	const Timestamp *bb = (const Timestamp *) b;
 | |
| 	Interval   *i;
 | |
| 
 | |
| 	if (TIMESTAMP_NOT_FINITE(*aa) || TIMESTAMP_NOT_FINITE(*bb))
 | |
| 		return get_float8_infinity();
 | |
| 
 | |
| 	i = DatumGetIntervalP(DirectFunctionCall2(timestamp_mi,
 | |
| 											  TimestampGetDatumFast(*aa),
 | |
| 											  TimestampGetDatumFast(*bb)));
 | |
| 	return (float8) Abs(INTERVAL_TO_SEC(i));
 | |
| }
 | |
| 
 | |
| 
 | |
| static const gbtree_ninfo tinfo =
 | |
| {
 | |
| 	gbt_t_ts,
 | |
| 	sizeof(Timestamp),
 | |
| 	16,							/* sizeof(gbtreekey16) */
 | |
| 	gbt_tsgt,
 | |
| 	gbt_tsge,
 | |
| 	gbt_tseq,
 | |
| 	gbt_tsle,
 | |
| 	gbt_tslt,
 | |
| 	gbt_tskey_cmp,
 | |
| 	gbt_ts_dist
 | |
| };
 | |
| 
 | |
| 
 | |
| PG_FUNCTION_INFO_V1(ts_dist);
 | |
| Datum
 | |
| ts_dist(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	Timestamp	a = PG_GETARG_TIMESTAMP(0);
 | |
| 	Timestamp	b = PG_GETARG_TIMESTAMP(1);
 | |
| 	Interval   *r;
 | |
| 
 | |
| 	if (TIMESTAMP_NOT_FINITE(a) || TIMESTAMP_NOT_FINITE(b))
 | |
| 	{
 | |
| 		Interval   *p = palloc(sizeof(Interval));
 | |
| 
 | |
| 		p->day = INT_MAX;
 | |
| 		p->month = INT_MAX;
 | |
| 		p->time = PG_INT64_MAX;
 | |
| 		PG_RETURN_INTERVAL_P(p);
 | |
| 	}
 | |
| 	else
 | |
| 		r = DatumGetIntervalP(DirectFunctionCall2(timestamp_mi,
 | |
| 												  PG_GETARG_DATUM(0),
 | |
| 												  PG_GETARG_DATUM(1)));
 | |
| 	PG_RETURN_INTERVAL_P(abs_interval(r));
 | |
| }
 | |
| 
 | |
| PG_FUNCTION_INFO_V1(tstz_dist);
 | |
| Datum
 | |
| tstz_dist(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	TimestampTz a = PG_GETARG_TIMESTAMPTZ(0);
 | |
| 	TimestampTz b = PG_GETARG_TIMESTAMPTZ(1);
 | |
| 	Interval   *r;
 | |
| 
 | |
| 	if (TIMESTAMP_NOT_FINITE(a) || TIMESTAMP_NOT_FINITE(b))
 | |
| 	{
 | |
| 		Interval   *p = palloc(sizeof(Interval));
 | |
| 
 | |
| 		p->day = INT_MAX;
 | |
| 		p->month = INT_MAX;
 | |
| 		p->time = PG_INT64_MAX;
 | |
| 		PG_RETURN_INTERVAL_P(p);
 | |
| 	}
 | |
| 
 | |
| 	r = DatumGetIntervalP(DirectFunctionCall2(timestamp_mi,
 | |
| 											  PG_GETARG_DATUM(0),
 | |
| 											  PG_GETARG_DATUM(1)));
 | |
| 	PG_RETURN_INTERVAL_P(abs_interval(r));
 | |
| }
 | |
| 
 | |
| 
 | |
| /**************************************************
 | |
|  * timestamp ops
 | |
|  **************************************************/
 | |
| 
 | |
| 
 | |
| static inline Timestamp
 | |
| tstz_to_ts_gmt(TimestampTz ts)
 | |
| {
 | |
| 	/* No timezone correction is needed, since GMT is offset 0 by definition */
 | |
| 	return (Timestamp) ts;
 | |
| }
 | |
| 
 | |
| 
 | |
| Datum
 | |
| gbt_ts_compress(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
 | |
| 
 | |
| 	PG_RETURN_POINTER(gbt_num_compress(entry, &tinfo));
 | |
| }
 | |
| 
 | |
| 
 | |
| Datum
 | |
| gbt_tstz_compress(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
 | |
| 	GISTENTRY  *retval;
 | |
| 
 | |
| 	if (entry->leafkey)
 | |
| 	{
 | |
| 		tsKEY	   *r = (tsKEY *) palloc(sizeof(tsKEY));
 | |
| 		TimestampTz ts = DatumGetTimestampTz(entry->key);
 | |
| 		Timestamp	gmt;
 | |
| 
 | |
| 		gmt = tstz_to_ts_gmt(ts);
 | |
| 
 | |
| 		retval = palloc(sizeof(GISTENTRY));
 | |
| 		r->lower = r->upper = gmt;
 | |
| 		gistentryinit(*retval, PointerGetDatum(r),
 | |
| 					  entry->rel, entry->page,
 | |
| 					  entry->offset, false);
 | |
| 	}
 | |
| 	else
 | |
| 		retval = entry;
 | |
| 
 | |
| 	PG_RETURN_POINTER(retval);
 | |
| }
 | |
| 
 | |
| Datum
 | |
| gbt_ts_fetch(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
 | |
| 
 | |
| 	PG_RETURN_POINTER(gbt_num_fetch(entry, &tinfo));
 | |
| }
 | |
| 
 | |
| Datum
 | |
| gbt_ts_consistent(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
 | |
| 	Timestamp	query = PG_GETARG_TIMESTAMP(1);
 | |
| 	StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
 | |
| 
 | |
| 	/* Oid		subtype = PG_GETARG_OID(3); */
 | |
| 	bool	   *recheck = (bool *) PG_GETARG_POINTER(4);
 | |
| 	tsKEY	   *kkk = (tsKEY *) DatumGetPointer(entry->key);
 | |
| 	GBT_NUMKEY_R key;
 | |
| 
 | |
| 	/* All cases served by this function are exact */
 | |
| 	*recheck = false;
 | |
| 
 | |
| 	key.lower = (GBT_NUMKEY *) &kkk->lower;
 | |
| 	key.upper = (GBT_NUMKEY *) &kkk->upper;
 | |
| 
 | |
| 	PG_RETURN_BOOL(
 | |
| 				   gbt_num_consistent(&key, (void *) &query, &strategy, GIST_LEAF(entry), &tinfo, fcinfo->flinfo)
 | |
| 		);
 | |
| }
 | |
| 
 | |
| Datum
 | |
| gbt_ts_distance(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
 | |
| 	Timestamp	query = PG_GETARG_TIMESTAMP(1);
 | |
| 
 | |
| 	/* Oid		subtype = PG_GETARG_OID(3); */
 | |
| 	tsKEY	   *kkk = (tsKEY *) DatumGetPointer(entry->key);
 | |
| 	GBT_NUMKEY_R key;
 | |
| 
 | |
| 	key.lower = (GBT_NUMKEY *) &kkk->lower;
 | |
| 	key.upper = (GBT_NUMKEY *) &kkk->upper;
 | |
| 
 | |
| 	PG_RETURN_FLOAT8(
 | |
| 					 gbt_num_distance(&key, (void *) &query, GIST_LEAF(entry), &tinfo, fcinfo->flinfo)
 | |
| 		);
 | |
| }
 | |
| 
 | |
| Datum
 | |
| gbt_tstz_consistent(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
 | |
| 	TimestampTz query = PG_GETARG_TIMESTAMPTZ(1);
 | |
| 	StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
 | |
| 
 | |
| 	/* Oid		subtype = PG_GETARG_OID(3); */
 | |
| 	bool	   *recheck = (bool *) PG_GETARG_POINTER(4);
 | |
| 	char	   *kkk = (char *) DatumGetPointer(entry->key);
 | |
| 	GBT_NUMKEY_R key;
 | |
| 	Timestamp	qqq;
 | |
| 
 | |
| 	/* All cases served by this function are exact */
 | |
| 	*recheck = false;
 | |
| 
 | |
| 	key.lower = (GBT_NUMKEY *) &kkk[0];
 | |
| 	key.upper = (GBT_NUMKEY *) &kkk[MAXALIGN(tinfo.size)];
 | |
| 	qqq = tstz_to_ts_gmt(query);
 | |
| 
 | |
| 	PG_RETURN_BOOL(
 | |
| 				   gbt_num_consistent(&key, (void *) &qqq, &strategy, GIST_LEAF(entry), &tinfo, fcinfo->flinfo)
 | |
| 		);
 | |
| }
 | |
| 
 | |
| Datum
 | |
| gbt_tstz_distance(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
 | |
| 	TimestampTz query = PG_GETARG_TIMESTAMPTZ(1);
 | |
| 
 | |
| 	/* Oid		subtype = PG_GETARG_OID(3); */
 | |
| 	char	   *kkk = (char *) DatumGetPointer(entry->key);
 | |
| 	GBT_NUMKEY_R key;
 | |
| 	Timestamp	qqq;
 | |
| 
 | |
| 	key.lower = (GBT_NUMKEY *) &kkk[0];
 | |
| 	key.upper = (GBT_NUMKEY *) &kkk[MAXALIGN(tinfo.size)];
 | |
| 	qqq = tstz_to_ts_gmt(query);
 | |
| 
 | |
| 	PG_RETURN_FLOAT8(
 | |
| 					 gbt_num_distance(&key, (void *) &qqq, GIST_LEAF(entry), &tinfo, fcinfo->flinfo)
 | |
| 		);
 | |
| }
 | |
| 
 | |
| 
 | |
| Datum
 | |
| gbt_ts_union(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
 | |
| 	void	   *out = palloc(sizeof(tsKEY));
 | |
| 
 | |
| 	*(int *) PG_GETARG_POINTER(1) = sizeof(tsKEY);
 | |
| 	PG_RETURN_POINTER(gbt_num_union((void *) out, entryvec, &tinfo, fcinfo->flinfo));
 | |
| }
 | |
| 
 | |
| 
 | |
| #define penalty_check_max_float(val) do { \
 | |
| 		if ( val > FLT_MAX ) \
 | |
| 				val = FLT_MAX; \
 | |
| 		if ( val < -FLT_MAX ) \
 | |
| 				val = -FLT_MAX; \
 | |
| } while(false);
 | |
| 
 | |
| 
 | |
| Datum
 | |
| gbt_ts_penalty(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	tsKEY	   *origentry = (tsKEY *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(0))->key);
 | |
| 	tsKEY	   *newentry = (tsKEY *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(1))->key);
 | |
| 	float	   *result = (float *) PG_GETARG_POINTER(2);
 | |
| 
 | |
| 	double		orgdbl[2],
 | |
| 				newdbl[2];
 | |
| 
 | |
| 	/*
 | |
| 	 * We are always using "double" timestamps here. Precision should be good
 | |
| 	 * enough.
 | |
| 	 */
 | |
| 	orgdbl[0] = ((double) origentry->lower);
 | |
| 	orgdbl[1] = ((double) origentry->upper);
 | |
| 	newdbl[0] = ((double) newentry->lower);
 | |
| 	newdbl[1] = ((double) newentry->upper);
 | |
| 
 | |
| 	penalty_check_max_float(orgdbl[0]);
 | |
| 	penalty_check_max_float(orgdbl[1]);
 | |
| 	penalty_check_max_float(newdbl[0]);
 | |
| 	penalty_check_max_float(newdbl[1]);
 | |
| 
 | |
| 	penalty_num(result, orgdbl[0], orgdbl[1], newdbl[0], newdbl[1]);
 | |
| 
 | |
| 	PG_RETURN_POINTER(result);
 | |
| 
 | |
| }
 | |
| 
 | |
| 
 | |
| Datum
 | |
| gbt_ts_picksplit(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	PG_RETURN_POINTER(gbt_num_picksplit(
 | |
| 										(GistEntryVector *) PG_GETARG_POINTER(0),
 | |
| 										(GIST_SPLITVEC *) PG_GETARG_POINTER(1),
 | |
| 										&tinfo, fcinfo->flinfo
 | |
| 										));
 | |
| }
 | |
| 
 | |
| Datum
 | |
| gbt_ts_same(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	tsKEY	   *b1 = (tsKEY *) PG_GETARG_POINTER(0);
 | |
| 	tsKEY	   *b2 = (tsKEY *) PG_GETARG_POINTER(1);
 | |
| 	bool	   *result = (bool *) PG_GETARG_POINTER(2);
 | |
| 
 | |
| 	*result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo);
 | |
| 	PG_RETURN_POINTER(result);
 | |
| }
 |