/ Check-in [e9c5e189]
Login

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

Overview
Comment:Fix memory leaks on this branch.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | reuse-schema
Files: files | file ages | folders
SHA3-256: e9c5e1891ff4f7e57131031f068efea87027ddab0cd7846e0514a105853be47d
User & Date: dan 2019-02-05 19:15:36
Wiki:reuse-schema
Context
2019-02-05
19:51
Merge latest trunk into this branch. check-in: c089cc4f user: dan tags: reuse-schema
19:15
Fix memory leaks on this branch. check-in: e9c5e189 user: dan tags: reuse-schema
2019-02-04
21:02
Fix a problem with reloading the schema on this branch. check-in: 5dfbef83 user: dan tags: reuse-schema
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/callback.c.

12
13
14
15
16
17
18















19
20
21
22
23
24
25






























26
27
28
29
30
31
32
...
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
...
522
523
524
525
526
527
528












529
530
531
532
533
534
535
...
576
577
578
579
580
581
582
583


584
585
586






587
588
589

590












591

592
593
594
595
596
597
598
599
600
601
602
603
604

605
606


607
608
609
610
611
612
613
...
623
624
625
626
627
628
629


630
631
632
633
634
635
636
637
638
639
640
641
642

643
644
645
646
647
648
649
**
** This file contains functions used to access the internal hash tables
** of user defined functions and collation sequences.
*/

#include "sqliteInt.h"
















struct SchemaPool {
  int nRef;                       /* Number of pointers to this object */
  u64 cksum;                      /* Checksum for this Schema contents */
  Schema *pSchema;                /* Linked list of Schema objects */
  Schema sSchema;                 /* The single dummy schema object */
  SchemaPool *pNext;              /* Next element in schemaPoolList */
};































/*
** Invoke the 'collation needed' callback to request a collation sequence
** in the encoding enc of name zName, length nName.
*/
static void callCollNeeded(sqlite3 *db, int enc, const char *zName){
  assert( !db->xCollNeeded || !db->xCollNeeded16 );
................................................................................

void sqlite3SchemaZero(sqlite3 *db, int iDb){
  Db *pDb = &db->aDb[iDb];
  if( IsReuseSchema(db) && iDb!=1 ){
    if( pDb->pSPool ){
      Schema *pNew = sqlite3SchemaGet(db, 0);
      if( pNew ){
        sqlite3SchemaDisconnect(db, iDb);
        pDb->pSchema = pNew;
      }
      return;
    }
  }
  sqlite3SchemaClear(pDb->pSchema);
}
................................................................................
  }
}

static void schemaDelete(Schema *pSchema){
  sqlite3SchemaClear((void*)pSchema);
  sqlite3_free(pSchema);
}













/*
** The schema for database iDb of database handle db, which was opened
** with SQLITE_OPEN_REUSE_SCHEMA, has just been parsed. This function either
** finds a matching SchemaPool object on the global list (schemaPoolList) or
** else allocates a new one and sets the Db.pSPool variable accordingly.
*/
................................................................................

  sqlite3_mutex_leave( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );

  db->aDb[iDb].pSPool = p;
  return (p ? SQLITE_OK : SQLITE_NOMEM);
}

void sqlite3SchemaDisconnect(sqlite3 *db, int iDb){


  Db *pDb = &db->aDb[iDb];
  if( pDb->pSPool ){
    SchemaPool *pSPool = pDb->pSPool;






    pDb->pSPool = 0;
    assert( iDb!=1 );


    sqlite3_mutex_enter( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );












    assert( pSPool->nRef>=1 );

    pSPool->nRef--;
    if( pSPool->nRef<=0 ){
      Schema *pNext;
      SchemaPool **pp;
      while( pSPool->pSchema ){
        Schema *pNext = pSPool->pSchema->pNext;
        schemaDelete(pSPool->pSchema);
        pSPool->pSchema = pNext;
      }
      for(pp=&schemaPoolList; (*pp)!=pSPool; pp=&((*pp)->pNext));
      *pp = pSPool->pNext;
      sqlite3_free(pSPool);
    }

    sqlite3_mutex_leave( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
  }


}

/*
** Extract and return a pointer to a schema object from the SchemaPool passed
** as the only argument, if one is available. If one is not available, return
** NULL.
*/
................................................................................
    sqlite3_mutex_leave( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
  }
  return pRet;
}

void sqlite3SchemaReleaseAll(sqlite3 *db){
  int i;


  for(i=0; i<db->nDb; i++){
    if( i!=1 ){
      Db *pDb = &db->aDb[i];
      if( pDb->pSPool && pDb->pSchema && DbHasProperty(db,i,DB_SchemaLoaded) ){
        sqlite3_mutex_enter( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
        pDb->pSchema->pNext = pDb->pSPool->pSchema;
        pDb->pSPool->pSchema = pDb->pSchema;
        pDb->pSchema = &pDb->pSPool->sSchema;
        assert( DbHasProperty(db, i, DB_SchemaLoaded)==0 );
        sqlite3_mutex_leave( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
      }
    }
  }

}

/*
** Find and return the schema associated with a BTree.  Create
** a new one if necessary.
*/
Schema *sqlite3SchemaGet(sqlite3 *db, Btree *pBt){







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







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







 







|







 







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







 







|
>
>
|
<

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







 







>
>




|
<
<
<
<
<



>







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
...
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
...
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
...
633
634
635
636
637
638
639
640
641
642
643

644
645
646
647
648
649
650
651

652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
...
703
704
705
706
707
708
709
710
711
712
713
714
715
716





717
718
719
720
721
722
723
724
725
726
727
**
** This file contains functions used to access the internal hash tables
** of user defined functions and collation sequences.
*/

#include "sqliteInt.h"

/*
** Connections opened with the SQLITE_OPEN_REUSE_SCHEMA flag specified
** may use SchemaPool objects for any database that is not the temp db
** (iDb==1). For such databases (type "struct Db") there are three states
** the Schema/SchemaPool object may be in.
**
**   1) pSPool==0, pSchema points to an empty object allocated by
**      sqlite3_malloc(). DB_SchemaLoaded flag is clear.
**
**   2) pSPool!=0, pSchema points to a populated object owned by the
**      SchemaPool. DB_SchemaLoaded flag is set.
**
**   3) pSPool!=0, pSchema points to the SchemaPool's static object
**      (SchemaPool.sSchema).
*/
struct SchemaPool {
  int nRef;                       /* Number of pointers to this object */
  u64 cksum;                      /* Checksum for this Schema contents */
  Schema *pSchema;                /* Linked list of Schema objects */
  Schema sSchema;                 /* The single dummy schema object */
  SchemaPool *pNext;              /* Next element in schemaPoolList */
};

#ifdef SQLITE_DEBUG
static void assert_schema_state_ok(sqlite3 *db){
  if( IsReuseSchema(db) ){
    int i;
    for(i=0; i<db->nDb; i++){
      if( i!=1 ){
        Db *pDb = &db->aDb[i];
        Btree *pBt = pDb->pBt;
        if( pDb->pSPool ){
          if( DbHasProperty(db, i, DB_SchemaLoaded)==0 ){
            assert( pDb->pSchema->tblHash.count==0 );
            assert( pDb->pSchema==&pDb->pSPool->sSchema );
          }else{
            assert( pBt==0 || pDb->pSchema!=sqlite3BtreeSchema(pBt, 0, 0) );
            assert( pDb->pSchema!=&pDb->pSPool->sSchema );
          }
        }else{
          assert( DbHasProperty(db, i, DB_SchemaLoaded)==0 );
          assert( pDb->pSchema->tblHash.count==0 );
          assert( pBt==0 || pDb->pSchema!=sqlite3BtreeSchema(pBt, 0, 0) );
          assert( pDb->pSchema!=&pDb->pSPool->sSchema );
        }
      }
    }
  }
}
#else
# define assert_schema_state_ok(x)
#endif

/*
** Invoke the 'collation needed' callback to request a collation sequence
** in the encoding enc of name zName, length nName.
*/
static void callCollNeeded(sqlite3 *db, int enc, const char *zName){
  assert( !db->xCollNeeded || !db->xCollNeeded16 );
................................................................................

void sqlite3SchemaZero(sqlite3 *db, int iDb){
  Db *pDb = &db->aDb[iDb];
  if( IsReuseSchema(db) && iDb!=1 ){
    if( pDb->pSPool ){
      Schema *pNew = sqlite3SchemaGet(db, 0);
      if( pNew ){
        sqlite3SchemaDisconnect(db, iDb, 0);
        pDb->pSchema = pNew;
      }
      return;
    }
  }
  sqlite3SchemaClear(pDb->pSchema);
}
................................................................................
  }
}

static void schemaDelete(Schema *pSchema){
  sqlite3SchemaClear((void*)pSchema);
  sqlite3_free(pSchema);
}

static void schemaRelease(Db *pDb){
  assert( pDb->pSPool && pDb->pSchema );
  assert( pDb->pSchema->schemaFlags & DB_SchemaLoaded );
  assert( sqlite3_mutex_held(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER)) );

  pDb->pSchema->pNext = pDb->pSPool->pSchema;
  pDb->pSPool->pSchema = pDb->pSchema;
  pDb->pSchema = &pDb->pSPool->sSchema;

  assert( (pDb->pSchema->schemaFlags & DB_SchemaLoaded)==0 );
}

/*
** The schema for database iDb of database handle db, which was opened
** with SQLITE_OPEN_REUSE_SCHEMA, has just been parsed. This function either
** finds a matching SchemaPool object on the global list (schemaPoolList) or
** else allocates a new one and sets the Db.pSPool variable accordingly.
*/
................................................................................

  sqlite3_mutex_leave( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );

  db->aDb[iDb].pSPool = p;
  return (p ? SQLITE_OK : SQLITE_NOMEM);
}

int sqlite3SchemaDisconnect(sqlite3 *db, int iDb, int bNew){
  int rc = SQLITE_OK;
  if( IsReuseSchema(db) && iDb!=1 ){
    Db *pDb = &db->aDb[iDb];

    SchemaPool *pSPool = pDb->pSPool;
    assert_schema_state_ok(db);
    assert( pDb->pSchema );

    if( pSPool==0 ){
      if( bNew==0 ){
        schemaDelete(pDb->pSchema);
        pDb->pSchema = 0;

      }
    }else{
      sqlite3_mutex_enter( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
      if( DbHasProperty(db, iDb, DB_SchemaLoaded) ){
        schemaRelease(pDb);
      }
      if( bNew ){
        Schema *pNew = sqlite3SchemaGet(db, 0);
        if( pNew==0 ){
          rc = SQLITE_NOMEM;
        }else{
          pDb->pSchema = pNew;
        }
      }
      if( rc==SQLITE_OK ){
        assert( pSPool->nRef>=1 );
        pDb->pSPool = 0;
        pSPool->nRef--;
        if( pSPool->nRef<=0 ){
          Schema *pNext;
          SchemaPool **pp;
          while( pSPool->pSchema ){
            Schema *pNext = pSPool->pSchema->pNext;
            schemaDelete(pSPool->pSchema);
            pSPool->pSchema = pNext;
          }
          for(pp=&schemaPoolList; (*pp)!=pSPool; pp=&((*pp)->pNext));
          *pp = pSPool->pNext;
          sqlite3_free(pSPool);
        }
      }
      sqlite3_mutex_leave( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
    }
  }
  return rc;
}

/*
** Extract and return a pointer to a schema object from the SchemaPool passed
** as the only argument, if one is available. If one is not available, return
** NULL.
*/
................................................................................
    sqlite3_mutex_leave( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
  }
  return pRet;
}

void sqlite3SchemaReleaseAll(sqlite3 *db){
  int i;
  assert_schema_state_ok(db);
  sqlite3_mutex_enter( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
  for(i=0; i<db->nDb; i++){
    if( i!=1 ){
      Db *pDb = &db->aDb[i];
      if( pDb->pSPool && pDb->pSchema && DbHasProperty(db,i,DB_SchemaLoaded) ){
        schemaRelease(pDb);





      }
    }
  }
  sqlite3_mutex_leave( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER) );
}

/*
** Find and return the schema associated with a BTree.  Create
** a new one if necessary.
*/
Schema *sqlite3SchemaGet(sqlite3 *db, Btree *pBt){

Changes to src/main.c.

1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
  /* Close all database connections */
  for(j=0; j<db->nDb; j++){
    struct Db *pDb = &db->aDb[j];
    if( pDb->pBt ){
      sqlite3BtreeClose(pDb->pBt);
      pDb->pBt = 0;
      if( j!=1 ){
        sqlite3SchemaDisconnect(db, j);
        pDb->pSchema = 0;
      }
    }
  }
  /* Clear the TEMP schema separately and last */
  if( db->aDb[1].pSchema ){
    sqlite3SchemaClear(db->aDb[1].pSchema);







|







1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
  /* Close all database connections */
  for(j=0; j<db->nDb; j++){
    struct Db *pDb = &db->aDb[j];
    if( pDb->pBt ){
      sqlite3BtreeClose(pDb->pBt);
      pDb->pBt = 0;
      if( j!=1 ){
        sqlite3SchemaDisconnect(db, j, 0);
        pDb->pSchema = 0;
      }
    }
  }
  /* Clear the TEMP schema separately and last */
  if( db->aDb[1].pSchema ){
    sqlite3SchemaClear(db->aDb[1].pSchema);

Changes to src/prepare.c.

188
189
190
191
192
193
194
195
196
197
198
199
200
201

202
203
204
205
206
207
208
    assert( IsReuseSchema(db) );
    /* See if there is a free schema object in the schema-pool. If not,
    ** disconnect from said schema pool and continue. This function will
    ** connect to a (possibly different) schema-pool before returning. */
    if( (pDb->pSchema = sqlite3SchemaExtract(pDb->pSPool)) ){
      return SQLITE_OK;
    }
    sqlite3SchemaDisconnect(db, iDb);
    assert( pDb->pSchema==0 && pDb->pSPool==0 );
    pDb->pSchema = sqlite3SchemaGet(db, 0);
    if( pDb->pSchema==0 ){
      rc = SQLITE_NOMEM_BKPT;
      goto error_out;
    }

  }

  db->init.busy = 1;

  /* Construct the in-memory representation schema tables (sqlite_master or
  ** sqlite_temp_master) by invoking the parser directly.  The appropriate
  ** table name will be inserted automatically by the parser so we can just







|
<
<
<
<
|
<
>







188
189
190
191
192
193
194
195




196

197
198
199
200
201
202
203
204
    assert( IsReuseSchema(db) );
    /* See if there is a free schema object in the schema-pool. If not,
    ** disconnect from said schema pool and continue. This function will
    ** connect to a (possibly different) schema-pool before returning. */
    if( (pDb->pSchema = sqlite3SchemaExtract(pDb->pSPool)) ){
      return SQLITE_OK;
    }
    rc = sqlite3SchemaDisconnect(db, iDb, 1);




    if( rc!=SQLITE_OK ) goto error_out;

    assert( pDb->pSchema && pDb->pSPool==0 );
  }

  db->init.busy = 1;

  /* Construct the in-memory representation schema tables (sqlite_master or
  ** sqlite_temp_master) by invoking the parser directly.  The appropriate
  ** table name will be inserted automatically by the parser so we can just

Changes to src/sqliteInt.h.

4290
4291
4292
4293
4294
4295
4296
4297
4298
4299
4300
4301
4302
4303
4304
int sqlite3AnalysisLoad(sqlite3*,int iDB);
void sqlite3DeleteIndexSamples(sqlite3*,Index*);
void sqlite3DefaultRowEst(Index*);
void sqlite3RegisterLikeFunctions(sqlite3*, int);
int sqlite3IsLikeFunction(sqlite3*,Expr*,int*,char*);
void sqlite3SchemaClear(void *);
int sqlite3SchemaConnect(sqlite3*, int, u64);
void sqlite3SchemaDisconnect(sqlite3 *, int);
void sqlite3SchemaZero(sqlite3*, int);
Schema *sqlite3SchemaExtract(SchemaPool*);
void sqlite3SchemaReleaseAll(sqlite3 *);
void sqlite3SchemaWritable(Parse*, int);
Schema *sqlite3SchemaGet(sqlite3 *, Btree *);
int sqlite3SchemaToIndex(sqlite3 *db, Schema *);
KeyInfo *sqlite3KeyInfoAlloc(sqlite3*,int,int);







|







4290
4291
4292
4293
4294
4295
4296
4297
4298
4299
4300
4301
4302
4303
4304
int sqlite3AnalysisLoad(sqlite3*,int iDB);
void sqlite3DeleteIndexSamples(sqlite3*,Index*);
void sqlite3DefaultRowEst(Index*);
void sqlite3RegisterLikeFunctions(sqlite3*, int);
int sqlite3IsLikeFunction(sqlite3*,Expr*,int*,char*);
void sqlite3SchemaClear(void *);
int sqlite3SchemaConnect(sqlite3*, int, u64);
int sqlite3SchemaDisconnect(sqlite3 *, int, int);
void sqlite3SchemaZero(sqlite3*, int);
Schema *sqlite3SchemaExtract(SchemaPool*);
void sqlite3SchemaReleaseAll(sqlite3 *);
void sqlite3SchemaWritable(Parse*, int);
Schema *sqlite3SchemaGet(sqlite3 *, Btree *);
int sqlite3SchemaToIndex(sqlite3 *db, Schema *);
KeyInfo *sqlite3KeyInfoAlloc(sqlite3*,int,int);

Changes to test/reuse1.test.

32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
  PRAGMA schema_version;
} {2}

do_test 1.2 {
  db close
  db2 close
  sqlite3 db2 test.db2 -reuse-schema 1
  sqlite3 db  test.db -reuse-schema 1
} {}

do_execsql_test -db db2 1.3.1 {
  INSERT INTO t1 VALUES(1, 2, 3);
  INSERT INTO t1 VALUES(4, 5, 6);
}








|







32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
  PRAGMA schema_version;
} {2}

do_test 1.2 {
  db close
  db2 close
  sqlite3 db2 test.db2 -reuse-schema 1
  sqlite3 db test.db -reuse-schema 1
} {}

do_execsql_test -db db2 1.3.1 {
  INSERT INTO t1 VALUES(1, 2, 3);
  INSERT INTO t1 VALUES(4, 5, 6);
}

Changes to test/tester.tcl.

2043
2044
2045
2046
2047
2048
2049


2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
    foreach f $lStack {
      set frames($f) 1
    }
  }

  set tbl2 "CREATE TABLE ${database}.frame(frame INTEGER PRIMARY KEY, line);\n"
  set tbl3 "CREATE TABLE ${database}.file(name PRIMARY KEY, content);\n"



  foreach f [array names frames] {
    set addr [format %x $f]
    set cmd "addr2line -e [info nameofexec] $addr"
    set line [eval exec $cmd]
    append sql "INSERT INTO ${database}.frame VALUES($f, '$line');\n"

    set file [lindex [split $line :] 0]
    set files($file) 1
  }








>
>



|







2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
    foreach f $lStack {
      set frames($f) 1
    }
  }

  set tbl2 "CREATE TABLE ${database}.frame(frame INTEGER PRIMARY KEY, line);\n"
  set tbl3 "CREATE TABLE ${database}.file(name PRIMARY KEY, content);\n"

  set pid [pid]

  foreach f [array names frames] {
    set addr [format %x $f]
    set cmd "eu-addr2line --pid=$pid $addr"
    set line [eval exec $cmd]
    append sql "INSERT INTO ${database}.frame VALUES($f, '$line');\n"

    set file [lindex [split $line :] 0]
    set files($file) 1
  }