diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 59b8a2e..447f2c2 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -2427,6 +2427,15 @@ JumbleExpr(pgssJumbleState *jstate, Node *node)
 				JumbleExpr(jstate, (Node *) aref->refassgnexpr);
 			}
 			break;
+		case T_JsonbRef:
+			{
+				JsonbRef   *jbref = (JsonbRef *) node;
+
+				JumbleExpr(jstate, (Node *) jbref->refpathexpr);
+				JumbleExpr(jstate, (Node *) jbref->refexpr);
+				JumbleExpr(jstate, (Node *) jbref->refassgnexpr);
+			}
+			break;
 		case T_FuncExpr:
 			{
 				FuncExpr   *expr = (FuncExpr *) node;
diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c
index 81cb2b4..7809a1c 100644
--- a/contrib/postgres_fdw/deparse.c
+++ b/contrib/postgres_fdw/deparse.c
@@ -122,6 +122,7 @@ static void deparseVar(Var *node, deparse_expr_cxt *context);
 static void deparseConst(Const *node, deparse_expr_cxt *context);
 static void deparseParam(Param *node, deparse_expr_cxt *context);
 static void deparseArrayRef(ArrayRef *node, deparse_expr_cxt *context);
+static void deparseJsonbRef(JsonbRef *node, deparse_expr_cxt *context);
 static void deparseFuncExpr(FuncExpr *node, deparse_expr_cxt *context);
 static void deparseOpExpr(OpExpr *node, deparse_expr_cxt *context);
 static void deparseOperatorName(StringInfo buf, Form_pg_operator opform);
@@ -352,6 +353,38 @@ foreign_expr_walker(Node *node,
 					state = FDW_COLLATE_UNSAFE;
 			}
 			break;
+		case T_JsonbRef:
+			{
+				JsonbRef   *jb = (JsonbRef *) node;
+
+				/* Assignment should not be in restrictions. */
+				if (jb->refassgnexpr != NULL)
+					return false;
+
+				/*
+				 * Recurse to remaining subexpressions.
+				 */
+				if (!foreign_expr_walker((Node *) jb->refpathexpr,
+										 glob_cxt, &inner_cxt))
+					return false;
+				if (!foreign_expr_walker((Node *) jb->refexpr,
+										 glob_cxt, &inner_cxt))
+					return false;
+
+				/*
+				 * Jsonb subscripting should yield same collation as input,
+				 * but for safety use same logic as for function nodes.
+				 */
+				collation = jb->refcollid;
+				if (collation == InvalidOid)
+					state = FDW_COLLATE_NONE;
+				else if (inner_cxt.state == FDW_COLLATE_SAFE &&
+						 collation == inner_cxt.collation)
+					state = FDW_COLLATE_SAFE;
+				else
+					state = FDW_COLLATE_UNSAFE;
+			}
+			break;
 		case T_FuncExpr:
 			{
 				FuncExpr   *fe = (FuncExpr *) node;
@@ -1230,6 +1263,9 @@ deparseExpr(Expr *node, deparse_expr_cxt *context)
 		case T_ArrayRef:
 			deparseArrayRef((ArrayRef *) node, context);
 			break;
+		case T_JsonbRef:
+			deparseJsonbRef((JsonbRef *) node, context);
+			break;
 		case T_FuncExpr:
 			deparseFuncExpr((FuncExpr *) node, context);
 			break;
@@ -1493,6 +1529,45 @@ deparseArrayRef(ArrayRef *node, deparse_expr_cxt *context)
 }
 
 /*
+ * Deparse a jsonb subscript expression.
+ */
+static void
+deparseJsonbRef(JsonbRef *node, deparse_expr_cxt *context)
+{
+	StringInfo	buf = context->buf;
+	ListCell   *lowlist_item;
+	ListCell   *uplist_item;
+
+	/* Always parenthesize the expression. */
+	appendStringInfoChar(buf, '(');
+
+	/*
+	 * Deparse referenced jsonb expression first.  If that expression includes
+	 * a cast, we have to parenthesize to prevent the jsonb subscript from
+	 * being taken as typename decoration.  We can avoid that in the typical
+	 * case of subscripting a Var, but otherwise do it.
+	 */
+	if (IsA(node->refexpr, Var))
+		deparseExpr(node->refexpr, context);
+	else
+	{
+		appendStringInfoChar(buf, '(');
+		deparseExpr(node->refexpr, context);
+		appendStringInfoChar(buf, ')');
+	}
+
+	/* Deparse subscript expressions. */
+	foreach(uplist_item, node->refpathexpr)
+	{
+		appendStringInfoChar(buf, '[');
+		deparseExpr(lfirst(uplist_item), context);
+		appendStringInfoChar(buf, ']');
+	}
+
+	appendStringInfoChar(buf, ')');
+}
+
+/*
  * Deparse a function call.
  */
 static void
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 29f058c..ffcda22 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -57,12 +57,16 @@
 #include "utils/memutils.h"
 #include "utils/typcache.h"
 #include "utils/xml.h"
+#include "utils/jsonb.h"
 
 
 /* static function decls */
 static Datum ExecEvalArrayRef(ArrayRefExprState *astate,
 				 ExprContext *econtext,
 				 bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalJsonbRef(JsonbRefExprState *astate,
+				 ExprContext *econtext,
+				 bool *isNull, ExprDoneCond *isDone);
 static bool isAssignmentIndirectionExpr(ExprState *exprstate);
 static Datum ExecEvalAggref(AggrefExprState *aggref,
 			   ExprContext *econtext,
@@ -481,6 +485,134 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
 							   astate->refelemalign);
 }
 
+/*----------
+ *	  ExecEvalJsonbRef
+ *
+ *	   This function takes a JsonbRef and returns the extracted Datum
+ *	   if it's a simple reference, or the modified jsonb value if it's
+ *	   an assignment.
+ *
+ * NOTE: if we get a NULL result from a subscript expression, we return NULL
+ * when it's an array reference, or raise an error when it's an assignment.
+ *----------
+ */
+static Datum
+ExecEvalJsonbRef(JsonbRefExprState *jbstate,
+				 ExprContext *econtext,
+				 bool *isNull,
+				 ExprDoneCond *isDone)
+{
+	JsonbRef   *jsonbRef = (JsonbRef *) jbstate->xprstate.expr;
+	Datum		array_source;
+	bool		isAssignment = (jsonbRef->refassgnexpr != NULL);
+	bool		eisnull;
+	ListCell   *l;
+	int			i = 0;
+	text	   **path;
+
+	array_source = ExecEvalExpr(jbstate->refexpr,
+								econtext,
+								isNull,
+								isDone);
+
+	/*
+	 * If refexpr yields NULL, and it's a fetch, then result is NULL. In the
+	 * assignment case, we'll cons up something below.
+	 */
+	if (*isNull)
+	{
+		if (isDone && *isDone == ExprEndResult)
+			return (Datum) NULL;	/* end of set result */
+		if (!isAssignment)
+			return (Datum) NULL;
+	}
+
+	path = (text **) palloc(jbstate->refpathexpr->length * sizeof(text*));
+
+	foreach(l, jbstate->refpathexpr)
+	{
+		ExprState  *eltstate = (ExprState *) lfirst(l);
+
+		path[i++] = cstring_to_text((char *)DatumGetPointer(ExecEvalExpr(eltstate,
+					econtext,
+					&eisnull,
+					NULL)));
+
+		/* If any index expr yields NULL, result is NULL or error */
+		if (eisnull)
+		{
+			if (isAssignment)
+				ereport(ERROR,
+						(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+				  errmsg("array subscript in assignment must not be null")));
+			*isNull = true;
+			return (Datum) NULL;
+		}
+	}
+
+	if (isAssignment)
+	{
+		Datum		sourceData;
+		Datum		save_datum;
+		bool		save_isNull;
+
+		/*
+		 * We might have a nested-assignment situation, in which the
+		 * refassgnexpr is itself a FieldStore or ArrayRef that needs to
+		 * obtain and modify the previous value of the array element or slice
+		 * being replaced.  If so, we have to extract that value from the
+		 * array and pass it down via the econtext's caseValue.  It's safe to
+		 * reuse the CASE mechanism because there cannot be a CASE between
+		 * here and where the value would be needed, and an array assignment
+		 * can't be within a CASE either.  (So saving and restoring the
+		 * caseValue is just paranoia, but let's do it anyway.)
+		 *
+		 * Since fetching the old element might be a nontrivial expense, do it
+		 * only if the argument appears to actually need it.
+		 */
+		save_datum = econtext->caseValue_datum;
+		save_isNull = econtext->caseValue_isNull;
+
+		/*
+		 * Evaluate the value to be assigned into the array.
+		 */
+		sourceData = ExecEvalExpr(jbstate->refassgnexpr,
+								  econtext,
+								  &eisnull,
+								  NULL);
+
+		econtext->caseValue_datum = save_datum;
+		econtext->caseValue_isNull = save_isNull;
+
+		/*
+		 * For an assignment to a fixed-length array type, both the original
+		 * array and the value to be assigned into it must be non-NULL, else
+		 * we punt and return the original array.
+		 */
+		if (jbstate->refattrlength > 0)	/* fixed-length array? */
+			if (eisnull || *isNull)
+				return array_source;
+
+		/*
+		 * For assignment to varlena arrays, we handle a NULL original array
+		 * by substituting an empty (zero-dimensional) array; insertion of the
+		 * new element will result in a singleton array value.  It does not
+		 * matter whether the new element is NULL.
+		 */
+		if (*isNull)
+		{
+			array_source = PointerGetDatum(construct_empty_array(jsonbRef->refelemtype));
+			*isNull = false;
+		}
+
+		return jsonb_set_element(array_source, path, i, sourceData,
+				((const JsonbRef *) jbstate->xprstate.expr)->refelemtype);
+	}
+	else
+		return jsonb_get_element(array_source, path, i, isNull);
+}
+
+
 /*
  * Helper for ExecEvalArrayRef: is expr a nested FieldStore or ArrayRef
  * that might need the old element value passed down?
@@ -507,6 +639,14 @@ isAssignmentIndirectionExpr(ExprState *exprstate)
 		if (arrayRef->refexpr && IsA(arrayRef->refexpr, CaseTestExpr))
 			return true;
 	}
+	else if (IsA(exprstate, JsonbRefExprState))
+	{
+		JsonbRef   *jsonbRef = (JsonbRef *) exprstate->expr;
+
+		if (jsonbRef->refexpr && IsA(jsonbRef->refexpr, CaseTestExpr))
+			return true;
+	}
+
 	return false;
 }
 
@@ -4589,6 +4729,25 @@ ExecInitExpr(Expr *node, PlanState *parent)
 				state = (ExprState *) astate;
 			}
 			break;
+		case T_JsonbRef:
+			{
+				JsonbRef   *jbref = (JsonbRef *) node;
+				JsonbRefExprState *jbstate = makeNode(JsonbRefExprState);
+
+				jbstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalJsonbRef;
+				jbstate->refpathexpr = (List *)
+					ExecInitExpr((Expr *) jbref->refpathexpr, parent);
+				jbstate->refexpr = ExecInitExpr(jbref->refexpr, parent);
+				jbstate->refassgnexpr = ExecInitExpr(jbref->refassgnexpr,
+													parent);
+				/* do one-time catalog lookups for type info */
+				get_typlenbyvalalign(jbref->refelemtype,
+									 &jbstate->refelemlength,
+									 &jbstate->refelembyval,
+									 &jbstate->refelemalign);
+				state = (ExprState *) jbstate;
+			}
+			break;
 		case T_FuncExpr:
 			{
 				FuncExpr   *funcexpr = (FuncExpr *) node;
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 62355aa..5179077 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -1277,6 +1277,24 @@ _copyArrayRef(const ArrayRef *from)
 }
 
 /*
+ * _copyJsonbRef
+ */
+static JsonbRef *
+_copyJsonbRef(const JsonbRef *from)
+{
+	JsonbRef   *newnode = makeNode(JsonbRef);
+
+	COPY_SCALAR_FIELD(refelemtype);
+	COPY_SCALAR_FIELD(reftypmod);
+	COPY_SCALAR_FIELD(refcollid);
+	COPY_NODE_FIELD(refpathexpr);
+	COPY_NODE_FIELD(refexpr);
+	COPY_NODE_FIELD(refassgnexpr);
+
+	return newnode;
+}
+
+/*
  * _copyFuncExpr
  */
 static FuncExpr *
@@ -4361,6 +4379,8 @@ copyObject(const void *from)
 		case T_ArrayRef:
 			retval = _copyArrayRef(from);
 			break;
+		case T_JsonbRef:
+			retval = _copyJsonbRef(from);
 		case T_FuncExpr:
 			retval = _copyFuncExpr(from);
 			break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 8f16833..0bc06a8 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -255,6 +255,19 @@ _equalArrayRef(const ArrayRef *a, const ArrayRef *b)
 }
 
 static bool
+_equalJsonbRef(const JsonbRef *a, const JsonbRef *b)
+{
+	COMPARE_SCALAR_FIELD(refelemtype);
+	COMPARE_SCALAR_FIELD(reftypmod);
+	COMPARE_SCALAR_FIELD(refcollid);
+	COMPARE_NODE_FIELD(refpathexpr);
+	COMPARE_NODE_FIELD(refexpr);
+	COMPARE_NODE_FIELD(refassgnexpr);
+
+	return true;
+}
+
+static bool
 _equalFuncExpr(const FuncExpr *a, const FuncExpr *b)
 {
 	COMPARE_SCALAR_FIELD(funcid);
@@ -2730,6 +2743,9 @@ equal(const void *a, const void *b)
 		case T_ArrayRef:
 			retval = _equalArrayRef(a, b);
 			break;
+		case T_JsonbRef:
+			retval = _equalJsonbRef(a, b);
+			break;
 		case T_FuncExpr:
 			retval = _equalFuncExpr(a, b);
 			break;
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index 4a24474..d6b9d8a 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -76,6 +76,12 @@ exprType(const Node *expr)
 					type = arrayref->refelemtype;
 			}
 			break;
+		case T_JsonbRef:
+			{
+				/* store operations yield the jsonb type */
+				type = JSONBOID;
+			}
+			break;
 		case T_FuncExpr:
 			type = ((const FuncExpr *) expr)->funcresulttype;
 			break;
@@ -283,6 +289,9 @@ exprTypmod(const Node *expr)
 		case T_ArrayRef:
 			/* typmod is the same for array or element */
 			return ((const ArrayRef *) expr)->reftypmod;
+		case T_JsonbRef:
+			/* typmod is the same for array or element */
+			return ((const JsonbRef *) expr)->reftypmod;
 		case T_FuncExpr:
 			{
 				int32		coercedTypmod;
@@ -767,6 +776,9 @@ exprCollation(const Node *expr)
 		case T_ArrayRef:
 			coll = ((const ArrayRef *) expr)->refcollid;
 			break;
+		case T_JsonbRef:
+			coll = ((const JsonbRef *) expr)->refcollid;
+			break;
 		case T_FuncExpr:
 			coll = ((const FuncExpr *) expr)->funccollid;
 			break;
@@ -1006,6 +1018,9 @@ exprSetCollation(Node *expr, Oid collation)
 		case T_ArrayRef:
 			((ArrayRef *) expr)->refcollid = collation;
 			break;
+		case T_JsonbRef:
+			((JsonbRef *) expr)->refcollid = collation;
+			break;
 		case T_FuncExpr:
 			((FuncExpr *) expr)->funccollid = collation;
 			break;
@@ -1227,6 +1242,10 @@ exprLocation(const Node *expr)
 			/* just use array argument's location */
 			loc = exprLocation((Node *) ((const ArrayRef *) expr)->refexpr);
 			break;
+		case T_JsonbRef:
+			/* just use jsonb argument's location */
+			loc = exprLocation((Node *) ((const JsonbRef *) expr)->refexpr);
+			break;
 		case T_FuncExpr:
 			{
 				const FuncExpr *fexpr = (const FuncExpr *) expr;
@@ -1747,6 +1766,22 @@ expression_tree_walker(Node *node,
 					return true;
 			}
 			break;
+		case T_JsonbRef:
+			{
+				JsonbRef   *jbref = (JsonbRef *) node;
+
+				/* recurse directly for jsonb path list */
+				if (expression_tree_walker((Node *) jbref->refpathexpr,
+										   walker, context))
+					return true;
+
+				/* walker must see the refexpr and refassgnexpr, however */
+				if (walker(jbref->refexpr, context))
+					return true;
+				if (walker(jbref->refassgnexpr, context))
+					return true;
+			}
+			break;
 		case T_FuncExpr:
 			{
 				FuncExpr   *expr = (FuncExpr *) node;
@@ -2332,6 +2367,21 @@ expression_tree_mutator(Node *node,
 				return (Node *) newnode;
 			}
 			break;
+		case T_JsonbRef:
+			{
+				JsonbRef   *jsonbref = (JsonbRef *) node;
+				JsonbRef   *newnode;
+
+				FLATCOPY(newnode, jsonbref, JsonbRef);
+				MUTATE(newnode->refpathexpr, jsonbref->refpathexpr,
+					   List *);
+				MUTATE(newnode->refexpr, jsonbref->refexpr,
+					   Expr *);
+				MUTATE(newnode->refassgnexpr, jsonbref->refassgnexpr,
+					   Expr *);
+				return (Node *) newnode;
+			}
+			break;
 		case T_FuncExpr:
 			{
 				FuncExpr   *expr = (FuncExpr *) node;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index e1b49d5..0188172 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -1055,6 +1055,19 @@ _outArrayRef(StringInfo str, const ArrayRef *node)
 }
 
 static void
+_outJsonbRef(StringInfo str, const JsonbRef *node)
+{
+	WRITE_NODE_TYPE("JSONBREF");
+
+	WRITE_OID_FIELD(refelemtype);
+	WRITE_INT_FIELD(reftypmod);
+	WRITE_OID_FIELD(refcollid);
+	WRITE_NODE_FIELD(refpathexpr);
+	WRITE_NODE_FIELD(refexpr);
+	WRITE_NODE_FIELD(refassgnexpr);
+}
+
+static void
 _outFuncExpr(StringInfo str, const FuncExpr *node)
 {
 	WRITE_NODE_TYPE("FUNCEXPR");
@@ -3123,6 +3136,9 @@ _outNode(StringInfo str, const void *obj)
 			case T_ArrayRef:
 				_outArrayRef(str, obj);
 				break;
+			case T_JsonbRef:
+				_outJsonbRef(str, obj);
+				break;
 			case T_FuncExpr:
 				_outFuncExpr(str, obj);
 				break;
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index df55b76..0d7fd10 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -588,6 +588,24 @@ _readArrayRef(void)
 }
 
 /*
+ * _readJsonbRef
+ */
+static JsonbRef *
+_readJsonbRef(void)
+{
+	READ_LOCALS(JsonbRef);
+
+	READ_OID_FIELD(refelemtype);
+	READ_INT_FIELD(reftypmod);
+	READ_OID_FIELD(refcollid);
+	READ_NODE_FIELD(refpathexpr);
+	READ_NODE_FIELD(refexpr);
+	READ_NODE_FIELD(refassgnexpr);
+
+	READ_DONE();
+}
+
+/*
  * _readFuncExpr
  */
 static FuncExpr *
@@ -1424,6 +1442,8 @@ parseNodeString(void)
 		return_value = _readWindowFunc();
 	else if (MATCH("ARRAYREF", 8))
 		return_value = _readArrayRef();
+	else if (MATCH("JSONBREF", 8))
+		return_value = _readJsonbRef();
 	else if (MATCH("FUNCEXPR", 8))
 		return_value = _readFuncExpr();
 	else if (MATCH("NAMEDARGEXPR", 12))
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index f2c8551..734ec0d 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -1360,6 +1360,13 @@ contain_nonstrict_functions_walker(Node *node, void *context)
 			return true;
 		/* else fall through to check args */
 	}
+	if (IsA(node, JsonbRef))
+	{
+		/* jsonb assignment is nonstrict, but subscripting is strict */
+		if (((JsonbRef *) node)->refassgnexpr != NULL)
+			return true;
+		/* else fall through to check args */
+	}
 	if (IsA(node, FuncExpr))
 	{
 		FuncExpr   *expr = (FuncExpr *) node;
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index fa77ef1..1a4466e 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -470,13 +470,24 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
 	}
 	/* process trailing subscripts, if any */
 	if (subscripts)
-		result = (Node *) transformArraySubscripts(pstate,
-												   result,
-												   exprType(result),
-												   InvalidOid,
-												   exprTypmod(result),
-												   subscripts,
-												   NULL);
+	{
+		if (exprType(result) == JSONBOID)
+			result = (Node *) transformJsonbSubscripts(pstate,
+													   result,
+													   JSONBOID,
+													   exprTypmod(result),
+													   subscripts,
+													   NULL);
+		else
+			result = (Node *) transformArraySubscripts(pstate,
+													   result,
+													   exprType(result),
+													   InvalidOid,
+													   exprTypmod(result),
+													   subscripts,
+													   NULL);
+
+	}
 
 	return result;
 }
diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c
index 4130cbf..ae9b76d 100644
--- a/src/backend/parser/parse_node.c
+++ b/src/backend/parser/parse_node.c
@@ -259,6 +259,68 @@ transformArrayType(Oid *arrayType, int32 *arrayTypmod)
 }
 
 /*
+ * transformJsonbSubscripts()
+ *		Transform jsonb subscripting. This is used for both
+ *		jsonb fetch and jsonb assignment.
+ *
+ * In an jsonb fetch, we are given a source jsonb value and we produce an
+ * expression that represents the result of extracting a single jsonb element.
+ *
+ * In an jsonb assignment, we are given a destination jsonb value plus a
+ * source value that is to be assigned to a single element of that jsonb.
+ * We produce an expression that represents the new jsonb value
+ * with the source data.
+ *
+ * pstate		Parse state
+ * jsonbBase	Already-transformed expression for the jsonb as a whole
+ * elementType	OID of jsonb's element type
+ * jsonbTypMod	typmod for the jsonb (which is also typmod for the elements)
+ * indirection	Untransformed list of subscripts (must not be NIL)
+ * assignFrom	NULL for jsonb fetch, else transformed expression for source.
+ */
+
+JsonbRef *
+transformJsonbSubscripts(ParseState *pstate,
+						 Node *jsonbBase,
+						 Oid elementType,
+						 int32 jsonbTypMod,
+						 List *indirection,
+						 Node *assignFrom)
+{
+	List			*pathExpr = NIL;
+	ListCell		*idx;
+	JsonbRef		*jbref;
+
+	/*
+	 * Transform the subscript expressions.
+	 */
+	foreach(idx, indirection)
+	{
+		A_Indices  *ai = (A_Indices *) lfirst(idx);
+		Node	   *subexpr;
+
+		Assert(IsA(ai, A_Indices));
+		subexpr = transformExpr(pstate, ai->uidx, pstate->p_expr_kind);
+		pathExpr = lappend(pathExpr, subexpr);
+	}
+
+	/*
+	 * Ready to build the JsonbRef node.
+	 */
+	jbref = makeNode(JsonbRef);
+	jbref->refelemtype = elementType;
+	jbref->reftypmod = jsonbTypMod;
+	/* refcollid will be set by parse_collate.c */
+	jbref->refpathexpr = pathExpr;
+	jbref->refexpr = (Expr *) jsonbBase;
+	jbref->refassgnexpr = (Expr *) assignFrom;
+
+	return jbref;
+}
+
+
+
+/*
  * transformArraySubscripts()
  *		Transform array subscripting.  This is used for both
  *		array fetch and array assignment.
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 1b3fcd6..5be882c 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -45,6 +45,17 @@ static Node *transformAssignmentIndirection(ParseState *pstate,
 							   ListCell *indirection,
 							   Node *rhs,
 							   int location);
+
+static Node *transformJsonbAssignmentSubscripts(ParseState *pstate,
+							  Node *basenode,
+							  const char *targetName,
+							  int32 targetTypMod,
+							  Oid targetCollation,
+							  List *subscripts,
+							  ListCell *next_indirection,
+							  Node *rhs,
+							  int location);
+
 static Node *transformAssignmentSubscripts(ParseState *pstate,
 							  Node *basenode,
 							  const char *targetName,
@@ -744,27 +755,42 @@ transformAssignmentIndirection(ParseState *pstate,
 	if (subscripts)
 	{
 		/* recurse, and then return because we're done */
-		return transformAssignmentSubscripts(pstate,
-											 basenode,
-											 targetName,
-											 targetTypeId,
-											 targetTypMod,
-											 targetCollation,
-											 subscripts,
-											 isSlice,
-											 NULL,
-											 rhs,
-											 location);
+		if (exprType(basenode) == JSONBOID)
+			return transformJsonbAssignmentSubscripts(pstate,
+													  basenode,
+													  targetName,
+													  targetTypMod,
+													  targetCollation,
+													  subscripts,
+													  i,
+													  rhs,
+													  location);
+		else
+			return transformAssignmentSubscripts(pstate,
+												 basenode,
+												 targetName,
+												 targetTypeId,
+												 targetTypMod,
+												 targetCollation,
+												 subscripts,
+												 isSlice,
+												 NULL,
+												 rhs,
+												 location);
 	}
 
 	/* base case: just coerce RHS to match target type ID */
 
-	result = coerce_to_target_type(pstate,
-								   rhs, exprType(rhs),
-								   targetTypeId, targetTypMod,
-								   COERCION_ASSIGNMENT,
-								   COERCE_IMPLICIT_CAST,
-								   -1);
+	if (targetTypeId != InvalidOid)
+		result = coerce_to_target_type(pstate,
+									   rhs, exprType(rhs),
+									   targetTypeId, targetTypMod,
+									   COERCION_ASSIGNMENT,
+									   COERCE_IMPLICIT_CAST,
+									   -1);
+	else
+		result = rhs;
+
 	if (result == NULL)
 	{
 		if (targetIsArray)
@@ -793,6 +819,47 @@ transformAssignmentIndirection(ParseState *pstate,
 }
 
 /*
+ * helper for transformAssignmentIndirection: process jsonb assignment
+ */
+static Node *
+transformJsonbAssignmentSubscripts(ParseState *pstate,
+							  Node *basenode,
+							  const char *targetName,
+							  int32 targetTypMod,
+							  Oid targetCollation,
+							  List *subscripts,
+							  ListCell *next_indirection,
+							  Node *rhs,
+							  int location)
+{
+	Node	   *result;
+
+	Assert(subscripts != NIL);
+
+	/* recurse to create appropriate RHS for jsonb assign */
+	rhs = transformAssignmentIndirection(pstate,
+										 NULL,
+										 targetName,
+										 true,
+										 InvalidOid,
+										 targetTypMod,
+										 targetCollation,
+										 next_indirection,
+										 rhs,
+										 location);
+
+	/* process subscripts */
+	result = (Node *) transformJsonbSubscripts(pstate,
+											   basenode,
+											   exprType(rhs),
+											   targetTypMod,
+											   subscripts,
+											   rhs);
+
+	return result;
+}
+
+/*
  * helper for transformAssignmentIndirection: process array assignment
  */
 static Node *
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 1b8e7b0..93a2ea4 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -980,6 +980,14 @@ process_matched_tle(TargetEntry *src_tle,
 		aref->refexpr = (Expr *) prior_expr;
 		newexpr = (Node *) aref;
 	}
+	else if (IsA(src_expr, JsonbRef))
+	{
+		JsonbRef   *jbref = makeNode(JsonbRef);
+
+		memcpy(jbref, src_expr, sizeof(JsonbRef));
+		jbref->refexpr = (Expr *) prior_expr;
+		newexpr = (Node *) jbref;
+	}
 	else
 	{
 		elog(ERROR, "cannot happen");
@@ -1013,6 +1021,14 @@ get_assignment_input(Node *node)
 			return NULL;
 		return (Node *) aref->refexpr;
 	}
+	else if (IsA(node, JsonbRef))
+	{
+		JsonbRef   *jbref = (JsonbRef *) node;
+
+		if (jbref->refassgnexpr == NULL)
+			return NULL;
+		return (Node *) jbref->refexpr;
+	}
 	return NULL;
 }
 
diff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c
index f0f1651..707f5a9 100644
--- a/src/backend/utils/adt/jsonb.c
+++ b/src/backend/utils/adt/jsonb.c
@@ -1960,3 +1960,45 @@ jsonb_object_agg_finalfn(PG_FUNCTION_ARGS)
 
 	PG_RETURN_POINTER(out);
 }
+
+
+Datum
+jsonb_set_element(Datum jsonbdatum,
+		text **path, int path_len, Datum sourceData, Oid source_type)
+{
+	Jsonb				*jb = DatumGetJsonb(jsonbdatum);
+	JsonbInState		result;
+	JsonbTypeCategory	tcategory;
+	Oid					outfuncoid;
+	JsonbValue			newval;
+	JsonbParseState 	*state = NULL;
+	JsonbIterator 		*it;
+	JsonbValue 			*res = NULL;
+	int					i = 0;
+	bool				*path_nulls = palloc(path_len * sizeof(bool));
+
+	jsonb_categorize_type(source_type,
+						  &tcategory, &outfuncoid);
+	memset(&result, 0, sizeof(JsonbInState));
+	result.parseState = NULL;
+	datum_to_jsonb(sourceData, false, &result, tcategory, outfuncoid, false);
+
+	it = JsonbIteratorInit(&jb->root);
+
+	newval = *result.res;
+
+	if (newval.type == jbvArray && newval.val.array.rawScalar == true)
+	{
+		newval = newval.val.array.elems[0];
+	}
+
+	for(i = 0; i < path_len; i++)
+	{
+		path_nulls[i]= false;
+	}
+
+	res = setPath(&it, (Datum *) path, path_nulls, path_len, &state, 0,
+				  (void *)&newval, true, true);
+
+	PG_RETURN_JSONB(JsonbValueToJsonb(res));
+}
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index 154a883..f60eafd 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -33,6 +33,12 @@
 #include "utils/memutils.h"
 #include "utils/typcache.h"
 
+#define add_newval(state, newval, unpacked) \
+	if (unpacked) \
+		(void) pushJsonbValue(st, WJB_VALUE, (JsonbValue *)newval); \
+	else  \
+		addJsonbToParseState(st, (Jsonb *)newval);
+
 /* semantic action functions for json_object_keys */
 static void okeys_object_field_start(void *state, char *fname, bool isnull);
 static void okeys_array_start(void *state);
@@ -127,17 +133,13 @@ static JsonbValue *findJsonbValueFromContainerLen(JsonbContainer *container,
 /* functions supporting jsonb_delete, jsonb_set and jsonb_concat */
 static JsonbValue *IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
 			   JsonbParseState **state);
-static JsonbValue *setPath(JsonbIterator **it, Datum *path_elems,
-		bool *path_nulls, int path_len,
-		JsonbParseState **st, int level, Jsonb *newval,
-		bool create);
 static void setPathObject(JsonbIterator **it, Datum *path_elems,
 			  bool *path_nulls, int path_len, JsonbParseState **st,
 			  int level,
-			  Jsonb *newval, uint32 npairs, bool create);
+			  Jsonb *newval, bool unpacked, uint32 npairs, bool create);
 static void setPathArray(JsonbIterator **it, Datum *path_elems,
 			 bool *path_nulls, int path_len, JsonbParseState **st,
-			 int level, Jsonb *newval, uint32 nelems, bool create);
+			 int level, Jsonb *newval, bool unpacked, uint32 nelems, bool create);
 static void addJsonbToParseState(JsonbParseState **jbps, Jsonb *jb);
 
 /* state for json_object_keys */
@@ -3544,7 +3546,7 @@ jsonb_set(PG_FUNCTION_ARGS)
 	it = JsonbIteratorInit(&in->root);
 
 	res = setPath(&it, path_elems, path_nulls, path_len, &st,
-				  0, newval, create);
+				  0, newval, false, create);
 
 	Assert(res != NULL);
 
@@ -3588,7 +3590,7 @@ jsonb_delete_path(PG_FUNCTION_ARGS)
 
 	it = JsonbIteratorInit(&in->root);
 
-	res = setPath(&it, path_elems, path_nulls, path_len, &st, 0, NULL, false);
+	res = setPath(&it, path_elems, path_nulls, path_len, &st, 0, NULL, false, false);
 
 	Assert(res != NULL);
 
@@ -3715,10 +3717,10 @@ IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
  * does not exist. All path elements before the last must already exist
  * whether or not create is true, or nothing is done.
  */
-static JsonbValue *
+JsonbValue *
 setPath(JsonbIterator **it, Datum *path_elems,
 		bool *path_nulls, int path_len,
-		JsonbParseState **st, int level, Jsonb *newval, bool create)
+		JsonbParseState **st, int level, void *newval, bool unpacked, bool create)
 {
 	JsonbValue	v;
 	JsonbValue *res = NULL;
@@ -3731,7 +3733,7 @@ setPath(JsonbIterator **it, Datum *path_elems,
 		case WJB_BEGIN_ARRAY:
 			(void) pushJsonbValue(st, r, NULL);
 			setPathArray(it, path_elems, path_nulls, path_len, st, level,
-						 newval, v.val.array.nElems, create);
+						 newval, unpacked, v.val.array.nElems, create);
 			r = JsonbIteratorNext(it, &v, false);
 			Assert(r == WJB_END_ARRAY);
 			res = pushJsonbValue(st, r, NULL);
@@ -3740,7 +3742,7 @@ setPath(JsonbIterator **it, Datum *path_elems,
 		case WJB_BEGIN_OBJECT:
 			(void) pushJsonbValue(st, r, NULL);
 			setPathObject(it, path_elems, path_nulls, path_len, st, level,
-						  newval, v.val.object.nPairs, create);
+						  newval, unpacked, v.val.object.nPairs, create);
 			r = JsonbIteratorNext(it, &v, true);
 			Assert(r == WJB_END_OBJECT);
 			res = pushJsonbValue(st, r, NULL);
@@ -3763,9 +3765,9 @@ setPath(JsonbIterator **it, Datum *path_elems,
 static void
 setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
 			  int path_len, JsonbParseState **st, int level,
-			  Jsonb *newval, uint32 npairs, bool create)
+			  Jsonb *newval, bool unpacked, uint32 npairs, bool create)
 {
-	JsonbValue	v;
+	JsonbValue	v, newkey;
 	int			i;
 	JsonbValue	k;
 	bool		done = false;
@@ -3783,7 +3785,7 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
 		newkey.val.string.val = VARDATA_ANY(path_elems[level]);
 
 		(void) pushJsonbValue(st, WJB_KEY, &newkey);
-		addJsonbToParseState(st, newval);
+		add_newval(st, newval, unpacked);
 	}
 
 	for (i = 0; i < npairs; i++)
@@ -3803,7 +3805,7 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
 				if (newval != NULL)
 				{
 					(void) pushJsonbValue(st, WJB_KEY, &k);
-					addJsonbToParseState(st, newval);
+					add_newval(st, newval, unpacked);
 				}
 				done = true;
 			}
@@ -3811,7 +3813,7 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
 			{
 				(void) pushJsonbValue(st, r, &k);
 				setPath(it, path_elems, path_nulls, path_len,
-						st, level + 1, newval, create);
+						st, level + 1, newval, unpacked, create);
 			}
 		}
 		else
@@ -3825,7 +3827,7 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
 				newkey.val.string.val = VARDATA_ANY(path_elems[level]);
 
 				(void) pushJsonbValue(st, WJB_KEY, &newkey);
-				addJsonbToParseState(st, newval);
+				add_newval(st, newval, unpacked);
 			}
 
 			(void) pushJsonbValue(st, r, &k);
@@ -3857,7 +3859,7 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
 static void
 setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
 			 int path_len, JsonbParseState **st, int level,
-			 Jsonb *newval, uint32 nelems, bool create)
+			 Jsonb *newval, bool unpacked, uint32 nelems, bool create)
 {
 	JsonbValue	v;
 	int			idx,
@@ -3902,7 +3904,7 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
 	if ((idx == INT_MIN || nelems == 0) && create && (level == path_len - 1))
 	{
 		Assert(newval != NULL);
-		addJsonbToParseState(st, newval);
+		add_newval(st, newval, unpacked);
 		done = true;
 	}
 
@@ -3917,13 +3919,15 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
 			{
 				r = JsonbIteratorNext(it, &v, true);	/* skip */
 				if (newval != NULL)
-					addJsonbToParseState(st, newval);
+				{
+					add_newval(st, newval, unpacked);
+				}
 
 				done = true;
 			}
 			else
 				(void) setPath(it, path_elems, path_nulls, path_len,
-							   st, level + 1, newval, create);
+							   st, level + 1, newval, unpacked, create);
 		}
 		else
 		{
@@ -3950,9 +3954,46 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
 
 			if (create && !done && level == path_len - 1 && i == nelems - 1)
 			{
-				addJsonbToParseState(st, newval);
+				add_newval(st, newval, unpacked);
 			}
 
 		}
 	}
 }
+
+
+Datum
+jsonb_get_element(Datum jsonbdatum,
+		text **path, int path_len, bool *isNull)
+{
+	Jsonb	   *jb = DatumGetJsonb(jsonbdatum);
+	JsonbValue *v;
+	int			level = 1;
+
+	if (!JB_ROOT_IS_OBJECT(jb))
+	{
+		*isNull = true;
+		return (Datum) 0;
+	}
+
+	v = findJsonbValueFromContainerLen(&jb->root, JB_FOBJECT,
+									   VARDATA_ANY(path[0]),
+									   VARSIZE_ANY_EXHDR(path[0]));
+
+	while (v != NULL &&
+		   v->type == jbvBinary && level < path_len)
+	{
+		v = findJsonbValueFromContainerLen(v->val.binary.data, JB_FOBJECT,
+									       VARDATA_ANY(path[level]),
+									       VARSIZE_ANY_EXHDR(path[level]));
+		level++;
+	}
+
+	if (v != NULL && level == path_len)
+	{
+		PG_RETURN_JSONB(JsonbValueToJsonb(v));
+	}
+
+	*isNull = true;
+	return (Datum) 0;
+}
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 51391f6..8d51fa5 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -419,7 +419,7 @@ static void get_opclass_name(Oid opclass, Oid actual_datatype,
 				 StringInfo buf);
 static Node *processIndirection(Node *node, deparse_context *context,
 				   bool printit);
-static void printSubscripts(ArrayRef *aref, deparse_context *context);
+static void printSubscripts(Node *node, deparse_context *context);
 static char *get_relation_name(Oid relid);
 static char *generate_relation_name(Oid relid, List *namespaces);
 static char *generate_function_name(Oid funcid, int nargs,
@@ -5640,6 +5640,14 @@ get_update_query_targetlist_def(Query *query, List *targetList,
 						break;
 					expr = (Node *) aref->refassgnexpr;
 				}
+				else if (IsA(expr, JsonbRef))
+				{
+					JsonbRef   *jbref = (JsonbRef *) expr;
+
+					if (jbref->refassgnexpr == NULL)
+						break;
+					expr = (Node *) jbref->refassgnexpr;
+				}
 				else
 					break;
 			}
@@ -6643,6 +6651,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
 			return true;
 
 		case T_ArrayRef:
+		case T_JsonbRef:
 		case T_ArrayExpr:
 		case T_RowExpr:
 		case T_CoalesceExpr:
@@ -6759,6 +6768,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
 					}
 				case T_BoolExpr:		/* lower precedence */
 				case T_ArrayRef:		/* other separators */
+				case T_JsonbRef:		/* other separators */
 				case T_ArrayExpr:		/* other separators */
 				case T_RowExpr:	/* other separators */
 				case T_CoalesceExpr:	/* own parentheses */
@@ -6809,6 +6819,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
 						return true;	/* own parentheses */
 					}
 				case T_ArrayRef:		/* other separators */
+				case T_JsonbRef:		/* other separators */
 				case T_ArrayExpr:		/* other separators */
 				case T_RowExpr:	/* other separators */
 				case T_CoalesceExpr:	/* own parentheses */
@@ -7054,7 +7065,72 @@ get_rule_expr(Node *node, deparse_context *context,
 				else
 				{
 					/* Just an ordinary array fetch, so print subscripts */
-					printSubscripts(aref, context);
+					printSubscripts(node, context);
+				}
+			}
+			break;
+
+		case T_JsonbRef:
+			{
+				JsonbRef   *jbref = (JsonbRef *) node;
+				bool		need_parens;
+
+				/*
+				 * If the argument is a CaseTestExpr, we must be inside a
+				 * FieldStore, ie, we are assigning to an element of a jsonb
+				 * within a composite column.  Since we already punted on
+				 * displaying the FieldStore's target information, just punt
+				 * here too, and display only the assignment source
+				 * expression.
+				 */
+				if (IsA(jbref->refexpr, CaseTestExpr))
+				{
+					Assert(jbref->refassgnexpr);
+					get_rule_expr((Node *) jbref->refassgnexpr,
+								  context, showimplicit);
+					break;
+				}
+
+				/*
+				 * Parenthesize the argument unless it's a simple Var or a
+				 * FieldSelect.  (In particular, if it's another JsonbRef, we
+				 * *must* parenthesize to avoid confusion.)
+				 */
+				need_parens = !IsA(jbref->refexpr, Var) &&
+					!IsA(jbref->refexpr, FieldSelect);
+				if (need_parens)
+					appendStringInfoChar(buf, '(');
+				get_rule_expr((Node *) jbref->refexpr, context, showimplicit);
+				if (need_parens)
+					appendStringInfoChar(buf, ')');
+
+				/*
+				 * If there's a refassgnexpr, we want to print the node in the
+				 * format "jsonb[subscripts] := refassgnexpr".  This is not
+				 * legal SQL, so decompilation of INSERT or UPDATE statements
+				 * should always use processIndirection as part of the
+				 * statement-level syntax.  We should only see this when
+				 * EXPLAIN tries to print the targetlist of a plan resulting
+				 * from such a statement.
+				 */
+				if (jbref->refassgnexpr)
+				{
+					Node	   *refassgnexpr;
+
+					/*
+					 * Use processIndirection to print this node's subscripts
+					 * as well as any additional field selections or
+					 * subscripting in immediate descendants.  It returns the
+					 * RHS expr that is actually being "assigned".
+					 */
+					refassgnexpr = processIndirection(node, context, true);
+					appendStringInfoString(buf, " := ");
+					get_rule_expr(refassgnexpr, context, showimplicit);
+				}
+				else
+				{
+					/* Just an ordinary array fetch, so print subscripts */
+					printSubscripts(node, context);
 				}
 			}
 			break;
@@ -7240,7 +7316,7 @@ get_rule_expr(Node *node, deparse_context *context,
 				 * WRONG to not parenthesize a Var argument; simplicity is not
 				 * the issue here, having the right number of names is.
 				 */
-				need_parens = !IsA(arg, ArrayRef) &&!IsA(arg, FieldSelect);
+				need_parens = !IsA(arg, ArrayRef) &&!IsA(arg, JsonbRef) &&!IsA(arg, FieldSelect);
 				if (need_parens)
 					appendStringInfoChar(buf, '(');
 				get_rule_expr(arg, context, true);
@@ -9250,7 +9326,7 @@ processIndirection(Node *node, deparse_context *context, bool printit)
 			if (aref->refassgnexpr == NULL)
 				break;
 			if (printit)
-				printSubscripts(aref, context);
+				printSubscripts(node, context);
 
 			/*
 			 * We ignore refexpr since it should be an uninteresting reference
@@ -9258,6 +9334,22 @@ processIndirection(Node *node, deparse_context *context, bool printit)
 			 */
 			node = (Node *) aref->refassgnexpr;
 		}
+		else if (IsA(node, JsonbRef))
+		{
+			JsonbRef   *jbref = (JsonbRef *) node;
+
+			if (jbref->refassgnexpr == NULL)
+				break;
+			if (printit)
+				printSubscripts(node, context);
+
+			/*
+			 * We ignore refexpr since it should be an uninteresting reference
+			 * to the target column or subcolumn.
+			 */
+			node = (Node *) jbref->refassgnexpr;
+		}
+
 		else
 			break;
 	}
@@ -9266,24 +9358,40 @@ processIndirection(Node *node, deparse_context *context, bool printit)
 }
 
 static void
-printSubscripts(ArrayRef *aref, deparse_context *context)
+printSubscripts(Node *node, deparse_context *context)
 {
 	StringInfo	buf = context->buf;
-	ListCell   *lowlist_item;
-	ListCell   *uplist_item;
+	if (IsA(node, ArrayRef))
+	{
+		ArrayRef	*aref = (ArrayRef *) node;
+		ListCell	*lowlist_item;
+		ListCell	*uplist_item;
 
-	lowlist_item = list_head(aref->reflowerindexpr);	/* could be NULL */
-	foreach(uplist_item, aref->refupperindexpr)
+		lowlist_item = list_head(aref->reflowerindexpr);	/* could be NULL */
+		foreach(uplist_item, aref->refupperindexpr)
+		{
+			appendStringInfoChar(buf, '[');
+			if (lowlist_item)
+			{
+				get_rule_expr((Node *) lfirst(lowlist_item), context, false);
+				appendStringInfoChar(buf, ':');
+				lowlist_item = lnext(lowlist_item);
+			}
+			get_rule_expr((Node *) lfirst(uplist_item), context, false);
+			appendStringInfoChar(buf, ']');
+		}
+	}
+	else if (IsA(node, JsonbRef))
 	{
-		appendStringInfoChar(buf, '[');
-		if (lowlist_item)
+		JsonbRef	*jbref = (JsonbRef *) node;
+		ListCell	*path_item;
+
+		foreach(path_item, jbref->refpathexpr)
 		{
-			get_rule_expr((Node *) lfirst(lowlist_item), context, false);
-			appendStringInfoChar(buf, ':');
-			lowlist_item = lnext(lowlist_item);
+			appendStringInfoChar(buf, '[');
+			get_rule_expr((Node *) lfirst(path_item), context, false);
+			appendStringInfoChar(buf, ']');
 		}
-		get_rule_expr((Node *) lfirst(uplist_item), context, false);
-		appendStringInfoChar(buf, ']');
 	}
 }
 
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 4ae2f3e..f8397ed 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -662,6 +662,22 @@ typedef struct ArrayRefExprState
 } ArrayRefExprState;
 
 /* ----------------
+ *		JsonbRefExprState node
+ * ----------------
+ */
+typedef struct JsonbRefExprState
+{
+	ExprState	xprstate;
+	List	   *refpathexpr;	/* states for child nodes */
+	ExprState  *refexpr;
+	ExprState  *refassgnexpr;
+	int16		refattrlength;	/* typlen of jsonb type */
+	int16		refelemlength;	/* typlen of the jsonb element type */
+	bool		refelembyval;	/* is the element type pass-by-value? */
+	char		refelemalign;	/* typalign of the element type */
+} JsonbRefExprState;
+
+/* ----------------
  *		FuncExprState node
  *
  * Although named for FuncExpr, this is also used for OpExpr, DistinctExpr,
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 274480e..3a99da6 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -178,6 +178,7 @@ typedef enum NodeTag
 	T_FromExpr,
 	T_OnConflictExpr,
 	T_IntoClause,
+	T_JsonbRef,
 
 	/*
 	 * TAGS FOR EXPRESSION STATE NODES (execnodes.h)
@@ -192,6 +193,7 @@ typedef enum NodeTag
 	T_GroupingFuncExprState,
 	T_WindowFuncExprState,
 	T_ArrayRefExprState,
+	T_JsonbRefExprState,
 	T_FuncExprState,
 	T_ScalarArrayOpExprState,
 	T_BoolExprState,
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 60c1ca2..7e6d9d1 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -367,6 +367,27 @@ typedef struct ArrayRef
 								 * fetch */
 } ArrayRef;
 
+/* ----------------
+ *	JsonbRef: describes a jsonb subscripting operation
+ *
+ * A JsonbRef can describe fetching a single element from a jsonb
+ * and storing a single value into a jsonb.
+ * ----------------
+ */
+typedef struct JsonbRef
+{
+	Expr		xpr;
+	Oid			refelemtype;	/* type of the jsonb elements */
+	int32		reftypmod;		/* typmod of the jsonb (and elements too) */
+	Oid			refcollid;		/* OID of collation, or InvalidOid if none */
+	List	   *refpathexpr;	/* expressions that evaluate to jsonb path */
+	Expr	   *refexpr;		/* the expression that evaluates to a jsonb
+								 * value */
+	Expr	   *refassgnexpr;	/* expression for the source value, or NULL if
+								 * fetch */
+} JsonbRef;
+
+
 /*
  * CoercionContext - distinguishes the allowed set of type casts
  *
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index 5249945..c6957af 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -224,6 +224,12 @@ extern void cancel_parser_errposition_callback(ParseCallbackState *pcbstate);
 extern Var *make_var(ParseState *pstate, RangeTblEntry *rte, int attrno,
 		 int location);
 extern Oid	transformArrayType(Oid *arrayType, int32 *arrayTypmod);
+extern JsonbRef *transformJsonbSubscripts(ParseState *pstate,
+						 Node *arrayBase,
+						 Oid elementType,
+						 int32 arrayTypMod,
+						 List *indirection,
+						 Node *assignFrom);
 extern ArrayRef *transformArraySubscripts(ParseState *pstate,
 						 Node *arrayBase,
 						 Oid arrayType,
diff --git a/src/include/utils/jsonb.h b/src/include/utils/jsonb.h
index 3049a87..124b6b4 100644
--- a/src/include/utils/jsonb.h
+++ b/src/include/utils/jsonb.h
@@ -433,5 +433,11 @@ extern char *JsonbToCString(StringInfo out, JsonbContainer *in,
 extern char *JsonbToCStringIndent(StringInfo out, JsonbContainer *in,
 					 int estimated_len);
 
+extern Datum jsonb_set_element(Datum datum, text **path, int path_len, Datum sourceData, Oid source_type);
+extern Datum jsonb_get_element(Datum datum, text **path, int path_len, bool *isNull);
+extern JsonbValue *setPath(JsonbIterator **it, Datum *path_elems,
+		bool *path_nulls, int path_len,
+		JsonbParseState **st, int level, void *newval,
+		bool unpacked, bool create);
 
 #endif   /* __JSONB_H__ */
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index c73f20b..c1006e5 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -6312,6 +6312,20 @@ exec_simple_check_node(Node *node)
 				return TRUE;
 			}
 
+		case T_JsonbRef:
+			{
+				JsonbRef   *expr = (JsonbRef *) node;
+
+				if (!exec_simple_check_node((Node *) expr->refpathexpr))
+					return FALSE;
+				if (!exec_simple_check_node((Node *) expr->refexpr))
+					return FALSE;
+				if (!exec_simple_check_node((Node *) expr->refassgnexpr))
+					return FALSE;
+
+				return TRUE;
+			}
+
 		case T_FuncExpr:
 			{
 				FuncExpr   *expr = (FuncExpr *) node;
diff --git a/src/test/regress/expected/jsonb.out b/src/test/regress/expected/jsonb.out
index 82d1b69..986a371 100644
--- a/src/test/regress/expected/jsonb.out
+++ b/src/test/regress/expected/jsonb.out
@@ -3331,3 +3331,115 @@ select jsonb_set('[]','{-99}','{"foo":123}');
  [{"foo": 123}]
 (1 row)
 
+-- jsonb subscript
+select ('{"a": 1}'::jsonb)['a'];
+ jsonb 
+-------
+ 1
+(1 row)
+
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['b'];
+ jsonb 
+-------
+ "c"
+(1 row)
+
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d'];
+   jsonb   
+-----------
+ [1, 2, 3]
+(1 row)
+
+select ('{"a": 1}'::jsonb)['not_exist'];
+ jsonb 
+-------
+ 
+(1 row)
+
+select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1'];
+     jsonb     
+---------------
+ {"a2": "aaa"}
+(1 row)
+
+select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1']['a2'];
+ jsonb 
+-------
+ "aaa"
+(1 row)
+
+select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1']['a2']['a3'];
+ jsonb 
+-------
+ 
+(1 row)
+
+create TEMP TABLE test_jsonb_subscript (
+       id int,
+       test_json jsonb
+);
+insert into test_jsonb_subscript values
+(1, '{}'), -- empty jsonb
+(2, '{"key": "value"}'); -- jsonb with data
+-- update empty jsonb
+update test_jsonb_subscript set test_json['a'] = 1 where id = 1;
+select * from test_jsonb_subscript;
+ id |    test_json     
+----+------------------
+  2 | {"key": "value"}
+  1 | {"a": 1}
+(2 rows)
+
+-- update jsonb with some data
+update test_jsonb_subscript set test_json['a'] = 1 where id = 2;
+select * from test_jsonb_subscript;
+ id |        test_json         
+----+--------------------------
+  1 | {"a": 1}
+  2 | {"a": 1, "key": "value"}
+(2 rows)
+
+-- replace jsonb
+update test_jsonb_subscript set test_json['a'] = 'test';
+select * from test_jsonb_subscript;
+ id |           test_json           
+----+-------------------------------
+  1 | {"a": "test"}
+  2 | {"a": "test", "key": "value"}
+(2 rows)
+
+-- replace by object
+update test_jsonb_subscript set test_json['a'] = '{"b": 1}'::jsonb;
+select * from test_jsonb_subscript;
+ id |            test_json            
+----+---------------------------------
+  1 | {"a": {"b": 1}}
+  2 | {"a": {"b": 1}, "key": "value"}
+(2 rows)
+
+-- replace by array
+update test_jsonb_subscript set test_json['a'] = '[1, 2, 3]'::jsonb;
+select * from test_jsonb_subscript;
+ id |            test_json             
+----+----------------------------------
+  1 | {"a": [1, 2, 3]}
+  2 | {"a": [1, 2, 3], "key": "value"}
+(2 rows)
+
+-- use jsonb subscription in where clause
+select * from test_jsonb_subscript where test_json['key'] = '"value"';
+ id |            test_json             
+----+----------------------------------
+  2 | {"a": [1, 2, 3], "key": "value"}
+(1 row)
+
+select * from test_jsonb_subscript where test_json['key_doesnt_exists'] = '"value"';
+ id | test_json 
+----+-----------
+(0 rows)
+
+select * from test_jsonb_subscript where test_json['key'] = '"wrong_value"';
+ id | test_json 
+----+-----------
+(0 rows)
+
diff --git a/src/test/regress/sql/jsonb.sql b/src/test/regress/sql/jsonb.sql
index cb03ada..f48ff79 100644
--- a/src/test/regress/sql/jsonb.sql
+++ b/src/test/regress/sql/jsonb.sql
@@ -816,3 +816,46 @@ select jsonb_set('{}','{x}','{"foo":123}');
 select jsonb_set('[]','{0}','{"foo":123}');
 select jsonb_set('[]','{99}','{"foo":123}');
 select jsonb_set('[]','{-99}','{"foo":123}');
+
+-- jsonb subscript
+select ('{"a": 1}'::jsonb)['a'];
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['b'];
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d'];
+select ('{"a": 1}'::jsonb)['not_exist'];
+select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1'];
+select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1']['a2'];
+select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1']['a2']['a3'];
+
+create TEMP TABLE test_jsonb_subscript (
+       id int,
+       test_json jsonb
+);
+
+insert into test_jsonb_subscript values
+(1, '{}'), -- empty jsonb
+(2, '{"key": "value"}'); -- jsonb with data
+
+-- update empty jsonb
+update test_jsonb_subscript set test_json['a'] = 1 where id = 1;
+select * from test_jsonb_subscript;
+
+-- update jsonb with some data
+update test_jsonb_subscript set test_json['a'] = 1 where id = 2;
+select * from test_jsonb_subscript;
+
+-- replace jsonb
+update test_jsonb_subscript set test_json['a'] = 'test';
+select * from test_jsonb_subscript;
+
+-- replace by object
+update test_jsonb_subscript set test_json['a'] = '{"b": 1}'::jsonb;
+select * from test_jsonb_subscript;
+
+-- replace by array
+update test_jsonb_subscript set test_json['a'] = '[1, 2, 3]'::jsonb;
+select * from test_jsonb_subscript;
+
+-- use jsonb subscription in where clause
+select * from test_jsonb_subscript where test_json['key'] = '"value"';
+select * from test_jsonb_subscript where test_json['key_doesnt_exists'] = '"value"';
+select * from test_jsonb_subscript where test_json['key'] = '"wrong_value"';
