basic_json-20100921.patch

application/octet-stream

Filename: basic_json-20100921.patch
Type: application/octet-stream
Part: 0
Message: Re: 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 be272b5..796fcac 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,66 ----
  
  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.h: json_scanner.c ;
+ 
+ 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
+ 
+ json.c: json_parser.h json_scanner.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 ...864bc87 .
*** a/src/backend/utils/adt/json.c
--- b/src/backend/utils/adt/json.c
***************
*** 0 ****
--- 1,460 ----
+ /*-------------------------------------------------------------------------
+  *
+  * 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
+  *
+  *
+  * IDENTIFICATION
+  *	  src/backend/utils/adt/json.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ 
+ #include "postgres.h"
+ 
+ #include "catalog/pg_type.h"
+ #include "libpq/pqformat.h"
+ #include "utils/array.h"
+ #include "utils/builtins.h"
+ #include "utils/json.h"
+ 
+ #include "json_parser.h"
+ #include "json_scanner.h"
+ 
+ static Json *json_to_node(const char *str, int len);
+ static bool json_validate(const char *str, int len);
+ static Json *json_parse_internal(const char *str, int len, bool *validate);
+ static void append_json(StringInfo buf, const Json *node, const text *space,
+ 						int indent);
+ static void append_indent(StringInfo buf, const text *space, int indent);
+ #ifdef NOT_USED
+ static void json_free(Json *node);
+ #endif
+ 
+ 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);
+ 
+ 
+ Datum
+ json_in(PG_FUNCTION_ARGS)
+ {
+ 	char	   *s = PG_GETARG_CSTRING(0);
+ 	jsontype   *json;
+ 
+ 	json = (jsontype *) cstring_to_text(s);
+ 
+ 	if (!json_validate(VARDATA_ANY(json), VARSIZE_ANY_EXHDR(json)))
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ 				 errmsg("invalid input syntax for JSON")));
+ 
+ 	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);
+ 	if (!json_validate(str, nbytes))
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
+ 				 errmsg("invalid input syntax for JSON")));
+ 
+ 	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);
+ }
+ 
+ /*
+  * json_is_well_formed(text) RETURNS bool
+  */
+ Datum
+ json_is_well_formed(PG_FUNCTION_ARGS)
+ {
+ 	text	   *t = PG_GETARG_TEXT_PP(0);
+ 	bool		ret;
+ 
+ 	ret = json_validate(VARDATA_ANY(t), VARSIZE_ANY_EXHDR(t));
+ 	PG_RETURN_BOOL(ret);
+ }
+ 
+ Datum
+ json_parse(PG_FUNCTION_ARGS)
+ {
+ 	text	   *t = PG_GETARG_TEXT_PP(0);
+ 
+ 	if (!json_validate(VARDATA_ANY(t), VARSIZE_ANY_EXHDR(t)))
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ 				 errmsg("invalid input syntax for JSON")));
+ 
+ 	PG_RETURN_JSON_P((jsontype *) t);
+ }
+ 
+ Datum
+ json_stringify(PG_FUNCTION_ARGS)
+ {
+ 	/* text and jsontype are binary-compatible */
+ 	PG_RETURN_DATUM(PG_GETARG_DATUM(0));
+ }
+ 
+ /*
+  * json_stringify(json, space) -- Format a JSON value into text with or
+  * without indentation. If 'space' is NULL, all of the unneeded whitespaces
+  * are removed.
+  *
+  * XXX: The whitespace-trimmer might be confusable because json_stringify(x)
+  * and json_stringify(x, NULL) behave differently. We could move the trimmer
+  * into json_in and json_parse. If do so, we cannot return the input as-is,
+  * but the above two calls can return the same result.
+  */
+ Datum
+ json_stringify_space(PG_FUNCTION_ARGS)
+ {
+ 	jsontype	   *json;
+ 	text		   *space;
+ 	Json		   *node;
+ 	StringInfoData	buf;
+ 
+ 	if (PG_ARGISNULL(0))
+ 		PG_RETURN_NULL();
+ 
+ 	json = PG_GETARG_JSON_PP(0);
+ 	space = (PG_ARGISNULL(1) ? NULL : PG_GETARG_TEXT_PP(1));
+ 
+ 	/* This should be succeeded because it have been validated on input. */
+ 	node = json_to_node(VARDATA_ANY(json), VARSIZE_ANY_EXHDR(json));
+ 	if (node == NULL)
+ 		elog(ERROR, "unexpected json value");
+ 
+ 	initStringInfo(&buf);
+ 	append_json(&buf, node, space, 0);
+ 
+ #ifdef NOT_USED
+ 	json_free(node);
+ #endif
+ 
+ 	return CStringGetTextDatum(buf.data);
+ }
+ 
+ /*
+  * json_to_node -- parse a JSON text into a node tree.
+  *
+  * Returns NULL if the input is not valid JSON.
+  */
+ static Json *
+ json_to_node(const char *str, int len)
+ {
+ 	return json_parse_internal(str, len, NULL);
+ }
+ 
+ /*
+  * json_validate -- validate a JSON text.
+  */
+ static bool
+ json_validate(const char *str, int len)
+ {
+ 	bool valid;
+ 	(void) json_parse_internal(str, len, &valid);
+ 	return valid;
+ }
+ 
+ /*
+  * json_to_array -- split a JSON array to array of JSON values.
+  */
+ Datum
+ json_to_array(PG_FUNCTION_ARGS)
+ {
+ 	jsontype	   *json = PG_GETARG_JSON_PP(0);
+ 	Json		   *node;
+ 	ListCell	   *cell;
+ 	int				nelems;
+ 	ArrayType	   *array;
+ 
+ 	/* This should be succeeded because it have been validated on input. */
+ 	node = json_to_node(VARDATA_ANY(json), VARSIZE_ANY_EXHDR(json));
+ 	if (node == NULL)
+ 		elog(ERROR, "unexpected json value");
+ 
+ 	/* json null to SQL NULL */
+ 	if (node->type == JSON_NULL)
+ 		PG_RETURN_NULL();
+ 
+ 	if (node->type != JSON_ARRAY)
+ 		elog(ERROR, "not a json array");
+ 
+ 	nelems = list_length(node->value.array);
+ 	if (nelems == 0)
+ 	{
+ 		/* fast path for empty array */
+ 		array = construct_empty_array(JSONOID);
+ 	}
+ 	else
+ 	{
+ 		Datum		   *elems;
+ 		StringInfoData	buf;
+ 		int				i;
+ 
+ 		elems = (Datum *) palloc(sizeof(Datum) * nelems);
+ 		initStringInfo(&buf);
+ 
+ 		i = 0;
+ 		foreach (cell, node->value.array)
+ 		{
+ 			Json *item = (Json *) lfirst(cell);
+ 
+ 			resetStringInfo(&buf);
+ 			append_json(&buf, item, NULL, 0);
+ 			elems[i++] = PointerGetDatum(
+ 							cstring_to_text_with_len(buf.data, buf.len));
+ 		}
+ 		array = construct_array(elems, nelems, JSONOID, -1, false, 'i');
+ 	}
+ 
+ 	PG_RETURN_ARRAYTYPE_P(array);
+ }
+ 
+ /*
+  * Helper for json_to_node and json_validate.
+  *
+  * If validate is NULL, this function returns a parse tree, or NULL on a
+  * parse error. If validate is not NULL, this function sets a boolean to the
+  * variable indicating whether the JSON was valid and always return NULL.
+  */
+ static Json *
+ json_parse_internal(const char *str, int len, bool *validate)
+ {
+ 	yyscan_t		scanner;
+ 	char		   *scanbuf;
+ 	JsonParser		parser;
+ 	int				rc;
+ 
+ 	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 = (validate != NULL);
+ 	if (!parser.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 */
+ 	rc = json_yyparse(scanner);
+ 
+ 	/* cleanup */
+ 	json_yylex_destroy(scanner);
+ 	pfree(scanbuf);
+ 	if (parser.buf.data)
+ 		pfree(parser.buf.data);
+ 
+ 	if (validate != NULL)
+ 		*validate = (rc == 0);
+ 
+ 	return rc == 0 ? parser.json : NULL;
+ }
+ 
+ /*
+  * append_json -- append JSON node tree to string with or without indentation
+  */
+ static void
+ append_json(StringInfo buf, const Json *node, const text *space, 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:
+ 		appendStringInfoChar(buf, '[');
+ 		if (space != NULL)
+ 			appendStringInfoChar(buf, '\n');
+ 		sep = false;
+ 		foreach (cell, node->value.array)
+ 		{
+ 			const Json *value = (const Json *) lfirst(cell);
+ 
+ 			if (sep)
+ 			{
+ 				appendStringInfoChar(buf, ',');
+ 				if (space != NULL)
+ 					appendStringInfoChar(buf, '\n');
+ 			}
+ 			else
+ 				sep = true;
+ 			append_indent(buf, space, indent + 1);
+ 			append_json(buf, value, space, indent + 1);
+ 		}
+ 		if (sep && space)
+ 			appendStringInfoChar(buf, '\n');
+ 		append_indent(buf, space, indent);
+ 		appendStringInfoChar(buf, ']');
+ 		break;
+ 	case JSON_OBJECT:
+ 		appendStringInfoChar(buf, '{');
+ 		if (space != NULL)
+ 			appendStringInfoChar(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)
+ 			{
+ 				appendStringInfoChar(buf, ',');
+ 				if (space != NULL)
+ 					appendStringInfoChar(buf, '\n');
+ 			}
+ 			else
+ 				sep = true;
+ 			append_indent(buf, space, indent + 1);
+ 			appendStringInfoString(buf, key);
+ 			appendStringInfoChar(buf, ':');
+ 			if (space != NULL)
+ 				appendStringInfoChar(buf, ' ');
+ 			append_json(buf, value, space, indent + 1);
+ 		}
+ 		if (sep && space != NULL)
+ 			appendStringInfoChar(buf, '\n');
+ 		append_indent(buf, space, indent);
+ 		appendStringInfoChar(buf, '}');
+ 		break;
+ 	default:
+ 		elog(ERROR, "unexpected json type: %d", node->type);
+ 	}
+ }
+ 
+ static void
+ append_indent(StringInfo buf, const text *space, int indent)
+ {
+ 	int		i;
+ 
+ 	if (space == NULL)
+ 		return;	/* do nothing */
+ 
+ 	for (i = 0; i < indent; i++)
+ 		appendBinaryStringInfo(buf,
+ 			VARDATA_ANY(space), VARSIZE_ANY_EXHDR(space));
+ }
+ 
+ #ifdef NOT_USED
+ /*
+  * 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);
+ }
+ #endif
+ 
+ void
+ json_yyerror(yyscan_t scanner, const char *error)
+ {
+ 	/*
+ 	 * No error report here. Instead, the caller will receives NULL node
+ 	 * tree on 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 ...dd3914d .
*** a/src/backend/utils/adt/json_parser.y
--- b/src/backend/utils/adt/json_parser.y
***************
*** 0 ****
--- 1,223 ----
+ %{
+ /*-------------------------------------------------------------------------
+  *
+  * 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
+  *
+  *
+  * IDENTIFICATION
+  *	  src/backend/utils/adt/json_parser.y
+  *
+  *-------------------------------------------------------------------------
+  */
+ 
+ #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 ...cdf2a6a .
*** a/src/backend/utils/adt/json_scanner.l
--- b/src/backend/utils/adt/json_scanner.l
***************
*** 0 ****
--- 1,92 ----
+ %{
+ /*-------------------------------------------------------------------------
+  *
+  * 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
+  *
+  *
+  * IDENTIFICATION
+  *	  src/backend/utils/adt/json_snanner.l
+  *
+  *-------------------------------------------------------------------------
+  */
+ 
+ #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]+)?([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>([^\"\\\x00-\x1F]+|\\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;
+ 	}
+ 		/* unterminated string */
+ <str>.|\n		{ return T_JSON_INVALID; }
+ <str><<EOF>>	{ return T_JSON_INVALID; }
+ 
+ 	/* Invalid */
+ . return T_JSON_INVALID;
diff --git a/src/include/catalog/pg_cast.h b/src/include/catalog/pg_cast.h
index 7e33b94..85b833f 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 61c6b27..76d95b7 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,4482 ----
  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_parse		PGNSP PGUID 12 1 0 0 f f f t f s 1 0 321 "25" _null_ _null_ _null_ _null_ json_parse _null_ _null_ _null_ ));
+ DESCR("parse a character string to produce a JSON value");
+ DATA(insert OID = 3831 (  json_is_well_formed PGNSP PGUID 12 1 0 0 f f f t f i 1 0 16 "25" _null_ _null_ _null_ _null_ json_is_well_formed _null_ _null_ _null_ ));
+ DESCR("determine if a string is well formed JSON");
+ DATA(insert OID = 3832 (  json_stringify	PGNSP PGUID 12 1 0 0 f f f t f i 1 0 25 "321" _null_ _null_ _null_ _null_ json_stringify _null_ _null_ _null_ ));
+ DESCR("serialize a JSON value to a character string");
+ DATA(insert OID = 3833 (  json_stringify	PGNSP PGUID 12 1 0 0 f f f f f i 2 0 25 "321 25" _null_ _null_ _null_ _null_ json_stringify_space _null_ _null_ _null_ ));
+ DESCR("serialize a JSON value to a character string with space option");
+ DATA(insert OID = 3834 (  json_to_array		PGNSP PGUID 12 1 0 0 f f f t f i 1 0 322 "321" _null_ _null_ _null_ _null_ json_to_array _null_ _null_ _null_ ));
+ DESCR("convert a JSON array to an array of JSON values");
+ 
  /* 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 fc2c306..6034cc3 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 ...55113b3 .
*** a/src/include/utils/json.h
--- b/src/include/utils/json.h
***************
*** 0 ****
--- 1,77 ----
+ /*-------------------------------------------------------------------------
+  *
+  * 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
+  *
+  * src/include/utils/json.h
+  *
+  *-------------------------------------------------------------------------
+  */
+ 
+ #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 json_parse(PG_FUNCTION_ARGS);
+ extern Datum json_is_well_formed(PG_FUNCTION_ARGS);
+ extern Datum json_stringify(PG_FUNCTION_ARGS);
+ extern Datum json_stringify_space(PG_FUNCTION_ARGS);
+ extern Datum json_to_array(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 ...fc87cd4 .
*** a/src/test/regress/expected/json.out
--- b/src/test/regress/expected/json.out
***************
*** 0 ****
--- 1,107 ----
+ CREATE TABLE jsontest (t text);
+ -- Control characters not allowed in JSON strings.
+ -- Can't test \x00 because it's not even a valid TEXT.
+ -- Although \x7F is a control character, the JSON RFC only says
+ -- "..., and the control characters (U+0000 through U+001F)".
+ -- However, whether to trust the RFC here or not is a tough call.
+ copy jsontest from stdin;
+ SELECT t, json_is_well_formed(t)::text FROM jsontest;
+                  t                 | json_is_well_formed 
+ -----------------------------------+---------------------
+                                    | false
+  null                              | true
+  {"booleans":[true,false]}         | true
+  0                                 | true
+  +0                                | false
+  -0                                | true
+  3                                 | true
+  -3                                | true
+  3.                                | false
+  3.1                               | true
+  .3                                | false
+  0.3                               | true
+  123.456e14234                     | true
+  123.456e-14234                    | true
+  123.456e+14234                    | true
+  0.3e                              | false
+  0.3e+                             | false
+  0.3e+5                            | true
+  0.3e-5                            | true
+  3e+1                              | true
+  5e5                               | true
+  5.e5                              | false
+  3.2e+1                            | true
+  3.2e+1.5                          | false
+  ""                                | true
+  "hello"                           | true
+  'hello'                           | false
+  hello                             | false
+  "hello                            | false
+  "hello"""                         | false
+  "\"hello\""                       | true
+  "newline                         +| false
+  "                                 | 
+  "\u00000"                         | true
+  "\u99999"                         | true
+  "\uDB00"                          | true
+  "\uDB00\uDBFF"                    | true
+  "\u1234\uD800"                    | true
+  "\ud800\udc00"                    | true
+  "\uFFFE\uFFFF"                    | true
+  "\UD834\UDD1E"                    | false
+  "\n\r\t\b"                        | true
+  "\n\r\t\v"                        | false
+  "\x01"                            | false
+  "\x1F"                            | false
+  "\x7F"                            | false
+  []                                | true
+  [,]                               | false
+  [,2]                              | false
+  [)                                | false
+  []]                               | false
+  [}                                | false
+  [1]                               | true
+  ["1":2]                           | false
+  [1,2,]                            | false
+  [1:2}                             | false
+  [3, [4, [5], 6]]                  | true
+  ["hello"]                         | true
+  ["hello", "bye", 3]               | true
+  {}                                | true
+  {]                                | false
+  {,}                               | false
+  {"1":{}}                          | true
+  {"1":2}                           | true
+  {"1":2,}                          | false
+  {1:2}                             | false
+  {"1":2, "3":4}                    | true
+  {"1":2, "3":4                     | false
+  {"1":2, "3" : [4, {}, ["5"]}      | false
+  {"no value"}                      | false
+  {"non-string key":null, 123:null} | false
+ (70 rows)
+ 
+ SELECT json_stringify('{"null":null, "boolean":true, "array":[1,2,3], "empty array":[], "empty object":{}}', '  ');
+    json_stringify    
+ ---------------------
+  {                  +
+    "null": null,    +
+    "boolean": true, +
+    "array": [       +
+      1,             +
+      2,             +
+      3              +
+    ],               +
+    "empty array": [ +
+    ],               +
+    "empty object": {+
+    }                +
+  }
+ (1 row)
+ 
+ SELECT json_to_array('[1, 2, 3, "string", [ "array" ], { "object":null }]');
+                      json_to_array                      
+ --------------------------------------------------------
+  {1,2,3,"\"string\"","[\"array\"]","{\"object\":null}"}
+ (1 row)
+ 
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 a05bfeb..286fd4b 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 e2f8351..d7d6e45 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 ...a7e283d .
*** a/src/test/regress/sql/json.sql
--- b/src/test/regress/sql/json.sql
***************
*** 0 ****
--- 1,85 ----
+ CREATE TABLE jsontest (t text);
+ 
+ -- Control characters not allowed in JSON strings.
+ -- Can't test \x00 because it's not even a valid TEXT.
+ -- Although \x7F is a control character, the JSON RFC only says
+ -- "..., and the control characters (U+0000 through U+001F)".
+ -- However, whether to trust the RFC here or not is a tough call.
+ 
+ copy jsontest from stdin;
+ 
+ null
+ {"booleans":[true,false]}
+ 0
+ +0
+ -0
+ 3
+ -3
+ 3.
+ 3.1
+ .3
+ 0.3
+ 123.456e14234
+ 123.456e-14234
+ 123.456e+14234
+ 0.3e
+ 0.3e+
+ 0.3e+5
+ 0.3e-5
+ 3e+1
+ 5e5
+ 5.e5
+ 3.2e+1
+ 3.2e+1.5
+ ""
+ "hello"
+ 'hello'
+ hello
+ "hello
+ "hello"""
+ "\\"hello\\""
+ "newline\n"
+ "\\u00000"
+ "\\u99999"
+ "\\uDB00"
+ "\\uDB00\\uDBFF"
+ "\\u1234\\uD800"
+ "\\ud800\\udc00"
+ "\\uFFFE\\uFFFF"
+ "\\UD834\\UDD1E"
+ "\\n\\r\\t\\b"
+ "\\n\\r\\t\\v"
+ "\\x01"
+ "\\x1F"
+ "\\x7F"
+ []
+ [,]
+ [,2]
+ [)
+ []]
+ [}
+ [1]
+ ["1":2]
+ [1,2,]
+ [1:2}
+ [3, [4, [5], 6]]
+ ["hello"]
+ ["hello", "bye", 3]
+ {}
+ {]
+ {,}
+ {"1":{}}
+ {"1":2}
+ {"1":2,}
+ {1:2}
+ {"1":2, "3":4}
+ {"1":2, "3":4
+ {"1":2, "3" : [4, {}, ["5"]}
+ {"no value"}
+ {"non-string key":null, 123:null}
+ \.
+ 
+ SELECT t, json_is_well_formed(t)::text FROM jsontest;
+ 
+ SELECT json_stringify('{"null":null, "boolean":true, "array":[1,2,3], "empty array":[], "empty object":{}}', '  ');
+ SELECT json_to_array('[1, 2, 3, "string", [ "array" ], { "object":null }]');
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