ampersand-3.0.3: src/Database/Design/Ampersand/FSpec/FSpecAux.hs
module Database.Design.Ampersand.FSpec.FSpecAux
(getDeclarationTableInfo,getConceptTableInfo)
where
import Database.Design.Ampersand.Basics
import Database.Design.Ampersand.Core.AbstractSyntaxTree
import Database.Design.Ampersand.Classes.Relational(isTrue)
import Database.Design.Ampersand.FSpec.ToFSpec.NormalForms(disjNF)
import Database.Design.Ampersand.FSpec.Plug(plugpath)
import Database.Design.Ampersand.FSpec.FSpec
import Data.List
fatal :: Int -> String -> a
fatal = fatalMsg "FSpecAux"
--WHY bestaat sqlRelPlugs?
-- | sqlRelPlugs levert alle mogelijkheden om een plug met twee velden te vinden waarin (primitieve) expressie e is opgeslagen.
-- | sqlRelPlugs mag alleen gebruikt worden voor primitieve expressies EDcD, EDcI, en EDcV
-- | Als (plug,sf,tf) `elem` sqlRelPlugs fSpec e, dan geldt e = (fldexpr sf)~;(fldexpr tf)
-- | Als sqlRelPlugs fSpec e = [], dan volstaat een enkele tabel lookup niet om e te bepalen
-- | Opletten dus, met de nieuwe ISA-structuur van 2013, omdat daarin tabellen bestaan met disjuncte verzamelingen...
sqlRelPlugs :: FSpec -> Expression -> [(PlugSQL,SqlField,SqlField)] --(plug,source,target)
sqlRelPlugs fSpec e
= [ (plug,fld0,fld1)
| InternalPlug plug<-plugInfos fSpec
, (fld0,fld1)<-sqlPlugFields fSpec plug e
]
-- return table name and source and target column names for relation rel, or nothing if the relation is not found
getDeclarationTableInfo :: FSpec -> Declaration -> (PlugSQL,SqlField,SqlField)
getDeclarationTableInfo fSpec decl =
case decl of
Sgn{} ->
case sqlRelPlugs fSpec (EDcD decl) of
[plugInfo] -> plugInfo
[] -> fatal 527 "Reference to a non-existing plug."
[(t1,src1,trg1),(t2,src2,trg2)]
-> if t1 ==t2 && src1 == trg2 && trg1 == src2
then (t1,src1,trg1)
else fatal 426 $ "Multiple plugs for relation "++ show decl ++"\n" ++
intercalate "\n\n" (map showPInfo [(t1,src1,trg1),(t2,src2,trg2)])
pinfos -> fatal 428 $ "Multiple plugs for relation "++ show decl ++"\n" ++
intercalate "\n\n" (map showPInfo pinfos)
-- TODO: some relations return multiple plugs (see ticket #217)
_ -> fatal 420 "getDeclarationTableInfo must not be used on this type of declaration!"
where
showPInfo (tab, src, trg) = intercalate " \n"
[ "Table: "++name tab
, " sourceField: "++fldname src
, " targetField: "++fldname trg
]
getConceptTableInfo :: FSpec -> A_Concept -> (PlugSQL,SqlField)
getConceptTableInfo fSpec cpt
= case lookupCpt fSpec cpt of
[] -> fatal 55 $ "No plug found for concept '"++name cpt++"'."
(x:_) -> x --Any of the resulting plugs should do.
--iff proven that e is equivalent to plugexpr
-- AND not proven that e is not equivalent to plugexpr
--then return (fld0,fld1)
--TODO -> can you prove for all e whether e is equivalent to plugexpr or not?
sqlPlugFields :: FSpec -> PlugSQL -> Expression -> [(SqlField, SqlField)]
sqlPlugFields fSpec p e' =
let e = disjNF (getOpts fSpec) e' -- SJ20140207 Why is this normalization necessary?
in nub
[(fld0,fld1)
| fld0<-[f |f<-plugFields p,target (fldexpr f)==source e] --fld0 must be a field matching the source of e
, fld1<-[f |f<-plugFields p,target (fldexpr f)==target e] --fld1 must be a field matching the target of e
, Just plugexpr <- [plugpath p fld0 fld1] --the smallest expression from fld0 to fld1 (both in same plug)
, let se = fldexpr fld0
te = fldexpr fld1
bs = (isTrue.disjNF (getOpts fSpec)) (notCpl e .\/. flp se .:. te) -- e |- se~;te
bt = (isTrue.disjNF (getOpts fSpec)) (notCpl (flp se .:. te) .\/. e) -- se~;te |- e
, --reasons why e is equivalent to plugexpr:
--because e and plugexpr are equal
e==plugexpr
-- || because1 e fld0 fld1
--OR e is equivalent to plugexpr for some other reason (requires reasoning)
|| bs && bt ] -- e = se~;te
{- the above should be enough.. but the relation algebra calculations
are not good enough yet. In particular:
isFalse ((I/\x);e /\ -e)
and
isTrue ((I/\e;e~);e \/ -e)
do not work (these should yield True instead of False in both cases)
The code below fixes exactly these ommissions
--
|| (isProp (se) && (te == e)
&& (isTrue$disjNF (getOpts fSpec)$ let c = source e in (EDcI c ./\. simplF [e,flp e] ) .\/. notCpl se))
|| (isProp (te) && se==flp e
&& (isTrue$disjNF (getOpts fSpec)$ let c = source e in (EDcI c ./\. simplF [e,flp e] ) .\/. notCpl te))
-- found another exception:
-- isFalse (I;I /\ -I)
-- and
-- isTrue (I;I \/ -I)
-- yield False, but should yield True
--
|| ( (se == te) && isIdent e && (isSur se) )
, --TODO -> reasons why e is not equivalent to plugexpr:
True
]
where
-- simplF: replace a;a~ by I if INJ&TOT
simplF ks = simplify ( if null fs || null (head fs) then replF ks else replF $ head fs )
where fs = [ts | ECps ts <- [simplify $ ECps ks]] -- if null, replF will probably not do a lot.
simplF ks = case simplify (foldr1 .:. ks) of
t@ECps{} -> simplify (replF (exprCps2list t))
_ -> simplify (replF ks)
-- null occurs especialy in cases of [I;e] and [e;I]
replF [k:k2] | k == flp k2 && isInj k && isTot k = EDcI (source k)
replF (k:k2:ks) | k == flp k2 && isInj k && isTot k = replF ks
replF [a] = a
replF (k:k2:ks) | fs /= [k2:ks]
= case res of ECps{} -> replF (exprCps2list res) ; _ -> ECps (k,res)
where res = replF (k2:ks)
fs = case res of ECps{} -> [exprCps2list res] ; _ -> []
replF [] -- this should not occur here, and if it does, it might cause errors in other code that should be solved here
= fatal 542 "Could not define a properly typed I for ECps[] in replF in sqlPlugFields in Prototype/RelBinGenSQL.hs"
-- this error does not guarantee, however, that simplF yields no ECps []. In particular: simplify (ECps [I;I]) == ECps []
replF ks = ECps (ks)
-----------------
-}