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 Nov 22, 2024
1 parent d7980e6 commit 42e30ab
Show file tree
Hide file tree
Showing 8 changed files with 175 additions and 44 deletions.
17 changes: 11 additions & 6 deletions src/Learning/KWData/KWAttribute.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -284,18 +284,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 @@ -2599,6 +2649,7 @@ boolean KWClass::CheckClassComposition(KWAttribute* parentAttribute, NumericKeyD
KWAttribute* attribute;
KWClass* parentClass;
KWClass* attributeClass;
ALString sTmp;

require(nkdComponentClasses != NULL);

Expand Down Expand Up @@ -2636,25 +2687,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
71 changes: 58 additions & 13 deletions src/Learning/KWData/KWMTDatabase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ void KWMTDatabase::UpdateMultiTableMappings()
ObjectArray oaPreviousMultiTableMappings;
ObjectDictionary odReferenceClasses;
ObjectArray oaRankedReferenceClasses;
ObjectDictionary odAnalysedCreatedClasses;
KWClass* referenceClass;
StringVector svAttributeName;
KWMTDatabaseMapping* mapping;
Expand Down Expand Up @@ -315,8 +316,9 @@ void KWMTDatabase::UpdateMultiTableMappings()

// Creation du mapping de la table principale
assert(svAttributeName.GetSize() == 0);
rootMultiTableMapping = CreateMapping(&odReferenceClasses, &oaRankedReferenceClasses, mainClass, false,
mainClass->GetName(), &svAttributeName, &oaMultiTableMappings);
rootMultiTableMapping =
CreateMapping(&odReferenceClasses, &oaRankedReferenceClasses, &odAnalysedCreatedClasses, mainClass,
false, mainClass->GetName(), &svAttributeName, &oaMultiTableMappings);
assert(svAttributeName.GetSize() == 0);

// Parcours des classes referencee pour creer leur mapping
Expand All @@ -336,9 +338,9 @@ void KWMTDatabase::UpdateMultiTableMappings()

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

Expand Down Expand Up @@ -577,7 +579,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 @@ -1370,17 +1372,23 @@ boolean KWMTDatabase::IsPhysicalObjectSelected(KWObject* kwoPhysicalObject)
}

KWMTDatabaseMapping* KWMTDatabase::CreateMapping(ObjectDictionary* odReferenceClasses,
ObjectArray* oaRankedReferenceClasses, KWClass* mappedClass,
ObjectArray* oaRankedReferenceClasses,
ObjectDictionary* odAnalysedCreatedClasses, KWClass* mappedClass,
boolean bIsExternalTable, const ALString& sOriginClassName,
StringVector* svAttributeNames, ObjectArray* oaCreatedMappings)
{
const KWDRReference referenceRule;
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(sOriginClassName != "");
Expand All @@ -1407,25 +1415,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)
{
// Ajout temporaire d'un attribut au mapping
svAttributeNames->Add(attribute->GetName());

// Creation du mapping dans une nouvelle table de mapping temporaire
subMapping = CreateMapping(odReferenceClasses, oaRankedReferenceClasses,
attribute->GetClass(), bIsExternalTable, sOriginClassName,
svAttributeNames, oaCreatedMappings);
subMapping =
CreateMapping(odReferenceClasses, oaRankedReferenceClasses,
odAnalysedCreatedClasses, attribute->GetClass(), bIsExternalTable,
sOriginClassName, svAttributeNames, oaCreatedMappings);

// Supression de l'attribut ajoute temporairement
svAttributeNames->SetSize(svAttributeNames->GetSize() - 1);

// 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
7 changes: 4 additions & 3 deletions src/Learning/KWData/KWMTDatabase.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,10 +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, boolean bIsExternalTable,
const ALString& sOriginClassName, StringVector* svAttributeNames,
ObjectArray* oaCreatedMappings);
ObjectDictionary* odAnalysedCreatedClasses, KWClass* mappedClass, boolean bIsExternalTable,
const ALString& sOriginClassName, StringVector* svAttributeNames,
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
3 changes: 2 additions & 1 deletion src/Learning/MODL/MODL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,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 42e30ab

Please sign in to comment.