mirror of
https://github.com/levinsv/pgadmin3.git
synced 2026-05-15 22:02:24 -06:00
509 lines
10 KiB
C++
509 lines
10 KiB
C++
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// pgScript - PostgreSQL Tools
|
|
//
|
|
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
|
// This software is released under the PostgreSQL Licence
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
#include "pgAdmin3.h"
|
|
#include "pgscript/objects/pgsRecord.h"
|
|
|
|
#include "pgscript/expressions/pgsEqual.h"
|
|
#include "pgscript/objects/pgsNumber.h"
|
|
#include "pgscript/objects/pgsString.h"
|
|
#include "pgscript/exceptions/pgsArithmeticException.h"
|
|
#include "pgscript/exceptions/pgsCastException.h"
|
|
|
|
#include <wx/arrimpl.cpp>
|
|
|
|
WX_DEFINE_OBJARRAY(pgsVectorRecordLine);
|
|
WX_DEFINE_OBJARRAY(pgsVectorRecord);
|
|
|
|
pgsRecord::pgsRecord(const USHORT &nb_columns) :
|
|
pgsVariable(pgsVariable::pgsTRecord)
|
|
{
|
|
m_columns.SetCount(nb_columns);
|
|
}
|
|
|
|
pgsRecord::~pgsRecord()
|
|
{
|
|
|
|
}
|
|
|
|
pgsVariable *pgsRecord::clone() const
|
|
{
|
|
return pnew pgsRecord(*this);
|
|
}
|
|
|
|
wxString pgsRecord::value() const
|
|
{
|
|
wxString data;
|
|
pgsVarMap vars;
|
|
|
|
// Go through each line and enclose it into braces
|
|
for (USHORT i = 0; i < count_lines(); i++)
|
|
{
|
|
data += wxT("(");
|
|
|
|
// Go through each column and separate them with commas
|
|
for (USHORT j = 0; j < count_columns(); j++)
|
|
{
|
|
wxString elm(m_record[i][j]->eval(vars)->value());
|
|
if (!m_record[i][j]->is_number())
|
|
{
|
|
elm.Replace(wxT("\\"), wxT("\\\\"));
|
|
elm.Replace(wxT("\""), wxT("\\\""));
|
|
elm = wxT("\"") + elm + wxT("\"");
|
|
}
|
|
data += elm + (j != count_columns() - 1 ? wxT(",") : wxT(""));
|
|
}
|
|
|
|
data += (i == count_lines() - 1) ? wxT(")") : wxT(")\n");
|
|
}
|
|
|
|
// Return the string representation of the record
|
|
return data;
|
|
}
|
|
|
|
pgsOperand pgsRecord::eval(pgsVarMap &vars) const
|
|
{
|
|
return this->clone();
|
|
}
|
|
|
|
USHORT pgsRecord::count_lines() const
|
|
{
|
|
return m_record.GetCount();
|
|
}
|
|
|
|
USHORT pgsRecord::count_columns() const
|
|
{
|
|
return m_columns.GetCount();
|
|
}
|
|
|
|
bool pgsRecord::insert(const USHORT &line, const USHORT &column,
|
|
pgsOperand value)
|
|
{
|
|
// Add lines to match the line number provided
|
|
for (USHORT i = count_lines(); i <= line; i++)
|
|
{
|
|
newline();
|
|
}
|
|
|
|
// Cannot insert if column is invalid
|
|
if (column >= count_columns())
|
|
{
|
|
return false;
|
|
}
|
|
// Insert the value at line.column
|
|
else
|
|
{
|
|
m_record[line][column] = value;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
pgsOperand pgsRecord::get(const USHORT &line,
|
|
const USHORT &column) const
|
|
{
|
|
if (line < count_lines() && column < count_columns())
|
|
{
|
|
return m_record[line][column];
|
|
}
|
|
else
|
|
{
|
|
return pnew pgsString(wxT(""));
|
|
}
|
|
}
|
|
|
|
pgsOperand pgsRecord::get_line(const USHORT &line) const
|
|
{
|
|
if (line < count_lines())
|
|
{
|
|
pgsRecord *rec = pnew pgsRecord(count_columns());
|
|
rec->m_columns = this->m_columns;
|
|
rec->newline();
|
|
rec->m_record[0] = this->m_record[line];
|
|
return rec;
|
|
}
|
|
else
|
|
{
|
|
return pnew pgsRecord(0);
|
|
}
|
|
}
|
|
|
|
bool pgsRecord::set_column_name(const USHORT &column, wxString name)
|
|
{
|
|
// Column number must be valid
|
|
if (column >= count_columns())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Column name must not exist
|
|
// Column name must not be empty
|
|
name = name.Strip(wxString::both).Lower();
|
|
if (m_columns.Index(name) != wxNOT_FOUND || name.IsEmpty())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Set the column name
|
|
m_columns[column] = name;
|
|
|
|
return true;
|
|
}
|
|
|
|
USHORT pgsRecord::get_column(wxString name) const
|
|
{
|
|
name = name.Strip(wxString::both).Lower();
|
|
if (name.IsEmpty())
|
|
{
|
|
return count_columns();
|
|
}
|
|
for (USHORT i = 0; i < count_columns(); i++)
|
|
{
|
|
if (m_columns[i] == name)
|
|
return i;
|
|
}
|
|
return count_columns();
|
|
}
|
|
|
|
bool pgsRecord::remove_line(const USHORT &line)
|
|
{
|
|
if (line < count_lines())
|
|
{
|
|
m_record.RemoveAt(line);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool pgsRecord::newline()
|
|
{
|
|
// Insert a line
|
|
m_record.Add(pgsVectorRecordLine());
|
|
// Initialize each column of the line with an empty string
|
|
for (USHORT i = 0; i < count_columns(); i++)
|
|
{
|
|
m_record.Last().Add(pnew pgsString(wxT("")));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool pgsRecord::valid() const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool pgsRecord::operator==(const pgsRecord &rhs) const
|
|
{
|
|
// Test the number of lines
|
|
if (this->count_lines() != rhs.count_lines())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return records_equal(*this, rhs, true);
|
|
}
|
|
|
|
bool pgsRecord::operator!=(const pgsRecord &rhs) const
|
|
{
|
|
return !(*this == rhs);
|
|
}
|
|
|
|
bool pgsRecord::operator<(const pgsRecord &rhs) const
|
|
{
|
|
// Test the number of lines
|
|
if (this->count_lines() >= rhs.count_lines())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return records_equal(*this, rhs, true);
|
|
}
|
|
|
|
bool pgsRecord::operator>(const pgsRecord &rhs) const
|
|
{
|
|
return (rhs < *this);
|
|
}
|
|
|
|
bool pgsRecord::operator<=(const pgsRecord &rhs) const
|
|
{
|
|
// Test the number of lines
|
|
if (this->count_lines() > rhs.count_lines())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return records_equal(*this, rhs, true);
|
|
}
|
|
|
|
bool pgsRecord::operator>=(const pgsRecord &rhs) const
|
|
{
|
|
return (rhs <= *this);
|
|
}
|
|
|
|
bool pgsRecord::almost_equal(const pgsRecord &rhs) const
|
|
{
|
|
// Test the number of lines
|
|
if (this->count_lines() != rhs.count_lines())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return records_equal(*this, rhs, false);
|
|
}
|
|
|
|
bool pgsRecord::records_equal(const pgsRecord &lhs, const pgsRecord &rhs,
|
|
bool case_sensitive) const
|
|
{
|
|
// Test the number of columns
|
|
if (lhs.count_columns() != rhs.count_columns())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Test each line
|
|
wxArrayInt seen;
|
|
for (USHORT i = 0; i < lhs.count_lines(); i++)
|
|
{
|
|
bool result = false;
|
|
|
|
// Test if the line of lhs matches with an unseen line of rhs
|
|
for (USHORT j = 0; result == false && j < rhs.count_lines(); j++)
|
|
{
|
|
int k = wx_static_cast(int, j);
|
|
if (seen.Index(k) == wxNOT_FOUND
|
|
&& lines_equal(lhs.m_record[i], rhs.m_record[j],
|
|
case_sensitive))
|
|
{
|
|
result = true;
|
|
seen.push_back(k);
|
|
}
|
|
else
|
|
{
|
|
// This is not OK... Continue with the next element
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (result == false)
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
return true; // End of the test
|
|
}
|
|
|
|
bool pgsRecord::lines_equal(const pgsVectorRecordLine &lhs,
|
|
const pgsVectorRecordLine &rhs, bool case_sensitive) const
|
|
{
|
|
pgsVarMap vars;
|
|
|
|
// Both lines must have the same number of columns
|
|
if (lhs.GetCount() != rhs.GetCount())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Test each element (column) of the line
|
|
for (USHORT j = 0; j < wx_static_cast(USHORT, lhs.GetCount()); j++)
|
|
{
|
|
// Test if the two elements are equal
|
|
pgsEqual test(lhs[j]->string().clone(), rhs[j]->string().clone(),
|
|
case_sensitive);
|
|
if (test.eval(vars)->value() == wxT("1"))
|
|
{
|
|
// lhs == rhs: continue
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
// lhs != rhs: lines are not equal
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
pgsOperand pgsRecord::pgs_plus(const pgsVariable &rhs) const
|
|
{
|
|
throw pgsArithmeticException(value(), rhs.value());
|
|
}
|
|
|
|
pgsOperand pgsRecord::pgs_minus(const pgsVariable &rhs) const
|
|
{
|
|
throw pgsArithmeticException(value(), rhs.value());
|
|
}
|
|
|
|
pgsOperand pgsRecord::pgs_times(const pgsVariable &rhs) const
|
|
{
|
|
throw pgsArithmeticException(value(), rhs.value());
|
|
}
|
|
|
|
pgsOperand pgsRecord::pgs_over(const pgsVariable &rhs) const
|
|
{
|
|
throw pgsArithmeticException(value(), rhs.value());
|
|
}
|
|
|
|
pgsOperand pgsRecord::pgs_modulo(const pgsVariable &rhs) const
|
|
{
|
|
throw pgsArithmeticException(value(), rhs.value());
|
|
}
|
|
|
|
pgsOperand pgsRecord::pgs_equal(const pgsVariable &rhs) const
|
|
{
|
|
if (rhs.is_record())
|
|
{
|
|
const pgsRecord &rhs_op = dynamic_cast<const pgsRecord &>(rhs);
|
|
return pnew pgsNumber(*this == rhs_op ? wxT("1") : wxT("0"));
|
|
}
|
|
else
|
|
{
|
|
throw pgsArithmeticException(value(), rhs.value());
|
|
}
|
|
}
|
|
|
|
pgsOperand pgsRecord::pgs_different(const pgsVariable &rhs) const
|
|
{
|
|
if (rhs.is_record())
|
|
{
|
|
const pgsRecord &rhs_op = dynamic_cast<const pgsRecord &>(rhs);
|
|
return pnew pgsNumber(*this != rhs_op ? wxT("1") : wxT("0"));
|
|
}
|
|
else
|
|
{
|
|
throw pgsArithmeticException(value(), rhs.value());
|
|
}
|
|
}
|
|
|
|
pgsOperand pgsRecord::pgs_greater(const pgsVariable &rhs) const
|
|
{
|
|
if (rhs.is_record())
|
|
{
|
|
const pgsRecord &rhs_op = dynamic_cast<const pgsRecord &>(rhs);
|
|
return pnew pgsNumber(*this > rhs_op ? wxT("1") : wxT("0"));
|
|
}
|
|
else
|
|
{
|
|
throw pgsArithmeticException(value(), rhs.value());
|
|
}
|
|
}
|
|
|
|
pgsOperand pgsRecord::pgs_lower(const pgsVariable &rhs) const
|
|
{
|
|
if (rhs.is_record())
|
|
{
|
|
const pgsRecord &rhs_op = dynamic_cast<const pgsRecord &>(rhs);
|
|
return pnew pgsNumber(*this < rhs_op ? wxT("1") : wxT("0"));
|
|
}
|
|
else
|
|
{
|
|
throw pgsArithmeticException(value(), rhs.value());
|
|
}
|
|
}
|
|
|
|
pgsOperand pgsRecord::pgs_lower_equal(const pgsVariable &rhs) const
|
|
{
|
|
if (rhs.is_record())
|
|
{
|
|
const pgsRecord &rhs_op = dynamic_cast<const pgsRecord &>(rhs);
|
|
return pnew pgsNumber(*this <= rhs_op ? wxT("1") : wxT("0"));
|
|
}
|
|
else
|
|
{
|
|
throw pgsArithmeticException(value(), rhs.value());
|
|
}
|
|
}
|
|
|
|
pgsOperand pgsRecord::pgs_greater_equal(const pgsVariable &rhs) const
|
|
{
|
|
if (rhs.is_record())
|
|
{
|
|
const pgsRecord &rhs_op = dynamic_cast<const pgsRecord &>(rhs);
|
|
return pnew pgsNumber(*this >= rhs_op ? wxT("1") : wxT("0"));
|
|
}
|
|
else
|
|
{
|
|
throw pgsArithmeticException(value(), rhs.value());
|
|
}
|
|
}
|
|
|
|
pgsOperand pgsRecord::pgs_not() const
|
|
{
|
|
if (pgs_is_true())
|
|
{
|
|
// A record with no line is false
|
|
return pnew pgsRecord(count_columns());
|
|
}
|
|
else
|
|
{
|
|
// A record with at least one line is true
|
|
pgsRecord *copy = pnew pgsRecord(*this);
|
|
copy->newline();
|
|
return copy; // Insert one line and return the record
|
|
}
|
|
}
|
|
|
|
bool pgsRecord::pgs_is_true() const
|
|
{
|
|
return (count_lines() > 0 ? true : false);
|
|
}
|
|
|
|
pgsOperand pgsRecord::pgs_almost_equal(const pgsVariable &rhs) const
|
|
{
|
|
if (rhs.is_record())
|
|
{
|
|
const pgsRecord &rhs_op = dynamic_cast<const pgsRecord &>(rhs);
|
|
return pnew pgsNumber(this->almost_equal(rhs_op) ? wxT("1") : wxT("0"));
|
|
}
|
|
else
|
|
{
|
|
throw pgsArithmeticException(value(), rhs.value());
|
|
}
|
|
}
|
|
|
|
pgsNumber pgsRecord::number() const
|
|
{
|
|
wxString data = value().Strip(wxString::both);
|
|
|
|
if (data.StartsWith(wxT("(")))
|
|
{
|
|
data = data.Mid(1);
|
|
}
|
|
|
|
if (data.EndsWith(wxT(")")))
|
|
{
|
|
data = data.Mid(0, data.Len() - 1);
|
|
}
|
|
|
|
pgsTypes type = pgsNumber::num_type(data);
|
|
switch (type)
|
|
{
|
|
case pgsTInt:
|
|
return pgsNumber(data, pgsInt);
|
|
case pgsTReal:
|
|
return pgsNumber(data, pgsReal);
|
|
default:
|
|
throw pgsCastException(data, wxT("number"));
|
|
}
|
|
}
|
|
|
|
pgsRecord pgsRecord::record() const
|
|
{
|
|
return pgsRecord(*this);
|
|
}
|
|
|
|
pgsString pgsRecord::string() const
|
|
{
|
|
return pgsString(value());
|
|
}
|