Skip to content

Commit

Permalink
Implement coexistence of created tables with and without keys
Browse files Browse the repository at this point in the history
KWDRRelationCreationRule
- clarification des comemntaire sur l'utilisation des cles et de Root pour les dictionnaire des tables creees par des regles

En resume:
- un dictionnaire peut ne pas avoir de cle s'il est cree
- verification a priori lors de la lecture d'un fichier dictionnaire
  - verification des cles pour les variables natives (non calculee) de type relation
  - contrainte uniquement si le dictionnaire utilisant a une cle
    - cree une contrainte sur la table calculee
      - peut ne pas avoir de cle
      - si elle une cle, ses sous-table doivent egalement en avoir
- verification a posteriori sur l'usage d'une flocon via le choix d'une dictionaire d'analyse
  - ok si coherence des cle, permet la lecture a partir de fichier
  - ko si des variable natives ne peuvent etre lues en rasion d'un manque de cle
- gestion des tables externes (Root)
  - interdiction de creer une table associee a un dictionnaire externe (Root)
    - sinon, incoherence de la gestion des references au objets externes, non rpesents dans des fichiers
  - attention, lors de l'analyse recursive d'une dictuionnaire d'analyse
    - on se limite aux table natives pour en deduire le flcon
    - il faut par contre parcourir entierement les tables calculees ou non pour en deduire les tables externes a utliser

Impacts
- interdiction de creer une table Root
  - KWDRRelationCreationRule::CheckOperandsCompleteness
- correction pour prendre en compte les regles associees a des blocs d'attributs
  - KWAttribute::GetReference
- tolerance sur la verification des cles
  - KWAttribute::Check
- amelioration des messages d'erreur et de warning
  - KWAttribute::Check
  - KWClass:CheckClassComposition
- extension en prenant en compte les tables externes references depuis des attributs table creees par des regles
  - KWClass::ComputeOverallNativeRelationAttributeNumber
  - KWMTDatabase::CreateMapping
  • Loading branch information
marcboulle committed Aug 12, 2024
1 parent 8736290 commit aa71d2a
Show file tree
Hide file tree
Showing 8 changed files with 179 additions and 46 deletions.
17 changes: 11 additions & 6 deletions src/Learning/KWData/KWAttribute.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -293,18 +293,23 @@ boolean KWAttribute::Check() const
}

// La classe utilisee doit avoir une cle dans le cas d'un attribut natif
// si la classe utilisante a elle meme une cle
// Exception dans le cas d'une classe sans-cle pouvant etre issue d'une regle de creation de table,
// auquel cas les cles ne sont pas necessaire y compris dans le cas d'un flocon creer par des regles
if (GetAnyDerivationRule() == NULL and attributeClass != NULL and
attributeClass->GetKeyAttributeNumber() == 0)
attributeClass->GetKeyAttributeNumber() == 0 and GetParentClass() != NULL and
GetParentClass()->GetKeyAttributeNumber() > 0)
{
AddError("The used dictionary " + attributeClass->GetName() + " should have a key");
AddError("The used dictionary " + attributeClass->GetName() +
" should have a key as the parent dictionary " + parentClass->GetName() +
" has a key");
bOk = false;
}

// La cle de la classe utilisee doit etre au moins aussi longue que
// celle de la classe utilisante dans le cas d'un lien de composition
if (not GetReference() and GetAnyDerivationRule() == NULL and attributeClass != NULL and
parentClass != NULL and not attributeClass->GetRoot() and
attributeClass->GetKeyAttributeNumber() < parentClass->GetKeyAttributeNumber())
else if (not GetReference() and GetAnyDerivationRule() == NULL and attributeClass != NULL and
parentClass != NULL and not attributeClass->GetRoot() and
attributeClass->GetKeyAttributeNumber() < parentClass->GetKeyAttributeNumber())
{
AddError("In used dictionary (" + attributeClass->GetName() + "), the length of the key (" +
IntToString(attributeClass->GetKeyAttributeNumber()) +
Expand Down
8 changes: 6 additions & 2 deletions src/Learning/KWData/KWAttribute.h
Original file line number Diff line number Diff line change
Expand Up @@ -322,11 +322,15 @@ inline void KWAttribute::SetStructureName(const ALString& sValue)

inline boolean KWAttribute::GetReference() const
{
KWDerivationRule* kwdrAnyRule;

require(KWType::IsRelation(GetType()));
if (kwdrRule == NULL)

kwdrAnyRule = GetAnyDerivationRule();
if (kwdrAnyRule == NULL)
return false;
else
return kwdrRule->GetReference();
return kwdrAnyRule->GetReference();
}

inline void KWAttribute::SetDerivationRule(KWDerivationRule* kwdrValue)
Expand Down
89 changes: 71 additions & 18 deletions src/Learning/KWData/KWClass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -611,10 +611,15 @@ int KWClass::ComputeOverallNativeRelationAttributeNumber(boolean bIncludingRefer
int nOverallNativeRelationAttributeNumber;
ObjectDictionary odReferenceClasses;
ObjectArray oaImpactedClasses;
ObjectDictionary odAnalysedCreatedClasses;
KWClass* kwcImpactedClass;
KWClass* kwcRefClass;
int nClass;
KWAttribute* attribute;
KWClass* kwcTargetClass;
ObjectArray oaUsedClass;
KWClass* kwcUsedClass;
int nUsedClass;

// On part de la classe de depart
kwcImpactedClass = cast(KWClass*, this);
Expand Down Expand Up @@ -648,6 +653,52 @@ int KWClass::ComputeOverallNativeRelationAttributeNumber(boolean bIncludingRefer
// Ajout de la classe a analyser
oaImpactedClasses.Add(kwcRefClass);
}
// Cas d'un attribut issue d'une regle de creation de table, pour rechercher
// les classes referencees depuis les tables creees par des regles
else if (bIncludingReferences and not attribute->GetReference())
{
assert(attribute->GetDerivationRule() != NULL);

// Recherche de la classe cible
kwcTargetClass = GetDomain()->LookupClass(
attribute->GetDerivationRule()->GetObjectClassName());
assert(kwcTargetClass != NULL);

// Analyse uniquement si la classe cible na pas deja ete analysees
if (odAnalysedCreatedClasses.Lookup(kwcTargetClass->GetName()) == NULL)
{
// Memorisation de la classe cible
odAnalysedCreatedClasses.SetAt(kwcTargetClass->GetName(),
kwcTargetClass);

// Recherche de toutes les classe utilisee recursivement
kwcTargetClass->BuildAllUsedClasses(&oaUsedClass);

// Recherches des classes externes
for (nUsedClass = 0; nUsedClass < oaUsedClass.GetSize();
nUsedClass++)
{
kwcUsedClass =
cast(KWClass*, oaUsedClass.GetAt(nUsedClass));

// Ajout de la classe a analyser si elle ne l'a pas deja ete
if (kwcUsedClass->GetRoot())
{
if (odReferenceClasses.Lookup(
kwcUsedClass->GetName()) == NULL)
{
nOverallNativeRelationAttributeNumber++;

// Memorisation de la classe externe pour ne pas faire l'analyse plusieurs fois
odReferenceClasses.SetAt(
kwcUsedClass->GetName(),
kwcUsedClass);
oaImpactedClasses.Add(kwcUsedClass);
}
}
}
}
}
// Prise en compte dans le cas d'une classe referencee
else if (bIncludingReferences and
attribute->GetAnyDerivationRule()->GetName() ==
Expand All @@ -659,8 +710,7 @@ int KWClass::ComputeOverallNativeRelationAttributeNumber(boolean bIncludingRefer
{
nOverallNativeRelationAttributeNumber++;

// Memorisation de la classe externe pour ne pas faire l'analyse
// plusieurs fois
// Memorisation de la classe externe pour ne pas faire l'analyse plusieurs fois
odReferenceClasses.SetAt(kwcRefClass->GetName(), kwcRefClass);
oaImpactedClasses.Add(kwcRefClass);
}
Expand Down Expand Up @@ -2561,6 +2611,7 @@ boolean KWClass::CheckClassComposition(KWAttribute* parentAttribute, NumericKeyD
KWAttribute* attribute;
KWClass* parentClass;
KWClass* attributeClass;
ALString sTmp;

require(nkdComponentClasses != NULL);

Expand Down Expand Up @@ -2598,25 +2649,27 @@ boolean KWClass::CheckClassComposition(KWAttribute* parentAttribute, NumericKeyD
break;
}

// La cle de la classe utilisee doit etre strictement plus longue que
// celle de la classe utilisante dans le cas d'un lien de composition multiple a trois niveau
// (ou plus)
// La cle de la classe utilisee doit etre strictement plus longue que celle de la classe utilisante
// dans le cas d'un lien de composition multiple a trois niveau (ou plus)
// Exception dans le cas d'une classe sans-cle pouvant etre issue d'une regle de creation de table,
// auquel cas les cles ne sont pas necessaire y compris dans le cas d'un flocon creer par des regles
assert(parentClass == NULL or parentClass->GetKeyAttributeNumber() <= GetKeyAttributeNumber());
assert(GetKeyAttributeNumber() <= attributeClass->GetKeyAttributeNumber());
if (parentClass != NULL and parentAttribute->GetType() == KWType::ObjectArray and
parentClass->GetKeyAttributeNumber() == GetKeyAttributeNumber())
parentClass->GetKeyAttributeNumber() > 0 and
parentClass->GetKeyAttributeNumber() >= GetKeyAttributeNumber())
{
AddError("The length of a key in a dictionary used as a Table, having a sub-" +
KWType::ToString(attribute->GetType()) +
" in its composition, must be strictly greater than that of its parent "
"Dictionary;\n " +
"in dictionary " + parentClass->GetName() + " (key length=" +
IntToString(parentClass->GetKeyAttributeNumber()) + "), dictionary " +
GetName() + "(key length = " + IntToString(GetKeyAttributeNumber()) +
") is used as a Table in variable " + parentAttribute->GetName() +
", and uses dictionary " + attributeClass->GetName() + " with variable " +
attribute->GetName() + " as a sub-" + KWType::ToString(attribute->GetType()) +
" in its composition.");
AddError(sTmp + "As dictionary " + parentClass->GetName() + " owns variable " +
parentAttribute->GetName() + " of type " +
KWType::ToString(parentAttribute->GetType()) + "(" +
parentAttribute->GetClass()->GetName() + ") and dictionary " +
parentAttribute->GetClass()->GetName() + " itself owns variable " +
attribute->GetName() + " of type " + KWType::ToString(attribute->GetType()) +
"(" + attribute->GetClass()->GetName() + "), the key length in dictionary " +
GetName() + " (key length = " + IntToString(GetKeyAttributeNumber()) +
") must be strictly greater than that of its parent "
"dictionary " +
parentClass->GetName() +
" (key length = " + IntToString(parentClass->GetKeyAttributeNumber()) + ")");
bOk = false;
}
}
Expand Down
76 changes: 61 additions & 15 deletions src/Learning/KWData/KWMTDatabase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ void KWMTDatabase::UpdateMultiTableMappings()
ObjectArray oaPreviousMultiTableMappings;
ObjectDictionary odReferenceClasses;
ObjectArray oaRankedReferenceClasses;
ObjectDictionary odAnalysedCreatedClasses;
KWClass* referenceClass;
KWMTDatabaseMapping* mapping;
KWMTDatabaseMapping* previousMapping;
Expand Down Expand Up @@ -297,8 +298,9 @@ void KWMTDatabase::UpdateMultiTableMappings()
oaRootRefTableMappings.SetSize(0);

// Creation du mapping de la table principale
rootMultiTableMapping = CreateMapping(&odReferenceClasses, &oaRankedReferenceClasses, mainClass,
mainClass->GetName(), "", &oaMultiTableMappings);
rootMultiTableMapping =
CreateMapping(&odReferenceClasses, &oaRankedReferenceClasses, &odAnalysedCreatedClasses, mainClass,
mainClass->GetName(), "", &oaMultiTableMappings);

// Parcours des classes referencee pour creer leur mapping
// Ce mapping des classes referencees n'est pas effectuee dans le cas d'une base en ecriture
Expand All @@ -316,7 +318,8 @@ void KWMTDatabase::UpdateMultiTableMappings()
oaAllMainCreatedMappings.Add(oaCreatedMappings);

// Creation du mapping et memorisation de tous les mapping des sous-classes
mapping = CreateMapping(&odReferenceClasses, &oaRankedReferenceClasses, referenceClass,
mapping = CreateMapping(&odReferenceClasses, &oaRankedReferenceClasses,
&odAnalysedCreatedClasses, referenceClass,
referenceClass->GetName(), "", oaCreatedMappings);
}

Expand Down Expand Up @@ -528,7 +531,7 @@ boolean KWMTDatabase::CheckPartially(boolean bWriteOnly) const
" variables)");
}

// Passage a la classe suivante dans la path
// Passage a la classe suivante dans le path
if (bOk)
pathClass = attribute->GetClass();

Expand Down Expand Up @@ -1321,7 +1324,8 @@ boolean KWMTDatabase::IsPhysicalObjectSelected(KWObject* kwoPhysicalObject)
}

KWMTDatabaseMapping* KWMTDatabase::CreateMapping(ObjectDictionary* odReferenceClasses,
ObjectArray* oaRankedReferenceClasses, KWClass* mappedClass,
ObjectArray* oaRankedReferenceClasses,
ObjectDictionary* odAnalysedCreatedClasses, KWClass* mappedClass,
const ALString& sDataPathClassName,
const ALString& sDataPathAttributeNames,
ObjectArray* oaCreatedMappings)
Expand All @@ -1330,9 +1334,14 @@ KWMTDatabaseMapping* KWMTDatabase::CreateMapping(ObjectDictionary* odReferenceCl
KWMTDatabaseMapping* mapping;
KWMTDatabaseMapping* subMapping;
KWAttribute* attribute;
KWClass* kwcTargetClass;
ObjectArray oaUsedClass;
KWClass* kwcUsedClass;
int nUsedClass;

require(odReferenceClasses != NULL);
require(oaRankedReferenceClasses != NULL);
require(odAnalysedCreatedClasses != NULL);
require(odReferenceClasses->GetCount() == oaRankedReferenceClasses->GetSize());
require(mappedClass != NULL);
require(sDataPathClassName != "");
Expand Down Expand Up @@ -1360,25 +1369,62 @@ KWMTDatabaseMapping* KWMTDatabase::CreateMapping(ObjectDictionary* odReferenceCl
if (KWType::IsRelation(attribute->GetType()) and attribute->GetClass() != NULL)
{
// Cas d'un attribut natif de la composition (sans regle de derivation)
if (not attribute->GetReference() and attribute->GetAnyDerivationRule() == NULL)
if (attribute->GetAnyDerivationRule() == NULL)
{
// Creation du mapping dans une nouvelle table de mapping temporaire
if (sDataPathAttributeNames != "")
subMapping = CreateMapping(odReferenceClasses, oaRankedReferenceClasses,
attribute->GetClass(), sDataPathClassName,
sDataPathAttributeNames + '`' + attribute->GetName(),
oaCreatedMappings);
subMapping = CreateMapping(
odReferenceClasses, oaRankedReferenceClasses, odAnalysedCreatedClasses,
attribute->GetClass(), sDataPathClassName,
sDataPathAttributeNames + '`' + attribute->GetName(), oaCreatedMappings);
else
subMapping = CreateMapping(odReferenceClasses, oaRankedReferenceClasses,
attribute->GetClass(), sDataPathClassName,
attribute->GetName(), oaCreatedMappings);
subMapping =
CreateMapping(odReferenceClasses, oaRankedReferenceClasses,
odAnalysedCreatedClasses, attribute->GetClass(),
sDataPathClassName, attribute->GetName(), oaCreatedMappings);

// Chainage du sous-mapping
mapping->GetComponentTableMappings()->Add(subMapping);
}
// Cas d'un attribut issue d'une regle de creation de table, pour rechercher
// les classes referencees depuis les tables creees par des regles
else if (not attribute->GetReference())
{
assert(attribute->GetAnyDerivationRule() != NULL);

// Recherche de la classe cible
kwcTargetClass = mappedClass->GetDomain()->LookupClass(
attribute->GetDerivationRule()->GetObjectClassName());
assert(kwcTargetClass != NULL);

// Analyse uniquement si la classe cible na pas deja ete analysees
if (odAnalysedCreatedClasses->Lookup(kwcTargetClass->GetName()) == NULL)
{
// Memorisation de la classe cible
odAnalysedCreatedClasses->SetAt(kwcTargetClass->GetName(), kwcTargetClass);
// Recherche de toutes les classe utilisee recursivement
kwcTargetClass->BuildAllUsedClasses(&oaUsedClass);

// Recherches des classes externes
for (nUsedClass = 0; nUsedClass < oaUsedClass.GetSize(); nUsedClass++)
{
kwcUsedClass = cast(KWClass*, oaUsedClass.GetAt(nUsedClass));

// Memorisation des mapping a traiter dans le cas de classe externes
if (kwcUsedClass->GetRoot())
{
if (odReferenceClasses->Lookup(kwcUsedClass->GetName()) == NULL)
{
odReferenceClasses->SetAt(kwcUsedClass->GetName(),
kwcUsedClass);
oaRankedReferenceClasses->Add(kwcUsedClass);
}
}
}
}
}
// Cas d'un attribut natif reference (avec regle de derivation predefinie)
else if (attribute->GetReference() and attribute->GetDerivationRule() != NULL and
attribute->GetDerivationRule()->GetName() == referenceRule.GetName())
else if (attribute->GetAnyDerivationRule()->GetName() == referenceRule.GetName())
{
// Memorisation du mapping a traiter
if (odReferenceClasses->Lookup(attribute->GetClass()->GetName()) == NULL)
Expand Down
6 changes: 4 additions & 2 deletions src/Learning/KWData/KWMTDatabase.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,11 @@ class KWMTDatabase : public KWDatabase
// Le tableaux mapping exhaustifs est egalement egalement mis a jour
// Les classes referencees sont memorisees dans un dictionnaire, pour gerer les mappings externes a creer
// ulterieurement Les mappings crees recursivement sont memorises dans un tableau
// Les classes crees analysees sont egalement memorisees dans un dictionnaire, pour eviter des analyse multiples
KWMTDatabaseMapping* CreateMapping(ObjectDictionary* odReferenceClasses, ObjectArray* oaRankedReferenceClasses,
KWClass* mappedClass, const ALString& sDataPathClassName,
const ALString& sDataPathAttributeNames, ObjectArray* oaCreatedMappings);
ObjectDictionary* odAnalysedCreatedClasses, KWClass* mappedClass,
const ALString& sDataPathClassName, const ALString& sDataPathAttributeNames,
ObjectArray* oaCreatedMappings);

/////////////////////////////////////////////////////////////////////////////////
// Gestion des objets natifs references
Expand Down
8 changes: 8 additions & 0 deletions src/Learning/KWData/KWRelationCreationRule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,14 @@ boolean KWDRRelationCreationRule::CheckOperandsCompleteness(const KWClass* kwcOw
kwcTargetClass = kwcOwnerClass->GetDomain()->LookupClass(GetObjectClassName());
assert(kwcTargetClass != NULL);

// La class cible ne doit pas etre Root
if (kwcTargetClass->GetRoot())
{
AddError(sTmp + "Invalid output dictionary " + kwcTargetClass->GetName() +
" that cannot be a root dictionary, dedicated to managing external tables");
bOk = false;
}

// Gestion de l'alimentation de type calcul via les operandes en sortie
if (bOk and GetOutputOperandNumber() > 0)
{
Expand Down
16 changes: 15 additions & 1 deletion src/Learning/KWData/KWRelationCreationRule.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,23 @@ class KWDRTableCreationRule;
// - alimentation de type calcul
// - le dictionnaire en sortie peut avoir ses variable utilisees ou non (Used),
// et des variable calculee si besoin
// - un dictionnaire en sortie n'est pas specifique a aux regle de creation de Table:
// - un dictionnaire en sortie n'est pas specifique aux regles de creation de Table:
// a priori, un meme dictionnaire pourrait etre utilise pour une variable native alimentee
// depuis un fichier, et pour une variable calculee alimentee par une regle de creation
// - quelques restrictions neanmoins vis a vis des cles qui servent a lire les donnees a partir de fichiers
// - un dictionnaire en sortie ne peut etre Root: ce cas est reserve aux tables externes, et cela ne pourrait
// pas etre traite correctement si des objet de type Root etaient cree au fil de l'eau
// - un dictionnaire en sortie peut par contre avoir des cles ou non
// - par exemple, si on construit un flocon de donnees a partir d'un champs json (base NoSql), les
// entites et tables correspondantes n'ont pas de cle
// - consequence: si un dictionnaire en sortie est une tete de flocon, il ne pourra pas etre utilise en
// dictionnaire d'analyse, car on ne saurait pas l'alimenter a partir d'une base multi-table de fichiers
// - cela declenchera une erreur non pas a la lecture du dictionnaire (flocon autorise pour des tabkes construite)
// mais a l'utilisation suite au choix d'un dictionnaire d'analyse, par exemple pour un apprentissage
// - contrainte: si une dictionnaire a une cle, ses sous-dictionnaires de flocon doivent egalement avoir des cles
// - cela limite un peu l'expressivite de la creation de table
// - mais cela permet d'avoir le maximum de detection d'erreur des la lecture d'un fichier dictionnaire,
// en reduisant le nombre d'erreurs diagnostiquees au moment de l'utilisation d'un dictionnaire d'analyse
//
// Alimentation de type vue:
// - par defaut, le premier operande de ce type de regle est de type relation, ce qui permet
Expand Down
5 changes: 3 additions & 2 deletions src/Learning/MODL/MODL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ void SetWindowsDebugDir(const ALString& sDatasetFamily, const ALString& sDataset
// A parametrer pour chaque utilisateur
// Devra etre fait plus proprement quand tout l'equipe sera sur git, par exemple via une variable
// d'environnement et quelques commentaires clairs
sUserRootPath = "C:/Applications/boullema/LearningTest.V10.5.0-b.1/TestKhiops/";
sUserRootPath = "C:/Applications/boullema/LearningTest.V10.5.1-b.0/TestKhiops/";

// Pour permettre de continuer a utiliser LearningTest, on ne fait rien s'il y a deja un fichier test.prm
// dans le repertoire courante
Expand All @@ -38,7 +38,8 @@ int main(int argc, char** argv)
// Choix du repertoire de lancement pour le debugage sous Windows (a commenter apres fin du debug)
// SetWindowsDebugDir("Standard", "IrisLight");
// SetWindowsDebugDir("Standard", "IrisU2D");
SetWindowsDebugDir("z_TableCreationRules", "EntityCreatedWithoutKey");
// SetWindowsDebugDir("z_TableCreationRules", "NoKeyInCreatedSnowflake");
SetWindowsDebugDir("z_TableCreationRules", "TableNoKey");

// Parametrage des logs memoires depuis les variables d'environnement, pris en compte dans KWLearningProject
// KhiopsMemStatsLogFileName, KhiopsMemStatsLogFrequency, KhiopsMemStatsLogToCollect
Expand Down

0 comments on commit aa71d2a

Please sign in to comment.