2008-05-12 02:00:54 +02:00
|
|
|
/*
|
2010-09-20 22:08:53 +02:00
|
|
|
* contrib/hstore/hstore_io.c
|
2008-05-12 02:00:54 +02:00
|
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
|
2006-09-05 20:00:58 +02:00
|
|
|
#include <ctype.h>
|
|
|
|
|
2012-08-30 22:15:44 +02:00
|
|
|
#include "access/htup_details.h"
|
2009-09-30 21:50:22 +02:00
|
|
|
#include "catalog/pg_type.h"
|
|
|
|
#include "funcapi.h"
|
2013-03-10 22:35:36 +01:00
|
|
|
#include "lib/stringinfo.h"
|
2009-09-30 21:50:22 +02:00
|
|
|
#include "libpq/pqformat.h"
|
2023-06-12 02:14:20 +02:00
|
|
|
#include "parser/scansup.h"
|
2013-03-10 22:35:36 +01:00
|
|
|
#include "utils/builtins.h"
|
|
|
|
#include "utils/json.h"
|
2014-12-01 17:28:45 +01:00
|
|
|
#include "utils/jsonapi.h"
|
Introduce jsonb, a structured format for storing json.
The new format accepts exactly the same data as the json type. However, it is
stored in a format that does not require reparsing the orgiginal text in order
to process it, making it much more suitable for indexing and other operations.
Insignificant whitespace is discarded, and the order of object keys is not
preserved. Neither are duplicate object keys kept - the later value for a given
key is the only one stored.
The new type has all the functions and operators that the json type has,
with the exception of the json generation functions (to_json, json_agg etc.)
and with identical semantics. In addition, there are operator classes for
hash and btree indexing, and two classes for GIN indexing, that have no
equivalent in the json type.
This feature grew out of previous work by Oleg Bartunov and Teodor Sigaev, which
was intended to provide similar facilities to a nested hstore type, but which
in the end proved to have some significant compatibility issues.
Authors: Oleg Bartunov, Teodor Sigaev, Peter Geoghegan and Andrew Dunstan.
Review: Andres Freund
2014-03-23 21:40:19 +01:00
|
|
|
#include "utils/jsonb.h"
|
2009-09-30 21:50:22 +02:00
|
|
|
#include "utils/lsyscache.h"
|
2014-02-17 15:33:31 +01:00
|
|
|
#include "utils/memutils.h"
|
2009-09-30 21:50:22 +02:00
|
|
|
#include "utils/typcache.h"
|
|
|
|
|
2008-05-12 02:00:54 +02:00
|
|
|
#include "hstore.h"
|
|
|
|
|
2006-09-05 20:00:58 +02:00
|
|
|
PG_MODULE_MAGIC;
|
|
|
|
|
2009-09-30 21:50:22 +02:00
|
|
|
/* old names for C functions */
|
|
|
|
HSTORE_POLLUTE(hstore_from_text, tconvert);
|
|
|
|
|
|
|
|
|
2006-09-05 20:00:58 +02:00
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
char *begin;
|
|
|
|
char *ptr;
|
|
|
|
char *cur;
|
|
|
|
char *word;
|
|
|
|
int wordlen;
|
2006-10-04 02:30:14 +02:00
|
|
|
|
2006-09-05 20:00:58 +02:00
|
|
|
Pairs *pairs;
|
|
|
|
int pcur;
|
|
|
|
int plen;
|
|
|
|
} HSParser;
|
|
|
|
|
|
|
|
#define RESIZEPRSBUF \
|
|
|
|
do { \
|
|
|
|
if ( state->cur - state->word + 1 >= state->wordlen ) \
|
|
|
|
{ \
|
2012-06-25 00:51:46 +02:00
|
|
|
int32 clen = state->cur - state->word; \
|
2006-09-05 20:00:58 +02:00
|
|
|
state->wordlen *= 2; \
|
|
|
|
state->word = (char*)repalloc( (void*)state->word, state->wordlen ); \
|
|
|
|
state->cur = state->word + clen; \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
|
|
|
|
#define GV_WAITVAL 0
|
|
|
|
#define GV_INVAL 1
|
|
|
|
#define GV_INESCVAL 2
|
|
|
|
#define GV_WAITESCIN 3
|
|
|
|
#define GV_WAITESCESCIN 4
|
|
|
|
|
|
|
|
static bool
|
|
|
|
get_val(HSParser *state, bool ignoreeq, bool *escaped)
|
|
|
|
{
|
|
|
|
int st = GV_WAITVAL;
|
2006-10-04 02:30:14 +02:00
|
|
|
|
2006-09-05 20:00:58 +02:00
|
|
|
state->wordlen = 32;
|
|
|
|
state->cur = state->word = palloc(state->wordlen);
|
|
|
|
*escaped = false;
|
2006-10-04 02:30:14 +02:00
|
|
|
|
2006-09-05 20:00:58 +02:00
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
if (st == GV_WAITVAL)
|
|
|
|
{
|
|
|
|
if (*(state->ptr) == '"')
|
|
|
|
{
|
|
|
|
*escaped = true;
|
|
|
|
st = GV_INESCVAL;
|
|
|
|
}
|
|
|
|
else if (*(state->ptr) == '\0')
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else if (*(state->ptr) == '=' && !ignoreeq)
|
|
|
|
{
|
2012-06-25 00:51:46 +02:00
|
|
|
elog(ERROR, "Syntax error near '%c' at position %d", *(state->ptr), (int32) (state->ptr - state->begin));
|
2006-09-05 20:00:58 +02:00
|
|
|
}
|
|
|
|
else if (*(state->ptr) == '\\')
|
|
|
|
{
|
|
|
|
st = GV_WAITESCIN;
|
2006-09-22 23:39:58 +02:00
|
|
|
}
|
2023-06-12 02:14:20 +02:00
|
|
|
else if (!scanner_isspace((unsigned char) *(state->ptr)))
|
2006-09-22 23:39:58 +02:00
|
|
|
{
|
2006-09-05 20:00:58 +02:00
|
|
|
*(state->cur) = *(state->ptr);
|
|
|
|
state->cur++;
|
|
|
|
st = GV_INVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (st == GV_INVAL)
|
|
|
|
{
|
|
|
|
if (*(state->ptr) == '\\')
|
2006-10-04 02:30:14 +02:00
|
|
|
{
|
2006-09-05 20:00:58 +02:00
|
|
|
st = GV_WAITESCIN;
|
|
|
|
}
|
|
|
|
else if (*(state->ptr) == '=' && !ignoreeq)
|
|
|
|
{
|
|
|
|
state->ptr--;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else if (*(state->ptr) == ',' && ignoreeq)
|
|
|
|
{
|
|
|
|
state->ptr--;
|
|
|
|
return true;
|
2006-09-22 23:39:58 +02:00
|
|
|
}
|
2023-06-12 02:14:20 +02:00
|
|
|
else if (scanner_isspace((unsigned char) *(state->ptr)))
|
2006-09-22 23:39:58 +02:00
|
|
|
{
|
2006-09-05 20:00:58 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else if (*(state->ptr) == '\0')
|
|
|
|
{
|
|
|
|
state->ptr--;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
RESIZEPRSBUF;
|
|
|
|
*(state->cur) = *(state->ptr);
|
|
|
|
state->cur++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (st == GV_INESCVAL)
|
|
|
|
{
|
|
|
|
if (*(state->ptr) == '\\')
|
2006-10-04 02:30:14 +02:00
|
|
|
{
|
2006-09-05 20:00:58 +02:00
|
|
|
st = GV_WAITESCESCIN;
|
|
|
|
}
|
|
|
|
else if (*(state->ptr) == '"')
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else if (*(state->ptr) == '\0')
|
|
|
|
{
|
|
|
|
elog(ERROR, "Unexpected end of string");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
RESIZEPRSBUF;
|
|
|
|
*(state->cur) = *(state->ptr);
|
|
|
|
state->cur++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (st == GV_WAITESCIN)
|
|
|
|
{
|
|
|
|
if (*(state->ptr) == '\0')
|
|
|
|
elog(ERROR, "Unexpected end of string");
|
|
|
|
RESIZEPRSBUF;
|
|
|
|
*(state->cur) = *(state->ptr);
|
|
|
|
state->cur++;
|
|
|
|
st = GV_INVAL;
|
|
|
|
}
|
|
|
|
else if (st == GV_WAITESCESCIN)
|
|
|
|
{
|
|
|
|
if (*(state->ptr) == '\0')
|
|
|
|
elog(ERROR, "Unexpected end of string");
|
|
|
|
RESIZEPRSBUF;
|
|
|
|
*(state->cur) = *(state->ptr);
|
|
|
|
state->cur++;
|
|
|
|
st = GV_INESCVAL;
|
|
|
|
}
|
|
|
|
else
|
2006-12-05 18:36:00 +01:00
|
|
|
elog(ERROR, "Unknown state %d at position line %d in file '%s'", st, __LINE__, __FILE__);
|
2006-09-05 20:00:58 +02:00
|
|
|
|
|
|
|
state->ptr++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#define WKEY 0
|
|
|
|
#define WVAL 1
|
|
|
|
#define WEQ 2
|
|
|
|
#define WGT 3
|
|
|
|
#define WDEL 4
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
parse_hstore(HSParser *state)
|
|
|
|
{
|
|
|
|
int st = WKEY;
|
|
|
|
bool escaped = false;
|
2006-10-04 02:30:14 +02:00
|
|
|
|
2006-09-05 20:00:58 +02:00
|
|
|
state->plen = 16;
|
|
|
|
state->pairs = (Pairs *) palloc(sizeof(Pairs) * state->plen);
|
|
|
|
state->pcur = 0;
|
|
|
|
state->ptr = state->begin;
|
|
|
|
state->word = NULL;
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
if (st == WKEY)
|
|
|
|
{
|
|
|
|
if (!get_val(state, false, &escaped))
|
|
|
|
return;
|
|
|
|
if (state->pcur >= state->plen)
|
|
|
|
{
|
|
|
|
state->plen *= 2;
|
|
|
|
state->pairs = (Pairs *) repalloc(state->pairs, sizeof(Pairs) * state->plen);
|
|
|
|
}
|
|
|
|
state->pairs[state->pcur].key = state->word;
|
2009-03-15 23:05:17 +01:00
|
|
|
state->pairs[state->pcur].keylen = hstoreCheckKeyLen(state->cur - state->word);
|
2006-09-05 20:00:58 +02:00
|
|
|
state->pairs[state->pcur].val = NULL;
|
|
|
|
state->word = NULL;
|
|
|
|
st = WEQ;
|
|
|
|
}
|
|
|
|
else if (st == WEQ)
|
|
|
|
{
|
|
|
|
if (*(state->ptr) == '=')
|
2006-10-04 02:30:14 +02:00
|
|
|
{
|
2006-09-05 20:00:58 +02:00
|
|
|
st = WGT;
|
|
|
|
}
|
|
|
|
else if (*(state->ptr) == '\0')
|
|
|
|
{
|
2006-12-05 18:36:00 +01:00
|
|
|
elog(ERROR, "Unexpected end of string");
|
2006-09-05 20:00:58 +02:00
|
|
|
}
|
2023-06-12 02:14:20 +02:00
|
|
|
else if (!scanner_isspace((unsigned char) *(state->ptr)))
|
2006-10-04 02:30:14 +02:00
|
|
|
{
|
2012-06-25 00:51:46 +02:00
|
|
|
elog(ERROR, "Syntax error near '%c' at position %d", *(state->ptr), (int32) (state->ptr - state->begin));
|
2006-10-04 02:30:14 +02:00
|
|
|
}
|
|
|
|
}
|
2006-09-05 20:00:58 +02:00
|
|
|
else if (st == WGT)
|
|
|
|
{
|
|
|
|
if (*(state->ptr) == '>')
|
2006-10-04 02:30:14 +02:00
|
|
|
{
|
2006-09-05 20:00:58 +02:00
|
|
|
st = WVAL;
|
|
|
|
}
|
|
|
|
else if (*(state->ptr) == '\0')
|
|
|
|
{
|
2006-12-05 18:36:00 +01:00
|
|
|
elog(ERROR, "Unexpected end of string");
|
2006-09-05 20:00:58 +02:00
|
|
|
}
|
2006-10-04 02:30:14 +02:00
|
|
|
else
|
|
|
|
{
|
2012-06-25 00:51:46 +02:00
|
|
|
elog(ERROR, "Syntax error near '%c' at position %d", *(state->ptr), (int32) (state->ptr - state->begin));
|
2006-10-04 02:30:14 +02:00
|
|
|
}
|
|
|
|
}
|
2006-09-05 20:00:58 +02:00
|
|
|
else if (st == WVAL)
|
2006-10-04 02:30:14 +02:00
|
|
|
{
|
2006-09-05 20:00:58 +02:00
|
|
|
if (!get_val(state, true, &escaped))
|
|
|
|
elog(ERROR, "Unexpected end of string");
|
|
|
|
state->pairs[state->pcur].val = state->word;
|
2009-03-15 23:05:17 +01:00
|
|
|
state->pairs[state->pcur].vallen = hstoreCheckValLen(state->cur - state->word);
|
2006-09-05 20:00:58 +02:00
|
|
|
state->pairs[state->pcur].isnull = false;
|
|
|
|
state->pairs[state->pcur].needfree = true;
|
|
|
|
if (state->cur - state->word == 4 && !escaped)
|
|
|
|
{
|
|
|
|
state->word[4] = '\0';
|
2006-09-15 23:35:30 +02:00
|
|
|
if (0 == pg_strcasecmp(state->word, "null"))
|
2006-09-05 20:00:58 +02:00
|
|
|
state->pairs[state->pcur].isnull = true;
|
|
|
|
}
|
|
|
|
state->word = NULL;
|
|
|
|
state->pcur++;
|
|
|
|
st = WDEL;
|
|
|
|
}
|
|
|
|
else if (st == WDEL)
|
|
|
|
{
|
|
|
|
if (*(state->ptr) == ',')
|
2006-10-04 02:30:14 +02:00
|
|
|
{
|
2006-09-05 20:00:58 +02:00
|
|
|
st = WKEY;
|
|
|
|
}
|
|
|
|
else if (*(state->ptr) == '\0')
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2023-06-12 02:14:20 +02:00
|
|
|
else if (!scanner_isspace((unsigned char) *(state->ptr)))
|
2006-10-04 02:30:14 +02:00
|
|
|
{
|
2012-06-25 00:51:46 +02:00
|
|
|
elog(ERROR, "Syntax error near '%c' at position %d", *(state->ptr), (int32) (state->ptr - state->begin));
|
2006-09-05 20:00:58 +02:00
|
|
|
}
|
2006-10-04 02:30:14 +02:00
|
|
|
}
|
|
|
|
else
|
2006-09-05 20:00:58 +02:00
|
|
|
elog(ERROR, "Unknown state %d at line %d in file '%s'", st, __LINE__, __FILE__);
|
|
|
|
|
|
|
|
state->ptr++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-09-30 21:50:22 +02:00
|
|
|
static int
|
2006-09-05 20:00:58 +02:00
|
|
|
comparePairs(const void *a, const void *b)
|
|
|
|
{
|
2011-09-11 20:54:32 +02:00
|
|
|
const Pairs *pa = a;
|
|
|
|
const Pairs *pb = b;
|
|
|
|
|
|
|
|
if (pa->keylen == pb->keylen)
|
2006-10-04 02:30:14 +02:00
|
|
|
{
|
2011-09-11 20:54:32 +02:00
|
|
|
int res = memcmp(pa->key, pb->key, pa->keylen);
|
2006-10-04 02:30:14 +02:00
|
|
|
|
2006-09-05 20:00:58 +02:00
|
|
|
if (res)
|
|
|
|
return res;
|
|
|
|
|
2006-12-05 18:36:00 +01:00
|
|
|
/* guarantee that needfree will be later */
|
2011-09-11 20:54:32 +02:00
|
|
|
if (pb->needfree == pa->needfree)
|
2006-09-05 20:00:58 +02:00
|
|
|
return 0;
|
2011-09-11 20:54:32 +02:00
|
|
|
else if (pa->needfree)
|
2006-09-05 20:00:58 +02:00
|
|
|
return 1;
|
|
|
|
else
|
|
|
|
return -1;
|
|
|
|
}
|
2011-09-11 20:54:32 +02:00
|
|
|
return (pa->keylen > pb->keylen) ? 1 : -1;
|
2006-09-05 20:00:58 +02:00
|
|
|
}
|
|
|
|
|
2009-09-30 21:50:22 +02:00
|
|
|
/*
|
|
|
|
* this code still respects pairs.needfree, even though in general
|
|
|
|
* it should never be called in a context where anything needs freeing.
|
|
|
|
* we keep it because (a) those calls are in a rare code path anyway,
|
|
|
|
* and (b) who knows whether they might be needed by some caller.
|
|
|
|
*/
|
2006-09-05 20:00:58 +02:00
|
|
|
int
|
2012-06-25 00:51:46 +02:00
|
|
|
hstoreUniquePairs(Pairs *a, int32 l, int32 *buflen)
|
2006-09-05 20:00:58 +02:00
|
|
|
{
|
|
|
|
Pairs *ptr,
|
|
|
|
*res;
|
2006-10-04 02:30:14 +02:00
|
|
|
|
2006-09-05 20:00:58 +02:00
|
|
|
*buflen = 0;
|
|
|
|
if (l < 2)
|
|
|
|
{
|
|
|
|
if (l == 1)
|
|
|
|
*buflen = a->keylen + ((a->isnull) ? 0 : a->vallen);
|
|
|
|
return l;
|
|
|
|
}
|
|
|
|
|
|
|
|
qsort((void *) a, l, sizeof(Pairs), comparePairs);
|
|
|
|
ptr = a + 1;
|
|
|
|
res = a;
|
|
|
|
while (ptr - a < l)
|
|
|
|
{
|
2009-09-30 21:50:22 +02:00
|
|
|
if (ptr->keylen == res->keylen &&
|
2010-12-22 04:11:40 +01:00
|
|
|
memcmp(ptr->key, res->key, res->keylen) == 0)
|
2006-09-05 20:00:58 +02:00
|
|
|
{
|
|
|
|
if (ptr->needfree)
|
|
|
|
{
|
|
|
|
pfree(ptr->key);
|
|
|
|
pfree(ptr->val);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*buflen += res->keylen + ((res->isnull) ? 0 : res->vallen);
|
|
|
|
res++;
|
2017-11-25 20:42:10 +01:00
|
|
|
if (res != ptr)
|
|
|
|
memcpy(res, ptr, sizeof(Pairs));
|
2006-09-05 20:00:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
ptr++;
|
|
|
|
}
|
|
|
|
|
|
|
|
*buflen += res->keylen + ((res->isnull) ? 0 : res->vallen);
|
|
|
|
return res + 1 - a;
|
|
|
|
}
|
|
|
|
|
2009-03-15 23:05:17 +01:00
|
|
|
size_t
|
|
|
|
hstoreCheckKeyLen(size_t len)
|
|
|
|
{
|
|
|
|
if (len > HSTORE_MAX_KEY_LEN)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
|
|
|
|
errmsg("string too long for hstore key")));
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t
|
|
|
|
hstoreCheckValLen(size_t len)
|
|
|
|
{
|
|
|
|
if (len > HSTORE_MAX_VALUE_LEN)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
|
|
|
|
errmsg("string too long for hstore value")));
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-09-30 21:50:22 +02:00
|
|
|
HStore *
|
2012-06-25 00:51:46 +02:00
|
|
|
hstorePairs(Pairs *pairs, int32 pcount, int32 buflen)
|
2009-09-30 21:50:22 +02:00
|
|
|
{
|
|
|
|
HStore *out;
|
|
|
|
HEntry *entry;
|
|
|
|
char *ptr;
|
|
|
|
char *buf;
|
2012-06-25 00:51:46 +02:00
|
|
|
int32 len;
|
|
|
|
int32 i;
|
2009-09-30 21:50:22 +02:00
|
|
|
|
|
|
|
len = CALCDATASIZE(pcount, buflen);
|
|
|
|
out = palloc(len);
|
|
|
|
SET_VARSIZE(out, len);
|
|
|
|
HS_SETCOUNT(out, pcount);
|
|
|
|
|
|
|
|
if (pcount == 0)
|
|
|
|
return out;
|
|
|
|
|
|
|
|
entry = ARRPTR(out);
|
|
|
|
buf = ptr = STRPTR(out);
|
|
|
|
|
|
|
|
for (i = 0; i < pcount; i++)
|
|
|
|
HS_ADDITEM(entry, buf, ptr, pairs[i]);
|
|
|
|
|
|
|
|
HS_FINALIZE(out, pcount, buf, ptr);
|
|
|
|
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-09-05 20:00:58 +02:00
|
|
|
PG_FUNCTION_INFO_V1(hstore_in);
|
|
|
|
Datum
|
|
|
|
hstore_in(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
HSParser state;
|
2012-06-25 00:51:46 +02:00
|
|
|
int32 buflen;
|
2006-09-05 20:00:58 +02:00
|
|
|
HStore *out;
|
2006-10-04 02:30:14 +02:00
|
|
|
|
2006-09-05 20:00:58 +02:00
|
|
|
state.begin = PG_GETARG_CSTRING(0);
|
|
|
|
|
|
|
|
parse_hstore(&state);
|
|
|
|
|
2009-09-30 21:50:22 +02:00
|
|
|
state.pcur = hstoreUniquePairs(state.pairs, state.pcur, &buflen);
|
|
|
|
|
|
|
|
out = hstorePairs(state.pairs, state.pcur, buflen);
|
|
|
|
|
|
|
|
PG_RETURN_POINTER(out);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
PG_FUNCTION_INFO_V1(hstore_recv);
|
|
|
|
Datum
|
|
|
|
hstore_recv(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2012-06-25 00:51:46 +02:00
|
|
|
int32 buflen;
|
2009-09-30 21:50:22 +02:00
|
|
|
HStore *out;
|
|
|
|
Pairs *pairs;
|
2012-06-25 00:51:46 +02:00
|
|
|
int32 i;
|
|
|
|
int32 pcount;
|
2009-09-30 21:50:22 +02:00
|
|
|
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
|
|
|
|
|
|
|
|
pcount = pq_getmsgint(buf, 4);
|
|
|
|
|
|
|
|
if (pcount == 0)
|
2006-09-05 20:00:58 +02:00
|
|
|
{
|
2009-09-30 21:50:22 +02:00
|
|
|
out = hstorePairs(NULL, 0, 0);
|
2006-09-05 20:00:58 +02:00
|
|
|
PG_RETURN_POINTER(out);
|
|
|
|
}
|
|
|
|
|
2014-02-17 15:33:31 +01:00
|
|
|
if (pcount < 0 || pcount > MaxAllocSize / sizeof(Pairs))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
|
|
|
|
errmsg("number of pairs (%d) exceeds the maximum allowed (%d)",
|
|
|
|
pcount, (int) (MaxAllocSize / sizeof(Pairs)))));
|
2009-09-30 21:50:22 +02:00
|
|
|
pairs = palloc(pcount * sizeof(Pairs));
|
2006-09-05 20:00:58 +02:00
|
|
|
|
2009-09-30 21:50:22 +02:00
|
|
|
for (i = 0; i < pcount; ++i)
|
|
|
|
{
|
|
|
|
int rawlen = pq_getmsgint(buf, 4);
|
|
|
|
int len;
|
|
|
|
|
|
|
|
if (rawlen < 0)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
|
|
|
|
errmsg("null value not allowed for hstore key")));
|
|
|
|
|
|
|
|
pairs[i].key = pq_getmsgtext(buf, rawlen, &len);
|
|
|
|
pairs[i].keylen = hstoreCheckKeyLen(len);
|
|
|
|
pairs[i].needfree = true;
|
|
|
|
|
|
|
|
rawlen = pq_getmsgint(buf, 4);
|
|
|
|
if (rawlen < 0)
|
|
|
|
{
|
|
|
|
pairs[i].val = NULL;
|
|
|
|
pairs[i].vallen = 0;
|
|
|
|
pairs[i].isnull = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pairs[i].val = pq_getmsgtext(buf, rawlen, &len);
|
|
|
|
pairs[i].vallen = hstoreCheckValLen(len);
|
|
|
|
pairs[i].isnull = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pcount = hstoreUniquePairs(pairs, pcount, &buflen);
|
|
|
|
|
|
|
|
out = hstorePairs(pairs, pcount, buflen);
|
|
|
|
|
|
|
|
PG_RETURN_POINTER(out);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
PG_FUNCTION_INFO_V1(hstore_from_text);
|
|
|
|
Datum
|
|
|
|
hstore_from_text(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
text *key;
|
|
|
|
text *val = NULL;
|
|
|
|
Pairs p;
|
|
|
|
HStore *out;
|
|
|
|
|
|
|
|
if (PG_ARGISNULL(0))
|
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
|
|
|
p.needfree = false;
|
|
|
|
key = PG_GETARG_TEXT_PP(0);
|
|
|
|
p.key = VARDATA_ANY(key);
|
|
|
|
p.keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(key));
|
|
|
|
|
|
|
|
if (PG_ARGISNULL(1))
|
|
|
|
{
|
|
|
|
p.vallen = 0;
|
|
|
|
p.isnull = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
val = PG_GETARG_TEXT_PP(1);
|
|
|
|
p.val = VARDATA_ANY(val);
|
|
|
|
p.vallen = hstoreCheckValLen(VARSIZE_ANY_EXHDR(val));
|
|
|
|
p.isnull = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
out = hstorePairs(&p, 1, p.keylen + p.vallen);
|
|
|
|
|
|
|
|
PG_RETURN_POINTER(out);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
PG_FUNCTION_INFO_V1(hstore_from_arrays);
|
|
|
|
Datum
|
|
|
|
hstore_from_arrays(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2012-06-25 00:51:46 +02:00
|
|
|
int32 buflen;
|
2009-09-30 21:50:22 +02:00
|
|
|
HStore *out;
|
|
|
|
Pairs *pairs;
|
|
|
|
Datum *key_datums;
|
|
|
|
bool *key_nulls;
|
|
|
|
int key_count;
|
|
|
|
Datum *value_datums;
|
|
|
|
bool *value_nulls;
|
|
|
|
int value_count;
|
|
|
|
ArrayType *key_array;
|
|
|
|
ArrayType *value_array;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (PG_ARGISNULL(0))
|
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
|
|
|
key_array = PG_GETARG_ARRAYTYPE_P(0);
|
|
|
|
|
|
|
|
Assert(ARR_ELEMTYPE(key_array) == TEXTOID);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* must check >1 rather than != 1 because empty arrays have 0 dimensions,
|
|
|
|
* not 1
|
|
|
|
*/
|
2006-09-05 20:00:58 +02:00
|
|
|
|
2009-09-30 21:50:22 +02:00
|
|
|
if (ARR_NDIM(key_array) > 1)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
|
|
|
|
errmsg("wrong number of array subscripts")));
|
|
|
|
|
|
|
|
deconstruct_array(key_array,
|
|
|
|
TEXTOID, -1, false, 'i',
|
|
|
|
&key_datums, &key_nulls, &key_count);
|
|
|
|
|
2014-02-17 15:33:31 +01:00
|
|
|
/* see discussion in hstoreArrayToPairs() */
|
|
|
|
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)))));
|
|
|
|
|
2009-09-30 21:50:22 +02:00
|
|
|
/* value_array might be NULL */
|
2006-09-05 20:00:58 +02:00
|
|
|
|
2009-09-30 21:50:22 +02:00
|
|
|
if (PG_ARGISNULL(1))
|
|
|
|
{
|
|
|
|
value_array = NULL;
|
|
|
|
value_count = key_count;
|
|
|
|
value_datums = NULL;
|
|
|
|
value_nulls = NULL;
|
|
|
|
}
|
|
|
|
else
|
2006-09-05 20:00:58 +02:00
|
|
|
{
|
2009-09-30 21:50:22 +02:00
|
|
|
value_array = PG_GETARG_ARRAYTYPE_P(1);
|
|
|
|
|
|
|
|
Assert(ARR_ELEMTYPE(value_array) == TEXTOID);
|
|
|
|
|
|
|
|
if (ARR_NDIM(value_array) > 1)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
|
|
|
|
errmsg("wrong number of array subscripts")));
|
|
|
|
|
|
|
|
if ((ARR_NDIM(key_array) > 0 || ARR_NDIM(value_array) > 0) &&
|
|
|
|
(ARR_NDIM(key_array) != ARR_NDIM(value_array) ||
|
|
|
|
ARR_DIMS(key_array)[0] != ARR_DIMS(value_array)[0] ||
|
|
|
|
ARR_LBOUND(key_array)[0] != ARR_LBOUND(value_array)[0]))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
|
|
|
|
errmsg("arrays must have same bounds")));
|
|
|
|
|
|
|
|
deconstruct_array(value_array,
|
|
|
|
TEXTOID, -1, false, 'i',
|
|
|
|
&value_datums, &value_nulls, &value_count);
|
2006-09-05 20:00:58 +02:00
|
|
|
|
2009-09-30 21:50:22 +02:00
|
|
|
Assert(key_count == value_count);
|
|
|
|
}
|
|
|
|
|
|
|
|
pairs = palloc(key_count * sizeof(Pairs));
|
|
|
|
|
|
|
|
for (i = 0; i < key_count; ++i)
|
|
|
|
{
|
|
|
|
if (key_nulls[i])
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
|
|
|
|
errmsg("null value not allowed for hstore key")));
|
|
|
|
|
|
|
|
if (!value_nulls || value_nulls[i])
|
|
|
|
{
|
2017-03-13 00:35:31 +01:00
|
|
|
pairs[i].key = VARDATA(key_datums[i]);
|
2009-09-30 21:50:22 +02:00
|
|
|
pairs[i].val = NULL;
|
2017-03-13 00:35:31 +01:00
|
|
|
pairs[i].keylen =
|
|
|
|
hstoreCheckKeyLen(VARSIZE(key_datums[i]) - VARHDRSZ);
|
2009-09-30 21:50:22 +02:00
|
|
|
pairs[i].vallen = 4;
|
|
|
|
pairs[i].isnull = true;
|
|
|
|
pairs[i].needfree = false;
|
|
|
|
}
|
2006-09-05 20:00:58 +02:00
|
|
|
else
|
|
|
|
{
|
2017-03-13 00:35:31 +01:00
|
|
|
pairs[i].key = VARDATA(key_datums[i]);
|
|
|
|
pairs[i].val = VARDATA(value_datums[i]);
|
|
|
|
pairs[i].keylen =
|
|
|
|
hstoreCheckKeyLen(VARSIZE(key_datums[i]) - VARHDRSZ);
|
|
|
|
pairs[i].vallen =
|
|
|
|
hstoreCheckValLen(VARSIZE(value_datums[i]) - VARHDRSZ);
|
2009-09-30 21:50:22 +02:00
|
|
|
pairs[i].isnull = false;
|
|
|
|
pairs[i].needfree = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
key_count = hstoreUniquePairs(pairs, key_count, &buflen);
|
|
|
|
|
|
|
|
out = hstorePairs(pairs, key_count, buflen);
|
|
|
|
|
|
|
|
PG_RETURN_POINTER(out);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
PG_FUNCTION_INFO_V1(hstore_from_array);
|
|
|
|
Datum
|
|
|
|
hstore_from_array(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
ArrayType *in_array = PG_GETARG_ARRAYTYPE_P(0);
|
|
|
|
int ndims = ARR_NDIM(in_array);
|
|
|
|
int count;
|
2012-06-25 00:51:46 +02:00
|
|
|
int32 buflen;
|
2009-09-30 21:50:22 +02:00
|
|
|
HStore *out;
|
|
|
|
Pairs *pairs;
|
|
|
|
Datum *in_datums;
|
|
|
|
bool *in_nulls;
|
|
|
|
int in_count;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
Assert(ARR_ELEMTYPE(in_array) == TEXTOID);
|
|
|
|
|
|
|
|
switch (ndims)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
out = hstorePairs(NULL, 0, 0);
|
|
|
|
PG_RETURN_POINTER(out);
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
if ((ARR_DIMS(in_array)[0]) % 2)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
|
|
|
|
errmsg("array must have even number of elements")));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
if ((ARR_DIMS(in_array)[1]) != 2)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
|
|
|
|
errmsg("array must have two columns")));
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
|
|
|
|
errmsg("wrong number of array subscripts")));
|
|
|
|
}
|
|
|
|
|
|
|
|
deconstruct_array(in_array,
|
|
|
|
TEXTOID, -1, false, 'i',
|
|
|
|
&in_datums, &in_nulls, &in_count);
|
|
|
|
|
|
|
|
count = in_count / 2;
|
|
|
|
|
2014-02-17 15:33:31 +01:00
|
|
|
/* see discussion in hstoreArrayToPairs() */
|
|
|
|
if (count > MaxAllocSize / sizeof(Pairs))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
|
|
|
|
errmsg("number of pairs (%d) exceeds the maximum allowed (%d)",
|
|
|
|
count, (int) (MaxAllocSize / sizeof(Pairs)))));
|
|
|
|
|
2009-09-30 21:50:22 +02:00
|
|
|
pairs = palloc(count * sizeof(Pairs));
|
|
|
|
|
|
|
|
for (i = 0; i < count; ++i)
|
|
|
|
{
|
|
|
|
if (in_nulls[i * 2])
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
|
|
|
|
errmsg("null value not allowed for hstore key")));
|
|
|
|
|
|
|
|
if (in_nulls[i * 2 + 1])
|
|
|
|
{
|
2017-03-13 00:35:31 +01:00
|
|
|
pairs[i].key = VARDATA(in_datums[i * 2]);
|
2009-09-30 21:50:22 +02:00
|
|
|
pairs[i].val = NULL;
|
2017-03-13 00:35:31 +01:00
|
|
|
pairs[i].keylen =
|
|
|
|
hstoreCheckKeyLen(VARSIZE(in_datums[i * 2]) - VARHDRSZ);
|
2009-09-30 21:50:22 +02:00
|
|
|
pairs[i].vallen = 4;
|
|
|
|
pairs[i].isnull = true;
|
|
|
|
pairs[i].needfree = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-03-13 00:35:31 +01:00
|
|
|
pairs[i].key = VARDATA(in_datums[i * 2]);
|
|
|
|
pairs[i].val = VARDATA(in_datums[i * 2 + 1]);
|
|
|
|
pairs[i].keylen =
|
|
|
|
hstoreCheckKeyLen(VARSIZE(in_datums[i * 2]) - VARHDRSZ);
|
|
|
|
pairs[i].vallen =
|
|
|
|
hstoreCheckValLen(VARSIZE(in_datums[i * 2 + 1]) - VARHDRSZ);
|
2009-09-30 21:50:22 +02:00
|
|
|
pairs[i].isnull = false;
|
|
|
|
pairs[i].needfree = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
count = hstoreUniquePairs(pairs, count, &buflen);
|
|
|
|
|
|
|
|
out = hstorePairs(pairs, count, buflen);
|
|
|
|
|
|
|
|
PG_RETURN_POINTER(out);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* most of hstore_from_record is shamelessly swiped from record_out */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* structure to cache metadata needed for record I/O
|
|
|
|
*/
|
|
|
|
typedef struct ColumnIOData
|
|
|
|
{
|
|
|
|
Oid column_type;
|
|
|
|
Oid typiofunc;
|
|
|
|
Oid typioparam;
|
|
|
|
FmgrInfo proc;
|
|
|
|
} ColumnIOData;
|
|
|
|
|
|
|
|
typedef struct RecordIOData
|
|
|
|
{
|
|
|
|
Oid record_type;
|
|
|
|
int32 record_typmod;
|
2017-10-26 19:47:45 +02:00
|
|
|
/* this field is used only if target type is domain over composite: */
|
|
|
|
void *domain_info; /* opaque cache for domain checks */
|
2009-09-30 21:50:22 +02:00
|
|
|
int ncolumns;
|
2015-02-20 23:03:12 +01:00
|
|
|
ColumnIOData columns[FLEXIBLE_ARRAY_MEMBER];
|
2009-09-30 21:50:22 +02:00
|
|
|
} RecordIOData;
|
|
|
|
|
|
|
|
PG_FUNCTION_INFO_V1(hstore_from_record);
|
|
|
|
Datum
|
|
|
|
hstore_from_record(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
HeapTupleHeader rec;
|
2012-06-25 00:51:46 +02:00
|
|
|
int32 buflen;
|
2009-09-30 21:50:22 +02:00
|
|
|
HStore *out;
|
|
|
|
Pairs *pairs;
|
|
|
|
Oid tupType;
|
|
|
|
int32 tupTypmod;
|
|
|
|
TupleDesc tupdesc;
|
|
|
|
HeapTupleData tuple;
|
|
|
|
RecordIOData *my_extra;
|
|
|
|
int ncolumns;
|
|
|
|
int i,
|
|
|
|
j;
|
|
|
|
Datum *values;
|
|
|
|
bool *nulls;
|
|
|
|
|
|
|
|
if (PG_ARGISNULL(0))
|
|
|
|
{
|
|
|
|
Oid argtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
|
|
|
|
|
|
|
|
/*
|
2017-10-26 19:47:45 +02:00
|
|
|
* We have no tuple to look at, so the only source of type info is the
|
|
|
|
* argtype --- which might be domain over composite, but we don't care
|
|
|
|
* here, since we have no need to be concerned about domain
|
|
|
|
* constraints. The lookup_rowtype_tupdesc_domain call below will
|
|
|
|
* error out if we don't have a known composite type oid here.
|
2009-09-30 21:50:22 +02:00
|
|
|
*/
|
|
|
|
tupType = argtype;
|
|
|
|
tupTypmod = -1;
|
|
|
|
|
|
|
|
rec = NULL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
rec = PG_GETARG_HEAPTUPLEHEADER(0);
|
|
|
|
|
2017-10-26 19:47:45 +02:00
|
|
|
/*
|
|
|
|
* Extract type info from the tuple itself -- this will work even for
|
|
|
|
* anonymous record types.
|
|
|
|
*/
|
2009-09-30 21:50:22 +02:00
|
|
|
tupType = HeapTupleHeaderGetTypeId(rec);
|
|
|
|
tupTypmod = HeapTupleHeaderGetTypMod(rec);
|
|
|
|
}
|
|
|
|
|
2017-10-26 19:47:45 +02:00
|
|
|
tupdesc = lookup_rowtype_tupdesc_domain(tupType, tupTypmod, false);
|
2009-09-30 21:50:22 +02:00
|
|
|
ncolumns = tupdesc->natts;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We arrange to look up the needed I/O info just once per series of
|
|
|
|
* calls, assuming the record type doesn't change underneath us.
|
|
|
|
*/
|
|
|
|
my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
|
|
|
|
if (my_extra == NULL ||
|
|
|
|
my_extra->ncolumns != ncolumns)
|
|
|
|
{
|
|
|
|
fcinfo->flinfo->fn_extra =
|
|
|
|
MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
|
2015-02-20 23:03:12 +01:00
|
|
|
offsetof(RecordIOData, columns) +
|
|
|
|
ncolumns * sizeof(ColumnIOData));
|
2009-09-30 21:50:22 +02:00
|
|
|
my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
|
|
|
|
my_extra->record_type = InvalidOid;
|
|
|
|
my_extra->record_typmod = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (my_extra->record_type != tupType ||
|
|
|
|
my_extra->record_typmod != tupTypmod)
|
|
|
|
{
|
|
|
|
MemSet(my_extra, 0,
|
2015-02-20 23:03:12 +01:00
|
|
|
offsetof(RecordIOData, columns) +
|
|
|
|
ncolumns * sizeof(ColumnIOData));
|
2009-09-30 21:50:22 +02:00
|
|
|
my_extra->record_type = tupType;
|
|
|
|
my_extra->record_typmod = tupTypmod;
|
|
|
|
my_extra->ncolumns = ncolumns;
|
|
|
|
}
|
|
|
|
|
2014-02-17 15:33:31 +01:00
|
|
|
Assert(ncolumns <= MaxTupleAttributeNumber); /* thus, no overflow */
|
2009-09-30 21:50:22 +02:00
|
|
|
pairs = palloc(ncolumns * sizeof(Pairs));
|
|
|
|
|
|
|
|
if (rec)
|
|
|
|
{
|
|
|
|
/* Build a temporary HeapTuple control structure */
|
|
|
|
tuple.t_len = HeapTupleHeaderGetDatumLength(rec);
|
|
|
|
ItemPointerSetInvalid(&(tuple.t_self));
|
|
|
|
tuple.t_tableOid = InvalidOid;
|
|
|
|
tuple.t_data = rec;
|
|
|
|
|
|
|
|
values = (Datum *) palloc(ncolumns * sizeof(Datum));
|
|
|
|
nulls = (bool *) palloc(ncolumns * sizeof(bool));
|
|
|
|
|
|
|
|
/* Break down the tuple into fields */
|
|
|
|
heap_deform_tuple(&tuple, tupdesc, values, nulls);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
values = NULL;
|
|
|
|
nulls = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0, j = 0; i < ncolumns; ++i)
|
|
|
|
{
|
|
|
|
ColumnIOData *column_info = &my_extra->columns[i];
|
2017-08-20 20:19:07 +02:00
|
|
|
Form_pg_attribute att = TupleDescAttr(tupdesc, i);
|
|
|
|
Oid column_type = att->atttypid;
|
2009-09-30 21:50:22 +02:00
|
|
|
char *value;
|
|
|
|
|
|
|
|
/* Ignore dropped columns in datatype */
|
2017-08-20 20:19:07 +02:00
|
|
|
if (att->attisdropped)
|
2009-09-30 21:50:22 +02:00
|
|
|
continue;
|
|
|
|
|
2017-08-20 20:19:07 +02:00
|
|
|
pairs[j].key = NameStr(att->attname);
|
|
|
|
pairs[j].keylen = hstoreCheckKeyLen(strlen(NameStr(att->attname)));
|
2009-09-30 21:50:22 +02:00
|
|
|
|
|
|
|
if (!nulls || nulls[i])
|
|
|
|
{
|
|
|
|
pairs[j].val = NULL;
|
|
|
|
pairs[j].vallen = 4;
|
|
|
|
pairs[j].isnull = true;
|
|
|
|
pairs[j].needfree = false;
|
|
|
|
++j;
|
|
|
|
continue;
|
2006-09-05 20:00:58 +02:00
|
|
|
}
|
2009-09-30 21:50:22 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Convert the column value to text
|
|
|
|
*/
|
|
|
|
if (column_info->column_type != column_type)
|
|
|
|
{
|
|
|
|
bool typIsVarlena;
|
|
|
|
|
|
|
|
getTypeOutputInfo(column_type,
|
|
|
|
&column_info->typiofunc,
|
|
|
|
&typIsVarlena);
|
|
|
|
fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
|
|
|
|
fcinfo->flinfo->fn_mcxt);
|
|
|
|
column_info->column_type = column_type;
|
|
|
|
}
|
|
|
|
|
|
|
|
value = OutputFunctionCall(&column_info->proc, values[i]);
|
|
|
|
|
|
|
|
pairs[j].val = value;
|
|
|
|
pairs[j].vallen = hstoreCheckValLen(strlen(value));
|
|
|
|
pairs[j].isnull = false;
|
|
|
|
pairs[j].needfree = false;
|
|
|
|
++j;
|
2006-09-05 20:00:58 +02:00
|
|
|
}
|
|
|
|
|
2009-09-30 21:50:22 +02:00
|
|
|
ncolumns = hstoreUniquePairs(pairs, j, &buflen);
|
|
|
|
|
|
|
|
out = hstorePairs(pairs, ncolumns, buflen);
|
|
|
|
|
|
|
|
ReleaseTupleDesc(tupdesc);
|
|
|
|
|
2006-09-05 20:00:58 +02:00
|
|
|
PG_RETURN_POINTER(out);
|
|
|
|
}
|
|
|
|
|
2009-09-30 21:50:22 +02:00
|
|
|
|
|
|
|
PG_FUNCTION_INFO_V1(hstore_populate_record);
|
|
|
|
Datum
|
|
|
|
hstore_populate_record(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
Oid argtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
|
|
|
|
HStore *hs;
|
|
|
|
HEntry *entries;
|
|
|
|
char *ptr;
|
|
|
|
HeapTupleHeader rec;
|
|
|
|
Oid tupType;
|
|
|
|
int32 tupTypmod;
|
|
|
|
TupleDesc tupdesc;
|
|
|
|
HeapTupleData tuple;
|
|
|
|
HeapTuple rettuple;
|
|
|
|
RecordIOData *my_extra;
|
|
|
|
int ncolumns;
|
|
|
|
int i;
|
|
|
|
Datum *values;
|
|
|
|
bool *nulls;
|
|
|
|
|
|
|
|
if (!type_is_rowtype(argtype))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
|
|
|
errmsg("first argument must be a rowtype")));
|
|
|
|
|
|
|
|
if (PG_ARGISNULL(0))
|
|
|
|
{
|
|
|
|
if (PG_ARGISNULL(1))
|
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
|
|
|
rec = NULL;
|
|
|
|
|
|
|
|
/*
|
2017-10-26 19:47:45 +02:00
|
|
|
* We have no tuple to look at, so the only source of type info is the
|
|
|
|
* argtype. The lookup_rowtype_tupdesc_domain call below will error
|
|
|
|
* out if we don't have a known composite type oid here.
|
2009-09-30 21:50:22 +02:00
|
|
|
*/
|
|
|
|
tupType = argtype;
|
|
|
|
tupTypmod = -1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
rec = PG_GETARG_HEAPTUPLEHEADER(0);
|
|
|
|
|
|
|
|
if (PG_ARGISNULL(1))
|
|
|
|
PG_RETURN_POINTER(rec);
|
|
|
|
|
2017-10-26 19:47:45 +02:00
|
|
|
/*
|
|
|
|
* Extract type info from the tuple itself -- this will work even for
|
|
|
|
* anonymous record types.
|
|
|
|
*/
|
2009-09-30 21:50:22 +02:00
|
|
|
tupType = HeapTupleHeaderGetTypeId(rec);
|
|
|
|
tupTypmod = HeapTupleHeaderGetTypMod(rec);
|
|
|
|
}
|
|
|
|
|
2017-09-18 21:21:23 +02:00
|
|
|
hs = PG_GETARG_HSTORE_P(1);
|
2009-09-30 21:50:22 +02:00
|
|
|
entries = ARRPTR(hs);
|
|
|
|
ptr = STRPTR(hs);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* if the input hstore is empty, we can only skip the rest if we were
|
|
|
|
* passed in a non-null record, since otherwise there may be issues with
|
|
|
|
* domain nulls.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (HS_COUNT(hs) == 0 && rec)
|
|
|
|
PG_RETURN_POINTER(rec);
|
|
|
|
|
2017-10-26 19:47:45 +02:00
|
|
|
/*
|
|
|
|
* Lookup the input record's tupdesc. For the moment, we don't worry
|
|
|
|
* about whether it is a domain over composite.
|
|
|
|
*/
|
|
|
|
tupdesc = lookup_rowtype_tupdesc_domain(tupType, tupTypmod, false);
|
2009-09-30 21:50:22 +02:00
|
|
|
ncolumns = tupdesc->natts;
|
|
|
|
|
|
|
|
if (rec)
|
|
|
|
{
|
|
|
|
/* Build a temporary HeapTuple control structure */
|
|
|
|
tuple.t_len = HeapTupleHeaderGetDatumLength(rec);
|
|
|
|
ItemPointerSetInvalid(&(tuple.t_self));
|
|
|
|
tuple.t_tableOid = InvalidOid;
|
|
|
|
tuple.t_data = rec;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We arrange to look up the needed I/O info just once per series of
|
|
|
|
* calls, assuming the record type doesn't change underneath us.
|
|
|
|
*/
|
|
|
|
my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
|
|
|
|
if (my_extra == NULL ||
|
|
|
|
my_extra->ncolumns != ncolumns)
|
|
|
|
{
|
|
|
|
fcinfo->flinfo->fn_extra =
|
|
|
|
MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
|
2015-02-20 23:03:12 +01:00
|
|
|
offsetof(RecordIOData, columns) +
|
|
|
|
ncolumns * sizeof(ColumnIOData));
|
2009-09-30 21:50:22 +02:00
|
|
|
my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
|
|
|
|
my_extra->record_type = InvalidOid;
|
|
|
|
my_extra->record_typmod = 0;
|
2017-10-26 19:47:45 +02:00
|
|
|
my_extra->domain_info = NULL;
|
2009-09-30 21:50:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (my_extra->record_type != tupType ||
|
|
|
|
my_extra->record_typmod != tupTypmod)
|
|
|
|
{
|
|
|
|
MemSet(my_extra, 0,
|
2015-02-20 23:03:12 +01:00
|
|
|
offsetof(RecordIOData, columns) +
|
|
|
|
ncolumns * sizeof(ColumnIOData));
|
2009-09-30 21:50:22 +02:00
|
|
|
my_extra->record_type = tupType;
|
|
|
|
my_extra->record_typmod = tupTypmod;
|
|
|
|
my_extra->ncolumns = ncolumns;
|
|
|
|
}
|
|
|
|
|
|
|
|
values = (Datum *) palloc(ncolumns * sizeof(Datum));
|
|
|
|
nulls = (bool *) palloc(ncolumns * sizeof(bool));
|
|
|
|
|
|
|
|
if (rec)
|
|
|
|
{
|
|
|
|
/* Break down the tuple into fields */
|
|
|
|
heap_deform_tuple(&tuple, tupdesc, values, nulls);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for (i = 0; i < ncolumns; ++i)
|
|
|
|
{
|
|
|
|
values[i] = (Datum) 0;
|
|
|
|
nulls[i] = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < ncolumns; ++i)
|
|
|
|
{
|
|
|
|
ColumnIOData *column_info = &my_extra->columns[i];
|
2017-08-20 20:19:07 +02:00
|
|
|
Form_pg_attribute att = TupleDescAttr(tupdesc, i);
|
|
|
|
Oid column_type = att->atttypid;
|
2009-09-30 21:50:22 +02:00
|
|
|
char *value;
|
|
|
|
int idx;
|
|
|
|
int vallen;
|
|
|
|
|
|
|
|
/* Ignore dropped columns in datatype */
|
2017-08-20 20:19:07 +02:00
|
|
|
if (att->attisdropped)
|
2009-09-30 21:50:22 +02:00
|
|
|
{
|
|
|
|
nulls[i] = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
idx = hstoreFindKey(hs, 0,
|
2017-08-20 20:19:07 +02:00
|
|
|
NameStr(att->attname),
|
|
|
|
strlen(NameStr(att->attname)));
|
2010-02-26 03:01:40 +01:00
|
|
|
|
2009-09-30 21:50:22 +02:00
|
|
|
/*
|
|
|
|
* we can't just skip here if the key wasn't found since we might have
|
|
|
|
* a domain to deal with. If we were passed in a non-null record
|
|
|
|
* datum, we assume that the existing values are valid (if they're
|
|
|
|
* not, then it's not our fault), but if we were passed in a null,
|
|
|
|
* then every field which we don't populate needs to be run through
|
|
|
|
* the input function just in case it's a domain type.
|
|
|
|
*/
|
|
|
|
if (idx < 0 && rec)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Prepare to convert the column value from text
|
|
|
|
*/
|
|
|
|
if (column_info->column_type != column_type)
|
|
|
|
{
|
|
|
|
getTypeInputInfo(column_type,
|
|
|
|
&column_info->typiofunc,
|
|
|
|
&column_info->typioparam);
|
|
|
|
fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
|
|
|
|
fcinfo->flinfo->fn_mcxt);
|
|
|
|
column_info->column_type = column_type;
|
|
|
|
}
|
|
|
|
|
2015-11-19 20:54:05 +01:00
|
|
|
if (idx < 0 || HSTORE_VALISNULL(entries, idx))
|
2009-09-30 21:50:22 +02:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* need InputFunctionCall to happen even for nulls, so that domain
|
|
|
|
* checks are done
|
|
|
|
*/
|
|
|
|
values[i] = InputFunctionCall(&column_info->proc, NULL,
|
|
|
|
column_info->typioparam,
|
2017-08-20 20:19:07 +02:00
|
|
|
att->atttypmod);
|
2009-09-30 21:50:22 +02:00
|
|
|
nulls[i] = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-11-19 20:54:05 +01:00
|
|
|
vallen = HSTORE_VALLEN(entries, idx);
|
2009-09-30 21:50:22 +02:00
|
|
|
value = palloc(1 + vallen);
|
2015-11-19 20:54:05 +01:00
|
|
|
memcpy(value, HSTORE_VAL(entries, ptr, idx), vallen);
|
2009-09-30 21:50:22 +02:00
|
|
|
value[vallen] = 0;
|
|
|
|
|
|
|
|
values[i] = InputFunctionCall(&column_info->proc, value,
|
|
|
|
column_info->typioparam,
|
2017-08-20 20:19:07 +02:00
|
|
|
att->atttypmod);
|
2009-09-30 21:50:22 +02:00
|
|
|
nulls[i] = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rettuple = heap_form_tuple(tupdesc, values, nulls);
|
|
|
|
|
2017-10-26 19:47:45 +02:00
|
|
|
/*
|
|
|
|
* If the target type is domain over composite, all we know at this point
|
|
|
|
* is that we've made a valid value of the base composite type. Must
|
|
|
|
* check domain constraints before deciding we're done.
|
|
|
|
*/
|
|
|
|
if (argtype != tupdesc->tdtypeid)
|
|
|
|
domain_check(HeapTupleGetDatum(rettuple), false,
|
|
|
|
argtype,
|
|
|
|
&my_extra->domain_info,
|
|
|
|
fcinfo->flinfo->fn_mcxt);
|
|
|
|
|
2009-09-30 21:50:22 +02:00
|
|
|
ReleaseTupleDesc(tupdesc);
|
|
|
|
|
|
|
|
PG_RETURN_DATUM(HeapTupleGetDatum(rettuple));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-09-05 20:00:58 +02:00
|
|
|
static char *
|
|
|
|
cpw(char *dst, char *src, int len)
|
|
|
|
{
|
|
|
|
char *ptr = src;
|
|
|
|
|
|
|
|
while (ptr - src < len)
|
|
|
|
{
|
|
|
|
if (*ptr == '"' || *ptr == '\\')
|
|
|
|
*dst++ = '\\';
|
|
|
|
*dst++ = *ptr++;
|
|
|
|
}
|
|
|
|
return dst;
|
|
|
|
}
|
|
|
|
|
|
|
|
PG_FUNCTION_INFO_V1(hstore_out);
|
|
|
|
Datum
|
|
|
|
hstore_out(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2017-09-18 21:21:23 +02:00
|
|
|
HStore *in = PG_GETARG_HSTORE_P(0);
|
2006-09-05 20:00:58 +02:00
|
|
|
int buflen,
|
2009-09-30 21:50:22 +02:00
|
|
|
i;
|
|
|
|
int count = HS_COUNT(in);
|
2006-09-05 20:00:58 +02:00
|
|
|
char *out,
|
|
|
|
*ptr;
|
|
|
|
char *base = STRPTR(in);
|
|
|
|
HEntry *entries = ARRPTR(in);
|
2006-10-04 02:30:14 +02:00
|
|
|
|
2009-09-30 21:50:22 +02:00
|
|
|
if (count == 0)
|
2014-01-07 03:30:26 +01:00
|
|
|
PG_RETURN_CSTRING(pstrdup(""));
|
2006-09-05 20:00:58 +02:00
|
|
|
|
2009-09-30 21:50:22 +02:00
|
|
|
buflen = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* this loop overestimates due to pessimistic assumptions about escaping,
|
|
|
|
* so very large hstore values can't be output. this could be fixed, but
|
|
|
|
* many other data types probably have the same issue. This replaced code
|
|
|
|
* that used the original varlena size for calculations, which was wrong
|
|
|
|
* in some subtle ways.
|
|
|
|
*/
|
2009-04-02 19:57:05 +02:00
|
|
|
|
2009-09-30 21:50:22 +02:00
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
{
|
|
|
|
/* include "" and => and comma-space */
|
2015-11-19 20:54:05 +01:00
|
|
|
buflen += 6 + 2 * HSTORE_KEYLEN(entries, i);
|
2009-09-30 21:50:22 +02:00
|
|
|
/* include "" only if nonnull */
|
2015-11-19 20:54:05 +01:00
|
|
|
buflen += 2 + (HSTORE_VALISNULL(entries, i)
|
2009-09-30 21:50:22 +02:00
|
|
|
? 2
|
2015-11-19 20:54:05 +01:00
|
|
|
: 2 * HSTORE_VALLEN(entries, i));
|
2009-09-30 21:50:22 +02:00
|
|
|
}
|
2006-10-04 02:30:14 +02:00
|
|
|
|
2006-09-05 20:00:58 +02:00
|
|
|
out = ptr = palloc(buflen);
|
2009-09-30 21:50:22 +02:00
|
|
|
|
|
|
|
for (i = 0; i < count; i++)
|
2006-09-05 20:00:58 +02:00
|
|
|
{
|
|
|
|
*ptr++ = '"';
|
2015-11-19 20:54:05 +01:00
|
|
|
ptr = cpw(ptr, HSTORE_KEY(entries, base, i), HSTORE_KEYLEN(entries, i));
|
2006-09-05 20:00:58 +02:00
|
|
|
*ptr++ = '"';
|
|
|
|
*ptr++ = '=';
|
|
|
|
*ptr++ = '>';
|
2015-11-19 20:54:05 +01:00
|
|
|
if (HSTORE_VALISNULL(entries, i))
|
2006-09-05 20:00:58 +02:00
|
|
|
{
|
|
|
|
*ptr++ = 'N';
|
|
|
|
*ptr++ = 'U';
|
|
|
|
*ptr++ = 'L';
|
|
|
|
*ptr++ = 'L';
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*ptr++ = '"';
|
2015-11-19 20:54:05 +01:00
|
|
|
ptr = cpw(ptr, HSTORE_VAL(entries, base, i), HSTORE_VALLEN(entries, i));
|
2006-09-05 20:00:58 +02:00
|
|
|
*ptr++ = '"';
|
|
|
|
}
|
|
|
|
|
2009-09-30 21:50:22 +02:00
|
|
|
if (i + 1 != count)
|
2006-09-05 20:00:58 +02:00
|
|
|
{
|
|
|
|
*ptr++ = ',';
|
|
|
|
*ptr++ = ' ';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*ptr = '\0';
|
|
|
|
|
|
|
|
PG_RETURN_CSTRING(out);
|
|
|
|
}
|
2009-09-30 21:50:22 +02:00
|
|
|
|
|
|
|
|
|
|
|
PG_FUNCTION_INFO_V1(hstore_send);
|
|
|
|
Datum
|
|
|
|
hstore_send(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2017-09-18 21:21:23 +02:00
|
|
|
HStore *in = PG_GETARG_HSTORE_P(0);
|
2009-09-30 21:50:22 +02:00
|
|
|
int i;
|
|
|
|
int count = HS_COUNT(in);
|
|
|
|
char *base = STRPTR(in);
|
|
|
|
HEntry *entries = ARRPTR(in);
|
|
|
|
StringInfoData buf;
|
|
|
|
|
|
|
|
pq_begintypsend(&buf);
|
|
|
|
|
2017-10-12 06:00:46 +02:00
|
|
|
pq_sendint32(&buf, count);
|
2009-09-30 21:50:22 +02:00
|
|
|
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
{
|
2015-11-19 20:54:05 +01:00
|
|
|
int32 keylen = HSTORE_KEYLEN(entries, i);
|
2010-02-26 03:01:40 +01:00
|
|
|
|
2017-10-12 06:00:46 +02:00
|
|
|
pq_sendint32(&buf, keylen);
|
2015-11-19 20:54:05 +01:00
|
|
|
pq_sendtext(&buf, HSTORE_KEY(entries, base, i), keylen);
|
|
|
|
if (HSTORE_VALISNULL(entries, i))
|
2009-09-30 21:50:22 +02:00
|
|
|
{
|
2017-10-12 06:00:46 +02:00
|
|
|
pq_sendint32(&buf, -1);
|
2009-09-30 21:50:22 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-11-19 20:54:05 +01:00
|
|
|
int32 vallen = HSTORE_VALLEN(entries, i);
|
2010-02-26 03:01:40 +01:00
|
|
|
|
2017-10-12 06:00:46 +02:00
|
|
|
pq_sendint32(&buf, vallen);
|
2015-11-19 20:54:05 +01:00
|
|
|
pq_sendtext(&buf, HSTORE_VAL(entries, base, i), vallen);
|
2009-09-30 21:50:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
|
|
|
|
}
|
2013-03-10 22:35:36 +01:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* hstore_to_json_loose
|
|
|
|
*
|
|
|
|
* This is a heuristic conversion to json which treats
|
|
|
|
* 't' and 'f' as booleans and strings that look like numbers as numbers,
|
|
|
|
* as long as they don't start with a leading zero followed by another digit
|
|
|
|
* (think zip codes or phone numbers starting with 0).
|
|
|
|
*/
|
|
|
|
PG_FUNCTION_INFO_V1(hstore_to_json_loose);
|
|
|
|
Datum
|
|
|
|
hstore_to_json_loose(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2017-09-18 21:21:23 +02:00
|
|
|
HStore *in = PG_GETARG_HSTORE_P(0);
|
2014-02-21 14:43:31 +01:00
|
|
|
int i;
|
2013-03-10 22:35:36 +01:00
|
|
|
int count = HS_COUNT(in);
|
|
|
|
char *base = STRPTR(in);
|
|
|
|
HEntry *entries = ARRPTR(in);
|
2014-02-21 14:43:31 +01:00
|
|
|
StringInfoData tmp,
|
2013-03-10 22:35:36 +01:00
|
|
|
dst;
|
|
|
|
|
|
|
|
if (count == 0)
|
2013-10-17 17:09:55 +02:00
|
|
|
PG_RETURN_TEXT_P(cstring_to_text_with_len("{}", 2));
|
2013-03-10 22:35:36 +01:00
|
|
|
|
2014-02-21 14:43:31 +01:00
|
|
|
initStringInfo(&tmp);
|
|
|
|
initStringInfo(&dst);
|
2013-03-10 22:35:36 +01:00
|
|
|
|
2014-02-21 14:43:31 +01:00
|
|
|
appendStringInfoChar(&dst, '{');
|
2013-03-10 22:35:36 +01:00
|
|
|
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
{
|
2014-02-21 14:43:31 +01:00
|
|
|
resetStringInfo(&tmp);
|
2015-11-19 20:54:05 +01:00
|
|
|
appendBinaryStringInfo(&tmp, HSTORE_KEY(entries, base, i),
|
|
|
|
HSTORE_KEYLEN(entries, i));
|
2014-02-21 14:43:31 +01:00
|
|
|
escape_json(&dst, tmp.data);
|
|
|
|
appendStringInfoString(&dst, ": ");
|
2015-11-19 20:54:05 +01:00
|
|
|
if (HSTORE_VALISNULL(entries, i))
|
2014-02-21 14:43:31 +01:00
|
|
|
appendStringInfoString(&dst, "null");
|
2013-03-10 22:35:36 +01:00
|
|
|
/* guess that values of 't' or 'f' are booleans */
|
2015-11-19 20:54:05 +01:00
|
|
|
else if (HSTORE_VALLEN(entries, i) == 1 &&
|
|
|
|
*(HSTORE_VAL(entries, base, i)) == 't')
|
2014-02-21 14:43:31 +01:00
|
|
|
appendStringInfoString(&dst, "true");
|
2015-11-19 20:54:05 +01:00
|
|
|
else if (HSTORE_VALLEN(entries, i) == 1 &&
|
|
|
|
*(HSTORE_VAL(entries, base, i)) == 'f')
|
2014-02-21 14:43:31 +01:00
|
|
|
appendStringInfoString(&dst, "false");
|
2013-03-10 22:35:36 +01:00
|
|
|
else
|
|
|
|
{
|
2014-02-21 14:43:31 +01:00
|
|
|
resetStringInfo(&tmp);
|
2015-11-19 20:54:05 +01:00
|
|
|
appendBinaryStringInfo(&tmp, HSTORE_VAL(entries, base, i),
|
|
|
|
HSTORE_VALLEN(entries, i));
|
2014-12-01 17:28:45 +01:00
|
|
|
if (IsValidJsonNumber(tmp.data, tmp.len))
|
2014-02-21 14:43:31 +01:00
|
|
|
appendBinaryStringInfo(&dst, tmp.data, tmp.len);
|
2013-03-10 22:35:36 +01:00
|
|
|
else
|
2014-02-21 14:43:31 +01:00
|
|
|
escape_json(&dst, tmp.data);
|
2013-03-10 22:35:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (i + 1 != count)
|
2014-02-21 14:43:31 +01:00
|
|
|
appendStringInfoString(&dst, ", ");
|
2013-03-10 22:35:36 +01:00
|
|
|
}
|
2014-02-21 14:43:31 +01:00
|
|
|
appendStringInfoChar(&dst, '}');
|
2013-03-10 22:35:36 +01:00
|
|
|
|
2014-02-21 14:43:31 +01:00
|
|
|
PG_RETURN_TEXT_P(cstring_to_text(dst.data));
|
2013-03-10 22:35:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
PG_FUNCTION_INFO_V1(hstore_to_json);
|
|
|
|
Datum
|
|
|
|
hstore_to_json(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2017-09-18 21:21:23 +02:00
|
|
|
HStore *in = PG_GETARG_HSTORE_P(0);
|
2014-02-21 14:43:31 +01:00
|
|
|
int i;
|
2013-03-10 22:35:36 +01:00
|
|
|
int count = HS_COUNT(in);
|
|
|
|
char *base = STRPTR(in);
|
|
|
|
HEntry *entries = ARRPTR(in);
|
2014-02-21 14:43:31 +01:00
|
|
|
StringInfoData tmp,
|
2013-03-10 22:35:36 +01:00
|
|
|
dst;
|
|
|
|
|
|
|
|
if (count == 0)
|
2013-10-17 17:09:55 +02:00
|
|
|
PG_RETURN_TEXT_P(cstring_to_text_with_len("{}", 2));
|
2013-03-10 22:35:36 +01:00
|
|
|
|
2014-02-21 14:43:31 +01:00
|
|
|
initStringInfo(&tmp);
|
|
|
|
initStringInfo(&dst);
|
2013-03-10 22:35:36 +01:00
|
|
|
|
2014-02-21 14:43:31 +01:00
|
|
|
appendStringInfoChar(&dst, '{');
|
2013-03-10 22:35:36 +01:00
|
|
|
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
{
|
2014-02-21 14:43:31 +01:00
|
|
|
resetStringInfo(&tmp);
|
2015-11-19 20:54:05 +01:00
|
|
|
appendBinaryStringInfo(&tmp, HSTORE_KEY(entries, base, i),
|
|
|
|
HSTORE_KEYLEN(entries, i));
|
2014-02-21 14:43:31 +01:00
|
|
|
escape_json(&dst, tmp.data);
|
|
|
|
appendStringInfoString(&dst, ": ");
|
2015-11-19 20:54:05 +01:00
|
|
|
if (HSTORE_VALISNULL(entries, i))
|
2014-02-21 14:43:31 +01:00
|
|
|
appendStringInfoString(&dst, "null");
|
2013-03-10 22:35:36 +01:00
|
|
|
else
|
|
|
|
{
|
2014-02-21 14:43:31 +01:00
|
|
|
resetStringInfo(&tmp);
|
2015-11-19 20:54:05 +01:00
|
|
|
appendBinaryStringInfo(&tmp, HSTORE_VAL(entries, base, i),
|
|
|
|
HSTORE_VALLEN(entries, i));
|
2014-02-21 14:43:31 +01:00
|
|
|
escape_json(&dst, tmp.data);
|
2013-03-10 22:35:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (i + 1 != count)
|
2014-02-21 14:43:31 +01:00
|
|
|
appendStringInfoString(&dst, ", ");
|
2013-03-10 22:35:36 +01:00
|
|
|
}
|
2014-02-21 14:43:31 +01:00
|
|
|
appendStringInfoChar(&dst, '}');
|
2013-03-10 22:35:36 +01:00
|
|
|
|
2014-02-21 14:43:31 +01:00
|
|
|
PG_RETURN_TEXT_P(cstring_to_text(dst.data));
|
2013-03-10 22:35:36 +01:00
|
|
|
}
|
Introduce jsonb, a structured format for storing json.
The new format accepts exactly the same data as the json type. However, it is
stored in a format that does not require reparsing the orgiginal text in order
to process it, making it much more suitable for indexing and other operations.
Insignificant whitespace is discarded, and the order of object keys is not
preserved. Neither are duplicate object keys kept - the later value for a given
key is the only one stored.
The new type has all the functions and operators that the json type has,
with the exception of the json generation functions (to_json, json_agg etc.)
and with identical semantics. In addition, there are operator classes for
hash and btree indexing, and two classes for GIN indexing, that have no
equivalent in the json type.
This feature grew out of previous work by Oleg Bartunov and Teodor Sigaev, which
was intended to provide similar facilities to a nested hstore type, but which
in the end proved to have some significant compatibility issues.
Authors: Oleg Bartunov, Teodor Sigaev, Peter Geoghegan and Andrew Dunstan.
Review: Andres Freund
2014-03-23 21:40:19 +01:00
|
|
|
|
|
|
|
PG_FUNCTION_INFO_V1(hstore_to_jsonb);
|
|
|
|
Datum
|
|
|
|
hstore_to_jsonb(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2017-09-18 21:21:23 +02:00
|
|
|
HStore *in = PG_GETARG_HSTORE_P(0);
|
Introduce jsonb, a structured format for storing json.
The new format accepts exactly the same data as the json type. However, it is
stored in a format that does not require reparsing the orgiginal text in order
to process it, making it much more suitable for indexing and other operations.
Insignificant whitespace is discarded, and the order of object keys is not
preserved. Neither are duplicate object keys kept - the later value for a given
key is the only one stored.
The new type has all the functions and operators that the json type has,
with the exception of the json generation functions (to_json, json_agg etc.)
and with identical semantics. In addition, there are operator classes for
hash and btree indexing, and two classes for GIN indexing, that have no
equivalent in the json type.
This feature grew out of previous work by Oleg Bartunov and Teodor Sigaev, which
was intended to provide similar facilities to a nested hstore type, but which
in the end proved to have some significant compatibility issues.
Authors: Oleg Bartunov, Teodor Sigaev, Peter Geoghegan and Andrew Dunstan.
Review: Andres Freund
2014-03-23 21:40:19 +01:00
|
|
|
int i;
|
|
|
|
int count = HS_COUNT(in);
|
|
|
|
char *base = STRPTR(in);
|
|
|
|
HEntry *entries = ARRPTR(in);
|
|
|
|
JsonbParseState *state = NULL;
|
|
|
|
JsonbValue *res;
|
|
|
|
|
2015-01-13 13:29:36 +01:00
|
|
|
(void) pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
|
Introduce jsonb, a structured format for storing json.
The new format accepts exactly the same data as the json type. However, it is
stored in a format that does not require reparsing the orgiginal text in order
to process it, making it much more suitable for indexing and other operations.
Insignificant whitespace is discarded, and the order of object keys is not
preserved. Neither are duplicate object keys kept - the later value for a given
key is the only one stored.
The new type has all the functions and operators that the json type has,
with the exception of the json generation functions (to_json, json_agg etc.)
and with identical semantics. In addition, there are operator classes for
hash and btree indexing, and two classes for GIN indexing, that have no
equivalent in the json type.
This feature grew out of previous work by Oleg Bartunov and Teodor Sigaev, which
was intended to provide similar facilities to a nested hstore type, but which
in the end proved to have some significant compatibility issues.
Authors: Oleg Bartunov, Teodor Sigaev, Peter Geoghegan and Andrew Dunstan.
Review: Andres Freund
2014-03-23 21:40:19 +01:00
|
|
|
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
{
|
|
|
|
JsonbValue key,
|
|
|
|
val;
|
|
|
|
|
|
|
|
key.type = jbvString;
|
2015-11-19 20:54:05 +01:00
|
|
|
key.val.string.len = HSTORE_KEYLEN(entries, i);
|
|
|
|
key.val.string.val = HSTORE_KEY(entries, base, i);
|
Introduce jsonb, a structured format for storing json.
The new format accepts exactly the same data as the json type. However, it is
stored in a format that does not require reparsing the orgiginal text in order
to process it, making it much more suitable for indexing and other operations.
Insignificant whitespace is discarded, and the order of object keys is not
preserved. Neither are duplicate object keys kept - the later value for a given
key is the only one stored.
The new type has all the functions and operators that the json type has,
with the exception of the json generation functions (to_json, json_agg etc.)
and with identical semantics. In addition, there are operator classes for
hash and btree indexing, and two classes for GIN indexing, that have no
equivalent in the json type.
This feature grew out of previous work by Oleg Bartunov and Teodor Sigaev, which
was intended to provide similar facilities to a nested hstore type, but which
in the end proved to have some significant compatibility issues.
Authors: Oleg Bartunov, Teodor Sigaev, Peter Geoghegan and Andrew Dunstan.
Review: Andres Freund
2014-03-23 21:40:19 +01:00
|
|
|
|
2015-01-13 13:29:36 +01:00
|
|
|
(void) pushJsonbValue(&state, WJB_KEY, &key);
|
Introduce jsonb, a structured format for storing json.
The new format accepts exactly the same data as the json type. However, it is
stored in a format that does not require reparsing the orgiginal text in order
to process it, making it much more suitable for indexing and other operations.
Insignificant whitespace is discarded, and the order of object keys is not
preserved. Neither are duplicate object keys kept - the later value for a given
key is the only one stored.
The new type has all the functions and operators that the json type has,
with the exception of the json generation functions (to_json, json_agg etc.)
and with identical semantics. In addition, there are operator classes for
hash and btree indexing, and two classes for GIN indexing, that have no
equivalent in the json type.
This feature grew out of previous work by Oleg Bartunov and Teodor Sigaev, which
was intended to provide similar facilities to a nested hstore type, but which
in the end proved to have some significant compatibility issues.
Authors: Oleg Bartunov, Teodor Sigaev, Peter Geoghegan and Andrew Dunstan.
Review: Andres Freund
2014-03-23 21:40:19 +01:00
|
|
|
|
2015-11-19 20:54:05 +01:00
|
|
|
if (HSTORE_VALISNULL(entries, i))
|
Introduce jsonb, a structured format for storing json.
The new format accepts exactly the same data as the json type. However, it is
stored in a format that does not require reparsing the orgiginal text in order
to process it, making it much more suitable for indexing and other operations.
Insignificant whitespace is discarded, and the order of object keys is not
preserved. Neither are duplicate object keys kept - the later value for a given
key is the only one stored.
The new type has all the functions and operators that the json type has,
with the exception of the json generation functions (to_json, json_agg etc.)
and with identical semantics. In addition, there are operator classes for
hash and btree indexing, and two classes for GIN indexing, that have no
equivalent in the json type.
This feature grew out of previous work by Oleg Bartunov and Teodor Sigaev, which
was intended to provide similar facilities to a nested hstore type, but which
in the end proved to have some significant compatibility issues.
Authors: Oleg Bartunov, Teodor Sigaev, Peter Geoghegan and Andrew Dunstan.
Review: Andres Freund
2014-03-23 21:40:19 +01:00
|
|
|
{
|
|
|
|
val.type = jbvNull;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
val.type = jbvString;
|
2015-11-19 20:54:05 +01:00
|
|
|
val.val.string.len = HSTORE_VALLEN(entries, i);
|
|
|
|
val.val.string.val = HSTORE_VAL(entries, base, i);
|
Introduce jsonb, a structured format for storing json.
The new format accepts exactly the same data as the json type. However, it is
stored in a format that does not require reparsing the orgiginal text in order
to process it, making it much more suitable for indexing and other operations.
Insignificant whitespace is discarded, and the order of object keys is not
preserved. Neither are duplicate object keys kept - the later value for a given
key is the only one stored.
The new type has all the functions and operators that the json type has,
with the exception of the json generation functions (to_json, json_agg etc.)
and with identical semantics. In addition, there are operator classes for
hash and btree indexing, and two classes for GIN indexing, that have no
equivalent in the json type.
This feature grew out of previous work by Oleg Bartunov and Teodor Sigaev, which
was intended to provide similar facilities to a nested hstore type, but which
in the end proved to have some significant compatibility issues.
Authors: Oleg Bartunov, Teodor Sigaev, Peter Geoghegan and Andrew Dunstan.
Review: Andres Freund
2014-03-23 21:40:19 +01:00
|
|
|
}
|
2015-01-13 13:29:36 +01:00
|
|
|
(void) pushJsonbValue(&state, WJB_VALUE, &val);
|
Introduce jsonb, a structured format for storing json.
The new format accepts exactly the same data as the json type. However, it is
stored in a format that does not require reparsing the orgiginal text in order
to process it, making it much more suitable for indexing and other operations.
Insignificant whitespace is discarded, and the order of object keys is not
preserved. Neither are duplicate object keys kept - the later value for a given
key is the only one stored.
The new type has all the functions and operators that the json type has,
with the exception of the json generation functions (to_json, json_agg etc.)
and with identical semantics. In addition, there are operator classes for
hash and btree indexing, and two classes for GIN indexing, that have no
equivalent in the json type.
This feature grew out of previous work by Oleg Bartunov and Teodor Sigaev, which
was intended to provide similar facilities to a nested hstore type, but which
in the end proved to have some significant compatibility issues.
Authors: Oleg Bartunov, Teodor Sigaev, Peter Geoghegan and Andrew Dunstan.
Review: Andres Freund
2014-03-23 21:40:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
res = pushJsonbValue(&state, WJB_END_OBJECT, NULL);
|
|
|
|
|
|
|
|
PG_RETURN_POINTER(JsonbValueToJsonb(res));
|
|
|
|
}
|
|
|
|
|
|
|
|
PG_FUNCTION_INFO_V1(hstore_to_jsonb_loose);
|
|
|
|
Datum
|
|
|
|
hstore_to_jsonb_loose(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2017-09-18 21:21:23 +02:00
|
|
|
HStore *in = PG_GETARG_HSTORE_P(0);
|
Introduce jsonb, a structured format for storing json.
The new format accepts exactly the same data as the json type. However, it is
stored in a format that does not require reparsing the orgiginal text in order
to process it, making it much more suitable for indexing and other operations.
Insignificant whitespace is discarded, and the order of object keys is not
preserved. Neither are duplicate object keys kept - the later value for a given
key is the only one stored.
The new type has all the functions and operators that the json type has,
with the exception of the json generation functions (to_json, json_agg etc.)
and with identical semantics. In addition, there are operator classes for
hash and btree indexing, and two classes for GIN indexing, that have no
equivalent in the json type.
This feature grew out of previous work by Oleg Bartunov and Teodor Sigaev, which
was intended to provide similar facilities to a nested hstore type, but which
in the end proved to have some significant compatibility issues.
Authors: Oleg Bartunov, Teodor Sigaev, Peter Geoghegan and Andrew Dunstan.
Review: Andres Freund
2014-03-23 21:40:19 +01:00
|
|
|
int i;
|
|
|
|
int count = HS_COUNT(in);
|
|
|
|
char *base = STRPTR(in);
|
|
|
|
HEntry *entries = ARRPTR(in);
|
|
|
|
JsonbParseState *state = NULL;
|
|
|
|
JsonbValue *res;
|
|
|
|
StringInfoData tmp;
|
|
|
|
|
|
|
|
initStringInfo(&tmp);
|
|
|
|
|
2015-01-13 13:29:36 +01:00
|
|
|
(void) pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
|
Introduce jsonb, a structured format for storing json.
The new format accepts exactly the same data as the json type. However, it is
stored in a format that does not require reparsing the orgiginal text in order
to process it, making it much more suitable for indexing and other operations.
Insignificant whitespace is discarded, and the order of object keys is not
preserved. Neither are duplicate object keys kept - the later value for a given
key is the only one stored.
The new type has all the functions and operators that the json type has,
with the exception of the json generation functions (to_json, json_agg etc.)
and with identical semantics. In addition, there are operator classes for
hash and btree indexing, and two classes for GIN indexing, that have no
equivalent in the json type.
This feature grew out of previous work by Oleg Bartunov and Teodor Sigaev, which
was intended to provide similar facilities to a nested hstore type, but which
in the end proved to have some significant compatibility issues.
Authors: Oleg Bartunov, Teodor Sigaev, Peter Geoghegan and Andrew Dunstan.
Review: Andres Freund
2014-03-23 21:40:19 +01:00
|
|
|
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
{
|
|
|
|
JsonbValue key,
|
|
|
|
val;
|
|
|
|
|
|
|
|
key.type = jbvString;
|
2015-11-19 20:54:05 +01:00
|
|
|
key.val.string.len = HSTORE_KEYLEN(entries, i);
|
|
|
|
key.val.string.val = HSTORE_KEY(entries, base, i);
|
Introduce jsonb, a structured format for storing json.
The new format accepts exactly the same data as the json type. However, it is
stored in a format that does not require reparsing the orgiginal text in order
to process it, making it much more suitable for indexing and other operations.
Insignificant whitespace is discarded, and the order of object keys is not
preserved. Neither are duplicate object keys kept - the later value for a given
key is the only one stored.
The new type has all the functions and operators that the json type has,
with the exception of the json generation functions (to_json, json_agg etc.)
and with identical semantics. In addition, there are operator classes for
hash and btree indexing, and two classes for GIN indexing, that have no
equivalent in the json type.
This feature grew out of previous work by Oleg Bartunov and Teodor Sigaev, which
was intended to provide similar facilities to a nested hstore type, but which
in the end proved to have some significant compatibility issues.
Authors: Oleg Bartunov, Teodor Sigaev, Peter Geoghegan and Andrew Dunstan.
Review: Andres Freund
2014-03-23 21:40:19 +01:00
|
|
|
|
2015-01-13 13:29:36 +01:00
|
|
|
(void) pushJsonbValue(&state, WJB_KEY, &key);
|
Introduce jsonb, a structured format for storing json.
The new format accepts exactly the same data as the json type. However, it is
stored in a format that does not require reparsing the orgiginal text in order
to process it, making it much more suitable for indexing and other operations.
Insignificant whitespace is discarded, and the order of object keys is not
preserved. Neither are duplicate object keys kept - the later value for a given
key is the only one stored.
The new type has all the functions and operators that the json type has,
with the exception of the json generation functions (to_json, json_agg etc.)
and with identical semantics. In addition, there are operator classes for
hash and btree indexing, and two classes for GIN indexing, that have no
equivalent in the json type.
This feature grew out of previous work by Oleg Bartunov and Teodor Sigaev, which
was intended to provide similar facilities to a nested hstore type, but which
in the end proved to have some significant compatibility issues.
Authors: Oleg Bartunov, Teodor Sigaev, Peter Geoghegan and Andrew Dunstan.
Review: Andres Freund
2014-03-23 21:40:19 +01:00
|
|
|
|
2015-11-19 20:54:05 +01:00
|
|
|
if (HSTORE_VALISNULL(entries, i))
|
Introduce jsonb, a structured format for storing json.
The new format accepts exactly the same data as the json type. However, it is
stored in a format that does not require reparsing the orgiginal text in order
to process it, making it much more suitable for indexing and other operations.
Insignificant whitespace is discarded, and the order of object keys is not
preserved. Neither are duplicate object keys kept - the later value for a given
key is the only one stored.
The new type has all the functions and operators that the json type has,
with the exception of the json generation functions (to_json, json_agg etc.)
and with identical semantics. In addition, there are operator classes for
hash and btree indexing, and two classes for GIN indexing, that have no
equivalent in the json type.
This feature grew out of previous work by Oleg Bartunov and Teodor Sigaev, which
was intended to provide similar facilities to a nested hstore type, but which
in the end proved to have some significant compatibility issues.
Authors: Oleg Bartunov, Teodor Sigaev, Peter Geoghegan and Andrew Dunstan.
Review: Andres Freund
2014-03-23 21:40:19 +01:00
|
|
|
{
|
|
|
|
val.type = jbvNull;
|
|
|
|
}
|
|
|
|
/* guess that values of 't' or 'f' are booleans */
|
2015-11-19 20:54:05 +01:00
|
|
|
else if (HSTORE_VALLEN(entries, i) == 1 &&
|
|
|
|
*(HSTORE_VAL(entries, base, i)) == 't')
|
Introduce jsonb, a structured format for storing json.
The new format accepts exactly the same data as the json type. However, it is
stored in a format that does not require reparsing the orgiginal text in order
to process it, making it much more suitable for indexing and other operations.
Insignificant whitespace is discarded, and the order of object keys is not
preserved. Neither are duplicate object keys kept - the later value for a given
key is the only one stored.
The new type has all the functions and operators that the json type has,
with the exception of the json generation functions (to_json, json_agg etc.)
and with identical semantics. In addition, there are operator classes for
hash and btree indexing, and two classes for GIN indexing, that have no
equivalent in the json type.
This feature grew out of previous work by Oleg Bartunov and Teodor Sigaev, which
was intended to provide similar facilities to a nested hstore type, but which
in the end proved to have some significant compatibility issues.
Authors: Oleg Bartunov, Teodor Sigaev, Peter Geoghegan and Andrew Dunstan.
Review: Andres Freund
2014-03-23 21:40:19 +01:00
|
|
|
{
|
|
|
|
val.type = jbvBool;
|
2014-04-02 20:30:08 +02:00
|
|
|
val.val.boolean = true;
|
Introduce jsonb, a structured format for storing json.
The new format accepts exactly the same data as the json type. However, it is
stored in a format that does not require reparsing the orgiginal text in order
to process it, making it much more suitable for indexing and other operations.
Insignificant whitespace is discarded, and the order of object keys is not
preserved. Neither are duplicate object keys kept - the later value for a given
key is the only one stored.
The new type has all the functions and operators that the json type has,
with the exception of the json generation functions (to_json, json_agg etc.)
and with identical semantics. In addition, there are operator classes for
hash and btree indexing, and two classes for GIN indexing, that have no
equivalent in the json type.
This feature grew out of previous work by Oleg Bartunov and Teodor Sigaev, which
was intended to provide similar facilities to a nested hstore type, but which
in the end proved to have some significant compatibility issues.
Authors: Oleg Bartunov, Teodor Sigaev, Peter Geoghegan and Andrew Dunstan.
Review: Andres Freund
2014-03-23 21:40:19 +01:00
|
|
|
}
|
2015-11-19 20:54:05 +01:00
|
|
|
else if (HSTORE_VALLEN(entries, i) == 1 &&
|
|
|
|
*(HSTORE_VAL(entries, base, i)) == 'f')
|
Introduce jsonb, a structured format for storing json.
The new format accepts exactly the same data as the json type. However, it is
stored in a format that does not require reparsing the orgiginal text in order
to process it, making it much more suitable for indexing and other operations.
Insignificant whitespace is discarded, and the order of object keys is not
preserved. Neither are duplicate object keys kept - the later value for a given
key is the only one stored.
The new type has all the functions and operators that the json type has,
with the exception of the json generation functions (to_json, json_agg etc.)
and with identical semantics. In addition, there are operator classes for
hash and btree indexing, and two classes for GIN indexing, that have no
equivalent in the json type.
This feature grew out of previous work by Oleg Bartunov and Teodor Sigaev, which
was intended to provide similar facilities to a nested hstore type, but which
in the end proved to have some significant compatibility issues.
Authors: Oleg Bartunov, Teodor Sigaev, Peter Geoghegan and Andrew Dunstan.
Review: Andres Freund
2014-03-23 21:40:19 +01:00
|
|
|
{
|
|
|
|
val.type = jbvBool;
|
2014-04-02 20:30:08 +02:00
|
|
|
val.val.boolean = false;
|
Introduce jsonb, a structured format for storing json.
The new format accepts exactly the same data as the json type. However, it is
stored in a format that does not require reparsing the orgiginal text in order
to process it, making it much more suitable for indexing and other operations.
Insignificant whitespace is discarded, and the order of object keys is not
preserved. Neither are duplicate object keys kept - the later value for a given
key is the only one stored.
The new type has all the functions and operators that the json type has,
with the exception of the json generation functions (to_json, json_agg etc.)
and with identical semantics. In addition, there are operator classes for
hash and btree indexing, and two classes for GIN indexing, that have no
equivalent in the json type.
This feature grew out of previous work by Oleg Bartunov and Teodor Sigaev, which
was intended to provide similar facilities to a nested hstore type, but which
in the end proved to have some significant compatibility issues.
Authors: Oleg Bartunov, Teodor Sigaev, Peter Geoghegan and Andrew Dunstan.
Review: Andres Freund
2014-03-23 21:40:19 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
resetStringInfo(&tmp);
|
2015-11-19 20:54:05 +01:00
|
|
|
appendBinaryStringInfo(&tmp, HSTORE_VAL(entries, base, i),
|
|
|
|
HSTORE_VALLEN(entries, i));
|
Make hstore_to_jsonb_loose match hstore_to_json_loose on what's a number.
Commit e09996ff8dee3f70 removed some ad-hoc code in hstore_to_json_loose
that determined whether an hstore value string looked like a number,
in favor of calling the JSON parser's is-it-a-number code. However,
it neglected the fact that the exact same code appeared in
hstore_to_jsonb_loose.
This is not a bug, exactly, because the requirements on the two functions
are not the same: hstore_to_json_loose must accept only syntactically legal
JSON numbers as numbers, or it will produce invalid JSON output, as per bug
#12070 which spawned the prior commit. But hstore_to_jsonb_loose could
accept anything that numeric_in will eat, other than Inf and NaN.
Nonetheless it seems surprising and arbitrary that the two functions don't
use the same rules for what is a number versus what is a string; especially
since they did use the same rules before the aforesaid commit. For one
thing, that means that doing hstore_to_json_loose and then casting to jsonb
can produce results different from doing just hstore_to_jsonb_loose.
Hence, change hstore_to_jsonb_loose's logic to match hstore_to_json_loose,
ie, hstore values are treated as numbers when they match the JSON syntax
for numbers.
No back-patch, since this is more in the nature of a definitional change
than a bug fix.
2016-02-03 18:03:50 +01:00
|
|
|
if (IsValidJsonNumber(tmp.data, tmp.len))
|
Introduce jsonb, a structured format for storing json.
The new format accepts exactly the same data as the json type. However, it is
stored in a format that does not require reparsing the orgiginal text in order
to process it, making it much more suitable for indexing and other operations.
Insignificant whitespace is discarded, and the order of object keys is not
preserved. Neither are duplicate object keys kept - the later value for a given
key is the only one stored.
The new type has all the functions and operators that the json type has,
with the exception of the json generation functions (to_json, json_agg etc.)
and with identical semantics. In addition, there are operator classes for
hash and btree indexing, and two classes for GIN indexing, that have no
equivalent in the json type.
This feature grew out of previous work by Oleg Bartunov and Teodor Sigaev, which
was intended to provide similar facilities to a nested hstore type, but which
in the end proved to have some significant compatibility issues.
Authors: Oleg Bartunov, Teodor Sigaev, Peter Geoghegan and Andrew Dunstan.
Review: Andres Freund
2014-03-23 21:40:19 +01:00
|
|
|
{
|
2018-05-02 21:52:54 +02:00
|
|
|
Datum numd;
|
|
|
|
|
Introduce jsonb, a structured format for storing json.
The new format accepts exactly the same data as the json type. However, it is
stored in a format that does not require reparsing the orgiginal text in order
to process it, making it much more suitable for indexing and other operations.
Insignificant whitespace is discarded, and the order of object keys is not
preserved. Neither are duplicate object keys kept - the later value for a given
key is the only one stored.
The new type has all the functions and operators that the json type has,
with the exception of the json generation functions (to_json, json_agg etc.)
and with identical semantics. In addition, there are operator classes for
hash and btree indexing, and two classes for GIN indexing, that have no
equivalent in the json type.
This feature grew out of previous work by Oleg Bartunov and Teodor Sigaev, which
was intended to provide similar facilities to a nested hstore type, but which
in the end proved to have some significant compatibility issues.
Authors: Oleg Bartunov, Teodor Sigaev, Peter Geoghegan and Andrew Dunstan.
Review: Andres Freund
2014-03-23 21:40:19 +01:00
|
|
|
val.type = jbvNumeric;
|
2018-05-02 21:52:54 +02:00
|
|
|
numd = DirectFunctionCall3(numeric_in,
|
|
|
|
CStringGetDatum(tmp.data),
|
|
|
|
ObjectIdGetDatum(InvalidOid),
|
|
|
|
Int32GetDatum(-1));
|
|
|
|
val.val.numeric = DatumGetNumeric(numd);
|
Introduce jsonb, a structured format for storing json.
The new format accepts exactly the same data as the json type. However, it is
stored in a format that does not require reparsing the orgiginal text in order
to process it, making it much more suitable for indexing and other operations.
Insignificant whitespace is discarded, and the order of object keys is not
preserved. Neither are duplicate object keys kept - the later value for a given
key is the only one stored.
The new type has all the functions and operators that the json type has,
with the exception of the json generation functions (to_json, json_agg etc.)
and with identical semantics. In addition, there are operator classes for
hash and btree indexing, and two classes for GIN indexing, that have no
equivalent in the json type.
This feature grew out of previous work by Oleg Bartunov and Teodor Sigaev, which
was intended to provide similar facilities to a nested hstore type, but which
in the end proved to have some significant compatibility issues.
Authors: Oleg Bartunov, Teodor Sigaev, Peter Geoghegan and Andrew Dunstan.
Review: Andres Freund
2014-03-23 21:40:19 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
val.type = jbvString;
|
2015-11-19 20:54:05 +01:00
|
|
|
val.val.string.len = HSTORE_VALLEN(entries, i);
|
|
|
|
val.val.string.val = HSTORE_VAL(entries, base, i);
|
Introduce jsonb, a structured format for storing json.
The new format accepts exactly the same data as the json type. However, it is
stored in a format that does not require reparsing the orgiginal text in order
to process it, making it much more suitable for indexing and other operations.
Insignificant whitespace is discarded, and the order of object keys is not
preserved. Neither are duplicate object keys kept - the later value for a given
key is the only one stored.
The new type has all the functions and operators that the json type has,
with the exception of the json generation functions (to_json, json_agg etc.)
and with identical semantics. In addition, there are operator classes for
hash and btree indexing, and two classes for GIN indexing, that have no
equivalent in the json type.
This feature grew out of previous work by Oleg Bartunov and Teodor Sigaev, which
was intended to provide similar facilities to a nested hstore type, but which
in the end proved to have some significant compatibility issues.
Authors: Oleg Bartunov, Teodor Sigaev, Peter Geoghegan and Andrew Dunstan.
Review: Andres Freund
2014-03-23 21:40:19 +01:00
|
|
|
}
|
|
|
|
}
|
2015-01-13 13:29:36 +01:00
|
|
|
(void) pushJsonbValue(&state, WJB_VALUE, &val);
|
Introduce jsonb, a structured format for storing json.
The new format accepts exactly the same data as the json type. However, it is
stored in a format that does not require reparsing the orgiginal text in order
to process it, making it much more suitable for indexing and other operations.
Insignificant whitespace is discarded, and the order of object keys is not
preserved. Neither are duplicate object keys kept - the later value for a given
key is the only one stored.
The new type has all the functions and operators that the json type has,
with the exception of the json generation functions (to_json, json_agg etc.)
and with identical semantics. In addition, there are operator classes for
hash and btree indexing, and two classes for GIN indexing, that have no
equivalent in the json type.
This feature grew out of previous work by Oleg Bartunov and Teodor Sigaev, which
was intended to provide similar facilities to a nested hstore type, but which
in the end proved to have some significant compatibility issues.
Authors: Oleg Bartunov, Teodor Sigaev, Peter Geoghegan and Andrew Dunstan.
Review: Andres Freund
2014-03-23 21:40:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
res = pushJsonbValue(&state, WJB_END_OBJECT, NULL);
|
|
|
|
|
|
|
|
PG_RETURN_POINTER(JsonbValueToJsonb(res));
|
|
|
|
}
|