-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
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
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
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
-ec1f3fae6f8cd8466892cd370e1802e492a76e6e
\ No newline at end of file
+78a50971e9adc8739e7888201c79465a40e1a152
\ No newline at end of file
** 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 <ctype.h>
}
}
+/*
+** 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; i<p->nCol; 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; i<p->nCol; 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.
** 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;
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
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);
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);
}
}
** 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"
case '\f': {
break;
}
+ case '[': {
+ isComplete = 0;
+ zSql++;
+ while( *zSql && *zSql!=']' ){ zSql++; }
+ if( *zSql==0 ) return 0;
+ break;
+ }
case '\'': {
isComplete = 0;
zSql++;
** 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}
///////////////////// 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.
*************************************************************************
** 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"
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);
# 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
}
} {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
(by Joel Luscy)</li>
<li>The VALUES clause of an INSERT can now contain expressions, including
scalar SELECT clauses.</li>
+<li>Added support for CREATE TABLE AS SELECT</li>
}
chng {2002 Feb 14 (2.3.2)} {
#
# 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 {<html>
<head><title>SQLite: An SQL Database Engine In A C Library</title></head>
<li>A complete database (with multiple tables and indices) is
stored in a single disk file.</li>
<li>Atomic commit and rollback protect data integrity.</li>
-<li>Small memory footprint: about 14000 lines of C code.</li>
+<li>Small memory footprint: less than 20K lines of C code.</li>
<li><a href="speed.html">Four times faster</a> than PostgreSQL.
Twice as fast as SQLite 1.0.</li>
<li>Very simple
#
# 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 {<html>
<head>
<column-def> [, <column-def>]*
[, <constraint>]*
)
+} {sql-command} {
+CREATE [TEMP | TEMPORARY] TABLE <table-name> AS <select-statement>
} {column-def} {
-<name> <type> [<column-constraint>]*
+<name> [<type>] [<column-constraint>]*
} {type} {
<typename> |
<typename> ( <number> ) |
<p>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
1 megabytes. (This limit can be increased to 16MB by changing
a single #define in the source code and recompiling.)</p>
+<p>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.</p>
+
<p>The exact text
of each CREATE TABLE statement is stored in the <b>sqlite_master</b>
table. Everytime the database is opened, all CREATE TABLE statements
are read from the <b>sqlite_master</b> table and used to regenerate
-SQLite's internal representation of the table layout.</p>
+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 <b>sqlite_master</b>
+in place of the original command.
+</p>
}
Section DELETE delete