/ 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 Unified Diffs Show Whitespace Changes Patch

Changes to ext/rtree/rtree.c.

419
420
421
422
423
424
425

426
427
428
429
430
431
432
....
2615
2616
2617
2618
2619
2620
2621
2622
2623

2624
2625
2626
2627
2628
2629
2630




2631
2632
2633



2634
2635
2636
2637
2638


























2639
2640



2641
2642
2643
2644






2645
2646



2647
2648
2649
2650
2651
2652
2653
....
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
....
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724

2725
2726
2727
2728
2729
2730
2731
....
2741
2742
2743
2744
2745
2746
2747

2748
2749
2750
2751
2752
2753
2754
  pNode->isDirty = 0;
  pNode->pNext = 0;

  sqlite3_bind_int64(pRtree->pReadNode, 1, iNode);
  rc = sqlite3_step(pRtree->pReadNode);
  if( rc==SQLITE_ROW ){
    const u8 *zBlob = sqlite3_column_blob(pRtree->pReadNode, 0);

    memcpy(pNode->zData, zBlob, pRtree->iNodeSize);
    nodeReference(pParent);
  }else{
    sqlite3_free(pNode);
    pNode = 0;
  }

................................................................................
    sqlite3_free(zSql);
  }

  return rc;
}

/*
** This routine queries database handle db for the page-size used by
** database zDb. If successful, the page-size in bytes is written to

** *piPageSize and SQLITE_OK returned. Otherwise, and an SQLite error 
** code is returned.
*/
static int getPageSize(sqlite3 *db, const char *zDb, int *piPageSize){
  int rc = SQLITE_NOMEM;
  char *zSql;
  sqlite3_stmt *pStmt = 0;





  zSql = sqlite3_mprintf("PRAGMA %Q.page_size", zDb);
  if( !zSql ){



    return SQLITE_NOMEM;
  }

  rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
  sqlite3_free(zSql);


























  if( rc!=SQLITE_OK ){
    return rc;



  }

  if( SQLITE_ROW==sqlite3_step(pStmt) ){
    *piPageSize = sqlite3_column_int(pStmt, 0);






  }
  return sqlite3_finalize(pStmt);



}

/* 
** This function is the implementation of both the xConnect and xCreate
** methods of the r-tree virtual table.
**
**   argv[0]   -> module name
................................................................................

  int iErr = (argc<6) ? 2 : argc>(RTREE_MAX_DIMENSIONS*2+4) ? 3 : argc%2;
  if( aErrMsg[iErr] ){
    *pzErr = sqlite3_mprintf("%s", aErrMsg[iErr]);
    return SQLITE_ERROR;
  }

  rc = getPageSize(db, argv[1], &iPageSize);
  if( rc!=SQLITE_OK ){
    return rc;
  }

  /* Allocate the sqlite3_vtab structure */
  nDb = strlen(argv[1]);
  nName = strlen(argv[2]);
  pRtree = (Rtree *)sqlite3_malloc(sizeof(Rtree)+nDb+nName+2);
  if( !pRtree ){
    return SQLITE_NOMEM;
  }
................................................................................
  pRtree->zName = &pRtree->zDb[nDb+1];
  pRtree->nDim = (argc-4)/2;
  pRtree->nBytesPerCell = 8 + pRtree->nDim*4*2;
  pRtree->eCoordType = eCoordType;
  memcpy(pRtree->zDb, argv[1], nDb);
  memcpy(pRtree->zName, argv[2], nName);

  /* Figure out the node size to use. By default, use 64 bytes less than
  ** the database page-size. This ensures that each node is stored on
  ** a single database page.
  **
  ** If the databasd page-size is so large that more than RTREE_MAXCELLS
  ** entries would fit in a single node, use a smaller node-size.
  */
  pRtree->iNodeSize = iPageSize-64;
  if( (4+pRtree->nBytesPerCell*RTREE_MAXCELLS)<pRtree->iNodeSize ){
    pRtree->iNodeSize = 4+pRtree->nBytesPerCell*RTREE_MAXCELLS;
  }

  /* Create/Connect to the underlying relational database schema. If
  ** that is successful, call sqlite3_declare_vtab() to configure
  ** the r-tree table schema.
  */

  if( (rc = rtreeSqlInit(pRtree, db, argv[1], argv[2], isCreate)) ){
    *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
  }else{
    char *zSql = sqlite3_mprintf("CREATE TABLE x(%s", argv[3]);
    char *zTmp;
    int ii;
    for(ii=4; zSql && ii<argc; ii++){
................................................................................
    if( !zSql ){
      rc = SQLITE_NOMEM;
    }else if( SQLITE_OK!=(rc = sqlite3_declare_vtab(db, zSql)) ){
      *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
    }
    sqlite3_free(zSql);
  }


  if( rc==SQLITE_OK ){
    *ppVtab = (sqlite3_vtab *)pRtree;
  }else{
    rtreeRelease(pRtree);
  }
  return rc;







>







 







|
|
>
|
|

|

|
|
>
>
>
>
|
<
<
>
>
>
|
|

<
<
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
<
>
>
>
|
|
<
<
>
>
>
>
>
>

<
>
>
>







 







<
<
<
<
<







 







|
|
<
<
<
<
<
<
<
<
<





>







 







>







419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
....
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637


2638
2639
2640
2641
2642
2643


2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670

2671
2672
2673
2674
2675


2676
2677
2678
2679
2680
2681
2682

2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
....
2718
2719
2720
2721
2722
2723
2724





2725
2726
2727
2728
2729
2730
2731
....
2736
2737
2738
2739
2740
2741
2742
2743
2744









2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
....
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
  pNode->isDirty = 0;
  pNode->pNext = 0;

  sqlite3_bind_int64(pRtree->pReadNode, 1, iNode);
  rc = sqlite3_step(pRtree->pReadNode);
  if( rc==SQLITE_ROW ){
    const u8 *zBlob = sqlite3_column_blob(pRtree->pReadNode, 0);
    assert( sqlite3_column_bytes(pRtree->pReadNode, 0)==pRtree->iNodeSize );
    memcpy(pNode->zData, zBlob, pRtree->iNodeSize);
    nodeReference(pParent);
  }else{
    sqlite3_free(pNode);
    pNode = 0;
  }

................................................................................
    sqlite3_free(zSql);
  }

  return rc;
}

/*
** The second argument to this function contains the text of an SQL statement
** that returns a single integer value. The statement is compiled and executed
** using database connection db. If successful, the integer value returned
** is written to *piVal and SQLITE_OK returned. Otherwise, an SQLite error
** code is returned and the value of *piVal after returning is not defined.
*/
static int getIntFromStmt(sqlite3 *db, const char *zSql, int *piVal){
  int rc = SQLITE_NOMEM;
  if( zSql ){
    sqlite3_stmt *pStmt = 0;
    rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
    if( rc==SQLITE_OK ){
      if( SQLITE_ROW==sqlite3_step(pStmt) ){
        *piVal = sqlite3_column_int(pStmt, 0);
      }


      rc = sqlite3_finalize(pStmt);
    }
  }
  return rc;
}



/*
** This function is called from within the xConnect() or xCreate() method to
** determine the node-size used by the rtree table being created or connected
** to. If successful, pRtree->iNodeSize is populated and SQLITE_OK returned.
** Otherwise, an SQLite error code is returned.
**
** If this function is being called as part of an xConnect(), then the rtree
** table already exists. In this case the node-size is determined by inspecting
** the root node of the tree.
**
** Otherwise, for an xCreate(), use 64 bytes less than the database page-size. 
** This ensures that each node is stored on a single database page. If the 
** database page-size is so large that more than RTREE_MAXCELLS entries 
** would fit in a single node, use a smaller node-size.
*/
static int getNodeSize(
  sqlite3 *db,                    /* Database handle */
  Rtree *pRtree,                  /* Rtree handle */
  int isCreate                    /* True for xCreate, false for xConnect */
){
  int rc;
  char *zSql;
  if( isCreate ){
    int iPageSize;
    zSql = sqlite3_mprintf("PRAGMA %Q.page_size", pRtree->zDb);
    rc = getIntFromStmt(db, zSql, &iPageSize);
    if( rc==SQLITE_OK ){

      pRtree->iNodeSize = iPageSize-64;
      if( (4+pRtree->nBytesPerCell*RTREE_MAXCELLS)<pRtree->iNodeSize ){
        pRtree->iNodeSize = 4+pRtree->nBytesPerCell*RTREE_MAXCELLS;
      }
    }


  }else{
    zSql = sqlite3_mprintf(
        "SELECT length(data) FROM '%q'.'%q_node' WHERE nodeno = 1",
        pRtree->zDb, pRtree->zName
    );
    rc = getIntFromStmt(db, zSql, &pRtree->iNodeSize);
  }


  sqlite3_free(zSql);
  return rc;
}

/* 
** This function is the implementation of both the xConnect and xCreate
** methods of the r-tree virtual table.
**
**   argv[0]   -> module name
................................................................................

  int iErr = (argc<6) ? 2 : argc>(RTREE_MAX_DIMENSIONS*2+4) ? 3 : argc%2;
  if( aErrMsg[iErr] ){
    *pzErr = sqlite3_mprintf("%s", aErrMsg[iErr]);
    return SQLITE_ERROR;
  }






  /* Allocate the sqlite3_vtab structure */
  nDb = strlen(argv[1]);
  nName = strlen(argv[2]);
  pRtree = (Rtree *)sqlite3_malloc(sizeof(Rtree)+nDb+nName+2);
  if( !pRtree ){
    return SQLITE_NOMEM;
  }
................................................................................
  pRtree->zName = &pRtree->zDb[nDb+1];
  pRtree->nDim = (argc-4)/2;
  pRtree->nBytesPerCell = 8 + pRtree->nDim*4*2;
  pRtree->eCoordType = eCoordType;
  memcpy(pRtree->zDb, argv[1], nDb);
  memcpy(pRtree->zName, argv[2], nName);

  /* Figure out the node size to use. */
  rc = getNodeSize(db, pRtree, isCreate);










  /* Create/Connect to the underlying relational database schema. If
  ** that is successful, call sqlite3_declare_vtab() to configure
  ** the r-tree table schema.
  */
  if( rc==SQLITE_OK ){
    if( (rc = rtreeSqlInit(pRtree, db, argv[1], argv[2], isCreate)) ){
      *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
    }else{
      char *zSql = sqlite3_mprintf("CREATE TABLE x(%s", argv[3]);
      char *zTmp;
      int ii;
      for(ii=4; zSql && ii<argc; ii++){
................................................................................
      if( !zSql ){
        rc = SQLITE_NOMEM;
      }else if( SQLITE_OK!=(rc = sqlite3_declare_vtab(db, zSql)) ){
        *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
      }
      sqlite3_free(zSql);
    }
  }

  if( rc==SQLITE_OK ){
    *ppVtab = (sqlite3_vtab *)pRtree;
  }else{
    rtreeRelease(pRtree);
  }
  return rc;

Added ext/rtree/rtree7.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
# 2010 February 16
#
# 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.
#
#***********************************************************************
# 
# Test that nothing goes wrong if an rtree table is created, then the
# database page-size is modified. At one point (3.6.22), this was causing
# malfunctions.
#

if {![info exists testdir]} {
  set testdir [file join [file dirname $argv0] .. .. test]
} 
source $testdir/tester.tcl

ifcapable !rtree||!vacuum {
  finish_test
  return
}

do_test rtree7-1.1 {
  execsql {
    PRAGMA page_size = 1024;
    CREATE VIRTUAL TABLE rt USING rtree(id, x1, x2, y1, y2);
    INSERT INTO rt VALUES(1, 1, 2, 3, 4);
  }
} {}
do_test rtree7-1.2 {
  execsql { SELECT * FROM rt }
} {1 1.0 2.0 3.0 4.0}
do_test rtree7-1.3 {
  execsql { 
    PRAGMA page_size = 2048;
    VACUUM;
    SELECT * FROM rt;
  }
} {1 1.0 2.0 3.0 4.0}
do_test rtree7-1.4 {
  for {set i 2} {$i <= 51} {incr i} {
    execsql { INSERT INTO rt VALUES($i, 1, 2, 3, 4) }
  }
  execsql { SELECT sum(x1), sum(x2), sum(y1), sum(y2) FROM rt }
} {51.0 102.0 153.0 204.0}
do_test rtree7-1.5 {
  execsql { 
    PRAGMA page_size = 512;
    VACUUM;
    SELECT sum(x1), sum(x2), sum(y1), sum(y2) FROM rt
  }
} {51.0 102.0 153.0 204.0}

finish_test