[postmodern-devel] query with keyword :on
Jens Teich
info at jensteich.de
Mon Aug 20 21:23:05 UTC 2007
Marijn Haverbeke wrote:
> Aha, I see the syntax error now -- might be some slight difference in
> the way LOOP works on LispWorks. But I'm on a public computer
> somewhere, and will not be near any machines running Lisp in the next
> weeks, so I can't really debug this for you. If you want to solve
> this, you can try digging into the code in s-sql/s-sql.lisp that
> produces such queries, and see where it goes wrong (shouldn't be too
> hard). Or maybe someone else on the list wants to take a quick look?
>
> Cheers,
> Marijn
Hi Marijn,
I looked into the code and found the function expand-join to be a
candidate for cause of the problems:
(defun expand-joins (args)
"Helper for the select operator. Turns the part following :from into
the proper SQL syntax for joining tables."
(labels ((is-join (x) (member x '(:left-join :right-join :inner-join :cross-join))))
(when (null args)
(error "Empty :from clause in select"))
(when (is-join (car args))
(error ":from clause starts with a join: ~A" args))
(loop :for table :on args
:for first = t :then nil
:append (cond ((is-join (car table))
(destructuring-bind (join name on clause) (subseq table 0 4)
(setf table (cdddr table))
;; debug start
(print (list "table" table))
;; debug end
(unless (and (eq on :on) clause)
(error "Incorrect join form in select."))
`(" " ,(ecase join
(:left-join "LEFT") (:right-join "RIGHT")
(:inner-join "INNER") (:cross-join "CROSS"))
" JOIN " ,@(sql-expand name)
" ON " ,@(sql-expand clause))))
(t `(,@(if first () '(", ")) ,@(sql-expand (car table))))))))
I traced it:
S-SQL 68 > (trace expand-joins)
(EXPAND-JOINS)
S-SQL 69 > (sql (:select '* :from 't1 :inner-join 't2 :on (:= 't1.id 't2.id)))
0 EXPAND-JOINS > ...
>> ARGS : ((QUOTE T1) :INNER-JOIN (QUOTE T2) :ON (:= (QUOTE T1.ID) (QUOTE T2.ID)))
0 EXPAND-JOINS < ...
<< VALUE-0 : ("t1" " " "INNER" " JOIN " "t2" " ON " "(" "t1.id" " = " "t2.id" ")" ", " "t2" ", " "on" ", " "(" "t1.id" " = " "t2.id" ")")
"(SELECT * FROM t1 INNER JOIN t2 ON (t1.id = t2.id), t2, on, (t1.id =
t2.id))"
For me it looks a bit strange to declare a loop variable
| :for table :on args
and later on to manipulate this variable with setf
| (setf table (cdddr table))
and indeed LispWorks and SBCL behave differently with such an
expression
LW> (loop for item
on '(1 2 3 4 5)
collect (progn
(setf item (cddr item))
item))
=> ((3 4 5) (4 5) (5) NIL NIL)
SBCL> (loop for item
on '(1 2 3 4 5)
collect (progn
(setf item (cddr item))
item))
=> ((3 4 5) NIL)
where LispWorks produces 'too much' output as in the original problem.
Jens
More information about the postmodern-devel
mailing list