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