}
/* Format a string, replacing each occurrence of the % character with
- * zName. This may be more convenient than sqlite_mprintf()
+ * zDb.zName. This may be more convenient than sqlite_mprintf()
* when one string is used repeatedly in a format string.
* The caller must free() the returned string. */
-static char *string_format(const char *zFormat, const char *zName){
+static char *string_format(const char *zFormat,
+ const char *zDb, const char *zName){
const char *p;
size_t len = 0;
+ size_t nDb = strlen(zDb);
size_t nName = strlen(zName);
+ size_t nFullTableName = nDb+1+nName;
char *result;
char *r;
/* first compute length needed */
for(p = zFormat ; *p ; ++p){
- len += (*p=='%' ? nName : 1);
+ len += (*p=='%' ? nFullTableName : 1);
}
len += 1; /* for null terminator */
r = result = malloc(len);
for(p = zFormat; *p; ++p){
if( *p=='%' ){
+ memcpy(r, zDb, nDb);
+ r += nDb;
+ *r++ = '.';
memcpy(r, zName, nName);
r += nName;
} else {
return result;
}
-static int sql_exec(sqlite3 *db, const char *zName, const char *zFormat){
- char *zCommand = string_format(zFormat, zName);
+static int sql_exec(sqlite3 *db, const char *zDb, const char *zName,
+ const char *zFormat){
+ char *zCommand = string_format(zFormat, zDb, zName);
int rc;
TRACE(("FTS1 sql: %s\n", zCommand));
rc = sqlite3_exec(db, zCommand, NULL, 0, NULL);
return rc;
}
-static int sql_prepare(sqlite3 *db, const char *zName, sqlite3_stmt **ppStmt,
- const char *zFormat){
- char *zCommand = string_format(zFormat, zName);
+static int sql_prepare(sqlite3 *db, const char *zDb, const char *zName,
+ sqlite3_stmt **ppStmt, const char *zFormat){
+ char *zCommand = string_format(zFormat, zDb, zName);
int rc;
TRACE(("FTS1 prepare: %s\n", zCommand));
rc = sqlite3_prepare(db, zCommand, -1, ppStmt, NULL);
struct fulltext_vtab {
sqlite3_vtab base; /* Base class used by SQLite core */
sqlite3 *db; /* The database connection */
+ const char *zDb; /* logical database name */
const char *zName; /* virtual table name */
int nColumn; /* number of columns in virtual table */
char **azColumn; /* column names. malloced */
default:
zStmt = fulltext_zStatement[iStmt];
}
- rc = sql_prepare(v->db, v->zName, &v->pFulltextStatements[iStmt],
+ rc = sql_prepare(v->db, v->zDb, v->zName, &v->pFulltextStatements[iStmt],
zStmt);
if( zStmt != fulltext_zStatement[iStmt]) free((void *) zStmt);
if( rc!=SQLITE_OK ) return rc;
** and use by fulltextConnect and fulltextCreate.
*/
typedef struct TableSpec {
+ const char *zDb; /* Logical database name */
const char *zName; /* Name of the full-text index */
int nColumn; /* Number of columns to be indexed */
char **azColumn; /* Original names of columns to be indexed */
/* Identify the column names and the tokenizer and delimiter arguments
** in the argv[][] array.
*/
+ pSpec->zDb = azArg[1];
pSpec->zName = azArg[2];
pSpec->nColumn = 0;
pSpec->azColumn = azArg;
memset(v, 0, sizeof(*v));
/* sqlite will initialize v->base */
v->db = db;
+ v->zDb = spec->zDb; /* Freed when azColumn is freed */
v->zName = spec->zName; /* Freed when azColumn is freed */
v->nColumn = spec->nColumn;
v->azContentColumn = spec->azContentColumn;
append(&schema, "CREATE TABLE %_content(");
appendList(&schema, spec.nColumn, spec.azContentColumn);
append(&schema, ")");
- rc = sql_exec(db, spec.zName, schema.s);
+ rc = sql_exec(db, spec.zDb, spec.zName, schema.s);
free(schema.s);
if( rc!=SQLITE_OK ) goto out;
- rc = sql_exec(db, spec.zName,
+ rc = sql_exec(db, spec.zDb, spec.zName,
"create table %_term(term text, segment integer, doclist blob, "
"primary key(term, segment));");
if( rc!=SQLITE_OK ) goto out;
int rc;
TRACE(("FTS1 Destroy %p\n", pVTab));
- rc = sql_exec(v->db, v->zName,
+ rc = sql_exec(v->db, v->zDb, v->zName,
"drop table if exists %_content;"
"drop table if exists %_term;"
);
zSql = sqlite3_mprintf("select rowid, * from %%_content %s",
idxNum==QUERY_GENERIC ? "" : "where rowid=?");
sqlite3_finalize(c->pStmt);
- rc = sql_prepare(v->db, v->zName, &c->pStmt, zSql);
+ rc = sql_prepare(v->db, v->zDb, v->zName, &c->pStmt, zSql);
sqlite3_free(zSql);
if( rc!=SQLITE_OK ) return rc;
}
/* Format a string, replacing each occurrence of the % character with
- * zName. This may be more convenient than sqlite_mprintf()
+ * zDb.zName. This may be more convenient than sqlite_mprintf()
* when one string is used repeatedly in a format string.
* The caller must free() the returned string. */
-static char *string_format(const char *zFormat, const char *zName){
+static char *string_format(const char *zFormat,
+ const char *zDb, const char *zName){
const char *p;
size_t len = 0;
+ size_t nDb = strlen(zDb);
size_t nName = strlen(zName);
+ size_t nFullTableName = nDb+1+nName;
char *result;
char *r;
/* first compute length needed */
for(p = zFormat ; *p ; ++p){
- len += (*p=='%' ? nName : 1);
+ len += (*p=='%' ? nFullTableName : 1);
}
len += 1; /* for null terminator */
r = result = malloc(len);
for(p = zFormat; *p; ++p){
if( *p=='%' ){
+ memcpy(r, zDb, nDb);
+ r += nDb;
+ *r++ = '.';
memcpy(r, zName, nName);
r += nName;
} else {
return result;
}
-static int sql_exec(sqlite3 *db, const char *zName, const char *zFormat){
- char *zCommand = string_format(zFormat, zName);
+static int sql_exec(sqlite3 *db, const char *zDb, const char *zName,
+ const char *zFormat){
+ char *zCommand = string_format(zFormat, zDb, zName);
int rc;
TRACE(("FTS2 sql: %s\n", zCommand));
rc = sqlite3_exec(db, zCommand, NULL, 0, NULL);
return rc;
}
-static int sql_prepare(sqlite3 *db, const char *zName, sqlite3_stmt **ppStmt,
- const char *zFormat){
- char *zCommand = string_format(zFormat, zName);
+static int sql_prepare(sqlite3 *db, const char *zDb, const char *zName,
+ sqlite3_stmt **ppStmt, const char *zFormat){
+ char *zCommand = string_format(zFormat, zDb, zName);
int rc;
TRACE(("FTS2 prepare: %s\n", zCommand));
rc = sqlite3_prepare(db, zCommand, -1, ppStmt, NULL);
struct fulltext_vtab {
sqlite3_vtab base; /* Base class used by SQLite core */
sqlite3 *db; /* The database connection */
+ const char *zDb; /* logical database name */
const char *zName; /* virtual table name */
int nColumn; /* number of columns in virtual table */
char **azColumn; /* column names. malloced */
default:
zStmt = fulltext_zStatement[iStmt];
}
- rc = sql_prepare(v->db, v->zName, &v->pFulltextStatements[iStmt],
+ rc = sql_prepare(v->db, v->zDb, v->zName, &v->pFulltextStatements[iStmt],
zStmt);
if( zStmt != fulltext_zStatement[iStmt]) free((void *) zStmt);
if( rc!=SQLITE_OK ) return rc;
sqlite3_stmt **ppStmt){
assert( idx>=0 && idx<MERGE_COUNT );
if( v->pLeafSelectStmts[idx]==NULL ){
- int rc = sql_prepare(v->db, v->zName, &v->pLeafSelectStmts[idx],
+ int rc = sql_prepare(v->db, v->zDb, v->zName, &v->pLeafSelectStmts[idx],
LEAF_SELECT);
if( rc!=SQLITE_OK ) return rc;
}else{
** and use by fulltextConnect and fulltextCreate.
*/
typedef struct TableSpec {
+ const char *zDb; /* Logical database name */
const char *zName; /* Name of the full-text index */
int nColumn; /* Number of columns to be indexed */
char **azColumn; /* Original names of columns to be indexed */
/* Identify the column names and the tokenizer and delimiter arguments
** in the argv[][] array.
*/
+ pSpec->zDb = azArg[1];
pSpec->zName = azArg[2];
pSpec->nColumn = 0;
pSpec->azColumn = azArg;
CLEAR(v);
/* sqlite will initialize v->base */
v->db = db;
+ v->zDb = spec->zDb; /* Freed when azColumn is freed */
v->zName = spec->zName; /* Freed when azColumn is freed */
v->nColumn = spec->nColumn;
v->azContentColumn = spec->azContentColumn;
append(&schema, "CREATE TABLE %_content(");
appendList(&schema, spec.nColumn, spec.azContentColumn);
append(&schema, ")");
- rc = sql_exec(db, spec.zName, stringBufferData(&schema));
+ rc = sql_exec(db, spec.zDb, spec.zName, stringBufferData(&schema));
stringBufferDestroy(&schema);
if( rc!=SQLITE_OK ) goto out;
- rc = sql_exec(db, spec.zName, "create table %_segments(block blob);");
+ rc = sql_exec(db, spec.zDb, spec.zName,
+ "create table %_segments(block blob);");
if( rc!=SQLITE_OK ) goto out;
- rc = sql_exec(db, spec.zName,
+ rc = sql_exec(db, spec.zDb, spec.zName,
"create table %_segdir("
" level integer,"
" idx integer,"
int rc;
TRACE(("FTS2 Destroy %p\n", pVTab));
- rc = sql_exec(v->db, v->zName,
+ rc = sql_exec(v->db, v->zDb, v->zName,
"drop table if exists %_content;"
"drop table if exists %_segments;"
"drop table if exists %_segdir;"
zSql = sqlite3_mprintf("select rowid, * from %%_content %s",
idxNum==QUERY_GENERIC ? "" : "where rowid=?");
sqlite3_finalize(c->pStmt);
- rc = sql_prepare(v->db, v->zName, &c->pStmt, zSql);
+ rc = sql_prepare(v->db, v->zDb, v->zName, &c->pStmt, zSql);
sqlite3_free(zSql);
if( rc!=SQLITE_OK ) return rc;
-C Additional\stests\sto\sgive\sfull\scoverage\stesting\sto\sticket\s#2211.\s(CVS\s3630)
-D 2007-02-06T23:41:34
+C http://www.sqlite.org/cvstrac/tktview?tn=2219\n\nWhen\screating\sfts\stables\sin\san\sattached\sdatabase,\sthe\sbacking\stables\nare\screated\sin\sdatabase\s'main'.\s\sThis\schange\spropagates\sthe\nappropriate\sdatabase\sname\sto\sthe\sroutines\swhich\sbuild\ssql\sstatements.\n\nNote\sthat\sI\spropagate\sthe\sdatabase\sname\sand\stable\sname\sseparately.\s\sI\nbriefly\sconsidered\sjust\smaking\sthe\stable\sname\sbe\s"db.table",\sbut\sit\ndidn't\sfit\sso\swell\sin\sthe\smodel\sused\sto\sstore\sthe\stable\sname\sand\sother\ninformation,\sand\shaving\sthe\sdb\sname\spassed\sseparately\sseemed\sa\sbit\nmore\stransparent.\s(CVS\s3631)
+D 2007-02-07T01:01:17
F Makefile.in 7fa74bf4359aa899da5586e394d17735f221315f
F Makefile.linux-gcc 2d8574d1ba75f129aba2019f0b959db380a90935
F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028
F ext/fts1/README.txt 20ac73b006a70bcfd80069bdaf59214b6cf1db5e
F ext/fts1/ft_hash.c 3927bd880e65329bdc6f506555b228b28924921b
F ext/fts1/ft_hash.h 1a35e654a235c2c662d3ca0dfc3138ad60b8b7d5
-F ext/fts1/fts1.c 37b91a1a39713220c5411debe46778495fcfc2f2
+F ext/fts1/fts1.c 0aab3cf20eefd38935c8f525494d689cb2785f1d
F ext/fts1/fts1.h 6060b8f62c1d925ea8356cb1a6598073eb9159a6
F ext/fts1/fts1_hash.c 3196cee866edbebb1c0521e21672e6d599965114
F ext/fts1/fts1_hash.h 957d378355ed29f672cd5add012ce8b088a5e089
F ext/fts1/simple_tokenizer.c 1844d72f7194c3fd3d7e4173053911bf0661b70d
F ext/fts1/tokenizer.h 0c53421b832366d20d720d21ea3e1f6e66a36ef9
F ext/fts2/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d
-F ext/fts2/fts2.c 5f7247b8eca4c83e95d66a8f6cadb0949bb356dc
+F ext/fts2/fts2.c a49ed7292cbacbfcde6fdee1be4f6529277be3fa
F ext/fts2/fts2.h bbdab26d34f91974d5b9ade8b7836c140a7c4ce1
F ext/fts2/fts2_hash.c b3f22116d4ef0bc8f2da6e3fdc435c86d0951a9b
F ext/fts2/fts2_hash.h e283308156018329f042816eb09334df714e105e
F test/fts1e.test 77244843e925560b5a0b70069c3e7ab62f181ed2
F test/fts1f.test cdab90834a7627a26fed2f75d2f451650fb31fad
F test/fts1i.test 6bfe08cdfdced063a39a50c8601da65e6274d879
+F test/fts1j.test e4c0ffcd0ba2adce09c6b7b43ffd0749b5fda5c7
F test/fts1porter.test d86e9c3e0c7f8ff95add6582b4b585fb4e02b96d
F test/fts2a.test 103fc178d134c54c44c1938a4331e9e2030792d9
F test/fts2b.test 964abc0236c849c07ca1ae496bb25c268ae94816
F test/fts2g.test c69a8ab43ec77d123976ba6cf9422d647ae63032
F test/fts2h.test 223af921323b409d4b5b18ff4e51619541b174bb
F test/fts2i.test 1b22451d1f13f7c509baec620dc3a4a754885dd6
+F test/fts2j.test f68d7611f76309bc8b94170f3740d9fbbc061d9b
F test/func.test 71938e2ac704d8ce12f11810d475597640656ae9
F test/hook.test 7e7645fd9a033f79cce8fdff151e32715e7ec50a
F test/in.test 369cb2aa1eab02296b4ec470732fe8c131260b1d
F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b
F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513
-P 912faf18d86416b1a36660851f8a4554e6746875
-R 7671dedbcdac0acf59ed5e145bb96e0d
-U drh
-Z cc828275335295a30c6cbbc23706d929
+P ecb1f2fd7b96797459287eaab95693ae17ec2079
+R 1584b834a23f13b852b265197c23db47
+U shess
+Z 3a97d1cfa72fc8326e11ebbeaf0b1200
-ecb1f2fd7b96797459287eaab95693ae17ec2079
\ No newline at end of file
+283385d20724f0144f38de89bd179715ee5e738b
\ No newline at end of file
--- /dev/null
+# 2007 February 6
+#
+# The author disclaims copyright to this source code.
+#
+#*************************************************************************
+# This file implements regression tests for SQLite library. This
+# tests creating fts1 tables in an attached database.
+#
+# $Id: fts1j.test,v 1.1 2007/02/07 01:01:18 shess Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS1 is defined, omit this file.
+ifcapable !fts1 {
+ finish_test
+ return
+}
+
+# Clean up anything left over from a previous pass.
+file delete -force test2.db
+file delete -force test2.db-journal
+sqlite3 db2 test2.db
+
+db eval {
+ CREATE VIRTUAL TABLE t3 USING fts1(content);
+ INSERT INTO t3 (rowid, content) VALUES(1, "hello world");
+}
+
+db2 eval {
+ CREATE VIRTUAL TABLE t1 USING fts1(content);
+ INSERT INTO t1 (rowid, content) VALUES(1, "hello world");
+ INSERT INTO t1 (rowid, content) VALUES(2, "hello there");
+ INSERT INTO t1 (rowid, content) VALUES(3, "cruel world");
+}
+
+# This has always worked because the t1_* tables used by fts1 will be
+# the defaults.
+do_test fts1j-1.1 {
+ execsql {
+ ATTACH DATABASE 'test2.db' AS two;
+ SELECT rowid FROM t1 WHERE t1 MATCH 'hello';
+ DETACH DATABASE two;
+ }
+} {1 2}
+# Make certain we're detached if there was an error.
+catch {db eval {DETACH DATABASE two}}
+
+# In older code, this appears to work fine, but the t2_* tables used
+# by fts1 will be created in database 'main' instead of database
+# 'two'. It appears to work fine because the tables end up being the
+# defaults, but obviously is badly broken if you hope to use things
+# other than in the exact same ATTACH setup.
+do_test fts1j-1.2 {
+ execsql {
+ ATTACH DATABASE 'test2.db' AS two;
+ CREATE VIRTUAL TABLE two.t2 USING fts1(content);
+ INSERT INTO t2 (rowid, content) VALUES(1, "hello world");
+ INSERT INTO t2 (rowid, content) VALUES(2, "hello there");
+ INSERT INTO t2 (rowid, content) VALUES(3, "cruel world");
+ SELECT rowid FROM t2 WHERE t2 MATCH 'hello';
+ DETACH DATABASE two;
+ }
+} {1 2}
+catch {db eval {DETACH DATABASE two}}
+
+# In older code, this broke because the fts1 code attempted to create
+# t3_* tables in database 'main', but they already existed. Normally
+# this wouldn't happen without t3 itself existing, in which case the
+# fts1 code would never be called in the first place.
+do_test fts1j-1.3 {
+ execsql {
+ ATTACH DATABASE 'test2.db' AS two;
+
+ CREATE VIRTUAL TABLE two.t3 USING fts1(content);
+ INSERT INTO two.t3 (rowid, content) VALUES(2, "hello there");
+ INSERT INTO two.t3 (rowid, content) VALUES(3, "cruel world");
+ SELECT rowid FROM two.t3 WHERE t3 MATCH 'hello';
+
+ DETACH DATABASE two;
+ } db2
+} {2}
+catch {db eval {DETACH DATABASE two}}
+
+catch {db2 close}
+file delete -force test2.db
+
+finish_test
--- /dev/null
+# 2007 February 6
+#
+# The author disclaims copyright to this source code.
+#
+#*************************************************************************
+# This file implements regression tests for SQLite library. This
+# tests creating fts2 tables in an attached database.
+#
+# $Id: fts2j.test,v 1.1 2007/02/07 01:01:18 shess Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS2 is defined, omit this file.
+ifcapable !fts2 {
+ finish_test
+ return
+}
+
+# Clean up anything left over from a previous pass.
+file delete -force test2.db
+file delete -force test2.db-journal
+sqlite3 db2 test2.db
+
+db eval {
+ CREATE VIRTUAL TABLE t3 USING fts2(content);
+ INSERT INTO t3 (rowid, content) VALUES(1, "hello world");
+}
+
+db2 eval {
+ CREATE VIRTUAL TABLE t1 USING fts2(content);
+ INSERT INTO t1 (rowid, content) VALUES(1, "hello world");
+ INSERT INTO t1 (rowid, content) VALUES(2, "hello there");
+ INSERT INTO t1 (rowid, content) VALUES(3, "cruel world");
+}
+
+# This has always worked because the t1_* tables used by fts2 will be
+# the defaults.
+do_test fts2j-1.1 {
+ execsql {
+ ATTACH DATABASE 'test2.db' AS two;
+ SELECT rowid FROM t1 WHERE t1 MATCH 'hello';
+ DETACH DATABASE two;
+ }
+} {1 2}
+# Make certain we're detached if there was an error.
+catch {db eval {DETACH DATABASE two}}
+
+# In older code, this appears to work fine, but the t2_* tables used
+# by fts2 will be created in database 'main' instead of database
+# 'two'. It appears to work fine because the tables end up being the
+# defaults, but obviously is badly broken if you hope to use things
+# other than in the exact same ATTACH setup.
+do_test fts2j-1.2 {
+ execsql {
+ ATTACH DATABASE 'test2.db' AS two;
+ CREATE VIRTUAL TABLE two.t2 USING fts2(content);
+ INSERT INTO t2 (rowid, content) VALUES(1, "hello world");
+ INSERT INTO t2 (rowid, content) VALUES(2, "hello there");
+ INSERT INTO t2 (rowid, content) VALUES(3, "cruel world");
+ SELECT rowid FROM t2 WHERE t2 MATCH 'hello';
+ DETACH DATABASE two;
+ }
+} {1 2}
+catch {db eval {DETACH DATABASE two}}
+
+# In older code, this broke because the fts2 code attempted to create
+# t3_* tables in database 'main', but they already existed. Normally
+# this wouldn't happen without t3 itself existing, in which case the
+# fts2 code would never be called in the first place.
+do_test fts2j-1.3 {
+ execsql {
+ ATTACH DATABASE 'test2.db' AS two;
+
+ CREATE VIRTUAL TABLE two.t3 USING fts2(content);
+ INSERT INTO two.t3 (rowid, content) VALUES(2, "hello there");
+ INSERT INTO two.t3 (rowid, content) VALUES(3, "cruel world");
+ SELECT rowid FROM two.t3 WHERE t3 MATCH 'hello';
+
+ DETACH DATABASE two;
+ } db2
+} {2}
+catch {db eval {DETACH DATABASE two}}
+
+catch {db2 close}
+file delete -force test2.db
+
+finish_test