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 "pgsqltype.h"
20 #include "schemaparser.h"
21 #include "attributes.h"
22
23 vector<UserTypeConfig> PgSqlType::user_types;
24
25 template<>
26 QStringList PgSqlType::TemplateType<PgSqlType>::type_names =
27 {
28 "", // Reserved for BaseType::null
29
30 //Types used by the class PgSQLType
31 //offsets 1 to 62
32 //Note: the type char is different from "char" (with quotes)
33 //Reference: http://www.postgresql.org/docs/9.2/static/datatype-character.html
34
35 "smallint", "integer", "bigint", "decimal", "numeric",
36 "real", "double precision", "float", "serial", "bigserial", "money",
37 "character varying", "varchar", "character", "char", "\"char\"",
38 "text", "bytea",
39 "timestamp", "date", "time","timetz","timestamptz",
40 "interval", "boolean", "bool",
41 "point", "line", "lseg", "box", "path",
42 "polygon", "circle", "cidr", "inet",
43 "macaddr", "macaddr8", "bit", "bit varying", "varbit", "uuid", "xml", "json", "jsonb",
44 "smallserial", "int2vector", "int2", "int4", "int8", "float4", "float8",
45 "bpchar", "name", "abstime", "aclitem", "gtsvector", "refcursor",
46 "reltime", "tinterval", "tsquery", "tsvector", "txid_snapshot",
47
48 //Spatial type specifics for the PostGiS extension
49 //offsets 63 to 76
50 "box2d","box3d","geometry",
51 "geometry_dump","geography",
52 "geomval", "addbandarg", "rastbandarg",
53 "raster", "reclassarg", "unionarg",
54 "\"TopoGeometry\"",
55 "getfaceedges_returntype",
56 "validatetopology_returntype",
57
58 //Range-types
59 //offsets 77 to 82
60 "int4range", "int8range", "numrange",
61 "tsrange","tstzrange","daterange",
62
63 //Object Identification type (OID)
64 //offsets 83 to 97
65 "oid", "regproc", "regprocedure",
66 "regoper", "regoperator", "regclass",
67 "regrole", "regnamespace", "regtype",
68 "regconfig", "regdictionary", "xid", "cid",
69 "tid", "oidvector",
70
71 //Pseudo-types
72 //offsets 98 to 112
73 "\"any\"","anyarray","anyelement","anyenum",
74 "anynonarray", "anyrange", "cstring","internal","language_handler",
75 "record","trigger","void","opaque", "fdw_handler", "event_trigger"
76 };
77
PgSqlType()78 PgSqlType::PgSqlType()
79 {
80 type_idx = type_names.indexOf("smallint");
81 length = 0;
82 precision=-1;
83 dimension=0;
84 with_timezone=false;
85 }
86
PgSqlType(const QString & type_name)87 PgSqlType::PgSqlType(const QString &type_name) : PgSqlType()
88 {
89 setType(type_name);
90 }
91
PgSqlType(void * ptype)92 PgSqlType::PgSqlType(void *ptype) : PgSqlType()
93 {
94 setUserType(ptype);
95 }
96
PgSqlType(void * ptype,unsigned dimension,unsigned length,int precision,bool with_timezone,IntervalType interv_type,SpatialType spatial_type)97 PgSqlType::PgSqlType(void *ptype, unsigned dimension, unsigned length, int precision, bool with_timezone, IntervalType interv_type, SpatialType spatial_type) : PgSqlType()
98 {
99 setUserType(ptype);
100 setDimension(dimension);
101 setLength(length);
102 setPrecision(precision);
103 setWithTimezone(with_timezone);
104 setIntervalType(interv_type);
105 setSpatialType(spatial_type);
106 }
107
PgSqlType(const QString & type_name,unsigned dimension,unsigned length,int precision,bool with_timezone,IntervalType interv_type,SpatialType spatial_type)108 PgSqlType::PgSqlType(const QString &type_name, unsigned dimension, unsigned length, int precision, bool with_timezone, IntervalType interv_type, SpatialType spatial_type) : PgSqlType()
109 {
110 setType(type_name);
111 setDimension(dimension);
112 setLength(length);
113 setPrecision(precision);
114 setWithTimezone(with_timezone);
115 setIntervalType(interv_type);
116 setSpatialType(spatial_type);
117 }
118
PgSqlType(unsigned type_id,unsigned dimension,unsigned length,int precision,bool with_timezone,IntervalType interv_type,SpatialType spatial_type)119 PgSqlType::PgSqlType(unsigned type_id, unsigned dimension, unsigned length, int precision, bool with_timezone, IntervalType interv_type, SpatialType spatial_type) : PgSqlType()
120 {
121 setType(type_id);
122 setDimension(dimension);
123 setLength(length);
124 setPrecision(precision);
125 setWithTimezone(with_timezone);
126 setIntervalType(interv_type);
127 setSpatialType(spatial_type);
128 }
129
parseString(const QString & str)130 PgSqlType PgSqlType::parseString(const QString &str)
131 {
132 QString type_str=str.toLower().simplified(), sptype, interv;
133 bool with_tz=false;
134 unsigned dim=0, srid=0;
135 int prec=-1, len = -1;
136 int start=-1, end=-1;
137 QStringList value, intervals;
138 PgSqlType type;
139
140 //Checking if the string contains one of interval types
141 intervals = IntervalType::getTypes();
142 while(!intervals.isEmpty())
143 {
144 interv=intervals.back();
145 intervals.pop_back();
146
147 start=type_str.indexOf(QRegExp(QString("( )") + interv.toLower()));
148 if(start>=0)
149 {
150 type_str.remove(start, interv.size()+1);
151 break;
152 }
153 else
154 interv.clear();
155 }
156
157 //Check if the type contains "with time zone" descriptor
158 with_tz=QRegExp(QString("(.)*(with time zone)(.)*")).exactMatch(type_str);
159
160 //Removes the timezone descriptor
161 type_str.remove(QRegExp(QString("(with)(out)*( time zone)")));
162
163 //Count the dimension of the type and removes the array descriptor
164 dim=type_str.count(QString("[]"));
165 type_str.remove(QString("[]"));
166
167 //Check if the type is a variable length type, e.g varchar(200)
168 if(QRegExp(QString("(.)+\\(( )*[0-9]+( )*\\)")).indexIn(type_str) >=0)
169 {
170 start=type_str.indexOf('(');
171 end=type_str.indexOf(')', start);
172 len=type_str.mid(start+1, end-start-1).toInt();
173 }
174 //Check if the type is a numeric type, e.g, numeric(10,2)
175 else if(QRegExp(QString("(.)+\\(( )*[0-9]+( )*(,)( )*[0-9]+( )*\\)")).indexIn(type_str) >=0)
176 {
177 start=type_str.indexOf('(');
178 end=type_str.indexOf(')', start);
179 value=type_str.mid(start+1, end-start-1).split(',');
180 len=value[0].toInt();
181 prec=value[1].toUInt();
182 }
183 //Check if the type is a spatial type (PostGiS), e.g, geography(POINTZ, 4296)
184 else if(QRegExp(QString("(.)+\\(( )*[a-z]+(( )*(,)( )*[0-9]+( )*)?\\)"), Qt::CaseInsensitive).indexIn(type_str) >=0)
185 {
186 start=type_str.indexOf('(');
187 end=type_str.indexOf(')', start);
188 value=type_str.mid(start+1, end-start-1).split(',');
189 sptype=value[0].toUpper();
190
191 if(value.size() > 1)
192 srid=value[1].toUInt();
193 }
194
195 //If the string matches one of the regexp above remove the analyzed parts
196 if(start >=0 && end>=0)
197 type_str.remove(start, end-start+1);
198
199 /* The resultant string must be only the name of the type without [] and ().
200 NOTE: Since the string was converted to lower case at start it's necessary to get
201 it's original form from the input string in order to correctly create the type. */
202 type_str=str.mid(str.indexOf(type_str, 0, Qt::CaseInsensitive),type_str.length()).trimmed();
203
204 try
205 {
206 try
207 {
208 //Creates the type based on the extracted values
209 type=PgSqlType(type_str);
210 }
211 catch(Exception &)
212 {
213 /* In case of error (specially with PostGiS types) split the string to remove
214 the schema name and try to create the type once more */
215 QStringList typname=type_str.split('.');
216
217 if(typname.size()==2)
218 type=PgSqlType(typname[1]);
219 else
220 {
221 /* One last try it to check if the type has an entry on user defined types
222 as pg_catalog.[type name] */
223 type=PgSqlType(QString("pg_catalog.") + type_str);
224 }
225 }
226
227 type.setWithTimezone(with_tz);
228 type.setDimension(dim);
229
230 if(type.isNumericType() && len > 0 && prec >=0)
231 {
232 type.setLength(len);
233 type.setPrecision(prec);
234 }
235 else if(type.isDateTimeType() && len >= 0)
236 type.setPrecision(len);
237 else if(type.hasVariableLength() && len > 0)
238 type.setLength(len);
239
240 if(!interv.isEmpty())
241 type.setIntervalType(IntervalType(interv));
242 else if(!sptype.isEmpty())
243 type.setSpatialType(SpatialType(sptype, srid));
244
245 return type;
246 }
247 catch(Exception &e)
248 {
249 throw Exception(e.getErrorMessage(), e.getErrorCode(),__PRETTY_FUNCTION__,__FILE__,__LINE__, &e, str);
250 }
251 }
252
getTypes(bool oids,bool pseudos)253 QStringList PgSqlType::getTypes(bool oids, bool pseudos)
254 {
255 QStringList type_list;
256 unsigned total = type_names.size();
257
258 for(unsigned idx = 1; idx < total; idx++)
259 {
260 if(idx < OidStart ||
261 (oids && idx >= OidStart && idx <= OidEnd) ||
262 (pseudos && idx >= PseudoStart && idx <= PseudoEnd))
263 type_list.push_back(type_names[idx]);
264 }
265
266 return type_list;
267 }
268
setType(unsigned type_id)269 unsigned PgSqlType::setType(unsigned type_id)
270 {
271 if(type_id == Null)
272 throw Exception(ErrorCode::AsgInvalidTypeObject,__PRETTY_FUNCTION__,__FILE__,__LINE__);
273
274 if(type_id >= static_cast<unsigned>(type_names.size()))
275 return setUserType(type_id);
276
277 return TemplateType<PgSqlType>::setType(type_id);
278 }
279
setType(const QString & type_name)280 unsigned PgSqlType::setType(const QString &type_name)
281 {
282 unsigned type_id = Null, usr_type_id = Null;
283
284 type_id = getBaseTypeIndex(type_name);
285 usr_type_id=getUserTypeIndex(type_name, nullptr);
286
287 if(type_id == Null && usr_type_id == Null)
288 throw Exception(ErrorCode::AsgInvalidTypeObject,__PRETTY_FUNCTION__,__FILE__,__LINE__);
289
290 if(type_id != Null)
291 return setType(type_id);
292
293 return setUserType(usr_type_id);
294 }
295
operator =(unsigned type_id)296 unsigned PgSqlType::operator = (unsigned type_id)
297 {
298 return setType(type_id);
299 }
300
operator =(const QString & type_name)301 unsigned PgSqlType::operator = (const QString &type_name)
302 {
303 return setType(type_name);
304 }
305
getUserTypeReference()306 void *PgSqlType::getUserTypeReference()
307 {
308 if(this->isUserType())
309 return (user_types[this->type_idx - (PseudoEnd + 1)].ptype);
310 else
311 return nullptr;
312 }
313
getUserTypeConfig()314 unsigned PgSqlType::getUserTypeConfig()
315 {
316 if(this->isUserType())
317 return (user_types[this->type_idx - (PseudoEnd + 1)].type_conf);
318 else
319 return 0;
320 }
321
getTypeId()322 unsigned PgSqlType::getTypeId()
323 {
324 return !(*this);
325 }
326
getTypeName(bool incl_dimension)327 QString PgSqlType::getTypeName(bool incl_dimension)
328 {
329 if(incl_dimension)
330 {
331 QString type;
332
333 type=~(*this);
334
335 if(type!=QString("void") && dimension > 0)
336 type+=QString("[]").repeated(dimension);
337
338 return type;
339 }
340
341 return ~(*this);
342 }
343
getSQLTypeName()344 QString PgSqlType::getSQLTypeName()
345 {
346 return *(*this);
347 }
348
isRegistered(const QString & type,void * pmodel)349 bool PgSqlType::isRegistered(const QString &type, void *pmodel)
350 {
351 if(getBaseTypeIndex(type)!=BaseType::Null)
352 return true;
353 else
354 return (getUserTypeIndex(type, nullptr, pmodel)!=BaseType::Null);
355 }
356
operator ==(unsigned type_id)357 bool PgSqlType::operator == (unsigned type_id)
358 {
359 return (this->type_idx==type_id);
360 }
361
operator ==(const QString & type_name)362 bool PgSqlType::operator == (const QString &type_name)
363 {
364 return (type_idx == static_cast<unsigned>(type_names.indexOf(type_name)));
365 }
366
operator !=(const QString & type_name)367 bool PgSqlType::operator != (const QString &type_name)
368 {
369 return (!((*this)==type_name));
370 }
371
operator !=(PgSqlType type)372 bool PgSqlType::operator != (PgSqlType type)
373 {
374 return (type_idx != type.type_idx);
375 }
376
operator !=(unsigned type_id)377 bool PgSqlType::operator != (unsigned type_id)
378 {
379 return (type_idx != type_id);
380 }
381
operator ==(PgSqlType type)382 bool PgSqlType::operator == (PgSqlType type)
383 {
384 return (type_idx == type.type_idx);
385 }
386
operator ==(void * ptype)387 bool PgSqlType::operator == (void *ptype)
388 {
389 int idx = getUserTypeIndex("", ptype);
390 return (static_cast<int>(type_idx) == idx);
391 }
392
getIntervalType()393 IntervalType PgSqlType::getIntervalType()
394 {
395 return interval_type;
396 }
397
getSpatialType()398 SpatialType PgSqlType::getSpatialType()
399 {
400 return spatial_type;
401 }
402
isWithTimezone()403 bool PgSqlType::isWithTimezone()
404 {
405 return with_timezone;
406 }
407
isOIDType()408 bool PgSqlType::isOIDType()
409 {
410 return (type_idx>=OidStart && type_idx<=OidEnd);
411 }
412
isPseudoType()413 bool PgSqlType::isPseudoType()
414 {
415 return (type_idx>=PseudoStart && type_idx<=PseudoEnd);
416 }
417
operator <<(void * ptype)418 unsigned PgSqlType::operator << (void *ptype)
419 {
420 return setUserType(ptype);
421 }
422
setIntervalType(IntervalType interv_type)423 void PgSqlType::setIntervalType(IntervalType interv_type)
424 {
425 interval_type=interv_type;
426 }
427
setSpatialType(SpatialType spat_type)428 void PgSqlType::setSpatialType(SpatialType spat_type)
429 {
430 spatial_type=spat_type;
431 }
432
setWithTimezone(bool with_tz)433 void PgSqlType::setWithTimezone(bool with_tz)
434 {
435 this->with_timezone=with_tz;
436 }
437
setUserType(unsigned type_id)438 unsigned PgSqlType::setUserType(unsigned type_id)
439 {
440 unsigned lim1 = PseudoEnd + 1,
441 lim2 = lim1 + PgSqlType::user_types.size();
442
443 if(user_types.size() > 0 &&
444 (type_id >= lim1 && type_id < lim2))
445 {
446 type_idx = type_id;
447 return type_idx;
448 }
449 else
450 throw Exception(ErrorCode::AsgInvalidTypeObject,__PRETTY_FUNCTION__,__FILE__,__LINE__);
451 }
452
setUserType(void * ptype)453 unsigned PgSqlType::setUserType(void *ptype)
454 {
455 int idx = getUserTypeIndex("", ptype);
456
457 if(idx <= 0)
458 throw Exception(ErrorCode::AsgInvalidTypeObject,__PRETTY_FUNCTION__,__FILE__,__LINE__);
459
460 type_idx = idx;
461 return type_idx;
462 }
463
addUserType(const QString & type_name,void * ptype,void * pmodel,unsigned type_conf)464 void PgSqlType::addUserType(const QString &type_name, void *ptype, void *pmodel, unsigned type_conf)
465 {
466 if(!type_name.isEmpty() && ptype && pmodel &&
467 (type_conf==UserTypeConfig::DomainType ||
468 type_conf==UserTypeConfig::SequenceType ||
469 type_conf==UserTypeConfig::TableType ||
470 type_conf==UserTypeConfig::ViewType ||
471 type_conf==UserTypeConfig::ExtensionType ||
472 type_conf==UserTypeConfig::ForeignTableType ||
473 type_conf==UserTypeConfig::BaseType) &&
474 getUserTypeIndex(type_name,ptype,pmodel)==0)
475 {
476 UserTypeConfig cfg;
477
478 cfg.name=type_name;
479 cfg.ptype=ptype;
480 cfg.pmodel=pmodel;
481 cfg.type_conf=type_conf;
482 PgSqlType::user_types.push_back(cfg);
483 }
484 }
485
removeUserType(const QString & type_name,void * ptype)486 void PgSqlType::removeUserType(const QString &type_name, void *ptype)
487 {
488 if(PgSqlType::user_types.size() > 0 &&
489 !type_name.isEmpty() && ptype)
490 {
491 vector<UserTypeConfig>::iterator itr, itr_end;
492
493 itr=PgSqlType::user_types.begin();
494 itr_end=PgSqlType::user_types.end();
495
496 while(itr!=itr_end)
497 {
498 if(itr->name==type_name && itr->ptype==ptype) break;
499 else itr++;
500 }
501
502 if(itr!=itr_end)
503 {
504 itr->name=QString("__invalidated_type__");
505 itr->ptype=nullptr;
506 itr->invalidated=true;
507 }
508 }
509 }
510
renameUserType(const QString & type_name,void * ptype,const QString & new_name)511 void PgSqlType::renameUserType(const QString &type_name, void *ptype,const QString &new_name)
512 {
513 if(PgSqlType::user_types.size() > 0 &&
514 !type_name.isEmpty() && ptype && type_name!=new_name)
515 {
516 vector<UserTypeConfig>::iterator itr, itr_end;
517
518 itr=PgSqlType::user_types.begin();
519 itr_end=PgSqlType::user_types.end();
520
521 while(itr!=itr_end)
522 {
523 if(!itr->invalidated && itr->name==type_name && itr->ptype==ptype)
524 {
525 itr->name=new_name;
526 break;
527 }
528 itr++;
529 }
530 }
531 }
532
removeUserTypes(void * pmodel)533 void PgSqlType::removeUserTypes(void *pmodel)
534 {
535 if(pmodel)
536 {
537 vector<UserTypeConfig>::iterator itr;
538 unsigned idx=0;
539
540 itr=user_types.begin();
541 while(itr!=user_types.end())
542 {
543 if(itr->pmodel==pmodel)
544 {
545 user_types.erase(itr);
546 itr=user_types.begin() + idx;
547 }
548 else
549 {
550 idx++;
551 itr++;
552 }
553 }
554 }
555 }
556
getBaseTypeIndex(const QString & type_name)557 unsigned PgSqlType::getBaseTypeIndex(const QString &type_name)
558 {
559 QString aux_name=type_name;
560
561 aux_name.remove(QString("[]"));
562 aux_name.remove(QRegExp(QString("( )(with)(out)?(.)*")));
563 aux_name=aux_name.trimmed();
564 return getType(aux_name, type_names);
565 }
566
getUserTypeIndex(const QString & type_name,void * ptype,void * pmodel)567 unsigned PgSqlType::getUserTypeIndex(const QString &type_name, void *ptype, void *pmodel)
568 {
569 if(user_types.size() > 0 && (!type_name.isEmpty() || ptype))
570 {
571 vector<UserTypeConfig>::iterator itr, itr_end;
572 int idx=0;
573
574 itr=user_types.begin();
575 itr_end=user_types.end();
576
577 while(itr!=itr_end)
578 {
579 if(!itr->invalidated &&
580 (((!type_name.isEmpty() && itr->name==type_name) || (ptype && itr->ptype==ptype)) &&
581 ((pmodel && itr->pmodel==pmodel) || !pmodel)))
582 break;
583
584 idx++;
585 itr++;
586 }
587
588 if(itr!=itr_end)
589 return (PseudoEnd + 1 + idx);
590 else
591 return BaseType::Null;
592 }
593 else return BaseType::Null;
594 }
595
getUserTypeName(unsigned type_id)596 QString PgSqlType::getUserTypeName(unsigned type_id)
597 {
598 unsigned lim1, lim2;
599
600 lim1=PseudoEnd + 1;
601 lim2=lim1 + user_types.size();
602
603
604 if(user_types.size() > 0 &&
605 (type_id >= lim1 && type_id < lim2))
606 return (user_types[type_id - lim1].name);
607 else
608 return "";
609 }
610
getUserTypes(QStringList & type_list,void * pmodel,unsigned inc_usr_types)611 void PgSqlType::getUserTypes(QStringList &type_list, void *pmodel, unsigned inc_usr_types)
612 {
613 unsigned idx,total;
614
615 type_list.clear();
616 total=user_types.size();
617
618 for(idx=0; idx < total; idx++)
619 {
620 //Only the user defined types of the specified model are retrieved
621 if(!user_types[idx].invalidated && user_types[idx].pmodel==pmodel &&
622 ((inc_usr_types & user_types[idx].type_conf) == user_types[idx].type_conf))
623 type_list.push_back(user_types[idx].name);
624 }
625 }
626
getUserTypes(vector<void * > & ptypes,void * pmodel,unsigned inc_usr_types)627 void PgSqlType::getUserTypes(vector<void *> &ptypes, void *pmodel, unsigned inc_usr_types)
628 {
629 unsigned idx, total;
630
631 ptypes.clear();
632 total=user_types.size();
633
634 for(idx=0; idx < total; idx++)
635 {
636 //Only the user defined types of the specified model are retrieved
637 if(!user_types[idx].invalidated && user_types[idx].pmodel==pmodel &&
638 ((inc_usr_types & user_types[idx].type_conf) == user_types[idx].type_conf))
639 ptypes.push_back(user_types[idx].ptype);
640 }
641 }
642
operator ~()643 QString PgSqlType::operator ~ ()
644 {
645 if(type_idx >= PseudoEnd + 1)
646 return (user_types[type_idx - (PseudoEnd + 1)].name);
647 else
648 {
649 QString name = type_names[type_idx];
650
651 if(with_timezone && (name==QString("time") || name==QString("timestamp")))
652 name+=QString(" with time zone");
653
654 return name;
655 }
656 }
657
isArrayType()658 bool PgSqlType::isArrayType()
659 {
660 return (dimension > 0);
661 }
662
isUserType()663 bool PgSqlType::isUserType()
664 {
665 return (type_idx > PseudoEnd);
666 }
667
isNetworkType()668 bool PgSqlType::isNetworkType()
669 {
670 QString curr_type=(!isUserType() ? type_names[type_idx] : "");
671
672 return (!isUserType() &&
673 (curr_type==QString("cidr") ||
674 curr_type==QString("inet") ||
675 curr_type==QString("macaddr") ||
676 curr_type==QString("macaddr8")));
677 }
678
isGiSType(const QString & type_name)679 bool PgSqlType::isGiSType(const QString &type_name)
680 {
681 return (type_name==QString("geography") ||
682 type_name==QString("geometry") ||
683 type_name==QString("geometry_dump"));
684 }
685
isBoxType()686 bool PgSqlType::isBoxType()
687 {
688 QString curr_type=(!isUserType() ? type_names[type_idx] : "");
689 return (!isUserType() && isBoxType(curr_type));
690 }
691
isBoxType(const QString & type_name)692 bool PgSqlType::isBoxType(const QString &type_name)
693 {
694 return (type_name==QString("box2d") ||
695 type_name==QString("box3d"));
696 }
697
isGiSType()698 bool PgSqlType::isGiSType()
699 {
700 QString curr_type=(!isUserType() ? type_names[type_idx] : "");
701 return (!isUserType() && isGiSType(curr_type));
702 }
703
isRangeType()704 bool PgSqlType::isRangeType()
705 {
706 QString curr_type=(!isUserType() ? type_names[type_idx] : "");
707
708 return (!isUserType() &&
709 (curr_type==QString("int4range") || curr_type==QString("int8range") ||
710 curr_type==QString("numrange") || curr_type==QString("tsrange") ||
711 curr_type==QString("tstzrange") || curr_type==QString("daterange")));
712 }
713
isSerialType()714 bool PgSqlType::isSerialType()
715 {
716 QString curr_type=(!isUserType() ? type_names[this->type_idx] : "");
717
718 return (!isUserType() &&
719 (curr_type==QString("serial") ||
720 curr_type==QString("smallserial") ||
721 curr_type==QString("bigserial")));
722 }
723
isDateTimeType()724 bool PgSqlType::isDateTimeType()
725 {
726 QString curr_type=(!isUserType() ? type_names[this->type_idx] : "");
727
728 return (!isUserType() &&
729 (curr_type==QString("time") || curr_type==QString("timestamp") ||
730 curr_type==QString("interval") || curr_type==QString("date") ||
731 curr_type==QString("timetz") || curr_type==QString("timestamptz")));
732 }
733
isNumericType()734 bool PgSqlType::isNumericType()
735 {
736 QString curr_type=(!isUserType() ? type_names[this->type_idx] : "");
737
738 return (!isUserType() &&
739 (curr_type==QString("numeric") || curr_type==QString("decimal")));
740 }
741
isIntegerType()742 bool PgSqlType::isIntegerType()
743 {
744 QString curr_type=(!isUserType() ? type_names[this->type_idx] : "");
745
746 return (!isUserType() &&
747 (curr_type==QString("smallint") || curr_type==QString("integer") ||
748 curr_type==QString("bigint") || curr_type==QString("int4") ||
749 curr_type==QString("int8") || curr_type==QString("int2")));
750 }
751
hasVariableLength()752 bool PgSqlType::hasVariableLength()
753 {
754 QString curr_type=(!isUserType() ? type_names[this->type_idx] : "");
755
756 return (!isUserType() &&
757 (curr_type==QString("numeric") || curr_type==QString("decimal") ||
758 curr_type==QString("character varying") || curr_type==QString("varchar") ||
759 curr_type==QString("character") || curr_type==QString("char") ||
760 curr_type==QString("bit") || curr_type==QString("bit varying") ||
761 curr_type==QString("varbit")));
762 }
763
isCharacterType()764 bool PgSqlType::isCharacterType()
765 {
766 QString curr_type=(!isUserType() ? type_names[this->type_idx] : "");
767
768 return (curr_type==QString("\"char\"") || curr_type==QString("char") ||
769 curr_type==QString("character") || curr_type==QString("varchar") ||
770 curr_type==QString("character varying") || curr_type==QString("text"));
771 }
772
isPolymorphicType()773 bool PgSqlType::isPolymorphicType()
774 {
775 QString curr_type=(!isUserType() ? type_names[this->type_idx] : "");
776
777 return (curr_type==QString("anyarray") || curr_type==QString("anyelement") ||
778 curr_type==QString("anyenum") || curr_type==QString("anynonarray") ||
779 curr_type==QString("anyrange") || curr_type==QString("\"any\""));
780 }
781
acceptsPrecision()782 bool PgSqlType::acceptsPrecision()
783 {
784 return (isNumericType() ||
785 (!isUserType() && type_names[this->type_idx]!=QString("date") && isDateTimeType()));
786 }
787
canCastTo(PgSqlType type)788 bool PgSqlType::canCastTo(PgSqlType type)
789 {
790 // If the types are the same of belongs to the same category they naturally can be casted
791 if(this->type_idx==type.type_idx ||
792 (isCharacterType() && type.isCharacterType()) ||
793 (isDateTimeType() && type.isDateTimeType()) ||
794 (isNumericType() && type.isNumericType()) ||
795 (isNetworkType() && type.isNetworkType()) ||
796
797 //Polymorphics anyarray, anyrange, anynoarray, anyenum to anyelement
798 ((isPolymorphicType() && type==QString("anyelement")) ||
799 ((*this)==QString("anyelement") && type.isPolymorphicType())) ||
800
801 //Character to network address
802 ((isCharacterType() || isNetworkType()) &&
803 (type.isCharacterType() || type.isNetworkType())) ||
804
805 //Integer to OID
806 ((isIntegerType() || isOIDType()) &&
807 (type.isIntegerType() || type.isOIDType())) ||
808
809 //abstime to integer
810 ((((*this)==QString("integer") || (*this)==QString("int4")) && type==QString("abstime")) ||
811 (((*this)==QString("abstime") && (type==QString("integer") || type==QString("int4"))))))
812
813 return true;
814
815 return false;
816 }
817
isEquivalentTo(PgSqlType type)818 bool PgSqlType::isEquivalentTo(PgSqlType type)
819 {
820 unsigned this_idx=0, type_idx=0;
821 static vector<QStringList> types={{QString("int2"),QString("smallint")},
822 {QString("int4"),QString("integer")},
823 {QString("int8"),QString("bigint")},
824 {QString("decimal"),QString("numeric")},
825 {QString("character varying"),QString("varchar")},
826 {QString("character"), QString("char")},
827 {QString("bool"), QString("boolean")},
828 {QString("bit varying"),QString("varbit")},
829 {QString("oid"),QString("regproc"),QString("regprocedure"),
830 QString("regoper"),QString("regoperator"),QString("regclass"),
831 QString("regtype"),QString("regconfig"),QString("regdictionary")},
832 {QString("timestamptz"),QString("timestamp with time zone")}};
833
834 //If the types are equal there is no need to perform further operations
835 if(*this==type)
836 return true;
837
838 //Getting the index which the this type is in
839 for(QStringList list : types)
840 {
841 if(list.contains(~(*this))) break;
842 this_idx++;
843 }
844
845 //Getting the index which 'type' is in
846 for(QStringList list : types)
847 {
848 if(list.contains(~type)) break;
849 type_idx++;
850 }
851
852 return (this_idx < types.size() && type_idx < types.size() &&
853 this_idx==type_idx &&
854 this->isArrayType()==type.isArrayType());
855 }
856
isExactTo(PgSqlType type)857 bool PgSqlType::isExactTo(PgSqlType type)
858 {
859 return (this->type_idx == type.type_idx &&
860 this->dimension == type.dimension &&
861 this->length == type.length &&
862 this->precision == type.precision &&
863 this->with_timezone == type.with_timezone &&
864 this->interval_type == type.interval_type &&
865 this->spatial_type == type.spatial_type);
866 }
867
getAliasType()868 PgSqlType PgSqlType::getAliasType()
869 {
870 if(!isUserType())
871 {
872 if(type_names[this->type_idx]==QString("serial"))
873 return (PgSqlType(QString("integer")));
874
875 if(type_names[this->type_idx]==QString("smallserial"))
876 return (PgSqlType(QString("smallint")));
877
878 if(type_names[this->type_idx]==QString("bigserial"))
879 return (PgSqlType(QString("bigint")));
880
881 return (PgSqlType(type_names[this->type_idx]));
882 }
883 else
884 return *this;
885 }
886
setDimension(unsigned dim)887 void PgSqlType::setDimension(unsigned dim)
888 {
889 if(dim > 0 && this->isUserType())
890 {
891 int idx=getUserTypeIndex(~(*this), nullptr) - (PseudoEnd + 1);
892 if(static_cast<unsigned>(idx) < user_types.size() &&
893 user_types[idx].type_conf==UserTypeConfig::SequenceType)
894 throw Exception(ErrorCode::AsgInvalidSequenceTypeArray,__PRETTY_FUNCTION__,__FILE__,__LINE__);
895 }
896
897 dimension=dim;
898 }
899
setLength(unsigned len)900 void PgSqlType::setLength(unsigned len)
901 {
902 this->length=len;
903 }
904
setPrecision(int prec)905 void PgSqlType::setPrecision(int prec)
906 {
907 if(!isUserType())
908 {
909 //Raises an error if the user tries to specify a precision > length
910 if(((type_names[type_idx]==QString("numeric") ||
911 type_names[type_idx]==QString("decimal")) && prec > static_cast<int>(length)))
912 throw Exception(ErrorCode::AsgInvalidPrecision,__PRETTY_FUNCTION__,__FILE__,__LINE__);
913
914 //Raises an error if the precision is greater thant 6
915 if(((type_names[type_idx]==QString("time") ||
916 type_names[type_idx]==QString("timestamp") ||
917 type_names[type_idx]==QString("interval")) && prec > 6))
918 throw Exception(ErrorCode::AsgInvalidPrecisionTimestamp,__PRETTY_FUNCTION__,__FILE__,__LINE__);
919
920 this->precision=prec;
921 }
922 }
923
getDimension()924 unsigned PgSqlType::getDimension()
925 {
926 return dimension;
927 }
928
getLength()929 unsigned PgSqlType::getLength()
930 {
931 return length;
932 }
933
getPrecision()934 int PgSqlType::getPrecision()
935 {
936 return precision;
937 }
938
getCodeDefinition(unsigned def_type,QString ref_type)939 QString PgSqlType::getCodeDefinition(unsigned def_type,QString ref_type)
940 {
941 if(def_type==SchemaParser::SqlDefinition)
942 return *(*this);
943 else
944 {
945 attribs_map attribs;
946 SchemaParser schparser;
947
948 attribs[Attributes::Length]="";
949 attribs[Attributes::Dimension]="";
950 attribs[Attributes::Precision]="";
951 attribs[Attributes::WithTimezone]="";
952 attribs[Attributes::IntervalType]="";
953 attribs[Attributes::SpatialType]="";
954 attribs[Attributes::Variation]="";
955 attribs[Attributes::Srid]="";
956 attribs[Attributes::RefType]=ref_type;
957
958 attribs[Attributes::Name]=(~(*this));
959 attribs[Attributes::Length]=QString("%1").arg(this->length);
960
961 if(dimension > 0)
962 attribs[Attributes::Dimension]=QString("%1").arg(this->dimension);
963
964 if(precision >= 0)
965 attribs[Attributes::Precision]=QString("%1").arg(this->precision);
966
967 if(interval_type != BaseType::Null)
968 attribs[Attributes::IntervalType]=(~interval_type);
969
970 if(isGiSType())
971 {
972 attribs[Attributes::SpatialType]=(~spatial_type);
973 attribs[Attributes::Variation]=QString("%1").arg(spatial_type.getVariation());
974 attribs[Attributes::Srid]=QString("%1").arg(spatial_type.getSRID());
975 }
976
977 if(with_timezone)
978 attribs[Attributes::WithTimezone]=Attributes::True;
979
980 return schparser.getCodeDefinition(Attributes::PgSqlBaseType, attribs, def_type);
981 }
982 }
983
operator *()984 QString PgSqlType::operator * ()
985 {
986 QString fmt_type, type, aux;
987 unsigned idx;
988
989 type=~(*this);
990
991 //Generation the definition for the spatial types (PostGiS)
992 if(type==QString("geometry") || type==QString("geography"))
993 fmt_type=type + (*spatial_type);
994 else if(hasVariableLength())
995 {
996 //Configuring the precision
997 if((type==QString("numeric") || type==QString("decimal")) && length >= 1 && precision>=0 && precision<=static_cast<int>(length))
998 aux=QString("%1(%2,%3)").arg(type_names[type_idx]).arg(length).arg(precision);
999 //Configuring the length for the type
1000 else if(length >= 1)
1001 aux=QString("%1(%2)").arg(type_names[type_idx]).arg(length);
1002 else
1003 aux=type;
1004
1005 fmt_type=aux;
1006 }
1007 else if(type!=QString("numeric") && type!=QString("decimal") && acceptsPrecision())
1008 {
1009 if(type!=QString("interval"))
1010 {
1011 aux = type_names[type_idx];
1012
1013 if(precision >= 0)
1014 aux+=QString("(%1)").arg(precision);
1015
1016 if(with_timezone)
1017 aux+=QString(" with time zone");
1018 }
1019 else
1020 {
1021 aux = type_names[type_idx];
1022
1023 if(interval_type!=BaseType::Null)
1024 aux+=QString(" %1 ").arg(~interval_type);
1025
1026 if(precision >= 0)
1027 aux+=QString("(%1)").arg(precision);
1028 }
1029
1030 fmt_type=aux;
1031 }
1032 else
1033 fmt_type=type;
1034
1035
1036 if(type!=QString("void") && dimension > 0)
1037 {
1038 for(idx=0; idx < dimension; idx++)
1039 fmt_type+=QString("[]");
1040 }
1041
1042 return fmt_type;
1043 }
1044