UnQL

Check-in [b4ea7f84c7]
Login

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

Overview
Comment:Add support for logical expression operators AND, OR and NOT.
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: b4ea7f84c7215b8de16bc9b00df69bc62f4e784d
User & Date: dan 2011-07-15 16:11:37
Context
2011-07-15
18:14
Add the bitwise expression operators (&, |, <<, >> and ~). Also fix the unary minus operator. check-in: d06f20c02a user: dan tags: trunk
16:11
Add support for logical expression operators AND, OR and NOT. check-in: b4ea7f84c7 user: dan tags: trunk
2011-07-14
19:17
Fix a cut and paste bug in update.c. check-in: 55db23bf44 user: dan tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/expr.c.

176
177
178
179
180
181
182


































183
184
185
186
187
188
189
...
228
229
230
231
232
233
234
235




















236
237
238
239
240
241
242
...
308
309
310
311
312
313
314


315
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
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
    case XJD1_FALSE:
    case XJD1_NULL:
    case XJD1_REAL:
      return 0;
  }
  return 1;
}



































/*
** Allocate a NULL JSON object.
*/
static JsonNode *nullJson(void){
  JsonNode *pRes = xjd1JsonNew(0);
  if( pRes ) pRes->eJType = XJD1_NULL;
................................................................................
    case TK_ID: {
      if( p->pQuery ){
        return xjd1QueryDoc(p->pQuery, p->u.id.zId);
      }else{
        return xjd1StmtDoc(p->pStmt, p->u.id.zId);
      }
    }
  }




















  pRes = xjd1JsonNew(0);
  if( pRes==0 ) return 0;
  pRes->eJType = XJD1_NULL;
  switch( p->eType ){
    case TK_STRUCT: {
      int i;
      JsonStructElem *pElem, **ppPrev;
................................................................................
        pRes->eJType = XJD1_REAL;
      }
      xjd1JsonFree(pJLeft);
      xjd1JsonFree(pJRight);
      break;
    }
    case TK_MINUS: {


      xjd1JsonToReal(pJLeft, &rLeft);
      xjd1JsonToReal(pJRight, &rRight);
      pRes->u.r = rLeft-rRight;
      pRes->eJType = XJD1_REAL;


      break;
    }










    default: {
      pRes->eJType = XJD1_NULL;
      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;
}







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







 







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







 







>
>




>
>


>
>
>
>
>
>
>
>
>
>








<

>
|
|
<




|
|
|
|
<
|
<
<
<
<
<
<
<
<
<
<


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
...
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
...
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396

397
398
399
400

401
402
403
404
405
406
407
408

409










410
411
    case XJD1_FALSE:
    case XJD1_NULL:
    case XJD1_REAL:
      return 0;
  }
  return 1;
}

/*
** Return non-zero if the JSON object passed should be considered TRUE in a 
** boolean context. For example in the result of a WHERE or HAVING clause.
**
** XJD1 uses the same rules as Javascript does to determine which
** values are considered TRUE:
**
**   1. Arrays and objects are always considered true.
**   2. Strings are false if they are zero bytes in length, otherwise true.
**   3. Numbers are false if equal to zero, or true otherwise.
**   4. NULL values are false.
**   5. "true" and "false" are "true" and "false". Respectively.
*/
static int isTrue(const JsonNode *p){
  int res = 0;                    /* Return value */
  switch( p->eJType ){
    case XJD1_REAL: 
      res = p->u.r!=0.0;
      break;

    case XJD1_STRING: 
      res = p->u.z[0]!='\0';
      break;

    case XJD1_ARRAY:
    case XJD1_STRUCT:
    case XJD1_TRUE:
      res = 1;
      break;
  }
  assert( res==1 || res==0 );
  return res;
}

/*
** Allocate a NULL JSON object.
*/
static JsonNode *nullJson(void){
  JsonNode *pRes = xjd1JsonNew(0);
  if( pRes ) pRes->eJType = XJD1_NULL;
................................................................................
    case TK_ID: {
      if( p->pQuery ){
        return xjd1QueryDoc(p->pQuery, p->u.id.zId);
      }else{
        return xjd1StmtDoc(p->pStmt, p->u.id.zId);
      }
    }

    /* The following two logical operators work in the same way as their
    ** javascript counterparts. i.e.
    **
    **    1. "x AND y" is equivalent to "x ? y : x"
    **    2. "x OR y" is equivalent to "x ? x : y"
    */
    case TK_AND:
    case TK_OR: {
      pJLeft = xjd1ExprEval(p->u.bi.pLeft);
      if( isTrue(pJLeft)==(p->eType==TK_OR) ){
        pRes = pJLeft;
      }else{
        xjd1JsonFree(pJLeft);
        pRes = xjd1ExprEval(p->u.bi.pRight);
      }
      return pRes;
    }
  }


  pRes = xjd1JsonNew(0);
  if( pRes==0 ) return 0;
  pRes->eJType = XJD1_NULL;
  switch( p->eType ){
    case TK_STRUCT: {
      int i;
      JsonStructElem *pElem, **ppPrev;
................................................................................
        pRes->eJType = XJD1_REAL;
      }
      xjd1JsonFree(pJLeft);
      xjd1JsonFree(pJRight);
      break;
    }
    case TK_MINUS: {
      pJLeft = xjd1ExprEval(p->u.bi.pLeft);
      pJRight = xjd1ExprEval(p->u.bi.pRight);
      xjd1JsonToReal(pJLeft, &rLeft);
      xjd1JsonToReal(pJRight, &rRight);
      pRes->u.r = rLeft-rRight;
      pRes->eJType = XJD1_REAL;
      xjd1JsonFree(pJLeft);
      xjd1JsonFree(pJRight);
      break;
    }

    case TK_NOT: {
      if( xjd1ExprTrue(p->u.bi.pLeft) ){
        pRes->eJType = XJD1_FALSE;
      }else{
        pRes->eJType = XJD1_TRUE;
      }
      break;
    }

    default: {
      pRes->eJType = XJD1_NULL;
      break;
    }
  }
  return pRes;
}


/*
** Return non-zero if the evaluation of the given expression should be
** considered TRUE in a boolean context. For example in result of a
** WHERE or HAVING clause.

*/
int xjd1ExprTrue(Expr *p){
  int rc = 0;
  JsonNode *pValue = xjd1ExprEval(p);
  if( pValue ){
    rc = isTrue(pValue);
    assert( rc==1 || rc==0 );
    xjd1JsonFree(pValue);

  }










  return rc;
}

Changes to src/xjd1Int.h.

225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
struct Query {
  int eQType;                   /* Query type */
  xjd1_stmt *pStmt;             /* Statement this query is part of */
  Query *pOuter;                /* Next outer query for a subquery */
  union {
    struct {                    /* For compound queries */
      Query *pLeft;               /* Left subquery */
      Query *pRight;              /* Righ subquery */
      int doneLeft;               /* True if left is run to completion */
    } compound;
    struct {                    /* For simple queries */
      Expr *pRes;                 /* Result JSON string */
      DataSrc *pFrom;             /* The FROM clause */
      Expr *pWhere;               /* The WHERE clause */
      ExprList *pGroupBy;         /* The GROUP BY clause */







|







225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
struct Query {
  int eQType;                   /* Query type */
  xjd1_stmt *pStmt;             /* Statement this query is part of */
  Query *pOuter;                /* Next outer query for a subquery */
  union {
    struct {                    /* For compound queries */
      Query *pLeft;               /* Left subquery */
      Query *pRight;              /* Right subquery */
      int doneLeft;               /* True if left is run to completion */
    } compound;
    struct {                    /* For simple queries */
      Expr *pRes;                 /* Result JSON string */
      DataSrc *pFrom;             /* The FROM clause */
      Expr *pWhere;               /* The WHERE clause */
      ExprList *pGroupBy;         /* The GROUP BY clause */

Changes to test/all.test.

1
2
3

-- Run all test scripts
--
.read base01.test




>
1
2
3
4
-- Run all test scripts
--
.read base01.test
.read base02.test

Added test/base02.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
47
48
49
50
51
-- Basic sanity checking.
--
.new t1.db
CREATE COLLECTION c1;
INSERT INTO c1 VALUE { x:true };

.testcase 1
SELECT 1  FROM c1 WHERE TRUE;
SELECT 2  FROM c1 WHERE 1.0;
SELECT 3  FROM c1 WHERE 0.1;
SELECT 4  FROM c1 WHERE -0.1;
SELECT 5  FROM c1 WHERE {a:1};
SELECT 6  FROM c1 WHERE [1];
SELECT 7  FROM c1 WHERE "x";
SELECT 8  FROM c1 WHERE "1";
SELECT 9  FROM c1 WHERE "0";
SELECT 10 FROM c1 WHERE "false";
.result 1 2 3 4 5 6 7 8 9 10

.testcase 2
SELECT 1 FROM c1 WHERE FALSE;
SELECT 2 FROM c1 WHERE NULL;
SELECT 3 FROM c1 WHERE 0.0;
SELECT 4 FROM c1 WHERE "";
.result

.testcase 3
SELECT TRUE AND TRUE FROM c1;
SELECT TRUE AND FALSE FROM c1;
SELECT FALSE AND TRUE FROM c1;
SELECT FALSE AND FALSE FROM c1;
SELECT "cat" AND "dog" FROM c1;
SELECT "" AND "dog" FROM c1;
.result true false false false "dog" "" 

.testcase 4
SELECT TRUE OR TRUE FROM c1;
SELECT TRUE OR FALSE FROM c1;
SELECT FALSE OR TRUE FROM c1;
SELECT FALSE OR FALSE FROM c1;
SELECT "cat" OR "dog" FROM c1;
SELECT "" OR "dog" FROM c1;
.result true true true false "cat" "dog"

.testcase 5
SELECT NOT TRUE  FROM c1;
SELECT NOT FALSE FROM c1;
SELECT NOT "cat" FROM c1;
SELECT NOT "" FROM c1;
.result false true false true