pgadmin3/gqb/gqbController.cpp
2020-07-07 22:19:12 +05:00

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
}
}