mirror of
https://github.com/levinsv/pgadmin3.git
synced 2026-05-15 14:15:49 -06:00
606 lines
16 KiB
C++
606 lines
16 KiB
C++
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// pgAdmin III - PostgreSQL Tools
|
|
//
|
|
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
|
// This software is released under the PostgreSQL Licence
|
|
//
|
|
// gqbController.cpp - Controller part of MVC Software Pattern used by GQB
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "pgAdmin3.h"
|
|
|
|
// wxWindows headers
|
|
#include <wx/wx.h>
|
|
#include <wx/grid.h>
|
|
#include <wx/dnd.h>
|
|
#include <wx/dataobj.h>
|
|
|
|
// App headers
|
|
#include "frm/frmQuery.h"
|
|
#include "gqb/gqbViewController.h"
|
|
#include "gqb/gqbModel.h"
|
|
#include "gqb/gqbSchema.h"
|
|
#include "gqb/gqbQueryObjs.h"
|
|
#include "gqb/gqbBrowser.h"
|
|
#include "gqb/gqbEvents.h"
|
|
#include "gqb/gqbViewPanels.h"
|
|
|
|
wxWindowID CTL_NTBKPANELS = ::wxNewId();
|
|
|
|
gqbController::gqbController(gqbModel *_model, wxWindow *gqbParent, ctlAuiNotebook *gridParent, wxSize size = wxSize(GQB_MIN_WIDTH, GQB_MIN_HEIGHT))
|
|
: wxObject()
|
|
{
|
|
pparent = gqbParent;
|
|
model = _model;
|
|
wxSize tablesBrowserSize = wxSize(200, 800); //Initial Size
|
|
|
|
// Initialize Main Splitter
|
|
gqbMainContainer = new wxSplitterWindow(gqbParent, GQB_HORZ_SASH, wxDefaultPosition, wxDefaultSize, wxSP_3D);
|
|
tabs = new wxNotebook(gqbMainContainer, CTL_NTBKPANELS, wxDefaultPosition, wxDefaultSize);
|
|
|
|
// Initialize view container with tables browser
|
|
// GQB-TODO: change 976 with a enum value of events.h
|
|
gqbContainer = new gqbSplitter(gqbMainContainer, 976, wxDefaultPosition, wxDefaultSize, wxSP_3D);
|
|
|
|
// Initialize view canvas and tables tree
|
|
browserPanel = new wxPanel(gqbContainer, wxID_ANY, wxDefaultPosition, tablesBrowserSize);
|
|
tablesBrowser = new gqbBrowser(browserPanel, GQB_BROWSER, wxDefaultPosition, wxDefaultSize, wxTR_HAS_BUTTONS | wxSIMPLE_BORDER, this);
|
|
view = new gqbView(gqbContainer, gridParent, size, this, model);
|
|
|
|
// Set Drag Target
|
|
view->SetDropTarget(new DnDText(tablesBrowser));
|
|
|
|
// Set Scroll Bar & split
|
|
view->SetScrollbars( 10, 10, 127, 80 );
|
|
gqbContainer->SplitVertically(browserPanel, view);
|
|
|
|
tabs->InsertPage(ti_colsGridPanel, view->getColsGridPanel(), _("Columns"));
|
|
tabs->InsertPage(ti_criteriaPanel, view->getCriteriaPanel(), _("Criteria"));
|
|
tabs->InsertPage(ti_orderPanel, view->getOrderPanel(), _("Ordering"));
|
|
tabs->InsertPage(ti_joinsPanel, view->getJoinsPanel(), _("Joins"));
|
|
gqbMainContainer->SplitHorizontally(gqbContainer, tabs);
|
|
|
|
// Fix Sash resize bug
|
|
gqbContainer->setTablesBrowser(tablesBrowser);
|
|
gqbContainer->setBrowserPanel(browserPanel);
|
|
}
|
|
|
|
|
|
// Destructor
|
|
gqbController::~gqbController()
|
|
{
|
|
if(view)
|
|
{
|
|
delete view;
|
|
view = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
// Add a table to the model
|
|
gqbQueryObject *gqbController::addTableToModel(gqbTable *table, wxPoint p)
|
|
{
|
|
gqbQueryObject *added = model->addTable(table, p);
|
|
view->newTableAdded(added);
|
|
return added;
|
|
}
|
|
|
|
|
|
// Remove a table from the model
|
|
void gqbController::removeTableFromModel(gqbQueryObject *mtable, gqbGridProjTable *gridTable = NULL, gqbGridOrderTable *orderLTable = NULL, gqbGridOrderTable *orderRTable = NULL)
|
|
{
|
|
model->deleteTable(mtable);
|
|
gridTable->removeAllRows(mtable); // GQB-TODO: move this functions to model??
|
|
orderLTable->emptyTableData(mtable);
|
|
orderRTable->emptyTableData(mtable);
|
|
}
|
|
|
|
|
|
// Add (Select to be use in projection) a column for table in a model
|
|
void gqbController::processColumnInModel(gqbQueryObject *table, gqbColumn *column, gqbGridProjTable *gridTable = NULL)
|
|
{
|
|
if(!table->existsColumn(column))
|
|
{
|
|
table->addColumn(column);
|
|
if(gridTable)
|
|
{
|
|
gridTable->AppendItem(0, table);
|
|
gridTable->AppendItem(1, column);
|
|
gridTable->AppendItem(2, NULL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Removes but don't delete because we needed the column at table [this is just a pointer to it]
|
|
table->removeColumn(column);
|
|
if(gridTable)
|
|
{
|
|
gridTable->removeRow(table, column);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Get selected item at position pt, marks item as selected and if there is a previous selected then unselect it
|
|
gqbObject *gqbController::getModelSelected(wxPoint &pt, gqbQueryObject *lastSelected, gqbQueryJoin *lastJoinSelected, bool mark)
|
|
{
|
|
gqbQueryObject *sel = NULL;
|
|
gqbQueryJoin *jSel = NULL;
|
|
gqbIteratorBase *iterator = model->createDownQueryIterator();
|
|
bool found = false;
|
|
|
|
while(!found && iterator->HasNext())
|
|
{
|
|
// Try to find if click over a table
|
|
sel = (gqbQueryObject *)iterator->Next();
|
|
if( ((pt.x - sel->position.x > 0 ) && (sel->position.x + sel->getWidth() > pt.x)) && ((pt.y - sel->position.y > 0) && (sel->position.y + sel->getHeight() > pt.y)) )
|
|
{
|
|
// GQB-TODO: this function should be move to the view to allow to
|
|
// recalculate it if the graphBehavior changes
|
|
if(mark)
|
|
sel->setSelected(true);
|
|
|
|
jSel = NULL;
|
|
found = true;
|
|
break;
|
|
}
|
|
|
|
// Try to find if user click over a Join
|
|
if(sel->getHaveJoins())
|
|
{
|
|
gqbIteratorBase *joinsIterator = sel->createJoinsIterator();
|
|
while(joinsIterator->HasNext())
|
|
{
|
|
// GQB-TODO: don't pass anchor because join it's passed as parameter yet.
|
|
jSel = (gqbQueryJoin *) joinsIterator->Next();
|
|
wxPoint o = jSel->getSourceAnchor();
|
|
wxPoint d = jSel->getDestAnchor();
|
|
|
|
if(view->clickOnJoin(jSel, pt, o, d))
|
|
{
|
|
if(mark)
|
|
jSel->setSelected(true);
|
|
found = true;
|
|
sel = NULL;
|
|
break;
|
|
}
|
|
|
|
}
|
|
delete joinsIterator;
|
|
}
|
|
}
|
|
|
|
delete iterator;
|
|
|
|
if(found)
|
|
{
|
|
if(mark)
|
|
{
|
|
if(lastSelected && lastSelected != sel) // Unselect previous table selected if exists
|
|
lastSelected->setSelected(false);
|
|
// Unselect previous join selected if exists
|
|
if(lastJoinSelected && lastJoinSelected != jSel)
|
|
lastJoinSelected->setSelected(false);
|
|
}
|
|
if(sel)
|
|
return sel;
|
|
else if(jSel)
|
|
return jSel;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
// Unselect any previously selected item in the model
|
|
void gqbController::unsetModelSelected(bool queryTable = true)
|
|
{
|
|
gqbQueryObject *sel = NULL;
|
|
gqbQueryJoin *jSel = NULL;
|
|
|
|
if(queryTable) // QueryTable
|
|
{
|
|
gqbIteratorBase *iterator = model->createQueryIterator();
|
|
while(iterator->HasNext())
|
|
{
|
|
sel = (gqbQueryObject *)iterator->Next();
|
|
if(sel->getSelected())
|
|
{
|
|
sel->setSelected(false);
|
|
}
|
|
}
|
|
delete iterator;
|
|
}
|
|
|
|
if(!queryTable) // QueryJoin
|
|
{
|
|
gqbIteratorBase *iterator = model->createQueryIterator();
|
|
gqbQueryObject *sel = NULL;
|
|
while(iterator->HasNext())
|
|
{
|
|
sel = (gqbQueryObject *)iterator->Next();
|
|
if(sel->getHaveJoins())
|
|
{
|
|
gqbIteratorBase *joinsIterator = sel->createJoinsIterator();
|
|
while(joinsIterator->HasNext())
|
|
{
|
|
// GQB-TODO: don't pass anchor because join it's passed as parameter yet.
|
|
jSel = (gqbQueryJoin *) joinsIterator->Next();
|
|
if(jSel->getSelected())
|
|
{
|
|
jSel->setSelected(false);
|
|
}
|
|
}
|
|
|
|
delete joinsIterator;
|
|
}
|
|
}
|
|
delete iterator;
|
|
}
|
|
}
|
|
|
|
|
|
// GQB-TODO: Create a less complex & simpler generation function
|
|
// Generate the SQL Sentence from the model
|
|
wxString gqbController::generateSQL()
|
|
{
|
|
wxString sentence = wxT("");
|
|
if(model->tablesCount() > 0)
|
|
{
|
|
sentence += wxT("SELECT \n");
|
|
|
|
// Add selected columns for Query
|
|
gqbQueryObject *sel = NULL;
|
|
gqbIteratorBase *iteratorRestrictions = NULL;
|
|
gqbIteratorBase *iteratorModel = NULL;
|
|
gqbIteratorBase *iteratorJoins = NULL;
|
|
|
|
// Projection Part [Select x,x,x...]
|
|
gqbObjsArray *cols = model->getOrderedColumns();
|
|
gqbObjsArray *tables = model->getColumnsParents();
|
|
wxArrayString *alias = model->getColumnsAlias();
|
|
|
|
int i, size = cols->GetCount();
|
|
|
|
for(i = 0; i < size; i++)
|
|
{
|
|
if(alias->Item(i).length() > 0)
|
|
{
|
|
if(((gqbQueryObject *)tables->Item(i))->getAlias().length() > 0)
|
|
{
|
|
sentence += wxT(" ") +
|
|
qtIdent(((gqbQueryObject *)tables->Item(i))->getAlias()) +
|
|
wxT(".") +
|
|
qtIdent(((gqbColumn *)cols->Item(i))->getName()) +
|
|
wxT(" AS ") +
|
|
qtIdent(alias->Item(i)) +
|
|
wxT(", \n");
|
|
}
|
|
else
|
|
{
|
|
sentence += wxT(" ") +
|
|
qtIdent(((gqbQueryObject *)tables->Item(i))->getName()) +
|
|
wxT(".") +
|
|
qtIdent(((gqbColumn *)cols->Item(i))->getName()) +
|
|
wxT(" AS ") +
|
|
qtIdent(alias->Item(i)) +
|
|
wxT(", \n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(((gqbQueryObject *)tables->Item(i))->getAlias().length() > 0)
|
|
{
|
|
sentence += wxT(" ") +
|
|
qtIdent(((gqbQueryObject *)tables->Item(i))->getAlias()) +
|
|
wxT(".") +
|
|
qtIdent(((gqbColumn *)cols->Item(i))->getName()) +
|
|
wxT(", \n");
|
|
}
|
|
else
|
|
{
|
|
sentence += wxT(" ") +
|
|
qtIdent(((gqbQueryObject *)tables->Item(i))->getName()) +
|
|
wxT(".") +
|
|
qtIdent(((gqbColumn *)cols->Item(i))->getName()) +
|
|
wxT(", \n");
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!size)
|
|
sentence += wxT(" * \n");
|
|
else
|
|
{
|
|
// remove last ", "
|
|
sentence.Truncate(sentence.Length() - 3);
|
|
sentence += wxT("\n");
|
|
}
|
|
|
|
sentence += wxT("FROM \n");
|
|
|
|
iteratorModel = model->createQueryIterator();
|
|
while(iteratorModel->HasNext())
|
|
{
|
|
sel = (gqbQueryObject *)iteratorModel->Next();
|
|
gqbSchema *schema = (gqbSchema *)sel->parent->getOwner();
|
|
if(sel->getAlias().length() > 0)
|
|
{
|
|
sentence += wxT(" ") +
|
|
qtIdent(schema->getName()) +
|
|
wxT(".") +
|
|
qtIdent(sel->getName()) +
|
|
wxT(" ") +
|
|
qtIdent(sel->getAlias()) +
|
|
wxT(", \n");
|
|
}
|
|
else
|
|
{
|
|
sentence += wxT(" ") +
|
|
qtIdent(schema->getName()) +
|
|
wxT(".") +
|
|
qtIdent(sel->getName()) +
|
|
wxT(", \n");
|
|
}
|
|
|
|
}
|
|
sentence.Truncate(sentence.Length() - 3); // remove last ", "
|
|
|
|
// WHERE PART
|
|
// [Joins]
|
|
bool first = true, truncAnd = false;
|
|
gqbQueryJoin *tmp;
|
|
iteratorModel->ResetIterator();
|
|
while(iteratorModel->HasNext())
|
|
{
|
|
sel = (gqbQueryObject *)iteratorModel->Next();
|
|
if(sel->getHaveJoins())
|
|
{
|
|
truncAnd = true;
|
|
iteratorJoins = sel->createJoinsIterator();
|
|
while(iteratorJoins->HasNext())
|
|
{
|
|
if(first)
|
|
{
|
|
first = false;
|
|
sentence += wxT("\nWHERE \n");
|
|
}
|
|
tmp = (gqbQueryJoin *)iteratorJoins->Next();
|
|
|
|
if(tmp->getSourceQTable()->getAlias().length() > 0)
|
|
sentence += wxT(" ") +
|
|
qtIdent(tmp->getSourceQTable()->getAlias()) +
|
|
wxT(".") +
|
|
qtIdent(tmp->getSourceCol());
|
|
else
|
|
sentence += wxT(" ") +
|
|
qtIdent(tmp->getSourceQTable()->getName()) +
|
|
wxT(".") +
|
|
qtIdent(tmp->getSourceCol());
|
|
|
|
switch(tmp->getKindofJoin())
|
|
{
|
|
case _equally:
|
|
sentence += wxT(" = ");
|
|
break;
|
|
case _greater:
|
|
sentence += wxT(" > ");
|
|
break;
|
|
case _lesser:
|
|
sentence += wxT(" < ");
|
|
break;
|
|
case _equgreater:
|
|
sentence += wxT(" >= ");
|
|
break;
|
|
case _equlesser:
|
|
sentence += wxT(" <= ");
|
|
break;
|
|
|
|
}
|
|
|
|
if(tmp->getDestQTable()->getAlias().length() > 0)
|
|
sentence += qtIdent(tmp->getDestQTable()->getAlias()) +
|
|
wxT(".") +
|
|
qtIdent(tmp->getDestCol()) +
|
|
wxT(" AND\n");
|
|
else
|
|
sentence += qtIdent(tmp->getDestQTable()->getName()) +
|
|
wxT(".") +
|
|
qtIdent(tmp->getDestCol()) +
|
|
wxT(" AND\n");
|
|
}
|
|
delete iteratorJoins;
|
|
}
|
|
}
|
|
delete iteratorModel;
|
|
|
|
gqbRestrictions *restrictions = model->getRestrictions();
|
|
|
|
// Remove last " AND " from joins if there isn't restrictions, only left white space
|
|
if(truncAnd && (restrictions->restrictionsCount() <= 0))
|
|
sentence.Truncate(sentence.Length() - 5);
|
|
|
|
// Never found a join
|
|
if (!truncAnd && (restrictions->restrictionsCount() > 0))
|
|
sentence += wxT("\nWHERE \n");
|
|
|
|
//GQB-TODO: VALIDATE RESTRICTIONS
|
|
iteratorRestrictions = restrictions->createRestrictionsIterator();
|
|
gqbQueryRestriction *r = NULL;
|
|
|
|
while(iteratorRestrictions->HasNext())
|
|
{
|
|
r = (gqbQueryRestriction *)iteratorRestrictions->Next();
|
|
sentence += wxT(" ") +
|
|
r->getLeft() +
|
|
wxT(" ") +
|
|
r->getRestriction() +
|
|
wxT(" ") +
|
|
r->getValue_s() +
|
|
wxT(" ") +
|
|
r->getConnector() +
|
|
wxT(" \n");
|
|
}
|
|
delete iteratorRestrictions;
|
|
|
|
if(restrictions->restrictionsCount() > 0)
|
|
{
|
|
if(r->getConnector().Contains(wxT("AND")))
|
|
{
|
|
sentence.Truncate(sentence.Length() - 6);
|
|
}
|
|
else
|
|
{
|
|
sentence.Truncate(sentence.Length() - 5);
|
|
}
|
|
}
|
|
// ORDER BY PART
|
|
gqbObjsArray *orderByColumns = model->getOrdByColumns();
|
|
gqbObjsArray *orderByParents = model->getOrdByParents();
|
|
charArray *typeOfOrder = model->getOrdByKind();
|
|
|
|
size = orderByColumns->GetCount();
|
|
if(size > 0)
|
|
{
|
|
sentence += wxT("\nORDER BY\n");
|
|
}
|
|
wxString typeOrder = wxT("");
|
|
for(i = 0; i < size; i++)
|
|
{
|
|
switch(typeOfOrder->Item(i))
|
|
{
|
|
case 'A':
|
|
typeOrder = wxT(" ASC");
|
|
break;
|
|
case 'D':
|
|
typeOrder = wxT(" DESC");
|
|
break;
|
|
};
|
|
if(((gqbQueryObject *)orderByParents->Item(i))->getAlias().length() > 0)
|
|
sentence += wxT(" ") +
|
|
qtIdent(((gqbQueryObject *)orderByParents->Item(i))->getAlias()) +
|
|
wxT(".") +
|
|
qtIdent(((gqbColumn *)orderByColumns->Item(i))->getName()) +
|
|
typeOrder +
|
|
wxT(", \n");
|
|
else
|
|
sentence += wxT(" ") +
|
|
qtIdent(((gqbQueryObject *)orderByParents->Item(i))->getName()) +
|
|
wxT(".") +
|
|
qtIdent(((gqbColumn *)orderByColumns->Item(i))->getName()) +
|
|
typeOrder +
|
|
wxT(", \n");
|
|
}
|
|
|
|
if(size > 0)
|
|
sentence.Truncate(sentence.Length() - 3);
|
|
|
|
sentence += wxT(";");
|
|
} // Close Tables Count > 0 on model
|
|
return sentence;
|
|
}
|
|
|
|
|
|
gqbQueryRestriction *gqbController::addRestriction()
|
|
{
|
|
return model->addRestriction();
|
|
}
|
|
|
|
|
|
gqbQueryJoin *gqbController::addJoin(gqbQueryObject *sTable, gqbColumn *sColumn, gqbQueryObject *dTable, gqbColumn *dColumn, type_Join kind)
|
|
{
|
|
return sTable->addJoin(sTable, dTable, sColumn, dColumn, kind);
|
|
}
|
|
|
|
|
|
void gqbController::removeJoin(gqbQueryJoin *join)
|
|
{
|
|
join->getSourceQTable()->removeJoin(join, true);
|
|
}
|
|
|
|
|
|
void gqbController::setPointerMode(pointerMode pm)
|
|
{
|
|
view->setPointerMode(pm);
|
|
}
|
|
|
|
|
|
void gqbController::emptyModel()
|
|
{
|
|
view->emptyPanelsData();
|
|
model->emptyAll();
|
|
}
|
|
|
|
|
|
void gqbController::calcGridColsSizes()
|
|
{
|
|
// Recalculate best internals sizes for all columns inside grids.
|
|
((gqbGridPanel *)view->getColsGridPanel())->SetGridColsSize();
|
|
((gqbCriteriaPanel *)view->getCriteriaPanel())->SetGridColsSize();
|
|
((gqbOrderPanel *)view->getOrderPanel())->SetGridColsSize();
|
|
((gqbJoinsPanel *)view->getJoinsPanel())->SetGridColsSize();
|
|
}
|
|
|
|
|
|
void gqbController::setSashVertPosition(int pos)
|
|
{
|
|
gqbContainer->UpdateSize();
|
|
gqbContainer->SetSashPosition(pos, true);
|
|
gqbContainer->SetMinimumPaneSize(pos);
|
|
gqbContainer->UpdateSize();
|
|
}
|
|
|
|
int gqbController::getSashHorizPosition()
|
|
{
|
|
return gqbMainContainer->GetSashPosition();
|
|
}
|
|
|
|
|
|
void gqbController::setSashHorizPosition(int pos)
|
|
{
|
|
gqbMainContainer->UpdateSize();
|
|
gqbMainContainer->SetSashPosition(pos, true);
|
|
|
|
if(pos >= 150 && pos <= 200)
|
|
{
|
|
gqbMainContainer->SetMinimumPaneSize(pos);
|
|
}
|
|
else
|
|
{
|
|
gqbMainContainer->SetMinimumPaneSize(150);
|
|
}
|
|
gqbMainContainer->UpdateSize();
|
|
}
|
|
|
|
|
|
//
|
|
// Utility Class to avoid a bug when the event sash resize is called [onVerticalSashResize]
|
|
//
|
|
|
|
// GQB-TODO: fix 976 for real one value here and above
|
|
|
|
BEGIN_EVENT_TABLE(gqbSplitter, wxSplitterWindow)
|
|
EVT_SPLITTER_SASH_POS_CHANGED(976, gqbSplitter::onVerticalSashResize)
|
|
END_EVENT_TABLE()
|
|
|
|
gqbSplitter::gqbSplitter(wxWindow *parent, wxWindowID id, const wxPoint &point, const wxSize &size, long style)
|
|
: wxSplitterWindow(parent, id, point, size, style),
|
|
tablesBrowser(NULL),
|
|
browserPanel(NULL)
|
|
{
|
|
}
|
|
|
|
|
|
void gqbSplitter::onVerticalSashResize(wxSplitterEvent &event)
|
|
{
|
|
if(tablesBrowser != NULL && browserPanel != NULL)
|
|
{
|
|
wxSize s = tablesBrowser->GetSize();
|
|
s.SetWidth(event.GetSashPosition());
|
|
browserPanel->SetSize(s);
|
|
tablesBrowser->SetSize(s);
|
|
// GQB-TODO: Set a minimun value
|
|
}
|
|
}
|