basic_json-20100915.patch
application/octet-stream
Filename: basic_json-20100915.patch
Type: application/octet-stream
Part: 0
Message:
Basic JSON support
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
| File | + | − |
|---|---|---|
| src/backend/utils/adt/json.c | 0 | 0 |
| src/backend/utils/adt/json_parser.y | 0 | 0 |
| src/backend/utils/adt/json_scanner.l | 0 | 0 |
| src/backend/utils/adt/Makefile | 0 | 0 |
| src/include/catalog/pg_cast.h | 0 | 0 |
| src/include/catalog/pg_proc.h | 0 | 0 |
| src/include/catalog/pg_type.h | 0 | 0 |
| src/include/utils/json.h | 0 | 0 |
| src/test/regress/expected/json.out | 0 | 0 |
| src/test/regress/expected/opr_sanity.out | 0 | 0 |
| src/test/regress/parallel_schedule | 0 | 0 |
| src/test/regress/serial_schedule | 0 | 0 |
| src/test/regress/sql/json.sql | 0 | 0 |
| src/test/regress/sql/opr_sanity.sql | 0 | 0 |
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 70e21b9..483abb6 100644
*** a/src/backend/utils/adt/Makefile
--- b/src/backend/utils/adt/Makefile
*************** endif
*** 18,24 ****
OBJS = acl.o arrayfuncs.o array_userfuncs.o arrayutils.o bool.o \
cash.o char.o date.o datetime.o datum.o domains.o \
enum.o float.o format_type.o \
! geo_ops.o geo_selfuncs.o int.o int8.o like.o lockfuncs.o \
misc.o nabstime.o name.o numeric.o numutils.o \
oid.o oracle_compat.o pseudotypes.o rowtypes.o \
regexp.o regproc.o ruleutils.o selfuncs.o \
--- 18,25 ----
OBJS = acl.o arrayfuncs.o array_userfuncs.o arrayutils.o bool.o \
cash.o char.o date.o datetime.o datum.o domains.o \
enum.o float.o format_type.o \
! geo_ops.o geo_selfuncs.o json.o json_parser.o json_scanner.o \
! int.o int8.o like.o lockfuncs.o \
misc.o nabstime.o name.o numeric.o numutils.o \
oid.o oracle_compat.o pseudotypes.o rowtypes.o \
regexp.o regproc.o ruleutils.o selfuncs.o \
*************** OBJS = acl.o arrayfuncs.o array_userfunc
*** 33,36 ****
--- 34,62 ----
like.o: like.c like_match.c
+ json_parser.o: json_scanner.c
+
+ # Latest flex causes warnings in this file.
+ #ifeq ($(GCC),yes)
+ #json_parser.o: CFLAGS += -Wno-error
+ #endif
+
+ json_parser.h: json_parser.c ;
+
+ json_parser.c: json_parser.y
+ ifdef BISON
+ $(BISON) -d $(BISONFLAGS) -o $@ $<
+ else
+ @$(missing) bison $< $@
+ endif
+
+ json_scanner.c: json_scanner.l
+ ifdef FLEX
+ $(FLEX) $(FLEXFLAGS) -o'$@' $<
+ else
+ @$(missing) flex $< $@
+ endif
+
+ json_parser.o keywords.o parser.o: json_parser.h
+
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/utils/adt/json.c b/src/backend/utils/adt/json.c
index ...13e5391 .
*** a/src/backend/utils/adt/json.c
--- b/src/backend/utils/adt/json.c
***************
*** 0 ****
--- 1,307 ----
+ /*-------------------------------------------------------------------------
+ *
+ * json.c
+ * JSON data type support.
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $PostgreSQL$
+ *
+ *-------------------------------------------------------------------------
+ */
+
+ #include "postgres.h"
+
+ #include "libpq/pqformat.h"
+ #include "utils/builtins.h"
+ #include "utils/json.h"
+
+ #include "json_parser.h"
+ #include "json_scanner.h"
+
+ static Json *json_parse(const char *str, int len, bool validateOnly);
+ static void json_free(Json *node);
+ static void append_json(StringInfo buf, const Json *node, int indent);
+ static void append_indent(StringInfo buf, int indent);
+
+ extern int json_yyparse(yyscan_t scanner);
+ extern void json_yyerror(yyscan_t scanner, const char *error);
+ extern void *json_yyalloc(size_t bytes, void *yyscanner);
+ extern void *json_yyrealloc(void *ptr, size_t bytes, void *yyscanner);
+ extern void json_yyfree(void *ptr, void *yyscanner);
+
+ /* indent with 4 spaces */
+ #define JSON_INDENT_STRING " "
+
+
+ Datum
+ json_in(PG_FUNCTION_ARGS)
+ {
+ char *s = PG_GETARG_CSTRING(0);
+ jsontype *json;
+
+ json = (jsontype *) cstring_to_text(s);
+
+ /*
+ * Parse the data to check if it is a JSON data. Assume that
+ * ERROR occurred if parsing failed.
+ */
+ json_parse(VARDATA_ANY(json), VARSIZE_ANY_EXHDR(json), true);
+
+ PG_RETURN_JSON_P(json);
+ }
+
+ Datum
+ json_out(PG_FUNCTION_ARGS)
+ {
+ /* text and jsontype are binary-compatible */
+ return textout(fcinfo);
+ }
+
+ Datum
+ json_recv(PG_FUNCTION_ARGS)
+ {
+ StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
+ jsontype *result;
+ char *str;
+ int nbytes;
+
+ str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
+ json_parse(str, nbytes, true);
+
+ result = (jsontype *) cstring_to_text_with_len(str, nbytes);
+ pfree(str);
+ PG_RETURN_JSON_P(result);
+ }
+
+ Datum
+ json_send(PG_FUNCTION_ARGS)
+ {
+ /* text and jsontype are binary-compatible */
+ return textsend(fcinfo);
+ }
+
+ Datum
+ text_to_json(PG_FUNCTION_ARGS)
+ {
+ text *t = PG_GETARG_TEXT_PP(0);
+
+ json_parse(VARDATA_ANY(t), VARSIZE_ANY_EXHDR(t), true);
+
+ PG_RETURN_JSON_P((jsontype *) t);
+ }
+
+ Datum
+ json_to_text(PG_FUNCTION_ARGS)
+ {
+ /* text and jsontype are binary-compatible */
+ PG_RETURN_DATUM(PG_GETARG_DATUM(0));
+ }
+
+ Datum
+ json_pretty(PG_FUNCTION_ARGS)
+ {
+ jsontype *json = PG_GETARG_JSON_PP(0);
+ Json *node;
+ StringInfoData buf;
+
+ node = json_parse(VARDATA_ANY(json), VARSIZE_ANY_EXHDR(json), false);
+ initStringInfo(&buf);
+ append_json(&buf, node, 0);
+ json_free(node);
+
+ return CStringGetTextDatum(buf.data);
+ }
+
+ /*
+ * json_parse -- parse and validate the JSON text.
+ *
+ * Only validates the input when validateOnly is true, or returns JSON
+ * tree on false.
+ */
+ static Json *
+ json_parse(const char *str, int len, bool validateOnly)
+ {
+ yyscan_t scanner;
+ char *scanbuf;
+ JsonParser parser;
+
+ scanbuf = (char *) palloc(len + 2);
+ memcpy(scanbuf, str, len);
+ scanbuf[len] = scanbuf[len + 1] = '\0';
+
+ /* initialize parser and scanner */
+ memset(&parser, 0, sizeof(parser));
+ parser.validateOnly = validateOnly;
+ if (!validateOnly)
+ initStringInfo(&parser.buf);
+
+ json_yylex_init(&scanner);
+ json_yyset_extra(&parser, scanner);
+ json_yy_scan_buffer(scanbuf, len + 2, scanner);
+
+ /* parse the JSON text */
+ json_yyparse(scanner);
+
+ /* cleanup */
+ json_yylex_destroy(scanner);
+ pfree(scanbuf);
+ if (parser.buf.data)
+ pfree(parser.buf.data);
+
+ return parser.json;
+ }
+
+ /*
+ * json_free -- free JSON node tree recursively
+ */
+ static void
+ json_free(Json *node)
+ {
+ ListCell *cell;
+
+ switch (node->type)
+ {
+ case JSON_NULL:
+ case JSON_BOOL:
+ break;
+ case JSON_NUMBER:
+ pfree(node->value.number);
+ break;
+ case JSON_STRING:
+ pfree(node->value.string);
+ break;
+ case JSON_ARRAY:
+ foreach (cell, node->value.array)
+ {
+ Json *item = (Json *) lfirst(cell);
+
+ json_free(item);
+ }
+ list_free(node->value.array);
+ break;
+ case JSON_OBJECT:
+ foreach (cell, node->value.object)
+ {
+ JsonAttr *attr = (JsonAttr *) lfirst(cell);
+
+ pfree(attr->key);
+ json_free(attr->value);
+ pfree(attr);
+ }
+ list_free(node->value.object);
+ break;
+ default:
+ elog(ERROR, "unexpected json type: %d", node->type);
+ }
+
+ pfree(node);
+ }
+
+ /*
+ * append_json -- append JSON node tree to string with indentation
+ */
+ static void
+ append_json(StringInfo buf, const Json *node, int indent)
+ {
+ ListCell *cell;
+ bool sep;
+
+ switch (node->type)
+ {
+ case JSON_NULL:
+ appendStringInfoString(buf, "null");
+ break;
+ case JSON_BOOL:
+ appendStringInfoString(buf, node->value.boolean ? "true" : "false");
+ break;
+ case JSON_NUMBER:
+ appendStringInfoString(buf, node->value.number);
+ break;
+ case JSON_STRING:
+ appendStringInfoString(buf, node->value.string);
+ break;
+ case JSON_ARRAY:
+ appendStringInfoString(buf, "[\n");
+ sep = false;
+ foreach (cell, node->value.array)
+ {
+ const Json *value = (const Json *) lfirst(cell);
+
+ if (sep)
+ appendStringInfoString(buf, ",\n");
+ else
+ sep = true;
+ append_indent(buf, indent + 1);
+ append_json(buf, value, indent + 1);
+ }
+ if (sep)
+ appendStringInfoChar(buf, '\n');
+ append_indent(buf, indent);
+ appendStringInfoChar(buf, ']');
+ break;
+ case JSON_OBJECT:
+ appendStringInfoString(buf, "{\n");
+ sep = false;
+ foreach (cell, node->value.object)
+ {
+ const JsonAttr *attr = (const JsonAttr *) lfirst(cell);
+ const char *key = attr->key;
+ const Json *value = attr->value;
+
+ if (sep)
+ appendStringInfoString(buf, ",\n");
+ else
+ sep = true;
+ append_indent(buf, indent + 1);
+ appendStringInfo(buf, "%s: ", key);
+ append_json(buf, value, indent + 1);
+ }
+ if (sep)
+ appendStringInfoChar(buf, '\n');
+ append_indent(buf, indent);
+ appendStringInfoChar(buf, '}');
+ break;
+ default:
+ elog(ERROR, "unexpected json type: %d", node->type);
+ }
+ }
+
+ static void
+ append_indent(StringInfo buf, int indent)
+ {
+ int i;
+ for (i = 0; i < indent; i++)
+ appendStringInfoString(buf, JSON_INDENT_STRING);
+ }
+
+ void
+ json_yyerror(yyscan_t scanner, const char *error)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("syntax error in json: %s", error)));
+ }
+
+ void *
+ json_yyalloc(size_t bytes, void *yyscanner)
+ {
+ return palloc(bytes);
+ }
+
+ void *
+ json_yyrealloc(void *ptr, size_t bytes, void *yyscanner)
+ {
+ if (ptr != NULL)
+ return repalloc(ptr, bytes);
+ else
+ return palloc(bytes);
+ }
+
+ void
+ json_yyfree(void *ptr, void *yyscanner)
+ {
+ if (ptr != NULL)
+ pfree(ptr);
+ }
diff --git a/src/backend/utils/adt/json_parser.y b/src/backend/utils/adt/json_parser.y
index ...fb769e2 .
*** a/src/backend/utils/adt/json_parser.y
--- b/src/backend/utils/adt/json_parser.y
***************
*** 0 ****
--- 1,221 ----
+ %{
+ /*-------------------------------------------------------------------------
+ *
+ * json_parser.y
+ * Parser for JSON data types.
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $PostgreSQL$
+ *
+ *-------------------------------------------------------------------------
+ */
+
+ #include "postgres.h"
+ #include "nodes/pg_list.h"
+ #include "utils/json.h"
+
+ static Json *makeJsonNode(JsonType type);
+ static Json *makeJsonNull(JsonParser *parser);
+ static Json *makeJsonBool(JsonParser *parser, bool value);
+ static Json *makeJsonNumber(JsonParser *parser, char *value);
+ static Json *makeJsonString(JsonParser *parser, char *value);
+ static Json *makeJsonArray(JsonParser *parser, List *items);
+ static Json *makeJsonObject(JsonParser *parser, List *attrs);
+ static JsonAttr *makeJsonAttr(JsonParser *parser, char *key, Json *value);
+ static List *makeList(JsonParser *parser, void *elem);
+ static List *lconsList(JsonParser *parser, void *elem, List *list);
+
+ #include "json_parser.h"
+ #include "json_scanner.h"
+
+ #define getParser() ((JsonParser *) json_yyget_extra(scanner))
+
+ extern void json_yyerror(yyscan_t, const char *);
+ %}
+
+ %defines
+ %expect 0
+ %name-prefix="json_yy"
+ %pure-parser
+ %parse-param {yyscan_t scanner}
+ %lex-param {yyscan_t scanner}
+
+ %union {
+ bool boolean;
+ char *number;
+ char *string;
+ Json *node;
+ JsonAttr *attr;
+ List *list;
+ }
+
+ %token <string> T_JSON_STRING
+ %token <number> T_JSON_NUMBER
+ %token <boolean> T_JSON_TRUE T_JSON_FALSE
+ %token T_JSON_NULL
+ %token T_JSON_INVALID
+
+ %type <number> number
+ %type <string> string
+ %type <node> json value array object
+ %type <list> items attrs
+ %type <attr> attr
+
+ %%
+ json:
+ value { getParser()->json = $1; }
+ ;
+
+ array:
+ '[' ']' { $$ = makeJsonArray(getParser(), NIL); }
+ | '[' items ']' { $$ = makeJsonArray(getParser(), $2);; }
+ ;
+
+ items:
+ value { $$ = makeList(getParser(), $1); }
+ | value ',' items { $$ = lconsList(getParser(), $1, $3); }
+ ;
+
+ object:
+ '{' '}' { $$ = makeJsonObject(getParser(), NIL); }
+ | '{' attrs '}' { $$ = makeJsonObject(getParser(), $2); }
+ ;
+
+ attrs:
+ attr { $$ = makeList(getParser(), $1); }
+ | attr ',' attrs { $$ = lconsList(getParser(), $1, $3); }
+ ;
+
+ attr:
+ string ':' value { $$ = makeJsonAttr(getParser(), $1, $3); }
+ ;
+
+ value:
+ T_JSON_NULL { $$ = makeJsonNull(getParser()); }
+ | T_JSON_TRUE { $$ = makeJsonBool(getParser(), true); }
+ | T_JSON_FALSE { $$ = makeJsonBool(getParser(), false); }
+ | number { $$ = makeJsonNumber(getParser(), $1); }
+ | string { $$ = makeJsonString(getParser(), $1); }
+ | array { $$ = $1; }
+ | object { $$ = $1; }
+ ;
+
+ number: T_JSON_NUMBER { $$ = $1; };
+ string: T_JSON_STRING { $$ = $1; };
+
+ %%
+ static Json *
+ makeJsonNode(JsonType type)
+ {
+ Json *node = (Json *) palloc(sizeof(Json));
+ node->type = type;
+ return node;
+ }
+
+ static Json *
+ makeJsonNull(JsonParser *parser)
+ {
+ if (parser->validateOnly)
+ return NULL;
+ else
+ return makeJsonNode(JSON_NULL);
+ }
+
+ static Json *
+ makeJsonBool(JsonParser *parser, bool value)
+ {
+ if (parser->validateOnly)
+ return NULL;
+ else
+ {
+ Json *node = makeJsonNode(JSON_BOOL);
+ node->value.boolean = value;
+ return node;
+ }
+ }
+
+ static Json *
+ makeJsonNumber(JsonParser *parser, char *value)
+ {
+ if (parser->validateOnly)
+ return NULL;
+ else
+ {
+ Json *node = makeJsonNode(JSON_NUMBER);
+ node->value.number = value;
+ return node;
+ }
+ }
+
+ static Json *
+ makeJsonString(JsonParser *parser, char *value)
+ {
+ if (parser->validateOnly)
+ return NULL;
+ else
+ {
+ Json *node = makeJsonNode(JSON_STRING);
+ node->value.string = value;
+ return node;
+ }
+ }
+
+ static Json *
+ makeJsonArray(JsonParser *parser, List *items)
+ {
+ if (parser->validateOnly)
+ return NULL;
+ else
+ {
+ Json *node = makeJsonNode(JSON_ARRAY);
+ node->value.array = items;
+ return node;
+ }
+ }
+
+ static Json *
+ makeJsonObject(JsonParser *parser, List *attrs)
+ {
+ if (parser->validateOnly)
+ return NULL;
+ else
+ {
+ Json *node = makeJsonNode(JSON_OBJECT);
+ node->value.object = attrs;
+ return node;
+ }
+ }
+
+ static JsonAttr *
+ makeJsonAttr(JsonParser *parser, char *key, Json *value)
+ {
+ if (parser->validateOnly)
+ return NULL;
+ else
+ {
+ JsonAttr *attr = (JsonAttr *) palloc(sizeof(JsonAttr));
+ attr->key = key;
+ attr->value = value;
+ return attr;
+ }
+ }
+
+ static List *
+ makeList(JsonParser *parser, void *elem)
+ {
+ if (parser->validateOnly)
+ return NIL;
+ else
+ return list_make1(elem);
+ }
+
+ static List *
+ lconsList(JsonParser *parser, void *elem, List *list)
+ {
+ if (parser->validateOnly)
+ return NIL;
+ else
+ return lcons(elem, list);
+ }
diff --git a/src/backend/utils/adt/json_scanner.l b/src/backend/utils/adt/json_scanner.l
index ...f4ed64f .
*** a/src/backend/utils/adt/json_scanner.l
--- b/src/backend/utils/adt/json_scanner.l
***************
*** 0 ****
--- 1,88 ----
+ %{
+ /*-------------------------------------------------------------------------
+ *
+ * json_snanner.l
+ * Lexical scanner for JSON data types.
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $PostgreSQL$
+ *
+ *-------------------------------------------------------------------------
+ */
+
+ #include "postgres.h"
+ #include "nodes/pg_list.h"
+ #include "utils/json.h"
+ #include "json_parser.h"
+
+ #define getParser() ((JsonParser *) json_yyget_extra(yyscanner))
+ %}
+
+ %option reentrant
+ %option bison-bridge
+ %option 8bit
+ %option never-interactive
+ %option nodefault
+ %option noinput
+ %option nounput
+ %option noyywrap
+ %option noyyalloc
+ %option noyyrealloc
+ %option noyyfree
+ %option warn
+ %option prefix="json_yy"
+ %option outfile="json_scanner.c" header-file="json_scanner.h"
+
+ %x str
+
+ %%
+ /* Whitespace */
+ [ \t\r\n]
+
+ /* Symbol */
+ [\[\]\{\}:,] return yytext[0];
+
+ /* Null */
+ null { return T_JSON_NULL; }
+
+ /* Boolean */
+ true { return T_JSON_TRUE; }
+ false { return T_JSON_FALSE; }
+
+ /* Number */
+ 0|-?([1-9][0-9]*(\.[0-9]+)?|0\.[0-9]+)([Ee][+-]?[0-9]+)? {
+ JsonParser *parser = getParser();
+ if (!parser->validateOnly)
+ yylval->number = pstrdup(yytext);
+ return T_JSON_NUMBER;
+ }
+
+ /* String */
+ \" {
+ JsonParser *parser = getParser();
+ BEGIN str;
+ if (!parser->validateOnly)
+ appendStringInfoString(&parser->buf, yytext);
+ }
+ <str>([^\"\\[:cntrl:]]+|\\u[0-9A-Fa-f]{4}|\\[\"\\/bfnrt]) {
+ JsonParser *parser = getParser();
+ if (!parser->validateOnly)
+ appendStringInfoString(&parser->buf, yytext);
+ }
+ <str>\" {
+ JsonParser *parser = getParser();
+ if (!parser->validateOnly)
+ {
+ appendStringInfoString(&parser->buf, yytext);
+ yylval->string = pstrdup(parser->buf.data);
+ resetStringInfo(&parser->buf);
+ }
+ BEGIN(INITIAL);
+ return T_JSON_STRING;
+ }
+ <str>.|\n { return T_JSON_INVALID; } /* unterminated string */
+
+ /* Invalid */
+ . return T_JSON_INVALID;
diff --git a/src/include/catalog/pg_cast.h b/src/include/catalog/pg_cast.h
index f53a39a..569dcaa 100644
*** a/src/include/catalog/pg_cast.h
--- b/src/include/catalog/pg_cast.h
*************** DATA(insert ( 869 25 730 a f ));
*** 320,325 ****
--- 320,327 ----
DATA(insert ( 16 25 2971 a f ));
DATA(insert ( 142 25 0 a b ));
DATA(insert ( 25 142 2896 e f ));
+ DATA(insert ( 321 25 0 a b ));
+ DATA(insert ( 25 321 3830 e f ));
/*
* Cross-category casts to and from VARCHAR
*************** DATA(insert ( 869 1043 730 a f ));
*** 331,336 ****
--- 333,340 ----
DATA(insert ( 16 1043 2971 a f ));
DATA(insert ( 142 1043 0 a b ));
DATA(insert ( 1043 142 2896 e f ));
+ DATA(insert ( 321 1043 0 a b ));
+ DATA(insert ( 1043 321 3830 e f ));
/*
* Cross-category casts to and from BPCHAR
*************** DATA(insert ( 869 1042 730 a f ));
*** 342,347 ****
--- 346,353 ----
DATA(insert ( 16 1042 2971 a f ));
DATA(insert ( 142 1042 0 a b ));
DATA(insert ( 1042 142 2896 e f ));
+ DATA(insert ( 321 1042 0 a b ));
+ DATA(insert ( 1042 321 3830 e f ));
/*
* Length-coercion functions
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 427f5ea..b3d3da5 100644
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
*************** DESCR("determine if a string is well for
*** 4457,4462 ****
--- 4457,4478 ----
DATA(insert OID = 3053 ( xml_is_well_formed_content PGNSP PGUID 12 1 0 0 f f f t f i 1 0 16 "25" _null_ _null_ _null_ _null_ xml_is_well_formed_content _null_ _null_ _null_ ));
DESCR("determine if a string is well formed XML content");
+ /* JSON support */
+ DATA(insert OID = 3826 ( json_in PGNSP PGUID 12 1 0 0 f f f t f i 1 0 321 "2275" _null_ _null_ _null_ _null_ json_in _null_ _null_ _null_ ));
+ DESCR("I/O");
+ DATA(insert OID = 3827 ( json_out PGNSP PGUID 12 1 0 0 f f f t f i 1 0 2275 "321" _null_ _null_ _null_ _null_ json_out _null_ _null_ _null_ ));
+ DESCR("I/O");
+ DATA(insert OID = 3828 ( json_recv PGNSP PGUID 12 1 0 0 f f f t f i 1 0 321 "2281" _null_ _null_ _null_ _null_ json_recv _null_ _null_ _null_ ));
+ DESCR("I/O");
+ DATA(insert OID = 3829 ( json_send PGNSP PGUID 12 1 0 0 f f f t f i 1 0 17 "321" _null_ _null_ _null_ _null_ json_send _null_ _null_ _null_ ));
+ DESCR("I/O");
+ DATA(insert OID = 3830 ( json PGNSP PGUID 12 1 0 0 f f f t f s 1 0 321 "25" _null_ _null_ _null_ _null_ text_to_json _null_ _null_ _null_ ));
+ DESCR("perform a non-validating parse of a character string to produce a JSON value");
+ DATA(insert OID = 3831 ( text PGNSP PGUID 12 1 0 0 f f f t f i 1 0 25 "321" _null_ _null_ _null_ _null_ json_to_text _null_ _null_ _null_ ));
+ DESCR("serialize a JSON value to a character string");
+ DATA(insert OID = 3832 ( json_pretty PGNSP PGUID 12 1 0 0 f f f t f i 1 0 25 "321" _null_ _null_ _null_ _null_ json_pretty _null_ _null_ _null_ ));
+ DESCR("convert a JSON to a human readable text");
+
/* uuid */
DATA(insert OID = 2952 ( uuid_in PGNSP PGUID 12 1 0 0 f f f t f i 1 0 2950 "2275" _null_ _null_ _null_ _null_ uuid_in _null_ _null_ _null_ ));
DESCR("I/O");
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 05fc974..6b110bf 100644
*** a/src/include/catalog/pg_type.h
--- b/src/include/catalog/pg_type.h
*************** DESCR("storage manager");
*** 354,359 ****
--- 354,364 ----
/* OIDS 300 - 399 */
+ DATA(insert OID = 321 ( json PGNSP PGUID -1 f b U f t \054 0 0 322 json_in json_out json_recv json_send - - - i x f 0 -1 0 _null_ _null_ ));
+ DESCR("JSON content");
+ #define JSONOID 321
+ DATA(insert OID = 322 ( _json PGNSP PGUID -1 f b A f t \054 0 321 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+
/* OIDS 400 - 499 */
/* OIDS 500 - 599 */
diff --git a/src/include/utils/json.h b/src/include/utils/json.h
index ...952d1bc .
*** a/src/include/utils/json.h
--- b/src/include/utils/json.h
***************
*** 0 ****
--- 1,75 ----
+ /*-------------------------------------------------------------------------
+ *
+ * json.h
+ * Declarations for JSON data type support.
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $PostgreSQL$
+ *
+ *-------------------------------------------------------------------------
+ */
+
+ #ifndef JSON_H
+ #define JSON_H
+
+ #include "fmgr.h"
+ #include "nodes/execnodes.h"
+ #include "nodes/primnodes.h"
+
+ typedef struct varlena jsontype;
+
+ #define DatumGetJsonP(X) ((jsontype *) PG_DETOAST_DATUM(X))
+ #define DatumGetJsonPP(X) ((jsontype *) PG_DETOAST_DATUM_PACKED(X))
+ #define JsonPGetDatum(X) PointerGetDatum(X)
+
+ #define PG_GETARG_JSON_P(n) DatumGetJsonP(PG_GETARG_DATUM(n))
+ #define PG_GETARG_JSON_PP(n) DatumGetJsonPP(PG_GETARG_DATUM(n))
+ #define PG_RETURN_JSON_P(x) PG_RETURN_POINTER(x)
+
+ extern Datum json_in(PG_FUNCTION_ARGS);
+ extern Datum json_out(PG_FUNCTION_ARGS);
+ extern Datum json_recv(PG_FUNCTION_ARGS);
+ extern Datum json_send(PG_FUNCTION_ARGS);
+ extern Datum text_to_json(PG_FUNCTION_ARGS);
+ extern Datum json_to_text(PG_FUNCTION_ARGS);
+ extern Datum json_pretty(PG_FUNCTION_ARGS);
+
+ typedef enum JsonType
+ {
+ JSON_NULL,
+ JSON_BOOL,
+ JSON_NUMBER,
+ JSON_STRING,
+ JSON_ARRAY,
+ JSON_OBJECT
+ } JsonType;
+
+ typedef struct Json
+ {
+ JsonType type;
+ union
+ {
+ bool boolean;
+ char *number;
+ char *string;
+ List *array; /* a list of Json */
+ List *object; /* a list of JsonAttr */
+ } value;
+ } Json;
+
+ typedef struct JsonAttr
+ {
+ char *key;
+ Json *value;
+ } JsonAttr;
+
+ typedef struct JsonParser
+ {
+ Json *json;
+ StringInfoData buf;
+ bool validateOnly;
+ } JsonParser;
+
+ #endif /* JSON_H */
diff --git a/src/test/regress/expected/json.out b/src/test/regress/expected/json.out
index ...1237714 .
*** a/src/test/regress/expected/json.out
--- b/src/test/regress/expected/json.out
***************
*** 0 ****
--- 1,39 ----
+ CREATE TABLE jsontest (
+ id int,
+ data json
+ );
+ INSERT INTO jsontest VALUES (1, 'null');
+ INSERT INTO jsontest VALUES (2, '{"booleans":[true,false],"null":null}');
+ INSERT INTO jsontest VALUES (3, E'[\n123.456,\n"string",\n{"name":"object"}\n]');
+ INSERT INTO jsontest VALUES (101, 'wrong');
+ ERROR: syntax error in json: syntax error
+ LINE 1: INSERT INTO jsontest VALUES (101, 'wrong');
+ ^
+ INSERT INTO jsontest VALUES (101, '[wrong');
+ ERROR: syntax error in json: syntax error
+ LINE 1: INSERT INTO jsontest VALUES (101, '[wrong');
+ ^
+ INSERT INTO jsontest VALUES (101, '{"wrong":123}}');
+ ERROR: syntax error in json: syntax error
+ LINE 1: INSERT INTO jsontest VALUES (101, '{"wrong":123}}');
+ ^
+ SELECT *, json_pretty(data) FROM jsontest;
+ id | data | json_pretty
+ ----+---------------------------------------+--------------------------
+ 1 | null | null
+ 2 | {"booleans":[true,false],"null":null} | { +
+ | | "booleans": [ +
+ | | true, +
+ | | false +
+ | | ], +
+ | | "null": null +
+ | | }
+ 3 | [ +| [ +
+ | 123.456, +| 123.456, +
+ | "string", +| "string", +
+ | {"name":"object"} +| { +
+ | ] | "name": "object"+
+ | | } +
+ | | ]
+ (3 rows)
+
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 4703d49..ca6ff28 100644
*** a/src/test/regress/expected/opr_sanity.out
--- b/src/test/regress/expected/opr_sanity.out
*************** WHERE c.castfunc = p.oid AND
*** 402,407 ****
--- 402,409 ----
-- texttoxml(), which does an XML syntax check.
-- As of 9.1, this finds the cast from pg_node_tree to text, which we
-- intentionally do not provide a reverse pathway for.
+ -- Also, this finds the casts from json to text, varchar, and bpchar,
+ -- because of the same reason as xml.
SELECT castsource::regtype, casttarget::regtype, castfunc, castcontext
FROM pg_cast c
WHERE c.castmethod = 'b' AND
*************** WHERE c.castmethod = 'b' AND
*** 416,424 ****
pg_node_tree | text | 0 | i
cidr | inet | 0 | i
xml | text | 0 | a
xml | character varying | 0 | a
xml | character | 0 | a
! (7 rows)
-- **************** pg_operator ****************
-- Look for illegal values in pg_operator fields.
--- 418,429 ----
pg_node_tree | text | 0 | i
cidr | inet | 0 | i
xml | text | 0 | a
+ json | text | 0 | a
xml | character varying | 0 | a
+ json | character varying | 0 | a
xml | character | 0 | a
! json | character | 0 | a
! (10 rows)
-- **************** pg_operator ****************
-- Look for illegal values in pg_operator fields.
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 191d1fe..e056bd2 100644
*** a/src/test/regress/parallel_schedule
--- b/src/test/regress/parallel_schedule
*************** test: select_views portals_p2 foreign_ke
*** 91,97 ****
# NB: temp.sql does a reconnect which transiently uses 2 connections,
# so keep this parallel group to at most 19 tests
# ----------
! test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml
# run stats by itself because its delay may be insufficient under heavy load
test: stats
--- 91,97 ----
# NB: temp.sql does a reconnect which transiently uses 2 connections,
# so keep this parallel group to at most 19 tests
# ----------
! test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml json
# run stats by itself because its delay may be insufficient under heavy load
test: stats
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 80a9881..3965bef 100644
*** a/src/test/regress/serial_schedule
--- b/src/test/regress/serial_schedule
*************** test: returning
*** 123,126 ****
--- 123,127 ----
test: largeobject
test: with
test: xml
+ text: json
test: stats
diff --git a/src/test/regress/sql/json.sql b/src/test/regress/sql/json.sql
index ...76a1d06 .
*** a/src/test/regress/sql/json.sql
--- b/src/test/regress/sql/json.sql
***************
*** 0 ****
--- 1,13 ----
+ CREATE TABLE jsontest (
+ id int,
+ data json
+ );
+
+ INSERT INTO jsontest VALUES (1, 'null');
+ INSERT INTO jsontest VALUES (2, '{"booleans":[true,false],"null":null}');
+ INSERT INTO jsontest VALUES (3, E'[\n123.456,\n"string",\n{"name":"object"}\n]');
+ INSERT INTO jsontest VALUES (101, 'wrong');
+ INSERT INTO jsontest VALUES (101, '[wrong');
+ INSERT INTO jsontest VALUES (101, '{"wrong":123}}');
+
+ SELECT *, json_pretty(data) FROM jsontest;
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 0d084a1..fb71bb2 100644
*** a/src/test/regress/sql/opr_sanity.sql
--- b/src/test/regress/sql/opr_sanity.sql
*************** WHERE c.castfunc = p.oid AND
*** 320,325 ****
--- 320,327 ----
-- As of 9.1, this finds the cast from pg_node_tree to text, which we
-- intentionally do not provide a reverse pathway for.
+ -- Also, this finds the casts from json to text, varchar, and bpchar,
+ -- because of the same reason as xml.
SELECT castsource::regtype, casttarget::regtype, castfunc, castcontext
FROM pg_cast c