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