Enforcing Integrity Constraints in Database Programming Languages Veronique Benzaken Universite de Paris I - Sorbonne, 12 place du Pantheon, 75005 Paris, France e-mail:
[email protected] Christophe Lecluse BL/AIS, 34 avenue du Roule, 92100 Neuilly sur seine, France. e-mail:
[email protected] Philippe Richardy Alcatel-Alsthom Recherche, Route de Nozay, 91460 Marcoussis, France. e-mail:
[email protected] April 26, 1995 Abstract
This paper is concerned with the problem of eciently checking of integrity constraints in data base programming languages supporting subtyping and class hierarchies. More speci cally, we consider two dierent problems: (1) statically reduce the number of constraints to be checked, and (2) generate an ecient run time checker. Using simple strategies, one can signi cantly improve the eciency of the veri cation. We show how to reduce the number of constraints to be checked by characterising the portions of the database that are concerned by the constraints and involved in a transaction. We also show how to generate ecient algorithms for checking a large class of constraints (universally quanti ed conjunctive constraints). Last, we show how all the techniques presented took great advantage of the underlying type system which provide a signi cant help both in solving (1) and (2). y
On leave from INRIA, BP 105, 78153 Rocquencourt le Chesnay cedex, since June 1991 On leave from INRIA, BP 105, 78153 Rocquencourt le Chesnay cedex, since April 1991
1
1 Introduction Eorts on database programming languages have mainly been devoted to the de nition of elaborated type systems and persistence mechanisms for those languages. In particular, the problems of polymorphism, static typing and inference, and object identity were the main topics of [7]. On the other hand, database programming languages are in general not able to express integrity constraints in a global and declarative way although some interesting works are done in the context of objectoriented databases [11]. On the contrary, extended relational systems take integrity constraints and views into consideration, mainly in the relational way [15, 5, 16]. Those systems are mainly relational systems in which relations attributes domains are not necessarily atomic but can be constructed using abstract types. The associated query language can also be extended to manipulate these user de ned types instances. However, extended relational systems are not integrated in the sense of database programming languages. In these systems, relations are a very special kind of data type that cannot be used orthogonally to the others. In most systems, sets cannot be constructed independently of relations, and the query languages are not integrated within the language used to de ne the new attributes domains. In the deductive database eld, the problem of integrity constraint checking has been fully investigated [6, 3, 8, 2]. A rst technique consists in determining without accessing the database whether a transaction may violate an integrity constraint. Such a technique relies on theorem proving mechanism [13]. A second approach assumes transactions to be provided with the atomicity property and consists in restricting the constraints to be enforced and in avoiding to retest the portion of the database that was consistent before the transaction [15, 12]. Although the methods proposed in this paper rely on the same ideas, we would like to emphasize the bene ts gained when relying on a type system in order to cope with object-oriented complexity. We consider two dierent problems: (1) statically reduce the number of constraints to be checked, and (2) generate an ecient run time checker. Of course, in the general case the problem is very complicate and nding an optimal solution to (1), for instance, is undecidable. What we want to show is that, using simple strategies, we can signi cantly improve the eciency of the veri cation. In this paper, we shall suppose that transactions can neither be nested nor call other transactions or functions, and that constraint speci cation does not involve method calls, in order to avoid interprocedural analysis. This general case will be the topic of a forthcoming study. Our main goal is to fully exploit the type information in order to simplify constraint violation detection and to speed up constraint checking. Not only classes are partially ordered according to an inheritance hierachy but we also have to face the problem of constraint checking in an environment that allows updates to be propagated among several distinct paths among objects. A rst part of the paper consist in using simple compilation techniques to statically determine which constraints might be violated by a transaction. The originality of this static analysis 2
is that it captures the notion of inheritance. A second contribution consists, given a transaction and a constraint, in generating a checking algorithm which will operate on the smallest portion of the database involved by the transaction. Unlike in deductive databases, objects are much more structured and they can be accessed through several dierent paths in the database. We show how to signi cantly reduce the number of checking operations to be performed, relying on the underlying typing information. In Section 2, we give an overview of the framework of [10] and show how to de ne integrity constraints in this framework. In Section 3, we consider the rst problem, that is the reduction of the number of constraints. In Section 4, we show how the constraints can be transformed in order to be more eciently checked. Section 5 contains some concluding remarks.
2 Overview of the Schema De nition Language We study the constraints de nition and maintenance problem in the framework proposed in [10] which we brie y recall here. We also give several examples that will be used in Section 3 and 4.
2.1 Types
We consider a framework in which all database manipulations are strongly and statically typed. Types can be atomic types like integer, string or boolean. Types can also be concrete structures like tuple and set types: [name: string, children: f string g] f Person g, f integer g Subtyping of concrete types is structural and inferred following the classical rules of Cardelli [4]. We have, for instance: [name: string, age: integer] [name: string] f [name: string, age: integer] g f [name: string] g Concrete type instances are non shared, non mutable values. Abstract types have a name and a list of features which model attributes and operations, as follows:
type Person is abstract name: string, age: integer,
: Person g, : Person, : string g, (integer), /* an operation */
children f spouse licences f changeAge
end
3
type Vehicle is abstract type: string, agev: integer, colour: string, number: integer, : Person,
owner
end
The de nition of the Person type states that a person has a name, an age, a set of children, a spouse and is equipped with an operation to update the age feature. Instances of an abstract type are \objects" in the sense that they have an identity which is independent of the value of their features (name, age, etc.). Instances of an abstract type are mutable and shared values.
2.2 Names and Classes
Types are used to describe the components of a database. The database can be seen as a graph of interconnected objects and values. The roots of this graph are names de ned at the schema level:
let aSetofInts : f integer g := f 1, 2, 3 g let Licences : f string g := f \truck", \car", \bike" g The let keyword de nes a new name with an associated type and binds an initial value to it. The value can be updated as for any variable. The notion of class is an extensional notion which is not captured by the type de nitions. A class represents a collection of objects of one type (abstract or not) and is characterised by a name and the type of its elements as follows:
class Persons of type Person class Vehicles of type Vehicle key self.number class Drivers of type [driver: Person, car: Vehicle] Class names are special kinds of identi ers as they can be considered as set value names but classes can be organised in a subclass hierarchy. We can write, for example:
class Planes subclass of Vehicles class BadDrivers subclass of Drivers The class Planes is declared as a subclass of the class Vehicles. The associated types are the same, that is type Vehicle. The semantics of this subclass relationship is an inclusion semantics [9]. For instance, the class Planes is a subset of the class Vehicles. 4
2.3 Integrity Constraints
In our framework, integrity constraints are well-typed boolean expressions built using the names and classes of the schema, and general operators. We detail these operators in the following:
Constants: true, false, nil. Variables: x, y, etc. Arithmetical operators: +, -, *, / Comparators: =, 6=, , , Boolean operators: and, or, not. Tuple eld or object feature extraction: x.a (where a is not a method). Set manipulations: isin, in, inter, union Aggregates: card
The equality operator can be applied on values of any type. The other comparators can be applied to numbers and sets. While such expressions could also be built over tuple methods extraction, we rst restrict our analysis to simple (non computable) eld extraction. A constraint has the following generic form: C = Q x1 in S1, : : : , Q x in S P(x1, : : : , x ) where Q denotes a quanti er (all or exists), the S 's are set expressions, and P(x1, : : : ,x ) is a boolean expression with the x 's as free variables. The expression \Q x1 in S1, : : : , Q x in S " will be referred as the head of the constraint while the predicate will be referred as its tail. More precisely, the predicate P(x1, : : : ,x ) is of the form: P(x1, : : : , x ) = r1 Co r2, : : : Co r where Co denotes a logical connector (or or and), each r denotes a comparison expression of the form (x x ) or (x c ), where is one of the comparators (, isin, etc.), and c are constants. The following section gives some example of constraints. k
k
k
j
k
j
k
k
k
k
n
i
j
i
j
i
j
2.3.1 Examples of constraints
The following are examples of constraints that can be constructed in our framework. They will be used as running examples in Sections 3 and 4. (C1) all p in Persons, p.age 130 and p.age 0; (C2) all d in Drivers, d.driver.age 18 (C3) all i in aSetofInts, i 100 5
These constraints are range constraints. The reader should notice that they are dierent from a domain restriction in a type speci cation. A domain restriction is valid for every instance of a type and does not depend on the collection to which this instance belongs. A range constraint is local to a class (here Persons) and does not mean that every instance of the type Person has to satisfy this restriction. This is one example of what cannot be expressed only by means of type systems. (C4) card(Licences) 6; (C5) all p in Persons, card(p.children) < 20; (C6) all p in Persons, p.licences Licences; (C7) all d in Drivers, card(d.driver.licences) 1 These constraints put cardinality constraints on the named value Licences and on the attribute children of class Persons. (C8) all d in Drivers, d.car.type 6= \truck" or \truck-licence" in d.driver.licences (C9) all c1, c2 in Vehicles, c1.number 6= c2.number or c1 = c2; (C10) all p in Persons, card(p.children) 12 or p.age 35; (C11) all p in Persons, p.spouse.spouse = p or p.spouse = nil; (C12) all c in Vehicles, exist d in Drivers, d.car = c and c.agev = d.driver.age The constraint C8 expresses that each driver in the Drivers class which drives a truck has the corresponding licence. The constraint C9 states that the number attribute in class Vehicles is a key for this class. The constraint C10 expresses that if a person has more than 12 children then he (she) is older than 35. The constraint C11 expresses that a person is either single or is the spouse of his (her) spouse. The dummy constraint C12imposes that for each vehicle there exists a driver whose age is the same as the age of his car. (C13) all x in f 3, 5, 7, 9 g, exists n in MyInts, x - n isin aSetofInts (C14) all x in aSetofInts union MyInts, x 15 The rst of these last constraints shows the ability to express constraints on (set-structured) values without any need to assign them a name while the other one is expressed on the union of two populations.
2.4 Transactions
A transaction is a function which has the atomicity property. The following are examples of transactions:
let T1 = trans(t, c: string, p: Person, a, n: integer) insert Vehicle[type: t, agev: a, colour: c, number: n, owner: p] into Vehicles /* this transaction inserts a new vehicle in the class Vehicles */ 6
let T2 = trans(p1, p2: Person):
p1.spouse := p2; p2.spouse := p1; /* this transaction performs a marriage between two persons */
let T3 = trans(p: Person, l: string)
p.licences += l; /* this transaction adds a new licence for a given person */
let T4 = trans() for p in Persons when (today = p.birthday)
print(\Happy Birthday \, p.name); p.age := p.age + 1 /* this transaction updates the age of all Persons born on the current day */
3 Static Characterisation of a Set of Constraints In order to avoid checking unnecessary constraints, we want to be able to statically characterise the integrity constraints that may be violated by a given transaction. As the problem of nding the exact solution is undecidable (because this depends on input data), we are only looking for a necessary condition, that is for a superset of the solution. In order to characterise this superset of constraints, for a given transaction, we shall consider the parts of the database that are dealt with in a given constraint and/or involved in a given transaction. In the following subsections, we thus de ne characterisations of constraints and transactions. Such characterisations consist, informally, in a set of paths into the database. A path starts from a schema name or a type name, which can be followed by one or several attribute names. For example, if the path \Person.age" is in the characterisation of a constraint, this means that the constraint refers to the age of an object of type Person. If the same path is in the characterisation of a transaction, this means that this transaction may update the age of a Person. Therefore, the transaction may violate the given constraint, and it will be checked at the end of the transaction. One of the characteristics of this static analysis is the use we make of the underlying type system. As we said before, abstract types instances are mutable and shared values, whereas concrete types instances are non shared non mutable values. This concept of mutability and sharing is crucial when dealing with constraints checking. Indeed, an object may be referred to by several other objects and, as a consequence, updated through dierent database paths. The only information that can be statically manipulated for those objects is their type. 7
3.1 Characterisation of constraints
A constraint has the following generic form: C = Q x1 in S1, : : : , Q x in S P(x1, : : : , x ) where Q denotes a quanti er (all or exists), the S 's are set expressions, and P(x1, : : : ,x ) is a boolean expression with the x 's as free variables. We illustrate this notation with some of the above constraint examples: C1 : all p in Persons, p.age 130 and p.age 0 S = Persons P(p) = p.age 130 and p.age 0; r1 = p.age 130 r2 = p.age 0 C4 : card(Licences) 1 S=; P = card(Licences) 1 r=P C8 : all d in Drivers, d.car.type 6= \truck" or \truck-licence" isin d.driver.licences S = Drivers P(d) = d.car.type 6= \truck" or \truck-licence" isin d.driver.licences r1 = d.car.type 6= \truck" r2 = \truck-licence" isin d.driver.licences C13 : all x in f 3, 5, 7, 9 g, exists n in MyInts, x - n isin aSetofInts S1 = f 3, 5, 7, 9 g S2 = MyInts P(x, n) = x - n isin aSetofInts r = P(x, n) As we said before, the characterisation of a constraint is a set of paths into the database. For a constraint C, we shall note (C) its characterisation. The set of paths (C) is recursively constructed as follows: k
k
k
j
k
j
(exp1 exp2) = (exp1) [ (exp2), where denotes any comparator or set operation and where exp1 and exp2 denote either a variable or a tuple eld extraction; (card(exp)) = (exp); () = , if name is a schema name and not a class name; = the set of all subclasses of , including itself, if is a class name. 8
(exp.a) = ftype(exp).ag [ (exp), if type(exp) is an abstract type (type() is a function which given an expression returns its corresponding type); = (exp) otherwise. (x) = (S), where x represents a quanti ed variable in the constraint ranging over the set expression S; (P and P') = (P) [ (P'), where P and P' are two literals; (P or P') = (P) [ (P'), where P and P' are two literals; The construction of \(exp.a)" deserves some comments. Such an expression can be either the extraction of a tuple eld (non mutable non shared value), or the extraction of an object feature (mutable and shared value). If the type of the expression is an abstract type, this means that the corresponding value is an object that can be shared. Every update of an object of the same type may violate the constraint. For tuples that are not shared nor mutable, the expression \exp.a" may change only if the expression \exp" changes. The characterisations for the constraint examples are listed in Figure 1. BadDrivers appears in the characterisation of constraints C2, C7, C8 and C12 because it is a subclass of Drivers. The same is true for Planes.
Constraint Characterisation C1 C2 C3 C4 C5 C6 C7 C8 C9 C10 C11 C12 C13 C14
Persons, Person.age Drivers, BadDrivers aSetofInts Licences Persons, Person.children Persons, Person.licences, Licences Drivers, BadDrivers, Person.licences Drivers, BadDrivers, Vehicle.type, Person.licences Vehicles, Planes, Vehicle.number Persons, Person.children, Person.age Persons, Person.spouse Vehicles, Planes, Drivers, BadDrivers, Vehicle.agev, Person.age MyInts, aSetofInts MyInts, aSetofInts Figure 1: Characterisation of constraints
The characterisation of the constraint C1 is made of the paths \Persons" and \Person.age". Indeed, there are two ways to violate the constraint : we can modify the class Persons (add a new person in it, for example) or we can modify the age of an existing person. The two paths in (C1) correspond to these two possibilities. For the constraint C8, we obtain three paths. The rst one expresses that a modi cation of the class Drivers may violate the constraint. The last two paths express that 9
the constraint may also be violated if the feature type of a Vehicle or the feature licences of a Person is updated.
3.2 Characterisation of transactions
In this subsection, we characterise the transactions that can be run on the database. Our goal is to build, for every transaction T, the set (T) of all paths involved in the transaction. Such a path leads to data that the transaction might modify. We rst list the operations performed by a transaction. The set B of elementary statements is de ned as follows: e1 := e2 2 B e1.a := e2 2 B insert e1 into e2 2 B drop e1 from e2 2 B The set I of transactions is recursively constructed as follows: 8 s 2 B, s 2 I 8 s1 , s2 2 I , s 1 ; s 2 2 I 8 s1 , s2 2 I , if (b) s1 else s2 2 I where b is a boolean expression with no side eect. 8 s 2 I , for (o in x) s 2 I In this paper, we suppose that transactions do not call other transactions or functions, so we shall do no interprocedural analysis. The characterisation of a transaction is then de ned as follows: (s1 ; : : : ; s ) = (s1) [: : : [ (s ); (if (b) s1 else s2) = (s1) [ (s2); (for (o in x) s) = (s); (e1 := e2) = (e1); () = ,if name is a schema name and not a class name; = the set of all subclasses of , including itself, if is a class name. (insert e1 into e2) = (e2); (drop e1 from e2) = (e2); This de nition means that the characterisation of a transaction is the union of the characterisations of all the elementary statements involved in it. For an assignment operation like \e1 := e2", the data that can be modi ed is the data corresponding to the expression e1, and it is characterised by (e1). We exemplify this de nition with the examples of transactions above. We have characterised both transactions and constraints as a set of paths into the database that are used by the constraint or involved in the transaction. The characterisation of the constraints which may be violated by a given transaction is then very simple, as stated by the following property: n
n
10
Transaction Characterisation T1 T2 T3 T4
Vehicles Person.spouse Person.licences Persons, Person.age
Figure 2: Characterisation of transactions
Property 1 Let T be a transaction and C be a constraint; the transaction T may violate the constraint C only if (C) \ (T) is non empty. As we said before, this property is only a sucient condition. If we use this property on the running examples, we obtain the following table:
Transaction Constraints T1 T2 T3 T4
C9, C12 C11 C6, C8 C1, C5, C6, C10, C11, C12
Figure 3: Constraints hit by a transaction
4 Enforcement Tests Generation In the previous section, we characterised, for each transaction, a set of constraints that have to be checked at the end of the transaction. We now consider the problem of checking these constraints. Our proposal is based on the idea that all these constraints are satis ed when the transaction begins, and still have to be satis ed after the end of the transaction. The checking mechanism have to take this into account. Informally, we propose to characterise the changes that are made by a transaction and collect (dynamically) some data that represent these changes; at the end of the transaction, these data are used to incrementally check the constraints.
4.1 Restriction of the constraint language
Incremental checking is not possible for every constraint. Existential quanti ers, for example, cannot be simply incrementally checked as illustrated by the following example:
exist x in aSet, Pred(x) 11
If a transaction removes an element from aSet and Pred() is true, then there is no simple and cheap way to ensure that there is still another element that satis es the predicate. In the remaining of this section, we shall thus consider only universally quanti ed and conjunctive constraints.
4.2 Collecting information at runtime
The problem of eciently checking a constraint at the end of a transaction consists in nding the minimal set of objects involved in the process of checking. Then, the constraint will be checked only on this set which guarantees that data consistency is ensured at the end of checking. However, in general, collecting such a set (at run time) is not always possible. In order to illustrate this we use three constraints C1, C11 and C15 and two transactions T2 and T4. For the rst constraint, the minimal set is easily collected at run time (when executing T4). For the second one, the minimal set could be obtained less easily (when executing T2). In the last case, the minimal set cannot be collected (when executing T4)1. (C1) all p in Persons, p.age 130 and p.age 0; (C11) all p in Persons, p.spouse.spouse = p or p.spouse = nil; (C15) all p in Persons, all c in p.children, p.age > c.age
let T2 = trans(p1, p2: Person):
p1.spouse = p2; p2.spouse = p1; /* this transaction performs a marriage between to persons */
let T4 = trans() for p in Persons when (today = p.birthday)
p.age = p.age + 1 /* this transaction updates the age of all Persons born on the current day */
If we consider T4, for the rst constraint, we just have to collect the identi ers of every person whose age is modi ed. The objects collected by this process correspond to the minimal set of objects on which C1 has to be checked. For the second constraint, the minimal set consists of the identi ers of p1 and p2 as well as the identi ers of p1.spouse and p2.spouse before the assignment. Of course, collecting the latter identi ers requires the checker manager to be provided with some kind of \intelligence". Unfortunately, for the third constraint, the strategies adopted previously don't work, due to the fact that we don't have backward pointers and thus given an element of Persons, we are unable to retrieve its father directly. As a consequence, we will not attempt to obtain the minimal set. At the same time, we will not assume the existence of special access structures like indexes or backward pointers. We rather address the problem of nding an ecient checking 1
Unless indices are available, but we choose to ignore this issue.
12
algorithm which can be applied to all constraints. For constraints such as C1, the algorithm will operate on the minimal set of objects; for other constraints, we shall show that the checking algorithm improves the trivial approach which consists in performing a whole scan on the populations involved in the constraints.
4.3 Constraints Transformation
Let T be a transaction, let C be a constraint. We are looking for an algorithm (C) satisfying the following properties: T
The evaluation of T(C) at the end of the transaction ensures that the constraint C is still satis ed. The evaluation of T(C) is more ecient (smaller iteration domain) than the direct evaluation of C.
In order to build T(C) for each constraint C, we will collect the following data during the transaction:
De nition 1 Given a class name \n", we posit n the set of elements which have
been inserted into n during the execution of T. Given an abstract type t, and a feature f of t, we posit tf the set of all instances of t whose feature f has been modi ed during the execution of T.
These sets represent information on the changes that the transaction T has made on the database. typically represents new objects, and tf represents objects that have been updated. Following the restriction described in Section 4.1, a constraint now has the following general form: n
C: all x1 in S1, : : : , all x in S , P(x1: : : x ) n
n
n
The expressions S can be any set expressions involving schema names and iteration variables x1: : : x ?1 as in constraint C15. We rst assume that only S1 is a class name, such that the predicate P involves paths x1.a1 1...a1 1 , ..., x .a 1...a . We de ne (P) by: i
i
;
;k
n
n;
n;kn
De nition 2 (P): x , if ( = x ) then check(P()) all in type 1 a type x a all in a , if ( = x1 .a1 1) then check(P()) ::: x a a ? , if ( = x1.a1 1....a1 ?1 ) then check(P()) all in type a 1;1
1;2
xn:an;1 ::::an;kn?1 >
c.age (C1): f all x in Persons check(C1(x)) all x in Persons, all in Person age if ( = x) then check(C1())g This can be simply rewritten in: (C1): f all x in Persons check(C1(x)) all in Person age if ( in Persons) then check(C1()) g testing for each This algorithm leads to check the constraint on the set Person age element if it belongs to the population Persons. We thus perform as many check operations as the minimal algorithm does. Moreover, we iterate on the set Person age which is, actually, the minimal set of objects involved in the process of checking. This algorithm is thus equivalent to the minimal checking algorithm. Note that the trivial algorithm would have performed as many checks as the number of elements in the class Persons. (C11): f all x in Persons check(C11(x)) all x in Persons, all in Person spouse if ( = x) then check(.spouse.spouse = or .spouse = nil) if (.spouse = x) then check(C11()) g
14
Again this can be rewritten in: (C11):
f
all x in Persons check(C11(x)) all in Person spouse if ( in Persons) then check(C11()) all x in Persons if (x.spouse in Person spouse) then check(C11(x)) g
For this algorithm we have to scan the whole population Persons and test whether an element of Person spouse corresponds to the spouse feature of a given instance of Persons. Again, this algorithm performs as many check operations as the minimal algorithm does. (C15): f all x in Persons check(C15(x)) all x in Persons, all in Person age if ( = x) then check(C15()) if ( in x.children) then check(C15(x, )) all in Person children if ( = x) then check(C15()) g This can be rewritten in: (C15):
f
all x in Persons check(C15(x)) all in Person age if ( in Persons) then check(C15()) all x in Persons all in Person age if ( in x.children) then check(C15()) all in Person children if ( in Persons) then check(C15()) g
This algorithm iterates over three sets: Persons, Person and Person age children. For each element x of Persons whose age has been modi ed we have to check the constraint C15. For each element x of Persons, if one of his/her children's age has been modi ed 15
we have to check if the above constraint is still valid. Last, for each element of Persons whose set of children has been modi ed, we also have to check the constraint. This algorithm performs as many check operations as the minimal algorithm does. However, the set of scanned objects is larger than the minimal set of objects. As we said in Section 4.2, in the general case, such minimal sets cannot be obtained unless indices are available.
5 Conclusion In this paper, we have considered the problem of eciently checking integrity constraints in database programming languages. We have shown how to reduce the number of constraints to be checked by characterising the portions of the database that are concerned by the constraints and involved in a transaction. We then have shown a way to generate ecient algorithms for checking a large class of constraints (universally quanti ed conjunctive constraints). This work is a preliminary work on the speci cation of a compiler for the language proposed in [10]. In order to get complete speci cations, we will extend our techniques in the following directions. In Section 3, we assumed that characterisations were attached to transactions and that transactions did not contain calls to functions and/or other transactions. We did not consider methods (that is n-ary features). The techniques described in Section 3 can be extended to deal with functions and methods in a straightforward manner by associating characterisations to functions and methods. Our work on static characterisation can also be extended by considering some theorem proving techniques [14, 13, 1], thus re ning the set of constraints to be checked. Such techniques, however, are outside the scope of this work. For the optimisation of constraint checking, we shall work in three dierent directions. First, we will try to extend our result in the case of quanti ers over general set expressions (not only class names). In the present work, we do not optimise such complex iterations. Then, we will consider the case of constraints with negation and/or existential quanti ers. Finally, the data collected during the execution of a transaction is somehow rough in our work. We will consider some more sophisticated ways of collecting informations during the execution of the transactions that could give more information on constraints and thus speed up their checking. In order to be able to estimate the impact of such extensions, we will try, at the same time, to formalise the cost (in time and space) of constraints veri cations. Such a formalisation would allow, for example, to make some trade-o between time (veri cation at the end of a transaction) and space (amount of data collected during the transaction). Finally, in order to get more results on constraint optimisation, we will continue to compare our work with the more general domain of logic programs compilation. We think that, although the underlying data models and type systems are very dierent, there are enough commonalities between these two problems to get some more results. It is, however, interesting to note that we already got signi cant 16
optimisations for a large class of constraints with only simple compilation techniques.
6 Aknowledgements We are thankful to C. Collet for helpful comments and careful reading of the paper and to E. Simon and J. M. Larchev^eque for helpful suggestions.
References [1] P. A. Bernstein and B. T. Blaustein. Fast methods for testing quanti ed relational calculus expressions. In ACM SIGMOD International Conference, Orlando, Florida, June 1982. [2] F. Bry, H. Decker, and R. Manthey. A Uniform Approach to Constraint Satisfaction and Constraint Satis ability in Deductive Databases. In EDBT International Conference, 1988. [3] F. Bry and R. Manthey. Checking Consistency of Database Constraints: A Logical Basis. In VLDB International Conference, 1986. [4] L. Cardelli. A Semantics of Multiple Inheritance. In Semantics of Data Types. Springer-Verlag, 1984. [5] G. Gardarin and M. Melkano. Proving the Consistency of Database Transactions. In VLDB International Conference, Rio, Brasil, October 1979. [6] A. Hsu and T. Imielinski. Integrity Checking for Multiple Updates. In ACM SIGMOD International Conference, 1985. [7] R. Hull, R. Morrison, and D. Stemple, editors. International Workshop on Database Programming Languages. Morgan Kaufmann, 1989. [8] R. Kowalski, F. Sadri, and P. Soper. Integrity Checking in Deductive Databases. In VLDB International Conference, 1987. [9] C. Lecluse and P. Richard. Modeling Complex Structures in Object-Oriented Databases. In ACM PODS International Conference, March 1989. [10] C. Lecluse and P. Richard. Data Base Schemas and Types Systems for DBPLs, a De nition and its Applications. Technical report, GIP Altar, June 1990. [11] H. Martin. Contr^ole de la coherence dans les bases objets : Une approche par le comportement. PhD thesis, Universite Joseph-Fourier - Grenoble I, 1991. [12] J.M. Nicolas. Logic for Improving Integrity Checking in Relational Databases. Technical report, ONERA-CERT, 1979. 17
[13] T. Sheard and D. Stemple. Automatic Veri cation of Database Transaction Safety. ACM Transactions on Database Systems, 14(3), September 1989. [14] E. Simon and P. Valduriez. Design and Analysis of a Relational Integrity Subsystem. Technical Report DB-015-87, MCC, 1987. [15] M. Stonebraker. Implementation of Integrity Constraints and Views by Query Modi cation. In ACM SIGMOD International Conference, San Jose, California, May 1975. [16] W. Weber, W. Stugky, and J. Karzt. Integrity Checking in database systems. Information Systems, 8(2), 1983.
18