source: project/release/4/postgresql/trunk/tests/run.scm @ 17783

Last change on this file since 17783 was 17783, checked in by sjamaan, 10 years ago

Do not try to be too "smart" about vector values. The Scheme values don't matter, as long as the output (pgsql values) are of the same type

File size: 23.5 KB
Line 
1(use test postgresql sql-null srfi-4)
2
3;; These tests assume that the current UNIX user has access to a database
4;; named 'test'.  The tests will fail otherwise.
5
6(test-group "connection management"
7  (test-assert "connect returns a connection"
8               (let* ((conn (connect '((dbname . test))))
9                      (isconn (connection? conn)))
10                 (disconnect conn)
11                 isconn))
12  (test-error "cannot connect with invalid credentials"
13              (connect '((dbname . does-not-exist)
14                         (username . nobody))))
15  (test-assert "reset-connection returns a connection"
16               (let* ((conn (connect '((dbname . test))))
17                      (isconn (connection? conn)))
18                 (reset-connection conn)
19                 (disconnect conn)
20                 isconn))
21  (test-error "disconnect invalidates the connection"
22              (let ((conn (connect '((dbname . test)))))
23                (disconnect conn)
24                (reset-connection conn)))
25  ;; It would be nice if we could test some more error cases here but
26  ;; that's hard to do
27  )
28
29;; From now on, just keep using the same connection
30(define conn (connect '((dbname . test))))
31
32(test-group "low-level interface"
33  (test-assert "query returns result"
34               (result? (query conn "SELECT 1")))
35  (test "Correct row count"
36        2
37        (row-count (query conn "SELECT 1 UNION SELECT 2")))
38  (test "Correct column count"
39        4
40        (column-count (query conn "SELECT 1, 2, 3, 4")))
41  (test "Correct column name"
42        'one
43        (column-name
44         (query conn "SELECT 1 AS one, 2 AS two") 0))
45  (test "Correct column names"
46        '(one two)
47        (column-names
48         (query conn "SELECT 1 AS one, 2 AS two")))
49  (test-error "Condition for nonexistant column index"
50              (column-name
51               (query conn "SELECT 1 AS one, 2 AS two") 3))
52  (test "Not false for nameless column"
53        #f ;; Could check for ?column?, but that's a bit too specific
54        (not (column-name
55              (query conn "SELECT 1, 2") 0)))
56  ;; Maybe add a few tests here for case folding/noncase folding variants?
57  ;; Perhaps column-index-ci vs column-index?  That would be
58  ;; misleading though, since column-index-ci isn't really ci,
59  ;; it will not match columns that are explicitly uppercased in the query.
60  (test "Correct column index"
61        0
62        (column-index
63         (query conn "SELECT 1 AS one, 2 AS two") 'one))
64  (test "False column index for nonexistant column name"
65        #f
66        (column-index
67         (query conn "SELECT 1 AS one, 2 AS two") 'foo))
68  (test "False oid for virtual table"
69        #f
70        (table-oid
71         (query conn "SELECT 1 AS one, 2 AS two") 0))
72  (test-assert "Number for nonvirtual table"
73               (number?
74                (table-oid
75                 (query conn "SELECT typlen FROM pg_type") 0)))
76 
77  (test-error "Condition for column index out of bounds"
78              (table-oid
79               (query conn "SELECT typname FROM pg_type") 1))
80  (test "Table column number for real table"
81        0
82        (table-column-index
83         (query conn "SELECT typname FROM pg_type") 0))
84  (test "Column format is text for normal data"
85        'text
86        (column-format
87         (query conn "SELECT 'hello'") 0))
88 
89  (test "Column format is binary for forced binary data"
90        'binary
91        (column-format
92         (query* conn "SELECT 1" '() format: 'binary) 0))
93 
94  (test "Column type OID ok"
95        23 ;; from catalog/pg_type.h
96        (column-type
97         (query conn "SELECT 1::int4") 0))
98  (test "Column modifier false"
99        #f
100        (column-type-modifier
101         (query conn "SELECT 1") 0))
102  (test "Column modifier for bit ok"
103        2
104        (column-type-modifier
105         (query conn "SELECT '10'::bit(2)") 0))
106  (test "Result value string for strings"
107        "test"
108        (value-at (query conn "SELECT 'test'")))
109  (test "Result row values"
110        '("one" "two")
111        (row-values
112         (query conn "SELECT 'one', 'two' UNION SELECT 'three', 'four'") 0))
113  (test "Result row values for second row"
114        '("three" "four")
115        (row-values
116         (query conn "SELECT 'one', 'two' UNION SELECT 'three', 'four'") 1))
117  (test "Result row alist"
118        '((a . "one") (b . "two"))
119        (row-alist
120         (query conn "SELECT 'one' AS a, 'two' AS b UNION SELECT 'three', 'four'") 0))
121  (test "Result row alist for second row"
122        '((a . "three") (b . "four"))
123        (row-alist
124         (query conn "SELECT 'one' AS a, 'two' AS b UNION SELECT 'three', 'four'") 1))
125  (test "Result column values"
126        '("one" "three")
127        (column-values
128         (query conn "SELECT 'one', 'two' UNION SELECT 'three', 'four'") 0))
129  (test "Result column values for second column"
130        '("two" "four")
131        (column-values
132         (query conn "SELECT 'one', 'two' UNION SELECT 'three', 'four'") 1))
133  (test "Result value number for numbers"
134        1
135        (value-at (query conn "SELECT 1")))
136  (test "Result value string for raw numbers"
137        "1"
138        (value-at (query conn "SELECT 1") 0 0 raw: #t))
139  ;; We are using two levels of escaping here because the ::bytea cast
140  ;; performs another string interpretation. Yes, this is kinda confusing...
141  (test "Result value for null-terminated byte array"
142        (blob->u8vector (string->blob "h\x00ello"))
143        (value-at (query conn "SELECT E'h\\\\000ello'::bytea")))
144  (test "Result value for raw null-terminated byte array"
145        "h\\000ello"
146        (value-at (query conn "SELECT E'h\\\\000ello'::bytea") 0 0 raw: #t))
147
148  (test "Result value blob for binary string"
149        (string->blob "hello")
150        (value-at (query* conn "SELECT 'hello'" '() format: 'binary)))
151 
152  (test "Result value blob for binary integer"
153        (u8vector->blob (u8vector 0 0 0 1))
154        (value-at (query* conn "SELECT 1::int4" '() format: 'binary)))
155
156  (test "Result value for binary string with NUL bytes"
157        (string->blob "h\x00ello")
158        (value-at (query* conn "SELECT E'h\\\\000ello'::bytea" '() format: 'binary)))
159
160  (test "Result value for array of integers"
161        `#(1 ,(sql-null) 3) ;; Not sure if comparing sql-null is kosher
162        (value-at (query conn "SELECT array[1, null, 3]")))
163
164  (test "Result value for nested array of ints"
165        `#(#(1 ,(sql-null) 3)
166           #(4 5 ,(sql-null))) ;; Not sure if comparing sql-null is kosher
167        (value-at
168         (query conn "SELECT array[array[1, null, 3], array[4, 5, NULL]]")))
169 
170  (test "Result value for array of strings"
171        `#("a" ,(sql-null) "c") ;; Not sure if comparing sql-null is kosher
172        (value-at (query conn "SELECT array['a', null, 'c']")))
173
174  (test "Result value for nested array of strings"
175        `#(#("a" ,(sql-null) "c")
176           #("NULL" "e" ,(sql-null))) ;; Not sure if comparing sql-null is kosher
177        (value-at (query conn "SELECT array[array['a', null, 'c'], array['NULL', 'e', NULL]]")))
178 
179  (test "Result value at row 0, column 1"
180        2
181        (value-at (query conn "SELECT 1, 2 UNION SELECT 3, 4") 1 0))
182  (test "Result value at row 1, column 0"
183        3
184        (value-at (query conn "SELECT 1, 2 UNION SELECT 3, 4") 0 1))
185  (test-assert "Result value sql-null for NULL"
186               (sql-null? (value-at (query conn "SELECT NULL"))))
187  (test-error "Result value error for out of bounds row"
188              (value-at (query conn "SELECT NULL") 0 1))
189  (test-error "Result value error for out of bounds column"
190              (value-at (query conn "SELECT NULL") 1 0))
191  (test "Number of affected rows false with SELECT"
192        #f
193        (affected-rows
194         (query conn "SELECT 1")))
195
196  (query conn "BEGIN")
197  (query conn "CREATE TEMP TABLE foo ( bar integer ) ON COMMIT DROP")
198  (test "Number of affected rows 1 with INSERT"
199        1
200        (affected-rows
201         (query conn "INSERT INTO foo (bar) VALUES (1);")))
202  (query conn "COMMIT")
203
204  (query conn "BEGIN")
205  (query conn "CREATE TEMP TABLE foo ( bar integer ) ON COMMIT DROP")
206  (query conn "INSERT INTO foo (bar) VALUES (100);")
207  (query conn "INSERT INTO foo (bar) VALUES (101);")
208  (test "Number of affected rows 2 with UPDATE of two rows"
209        2
210        (affected-rows
211         (query conn "UPDATE foo SET bar=102;")))
212  (query conn "COMMIT")
213 
214  (test "Inserted OID false on SELECT"
215        #f
216        (inserted-oid
217         (query conn "SELECT 1")))
218
219  (query conn "BEGIN")
220  (query conn "CREATE TEMP TABLE foo ( bar integer ) ON COMMIT DROP")
221  (test "Inserted OID false on OID-less table"
222        #f
223        (inserted-oid
224         (query conn  "INSERT INTO foo (bar) VALUES (1);")))
225  (query conn "COMMIT")
226 
227  (query conn "BEGIN")
228  (query conn "CREATE TEMP TABLE foo ( bar integer ) WITH (OIDS=true) ON COMMIT DROP")
229  (test-assert "Inserted OID number on table with OID"
230               (number?
231                (inserted-oid
232                 (query conn "INSERT INTO foo (bar) VALUES (1)"))))
233  (query conn "COMMIT")
234
235  (test "regular parameters"
236        "hi"
237        (value-at (query conn "SELECT $1::text" "hi") 0 0))
238  (test-assert "NULL parameters"
239               (sql-null? (value-at
240                           (query conn "SELECT $1::text" (sql-null)) 0 0)))
241  (test "blob parameters"
242        "hi"
243        (value-at (query conn "SELECT $1::text" (string->blob "hi")) 0 0))
244  (test "boolean parameters"
245        '(#t #f)
246        (row-values (query conn "SELECT $1::bool, $2::bool" #t #f)))
247  (test "integer array parameters"
248        `(#(1 2) #(,(sql-null) 4))
249        (row-values (query conn "SELECT $1::int[], $2::int[]"
250                           `#(1 2) `#(,(sql-null) 4))))
251  (test "nested integer array parameters"
252        `(#(#(1 2) #(,(sql-null) 4)))
253        (row-values (query conn "SELECT $1::int[][]"
254                           `#(#(1 2) #(,(sql-null) 4)))))
255  (test "string array parameters (including 'null')"
256        `(#("a" "b") #(,(sql-null) "null"))
257        (row-values (query conn "SELECT $1::text[], $2::text[]"
258                           `#("a" "b") `#(,(sql-null) "null"))))
259  (test "string array parameters with meta-characters"
260        `(#("a\\b" "c\"d") #("{" "}"))
261        (row-values (query conn "SELECT $1::text[], $2::text[]"
262                           '#("a\\b" "c\"d") '#("{" "}"))))
263  (test "nested string array parameters"
264        `(#(#("a" "b") #(,(sql-null) "null")))
265        (row-values (query conn "SELECT $1::text[][]"
266                           `#(#("a" "b") #(,(sql-null) "null")))))
267  (test "array bounds are ignored"
268        `#(#(#(1 2 3) #(4 5 6)))
269        (value-at
270         (query conn
271                "SELECT '[1:1][-2:-1][3:5]={{{1,2,3},{4,5,6}}}'::int[] AS f1")))
272
273  ;; Basic domains seem to return just their base type
274  (query conn "BEGIN")
275  (query conn "CREATE DOMAIN mydom AS integer CHECK (VALUE > 2)")
276  (test "basic domains"
277        3
278        (value-at (query conn "SELECT $1::mydom" 3)))
279  (query conn "ROLLBACK")
280 
281  (query conn "BEGIN")
282  (query conn "CREATE TYPE foo AS ( a integer, b text )")
283  (test "basic composite type"
284        `(1 "one")
285        (value-at (query conn "SELECT $1::foo" `(1 "one"))))
286  (test "composite type with meta-characters"
287        `(123 "\\backslash\"quote")
288        (value-at (query conn "SELECT $1::foo" `(123 "\\backslash\"quote"))))
289  (query conn "CREATE TYPE bar AS ( x foo, y integer )")
290  (test "Nested composite type"
291        `((2 "two") 3)
292        (value-at (query conn "SELECT $1::bar" `((2 "two") 3))))
293  (query conn "CREATE DOMAIN mydom AS integer CHECK (VALUE > 1)")
294  (query conn "CREATE TYPE qux AS ( i integer, d mydom )")
295  (test "Composite type containing domain value"
296        `(1 2)
297        (value-at (query conn "SELECT $1::qux" `(1 2))))
298  (query conn "ROLLBACK")
299
300  (test "anonymous composite type ('record')"
301        '("one" "two")
302        (value-at (query conn "SELECT ROW('one', 'two')"))))
303
304(test-group "value escaping"
305  (test "String is escaped correctly"
306        "What''s up?"
307        (escape-string conn "What's up?"))
308  (test "Bytea is escaped correctly"
309        "Wh\\\\000at''s\\\\012up?"
310        (escape-bytea conn "Wh\x00at's\nup?"))
311  (test "Bytea is unescaped correctly"
312        "What's\nup?"
313        ;; The extra quote is dropped here because it wouldn't be returned
314        ;; by pgsql either.
315        (unescape-bytea "What's\\012up?")))
316
317(test-group "COPY support"
318  (query conn "CREATE TEMP TABLE copy_table ( nr integer, s text )")
319  (test-group "low-level interface"
320    (test-error "Cannot put copy data while no COPY in progress"
321                (put-copy-data conn "whatever"))
322    (query conn "COPY copy_table (s, nr) FROM STDIN")
323    (test-error "Cannot initiate new query while waiting for COPY input"
324                (query conn "SELECT 1"))
325    (put-copy-data conn "one\t1\n")
326    (test-error "Cannot initiate new query while COPY data in progress"
327                (query conn "SELECT 1"))
328    (put-copy-data conn "two\t2")
329    (put-copy-end conn)
330    (let ((res (query conn "SELECT * FROM copy_table")))
331      (test "Simple copy from STDIN works"
332            '((1 "one")
333              (2 "two"))         
334            (list (row-values res 0) (row-values res 1))))
335    (test-error "Cannot get copy data while no COPY in progress"
336                (get-copy-data conn))
337    (query conn "COPY copy_table (s, nr) TO STDOUT")
338    (test-error "Cannot initiate new query while waiting for COPY output"
339                (query conn "SELECT 1"))
340    (test "Simple copy to STDOUT works, first record"
341          "one\t1\n"
342          (get-copy-data conn))
343    (test-error "Cannot initiate new query while reading COPY data"
344                (query conn "SELECT 1"))
345    (test "Simple copy to STDOUT works, second record"
346          "two\t2\n"
347          (get-copy-data conn))
348    (test-assert "EOF is marked by a result object"
349                 (result? (get-copy-data conn))))
350  (test-group "high-level interface"
351    (test "Mapping"
352          '(("one" "1")
353            ("two" "2"))
354          (copy-query-map string-split conn "COPY copy_table (s, nr) TO STDOUT"))
355    (test "Error while mapping gets connection out of COPY state"
356          "okay"
357          (handle-exceptions exn
358            (value-at (query conn "SELECT 'okay'"))
359            (copy-query-map (lambda _ (error "blah"))
360                            conn "COPY copy_table (s, nr) TO STDOUT")))
361    (test "Fold"
362          '(("one" "1")
363            ("two" "2"))
364          (reverse
365           (copy-query-fold
366            (lambda (data result)
367              (cons (string-split data) result))
368            '() conn "COPY copy_table (s, nr) TO STDOUT")))
369    (test "Error while folding gets connection out of COPY state"
370          "okay"
371          (handle-exceptions exn
372            (value-at (query conn "SELECT 'okay'"))
373            (copy-query-fold (lambda _ (error "blah"))
374                             '() conn "COPY copy_table (s, nr) TO STDOUT")))
375    (test "Fold-right"
376          '(("one" "1")
377            ("two" "2"))
378          (copy-query-fold-right
379           (lambda (data result)
380             (cons (string-split data) result))
381           '() conn "COPY copy_table (s, nr) TO STDOUT"))
382    (test "Error while folding right gets connection out of COPY state"
383          "okay"
384          (handle-exceptions exn
385            (value-at (query conn "SELECT 'okay'"))
386            (copy-query-fold-right (lambda _ (error "blah"))
387                                   '() conn "COPY copy_table (s, nr) TO STDOUT")))
388    (test "For-each"
389          '(("one" "1")
390            ("two" "2"))
391          (let ((res '()))
392            (copy-query-for-each (lambda (x)
393                                   (set! res (cons (string-split x) res)))
394                                 conn "COPY copy_table (s, nr) TO STDOUT")
395            (reverse res)))
396    (test "Error during for-each gets connection out of COPY state"
397          "okay"
398          (handle-exceptions exn
399            (value-at (query conn "SELECT 'okay'"))
400            (copy-query-for-each (lambda (x) (error "blah"))
401                                 conn "COPY copy_table (s, nr) TO STDOUT")))
402    (query conn "TRUNCATE copy_table")
403    (with-output-to-copy-query (lambda ()
404                                 (print "first\t1")
405                                 (print "second\t2"))
406                               conn "COPY copy_table (s, nr) FROM STDIN")
407    (test "Port interface inserted data correctly"
408          '(("first" 1)
409            ("second" 2))
410          (let ((res (query conn "SELECT s, nr FROM copy_table")))
411            (list (row-values res 0) (row-values res 1))))
412    (query conn "TRUNCATE copy_table")
413    (handle-exceptions _
414      (void)
415      (with-output-to-copy-query (lambda ()
416                                   (print "first\t1")
417                                   (print "second\t2")
418                                   (error "blah"))
419                                 conn "COPY copy_table (s, nr) FROM STDIN"))
420    (test "Error inside with-output-to-copy caused abort of insert"
421          0 (value-at (query conn "SELECT COUNT(*) FROM copy_table")))))
422
423(test-group "type parsers"
424  (test "Integer parsed correctly"
425        1234
426        (numeric-parser "1234"))
427  (test "Float parsed correctly"
428        123.456
429        (numeric-parser "123.456"))
430  (test-error "Non-integer is an error"
431              (numeric-parser "not an integer"))
432  (test "Boolean true parsed correctly"
433        #t
434        (bool-parser "t"))
435  (test "Boolean false parsed correctly"
436        #f
437        (bool-parser "f"))
438  (test "Byte array parsed correctly"
439        (blob->u8vector/shared (string->blob "abc\x01\x02\xffdef"))
440        (bytea-parser "abc\\001\\002\\377def"))
441  (test "Char parser"
442        #\x
443        (char-parser "x")))
444
445(test-group "type unparsers"
446  (test "Boolean true unparsed correctly"
447        "TRUE"
448        (bool-unparser #t))
449  (test "Boolean false unparsed correctly"
450        "FALSE"
451        (bool-unparser #f)))
452
453(test-group "high-level interface"
454  (test "row-fold"
455        '(("one" 2)
456          ("three" 4))
457        (reverse
458         (row-fold
459          cons '()
460          (query conn
461                 "SELECT $1::text, $2::integer UNION SELECT 'three', 4"
462                 "one" 2))))
463  (test "column-fold"
464        '(("one" "three")
465          (2 4))
466        (reverse
467         (column-fold
468          cons '()
469          (query conn
470                 "SELECT $1::text, $2::integer UNION SELECT 'three', 4"
471                 "one" 2))))
472  (test "row-fold-right"
473        '(("one" 2)
474          ("three" 4))
475        (row-fold-right
476         cons '()
477         (query conn
478                "SELECT $1::text, $2::integer UNION SELECT 'three', 4"
479                "one" 2)))
480  (test "column-fold-right"
481        '(("one" "three")
482          (2 4))
483        (column-fold-right
484         cons '()
485         (query conn
486                "SELECT $1::text, $2::integer UNION SELECT 'three', 4"
487                "one" 2)))
488  (test "row-for-each"
489        '(("one" 2)
490          ("three" 4))
491        (let ((res '()))
492          (row-for-each
493           (lambda (row) (set! res (cons row res)))
494           (query
495            conn
496            "SELECT $1::text, $2::integer UNION SELECT 'three', 4" "one" 2))
497          (reverse res)))
498  (test "column-for-each"
499        '(("one" "three")
500          (2 4))
501        (let ((res '()))
502          (column-for-each
503           (lambda (col) (set! res (cons col res)))
504           (query
505            conn
506            "SELECT $1::text, $2::integer UNION SELECT 'three', 4" "one" 2))
507          (reverse res)))
508  (test "row-map"
509        '(("one" 2)
510          ("three" 4))
511        (row-map
512         identity
513         (query conn
514                "SELECT $1::text, $2::integer UNION SELECT 'three', 4" "one" 2)))
515  (test "column-map"
516        '(("one" "three")
517          (2 4))
518        (column-map
519         identity
520         (query conn
521                "SELECT $1::text, $2::integer UNION SELECT 'three', 4" "one" 2))))
522
523(test-group "transactions"
524  (query conn "CREATE TEMP TABLE foo ( bar integer )")
525
526  (test-group "simple transactions"
527    (test "Transaction inactive"
528          #f
529          (in-transaction? conn))
530    (test "Transaction active"
531          #t
532          (with-transaction conn
533                            (lambda () (in-transaction? conn))))
534    (test "Successful transaction"
535          '(1)
536          (and
537           (with-transaction
538            conn (lambda ()
539                   (query conn "INSERT INTO foo (bar) VALUES (1)")))
540           (column-values (query conn "SELECT * FROM foo"))))
541 
542    (query conn "TRUNCATE foo")
543 
544    (test "Unsuccessful transaction"
545          #f
546          (with-transaction
547           conn (lambda ()
548                  (query conn "INSERT INTO foo (bar) VALUES (1)")
549                  #f)))
550
551    (test "Empty table after unsuccessful transaction"
552          '()
553          (column-values (query conn "SELECT * FROM foo")))
554
555    (handle-exceptions exn
556      (void)
557      (with-transaction
558       conn (lambda ()
559              (query conn "INSERT INTO foo (bar) VALUES (1)")
560              (error "oops!"))))
561 
562    (test "Exception during transaction causes reset"
563          '()
564          (column-values (query conn "SELECT * FROM foo"))))
565
566  (test-group "nested transactions"
567    (test "Successful transaction"
568          '(1 2)
569          (and
570           (with-transaction
571            conn (lambda ()
572                   (query conn "INSERT INTO foo (bar) VALUES (1)")
573                   (with-transaction
574                    conn (lambda ()
575                           (query conn "INSERT INTO foo (bar) VALUES (2)")))))
576           (column-values (query conn "SELECT * FROM foo"))))
577   
578    (query conn "TRUNCATE foo")
579
580    (test "Unsuccessful main transaction"
581          '()
582          (and
583           (not
584            (with-transaction
585             conn (lambda ()
586                    (query conn "INSERT INTO foo (bar) VALUES (1)")
587                    (with-transaction
588                     conn (lambda ()
589                            (query conn "INSERT INTO foo (bar) VALUES (2)")))
590                    #f)))
591           (column-values (query conn "SELECT * FROM foo"))))
592   
593    (test "Unsuccessful subtransaction"
594          '(1)
595          (and
596           (with-transaction
597            conn (lambda ()
598                   (query conn "INSERT INTO foo (bar) VALUES (1)")
599                   (with-transaction
600                    conn (lambda ()
601                           (query conn "INSERT INTO foo (bar) VALUES (2)")
602                           #f))
603                   #t))
604           (column-values (query conn "SELECT * FROM foo"))))
605
606    (query conn "TRUNCATE foo")
607
608    ;; Test that errors do not kill the transaction.  Apparently
609    ;; aborting transactions on errors is a psql(1) "feature", not a
610    ;; libpq one.
611    (test "Unsuccessful subtransaction with bad query"
612          '(1 2)
613          (and
614           (with-transaction
615            conn (lambda ()
616                   (query conn "INSERT INTO foo (bar) VALUES (1)")
617                   (handle-exceptions exn
618                     #t
619                     (with-transaction
620                      conn (lambda ()
621                             (query conn "INVALID QUERY"))))
622                   (query conn "INSERT INTO foo (bar) VALUES (2)")))
623           (column-values (query conn "SELECT * FROM foo"))))
624
625    (query conn "TRUNCATE foo")
626
627    (test "Multiple subtransactions"
628          '(1 3)
629          (and
630           (with-transaction
631            conn (lambda ()
632                   (query conn "INSERT INTO foo (bar) VALUES (1)")
633                   (with-transaction
634                    conn (lambda ()
635                           (query conn "INSERT INTO foo (bar) VALUES (2)")
636                           #f))
637                   (with-transaction
638                    conn (lambda ()
639                           (query conn "INSERT INTO foo (bar) VALUES (3)")))))
640           (column-values (query conn "SELECT * FROM foo")))))
641  )
Note: See TracBrowser for help on using the repository browser.