diff --git a/manifest b/manifest
index feacd00e39..84a18cf50b 100644
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Allow\sgeneral\sexpressions\sin\sthe\sVALUES\sclause\sof\san\sINSERT\sstatement.\s(CVS\s376)
-D 2002-02-18T13:56:37
+C Add\ssupport\sfor\sCREATE\sTABLE\sAS.\s(CVS\s377)
+D 2002-02-18T18:30:32
F Makefile.in 9fa4277413bf1d9cf91365f07d4108d7d87ed2af
F Makefile.template 3372d45f8853afdb70bd30cc6fb50a3cd9069834
F README a4c0ba11354ef6ba0776b400d057c59da47a4cc0
@@ -21,26 +21,26 @@ F sqlite.1 2e2bb0529ef468ade9e4322bd609d0695fb9ded9
F src/TODO af7f3cab0228e34149cf98e073aa83d45878e7e6
F src/btree.c 340254ee2ea8b3bd8b60f9768b20382b4909eec0
F src/btree.h b131caa44354d0305734d87b1c71440b4c436608
-F src/build.c 29504057ac5e2f40c08f19cb1574bd0512353169
+F src/build.c 6643e708b99ee801305584f81550a1e4095b5f97
F src/delete.c f8ad71be53cf18656b6573de65395852fe817f0c
F src/expr.c 7aff65ea0732b07d36925087ad611019103ad69a
F src/hash.c 8f7c740ef2eaaa8decfa8751f2be30680b123e46
F src/hash.h d1ce47900c7325af5e41c4feb4855c4bf2b841e7
F src/insert.c 9453cbba2a62f8a2fb37d77cbdc5c84aa24752d7
-F src/main.c 300320ba68d3e5b22c2c5b2c07fa884878202181
+F src/main.c 669cfd9a8c40f6c9ff2d478e1695d1ea1fdafad1
F src/md5.c 52f677bfc590e09f71d07d7e327bd59da738d07c
F src/os.c f6bc9b7ab530346bb7fef2ed39f2f1f214bc14ea
F src/os.h a17596ecc7f38a228b83ecdb661fb03ce44726d6
F src/pager.c d261a3a0b4e96a400ef5432297edec09b041e9c7
F src/pager.h b28f004e2f5541dc60cc32db01bf80cf4d056283
-F src/parse.y 39c336ff9ad75429205feaa704d6c1832c648c81
+F src/parse.y 722bc0b6aacb5a8c365b187bb84e740d802cb51b
F src/printf.c 300a90554345751f26e1fc0c0333b90a66110a1d
F src/random.c f6b36bec5ebd3edb3440224bf5bf811fe4ac9a1b
F src/select.c d2bbaf4cba97b4c40503d0dc47e8b729e7088e0b
F src/shell.c c102dfe388c7618a668c944ff157c49cb48f28e3
F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
F src/sqlite.h.in f57074c84a2c112a5093ba7a9d9636aa9cacc87c
-F src/sqliteInt.h 6c5d0415a6ff54e14fab74d5686016cc53f0be5c
+F src/sqliteInt.h 75cf79d3d0a0ea40f43bbc2d74a4aefb2ea37ccf
F src/table.c c89698bd5bb4b8d14722d6ee7e9be014c383d24a
F src/tclsqlite.c b9cf346e95291cb4c4f1bf5ac1d77db6b8ad023d
F src/test1.c 33efd350dca27c52c58c553c04fd3a6a51f13c1f
@@ -87,7 +87,7 @@ F test/select5.test c2a6c4a003316ee42cbbd689eebef8fdce0db2ac
F test/select6.test d9fb417d6cab75a072b547ba6303120f327fd6fd
F test/sort.test 3b996ce7ca385f9cd559944ac0f4027a23aa546b
F test/subselect.test 335d3dad8d585726c447dfee8d9c4f7383c76b78
-F test/table.test 3ef4254d62ece31a3872ab11cdaec846f6fa8fd1
+F test/table.test b8be4d3d3ff6b4858516fa274d38075ba06b5004
F test/tableapi.test 51d0c209aa6b1158cb952ec917c656d4ce66e9e4
F test/tclsqlite.test ca8dd89b02ab68bd4540163c24551756a69f6783
F test/temptable.test 0e9934283259a5e637eec756a7eefd6964c0f79b
@@ -108,22 +108,22 @@ F www/arch.fig d5f9752a4dbf242e9cfffffd3f5762b6c63b3bcf
F www/arch.png 82ef36db1143828a7abc88b1e308a5f55d4336f4
F www/arch.tcl 72a0c80e9054cc7025a50928d28d9c75c02c2b8b
F www/c_interface.tcl 82a026b1681757f13b3f62e035f3a31407c1d353
-F www/changes.tcl e97a76943c2c4029f6ffba60c4dc6c24478e151c
+F www/changes.tcl 46137c6e31a9b805dddaebac048177bc48fee1e7
F www/conflict.tcl 81dd21f9a679e60aae049e9dd8ab53d59570cda2
F www/crosscompile.tcl 3622ebbe518927a3854a12de51344673eb2dd060
F www/download.tcl a6d75b8b117cd33dcb090bef7e80d7556d28ebe0
F www/dynload.tcl 02eb8273aa78cfa9070dd4501dca937fb22b466c
F www/faq.tcl 32cbc134879871604070d4cc3a32e73fb22a35f9
F www/formatchng.tcl 2d9a35c787823b48d72a5c64bb5414a43e26d5ad
-F www/index.tcl fba075f31a9cbbf13e6adec080e4943db54dbc05
-F www/lang.tcl 4865287af7bb8649d8d985772a13e2f6ec639a3e
+F www/index.tcl eacd99bcc3132d6e6b74a51422d415cc0bf7bfdf
+F www/lang.tcl 1712d94f9606b6b84982e697df0667b6a2cb1a5e
F www/mingw.tcl f1c7c0a7f53387dd9bb4f8c7e8571b7561510ebc
F www/opcode.tcl bdec8ef9f100dbd87bbef8976c54b88e43fd8ccc
F www/speed.tcl 83457b2bf6bb430900bd48ca3dd98264d9a916a5
F www/sqlite.tcl 8b5884354cb615049aed83039f8dfe1552a44279
F www/tclsqlite.tcl 829b393d1ab187fd7a5e978631b3429318885c49
F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218
-P a0019fce701fc858134f0a33bda9a511e41a09f8
-R b95029b9b39abf918f39e16a59c21b3e
+P ec1f3fae6f8cd8466892cd370e1802e492a76e6e
+R 30ea68cf718b068f84b3b8ebb60eb037
U drh
-Z 7c3a4a0f74cd2eb9fb88495aad858df0
+Z 8fa30e75945f1116f95b36a95a26e476
diff --git a/manifest.uuid b/manifest.uuid
index 1caac77919..398f6666db 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-ec1f3fae6f8cd8466892cd370e1802e492a76e6e
\ No newline at end of file
+78a50971e9adc8739e7888201c79465a40e1a152
\ No newline at end of file
diff --git a/src/build.c b/src/build.c
index c7159f0d6c..69751158ac 100644
--- a/src/build.c
+++ b/src/build.c
@@ -25,7 +25,7 @@
** ROLLBACK
** PRAGMA
**
-** $Id: build.c,v 1.74 2002/02/03 17:37:36 drh Exp $
+** $Id: build.c,v 1.75 2002/02/18 18:30:32 drh Exp $
*/
#include "sqliteInt.h"
#include
@@ -650,6 +650,77 @@ static void changeCookie(sqlite *db){
}
}
+/*
+** Measure the number of characters needed to output the given
+** identifier. The number returned includes any quotes used
+** but does not include the null terminator.
+*/
+static int identLength(const char *z){
+ int n;
+ for(n=2; *z; n++, z++){
+ if( *z=='\'' ){ n++; }
+ }
+ return n;
+}
+
+/*
+** Write an identifier onto the end of the given string. Add
+** quote characters as needed.
+*/
+static void identPut(char *z, int *pIdx, char *zIdent){
+ int i, j;
+ i = *pIdx;
+ z[i++] = '\'';
+ for(j=0; zIdent[j]; j++){
+ z[i++] = zIdent[j];
+ if( zIdent[j]=='\'' ) z[i++] = '\'';
+ }
+ z[i++] = '\'';
+ z[i] = 0;
+ *pIdx = i;
+}
+
+/*
+** Generate a CREATE TABLE statement appropriate for the given
+** table. Memory to hold the text of the statement is obtained
+** from sqliteMalloc() and must be freed by the calling function.
+*/
+static char *createTableStmt(Table *p){
+ int i, k, n;
+ char *zStmt;
+ char *zSep, *zSep2, *zEnd;
+ n = 0;
+ for(i=0; inCol; i++){
+ n += identLength(p->aCol[i].zName);
+ }
+ n += identLength(p->zName);
+ if( n<40 ){
+ zSep = "";
+ zSep2 = ",";
+ zEnd = ")";
+ }else{
+ zSep = "\n ";
+ zSep2 = ",\n ";
+ zEnd = "\n)";
+ }
+ n += 25 + 6*p->nCol;
+ zStmt = sqliteMalloc( n );
+ if( zStmt==0 ) return 0;
+ assert( !p->isTemp );
+ strcpy(zStmt, "CREATE TABLE ");
+ k = strlen(zStmt);
+ identPut(zStmt, &k, p->zName);
+ zStmt[k++] = '(';
+ for(i=0; inCol; i++){
+ strcpy(&zStmt[k], zSep);
+ k += strlen(&zStmt[k]);
+ zSep = zSep2;
+ identPut(zStmt, &k, p->aCol[i].zName);
+ }
+ strcpy(&zStmt[k], zEnd);
+ return zStmt;
+}
+
/*
** This routine is called to report the final ")" that terminates
** a CREATE TABLE statement.
@@ -664,12 +735,17 @@ static void changeCookie(sqlite *db){
** connected to the database or because the sqlite_master table has
** recently changes, so the entry for this table already exists in
** the sqlite_master table. We do not want to create it again.
+**
+** If the pSelect argument is not NULL, it means that this routine
+** was called to create a table generated from a
+** "CREATE TABLE ... AS SELECT ..." statement. The column names of
+** the new table will match the result set of the SELECT.
*/
-void sqliteEndTable(Parse *pParse, Token *pEnd){
+void sqliteEndTable(Parse *pParse, Token *pEnd, Select *pSelect){
Table *p;
sqlite *db = pParse->db;
- if( pEnd==0 || pParse->nErr || sqlite_malloc_failed ) return;
+ if( (pEnd==0 && pSelect==0) || pParse->nErr || sqlite_malloc_failed ) return;
p = pParse->pNewTable;
if( p==0 ) return;
@@ -688,6 +764,19 @@ void sqliteEndTable(Parse *pParse, Token *pEnd){
db->flags |= SQLITE_InternChanges;
}
+ /* If the table is generated from a SELECT, then construct the
+ ** list of columns and the text of the table.
+ */
+ if( pSelect ){
+ Table *pSelTab = sqliteResultSetOfSelect(pParse, 0, pSelect);
+ assert( p->aCol==0 );
+ p->nCol = pSelTab->nCol;
+ p->aCol = pSelTab->aCol;
+ pSelTab->nCol = 0;
+ pSelTab->aCol = 0;
+ sqliteDeleteTable(0, pSelTab);
+ }
+
/* If the initFlag is 1 it means we are reading the SQL off the
** "sqlite_master" table on the disk. So do not write to the disk
** again. Extract the root page number for the table from the
@@ -710,7 +799,9 @@ void sqliteEndTable(Parse *pParse, Token *pEnd){
v = sqliteGetVdbe(pParse);
if( v==0 ) return;
- n = Addr(pEnd->z) - Addr(pParse->sFirstToken.z) + 1;
+ addr = sqliteVdbeAddOp(v, OP_CreateTable, 0, p->isTemp);
+ sqliteVdbeChangeP3(v, addr, (char *)&p->tnum, P3_POINTER);
+ p->tnum = 0;
if( !p->isTemp ){
sqliteVdbeAddOp(v, OP_NewRecno, 0, 0);
sqliteVdbeAddOp(v, OP_String, 0, 0);
@@ -719,19 +810,30 @@ void sqliteEndTable(Parse *pParse, Token *pEnd){
sqliteVdbeChangeP3(v, -1, p->zName, P3_STATIC);
sqliteVdbeAddOp(v, OP_String, 0, 0);
sqliteVdbeChangeP3(v, -1, p->zName, P3_STATIC);
- }
- addr = sqliteVdbeAddOp(v, OP_CreateTable, 0, p->isTemp);
- sqliteVdbeChangeP3(v, addr, (char *)&p->tnum, P3_POINTER);
- p->tnum = 0;
- if( !p->isTemp ){
- addr = sqliteVdbeAddOp(v, OP_String, 0, 0);
- sqliteVdbeChangeP3(v, addr, pParse->sFirstToken.z, n);
+ sqliteVdbeAddOp(v, OP_Dup, 4, 0);
+ sqliteVdbeAddOp(v, OP_String, 0, 0);
+ if( pSelect ){
+ char *z = createTableStmt(p);
+ n = z ? strlen(z) : 0;
+ sqliteVdbeChangeP3(v, -1, z, n);
+ sqliteFree(z);
+ }else{
+ assert( pEnd!=0 );
+ n = Addr(pEnd->z) - Addr(pParse->sFirstToken.z) + 1;
+ sqliteVdbeChangeP3(v, -1, pParse->sFirstToken.z, n);
+ }
sqliteVdbeAddOp(v, OP_MakeRecord, 5, 0);
sqliteVdbeAddOp(v, OP_PutIntKey, 0, 0);
changeCookie(db);
sqliteVdbeAddOp(v, OP_SetCookie, db->next_cookie, 0);
sqliteVdbeAddOp(v, OP_Close, 0, 0);
}
+ if( pSelect ){
+ int op = p->isTemp ? OP_OpenWrAux : OP_OpenWrite;
+ sqliteVdbeAddOp(v, op, 1, 0);
+ pParse->nTab = 2;
+ sqliteSelect(pParse, pSelect, SRT_Table, 1);
+ }
sqliteEndWriteOperation(pParse);
}
}
diff --git a/src/main.c b/src/main.c
index 4408dabbe7..61450217e0 100644
--- a/src/main.c
+++ b/src/main.c
@@ -14,7 +14,7 @@
** other files are for internal use by SQLite and should not be
** accessed by users of the library.
**
-** $Id: main.c,v 1.58 2002/02/02 15:01:16 drh Exp $
+** $Id: main.c,v 1.59 2002/02/18 18:30:33 drh Exp $
*/
#include "sqliteInt.h"
#include "os.h"
@@ -395,6 +395,13 @@ int sqlite_complete(const char *zSql){
case '\f': {
break;
}
+ case '[': {
+ isComplete = 0;
+ zSql++;
+ while( *zSql && *zSql!=']' ){ zSql++; }
+ if( *zSql==0 ) return 0;
+ break;
+ }
case '\'': {
isComplete = 0;
zSql++;
diff --git a/src/parse.y b/src/parse.y
index 8d71515be0..77c7ceaf26 100644
--- a/src/parse.y
+++ b/src/parse.y
@@ -14,7 +14,7 @@
** the parser. Lemon will also generate a header file containing
** numeric codes for all of the tokens.
**
-** @(#) $Id: parse.y,v 1.50 2002/02/18 13:56:37 drh Exp $
+** @(#) $Id: parse.y,v 1.51 2002/02/18 18:30:33 drh Exp $
*/
%token_prefix TK_
%token_type {Token}
@@ -68,13 +68,19 @@ cmd ::= ROLLBACK trans_opt. {sqliteRollbackTransaction(pParse);}
///////////////////// The CREATE TABLE statement ////////////////////////////
//
cmd ::= create_table create_table_args.
-create_table ::= CREATE(X) temp(T) TABLE ids(Y).
- {sqliteStartTable(pParse,&X,&Y,T);}
+create_table ::= CREATE(X) temp(T) TABLE ids(Y). {
+ sqliteStartTable(pParse,&X,&Y,T);
+}
%type temp {int}
temp(A) ::= TEMP. {A = 1;}
temp(A) ::= . {A = 0;}
-create_table_args ::= LP columnlist conslist_opt RP(X).
- {sqliteEndTable(pParse,&X);}
+create_table_args ::= LP columnlist conslist_opt RP(X). {
+ sqliteEndTable(pParse,&X,0);
+}
+create_table_args ::= AS select(S). {
+ sqliteEndTable(pParse,0,S);
+ sqliteSelectDelete(S);
+}
columnlist ::= columnlist COMMA column.
columnlist ::= column.
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 03da707bb2..1f3db95bf5 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -11,7 +11,7 @@
*************************************************************************
** Internal interface definitions for SQLite.
**
-** @(#) $Id: sqliteInt.h,v 1.86 2002/02/18 01:17:00 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.87 2002/02/18 18:30:33 drh Exp $
*/
#include "sqlite.h"
#include "hash.h"
@@ -566,13 +566,14 @@ void sqliteExprListDelete(ExprList*);
void sqlitePragma(Parse*,Token*,Token*,int);
void sqliteCommitInternalChanges(sqlite*);
void sqliteRollbackInternalChanges(sqlite*);
+Table *sqliteResultSetOfSelect(Parse*,char*,Select*);
void sqliteStartTable(Parse*,Token*,Token*,int);
void sqliteAddColumn(Parse*,Token*);
void sqliteAddNotNull(Parse*, int);
void sqliteAddPrimaryKey(Parse*, IdList*, int);
void sqliteAddColumnType(Parse*,Token*,Token*);
void sqliteAddDefaultValue(Parse*,Token*,int);
-void sqliteEndTable(Parse*,Token*);
+void sqliteEndTable(Parse*,Token*,Select*);
void sqliteDropTable(Parse*, Token*);
void sqliteDeleteTable(sqlite*, Table*);
void sqliteInsert(Parse*, Token*, ExprList*, Select*, IdList*, int);
diff --git a/test/table.test b/test/table.test
index 9c41c1fcc9..cfe64c6015 100644
--- a/test/table.test
+++ b/test/table.test
@@ -11,7 +11,7 @@
# This file implements regression tests for SQLite library. The
# focus of this file is testing the CREATE TABLE statement.
#
-# $Id: table.test,v 1.13 2001/09/27 15:11:55 drh Exp $
+# $Id: table.test,v 1.14 2002/02/18 18:30:33 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@@ -302,4 +302,49 @@ do_test table-7.3 {
}
} {desc a asc b explain 9 vacuum 0 delimiters xyz begin hi end y'all}
+# Try out the CREATE TABLE AS syntax
+#
+do_test table-8.1 {
+ execsql2 {
+ CREATE TABLE t2 AS SELECT * FROM weird;
+ SELECT * FROM t2;
+ }
+} {desc a asc b explain 9 vacuum 0 delimiters xyz begin hi end y'all}
+do_test table-8.2 {
+ execsql {
+ CREATE TABLE 't3''xyz'(a,b,c);
+ INSERT INTO [t3'xyz] VALUES(1,2,3);
+ SELECT * FROM [t3'xyz];
+ }
+} {1 2 3}
+do_test table-8.3 {
+ execsql2 {
+ CREATE TABLE [t4'abc] AS SELECT count(*), min(a), max(b+c) FROM [t3'xyz];
+ SELECT * FROM [t4'abc];
+ }
+} {count(*) 1 min(a) 1 max(b+c) 5}
+do_test table-8.4 {
+ execsql2 {
+ CREATE TEMPORARY TABLE t5 AS SELECT count(*) AS [y'all] FROM [t3'xyz];
+ SELECT * FROM t5;
+ }
+} {y'all 1}
+do_test table-8.5 {
+ db close
+ sqlite db test.db
+ execsql2 {
+ SELECT * FROM [t4'abc];
+ }
+} {count(*) 1 min(a) 1 max(b+c) 5}
+do_test table-8.6 {
+ execsql2 {
+ SELECT * FROM t2;
+ }
+} {desc a asc b explain 9 vacuum 0 delimiters xyz begin hi end y'all}
+do_test table-8.7 {
+ catchsql {
+ SELECT * FROM t5;
+ }
+} {1 {no such table: t5}}
+
finish_test
diff --git a/www/changes.tcl b/www/changes.tcl
index 79f18b23d8..2054d13751 100644
--- a/www/changes.tcl
+++ b/www/changes.tcl
@@ -25,6 +25,7 @@ chng {2002 Feb * (2.3.3)} {
(by Joel Luscy)
The VALUES clause of an INSERT can now contain expressions, including
scalar SELECT clauses.
+Added support for CREATE TABLE AS SELECT
}
chng {2002 Feb 14 (2.3.2)} {
diff --git a/www/index.tcl b/www/index.tcl
index 10e78ba103..9d8b021ba6 100644
--- a/www/index.tcl
+++ b/www/index.tcl
@@ -1,7 +1,7 @@
#
# Run this TCL script to generate HTML for the index.html file.
#
-set rcsid {$Id: index.tcl,v 1.53 2002/02/03 19:06:04 drh Exp $}
+set rcsid {$Id: index.tcl,v 1.54 2002/02/18 18:30:33 drh Exp $}
puts {
SQLite: An SQL Database Engine In A C Library
@@ -44,7 +44,7 @@ puts {Features
A complete database (with multiple tables and indices) is
stored in a single disk file.
Atomic commit and rollback protect data integrity.
-Small memory footprint: about 14000 lines of C code.
+Small memory footprint: less than 20K lines of C code.
Four times faster than PostgreSQL.
Twice as fast as SQLite 1.0.
Very simple
diff --git a/www/lang.tcl b/www/lang.tcl
index 1e5a6aa89f..e481384ace 100644
--- a/www/lang.tcl
+++ b/www/lang.tcl
@@ -1,7 +1,7 @@
#
# Run this Tcl script to generate the sqlite.html file.
#
-set rcsid {$Id: lang.tcl,v 1.23 2002/02/18 03:21:47 drh Exp $}
+set rcsid {$Id: lang.tcl,v 1.24 2002/02/18 18:30:33 drh Exp $}
puts {
@@ -245,8 +245,10 @@ CREATE [TEMP | TEMPORARY] TABLE (
[, ]*
[, ]*
)
+} {sql-command} {
+CREATE [TEMP | TEMPORARY] TABLE AS
} {column-def} {
- []*
+ [] []*
} {type} {
|
( ) |
@@ -274,8 +276,8 @@ is the name of the table that records the database schema.
Each column definition is the name of the column followed by the
datatype for that column, then one or more optional column constraints.
-The datatype for the column is ignored. All information
-is stored as null-terminated strings.
+The datatype for the column is (usually) ignored and may be omitted.
+All information is stored as null-terminated strings.
The UNIQUE constraint causes an index to be created on the specified
columns. This index must contain unique keys.
The DEFAULT constraint
@@ -325,11 +327,19 @@ The total amount of data in a single row is limited to about
1 megabytes. (This limit can be increased to 16MB by changing
a single #define in the source code and recompiling.)
+The CREATE TABLE AS form defines the table to be
+the result set of a query. The names of the table columns are
+the names of the columns in the result.
+
The exact text
of each CREATE TABLE statement is stored in the sqlite_master
table. Everytime the database is opened, all CREATE TABLE statements
are read from the sqlite_master table and used to regenerate
-SQLite's internal representation of the table layout.
+SQLite's internal representation of the table layout.
+If the original command was a CREATE TABLE AS then then an equivalent
+CREATE TABLE statement is synthesized and store in sqlite_master
+in place of the original command.
+
}
Section DELETE delete