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))))