summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoey Adams2011-03-28 16:16:25 +0000
committerJoey Adams2011-03-28 16:16:25 +0000
commite2be9574366c72cd0d78659ff47b058f13fc7a68 (patch)
tree78d6c3fe2bd9536d8dcf63865fe4dc526dbd97c8
parent1722a516c355dcfab126a0db7073f50fd5515d0c (diff)
[cleanup] Deleted jsonpath.[ch] .json2
-rw-r--r--jsonpath.c596
-rw-r--r--jsonpath.h103
2 files changed, 0 insertions, 699 deletions
diff --git a/jsonpath.c b/jsonpath.c
deleted file mode 100644
index edfa6e8..0000000
--- a/jsonpath.c
+++ /dev/null
@@ -1,596 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * jsonpath.c
- * JSONPath implementation routines for JSON data type support.
- *
- * Copyright (c) 2010, PostgreSQL Global Development Group
- * Written by Joey Adams <[email protected]>.
- *
- *-------------------------------------------------------------------------
- */
-
-#include "jsonpath.h"
-#include "util.h"
-
-#include <ctype.h>
-
-/* 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] . */
-#define isextended(c) ((unsigned char)(c) > 127)
-
-/* Note that Unicode characters are allowed in identifiers. */
-#define identifier_start(c) (isletter(c) || (c) == '_' || (c) == '$' || isextended(c))
-#define identifier_char(c) (identifier_start(c) || isdigit(c))
-
-#define integer_start(c) (isdigit(c) || (c) == '+' || (c) == '-')
-
-/*
- * In a valid JSONPath list, the first element is always of type JP_ROOT.
- * This element is used so an otherwise empty JSONPath list won't be NULL.
- * This allows us to use NULL to indicate an invalid JSONPath.
- *
- * This function returns the first cell,
- * making sure it is of type JP_ROOT.
- */
-static ListCell *
-jp_root(JSONPath * jp)
-{
- ListCell *cell;
- jp_element *elem;
-
- Assert(jp != NULL);
-
- cell = list_head(jp);
- elem = lfirst(cell);
- Assert(elem->type == JP_ROOT);
-
- return cell;
-}
-
-/*
- * This function returns the second cell of a JSONPath list
- * (the first cell after the JP_ROOT).
- */
-static ListCell *
-jp_head(JSONPath * jp)
-{
- return lnext(jp_root(jp));
-}
-
-/*
- * Note that skip_spaces differs from skip_whitespace in json.c
- * in that this function treats '\f' and '\v' as whitespace.
- * This is because JSON does not accept these characters as
- * whitespace, but since this is JSONPath,
- * we can do whatever we want here :-)
- */
-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)
-{
- jp_element *elem = palloc0(sizeof(*elem));
-
- elem->type = type;
- elem->recursive_descent = rd;
- return elem;
-}
-
-static jp_element *
-mkRoot(void)
-{
- jp_element *elem = mkElement(JP_ROOT, false);
-
- return elem;
-}
-
-static jp_element *
-mkWildcard(bool rd)
-{
- jp_element *elem = mkElement(JP_WILDCARD, rd);
-
- return elem;
-}
-
-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)
-{
- 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)
-{
- jp_element *elem = mkElement(JP_CALL_CHAR, rd);
-
- elem->data.index = index;
- return elem;
-}
-
-static JPRef *
-mkRef(JPRefType type)
-{
- JPRef *ref = palloc0(sizeof(*ref));
-
- ref->type = type;
- return ref;
-}
-
-static JPRef *
-mkRefNode(JSON * node)
-{
- JPRef *ref = mkRef(JP_REF_NODE);
-
- ref->u.node = node;
- return ref;
-}
-
-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;
-
- return ref;
-}
-
-/*
- * jp_show
- * Unparse a JSONPath expression. This is used by parse_json_path
- * to stringify successfully parsed JSONPaths.
- */
-char *
-jp_show(JSONPath * jp)
-{
- StringInfoData string[1];
- ListCell *cell;
- jp_element *elem;
- bool rd;
- char *tmp;
-
- initStringInfo(string);
-
- foreach(cell, jp)
- {
- elem = lfirst(cell);
- rd = elem->recursive_descent;
-
- switch (elem->type)
- {
- case JP_ROOT:
- appendStringInfoChar(string, '$');
- break;
- case JP_WILDCARD:
- appendStringInfoString(string, rd ? "..[*]" : "[*]");
- break;
- case JP_INDEX_SUBSCRIPT:
- appendStringInfo(string, "%s[%ld]", rd ? ".." : "", elem->data.index);
- break;
- case JP_KEY_SUBSCRIPT:
- tmp = json_encode_string(elem->data.key.ptr, elem->data.key.length, '"', false);
- appendStringInfo(string, "%s[%s]", rd ? ".." : "", tmp);
- pfree(tmp);
- break;
- case JP_CALL_CHAR:
- appendStringInfo(string, "%s(%ld)", rd ? "..char" : ".char", elem->data.index);
- break;
- default:
- Assert(false);
- }
- }
-
- return string->data;
-}
-
-/*
- * Parse a long starting at *s .
- *
- * On success, return true and update *s to point to the end of the number.
- * On failure, return false and leave *s untouched.
- */
-static bool
-parse_long(const char **s, long *out)
-{
- const char *p = *s;
-
- errno = 0;
- *out = strtol(*s, (char **) &p, 10);
- if (p <= *s || errno != 0)
- return false;
-
- *s = p;
- return true;
-}
-
-/*
- * jp_parse
- * Parse a JSONPath expression (into a List of jp_element items).
- *
- * TODO: Get rid of all those gotos. The parser uses constant space,
- * so there's no chance of a stack overflow anyway.
- */
-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;
- long index;
- char *key;
- size_t key_length;
-
- skip_spaces(&s);
-
- /* pattern may not be empty */
- if (*s == '\0')
- return NULL;
-
- jp = lappend(jp, mkRoot());
-
- if (*s == '$')
- {
- s++;
- goto begin_element;
- }
- else if (*s != '.')
- {
- goto dot_subscript;
- /* implicit '.' at beginning */
- }
-
-begin_element:
- skip_spaces(&s);
-begin_element_noskip:
-
- recursive_descent = false;
- bracket = false;
-
- if (*s == '\0')
- goto end;
- if (s[0] == '.' && s[1] == '.')
- {
- recursive_descent = true;
- s += 2;
- goto dot_subscript;
- }
- if (s[0] == '.')
- {
- s++;
- goto dot_subscript;
- }
- if (s[0] == '[')
- {
- s++;
- goto bracket_subscript;
- }
-
- goto failed;
-
-next_element:
- if (bracket)
- {
- skip_spaces(&s);
- if (*s != ']')
- goto failed;
- s++;
- }
- goto begin_element;
-
-dot_subscript:
- skip_spaces(&s);
-
- if (*s == '*')
- goto wildcard;
- if (integer_start(*s))
- goto integer;
- if (identifier_start(*s))
- goto identifier;
- if (*s == '"' || *s == '\'')
- goto string;
- if (*s == '[')
- {
- s++;
- goto bracket_subscript;
- }
-
- goto failed;
-
-bracket_subscript:
- skip_spaces(&s);
-
- bracket = true;
-
- if (*s == '*')
- goto wildcard;
- if (integer_start(*s))
- goto integer;
- 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:
- s++;
- jp = lappend(jp, mkWildcard(recursive_descent));
- goto next_element;
-
-integer:
- if (!parse_long(&s, &index))
- goto failed;
-
- jp = lappend(jp, mkIndexSubscript(index, recursive_descent));
- goto next_element;
-
-identifier:
- start = s;
- while (identifier_char(*s))
- s++;
- end = s;
-
- skip_spaces(&s);
-
- if (*s == '(')
- {
- if (end - start == 4 && memcmp(start, "char", 4) == 0)
- {
- s++;
- skip_spaces(&s);
- goto call_char;
- }
-
- goto failed;
- }
-
- key = pnstrdup(start, end - start);
- key_length = end - start;
-
- jp = lappend(jp, mkKeySubscript(key, key_length, recursive_descent));
- goto begin_element_noskip;
-
-string:
- key = json_decode_string(&s, &key_length, false);
- if (key == NULL)
- goto failed;
-
- jp = lappend(jp, mkKeySubscript(key, key_length, recursive_descent));
- goto next_element;
-
-call_char:
- if (!parse_long(&s, &index))
- goto failed;
-
- skip_spaces(&s);
-
- if (*s++ != ')')
- goto failed;
-
- jp = lappend(jp, mkCallChar(index, recursive_descent));
- goto begin_element;
-
-end:
- return jp;
-
-failed:
- return NULL;
-}
-
-/*
- * Currently, a lot of JPRef nodes are allocated just to pass JSON pointers
- * to match_recurse. If this becomes a memory/performance issue in the future,
- * JPRef could merged with JSON by adding JPRef's specialty types to the
- * json_type enum and JSON union. JPRef is currently not merged with
- * JSON 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)
-{
- jp_element *elem;
- JSON *json,
- *child;
-
- if (path == NULL)
- {
- /* The end of the JSONPath list is the "accept" state. */
- on_match(ctx, ref);
- return;
- }
-
- elem = lfirst(path);
-
- if (ref->type == JP_REF_NODE)
- json = ref->u.node;
- else
- json = NULL;
-
- switch (elem->type)
- {
- case JP_WILDCARD:
- if (json != NULL)
- {
- json_foreach(child, json)
- match_recurse(on_match, ctx, lnext(path), mkRefNode(child));
- }
- break;
-
- case JP_INDEX_SUBSCRIPT:
- if (json != NULL && json->type == JSON_ARRAY &&
- elem->data.index >= 0 &&
- (size_t) elem->data.index < json->v.children.count)
- {
- size_t i;
-
- for (child = json->v.children.head, i = 0;
- child != NULL && i < (size_t) elem->data.index;
- child = child->next, i++)
- {
- }
-
- /*
- * If this fails, it means json->v.children.count was greater
- * than the actual number of children.
- */
- Assert(i == elem->data.index && child != NULL);
-
- match_recurse(on_match, ctx, lnext(path), mkRefNode(child));
- }
- break;
-
- case JP_KEY_SUBSCRIPT:
- if (json != NULL && 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) == 0)
- {
- match_recurse(on_match, ctx, lnext(path), mkRefNode(child));
- }
- }
- }
- break;
-
- case JP_CALL_CHAR:
- if (json != NULL && 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);
-
- 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)
- {
- /* char(0) on a character yields itself. */
- match_recurse(on_match, ctx, lnext(path), ref);
- }
- break;
-
- default:;
- }
-
- if (elem->recursive_descent && json != NULL)
- {
- 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)
-{
- *results = lappend(*results, ref);
-}
-
-/*
- * jp_match
- * Match a parsed JSONPath expression against a JSON tree,
- * yielding a List of JPRef* items.
- *
- * To convert the JPRef* items to JSON-formatted strings, use jpref_encode.
- */
-List *
-jp_match(JSONPath * jp, JSON * json)
-{
- ListCell *lc = jp_head(jp);
- List *results = NIL;
-
- match_recurse((void *) jp_match_callback, &results, lc, mkRefNode(json));
-
- return results;
-}
-
-static void
-jp_set_callback(JSON * value, JPRef * ref)
-{
- 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. */
- }
-}
-
-/*
- * jp_set
- * Set all elements that match a parsed JSONPath expression
- * in a JSON tree to a new value.
- *
- * Note that jp_set uses json_replace_value so it doesn't have to deep-copy
- * on every assignment if @value is a tree. This means that parent pointers
- * of the resulting tree will not be trustworthy.
- */
-void
-jp_set(JSONPath * jp, JSON * json, JSON * value)
-{
- ListCell *lc = jp_head(jp);
-
- match_recurse((void *) jp_set_callback, value, lc, mkRefNode(json));
-}
-
-/*
- * jpref_encode
- * Convert a JPRef to a JSON-formatted string.
- */
-char *
-jpref_encode(JPRef * ref)
-{
- switch (ref->type)
- {
- case JP_REF_NODE:
- return json_encode(ref->u.node, JSONOPT_USE_ORIG);
-
- case JP_REF_CHAR:
- return json_encode_string(ref->u.chr.bytes, ref->u.chr.length, '"', false);
-
- default:
- Assert(false);
- return NULL;
- }
-}
diff --git a/jsonpath.h b/jsonpath.h
deleted file mode 100644
index 4be498d..0000000
--- a/jsonpath.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * jsonpath.h
- * JSONPath implementation routines for JSON data type support.
- *
- * The "JSONPath" implemented here is similar to, but not exactly the same as,
- * the JSONPath described at http://goessner.net/articles/JsonPath/ .
- * The main differences are stronger subscripting rules, special methods
- * via function-call notation (currently, the only one provided is .char()),
- * and that the '$' at the beginning of a JSONPath expression is optional.
- * Also, array indices as a set (e.g. [0,1]) and filters/scripts are currently
- * not implemented. Array indices are a planned feature. True filters
- * would require an honest-to-goodness JavaScript engine, so perhaps
- * someone will write a module for that in the future.
- *
- * Copyright (c) 2010, PostgreSQL Global Development Group
- * Written by Joey Adams <[email protected]>.
- *
- *-------------------------------------------------------------------------
- */
-
-#ifndef JSONPATH_H
-#define JSONPATH_H
-
-#include "json.h"
-
-#include "nodes/pg_list.h"
-
-typedef enum
-{
- JP_ROOT,
- JP_WILDCARD,
- JP_INDEX_SUBSCRIPT,
- JP_KEY_SUBSCRIPT,
- JP_CALL_CHAR
-} jp_element_type;
-
-/*
- * A jp_element is a single piece of a JSONPath expression
- * (e.g. [3], .foo, .char(3), ..*). It represents subscripting
- * down one level in a JSON tree, or invoking a special method
- * (like .char() ). However, if recursive_descent is set to true,
- * the subscript applies to a value and all of its descendants.
- */
-typedef struct
-{
- jp_element_type type;
-
- union
- {
- long index;
- struct
- {
- char *ptr;
- size_t length;
- } key;
- } data;
-
- /* If element was preceded by ".." in pattern */
- bool recursive_descent;
-} jp_element;
-
-typedef enum
-{
- JP_REF_NODE,
- JP_REF_CHAR
-} JPRefType;
-
-/*
- * A JPRef is a reference to some part of a JSON tree,
- * typically a value (but not always).
- *
- * JPRef* is really a "subclass" of JSON* . In the future, this structure
- * will likely be merged into the JSON structure to improve performance.
- */
-typedef struct
-{
- JPRefType type;
-
- union
- {
- JSON *node;
-
- struct
- {
- const char *bytes;
- size_t length;
- } chr;
- } u;
-} JPRef;
-
-typedef List /* jp_element* */ JSONPath;
-
-JSONPath *jp_parse(const char *pattern);
-char *jp_show(JSONPath * jp);
-
-List /* JPRef* */ *jp_match(JSONPath * jp, JSON * json);
-void jp_set(JSONPath * jp, JSON * json, JSON * value);
-
-/* Returns the JSON encoding of the given reference. */
-char *jpref_encode(JPRef * ref);
-
-#endif