library will provide, which you'll use in Chapter 29.

(defparameter *default-table-size* 100)

(defun make-rows (&optional (size *default-table-size*))

(make-array size :adjustable t :fill-pointer 0))

To represent a table's schema, you need to define another class, column, each instance of which will contain information about one column in the table: its name, how to compare values in the column for equality and ordering, a default value, and a function that will be used to normalize the column's values when inserting data into the table and when querying the table. The schema slot will hold a list of column objects. The class definition looks like this:

(defclass column ()

((name

:reader name

:initarg :name)

(equality-predicate

:reader equality-predicate

:initarg :equality-predicate)

(comparator

:reader comparator

:initarg :comparator)

(default-value

:reader default-value

:initarg :default-value

:initform nil)

(value-normalizer

:reader value-normalizer

:initarg :value-normalizer

:initform #'(lambda (v column) (declare (ignore column)) v))))

The equality-predicate and comparator slots of a column object hold functions used to compare values from the given column for equivalence and ordering. Thus, a column containing string values might have STRING= as its equality- predicate and STRING< as its comparator, while a column containing numbers might have = and <.

The default-value and value-normalizer slots are used when inserting rows into the database and, in the case of value-normalizer, when querying the database. When you insert a row into the database, if no value is provided for a particular column, you can use the value stored in the column's default-value slot. Then the value—defaulted or otherwise—is normalized by passing it and the column object to the function stored in the value-normalizer slot. You pass the column in case the value-normalizer function needs to use some data associated with the column object. (You'll see an example of this in the next section.) You should also normalize values passed in queries before comparing them with values in the database.

Thus, the value-normalizer's responsibility is primarily to return a value that can be safely and correctly passed to the equality-predicate and comparator functions. If the value-normalizer can't figure out an appropriate value to return, it can signal an error.

The other reason to normalize values before you store them in the database is to save both memory and CPU cycles. For instance, if you have a column that's going to contain string values but the number of distinct strings that will be stored in the column is small—for instance, the genre column in the MP3 database—you can save space and speed by using the value-normalizer to intern the strings (translate all STRING= values to a single string object). Thus, you'll need only as many strings as there are distinct values, regardless of how many rows are in the table, and you can use EQL to compare column values rather than the slower STRING=.[292]

Defining a Schema

Thus, to make an instance of table, you need to build a list of column objects. You could build the list by hand, using LIST and MAKE-INSTANCE. But you'll soon notice that you're frequently making a lot column objects with the same comparator and equality-predicate combinations. This is because the combination of a comparator and equality predicate essentially defines a column type. It'd be nice if there was a way to give those types names that would allow you to say simply that a given column is a string column, rather than having to specify STRING< as its comparator and STRING= as its equality predicate. One way is to define a generic function, make-column, like this:

(defgeneric make-column (name type &optional default-value))

Now you can implement methods on this generic function that specialize on type with EQL specializers and return column objects with the slots filled in with appropriate values. Here's the generic function and methods that define column types for the type names string and number:

(defmethod make-column (name (type (eql 'string)) &optional default-value)

(make-instance

'column

:name name

:comparator #'string<

:equality-predicate #'string=

:default-value default-value

:value-normalizer #'not-nullable))

(defmethod make-column (name (type (eql 'number)) &optional default-value)

(make-instance

'column

:name name

:comparator #'<

:equality-predicate #'=

:default-value default-value))

The following function, not-nullable, used as the value-normalizer for string columns, simply returns the value it's given unless the value is NIL, in which case it signals an error:

(defun not-nullable (value column)

(or value (error 'Column ~a can't be null' (name column))))

Вы читаете Practical Common Lisp
Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

Вы можете отметить интересные вам фрагменты текста, которые будут доступны по уникальной ссылке в адресной строке браузера.

Отметить Добавить цитату