]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add support for CREATE TABLE AS. (CVS 377)
authordrh <drh@noemail.net>
Mon, 18 Feb 2002 18:30:32 +0000 (18:30 +0000)
committerdrh <drh@noemail.net>
Mon, 18 Feb 2002 18:30:32 +0000 (18:30 +0000)
FossilOrigin-Name: 78a50971e9adc8739e7888201c79465a40e1a152

manifest
manifest.uuid
src/build.c
src/main.c
src/parse.y
src/sqliteInt.h
test/table.test
www/changes.tcl
www/index.tcl
www/lang.tcl

index feacd00e39a7973f8fe0b7ac3095b8ed26c06b32..84a18cf50b95bb13d17160c6899f51ff4ac61d08 100644 (file)
--- 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
index 1caac77919d9f7d08d6ce42bf829b6e8069c12db..398f6666db20faaa6def462a2cb8d0b64f499547 100644 (file)
@@ -1 +1 @@
-ec1f3fae6f8cd8466892cd370e1802e492a76e6e
\ No newline at end of file
+78a50971e9adc8739e7888201c79465a40e1a152
\ No newline at end of file
index c7159f0d6cfc4dc1d2a0f9dd3d6f31e83c987edb..69751158ac51f6834eadbf791d3593806f99df0b 100644 (file)
@@ -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 <ctype.h>
@@ -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; 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.
@@ -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);
   }
 }
index 4408dabbe75e42933775c726bdd99941cae7cb2c..61450217e09f043a5d701bebd9012746e37230d8 100644 (file)
@@ -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++;
index 8d71515be0b1cfd9361e8752a69b926b2bbaaf6f..77c7ceaf26fb96f33074abe74edefead0645e616 100644 (file)
@@ -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.
 
index 03da707bb2f2739a8838889b099edb9c0bff6de7..1f3db95bf5b7b445b6aaaf2ee77a95b20cfc2c6a 100644 (file)
@@ -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);
index 9c41c1fcc964a94a44509db7275cab370565c458..cfe64c60159985b6bf8989cd5227b648616b7ba0 100644 (file)
@@ -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
index 79f18b23d82da3dd8e465139c4f71d57f227c1f2..2054d1375157b4abfce600bf64b11cc3f31cd144 100644 (file)
@@ -25,6 +25,7 @@ chng {2002 Feb * (2.3.3)} {
     (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)} {
index 10e78ba103cdbf3ce06e5cd496e82c78b485e3a6..9d8b021ba61b71815351b01f74341d0d920b2ec4 100644 (file)
@@ -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 {<html>
 <head><title>SQLite: An SQL Database Engine In A C Library</title></head>
@@ -44,7 +44,7 @@ puts {<h2>Features</h2>
 <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 
index 1e5a6aa89f77099aedd8aec59017e47316e9134b..e481384ace7ef9c133b7a81ff6c789c8fc5c14d9 100644 (file)
@@ -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 {<html>
 <head>
@@ -245,8 +245,10 @@ CREATE [TEMP | TEMPORARY] TABLE <table-name> (
   <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> ) |
@@ -274,8 +276,8 @@ is the name of the table that records the database schema.</p>
 
 <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
@@ -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.)</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