From fdfd81329bda742b494490d9dd814a2bc8fc807d Mon Sep 17 00:00:00 2001
From: Dmitrii Dolgov <9erthalion6@gmail.com>
Date: Thu, 19 Dec 2019 14:08:59 +0100
Subject: [PATCH v29 5/6] Polymorphic subscripting

To improve performance and compliance with the SQL standard (in regards
how float indexes are considered), interpret each subscript expression
variant depending on the result of previous subscripting step. There are
two variants of jsonb subscript expressions - the first is casted to
text and the second is casted to int4.  Executor at each subscripting
step selects which variant to execute by calling callback
jsonb_subscript_selectexpr(). To manage the subscripting state, another
callback jsonb_subscript_step() was introduced along with the new field
SubscriptingRefState.privatedata.

Author: Nikita Glukhov
---
 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     | 785 ++++++++++++++++++++------
 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, 1211 insertions(+), 254 deletions(-)

diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index 37aad3c625..cc941d87b8 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 df11ac6715..2da8648f38 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -410,6 +410,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,
@@ -1387,6 +1389,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 */
@@ -3144,6 +3169,46 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 	*op->resnull = false;
 }
 
+/*
+ * 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.
  *
@@ -3159,8 +3224,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)
@@ -3173,15 +3240,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;
 }
 
@@ -3199,7 +3275,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 fa0c33db80..e1d5aed07c 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 7be928fe2f..6866902f34 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 408ca7d9b2..a5120d005e 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,362 @@ 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;
+	}
+}
+
+/* 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;
+		}
+	}
+	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 +1768,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 +1810,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 +1847,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 +1898,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 +5013,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 +5030,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;
+		JsonbValue	val;
 
-		newkey.type = jbvString;
-		newkey.val.string.len = VARSIZE_ANY_EXHDR(path_elems[level]);
-		newkey.val.string.val = VARDATA_ANY(path_elems[level]);
+		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 */
 
-		(void) pushJsonbValue(st, WJB_KEY, &newkey);
-		(void) pushJsonbValue(st, WJB_VALUE, newval);
-	}
-
-	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 +5161,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 +5270,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 +5285,51 @@ 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 && 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));
 }
 
 /*
@@ -4963,12 +5349,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);
 }
@@ -4991,19 +5380,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,
@@ -5012,18 +5419,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 fb5947ae73..c337d567bf 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -783,7 +783,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 f9cf0af117..9a8e8cb4fb 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 1800d5ecf5..d752bf6490 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 6081fabb74..ec6dae1f7a 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 c7d7e30b45..8b403aa2a2 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", 8]}
+  2 | {"a": [1, 2, 3, "4", 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, 5, 3, "4", 8]}
+  2 | {"a": [1, 5, 3, "4", 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, 1, 5, 3, "4", 8]}
+  2 | {"a": [6, 1, 5, 3, "4", 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, 1, 5, 3, "4", 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, 1, 5, 3, "4", 8], "another_key": null}
+  2 | {"a": [6, 1, 5, 3, "4", 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": [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": [4], "c": [5]}]}
+(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": [4], "b": {"x": 6}, "c": [5]}]}
+(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": [4], "b": {"x": 6}, "c": [5]}], "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 934fa9d67a..f0d9c51b3a 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.21.0

