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