openGauss-server/contrib/hstore/hstore_io.cpp

1035 lines
32 KiB
C++
Executable File

/*
* contrib/hstore/hstore_io.c
*/
#include "postgres.h"
#include "knl/knl_variable.h"
#include <ctype.h>
#include "catalog/pg_type.h"
#include "funcapi.h"
#include "libpq/pqformat.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/typcache.h"
#include "hstore.h"
PG_MODULE_MAGIC;
/* old names for C functions */
HSTORE_POLLUTE(hstore_from_text, tconvert);
typedef struct {
char* begin;
char* ptr;
char* cur;
char* word;
int wordlen;
Pairs* pairs;
int pcur;
int plen;
} HSParser;
#define RESIZEPRSBUF \
do { \
if (state->cur - state->word + 1 >= state->wordlen) { \
int4 clen = state->cur - state->word; \
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;
state->wordlen = 32;
state->cur = state->word = (char*)palloc(state->wordlen);
*escaped = false;
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) {
elog(ERROR, "Syntax error near '%c' at position %d", *(state->ptr), (int4)(state->ptr - state->begin));
} else if (*(state->ptr) == '\\') {
st = GV_WAITESCIN;
} else if (!isspace((unsigned char)*(state->ptr))) {
*(state->cur) = *(state->ptr);
state->cur++;
st = GV_INVAL;
}
} else if (st == GV_INVAL) {
if (*(state->ptr) == '\\') {
st = GV_WAITESCIN;
} else if (*(state->ptr) == '=' && !ignoreeq) {
state->ptr--;
return true;
} else if (*(state->ptr) == ',' && ignoreeq) {
state->ptr--;
return true;
} else if (isspace((unsigned char)*(state->ptr))) {
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) == '\\') {
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
elog(ERROR, "Unknown state %d at position line %d in file '%s'", st, __LINE__, __FILE__);
state->ptr++;
}
return false;
}
#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;
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;
state->pairs[state->pcur].keylen = hstoreCheckKeyLen(state->cur - state->word);
state->pairs[state->pcur].val = NULL;
state->word = NULL;
st = WEQ;
} else if (st == WEQ) {
if (*(state->ptr) == '=') {
st = WGT;
} else if (*(state->ptr) == '\0') {
elog(ERROR, "Unexpected end of string");
} else if (!isspace((unsigned char)*(state->ptr))) {
elog(ERROR, "Syntax error near '%c' at position %d", *(state->ptr), (int4)(state->ptr - state->begin));
}
} else if (st == WGT) {
if (*(state->ptr) == '>') {
st = WVAL;
} else if (*(state->ptr) == '\0') {
elog(ERROR, "Unexpected end of string");
} else {
elog(ERROR, "Syntax error near '%c' at position %d", *(state->ptr), (int4)(state->ptr - state->begin));
}
} else if (st == WVAL) {
if (!get_val(state, true, &escaped))
elog(ERROR, "Unexpected end of string");
state->pairs[state->pcur].val = state->word;
state->pairs[state->pcur].vallen = hstoreCheckValLen(state->cur - state->word);
state->pairs[state->pcur].isnull = false;
state->pairs[state->pcur].needfree = true;
if (state->cur - state->word == 4 && !escaped) {
state->word[4] = '\0';
if (0 == pg_strcasecmp(state->word, "null"))
state->pairs[state->pcur].isnull = true;
}
state->word = NULL;
state->pcur++;
st = WDEL;
} else if (st == WDEL) {
if (*(state->ptr) == ',') {
st = WKEY;
} else if (*(state->ptr) == '\0') {
return;
} else if (!isspace((unsigned char)*(state->ptr))) {
elog(ERROR, "Syntax error near '%c' at position %d", *(state->ptr), (int4)(state->ptr - state->begin));
}
} else
elog(ERROR, "Unknown state %d at line %d in file '%s'", st, __LINE__, __FILE__);
state->ptr++;
}
}
static int comparePairs(const void* a, const void* b)
{
const Pairs* pa = (const Pairs*)a;
const Pairs* pb = (const Pairs*)b;
if (pa->keylen == pb->keylen) {
int res = memcmp(pa->key, pb->key, pa->keylen);
if (res)
return res;
/* guarantee that needfree will be later */
if (pb->needfree == pa->needfree) {
return 0;
} else if (pa->needfree) {
return 1;
} else {
return -1;
}
}
return (pa->keylen > pb->keylen) ? 1 : -1;
}
/*
* 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.
*/
int hstoreUniquePairs(Pairs* a, int4 l, int4* buflen)
{
Pairs *ptr = NULL, *res = NULL;
*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) {
if (ptr->keylen == res->keylen && memcmp(ptr->key, res->key, res->keylen) == 0) {
if (ptr->needfree) {
pfree_ext(ptr->key);
pfree_ext(ptr->val);
}
} else {
*buflen += res->keylen + ((res->isnull) ? 0 : res->vallen);
res++;
errno_t rc = memcpy_s(res, sizeof(Pairs), ptr, sizeof(Pairs));
securec_check(rc, "", "");
}
ptr++;
}
*buflen += res->keylen + ((res->isnull) ? 0 : res->vallen);
return res + 1 - a;
}
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;
}
HStore* hstorePairs(Pairs* pairs, int4 pcount, int4 buflen)
{
HStore* out = NULL;
HEntry* entry = NULL;
char* ptr = NULL;
char* buf = NULL;
int4 len;
int4 i;
len = CALCDATASIZE(pcount, buflen);
out = (HStore*)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], len);
HS_FINALIZE(out, len, (uint32)pcount, buf, ptr);
return out;
}
PG_FUNCTION_INFO_V1(hstore_in);
extern "C" Datum hstore_in(PG_FUNCTION_ARGS);
Datum hstore_in(PG_FUNCTION_ARGS)
{
HSParser state;
int4 buflen;
HStore* out = NULL;
state.begin = PG_GETARG_CSTRING(0);
parse_hstore(&state);
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);
extern "C" Datum hstore_recv(PG_FUNCTION_ARGS);
Datum hstore_recv(PG_FUNCTION_ARGS)
{
int4 buflen;
HStore* out = NULL;
Pairs* pairs = NULL;
int4 i;
int4 pcount;
StringInfo buf = (StringInfo)PG_GETARG_POINTER(0);
pcount = pq_getmsgint(buf, 4);
if (pcount == 0) {
out = hstorePairs(NULL, 0, 0);
PG_RETURN_POINTER(out);
}
if (pcount < 0 || pcount > (int)(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)))));
pairs = (Pairs*)palloc(pcount * sizeof(Pairs));
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);
extern "C" Datum hstore_from_text(PG_FUNCTION_ARGS);
Datum hstore_from_text(PG_FUNCTION_ARGS)
{
text* key = NULL;
text* val = NULL;
Pairs p;
HStore* out = NULL;
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);
extern "C" Datum hstore_from_arrays(PG_FUNCTION_ARGS);
Datum hstore_from_arrays(PG_FUNCTION_ARGS)
{
int4 buflen;
HStore* out = NULL;
Pairs* pairs = NULL;
Datum* key_datums = NULL;
bool* key_nulls = NULL;
int key_count;
Datum* value_datums = NULL;
bool* value_nulls = NULL;
int value_count;
ArrayType* key_array = NULL;
ArrayType* value_array = NULL;
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
*/
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);
/* see discussion in hstoreArrayToPairs() */
if (key_count > (int)(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)))));
/* value_array might be NULL */
if (PG_ARGISNULL(1)) {
value_array = NULL;
value_count = key_count;
value_datums = NULL;
value_nulls = NULL;
} else {
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);
Assert(key_count == value_count);
}
pairs = (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 == NULL || value_nulls[i]) {
pairs[i].key = VARDATA_ANY(key_datums[i]);
pairs[i].val = NULL;
pairs[i].keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(key_datums[i]));
pairs[i].vallen = 4;
pairs[i].isnull = true;
pairs[i].needfree = false;
} else {
pairs[i].key = VARDATA_ANY(key_datums[i]);
pairs[i].val = VARDATA_ANY(value_datums[i]);
pairs[i].keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(key_datums[i]));
pairs[i].vallen = hstoreCheckValLen(VARSIZE_ANY_EXHDR(value_datums[i]));
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);
extern "C" Datum hstore_from_array(PG_FUNCTION_ARGS);
Datum hstore_from_array(PG_FUNCTION_ARGS)
{
ArrayType* in_array = PG_GETARG_ARRAYTYPE_P(0);
int ndims = ARR_NDIM(in_array);
int count;
int4 buflen;
HStore* out = NULL;
Pairs* pairs = NULL;
Datum* in_datums = NULL;
bool* in_nulls = NULL;
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;
/* see discussion in hstoreArrayToPairs() */
if (count > (int)(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)))));
pairs = (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]) {
pairs[i].key = VARDATA_ANY(in_datums[i * 2]);
pairs[i].val = NULL;
pairs[i].keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(in_datums[i * 2]));
pairs[i].vallen = 4;
pairs[i].isnull = true;
pairs[i].needfree = false;
} else {
pairs[i].key = VARDATA_ANY(in_datums[i * 2]);
pairs[i].val = VARDATA_ANY(in_datums[i * 2 + 1]);
pairs[i].keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(in_datums[i * 2]));
pairs[i].vallen = hstoreCheckValLen(VARSIZE_ANY_EXHDR(in_datums[i * 2 + 1]));
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;
int ncolumns;
ColumnIOData columns[1]; /* VARIABLE LENGTH ARRAY */
} RecordIOData;
PG_FUNCTION_INFO_V1(hstore_from_record);
extern "C" Datum hstore_from_record(PG_FUNCTION_ARGS);
Datum hstore_from_record(PG_FUNCTION_ARGS)
{
HeapTupleHeader rec;
int4 buflen;
HStore* out = NULL;
Pairs* pairs = NULL;
Oid tupType;
int32 tupTypmod;
TupleDesc tupdesc;
HeapTupleData tuple;
RecordIOData* my_extra = NULL;
int ncolumns;
int i, j;
Datum* values = NULL;
bool* nulls = NULL;
if (PG_ARGISNULL(0)) {
Oid argtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
/*
* have no tuple to look at, so the only source of type info is the
* argtype. The lookup_rowtype_tupdesc call below will error out if we
* don't have a known composite type oid here.
*/
tupType = argtype;
tupTypmod = -1;
rec = NULL;
} else {
rec = PG_GETARG_HEAPTUPLEHEADER(0);
/* Extract type info from the tuple itself */
tupType = HeapTupleHeaderGetTypeId(rec);
tupTypmod = HeapTupleHeaderGetTypMod(rec);
}
tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
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, sizeof(RecordIOData) - sizeof(ColumnIOData) + ncolumns * sizeof(ColumnIOData));
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) {
int rc = memset_s(my_extra, sizeof(RecordIOData) - sizeof(ColumnIOData) + ncolumns * sizeof(ColumnIOData),
0, sizeof(RecordIOData) - sizeof(ColumnIOData) + ncolumns * sizeof(ColumnIOData));
securec_check(rc, "", "");
my_extra->record_type = tupType;
my_extra->record_typmod = tupTypmod;
my_extra->ncolumns = ncolumns;
}
Assert(ncolumns <= MaxTupleAttributeNumber); /* thus, no overflow */
pairs = (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_bucketId = InvalidBktId;
HeapTupleSetZeroBase(&tuple);
#ifdef PGXC
tuple.t_xc_node_id = 0;
#endif
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];
Oid column_type = tupdesc->attrs[i]->atttypid;
char* value = NULL;
/* Ignore dropped columns in datatype */
if (tupdesc->attrs[i]->attisdropped)
continue;
pairs[j].key = NameStr(tupdesc->attrs[i]->attname);
pairs[j].keylen = hstoreCheckKeyLen(strlen(NameStr(tupdesc->attrs[i]->attname)));
if (nulls == NULL || nulls[i]) {
pairs[j].val = NULL;
pairs[j].vallen = 4;
pairs[j].isnull = true;
pairs[j].needfree = false;
++j;
continue;
}
/*
* Convert the column value to text
*/
if (column_info->column_type != column_type) {
bool typIsVarlena = false;
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;
}
ncolumns = hstoreUniquePairs(pairs, j, &buflen);
out = hstorePairs(pairs, ncolumns, buflen);
ReleaseTupleDesc(tupdesc);
PG_RETURN_POINTER(out);
}
PG_FUNCTION_INFO_V1(hstore_populate_record);
extern "C" Datum hstore_populate_record(PG_FUNCTION_ARGS);
Datum hstore_populate_record(PG_FUNCTION_ARGS)
{
Oid argtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
HStore* hs = NULL;
HEntry* entries = NULL;
char* ptr = NULL;
HeapTupleHeader rec;
Oid tupType;
int32 tupTypmod;
TupleDesc tupdesc;
HeapTupleData tuple;
HeapTuple rettuple;
RecordIOData* my_extra = NULL;
int ncolumns;
int i;
Datum* values = NULL;
bool* nulls = NULL;
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;
/*
* have no tuple to look at, so the only source of type info is the
* argtype. The lookup_rowtype_tupdesc call below will error out if we
* don't have a known composite type oid here.
*/
tupType = argtype;
tupTypmod = -1;
} else {
rec = PG_GETARG_HEAPTUPLEHEADER(0);
if (PG_ARGISNULL(1))
PG_RETURN_POINTER(rec);
/* Extract type info from the tuple itself */
tupType = HeapTupleHeaderGetTypeId(rec);
tupTypmod = HeapTupleHeaderGetTypMod(rec);
}
hs = PG_GETARG_HS(1);
NOT_NULL_HS(hs);
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);
tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
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_bucketId = InvalidBktId;
HeapTupleSetZeroBase(&tuple);
#ifdef PGXC
tuple.t_xc_node_id = 0;
#endif
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, sizeof(RecordIOData) - sizeof(ColumnIOData) + ncolumns * sizeof(ColumnIOData));
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) {
int rc = memset_s(my_extra, sizeof(RecordIOData) - sizeof(ColumnIOData) + ncolumns * sizeof(ColumnIOData),
0, sizeof(RecordIOData) - sizeof(ColumnIOData) + ncolumns * sizeof(ColumnIOData));
securec_check(rc, "", "");
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];
Oid column_type = tupdesc->attrs[i]->atttypid;
char* value = NULL;
int idx;
int vallen;
/* Ignore dropped columns in datatype */
if (tupdesc->attrs[i]->attisdropped) {
nulls[i] = true;
continue;
}
idx = hstoreFindKey(hs, 0, NameStr(tupdesc->attrs[i]->attname), strlen(NameStr(tupdesc->attrs[i]->attname)));
/*
* 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;
}
if (idx < 0 || HS_VALISNULL(entries, idx)) {
/*
* need InputFunctionCall to happen even for nulls, so that domain
* checks are done
*/
values[i] =
InputFunctionCall(&column_info->proc, NULL, column_info->typioparam, tupdesc->attrs[i]->atttypmod);
nulls[i] = true;
} else {
vallen = HS_VALLEN(entries, idx);
value = (char*)palloc(1 + vallen);
errno_t rc = memcpy_s(value, 1 + vallen, HS_VAL(entries, ptr, idx), vallen);
securec_check(rc, "", "");
value[vallen] = 0;
values[i] =
InputFunctionCall(&column_info->proc, value, column_info->typioparam, tupdesc->attrs[i]->atttypmod);
nulls[i] = false;
}
}
rettuple = heap_form_tuple(tupdesc, values, nulls);
ReleaseTupleDesc(tupdesc);
PG_RETURN_DATUM(HeapTupleGetDatum(rettuple));
}
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);
extern "C" Datum hstore_out(PG_FUNCTION_ARGS);
Datum hstore_out(PG_FUNCTION_ARGS)
{
HStore* in = PG_GETARG_HS(0);
NOT_NULL_HS(in);
int buflen, i;
int count = HS_COUNT(in);
char *out = NULL, *ptr = NULL;
char* base = STRPTR(in);
HEntry* entries = ARRPTR(in);
if (count == 0) {
out = (char*)palloc(1);
*out = '\0';
PG_RETURN_CSTRING(out);
}
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.
*/
for (i = 0; i < count; i++) {
/* include "" and => and comma-space */
buflen += 6 + 2 * HS_KEYLEN(entries, i);
/* include "" only if nonnull */
buflen += 2 + (HS_VALISNULL(entries, i) ? 2 : (2 * HS_VALLEN(entries, i)));
}
out = ptr = (char*)palloc(buflen);
for (i = 0; i < count; i++) {
*ptr++ = '"';
ptr = cpw(ptr, HS_KEY(entries, base, i), HS_KEYLEN(entries, i));
*ptr++ = '"';
*ptr++ = '=';
*ptr++ = '>';
if (HS_VALISNULL(entries, i)) {
*ptr++ = 'N';
*ptr++ = 'U';
*ptr++ = 'L';
*ptr++ = 'L';
} else {
*ptr++ = '"';
ptr = cpw(ptr, HS_VAL(entries, base, i), HS_VALLEN(entries, i));
*ptr++ = '"';
}
if (i + 1 != count) {
*ptr++ = ',';
*ptr++ = ' ';
}
}
*ptr = '\0';
PG_RETURN_CSTRING(out);
}
PG_FUNCTION_INFO_V1(hstore_send);
extern "C" Datum hstore_send(PG_FUNCTION_ARGS);
Datum hstore_send(PG_FUNCTION_ARGS)
{
HStore* in = PG_GETARG_HS(0);
NOT_NULL_HS(in);
int i;
int count = HS_COUNT(in);
char* base = STRPTR(in);
HEntry* entries = ARRPTR(in);
StringInfoData buf;
pq_begintypsend(&buf);
pq_sendint(&buf, count, 4);
for (i = 0; i < count; i++) {
int32 keylen = HS_KEYLEN(entries, i);
pq_sendint(&buf, keylen, 4);
pq_sendtext(&buf, HS_KEY(entries, base, i), keylen);
if (HS_VALISNULL(entries, i)) {
pq_sendint(&buf, -1, 4);
} else {
int32 vallen = HS_VALLEN(entries, i);
pq_sendint(&buf, vallen, 4);
pq_sendtext(&buf, HS_VAL(entries, base, i), vallen);
}
}
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}