1 //////////////////////////////////////////////////////////////////////////
2 //
3 // pgAdmin III - PostgreSQL Tools
4 //
5 // Copyright (C) 2002 - 2016, The pgAdmin Development Team
6 // This software is released under the PostgreSQL Licence
7 //
8 // pgDomain.cpp - Domain class
9 //
10 //////////////////////////////////////////////////////////////////////////
11 
12 // wxWindows headers
13 #include <wx/wx.h>
14 
15 // App headers
16 #include "pgAdmin3.h"
17 #include "frm/frmMain.h"
18 #include "utils/misc.h"
19 #include "schema/pgDomain.h"
20 #include "schema/pgDatatype.h"
21 
22 #include "schema/pgTable.h"
23 #include "schema/pgColumn.h"
24 #include "schema/pgIndexConstraint.h"
25 #include "schema/pgForeignKey.h"
26 #include "schema/pgCheck.h"
27 #include "utils/sysSettings.h"
28 #include "utils/pgfeatures.h"
29 #include "schema/pgRule.h"
30 #include "schema/pgTrigger.h"
31 #include "schema/pgConstraints.h"
32 #include "schema/gpPartition.h"
33 
34 
pgDomain(pgSchema * newSchema,const wxString & newName)35 pgDomain::pgDomain(pgSchema *newSchema, const wxString &newName)
36 	: pgSchemaObject(newSchema, domainFactory, newName)
37 {
38 }
39 
~pgDomain()40 pgDomain::~pgDomain()
41 {
42 }
43 
GetTranslatedMessage(int kindOfMessage) const44 wxString pgDomain::GetTranslatedMessage(int kindOfMessage) const
45 {
46 	wxString message = wxEmptyString;
47 
48 	switch (kindOfMessage)
49 	{
50 		case RETRIEVINGDETAILS:
51 			message = _("Retrieving details on domain");
52 			message += wxT(" ") + GetName();
53 			break;
54 		case REFRESHINGDETAILS:
55 			message = _("Refreshing domain");
56 			message += wxT(" ") + GetName();
57 			break;
58 		case DROPINCLUDINGDEPS:
59 			message = wxString::Format(_("Are you sure you wish to drop domain \"%s\" including all objects that depend on it?"),
60 			                           GetFullIdentifier().c_str());
61 			break;
62 		case DROPEXCLUDINGDEPS:
63 			message = wxString::Format(_("Are you sure you wish to drop domain \"%s\"?"),
64 			                           GetFullIdentifier().c_str());
65 			break;
66 		case DROPCASCADETITLE:
67 			message = _("Drop domain cascaded?");
68 			break;
69 		case DROPTITLE:
70 			message = _("Drop domain?");
71 			break;
72 		case PROPERTIESREPORT:
73 			message = _("Domain properties report");
74 			message += wxT(" - ") + GetName();
75 			break;
76 		case PROPERTIES:
77 			message = _("Domain properties");
78 			break;
79 		case DDLREPORT:
80 			message = _("Domain DDL report");
81 			message += wxT(" - ") + GetName();
82 			break;
83 		case DDL:
84 			message = _("Domain DDL");
85 			break;
86 		case DEPENDENCIESREPORT:
87 			message = _("Domain dependencies report");
88 			message += wxT(" - ") + GetName();
89 			break;
90 		case DEPENDENCIES:
91 			message = _("Domain dependencies");
92 			break;
93 		case DEPENDENTSREPORT:
94 			message = _("Domain dependents report");
95 			message += wxT(" - ") + GetName();
96 			break;
97 		case DEPENDENTS:
98 			message = _("Domain dependents");
99 			break;
100 	}
101 
102 	return message;
103 }
104 
105 
DropObject(wxFrame * frame,ctlTree * browser,bool cascaded)106 bool pgDomain::DropObject(wxFrame *frame, ctlTree *browser, bool cascaded)
107 {
108 	wxString sql = wxT("DROP DOMAIN ") + this->GetSchema()->GetQuotedIdentifier() + wxT(".") + this->GetQuotedIdentifier();
109 	if (cascaded)
110 		sql += wxT(" CASCADE");
111 	return GetDatabase()->ExecuteVoid(sql);
112 }
113 
GetSql(ctlTree * browser)114 wxString pgDomain::GetSql(ctlTree *browser)
115 {
116 	if (sql.IsNull())
117 	{
118 		sql = wxT("-- Domain: ") + GetQuotedFullIdentifier() + wxT("\n\n")
119 		      + wxT("-- DROP DOMAIN ") + GetQuotedFullIdentifier() + wxT(";")
120 		      + wxT("\n\nCREATE DOMAIN ") + GetQuotedFullIdentifier()
121 		      + wxT("\n  AS ") + GetQuotedBasetype();
122 		if (GetCollationOid() > 0)
123 			sql += wxT("\n  COLLATE ") + GetQuotedCollation();
124 		AppendIfFilled(sql, wxT("\n  DEFAULT "), GetDefault());
125 		// CONSTRAINT Name Dont know where it's stored, may be omitted anyway
126 		if (notNull)
127 			sql += wxT("\n  NOT NULL");
128 
129 		// Get a count of the constraints.
130 		int consCount = 0;
131 		pgCollection *constraints = browser->FindCollection(checkFactory, GetId());
132 		if (constraints)
133 		{
134 			constraints->ShowTreeDetail(browser);
135 			treeObjectIterator consIt(browser, constraints);
136 
137 			pgObject *data;
138 
139 			while ((data = consIt.GetNextObject()) != 0)
140 			{
141 				data->ShowTreeDetail(browser);
142 
143 				sql += wxT("\n  CONSTRAINT ") + data->GetQuotedIdentifier()
144 				       + wxT(" ") + data->GetTypeName().Upper()
145 				       + wxT(" ") ;
146 
147 				switch (data->GetMetaType())
148 				{
149 					case PGM_CHECK:
150 						sql += wxT("(") + ((pgCheck *)data)->GetDefinition() + wxT(")");
151 						if (GetDatabase()->BackendMinimumVersion(9, 2) && !((pgCheck *)data)->GetValid())
152 							sql += wxT(" NOT VALID");
153 						break;
154 				}
155 			}
156 		}
157 
158 		sql += wxT(";\n")
159 		       + GetOwnerSql(7, 4)
160 		       + GetCommentSql();
161 
162 		if (GetConnection()->BackendMinimumVersion(9, 1))
163 			sql += GetSeqLabelsSql();
164 	}
165 
166 	return sql;
167 }
168 
169 
GetNewMenu()170 wxMenu *pgDomain::GetNewMenu()
171 {
172 	wxMenu *menu = pgObject::GetNewMenu();
173 	if (schema->GetCreatePrivilege())
174 	{
175 		checkFactory.AppendMenu(menu);
176 	}
177 	return menu;
178 }
179 
180 
ShowTreeDetail(ctlTree * browser,frmMain * form,ctlListView * properties,ctlSQLBox * sqlPane)181 void pgDomain::ShowTreeDetail(ctlTree *browser, frmMain *form, ctlListView *properties, ctlSQLBox *sqlPane)
182 {
183 	if (!expandedKids)
184 	{
185 		expandedKids = true;
186 
187 		browser->RemoveDummyChild(this);
188 
189 		// Log
190 		wxLogInfo(wxT("Adding child object to domain %s"), GetIdentifier().c_str());
191 		if (GetConnection()->BackendMinimumVersion(7, 4))
192 			browser->AppendCollection(this, constraintFactory);
193 	}
194 
195 	if (properties)
196 	{
197 		CreateListColumns(properties);
198 
199 		properties->AppendItem(_("Name"), GetName());
200 		properties->AppendItem(_("OID"), GetOid());
201 		properties->AppendItem(_("Owner"), GetOwner());
202 		properties->AppendItem(_("Base type"), GetBasetype());
203 		if (GetDimensions())
204 			properties->AppendItem(_("Dimensions"), GetDimensions());
205 		if (GetCollationOid() > 0)
206 			properties->AppendItem(_("Collation"), GetQuotedCollation());
207 		properties->AppendItem(_("Default"), GetDefault());
208 		properties->AppendYesNoItem(_("Not NULL?"), GetNotNull());
209 		properties->AppendYesNoItem(_("System domain?"), GetSystemObject());
210 		properties->AppendItem(_("Comment"), firstLineOnly(GetComment()));
211 
212 		if (!GetLabels().IsEmpty())
213 		{
214 			wxArrayString seclabels = GetProviderLabelArray();
215 			if (seclabels.GetCount() > 0)
216 			{
217 				for (unsigned int index = 0 ; index < seclabels.GetCount() - 1 ; index += 2)
218 				{
219 					properties->AppendItem(seclabels.Item(index), seclabels.Item(index + 1));
220 				}
221 			}
222 		}
223 	}
224 }
225 
226 
227 
Refresh(ctlTree * browser,const wxTreeItemId item)228 pgObject *pgDomain::Refresh(ctlTree *browser, const wxTreeItemId item)
229 {
230 	pgObject *domain = 0;
231 
232 	pgCollection *coll = browser->GetParentCollection(item);
233 	if (coll)
234 		domain = domainFactory.CreateObjects(coll, 0, wxT("   AND d.oid=") + GetOidStr() + wxT("\n"));
235 
236 	return domain;
237 }
238 
239 
Validate(frmMain * form)240 void pgDomain::Validate(frmMain *form)
241 {
242 	wxString sql = wxT("ALTER DOMAIN ") + GetQuotedFullIdentifier()
243 	               + wxT("\n  VALIDATE CONSTRAINT ") + GetCheckConstraintName();
244 	GetDatabase()->ExecuteVoid(sql);
245 
246 	iSetValid(true);
247 	UpdateIcon(form->GetBrowser());
248 }
249 
250 
251 ////////////////////////////////////////////////////
252 
253 
254 
CreateObjects(pgCollection * collection,ctlTree * browser,const wxString & restriction)255 pgObject *pgDomainFactory::CreateObjects(pgCollection *collection, ctlTree *browser, const wxString &restriction)
256 {
257 	wxString sql;
258 	pgDomain *domain = 0;
259 
260 	pgDatabase *db = collection->GetDatabase();
261 
262 	sql = wxT("SELECT d.oid, d.typname as domname, d.typbasetype, format_type(b.oid,NULL) as basetype, pg_get_userbyid(d.typowner) as domainowner, \n");
263 	if (collection->GetDatabase()->BackendMinimumVersion(9, 1))
264 		sql += wxT("c.oid AS colloid, c.collname, cn.nspname as collnspname, \n");
265 	sql += wxT("       d.typlen, d.typtypmod, d.typnotnull, d.typdefault, d.typndims, d.typdelim, bn.nspname as basensp,\n")
266 	       wxT("       description, (SELECT COUNT(1) FROM pg_type t2 WHERE t2.typname=d.typname) > 1 AS domisdup,\n")
267 	       wxT("       (SELECT COUNT(1) FROM pg_type t3 WHERE t3.typname=b.typname) > 1 AS baseisdup");
268 	if (collection->GetDatabase()->BackendMinimumVersion(9, 1))
269 	{
270 		sql += wxT(",\n(SELECT array_agg(label) FROM pg_seclabels sl1 WHERE sl1.objoid=d.oid) AS labels");
271 		sql += wxT(",\n(SELECT array_agg(provider) FROM pg_seclabels sl2 WHERE sl2.objoid=d.oid) AS providers");
272 	}
273 	sql += wxT("\n   FROM pg_type d\n")
274 	       wxT("  JOIN pg_type b ON b.oid = d.typbasetype\n")
275 	       wxT("  JOIN pg_namespace bn ON bn.oid=b.typnamespace\n")
276 	       wxT("  LEFT OUTER JOIN pg_description des ON (des.objoid=d.oid AND des.classoid='pg_type'::regclass)\n");
277 	if (collection->GetDatabase()->BackendMinimumVersion(9, 1))
278 		sql += wxT("  LEFT OUTER JOIN pg_collation c ON d.typcollation=c.oid\n")
279 		       wxT("  LEFT OUTER JOIN pg_namespace cn ON c.collnamespace=cn.oid\n");
280 	sql += wxT(" WHERE d.typtype = 'd' AND d.typnamespace = ") + NumToStr(collection->GetSchema()->GetOid()) + wxT("::oid\n")
281 	       + restriction +
282 	       wxT(" ORDER BY d.typname");
283 	pgSet *domains = db->ExecuteSet(sql);
284 
285 	if (domains)
286 	{
287 		while (!domains->Eof())
288 		{
289 			domain = new pgDomain(collection->GetSchema(), domains->GetVal(wxT("domname")));
290 
291 			domain->iSetOid(domains->GetOid(wxT("oid")));
292 			domain->iSetOwner(domains->GetVal(wxT("domainowner")));
293 			domain->iSetBasetype(domains->GetVal(wxT("basetype")));
294 			domain->iSetBasetypeOid(domains->GetOid(wxT("typbasetype")));
295 			domain->iSetComment(domains->GetVal(wxT("description")));
296 			long typmod = domains->GetLong(wxT("typtypmod"));
297 
298 			pgDatatype dt(domains->GetVal(wxT("basensp")), domains->GetVal(wxT("basetype")),
299 			              domains->GetBool(wxT("baseisdup")), domains->GetLong(wxT("typndims")), typmod);
300 
301 			domain->iSetTyplen(domains->GetLong(wxT("typlen")));
302 			domain->iSetTypmod(typmod);
303 			domain->iSetLength(dt.Length());
304 			domain->iSetPrecision(dt.Precision());
305 			domain->iSetBasetype(dt.GetSchemaPrefix(db) + dt.FullName());
306 			domain->iSetQuotedBasetype(dt.GetQuotedSchemaPrefix(db) + dt.QuotedFullName());
307 			domain->iSetDefault(domains->GetVal(wxT("typdefault")));
308 			domain->iSetNotNull(domains->GetBool(wxT("typnotnull")));
309 			domain->iSetDimensions(domains->GetLong(wxT("typndims")));
310 			domain->iSetDelimiter(domains->GetVal(wxT("typdelim")));
311 			domain->iSetIsDup(domains->GetBool(wxT("domisdup")));
312 			if (collection->GetDatabase()->BackendMinimumVersion(9, 1))
313 			{
314 				domain->iSetCollation(domains->GetVal(wxT("collname")));
315 				domain->iSetQuotedCollation(qtIdent(domains->GetVal(wxT("collnspname"))) + wxT(".") + qtIdent(domains->GetVal(wxT("collname"))));
316 				domain->iSetCollationOid(domains->GetOid(wxT("colloid")));
317 			}
318 			else
319 				domain->iSetCollationOid(0);
320 
321 			if (collection->GetDatabase()->BackendMinimumVersion(9, 1))
322 			{
323 				domain->iSetProviders(domains->GetVal(wxT("providers")));
324 				domain->iSetLabels(domains->GetVal(wxT("labels")));
325 			}
326 
327 			// we suppose the constraint valid now
328 			// this is checked in ShowTreeDetail for each domain
329 			domain->iSetValid(true);
330 
331 			if (browser)
332 			{
333 				browser->AppendObject(collection, domain);
334 				domains->MoveNext();
335 			}
336 			else
337 				break;
338 		}
339 
340 		delete domains;
341 	}
342 	return domain;
343 }
344 
345 /////////////////////////////
346 
pgDomainCollection(pgaFactory * factory,pgSchema * sch)347 pgDomainCollection::pgDomainCollection(pgaFactory *factory, pgSchema *sch)
348 	: pgSchemaObjCollection(factory, sch)
349 {
350 }
351 
352 
GetTranslatedMessage(int kindOfMessage) const353 wxString pgDomainCollection::GetTranslatedMessage(int kindOfMessage) const
354 {
355 	wxString message = wxEmptyString;
356 
357 	switch (kindOfMessage)
358 	{
359 		case RETRIEVINGDETAILS:
360 			message = _("Retrieving details on domains");
361 			break;
362 		case REFRESHINGDETAILS:
363 			message = _("Refreshing domains");
364 			break;
365 		case OBJECTSLISTREPORT:
366 			message = _("Domains list report");
367 			break;
368 	}
369 
370 	return message;
371 }
372 
373 /////////////////////////////
374 
375 #include "images/domain.pngc"
376 #include "images/domain-sm.pngc"
377 #include "images/domains.pngc"
378 
pgDomainFactory()379 pgDomainFactory::pgDomainFactory()
380 	: pgSchemaObjFactory(__("Domain"), __("New Domain..."), __("Create a new Domain."), domain_png_img, domain_sm_png_img)
381 {
382 	metaType = PGM_DOMAIN;
383 }
384 
385 
CreateCollection(pgObject * obj)386 pgCollection *pgDomainFactory::CreateCollection(pgObject *obj)
387 {
388 	return new pgDomainCollection(GetCollectionFactory(), (pgSchema *)obj);
389 }
390 
391 pgDomainFactory domainFactory;
392 static pgaCollectionFactory cf(&domainFactory, __("Domains"), domains_png_img);
393 
validateDomainCheckFactory(menuFactoryList * list,wxMenu * mnu,ctlMenuToolbar * toolbar)394 validateDomainCheckFactory::validateDomainCheckFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar) : contextActionFactory(list)
395 {
396 	mnu->Append(id, _("Validate domain check constraint"), _("Validate the selected domain check constraint."));
397 }
398 
399 
StartDialog(frmMain * form,pgObject * obj)400 wxWindow *validateDomainCheckFactory::StartDialog(frmMain *form, pgObject *obj)
401 {
402 	((pgDomain *)obj)->Validate(form);
403 	((pgDomain *)obj)->SetDirty();
404 
405 	wxTreeItemId item = form->GetBrowser()->GetSelection();
406 	if (obj == form->GetBrowser()->GetObject(item))
407 	{
408 		obj->ShowTreeDetail(form->GetBrowser(), 0, form->GetProperties());
409 		form->GetSqlPane()->SetReadOnly(false);
410 		form->GetSqlPane()->SetText(((pgDomain *)obj)->GetSql(form->GetBrowser()));
411 		form->GetSqlPane()->SetReadOnly(true);
412 	}
413 	form->GetMenuFactories()->CheckMenu(obj, form->GetMenuBar(), (ctlMenuToolbar *)form->GetToolBar());
414 
415 	return 0;
416 }
417 
418 
CheckEnable(pgObject * obj)419 bool validateDomainCheckFactory::CheckEnable(pgObject *obj)
420 {
421 	return obj && obj->IsCreatedBy(domainFactory) && obj->CanEdit()
422 	       && ((pgDomain *)obj)->GetConnection()->BackendMinimumVersion(9, 2)
423 	       && !((pgDomain *)obj)->GetValid();
424 }
425 
426