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 "columnwidget.h"
20 #include "sequencewidget.h"
21 #include "baseform.h"
22 #include "generalconfigwidget.h"
23 
ColumnWidget(QWidget * parent)24 ColumnWidget::ColumnWidget(QWidget *parent): BaseObjectWidget(parent, ObjectType::Column)
25 {
26 	try
27 	{
28 		QSpacerItem *spacer=new QSpacerItem(10,10,QSizePolicy::Fixed,QSizePolicy::Expanding);
29 		QStringList list;
30 
31 		Ui_ColumnWidget::setupUi(this);
32 		edit_seq_btn->setVisible(false);
33 
34 		identity_type_cmb->addItems(IdentityType::getTypes());
35 
36 		data_type=nullptr;
37 		data_type=new PgSQLTypeWidget(this);
38 
39 		hl_default_value=nullptr;
40 		hl_default_value=new SyntaxHighlighter(def_value_txt, true);
41 		hl_default_value->loadConfiguration(GlobalAttributes::getSQLHighlightConfPath());
42 
43 		sequence_sel=new ObjectSelectorWidget(ObjectType::Sequence, true, this);
44 		sequence_sel->setEnabled(false);
45 
46 		column_grid->addWidget(data_type,0,0,1,0);
47 		column_grid->addWidget(default_value_grp,1,0,1,1);
48 
49 		column_grid->addItem(spacer,column_grid->count(),0);
50 		dynamic_cast<QGridLayout *>(default_value_grp->layout())->addWidget(sequence_sel, 1, 1, 1, 6);
51 
52 		configureFormLayout(column_grid, ObjectType::Column);
53 		configureTabOrder({ data_type });
54 
55 		map<QString, vector<QWidget *> > fields_map;
56 		fields_map[generateVersionsInterval(AfterVersion, PgSqlVersions::PgSqlVersion100)].push_back(identity_rb);
57 		fields_map[generateVersionsInterval(AfterVersion, PgSqlVersions::PgSqlVersion120)].push_back(generated_chk);
58 		highlightVersionSpecificFields(fields_map);
59 
60 		connect(expression_rb, SIGNAL(toggled(bool)), this, SLOT(enableDefaultValueFields()));
61 		connect(sequence_rb, SIGNAL(toggled(bool)), this, SLOT(enableDefaultValueFields()));
62 		connect(identity_rb, SIGNAL(toggled(bool)), this, SLOT(enableDefaultValueFields()));
63 
64 		connect(generated_chk, &QCheckBox::toggled, [&](bool value){
65 			notnull_chk->setDisabled(value);
66 			notnull_chk->setChecked(false);
67 		});
68 
69 		connect(edit_seq_btn, SIGNAL(clicked(bool)), this, SLOT(editSequenceAttributes()));
70 		setMinimumSize(540, 480);
71 	}
72 	catch(Exception &e)
73 	{
74 		throw Exception(e.getErrorMessage(),e.getErrorCode(),__PRETTY_FUNCTION__,__FILE__,__LINE__, &e);
75 	}
76 }
77 
enableDefaultValueFields()78 void ColumnWidget::enableDefaultValueFields()
79 {
80 	bool is_expr = sender() == expression_rb,
81 			is_seq = sender() == sequence_rb,
82 			is_ident = sender() == identity_rb;
83 
84 	sequence_sel->setEnabled(is_seq);
85 	def_value_txt->setEnabled(is_expr);
86 	identity_type_cmb->setEnabled(is_ident);
87 	notnull_chk->setEnabled(is_expr || is_seq);
88 	edit_seq_btn->setVisible(is_ident);
89 	generated_chk->setEnabled(is_expr);
90 	generated_chk->setChecked(false);
91 }
92 
setAttributes(DatabaseModel * model,OperationList * op_list,BaseObject * parent_obj,Column * column)93 void ColumnWidget::setAttributes(DatabaseModel *model, OperationList *op_list, BaseObject *parent_obj, Column *column)
94 {
95 	PgSqlType type;
96 
97 	if(!parent_obj)
98 		throw Exception(ErrorCode::AsgNotAllocattedObject,__PRETTY_FUNCTION__,__FILE__,__LINE__);
99 
100 	BaseObjectWidget::setAttributes(model, op_list, column, parent_obj);
101 	sequence_sel->setModel(model);
102 
103 	ident_col_seq.setValues("", "", "", "", "");
104 	ident_col_seq.setCycle(false);
105 
106 	if(column)
107 	{
108 		type=column->getType();
109 		notnull_chk->setChecked(column->isNotNull());
110 		generated_chk->setChecked(column->isGenerated());
111 		def_value_txt->setPlainText(column->getDefaultValue());
112 
113 		if(column->getSequence())
114 		{
115 			sequence_rb->click();
116 			sequence_sel->setEnabled(true);
117 			sequence_sel->setSelectedObject(column->getSequence());
118 		}
119 		else if(column->getIdentityType() != BaseType::Null)
120 		{
121 			identity_rb->click();
122 			identity_type_cmb->setEnabled(true);
123 			identity_type_cmb->setCurrentText(~column->getIdentityType());
124 			notnull_chk->setEnabled(false);
125 		}
126 	}
127 
128 	data_type->setAttributes(type, model,
129 													 UserTypeConfig::BaseType | UserTypeConfig::TableType |
130 													 UserTypeConfig::ViewType | UserTypeConfig::ForeignTableType |
131 													 UserTypeConfig::DomainType | UserTypeConfig::ExtensionType, true,false);
132 }
133 
editSequenceAttributes()134 void ColumnWidget::editSequenceAttributes()
135 {
136 	Column *col = dynamic_cast<Column *>(this->object);
137 	Schema *schema = nullptr;
138 	BaseForm editing_form(this);
139 	SequenceWidget *seq_wgt=new SequenceWidget;
140 	BaseTable *table = col ? col->getParentTable() : nullptr;
141 
142 	if(table)
143 		schema = dynamic_cast<Schema *>(table->getSchema());
144 	else
145 		schema = this->model->getSchema("public");
146 
147 	ident_col_seq.setName(QString("%1_%2_seq").arg(table ? table->getName() : "").arg(col ? col->getName() : QString("new_column")));
148 	ident_col_seq.setName(PgModelerNs::generateUniqueName(&ident_col_seq, *model->getObjectList(ObjectType::Sequence), false));
149 	ident_col_seq.setSchema(schema);
150 
151 	if(col)
152 	{
153 		ident_col_seq.setDefaultValues(col->getType());
154 		ident_col_seq.setValues(col->getIdSeqMinValue(), col->getIdSeqMaxValue(), col->getIdSeqIncrement(), col->getIdSeqStart(), col->getIdSeqCache());
155 		ident_col_seq.setCycle(col->isIdSeqCycle());
156 	}
157 
158 	seq_wgt->setAttributesReadonly(this->model, nullptr, nullptr, &ident_col_seq, col);
159 	editing_form.setMainWidget(seq_wgt);
160 	GeneralConfigWidget::restoreWidgetGeometry(&editing_form, seq_wgt->metaObject()->className());
161 	editing_form.exec();
162 	GeneralConfigWidget::saveWidgetGeometry(&editing_form, seq_wgt->metaObject()->className());
163 }
164 
applyConfiguration()165 void ColumnWidget::applyConfiguration()
166 {
167 	try
168 	{
169 		Column *column=nullptr;
170 		Constraint *pk = nullptr, *constr = nullptr;
171 		PhysicalTable *parent_tab = dynamic_cast<PhysicalTable *>(table);
172 		vector<Constraint *> fks;
173 		BaseRelationship *rel = nullptr;
174 		startConfiguration<Column>();
175 
176 		column=dynamic_cast<Column *>(this->object);
177 		column->setNotNull(notnull_chk->isChecked());
178 		column->setGenerated(generated_chk->isChecked());
179 		column->setType(data_type->getPgSQLType());
180 
181 		if(expression_rb->isChecked())
182 			column->setDefaultValue(def_value_txt->toPlainText());
183 		else if(sequence_rb->isChecked())
184 			column->setSequence(sequence_sel->getSelectedObject());
185 		else
186 			column->setIdentityType(IdentityType(identity_type_cmb->currentText()));
187 
188 		column->setIdSeqAttributes(ident_col_seq.getMinValue(), ident_col_seq.getMaxValue(), ident_col_seq.getIncrement(),
189 															 ident_col_seq.getStart(), ident_col_seq.getCache(), ident_col_seq.isCycle());
190 
191 		if(parent_tab)
192 		{
193 			pk = parent_tab->getPrimaryKey();
194 
195 			if(pk && pk->isColumnReferenced(column) && !notnull_chk->isChecked())
196 				throw Exception(Exception::getErrorMessage(ErrorCode::NullPrimaryKeyColumn)
197 												.arg(column->getName())
198 												.arg(pk->getParentTable()->getSignature(true)),
199 												ErrorCode::NullPrimaryKeyColumn,__PRETTY_FUNCTION__,__FILE__,__LINE__);
200 
201 			// Separating fks in which the column is part so the fk relationships can be properly updated
202 			for(unsigned idx = 0; idx < parent_tab->getConstraintCount(); idx++)
203 			{
204 				constr = parent_tab->getConstraint(idx);
205 
206 				if(constr && constr->getConstraintType() == ConstraintType::ForeignKey && constr->isColumnExists(column, Constraint::SourceCols))
207 					fks.push_back(constr);
208 			}
209 		}
210 
211 		BaseObjectWidget::applyConfiguration();
212 		model->updateViewsReferencingTable(parent_tab);
213 
214 		/* Updating the mandatory state of destination tables on FK relationships
215 		 * derived from the table's fk in which the column is part of based upon
216 		 * notnull state of the column */
217 		for(auto &fk : fks)
218 		{
219 			rel = model->getRelationship(fk->getParentTable(), fk->getReferencedTable(), fk);
220 
221 			if(rel)
222 			{
223 				rel->setMandatoryTable(BaseRelationship::DstTable, column->isNotNull());
224 				rel->setModified(true);
225 			}
226 		}
227 
228 		finishConfiguration();
229 	}
230 	catch(Exception &e)
231 	{
232 		cancelConfiguration();
233 		throw Exception(e.getErrorMessage(),e.getErrorCode(),__PRETTY_FUNCTION__,__FILE__,__LINE__, &e);
234 	}
235 }
236 
237