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 "function.h"
20 #include "defaultlanguages.h"
21 
Function()22 Function::Function()
23 {
24 	return_type=PgSqlType(QString("void"));
25 	language=nullptr;
26 	returns_setof=false;
27 	is_wnd_function=false;
28 	is_leakproof=false;
29 	obj_type=ObjectType::Function;
30 
31 	execution_cost=100;
32 	row_amount=1000;
33 
34 	attributes[Attributes::Parameters]="";
35 	attributes[Attributes::ExecutionCost]="";
36 	attributes[Attributes::RowAmount]="";
37 	attributes[Attributes::ReturnType]="";
38 	attributes[Attributes::FunctionType]="";
39 	attributes[Attributes::Language]="";
40 	attributes[Attributes::ReturnsSetOf]="";
41 	attributes[Attributes::SecurityType]="";
42 	attributes[Attributes::BehaviorType]="";
43 	attributes[Attributes::Definition]="";
44 	attributes[Attributes::Signature]="";
45 	attributes[Attributes::RefType]="";
46 	attributes[Attributes::WindowFunc]="";
47 	attributes[Attributes::ReturnTable]="";
48 	attributes[Attributes::Library]="";
49 	attributes[Attributes::Symbol]="";
50 	attributes[Attributes::LeakProof]="";
51 }
52 
setName(const QString & name)53 void Function::setName(const QString &name)
54 {
55 	BaseObject::setName(name);
56 	createSignature();
57 }
58 
setSchema(BaseObject * schema)59 void Function::setSchema(BaseObject *schema)
60 {
61 	BaseObject::setSchema(schema);
62 	createSignature();
63 }
64 
addParameter(Parameter param)65 void Function::addParameter(Parameter param)
66 {
67 	vector<Parameter>::iterator itr,itr_end;
68 	bool found=false;
69 
70 	itr=parameters.begin();
71 	itr_end=parameters.end();
72 
73 	//Checks the duplicity of parameter names
74 	while(itr!=itr_end && !found)
75 	{
76 		/* Compares the parameters name storing in the 'found' flag
77 		 if already exists in the function */
78 		found=(itr->getName()==param.getName());
79 		itr++;
80 	}
81 
82 	//If a duplicated parameter is found an error is raised
83 	if(found)
84 		throw Exception(Exception::getErrorMessage(ErrorCode::AsgDuplicatedParameterFunction)
85 						.arg(param.getName())
86 						.arg(this->signature),
87 						ErrorCode::AsgDuplicatedParameterFunction,__PRETTY_FUNCTION__,__FILE__,__LINE__);
88 
89 	//Inserts the parameter in the function
90 	parameters.push_back(param);
91 	createSignature();
92 }
93 
addReturnedTableColumn(const QString & name,PgSqlType type)94 void Function::addReturnedTableColumn(const QString &name, PgSqlType type)
95 {
96 	//Raises an error if the column name is empty
97 	if(name.isEmpty())
98 		throw Exception(ErrorCode::AsgEmptyNameTableReturnType,__PRETTY_FUNCTION__,__FILE__,__LINE__);
99 
100 	vector<Parameter>::iterator itr,itr_end;
101 	bool found=false;
102 
103 	itr=ret_table_columns.begin();
104 	itr_end=ret_table_columns.end();
105 
106 	//Checks the duplicity of table column names
107 	while(itr!=itr_end && !found)
108 	{
109 		/* Compares the column name storing in the 'found' flag
110 		 if already exists in the returned table */
111 		found=(itr->getName()==name);
112 		itr++;
113 	}
114 
115 	//Raises an error if the column is duplicated
116 	if(found)
117 		throw Exception(Exception::getErrorMessage(ErrorCode::InsDuplicatedTableReturnType)
118 						.arg(name)
119 						.arg(this->signature),
120 						ErrorCode::InsDuplicatedTableReturnType,__PRETTY_FUNCTION__,__FILE__,__LINE__);
121 
122 	Parameter p;
123 	p.setName(name);
124 	p.setType(type);
125 	ret_table_columns.push_back(p);
126 	setCodeInvalidated(true);
127 }
128 
setParametersAttribute(unsigned def_type)129 void Function::setParametersAttribute(unsigned def_type)
130 {
131 	QString str_param;
132 	unsigned i, count;
133 
134 	count=parameters.size();
135 	for(i=0; i < count; i++)
136 	{
137 		str_param+=parameters[i].getCodeDefinition(def_type);
138 	}
139 
140 	if(def_type==SchemaParser::SqlDefinition)
141 		str_param.remove(str_param.size()-2,2);
142 
143 	attributes[Attributes::Parameters]=str_param;
144 }
145 
setTableReturnTypeAttribute(unsigned def_type)146 void Function::setTableReturnTypeAttribute(unsigned def_type)
147 {
148 	QString str_type;
149 	unsigned i, count;
150 
151 	count=ret_table_columns.size();
152 	for(i=0; i < count; i++)
153 	{
154 		str_type+=ret_table_columns[i].getCodeDefinition(def_type);
155 	}
156 
157 	if(def_type==SchemaParser::SqlDefinition)
158 		str_type.remove(str_type.size()-2,2);
159 
160 	attributes[Attributes::ReturnTable]=str_type;
161 }
162 
setExecutionCost(unsigned exec_cost)163 void Function::setExecutionCost(unsigned exec_cost)
164 {
165 	setCodeInvalidated(execution_cost != exec_cost);
166 	execution_cost=exec_cost;
167 }
168 
setRowAmount(unsigned row_amount)169 void Function::setRowAmount(unsigned row_amount)
170 {
171 	setCodeInvalidated(this->row_amount != row_amount);
172 	this->row_amount=row_amount;
173 }
174 
setLibrary(const QString & library)175 void Function::setLibrary(const QString &library)
176 {
177 	if(language->getName().toLower() != DefaultLanguages::C)
178 		throw Exception(Exception::getErrorMessage(ErrorCode::AsgRefLibraryFuncLanguageNotC)
179 						.arg(this->getSignature()),
180 						ErrorCode::AsgRefLibraryFuncLanguageNotC,__PRETTY_FUNCTION__,__FILE__,__LINE__);
181 
182 	setCodeInvalidated(this->library != library);
183 	this->library=library;
184 }
185 
setSymbol(const QString & symbol)186 void Function::setSymbol(const QString &symbol)
187 {
188 	if(language->getName().toLower() != DefaultLanguages::C)
189 		throw Exception(Exception::getErrorMessage(ErrorCode::AsgRefLibraryFuncLanguageNotC)
190 						.arg(this->getSignature()),
191 						ErrorCode::AsgRefLibraryFuncLanguageNotC,__PRETTY_FUNCTION__,__FILE__,__LINE__);
192 
193 	setCodeInvalidated(this->symbol != symbol);
194 	this->symbol=symbol;
195 }
196 
setReturnType(PgSqlType type)197 void Function::setReturnType(PgSqlType type)
198 {
199 	setCodeInvalidated(return_type != type);
200 	return_type=type;
201 }
202 
setFunctionType(FunctionType func_type)203 void Function::setFunctionType(FunctionType func_type)
204 {
205 	setCodeInvalidated(function_type != func_type);
206 	function_type=func_type;
207 }
208 
setLanguage(BaseObject * language)209 void Function::setLanguage(BaseObject *language)
210 {
211 	//Raises an error if the language is not allocated
212 	if(!language)
213 		throw Exception(ErrorCode::AsgNotAllocatedLanguage,__PRETTY_FUNCTION__,__FILE__,__LINE__);
214 	//Raises an error if the language object is invalid
215 	else if(language->getObjectType()!=ObjectType::Language)
216 		throw Exception(ErrorCode::AsgInvalidLanguageObject,__PRETTY_FUNCTION__,__FILE__,__LINE__);
217 
218 	setCodeInvalidated(this->language != language);
219 	this->language=language;
220 }
221 
setReturnSetOf(bool value)222 void Function::setReturnSetOf(bool value)
223 {
224 	setCodeInvalidated(returns_setof != value);
225 	returns_setof=value;
226 }
227 
setWindowFunction(bool value)228 void Function::setWindowFunction(bool value)
229 {
230 	setCodeInvalidated(is_wnd_function != value);
231 	is_wnd_function=value;
232 }
233 
setLeakProof(bool value)234 void Function::setLeakProof(bool value)
235 {
236 	setCodeInvalidated(is_leakproof != value);
237 	is_leakproof=value;
238 }
239 
setSecurityType(SecurityType sec_type)240 void Function::setSecurityType(SecurityType sec_type)
241 {
242 	setCodeInvalidated(security_type != sec_type);
243 	security_type=sec_type;
244 }
245 
setBehaviorType(BehaviorType behav_type)246 void Function::setBehaviorType(BehaviorType behav_type)
247 {
248 	setCodeInvalidated(behavior_type != behav_type);
249 	behavior_type=behav_type;
250 }
251 
setSourceCode(const QString & src_code)252 void Function::setSourceCode(const QString &src_code)
253 {
254 	if(language && language->getName().toLower() == DefaultLanguages::C)
255 		throw Exception(Exception::getErrorMessage(ErrorCode::AsgSourceCodeFuncCLanguage)
256 						.arg(this->getSignature()),
257 						ErrorCode::AsgSourceCodeFuncCLanguage,__PRETTY_FUNCTION__,__FILE__,__LINE__);
258 
259 	setCodeInvalidated(this->source_code != src_code);
260 	this->source_code=src_code;
261 }
262 
getReturnType()263 PgSqlType Function::getReturnType()
264 {
265 	return return_type;
266 }
267 
getFunctionType()268 FunctionType Function::getFunctionType()
269 {
270 	return function_type;
271 }
272 
getLanguage()273 BaseObject *Function::getLanguage()
274 {
275 	return language;
276 }
277 
getParameterCount()278 unsigned Function::getParameterCount()
279 {
280 	return parameters.size();
281 }
282 
getReturnedTableColumnCount()283 unsigned Function::getReturnedTableColumnCount()
284 {
285 	return ret_table_columns.size();
286 }
287 
isReturnSetOf()288 bool Function::isReturnSetOf()
289 {
290 	return returns_setof;
291 }
292 
isReturnTable()293 bool Function::isReturnTable()
294 {
295 	return (ret_table_columns.size() > 0);
296 }
297 
isWindowFunction()298 bool Function::isWindowFunction()
299 {
300 	return is_wnd_function;
301 }
302 
isLeakProof()303 bool Function::isLeakProof()
304 {
305 	return is_leakproof;
306 }
307 
getSecurityType()308 SecurityType Function::getSecurityType()
309 {
310 	return security_type;
311 }
312 
getBehaviorType()313 BehaviorType Function::getBehaviorType()
314 {
315 	return behavior_type;
316 }
317 
getSourceCode()318 QString Function::getSourceCode()
319 {
320 	return source_code;
321 }
322 
getParameter(unsigned param_idx)323 Parameter Function::getParameter(unsigned param_idx)
324 {
325 	//Raises an error if the parameter index is out of bound
326 	if(param_idx>=parameters.size())
327 		throw Exception(ErrorCode::RefParameterInvalidIndex,__PRETTY_FUNCTION__,__FILE__,__LINE__);
328 
329 	return parameters[param_idx];
330 }
331 
getReturnedTableColumn(unsigned column_idx)332 Parameter Function::getReturnedTableColumn(unsigned column_idx)
333 {
334 	//Raises an error if the column index is out of bound
335 	if(column_idx>=ret_table_columns.size())
336 		throw Exception(ErrorCode::RefObjectInvalidIndex,__PRETTY_FUNCTION__,__FILE__,__LINE__);
337 
338 	return ret_table_columns[column_idx];
339 }
340 
getExecutionCost()341 unsigned Function::getExecutionCost()
342 {
343 	return execution_cost;
344 }
345 
getRowAmount()346 unsigned Function::getRowAmount()
347 {
348 	return row_amount;
349 }
350 
getLibrary()351 QString Function::getLibrary()
352 {
353 	return library;
354 }
355 
getSymbol()356 QString Function::getSymbol()
357 {
358 	return symbol;
359 }
360 
removeParameters()361 void Function::removeParameters()
362 {
363 	parameters.clear();
364 	createSignature();
365 }
366 
removeReturnedTableColumns()367 void Function::removeReturnedTableColumns()
368 {
369 	ret_table_columns.clear();
370 	setCodeInvalidated(true);
371 }
372 
removeParameter(const QString & name,PgSqlType type)373 void Function::removeParameter(const QString &name, PgSqlType type)
374 {
375 	vector<Parameter>::iterator itr,itr_end;
376 
377 	itr=parameters.begin();
378 	itr_end=parameters.end();
379 
380 	while(itr!=itr_end)
381 	{
382 		//Compares the iterator name and type with the passed name an type
383 		if(itr->getName()==name && itr->getType()==(~type))
384 		{
385 			parameters.erase(itr);
386 			break;
387 		}
388 		itr++;
389 	}
390 
391 	//After remove the parameter is necessary updated the signature
392 	createSignature();
393 }
394 
removeParameter(unsigned param_idx)395 void Function::removeParameter(unsigned param_idx)
396 {
397 	//Raises an error if parameter index is out of bound
398 	if(param_idx>=parameters.size())
399 		throw Exception(ErrorCode::RefParameterInvalidIndex,__PRETTY_FUNCTION__,__FILE__,__LINE__);
400 
401 	vector<Parameter>::iterator itr;
402 	itr=parameters.begin()+param_idx;
403 	parameters.erase(itr);
404 
405 	createSignature();
406 }
407 
removeReturnedTableColumn(unsigned column_idx)408 void Function::removeReturnedTableColumn(unsigned column_idx)
409 {
410 	if(column_idx>=ret_table_columns.size())
411 		throw Exception(ErrorCode::RefObjectInvalidIndex,__PRETTY_FUNCTION__,__FILE__,__LINE__);
412 
413 	vector<Parameter>::iterator itr;
414 	itr=ret_table_columns.begin()+column_idx;
415 	ret_table_columns.erase(itr);
416 	setCodeInvalidated(true);
417 }
418 
getSignature(bool)419 QString Function::getSignature(bool)
420 {
421 	return signature;
422 }
423 
createSignature(bool format,bool prepend_schema)424 void Function::createSignature(bool format, bool prepend_schema)
425 {
426 	QString str_params, aux_str;
427 	unsigned i, count;
428 
429 	count=parameters.size();
430 	for(i=0; i < count; i++)
431 	{
432 		//OUT parameters is not part of function's signature
433 		if(!parameters[i].isOut() || parameters[i].isVariadic() ||
434 				(parameters[i].isIn() && parameters[i].isOut()) ||
435 				(parameters[i].isIn() && !parameters[i].isOut()))
436 		{
437 			/* Removing the arg mode IN from parameter signature because this is de default for any kind of parameter
438 			 * So in order to avoid signature conflicts (mainly whe diff functions) we remove it */
439 			aux_str=parameters[i].getCodeDefinition(SchemaParser::SqlDefinition, true).replace(QRegExp("^(IN)( )"),"");
440 			str_params+=aux_str.trimmed();
441 			parameters[i].setCodeInvalidated(true);
442 		}
443 	}
444 
445 	str_params.remove(str_params.length()-1, 1);
446 
447 	//Signature format NAME(IN|OUT PARAM1_TYPE,IN|OUT PARAM2_TYPE,...,IN|OUT PARAMn_TYPE)
448 	signature=this->getName(format, prepend_schema) + QString("(") + str_params + QString(")");
449 	this->setCodeInvalidated(true);
450 }
451 
getCodeDefinition(unsigned def_type)452 QString Function::getCodeDefinition(unsigned def_type)
453 {
454 	return this->getCodeDefinition(def_type, false);
455 }
456 
getCodeDefinition(unsigned def_type,bool reduced_form)457 QString Function::getCodeDefinition(unsigned def_type, bool reduced_form)
458 {
459 	QString code_def=getCachedCode(def_type, reduced_form);
460 	if(!code_def.isEmpty()) return code_def;
461 
462 	setParametersAttribute(def_type);
463 
464 	attributes[Attributes::ExecutionCost]=QString("%1").arg(execution_cost);
465 	attributes[Attributes::RowAmount]=QString("%1").arg(row_amount);
466 	attributes[Attributes::FunctionType]=(~function_type);
467 
468 	if(language)
469 	{
470 		if(def_type==SchemaParser::SqlDefinition)
471 		{
472 			attributes[Attributes::Language]=language->getName(false);
473 			attributes[Attributes::ReturnType]=(*return_type);
474 		}
475 		else
476 		{
477 			attributes[Attributes::Language]=language->getCodeDefinition(def_type,true);
478 			attributes[Attributes::ReturnType]=return_type.getCodeDefinition(def_type);
479 		}
480 
481 		if(language->getName().toLower() == DefaultLanguages::C)
482 		{
483 			attributes[Attributes::Symbol]=symbol;
484 			attributes[Attributes::Library]=library;
485 		}
486 	}
487 
488 	setTableReturnTypeAttribute(def_type);
489 
490 	attributes[Attributes::ReturnsSetOf]=(returns_setof ? Attributes::True : "");
491 	attributes[Attributes::WindowFunc]=(is_wnd_function ? Attributes::True : "");
492 	attributes[Attributes::LeakProof]=(is_leakproof ? Attributes::True : "");
493 	attributes[Attributes::SecurityType]=~security_type;
494 	attributes[Attributes::BehaviorType]=~behavior_type;
495 	attributes[Attributes::Definition]=source_code;
496 
497 	attributes[Attributes::Signature]=signature;
498 	return BaseObject::getCodeDefinition(def_type, reduced_form);
499 }
500 
getAlterDefinition(BaseObject * object)501 QString Function::getAlterDefinition(BaseObject *object)
502 {
503 	Function *func=dynamic_cast<Function *>(object);
504 
505 	if(!func)
506 		throw Exception(ErrorCode::OprNotAllocatedObject,__PRETTY_FUNCTION__,__FILE__,__LINE__);
507 
508 	try
509 	{
510 		attribs_map attribs;
511 
512 		attributes[Attributes::AlterCmds]=BaseObject::getAlterDefinition(object);
513 
514 		if(this->source_code.simplified() != func->source_code.simplified() ||
515 			 this->library!=func->library || this->symbol!=func->symbol)
516 		{
517 			attribs[Attributes::Definition]=func->getCodeDefinition(SchemaParser::SqlDefinition);
518 			attribs[Attributes::Definition].replace(QString("CREATE FUNCTION"), QString("CREATE OR REPLACE FUNCTION"));
519 		}
520 		else
521 		{
522 			if(this->execution_cost!=func->execution_cost)
523 				attribs[Attributes::ExecutionCost]=QString::number(func->execution_cost);
524 
525 			if(this->returns_setof && func->returns_setof && this->row_amount!=func->row_amount)
526 			{
527 				attribs[Attributes::ReturnsSetOf]=Attributes::True;
528 				attribs[Attributes::RowAmount]=QString::number(row_amount);
529 			}
530 
531 			if(this->function_type!=func->function_type)
532 				attribs[Attributes::FunctionType]=~func->function_type;
533 
534 			if(this->is_leakproof!=func->is_leakproof)
535 				attribs[Attributes::LeakProof]=(func->is_leakproof ? Attributes::True : Attributes::Unset);
536 
537 			if(this->security_type!=func->security_type)
538 				attribs[Attributes::SecurityType]=~func->security_type;
539 
540 			if((this->behavior_type!=func->behavior_type) &&
541 					((this->behavior_type==BehaviorType::CalledOnNullInput) ||
542 					 ((this->behavior_type==BehaviorType::Strict || this->behavior_type==BehaviorType::ReturnsNullOnNullInput) &&
543 					  func->function_type==BehaviorType::CalledOnNullInput)))
544 				attribs[Attributes::BehaviorType]=~func->behavior_type;
545 		}
546 
547 		copyAttributes(attribs);
548 
549 		return BaseObject::getAlterDefinition(this->getSchemaName(), attributes, false, true);
550 	}
551 	catch(Exception &e)
552 	{
553 		throw Exception(e.getErrorMessage(),e.getErrorCode(),__PRETTY_FUNCTION__,__FILE__,__LINE__,&e);
554 	}
555 }
556 
configureSearchAttributes()557 void Function::configureSearchAttributes()
558 {
559 	QStringList param_types;
560 
561 	BaseObject::configureSearchAttributes();
562 	search_attribs[Attributes::ReturnType] = ret_table_columns.empty() ? *return_type : "";
563 
564 	for(auto &param : parameters)
565 		param_types += *param.getType();
566 
567 	search_attribs[Attributes::Type] = param_types.join("; ");
568 }
569