mirror of
https://github.com/levinsv/pgadmin3.git
synced 2026-05-15 14:15:49 -06:00
1798 lines
53 KiB
C++
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;
|
|
}
|