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

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

Get rid of the current-type-unparsers parameter, pass the connection to each parser instead

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 conn #t))
449  (test "Boolean false unparsed correctly"
450        "FALSE"
451        (bool-unparser conn #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.