Remove level from JSON parsing state

When parsing key provider options. This was only ever really -1, 0 or 1
and the parser is easier to understand if these are just modelled with
the parser's semantic state instead.
This commit is contained in:
Anders Åstrand 2025-05-06 08:49:50 +02:00 committed by AndersAstrand
parent 6478b46a2f
commit 01ba91f891

View File

@ -44,6 +44,7 @@
typedef enum JsonKeyringSemState
{
JK_EXPECT_TOP_LEVEL_OBJECT,
JK_EXPECT_TOP_FIELD,
JK_EXPECT_EXTERN_VAL,
} JsonKeyringSemState;
@ -102,7 +103,6 @@ static const char *JK_FIELD_NAMES[JK_FIELDS_TOTAL] = {
[JK_KMIP_CERT_PATH] = "certPath",
};
#define MAX_JSON_DEPTH 64
typedef struct JsonKeyringState
{
ProviderType provider_type;
@ -110,14 +110,13 @@ typedef struct JsonKeyringState
/* Caller's options to be set from JSON values. */
GenericKeyring *provider_opts;
/*
* A field hierarchy of the current branch, field[level] is the current
* one, field[level-1] is the parent and so on. We need to track parent
* fields because of the external values
*/
JsonKeyringField field[MAX_JSON_DEPTH];
/* The current field in the top level object */
JsonKeyringField top_level_field;
/* Current field in any external field object, if any. */
JsonKeyringField extern_field;
JsonKeyringSemState state;
int level;
/*
* The rest of the scalar fields might be in the JSON document but has no
@ -154,9 +153,7 @@ ParseKeyringJSONOptions(ProviderType provider_type, GenericKeyring *out_opts, ch
/* Set up parsing context and initial semantic state */
parse.provider_type = provider_type;
parse.provider_opts = out_opts;
parse.level = -1;
parse.state = JK_EXPECT_TOP_FIELD;
memset(parse.field, 0, MAX_JSON_DEPTH * sizeof(JsonKeyringField));
parse.state = JK_EXPECT_TOP_LEVEL_OBJECT;
#if PG_VERSION_NUM >= 170000
jlex = makeJsonLexContextCstringLen(NULL, in_buf, buf_len, PG_UTF8, true);
@ -200,32 +197,28 @@ ParseKeyringJSONOptions(ProviderType provider_type, GenericKeyring *out_opts, ch
/*
* Invoked at the start of each object in the JSON document.
*
* Every new object increases the level of nesting as the whole document is the
* object itself (level 0) and every next one means going deeper into nesting.
*
* On the top level, we expect either scalar (string) values or objects referencing
* the external value of the field. Hence, if we are on level 1, we expect an
* "external field object" e.g. ({"type" : "remote", "url" : "http://localhost:8888/hello"})
* In the top level object, we expect either scalar (string) values or objects
* referencing the external value of the field. If we are already parsing top
* level fields, we expect an "external field object" e.g. ({"type" : "remote",
* "url" : "http://localhost:8888/hello"})
*/
static JsonParseErrorType
json_kring_object_start(void *state)
{
JsonKeyringState *parse = state;
if (MAX_JSON_DEPTH == ++parse->level)
switch (parse->state)
{
elog(WARNING, "reached max depth of JSON nesting");
return JSON_SEM_ACTION_FAILED;
}
switch (parse->level)
{
case 0:
case JK_EXPECT_TOP_LEVEL_OBJECT:
parse->state = JK_EXPECT_TOP_FIELD;
break;
case 1:
case JK_EXPECT_TOP_FIELD:
parse->state = JK_EXPECT_EXTERN_VAL;
break;
case JK_EXPECT_EXTERN_VAL:
ereport(ERROR,
errmsg("invalid semantic state"));
break;
}
return JSON_SUCCESS;
@ -234,10 +227,8 @@ json_kring_object_start(void *state)
/*
* Invoked at the end of each object in the JSON document.
*
* First, it means we are going back to the higher level. Plus, if it was the
* level 1, we expect only external objects there, which means we have all
* the necessary info to extract the value and assign the result to the
* appropriate (parent) field.
* If we're done parsing an external field object we fetch the value from the
* source and assign it to the top level object field.
*/
static JsonParseErrorType
json_kring_object_end(void *state)
@ -252,38 +243,42 @@ json_kring_object_end(void *state)
* "/tmp/datafile-location"} the "field"'s value should be the content of
* "path" or "url" respectively
*/
if (parse->level == 1)
switch (parse->state)
{
if (parse->state == JK_EXPECT_EXTERN_VAL)
{
JsonKeyringField parent_field = parse->field[0];
JsonParseErrorType ret;
char *value = NULL;
if (strcmp(parse->field_type, KEYRING_REMOTE_FIELD_TYPE) == 0)
value = get_remote_kring_value(parse->extern_url, JK_FIELD_NAMES[parent_field]);
if (strcmp(parse->field_type, KEYRING_FILE_FIELD_TYPE) == 0)
value = get_file_kring_value(parse->extern_path, JK_FIELD_NAMES[parent_field]);
if (value == NULL)
case JK_EXPECT_TOP_LEVEL_OBJECT:
ereport(ERROR,
errmsg("invalid semantic state"));
break;
case JK_EXPECT_TOP_FIELD:
/* We're done parsing the top level object */
break;
case JK_EXPECT_EXTERN_VAL:
{
return JSON_INCOMPLETE;
JsonParseErrorType ret;
char *value = NULL;
if (strcmp(parse->field_type, KEYRING_REMOTE_FIELD_TYPE) == 0)
value = get_remote_kring_value(parse->extern_url, JK_FIELD_NAMES[parse->top_level_field]);
if (strcmp(parse->field_type, KEYRING_FILE_FIELD_TYPE) == 0)
value = get_file_kring_value(parse->extern_path, JK_FIELD_NAMES[parse->top_level_field]);
if (value == NULL)
{
return JSON_INCOMPLETE;
}
ret = json_kring_assign_scalar(parse, parse->top_level_field, value);
if (ret != JSON_SUCCESS)
{
return ret;
}
parse->state = JK_EXPECT_TOP_FIELD;
break;
}
ret = json_kring_assign_scalar(parse, parent_field, value);
if (ret != JSON_SUCCESS)
{
return ret;
}
}
parse->state = JK_EXPECT_TOP_FIELD;
}
parse->level--;
return JSON_SUCCESS;
}
@ -298,55 +293,54 @@ static JsonParseErrorType
json_kring_object_field_start(void *state, char *fname, bool isnull)
{
JsonKeyringState *parse = state;
JsonKeyringField *field;
Assert(parse->level >= 0);
field = &parse->field[parse->level];
switch (parse->state)
{
case JK_EXPECT_TOP_LEVEL_OBJECT:
ereport(ERROR,
errmsg("invalid semantic state"));
break;
case JK_EXPECT_TOP_FIELD:
switch (parse->provider_type)
{
case FILE_KEY_PROVIDER:
if (strcmp(fname, JK_FIELD_NAMES[JK_FILE_PATH]) == 0)
*field = JK_FILE_PATH;
parse->top_level_field = JK_FILE_PATH;
else
{
*field = JK_FIELD_UNKNOWN;
parse->top_level_field = JK_FIELD_UNKNOWN;
elog(ERROR, "parse file keyring config: unexpected field %s", fname);
}
break;
case VAULT_V2_KEY_PROVIDER:
if (strcmp(fname, JK_FIELD_NAMES[JK_VAULT_TOKEN]) == 0)
*field = JK_VAULT_TOKEN;
parse->top_level_field = JK_VAULT_TOKEN;
else if (strcmp(fname, JK_FIELD_NAMES[JK_VAULT_URL]) == 0)
*field = JK_VAULT_URL;
parse->top_level_field = JK_VAULT_URL;
else if (strcmp(fname, JK_FIELD_NAMES[JK_VAULT_MOUNT_PATH]) == 0)
*field = JK_VAULT_MOUNT_PATH;
parse->top_level_field = JK_VAULT_MOUNT_PATH;
else if (strcmp(fname, JK_FIELD_NAMES[JK_VAULT_CA_PATH]) == 0)
*field = JK_VAULT_CA_PATH;
parse->top_level_field = JK_VAULT_CA_PATH;
else
{
*field = JK_FIELD_UNKNOWN;
parse->top_level_field = JK_FIELD_UNKNOWN;
elog(ERROR, "parse json keyring config: unexpected field %s", fname);
}
break;
case KMIP_KEY_PROVIDER:
if (strcmp(fname, JK_FIELD_NAMES[JK_KMIP_HOST]) == 0)
*field = JK_KMIP_HOST;
parse->top_level_field = JK_KMIP_HOST;
else if (strcmp(fname, JK_FIELD_NAMES[JK_KMIP_PORT]) == 0)
*field = JK_KMIP_PORT;
parse->top_level_field = JK_KMIP_PORT;
else if (strcmp(fname, JK_FIELD_NAMES[JK_KMIP_CA_PATH]) == 0)
*field = JK_KMIP_CA_PATH;
parse->top_level_field = JK_KMIP_CA_PATH;
else if (strcmp(fname, JK_FIELD_NAMES[JK_KMIP_CERT_PATH]) == 0)
*field = JK_KMIP_CERT_PATH;
parse->top_level_field = JK_KMIP_CERT_PATH;
else
{
*field = JK_FIELD_UNKNOWN;
parse->top_level_field = JK_FIELD_UNKNOWN;
elog(ERROR, "parse json keyring config: unexpected field %s", fname);
}
break;
@ -358,14 +352,14 @@ json_kring_object_field_start(void *state, char *fname, bool isnull)
case JK_EXPECT_EXTERN_VAL:
if (strcmp(fname, JK_FIELD_NAMES[JK_FIELD_TYPE]) == 0)
*field = JK_FIELD_TYPE;
parse->extern_field = JK_FIELD_TYPE;
else if (strcmp(fname, JK_FIELD_NAMES[JK_FIELD_URL]) == 0)
*field = JK_FIELD_URL;
parse->extern_field = JK_FIELD_URL;
else if (strcmp(fname, JK_FIELD_NAMES[JK_FIELD_PATH]) == 0)
*field = JK_FIELD_PATH;
parse->extern_field = JK_FIELD_PATH;
else
{
*field = JK_FIELD_UNKNOWN;
parse->extern_field = JK_FIELD_UNKNOWN;
elog(ERROR, "parse json keyring config: unexpected field %s", fname);
}
break;
@ -384,8 +378,23 @@ static JsonParseErrorType
json_kring_scalar(void *state, char *token, JsonTokenType tokentype)
{
JsonKeyringState *parse = state;
JsonKeyringField *field = NULL;
return json_kring_assign_scalar(parse, parse->field[parse->level], token);
switch (parse->state)
{
case JK_EXPECT_TOP_LEVEL_OBJECT:
ereport(ERROR,
errmsg("invalid semantic state"));
break;
case JK_EXPECT_TOP_FIELD:
field = &parse->top_level_field;
break;
case JK_EXPECT_EXTERN_VAL:
field = &parse->extern_field;
break;
}
return json_kring_assign_scalar(parse, *field, token);
}
static JsonParseErrorType