Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Further progress on using decimal arithmetic. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | sqlite4-num |
Files: | files | file ages | folders |
SHA1: |
f875ba19445081b7001599e43facb073 |
User & Date: | dan 2013-05-25 20:13:47.089 |
Context
2013-05-28
| ||
20:33 | Further progress on decimal arithmetic. check-in: b55b217f6a user: dan tags: sqlite4-num | |
2013-05-25
| ||
20:13 | Further progress on using decimal arithmetic. check-in: f875ba1944 user: dan tags: sqlite4-num | |
16:41 | Fix some bugs in the code that uses sqlite4_num. check-in: 598f3f02f4 user: dan tags: sqlite4-num | |
Changes
Changes to src/expr.c.
︙ | ︙ | |||
1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 | ** ** The z[] string will probably not be zero-terminated. But the ** z[n] character is guaranteed to be something that does not look ** like the continuation of the number. */ static void codeReal(Vdbe *v, const char *z, int negateFlag, int iMem){ if( ALWAYS(z!=0) ){ double value; char *zV; sqlite4AtoF(z, &value, sqlite4Strlen30(z), SQLITE4_UTF8); assert( !sqlite4IsNaN(value) ); /* The new AtoF never returns NaN */ if( negateFlag ) value = -value; zV = dup8bytes(v, (char*)&value); sqlite4VdbeAddOp4(v, OP_Real, 0, iMem, 0, zV, P4_REAL); } } #endif /* ** Generate an instruction that will put the integer describe by ** text z[0..n-1] into register iMem. ** ** Expr.u.zToken is always UTF8 and zero-terminated. */ static void codeInteger(Parse *pParse, Expr *pExpr, int negFlag, int iMem){ Vdbe *v = pParse->pVdbe; if( pExpr->flags & EP_IntValue ){ int i = pExpr->u.iValue; assert( i>=0 ); if( negFlag ) i = -i; sqlite4VdbeAddOp2(v, OP_Integer, i, iMem); }else{ int c; i64 value; const char *z = pExpr->u.zToken; assert( z!=0 ); c = sqlite4Atoi64(z, &value, sqlite4Strlen30(z), SQLITE4_UTF8); if( c==0 || (c==2 && negFlag) ){ char *zV; if( negFlag ){ value = c==2 ? SMALLEST_INT64 : -value; } zV = dup8bytes(v, (char*)&value); sqlite4VdbeAddOp4(v, OP_Int64, 0, iMem, 0, zV, P4_INT64); }else{ #ifdef SQLITE4_OMIT_FLOATING_POINT sqlite4ErrorMsg(pParse, "oversized integer: %s%s", negFlag ? "-" : "", z); #else codeReal(v, z, negFlag, iMem); #endif } } } /* ** Clear a cache entry. */ static void cacheEntryClear(Parse *pParse, ParseYColCache *p){ | > > > > > > > > > > > > > > > > > > > > > | 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 | ** ** The z[] string will probably not be zero-terminated. But the ** z[n] character is guaranteed to be something that does not look ** like the continuation of the number. */ static void codeReal(Vdbe *v, const char *z, int negateFlag, int iMem){ if( ALWAYS(z!=0) ){ int s = sizeof(sqlite4_num); sqlite4_num *p = (sqlite4_num *)sqlite4DbMallocZero(sqlite4VdbeDb(v), s); if( p ){ *p = sqlite4_num_from_text(z, -1, 0); assert( p->sign==0 ); assert( negateFlag==0 || negateFlag==1 ); p->sign = negateFlag; sqlite4VdbeAddOp4(v, OP_Num, 0, iMem, 0, (const char *)p, P4_NUM); } #if 0 double value; char *zV; sqlite4AtoF(z, &value, sqlite4Strlen30(z), SQLITE4_UTF8); assert( !sqlite4IsNaN(value) ); /* The new AtoF never returns NaN */ if( negateFlag ) value = -value; zV = dup8bytes(v, (char*)&value); sqlite4VdbeAddOp4(v, OP_Real, 0, iMem, 0, zV, P4_REAL); #endif } } #endif /* ** Generate an instruction that will put the integer describe by ** text z[0..n-1] into register iMem. ** ** Expr.u.zToken is always UTF8 and zero-terminated. */ static void codeInteger(Parse *pParse, Expr *pExpr, int negFlag, int iMem){ Vdbe *v = pParse->pVdbe; if( pExpr->flags & EP_IntValue ){ int i = pExpr->u.iValue; assert( i>=0 ); if( negFlag ) i = -i; sqlite4VdbeAddOp2(v, OP_Integer, i, iMem); }else{ sqlite4_num *p; int c; i64 value; const char *z = pExpr->u.zToken; assert( z!=0 ); p = (sqlite4_num *)sqlite4DbMallocRaw(pParse->db, sizeof(sqlite4_num)); if( p ){ *p = sqlite4_num_from_text(z, -1, 0); sqlite4VdbeAddOp4(v, OP_Num, p->e==0, iMem, 0, (const char *)p, P4_NUM); } #if 0 c = sqlite4Atoi64(z, &value, sqlite4Strlen30(z), SQLITE4_UTF8); if( c==0 || (c==2 && negFlag) ){ char *zV; if( negFlag ){ value = c==2 ? SMALLEST_INT64 : -value; } zV = dup8bytes(v, (char*)&value); sqlite4VdbeAddOp4(v, OP_Int64, 0, iMem, 0, zV, P4_INT64); }else{ #ifdef SQLITE4_OMIT_FLOATING_POINT sqlite4ErrorMsg(pParse, "oversized integer: %s%s", negFlag ? "-" : "", z); #else codeReal(v, z, negFlag, iMem); #endif } #endif } } /* ** Clear a cache entry. */ static void cacheEntryClear(Parse *pParse, ParseYColCache *p){ |
︙ | ︙ |
Changes to src/math.c.
︙ | ︙ | |||
304 305 306 307 308 309 310 | ** ** Conversion stops at the first \000 character. At most nIn bytes ** of zIn are examined. Or if nIn is negative, up to a billion bytes ** are scanned, which we assume is more than will be found in any valid ** numeric string. */ sqlite4_num sqlite4_num_from_text(const char *zIn, int nIn, unsigned flags){ | > > | > | < < > > > > > > | > | | | | < < > | > | > | 304 305 306 307 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 356 357 358 359 360 361 362 363 364 365 | ** ** Conversion stops at the first \000 character. At most nIn bytes ** of zIn are examined. Or if nIn is negative, up to a billion bytes ** are scanned, which we assume is more than will be found in any valid ** numeric string. */ sqlite4_num sqlite4_num_from_text(const char *zIn, int nIn, unsigned flags){ static int one = 1; /* Used to test machine endianness */ int seenRadix = 0; /* True after decimal point has been parsed */ int incr = 1; /* 1 for utf-8, 2 for utf-16 */ int bInvalid = 1; /* True for a bad parse */ sqlite4_num r; /* Value to return */ char c; int nDigit = 0; int i; memset(&r, 0, sizeof(r)); if( nIn<0 ) nIn = 1000000000; c = flags & 0xf; if( c==0 || c==SQLITE4_UTF8 ){ incr = 1; }else if( c==SQLITE4_UTF16 ){ incr = 2; c = *(char*)&one; zIn += c; nIn -= c; } /* If the PREFIX_ONLY flag is set, ignore any leading whitespace. */ i = 0; if( flags & SQLITE4_PREFIX_ONLY ){ while( sqlite4Isspace(zIn[i]) && i<nIn ) i+=incr; } if( nIn<=i ) goto finished; /* Check for a leading '+' or '-' symbol. */ if( zIn[i]=='-' ){ r.sign = 1; i += incr; }else if( zIn[i]=='+' ){ i += incr; } if( nIn<=i ) goto finished; /* Check for the string "inf". This is a special case. */ if( (nIn-i)>=incr*3 && ((c=zIn[i])=='i' || c=='I') && ((c=zIn[i+incr])=='n' || c=='N') && ((c=zIn[i+incr*2])=='f' || c=='F') ){ r.e = SQLITE4_MX_EXP+1; r.m = nIn<=i+incr*3 || zIn[i+incr*3]==0; return r; } while( i<nIn && (c = zIn[i])!=0 ){ i += incr; if( c>='0' && c<='9' ){ if( c=='0' && nDigit==0 ){ if( seenRadix && r.e > -(SQLITE4_MX_EXP+1000) ) r.e--; continue; } |
︙ | ︙ | |||
370 371 372 373 374 375 376 | int nEDigit = 0; if( zIn[i]=='-' ){ expsign = 1; i += incr; }else if( zIn[i]=='+' ){ i += incr; } | | | | | | | > | | | > > > > > > > | | 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 412 413 414 415 416 417 418 419 420 421 422 | int nEDigit = 0; if( zIn[i]=='-' ){ expsign = 1; i += incr; }else if( zIn[i]=='+' ){ i += incr; } if( i>=nIn ) goto finished; while( i<nIn && (c = zIn[i])!=0 ){ i += incr; if( c<'0' || c>'9' ) goto finished; if( c=='0' && nEDigit==0 ) continue; nEDigit++; if( nEDigit>3 ) goto finished; exp = exp*10 + c - '0'; } if( expsign ) exp = -exp; r.e += exp; break; }else{ goto finished; } } bInvalid = 0; finished: if( bInvalid && (flags & SQLITE4_PREFIX_ONLY)==0 ){ r.e = SQLITE4_MX_EXP+1; r.m = 0; } else if( seenRadix==0 && r.e==1 && r.m<=(LARGEST_INT64/10) ){ r.m = r.m*10; r.e = 0; } return r; } /* ** Convert an sqlite4_int64 to a number and return that number. */ sqlite4_num sqlite4_num_from_int64(sqlite4_int64 n){ sqlite4_num r; |
︙ | ︙ | |||
485 486 487 488 489 490 491 492 493 494 495 496 497 498 | for(i=num.e; i<0; i++){ iRet = iRet / 10; } *piOut = iRet; return SQLITE4_OK; } /* ** Convert an integer into text in the buffer supplied. The ** text is zero-terminated and right-justified in the buffer. ** A pointer to the first character of text is returned. ** ** The buffer needs to be at least 21 bytes in length. */ | > > > > > > > > > > > > > > | 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 | for(i=num.e; i<0; i++){ iRet = iRet / 10; } *piOut = iRet; return SQLITE4_OK; } /* ** Return true if the sqlite4_num value passed as the first argument ** may be stored in an i64 without loss of precision. ** ** TODO: This only works with values just returned from ** sqlite4_num_from_text(). */ int sqlite4_num_is_int64(sqlite4_num num){ if( num.approx==1 ) return 0; if( num.e<0 ){ } } /* ** Convert an integer into text in the buffer supplied. The ** text is zero-terminated and right-justified in the buffer. ** A pointer to the first character of text is returned. ** ** The buffer needs to be at least 21 bytes in length. */ |
︙ | ︙ |
Changes to src/vdbe.c.
︙ | ︙ | |||
864 865 866 867 868 869 870 871 872 873 874 875 876 877 | case OP_Real: { /* same as TK_FLOAT, out2-prerelease */ pOut->flags = MEM_Real; assert( !sqlite4IsNaN(*pOp->p4.pReal) ); pOut->u.num = sqlite4_num_from_double(*pOp->p4.pReal); break; } #endif /* Opcode: String8 * P2 * P4 * ** ** P4 points to a nul terminated UTF-8 string. This opcode is transformed ** into an OP_String before it is executed for the first time. */ case OP_String8: { /* same as TK_STRING, out2-prerelease */ | > > > > > > > > > > > > | 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 | case OP_Real: { /* same as TK_FLOAT, out2-prerelease */ pOut->flags = MEM_Real; assert( !sqlite4IsNaN(*pOp->p4.pReal) ); pOut->u.num = sqlite4_num_from_double(*pOp->p4.pReal); break; } #endif /* Opcode: Num P1 P2 * P4 * ** ** P4 is a pointer to an sqlite4_num value. Write that value into ** register P2. Set the register flags to MEM_Int if P1 is non-zero, ** or MEM_Real otherwise. */ case OP_Num: { /* out2-prerelease */ pOut->flags = (pOp->p1 ? MEM_Int : MEM_Real); pOut->u.num = *(pOp->p4.pNum); break; } /* Opcode: String8 * P2 * P4 * ** ** P4 points to a nul terminated UTF-8 string. This opcode is transformed ** into an OP_String before it is executed for the first time. */ case OP_String8: { /* same as TK_STRING, out2-prerelease */ |
︙ | ︙ | |||
1689 1690 1691 1692 1693 1694 1695 | ** equivalent of atoi() and store 0.0 if no such conversion is possible. ** ** A NULL value is not changed by this routine. It remains NULL. */ case OP_ToReal: { /* same as TK_TO_REAL, in1 */ pIn1 = &aMem[pOp->p1]; memAboutToChange(p, pIn1); | > | | < | 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 | ** equivalent of atoi() and store 0.0 if no such conversion is possible. ** ** A NULL value is not changed by this routine. It remains NULL. */ case OP_ToReal: { /* same as TK_TO_REAL, in1 */ pIn1 = &aMem[pOp->p1]; memAboutToChange(p, pIn1); sqlite4VdbeMemNumerify(pIn1); pIn1->flags |= MEM_Real; pIn1->flags &= ~MEM_Int; break; } #endif /* !defined(SQLITE4_OMIT_CAST) && !defined(SQLITE4_OMIT_FLOATING_POINT) */ /* Opcode: Lt P1 P2 P3 P4 P5 ** ** Compare the values in register P1 and P3. If reg(P3)<reg(P1) then |
︙ | ︙ |
Changes to src/vdbe.h.
︙ | ︙ | |||
60 61 62 63 64 65 66 67 68 69 70 71 72 73 | Mem *pMem; /* Used when p4type is P4_MEM */ VTable *pVtab; /* Used when p4type is P4_VTAB */ KeyInfo *pKeyInfo; /* Used when p4type is P4_KEYINFO */ int *ai; /* Used when p4type is P4_INTARRAY */ SubProgram *pProgram; /* Used when p4type is P4_SUBPROGRAM */ Fts5Info *pFtsInfo; /* Used when p4type is P4_FTS5INDEXINFO */ int (*xAdvance)(VdbeCursor*); } p4; #ifdef SQLITE4_DEBUG char *zComment; /* Comment to improve readability */ #endif #ifdef VDBE_PROFILE int cnt; /* Number of times this instruction was executed */ u64 cycles; /* Total time spent executing this instruction */ | > | 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | Mem *pMem; /* Used when p4type is P4_MEM */ VTable *pVtab; /* Used when p4type is P4_VTAB */ KeyInfo *pKeyInfo; /* Used when p4type is P4_KEYINFO */ int *ai; /* Used when p4type is P4_INTARRAY */ SubProgram *pProgram; /* Used when p4type is P4_SUBPROGRAM */ Fts5Info *pFtsInfo; /* Used when p4type is P4_FTS5INDEXINFO */ int (*xAdvance)(VdbeCursor*); sqlite4_num *pNum; /* Used when p4type is P4_NUM */ } p4; #ifdef SQLITE4_DEBUG char *zComment; /* Comment to improve readability */ #endif #ifdef VDBE_PROFILE int cnt; /* Number of times this instruction was executed */ u64 cycles; /* Total time spent executing this instruction */ |
︙ | ︙ | |||
118 119 120 121 122 123 124 125 126 127 128 129 130 131 | #define P4_REAL (-12) /* P4 is a 64-bit floating point value */ #define P4_INT64 (-13) /* P4 is a 64-bit signed integer */ #define P4_INT32 (-14) /* P4 is a 32-bit signed integer */ #define P4_INTARRAY (-15) /* P4 is a vector of 32-bit integers */ #define P4_SUBPROGRAM (-18) /* P4 is a pointer to a SubProgram structure */ #define P4_ADVANCE (-19) /* P4 is a pointer to BtreeNext() or BtreePrev() */ #define P4_FTS5INFO (-20) /* P4 points to an Fts5Info structure */ /* When adding a P4 argument using P4_KEYINFO, a copy of the KeyInfo structure ** is made. That copy is freed when the Vdbe is finalized. But if the ** argument is P4_KEYINFO_HANDOFF, the passed in pointer is used. It still ** gets freed when the Vdbe is finalized so it still should be obtained ** from a single sqliteMalloc(). But no copy is made and the calling ** function should *not* try to free the KeyInfo. | > | 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | #define P4_REAL (-12) /* P4 is a 64-bit floating point value */ #define P4_INT64 (-13) /* P4 is a 64-bit signed integer */ #define P4_INT32 (-14) /* P4 is a 32-bit signed integer */ #define P4_INTARRAY (-15) /* P4 is a vector of 32-bit integers */ #define P4_SUBPROGRAM (-18) /* P4 is a pointer to a SubProgram structure */ #define P4_ADVANCE (-19) /* P4 is a pointer to BtreeNext() or BtreePrev() */ #define P4_FTS5INFO (-20) /* P4 points to an Fts5Info structure */ #define P4_NUM (-21) /* P4 points to an Fts5Info structure */ /* When adding a P4 argument using P4_KEYINFO, a copy of the KeyInfo structure ** is made. That copy is freed when the Vdbe is finalized. But if the ** argument is P4_KEYINFO_HANDOFF, the passed in pointer is used. It still ** gets freed when the Vdbe is finalized so it still should be obtained ** from a single sqliteMalloc(). But no copy is made and the calling ** function should *not* try to free the KeyInfo. |
︙ | ︙ |
Changes to src/vdbeaux.c.
︙ | ︙ | |||
578 579 580 581 582 583 584 585 586 587 588 589 590 591 | ** Delete a P4 value if necessary. */ static void freeP4(sqlite4 *db, int p4type, void *p4){ if( p4 ){ assert( db ); switch( p4type ){ case P4_REAL: case P4_INT64: case P4_DYNAMIC: case P4_KEYINFO: case P4_INTARRAY: case P4_KEYINFO_HANDOFF: { sqlite4DbFree(db, p4); break; | > | 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 | ** Delete a P4 value if necessary. */ static void freeP4(sqlite4 *db, int p4type, void *p4){ if( p4 ){ assert( db ); switch( p4type ){ case P4_REAL: case P4_NUM: case P4_INT64: case P4_DYNAMIC: case P4_KEYINFO: case P4_INTARRAY: case P4_KEYINFO_HANDOFF: { sqlite4DbFree(db, p4); break; |
︙ | ︙ |
Changes to src/vdbemem.c.
︙ | ︙ | |||
392 393 394 395 396 397 398 | ** Convert pMem to type integer. Invalidate any prior representations. */ int sqlite4VdbeMemIntegerify(Mem *pMem){ assert( pMem->db==0 || sqlite4_mutex_held(pMem->db->mutex) ); assert( (pMem->flags & MEM_RowSet)==0 ); assert( EIGHT_BYTE_ALIGNMENT(pMem) ); | > > > > > > | > > | 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 | ** Convert pMem to type integer. Invalidate any prior representations. */ int sqlite4VdbeMemIntegerify(Mem *pMem){ assert( pMem->db==0 || sqlite4_mutex_held(pMem->db->mutex) ); assert( (pMem->flags & MEM_RowSet)==0 ); assert( EIGHT_BYTE_ALIGNMENT(pMem) ); if( (pMem->flags & MEM_Int)==0 ){ sqlite4VdbeMemNumerify(pMem); } if( (pMem->flags & MEM_Int)==0 ){ i64 iVal; sqlite4_num_to_int64(pMem->u.num, &iVal); pMem->u.num = sqlite4_num_from_int64(iVal); } MemSetTypeFlag(pMem, MEM_Int); return SQLITE4_OK; } /* ** Convert pMem so that it is of type MEM_Real. ** Invalidate any prior representations. */ |
︙ | ︙ | |||
420 421 422 423 424 425 426 | ** ** Every effort is made to force the conversion, even if the input ** is a string that does not look completely like a number. Convert ** as much of the string as we can and ignore the rest. */ int sqlite4VdbeMemNumerify(Mem *pMem){ if( (pMem->flags & (MEM_Int|MEM_Real|MEM_Null))==0 ){ | > | > > > > > > > > | 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 | ** ** Every effort is made to force the conversion, even if the input ** is a string that does not look completely like a number. Convert ** as much of the string as we can and ignore the rest. */ int sqlite4VdbeMemNumerify(Mem *pMem){ if( (pMem->flags & (MEM_Int|MEM_Real|MEM_Null))==0 ){ int flags = (pMem->enc | SQLITE4_PREFIX_ONLY); assert( (pMem->flags & (MEM_Blob|MEM_Str))!=0 ); assert( pMem->db==0 || sqlite4_mutex_held(pMem->db->mutex) ); pMem->u.num = sqlite4_num_from_text(pMem->z, pMem->n, flags); if( pMem->u.num.e==0 ){ MemSetTypeFlag(pMem, MEM_Int); }else{ MemSetTypeFlag(pMem, MEM_Real); } #if 0 if( 0==sqlite4Atoi64(pMem->z, &i1, pMem->n, pMem->enc) ){ pMem->u.num = sqlite4_num_from_int64(i1); MemSetTypeFlag(pMem, MEM_Int); }else{ pMem->u.num = sqlite4_num_from_double(sqlite4VdbeRealValue(pMem)); MemSetTypeFlag(pMem, MEM_Real); sqlite4VdbeIntegerAffinity(pMem); } #endif } assert( (pMem->flags & (MEM_Int|MEM_Real|MEM_Null))!=0 ); pMem->flags &= ~(MEM_Str|MEM_Blob); return SQLITE4_OK; } /* |
︙ | ︙ |
Added test/auth4.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 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 130 131 | # 2013 May 8 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # This file contains tests for the sqlite4_authorizer_push() and # sqlite4_authorizer_pop() API functions. # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix auth4 ifcapable !auth { finish_test ; return } #-------------------------------------------------------------------- # Test cases auth4-1.* test that when there are multiple authorizers # on the stack, they are invoked in order from most to least recently # added until all have been invoked or one of them returns other than # SQLITE4_OK. # do_execsql_test 1.0 { CREATE TABLE t1(x, y); INSERT INTO t1 VALUES(1, 'one'); INSERT INTO t1 VALUES(2, 'two'); } proc auth_callback {id code z1 z2 z3 z4} { if {$code == "SQLITE4_READ" && $z1=="t1" && $z2=="y"} { incr ::NAUTH return [lindex $::AUTH $id] } return SQLITE4_OK } sqlite4_authorizer_push db {auth_callback 2} sqlite4_authorizer_push db {auth_callback 1} sqlite4_authorizer_push db {auth_callback 0} foreach {tn codes ncall res} { 1 {SQLITE4_OK SQLITE4_OK SQLITE4_OK} 3 {0 {1 one 2 two}} 2 {SQLITE4_OK SQLITE4_OK SQLITE4_DENY} 3 {1 {access to t1.y is prohibited}} 3 {SQLITE4_DENY SQLITE4_OK SQLITE4_OK} 1 {1 {access to t1.y is prohibited}} 4 {SQLITE4_OK SQLITE4_DENY SQLITE4_OK} 2 {1 {access to t1.y is prohibited}} 5 {SQLITE4_OK SQLITE4_OK SQLITE4_IGNORE} 3 {0 {1 {} 2 {}}} 6 {SQLITE4_IGNORE SQLITE4_OK SQLITE4_OK} 1 {0 {1 {} 2 {}}} 7 {SQLITE4_OK SQLITE4_IGNORE SQLITE4_OK} 2 {0 {1 {} 2 {}}} 8 {SQLITE4_OK SQLITE4_OK SQLITE4_ALLOW} 3 {0 {1 one 2 two}} 9 {SQLITE4_ALLOW SQLITE4_OK SQLITE4_OK} 1 {0 {1 one 2 two}} 10 {SQLITE4_OK SQLITE4_ALLOW SQLITE4_OK} 2 {0 {1 one 2 two}} } { db cache flush set ::AUTH $codes set ::NAUTH 0 do_catchsql_test 1.$tn.1 { SELECT * FROM t1; } $res do_test 1.$tn.2 { set ::NAUTH } $ncall } sqlite4_authorizer_pop db sqlite4_authorizer_pop db sqlite4_authorizer_pop db #-------------------------------------------------------------------- # Test cases auth4-2.* test that the push and pop operations seem to # work correctly. # set ::STACK [list] proc auth_callback {id code z1 z2 z3 z4} { if {$code == "SQLITE4_READ" && $z1=="t1" && $z2=="y"} { lappend ::AUTH $id } return SQLITE4_OK } proc test_stack {} { set ::AUTH [list] db eval { SELECT * FROM t1 } set ::AUTH } proc push {id} { set ::STACK [concat $id $::STACK] sqlite4_authorizer_push db [list auth_callback $id] } proc pop {} { set ::STACK [lrange $::STACK 1 end] sqlite4_authorizer_pop db } do_execsql_test 2.0 { DROP TABLE IF EXISTS t1; CREATE TABLE t1(x, y); INSERT INTO t1 VALUES(1, 'one'); } for {set i 1} {$i <= 100} {incr i} { if { int(rand()*2.0) } { pop } else { push [expr int(rand() * 500.0)] } do_test 2.$i { test_stack } $::STACK } #-------------------------------------------------------------------- # Test that sqlite4_authorizer_pop() returns an error if the stack is # empty when it is called. # db close sqlite4 db test.db do_test 3.1 { sqlite4_authorizer_pop db } {SQLITE4_ERROR} do_test 3.2 { sqlite4_authorizer_push db xyz sqlite4_authorizer_pop db } {SQLITE4_OK} finish_test |