/ Check-in [271c110b]
Login

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

Overview
Comment:Merge obscure problem fixes from trunk.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | sessions
Files: files | file ages | folders
SHA1: 271c110bcf5bf2ea7e113dd01dec876a08e3c047
User & Date: drh 2015-04-06 12:08:24
Context
2015-04-07
23:10
Merge printf() width and precision overflow fixes from trunk. check-in: aeca95ac user: drh tags: sessions
2015-04-06
12:08
Merge obscure problem fixes from trunk. check-in: 271c110b user: drh tags: sessions
11:04
Fix a problem with fts3 prefix terms within phrase queries on "order=DESC" tables with a mix of negative and positive rowids. check-in: 3ad829e5 user: dan tags: trunk
2015-04-01
16:39
Merge recent enhancements from trunk. check-in: aea439bd user: drh tags: sessions
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to ext/fts3/fts3.c.

  2498   2498   **
  2499   2499   ** If the docids in the input doclists are sorted in ascending order,
  2500   2500   ** parameter bDescDoclist should be false. If they are sorted in ascending 
  2501   2501   ** order, it should be passed a non-zero value.
  2502   2502   **
  2503   2503   ** The right-hand input doclist is overwritten by this function.
  2504   2504   */
  2505         -static void fts3DoclistPhraseMerge(
         2505  +static int fts3DoclistPhraseMerge(
  2506   2506     int bDescDoclist,               /* True if arguments are desc */
  2507   2507     int nDist,                      /* Distance from left to right (1=adjacent) */
  2508   2508     char *aLeft, int nLeft,         /* Left doclist */
  2509         -  char *aRight, int *pnRight      /* IN/OUT: Right/output doclist */
         2509  +  char **paRight, int *pnRight    /* IN/OUT: Right/output doclist */
  2510   2510   ){
  2511   2511     sqlite3_int64 i1 = 0;
  2512   2512     sqlite3_int64 i2 = 0;
  2513   2513     sqlite3_int64 iPrev = 0;
         2514  +  char *aRight = *paRight;
  2514   2515     char *pEnd1 = &aLeft[nLeft];
  2515   2516     char *pEnd2 = &aRight[*pnRight];
  2516   2517     char *p1 = aLeft;
  2517   2518     char *p2 = aRight;
  2518   2519     char *p;
  2519   2520     int bFirstOut = 0;
  2520         -  char *aOut = aRight;
         2521  +  char *aOut;
  2521   2522   
  2522   2523     assert( nDist>0 );
  2523         -
         2524  +  if( bDescDoclist ){
         2525  +    aOut = sqlite3_malloc(*pnRight + FTS3_VARINT_MAX);
         2526  +    if( aOut==0 ) return SQLITE_NOMEM;
         2527  +  }else{
         2528  +    aOut = aRight;
         2529  +  }
  2524   2530     p = aOut;
         2531  +
  2525   2532     fts3GetDeltaVarint3(&p1, pEnd1, 0, &i1);
  2526   2533     fts3GetDeltaVarint3(&p2, pEnd2, 0, &i2);
  2527   2534   
  2528   2535     while( p1 && p2 ){
  2529   2536       sqlite3_int64 iDiff = DOCID_CMP(i1, i2);
  2530   2537       if( iDiff==0 ){
  2531   2538         char *pSave = p;
................................................................................
  2546   2553       }else{
  2547   2554         fts3PoslistCopy(0, &p2);
  2548   2555         fts3GetDeltaVarint3(&p2, pEnd2, bDescDoclist, &i2);
  2549   2556       }
  2550   2557     }
  2551   2558   
  2552   2559     *pnRight = (int)(p - aOut);
         2560  +  if( bDescDoclist ){
         2561  +    sqlite3_free(aRight);
         2562  +    *paRight = aOut;
         2563  +  }
         2564  +
         2565  +  return SQLITE_OK;
  2553   2566   }
  2554   2567   
  2555   2568   /*
  2556   2569   ** Argument pList points to a position list nList bytes in size. This
  2557   2570   ** function checks to see if the position list contains any entries for
  2558   2571   ** a token in position 0 (of any column). If so, it writes argument iDelta
  2559   2572   ** to the output buffer pOut, followed by a position list consisting only
................................................................................
  2670   2683     Fts3Table *p,                   /* FTS table handle */
  2671   2684     TermSelect *pTS,                /* TermSelect object to merge into */
  2672   2685     char *aDoclist,                 /* Pointer to doclist */
  2673   2686     int nDoclist                    /* Size of aDoclist in bytes */
  2674   2687   ){
  2675   2688     if( pTS->aaOutput[0]==0 ){
  2676   2689       /* If this is the first term selected, copy the doclist to the output
  2677         -    ** buffer using memcpy(). */
  2678         -    pTS->aaOutput[0] = sqlite3_malloc(nDoclist);
         2690  +    ** buffer using memcpy(). 
         2691  +    **
         2692  +    ** Add FTS3_VARINT_MAX bytes of unused space to the end of the 
         2693  +    ** allocation. This is so as to ensure that the buffer is big enough
         2694  +    ** to hold the current doclist AND'd with any other doclist. If the
         2695  +    ** doclists are stored in order=ASC order, this padding would not be
         2696  +    ** required (since the size of [doclistA AND doclistB] is always less
         2697  +    ** than or equal to the size of [doclistA] in that case). But this is
         2698  +    ** not true for order=DESC. For example, a doclist containing (1, -1) 
         2699  +    ** may be smaller than (-1), as in the first example the -1 may be stored
         2700  +    ** as a single-byte delta, whereas in the second it must be stored as a
         2701  +    ** FTS3_VARINT_MAX byte varint.
         2702  +    **
         2703  +    ** Similar padding is added in the fts3DoclistOrMerge() function.
         2704  +    */
         2705  +    pTS->aaOutput[0] = sqlite3_malloc(nDoclist + FTS3_VARINT_MAX + 1);
  2679   2706       pTS->anOutput[0] = nDoclist;
  2680   2707       if( pTS->aaOutput[0] ){
  2681   2708         memcpy(pTS->aaOutput[0], aDoclist, nDoclist);
  2682   2709       }else{
  2683   2710         return SQLITE_NOMEM;
  2684   2711       }
  2685   2712     }else{
................................................................................
  3927   3954   /*
  3928   3955   ** Arguments pList/nList contain the doclist for token iToken of phrase p.
  3929   3956   ** It is merged into the main doclist stored in p->doclist.aAll/nAll.
  3930   3957   **
  3931   3958   ** This function assumes that pList points to a buffer allocated using
  3932   3959   ** sqlite3_malloc(). This function takes responsibility for eventually
  3933   3960   ** freeing the buffer.
         3961  +**
         3962  +** SQLITE_OK is returned if successful, or SQLITE_NOMEM if an error occurs.
  3934   3963   */
  3935         -static void fts3EvalPhraseMergeToken(
         3964  +static int fts3EvalPhraseMergeToken(
  3936   3965     Fts3Table *pTab,                /* FTS Table pointer */
  3937   3966     Fts3Phrase *p,                  /* Phrase to merge pList/nList into */
  3938   3967     int iToken,                     /* Token pList/nList corresponds to */
  3939   3968     char *pList,                    /* Pointer to doclist */
  3940   3969     int nList                       /* Number of bytes in pList */
  3941   3970   ){
         3971  +  int rc = SQLITE_OK;
  3942   3972     assert( iToken!=p->iDoclistToken );
  3943   3973   
  3944   3974     if( pList==0 ){
  3945   3975       sqlite3_free(p->doclist.aAll);
  3946   3976       p->doclist.aAll = 0;
  3947   3977       p->doclist.nAll = 0;
  3948   3978     }
................................................................................
  3973   4003         pRight = p->doclist.aAll;
  3974   4004         nRight = p->doclist.nAll;
  3975   4005         pLeft = pList;
  3976   4006         nLeft = nList;
  3977   4007         nDiff = p->iDoclistToken - iToken;
  3978   4008       }
  3979   4009   
  3980         -    fts3DoclistPhraseMerge(pTab->bDescIdx, nDiff, pLeft, nLeft, pRight,&nRight);
         4010  +    rc = fts3DoclistPhraseMerge(
         4011  +        pTab->bDescIdx, nDiff, pLeft, nLeft, &pRight, &nRight
         4012  +    );
  3981   4013       sqlite3_free(pLeft);
  3982   4014       p->doclist.aAll = pRight;
  3983   4015       p->doclist.nAll = nRight;
  3984   4016     }
  3985   4017   
  3986   4018     if( iToken>p->iDoclistToken ) p->iDoclistToken = iToken;
         4019  +  return rc;
  3987   4020   }
  3988   4021   
  3989   4022   /*
  3990   4023   ** Load the doclist for phrase p into p->doclist.aAll/nAll. The loaded doclist
  3991   4024   ** does not take deferred tokens into account.
  3992   4025   **
  3993   4026   ** SQLITE_OK is returned if no error occurs, otherwise an SQLite error code.
................................................................................
  4005   4038       assert( pToken->pDeferred==0 || pToken->pSegcsr==0 );
  4006   4039   
  4007   4040       if( pToken->pSegcsr ){
  4008   4041         int nThis = 0;
  4009   4042         char *pThis = 0;
  4010   4043         rc = fts3TermSelect(pTab, pToken, p->iColumn, &nThis, &pThis);
  4011   4044         if( rc==SQLITE_OK ){
  4012         -        fts3EvalPhraseMergeToken(pTab, p, iToken, pThis, nThis);
         4045  +        rc = fts3EvalPhraseMergeToken(pTab, p, iToken, pThis, nThis);
  4013   4046         }
  4014   4047       }
  4015   4048       assert( pToken->pSegcsr==0 );
  4016   4049     }
  4017   4050   
  4018   4051     return rc;
  4019   4052   }
................................................................................
  4807   4840           ** part of a multi-token phrase. Either way, the entire doclist will
  4808   4841           ** (eventually) be loaded into memory. It may as well be now. */
  4809   4842           Fts3PhraseToken *pToken = pTC->pToken;
  4810   4843           int nList = 0;
  4811   4844           char *pList = 0;
  4812   4845           rc = fts3TermSelect(pTab, pToken, pTC->iCol, &nList, &pList);
  4813   4846           assert( rc==SQLITE_OK || pList==0 );
         4847  +        if( rc==SQLITE_OK ){
         4848  +          rc = fts3EvalPhraseMergeToken(
         4849  +              pTab, pTC->pPhrase, pTC->iToken,pList,nList
         4850  +          );
         4851  +        }
  4814   4852           if( rc==SQLITE_OK ){
  4815   4853             int nCount;
  4816         -          fts3EvalPhraseMergeToken(pTab, pTC->pPhrase, pTC->iToken,pList,nList);
  4817   4854             nCount = fts3DoclistCountDocids(
  4818   4855                 pTC->pPhrase->doclist.aAll, pTC->pPhrase->doclist.nAll
  4819   4856             );
  4820   4857             if( ii==0 || nCount<nMinEst ) nMinEst = nCount;
  4821   4858           }
  4822   4859         }
  4823   4860       }

Changes to src/os_unix.c.

  3779   3779   /*
  3780   3780   ** Information and control of an open file handle.
  3781   3781   */
  3782   3782   static int unixFileControl(sqlite3_file *id, int op, void *pArg){
  3783   3783     unixFile *pFile = (unixFile*)id;
  3784   3784     switch( op ){
  3785   3785       case SQLITE_FCNTL_WAL_BLOCK: {
  3786         -      pFile->ctrlFlags |= UNIXFILE_BLOCK;
         3786  +      /* pFile->ctrlFlags |= UNIXFILE_BLOCK; // Deferred feature */
  3787   3787         return SQLITE_OK;
  3788   3788       }
  3789   3789       case SQLITE_FCNTL_LOCKSTATE: {
  3790   3790         *(int*)pArg = pFile->eFileLock;
  3791   3791         return SQLITE_OK;
  3792   3792       }
  3793   3793       case SQLITE_FCNTL_LAST_ERRNO: {

Changes to src/resolve.c.

  1182   1182       */
  1183   1183       memset(&sNC, 0, sizeof(sNC));
  1184   1184       sNC.pParse = pParse;
  1185   1185       if( sqlite3ResolveExprNames(&sNC, p->pLimit) ||
  1186   1186           sqlite3ResolveExprNames(&sNC, p->pOffset) ){
  1187   1187         return WRC_Abort;
  1188   1188       }
         1189  +
         1190  +    /* If the SF_Converted flags is set, then this Select object was
         1191  +    ** was created by the convertCompoundSelectToSubquery() function.
         1192  +    ** In this case the ORDER BY clause (p->pOrderBy) should be resolved
         1193  +    ** as if it were part of the sub-query, not the parent. This block
         1194  +    ** moves the pOrderBy down to the sub-query. It will be moved back
         1195  +    ** after the names have been resolved.  */
         1196  +    if( p->selFlags & SF_Converted ){
         1197  +      Select *pSub = p->pSrc->a[0].pSelect;
         1198  +      assert( p->pSrc->nSrc==1 && isCompound==0 && p->pOrderBy );
         1199  +      assert( pSub->pPrior && pSub->pOrderBy==0 );
         1200  +      pSub->pOrderBy = p->pOrderBy;
         1201  +      p->pOrderBy = 0;
         1202  +    }
  1189   1203     
  1190   1204       /* Recursively resolve names in all subqueries
  1191   1205       */
  1192   1206       for(i=0; i<p->pSrc->nSrc; i++){
  1193   1207         struct SrcList_item *pItem = &p->pSrc->a[i];
  1194   1208         if( pItem->pSelect ){
  1195   1209           NameContext *pNC;         /* Used to iterate name contexts */
................................................................................
  1263   1277       if( sqlite3ResolveExprNames(&sNC, p->pWhere) ) return WRC_Abort;
  1264   1278   
  1265   1279       /* The ORDER BY and GROUP BY clauses may not refer to terms in
  1266   1280       ** outer queries 
  1267   1281       */
  1268   1282       sNC.pNext = 0;
  1269   1283       sNC.ncFlags |= NC_AllowAgg;
         1284  +
         1285  +    /* If this is a converted compound query, move the ORDER BY clause from 
         1286  +    ** the sub-query back to the parent query. At this point each term
         1287  +    ** within the ORDER BY clause has been transformed to an integer value.
         1288  +    ** These integers will be replaced by copies of the corresponding result
         1289  +    ** set expressions by the call to resolveOrderGroupBy() below.  */
         1290  +    if( p->selFlags & SF_Converted ){
         1291  +      Select *pSub = p->pSrc->a[0].pSelect;
         1292  +      p->pOrderBy = pSub->pOrderBy;
         1293  +      pSub->pOrderBy = 0;
         1294  +    }
  1270   1295   
  1271   1296       /* Process the ORDER BY clause for singleton SELECT statements.
  1272   1297       ** The ORDER BY clause for compounds SELECT statements is handled
  1273   1298       ** below, after all of the result-sets for all of the elements of
  1274   1299       ** the compound have been resolved.
  1275   1300       */
  1276   1301       if( !isCompound && resolveOrderGroupBy(&sNC, p, p->pOrderBy, "ORDER") ){

Changes to src/select.c.

  3880   3880     p->pWhere = 0;
  3881   3881     pNew->pGroupBy = 0;
  3882   3882     pNew->pHaving = 0;
  3883   3883     pNew->pOrderBy = 0;
  3884   3884     p->pPrior = 0;
  3885   3885     p->pNext = 0;
  3886   3886     p->selFlags &= ~SF_Compound;
         3887  +  assert( (p->selFlags & SF_Converted)==0 );
         3888  +  p->selFlags |= SF_Converted;
  3887   3889     assert( pNew->pPrior!=0 );
  3888   3890     pNew->pPrior->pNext = pNew;
  3889   3891     pNew->pLimit = 0;
  3890   3892     pNew->pOffset = 0;
  3891   3893     return WRC_Continue;
  3892   3894   }
  3893   3895   

Changes to src/sqliteInt.h.

  2393   2393   #define SF_Compound        0x0040  /* Part of a compound query */
  2394   2394   #define SF_Values          0x0080  /* Synthesized from VALUES clause */
  2395   2395   #define SF_AllValues       0x0100  /* All terms of compound are VALUES */
  2396   2396   #define SF_NestedFrom      0x0200  /* Part of a parenthesized FROM clause */
  2397   2397   #define SF_MaybeConvert    0x0400  /* Need convertCompoundSelectToSubquery() */
  2398   2398   #define SF_Recursive       0x0800  /* The recursive part of a recursive CTE */
  2399   2399   #define SF_MinMaxAgg       0x1000  /* Aggregate containing min() or max() */
         2400  +#define SF_Converted       0x2000  /* By convertCompoundSelectToSubquery() */
  2400   2401   
  2401   2402   
  2402   2403   /*
  2403   2404   ** The results of a SELECT can be distributed in several ways, as defined
  2404   2405   ** by one of the following macros.  The "SRT" prefix means "SELECT Result
  2405   2406   ** Type".
  2406   2407   **

Changes to test/e_walauto.test.

    11     11   #
    12     12   
    13     13   set testdir [file dirname $argv0]
    14     14   source $testdir/tester.tcl
    15     15   source $testdir/wal_common.tcl
    16     16   set testprefix e_walauto
    17     17   
           18  +# Do not run this test on OpenBSD, as it depends on read() and mmap both
           19  +# accessing the same coherent view of the "test.db-shm" file. This doesn't
           20  +# work on OpenBSD.
           21  +#
           22  +if {$tcl_platform(os) == "OpenBSD"} {
           23  +  finish_test
           24  +  return
           25  +}
    18     26   
    19     27   proc read_nbackfill {} {
    20     28     seek $::shmfd 96
    21     29     binary scan [read $::shmfd 4] n nBackfill
    22     30     set nBackfill
    23     31   }
    24     32   proc read_mxframe {} {

Changes to test/fts3fault2.test.

   150    150         );
   151    151       }
   152    152       execsql { SELECT docid FROM ft WHERE ft MATCH 'th*' }
   153    153     } -test {
   154    154       faultsim_test_result {0 {1 2}}
   155    155     }
   156    156   }
          157  +
          158  +reset_db
          159  +do_test 6.0 {
          160  +  execsql {
          161  +    CREATE VIRTUAL TABLE t6 USING fts4(x,order=DESC);
          162  +    INSERT INTO t6(docid, x) VALUES(-1,'a b');
          163  +    INSERT INTO t6(docid, x) VALUES(1, 'b');
          164  +  }
          165  +  faultsim_save_and_close
          166  +} {}
          167  +
          168  +do_faultsim_test 6.1 -faults oom* -prep {
          169  +  faultsim_restore_and_reopen
          170  +  db eval {SELECT * FROM sqlite_master}
          171  +} -body {
          172  +  execsql { SELECT docid FROM t6 WHERE t6 MATCH '"a* b"' }
          173  +} -test {
          174  +  faultsim_test_result {0 -1}
          175  +}
   157    176   
   158    177   finish_test

Changes to test/fts3prefix.test.

   269    269     CREATE VIRTUAL TABLE t2 USING fts4(prefix=);
   270    270     INSERT INTO t1 VALUES('He dressed himself in cycling clothes');
   271    271     INSERT INTO t2 VALUES('He dressed himself in cycling clothes');
   272    272   } {}
   273    273   do_execsql_test 6.5.2 {
   274    274     SELECT md5sum(quote(root)) FROM t1_segdir;
   275    275   } [db eval {SELECT md5sum(quote(root)) FROM t2_segdir}]
          276  +
          277  +
          278  +do_execsql_test 7.0 {
          279  +  CREATE VIRTUAL TABLE t6 USING fts4(x,order=DESC);
          280  +  INSERT INTO t6(docid, x) VALUES(-1,'a b');
          281  +  INSERT INTO t6(docid, x) VALUES(1, 'b');
          282  +}
          283  +do_execsql_test 7.1 {
          284  +  SELECT docid FROM t6 WHERE t6 MATCH '"a* b"';
          285  +} {-1}
          286  +do_execsql_test 7.2 {
          287  +  SELECT docid FROM t6 WHERE t6 MATCH 'a*';
          288  +} {-1}
          289  +do_execsql_test 7.3 {
          290  +  SELECT docid FROM t6 WHERE t6 MATCH 'a* b';
          291  +} {-1}
          292  +
          293  +
   276    294   
   277    295   finish_test

Changes to test/selectA.test.

  1370   1370   do_execsql_test 4.2.2 {
  1371   1371     SELECT c, f(d,c,d,c,d) FROM t7
  1372   1372     UNION ALL
  1373   1373     SELECT a, b FROM t6 
  1374   1374     ORDER BY 1,2
  1375   1375   } {/2 . 3 . 4 . 5 . 6 . 7 ./}
  1376   1376   
         1377  +
         1378  +proc strip_rnd {explain} {
         1379  +  regexp -all {sqlite_sq_[0123456789ABCDEF]*} $explain sqlite_sq
         1380  +}
         1381  +
         1382  +proc do_same_test {tn q1 args} {
         1383  +  set r2 [strip_rnd [db eval "EXPLAIN $q1"]]
         1384  +  set i 1
         1385  +  foreach q $args {
         1386  +    set tst [subst -nocommands {strip_rnd [db eval "EXPLAIN $q"]}]
         1387  +    uplevel do_test $tn.$i [list $tst] [list $r2]
         1388  +    incr i
         1389  +  }
         1390  +}
         1391  +
         1392  +do_execsql_test 5.0 {
         1393  +  CREATE TABLE t8(a, b);
         1394  +  CREATE TABLE t9(c, d);
         1395  +} {}
         1396  +
         1397  +do_same_test 5.1 {
         1398  +  SELECT a, b FROM t8 INTERSECT SELECT c, d FROM t9 ORDER BY a;
         1399  +} {
         1400  +  SELECT a, b FROM t8 INTERSECT SELECT c, d FROM t9 ORDER BY t8.a;
         1401  +} {
         1402  +  SELECT a, b FROM t8 INTERSECT SELECT c, d FROM t9 ORDER BY 1;
         1403  +} {
         1404  +  SELECT a, b FROM t8 INTERSECT SELECT c, d FROM t9 ORDER BY c;
         1405  +} {
         1406  +  SELECT a, b FROM t8 INTERSECT SELECT c, d FROM t9 ORDER BY t9.c;
         1407  +}
         1408  +
         1409  +do_same_test 5.2 {
         1410  +  SELECT a, b FROM t8 UNION SELECT c, d FROM t9 ORDER BY a COLLATE NOCASE
         1411  +} {
         1412  +  SELECT a, b FROM t8 UNION SELECT c, d FROM t9 ORDER BY t8.a COLLATE NOCASE
         1413  +} {
         1414  +  SELECT a, b FROM t8 UNION SELECT c, d FROM t9 ORDER BY 1 COLLATE NOCASE
         1415  +} {
         1416  +  SELECT a, b FROM t8 UNION SELECT c, d FROM t9 ORDER BY c COLLATE NOCASE
         1417  +} {
         1418  +  SELECT a, b FROM t8 UNION SELECT c, d FROM t9 ORDER BY t9.c COLLATE NOCASE
         1419  +}
         1420  +
         1421  +do_same_test 5.3 {
         1422  +  SELECT a, b FROM t8 EXCEPT SELECT c, d FROM t9 ORDER BY b, c COLLATE NOCASE
         1423  +} {
         1424  +  SELECT a, b FROM t8 EXCEPT SELECT c, d FROM t9 ORDER BY 2, 1 COLLATE NOCASE
         1425  +} {
         1426  +  SELECT a, b FROM t8 EXCEPT SELECT c, d FROM t9 ORDER BY d, a COLLATE NOCASE
         1427  +} {
         1428  +  SELECT a, b FROM t8 EXCEPT SELECT * FROM t9 ORDER BY t9.d, c COLLATE NOCASE
         1429  +} {
         1430  +  SELECT * FROM t8 EXCEPT SELECT c, d FROM t9 ORDER BY d, t8.a COLLATE NOCASE
         1431  +}
         1432  +
         1433  +do_catchsql_test 5.4 {
         1434  +  SELECT * FROM t8 UNION SELECT * FROM t9 ORDER BY a+b COLLATE NOCASE
         1435  +} {1 {1st ORDER BY term does not match any column in the result set}}
         1436  +
  1377   1437   
  1378   1438   finish_test

Changes to test/walblock.test.

    11     11   #
    12     12   
    13     13   set testdir [file dirname $argv0]
    14     14   source $testdir/tester.tcl
    15     15   source $testdir/lock_common.tcl
    16     16   source $testdir/wal_common.tcl
    17     17   
           18  +finish_test; return;    #  Feature currently not implemented.
    18     19   ifcapable !wal {finish_test ; return }
    19     20   if {$::tcl_platform(platform)!="unix"} { finish_test ; return }
    20     21   set testprefix walblock
    21     22   
    22     23   catch { db close }
    23     24   testvfs tvfs -fullshm 1
    24     25   foreach f [glob test.db*] { forcedelete $f }
................................................................................
   107    108     after 500 {set ::continue 1}
   108    109     vwait ::continue
   109    110     set ::out 
   110    111   } {1 2 3 4 5 6 7 8 9 10}
   111    112   
   112    113   
   113    114   finish_test
   114         -
   115         -
   116         -
   117         -

Added tool/showlocks.c.

            1  +/*
            2  +** This file implements a simple command-line utility that shows all of the
            3  +** Posix Advisory Locks on a file.
            4  +**
            5  +** Usage:
            6  +**
            7  +**     showlocks FILENAME
            8  +**
            9  +** To compile:  gcc -o showlocks showlocks.c
           10  +*/
           11  +#include <stdio.h>
           12  +#include <unistd.h>
           13  +#include <fcntl.h>
           14  +#include <stdlib.h>
           15  +#include <string.h>
           16  +
           17  +/* This utility only looks for locks in the first 2 billion bytes */
           18  +#define MX_LCK 2147483647
           19  +
           20  +/*
           21  +** Print all locks on the inode of "fd" that occur in between
           22  +** lwr and upr, inclusive.
           23  +*/
           24  +static int showLocksInRange(int fd, off_t lwr, off_t upr){
           25  +  int cnt = 0;
           26  +  struct flock x;
           27  +
           28  +  x.l_type = F_WRLCK;
           29  +  x.l_whence = SEEK_SET;
           30  +  x.l_start = lwr;
           31  +  x.l_len = upr-lwr;
           32  +  fcntl(fd, F_GETLK, &x);
           33  +  if( x.l_type==F_UNLCK ) return 0;
           34  +  printf("start: %-12d len: %-5d pid: %-5d type: %s\n",
           35  +       (int)x.l_start, (int)x.l_len,
           36  +       x.l_pid, x.l_type==F_WRLCK ? "WRLCK" : "RDLCK");
           37  +  cnt++;
           38  +  if( x.l_start>lwr ){
           39  +    cnt += showLocksInRange(fd, lwr, x.l_start-1);
           40  +  }
           41  +  if( x.l_start+x.l_len<upr ){
           42  +    cnt += showLocksInRange(fd, x.l_start+x.l_len+1, upr);
           43  +  }
           44  +  return cnt;
           45  +}
           46  +
           47  +int main(int argc, char **argv){
           48  +  int fd;
           49  +  int cnt;
           50  +
           51  +  if( argc!=2 ){
           52  +    fprintf(stderr, "Usage: %s FILENAME\n", argv[0]);
           53  +    return 1;
           54  +  }
           55  +  fd = open(argv[1], O_RDWR, 0);
           56  +  if( fd<0 ){
           57  +    fprintf(stderr, "%s: cannot open %s\n", argv[0], argv[1]);
           58  +    return 1;
           59  +  }
           60  +  cnt = showLocksInRange(fd, 0, MX_LCK);
           61  +  if( cnt==0 ) printf("no locks\n");  
           62  +  close(fd);
           63  +  return 0;
           64  +}