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.
pull/220/head
Anders Åstrand 4 months ago committed by AndersAstrand
parent 6478b46a2f
commit 01ba91f891
  1. 133
      contrib/pg_tde/src/catalog/tde_keyring_parse_opts.c

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

Loading…
Cancel
Save