UnQL

Check-in [f19d31ea5d]
Login

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

Overview
Comment:Improve error handling.
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: f19d31ea5d810a88e0ac8d39d5f92c5f10d9bb7a
User & Date: dan 2011-07-20 14:13:36
Context
2011-07-20
19:50
Add support for simple aggregates. Fix a problem with joins. check-in: 514e16d7cb user: dan tags: trunk
14:13
Improve error handling. check-in: f19d31ea5d user: dan tags: trunk
2011-07-19
19:31
Add a scalar length() function to return the length of strings. In preparation for adding aggregation. check-in: a6fb845b35 user: dan tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/conn.c.

81
82
83
84
85
86
87

88
89
90
91
92
93
94
...
116
117
118
119
120
121
122

123
124
125





126



int xjd1_errcode(xjd1 *pConn){
  return pConn ? pConn->errCode : XJD1_ERROR;
}
const char *xjd1_errmsg(xjd1 *pConn){
  if( pConn==0 ) return "out of memory";
  return xjd1StringText(&pConn->errMsg);
}

const char *xjd1_errcode_name(xjd1 *pConn){
  const char *z = "???";
  switch( xjd1_errcode(pConn) ){
    case XJD1_OK:        z = "OK";         break;
    case XJD1_ERROR:     z = "ERROR";      break;
    case XJD1_MISUSE:    z = "MISUSE";     break;
    case XJD1_NOMEM:     z = "NOMEM";      break;
................................................................................
  if( pConn==0 ) return;
  if( !pConn->appendErr ){
    xjd1StringTruncate(&pConn->errMsg);
  }else if( xjd1StringLen(&pConn->errMsg) ){
    xjd1StringAppend(&pConn->errMsg, "\n", 1);
  }
  pConn->errCode = errCode;

  va_start(ap, zFormat);
  xjd1StringVAppendF(&pConn->errMsg, zFormat, ap);
  va_end(ap);





}










>







 







>
|
|
|
>
>
>
>
>
|
>
>
>
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
...
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
int xjd1_errcode(xjd1 *pConn){
  return pConn ? pConn->errCode : XJD1_ERROR;
}
const char *xjd1_errmsg(xjd1 *pConn){
  if( pConn==0 ) return "out of memory";
  return xjd1StringText(&pConn->errMsg);
}

const char *xjd1_errcode_name(xjd1 *pConn){
  const char *z = "???";
  switch( xjd1_errcode(pConn) ){
    case XJD1_OK:        z = "OK";         break;
    case XJD1_ERROR:     z = "ERROR";      break;
    case XJD1_MISUSE:    z = "MISUSE";     break;
    case XJD1_NOMEM:     z = "NOMEM";      break;
................................................................................
  if( pConn==0 ) return;
  if( !pConn->appendErr ){
    xjd1StringTruncate(&pConn->errMsg);
  }else if( xjd1StringLen(&pConn->errMsg) ){
    xjd1StringAppend(&pConn->errMsg, "\n", 1);
  }
  pConn->errCode = errCode;
  if( zFormat ){
    va_start(ap, zFormat);
    xjd1StringVAppendF(&pConn->errMsg, zFormat, ap);
    va_end(ap);
  }else{
    const char *z;
    switch( errCode ){
      case XJD1_NOMEM: z = "out of memory";          break;
      default:         z = xjd1_errcode_name(pConn); break;
    }
    xjd1StringAppend(&pConn->errMsg, z, -1);
  }
}

Changes to src/func.c.

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
  pRet = xjd1JsonNew(0);
  pRet->eJType = XJD1_REAL;
  pRet->u.r = (double)nRet;

  return pRet;
}







int xjd1FunctionInit(Expr *p, xjd1_stmt *pStmt){
  char *zName;
  int nArg;
  int nByte;
  int i;

  static Function aFunc[] = {
    { 1, "length", xLength },
  };



  zName = p->u.func.zFName;
  nArg = p->u.func.args->nEItem;

  for(i=0; i<ArraySize(aFunc); i++){
    Function *pFunc = &aFunc[i];
    if( strcmp(pFunc->zName, zName)==0 && nArg==pFunc->nArg ){
      p->u.func.pFunction = pFunc;
      break;
    }
  }

  if( !p->u.func.pFunction ){

    return XJD1_ERROR;
  }

  nByte = sizeof(JsonNode *) * nArg;
  p->u.func.apArg = (JsonNode **)xjd1PoolMallocZero(&pStmt->sPool, nByte);
  if( !p->u.func.apArg ){
    return XJD1_NOMEM;







>
>
>
>
>
>









>
>













>







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
  pRet = xjd1JsonNew(0);
  pRet->eJType = XJD1_REAL;
  pRet->u.r = (double)nRet;

  return pRet;
}

/*
** The expression passed as the first argument is of type TK_FUNCTION.
** This function initializes the expression object. If successful, XJD1_OK
** is returned. Otherwise, an error code is returned and an error left in
** the pStmt statement handle.
*/
int xjd1FunctionInit(Expr *p, xjd1_stmt *pStmt){
  char *zName;
  int nArg;
  int nByte;
  int i;

  static Function aFunc[] = {
    { 1, "length", xLength },
  };

  assert( p->eType==TK_FUNCTION && p->eClass==XJD1_EXPR_FUNC );

  zName = p->u.func.zFName;
  nArg = p->u.func.args->nEItem;

  for(i=0; i<ArraySize(aFunc); i++){
    Function *pFunc = &aFunc[i];
    if( strcmp(pFunc->zName, zName)==0 && nArg==pFunc->nArg ){
      p->u.func.pFunction = pFunc;
      break;
    }
  }

  if( !p->u.func.pFunction ){
    xjd1StmtError(pStmt, XJD1_ERROR, "no such function: %s", zName);
    return XJD1_ERROR;
  }

  nByte = sizeof(JsonNode *) * nArg;
  p->u.func.apArg = (JsonNode **)xjd1PoolMallocZero(&pStmt->sPool, nByte);
  if( !p->u.func.apArg ){
    return XJD1_NOMEM;

Changes to src/parse.y.

31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

// The name of the generated procedure that implements the parser
// is as follows:
%name xjd1Parser

// Handle syntax errors.
%syntax_error {
  p->errCode = XJD1_SYNTAX;
  xjd1Error(p->pConn, XJD1_SYNTAX, "syntax error near \"%.*s\"",
            p->sTok.n, p->sTok.z);
}

// The following text is included near the beginning of the C source
// code file that implements the parser.
//
%include {
#include "xjd1Int.h"







|
|
|







31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

// The name of the generated procedure that implements the parser
// is as follows:
%name xjd1Parser

// Handle syntax errors.
%syntax_error {
  xjd1ParseError(p, XJD1_SYNTAX,
      "syntax error near \"%.*s\"", p->sTok.n, p->sTok.z
  );
}

// The following text is included near the beginning of the C source
// code file that implements the parser.
//
%include {
#include "xjd1Int.h"

Changes to src/shell.c.

99
100
101
102
103
104
105

106
107
108
109
110
111
112
...
185
186
187
188
189
190
191

192
193
194
195
196
197
198
...
392
393
394
395
396
397
398









399
400
401
402
403
404
405
...
447
448
449
450
451
452
453







454
455
456
457
458
459
460
...
513
514
515
516
517
518
519
520
521
522
523


524
525
526

527
528
529
530
531
532
533
534
535
536



537
538
539
540
541
542
543
...
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
struct Shell {
  xjd1 *pDb;           /* Open database connection */
  const char *zFile;   /* Current filename */
  int nLine;           /* Current line number */
  FILE *pIn;           /* Input file */
  int isTTY;           /* True if pIn is a TTY */
  int shellFlags;      /* Flag settings */

  String testOut;      /* Output from a test case */
  char zModule[50];    /* Mame of current test module */
  char zTestCase[50];  /* Name of current testcase */
  String inBuf;        /* Buffered input text */
  int nCase;           /* Number of --testcase commands seen */
  int nTest;           /* Number of tests performed */
  int nErr;            /* Number of test errors */
................................................................................
static int shellTestcase(Shell *p, int argc, char **argv){
  if( argc>=2 ){
    int n = strlen(argv[1]);
    if( n>=sizeof(p->zTestCase) ) n = sizeof(p->zTestCase)-1;
    memcpy(p->zTestCase, argv[1], n);
    p->zTestCase[n] = 0;
    xjd1StringTruncate(&p->testOut);

    p->shellFlags |= SHELL_TEST_MODE;
  }
  return 0;
}

/*
** Return non-zero if string z matches glob pattern zGlob and zero if the
................................................................................
/*
** Command:  .puts TEXT
*/
static int shellPuts(Shell *p, int argc, char **argv){
  if( argc>=2 ) printf("%s\n", argv[1]);
  return 0;
}










/*
** Process a command intended for this shell - not for the database.
**
** Return 1 to abort or 0 to continue.
*/
static int processMetaCommand(Shell *p){
................................................................................
    while( shellIsSpace(z[0]) ) z++;
    azArg[1] = z;
    nArg = 2;
  }else{
    azArg[1] = 0;
    nArg = 1;
  }








  /* Find the command */
  for(i=0; i<sizeof(cmds)/sizeof(cmds[0]); i++){
    if( strcmp(azArg[0], cmds[i].zCmd)==0 ){
      rc = cmds[i].xCmd(p, nArg, azArg);
      break;
    }
................................................................................
        }else{
          appendTestOut(p, zValue, -1);
        }
      }
    }while( rc==XJD1_ROW );
    xjd1_stmt_delete(pStmt);
  }else{
    if( p->zTestCase[0] ){
      appendTestOut(p, xjd1_errcode_name(p->pDb), -1);
      appendTestOut(p, xjd1_errmsg(p->pDb), -1);
    }


    fprintf(stderr, "%s:%d: ERROR: %s\n",
            p->zFile, p->nLine, xjd1_errmsg(p->pDb));
    p->nErr++;

  }
  if( once ) printf("---------------------------------\n");
}

/*
** Process one or more database commands
*/
static void processScript(Shell *p){
  char *z, c;
  int i;



  if( p->pDb==0 ){
    if( p->shellFlags & SHELL_ECHO ) printf("%s", xjd1StringText(&p->inBuf));
    xjd1StringTruncate(&p->inBuf);
    return;
  }
  while( xjd1StringLen(&p->inBuf) ){
    z = xjd1StringText(&p->inBuf);
................................................................................
    fclose(p->pIn);
  }
  p->zFile = savedState.zFile;
  p->nLine = savedState.nLine;
  p->pIn = savedState.pIn;
  p->isTTY = savedState.isTTY;
}


int main(int argc, char **argv){
  int i;
  Shell s;

  memset(&s, 0, sizeof(s));
  if( argc>1 ){







>







 







>







 







>
>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>







 







|


<
>
>
|
|
|
>










>
>
>







 







<







99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
...
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
...
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
...
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
...
531
532
533
534
535
536
537
538
539
540

541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
...
646
647
648
649
650
651
652

653
654
655
656
657
658
659
struct Shell {
  xjd1 *pDb;           /* Open database connection */
  const char *zFile;   /* Current filename */
  int nLine;           /* Current line number */
  FILE *pIn;           /* Input file */
  int isTTY;           /* True if pIn is a TTY */
  int shellFlags;      /* Flag settings */
  int testErrcode;     /* Test case error code */
  String testOut;      /* Output from a test case */
  char zModule[50];    /* Mame of current test module */
  char zTestCase[50];  /* Name of current testcase */
  String inBuf;        /* Buffered input text */
  int nCase;           /* Number of --testcase commands seen */
  int nTest;           /* Number of tests performed */
  int nErr;            /* Number of test errors */
................................................................................
static int shellTestcase(Shell *p, int argc, char **argv){
  if( argc>=2 ){
    int n = strlen(argv[1]);
    if( n>=sizeof(p->zTestCase) ) n = sizeof(p->zTestCase)-1;
    memcpy(p->zTestCase, argv[1], n);
    p->zTestCase[n] = 0;
    xjd1StringTruncate(&p->testOut);
    p->testErrcode = XJD1_OK;
    p->shellFlags |= SHELL_TEST_MODE;
  }
  return 0;
}

/*
** Return non-zero if string z matches glob pattern zGlob and zero if the
................................................................................
/*
** Command:  .puts TEXT
*/
static int shellPuts(Shell *p, int argc, char **argv){
  if( argc>=2 ) printf("%s\n", argv[1]);
  return 0;
}

static void checkForTestError(Shell *p){
  if( (p->shellFlags & SHELL_TEST_MODE) && p->testErrcode ){
    fprintf(stderr, "%s:%d: ERROR: %s\n", p->zFile, p->nLine, p->testOut.zBuf);
    xjd1StringTruncate(&p->testOut);
    p->testErrcode = XJD1_OK;
    p->nErr++;
  }
}

/*
** Process a command intended for this shell - not for the database.
**
** Return 1 to abort or 0 to continue.
*/
static int processMetaCommand(Shell *p){
................................................................................
    while( shellIsSpace(z[0]) ) z++;
    azArg[1] = z;
    nArg = 2;
  }else{
    azArg[1] = 0;
    nArg = 1;
  }

  if( strcmp(azArg[0], "error") ){
    checkForTestError(p);
  }else{
    p->testErrcode = XJD1_OK;
    azArg[0] = "result";
  }

  /* Find the command */
  for(i=0; i<sizeof(cmds)/sizeof(cmds[0]); i++){
    if( strcmp(azArg[0], cmds[i].zCmd)==0 ){
      rc = cmds[i].xCmd(p, nArg, azArg);
      break;
    }
................................................................................
        }else{
          appendTestOut(p, zValue, -1);
        }
      }
    }while( rc==XJD1_ROW );
    xjd1_stmt_delete(pStmt);
  }else{
    if( p->shellFlags & SHELL_TEST_MODE ){
      appendTestOut(p, xjd1_errcode_name(p->pDb), -1);
      appendTestOut(p, xjd1_errmsg(p->pDb), -1);

      p->testErrcode = rc;
    }else{
      fprintf(stderr, "%s:%d: ERROR: %s\n",
              p->zFile, p->nLine, xjd1_errmsg(p->pDb));
      p->nErr++;
    }
  }
  if( once ) printf("---------------------------------\n");
}

/*
** Process one or more database commands
*/
static void processScript(Shell *p){
  char *z, c;
  int i;

  checkForTestError(p);

  if( p->pDb==0 ){
    if( p->shellFlags & SHELL_ECHO ) printf("%s", xjd1StringText(&p->inBuf));
    xjd1StringTruncate(&p->inBuf);
    return;
  }
  while( xjd1StringLen(&p->inBuf) ){
    z = xjd1StringText(&p->inBuf);
................................................................................
    fclose(p->pIn);
  }
  p->zFile = savedState.zFile;
  p->nLine = savedState.nLine;
  p->pIn = savedState.pIn;
  p->isTTY = savedState.isTTY;
}


int main(int argc, char **argv){
  int i;
  Shell s;

  memset(&s, 0, sizeof(s));
  if( argc>1 ){

Changes to src/stmt.c.

25
26
27
28
29
30
31

32
33
34
35
36
37
38
39
40


41
42


43
44
45
46
47
48
49
..
57
58
59
60
61
62
63







64
65
66
67
68
69
70
71
72
73
74
...
296
297
298
299
300
301
302









*/
int xjd1_stmt_new(xjd1 *pConn, const char *zStmt, xjd1_stmt **ppNew, int *pN){
  xjd1_stmt *p;
  int dummy;
  Command *pCmd;
  int rc;


  *pN = strlen(zStmt);
  *ppNew = p = malloc( sizeof(*p) );
  if( p==0 ) return XJD1_NOMEM;
  memset(p, 0, sizeof(*p));
  p->pConn = pConn;
  p->pNext = pConn->pStmt;
  pConn->pStmt = p;
  p->zCode = xjd1PoolDup(&p->sPool, zStmt, -1);
  if( pN==0 ) pN = &dummy;


  rc = xjd1RunParser(pConn, p, p->zCode, pN);
  pCmd = p->pCmd;


  if( pCmd ){
    switch( pCmd->eCmdType ){
      case TK_SELECT: {
        rc = xjd1QueryInit(pCmd->u.q.pQuery, p, 0);
        break;
      }
      case TK_INSERT: {
................................................................................
      case TK_UPDATE: {
        xjd1ExprInit(pCmd->u.update.pWhere, p, 0);
        xjd1ExprListInit(pCmd->u.update.pChng, p, 0);
        xjd1ExprInit(pCmd->u.update.pUpsert, p, 0);
        break;
      }
    }







  }

  if( rc!=XJD1_OK ){
    xjd1Error(pConn, rc, "");
    xjd1_stmt_delete(p);
    *ppNew = 0;
  }
  return rc;
}

/*
................................................................................
    case TK_DELETE: {
      pRes = xjd1JsonRef(pStmt->pDoc);
      break;
    }
  }
  return pRes;
}
















>








|
>
>


>
>







 







>
>
>
>
>
>
>



<







 







>
>
>
>
>
>
>
>
>
25
26
27
28
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
..
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78

79
80
81
82
83
84
85
...
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
*/
int xjd1_stmt_new(xjd1 *pConn, const char *zStmt, xjd1_stmt **ppNew, int *pN){
  xjd1_stmt *p;
  int dummy;
  Command *pCmd;
  int rc;

  if( pN==0 ) pN = &dummy;
  *pN = strlen(zStmt);
  *ppNew = p = malloc( sizeof(*p) );
  if( p==0 ) return XJD1_NOMEM;
  memset(p, 0, sizeof(*p));
  p->pConn = pConn;
  p->pNext = pConn->pStmt;
  pConn->pStmt = p;
  p->zCode = xjd1PoolDup(&p->sPool, zStmt, -1);
  xjd1StringInit(&p->retValue, &p->sPool, 0);
  xjd1StringInit(&p->errMsg, &p->sPool, 0);

  rc = xjd1RunParser(pConn, p, p->zCode, pN);
  pCmd = p->pCmd;
  assert( rc==XJD1_OK || (pCmd==0 && pConn->errCode==rc) );

  if( pCmd ){
    switch( pCmd->eCmdType ){
      case TK_SELECT: {
        rc = xjd1QueryInit(pCmd->u.q.pQuery, p, 0);
        break;
      }
      case TK_INSERT: {
................................................................................
      case TK_UPDATE: {
        xjd1ExprInit(pCmd->u.update.pWhere, p, 0);
        xjd1ExprListInit(pCmd->u.update.pChng, p, 0);
        xjd1ExprInit(pCmd->u.update.pUpsert, p, 0);
        break;
      }
    }

    if( p->errCode ){
      xjd1Error(pConn, p->errCode, "%s", p->errMsg.zBuf);
      rc = p->errCode;
    }else if( rc!=XJD1_OK ){
      xjd1Error(pConn, rc, 0);
    }
  }

  if( rc!=XJD1_OK ){

    xjd1_stmt_delete(p);
    *ppNew = 0;
  }
  return rc;
}

/*
................................................................................
    case TK_DELETE: {
      pRes = xjd1JsonRef(pStmt->pDoc);
      break;
    }
  }
  return pRes;
}

void xjd1StmtError(xjd1_stmt *pStmt, int errCode, const char *zFormat, ...){
  va_list ap;
  pStmt->errCode = errCode;
  va_start(ap, zFormat);
  xjd1StringVAppendF(&pStmt->errMsg, zFormat, ap);
  va_end(ap);
}

Changes to src/tokenize.c.

394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412

413
414
415





416
417
418
419
420
421
422
423
424
425
426
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
456
457

458
459
460

















){
  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 ){
    extern void xjd1ParserTrace(FILE*, char*);
    xjd1ParserTrace(stdout, "parser> ");
  }
#endif
  *pN = 0;

  pEngine = xjd1ParserAlloc(malloc);
  if( pEngine==0 ) return XJD1_NOMEM;
  memset(&sParse, 0, sizeof(sParse));





  sParse.pConn = pConn;
  sParse.pPool = &pStmt->sPool;
  xjd1StringInit(&sParse.errMsg, &pStmt->sPool, 0);
  while( zCode[i] && sParse.errCode==0 ){
    assert( i>=0 );
    sParse.sTok.z = &zCode[i];
    sParse.sTok.n = xjd1GetToken((unsigned char*)&zCode[i], &tokenType);
    i += sParse.sTok.n;
    switch( tokenType ){
      case TK_SPACE: {
        break;
      }
      case TK_ILLEGAL: {
        xjd1StringTruncate(&sParse.errMsg);
        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;
}
























<










<
>

<

>
>
>
>
>













|
<
|
<
<
>











>

<
<
|







<

>
|
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
394
395
396
397
398
399
400

401
402
403
404
405
406
407
408
409
410

411
412

413
414
415
416
417
418
419
420
421
422
423
424
425
426
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

456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
){
  int i = 0;
  Parse sParse;
  int tokenType;
  void *pEngine;
  int lastTokenParsed = 0;
  int nToken = 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 ){
    extern void xjd1ParserTrace(FILE*, char*);
    xjd1ParserTrace(stdout, "parser> ");
  }
#endif


  pEngine = xjd1ParserAlloc(malloc);

  memset(&sParse, 0, sizeof(sParse));
  if( pEngine==0 ){
    sParse.errCode = XJD1_NOMEM;
    goto abort_parse;
  }

  sParse.pConn = pConn;
  sParse.pPool = &pStmt->sPool;
  xjd1StringInit(&sParse.errMsg, &pStmt->sPool, 0);
  while( zCode[i] && sParse.errCode==0 ){
    assert( i>=0 );
    sParse.sTok.z = &zCode[i];
    sParse.sTok.n = xjd1GetToken((unsigned char*)&zCode[i], &tokenType);
    i += sParse.sTok.n;
    switch( tokenType ){
      case TK_SPACE: {
        break;
      }
      case TK_ILLEGAL: {
        xjd1ParseError(&sParse, XJD1_ERROR,

            "unrecognized token: \"%.*s\"", sParse.sTok.n, sParse.sTok.z


        );
        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:


  if( 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);
  }

  xjd1ParserFree(pEngine, free);

  if( sParse.errCode!=XJD1_OK ){
    xjd1Error(pConn, sParse.errCode, "%s", sParse.errMsg.zBuf);
  }
  pStmt->pCmd = sParse.pCmd;
  *pN = i;

  return sParse.errCode;
}

/*
** Set the error code and message for the Parse object passed as the
** first argument.
*/
void xjd1ParseError(Parse *p, int errCode, const char *zFormat, ...){
  va_list ap;
  p->errCode = errCode;
  va_start(ap, zFormat);
  xjd1StringVAppendF(&p->errMsg, zFormat, ap);
  va_end(ap);
}

Changes to src/xjd1Int.h.

111
112
113
114
115
116
117


118
119
120
121
122
123
124
125
...
388
389
390
391
392
393
394

395
396
397
398
399
400
401
...
414
415
416
417
418
419
420

421
422
423
424
425
426
427
  int nRef;                         /* Reference count */
  u8 isDying;                       /* True if has been closed */
  char *zCode;                      /* Text of the query */
  Command *pCmd;                    /* Parsed command */
  JsonNode *pDoc;                   /* Current document */
  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 */
};
................................................................................
int xjd1QueryRewind(Query*);
int xjd1QueryStep(Query*);
int xjd1QueryClose(Query*);
JsonNode *xjd1QueryDoc(Query*, const char*);

/******************************** stmt.c *************************************/
JsonNode *xjd1StmtDoc(xjd1_stmt*, const char*);


/******************************** string.c ***********************************/
int xjd1Strlen30(const char *);
void xjd1StringInit(String*, Pool*, int);
String *xjd1StringNew(Pool*, int);
char *xjd1StringGet(String*);
int xjd1StringAppend(String*, const char*, int);
................................................................................
#define xjd1Isspace(x)   (xjd1CtypeMap[(unsigned char)(x)]&0x01)
#define xjd1Isalnum(x)   (xjd1CtypeMap[(unsigned char)(x)]&0x06)
#define xjd1Isalpha(x)   (xjd1CtypeMap[(unsigned char)(x)]&0x02)
#define xjd1Isdigit(x)   (xjd1CtypeMap[(unsigned char)(x)]&0x04)
#define xjd1Isxdigit(x)  (xjd1CtypeMap[(unsigned char)(x)]&0x08)
#define xjd1Isident(x)   (xjd1CtypeMap[(unsigned char)(x)]&0x46)
int xjd1RunParser(xjd1*, xjd1_stmt*, const char*, int*);


/******************************** trace.c ************************************/
const char *xjd1TokenName(int);
void xjd1TraceCommand(String*,int,const Command*);
void xjd1TraceQuery(String*,int,const Query*);
void xjd1TraceDataSrc(String*,int,const DataSrc*);
void xjd1TraceExpr(String*,const Expr*);







>
>
|







 







>







 







>







111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
...
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
...
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
  int nRef;                         /* Reference count */
  u8 isDying;                       /* True if has been closed */
  char *zCode;                      /* Text of the query */
  Command *pCmd;                    /* Parsed command */
  JsonNode *pDoc;                   /* Current document */
  int okValue;                      /* True if retValue is valid */
  String retValue;                  /* String rendering of return value */

  int errCode;                      /* Error code */
  String errMsg;                    /* Error message */
};

/* A token into to the parser */
struct Token {
  const char *z;                    /* Text of the token */
  int n;                            /* Number of characters */
};
................................................................................
int xjd1QueryRewind(Query*);
int xjd1QueryStep(Query*);
int xjd1QueryClose(Query*);
JsonNode *xjd1QueryDoc(Query*, const char*);

/******************************** stmt.c *************************************/
JsonNode *xjd1StmtDoc(xjd1_stmt*, const char*);
void xjd1StmtError(xjd1_stmt *,int,const char*,...);

/******************************** string.c ***********************************/
int xjd1Strlen30(const char *);
void xjd1StringInit(String*, Pool*, int);
String *xjd1StringNew(Pool*, int);
char *xjd1StringGet(String*);
int xjd1StringAppend(String*, const char*, int);
................................................................................
#define xjd1Isspace(x)   (xjd1CtypeMap[(unsigned char)(x)]&0x01)
#define xjd1Isalnum(x)   (xjd1CtypeMap[(unsigned char)(x)]&0x06)
#define xjd1Isalpha(x)   (xjd1CtypeMap[(unsigned char)(x)]&0x02)
#define xjd1Isdigit(x)   (xjd1CtypeMap[(unsigned char)(x)]&0x04)
#define xjd1Isxdigit(x)  (xjd1CtypeMap[(unsigned char)(x)]&0x08)
#define xjd1Isident(x)   (xjd1CtypeMap[(unsigned char)(x)]&0x46)
int xjd1RunParser(xjd1*, xjd1_stmt*, const char*, int*);
void xjd1ParseError(Parse *, int, const char *, ...);

/******************************** trace.c ************************************/
const char *xjd1TokenName(int);
void xjd1TraceCommand(String*,int,const Command*);
void xjd1TraceQuery(String*,int,const Query*);
void xjd1TraceDataSrc(String*,int,const DataSrc*);
void xjd1TraceExpr(String*,const Expr*);

Changes to test/base05.test.

57
58
59
60
61
62
63
64
65
66
67
68
69
70
SELECT length("hello world");
SELECT length(11);
SELECT length(true);
SELECT length(false);
SELECT length(null);
.result 11 2 4 5 4

-- .testcase 10
-- SELECT xyz("hello world");
-- .result ERROR











|
|
|




57
58
59
60
61
62
63
64
65
66
67
68
69
70
SELECT length("hello world");
SELECT length(11);
SELECT length(true);
SELECT length(false);
SELECT length(null);
.result 11 2 4 5 4

.testcase 10
SELECT xyz("hello world");
.error ERROR no such function: xyz