UnQL

Check-in [4eda880431]
Login

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

Overview
Comment:Very simply queries working.
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 4eda880431dd927a7c7dd16d24f70e50f80d81c1
User & Date: drh 2011-06-25 00:07:31
Context
2011-06-25
00:13
Fix memory leaks. check-in: 9ee98afd91 user: drh tags: trunk
00:07
Very simply queries working. check-in: 4eda880431 user: drh tags: trunk
2011-06-24
19:14
Typing in more code. check-in: 9d3d2f9226 user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/datasrc.c.

29
30
31
32
33
34
35








36
37
38
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
      xjd1DataSrcInit(p->u.join.pRight, pQuery);
      break;
    }
    case TK_SELECT: {
      xjd1QueryInit(p->u.subq.q, pQuery->pStmt, pQuery);
      break;
    }








  }
  return XJD1_OK;
}

/*
** Rewind a data source so that it is pointing at the first row.
** Return XJD1_DONE if the data source is empty and XJD1_ROW if
** the rewind results in a row of content being available.
*/
int xjd1DataSrcRewind(DataSrc *p){

  return XJD1_DONE;












}






/*
** 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.
*/






int xjd1DataSrcStep(DataSrc *p){


  return XJD1_DONE;
}

/*
** Return true if the data source is at the end of file
*/
int xjd1DataSrcEOF(DataSrc *p){
................................................................................
  return 1;
}

/*
** The destructor for a Query object.
*/
int xjd1DataSrcClose(DataSrc *p){








  return XJD1_OK;
}







>
>
>
>
>
>
>
>





|

|

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

<
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

|

>
>
>
>
>
>
|
>
>







 







>
>
>
>
>
>
>
>


29
30
31
32
33
34
35
36
37
38
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
108
109
110
111
112
...
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
      xjd1DataSrcInit(p->u.join.pRight, pQuery);
      break;
    }
    case TK_SELECT: {
      xjd1QueryInit(p->u.subq.q, pQuery->pStmt, pQuery);
      break;
    }
    case TK_ID: {
      char *zSql = sqlite3_mprintf("SELECT x FROM \"%.*w\"",
                            p->u.tab.name.n, p->u.tab.name.z);
      sqlite3_prepare_v2(pQuery->pStmt->pConn->db, zSql, -1, 
                         &p->u.tab.pStmt, 0);
      sqlite3_free(zSql);
      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.
*/
int xjd1DataSrcStep(DataSrc *p){
  int rc= XJD1_DONE;
  if( p==0 ) return XJD1_DONE;
  switch( p->eDSType ){
    case TK_ID: {
      rc = sqlite3_step(p->u.tab.pStmt);
      xjd1JsonFree(p->pValue);
      p->pValue = 0;
      if( rc==SQLITE_ROW ){
        const char *zJson = (const char*)sqlite3_column_text(p->u.tab.pStmt, 0);
        p->pValue = xjd1JsonParse(zJson, -1);
        rc = XJD1_ROW;
      }else{
        p->u.tab.eofSeen = 1;
        rc = XJD1_DONE;
      }
      break;
    }
  }
  return rc;
}

/*

** Return the value after the most recent Step.  The returned JsonNode
** is managed by the DataSrc object will be freed by the next call to
** DataSrcStep(), DataSrcRewind(), or DataSrcClose().
*/
JsonNode *xjd1DataSrcValue(DataSrc *p){
  JsonNode *pOut = 0;
  if( p==0 ) return 0;
  switch( p->eDSType ){
    case TK_ID: {
      pOut = p->pValue;
      break;
    }
  }
  return pOut;
}

/*
** Rewind a data source so that it is pointing at the first row.
** Return XJD1_DONE if the data source is empty and XJD1_ROW if
** the rewind results in a row of content being available.
*/
int xjd1DataSrcRewind(DataSrc *p){
  if( p==0 ) return XJD1_DONE;
  xjd1JsonFree(p->pValue);  p->pValue = 0;
  switch( p->eDSType ){
    case TK_ID: {
      sqlite3_reset(p->u.tab.pStmt);
      return xjd1DataSrcStep(p);
    }
  }
  return XJD1_DONE;
}

/*
** Return true if the data source is at the end of file
*/
int xjd1DataSrcEOF(DataSrc *p){
................................................................................
  return 1;
}

/*
** The destructor for a Query object.
*/
int xjd1DataSrcClose(DataSrc *p){
  if( p==0 ) return XJD1_OK;
  xjd1JsonFree(p->pValue);  p->pValue = 0;
  switch( p->eDSType ){
    case TK_ID: {
      sqlite3_finalize(p->u.tab.pStmt);
      break;
    }
  }
  return XJD1_OK;
}

Changes to src/expr.c.

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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
...
144
145
146
147
148
149
150























































































  return rc;
}


/*
** Callback for query expressions
*/
static int initQueryCallback(Expr *p, WalkAction *pAction){
  assert( p );
  assert( p->eType==TK_SELECT );
  return xjd1QueryInit(p->u.q, pAction->pStmt, pAction->pQuery);
}


/*
** Initialize an expression in preparation for evaluation of a
** statement.
*/
int xjd1ExprInit(Expr *p, xjd1_stmt *pStmt, Query *pQuery){
  WalkAction sAction;
  memset(&sAction, 0, sizeof(sAction));
  sAction.xQueryAction = initQueryCallback;
  sAction.pStmt = pStmt;
  sAction.pQuery = pQuery;
  return walkExpr(p, &sAction);
}

/*
** Initialize a list of expression in preparation for evaluation of a
** statement.
*/
int xjd1ExprListInit(ExprList *p, xjd1_stmt *pStmt, Query *pQuery){
  WalkAction sAction;
  memset(&sAction, 0, sizeof(sAction));
  sAction.xQueryAction = initQueryCallback;
  sAction.pStmt = pStmt;
  sAction.pQuery = pQuery;
  return walkExprList(p, &sAction);
}

/*
** Return TRUE if the given expression evaluates to TRUE.
** An empty expression is considered to be TRUE.  A NULL value
** is not TRUE.
*/
int xjd1ExprTrue(Expr *p){
  return 1;
}



/* Walker callback for ExprClose() */
static int walkCloseQueryCallback(Expr *p, WalkAction *pAction){
  assert( p );
  assert( p->eType==TK_SELECT );
  return xjd1QueryClose(p->u.q);
................................................................................
*/
int xjd1ExprListClose(ExprList *p){
  WalkAction sAction;
  memset(&sAction, 0, sizeof(sAction));
  sAction.xQueryAction = walkCloseQueryCallback;
  return walkExprList(p,&sAction);
}






























































































|













|












|




<
<
<
<
<
<
<
<
<
<







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
108
109
110
111
112










113
114
115
116
117
118
119
...
134
135
136
137
138
139
140
141
142
143
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
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
  return rc;
}


/*
** Callback for query expressions
*/
static int walkInitQueryCallback(Expr *p, WalkAction *pAction){
  assert( p );
  assert( p->eType==TK_SELECT );
  return xjd1QueryInit(p->u.q, pAction->pStmt, pAction->pQuery);
}


/*
** Initialize an expression in preparation for evaluation of a
** statement.
*/
int xjd1ExprInit(Expr *p, xjd1_stmt *pStmt, Query *pQuery){
  WalkAction sAction;
  memset(&sAction, 0, sizeof(sAction));
  sAction.xQueryAction = walkInitQueryCallback;
  sAction.pStmt = pStmt;
  sAction.pQuery = pQuery;
  return walkExpr(p, &sAction);
}

/*
** Initialize a list of expression in preparation for evaluation of a
** statement.
*/
int xjd1ExprListInit(ExprList *p, xjd1_stmt *pStmt, Query *pQuery){
  WalkAction sAction;
  memset(&sAction, 0, sizeof(sAction));
  sAction.xQueryAction = walkInitQueryCallback;
  sAction.pStmt = pStmt;
  sAction.pQuery = pQuery;
  return walkExprList(p, &sAction);
}












/* Walker callback for ExprClose() */
static int walkCloseQueryCallback(Expr *p, WalkAction *pAction){
  assert( p );
  assert( p->eType==TK_SELECT );
  return xjd1QueryClose(p->u.q);
................................................................................
*/
int xjd1ExprListClose(ExprList *p){
  WalkAction sAction;
  memset(&sAction, 0, sizeof(sAction));
  sAction.xQueryAction = walkCloseQueryCallback;
  return walkExprList(p,&sAction);
}

/*
** 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){
  JsonNode *pRes;
  pRes = malloc( sizeof(*pRes) );
  if( pRes==0 ) return 0;
  memset(pRes, 0, sizeof(*pRes));
  if( p==0 ){
    pRes->eJType = XJD1_NULL;
    return pRes;
  }
  switch( p->eType ){
    case TK_INTEGER:
    case TK_FLOAT: {
      pRes->u.r = atof(p->u.tk.z);
      pRes->eJType = XJD1_REAL;
      break;
    }
    default:
    case TK_NULL: {
      pRes->eJType = XJD1_NULL;
      break;
    }
    case TK_TRUE: {
      pRes->eJType = XJD1_TRUE;
      break;
    }
    case TK_FALSE: {
      pRes->eJType = XJD1_FALSE;
      break;
    }
    case TK_STRING: {
      pRes->u.z = malloc( p->u.tk.n );
      if( pRes->u.z ){
        int i, j;
        for(i=1, j=0; i<p->u.tk.n-1; i++){
          char c = p->u.tk.z[i];
          if( c=='\\' ){
            c = p->u.tk.z[++i];
            if( c=='n' ){
              c = '\n';
            }else if( c=='t' ){
              c = '\t';
            }
          }
          pRes->u.z[++j] = c;
        }
        pRes->u.z[j] = 0;
      }
      pRes->eJType = XJD1_STRING;
      break;
    }
  }
  return pRes;
}


/*
** Return TRUE if the given expression evaluates to TRUE.
** An empty expression is considered to be TRUE.  A NULL value
** is not TRUE.
*/
int xjd1ExprTrue(Expr *p){
  int rc = 0;
  JsonNode *pValue = xjd1ExprEval(p);
  if( pValue==0 ) return 0;
  switch( pValue->eJType ){
    case XJD1_REAL: {
      rc = pValue->u.r!=0.0;
      break;
    }
    case XJD1_TRUE: {
      rc = 1;
      break;
    }
    case XJD1_STRING: {
      rc = atof(pValue->u.z)!=0.0;
      break;
    }
  }
  xjd1JsonFree(pValue);
  return rc;
}

Changes to src/json.c.

454
455
456
457
458
459
460
461
462
463
464
465
466
467

/*
** Parse up a JSON string
*/
JsonNode *xjd1JsonParse(const char *zIn, int mxIn){
  JsonStr x;
  x.zIn = zIn;
  x.mxIn = mxIn ? mxIn : xjd1Strlen30(zIn);
  x.iCur = 0;
  x.n = 0;
  x.eType = 0;
  tokenNext(&x);
  return parseJson(&x);
}







|






454
455
456
457
458
459
460
461
462
463
464
465
466
467

/*
** Parse up a JSON string
*/
JsonNode *xjd1JsonParse(const char *zIn, int mxIn){
  JsonStr x;
  x.zIn = zIn;
  x.mxIn = mxIn>0 ? mxIn : xjd1Strlen30(zIn);
  x.iCur = 0;
  x.n = 0;
  x.eType = 0;
  tokenNext(&x);
  return parseJson(&x);
}

Changes to src/query.c.

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
  int rc;
  if( p==0 ) return XJD1_DONE;
  if( p->eQType==TK_SELECT ){
    do{
      rc = xjd1DataSrcStep(p->u.simple.pFrom);
    }while(
         rc==XJD1_ROW
      && (p->u.simple.pWhere==0 || !xjd1ExprTrue(p->u.simple.pWhere))
    );
  }else{
    rc = XJD1_ROW;
    if( !p->u.compound.doneLeft ){
      rc = xjd1QueryStep(p->u.compound.pLeft);
      if( rc==XJD1_DONE ) p->u.compound.doneLeft = 1;
    }
    if( p->u.compound.doneLeft ){
      rc = xjd1QueryStep(p->u.compound.pRight);
    }
  }
  return rc;
}






















/* Return true if there are no more rows available on this query */
int xjd1QueryEOF(Query *p){
  int rc;
  if( p->eQType==TK_SELECT ){
    rc = xjd1DataSrcEOF(p->u.simple.pFrom);
  }else{







|













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







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
108
109
110
111
112
113
  int rc;
  if( p==0 ) return XJD1_DONE;
  if( p->eQType==TK_SELECT ){
    do{
      rc = xjd1DataSrcStep(p->u.simple.pFrom);
    }while(
         rc==XJD1_ROW
      && (p->u.simple.pWhere!=0 && !xjd1ExprTrue(p->u.simple.pWhere))
    );
  }else{
    rc = XJD1_ROW;
    if( !p->u.compound.doneLeft ){
      rc = xjd1QueryStep(p->u.compound.pLeft);
      if( rc==XJD1_DONE ) p->u.compound.doneLeft = 1;
    }
    if( p->u.compound.doneLeft ){
      rc = xjd1QueryStep(p->u.compound.pRight);
    }
  }
  return rc;
}

/*
** Return the value from the most recent query step.  
**
** The pointer returned is managed by the query.  It will be freed
** by the next call to QueryStep(), QueryReset(), or QueryClose().
*/
JsonNode *xjd1QueryValue(Query *p){
  JsonNode *pOut = 0;
  if( p ){
    if( p->eQType==TK_SELECT ){
      pOut = xjd1DataSrcValue(p->u.simple.pFrom);
    }else if( !p->u.compound.doneLeft ){
      pOut = xjd1QueryValue(p->u.compound.pLeft);
    }else{
      pOut = xjd1QueryValue(p->u.compound.pRight);
    }
  }
  return pOut;
}


/* Return true if there are no more rows available on this query */
int xjd1QueryEOF(Query *p){
  int rc;
  if( p->eQType==TK_SELECT ){
    rc = xjd1DataSrcEOF(p->u.simple.pFrom);
  }else{

Changes to src/stmt.c.

92
93
94
95
96
97
98

99
100
101
102
103
104
105
...
173
174
175
176
177
178
179

180







181
182
183
184
185
186
187
...
193
194
195
196
197
198
199


200
201
202
203
204
205
206
...
209
210
211
212
213
214
215

216
217
218
219
220
221
222
223
224
    pStmt->pConn->pStmt = pStmt->pNext;
  }
  if( pStmt->pNext ){
    pStmt->pNext->pPrev = pStmt->pPrev;
  }
  xjd1Unref(pStmt->pConn);
  xjd1PoolClear(&pStmt->sPool);

  free(pStmt);
  return XJD1_OK;
}

/*
** Execute a prepared statement up to its next return value or until
** it completes.
................................................................................
        sqlite3_free(zErr);
        rc = XJD1_ERROR;
      }
      sqlite3_free(zSql);
      break;
    }
    case TK_SELECT: {

      rc = xjd1QueryStep(pStmt->pCmd->u.q.pQuery);







      break;
    }
    case TK_PRAGMA: {
      rc = xjd1PragmaStep(pStmt);
      break;
    }
  }
................................................................................
*/
int xjd1_stmt_rewind(xjd1_stmt *pStmt){
  Command *pCmd = pStmt->pCmd;
  if( pCmd ){
    switch( pCmd->eCmdType ){
      case TK_SELECT: {
        xjd1QueryRewind(pCmd->u.q.pQuery);


        break;
      }
      case TK_INSERT: {
        xjd1QueryRewind(pCmd->u.ins.pQuery);
        break;
      }
    }
................................................................................
}

/*
** Return the output value of a prepared statement resulting from
** its most recent xjd1_stmt_step() call.
*/
int xjd1_stmt_value(xjd1_stmt *pStmt, const char **pzValue){

  *pzValue = 0;
  return XJD1_MISUSE;
}

/*
** Construct a human-readable listing of a prepared statement showing
** its internal structure.  Used for debugging and analysis only.
**
** The return string is obtained from malloc and must be freed by







>







 







>
|
>
>
>
>
>
>
>







 







>
>







 







>
|
|







92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
...
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
...
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
...
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
    pStmt->pConn->pStmt = pStmt->pNext;
  }
  if( pStmt->pNext ){
    pStmt->pNext->pPrev = pStmt->pPrev;
  }
  xjd1Unref(pStmt->pConn);
  xjd1PoolClear(&pStmt->sPool);
  xjd1StringClear(&pStmt->retValue);
  free(pStmt);
  return XJD1_OK;
}

/*
** Execute a prepared statement up to its next return value or until
** it completes.
................................................................................
        sqlite3_free(zErr);
        rc = XJD1_ERROR;
      }
      sqlite3_free(zSql);
      break;
    }
    case TK_SELECT: {
      Query *pQuery = pCmd->u.q.pQuery;
      rc = xjd1QueryStep(pQuery);
      xjd1StringClear(&pStmt->retValue);
      if( rc==XJD1_ROW ){
        xjd1JsonRender(&pStmt->retValue, xjd1QueryValue(pQuery));
        pStmt->okValue = 1;
      }else{
        pStmt->okValue = 0;
      }
      break;
    }
    case TK_PRAGMA: {
      rc = xjd1PragmaStep(pStmt);
      break;
    }
  }
................................................................................
*/
int xjd1_stmt_rewind(xjd1_stmt *pStmt){
  Command *pCmd = pStmt->pCmd;
  if( pCmd ){
    switch( pCmd->eCmdType ){
      case TK_SELECT: {
        xjd1QueryRewind(pCmd->u.q.pQuery);
        xjd1StringTruncate(&pStmt->retValue);
        pStmt->okValue = 0;
        break;
      }
      case TK_INSERT: {
        xjd1QueryRewind(pCmd->u.ins.pQuery);
        break;
      }
    }
................................................................................
}

/*
** Return the output value of a prepared statement resulting from
** its most recent xjd1_stmt_step() call.
*/
int xjd1_stmt_value(xjd1_stmt *pStmt, const char **pzValue){
  if( pStmt==0 ) return XJD1_MISUSE;
  *pzValue = pStmt->retValue.zBuf;
  return XJD1_OK;
}

/*
** Construct a human-readable listing of a prepared statement showing
** its internal structure.  Used for debugging and analysis only.
**
** The return string is obtained from malloc and must be freed by

Changes to src/string.c.

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
...
103
104
105
106
107
108
109

110


111
112
113
114
115
116
117
    zNew = realloc(pStr->zBuf, newSize);
    if( zNew==0 ) return 1;
  }
  pStr->nAlloc = newSize;
  pStr->zBuf = zNew;
  return 0;
}











/*
** Append text in z to a string.  If n>=0 then append exactly
** n bytes.  If n<0 then append all of z[] up to the zero terminator.
**
** Return the number of bytes appended.  0 is returned on an OOM
** error.
*/
PRIVATE int xjd1StringAppend(String *pStr, const char *z, int n){
  if( n<0 ) n = xjd1Strlen30(z);
  if( pStr->nUsed + n >= pStr->nAlloc ){
    if( resizeString(pStr, pStr->nAlloc*2 + n + 100) ) return 0;
  }
  if( z ){
    memcpy(&pStr->zBuf[pStr->nUsed], z, n);
    pStr->nUsed += n;
    pStr->zBuf[pStr->nUsed] = 0;
  }
................................................................................
  return pStr;
}

/*
** Free the content of a string but not the String object itself.
*/
PRIVATE void xjd1StringClear(String *pStr){

  if( pStr && pStr->pPool==0 ) free(pStr->zBuf);


}

/*
** Free a string previously allocated using xjd1StringNew().
*/
PRIVATE void xjd1StringDelete(String *pStr){
  if( pStr && pStr->pPool==0 ){







>
>
>
>
>
>
>
>
>
>










|







 







>
|
>
>







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
...
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
    zNew = realloc(pStr->zBuf, newSize);
    if( zNew==0 ) return 1;
  }
  pStr->nAlloc = newSize;
  pStr->zBuf = zNew;
  return 0;
}

/*
** Truncate a string to zero length
*/
void xjd1StringTruncate(String *pStr){
  if( pStr ){
    pStr->nUsed = 0;
    if( pStr->zBuf ) pStr->zBuf[0] = 0;
  }
}

/*
** Append text in z to a string.  If n>=0 then append exactly
** n bytes.  If n<0 then append all of z[] up to the zero terminator.
**
** Return the number of bytes appended.  0 is returned on an OOM
** error.
*/
PRIVATE int xjd1StringAppend(String *pStr, const char *z, int n){
  if( n<0 ) n = xjd1Strlen30(z);
  if( pStr->nUsed + n + 1 >= pStr->nAlloc ){
    if( resizeString(pStr, pStr->nAlloc*2 + n + 100) ) return 0;
  }
  if( z ){
    memcpy(&pStr->zBuf[pStr->nUsed], z, n);
    pStr->nUsed += n;
    pStr->zBuf[pStr->nUsed] = 0;
  }
................................................................................
  return pStr;
}

/*
** Free the content of a string but not the String object itself.
*/
PRIVATE void xjd1StringClear(String *pStr){
  if( pStr ){
    if( pStr->pPool==0 ) free(pStr->zBuf);
    memset(pStr, 0, sizeof(*pStr));
  }
}

/*
** Free a string previously allocated using xjd1StringNew().
*/
PRIVATE void xjd1StringDelete(String *pStr){
  if( pStr && pStr->pPool==0 ){

Changes to src/tokenize.c.

391
392
393
394
395
396
397

398
399
400
401
402
403
404
...
427
428
429
430
431
432
433

434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454

455
  int *pN
){
  int i = 0;
  Parse sParse;
  int tokenType;
  void *pEngine;
  int lastTokenParsed = 0;

  int nErr = 0;
  extern void *xjd1ParserAlloc(void*(*)(size_t));
  extern void xjd1ParserFree(void*, void(*)(void*));
  extern void xjd1Parser(void*,int,Token,Parse*);

#ifndef NDEBUG
  if( pConn->parserTrace ){
................................................................................
        xjd1StringAppendF(&sParse.errMsg,
             "unrecognized token: \"%.*s\"",
             sParse.sTok.n, sParse.sTok.z);
        nErr++;
        goto abort_parse;
      }
      default: {

        xjd1Parser(pEngine, tokenType, sParse.sTok, &sParse);
        lastTokenParsed = tokenType;
        if( sParse.errCode || tokenType==TK_SEMI ) goto abort_parse;
        break;
      }
    }
  }
abort_parse:
  *pN = i;
  if( sParse.errCode ) nErr++;
  if( nErr==0 && sParse.errCode==XJD1_OK ){
    if( lastTokenParsed!=TK_SEMI ){
      sParse.sTok.z = ";";
      sParse.sTok.n = 1;
      xjd1Parser(pEngine, TK_SEMI, sParse.sTok, &sParse);
    }
    xjd1Parser(pEngine, 0, sParse.sTok, &sParse);
  }
  pStmt->pCmd = sParse.pCmd;
  xjd1ParserFree(pEngine, free);
  return nErr==0 && sParse.errCode==0 && sParse.pCmd!=0 ? XJD1_OK : XJD1_ERROR;

}







>







 







>










|









|
>

391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
...
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
  int *pN
){
  int i = 0;
  Parse sParse;
  int tokenType;
  void *pEngine;
  int lastTokenParsed = 0;
  int nToken = 0;
  int nErr = 0;
  extern void *xjd1ParserAlloc(void*(*)(size_t));
  extern void xjd1ParserFree(void*, void(*)(void*));
  extern void xjd1Parser(void*,int,Token,Parse*);

#ifndef NDEBUG
  if( pConn->parserTrace ){
................................................................................
        xjd1StringAppendF(&sParse.errMsg,
             "unrecognized token: \"%.*s\"",
             sParse.sTok.n, sParse.sTok.z);
        nErr++;
        goto abort_parse;
      }
      default: {
        nToken++;
        xjd1Parser(pEngine, tokenType, sParse.sTok, &sParse);
        lastTokenParsed = tokenType;
        if( sParse.errCode || tokenType==TK_SEMI ) goto abort_parse;
        break;
      }
    }
  }
abort_parse:
  *pN = i;
  if( sParse.errCode ) nErr++;
  if( nErr==0 && sParse.errCode==XJD1_OK && nToken>0 ){
    if( lastTokenParsed!=TK_SEMI ){
      sParse.sTok.z = ";";
      sParse.sTok.n = 1;
      xjd1Parser(pEngine, TK_SEMI, sParse.sTok, &sParse);
    }
    xjd1Parser(pEngine, 0, sParse.sTok, &sParse);
  }
  pStmt->pCmd = sParse.pCmd;
  xjd1ParserFree(pEngine, free);
  return nErr==0 && sParse.errCode==0
       && (sParse.pCmd!=0 || nToken==0) ? XJD1_OK : XJD1_ERROR;
}

Changes to src/xjd1Int.h.

103
104
105
106
107
108
109


110
111
112
113
114
115
116
...
196
197
198
199
200
201
202

203
204
205
206
207
208
209
...
286
287
288
289
290
291
292

293
294
295
296
297
298
299
...
316
317
318
319
320
321
322

323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
  xjd1 *pConn;                      /* Database connection */
  xjd1_stmt *pNext, *pPrev;         /* List of all statements */
  Pool sPool;                       /* Memory pool used for parsing */
  int nRef;                         /* Reference count */
  u8 isDying;                       /* True if has been closed */
  char *zCode;                      /* Text of the query */
  Command *pCmd;                    /* Parsed command */


  char *zErrMsg;                    /* Error message */
};

/* A token into to the parser */
struct Token {
  const char *z;                    /* Text of the token */
  int n;                            /* Number of characters */
................................................................................
    struct {                /* For a join.  eDSType==TK_COMMA */
      DataSrc *pLeft;          /* Data source on the left */
      DataSrc *pRight;         /* Data source on the right */
    } join;
    struct {                /* For a named table.  eDSType==TK_ID */
      Token name;              /* The table name */
      sqlite3_stmt *pStmt;     /* Cursor for reading content */

    } tab;
    struct {                /* EACH() or FLATTEN().  eDSType==TK_FLATTENOP */
      DataSrc *pNext;          /* Data source to the left */
      Token opName;            /* "EACH" or "FLATTEN" */
      ExprList *pList;         /* List of arguments */
    } flatten;
    struct {                /* A subquery.  eDSType==TK_SELECT */
................................................................................

/******************************** datasrc.c **********************************/
int xjd1DataSrcInit(DataSrc*,Query*);
int xjd1DataSrcRewind(DataSrc*);
int xjd1DataSrcStep(DataSrc*);
int xjd1DataSrcEOF(DataSrc*);
int xjd1DataSrcClose(DataSrc*);


/******************************** expr.c *************************************/
int xjd1ExprInit(Expr*, xjd1_stmt*, Query*);
int xjd1ExprListInit(ExprList*, xjd1_stmt*, Query*);
int xjd1ExprTrue(Expr*);
int xjd1ExprClose(Expr*);
int xjd1ExprListClose(ExprList*);
................................................................................

/******************************** query.c ************************************/
int xjd1QueryInit(Query*,xjd1_stmt*,Query*);
int xjd1QueryRewind(Query*);
int xjd1QueryStep(Query*);
int xjd1QueryEOF(Query*);
int xjd1QueryClose(Query*);


/******************************** stmt.c *************************************/

/******************************** string.c ***********************************/
int xjd1Strlen30(const char *);
void xjd1StringInit(String*, Pool*, int);
String *xjd1StringNew(Pool*, int);
int xjd1StringAppend(String*, const char*, int);
#define xjd1StringText(S)      ((S)->zBuf)
#define xjd1StringLen(S)       ((S)->nUsed)
#define xjd1StringTruncate(S)  ((S)->nUsed=0)
void xjd1StringClear(String*);
void xjd1StringDelete(String*);
int xjd1StringVAppendF(String*, const char*, va_list);
int xjd1StringAppendF(String*, const char*, ...);


/******************************** tokenize.c *********************************/







>
>







 







>







 







>







 







>










|







103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
...
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
...
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
...
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
  xjd1 *pConn;                      /* Database connection */
  xjd1_stmt *pNext, *pPrev;         /* List of all statements */
  Pool sPool;                       /* Memory pool used for parsing */
  int nRef;                         /* Reference count */
  u8 isDying;                       /* True if has been closed */
  char *zCode;                      /* Text of the query */
  Command *pCmd;                    /* Parsed command */
  int okValue;                      /* True if retValue is valid */
  String retValue;                  /* String rendering of return value */
  char *zErrMsg;                    /* Error message */
};

/* A token into to the parser */
struct Token {
  const char *z;                    /* Text of the token */
  int n;                            /* Number of characters */
................................................................................
    struct {                /* For a join.  eDSType==TK_COMMA */
      DataSrc *pLeft;          /* Data source on the left */
      DataSrc *pRight;         /* Data source on the right */
    } join;
    struct {                /* For a named table.  eDSType==TK_ID */
      Token name;              /* The table 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 */
      Token opName;            /* "EACH" or "FLATTEN" */
      ExprList *pList;         /* List of arguments */
    } flatten;
    struct {                /* A subquery.  eDSType==TK_SELECT */
................................................................................

/******************************** datasrc.c **********************************/
int xjd1DataSrcInit(DataSrc*,Query*);
int xjd1DataSrcRewind(DataSrc*);
int xjd1DataSrcStep(DataSrc*);
int xjd1DataSrcEOF(DataSrc*);
int xjd1DataSrcClose(DataSrc*);
JsonNode *xjd1DataSrcValue(DataSrc*);

/******************************** expr.c *************************************/
int xjd1ExprInit(Expr*, xjd1_stmt*, Query*);
int xjd1ExprListInit(ExprList*, xjd1_stmt*, Query*);
int xjd1ExprTrue(Expr*);
int xjd1ExprClose(Expr*);
int xjd1ExprListClose(ExprList*);
................................................................................

/******************************** query.c ************************************/
int xjd1QueryInit(Query*,xjd1_stmt*,Query*);
int xjd1QueryRewind(Query*);
int xjd1QueryStep(Query*);
int xjd1QueryEOF(Query*);
int xjd1QueryClose(Query*);
JsonNode *xjd1QueryValue(Query*);

/******************************** stmt.c *************************************/

/******************************** string.c ***********************************/
int xjd1Strlen30(const char *);
void xjd1StringInit(String*, Pool*, int);
String *xjd1StringNew(Pool*, int);
int xjd1StringAppend(String*, const char*, int);
#define xjd1StringText(S)      ((S)->zBuf)
#define xjd1StringLen(S)       ((S)->nUsed)
void xjd1StringTruncate(String*);
void xjd1StringClear(String*);
void xjd1StringDelete(String*);
int xjd1StringVAppendF(String*, const char*, va_list);
int xjd1StringAppendF(String*, const char*, ...);


/******************************** tokenize.c *********************************/