pgadmin3/dd/dditems/figures/ddTableFigure.cpp
levinsv 4af765213c support PG11
Поддержка PostgreSQL 11 только для Windows
2018-10-10 22:59:25 +05:00

1798 lines
53 KiB
C++

//////////////////////////////////////////////////////////////////////////
//
// pgAdmin III - PostgreSQL Tools
//
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
// ddTableFigure.cpp - Draw table figure of a model
//
////////////////////////////////////////////////////////////////////////////
#include "pgAdmin3.h"
// wxWindows headers
#include <wx/wx.h>
#include <wx/dcbuffer.h>
#include <wx/pen.h>
// App headers
#include "dd/dditems/figures/ddTableFigure.h"
#include "dd/dditems/figures/ddTextTableItemFigure.h"
#include "dd/dditems/figures/ddColumnFigure.h"
#include "hotdraw/main/hdDrawingView.h"
#include "hotdraw/main/hdDrawingEditor.h"
#include "dd/dditems/utilities/ddDataType.h"
#include "dd/dditems/handles/ddAddColButtonHandle.h"
#include "dd/dditems/locators/ddAddColLocator.h"
#include "dd/dditems/handles/ddAddFkButtonHandle.h"
#include "dd/dditems/locators/ddAddFkLocator.h"
#include "dd/dditems/handles/ddRemoveTableButtonHandle.h"
#include "dd/dditems/locators/ddRemoveTableLocator.h"
#include "dd/dditems/handles/ddMinMaxTableButtonHandle.h"
#include "dd/dditems/locators/ddMinMaxTableLocator.h"
#include "dd/dditems/handles/ddScrollBarHandle.h"
#include "dd/dditems/locators/ddScrollBarTableLocator.h"
#include "dd/dditems/handles/ddSouthTableSizeHandle.h"
#include "dd/dditems/locators/ddTableBottomLocator.h"
#include "dd/ddmodel/ddDBReverseEngineering.h"
#include "hotdraw/utilities/hdGeometry.h"
#include "dd/dditems/figures/ddRelationshipFigure.h"
#include "hotdraw/connectors/hdLocatorConnector.h"
#include "hotdraw/main/hdDrawing.h"
#include "dd/ddmodel/ddDatabaseDesign.h"
//Images
#include "images/ddAddColumn.pngc"
#include "images/ddRemoveColumn.pngc"
#include "images/ddAddForeignKey.pngc"
#include "images/ddMaximizeTable.pngc"
#include "images/ddMinimizeTable.pngc"
#include "images/ddRemoveTable.pngc"
/*
All figures title, colums, indexes are store at same array to improve performance in the following order:
[0] = table border rect
[1] = table title
[2] = first column index
[maxColIndex] = last column index
[minIdxIndex] = first index index
[maxIdxIndex] = last index index
*/
void ddTableFigure::Init(wxString tableName, int x, int y)
{
setKindId(DDTABLEFIGURE);
internalPadding = 2;
externalPadding = 4;
selectingFkDestination = false;
//Set Value default Attributes
fontColorAttribute->fontColor = wxColour(49, 79, 79);
//Set Value default selected Attributes
lineSelAttribute->pen().SetColour(wxColour(204, 0, 0));
lineSelAttribute->pen().SetStyle(wxSOLID);
lineSelAttribute->pen().SetWidth(1);
fillSelAttribute->brush().SetColour(wxColour(255, 250, 205));
fillAttribute->brush().SetColour(wxColour(248, 248, 255));
fontSelColorAttribute->fontColor = wxColour(49, 79, 79);
//Set table size, width and position
rectangleFigure = new hdRectangleFigure();
rectangleFigure->moveTo(0, x, y);
add(rectangleFigure);
tableTitle = new ddTextTableItemFigure(tableName, dt_null, NULL);
tableTitle->setOwnerTable(this);
tableTitle->setEditable(true);
tableTitle->moveTo(0, x, y);
tableTitle->disablePopUp();
tableTitle->setShowDataType(false);
add(tableTitle);
tableTitle->moveTo(0, rectangleFigure->getBasicDisplayBox().x[0] + internalPadding * 2, rectangleFigure->getBasicDisplayBox().y[0] + internalPadding / 2);
//Intialize handles
wxBitmap image = wxBitmap(*ddAddColumn_png_img);
wxSize valueSize = wxSize(8, 8);
figureHandles->addItem(new ddAddColButtonHandle((hdIFigure *)this, (hdILocator *)new ddAddColLocator(), image, valueSize));
image = wxBitmap(*ddAddForeignKey_png_img);
figureHandles->addItem(new ddAddFkButtonHandle((hdIFigure *)this, (hdILocator *)new ddAddFkLocator(), image, valueSize));
image = wxBitmap(*ddRemoveTable_png_img);
figureHandles->addItem(new ddRemoveTableButtonHandle((hdIFigure *)this, (hdILocator *)new ddRemoveTableLocator(), image, valueSize));
image = wxBitmap(*ddMinimizeTable_png_img);
wxBitmap image2 = wxBitmap(*ddMaximizeTable_png_img);
figureHandles->addItem(new ddMinMaxTableButtonHandle((hdIFigure *)this, (hdILocator *)new ddMinMaxTableLocator(), image, image2, valueSize));
figureHandles->addItem(new ddSouthTableSizeHandle(this, (hdILocator *)new ddTableBottomLocator()));
//Intialize special handle
valueSize = wxSize(10, colsRect.GetSize().GetHeight());
scrollbar = new ddScrollBarHandle(this, (hdILocator *)new ddScrollBarTableLocator(), valueSize);
//Intialize columns window (min is always 1 in both, with or without cols & indxs)
colsRowsSize = 0;
colsWindow = 0;
idxsRowsSize = 0;
idxsWindow = 0;
//Initialize indexes (pointers to array segments)
maxColIndex = 2;
minIdxIndex = 2;
maxIdxIndex = 2;
//Initialize position where start to draw columns & indexes, this is the value to allow scrollbars
beginDrawCols = 2;
beginDrawIdxs = 2;
//Initialize
pkName = wxEmptyString;
ukNames.clear();
updateTableSize();
basicDisplayBox.x[0] = x;
basicDisplayBox.y[0] = y;
belongsToSchema = false;
}
ddTableFigure::ddTableFigure(wxString tableName, int x, int y):
hdCompositeFigure()
{
Init(tableName, x, y);
}
ddTableFigure::ddTableFigure(wxString tableName, int posIdx, int x, int y):
hdCompositeFigure()
{
Init(tableName, 0, 0);
//Check figure available positions for diagrams, add at least needed to allow initialization of the class
int i, start;
start = basicDisplayBox.CountPositions();
for(i = start; i < (posIdx + 1); i++)
{
AddPosForNewDiagram();
}
syncInternalsPosAt(posIdx, x, y);
}
//Used by persistence classes
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)
{
ukNames = UniqueKeysName;
pkName = primaryKeyName;
beginDrawCols = bdc;
beginDrawIdxs = bdi;
maxColIndex = maxcolsi;
minIdxIndex = minidxsi;
maxIdxIndex = maxidxsi;
colsRowsSize = colsrs;
colsWindow = colsw;
idxsRowsSize = idxsrs;
idxsWindow = idxsw;
updateTableSize();
}
ddTableFigure::~ddTableFigure()
{
if(scrollbar)
{
if(figureHandles->existsObject(scrollbar))
figureHandles->removeItem(scrollbar);
delete scrollbar;
}
}
void ddTableFigure::AddPosForNewDiagram()
{
//Add new position to internal calculations figure
fullSizeRect.addNewXYPosition();
titleRect.addNewXYPosition();
titleColsRect.addNewXYPosition();
colsRect.addNewXYPosition();
titleIndxsRect.addNewXYPosition();
indxsRect.addNewXYPosition();
unScrolledColsRect.addNewXYPosition();
unScrolledFullSizeRect.addNewXYPosition();
unScrolledTitleRect.addNewXYPosition();
//Add to all figure figures
hdCompositeFigure::AddPosForNewDiagram();
}
void ddTableFigure::RemovePosOfDiagram(int posIdx)
{
//Remove position for internal calculations figure
fullSizeRect.removeXYPosition(posIdx);
titleRect.removeXYPosition(posIdx);
titleColsRect.removeXYPosition(posIdx);
colsRect.removeXYPosition(posIdx);
titleIndxsRect.removeXYPosition(posIdx);
indxsRect.removeXYPosition(posIdx);
unScrolledColsRect.removeXYPosition(posIdx);
unScrolledFullSizeRect.removeXYPosition(posIdx);
unScrolledTitleRect.removeXYPosition(posIdx);
//remove position at all figure figures
hdCompositeFigure::RemovePosOfDiagram(posIdx);
}
ddColumnFigure *ddTableFigure::getColByName(wxString name)
{
ddColumnFigure *out = NULL;
ddColumnFigure *f;
hdIteratorBase *iterator = figuresEnumerator();
iterator->Next(); //First figure is main rect
iterator->Next(); //Second figure is main title
while(iterator->HasNext())
{
f = (ddColumnFigure *) iterator->Next();
if(f->getColumnName(false).IsSameAs(name))
{
out = f;
break;
}
}
delete iterator;
return out;
}
//WARNING: Columns SHOULD BE ADDED only using this function to avoid strange behaviors
void ddTableFigure::addColumn(int posIdx, ddColumnFigure *column)
{
column->setOwnerTable(this);
add(column);
//Update Indexes
if(maxColIndex == minIdxIndex) //maxColIndex == minIdxIndex means not indexes at this table, then update too
{
minIdxIndex++;
maxIdxIndex++;
}
maxColIndex++;
colsWindow++; //by default add a column increase initial window
colsRowsSize++;
updateTableSize(true);
//Fix column position at all available positions (Diagrams)
int i;
for(i = 0; i < basicDisplayBox.CountPositions(); i++)
{
syncInternalsPosAt(i, basicDisplayBox.x[i], basicDisplayBox.y[i]);
}
}
//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)
void ddTableFigure::syncInternalsPosAt(int posIdx, int x, int y)
{
basicDisplayBox.x[posIdx] = x;
basicDisplayBox.y[posIdx] = y;
rectangleFigure->moveTo(posIdx, x, y);
tableTitle->moveTo(posIdx, rectangleFigure->getBasicDisplayBox().x[posIdx] + internalPadding * 2, rectangleFigure->getBasicDisplayBox().y[posIdx] + internalPadding / 2);
calcInternalSubAreas(posIdx);
recalculateColsPos(posIdx);
}
//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)
void ddTableFigure::syncInternalsPosAt(wxArrayInt &x, wxArrayInt &y)
{
unsigned int posIdx, pointsCount = tableTitle->getBasicDisplayBox().CountPositions(), finalValue = x.Count();
//I need to check that figures inside figure have all points too
while(pointsCount < finalValue)
{
AddPosForNewDiagram();
pointsCount = tableTitle->getBasicDisplayBox().CountPositions();
}
//optimize this, because this is hack right now to avoid some weird problem when recreating figure status
basicDisplayBox.x = x;
basicDisplayBox.y = y;
for(posIdx = 0; posIdx < finalValue; posIdx++)
{
rectangleFigure->moveTo(posIdx, x[posIdx], y[posIdx]);
tableTitle->moveTo(posIdx, rectangleFigure->getBasicDisplayBox().x[posIdx] + internalPadding * 2, rectangleFigure->getBasicDisplayBox().y[posIdx] + internalPadding / 2);
calcInternalSubAreas(posIdx);
recalculateColsPos(posIdx);
}
}
//WARNING: Columns SHOULD BE ADDED only using this columns if was created as an image from storage one
void ddTableFigure::addColumnFromStorage(ddColumnFigure *column)
{
add(column);
}
void ddTableFigure::removeColumn(int posIdx, ddColumnFigure *column)
{
//Hack to allow to remove Fk before delete it.
if(column->isPrimaryKey() || column->isUniqueKey())
{
column->setColumnKindToNone();
}
column->setOwnerTable(NULL);
remove(column);
if(column)
delete column;
//Update Indexes
if(maxColIndex == minIdxIndex) //means not indexes at this table, then update too
{
minIdxIndex--;
maxIdxIndex--;
}
maxColIndex--;
if(colsRowsSize == colsWindow) //only decrease if size of window and columns is the same
colsWindow--;
colsRowsSize--;
if(beginDrawCols > 2)
beginDrawCols--;
calcInternalSubAreas(posIdx);
recalculateColsPos(posIdx);
if(colsWindow == colsRowsSize) //if handle need to be removed, remove it
{
if(figureHandles->existsObject(scrollbar))
figureHandles->removeItem(scrollbar);
}
//hack to update relationship position when table size change
manuallyNotifyChange(posIdx);
column = NULL;
}
void ddTableFigure::recalculateColsPos(int posIdx)
{
wxFont font = fontAttribute->font();
int defaultHeight = getColDefaultHeight(font);
hdIFigure *f = (hdIFigure *) figureFigures->getItemAt(0); //first figure is always Rect
int horizontalPos = f->displayBox().x[posIdx] + 2;
int verticalPos = 0;
for(int i = 2; i < maxColIndex ; i++)
{
f = (hdIFigure *) figureFigures->getItemAt(i); //table title
if( (i >= beginDrawCols) && (i <= (colsWindow + beginDrawCols)) ) //Visible to draw
{
verticalPos = colsRect.y[posIdx] + (defaultHeight * (i - beginDrawCols) + ((i - beginDrawCols) * internalPadding));
f->moveTo(posIdx, horizontalPos, verticalPos);
}
else
f->moveTo(posIdx, -65000, -65000); //any figure outside canvas (x<0 || y<0) is not draw & not used to calculate displaybox
}
}
void ddTableFigure::basicDraw(wxBufferedDC &context, hdDrawingView *view)
{
int idx = view->getIdx();
calcInternalSubAreas(idx);
if(calcScrolled) //Hack to avoid pass view as parameter to calcInternalSubAreas() because is sometimes called outside a paint event
{
view->CalcScrolledPosition(fullSizeRect.x[idx], fullSizeRect.y[idx], &fullSizeRect.x[idx], &fullSizeRect.y[idx]);
view->CalcScrolledPosition(titleRect.x[idx], titleRect.y[idx], &titleRect.x[idx], &titleRect.y[idx]);
view->CalcScrolledPosition(titleColsRect.x[idx], titleColsRect.y[idx], &titleColsRect.x[idx], &titleColsRect.y[idx]);
view->CalcScrolledPosition(colsRect.x[idx], colsRect.y[idx], &colsRect.x[idx], &colsRect.y[idx]);
view->CalcScrolledPosition(titleIndxsRect.x[idx], titleIndxsRect.y[idx], &titleIndxsRect.x[idx], &titleIndxsRect.y[idx]);
view->CalcScrolledPosition(indxsRect.x[idx], indxsRect.y[idx], &indxsRect.x[idx], &indxsRect.y[idx]);
calcScrolled = false;
}
hdIFigure *f = (hdIFigure *) figureFigures->getItemAt(0); //table rectangle
f->draw(context, view);
f = (hdIFigure *) figureFigures->getItemAt(1); //table title
f->draw(context, view);
for(int i = beginDrawCols; i < (colsWindow + beginDrawCols); i++)
{
f = (hdIFigure *) figureFigures->getItemAt(i); //table title
if(f->displayBox().GetPosition(view->getIdx()).x > 0 && f->displayBox().GetPosition(view->getIdx()).y > 0)
{
f->draw(context, view);
}
}
reapplyAttributes(context, view); //reset attributes to default of figure because can be modified at Draw functions.
//Set Font for title "Columns"
wxFont font = fontAttribute->font();
int newSize = font.GetPointSize() * 0.7;
font.SetPointSize(newSize);
context.SetFont(font);
//Draw Columns Title Line 1
context.DrawLine(titleColsRect.GetTopLeft(idx), titleColsRect.GetTopRight(idx));
//Draw Columns Title
context.DrawText(wxT("Columns"), titleColsRect.x[idx] + 3, titleColsRect.y[idx]);
//Draw Columns Title Line 2
context.DrawLine(titleColsRect.GetBottomLeft(idx), titleColsRect.GetBottomRight(idx));
//DrawVertical Lines
context.DrawLine(titleColsRect.GetBottomLeft(idx).x + 11, titleColsRect.GetBottomLeft(idx).y, titleColsRect.GetBottomLeft(idx).x + 11, titleIndxsRect.GetTopLeft(idx).y);
context.DrawLine(titleColsRect.GetBottomLeft(idx).x + 22, titleColsRect.GetBottomLeft(idx).y, titleColsRect.GetBottomLeft(idx).x + 22, titleIndxsRect.GetTopLeft(idx).y);
//Draw Indexes Title Line 1
context.DrawLine(titleIndxsRect.GetTopLeft(idx), titleIndxsRect.GetTopRight(idx));
//Draw Indexes Title
//disable until implemented in a future: context.DrawText(wxT("Indexes"),titleIndxsRect.x+3,titleIndxsRect.y);
//Draw Indexes Title Line 2
context.DrawLine(titleIndxsRect.GetBottomLeft(idx), titleIndxsRect.GetBottomRight(idx));
context.SetFont(fontAttribute->font()); //after change font return always to initial one
//Draw scrollbar is needed
if(scrollbar && figureHandles->existsObject(scrollbar))
scrollbar->draw(context, view);
//Use this in a future
//Hack to show message to select fk destination table
if(selectingFkDestination)
{
context.SetTextForeground(*wxWHITE);
wxBrush old = context.GetBrush();
context.SetBrush(*wxBLACK_BRUSH);
int w, h, x, y;
context.GetTextExtent(wxString(wxT("Select Destination table of foreign key")), &w, &h);
x = fullSizeRect.GetTopLeft(idx).x + (((fullSizeRect.GetTopRight(idx).x - fullSizeRect.GetTopLeft(idx).x) - w) / 2);
y = fullSizeRect.GetTopLeft(idx).y - h - 2;
context.DrawRectangle(wxRect(x, y, w, h));
context.DrawText(wxString(wxT("Select Destination table of foreign key")), x, y);
context.SetBrush(old);
context.SetTextForeground(*wxBLACK);
context.SetBackground(*wxWHITE);
//don't draw anything else then don't reapply default attributes
}
}
void ddTableFigure::basicDrawSelected(wxBufferedDC &context, hdDrawingView *view)
{
int idx = view->getIdx();
calcInternalSubAreas(idx);
if(calcScrolled) //Hack to avoid pass view as parameter to calcInternalSubAreas() because is sometimes called outside a paint event
{
view->CalcScrolledPosition(fullSizeRect.x[idx], fullSizeRect.y[idx], &fullSizeRect.x[idx], &fullSizeRect.y[idx]);
view->CalcScrolledPosition(titleRect.x[idx], titleRect.y[idx], &titleRect.x[idx], &titleRect.y[idx]);
view->CalcScrolledPosition(titleColsRect.x[idx], titleColsRect.y[idx], &titleColsRect.x[idx], &titleColsRect.y[idx]);
view->CalcScrolledPosition(colsRect.x[idx], colsRect.y[idx], &colsRect.x[idx], &colsRect.y[idx]);
view->CalcScrolledPosition(titleIndxsRect.x[idx], titleIndxsRect.y[idx], &titleIndxsRect.x[idx], &titleIndxsRect.y[idx]);
view->CalcScrolledPosition(indxsRect.x[idx], indxsRect.y[idx], &indxsRect.x[idx], &indxsRect.y[idx]);
calcScrolled = false;
}
hdIFigure *f = (hdIFigure *) figureFigures->getItemAt(0); //table rectangle
f->drawSelected(context, view);
f = (hdIFigure *) figureFigures->getItemAt(1); //table title
f->drawSelected(context, view);
for(int i = beginDrawCols; i < (colsWindow + beginDrawCols); i++)
{
f = (hdIFigure *) figureFigures->getItemAt(i); //table title
if(f->displayBox().GetPosition(view->getIdx()).x > 0 && f->displayBox().GetPosition(view->getIdx()).y > 0)
{
f->drawSelected(context, view);
}
}
reapplySelAttributes(context, view); //reset attributes to default of figure because can be modified at Draw functions.
wxFont font = fontAttribute->font();
float t = font.GetPointSize();
int newSize = font.GetPointSize() * 0.7;
font.SetPointSize(newSize);
context.SetFont(font);
//Draw Columns Title Line 1
context.DrawLine(titleColsRect.GetTopLeft(idx), titleColsRect.GetTopRight(idx));
//Draw Columns Title
context.DrawText(wxT("Columns"), titleColsRect.x[idx] + 3, titleColsRect.y[idx]);
//Draw Columns Title Line 2
context.DrawLine(titleColsRect.GetBottomLeft(idx), titleColsRect.GetBottomRight(idx));
//DrawVertical Lines
context.DrawLine(titleColsRect.GetBottomLeft(idx).x + 11, titleColsRect.GetBottomLeft(idx).y, titleColsRect.GetBottomLeft(idx).x + 11, titleIndxsRect.GetTopLeft(idx).y);
context.DrawLine(titleColsRect.GetBottomLeft(idx).x + 22, titleColsRect.GetBottomLeft(idx).y, titleColsRect.GetBottomLeft(idx).x + 22, titleIndxsRect.GetTopLeft(idx).y);
//Draw Indexes Title Line 1
context.DrawLine(titleIndxsRect.GetTopLeft(idx), titleIndxsRect.GetTopRight(idx));
//Draw Indexes Title
//disable until implemented in a future: context.DrawText(wxT("Indexes"),titleIndxsRect.x+3,titleIndxsRect.y);
//Draw Indexes Title Line 2
context.DrawLine(titleIndxsRect.GetBottomLeft(idx), titleIndxsRect.GetBottomRight(idx));
}
hdMultiPosRect &ddTableFigure::getBasicDisplayBox()
{
return basicDisplayBox;
}
void ddTableFigure::setColsRowsWindow(int num)
{
if(num > 0)
{
colsWindow = num;
wxFont font = fontAttribute->font();
colsRect.height = getColDefaultHeight(font) * colsWindow;
colsRect.width = getFiguresMaxWidth();
}
}
int ddTableFigure::getHeightFontMetric(wxString text, wxFont font)
{
int width, height;
wxBitmap emptyBitmap(*ddAddColumn_png_img);
wxMemoryDC temp_dc;
temp_dc.SelectObject(emptyBitmap);
temp_dc.SetFont(font);
temp_dc.GetTextExtent(text, &width, &height);
return height;
}
int ddTableFigure::getColDefaultHeight(wxFont font)
{
if(figureFigures->count() <= 0)
{
int width, height;
wxBitmap emptyBitmap(*ddAddColumn_png_img);
wxMemoryDC temp_dc;
temp_dc.SelectObject(emptyBitmap);
temp_dc.SetFont(font);
temp_dc.GetTextExtent(wxT("NewColumn"), &width, &height);
return height;
}
else
{
hdIFigure *f = (hdIFigure *) figureFigures->getItemAt(1); //table title
return f->displayBox().height;
}
}
//Show select fk destination Message Hack
void ddTableFigure::setSelectFkDestMode(bool value)
{
selectingFkDestination = value;
}
int ddTableFigure::getFiguresMaxWidth()
{
ddColumnFigure *cf;
hdGeometry g;
hdIteratorBase *iterator = figuresEnumerator();
iterator->Next(); //First figure is main rect
int maxWidth = 0;
cf = (ddColumnFigure *) iterator->Next(); //Second figure is main title
maxWidth = g.max(maxWidth, cf->displayBox().width + 20);
while(iterator->HasNext())
{
cf = (ddColumnFigure *) iterator->Next();
maxWidth = g.max(maxWidth, cf->displayBox().width);
}
delete iterator;
if(figureHandles->existsObject(scrollbar))
return maxWidth + 11; //as defined at locator
else
return maxWidth;
}
void ddTableFigure::calcInternalSubAreas(int posIdx)
{
calcScrolled = true;
int maxWidth = getFiguresMaxWidth() + externalPadding;
if(maxWidth < 100)
maxWidth = 100;
wxFont font = fontAttribute->font();
int defaultHeight = getColDefaultHeight(font);
hdRect db = basicDisplayBox.gethdRect(posIdx);
//*** titleRect
float t = font.GetPointSize();
int newSize = font.GetPointSize() * 0.7;
font.SetPointSize(newSize);
int colsTitleHeight = getHeightFontMetric(wxT("Columns"), font);
titleRect.x[posIdx] = db.x;
titleRect.y[posIdx] = db.y;
titleRect.width = maxWidth;
titleRect.height = defaultHeight;
titleColsRect.x[posIdx] = db.x;
titleColsRect.y[posIdx] = titleRect.y[posIdx] + titleRect.height;
titleColsRect.width = maxWidth;
titleColsRect.height = colsTitleHeight;
unScrolledTitleRect = titleColsRect;
//*** colsRect
colsRect.width = maxWidth;
if(colsWindow > 0)
colsRect.height = defaultHeight * colsWindow + (colsWindow * internalPadding);
else
colsRect.height = defaultHeight;
colsRect.x[posIdx] = db.x;
colsRect.y[posIdx] = titleRect.y[posIdx] + titleRect.height + titleColsRect.height;
unScrolledColsRect = colsRect;
//*** idxTitleRect
titleIndxsRect.width = maxWidth;
titleIndxsRect.height = colsTitleHeight;
titleIndxsRect.x[posIdx] = db.x;
titleIndxsRect.y[posIdx] = colsRect.y[posIdx] + colsRect.height;
//*** indexesRect
indxsRect.width = maxWidth;
indxsRect.height = defaultHeight * idxsWindow + (idxsWindow * internalPadding);
indxsRect.x[posIdx] = db.x;
indxsRect.y[posIdx] = titleIndxsRect.y[posIdx] + titleIndxsRect.height;
//*** FullTable Size
fullSizeRect.width = maxWidth;
fullSizeRect.height = titleRect.height + titleColsRect.height + colsRect.height + titleIndxsRect.height + indxsRect.height;
fullSizeRect.x[posIdx] = db.x;
fullSizeRect.y[posIdx] = titleRect.y[posIdx];
unScrolledFullSizeRect = fullSizeRect;
//Update size
wxSize sizeValue = fullSizeRect.GetSize();
rectangleFigure->setSize(sizeValue);
}
void ddTableFigure::updateTableSize(bool notifyChange)
{
//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)
ddColumnFigure *cf;
hdIteratorBase *iterator = figuresEnumerator();
iterator->Next(); //First figure is main rect
cf = (ddColumnFigure *) iterator->Next(); //Second figure is main title
while(iterator->HasNext())
{
cf = (ddColumnFigure *) iterator->Next();
cf->displayBoxUpdate();
}
delete iterator;
//Step 1: Update table size
calcInternalSubAreas(0);
basicDisplayBox.SetSize(fullSizeRect.GetSize());
if(notifyChange)
{
//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
manuallyNotifyChange(0);
}
}
hdMultiPosRect &ddTableFigure::getColsSpace()
{
return unScrolledColsRect;
}
hdMultiPosRect &ddTableFigure::getFullSpace()
{
return unScrolledFullSizeRect;
}
hdMultiPosRect &ddTableFigure::getTitleRect()
{
return unScrolledTitleRect;
}
int ddTableFigure::getTotalColumns()
{
return colsRowsSize;
}
int ddTableFigure::getColumnsWindow()
{
return colsWindow;
}
void ddTableFigure::setColumnsWindow(int posIdx, int value, bool maximize)
{
if(!maximize)
{
//if value >0 && <= max size table && table+offset < maxColIndex with window
if( (value > 0) && (value <= colsRowsSize) && (maxColIndex >= ( beginDrawCols + value ) ) )
{
colsWindow = value;
calcInternalSubAreas(posIdx);
recalculateColsPos(posIdx);
}
//if special case of needing to modify beginDrawCols then do it
if( (value > 0) && (value <= colsRowsSize) && (maxColIndex < ( beginDrawCols + value ) ) )
{
if( (beginDrawCols + colsWindow) == maxColIndex) //if index is at max
{
int diff = value - colsWindow; // value should be always higher tan colsWindows
if(diff > 0 && (beginDrawCols - diff) >= 0 )
{
beginDrawCols -= diff;
colsWindow = value;
calcInternalSubAreas(posIdx);
recalculateColsPos(posIdx);
}
}
}
}
else
{
beginDrawCols = 2;
colsWindow = value;
calcInternalSubAreas(posIdx);
recalculateColsPos(posIdx);
}
//Hide Scrollbar if needed
if(colsWindow == colsRowsSize)
{
if(figureHandles->existsObject(scrollbar))
figureHandles->removeItem(scrollbar);
}
else
{
if (!figureHandles->existsObject(scrollbar))
figureHandles->addItem(scrollbar);
}
}
void ddTableFigure::columnsWindowUp(int posIdx) //move window from number to zero
{
if( beginDrawCols > 2 )
{
beginDrawCols--;
calcInternalSubAreas(posIdx);
recalculateColsPos(posIdx);
}
}
void ddTableFigure::columnsWindowDown(int posIdx) //move window from number to maxcolumns
{
if( (beginDrawCols + colsWindow) < maxColIndex)
{
beginDrawCols++;
calcInternalSubAreas(posIdx);
recalculateColsPos(posIdx);
}
}
int ddTableFigure::getTopColWindowIndex()
{
return (beginDrawCols - 2);
}
void ddTableFigure::setPkConstraintName(wxString name)
{
pkName = name;
}
wxString ddTableFigure::getPkConstraintName()
{
return pkName;
}
wxArrayString ddTableFigure::getAllColumnsNames()
{
wxArrayString tmp;
ddColumnFigure *f;
tmp.Clear();
hdIteratorBase *iterator = figuresEnumerator();
iterator->Next(); //First figure is main rect
iterator->Next(); //Second figure is main title
while(iterator->HasNext())
{
f = (ddColumnFigure *) iterator->Next();
tmp.Add(f->getColumnName(false));
}
delete iterator;
return tmp;
}
wxArrayString ddTableFigure::getAllFkSourceColsNames(bool pk, int ukIndex)
{
wxArrayString tmp;
ddColumnFigure *f;
tmp.Clear();
hdIteratorBase *iterator = figuresEnumerator();
iterator->Next(); //First figure is main rect
iterator->Next(); //Second figure is main title
while(iterator->HasNext())
{
f = (ddColumnFigure *) iterator->Next();
if(pk)
{
if(f->isPrimaryKey())
tmp.Add(f->getColumnName(false));
}
else
{
if(f->isUniqueKey(ukIndex))
tmp.Add(f->getColumnName(false));
}
}
delete iterator;
return tmp;
}
ddColumnFigure *ddTableFigure::getColumnByName(wxString name)
{
ddColumnFigure *f;
hdIteratorBase *iterator = figuresEnumerator();
iterator->Next(); //First figure is main rect
iterator->Next(); //Second figure is main title
while(iterator->HasNext())
{
f = (ddColumnFigure *) iterator->Next();
if(f->getColumnName().IsSameAs(name))
{
return f;
}
}
delete iterator;
return NULL;
}
wxArrayString &ddTableFigure::getUkConstraintsNames()
{
return ukNames;
}
wxString ddTableFigure::getTableName()
{
ddTextTableItemFigure *c = (ddTextTableItemFigure *) figureFigures->getItemAt(1);
c->setOneTimeNoAlias();
return c->getText(false);
}
//set Null on all relationship items with a fk column to be delete or a pk to be removed (pk attribute)
void ddTableFigure::prepareForDeleteFkColumn(ddColumnFigure *column)
{
hdIteratorBase *iterator = observersEnumerator();
while(iterator->HasNext())
{
ddRelationshipFigure *r = (ddRelationshipFigure *) iterator->Next();
if(r->getStartFigure() == this) //Only update FK of connection with this table as source. source ---<| destination
r->prepareFkForDelete(column);
}
delete iterator;
}
// Note about observers:
// A table is observed by several relationships at same time, where that observers
// are just looking for changes that will affect relationship behavior.
// Ex: if I delete a pk on observed table (source) all observers (destination)
// should modify their columns to remove that fk created from that pk column.
// Warning: when a relationship is created an observer is added to both sides of relationship
// because this behavior (needed for update connection) to identify if is an observer
// of source table or destination table, should be check end figure, start!=end and end=this is end figure
// If start = and is recursive
void ddTableFigure::updateFkObservers()
{
hdIteratorBase *iterator = observersEnumerator();
while(iterator->HasNext())
{
ddRelationshipFigure *r = (ddRelationshipFigure *) iterator->Next();
if(r->getStartFigure() == this) //Only update FK of connection with this table as source. source ---<| destination
{
r->updateForeignKey();
}
}
delete iterator;
}
//If a column change datatype, should alert all others table to adjust their size with new values
void ddTableFigure::updateSizeOfObservers()
{
//For all tables that are observing this table, update their size
hdIteratorBase *iterator = observersEnumerator();
while(iterator->HasNext())
{
ddRelationshipFigure *r = (ddRelationshipFigure *) iterator->Next();
ddTableFigure *destFkTable = (ddTableFigure *) r->getEndFigure();
destFkTable->updateTableSize();
}
delete iterator;
}
//drop foreign keys with this table as origin or destination because table is going to be deleted
void ddTableFigure::processDeleteAlert(hdDrawing *drawing)
{
hdIteratorBase *iterator = observersEnumerator();
bool repeatFlag;
do
{
repeatFlag = false;
iterator->ResetIterator();
while(iterator->HasNext())
{
ddRelationshipFigure *rel = (ddRelationshipFigure *) iterator->Next();
rel->disconnectStart();
rel->disconnectEnd();
drawing->getOwnerEditor()->removeFromAllSelections(rel);
drawing->getOwnerEditor()->deleteModelFigure(rel);
repeatFlag = true;
break;
}
}
while(repeatFlag);
delete iterator;
}
void ddTableFigure::basicMoveBy(int posIdx, int x, int y)
{
hdIFigure *f = (hdIFigure *) figureFigures->getItemAt(0);
//Hack to avoid bug in if clause
int width = spaceForMovement.GetWidth();
int height = spaceForMovement.GetHeight();
int bottom = f->displayBox().y[posIdx] + f->displayBox().height + y;
int right = f->displayBox().x[posIdx] + f->displayBox().width + x;
int left = f->displayBox().x[posIdx] + x;
int top = f->displayBox().y[posIdx] + y;
//limit movemnt of table figures to canvas space
if( (left > 0) && (top > 0) && (right < width) && (bottom < height) )
hdCompositeFigure::basicMoveBy(posIdx, x, y);
}
//Validate status of table for SQL DDL generation
bool ddTableFigure::validateTable(wxString &errors)
{
bool out = true;
wxString tmp = wxEmptyString;
ddColumnFigure *f;
hdIteratorBase *iterator = figuresEnumerator();
iterator->Next(); //First figure is main rect
iterator->Next(); //Second figure is main title
while(iterator->HasNext())
{
f = (ddColumnFigure *) iterator->Next();
if(!f->validateColumn(tmp))
{
out = false;
}
}
if(!out)
{
errors.Append(wxT("\n"));
errors.Append(wxT("Errors detected at table") + this->getTableName() + wxT(" \n"));
errors.Append(tmp);
errors.Append(wxT("\n"));
}
delete iterator;
return out;
}
//Using some options from http://www.postgresql.org/docs/8.1/static/sql-createtable.html, but new options can be added in a future.
wxString ddTableFigure::generateSQLCreate(wxString schemaName)
{
//Columns and table
wxString tmp(wxT("CREATE TABLE "));
if(!schemaName.IsEmpty())
{
tmp += wxT("\"") + schemaName + wxT("\".\"") + getTableName() + wxT("\" (\n");
}
else
{
tmp += wxT("\"") + getTableName() + wxT("\" (\n");
}
hdIteratorBase *iterator = figuresEnumerator();
iterator->Next(); //Fixed Position for table rectangle
iterator->Next(); //Fixed Position for table name
while(iterator->HasNext())
{
ddColumnFigure *column = (ddColumnFigure *) iterator->Next();
tmp += column->generateSQL();
if(column->isNotNull())
{
tmp += wxT(" NOT NULL");
}
if(iterator->HasNext())
{
tmp += wxT(" , \n");
}
}
tmp += wxT("\n ); ");
return tmp;
}
wxString ddTableFigure::generateSQLAlterPks(wxString schemaName)
{
wxString tmp;
hdIteratorBase *iterator = figuresEnumerator();
//Pk, Uk Constraints
iterator->Next(); //Fixed Position for table rectangle
iterator->Next(); //Fixed Position for table name
int contPk = 0;
while(iterator->HasNext())
{
ddColumnFigure *column = (ddColumnFigure *) iterator->Next();
if(column->isPrimaryKey())
contPk++;
}
if(contPk > 0)
{
tmp += wxT("\nALTER TABLE ");
if(!schemaName.IsEmpty())
{
tmp += wxT("\"") + schemaName + wxT("\".\"") + getTableName() + wxT("\"");
}
else
{
tmp += wxT("\"") + getTableName() + wxT("\"") ;
}
tmp += wxT(" ADD ");
if(!pkName.IsEmpty())
{
tmp += wxT("CONSTRAINT \"") + pkName + wxT("\" ");
}
tmp += wxT("PRIMARY KEY ( ");
iterator->ResetIterator();
iterator->Next(); //Fixed Position for table rectangle
iterator->Next(); //Fixed Position for table name
while(iterator->HasNext())
{
ddColumnFigure *column = (ddColumnFigure *) iterator->Next();
if(column->isPrimaryKey())
{
tmp += wxT("\"") + column->getColumnName() + wxT("\"");
contPk--;
if(contPk > 0)
{
tmp += wxT(" , ");
}
else
{
tmp += wxT(" ); ");
}
}
}
}
delete iterator;
return tmp;
}
wxString ddTableFigure::generateSQLAlterFks(wxString schemaName)
{
wxString tmp;
hdIteratorBase *iterator = figuresEnumerator();
//Fk Constraint
iterator = observersEnumerator();
if(!iterator->HasNext())
{
tmp = wxEmptyString;
}
else
{
while(iterator->HasNext())
{
ddRelationshipFigure *rel = (ddRelationshipFigure *) iterator->Next();
if(rel->getStartFigure() != this)
{
tmp += rel->generateSQL(schemaName);
}
}
}
delete iterator;
return tmp;
}
wxString ddTableFigure::generateSQLAlterUks(wxString schemaName)
{
hdIteratorBase *iterator = figuresEnumerator();
//Pk, Uk Constraints
iterator->Next(); //Fixed Position for table rectangle
iterator->Next(); //Fixed Position for table name
int MaxUk = -1;
while(iterator->HasNext())
{
ddColumnFigure *column = (ddColumnFigure *) iterator->Next();
if(column->isUniqueKey() && column->getUniqueConstraintIndex() > MaxUk)
MaxUk = column->getUniqueConstraintIndex();
}
wxString tmp = wxEmptyString;
if(MaxUk >= 0)
{
int i;
for(i = 0; i <= MaxUk; i++)
{
tmp += wxT("\nALTER TABLE ");
if(!schemaName.IsEmpty())
tmp += wxT("\"") + schemaName + wxT("\".");
tmp += wxT("\"") + getTableName() + wxT("\"") ;
tmp += wxT(" ADD ");
if(!getUkConstraintsNames()[i].IsEmpty())
{
tmp += wxT("CONSTRAINT \"") + getUkConstraintsNames()[i] + wxT("\" ") ;
}
tmp += wxT("UNIQUE ( ");
int countUk = 0;
iterator->ResetIterator();
iterator->Next(); //Fixed Position for table rectangle
iterator->Next(); //Fixed Position for table name
while(iterator->HasNext())
{
ddColumnFigure *column = (ddColumnFigure *) iterator->Next();
if(column->getUniqueConstraintIndex() == i)
countUk++;
}
iterator->ResetIterator();
iterator->Next(); //Fixed Position for table rectangle
iterator->Next(); //Fixed Position for table name
while(iterator->HasNext())
{
ddColumnFigure *column = (ddColumnFigure *) iterator->Next();
if(column->isUniqueKey() && column->getUniqueConstraintIndex() == i)
{
tmp += wxT("\"") + column->getColumnName() + wxT("\"");
countUk--;
if(countUk > 0)
{
tmp += wxT(", ");
}
}
}
tmp += wxT(" ); ");
}
}
delete iterator;
return tmp;
}
wxString ddTableFigure::generateAltersTable(pgConn *connection, wxString schemaName, ddDatabaseDesign *design)
{
wxString out;
OID oidTable = ddImportDBUtils::getTableOID(connection, schemaName, getTableName());
if(oidTable == -1)
{
wxMessageBox(wxString::Format(_("Cannot build an ALTER TABLE statement for a non existing table (%s.%s)."),
schemaName.c_str(), getTableName().c_str()), _("Error when trying to get table OID"), wxICON_ERROR);
return wxEmptyString;
}
ddStubTable *dbTable = ddImportDBUtils::getTable(connection, getTableName(), oidTable);
if(!dbTable)
{
wxMessageBox(wxString::Format(_("Cannot reverse engineering table %s.%s."),
schemaName.c_str(), getTableName().c_str()), _("Error when trying to get stub table"), wxICON_ERROR);
return wxEmptyString;
}
//--------------- DETECT TABLE LEVEL CHANGES
// Check if TABLE was renamed
// **Not supported yet**
// Check if PRIMARY KEY constraint was renamed
// Some models don't set a name for the primary key, so we ignore those models
if(this->getPkConstraintName().Len() > 0 && !dbTable->PrimaryKeyName.IsSameAs(this->getPkConstraintName()))
{
out += wxT("\n");
out += wxT("ALTER INDEX \"") + dbTable->PrimaryKeyName + wxT("\" RENAME TO \"") + this->getPkConstraintName() + wxT("\";");
out += wxT("\n");
}
// Check if a UNIQUE KEY constraint was renamed
// **Not supported yet** (is it possible?)
//--------------- DETECT COLUMN LEVEL CHANGES
// Check if A COLUMN was renamed
// **Not supported yet**
// Look for columns that exist in the database but not in the model
bool pkRemovedflag = false;
bool ukRemoved = false;
stubColsHashMap::iterator it;
ddStubColumn *item;
for (it = dbTable->cols.begin(); it != dbTable->cols.end(); ++it)
{
wxString key = it->first;
item = it->second;
ddColumnFigure *column = getColumnByName(key);
// Check for changes at the column
if(column)
{
// Generate ALTER COLUMN statement if needed
// Datatype change, length, and precision are checked
// Temporary conversion fix to datatype of designer should be improved in a future
wxString dataType = item->typeColumn->Name();
bool sameDatatype = true, sameScale = true, samePrecision = true;
int s = -1, p = -1;
bool useScale = true, needps = false;
s = item->typeColumn->Length();
p = item->typeColumn->Precision();
if(dataType.IsSameAs(wxT("character varying"), false))
{
needps = true;
dataType = wxT("varchar(n)");
}
else if(dataType.IsSameAs(wxT("numeric"), false))
{
needps = true;
useScale = false;
dataType = wxT("numeric(p,s)");
}
else if(dataType.IsSameAs(wxT("interval"), false))
{
needps = true;
dataType = wxT("interval(n)");
}
else if(dataType.IsSameAs(wxT("bit"), false))
{
needps = true;
dataType = wxT("bit(n)");
}
else if(dataType.IsSameAs(wxT("char"), false))
{
needps = true;
dataType = wxT("char(n)");
}
else if(dataType.IsSameAs(wxT("varbit"), false))
{
needps = true;
dataType = wxT("varbit(n)");
}
else if(dataType.IsSameAs(wxT("character"), false))
{
needps = true;
dataType = wxT("char(n)");
}
if(needps)
{
if(useScale)
{
samePrecision = column->getPrecision() == s;
}
else
{
samePrecision = column->getPrecision() == s;
sameScale = column->getScale() == p;
}
}
sameDatatype = column->getRawDataType().IsSameAs(dataType, false);
if(!samePrecision || !sameScale || !sameDatatype)
{
out += wxT("\n");
out += wxT("ALTER TABLE ");
if(!schemaName.IsEmpty())
out += wxT("\"") + schemaName + wxT("\".");
out += wxT("\"") + getTableName() + wxT("\"") ;
out += wxT(" ALTER COLUMN ") + column->generateSQL(true) + wxT(";");
out += wxT("\n");
}
}
else // DROP COLUMN because it doesn't exist in the model anymore
{
out += wxT("\n");
out += wxT("ALTER TABLE ");
if(!schemaName.IsEmpty())
out += wxT("\"") + schemaName + wxT("\".");
out += wxT("\"") + getTableName() + wxT("\"") ;
out += wxT(" DROP COLUMN \"") + key + wxT("\";");
out += wxT("\n");
if(item->isPrimaryKey)
{
pkRemovedflag = true;
}
}
}
// Look for columns that exist in the model but not in the database
hdIteratorBase *iteratorPK = figuresEnumerator();
iteratorPK->Next(); //Fixed Position for table rectangle
iteratorPK->Next(); //Fixed Position for table name
bool pkAddedflag = false;
while(iteratorPK->HasNext())
{
ddColumnFigure *column = (ddColumnFigure *) iteratorPK->Next();
if(dbTable->cols.find(column->getColumnName()) == dbTable->cols.end()) //Exist in model but not in db
{
out += wxT("\n");
out += wxT("ALTER TABLE ");
if(!schemaName.IsEmpty())
out += wxT("\"") + schemaName + wxT("\".");
out += wxT("\"") + getTableName() + wxT("\"") ;
out += wxT(" ADD COLUMN ") + column->generateSQL() + wxT(";");
out += wxT("\n");
if(column->isPrimaryKey())
{
pkAddedflag = true;
}
}
}
// Look for all PRIMARY columns in the database's tables
bool pkChanged = false;
iteratorPK->ResetIterator();
iteratorPK->Next(); //Fixed Position for table rectangle
iteratorPK->Next(); //Fixed Position for table name
while(iteratorPK->HasNext())
{
ddColumnFigure *column = (ddColumnFigure *) iteratorPK->Next();
if(column->isPrimaryKey())
{
if(dbTable->cols.find(column->getColumnName()) == dbTable->cols.end())
{
pkChanged = true;
}
else
{
if(!dbTable->cols[column->getColumnName()]->isPrimaryKey)
{
pkChanged = true;
}
}
}
}
delete iteratorPK;
// Look for all PRIMARY columns in the model
stubColsHashMap::iterator itCol;
ddStubColumn *itemCol;
for (itCol = dbTable->cols.begin(); itCol != dbTable->cols.end(); ++itCol)
{
wxString colStubName = itCol->first;
itemCol = itCol->second;
if(itemCol->isPrimaryKey)
{
ddColumnFigure *col = getColumnByName(colStubName);
if(col != NULL)
{
if(!col->isPrimaryKey())
{
pkChanged = true;
}
}
else
{
pkChanged = true;
}
}
}
// Handle the addition of a new column to the primary key
if(pkAddedflag || pkRemovedflag || pkChanged)
{
// Drop existing primary key
out += wxT("\n");
out += wxT("ALTER TABLE ");
if(!schemaName.IsEmpty())
out += wxT("\"") + schemaName + wxT("\".");
out += wxT("\"") + getTableName() + wxT("\"") ;
out += wxT(" DROP CONSTRAINT \"") + this->getPkConstraintName() + wxT("\";");
// Create the new one
out += generateSQLAlterPks(schemaName);
}
// Handle changes from NOT NULL to NULL (always after dropping PK)
for (it = dbTable->cols.begin(); it != dbTable->cols.end(); ++it)
{
wxString key = it->first;
item = it->second;
ddColumnFigure *column = getColumnByName(key);
// Check for changes at the column level
if(column)
{
// Generate ALTER COLUMN statement if needed
// Datatype change, length and precision are handled.
// Temporary conversion fix to datatype of designer should be improved in a future
wxString dataType = item->typeColumn->Name();
bool sameDatatype = true, sameScale = true, samePrecision = true;
int s = -1, p = -1;
// Model has now NULL constraint, so drop the NOT NULL constraint
if(column->isNotNull())
{
if(!item->isNotNull)
{
out += wxT("\n");
out += wxT("ALTER TABLE ");
if(!schemaName.IsEmpty())
out += wxT("\"") + schemaName + wxT("\".");
out += wxT("\"") + getTableName() + wxT("\"") ;
out += wxT(" ALTER COLUMN \"") + column->getColumnName() + _("\" SET NOT NULL;");
out += wxT("\n");
}
}
else if(!column->isNotNull())
{
if(item->isNotNull)
{
out += wxT("\n");
out += wxT("ALTER TABLE \"");
if(!schemaName.IsEmpty())
out += wxT("\"") + schemaName + wxT("\".");
out += wxT("\"") + getTableName() + wxT("\"") ;
out += wxT("\" ALTER COLUMN \"") + column->getColumnName() + wxT("\" DROP NOT NULL;");
out += wxT("\n");
}
}
}
}
// Check UK conditions
int i, maxUkn = this->getUkConstraintsNames().Count();
for(i = 0; i < maxUkn; i++)
{
if(this->getUkConstraintsNames()[i].Len() == 0)
{
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."),
getTableName().c_str()), _("Trying to build ALTER sentences"), wxICON_ERROR);
return wxEmptyString;
}
}
// Search for UNIQUE key deleted from model, but available in the database
// Two steps process: first drop then delete from wxarraystring
int maxUkStub = dbTable->UniqueKeysNames.Count();
for(i = 0; i < maxUkStub; i++)
{
// Drop UK [in db but not in model]
if(this->getUkConstraintsNames().Index(dbTable->UniqueKeysNames[i]) == wxNOT_FOUND)
{
// Drop it
out += wxT("\n");
out += wxT("DROP INDEX \"") + dbTable->UniqueKeysNames[i] + wxT("\";");
out += wxT("\n");
// This index metadata is not useful anymore, so erase it at temporary stub table.
stubColsHashMap::iterator itDelUkIdx;
ddStubColumn *itemDelUkIdx;
for (itDelUkIdx = dbTable->cols.begin(); itDelUkIdx != dbTable->cols.end(); ++itDelUkIdx)
{
wxString keyDelUkIdx = itDelUkIdx->first;
itemDelUkIdx = itDelUkIdx->second;
if(itemDelUkIdx->uniqueKeyIndex == i)
{
itemDelUkIdx->uniqueKeyIndex = -1;
}
}
}
}
maxUkStub = dbTable->UniqueKeysNames.Count();
for(i = 0; i < maxUkStub; i++)
{
// Drop UK [in db but not in model]
if(this->getUkConstraintsNames().Index(dbTable->UniqueKeysNames[i]) == wxNOT_FOUND)
{
dbTable->UniqueKeysNames.RemoveAt(i);
maxUkStub = dbTable->UniqueKeysNames.Count();
}
}
// Search for new UK in model to add ADD CONSTRAINT clause
int maxUkModel = this->getUkConstraintsNames().Count();
for(i = 0; i < maxUkModel; i++)
{
// Add UK [in model but not in db]
if(dbTable->UniqueKeysNames.Index(this->getUkConstraintsNames()[i]) == wxNOT_FOUND)
{
// Create it
out += wxT("\nALTER TABLE ");
if(!schemaName.IsEmpty())
out += wxT("\"") + schemaName + wxT("\".");
out += wxT("\"") + getTableName() + wxT("\"") ;
out += wxT(" ADD ");
if(!getUkConstraintsNames()[i].IsEmpty())
{
out += wxT(" CONSTRAINT \"") + getUkConstraintsNames()[i] + wxT("\" ");
}
out += wxT(" UNIQUE ( ");
int countUk = 0;
hdIteratorBase *iteratorUk = figuresEnumerator();
iteratorUk->Next(); //Fixed Position for table rectangle
iteratorUk->Next(); //Fixed Position for table name
while(iteratorUk->HasNext())
{
ddColumnFigure *column = (ddColumnFigure *) iteratorUk->Next();
if(column->getUniqueConstraintIndex() == i)
countUk++;
}
iteratorUk->ResetIterator();
iteratorUk->Next(); //Fixed Position for table rectangle
iteratorUk->Next(); //Fixed Position for table name
while(iteratorUk->HasNext())
{
ddColumnFigure *column = (ddColumnFigure *) iteratorUk->Next();
if(column->isUniqueKey() && column->getUniqueConstraintIndex() == i)
{
out += wxT("\"") + column->getColumnName() + wxT("\"");
countUk--;
if(countUk > 0)
{
out += wxT(", ");
}
else
{
out += wxT(");");
}
}
}
delete iteratorUk;
}
}
// After delete/create UK, look for changes at existing UK
// Unified UK indexes at both dbtable and ddTableFigure [same index at dbtable that in tablefigure]
// BUT in table figure exists indexes without an equivalent in dbtable
maxUkStub = this->getUkConstraintsNames().Count();
for(i = 0; i < maxUkStub; i++)
{
int oldIndex = dbTable->UniqueKeysNames.Index(this->getUkConstraintsNames()[i]);
int newIndex = i;
if(oldIndex != wxNOT_FOUND)
{
stubColsHashMap::iterator itDelUkIdx;
ddStubColumn *itemDelUkIdx;
for (itDelUkIdx = dbTable->cols.begin(); itDelUkIdx != dbTable->cols.end(); ++itDelUkIdx)
{
wxString keyDelUkIdx = itDelUkIdx->first;
itemDelUkIdx = itDelUkIdx->second;
if(item->uniqueKeyIndex == oldIndex)
{
item->uniqueKeyIndex = newIndex;
}
}
}
}
// Generate again those UK that fill one of these conditions:
// 1. UK at tablefigure have more columns that at stub
// 2. Uk at stub have more column that at tablefigure
// Right now, UK constraints have unified indexes (same index at tablefigure and stub)
maxUkStub = this->getUkConstraintsNames().Count();
// for each UK
for(i = 0; i < maxUkStub; i++)
{
// Only for UKs with number unified that exists at both sides [db and tablefigure] [with boundaries check]
int boundarie1 = getUkConstraintsNames().Count();
int boundarie2 = dbTable->UniqueKeysNames.Count();
if( (i < boundarie1) && (i < boundarie2) && getUkConstraintsNames().Index( dbTable->UniqueKeysNames[i]) != wxNOT_FOUND )
{
// Assumption
bool createUkAgain = false;
// CHECK FIRST CONDITION ---> 1. UK at tablefigure have more columns that at stub?
hdIteratorBase *iteratorUk = figuresEnumerator();
iteratorUk->Next(); //Fixed Position for table rectangle
iteratorUk->Next(); //Fixed Position for table name
while(iteratorUk->HasNext())
{
ddColumnFigure *column = (ddColumnFigure *) iteratorUk->Next();
// For each UK column of the constraint with same index (position i, constraint at getUkConstraintsNames()[] )
if(column->getUniqueConstraintIndex() == i)
{
if(dbTable->cols.find(column->getColumnName()) == dbTable->cols.end()) //found at dbtable that uk column
{
createUkAgain = true;
}
else
{
ddStubColumn *stubCol = dbTable->cols[column->getColumnName()];
if(!stubCol->isUniqueKey())
{
createUkAgain = true;
}
}
}
}
delete iteratorUk;
// CHECK SECOND CONDITION ---> UK at stub have more uk column of same index that at tablefigure?
stubColsHashMap::iterator itCol;
ddStubColumn *itemCol;
for (itCol = dbTable->cols.begin(); itCol != dbTable->cols.end(); ++itCol)
{
bool isAtFigure = false;
wxString colStubName = itCol->first;
itemCol = itCol->second;
if(itemCol->uniqueKeyIndex == i)
{
ddColumnFigure *colUk = getColumnByName(colStubName);
if(colUk == NULL)
{
createUkAgain = true;
}
else
{
if(!colUk->isUniqueKey())
{
createUkAgain = true;
}
}
}
}
if(createUkAgain)
{
// Drop it
out += wxT("\n");
out += wxT("DROP INDEX \"") + dbTable->UniqueKeysNames[i] + wxT("\";");
out += wxT("\n");
// Create it
out += wxT("\nALTER TABLE ");
if(!schemaName.IsEmpty())
out += wxT("\"") + schemaName + wxT("\".");
out += wxT("\"") + getTableName() + wxT("\"") ;
out += wxT(" ADD ");
if(!getUkConstraintsNames()[i].IsEmpty())
{
out += wxT(" CONSTRAINT \"") + getUkConstraintsNames()[i] + wxT("\" ");
}
out += wxT(" UNIQUE ( ");
int countUk = 0;
hdIteratorBase *iteratorUk = figuresEnumerator();
iteratorUk->Next(); //Fixed Position for table rectangle
iteratorUk->Next(); //Fixed Position for table name
while(iteratorUk->HasNext())
{
ddColumnFigure *column = (ddColumnFigure *) iteratorUk->Next();
if(column->getUniqueConstraintIndex() == i)
countUk++;
}
iteratorUk->ResetIterator();
iteratorUk->Next(); //Fixed Position for table rectangle
iteratorUk->Next(); //Fixed Position for table name
while(iteratorUk->HasNext())
{
ddColumnFigure *column = (ddColumnFigure *) iteratorUk->Next();
if(column->isUniqueKey() && column->getUniqueConstraintIndex() == i)
{
out += wxT("\"") + column->getColumnName() + wxT("\"");
countUk--;
if(countUk > 0)
{
out += wxT(", ");
}
else
{
out += wxT(" );");
}
}
}
delete iteratorUk;
}
}
}
// Validate all FKs have a name defined at model
hdIteratorBase *iteratorRelations = observersEnumerator();
while(iteratorRelations->HasNext())
{
ddRelationshipFigure *r = (ddRelationshipFigure *) iteratorRelations->Next();
if(r->getConstraintName().Len() == 0) // Add to list, FKs with this table as destination. source ---<| destination
{
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."),
getTableName().c_str()), _("Trying to build ALTER sentences"), wxICON_ERROR);
return wxEmptyString;
}
}
//Check Foreign Keys
// FK at model are same (including attributes and columns) at db
iteratorRelations->ResetIterator();
while(iteratorRelations->HasNext())
{
ddRelationshipFigure *r = (ddRelationshipFigure *) iteratorRelations->Next();
if(r->getEndFigure() == this) // Only check FK this table as destination. source ---<| destination
{
// Check relationship from model exists a db?
if(ddImportDBUtils::existsFk(connection, dbTable->OIDTable, schemaName, r->getConstraintName(), r->getStartTable()->getTableName()))
{
// Columns and other properties are exactly the same? if not, then drop and create again
if(!ddImportDBUtils::isModelSameDbFk(connection, dbTable->OIDTable, schemaName, r->getConstraintName(), r->getStartTable()->getTableName(), r->getEndTable()->getTableName(), dbTable, r))
{
// Drop it first
out += wxT("\n");
out += wxT("ALTER TABLE ");
if(!schemaName.IsEmpty())
out += wxT("\"") + schemaName + wxT("\".");
out += wxT("\"") + this->getTableName() + wxT("\"") ;
out += wxT(" DROP CONSTRAINT \"") + r->getConstraintName() + wxT("\";");
out += wxT("\n");
// Then Add it again with changes
out += r->generateSQL(schemaName);
}
}
else //relationship only exists at model and not in db
{
//Create because it doesn't exists
out += r->generateSQL(schemaName);
}
}
}
// Check foreign keys available in the database but not in the model
// because we need to drop them
// First, create a list of FKs at destination table in the model
wxArrayString validFks;
iteratorRelations->ResetIterator();
while(iteratorRelations->HasNext())
{
ddRelationshipFigure *r = (ddRelationshipFigure *) iteratorRelations->Next();
if(r->getEndFigure() == this) // Add to list, FKs with this table as destination. source ---<| destination
{
validFks.Add(r->getConstraintName());
}
}
delete iteratorRelations;
wxArrayString fksToDelete = ddImportDBUtils::getFkAtDbNotInModel(connection, dbTable->OIDTable, schemaName, validFks , design);
int max = fksToDelete.Count();
for(i = 0; i < max; i++)
{
// Drop it
out += wxT("\n");
out += wxT("ALTER TABLE ");
if(!schemaName.IsEmpty())
out += wxT("\"") + schemaName + wxT("\".");
out += wxT("\"") + this->getTableName() + wxT("\"") ;
out += wxT(" DROP CONSTRAINT \"") + fksToDelete[i] + wxT("\";");
out += wxT("\n");
}
if(dbTable)
delete dbTable;
return out;
}