septeql

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

commit fd7b52cf454e0d3e728fca0d96d6841857dededa
parent 1c5e559f907c867fae6c09b7b09c5b347de8e7e0
Author: septeql <septeql@telent.net>
Date:   Wed,  8 Aug 2007 17:14:22 +0000

documentation never hurt anyone
Diffstat:
AREADME | 179+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 179 insertions(+), 0 deletions(-)

diff --git a/README b/README @@ -0,0 +1,179 @@ + +SEPTEQL + + +--- WARNING --------------------------------------------------------+ + | This is *work in progress*: syntax may (quite likely, will) change | + | without warning. | + +--------------------------------------------------------------------+ + +SEPTEQL is a Lispy composable query language for talking to RDBMSes. +It translates to SQL, so (at least in principle) is database- and +driver-independent and will talk to anything. + +* Motivating Examples + +(to-sql '(join rel1 rel2 predicate)) +=> "SELECT * FROM (REL1 JOIN REL2 ON PREDICATE) " + +(to-sql '(project (att1 (as att2 name) att3) relation)) +=> "SELECT ATT1,ATT2 AS NAME,ATT3 FROM RELATION " + +(to-sql '(order (limit relation 5 15) attribute :desc) ) + ; why anyone would want to limit *first* and then order the result I + ; don't know, but look, it handles that too +=> "SELECT * FROM (SELECT * FROM RELATION OFFSET 5 LIMIT 10 ) RELATION ORDER BY ATTRIBUTE DESC " + +(to-sql '(group ((as (sum quantity) quantity) + (as (sum (* quantity cost)) value)) + (event-id event-description) + order-line)) +=> "SELECT SUM(QUANTITY) AS QUANTITY,SUM((QUANTITY * COST)) AS VALUE,EVENT_ID,EVENT_DESCRIPTION FROM ORDER_LINE GROUP BY EVENT_ID,EVENT_DESCRIPTION " + +Backquote and comma are your friends [1] + +* Syntax overview + +Look at tests.lisp for examples. This is all subject to change, so +UTSL is not bad advice. + +** Relations + +At the risk of doing considerable violence to accepted terminology: a +relation is an "ordered bag" of tuples (aka rows) [2]. A tuple is a bag +of attributes (informally, columns), each attribute having a name and +a value. + +(SELECT relation predicate) +- get rows that match +(PROJECT attributes relation) +- get columns that match +(JOIN r1 r2 expr) +- inner join of R1 and R2 where EXPR +(GROUP aggregates group-by rel) +- for each distinct value of the attributes GROUP-BY in REL, apply AGGREGATES + across all rows of REL +(ORDER relation attribute &optional direction) +- Sort RELATION according to ATTRIBUTE. DIRECTION is :asc or :desc +(RENAME relation new-name) +- what it says. Also has the effect of "flattening" a relation: for example + (project (a.foo (as b.foo bar)) (a join b on p)) + is OK, but not + (project (a.foo (as b.foo bar)) (rename (a join b on b) c)) + because the "a" and "b" prefixes get squashed. +(LIMIT relation start end) +- return rows START <= n < END. Note exclusive upper bound, as in CL + sequence functions + +Changes likely in the following areas: +- ORDER to gain some way of sorting on multiple columns +- some kind of syntax for left and right joins (more probably + new operators than extending JOIN with keywords) + + +** Scalars + +Values that are not relations we call "scalars". Scalar expressions +are e.g. the predicate in a SELECT, the expr in a JOIN, and so on. + +Scalars include attribute names, literals (string, numeric, booleans) and +functions that work on them. We use a Lisp-style prefix +representation: thus + +1 +"fish" +2d0 +t +(+ 1 2) +(>= att1 17) +(like att2 "hello%") +(++ "hello " "world") ; string catenation using the SQL || operator +(sum quantity) +(case ((p1 res1) (p2 res2) ... (else res-default))) + +CAUTION NEEDED: How we deal with NIL is largely undecided, as SQL has +distinct NULL and FALSE, but CL doesn't + +Operators in the list *INFIX-SYNTAX* are transformed to infix syntax. +Some stuff (CASE, ++, AS) is handled specially. Anything we don't +recognise is assumed to be a call to a SQL function. + +Note that CASE in SQL is more like CL's COND (where p1, p2 ... are +arbitrary predicates) than its CASE (where they are literals possible +values of a key form). To that end, we have defined COND as an alias +for CASE, mostly for the convenience of editors with magic indentation +that will otherwise get it wrong. + +We don't have provision for many of the SQL string-processing +functions with stupid positional syntax; expect that to be added if +and when I need to use them. Or add them yourself and send patches. + + +* Installation + +Works best with ASDF, but that's probably not a particularly onerous +requirement these days. If you don't have it, arrange for +defpackage.lisp and septeql.lisp to be compiled and loaded in that +order. + + +* Compatibility + +Septeql is developed in SBCL and Postgresql. The author would like to +hear about importabilities that break it when used with other ANSI CL +implementations or SQL databases, but is not particularly interested +in filling it full of ugly workarounds if the problem is with the +Lisp/RDBMS rather than with Septeql. Done that too often already, +thanks. + + +* Object-Relational Mapping: a short rant + + "And that means the Great Orm will come in the night and wind out my + entrails on a stick!" + "Does it?" + "And suck out my eyes, my mother said" + "Gosh!" said Teppic, fascinated. "Really?" He was quite glad his + bed was opposite Arthur's and would offer an unrivalled view. + + - Terry Pratchett, Pyramids + + +Everyone who was anywhere near the dotcom boom has written an ORM at +some time in their life. Some of us who Just Don't Learn have written +several. + +I'm going to ignore the fundamental theoretical problems with +declaring tuples and instances to be equivalent. This is because (a) +I don't pretend to understand them, I only know that C J Date is +smarter than I am [3], and (b) nobody who works with databases in the +so-called Real World cares one jot or tittle for the theory anyway. +So let's just say + +* having to keep the object schema in sync with the database schema is +burdensome. You might have tools to autogenerate an empty database +from your object definitions, but being able to automatically migrate +all your existing data when you make schema changes is a lot rarer. + +* the OO chase-the-pointers style of data interrogation makes for a +million DB roundtrips, which is grossly inefficient. Better to do the +joins in the database and get everything you need in one step, but how +do you squish that back into objects? + +* object identity usually goes out the window when you can retrieve +the same row by more than one route. And semi-reliable object +identity is worse than none. + +* there isn't a 1:1 relationship between entities in the problem +domain and tables in the database. If you have a Colour class that +exists solely to map onto the COLOUR table (because you can't put all +its contents in a dropdown list otherwise), I'd call that a code +smell. You'll never use it outside of the CRUD screens. + + +[1] Well, they're my friends anyway. They talk to me when I sleep, +you know. They tell me what to do. + +[2] If we were talking to a proper relational system, there would be no +ordering and no duplicate rows. In the so-called Real World, however ... + +[3] I only hope I can stay out of the dbdebunk.com Hall of Shame, is all.