UnQL

Check-in [5d36d9f104]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Add partial support for the EACH keyword.
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 5d36d9f104de8aefc3ae1596ce871c83cd70a546
User & Date: dan 2011-07-25 19:26:11
Context
2011-07-26
19:04
Add some support for FLATTEN. check-in: 269c12e5ee user: dan tags: trunk
2011-07-25
19:26
Add partial support for the EACH keyword. check-in: 5d36d9f104 user: dan tags: trunk
13:59
Stubs for the flatten code. check-in: 4c69266688 user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/datasrc.c.

13
14
15
16
17
18
19

20
21
22
23
24

25
26
27
28
29
30
31
..
37
38
39
40
41
42
43


44

45
46
47
48
49
50
51












































52
53
54
55
56
57
58
..
95
96
97
98
99
100
101

102






























103
104



















105
106
107
108
109
110
111
...
173
174
175
176
177
178
179



180
181
182
183
184
185
186
...
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273


274
275
276
277
278
279
280
...
303
304
305
306
307
308
309

310
311
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*************************************************************************
** This file contains code used to implement methods for the DataSrc object.
*/
#include "xjd1Int.h"


/*
** Called after statement parsing to initalize every DataSrc object.
*/
int xjd1DataSrcInit(DataSrc *p, Query *pQuery){

  p->pQuery = pQuery;
  switch( p->eDSType ){
    case TK_COMMA: {
      xjd1DataSrcInit(p->u.join.pLeft, pQuery);
      xjd1DataSrcInit(p->u.join.pRight, pQuery);
      break;
    }
................................................................................
      char *zSql = sqlite3_mprintf("SELECT x FROM \"%w\"", p->u.tab.zName);
      sqlite3_prepare_v2(pQuery->pStmt->pConn->db, zSql, -1, 
                         &p->u.tab.pStmt, 0);
      sqlite3_free(zSql);
      break;
    }
    case TK_FLATTENOP: {


      xjd1DataSrcInit(p->u.flatten.pNext, pQuery);

      break;
    }
    case TK_NULL:                 /* Initializing a NULL DS is a no-op */
      assert( p->u.null.isDone==0 );
      break;
  }
  return XJD1_OK;












































}

/*
** Advance a data source to the next row.
** Return XJD1_DONE if the data source is empty and XJD1_ROW if
** the step results in a row of content being available.
*/
................................................................................
        rc = XJD1_ROW;
      }else{
        p->u.tab.eofSeen = 1;
        rc = XJD1_DONE;
      }
      break;
    }

    case TK_FLATTENOP: {






























      break;
    }



















    case TK_NULL: {
      rc = (p->u.null.isDone ? XJD1_DONE : XJD1_ROW);
      p->u.null.isDone = 1;
      break;
    }
  }
  return rc;
................................................................................
    }
    case TK_ID: {
      sqlite3_reset(p->u.tab.pStmt);
      break;
    }
    case TK_FLATTENOP: {
      xjd1DataSrcRewind(p->u.flatten.pNext);



      break;
    }
    case TK_NULL: {
      p->u.null.isDone = 0;
      break;
    }
  }
................................................................................
}

void xjd1DataSrcCacheSave(DataSrc *p, JsonNode **apNode){
  JsonNode **pp = apNode;
  cacheSaveRecursive(p, &pp);
}

static JsonNode *cacheReadRecursive(
  DataSrc *p, 
  JsonNode ***papNode, 
  const char *zDocname
){
  JsonNode *pRet = 0;
  if( p->eDSType==TK_COMMA ){
    pRet = cacheReadRecursive(p->u.join.pLeft, papNode, zDocname);
    if( !pRet ) pRet = cacheReadRecursive(p->u.join.pRight, papNode, zDocname);
  }else{
    if( zDocname==0 
     || (p->zAs && 0==strcmp(zDocname, p->zAs))
     || (p->zAs==0 && p->eDSType==TK_ID && strcmp(p->u.tab.zName, zDocname)==0)
    ){
      pRet = xjd1JsonRef(**papNode);
    }
    (*papNode)++;
  }
  return pRet;
}

JsonNode *xjd1DataSrcCacheRead(
  DataSrc *p,                     /* The data-source */
  JsonNode **apNode,              /* Array of cached values */
  const char *zDocname            /* The document name to search for */
){
  JsonNode **pp = apNode;
  return cacheReadRecursive(p, &pp, zDocname);
}

static int datasrcResolveRecursive(
  DataSrc *p, 
  int *piEntry, 
  const char *zDocname
){
  int ret = 0;
  if( p->eDSType==TK_COMMA ){
    ret = datasrcResolveRecursive(p->u.join.pLeft, piEntry, zDocname);
    if( 0==ret ){
      ret = datasrcResolveRecursive(p->u.join.pRight, piEntry, zDocname);
    }


  }else{
    if( (p->zAs && 0==strcmp(zDocname, p->zAs))
     || (p->zAs==0 && p->eDSType==TK_ID && strcmp(p->u.tab.zName, zDocname)==0)
    ){
      ret = *piEntry;
    }
    (*piEntry)++;
................................................................................
    }
    (*piEntry)++;
  }
  return pRet;
}
JsonNode *xjd1DataSrcRead(DataSrc *p, int iDoc){
  int iEntry = 1;

  return datasrcReadRecursive(p, &iEntry, iDoc);
}







>





>







 







>
>

>






|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>
>
>







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<











>
>







 







>


13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
..
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
...
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
...
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
...
328
329
330
331
332
333
334






























335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
...
377
378
379
380
381
382
383
384
385
386
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*************************************************************************
** This file contains code used to implement methods for the DataSrc object.
*/
#include "xjd1Int.h"


/*
** Called after statement parsing to initalize every DataSrc object.
*/
int xjd1DataSrcInit(DataSrc *p, Query *pQuery){
  int rc = XJD1_OK;
  p->pQuery = pQuery;
  switch( p->eDSType ){
    case TK_COMMA: {
      xjd1DataSrcInit(p->u.join.pLeft, pQuery);
      xjd1DataSrcInit(p->u.join.pRight, pQuery);
      break;
    }
................................................................................
      char *zSql = sqlite3_mprintf("SELECT x FROM \"%w\"", p->u.tab.zName);
      sqlite3_prepare_v2(pQuery->pStmt->pConn->db, zSql, -1, 
                         &p->u.tab.pStmt, 0);
      sqlite3_free(zSql);
      break;
    }
    case TK_FLATTENOP: {
      const char *zOp = (p->u.flatten.cOpName=='E' ? "EACH" : "FLATTEN");
      ExprItem *pItem = &p->u.flatten.pList->apEItem[0];
      xjd1DataSrcInit(p->u.flatten.pNext, pQuery);
      rc = xjd1FlattenExprInit(pItem->pExpr,p->u.flatten.pNext,&pItem->zAs,zOp);
      break;
    }
    case TK_NULL:                 /* Initializing a NULL DS is a no-op */
      assert( p->u.null.isDone==0 );
      break;
  }
  return rc;
}

static JsonNode *newIntValue(int i){
  JsonNode *pRet = xjd1JsonNew(0);
  pRet->eJType = XJD1_REAL;
  pRet->u.r = (double)i;
  return pRet;
}

static JsonNode *newStringValue(const char *z){
  JsonNode *pRet = xjd1JsonNew(0);
  pRet->eJType = XJD1_STRING;
  pRet->u.z = xjd1PoolDup(0, z, -1);
  return pRet;
}

static JsonNode *flattenedObject(
  JsonNode *pBase,
  JsonNode *pKey,
  JsonNode *pValue,
  const char *zAs
){
  JsonNode *pRet;                 /* Value to return */
  JsonNode *pKV;                  /* Value to return */

  pKV = xjd1JsonNew(0);
  pKV->eJType = XJD1_STRUCT;
  xjd1JsonInsert(pKV, "k", pKey);
  xjd1JsonInsert(pKV, "v", pValue);

  pRet = xjd1JsonEdit(xjd1JsonRef(pBase));
  assert( pRet->nRef==1 && pRet!=pBase );
  xjd1JsonInsert(pRet, zAs, pKV);
  return pRet;
}

static int valueIsEmpty(JsonNode *p){
  if( p && ( 
      (p->eJType==XJD1_ARRAY && p->u.ar.nElem>0)
   || (p->eJType==XJD1_STRUCT && p->u.st.pFirst!=0)
  )){
    return 0;
  }
  return 1;
}

/*
** Advance a data source to the next row.
** Return XJD1_DONE if the data source is empty and XJD1_ROW if
** the step results in a row of content being available.
*/
................................................................................
        rc = XJD1_ROW;
      }else{
        p->u.tab.eofSeen = 1;
        rc = XJD1_DONE;
      }
      break;
    }

    case TK_FLATTENOP: {
      ExprItem *pItem = &p->u.flatten.pList->apEItem[0];

      xjd1JsonFree(p->pValue);
      p->pValue = 0;
      rc = XJD1_ROW;

      while( rc==XJD1_ROW && valueIsEmpty(p->u.flatten.pValue) ){
        rc = xjd1DataSrcStep(p->u.flatten.pNext);
        if( rc==XJD1_ROW ){
          p->u.flatten.pValue = xjd1ExprEval(pItem->pExpr);
          p->u.flatten.iIdx = 0;
        }
      }

      if( rc==XJD1_ROW ){
        JsonNode *pOut = 0;
        JsonNode *pVal = p->u.flatten.pValue;
        JsonNode *pBase = p->u.flatten.pNext->pValue;
        int iIdx = p->u.flatten.iIdx++;
        int bEof = 0;

        switch( pVal->eJType ){
          case XJD1_STRUCT: {
            int i;
            JsonStructElem *pElem = pVal->u.st.pFirst;
            for(i=0; i<iIdx; i++) pElem = pElem->pNext;
            bEof = (pElem->pNext==0);
            pOut = flattenedObject(pBase, newStringValue(pElem->zLabel), 
                xjd1JsonRef(pElem->pValue), pItem->zAs
            );
            break;
          }
          case XJD1_ARRAY: {
            assert( iIdx<pVal->u.ar.nElem );
            bEof = ( (iIdx+1)>=pVal->u.ar.nElem );
            pOut = flattenedObject(pBase, newIntValue(iIdx),
                xjd1JsonRef(pVal->u.ar.apElem[iIdx]), pItem->zAs
            );
            break;
          }
        }

        if( bEof ){
          p->u.flatten.pValue = 0;
          xjd1JsonFree(p->u.flatten.pValue);
        }
        p->pValue = pOut;
      }
      break;
    }

    case TK_NULL: {
      rc = (p->u.null.isDone ? XJD1_DONE : XJD1_ROW);
      p->u.null.isDone = 1;
      break;
    }
  }
  return rc;
................................................................................
    }
    case TK_ID: {
      sqlite3_reset(p->u.tab.pStmt);
      break;
    }
    case TK_FLATTENOP: {
      xjd1DataSrcRewind(p->u.flatten.pNext);
      xjd1JsonFree(p->u.flatten.pValue);
      p->u.flatten.pValue = 0;
      p->u.flatten.iIdx = 0;
      break;
    }
    case TK_NULL: {
      p->u.null.isDone = 0;
      break;
    }
  }
................................................................................
}

void xjd1DataSrcCacheSave(DataSrc *p, JsonNode **apNode){
  JsonNode **pp = apNode;
  cacheSaveRecursive(p, &pp);
}































static int datasrcResolveRecursive(
  DataSrc *p, 
  int *piEntry, 
  const char *zDocname
){
  int ret = 0;
  if( p->eDSType==TK_COMMA ){
    ret = datasrcResolveRecursive(p->u.join.pLeft, piEntry, zDocname);
    if( 0==ret ){
      ret = datasrcResolveRecursive(p->u.join.pRight, piEntry, zDocname);
    }
  }else if( p->eDSType==TK_FLATTENOP ){
    ret = datasrcResolveRecursive(p->u.flatten.pNext, piEntry, zDocname);
  }else{
    if( (p->zAs && 0==strcmp(zDocname, p->zAs))
     || (p->zAs==0 && p->eDSType==TK_ID && strcmp(p->u.tab.zName, zDocname)==0)
    ){
      ret = *piEntry;
    }
    (*piEntry)++;
................................................................................
    }
    (*piEntry)++;
  }
  return pRet;
}
JsonNode *xjd1DataSrcRead(DataSrc *p, int iDoc){
  int iEntry = 1;
  assert( iDoc>=1 );
  return datasrcReadRecursive(p, &iEntry, iDoc);
}

Changes to src/expr.c.

207
208
209
210
211
212
213







































214
215
216
217
218
219
220
...
438
439
440
441
442
443
444

445
446
447
448
449
450
451
...
537
538
539
540
541
542
543





544
545
546
547
548
549
550
551
  assert( pQuery==0 || pQuery->pStmt==pStmt );
  sCtx.pStmt = pStmt;
  sCtx.pQuery = pQuery;
  sCtx.eExpr = eExpr;
  sCtx.pParent = (ResolveCtx *)pCtx;
  return walkExpr(p, walkInitCallback, (void *)&sCtx);
}








































/*
** Initialize a list of expression in preparation for evaluation of a
** statement.
*/
int xjd1ExprListInit(
  ExprList *p,                    /* List of expressions to initialize */
................................................................................
** advance zIn to point to the first byte of the next UTF-8 character.
*/
#define XJD1_SKIP_UTF8(zIn) {                          \
    if( (*(zIn++))>=0xc0 ){                            \
          while( (*zIn & 0xc0)==0x80 ){ zIn++; }       \
    }                                                  \
}


/*
** Evaluate an expression.  Return the result as a JSON object.
**
** The caller must free the returned JSON by a call xjdJsonFree().
*/
JsonNode *xjd1ExprEval(Expr *p){
................................................................................
      xjd1JsonFree(pJLeft);
      xjd1JsonFree(pJRight);
      if( pRes==0 ) pRes = nullJson();
      return pRes;
    }

    case TK_ID: {





      if( p->pQuery ){
        return xjd1QueryDoc(p->u.id.pQuery, p->u.id.iDatasrc);
      }else{
        return xjd1StmtDoc(p->pStmt);
      }
    }

    /* The following two logical operators work in the same way as their







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>







 







>
>
>
>
>
|







207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
...
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
...
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
  assert( pQuery==0 || pQuery->pStmt==pStmt );
  sCtx.pStmt = pStmt;
  sCtx.pQuery = pQuery;
  sCtx.eExpr = eExpr;
  sCtx.pParent = (ResolveCtx *)pCtx;
  return walkExpr(p, walkInitCallback, (void *)&sCtx);
}

typedef struct FlattenCtx FlattenCtx;
struct FlattenCtx {
  DataSrc *pSrc;
  xjd1_stmt *pStmt;
  char *zAs;
  const char *zOp;
};

static int walkInitFlattenCallback(Expr *p, void *pArg){
  FlattenCtx *pCtx = (FlattenCtx *)pArg;
  int rc = XJD1_OK;
  switch( p->eType ){
    case TK_ID:
      p->u.id.pDataSrc = pCtx->pSrc;
      pCtx->zAs = p->u.id.zId;
      break;

    default: {
      const char *zErrMsg = "error in %s expression";
      xjd1StmtError(pCtx->pStmt, XJD1_ERROR, zErrMsg, pCtx->zOp);
      rc = XJD1_ERROR;
      break;
    }
  }
  return rc;
}

int xjd1FlattenExprInit(Expr *p, DataSrc *pSrc, char **pzAs, const char *zOp){
  int rc;
  FlattenCtx sCtx;
  sCtx.pSrc = pSrc;
  sCtx.pStmt = pSrc->pQuery->pStmt;
  sCtx.zAs = 0;
  sCtx.zOp = zOp;
  rc = walkExpr(p, walkInitFlattenCallback, (void *)&sCtx);
  if( *pzAs==0 ) *pzAs = sCtx.zAs;
  return rc;
}

/*
** Initialize a list of expression in preparation for evaluation of a
** statement.
*/
int xjd1ExprListInit(
  ExprList *p,                    /* List of expressions to initialize */
................................................................................
** advance zIn to point to the first byte of the next UTF-8 character.
*/
#define XJD1_SKIP_UTF8(zIn) {                          \
    if( (*(zIn++))>=0xc0 ){                            \
          while( (*zIn & 0xc0)==0x80 ){ zIn++; }       \
    }                                                  \
}


/*
** Evaluate an expression.  Return the result as a JSON object.
**
** The caller must free the returned JSON by a call xjdJsonFree().
*/
JsonNode *xjd1ExprEval(Expr *p){
................................................................................
      xjd1JsonFree(pJLeft);
      xjd1JsonFree(pJRight);
      if( pRes==0 ) pRes = nullJson();
      return pRes;
    }

    case TK_ID: {
      if( p->u.id.pDataSrc ){
        JsonNode *pVal = xjd1DataSrcRead(p->u.id.pDataSrc, 1);
        pRes = getProperty(pVal, p->u.id.zId);
        xjd1JsonFree(pVal);
        return pRes;
      }else if( p->pQuery ){
        return xjd1QueryDoc(p->u.id.pQuery, p->u.id.iDatasrc);
      }else{
        return xjd1StmtDoc(p->pStmt);
      }
    }

    /* The following two logical operators work in the same way as their

Changes to src/json.c.

707
708
709
710
711
712
713
















































  x.mxIn = mxIn>0 ? mxIn : xjd1Strlen30(zIn);
  x.iCur = 0;
  x.n = 0;
  x.eType = 0;
  tokenNext(&x);
  return parseJson(&x);
}























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
  x.mxIn = mxIn>0 ? mxIn : xjd1Strlen30(zIn);
  x.iCur = 0;
  x.n = 0;
  x.eType = 0;
  tokenNext(&x);
  return parseJson(&x);
}

/*
** The JsonNode passed as the first argument must be of type XJD1_STRUCT.
** This function adds a property to the object. The name of the new 
** property is passed as the second argument to this function. The third
** argument is used as the initial value. If the property already exists,
** it is replaced.
**
** This function decrements the reference count of the JsonNode passed
** as the third argument. This means it is possible to do:
**
**     pVal = xjd1JsonNew();            // nRef==1
**     xjd1JsonInsert(p, "x", pVal);    // Object p assumes ownership of pVal
**
** XJD1_OK is returned if the operation is completed successfully. Or
** XJD1_NOMEM if an OOM error is encountered.
*/
int xjd1JsonInsert(JsonNode *p, const char *zLabel, JsonNode *pVal){
  JsonStructElem *pElem;

  assert( p && p->eJType==XJD1_STRUCT && p->nRef==1 );
  for(pElem=p->u.st.pFirst; pElem; pElem=pElem->pNext){
    if( strcmp(zLabel, pElem->zLabel)==0 ) break;
  }
  if( pElem ){
    xjd1_free(pElem->zLabel);
    xjd1JsonFree(pElem->pValue);
  }else{
    pElem = xjd1_malloc(sizeof(*pElem));
    if( !pElem ){
      xjd1JsonFree(pVal);
      return XJD1_NOMEM;
    }

    if( p->u.st.pLast ){
      p->u.st.pLast->pNext = pElem;
    }else{
      p->u.st.pFirst = pElem;
    }
    pElem->pNext = 0;
    p->u.st.pLast = pElem;
  }

  pElem->pValue = pVal;
  pElem->zLabel = xjd1PoolDup(0, zLabel, -1);
  return (pElem->zLabel ? XJD1_OK : XJD1_NOMEM);
}

Changes to src/parse.y.

518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
fromitem(A) ::= ID(X).                           {A = tblDataSrc(p,&X,0);}
fromitem(A) ::= ID(X) AS ID(Y).                  {A = tblDataSrc(p,&X,&Y);}
fromitem(A) ::= LP select(X) RP AS ID(Y).        {A = subqDataSrc(p,X,&Y);}
fromitem(A) ::= fromitem(X) FLATTENOP(Y) LP eachexpr_list(Z) RP.
                                                 {A = flattenDataSrc(p,X,&Y,Z);}

%type eachexpr_list {ExprList*}
eachexpr_list(A) ::= expr(Y).                         {A = apndExpr(p,0,Y,0);}
eachexpr_list(A) ::= expr(Y) AS ID(Z).                {A = apndExpr(p,0,Y,&Z);}
eachexpr_list(A) ::= eachexpr_list(X) COMMA expr(Y).  {A = apndExpr(p,X,Y,0);}
eachexpr_list(A) ::= eachexpr_list(X) COMMA expr(Y) AS ID(Z).
                                                      {A = apndExpr(p,X,Y,&Z);}

%type groupby_opt {GroupByHaving}
groupby_opt(A) ::= .                            {A.pGroupBy=0; A.pHaving=0;}
groupby_opt(A) ::= GROUP BY exprlist(X).        {A.pGroupBy=X; A.pHaving=0;}
groupby_opt(A) ::= GROUP BY exprlist(X) HAVING expr(Y).
                                                {A.pGroupBy=X; A.pHaving=Y;}








|
|
<
<
<







518
519
520
521
522
523
524
525
526



527
528
529
530
531
532
533
fromitem(A) ::= ID(X).                           {A = tblDataSrc(p,&X,0);}
fromitem(A) ::= ID(X) AS ID(Y).                  {A = tblDataSrc(p,&X,&Y);}
fromitem(A) ::= LP select(X) RP AS ID(Y).        {A = subqDataSrc(p,X,&Y);}
fromitem(A) ::= fromitem(X) FLATTENOP(Y) LP eachexpr_list(Z) RP.
                                                 {A = flattenDataSrc(p,X,&Y,Z);}

%type eachexpr_list {ExprList*}
eachexpr_list(A) ::= lvalue(Y).                  {A = apndExpr(p,0,Y,0);}
eachexpr_list(A) ::= lvalue(Y) AS ID(Z).         {A = apndExpr(p,0,Y,&Z);}




%type groupby_opt {GroupByHaving}
groupby_opt(A) ::= .                            {A.pGroupBy=0; A.pHaving=0;}
groupby_opt(A) ::= GROUP BY exprlist(X).        {A.pGroupBy=X; A.pHaving=0;}
groupby_opt(A) ::= GROUP BY exprlist(X) HAVING expr(Y).
                                                {A.pGroupBy=X; A.pHaving=Y;}

Changes to src/xjd1Int.h.

181
182
183
184
185
186
187

188
189
190
191
192
193
194
...
336
337
338
339
340
341
342
343
344


345
346
347
348
349
350
351
...
414
415
416
417
418
419
420

421
422
423
424
425
426
427
...
435
436
437
438
439
440
441

442
443
444
445
446
447
448
      Expr *pLeft;             /* Lvalue or id to the left */
      char *zId;               /* ID to the right */
    } lvalue;
    struct {                /* Identifiers */
      char *zId;               /* token value.  eClass=EXPR_TK */
      int iDatasrc;
      Query *pQuery;

    } id;
    struct {                /* Function calls.  eClass=EXPR_FUNC */
      char *zFName;            /* Name of the function */
      ExprList *args;          /* List of arguments */
      Function *pFunction;     /* Function object */
      JsonNode **apArg;        /* Array to martial function arguments in */
      int iAgg;
................................................................................
    struct {                /* For a named collection.  eDSType==TK_ID */
      char *zName;             /* The collection name */
      sqlite3_stmt *pStmt;     /* Cursor for reading content */
      int eofSeen;             /* True if at EOF */
    } tab;
    struct {                /* EACH() or FLATTEN().  eDSType==TK_FLATTENOP */
      DataSrc *pNext;          /* Data source to the left */
      char cOpName;            /* E or F for "EACH" or "FLATTEN" */
      ExprList *pList;         /* List of arguments */


    } flatten;
    struct {                /* A subquery.  eDSType==TK_SELECT */
      Query *q;                /* The subquery */
    } subq;
    struct {                /* An empty FROM clause.  eDSType==TK_NULL */
      int isDone;              /* True if single row already returned */
    } null;
................................................................................
/******************************** expr.c *************************************/
int xjd1ExprInit(Expr*, xjd1_stmt*, Query*, int, void *);
int xjd1ExprListInit(ExprList*, xjd1_stmt*, Query*, int, void *);
JsonNode *xjd1ExprEval(Expr*);
int xjd1ExprTrue(Expr*);
int xjd1ExprClose(Expr*);
int xjd1ExprListClose(ExprList*);

/* Candidates for the 4th parameter to xjd1ExprInit() */
#define XJD1_EXPR_RESULT  1
#define XJD1_EXPR_WHERE   2
#define XJD1_EXPR_GROUPBY 3
#define XJD1_EXPR_HAVING  4
#define XJD1_EXPR_ORDERBY 5
#define XJD1_EXPR_LIMIT   6
................................................................................
int xjd1JsonToString(const JsonNode*, String*);
int xjd1JsonCompare(const JsonNode*, const JsonNode*);
JsonNode *xjd1JsonNew(Pool*);
JsonNode *xjd1JsonEdit(JsonNode*);
void xjd1JsonFree(JsonNode*);
void xjd1JsonToNull(JsonNode*);
void xjd1DequoteString(char*,int);


/******************************** malloc.c ***********************************/
Pool *xjd1PoolNew(void);
void xjd1PoolClear(Pool*);
void xjd1PoolDelete(Pool*);
void *xjd1PoolMalloc(Pool*, int);
void *xjd1PoolMallocZero(Pool*, int);







>







 







|

>
>







 







>







 







>







181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
...
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
...
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
...
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
      Expr *pLeft;             /* Lvalue or id to the left */
      char *zId;               /* ID to the right */
    } lvalue;
    struct {                /* Identifiers */
      char *zId;               /* token value.  eClass=EXPR_TK */
      int iDatasrc;
      Query *pQuery;
      DataSrc *pDataSrc;       /* Read property from this datasource */
    } id;
    struct {                /* Function calls.  eClass=EXPR_FUNC */
      char *zFName;            /* Name of the function */
      ExprList *args;          /* List of arguments */
      Function *pFunction;     /* Function object */
      JsonNode **apArg;        /* Array to martial function arguments in */
      int iAgg;
................................................................................
    struct {                /* For a named collection.  eDSType==TK_ID */
      char *zName;             /* The collection name */
      sqlite3_stmt *pStmt;     /* Cursor for reading content */
      int eofSeen;             /* True if at EOF */
    } tab;
    struct {                /* EACH() or FLATTEN().  eDSType==TK_FLATTENOP */
      DataSrc *pNext;          /* Data source to the left */
      char cOpName;            /* 'E' or 'F' for "EACH" or "FLATTEN" */
      ExprList *pList;         /* List of arguments */
      JsonNode *pValue;        /* Value to flatten on (or NULL) */
      int iIdx;                /* Index of value field just returned */
    } flatten;
    struct {                /* A subquery.  eDSType==TK_SELECT */
      Query *q;                /* The subquery */
    } subq;
    struct {                /* An empty FROM clause.  eDSType==TK_NULL */
      int isDone;              /* True if single row already returned */
    } null;
................................................................................
/******************************** expr.c *************************************/
int xjd1ExprInit(Expr*, xjd1_stmt*, Query*, int, void *);
int xjd1ExprListInit(ExprList*, xjd1_stmt*, Query*, int, void *);
JsonNode *xjd1ExprEval(Expr*);
int xjd1ExprTrue(Expr*);
int xjd1ExprClose(Expr*);
int xjd1ExprListClose(ExprList*);
int xjd1FlattenExprInit(Expr*, DataSrc *, char **, const char *);
/* Candidates for the 4th parameter to xjd1ExprInit() */
#define XJD1_EXPR_RESULT  1
#define XJD1_EXPR_WHERE   2
#define XJD1_EXPR_GROUPBY 3
#define XJD1_EXPR_HAVING  4
#define XJD1_EXPR_ORDERBY 5
#define XJD1_EXPR_LIMIT   6
................................................................................
int xjd1JsonToString(const JsonNode*, String*);
int xjd1JsonCompare(const JsonNode*, const JsonNode*);
JsonNode *xjd1JsonNew(Pool*);
JsonNode *xjd1JsonEdit(JsonNode*);
void xjd1JsonFree(JsonNode*);
void xjd1JsonToNull(JsonNode*);
void xjd1DequoteString(char*,int);
int xjd1JsonInsert(JsonNode *, const char *, JsonNode *);

/******************************** malloc.c ***********************************/
Pool *xjd1PoolNew(void);
void xjd1PoolClear(Pool*);
void xjd1PoolDelete(Pool*);
void *xjd1PoolMalloc(Pool*, int);
void *xjd1PoolMallocZero(Pool*, int);

Added test/base08.test.





























































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

.new t1.db
CREATE COLLECTION c1;
INSERT INTO c1 VALUE { w:"hi", x:["a",{b:123},456] };

.testcase 1
SELECT FROM c1 EACH(x AS y);
.result {"w":"hi","x":["a",{"b":123},456],"y":{"k":0,"v":"a"}} {"w":"hi","x":["a",{"b":123},456],"y":{"k":1,"v":{"b":123}}} {"w":"hi","x":["a",{"b":123},456],"y":{"k":2,"v":456}}

.testcase 2
CREATE COLLECTION c2;
INSERT INTO c2 VALUE { z:"one", l:[1,2,8] };
INSERT INTO c2 VALUE { z:"two", l:[2,4,6] };
SELECT c2.z FROM c2 EACH(l);
.result "one" "one" "one" "two" "two" "two"

.testcase 3
SELECT c2.l.v AS v FROM c2 EACH(l) ORDER BY v DESC;
.result 8 6 4 2 2 1

.testcase 4
SELECT c2.m.v AS v FROM c2 EACH(l AS m) WHERE c2.z=="one" ORDER BY v%4;
.result 8 1 2

.testcase 5
CREATE COLLECTION c3;
INSERT INTO c3 VALUE {y:0, x:{a:"A",b:"B"}};
SELECT FROM c3 EACH(x);
.result {"y":0,"x":{"k":"a","v":"A"}} {"y":0,"x":{"k":"b","v":"B"}}

.testcase 6
SELECT FROM c3 EACH(x AS y);
.result {"y":{"k":"a","v":"A"},"x":{"a":"A","b":"B"}} {"y":{"k":"b","v":"B"},"x":{"a":"A","b":"B"}}

.testcase 7
SELECT FROM c3 EACH(2 AS y);
.error SYNTAX syntax error near "2"

.testcase 8
SELECT FROM c3 EACH(a.b AS y);
.error ERROR error in EACH expression

.testcase 9
SELECT FROM c3 FLATTEN(a.b AS y);
.error ERROR error in FLATTEN expression