From 564d41faf4938596c7f0bccd56a4f820048dc031 Mon Sep 17 00:00:00 2001 From: Olivier Bertrand Date: Thu, 19 Feb 2015 00:59:02 +0100 Subject: [PATCH] - Work on JSON and JSON UDF's modified: storage/connect/json.cpp storage/connect/jsonudf.cpp storage/connect/tabjson.cpp - CntReadNext: Enable EvalColumns for longjmp modified: storage/connect/connect.cc --- storage/connect/connect.cc | 5 +- storage/connect/json.cpp | 2191 ++++++++++++++++++----------------- storage/connect/jsonudf.cpp | 146 ++- storage/connect/tabjson.cpp | 10 +- 4 files changed, 1247 insertions(+), 1105 deletions(-) diff --git a/storage/connect/connect.cc b/storage/connect/connect.cc index a54d8ebcc44..1b0db6dca6b 100644 --- a/storage/connect/connect.cc +++ b/storage/connect/connect.cc @@ -469,9 +469,12 @@ RCODE CntReadNext(PGLOBAL g, PTDB tdbp) } while (rc == RC_NF); + if (rc == RC_OK) + rc= EvalColumns(g, tdbp, false); + err: g->jump_level--; - return (rc != RC_OK) ? rc : EvalColumns(g, tdbp, false); + return rc; } // end of CntReadNext /***********************************************************************/ diff --git a/storage/connect/json.cpp b/storage/connect/json.cpp index 0ace3d9159b..251ba762a53 100644 --- a/storage/connect/json.cpp +++ b/storage/connect/json.cpp @@ -1,1085 +1,1106 @@ -/*************** json CPP Declares Source Code File (.H) ***************/ -/* Name: json.cpp Version 1.0 */ -/* */ -/* (C) Copyright to the author Olivier BERTRAND 2014 - 2015 */ -/* */ -/* This file contains the JSON classes functions. */ -/***********************************************************************/ - -/***********************************************************************/ -/* Include relevant sections of the MariaDB header file. */ -/***********************************************************************/ -#include - -/***********************************************************************/ -/* Include application header files: */ -/* global.h is header containing all global declarations. */ -/* plgdbsem.h is header containing the DB application declarations. */ -/* xjson.h is header containing the JSON classes declarations. */ -/***********************************************************************/ -#include "global.h" -#include "plgdbsem.h" -#include "json.h" - -#define ARGS MY_MIN(24,len-i),s+MY_MAX(i-3,0) - -#if defined(WIN32) -#define EL "\r\n" -#else -#define EL "\n" -#endif - -/***********************************************************************/ -/* Parse a json string. */ -/***********************************************************************/ -PJSON ParseJson(PGLOBAL g, char *s, int len, int pretty, bool *comma) -{ - int i; - bool b = false; - PJSON jsp = NULL; - STRG src; - - if (!s || !len) { - strcpy(g->Message, "Void JSON object"); - return NULL; - } else if (comma) - *comma = false; - - src.str = s; - src.len = len; - - for (i = 0; i < len; i++) - switch (s[i]) { - case '[': - if (jsp) { - strcpy(g->Message, "More than one item in file"); - return NULL; - } else if (!(jsp = ParseArray(g, ++i, src))) - return NULL; - - break; - case '{': - if (jsp) { - strcpy(g->Message, "More than one item in file"); - return NULL; - } else if (!(jsp = ParseObject(g, ++i, src))) - return NULL; - break; - case ' ': - case '\t': - case '\n': - case '\r': - break; - case ',': - if (jsp && pretty == 1) { - if (comma) - *comma = true; - - break; - } // endif pretty - - sprintf(g->Message, "Unexpected ',' (pretty=%d)", pretty); - return NULL; - case '(': - b = true; - break; - case ')': - if (b) { - b = false; - break; - } // endif b - - default: - sprintf(g->Message, "Bad '%c' character near %.*s", - s[i], ARGS); - return NULL; - }; // endswitch s[i] - - if (!jsp) - sprintf(g->Message, "Invalid Json string '%.*s'", 50, s); - - return jsp; -} // end of ParseJson - -/***********************************************************************/ -/* Parse a JSON Array. */ -/***********************************************************************/ -PJAR ParseArray(PGLOBAL g, int& i, STRG& src) -{ - char *s = src.str; - int len = src.len; - int level = 0; - PJAR jarp = new(g) JARRAY; - PJVAL jvp = NULL; - - for (; i < len; i++) - switch (s[i]) { - case ',': - if (level < 2) { - sprintf(g->Message, "Unexpected ',' near %.*s",ARGS); - return NULL; - } else - level = 1; - - break; - case ']': - if (level == 1) { - sprintf(g->Message, "Unexpected ',]' near %.*s", ARGS); - return NULL; - } // endif level - - jarp->InitArray(g); - return jarp; - case ' ': - case '\t': - case '\n': - case '\r': - break; - default: - if (level == 2) { - sprintf(g->Message, "Unexpected value near %.*s", ARGS); - return NULL; - } else if ((jvp = ParseValue(g, i, src))) { - jarp->AddValue(g, jvp); - level = 2; - } else - return NULL; - - level = 2; - break; - }; // endswitch s[i] - - strcpy(g->Message, "Unexpected EOF in array"); - return NULL; -} // end of ParseArray - -/***********************************************************************/ -/* Parse a JSON Object. */ -/***********************************************************************/ -PJOB ParseObject(PGLOBAL g, int& i, STRG& src) -{ - PSZ key; - char *s = src.str; - int len = src.len; - int level = 0; - PJOB jobp = new(g) JOBJECT; - PJPR jpp = NULL; - - for (; i < len; i++) - switch (s[i]) { - case '"': - if (level < 2) { - if ((key = ParseString(g, ++i, src))) { - jpp = jobp->AddPair(g, key); - level = 1; - } else - return NULL; - - } else { - sprintf(g->Message, "misplaced string near %.*s", ARGS); - return NULL; - } // endif level - - break; - case ':': - if (level == 1) { - if (!(jpp->Val = ParseValue(g, ++i, src))) - return NULL; - - level = 2; - } else { - sprintf(g->Message, "Unexpected ':' near %.*s", ARGS); - return NULL; - } // endif level - - break; - case ',': - if (level < 2) { - sprintf(g->Message, "Unexpected ',' near %.*s", ARGS); - return NULL; - } else - level = 1; - - break; - case '}': - if (level == 1) { - sprintf(g->Message, "Unexpected '}' near %.*s", ARGS); - return NULL; - } // endif level - - return jobp; - case ' ': - case '\t': - case '\n': - case '\r': - break; - default: - sprintf(g->Message, "Unexpected character '%c' near %.*s", - s[i], ARGS); - return NULL; - }; // endswitch s[i] - - strcpy(g->Message, "Unexpected EOF in Object"); - return NULL; -} // end of ParseObject - -/***********************************************************************/ -/* Parse a JSON Value. */ -/***********************************************************************/ -PJVAL ParseValue(PGLOBAL g, int& i, STRG& src) -{ - char *strval, *s = src.str; - int n, len = src.len; - PJVAL jvp = new(g) JVALUE; - - for (; i < len; i++) - switch (s[i]) { - case ' ': - case '\t': - case '\n': - case '\r': - break; - default: - goto suite; - } // endswitch - - suite: - switch (s[i]) { - case '[': - if (!(jvp->Jsp = ParseArray(g, ++i, src))) - return NULL; - - break; - case '{': - if (!(jvp->Jsp = ParseObject(g, ++i, src))) - return NULL; - - break; - case '"': - if ((strval = ParseString(g, ++i, src))) - jvp->Value = AllocateValue(g, strval, TYPE_STRING); - else - return NULL; - - break; - case 't': - if (!strncmp(s + i, "true", 4)) { - n = 1; - jvp->Value = AllocateValue(g, &n, TYPE_TINY); - i += 3; - } else - goto err; - - break; - case 'f': - if (!strncmp(s + i, "false", 5)) { - n = 0; - jvp->Value = AllocateValue(g, &n, TYPE_TINY); - i += 4; - } else - goto err; - - break; - case 'n': - if (!strncmp(s + i, "null", 4)) - i += 3; - else - goto err; - - break; - case '-': - default: - if (s[i] == '-' || isdigit(s[i])) { - if (!(jvp->Value = ParseNumeric(g, i, src))) - goto err; - - } else - goto err; - - }; // endswitch s[i] - - jvp->Size = 1; - return jvp; - -err: - sprintf(g->Message, "Unexpected character '%c' near %.*s", - s[i], ARGS); - return NULL; -} // end of ParseValue - -/***********************************************************************/ -/* Unescape and parse a JSON string. */ -/***********************************************************************/ -char *ParseString(PGLOBAL g, int& i, STRG& src) -{ - char *s = src.str; - uchar *p; - int n = 0, len = src.len; - - // The size to allocate is not known yet - p = (uchar*)PlugSubAlloc(g, NULL, 0); - - for (; i < len; i++) - switch (s[i]) { - case '"': - p[n++] = 0; - PlugSubAlloc(g, NULL, n); - return (char*)p; - case '\\': - if (++i < len) { - if (s[i] == 'u') { - if (len - i > 5) { -// if (charset == utf8) { - char xs[5]; - uint hex; - - xs[0] = s[++i]; - xs[1] = s[++i]; - xs[2] = s[++i]; - xs[3] = s[++i]; - xs[4] = 0; - hex = strtoul(xs, NULL, 16); - - if (hex < 0x80) { - p[n] = (uchar)hex; - } else if (hex < 0x800) { - p[n++] = (uchar)(0xC0 | (hex >> 6)); - p[n] = (uchar)(0x80 | (hex & 0x3F)); - } else if (hex < 0x10000) { - p[n++] = (uchar)(0xE0 | (hex >> 12)); - p[n++] = (uchar)(0x80 | ((hex >> 6) & 0x3f)); - p[n] = (uchar)(0x80 | (hex & 0x3f)); - } else - p[n] = '?'; - -#if 0 - } else { - char xs[3]; - UINT hex; - - i += 2; - xs[0] = s[++i]; - xs[1] = s[++i]; - xs[2] = 0; - hex = strtoul(xs, NULL, 16); - p[n] = (char)hex; - } // endif charset -#endif // 0 - } else - goto err; - - } else switch(s[i]) { - case 't': p[n] = '\t'; break; - case 'n': p[n] = '\n'; break; - case 'r': p[n] = '\r'; break; - case 'b': p[n] = '\b'; break; - case 'f': p[n] = '\f'; break; - default: p[n] = s[i]; break; - } // endswitch - - n++; - } else - goto err; - - break; - default: - p[n++] = s[i]; - break; - }; // endswitch s[i] - - err: - strcpy(g->Message, "Unexpected EOF in String"); - return NULL; -} // end of ParseString - -/***********************************************************************/ -/* Parse a JSON numeric value. */ -/***********************************************************************/ -PVAL ParseNumeric(PGLOBAL g, int& i, STRG& src) -{ - char *s = src.str, buf[50]; - int n = 0, len = src.len; - short nd = 0; - bool has_dot = false; - bool has_e = false; - bool found_digit = false; - PVAL valp = NULL; - - for (; i < len; i++) { - switch (s[i]) { - case '.': - if (!found_digit || has_dot || has_e) - goto err; - - has_dot = true; - break; - case 'e': - case 'E': - if (!found_digit || has_e) - goto err; - - has_e = true; - found_digit = false; - break; - case '+': - if (!has_e) - goto err; - - // passthru - case '-': - if (found_digit) - goto err; - - break; - default: - if (isdigit(s[i])) { - if (has_dot && !has_e) - nd++; // Number of decimals - - found_digit = true; - } else - goto fin; - - }; // endswitch s[i] - - buf[n++] = s[i]; - } // endfor i - - fin: - if (found_digit) { - buf[n] = 0; - - if (has_dot || has_e) { - double dv = strtod(buf, NULL); - - valp = AllocateValue(g, &dv, TYPE_DOUBLE, nd); - } else { - int iv = strtol(buf, NULL, 10); - - valp = AllocateValue(g, &iv, TYPE_INT); - } // endif has - - i--; // Unstack following character - return valp; - } else { - strcpy(g->Message, "No digit found"); - return NULL; - } // endif found_digit - - err: - strcpy(g->Message, "Unexpected EOF in number"); - return NULL; -} // end of ParseNumeric - -/***********************************************************************/ -/* Serialize a JSON tree: */ -/***********************************************************************/ -PSZ Serialize(PGLOBAL g, PJSON jsp, FILE *fs, int pretty) -{ - bool b = false, err = true; - JOUT *jp; - - g->Message[0] = 0; - - if (!jsp) { - strcpy(g->Message, "Null json tree"); - return NULL; - } else if (!fs) { - // Serialize to a string - jp = new(g) JOUTSTR(g); - b = pretty == 1; - } else if (pretty == 2) { - // Serialize to a pretty file - jp = new(g) JOUTPRT(g, fs); - } else { - // Serialize to a flat file - jp = new(g) JOUTFILE(g, fs); - b = pretty == 1; - } // endif's - - switch (jsp->GetType()) { - case TYPE_JAR: - err = SerializeArray(jp, (PJAR)jsp, b); - break; - case TYPE_JOB: - err = (b && jp->WriteChr('\t')); - err |= SerializeObject(jp, (PJOB)jsp); - break; - case TYPE_JVAL: - err = SerializeValue(jp, (PJVAL)jsp); - break; - default: - strcpy(g->Message, "Invalid json tree"); - } // endswitch Type - - if (fs) { - fputc('\n', fs); - fclose(fs); - return (err) ? g->Message : NULL; - } else if (!err) { - PSZ str = ((JOUTSTR*)jp)->Strp; - - jp->WriteChr('\0'); - PlugSubAlloc(g, NULL, ((JOUTSTR*)jp)->N); - return str; - } else { - if (!g->Message[0]) - strcpy(g->Message, "Error in Serialize"); - - return NULL; - } // endif's - -} // end of Serialize - -/***********************************************************************/ -/* Serialize a JSON Array. */ -/***********************************************************************/ -bool SerializeArray(JOUT *js, PJAR jarp, bool b) -{ - bool first = true; - - - if (js->WriteChr('[')) - return true; - else if (b && (js->WriteStr(EL) || js->WriteChr('\t'))) - return true; - - for (int i = 0; i < jarp->size(); i++) { - if (first) - first = false; - else if (js->WriteChr(',')) - return true; - else if (b && (js->WriteStr(EL) || js->WriteChr('\t'))) - return true; - - if (SerializeValue(js, jarp->GetValue(i))) - return true; - - } // endfor i - - if (b && js->WriteStr(EL)) - return true; - - return js->WriteChr(']'); -} // end of SerializeArray - -/***********************************************************************/ -/* Serialize a JSON Object. */ -/***********************************************************************/ -bool SerializeObject(JOUT *js, PJOB jobp) -{ - bool first = true; - - if (js->WriteChr('{')) - return true; - - for (PJPR pair = jobp->First; pair; pair = pair->Next) { - if (first) - first = false; - else if (js->WriteChr(',')) - return true; - - if (js->WriteChr('"') || - js->WriteStr(pair->Key) || - js->WriteChr('"') || - js->WriteChr(':') || - SerializeValue(js, pair->Val)) - return true; - - } // endfor i - - return js->WriteChr('}'); -} // end of SerializeObject - -/***********************************************************************/ -/* Serialize a JSON Value. */ -/***********************************************************************/ -bool SerializeValue(JOUT *js, PJVAL jvp) -{ - PJAR jap; - PJOB jop; - PVAL valp; - - if ((jap = jvp->GetArray())) - return SerializeArray(js, jap, false); - else if ((jop = jvp->GetObject())) - return SerializeObject(js, jop); - else if (!(valp = jvp->Value) || valp->IsNull()) - return js->WriteStr("null"); - else switch (valp->GetType()) { - case TYPE_TINY: - return js->WriteStr(valp->GetTinyValue() ? "true" : "false"); - case TYPE_STRING: - return js->Escape(valp->GetCharValue()); - default: - if (valp->IsTypeNum()) { - char buf[32]; - - return js->WriteStr(valp->GetCharString(buf)); - } // endif valp - - } // endswitch Type - -strcpy(js->g->Message, "Unrecognized value"); -return true; -} // end of SerializeValue - -/* -------------------------- Class JOUTSTR -------------------------- */ - -/***********************************************************************/ -/* JOUTSTR constructor. */ -/***********************************************************************/ -JOUTSTR::JOUTSTR(PGLOBAL g) : JOUT(g) -{ - PPOOLHEADER pph = (PPOOLHEADER)g->Sarea; - - N = 0; - Max = pph->FreeBlk; - Max = (Max > 512) ? Max - 512 : Max; - Strp = (char*)PlugSubAlloc(g, NULL, 0); // Size not know yet -} // end of JOUTSTR constructor - -/***********************************************************************/ -/* Concatenate a string to the Serialize string. */ -/***********************************************************************/ -bool JOUTSTR::WriteStr(const char *s) -{ - if (s) { - size_t len = strlen(s); - - if (N + len > Max) - return true; - - memcpy(Strp + N, s, len); - N += len; - return false; - } else - return true; - -} // end of WriteStr - -/***********************************************************************/ -/* Concatenate a character to the Serialize string. */ -/***********************************************************************/ -bool JOUTSTR::WriteChr(const char c) -{ - if (N + 1 > Max) - return true; - - Strp[N++] = c; - return false; -} // end of WriteChr - -/***********************************************************************/ -/* Escape and Concatenate a string to the Serialize string. */ -/***********************************************************************/ -bool JOUTSTR::Escape(const char *s) -{ - WriteChr('"'); - - for (unsigned int i = 0; i < strlen(s); i++) - switch (s[i]) { - case '"': - case '\\': - case '\t': - case '\n': - case '\r': - case '\b': - case '\f': WriteChr('\\'); - // passthru - default: - WriteChr(s[i]); - break; - } // endswitch s[i] - - WriteChr('"'); - return false; -} // end of Escape - -/* ------------------------- Class JOUTFILE -------------------------- */ - -/***********************************************************************/ -/* Write a string to the Serialize file. */ -/***********************************************************************/ -bool JOUTFILE::WriteStr(const char *s) -{ - // This is temporary - fputs(s, Stream); - return false; -} // end of WriteStr - -/***********************************************************************/ -/* Write a character to the Serialize file. */ -/***********************************************************************/ -bool JOUTFILE::WriteChr(const char c) -{ - // This is temporary - fputc(c, Stream); - return false; -} // end of WriteChr - -/***********************************************************************/ -/* Escape and Concatenate a string to the Serialize string. */ -/***********************************************************************/ -bool JOUTFILE::Escape(const char *s) -{ - // This is temporary - fputc('"', Stream); - - for (unsigned int i = 0; i < strlen(s); i++) - switch (s[i]) { - case '"': fputs("\\\"", Stream); break; - case '\\': fputs("\\\\", Stream); break; - case '\t': fputs("\\t", Stream); break; - case '\n': fputs("\\n", Stream); break; - case '\r': fputs("\\r", Stream); break; - case '\b': fputs("\\b", Stream); break; - case '\f': fputs("\\f", Stream); break; - default: - fputc(s[i], Stream); - break; - } // endswitch s[i] - - fputc('"', Stream); - return false; -} // end of Escape - -/* ------------------------- Class JOUTPRT --------------------------- */ - -/***********************************************************************/ -/* Write a string to the Serialize pretty file. */ -/***********************************************************************/ -bool JOUTPRT::WriteStr(const char *s) -{ - // This is temporary - if (B) { - fputs(EL, Stream); - M--; - - for (int i = 0; i < M; i++) - fputc('\t', Stream); - - B = false; - } // endif B - - fputs(s, Stream); - return false; -} // end of WriteStr - -/***********************************************************************/ -/* Write a character to the Serialize pretty file. */ -/***********************************************************************/ -bool JOUTPRT::WriteChr(const char c) -{ - switch (c) { - case ':': - fputs(": ", Stream); - break; - case '{': - case '[': -#if 0 - if (M) - fputs(EL, Stream); - - for (int i = 0; i < M; i++) - fputc('\t', Stream); -#endif // 0 - - fputc(c, Stream); - fputs(EL, Stream); - M++; - - for (int i = 0; i < M; i++) - fputc('\t', Stream); - - break; - case '}': - case ']': - M--; - fputs(EL, Stream); - - for (int i = 0; i < M; i++) - fputc('\t', Stream); - - fputc(c, Stream); - B = true; - break; - case ',': - fputc(c, Stream); - fputs(EL, Stream); - - for (int i = 0; i < M; i++) - fputc('\t', Stream); - - B = false; - break; - default: - fputc(c, Stream); - } // endswitch c - -return false; -} // end of WriteChr - -/* -------------------------- Class JOBJECT -------------------------- */ - -/***********************************************************************/ -/* Add a new pair to an Object. */ -/***********************************************************************/ -PJPR JOBJECT::AddPair(PGLOBAL g, PSZ key) -{ - PJPR jpp = new(g) JPAIR(key); - - if (Last) - Last->Next = jpp; - else - First = jpp; - - Last = jpp; - Size++; - return jpp; -} // end of AddPair - -/***********************************************************************/ -/* Get the value corresponding to the given key. */ -/***********************************************************************/ -PJVAL JOBJECT::GetValue(const char* key) -{ - for (PJPR jp = First; jp; jp = jp->Next) - if (!strcmp(jp->Key, key)) - return jp->Val; - - return NULL; -} // end of GetValue; - -/***********************************************************************/ -/* Return the text corresponding to all keys (XML like). */ -/***********************************************************************/ -PSZ JOBJECT::GetText(PGLOBAL g) -{ - char *p, *text = (char*)PlugSubAlloc(g, NULL, 0); - bool b = true; - - if (!First) - return NULL; - else for (PJPR jp = First; jp; jp = jp->Next) { - if (!(p = jp->Val->GetString())) - p = "???"; - - if (b) { - strcpy(text, p); - b = false; - } else - strcat(strcat(text, " "), p); - - } // endfor jp - - PlugSubAlloc(g, NULL, strlen(text) + 1); - return text; -} // end of GetValue; - -/***********************************************************************/ -/* Set or add a value corresponding to the given key. */ -/***********************************************************************/ -void JOBJECT::SetValue(PGLOBAL g, PJVAL jvp, PSZ key) -{ - PJPR jp; - - for (jp = First; jp; jp = jp->Next) - if (!strcmp(jp->Key, key)) { - jp->Val = jvp; - break; - } // endif key - - if (!jp) { - jp = AddPair(g, key); - jp->Val = jvp; - } // endif jp - -} // end of SetValue - -/* -------------------------- Class JARRAY --------------------------- */ - -/***********************************************************************/ -/* Make the array of values from the values list. */ -/***********************************************************************/ -void JARRAY::InitArray(PGLOBAL g) -{ - int i; - PJVAL jvp; - - for (Size = 0, jvp = First; jvp; jvp = jvp->Next) - if (!jvp->Del) - Size++; - - if (!Size) { - return; - } else if (Size > Alloc) { - // No need to realloc after deleting values - Mvals = (PJVAL*)PlugSubAlloc(g, NULL, Size * sizeof(PJVAL)); - Alloc = Size; - } // endif Size - - for (i = 0, jvp = First; jvp; jvp = jvp->Next) - if (!jvp->Del) - Mvals[i++] = jvp; - -} // end of InitArray - -/***********************************************************************/ -/* Get the Nth value of an Array. */ -/***********************************************************************/ -PJVAL JARRAY::GetValue(int i) -{ - if (Mvals && i >= 0 && i < Size) - return Mvals[i]; - else - return NULL; -} // end of GetValue - -/***********************************************************************/ -/* Add a Value to the Arrays Value list. */ -/***********************************************************************/ -PJVAL JARRAY::AddValue(PGLOBAL g, PJVAL jvp) -{ - if (!jvp) - jvp = new(g) JVALUE; - - if (Last) - Last->Next = jvp; - else - First = jvp; - - Last = jvp; - return jvp; -} // end of AddValue - -/***********************************************************************/ -/* Add a Value to the Arrays Value list. */ -/***********************************************************************/ -bool JARRAY::SetValue(PGLOBAL g, PJVAL jvp, int n) -{ - int i = 0; - PJVAL jp, *jpp = &First; - - for (i = 0, jp = First; i < n; i++, jp = *(jpp = &jp->Next)) - if (!jp) - *jpp = jp = new(g) JVALUE; - - *jpp = jvp; - jvp->Next = (jp ? jp->Next : NULL); - return false; -} // end of SetValue - -/***********************************************************************/ -/* Delete a Value from the Arrays Value list. */ -/***********************************************************************/ -bool JARRAY::DeleteValue(int n) -{ - PJVAL jvp = GetValue(n); - - if (jvp) { - jvp->Del = true; - return false; - } else - return true; - -} // end of DeleteValue - -/* -------------------------- Class JVALUE- -------------------------- */ - -/***********************************************************************/ -/* Constructor for a Value with a given string or numeric value. */ -/***********************************************************************/ -JVALUE::JVALUE(PGLOBAL g, PVAL valp) : JSON() -{ - Jsp = NULL; - Value = AllocateValue(g, valp); - Next = NULL; - Del = false; -} // end of JVALUE constructor - -/***********************************************************************/ -/* Returns the type of the Value's value. */ -/***********************************************************************/ -JTYP JVALUE::GetValType(void) -{ - if (Jsp) - return Jsp->GetType(); - else if (Value) - return (JTYP)Value->GetType(); - else - return (JTYP)TYPE_VOID; - -} // end of GetValType - -/***********************************************************************/ -/* Return the Value's Object value. */ -/***********************************************************************/ -PJOB JVALUE::GetObject(void) -{ - if (Jsp && Jsp->GetType() == TYPE_JOB) - return (PJOB)Jsp; - - return NULL; -} // end of GetObject - -/***********************************************************************/ -/* Return the Value's Array value. */ -/***********************************************************************/ -PJAR JVALUE::GetArray(void) -{ - if (Jsp && Jsp->GetType() == TYPE_JAR) - return (PJAR)Jsp; - - return NULL; -} // end of GetArray - -/***********************************************************************/ -/* Return the Value's Integer value. */ -/***********************************************************************/ -int JVALUE::GetInteger(void) -{ - return (Value) ? Value->GetIntValue() : 0; -} // end of GetInteger - -/***********************************************************************/ -/* Return the Value's Double value. */ -/***********************************************************************/ -double JVALUE::GetFloat(void) -{ - return (Value) ? Value->GetFloatValue() : 0.0; -} // end of GetFloat - -/***********************************************************************/ -/* Return the Value's String value. */ -/***********************************************************************/ -PSZ JVALUE::GetString(void) -{ - char buf[32]; - return (Value) ? Value->GetCharString(buf) : NULL; -} // end of GetString - -/***********************************************************************/ -/* Set the Value's value as the given integer. */ -/***********************************************************************/ -void JVALUE::SetInteger(PGLOBAL g, int n) -{ - Value = AllocateValue(g, &n, TYPE_INT); -} // end of AddInteger - -/***********************************************************************/ -/* Set the Value's value as the given DOUBLE. */ -/***********************************************************************/ -void JVALUE::SetFloat(PGLOBAL g, double f) -{ - Value = AllocateValue(g, &f, TYPE_DOUBLE, 6); -} // end of AddFloat - -/***********************************************************************/ -/* Set the Value's value as the given string. */ -/***********************************************************************/ -void JVALUE::SetString(PGLOBAL g, PSZ s) -{ - Value = AllocateValue(g, s, TYPE_STRING); -} // end of AddFloat - +/*************** json CPP Declares Source Code File (.H) ***************/ +/* Name: json.cpp Version 1.0 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 2014 - 2015 */ +/* */ +/* This file contains the JSON classes functions. */ +/***********************************************************************/ + +/***********************************************************************/ +/* Include relevant sections of the MariaDB header file. */ +/***********************************************************************/ +#include + +/***********************************************************************/ +/* Include application header files: */ +/* global.h is header containing all global declarations. */ +/* plgdbsem.h is header containing the DB application declarations. */ +/* xjson.h is header containing the JSON classes declarations. */ +/***********************************************************************/ +#include "global.h" +#include "plgdbsem.h" +#include "json.h" + +#define ARGS MY_MIN(24,len-i),s+MY_MAX(i-3,0) + +#if defined(WIN32) +#define EL "\r\n" +#else +#define EL "\n" +#endif + +/***********************************************************************/ +/* Parse a json string. */ +/***********************************************************************/ +PJSON ParseJson(PGLOBAL g, char *s, int len, int pretty, bool *comma) +{ + int i, rc; + bool b = false; + PJSON jsp = NULL; + STRG src; + + if (!s || !len) { + strcpy(g->Message, "Void JSON object"); + return NULL; + } else if (comma) + *comma = false; + + src.str = s; + src.len = len; + + // Save stack and allocation environment and prepare error return + if (g->jump_level == MAX_JUMP) { + strcpy(g->Message, MSG(TOO_MANY_JUMPS)); + return NULL; + } // endif jump_level + + if ((rc= setjmp(g->jumper[++g->jump_level])) != 0) { + goto err; + } // endif rc + + for (i = 0; i < len; i++) + switch (s[i]) { + case '[': + if (jsp) { + strcpy(g->Message, "More than one item in file"); + goto err; + } else if (!(jsp = ParseArray(g, ++i, src))) + goto err; + + break; + case '{': + if (jsp) { + strcpy(g->Message, "More than one item in file"); + goto err; + } else if (!(jsp = ParseObject(g, ++i, src))) + goto err; + break; + case ' ': + case '\t': + case '\n': + case '\r': + break; + case ',': + if (jsp && pretty == 1) { + if (comma) + *comma = true; + + break; + } // endif pretty + + sprintf(g->Message, "Unexpected ',' (pretty=%d)", pretty); + goto err; + case '(': + b = true; + break; + case ')': + if (b) { + b = false; + break; + } // endif b + + default: + sprintf(g->Message, "Bad '%c' character near %.*s", + s[i], ARGS); + goto err; + }; // endswitch s[i] + + if (!jsp) + sprintf(g->Message, "Invalid Json string '%.*s'", 50, s); + + g->jump_level--; + return jsp; + + err: + g->jump_level--; + return NULL; +} // end of ParseJson + +/***********************************************************************/ +/* Parse a JSON Array. */ +/***********************************************************************/ +PJAR ParseArray(PGLOBAL g, int& i, STRG& src) +{ + char *s = src.str; + int len = src.len; + int level = 0; + PJAR jarp = new(g) JARRAY; + PJVAL jvp = NULL; + + for (; i < len; i++) + switch (s[i]) { + case ',': + if (level < 2) { + sprintf(g->Message, "Unexpected ',' near %.*s",ARGS); + return NULL; + } else + level = 1; + + break; + case ']': + if (level == 1) { + sprintf(g->Message, "Unexpected ',]' near %.*s", ARGS); + return NULL; + } // endif level + + jarp->InitArray(g); + return jarp; + case ' ': + case '\t': + case '\n': + case '\r': + break; + default: + if (level == 2) { + sprintf(g->Message, "Unexpected value near %.*s", ARGS); + return NULL; + } else if ((jvp = ParseValue(g, i, src))) { + jarp->AddValue(g, jvp); + level = 2; + } else + return NULL; + + level = 2; + break; + }; // endswitch s[i] + + strcpy(g->Message, "Unexpected EOF in array"); + return NULL; +} // end of ParseArray + +/***********************************************************************/ +/* Parse a JSON Object. */ +/***********************************************************************/ +PJOB ParseObject(PGLOBAL g, int& i, STRG& src) +{ + PSZ key; + char *s = src.str; + int len = src.len; + int level = 0; + PJOB jobp = new(g) JOBJECT; + PJPR jpp = NULL; + + for (; i < len; i++) + switch (s[i]) { + case '"': + if (level < 2) { + if ((key = ParseString(g, ++i, src))) { + jpp = jobp->AddPair(g, key); + level = 1; + } else + return NULL; + + } else { + sprintf(g->Message, "misplaced string near %.*s", ARGS); + return NULL; + } // endif level + + break; + case ':': + if (level == 1) { + if (!(jpp->Val = ParseValue(g, ++i, src))) + return NULL; + + level = 2; + } else { + sprintf(g->Message, "Unexpected ':' near %.*s", ARGS); + return NULL; + } // endif level + + break; + case ',': + if (level < 2) { + sprintf(g->Message, "Unexpected ',' near %.*s", ARGS); + return NULL; + } else + level = 1; + + break; + case '}': + if (level == 1) { + sprintf(g->Message, "Unexpected '}' near %.*s", ARGS); + return NULL; + } // endif level + + return jobp; + case ' ': + case '\t': + case '\n': + case '\r': + break; + default: + sprintf(g->Message, "Unexpected character '%c' near %.*s", + s[i], ARGS); + return NULL; + }; // endswitch s[i] + + strcpy(g->Message, "Unexpected EOF in Object"); + return NULL; +} // end of ParseObject + +/***********************************************************************/ +/* Parse a JSON Value. */ +/***********************************************************************/ +PJVAL ParseValue(PGLOBAL g, int& i, STRG& src) +{ + char *strval, *s = src.str; + int n, len = src.len; + PJVAL jvp = new(g) JVALUE; + + for (; i < len; i++) + switch (s[i]) { + case ' ': + case '\t': + case '\n': + case '\r': + break; + default: + goto suite; + } // endswitch + + suite: + switch (s[i]) { + case '[': + if (!(jvp->Jsp = ParseArray(g, ++i, src))) + return NULL; + + break; + case '{': + if (!(jvp->Jsp = ParseObject(g, ++i, src))) + return NULL; + + break; + case '"': + if ((strval = ParseString(g, ++i, src))) + jvp->Value = AllocateValue(g, strval, TYPE_STRING); + else + return NULL; + + break; + case 't': + if (!strncmp(s + i, "true", 4)) { + n = 1; + jvp->Value = AllocateValue(g, &n, TYPE_TINY); + i += 3; + } else + goto err; + + break; + case 'f': + if (!strncmp(s + i, "false", 5)) { + n = 0; + jvp->Value = AllocateValue(g, &n, TYPE_TINY); + i += 4; + } else + goto err; + + break; + case 'n': + if (!strncmp(s + i, "null", 4)) + i += 3; + else + goto err; + + break; + case '-': + default: + if (s[i] == '-' || isdigit(s[i])) { + if (!(jvp->Value = ParseNumeric(g, i, src))) + goto err; + + } else + goto err; + + }; // endswitch s[i] + + jvp->Size = 1; + return jvp; + +err: + sprintf(g->Message, "Unexpected character '%c' near %.*s", + s[i], ARGS); + return NULL; +} // end of ParseValue + +/***********************************************************************/ +/* Unescape and parse a JSON string. */ +/***********************************************************************/ +char *ParseString(PGLOBAL g, int& i, STRG& src) +{ + char *s = src.str; + uchar *p; + int n = 0, len = src.len; + + // Be sure of memory availability + if (len + 1 - i > (signed)((PPOOLHEADER)g->Sarea)->FreeBlk) { + strcpy(g->Message, "ParseString: Out of memory"); + return NULL; + } // endif len + + // The size to allocate is not known yet + p = (uchar*)PlugSubAlloc(g, NULL, 0); + + for (; i < len; i++) + switch (s[i]) { + case '"': + p[n++] = 0; + PlugSubAlloc(g, NULL, n); + return (char*)p; + case '\\': + if (++i < len) { + if (s[i] == 'u') { + if (len - i > 5) { +// if (charset == utf8) { + char xs[5]; + uint hex; + + xs[0] = s[++i]; + xs[1] = s[++i]; + xs[2] = s[++i]; + xs[3] = s[++i]; + xs[4] = 0; + hex = strtoul(xs, NULL, 16); + + if (hex < 0x80) { + p[n] = (uchar)hex; + } else if (hex < 0x800) { + p[n++] = (uchar)(0xC0 | (hex >> 6)); + p[n] = (uchar)(0x80 | (hex & 0x3F)); + } else if (hex < 0x10000) { + p[n++] = (uchar)(0xE0 | (hex >> 12)); + p[n++] = (uchar)(0x80 | ((hex >> 6) & 0x3f)); + p[n] = (uchar)(0x80 | (hex & 0x3f)); + } else + p[n] = '?'; + +#if 0 + } else { + char xs[3]; + UINT hex; + + i += 2; + xs[0] = s[++i]; + xs[1] = s[++i]; + xs[2] = 0; + hex = strtoul(xs, NULL, 16); + p[n] = (char)hex; + } // endif charset +#endif // 0 + } else + goto err; + + } else switch(s[i]) { + case 't': p[n] = '\t'; break; + case 'n': p[n] = '\n'; break; + case 'r': p[n] = '\r'; break; + case 'b': p[n] = '\b'; break; + case 'f': p[n] = '\f'; break; + default: p[n] = s[i]; break; + } // endswitch + + n++; + } else + goto err; + + break; + default: + p[n++] = s[i]; + break; + }; // endswitch s[i] + + err: + strcpy(g->Message, "Unexpected EOF in String"); + return NULL; +} // end of ParseString + +/***********************************************************************/ +/* Parse a JSON numeric value. */ +/***********************************************************************/ +PVAL ParseNumeric(PGLOBAL g, int& i, STRG& src) +{ + char *s = src.str, buf[50]; + int n = 0, len = src.len; + short nd = 0; + bool has_dot = false; + bool has_e = false; + bool found_digit = false; + PVAL valp = NULL; + + for (; i < len; i++) { + switch (s[i]) { + case '.': + if (!found_digit || has_dot || has_e) + goto err; + + has_dot = true; + break; + case 'e': + case 'E': + if (!found_digit || has_e) + goto err; + + has_e = true; + found_digit = false; + break; + case '+': + if (!has_e) + goto err; + + // passthru + case '-': + if (found_digit) + goto err; + + break; + default: + if (isdigit(s[i])) { + if (has_dot && !has_e) + nd++; // Number of decimals + + found_digit = true; + } else + goto fin; + + }; // endswitch s[i] + + buf[n++] = s[i]; + } // endfor i + + fin: + if (found_digit) { + buf[n] = 0; + + if (has_dot || has_e) { + double dv = strtod(buf, NULL); + + valp = AllocateValue(g, &dv, TYPE_DOUBLE, nd); + } else { + int iv = strtol(buf, NULL, 10); + + valp = AllocateValue(g, &iv, TYPE_INT); + } // endif has + + i--; // Unstack following character + return valp; + } else { + strcpy(g->Message, "No digit found"); + return NULL; + } // endif found_digit + + err: + strcpy(g->Message, "Unexpected EOF in number"); + return NULL; +} // end of ParseNumeric + +/***********************************************************************/ +/* Serialize a JSON tree: */ +/***********************************************************************/ +PSZ Serialize(PGLOBAL g, PJSON jsp, FILE *fs, int pretty) +{ + bool b = false, err = true; + JOUT *jp; + + g->Message[0] = 0; + + if (!jsp) { + strcpy(g->Message, "Null json tree"); + return NULL; + } else if (!fs) { + // Serialize to a string + jp = new(g) JOUTSTR(g); + b = pretty == 1; + } else if (pretty == 2) { + // Serialize to a pretty file + jp = new(g) JOUTPRT(g, fs); + } else { + // Serialize to a flat file + jp = new(g) JOUTFILE(g, fs); + b = pretty == 1; + } // endif's + + switch (jsp->GetType()) { + case TYPE_JAR: + err = SerializeArray(jp, (PJAR)jsp, b); + break; + case TYPE_JOB: + err = (b && jp->WriteChr('\t')); + err |= SerializeObject(jp, (PJOB)jsp); + break; + case TYPE_JVAL: + err = SerializeValue(jp, (PJVAL)jsp); + break; + default: + strcpy(g->Message, "Invalid json tree"); + } // endswitch Type + + if (fs) { + fputc('\n', fs); + fclose(fs); + return (err) ? g->Message : NULL; + } else if (!err) { + PSZ str = ((JOUTSTR*)jp)->Strp; + + jp->WriteChr('\0'); + PlugSubAlloc(g, NULL, ((JOUTSTR*)jp)->N); + return str; + } else { + if (!g->Message[0]) + strcpy(g->Message, "Error in Serialize"); + + return NULL; + } // endif's + +} // end of Serialize + +/***********************************************************************/ +/* Serialize a JSON Array. */ +/***********************************************************************/ +bool SerializeArray(JOUT *js, PJAR jarp, bool b) +{ + bool first = true; + + + if (js->WriteChr('[')) + return true; + else if (b && (js->WriteStr(EL) || js->WriteChr('\t'))) + return true; + + for (int i = 0; i < jarp->size(); i++) { + if (first) + first = false; + else if (js->WriteChr(',')) + return true; + else if (b && (js->WriteStr(EL) || js->WriteChr('\t'))) + return true; + + if (SerializeValue(js, jarp->GetValue(i))) + return true; + + } // endfor i + + if (b && js->WriteStr(EL)) + return true; + + return js->WriteChr(']'); +} // end of SerializeArray + +/***********************************************************************/ +/* Serialize a JSON Object. */ +/***********************************************************************/ +bool SerializeObject(JOUT *js, PJOB jobp) +{ + bool first = true; + + if (js->WriteChr('{')) + return true; + + for (PJPR pair = jobp->First; pair; pair = pair->Next) { + if (first) + first = false; + else if (js->WriteChr(',')) + return true; + + if (js->WriteChr('"') || + js->WriteStr(pair->Key) || + js->WriteChr('"') || + js->WriteChr(':') || + SerializeValue(js, pair->Val)) + return true; + + } // endfor i + + return js->WriteChr('}'); +} // end of SerializeObject + +/***********************************************************************/ +/* Serialize a JSON Value. */ +/***********************************************************************/ +bool SerializeValue(JOUT *js, PJVAL jvp) +{ + PJAR jap; + PJOB jop; + PVAL valp; + + if ((jap = jvp->GetArray())) + return SerializeArray(js, jap, false); + else if ((jop = jvp->GetObject())) + return SerializeObject(js, jop); + else if (!(valp = jvp->Value) || valp->IsNull()) + return js->WriteStr("null"); + else switch (valp->GetType()) { + case TYPE_TINY: + return js->WriteStr(valp->GetTinyValue() ? "true" : "false"); + case TYPE_STRING: + return js->Escape(valp->GetCharValue()); + default: + if (valp->IsTypeNum()) { + char buf[32]; + + return js->WriteStr(valp->GetCharString(buf)); + } // endif valp + + } // endswitch Type + +strcpy(js->g->Message, "Unrecognized value"); +return true; +} // end of SerializeValue + +/* -------------------------- Class JOUTSTR -------------------------- */ + +/***********************************************************************/ +/* JOUTSTR constructor. */ +/***********************************************************************/ +JOUTSTR::JOUTSTR(PGLOBAL g) : JOUT(g) +{ + PPOOLHEADER pph = (PPOOLHEADER)g->Sarea; + + N = 0; + Max = pph->FreeBlk; + Max = (Max > 512) ? Max - 512 : Max; + Strp = (char*)PlugSubAlloc(g, NULL, 0); // Size not know yet +} // end of JOUTSTR constructor + +/***********************************************************************/ +/* Concatenate a string to the Serialize string. */ +/***********************************************************************/ +bool JOUTSTR::WriteStr(const char *s) +{ + if (s) { + size_t len = strlen(s); + + if (N + len > Max) + return true; + + memcpy(Strp + N, s, len); + N += len; + return false; + } else + return true; + +} // end of WriteStr + +/***********************************************************************/ +/* Concatenate a character to the Serialize string. */ +/***********************************************************************/ +bool JOUTSTR::WriteChr(const char c) +{ + if (N + 1 > Max) + return true; + + Strp[N++] = c; + return false; +} // end of WriteChr + +/***********************************************************************/ +/* Escape and Concatenate a string to the Serialize string. */ +/***********************************************************************/ +bool JOUTSTR::Escape(const char *s) +{ + WriteChr('"'); + + for (unsigned int i = 0; i < strlen(s); i++) + switch (s[i]) { + case '"': + case '\\': + case '\t': + case '\n': + case '\r': + case '\b': + case '\f': WriteChr('\\'); + // passthru + default: + WriteChr(s[i]); + break; + } // endswitch s[i] + + WriteChr('"'); + return false; +} // end of Escape + +/* ------------------------- Class JOUTFILE -------------------------- */ + +/***********************************************************************/ +/* Write a string to the Serialize file. */ +/***********************************************************************/ +bool JOUTFILE::WriteStr(const char *s) +{ + // This is temporary + fputs(s, Stream); + return false; +} // end of WriteStr + +/***********************************************************************/ +/* Write a character to the Serialize file. */ +/***********************************************************************/ +bool JOUTFILE::WriteChr(const char c) +{ + // This is temporary + fputc(c, Stream); + return false; +} // end of WriteChr + +/***********************************************************************/ +/* Escape and Concatenate a string to the Serialize string. */ +/***********************************************************************/ +bool JOUTFILE::Escape(const char *s) +{ + // This is temporary + fputc('"', Stream); + + for (unsigned int i = 0; i < strlen(s); i++) + switch (s[i]) { + case '"': fputs("\\\"", Stream); break; + case '\\': fputs("\\\\", Stream); break; + case '\t': fputs("\\t", Stream); break; + case '\n': fputs("\\n", Stream); break; + case '\r': fputs("\\r", Stream); break; + case '\b': fputs("\\b", Stream); break; + case '\f': fputs("\\f", Stream); break; + default: + fputc(s[i], Stream); + break; + } // endswitch s[i] + + fputc('"', Stream); + return false; +} // end of Escape + +/* ------------------------- Class JOUTPRT --------------------------- */ + +/***********************************************************************/ +/* Write a string to the Serialize pretty file. */ +/***********************************************************************/ +bool JOUTPRT::WriteStr(const char *s) +{ + // This is temporary + if (B) { + fputs(EL, Stream); + M--; + + for (int i = 0; i < M; i++) + fputc('\t', Stream); + + B = false; + } // endif B + + fputs(s, Stream); + return false; +} // end of WriteStr + +/***********************************************************************/ +/* Write a character to the Serialize pretty file. */ +/***********************************************************************/ +bool JOUTPRT::WriteChr(const char c) +{ + switch (c) { + case ':': + fputs(": ", Stream); + break; + case '{': + case '[': +#if 0 + if (M) + fputs(EL, Stream); + + for (int i = 0; i < M; i++) + fputc('\t', Stream); +#endif // 0 + + fputc(c, Stream); + fputs(EL, Stream); + M++; + + for (int i = 0; i < M; i++) + fputc('\t', Stream); + + break; + case '}': + case ']': + M--; + fputs(EL, Stream); + + for (int i = 0; i < M; i++) + fputc('\t', Stream); + + fputc(c, Stream); + B = true; + break; + case ',': + fputc(c, Stream); + fputs(EL, Stream); + + for (int i = 0; i < M; i++) + fputc('\t', Stream); + + B = false; + break; + default: + fputc(c, Stream); + } // endswitch c + +return false; +} // end of WriteChr + +/* -------------------------- Class JOBJECT -------------------------- */ + +/***********************************************************************/ +/* Add a new pair to an Object. */ +/***********************************************************************/ +PJPR JOBJECT::AddPair(PGLOBAL g, PSZ key) +{ + PJPR jpp = new(g) JPAIR(key); + + if (Last) + Last->Next = jpp; + else + First = jpp; + + Last = jpp; + Size++; + return jpp; +} // end of AddPair + +/***********************************************************************/ +/* Get the value corresponding to the given key. */ +/***********************************************************************/ +PJVAL JOBJECT::GetValue(const char* key) +{ + for (PJPR jp = First; jp; jp = jp->Next) + if (!strcmp(jp->Key, key)) + return jp->Val; + + return NULL; +} // end of GetValue; + +/***********************************************************************/ +/* Return the text corresponding to all keys (XML like). */ +/***********************************************************************/ +PSZ JOBJECT::GetText(PGLOBAL g) +{ + char *p, *text = (char*)PlugSubAlloc(g, NULL, 0); + bool b = true; + + if (!First) + return NULL; + else for (PJPR jp = First; jp; jp = jp->Next) { + if (!(p = jp->Val->GetString())) + p = "???"; + + if (b) { + strcpy(text, p); + b = false; + } else + strcat(strcat(text, " "), p); + + } // endfor jp + + PlugSubAlloc(g, NULL, strlen(text) + 1); + return text; +} // end of GetValue; + +/***********************************************************************/ +/* Set or add a value corresponding to the given key. */ +/***********************************************************************/ +void JOBJECT::SetValue(PGLOBAL g, PJVAL jvp, PSZ key) +{ + PJPR jp; + + for (jp = First; jp; jp = jp->Next) + if (!strcmp(jp->Key, key)) { + jp->Val = jvp; + break; + } // endif key + + if (!jp) { + jp = AddPair(g, key); + jp->Val = jvp; + } // endif jp + +} // end of SetValue + +/* -------------------------- Class JARRAY --------------------------- */ + +/***********************************************************************/ +/* Make the array of values from the values list. */ +/***********************************************************************/ +void JARRAY::InitArray(PGLOBAL g) +{ + int i; + PJVAL jvp; + + for (Size = 0, jvp = First; jvp; jvp = jvp->Next) + if (!jvp->Del) + Size++; + + if (!Size) { + return; + } else if (Size > Alloc) { + // No need to realloc after deleting values + Mvals = (PJVAL*)PlugSubAlloc(g, NULL, Size * sizeof(PJVAL)); + Alloc = Size; + } // endif Size + + for (i = 0, jvp = First; jvp; jvp = jvp->Next) + if (!jvp->Del) + Mvals[i++] = jvp; + +} // end of InitArray + +/***********************************************************************/ +/* Get the Nth value of an Array. */ +/***********************************************************************/ +PJVAL JARRAY::GetValue(int i) +{ + if (Mvals && i >= 0 && i < Size) + return Mvals[i]; + else + return NULL; +} // end of GetValue + +/***********************************************************************/ +/* Add a Value to the Arrays Value list. */ +/***********************************************************************/ +PJVAL JARRAY::AddValue(PGLOBAL g, PJVAL jvp) +{ + if (!jvp) + jvp = new(g) JVALUE; + + if (Last) + Last->Next = jvp; + else + First = jvp; + + Last = jvp; + return jvp; +} // end of AddValue + +/***********************************************************************/ +/* Add a Value to the Arrays Value list. */ +/***********************************************************************/ +bool JARRAY::SetValue(PGLOBAL g, PJVAL jvp, int n) +{ + int i = 0; + PJVAL jp, *jpp = &First; + + for (i = 0, jp = First; i < n; i++, jp = *(jpp = &jp->Next)) + if (!jp) + *jpp = jp = new(g) JVALUE; + + *jpp = jvp; + jvp->Next = (jp ? jp->Next : NULL); + return false; +} // end of SetValue + +/***********************************************************************/ +/* Delete a Value from the Arrays Value list. */ +/***********************************************************************/ +bool JARRAY::DeleteValue(int n) +{ + PJVAL jvp = GetValue(n); + + if (jvp) { + jvp->Del = true; + return false; + } else + return true; + +} // end of DeleteValue + +/* -------------------------- Class JVALUE- -------------------------- */ + +/***********************************************************************/ +/* Constructor for a Value with a given string or numeric value. */ +/***********************************************************************/ +JVALUE::JVALUE(PGLOBAL g, PVAL valp) : JSON() +{ + Jsp = NULL; + Value = AllocateValue(g, valp); + Next = NULL; + Del = false; +} // end of JVALUE constructor + +/***********************************************************************/ +/* Returns the type of the Value's value. */ +/***********************************************************************/ +JTYP JVALUE::GetValType(void) +{ + if (Jsp) + return Jsp->GetType(); + else if (Value) + return (JTYP)Value->GetType(); + else + return (JTYP)TYPE_VOID; + +} // end of GetValType + +/***********************************************************************/ +/* Return the Value's Object value. */ +/***********************************************************************/ +PJOB JVALUE::GetObject(void) +{ + if (Jsp && Jsp->GetType() == TYPE_JOB) + return (PJOB)Jsp; + + return NULL; +} // end of GetObject + +/***********************************************************************/ +/* Return the Value's Array value. */ +/***********************************************************************/ +PJAR JVALUE::GetArray(void) +{ + if (Jsp && Jsp->GetType() == TYPE_JAR) + return (PJAR)Jsp; + + return NULL; +} // end of GetArray + +/***********************************************************************/ +/* Return the Value's Integer value. */ +/***********************************************************************/ +int JVALUE::GetInteger(void) +{ + return (Value) ? Value->GetIntValue() : 0; +} // end of GetInteger + +/***********************************************************************/ +/* Return the Value's Double value. */ +/***********************************************************************/ +double JVALUE::GetFloat(void) +{ + return (Value) ? Value->GetFloatValue() : 0.0; +} // end of GetFloat + +/***********************************************************************/ +/* Return the Value's String value. */ +/***********************************************************************/ +PSZ JVALUE::GetString(void) +{ + char buf[32]; + return (Value) ? Value->GetCharString(buf) : NULL; +} // end of GetString + +/***********************************************************************/ +/* Set the Value's value as the given integer. */ +/***********************************************************************/ +void JVALUE::SetInteger(PGLOBAL g, int n) +{ + Value = AllocateValue(g, &n, TYPE_INT); +} // end of AddInteger + +/***********************************************************************/ +/* Set the Value's value as the given DOUBLE. */ +/***********************************************************************/ +void JVALUE::SetFloat(PGLOBAL g, double f) +{ + Value = AllocateValue(g, &f, TYPE_DOUBLE, 6); +} // end of AddFloat + +/***********************************************************************/ +/* Set the Value's value as the given string. */ +/***********************************************************************/ +void JVALUE::SetString(PGLOBAL g, PSZ s) +{ + Value = AllocateValue(g, s, TYPE_STRING); +} // end of AddFloat + diff --git a/storage/connect/jsonudf.cpp b/storage/connect/jsonudf.cpp index 59ffd5a424a..132c5060064 100644 --- a/storage/connect/jsonudf.cpp +++ b/storage/connect/jsonudf.cpp @@ -44,9 +44,10 @@ DllExport void Json_Object_Grp_deinit(UDF_INIT*); /***********************************************************************/ /* Allocate and initialise the memory area. */ /***********************************************************************/ -my_bool JsonInit(UDF_INIT *initid, char *message, unsigned long len) +my_bool JsonInit(UDF_INIT *initid, char *message, unsigned long reslen, + unsigned long memlen) { - PGLOBAL g = PlugInit(NULL, len); + PGLOBAL g = PlugInit(NULL, memlen); if (!g) { strcpy(message, "Allocation error"); @@ -59,7 +60,7 @@ my_bool JsonInit(UDF_INIT *initid, char *message, unsigned long len) initid->ptr = (char*)g; initid->maybe_null = false; - initid->max_length = len - 512; + initid->max_length = reslen; return false; } // end of Json_Object_init @@ -72,6 +73,92 @@ static my_bool IsJson(UDF_ARGS *args, int i) !strnicmp(args->attributes[i], "Json_", 5)); } // end of IsJson +/***********************************************************************/ +/* Calculate the reslen and memlen needed by a function. */ +/***********************************************************************/ +static my_bool CalcLen(UDF_ARGS *args, my_bool obj, + unsigned long& reslen, unsigned long& memlen) +{ + unsigned long i, k; + reslen = args->arg_count + 2; + + // Calculate the result max length + for (i = 0; i < args->arg_count; i++) { + if (obj) { + if (!(k = args->attribute_lengths[i])) + k = strlen(args->attributes[i]); + + reslen += (k + 3); // For quotes and : + } // endif obj + + switch (args->arg_type[i]) { + case STRING_RESULT: + if (IsJson(args, i)) + reslen += args->lengths[i]; + else + reslen += (args->lengths[i] + 1) * 2; // Pessimistic ! + + break; + case INT_RESULT: + reslen += 20; + break; + case REAL_RESULT: + reslen += 31; + break; + case DECIMAL_RESULT: + reslen += (args->lengths[i] + 7); // 6 decimals + break; + case TIME_RESULT: + case ROW_RESULT: + case IMPOSSIBLE_RESULT: + default: + // What should we do here ? + break; + } // endswitch arg_type + + } // endfor i + + // Calculate the amount of memory needed + memlen = 1024 + sizeof(JOUTSTR) + reslen; + + for (i = 0; i < args->arg_count; i++) { + memlen += (args->lengths[i] + sizeof(JVALUE)); + + if (obj) { + if (!(k = args->attribute_lengths[i])) + k = strlen(args->attributes[i]); + + memlen += (k + sizeof(JOBJECT) + sizeof(JPAIR)); + } else + memlen += sizeof(JARRAY); + + switch (args->arg_type[i]) { + case STRING_RESULT: + if (IsJson(args, i)) + memlen += args->lengths[i] * 5; // Estimate parse memory + + memlen += sizeof(TYPVAL); + break; + case INT_RESULT: + memlen += sizeof(TYPVAL); + break; + case REAL_RESULT: + case DECIMAL_RESULT: + memlen += sizeof(TYPVAL); + break; + case TIME_RESULT: + case ROW_RESULT: + case IMPOSSIBLE_RESULT: + default: + // What should we do here ? + break; + } // endswitch arg_type + + } // endfor i + + return false; +} // end of CalcLen + /***********************************************************************/ /* Make a zero terminated string from the passed argument. */ /***********************************************************************/ @@ -131,25 +218,25 @@ static PSZ MakeKey(PGLOBAL g, UDF_ARGS *args, int i) /***********************************************************************/ static PJVAL MakeValue(PGLOBAL g, UDF_ARGS *args, int i) { - char *str; + char *sap = args->args[i]; PJVAL jvp = new(g) JVALUE; - switch (args->arg_type[i]) { + if (sap) switch (args->arg_type[i]) { case STRING_RESULT: - if ((str = MakePSZ(g, args, i))) { + if (args->lengths[i]) { if (IsJson(args, i)) - jvp->SetValue(ParseJson(g, str, strlen(str), 0)); + jvp->SetValue(ParseJson(g, sap, args->lengths[i], 0)); else - jvp->SetString(g, str); + jvp->SetString(g, MakePSZ(g, args, i)); } // endif str break; case INT_RESULT: - jvp->SetInteger(g, *(int*)args->args[i]); + jvp->SetInteger(g, *(int*)sap); break; case REAL_RESULT: - jvp->SetFloat(g, *(double*)args->args[i]); + jvp->SetFloat(g, *(double*)sap); break; case DECIMAL_RESULT: jvp->SetFloat(g, atof(MakePSZ(g, args, i))); @@ -169,12 +256,15 @@ static PJVAL MakeValue(PGLOBAL g, UDF_ARGS *args, int i) /***********************************************************************/ my_bool Json_Value_init(UDF_INIT *initid, UDF_ARGS *args, char *message) { + unsigned long reslen, memlen; + if (args->arg_count > 1) { strcpy(message, "Json_Value cannot accept more than 1 argument"); return true; - } // endif arg_count + } else + CalcLen(args, false, reslen, memlen); - return JsonInit(initid, message, 1024); + return JsonInit(initid, message, reslen, memlen); } // end of Json_Value_init char *Json_Value(UDF_INIT *initid, UDF_ARGS *args, char *result, @@ -204,7 +294,10 @@ void Json_Value_deinit(UDF_INIT* initid) /***********************************************************************/ my_bool Json_Array_init(UDF_INIT *initid, UDF_ARGS *args, char *message) { - return JsonInit(initid, message, 8192); + unsigned long reslen, memlen; + + CalcLen(args, false, reslen, memlen); + return JsonInit(initid, message, reslen, memlen); } // end of Json_Array_init char *Json_Array(UDF_INIT *initid, UDF_ARGS *args, char *result, @@ -240,11 +333,14 @@ void Json_Array_deinit(UDF_INIT* initid) /***********************************************************************/ my_bool Json_Object_init(UDF_INIT *initid, UDF_ARGS *args, char *message) { - return JsonInit(initid, message, 8192); + unsigned long reslen, memlen; + + CalcLen(args, true, reslen, memlen); + return JsonInit(initid, message, reslen, memlen); } // end of Json_Object_init char *Json_Object(UDF_INIT *initid, UDF_ARGS *args, char *result, - unsigned long *res_length, char *is_null, char *error) + unsigned long *res_length, char *is_null, char *error) { char *str; uint i; @@ -274,10 +370,18 @@ void Json_Object_deinit(UDF_INIT* initid) /***********************************************************************/ my_bool Json_Array_Grp_init(UDF_INIT *initid, UDF_ARGS *args, char *message) { + unsigned long reslen, memlen, n = 10; + if (args->arg_count != 1) { strcpy(message, "Json_Array_Grp can only accept 1 argument"); return true; - } else if (JsonInit(initid, message, 16384)) + } else + CalcLen(args, false, reslen, memlen); + + reslen *= n; + memlen *= n; + + if (JsonInit(initid, message, reslen, memlen)) return true; PGLOBAL g = (PGLOBAL)initid->ptr; @@ -330,10 +434,18 @@ void Json_Array_Grp_deinit(UDF_INIT* initid) /***********************************************************************/ my_bool Json_Object_Grp_init(UDF_INIT *initid, UDF_ARGS *args, char *message) { + unsigned long reslen, memlen, n = 10; + if (args->arg_count != 2) { strcpy(message, "Json_Array_Grp can only accept 2 argument"); return true; - } else if (JsonInit(initid, message, 16384)) + } else + CalcLen(args, true, reslen, memlen); + + reslen *= n; + memlen *= n; + + if (JsonInit(initid, message, reslen, memlen)) return true; PGLOBAL g = (PGLOBAL)initid->ptr; diff --git a/storage/connect/tabjson.cpp b/storage/connect/tabjson.cpp index b4b9ca18cda..362b28ba79b 100644 --- a/storage/connect/tabjson.cpp +++ b/storage/connect/tabjson.cpp @@ -825,7 +825,9 @@ PJSON JSONCOL::GetRow(PGLOBAL g) PJSON nwr, row = Tjp->Row; for (int i = 0; i < Nod-1 && row; i++) { - switch (row->GetType()) { + if (Nodes[i+1].Op == OP_XX) + break; + else switch (row->GetType()) { case TYPE_JOB: if (!Nodes[i].Key) // Expected Array was not there @@ -932,7 +934,11 @@ void JSONCOL::WriteColumn(PGLOBAL g) jsp = ParseJson(g, s, (int)strlen(s), 0); if (arp) { - arp->AddValue(g, new(g) JVALUE(jsp)); + if (Nod > 1 && Nodes[Nod-2].Rank) + arp->SetValue(g, new(g) JVALUE(jsp), Nodes[Nod-2].Rank-1); + else + arp->AddValue(g, new(g) JVALUE(jsp)); + arp->InitArray(g); } else if (objp) { if (Nod > 1 && Nodes[Nod-2].Key)