From 1cbb5fa876110e5d161884efe9f8e3e51fa08f24 Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Tue, 29 Oct 2019 02:18:48 +0300
Subject: [PATCH v28 5/5] Polymorphic subscripting

---
 src/backend/executor/execExpr.c       | 226 +++++++---
 src/backend/executor/execExprInterp.c |  87 +++-
 src/backend/utils/adt/arrayfuncs.c    |   4 +-
 src/backend/utils/adt/jsonb_util.c    |   6 +-
 src/backend/utils/adt/jsonfuncs.c     | 807 ++++++++++++++++++++++++++--------
 src/backend/utils/adt/jsonpath_exec.c |   2 +-
 src/include/executor/execExpr.h       |  26 +-
 src/include/nodes/subscripting.h      |  14 +-
 src/include/utils/jsonb.h             |   2 +-
 src/test/regress/expected/jsonb.out   | 233 +++++++++-
 src/test/regress/sql/jsonb.sql        |  80 ++++
 11 files changed, 1233 insertions(+), 254 deletions(-)

diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index 854b2e4..498e72e 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -2535,6 +2535,144 @@ ExecInitWholeRowVar(ExprEvalStep *scratch, Var *variable, ExprState *state)
 	}
 }
 
+static void
+ExecInitSubscriptExpr(ExprEvalStep *scratch, SubscriptingRefState *sbsrefstate,
+					  ExprState *state, Node *expr, int off, bool isupper,
+					  List **adjust_jumps)
+{
+	/* Each subscript is evaluated into subscriptvalue/subscriptnull */
+	ExecInitExprRec((Expr *) expr, state,
+					&sbsrefstate->subscriptvalue,
+					&sbsrefstate->subscriptnull);
+
+	/* ... and then SBSREF_SUBSCRIPT saves it into step's workspace */
+	scratch->opcode = EEOP_SBSREF_SUBSCRIPT;
+	scratch->d.sbsref_subscript.state = sbsrefstate;
+	scratch->d.sbsref_subscript.typid = exprType(expr);
+	scratch->d.sbsref_subscript.off = off;
+	scratch->d.sbsref_subscript.isupper = isupper;
+	scratch->d.sbsref_subscript.jumpdone = -1;	/* adjust later */
+	ExprEvalPushStep(state, scratch);
+
+	*adjust_jumps = lappend_int(*adjust_jumps, state->steps_len - 1);
+}
+
+/* Init subscript expressions. */
+static void
+ExecInitSubscript(ExprEvalStep *scratch, SubscriptingRefState *sbsrefstate,
+				  ExprState *state, Node *expr, int i, bool isupper,
+				  List **adjust_jumps, bool *isprovided, Oid *exprtype)
+{
+	List	   *exprs = NULL;
+	int			nexprs = 0;
+	int			select_step;
+
+	/* When slicing, individual subscript bounds can be omitted */
+	*isprovided = expr != NULL;
+	if (!*isprovided)
+		return;
+
+	/*
+	 * Node can be a list of expression variants.  The first variant is
+	 * an unmodified expression, other variants can be NULL, so we need
+	 * to check if there are any non-NULL and emit SELECTEXPR if any.
+	 */
+	if (IsA(expr, List))
+	{
+		exprs = (List *) expr;
+		expr = linitial(exprs);
+
+		if (list_length(exprs) > 1)
+		{
+			ListCell   *lc = list_head(exprs);
+
+			while ((lc = lnext(exprs, lc)))
+			{
+				if (lfirst(lc))
+				{
+					nexprs = list_length(exprs) - 1;
+					break;
+				}
+			}
+		}
+	}
+
+	*exprtype = exprType(expr);
+
+	/* Emit SELECTEXPR step if there are expression variants */
+	if (nexprs)
+	{
+		scratch->opcode = EEOP_SBSREF_SELECTEXPR;
+		scratch->d.sbsref_selectexpr.state = sbsrefstate;
+		scratch->d.sbsref_selectexpr.off = i;
+		scratch->d.sbsref_selectexpr.isupper = isupper;
+		scratch->d.sbsref_selectexpr.nexprs = nexprs;
+		scratch->d.sbsref_selectexpr.exprtypes = palloc(sizeof(Oid) * nexprs);
+		scratch->d.sbsref_selectexpr.jumpdones = palloc(sizeof(int) * nexprs);
+		ExprEvalPushStep(state, scratch);
+		select_step = state->steps_len - 1;
+	}
+
+	/* Emit main expression */
+	ExecInitSubscriptExpr(scratch, sbsrefstate, state, expr, i, isupper,
+						  adjust_jumps);
+
+	/* Emit additional expression variants, if any */
+	if (nexprs)
+	{
+		ListCell   *lc = list_head(exprs);
+		List	   *adjust_subexpr_jumps = NIL;
+		int			j = 0;
+
+		/* Skip first expression which is already emitted */
+		while ((lc = lnext(exprs, lc)))
+		{
+			int			jumpdone;
+			Oid			exprtype;
+			ExprEvalStep *step;
+
+			expr = lfirst(lc);
+
+			if (expr)
+			{
+				/* Emit JUMP to the end for previous expression */
+				scratch->opcode = EEOP_JUMP;
+				scratch->d.jump.jumpdone = -1; /* adjust later */
+				ExprEvalPushStep(state, scratch);
+
+				adjust_subexpr_jumps = lappend_int(adjust_subexpr_jumps,
+												   state->steps_len - 1);
+
+				exprtype = exprType((Node *) expr);
+				jumpdone = state->steps_len;
+
+				ExecInitSubscriptExpr(scratch, sbsrefstate, state, expr,
+									  i, isupper, adjust_jumps);
+			}
+			else
+			{
+				exprtype = InvalidOid;
+				jumpdone = -1;
+			}
+
+			step = &state->steps[select_step];
+			step->d.sbsref_selectexpr.exprtypes[j] = exprtype;
+			step->d.sbsref_selectexpr.jumpdones[j] = jumpdone;
+
+			j++;
+		}
+
+		/* Adjust JUMPs for expression variants */
+		foreach(lc, adjust_subexpr_jumps)
+		{
+			ExprEvalStep *step = &state->steps[lfirst_int(lc)];
+
+			Assert(step->opcode == EEOP_JUMP);
+			step->d.jump.jumpdone = state->steps_len;
+		}
+	}
+}
+
 /*
  * Prepare evaluation of a SubscriptingRef expression.
  */
@@ -2542,12 +2680,14 @@ static void
 ExecInitSubscriptingRef(ExprEvalStep *scratch, SubscriptingRef *sbsref,
 						ExprState *state, Datum *resv, bool *resnull)
 {
-	bool		isAssignment = (sbsref->refassgnexpr != NULL);
 	SubscriptingRefState *sbsrefstate = palloc0(sizeof(SubscriptingRefState));
-	List				 *adjust_jumps = NIL;
-	ListCell   			 *lc;
-	int		   			  i;
-	RegProcedure		  typsubshandler = get_typsubsprocs(sbsref->refcontainertype);
+	List	   *adjust_jumps = NIL;
+	ListCell   *lc;
+	ListCell   *ulc;
+	ListCell   *llc;
+	int			i;
+	RegProcedure typsubshandler = get_typsubsprocs(sbsref->refcontainertype);
+	bool		isAssignment = (sbsref->refassgnexpr != NULL);
 
 	/* Fill constant fields of SubscriptingRefState */
 	sbsrefstate->isassignment = isAssignment;
@@ -2577,71 +2717,43 @@ ExecInitSubscriptingRef(ExprEvalStep *scratch, SubscriptingRef *sbsref,
 								   state->steps_len - 1);
 	}
 
-	/* Evaluate upper subscripts */
-	i = 0;
-	foreach(lc, sbsref->refupperindexpr)
+	/* Emit INIT step if needed. */
+	if (sbsrefstate->sbsroutines->init)
 	{
-		Expr	   *e = (Expr *) lfirst(lc);
-
-		/* When slicing, individual subscript bounds can be omitted */
-		if (!e)
-		{
-			sbsrefstate->upperprovided[i] = false;
-			i++;
-			continue;
-		}
-
-		sbsrefstate->upperprovided[i] = true;
-
-		/* Each subscript is evaluated into subscriptvalue/subscriptnull */
-		ExecInitExprRec(e, state,
-						&sbsrefstate->subscriptvalue, &sbsrefstate->subscriptnull);
-
-		/* ... and then SBSREF_SUBSCRIPT saves it into step's workspace */
-		scratch->opcode = EEOP_SBSREF_SUBSCRIPT;
-		scratch->d.sbsref_subscript.state = sbsrefstate;
-		scratch->d.sbsref_subscript.off = i;
-		scratch->d.sbsref_subscript.isupper = true;
-		scratch->d.sbsref_subscript.jumpdone = -1;	/* adjust later */
+		scratch->opcode = EEOP_SBSREF_INIT;
+		scratch->d.sbsref.state = sbsrefstate;
 		ExprEvalPushStep(state, scratch);
-		adjust_jumps = lappend_int(adjust_jumps,
-								   state->steps_len - 1);
-		i++;
 	}
-	sbsrefstate->numupper = i;
 
-	/* Evaluate lower subscripts similarly */
+	/* Evaluate upper and lower subscripts */
 	i = 0;
-	foreach(lc, sbsref->reflowerindexpr)
+	llc = list_head(sbsref->reflowerindexpr);
+
+	sbsrefstate->numlower = 0;
+
+	foreach(ulc, sbsref->refupperindexpr)
 	{
-		Expr	   *e = (Expr *) lfirst(lc);
+		ExecInitSubscript(scratch, sbsrefstate, state, lfirst(ulc), i,
+						  true, &adjust_jumps,
+						  &sbsrefstate->upperprovided[i],
+						  &sbsrefstate->uppertypid[i]);
 
-		/* When slicing, individual subscript bounds can be omitted */
-		if (!e)
+		if (llc)
 		{
-			sbsrefstate->lowerprovided[i] = false;
-			i++;
-			continue;
-		}
+			ExecInitSubscript(scratch, sbsrefstate, state, lfirst(llc),
+							  i, false, &adjust_jumps,
+							  &sbsrefstate->lowerprovided[i],
+							  &sbsrefstate->lowertypid[i]);
 
-		sbsrefstate->lowerprovided[i] = true;
+			llc = lnext(sbsref->reflowerindexpr, llc);
 
-		/* Each subscript is evaluated into subscriptvalue/subscriptnull */
-		ExecInitExprRec(e, state,
-						&sbsrefstate->subscriptvalue, &sbsrefstate->subscriptnull);
+			sbsrefstate->numlower++;
+		}
 
-		/* ... and then SBSREF_SUBSCRIPT saves it into step's workspace */
-		scratch->opcode = EEOP_SBSREF_SUBSCRIPT;
-		scratch->d.sbsref_subscript.state = sbsrefstate;
-		scratch->d.sbsref_subscript.off = i;
-		scratch->d.sbsref_subscript.isupper = false;
-		scratch->d.sbsref_subscript.jumpdone = -1;	/* adjust later */
-		ExprEvalPushStep(state, scratch);
-		adjust_jumps = lappend_int(adjust_jumps,
-								   state->steps_len - 1);
 		i++;
 	}
-	sbsrefstate->numlower = i;
+
+	sbsrefstate->numupper = i;
 
 	/* Should be impossible if parser is sane, but check anyway: */
 	if (sbsrefstate->numlower != 0 &&
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 18b9a70..43de626 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -411,6 +411,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 		&&CASE_EEOP_FIELDSELECT,
 		&&CASE_EEOP_FIELDSTORE_DEFORM,
 		&&CASE_EEOP_FIELDSTORE_FORM,
+		&&CASE_EEOP_SBSREF_INIT,
+		&&CASE_EEOP_SBSREF_SELECTEXPR,
 		&&CASE_EEOP_SBSREF_SUBSCRIPT,
 		&&CASE_EEOP_SBSREF_OLD,
 		&&CASE_EEOP_SBSREF_ASSIGN,
@@ -1388,6 +1390,29 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 			EEO_NEXT();
 		}
 
+		EEO_CASE(EEOP_SBSREF_INIT)
+		{
+			/* too complex for an inline implementation */
+			ExecEvalSubscriptingRefInit(state, op);
+
+			EEO_NEXT();
+		}
+
+		EEO_CASE(EEOP_SBSREF_SELECTEXPR)
+		{
+			/* too complex for an inline implementation */
+			int			selectedExpr = ExecEvalSubscriptingRefSelect(state, op);
+
+			/*
+			 * Jump to selected expression variant or simply continue
+			 * to the first (0th) expression
+			 */
+			if (selectedExpr > 0)
+				EEO_JUMP(op->d.sbsref_selectexpr.jumpdones[selectedExpr - 1]);
+			else
+				EEO_NEXT();
+		}
+
 		EEO_CASE(EEOP_SBSREF_SUBSCRIPT)
 		{
 			/* Process an array subscript */
@@ -3146,6 +3171,46 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 }
 
 /*
+ * Initialize subscripting state.
+ */
+void
+ExecEvalSubscriptingRefInit(ExprState *state, ExprEvalStep *op)
+{
+	SubscriptingRefState *sbsrefstate = op->d.sbsref_selectexpr.state;
+	SubscriptRoutines	 *sbsroutines = sbsrefstate->sbsroutines;
+
+	/* init private subsripting state */
+	sbsroutines->init(sbsrefstate, *op->resvalue, *op->resnull);
+}
+
+/*
+ * Select expression variant for subscript evaluation
+ */
+int
+ExecEvalSubscriptingRefSelect(ExprState *state, ExprEvalStep *op)
+{
+	SubscriptingRefState *sbsrefstate = op->d.sbsref_selectexpr.state;
+	SubscriptRoutines *sbsroutines = sbsrefstate->sbsroutines;
+	Oid		   *typids = op->d.sbsref_selectexpr.isupper ?
+		sbsrefstate->uppertypid : sbsrefstate->lowertypid;
+	int			off = op->d.sbsref_selectexpr.off;
+	Oid		   *exprtypes = op->d.sbsref_selectexpr.exprtypes;
+	Oid			typid = typids[off];
+	int			selected;
+
+	selected = sbsroutines->selectexpr(sbsrefstate, off, typid, exprtypes,
+									   op->d.sbsref_selectexpr.nexprs);
+
+	if (selected)
+	{
+		Assert(OidIsValid(exprtypes[selected]));
+		typids[off] = exprtypes[selected - 1];
+	}
+
+	return selected;
+}
+
+/*
  * Process a subscript in a SubscriptingRef expression.
  *
  * If subscript is NULL, throw error in assignment case, or in fetch case
@@ -3160,8 +3225,10 @@ bool
 ExecEvalSubscriptingRef(ExprState *state, ExprEvalStep *op)
 {
 	SubscriptingRefState *sbsrefstate = op->d.sbsref_subscript.state;
-	Datum				 *indexes;
-	int					 off;
+	SubscriptRoutines *sbsroutines = sbsrefstate->sbsroutines;
+	Datum	   *indexes;
+	int			off;
+	bool		isupper;
 
 	/* If any index expr yields NULL, result is NULL or error */
 	if (sbsrefstate->subscriptnull)
@@ -3174,15 +3241,24 @@ ExecEvalSubscriptingRef(ExprState *state, ExprEvalStep *op)
 		return false;
 	}
 
-	/* Convert datum to int, save in appropriate place */
-	if (op->d.sbsref_subscript.isupper)
+	off = op->d.sbsref_subscript.off;
+	isupper = op->d.sbsref_subscript.isupper;
+
+	/* Save converted datum in appropriate place */
+	if (isupper)
 		indexes = sbsrefstate->upperindex;
 	else
 		indexes = sbsrefstate->lowerindex;
-	off = op->d.sbsref_subscript.off;
 
 	indexes[off] = sbsrefstate->subscriptvalue;
 
+	if (sbsroutines->step &&
+		!sbsroutines->step(sbsrefstate, off, isupper))
+	{
+		*op->resnull = true;
+		return false;
+	}
+
 	return true;
 }
 
@@ -3200,7 +3276,6 @@ ExecEvalSubscriptingRefFetch(ExprState *state, ExprEvalStep *op)
 	/* Should not get here if source container (or any subscript) is null */
 	Assert(!(*op->resnull));
 
-
 	*op->resvalue = sbsroutines->fetch(*op->resvalue, sbsrefstate);
 	*op->resnull = sbsrefstate->resnull;
 }
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index fa0c33d..e1d5aed 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -6729,7 +6729,7 @@ Datum
 array_subscript_handler(PG_FUNCTION_ARGS)
 {
 	SubscriptRoutines *sbsroutines = (SubscriptRoutines *)
-									 palloc(sizeof(SubscriptRoutines));
+									 palloc0(sizeof(SubscriptRoutines));
 
 	sbsroutines->prepare = array_subscript_prepare;
 	sbsroutines->validate = array_subscript_validate;
@@ -6863,7 +6863,7 @@ array_subscript_validate(bool isAssignment, SubscriptingRef *sbsref,
 					(errcode(ERRCODE_DATATYPE_MISMATCH),
 					 errmsg("array assignment requires type %s"
 							" but expression is of type %s",
-							format_type_be(sbsref->refelemtype),
+							format_type_be(typeneeded),
 							format_type_be(typesource)),
 				 errhint("You will need to rewrite or cast the expression."),
 					 parser_errposition(pstate, exprLocation(assignExpr))));
diff --git a/src/backend/utils/adt/jsonb_util.c b/src/backend/utils/adt/jsonb_util.c
index 7be928f..6866902 100644
--- a/src/backend/utils/adt/jsonb_util.c
+++ b/src/backend/utils/adt/jsonb_util.c
@@ -468,9 +468,8 @@ getKeyJsonValueFromContainer(JsonbContainer *container,
  * Returns palloc()'d copy of the value, or NULL if it does not exist.
  */
 JsonbValue *
-getIthJsonbValueFromContainer(JsonbContainer *container, uint32 i)
+getIthJsonbValueFromContainer(JsonbContainer *container, uint32 i, JsonbValue *result)
 {
-	JsonbValue *result;
 	char	   *base_addr;
 	uint32		nelements;
 
@@ -483,7 +482,8 @@ getIthJsonbValueFromContainer(JsonbContainer *container, uint32 i)
 	if (i >= nelements)
 		return NULL;
 
-	result = palloc(sizeof(JsonbValue));
+	if (!result)
+		result = palloc(sizeof(JsonbValue));
 
 	fillJsonbValue(container, i, base_addr,
 				   getJsonbOffset(container, i),
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index ca9e134..6448c78 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -332,6 +332,23 @@ typedef struct JsObject
 			hash_destroy((jso)->val.json_hash); \
 	} while (0)
 
+/* state for assignment of a single subscript */
+typedef struct JsonbSubscriptState
+{
+	bool		exists;			/* does this element exist? */
+	bool		is_array;		/* is it array or object? */
+	int			array_size;		/* size of array */
+	int			array_index;	/* index in array (negative means prepending) */
+} JsonbSubscriptState;
+
+/* state for subscript assignment */
+typedef struct JsonbAssignState
+{
+	JsonbParseState *ps;		/* jsonb building state */
+	JsonbIterator *iter;		/* source jsonb iterator */
+	JsonbSubscriptState subscripts[MAX_SUBSCRIPT_DEPTH + 1]; /* per-subscript states */
+} JsonbAssignState;
+
 /* 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);
@@ -461,8 +478,6 @@ static Datum populate_domain(DomainIOData *io, Oid typid, const char *colname,
 /* functions supporting jsonb_delete, jsonb_set and jsonb_concat */
 static JsonbValue *IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
 								  JsonbParseState **state);
-static Datum jsonb_set_element(Datum datum, Datum *path, int path_len,
-							   Datum sourceData, Oid source_type, bool is_null);
 static Datum jsonb_get_element(Jsonb *jb, Datum *path, int npath,
 							   bool *isnull, bool as_text);
 static JsonbValue *setPath(JsonbIterator **it, Datum *path_elems,
@@ -471,12 +486,13 @@ static JsonbValue *setPath(JsonbIterator **it, Datum *path_elems,
 						   int op_type);
 static void setPathObject(JsonbIterator **it, Datum *path_elems,
 						  bool *path_nulls, int path_len, JsonbParseState **st,
-						  int level,
-						  JsonbValue *newval, uint32 npairs, int op_type);
+						  int level, JsonbValue *newval, int op_type);
 static void setPathArray(JsonbIterator **it, Datum *path_elems,
 						 bool *path_nulls, int path_len, JsonbParseState **st,
 						 int level,
 						 JsonbValue *newval, uint32 nelems, int op_type);
+static bool copyJsonbObject(JsonbParseState **st, JsonbIterator **it,
+							const JsonbValue *key);
 
 /* function supporting iterate_json_values */
 static void iterate_values_scalar(void *state, char *token, JsonTokenType tokentype);
@@ -804,6 +820,7 @@ jsonb_array_element(PG_FUNCTION_ARGS)
 	Jsonb	   *jb = PG_GETARG_JSONB_P(0);
 	int			element = PG_GETARG_INT32(1);
 	JsonbValue *v;
+	JsonbValue	vbuf;
 
 	if (!JB_ROOT_IS_ARRAY(jb))
 		PG_RETURN_NULL();
@@ -819,7 +836,7 @@ jsonb_array_element(PG_FUNCTION_ARGS)
 			element += nelements;
 	}
 
-	v = getIthJsonbValueFromContainer(&jb->root, element);
+	v = getIthJsonbValueFromContainer(&jb->root, element, &vbuf);
 	if (v != NULL)
 		PG_RETURN_JSONB_P(JsonbValueToJsonb(v));
 
@@ -847,6 +864,7 @@ jsonb_array_element_text(PG_FUNCTION_ARGS)
 	Jsonb	   *jb = PG_GETARG_JSONB_P(0);
 	int			element = PG_GETARG_INT32(1);
 	JsonbValue *v;
+	JsonbValue	vbuf;
 
 	if (!JB_ROOT_IS_ARRAY(jb))
 		PG_RETURN_NULL();
@@ -862,7 +880,7 @@ jsonb_array_element_text(PG_FUNCTION_ARGS)
 			element += nelements;
 	}
 
-	v = getIthJsonbValueFromContainer(&jb->root, element);
+	v = getIthJsonbValueFromContainer(&jb->root, element, &vbuf);
 
 	if (v != NULL && v->type != jbvNull)
 		PG_RETURN_TEXT_P(JsonbValueAsText(v));
@@ -882,6 +900,26 @@ json_extract_path_text(PG_FUNCTION_ARGS)
 	return get_path_all(fcinfo, true);
 }
 
+static inline bool
+jsonb_get_array_index_from_cstring(char *indexstr, long *index)
+{
+	char	   *endptr;
+
+	errno = 0;
+	*index = strtol(indexstr, &endptr, 10);
+	if (endptr == indexstr || *endptr != '\0' || errno != 0 ||
+		*index > INT_MAX || *index < INT_MIN)
+		return false;
+
+	return true;
+}
+
+static inline bool
+jsonb_get_array_index_from_text(Datum indextext, long *index)
+{
+	return jsonb_get_array_index_from_cstring(TextDatumGetCString(indextext), index);
+}
+
 /*
  * common routine for extract_path functions
  */
@@ -927,11 +965,8 @@ get_path_all(FunctionCallInfo fcinfo, bool as_text)
 		if (*tpath[i] != '\0')
 		{
 			long		ind;
-			char	   *endptr;
 
-			errno = 0;
-			ind = strtol(tpath[i], &endptr, 10);
-			if (*endptr == '\0' && errno == 0 && ind <= INT_MAX && ind >= INT_MIN)
+			if (jsonb_get_array_index_from_cstring(tpath[i], &ind))
 				ipath[i] = (int) ind;
 			else
 				ipath[i] = INT_MIN;
@@ -1370,13 +1405,377 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
 		PG_RETURN_DATUM(res);
 }
 
+/* Initialize private jsonb subscripting state. */
+static void
+jsonb_subscript_init(SubscriptingRefState *sbstate, Datum container, bool isnull)
+{
+	Jsonb	   *jb = isnull ? NULL : DatumGetJsonbP(container);
+
+	if (sbstate->isassignment)
+	{
+		JsonbAssignState *astate = palloc0(sizeof(*astate));
+		JsonbSubscriptState *subscript = &astate->subscripts[0];
+
+		astate->ps = NULL;
+
+		if (jb)
+		{
+			JsonbValue	jbv;
+			JsonbIteratorToken tok;
+
+			astate->iter = JsonbIteratorInit(&jb->root);
+
+			tok = JsonbIteratorNext(&astate->iter, &jbv, false);
+
+			if (tok == WJB_BEGIN_ARRAY)
+			{
+				if (jbv.val.array.rawScalar)
+					ereport(ERROR,
+							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+							 errmsg("cannot assign to subscript of scalar jsonb")));
+
+				subscript->is_array = true;
+				subscript->array_size = jbv.val.array.nElems;
+			}
+			else
+				subscript->is_array = false;
+
+			subscript->exists = true;
+		}
+		else
+		{
+			astate->iter = NULL;
+			subscript->exists = false;
+		}
+
+		sbstate->privatedata = astate;
+	}
+	else
+	{
+		JsonbValue *jbv;
+
+		/* Initialize a binary JsonbValue and use it as a private state */
+		if (jb)
+		{
+			jbv = palloc(sizeof(*jbv));
+
+			jbv->type = jbvBinary;
+			jbv->val.binary.data = &jb->root;
+			jbv->val.binary.len = VARSIZE(jb) - VARHDRSZ;
+		}
+		else
+			jbv = NULL;
+
+		sbstate->privatedata = jbv;
+	}
+}
+
+/*
+ * Select subscript expression variant.
+ *
+ * There are two expression variants of jsonb subscripts:
+ *   0th - unmodified expression
+ *   1st - expression casted to int4 (optional, if type is numeric)
+ *
+ * If the current jsonb is an array then we select 1st variant, otherwise
+ * default 0th variant is selected.
+ */
+static int
+jsonb_subscript_selectexpr(SubscriptingRefState *sbstate, int num,
+						   Oid subscriptType, Oid *exprTypes, int nExprs)
+{
+	bool		is_array = false;
+
+	Assert(nExprs == 1);
+	Assert(!OidIsValid(exprTypes[0]) || exprTypes[0] == INT4OID);
+
+	if (sbstate->isassignment)
+	{
+		JsonbAssignState *astate = sbstate->privatedata;
+		JsonbSubscriptState *subscript = &astate->subscripts[num];
+
+		if (!subscript->exists)
+		{
+			/* NULL can be only in assignments, select int4 variant if available. */
+			Assert(sbstate->isassignment);
+
+			subscript->is_array = OidIsValid(exprTypes[0]);
+			subscript->array_size = 0;
+		}
+
+		is_array = subscript->is_array;
+	}
+	else
+	{
+		JsonbValue *jbv = sbstate->privatedata;
+
+		Assert(jbv);
+
+		if (jbv->type == jbvBinary)
+		{
+			JsonbContainer *jbc = jbv->val.binary.data;
+
+			if (JsonContainerIsArray(jbc) && !JsonContainerIsScalar(jbc))
+				is_array = true;
+		}
+	}
+
+	if (is_array && OidIsValid(exprTypes[0]))
+		return 1;
+
+	return 0;
+}
+
+/* Get the integer index from a subscript datum */
+static int32
+jsonb_subscript_get_array_index(Datum value, Oid typid, int num,
+								int arraySize, bool isAssignment)
+{
+	long		lindex;
+
+	if (typid == INT4OID)
+		lindex = DatumGetInt32(value);
+	else if (typid != TEXTOID)
+		elog(ERROR, "invalid jsonb subscript type: %u", typid);
+	else if (!jsonb_get_array_index_from_text(value, &lindex))
+	{
+		if (isAssignment)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+					 errmsg("jsonb array subscript is not an integer: \"%s\"",
+							TextDatumGetCString(value))));
+		return -1;
+	}
+
+	if (lindex >= 0)
+		return lindex;
+	else /* handle negative subscript */
+		return arraySize + lindex;
+}
+
+/* Get the string key from a subscript datum */
+static char *
+jsonb_subscript_get_object_key(Datum value, Oid typid, int *len)
+{
+	if (typid == TEXTOID)
+	{
+		*len = VARSIZE_ANY_EXHDR(value);
+
+		return VARDATA_ANY(value);
+	}
+	else if (typid == INT4OID)
+	{
+		char		*key = DatumGetCString(DirectFunctionCall1(int4out, value));
+
+		*len = strlen(key);
+
+		return key;
+	}
+	else
+	{
+		elog(ERROR, "invalid jsonb subscript type: %u", typid);
+		return NULL;
+	}
+}
+
+/* Apply single susbscript to jsonb container */
+static inline JsonbValue *
+jsonb_subscript_apply(JsonbValue *jbv, Datum subscriptVal, Oid subscriptTypid,
+					  int subscriptIdx)
+{
+	JsonbContainer *jbc;
+
+	if (jbv->type != jbvBinary ||
+		JsonContainerIsScalar(jbv->val.binary.data))
+		return NULL;	/* scalar, extraction yields a null */
+
+	jbc = jbv->val.binary.data;
+
+	if (JsonContainerIsObject(jbc))
+	{
+		int			keylen;
+		char	   *keystr = jsonb_subscript_get_object_key(subscriptVal,
+															subscriptTypid,
+															&keylen);
+
+		return getKeyJsonValueFromContainer(jbc, keystr, keylen, jbv);
+	}
+	else if (JsonContainerIsArray(jbc) && !JsonContainerIsScalar(jbc))
+	{
+		int32		index = jsonb_subscript_get_array_index(subscriptVal,
+															subscriptTypid,
+															subscriptIdx,
+															JsonContainerSize(jbc),
+															false);
+
+		if (index < 0)
+			return NULL;
+
+		return getIthJsonbValueFromContainer(jbc, index, jbv);
+	}
+	else
+	{
+		/* scalar, extraction yields a null */
+		return NULL;
+	}
+}
+
+static void
+push_null_elements(JsonbParseState **ps, int num)
+{
+	JsonbValue	null;
+
+	null.type = jbvNull;
+
+	while (num-- > 0)
+		pushJsonbValue(ps, WJB_ELEM, &null);
+}
+
+/* Perfrom one subscript assignment step */
+static void
+jsonb_subscript_step_assignment(SubscriptingRefState *sbstate, Datum value,
+								Oid typid, int num, bool isupper)
+{
+	JsonbAssignState *astate = sbstate->privatedata;
+	JsonbSubscriptState *subscript = &astate->subscripts[num];
+	JsonbIteratorToken tok;
+	JsonbValue	jbv;
+	bool		last = num >= sbstate->numupper - 1;
+
+	if (!subscript->exists)
+	{
+		/* Select the type of newly created container. */
+		if (typid == INT4OID)
+		{
+			subscript->is_array = true;
+			subscript->array_size = 0;
+		}
+		else if (typid == TEXTOID)
+			subscript->is_array = false;
+		else
+			elog(ERROR, "invalid jsonb subscript type: %u", typid);
+	}
+
+	subscript[1].exists = false;
+
+	if (subscript->is_array)
+	{
+		int32		i = 0;
+		int32		index = jsonb_subscript_get_array_index(value, typid, num,
+															subscript->array_size,
+															true);
+
+		pushJsonbValue(&astate->ps, WJB_BEGIN_ARRAY, NULL);
+
+		subscript->array_index = index;
+
+		if (index >= 0 && subscript->exists)
+		{
+			/* Try to copy preceding elements */
+			for (; i < index; i++)
+			{
+				tok = JsonbIteratorNext(&astate->iter, &jbv, true);
+
+				if (tok != WJB_ELEM)
+					break;
+
+				pushJsonbValue(&astate->ps, tok, &jbv);
+			}
+
+			/* Try to read replaced element */
+			if (i >= index &&
+				JsonbIteratorNext(&astate->iter, &jbv, last) != WJB_END_ARRAY)
+				subscript[1].exists = true;
+		}
+
+		/* Fill the gap before the new element with nulls */
+		if (i < index)
+			push_null_elements(&astate->ps, index - i);
+	}
+	else
+	{
+		JsonbValue	key;
+
+		key.type = jbvString;
+		key.val.string.val = jsonb_subscript_get_object_key(value, typid,
+															&key.val.string.len);
+
+		pushJsonbValue(&astate->ps, WJB_BEGIN_OBJECT, NULL);
+
+		if (subscript->exists &&
+			copyJsonbObject(&astate->ps, &astate->iter, &key))
+		{
+			subscript[1].exists = true;		/* key is found */
+			tok = JsonbIteratorNext(&astate->iter, &jbv, last);
+		}
+
+		pushJsonbValue(&astate->ps, WJB_KEY, &key);
+	}
+
+	/* If the value does exits, process and validate its type. */
+	if (subscript[1].exists)
+	{
+		if (jbv.type == jbvArray)
+		{
+			subscript[1].is_array = true;
+			subscript[1].array_size = jbv.val.array.nElems;
+		}
+		else
+			subscript[1].is_array = false;
+
+		if (!last && IsAJsonbScalar(&jbv))
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("cannot assign to subscript of scalar jsonb")));
+	}
+}
+
+/* Perform one subscripting step */
+static bool
+jsonb_subscript_step(SubscriptingRefState *sbstate, int num, bool isupper)
+{
+	Datum		value;
+	Oid			typid;
+
+	if (isupper)
+	{
+		value = sbstate->upperindex[num];
+		typid = sbstate->uppertypid[num];
+	}
+	else
+		elog(ERROR, "jsonb subscript cannot be lower");
+
+	if (sbstate->isassignment)
+	{
+		jsonb_subscript_step_assignment(sbstate, value, typid, num, isupper);
+
+		return true;	/* always process next subscripts */
+	}
+	else
+	{
+		/*
+		 * Perform one subscripting step by applying subscript value to current
+		 * jsonb container and saving the result into private state.
+		 */
+		JsonbValue *jbv = sbstate->privatedata;
+
+		Assert(jbv); 	/* NULL can only be in assignments */
+
+		jbv = jsonb_subscript_apply(jbv, value, typid, num);
+
+		sbstate->privatedata = jbv;
+
+		/* Process next subscripts only if the result is not NULL */
+		return jbv != NULL;
+	}
+}
+
 static Datum
 jsonb_get_element(Jsonb *jb, Datum *path, int npath, bool *isnull, bool as_text)
 {
-	Jsonb		   *res;
-	JsonbContainer *container = &jb->root;
+	JsonbValue		jbv;
 	JsonbValue	   *jbvp = NULL;
-	JsonbValue		tv;
+	JsonbContainer *container = &jb->root;
 	int				i;
 	bool			have_object = false,
 					have_array = false;
@@ -1384,16 +1783,16 @@ jsonb_get_element(Jsonb *jb, Datum *path, int npath, bool *isnull, bool as_text)
 	*isnull = false;
 
 	/* Identify whether we have object, array, or scalar at top-level */
-	if (JB_ROOT_IS_OBJECT(jb))
+	if (JsonContainerIsObject(container))
 		have_object = true;
-	else if (JB_ROOT_IS_ARRAY(jb) && !JB_ROOT_IS_SCALAR(jb))
+	else if (JsonContainerIsArray(container) && !JsonContainerIsScalar(container))
 		have_array = true;
 	else
 	{
-		Assert(JB_ROOT_IS_ARRAY(jb) && JB_ROOT_IS_SCALAR(jb));
+		Assert(JsonContainerIsArray(container) && JsonContainerIsScalar(container));
 		/* Extract the scalar value, if it is what we'll return */
 		if (npath <= 0)
-			jbvp = getIthJsonbValueFromContainer(container, 0);
+			jbvp = getIthJsonbValueFromContainer(container, 0, &jbv);
 	}
 
 	/*
@@ -1426,19 +1825,14 @@ jsonb_get_element(Jsonb *jb, Datum *path, int npath, bool *isnull, bool as_text)
 			jbvp = getKeyJsonValueFromContainer(container,
 												VARDATA(path[i]),
 												VARSIZE(path[i]) - VARHDRSZ,
-												NULL);
+												jbvp);
 		}
 		else if (have_array)
 		{
 			long		lindex;
 			uint32		index;
-			char	   *indextext = TextDatumGetCString(path[i]);
-			char	   *endptr;
 
-			errno = 0;
-			lindex = strtol(indextext, &endptr, 10);
-			if (endptr == indextext || *endptr != '\0' || errno != 0 ||
-				lindex > INT_MAX || lindex < INT_MIN)
+			if (!jsonb_get_array_index_from_text(path[i], &lindex))
 			{
 				*isnull = true;
 				return PointerGetDatum(NULL);
@@ -1468,7 +1862,7 @@ jsonb_get_element(Jsonb *jb, Datum *path, int npath, bool *isnull, bool as_text)
 					index = nelements + lindex;
 			}
 
-			jbvp = getIthJsonbValueFromContainer(container, index);
+			jbvp = getIthJsonbValueFromContainer(container, index, jbvp);
 		}
 		else
 		{
@@ -1519,32 +1913,6 @@ jsonb_get_element(Jsonb *jb, Datum *path, int npath, bool *isnull, bool as_text)
 	}
 }
 
-Datum
-jsonb_set_element(Datum jsonbdatum, Datum *path, int path_len,
-				  Datum sourceData, Oid source_type, bool is_null)
-{
-	Jsonb			   *jb = DatumGetJsonbP(jsonbdatum);
-	JsonbValue		   *newval,
-					   *res;
-	JsonbParseState    *state = NULL;
-	JsonbIterator 	   *it;
-	bool			   *path_nulls = palloc0(path_len * sizeof(bool));
-
-	newval = to_jsonb_worker(sourceData, source_type, is_null);
-
-	if (newval->type == jbvArray && newval->val.array.rawScalar)
-		*newval = newval->val.array.elems[0];
-
-	it = JsonbIteratorInit(&jb->root);
-
-	res = setPath(&it, path, path_nulls, path_len, &state, 0,
-				  newval, JB_PATH_CREATE);
-
-	pfree(path_nulls);
-
-	PG_RETURN_JSONB_P(JsonbValueToJsonb(res));
-}
-
 /*
  * Return the text representation of the given JsonbValue.
  */
@@ -4660,10 +5028,8 @@ 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, op_type);
-			r = JsonbIteratorNext(it, &v, true);
-			Assert(r == WJB_END_OBJECT);
-			res = pushJsonbValue(st, r, NULL);
+						  newval, op_type);
+			res = pushJsonbValue(st, WJB_END_OBJECT, NULL);
 			break;
 		case WJB_ELEM:
 		case WJB_VALUE:
@@ -4679,109 +5045,118 @@ setPath(JsonbIterator **it, Datum *path_elems,
 }
 
 /*
- * Object walker for setPath
+ * Copy object fields, but stop on the desired key if it is specified.
+ *
+ * True is returned if the key was found, otherwise false.
  */
-static void
-setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
-			  int path_len, JsonbParseState **st, int level,
-			  JsonbValue *newval, uint32 npairs, int op_type)
+static bool
+copyJsonbObject(JsonbParseState **st, JsonbIterator **it, const JsonbValue *key)
 {
-	int			i;
-	JsonbValue	k,
-				v;
-	bool		done = false;
-
-	if (level >= path_len || path_nulls[level])
-		done = true;
+	JsonbIteratorToken r;
+	JsonbValue	keybuf;
 
-	/* empty object is a special case for create */
-	if ((npairs == 0) && (op_type & JB_PATH_CREATE_OR_INSERT) &&
-		(level == path_len - 1))
+	while ((r = JsonbIteratorNext(it, &keybuf, true)) == WJB_KEY)
 	{
-		JsonbValue	newkey;
-
-		newkey.type = jbvString;
-		newkey.val.string.len = VARSIZE_ANY_EXHDR(path_elems[level]);
-		newkey.val.string.val = VARDATA_ANY(path_elems[level]);
+		JsonbValue	val;
 
-		(void) pushJsonbValue(st, WJB_KEY, &newkey);
-		(void) pushJsonbValue(st, WJB_VALUE, newval);
-	}
+		if (key &&
+			key->val.string.len == keybuf.val.string.len &&
+			memcmp(key->val.string.val, keybuf.val.string.val,
+				   key->val.string.len) == 0)
+			return true;	/* stop, key is found */
 
-	for (i = 0; i < npairs; i++)
-	{
-		JsonbIteratorToken r = JsonbIteratorNext(it, &k, true);
+		(void) pushJsonbValue(st, r, &keybuf);
 
-		Assert(r == WJB_KEY);
+		/* Copy value */
+		r = JsonbIteratorNext(it, &val, false);
+		(void) pushJsonbValue(st, r, r < WJB_BEGIN_ARRAY ? &val : NULL);
 
-		if (!done &&
-			k.val.string.len == VARSIZE_ANY_EXHDR(path_elems[level]) &&
-			memcmp(k.val.string.val, VARDATA_ANY(path_elems[level]),
-				   k.val.string.len) == 0)
+		if (r == WJB_BEGIN_ARRAY || r == WJB_BEGIN_OBJECT)
 		{
-			if (level == path_len - 1)
-			{
-				/*
-				 * called from jsonb_insert(), it forbids redefining an
-				 * existing value
-				 */
-				if (op_type & (JB_PATH_INSERT_BEFORE | JB_PATH_INSERT_AFTER))
-					ereport(ERROR,
-							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-							 errmsg("cannot replace existing key"),
-							 errhint("Try using the function jsonb_set "
-									 "to replace key value.")));
+			int			walking_level = 1;
 
-				r = JsonbIteratorNext(it, &v, true);	/* skip value */
-				if (!(op_type & JB_PATH_DELETE))
-				{
-					(void) pushJsonbValue(st, WJB_KEY, &k);
-					(void) pushJsonbValue(st, WJB_VALUE, newval);
-				}
-				done = true;
-			}
-			else
+			while (walking_level != 0)
 			{
-				(void) pushJsonbValue(st, r, &k);
-				setPath(it, path_elems, path_nulls, path_len,
-						st, level + 1, newval, op_type);
+				r = JsonbIteratorNext(it, &val, false);
+
+				if (r == WJB_BEGIN_ARRAY || r == WJB_BEGIN_OBJECT)
+					++walking_level;
+				if (r == WJB_END_ARRAY || r == WJB_END_OBJECT)
+					--walking_level;
+
+				(void) pushJsonbValue(st, r, r < WJB_BEGIN_ARRAY ? &val : NULL);
 			}
 		}
-		else
-		{
-			if ((op_type & JB_PATH_CREATE_OR_INSERT) && !done &&
-				level == path_len - 1 && i == npairs - 1)
-			{
-				JsonbValue	newkey;
+	}
 
-				newkey.type = jbvString;
-				newkey.val.string.len = VARSIZE_ANY_EXHDR(path_elems[level]);
-				newkey.val.string.val = VARDATA_ANY(path_elems[level]);
+	Assert(r == WJB_END_OBJECT);
 
-				(void) pushJsonbValue(st, WJB_KEY, &newkey);
-				(void) pushJsonbValue(st, WJB_VALUE, newval);
-			}
+	return false;	/* key was not found */
+}
 
-			(void) pushJsonbValue(st, r, &k);
-			r = JsonbIteratorNext(it, &v, false);
-			(void) pushJsonbValue(st, r, r < WJB_BEGIN_ARRAY ? &v : NULL);
-			if (r == WJB_BEGIN_ARRAY || r == WJB_BEGIN_OBJECT)
-			{
-				int			walking_level = 1;
+/*
+ * Object walker for setPath
+ */
+static void
+setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
+			  int path_len, JsonbParseState **st, int level,
+			  JsonbValue *newval, int op_type)
+{
+	JsonbValue *key,
+				keybuf,
+				val;
 
-				while (walking_level != 0)
-				{
-					r = JsonbIteratorNext(it, &v, false);
+	if (level >= path_len || path_nulls[level])
+		key = NULL;
+	else
+	{
+		key = &keybuf;
+		key->type = jbvString;
+		key->val.string.val = VARDATA_ANY(path_elems[level]);
+		key->val.string.len = VARSIZE_ANY_EXHDR(path_elems[level]);
+	}
 
-					if (r == WJB_BEGIN_ARRAY || r == WJB_BEGIN_OBJECT)
-						++walking_level;
-					if (r == WJB_END_ARRAY || r == WJB_END_OBJECT)
-						--walking_level;
+	/* Start copying object fields and stop on the desired key. */
+	if (copyJsonbObject(st, it, key))
+	{
+		/* The desired key was found. */
+		if (level == path_len - 1)
+		{
+			/*
+			 * called from jsonb_insert(), it forbids redefining an
+			 * existing value
+			 */
+			if (op_type & (JB_PATH_INSERT_BEFORE | JB_PATH_INSERT_AFTER))
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						 errmsg("cannot replace existing key"),
+						 errhint("Try using the function jsonb_set "
+								 "to replace key value.")));
 
-					(void) pushJsonbValue(st, r, r < WJB_BEGIN_ARRAY ? &v : NULL);
-				}
+			(void) JsonbIteratorNext(it, &val, true);	/* skip value */
+
+			if (!(op_type & JB_PATH_DELETE))
+			{
+				(void) pushJsonbValue(st, WJB_KEY, key);
+				(void) pushJsonbValue(st, WJB_VALUE, newval);
 			}
 		}
+		else
+		{
+			(void) pushJsonbValue(st, WJB_KEY, key);
+			setPath(it, path_elems, path_nulls, path_len, st, level + 1,
+					newval, op_type);
+		}
+
+		/* Copy the remaining fields. */
+		(void) copyJsonbObject(st, it, NULL);
+	}
+	else if (key && (op_type & JB_PATH_CREATE_OR_INSERT) &&
+			 level == path_len - 1)
+	{
+		/* All fields were copied, but the desired key was not found. */
+		(void) pushJsonbValue(st, WJB_KEY, key);
+		(void) pushJsonbValue(st, WJB_VALUE, newval);
 	}
 }
 
@@ -4801,18 +5176,14 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
 	/* pick correct index */
 	if (level < path_len && !path_nulls[level])
 	{
-		char	   *c = TextDatumGetCString(path_elems[level]);
 		long		lindex;
-		char	   *badp;
 
-		errno = 0;
-		lindex = strtol(c, &badp, 10);
-		if (errno != 0 || badp == c || *badp != '\0' || lindex > INT_MAX ||
-			lindex < INT_MIN)
+		if (!jsonb_get_array_index_from_text(path_elems[level], &lindex))
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 					 errmsg("path element at position %d is not an integer: \"%s\"",
-							level + 1, c)));
+							level + 1, TextDatumGetCString(path_elems[level]))));
+
 		idx = lindex;
 	}
 	else
@@ -4914,14 +5285,12 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
 Datum
 jsonb_subscript_fetch(Datum containerSource, SubscriptingRefState *sbstate)
 {
-	return jsonb_get_element(DatumGetJsonbP(containerSource),
-							 sbstate->upperindex,
-							 sbstate->numupper,
-							 &sbstate->resnull,
-							 false);
-}
+	JsonbValue *jbv = sbstate->privatedata;
 
+	Assert(!sbstate->isassignment);
 
+	return JsonbPGetDatum(JsonbValueToJsonb(jbv));
+}
 
 /*
  * Perform an actual data extraction or modification for the jsonb
@@ -4931,19 +5300,58 @@ jsonb_subscript_fetch(Datum containerSource, SubscriptingRefState *sbstate)
 Datum
 jsonb_subscript_assign(Datum containerSource, SubscriptingRefState *sbstate)
 {
-	/*
-	 * the original jsonb must be non-NULL, else we punt and return the
-	 * original array.
-	 */
+	JsonbAssignState *astate = sbstate->privatedata;
+	JsonbSubscriptState *subscript = &astate->subscripts[sbstate->numupper - 1];
+	JsonbValue *res = NULL;
+	JsonbValue *newval;
+	JsonbValue jbv;
+	JsonbIteratorToken tok;
+
+	/* If the original jsonb is NULL, we will create a new container. */
 	if (sbstate->resnull)
-		return containerSource;
+		sbstate->resnull = false;
 
-	return jsonb_set_element(containerSource,
-							 sbstate->upperindex,
-							 sbstate->numupper,
-							 sbstate->replacevalue,
-							 sbstate->refelemtype,
+	/* Transform the new value to jsonb */
+	newval = to_jsonb_worker(sbstate->replacevalue, sbstate->refelemtype,
 							 sbstate->replacenull);
+
+	if (newval->type == jbvArray && newval->val.array.rawScalar)
+		*newval = newval->val.array.elems[0];
+
+	/* Push the new value */
+	tok = subscript->is_array ? WJB_ELEM : WJB_VALUE;
+	res = pushJsonbValue(&astate->ps, tok, newval);
+
+	/* Finish unclosed arrays/objects */
+	for (; subscript >= astate->subscripts; subscript--)
+	{
+		/*
+		 * If the element does exists, then all preceding subscripts must exist
+		 * and the iterator may contain remaining elements.  So we need to
+		 * switch to copying from the iterator now.
+		 */
+		if (subscript[1].exists)
+			break;
+
+		if (subscript->is_array && subscript->array_index < 0)
+		{
+			/* Fill the gap between prepended element and 0th element */
+			if (subscript->array_index < -1)
+				push_null_elements(&astate->ps, -1 - subscript->array_index);
+
+			if (subscript->exists)
+				break;	/* original elements are copied from the iterator */
+		}
+
+		tok = subscript->is_array ? WJB_END_ARRAY : WJB_END_OBJECT;
+		res = pushJsonbValue(&astate->ps, tok, NULL);
+	}
+
+	/* Copy remaining elements from the iterator */
+	while ((tok = JsonbIteratorNext(&astate->iter, &jbv, false)) != WJB_DONE)
+		res = pushJsonbValue(&astate->ps, tok, tok < WJB_BEGIN_ARRAY ? &jbv : NULL);
+
+	return JsonbPGetDatum(JsonbValueToJsonb(res));
 }
 
 /*
@@ -4962,12 +5370,15 @@ Datum
 jsonb_subscript_handler(PG_FUNCTION_ARGS)
 {
 	SubscriptRoutines *sbsroutines = (SubscriptRoutines *)
-									 palloc(sizeof(SubscriptRoutines));
+		palloc0(sizeof(SubscriptRoutines));
 
 	sbsroutines->prepare = jsonb_subscript_prepare;
 	sbsroutines->validate = jsonb_subscript_validate;
 	sbsroutines->fetch = jsonb_subscript_fetch;
 	sbsroutines->assign = jsonb_subscript_assign;
+	sbsroutines->init = jsonb_subscript_init;
+	sbsroutines->step = jsonb_subscript_step;
+	sbsroutines->selectexpr = jsonb_subscript_selectexpr;
 
 	PG_RETURN_POINTER(sbsroutines);
 }
@@ -4990,19 +5401,37 @@ SubscriptingRef *
 jsonb_subscript_validate(bool isAssignment, SubscriptingRef *sbsref,
 						 ParseState *pstate)
 {
-	List			   *upperIndexpr = NIL;
-	ListCell		   *l;
+	List	   *upperIndexpr = NIL;
+	ListCell   *l;
 
 	if (sbsref->reflowerindexpr != NIL)
+	{
+		Node	   *slice = NULL;
+
+		/* Try to find first non-NULL lower subscript */
+		foreach(l, sbsref->reflowerindexpr)
+		{
+			if (lfirst(l) != NULL)
+			{
+				slice = (Node *) lfirst(l);
+				break;
+			}
+		}
+
 		ereport(ERROR,
 				(errcode(ERRCODE_DATATYPE_MISMATCH),
 				 errmsg("jsonb subscript does not support slices"),
-				 parser_errposition(pstate, exprLocation(
-						 ((Node *) linitial(sbsref->reflowerindexpr))))));
+				 parser_errposition(pstate, exprLocation(slice))));
+	}
 
 	foreach(l, sbsref->refupperindexpr)
 	{
-		Node *subexpr = (Node *) lfirst(l);
+		Node	   *subexpr = (Node *) lfirst(l);
+		Node	   *textexpr;
+		Node	   *intexpr;
+		Oid			subexprType;
+		char		typcategory;
+		bool		typispreferred;
 
 		if (subexpr == NULL)
 			ereport(ERROR,
@@ -5011,18 +5440,48 @@ jsonb_subscript_validate(bool isAssignment, SubscriptingRef *sbsref,
 					 parser_errposition(pstate, exprLocation(
 						((Node *) linitial(sbsref->refupperindexpr))))));
 
-		subexpr = coerce_to_target_type(pstate,
-										subexpr, exprType(subexpr),
-										TEXTOID, -1,
-										COERCION_ASSIGNMENT,
-										COERCE_IMPLICIT_CAST,
-										-1);
-		if (subexpr == NULL)
+		subexprType = exprType(subexpr);
+
+		textexpr = coerce_to_target_type(pstate,
+										 subexpr, subexprType,
+										 TEXTOID, -1,
+										 COERCION_ASSIGNMENT,
+										 COERCE_IMPLICIT_CAST,
+										 -1);
+
+		if (textexpr == NULL)
 			ereport(ERROR,
 					(errcode(ERRCODE_DATATYPE_MISMATCH),
-					 errmsg("jsonb subscript must have text type"),
+					 errmsg("jsonb subscript must have %s type", "text"),
 					 parser_errposition(pstate, exprLocation(subexpr))));
 
+		/* Try to coerce numeric types to int4 for array subscripting. */
+		get_type_category_preferred(subexprType, &typcategory, &typispreferred);
+
+		if (typcategory == TYPCATEGORY_NUMERIC)
+		{
+			intexpr = coerce_to_target_type(pstate,
+											subexpr, subexprType,
+											INT4OID, -1,
+											COERCION_ASSIGNMENT,
+											COERCE_IMPLICIT_CAST,
+											-1);
+
+			if (intexpr &&
+				(subexprType == INT4OID ||
+				 subexprType == INT2OID))
+				textexpr = NULL;
+		}
+		else
+			intexpr = NULL;
+
+		/*
+		 * If int4 expression variant exists, create a list with both text and
+		 * int4 variants.
+		 */
+		subexpr = textexpr && intexpr ? (Node *) list_make2(textexpr, intexpr) :
+			textexpr ? textexpr : intexpr;
+
 		upperIndexpr = lappend(upperIndexpr, subexpr);
 	}
 
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index e2c1bfb5..72dcf6a 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -784,7 +784,7 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
 						else
 						{
 							v = getIthJsonbValueFromContainer(jb->val.binary.data,
-															  (uint32) index);
+															  (uint32) index, NULL);
 
 							if (v == NULL)
 								continue;
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index f9cf0af..9a8e8cb 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -185,6 +185,12 @@ typedef enum ExprEvalOp
 	 */
 	EEOP_FIELDSTORE_FORM,
 
+	/* Init subscripting */
+	EEOP_SBSREF_INIT,
+
+	/* Select an expression for container subscript evaluation */
+	EEOP_SBSREF_SELECTEXPR,
+
 	/* Process a container subscript; short-circuit expression to NULL if NULL */
 	EEOP_SBSREF_SUBSCRIPT,
 
@@ -492,17 +498,30 @@ typedef struct ExprEvalStep
 			int			ncolumns;
 		}			fieldstore;
 
+		/* for EEOP_SBSREF_SELECTEXPR */
+		struct
+		{
+			/* too big to have inline */
+			struct SubscriptingRefState *state;
+			int			off;	/* 0-based index of this subscript */
+			bool		isupper;	/* is it upper or lower subscript? */
+			int			nexprs;		/* subscript expression count */
+			Oid		   *exprtypes;		/* type oids of subscript expression variants */
+			int		   *jumpdones;		/* jumps to expression variants */
+		}			sbsref_selectexpr;
+
 		/* for EEOP_SBSREF_SUBSCRIPT */
 		struct
 		{
 			/* too big to have inline */
 			struct SubscriptingRefState *state;
+			Oid			typid;	/* type oid of subscript */
 			int			off;	/* 0-based index of this subscript */
 			bool		isupper;	/* is it upper or lower subscript? */
 			int			jumpdone;	/* jump here on null */
 		}			sbsref_subscript;
 
-		/* for EEOP_SBSREF_OLD / ASSIGN / FETCH */
+		/* for EEOP_SBSREF_OLD / ASSIGN / FETCH / INIT */
 		struct
 		{
 			/* too big to have inline */
@@ -673,11 +692,13 @@ typedef struct SubscriptingRefState
 	/* at runtime, extracted subscript datums get stored in upperindex[] */
 	int			numupper;
 	bool		upperprovided[MAX_SUBSCRIPT_DEPTH];
+	Oid			uppertypid[MAX_SUBSCRIPT_DEPTH];
 	Datum		upperindex[MAX_SUBSCRIPT_DEPTH];
 
 	/* similarly for lower indexes, if any */
 	int			numlower;
 	bool		lowerprovided[MAX_SUBSCRIPT_DEPTH];
+	Oid			lowertypid[MAX_SUBSCRIPT_DEPTH];
 	Datum		lowerindex[MAX_SUBSCRIPT_DEPTH];
 
 	/* subscript expressions get evaluated into here */
@@ -694,6 +715,7 @@ typedef struct SubscriptingRefState
 
 	bool		resnull;
 	struct SubscriptRoutines *sbsroutines;
+	void	   *privatedata;
 } SubscriptingRefState;
 
 
@@ -738,6 +760,8 @@ extern void ExecEvalFieldStoreDeForm(ExprState *state, ExprEvalStep *op,
 									 ExprContext *econtext);
 extern void ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op,
 								   ExprContext *econtext);
+extern void ExecEvalSubscriptingRefInit(ExprState *state, ExprEvalStep *op);
+extern int ExecEvalSubscriptingRefSelect(ExprState *state, ExprEvalStep *op);
 extern bool ExecEvalSubscriptingRef(ExprState *state, ExprEvalStep *op);
 extern void ExecEvalSubscriptingRefFetch(ExprState *state, ExprEvalStep *op);
 extern void ExecEvalSubscriptingRefOld(ExprState *state, ExprEvalStep *op);
diff --git a/src/include/nodes/subscripting.h b/src/include/nodes/subscripting.h
index 1800d5e..d752bf6 100644
--- a/src/include/nodes/subscripting.h
+++ b/src/include/nodes/subscripting.h
@@ -29,13 +29,25 @@ typedef Datum (*SubscriptingFetch) (Datum source, struct SubscriptingRefState *s
 
 typedef Datum (*SubscriptingAssign) (Datum source, struct SubscriptingRefState *sbrsefstate);
 
+typedef void (*SubscriptingInit) (struct SubscriptingRefState *sbrefstate,
+								  Datum source, bool isnull);
+
+typedef int (*SubscriptingSelectExpr) (struct SubscriptingRefState *sbsreftate,
+									   int subscriptNum, Oid subscriptType,
+									   Oid *subscriptExprTypes, int nexprs);
+
+typedef bool (*SubscriptingStep) (struct SubscriptingRefState *sbrefstate,
+								  int subscriptNum, bool isupper);
+
 typedef struct SubscriptRoutines
 {
 	SubscriptingPrepare		prepare;
 	SubscriptingValidate	validate;
 	SubscriptingFetch		fetch;
 	SubscriptingAssign		assign;
-
+	SubscriptingInit		init;
+	SubscriptingStep		step;
+	SubscriptingSelectExpr	selectexpr;
 } SubscriptRoutines;
 
 
diff --git a/src/include/utils/jsonb.h b/src/include/utils/jsonb.h
index 6081fab..ec6dae1 100644
--- a/src/include/utils/jsonb.h
+++ b/src/include/utils/jsonb.h
@@ -386,7 +386,7 @@ extern JsonbValue *getKeyJsonValueFromContainer(JsonbContainer *container,
 												const char *keyVal, int keyLen,
 												JsonbValue *res);
 extern JsonbValue *getIthJsonbValueFromContainer(JsonbContainer *sheader,
-												 uint32 i);
+												 uint32 i, JsonbValue *result);
 extern JsonbValue *pushJsonbValue(JsonbParseState **pstate,
 								  JsonbIteratorToken seq, JsonbValue *jbval);
 extern JsonbIterator *JsonbIteratorInit(JsonbContainer *container);
diff --git a/src/test/regress/expected/jsonb.out b/src/test/regress/expected/jsonb.out
index c7d7e30..526fb75 100644
--- a/src/test/regress/expected/jsonb.out
+++ b/src/test/regress/expected/jsonb.out
@@ -4701,12 +4701,48 @@ select ('[1, "2", null]'::jsonb)['1'];
  "2"
 (1 row)
 
-select ('[1, "2", null]'::jsonb)[1.0];
+select ('[1, "2", null]'::jsonb)['1.0'];
  jsonb 
 -------
  
 (1 row)
 
+select ('[1, "2", null]'::jsonb)[1.0];
+ jsonb 
+-------
+ "2"
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[1.4];
+ jsonb 
+-------
+ "2"
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[1.5];
+ jsonb 
+-------
+ null
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[1.6];
+ jsonb 
+-------
+ null
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[0.6];
+ jsonb 
+-------
+ "2"
+(1 row)
+
+select ('[1, "2", null]'::jsonb)['0.3'::numeric];
+ jsonb 
+-------
+ 1
+(1 row)
+
 select ('[1, "2", null]'::jsonb)[2];
  jsonb 
 -------
@@ -4791,6 +4827,54 @@ select ('{"a": ["a1", {"b1": ["aaa", "bbb", "ccc"]}], "b": "bb"}'::jsonb)['a'][1
  "ccc"
 (1 row)
 
+select ('{"1": "a", "1.0": "b", "1.2": "c"}'::jsonb)[1];
+ jsonb 
+-------
+ "a"
+(1 row)
+
+select ('{"1": "a", "1.0": "b", "1.2": "c"}'::jsonb)[1.0];
+ jsonb 
+-------
+ "b"
+(1 row)
+
+select ('{"1": "a", "1.0": "b", "1.2": "c"}'::jsonb)[1.1];
+ jsonb 
+-------
+ 
+(1 row)
+
+select ('{"1": "a", "1.0": "b", "1.2": "c"}'::jsonb)[1.2];
+ jsonb 
+-------
+ "c"
+(1 row)
+
+select ('{"1": "a", "1.0": "b", "1.2": "c"}'::jsonb)['1'];
+ jsonb 
+-------
+ "a"
+(1 row)
+
+select ('{"1": "a", "1.0": "b", "1.2": "c"}'::jsonb)['1.0'];
+ jsonb 
+-------
+ "b"
+(1 row)
+
+select ('{"1": "a", "1.0": "b", "1.2": "c"}'::jsonb)['1.1'];
+ jsonb 
+-------
+ 
+(1 row)
+
+select ('{"1": "a", "1.0": "b", "1.2": "c"}'::jsonb)['1.2'];
+ jsonb 
+-------
+ "c"
+(1 row)
+
 create TEMP TABLE test_jsonb_subscript (
        id int,
        test_json jsonb
@@ -4843,11 +4927,75 @@ select * from test_jsonb_subscript;
   2 | {"a": [1, 2, 3], "key": "value"}
 (2 rows)
 
+-- replace element in string subscript
+update test_jsonb_subscript set test_json['a']['3'] = '4'::jsonb;
+select * from test_jsonb_subscript;
+ id |              test_json              
+----+-------------------------------------
+  1 | {"a": [1, 2, 3, 4]}
+  2 | {"a": [1, 2, 3, 4], "key": "value"}
+(2 rows)
+
+-- bad array subscripts
+update test_jsonb_subscript set test_json['a']['a'] = '5'::jsonb;
+ERROR:  jsonb array subscript is not an integer: "a"
+select * from test_jsonb_subscript;
+ id |              test_json              
+----+-------------------------------------
+  1 | {"a": [1, 2, 3, 4]}
+  2 | {"a": [1, 2, 3, 4], "key": "value"}
+(2 rows)
+
+update test_jsonb_subscript set test_json['a'][0][1] = '5'::jsonb;
+ERROR:  cannot assign to subscript of scalar jsonb
+select * from test_jsonb_subscript;
+ id |              test_json              
+----+-------------------------------------
+  1 | {"a": [1, 2, 3, 4]}
+  2 | {"a": [1, 2, 3, 4], "key": "value"}
+(2 rows)
+
+-- append element to array
+update test_jsonb_subscript set test_json['a'][2.9] = '"4"'::jsonb;
+select * from test_jsonb_subscript;
+ id |               test_json               
+----+---------------------------------------
+  1 | {"a": [1, 2, 3, "4"]}
+  2 | {"a": [1, 2, 3, "4"], "key": "value"}
+(2 rows)
+
+-- append element to array with a gap filled with nulls
+update test_jsonb_subscript set test_json['a'][7] = '8'::jsonb;
+select * from test_jsonb_subscript;
+ id |                         test_json                          
+----+------------------------------------------------------------
+  1 | {"a": [1, 2, 3, "4", null, null, null, 8]}
+  2 | {"a": [1, 2, 3, "4", null, null, null, 8], "key": "value"}
+(2 rows)
+
+-- replace element in array using negative subscript
+update test_jsonb_subscript set test_json['a'][-4] = '5'::jsonb;
+select * from test_jsonb_subscript;
+ id |                        test_json                        
+----+---------------------------------------------------------
+  1 | {"a": [1, 2, 3, "4", 5, null, null, 8]}
+  2 | {"a": [1, 2, 3, "4", 5, null, null, 8], "key": "value"}
+(2 rows)
+
+-- prepend element to array using negative subscript with a gap filled with nulls
+update test_jsonb_subscript set test_json['a'][-10] = '6'::jsonb;
+select * from test_jsonb_subscript;
+ id |                            test_json                             
+----+------------------------------------------------------------------
+  1 | {"a": [6, null, 1, 2, 3, "4", 5, null, null, 8]}
+  2 | {"a": [6, null, 1, 2, 3, "4", 5, null, null, 8], "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"}
+ id |                            test_json                             
+----+------------------------------------------------------------------
+  2 | {"a": [6, null, 1, 2, 3, "4", 5, null, null, 8], "key": "value"}
 (1 row)
 
 select * from test_jsonb_subscript where test_json['key_doesnt_exists'] = '"value"';
@@ -4865,12 +5013,81 @@ update test_jsonb_subscript set test_json[NULL] = 1;
 ERROR:  subscript in assignment must not be null
 update test_jsonb_subscript set test_json['another_key'] = NULL;
 select * from test_jsonb_subscript;
- id |                       test_json                       
-----+-------------------------------------------------------
-  1 | {"a": [1, 2, 3], "another_key": null}
-  2 | {"a": [1, 2, 3], "key": "value", "another_key": null}
+ id |                                       test_json                                       
+----+---------------------------------------------------------------------------------------
+  1 | {"a": [6, null, 1, 2, 3, "4", 5, null, null, 8], "another_key": null}
+  2 | {"a": [6, null, 1, 2, 3, "4", 5, null, null, 8], "key": "value", "another_key": null}
 (2 rows)
 
+-- create a path
+delete from test_jsonb_subscript;
+insert into test_jsonb_subscript values (0, NULL);
+update test_jsonb_subscript set test_json['a'] = '1'::jsonb;
+select * from test_jsonb_subscript;
+ id | test_json 
+----+-----------
+  0 | {"a": 1}
+(1 row)
+
+update test_jsonb_subscript set test_json['b'][0] = '2'::jsonb;
+select * from test_jsonb_subscript;
+ id |     test_json      
+----+--------------------
+  0 | {"a": 1, "b": [2]}
+(1 row)
+
+update test_jsonb_subscript set test_json['c'][0]['a'] = '3'::jsonb;
+select * from test_jsonb_subscript;
+ id |              test_json              
+----+-------------------------------------
+  0 | {"a": 1, "b": [2], "c": [{"a": 3}]}
+(1 row)
+
+update test_jsonb_subscript set test_json['d'][0]['a'][3] = '4'::jsonb;
+select * from test_jsonb_subscript;
+ id |                                test_json                                 
+----+--------------------------------------------------------------------------
+  0 | {"a": 1, "b": [2], "c": [{"a": 3}], "d": [{"a": [null, null, null, 4]}]}
+(1 row)
+
+update test_jsonb_subscript set test_json['d'][0]['c'][-3] = '5'::jsonb;
+select * from test_jsonb_subscript;
+ id |                                           test_json                                            
+----+------------------------------------------------------------------------------------------------
+  0 | {"a": 1, "b": [2], "c": [{"a": 3}], "d": [{"a": [null, null, null, 4], "c": [5, null, null]}]}
+(1 row)
+
+update test_jsonb_subscript set test_json['d'][0]['b']['x'] = '6'::jsonb;
+select * from test_jsonb_subscript;
+ id |                                                   test_json                                                   
+----+---------------------------------------------------------------------------------------------------------------
+  0 | {"a": 1, "b": [2], "c": [{"a": 3}], "d": [{"a": [null, null, null, 4], "b": {"x": 6}, "c": [5, null, null]}]}
+(1 row)
+
+update test_jsonb_subscript set test_json['e']['y'] = '7'::jsonb;
+select * from test_jsonb_subscript;
+ id |                                                          test_json                                                           
+----+------------------------------------------------------------------------------------------------------------------------------
+  0 | {"a": 1, "b": [2], "c": [{"a": 3}], "d": [{"a": [null, null, null, 4], "b": {"x": 6}, "c": [5, null, null]}], "e": {"y": 7}}
+(1 row)
+
+-- updating of scalar's subscripts
+update test_jsonb_subscript set test_json = '1';
+update test_jsonb_subscript set test_json['a'] = '1'::jsonb;
+ERROR:  cannot assign to subscript of scalar jsonb
+update test_jsonb_subscript set test_json[0] = '1'::jsonb;
+ERROR:  cannot assign to subscript of scalar jsonb
+update test_jsonb_subscript set test_json = '{"a": 1}';
+update test_jsonb_subscript set test_json['a']['a'] = '1'::jsonb;
+ERROR:  cannot assign to subscript of scalar jsonb
+update test_jsonb_subscript set test_json['a'][0] = '1'::jsonb;
+ERROR:  cannot assign to subscript of scalar jsonb
+select * from test_jsonb_subscript;
+ id | test_json 
+----+-----------
+  0 | {"a": 1}
+(1 row)
+
 -- jsonb to tsvector
 select to_tsvector('{"a": "aaa bbb ddd ccc", "b": ["eee fff ggg"], "c": {"d": "hhh iii"}}'::jsonb);
                                 to_tsvector                                
diff --git a/src/test/regress/sql/jsonb.sql b/src/test/regress/sql/jsonb.sql
index 934fa9d..f0d9c51 100644
--- a/src/test/regress/sql/jsonb.sql
+++ b/src/test/regress/sql/jsonb.sql
@@ -1194,7 +1194,13 @@ select ('{"a": 1}'::jsonb)[NULL];
 select ('[1, "2", null]'::jsonb)['a'];
 select ('[1, "2", null]'::jsonb)[0];
 select ('[1, "2", null]'::jsonb)['1'];
+select ('[1, "2", null]'::jsonb)['1.0'];
 select ('[1, "2", null]'::jsonb)[1.0];
+select ('[1, "2", null]'::jsonb)[1.4];
+select ('[1, "2", null]'::jsonb)[1.5];
+select ('[1, "2", null]'::jsonb)[1.6];
+select ('[1, "2", null]'::jsonb)[0.6];
+select ('[1, "2", null]'::jsonb)['0.3'::numeric];
 select ('[1, "2", null]'::jsonb)[2];
 select ('[1, "2", null]'::jsonb)[3];
 select ('[1, "2", null]'::jsonb)[-2];
@@ -1209,6 +1215,15 @@ 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']['a3'];
 select ('{"a": ["a1", {"b1": ["aaa", "bbb", "ccc"]}], "b": "bb"}'::jsonb)['a'][1]['b1'];
 select ('{"a": ["a1", {"b1": ["aaa", "bbb", "ccc"]}], "b": "bb"}'::jsonb)['a'][1]['b1'][2];
+select ('{"1": "a", "1.0": "b", "1.2": "c"}'::jsonb)[1];
+select ('{"1": "a", "1.0": "b", "1.2": "c"}'::jsonb)[1.0];
+select ('{"1": "a", "1.0": "b", "1.2": "c"}'::jsonb)[1.1];
+select ('{"1": "a", "1.0": "b", "1.2": "c"}'::jsonb)[1.2];
+select ('{"1": "a", "1.0": "b", "1.2": "c"}'::jsonb)['1'];
+select ('{"1": "a", "1.0": "b", "1.2": "c"}'::jsonb)['1.0'];
+select ('{"1": "a", "1.0": "b", "1.2": "c"}'::jsonb)['1.1'];
+select ('{"1": "a", "1.0": "b", "1.2": "c"}'::jsonb)['1.2'];
+
 
 create TEMP TABLE test_jsonb_subscript (
        id int,
@@ -1239,6 +1254,35 @@ select * from test_jsonb_subscript;
 update test_jsonb_subscript set test_json['a'] = '[1, 2, 3]'::jsonb;
 select * from test_jsonb_subscript;
 
+-- replace element in string subscript
+update test_jsonb_subscript set test_json['a']['3'] = '4'::jsonb;
+select * from test_jsonb_subscript;
+
+-- bad array subscripts
+update test_jsonb_subscript set test_json['a']['a'] = '5'::jsonb;
+select * from test_jsonb_subscript;
+
+update test_jsonb_subscript set test_json['a'][0][1] = '5'::jsonb;
+select * from test_jsonb_subscript;
+
+
+-- append element to array
+update test_jsonb_subscript set test_json['a'][2.9] = '"4"'::jsonb;
+select * from test_jsonb_subscript;
+
+-- append element to array with a gap filled with nulls
+update test_jsonb_subscript set test_json['a'][7] = '8'::jsonb;
+select * from test_jsonb_subscript;
+
+-- replace element in array using negative subscript
+update test_jsonb_subscript set test_json['a'][-4] = '5'::jsonb;
+select * from test_jsonb_subscript;
+
+-- prepend element to array using negative subscript with a gap filled with nulls
+update test_jsonb_subscript set test_json['a'][-10] = '6'::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"';
@@ -1249,6 +1293,42 @@ update test_jsonb_subscript set test_json[NULL] = 1;
 update test_jsonb_subscript set test_json['another_key'] = NULL;
 select * from test_jsonb_subscript;
 
+-- create a path
+delete from test_jsonb_subscript;
+insert into test_jsonb_subscript values (0, NULL);
+
+update test_jsonb_subscript set test_json['a'] = '1'::jsonb;
+select * from test_jsonb_subscript;
+
+update test_jsonb_subscript set test_json['b'][0] = '2'::jsonb;
+select * from test_jsonb_subscript;
+
+update test_jsonb_subscript set test_json['c'][0]['a'] = '3'::jsonb;
+select * from test_jsonb_subscript;
+
+update test_jsonb_subscript set test_json['d'][0]['a'][3] = '4'::jsonb;
+select * from test_jsonb_subscript;
+
+update test_jsonb_subscript set test_json['d'][0]['c'][-3] = '5'::jsonb;
+select * from test_jsonb_subscript;
+
+update test_jsonb_subscript set test_json['d'][0]['b']['x'] = '6'::jsonb;
+select * from test_jsonb_subscript;
+
+update test_jsonb_subscript set test_json['e']['y'] = '7'::jsonb;
+select * from test_jsonb_subscript;
+
+-- updating of scalar's subscripts
+update test_jsonb_subscript set test_json = '1';
+update test_jsonb_subscript set test_json['a'] = '1'::jsonb;
+update test_jsonb_subscript set test_json[0] = '1'::jsonb;
+
+update test_jsonb_subscript set test_json = '{"a": 1}';
+update test_jsonb_subscript set test_json['a']['a'] = '1'::jsonb;
+update test_jsonb_subscript set test_json['a'][0] = '1'::jsonb;
+
+select * from test_jsonb_subscript;
+
 -- jsonb to tsvector
 select to_tsvector('{"a": "aaa bbb ddd ccc", "b": ["eee fff ggg"], "c": {"d": "hhh iii"}}'::jsonb);
 
-- 
2.7.4