home *** CD-ROM | disk | FTP | other *** search
/ Complete Linux / Complete Linux.iso / docs / apps / database / postgres / postgre2.z / postgre2 / doc / papers / implementation < prev    next >
Encoding:
Text File  |  1992-08-27  |  91.5 KB  |  2,246 lines

  1.  
  2. .nr tp 12
  3. .nr sp 12
  4. .nr pp 10
  5. .nr fp 8
  6. .sz \n(pp
  7. .vs 18        \" set 1.5 spacing between lines
  8. .nr $r \n(.vu/\n(.su
  9. .nr $R \n($r
  10. .fo ''%''
  11. .(l C
  12. .sz \n(sp
  13. .b
  14. THE IMPLEMENTATION OF POSTGRES
  15.  
  16. .sz \n(pp
  17. .sp 3
  18. .i
  19. Michael Stonebraker, Lawrence A. Rowe and Michael Hirohama
  20. EECS Department
  21. University of California, Berkeley
  22. .sp
  23. .r
  24. .)l
  25. .sp 3
  26. .(f
  27. This research was sponsored
  28. by the Defense Advanced Research Projects Agency
  29. through
  30. NASA Grant NAG 2-530
  31. and
  32. by the Army Research Office through
  33. Grant DAALO3-87-K-0083.
  34. .)f
  35. .uh Abstract
  36. .pp
  37. Currently, POSTGRES is about 90,000
  38. lines of code in C and is being used by 
  39. assorted ``bold and brave''
  40. early users.  The system has been constructed by a team of
  41. 5 part time students led by a full time chief
  42. programmer over the last three years.  During 
  43. this period, we have made a large number of design and
  44. implementation choices.
  45. Moreover, in some areas we would do 
  46. things quite differently if we were to start from 
  47. scratch again.  The purpose of this paper is to
  48. reflect on the design and implementation decisions
  49. we made and to offer advice to implementors who 
  50. might follow some of our paths.
  51. In this paper we restrict our attention to
  52. the DBMS ``backend'' functions.  In another paper some of us
  53. treat PICASSO, the application development environment that is being
  54. built on
  55. top of POSTGRES.
  56. .sh 1  "INTRODUCTION"
  57. .pp
  58. Current relational DBMSs are oriented toward efficient support for 
  59. business data processing applications where large numbers of instances
  60. of fixed format records must be stored and accessed.  
  61. Traditional transaction management and query facilities for this
  62. application area will be termed 
  63. .b data 
  64. .b management.
  65. .pp
  66. To satisfy the broader application community outside
  67. of business applications,
  68. DBMSs will have to expand to offer services in two other
  69. dimensions, namely 
  70. .b object 
  71. .b management
  72. and
  73. .b knowledge
  74. .b management.
  75. Object management entails efficiently storing and manipulating
  76. non-traditional data types such as bitmaps, icons, text, and polygons.
  77. Object management problems abound in CAD and many
  78. other engineering applications.  
  79. Object-oriented programming languages and data bases provide services
  80. in this area.
  81. .pp
  82. Knowledge management entails
  83. the ability to store and enforce a collection of
  84. .b rules
  85. that are part of the semantics of an application.
  86. Such rules describe integrity constraints about the application,
  87. as well as 
  88. allowing the derivation of data that is not directly stored in
  89. the data base.
  90. .pp
  91. We now indicate a simple example which requires 
  92. services in all three dimensions.  
  93. Consider an application that stores and manipulates text and graphics
  94. to facilitate the layout of
  95. newspaper copy.  
  96. Such a system will be naturally integrated with subscription 
  97. and classified advertisement data.  Billing customers for these
  98. services will require traditional data management services.  In addition,
  99. this application must store non-traditional objects including text,
  100. bitmaps (pictures), and icons (the banner across the top of the paper).
  101. Hence, object management services are required.
  102. Lastly, there are many rules that control newspaper layout.
  103. For example, the ad copy for two major department stores can never be
  104. on facing pages.  Support for such rules is 
  105. desirable in this application.  
  106. .pp
  107. We believe that 
  108. .b most 
  109. real
  110. world data management problems are 
  111. .b three 
  112. .b dimensional.  
  113. Like the
  114. newspaper
  115. application, they  will
  116. require a three dimensional solution.  
  117. The fundamental goal of POSTGRES [STON86, WENS88] is to 
  118. provide support for such three dimensional
  119. applications.  To the best of our knowledge
  120. it is the first 
  121. three
  122. dimensional
  123. data manager.
  124. However, we expect that most DBMSs will 
  125. follow the lead of POSTGRES into these new dimensions.
  126. .pp
  127. To accomplish this objective, object and rule management
  128. capabilities were added to the services found in a traditional data manager.
  129. In the next two sections we describe the capabilities provided and comment
  130. on our implementation decisions.
  131. Then, in Section 4 we discuss the novel
  132. .b no-overwrite
  133. storage manager that we implemented in POSTGRES.
  134. Other papers have explained 
  135. the
  136. major
  137. POSTGRES design decisions in these areas, and we assume that the reader is
  138. familiar with [ROWE87] on the data model, [STON88] on
  139. rule management, and [STON87] on storage management.  Hence, in these
  140. three sections 
  141. we stress considerations that led to our design,
  142. what we liked about the design, and the mistakes that we felt 
  143. we made.
  144. Where appropriate we make suggestions for future implementors based
  145. on our experience.
  146. .pp
  147. Section 5 of the paper comments on specific issues
  148. in the implementation of POSTGRES and critiques the choices that we
  149. made.
  150. In this section we discuss how we interfaced to the operating system,
  151. our choice of programming languages and some of our implementation philosophy.
  152. .pp
  153. The final section concludes with some performance measurements of
  154. POSTGRES.  Specifically, we report the results of 
  155. some of the queries in the Wisconsin
  156. benchmark [BITT83].
  157. .sh 1  "THE POSTGRES DATA MODEL AND QUERY LANGUAGE"
  158. .sh 2  "Introduction"
  159. .pp
  160. Traditional relational DBMSs support a data model consisting
  161. of a collection of named relations, each attribute of which has
  162. a specific type. In current commercial systems possible 
  163. types are floating
  164. point numbers, integers, character strings, and dates.  It is commonly
  165. recognized that this data model is insufficient for non-business
  166. data processing applications.
  167. In designing a new data model and query language, we were guided by the
  168. following three design criteria. 
  169.  
  170. 1) orientation toward data base access from a query language
  171. .pp
  172. We expect POSTGRES users to interact with their data bases primarily by using
  173. the set-oriented query language, POSTQUEL.  Hence, inclusion of a query
  174. language, an optimizer and the corresponding run-time system was
  175. a primary design goal.  
  176. .pp
  177. It is also possible to interact with a POSTGRES data base
  178. by utilizing a navigational interface.  Such interfaces were
  179. popularized by the CODASYL proposals of the 1970's and are
  180. enjoying a renaissance in recent object-oriented proposals
  181. such as ORION [BANE87] or O2 [VELE89]. Because POSTGRES 
  182. gives each record a unique 
  183. identifier (OID), it is possible to use the identifier for one record as a data
  184. item in a second record.  Using optionally definable indexes on OIDs, it
  185. is then possible to navigate from one record to the 
  186. next by running one query per navigation
  187. step.  
  188. In addition, POSTGRES allows a 
  189. user to define functions (methods) to the DBMS.  Such functions
  190. can intersperce statements in a programming language, query language
  191. commands, and direct calls to internal POSTGRES interfaces.
  192. The ability to directly execute
  193. functions which we call  
  194. .b fast 
  195. .b path
  196. is provided in POSTGRES and
  197. allows a user to navigate the data base by executing a sequence of
  198. functions.
  199. .pp
  200. However,
  201. we do not expect this sort of mechanism to become popular.  All
  202. navigational interfaces have the same disadvantages of CODASYL systems,
  203. namely the application programmer must construct a query plan for each
  204. task he wants to accomplish and substantial application maintenance
  205. is required whenever the schema changes.
  206.  
  207. 2) Orientation toward multi-lingual access 
  208. .pp
  209. We could have picked our favorite programming language
  210. and then tightly coupled POSTGRES to the compiler and run-time
  211. environment of that language.  Such an approach would
  212. offer
  213. .b persistence
  214. for variables in this programming language, as well as a query
  215. language integrated with the control statements of the language. 
  216. This approach has been followed in
  217. ODE [AGRA89] and many of the recent commercial start-ups doing object-oriented
  218. data bases.  
  219. .pp
  220. Our point of view is that most data bases are accessed
  221. by programs written in several different languages, and we 
  222. do not see any
  223. programming language Esperanto on the horizon. 
  224. Therefore, most application
  225. development organizations are 
  226. .b multi-lingual
  227. and require access to a data base from different languages.  In addition,
  228. data base application packages that a user might acquire, for example
  229. to perform statistical or spreadsheet services,
  230. are often not coded in the language being used for developing applications.
  231. Again, this results in a multi-lingual environment.
  232. .pp
  233. Hence, POSTGRES is programming language
  234. .b neutral,
  235. that is, it can be called
  236. from many different languages.
  237. Tight integration of POSTGRES to a particular
  238. language requires compiler extensions and a run time system specific
  239. to that programming
  240. language.
  241. One of us has built an implementation of persistent CLOS (Common LISP Object System)
  242. on top of POSTGRES.  
  243. Persistent CLOS (or persistent X for any programming language, X)
  244. is inevitably language specific.  The run-time system must map
  245. the disk representation for language objects, including pointers,
  246. into the main memory representation expected by the language.  Moreover,
  247. an object cache must be maintained in the program address space, 
  248. or performance will suffer badly.  Both tasks are inherently 
  249. language specific.
  250. .pp
  251. We expect many language specific interfaces to be built for POSTGRES and
  252. believe that the query language plus the 
  253. .b fast 
  254. .b path 
  255. interface available
  256. in POSTGRES offers
  257. a powerful, convenient abstraction against which to build
  258. these programming language interfaces.
  259.  
  260. 3) small number of concepts
  261. .pp
  262. We tried to build a data model with as few concepts as possible.  The
  263. relational model succeeded in replacing previous data models in 
  264. part because of its simplicity.  We wanted to have as few concepts as possible 
  265. so that users would have minimum complexity to contend with.
  266. Hence, POSTGRES leverages the following three constructs:
  267. .(l
  268. types
  269. functions
  270. inheritance
  271. .)l
  272. In the next subsection we briefly review the POSTGRES data model.  Then, we turn to
  273. a short description of POSTQUEL and fast path.  We conclude the section with a
  274. discussion of whether POSTGRES is object-oriented followed by a critique of our data model and
  275. query language.
  276. .sh 2  "The POSTGRES Data Model"
  277. .pp
  278. As mentioned in the previous section POSTGRES leverages 
  279. .b types
  280. and
  281. .b functions
  282. as fundamental constructs.  There are three kinds of types
  283. in POSTGRES and three kinds of functions and we discuss the six possibilities 
  284. in this section.
  285. .pp
  286. Some researchers, e.g.
  287. [STON86b, OSBO86], have argued that one should be able
  288. to construct new 
  289. .b base 
  290. .b types 
  291. such as
  292. bits, bitstrings, 
  293. encoded character strings, bitmaps, 
  294. compressed integers, packed decimal numbers, radix 50
  295. decimal numbers, money, etc.
  296. Unlike most next generation DBMSs which have a hard-wired collection
  297. of 
  298. base
  299. types (typically integers, floats and character strings), POSTGRES
  300. contains an 
  301. .b abstract 
  302. .b data 
  303. .b type 
  304. facility whereby any user
  305. can construct
  306. an arbitrary number of new base types.  
  307. Such types can be added to the system while it is executing
  308. and require the defining user to
  309. specify functions to convert instances of the type to and from the character string
  310. data type.  Details of the syntax appear in [WENS88].  
  311. .pp
  312. The second kind of type available in POSTGRES is a 
  313. .b constructed
  314. .b type.**  
  315. .(f
  316. ** In this section the reader can use the words
  317. .b constructed
  318. .b type,
  319. .b relation, 
  320. and
  321. .b class
  322. interchangeably.  Moreover, the words
  323. .b record,
  324. .b instance,
  325. and
  326. .b tuple
  327. are similarly interchangeable.  
  328. This section has been purposely written with
  329. the chosen notation to illustrate a point about
  330. object-oriented data bases which is discussed in Section 2.5.
  331. .)f
  332. A user can create a new type by constructing a 
  333. .b record 
  334. of
  335. base types and instances of other constructed types.  For example:
  336. .(l
  337. create DEPT (dname = c10, floor = integer, floorspace = polygon)
  338. create EMP (name = c12, dept = DEPT, salary = float)
  339. .)l
  340. Here, DEPT is a type constructed from an instance of each of three base types, a character string,
  341. an integer and a polygon.  EMP, on the
  342. other hand, is fabricated from base types and other constructed types.
  343. .pp
  344. A constructed type can optionally 
  345. .b inherit
  346. data elements from other constructed types.  For example, a SALESMAN type
  347. can be created as follows:
  348. .(l
  349. create SALESMAN (quota = float) inherits EMP
  350. .)l
  351. In this case, an instance of SALESMAN has a quota and inherits all data elements from EMP, namely name, dept and salary.
  352. We had the standard discussion about whether to include single
  353. or multiple inheritance and concluded that a single inheritance scheme would simply
  354. be too restrictive.  As a result POSTGRES allows a constructed type to inherit from an arbitrary 
  355. collection of other constructed types.
  356. .pp
  357. When ambiguities arise because an object has multiple
  358. parents with the same field name, we elected
  359. to refuse to create the new type.  However, we
  360. isolated the resolution semantics in a single routine, 
  361. which can be easily changed to track multiple inheritance semantics as they 
  362. unfold over time in programming languages.  
  363. .pp
  364. We now turn to the POSTGRES notion of functions.  There are three different 
  365. classes of POSTGRES functions,
  366. .(l
  367. normal functions
  368. operators
  369. POSTQUEL functions
  370. .)l
  371. and we discuss each in turn.
  372. .pp
  373. A user can define an arbitrary collection of
  374. .b normal
  375. .b functions
  376. whose operands are base types or constructed types.  For example, 
  377. he can define
  378. a function, 
  379. area,
  380. which maps an instance of a 
  381. polygon into an instance of a floating point number.  
  382. Such functions are automatically available in the query language 
  383. as illustrated in the following example:
  384. .(l
  385. retrieve (DEPT.dname) where area (DEPT.floorspace) > 500
  386. .)l
  387. Normal functions can be defined to POSTGRES while the system
  388. is running and are dynamically loaded
  389. when required during query execution.
  390. .pp
  391. Functions are allowed on constructed types, e.g:
  392. .(l
  393. retrieve (EMP.name) where overpaid (EMP)
  394. .)l
  395. In this case overpaid has an operand of type EMP and returns a boolean.
  396. Functions whose operands are constructed types are inherited down the type hierarchy
  397. in the standard way. 
  398. .pp
  399. Normal functions are arbitrary procedures written in a general purpose programming
  400. language (in our case C or LISP).  Hence, they have arbitrary semantics and can 
  401. run other POSTQUEL commands
  402. during execution.  Therefore, queries with normal
  403. functions in the qualification
  404. cannot be optimized by the POSTGRES query optimizer.  For example, the above
  405. query on overpaid employees will
  406. result in a sequential
  407. scan of all employees.
  408. .pp
  409. To utilize indexes in processing queries, POSTGRES
  410. supports a second class of functions, called 
  411. .b operators.
  412. Operators are functions with one or two operands which use the standard operator
  413. notation in the query language. For example the following query looks for
  414. departments whose floor space has a greater area than that of a specific polygon:
  415. .(l
  416. retrieve (DEPT.dname) where DEPT.floorspace AGT polygon["(0,0), (1,1), (0,2)"]
  417. .)l
  418. The "area greater than" operator AGT is defined by indicating the token to use in the query language 
  419. as well as the function to call to evaluate the operator.  Moreover, several
  420. .b hints
  421. can also be included in the definition which assist the query optimizer.  One of these
  422. hints is that ALE is the negator of this operator.  Therefore, the query optimizer
  423. can transform the query: 
  424. .(l
  425. retrieve (DEPT.dname) where not (DEPT.floorspace ALE polygon["(0,0), (1,1), (0,2)"])
  426. .)l
  427. which cannot be optimized into the one above which can be.
  428. In addition, the design of the POSTGRES access methods allows a B+-tree index to 
  429. be constructed for the instances of 
  430. floorspace appearing in DEPT records.  This index can support efficient access for the
  431. .b class
  432. of operators {ALT, ALE, AE, AGT, AGE}.  Information on the access paths
  433. available to the various operators is recorded in the POSTGRES system
  434. catalogs.
  435. .pp
  436. As pointed out in [STON87b] it is imperative that a user be able
  437. to construct new access methods to provide efficient access to instances of non-traditional
  438. base types. For example, suppose a user introduces a new operator "!!" defined on
  439. polygons that returns true if two polygons overlap.  Then, he might ask a query such as:
  440. .(l
  441. retrieve (DEPT.dname) where DEPT.floorspace !! polygon["(0,0), (1,1), (0,2)"]
  442. .)l
  443. There is no B+-tree or hash access method that will allow this query
  444. to be rapidly executed.  Rather, the query must be supported by some
  445. multidimensional access method such as R-trees, grid files, K-D-B trees, etc.  Hence,
  446. POSTGRES was designed to allow new access methods to be written by POSTGRES users and then dynamically
  447. added to the system.  Basically, an access method to POSTGRES is a collection
  448. of 13 normal functions which perform record level 
  449. operations such as fetching the next record in a scan, inserting
  450. a new record, deleting a specific record, etc.
  451. All a user need do is define implementations for each of these 
  452. functions and make a collection of entries in the system catalogs.
  453. .pp
  454. Operators are only available for operands which are base types because 
  455. access methods traditionally support fast access to specific fields in records.
  456. It is unclear what an access method for a constructed type should do, and therefore
  457. POSTGRES does not include this capability.
  458. .pp
  459. The third kind of function available in POSTGRES is
  460. .b POSTQUEL 
  461. .b functions.
  462. Any collection of commands in the POSTQUEL query language can be packaged together
  463. and defined as a function.  For example, the following function defines
  464. the overpaid employees:
  465. .(l
  466. define function high-pay as retrieve (EMP.all) where EMP.salary > 50000
  467. .)l
  468. POSTQUEL functions can also have parameters, for example:
  469. .(l
  470. define function ret-sal as retrieve (EMP.salary) where EMP.name = $1
  471. .)l
  472. Notice that 
  473. ret-sal has one parameter in the body of the function, the name
  474. of the person involved. 
  475. Such parameters must be provided at the time the function is called.
  476. A third example POSTQUEL function is:
  477. .(l
  478. define function set-of-DEPT as retrieve (DEPT.all) where DEPT.floor = $.floor
  479. .)l
  480. This function has a single parameter "$.floor".  It is
  481. expected to appear in a record and receives the value of its parameter from 
  482. the floor field defined elsewhere in the same record.
  483. .pp
  484. Each POSTQUEL function is automatically 
  485. a constructed type.  For example, one can define a FLOORS type as follows:
  486. .(l
  487. create FLOORS (floor = i2, depts = set-of-DEPT)  
  488. .)l
  489. This constructed type uses the set-of-DEPT function as a constructed type.  In this
  490. case, each instance of FLOORS has a value for depts which is the 
  491. value of the function set-of-DEPT for that record.
  492. .pp
  493. In addition, POSTGRES allows a user to form a constructed type, one or
  494. more of whose fields has the special type POSTQUEL.  
  495. For example, a user can
  496. construct the following type:
  497. .(l
  498. create PERSON (name = c12, hobbies = POSTQUEL)
  499. .)l
  500. In this case, each instance of hobbies contains a different 
  501. POSTQUEL function, and therefore each person
  502. has a name and a POSTQUEL function that defines his particular hobbies.  This
  503. support for POSTQUEL as a type allows the system to simulate 
  504. non-normalized
  505. relations as found in NF**2 [DADA86].
  506. .pp
  507. POSTQUEL functions can appear in the query language in the same manner as
  508. normal functions.  The following example ensures that Joe has
  509. the same salary as Sam:
  510. .(l
  511. replace EMP (salary = ret-sal("Joe")) where EMP.name = "Sam"
  512. .)l
  513. .pp
  514. In addition, since POSTQUEL functions are a constructed type,
  515. queries can be executed against POSTQUEL functions just like other
  516. constructed types.  For example,
  517. the following query can be run on the constructed type, high-pay:
  518. .(l
  519. retrieve (high-pay.salary) where high-pay.name = "george"
  520. .)l
  521. If a POSTQUEL function contains a single retrieve command, then 
  522. it is very similar to a relational view definition, and this
  523. capability
  524. allows retrieval operations to be performed on 
  525. objects which are essentially relational views.
  526. .pp
  527. Lastly, every time a user defines a constructed type, a POSTQUEL function is
  528. automatically defined with the same name.  For example, when DEPT is constructed,
  529. the following function is automatically defined:
  530. .(l
  531. define function DEPT as retrieve (DEPT.all) where DEPT.OID = $1
  532. .)l
  533. When EMP was defined earlier in this section, it contained
  534. a field dept which was of type DEPT.  In fact, DEPT
  535. was the above automatically defined POSTQUEL function.
  536. As a result, instance of a constructed type is available as a type because
  537. POSTGRES automatically defines a POSTQUEL function for each such type.
  538. .pp
  539. POSTQUEL functions are a very powerful notion because they allow 
  540. arbitrary collections of instances of types to be returned as the value of the
  541. function.  Since POSTQUEL functions can reference other POSTQUEL functions, 
  542. arbitrary structures of complex objects can be assembled.
  543. Lastly, POSTQUEL functions allow collections of commands 
  544. such as the 5 SQL commands that make up
  545. TP1 [ANON85] to be assembled into a 
  546. single function and stored inside the DBMS.  
  547. Then, one can execute TP1 by executing the single function.  This 
  548. approach is preferred
  549. to having to
  550. submit the 5 SQL commands in TP1 one by one from an application program.
  551. Using a POSTQUEL function, one replaces 5 round trips between the application
  552. and the DBMS with 1, which results in a 25% performance improvement in a 
  553. typical OLTP application.  
  554. .sh 2  "The POSTGRES Query Language"
  555. .pp
  556. The previous
  557. section presented several 
  558. examples of the POSTQUEL language.
  559. It
  560. is a set oriented query language that resembles a superset of a relational query language.
  561. Besides user defined functions and operators which were
  562. illustrated earlier, the features which have been added to a traditional relational language include:
  563. .(l
  564. path expressions
  565. support for nested queries
  566. transitive closure
  567. support for inheritance
  568. support for time travel
  569. .)l
  570. .pp
  571. Path expressions are included because
  572. POSTQUEL allows constructed types which contain other
  573. constructed types to be hierarchically referenced.  For example, the EMP type
  574. defined above contains a field which is an instance of the constructed type, DEPT.  Hence, one
  575. can ask for the names of employees who work on the first floor as follows:
  576. .(l
  577. retrieve (EMP.name) where EMP.dept.floor = 1
  578. .)l
  579. rather than being forced to do a join, e.g:
  580. .(l
  581. retrieve (EMP.name) where EMP.dept = DEPT.OID and DEPT.floor = 1
  582. .)l
  583. .pp
  584. POSTQUEL also allows queries to be nested and has operators
  585. that have sets of instances as operands.  For example, to find the departments
  586. which occupy an entire floor, one would query:
  587. .(l
  588. retrieve (DEPT.dname) 
  589. where DEPT.floor NOTIN {D.floor from D in DEPT where D.dname != DEPT.dname}
  590. .)l
  591. In this case, the expression inside the curly braces represents a set of instances and
  592. NOTIN is an operator which takes a set of instances as its right operand.
  593. .pp
  594. The transitive closure operation allows one to explode a parts or ancestor hierarchy.  Consider
  595. for example the constructed type:
  596. .(l
  597. parent (older, younger)
  598. .)l
  599. One can ask for all the ancestors of John as follows:
  600. .(l
  601. retrieve* into answer (parent.older)
  602. using a in answer
  603. where parent.younger = "John" 
  604. or parent.younger = a.older
  605. .)l
  606. In this case the * after retrieve indicates that the associated query should be run until
  607. answer fails to grow.
  608. .pp
  609. If one wishes to find the names of all employees over 40, one would
  610. write:
  611. .(l
  612. retrieve (E.name) using E in EMP 
  613. where E.age > 40
  614. .)l
  615. On the other hand, if one wanted the names of all salesmen or employees
  616. over 40, the notation is:
  617. .(l
  618. retrieve (E.name) using E in EMP*
  619. where E.age > 40
  620. .)l
  621. Here the * after the constructed type EMP indicates
  622. that the query should be run over EMP and all constructed types 
  623. under EMP in the inheritance hierarchy.
  624. This use of * allows a user to easily run queries
  625. over a constructed 
  626. type and all its descendents.
  627. .pp
  628. Lastly, POSTGRES supports the notion of
  629. .b time
  630. .b travel.
  631. This feature allows a user to run historical queries.  For example to find the salary
  632. of Sam at time T one would query:
  633. .(l
  634. retrieve (EMP.salary)
  635. using EMP [T]
  636. where EMP.name = "Sam"
  637. .)l
  638. POSTGRES will automatically find the version of Sam's record valid at
  639. the correct time and get the appropriate salary.
  640. .pp
  641. Like relational systems, the result of a POSTQUEL command 
  642. can be added to the data base as a new constructed type.  In this case, POSTQUEL follows the
  643. lead of relational systems by removing duplicate records from the result.  The user who is
  644. interested in retaining
  645. duplicates can do so by ensuring that the OID field of some instance is included in the
  646. target list being selected.  
  647. For a full description of POSTQUEL the interested reader should consult [WENS88].  
  648. .sh 2  "Fast Path"
  649. .pp
  650. There are three reasons why we chose to implement a 
  651. .b fast 
  652. .b path
  653. feature.
  654. First, a user who wishes to interact with a data base by executing a
  655. sequence of functions to navigate to desired data can use fast path to accomplish
  656. his objective.  
  657. Second, there are a variety of decision support
  658. applications in which the
  659. end user is given a specialized query language.  In such environments,
  660. it is often easier for the application developer to construct a 
  661. parse tree representation for a query rather than an ASCII
  662. one.  Hence, it would be desirable for the application
  663. designer to be able to directly call the POSTGRES optimizer or
  664. executor.
  665. Most DBMSs do not allow direct access to internal system modules.
  666. .pp
  667. The third reason is a bit more complex.  In the persistent CLOS 
  668. layer of PICASSO, it is necessary for the run time system to assign a unique 
  669. identifier (OID) to every constructed object that is persistent.  It is
  670. undesirable for the system to synchronously insert each object directly
  671. into a POSTGRES data base and thereby assign a POSTGRES identifier to
  672. the object.  This would result in poor performance in executing a
  673. persistent CLOS program.  Rather, persistent CLOS maintains a cache of
  674. objects in the address space of the program and only inserts a persistent
  675. object into this cache synchronously.  There are several options
  676. which control how the cache is written out to the data base at a later time. 
  677. Unfortunately, it is essential that a persistent object be assigned a unique 
  678. identifier at the time it enters the cache, because other objects may
  679. have to point 
  680. to the newly created object and use its OID to do so.
  681. .pp
  682. If persistent CLOS assigns unique identifiers, then there will be a complex
  683. mapping that must be performed when objects are written out to the data
  684. base and real POSTGRES unique identifiers are assigned.  Alternately,
  685. persistent CLOS must maintain its own system for unique identifiers,
  686. independent of the POSTGRES one, an obvious duplication of effort.
  687. The solution chosen was to allow persistent CLOS to access the POSTGRES
  688. routine that assigns unique identifiers and allow it to preassign
  689. N POSTGRES object identifiers which it can subsequently assign to cached
  690. objects.  At a later time, these objects can be written to a POSTGRES
  691. data base using the preassigned unique identifiers.  When the supply of
  692. identifiers is exhausted, persistent CLOS can request another collection.
  693. .pp
  694. In all of these examples, an application program requires direct
  695. access to a user-defined or internal POSTGRES function, and therefore the POSTGRES
  696. query
  697. language has been extended with:
  698. .(l
  699. function-name (param-list)
  700. .)l
  701. In this case, besides running queries in POSTQUEL, a user can
  702. ask that any function known to POSTGRES be executed.  
  703. This function can be one
  704. that a user has previously 
  705. defined as a normal, operator, or POSTQUEL function 
  706. or it can be one that is included in the POSTGRES implementation.
  707. .pp
  708. Hence, the user can directly call the parser, the optimizer, the
  709. executor, the access methods, the buffer manager or the utility routines.
  710. In addition he can define functions which in turn make calls on POSTGRES
  711. internals.  In this way, he can have considerable
  712. control over the low level flow of control, much as is 
  713. available
  714. through a DBMS toolkit such as Exodus [RICH87], but without 
  715. all the effort involved in configuring
  716. a tailored DBMS from the toolkit.
  717. Moreover, should the user wish to interact with his data base
  718. by making a collection of function calls (method invocations), this
  719. facility allows the possibility.  As noted in the introduction, we do not expect this
  720. interface to be especially popular.
  721. .pp
  722. The above capability
  723. is called
  724. .b fast
  725. .b path
  726. because it provided direct access to specific functions 
  727. without checking the validity of parameters.  As such, it is effectively
  728. a remote procedure call facility and allows a user program to
  729. call a function in another address space rather than in its own
  730. address space.  
  731. .sh 2  "Is POSTGRES Object-oriented?"
  732. .pp
  733. There have been many next generation data models proposed in the last few years.
  734. Some are characterized by the
  735. term "extended relational", others are considered "object-oriented" while
  736. yet others are termed "nested relational".
  737. POSTGRES could be accurately described as an object-oriented system because it
  738. includes unique identity for objects, abstract data types, classes (constructed types),
  739. methods (functions), and inheritance for both data and functions.  
  740. Others (e.g. [ATKI89]) are suggesting definitions 
  741. for the word "object-oriented", and POSTGRES satisfies
  742. virtually all of the proposed litmus tests.
  743. .pp
  744. On the other hand, POSTGRES could also be considered an extended relational system.  As noted in 
  745. a previous footnote, Section
  746. 2 could have been equally well written with the word "constructed type" and
  747. "instance" replaced by the words "relation" and "tuple".
  748. In fact, in previous descriptions of POSTGRES [STON86], this notation
  749. was employed.  Hence, others, e.g. [MAIE89] have characterized POSTGRES as
  750. an extended relational system.  
  751. .pp
  752. Lastly, POSTGRES supports the POSTQUEL type, which is
  753. exactly a nested relational structure.
  754. Consequently, POSTGRES could be classified as a nested relational system as well.
  755. .pp
  756. As a result POSTGRES could be described using any of the three adjectives above.  In our opinion 
  757. we can interchangeably use the words 
  758. .b relations,
  759. .b classes,
  760. and 
  761. .b constructed 
  762. .b types 
  763. in describing POSTGRES.
  764. Moreover, we can also interchangeably use the words
  765. .b function
  766. and 
  767. .b method.
  768. Lastly, we can interchangeably use the words
  769. .b instance,
  770. .b record,
  771. and
  772. .b tuple.
  773. Hence, POSTGRES seems to be either object-oriented or not
  774. object-oriented, depending on the choice of a few tokens in the parser.  As a 
  775. result, we feel that most
  776. of the efforts to classify the extended data models in next generation data base systems
  777. are silly exercises in surface syntax.  
  778. .pp
  779. In the remainder of this section, we comment briefly on the POSTGRES implementation of
  780. OIDs and inheritance. 
  781. POSTGRES gives each record a unique identifier (OID), and then allows the
  782. application designer to decide for each constructed type whether he
  783. wishes to have an index on the OID field.
  784. This decision should be contrasted with most object-oriented systems
  785. which construct an OID index for all constructed types in the system automatically. 
  786. The POSTGRES scheme allows the cost of the index to be 
  787. paid only for those types of objects
  788. for which it is profitable.  In our opinion, this flexibility has been
  789. an excellent decision.
  790. .pp
  791. Second, there are several possible ways to implement an inheritance
  792. hierarchy.  Considering the SALESMEN and 
  793. EMP example noted earlier, one can store
  794. instances of SALEMAN by storing them as EMP records and then
  795. only storing the extra quota information in 
  796. a separate SALESMAN record.  Alternately, one
  797. can store no information on each salesman in EMP and then store complete
  798. SALESMAN records elsewhere.  Clearly, there are 
  799. a variety of additional schemes.
  800. .pp
  801. POSTGRES chose one implementation, namely storing all SALESMAN fields
  802. in a single record.  However, it is likely that 
  803. applications designers will demand several 
  804. other representations to give them the 
  805. flexibility to optimize their particular
  806. data.
  807. Future implementations of inheritance will likely require several
  808. storage options.
  809. .sh 2  "A Critique of the POSTGRES Data Model"
  810. .pp
  811. There are five areas where we feel we made mistakes in
  812. the POSTGRES data model:
  813. .(l
  814. union types
  815. access method interface
  816. functions
  817. big objects
  818. arrays
  819. .)l
  820. We discuss each in turn.
  821. .pp
  822. A desirable feature in any next-generation DBMS would be to support
  823. union types, i.e. an instance of a type 
  824. can be an 
  825. instance of one of several given types.
  826. A persuasive example (similar to one from [COPE84]) is that employees
  827. can be on loan to another plant or on loan to a customer.  If two base types, customer
  828. and plant exist, one would 
  829. like to change the EMP type to:
  830. .(l
  831. create EMP (name = c12, dept = DEPT, salary = float, on-loan-to = plant or customer)  
  832. .)l
  833. Unfortunately including union types makes a query optimizer more complex.  For example, to
  834. find all the employees on loan to the same organization one would state the query:
  835. .(l
  836. retrieve (EMP.name, E.name)
  837. using E in EMP 
  838. where EMP.on-loan-to = E.on-loan-to
  839. .)l
  840. However, the optimizer must construct two different plans, one for employees on loan to
  841. a customer and one for employees on loan to a different plant.  The reason
  842. for two plans is that the equality operator may be different for the 
  843. two types.
  844. In addition, one must construct indexes on union 
  845. fields, which entails substantial complexity in the access methods.
  846. .pp
  847. Union types are 
  848. .b highly
  849. desirable in certain applications, and  
  850. we considered three possible
  851. stances with respect to union types:
  852. .(l
  853. 1) support only through abstract data types
  854. 2) support through POSTQUEL functions
  855. 3) full support
  856. .)l
  857. Union types can be easily constructed using the POSTGRES abstract
  858. data type facility.  If a user wants a specific union type, he
  859. can construct it and then write appropriate operators and functions for 
  860. the type.  The implementation complexity of union types is thus forced into
  861. the routines for the operators and functions and onto
  862. the implementor of the type.  Moreover,
  863. it is clear that there
  864. are a vast number of union types and
  865. an extensive type library must be constructed by the application designer.
  866. The PICASSO team 
  867. stated that this approach placed an unacceptably difficult burden on them,
  868. and therefore position 1 was rejected. 
  869. .pp
  870. Position
  871. 2 offers some support for union types but has problems.  Consider the
  872. example of employees and their hobbies from [STON86]:
  873. .(l
  874. create EMP (name = c12, hobbies = POSTQUEL)
  875. .)l
  876. Here the hobbies field is a POSTQUEL function, one per employee, which retrieves
  877. all hobby information about that particular employee.  Now consider the
  878. following POSTQUEL query:
  879. .(l
  880. retrieve (EMP.hobbies.average) where EMP.name = "Fred"
  881. .)l
  882. In this case the field average for each hobby record will
  883. be returned whenever it is defined.  Suppose, however, that
  884. average is a float for the softball hobby and an integer for
  885. the cricket hobby.  In this case, the application program
  886. must be prepared to accept values of different types.  
  887. .pp
  888. The more
  889. difficult problem is the following legal POSTQUEL query:
  890. .(l
  891. retrieve into TEMP (result = EMP.hobbies.average) where EMP.name = "Fred"
  892. .)l
  893. In this case, a problem arises concerning the type of the result field, because
  894. it is a union type.
  895. Hence, adopting
  896. position 2 leaves one in an awkward position 
  897. of not having a reasonable type for the result of the above
  898. query.
  899. .pp
  900. Of course, position 3 requires extending the indexing and query optimization
  901. routines to deal with union types.  
  902. Our solution 
  903. was to adopt position 2 and to add an abstract data type, ANY, which 
  904. can hold an instance of
  905. any type.  This solution which turns the type 
  906. of the result of the above query from
  907. .(l
  908. one-of {integer, float}
  909. .)l
  910. into ANY is not very satisfying.  Not only is information lost, but
  911. we are also forced to include with POSTGRES this universal type.  
  912. .pp
  913. In our opinion, the only realistic alternative is to adopt position
  914. 3, swallow the complexity increase, and that is what we would do in any next system.  
  915. .pp
  916. Another failure concerned the access method design and was 
  917. the decision to support indexing only on the value of a field and
  918. not on a function of
  919. a value.  The utility of indexes on functions 
  920. of values is discussed in [LYNC88], and the capability was
  921. retrofitted, rather inelegantly, into one version of POSTGRES [AOKI89].
  922. .pp
  923. Another comment on the access method design concerns extendibility.
  924. Because
  925. a user can add new base types
  926. dynamically, it is essential that he also
  927. be able to add new access methods to POSTGRES
  928. if the system does not come with an access method 
  929. that supports efficient access
  930. to his types.  The standard example of this capability is the use
  931. of R-trees [GUTM84] to speed access to geometric objects.  
  932. We have now designed and/or coded three access methods
  933. for POSTGRES in addition to B+-trees.  Our experience has 
  934. consistently been that
  935. adding an access method is 
  936. .b VERY 
  937. .b HARD.  
  938. There are four
  939. problems that complicate the situation.  First, the access method must
  940. include explicit calls to the POSTGRES locking subsystem to set and
  941. release locks on access method objects.  Hence, the designer of a new
  942. access method must understand locking and how to use the particular
  943. POSTGRES facilities.  Second, the designer must understand how to
  944. interface to the buffer manager and be able to get, put, pin and unpin
  945. pages.  Next, the POSTGRES execution engine contains the ``state'' of the
  946. execution of any query and the access methods must understand
  947. portions of this state and the data structures involved.  
  948. Last but not least, the
  949. designer must write 13 non-trivial routines.  Our experience so far is that
  950. novice programmers can add new types to POSTGRES; however, it requires
  951. a highly skilled programmer to add a new access method.  Put differently, the
  952. manual on how to add new data types to POSTGRES is 2 pages long, the one for
  953. access methods is 50 pages.
  954. .pp
  955. We failed to realize the difficulty of access method construction.  Hence,
  956. we designed a system that allows end users to add access methods dynamically to
  957. a running system.  However, access methods will be built by sophisticated
  958. system programmers who could have used a simpler to build interface.
  959. .pp
  960. A third area where our design is flawed
  961. concerns POSTGRES support for POSTQUEL functions.  
  962. Currently,
  963. such functions in POSTGRES are collections of commands in the query
  964. language POSTQUEL.  If one defined budget in DEPT as a 
  965. POSTQUEL function, then the value for the shoe department budget might be the following
  966. command:
  967. .(l
  968. retrieve (DEPT.budget) where DEPT.dname = "candy"
  969. .)l
  970. In this case, the shoe department will automatically be 
  971. assigned the same budget as the candy department.  However,
  972. it is impossible for the budget of the shoe department to be specified as:
  973. .(l
  974. if floor = 1  then
  975.    retrieve (DEPT.budget) where DEPT.dname = "candy"
  976. else
  977.    retrieve (DEPT.budget) where DEPT.dname = "toy"
  978. .)l
  979. This specification defines the budget of the shoe department to be 
  980. the candy department budget if it is on the first floor.
  981. Otherwise, it is the same as the toy department.
  982. This query is not possible because POSTQUEL has no
  983. conditional expressions.  We had 
  984. .b extensive 
  985. discussions 
  986. about this and other extensions to POSTQUEL.  Each such extension was
  987. rejected because it seemed to turn POSTQUEL into a programming
  988. language and not a query language.
  989. .pp
  990. A better solution would be
  991. be to allow 
  992. a POSTQUEL function to be expressible in a general purpose
  993. programming language enhanced with POSTQUEL queries.  Hence,
  994. there would be no distinction between normal functions and POSTQUEL
  995. functions. 
  996. Put differently, normal functions would be able to be constructed types
  997. and would support path expressions.
  998. .pp
  999. There are three problems with this approach.
  1000. First, path expressions for normal functions
  1001. cannot be optimized by the POSTGRES query
  1002. optimizer because they have arbitrary semantics.  Hence, most
  1003. of the optimizations planned for POSTQUEL 
  1004. functions would have to be discarded.
  1005. Second, POSTQUEL functions are much easier to define than normal functions
  1006. because a user need not know a general purpose programming language.  Also, he need
  1007. not specify the types of the function arguments or the return type because POSTGRES 
  1008. can figure these out from the query specification.
  1009. Hence, we would have to give up ease of definition in moving from
  1010. POSTQUEL functions to normal functions.
  1011. Lastly,, normal functions have a protection problem 
  1012. because they can do arbitrary things, such as zeroing
  1013. the data base.  POSTGRES
  1014. deals with this problem 
  1015. by calling normal functions in two ways:
  1016. .(l
  1017. trusted -- loaded into the POSTGRES address space
  1018. untrusted -- loaded into a separate address space
  1019. .)l
  1020. Hence, normal functions are either called quickly with no security or
  1021. slowly in a protected fashion.  No such security problem arises with
  1022. POSTQUEL functions.
  1023. .pp
  1024. An better approach might have been to support POSTQUEL functions
  1025. written in the 4th generation language (4GL) 
  1026. being designed for PICASSO [ROWE89].  This programming
  1027. system leaves type information in the system catalogs.  Consequently,
  1028. there would be no need for a separate registrations step to
  1029. indicate type information to POSTGRES.  Moreover, a
  1030. processor for the language is available for integration in POSTGRES.
  1031. It is also easy to make a 4GL "safe", 
  1032. i.e. unable to perform wild branches or malicious actions 
  1033. Hence, there would be no security problem.  
  1034. Also, it seems possible that path expressions could be optimized for 4GL
  1035. functions.  
  1036. .pp
  1037. Current
  1038. commercial relational products seem to be moving in this
  1039. direction by allowing data base
  1040. procedures to be coded in their proprietary 4th generation languages (4GLs).
  1041. In retrospect we probably should have looked seriously at designing POSTGRES to support
  1042. functions written in a 4GL. 
  1043. .pp
  1044. Next, POSTGRES allows types to be constructed that are of arbitrary size.
  1045. Hence,
  1046. large bitmaps are a perfectly acceptable POSTGRES data type.  However,
  1047. the current POSTGRES user interface (portals) allows a user to fetch one or
  1048. more instances of a constructed type.  It is currently impossible to fetch
  1049. only a portion of an instance.  This presents
  1050. an application program with a severe buffering problem; it must be capable
  1051. of accepting an entire instance, no matter how large it is.  We should
  1052. extend the portal syntax in a straightforward way to allow
  1053. an application to position a portal on a specific field of an instance of a constructed type
  1054. and then 
  1055. specify a byte-count that he would like to retrieve.  These changes would 
  1056. make it much easier to insert and retrieve big fields.
  1057. .pp
  1058. Lastly, we included arrays in the POSTGRES data model.  Hence, one could
  1059. have specified 
  1060. the SALESMAN type as:
  1061. .(l
  1062. create SALESMAN (name = c12, dept = DEPT, salary = float, quota = float[12])
  1063. .)l
  1064. Here, the SALESMAN has all the fields of
  1065. EMP plus a quota which is an array of 12 floats, one for each month of the year.
  1066. In fact, character strings are really an array of characters, and the correct 
  1067. notation for the above type is:
  1068. .(l
  1069. create SALESMAN (name = c[12], dept = DEPT, salary = float, quota = float[12])
  1070. .)l
  1071. In POSTGRES we support fixed and variable length arrays of base types, along
  1072. with an array notation in POSTQUEL.  For example to request all salesmen
  1073. who have an April quota over 1000, one would write:
  1074. .(l
  1075. retrieve (SALESMAN.name) where SALESMAN.quota[4] > 1000
  1076. .)l
  1077. However, we do not support arrays of constructed types; hence it is not 
  1078. possible to have an array of instances of a constructed type.
  1079. We omitted this capability only because
  1080. it would have made the query optimizer and executor somewhat harder.
  1081. In addition, there is no built-in search mechanism for the elements of an array.
  1082. For example, it is not possible to find the names of all salesmen who have
  1083. a quota over 1000 during any month of the year.  
  1084. In retrospect, we should included general support for arrays or no support at all.
  1085. .sh 1  "THE RULES SYSTEM"
  1086. .sh 2  "Introduction"
  1087. .pp
  1088. It is clear to us that all DBMSs need a rules system.  Current commercial
  1089. systems are required to support referential integrity [DATE81], which
  1090. is merely a simple-minded collection of rules.  In addition, most current
  1091. systems have special purpose rules systems to support relational views,
  1092. protection, and integrity constraints.  Lastly, a rules system allows
  1093. users to do event-driven programming as well as enforce integrity
  1094. constraints that cannot be performed in other ways.
  1095. There are three high level decisions that the POSTGRES
  1096. team had to make concerning the philosophy of rule systems.
  1097. .pp
  1098. First, a decision was required concerning how many rule syntaxes
  1099. there would be.  Some approaches, e.g. [ESWA76, WIDO89] propose
  1100. rule systems oriented toward application designers that would augment
  1101. other rule systems present for DBMS internal purposes.  Hence, 
  1102. such systems would contain several independently
  1103. functioning rules systems.  On the other hand, [STON82] proposed a rule
  1104. system that tried to support user functionality as well as needed
  1105. DBMS internal functions in a single syntax.
  1106. .pp
  1107. From the beginning, a goal of the POSTGRES rules system was to have only
  1108. one syntax.  It was felt that this would simplify the user interface, since
  1109. application designers need learn only one construct.  Also, they would not
  1110. have to deal with deciding which system to use in the cases where
  1111. a function could be performed by more than one rules system.
  1112. It was also felt that a single rules system
  1113. would ease the implementation
  1114. difficulties that would be faced.
  1115. .pp
  1116. Second, there are two implementation philosophies by which one could
  1117. support a rule system.  The first is a 
  1118. .b query 
  1119. .b rewrite 
  1120. implementation.
  1121. Here, a rule would be applied by 
  1122. converting a user query to an alternate form 
  1123. prior to execution.  This transformation is performed between the
  1124. query language parser and the optimizer.  Support for views [STON75] 
  1125. is done this way along with many of the proposals for recursive query
  1126. support [BANC86, ULLM85].  Such an implementation
  1127. will be very efficient when there are a small number of rules on any
  1128. given constructed type and most rules cover the whole constructed type.  For example,
  1129. a rule such as:
  1130. .(l
  1131. EMP [dept] contained-in DEPT[dname]
  1132. .)l
  1133. expresses the referential integrity condition that employees cannot be in a
  1134. non-existent department and 
  1135. applies to all EMP instances.
  1136. However, a query rewrite 
  1137. implementation will not work well if there are a large number of rules on 
  1138. each constructed type, each of them covering
  1139. only a few instances.  Consider, for example, the following three rules:
  1140. .(l
  1141. employees in the shoe department have a steel desk
  1142. employees over 40 have a wood desk
  1143. employees in the candy department do not have a desk
  1144. .)l
  1145. To retrieve the kind of a desk that Sam has, one must run the
  1146. following three queries:
  1147. .(l
  1148. retrieve (desk = ``steel'') where EMP.name = ``Sam'' and EMP.dept = ``shoe''
  1149. retrieve (desk = ``wood'') where EMP.name= ``Sam'' and EMP.age > 40
  1150. retrieve (desk = null) where EMP.name = ``Sam'' and EMP.dept = ``candy''
  1151. .)l
  1152. Hence, a user query must be rewritten for each rule, resulting
  1153. in a serious degradation of performance unless all
  1154. queries are processed as a group using multiple query optimization
  1155. techniques [SELL86].
  1156. .pp
  1157. Moreover, 
  1158. a query rewrite system has great difficulty with 
  1159. .b exceptions
  1160. [BORG85].  
  1161. For example consider the rule ``all employees have a
  1162. steel desk'' together with the exception ``Jones is an employee who has a 
  1163. wood desk''. 
  1164. If one ask for the kind of desk and age for all employees over 35, 
  1165. then the query must be
  1166. rewritten as the following 2 queries:
  1167. .(l
  1168. retrieve (desk = "steel", EMP.age) where EMP.age > 35 and EMP.name != "Jones"
  1169. retrieve (desk = "wood", EMP.age) where EMP.age > 35 and EMP.name = "Jones"
  1170. .)l
  1171. In general, the number of queries as well as the complexity of their
  1172. qualifications increases linearly with the number
  1173. of rules.  Again, this will result in bad performance unless 
  1174. multiple query optimization techniques are applied.
  1175. .pp
  1176. Lastly, a query rewrite system
  1177. does not offer any help in resolving situations when the rules are
  1178. violated.  For example, the above referential integrity rule is 
  1179. silent on what to
  1180. do if a user tries to insert an employee into a non-existent department.
  1181. .pp
  1182. On the other hand, one could adopt a 
  1183. .b trigger
  1184. implementation based on individual record accesses and updates
  1185. to the data base.  Whenever a record is accessed, inserted, deleted
  1186. or modified, the low level execution code has both the old record and the
  1187. new record readily available.  Hence, assorted actions can 
  1188. easily be taken by the
  1189. low level code. 
  1190. Such an implementation requires the rule firing code to be placed deep in
  1191. the query execution routines.  It 
  1192. will work well if there are many rules each affecting only a few instances,
  1193. and it is easy to deal successfully with conflict resolution at this
  1194. level.
  1195. However, rule firing is deep in the executor, and it is thereby impossible for
  1196. the query optimizer to construct an efficient execution plan for 
  1197. a chain of rules that are awakened.
  1198. .pp
  1199. Hence, this implementation complements a query rewrite scheme in
  1200. that it excels where a rewrite scheme
  1201. is weak and vica-versa.  
  1202. Since we wanted to have a single rule system, 
  1203. it was
  1204. clear that we needed to provide both styles of implementation.  
  1205. .pp
  1206. A third issue that we faced was the paradigm for the rules system.  A 
  1207. conventional production system consisting of collections
  1208. of if-then rules has been explored in the past [ESWA76, STON82],
  1209. and is a readily available alternative.  However, such a scheme lacks
  1210. expressive power.  For example, suppose one wants to enforce a rule
  1211. that Joe makes the same salary as Fred.  In this case, one must
  1212. specify two different if-then rules.  The first one indicates the
  1213. action to take if Fred receives a raise, namely to propagate the
  1214. change on to Joe.  The second rule specifies that any update
  1215. to Joe's salary must be refused.  Hence, many user rules require two or more
  1216. if-then specifications to achieve the desired effect.  
  1217. .pp
  1218. The intent in POSTGRES
  1219. was to explore a more powerful paradigm.
  1220. Basically,
  1221. any POSTGRES command can be turned into a rule by changing the semantics of the
  1222. command so that it is logically either
  1223. .b always
  1224. running or
  1225. .b never
  1226. running.  For example, Joe may be specified to have the same
  1227. salary as Fred by the rule:
  1228. .(l
  1229. always replace EMP (salary = E.salary)
  1230. using E in EMP
  1231. where EMP.name = "Fred" and E.name = "Joe"
  1232. .)l
  1233. This single specification will propagate Joe's salary
  1234. on to Fred as well as refuse direct updates to Fred's salary.  
  1235. In this way a single ``always'' rule 
  1236. replaces the two statements needed in a production
  1237. rule syntax.
  1238. .pp
  1239. Moreover, to efficiently support the 
  1240. triggering implementation where there are a large number of rules present for
  1241. a single constructed type, each of which applies to only a few
  1242. instances, the POSTGRES team designed a sophisticated 
  1243. marking scheme whereby rule wake-up information is placed on individual
  1244. instances.  Consequently, regardless of the number of rules present for
  1245. a single constructed type, only those which actually must fire will be awakened. 
  1246. This should be contrasted to proposals without such data structures, 
  1247. which will be hopelessly inefficient whenever a large 
  1248. number of rules are present for a single constructed type.
  1249. .pp
  1250. Lastly, the decision was made to support the query rewrite scheme by 
  1251. escalating markers to the constructed type level.
  1252. For example, consider the
  1253. rule:
  1254. .(l
  1255. always replace EMP (age = 40) where name != "Bill"
  1256. .)l
  1257. This rule applies to all employees except Bill and it would be 
  1258. a waste of space to
  1259. mark each individual employee.  Rather, one would prefer to set
  1260. a single marker in the system catalogs to 
  1261. cover the whole constructed type implicitly.
  1262. In this case, any query, e.g:
  1263. .(l
  1264. retrieve (EMP.age) where EMP.name = "Sam"
  1265. .)l
  1266. will be altered prior to execution by the query rewrite implementation
  1267. to:
  1268. .(l
  1269. retrieve (age = 40) where EMP.name = "Sam" and EMP.name != "Bill"
  1270. .)l
  1271. .pp
  1272. At the current time much of the POSTGRES Rules System (PRS)
  1273. as described in [STON88]
  1274. is operational, and there are three aspects of the design
  1275. which we wish to
  1276. discuss
  1277. in the next three subsections, namely:
  1278. .(l
  1279. complexity
  1280. absence of needed function and
  1281. efficiency
  1282. .)l
  1283. Then, we close with the second version of the POSTGRES Rules
  1284. system (PRS II) which we are currently designing.  This rules system 
  1285. is described in
  1286. more detail in [STON89, STON89b].
  1287. .sh 2  "Complexity"
  1288. .pp
  1289. The first problem with PRS is that the implementation
  1290. is
  1291. exceedingly complex.  It is difficult to explain the marking mechanisms that
  1292. cause rule wake-up even to a sophisticated person.  Moreover, some of
  1293. us have an uneasy feeling that the implementation may not be
  1294. quite correct.  The fundamental problem can be illustrated using the
  1295. Joe-Fred example above.
  1296. First, the rule must be awakened and
  1297. run whenever Fred's salary changes.  This requires that one kind of marker
  1298. be placed on the salary of Fred.  However, if Fred is
  1299. given a new
  1300. name, say Bill, then the rule must be deleted and reinstalled.  This
  1301. requires a second kind of marker on the name of Fred.  Additionally,
  1302. it is inappropriate to allow any update to Joe's salary; hence a third
  1303. kind of marker is required on that field.  Furthermore, if Fred 
  1304. has not yet been hired, then the rule must take effect on the
  1305. insertion of his record.  This requires a marker to be placed in the
  1306. index for employee names.  To support rules that deal
  1307. with ranges of values, for example:
  1308. .(l
  1309. always replace EMP (age = 40) 
  1310. where EMP.salary > 50000 and EMP.salary < 60000
  1311. .)l
  1312. we require that two ``stub'' markers be placed in the index to denote
  1313. the ends of the scan.  In addition, each intervening index record must
  1314. also be marked.  
  1315. Ensuring that all markers are correctly installed and
  1316. appropriate actions taken when record accesses and updates occur
  1317. has been a challenge.
  1318. .pp
  1319. Another source of substantial complexity is the necessity to
  1320. deal with priorities.
  1321. For example, consider a 
  1322. second rule:
  1323. .(l
  1324. always replace EMP (age = 50) where EMP.dept = "shoe"
  1325. .)l
  1326. In this case a highly paid shoe department employee would be
  1327. given two different ages.  To alleviate this situation, the second
  1328. rule could be given a higher priority, e.g:
  1329. .(l
  1330. always replace EMP (age = 50) where EMP.dept = "shoe"
  1331. priority = 1
  1332. .)l
  1333. The default priority for rules is 0; hence the first rule would set
  1334. the age of highly paid employees to 40 unless they were in the
  1335. shoe department, in which case their age would be set to 50 by the second
  1336. rule.  Priorities, of course, add complications to the rules system.
  1337. For example, if the second rule above is deleted, then the first rule
  1338. must be awakened to correct the ages of employees in the shoe
  1339. department.
  1340. .pp
  1341. Another aspect of complexity is our decision to support both early
  1342. and late evaluation of rules.  Consider the example rule that
  1343. Joe makes the same salary as Fred.  This rule can be awakened when
  1344. Fred gets a salary adjustment, or activation can be delayed until
  1345. a user requests the salary of Joe.  Activation can be 
  1346. delayed as long as possible in the second case, and we term this 
  1347. .b late
  1348. evaluation while the former case is termed 
  1349. .b early 
  1350. evaluation.  This flexibility also results in substantial
  1351. extra complexity.  For example, certain rules cannot be activated 
  1352. late.  If salaries of employees are indexed, then the
  1353. rule that sets Joe's salary to that of Fred must be activated early
  1354. because the index must be kept correct.  Moreover, it is impossible
  1355. for an early rule to read data that is written by a late rule.  Hence,
  1356. additional restrictions must be imposed.  
  1357. .pp
  1358. Getting PRS correct has entailed 
  1359. uncounted hours of discussion and considerable implementation 
  1360. complexity.
  1361. The bottom line is that the implementation of a rule system that is
  1362. clean and simple to the user is, in fact, extremely complex and
  1363. tricky.  Our personal feeling is that we should have embarked on a
  1364. more modest rules system.  
  1365. .sh 2  "Absence of Needed Function"
  1366. .pp
  1367. The definition of a 
  1368. .b useful 
  1369. rules system is one that can handle
  1370. at least all of the following problems in one integrated system:
  1371. .(l
  1372. support for views
  1373. protection
  1374. referential integrity
  1375. other integrity constraints
  1376. .)l
  1377. We focus in this section on support 
  1378. for views.  The query rewrite implementation
  1379. of a rules system
  1380. should be able to translate queries on views into queries on real objects. 
  1381. In addition, updates to views should be similarly mapped to updates on 
  1382. real objects.
  1383. .pp
  1384. There are various special cases of view support that can be 
  1385. performed by PRS, for example materialized views.
  1386. Consider the following view definition:
  1387. .(l
  1388. define view SHOE-EMP (name = EMP.name, age = EMP.age, salary = EMP.salary) 
  1389. where EMP.dept = ``shoe''
  1390. .)l
  1391. The following two PRS rules specify a materialization of this view:
  1392. .(l
  1393. always append to SHOE-EMP (name = EMP.name, salary = EMP.salary) where EMP.dept = ``shoe''
  1394. always delete SHOE-EMP where SHOE-EMP.name not-in {EMP.name where EMP.dept = ``shoe''}
  1395. .)l
  1396. In this case, SHOE-EMP will always contain a correct materialization
  1397. of the shoe department employees,
  1398. and queries can be directed to this materialization.
  1399. .pp
  1400. However, there seemed to be no way to support updates on views that are not
  1401. materialized.  One of us has spent countless hours attempting to
  1402. support this function through PRS and failed.  Hence,
  1403. inability to support operations conventional views is a major weakness of PRS.
  1404. .sh 2  "Implementation Efficiency"
  1405. .pp
  1406. The current POSTGRES implementation uses markers on individual fields
  1407. to support rule activation.  The only escalation supported
  1408. is to convert a collection of field level markers to a single
  1409. marker on the entire constructed type.  Consequently, if a rule
  1410. covers a single instance, e.g:
  1411. .(l
  1412. always replace EMP (salary = 1000) where EMP.name = "Sam"
  1413. .)l
  1414. then a total of 3 markers will be set, one in the index, one
  1415. on the salary field and one on the name field.  Each marker is 
  1416. composed of:
  1417. .(l
  1418. rule-id        -- 6 bytes
  1419. priority        -- 1 byte
  1420. marker-type     -- 1 byte
  1421. .)l
  1422. Consequently, the marker overhead for the rule is 24 bytes,
  1423. Now consider a more complex rule:
  1424. .(l
  1425. always replace EMP (salary = 1000) where EMP.dept = "shoe"
  1426. .)l
  1427. If 1000 employees work in the shoe department, then 24,000 bytes of
  1428. overhead will be consumed in markers.  The only other option is
  1429. to escalate to a marker on the 
  1430. entire constructed type, in which case the rule will be
  1431. activated if any salary is read or written
  1432. and not just for employees in the shoe department.  This will be
  1433. an overhead intensive option.  Hence, for rules which cover 
  1434. many instances but not a significant fraction of all instances, the POSTGRES
  1435. implementation will not be very space efficient.  
  1436. .pp
  1437. We are considering several solutions to this problem.  First, we have 
  1438. generalized
  1439. B+-trees to efficiently store interval data as well as point data.  Such
  1440. ``segmented B+-trees'' are the subject of a separate paper [KOLE89].  This
  1441. will remove the space overhead in the index for the dominant form of
  1442. access method.  Second, to lower the overhead on data records, we will
  1443. probably implement markers at the physical block level as well as at the
  1444. instance and constructed type levels.  The appropriate extra granularities are
  1445. currently under investigation.
  1446. .sh 2  "The Second POSTGRES Rules System"
  1447. .pp
  1448. Because of the inability of the current rules paradigm to support 
  1449. views and to a lesser extent the fundamental complexity
  1450. of the implementation, we are converting to a second POSTGRES 
  1451. rules system (PRS II). 
  1452. This rules system has much in common with the first 
  1453. implementation, but 
  1454. returns to the traditional production rule 
  1455. paradigm to obtain
  1456. sufficient control to perform view updates correctly.  
  1457. This section 
  1458. outlines our thinking, and a complete proposal appears in [STON89b].
  1459. .pp
  1460. The production rule syntax we are using in PRS II
  1461. has the form:
  1462. .(l
  1463. ON event TO object WHERE POSTQUEL-qualification
  1464. THEN DO POSTQUEL-command(s)
  1465. .)l
  1466. Here, event is 
  1467. retrieve, 
  1468. replace, 
  1469. delete,
  1470. append,
  1471. new (i. e. replace or append) or
  1472. old (i.e. delete or replace).
  1473. Moreover, object is either
  1474. the name of a constructed type or
  1475. constructed-type.column.
  1476. POSTQUEL-qualification is a normal qualification, with no additions or
  1477. changes.  Lastly, POSTQUEL-commands is a set of POSTQUEL commands
  1478. with the following two changes:
  1479. .(l
  1480. NEW, OLD or CURRENT can appear instead of the name of a constructed type in front of
  1481. any attribute.
  1482.  
  1483. refuse (target-list) is added as a new POSTQUEL command
  1484. .)l
  1485. In this notation we would specify the "Fred-Joe" rule as:
  1486. .(l
  1487. on NEW EMP.salary where EMP.name = "Fred" 
  1488. then do 
  1489.    replace E (salary = CURRENT.salary)
  1490.    using E in EMP
  1491.    where E.name = "Joe"
  1492.  
  1493. on NEW EMP.salary where EMP.name = "Joe"
  1494. then do
  1495.    refuse
  1496. .)l
  1497. Notice, that PRS II
  1498. is less powerful than the "always" system because
  1499. the Fred-Joe rule require two specifications instead of one.  
  1500. .pp
  1501. PRS II has both a query rewrite implementation and a trigger implementation, and it is an optimization
  1502. decision which one to use as noted in [STON89b].
  1503. For example, consider the rule:
  1504. .(l
  1505. on retrieve to SHOE-EMP
  1506. then do
  1507. retrieve (EMP.name, EMP.age, EMP.salary) where EMP.dept = "shoe"
  1508. .)l
  1509. Any
  1510. query utilizing such a rule, e.g:
  1511. .(l
  1512. retrieve (SHOE-EMP.name) where SHOE-EMP.age < 40
  1513. .)l
  1514. would be processed by the 
  1515. rewrite implementation to:
  1516. .(l
  1517. retrieve (EMP.name) where EMP.age < 40 and EMP.dept = ``shoe''
  1518. .)l
  1519. As can be seen, this is identical to the 
  1520. query modification performed in relational view processing techniques [STON75].
  1521. This rule could also be processed by the triggering system, in which case the rule would
  1522. materialize the records in SHOE-EMP iteratively.
  1523. .pp
  1524. Moreover, it is straightforward to support
  1525. additional functionality, such as allowing multiple queries in the
  1526. definition of a view.  
  1527. Supporting materialized views can
  1528. be efficiently done by 
  1529. .b caching 
  1530. the action part of the above rule, i.e. executing
  1531. the command before a user requests evaluation.
  1532. This corresponds to moving the rule to early evaluation.
  1533. Lastly, supporting views that are partly materialized and partly specified as
  1534. procedures as well as views that involve recursion appears fairly simple.
  1535. In [STON89b] we present details on these extensions.
  1536. .pp
  1537. Consider the following collection of rules that support
  1538. updates to SHOE-EMP:
  1539. .(l
  1540. on NEW SHOE-EMP
  1541. then do
  1542.    append to EMP (name = NEW.name, salary = NEW.salary)
  1543.  
  1544. on OLD SHOE-EMP
  1545. then do
  1546.    delete EMP where EMP.name = OLD.name and EMP.salary = OLD.salary
  1547.  
  1548. on UPDATE to SHOE-EMP
  1549. then do
  1550.    replace EMP (name = NEW.name, salary = NEW.salary) 
  1551.    where EMP.name = NEW.name
  1552. .)l
  1553. If these rules are processed by the trigger implementation, then an update to SHOE-EMP, e.g:
  1554. .(l
  1555. replace SHOE-EMP (salary = 1000) where SHOE-EMP.name = ``Mike''
  1556. .)l
  1557. will be processed
  1558. normally until it generates a collection of
  1559. .(l
  1560. [new-record, old-record]
  1561. .)l
  1562. pairs.  At this point the triggering system can be activated to make
  1563. appropriate updates to underlying constructed types.  
  1564. Moreover, if a user wishes non-standard view update semantics, he can 
  1565. perform any particular actions he desires 
  1566. by changing the action part of the above rules.
  1567. .pp
  1568. PRS II thereby allows a user to use the rules system
  1569. to define semantics for retrievals and updates to views.  In fact, we expect to build a
  1570. compiler that will convert a higher level view notration into the needed collection of PRS II rules.
  1571. In addition, PRS II retains all functionality of the first rules system, so protection, alerters
  1572. integrity constraints, and arbitrary triggers are readily expressed.  
  1573. The only disadvantage is that PRS II requires two rules to perform
  1574. many tasks expressible as a single PRS rule.  To overcome this disadvantage, we
  1575. will likely continue to support the PRS syntax in addition to the PRS II syntax 
  1576. and compile PRS into PRS II.
  1577. support 
  1578. .pp
  1579. PRS II can be supported by the same implementation that
  1580. we proposed for the query rewrite implementation of PRS, 
  1581. namely marking instances in the system catalogs.  Moreover, the 
  1582. query rewrite
  1583. algorithm is nearly the same as in the first implementation. 
  1584. The triggering system can be supported by the same instance markers as in PRS. 
  1585. In fact, the implementation is bit simpler because 
  1586. a couple of the types of markers are not required.
  1587. Because the implementation of PRS II is so similar to our initial 
  1588. rules system, we expect to
  1589. have the conversion completed in the near future.
  1590. .sh 1  "STORAGE SYSTEM"
  1591. .sh 2  "Introduction"
  1592. .pp
  1593. When considering the POSTGRES storage system, we were guided by a missionary
  1594. zeal to do something different.  All current commercial systems use
  1595. a storage manager with a write-ahead log (WAL), and we felt that this
  1596. technology was well understood.  Moreover, the original INGRES 
  1597. prototype from the 1970s used a similar storage manager, and we had no
  1598. desire to do another implementation.
  1599. .pp
  1600. Hence, we seized on the idea of implementing a 
  1601. ``no-overwrite'' storage manager.  Using this technique
  1602. the old record remains in the data base whenever an update occurs, and
  1603. serves the purpose
  1604. normally performed by a write-ahead log.  Consequently, POSTGRES has
  1605. no log in the conventional sense of the term.  Instead the
  1606. POSTGRES log is simply 2 bits per transaction indicating whether each
  1607. transaction committed, aborted, or is in progress.  
  1608. .pp
  1609. Two very nice features can be exploited in a no-overwrite system.  First,
  1610. aborting a transaction can be instantaneous because one does not need
  1611. to process the
  1612. log undoing the effects of updates; the previous records are readily
  1613. available in the data base.  More generally, to recover from a crash,
  1614. one must abort all the transactions in progress at the time of the crash.
  1615. This process can be effectively instantaneous in POSTGRES.
  1616. .pp
  1617. The
  1618. second benefit
  1619. of a no-overwrite storage manager is the possibility of
  1620. .b time
  1621. .b travel.
  1622. As noted earlier, a user can ask a historical query and POSTGRES will
  1623. automatically return information from the record valid at the correct time.
  1624. .pp
  1625. This storage manager should be contrasted with a conventional
  1626. one where the previous record is overwritten with a new one.
  1627. In 
  1628. this case a write-ahead log is required to maintain the previous version
  1629. of each record.  There is no possibility of time travel because the
  1630. log cannot be queried since it is in a 
  1631. different format.  Moreover, the data base
  1632. must be restored to a consistent state when a crash occurs by
  1633. processing the log to undo any partially completed transactions.
  1634. Hence, there is no possibility of instantaneous crash recovery.
  1635. .pp
  1636. Clearly a no-overwrite storage manager is superior to a conventional
  1637. one if it can be implemented at comparable performance.  There is
  1638. a brief hand-wave of an argument in [STON87] that alleges this
  1639. might be the case.  In our opinion, the argument hinges around the
  1640. existence of
  1641. .b stable
  1642. main memory.  In the absence of stable memory, a no-overwrite 
  1643. storage manager must force to disk at commit time all 
  1644. pages written by a transaction.
  1645. This is required because the effects of a committed
  1646. transaction must be durable in case a crash occurs and
  1647. main memory is lost.
  1648. A conventional
  1649. data manager on the other hand, need only force to disk at commit time
  1650. the log pages for the
  1651. transaction's updates.
  1652. Even if there are as
  1653. many log pages as data pages (a highly
  1654. unlikely occurence), the conventional storage manager
  1655. is doing sequential I/O to the log while a no-overwrite
  1656. storage manager is doing random I/O.  Since sequential I/O is substantially
  1657. faster than random I/O, the no-overwrite solution is guaranteed to offer
  1658. worse performance. 
  1659. .pp
  1660. However, if stable main memory is present then neither solution must
  1661. force pages to disk.  In this environment, performance should be comparable.
  1662. Hence,
  1663. with stable main memory it appears that a no-overwrite solution is competitive.
  1664. As computer manufacturers offer some form of stable
  1665. main memory, a no-overwrite
  1666. solution may become a viable storage option.
  1667. .pp
  1668. In designing the POSTGRES storage system, we were guided by two
  1669. philosophical premises.
  1670. First, we decided to make a clear distinction between
  1671. current data and historical data.
  1672. We expected 
  1673. access patterns to be highly skewed toward
  1674. current records.  In addition, queries to the archive might look very
  1675. different from those accessing current data.  For both reasons, POSTGRES
  1676. maintains two different physical 
  1677. collections of records, one for the current data
  1678. and one for historical data, each with its own indexes.
  1679. .pp
  1680. Second, our design assumes the existence
  1681. of a randomly addressable archive device on which historical records
  1682. are placed. 
  1683. Our intuitive model for this archive is an optical disk.
  1684. Our design was purposely made
  1685. consistent with an archive that has
  1686. a write-once-read-many (WORM) orientation.  This 
  1687. characterizes many of the 
  1688. optical disks on the market today.
  1689. .pp
  1690. In the next subsection we indicate two problems with the POSTGRES design.
  1691. Then, in Section 5.3 we make additional comments on the storage manager.
  1692. .sh 2  "Problems in the POSTGRES Design"
  1693. .pp
  1694. There are at least two problems with our design.  First, it is unstable
  1695. under heavy load. An asynchronous demon, known as 
  1696. vacuum cleaner, is responsible for moving historical records from
  1697. the magnetic disk structure holding the current records to
  1698. the archive where historical records remain.
  1699. Under normal circumstances, 
  1700. the magnetic
  1701. disk portion of each constructed type is (say) only 1.1 times 
  1702. the minimum possible size
  1703. of the constructed type.  Of course, the vacuum cleaner consumes CPU and I/O
  1704. resources running in background achieving this goal.  However, if
  1705. the load on a POSTGRES data base 
  1706. increases, then the vacuum cleaner may not get to
  1707. run.  In this case the magnetic 
  1708. disk portion of a constructed type will increase,
  1709. and performance will suffer because the execution engine must read
  1710. historical records on the magnetic disk during the (presumably frequent)
  1711. processing
  1712. of queries to the current data base.  As a result,
  1713. performance will degrade proportionally to the excess size of
  1714. the magnetic disk portion of the data base.  As load increases, the 
  1715. vacuum cleaner gets less resources, and performance
  1716. degrades as the size of the magnetic disk data base increases.  This
  1717. will ultimately result in a POSTGRES data base going into meltdown.
  1718. .pp
  1719. Obviously,
  1720. the vacuum cleaner should be run in background if possible so that
  1721. it can consume resources at 2:00 A.M. when there is little other activity. 
  1722. However, if there is consistent heavy load on a system, then 
  1723. the vacuum cleaner must
  1724. be scheduled at the same priority as other tasks, so the above instability
  1725. does not occur.  The bottom line is that scheduling the vacuum cleaner is
  1726. a tricky optimization problem.
  1727. .pp
  1728. The second comment which we wish to make is that future archive systems are
  1729. likely to be read/write, and rewritable optical disks have already
  1730. appeared on the
  1731. market. Consequently, there is no reason for us to have 
  1732. restricted ourselves to
  1733. WORM technology.
  1734. Certain POSTGRES assumptions were therefore unnecessary, such as
  1735. requiring the current portion
  1736. of any constructed type to be on magnetic disk.
  1737. .sh 2  "Other Comments"
  1738. .pp
  1739. Historical indexes will usually be on
  1740. a combined key consisting of a time range together with one or
  1741. more keys from the record itself.  Such two-dimensional
  1742. indexes can be stored using the technology of R-trees [GUTM84],
  1743. R+-trees [FALO87] or perhaps in some
  1744. new way.  We are not particularly comfortable that good ways to
  1745. index time ranges have been found, and we encourage additional 
  1746. work in this area. 
  1747. A possible approach is segmented R-trees which we are studying [KOLE89].
  1748. .pp
  1749. Another comment concerns POSTGRES support for time travel.  There are many
  1750. tasks that are very difficult to express with our mechanisms.  For example, the query to
  1751. find the time at which Sam's salary increased from $5000 to $6000
  1752. is very tricky in POSTQUEL.
  1753. .pp
  1754. A last comment is that time travel can be implemented with a
  1755. conventional transaction system using a write ahead log.
  1756. For example, 
  1757. one need only have an ``archive'' constructed type for each physical
  1758. constructed type for which 
  1759. time travel is desired.  When a record is updated, its previous value is written
  1760. in the archive with the appropriate timestamps.  If the transaction fails to
  1761. commit, this archive insert and the corresponding
  1762. record update is unwound using a conventional
  1763. log.  Such an implementation may well have substantial
  1764. benefits, and we should have probably considered such a
  1765. possibility.  
  1766. In making storage system decisions we were guided by
  1767. a missionary zeal 
  1768. to do something different than a conventional write ahead log
  1769. scheme. 
  1770. Hence, we may have overlooked other intriguing options.
  1771. .sh 1  "THE POSTGRES IMPLEMENTATION"
  1772. .sh 2  "Introduction"
  1773. .pp
  1774. POSTGRES contains a fairly conventional parser, query optimizer and
  1775. execution engine.  Two aspects of the implementation deserve special
  1776. mention, 
  1777. .(l
  1778. dynamic loading and the process structure
  1779. choice of implementation language
  1780. .)l
  1781. and we discuss each in turn.
  1782. .sh 2  "Dynamic Loading and Process Structure"
  1783. .pp
  1784. POSTGRES assumes that data types, operators
  1785. and functions can be added and subtracted dynamically, i.e. while
  1786. the system is executing.  Moreover, we have designed the system 
  1787. so that it can accommodate a potentially very large number of
  1788. types and operators.  
  1789. Consequently, the user functions that support the implementation of a type must
  1790. be dynamically loaded and unloaded.  Hence, POSTGRES maintains a
  1791. cache of currently loaded functions and dynamically moves functions
  1792. into the cache and then ages them out of the cache.  Moreover,
  1793. the parser and optimizer run off of a main memory cache of information
  1794. about types and operators.  Again this cache must be maintained 
  1795. by POSTGRES software.  It would have been much easier to assume that
  1796. all types and operators were linked into the system at POSTGRES
  1797. initialization time and have required a user to reinstall
  1798. POSTGRES when he wished to add or drop types.
  1799. Moreover, users of prototype software are not running systems which cannot
  1800. go down for rebooting.  Hence, the function is not essential.
  1801. .pp
  1802. Second, the rules system forces significant complexity on the design.
  1803. A user can add a rule such as:
  1804. .(l
  1805. always retrieve (EMP.salary)
  1806. where EMP.name = "Joe"
  1807. .)l
  1808. In this case his application process wishes to be notified of any
  1809. salary adjustment to Joe.  Consider a second user who gives
  1810. Joe a raise.  The POSTGRES process that actually does the adjustment
  1811. will notice that a marker has been placed on the salary field.  However,
  1812. in order to alert the first user, one of four things must happen:
  1813.  
  1814. a) POSTGRES could be designed as a single server process.  In this case
  1815. within the current process the first user's query could simply be activated.
  1816. However, such a design is incompatible with running on a shared memory
  1817. multiprocessor, where a so-called multi-server is required.  Hence, this
  1818. design was discarded.
  1819.  
  1820. b) The POSTGRES process for the second user could run the first user's
  1821. query and then connect to his application process to deliver results.
  1822. This requires that an application process be coded to expect communication
  1823. from random other processes.  We felt this was too difficult to
  1824. be a reasonable solution.
  1825.  
  1826. c) The POSTGRES process for the second user could connect to the input
  1827. socket for the first user's POSTGRES and deliver the query to be run.
  1828. The first POSTGRES would run the query and then send results to the
  1829. user.  This would require careful synchronization of the input socket
  1830. among multiple independent command streams.  Moreover, it would
  1831. require the second POSTGRES to know the portal name on which the first
  1832. user's rule was running.
  1833.  
  1834. d) The POSTGRES process for the second user could alert a special
  1835. process called the
  1836. .b POSTMASTER.  
  1837. This process would in turn alert the process for the first user where the query
  1838. would be run and the results delivered to the application process.  
  1839.  
  1840. We have adopted the fourth design as the only one we thought was practical.
  1841. However, we have thereby constructed a process through which everybody 
  1842. must channel communications.  If the POSTMASTER crashes, then
  1843. the whole POSTGRES environment must be restarted.  This is a handicap, but
  1844. we could think of no better solution.  Moreover, there are a collection
  1845. of system demons, including the vacuum cleaner mentioned above, which need
  1846. a place to run.  In POSTGRES they are run as subprocesses managed by
  1847. the POSTMASTER.
  1848. .pp
  1849. A last aspect of our design concerns the operating system
  1850. process structure.
  1851. Currently, POSTGRES runs as one
  1852. process for each active user.  This was done as an expedient to
  1853. get a system operational as quickly as possible.  We plan on
  1854. converting POSTGRES to use lightweight processes available in
  1855. the operating systems we are using.  These include PRESTO 
  1856. for the Sequent Symmetry and threads in Version 4 of Sun/OS.
  1857. .sh 2  "Programming Language Used"
  1858. .pp
  1859. At the beginning of the project, we were forced to make 
  1860. a commitment to a programming language and machine environment.
  1861. The machine was an easy one, since
  1862. SUN workstations were nearly omnipresent at Berkeley, and any other
  1863. choice would have been non-standard.  However, we were free to
  1864. choose any language in which to program.  We considered the following:
  1865. .(l
  1866. C
  1867. C++
  1868. MODULA 2+
  1869. LISP
  1870. ADA
  1871. SMALLTALK
  1872. .)l
  1873. We dismissed SMALLTALK quickly because we felt it was too
  1874. slow and compilers were not readily available for a wide
  1875. variety of platforms.  We felt it desirable to keep open the
  1876. option of distributing our software widely.
  1877. We felt ADA and MODULA 2+ offered limited advantages over C++ and
  1878. were not widely used in the Berkeley environment.  Hence, obtaining
  1879. pretrained programmers would have been a problem.  Lastly, we
  1880. were not thrilled to use C, since INGRES had been coded in C and we were anxious
  1881. to choose a different language, if only for the sake of doing something
  1882. different.  At the time we started (10/85), there was not a stable C++
  1883. compiler, so we did not seriously consider this option.  
  1884. .pp
  1885. By a process of elimination, we decided to try writing POSTGRES
  1886. in LISP.  We expected that it would be especially
  1887. easy to write the optimizer and inference engine in LISP, since
  1888. both are mostly tree processing modules.  Moreover, we
  1889. were 
  1890. seduced by AI claims of high programmer productivity for applications
  1891. written in LISP.  
  1892. .pp
  1893. We soon realized that parts of the system were more easily coded in C,
  1894. for example the buffer manager which
  1895. moves 8K pages back and forth
  1896. to the disk and uses a modified LRU algorithm to control what pages
  1897. are resident.  
  1898. Hence, we adopted the
  1899. policy that we would use both C and LISP and code
  1900. modules of POSTGRES in whichever language was most appropriate.
  1901. By the time Version 1 was operational, it contained
  1902. about 17000 lines of LISP and about 63000
  1903. lines of C.  
  1904. .pp
  1905. Our feeling is that the use of LISP has been a terrible mistake
  1906. for several reasons.
  1907. First, current LISP environments
  1908. are very large.  To run a ``nothing'' program in LISP requires 
  1909. about 3 mbytes of address space.  Hence, POSTGRES exceeds 4 mbytes in
  1910. size, all but 1 mbyte is the LISP compiler, editor and assorted other
  1911. non required (or even desired) functions.  Hence, we suffer from a
  1912. gigantic footprint.  Second, a DBMS never wants to stop when
  1913. garbage collection happens.  Any response time sensitive
  1914. program must therefore allocate and deallocate space manually, so that
  1915. garbage collection never happens during normal processing.  Consequently, we
  1916. spent extra effort ensuring that LISP garbage collection is not
  1917. used by POSTGRES.  Hence, this aspect of LISP, which improves
  1918. programmer productivity, was not available to us.
  1919. Third, LISP execution is slow.  As noted in the performance figures
  1920. in the next section our 
  1921. LISP code is more than twice as slow as the comparable C code. 
  1922. Of course, it is possible that we are not skilled LISP programmers
  1923. or do not know how to optimize the language; hence our experience should be
  1924. suitably discounted.
  1925. .pp
  1926. However, none of these irritants
  1927. was the real disaster.  We have found that debugging a two language system is
  1928. extremely difficult.  
  1929. The C debugger, of course, knows nothing about LISP while the LISP
  1930. debugger knows nothing about C.  As a result, we have found debugging
  1931. POSTGRES to be a painful and frustrating task.  
  1932. Memory allocation bugs were among the most painful since LISP and C have
  1933. very different models of dynamic memory.
  1934. Of course, it is true that the
  1935. optimizer and inference engine were easier to code in LISP.  Hence,
  1936. we saved some time there.  However, this was more than compensated 
  1937. by the requirement of writing a lot of utility code that would convert
  1938. LISP data structures into C and vica versa.  In fact, our assessment
  1939. is that the primary productivity increases in LISP come from the nice
  1940. programming environment (e.g. interactive debugger, nice workstation
  1941. tools, etc.) and not from the language itself.  Hence, we would encourage
  1942. the implementors of other programming languages 
  1943. to study the LISP environment carefully and
  1944. implement the better ideas. 
  1945. .pp
  1946. As a result we have just finished moving our 17000 lines of
  1947. LISP to C to
  1948. avoid the debugging hassle and secondarily to avoid the performance
  1949. and footprint problems in LISP.  Our experience with LISP and two
  1950. language systems has
  1951. not been positive, and we would caution others not to follow in our
  1952. footsteps.
  1953. .sh 1  "STATUS AND PERFORMANCE
  1954. .pp
  1955. At the current time (October 1989) the LISP-less Version 1 
  1956. of POSTGRES has been in 
  1957. the hands of users for a short time, and we are shaking the last bugs
  1958. out of the C port.  In addition, we have designed all of
  1959. the additional functionality to appear in Version 2. 
  1960. The characteristics of Version 1 are:
  1961.  
  1962. a) The query language POSTQUEL runs except for aggregates, functions and
  1963. set operators.
  1964.  
  1965. b) All object management capabilities are operational 
  1966. except POSTQUEL types.
  1967.  
  1968. c) Some support for rules exists.  Specifically, 
  1969. replace always commands are operational; however the implementation
  1970. currently only supports early evaluation and only with
  1971. markers on whole columns.
  1972.  
  1973. d) The storage system is complete.  However, we are taking delivery
  1974. shortly on an optical disk jukebox, and so the archive is
  1975. currently not implemented on a real optical disk.  Moreover, 
  1976. R-trees to support time travel are not yet implemented.
  1977.  
  1978. e) Transaction management runs.
  1979.  
  1980. .pp
  1981. The focus has been on getting the function in POSTGRES to
  1982. run.  So far, only minimal attention has been paid to
  1983. performance.  
  1984. Figure 1 shows assorted queries in the
  1985. Wisconsin benchmark and gives results for three systems 
  1986. running on
  1987. a SUN 3/280.  All numbers are run on a non-quiescent system
  1988. so there may be significant fluctuations.  
  1989. The first two are the C and LISP versions of
  1990. POSTGRES.  These are functionally identical systems with the same
  1991. algorithms embodied in the code.  The footprint of the
  1992. LISP system is about 4.5 Mbytes
  1993. while the C system is about 1 Mbyte.  For comparison purposes
  1994. we also include the performance numbers for the commercial version of
  1995. INGRES in the third column.
  1996. .(z
  1997. .hl
  1998. .(c
  1999. .TS
  2000. c c c c
  2001. l l l l.
  2002.     POSTGRES    POSTGRES    INGRES
  2003.     C-based    LISP-based    RTI 5.0
  2004.  
  2005. nullqry    0.4    0.3    0.2
  2006.  
  2007. scan 10Ktups    36.    180.    5.2
  2008.  
  2009. retrieve into query
  2010. 1% selectivity    38.    n/a    9.9
  2011.  
  2012. append to 10Ktup    4.7    180.    0.4
  2013.  
  2014. delete from 10Ktup    37.    n/a    5.7
  2015.  
  2016. replace in 10Ktup    42.    280.    5.7
  2017.  
  2018. .TE
  2019. .)c
  2020. .sp
  2021. .ce
  2022. A Comparison of INGRES and POSTGRES
  2023. .ce
  2024. (Times are listed in seconds per query.)
  2025. .ce
  2026. Figure 1
  2027. .hl
  2028. .)z
  2029. As can be seen, the LISP system is several times slower than the C system.
  2030. In various other benchmarks we have never seen the C system less than twice 
  2031. as fast as the LISP system.  Moreover, the C system is several 
  2032. times slower than a
  2033. commercial system.  The Public domain version of INGRES that we worked on in the
  2034. mid 1970's is about a factor of two slower than commercial INGRES.  Hence, it
  2035. appears that POSTGRES s about one-half the speed of the original INGRES.
  2036. There are substantial inefficiencies in POSTGRES, especially in the
  2037. code which checks that a retrieved record is valid.  
  2038. We expect that subsequent tuning  
  2039. will get us somewhere in between the performance of
  2040. Public domain INGRES and RTI INGRES.
  2041. .sh 1 "CONCLUSIONS"
  2042. .pp
  2043. In this section we summarize our opinions about certain aspects of 
  2044. the design of POSTGRES.  First, we are uneasy about the complexity of
  2045. the POSTGRES data model.  
  2046. The comments in Section 2 all contain suggestions to make it more complex.
  2047. Moreover, other research teams have tended to construct even more
  2048. complex data models, e.g. EXTRA [CARE88].  Consequently, a simple concept
  2049. such as referential integrity, which can be done in only one way in
  2050. existing commercial systems, can be done in several different ways in
  2051. POSTGRES.  For example, the user can implement an abstract data type and
  2052. then do the required checking in the input conversion routine. Alternately, 
  2053. he can use a rule in the POSTGRES rules system.  Lastly, he can use a POSTQUEL function
  2054. for the field that corresponds to the foreign key in a current relational
  2055. system.  There are complex performance tradeoffs between these three 
  2056. solutions, and a decision must be made by a sophisticated application
  2057. designer.  We fear that real users, who have a hard time with data base design
  2058. for existing relational systems, will find the next-generation
  2059. data models, such as the one in POSTGRES, impossibly complex.
  2060. The problem is that applications exist where each representation
  2061. is the only acceptable one.  The demand for wider application
  2062. of data base technology ensures that vendors will produce
  2063. systems with these more complex data models.
  2064. .pp
  2065. Another source of uneasiness is the fact that rules and POSTQUEL
  2066. functions have substantial overlap in function.  For example,
  2067. a POSTQUEL function can be simulated
  2068. by one rule per record, albeit at some performance penalty.  On the
  2069. other hand, all rules, except retrieve always commands, can be alternately
  2070. implemented using POSTQUEL functions.  We expect to merge the two concepts in 
  2071. Version 2, and our proposal appears in [STON89b].
  2072. .pp
  2073. In the areas of rules and storage management, we are basically satisfied
  2074. with POSTGRES capabilities.  The syntax of the rule system should be
  2075. changed as noted in Section 3; however this is not a significant 
  2076. issue and it should be available easily in Version 2.  The storage manager
  2077. has been quite simple to implement.  Crash recovery code has been 
  2078. easy to write because the only routine which must be carefully written is
  2079. the vacuum cleaner.  Moreover, access 
  2080. to past history seems to be a highly desirable
  2081. capability.
  2082. .pp 
  2083. Furthermore, the POSTGRES implementation certainly erred in the direction of
  2084. excessive sophistication.  For example, new types and functions
  2085. can be added on the fly without recompiling POSTGRES.  It would have
  2086. been much simpler to construct a system that required recompilation
  2087. to add a new type.  Second, we have implemented a complete
  2088. transaction system in Version 1.  Other prototypes tend to assume a single
  2089. user environment.
  2090. In these and many other ways, we strove for 
  2091. substantial generality; however the net effect has been to slow down the
  2092. implementation effort and make the POSTGRES internals much more
  2093. complex.  As a result, POSTGRES has taken us considerably longer to build
  2094. than the original version of INGRES.  
  2095. One could call this the ``second system'' effect.  It was essential that
  2096. POSTGRES be more usable than the original INGRES prototype in 
  2097. order for us to feel like we were making a contribution.
  2098. .pp
  2099. A last comment concerns technology transfer to commercial systems.  It
  2100. appears that the process is substantially accelerating.  For example, 
  2101. the relational model was constructed in 1970, first prototypes of
  2102. implementations appeared around 1976-77, commercial versions first
  2103. surfaced around 1981 and popularity of relational systems in the
  2104. marketplace occurred around 1985.  Hence, there was a 15
  2105. year period during which the ideas were transferred to commercial systems.
  2106. Most of the ideas in POSTGRES and in other next generation systems
  2107. date from 1984 or later.  Commercial systems embodying some of these
  2108. ideas have already appeared and major vendors are expected to have 
  2109. advanced systems within the next year or two.  Hence, the 15 year period
  2110. appears to have shrunk to less than half that amount.  This acceleration
  2111. is impressive, but it will lead to rather short lifetimes for the
  2112. current collection of prototypes.
  2113.  
  2114. .vs 12
  2115. \fBREFERENCES\fP
  2116. .nr ii 10m
  2117. .ip [AGRA89]
  2118. Agrawal, R. and Gehani, N., "ODE: The Language and the Data Model," Proc. 1989 ACM-SIGMOD
  2119. Conference on Management of Data, Portland, Or., May 1989.
  2120. .ip [ATKI89]
  2121. Atkinson, M. et. al., ``The Object-oriented Database System Manifesto,'' Altair Technical Report 30-89,
  2122. Rocquencourt, France, August 1989.
  2123. .ip [ANON85]
  2124. Anon et. al.,
  2125. .q "A Measure of Transaction Processing Power,"
  2126. Tandem Computers, Cupertino, CA, Technical
  2127. Report 85.1, 1985. 
  2128. .ip [AOKI89]
  2129. Aoki, P., ``Implementation of Extended Indexes in POSTGRES,'' Electronics
  2130. Research Laboratory, University of California, Technical Report 89-62,
  2131. July 1989.
  2132. .ip [BANC86]
  2133. Bancilhon, F. and Ramakrishnan, R., ``An Amateur's Introduction to Recursive
  2134. Query Processing,'' Proc. 1986 ACM-SIGMOD Conference on Management of 
  2135. Data, Washington, D.C., May 1986.
  2136. .ip [BANE87]
  2137. Banerjee, J. et. al., ``Semantics and Implementation of Schema Evolution
  2138. in Object-oriented Databases,'' Proc. 1987 ACM SIGMOD Conference on
  2139. Management of Data, San Francisco, Ca., May 1987.
  2140. .ip [BITT83]
  2141. Bitton, D. et. al.,
  2142. ``Benchmarking Database Systems: A Systematic Approach,'' Proc. 1983 VLDB
  2143. Conference, Cannes, France, Sept. 1983. 
  2144. .ip [BORG85]
  2145. Borgida, A., \`\`Language Features for Flexible Handling of Exceptions
  2146. in Information Systems,\'\' ACM-TODS, Dec. 1985.
  2147. .ip [CARE88]
  2148. Carey, M. et. al., ``A Data Model and Query Language for EXODUS,''
  2149. Proc. 1988 ACM-SIGMOD Conference on Management of Data, Chicago, Ill., 
  2150. June 1988.
  2151. .ip [COPE84]
  2152. Copeland, G. and D. Maier, ``Making Smalltalk a Database System,''
  2153. Proc. 1984 ACM-SIGMOD Conference on Management of Data, Boston, Mass.
  2154. June 1984.
  2155. .ip [DADA86]
  2156. Dadam, P. et. al., ``A DBMS Prototype to Support NF2 Relations,'' Proc.
  2157. 1986 ACM-SIGMOD Conference on Management of Data, Washington, D.C., May 1986.
  2158. .ip [DATE81]
  2159. Date, C., \`\`Referential Integrity,\'\' Proc. Seventh International VLDB
  2160. Conference, Cannes, France, Sept. 1981.
  2161. .ip [ESWA76]
  2162. Eswaren, K., ``Specification, Implementation and Interactions of a 
  2163. Rule Subsystem in an Integrated Database System,'' IBM Research,
  2164. San Jose, Ca., Research Report RJ1820, August 1976.
  2165. .ip [FALO87]
  2166. Faloutsos, C. et. al., ``Analysis of Object Oriented Spatial Access Methods,''
  2167. Proc. 1987 ACM-SIGMOD Conference on Management 
  2168. of Data, San Francisco, Ca., May 1987.
  2169. .ip [GUTM84]
  2170. Gutman, A., ``R-trees: A Dynamic Index Structure for Spatial Searching,''
  2171. Proc. 1984 ACM-SIGMOD Conference on Management of Data, Boston, Mass.
  2172. June 1984.
  2173. .ip [KOLE89]
  2174. Kolovson, C. and Stonebraker, M., ``Segmented Search Trees and
  2175. their Application to Data Bases,'' (in preparation).
  2176. .ip [LYNC88]
  2177. Lynch, C. and Stonebraker, M., ``Extended User-Defined Indexing with
  2178. Application to Textual Databases,'' Proc. 1988 VLDB Conference,
  2179. Los Angeles, Ca., Sept. 1988.
  2180. .ip [MAIE89]
  2181. Maier, D., ``Why Isn't There an Object-oriented Data Model?'' Proc.
  2182. 11th IFIP World Congress, San Francisco, Ca., August 1989.
  2183. .ip [OSBO86]
  2184. Osborne, S. and Heaven, T., ``The Design of a Relational System with Abstract
  2185. Data Types as Domains,'' ACM TODS, Sept. 1986.
  2186. .ip [RICH87]
  2187. Richardson, J. and Carey, M., ``Programming Constructs for Database System
  2188. Implementation in EXODUS,'' Proc. 1987 ACM-SIGMOD Conference
  2189. on Management of Data, San Francisco, Ca., May 1987.
  2190. .ip [ROWE87]
  2191. Rowe, L. and Stonebraker, M., "The POSTGRES Data Model,"
  2192. Proc. 1987 VLDB Conference, Brighton, England, Sept 1987.
  2193. .ip [ROWE89]
  2194. Rowe, L. et. al., ``The Design and Implementation 
  2195. of Picasso,'' (in preparation).
  2196. .ip [SELL86]
  2197. Sellis, T., ``Global Query Optimization,'' Proc 1986 ACM-SIGMOD Conference on
  2198. Management of Data, Washington, D.C., June 1986.
  2199. .ip [STON75]
  2200. Stonebraker, M., ``Implementation of Integrity Constraints and
  2201. Views by Query Modification,'' Proc. 1975 ACM-SIGMOD Conference,
  2202. San Jose, Ca., May 1975.
  2203. .ip [STON82]
  2204. Stonebraker, M. et. al., ``A Rules System for a Relational Data Base Management
  2205. System,'' Proc. 2nd International Conference on Databases,'' Jerusalem,
  2206. Israel, June 1982 (available from Academic press).
  2207. .ip [STON86]
  2208. Stonebraker, M. and Rowe, L., 
  2209. .q "The Design of POSTGRES,"
  2210. Proc. 
  2211. 1986 ACM-SIGMOD Conference, Washington, D.C., June 1986.
  2212. .ip [STON86b]
  2213. Stonebraker, M., \`\`Inclusion of New Types in Relational Data Base Systems,\'\'
  2214. Proc. Second International Conference on Data Engineering, Los Angeles,
  2215. Ca., Feb. 1986.
  2216. .ip [STON87]
  2217. Stonebraker, M., "The POSTGRES Storage System," Proc. 
  2218. 1987 VLDB Conference, Brighton, England, Sept. 1987.
  2219. .ip [STON87b]
  2220. Stonebraker, M. et. al., ``Extensibility in POSTGRES,'' IEEE Database
  2221. Engineering, Sept. 1987.
  2222. .ip [STON88]
  2223. Stonebraker, M. et. al., "The POSTGRES Rules System,"
  2224. IEEE Transactions on Software Engineering, July 1988.
  2225. .ip [STON89]
  2226. Stonebraker, M. et. al., ``Commentary on the POSTGRES Rules
  2227. System,'' SIGMOD RECORD, Sept. 1989.
  2228. .ip [STON89b]
  2229. Stonebraker, M. et. al., ``Rules, Procedures and Views,'' (in preparation).
  2230. .ip [ULLM85]
  2231. Ullman, J., ``Implementation of Logical Query Languages for Databases,''
  2232. ACM-TODS, Sept. 1985.
  2233. .ip [VELE89]
  2234. Velez, F. et. al., ``The O2 Object manager: An Overview,'' GIP ALTAIR,
  2235. Le Chesnay, France, Technical Report 27-89, February 1989.
  2236. .ip [WENS88]
  2237. Wensel, S. (ed.), 
  2238. .q "The POSTGRES Reference Manual,"
  2239. Electronics Research
  2240. Laboratory, University of California, Berkeley, CA, Report M88/20, March 1988.
  2241. .ip [WIDO89]
  2242. Widom, J. and Finkelstein, S., ``A Syntax and Semantics for Set-oriented
  2243. Production Rules in Relational Data Bases, IBM Research, San Jose, Ca.,
  2244. June 1989.
  2245.  
  2246.