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 // ddTableFigure.cpp - Draw table figure of a model
9 //
10 ////////////////////////////////////////////////////////////////////////////
11 
12 
13 #include "pgAdmin3.h"
14 
15 // wxWindows headers
16 #include <wx/wx.h>
17 #include <wx/dcbuffer.h>
18 #include <wx/pen.h>
19 
20 // App headers
21 #include "dd/dditems/figures/ddTableFigure.h"
22 #include "dd/dditems/figures/ddTextTableItemFigure.h"
23 #include "dd/dditems/figures/ddColumnFigure.h"
24 #include "hotdraw/main/hdDrawingView.h"
25 #include "hotdraw/main/hdDrawingEditor.h"
26 #include "dd/dditems/utilities/ddDataType.h"
27 #include "dd/dditems/handles/ddAddColButtonHandle.h"
28 #include "dd/dditems/locators/ddAddColLocator.h"
29 #include "dd/dditems/handles/ddAddFkButtonHandle.h"
30 #include "dd/dditems/locators/ddAddFkLocator.h"
31 #include "dd/dditems/handles/ddRemoveTableButtonHandle.h"
32 #include "dd/dditems/locators/ddRemoveTableLocator.h"
33 #include "dd/dditems/handles/ddMinMaxTableButtonHandle.h"
34 #include "dd/dditems/locators/ddMinMaxTableLocator.h"
35 #include "dd/dditems/handles/ddScrollBarHandle.h"
36 #include "dd/dditems/locators/ddScrollBarTableLocator.h"
37 #include "dd/dditems/handles/ddSouthTableSizeHandle.h"
38 #include "dd/dditems/locators/ddTableBottomLocator.h"
39 #include "dd/ddmodel/ddDBReverseEngineering.h"
40 #include "hotdraw/utilities/hdGeometry.h"
41 #include "dd/dditems/figures/ddRelationshipFigure.h"
42 #include "hotdraw/connectors/hdLocatorConnector.h"
43 #include "hotdraw/main/hdDrawing.h"
44 #include "dd/ddmodel/ddDatabaseDesign.h"
45 
46 //Images
47 #include "images/ddAddColumn.pngc"
48 #include "images/ddRemoveColumn.pngc"
49 #include "images/ddAddForeignKey.pngc"
50 #include "images/ddMaximizeTable.pngc"
51 #include "images/ddMinimizeTable.pngc"
52 #include "images/ddRemoveTable.pngc"
53 
54 /*
55 All figures title, colums, indexes are store at same array to improve performance in the following order:
56 	[0] = table border rect
57 	[1] = table title
58 	[2] = first column index
59 	[maxColIndex] = last column index
60 	[minIdxIndex] = first index index
61 	[maxIdxIndex] = last index index
62 */
63 
Init(wxString tableName,int x,int y)64 void ddTableFigure::Init(wxString tableName, int x, int y)
65 {
66 	setKindId(DDTABLEFIGURE);
67 	internalPadding = 2;
68 	externalPadding = 4;
69 	selectingFkDestination = false;
70 
71 	//Set Value default Attributes
72 	fontColorAttribute->fontColor = wxColour(49, 79, 79);
73 	//Set Value default selected Attributes
74 	lineSelAttribute->pen().SetColour(wxColour(204, 0, 0));
75 	lineSelAttribute->pen().SetStyle(wxSOLID);
76 	lineSelAttribute->pen().SetWidth(1);
77 	fillSelAttribute->brush().SetColour(wxColour(255, 250, 205));
78 	fillAttribute->brush().SetColour(wxColour(248, 248, 255));
79 	fontSelColorAttribute->fontColor = wxColour(49, 79, 79);
80 
81 	//Set table size, width and position
82 	rectangleFigure = new hdRectangleFigure();
83 	rectangleFigure->moveTo(0, x, y);
84 	add(rectangleFigure);
85 
86 	tableTitle = new ddTextTableItemFigure(tableName, dt_null, NULL);
87 	tableTitle->setOwnerTable(this);
88 	tableTitle->setEditable(true);
89 	tableTitle->moveTo(0, x, y);
90 	tableTitle->disablePopUp();
91 	tableTitle->setShowDataType(false);
92 	add(tableTitle);
93 	tableTitle->moveTo(0, rectangleFigure->getBasicDisplayBox().x[0] + internalPadding * 2, rectangleFigure->getBasicDisplayBox().y[0] + internalPadding / 2);
94 
95 	//Intialize handles
96 	wxBitmap image = wxBitmap(*ddAddColumn_png_img);
97 	wxSize valueSize = wxSize(8, 8);
98 	figureHandles->addItem(new ddAddColButtonHandle((hdIFigure *)this, (hdILocator *)new ddAddColLocator(), image, valueSize));
99 	image = wxBitmap(*ddAddForeignKey_png_img);
100 	figureHandles->addItem(new ddAddFkButtonHandle((hdIFigure *)this, (hdILocator *)new ddAddFkLocator(), image, valueSize));
101 	image = wxBitmap(*ddRemoveTable_png_img);
102 	figureHandles->addItem(new ddRemoveTableButtonHandle((hdIFigure *)this, (hdILocator *)new ddRemoveTableLocator(), image, valueSize));
103 	image = wxBitmap(*ddMinimizeTable_png_img);
104 	wxBitmap image2 = wxBitmap(*ddMaximizeTable_png_img);
105 	figureHandles->addItem(new ddMinMaxTableButtonHandle((hdIFigure *)this, (hdILocator *)new ddMinMaxTableLocator(), image, image2, valueSize));
106 	figureHandles->addItem(new ddSouthTableSizeHandle(this, (hdILocator *)new ddTableBottomLocator()));
107 
108 	//Intialize special handle
109 	valueSize = wxSize(10, colsRect.GetSize().GetHeight());
110 	scrollbar = new ddScrollBarHandle(this, (hdILocator *)new ddScrollBarTableLocator(), valueSize);
111 
112 	//Intialize columns window (min is always 1 in both, with or without cols & indxs)
113 	colsRowsSize = 0;
114 	colsWindow = 0;
115 	idxsRowsSize = 0;
116 	idxsWindow = 0;
117 
118 	//Initialize indexes (pointers to array segments)
119 	maxColIndex = 2;
120 	minIdxIndex = 2;
121 	maxIdxIndex = 2;
122 
123 	//Initialize position where start to draw columns & indexes, this is the value to allow scrollbars
124 	beginDrawCols = 2;
125 	beginDrawIdxs = 2;
126 
127 	//Initialize
128 	pkName = wxEmptyString;
129 	ukNames.clear();
130 
131 	updateTableSize();
132 
133 	basicDisplayBox.x[0] = x;
134 	basicDisplayBox.y[0] = y;
135 	belongsToSchema = false;
136 }
137 
ddTableFigure(wxString tableName,int x,int y)138 ddTableFigure::ddTableFigure(wxString tableName, int x, int y):
139 	hdCompositeFigure()
140 {
141 	Init(tableName, x, y);
142 }
143 
ddTableFigure(wxString tableName,int posIdx,int x,int y)144 ddTableFigure::ddTableFigure(wxString tableName, int posIdx, int x, int y):
145 	hdCompositeFigure()
146 {
147 	Init(tableName, 0, 0);
148 	//Check figure available positions for diagrams, add at least needed to allow initialization of the class
149 	int i, start;
150 	start = basicDisplayBox.CountPositions();
151 	for(i = start; i < (posIdx + 1); i++)
152 	{
153 		AddPosForNewDiagram();
154 	}
155 	syncInternalsPosAt(posIdx, x, y);
156 }
157 
158 //Used by persistence classes
InitTableValues(wxArrayString UniqueKeysName,wxString primaryKeyName,int bdc,int bdi,int maxcolsi,int minidxsi,int maxidxsi,int colsrs,int colsw,int idxsrs,int idxsw)159 void ddTableFigure::InitTableValues(wxArrayString UniqueKeysName, wxString primaryKeyName, int bdc, int bdi, int maxcolsi, int minidxsi, int maxidxsi, int colsrs, int colsw, int idxsrs, int idxsw)
160 {
161 	ukNames = UniqueKeysName;
162 	pkName = primaryKeyName;
163 	beginDrawCols = bdc;
164 	beginDrawIdxs = bdi;
165 	maxColIndex = maxcolsi;
166 	minIdxIndex = minidxsi;
167 	maxIdxIndex = maxidxsi;
168 	colsRowsSize = colsrs;
169 	colsWindow = colsw;
170 	idxsRowsSize = idxsrs;
171 	idxsWindow = idxsw;
172 	updateTableSize();
173 }
174 
~ddTableFigure()175 ddTableFigure::~ddTableFigure()
176 {
177 	if(scrollbar)
178 	{
179 		if(figureHandles->existsObject(scrollbar))
180 			figureHandles->removeItem(scrollbar);
181 		delete scrollbar;
182 	}
183 }
184 
AddPosForNewDiagram()185 void ddTableFigure::AddPosForNewDiagram()
186 {
187 	//Add new position to internal calculations figure
188 	fullSizeRect.addNewXYPosition();
189 	titleRect.addNewXYPosition();
190 	titleColsRect.addNewXYPosition();
191 	colsRect.addNewXYPosition();
192 	titleIndxsRect.addNewXYPosition();
193 	indxsRect.addNewXYPosition();
194 	unScrolledColsRect.addNewXYPosition();
195 	unScrolledFullSizeRect.addNewXYPosition();
196 	unScrolledTitleRect.addNewXYPosition();
197 	//Add to all figure figures
198 	hdCompositeFigure::AddPosForNewDiagram();
199 }
200 
RemovePosOfDiagram(int posIdx)201 void ddTableFigure::RemovePosOfDiagram(int posIdx)
202 {
203 	//Remove position for internal calculations figure
204 	fullSizeRect.removeXYPosition(posIdx);
205 	titleRect.removeXYPosition(posIdx);
206 	titleColsRect.removeXYPosition(posIdx);
207 	colsRect.removeXYPosition(posIdx);
208 	titleIndxsRect.removeXYPosition(posIdx);
209 	indxsRect.removeXYPosition(posIdx);
210 	unScrolledColsRect.removeXYPosition(posIdx);
211 	unScrolledFullSizeRect.removeXYPosition(posIdx);
212 	unScrolledTitleRect.removeXYPosition(posIdx);
213 	//remove position at all figure figures
214 	hdCompositeFigure::RemovePosOfDiagram(posIdx);
215 }
216 
getColByName(wxString name)217 ddColumnFigure *ddTableFigure::getColByName(wxString name)
218 {
219 	ddColumnFigure *out = NULL;
220 	ddColumnFigure *f;
221 
222 	hdIteratorBase *iterator = figuresEnumerator();
223 	iterator->Next(); //First figure is main rect
224 	iterator->Next(); //Second figure is main title
225 
226 	while(iterator->HasNext())
227 	{
228 		f = (ddColumnFigure *) iterator->Next();
229 		if(f->getColumnName(false).IsSameAs(name))
230 		{
231 			out = f;
232 			break;
233 		}
234 	}
235 	delete iterator;
236 
237 	return out;
238 }
239 
240 //WARNING: Columns SHOULD BE ADDED only using this function to avoid strange behaviors
addColumn(int posIdx,ddColumnFigure * column)241 void ddTableFigure::addColumn(int posIdx, ddColumnFigure *column)
242 {
243 	column->setOwnerTable(this);
244 	add(column);
245 	//Update Indexes
246 	if(maxColIndex == minIdxIndex) //maxColIndex == minIdxIndex means not indexes at this table, then update too
247 	{
248 		minIdxIndex++;
249 		maxIdxIndex++;
250 	}
251 	maxColIndex++;
252 	colsWindow++;  //by default add a column increase initial window
253 	colsRowsSize++;
254 
255 	updateTableSize(true);
256 
257 	//Fix column position at all available positions (Diagrams)
258 	int i;
259 	for(i = 0; i < basicDisplayBox.CountPositions(); i++)
260 	{
261 		syncInternalsPosAt(i, basicDisplayBox.x[i], basicDisplayBox.y[i]);
262 	}
263 }
264 
265 //WARNING: Function should be called on a table generated from a storage or to sync values after a big change at model (not derived from hotdraw events)
syncInternalsPosAt(int posIdx,int x,int y)266 void ddTableFigure::syncInternalsPosAt(int posIdx, int x, int y)
267 {
268 	basicDisplayBox.x[posIdx] = x;
269 	basicDisplayBox.y[posIdx] = y;
270 	rectangleFigure->moveTo(posIdx, x, y);
271 	tableTitle->moveTo(posIdx, rectangleFigure->getBasicDisplayBox().x[posIdx] + internalPadding * 2, rectangleFigure->getBasicDisplayBox().y[posIdx] + internalPadding / 2);
272 	calcInternalSubAreas(posIdx);
273 	recalculateColsPos(posIdx);
274 }
275 
276 //WARNING: Function should be called on a table generated from a storage or to sync values after a big change at model (not derived from hotdraw events)
syncInternalsPosAt(wxArrayInt & x,wxArrayInt & y)277 void ddTableFigure::syncInternalsPosAt(wxArrayInt &x, wxArrayInt &y)
278 {
279 	unsigned int posIdx, pointsCount = tableTitle->getBasicDisplayBox().CountPositions(), finalValue = x.Count();
280 	//I need to check that figures inside figure have all points too
281 	while(pointsCount < finalValue)
282 	{
283 		AddPosForNewDiagram();
284 		pointsCount = tableTitle->getBasicDisplayBox().CountPositions();
285 	}
286 
287 	//optimize this, because this is hack right now to avoid some weird problem when recreating figure status
288 	basicDisplayBox.x = x;
289 	basicDisplayBox.y = y;
290 
291 	for(posIdx = 0; posIdx < finalValue; posIdx++)
292 	{
293 		rectangleFigure->moveTo(posIdx, x[posIdx], y[posIdx]);
294 		tableTitle->moveTo(posIdx, rectangleFigure->getBasicDisplayBox().x[posIdx] + internalPadding * 2, rectangleFigure->getBasicDisplayBox().y[posIdx] + internalPadding / 2);
295 		calcInternalSubAreas(posIdx);
296 		recalculateColsPos(posIdx);
297 	}
298 }
299 
300 //WARNING: Columns SHOULD BE ADDED only using this columns if was created as an image from storage one
addColumnFromStorage(ddColumnFigure * column)301 void ddTableFigure::addColumnFromStorage(ddColumnFigure *column)
302 {
303 	add(column);
304 }
305 
removeColumn(int posIdx,ddColumnFigure * column)306 void ddTableFigure::removeColumn(int posIdx, ddColumnFigure *column)
307 {
308 	//Hack to allow to remove Fk before delete it.
309 	if(column->isPrimaryKey() || column->isUniqueKey())
310 	{
311 		column->setColumnKindToNone();
312 	}
313 
314 	column->setOwnerTable(NULL);
315 	remove(column);
316 
317 	if(column)
318 		delete column;
319 	//Update Indexes
320 	if(maxColIndex == minIdxIndex) //means not indexes at this table, then update too
321 	{
322 		minIdxIndex--;
323 		maxIdxIndex--;
324 	}
325 	maxColIndex--;
326 	if(colsRowsSize == colsWindow) //only decrease if size of window and columns is the same
327 		colsWindow--;
328 	colsRowsSize--;
329 	if(beginDrawCols > 2)
330 		beginDrawCols--;
331 	calcInternalSubAreas(posIdx);
332 	recalculateColsPos(posIdx);
333 	if(colsWindow == colsRowsSize) //if handle need to be removed, remove it
334 	{
335 		if(figureHandles->existsObject(scrollbar))
336 			figureHandles->removeItem(scrollbar);
337 	}
338 	//hack to update relationship position when table size change
339 	manuallyNotifyChange(posIdx);
340 	column = NULL;
341 }
342 
recalculateColsPos(int posIdx)343 void ddTableFigure::recalculateColsPos(int posIdx)
344 {
345 	wxFont font = fontAttribute->font();
346 	int defaultHeight = getColDefaultHeight(font);
347 
348 	hdIFigure *f = (hdIFigure *) figureFigures->getItemAt(0); //first figure is always Rect
349 	int horizontalPos = f->displayBox().x[posIdx] + 2;
350 	int verticalPos = 0;
351 
352 	for(int i = 2; i < maxColIndex ; i++)
353 	{
354 		f = (hdIFigure *) figureFigures->getItemAt(i); //table title
355 		if( (i >= beginDrawCols) && (i <= (colsWindow + beginDrawCols)) ) //Visible to draw
356 		{
357 			verticalPos = colsRect.y[posIdx] + (defaultHeight * (i - beginDrawCols) + ((i - beginDrawCols) * internalPadding));
358 			f->moveTo(posIdx, horizontalPos, verticalPos);
359 		}
360 		else
361 			f->moveTo(posIdx, -65000, -65000); //any figure outside canvas (x<0 || y<0) is not draw & not used to calculate displaybox
362 	}
363 }
364 
365 
366 
basicDraw(wxBufferedDC & context,hdDrawingView * view)367 void ddTableFigure::basicDraw(wxBufferedDC &context, hdDrawingView *view)
368 {
369 	int idx = view->getIdx();
370 	calcInternalSubAreas(idx);
371 
372 	if(calcScrolled) //Hack to avoid pass view as parameter to calcInternalSubAreas() because is sometimes called outside a paint event
373 	{
374 		view->CalcScrolledPosition(fullSizeRect.x[idx], fullSizeRect.y[idx], &fullSizeRect.x[idx], &fullSizeRect.y[idx]);
375 		view->CalcScrolledPosition(titleRect.x[idx], titleRect.y[idx], &titleRect.x[idx], &titleRect.y[idx]);
376 		view->CalcScrolledPosition(titleColsRect.x[idx], titleColsRect.y[idx], &titleColsRect.x[idx], &titleColsRect.y[idx]);
377 		view->CalcScrolledPosition(colsRect.x[idx], colsRect.y[idx], &colsRect.x[idx], &colsRect.y[idx]);
378 		view->CalcScrolledPosition(titleIndxsRect.x[idx], titleIndxsRect.y[idx], &titleIndxsRect.x[idx], &titleIndxsRect.y[idx]);
379 		view->CalcScrolledPosition(indxsRect.x[idx], indxsRect.y[idx], &indxsRect.x[idx], &indxsRect.y[idx]);
380 		calcScrolled = false;
381 	}
382 
383 	hdIFigure *f = (hdIFigure *) figureFigures->getItemAt(0); //table rectangle
384 	f->draw(context, view);
385 	f = (hdIFigure *) figureFigures->getItemAt(1); //table title
386 	f->draw(context, view);
387 
388 	for(int i = beginDrawCols; i < (colsWindow + beginDrawCols); i++)
389 	{
390 		f = (hdIFigure *) figureFigures->getItemAt(i); //table title
391 		if(f->displayBox().GetPosition(view->getIdx()).x > 0 && f->displayBox().GetPosition(view->getIdx()).y > 0)
392 		{
393 			f->draw(context, view);
394 		}
395 	}
396 
397 	reapplyAttributes(context, view); //reset attributes to default of figure because can be modified at Draw functions.
398 
399 	//Set Font for title "Columns"
400 	wxFont font = fontAttribute->font();
401 	int newSize = font.GetPointSize() * 0.7;
402 	font.SetPointSize(newSize);
403 	context.SetFont(font);
404 
405 	//Draw Columns Title Line 1
406 	context.DrawLine(titleColsRect.GetTopLeft(idx), titleColsRect.GetTopRight(idx));
407 	//Draw Columns Title
408 	context.DrawText(wxT("Columns"), titleColsRect.x[idx] + 3, titleColsRect.y[idx]);
409 	//Draw Columns Title Line 2
410 	context.DrawLine(titleColsRect.GetBottomLeft(idx), titleColsRect.GetBottomRight(idx));
411 	//DrawVertical Lines
412 	context.DrawLine(titleColsRect.GetBottomLeft(idx).x + 11, titleColsRect.GetBottomLeft(idx).y, titleColsRect.GetBottomLeft(idx).x + 11, titleIndxsRect.GetTopLeft(idx).y);
413 	context.DrawLine(titleColsRect.GetBottomLeft(idx).x + 22, titleColsRect.GetBottomLeft(idx).y, titleColsRect.GetBottomLeft(idx).x + 22, titleIndxsRect.GetTopLeft(idx).y);
414 	//Draw Indexes Title Line 1
415 	context.DrawLine(titleIndxsRect.GetTopLeft(idx), titleIndxsRect.GetTopRight(idx));
416 	//Draw Indexes Title
417 	//disable until implemented in a future: context.DrawText(wxT("Indexes"),titleIndxsRect.x+3,titleIndxsRect.y);
418 	//Draw Indexes Title Line 2
419 	context.DrawLine(titleIndxsRect.GetBottomLeft(idx), titleIndxsRect.GetBottomRight(idx));
420 
421 	context.SetFont(fontAttribute->font()); 		//after change font return always to initial one
422 
423 	//Draw scrollbar is needed
424 	if(scrollbar && figureHandles->existsObject(scrollbar))
425 		scrollbar->draw(context, view);
426 
427 	//Use this in a future
428 	//Hack to show message to select fk destination table
429 	if(selectingFkDestination)
430 	{
431 		context.SetTextForeground(*wxWHITE);
432 		wxBrush old = context.GetBrush();
433 		context.SetBrush(*wxBLACK_BRUSH);
434 
435 		int w, h, x, y;
436 		context.GetTextExtent(wxString(wxT("Select Destination table of foreign key")), &w, &h);
437 		x = fullSizeRect.GetTopLeft(idx).x + (((fullSizeRect.GetTopRight(idx).x - fullSizeRect.GetTopLeft(idx).x) - w) / 2);
438 		y = fullSizeRect.GetTopLeft(idx).y - h - 2;
439 		context.DrawRectangle(wxRect(x, y, w, h));
440 		context.DrawText(wxString(wxT("Select Destination table of foreign key")), x, y);
441 
442 		context.SetBrush(old);
443 		context.SetTextForeground(*wxBLACK);
444 		context.SetBackground(*wxWHITE);
445 
446 		//don't draw anything else then don't reapply default attributes
447 	}
448 }
449 
basicDrawSelected(wxBufferedDC & context,hdDrawingView * view)450 void ddTableFigure::basicDrawSelected(wxBufferedDC &context, hdDrawingView *view)
451 {
452 	int idx = view->getIdx();
453 	calcInternalSubAreas(idx);
454 
455 	if(calcScrolled) //Hack to avoid pass view as parameter to calcInternalSubAreas() because is sometimes called outside a paint event
456 	{
457 		view->CalcScrolledPosition(fullSizeRect.x[idx], fullSizeRect.y[idx], &fullSizeRect.x[idx], &fullSizeRect.y[idx]);
458 		view->CalcScrolledPosition(titleRect.x[idx], titleRect.y[idx], &titleRect.x[idx], &titleRect.y[idx]);
459 		view->CalcScrolledPosition(titleColsRect.x[idx], titleColsRect.y[idx], &titleColsRect.x[idx], &titleColsRect.y[idx]);
460 		view->CalcScrolledPosition(colsRect.x[idx], colsRect.y[idx], &colsRect.x[idx], &colsRect.y[idx]);
461 		view->CalcScrolledPosition(titleIndxsRect.x[idx], titleIndxsRect.y[idx], &titleIndxsRect.x[idx], &titleIndxsRect.y[idx]);
462 		view->CalcScrolledPosition(indxsRect.x[idx], indxsRect.y[idx], &indxsRect.x[idx], &indxsRect.y[idx]);
463 		calcScrolled = false;
464 	}
465 
466 	hdIFigure *f = (hdIFigure *) figureFigures->getItemAt(0); //table rectangle
467 	f->drawSelected(context, view);
468 	f = (hdIFigure *) figureFigures->getItemAt(1); //table title
469 	f->drawSelected(context, view);
470 
471 	for(int i = beginDrawCols; i < (colsWindow + beginDrawCols); i++)
472 	{
473 		f = (hdIFigure *) figureFigures->getItemAt(i); //table title
474 		if(f->displayBox().GetPosition(view->getIdx()).x > 0 && f->displayBox().GetPosition(view->getIdx()).y > 0)
475 		{
476 			f->drawSelected(context, view);
477 		}
478 	}
479 
480 	reapplySelAttributes(context, view); //reset attributes to default of figure because can be modified at Draw functions.
481 	wxFont font = fontAttribute->font();
482 	float t = font.GetPointSize();
483 	int newSize = font.GetPointSize() * 0.7;
484 	font.SetPointSize(newSize);
485 	context.SetFont(font);
486 
487 	//Draw Columns Title Line 1
488 	context.DrawLine(titleColsRect.GetTopLeft(idx), titleColsRect.GetTopRight(idx));
489 	//Draw Columns Title
490 	context.DrawText(wxT("Columns"), titleColsRect.x[idx] + 3, titleColsRect.y[idx]);
491 	//Draw Columns Title Line 2
492 	context.DrawLine(titleColsRect.GetBottomLeft(idx), titleColsRect.GetBottomRight(idx));
493 	//DrawVertical Lines
494 	context.DrawLine(titleColsRect.GetBottomLeft(idx).x + 11, titleColsRect.GetBottomLeft(idx).y, titleColsRect.GetBottomLeft(idx).x + 11, titleIndxsRect.GetTopLeft(idx).y);
495 	context.DrawLine(titleColsRect.GetBottomLeft(idx).x + 22, titleColsRect.GetBottomLeft(idx).y, titleColsRect.GetBottomLeft(idx).x + 22, titleIndxsRect.GetTopLeft(idx).y);
496 	//Draw Indexes Title Line 1
497 	context.DrawLine(titleIndxsRect.GetTopLeft(idx), titleIndxsRect.GetTopRight(idx));
498 	//Draw Indexes Title
499 	//disable until implemented in a future: context.DrawText(wxT("Indexes"),titleIndxsRect.x+3,titleIndxsRect.y);
500 	//Draw Indexes Title Line 2
501 	context.DrawLine(titleIndxsRect.GetBottomLeft(idx), titleIndxsRect.GetBottomRight(idx));
502 }
503 
getBasicDisplayBox()504 hdMultiPosRect &ddTableFigure::getBasicDisplayBox()
505 {
506 	return basicDisplayBox;
507 }
508 
setColsRowsWindow(int num)509 void ddTableFigure::setColsRowsWindow(int num)
510 {
511 	if(num > 0)
512 	{
513 		colsWindow = num;
514 		wxFont font = fontAttribute->font();
515 		colsRect.height = getColDefaultHeight(font) * colsWindow;
516 		colsRect.width = getFiguresMaxWidth();
517 	}
518 }
519 
getHeightFontMetric(wxString text,wxFont font)520 int ddTableFigure::getHeightFontMetric(wxString text, wxFont font)
521 {
522 	int width, height;
523 	wxBitmap emptyBitmap(*ddAddColumn_png_img);
524 	wxMemoryDC temp_dc;
525 	temp_dc.SelectObject(emptyBitmap);
526 	temp_dc.SetFont(font);
527 	temp_dc.GetTextExtent(text, &width, &height);
528 	return height;
529 }
530 
getColDefaultHeight(wxFont font)531 int ddTableFigure::getColDefaultHeight(wxFont font)
532 {
533 	if(figureFigures->count() <= 0)
534 	{
535 		int width, height;
536 		wxBitmap emptyBitmap(*ddAddColumn_png_img);
537 		wxMemoryDC temp_dc;
538 		temp_dc.SelectObject(emptyBitmap);
539 		temp_dc.SetFont(font);
540 		temp_dc.GetTextExtent(wxT("NewColumn"), &width, &height);
541 		return height;
542 	}
543 	else
544 	{
545 		hdIFigure *f = (hdIFigure *) figureFigures->getItemAt(1); //table title
546 		return f->displayBox().height;
547 	}
548 }
549 
550 //Show select fk destination Message Hack
setSelectFkDestMode(bool value)551 void ddTableFigure::setSelectFkDestMode(bool value)
552 {
553 	selectingFkDestination = value;
554 }
555 
getFiguresMaxWidth()556 int ddTableFigure::getFiguresMaxWidth()
557 {
558 	ddColumnFigure *cf;
559 	hdGeometry g;
560 
561 	hdIteratorBase *iterator = figuresEnumerator();
562 	iterator->Next(); //First figure is main rect
563 	int maxWidth = 0;
564 	cf = (ddColumnFigure *) iterator->Next(); //Second figure is main title
565 	maxWidth = g.max(maxWidth, cf->displayBox().width + 20);
566 	while(iterator->HasNext())
567 	{
568 		cf = (ddColumnFigure *) iterator->Next();
569 		maxWidth = g.max(maxWidth, cf->displayBox().width);
570 	}
571 	delete iterator;
572 	if(figureHandles->existsObject(scrollbar))
573 		return maxWidth + 11;  //as defined at locator
574 	else
575 		return maxWidth;
576 }
577 
calcInternalSubAreas(int posIdx)578 void ddTableFigure::calcInternalSubAreas(int posIdx)
579 {
580 	calcScrolled = true;
581 
582 	int maxWidth = getFiguresMaxWidth() + externalPadding;
583 	if(maxWidth < 100)
584 		maxWidth = 100;
585 	wxFont font = fontAttribute->font();
586 	int defaultHeight = getColDefaultHeight(font);
587 
588 	hdRect db = basicDisplayBox.gethdRect(posIdx);
589 
590 	//*** titleRect
591 	float t = font.GetPointSize();
592 	int newSize = font.GetPointSize() * 0.7;
593 	font.SetPointSize(newSize);
594 	int colsTitleHeight = getHeightFontMetric(wxT("Columns"), font);
595 
596 	titleRect.x[posIdx] = db.x;
597 	titleRect.y[posIdx] = db.y;
598 	titleRect.width = maxWidth;
599 	titleRect.height = defaultHeight;
600 
601 	titleColsRect.x[posIdx] = db.x;
602 	titleColsRect.y[posIdx] = titleRect.y[posIdx] + titleRect.height;
603 	titleColsRect.width = maxWidth;
604 	titleColsRect.height = colsTitleHeight;
605 	unScrolledTitleRect = titleColsRect;
606 
607 	//*** colsRect
608 	colsRect.width = maxWidth;
609 	if(colsWindow > 0)
610 		colsRect.height = defaultHeight * colsWindow + (colsWindow * internalPadding);
611 	else
612 		colsRect.height = defaultHeight;
613 	colsRect.x[posIdx] = db.x;
614 	colsRect.y[posIdx] = titleRect.y[posIdx] + titleRect.height + titleColsRect.height;
615 	unScrolledColsRect = colsRect;
616 
617 	//*** idxTitleRect
618 	titleIndxsRect.width = maxWidth;
619 	titleIndxsRect.height = colsTitleHeight;
620 	titleIndxsRect.x[posIdx] = db.x;
621 	titleIndxsRect.y[posIdx] = colsRect.y[posIdx] + colsRect.height;
622 
623 	//*** indexesRect
624 	indxsRect.width = maxWidth;
625 	indxsRect.height = defaultHeight * idxsWindow + (idxsWindow * internalPadding);
626 	indxsRect.x[posIdx] = db.x;
627 	indxsRect.y[posIdx] = titleIndxsRect.y[posIdx] + titleIndxsRect.height;
628 
629 	//*** FullTable Size
630 	fullSizeRect.width = maxWidth;
631 	fullSizeRect.height = titleRect.height + titleColsRect.height + colsRect.height + titleIndxsRect.height + indxsRect.height;
632 	fullSizeRect.x[posIdx] = db.x;
633 	fullSizeRect.y[posIdx] = titleRect.y[posIdx];
634 	unScrolledFullSizeRect = fullSizeRect;
635 
636 	//Update size
637 	wxSize sizeValue = fullSizeRect.GetSize();
638 	rectangleFigure->setSize(sizeValue);
639 }
640 
updateTableSize(bool notifyChange)641 void ddTableFigure::updateTableSize(bool notifyChange)
642 {
643 	//Step 0: Recalculate displaybox size, in case of an external modification as change of datatype in a fk from a source (data is stored in original table)
644 	ddColumnFigure *cf;
645 	hdIteratorBase *iterator = figuresEnumerator();
646 	iterator->Next(); //First figure is main rect
647 	cf = (ddColumnFigure *) iterator->Next(); //Second figure is main title
648 	while(iterator->HasNext())
649 	{
650 		cf = (ddColumnFigure *) iterator->Next();
651 		cf->displayBoxUpdate();
652 	}
653 	delete iterator;
654 
655 	//Step 1: Update table size
656 	calcInternalSubAreas(0);
657 	basicDisplayBox.SetSize(fullSizeRect.GetSize());
658 	if(notifyChange)
659 	{
660 		//hack to update relationship position when table size change, but need to be notified to all views only doing it right now for first view
661 		manuallyNotifyChange(0);
662 	}
663 }
664 
getColsSpace()665 hdMultiPosRect &ddTableFigure::getColsSpace()
666 {
667 	return unScrolledColsRect;
668 }
669 
getFullSpace()670 hdMultiPosRect &ddTableFigure::getFullSpace()
671 {
672 	return unScrolledFullSizeRect;
673 }
674 
getTitleRect()675 hdMultiPosRect &ddTableFigure::getTitleRect()
676 {
677 	return unScrolledTitleRect;
678 }
679 
680 
getTotalColumns()681 int ddTableFigure::getTotalColumns()
682 {
683 	return colsRowsSize;
684 }
685 
getColumnsWindow()686 int ddTableFigure::getColumnsWindow()
687 {
688 	return colsWindow;
689 }
690 
setColumnsWindow(int posIdx,int value,bool maximize)691 void ddTableFigure::setColumnsWindow(int posIdx, int value, bool maximize)
692 {
693 
694 	if(!maximize)
695 	{
696 
697 		//if value >0 && <= max size table && table+offset < maxColIndex with window
698 		if( (value > 0) && (value <= colsRowsSize) && (maxColIndex >= ( beginDrawCols + value ) ) )
699 		{
700 			colsWindow = value;
701 			calcInternalSubAreas(posIdx);
702 			recalculateColsPos(posIdx);
703 		}
704 
705 		//if special case of needing to modify beginDrawCols then do it
706 		if( (value > 0) && (value <= colsRowsSize) && (maxColIndex < ( beginDrawCols + value ) ) )
707 		{
708 			if( (beginDrawCols + colsWindow) == maxColIndex) //if index is at max
709 			{
710 				int diff = value - colsWindow; // value should be always higher tan colsWindows
711 				if(diff > 0 && (beginDrawCols - diff) >= 0 )
712 				{
713 					beginDrawCols -= diff;
714 					colsWindow = value;
715 					calcInternalSubAreas(posIdx);
716 					recalculateColsPos(posIdx);
717 
718 				}
719 			}
720 		}
721 	}
722 	else
723 	{
724 		beginDrawCols = 2;
725 		colsWindow = value;
726 		calcInternalSubAreas(posIdx);
727 		recalculateColsPos(posIdx);
728 	}
729 
730 
731 	//Hide Scrollbar if needed
732 	if(colsWindow == colsRowsSize)
733 	{
734 		if(figureHandles->existsObject(scrollbar))
735 			figureHandles->removeItem(scrollbar);
736 	}
737 	else
738 	{
739 		if (!figureHandles->existsObject(scrollbar))
740 			figureHandles->addItem(scrollbar);
741 	}
742 
743 }
744 
columnsWindowUp(int posIdx)745 void ddTableFigure::columnsWindowUp(int posIdx)  //move window from number to zero
746 {
747 	if( beginDrawCols > 2 )
748 	{
749 		beginDrawCols--;
750 		calcInternalSubAreas(posIdx);
751 		recalculateColsPos(posIdx);
752 	}
753 }
754 
columnsWindowDown(int posIdx)755 void ddTableFigure::columnsWindowDown(int posIdx)  //move window from number to maxcolumns
756 {
757 	if( (beginDrawCols + colsWindow) < maxColIndex)
758 	{
759 		beginDrawCols++;
760 		calcInternalSubAreas(posIdx);
761 		recalculateColsPos(posIdx);
762 	}
763 }
764 
getTopColWindowIndex()765 int ddTableFigure::getTopColWindowIndex()
766 {
767 	return (beginDrawCols - 2);
768 }
769 
setPkConstraintName(wxString name)770 void ddTableFigure::setPkConstraintName(wxString name)
771 {
772 	pkName = name;
773 }
774 
getPkConstraintName()775 wxString ddTableFigure::getPkConstraintName()
776 {
777 	return pkName;
778 }
779 
getAllColumnsNames()780 wxArrayString ddTableFigure::getAllColumnsNames()
781 {
782 	wxArrayString tmp;
783 	ddColumnFigure *f;
784 	tmp.Clear();
785 	hdIteratorBase *iterator = figuresEnumerator();
786 	iterator->Next(); //First figure is main rect
787 	iterator->Next(); //Second figure is main title
788 
789 	while(iterator->HasNext())
790 	{
791 		f = (ddColumnFigure *) iterator->Next();
792 		tmp.Add(f->getColumnName(false));
793 	}
794 	delete iterator;
795 	return tmp;
796 }
797 
getAllFkSourceColsNames(bool pk,int ukIndex)798 wxArrayString ddTableFigure::getAllFkSourceColsNames(bool pk, int ukIndex)
799 {
800 	wxArrayString tmp;
801 	ddColumnFigure *f;
802 	tmp.Clear();
803 	hdIteratorBase *iterator = figuresEnumerator();
804 	iterator->Next(); //First figure is main rect
805 	iterator->Next(); //Second figure is main title
806 
807 	while(iterator->HasNext())
808 	{
809 		f = (ddColumnFigure *) iterator->Next();
810 		if(pk)
811 		{
812 			if(f->isPrimaryKey())
813 				tmp.Add(f->getColumnName(false));
814 		}
815 		else
816 		{
817 			if(f->isUniqueKey(ukIndex))
818 				tmp.Add(f->getColumnName(false));
819 		}
820 	}
821 	delete iterator;
822 	return tmp;
823 }
824 
getColumnByName(wxString name)825 ddColumnFigure *ddTableFigure::getColumnByName(wxString name)
826 {
827 	ddColumnFigure *f;
828 	hdIteratorBase *iterator = figuresEnumerator();
829 	iterator->Next(); //First figure is main rect
830 	iterator->Next(); //Second figure is main title
831 
832 	while(iterator->HasNext())
833 	{
834 		f = (ddColumnFigure *) iterator->Next();
835 		if(f->getColumnName().IsSameAs(name))
836 		{
837 			return f;
838 		}
839 	}
840 	delete iterator;
841 	return NULL;
842 }
843 
getUkConstraintsNames()844 wxArrayString &ddTableFigure::getUkConstraintsNames()
845 {
846 	return ukNames;
847 }
848 
getTableName()849 wxString ddTableFigure::getTableName()
850 {
851 	ddTextTableItemFigure *c = (ddTextTableItemFigure *) figureFigures->getItemAt(1);
852 	c->setOneTimeNoAlias();
853 	return c->getText(false);
854 }
855 
856 //set Null on all relationship items with a fk column to be delete or a pk to be removed (pk attribute)
prepareForDeleteFkColumn(ddColumnFigure * column)857 void ddTableFigure::prepareForDeleteFkColumn(ddColumnFigure *column)
858 {
859 	hdIteratorBase *iterator = observersEnumerator();
860 	while(iterator->HasNext())
861 	{
862 		ddRelationshipFigure *r = (ddRelationshipFigure *) iterator->Next();
863 		if(r->getStartFigure() == this)	//Only update FK of connection with this table as source. source ---<| destination
864 			r->prepareFkForDelete(column);
865 	}
866 	delete iterator;
867 
868 }
869 
870 //	Note about observers:
871 //	A table is observed by several relationships at same time, where that observers
872 //	are just looking for changes that will affect relationship behavior.
873 //	Ex: if I delete a pk on observed table (source) all observers (destination)
874 //		should modify their columns to remove that fk created from that pk column.
875 // Warning: when a relationship is created an observer is added to both sides of relationship
876 // because this behavior (needed for update connection) to identify if is an observer
877 // of source table or destination table, should be check end figure, start!=end and end=this is end figure
878 // If start = and is recursive
updateFkObservers()879 void ddTableFigure::updateFkObservers()
880 {
881 	hdIteratorBase *iterator = observersEnumerator();
882 	while(iterator->HasNext())
883 	{
884 		ddRelationshipFigure *r = (ddRelationshipFigure *) iterator->Next();
885 		if(r->getStartFigure() == this)	//Only update FK of connection with this table as source. source ---<| destination
886 		{
887 			r->updateForeignKey();
888 		}
889 	}
890 	delete iterator;
891 }
892 
893 //If a column change datatype, should alert all others table to adjust their size with new values
updateSizeOfObservers()894 void ddTableFigure::updateSizeOfObservers()
895 {
896 	//For all tables that are observing this table, update their size
897 	hdIteratorBase *iterator = observersEnumerator();
898 	while(iterator->HasNext())
899 	{
900 		ddRelationshipFigure *r = (ddRelationshipFigure *) iterator->Next();
901 		ddTableFigure *destFkTable = (ddTableFigure *) r->getEndFigure();
902 		destFkTable->updateTableSize();
903 	}
904 	delete iterator;
905 }
906 
907 //drop foreign keys with this table as origin or destination because table is going to be deleted
processDeleteAlert(hdDrawing * drawing)908 void ddTableFigure::processDeleteAlert(hdDrawing *drawing)
909 {
910 	hdIteratorBase *iterator = observersEnumerator();
911 	bool repeatFlag;
912 	do
913 	{
914 		repeatFlag = false;
915 		iterator->ResetIterator();
916 		while(iterator->HasNext())
917 		{
918 			ddRelationshipFigure *rel = (ddRelationshipFigure *) iterator->Next();
919 			rel->disconnectStart();
920 			rel->disconnectEnd();
921 
922 			drawing->getOwnerEditor()->removeFromAllSelections(rel);
923 			drawing->getOwnerEditor()->deleteModelFigure(rel);
924 			repeatFlag = true;
925 			break;
926 		}
927 	}
928 	while(repeatFlag);
929 
930 	delete iterator;
931 }
932 
basicMoveBy(int posIdx,int x,int y)933 void ddTableFigure::basicMoveBy(int posIdx, int x, int y)
934 {
935 
936 	hdIFigure *f = (hdIFigure *) figureFigures->getItemAt(0);
937 //Hack to avoid bug in if clause
938 	int width =  spaceForMovement.GetWidth();
939 	int height =  spaceForMovement.GetHeight();
940 	int bottom = f->displayBox().y[posIdx] + f->displayBox().height + y;
941 	int right = f->displayBox().x[posIdx] + f->displayBox().width + x;
942 	int left = f->displayBox().x[posIdx] + x;
943 	int top = f->displayBox().y[posIdx] + y;
944 
945 //limit movemnt of table figures to canvas space
946 	if( (left > 0) && (top > 0) && (right < width) && (bottom < height) )
947 		hdCompositeFigure::basicMoveBy(posIdx, x, y);
948 }
949 
950 //Validate status of table for SQL DDL generation
validateTable(wxString & errors)951 bool ddTableFigure::validateTable(wxString &errors)
952 {
953 	bool out = true;
954 	wxString tmp = wxEmptyString;
955 	ddColumnFigure *f;
956 
957 	hdIteratorBase *iterator = figuresEnumerator();
958 	iterator->Next(); //First figure is main rect
959 	iterator->Next(); //Second figure is main title
960 
961 	while(iterator->HasNext())
962 	{
963 		f = (ddColumnFigure *) iterator->Next();
964 		if(!f->validateColumn(tmp))
965 		{
966 			out = false;
967 		}
968 	}
969 
970 	if(!out)
971 	{
972 		errors.Append(wxT("\n"));
973 		errors.Append(wxT("Errors detected at table") + this->getTableName() + wxT(" \n"));
974 		errors.Append(tmp);
975 		errors.Append(wxT("\n"));
976 	}
977 
978 	delete iterator;
979 
980 	return out;
981 }
982 
983 //Using some options from http://www.postgresql.org/docs/8.1/static/sql-createtable.html, but new options can be added in a future.
generateSQLCreate(wxString schemaName)984 wxString ddTableFigure::generateSQLCreate(wxString schemaName)
985 {
986 	//Columns and table
987 	wxString tmp(wxT("CREATE TABLE "));
988 	if(!schemaName.IsEmpty())
989 	{
990 		tmp += wxT("\"") + schemaName + wxT("\".\"") + getTableName() + wxT("\" (\n");
991 	}
992 	else
993 	{
994 		tmp += wxT("\"") + getTableName() + wxT("\" (\n");
995 	}
996 	hdIteratorBase *iterator = figuresEnumerator();
997 	iterator->Next(); //Fixed Position for table rectangle
998 	iterator->Next(); //Fixed Position for table name
999 	while(iterator->HasNext())
1000 	{
1001 		ddColumnFigure *column = (ddColumnFigure *) iterator->Next();
1002 		tmp += column->generateSQL();
1003 		if(column->isNotNull())
1004 		{
1005 			tmp += wxT(" NOT NULL");
1006 		}
1007 		if(iterator->HasNext())
1008 		{
1009 			tmp += wxT(" , \n");
1010 		}
1011 	}
1012 	tmp += wxT("\n ); ");
1013 
1014 	return tmp;
1015 }
1016 
generateSQLAlterPks(wxString schemaName)1017 wxString ddTableFigure::generateSQLAlterPks(wxString schemaName)
1018 {
1019 	wxString tmp;
1020 	hdIteratorBase *iterator = figuresEnumerator();
1021 	//Pk, Uk Constraints
1022 	iterator->Next(); //Fixed Position for table rectangle
1023 	iterator->Next(); //Fixed Position for table name
1024 	int contPk = 0;
1025 	while(iterator->HasNext())
1026 	{
1027 		ddColumnFigure *column = (ddColumnFigure *) iterator->Next();
1028 		if(column->isPrimaryKey())
1029 			contPk++;
1030 	}
1031 	if(contPk > 0)
1032 	{
1033 		tmp += wxT("\nALTER TABLE ");
1034 		if(!schemaName.IsEmpty())
1035 		{
1036 			tmp += wxT("\"") + schemaName + wxT("\".\"") + getTableName() + wxT("\"");
1037 		}
1038 		else
1039 		{
1040 			tmp += wxT("\"") + getTableName() + wxT("\"") ;
1041 		}
1042 
1043 		tmp += wxT(" ADD ");
1044 
1045 		if(!pkName.IsEmpty())
1046 		{
1047 			tmp += wxT("CONSTRAINT \"") + pkName + wxT("\" ");
1048 		}
1049 		tmp += wxT("PRIMARY KEY ( ");
1050 		iterator->ResetIterator();
1051 		iterator->Next(); //Fixed Position for table rectangle
1052 		iterator->Next(); //Fixed Position for table name
1053 
1054 		while(iterator->HasNext())
1055 		{
1056 			ddColumnFigure *column = (ddColumnFigure *) iterator->Next();
1057 			if(column->isPrimaryKey())
1058 			{
1059 				tmp += wxT("\"") + column->getColumnName() + wxT("\"");
1060 				contPk--;
1061 				if(contPk > 0)
1062 				{
1063 					tmp += wxT(" , ");
1064 				}
1065 				else
1066 				{
1067 					tmp += wxT(" ); ");
1068 				}
1069 			}
1070 		}
1071 	}
1072 	delete iterator;
1073 
1074 	return tmp;
1075 }
1076 
generateSQLAlterFks(wxString schemaName)1077 wxString ddTableFigure::generateSQLAlterFks(wxString schemaName)
1078 {
1079 	wxString tmp;
1080 	hdIteratorBase *iterator = figuresEnumerator();
1081 	//Fk Constraint
1082 	iterator = observersEnumerator();
1083 	if(!iterator->HasNext())
1084 	{
1085 		tmp = wxEmptyString;
1086 	}
1087 	else
1088 	{
1089 		while(iterator->HasNext())
1090 		{
1091 			ddRelationshipFigure *rel = (ddRelationshipFigure *) iterator->Next();
1092 			if(rel->getStartFigure() != this)
1093 			{
1094 				tmp += rel->generateSQL(schemaName);
1095 			}
1096 		}
1097 	}
1098 	delete iterator;
1099 	return tmp;
1100 }
1101 
generateSQLAlterUks(wxString schemaName)1102 wxString ddTableFigure::generateSQLAlterUks(wxString schemaName)
1103 {
1104 	hdIteratorBase *iterator = figuresEnumerator();
1105 
1106 	//Pk, Uk Constraints
1107 	iterator->Next(); //Fixed Position for table rectangle
1108 	iterator->Next(); //Fixed Position for table name
1109 	int MaxUk = -1;
1110 	while(iterator->HasNext())
1111 	{
1112 		ddColumnFigure *column = (ddColumnFigure *) iterator->Next();
1113 		if(column->isUniqueKey() && column->getUniqueConstraintIndex() > MaxUk)
1114 			MaxUk = column->getUniqueConstraintIndex();
1115 	}
1116 
1117 	wxString tmp = wxEmptyString;
1118 	if(MaxUk >= 0)
1119 	{
1120 		int i;
1121 		for(i = 0; i <= MaxUk; i++)
1122 		{
1123 			tmp += wxT("\nALTER TABLE ");
1124 
1125 			if(!schemaName.IsEmpty())
1126 				tmp += wxT("\"") + schemaName + wxT("\".");
1127 			tmp += wxT("\"") + getTableName() + wxT("\"") ;
1128 
1129 			tmp += wxT(" ADD ");
1130 
1131 			if(!getUkConstraintsNames()[i].IsEmpty())
1132 			{
1133 				tmp += wxT("CONSTRAINT \"") + getUkConstraintsNames()[i] + wxT("\" ") ;
1134 			}
1135 			tmp += wxT("UNIQUE ( ");
1136 
1137 			int countUk = 0;
1138 			iterator->ResetIterator();
1139 			iterator->Next(); //Fixed Position for table rectangle
1140 			iterator->Next(); //Fixed Position for table name
1141 			while(iterator->HasNext())
1142 			{
1143 				ddColumnFigure *column = (ddColumnFigure *) iterator->Next();
1144 				if(column->getUniqueConstraintIndex() == i)
1145 					countUk++;
1146 			}
1147 
1148 			iterator->ResetIterator();
1149 			iterator->Next(); //Fixed Position for table rectangle
1150 			iterator->Next(); //Fixed Position for table name
1151 			while(iterator->HasNext())
1152 			{
1153 				ddColumnFigure *column = (ddColumnFigure *) iterator->Next();
1154 				if(column->isUniqueKey() && column->getUniqueConstraintIndex() == i)
1155 				{
1156 					tmp += wxT("\"") + column->getColumnName() + wxT("\"");
1157 					countUk--;
1158 					if(countUk > 0)
1159 					{
1160 						tmp += wxT(", ");
1161 					}
1162 				}
1163 			}
1164 			tmp += wxT(" ); ");
1165 		}
1166 	}
1167 	delete iterator;
1168 	return tmp;
1169 }
1170 
generateAltersTable(pgConn * connection,wxString schemaName,ddDatabaseDesign * design)1171 wxString ddTableFigure::generateAltersTable(pgConn *connection, wxString schemaName, ddDatabaseDesign *design)
1172 {
1173 	wxString out;
1174 
1175 	OID oidTable = ddImportDBUtils::getTableOID(connection, schemaName, getTableName());
1176 	if(oidTable == -1)
1177 	{
1178 		wxMessageBox(wxString::Format(_("Cannot build an ALTER TABLE statement for a non existing table (%s.%s)."),
1179 		                              schemaName.c_str(), getTableName().c_str()), _("Error when trying to get table OID"),  wxICON_ERROR);
1180 		return wxEmptyString;
1181 	}
1182 
1183 	ddStubTable *dbTable = ddImportDBUtils::getTable(connection, getTableName(), oidTable);
1184 	if(!dbTable)
1185 	{
1186 		wxMessageBox(wxString::Format(_("Cannot reverse engineering table %s.%s."),
1187 		                              schemaName.c_str(), getTableName().c_str()), _("Error when trying to get stub table"),  wxICON_ERROR);
1188 		return wxEmptyString;
1189 	}
1190 
1191 	//--------------- DETECT TABLE LEVEL CHANGES
1192 
1193 	// Check if TABLE was renamed
1194 	// **Not supported yet**
1195 
1196 	// Check if PRIMARY KEY constraint was renamed
1197 	// Some models don't set a name for the primary key, so we ignore those models
1198 	if(this->getPkConstraintName().Len() > 0 && !dbTable->PrimaryKeyName.IsSameAs(this->getPkConstraintName()))
1199 	{
1200 		out += wxT("\n");
1201 		out += wxT("ALTER INDEX \"") + dbTable->PrimaryKeyName + wxT("\" RENAME TO \"") + this->getPkConstraintName() + wxT("\";");
1202 		out += wxT("\n");
1203 	}
1204 
1205 	// Check if a UNIQUE KEY constraint was renamed
1206 	// **Not supported yet** (is it possible?)
1207 
1208 	//--------------- DETECT COLUMN LEVEL CHANGES
1209 
1210 	// Check if A COLUMN was renamed
1211 	// **Not supported yet**
1212 
1213 	// Look for columns that exist in the database but not in the model
1214 	bool pkRemovedflag = false;
1215 	bool ukRemoved = false;
1216 	stubColsHashMap::iterator it;
1217 	ddStubColumn *item;
1218 
1219 	for (it = dbTable->cols.begin(); it != dbTable->cols.end(); ++it)
1220 	{
1221 		wxString key = it->first;
1222 		item = it->second;
1223 		ddColumnFigure *column = getColumnByName(key);
1224 
1225 		// Check for changes at the column
1226 		if(column)
1227 		{
1228 			// Generate ALTER COLUMN statement if needed
1229 			// Datatype change, length, and precision are checked
1230 
1231 			// Temporary conversion fix to datatype of designer should be improved in a future
1232 			wxString dataType = item->typeColumn->Name();
1233 			bool sameDatatype = true, sameScale = true, samePrecision = true;
1234 			int s = -1, p = -1;
1235 			bool useScale = true, needps = false;
1236 
1237 			s = item->typeColumn->Length();
1238 			p = item->typeColumn->Precision();
1239 
1240 			if(dataType.IsSameAs(wxT("character varying"), false))
1241 			{
1242 				needps = true;
1243 				dataType = wxT("varchar(n)");
1244 			}
1245 
1246 			else if(dataType.IsSameAs(wxT("numeric"), false))
1247 			{
1248 				needps = true;
1249 				useScale = false;
1250 				dataType = wxT("numeric(p,s)");
1251 			}
1252 			else if(dataType.IsSameAs(wxT("interval"), false))
1253 			{
1254 				needps = true;
1255 				dataType = wxT("interval(n)");
1256 			}
1257 			else if(dataType.IsSameAs(wxT("bit"), false))
1258 			{
1259 				needps = true;
1260 				dataType = wxT("bit(n)");
1261 			}
1262 			else if(dataType.IsSameAs(wxT("char"), false))
1263 			{
1264 				needps = true;
1265 				dataType = wxT("char(n)");
1266 			}
1267 			else if(dataType.IsSameAs(wxT("varbit"), false))
1268 			{
1269 				needps = true;
1270 				dataType = wxT("varbit(n)");
1271 			}
1272 			else if(dataType.IsSameAs(wxT("character"), false))
1273 			{
1274 				needps = true;
1275 				dataType = wxT("char(n)");
1276 			}
1277 
1278 			if(needps)
1279 			{
1280 				if(useScale)
1281 				{
1282 					samePrecision = column->getPrecision() == s;
1283 				}
1284 				else
1285 				{
1286 					samePrecision = column->getPrecision() == s;
1287 					sameScale = column->getScale() == p;
1288 				}
1289 			}
1290 
1291 
1292 			sameDatatype = column->getRawDataType().IsSameAs(dataType, false);
1293 
1294 			if(!samePrecision || !sameScale || !sameDatatype)
1295 			{
1296 				out += wxT("\n");
1297 				out += wxT("ALTER TABLE ");
1298 
1299 				if(!schemaName.IsEmpty())
1300 					out += wxT("\"") + schemaName + wxT("\".");
1301 				out += wxT("\"") + getTableName() + wxT("\"") ;
1302 
1303 				out	+= wxT(" ALTER COLUMN ") + column->generateSQL(true) + wxT(";");
1304 				out += wxT("\n");
1305 			}
1306 		}
1307 		else  // DROP COLUMN because it doesn't exist in the model anymore
1308 		{
1309 			out += wxT("\n");
1310 			out += wxT("ALTER TABLE ");
1311 
1312 			if(!schemaName.IsEmpty())
1313 				out += wxT("\"") + schemaName + wxT("\".");
1314 			out += wxT("\"") + getTableName() + wxT("\"") ;
1315 
1316 			out += wxT(" DROP COLUMN \"") + key + wxT("\";");
1317 			out += wxT("\n");
1318 			if(item->isPrimaryKey)
1319 			{
1320 				pkRemovedflag = true;
1321 			}
1322 		}
1323 	}
1324 
1325 	// Look for columns that exist in the model but not in the database
1326 	hdIteratorBase *iteratorPK = figuresEnumerator();
1327 	iteratorPK->Next(); //Fixed Position for table rectangle
1328 	iteratorPK->Next(); //Fixed Position for table name
1329 
1330 	bool pkAddedflag = false;
1331 	while(iteratorPK->HasNext())
1332 	{
1333 		ddColumnFigure *column = (ddColumnFigure *) iteratorPK->Next();
1334 		if(dbTable->cols.find(column->getColumnName()) == dbTable->cols.end())  //Exist in model but not in db
1335 		{
1336 			out += wxT("\n");
1337 			out += wxT("ALTER TABLE ");
1338 			if(!schemaName.IsEmpty())
1339 				out += wxT("\"") + schemaName + wxT("\".");
1340 			out += wxT("\"") + getTableName() + wxT("\"") ;
1341 			out += wxT(" ADD COLUMN ") + column->generateSQL() + wxT(";");
1342 			out += wxT("\n");
1343 			if(column->isPrimaryKey())
1344 			{
1345 				pkAddedflag = true;
1346 			}
1347 		}
1348 	}
1349 
1350 	// Look for all PRIMARY columns in the database's tables
1351 	bool pkChanged = false;
1352 	iteratorPK->ResetIterator();
1353 	iteratorPK->Next(); //Fixed Position for table rectangle
1354 	iteratorPK->Next(); //Fixed Position for table name
1355 	while(iteratorPK->HasNext())
1356 	{
1357 		ddColumnFigure *column = (ddColumnFigure *) iteratorPK->Next();
1358 		if(column->isPrimaryKey())
1359 		{
1360 			if(dbTable->cols.find(column->getColumnName()) == dbTable->cols.end())
1361 			{
1362 				pkChanged = true;
1363 			}
1364 			else
1365 			{
1366 				if(!dbTable->cols[column->getColumnName()]->isPrimaryKey)
1367 				{
1368 					pkChanged = true;
1369 				}
1370 			}
1371 		}
1372 	}
1373 	delete iteratorPK;
1374 
1375 	// Look for all PRIMARY columns in the model
1376 	stubColsHashMap::iterator itCol;
1377 	ddStubColumn *itemCol;
1378 	for (itCol = dbTable->cols.begin(); itCol != dbTable->cols.end(); ++itCol)
1379 	{
1380 		wxString colStubName = itCol->first;
1381 		itemCol = itCol->second;
1382 		if(itemCol->isPrimaryKey)
1383 		{
1384 			ddColumnFigure *col = getColumnByName(colStubName);
1385 			if(col != NULL)
1386 			{
1387 				if(!col->isPrimaryKey())
1388 				{
1389 					pkChanged = true;
1390 				}
1391 			}
1392 			else
1393 			{
1394 				pkChanged = true;
1395 			}
1396 		}
1397 	}
1398 
1399 
1400 	// Handle the addition of a new column to the primary key
1401 	if(pkAddedflag || pkRemovedflag || pkChanged)
1402 	{
1403 		// Drop existing primary key
1404 		out += wxT("\n");
1405 		out += wxT("ALTER TABLE ");
1406 		if(!schemaName.IsEmpty())
1407 			out += wxT("\"") + schemaName + wxT("\".");
1408 		out += wxT("\"") + getTableName() + wxT("\"") ;
1409 		out += wxT(" DROP CONSTRAINT \"") + this->getPkConstraintName() + wxT("\";");
1410 		// Create the new one
1411 		out += generateSQLAlterPks(schemaName);
1412 	}
1413 
1414 	// Handle changes from NOT NULL to NULL (always after dropping PK)
1415 	for (it = dbTable->cols.begin(); it != dbTable->cols.end(); ++it)
1416 	{
1417 		wxString key = it->first;
1418 		item = it->second;
1419 		ddColumnFigure *column = getColumnByName(key);
1420 
1421 		// Check for changes at the column level
1422 		if(column)
1423 		{
1424 			// Generate ALTER COLUMN statement if needed
1425 			// Datatype change, length and precision are handled.
1426 
1427 			// Temporary conversion fix to datatype of designer should be improved in a future
1428 			wxString dataType = item->typeColumn->Name();
1429 			bool sameDatatype = true, sameScale = true, samePrecision = true;
1430 			int s = -1, p = -1;
1431 
1432 			// Model has now NULL constraint, so drop the NOT NULL constraint
1433 			if(column->isNotNull())
1434 			{
1435 				if(!item->isNotNull)
1436 				{
1437 					out += wxT("\n");
1438 					out += wxT("ALTER TABLE ");
1439 					if(!schemaName.IsEmpty())
1440 						out += wxT("\"") + schemaName + wxT("\".");
1441 					out += wxT("\"") + getTableName() + wxT("\"") ;
1442 					out += wxT(" ALTER COLUMN \"") + column->getColumnName() + _("\" SET NOT NULL;");
1443 					out += wxT("\n");
1444 				}
1445 			}
1446 			else if(!column->isNotNull())
1447 			{
1448 				if(item->isNotNull)
1449 				{
1450 					out += wxT("\n");
1451 					out += wxT("ALTER TABLE \"");
1452 					if(!schemaName.IsEmpty())
1453 						out += wxT("\"") + schemaName + wxT("\".");
1454 					out += wxT("\"") + getTableName() + wxT("\"") ;
1455 					out += wxT("\" ALTER COLUMN \"") + column->getColumnName() + wxT("\" DROP NOT NULL;");
1456 					out += wxT("\n");
1457 				}
1458 			}
1459 		}
1460 	}
1461 
1462 	// Check UK conditions
1463 	int i, maxUkn = this->getUkConstraintsNames().Count();
1464 	for(i = 0; i < maxUkn; i++)
1465 	{
1466 		if(this->getUkConstraintsNames()[i].Len() == 0)
1467 		{
1468 			wxMessageBox(wxString::Format(_("Some UNIQUE keys on table %s have no name.\nYou should set a name for them, so that pgAdmin can check consistency with the already available database constraints."),
1469 			                              getTableName().c_str()), _("Trying to build ALTER sentences"),  wxICON_ERROR);
1470 			return wxEmptyString;
1471 		}
1472 	}
1473 
1474 	// Search for UNIQUE key deleted from model, but available in the database
1475 	// Two steps process: first drop then delete from wxarraystring
1476 	int maxUkStub = dbTable->UniqueKeysNames.Count();
1477 	for(i = 0; i < maxUkStub; i++)
1478 	{
1479 		// Drop UK [in db but not in model]
1480 		if(this->getUkConstraintsNames().Index(dbTable->UniqueKeysNames[i]) == wxNOT_FOUND)
1481 		{
1482 			// Drop it
1483 			out += wxT("\n");
1484 			out += wxT("DROP INDEX \"") + dbTable->UniqueKeysNames[i] + wxT("\";");
1485 			out += wxT("\n");
1486 
1487 			// This index metadata is not useful anymore, so erase it at temporary stub table.
1488 			stubColsHashMap::iterator itDelUkIdx;
1489 			ddStubColumn *itemDelUkIdx;
1490 			for (itDelUkIdx = dbTable->cols.begin(); itDelUkIdx != dbTable->cols.end(); ++itDelUkIdx)
1491 			{
1492 				wxString keyDelUkIdx = itDelUkIdx->first;
1493 				itemDelUkIdx = itDelUkIdx->second;
1494 				if(itemDelUkIdx->uniqueKeyIndex == i)
1495 				{
1496 					itemDelUkIdx->uniqueKeyIndex = -1;
1497 				}
1498 			}
1499 		}
1500 	}
1501 
1502 	maxUkStub = dbTable->UniqueKeysNames.Count();
1503 	for(i = 0; i < maxUkStub; i++)
1504 	{
1505 		// Drop UK [in db but not in model]
1506 		if(this->getUkConstraintsNames().Index(dbTable->UniqueKeysNames[i]) == wxNOT_FOUND)
1507 		{
1508 			dbTable->UniqueKeysNames.RemoveAt(i);
1509 			maxUkStub = dbTable->UniqueKeysNames.Count();
1510 		}
1511 	}
1512 
1513 	// Search for new UK in model to add ADD CONSTRAINT clause
1514 	int maxUkModel = this->getUkConstraintsNames().Count();
1515 	for(i = 0; i < maxUkModel; i++)
1516 	{
1517 		// Add UK [in model but not in db]
1518 		if(dbTable->UniqueKeysNames.Index(this->getUkConstraintsNames()[i]) == wxNOT_FOUND)
1519 		{
1520 			// Create it
1521 			out += wxT("\nALTER TABLE ");
1522 			if(!schemaName.IsEmpty())
1523 				out += wxT("\"") + schemaName + wxT("\".");
1524 			out += wxT("\"") + getTableName() + wxT("\"") ;
1525 			out += wxT(" ADD ");
1526 			if(!getUkConstraintsNames()[i].IsEmpty())
1527 			{
1528 				out += wxT(" CONSTRAINT \"") + getUkConstraintsNames()[i] + wxT("\" ");
1529 			}
1530 			out += wxT(" UNIQUE ( ");
1531 
1532 			int countUk = 0;
1533 			hdIteratorBase *iteratorUk = figuresEnumerator();
1534 			iteratorUk->Next(); //Fixed Position for table rectangle
1535 			iteratorUk->Next(); //Fixed Position for table name
1536 			while(iteratorUk->HasNext())
1537 			{
1538 				ddColumnFigure *column = (ddColumnFigure *) iteratorUk->Next();
1539 				if(column->getUniqueConstraintIndex() == i)
1540 					countUk++;
1541 			}
1542 
1543 			iteratorUk->ResetIterator();
1544 			iteratorUk->Next(); //Fixed Position for table rectangle
1545 			iteratorUk->Next(); //Fixed Position for table name
1546 			while(iteratorUk->HasNext())
1547 			{
1548 				ddColumnFigure *column = (ddColumnFigure *) iteratorUk->Next();
1549 				if(column->isUniqueKey() && column->getUniqueConstraintIndex() == i)
1550 				{
1551 					out += wxT("\"") + column->getColumnName() + wxT("\"");
1552 					countUk--;
1553 					if(countUk > 0)
1554 					{
1555 						out += wxT(", ");
1556 					}
1557 					else
1558 					{
1559 						out += wxT(");");
1560 					}
1561 				}
1562 			}
1563 			delete iteratorUk;
1564 		}
1565 	}
1566 
1567 	// After delete/create UK, look for changes at existing UK
1568 	// Unified UK indexes at both dbtable and ddTableFigure [same index at dbtable that in tablefigure]
1569 	// BUT in table figure exists indexes without an equivalent in dbtable
1570 	maxUkStub = this->getUkConstraintsNames().Count();
1571 	for(i = 0; i < maxUkStub; i++)
1572 	{
1573 		int oldIndex = dbTable->UniqueKeysNames.Index(this->getUkConstraintsNames()[i]);
1574 		int newIndex = i;
1575 
1576 		if(oldIndex != wxNOT_FOUND)
1577 		{
1578 			stubColsHashMap::iterator itDelUkIdx;
1579 			ddStubColumn *itemDelUkIdx;
1580 			for (itDelUkIdx = dbTable->cols.begin(); itDelUkIdx != dbTable->cols.end(); ++itDelUkIdx)
1581 			{
1582 				wxString keyDelUkIdx = itDelUkIdx->first;
1583 				itemDelUkIdx = itDelUkIdx->second;
1584 				if(item->uniqueKeyIndex == oldIndex)
1585 				{
1586 					item->uniqueKeyIndex = newIndex;
1587 				}
1588 			}
1589 		}
1590 	}
1591 
1592 	// Generate again those UK that fill one of these conditions:
1593 	// 1. UK at tablefigure have more columns that at stub
1594 	// 2. Uk at stub have more column that at tablefigure
1595 	// Right now, UK constraints have unified indexes (same index at tablefigure and stub)
1596 	maxUkStub = this->getUkConstraintsNames().Count();
1597 
1598 	// for each UK
1599 	for(i = 0; i < maxUkStub; i++)
1600 	{
1601 		// Only for UKs with number unified that exists at both sides [db and tablefigure] [with boundaries check]
1602 		int boundarie1 = getUkConstraintsNames().Count();
1603 		int boundarie2 = dbTable->UniqueKeysNames.Count();
1604 		if( (i < boundarie1) && (i < boundarie2) && getUkConstraintsNames().Index( dbTable->UniqueKeysNames[i]) != wxNOT_FOUND )
1605 		{
1606 			// Assumption
1607 			bool createUkAgain = false;
1608 
1609 			// CHECK FIRST CONDITION --->   1. UK at tablefigure have more columns that at stub?
1610 			hdIteratorBase *iteratorUk = figuresEnumerator();
1611 			iteratorUk->Next(); //Fixed Position for table rectangle
1612 			iteratorUk->Next(); //Fixed Position for table name
1613 
1614 			while(iteratorUk->HasNext())
1615 			{
1616 				ddColumnFigure *column = (ddColumnFigure *) iteratorUk->Next();
1617 				// For each UK column of the constraint with same index (position i, constraint at getUkConstraintsNames()[] )
1618 				if(column->getUniqueConstraintIndex() == i)
1619 				{
1620 					if(dbTable->cols.find(column->getColumnName()) == dbTable->cols.end())  //found at dbtable that uk column
1621 					{
1622 						createUkAgain = true;
1623 					}
1624 					else
1625 					{
1626 						ddStubColumn *stubCol = dbTable->cols[column->getColumnName()];
1627 						if(!stubCol->isUniqueKey())
1628 						{
1629 							createUkAgain = true;
1630 						}
1631 					}
1632 				}
1633 			}
1634 			delete iteratorUk;
1635 
1636 			// CHECK SECOND CONDITION --->   UK at stub have more uk column of same index that at tablefigure?
1637 			stubColsHashMap::iterator itCol;
1638 			ddStubColumn *itemCol;
1639 			for (itCol = dbTable->cols.begin(); itCol != dbTable->cols.end(); ++itCol)
1640 			{
1641 				bool isAtFigure = false;
1642 				wxString colStubName = itCol->first;
1643 				itemCol = itCol->second;
1644 				if(itemCol->uniqueKeyIndex == i)
1645 				{
1646 					ddColumnFigure *colUk = getColumnByName(colStubName);
1647 					if(colUk == NULL)
1648 					{
1649 						createUkAgain = true;
1650 					}
1651 					else
1652 					{
1653 						if(!colUk->isUniqueKey())
1654 						{
1655 							createUkAgain = true;
1656 						}
1657 					}
1658 				}
1659 			}
1660 
1661 			if(createUkAgain)
1662 			{
1663 				// Drop it
1664 				out += wxT("\n");
1665 				out += wxT("DROP INDEX \"") + dbTable->UniqueKeysNames[i] + wxT("\";");
1666 				out += wxT("\n");
1667 
1668 				// Create it
1669 				out += wxT("\nALTER TABLE ");
1670 				if(!schemaName.IsEmpty())
1671 					out += wxT("\"") + schemaName + wxT("\".");
1672 				out += wxT("\"") + getTableName() + wxT("\"") ;
1673 				out += wxT(" ADD ");
1674 				if(!getUkConstraintsNames()[i].IsEmpty())
1675 				{
1676 					out += wxT(" CONSTRAINT \"") + getUkConstraintsNames()[i] + wxT("\" ");
1677 				}
1678 				out += wxT(" UNIQUE ( ");
1679 
1680 				int countUk = 0;
1681 				hdIteratorBase *iteratorUk = figuresEnumerator();
1682 				iteratorUk->Next(); //Fixed Position for table rectangle
1683 				iteratorUk->Next(); //Fixed Position for table name
1684 				while(iteratorUk->HasNext())
1685 				{
1686 					ddColumnFigure *column = (ddColumnFigure *) iteratorUk->Next();
1687 					if(column->getUniqueConstraintIndex() == i)
1688 						countUk++;
1689 				}
1690 
1691 				iteratorUk->ResetIterator();
1692 				iteratorUk->Next(); //Fixed Position for table rectangle
1693 				iteratorUk->Next(); //Fixed Position for table name
1694 				while(iteratorUk->HasNext())
1695 				{
1696 					ddColumnFigure *column = (ddColumnFigure *) iteratorUk->Next();
1697 					if(column->isUniqueKey() && column->getUniqueConstraintIndex() == i)
1698 					{
1699 						out += wxT("\"") + column->getColumnName() + wxT("\"");
1700 						countUk--;
1701 						if(countUk > 0)
1702 						{
1703 							out += wxT(", ");
1704 						}
1705 						else
1706 						{
1707 							out += wxT(" );");
1708 						}
1709 					}
1710 				}
1711 				delete iteratorUk;
1712 			}
1713 		}
1714 	}
1715 
1716 	// Validate all FKs have a name defined at model
1717 	hdIteratorBase *iteratorRelations = observersEnumerator();
1718 	while(iteratorRelations->HasNext())
1719 	{
1720 		ddRelationshipFigure *r = (ddRelationshipFigure *) iteratorRelations->Next();
1721 		if(r->getConstraintName().Len() == 0)	// Add to list, FKs with this table as destination. source ---<| destination
1722 		{
1723 			wxMessageBox(wxString::Format(_("Some foreign keys keys on table %s have no name.\nYou should set a name for them, so that pgAdmin can check consistency with the already available database constraints."),
1724 			                              getTableName().c_str()), _("Trying to build ALTER sentences"),  wxICON_ERROR);
1725 			return wxEmptyString;
1726 		}
1727 	}
1728 
1729 	//Check Foreign Keys
1730 	// FK at model are same (including attributes and columns) at db
1731 	iteratorRelations->ResetIterator();
1732 	while(iteratorRelations->HasNext())
1733 	{
1734 		ddRelationshipFigure *r = (ddRelationshipFigure *) iteratorRelations->Next();
1735 		if(r->getEndFigure() == this)	// Only check FK this table as destination. source ---<| destination
1736 		{
1737 			// Check relationship from model exists a db?
1738 			if(ddImportDBUtils::existsFk(connection, dbTable->OIDTable, schemaName, r->getConstraintName(), r->getStartTable()->getTableName()))
1739 			{
1740 				// Columns and other properties are exactly the same? if not, then drop and create again
1741 				if(!ddImportDBUtils::isModelSameDbFk(connection, dbTable->OIDTable, schemaName, r->getConstraintName(), r->getStartTable()->getTableName(), r->getEndTable()->getTableName(), dbTable, r))
1742 				{
1743 					// Drop it first
1744 					out += wxT("\n");
1745 					out += wxT("ALTER TABLE ");
1746 					if(!schemaName.IsEmpty())
1747 						out += wxT("\"") + schemaName + wxT("\".");
1748 					out += wxT("\"") + this->getTableName() + wxT("\"") ;
1749 					out += wxT(" DROP CONSTRAINT \"") + r->getConstraintName() + wxT("\";");
1750 					out += wxT("\n");
1751 
1752 					// Then Add it again with changes
1753 					out += r->generateSQL(schemaName);
1754 
1755 				}
1756 			}
1757 			else //relationship only exists at model and not in db
1758 			{
1759 				//Create because it doesn't exists
1760 				out += r->generateSQL(schemaName);
1761 			}
1762 		}
1763 	}
1764 
1765 	// Check foreign keys available in the database but not in the model
1766 	// because we need to drop them
1767 	// First, create a list of FKs at destination table in the model
1768 	wxArrayString validFks;
1769 	iteratorRelations->ResetIterator();
1770 	while(iteratorRelations->HasNext())
1771 	{
1772 		ddRelationshipFigure *r = (ddRelationshipFigure *) iteratorRelations->Next();
1773 		if(r->getEndFigure() == this)	// Add to list, FKs with this table as destination. source ---<| destination
1774 		{
1775 			validFks.Add(r->getConstraintName());
1776 		}
1777 	}
1778 	delete iteratorRelations;
1779 
1780 	wxArrayString fksToDelete = ddImportDBUtils::getFkAtDbNotInModel(connection, dbTable->OIDTable, schemaName, validFks , design);
1781 
1782 	int max = fksToDelete.Count();
1783 	for(i = 0; i < max; i++)
1784 	{
1785 		// Drop it
1786 		out += wxT("\n");
1787 		out += wxT("ALTER TABLE ");
1788 		if(!schemaName.IsEmpty())
1789 			out += wxT("\"") + schemaName + wxT("\".");
1790 		out += wxT("\"") + this->getTableName() + wxT("\"") ;
1791 		out += wxT(" DROP CONSTRAINT \"") + fksToDelete[i] + wxT("\";");
1792 		out += wxT("\n");
1793 	}
1794 
1795 	if(dbTable)
1796 		delete dbTable;
1797 	return out;
1798 }
1799