samples/db2-test.scm
;;;=head1 Test code for PostgreSQL
;;;
;;;This program gives more insight about how to use SQLI/SQLD It is a small test program
;;;that is distributed with the SQLI/SQLD package. Works for both bigloo and mzscheme.
;;;To prepare it for a scheme dialect, you need C<staste>.
;;;
;;;=head2 Code explained
;;;
;;;We begin with importing or requiring the right modules. You can see that here
;;;the right driver (PostgreSQL) is loaded.
;;;
;;;=verbatim scm,8
(module db2-test mzscheme
	(require (lib "time.ss" "srfi" "19"))
	(require "sqli.scm")
	(require "sqld-db2-internal.scm")
;;;=verbatim
;;;
;;;Some code to display a list of arguments keeps us from the need for a swarm of display
;;;invocations.
;;;
;;;=verbatim scm,8
(define (displayp . args)
  (define (p args)
    (if (null? args)
	(newline)
	(begin
	  (display (car args))
	  (p (cdr args)))))
  (p args))
;;;=verbatim
;;;
;;;Now for the main function. Let's first process some arguments
;;;
;;;=verbatim scm,8
(define (main argv)
  (let* (
;#+ mzscheme
	 (host (if (null? argv) "localhost" argv))
;#+ bigloo
	 (host (if (null? (cdr argv)) "localhost" (cadr argv)))
;##
;;;=verbatim
;;;
;;;Here is the code that differs the most for all SQLI/SQLD programs, because here we instantiate
;;;the driver. This is the SQLD part of SQLI/SQLD.
;;;
;;;=verbatim scm,8
	 (user (getenv "USER"))
	 (pass (getenv "PASSWD"))
	 (sqld (sqld-db2-new (let ((dsn (string-append "alias=test user=" user " passwd=" pass)))
			       (display (format "dsn=~a~%" dsn))
			       dsn)))
;;;=verbatim
;;;
;;;Next, we can connect to the database and wrap the connection into the generic SQLI interface.
;;;
;;;=verbatim scm,8
	 (sqli (sqli-connect sqld))
;;;=verbatim
;;;
;;;Right away we try to create a tabe in the database.
;;;
;;;=verbatim scm,8
	 (results (sqli-query sqli "CREATE TABLE test (name varchar(250), age numeric(4), nice smallint, dt timestamp)"))
	 (i 0))
;;;=verbatim
;;;
;;;What comes now is a lot of testing code.
;;;
;;;=verbatim scm,8
    ; Look at result of the table creation

    (displayp results)
    (displayp (sqli-error? sqli) " - " (sqli-error-message sqli))

    ; version and name of the driver

    (displayp (sqli-version) " - " (sqli-driver-name sqld) " - " (sqli-driver-version sqld))
    
    ; First test: Select * from test.

    (do 
	((a (sqli-fetchrow sqli) (sqli-fetchrow sqli)))
	((eq? a #f) #t)
      (display a))
    (displayp)

    ; Transaction of insert statements.
    
    (sqli-begin sqli)

    (do 
	((i 1 (+ i 1)))
	((> i 10) #t)
      (sqli-query sqli "INSERT INTO test VALUES ($1, $2, $3, $4)" (string-append "row'" (number->string i)) i #t (current-date)))

    (sqli-commit sqli)

    ; Select from database

    (set! results (sqli-query sqli "SELECT * FROM test"))
    (displayp (sqli-error? sqli) " - " (sqli-error-message sqli))
    (displayp (sqli-fetchall sqli))
    (displayp)

    ; Wrong SQL syntax.
    
    (sqli-begin sqli)
    (sqli-query sqli "INSERT INTO test VALUES ('joost',42);SELEKT * FROM test")
    (displayp (sqli-error? sqli) " - " (sqli-error-message sqli))
    (if (sqli-error? sqli)
	(sqli-rollback sqli)
	(sqli-commit sqli))
;;;=verbatim
;;;
;;;Registering queries is a nice feature of SQLI. One can register a query under a name and
;;;provide an implicit conversion function for select statements, or the expected types from
;;;a select statement, so that conversion to scheme types is done automatically.
;;;
;;;The following nice properties apply to registering queries:
;;;
;;;=over 1
;;;
;;;=item 1
;;;
;;;It is more efficient, because a registered query is preprocessed by SQLI on the
;;;possible arguments and stored as a list of query parts instead of a string.
;;;
;;;=item 1
;;;
;;;More important, one can register queries specific to a certain database SQL dialect
;;;under a name, thereby making the code more portable.
;;;
;;;=back
;;;
;;;=verbatim scm,8
    ; Register a query. This query is registered with a conversion function
    ; to convert the results of the select statement. Another thing to notice,
    ; is that the column for the select statement and the operator for the
    ; where part are parameterized ($1 and $2).

    (sqli-register sqli 'select 
		   "SELECT $1 FROM test WHERE age $2 $3"
		    (let ((c 0))
		     (lambda (a) 
		       (begin
			 (set! c (+ c 1))
			 (list c (car a))))))
    (displayp sqli)

    ; Here the registered query is executed for column name, with operator >.
    ; the first 10 rows of the result of the query are fetched and displayed.

    (displayp (sqli-exec sqli 'select 'name '> 2))
    (displayp (sqli-fetch sqli 10))

    ; Register an other query. This one demonstrates the type conversion
    ; capabilities of SQLI. We can see that the results of the select from test
    ; will be converted to string, integer, boolean and date.

    (sqli-register sqli 'select-convert
		   "SELECT * FROM test WHERE age=10"
		   'string 'integer 'boolean 'date)
    (displayp sqli)

    ; Here the registered query is executed. All results are fetched and displayed.

    (displayp (sqli-exec sqli 'select-convert))
    (displayp (sqli-fetchall sqli))
;;;=verbatim
;;;
;;;The next part uses the 'closure' interface of sqli, which is just a wrapper
;;;interface around the normal C<(sqli-[anything])> calls.
;;;
;;;=verbatim scm,8
    ; Use closure.

    (let ((clos (sqli-closure sqli)))
      (displayp (clos 'exec 'select 'age '<= 2))
      (displayp (clos 'fetchall))
      (clos 'register 'count "SELECT COUNT(name) FROM test"
	    (lambda (row) (car row)))
      (clos 'exec 'count);
      (displayp (clos 'fetchrow))

      (clos 'register 'and "SELECT nice FROM test" 
	    (lambda (row) 
	      (let ((b (clos 'convert (car row) 'boolean)))
		(list b))))
      
      (displayp (clos 'exec 'and))
      (displayp (clos 'fetchall))

      ;;; NOTE! THIS IS DIFFERENT FROM MOST OTHER DATABASES!
      (clos 'query "INSERT INTO test (name, age, nice) VALUES ('false',0,$1)" #f)

      (displayp (clos 'exec 'and))
      (displayp (clos 'fetchall))

      (clos 'hithere "errors"))

    (displayp sqli)
    (displayp (sqli-exec sqli 'and))
    (displayp (sqli-fetchall sqli))
;;;=verbatim
;;;
;;;In the end, one needs to disconnect from the database. This is mandatory,
;;;because it will cleanup the C structures, that are not garbage collected
;;;automatically. Apart from the fact that it is never a good idea to stop
;;;your program with an open connection to the database.
;;;
;;;=verbatim scm,8
  (sqli-disconnect sqli)

  0))
;;;=verbatim
;;;
;;;Now for some mzscheme specific code...
;;;
;;;=verbatim scm,8
;#+ mzscheme
(provide main))
;##
;;;=verbatim
;;;
;;;That's all to it!
;;;
;;;=head2 Info   
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;S<C<Package   : >> db2-test.scmE<lb>
;;;S<C<Author    : >> Hans Oesterholt-Dijkema.E<lb>
;;;S<C<Copyright : >> HOD 2004/2005.E<lb>
;;;S<C<License   : >> The Elemental Programming Artistic License.E<lb>
;;;S<C<CVS       : >> $Id: db2-test.scm,v 1.1 2006/01/04 20:15:18 HansOesterholt Exp $E<lb>
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;=cut