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 ¶m : parameters)
565 param_types += *param.getType();
566
567 search_attribs[Attributes::Type] = param_types.join("; ");
568 }
569