190 lines
6.2 KiB
Plaintext
190 lines
6.2 KiB
Plaintext
SrPersist Tutorial
|
|
==================
|
|
|
|
If you look at the doc.txt file in the SrPersist collection,
|
|
you soon realize that the ODBC standard is quite complex.
|
|
It is not necessary to master the standard before using it.
|
|
Performing simple data retrievals and insertions is not very
|
|
difficult. This tutorial provides some simple examples that
|
|
you can start with when writing your own code.
|
|
|
|
This tutorial does not address issues of compiling and
|
|
installing SrPersist. See the README file in the
|
|
SrPersist source directory for such information.
|
|
|
|
See the section "Loading" in doc.txt to learn how to load
|
|
SrPersist into MzScheme or DrScheme. From Help Desk,
|
|
search for "SrPersist" and scroll down to that section.
|
|
|
|
Allocating handles
|
|
------------------
|
|
|
|
Before you can connect to a database, you need to allocate
|
|
an environment handle, and a connection handle:
|
|
|
|
(define henv (alloc-env))
|
|
(define hdbc (alloc-connect henv))
|
|
|
|
We bind these identifiers to the handle values, so that
|
|
we can refer to the handles at later points in the program.
|
|
|
|
Getting a connection
|
|
--------------------
|
|
|
|
SrPersist provides three procedures to connect to a database,
|
|
two of which we mention here.
|
|
|
|
When getting started, you can use
|
|
|
|
(driver-connect hdbc "" 'sql-driver-prompt)
|
|
|
|
where `hdbc' is the connection handle just allocated.
|
|
This procedure displays a dialog box, or series of them.
|
|
From the dialog boxes, you should be able to choose
|
|
the database system and a particular database to
|
|
connect to. This procedure returns a string, which
|
|
you can use in place of the empty string the next
|
|
time you need to call this procedure. The string
|
|
contains information about the database system and
|
|
database you chose through the dialogs. Using that
|
|
returned string, this procedure will not show a
|
|
dialog box.
|
|
|
|
Alternatively, you can use
|
|
|
|
(connect hdbc dbms name password)
|
|
|
|
where again `hdbc' is the connection handle you've
|
|
allocated. `dbms', `name', and `password' are strings
|
|
indicating a database system (a "data source" in ODBC
|
|
parlance), a login name, and login password. Unlike
|
|
driver-connect, you have to know the name of the
|
|
database system, which may not be obvious. To find out
|
|
this information, you can call
|
|
|
|
(data-sources henv 'sql-fetch-first)
|
|
|
|
to get a data source name and its description. Calling
|
|
data-sources with 'sql-fetch-next gets the next data source;
|
|
you can continue making such calls until you've enumerated
|
|
all possible data sources.
|
|
|
|
Making a statement
|
|
------------------
|
|
|
|
Once your program is connected to a database system,
|
|
you'll want to submit queries in the form of SQL.
|
|
Be patient, for it takes several steps to submit such
|
|
a query.
|
|
|
|
First you'll need to allocate a statement handle
|
|
using the existing connection handle:
|
|
|
|
(define hstmt (alloc-stmt hdbc))
|
|
|
|
We'll see that we can reuse this statement handle
|
|
for several SQL queries.
|
|
|
|
When you connected to the database system, you
|
|
chose some particular database. The database system
|
|
may contain several databases. SQL has the USE statement
|
|
to choose among them. In SrPersist, we write:
|
|
|
|
(prepare hstmt "USE test_db")
|
|
(sql-execute hstmt)
|
|
|
|
Note that some database systems, such as Microsoft Access,
|
|
do not allow you to switch databases in this way. You can
|
|
think of prepare as performing a compilation step; sql-execute
|
|
runs the resulting code.
|
|
|
|
Now suppose the database test_db contains a table
|
|
"people" that has columns for name, a string, and
|
|
age, an integer.
|
|
|
|
We can make a query to get the desired data from the
|
|
database.
|
|
|
|
(prepare hstmt "SELECT name,age FROM people")
|
|
(sql-execute hstmt)
|
|
|
|
Conceptually, the statement above creates a new table,
|
|
consisting of rows of data. We need some location
|
|
in our program to store the data. ODBC uses buffers
|
|
for data storage. SrPersist associates an ODBC C type
|
|
with each buffer.
|
|
|
|
Assume that the name column consists of strings
|
|
no longer than 50 characters. We create a
|
|
buffer to hold results:
|
|
|
|
(define name-buffer (make-buffer '(sql-c-char 50)))
|
|
|
|
For the age column:
|
|
|
|
(define age-buffer (make-buffer 'sql-c-slong))
|
|
|
|
There are ways to find out the types associated with columns,
|
|
but unfortunately, it's a complicated business. There are
|
|
actually distinct types (SQL types) for the columns themselves,
|
|
and separate C types for buffers that receive their data.
|
|
But 'sql-c-char is probably what you want for string buffers,
|
|
and 'sql-c-slong for integer buffers.
|
|
|
|
We'll need another kind of buffer, an "indicator":
|
|
|
|
(define name-indicator (make-indicator))
|
|
(define age-indicator (make-indicator))
|
|
|
|
These indicators do not hold data, just status information.
|
|
We can safely ignore their role for the remainder of this
|
|
tutorial.
|
|
|
|
Next, we wish to associate the buffers we've created with
|
|
the database columns:
|
|
|
|
(bind-col hstmt 1 name-buffer name-indicator)
|
|
(bind-col hstmt 2 age-buffer age-indicator)
|
|
|
|
Columns are numbered from 1. Although the people table
|
|
may have had the name and age at any position, our
|
|
query above created the name column as column 1,
|
|
and the age column as column 2.
|
|
|
|
Now we can retrieve the data and print it out:
|
|
|
|
(with-handlers
|
|
([(lambda (exn) (exn-no-data? exn))
|
|
(lambda (exn) (printf "** End of data **~n"))])
|
|
(let loop ()
|
|
(fetch hstmt)
|
|
(printf "Name: ~a Age: ~a~n"
|
|
(read-buffer name-buffer)
|
|
(read-buffer age-buffer))
|
|
(loop)))
|
|
|
|
The code loops through each row and prints the
|
|
values stored in the buffers. When all the
|
|
data has been read, the call to fetch raises
|
|
the exn-no-data exception.
|
|
|
|
Suppose we want to insert a new record into the table people.
|
|
Assume that the table consists of the columns name, address,
|
|
and age. To perform the insertion, we simply write the
|
|
appropriate SQL, and run it:
|
|
|
|
(prepare hstmt
|
|
(string-append "INSERT INTO people SET "
|
|
"name=\"Joe Bloggs\","
|
|
"address=\"123 Main Street\","
|
|
"age=42"))
|
|
(sql-execute hstmt)
|
|
|
|
If you now perform the SELECT query above, and run the
|
|
given loop over the results, you should see the effect
|
|
of the insertion.
|
|
|
|
While there's much more in the ODBC standard, this example
|
|
code should give you the flavor of how it works.
|
|
|