Commit 963df404 authored by Michael Hanus 's avatar Michael Hanus
Browse files

ertools, peval, xmldata removed since it is available via CPM

parent 35704d59
......@@ -4,6 +4,8 @@
Curry_Main_Goal.curry
*.agdai
*_cache
optimize/.cpm/packages/cass-0.0.1
optimize/.cpm/packages/cass-analysis-0.0.4
# executables
addtypes/AddTypes
......@@ -19,17 +21,11 @@ cpm/src/CPM.Main
cpns/CPNSD
curry2js/Curry2JS
currypp/Main
currypp/ContractWrapper/cwrapper
currycheck/CurryCheck
currydoc/CurryDoc
currytest/CurryTest
cusage/CheckUsage
ertools/ERD2Curry
importcalls/ImportCalls
optimize/BindingOpt
runcurry/RunCurry
spicey/spiceup
verification/ToVerifier
www/Registry
www/SubmitForm
xmldata/Data2Xml
......@@ -82,7 +82,7 @@ $(uninstall_TOOLDIRS):
# Testing the tools
# Tools with test suites:
TESTTOOLS = optimize currypp runcurry currycheck xmldata cpm
TESTTOOLS = optimize currypp runcurry currycheck cpm
# run the test suites to check the tools
.PHONY: runtest
......
......@@ -3,6 +3,17 @@
CURRYBIN="../../../../bin"
ERD2CURRY=$HOME/.cpm/bin/erd2curry
if [ ! -x "$ERD2CURRY" ] ; then
ERD2CURRY=$CURRYBIN/$CURRYSYSTEM-erd2curry
if [ ! -x "$ERD2CURRY" ] ; then
echo "SQL integration not tested: no executable 'erd2curry' found!"
echo "To run the SQL integration test, install 'erd2curry' by:"
echo "> cpm installbin ertools"
exit
fi
fi
ALLTESTS="test*.curry"
VERBOSE=no
......@@ -25,7 +36,7 @@ cleandir () {
exectests() {
cleandir
# compile model:
$CURRYBIN/curry erd2curry --db `pwd`/Uni.db --cdbi UniERD.curry
"$ERD2CURRY" --db `pwd`/Uni.db --cdbi UniERD.curry
# fill database:
$CURRYBIN/curry $REPL_OPTS :l CreateData :eval createTestData :q
# run query tests:
......
......@@ -34,8 +34,12 @@ rm -rf .git*
rm -rf .cpm/*_cache
rm -rf .cpm/packages/*/.git*
cd .cpm/packages
mv cass-analysis-* cass-analysis
mv cass-*\.*\.* cass
CANAV=`ls -d cass-analysis-*`
mv $CANAV cass-analysis
CASSV=`ls -d cass-*\.*\.*`
mv $CASSV cass
ln -s cass-analysis $CANAV
ln -s cass $CASSV
cd ../..
cd ..
mv Makefile.optimize optimize/Makefile
......
#****************************************************************************
# The ERD2Curry tool transforms an ERD term into datatypes for CDBI.
# It also creates an information file for the currypp SQL-Parser.
# If the tool is invoked with the option --db, it creates a
# corresponding sqlite database.
#
#****************************************************************************
# The tool name of the application:
TOOL = $(BINDIR)/$(CURRYSYSTEM)-erd2curry
# The load path of the application:
LOADPATH = src
# Some modules required by the erd2curry tool:
DEP = src/*.curry \
$(ROOT)/lib/Database/ERD*.curry $(ROOT)/lib/Database/CDBI/*.curry \
$(LIBDIR)/AbstractCurry/*.curry
.PHONY: all compile install clean uninstall
all: install
compile: ERD2Curry
install: compile
rm -f $(TOOL)
cd $(BINDIR) && ln -s ../currytools/ertools/ERD2Curry $(notdir $(TOOL))
clean:
cd src && $(CLEANCURRY)
uninstall: clean
rm -f ERD2Curry $(TOOL)
ERD2Curry: $(DEPS)
$(REPL) $(REPL_OPTS) :set path $(LOADPATH) :load ERD2Curry :save :quit
ER Tools for Curry
==================
This directory contains tools for dealing with database applications
specified by entity-relationship diagrams (ERDs) in Curry programs.
----------------------------------------------------------------------
ERD2CDBI compiler
-----------------
This tool transforms an ERD term into datatypes used in the
Database.CDBI. libraries. It also creates an information file
for the currypp SQL-Parser. This compiler is invoked by
erd2curry --db <sqlite3 db file> --cdbi <Curry program with ERD>
----------------------------------------------------------------------
ERD2Curry compiler
------------------
This is a compiler for database applications
specified by entity-relationship diagrams (ERDs) into Curry programs.
The basic ideas and details about this approach are described in
B. Brassel, M. Hanus, M. Mueller:
High-Level Database Programming in Curry
In Proc. of the Tenth International Symposium on
Practical Aspects of Declarative Languages (PADL 2008), pp. 316-332,
Springer LNCS 4902, 2008
Usage information:
------------------
Default use without Umbrello UML Modeller but ERD term descriptions:
1. Go to the directory where you want to create your Curry program and
create an ERD description as a Curry term of type ERD
(w.r.t. type definition given in module ERD.curry), e.g.,
stored in "mymodel.erdterm".
2. Compile it into a Curry program with
erd2curry mymodel.erdterm
This generates two auxiliary files `ERDGeneric.curry` and
`KeyDatabase.curry` and a main file `<Model name>.curry>`
containing the Curry API operations to the database.
Use with Umbrello UML Modeller (no longer actively supported):
1. Create an XML description of the ERD (with Umbrello)
in xmi format, e.g., stored in "mymodel.xmi".
2. Compile it into a Curry program with
erd2curry -x myerd.xmi
Visualization:
--------------
To visualize an ERD term file as a graph with dotty, execute
erd2curry -v mymodel.erdterm
Examples:
---------
The directory `examples` contains two examples for the specification
of ERD models:
* `BlogERD.curry`: a simple ERD model for a blog with entries, comments,
and tags.
* `UniERD.curry`: an ERD model for university lectures as
presented in the original paper cited above.
---
Further infos and contact:
Michael Hanus <http://www.informatik.uni-kiel.de/~mh>
This directory contains some documention for the tool:
manual.tex:
A short description to be included in the main manual of the Curry system.
\ No newline at end of file
\section{ERD2Curry: A Tool to Generate Programs from ER Specifications}
\label{sec-erd2curry}
ERD2Curry\index{ERD2Curry}\index{database programming}
is a tool to generate Curry code to access and manipulate data
persistently stored from
entity relationship diagrams.\index{entity relationship diagrams}
The idea of this tool is described in detail in
\cite{BrasselHanusMueller08PADL}.
Thus, we describe only the basic steps to use this tool
in the following.
If one creates an entity relationship diagram (ERD)
with the Umbrello UML Modeller, one has to store its
XML description in XMI format (as offered by Umbrello)
in a file, e.g., \ccode{myerd.xmi}.
This description can be compiled into a Curry program by the
command\pindex{curry erd2curry}\pindex{erd2curry}
\begin{curry}
curry erd2curry -x myerd.xmi
\end{curry}
If \code{MyData} is the name of the ERD, the Curry program file
\ccode{MyData.curry} is generated containing all the necessary
database access code as described in \cite{BrasselHanusMueller08PADL}.
In addition to the generated Curry program file,
two auxiliary program files
\code{ERDGeneric.curry} and \code{KeyDatabase.curry}
are created in the same directory.
If one does not want to use the Umbrello UML Modeller,
which might be the preferred method since the interface to the
Umbrello UML Modeller is no longer actively supported,
one can also define an ERD in a Curry program as a (exported!)
top-level operation of type \code{ERD}
(w.r.t.\ the type definition given in the library
\code{\cyshome/lib/Database/ERD.curry}).
If this definition is stored in the Curry program file \ccode{MyERD.curry},
it can be compiled into a Curry program by the
command\pindex{curry erd2curry}
\begin{curry}
curry erd2curry MyERD.curry
\end{curry}
%
The directory \code{\cyshome/currytools/erd2curry/}
contains two examples for such ERD program files:
\begin{description}
\item[\code{BlogERD.curry}:]
This is a simple ERD model for a blog with entries, comments,
and tags.
\item[\code{UniERD.curry}:]
This is an ERD model for university lectures as
presented in the paper \cite{BrasselHanusMueller08PADL}.
\end{description}
%
There is also the possibility to visualize an ERD term
as a graph with the graph visualization program \code{dotty}
(for this purpose, it might be necessary to adapt the definition
of \code{dotviewcommand} in your \ccode{\curryrc} file,
see Section~\ref{sec-customization},
according to your local environment).
The visualization can be performed by the command
\begin{curry}
curry erd2curry -v MyERD.curry
\end{curry}
import Database.ERD
blogERD :: ERD
blogERD =
ERD "Blog"
[Entity "Entry"
[Attribute "Title" (StringDom Nothing) Unique False,
Attribute "Text" (StringDom Nothing) NoKey False,
Attribute "Author" (StringDom Nothing) NoKey False,
Attribute "Date" (DateDom Nothing) NoKey False],
Entity "Comment"
[Attribute "Text" (StringDom Nothing) NoKey False,
Attribute "Author" (StringDom Nothing) NoKey False,
Attribute "Date" (DateDom Nothing) NoKey False],
Entity "Tag"
[Attribute "Name" (StringDom Nothing) Unique False]
]
[Relationship "Commenting"
[REnd "Entry" "commentsOn" (Exactly 1),
REnd "Comment" "isCommentedBy" (Between 0 Infinite)],
Relationship "Tagging"
[REnd "Entry" "tags" (Between 0 Infinite),
REnd "Tag" "tagged" (Between 0 Infinite)]
]
------------------------------------------------------------------------------
--- Generic operations and integrity tests
--- to support the database code generated from ERDs
------------------------------------------------------------------------------
module ERDGeneric where
import KeyDatabaseSQLite
import List
import ReadShowTerm
import Read
import Char(isDigit)
------------------------------------------------------------------------------
-- Handling of database keys
--- The general type of database keys.
type Key = Int
--- Shows a database key for an entity name as a string.
--- Useful if a textual representation of a database key is necessary,
--- e.g., as URL parameters in web pages. This textual representation
--- should not be used to store database keys in attributes!
showDatabaseKey :: String -> (enkey -> Key) -> enkey -> String
showDatabaseKey en fromenkey enkey = en ++ show (fromenkey enkey)
--- Transforms a string into a key for an entity name.
--- Nothing is returned if the string does not represent a reasonable key.
readDatabaseKey :: String -> (Key -> enkey) -> String -> Maybe enkey
readDatabaseKey en toenkey s =
let (ens,ks) = splitAt (length en) s
in if ens==en && all isDigit ks then Just (toenkey (readNat ks))
else Nothing
------------------------------------------------------------------------------
-- Generic operations to modify the database
--- Insert a new entity and assign a new key for it.
newEntry :: (Key -> t -> Dynamic) -> (Key -> t -> en) -> t -> Transaction en
newEntry pred info2entry info =
newDBEntry pred info |>>= \k -> returnT (info2entry k info)
-- Insert new relationship represented as an entity.
newEntryR :: (Key -> (a,b) -> Dynamic) -> a -> b -> Transaction ()
newEntryR entrypred key1 key2 = newDBEntry entrypred (key1,key2) |>> doneT
getEntry :: (Key -> t -> Dynamic) -> (Key -> t -> en) -> Key -> Transaction en
getEntry pred info2entry key = seq pred $ seq key $
getDB (getDBInfo pred key) |>>=
maybe (errorT (TError KeyNotExistsError
("database contains no entry for key: "++show key)))
(\info -> returnT (info2entry key info))
-- Delete a relationship represented as an entity.
-- If the relationship does not exist, a NoRelationshipError is raised.
deleteEntryR :: (Key -> (a,b) -> Dynamic) -> a -> b -> Transaction ()
deleteEntryR entrypred key1 key2 =
getDB (transformQ (map fst . filter (\ (_,i) -> i==(key1,key2)))
(allDBKeyInfos entrypred)) |>>= \kis ->
if null kis
then errorT (TError NoRelationshipError
("relationship for deletion not found for keys: "
++show key1++" "++show key2))
else deleteDBEntries entrypred kis
------------------------------------------------------------------------------
-- Generic integrity tests for keys.
-- If there is no entry with a given key, raise a transaction error.
existsEntryWithDBKey :: String -> (Key -> t -> Dynamic) -> Key -> Transaction ()
existsEntryWithDBKey ename entrypred key =
getDB (getDBInfo entrypred key) |>>=
maybe (errorT (TError KeyNotExistsError
("database contains no entry for key: "++show key
++" in table: "++ename)) )
(const doneT)
-- If a given key occurs in a (foreign key) attribute of an entity,
-- raise a transaction error.
requiredForeignDBKey :: String -> (Key -> t -> Dynamic) -> (Key -> t -> en)
-> (en -> k) -> k -> Transaction ()
requiredForeignDBKey ename entrypred info2entry keyf key =
getDB (getAllEntities entrypred info2entry) |>>= \ens ->
if null (filter (\e -> keyf e == key) ens)
then doneT
else errorT (TError KeyRequiredError
("key: "++show key ++ " required in table: " ++ ename))
getAllEntities :: (Key -> t -> Dynamic) -> (Key -> t -> en) -> Query [en]
getAllEntities entrypred info2entry =
transformQ (map (uncurry info2entry)) (allDBKeyInfos entrypred)
duplicateKeyTest :: (Key -> t -> Dynamic) -> Transaction ()
duplicateKeyTest pred =
getDB (allDBKeys pred) |>>= \keys ->
if length (nub keys) == length keys
then doneT
else errorT (TError DuplicateKeyError
("database contains duplicate key for table: "
++show pred))
duplicatePTest :: [a] -> Transaction ()
duplicatePTest xs =
if length (nub xs) == length xs
then doneT
else errorT (TError DuplicateKeyError "duplicate parameters in new-function")
-------------------------------------------------------------------------
-- Uniqueness tests.
-- Test whether an attribute value does not yet exist
unique :: String -> (Key -> t -> Dynamic) -> (Key -> t -> en) -> (en -> a) -> a
-> Transaction ()
unique ename entrypred info2entry selector attrval =
getDB (allDBKeyInfos entrypred) |>>= \kis ->
if null (filter (\e -> selector e == attrval)
(map (\(k,i) -> info2entry k i) kis))
then doneT
else errorT (TError UniqueError
(ename++" entry for unique attribute "
++show attrval++" already exists"))
uniqueUpdate :: String -> (Key -> t -> Dynamic) -> (Key -> t -> en)
-> (en -> Key) -> (en -> a) -> en -> Transaction ()
uniqueUpdate ename entrypred info2entry keyf selector obj =
let oldkey = keyf obj
in
getDB (getDBInfo entrypred oldkey) |>>=
maybe (errorT (TError KeyNotExistsError
("database contains no entry for key: "++show oldkey)))
(\oldt -> getDB (allDBKeyInfos entrypred) |>>= \kis ->
let oldentry = info2entry oldkey oldt
entries = filter (\e -> selector obj == selector e)
(map (uncurry info2entry) kis)
in if null entries ||
(length entries == 1 && selector oldentry == selector obj)
then doneT
else errorT (TError UniqueError
(ename++" entry for unique attribute "
++show (selector obj)++" already exists")))
uniqueC :: String -> (Key -> t -> Dynamic) -> (Key -> t -> en)
-> (en -> a) -> en -> Transaction ()
uniqueC ename entrypred info2entry selector obj =
getDB (allDBKeyInfos entrypred) |>>= \kis ->
let entries = filter (\e -> selector obj == selector e)
(map (uncurry info2entry) kis)
in if length entries <= 1
then doneT
else errorT (TError UniqueError
(ename++" unique attribute "
++show (selector obj)++" is not unique"))
-- Uniqueness of a combination of two attributes.
-- Check whether this combination already exists.
-- If it exists, a transaction error is generated, otherwise everything is ok.
unique2 :: (Key -> (a,b) -> Dynamic) -> a -> b -> Transaction ()
unique2 entrypred k1 k2 =
getDB (allDBInfos entrypred) |>>= \is ->
if null (filter (== (k1,k2)) is)
then doneT
else errorT (TError UniqueError "relationship already exists")
unique2C :: (Key -> (a,b) -> Dynamic) -> a -> b -> Transaction ()
unique2C entrypred k1 k2 =
getDB (allDBInfos entrypred) |>>= \is ->
if length (filter (== (k1,k2)) is) > 1
then errorT (TError UniqueError "relationship not unique")
else doneT
-------------------------------------------------------------------------
-- Maximum and minimum tests.
maxPTest :: Int -> [a] -> Transaction ()
maxPTest max xs =
if length xs > max
then errorT (TError MaxError "max reached in parameter list in new function")
else doneT
maxTest :: String -> (Key -> t -> Dynamic) -> (Key -> t -> en)
-> (en -> a) -> Int -> a -> Transaction ()
maxTest ename entrypred info2entry selector max attr =
getDB (getAllEntities entrypred info2entry) |>>= \es ->
let entries = filter (\e -> attr == selector e) es in
if length entries < max
then doneT
else errorT (TError MaxError ("max reached for attribute "
++show attr++" in entity "++ename))
maxTestUpdate :: String -> (Key -> t -> Dynamic) -> (Key -> t -> en)
-> (en -> Key) -> (en -> a) -> Int -> en -> Transaction ()
maxTestUpdate ename entrypred info2entry keyf selector max obj =
getDB (getAllEntities entrypred info2entry) |>>= \es ->
let entries = filter (\e -> selector obj == selector e) es in
getEntry entrypred info2entry (keyf obj) |>>= \old ->
if (length entries < max
|| (length entries == max && selector old == selector obj))
then doneT
else errorT (TError MaxError ("max reached for attribute "
++show (selector obj)++" in entity "++ename))
maxTestC :: String -> (Key -> t -> Dynamic) -> (Key -> t -> en)
-> (en -> a) -> Int -> a -> Transaction ()
maxTestC ename entrypred info2entry selector max attr =
getDB (getAllEntities entrypred info2entry) |>>= \es ->
if length (filter (\e -> selector e == attr) es) <= max
then doneT
else errorT (TError MaxError ("maximum exceeded for attribute "
++show attr++" in entity "++ename))
minTestC :: String -> (Key -> t -> Dynamic) -> (Key -> t -> en)
-> (en -> a) -> Int -> a -> Transaction ()
minTestC ename entrypred info2entry selector min attr =
getDB (getAllEntities entrypred info2entry) |>>= \es ->
if length (filter (\e -> selector e == attr) es) >= min
then doneT
else errorT (TError MinError ("below min for attribute "
++show attr++" in entity "++ename))
-- Maximum test before inserting a relationship with a given key:
maxTestInsert :: String -> (Key -> t -> Dynamic) -> (Key -> t -> en)
-> (en -> a) -> Int -> a -> Transaction ()
maxTestInsert ename entrypred info2entry selector maxrange attr =
getDB (getAllEntities entrypred info2entry) |>>= \es ->
if length (filter (\e -> selector e == attr) es) < maxrange
then doneT
else errorT (TError MaxError ("maximum reached for attribute "
++show attr++" in entity "++ename))
-- Minimum test before deleting a relationship
minTestDelete :: String -> (Key -> t -> Dynamic) -> (Key -> t -> en)
-> (en -> a) -> Int -> a -> Transaction ()
minTestDelete ename entrypred info2entry selector min attr =
getDB (getAllEntities entrypred info2entry) |>>= \es ->
if length (filter (\e -> selector e == attr) es) > min
then doneT
else errorT (TError MinError ("below min for attribute "
++show attr++" in entity "++ename))
-------------------------------------------------------------------------
-- Saving and restoring dynamic predicates.
saveDBTerms :: String -> String -> (Key -> a -> Dynamic)
-> (Key -> a -> _) -> IO ()
saveDBTerms path ename dynpred toentity = do
keyinfos <- runQ (allDBKeyInfos dynpred)
let savefile = path++"/"++ename++".terms"
terms = map (uncurry toentity) keyinfos
if null path
then putStrLn (unlines (map showQTerm terms)) -- show only
else do putStrLn $ "Saving into "++savefile
writeQTermListFile savefile terms
restoreDBTerms :: String -> String -> (Key -> a -> Dynamic)
-> (en->Key) -> (en->a) -> IO ()
restoreDBTerms path ename dynpred enkey eninfo = do
let savefile = path++"/"++ename++".terms"
putStrLn $ "Restoring from "++savefile
terms <- readQTermListFile savefile
runJustT (mapT_ (\t -> newDBKeyEntry dynpred (enkey t) (eninfo t)) terms)
restoreDBRelTerms :: String -> String -> (Key -> a -> Dynamic)
-> (en->a) -> IO ()
restoreDBRelTerms path ename dynpred eninfo = do
let savefile = path++"/"++ename++".terms"
putStrLn $ "Restoring from "++savefile
terms <- readQTermListFile savefile
runJustT (mapT_ (\t -> newDBEntry dynpred (eninfo t)) terms)
-------------------------------------------------------------------------
-- If the second argument is a null string, return the first argument
-- (the default string), otherwise return the second argument.
defaultString :: String -> String -> String
defaultString def s = if null s then def else s
-------------------------------------------------------------------------
import Database.ERD
uniERD :: ERD
uniERD =
ERD "Uni"
[Entity "Student" [Attribute "MatNum" (IntDom Nothing) PKey False,
Attribute "Name" (StringDom Nothing) NoKey False,
Attribute "Firstname" (StringDom Nothing) NoKey False,
Attribute "Email" (UserDefined "MyModule.Email" Nothing)
NoKey True],
Entity "Lecture" [Attribute "Id" (IntDom Nothing) PKey False,
Attribute "Title" (StringDom Nothing) Unique False,
Attribute "Hours" (IntDom (Just 4)) NoKey False],
Entity "Lecturer" [Attribute "Id" (IntDom Nothing) PKey False,
Attribute "Name" (StringDom Nothing) NoKey False,
Attribute "Firstname" (StringDom Nothing) NoKey False],
Entity "Group" [Attribute "Time" (StringDom Nothing) NoKey False]]
[Relationship "Teaching"
[REnd "Lecturer" "taught_by" (Exactly 1),
REnd "Lecture" "teaches" (Between 0 Infinite)],
Relationship "Participation"
[REnd "Student" "participated_by" (Between 0 Infinite),
REnd "Lecture" "participates" (Between 0 Infinite)],
Relationship "Membership"
[REnd "Student" "consists_of" (Exactly 3),
REnd "Group" "member_of" (Between 0 Infinite)]]
{
"name": "ertools",
"version": "1.0.0",
"author": "Michael Hanus <mh@informatik.uni-kiel.de>",
"synopsis": "Tools for dealing with database applications specified by entity-relationship diagrams",
"category": [ "Database" ],
"dependencies": {
},
"configModule": "ERToolsPackageConfig",
"executable": {
"name": "erd2curry",
"main": "ERD2Curry"
},
"source": {
"git": "https://git.ps.informatik.uni-kiel.de/curry-packages/ertools.git",
"tag": "$version"
}
}
This diff is collapsed.
This diff is collapsed.
module ERD2Curry( main, erd2curryWithDBandERD )
where
import AbstractCurry.Files (readCurry)
import AbstractCurry.Select (imports)
import AbstractCurry.Pretty
import Database.ERD
import Database.ERDGoodies
import Directory
import Distribution (curryCompiler, installDir)