X-Git-Url: http://git.kpe.io/?a=blobdiff_plain;f=sql%2Fexpressions.lisp;h=efb54bb650f00338d4b17590b9e6d346060aa3ef;hb=b06efa82;hp=770bf379a69c1e74ecd9a51e0204d81107e2e6db;hpb=8456b79be8685d58e341aaadbdb9063a42729304;p=clsql.git diff --git a/sql/expressions.lisp b/sql/expressions.lisp index 770bf37..efb54bb 100644 --- a/sql/expressions.lisp +++ b/sql/expressions.lisp @@ -1,8 +1,6 @@ ;;;; -*- Mode: LISP; Syntax: ANSI-Common-Lisp; Base: 10 -*- ;;;; ************************************************************************* ;;;; -;;;; $Id$ -;;;; ;;;; Classes defining SQL expressions and methods for formatting the ;;;; appropriate SQL commands. ;;;; @@ -194,15 +192,25 @@ sql `(make-instance 'sql-ident-table :name ',name :table-alias ',alias))) +(defun special-char-p (s) + "Check if a string has any special characters" + (loop for char across s + thereis (find char '(#\space #\, #\. #\! #\@ #\# #\$ #\% + #\^ #\& #\* #\| #\( #\) #\- #\+)))) + (defmethod output-sql ((expr sql-ident-table) database) (with-slots (name alias) expr - (etypecase name - (string - (format *sql-stream* "~s" (sql-escape name))) - (symbol - (write-string (sql-escape name) *sql-stream*))) - (when alias - (format *sql-stream* " ~s" alias))) + (flet ((p (s) ;; the etypecase is in sql-escape too + (let ((sym? (symbolp s)) + (s (sql-escape s))) + (format *sql-stream* + (if (and sym? (not (special-char-p s))) + "~a" "~s") + s)))) + (p name) + (when alias + (princ #\space *sql-stream*) + (p alias)))) t) (defmethod output-sql-hash-key ((expr sql-ident-table) database) @@ -249,12 +257,7 @@ ;; the next loop simply emits each sub-expression with the appropriate number of ;; parens and operators (flet ((trim (sub) - (string-trim '(#\space #\newline #\return #\tab - ;; sbcl, allegrocl, and clisp use #\no-break_space - ;; lispworks uses #\no-break-space - #-lispworks #\no-break_space - #+lispworks #\no-break-space - ) + (string-trim +whitespace-chars+ (with-output-to-string (*sql-stream*) (output-sql sub database))))) (let ((str-subs (loop for sub in sub-expressions @@ -418,7 +421,7 @@ (defmethod output-sql ((expr sql-query-modifier-exp) database) (with-slots (modifier components) expr - (output-sql modifier database) + (%write-operator modifier database) (write-string " " *sql-stream*) (output-sql (car components) database) (when components @@ -584,13 +587,17 @@ uninclusive, and the args from that keyword to the end." (write-string "(" *sql-stream*)) (write-string "SELECT " *sql-stream*) (when all - (write-string "ALL " *sql-stream*)) + (write-string " ALL " *sql-stream*)) (when (and distinct (not all)) - (write-string "DISTINCT " *sql-stream*) + (write-string " DISTINCT " *sql-stream*) (unless (eql t distinct) - (write-string "ON " *sql-stream*) + (write-string " ON " *sql-stream*) (output-sql distinct database) (write-char #\Space *sql-stream*))) + (when (and limit (eql :mssql (database-underlying-type database))) + (write-string " TOP " *sql-stream*) + (output-sql limit database) + (write-string " " *sql-stream*)) (let ((*in-subselect* t)) (output-sql (apply #'vector selections) database)) (when from @@ -618,9 +625,14 @@ uninclusive, and the args from that keyword to the end." (write-string " ON " *sql-stream*) (output-sql on database)) (when where - (write-string " WHERE " *sql-stream*) - (let ((*in-subselect* t)) - (output-sql where database))) + (let ((where-out (string-trim + '(#\newline #\space #\tab #\return) + (with-output-to-string (*sql-stream*) + (let ((*in-subselect* t)) + (output-sql where database)))))) + (when (> (length where-out) 0) + (write-string " WHERE " *sql-stream*) + (write-string where-out *sql-stream*)))) (when group-by (write-string " GROUP BY " *sql-stream*) (if (listp group-by) @@ -654,7 +666,7 @@ uninclusive, and the args from that keyword to the end." (when (cdr order) (write-char #\, *sql-stream*)))) (output-sql order-by database))) - (when limit + (when (and limit (not (eql :mssql (database-underlying-type database)))) (write-string " LIMIT " *sql-stream*) (output-sql limit database)) (when offset @@ -794,9 +806,9 @@ uninclusive, and the args from that keyword to the end." (declaim (inline listify)) (defun listify (x) - (if (atom x) - (list x) - x)) + (if (listp x) + x + (list x))) (defmethod output-sql ((stmt sql-create-table) database) (flet ((output-column (column-spec) @@ -987,7 +999,10 @@ uninclusive, and the args from that keyword to the end." (cons (symbol-name-default-case "UNSIGNED") "UNSIGNED") (cons (symbol-name-default-case "ZEROFILL") "ZEROFILL") (cons (symbol-name-default-case "AUTO-INCREMENT") "AUTO_INCREMENT") - (cons (symbol-name-default-case "UNIQUE") "UNIQUE"))) + (cons (symbol-name-default-case "DEFAULT") "DEFAULT") + (cons (symbol-name-default-case "UNIQUE") "UNIQUE") + (cons (symbol-name-default-case "IDENTITY") "IDENTITY (1,1)") ;; added for sql-server support + )) (defmethod database-constraint-statement (constraint-list database) (declare (ignore database)) @@ -1006,6 +1021,9 @@ uninclusive, and the args from that keyword to the end." :message (format nil "unsupported column constraint '~A'" constraint)) (setq string (concatenate 'string string (cdr output)))) + (when (equal (symbol-name (car constraint)) "DEFAULT") + (setq constraint (cdr constraint)) + (setq string (concatenate 'string string " " (car constraint)))) (if (< 1 (length constraint)) (setq string (concatenate 'string string " "))))))))