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 "index.h"
20
Index()21 Index::Index()
22 {
23 obj_type=ObjectType::Index;
24 index_attribs[Unique]=index_attribs[Concurrent]=
25 index_attribs[FastUpdate]=index_attribs[Buffering]=false;
26 fill_factor=90;
27 attributes[Attributes::Unique]="";
28 attributes[Attributes::Concurrent]="";
29 attributes[Attributes::Table]="";
30 attributes[Attributes::IndexType]="";
31 attributes[Attributes::Columns]="";
32 attributes[Attributes::Expression]="";
33 attributes[Attributes::Factor]="";
34 attributes[Attributes::Predicate]="";
35 attributes[Attributes::OpClass]="";
36 attributes[Attributes::NullsFirst]="";
37 attributes[Attributes::AscOrder]="";
38 attributes[Attributes::DeclInTable]="";
39 attributes[Attributes::Elements]="";
40 attributes[Attributes::FastUpdate]="";
41 attributes[Attributes::Buffering]="";
42 attributes[Attributes::StorageParams]="";
43 }
44
setIndexElementsAttribute(unsigned def_type)45 void Index::setIndexElementsAttribute(unsigned def_type)
46 {
47 QString str_elem;
48 unsigned i, count;
49
50 count=idx_elements.size();
51 for(i=0; i < count; i++)
52 {
53 str_elem+=idx_elements[i].getCodeDefinition(def_type);
54 if(i < (count-1) && def_type==SchemaParser::SqlDefinition) str_elem+=',';
55 }
56
57 attributes[Attributes::Elements]=str_elem;
58 }
59
getElementIndex(IndexElement elem)60 int Index::getElementIndex(IndexElement elem)
61 {
62 int idx=0;
63 bool found=false;
64
65 while(idx < static_cast<int>(idx_elements.size()) && !found)
66 {
67 found=(idx_elements[idx]==elem);
68 if(!found) idx++;
69 }
70
71 return (found ? idx : -1);
72 }
73
addIndexElement(IndexElement elem)74 void Index::addIndexElement(IndexElement elem)
75 {
76 if(getElementIndex(elem) >= 0)
77 throw Exception(ErrorCode::InsDuplicatedElement,__PRETTY_FUNCTION__,__FILE__,__LINE__);
78 else if(elem.getExpression().isEmpty() && !elem.getColumn())
79 throw Exception(ErrorCode::AsgInvalidExpressionObject,__PRETTY_FUNCTION__,__FILE__,__LINE__);
80
81 idx_elements.push_back(elem);
82 setCodeInvalidated(true);
83 validateElements();
84 }
85
addIndexElement(const QString & expr,Collation * coll,OperatorClass * op_class,bool use_sorting,bool asc_order,bool nulls_first)86 void Index::addIndexElement(const QString &expr, Collation *coll, OperatorClass *op_class, bool use_sorting, bool asc_order, bool nulls_first)
87 {
88 try
89 {
90 IndexElement elem;
91
92 //Raises an error if the expression is empty
93 if(expr.isEmpty())
94 throw Exception(ErrorCode::AsgInvalidExpressionObject,__PRETTY_FUNCTION__,__FILE__,__LINE__);
95
96 //Configures the element
97 elem.setExpression(expr);
98 elem.setOperatorClass(op_class);
99 elem.setCollation(coll);
100 elem.setSortingEnabled(use_sorting);
101 elem.setSortingAttribute(IndexElement::NullsFirst, nulls_first);
102 elem.setSortingAttribute(IndexElement::AscOrder, asc_order);
103
104 if(getElementIndex(elem) >= 0)
105 throw Exception(ErrorCode::InsDuplicatedElement,__PRETTY_FUNCTION__,__FILE__,__LINE__);
106
107 idx_elements.push_back(elem);
108 setCodeInvalidated(true);
109 validateElements();
110 }
111 catch(Exception &e)
112 {
113 throw Exception(e.getErrorMessage(), e.getErrorCode(),__PRETTY_FUNCTION__,__FILE__,__LINE__, &e);
114 }
115 }
116
addIndexElement(Column * column,Collation * coll,OperatorClass * op_class,bool use_sorting,bool asc_order,bool nulls_first)117 void Index::addIndexElement(Column *column, Collation *coll, OperatorClass *op_class, bool use_sorting, bool asc_order, bool nulls_first)
118 {
119 try
120 {
121 IndexElement elem;
122
123 //Case the column is not allocated raises an error
124 if(!column)
125 throw Exception(Exception::getErrorMessage(ErrorCode::AsgNotAllocatedColumn)
126 .arg(this->getName())
127 .arg(this->getTypeName()),
128 ErrorCode::AsgNotAllocatedColumn, __PRETTY_FUNCTION__,__FILE__,__LINE__);
129
130 //Configures the element
131 elem.setColumn(column);
132 elem.setOperatorClass(op_class);
133 elem.setCollation(coll);
134 elem.setSortingEnabled(use_sorting);
135 elem.setSortingAttribute(IndexElement::NullsFirst, nulls_first);
136 elem.setSortingAttribute(IndexElement::AscOrder, asc_order);
137
138 if(getElementIndex(elem) >= 0)
139 throw Exception(ErrorCode::InsDuplicatedElement,__PRETTY_FUNCTION__,__FILE__,__LINE__);
140
141 idx_elements.push_back(elem);
142 setCodeInvalidated(true);
143 validateElements();
144 }
145 catch(Exception &e)
146 {
147 throw Exception(e.getErrorMessage(), e.getErrorCode(),__PRETTY_FUNCTION__,__FILE__,__LINE__, &e);
148 }
149 }
150
addIndexElements(vector<IndexElement> & elems)151 void Index::addIndexElements(vector<IndexElement> &elems)
152 {
153 vector<IndexElement> elems_bkp=idx_elements;
154
155 try
156 {
157 idx_elements.clear();
158
159 for(unsigned i=0; i < elems.size(); i++)
160 addIndexElement(elems[i]);
161 }
162 catch(Exception &e)
163 {
164 idx_elements = elems_bkp;
165 throw Exception(e.getErrorMessage(), e.getErrorCode(),__PRETTY_FUNCTION__,__FILE__,__LINE__, &e);
166 }
167 }
168
removeIndexElement(unsigned idx_elem)169 void Index::removeIndexElement(unsigned idx_elem)
170 {
171 if(idx_elem >= idx_elements.size())
172 throw Exception(ErrorCode::RefElementInvalidIndex,__PRETTY_FUNCTION__,__FILE__,__LINE__);
173
174 idx_elements.erase(idx_elements.begin() + idx_elem);
175 setCodeInvalidated(true);
176 }
177
removeIndexElements()178 void Index::removeIndexElements()
179 {
180 idx_elements.clear();
181 setCodeInvalidated(true);
182 }
183
getIndexElement(unsigned elem_idx)184 IndexElement Index::getIndexElement(unsigned elem_idx)
185 {
186 if(elem_idx >= idx_elements.size())
187 throw Exception(ErrorCode::RefElementInvalidIndex,__PRETTY_FUNCTION__,__FILE__,__LINE__);
188
189 return idx_elements[elem_idx];
190 }
191
getIndexElements()192 vector<IndexElement> Index::getIndexElements()
193 {
194 return idx_elements;
195 }
196
getIndexElementCount()197 unsigned Index::getIndexElementCount()
198 {
199 return idx_elements.size();
200 }
201
setIndexAttribute(unsigned attrib_id,bool value)202 void Index::setIndexAttribute(unsigned attrib_id, bool value)
203 {
204 if(attrib_id > Buffering)
205 throw Exception(ErrorCode::RefAttributeInvalidIndex,__PRETTY_FUNCTION__,__FILE__,__LINE__);
206
207 setCodeInvalidated(index_attribs[attrib_id] != value);
208 index_attribs[attrib_id]=value;
209 }
210
setFillFactor(unsigned factor)211 void Index::setFillFactor(unsigned factor)
212 {
213 setCodeInvalidated(fill_factor != factor);
214 fill_factor=factor;
215 }
216
setIndexingType(IndexingType idx_type)217 void Index::setIndexingType(IndexingType idx_type)
218 {
219 setCodeInvalidated(indexing_type != idx_type);
220 this->indexing_type=idx_type;
221 validateElements();
222 }
223
setPredicate(const QString & expr)224 void Index::setPredicate(const QString &expr)
225 {
226 setCodeInvalidated(predicate != expr);
227 predicate=expr;
228 }
229
getFillFactor()230 unsigned Index::getFillFactor()
231 {
232 return fill_factor;
233 }
234
getIndexAttribute(unsigned attrib_id)235 bool Index::getIndexAttribute(unsigned attrib_id)
236 {
237 if(attrib_id > Buffering)
238 throw Exception(ErrorCode::RefAttributeInvalidIndex,__PRETTY_FUNCTION__,__FILE__,__LINE__);
239
240 return index_attribs[attrib_id];
241 }
242
getIndexingType()243 IndexingType Index::getIndexingType()
244 {
245 return indexing_type;
246 }
247
getPredicate()248 QString Index::getPredicate()
249 {
250 return predicate;
251 }
252
isReferRelationshipAddedColumn()253 bool Index::isReferRelationshipAddedColumn()
254 {
255 vector<IndexElement>::iterator itr, itr_end;
256 Column *col=nullptr;
257 bool found=false;
258
259 itr=idx_elements.begin();
260 itr_end=idx_elements.end();
261
262 //Checks if some of the elements if referencing columns added by relationship
263 while(itr!=itr_end && !found)
264 {
265 col=(*itr).getColumn();
266 found=(col && col->isAddedByRelationship());
267 itr++;
268 }
269
270 return found;
271 }
272
getRelationshipAddedColumns()273 vector<Column *> Index::getRelationshipAddedColumns()
274 {
275 vector<Column *> cols;
276 Column *col=nullptr;
277
278 for(auto &elem : idx_elements)
279 {
280 col=elem.getColumn();
281
282 if(col && col->isAddedByRelationship())
283 cols.push_back(col);
284 }
285
286 return cols;
287 }
288
isReferCollation(Collation * collation)289 bool Index::isReferCollation(Collation *collation)
290 {
291 vector<IndexElement>::iterator itr, itr_end;
292 bool found=false;
293
294 if(!collation) return false;
295
296 itr=idx_elements.begin();
297 itr_end=idx_elements.end();
298
299 //Checks if some of the elements is referencing the collation
300 while(itr!=itr_end && !found)
301 {
302 found=((*itr).getCollation()==collation);
303 itr++;
304 }
305
306 return found;
307 }
308
isReferColumn(Column * column)309 bool Index::isReferColumn(Column *column)
310 {
311 vector<IndexElement>::iterator itr, itr_end;
312 bool found=false;
313
314 if(!column) return false;
315
316 itr=idx_elements.begin();
317 itr_end=idx_elements.end();
318
319 while(itr!=itr_end && !found)
320 {
321 found=((*itr).getColumn()==column);
322 itr++;
323 }
324
325 return found;
326 }
327
getCodeDefinition(unsigned def_type)328 QString Index::getCodeDefinition(unsigned def_type)
329 {
330 QString code_def=getCachedCode(def_type, false);
331 if(!code_def.isEmpty()) return code_def;
332
333 setIndexElementsAttribute(def_type);
334 attributes[Attributes::Unique]=(index_attribs[Unique] ? Attributes::True : "");
335 attributes[Attributes::Concurrent]=(index_attribs[Concurrent] ? Attributes::True : "");
336 attributes[Attributes::IndexType]=(~indexing_type);
337 attributes[Attributes::Predicate]=predicate;
338 attributes[Attributes::StorageParams]="";
339
340 if(getParentTable())
341 {
342 attributes[Attributes::Table]=getParentTable()->getName(true);
343
344 if(def_type==SchemaParser::SqlDefinition && getParentTable()->getSchema())
345 attributes[Attributes::Schema]=getParentTable()->getSchema()->getName(true);
346 }
347
348 if(this->indexing_type==IndexingType::Gin)
349 attributes[Attributes::StorageParams]=attributes[Attributes::FastUpdate]=(index_attribs[FastUpdate] ? Attributes::True : "");
350
351 if(this->indexing_type==IndexingType::Gist)
352 attributes[Attributes::StorageParams]=attributes[Attributes::Buffering]=(index_attribs[Buffering] ? Attributes::True : "");
353
354 if(this->indexing_type!=IndexingType::Gin && fill_factor >= 10)
355 {
356 attributes[Attributes::Factor]=QString("%1").arg(fill_factor);
357 attributes[Attributes::StorageParams]=Attributes::True;
358 }
359 else if(def_type==SchemaParser::XmlDefinition)
360 attributes[Attributes::Factor]=QString("0");
361
362 /* Case the index doesn't referece some column added by relationship it will be declared
363 inside the parent table construction by the use of 'decl-in-table' schema attribute */
364 if(!isReferRelationshipAddedColumn())
365 attributes[Attributes::DeclInTable]=Attributes::True;
366
367 return BaseObject::__getCodeDefinition(def_type);
368 }
369
getSignature(bool format)370 QString Index::getSignature(bool format)
371 {
372 if(!getParentTable() || !getParentTable()->getSchema())
373 return BaseObject::getSignature(format);
374
375 return QString("%1.%2").arg(getParentTable()->getSchema()->getName(format)).arg(this->getName(format));
376 }
377
getAlterDefinition(BaseObject * object)378 QString Index::getAlterDefinition(BaseObject *object)
379 {
380 Index *index=dynamic_cast<Index *>(object);
381
382 if(!index)
383 throw Exception(ErrorCode::OprNotAllocatedObject,__PRETTY_FUNCTION__,__FILE__,__LINE__);
384
385 try
386 {
387 attribs_map attribs;
388 attributes[Attributes::AlterCmds]=BaseObject::getAlterDefinition(object);
389
390 if(this->indexing_type==index->indexing_type)
391 {
392 if(this->indexing_type != IndexingType::Gin &&
393 this->fill_factor!=index->fill_factor && index->fill_factor >= 10)
394 attribs[Attributes::Factor]=QString::number(index->fill_factor);
395
396 if(this->indexing_type==IndexingType::Gin &&
397 this->index_attribs[FastUpdate] != index->index_attribs[FastUpdate])
398 attribs[Attributes::FastUpdate]=(index->index_attribs[FastUpdate] ? Attributes::True : Attributes::Unset);
399
400 if(this->indexing_type==IndexingType::Gist &&
401 this->index_attribs[Buffering] != index->index_attribs[Buffering])
402 attribs[Attributes::Buffering]=(index->index_attribs[Buffering] ? Attributes::True : Attributes::Unset);
403 }
404
405 copyAttributes(attribs);
406 return BaseObject::getAlterDefinition(this->getSchemaName(), attributes, false, true);
407 }
408 catch(Exception &e)
409 {
410 throw Exception(e.getErrorMessage(),e.getErrorCode(),__PRETTY_FUNCTION__,__FILE__,__LINE__,&e);
411 }
412 }
413
validateElements()414 void Index::validateElements()
415 {
416 if(indexing_type!=IndexingType::Btree)
417 {
418 for(unsigned i=0; i < idx_elements.size(); i++)
419 {
420 if(idx_elements[i].isSortingEnabled())
421 {
422 idx_elements[i].setSortingEnabled(false);
423 setCodeInvalidated(true);
424 }
425 }
426 }
427 }
428