1 /*
2 # PostgreSQL Database Modeler (pgModeler)
3 #
4 # Copyright 2006-2020 - Raphael Araújo e Silva <raphael@pgmodeler.io>
5 #
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation version 3.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14 #
15 # The complete text of GPLv3 is at LICENSE file on source code root directory.
16 # Also, you can get the complete GNU General Public License at <http://www.gnu.org/licenses/>
17 */
18
19 #include "baserelationship.h"
20 #include <QApplication>
21
BaseRelationship(BaseRelationship * rel)22 BaseRelationship::BaseRelationship(BaseRelationship *rel)
23 {
24 if(!rel)
25 throw Exception(ErrorCode::AsgNotAllocattedObject,__PRETTY_FUNCTION__,__FILE__,__LINE__);
26
27 for(unsigned i=0; i < 3; i++)
28 lables[i]=nullptr;
29
30 src_table=dst_table=nullptr;
31
32 (*(this))=(*rel);
33
34 custom_color=QColor(Qt::transparent);
35 reference_fk = nullptr;
36 }
37
BaseRelationship(unsigned rel_type,BaseTable * src_tab,BaseTable * dst_tab,bool src_mandatory,bool dst_mandatory)38 BaseRelationship::BaseRelationship(unsigned rel_type, BaseTable *src_tab, BaseTable *dst_tab, bool src_mandatory, bool dst_mandatory)
39
40 {
41 try
42 {
43 QString str_aux;
44
45 this->connected=false;
46 this->src_mandatory=src_mandatory;
47 this->dst_mandatory=dst_mandatory;
48 this->src_table=src_tab;
49 this->dst_table=dst_tab;
50 this->rel_type=rel_type;
51 this->custom_color=QColor(Qt::transparent);
52 this->reference_fk=nullptr;
53
54 for(unsigned i=0; i < 3; i++)
55 {
56 lables[i]=nullptr;
57 lables_dist[i]=QPointF(DNaN, DNaN);
58 }
59
60 configureRelationship();
61
62 str_aux=QApplication::translate("BaseRelationship","rel_%1_%2","")
63 .arg(src_tab->getName()).arg(dst_tab->getName());
64
65 if(str_aux.size() > BaseObject::ObjectNameMaxLength)
66 str_aux.resize(BaseObject::ObjectNameMaxLength);
67
68 setName(str_aux);
69 }
70 catch(Exception &e)
71 {
72 throw Exception(e.getErrorMessage(),e.getErrorCode(),__PRETTY_FUNCTION__,__FILE__,__LINE__, &e);
73 }
74 }
75
configureRelationship()76 void BaseRelationship::configureRelationship()
77 {
78 obj_type=ObjectType::BaseRelationship;
79
80 attributes[Attributes::Type]="";
81 attributes[Attributes::SrcRequired]="";
82 attributes[Attributes::DstRequired]="";
83 attributes[Attributes::SrcTable]="";
84 attributes[Attributes::DstTable]="";
85 attributes[Attributes::Points]="";
86 attributes[Attributes::Columns]="";
87 attributes[Attributes::Constraints]="";
88 attributes[Attributes::Elements]="";
89 attributes[Attributes::Identifier]="";
90 attributes[Attributes::ReducedForm]="";
91 attributes[Attributes::Deferrable]="";
92 attributes[Attributes::DeferType]="";
93 attributes[Attributes::TableName]="";
94 attributes[Attributes::SpecialPkCols]="";
95 attributes[Attributes::RelationshipNn]="";
96 attributes[Attributes::RelationshipGen]="";
97 attributes[Attributes::RelationshipDep]="";
98 attributes[Attributes::RelationshipPart]="";
99 attributes[Attributes::Relationship1n]="";
100 attributes[Attributes::Relationship11]="";
101 attributes[Attributes::Constraints]="";
102 attributes[Attributes::Table]="";
103 attributes[Attributes::AncestorTable]="";
104 attributes[Attributes::CopyOptions]="";
105 attributes[Attributes::CopyMode]="";
106 attributes[Attributes::SrcColPattern]="";
107 attributes[Attributes::DstColPattern]="";
108 attributes[Attributes::PkPattern]="";
109 attributes[Attributes::UqPattern]="";
110 attributes[Attributes::SrcFkPattern]="";
111 attributes[Attributes::DstFkPattern]="";
112 attributes[Attributes::PkColPattern]="";
113 attributes[Attributes::SinglePkColumn]="";
114 attributes[Attributes::UpdAction]="";
115 attributes[Attributes::DelAction]="";
116 attributes[Attributes::CustomColor]="";
117 attributes[Attributes::ReferenceFk]="";
118 attributes[Attributes::PartitionBoundExpr]="";
119 attributes[Attributes::OriginalPk]="";
120
121 //Check if the relationship type is valid
122 if(rel_type <= RelationshipFk)
123 {
124 //Raises an error if one of the tables is not allocated
125 if(!src_table || !dst_table)
126 throw Exception(Exception::getErrorMessage(ErrorCode::AsgNotAllocatedTable)
127 .arg(this->getName())
128 .arg(BaseObject::getTypeName(ObjectType::BaseRelationship)),
129 ErrorCode::AsgNotAllocatedTable,__PRETTY_FUNCTION__,__FILE__,__LINE__);
130
131 /* Raises an error if the relationship type is generalization or dependency
132 and the source and destination table are the same. */
133 if((rel_type==RelationshipGen || rel_type==RelationshipDep || rel_type==RelationshipPart) && src_table==dst_table)
134 throw Exception(ErrorCode::InvInheritCopyPartRelationship,__PRETTY_FUNCTION__,__FILE__,__LINE__);
135
136 //Allocates the textbox for the name label
137 lables[RelNameLabel]=new Textbox;
138 lables[RelNameLabel]->setTextAttribute(Textbox::ItalicText, true);
139
140 //Allocates the cardinality labels only when the relationship is not generalization or dependency (copy)
141 if(rel_type!=RelationshipGen &&
142 rel_type!=RelationshipDep &&
143 rel_type!=RelationshipPart)
144 {
145 lables[SrcCardLabel]=new Textbox;
146 lables[DstCardLabel]=new Textbox;
147 lables[SrcCardLabel]->setTextAttribute(Textbox::ItalicText, true);
148 lables[DstCardLabel]->setTextAttribute(Textbox::ItalicText, true);
149
150 //Configures the mandatory participation for both tables
151 setMandatoryTable(SrcTable,src_mandatory);
152 setMandatoryTable(DstTable,dst_mandatory);
153 }
154 }
155 else
156 //Raises an error if the specified relationship typ is invalid
157 throw Exception(ErrorCode::AllocationObjectInvalidType,__PRETTY_FUNCTION__,__FILE__,__LINE__);
158 }
159
~BaseRelationship()160 BaseRelationship::~BaseRelationship()
161 {
162 disconnectRelationship();
163
164 //Unallocates the labels
165 for(unsigned i=0; i<3; i++)
166 if(lables[i]) delete lables[i];
167 }
168
setName(const QString & name)169 void BaseRelationship::setName(const QString &name)
170 {
171 try
172 {
173 BaseObject::setName(name);
174
175 if(lables[RelNameLabel])
176 lables[RelNameLabel]->setComment(name);
177 }
178 catch(Exception &e)
179 {
180 throw Exception(e.getErrorMessage(),e.getErrorCode(),__PRETTY_FUNCTION__,__FILE__,__LINE__, &e);
181 }
182 }
183
setMandatoryTable(unsigned table_id,bool value)184 void BaseRelationship::setMandatoryTable(unsigned table_id, bool value)
185 {
186 QString cmin, aux;
187 unsigned label_id;
188
189 /* Raises an error if the user tries to create an relationship
190 One to One where both tables are mandatory partitipation
191 (1,1)-<>-(1,1). This type of relationship is not implemented because
192 it requires the table fusion. */
193 if(rel_type==Relationship11 &&
194 ((table_id==SrcTable && value && dst_mandatory) ||
195 (table_id==DstTable && value && src_mandatory)))
196 throw Exception(ErrorCode::NotImplementedRelationshipType,__PRETTY_FUNCTION__,__FILE__,__LINE__);
197
198 //Case the source table is mandatory
199 if(table_id==SrcTable)
200 {
201 src_mandatory=value;
202 //Indicates that the source cardinality label will be configured
203 label_id=SrcCardLabel;
204 }
205 else
206 {
207 if(rel_type!=Relationship1n)
208 dst_mandatory=value;
209 else
210 /* For One to many (1-n) relationship the entity on the "many" side
211 will be always in the format (0,n) */
212 dst_mandatory=false;
213
214 //Indicates that the destination cardinality label will be configured
215 label_id=DstCardLabel;
216 }
217
218 if(!value) cmin=QString("0");
219 else cmin=QString("1");
220
221 if(lables[label_id])
222 {
223 if(rel_type==Relationship11)
224 lables[label_id]->setComment(cmin + QString(":1"));
225 else if(rel_type==Relationship1n)
226 {
227 aux=(table_id==SrcTable ? QString("1") : QString("n"));
228 lables[label_id]->setComment(cmin + QString(":") + aux);
229 }
230 else if(rel_type==RelationshipFk)
231 {
232 if((table_id==SrcTable && dynamic_cast<Table *>(src_table)->isReferTableOnForeignKey(dynamic_cast<Table *>(dst_table))) ||
233 (!isSelfRelationship() && table_id==DstTable && dynamic_cast<Table *>(dst_table)->isReferTableOnForeignKey(dynamic_cast<Table *>(src_table))))
234 {
235 if(table_id == SrcTable && canSimulateRelationship11())
236 aux = "1";
237 else
238 aux = "n";
239 }
240 else
241 aux = "1";
242
243 if((table_id == DstTable && dst_mandatory) ||
244 (table_id == SrcTable && src_mandatory))
245 aux.prepend("1:");
246 else
247 aux.prepend("0:");
248
249 lables[label_id]->setComment(aux);
250 }
251 else if(rel_type==RelationshipNn)
252 lables[label_id]->setComment("n");
253
254 lables[label_id]->setModified(true);
255 }
256 }
257
getTable(unsigned table_id)258 BaseTable *BaseRelationship::getTable(unsigned table_id)
259 {
260 if(table_id==SrcTable)
261 return src_table;
262 else if(table_id==DstTable)
263 return dst_table;
264 else
265 return nullptr;
266 }
267
isTableMandatory(unsigned table_id)268 bool BaseRelationship::isTableMandatory(unsigned table_id)
269 {
270 if(table_id==SrcTable)
271 return src_mandatory;
272 else
273 return dst_mandatory;
274 }
275
setConnected(bool value)276 void BaseRelationship::setConnected(bool value)
277 {
278 connected=value;
279
280 if(!this->signalsBlocked())
281 {
282 src_table->setModified(true);
283
284 if(dst_table!=src_table)
285 dst_table->setModified(true);
286
287 dynamic_cast<Schema *>(src_table->getSchema())->setModified(true);
288
289 if(dst_table->getSchema()!=src_table->getSchema())
290 dynamic_cast<Schema *>(dst_table->getSchema())->setModified(true);
291
292 this->setModified(true);
293 }
294 }
295
disconnectRelationship()296 void BaseRelationship::disconnectRelationship()
297 {
298 if(connected)
299 {
300 setConnected(false);
301 setCodeInvalidated(true);
302 }
303 }
304
connectRelationship()305 void BaseRelationship::connectRelationship()
306 {
307 if(!connected)
308 {
309 setConnected(true);
310 setCodeInvalidated(true);
311 }
312 }
313
getLabel(unsigned label_id)314 Textbox *BaseRelationship::getLabel(unsigned label_id)
315 {
316 if(label_id<=RelNameLabel)
317 return lables[label_id];
318
319 //Raises an error when the label id is invalid
320 throw Exception(ErrorCode::RefLabelInvalidIndex,__PRETTY_FUNCTION__,__FILE__,__LINE__);
321 }
322
getRelationshipType()323 unsigned BaseRelationship::getRelationshipType()
324 {
325 return rel_type;
326 }
327
isRelationshipConnected()328 bool BaseRelationship::isRelationshipConnected()
329 {
330 return connected;
331 }
332
isSelfRelationship()333 bool BaseRelationship::isSelfRelationship()
334 {
335 return (dst_table==src_table);
336 }
337
setRelationshipAttributes()338 void BaseRelationship::setRelationshipAttributes()
339 {
340 unsigned count, i;
341 QString str_aux,
342 label_attribs[3]={ Attributes::SrcLabel,
343 Attributes::DstLabel,
344 Attributes::NameLabel};
345
346 attributes[Attributes::Layer]=QString::number(layer);
347 attributes[Attributes::Type]=getRelTypeAttribute();
348 attributes[Attributes::SrcRequired]=(src_mandatory ? Attributes::True : "");
349 attributes[Attributes::DstRequired]=(dst_mandatory ? Attributes::True : "");
350
351 if(src_table)
352 attributes[Attributes::SrcTable]=src_table->getName(true);
353
354 if(dst_table)
355 attributes[Attributes::DstTable]=dst_table->getName(true);
356
357
358 count=points.size();
359 for(i=0; i < count; i++)
360 {
361 attributes[Attributes::XPos]=QString("%1").arg(points[i].x());
362 attributes[Attributes::YPos]=QString("%1").arg(points[i].y());
363 str_aux+=schparser.getCodeDefinition(Attributes::Position, attributes, SchemaParser::XmlDefinition);
364 }
365 attributes[Attributes::Points]=str_aux;
366
367 str_aux="";
368 for(i=0; i < 3; i++)
369 {
370 if(!std::isnan(lables_dist[i].x()))
371 {
372 attributes[Attributes::XPos]=QString("%1").arg(lables_dist[i].x());
373 attributes[Attributes::YPos]=QString("%1").arg(lables_dist[i].y());
374 attributes[Attributes::Position]=schparser.getCodeDefinition(Attributes::Position, attributes, SchemaParser::XmlDefinition);
375 attributes[Attributes::RefType]=label_attribs[i];
376 str_aux+=schparser.getCodeDefinition(Attributes::Label, attributes, SchemaParser::XmlDefinition);
377 }
378 }
379
380 attributes[Attributes::LabelsPos]=str_aux;
381 attributes[Attributes::CustomColor]=(custom_color!=Qt::transparent ? custom_color.name() : "");
382 attributes[Attributes::ReferenceFk]=(reference_fk ? reference_fk->getName() : "");
383 setFadedOutAttribute();
384 }
385
getCachedCode(unsigned def_type)386 QString BaseRelationship::getCachedCode(unsigned def_type)
387 {
388 if(!code_invalidated &&
389 ((!cached_code[def_type].isEmpty()) ||
390 (def_type==SchemaParser::XmlDefinition && !cached_reduced_code.isEmpty())))
391 {
392 if(def_type==SchemaParser::XmlDefinition && !cached_reduced_code.isEmpty())
393 return cached_reduced_code;
394 else
395 return cached_code[def_type];
396 }
397 else
398 return "";
399 }
400
setReferenceForeignKey(Constraint * ref_fk)401 void BaseRelationship::setReferenceForeignKey(Constraint *ref_fk)
402 {
403 //if(ref_fk && rel_type != RELATIONSHIP_FK)
404 //Throw error...
405 this->reference_fk = ref_fk;
406 }
407
getReferenceForeignKey()408 Constraint *BaseRelationship::getReferenceForeignKey()
409 {
410 return reference_fk;
411 }
412
canSimulateRelationship11()413 bool BaseRelationship::canSimulateRelationship11()
414 {
415 if(rel_type != BaseRelationship::RelationshipFk)
416 return false;
417
418 bool fake_rel11 = false;
419 PhysicalTable *table = dynamic_cast<PhysicalTable *>(getTable(BaseRelationship::SrcTable));
420
421 if(table)
422 {
423 Constraint *constr = nullptr, *uq_constr = nullptr;
424
425 for(unsigned idx = 0; idx < table->getConstraintCount() && !fake_rel11; idx++)
426 {
427 constr = table->getConstraint(idx);
428
429 if(constr->getConstraintType() == ConstraintType::ForeignKey)
430 {
431 for(unsigned idx1 = 0; idx1 < table->getConstraintCount(); idx1++)
432 {
433 uq_constr = table->getConstraint(idx1);
434
435 if(uq_constr->getConstraintType() == ConstraintType::Unique &&
436 uq_constr->isColumnsExist(constr->getColumns(Constraint::SourceCols), Constraint::SourceCols))
437 {
438 fake_rel11 = true;
439 break;
440 }
441 }
442 }
443 }
444 }
445
446 return fake_rel11;
447 }
448
getCodeDefinition(unsigned def_type)449 QString BaseRelationship::getCodeDefinition(unsigned def_type)
450 {
451 QString code_def=getCachedCode(def_type);
452 if(!code_def.isEmpty()) return code_def;
453
454 if(def_type==SchemaParser::SqlDefinition)
455 {
456 if(rel_type!=RelationshipFk)
457 return "";
458 else
459 {
460 cached_code[def_type] = reference_fk->getCodeDefinition(SchemaParser::SqlDefinition);
461 return cached_code[def_type];
462 }
463 }
464 else
465 {
466 bool reduced_form;
467 setRelationshipAttributes();
468 reduced_form=(attributes[Attributes::Points].isEmpty() &&
469 attributes[Attributes::LabelsPos].isEmpty());
470
471 if(!reduced_form)
472 cached_reduced_code.clear();
473
474 return BaseObject::getCodeDefinition(SchemaParser::XmlDefinition,reduced_form);
475 }
476 }
477
setPoints(const vector<QPointF> & points)478 void BaseRelationship::setPoints(const vector<QPointF> &points)
479 {
480 this->setCodeInvalidated(true);
481 this->points=points;
482 }
483
setLabelDistance(unsigned label_id,QPointF label_dist)484 void BaseRelationship::setLabelDistance(unsigned label_id, QPointF label_dist)
485 {
486 if(label_id > RelNameLabel)
487 throw Exception(ErrorCode::RefObjectInvalidIndex,__PRETTY_FUNCTION__,__FILE__,__LINE__);
488
489 this->lables_dist[label_id]=label_dist;
490 this->setCodeInvalidated(true);
491 }
492
getLabelDistance(unsigned label_id)493 QPointF BaseRelationship::getLabelDistance(unsigned label_id)
494 {
495 if(label_id > RelNameLabel)
496 throw Exception(ErrorCode::RefObjectInvalidIndex,__PRETTY_FUNCTION__,__FILE__,__LINE__);
497
498 return this->lables_dist[label_id];
499 }
500
setCustomColor(const QColor & color)501 void BaseRelationship::setCustomColor(const QColor &color)
502 {
503 custom_color=color;
504 }
505
getCustomColor()506 QColor BaseRelationship::getCustomColor()
507 {
508 return custom_color;
509 }
510
resetLabelsDistance()511 void BaseRelationship::resetLabelsDistance()
512 {
513 for(unsigned i=0; i < 3; i++)
514 this->setLabelDistance(i, QPointF(DNaN,DNaN));
515 }
516
getPoints()517 vector<QPointF> BaseRelationship::getPoints()
518 {
519 return points;
520 }
521
operator =(BaseRelationship & rel)522 void BaseRelationship::operator = (BaseRelationship &rel)
523 {
524 (*dynamic_cast<BaseGraphicObject *>(this))=dynamic_cast<BaseGraphicObject &>(rel);
525 this->connected=false;
526 this->src_table=rel.src_table;
527 this->dst_table=rel.dst_table;
528 this->rel_type=rel.rel_type;
529 this->points=rel.points;
530 this->custom_color=rel.custom_color;
531
532 for(int i=0; i < 3; i++)
533 {
534 if(rel.lables[i])
535 {
536 if(!this->lables[i])
537 this->lables[i]=new Textbox;
538
539 (*this->lables[i])=(*rel.lables[i]);
540 }
541 this->lables_dist[i]=rel.lables_dist[i];
542 }
543
544 this->setMandatoryTable(SrcTable, false);
545 this->setMandatoryTable(DstTable, false);
546
547 this->setMandatoryTable(SrcTable, rel.src_mandatory);
548 this->setMandatoryTable(DstTable, rel.dst_mandatory);
549 }
550
getRelTypeAttribute()551 QString BaseRelationship::getRelTypeAttribute()
552 {
553 switch(rel_type)
554 {
555 case Relationship11: return Attributes::Relationship11;
556 case Relationship1n: return Attributes::Relationship1n;
557 case RelationshipNn: return Attributes::RelationshipNn;
558 case RelationshipGen: return Attributes::RelationshipGen;
559 case RelationshipPart: return Attributes::RelationshipPart;
560 case RelationshipFk: return Attributes::RelationshipFk;
561 default:
562 {
563 if(src_table->getObjectType()==ObjectType::View)
564 return Attributes::RelationshipTabView;
565 else
566 return Attributes::RelationshipDep;
567 }
568 }
569 }
570
getRelationshipTypeName(unsigned rel_type,bool is_view)571 QString BaseRelationship::getRelationshipTypeName(unsigned rel_type, bool is_view)
572 {
573 switch(rel_type)
574 {
575 case Relationship11: return tr("One-to-one");
576 case Relationship1n: return tr("One-to-many");
577 case RelationshipNn: return tr("Many-to-many");
578 case RelationshipGen: return tr("Inheritance");
579 case RelationshipPart: return tr("Partitioning");
580 case RelationshipFk: return tr("FK relationship");
581 default:
582 {
583 if(is_view)
584 return tr("Dependency");
585 else
586 return tr("Copy");
587 }
588 }
589 }
590
getRelationshipTypeName()591 QString BaseRelationship::getRelationshipTypeName()
592 {
593 return getRelationshipTypeName(rel_type, src_table->getObjectType()==ObjectType::View);
594 }
595
setCodeInvalidated(bool value)596 void BaseRelationship::setCodeInvalidated(bool value)
597 {
598 BaseObject::setCodeInvalidated(value);
599
600 if(src_table)
601 src_table->setCodeInvalidated(value);
602
603 if(dst_table)
604 dst_table->setCodeInvalidated(value);
605 }
606
607