mirror of
https://github.com/postgres/postgres.git
synced 2025-11-21 00:42:43 +03:00
IS JSON predicate
This patch intrdocuces the SQL standard IS JSON predicate. It operates on text and bytea values representing JSON as well as on the json and jsonb types. Each test has an IS and IS NOT variant. The tests are: IS JSON [VALUE] IS JSON ARRAY IS JSON OBJECT IS JSON SCALAR IS JSON WITH | WITHOUT UNIQUE KEYS These are mostly self-explanatory, but note that IS JSON WITHOUT UNIQUE KEYS is true whenever IS JSON is true, and IS JSON WITH UNIQUE KEYS is true whenever IS JSON is true except it IS JSON OBJECT is true and there are duplicate keys (which is never the case when applied to jsonb values). Nikita Glukhov Reviewers have included (in no particular order) Andres Freund, Alexander Korotkov, Pavel Stehule, Andrew Alsup, Erik Rijkers, Zihong Yu, Himanshu Upadhyaya, Daniel Gustafsson, Justin Pryzby. Discussion: https://postgr.es/m/cd0bb935-0158-78a7-08b5-904886deac4b@postgrespro.ru
This commit is contained in:
@@ -13,6 +13,7 @@
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include "access/hash.h"
|
||||
#include "catalog/pg_proc.h"
|
||||
#include "catalog/pg_type.h"
|
||||
#include "common/hashfn.h"
|
||||
@@ -1655,6 +1656,94 @@ escape_json(StringInfo buf, const char *str)
|
||||
appendStringInfoCharMacro(buf, '"');
|
||||
}
|
||||
|
||||
/* Semantic actions for key uniqueness check */
|
||||
static void
|
||||
json_unique_object_start(void *_state)
|
||||
{
|
||||
JsonUniqueParsingState *state = _state;
|
||||
JsonUniqueStackEntry *entry;
|
||||
|
||||
if (!state->unique)
|
||||
return;
|
||||
|
||||
/* push object entry to stack */
|
||||
entry = palloc(sizeof(*entry));
|
||||
entry->object_id = state->id_counter++;
|
||||
entry->parent = state->stack;
|
||||
state->stack = entry;
|
||||
}
|
||||
|
||||
static void
|
||||
json_unique_object_end(void *_state)
|
||||
{
|
||||
JsonUniqueParsingState *state = _state;
|
||||
JsonUniqueStackEntry *entry;
|
||||
|
||||
if (!state->unique)
|
||||
return;
|
||||
|
||||
entry = state->stack;
|
||||
state->stack = entry->parent; /* pop object from stack */
|
||||
pfree(entry);
|
||||
}
|
||||
|
||||
static void
|
||||
json_unique_object_field_start(void *_state, char *field, bool isnull)
|
||||
{
|
||||
JsonUniqueParsingState *state = _state;
|
||||
JsonUniqueStackEntry *entry;
|
||||
|
||||
if (!state->unique)
|
||||
return;
|
||||
|
||||
/* find key collision in the current object */
|
||||
if (json_unique_check_key(&state->check, field, state->stack->object_id))
|
||||
return;
|
||||
|
||||
state->unique = false;
|
||||
|
||||
/* pop all objects entries */
|
||||
while ((entry = state->stack))
|
||||
{
|
||||
state->stack = entry->parent;
|
||||
pfree(entry);
|
||||
}
|
||||
}
|
||||
|
||||
/* Validate JSON text and additionally check key uniqueness */
|
||||
bool
|
||||
json_validate(text *json, bool check_unique_keys)
|
||||
{
|
||||
JsonLexContext *lex = makeJsonLexContext(json, check_unique_keys);
|
||||
JsonSemAction uniqueSemAction = {0};
|
||||
JsonUniqueParsingState state;
|
||||
JsonParseErrorType result;
|
||||
|
||||
if (check_unique_keys)
|
||||
{
|
||||
state.lex = lex;
|
||||
state.stack = NULL;
|
||||
state.id_counter = 0;
|
||||
state.unique = true;
|
||||
json_unique_check_init(&state.check);
|
||||
|
||||
uniqueSemAction.semstate = &state;
|
||||
uniqueSemAction.object_start = json_unique_object_start;
|
||||
uniqueSemAction.object_field_start = json_unique_object_field_start;
|
||||
uniqueSemAction.object_end = json_unique_object_end;
|
||||
}
|
||||
|
||||
result = pg_parse_json(lex, check_unique_keys ? &uniqueSemAction : &nullSemAction);
|
||||
|
||||
if (result != JSON_SUCCESS)
|
||||
return false; /* invalid json */
|
||||
|
||||
if (check_unique_keys && !state.unique)
|
||||
return false; /* not unique keys */
|
||||
|
||||
return true; /* ok */
|
||||
}
|
||||
|
||||
/*
|
||||
* SQL function json_typeof(json) -> text
|
||||
*
|
||||
@@ -1670,21 +1759,13 @@ escape_json(StringInfo buf, const char *str)
|
||||
Datum
|
||||
json_typeof(PG_FUNCTION_ARGS)
|
||||
{
|
||||
text *json;
|
||||
|
||||
JsonLexContext *lex;
|
||||
JsonTokenType tok;
|
||||
text *json = PG_GETARG_TEXT_PP(0);
|
||||
char *type;
|
||||
JsonParseErrorType result;
|
||||
|
||||
json = PG_GETARG_TEXT_PP(0);
|
||||
lex = makeJsonLexContext(json, false);
|
||||
JsonTokenType tok;
|
||||
|
||||
/* Lex exactly one token from the input and check its type. */
|
||||
result = json_lex(lex);
|
||||
if (result != JSON_SUCCESS)
|
||||
json_ereport_error(result, lex);
|
||||
tok = lex->token_type;
|
||||
tok = json_get_first_token(json, true);
|
||||
|
||||
switch (tok)
|
||||
{
|
||||
case JSON_TOKEN_OBJECT_START:
|
||||
|
||||
Reference in New Issue
Block a user