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