/****************** bsonudf C++ Program Source Code File (.CPP) ******************/
/*  PROGRAM NAME: bsonudf     Version 1.0                                        */
/*  (C) Copyright to the author Olivier BERTRAND          2020                   */
/*  This program are the BSON User Defined Functions.                            */
/*********************************************************************************/

/*********************************************************************************/
/*  Include relevant sections of the MariaDB header file.                        */
/*********************************************************************************/
#include <my_global.h>
#include <mysqld.h>
#include <mysql.h>
#include <sql_error.h>
#include <stdio.h>

#include "bsonudf.h"

#if defined(UNIX) || defined(UNIV_LINUX)
#define _O_RDONLY O_RDONLY
#endif

#define MEMFIX  4096
#if defined(connect_EXPORTS)
#define PUSH_WARNING(M) push_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, 0, M)
#else
#define PUSH_WARNING(M) htrc(M)
#endif
#define M 6

/* --------------------------------- JSON UDF ---------------------------------- */

/*********************************************************************************/
/*  Program for saving the status of the memory pools.                           */
/*********************************************************************************/
inline void JsonMemSave(PGLOBAL g) {
	g->Saved_Size = ((PPOOLHEADER)g->Sarea)->To_Free;
} /* end of JsonMemSave */

/*********************************************************************************/
/*  Program for freeing the memory pools.                                        */
/*********************************************************************************/
inline void JsonFreeMem(PGLOBAL g) {
	g->Activityp = NULL;
	g = PlugExit(g);
} /* end of JsonFreeMem */

/* --------------------------- New Testing BJSON Stuff --------------------------*/

/*********************************************************************************/
/*  SubAlloc a new BJNX class with protection against memory exhaustion.         */
/*********************************************************************************/
static PBJNX BjnxNew(PGLOBAL g, PBVAL vlp, int type, int len)
{
	PBJNX bjnx;

	try {
		bjnx = new(g) BJNX(g, vlp, type, len);
	} catch (...) {
		if (trace(1023))
			htrc("%s\n", g->Message);

		PUSH_WARNING(g->Message);
		bjnx = NULL;
	}	// end try/catch

	return bjnx;
} /* end of BjnxNew */

/* ----------------------------------- BSNX ------------------------------------ */

/*********************************************************************************/
/*  BSNX public constructor.                                                     */
/*********************************************************************************/
BJNX::BJNX(PGLOBAL g, PBVAL row, int type, int len, int prec, my_bool wr) : BDOC(g)
{
	Row = row;
	Bvalp = NULL;
	Jpnp = NULL;
	Jp = NULL;
	Nodes = NULL;
	Value = AllocateValue(g, type, len, prec);
	MulVal = NULL;
	Jpath = NULL;
	Buf_Type = type;
	Long = len;
	Prec = prec;
	Nod = 0;
	Xnod = -1;
	K = 0;
	I = -1;
	Imax = 9;
	B = 0;
	Xpd = false;
	Parsed = false;
	Found = false;
	Wr = wr;
	Jb = false;
} // end of BJNX constructor

/*********************************************************************************/
/*  SetJpath: set and parse the json path.                                       */
/*********************************************************************************/
my_bool BJNX::SetJpath(PGLOBAL g, char* path, my_bool jb)
{
	// Check Value was allocated
	if (!Value)
		return true;

	Value->SetNullable(true);
	Jpath = path;

	// Parse the json path
	Parsed = false;
	Nod = 0;
	Jb = jb;
	return ParseJpath(g);
} // end of SetJpath

/*********************************************************************************/
/*  Analyse array processing options.                                            */
/*********************************************************************************/
my_bool BJNX::SetArrayOptions(PGLOBAL g, char* p, int i, PSZ nm)
{
	int     n = (int)strlen(p);
	my_bool dg = true, b = false;
	PJNODE  jnp = &Nodes[i];

	if (*p) {
		if (p[n - 1] == ']') {
			p[--n] = 0;
		} else if (!IsNum(p)) {
			// Wrong array specification
			sprintf(g->Message, "Invalid array specification %s", p);
			return true;
		} // endif p

	} else
		b = true;

	// To check whether a numeric Rank was specified
	dg = IsNum(p);

	if (!n) {
		// Default specifications
		if (jnp->Op != OP_EXP) {
			if (Wr) {
				// Force append
				jnp->Rank = INT_MAX32;
				jnp->Op = OP_LE;
			} else if (Jb) {
				// Return a Json item
				jnp->Op = OP_XX;
			} else if (b) {
				// Return 1st value (B is the index base)
				jnp->Rank = B;
				jnp->Op = OP_LE;
			} else if (!Value->IsTypeNum()) {
				jnp->CncVal = AllocateValue(g, PlugDup(g, ", "), TYPE_STRING);
				jnp->Op = OP_CNC;
			} else
				jnp->Op = OP_ADD;

		} // endif OP

	} else if (dg) {
		// Return nth value
		jnp->Rank = atoi(p) - B;
		jnp->Op = OP_EQ;
	} else if (Wr) {
		sprintf(g->Message, "Invalid specification %s in a write path", p);
		return true;
	} else if (n == 1) {
		// Set the Op value;
		switch (*p) {
		case '+': jnp->Op = OP_ADD;  break;
		case 'x': jnp->Op = OP_MULT; break;
		case '>': jnp->Op = OP_MAX;  break;
		case '<': jnp->Op = OP_MIN;  break;
		case '!': jnp->Op = OP_SEP;  break; // Average
		case '#': jnp->Op = OP_NUM;  break;
		case '*': // Expand this array
			strcpy(g->Message, "Expand not supported by this function");
			return true;
		default:
			sprintf(g->Message, "Invalid function specification %c", *p);
			return true;
		} // endswitch *p

	} else if (*p == '"' && p[n - 1] == '"') {
		// This is a concat specification
		jnp->Op = OP_CNC;

		if (n > 2) {
			// Set concat intermediate string
			p[n - 1] = 0;

			if (trace(1))
				htrc("Concat string=%s\n", p + 1);

			jnp->CncVal = AllocateValue(g, p + 1, TYPE_STRING);
		} // endif n

	} else {
		strcpy(g->Message, "Wrong array specification");
		return true;
	} // endif's

	// For calculated arrays, a local Value must be used
	switch (jnp->Op) {
	case OP_NUM:
		jnp->Valp = AllocateValue(g, TYPE_INT);
		break;
	case OP_ADD:
	case OP_MULT:
	case OP_SEP:
		if (!IsTypeChar(Buf_Type))
			jnp->Valp = AllocateValue(g, Buf_Type, 0, GetPrecision());
		else
			jnp->Valp = AllocateValue(g, TYPE_DOUBLE, 0, 2);

		break;
	case OP_MIN:
	case OP_MAX:
		jnp->Valp = AllocateValue(g, Buf_Type, Long, GetPrecision());
		break;
	case OP_CNC:
		if (IsTypeChar(Buf_Type))
			jnp->Valp = AllocateValue(g, TYPE_STRING, Long, GetPrecision());
		else
			jnp->Valp = AllocateValue(g, TYPE_STRING, 512);

		break;
	default:
		break;
	} // endswitch Op

	if (jnp->Valp)
		MulVal = AllocateValue(g, jnp->Valp);

	return false;
} // end of SetArrayOptions

/*********************************************************************************/
/*  Parse the eventual passed Jpath information.                                 */
/*  This information can be specified in the Fieldfmt column option when         */
/*  creating the table. It permits to indicate the position of the node          */
/*  corresponding to that column.                                                */
/*********************************************************************************/
my_bool BJNX::ParseJpath(PGLOBAL g)
{
	char* p, * p1 = NULL, * p2 = NULL, * pbuf = NULL;
	int     i;
	my_bool a, mul = false;

	if (Parsed)
		return false;                       // Already done
	else if (!Jpath)
		//	Jpath = Name;
		return true;

	if (trace(1))
		htrc("ParseJpath %s\n", SVP(Jpath));

	if (!(pbuf = PlgDBDup(g, Jpath)))
		return true;

	if (*pbuf == '$') pbuf++;
	if (*pbuf == '.') pbuf++;
	if (*pbuf == '[') p1 = pbuf++;

	// Estimate the required number of nodes
	for (i = 0, p = pbuf; (p = NextChr(p, '.')); i++, p++)
		Nod++;                         // One path node found

	if (!(Nodes = (PJNODE)PlgDBSubAlloc(g, NULL, (++Nod) * sizeof(JNODE))))
		return true;

	memset(Nodes, 0, (Nod) * sizeof(JNODE));

	// Analyze the Jpath for this column
	for (i = 0, p = pbuf; p && i < Nod; i++, p = (p2 ? p2 : NULL)) {
		a = (p1 != NULL);
		p1 = strchr(p, '[');
		p2 = strchr(p, '.');

		if (!p2)
			p2 = p1;
		else if (p1) {
			if (p1 < p2)
				p2 = p1;
			else if (p1 == p2 + 1)
				*p2++ = 0;		 // Old syntax .[
			else
				p1 = NULL;

		}	// endif p1

		if (p2)
			*p2++ = 0;

		// Jpath must be explicit
		if (a || *p == 0 || *p == '[' || IsNum(p)) {
			// Analyse intermediate array processing
			if (SetArrayOptions(g, p, i, Nodes[i - 1].Key))
				return true;

		} else if (*p == '*') {
			if (Wr) {
				sprintf(g->Message, "Invalid specification %c in a write path", *p);
				return true;
			} else     			// Return JSON
				Nodes[i].Op = OP_XX;

		} else {
			Nodes[i].Key = p;
			Nodes[i].Op = OP_EXIST;
		} // endif's

	} // endfor i, p

	Nod = i;
	MulVal = AllocateValue(g, Value);

	if (trace(1))
		for (i = 0; i < Nod; i++)
			htrc("Node(%d) Key=%s Op=%d Rank=%d\n",
				i, SVP(Nodes[i].Key), Nodes[i].Op, Nodes[i].Rank);

	Parsed = true;
	return false;
} // end of ParseJpath

/*********************************************************************************/
/*  MakeJson: Serialize the json item and set value to it.                       */
/*********************************************************************************/
PVAL BJNX::MakeJson(PGLOBAL g, PBVAL bvp)
{
	if (Value->IsTypeNum()) {
		strcpy(g->Message, "Cannot make Json for a numeric value");
		Value->Reset();
	} else if (bvp->Type != TYPE_JAR && bvp->Type != TYPE_JOB) {
		strcpy(g->Message, "Target is not an array or object");
		Value->Reset();
	} else
		Value->SetValue_psz(Serialize(g, bvp, NULL, 0));

	return Value;
} // end of MakeJson

/*********************************************************************************/
/*  SetValue: Set a value from a JVALUE contains.                                */
/*********************************************************************************/
void BJNX::SetJsonValue(PGLOBAL g, PVAL vp, PBVAL vlp)
{
	if (vlp) {
		vp->SetNull(false);

		if (Jb) {
			vp->SetValue_psz(Serialize(g, vlp, NULL, 0));
		} else switch (vlp->Type) {
		case TYPE_DTM:
		case TYPE_STRG:
			vp->SetValue_psz(GetString(vlp));
			break;
		case TYPE_INTG:
		case TYPE_BINT:
			vp->SetValue(GetInteger(vlp));
			break;
		case TYPE_DBL:
			if (vp->IsTypeNum())
				vp->SetValue(GetDouble(vlp));
			else // Get the proper number of decimals
				vp->SetValue_psz(GetString(vlp));

			break;
		case TYPE_BOOL:
			if (vp->IsTypeNum())
				vp->SetValue(GetInteger(vlp) ? 1 : 0);
			else
				vp->SetValue_psz(GetString(vlp));

			break;
		case TYPE_JAR:
			vp->SetValue_psz(GetArrayText(g, MVP(vlp->To_Val), NULL));
			break;
		case TYPE_JOB:
			vp->SetValue_psz(GetObjectText(g, MPP(vlp->To_Val), NULL));
			break;
		case TYPE_NULL:
			vp->SetNull(true);
		default:
			vp->Reset();
		} // endswitch Type

	} else {
		vp->SetNull(true);
		vp->Reset();
	} // endif val

} // end of SetJsonValue

/*********************************************************************************/
/*  GetJson:                                                                     */
/*********************************************************************************/
PBVAL BJNX::GetJson(PGLOBAL g)
{
	return GetRowValue(g, Row, 0);
} // end of GetJson

/*********************************************************************************/
/*  ReadValue:                                                                   */
/*********************************************************************************/
void BJNX::ReadValue(PGLOBAL g)
{
	Value->SetValue_pval(GetColumnValue(g, Row, 0));
} // end of ReadValue

/*********************************************************************************/
/*  GetColumnValue:                                                              */
/*********************************************************************************/
PVAL BJNX::GetColumnValue(PGLOBAL g, PBVAL row, int i)
{
	PBVAL vlp = GetRowValue(g, row, i);

	SetJsonValue(g, Value, vlp);
	return Value;
} // end of GetColumnValue

/*********************************************************************************/
/*  GetRowValue:                                                                 */
/*********************************************************************************/
PBVAL BJNX::GetRowValue(PGLOBAL g, PBVAL row, int i, my_bool b)
{
	my_bool expd = false;
	PBVAL   bap;
	PBVAL   vlp = NULL;

	for (; i < Nod && row; i++) {
		if (Nodes[i].Op == OP_NUM) {
			Value->SetValue(row->Type == TYPE_JAR ? GetArraySize(MVP(row->To_Val)) : 1);
			vlp = SubAllocVal(Value);
			return vlp;
		} else if (Nodes[i].Op == OP_XX) {
			Jb = b;
			//		return DupVal(g, row);
			return row;		 // or last line ???
		} else switch (row->Type) {
		case TYPE_JOB:
			if (!Nodes[i].Key) {
				// Expected Array was not there
				if (Nodes[i].Op == OP_LE) {
					if (i < Nod - 1)
						continue;
					else
						vlp = row;  // DupVal(g, row) ???

				} else {
					strcpy(g->Message, "Unexpected object");
					vlp = NULL;
				} //endif Op

			} else
				vlp = GetKeyValue(MPP(row->To_Val), Nodes[i].Key);

			break;
		case TYPE_JAR:
			bap = MVP(row->To_Val);

			if (!Nodes[i].Key) {
				if (Nodes[i].Op == OP_EQ || Nodes[i].Op == OP_LE)
					vlp = GetArrayValue(bap, Nodes[i].Rank);
				else if (Nodes[i].Op == OP_EXP)
					return (PBVAL)ExpandArray(g, bap, i);
				else
					return SubAllocVal(CalculateArray(g, bap, i));

			} else {
				// Unexpected array, unwrap it as [0]
				vlp = GetArrayValue(bap, 0);
				i--;
			}	// endif's

			break;
		case TYPE_JVAL:
			vlp = row;
			break;
		default:
			sprintf(g->Message, "Invalid row JSON type %d", row->Type);
			vlp = NULL;
		} // endswitch Type

		row = vlp;
	} // endfor i

	return vlp;
} // end of GetRowValue

/*********************************************************************************/
/*  ExpandArray:                                                                 */
/*********************************************************************************/
PVAL BJNX::ExpandArray(PGLOBAL g, PBVAL arp, int n)
{
	strcpy(g->Message, "Expand cannot be done by this function");
	return NULL;
} // end of ExpandArray

/*********************************************************************************/
/*  CalculateArray: NIY                                                          */
/*********************************************************************************/
PVAL BJNX::CalculateArray(PGLOBAL g, PBVAL bap, int n)
{
#if 0
	int     i, ars = GetArraySize(bap), nv = 0;
	bool    err;
	OPVAL   op = Nodes[n].Op;
	PVAL    val[2], vp = Nodes[n].Valp;
	PBVAL   bvrp, bvp;
	BVAL    bval;

	vp->Reset();
	xtrc(1, "CalculateArray size=%d op=%d\n", ars, op);

	for (i = 0; i < ars; i++) {
		bvrp = GetArrayValue(bap, i);
		xtrc(1, "i=%d nv=%d\n", i, nv);

		if (!IsValueNull(bvrp) || (op == OP_CNC && GetJsonNull())) {
			if (IsValueNull(bvrp)) {
				SetString(bvrp, GetJsonNull(), 0);
				bvp = bvrp;
			} else if (n < Nod - 1 && bvrp->GetJson()) {
				bval.SetValue(g, GetColumnValue(g, jvrp->GetJson(), n + 1));
				bvp = &bval;
			} else
				jvp = jvrp;

			if (trace(1))
				htrc("jvp=%s null=%d\n",
					jvp->GetString(g), jvp->IsNull() ? 1 : 0);

			if (!nv++) {
				SetJsonValue(g, vp, jvp);
				continue;
			} else
				SetJsonValue(g, MulVal, jvp);

			if (!MulVal->IsNull()) {
				switch (op) {
				case OP_CNC:
					if (Nodes[n].CncVal) {
						val[0] = Nodes[n].CncVal;
						err = vp->Compute(g, val, 1, op);
					} // endif CncVal

					val[0] = MulVal;
					err = vp->Compute(g, val, 1, op);
					break;
					//        case OP_NUM:
				case OP_SEP:
					val[0] = Nodes[n].Valp;
					val[1] = MulVal;
					err = vp->Compute(g, val, 2, OP_ADD);
					break;
				default:
					val[0] = Nodes[n].Valp;
					val[1] = MulVal;
					err = vp->Compute(g, val, 2, op);
				} // endswitch Op

				if (err)
					vp->Reset();

				if (trace(1)) {
					char buf(32);

					htrc("vp='%s' err=%d\n",
						vp->GetCharString(&buf), err ? 1 : 0);
				} // endif trace

			} // endif Zero

		}	// endif jvrp

	} // endfor i

	if (op == OP_SEP) {
		// Calculate average
		MulVal->SetValue(nv);
		val[0] = vp;
		val[1] = MulVal;

		if (vp->Compute(g, val, 2, OP_DIV))
			vp->Reset();

	} // endif Op

	return vp;
#else
	strcpy(g->Message, "Calculate array NIY");
	return NULL;
#endif
} // end of CalculateArray

/*********************************************************************************/
/* CheckPath: Checks whether the path exists in the document.                    */
/*********************************************************************************/
my_bool BJNX::CheckPath(PGLOBAL g)
{
	PBVAL   val = NULL;
	PBVAL   row = Row;

	for (int i = 0; i < Nod && row; i++) {
		val = NULL;

		if (Nodes[i].Op == OP_NUM || Nodes[i].Op == OP_XX) {
		} else switch (row->Type) {
		case TYPE_JOB:
			if (Nodes[i].Key)
				val = GetKeyValue(MPP(row->To_Val), Nodes[i].Key);

			break;
		case TYPE_JAR:
			if (!Nodes[i].Key)
				if (Nodes[i].Op == OP_EQ || Nodes[i].Op == OP_LE)
					val = GetArrayValue(MVP(row->To_Val), Nodes[i].Rank);

			break;
		case TYPE_JVAL:
			val = MVP(row->To_Val);
			break;
		default:
			sprintf(g->Message, "Invalid row JSON type %d", row->Type);
		} // endswitch Type

//		if (i < Nod - 1)
//			if (!(row = (val) ? val->GetJsp() : NULL))
//				val = NULL;

		row = val;
	} // endfor i

	return (val != NULL);
} // end of CheckPath

/***********************************************************************/
/*  GetRow: Set the complete path of the object to be set.             */
/***********************************************************************/
PBVAL BJNX::GetRow(PGLOBAL g)
{
	PBVAL val = NULL;
	PBVAL arp;
	PBVAL nwr, row = Row;

	for (int i = 0; i < Nod - 1 && row; i++) {
		if (Nodes[i].Op == OP_XX)
			break;
		else switch (row->Type) {
		case TYPE_JOB:
			if (!Nodes[i].Key)
				// Expected Array was not there, wrap the value
				continue;

			val = GetKeyValue(MPP(row->To_Val), Nodes[i].Key);
			break;
		case TYPE_JAR:
			arp = MVP(row->To_Val);

			if (!Nodes[i].Key) {
				if (Nodes[i].Op == OP_EQ)
					val = GetArrayValue(arp, Nodes[i].Rank);
				else
					val = GetArrayValue(arp, Nodes[i].Rx);

			} else {
				// Unexpected array, unwrap it as [0]
				val = GetArrayValue(arp, 0);
				i--;
			} // endif Nodes

			break;
		case TYPE_JVAL:
			val = MVP(row->To_Val);
			break;
		default:
			sprintf(g->Message, "Invalid row JSON type %d", row->Type);
			val = NULL;
		} // endswitch Type

		if (val) {
			row = val;
		} else {
			// Construct missing objects
			for (i++; row && i < Nod; i++) {
				if (Nodes[i].Op == OP_XX)
					break;
				//				else if (!Nodes[i].Key)
									// Construct intermediate array
				//					nwr = SubAllocVal(g);
				//				else
				//					nwr = SubAllocPair(g);

								// Construct new row
				nwr = NewVal();

				if (row->Type == TYPE_JOB) {
					SetKeyValue(MPP(row->To_Val), MOF(nwr), Nodes[i - 1].Key);
				} else if (row->Type == TYPE_JAR) {
					AddArrayValue(MVP(row->To_Val), nwr);
				} else {
					strcpy(g->Message, "Wrong type when writing new row");
					nwr = NULL;
				} // endif's

				row = nwr;
			} // endfor i

			break;
		} // endelse

	} // endfor i

	return row;
} // end of GetRow

/***********************************************************************/
/*  WriteValue:                                                        */
/***********************************************************************/
my_bool BJNX::WriteValue(PGLOBAL g, PBVAL jvalp)
{
	PBPR  objp = NULL;
	PBVAL arp = NULL;
	PBVAL jvp = NULL;
	PBVAL row = GetRow(g);

	if (!row)
		return true;

	switch (row->Type) {
	case TYPE_JOB:  objp = MPP(row->To_Val); break;
	case TYPE_JAR:  arp = MVP(row->To_Val);  break;
	case TYPE_JVAL: jvp = MVP(row->To_Val);  break;
	default:
		strcpy(g->Message, "Invalid target type");
		return true;
	} // endswitch Type

	if (arp) {
		if (!Nodes[Nod - 1].Key) {
			if (Nodes[Nod - 1].Op == OP_EQ)
				SetArrayValue(arp, jvalp, Nodes[Nod - 1].Rank);
			else
				AddArrayValue(arp, jvalp);

		}	// endif Key

	} else if (objp) {
		if (Nodes[Nod - 1].Key)
			SetKeyValue(objp, MOF(jvalp), Nodes[Nod - 1].Key);

	} else if (jvp)
		SetValueVal(jvp, jvalp);

	return false;
} // end of WriteValue

/*********************************************************************************/
/*  Locate a value in a JSON tree:                                               */
/*********************************************************************************/
PSZ BJNX::Locate(PGLOBAL g, PBVAL jsp, PBVAL jvp, int k)
{
	PSZ     str = NULL;
	my_bool b = false, err = true;

	g->Message[0] = 0;

	if (!jsp) {
		strcpy(g->Message, "Null json tree");
		return NULL;
	} // endif jsp

	try {
		// Write to the path string
		Jp = new(g) JOUTSTR(g);
		Jp->WriteChr('$');
		Bvalp = jvp;
		K = k;

		switch (jsp->Type) {
		case TYPE_JAR:
			err = LocateArray(g, MVP(jsp->To_Val));
			break;
		case TYPE_JOB:
			err = LocateObject(g, MPP(jsp->To_Val));
			break;
		case TYPE_JVAL:
			err = LocateValue(g, MVP(jsp->To_Val));
			break;
		default:
			err = true;
		} // endswitch Type

		if (err) {
			if (!g->Message[0])
				strcpy(g->Message, "Invalid json tree");

		} else if (Found) {
			Jp->WriteChr('\0');
			PlugSubAlloc(g, NULL, Jp->N);
			str = Jp->Strp;
		} // endif's

	} catch (int n) {
		if (trace(1))
			htrc("Exception %d: %s\n", n, g->Message);

		PUSH_WARNING(g->Message);
	} catch (const char* msg) {
		strcpy(g->Message, msg);
	} // end catch

	return str;
} // end of Locate

/*********************************************************************************/
/*  Locate in a JSON Array.                                                      */
/*********************************************************************************/
my_bool BJNX::LocateArray(PGLOBAL g, PBVAL jarp)
{
	char   s[16];
	int    n = GetArraySize(jarp);
	size_t m = Jp->N;

	for (int i = 0; i < n && !Found; i++) {
		Jp->N = m;
		sprintf(s, "[%d]", i + B);

		if (Jp->WriteStr(s))
			return true;

		if (LocateValue(g, GetArrayValue(jarp, i)))
			return true;

	} // endfor i

	return false;
} // end of LocateArray

/*********************************************************************************/
/*  Locate in a JSON Object.                                                     */
/*********************************************************************************/
my_bool BJNX::LocateObject(PGLOBAL g, PBPR jobp)
{
	size_t m;

	if (Jp->WriteChr('.'))
		return true;

	m = Jp->N;

	for (PBPR pair = jobp; pair && !Found; pair = MPP(pair->Next)) {
		Jp->N = m;

		if (Jp->WriteStr(MZP(pair->Key)))
			return true;

		if (LocateValue(g, MVP(pair->Vlp)))
			return true;

	} // endfor i

	return false;
} // end of LocateObject

/*********************************************************************************/
/*  Locate a JSON Value.                                                         */
/*********************************************************************************/
my_bool BJNX::LocateValue(PGLOBAL g, PBVAL jvp)
{
	if (CompareTree(g, Bvalp, jvp))
		Found = (--K == 0);
	else if (jvp->Type == TYPE_JAR)
		return LocateArray(g, GetArray(jvp));
	else if (jvp->Type == TYPE_JOB)
		return LocateObject(g, GetObject(jvp));

	return false;
} // end of LocateValue

/*********************************************************************************/
/*  Locate all occurrences of a value in a JSON tree:                            */
/*********************************************************************************/
PSZ BJNX::LocateAll(PGLOBAL g, PBVAL jsp, PBVAL bvp, int mx)
{
	PSZ     str = NULL;
	my_bool b = false, err = true;
	PJPN    jnp;

	if (!jsp) {
		strcpy(g->Message, "Null json tree");
		return NULL;
	} // endif jsp

	try {
		jnp = (PJPN)PlugSubAlloc(g, NULL, sizeof(JPN) * mx);
		memset(jnp, 0, sizeof(JPN) * mx);
		g->Message[0] = 0;

		// Write to the path string
		Jp = new(g)JOUTSTR(g);
		Bvalp = bvp;
		Imax = mx - 1;
		Jpnp = jnp;
		Jp->WriteChr('[');

		switch (jsp->Type) {
		case TYPE_JAR:
			err = LocateArrayAll(g, MVP(jsp->To_Val));
			break;
		case TYPE_JOB:
			err = LocateObjectAll(g, MPP(jsp->To_Val));
			break;
		case TYPE_JVAL:
			err = LocateValueAll(g, MVP(jsp->To_Val));
			break;
		default:
			err = LocateValueAll(g, jsp);
		} // endswitch Type

		if (!err) {
			if (Jp->N > 1)
				Jp->N--;

			Jp->WriteChr(']');
			Jp->WriteChr('\0');
			PlugSubAlloc(g, NULL, Jp->N);
			str = Jp->Strp;
		} else if (!g->Message[0])
			strcpy(g->Message, "Invalid json tree");

	} catch (int n) {
		xtrc(1, "Exception %d: %s\n", n, g->Message);
		PUSH_WARNING(g->Message);
	} catch (const char* msg) {
		strcpy(g->Message, msg);
	} // end catch

	return str;
} // end of LocateAll

/*********************************************************************************/
/*  Locate in a JSON Array.                                                      */
/*********************************************************************************/
my_bool BJNX::LocateArrayAll(PGLOBAL g, PBVAL jarp)
{
	int i = 0;

	if (I < Imax) {
		Jpnp[++I].Type = TYPE_JAR;

		for (PBVAL vp = jarp; vp; vp = MVP(vp->Next)) {
			Jpnp[I].N = i;

			if (LocateValueAll(g, GetArrayValue(jarp, i)))
				return true;

			i++;
		} // endfor i

		I--;
	} // endif I

	return false;
} // end of LocateArrayAll

/*********************************************************************************/
/*  Locate in a JSON Object.                                                     */
/*********************************************************************************/
my_bool BJNX::LocateObjectAll(PGLOBAL g, PBPR jobp)
{
	if (I < Imax) {
		Jpnp[++I].Type = TYPE_JOB;

		for (PBPR pair = jobp; pair; pair = MPP(pair->Next)) {
			Jpnp[I].Key = MZP(pair->Key);

			if (LocateValueAll(g, MVP(pair->Vlp)))
				return true;

		} // endfor i

		I--;
	} // endif I

	return false;
} // end of LocateObjectAll

/*********************************************************************************/
/*  Locate a JSON Value.                                                         */
/*********************************************************************************/
my_bool BJNX::LocateValueAll(PGLOBAL g, PBVAL jvp)
{
	if (CompareTree(g, Bvalp, jvp))
		return AddPath();
	else if (jvp->Type == TYPE_JAR)
		return LocateArrayAll(g, GetArray(jvp));
	else if (jvp->Type == TYPE_JOB)
		return LocateObjectAll(g, GetObject(jvp));

	return false;
} // end of LocateValueAll

/*********************************************************************************/
/*  Compare two JSON trees.                                                      */
/*********************************************************************************/
my_bool BJNX::CompareTree(PGLOBAL g, PBVAL jp1, PBVAL jp2)
{
	if (!jp1 || !jp2 || jp1->Type != jp2->Type || GetSize(jp1) != GetSize(jp2))
		return false;

	my_bool found = true;

	if (jp1->Type == TYPE_JAR) {
		for (int i = 0; found && i < GetArraySize(jp1); i++)
			found = (CompareValues(g, GetArrayValue(jp1, i), GetArrayValue(jp2, i)));

	} else if (jp1->Type == TYPE_JOB) {
		PBPR p1 = MPP(jp1->To_Val), p2 = MPP(jp2->To_Val);

		// Keys can be differently ordered
		for (; found && p1 && p2; p1 = MPP(p1->Next))
			found = CompareValues(g, MVP(p1->Vlp), GetKeyValue(p2, MZP(p1->Key)));

	} else if (jp1->Type == TYPE_JVAL) {
		found = CompareTree(g, MVP(jp1->To_Val), (MVP(jp2->To_Val)));
	} else
		found = CompareValues(g, jp1, jp2);

	return found;
} // end of CompareTree

/*********************************************************************************/
/*  Compare two VAL values and return true if they are equal.                    */
/*********************************************************************************/
my_bool BJNX::CompareValues(PGLOBAL g, PBVAL v1, PBVAL v2)
{
	my_bool b = false;

	if (v1 && v2)
		switch (v1->Type) {
		case TYPE_JAR:
			if (v2->Type == TYPE_JAR)
				b = CompareTree(g, MVP(v1->To_Val), MVP(v2->To_Val));

			break;
		case TYPE_STRG:
			if (v2->Type == TYPE_STRG) {
				if (v1->Nd || v2->Nd)		// Case insensitive
					b = (!stricmp(MZP(v1->To_Val), MZP(v2->To_Val)));
				else
					b = (!strcmp(MZP(v1->To_Val), MZP(v2->To_Val)));

			} // endif Type

			break;
		case TYPE_DTM:
			if (v2->Type == TYPE_DTM)
				b = (!strcmp(MZP(v1->To_Val), MZP(v2->To_Val)));

			break;
		case TYPE_INTG:
			if (v2->Type == TYPE_INTG)
				b = (v1->N == v2->N);
			else if (v2->Type == TYPE_BINT)
				b = ((longlong)v1->N == LLN(v2->To_Val));

			break;
		case TYPE_BINT:
			if (v2->Type == TYPE_INTG)
				b = (LLN(v1->To_Val) == (longlong)v2->N);
			else if (v2->Type == TYPE_BINT)
				b = (LLN(v1->To_Val) == LLN(v2->To_Val));

			break;
		case TYPE_FLOAT:
			if (v2->Type == TYPE_FLOAT)
				b = (v1->F == v2->F);
			else if (v2->Type == TYPE_DBL)
				b = ((double)v1->F == DBL(v2->To_Val));

			break;
		case TYPE_DBL:
			if (v2->Type == TYPE_DBL)
				b = (DBL(v1->To_Val) == DBL(v2->To_Val));
			else if (v2->Type == TYPE_FLOAT)
				b = (DBL(v1->To_Val) == (double)v2->F);

			break;
		case TYPE_BOOL:
			if (v2->Type == TYPE_BOOL)
				b = (v1->B == v2->B);

			break;
		case TYPE_NULL:
			b = (v2->Type == TYPE_NULL);
			break;
		default:
			break;
		}	// endswitch Type

	else
		b = (!v1 && !v2);

	return b;
} // end of CompareValues

/*********************************************************************************/
/*  Add the found path to the list.                                              */
/*********************************************************************************/
my_bool BJNX::AddPath(void)
{
	char s[16];

	if (Jp->WriteStr("\"$"))
		return true;

	for (int i = 0; i <= I; i++) {
		if (Jpnp[i].Type == TYPE_JAR) {
			sprintf(s, "[%d]", Jpnp[i].N + B);

			if (Jp->WriteStr(s))
				return true;

		} else {
			if (Jp->WriteChr('.'))
				return true;

			if (Jp->WriteStr(Jpnp[i].Key))
				return true;

		}	// endif's

	}	// endfor i

	if (Jp->WriteStr("\","))
		return true;

	return false;
}	// end of AddPath

/* -----------------------------Utility functions ------------------------------ */

/*********************************************************************************/
/*  Make a BVAL value from the passed argument.                                  */
/*********************************************************************************/
static PBVAL MakeBinValue(PGLOBAL g, UDF_ARGS* args, uint i)
{
	char* sap = (args->arg_count > i) ? args->args[i] : NULL;
	int   n, len;
	int   ci;
	longlong bigint;
	BDOC  doc(g);
	PBVAL bp, bvp = doc.NewVal();

	if (sap) {
		if (args->arg_type[i] == STRING_RESULT) {
			if ((len = args->lengths[i])) {
				if ((n = IsJson(args, i)) < 3)
					sap = MakePSZ(g, args, i);

				if (n) {
					if (n == 2) {
						if (!(sap = GetJsonFile(g, sap))) {
							PUSH_WARNING(g->Message);
							return NULL;
						} // endif sap

						len = strlen(sap);
					} // endif 2

					if (!(bp = doc.ParseJson(g, sap, strlen(sap)))) {
						PUSH_WARNING(g->Message);
						return NULL;
					} else
						bvp = bp;

				} else {
					// Check whether this string is a valid json string
					JsonMemSave(g);

					if (!(bp = doc.ParseJson(g, sap, strlen(sap)))) {
						// Recover suballocated memory
						JsonSubSet(g);
						ci = (strnicmp(args->attributes[i], "ci", 2)) ? 0 : 1;
						doc.SetString(bvp, sap, ci);
					} else
						bvp = bp;

					g->Saved_Size = 0;
				}	// endif n

			} // endif len

		} else switch (args->arg_type[i]) {
			case INT_RESULT:
				bigint = *(longlong*)sap;

				if ((bigint == 0LL && !strcmp(args->attributes[i], "FALSE")) ||
					  (bigint == 1LL && !strcmp(args->attributes[i], "TRUE")))
					doc.SetBool(bvp, (bool)bigint);
				else
					doc.SetBigint(bvp, bigint);

				break;
			case REAL_RESULT:
				doc.SetFloat(bvp, *(double*)sap);
				break;
			case DECIMAL_RESULT:
				doc.SetFloat(bvp, atof(MakePSZ(g, args, i)));
				break;
			case TIME_RESULT:
			case ROW_RESULT:
			default:
				bvp->Type = TYPE_UNKNOWN;
				break;
			} // endswitch arg_type

	} // endif sap

	return bvp;
} // end of MakeBinValue

/* ------------------------- Now the new Bin UDF's ----------------------------- */

/*********************************************************************************/
/*  Make a Json value containing the parameter.                                  */
/*********************************************************************************/
my_bool bsonvalue_init(UDF_INIT* initid, UDF_ARGS* args, char* message)
{
	unsigned long reslen, memlen;

	if (args->arg_count > 1) {
		strcpy(message, "Cannot accept more than 1 argument");
		return true;
	} else
		CalcLen(args, false, reslen, memlen);

	return JsonInit(initid, args, message, false, reslen, memlen);
} // end of bsonvalue_init

char* bsonvalue(UDF_INIT* initid, UDF_ARGS* args, char* result,
	unsigned long* res_length, char*, char*)
{
	char   *str;
	PGLOBAL g = (PGLOBAL)initid->ptr;

	if (!g->Xchk) {
		if (!CheckMemory(g, initid, args, 1, false)) {
			BDOC  doc(g);
			PBVAL bvp = MakeBinValue(g, args, 0);

			if (!(str = doc.Serialize(g, bvp, NULL, 0)))
				str = strcpy(result, g->Message);

		} else
			str = strcpy(result, g->Message);

		// Keep result of constant function
		g->Xchk = (initid->const_item) ? str : NULL;
	} else
		str = (char*)g->Xchk;

	*res_length = strlen(str);
	return str;
} // end of bsonValue

void bsonvalue_deinit(UDF_INIT* initid) {
	JsonFreeMem((PGLOBAL)initid->ptr);
} // end of bsonvalue_deinit

/*********************************************************************************/
/*  Make a Bson array containing all the parameters.                             */
/*********************************************************************************/
my_bool bson_make_array_init(UDF_INIT* initid, UDF_ARGS* args, char* message)
{
	unsigned long reslen, memlen;

	CalcLen(args, false, reslen, memlen);
	return JsonInit(initid, args, message, false, reslen, memlen);
} // end of bson_make_array_init

char* bson_make_array(UDF_INIT* initid, UDF_ARGS* args, char* result,
	unsigned long* res_length, char*, char*)
{
	char* str = NULL;
	PGLOBAL g = (PGLOBAL)initid->ptr;

	if (!g->Xchk) {
		if (!CheckMemory(g, initid, args, args->arg_count, false)) {
			BDOC  doc(g);
			PBVAL bvp = NULL, arp = NULL;

			for (uint i = 0; i < args->arg_count; i++)
				bvp = doc.AddArrayValue(bvp, MakeBinValue(g, args, i));

			arp = doc.SubAllocVal(bvp, TYPE_JAR);

			if (!(str = doc.Serialize(g, arp, NULL, 0)))
				str = strcpy(result, g->Message);

		} else
			str = strcpy(result, g->Message);

		// Keep result of constant function
		g->Xchk = (initid->const_item) ? str : NULL;
	} else
		str = (char*)g->Xchk;

	*res_length = strlen(str);
	return str;
} // end of bson_make_array

void bson_make_array_deinit(UDF_INIT* initid) {
	JsonFreeMem((PGLOBAL)initid->ptr);
} // end of bson_make_array_deinit

/*********************************************************************************/
/*  Add one or several values to a Bson array.                                   */
/*********************************************************************************/
my_bool bson_array_add_values_init(UDF_INIT* initid, UDF_ARGS* args, char* message) {
	unsigned long reslen, memlen;

	if (args->arg_count < 2) {
		strcpy(message, "This function must have at least 2 arguments");
		return true;
		//} else if (!IsJson(args, 0, true)) {
		//	strcpy(message, "First argument must be a valid json string or item");
		//	return true;
	} else
		CalcLen(args, false, reslen, memlen);

	if (!JsonInit(initid, args, message, true, reslen, memlen)) {
		PGLOBAL g = (PGLOBAL)initid->ptr;

		// This is a constant function
		g->N = (initid->const_item) ? 1 : 0;

		// This is to avoid double execution when using prepared statements
		if (IsJson(args, 0) > 1)
			initid->const_item = 0;

		return false;
	} else
		return true;

} // end of bson_array_add_values_init

char* bson_array_add_values(UDF_INIT* initid, UDF_ARGS* args, char* result,
	unsigned long* res_length, char* is_null, char*) {
	char* str = NULL;
	PGLOBAL g = (PGLOBAL)initid->ptr;

	if (!g->Xchk) {
		if (!CheckMemory(g, initid, args, args->arg_count, true)) {
			uint  n = 1;
			bool  b = false;
			BDOC  doc(g);
			PBVAL bvp = NULL, arp = MakeBinValue(g, args, 0);

			if (arp->Type == TYPE_JAR) {
				bvp = doc.GetArray(arp);
				b = !bvp;
			} else
				n = 0;

			for (uint i = n; i < args->arg_count; i++)
				bvp = doc.AddArrayValue(bvp, MakeBinValue(g, args, i));

			if (!n)
				arp = doc.SubAllocVal(bvp, TYPE_JAR);
			else if (b)
				doc.SetValueArr(arp, bvp);

//		str = MakeResult(g, args, top, args->arg_count);
			str = doc.Serialize(g, arp, NULL, 0);
		} // endif CheckMemory

		if (!str) {
			PUSH_WARNING(g->Message);
			str = args->args[0];
		}	// endif str

		// Keep result of constant function
		g->Xchk = (g->N) ? str : NULL;
	} else
		str = (char*)g->Xchk;

	if (!str) {
		*res_length = 0;
		*is_null = 1;
	} else
		*res_length = strlen(str);

	return str;
} // end of bson_array_add_values

void bson_array_add_values_deinit(UDF_INIT* initid) {
	JsonFreeMem((PGLOBAL)initid->ptr);
} // end of bson_array_add_values_deinit

/*********************************************************************************/
/*  Test BJSON parse and serialize.                                              */
/*********************************************************************************/
my_bool bson_test_init(UDF_INIT* initid, UDF_ARGS* args, char* message) {
	unsigned long reslen, memlen, more = 1000;

	if (args->arg_count == 0) {
		strcpy(message, "At least 1 argument required (json)");
		return true;
	} else if (!IsJson(args, 0) && args->arg_type[0] != STRING_RESULT) {
		strcpy(message, "First argument must be a json item");
		return true;
	} else
		CalcLen(args, false, reslen, memlen);

	return JsonInit(initid, args, message, true, reslen, memlen, more);
} // end of bson_test_init

char* bson_test(UDF_INIT* initid, UDF_ARGS* args, char* result,
	unsigned long* res_length, char* is_null, char* error) {
	char* str = NULL, * sap = NULL, * fn = NULL;
	int     pretty = 1;
	PBVAL   bvp;
	PGLOBAL g = (PGLOBAL)initid->ptr;
	BDOC    doc(g);

	if (g->N) {
		str = (char*)g->Activityp;
		goto err;
	} else if (initid->const_item)
		g->N = 1;

	try {
		if (!g->Xchk) {
			if (CheckMemory(g, initid, args, 1, !g->Xchk)) {
				PUSH_WARNING("CheckMemory error");
				*error = 1;
				goto err;
			} else if (!(bvp = MakeBinValue(g, args, 0))) {
				PUSH_WARNING(g->Message);
				goto err;
			}	// endif bvp

			if (g->Mrr) {			 // First argument is a constant
				g->Xchk = bvp;
				JsonMemSave(g);
			} // endif Mrr

		} else
			bvp = (PBVAL)g->Xchk;

		for (uint i = 1; i < args->arg_count; i++)
			if (args->arg_type[i] == STRING_RESULT)
				fn = args->args[i];
			else if (args->arg_type[i] == INT_RESULT)
				pretty = (int)*(longlong*)args->args[i];

		// Serialize the parse tree
		str = doc.Serialize(g, bvp, fn, pretty);

		if (initid->const_item)
			// Keep result of constant function
			g->Activityp = (PACTIVITY)str;

	} catch (int n) {
		xtrc(1, "json_test_bson: error %d: %s\n", n, g->Message);
		PUSH_WARNING(g->Message);
		*error = 1;
		str = NULL;
	} catch (const char* msg) {
		strcpy(g->Message, msg);
		PUSH_WARNING(g->Message);
		*error = 1;
		str = NULL;
	} // end catch

err:
	if (!str) {
		*res_length = 0;
		*is_null = 1;
	} else
		*res_length = strlen(str);

	return str;
} // end of bson_test

void bson_test_deinit(UDF_INIT* initid) {
	JsonFreeMem((PGLOBAL)initid->ptr);
} // end of bson_test_deinit

/*********************************************************************************/
/*  Locate a value in a Json tree.                                               */
/*********************************************************************************/
my_bool bsonlocate_init(UDF_INIT* initid, UDF_ARGS* args, char* message) {
	unsigned long reslen, memlen, more = 1000;

	if (args->arg_count < 2) {
		strcpy(message, "At least 2 arguments required");
		return true;
	} else if (!IsJson(args, 0) && args->arg_type[0] != STRING_RESULT) {
		strcpy(message, "First argument must be a json item");
		return true;
	} else if (args->arg_count > 2 && args->arg_type[2] != INT_RESULT) {
		strcpy(message, "Third argument is not an integer (rank)");
		return true;
	} // endifs args

	CalcLen(args, false, reslen, memlen);

	// TODO: calculate this
	if (IsJson(args, 0) == 3)
		more = 0;

	return JsonInit(initid, args, message, true, reslen, memlen, more);
} // end of bsonlocate_init

char* bsonlocate(UDF_INIT* initid, UDF_ARGS* args, char* result,
	unsigned long* res_length, char* is_null, char* error) {
	char   *path = NULL;
	int     k;
	PBVAL   bvp, bvp2;
	PBJNX   bnxp;
	PGLOBAL g = (PGLOBAL)initid->ptr;

	if (g->N) {
		if (g->Activityp) {
			path = (char*)g->Activityp;
			*res_length = strlen(path);
			return path;
		} else {
			*res_length = 0;
			*is_null = 1;
			return NULL;
		}	// endif Activityp

	} else if (initid->const_item)
		g->N = 1;

	try {
		if (!g->Xchk) {
			if (CheckMemory(g, initid, args, 1, !g->Xchk)) {
				PUSH_WARNING("CheckMemory error");
				*error = 1;
				goto err;
			} else
				bvp = MakeBinValue(g, args, 0);

			if (!bvp) {
				PUSH_WARNING("First argument is not a valid JSON item");
				goto err;
			}	// endif bvp

			if (g->Mrr) {			 // First argument is a constant
				g->Xchk = bvp;
				JsonMemSave(g);
			} // endif Mrr

		} else
			bvp = (PBVAL)g->Xchk;

		// The item to locate
		if (!(bvp2 = MakeBinValue(g, args, 1))) {
			PUSH_WARNING("Invalid second argument");
			goto err;
		}	// endif bvp

		k = (args->arg_count > 2) ? (int)*(long long*)args->args[2] : 1;

		bnxp = new(g) BJNX(g, bvp, TYPE_STRING);
		path = bnxp->Locate(g, bvp, bvp2, k);

		if (initid->const_item)
			// Keep result of constant function
			g->Activityp = (PACTIVITY)path;

	} catch (int n) {
		xtrc(1, "Exception %d: %s\n", n, g->Message);
		PUSH_WARNING(g->Message);
		*error = 1;
		path = NULL;
	} catch (const char* msg) {
		strcpy(g->Message, msg);
		PUSH_WARNING(g->Message);
		*error = 1;
		path = NULL;
	} // end catch

err:
	if (!path) {
		*res_length = 0;
		*is_null = 1;
	} else
		*res_length = strlen(path);

	return path;
} // end of bsonlocate

void bsonlocate_deinit(UDF_INIT* initid) {
	JsonFreeMem((PGLOBAL)initid->ptr);
} // end of bsonlocate_deinit

/*********************************************************************************/
/*  Locate all occurences of a value in a Json tree.                             */
/*********************************************************************************/
my_bool bson_locate_all_init(UDF_INIT* initid, UDF_ARGS* args, char* message) {
	unsigned long reslen, memlen, more = 1000;

	if (args->arg_count < 2) {
		strcpy(message, "At least 2 arguments required");
		return true;
	} else if (!IsJson(args, 0) && args->arg_type[0] != STRING_RESULT) {
		strcpy(message, "First argument must be a json item");
		return true;
	} else if (args->arg_count > 2 && args->arg_type[2] != INT_RESULT) {
		strcpy(message, "Third argument is not an integer (Depth)");
		return true;
	} // endifs

	CalcLen(args, false, reslen, memlen);

	// TODO: calculate this
	if (IsJson(args, 0) == 3)
		more = 0;

	return JsonInit(initid, args, message, true, reslen, memlen, more);
} // end of bson_locate_all_init

char* bson_locate_all(UDF_INIT* initid, UDF_ARGS* args, char* result,
	unsigned long* res_length, char* is_null, char* error) {
	char* path = NULL;
	int     mx = 10;
	PBVAL   bvp, bvp2;
	PBJNX   bnxp;
	PGLOBAL g = (PGLOBAL)initid->ptr;

	if (g->N) {
		if (g->Activityp) {
			path = (char*)g->Activityp;
			*res_length = strlen(path);
			return path;
		} else {
			*error = 1;
			*res_length = 0;
			*is_null = 1;
			return NULL;
		}	// endif Activityp

	} else if (initid->const_item)
		g->N = 1;

	try {
		if (!g->Xchk) {
			if (CheckMemory(g, initid, args, 1, true)) {
				PUSH_WARNING("CheckMemory error");
				*error = 1;
				goto err;
			} else
				bvp = MakeBinValue(g, args, 0);

			if (!bvp) {
				PUSH_WARNING("First argument is not a valid JSON item");
				goto err;
			}	// endif bvp

			if (g->Mrr) {			 // First argument is a constant
				g->Xchk = bvp;
				JsonMemSave(g);
			} // endif Mrr

		} else
			bvp = (PBVAL)g->Xchk;

		// The item to locate
		if (!(bvp2 = MakeBinValue(g, args, 1))) {
			PUSH_WARNING("Invalid second argument");
			goto err;
		}	// endif bvp

		if (args->arg_count > 2)
			mx = (int)*(long long*)args->args[2];

		bnxp = new(g) BJNX(g, bvp, TYPE_STRING);
		path = bnxp->LocateAll(g, bvp, bvp2, mx);

		if (initid->const_item)
			// Keep result of constant function
			g->Activityp = (PACTIVITY)path;

	} catch (int n) {
		xtrc(1, "Exception %d: %s\n", n, g->Message);
		PUSH_WARNING(g->Message);
		*error = 1;
		path = NULL;
	} catch (const char* msg) {
		strcpy(g->Message, msg);
		PUSH_WARNING(g->Message);
		*error = 1;
		path = NULL;
	} // end catch

err:
	if (!path) {
		*res_length = 0;
		*is_null = 1;
	} else
		*res_length = strlen(path);

	return path;
} // end of bson_locate_all

void bson_locate_all_deinit(UDF_INIT* initid) {
	JsonFreeMem((PGLOBAL)initid->ptr);
} // end of bson_locate_all_deinit