cl-2sql

Lisp to SQL compiler for Common Lisp
git clone https://logand.com/git/cl-2sql.git/
Log | Files | Refs | README | LICENSE

test.lisp (19987B)


      1 ;;; cl-2sql Copyright (c) 2011 Tomas Hlavaty
      2 
      3 (defpackage 2sql-tests
      4   (:use :cl :2sql))
      5 
      6 (in-package :2sql-tests)
      7 
      8 ;; (setq c (make-sqlite-server "./dbquery-sqlite" "sqlite.db"))
      9 ;; (time (funcall c "select 1, 2+3"))
     10 ;; (time (funcall c "select 4, 'hello'"))
     11 ;; (funcall c nil)
     12 ;; (funcall c)
     13 
     14 ;; (setq c (make-pg-server "./dbquery-pg" "dbname='pokus' user='tomas'"))
     15 ;; (time (funcall c "select 1, 2+3"))
     16 ;; (time (funcall c "select 4, 'hello'"))
     17 ;; (funcall c nil)
     18 ;; (funcall c)
     19 
     20 ;; (setq c (make-mysql-server "./dbquery-mysql" "localhost" "tomas" "Ri3OoL3h" "pokus"))
     21 ;; (time (funcall c "select 1, 2+3"))
     22 ;; (time (funcall c "select 4, 'hello'"))
     23 ;; (funcall c nil)
     24 ;; (funcall c)
     25 
     26 
     27 (loop
     28    for (2sql:*backend* backend)
     29    in (list
     30        (list :postgresql
     31              (2sql-dbquery:make-backend
     32               (dbquery:make-pg-server "/home/tomas/git/dbquery/dbquery-pg"
     33                                       "dbname='pokus' user='tomas'")))
     34        #+nil
     35        (list :postgresql
     36              (2sql-cl-postgres:make-backend "pokus" "tomas" "test123" "localhost" 5432 :no))
     37        #+nil
     38        (list :sqlite
     39              (2sql-dbquery:make-backend
     40               (dbquery:make-sqlite-server "/home/tomas/git/dbquery/dbquery-sqlite"
     41                                           "/home/tomas/git/dbquery/sqlite.db")))
     42        #+nil ;; TODO dbquery-mysql
     43        (list :mysql
     44              (2sql-dbquery:make-backend
     45               (dbquery:make-mysql-server "/home/tomas/git/dbquery/dbquery-mysql"
     46                                          "localhost" "tomas" "Ri3OoL3h" "pokus"))))
     47    collect (progn ;;time
     48             (2sql:with-server ((2sql::make-server backend))
     49               (2sql:with-backend (2sql:*backend*)
     50                 (let ((a 2))
     51                   ;;(EXECUTE "SELECT |/ $1::INTEGER" (LIST A))
     52                   ;;(funcall (compile nil (lambda (a) (query (q:select ((q:sqrt (q:qvar a :integer))))))) a)
     53                   ;;(query (q:select ((q:sqrt (q:qvar a :integer)))))
     54                   (let ((q (qlambda (a)
     55                              #+nil
     56                              (q:select ((q:sqrt 2)))
     57                              #+nil
     58                              (q:select ((q:sqrt (q:qvar a #+nil(q:integer-type)))))
     59                              (q:select ((q:sqrt (q:qvar a (q:integer-type))))))))
     60                     (progn ;;time
     61                      (list 2sql:*backend*
     62                            (funcall q a)
     63                            (funcall q a))))))))))
     64 
     65 
     66 
     67 
     68 
     69 
     70 ;; s-sql
     71 (to-string
     72  '(q:select ((q:+ field-1 100 @var) field-5)
     73    (q:from (q:left-join my-table #+nil(my-table x) your-table (q:= x.field-2 your-table.field-1)))
     74    (q:where (q:and field-1 field-2 (q:not field-1)))))
     75 
     76 '(:select ((:+ field-1 100 @var) field-5)
     77   (:from (:left-join my-table #+nil(my-table x) your-table (:= x.field-2 your-table.field-1)))
     78   (:where (:not-null a.field-3)))
     79 
     80 '(:select (:+ 'field-1 100) 'field-5
     81   :from (:as 'my-ta
     82   :left-join 'your-table :on (:= 'x.field-2 'your-table.field-1)
     83   :where (:not-null 'a.field-3)))
     84 
     85 (to-string '(q:select ((q:+ 1 1) (q:- 1 1)) (q:from :dual)))
     86 (to-string '(q:select (:sysdate) (q:from :dual)))
     87 (to-string '(q:select (user) (q:from :dual)))
     88 (to-string '(q:select ((q:+ 1 1) x.sysdate) (q:from :dual)))
     89 (to-string '(q:delete-from user))
     90 (to-string '(q:delete-from user (q:where (q:= name "John"))))
     91 
     92 
     93 (defun optional (value)
     94   (when value (list value)))
     95 
     96 
     97 
     98 (to-string '(q:drop-table user))
     99 (to-string '(q:drop-table user nil t))
    100 (to-string '(q:drop-table user t))
    101 (to-string '(q:drop-table user t t))
    102 
    103 (to-string '(q:drop-view user nil))
    104 (to-string '(q:drop-view user t))
    105 
    106 (to-string '(q:select ((q:cond ((q:zerop x) "yes") (t "no"))) (q:from :dual)))
    107 (to-string '(q:select ((q:count (q:distinct user))) (q:from :dual)))
    108 
    109 ;; clsql
    110 (q:select [Researcher] :from '([TreeData] [SpeciesList])
    111         :where [and [= [TreeData SpeciesID]
    112         [SpeciesList SpeciesID]]
    113         [like [LocalName] "v%"]]
    114         :distinct t :flatp t)
    115 
    116 (to-string
    117  '(q:select ((q:distinct researcher))
    118    (q:from treedata specieslist)
    119    (q:where (q:and (q:= treedata.speciesid specieslist.speciesid)
    120                    (q:like localname "v%")))))
    121 
    122 (to-string
    123  '(q:select (:*)
    124    (q:from specieslist)
    125    (q:where (q:between speciesid 6 9))))
    126 
    127 (print-query "select * from SpeciesList
    128                             where (SpeciesID between 6 and 9)"
    129                            :titles (list-attributes "SpeciesList"))
    130 
    131 
    132 
    133 (to-string '(q:select (:*) (q:from x) (q:where (q:qvar hi))))
    134 -> "SELECT * FROM x_ WHERE :1", ((hi)), NIL
    135 
    136 (let ((v1 :v1)
    137       (v2 :v2))
    138   (query
    139    (q:select (:*)
    140      (q:from x)
    141      (q:where (q:and (q:= (q:qvar v1) (q:qchunk (+ 1 2)))
    142                      (q:= (q:qvar v2) (q:qchunk (+ 3 4))))))))
    143 
    144 (let ((v1 "hello")
    145       (v2 314159))
    146   (query
    147    (q:select (:*)
    148      (q:from x)
    149      (q:where (q:= (q:qchunk (+ 1 2)) (q:qchunk "hello"))))))
    150 
    151 
    152 
    153 
    154 
    155 
    156 
    157 (2sql-lambda ()
    158   (q:select (:*)
    159     (q:from x)
    160     (q:where (q:= (q:qchunk (+ 1 2)) (q:qchunk (+ 3 4))))))
    161 
    162 (define-function dasd ()
    163   (q:select (:*)
    164     (q:from x)
    165     (q:where (q:= (q:qchunk (+ 1 2)) (q:qchunk (+ 3 4))))))
    166 
    167 (dasd)
    168 
    169 
    170 
    171 (define-function myfun (v1 v2)
    172   (q:select (:*)
    173     (q:from x)
    174     (q:where (q:= (q:qvar v1) (q:qvar v2)))))
    175 
    176 (myfun 1 2)
    177 
    178 (define-function myfun (a1 a2)
    179   (q:select (:*)
    180     (q:from x)
    181     (q:where (q:= a1 a2))))
    182 
    183 
    184 
    185 
    186 
    187 (to-string
    188  '(q:select (:*)
    189    (q:from x)
    190    (q:where (q:= (q:qvar v1) (q:qvar v2)))))
    191 
    192 (to-string
    193  '(q:select (:*)
    194    (q:from x)
    195    (q:where (q:= (q:qchunk (+ 1 2)) (q:qchunk (+ 3 4))))))
    196 
    197 
    198 
    199 
    200 (let ((v1 "hello")
    201       (v2 314159))
    202   (2sql:query (v1 v2)
    203     '(q:select (q:+ (q:qvar v1) (q:qvar v2)))))
    204 
    205 
    206 
    207 
    208 (let ((v1 "hello")
    209       (v2 314159))
    210   (2sql:query (v1 v2) ;; macroexpand
    211     '(q:select (:*)
    212       (q:from x)
    213       (q:where (q:where (q:= (q:qvar v1) (q:qvar v2)))))))
    214 
    215 (let ((v1 "hello")
    216       (v2 314159)
    217       (v3 :v3)
    218       (v4 :v4))
    219   (query ;; macroexpand
    220    (q:union
    221      (q:select (:*)
    222        (q:from x)
    223        (q:where
    224          (q:and
    225            (q:= (q:qvar v1) (q:qvar v2))
    226            (q:= (q:qvar v2) (q:qchunk (+ 1 2)))
    227            (q:= (q:qvar v3) (q:qchunk "one"))
    228            (q:= (q:qvar v4) (q:qchunk '(q:desc x))))))
    229      (q:select (:*)
    230        (q:from x)
    231        (q:where
    232          (q:and
    233            (q:= (q:qvar v1) (q:qvar v2))
    234            (q:= (q:qvar v2) (q:qchunk (+ 1 2)))
    235            (q:= (q:qvar v3) (q:qchunk "one"))
    236            (q:= (q:qvar v4) (q:qchunk '(q:desc x)))))))))
    237 
    238 
    239 
    240 
    241 
    242 
    243 
    244 ;; *compiled-query-lambda-cache* test
    245 (2sql-backend:with-postgresql-connection ("pokus" "tomas" "test123" "localhost")
    246   (let ((a 2))
    247     ;;(EXECUTE "SELECT |/ $1::INTEGER" (LIST A))
    248     ;;(funcall (compile nil (lambda (a) (query (q:select ((q:sqrt (q:qvar a :integer))))))) a)
    249     ;;(query (q:select ((q:sqrt (q:qvar a :integer)))))
    250     (let ((q (qlambda (a)
    251                (q:select ((q:sqrt (q:qvar a (q:integer-type))))))))
    252       (funcall q a)
    253       (funcall q a)))) ;; reusing from *compiled-query-lambda-cache*
    254 
    255 ;; explicit lexvars
    256 (2sql-backend:with-postgresql-connection ("pokus" "tomas" "test123" "localhost")
    257   (let ((a 2))
    258     ;;(EXECUTE "SELECT |/ $1::INTEGER" (LIST A))
    259     ;;(funcall (compile nil (lambda (a) (query (q:select ((q:sqrt (q:qvar a :integer))))))) a)
    260     (query (a) '(q:select ((q:sqrt (q:qvar a :integer)))))
    261     (query (a) '(q:select ((q:sqrt (q:qvar a :integer)))))))
    262 
    263 (qdefun foo1 (a)
    264   (q:select ((q:sqrt (q:qvar a :integer)))))
    265 
    266 (2sql-backend:with-postgresql-connection ("pokus" "tomas" "test123" "localhost")
    267   (foo1 1)
    268   (foo1 2))
    269 
    270 
    271 ;; suppress-qvar
    272 
    273 (2sql-backend:with-postgresql-connection ("pokus" "tomas" "test123" "localhost")
    274   (let ((a 1)
    275         (b 2))
    276     (2sql:query (a b)
    277       '(q:select ((q:+ (q:qvar a :integer)
    278                        (q:suppress-qvar (q:qvar b :integer)))))))
    279   #+nil(query (q:select (3)))
    280   #+nil(query (q:select (4))))
    281 
    282 (2sql-backend:with-postgresql-connection ("pokus" "tomas" "test123" "localhost")
    283   (let ((q (qlambda (a b)
    284              (q:select ((q:+ (q:qvar a :integer)
    285                              (q:suppress-qvar (q:qvar b :integer))))))))
    286     (list :first (funcall q 1 2) :second (funcall q 2 3))))
    287 
    288 #+nil
    289 (2sql-backend:with-postgresql-connection ("pokus" "tomas" "test123" "localhost")
    290   (2sql:qlet ((q (a b) ;; TODO indenting like flet?
    291                  (q:select ((q:+ (q:qvar a :integer)
    292                                  (q:suppress-qvar (q:qvar b :integer))
    293                                  (q:qchunk (* a b)))))))
    294              (list :first (q 1 2) :second (q 2 3))))
    295 
    296 
    297 
    298 
    299 #+nil
    300 (with-postgresql-connection ("pokus" "tomas" "test123" "localhost")
    301   (flet ((mul (x y) ;; TODO undefined function!  capture flet?
    302            (* x y)))
    303     (qlet ((q (a b) ;; TODO indenting like flet?
    304               (q:select ((q:+ (q:qvar a :integer)
    305                               (q:suppress-qvar (q:qvar b :integer))
    306                               (q:qchunk (mul a b)))))))
    307       #+nil(list :first (q 1 2) :second (q 2 3)))))
    308 
    309 cl-postgres:to-sql-string  function value->type? would be handy
    310 
    311 smallint	integer
    312 integer	integer
    313 bigint	integer
    314 numeric	ratio
    315 real	float
    316 double precision	double-float
    317 boolean	boolean
    318 varchar	string
    319 text	string
    320 bytea	(vector (unsigned-byte 8))
    321 
    322 
    323 ;; custom query macro example
    324 
    325 (defmacro integer-qvar (name)
    326   `(q:qvar ,name :integer))
    327 
    328 (2sql-backend:with-postgresql-connection ("pokus" "tomas" "test123" "localhost")
    329   (let ((a 2))
    330     (2sql:query (a)
    331       '(q:select ((q:sqrt (integer-qvar a)) (q:+ 1 (q:qchunk (+ 2 3))))))))
    332 
    333 
    334 
    335 
    336 
    337 (2sql-backend:with-postgresql-connection ("pokus" "tomas" "test123" "localhost")
    338   (2sql:query () '(q:select (q:clist 22 "Folie et déraison" #+nil 4.5 ))))
    339 
    340 (2sql-backend:with-postgresql-connection ("pokus" "tomas" "test123" "localhost")
    341   (let ((a 1)
    342         (b 2)
    343         (c "X")
    344         (d "hello"))
    345     (2sql:query (a b c d)
    346       '(q:select ((q:+ (q:qvar a :numeric)
    347                        (q:qvar b :integer))
    348                   (q:\|\| (q:qvar c (q:char-type))
    349                    (q:qvar d (q:varchar-type 10))))))))
    350 
    351 
    352 
    353 
    354 (doquery (:select 'x 'y :from 'some-imaginary-table) (x y)
    355   (format t "On this row, x = ~A and y = ~A.~%" x y))
    356 
    357 
    358 
    359 
    360 (with-postgresql-connection ("pokus" "tomas" "test123" "localhost")
    361   (query (q:create-table t1 (q:columns
    362                               (q:column c1 (q:integer-type))
    363                               (q:column c2 (q:integer-type) 314))
    364              t))
    365   (qlet ((insert (a b) (q:insert-into t1 (c1 c2)
    366                          (q:values (q:qvar a) (q:qvar b)))))
    367     (insert 1 2)
    368     (insert 3 4))
    369   (qmap (lambda (a b) (print (list :@@@ a b)))
    370         (query (q:select (c1 c2) (q:from t1))))
    371   (multiple-value-prog1 (query (q:select (c1 c2) (q:from t1)))
    372     (query (q:drop-table t1))))
    373 
    374 
    375 
    376 
    377 
    378 
    379 (2sql-backend:with-postgresql-connection ("pokus" "tomas" "test123" "localhost")
    380   (query ()
    381     '(q:create-table t1 (q:columns
    382                           (q:column c1 (q:integer-type))
    383                           (q:column c2 (q:integer-type) 314)))))
    384 
    385 (2sql-backend:with-postgresql-connection ("pokus" "tomas" "test123" "localhost")
    386   (query ()
    387     '(q:drop-table t1)))
    388 
    389 
    390 
    391 
    392 
    393 
    394 
    395 
    396 
    397 (2sql-backend:with-postgresql-connection ("pokus" "tomas" "test123" "localhost")
    398   (2sql:query ()
    399     '(q:select :relname
    400       (q:from (q:inner-join :pg_catalog.pg_class
    401                             :pg_catalog.pg_namespace
    402                             (q:= :relnamespace :pg_namespace.oid)))
    403       (q:where (q:and (q:= :relkind "r")
    404                       (q:not (q:in :nspname (q:set "pg_catalog" "pg_toast")))
    405                       (q:function :pg_catalog.pg_table_is_visible :pg_class.oid))))))
    406 
    407 (2sql-backend:with-postgresql-connection ("pokus" "tomas" "test123" "localhost")
    408   (2sql:query ()
    409     '(q:select :relname
    410       (q:from (q:inner-join (q:as :pg_catalog.pg_class :c)
    411                             (q:as :pg_catalog.pg_namespace :n)
    412                             (q:= :relnamespace :n.oid)))
    413       (q:where (q:and (q:= :relkind "r")
    414                       (q:not (q:in :nspname (q:set "pg_catalog" "pg_toast")))
    415                       (q:function :pg_catalog.pg_table_is_visible :c.oid))))))
    416 
    417 #+nil
    418 (sql
    419  (:select 'relname
    420    :from 'pg-catalog.pg-class
    421    :inner-join 'pg-catalog.pg-namespace
    422    :on (:= 'relnamespace 'pg-namespace.oid)
    423    :where (:and (:= 'relkind "r")
    424                 (:not-in 'nspname (:set "pg_catalog" "pg_toast"))
    425                 (:pg-catalog.pg-table-is-visible 'pg-class.oid))))
    426 ;; => "(SELECT relname FROM pg_catalog.pg_class 
    427 ;;      INNER JOIN pg_catalog.pg_namespace ON (relnamespace = pg_namespace.oid)
    428 ;;      WHERE ((relkind = 'r') and (nspname NOT IN ('pg_catalog', 'pg_toast'))
    429 ;;             and pg_catalog.pg_table_is_visible(pg_class.oid)))"
    430 
    431 (2sql-backend:with-sqlite-connection (":memory:")
    432   (query ()
    433     '(q:create-table t1 (q:columns
    434                           (q:column c1 (q:integer-type))
    435                           (q:column c2 (q:integer-type) 314)
    436                           (q:column c3 (q:boolean-type))
    437                           (q:column c4 (q:varchar-type)))
    438       nil nil :fts3))
    439   (let ((tt (2sql:qmacroexpand (q:true-value)))
    440         (ff (2sql:qmacroexpand (q:false-value))))
    441     (loop
    442        for (a b c d) in `((11 12 ,tt "Ivan Ivanovic Ivanov")
    443                           (21 22 ,ff "Ivan Ovic"))
    444        do (query (a b c d)
    445             '(q:insert-into t1 (c1 c2 c3 c4)
    446               (q:values (q:qvar a) (q:qvar b) (q:qvar c) (q:qvar d))))))
    447   ;; my sqlite version supports word and prefix search only
    448   (query ()
    449     '(q:select :*
    450       (q:from t1)
    451       ;;(q:where (q:infix " MATCH " c4 "ivan"))
    452       (q:where (q:infix " MATCH " c4 "ov*"))
    453       ;;(q:where (q:infix " MATCH " c4 "*ov")) ;; doesnt work
    454       ;;(q:where (q:infix " MATCH " c4 "ivan AND ivanov"))
    455       )))
    456 
    457 
    458 
    459 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; orm
    460 
    461 (2sql-orm:defpclass t1 ()
    462   ((c1 :type integer :initarg :c1 :accessor c1)
    463    (c2 :type (or null integer) :initarg :c2 :initform nil :accessor c2)
    464    (c3 :type integer :initarg :c3 :initform 321 :accessor c3)))
    465 
    466 (2sql-orm:defpclass t2 ()
    467   ((d1 :type integer :initarg :d1 :accessor d1)
    468    (d2 :type (or null integer) :initarg :d2 :initform nil :accessor d2)
    469    (d3 :type integer :initarg :d3 :initform 271 :accessor d3)))
    470 
    471 (trace 2sql:execute)
    472 
    473 (2sql-backend:with-postgresql-connection ("pokus" "tomas" "test123" "localhost")
    474   (2sql-orm:with-pinstance-collector-cache ()
    475     (2sql-orm:with-instance-cache ()
    476       (2sql:query () '(q:drop-sequence oid-seq t))
    477       (2sql:query () '(q:create-sequence oid-seq))
    478       (2sql-orm::with-pclasses (t1 t2)
    479         (2sql-orm:make-pinstance 't1 :c1 1 :c2 2 :c3 3)
    480         (2sql-orm:make-pinstance 't1 :c1 1 :c2 2)
    481         (2sql:query () '(q:select :* (q:from t1)))
    482         #+nil
    483         (x-query ()
    484                  '(q:select ((x-instance t1 x) (q:sum x.c1))
    485                    (q:from (q:as t1 x))))
    486         ;;(x-query () '(q:select ((x-instance t1)) (q:from t1)))
    487         #+nil
    488         (x-query ()
    489                  '(q:select ((x-instance t1 a) (x-instance t2 b) a.c2)
    490                    (q:from (q:left-join (q:as t1 a) (q:as t2 b) (q:= a.c1 b.d1)))))
    491         ;; this works 2nd time only with *instance-collector-cache*
    492         ;;(x-query () '(q:select ((x-instance t1)) (q:from t1)))
    493         ;;(x-query () '(q:select ((x-instance t1)) (q:from t1)))
    494         ))))
    495 
    496 ;;; http://www.pui.ch/phred/archives/2005/04/tags-database-schemas.html
    497 ;;; toxi solution
    498 
    499 (2sql-orm:defptype tag-name () '(2sql-orm:text 32))
    500 (2sql-orm:defptype bookmark-name () '(2sql-orm:text 128))
    501 
    502 (2sql-orm:defpclass tag ()
    503   ((name :type tag-name :initarg :name)))
    504 
    505 (2sql-orm:defpclass bookmark ()
    506   ((name :type bookmark-name :initarg :name)))
    507 
    508 (2sql-orm:defpclass tagmap ()
    509   ((bookmark :type bookmark :initarg :bookmark)
    510    (tag :type tag :initarg :tag)))
    511 
    512 (defun make-tag (name)
    513   (2sql-orm:make-pinstance 'tag :name name))
    514 
    515 (defun make-bookmark (name)
    516   (2sql-orm:make-pinstance 'bookmark :name name))
    517 
    518 (defun make-tagmap (bookmark tag)
    519   (2sql-orm:make-pinstance 'tagmap :bookmark bookmark :tag tag))
    520 
    521 (defun build-sql-query (q)
    522   `(q:par
    523      ,(if (atom q)
    524           ;; TODO not :b_.* but (2sql-orm:instance b) and coolect only if toplevel select
    525           `(q:select :b_.* ;; TODO b.* w/o interning (q:dotted-name b :*)
    526              (q:from (q:as bookmark b) (q:as tag t) (q:as tagmap m))
    527              (q:where (q:and (q:= b.oid m.bookmark)
    528                              (q:= t.oid m.tag)
    529                              (q:= t.name ,q))))
    530           (ecase (car q)
    531             (and `(q:intersect nil nil nil ,@(mapcar 'build-sql-query (cdr q))))
    532             (or `(q:union nil nil nil ,@(mapcar 'build-sql-query (cdr q))))
    533             (not `(q:except nil nil nil ,@(mapcar 'build-sql-query (cdr q))))))))
    534 
    535 (2sql-backend:with-postgresql-connection ("pokus" "tomas" "test123" "localhost")
    536   (2sql-orm:with-pinstance-collector-cache ()
    537     (2sql-orm:with-instance-cache ()
    538       (2sql-orm::with-psequences (oid-seq)
    539         (2sql-orm::with-pclasses (tag bookmark tagmap)
    540           (let ((t1 (make-tag "bookmark"))
    541                 (t2 (make-tag "webservice"))
    542                 (t3 (make-tag "semweb"))
    543                 (t4 (make-tag "lisp"))
    544                 (t5 (make-tag "sql"))
    545                 (b1 (make-bookmark "b1"))
    546                 (b2 (make-bookmark "b2")))
    547             (make-tagmap b1 t1)
    548             (make-tagmap b1 t2)
    549             (make-tagmap b1 t3)
    550             (make-tagmap b1 t4)
    551             (make-tagmap b2 t1)
    552             (make-tagmap b2 t2)
    553             (flet ((look-up (q)
    554                      (2sql-orm:query () ;; TODO cache compiled queries
    555                        `(q:select ((2sql-orm:instance bookmark x))
    556                           (q:from (q:as ,(build-sql-query q) x))))))
    557               ;; Query for “bookmark+webservice+semweb”
    558               (look-up '(and "bookmark" "webservice" "semweb"))
    559               ;; Query for “bookmark|webservice|semweb”
    560               (look-up '(or "bookmark" "webservice" "semweb"))
    561               ;; Query for “bookmark+webservice-semweb”
    562               (look-up '(not (and "bookmark" "webservice") "semweb")))))))))
    563 
    564 ;; http://pinterface.livejournal.com/34706.html
    565 ;; http://pinterface.livejournal.com/35042.html
    566 ;; http://pinterface.livejournal.com/35586.html
    567 ;; http://pinterface.livejournal.com/35935.html
    568 
    569 ;; http://roeim.net/vetle/docs/cl-webapp-intro/ BLOG
    570 
    571 (2sql-orm:defptype title () '(2sql-orm:text 64))
    572 (2sql-orm:defptype body () '(2sql-orm:text 128))
    573 (2sql-orm:defptype timestamp () '2sql-orm::ptimestamp-tz)
    574 
    575 (2sql-orm:defpclass blog-post ()
    576   ((title :type title :initarg :title)
    577    (body :type body :initarg :body)
    578    (created :type timestamp :initarg :created)))
    579 
    580 (defun now ()
    581   (2sql-orm::make-ptimestamp-tz (2sql-orm::make-pdate 2011 8 13)
    582                                 (2sql-orm::make-ptime 17 4 0 0)
    583                                 nil))
    584 
    585 (defun make-blog-post (title body)
    586   (2sql-orm:make-pinstance 'blog-post :title title :body body :created (now)))
    587 
    588 (2sql-backend:with-postgresql-connection ("pokus" "tomas" "test123" "localhost")
    589   (2sql-orm:with-pinstance-collector-cache ()
    590     (2sql-orm:with-instance-cache ()
    591       (2sql-orm::with-psequences (oid-seq)
    592         (2sql-orm::with-pclasses (blog-post)
    593           (let ((p1 (make-blog-post "Hello blog world" "First post!"))
    594                 (p2 (make-blog-post "This is fun" "Common Lisp is easy!")))
    595             (2sql-orm:query ()
    596               `(q:select ((2sql-orm:instance blog-post x))
    597                  (q:from (q:as blog-post x))
    598                  (q:where (q:like x.title "%wor%"))))))))))
    599 
    600 #+nil
    601 (defun save-blog-post ()
    602   "Read POST data and modify blog post."
    603   (let ((blog-post
    604          (get-instance-by-value 'blog-post
    605                                 'url-part (hunchentoot:query-string))))
    606     (setf (title blog-post) (hunchentoot:post-parameter "title"))
    607     (setf (body blog-post) (hunchentoot:post-parameter "body"))
    608     (setf (url-part blog-post) (make-url-part (title blog-post)))
    609     (hunchentoot:redirect (url-part blog-post))))