r8821: integrate usql support
[clsql.git] / usql / doc / usql-tutorial.txt
1 INTRODUCTION
2
3 The goal of this tutorial is to guide a new developer thru the process
4 of creating a set of USQL classes providing a Object-Oriented
5 interface to persistent data stored in an SQL database.  We will
6 assume that the reader is familiar with how SQL works, how relations
7 (tables) should be structured, and has created at least one SQL
8 application previously.  We will also assume a minor level of
9 experience with Common Lisp.
10
11 UncommonSQL (USQL) provides two different interfaces to SQL databases,
12 a Functional interface, and an Object-Oriented interface.  The
13 Functional interface consists of a special syntax for embedded SQL
14 expressions in Lisp, and provides lisp functions for SQL operations
15 like SELECT and UPDATE.  The OO interface provides a way for mapping
16 Common Lisp Objects System (CLOS) objects into databases and includes
17 functions for inserting new objects, querying objects, and removing
18 objects.  Most applications will use a combination of the two.
19
20 USQL is based on the CommonSQL package from Xanalys, so the
21 documentation that Xanalys makes available online is useful for USQL
22 as well.  It is suggested that developers new to USQL check their
23 documentation out, as any differences between CommonSQL and USQL are
24 minor. Xanalys makes the following documents available:
25
26 Xanalys LispWorks User Guide  - The CommonSQL Package
27 http://www.lispworks.com/reference/lw43/LWUG/html/lwuser-167.htm
28
29 Xanalys LispWorks Reference Manual -- The SQL Package
30 http://www.lispworks.com/reference/lw43/LWRM/html/lwref-383.htm
31
32 CommonSQL Tutorial by Nick Levine
33 http://www.ravenbrook.com/doc/2002/09/13/common-sql/
34
35
36 DATA MODELING WITH UNCOMMONSQL
37
38 Before we can create, query and manipulate USQL objects, we need to
39 define our data model. To borrow from Philip Greenspun[1]:
40
41 When data modeling, you are telling the RDBMS the following: 
42
43     * What elements of the data you will store 
44     * How large each element can be 
45     * What kind of information each element can contain 
46     * What elements may be left blank 
47     * Which elements are constrained to a fixed range 
48     * Whether and how various tables are to be linked 
49
50 With SQL database one would do this by defining a set of relations, or
51 tables, followed by a set of queries for joining the tables together
52 in order to construct complex records.  However, with USQL we do this
53 by defining a set of CLOS classes, specifying how they will be turned
54 into tables, and how they can be joined to one another via relations
55 between their attributes.  The SQL tables, as well as the queries for
56 joining them together are created for us automatically, saving us from
57 dealing with some of the tedium of SQL.
58
59 Let us start with a simple example of two SQL tables, and the
60 relations between them.
61
62 CREATE TABLE EMPLOYEE (
63        emplid           NOT NULL        number(38),
64        first_name       NOT NULL        varchar2(30),
65        last_name        NOT NULL        varchar2(30),
66        emall                            varchar2(100),
67        companyid        NOT NULL        number(38),
68        managerid                        number(38)
69 )
70
71 CREATE TABLE COMPANY (
72        companyid        NOT NULL        number(38),
73        name             NOT NULL        varchar2(100),
74        presidentid      NOT NULL        number(38)
75 )
76
77 This is of course the canonical SQL tutorial example, "The Org Chart".
78
79 In USQL, we would have two "view classes" (a fancy word for a class
80 mapped into a database).  They would be defined as follows:
81
82 (sql:def-view-class employee ()
83   ((emplid
84     :db-kind :key
85     :db-constraints :not-null
86     :type integer
87     :initarg :emplid)
88    (first-name
89     :accessor first-name
90     :type (string 30)
91     :initarg :first-name)
92    (last-name
93     :accessor last-name
94     :type (string 30)
95     :initarg :last-name)
96    (email
97     :accessor employee-email
98     :type (string 100)
99     :nulls-ok t
100     :initarg :email)
101    (companyid
102     :type integer)
103    (managerid
104     :type integer
105     :nulls-ok t))
106   (:base-table employee))
107
108 (sql:def-view-class company ()
109   ((companyid
110     :db-type :key
111     :db-constraints :not-null
112     :type integer
113     :initarg :companyid)
114    (name
115     :type (string 100)
116     :initarg :name)
117    (presidentid
118     :type integer))
119   (:base-table company))
120
121
122 The DEF-VIEW-CLASS macro is just like the normal CLOS DEFCLASS macro,
123 except that it handles several slot options that DEFCLASS doesn't.
124 These slot options have to do with the mapping of the slot into the
125 database.  We only use a few of the slot options in the above example,
126 but there are several others.
127
128     :column -- The name of the SQL column this slot is stored in.
129     Defaults to the slot name.  If the slot name is not a valid SQL
130     identifier, it is escaped, so foo-bar becomes foo_bar.
131
132     :db-kind -- The kind of DB mapping which is performed for this
133     slot.  :BASE indicates the slot maps to an ordinary column of the
134     DB view.  :KEY indicates that this slot corresponds to part of the
135     unique keys for this view, :JOIN indicates a join slot
136     representing a relation to another view and :virtual indicates
137     that this slot is an ordinary CLOS slot.  Defaults to :base.
138
139    :db-reader -- If a string, then when reading values from the DB, the
140     string will be used for a format string, with the only value being
141     the value from the database.  The resulting string will be used as
142     the slot value.  If a function then it will take one argument, the
143     value from the database, and return the value that should be put
144     into the slot.
145
146    :db-writer -- If a string, then when reading values from the slot
147     for the DB, the string will be used for a format string, with the
148     only value being the value of the slot.  The resulting string will
149     be used as the column value in the DB.  If a function then it will
150     take one argument, the value of the slot, and return the value
151     that should be put into the database.
152
153    :db-type -- A string which will be used as the type specifier for
154     this slots column definition in the database.
155
156    :nulls-ok -- If t, all sql NULL values retrieved from the database
157     become nil; if nil, all NULL values retrieved are converted by
158     DATABASE-NULL-VALUE
159
160    :db-info -- A join specification.
161
162 In our example each table as a primary key attribute, which is
163 required to be unique.  We indicate that a slot is part of the primary
164 key (USQL supports multi-field primary keys) by specifying the
165 :db-kind :key slot option.  
166
167 The SQL type of a slot when it is mapped into the database is
168 determined by the :type slot option.  The argument for the :type
169 option is a Common Lisp datatype.  The USQL framework will determine
170 the appropriate mapping depending on the database system the table is
171 being created in.  If we really wanted to determine what SQL type was
172 used for a slot, we could specify a :db-type option like "NUMBER(38)"
173 and we would be guaranteed that the slot would be stored n the DB as a
174 NUMBER(38).  This is not recomended because it could makes your view
175 class unportable across database systems.
176
177 DEF-VIEW-CLASS also supports some class options, like :base-table.
178 The :base-table option specifies what the table name for the view
179 class will be when it is mapped into the database.
180
181
182 CLASS RELATIONS
183
184 In an SQL only application, the EMPLOYEE and COMPANY tables can be
185 queried to determine things like, "Who is Vladamir's manager?", What
186 company does Josef work for?", and "What employees work for Widgets
187 Inc.".  This is done by joining tables with an SQL query.
188
189 Who works for Widgets Inc.?
190
191 SELECT first_name, last_name FROM employee, company
192        WHERE employee.companyid = company.companyid
193              AND company.company_name = "Widgets Inc."
194
195 Who is Vladamir's manager
196
197 SELECT managerid FROM employee
198        WHERE employee.first_name = "Vladamir"
199              AND employee.last_name = "Lenin"
200
201 What company does Josef work for?
202
203 SELECT company_name FROM company, employee
204        WHERE employee.first_name = "Josef"
205              AND employee.last-name = "Stalin"
206              AND employee.companyid = company.companyid
207
208 With USQL however we do not need to write out such queries because our
209 view classes can maintain the relations between employees and
210 companies, and employees to their managers for us.  We can then access
211 these relations like we would any other attribute of an employee or
212 company object.  In order to do this we define some join slots for our
213 view classes.
214
215 What company does an employee work for?  If we add the following slot
216 definition to the employee class we can then ask for it's COMPANY slot
217 and get the appropriate result.
218
219     ;; In the employee slot list
220     (company
221       :accessor employee-company
222       :db-kind :join
223       :db-info (:join-class company
224                 :home-key companyid
225                 :foreign-key companyid
226                 :set nil))
227
228 Who are the employees of a given company?  And who is the president of
229 it? We add the following slot definition to the company view class and
230 we can then ask for it's EMPLOYEES slot and get the right result.
231
232       ;; In the company slot list
233       (employees
234         :reader company-employees
235         :db-kind :join
236         :db-info (:join-class employee
237                   :home-key companyid
238                   :foreign-key companyid
239                   :set t))
240
241        (president
242         :reader president
243         :db-kind :join
244         :db-info (:join-class employee
245                   :home-key presidentid
246                   :foreign-key emplid
247                   :set nil))
248
249 And lastly, to define the relation between an employee and their
250 manager.
251
252         ;; In the employee slot list
253        (manager
254         :accessor employee-manager
255         :db-kind :join
256         :db-info (:join-class employee
257                   :home-key managerid
258                   :foreign-key emplid
259                   :set nil))
260
261 USQL join slots can represent one-to-one, one-to-many, and
262 many-to-many relations.  Above we only have one-to-one and one-to-many
263 relations, later we will explain how to model many-to-many relations.
264 First, let's go over the slot definitions and the available options.
265
266 In order for a slot to be a join, we must specify that it's :db-kind
267 :join, as opposed to :base or :key.  Once we do that, we still need to
268 tell USQL how to create the join statements for the relation.  This is
269 what the :db-info option does.  It is a list of keywords and values.
270 The available keywords are:
271
272     :join-class -- The view class to which we want to join.  It can be
273     another view class, or the same view class as our object.
274
275     :home-key -- The slot(s) in the immediate object whose value will
276     be compared to the foreign-key slot(s) in the join-class in order
277     to join the two tables.  It can be a single slot-name, or it can
278     be a list of slot names.
279
280     :foreign-key -- The slot(s) in the join-class which will be compared
281     to the value(s) of the home-key.
282
283     :set -- A boolean which if false, indicates that this is a
284     one-to-one relation, only one object will be returned.  If true,
285     than this is a one-to-many relation, a list of objects will be
286     returned when we ask for this slots value.
287
288 There are other :join-info options available in USQL, but we will save
289 those till we get to the many-to-many relation examples.
290
291
292 OBJECT CREATION
293
294 Now that we have our model laid out, we should create some object.
295 Let us assume that we have a database connect set up already.  We
296 first need to create our tables in the database:
297
298 Note: the file usql-tutorial.lisp contains view class definitions
299 which you can load into your list at this point in order to play along
300 at home.
301
302 (sql:create-view-from-class 'employee)
303 (sql:create-view-from-class 'company)
304
305 Then we will create our objects.  We create them just like you would
306 any other CLOS object:
307
308 (defvar employee1 (make-instance 'employee
309                                :emplid 1
310                                :first-name "Vladamir"
311                                :last-name "Lenin"
312                                :email "lenin@soviet.org"))
313
314 (defvar company1 (make-instance 'company
315                               :companyid 1
316                               :name "Widgets Inc."))
317                               
318
319 (defvar employee2 (make-instance 'employee
320                                :emplid 2
321                                :first-name "Josef"
322                                :last-name "Stalin"
323                                :email "stalin@soviet.org"))
324
325 In order to insert an objects into the database we use the
326 UPDATE-RECORDS-FROM-INSTANCE function as follows:
327
328 (sql:update-records-from-instance employee1)
329 (sql:update-records-from-instance employee2)
330 (sql:update-records-from-instance company1)
331
332 Now we can set up some of the relations between employees and
333 companies, and their managers.  The ADD-TO-RELATION method provides us
334 with an easy way of doing that.  It will update both the relation
335 slot, as well as the home-key and foreign-key slots in both objects in
336 the relation.
337
338 ;; Lenin manages Stalin (for now)
339 (sql:add-to-relation employee2 'manager employee1)
340
341 ;; Lenin and Stalin both work for Widgets Inc.
342 (sql:add-to-relation company1 'employees employee1)
343 (sql:add-to-relation company1 'employees employee2)
344
345 ;; Lenin is president of Widgets Inc.
346 (sql:add-to-relation company1 'president employee1)
347
348 After you make any changes to an object, you have to specifically tell
349 USQL to update the SQL database.  The UPDATE-RECORDS-FROM-INSTANCE
350 method will write all of the changes you have made to the object into
351 the database.
352
353 Since USQL objects re just normal CLOS objects, we can manipulate
354 their slots just like any other object.  For instance, let's say that
355 Lenin changes his email because he was getting too much SPAM fro the
356 German Socialists.
357
358 ;; Print Lenin's current email address, change it and save it to the
359 ;; database.  Get a new object representing Lenin from the database
360 ;; and print the email
361
362 ;; This lets us use the functional USQL interface with [] syntax
363 (sql:locally-enable-sql-reader-syntax)
364
365 (format t "The email address of ~A ~A is ~A"
366         (first-name employee1)
367         (last-name employee1)
368         (employee-email employee1))
369
370 (setf (employee-email employee1) "lenin-nospam@soviets.org")
371
372 ;; Update the database
373 (sql:update-records-from-instance employee1)
374
375 (let ((new-lenin (car (sql:select 'employee
376                         :where [= [slot-value 'employee 'emplid] 1]))))
377       (format t "His new email is ~A"
378           (employee-email new-lenin)))
379
380 Everything except for the last LET expression is already familiar to
381 us by now.  To understand the call to SQL:SELECT we need to discuss
382 the Functional SQL interface and it's integration with the Object
383 Oriented interface of USQL.
384
385
386 FINDING OBJECTS
387
388 Now that we have our objects in the database, how do we get them out
389 when we need to work with them?  USQL provides a Functional interface
390 to SQL, which consists of a special Lisp reader macro and some
391 functions.  The special syntax allows us to embed SQL in lisp
392 expressions, and lisp expressions in SQL, with ease.
393
394 Once we have turned on the syntax with the expression:
395
396 (sql:locally-enable-sql-reader-syntax)
397
398 we can start entering fragments of SQL into our lisp reader.  We will
399 get back objects which represent the lisp expressions.  These objects
400 will later be compiled into SQL expressions that are optimized for the
401 database backed we are connected to.  This means that we have a
402 database independent SQL syntax.  Here are some examples:
403
404 ;; an attribute or table name
405 [foo] => #<MAISQL-SYS::SQL-IDENT-ATTRIBUTE FOO>
406
407 ;; a attribute identifier with table qualifier
408 [foo bar] => #<MAISQL-SYS::SQL-IDENT-ATTRIBUTE FOO.BAR>
409
410 ;; a attribute identifier with table qualifier
411 [= "Lenin" [first_name]] =>
412    #<MAISQL-SYS::SQL-RELATIONAL-EXP ('Lenin' = FIRST_NAME)>
413
414 [< [emplid] 3] =>
415    #<MAISQL-SYS::SQL-RELATIONAL-EXP (EMPLID < 3)>
416
417 [and [< [emplid] 2] [= [first_name] "Lenin"]] =>
418    #<MAISQL-SYS::SQL-RELATIONAL-EXP ((EMPLID < 2) AND
419                                      (FIRST_NAME = 'Lenin'))>
420
421
422 ;; If we want to reference a slot in an object we can us the
423 ;;  SLOT-VALUE sql extension
424 [= [slot-value 'employee 'emplid] 1] =>
425    #<MAISQL-SYS::SQL-RELATIONAL-EXP (EMPLOYEE.EMPLID = 1)>
426
427 [= [slot-value 'employee 'emplid]
428    [slot-value 'company 'presidentid]] =>
429    #<MAISQL-SYS::SQL-RELATIONAL-EXP (EMPLOYEE.EMPLID = COMPANY.PRESIDENTID)>
430
431 The SLOT-VALUE operator is important because it let's us query objects
432 in a way that is robust to any changes in the object->table mapping,
433 like column name changes, or table name changes.  So when you are
434 querying objects, be sure to use the SLOT-VALUE SQL extension.
435
436 Since we can now formulate SQL relational expression which can be used
437 as qualifiers, like we put after the WHERE keyword in SQL statements,
438 we can start querying our objects.  USQL provides a function SELECT
439 which can return use complete objects from the database which conform
440 to a qualifier, can be sorted, and various other SQL operations.
441
442 The first argument to SELECT is a class name.  it also has a set of
443 keyword arguments which are covered in the documentation.  For now we
444 will concern ourselves only with the :where keyword.  Select returns a
445 list of objects, or nil if it can't find any.  It's important to
446 remember that it always returns a list, so even if you are expecting
447 only one result, you should remember to extract it from the list you
448 get from SELECT.
449
450 ;; all employees
451 (sql:select 'employee)
452 ;; all companies
453 (sql:select 'company)
454
455 ;; employees named Lenin
456 (sql:select 'employee :where [= [slot-value 'employee 'last-name]
457                                 "Lenin"])
458
459 (sql:select 'company :where [= [slot-value 'company 'name]
460                                "Widgets Inc."])
461
462 ;; Employees of Widget's Inc.
463 (sql:select 'employee
464             :where [and [= [slot-value 'employee 'companyid]
465                            [slot-value 'company 'companyid]]
466                         [= [slot-value 'company 'name]
467                            "Widgets Inc."]])
468
469 ;; Same thing, except that we are using the employee
470 ;; relation in the company view class to do the join for us,
471 ;; saving us the work of writing out the SQL!
472 (company-employees company1)
473
474 ;; President of Widgets Inc.
475 (president company1)
476
477 ;; Manager of Josef Stalin
478 (employee-manager employee2)
479
480
481 DELETING OBJECTS
482
483 Now that we know how to create objects in our database, manipulate
484 them and query them (including using our predefined relations to save
485 us the trouble writing alot of SQL) we should learn how to clean up
486 after ourself.  It's quite simple really. The function
487 DELETE-INSTANCE-RECORDS will remove an object from the database.
488 However, when we remove an object we are responsible for making sure
489 that the database is left in a correct state.
490
491 For example, if we remove a company record, we need to either remove
492 all of it's employees or we need to move them to another company.
493 Likewise if we remove an employee, we should make sure to update any
494 other employees who had them as a manager.
495
496
497 CONCLUSION
498
499 There are alot more nooks and crannies to USQL, some of which are
500 covered n the Xanalys documents we refered to earlier, some are not.
501 The best documentation at this time is still the source code for USQL
502 itself and the inline documentation for it's various function.
503
504
505
506 [1] Philip Greenspun's "SQL For Web Nerds" - Data Modeling
507     http://www.arsdigita.com/books/sql/data-modeling.html
508
509