mirror of
				https://github.com/postgres/postgres.git
				synced 2025-10-28 11:55:03 +03:00 
			
		
		
		
	By project convention, these names should include "P" when dealing with a pointer type; that is, if the result of a GETARG macro is of type FOO *, it should be called PG_GETARG_FOO_P not just PG_GETARG_FOO. Some newer types such as JSONB and ranges had not followed the convention, and a number of contrib modules hadn't gotten that memo either. Rename the offending macros to improve consistency. In passing, fix a few places that thought PG_DETOAST_DATUM() returns a Datum; it does not, it returns "struct varlena *". Applying DatumGetPointer to that happens not to cause any bad effects today, but it's formally wrong. Also, adjust an ltree macro that was designed without any thought for what pgindent would do with it. This is all cosmetic and shouldn't have any impact on generated code. Mark Dilger, some further tweaks by me Discussion: https://postgr.es/m/EA5676F4-766F-4F38-8348-ECC7DB427C6A@gmail.com
		
			
				
	
	
		
			1256 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1256 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * contrib/hstore/hstore_op.c
 | |
|  */
 | |
| #include "postgres.h"
 | |
| 
 | |
| #include "access/hash.h"
 | |
| #include "access/htup_details.h"
 | |
| #include "catalog/pg_type.h"
 | |
| #include "funcapi.h"
 | |
| #include "utils/builtins.h"
 | |
| #include "utils/memutils.h"
 | |
| 
 | |
| #include "hstore.h"
 | |
| 
 | |
| /* old names for C functions */
 | |
| HSTORE_POLLUTE(hstore_fetchval, fetchval);
 | |
| HSTORE_POLLUTE(hstore_exists, exists);
 | |
| HSTORE_POLLUTE(hstore_defined, defined);
 | |
| HSTORE_POLLUTE(hstore_delete, delete);
 | |
| HSTORE_POLLUTE(hstore_concat, hs_concat);
 | |
| HSTORE_POLLUTE(hstore_contains, hs_contains);
 | |
| HSTORE_POLLUTE(hstore_contained, hs_contained);
 | |
| HSTORE_POLLUTE(hstore_akeys, akeys);
 | |
| HSTORE_POLLUTE(hstore_avals, avals);
 | |
| HSTORE_POLLUTE(hstore_skeys, skeys);
 | |
| HSTORE_POLLUTE(hstore_svals, svals);
 | |
| HSTORE_POLLUTE(hstore_each, each);
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * We're often finding a sequence of keys in ascending order. The
 | |
|  * "lowbound" parameter is used to cache lower bounds of searches
 | |
|  * between calls, based on this assumption. Pass NULL for it for
 | |
|  * one-off or unordered searches.
 | |
|  */
 | |
| int
 | |
| hstoreFindKey(HStore *hs, int *lowbound, char *key, int keylen)
 | |
| {
 | |
| 	HEntry	   *entries = ARRPTR(hs);
 | |
| 	int			stopLow = lowbound ? *lowbound : 0;
 | |
| 	int			stopHigh = HS_COUNT(hs);
 | |
| 	int			stopMiddle;
 | |
| 	char	   *base = STRPTR(hs);
 | |
| 
 | |
| 	while (stopLow < stopHigh)
 | |
| 	{
 | |
| 		int			difference;
 | |
| 
 | |
| 		stopMiddle = stopLow + (stopHigh - stopLow) / 2;
 | |
| 
 | |
| 		if (HSTORE_KEYLEN(entries, stopMiddle) == keylen)
 | |
| 			difference = memcmp(HSTORE_KEY(entries, base, stopMiddle), key, keylen);
 | |
| 		else
 | |
| 			difference = (HSTORE_KEYLEN(entries, stopMiddle) > keylen) ? 1 : -1;
 | |
| 
 | |
| 		if (difference == 0)
 | |
| 		{
 | |
| 			if (lowbound)
 | |
| 				*lowbound = stopMiddle + 1;
 | |
| 			return stopMiddle;
 | |
| 		}
 | |
| 		else if (difference < 0)
 | |
| 			stopLow = stopMiddle + 1;
 | |
| 		else
 | |
| 			stopHigh = stopMiddle;
 | |
| 	}
 | |
| 
 | |
| 	if (lowbound)
 | |
| 		*lowbound = stopLow;
 | |
| 	return -1;
 | |
| }
 | |
| 
 | |
| Pairs *
 | |
| hstoreArrayToPairs(ArrayType *a, int *npairs)
 | |
| {
 | |
| 	Datum	   *key_datums;
 | |
| 	bool	   *key_nulls;
 | |
| 	int			key_count;
 | |
| 	Pairs	   *key_pairs;
 | |
| 	int			bufsiz;
 | |
| 	int			i,
 | |
| 				j;
 | |
| 
 | |
| 	deconstruct_array(a,
 | |
| 					  TEXTOID, -1, false, 'i',
 | |
| 					  &key_datums, &key_nulls, &key_count);
 | |
| 
 | |
| 	if (key_count == 0)
 | |
| 	{
 | |
| 		*npairs = 0;
 | |
| 		return NULL;
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * A text array uses at least eight bytes per element, so any overflow in
 | |
| 	 * "key_count * sizeof(Pairs)" is small enough for palloc() to catch.
 | |
| 	 * However, credible improvements to the array format could invalidate
 | |
| 	 * that assumption.  Therefore, use an explicit check rather than relying
 | |
| 	 * on palloc() to complain.
 | |
| 	 */
 | |
| 	if (key_count > MaxAllocSize / sizeof(Pairs))
 | |
| 		ereport(ERROR,
 | |
| 				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 | |
| 				 errmsg("number of pairs (%d) exceeds the maximum allowed (%d)",
 | |
| 						key_count, (int) (MaxAllocSize / sizeof(Pairs)))));
 | |
| 
 | |
| 	key_pairs = palloc(sizeof(Pairs) * key_count);
 | |
| 
 | |
| 	for (i = 0, j = 0; i < key_count; i++)
 | |
| 	{
 | |
| 		if (!key_nulls[i])
 | |
| 		{
 | |
| 			key_pairs[j].key = VARDATA(key_datums[i]);
 | |
| 			key_pairs[j].keylen = VARSIZE(key_datums[i]) - VARHDRSZ;
 | |
| 			key_pairs[j].val = NULL;
 | |
| 			key_pairs[j].vallen = 0;
 | |
| 			key_pairs[j].needfree = 0;
 | |
| 			key_pairs[j].isnull = 1;
 | |
| 			j++;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	*npairs = hstoreUniquePairs(key_pairs, j, &bufsiz);
 | |
| 
 | |
| 	return key_pairs;
 | |
| }
 | |
| 
 | |
| 
 | |
| PG_FUNCTION_INFO_V1(hstore_fetchval);
 | |
| Datum
 | |
| hstore_fetchval(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	HStore	   *hs = PG_GETARG_HSTORE_P(0);
 | |
| 	text	   *key = PG_GETARG_TEXT_PP(1);
 | |
| 	HEntry	   *entries = ARRPTR(hs);
 | |
| 	text	   *out;
 | |
| 	int			idx = hstoreFindKey(hs, NULL,
 | |
| 									VARDATA_ANY(key), VARSIZE_ANY_EXHDR(key));
 | |
| 
 | |
| 	if (idx < 0 || HSTORE_VALISNULL(entries, idx))
 | |
| 		PG_RETURN_NULL();
 | |
| 
 | |
| 	out = cstring_to_text_with_len(HSTORE_VAL(entries, STRPTR(hs), idx),
 | |
| 								   HSTORE_VALLEN(entries, idx));
 | |
| 
 | |
| 	PG_RETURN_TEXT_P(out);
 | |
| }
 | |
| 
 | |
| 
 | |
| PG_FUNCTION_INFO_V1(hstore_exists);
 | |
| Datum
 | |
| hstore_exists(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	HStore	   *hs = PG_GETARG_HSTORE_P(0);
 | |
| 	text	   *key = PG_GETARG_TEXT_PP(1);
 | |
| 	int			idx = hstoreFindKey(hs, NULL,
 | |
| 									VARDATA_ANY(key), VARSIZE_ANY_EXHDR(key));
 | |
| 
 | |
| 	PG_RETURN_BOOL(idx >= 0);
 | |
| }
 | |
| 
 | |
| 
 | |
| PG_FUNCTION_INFO_V1(hstore_exists_any);
 | |
| Datum
 | |
| hstore_exists_any(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	HStore	   *hs = PG_GETARG_HSTORE_P(0);
 | |
| 	ArrayType  *keys = PG_GETARG_ARRAYTYPE_P(1);
 | |
| 	int			nkeys;
 | |
| 	Pairs	   *key_pairs = hstoreArrayToPairs(keys, &nkeys);
 | |
| 	int			i;
 | |
| 	int			lowbound = 0;
 | |
| 	bool		res = false;
 | |
| 
 | |
| 	/*
 | |
| 	 * we exploit the fact that the pairs list is already sorted into strictly
 | |
| 	 * increasing order to narrow the hstoreFindKey search; each search can
 | |
| 	 * start one entry past the previous "found" entry, or at the lower bound
 | |
| 	 * of the last search.
 | |
| 	 */
 | |
| 	for (i = 0; i < nkeys; i++)
 | |
| 	{
 | |
| 		int			idx = hstoreFindKey(hs, &lowbound,
 | |
| 										key_pairs[i].key, key_pairs[i].keylen);
 | |
| 
 | |
| 		if (idx >= 0)
 | |
| 		{
 | |
| 			res = true;
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	PG_RETURN_BOOL(res);
 | |
| }
 | |
| 
 | |
| 
 | |
| PG_FUNCTION_INFO_V1(hstore_exists_all);
 | |
| Datum
 | |
| hstore_exists_all(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	HStore	   *hs = PG_GETARG_HSTORE_P(0);
 | |
| 	ArrayType  *keys = PG_GETARG_ARRAYTYPE_P(1);
 | |
| 	int			nkeys;
 | |
| 	Pairs	   *key_pairs = hstoreArrayToPairs(keys, &nkeys);
 | |
| 	int			i;
 | |
| 	int			lowbound = 0;
 | |
| 	bool		res = true;
 | |
| 
 | |
| 	/*
 | |
| 	 * we exploit the fact that the pairs list is already sorted into strictly
 | |
| 	 * increasing order to narrow the hstoreFindKey search; each search can
 | |
| 	 * start one entry past the previous "found" entry, or at the lower bound
 | |
| 	 * of the last search.
 | |
| 	 */
 | |
| 	for (i = 0; i < nkeys; i++)
 | |
| 	{
 | |
| 		int			idx = hstoreFindKey(hs, &lowbound,
 | |
| 										key_pairs[i].key, key_pairs[i].keylen);
 | |
| 
 | |
| 		if (idx < 0)
 | |
| 		{
 | |
| 			res = false;
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	PG_RETURN_BOOL(res);
 | |
| }
 | |
| 
 | |
| 
 | |
| PG_FUNCTION_INFO_V1(hstore_defined);
 | |
| Datum
 | |
| hstore_defined(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	HStore	   *hs = PG_GETARG_HSTORE_P(0);
 | |
| 	text	   *key = PG_GETARG_TEXT_PP(1);
 | |
| 	HEntry	   *entries = ARRPTR(hs);
 | |
| 	int			idx = hstoreFindKey(hs, NULL,
 | |
| 									VARDATA_ANY(key), VARSIZE_ANY_EXHDR(key));
 | |
| 	bool		res = (idx >= 0 && !HSTORE_VALISNULL(entries, idx));
 | |
| 
 | |
| 	PG_RETURN_BOOL(res);
 | |
| }
 | |
| 
 | |
| 
 | |
| PG_FUNCTION_INFO_V1(hstore_delete);
 | |
| Datum
 | |
| hstore_delete(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	HStore	   *hs = PG_GETARG_HSTORE_P(0);
 | |
| 	text	   *key = PG_GETARG_TEXT_PP(1);
 | |
| 	char	   *keyptr = VARDATA_ANY(key);
 | |
| 	int			keylen = VARSIZE_ANY_EXHDR(key);
 | |
| 	HStore	   *out = palloc(VARSIZE(hs));
 | |
| 	char	   *bufs,
 | |
| 			   *bufd,
 | |
| 			   *ptrd;
 | |
| 	HEntry	   *es,
 | |
| 			   *ed;
 | |
| 	int			i;
 | |
| 	int			count = HS_COUNT(hs);
 | |
| 	int			outcount = 0;
 | |
| 
 | |
| 	SET_VARSIZE(out, VARSIZE(hs));
 | |
| 	HS_SETCOUNT(out, count);	/* temporary! */
 | |
| 
 | |
| 	bufs = STRPTR(hs);
 | |
| 	es = ARRPTR(hs);
 | |
| 	bufd = ptrd = STRPTR(out);
 | |
| 	ed = ARRPTR(out);
 | |
| 
 | |
| 	for (i = 0; i < count; ++i)
 | |
| 	{
 | |
| 		int			len = HSTORE_KEYLEN(es, i);
 | |
| 		char	   *ptrs = HSTORE_KEY(es, bufs, i);
 | |
| 
 | |
| 		if (!(len == keylen && memcmp(ptrs, keyptr, keylen) == 0))
 | |
| 		{
 | |
| 			int			vallen = HSTORE_VALLEN(es, i);
 | |
| 
 | |
| 			HS_COPYITEM(ed, bufd, ptrd, ptrs, len, vallen,
 | |
| 						HSTORE_VALISNULL(es, i));
 | |
| 			++outcount;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	HS_FINALIZE(out, outcount, bufd, ptrd);
 | |
| 
 | |
| 	PG_RETURN_POINTER(out);
 | |
| }
 | |
| 
 | |
| 
 | |
| PG_FUNCTION_INFO_V1(hstore_delete_array);
 | |
| Datum
 | |
| hstore_delete_array(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	HStore	   *hs = PG_GETARG_HSTORE_P(0);
 | |
| 	HStore	   *out = palloc(VARSIZE(hs));
 | |
| 	int			hs_count = HS_COUNT(hs);
 | |
| 	char	   *ps,
 | |
| 			   *bufd,
 | |
| 			   *pd;
 | |
| 	HEntry	   *es,
 | |
| 			   *ed;
 | |
| 	int			i,
 | |
| 				j;
 | |
| 	int			outcount = 0;
 | |
| 	ArrayType  *key_array = PG_GETARG_ARRAYTYPE_P(1);
 | |
| 	int			nkeys;
 | |
| 	Pairs	   *key_pairs = hstoreArrayToPairs(key_array, &nkeys);
 | |
| 
 | |
| 	SET_VARSIZE(out, VARSIZE(hs));
 | |
| 	HS_SETCOUNT(out, hs_count); /* temporary! */
 | |
| 
 | |
| 	ps = STRPTR(hs);
 | |
| 	es = ARRPTR(hs);
 | |
| 	bufd = pd = STRPTR(out);
 | |
| 	ed = ARRPTR(out);
 | |
| 
 | |
| 	if (nkeys == 0)
 | |
| 	{
 | |
| 		/* return a copy of the input, unchanged */
 | |
| 		memcpy(out, hs, VARSIZE(hs));
 | |
| 		HS_FIXSIZE(out, hs_count);
 | |
| 		HS_SETCOUNT(out, hs_count);
 | |
| 		PG_RETURN_POINTER(out);
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * this is in effect a merge between hs and key_pairs, both of which are
 | |
| 	 * already sorted by (keylen,key); we take keys from hs only
 | |
| 	 */
 | |
| 
 | |
| 	for (i = j = 0; i < hs_count;)
 | |
| 	{
 | |
| 		int			difference;
 | |
| 
 | |
| 		if (j >= nkeys)
 | |
| 			difference = -1;
 | |
| 		else
 | |
| 		{
 | |
| 			int			skeylen = HSTORE_KEYLEN(es, i);
 | |
| 
 | |
| 			if (skeylen == key_pairs[j].keylen)
 | |
| 				difference = memcmp(HSTORE_KEY(es, ps, i),
 | |
| 									key_pairs[j].key,
 | |
| 									key_pairs[j].keylen);
 | |
| 			else
 | |
| 				difference = (skeylen > key_pairs[j].keylen) ? 1 : -1;
 | |
| 		}
 | |
| 
 | |
| 		if (difference > 0)
 | |
| 			++j;
 | |
| 		else if (difference == 0)
 | |
| 			++i, ++j;
 | |
| 		else
 | |
| 		{
 | |
| 			HS_COPYITEM(ed, bufd, pd,
 | |
| 						HSTORE_KEY(es, ps, i), HSTORE_KEYLEN(es, i),
 | |
| 						HSTORE_VALLEN(es, i), HSTORE_VALISNULL(es, i));
 | |
| 			++outcount;
 | |
| 			++i;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	HS_FINALIZE(out, outcount, bufd, pd);
 | |
| 
 | |
| 	PG_RETURN_POINTER(out);
 | |
| }
 | |
| 
 | |
| 
 | |
| PG_FUNCTION_INFO_V1(hstore_delete_hstore);
 | |
| Datum
 | |
| hstore_delete_hstore(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	HStore	   *hs = PG_GETARG_HSTORE_P(0);
 | |
| 	HStore	   *hs2 = PG_GETARG_HSTORE_P(1);
 | |
| 	HStore	   *out = palloc(VARSIZE(hs));
 | |
| 	int			hs_count = HS_COUNT(hs);
 | |
| 	int			hs2_count = HS_COUNT(hs2);
 | |
| 	char	   *ps,
 | |
| 			   *ps2,
 | |
| 			   *bufd,
 | |
| 			   *pd;
 | |
| 	HEntry	   *es,
 | |
| 			   *es2,
 | |
| 			   *ed;
 | |
| 	int			i,
 | |
| 				j;
 | |
| 	int			outcount = 0;
 | |
| 
 | |
| 	SET_VARSIZE(out, VARSIZE(hs));
 | |
| 	HS_SETCOUNT(out, hs_count); /* temporary! */
 | |
| 
 | |
| 	ps = STRPTR(hs);
 | |
| 	es = ARRPTR(hs);
 | |
| 	ps2 = STRPTR(hs2);
 | |
| 	es2 = ARRPTR(hs2);
 | |
| 	bufd = pd = STRPTR(out);
 | |
| 	ed = ARRPTR(out);
 | |
| 
 | |
| 	if (hs2_count == 0)
 | |
| 	{
 | |
| 		/* return a copy of the input, unchanged */
 | |
| 		memcpy(out, hs, VARSIZE(hs));
 | |
| 		HS_FIXSIZE(out, hs_count);
 | |
| 		HS_SETCOUNT(out, hs_count);
 | |
| 		PG_RETURN_POINTER(out);
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * this is in effect a merge between hs and hs2, both of which are already
 | |
| 	 * sorted by (keylen,key); we take keys from hs only; for equal keys, we
 | |
| 	 * take the value from hs unless the values are equal
 | |
| 	 */
 | |
| 
 | |
| 	for (i = j = 0; i < hs_count;)
 | |
| 	{
 | |
| 		int			difference;
 | |
| 
 | |
| 		if (j >= hs2_count)
 | |
| 			difference = -1;
 | |
| 		else
 | |
| 		{
 | |
| 			int			skeylen = HSTORE_KEYLEN(es, i);
 | |
| 			int			s2keylen = HSTORE_KEYLEN(es2, j);
 | |
| 
 | |
| 			if (skeylen == s2keylen)
 | |
| 				difference = memcmp(HSTORE_KEY(es, ps, i),
 | |
| 									HSTORE_KEY(es2, ps2, j),
 | |
| 									skeylen);
 | |
| 			else
 | |
| 				difference = (skeylen > s2keylen) ? 1 : -1;
 | |
| 		}
 | |
| 
 | |
| 		if (difference > 0)
 | |
| 			++j;
 | |
| 		else if (difference == 0)
 | |
| 		{
 | |
| 			int			svallen = HSTORE_VALLEN(es, i);
 | |
| 			int			snullval = HSTORE_VALISNULL(es, i);
 | |
| 
 | |
| 			if (snullval != HSTORE_VALISNULL(es2, j) ||
 | |
| 				(!snullval && (svallen != HSTORE_VALLEN(es2, j) ||
 | |
| 							   memcmp(HSTORE_VAL(es, ps, i),
 | |
| 									  HSTORE_VAL(es2, ps2, j),
 | |
| 									  svallen) != 0)))
 | |
| 			{
 | |
| 				HS_COPYITEM(ed, bufd, pd,
 | |
| 							HSTORE_KEY(es, ps, i), HSTORE_KEYLEN(es, i),
 | |
| 							svallen, snullval);
 | |
| 				++outcount;
 | |
| 			}
 | |
| 			++i, ++j;
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			HS_COPYITEM(ed, bufd, pd,
 | |
| 						HSTORE_KEY(es, ps, i), HSTORE_KEYLEN(es, i),
 | |
| 						HSTORE_VALLEN(es, i), HSTORE_VALISNULL(es, i));
 | |
| 			++outcount;
 | |
| 			++i;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	HS_FINALIZE(out, outcount, bufd, pd);
 | |
| 
 | |
| 	PG_RETURN_POINTER(out);
 | |
| }
 | |
| 
 | |
| 
 | |
| PG_FUNCTION_INFO_V1(hstore_concat);
 | |
| Datum
 | |
| hstore_concat(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	HStore	   *s1 = PG_GETARG_HSTORE_P(0);
 | |
| 	HStore	   *s2 = PG_GETARG_HSTORE_P(1);
 | |
| 	HStore	   *out = palloc(VARSIZE(s1) + VARSIZE(s2));
 | |
| 	char	   *ps1,
 | |
| 			   *ps2,
 | |
| 			   *bufd,
 | |
| 			   *pd;
 | |
| 	HEntry	   *es1,
 | |
| 			   *es2,
 | |
| 			   *ed;
 | |
| 	int			s1idx;
 | |
| 	int			s2idx;
 | |
| 	int			s1count = HS_COUNT(s1);
 | |
| 	int			s2count = HS_COUNT(s2);
 | |
| 	int			outcount = 0;
 | |
| 
 | |
| 	SET_VARSIZE(out, VARSIZE(s1) + VARSIZE(s2) - HSHRDSIZE);
 | |
| 	HS_SETCOUNT(out, s1count + s2count);
 | |
| 
 | |
| 	if (s1count == 0)
 | |
| 	{
 | |
| 		/* return a copy of the input, unchanged */
 | |
| 		memcpy(out, s2, VARSIZE(s2));
 | |
| 		HS_FIXSIZE(out, s2count);
 | |
| 		HS_SETCOUNT(out, s2count);
 | |
| 		PG_RETURN_POINTER(out);
 | |
| 	}
 | |
| 
 | |
| 	if (s2count == 0)
 | |
| 	{
 | |
| 		/* return a copy of the input, unchanged */
 | |
| 		memcpy(out, s1, VARSIZE(s1));
 | |
| 		HS_FIXSIZE(out, s1count);
 | |
| 		HS_SETCOUNT(out, s1count);
 | |
| 		PG_RETURN_POINTER(out);
 | |
| 	}
 | |
| 
 | |
| 	ps1 = STRPTR(s1);
 | |
| 	ps2 = STRPTR(s2);
 | |
| 	bufd = pd = STRPTR(out);
 | |
| 	es1 = ARRPTR(s1);
 | |
| 	es2 = ARRPTR(s2);
 | |
| 	ed = ARRPTR(out);
 | |
| 
 | |
| 	/*
 | |
| 	 * this is in effect a merge between s1 and s2, both of which are already
 | |
| 	 * sorted by (keylen,key); we take s2 for equal keys
 | |
| 	 */
 | |
| 
 | |
| 	for (s1idx = s2idx = 0; s1idx < s1count || s2idx < s2count; ++outcount)
 | |
| 	{
 | |
| 		int			difference;
 | |
| 
 | |
| 		if (s1idx >= s1count)
 | |
| 			difference = 1;
 | |
| 		else if (s2idx >= s2count)
 | |
| 			difference = -1;
 | |
| 		else
 | |
| 		{
 | |
| 			int			s1keylen = HSTORE_KEYLEN(es1, s1idx);
 | |
| 			int			s2keylen = HSTORE_KEYLEN(es2, s2idx);
 | |
| 
 | |
| 			if (s1keylen == s2keylen)
 | |
| 				difference = memcmp(HSTORE_KEY(es1, ps1, s1idx),
 | |
| 									HSTORE_KEY(es2, ps2, s2idx),
 | |
| 									s1keylen);
 | |
| 			else
 | |
| 				difference = (s1keylen > s2keylen) ? 1 : -1;
 | |
| 		}
 | |
| 
 | |
| 		if (difference >= 0)
 | |
| 		{
 | |
| 			HS_COPYITEM(ed, bufd, pd,
 | |
| 						HSTORE_KEY(es2, ps2, s2idx), HSTORE_KEYLEN(es2, s2idx),
 | |
| 						HSTORE_VALLEN(es2, s2idx), HSTORE_VALISNULL(es2, s2idx));
 | |
| 			++s2idx;
 | |
| 			if (difference == 0)
 | |
| 				++s1idx;
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			HS_COPYITEM(ed, bufd, pd,
 | |
| 						HSTORE_KEY(es1, ps1, s1idx), HSTORE_KEYLEN(es1, s1idx),
 | |
| 						HSTORE_VALLEN(es1, s1idx), HSTORE_VALISNULL(es1, s1idx));
 | |
| 			++s1idx;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	HS_FINALIZE(out, outcount, bufd, pd);
 | |
| 
 | |
| 	PG_RETURN_POINTER(out);
 | |
| }
 | |
| 
 | |
| 
 | |
| PG_FUNCTION_INFO_V1(hstore_slice_to_array);
 | |
| Datum
 | |
| hstore_slice_to_array(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	HStore	   *hs = PG_GETARG_HSTORE_P(0);
 | |
| 	HEntry	   *entries = ARRPTR(hs);
 | |
| 	char	   *ptr = STRPTR(hs);
 | |
| 	ArrayType  *key_array = PG_GETARG_ARRAYTYPE_P(1);
 | |
| 	ArrayType  *aout;
 | |
| 	Datum	   *key_datums;
 | |
| 	bool	   *key_nulls;
 | |
| 	Datum	   *out_datums;
 | |
| 	bool	   *out_nulls;
 | |
| 	int			key_count;
 | |
| 	int			i;
 | |
| 
 | |
| 	deconstruct_array(key_array,
 | |
| 					  TEXTOID, -1, false, 'i',
 | |
| 					  &key_datums, &key_nulls, &key_count);
 | |
| 
 | |
| 	if (key_count == 0)
 | |
| 	{
 | |
| 		aout = construct_empty_array(TEXTOID);
 | |
| 		PG_RETURN_POINTER(aout);
 | |
| 	}
 | |
| 
 | |
| 	out_datums = palloc(sizeof(Datum) * key_count);
 | |
| 	out_nulls = palloc(sizeof(bool) * key_count);
 | |
| 
 | |
| 	for (i = 0; i < key_count; ++i)
 | |
| 	{
 | |
| 		text	   *key = (text *) DatumGetPointer(key_datums[i]);
 | |
| 		int			idx;
 | |
| 
 | |
| 		if (key_nulls[i])
 | |
| 			idx = -1;
 | |
| 		else
 | |
| 			idx = hstoreFindKey(hs, NULL, VARDATA(key), VARSIZE(key) - VARHDRSZ);
 | |
| 
 | |
| 		if (idx < 0 || HSTORE_VALISNULL(entries, idx))
 | |
| 		{
 | |
| 			out_nulls[i] = true;
 | |
| 			out_datums[i] = (Datum) 0;
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			out_datums[i] = PointerGetDatum(
 | |
| 											cstring_to_text_with_len(HSTORE_VAL(entries, ptr, idx),
 | |
| 																	 HSTORE_VALLEN(entries, idx)));
 | |
| 			out_nulls[i] = false;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	aout = construct_md_array(out_datums, out_nulls,
 | |
| 							  ARR_NDIM(key_array),
 | |
| 							  ARR_DIMS(key_array),
 | |
| 							  ARR_LBOUND(key_array),
 | |
| 							  TEXTOID, -1, false, 'i');
 | |
| 
 | |
| 	PG_RETURN_POINTER(aout);
 | |
| }
 | |
| 
 | |
| 
 | |
| PG_FUNCTION_INFO_V1(hstore_slice_to_hstore);
 | |
| Datum
 | |
| hstore_slice_to_hstore(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	HStore	   *hs = PG_GETARG_HSTORE_P(0);
 | |
| 	HEntry	   *entries = ARRPTR(hs);
 | |
| 	char	   *ptr = STRPTR(hs);
 | |
| 	ArrayType  *key_array = PG_GETARG_ARRAYTYPE_P(1);
 | |
| 	HStore	   *out;
 | |
| 	int			nkeys;
 | |
| 	Pairs	   *key_pairs = hstoreArrayToPairs(key_array, &nkeys);
 | |
| 	Pairs	   *out_pairs;
 | |
| 	int			bufsiz;
 | |
| 	int			lastidx = 0;
 | |
| 	int			i;
 | |
| 	int			out_count = 0;
 | |
| 
 | |
| 	if (nkeys == 0)
 | |
| 	{
 | |
| 		out = hstorePairs(NULL, 0, 0);
 | |
| 		PG_RETURN_POINTER(out);
 | |
| 	}
 | |
| 
 | |
| 	/* hstoreArrayToPairs() checked overflow */
 | |
| 	out_pairs = palloc(sizeof(Pairs) * nkeys);
 | |
| 	bufsiz = 0;
 | |
| 
 | |
| 	/*
 | |
| 	 * we exploit the fact that the pairs list is already sorted into strictly
 | |
| 	 * increasing order to narrow the hstoreFindKey search; each search can
 | |
| 	 * start one entry past the previous "found" entry, or at the lower bound
 | |
| 	 * of the last search.
 | |
| 	 */
 | |
| 
 | |
| 	for (i = 0; i < nkeys; ++i)
 | |
| 	{
 | |
| 		int			idx = hstoreFindKey(hs, &lastidx,
 | |
| 										key_pairs[i].key, key_pairs[i].keylen);
 | |
| 
 | |
| 		if (idx >= 0)
 | |
| 		{
 | |
| 			out_pairs[out_count].key = key_pairs[i].key;
 | |
| 			bufsiz += (out_pairs[out_count].keylen = key_pairs[i].keylen);
 | |
| 			out_pairs[out_count].val = HSTORE_VAL(entries, ptr, idx);
 | |
| 			bufsiz += (out_pairs[out_count].vallen = HSTORE_VALLEN(entries, idx));
 | |
| 			out_pairs[out_count].isnull = HSTORE_VALISNULL(entries, idx);
 | |
| 			out_pairs[out_count].needfree = false;
 | |
| 			++out_count;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * we don't use uniquePairs here because we know that the pairs list is
 | |
| 	 * already sorted and uniq'ed.
 | |
| 	 */
 | |
| 
 | |
| 	out = hstorePairs(out_pairs, out_count, bufsiz);
 | |
| 
 | |
| 	PG_RETURN_POINTER(out);
 | |
| }
 | |
| 
 | |
| 
 | |
| PG_FUNCTION_INFO_V1(hstore_akeys);
 | |
| Datum
 | |
| hstore_akeys(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	HStore	   *hs = PG_GETARG_HSTORE_P(0);
 | |
| 	Datum	   *d;
 | |
| 	ArrayType  *a;
 | |
| 	HEntry	   *entries = ARRPTR(hs);
 | |
| 	char	   *base = STRPTR(hs);
 | |
| 	int			count = HS_COUNT(hs);
 | |
| 	int			i;
 | |
| 
 | |
| 	if (count == 0)
 | |
| 	{
 | |
| 		a = construct_empty_array(TEXTOID);
 | |
| 		PG_RETURN_POINTER(a);
 | |
| 	}
 | |
| 
 | |
| 	d = (Datum *) palloc(sizeof(Datum) * count);
 | |
| 
 | |
| 	for (i = 0; i < count; ++i)
 | |
| 	{
 | |
| 		text	   *t = cstring_to_text_with_len(HSTORE_KEY(entries, base, i),
 | |
| 												 HSTORE_KEYLEN(entries, i));
 | |
| 
 | |
| 		d[i] = PointerGetDatum(t);
 | |
| 	}
 | |
| 
 | |
| 	a = construct_array(d, count,
 | |
| 						TEXTOID, -1, false, 'i');
 | |
| 
 | |
| 	PG_RETURN_POINTER(a);
 | |
| }
 | |
| 
 | |
| 
 | |
| PG_FUNCTION_INFO_V1(hstore_avals);
 | |
| Datum
 | |
| hstore_avals(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	HStore	   *hs = PG_GETARG_HSTORE_P(0);
 | |
| 	Datum	   *d;
 | |
| 	bool	   *nulls;
 | |
| 	ArrayType  *a;
 | |
| 	HEntry	   *entries = ARRPTR(hs);
 | |
| 	char	   *base = STRPTR(hs);
 | |
| 	int			count = HS_COUNT(hs);
 | |
| 	int			lb = 1;
 | |
| 	int			i;
 | |
| 
 | |
| 	if (count == 0)
 | |
| 	{
 | |
| 		a = construct_empty_array(TEXTOID);
 | |
| 		PG_RETURN_POINTER(a);
 | |
| 	}
 | |
| 
 | |
| 	d = (Datum *) palloc(sizeof(Datum) * count);
 | |
| 	nulls = (bool *) palloc(sizeof(bool) * count);
 | |
| 
 | |
| 	for (i = 0; i < count; ++i)
 | |
| 	{
 | |
| 		if (HSTORE_VALISNULL(entries, i))
 | |
| 		{
 | |
| 			d[i] = (Datum) 0;
 | |
| 			nulls[i] = true;
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			text	   *item = cstring_to_text_with_len(HSTORE_VAL(entries, base, i),
 | |
| 														HSTORE_VALLEN(entries, i));
 | |
| 
 | |
| 			d[i] = PointerGetDatum(item);
 | |
| 			nulls[i] = false;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	a = construct_md_array(d, nulls, 1, &count, &lb,
 | |
| 						   TEXTOID, -1, false, 'i');
 | |
| 
 | |
| 	PG_RETURN_POINTER(a);
 | |
| }
 | |
| 
 | |
| 
 | |
| static ArrayType *
 | |
| hstore_to_array_internal(HStore *hs, int ndims)
 | |
| {
 | |
| 	HEntry	   *entries = ARRPTR(hs);
 | |
| 	char	   *base = STRPTR(hs);
 | |
| 	int			count = HS_COUNT(hs);
 | |
| 	int			out_size[2] = {0, 2};
 | |
| 	int			lb[2] = {1, 1};
 | |
| 	Datum	   *out_datums;
 | |
| 	bool	   *out_nulls;
 | |
| 	int			i;
 | |
| 
 | |
| 	Assert(ndims < 3);
 | |
| 
 | |
| 	if (count == 0 || ndims == 0)
 | |
| 		return construct_empty_array(TEXTOID);
 | |
| 
 | |
| 	out_size[0] = count * 2 / ndims;
 | |
| 	out_datums = palloc(sizeof(Datum) * count * 2);
 | |
| 	out_nulls = palloc(sizeof(bool) * count * 2);
 | |
| 
 | |
| 	for (i = 0; i < count; ++i)
 | |
| 	{
 | |
| 		text	   *key = cstring_to_text_with_len(HSTORE_KEY(entries, base, i),
 | |
| 												   HSTORE_KEYLEN(entries, i));
 | |
| 
 | |
| 		out_datums[i * 2] = PointerGetDatum(key);
 | |
| 		out_nulls[i * 2] = false;
 | |
| 
 | |
| 		if (HSTORE_VALISNULL(entries, i))
 | |
| 		{
 | |
| 			out_datums[i * 2 + 1] = (Datum) 0;
 | |
| 			out_nulls[i * 2 + 1] = true;
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			text	   *item = cstring_to_text_with_len(HSTORE_VAL(entries, base, i),
 | |
| 														HSTORE_VALLEN(entries, i));
 | |
| 
 | |
| 			out_datums[i * 2 + 1] = PointerGetDatum(item);
 | |
| 			out_nulls[i * 2 + 1] = false;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return construct_md_array(out_datums, out_nulls,
 | |
| 							  ndims, out_size, lb,
 | |
| 							  TEXTOID, -1, false, 'i');
 | |
| }
 | |
| 
 | |
| PG_FUNCTION_INFO_V1(hstore_to_array);
 | |
| Datum
 | |
| hstore_to_array(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	HStore	   *hs = PG_GETARG_HSTORE_P(0);
 | |
| 	ArrayType  *out = hstore_to_array_internal(hs, 1);
 | |
| 
 | |
| 	PG_RETURN_POINTER(out);
 | |
| }
 | |
| 
 | |
| PG_FUNCTION_INFO_V1(hstore_to_matrix);
 | |
| Datum
 | |
| hstore_to_matrix(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	HStore	   *hs = PG_GETARG_HSTORE_P(0);
 | |
| 	ArrayType  *out = hstore_to_array_internal(hs, 2);
 | |
| 
 | |
| 	PG_RETURN_POINTER(out);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Common initialization function for the various set-returning
 | |
|  * funcs. fcinfo is only passed if the function is to return a
 | |
|  * composite; it will be used to look up the return tupledesc.
 | |
|  * we stash a copy of the hstore in the multi-call context in
 | |
|  * case it was originally toasted. (At least I assume that's why;
 | |
|  * there was no explanatory comment in the original code. --AG)
 | |
|  */
 | |
| 
 | |
| static void
 | |
| setup_firstcall(FuncCallContext *funcctx, HStore *hs,
 | |
| 				FunctionCallInfoData *fcinfo)
 | |
| {
 | |
| 	MemoryContext oldcontext;
 | |
| 	HStore	   *st;
 | |
| 
 | |
| 	oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
 | |
| 
 | |
| 	st = (HStore *) palloc(VARSIZE(hs));
 | |
| 	memcpy(st, hs, VARSIZE(hs));
 | |
| 
 | |
| 	funcctx->user_fctx = (void *) st;
 | |
| 
 | |
| 	if (fcinfo)
 | |
| 	{
 | |
| 		TupleDesc	tupdesc;
 | |
| 
 | |
| 		/* Build a tuple descriptor for our result type */
 | |
| 		if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
 | |
| 			elog(ERROR, "return type must be a row type");
 | |
| 
 | |
| 		funcctx->tuple_desc = BlessTupleDesc(tupdesc);
 | |
| 	}
 | |
| 
 | |
| 	MemoryContextSwitchTo(oldcontext);
 | |
| }
 | |
| 
 | |
| 
 | |
| PG_FUNCTION_INFO_V1(hstore_skeys);
 | |
| Datum
 | |
| hstore_skeys(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	FuncCallContext *funcctx;
 | |
| 	HStore	   *hs;
 | |
| 	int			i;
 | |
| 
 | |
| 	if (SRF_IS_FIRSTCALL())
 | |
| 	{
 | |
| 		hs = PG_GETARG_HSTORE_P(0);
 | |
| 		funcctx = SRF_FIRSTCALL_INIT();
 | |
| 		setup_firstcall(funcctx, hs, NULL);
 | |
| 	}
 | |
| 
 | |
| 	funcctx = SRF_PERCALL_SETUP();
 | |
| 	hs = (HStore *) funcctx->user_fctx;
 | |
| 	i = funcctx->call_cntr;
 | |
| 
 | |
| 	if (i < HS_COUNT(hs))
 | |
| 	{
 | |
| 		HEntry	   *entries = ARRPTR(hs);
 | |
| 		text	   *item;
 | |
| 
 | |
| 		item = cstring_to_text_with_len(HSTORE_KEY(entries, STRPTR(hs), i),
 | |
| 										HSTORE_KEYLEN(entries, i));
 | |
| 
 | |
| 		SRF_RETURN_NEXT(funcctx, PointerGetDatum(item));
 | |
| 	}
 | |
| 
 | |
| 	SRF_RETURN_DONE(funcctx);
 | |
| }
 | |
| 
 | |
| 
 | |
| PG_FUNCTION_INFO_V1(hstore_svals);
 | |
| Datum
 | |
| hstore_svals(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	FuncCallContext *funcctx;
 | |
| 	HStore	   *hs;
 | |
| 	int			i;
 | |
| 
 | |
| 	if (SRF_IS_FIRSTCALL())
 | |
| 	{
 | |
| 		hs = PG_GETARG_HSTORE_P(0);
 | |
| 		funcctx = SRF_FIRSTCALL_INIT();
 | |
| 		setup_firstcall(funcctx, hs, NULL);
 | |
| 	}
 | |
| 
 | |
| 	funcctx = SRF_PERCALL_SETUP();
 | |
| 	hs = (HStore *) funcctx->user_fctx;
 | |
| 	i = funcctx->call_cntr;
 | |
| 
 | |
| 	if (i < HS_COUNT(hs))
 | |
| 	{
 | |
| 		HEntry	   *entries = ARRPTR(hs);
 | |
| 
 | |
| 		if (HSTORE_VALISNULL(entries, i))
 | |
| 		{
 | |
| 			ReturnSetInfo *rsi;
 | |
| 
 | |
| 			/* ugly ugly ugly. why no macro for this? */
 | |
| 			(funcctx)->call_cntr++;
 | |
| 			rsi = (ReturnSetInfo *) fcinfo->resultinfo;
 | |
| 			rsi->isDone = ExprMultipleResult;
 | |
| 			PG_RETURN_NULL();
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			text	   *item;
 | |
| 
 | |
| 			item = cstring_to_text_with_len(HSTORE_VAL(entries, STRPTR(hs), i),
 | |
| 											HSTORE_VALLEN(entries, i));
 | |
| 
 | |
| 			SRF_RETURN_NEXT(funcctx, PointerGetDatum(item));
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	SRF_RETURN_DONE(funcctx);
 | |
| }
 | |
| 
 | |
| 
 | |
| PG_FUNCTION_INFO_V1(hstore_contains);
 | |
| Datum
 | |
| hstore_contains(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	HStore	   *val = PG_GETARG_HSTORE_P(0);
 | |
| 	HStore	   *tmpl = PG_GETARG_HSTORE_P(1);
 | |
| 	bool		res = true;
 | |
| 	HEntry	   *te = ARRPTR(tmpl);
 | |
| 	char	   *tstr = STRPTR(tmpl);
 | |
| 	HEntry	   *ve = ARRPTR(val);
 | |
| 	char	   *vstr = STRPTR(val);
 | |
| 	int			tcount = HS_COUNT(tmpl);
 | |
| 	int			lastidx = 0;
 | |
| 	int			i;
 | |
| 
 | |
| 	/*
 | |
| 	 * we exploit the fact that keys in "tmpl" are in strictly increasing
 | |
| 	 * order to narrow the hstoreFindKey search; each search can start one
 | |
| 	 * entry past the previous "found" entry, or at the lower bound of the
 | |
| 	 * search
 | |
| 	 */
 | |
| 
 | |
| 	for (i = 0; res && i < tcount; ++i)
 | |
| 	{
 | |
| 		int			idx = hstoreFindKey(val, &lastidx,
 | |
| 										HSTORE_KEY(te, tstr, i),
 | |
| 										HSTORE_KEYLEN(te, i));
 | |
| 
 | |
| 		if (idx >= 0)
 | |
| 		{
 | |
| 			bool		nullval = HSTORE_VALISNULL(te, i);
 | |
| 			int			vallen = HSTORE_VALLEN(te, i);
 | |
| 
 | |
| 			if (nullval != HSTORE_VALISNULL(ve, idx) ||
 | |
| 				(!nullval && (vallen != HSTORE_VALLEN(ve, idx) ||
 | |
| 							  memcmp(HSTORE_VAL(te, tstr, i),
 | |
| 									 HSTORE_VAL(ve, vstr, idx),
 | |
| 									 vallen) != 0)))
 | |
| 				res = false;
 | |
| 		}
 | |
| 		else
 | |
| 			res = false;
 | |
| 	}
 | |
| 
 | |
| 	PG_RETURN_BOOL(res);
 | |
| }
 | |
| 
 | |
| 
 | |
| PG_FUNCTION_INFO_V1(hstore_contained);
 | |
| Datum
 | |
| hstore_contained(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	PG_RETURN_DATUM(DirectFunctionCall2(hstore_contains,
 | |
| 										PG_GETARG_DATUM(1),
 | |
| 										PG_GETARG_DATUM(0)
 | |
| 										));
 | |
| }
 | |
| 
 | |
| 
 | |
| PG_FUNCTION_INFO_V1(hstore_each);
 | |
| Datum
 | |
| hstore_each(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	FuncCallContext *funcctx;
 | |
| 	HStore	   *hs;
 | |
| 	int			i;
 | |
| 
 | |
| 	if (SRF_IS_FIRSTCALL())
 | |
| 	{
 | |
| 		hs = PG_GETARG_HSTORE_P(0);
 | |
| 		funcctx = SRF_FIRSTCALL_INIT();
 | |
| 		setup_firstcall(funcctx, hs, fcinfo);
 | |
| 	}
 | |
| 
 | |
| 	funcctx = SRF_PERCALL_SETUP();
 | |
| 	hs = (HStore *) funcctx->user_fctx;
 | |
| 	i = funcctx->call_cntr;
 | |
| 
 | |
| 	if (i < HS_COUNT(hs))
 | |
| 	{
 | |
| 		HEntry	   *entries = ARRPTR(hs);
 | |
| 		char	   *ptr = STRPTR(hs);
 | |
| 		Datum		res,
 | |
| 					dvalues[2];
 | |
| 		bool		nulls[2] = {false, false};
 | |
| 		text	   *item;
 | |
| 		HeapTuple	tuple;
 | |
| 
 | |
| 		item = cstring_to_text_with_len(HSTORE_KEY(entries, ptr, i),
 | |
| 										HSTORE_KEYLEN(entries, i));
 | |
| 		dvalues[0] = PointerGetDatum(item);
 | |
| 
 | |
| 		if (HSTORE_VALISNULL(entries, i))
 | |
| 		{
 | |
| 			dvalues[1] = (Datum) 0;
 | |
| 			nulls[1] = true;
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			item = cstring_to_text_with_len(HSTORE_VAL(entries, ptr, i),
 | |
| 											HSTORE_VALLEN(entries, i));
 | |
| 			dvalues[1] = PointerGetDatum(item);
 | |
| 		}
 | |
| 
 | |
| 		tuple = heap_form_tuple(funcctx->tuple_desc, dvalues, nulls);
 | |
| 		res = HeapTupleGetDatum(tuple);
 | |
| 
 | |
| 		SRF_RETURN_NEXT(funcctx, PointerGetDatum(res));
 | |
| 	}
 | |
| 
 | |
| 	SRF_RETURN_DONE(funcctx);
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * btree sort order for hstores isn't intended to be useful; we really only
 | |
|  * care about equality versus non-equality.  we compare the entire string
 | |
|  * buffer first, then the entry pos array.
 | |
|  */
 | |
| 
 | |
| PG_FUNCTION_INFO_V1(hstore_cmp);
 | |
| Datum
 | |
| hstore_cmp(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	HStore	   *hs1 = PG_GETARG_HSTORE_P(0);
 | |
| 	HStore	   *hs2 = PG_GETARG_HSTORE_P(1);
 | |
| 	int			hcount1 = HS_COUNT(hs1);
 | |
| 	int			hcount2 = HS_COUNT(hs2);
 | |
| 	int			res = 0;
 | |
| 
 | |
| 	if (hcount1 == 0 || hcount2 == 0)
 | |
| 	{
 | |
| 		/*
 | |
| 		 * if either operand is empty, and the other is nonempty, the nonempty
 | |
| 		 * one is larger. If both are empty they are equal.
 | |
| 		 */
 | |
| 		if (hcount1 > 0)
 | |
| 			res = 1;
 | |
| 		else if (hcount2 > 0)
 | |
| 			res = -1;
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		/* here we know both operands are nonempty */
 | |
| 		char	   *str1 = STRPTR(hs1);
 | |
| 		char	   *str2 = STRPTR(hs2);
 | |
| 		HEntry	   *ent1 = ARRPTR(hs1);
 | |
| 		HEntry	   *ent2 = ARRPTR(hs2);
 | |
| 		size_t		len1 = HSE_ENDPOS(ent1[2 * hcount1 - 1]);
 | |
| 		size_t		len2 = HSE_ENDPOS(ent2[2 * hcount2 - 1]);
 | |
| 
 | |
| 		res = memcmp(str1, str2, Min(len1, len2));
 | |
| 
 | |
| 		if (res == 0)
 | |
| 		{
 | |
| 			if (len1 > len2)
 | |
| 				res = 1;
 | |
| 			else if (len1 < len2)
 | |
| 				res = -1;
 | |
| 			else if (hcount1 > hcount2)
 | |
| 				res = 1;
 | |
| 			else if (hcount2 > hcount1)
 | |
| 				res = -1;
 | |
| 			else
 | |
| 			{
 | |
| 				int			count = hcount1 * 2;
 | |
| 				int			i;
 | |
| 
 | |
| 				for (i = 0; i < count; ++i)
 | |
| 					if (HSE_ENDPOS(ent1[i]) != HSE_ENDPOS(ent2[i]) ||
 | |
| 						HSE_ISNULL(ent1[i]) != HSE_ISNULL(ent2[i]))
 | |
| 						break;
 | |
| 				if (i < count)
 | |
| 				{
 | |
| 					if (HSE_ENDPOS(ent1[i]) < HSE_ENDPOS(ent2[i]))
 | |
| 						res = -1;
 | |
| 					else if (HSE_ENDPOS(ent1[i]) > HSE_ENDPOS(ent2[i]))
 | |
| 						res = 1;
 | |
| 					else if (HSE_ISNULL(ent1[i]))
 | |
| 						res = 1;
 | |
| 					else if (HSE_ISNULL(ent2[i]))
 | |
| 						res = -1;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			res = (res > 0) ? 1 : -1;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * this is a btree support function; this is one of the few places where
 | |
| 	 * memory needs to be explicitly freed.
 | |
| 	 */
 | |
| 	PG_FREE_IF_COPY(hs1, 0);
 | |
| 	PG_FREE_IF_COPY(hs2, 1);
 | |
| 	PG_RETURN_INT32(res);
 | |
| }
 | |
| 
 | |
| 
 | |
| PG_FUNCTION_INFO_V1(hstore_eq);
 | |
| Datum
 | |
| hstore_eq(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	int			res = DatumGetInt32(DirectFunctionCall2(hstore_cmp,
 | |
| 														PG_GETARG_DATUM(0),
 | |
| 														PG_GETARG_DATUM(1)));
 | |
| 
 | |
| 	PG_RETURN_BOOL(res == 0);
 | |
| }
 | |
| 
 | |
| PG_FUNCTION_INFO_V1(hstore_ne);
 | |
| Datum
 | |
| hstore_ne(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	int			res = DatumGetInt32(DirectFunctionCall2(hstore_cmp,
 | |
| 														PG_GETARG_DATUM(0),
 | |
| 														PG_GETARG_DATUM(1)));
 | |
| 
 | |
| 	PG_RETURN_BOOL(res != 0);
 | |
| }
 | |
| 
 | |
| PG_FUNCTION_INFO_V1(hstore_gt);
 | |
| Datum
 | |
| hstore_gt(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	int			res = DatumGetInt32(DirectFunctionCall2(hstore_cmp,
 | |
| 														PG_GETARG_DATUM(0),
 | |
| 														PG_GETARG_DATUM(1)));
 | |
| 
 | |
| 	PG_RETURN_BOOL(res > 0);
 | |
| }
 | |
| 
 | |
| PG_FUNCTION_INFO_V1(hstore_ge);
 | |
| Datum
 | |
| hstore_ge(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	int			res = DatumGetInt32(DirectFunctionCall2(hstore_cmp,
 | |
| 														PG_GETARG_DATUM(0),
 | |
| 														PG_GETARG_DATUM(1)));
 | |
| 
 | |
| 	PG_RETURN_BOOL(res >= 0);
 | |
| }
 | |
| 
 | |
| PG_FUNCTION_INFO_V1(hstore_lt);
 | |
| Datum
 | |
| hstore_lt(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	int			res = DatumGetInt32(DirectFunctionCall2(hstore_cmp,
 | |
| 														PG_GETARG_DATUM(0),
 | |
| 														PG_GETARG_DATUM(1)));
 | |
| 
 | |
| 	PG_RETURN_BOOL(res < 0);
 | |
| }
 | |
| 
 | |
| PG_FUNCTION_INFO_V1(hstore_le);
 | |
| Datum
 | |
| hstore_le(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	int			res = DatumGetInt32(DirectFunctionCall2(hstore_cmp,
 | |
| 														PG_GETARG_DATUM(0),
 | |
| 														PG_GETARG_DATUM(1)));
 | |
| 
 | |
| 	PG_RETURN_BOOL(res <= 0);
 | |
| }
 | |
| 
 | |
| 
 | |
| PG_FUNCTION_INFO_V1(hstore_hash);
 | |
| Datum
 | |
| hstore_hash(PG_FUNCTION_ARGS)
 | |
| {
 | |
| 	HStore	   *hs = PG_GETARG_HSTORE_P(0);
 | |
| 	Datum		hval = hash_any((unsigned char *) VARDATA(hs),
 | |
| 								VARSIZE(hs) - VARHDRSZ);
 | |
| 
 | |
| 	/*
 | |
| 	 * this is the only place in the code that cares whether the overall
 | |
| 	 * varlena size exactly matches the true data size; this assertion should
 | |
| 	 * be maintained by all the other code, but we make it explicit here.
 | |
| 	 */
 | |
| 	Assert(VARSIZE(hs) ==
 | |
| 		   (HS_COUNT(hs) != 0 ?
 | |
| 			CALCDATASIZE(HS_COUNT(hs),
 | |
| 						 HSE_ENDPOS(ARRPTR(hs)[2 * HS_COUNT(hs) - 1])) :
 | |
| 			HSHRDSIZE));
 | |
| 
 | |
| 	PG_FREE_IF_COPY(hs, 0);
 | |
| 	PG_RETURN_DATUM(hval);
 | |
| }
 |