Compare commits

...

6 Commits

Author SHA1 Message Date
drh 818d5bcf28 Remove redundant #ifdef accidentally inserted into mkkeywordhash.c
FossilOrigin-Name: b08f3b74495e125f92c8968472fc8028c73d8df5400195df3081e3d8df8bd985
2024-08-26 14:20:20 +00:00
drh 8941cb4817 Add the AGGREGATE clause to the pipeline.
FossilOrigin-Name: 16d32676e2e98a7710787de16850c328e7519e893f81ed56f6155a8fa9cbc3a4
2024-08-26 14:06:08 +00:00
drh 1082e86417 Working better now.
FossilOrigin-Name: 4bdcc18d2cf4d11a7ca81117f6f8932fc8d280780d2c1e612189b2f5359cc778
2024-08-26 13:20:38 +00:00
drh 7267c510ac Reduce rules added to the grammar. Kinda works, but there are still bugs.
FossilOrigin-Name: 3c7a5cc6d0d186e68a5885299f7474ad2e31b839af404a649dbc8f5b8f1d0a62
2024-08-26 12:26:26 +00:00
drh f53ed4e68e Another version of the parser with a slightly richer syntax. The ghastly
pipe operator is now optional, but is never required.  Pipelines cannot
be initiated from an arbitrary SELECT unless the arbitrary SELECT is a subquery
on the initial FROM.

FossilOrigin-Name: 8781d7352b926129027f73d1fe21df78fcb6d21ab3caeefe0f829437c7c1250b
2024-08-26 02:53:31 +00:00
drh 7210e4c5b4 Proof-of-concept grammar rules to parse Google-style "pipe" syntax for SQL,
without the ghastly "|>" operator.  The grammar rules are not connected to
working code.  They just parse the syntax.  Pipelines that begin with an
ordinary SELECT statement must have the keyword "INTO" (rather than the
"|>" operator) separating the SELECT from the start of the pipeline, to
avoid syntactic and semantic ambiguity.

FossilOrigin-Name: 2c4bae3e68eea14a5edbc753a7a2d764924c95060aff95fa1d2141c11fa77fed
2024-08-26 00:15:35 +00:00
5 changed files with 256 additions and 54 deletions
+9 -9
View File
@@ -1,5 +1,5 @@
C shell.c.in:\suse\seputz/oputz()\sinstead\sof\seputf/oputf()\swhere\sappropriate\sto\savoid\scompilation\serrors\sin\s-std=c99\smode\s(namely\swasm\sbuilds).
D 2024-08-25T11:59:29.477
C Remove\sredundant\s#ifdef\saccidentally\sinserted\sinto\smkkeywordhash.c
D 2024-08-26T14:20:20.876
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -753,7 +753,7 @@ F src/os_win.c 6ff43bac175bd9ed79e7c0f96840b139f2f51d01689a638fd05128becf94908a
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
F src/pager.c b08600ebf0db90b6d1e9b8b6577c6fa3877cbe1a100bd0b2899e4c6e9adad4b3
F src/pager.h 4b1140d691860de0be1347474c51fee07d5420bd7f802d38cbab8ea4ab9f538a
F src/parse.y 5972b7d00af4c8d96fdad781af1ea1d5d51fc3b907ad61bda60e49503274e5ed
F src/parse.y f4c8ded81b05059c284bc9512aaa07f6ecd29add585e35d53d72a67894b6e4d7
F src/pcache.c 588cc3c5ccaaadde689ed35ce5c5c891a1f7b1f4d1f56f6cf0143b74d8ee6484
F src/pcache.h 1497ce1b823cf00094bb0cf3bac37b345937e6f910890c626b16512316d3abf5
F src/pcache1.c 49516ad7718a3626f28f710fa7448ef1fce3c07fd169acbb4817341950264319
@@ -827,7 +827,7 @@ F src/test_windirent.h da2e5b73c32d09905fbdd00f27cd802212a32a58ead882736fe4f5eb7
F src/test_window.c 6d80e11fba89a1796525e6f0048ff0c7789aa2c6b0b11c80827dc1437bd8ea72
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
F src/tokenize.c 3f703cacdab728d7741e5a6ac242006d74fe1c2754d4f03ed889d7253259bd68
F src/tokenize.c 6fac7f14dd021fecfdd8be93c27937804c4b665d2da9e667a10e4b5f04c6e085
F src/treeview.c 88aa39b754f5ef7214385c1bbbdd2f3dc20efafeed0cf590e8d1199b9c6e44aa
F src/trigger.c 0bb986a5b96047fd597c6aac28588853df56064e576e6b81ba777ef2ccaac461
F src/update.c 0e01aa6a3edf9ec112b33eb714b9016a81241497b1fb7c3e74332f4f71756508
@@ -2131,7 +2131,7 @@ F tool/merge-test.tcl de76b62f2de2a92d4c1ca4f976bce0aea6899e0229e250479b229b2a19
F tool/mkautoconfamal.sh cbdcf993fa83dccbef7fb77b39cdeb31ef9f77d9d88c9e343b58d35ca3898a6a
F tool/mkccode.tcl 86463e68ce9c15d3041610fedd285ce32a5cf7a58fc88b3202b8b76837650dbe x
F tool/mkctimec.tcl 060e9785e9503bf51f8b1b11b542bdeef90fd0ceb0738154f6762acec0c61e5f x
F tool/mkkeywordhash.c b9faa0ae7e14e4dbbcd951cddd786bf46b8a65bb07b129ba8c0cfade723aaffd
F tool/mkkeywordhash.c 8a0a0401a6e0f01ca6e02f58b294a813d067554474875f4fe0222c2b8b0d6ed3
F tool/mkmsvcmin.tcl d76c45efda1cce2d4005bcea7b8a22bb752e3256009f331120fb4fecb14ebb7a
F tool/mkopcodec.tcl 33d20791e191df43209b77d37f0ff0904620b28465cca6990cf8d60da61a07ef
F tool/mkopcodeh.tcl 2b4e6967a670ef21bf53a164964c35c6163277d002a4c6f56fa231d68c88d023
@@ -2210,8 +2210,8 @@ F vsixtest/vsixtest.tcl 6195aba1f12a5e10efc2b8c0009532167be5e301abe5b31385638080
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P df65d00f104f31741056686f2ba41ecb192c552012bffb17c2a8b5d4db058328
R 8d4b348b6bf673b94cab6e107b5bbb73
U stephan
Z 5120e8419efd39e01384da30ed409ec4
P 16d32676e2e98a7710787de16850c328e7519e893f81ed56f6155a8fa9cbc3a4
R a100ff1c9bda3fc624920cd66e34ae7c
U drh
Z 58c98d8befdb125e16f0435abf5b1a98
# Remove this line to create a well-formed Fossil manifest.
+1 -1
View File
@@ -1 +1 @@
9ef8317faebc29d016bdf2e8c678fb21ca2cfa95272f1f18b461fcaf0e220ac5
b08f3b74495e125f92c8968472fc8028c73d8df5400195df3081e3d8df8bd985
+231 -40
View File
@@ -268,7 +268,7 @@ columnname(A) ::= nm(A) typetoken(Y). {sqlite3AddColumn(pParse,A,Y);}
GENERATED ALWAYS
%endif
MATERIALIZED
REINDEX RENAME CTIME_KW IF
REINDEX RENAME CTIME_KW IF AGGREGATE
.
%wildcard ANY.
@@ -295,7 +295,8 @@ columnname(A) ::= nm(A) typetoken(Y). {sqlite3AddColumn(pParse,A,Y);}
%left CONCAT PTR.
%left COLLATE.
%right BITNOT.
%nonassoc ON.
%nonassoc ON FROM.
%nonassoc JOIN_KW JOIN.
// An IDENTIFIER can be a generic identifier, or one of several
// keywords. Any non-standard keyword can also be an identifier.
@@ -624,6 +625,233 @@ oneselect(A) ::= SELECT distinct(D) selcollist(W) from(X) where_opt(Y)
}
%endif
%ifndef SQLITE_OMIT_SUBQUERY
%include {
SQLITE_NOINLINE SrcList *sqlite3SrcListAppendSubFrom(
Parse *pParse, /* Parsing context */
SrcList *pFrom, /* LHS of the FROM clause */
SrcList *pSubFrom, /* New parenthesized sub-FROM to append */
Token *pAs, /* AS-Name of the sub-FROM */
OnOrUsing *pOnUsing /* ON or USING clause associated with the join */
){
if( pFrom==0 && pAs->n==0 && pOnUsing->pOn==0 && pOnUsing->pUsing==0 ){
pFrom = pSubFrom;
}else if( ALWAYS(pSubFrom!=0) && pSubFrom->nSrc==1 ){
pFrom = sqlite3SrcListAppendFromTerm(pParse,pFrom,0,0,pAs,0,pOnUsing);
if( pFrom ){
SrcItem *pNew = &pFrom->a[pFrom->nSrc-1];
SrcItem *pOld = pSubFrom->a;
assert( pOld->fg.fixedSchema==0 );
pNew->zName = pOld->zName;
assert( pOld->fg.fixedSchema==0 );
if( pOld->fg.isSubquery ){
pNew->fg.isSubquery = 1;
pNew->u4.pSubq = pOld->u4.pSubq;
pOld->u4.pSubq = 0;
pOld->fg.isSubquery = 0;
assert( pNew->u4.pSubq!=0 && pNew->u4.pSubq->pSelect!=0 );
if( (pNew->u4.pSubq->pSelect->selFlags & SF_NestedFrom)!=0 ){
pNew->fg.isNestedFrom = 1;
}
}else{
pNew->u4.zDatabase = pOld->u4.zDatabase;
pOld->u4.zDatabase = 0;
}
if( pOld->fg.isTabFunc ){
pNew->u1.pFuncArg = pOld->u1.pFuncArg;
pOld->u1.pFuncArg = 0;
pOld->fg.isTabFunc = 0;
pNew->fg.isTabFunc = 1;
}
pOld->zName = 0;
}
sqlite3SrcListDelete(pParse->db, pSubFrom);
}else{
Select *pSubquery;
sqlite3SrcListShiftJoinType(pParse,pSubFrom);
pSubquery = sqlite3SelectNew(pParse,0,pSubFrom,0,0,0,0,SF_NestedFrom,0);
pFrom = sqlite3SrcListAppendFromTerm(pParse,pFrom,0,0,pAs,pSubquery,pOnUsing);
}
return pFrom;
}
}
%endif SQLITE_OMIT_SUBQUERY
%ifndef SQLITE_OMIT_FROM_FIRST
%include {
/* The Pipe object represents a pipe under construction. It is a transient
** object found only on the LALR(1) parser stack. */
typedef struct Pipe Pipe;
struct Pipe {
SrcList *pFrom; /* The FROM clause */
ExprList *pProj; /* The projection - list of columns to return */
ExprList *pOrderBy; /* The ORDER BY clause */
ExprList *pGroupBy; /* The GROUP BY clause */
Expr *pLimit; /* The LIMIT or LIMIT/OFFSET clause */
Expr *pWhere; /* The WHERE clause */
Expr *pHaving; /* The HAVING clause */
int selFlags; /* SF_DISTINCT or SF_ALL or 0 */
u8 bSeenAgg; /* True if AGGREGATE has been seen */
u8 bNeedPush; /* PipePush needed before adding terms to pProj */
Token sAs; /* Value of a pending AS clause */
};
/* Delete a pipe object */
void sqlite3PipeDelete(sqlite3 *db, Pipe *p){
sqlite3SrcListDelete(db, p->pFrom);
sqlite3ExprListDelete(db, p->pProj);
sqlite3ExprListDelete(db, p->pOrderBy);
sqlite3ExprListDelete(db, p->pGroupBy);
sqlite3ExprDelete(db, p->pLimit);
sqlite3ExprDelete(db, p->pWhere);
sqlite3ExprDelete(db, p->pHaving);
sqlite3DbFree(db, p);
}
/* Generate a Select from the current content of a Pipe. Reset the Pipe
** to be empty, except do not remove the AS token if there is one and do
** not delete the Pipe.
*/
Select *sqlite3SelectFromPipe(Parse *pParse, Pipe *p){
Token sAs = p->sAs;
Select *pSel = sqlite3SelectNew(pParse, p->pProj, p->pFrom, p->pWhere,
p->pGroupBy, p->pHaving, p->pOrderBy,
p->selFlags, p->pLimit);
memset(p, 0, sizeof(*p));
p->sAs = sAs;
return pSel;
}
/* Take the current content of the pipe and make it "FROM <subquery>" pipe.
*/
void sqlite3PipePush(Parse *pParse, Pipe *pPipe, int jointype){
Select *pSel = sqlite3SelectFromPipe(pParse, pPipe);
SrcList *pFrom = pPipe->pFrom;
if( pFrom ) pFrom->a[pFrom->nSrc-1].fg.jointype = jointype;
pPipe->pFrom = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&pPipe->sAs,pSel,0);
pPipe->sAs.z = 0;
}
}
%type pipeline {Pipe*}
%destructor pipeline {sqlite3PipeDelete(pParse->db,$$);}
oneselect(A) ::= pipeline(X). {
Pipe *pPipe = X;
Select *pSel = sqlite3SelectFromPipe(pParse,pPipe);
sqlite3DbFree(pParse->db, pPipe);
A = pSel; /*A-overwrites-X */
}
pipeline(A) ::= FROM seltablist(X). {
Pipe *pPipe = sqlite3DbMallocZero(pParse->db, sizeof(*pPipe));
A = pPipe;
if( pPipe ){
pPipe->pFrom = X;
}else{
sqlite3SrcListDelete(pParse->db, X);
}
}
pipeline(A) ::= pipeline(A) pipe pipejoinop(J) nm(Y) dbnm(D) as(Z) on_using(N). {
Pipe *p = A;
if( p->bNeedPush ) sqlite3PipePush(pParse, p, J);
p->pFrom = sqlite3SrcListAppendFromTerm(pParse, p->pFrom, &Y, &D, &Z, 0, &N);
}
pipeline(A) ::= pipeline(A) pipe
pipejoinop(J) nm(Y) dbnm(D) LP exprlist(E) RP as(Z) on_using(N). {
Pipe *p = A;
if( p->bNeedPush ) sqlite3PipePush(pParse, p, J);
p->pFrom = sqlite3SrcListAppendFromTerm(pParse, p->pFrom, &Y, &D, &Z, 0, &N);
sqlite3SrcListFuncArgs(pParse, p->pFrom, E);
}
pipeline(A) ::= pipeline(A) pipe pipejoinop(J) LP select(S) RP as(Z) on_using(N). {
Pipe *p = A;
if( p->bNeedPush ) sqlite3PipePush(pParse, p, J);
p->pFrom = sqlite3SrcListAppendFromTerm(pParse,p->pFrom,0,0,&Z,S,&N);
}
pipeline(A) ::= pipeline(A) pipe pipejoinop(J) LP seltablist(F) RP as(Z) on_using(N).{
Pipe *p = A;
if( p->bNeedPush ) sqlite3PipePush(pParse, p, J);
p->pFrom = sqlite3SrcListAppendSubFrom(pParse,p->pFrom,F,&Z,&N);
}
pipeline(A) ::= pipeline(A) pipe WHERE expr(W). {
Pipe *p = A;
if( p->bSeenAgg ){
p->pHaving = sqlite3ExprAnd(pParse, A->pHaving, W);
}else{
p->pWhere = sqlite3ExprAnd(pParse, A->pWhere, W);
}
p->bNeedPush = 1;
}
pipeline(A) ::= pipeline(A) pipe HAVING expr(W). {
Pipe *p = A;
p->pHaving = sqlite3ExprAnd(pParse, A->pHaving, W);
p->bNeedPush = 1;
}
pipeline(A) ::= pipeline(A) pipe AGGREGATE selcollist(W) groupby_opt(G). {
Pipe *p = A;
ExprList *pProj = W;
ExprList *pGroupby = G;
if( p->pProj ) sqlite3PipePush(pParse, p, 0);
if( pGroupby ){
int i;
for(i=0; i<pGroupby->nExpr; i++){
pProj = sqlite3ExprListAppend(pParse, pProj,
sqlite3ExprDup(pParse->db, pGroupby->a[i].pExpr, 0));
}
p->pGroupBy = pGroupby;
}
p->pProj = pProj;
p->bSeenAgg = 1;
p->bNeedPush = 1;
}
pipeline(A) ::= pipeline(A) pipe SELECT distinct(D) selcollist(W). {
Pipe *p = A;
if( p->pProj ) sqlite3PipePush(pParse, p, 0);
p->pProj = W;
p->selFlags = D;
p->bNeedPush = 1;
}
pipeline(A) ::= pipeline(A) pipe ORDER BY sortlist(X). {
Pipe *p = A;
if( p->pOrderBy || p->pLimit ) sqlite3PipePush(pParse, p, 0);
p->pOrderBy = X;
p->bNeedPush = 1;
}
pipeline(A) ::= pipeline(A) pipe LIMIT expr(X). {
Pipe *p = A;
Expr *pLimit = sqlite3PExpr(pParse,TK_LIMIT,X,0);
if( pLimit ){
sqlite3ExprDelete(pParse->db, p->pLimit);
p->pLimit = pLimit;
}
p->bNeedPush = 1;
}
pipeline(A) ::= pipeline(A) pipe LIMIT expr(X) OFFSET expr(Y). {
Pipe *p = A;
Expr *pLimit = sqlite3PExpr(pParse,TK_LIMIT,X,Y);
if( pLimit ){
sqlite3ExprDelete(pParse->db, p->pLimit);
p->pLimit = pLimit;
}
p->bNeedPush = 1;
}
pipeline(A) ::= pipeline(A) PIPE AS nm(X). {A->sAs = X; A->bNeedPush = 1;}
pipeline(A) ::= pipeline(A) pipe VIEW nm(X). {A->sAs = X; A->bNeedPush = 1;}
//pipeline ::= pipeline pipe DISTINCT ON nexprlist.
pipe ::= .
pipe ::= PIPE.
%type pipejoinop {int}
pipejoinop(X) ::= JOIN. { X = JT_INNER; }
pipejoinop(X) ::= JOIN_KW(A) JOIN.
{X = sqlite3JoinType(pParse,&A,0,0); /*X-overwrites-A*/}
pipejoinop(X) ::= JOIN_KW(A) nm(B) JOIN.
{X = sqlite3JoinType(pParse,&A,&B,0); /*X-overwrites-A*/}
pipejoinop(X) ::= JOIN_KW(A) nm(B) nm(C) JOIN.
{X = sqlite3JoinType(pParse,&A,&B,&C);/*X-overwrites-A*/}
%endif FROM_first
// Single row VALUES clause.
//
@@ -733,44 +961,7 @@ seltablist(A) ::= stl_prefix(A) nm(Y) dbnm(D) LP exprlist(E) RP as(Z) on_using(N
A = sqlite3SrcListAppendFromTerm(pParse,A,0,0,&Z,S,&N);
}
seltablist(A) ::= stl_prefix(A) LP seltablist(F) RP as(Z) on_using(N). {
if( A==0 && Z.n==0 && N.pOn==0 && N.pUsing==0 ){
A = F;
}else if( ALWAYS(F!=0) && F->nSrc==1 ){
A = sqlite3SrcListAppendFromTerm(pParse,A,0,0,&Z,0,&N);
if( A ){
SrcItem *pNew = &A->a[A->nSrc-1];
SrcItem *pOld = F->a;
assert( pOld->fg.fixedSchema==0 );
pNew->zName = pOld->zName;
assert( pOld->fg.fixedSchema==0 );
if( pOld->fg.isSubquery ){
pNew->fg.isSubquery = 1;
pNew->u4.pSubq = pOld->u4.pSubq;
pOld->u4.pSubq = 0;
pOld->fg.isSubquery = 0;
assert( pNew->u4.pSubq!=0 && pNew->u4.pSubq->pSelect!=0 );
if( (pNew->u4.pSubq->pSelect->selFlags & SF_NestedFrom)!=0 ){
pNew->fg.isNestedFrom = 1;
}
}else{
pNew->u4.zDatabase = pOld->u4.zDatabase;
pOld->u4.zDatabase = 0;
}
if( pOld->fg.isTabFunc ){
pNew->u1.pFuncArg = pOld->u1.pFuncArg;
pOld->u1.pFuncArg = 0;
pOld->fg.isTabFunc = 0;
pNew->fg.isTabFunc = 1;
}
pOld->zName = 0;
}
sqlite3SrcListDelete(pParse->db, F);
}else{
Select *pSubquery;
sqlite3SrcListShiftJoinType(pParse,F);
pSubquery = sqlite3SelectNew(pParse,0,F,0,0,0,0,SF_NestedFrom,0);
A = sqlite3SrcListAppendFromTerm(pParse,A,0,0,&Z,pSubquery,&N);
}
A = sqlite3SrcListAppendSubFrom(pParse,A,F,&Z,&N);
}
%endif SQLITE_OMIT_SUBQUERY
+9 -4
View File
@@ -372,12 +372,17 @@ int sqlite3GetToken(const unsigned char *z, int *tokenType){
}
}
case CC_PIPE: {
if( z[1]!='|' ){
*tokenType = TK_BITOR;
return 1;
}else{
if( z[1]=='|' ){
*tokenType = TK_CONCAT;
return 2;
#ifdef TK_PIPE
}else if( z[1]=='>' ){
*tokenType = TK_PIPE;
return 2;
#endif
}else{
*tokenType = TK_BITOR;
return 1;
}
}
case CC_COMMA: {
+6
View File
@@ -164,6 +164,11 @@ struct Keyword {
#else
# define RETURNING 0x00400000
#endif
#ifdef SQLITE_OMIT_FROM_FIRST
# define PIPE 0
#else
# define PIPE 0x00800000
#endif
/*
@@ -174,6 +179,7 @@ static Keyword aKeywordTable[] = {
{ "ACTION", "TK_ACTION", FKEY, 0 },
{ "ADD", "TK_ADD", ALTER, 1 },
{ "AFTER", "TK_AFTER", TRIGGER, 0 },
{ "AGGREGATE", "TK_AGGREGATE", PIPE, 10 },
{ "ALL", "TK_ALL", ALWAYS, 0 },
{ "ALTER", "TK_ALTER", ALTER, 0 },
{ "ALWAYS", "TK_ALWAYS", GENCOL, 0 },