#include <limits.h>
-#include "fmgr.h"
-#include "funcapi.h"
-#include "miscadmin.h"
#include "access/htup_details.h"
#include "catalog/pg_type.h"
+#include "fmgr.h"
+#include "funcapi.h"
#include "lib/stringinfo.h"
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/hsearch.h"
#include "utils/json.h"
-#include "utils/jsonb.h"
#include "utils/jsonapi.h"
+#include "utils/jsonb.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/typcache.h"
static void get_scalar(void *state, char *token, JsonTokenType tokentype);
/* common worker function for json getter functions */
-static inline Datum get_path_all(FunctionCallInfo fcinfo, bool as_text);
-static inline text *get_worker(text *json, char *field, int elem_index,
+static Datum get_path_all(FunctionCallInfo fcinfo, bool as_text);
+static text *get_worker(text *json, char *field, int elem_index,
char **tpath, int *ipath, int npath,
bool normalize_results);
-static inline Datum get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text);
+static Datum get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text);
/* semantic action functions for json_array_length */
static void alen_object_start(void *state);
static void alen_array_element_start(void *state, bool isnull);
/* common workers for json{b}_each* functions */
-static inline Datum each_worker(FunctionCallInfo fcinfo, bool as_text);
-static inline Datum each_worker_jsonb(FunctionCallInfo fcinfo, bool as_text);
+static Datum each_worker(FunctionCallInfo fcinfo, bool as_text);
+static Datum each_worker_jsonb(FunctionCallInfo fcinfo, bool as_text);
/* semantic action functions for json_each */
static void each_object_field_start(void *state, char *fname, bool isnull);
static void each_scalar(void *state, char *token, JsonTokenType tokentype);
/* common workers for json{b}_array_elements_* functions */
-static inline Datum elements_worker(FunctionCallInfo fcinfo, bool as_text);
-static inline Datum elements_worker_jsonb(FunctionCallInfo fcinfo, bool as_text);
+static Datum elements_worker(FunctionCallInfo fcinfo, bool as_text);
+static Datum elements_worker_jsonb(FunctionCallInfo fcinfo, bool as_text);
/* semantic action functions for json_array_elements */
static void elements_object_start(void *state);
static HTAB *get_json_object_as_hash(text *json, char *funcname, bool use_json_as_text);
/* common worker for populate_record and to_record */
-static inline Datum populate_record_worker(FunctionCallInfo fcinfo,
+static Datum populate_record_worker(FunctionCallInfo fcinfo,
bool have_record_arg);
/* semantic action functions for get_json_object_as_hash */
static void populate_recordset_array_element_start(void *state, bool isnull);
/* worker function for populate_recordset and to_recordset */
-static inline Datum populate_recordset_worker(FunctionCallInfo fcinfo,
+static Datum populate_recordset_worker(FunctionCallInfo fcinfo,
bool have_record_arg);
/* Worker that takes care of common setup for us */
static JsonbValue *findJsonbValueFromContainerLen(JsonbContainer *container,
- uint32 flags,
- char *key,
- uint32 keylen);
+ uint32 flags,
+ char *key,
+ uint32 keylen);
/* search type classification for json_get* functions */
typedef enum
char *function_name;
} JHashState;
-/* used to build the hashtable */
+/* hashtable element */
typedef struct JsonHashEntry
{
- char fname[NAMEDATALEN];
+ char fname[NAMEDATALEN]; /* hash key (MUST BE FIRST) */
char *val;
char *json;
bool isnull;
}
}
-
MemoryContextSwitchTo(oldcontext);
funcctx->user_fctx = (void *) state;
-
}
funcctx = SRF_PERCALL_SETUP();
text *json = PG_GETARG_TEXT_P(0);
JsonLexContext *lex = makeJsonLexContext(json, true);
JsonSemAction *sem;
-
MemoryContext oldcontext;
funcctx = SRF_FIRSTCALL_INIT();
MemoryContextSwitchTo(oldcontext);
funcctx->user_fctx = (void *) state;
-
}
funcctx = SRF_PERCALL_SETUP();
if (_state->result_count >= _state->result_size)
{
_state->result_size *= 2;
- _state->result =
+ _state->result = (char **)
repalloc(_state->result, sizeof(char *) * _state->result_size);
}
json_object_field(PG_FUNCTION_ARGS)
{
text *json = PG_GETARG_TEXT_P(0);
- text *result;
text *fname = PG_GETARG_TEXT_P(1);
char *fnamestr = text_to_cstring(fname);
+ text *result;
result = get_worker(json, fnamestr, -1, NULL, NULL, -1, false);
Assert(JB_ROOT_IS_OBJECT(jb));
v = findJsonbValueFromContainerLen(&jb->root, JB_FOBJECT,
- VARDATA_ANY(key), VARSIZE_ANY_EXHDR(key));
+ VARDATA_ANY(key), VARSIZE_ANY_EXHDR(key));
if (v != NULL)
PG_RETURN_JSONB(JsonbValueToJsonb(v));
json_object_field_text(PG_FUNCTION_ARGS)
{
text *json = PG_GETARG_TEXT_P(0);
- text *result;
text *fname = PG_GETARG_TEXT_P(1);
char *fnamestr = text_to_cstring(fname);
+ text *result;
result = get_worker(json, fnamestr, -1, NULL, NULL, -1, true);
Assert(JB_ROOT_IS_OBJECT(jb));
v = findJsonbValueFromContainerLen(&jb->root, JB_FOBJECT,
- VARDATA_ANY(key), VARSIZE_ANY_EXHDR(key));
+ VARDATA_ANY(key), VARSIZE_ANY_EXHDR(key));
if (v != NULL)
{
text *result = NULL;
- switch(v->type)
+ switch (v->type)
{
case jbvNull:
break;
break;
case jbvNumeric:
result = cstring_to_text(DatumGetCString(DirectFunctionCall1(numeric_out,
- PointerGetDatum(v->val.numeric))));
+ PointerGetDatum(v->val.numeric))));
break;
case jbvBinary:
{
- StringInfo jtext = makeStringInfo();
+ StringInfo jtext = makeStringInfo();
(void) JsonbToCString(jtext, v->val.binary.data, -1);
result = cstring_to_text_with_len(jtext->data, jtext->len);
json_array_element(PG_FUNCTION_ARGS)
{
text *json = PG_GETARG_TEXT_P(0);
- text *result;
int element = PG_GETARG_INT32(1);
+ text *result;
result = get_worker(json, NULL, element, NULL, NULL, -1, false);
json_array_element_text(PG_FUNCTION_ARGS)
{
text *json = PG_GETARG_TEXT_P(0);
- text *result;
int element = PG_GETARG_INT32(1);
+ text *result;
result = get_worker(json, NULL, element, NULL, NULL, -1, true);
{
text *result = NULL;
- switch(v->type)
+ switch (v->type)
{
case jbvNull:
break;
break;
case jbvNumeric:
result = cstring_to_text(DatumGetCString(DirectFunctionCall1(numeric_out,
- PointerGetDatum(v->val.numeric))));
+ PointerGetDatum(v->val.numeric))));
break;
case jbvBinary:
{
- StringInfo jtext = makeStringInfo();
+ StringInfo jtext = makeStringInfo();
(void) JsonbToCString(jtext, v->val.binary.data, -1);
result = cstring_to_text_with_len(jtext->data, jtext->len);
/*
* common routine for extract_path functions
*/
-static inline Datum
+static Datum
get_path_all(FunctionCallInfo fcinfo, bool as_text)
{
- text *json;
+ text *json = PG_GETARG_TEXT_P(0);
ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
text *result;
Datum *pathtext;
long ind;
char *endptr;
- json = PG_GETARG_TEXT_P(0);
-
if (array_contains_nulls(path))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("cannot call function with null path elements")));
-
deconstruct_array(path, TEXTOID, -1, false, 'i',
&pathtext, &pathnulls, &npath);
tpath = palloc(npath * sizeof(char *));
ipath = palloc(npath * sizeof(int));
-
for (i = 0; i < npath; i++)
{
tpath[i] = TextDatumGetCString(pathtext[i]);
ipath[i] = -1;
}
-
result = get_worker(json, NULL, -1, tpath, ipath, npath, as_text);
if (result != NULL)
*
* common worker for all the json getter functions
*/
-static inline text *
+static text *
get_worker(text *json,
char *field,
int elem_index,
state->pathok[0] = true;
state->array_level_index = palloc(sizeof(int) * npath);
state->path_level_index = ipath;
-
}
else
{
if (lex_level == 1 && _state->search_type == JSON_SEARCH_OBJECT &&
strcmp(fname, _state->search_term) == 0)
{
-
_state->tresult = NULL;
_state->result_start = NULL;
get_next = true;
/* path search, path so far is ok, and we have a match */
/* this object overrides any previous matching object */
-
_state->tresult = NULL;
_state->result_start = NULL;
bool get_last = false;
int lex_level = _state->lex->lex_level;
-
/* same tests as in get_object_field_start, mutatis mutandis */
if (lex_level == 1 && _state->search_type == JSON_SEARCH_OBJECT &&
strcmp(fname, _state->search_term) == 0)
errmsg("cannot extract field from a non-object")));
/*
- * initialize array count for this nesting level Note: the lex_level seen
+ * initialize array count for this nesting level. Note: the lex_level seen
* by array_start is one less than that seen by the elements of the array.
*/
if (_state->search_type == JSON_SEARCH_PATH &&
*
* then check if we have a match.
*/
-
if (++_state->array_level_index[lex_level - 1] ==
_state->path_level_index[lex_level - 1])
{
_state->pathok[lex_level] = true;
}
}
-
}
/* same logic as for objects */
/* make sure the next call to get_scalar doesn't overwrite it */
_state->next_scalar = false;
}
-
}
Datum
return get_jsonb_path_all(fcinfo, true);
}
-static inline Datum
+static Datum
get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
{
Jsonb *jb = PG_GETARG_JSONB(0);
Datum
json_array_length(PG_FUNCTION_ARGS)
{
- text *json;
-
+ text *json = PG_GETARG_TEXT_P(0);
AlenState *state;
JsonLexContext *lex;
JsonSemAction *sem;
- json = PG_GETARG_TEXT_P(0);
lex = makeJsonLexContext(json, false);
state = palloc0(sizeof(AlenState));
sem = palloc0(sizeof(JsonSemAction));
}
/*
- * These next two check ensure that the json is an array (since it can't be
+ * These next two checks ensure that the json is an array (since it can't be
* a scalar or an object).
*/
return each_worker_jsonb(fcinfo, true);
}
-static inline Datum
+static Datum
each_worker_jsonb(FunctionCallInfo fcinfo, bool as_text)
{
Jsonb *jb = PG_GETARG_JSONB(0);
errmsg("set-valued function called in context that "
"cannot accept a set")));
-
rsi->returnMode = SFRM_Materialize;
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
-
it = JsonbIteratorInit(&jb->root);
while ((r = JsonbIteratorNext(&it, &v, skipNested)) != WJB_DONE)
}
-static inline Datum
+static Datum
each_worker(FunctionCallInfo fcinfo, bool as_text)
{
- text *json;
+ text *json = PG_GETARG_TEXT_P(0);
JsonLexContext *lex;
JsonSemAction *sem;
ReturnSetInfo *rsi;
TupleDesc tupdesc;
EachState *state;
- json = PG_GETARG_TEXT_P(0);
-
lex = makeJsonLexContext(json, true);
state = palloc0(sizeof(EachState));
sem = palloc0(sizeof(JsonSemAction));
errmsg("set-valued function called in context that "
"cannot accept a set")));
-
rsi->returnMode = SFRM_Materialize;
(void) get_call_result_type(fcinfo, NULL, &tupdesc);
state->normalize_results = as_text;
state->next_scalar = false;
-
state->lex = lex;
state->tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
"json_each temporary cxt",
if (isnull && _state->normalize_results)
{
nulls[1] = true;
- values[1] = (Datum) NULL;
+ values[1] = (Datum) 0;
}
else if (_state->next_scalar)
{
values[1] = PointerGetDatum(val);
}
-
tuple = heap_form_tuple(_state->ret_tdesc, values, nulls);
tuplestore_puttuple(_state->tuple_store, tuple);
return elements_worker_jsonb(fcinfo, true);
}
-static inline Datum
+static Datum
elements_worker_jsonb(FunctionCallInfo fcinfo, bool as_text)
{
Jsonb *jb = PG_GETARG_JSONB(0);
errmsg("set-valued function called in context that "
"cannot accept a set")));
-
rsi->returnMode = SFRM_Materialize;
/* it's a simple type, so don't use get_call_result_type() */
MemoryContextSwitchTo(old_cxt);
tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
- "jsonb_each temporary cxt",
+ "jsonb_array_elements temporary cxt",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
-
it = JsonbIteratorInit(&jb->root);
while ((r = JsonbIteratorNext(&it, &v, skipNested)) != WJB_DONE)
return elements_worker(fcinfo, true);
}
-static inline Datum
+static Datum
elements_worker(FunctionCallInfo fcinfo, bool as_text)
{
text *json = PG_GETARG_TEXT_P(0);
errmsg("set-valued function called in context that "
"cannot accept a set")));
-
rsi->returnMode = SFRM_Materialize;
/* it's a simple type, so don't use get_call_result_type() */
state->normalize_results = as_text;
state->next_scalar = false;
-
state->lex = lex;
state->tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
"json_array_elements temporary cxt",
values[0] = PointerGetDatum(val);
}
-
tuple = heap_form_tuple(_state->ret_tdesc, values, nulls);
tuplestore_puttuple(_state->tuple_store, tuple);
return populate_record_worker(fcinfo, false);
}
-static inline Datum
+static Datum
populate_record_worker(FunctionCallInfo fcinfo, bool have_record_arg)
{
int json_arg_num = have_record_arg ? 1 : 0;
tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
}
else
- { /* json{b}_to_record case */
-
+ {
+ /* json{b}_to_record case */
if (PG_ARGISNULL(0))
PG_RETURN_NULL();
ColumnIOData *column_info = &my_extra->columns[i];
Oid column_type = tupdesc->attrs[i]->atttypid;
JsonbValue *v = NULL;
- char fname[NAMEDATALEN];
JsonHashEntry *hashentry = NULL;
/* Ignore dropped columns in datatype */
if (jtype == JSONOID)
{
-
- memset(fname, 0, NAMEDATALEN);
- strncpy(fname, NameStr(tupdesc->attrs[i]->attname), NAMEDATALEN);
- hashentry = hash_search(json_hash, fname, HASH_FIND, NULL);
+ hashentry = hash_search(json_hash,
+ NameStr(tupdesc->attrs[i]->attname),
+ HASH_FIND, NULL);
}
else
{
JHashState *_state = (JHashState *) state;
JsonHashEntry *hashentry;
bool found;
- char name[NAMEDATALEN];
/*
- * ignore field names >= NAMEDATALEN - they can't match a record field
- * ignore nested fields.
+ * Ignore nested fields.
*/
- if (_state->lex->lex_level > 2 || strlen(fname) >= NAMEDATALEN)
+ if (_state->lex->lex_level > 2)
return;
- memset(name, 0, NAMEDATALEN);
- strncpy(name, fname, NAMEDATALEN);
+ /*
+ * Ignore field names >= NAMEDATALEN - they can't match a record field.
+ * (Note: without this test, the hash code would truncate the string at
+ * NAMEDATALEN-1, and could then match against a similarly-truncated
+ * record field name. That would be a reasonable behavior, but this code
+ * has previously insisted on exact equality, so we keep this behavior.)
+ */
+ if (strlen(fname) >= NAMEDATALEN)
+ return;
- hashentry = hash_search(_state->hash, name, HASH_ENTER, &found);
+ hashentry = hash_search(_state->hash, fname, HASH_ENTER, &found);
/*
* found being true indicates a duplicate. We don't do anything about
/*
* common worker for json_populate_recordset() and json_to_recordset()
*/
-static inline Datum
+static Datum
populate_recordset_worker(FunctionCallInfo fcinfo, bool have_record_arg)
{
int json_arg_num = have_record_arg ? 1 : 0;
errmsg("set-valued function called in context that "
"cannot accept a set")));
-
rsi->returnMode = SFRM_Materialize;
/*
if (jtype == JSONOID)
{
- text *json = PG_GETARG_TEXT_P(have_record_arg ? 1 : 0);
+ text *json = PG_GETARG_TEXT_P(json_arg_num);
JsonLexContext *lex;
JsonSemAction *sem;
state->lex = lex;
pg_parse_json(lex, sem);
-
}
else
{
- Jsonb *jb;
+ Jsonb *jb = PG_GETARG_JSONB(json_arg_num);
JsonbIterator *it;
JsonbValue v;
bool skipNested = false;
int r;
Assert(jtype == JSONBOID);
- jb = PG_GETARG_JSONB(have_record_arg ? 1 : 0);
if (JB_ROOT_IS_SCALAR(jb) || !JB_ROOT_IS_ARRAY(jb))
ereport(ERROR,
rsi->setDesc = state->ret_tdesc;
PG_RETURN_NULL();
-
}
static void
HTAB *json_hash = _state->json_hash;
Datum *values;
bool *nulls;
- char fname[NAMEDATALEN];
int i;
RecordIOData *my_extra = _state->my_extra;
int ncolumns = my_extra->ncolumns;
continue;
}
- memset(fname, 0, NAMEDATALEN);
- strncpy(fname, NameStr(tupdesc->attrs[i]->attname), NAMEDATALEN);
- hashentry = hash_search(json_hash, fname, HASH_FIND, NULL);
+ hashentry = hash_search(json_hash,
+ NameStr(tupdesc->attrs[i]->attname),
+ HASH_FIND, NULL);
/*
* we can't just skip here if the key wasn't found since we might have
PopulateRecordsetState *_state = (PopulateRecordsetState *) state;
JsonHashEntry *hashentry;
bool found;
- char name[NAMEDATALEN];
/*
- * ignore field names >= NAMEDATALEN - they can't match a record field
- * ignore nested fields.
+ * Ignore nested fields.
*/
- if (_state->lex->lex_level > 2 || strlen(fname) >= NAMEDATALEN)
+ if (_state->lex->lex_level > 2)
return;
- memset(name, 0, NAMEDATALEN);
- strncpy(name, fname, NAMEDATALEN);
+ /*
+ * Ignore field names >= NAMEDATALEN - they can't match a record field.
+ * (Note: without this test, the hash code would truncate the string at
+ * NAMEDATALEN-1, and could then match against a similarly-truncated
+ * record field name. That would be a reasonable behavior, but this code
+ * has previously insisted on exact equality, so we keep this behavior.)
+ */
+ if (strlen(fname) >= NAMEDATALEN)
+ return;
- hashentry = hash_search(_state->json_hash, name, HASH_ENTER, &found);
+ hashentry = hash_search(_state->json_hash, fname, HASH_ENTER, &found);
/*
* found being true indicates a duplicate. We don't do anything about