This file is indexed.

/usr/share/perl5/Tangram/Relational/Mappings.pod is in libtangram-perl 2.12-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
=head1 NAME

Tangram::Relational::Mappings - Mapping inheritance

=head1 DESCRIPTION

There are many ways of representing inheritance relationships in a relational
database. This document describes three popular ways and how Tangram supports
them.

=head1 STRATEGIES FOR MAPPING INHERITANCE

Inheritance is a concept that has no equivalent in the relational
world. However, it is possible to implement it by using strict
disciplines and a combination of relational features like tables and
foreign keys.

One of the paramount issues about mapping inheritance is how well the
mapping supports polymorphism.  Any Object-Oriented persistence
facility that deserves its name needs to allow the retrieval of all
the Fruits, and return a heterogeneous collection of Apples, Oranges
and Bananas.  Also, it must perform this operation in an efficient
manner.  In particular, polymorphic retrieval should not cost one
SELECT per retrieved object.

A secondary - yet important - issue is how well the mapping plays by
the rules of orthogonal orthodoxy.

Another issue we'll examine is how well the mapping supports 'complex'
queries, that is, queries that involve several objects.

Three strategies are in common use, that go by the name Vertical,
Horizontal and Filtered mapping.  They all have advantages and
disadvantages.

The following sections describe the three strategies in details. They
make use of a simple object model to illustrate the mappings.


                          +---------------------+
                          |        Person       |
                          |      {abstract}     |
     +---------<------- 1 +---------------------+
     |                    | name: string        |
     |                    +---------------------+
     |                               |
     |                               ^
     |                               |
     |            +------------------+---------------------+
     |            |                                        |
     |   +---------------+                        +-----------------+
     V   | NaturalPerson |                        |    LegalPerson  |
     |   +---------------+                        +-----------------+
     |   |  age: integer |                        | form: string    |
     |   +---------------+                        +-----------------+
     |
     |
     |
     |                    +---------------------+
     +-------->-------- * |        Vehicle      |
                          |       {abstract}    |
                          +---------------------+
                          | make: string        |
                          +---------------------+
                                     |
                                     ^
                                     |
                  +------------------+-------------------+
                  |                                      |
         +---------------+                      +-----------------+
         |      Car      |                      |      Plane      |
         +---------------+                      +-----------------+
         | plate: string |                      | ident: string   |
         +---------------+                      +-----------------+


=head1 Horizontal Mapping

=head1 description

Each I<concrete> class is mapped onto a single table. Each row in the
table describes the persistent state of one object.

The attributes are mapped onto columns, usually one column per
attribute but not necessarily. For example, collections may be stored
elsewhere (for example on a link table) and thus require no column on
the class' table.

In effect, the database looks like this:

         +---------------+
         | NaturalPerson |
         +------+--------+-------+------+
         |  id  |  name          | age  |
         ================================
         |  17  | Bill Gates     |  46  |
         +------+----------------+------+
         |  23  | Georges Bush   |  50  |
         +------+----------------+------+


         +-------------+
         | LegalPerson |
         +------+------+---------+------+
         |  id  |  name          | form |
         ================================
         |  36  |  Microsoft     |  Inc |
         +------+----------------+------+


         +------+
         | Car  |
         +------+-------+----------------+--------+
         |  id  | owner	|  make          | plate  |
         ==========================================
         |  12  |  17  	| Saab           | BILL-1 |
         +------+-------+----------------+--------+
         |  50  |  36  	| Miata          | MS-001 |
         +------+-------+----------------+--------+
         |  51  |  36  	| Miata          | MS-002 |
         +------+-------+----------------+--------+


         +-------+
         | Plane |
         +------++-----+----------------+--------+
         |  id  | owner|  make          | ident  |
         =========================================
         |  29  |  23  |  Boeing        | AF-001 |
         +------+------+----------------+--------+

=head2 advantages

Polymorphic retrieval costs one SELECT per concrete conforming class;
retrieving all the Persons costs two SELECTs. These SELECTs, however,
don't use joins - an expensive operation. In our example, retrieving
all the Persons requires the following two SELECTs:

   SELECT id, name, age FROM NaturalPerson
   SELECT id, name, form FROM LegalPerson

=head2 disadvantages

This mapping is reasonable with regard to relational orthodoxy, but
not perfect: the 'name' column is present on two different tables,
with the same semantic.

The biggest drawback, however, happens when you try to perfrom complex
queries.  Suppose oyu want to retrieve all the Persons (Natural- or
Legal-) that own a Vehicle of make 'Saab' (be it a Car or a
Plane). Sticking with equijoins, the cost of the operation is four
SELECTs:

   SELECT NaturalPerson.id, NaturalPerson.name, NaturalPerson.age
      FROM NaturalPerson, Car
      WHERE Car.owner = NaturalPerson.id

   SELECT NaturalPerson.id, NaturalPerson.name, NaturalPerson.age
      FROM NaturalPerson, Plane
      WHERE Plane.owner = NaturalPerson.id

   SELECT LegalPerson.id, LegalPerson.name, LegalPerson.form
      FROM LegalPerson, Car
      WHERE Car.owner = LegalPerson.id

   SELECT LegalPerson.id, LegalPerson.name, LegalPerson.form
      FROM LegalPerson, Plane
      WHERE Plane.owner = LegalPerson.id

When the depth of the hierarchies increase, the combinatory explosion
makes complex queries prohibitive.

=head1 Vertical Mapping

=head2 description

Each class has its corresponding table, which contains only the class'
direct fields. In other words, the table doesn't store the inherited
fields. Both concrete and abstract classes get a table. The state of
an object is thus scattered over several tables.

For example:

         +--------+
         | Person |
         +------+-+------+-------+
         |  id  |  name          |
         =========================
         |  17  | Bill Gates     |
         +------+----------------+
         |  23  | Georges Bush   |
         +------+----------------+
         |  36  | Microsoft      |
         +------+----------------+


         +---------------+     +-------------+
         | NaturalPerson |     | LegalPerson |
         +------+--------+     +-------+-----++
         |  id  | age    |     |  id   | form |
         =================     ================
         |  17  |  46    |     |  36   |  Inc |
         +------+--------+     +-------+------+
         |  23  |  50    |
         +------+--------+

         +---------+
         | Vehicle |
         +------+--+----+----------------+
         |  id  | owner |  make          |
         =================================
         |  12  | 17    | Saab           |
         +------+-------+----------------+
         |  29  | 23    | AF-001         |
         +------+-------+----------------+
         |  50  | 36    | Miata          |
         +------+-------+----------------+
         |  51  | 36    | Miata          |
         +------+-------+----------------+

         +------+              +-------+
         | Car  |              | Plane |
         +------++--------+    +-------+--------+
         |  id   | plate  |    |  id   | ident  |
         ==================    ==================
         |  12   | BILL-1 |    |  29   | AF-001 |
         +-------+--------+    +-------+--------+
         |  50   | MS-001 |
         +-------+--------+
         |  51   | MS-002 |
         +-------+--------+

Polymorphic retrieval is achieved by issuing one SELECT per concrete
conforming class; retrieving In our example, retrieving all the
Persons requires the following two SELECTs:

   SELECT Person.id, Person.name, NaturalPerson.age
      FROM Person, NaturalPerson
      WHERE Person.id = NaturalPerson.id

   SELECT Person.id, Person.name, LegalPerson.form
      FROM Person, LegalPerson
      WHERE Person.id = LegalPerson.id

This mapping sometimes needs an extra column that carries a type
identifier. In our example, we take the very resonable assumption that
Person is an abstract class. Had we decided to allow 'pure' Persons,
we would have been faced with the following problem: the Person table
would contain rows that describe pure Persons, but also rows that
describe the Person part of Natural- and LegalPersons. We would need
to filter those incomplete objects out when retrieving the pure
Persons. Thus the Person table would look like this:

         +--------+
         | Person |
         +-----+--+---+----------------+
         | id  | type |  name          |
         ===============================
         | 13  | 1    | Pure Person    |
         +-----+------+----------------+
         | 17  | 2    | Bill Gates     |
         +-----+------+----------------+
         | 23  | 2    | Georges Bush   |
         +-----+------+----------------+
         | 36  | 3    | Microsoft      |
         +-----+------+----------------+

In this case, we need an extra SELECT for retrieving pure Persons:

   SELECT Person.id, Person.name
      FROM Person
      WHERE Person.type IN (1)

=head2 advantages

From the relational point of view, this mapping is excellent: the
resulting database is in third normal form.

This mapping also supports complex queries very well. Take the Saab
owners example again: we don't need to involve the Car nor Plane
tables in the query. As a result, two SELECTs suffice:

   SELECT Person.id, Person.name, NaturalPerson.age
      FROM Person, NaturalPerson, Vehicle
      WHERE Person.id = NaturalPerson.id AND Vehicle.owner = Person.id

   SELECT Person.id, Person.name, LegalPerson.form
      FROM Person, LegalPerson, Vehicle
      WHERE Person.id = LegalPerson.id AND Vehicle.owner = Person.id


=head2 disadvantages

The mapping potentially has the highest performance cost: it requires
multiple SELECTs like the horizontal mapping, but in addition, these
SELECTs use joins.


=head1 Filtered Mapping

=head2 description

Entire hierarchies are mapped onto a single table. Two rows may
describe objects of different types, maybe completely unrelated. The
set of columns is the uperset of all the columns needed by all the
attributes of any of the classes involved in the mapping.

A special 'type' column contains an value that uniquely identifies the
concrete class of the object described by the row.

All the columns related to attributes that don't occur in all the
classes must be declared as NULLABLE. Indeed, the table may contain
mostly NULL values.

In our example, the database may look either like this:

         +---------+
         | Persons |
         +-----+---+--+----------------+------+------+
         | id  | type |  name          | age  | form |
         =============================================
         | 17  |  1   | Bill Gates     |  46  | NULL |
         +-----+------+----------------+------+------+
         | 23  |  1   | Georges Bush   |  50  | NULL |
         +-----+------+----------------+------+------+
         | 36  |  2   | Microsoft      | NULL |  Inc |
         +-----+------+----------------+------+------+

         +---------+
         | Persons |
         +-----+---+--+----------------+------+------+
         | id  | type |  name          | age  | form |
         =============================================
         | 17  |  1   | Bill Gates     |  46  | NULL |
         +-----+------+----------------+------+------+
         | 23  |  1   | Georges Bush   |  50  | NULL |
         +-----+------+----------------+------+------+
         | 36  |  2   | Microsoft      | NULL |  Inc |
         +-----+------+----------------+------+------+
         | 36  |  2   | Microsoft      | NULL |  Inc |
         +-----+------+----------------+------+------+

         +----------+
         | Vehicles |
         +-----+----+-+-------+----------------+--------+--------+
         | id  | type | owner |  make          | plate  | ident  |
         =========================================================
         | 12  |  3   |  17   | Saab           | BILL-1 | NULL   |
         +-----+------+-------+----------------+--------+--------+
         | 29  |  4   |  23   | Boeing         | NULL   | AF-001 |
         +-----+------+-------+----------------+--------+--------+
         | 50  |  3   |  36   | Miata          | MS-001 | NULL   |
         +-----+------+-------+----------------+--------+--------+
         | 51  |  3   |  36   | Miata          | MS-002 | NULL   |
         +-----+------+-------+----------------+--------+--------+

Retrieving all the Persons requires only one SELECT:

   SELECT id, name, age, form FROM Persons 

When retrieving NaturalPersons we must take care to filter out the
rows that belog to LegalPersons:

   SELECT id, name, age FROM Persons WHERE type = 1

We may even decide to place unrelated hierarchies on the same table:

   +---------+
   | Objects |
   +-----+---+--+---------------+------+------+--------+--------+--------+
   | id  | type |  name         | age  | form | make   | plate  | ident  |
   =======================================================================
   | 17  |  1   | Bill Gates    |  46  | NULL | NULL   | NULL   | NULL   |
   +-----+------+---------------+------+------+--------+--------+--------+
   | 23  |  1   | Georges Bush  |  50  | NULL | NULL   | NULL   | NULL   |
   +-----+------+---------------+------+------+--------+--------+--------+
   | 36  |  2   | Microsoft     | NULL | Inc  | NULL   | NULL   | NULL   |
   +-----+------+---------------+------+------+--------+--------+--------+
   | 12  |  3   | NULL          | NULL | NULL | Saab   | BILL-1 | NULL   |
   +-----+------+---------------+------+------+--------+--------+--------+
   | 29  |  4   | NULL          | NULL | NULL | Boeing | NULL   | AF-001 |
   +-----+------+---------------+------+------+--------+--------+--------+
   | 50  |  3   | NULL          | NULL | NULL | Miata  | MS-001 | NULL   |
   +-----+------+---------------+------+------+--------+--------+--------+
   | 51  |  3   | NULL          | NULL | NULL | Miata  | MS-002 | NULL   |
   +-----+------+---------------+------+------+--------+--------+--------+

=head2 advantages

Polymorphic retrieval costs exactly one SELECT, regardless of the
number of conforming types. Thus this mapping potentially is the most
efficient.

=head2 disadvantages

This mapping is very questionable according to relational
orthodoxy. Even if one decides to forgo these rules, using such a
mapping takes away many of the interesting features offered by modern
RDBM systems. Because nearly all the columns must allow NULL values,
we cannot take advantage of features like referential integrity
constraints, domain constraints, indexes, etc.

Also, as the table becomes cluttered with NULL values, the relative
number of significant columns in any given row tends towards zero: we
may end up retrieving rows consisting of a little information swimming
in a sea of NULLs.

In effect, this mapping may end up hindering performance instead of
improving it in presence of deep hierarchies with many attributes.

=head1 MAPPINGS SUPPORTED BY TANGRAM

Tangram supports both vertical mapping and filtered mapping, and any
hybrid of the two.

The 'table' attribute in the class description in the Schema can be
used to put the state of several classes on the same table. The table
name defaults to the class name, resulting in a vertical mapping.

For example, the following schema:

   Tangram::Relational->schema( {
        classes =>
          [ Person =>
            {
             table => 'Persons',
             fields => { string => [ qw( name ) ] }
            },
            
            NaturalPerson =>
            {
             table => 'Persons',
             fields => { int => [ qw( age ) ] }
            },
            
            LegalPerson =>
            {
             table => 'Persons',
             fields => { string => [ qw( form ) ] }
            }
          ] } );

...specifies a pure filtered mapping for the Person hierarchy:

   CREATE TABLE Persons
   (
     id INTEGER NOT NULL,
     PRIMARY KEY( id ),
     type INTEGER NOT NULL,
     form VARCHAR(255) NULL,
     age INT NULL,
     name VARCHAR(255) NULL
   );

The following schema:
   
   Tangram::Relational->schema( {
        classes =>
          [ Person =>
            {
             table => 'Person',
             fields => { string => [ qw( name ) ] }
            },
            
            NaturalPerson =>
            {
             table => 'NaturalPerson',
             fields => { int => [ qw( age ) ] }
            },
            
            LegalPerson =>
            {
             table => 'Person',
             fields => { string => [ qw( form ) ] }
            }
          ] } );

...gives NaturalPerson its own table, but LegalPerson shares the
Person table:

   CREATE TABLE Person
   (
     id INTEGER NOT NULL,
     PRIMARY KEY( id ),
     type INTEGER NOT NULL,
     form VARCHAR(255) NULL,
     name VARCHAR(255) NULL
   );

   CREATE TABLE NaturalPerson
   (
     id INTEGER NOT NULL,
     PRIMARY KEY( id ),
     type INTEGER NOT NULL,
     age INT NULL
   );