diff options
Diffstat (limited to 'jsonpath.c')
| -rw-r--r-- | jsonpath.c | 297 |
1 files changed, 183 insertions, 114 deletions
@@ -6,7 +6,7 @@ /* NB: These macros evaluate their argument multiple times. */ #define isletter(c) (((c) >= 'A' && (c) <= 'Z') || ((c) >= 'a' && (c) <= 'z')) - /* isalpha() is locale-specific. This simply matches [A-Za-z] . */ + /* isalpha() is locale-specific. This simply matches [A-Za-z] . */ #define isextended(c) ((unsigned char)(c) > 127) /* Note that Unicode characters are allowed in identifiers. */ @@ -23,10 +23,11 @@ * This function returns the first cell, * making sure it is of type JP_ROOT. */ -static ListCell *jp_root(JSONPath *jp) +static ListCell * +jp_root(JSONPath * jp) { - ListCell *cell; - jp_element *elem; + ListCell *cell; + jp_element *elem; Assert(jp != NULL); @@ -41,7 +42,8 @@ static ListCell *jp_root(JSONPath *jp) * This function returns the second cell of a JSONPath list * (the first cell after the JP_ROOT). */ -static ListCell *jp_head(JSONPath *jp) +static ListCell * +jp_head(JSONPath * jp) { return lnext(jp_root(jp)); } @@ -53,93 +55,116 @@ static ListCell *jp_head(JSONPath *jp) * whitespace, but since this is JSONPath, * we can do whatever we want here :-) */ -static void skip_spaces(const char **sp) +static void +skip_spaces(const char **sp) { const char *s = *sp; + while (isspace(*s)) s++; *sp = s; } -static jp_element *mkElement(jp_element_type type, bool rd) +static jp_element * +mkElement(jp_element_type type, bool rd) { jp_element *elem = palloc0(sizeof(*elem)); + elem->type = type; elem->recursive_descent = rd; return elem; } -static jp_element *mkRoot(void) +static jp_element * +mkRoot(void) { jp_element *elem = mkElement(JP_ROOT, false); + return elem; } -static jp_element *mkWildcard(bool rd) +static jp_element * +mkWildcard(bool rd) { jp_element *elem = mkElement(JP_WILDCARD, rd); + return elem; } -static jp_element *mkIndexSubscript(long index, bool rd) +static jp_element * +mkIndexSubscript(long index, bool rd) { jp_element *elem = mkElement(JP_INDEX_SUBSCRIPT, rd); + elem->data.index = index; return elem; } -static jp_element *mkKeySubscript(char *key, size_t length, bool rd) +static jp_element * +mkKeySubscript(char *key, size_t length, bool rd) { jp_element *elem = mkElement(JP_KEY_SUBSCRIPT, rd); + elem->data.key.ptr = key; elem->data.key.length = length; return elem; } -static jp_element *mkCallChar(long index, bool rd) +static jp_element * +mkCallChar(long index, bool rd) { jp_element *elem = mkElement(JP_CALL_CHAR, rd); + elem->data.index = index; return elem; } -static JPRef *mkRef(JPRefType type) +static JPRef * +mkRef(JPRefType type) { - JPRef *ref = palloc0(sizeof(*ref)); + JPRef *ref = palloc0(sizeof(*ref)); + ref->type = type; return ref; } -static JPRef *mkRefNode(json_node *node) +static JPRef * +mkRefNode(json_node * node) { - JPRef *ref = mkRef(JP_REF_NODE); + JPRef *ref = mkRef(JP_REF_NODE); + ref->u.node = node; return ref; } -static JPRef *mkRefChar(const char *bytes, size_t length) +static JPRef * +mkRefChar(const char *bytes, size_t length) { - JPRef *ref = mkRef(JP_REF_CHAR); - ref->u.chr.bytes = bytes; - ref->u.chr.length = length; + JPRef *ref = mkRef(JP_REF_CHAR); + ref->u.chr. bytes = bytes; + ref->u.chr. length = length; + return ref; } -char *jp_show(JSONPath *jp) +char * +jp_show(JSONPath * jp) { - StringInfoData string[1]; - ListCell *cell; - jp_element *elem; - bool rd; - char *tmp; + StringInfoData string[1]; + ListCell *cell; + jp_element *elem; + bool rd; + char *tmp; initStringInfo(string); - foreach(cell, jp) { + foreach(cell, jp) + { elem = lfirst(cell); - rd = elem->recursive_descent; + rd = elem->recursive_descent; - switch (elem->type) { + switch (elem->type) + { case JP_ROOT: appendStringInfoChar(string, '$'); break; @@ -166,12 +191,13 @@ char *jp_show(JSONPath *jp) return string->data; } -static bool parse_long(const char **s, long *out) +static bool +parse_long(const char **s, long *out) { const char *p = *s; errno = 0; - *out = strtol(*s, (char**)&p, 10); + *out = strtol(*s, (char **) &p, 10); if (p <= *s || errno != 0) return false; @@ -179,34 +205,39 @@ static bool parse_long(const char **s, long *out) return true; } -JSONPath *jp_parse(const char *pattern) +JSONPath * +jp_parse(const char *pattern) { - JSONPath *jp = NIL; - const char *s = pattern; - const char *start; - const char *end; - bool recursive_descent = false; - bool bracket = false; - const char *err_msg = NULL; + JSONPath *jp = NIL; + const char *s = pattern; + const char *start; + const char *end; + bool recursive_descent = false; + bool bracket = false; + const char *err_msg = NULL; long index; - char *key; + char *key; size_t key_length; - + skip_spaces(&s); - + /* pattern may not be empty */ if (!*s) return NULL; - + jp = lappend(jp, mkRoot()); - - if (*s == '$') { + + if (*s == '$') + { s++; goto begin_element; - } else if (*s != '.') { - goto dot_subscript; // implicit '.' at beginning } - + else if (*s != '.') + { + goto dot_subscript; + /* implicit '.' at beginning */ + } + begin_element: skip_spaces(&s); begin_element_noskip: @@ -216,16 +247,19 @@ begin_element_noskip: if (*s == '\0') goto end; - if (s[0] == '.' && s[1] == '.') { + if (s[0] == '.' && s[1] == '.') + { recursive_descent = true; s += 2; goto dot_subscript; } - if (s[0] == '.') { + if (s[0] == '.') + { s++; goto dot_subscript; } - if (s[0] == '[') { + if (s[0] == '[') + { s++; goto bracket_subscript; } @@ -233,7 +267,8 @@ begin_element_noskip: goto failed; next_element: - if (bracket) { + if (bracket) + { skip_spaces(&s); if (*s != ']') goto failed; @@ -243,7 +278,7 @@ next_element: dot_subscript: skip_spaces(&s); - + if (*s == '*') goto wildcard; if (integer_start(*s)) @@ -252,7 +287,8 @@ dot_subscript: goto identifier; if (*s == '"' || *s == '\'') goto string; - if (*s == '[') { + if (*s == '[') + { s++; goto bracket_subscript; } @@ -268,13 +304,14 @@ bracket_subscript: goto wildcard; if (integer_start(*s)) goto integer; - if (identifier_start(*s)) { + if (identifier_start(*s)) + { err_msg = "Identifiers may not be bracketed. This syntax is reserved for future use."; goto failed; } if (*s == '"' || *s == '\'') goto string; - + goto failed; wildcard: @@ -285,7 +322,7 @@ wildcard: integer: if (!parse_long(&s, &index)) goto failed; - + jp = lappend(jp, mkIndexSubscript(index, recursive_descent)); goto next_element; @@ -297,7 +334,8 @@ identifier: skip_spaces(&s); - if (*s == '(') { + if (*s == '(') + { if (end - start == 4 && !memcmp(start, "char", 4)) { s++; @@ -318,7 +356,7 @@ string: key = json_decode_string(&s, &key_length, false); if (!key) goto failed; - + jp = lappend(jp, mkKeySubscript(key, key_length, recursive_descent)); goto next_element; @@ -341,34 +379,40 @@ failed: return NULL; } -static size_t utf8_substring( - const char *src, size_t srcbytes, - size_t start, size_t length, - const char **out_start, size_t *out_bytes) +static size_t +utf8_substring( + const char *src, size_t srcbytes, + size_t start, size_t length, + const char **out_start, size_t *out_bytes) { - const char *e = src + srcbytes; - const char *sub_start; - const char *sub_end; + const char *e = src + srcbytes; + const char *sub_start; + const char *sub_end; size_t sub_length; sub_start = src; - while (start > 0 && sub_start < e) { - sub_start += pg_utf_mblen((const unsigned char*)sub_start); + while (start > 0 && sub_start < e) + { + sub_start += pg_utf_mblen((const unsigned char *) sub_start); start--; } sub_end = sub_start; sub_length = 0; - while (sub_length < length && sub_end < e) { - sub_end += pg_utf_mblen((const unsigned char*)sub_end); + while (sub_length < length && sub_end < e) + { + sub_end += pg_utf_mblen((const unsigned char *) sub_end); sub_length++; } /* Make sure the input didn't have a clipped UTF-8 character */ - if(sub_start > e) { + if (sub_start > e) + { Assert(false); sub_start = sub_end = e; - } else if (sub_end > e) { + } + else if (sub_end > e) + { Assert(false); sub_end = e; } @@ -385,13 +429,15 @@ JPRef could merged with json_node by adding JPRef's specialty types to the json_type enum and json_node union. JPRef is currently not merged with json_node in an attempt to keep the codebase tidy and easier to extend. */ -static void match_recurse(void on_match(void *ctx, JPRef *ref), void *ctx, - ListCell *path, JPRef *ref) +static void match_recurse(void on_match(void *ctx, JPRef * ref), void *ctx, + ListCell *path, JPRef * ref) { - jp_element *elem; - json_node *json, *child; + jp_element *elem; + json_node *json, + *child; - if (path == NULL) { + if (path == NULL) + { /* The end of the JSONPath list is the "accept" state. */ on_match(ctx, ref); return; @@ -404,30 +450,39 @@ static void match_recurse(void on_match(void *ctx, JPRef *ref), void *ctx, else json = NULL; - switch (elem->type) { + switch (elem->type) + { case JP_WILDCARD: - if (json) { + if (json) + { json_foreach(child, json) match_recurse(on_match, ctx, lnext(path), mkRefNode(child)); } break; case JP_INDEX_SUBSCRIPT: - if (json && json->type == JSON_ARRAY) { - size_t i; - size_t index = elem->data.index; - /* Note: elem->data.index is signed (long), - while index is unsigned (size_t). */ - - if (elem->data.index >= 0 && index < json->v.children.count) { + if (json && json->type == JSON_ARRAY) + { + size_t i; + size_t index = elem->data.index; + + /* + * Note: elem->data.index is signed (long), while index is + * unsigned (size_t). + */ + + if (elem->data.index >= 0 && index < json->v.children.count) + { for (child = json->v.children.head, i = 0; - child != NULL && i < index; + child != NULL && i < index; child = child->next, i++) { } - /* If this fails, it means json->v.children.count - was greater than the actual number of children. */ + /* + * If this fails, it means json->v.children.count was + * greater than the actual number of children. + */ Assert(i == index && child != NULL); match_recurse(on_match, ctx, lnext(path), mkRefNode(child)); @@ -436,8 +491,10 @@ static void match_recurse(void on_match(void *ctx, JPRef *ref), void *ctx, break; case JP_KEY_SUBSCRIPT: - if (json && json->type == JSON_OBJECT) { - json_foreach(child, json) { + if (json && json->type == JSON_OBJECT) + { + json_foreach(child, json) + { if (child->key != NULL && child->key_length == elem->data.key.length && !memcmp(child->key, elem->data.key.ptr, child->key_length)) @@ -449,19 +506,22 @@ static void match_recurse(void on_match(void *ctx, JPRef *ref), void *ctx, break; case JP_CALL_CHAR: - if (json && json->type == JSON_STRING && elem->data.index >= 0) { - const char *sub_start; - size_t sub_bytes; - size_t sub_length; + if (json && json->type == JSON_STRING && elem->data.index >= 0) + { + const char *sub_start; + size_t sub_bytes; + size_t sub_length; sub_length = utf8_substring( - json->v.string.str, json->v.string.length, - elem->data.index, 1, - &sub_start, &sub_bytes); + json->v.string.str, json->v.string.length, + elem->data.index, 1, + &sub_start, &sub_bytes); if (sub_length == 1) match_recurse(on_match, ctx, lnext(path), mkRefChar(sub_start, sub_bytes)); - } else if (ref->type == JP_REF_CHAR && elem->data.index == 0) { + } + else if (ref->type == JP_REF_CHAR && elem->data.index == 0) + { /* char(0) on a character yields itself. */ match_recurse(on_match, ctx, lnext(path), ref); } @@ -470,51 +530,60 @@ static void match_recurse(void on_match(void *ctx, JPRef *ref), void *ctx, default:; } - if (elem->recursive_descent && json) { - json_foreach(child, json) { + if (elem->recursive_descent && json) + { + json_foreach(child, json) + { if (!child->jp_changed) match_recurse(on_match, ctx, path, mkRefNode(child)); } } } -static void jp_match_callback(List **results, JPRef *ref) +static void +jp_match_callback(List **results, JPRef * ref) { *results = lappend(*results, ref); } -List *jp_match(JSONPath *jp, json_node *json) +List * +jp_match(JSONPath * jp, json_node * json) { - ListCell *lc = jp_head(jp); - List *results = NIL; + ListCell *lc = jp_head(jp); + List *results = NIL; - match_recurse((void*)jp_match_callback, &results, lc, mkRefNode(json)); + match_recurse((void *) jp_match_callback, &results, lc, mkRefNode(json)); return results; } -static void jp_set_callback(json_node *value, JPRef *ref) +static void +jp_set_callback(json_node * value, JPRef * ref) { - switch (ref->type) { + switch (ref->type) + { case JP_REF_NODE: json_replace_value(ref->u.node, value); ref->u.node->jp_changed = true; break; - default:; /* Do nothing if ref is immutable. */ + default:; /* Do nothing if ref is immutable. */ } } -void jp_set(JSONPath *jp, json_node *json, json_node *value) +void +jp_set(JSONPath * jp, json_node * json, json_node * value) { - ListCell *lc = jp_head(jp); + ListCell *lc = jp_head(jp); - match_recurse((void*)jp_set_callback, value, lc, mkRefNode(json)); + match_recurse((void *) jp_set_callback, value, lc, mkRefNode(json)); } -char *jpref_encode(JPRef *ref) +char * +jpref_encode(JPRef * ref) { - switch (ref->type) { + switch (ref->type) + { case JP_REF_NODE: return json_encode(ref->u.node, JSONOPT_USE_ORIG); |
