void test() { screens.specify_foreign(screens->cinema_id, cinemas); screens.specify_foreign(screens->current_movie_id, movies); movies.open(); cinemas.open(); screens.open();
Let's start with the open()
calls, and remember that, for each table,
quince has already built a value mapper, which tells it how the table's
value type should be represented, all the way down to columns. Using that
information, each open()
call does one of two things:
open()
creates it, with the columns that
it should have.
open()
checks that its columns match those
that it should have, and throws if they don't.
So the first run of the program will create the SQL tables, and subsequent runs will check that they're still in good shape.
The last thing open()
does (assuming it hasn't thrown) is to mark the table object as being in
the open state. That is a precondition for all subsequent quince operations
that modify or inspect the table's data, i.e. anything that assumes the
columns are what they should be.
Now let's move up the page to the specify_foreign()
calls. These give quince instructions
in advance. E.g. the first one says: “If, in a forthcoming invocation
of screens.open()
,
you find yourself creating the SQL table, then please create it with a
foreign key constraint that ties its cinema_id
to the primary keys in the cinemas
table.” Consequently, in the first run of our program, screens.open()
will create foreign key constraints as it creates the table.
(That is the simplest form of specify_foreign()
, for the common case where the foreign
key is tied to some table's primary key. There are variations to cover
other cases.)
Perhaps the most valuable thing to notice about the two specify_foreign
lines is their use of operator->()
, with a table on the left-hand side.
It returns a pointer to the table's value mapper. In the case of screens->
...
,
that's the mapper that knows how to map a screen
when it occurs in the screens
table.
What, then, is screens->cinema_id
?
Obviously it's a member of the mapper just described. Now remembering our
mantra that the members of the mapper are the mappers of the
members, it follows that screens->cinema_id
is the mapper that tells quince how to map screen::cinema_id
,
when it occurs in the screens
table; and likewise screens->current_movie_id
is the mapper that tells quince how to map screen::current_movie_id
when it occurs in the screens
table.
But wait: why are we talking about mappers? In the context of specify_foreign()
,
no data is being converted from C++ types to columns or vice
versa. What's the mapping angle?
The truth is that mappers are the all-purpose go-to object whenever we want to talk to quince about metadata. Mappers are to quince what column names are to SQL. This is something we shall see over and over.