/ Check-in [ebc9433f]
Login

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

Overview
Comment:Fix for [e9a9fde1f4]. When opening an existing rtree, determine the node size by inspecting the root node of the r-tree structure (instead of assuming it is a function of the page-size).
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: ebc9433fddf78ef7b4237686951d8d79c1c98f03
User & Date: dan 2010-02-16 10:59:41
References
2017-08-17
02:25 New ticket [be436a7f] Use-after-free on schema change where RTREE is used inside of a trigger. artifact: 580eefd3 user: drh
Context
2010-02-16
12:18
Change a C++ style comment in os_unix.c to use normal C style commenting. check-in: 7a193309 user: dan tags: trunk
10:59
Fix for [e9a9fde1f4]. When opening an existing rtree, determine the node size by inspecting the root node of the r-tree structure (instead of assuming it is a function of the page-size). check-in: ebc9433f user: dan tags: trunk
2010-02-15
18:03
Fix the ALTER TABLE RENAME command so that it converts FOREIGN KEY constraints in ATTACH-ed and in TEMP tables as well as in the main database. Ticket [13336e9c3c8c3f]. check-in: ab197d0a user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to ext/rtree/rtree.c.

   419    419     pNode->isDirty = 0;
   420    420     pNode->pNext = 0;
   421    421   
   422    422     sqlite3_bind_int64(pRtree->pReadNode, 1, iNode);
   423    423     rc = sqlite3_step(pRtree->pReadNode);
   424    424     if( rc==SQLITE_ROW ){
   425    425       const u8 *zBlob = sqlite3_column_blob(pRtree->pReadNode, 0);
          426  +    assert( sqlite3_column_bytes(pRtree->pReadNode, 0)==pRtree->iNodeSize );
   426    427       memcpy(pNode->zData, zBlob, pRtree->iNodeSize);
   427    428       nodeReference(pParent);
   428    429     }else{
   429    430       sqlite3_free(pNode);
   430    431       pNode = 0;
   431    432     }
   432    433   
................................................................................
  2615   2616       sqlite3_free(zSql);
  2616   2617     }
  2617   2618   
  2618   2619     return rc;
  2619   2620   }
  2620   2621   
  2621   2622   /*
  2622         -** This routine queries database handle db for the page-size used by
  2623         -** database zDb. If successful, the page-size in bytes is written to
  2624         -** *piPageSize and SQLITE_OK returned. Otherwise, and an SQLite error 
  2625         -** code is returned.
         2623  +** The second argument to this function contains the text of an SQL statement
         2624  +** that returns a single integer value. The statement is compiled and executed
         2625  +** using database connection db. If successful, the integer value returned
         2626  +** is written to *piVal and SQLITE_OK returned. Otherwise, an SQLite error
         2627  +** code is returned and the value of *piVal after returning is not defined.
  2626   2628   */
  2627         -static int getPageSize(sqlite3 *db, const char *zDb, int *piPageSize){
         2629  +static int getIntFromStmt(sqlite3 *db, const char *zSql, int *piVal){
  2628   2630     int rc = SQLITE_NOMEM;
         2631  +  if( zSql ){
         2632  +    sqlite3_stmt *pStmt = 0;
         2633  +    rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
         2634  +    if( rc==SQLITE_OK ){
         2635  +      if( SQLITE_ROW==sqlite3_step(pStmt) ){
         2636  +        *piVal = sqlite3_column_int(pStmt, 0);
         2637  +      }
         2638  +      rc = sqlite3_finalize(pStmt);
         2639  +    }
         2640  +  }
         2641  +  return rc;
         2642  +}
         2643  +
         2644  +/*
         2645  +** This function is called from within the xConnect() or xCreate() method to
         2646  +** determine the node-size used by the rtree table being created or connected
         2647  +** to. If successful, pRtree->iNodeSize is populated and SQLITE_OK returned.
         2648  +** Otherwise, an SQLite error code is returned.
         2649  +**
         2650  +** If this function is being called as part of an xConnect(), then the rtree
         2651  +** table already exists. In this case the node-size is determined by inspecting
         2652  +** the root node of the tree.
         2653  +**
         2654  +** Otherwise, for an xCreate(), use 64 bytes less than the database page-size. 
         2655  +** This ensures that each node is stored on a single database page. If the 
         2656  +** database page-size is so large that more than RTREE_MAXCELLS entries 
         2657  +** would fit in a single node, use a smaller node-size.
         2658  +*/
         2659  +static int getNodeSize(
         2660  +  sqlite3 *db,                    /* Database handle */
         2661  +  Rtree *pRtree,                  /* Rtree handle */
         2662  +  int isCreate                    /* True for xCreate, false for xConnect */
         2663  +){
         2664  +  int rc;
  2629   2665     char *zSql;
  2630         -  sqlite3_stmt *pStmt = 0;
  2631         -
  2632         -  zSql = sqlite3_mprintf("PRAGMA %Q.page_size", zDb);
  2633         -  if( !zSql ){
  2634         -    return SQLITE_NOMEM;
         2666  +  if( isCreate ){
         2667  +    int iPageSize;
         2668  +    zSql = sqlite3_mprintf("PRAGMA %Q.page_size", pRtree->zDb);
         2669  +    rc = getIntFromStmt(db, zSql, &iPageSize);
         2670  +    if( rc==SQLITE_OK ){
         2671  +      pRtree->iNodeSize = iPageSize-64;
         2672  +      if( (4+pRtree->nBytesPerCell*RTREE_MAXCELLS)<pRtree->iNodeSize ){
         2673  +        pRtree->iNodeSize = 4+pRtree->nBytesPerCell*RTREE_MAXCELLS;
         2674  +      }
         2675  +    }
         2676  +  }else{
         2677  +    zSql = sqlite3_mprintf(
         2678  +        "SELECT length(data) FROM '%q'.'%q_node' WHERE nodeno = 1",
         2679  +        pRtree->zDb, pRtree->zName
         2680  +    );
         2681  +    rc = getIntFromStmt(db, zSql, &pRtree->iNodeSize);
  2635   2682     }
  2636   2683   
  2637         -  rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
  2638   2684     sqlite3_free(zSql);
  2639         -  if( rc!=SQLITE_OK ){
  2640         -    return rc;
  2641         -  }
  2642         -
  2643         -  if( SQLITE_ROW==sqlite3_step(pStmt) ){
  2644         -    *piPageSize = sqlite3_column_int(pStmt, 0);
  2645         -  }
  2646         -  return sqlite3_finalize(pStmt);
         2685  +  return rc;
  2647   2686   }
  2648   2687   
  2649   2688   /* 
  2650   2689   ** This function is the implementation of both the xConnect and xCreate
  2651   2690   ** methods of the r-tree virtual table.
  2652   2691   **
  2653   2692   **   argv[0]   -> module name
................................................................................
  2679   2718   
  2680   2719     int iErr = (argc<6) ? 2 : argc>(RTREE_MAX_DIMENSIONS*2+4) ? 3 : argc%2;
  2681   2720     if( aErrMsg[iErr] ){
  2682   2721       *pzErr = sqlite3_mprintf("%s", aErrMsg[iErr]);
  2683   2722       return SQLITE_ERROR;
  2684   2723     }
  2685   2724   
  2686         -  rc = getPageSize(db, argv[1], &iPageSize);
  2687         -  if( rc!=SQLITE_OK ){
  2688         -    return rc;
  2689         -  }
  2690         -
  2691   2725     /* Allocate the sqlite3_vtab structure */
  2692   2726     nDb = strlen(argv[1]);
  2693   2727     nName = strlen(argv[2]);
  2694   2728     pRtree = (Rtree *)sqlite3_malloc(sizeof(Rtree)+nDb+nName+2);
  2695   2729     if( !pRtree ){
  2696   2730       return SQLITE_NOMEM;
  2697   2731     }
................................................................................
  2702   2736     pRtree->zName = &pRtree->zDb[nDb+1];
  2703   2737     pRtree->nDim = (argc-4)/2;
  2704   2738     pRtree->nBytesPerCell = 8 + pRtree->nDim*4*2;
  2705   2739     pRtree->eCoordType = eCoordType;
  2706   2740     memcpy(pRtree->zDb, argv[1], nDb);
  2707   2741     memcpy(pRtree->zName, argv[2], nName);
  2708   2742   
  2709         -  /* Figure out the node size to use. By default, use 64 bytes less than
  2710         -  ** the database page-size. This ensures that each node is stored on
  2711         -  ** a single database page.
  2712         -  **
  2713         -  ** If the databasd page-size is so large that more than RTREE_MAXCELLS
  2714         -  ** entries would fit in a single node, use a smaller node-size.
  2715         -  */
  2716         -  pRtree->iNodeSize = iPageSize-64;
  2717         -  if( (4+pRtree->nBytesPerCell*RTREE_MAXCELLS)<pRtree->iNodeSize ){
  2718         -    pRtree->iNodeSize = 4+pRtree->nBytesPerCell*RTREE_MAXCELLS;
  2719         -  }
         2743  +  /* Figure out the node size to use. */
         2744  +  rc = getNodeSize(db, pRtree, isCreate);
  2720   2745   
  2721   2746     /* Create/Connect to the underlying relational database schema. If
  2722   2747     ** that is successful, call sqlite3_declare_vtab() to configure
  2723   2748     ** the r-tree table schema.
  2724   2749     */
  2725         -  if( (rc = rtreeSqlInit(pRtree, db, argv[1], argv[2], isCreate)) ){
  2726         -    *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
  2727         -  }else{
  2728         -    char *zSql = sqlite3_mprintf("CREATE TABLE x(%s", argv[3]);
  2729         -    char *zTmp;
  2730         -    int ii;
  2731         -    for(ii=4; zSql && ii<argc; ii++){
  2732         -      zTmp = zSql;
  2733         -      zSql = sqlite3_mprintf("%s, %s", zTmp, argv[ii]);
  2734         -      sqlite3_free(zTmp);
  2735         -    }
  2736         -    if( zSql ){
  2737         -      zTmp = zSql;
  2738         -      zSql = sqlite3_mprintf("%s);", zTmp);
  2739         -      sqlite3_free(zTmp);
  2740         -    }
  2741         -    if( !zSql ){
  2742         -      rc = SQLITE_NOMEM;
  2743         -    }else if( SQLITE_OK!=(rc = sqlite3_declare_vtab(db, zSql)) ){
         2750  +  if( rc==SQLITE_OK ){
         2751  +    if( (rc = rtreeSqlInit(pRtree, db, argv[1], argv[2], isCreate)) ){
  2744   2752         *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
         2753  +    }else{
         2754  +      char *zSql = sqlite3_mprintf("CREATE TABLE x(%s", argv[3]);
         2755  +      char *zTmp;
         2756  +      int ii;
         2757  +      for(ii=4; zSql && ii<argc; ii++){
         2758  +        zTmp = zSql;
         2759  +        zSql = sqlite3_mprintf("%s, %s", zTmp, argv[ii]);
         2760  +        sqlite3_free(zTmp);
         2761  +      }
         2762  +      if( zSql ){
         2763  +        zTmp = zSql;
         2764  +        zSql = sqlite3_mprintf("%s);", zTmp);
         2765  +        sqlite3_free(zTmp);
         2766  +      }
         2767  +      if( !zSql ){
         2768  +        rc = SQLITE_NOMEM;
         2769  +      }else if( SQLITE_OK!=(rc = sqlite3_declare_vtab(db, zSql)) ){
         2770  +        *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
         2771  +      }
         2772  +      sqlite3_free(zSql);
  2745   2773       }
  2746         -    sqlite3_free(zSql);
  2747   2774     }
  2748   2775   
  2749   2776     if( rc==SQLITE_OK ){
  2750   2777       *ppVtab = (sqlite3_vtab *)pRtree;
  2751   2778     }else{
  2752   2779       rtreeRelease(pRtree);
  2753   2780     }

Added ext/rtree/rtree7.test.

            1  +# 2010 February 16
            2  +#
            3  +# The author disclaims copyright to this source code.  In place of
            4  +# a legal notice, here is a blessing:
            5  +#
            6  +#    May you do good and not evil.
            7  +#    May you find forgiveness for yourself and forgive others.
            8  +#    May you share freely, never taking more than you give.
            9  +#
           10  +#***********************************************************************
           11  +# 
           12  +# Test that nothing goes wrong if an rtree table is created, then the
           13  +# database page-size is modified. At one point (3.6.22), this was causing
           14  +# malfunctions.
           15  +#
           16  +
           17  +if {![info exists testdir]} {
           18  +  set testdir [file join [file dirname $argv0] .. .. test]
           19  +} 
           20  +source $testdir/tester.tcl
           21  +
           22  +ifcapable !rtree||!vacuum {
           23  +  finish_test
           24  +  return
           25  +}
           26  +
           27  +do_test rtree7-1.1 {
           28  +  execsql {
           29  +    PRAGMA page_size = 1024;
           30  +    CREATE VIRTUAL TABLE rt USING rtree(id, x1, x2, y1, y2);
           31  +    INSERT INTO rt VALUES(1, 1, 2, 3, 4);
           32  +  }
           33  +} {}
           34  +do_test rtree7-1.2 {
           35  +  execsql { SELECT * FROM rt }
           36  +} {1 1.0 2.0 3.0 4.0}
           37  +do_test rtree7-1.3 {
           38  +  execsql { 
           39  +    PRAGMA page_size = 2048;
           40  +    VACUUM;
           41  +    SELECT * FROM rt;
           42  +  }
           43  +} {1 1.0 2.0 3.0 4.0}
           44  +do_test rtree7-1.4 {
           45  +  for {set i 2} {$i <= 51} {incr i} {
           46  +    execsql { INSERT INTO rt VALUES($i, 1, 2, 3, 4) }
           47  +  }
           48  +  execsql { SELECT sum(x1), sum(x2), sum(y1), sum(y2) FROM rt }
           49  +} {51.0 102.0 153.0 204.0}
           50  +do_test rtree7-1.5 {
           51  +  execsql { 
           52  +    PRAGMA page_size = 512;
           53  +    VACUUM;
           54  +    SELECT sum(x1), sum(x2), sum(y1), sum(y2) FROM rt
           55  +  }
           56  +} {51.0 102.0 153.0 204.0}
           57  +
           58  +finish_test