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