LCOV - code coverage report
Current view: top level - monetdb5/modules/atoms - json.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1465 1995 73.4 %
Date: 2025-03-25 21:27:32 Functions: 59 71 83.1 %

          Line data    Source code
       1             : /*
       2             :  * SPDX-License-Identifier: MPL-2.0
       3             :  *
       4             :  * This Source Code Form is subject to the terms of the Mozilla Public
       5             :  * License, v. 2.0.  If a copy of the MPL was not distributed with this
       6             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       7             :  *
       8             :  * Copyright 2024, 2025 MonetDB Foundation;
       9             :  * Copyright August 2008 - 2023 MonetDB B.V.;
      10             :  * Copyright 1997 - July 2008 CWI.
      11             :  */
      12             : 
      13             : /*
      14             :  * (c) 2013 Martin Kersten
      15             :  */
      16             : #include "monetdb_config.h"
      17             : #include "gdk.h"
      18             : #include "mal.h"
      19             : #include "mal_client.h"
      20             : #include "mal_instruction.h"
      21             : #include "mal_exception.h"
      22             : #include "mal_interpreter.h"
      23             : 
      24             : #include "json.h"
      25             : 
      26             : typedef str json;
      27             : 
      28             : // just validate the string according to www.json.org
      29             : // A straightforward recursive solution
      30             : #define skipblancs(J)                                                   \
      31             :         do {                                                                            \
      32             :                 for(; *(J); (J)++)                                              \
      33             :                         if (*(J) != ' ' &&                                      \
      34             :                                 *(J) != '\n' &&                                 \
      35             :                                 *(J) != '\t' &&                                 \
      36             :                                 *(J) != '\r')                                   \
      37             :                                 break;                                                  \
      38             :         } while (0)
      39             : 
      40             : #define CHECK_JSON(jt)                                                                                                  \
      41             :         do {                                                                                                                            \
      42             :                 if (jt == NULL || jt->error) {                                                                       \
      43             :                         char *msg;                                                                                                      \
      44             :                         if (jt) {                                                                                                       \
      45             :                                 msg = jt->error;                                                                             \
      46             :                                 jt->error = NULL;                                                                            \
      47             :                                 JSONfree(jt);                                                                                   \
      48             :                         } else {                                                                                                        \
      49             :                                 msg = createException(MAL, "json.new", SQLSTATE(HY013) MAL_MALLOC_FAIL); \
      50             :                         }                                                                                                                       \
      51             :                         return msg;                                                                                                     \
      52             :                 }                                                                                                                               \
      53             :         } while (0)
      54             : 
      55             : static int TYPE_json;
      56             : 
      57             : /* Internal constructors. */
      58             : static int jsonhint = 8;
      59             : 
      60             : static JSON *
      61     2181398 : JSONnewtree(void)
      62             : {
      63     2181398 :         JSON *js;
      64             : 
      65     2181398 :         js = GDKzalloc(sizeof(JSON));
      66     2181404 :         if (js == NULL)
      67             :                 return NULL;
      68     2181404 :         js->elm = GDKzalloc(sizeof(JSONterm) * jsonhint);
      69     2181404 :         if (js->elm == NULL) {
      70           0 :                 GDKfree(js);
      71           0 :                 return NULL;
      72             :         }
      73     2181404 :         js->size = jsonhint;
      74     2181404 :         return js;
      75             : }
      76             : 
      77             : static int
      78    48065451 : JSONnew(JSON *js)
      79             : {
      80    48065451 :         JSONterm *term;
      81             : 
      82    48065451 :         if (js->free == js->size) {
      83        3239 :                 term = GDKrealloc(js->elm, sizeof(JSONterm) * (js->size + 8));
      84        3239 :                 if (term == NULL) {
      85           0 :                         js->error = createException(MAL, "json.new",
      86             :                                                                                 SQLSTATE(HY013) MAL_MALLOC_FAIL);
      87           0 :                         return js->free - 1;
      88             :                 }
      89        3239 :                 js->elm = term;
      90        3239 :                 memset(term + js->size, 0, 8 * sizeof(JSONterm));
      91        3239 :                 js->size += 8;
      92        3239 :                 if (jsonhint < js->size)
      93        3239 :                         jsonhint = js->size;
      94             :         }
      95    48065451 :         return js->free++;
      96             : }
      97             : 
      98             : /* Delete a JSON structure. */
      99             : void
     100     2181399 : JSONfree(JSON *c)
     101             : {
     102     2181399 :         if (c == 0)
     103             :                 return;
     104     2181398 :         freeException(c->error);
     105     2181398 :         GDKfree(c->elm);
     106     2181404 :         GDKfree(c);
     107             : }
     108             : 
     109             : static str
     110    48005325 : JSONtoStorageString(JSON *jt, int idx, json *ret, size_t *out_size)
     111             : {
     112    48005325 :         char *p = *ret;
     113    48005325 :         size_t sz = 0;
     114    48005325 :         str msg = MAL_SUCCEED;
     115             : 
     116    48005325 :         if (THRhighwater()) {
     117           0 :                 msg = createException(MAL, "json.new",
     118             :                                                                         SQLSTATE(42000)
     119             :                                                                         "JSON object too complex to render into string.");
     120           0 :                 return msg;
     121             :         }
     122             : 
     123             : 
     124    48005325 :         switch(jt->elm[idx].kind) {
     125     3304326 :         case JSON_OBJECT:
     126     3304326 :                 *p++ = '{';
     127     3304326 :                 *out_size += 1;
     128    25526072 :                 for(int i = jt->elm[idx].next; i != 0; i = jt->elm[i].next) {
     129    22221746 :                         sz = 0;
     130    22221746 :                         if (i != jt->elm[idx].next) {
     131    18924563 :                                 *p++ = ',';
     132    18924563 :                                 *out_size += 1;
     133             :                         }
     134    22221746 :                         msg = JSONtoStorageString(jt, i, &p, &sz);
     135    22221746 :                         if (msg != MAL_SUCCEED) {
     136           0 :                                 return msg;
     137             :                         }
     138    22221746 :                         *out_size += sz;
     139    22221746 :                         p += sz;
     140             :                 }
     141     3304326 :                 *p++ = '}';
     142     3304326 :                 *out_size += 1;
     143     3304326 :                 break;
     144     1582093 :         case JSON_ARRAY:
     145     1582093 :                 *p++ = '[';
     146     1582093 :                 *out_size += 1;
     147     2621074 :                 for(int i = jt->elm[idx].next; i != 0; i = jt->elm[i].next) {
     148     1038981 :                         sz = 0;
     149     1038981 :                         if (i != jt->elm[idx].next) {
     150      437794 :                                 *p++ = ',';
     151      437794 :                                 *out_size += 1;
     152             :                         }
     153     1038981 :                         msg = JSONtoStorageString(jt, i, &p, &sz);
     154     1038981 :                         if (msg != MAL_SUCCEED) {
     155           0 :                                 return msg;
     156             :                         }
     157     1038981 :                         *out_size += sz;
     158     1038981 :                         p += sz;
     159             :                 }
     160     1582093 :                 *p++ = ']';
     161     1582093 :                 *out_size += 1;
     162     1582093 :                 break;
     163    22221746 :         case JSON_ELEMENT:
     164    22221746 :                 *p++ = '"';
     165    22221746 :                 strncpy(p, jt->elm[idx].value, jt->elm[idx].valuelen);
     166    22221746 :                 p += jt->elm[idx].valuelen;
     167    22221746 :                 *p++ = '"';
     168    22221746 :                 *p++ = ':';
     169    22221746 :                 *out_size = jt->elm[idx].valuelen + 3;
     170    22221746 :                 msg = JSONtoStorageString(jt, jt->elm[idx].child, &p, &sz);
     171    22221746 :                 if (msg != MAL_SUCCEED) {
     172             :                         return msg;
     173             :                 }
     174    22221746 :                 *out_size += sz;
     175    22221746 :                 p += sz;
     176    22221746 :                 break;
     177    17590033 :         case JSON_NUMBER:
     178             :                 /* fall through */
     179             :         case JSON_STRING:
     180    17590033 :                 strncpy(p, jt->elm[idx].value, jt->elm[idx].valuelen);
     181    17590033 :                 *out_size += jt->elm[idx].valuelen;
     182    17590033 :                 p += *out_size;
     183    17590033 :                 break;
     184      342592 :         case JSON_VALUE:
     185      342592 :                 msg = JSONtoStorageString(jt, jt->elm[idx].child, &p, &sz);
     186      342592 :                 if (msg != MAL_SUCCEED) {
     187             :                         return msg;
     188             :                 }
     189      342592 :                 *out_size += sz;
     190      342592 :                 p += sz;
     191      342592 :                 break;
     192     2964535 :         case JSON_NULL:
     193     2964535 :                 strncpy(p, "null", 5);
     194     2964535 :                 *out_size += 4;
     195     2964535 :                 p += *out_size;
     196     2964535 :                 break;
     197           0 :         default:
     198           0 :                 msg = createException(MAL, "json.new", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     199           0 :                 break;
     200             :         }
     201             : 
     202    48005325 :         *p = 0;
     203             : 
     204    48005325 :         return msg;
     205             : }
     206             : 
     207             : static str JSONstr2json(json *ret, const char *const*j);
     208             : 
     209             : static ssize_t
     210     2200098 : JSONfromString(const char *src, size_t *len, void **J, bool external)
     211             : {
     212     2200098 :         json *buf = (json *) J;
     213     2200098 :         if(*buf) {
     214     2200036 :                 GDKfree(*buf);
     215     2200036 :                 *buf = NULL;
     216             :         }
     217     4380196 :         if (strNil(src) || (external && strncmp(src, "nil", 3) == 0)) {
     218       20000 :                 *buf = GDKstrdup(str_nil);
     219       20000 :                 if (*buf == NULL)
     220             :                         return -1;
     221       20000 :                 *len = 2;
     222       40000 :                 return strNil(src) ? 1 : 3;
     223             :         } else {
     224     2180098 :                 str msg = JSONstr2json(buf, &src);
     225     2180098 :                 if (msg != MAL_SUCCEED) {
     226           2 :                         GDKerror("%s", getExceptionMessageAndState(msg));
     227           2 :                         freeException(msg);
     228           2 :                         return -1;
     229             :                 }
     230             :         }
     231     2180096 :         *len = strlen(*buf) + 1;
     232     2180096 :         return (ssize_t) *len - 1;
     233             : }
     234             : 
     235             : static ssize_t
     236         564 : JSONtoString(str *s, size_t *len, const void *SRC, bool external)
     237             : {
     238         564 :         const char *src = SRC;
     239         564 :         size_t cnt;
     240         564 :         const char *c;
     241         564 :         char *dst;
     242             : 
     243         564 :         if (strNil(src)) {
     244           1 :                 if (*s == NULL || *len < 4) {
     245           1 :                         GDKfree(*s);
     246           1 :                         *len = 4;
     247           1 :                         *s = GDKmalloc(4);
     248           1 :                         if (*s == NULL)
     249             :                                 return -1;
     250             :                 }
     251           1 :                 if (external) {
     252           1 :                         assert(*len >= strlen("nil") + 1);
     253           1 :                         strcpy(*s, "nil");
     254           1 :                         return 3;
     255             :                 }
     256           0 :                 assert(*len >= strlen(str_nil) + 1);
     257           0 :                 strcpy(*s, str_nil);
     258           0 :                 return 1;
     259             :         }
     260             :         /* count how much space we need for the output string */
     261         563 :         if (external) {
     262             :                 cnt = 3;                                /* two times " plus \0 */
     263       25308 :                 for (c = src; *c; c++)
     264       24760 :                         switch (*c) {
     265        4253 :                         case '"':
     266             :                         case '\\':
     267             :                         case '\n':
     268        4253 :                                 cnt++;
     269             :                                 /* fall through */
     270       24760 :                         default:
     271       24760 :                                 cnt++;
     272       24760 :                                 break;
     273             :                         }
     274             :         } else {
     275          15 :                 cnt = strlen(src) + 1;  /* just the \0 */
     276             :         }
     277             : 
     278         563 :         if (cnt > (size_t) *len) {
     279          95 :                 GDKfree(*s);
     280          95 :                 *s = GDKmalloc(cnt);
     281          95 :                 if (*s == NULL)
     282             :                         return -1;
     283          95 :                 *len = cnt;
     284             :         }
     285         563 :         dst = *s;
     286         563 :         if (external) {
     287         548 :                 *dst++ = '"';
     288       25308 :                 for (c = src; *c; c++) {
     289       24760 :                         switch (*c) {
     290        4253 :                         case '"':
     291             :                         case '\\':
     292        4253 :                                 *dst++ = '\\';
     293             :                                 /* fall through */
     294       24760 :                         default:
     295       24760 :                                 *dst++ = *c;
     296       24760 :                                 break;
     297           0 :                         case '\n':
     298           0 :                                 *dst++ = '\\';
     299           0 :                                 *dst++ = 'n';
     300           0 :                                 break;
     301             :                         }
     302             :                 }
     303         548 :                 *dst++ = '"';
     304         548 :                 *dst = 0;
     305             :         } else {
     306          15 :                 dst += snprintf(dst, cnt, "%s", src);
     307             :         }
     308         563 :         return (ssize_t) (dst - *s);
     309             : }
     310             : 
     311             : static BAT *
     312           1 : JSONdumpInternal(const JSON *jt, int depth)
     313             : {
     314           1 :         int i, idx;
     315           1 :         JSONterm *je;
     316           1 :         size_t buflen = 1024;
     317           1 :         char *buffer = GDKmalloc(buflen);
     318           1 :         BAT *bn = COLnew(0, TYPE_str, 0, TRANSIENT);
     319             : 
     320           1 :         if (!buffer || !bn) {
     321           0 :                 GDKfree(buffer);
     322           0 :                 BBPreclaim(bn);
     323           0 :                 return NULL;
     324             :         }
     325             : 
     326          56 :         for (idx = 0; idx < jt->free; idx++) {
     327          55 :                 size_t datlen = 0;
     328          55 :                 je = jt->elm + idx;
     329             : 
     330          55 :                 if (datlen + depth * 4 + 512 > buflen) {
     331           0 :                         do {
     332           0 :                                 buflen += 1024;
     333           0 :                         } while (datlen + depth * 4 + 512 > buflen);
     334           0 :                         char *newbuf = GDKrealloc(buffer, buflen);
     335           0 :                         if (newbuf == NULL) {
     336           0 :                                 GDKfree(buffer);
     337           0 :                                 BBPreclaim(bn);
     338           0 :                                 return NULL;
     339             :                         }
     340             :                         buffer = newbuf;
     341             :                 }
     342          55 :                 datlen += snprintf(buffer + datlen, buflen - datlen, "%*s", depth * 4,
     343             :                                                    "");
     344          55 :                 datlen += snprintf(buffer + datlen, buflen - datlen, "[%d] ", idx);
     345          55 :                 switch (je->kind) {
     346           7 :                 case JSON_OBJECT:
     347           7 :                         datlen += snprintf(buffer + datlen, buflen - datlen, "object ");
     348           7 :                         break;
     349           1 :                 case JSON_ARRAY:
     350           1 :                         datlen += snprintf(buffer + datlen, buflen - datlen, "array ");
     351           1 :                         break;
     352          23 :                 case JSON_ELEMENT:
     353          23 :                         datlen += snprintf(buffer + datlen, buflen - datlen, "element ");
     354          23 :                         break;
     355           4 :                 case JSON_VALUE:
     356           4 :                         datlen += snprintf(buffer + datlen, buflen - datlen, "value ");
     357           4 :                         break;
     358          15 :                 case JSON_STRING:
     359          15 :                         datlen += snprintf(buffer + datlen, buflen - datlen, "string ");
     360          15 :                         break;
     361           5 :                 case JSON_NUMBER:
     362           5 :                         datlen += snprintf(buffer + datlen, buflen - datlen, "number ");
     363           5 :                         break;
     364           0 :                 case JSON_BOOL:
     365           0 :                         datlen += snprintf(buffer + datlen, buflen - datlen, "bool ");
     366           0 :                         break;
     367           0 :                 case JSON_NULL:
     368           0 :                         datlen += snprintf(buffer + datlen, buflen - datlen, "null ");
     369           0 :                         break;
     370           0 :                 default:
     371           0 :                         datlen += snprintf(buffer + datlen, buflen - datlen, "unknown %d ",
     372             :                                                            (int) je->kind);
     373             :                 }
     374          55 :                 datlen += snprintf(buffer + datlen, buflen - datlen, "child %d list ",
     375             :                                                    je->child);
     376         122 :                 for (i = je->next; i; i = jt->elm[i].next) {
     377          67 :                         if (datlen + 10 > buflen) {
     378           0 :                                 buflen += 1024;
     379           0 :                                 char *newbuf = GDKrealloc(buffer, buflen);
     380           0 :                                 if (newbuf == NULL) {
     381           0 :                                         GDKfree(buffer);
     382           0 :                                         BBPreclaim(bn);
     383           0 :                                         return NULL;
     384             :                                 }
     385             :                                 buffer = newbuf;
     386             :                         }
     387          67 :                         datlen += snprintf(buffer + datlen, buflen - datlen, "%d ", i);
     388             :                 }
     389          55 :                 if (je->name) {
     390           0 :                         if (datlen + 10 + je->namelen > buflen) {
     391           0 :                                 do {
     392           0 :                                         buflen += 1024;
     393           0 :                                 } while (datlen + 10 + je->namelen > buflen);
     394           0 :                                 char *newbuf = GDKrealloc(buffer, buflen);
     395           0 :                                 if (newbuf == NULL) {
     396           0 :                                         GDKfree(buffer);
     397           0 :                                         BBPreclaim(bn);
     398           0 :                                         return NULL;
     399             :                                 }
     400             :                                 buffer = newbuf;
     401             :                         }
     402           0 :                         datlen += snprintf(buffer + datlen, buflen - datlen, "%.*s : ",
     403           0 :                                                            (int) je->namelen, je->name);
     404             :                 }
     405          55 :                 if (je->value) {
     406          51 :                         if (datlen + 10 + je->valuelen > buflen) {
     407           0 :                                 do {
     408           0 :                                         buflen += 1024;
     409           0 :                                 } while (datlen + 10 + je->valuelen > buflen);
     410           0 :                                 char *newbuf = GDKrealloc(buffer, buflen);
     411           0 :                                 if (newbuf == NULL) {
     412           0 :                                         GDKfree(buffer);
     413           0 :                                         BBPreclaim(bn);
     414           0 :                                         return NULL;
     415             :                                 }
     416             :                                 buffer = newbuf;
     417             :                         }
     418          51 :                         datlen += snprintf(buffer + datlen, buflen - datlen, "%.*s",
     419          51 :                                                            (int) je->valuelen, je->value);
     420             :                 }
     421          55 :                 if (BUNappend(bn, buffer, false) != GDK_SUCCEED) {
     422           0 :                         BBPreclaim(bn);
     423           0 :                         GDKfree(buffer);
     424           0 :                         return NULL;
     425             :                 }
     426             :         }
     427           1 :         GDKfree(buffer);
     428           1 :         return bn;
     429             : }
     430             : 
     431             : static str
     432           1 : JSONdump(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
     433             : {
     434           1 :         (void) mb;
     435           1 :         (void) cntxt;
     436             : 
     437           1 :         bat *ret = getArgReference_bat(stk, pci, 0);
     438           1 :         const json *val = (json *) getArgReference(stk, pci, 1);
     439           1 :         JSON *jt = JSONparse(*val);
     440             : 
     441           1 :         CHECK_JSON(jt);
     442           1 :         BAT *bn = JSONdumpInternal(jt, 0);
     443           1 :         JSONfree(jt);
     444           1 :         if (bn == NULL)
     445           0 :                 throw(MAL, "json.dump", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     446           1 :         *ret = bn->batCacheid;
     447           1 :         BBPkeepref(bn);
     448           1 :         return MAL_SUCCEED;
     449             : }
     450             : 
     451             : static str
     452           0 : JSONjson2str(str *ret, json *j)
     453             : {
     454           0 :         char *s = *j, *c;
     455             : 
     456           0 :         if (*s == '"')
     457           0 :                 s++;
     458           0 :         if ((s = GDKstrdup(s)) == NULL)
     459           0 :                 throw(MAL, "json.str", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     460           0 :         c = s + strlen(s) - 1;
     461           0 :         if (*c == '"')
     462           0 :                 *c = 0;
     463           0 :         *ret = s;
     464           0 :         return MAL_SUCCEED;
     465             : }
     466             : 
     467             : static str
     468           0 : JSON2json(json *ret, const json *j)
     469             : {
     470           0 :         *ret = GDKstrdup(*j);
     471           0 :         if (*ret == NULL)
     472           0 :                 throw(MAL, "json.json", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     473             :         return MAL_SUCCEED;
     474             : }
     475             : 
     476             : static str
     477     2180283 : JSONstr2json(json *ret, const char *const*j)
     478             : {
     479     2180283 :         str msg = MAL_SUCCEED;
     480     2180283 :         json buf = NULL;
     481     2180283 :         size_t ln = strlen(*j)+1;
     482     2180283 :         size_t out_size = 0;
     483             : 
     484     2180283 :         JSON *jt = NULL;
     485             : 
     486     2180283 :         if (strNil(*j)) {
     487           1 :                 buf = GDKstrdup(*j);
     488             :         } else {
     489     2180282 :                 jt = JSONparse(*j);
     490     2180282 :                 CHECK_JSON(jt);
     491             : 
     492     2180260 :                 buf = (json)GDKmalloc(ln);
     493             :         }
     494     2180261 :         if (buf == NULL) {
     495           0 :                 msg = createException(MAL, "json.new", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     496           0 :                 goto bailout;
     497             :         }
     498             : 
     499     2180261 :         if (jt != NULL) {
     500     2180260 :                 msg = JSONtoStorageString(jt, 0, &buf, &out_size);
     501     2180260 :                 if (msg != MAL_SUCCEED) {
     502           0 :                         GDKfree(buf);
     503           0 :                         goto bailout;
     504             :                 }
     505             :         }
     506             : 
     507     2180261 :         *ret = buf;
     508             : 
     509     2180261 :  bailout:
     510     2180261 :         JSONfree(jt);
     511     2180261 :         return msg;
     512             : }
     513             : 
     514             : static str
     515         370 : JSONisvalid(bit *ret, const char *const *j)
     516             : {
     517         370 :         if (strNil(*j)) {
     518           0 :                 *ret = bit_nil;
     519             :         } else {
     520         370 :                 JSON *jt = JSONparse(*j);
     521         370 :                 if (jt == NULL)
     522           0 :                         throw(MAL, "json.isvalid", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     523         370 :                 *ret = jt->error == MAL_SUCCEED;
     524         370 :                 JSONfree(jt);
     525             :         }
     526             :         return MAL_SUCCEED;
     527             : }
     528             : 
     529             : static str
     530          15 : JSONisobject(bit *ret, const json *js)
     531             : {
     532          15 :         if (strNil(*js)) {
     533           0 :                 *ret = bit_nil;
     534             :         } else {
     535             :                 const char *j = *js;
     536             : 
     537          15 :                 skipblancs(j);
     538          15 :                 *ret = *j == '{';
     539             :         }
     540          15 :         return MAL_SUCCEED;
     541             : }
     542             : 
     543             : static str
     544          16 : JSONisarray(bit *ret, const json *js)
     545             : {
     546          16 :         if (strNil(*js)) {
     547           0 :                 *ret = bit_nil;
     548             :         } else {
     549             :                 const char *j = *js;
     550             : 
     551          16 :                 skipblancs(j);
     552          16 :                 *ret = *j == '[';
     553             :         }
     554          16 :         return MAL_SUCCEED;
     555             : }
     556             : 
     557             : #ifdef GDKLIBRARY_JSON
     558             : static gdk_return
     559           0 : upgradeJSONStorage(char **out, const char **in)
     560             : {
     561           0 :         str msg;
     562           0 :         if ((msg = JSONstr2json(out, in)) != MAL_SUCCEED) {
     563           0 :                 freeException(msg);
     564           0 :                 return GDK_FAIL;
     565             :         }
     566             :         return GDK_SUCCEED;
     567             : }
     568             : 
     569             : static str
     570           0 : jsonRead(str a, size_t *dstlen, stream *s, size_t cnt)
     571             : {
     572           0 :         str out = NULL;
     573           0 :         str msg;
     574             : 
     575           0 :         if ((a = BATatoms[TYPE_str].atomRead(a, dstlen, s, cnt)) == NULL)
     576             :                 return NULL;
     577             : 
     578           0 :         if ((msg = JSONstr2json(&out, &(const char *){a})) != MAL_SUCCEED) {
     579           0 :                 freeException(msg);
     580           0 :                 GDKfree(a);
     581           0 :                 return NULL;
     582             :         }
     583           0 :         *dstlen = strlen(out) + 1;
     584           0 :         GDKfree(a);
     585             : 
     586           0 :         return out;
     587             : }
     588             : 
     589             : #endif
     590             : 
     591             : 
     592             : static str
     593         358 : JSONprelude(void)
     594             : {
     595         358 :         TYPE_json = ATOMindex("json");
     596             : #ifdef GDKLIBRARY_JSON
     597             : /* Run the gdk upgrade library function with a callback that
     598             :  * performs the actual upgrade.
     599             :  */
     600         358 :         char jsonupgrade[MAXPATH];
     601         358 :         struct stat st;
     602         358 :         if (GDKfilepath(jsonupgrade, sizeof(jsonupgrade), 0, BATDIR, "jsonupgradeneeded", NULL) != GDK_SUCCEED)
     603           0 :                 throw(MAL, "json.prelude", "cannot allocate filename for json upgrade signal file");
     604         358 :         int r = stat(jsonupgrade, &st);
     605         358 :         if (r == 0) {
     606             :                 /* The file exists so we need to run the upgrade code */
     607           0 :                 if (BBPjson_upgrade(upgradeJSONStorage) != GDK_SUCCEED) {
     608           0 :                         throw(MAL, "json.prelude", "JSON storage upgrade failed");
     609             :                 }
     610             :                 /* Change the read function of the json atom so that any values in the WAL
     611             :                  * will also be upgraded.
     612             :                  */
     613           0 :                 BATatoms[TYPE_json].atomRead = (void *(*)(void *, size_t *, stream *, size_t)) jsonRead;
     614             :         }
     615             : #endif
     616             :         return MAL_SUCCEED;
     617             : }
     618             : 
     619             : static void
     620    23271125 : JSONappend(JSON *jt, int idx, int nxt)
     621             : {
     622    23271125 :         int chld;
     623             : 
     624    23271125 :         if (jt->elm[nxt].kind == JSON_OBJECT || jt->elm[nxt].kind == JSON_ARRAY) {
     625           0 :                 chld = JSONnew(jt);
     626           0 :                 if (jt->error)
     627             :                         return;
     628           0 :                 jt->elm[chld].kind = jt->elm[nxt].kind;
     629           0 :                 jt->elm[chld].name = jt->elm[nxt].name;
     630           0 :                 jt->elm[chld].namelen = jt->elm[nxt].namelen;
     631           0 :                 jt->elm[chld].value = jt->elm[nxt].value;
     632           0 :                 jt->elm[chld].valuelen = jt->elm[nxt].valuelen;
     633           0 :                 jt->elm[chld].child = jt->elm[nxt].child;
     634           0 :                 jt->elm[chld].next = jt->elm[nxt].next;
     635           0 :                 jt->elm[chld].tail = jt->elm[nxt].tail;
     636           0 :                 jt->elm[chld].child = nxt;
     637             : 
     638           0 :                 jt->elm[nxt].child = 0;
     639           0 :                 jt->elm[nxt].next = 0;
     640           0 :                 jt->elm[nxt].tail = 0;
     641           0 :                 nxt = chld;
     642             :         }
     643    23271125 :         if (jt->elm[idx].next == 0)
     644     3901556 :                 jt->elm[idx].next = jt->elm[idx].tail = nxt;
     645             :         else {
     646    19369569 :                 jt->elm[jt->elm[idx].tail].next = nxt;
     647    19369569 :                 jt->elm[idx].tail = nxt;
     648             :         }
     649             : }
     650             : 
     651             : /*
     652             :  * The JSON filter operation takes a path expression which is
     653             :  * purposely kept simple, It provides step (.), multistep (..) and
     654             :  * indexed ([nr]) access to the JSON elements.  A wildcard * can be
     655             :  * used as placeholder for a step identifier.
     656             :  *
     657             :  * A path expression is always validated upfront and can only be
     658             :  * applied to valid json strings.
     659             :  * Path samples:
     660             :  * .store.book
     661             :  * .store.book[0]
     662             :  * .store.book.author
     663             :  * ..author
     664             :  */
     665             : #define MAXTERMS 256
     666             : 
     667             : typedef enum path_token {
     668             :         ROOT_STEP,                                      /* $ */
     669             :         CHILD_STEP,                                     /* . */
     670             :         INDEX_STEP,                                     /* [n] */
     671             :         ANY_STEP,                                       /* .. */
     672             :         END_STEP                                        /* end of expression */
     673             : } path_token;
     674             : 
     675             : typedef struct {
     676             :         path_token token;                       /* Token kind */
     677             :         char *name;                                     /* specific key */
     678             :         size_t namelen;                         /* the size of the key */
     679             :         int index;                                      /* if index == INT_MAX we got [*] */
     680             :         int first, last;
     681             : } pattern;
     682             : 
     683             : static str
     684         422 : JSONcompile(const char *expr, pattern terms[])
     685             : {
     686         422 :         int t = 0;
     687         422 :         const char *s, *beg;
     688             : 
     689         609 :         for (s = expr; *s; s++) {
     690         552 :                 terms[t].token = CHILD_STEP;
     691         552 :                 terms[t].index = INT_MAX;
     692         552 :                 terms[t].first = INT_MAX;
     693         552 :                 terms[t].last = INT_MAX;
     694         552 :                 if (*s == '$') {
     695          55 :                         if (t && terms[t - 1].token != END_STEP)
     696           0 :                                 throw(MAL, "json.compile", "Root node must be first");
     697          55 :                         if (!(*(s + 1) == '.' || *(s + 1) == '[' || *(s + 1) == 0))
     698           2 :                                 throw(MAL, "json.compile", "Root node must be first");
     699          53 :                         s++;
     700          53 :                         if (*s == 0)
     701           1 :                                 terms[t].token = ROOT_STEP;
     702             :                 }
     703         550 :                 if (*s == '.' && *(s + 1) == '.') {
     704         123 :                         terms[t].token = ANY_STEP;
     705         123 :                         s += 2;
     706         123 :                         if (*s == '.')
     707           2 :                                 throw(MAL, "json.compile", "Step identifier expected");
     708         427 :                 } else if (*s == '.')
     709         137 :                         s++;
     710             : 
     711             :                 // child step
     712         548 :                 if (*s != '[') {
     713        2880 :                         for (beg = s; *s; s++)
     714        2523 :                                 if (*s == '.' || *s == '[' || *s == ',')
     715             :                                         break;
     716         500 :                         terms[t].name = GDKzalloc(s - beg + 1);
     717         497 :                         if (terms[t].name == NULL)
     718           0 :                                 throw(MAL, "json.compile", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     719         497 :                         terms[t].namelen = s - beg;
     720         497 :                         strncpy(terms[t].name, beg, s - beg);
     721         497 :                         if (*s == '.')
     722          68 :                                 s--;
     723         497 :                         if (*s == 0) {
     724         353 :                                 t++;
     725         353 :                                 break;
     726             :                         }
     727             :                 }
     728         192 :                 if (*s == '[') {
     729             :                         // array step
     730          82 :                         bool closed = false;
     731          82 :                         s++;
     732          82 :                         skipblancs(s);
     733          82 :                         if (*s != '*') {
     734             :                                 /* TODO: Implement [start:step:end] indexing */
     735          75 :                                 if (isdigit((unsigned char) *s)) {
     736          71 :                                         terms[t].index = atoi(s);
     737          71 :                                         terms[t].first = terms[t].last = atoi(s);
     738             :                                 } else
     739           4 :                                         throw(MAL, "json.path", "'*' or digit expected");
     740             :                         }
     741         156 :                         for (; *s; s++)
     742         155 :                                 if (*s == ']') {
     743             :                                         closed = true;
     744             :                                         break;
     745             :                                 }
     746          78 :                         if (*s == 0) {
     747           1 :                                 if (!closed) {
     748           1 :                                         throw(MAL, "json.path", "] expected");
     749             :                                 }
     750           0 :                                 t++;
     751           0 :                                 break;
     752             :                         }
     753             :                         if (*s != ']')
     754             :                                 throw(MAL, "json.path", "] expected");
     755             :                 }
     756         187 :                 if (*s == ',') {
     757          42 :                         if (++t == MAXTERMS)
     758           0 :                                 throw(MAL, "json.path", "too many terms");
     759          42 :                         terms[t].token = END_STEP;
     760             :                 }
     761         187 :                 if (++t == MAXTERMS)
     762           0 :                         throw(MAL, "json.path", "too many terms");
     763             :         }
     764         410 :         if (t >= MAXTERMS - 1)
     765           0 :                 throw(MAL, "json.path", "too many terms");
     766         410 :         terms[t].token = END_STEP;
     767         410 :         return MAL_SUCCEED;
     768             : }
     769             : 
     770             : static str
     771         516 : JSONgetValue(const JSON *jt, int idx)
     772             : {
     773         516 :         str s;
     774             : 
     775         516 :         if (jt->elm[idx].valuelen == 0)
     776           0 :                 return GDKstrdup(str_nil);
     777         516 :         s = GDKmalloc(jt->elm[idx].valuelen + 1);
     778         516 :         if (s)
     779         516 :                 strcpy_len(s, jt->elm[idx].value, jt->elm[idx].valuelen + 1);
     780             :         return s;
     781             : }
     782             : 
     783             : /* eats res and r */
     784             : static str
     785        2286 : JSONglue(str res, str r, char sep)
     786             : {
     787        2286 :         size_t len, l;
     788        2286 :         str n;
     789             : 
     790        2286 :         if (r == 0 || *r == 0) {
     791           0 :                 GDKfree(r);
     792        1890 :                 return res;
     793             :         }
     794         396 :         len = strlen(r);
     795         396 :         if (res == 0)
     796             :                 return r;
     797         128 :         l = strlen(res);
     798         128 :         n = GDKmalloc(l + len + 3);
     799         128 :         if (n == NULL) {
     800           0 :                 GDKfree(res);
     801           0 :                 GDKfree(r);
     802           0 :                 return NULL;
     803             :         }
     804         128 :         snprintf(n, l + len + 3, "%s%s%s", res, sep ? "," : "", r);
     805         128 :         GDKfree(res);
     806         128 :         GDKfree(r);
     807         128 :         return n;
     808             : }
     809             : 
     810             : /* return NULL on no match, return (str) -1 on (malloc) failure, (str) -2 on stack overflow */
     811             : static str
     812        2281 : JSONmatch(JSON *jt, int ji, const pattern *terms, int ti, bool accumulate)
     813             : {
     814        2281 :         str r = NULL, res = NULL;
     815        2281 :         int i;
     816        2281 :         int cnt;
     817             : 
     818        2281 :         if (THRhighwater())
     819             :                 return (str) -2;
     820        2283 :         if (ti >= MAXTERMS)
     821             :                 return res;
     822             : 
     823        2283 :         if (terms[ti].token == ROOT_STEP) {
     824           1 :                 if (ti + 1 == MAXTERMS)
     825             :                         return NULL;
     826           1 :                 if (terms[ti + 1].token == END_STEP) {
     827           1 :                         res = JSONgetValue(jt, 0);
     828           1 :                         if (res == NULL)
     829           0 :                                 res = (str) -1;
     830           1 :                         return res;
     831             :                 }
     832           0 :                 ti++;
     833             :         }
     834             : 
     835        2282 :         switch (jt->elm[ji].kind) {
     836         149 :         case JSON_ARRAY:
     837         149 :                 if (terms[ti].name != 0 && terms[ti].token != ANY_STEP) {
     838           6 :                         if (terms[ti].token == END_STEP) {
     839           0 :                                 res = JSONgetValue(jt, ji);
     840           0 :                                 if (res == NULL)
     841           0 :                                         res = (str) -1;
     842             :                         }
     843           6 :                         return res;
     844             :                 }
     845         143 :                 cnt = 0;
     846         554 :                 for (i = jt->elm[ji].next; i && cnt >= 0; i = jt->elm[i].next, cnt++) {
     847         411 :                         if (terms[ti].index == INT_MAX
     848         131 :                                 || (cnt >= terms[ti].first && cnt <= terms[ti].last)) {
     849         321 :                                 if (terms[ti].token == ANY_STEP) {
     850         276 :                                         if (jt->elm[i].child)
     851          55 :                                                 r = JSONmatch(jt, jt->elm[i].child, terms, ti, true);
     852             :                                         else
     853             :                                                 r = 0;
     854          45 :                                 } else if (ti + 1 == MAXTERMS) {
     855             :                                         return NULL;
     856          45 :                                 } else if (terms[ti + 1].token == END_STEP) {
     857          24 :                                         if (jt->elm[i].kind == JSON_VALUE)
     858           8 :                                                 r = JSONgetValue(jt, jt->elm[i].child);
     859             :                                         else
     860          16 :                                                 r = JSONgetValue(jt, i);
     861          24 :                                         if (r == NULL)
     862           0 :                                                 r = (str) -1;
     863             :                                 } else {
     864          21 :                                         r = JSONmatch(jt, jt->elm[i].child, terms, ti + 1, terms[ti].index == INT_MAX);
     865             :                                 }
     866         321 :                                 if (r == (str) -1 || r == (str) -2) {
     867           0 :                                         GDKfree(res);
     868           0 :                                         return r;
     869             :                                 }
     870         321 :                                 res = JSONglue(res, r, ',');
     871             :                         }
     872             :                 }
     873             :                 break;
     874         629 :         case JSON_OBJECT:
     875         629 :                 cnt = 0;
     876        6509 :                 for (i = jt->elm[ji].next; i && cnt >= 0; i = jt->elm[i].next) {
     877             :                         // check the element label
     878        5880 :                         if ((terms[ti].name && jt->elm[i].valuelen == terms[ti].namelen
     879        2096 :                                  && strncmp(terms[ti].name, jt->elm[i].value,
     880        5418 :                                                         terms[ti].namelen) == 0) || terms[ti].name == 0
     881        5416 :                                 || terms[ti].name[0] == '*') {
     882         471 :                                 if (terms[ti].index == INT_MAX
     883          24 :                                         || (cnt >= terms[ti].first && cnt <= terms[ti].last)) {
     884         463 :                                         if (ti + 1 == MAXTERMS)
     885             :                                                 return NULL;
     886         463 :                                         if (terms[ti + 1].token == END_STEP) {
     887         393 :                                                 r = JSONgetValue(jt, jt->elm[i].child);
     888         394 :                                                 if (r == NULL)
     889           0 :                                                         r = (str) -1;
     890             :                                         } else {
     891          70 :                                                 r = JSONmatch(jt, jt->elm[i].child, terms, ti + 1, terms[ti].index == INT_MAX);
     892             :                                         }
     893         462 :                                         if (r == (str) -1 || r == (str) -2) {
     894           0 :                                                 GDKfree(res);
     895           0 :                                                 return r;
     896             :                                         }
     897         462 :                                         if (accumulate) {
     898         180 :                                                 res = JSONglue(res, r, ',');
     899             :                                         } else {  // Keep the last matching value
     900         282 :                                                 if (res)
     901           0 :                                                         GDKfree(res);
     902             :                                                 res = r;
     903             :                                         }
     904             :                                 }
     905         471 :                                 cnt++;
     906        5409 :                         } else if (terms[ti].token == ANY_STEP && jt->elm[i].child) {
     907        1684 :                                 r = JSONmatch(jt, jt->elm[i].child, terms, ti, true);
     908        1684 :                                 if (r == (str) -1 || r == (str) -2) {
     909           0 :                                         GDKfree(res);
     910           0 :                                         return r;
     911             :                                 }
     912        1684 :                                 res = JSONglue(res, r, ',');
     913        1684 :                                 cnt++;
     914             :                         }
     915             :                 }
     916             :                 break;
     917             :         default:
     918             :                 res = NULL;
     919             :         }
     920             :         return res;
     921             : }
     922             : 
     923             : static str
     924         419 : JSONfilterInternal(json *ret, const json *js, const char *const *expr, const char *other)
     925             : {
     926         419 :         pattern terms[MAXTERMS];
     927         419 :         int tidx = 0;
     928         419 :         JSON *jt;
     929         419 :         str j = *js, msg = MAL_SUCCEED, s;
     930         419 :         json result = 0;
     931         419 :         size_t l;
     932             : 
     933         419 :         (void) other;
     934         419 :         if (strNil(j)) {
     935           0 :                 *ret = GDKstrdup(j);
     936           0 :                 if (*ret == NULL)
     937           0 :                         throw(MAL, "JSONfilterInternal", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     938             :                 return MAL_SUCCEED;
     939             :         }
     940         419 :         jt = JSONparse(j);
     941         420 :         CHECK_JSON(jt);
     942         420 :         memset(terms, 0, sizeof(terms));
     943         420 :         msg = JSONcompile(*expr, terms);
     944         419 :         if (msg)
     945           9 :                 goto bailout;
     946             : 
     947         410 :         bool accumulate = terms[tidx].token == ANY_STEP || (terms[tidx].name && terms[tidx].name[0] == '*');
     948         410 :         s = JSONmatch(jt, 0, terms, tidx, accumulate);
     949         412 :         if (s == (char *) -1) {
     950           0 :                 msg = createException(MAL, "JSONfilterInternal",
     951             :                                                           SQLSTATE(HY013) MAL_MALLOC_FAIL);
     952           0 :                 goto bailout;
     953             :         }
     954         412 :         if (s == (char *) -2) {
     955           0 :                 msg = createException(MAL, "JSONfilterInternal",
     956             :                                                           SQLSTATE(42000)
     957             :                                                           "Expression too complex to parse");
     958           0 :                 goto bailout;
     959             :         }
     960             :         result = s;
     961             :         bool return_array = false;
     962             :         // process all other PATH expression
     963         950 :         for (tidx++; tidx < MAXTERMS && terms[tidx].token; tidx++)
     964         540 :                 if (terms[tidx].token == END_STEP && tidx + 1 < MAXTERMS
     965         455 :                         && terms[tidx + 1].token) {
     966          42 :                         tidx += 1;
     967          42 :                         accumulate = terms[tidx].token == ANY_STEP || (terms[tidx].name && terms[tidx].name[0] == '*');
     968          42 :                         s = JSONmatch(jt, 0, terms, tidx, accumulate);
     969          42 :                         if (s == (char *) -1) {
     970           0 :                                 msg = createException(MAL, "JSONfilterInternal",
     971             :                                                                           SQLSTATE(HY013) MAL_MALLOC_FAIL);
     972           0 :                                 goto bailout;
     973             :                         }
     974          42 :                         if (s == (char *) -2) {
     975           0 :                                 msg = createException(MAL, "JSONfilterInternal",
     976             :                                                                           SQLSTATE(42000)
     977             :                                                                           "Expression too complex to parse");
     978           0 :                                 goto bailout;
     979             :                         }
     980          42 :                         return_array = true;
     981          42 :                         result = JSONglue(result, s, ',');
     982             :                 }
     983         410 :         if (result) {
     984         338 :                 l = strlen(result);
     985         338 :                 if (result[l - 1] == ',')
     986           0 :                         result[l - 1] = 0;
     987             :         } else
     988             :                 l = 3;
     989             : 
     990        1141 :         for (int i = 0; i < MAXTERMS && terms[i].token; i++) {
     991             :                 // pattern contains the .. operator
     992         825 :                 if (terms[i].token == ANY_STEP ||
     993             :                         // pattern contains the [*] operator
     994         736 :                         (terms[i].token == CHILD_STEP && terms[i].index == INT_MAX && terms[i].name == NULL) ||
     995         407 :                         (terms[i].token == CHILD_STEP && terms[i].index == INT_MAX && *terms[i].name == '*')) {
     996             : 
     997             :                         return_array = true;
     998             :                         break;
     999             :                 }
    1000             :         }
    1001         410 :         if (return_array || accumulate) {
    1002         105 :                 s = GDKmalloc(l + 3);
    1003         105 :                 if (s)
    1004         176 :                         snprintf(s, l + 3, "[%s]", (result ? result : ""));
    1005             :         }
    1006         305 :         else if (result == NULL || *result == 0) {
    1007          38 :                 s = GDKstrdup("[]");
    1008             :         }
    1009             :         else {
    1010         267 :                 s = GDKmalloc(l + 1);
    1011         268 :                 if (s)
    1012         268 :                         snprintf(s, l + 1, "%s", (result ? result : ""));
    1013             :         }
    1014         413 :         if (s == NULL) {
    1015           0 :                 msg = createException(MAL, "JSONfilterInternal", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1016           0 :                 goto bailout;
    1017             :         }
    1018         413 :         *ret = s;
    1019             : 
    1020         413 :   bailout:
    1021         422 :         GDKfree(result);
    1022      108320 :         for (l = 0; l < MAXTERMS; l++)
    1023      107477 :                 if (terms[l].name)
    1024         496 :                         GDKfree(terms[l].name);
    1025         421 :         JSONfree(jt);
    1026         421 :         return msg;
    1027             : }
    1028             : 
    1029             : 
    1030             : static str
    1031    31157157 : JSONstringParser(const char *j, const char **next)
    1032             : {
    1033    31157157 :         unsigned int u;
    1034    31157157 :         bool seensurrogate = false;
    1035             : 
    1036    31157157 :         assert(*j == '"');
    1037    31157157 :         j++;
    1038   473720797 :         for (; *j; j++) {
    1039   473720788 :                 switch (*j) {
    1040    16757433 :                 case '\\':
    1041             :                         // parse all escapes
    1042    16757433 :                         j++;
    1043    16757433 :                         switch (*j) {
    1044     9849188 :                         case '"':
    1045             :                         case '\\':
    1046             :                         case '/':
    1047             :                         case 'b':
    1048             :                         case 'f':
    1049             :                         case 'n':
    1050             :                         case 'r':
    1051             :                         case 't':
    1052     9849188 :                                 if (seensurrogate)
    1053           2 :                                         throw(MAL, "json.parser", "illegal escape char");
    1054     9849186 :                                 continue;
    1055             :                         case 'u':
    1056             :                                 u = 0;
    1057    34541164 :                                 for (int i = 0; i < 4; i++) {
    1058    27632934 :                                         u <<= 4;
    1059    27632934 :                                         j++;
    1060    27632934 :                                         if ('0' <= *j && *j <= '9')
    1061    22675810 :                                                 u |= *j - '0';
    1062     4957124 :                                         else if ('a' <= *j && *j <= 'f')
    1063     4957030 :                                                 u |= *j - 'a' + 10;
    1064          94 :                                         else if ('A' <= *j && *j <= 'F')
    1065          87 :                                                 u |= *j - 'A' + 10;
    1066             :                                         else
    1067           7 :                                                 throw(MAL, "json.parser", "illegal escape char");
    1068             :                                 }
    1069     6908230 :                                 if (seensurrogate) {
    1070       96220 :                                         if ((u & 0xFC00) == 0xDC00)
    1071             :                                                 seensurrogate = false;
    1072             :                                         else
    1073           3 :                                                 throw(MAL, "json.parser", "illegal escape char");
    1074             :                                 } else {
    1075     6812010 :                                         if ((u & 0xFC00) == 0xD800)
    1076             :                                                 seensurrogate = true;
    1077     6715781 :                                         else if ((u & 0xFC00) == 0xDC00)
    1078           4 :                                                 throw(MAL, "json.parser", "illegal escape char");
    1079             :                                 }
    1080             :                                 break;
    1081           8 :                         default:
    1082           8 :                                 *next = j;
    1083           8 :                                 throw(MAL, "json.parser", "illegal escape char");
    1084             :                         }
    1085             :                         break;
    1086    31157120 :                 case '"':
    1087    31157120 :                         if (seensurrogate)
    1088           2 :                                 throw(MAL, "json.parser", "illegal escape char");
    1089    31157118 :                         j++;
    1090    31157118 :                         *next = j;
    1091    31157118 :                         return MAL_SUCCEED;
    1092   425806235 :                 default:
    1093   425806235 :                         if ((unsigned char) *j < ' ')
    1094           3 :                                 throw(MAL, "json.parser", "illegal control char");
    1095   425806232 :                         if (seensurrogate)
    1096           1 :                                 throw(MAL, "json.parser", "illegal escape char");
    1097             :                         break;
    1098             :                 }
    1099             :         }
    1100           9 :         *next = j;
    1101           9 :         throw(MAL, "json.parser", "Nonterminated string");
    1102             : }
    1103             : 
    1104             : static bool
    1105     5613952 : JSONintegerParser(const char *j, const char **next)
    1106             : {
    1107     5613952 :         if (*j == '-')
    1108      116377 :                 j++;
    1109             : 
    1110             :         // skipblancs(j);
    1111     5613952 :         if (!isdigit((unsigned char) *j)) {
    1112           6 :                 *next = j;
    1113           6 :                 return false;
    1114             :         }
    1115             : 
    1116     5613946 :         if (*j == '0') {
    1117      544271 :                 *next = ++j;
    1118      544271 :                 return true;
    1119             :         }
    1120             : 
    1121    33918384 :         for (; *j; j++)
    1122    33918278 :                 if (!(isdigit((unsigned char) *j)))
    1123             :                         break;
    1124     5069675 :         *next = j;
    1125             : 
    1126     5069675 :         return true;
    1127             : }
    1128             : 
    1129             : static bool
    1130     5613947 : JSONfractionParser(const char *j, const char **next)
    1131             : {
    1132     5613947 :         if (*j != '.')
    1133             :                 return false;
    1134             : 
    1135             :         // skip the period character
    1136       82787 :         j++;
    1137             :         // must be followed by more digits
    1138       82787 :         if (!isdigit((unsigned char) *j))
    1139             :                 return false;
    1140      633879 :         for (; *j; j++)
    1141      633864 :                 if (!isdigit((unsigned char) *j))
    1142             :                         break;
    1143       82780 :         *next = j;
    1144             : 
    1145       82780 :         return true;
    1146             : }
    1147             : 
    1148             : static bool
    1149     5613947 : JSONexponentParser(const char *j, const char **next)
    1150             : {
    1151     5613947 :         const char *s = j;
    1152     5613947 :         bool saw_digit = false;
    1153             : 
    1154     5613947 :         if (*j != 'e' && *j != 'E') {
    1155             :                 return false;
    1156             :         }
    1157             : 
    1158          45 :         j++;
    1159          45 :         if (*j == '-' || *j == '+')
    1160          20 :                 j++;
    1161             : 
    1162         248 :         for (; *j; j++) {
    1163         245 :                 if (!isdigit((unsigned char) *j))
    1164             :                         break;
    1165         203 :                 saw_digit = true;
    1166             :         }
    1167             : 
    1168             : 
    1169          45 :         if (!saw_digit) {
    1170     5613947 :                 j = s;
    1171             :                 return false;
    1172             :         }
    1173             : 
    1174             : 
    1175          32 :         *next = j;
    1176             : 
    1177          32 :         return true;
    1178             : }
    1179             : 
    1180             : static str
    1181     5613951 : JSONnumberParser(const char *j, const char **next)
    1182             : {
    1183     5613951 :         if (!JSONintegerParser(j, next)) {
    1184           6 :                 throw(MAL, "json.parser", "Number expected");
    1185             :         }
    1186             : 
    1187     5613946 :         j = *next;
    1188             :         // backup = j;
    1189             :         // skipblancs(j);
    1190             : 
    1191     5613946 :         if (!JSONfractionParser(j, next)) {
    1192     5531166 :                 *next = j;
    1193             :         }
    1194             : 
    1195     5613945 :         j = *next;
    1196             : 
    1197     5613945 :         if (!JSONexponentParser(j, next)) {
    1198     5613915 :                 *next = j;
    1199             :         }
    1200             :         return MAL_SUCCEED;
    1201             : }
    1202             : 
    1203             : static int
    1204    47721749 : JSONtoken(JSON *jt, const char *j, const char **next)
    1205             : {
    1206    47721749 :         str msg;
    1207    47721749 :         int nxt, idx = JSONnew(jt);
    1208    47721763 :         const char *string_start = j;
    1209    47721763 :         int pidx;
    1210             : 
    1211    47721763 :         if (jt->error)
    1212             :                 return idx;
    1213    47721765 :         if (THRhighwater()) {
    1214           2 :                 jt->error = createException(MAL, "json.parser",
    1215             :                                                                         SQLSTATE(42000)
    1216             :                                                                         "Expression too complex to parse");
    1217           2 :                 return idx;
    1218             :         }
    1219    47721853 :         skipblancs(j);
    1220    47721853 :         switch (*j) {
    1221     3314278 :         case '{':
    1222     3314278 :                 jt->elm[idx].kind = JSON_OBJECT;
    1223     3314278 :                 jt->elm[idx].value = j;
    1224     3314278 :                 j++;
    1225    22245025 :                 while (*j) {
    1226    22257346 :                         skipblancs(j);
    1227    22245019 :                         if (*j == '}')
    1228             :                                 break;
    1229    22237846 :                         nxt = JSONtoken(jt, j, next);
    1230    22237862 :                         if (jt->error)
    1231             :                                 return idx;
    1232    22237841 :                         j = *next;
    1233    22237852 :                         skipblancs(j);
    1234    22237841 :                         if (jt->elm[nxt].kind != JSON_STRING || *j != ':') {
    1235          22 :                                 jt->error = createException(MAL, "json.parser",
    1236             :                                                                                         "JSON syntax error: element expected at offset %td",
    1237          11 :                                                                                         jt->elm[nxt].value - string_start);
    1238          11 :                                 return idx;
    1239             :                         }
    1240    22237830 :                         j++;
    1241    22239713 :                         skipblancs(j);
    1242    22237830 :                         jt->elm[nxt].kind = JSON_ELEMENT;
    1243             :                         /* do in two steps since JSONtoken may realloc jt->elm */
    1244    22237830 :                         int chld = JSONtoken(jt, j, next);
    1245    22237815 :                         if (jt->error)
    1246             :                                 return idx;
    1247             : 
    1248             :                         /* Search for a duplicate key */
    1249   263019433 :                         for(pidx = jt->elm[idx].next; pidx != 0; pidx = jt->elm[pidx].next) {
    1250   240789506 :                                 if (jt->elm[pidx].kind == JSON_ELEMENT &&
    1251   240789451 :                                         jt->elm[pidx].valuelen == jt->elm[nxt].valuelen - 2 &&
    1252     7094795 :                                         strncmp(jt->elm[pidx].value, jt->elm[nxt].value + 1,
    1253             :                                                         jt->elm[nxt].valuelen) == 0) {
    1254             :                                         break;
    1255             :                                 }
    1256             :                         }
    1257             : 
    1258             :                         /* Duplicate found: Change the value of the previous key. */
    1259    22229943 :                         if (pidx > 0) {
    1260          16 :                                 jt->elm[pidx].child = chld;
    1261             :                                 /* Note that we do not call JSONappend here.
    1262             :                                  *
    1263             :                                  * Normally we would de-allocate the old child value and the new key,
    1264             :                                  * but since we are using an arena provided by JSONnew, we don't need to.
    1265             :                                  * This might get expensive for big objects with lagre values for
    1266             :                                  * repeated keys.
    1267             :                                  */
    1268             : 
    1269             :                         } else {
    1270    22229927 :                                 jt->elm[nxt].child = chld;
    1271    22229927 :                                 jt->elm[nxt].value++;
    1272    22229927 :                                 jt->elm[nxt].valuelen -= 2;
    1273    22229927 :                                 JSONappend(jt, idx, nxt);
    1274    22229928 :                                 if (jt->error)
    1275             :                                         return idx;
    1276             :                         }
    1277    22229944 :                         j = *next;
    1278    22240280 :                         skipblancs(j);
    1279    22229944 :                         if (*j == '}')
    1280             :                                 break;
    1281    18930757 :                         if (*j != ',') {
    1282           8 :                                 jt->error = createException(MAL, "json.parser",
    1283             :                                                                                         "JSON syntax error: ',' or '}' expected at offset %td",
    1284             :                                                                                         j - string_start);
    1285           8 :                                 return idx;
    1286             :                         }
    1287    18930749 :                         j++;
    1288    18939431 :                         skipblancs(j);
    1289    18930749 :                         if (*j == '}') {
    1290           2 :                                 jt->error = createException(MAL, "json.parser",
    1291             :                                                                                         "JSON syntax error: '}' not expected at offset %td",
    1292             :                                                                                         j - string_start);
    1293           2 :                                 return idx;
    1294             :                         }
    1295             :                 }
    1296     3306366 :                 if (*j != '}') {
    1297           6 :                         jt->error = createException(MAL, "json.parser",
    1298             :                                                                                 "JSON syntax error: '}' expected at offset %td",
    1299             :                                                                                 j - string_start);
    1300           6 :                         return idx;
    1301             :                 } else
    1302     3306360 :                         j++;
    1303     3306360 :                 *next = j;
    1304     3306360 :                 jt->elm[idx].valuelen = *next - jt->elm[idx].value;
    1305     3306360 :                 return idx;
    1306     1607000 :         case '[':
    1307     1607000 :                 jt->elm[idx].kind = JSON_ARRAY;
    1308     1607000 :                 jt->elm[idx].value = j;
    1309     1607000 :                 j++;
    1310     2045858 :                 while (*j) {
    1311     2045950 :                         skipblancs(j);
    1312     2045854 :                         if (*j == ']')
    1313             :                                 break;
    1314     1064878 :                         nxt = JSONtoken(jt, j, next);
    1315     1064878 :                         if (jt->error)
    1316             :                                 return idx;
    1317     1041206 :                         switch (jt->elm[nxt].kind) {
    1318           0 :                         case JSON_ELEMENT:{
    1319           0 :                                 int k = JSONnew(jt);
    1320           0 :                                 if (jt->error)
    1321             :                                         return idx;
    1322           0 :                                 jt->elm[k].kind = JSON_OBJECT;
    1323           0 :                                 jt->elm[k].child = nxt;
    1324           0 :                                 nxt = k;
    1325             :                         }
    1326             :                                 /* fall through */
    1327      343702 :                         case JSON_OBJECT:
    1328             :                         case JSON_ARRAY:
    1329      343702 :                                 if (jt->elm[nxt].kind == JSON_OBJECT
    1330      343702 :                                         || jt->elm[nxt].kind == JSON_ARRAY) {
    1331      343702 :                                         int k = JSONnew(jt);
    1332      343702 :                                         if (jt->error)
    1333             :                                                 return idx;
    1334      343702 :                                         JSONappend(jt, idx, k);
    1335      343702 :                                         if (jt->error)
    1336             :                                                 return idx;
    1337      343702 :                                         jt->elm[k].kind = JSON_VALUE;
    1338      343702 :                                         jt->elm[k].child = nxt;
    1339             :                                 }
    1340             :                                 break;
    1341      697504 :                         default:
    1342      697504 :                                 JSONappend(jt, idx, nxt);
    1343      697504 :                                 if (jt->error)
    1344             :                                         return idx;
    1345             :                         }
    1346     1041206 :                         j = *next;
    1347     1041253 :                         skipblancs(j);
    1348     1041206 :                         if (*j == ']')
    1349             :                                 break;
    1350      438909 :                         if (jt->elm[nxt].kind == JSON_ELEMENT) {
    1351           0 :                                 jt->error = createException(MAL, "json.parser",
    1352             :                                                                                         "JSON syntax error: Array value expected at offset %td",
    1353             :                                                                                         j - string_start);
    1354           0 :                                 return idx;
    1355             :                         }
    1356      438909 :                         if (*j != ',') {
    1357          98 :                                 jt->error = createException(MAL, "json.parser",
    1358             :                                                                                         "JSON syntax error: ',' or ']' expected at offset %td (context: %c%c%c)",
    1359          49 :                                                                                         j - string_start, *(j - 1), *j,
    1360          49 :                                                                                         *(j + 1));
    1361          49 :                                 return idx;
    1362             :                         }
    1363      438860 :                         j++;
    1364      439364 :                         skipblancs(j);
    1365      438860 :                         if (*j == ']') {
    1366           2 :                                 jt->error = createException(MAL, "json.parser",
    1367             :                                                                                         "JSON syntax error: '}' not expected at offset %td",
    1368             :                                                                                         j - string_start);
    1369           2 :                                 return idx;
    1370             :                         }
    1371             :                 }
    1372     1583277 :                 if (*j != ']') {
    1373           4 :                         jt->error = createException(MAL, "json.parser",
    1374             :                                                                                 "JSON syntax error: ']' expected at offset %td",
    1375             :                                                                                 j - string_start);
    1376             :                 } else
    1377     1583273 :                         j++;
    1378     1583277 :                 *next = j;
    1379     1583277 :                 jt->elm[idx].valuelen = *next - jt->elm[idx].value;
    1380     1583277 :                 return idx;
    1381    31157108 :         case '"':
    1382    31157108 :                 msg = JSONstringParser(j, next);
    1383    31157146 :                 if (msg) {
    1384          38 :                         jt->error = msg;
    1385          38 :                         return idx;
    1386             :                 }
    1387    31157108 :                 jt->elm[idx].kind = JSON_STRING;
    1388    31157108 :                 jt->elm[idx].value = j;
    1389    31157108 :                 jt->elm[idx].valuelen = *next - j;
    1390    31157108 :                 return idx;
    1391     2964610 :         case 'n':
    1392     2964610 :                 if (strncmp("null", j, 4) == 0) {
    1393     2964608 :                         *next = j + 4;
    1394     2964608 :                         jt->elm[idx].kind = JSON_NULL;
    1395     2964608 :                         jt->elm[idx].value = j;
    1396     2964608 :                         jt->elm[idx].valuelen = 4;
    1397     2964608 :                         return idx;
    1398             :                 }
    1399           2 :                 jt->error = createException(MAL, "json.parser",
    1400             :                                                                         "JSON syntax error: NULL expected at offset %td",
    1401             :                                                                         j - string_start);
    1402           2 :                 return idx;
    1403      508579 :         case 't':
    1404      508579 :                 if (strncmp("true", j, 4) == 0) {
    1405      508576 :                         *next = j + 4;
    1406      508576 :                         jt->elm[idx].kind = JSON_NUMBER;
    1407      508576 :                         jt->elm[idx].value = j;
    1408      508576 :                         jt->elm[idx].valuelen = 4;
    1409      508576 :                         return idx;
    1410             :                 }
    1411           3 :                 jt->error = createException(MAL, "json.parser",
    1412             :                                                                         "JSON syntax error: True expected at offset %td",
    1413             :                                                                         j - string_start);
    1414           3 :                 return idx;
    1415     2556250 :         case 'f':
    1416     2556250 :                 if (strncmp("false", j, 5) == 0) {
    1417     2556248 :                         *next = j + 5;
    1418     2556248 :                         jt->elm[idx].kind = JSON_NUMBER;
    1419     2556248 :                         jt->elm[idx].value = j;
    1420     2556248 :                         jt->elm[idx].valuelen = 5;
    1421     2556248 :                         return idx;
    1422             :                 }
    1423           2 :                 jt->error = createException(MAL, "json.parser",
    1424             :                                                                         "JSON syntax error: False expected at offset %td",
    1425             :                                                                         j - string_start);
    1426           2 :                 return idx;
    1427     5614028 :         default:
    1428     5614028 :                 if (*j == '-' || isdigit((unsigned char) *j)) {
    1429     5613952 :                         jt->elm[idx].value = j;
    1430     5613952 :                         msg = JSONnumberParser(j, next);
    1431     5613953 :                         if (msg)
    1432           6 :                                 jt->error = msg;
    1433     5613953 :                         jt->elm[idx].kind = JSON_NUMBER;
    1434     5613953 :                         jt->elm[idx].valuelen = *next - jt->elm[idx].value;
    1435     5613953 :                         return idx;
    1436             :                 }
    1437          76 :                 jt->error = createException(MAL, "json.parser",
    1438             :                                                                         "JSON syntax error: value expected at offset %td",
    1439             :                                                                         j - string_start);
    1440          76 :                 return idx;
    1441             :         }
    1442             : }
    1443             : 
    1444             : 
    1445             : JSON *
    1446     2181400 : JSONparse(const char *j)
    1447             : {
    1448     2181400 :         JSON *jt = JSONnewtree();
    1449             : 
    1450     2181404 :         if (jt == NULL)
    1451             :                 return NULL;
    1452     2181412 :         skipblancs(j);
    1453     2181404 :         JSONtoken(jt, j, &j);
    1454     2181402 :         if (jt->error)
    1455             :                 return jt;
    1456     2181226 :         skipblancs(j);
    1457     2181190 :         if (*j)
    1458          28 :                 jt->error = createException(MAL, "json.parser",
    1459             :                                                                         "JSON syntax error: json parse failed");
    1460             :         return jt;
    1461             : }
    1462             : 
    1463             : static str
    1464           9 : JSONlength(int *ret, const json *j)
    1465             : {
    1466           9 :         int i, cnt = 0;
    1467           9 :         JSON *jt;
    1468             : 
    1469           9 :         if (strNil(*j)) {
    1470           0 :                 *ret = int_nil;
    1471           0 :                 return MAL_SUCCEED;
    1472             :         }
    1473             : 
    1474           9 :         jt = JSONparse(*j);
    1475           9 :         CHECK_JSON(jt);
    1476          26 :         for (i = jt->elm[0].next; i; i = jt->elm[i].next)
    1477          17 :                 cnt++;
    1478           9 :         *ret = cnt;
    1479           9 :         JSONfree(jt);
    1480           9 :         return MAL_SUCCEED;
    1481             : }
    1482             : 
    1483             : static str
    1484          17 : JSONfilterArrayDefault(json *ret, const json *js, lng index, const char *other)
    1485             : {
    1486          17 :         char expr[BUFSIZ], *s = expr;
    1487             : 
    1488          17 :         if (index < 0)
    1489           0 :                 throw(MAL, "json.filter",
    1490             :                           SQLSTATE(42000) "Filter index cannot be negative");
    1491          17 :         snprintf(expr, BUFSIZ, "[" LLFMT "]", index);
    1492          17 :         return JSONfilterInternal(ret, js, &(const char *){s}, other);
    1493             : }
    1494             : 
    1495             : static str
    1496           4 : JSONfilterArray_bte(json *ret, const json *js, const bte *index)
    1497             : {
    1498           8 :         if (strNil(*js) || is_bte_nil(*index)) {
    1499           0 :                 if (!(*ret = GDKstrdup(str_nil)))
    1500           0 :                         throw(MAL, "json.filter", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1501             :                 return MAL_SUCCEED;
    1502             :         }
    1503           4 :         return JSONfilterArrayDefault(ret, js, (lng) *index, 0);
    1504             : }
    1505             : 
    1506             : static str
    1507           0 : JSONfilterArrayDefault_bte(json *ret, const json *js, const bte *index, const char *const *other)
    1508             : {
    1509           0 :         if (strNil(*js) || is_bte_nil(*index) || strNil(*other)) {
    1510           0 :                 if (!(*ret = GDKstrdup(str_nil)))
    1511           0 :                         throw(MAL, "json.filter", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1512             :                 return MAL_SUCCEED;
    1513             :         }
    1514           0 :         return JSONfilterArrayDefault(ret, js, (lng) *index, *other);
    1515             : }
    1516             : 
    1517             : static str
    1518           0 : JSONfilterArray_sht(json *ret, const json *js, const sht *index)
    1519             : {
    1520           0 :         if (strNil(*js) || is_sht_nil(*index)) {
    1521           0 :                 if (!(*ret = GDKstrdup(str_nil)))
    1522           0 :                         throw(MAL, "json.filter", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1523             :                 return MAL_SUCCEED;
    1524             :         }
    1525           0 :         return JSONfilterArrayDefault(ret, js, (lng) *index, 0);
    1526             : }
    1527             : 
    1528             : static str
    1529           0 : JSONfilterArrayDefault_sht(json *ret, const json *js, const sht *index, const char *const *other)
    1530             : {
    1531           0 :         if (strNil(*js) || is_sht_nil(*index) || strNil(*other)) {
    1532           0 :                 if (!(*ret = GDKstrdup(str_nil)))
    1533           0 :                         throw(MAL, "json.filter", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1534             :                 return MAL_SUCCEED;
    1535             :         }
    1536           0 :         return JSONfilterArrayDefault(ret, js, (lng) *index, *other);
    1537             : }
    1538             : 
    1539             : static str
    1540          13 : JSONfilterArray_int(json *ret, const json *js, const int *index)
    1541             : {
    1542          26 :         if (strNil(*js) || is_int_nil(*index)) {
    1543           0 :                 if (!(*ret = GDKstrdup(str_nil)))
    1544           0 :                         throw(MAL, "json.filter", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1545             :                 return MAL_SUCCEED;
    1546             :         }
    1547          13 :         return JSONfilterArrayDefault(ret, js, (lng) *index, 0);
    1548             : }
    1549             : 
    1550             : static str
    1551           0 : JSONfilterArrayDefault_int(json *ret, const json *js, const int *index, const char *const *other)
    1552             : {
    1553           0 :         if (strNil(*js) || is_int_nil(*index) || strNil(*other)) {
    1554           0 :                 if (!(*ret = GDKstrdup(str_nil)))
    1555           0 :                         throw(MAL, "json.filter", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1556             :                 return MAL_SUCCEED;
    1557             :         }
    1558           0 :         return JSONfilterArrayDefault(ret, js, (lng) *index, *other);
    1559             : }
    1560             : 
    1561             : static str
    1562           0 : JSONfilterArray_lng(json *ret, const json *js, const lng *index)
    1563             : {
    1564           0 :         if (strNil(*js) || is_lng_nil(*index)) {
    1565           0 :                 if (!(*ret = GDKstrdup(str_nil)))
    1566           0 :                         throw(MAL, "json.filter", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1567             :                 return MAL_SUCCEED;
    1568             :         }
    1569           0 :         return JSONfilterArrayDefault(ret, js, (lng) *index, 0);
    1570             : }
    1571             : 
    1572             : static str
    1573           0 : JSONfilterArrayDefault_lng(json *ret, const json *js, const lng *index, const char *const *other)
    1574             : {
    1575           0 :         if (strNil(*js) || is_lng_nil(*index) || strNil(*other)) {
    1576           0 :                 if (!(*ret = GDKstrdup(str_nil)))
    1577           0 :                         throw(MAL, "json.filter", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1578             :                 return MAL_SUCCEED;
    1579             :         }
    1580           0 :         return JSONfilterArrayDefault(ret, js, (lng) *index, *other);
    1581             : }
    1582             : 
    1583             : #ifdef HAVE_HGE
    1584             : static str
    1585           0 : JSONfilterArray_hge(json *ret, const json *js, const hge *index)
    1586             : {
    1587           0 :         if (strNil(*js) || is_hge_nil(*index)) {
    1588           0 :                 if (!(*ret = GDKstrdup(str_nil)))
    1589           0 :                         throw(MAL, "json.filter", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1590             :                 return MAL_SUCCEED;
    1591             :         }
    1592           0 :         if (*index < (hge) GDK_lng_min || *index > (hge) GDK_lng_max)
    1593           0 :                 throw(MAL, "json.filter", "index out of range");
    1594           0 :         return JSONfilterArrayDefault(ret, js, (lng) *index, 0);
    1595             : }
    1596             : 
    1597             : static str
    1598           0 : JSONfilterArrayDefault_hge(json *ret, const json *js, const hge *index, const char *const *other)
    1599             : {
    1600           0 :         if (strNil(*js) || is_hge_nil(*index) || strNil(*other)) {
    1601           0 :                 if (!(*ret = GDKstrdup(str_nil)))
    1602           0 :                         throw(MAL, "json.filter", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1603             :                 return MAL_SUCCEED;
    1604             :         }
    1605           0 :         if (*index < (hge) GDK_lng_min || *index > (hge) GDK_lng_max)
    1606           0 :                 throw(MAL, "json.filter", "index out of range");
    1607           0 :         return JSONfilterArrayDefault(ret, js, (lng) *index, *other);
    1608             : }
    1609             : #endif
    1610             : 
    1611             : static str
    1612         402 : JSONfilter(json *ret, const json *js, const char *const *expr)
    1613             : {
    1614         804 :         if (strNil(*js) || strNil(*expr)) {
    1615           0 :                 if (!(*ret = GDKstrdup(str_nil)))
    1616           0 :                         throw(MAL, "json.filter", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1617             :                 return MAL_SUCCEED;
    1618             :         }
    1619         402 :         return JSONfilterInternal(ret, js, expr, 0);
    1620             : }
    1621             : 
    1622             : // glue all values together with an optional separator
    1623             : // The json string should be valid
    1624             : 
    1625             : static str
    1626         235 : JSONplaintext(char **r, size_t *l, size_t *ilen, const JSON *jt, int idx, const char *sep,
    1627             :                           size_t sep_len)
    1628             : {
    1629         235 :         int i;
    1630         235 :         size_t j, next_len, next_concat_len;
    1631         235 :         unsigned int u;
    1632         235 :         str msg = MAL_SUCCEED;
    1633             : 
    1634         235 :         if (THRhighwater()) {
    1635           0 :                 *r = *r - (*ilen - *l);
    1636           0 :                 throw(MAL, "JSONplaintext",
    1637             :                           SQLSTATE(42000) "Expression too complex to parse");
    1638             :         }
    1639             : 
    1640         235 :         switch (jt->elm[idx].kind) {
    1641          30 :         case JSON_OBJECT:
    1642          86 :                 for (i = jt->elm[idx].next; i; i = jt->elm[i].next)
    1643          56 :                         if (jt->elm[i].child
    1644          56 :                                 && (msg = JSONplaintext(r, l, ilen, jt, jt->elm[i].child, sep,
    1645             :                                                                                 sep_len)))
    1646           0 :                                 return msg;
    1647             :                 break;
    1648          33 :         case JSON_ARRAY:
    1649         104 :                 for (i = jt->elm[idx].next; i; i = jt->elm[i].next)
    1650          71 :                         if ((msg = JSONplaintext(r, l, ilen, jt, i, sep, sep_len)))
    1651           0 :                                 return msg;
    1652             :                 break;
    1653           3 :         case JSON_ELEMENT:
    1654             :         case JSON_VALUE:
    1655           3 :                 if (jt->elm[idx].child
    1656           3 :                         && (msg = JSONplaintext(r, l, ilen, jt, jt->elm[idx].child, sep,
    1657             :                                                                         sep_len)))
    1658             :                         return msg;
    1659             :                 break;
    1660          93 :         case JSON_STRING:
    1661             :                 // Make sure there is enough space for the value plus the separator plus the NULL byte
    1662          93 :                 next_len = jt->elm[idx].valuelen;
    1663          93 :                 next_concat_len = next_len - 2 + sep_len + 1;
    1664          93 :                 if (*l < next_concat_len) {
    1665           1 :                         size_t prev_ilen = *ilen, prev_l = *l;
    1666           1 :                         char *p = *r - (prev_ilen - prev_l), *nr;
    1667             : 
    1668           1 :                         *ilen = (prev_ilen * 2) + next_concat_len;      /* make sure sep_len + 1 is always included */
    1669           1 :                         if (!(nr = GDKrealloc(p, *ilen))) {
    1670           0 :                                 *r = p;
    1671           0 :                                 throw(MAL, "JSONplaintext", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1672             :                         }
    1673           1 :                         *r = nr + (prev_ilen - prev_l);
    1674           1 :                         *l = *ilen - prev_ilen + prev_l;
    1675             :                 }
    1676          93 :                 assert(next_len >= 2);
    1677          93 :                 next_len--;
    1678         869 :                 for (j = 1; j < next_len; j++) {
    1679         776 :                         if (jt->elm[idx].value[j] == '\\') {
    1680           8 :                                 switch (jt->elm[idx].value[++j]) {
    1681           4 :                                 case '"':
    1682             :                                 case '\\':
    1683             :                                 case '/':
    1684           4 :                                         *(*r)++ = jt->elm[idx].value[j];
    1685           4 :                                         (*l)--;
    1686           4 :                                         break;
    1687           0 :                                 case 'b':
    1688           0 :                                         *(*r)++ = '\b';
    1689           0 :                                         (*l)--;
    1690           0 :                                         break;
    1691           0 :                                 case 'f':
    1692           0 :                                         *(*r)++ = '\f';
    1693           0 :                                         (*l)--;
    1694           0 :                                         break;
    1695           0 :                                 case 'r':
    1696           0 :                                         *(*r)++ = '\r';
    1697           0 :                                         (*l)--;
    1698           0 :                                         break;
    1699           0 :                                 case 'n':
    1700           0 :                                         *(*r)++ = '\n';
    1701           0 :                                         (*l)--;
    1702           0 :                                         break;
    1703           0 :                                 case 't':
    1704           0 :                                         *(*r)++ = '\t';
    1705           0 :                                         (*l)--;
    1706           0 :                                         break;
    1707             :                                 case 'u':
    1708             :                                         u = 0;
    1709          20 :                                         for (int i = 0; i < 4; i++) {
    1710          16 :                                                 char c = jt->elm[idx].value[++j];
    1711          16 :                                                 u <<= 4;
    1712          16 :                                                 if ('0' <= c && c <= '9')
    1713           9 :                                                         u |= c - '0';
    1714           7 :                                                 else if ('a' <= c && c <= 'f')
    1715           0 :                                                         u |= c - 'a' + 10;
    1716             :                                                 else    /* if ('A' <= c && c <= 'F') */
    1717           7 :                                                         u |= c - 'A' + 10;
    1718             :                                         }
    1719           4 :                                         if (u <= 0x7F) {
    1720           0 :                                                 *(*r)++ = (char) u;
    1721           0 :                                                 (*l)--;
    1722           4 :                                         } else if (u <= 0x7FF) {
    1723           1 :                                                 *(*r)++ = 0xC0 | (u >> 6);
    1724           1 :                                                 *(*r)++ = 0x80 | (u & 0x3F);
    1725           1 :                                                 (*l) -= 2;
    1726           3 :                                         } else if ((u & 0xFC00) == 0xD800) {
    1727             :                                                 /* high surrogate; must be followed by low surrogate */
    1728           1 :                                                 *(*r)++ = 0xF0 | (((u & 0x03C0) + 0x0040) >> 8);
    1729           1 :                                                 *(*r)++ = 0x80 | ((((u & 0x3FF) + 0x0040) >> 2) & 0x3F);
    1730           1 :                                                 **r = 0x80 | ((u & 0x0003) << 4);     /* no increment */
    1731           1 :                                                 (*l) -= 2;
    1732           2 :                                         } else if ((u & 0xFC00) == 0xDC00) {
    1733             :                                                 /* low surrogate; must follow high surrogate */
    1734           1 :                                                 *(*r)++ |= (u & 0x03C0) >> 6; /* amend last value */
    1735           1 :                                                 *(*r)++ = 0x80 | (u & 0x3F);
    1736           1 :                                                 (*l) -= 2;
    1737             :                                         } else {        /* if (u <= 0xFFFF) */
    1738           1 :                                                 *(*r)++ = 0xE0 | (u >> 12);
    1739           1 :                                                 *(*r)++ = 0x80 | ((u >> 6) & 0x3F);
    1740           1 :                                                 *(*r)++ = 0x80 | (u & 0x3F);
    1741           1 :                                                 (*l) -= 3;
    1742             :                                         }
    1743             :                                 }
    1744             :                         } else {
    1745         768 :                                 *(*r)++ = jt->elm[idx].value[j];
    1746         768 :                                 (*l)--;
    1747             :                         }
    1748             :                 }
    1749          93 :                 memcpy(*r, sep, sep_len);
    1750          93 :                 *l -= sep_len;
    1751          93 :                 *r += sep_len;
    1752          93 :                 break;
    1753          76 :         default:
    1754          76 :                 next_len = jt->elm[idx].valuelen;
    1755          76 :                 next_concat_len = next_len + sep_len + 1;
    1756          76 :                 if (*l < next_concat_len) {
    1757          21 :                         size_t prev_ilen = *ilen, prev_l = *l;
    1758          21 :                         char *p = *r - (prev_ilen - prev_l), *nr;
    1759             : 
    1760          21 :                         *ilen = (prev_ilen * 2) + next_concat_len;      /* make sure sep_len + 1 is always included */
    1761          21 :                         if (!(nr = GDKrealloc(p, *ilen))) {
    1762           0 :                                 *r = p;
    1763           0 :                                 throw(MAL, "JSONplaintext", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1764             :                         }
    1765          21 :                         *r = nr + (prev_ilen - prev_l);
    1766          21 :                         *l = *ilen - prev_ilen + prev_l;
    1767             :                 }
    1768          76 :                 memcpy(*r, jt->elm[idx].value, next_len);
    1769          76 :                 *l -= next_len;
    1770          76 :                 *r += next_len;
    1771          76 :                 memcpy(*r, sep, sep_len);
    1772          76 :                 *l -= sep_len;
    1773          76 :                 *r += sep_len;
    1774             :         }
    1775         235 :         assert(*l > 0);
    1776         235 :         **r = 0;
    1777         235 :         return MAL_SUCCEED;
    1778             : }
    1779             : 
    1780             : static str
    1781         107 : JSONjson2textSeparator(str *ret, const json *js, const char *const *sep)
    1782             : {
    1783         107 :         size_t l, ilen, sep_len;
    1784         107 :         str s, msg;
    1785         107 :         JSON *jt;
    1786             : 
    1787         213 :         if (strNil(*js) || strNil(*sep)) {
    1788           2 :                 if (!(*ret = GDKstrdup(str_nil)))
    1789           0 :                         throw(MAL, "json2txt", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1790             :                 return MAL_SUCCEED;
    1791             :         }
    1792             : 
    1793         105 :         jt = JSONparse(*js);
    1794         107 :         CHECK_JSON(jt);
    1795         107 :         sep_len = strlen(*sep);
    1796         107 :         ilen = l = strlen(*js) + 1;
    1797         107 :         if (!(s = GDKmalloc(l))) {
    1798           0 :                 JSONfree(jt);
    1799           0 :                 throw(MAL, "json2txt", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1800             :         }
    1801         106 :         msg = JSONplaintext(&s, &l, &ilen, jt, 0, *sep, sep_len);
    1802         105 :         JSONfree(jt);
    1803         106 :         if (msg) {
    1804           0 :                 GDKfree(s);
    1805           0 :                 return msg;
    1806             :         }
    1807         106 :         s -= ilen - l;
    1808         106 :         l = strlen(s);
    1809         106 :         if (l && sep_len)
    1810          91 :                 s[l - sep_len] = 0;
    1811         106 :         *ret = s;
    1812         106 :         return MAL_SUCCEED;
    1813             : }
    1814             : 
    1815             : static str
    1816          90 : JSONjson2text(str *ret, const json *js)
    1817             : {
    1818          90 :         const char *sep = " ";
    1819          90 :         return JSONjson2textSeparator(ret, js, &sep);
    1820             : }
    1821             : 
    1822             : static str
    1823         107 : JSONjson2numberInternal(void **ret, const json *js,
    1824             :                                                 void (*str2num)(void **ret, const char *nptr,
    1825             :                                                                                 size_t len))
    1826             : {
    1827         107 :         JSON *jt;
    1828             : 
    1829         107 :         jt = JSONparse(*js);
    1830         108 :         CHECK_JSON(jt);
    1831         108 :         switch (jt->elm[0].kind) {
    1832          90 :         case JSON_NUMBER:
    1833          90 :                 str2num(ret, jt->elm[0].value, jt->elm[0].valuelen);
    1834          90 :                 break;
    1835           8 :         case JSON_ARRAY:
    1836           8 :                 if (jt->free == 2) {
    1837           6 :                         str2num(ret, jt->elm[1].value, jt->elm[1].valuelen);
    1838             :                 } else {
    1839           2 :                         *ret = NULL;
    1840             :                 }
    1841             :                 break;
    1842           6 :         case JSON_OBJECT:
    1843           6 :                 if (jt->free == 3) {
    1844           4 :                         str2num(ret, jt->elm[2].value, jt->elm[2].valuelen);
    1845             :                 } else {
    1846           2 :                         *ret = NULL;
    1847             :                 }
    1848             :                 break;
    1849           4 :         default:
    1850           4 :                 *ret = NULL;
    1851             :         }
    1852         107 :         JSONfree(jt);
    1853             : 
    1854         107 :         return MAL_SUCCEED;
    1855             : }
    1856             : 
    1857             : static void
    1858          72 : strtod_wrapper(void **ret, const char *nptr, size_t len)
    1859             : {
    1860          72 :         char *rest;
    1861          72 :         dbl val;
    1862             : 
    1863          72 :         val = strtod(nptr, &rest);
    1864          74 :         if (rest && (size_t) (rest - nptr) != len) {
    1865           0 :                 *ret = NULL;
    1866             :         } else {
    1867          74 :                 **(dbl **) ret = val;
    1868             :         }
    1869          74 : }
    1870             : 
    1871             : static void
    1872          26 : strtol_wrapper(void **ret, const char *nptr, size_t len)
    1873             : {
    1874          26 :         char *rest;
    1875          26 :         lng val;
    1876             : 
    1877          26 :         val = strtol(nptr, &rest, 0);
    1878          26 :         if (rest && (size_t) (rest - nptr) != len) {
    1879           2 :                 *ret = NULL;
    1880             :         } else {
    1881          24 :                 **(lng **) ret = val;
    1882             :         }
    1883          26 : }
    1884             : 
    1885             : static str
    1886          78 : JSONjson2number(dbl *ret, const json *js)
    1887             : {
    1888          78 :         dbl val = 0;
    1889          78 :         dbl *val_ptr = &val;
    1890          78 :         str tmp;
    1891             : 
    1892          78 :         if (strNil(*js)) {
    1893           0 :                 *ret = dbl_nil;
    1894           0 :                 return MAL_SUCCEED;
    1895             :         }
    1896             : 
    1897          78 :         rethrow(__func__, tmp,
    1898             :                         JSONjson2numberInternal((void **) &val_ptr, js, strtod_wrapper));
    1899          78 :         if (val_ptr == NULL)
    1900           5 :                 *ret = dbl_nil;
    1901             :         else
    1902          73 :                 *ret = val;
    1903             : 
    1904             :         return MAL_SUCCEED;
    1905             : }
    1906             : 
    1907             : static str
    1908          29 : JSONjson2integer(lng *ret, const json *js)
    1909             : {
    1910          29 :         lng val = 0;
    1911          29 :         lng *val_ptr = &val;
    1912          29 :         str tmp;
    1913             : 
    1914          29 :         if (strNil(*js)) {
    1915           0 :                 *ret = lng_nil;
    1916           0 :                 return MAL_SUCCEED;
    1917             :         }
    1918             : 
    1919          29 :         rethrow(__func__, tmp,
    1920             :                         JSONjson2numberInternal((void **) &val_ptr, js, strtol_wrapper));
    1921          29 :         if (val_ptr == NULL)
    1922           5 :                 *ret = lng_nil;
    1923             :         else
    1924          24 :                 *ret = val;
    1925             : 
    1926             :         return MAL_SUCCEED;
    1927             : }
    1928             : 
    1929             : static str
    1930           8 : JSONunfoldContainer(JSON *jt, int idx, BAT *bo, BAT *bk, BAT *bv, oid *o)
    1931             : {
    1932           8 :         int i, last;
    1933           8 :         char *r;
    1934             : 
    1935           8 :         last = jt->elm[idx].tail;
    1936           8 :         if (jt->elm[idx].kind == JSON_OBJECT) {
    1937           8 :                 for (i = jt->elm[idx].next; i; i = jt->elm[i].next) {
    1938           8 :                         if ((r = JSONgetValue(jt, i)) == NULL)
    1939           0 :                                 goto memfail;
    1940           8 :                         if (BUNappend(bk, r, false) != GDK_SUCCEED) {
    1941           0 :                                 GDKfree(r);
    1942           0 :                                 goto memfail;
    1943             :                         }
    1944           8 :                         GDKfree(r);
    1945           8 :                         if ((r = JSONgetValue(jt, jt->elm[i].child)) == NULL)
    1946           0 :                                 goto memfail;
    1947           8 :                         if (BUNappend(bv, r, false) != GDK_SUCCEED) {
    1948           0 :                                 GDKfree(r);
    1949           0 :                                 goto memfail;
    1950             :                         }
    1951           8 :                         GDKfree(r);
    1952           8 :                         if (bo) {
    1953           4 :                                 if (BUNappend(bo, o, false) != GDK_SUCCEED)
    1954           0 :                                         goto memfail;
    1955             :                         }
    1956           8 :                         (*o)++;
    1957           8 :                         if (i == last)
    1958             :                                 break;
    1959             :                 }
    1960           6 :         } else if (jt->elm[idx].kind == JSON_ARRAY) {
    1961          26 :                 for (i = jt->elm[idx].next; i; i = jt->elm[i].next) {
    1962          26 :                         if (BUNappend(bk, str_nil, false) != GDK_SUCCEED)
    1963           0 :                                 goto memfail;
    1964          26 :                         if (jt->elm[i].kind == JSON_VALUE)
    1965          22 :                                 r = JSONgetValue(jt, jt->elm[i].child);
    1966             :                         else
    1967           4 :                                 r = JSONgetValue(jt, i);
    1968          26 :                         if (r == NULL)
    1969           0 :                                 goto memfail;
    1970          26 :                         if (BUNappend(bv, r, false) != GDK_SUCCEED) {
    1971           0 :                                 GDKfree(r);
    1972           0 :                                 goto memfail;
    1973             :                         }
    1974          26 :                         GDKfree(r);
    1975          26 :                         if (bo) {
    1976          13 :                                 if (BUNappend(bo, o, false) != GDK_SUCCEED)
    1977           0 :                                         goto memfail;
    1978             :                         }
    1979          26 :                         (*o)++;
    1980          26 :                         if (i == last)
    1981             :                                 break;
    1982             :                 }
    1983             :         }
    1984             :         return MAL_SUCCEED;
    1985             : 
    1986           0 :   memfail:
    1987           0 :         throw(MAL, "json.unfold", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1988             : }
    1989             : 
    1990             : static str
    1991           8 : JSONunfoldInternal(bat *od, bat *key, bat *val, const json *js)
    1992             : {
    1993           8 :         BAT *bo = NULL, *bk, *bv;
    1994           8 :         oid o = 0;
    1995           8 :         str msg = MAL_SUCCEED;
    1996             : 
    1997           8 :         JSON *jt = JSONparse(*js);
    1998             : 
    1999           8 :         CHECK_JSON(jt);
    2000           8 :         bk = COLnew(0, TYPE_str, 64, TRANSIENT);
    2001           8 :         if (bk == NULL) {
    2002           0 :                 JSONfree(jt);
    2003           0 :                 throw(MAL, "json.unfold", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    2004             :         }
    2005             : 
    2006           8 :         if (od) {
    2007           4 :                 bo = COLnew(0, TYPE_oid, 64, TRANSIENT);
    2008           4 :                 if (bo == NULL) {
    2009           0 :                         BBPreclaim(bk);
    2010           0 :                         JSONfree(jt);
    2011           0 :                         throw(MAL, "json.unfold", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    2012             :                 }
    2013             :         }
    2014             : 
    2015           8 :         bv = COLnew(0, TYPE_json, 64, TRANSIENT);
    2016           8 :         if (bv == NULL) {
    2017           0 :                 JSONfree(jt);
    2018           0 :                 BBPreclaim(bo);
    2019           0 :                 BBPreclaim(bk);
    2020           0 :                 throw(MAL, "json.unfold", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    2021             :         }
    2022             : 
    2023           8 :         if (jt->elm[0].kind == JSON_ARRAY || jt->elm[0].kind == JSON_OBJECT)
    2024          12 :                 msg = JSONunfoldContainer(jt, 0, (od ? bo : 0), bk, bv, &o);
    2025             :         else
    2026           0 :                 msg = createException(MAL, "json.unfold",
    2027             :                                                           "JSON object or array expected");
    2028           8 :         JSONfree(jt);
    2029           8 :         if (msg) {
    2030           0 :                 BBPreclaim(bk);
    2031           0 :                 BBPreclaim(bo);
    2032           0 :                 BBPreclaim(bv);
    2033             :         } else {
    2034           8 :                 *key = bk->batCacheid;
    2035           8 :                 BBPkeepref(bk);
    2036           8 :                 *val = bv->batCacheid;
    2037           8 :                 BBPkeepref(bv);
    2038           8 :                 if (od) {
    2039           4 :                         *od = bo->batCacheid;
    2040           4 :                         BBPkeepref(bo);
    2041             :                 }
    2042             :         }
    2043             :         return msg;
    2044             : }
    2045             : 
    2046             : 
    2047             : 
    2048             : static str
    2049           6 : JSONkeyTable(bat *ret, const json *js)
    2050             : {
    2051           6 :         BAT *bn;
    2052           6 :         char *r;
    2053           6 :         int i;
    2054           6 :         JSON *jt;
    2055             : 
    2056           6 :         jt = JSONparse(*js);            // already validated
    2057           6 :         CHECK_JSON(jt);
    2058           6 :         bn = COLnew(0, TYPE_str, 64, TRANSIENT);
    2059           6 :         if (bn == NULL) {
    2060           0 :                 JSONfree(jt);
    2061           0 :                 throw(MAL, "json.keys", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    2062             :         }
    2063             : 
    2064          20 :         for (i = jt->elm[0].next; i; i = jt->elm[i].next) {
    2065          14 :                 r = JSONgetValue(jt, i);
    2066          14 :                 if (r == NULL || BUNappend(bn, r, false) != GDK_SUCCEED) {
    2067           0 :                         GDKfree(r);
    2068           0 :                         JSONfree(jt);
    2069           0 :                         BBPreclaim(bn);
    2070           0 :                         throw(MAL, "json.keys", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    2071             :                 }
    2072          14 :                 GDKfree(r);
    2073             :         }
    2074           6 :         JSONfree(jt);
    2075           6 :         *ret = bn->batCacheid;
    2076           6 :         BBPkeepref(bn);
    2077           6 :         return MAL_SUCCEED;
    2078             : }
    2079             : 
    2080             : static str
    2081           7 : JSONkeyArray(json *ret, const json *js)
    2082             : {
    2083           7 :         char *result = NULL;
    2084           7 :         str r;
    2085           7 :         int i;
    2086           7 :         JSON *jt;
    2087             : 
    2088           7 :         if (strNil(*js)) {
    2089           0 :                 if (!(*ret = GDKstrdup(str_nil)))
    2090           0 :                         throw(MAL, "json.keyarray", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    2091             :                 return MAL_SUCCEED;
    2092             :         }
    2093             : 
    2094           7 :         jt = JSONparse(*js);            // already validated
    2095             : 
    2096           7 :         CHECK_JSON(jt);
    2097           7 :         if (jt->elm[0].kind == JSON_OBJECT) {
    2098          27 :                 for (i = jt->elm[0].next; i; i = jt->elm[i].next) {
    2099          20 :                         if (jt->elm[i].valuelen) {
    2100          18 :                                 r = GDKmalloc(jt->elm[i].valuelen + 3);
    2101          18 :                                 if (r == NULL) {
    2102           0 :                                         JSONfree(jt);
    2103           0 :                                         goto memfail;
    2104             :                                 }
    2105          18 :                                 strcpy_len(r, jt->elm[i].value - 1, jt->elm[i].valuelen + 3);
    2106             :                         } else {
    2107           2 :                                 r = GDKstrdup("\"\"");
    2108           2 :                                 if (r == NULL) {
    2109           0 :                                         JSONfree(jt);
    2110           0 :                                         goto memfail;
    2111             :                                 }
    2112             :                         }
    2113          20 :                         result = JSONglue(result, r, ',');
    2114          20 :                         if (result == NULL) {
    2115           0 :                                 JSONfree(jt);
    2116           0 :                                 goto memfail;
    2117             :                         }
    2118             :                 }
    2119           7 :                 JSONfree(jt);
    2120             :         } else {
    2121           0 :                 JSONfree(jt);
    2122           0 :                 throw(MAL, "json.keyarray", "Object expected");
    2123             :         }
    2124           7 :         r = GDKstrdup("[");
    2125           7 :         if (r == NULL)
    2126           0 :                 goto memfail;
    2127           7 :         result = JSONglue(r, result, 0);
    2128           7 :         if (result == NULL)
    2129           0 :                 goto memfail;
    2130           7 :         r = GDKstrdup("]");
    2131           7 :         if (r == NULL)
    2132           0 :                 goto memfail;
    2133           7 :         result = JSONglue(result, r, 0);
    2134           7 :         if (result == NULL)
    2135           0 :                 goto memfail;
    2136           7 :         *ret = result;
    2137           7 :         return MAL_SUCCEED;
    2138             : 
    2139           0 :   memfail:
    2140           0 :         GDKfree(result);
    2141           0 :         throw(MAL, "json.keyarray", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    2142             : }
    2143             : 
    2144             : 
    2145             : static str
    2146          10 : JSONvalueTable(bat *ret, const json *js)
    2147             : {
    2148          10 :         BAT *bn;
    2149          10 :         char *r;
    2150          10 :         int i;
    2151          10 :         JSON *jt;
    2152             : 
    2153          10 :         jt = JSONparse(*js);            // already validated
    2154          10 :         CHECK_JSON(jt);
    2155          10 :         bn = COLnew(0, TYPE_json, 64, TRANSIENT);
    2156          10 :         if (bn == NULL) {
    2157           0 :                 JSONfree(jt);
    2158           0 :                 throw(MAL, "json.values", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    2159             :         }
    2160             : 
    2161          34 :         for (i = jt->elm[0].next; i; i = jt->elm[i].next) {
    2162          24 :                 if (jt->elm[i].kind == JSON_ELEMENT)
    2163          14 :                         r = JSONgetValue(jt, jt->elm[i].child);
    2164             :                 else
    2165          10 :                         r = JSONgetValue(jt, i);
    2166          24 :                 if (r == NULL || BUNappend(bn, r, false) != GDK_SUCCEED) {
    2167           0 :                         GDKfree(r);
    2168           0 :                         BBPreclaim(bn);
    2169           0 :                         JSONfree(jt);
    2170           0 :                         throw(MAL, "json.values", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    2171             :                 }
    2172          24 :                 GDKfree(r);
    2173             :         }
    2174          10 :         JSONfree(jt);
    2175          10 :         *ret = bn->batCacheid;
    2176          10 :         BBPkeepref(bn);
    2177          10 :         return MAL_SUCCEED;
    2178             : }
    2179             : 
    2180             : static str
    2181           4 : JSONvalueArray(json *ret, const json *js)
    2182             : {
    2183           4 :         char *result = NULL;
    2184           4 :         str r;
    2185           4 :         int i;
    2186           4 :         JSON *jt;
    2187             : 
    2188           4 :         if (strNil(*js)) {
    2189           0 :                 if (!(*ret = GDKstrdup(str_nil)))
    2190           0 :                         throw(MAL, "json.valuearray", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    2191             :                 return MAL_SUCCEED;
    2192             :         }
    2193             : 
    2194           4 :         jt = JSONparse(*js);            // already validated
    2195             : 
    2196           4 :         CHECK_JSON(jt);
    2197           4 :         if (jt->elm[0].kind == JSON_OBJECT) {
    2198          21 :                 for (i = jt->elm[0].next; i; i = jt->elm[i].next) {
    2199          17 :                         r = JSONgetValue(jt, jt->elm[i].child);
    2200          17 :                         if (r == NULL) {
    2201           0 :                                 JSONfree(jt);
    2202           0 :                                 goto memfail;
    2203             :                         }
    2204          17 :                         result = JSONglue(result, r, ',');
    2205          17 :                         if (result == NULL) {
    2206           0 :                                 JSONfree(jt);
    2207           0 :                                 goto memfail;
    2208             :                         }
    2209             :                 }
    2210           4 :                 JSONfree(jt);
    2211             :         } else {
    2212           0 :                 JSONfree(jt);
    2213           0 :                 throw(MAL, "json.valuearray", "Object expected");
    2214             :         }
    2215           4 :         r = GDKstrdup("[");
    2216           4 :         if (r == NULL)
    2217           0 :                 goto memfail;
    2218           4 :         result = JSONglue(r, result, 0);
    2219           4 :         if (result == NULL)
    2220           0 :                 goto memfail;
    2221           4 :         r = GDKstrdup("]");
    2222           4 :         if (r == NULL)
    2223           0 :                 goto memfail;
    2224           4 :         result = JSONglue(result, r, 0);
    2225           4 :         if (result == NULL)
    2226           0 :                 goto memfail;
    2227           4 :         *ret = result;
    2228           4 :         return MAL_SUCCEED;
    2229             : 
    2230           0 :   memfail:
    2231           0 :         GDKfree(result);
    2232           0 :         throw(MAL, "json.valuearray", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    2233             : }
    2234             : 
    2235             : static BAT **
    2236           2 : JSONargumentlist(MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
    2237             : {
    2238           2 :         int i, error = 0, bats = 0;
    2239           2 :         BUN cnt = 0;
    2240           2 :         BAT **bl;
    2241             : 
    2242           2 :         bl = GDKzalloc(sizeof(*bl) * pci->argc);
    2243           2 :         if (bl == NULL)
    2244             :                 return NULL;
    2245          11 :         for (i = pci->retc; i < pci->argc; i++)
    2246           9 :                 if (isaBatType(getArgType(mb, pci, i))) {
    2247           6 :                         bats++;
    2248           6 :                         bl[i] = BATdescriptor(stk->stk[getArg(pci, i)].val.bval);
    2249           6 :                         if (bl[i] == NULL || (cnt > 0 && BATcount(bl[i]) != cnt)) {
    2250             :                                 error = 1;
    2251             :                                 break;
    2252             :                         }
    2253           6 :                         cnt = BATcount(bl[i]);
    2254             :                 }
    2255           2 :         if (error || bats == 0) {
    2256           0 :                 for (i = pci->retc; i < pci->argc; i++)
    2257           0 :                         BBPreclaim(bl[i]);
    2258           0 :                 GDKfree(bl);
    2259           0 :                 return NULL;
    2260             :         }
    2261             :         return bl;
    2262             : }
    2263             : 
    2264             : static void
    2265           2 : JSONfreeArgumentlist(BAT **bl, InstrPtr pci)
    2266             : {
    2267           2 :         int i;
    2268             : 
    2269          11 :         for (i = pci->retc; i < pci->argc; i++)
    2270          15 :                 BBPreclaim(bl[i]);
    2271           2 :         GDKfree(bl);
    2272           2 : }
    2273             : 
    2274             : static str
    2275           3 : JSONrenderRowObject(BAT **bl, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci,
    2276             :                                         BUN idx)
    2277             : {
    2278           3 :         int i, tpe;
    2279           3 :         char *row, *row2, *name = 0, *val = 0;
    2280           3 :         size_t len, lim, l;
    2281           3 :         void *p;
    2282           3 :         BATiter bi;
    2283             : 
    2284           3 :         row = GDKmalloc(lim = BUFSIZ);
    2285           3 :         if (row == NULL)
    2286             :                 return NULL;
    2287           3 :         row[0] = '{';
    2288           3 :         row[1] = 0;
    2289           3 :         len = 1;
    2290          12 :         for (i = pci->retc; i < pci->argc; i += 2) {
    2291           9 :                 name = stk->stk[getArg(pci, i)].val.sval;
    2292           9 :                 tpe = getBatType(getArgType(mb, pci, i + 1));
    2293           9 :                 bi = bat_iterator(bl[i + 1]);
    2294           9 :                 p = BUNtail(bi, idx);
    2295           9 :                 val = ATOMformat(tpe, p);
    2296           9 :                 bat_iterator_end(&bi);
    2297           9 :                 if (val == NULL) {
    2298           0 :                         GDKfree(row);
    2299           0 :                         return NULL;
    2300             :                 }
    2301           9 :                 if (strncmp(val, "nil", 3) == 0) {
    2302           1 :                         GDKfree(val);
    2303           1 :                         val = NULL;
    2304           1 :                         l = 4;
    2305             :                 } else {
    2306           8 :                         l = strlen(val);
    2307             :                 }
    2308           9 :                 l += strlen(name) + 4;
    2309           9 :                 while (l > lim - len)
    2310           0 :                         lim += BUFSIZ;
    2311           9 :                 row2 = GDKrealloc(row, lim);
    2312           9 :                 if (row2 == NULL) {
    2313           0 :                         GDKfree(row);
    2314           0 :                         GDKfree(val);
    2315           0 :                         return NULL;
    2316             :                 }
    2317           9 :                 row = row2;
    2318           9 :                 snprintf(row + len, lim - len, "\"%s\":%s,", name, val ? val : "null");
    2319           9 :                 len += l;
    2320           9 :                 GDKfree(val);
    2321             :         }
    2322           3 :         if (row[1])
    2323           3 :                 row[len - 1] = '}';
    2324             :         else {
    2325           0 :                 row[1] = '}';
    2326           0 :                 row[2] = 0;
    2327             :         }
    2328             :         return row;
    2329             : }
    2330             : 
    2331             : static str
    2332           1 : JSONrenderobject(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
    2333             : {
    2334           1 :         BAT **bl;
    2335           1 :         char *result, *row;
    2336           1 :         int i;
    2337           1 :         size_t len, lim, l;
    2338           1 :         json *ret;
    2339           1 :         BUN j, cnt;
    2340             : 
    2341           1 :         (void) cntxt;
    2342           1 :         bl = JSONargumentlist(mb, stk, pci);
    2343           1 :         if (bl == 0)
    2344           0 :                 throw(MAL, "json.renderobject", "Non-aligned BAT sizes");
    2345           4 :         for (i = pci->retc; i < pci->argc; i += 2) {
    2346           3 :                 if (getArgType(mb, pci, i) != TYPE_str) {
    2347           0 :                         JSONfreeArgumentlist(bl, pci);
    2348           0 :                         throw(MAL, "json.renderobject", "Keys missing");
    2349             :                 }
    2350             :         }
    2351             : 
    2352           1 :         cnt = BATcount(bl[pci->retc + 1]);
    2353           1 :         result = GDKmalloc(lim = BUFSIZ);
    2354           1 :         if (result == NULL) {
    2355           0 :                 JSONfreeArgumentlist(bl, pci);
    2356           0 :                 throw(MAL, "json.renderobject", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    2357             :         }
    2358           1 :         result[0] = '[';
    2359           1 :         result[1] = 0;
    2360           1 :         len = 1;
    2361             : 
    2362           4 :         for (j = 0; j < cnt; j++) {
    2363           3 :                 char *result2;
    2364           3 :                 row = JSONrenderRowObject(bl, mb, stk, pci, j);
    2365           3 :                 if (row == NULL)
    2366           0 :                         goto memfail;
    2367           3 :                 l = strlen(row);
    2368           6 :                 while (l + 2 > lim - len)
    2369           0 :                         lim = cnt * l <= lim ? cnt * l : lim + BUFSIZ;
    2370           3 :                 result2 = GDKrealloc(result, lim);
    2371           3 :                 if (result2 == NULL)
    2372           0 :                         goto memfail;
    2373           3 :                 result = result2;
    2374           3 :                 strcpy(result + len, row);
    2375           3 :                 GDKfree(row);
    2376           3 :                 len += l;
    2377           3 :                 result[len++] = ',';
    2378           3 :                 result[len] = 0;
    2379             :         }
    2380           1 :         result[len - 1] = ']';
    2381           1 :         ret = getArgReference_TYPE(stk, pci, 0, json);
    2382           1 :         *ret = result;
    2383           1 :         JSONfreeArgumentlist(bl, pci);
    2384           1 :         return MAL_SUCCEED;
    2385             : 
    2386           0 :   memfail:
    2387           0 :         GDKfree(result);
    2388           0 :         GDKfree(row);
    2389           0 :         JSONfreeArgumentlist(bl, pci);
    2390           0 :         throw(MAL, "json.renderobject", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    2391             : }
    2392             : 
    2393             : static str
    2394           3 : JSONrenderRowArray(BAT **bl, MalBlkPtr mb, InstrPtr pci, BUN idx)
    2395             : {
    2396           3 :         int i, tpe;
    2397           3 :         char *row, *row2, *val = 0;
    2398           3 :         size_t len, lim, l;
    2399           3 :         void *p;
    2400           3 :         BATiter bi;
    2401             : 
    2402           3 :         row = GDKmalloc(lim = BUFSIZ);
    2403           3 :         if (row == NULL)
    2404             :                 return NULL;
    2405           3 :         row[0] = '[';
    2406           3 :         row[1] = 0;
    2407           3 :         len = 1;
    2408          12 :         for (i = pci->retc; i < pci->argc; i++) {
    2409           9 :                 tpe = getBatType(getArgType(mb, pci, i));
    2410           9 :                 bi = bat_iterator(bl[i]);
    2411           9 :                 p = BUNtail(bi, idx);
    2412           9 :                 val = ATOMformat(tpe, p);
    2413           9 :                 bat_iterator_end(&bi);
    2414           9 :                 if (val == NULL)
    2415           0 :                         goto memfail;
    2416           9 :                 if (strcmp(val, "nil") == 0) {
    2417           1 :                         GDKfree(val);
    2418           1 :                         val = NULL;
    2419           1 :                         l = 4;
    2420             :                 } else {
    2421           8 :                         l = strlen(val);
    2422             :                 }
    2423           9 :                 while (len + l > lim)
    2424           0 :                         lim += BUFSIZ;
    2425           9 :                 row2 = GDKrealloc(row, lim);
    2426           9 :                 if (row2 == NULL)
    2427           0 :                         goto memfail;
    2428           9 :                 row = row2;
    2429           9 :                 snprintf(row + len, lim - len, "%s,", val ? val : "null");
    2430           9 :                 len += l + 1;
    2431           9 :                 GDKfree(val);
    2432           9 :                 val = NULL;
    2433             :         }
    2434           3 :         if (row[1])
    2435           3 :                 row[len - 1] = ']';
    2436             :         else {
    2437           0 :                 row[1] = '}';
    2438           0 :                 row[2] = 0;
    2439             :         }
    2440             :         return row;
    2441             : 
    2442           0 :   memfail:
    2443           0 :         GDKfree(row);
    2444           0 :         GDKfree(val);
    2445           0 :         return NULL;
    2446             : }
    2447             : 
    2448             : static str
    2449           1 : JSONrenderarray(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
    2450             : {
    2451           1 :         BAT **bl;
    2452           1 :         char *result, *row;
    2453           1 :         size_t len, lim, l;
    2454           1 :         str *ret;
    2455           1 :         BUN j, cnt;
    2456             : 
    2457           1 :         (void) cntxt;
    2458           1 :         bl = JSONargumentlist(mb, stk, pci);
    2459           1 :         if (bl == 0)
    2460           0 :                 throw(MAL, "json.renderrray", "Non-aligned BAT sizes");
    2461             : 
    2462           1 :         cnt = BATcount(bl[pci->retc + 1]);
    2463           1 :         result = GDKmalloc(lim = BUFSIZ);
    2464           1 :         if (result == NULL) {
    2465           0 :                 goto memfail;
    2466             :         }
    2467           1 :         result[0] = '[';
    2468           1 :         result[1] = 0;
    2469           1 :         len = 1;
    2470             : 
    2471           4 :         for (j = 0; j < cnt; j++) {
    2472           3 :                 char *result2;
    2473           3 :                 row = JSONrenderRowArray(bl, mb, pci, j);
    2474           3 :                 if (row == NULL) {
    2475           0 :                         goto memfail;
    2476             :                 }
    2477           3 :                 l = strlen(row);
    2478           6 :                 while (l + 2 > lim - len)
    2479           0 :                         lim = cnt * l <= lim ? cnt * l : lim + BUFSIZ;
    2480           3 :                 result2 = GDKrealloc(result, lim);
    2481           3 :                 if (result2 == NULL) {
    2482           0 :                         GDKfree(row);
    2483           0 :                         goto memfail;
    2484             :                 }
    2485           3 :                 result = result2;
    2486           3 :                 strcpy(result + len, row);
    2487           3 :                 GDKfree(row);
    2488           3 :                 len += l;
    2489           3 :                 result[len++] = ',';
    2490           3 :                 result[len] = 0;
    2491             :         }
    2492           1 :         result[len - 1] = ']';
    2493           1 :         ret = getArgReference_TYPE(stk, pci, 0, json);
    2494           1 :         *ret = result;
    2495           1 :         JSONfreeArgumentlist(bl, pci);
    2496           1 :         return MAL_SUCCEED;
    2497             : 
    2498           0 :   memfail:
    2499           0 :         GDKfree(result);
    2500           0 :         JSONfreeArgumentlist(bl, pci);
    2501           0 :         throw(MAL, "json.renderArray", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    2502             : }
    2503             : 
    2504             : static str
    2505           7 : JSONfoldKeyValue(str *ret, const bat *id, const bat *key, const bat *values)
    2506             : {
    2507           7 :         BAT *bo = 0, *bk = 0, *bv;
    2508           7 :         BATiter bki, bvi;
    2509           7 :         int tpe;
    2510           7 :         char *row, *val = 0, *nme = 0;
    2511           7 :         BUN i, cnt;
    2512           7 :         size_t len, lim, l;
    2513           7 :         void *p;
    2514           7 :         oid o = 0;
    2515             : 
    2516           7 :         if (key) {
    2517           6 :                 bk = BATdescriptor(*key);
    2518           6 :                 if (bk == NULL) {
    2519           0 :                         throw(MAL, "json.fold", SQLSTATE(HY002) RUNTIME_OBJECT_MISSING);
    2520             :                 }
    2521             :         }
    2522             : 
    2523           7 :         bv = BATdescriptor(*values);
    2524           7 :         if (bv == NULL) {
    2525           0 :                 BBPreclaim(bk);
    2526           0 :                 throw(MAL, "json.fold", SQLSTATE(HY002) RUNTIME_OBJECT_MISSING);
    2527             :         }
    2528           7 :         tpe = bv->ttype;
    2529           7 :         cnt = BATcount(bv);
    2530           7 :         if (id) {
    2531           3 :                 bo = BATdescriptor(*id);
    2532           3 :                 if (bo == NULL) {
    2533           0 :                         BBPreclaim(bk);
    2534           0 :                         BBPunfix(bv->batCacheid);
    2535           0 :                         throw(MAL, "json.nest", SQLSTATE(HY002) RUNTIME_OBJECT_MISSING);
    2536             :                 }
    2537             :         }
    2538             : 
    2539           7 :         row = GDKmalloc(lim = BUFSIZ);
    2540           7 :         if (row == NULL) {
    2541           0 :                 goto memfail;
    2542             :         }
    2543           7 :         row[0] = '[';
    2544           7 :         row[1] = 0;
    2545           7 :         len = 1;
    2546           7 :         if (id) {
    2547           3 :                 o = BUNtoid(bo, 0);
    2548             :         }
    2549             : 
    2550           7 :         bki = bat_iterator(bk);
    2551           7 :         bvi = bat_iterator(bv);
    2552          46 :         for (i = 0; i < cnt; i++) {
    2553          32 :                 if (id && bk) {
    2554          15 :                         if (BUNtoid(bo, i) != o) {
    2555           9 :                                 snprintf(row + len, lim - len, ", ");
    2556           9 :                                 len += 2;
    2557           9 :                                 o = BUNtoid(bo, i);
    2558             :                         }
    2559             :                 }
    2560             : 
    2561          32 :                 if (bk) {
    2562          29 :                         nme = (str) BUNtvar(bki, i);
    2563          29 :                         l = strlen(nme);
    2564          29 :                         while (l + 3 > lim - len)
    2565           0 :                                 lim = (lim / (i + 1)) * cnt + BUFSIZ + l + 3;
    2566          29 :                         p = GDKrealloc(row, lim);
    2567          29 :                         if (p == NULL) {
    2568           0 :                                 bat_iterator_end(&bki);
    2569           0 :                                 bat_iterator_end(&bvi);
    2570           0 :                                 goto memfail;
    2571             :                         }
    2572          29 :                         row = p;
    2573          58 :                         if (!strNil(nme)) {
    2574          14 :                                 snprintf(row + len, lim - len, "\"%s\":", nme);
    2575          14 :                                 len += l + 3;
    2576             :                         }
    2577             :                 }
    2578             : 
    2579          32 :                 p = BUNtail(bvi, i);
    2580          32 :                 if (tpe == TYPE_json)
    2581             :                         val = p;
    2582             :                 else {
    2583           9 :                         if ((val = ATOMformat(tpe, p)) == NULL) {
    2584           0 :                                 bat_iterator_end(&bki);
    2585           0 :                                 bat_iterator_end(&bvi);
    2586           0 :                                 goto memfail;
    2587             :                         }
    2588           9 :                         if (strcmp(val, "nil") == 0) {
    2589           0 :                                 GDKfree(val);
    2590           0 :                                 val = NULL;
    2591             :                         }
    2592             :                 }
    2593          32 :                 l = val ? strlen(val) : 4;
    2594          32 :                 while (l > lim - len)
    2595           0 :                         lim = (lim / (i + 1)) * cnt + BUFSIZ + l + 3;
    2596          32 :                 p = GDKrealloc(row, lim);
    2597          32 :                 if (p == NULL) {
    2598           0 :                         if (tpe != TYPE_json)
    2599           0 :                                 GDKfree(val);
    2600           0 :                         bat_iterator_end(&bki);
    2601           0 :                         bat_iterator_end(&bvi);
    2602           0 :                         goto memfail;
    2603             :                 }
    2604          32 :                 row = p;
    2605          32 :                 strncpy(row + len, val ? val : "null", l);
    2606          32 :                 len += l;
    2607          32 :                 row[len++] = ',';
    2608          32 :                 row[len] = 0;
    2609          32 :                 if (tpe != TYPE_json)
    2610           9 :                         GDKfree(val);
    2611             :         }
    2612           7 :         bat_iterator_end(&bki);
    2613           7 :         bat_iterator_end(&bvi);
    2614           7 :         if (row[1]) {
    2615           7 :                 row[len - 1] = ']';
    2616           7 :                 row[len] = 0;
    2617             :         } else {
    2618           0 :                 row[1] = ']';
    2619           0 :                 row[2] = 0;
    2620             :         }
    2621           7 :         BBPreclaim(bo);
    2622           7 :         BBPreclaim(bk);
    2623           7 :         BBPunfix(bv->batCacheid);
    2624           7 :         *ret = row;
    2625           7 :         return MAL_SUCCEED;
    2626             : 
    2627           0 :   memfail:
    2628           0 :         GDKfree(row);
    2629           0 :         BBPreclaim(bo);
    2630           0 :         BBPreclaim(bk);
    2631           0 :         BBPunfix(bv->batCacheid);
    2632           0 :         throw(MAL, "json.fold", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    2633             : }
    2634             : 
    2635             : static str
    2636           8 : JSONunfold(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
    2637             : {
    2638           8 :         bat *id = 0, *key = 0, *val = 0;
    2639           8 :         json *js;
    2640             : 
    2641           8 :         (void) cntxt;
    2642           8 :         (void) mb;
    2643             : 
    2644           8 :         switch (pci->retc) {
    2645           4 :         case 2:
    2646           4 :                 key = getArgReference_bat(stk, pci, 0);
    2647           4 :                 val = getArgReference_bat(stk, pci, 1);
    2648           4 :                 break;
    2649           4 :         case 3:
    2650           4 :                 id = getArgReference_bat(stk, pci, 0);
    2651           4 :                 key = getArgReference_bat(stk, pci, 1);
    2652           4 :                 val = getArgReference_bat(stk, pci, 2);
    2653           4 :                 break;
    2654             :         default:
    2655           0 :                 assert(0);
    2656             :                 throw(MAL, "json.unfold", ILLEGAL_ARGUMENT);
    2657             :         }
    2658           8 :         js = getArgReference_TYPE(stk, pci, pci->retc, json);
    2659           8 :         return JSONunfoldInternal(id, key, val, js);
    2660             : }
    2661             : 
    2662             : static str
    2663           7 : JSONfold(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
    2664             : {
    2665           7 :         bat *id = 0, *key = 0, *val = 0;
    2666           7 :         str *ret;
    2667             : 
    2668           7 :         (void) cntxt;
    2669           7 :         (void) mb;
    2670             : 
    2671           7 :         assert(pci->retc == 1);
    2672           7 :         switch (pci->argc - pci->retc) {
    2673           1 :         case 1:
    2674           1 :                 val = getArgReference_bat(stk, pci, 1);
    2675           1 :                 break;
    2676           3 :         case 2:
    2677           3 :                 key = getArgReference_bat(stk, pci, 1);
    2678           3 :                 val = getArgReference_bat(stk, pci, 2);
    2679           3 :                 break;
    2680           3 :         case 3:
    2681           3 :                 id = getArgReference_bat(stk, pci, 1);
    2682           3 :                 key = getArgReference_bat(stk, pci, 2);
    2683           3 :                 val = getArgReference_bat(stk, pci, 3);
    2684           3 :                 break;
    2685             :         default:
    2686           0 :                 assert(0);
    2687             :                 throw(MAL, "json.fold", ILLEGAL_ARGUMENT);
    2688             :         }
    2689           7 :         ret = getArgReference_TYPE(stk, pci, 0, json);
    2690           7 :         return JSONfoldKeyValue(ret, id, key, val);
    2691             : }
    2692             : 
    2693             : #define JSON_STR_CPY    \
    2694             :         do {    \
    2695             :                 for (; *v; v++) {       \
    2696             :                         switch (*v) {   \
    2697             :                         case '"':  \
    2698             :                         case '\\':      \
    2699             :                                 *dst++ = '\\';  \
    2700             :                                 /* fall through */      \
    2701             :                         default:        \
    2702             :                                 *dst++ = *v;    \
    2703             :                                 break;  \
    2704             :                         case '\n':      \
    2705             :                                 *dst++ = '\\';  \
    2706             :                                 *dst++ = 'n';   \
    2707             :                                 break;  \
    2708             :                         }       \
    2709             :                 }       \
    2710             :         } while (0)
    2711             : 
    2712             : #define JSON_AGGR_CHECK_NEXT_LENGTH(CALC)       \
    2713             :         do {    \
    2714             :                 len = CALC;     \
    2715             :                 if (len >= maxlen - buflen) {        \
    2716             :                         maxlen = maxlen + len + BUFSIZ; \
    2717             :                         buf2 = GDKrealloc(buf, maxlen); \
    2718             :                         if (buf2 == NULL) {     \
    2719             :                                 err = SQLSTATE(HY013) MAL_MALLOC_FAIL;  \
    2720             :                                 goto bunins_failed;     \
    2721             :                         }       \
    2722             :                         buf = buf2;     \
    2723             :                 }       \
    2724             :         } while (0)
    2725             : 
    2726             : static str
    2727          17 : JSONgroupStr(str *ret, const bat *bid)
    2728             : {
    2729          17 :         BAT *b;
    2730          17 :         BUN p, q;
    2731          17 :         size_t len, maxlen = BUFSIZ, buflen = 0;
    2732          17 :         char *buf = GDKmalloc(maxlen), *buf2;
    2733          17 :         BATiter bi;
    2734          17 :         const char *err = NULL;
    2735          17 :         dbl *restrict vals;
    2736             : 
    2737          17 :         if (buf == NULL)
    2738           0 :                 throw(MAL, "json.group", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    2739          17 :         if ((b = BATdescriptor(*bid)) == NULL) {
    2740           0 :                 GDKfree(buf);
    2741           0 :                 throw(MAL, "json.group", SQLSTATE(HY002) RUNTIME_OBJECT_MISSING);
    2742             :         }
    2743          17 :         assert(maxlen > 256);                /* make sure every floating point fits on the dense case */
    2744          17 :         assert(b->ttype == TYPE_str || b->ttype == TYPE_dbl);
    2745             : 
    2746          17 :         bi = bat_iterator(b);
    2747          17 :         vals = (dbl *) Tloc(b, 0);
    2748          17 :         switch (b->ttype) {
    2749           8 :         case TYPE_str:
    2750          23 :                 for (p = 0, q = BATcount(b); p < q; p++) {
    2751          15 :                         const char *v = (const char *) BUNtvar(bi, p);
    2752             : 
    2753          15 :                         if (strNil(v))
    2754           3 :                                 continue;
    2755             :                         /* '[' or ',' plus space and null terminator and " ]" final string */
    2756          12 :                         JSON_AGGR_CHECK_NEXT_LENGTH(strlen(v) * 2 + 7);
    2757          12 :                         char *dst = buf + buflen, *odst = dst;
    2758          12 :                         if (buflen == 0)
    2759           6 :                                 *dst++ = '[';
    2760             :                         else
    2761           6 :                                 *dst++ = ',';
    2762          12 :                         *dst++ = ' ';
    2763          12 :                         *dst++ = '"';
    2764          64 :                         JSON_STR_CPY;
    2765          12 :                         *dst++ = '"';
    2766          12 :                         buflen += (dst - odst);
    2767             :                 }
    2768             :                 break;
    2769           9 :         case TYPE_dbl:
    2770          27 :                 for (p = 0, q = BATcount(b); p < q; p++) {
    2771          18 :                         dbl val = vals[p];
    2772             : 
    2773          18 :                         if (is_dbl_nil(val))
    2774           7 :                                 continue;
    2775             :                         /* '[' or ',' plus space and null terminator and " ]" final string */
    2776          11 :                         JSON_AGGR_CHECK_NEXT_LENGTH(130 + 6);
    2777          11 :                         char *dst = buf + buflen;
    2778          11 :                         if (buflen == 0)
    2779           6 :                                 *dst++ = '[';
    2780             :                         else
    2781           5 :                                 *dst++ = ',';
    2782          11 :                         *dst++ = ' ';
    2783          11 :                         buflen += 2;
    2784          11 :                         buflen += snprintf(buf + buflen, maxlen - buflen, "%f", val);
    2785             :                 }
    2786             :                 break;
    2787             :         default:
    2788           0 :                 assert(0);
    2789             :         }
    2790          17 :         bat_iterator_end(&bi);
    2791          17 :         BBPunfix(b->batCacheid);
    2792          17 :         if (buflen > 0)
    2793          12 :                 strcpy(buf + buflen, " ]");
    2794             :         else
    2795           5 :                 strcpy(buf, str_nil);
    2796          17 :         *ret = GDKstrdup(buf);
    2797          17 :         GDKfree(buf);
    2798          17 :         if (!*ret)                                      /* Don't return a too large string */
    2799           0 :                 throw(MAL, "json.group", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    2800             :         return MAL_SUCCEED;
    2801           0 :   bunins_failed:
    2802           0 :         bat_iterator_end(&bi);
    2803           0 :         BBPunfix(b->batCacheid);
    2804           0 :         GDKfree(buf);
    2805           0 :         throw(MAL, "json.group", "%s", err);
    2806             : }
    2807             : 
    2808             : static const char *
    2809           9 : JSONjsonaggr(BAT **bnp, BAT *b, BAT *g, BAT *e, BAT *s, int skip_nils)
    2810             : {
    2811           9 :         BAT *bn = NULL, *t1, *t2 = NULL;
    2812           9 :         BATiter bi;
    2813           9 :         oid min, max, mapoff = 0, prev;
    2814           9 :         BUN ngrp, nils = 0, p, q;
    2815           9 :         struct canditer ci;
    2816           9 :         const char *err = NULL;
    2817           9 :         const oid *grps, *map;
    2818           9 :         int freeb = 0, freeg = 0, isnil = 0;
    2819           9 :         char *buf = NULL, *buf2;
    2820           9 :         size_t buflen, maxlen = BUFSIZ, len;
    2821           9 :         dbl *restrict vals;
    2822             : 
    2823           9 :         assert(maxlen > 256);                /* make sure every floating point fits on the dense case */
    2824           9 :         assert(b->ttype == TYPE_str || b->ttype == TYPE_dbl);
    2825           9 :         if ((err = BATgroupaggrinit(b, g, e, s, &min, &max, &ngrp, &ci)) != NULL) {
    2826             :                 return err;
    2827             :         }
    2828           9 :         if (BATcount(b) == 0 || ngrp == 0) {
    2829           0 :                 bn = BATconstant(ngrp == 0 ? 0 : min, TYPE_str, ATOMnilptr(TYPE_str),
    2830             :                                                  ngrp, TRANSIENT);
    2831           0 :                 if (bn == NULL)
    2832             :                         return SQLSTATE(HY013) MAL_MALLOC_FAIL;
    2833           0 :                 *bnp = bn;
    2834           0 :                 return NULL;
    2835             :         }
    2836           9 :         if (s) {
    2837           0 :                 b = BATproject(s, b);
    2838           0 :                 if (b == NULL) {
    2839           0 :                         err = GDK_EXCEPTION;
    2840           0 :                         goto out;
    2841             :                 }
    2842           0 :                 freeb = 1;
    2843           0 :                 if (g) {
    2844           0 :                         g = BATproject(s, g);
    2845           0 :                         if (g == NULL) {
    2846           0 :                                 err = GDK_EXCEPTION;
    2847           0 :                                 goto out;
    2848             :                         }
    2849             :                         freeg = 1;
    2850             :                 }
    2851             :         }
    2852             : 
    2853           9 :         if ((buf = GDKmalloc(maxlen)) == NULL) {
    2854           0 :                 err = SQLSTATE(HY013) MAL_MALLOC_FAIL;
    2855           0 :                 goto out;
    2856             :         }
    2857           9 :         buflen = 0;
    2858           9 :         bn = COLnew(min, TYPE_str, ngrp, TRANSIENT);
    2859           9 :         if (bn == NULL) {
    2860           0 :                 err = SQLSTATE(HY013) MAL_MALLOC_FAIL;
    2861           0 :                 goto out;
    2862             :         }
    2863           9 :         bi = bat_iterator(b);
    2864           9 :         vals = (dbl *) Tloc(b, 0);
    2865           9 :         if (g) {
    2866             :                 /* stable sort g */
    2867           9 :                 if (BATsort(&t1, &t2, NULL, g, NULL, NULL, false, false, true) != GDK_SUCCEED) {
    2868           0 :                         err = GDK_EXCEPTION;
    2869           0 :                         bat_iterator_end(&bi);
    2870           0 :                         goto out;
    2871             :                 }
    2872           9 :                 if (freeg)
    2873           0 :                         BBPunfix(g->batCacheid);
    2874           9 :                 g = t1;
    2875           9 :                 freeg = 1;
    2876           9 :                 if (t2->ttype == TYPE_void) {
    2877             :                         map = NULL;
    2878             :                 } else {
    2879           1 :                         map = (const oid *) Tloc(t2, 0);
    2880           1 :                         mapoff = t2->tseqbase;
    2881             :                 }
    2882           9 :                 if (g && BATtdense(g)) {
    2883           1 :                         switch (b->ttype) {
    2884           0 :                         case TYPE_str:
    2885           0 :                                 for (p = 0, q = BATcount(g); p < q; p++) {
    2886           0 :                                         const char *v = (const char *) BUNtvar(bi,
    2887             :                                                                                                                    (map ? (BUN) map[p] -
    2888             :                                                                                                                         mapoff : p));
    2889           0 :                                         if (strNil(v)) {
    2890           0 :                                                 if (skip_nils) {
    2891             :                                                         /*
    2892             :                                                          * if q is 1 and the value is
    2893             :                                                          * null, then we need to fill
    2894             :                                                          * in a value. Otherwise
    2895             :                                                          * BATproject will fail.
    2896             :                                                          */
    2897           0 :                                                         if (p == 0 && q == 1)
    2898           0 :                                                                 strcpy(buf, "[ null ]");
    2899             :                                                         else
    2900           0 :                                                                 continue;
    2901             :                                                 } else {
    2902           0 :                                                         strcpy(buf, str_nil);
    2903           0 :                                                         nils = 1;
    2904             :                                                 }
    2905             :                                         } else {
    2906           0 :                                                 JSON_AGGR_CHECK_NEXT_LENGTH(strlen(v) * 2 + 7);
    2907           0 :                                                 char *dst = buf;
    2908           0 :                                                 *dst++ = '[';
    2909           0 :                                                 *dst++ = ' ';
    2910           0 :                                                 *dst++ = '"';
    2911           0 :                                                 JSON_STR_CPY;
    2912           0 :                                                 *dst++ = '"';
    2913           0 :                                                 *dst++ = ' ';
    2914           0 :                                                 *dst++ = ']';
    2915           0 :                                                 *dst = '\0';
    2916             :                                         }
    2917           0 :                                         if (bunfastapp_nocheckVAR(bn, buf) != GDK_SUCCEED)
    2918           0 :                                                 goto bunins_failed;
    2919             :                                 }
    2920             :                                 break;
    2921           1 :                         case TYPE_dbl:
    2922           3 :                                 for (p = 0, q = BATcount(g); p < q; p++) {
    2923           2 :                                         dbl val = vals[(map ? (BUN) map[p] - mapoff : p)];
    2924           2 :                                         if (is_dbl_nil(val)) {
    2925           0 :                                                 if (skip_nils) {
    2926           0 :                                                         if (p == 0 && q == 1)
    2927           0 :                                                                 strcpy(buf, "[ null ]");
    2928             :                                                         else
    2929           0 :                                                                 continue;
    2930             :                                                 } else {
    2931           0 :                                                         strcpy(buf, str_nil);
    2932           0 :                                                         nils = 1;
    2933             :                                                 }
    2934             :                                         } else {
    2935           2 :                                                 char *dst = buf;
    2936           2 :                                                 *dst++ = '[';
    2937           2 :                                                 *dst++ = ' ';
    2938           2 :                                                 dst += sprintf(dst, "%f", val);
    2939           2 :                                                 *dst++ = ' ';
    2940           2 :                                                 *dst++ = ']';
    2941           2 :                                                 *dst = '\0';
    2942             :                                         }
    2943           2 :                                         if (bunfastapp_nocheckVAR(bn, buf) != GDK_SUCCEED)
    2944           0 :                                                 goto bunins_failed;
    2945             :                                 }
    2946             :                                 break;
    2947             :                         default:
    2948           0 :                                 assert(0);
    2949             :                         }
    2950             :                 } else {
    2951           8 :                         grps = (const oid *) Tloc(g, 0);
    2952           8 :                         prev = grps[0];
    2953        2127 :                         for (p = 0, q = BATcount(g); p <= q; p++) {
    2954        2127 :                                 if (p == q || grps[p] != prev) {
    2955          15 :                                         if (isnil) {
    2956           0 :                                                 strcpy(buf, str_nil);
    2957           0 :                                                 nils = 1;
    2958          15 :                                         } else if (buflen == 0) {
    2959           1 :                                                 strcpy(buf, "[  ]");
    2960             :                                         } else {
    2961          14 :                                                 strcpy(buf + buflen, " ]");
    2962             :                                         }
    2963          15 :                                         while (BATcount(bn) < prev - min) {
    2964           0 :                                                 if (bunfastapp_nocheckVAR(bn, str_nil) != GDK_SUCCEED)
    2965           0 :                                                         goto bunins_failed;
    2966             :                                                 nils = 1;
    2967             :                                         }
    2968          15 :                                         if (bunfastapp_nocheckVAR(bn, buf) != GDK_SUCCEED)
    2969           0 :                                                 goto bunins_failed;
    2970          15 :                                         if (p == q)
    2971             :                                                 break;
    2972           7 :                                         buflen = 0;
    2973           7 :                                         isnil = 0;
    2974           7 :                                         prev = grps[p];
    2975             :                                 }
    2976        2119 :                                 if (isnil)
    2977           0 :                                         continue;
    2978        2119 :                                 switch (b->ttype) {
    2979        2108 :                                 case TYPE_str:{
    2980        2108 :                                         const char *v = (const char *) BUNtvar(bi, p);
    2981        2108 :                                         if (strNil(v)) {
    2982           1 :                                                 if (skip_nils)
    2983           1 :                                                         continue;
    2984             :                                                 isnil = 1;
    2985             :                                         } else {
    2986             :                                                 /* '[' or ',' plus space and null terminator and " ]" final string */
    2987        2107 :                                                 JSON_AGGR_CHECK_NEXT_LENGTH(strlen(v) * 2 + 7);
    2988        2107 :                                                 char *dst = buf + buflen, *odst = dst;
    2989        2107 :                                                 if (buflen == 0)
    2990           9 :                                                         *dst++ = '[';
    2991             :                                                 else
    2992        2098 :                                                         *dst++ = ',';
    2993        2107 :                                                 *dst++ = ' ';
    2994        2107 :                                                 *dst++ = '"';
    2995        4243 :                                                 JSON_STR_CPY;
    2996        2107 :                                                 *dst++ = '"';
    2997        2107 :                                                 buflen += (dst - odst);
    2998             :                                         }
    2999             :                                         break;
    3000             :                                 }
    3001          11 :                                 case TYPE_dbl:{
    3002          11 :                                         dbl val = vals[p];
    3003          11 :                                         if (is_dbl_nil(val)) {
    3004           4 :                                                 if (skip_nils)
    3005           4 :                                                         continue;
    3006             :                                                 isnil = 1;
    3007             :                                         } else {
    3008             :                                                 /* '[' or ',' plus space and null terminator and " ]" final string */
    3009           7 :                                                 JSON_AGGR_CHECK_NEXT_LENGTH(130 + 6);
    3010           7 :                                                 char *dst = buf + buflen;
    3011           7 :                                                 if (buflen == 0)
    3012           5 :                                                         *dst++ = '[';
    3013             :                                                 else
    3014           2 :                                                         *dst++ = ',';
    3015           7 :                                                 *dst++ = ' ';
    3016           7 :                                                 buflen += 2;
    3017           7 :                                                 buflen += snprintf(buf + buflen, maxlen - buflen, "%f",
    3018             :                                                                                    val);
    3019             :                                         }
    3020             :                                         break;
    3021             :                                 }
    3022             :                                 default:
    3023           0 :                                         assert(0);
    3024             :                                 }
    3025             :                         }
    3026           8 :                         BBPunfix(t2->batCacheid);
    3027           8 :                         t2 = NULL;
    3028             :                 }
    3029             :         } else {
    3030           0 :                 switch (b->ttype) {
    3031           0 :                 case TYPE_str:
    3032           0 :                         for (p = 0, q = p + BATcount(b); p < q; p++) {
    3033           0 :                                 const char *v = (const char *) BUNtvar(bi, p);
    3034           0 :                                 if (strNil(v)) {
    3035           0 :                                         if (skip_nils)
    3036           0 :                                                 continue;
    3037             :                                         nils = 1;
    3038             :                                         break;
    3039             :                                 }
    3040             :                                 /* '[' or ',' plus space and null terminator and " ]" final string */
    3041           0 :                                 JSON_AGGR_CHECK_NEXT_LENGTH(strlen(v) * 2 + 7);
    3042           0 :                                 char *dst = buf + buflen, *odst = dst;
    3043           0 :                                 if (buflen == 0)
    3044           0 :                                         *dst++ = '[';
    3045             :                                 else
    3046           0 :                                         *dst++ = ',';
    3047           0 :                                 *dst++ = ' ';
    3048           0 :                                 *dst++ = '"';
    3049           0 :                                 JSON_STR_CPY;
    3050           0 :                                 *dst++ = '"';
    3051           0 :                                 buflen += (dst - odst);
    3052             :                         }
    3053             :                         break;
    3054           0 :                 case TYPE_dbl:
    3055           0 :                         for (p = 0, q = p + BATcount(b); p < q; p++) {
    3056           0 :                                 dbl val = vals[p];
    3057           0 :                                 if (is_dbl_nil(val)) {
    3058           0 :                                         if (skip_nils)
    3059           0 :                                                 continue;
    3060             :                                         nils = 1;
    3061             :                                         break;
    3062             :                                 }
    3063             :                                 /* '[' or ',' plus space and null terminator and " ]" final string */
    3064           0 :                                 JSON_AGGR_CHECK_NEXT_LENGTH(130 + 6);
    3065           0 :                                 char *dst = buf + buflen;
    3066           0 :                                 if (buflen == 0)
    3067           0 :                                         *dst++ = '[';
    3068             :                                 else
    3069           0 :                                         *dst++ = ',';
    3070           0 :                                 *dst++ = ' ';
    3071           0 :                                 buflen += 2;
    3072           0 :                                 buflen += snprintf(buf + buflen, maxlen - buflen, "%f", val);
    3073             :                         }
    3074             :                         break;
    3075             :                 default:
    3076           0 :                         assert(0);
    3077             :                 }
    3078           0 :                 if (nils) {
    3079           0 :                         strcpy(buf, str_nil);
    3080           0 :                 } else if (buflen == 0) {
    3081           0 :                         strcpy(buf, "[  ]");
    3082             :                 } else {
    3083           0 :                         strcpy(buf + buflen, " ]");
    3084             :                 }
    3085           0 :                 if (bunfastapp_nocheckVAR(bn, buf) != GDK_SUCCEED)
    3086           0 :                         goto bunins_failed;
    3087             :         }
    3088           9 :         bat_iterator_end(&bi);
    3089           9 :         bn->tnil = nils != 0;
    3090           9 :         bn->tnonil = nils == 0;
    3091           9 :         bn->tsorted = BATcount(bn) <= 1;
    3092           9 :         bn->trevsorted = BATcount(bn) <= 1;
    3093           9 :         bn->tkey = BATcount(bn) <= 1;
    3094             : 
    3095           9 :   out:
    3096           9 :         if (bn)
    3097           9 :                 bn->theap->dirty |= BATcount(bn) > 0;
    3098           9 :         BBPreclaim(t2);
    3099           9 :         if (freeb)
    3100           0 :                 BBPunfix(b->batCacheid);
    3101           9 :         if (freeg)
    3102           9 :                 BBPunfix(g->batCacheid);
    3103           9 :         GDKfree(buf);
    3104           9 :         if (err && bn) {
    3105           0 :                 BBPreclaim(bn);
    3106           0 :                 bn = NULL;
    3107             :         }
    3108           9 :         *bnp = bn;
    3109           9 :         return err;
    3110             : 
    3111           0 :   bunins_failed:
    3112           0 :         bat_iterator_end(&bi);
    3113           0 :         if (err == NULL)
    3114           0 :                 err = SQLSTATE(HY013) MAL_MALLOC_FAIL;  /* insertion into result BAT failed */
    3115           0 :         goto out;
    3116             : }
    3117             : 
    3118             : static str
    3119           9 : JSONsubjsoncand(bat *retval, const bat *bid, const bat *gid, const bat *eid,
    3120             :                                 const bat *sid, const bit *skip_nils)
    3121             : {
    3122           9 :         BAT *b, *g, *e, *s, *bn = NULL;
    3123           9 :         const char *err;
    3124             : 
    3125           9 :         b = BATdescriptor(*bid);
    3126           9 :         g = gid ? BATdescriptor(*gid) : NULL;
    3127           9 :         e = eid ? BATdescriptor(*eid) : NULL;
    3128           9 :         s = sid ? BATdescriptor(*sid) : NULL;
    3129           9 :         if (b == NULL || (gid != NULL && g == NULL) || (eid != NULL && e == NULL)
    3130           9 :                 || (sid != NULL && s == NULL)) {
    3131             :                 err = SQLSTATE(HY002) RUNTIME_OBJECT_MISSING;
    3132             :         } else {
    3133           9 :                 err = JSONjsonaggr(&bn, b, g, e, s, *skip_nils);
    3134             :         }
    3135           9 :         BBPreclaim(b);
    3136           9 :         BBPreclaim(g);
    3137           9 :         BBPreclaim(e);
    3138           9 :         BBPreclaim(s);
    3139           9 :         if (err != NULL)
    3140           0 :                 throw(MAL, "aggr.subjson", "%s", err);
    3141             : 
    3142           9 :         *retval = bn->batCacheid;
    3143           9 :         BBPkeepref(bn);
    3144           9 :         return MAL_SUCCEED;
    3145             : }
    3146             : 
    3147             : static str
    3148           9 : JSONsubjson(bat *retval, const bat *bid, const bat *gid, const bat *eid,
    3149             :                         const bit *skip_nils)
    3150             : {
    3151           9 :         return JSONsubjsoncand(retval, bid, gid, eid, NULL, skip_nils);
    3152             : }
    3153             : 
    3154             : 
    3155             : #include "mel.h"
    3156             : static mel_atom json_init_atoms[] = {
    3157             :         { .name="json", .basetype="str", .fromstr=JSONfromString, .tostr=JSONtoString },  { .cmp=NULL },
    3158             : };
    3159             : static mel_func json_init_funcs[] = {
    3160             :  command("json", "new", JSONstr2json, false, "Convert string to its JSON. Dealing with escape characters", args(1,2, arg("",json),arg("j",str))),
    3161             :  command("calc", "json", JSON2json, false, "Convert JSON to JSON", args(1,2, arg("",json),arg("s",json))),
    3162             :  command("calc", "json", JSONstr2json, false, "Convert string to its JSON. Dealing with escape characters", args(1,2, arg("",json),arg("j",str))),
    3163             :  command("json", "str", JSONjson2str, false, "Convert JSON to its string equivalent. Dealing with escape characters", args(1,2, arg("",str),arg("j",json))),
    3164             :  command("json", "text", JSONjson2text, false, "Convert JSON values to their plain string equivalent.", args(1,2, arg("",str),arg("j",json))),
    3165             :  command("json", "text", JSONjson2textSeparator, false, "Convert JSON values to their plain string equivalent, injecting a separator.", args(1,3, arg("",str),arg("j",json),arg("s",str))),
    3166             :  command("json", "number", JSONjson2number, false, "Convert simple JSON values to a double, return nil upon error.", args(1,2, arg("",dbl),arg("j",json))),
    3167             :  command("json", "integer", JSONjson2integer, false, "Convert simple JSON values to an integer, return nil upon error.", args(1,2, arg("",lng),arg("j",json))),
    3168             :  pattern("json", "dump", JSONdump, false, "", args(1,2, batarg("",str),arg("j",json))),
    3169             :  command("json", "filter", JSONfilter, false, "Filter all members of an object by a path expression, returning an array.\nNon-matching elements are skipped.", args(1,3, arg("",json),arg("name",json),arg("pathexpr",str))),
    3170             :  command("json", "filter", JSONfilterArray_bte, false, "", args(1,3, arg("",json),arg("name",json),arg("idx",bte))),
    3171             :  command("json", "filter", JSONfilterArrayDefault_bte, false, "", args(1,4, arg("",json),arg("name",json),arg("idx",bte),arg("other",str))),
    3172             :  command("json", "filter", JSONfilterArray_sht, false, "", args(1,3, arg("",json),arg("name",json),arg("idx",sht))),
    3173             :  command("json", "filter", JSONfilterArrayDefault_sht, false, "", args(1,4, arg("",json),arg("name",json),arg("idx",sht),arg("other",str))),
    3174             :  command("json", "filter", JSONfilterArray_int, false, "", args(1,3, arg("",json),arg("name",json),arg("idx",int))),
    3175             :  command("json", "filter", JSONfilterArrayDefault_int, false, "", args(1,4, arg("",json),arg("name",json),arg("idx",int),arg("other",str))),
    3176             :  command("json", "filter", JSONfilterArray_lng, false, "", args(1,3, arg("",json),arg("name",json),arg("idx",lng))),
    3177             :  command("json", "filter", JSONfilterArrayDefault_lng, false, "Extract a single array element", args(1,4, arg("",json),arg("name",json),arg("idx",lng),arg("other",str))),
    3178             : #ifdef HAVE_HGE
    3179             :  command("json", "filter", JSONfilterArray_hge, false, "", args(1,3, arg("",json),arg("name",json),arg("idx",hge))),
    3180             :  command("json", "filter", JSONfilterArrayDefault_hge, false, "Extract a single array element", args(1,4, arg("",json),arg("name",json),arg("idx",hge),arg("other",str))),
    3181             : #endif
    3182             :  command("json", "isobject", JSONisobject, false, "Validate the string as a valid JSON object", args(1,2, arg("",bit),arg("val",json))),
    3183             :  command("json", "isarray", JSONisarray, false, "Validate the string as a valid JSON array", args(1,2, arg("",bit),arg("val",json))),
    3184             :  command("json", "isvalid", JSONisvalid, false, "Validate the string as a valid JSON document", args(1,2, arg("",bit),arg("val",str))),
    3185             :  command("json", "length", JSONlength, false, "Returns the number of elements in the outermost JSON object.", args(1,2, arg("",int),arg("val",json))),
    3186             :  pattern("json", "unfold", JSONunfold, false, "Expands the outermost JSON object into key-value pairs.", args(2,3, batarg("k",str),batarg("v",json),arg("val",json))),
    3187             :  pattern("json", "unfold", JSONunfold, false, "Expands the outermost JSON object into key-value pairs.", args(3,4, batarg("o",oid),batarg("k",str),batarg("v",json),arg("val",json))),
    3188             :  pattern("json", "fold", JSONfold, false, "Combine the key-value pairs into a single json object list.", args(1,4, arg("",json),batarg("o",oid),batarg("k",str),batargany("v",0))),
    3189             :  pattern("json", "fold", JSONfold, false, "Combine the key-value pairs into a single json object list.", args(1,3, arg("",json),batarg("k",str),batargany("v",0))),
    3190             :  pattern("json", "fold", JSONfold, false, "Combine the value list into a single json array object.", args(1,2, arg("",json),batargany("v",0))),
    3191             :  command("json", "keyarray", JSONkeyArray, false, "Expands the outermost JSON object keys into a JSON value array.", args(1,2, arg("",json),arg("val",json))),
    3192             :  command("json", "valuearray", JSONvalueArray, false, "Expands the outermost JSON object values into a JSON value array.", args(1,2, arg("",json),arg("val",json))),
    3193             :  command("json", "keys", JSONkeyTable, false, "Expands the outermost JSON object names.", args(1,2, batarg("",str),arg("val",json))),
    3194             :  command("json", "values", JSONvalueTable, false, "Expands the outermost JSON values.", args(1,2, batarg("",json),arg("val",json))),
    3195             :  pattern("json", "renderobject", JSONrenderobject, false, "", args(1,2, arg("",json),varargany("val",0))),
    3196             :  pattern("json", "renderarray", JSONrenderarray, false, "", args(1,2, arg("",json),varargany("val",0))),
    3197             :  command("aggr", "jsonaggr", JSONgroupStr, false, "Aggregate the string values to array.", args(1,2, arg("",str),batarg("val",str))),
    3198             :  command("aggr", "jsonaggr", JSONgroupStr, false, "Aggregate the double values to array.", args(1,2, arg("",str),batarg("val",dbl))),
    3199             :  command("aggr", "subjsonaggr", JSONsubjson, false, "Grouped aggregation of values.", args(1,5, batarg("",str),batarg("val",str),batarg("g",oid),batargany("e",1),arg("skip_nils",bit))),
    3200             :  command("aggr", "subjsonaggr", JSONsubjson, false, "Grouped aggregation of values.", args(1,5, batarg("",str),batarg("val",dbl),batarg("g",oid),batargany("e",1),arg("skip_nils",bit))),
    3201             :  command("aggr", "subjsonaggr", JSONsubjsoncand, false, "Grouped aggregation of values with candidates list.", args(1,6, batarg("",str),batarg("val",str),batarg("g",oid),batargany("e",1),batarg("s",oid),arg("skip_nils",bit))),
    3202             :  command("aggr", "subjsonaggr", JSONsubjsoncand, false, "Grouped aggregation of values with candidates list.", args(1,6, batarg("",str),batarg("val",dbl),batarg("g",oid),batargany("e",1),batarg("s",oid),arg("skip_nils",bit))),
    3203             :  { .imp=NULL }
    3204             : };
    3205             : #include "mal_import.h"
    3206             : #ifdef _MSC_VER
    3207             : #undef read
    3208             : #pragma section(".CRT$XCU",read)
    3209             : #endif
    3210         351 : LIB_STARTUP_FUNC(init_json_mal)
    3211         351 : { mal_module2("json", json_init_atoms, json_init_funcs, JSONprelude, NULL); }

Generated by: LCOV version 1.14