diff --git a/.gitignore b/.gitignore index b0a43a1..66f9c96 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ build/* DIST/ *.autosave +srcdoc/ diff --git a/pglab/CrudModel.cpp b/pglab/CrudModel.cpp index 6605d22..e08a832 100644 --- a/pglab/CrudModel.cpp +++ b/pglab/CrudModel.cpp @@ -7,17 +7,19 @@ #include "GlobalIoService.h" #include "SqlFormattingUtils.h" #include "WorkManager.h" +#include "Pgsql_oids.h" #include #include #include #include "Pgsql_oids.h" - +#include "Pgsql_Params.h" #include CrudModel::CrudModel(ASyncWindow *async_window) : m_asyncWindow(async_window) , m_dbConn(*getGlobalAsioIoService()) { + qDebug("CrudModel created"); connect(&m_dbConn, &ASyncDBConnection::onStateChanged, this, &CrudModel::connectionStateChanged); } @@ -26,7 +28,6 @@ CrudModel::~CrudModel() m_dbConn.closeConnection(); } - /* * Strategy * when ordered by primary key, offset and limit work very quickly so we can get away with not loading @@ -61,13 +62,13 @@ QVariant CrudModel::headerData(int section, Qt::Orientation orientation, int rol // Basic functionality: -int CrudModel::rowCount(const QModelIndex &parent) const +int CrudModel::rowCount(const QModelIndex &/*parent*/) const { return m_roData ? m_roData->rows() : 0; } -int CrudModel::columnCount(const QModelIndex &parent) const +int CrudModel::columnCount(const QModelIndex &/*parent*/) const { return m_roData ? m_roData->cols() : 0; } @@ -77,40 +78,82 @@ Oid CrudModel::getType(int column) const return m_roData ? m_roData->type(column) : InvalidOid; } -QVariant CrudModel::getData(const QModelIndex &index) const +CrudModel::Value CrudModel::getData(const QModelIndex &index) const { - QVariant r; + Value value; if (m_roData) { int rij = index.row(); int col = index.column(); - if (m_roData->null(col, rij)) { - r = "null"; - } - else { - Oid o = m_roData->type(col); - auto value = m_roData->get(col, rij); - switch (o) { - case Pgsql::bool_oid: - r = bool(value); //s = (s == "t") ? "TRUE" : "FALSE"; - break; - default: { - QString s = value; - if (s.length() > 256) { - s.truncate(256); - } - r = s; - } + + //Oid o = m_roData->type(col); + // First see if we have buffered editted values that still need saving + boost::optional val = m_pendingRowList.getValue(col, rij); + if (!val) { + // No pending save have a look if we have modified saved data in the modified list + auto find_res = m_modifiedRowList.find(rij); + if (find_res != m_modifiedRowList.end()) { + val = find_res->second.data()[col]; } } + + //Value value; + // If we did not have pending or modified data + if (!val) { + // Then we are going to read the original data. + if (!m_roData->null(col, rij)) { + value = std::string(m_roData->val(col, rij)); + } + } + else { + value = *val; + } } - return r; + return value; } QVariant CrudModel::data(const QModelIndex &index, int role) const { QVariant v; - if (role == Qt::EditRole || role == Qt::DisplayRole) { - v = getData(index); + if (role == Qt::EditRole) { +// int row = index.row(); +// if (m_pendingChanges.needsToSave(row)) { +//// if (savePendingChanges()) +//// startEditingRow(); +// } + auto value = getData(index); + if (value) { +// Oid o = m_roData->type(index.column()); +// if (o == Pgsql::bool_oid) { +// v = *value == "t"; //s = (s == "t") ? "TRUE" : "FALSE"; +// } +// else { + QString s = QString::fromUtf8(value->c_str()); + v = s; +// } + } + + } + else if (role == Qt::DisplayRole) { + auto value = getData(index); + if (value) { + Oid o = m_roData->type(index.column()); + if (o == Pgsql::bool_oid) { + if (value) + v = *value == "t"; //s = (s == "t") ? "TRUE" : "FALSE"; + else + v = "null"; + } + else { + QString s = QString::fromUtf8(value->c_str()); + if (s.length() > 256) { + s.truncate(256); + } + v = s; + } + } + else { + v = "null"; + } } else if (role == Qt::UserRole) { v = getType(index.column()); @@ -176,3 +219,163 @@ Qt::ItemFlags CrudModel::flags(const QModelIndex &) const { return Qt::ItemIsSelectable + Qt::ItemIsEditable + Qt::ItemIsEnabled; } + +bool CrudModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (role == Qt::EditRole) { + int row = index.row(); + int col = index.column(); + //m_pendingRowList.getRow(row); + + Value val; +// Oid o = m_roData->type(index.column()); +// if (o == Pgsql::bool_oid) { +// val = value.Bool ? "t" : "f"; +// } +// else { + std::string s = value.toString().toUtf8().data(); + if (!s.empty()) { + if (s == "''") + s.clear(); + val = s; + } +// } + m_pendingRowList.setValue(col, row, val); + + emit dataChanged(index, index, QVector() << role); + return true; + } + return false; +} + +const CrudModel::ModifiedRow* CrudModel::getModifiedRow(int row) const +{ + auto iter = m_modifiedRowList.find(row); + if (iter == m_modifiedRowList.end()) + return nullptr; + else + return &iter->second; +} + +CrudModel::PKeyValues CrudModel::getPKeyForRow(int row) const +{ + PKeyValues values; + values.reserve(m_primaryKey->fkey.size()); + + auto mod_row = getModifiedRow(row); + if (mod_row){ + for (auto attnum : m_primaryKey->key) { + int col = attnum - 1; // Assume column ordering matches table, also we assume know special columns like oid are shown + values.push_back(*(mod_row->data()[col])); + } + } + else { + for (auto attnum : m_primaryKey->key) { + int col = attnum - 1; // Assume column ordering matches table, also we assume know special columns like oid are shown + values.push_back(m_roData->get(col, row).c_str()); + } + } + + return values; +} + +QString CrudModel::columnName(int col) const +{ + return m_roData->getColName(col); +} + + + +void CrudModel::updateRow(const PendingRow &pending_row) +{ + auto pkey_values = getPKeyForRow(pending_row.row()); + + QString buffer; + QTextStream q(&buffer); + q << "UPDATE "; + q << genFQTableName(*m_database->catalogue(), m_table); + q << " AS d\n SET "; + + auto data = pending_row.data(); + Pgsql::Params params; + if (!data.empty()) { + int param = 0; + for (auto e : data) { + if (param > 0) + q << ","; + + q << quoteIdent(columnName(e.first)) + << "=$" << ++param; + + if (e.second) + params.add(e.second->c_str(), getType(e.first)); + else + params.add(nullptr, getType(e.first)); + } + + q << "\nWHERE "; + int i = 0; + for (auto attnum : m_primaryKey->key) { + int col = attnum - 1; // Assume column ordering matches table, also we assume know special columns like oid are shown + params.add(pkey_values[i].c_str(), getType(col)); + if (i > 0) + q << " AND "; + q << quoteIdent(columnName(col)) << "=$" << ++param; + ++i; + } + q << "\nRETURNING *"; + q.flush(); + + + m_dbConn.send(buffer.toUtf8().data(), params, + [this] (Expected> result, qint64) { + + } ); + } + +} + +bool CrudModel::savePendingChanges() +{ + // need to start async store of changes + + // modified results are only known after updates/inserts have returned the new values + // (new values can be modified by triggers and such things) + + // do we leave panding changes in the list until async task complete? But then we do not know which + // ones we are storing because user can add new ones. + + // We could also clear the pending list and keep a copy in the async task but then display would revert back + // to original values until save has completed. + + // Moving them to modified list doesn't seem a good idea either because we would have to undo the changes + // when the save fails. + + + +// PendingRowList copy = m_pendingRowList; + // + + + for (const auto& row : m_pendingRowList) { +// int row_number = row.first; +// if (row_number >= 0) { +//// update +// } + updateRow(row.second); + + } + m_pendingRowList.clear(); + + return true; +} + +bool CrudModel::submit() +{ + return savePendingChanges(); +} + +void CrudModel::revert() +{ + +} diff --git a/pglab/CrudModel.h b/pglab/CrudModel.h index 53d45ae..bd1d99f 100644 --- a/pglab/CrudModel.h +++ b/pglab/CrudModel.h @@ -8,6 +8,7 @@ #include "Pgsql_Connection.h" #include #include +#include #include #include @@ -25,13 +26,27 @@ class ASyncWindow; * - hide columns (will not be retrieved can greatly speed up things when you disable wide columns) * - can use foreign keys to display dropdown * - * How to load data? - * - 2D array of QVariants won't be efficient - * - 2D array of strings - * - We do know the type of a single column is fixed - * - Keep data in Result and only load data in replacement rows - * std::string has short string optimization so this probably means - * that a two dimensional array of std::string objects could actually be quite efficient! + * + * Need to keep track of changes from the original. Things to track + * - new data being entered for existing row + * - new data for new row + * - current data of modified row + * - current data of new row + * - which rows are deleted + * We need to be able to: + * - determine row count easily + * - find specific row from table easily + * - support resorting (without requerying) + * //- support custom filtering (without requerying) + * + * Keep data as much in original result + * - use redirect list to go from index to actual row in the result + * - use an additional map index by original row number containing changed data + * - might be beneficial to have vector in sync with original list to signal changes from the result + * this could prevent doing a lot of slower searches in the changed row set by only doing this when the direty markes + * has been set vector takes 1 bit per value making it very efficient and fast. We also could put a bit in the mapping + * vector but this probably would double the size of that list taking 32 times the size of vector. + * */ class CrudModel: public QAbstractTableModel { Q_OBJECT @@ -52,13 +67,15 @@ public: virtual QVariant data(const QModelIndex &index, int role) const override; virtual Qt::ItemFlags flags(const QModelIndex &) const override; + virtual bool setData(const QModelIndex &index, const QVariant &value, int role) override; + +public slots: + virtual bool submit() override; + virtual void revert() override; + private: - ASyncWindow * m_asyncWindow; - std::shared_ptr m_database; - PgClass m_table; - boost::optional m_primaryKey; - ASyncDBConnection m_dbConn; - bool callLoadData = false; + using PKeyValues = std::vector; + // using RowData = std::vector; @@ -69,58 +86,157 @@ private: // */ // using RowList = std::vector; - std::shared_ptr m_roData; - void loadData(); - void loadIntoModel(std::shared_ptr data); // std::shared_ptr resultToRowList(std::shared_ptr result); using Value = boost::optional; - /** Remembers the values that have been applies to a row after the original result was retrieved. + /** Used to remember the changes that have been made to rows. + * + * For simplicity it also holds the values for columns that have not been changed. */ class ModifiedRow { public: - + ModifiedRow(int row, const std::vector &values) + : m_row(row), m_values(values) + {} + const auto& data() const { return m_values; } + int row() const { return m_row; } private: + int m_row; std::vector m_values; }; using ModifiedRowList = std::map; - /** Manages the changes for the current row + /** Similar to a modified row but it only stores values for columns that actually have been edited. * */ - class PendingChanges { + class PendingRow { public: + using ValueMap = std::map; + + explicit PendingRow(int row) + : m_row(row) + {} + + const auto& data() const { return m_values; } + + int row() const { return m_row; } + + void setValue(int col, const Value &val) + { + m_values.insert_or_assign(col, val); + } private: - int m_currentRow; - std::vector m_currentPKeyValues; ///< The values to use for updating the row, for new rows this list is empty - std::map m_values; ///< values that need to be applied + int m_row; + ValueMap m_values; }; - using DeletedList = std::vector; // These are the original indexes before there were any modifications - PendingChanges m_pendingChanges; + class PendingRowList { + public: + using Map = std::map; + + PendingRow& getRow(int row) + { + auto iter = m_rows.lower_bound(row); + if (iter != m_rows.end() && iter->first == row) { + return iter->second; + } + else { + return m_rows.insert(iter, {row, PendingRow(row)})->second; + } + } + + void setValue(int col, int row, const Value &value) + { + auto iter = m_rows.find(row); + if (iter == m_rows.end()) { + iter = m_rows.insert({row, PendingRow(row)}).first; + } + iter->second.setValue(col, value); + } + + boost::optional getValue(int col, int row) const + { + auto iter = m_rows.find(row); + if (iter != m_rows.end()) { + auto &r = iter->second; + auto cell = r.data().find(col); + if (cell != r.data().end()) + return cell->second; + + } + return boost::none; + } + + auto begin() { return m_rows.begin(); } + auto end() { return m_rows.end(); } + + void clear() { m_rows.clear(); } + + private: + Map m_rows; + }; + + + + ASyncWindow * m_asyncWindow; + std::shared_ptr m_database; + PgClass m_table; + boost::optional m_primaryKey; + ASyncDBConnection m_dbConn; + bool callLoadData = false; + + std::shared_ptr m_roData; + + PendingRowList m_pendingRowList; + /** Maintains a list of all modified rows. * * The key values are the indexes of the row before any rows were deleted. */ ModifiedRowList m_modifiedRowList; - - // Alternative, vector of rows - class Row { - public: - int resultRow; ///< row index in original result, -1 for new rows - std::vector currentValues; /// - - }; + using RedirectVec = std::vector; + /// In sync with the actual table, used to efficiently find the correct row in the result + RedirectVec m_redirectVector; - QVariant getData(const QModelIndex &index) const; + void loadData(); + void loadIntoModel(std::shared_ptr data); + + Value getData(const QModelIndex &index) const; Oid getType(int column) const; + /// + /// \brief columnName + /// \param col + /// \return + /// + QString columnName(int col) const; + + /// + /// \brief getModifiedRow searches for the specified row in the modified set + /// \param row + /// \return Pointer to the modified element or nullptr if there is no modification + /// + const ModifiedRow* getModifiedRow(int row) const; + + /// + /// \brief getPKeyForRow retrieve the primary key of the specified row + /// + /// This function should not be called when there is no primarykey. (Editing should be disabled anyway in that case) + /// + /// \param row Actual result row not intermeddiat model row + /// \return + /// + PKeyValues getPKeyForRow(int row) const; + + bool savePendingChanges(); + + void updateRow(const PendingRow &pending_row); private slots: void connectionStateChanged(ASyncDBConnection::State state); diff --git a/pglab/CrudTab.cpp b/pglab/CrudTab.cpp index 46a8ba9..bce0fdf 100644 --- a/pglab/CrudTab.cpp +++ b/pglab/CrudTab.cpp @@ -21,6 +21,10 @@ CrudTab::CrudTab(MainWindow *parent) m_crudModel = new CrudModel(parent); ui->tableView->setModel(m_crudModel); + ui->tableView->setSelectionMode(QAbstractItemView::SingleSelection); + //auto selection_model = ui->tableView->selectionModel(); +// connect(ui->tableView->selectionModel(), &QItemSelectionModel::currentRowChanged, this, +// &CrudTab::tableView_currentRowChanged); } CrudTab::~CrudTab() @@ -41,3 +45,8 @@ void CrudTab::setConfig(std::shared_ptr db, const PgClass &table) // highlighter->setTypes(*cat->types()); m_crudModel->setConfig(db, table); } + +//void CrudTab::tableView_currentRowChanged(const QModelIndex ¤t, const QModelIndex &previous) +//{ + +//} diff --git a/pglab/CrudTab.h b/pglab/CrudTab.h index 23cad8d..af9c58f 100644 --- a/pglab/CrudTab.h +++ b/pglab/CrudTab.h @@ -32,6 +32,9 @@ private: PgClass m_table; CrudModel *m_crudModel = nullptr; + +private slots: +// void tableView_currentRowChanged(const QModelIndex ¤t, const QModelIndex &previous); }; #endif // CRUDTAB_H diff --git a/pglablib/PgCatalogTypes.h b/pglablib/PgCatalogTypes.h index 13171ad..c3c3a38 100644 --- a/pglablib/PgCatalogTypes.h +++ b/pglablib/PgCatalogTypes.h @@ -3,8 +3,11 @@ #include #include +#include using AttNumVec = std::vector; +template +using SmallAttNumVec = boost::container::small_vector; using OidVec = std::vector; #endif // PGCATALOGTYPES_H diff --git a/pglablib/PgConstraint.h b/pglablib/PgConstraint.h index 9174736..d770801 100644 --- a/pglablib/PgConstraint.h +++ b/pglablib/PgConstraint.h @@ -65,8 +65,8 @@ public: bool islocal; int32_t inhcount; bool noinherit; - AttNumVec key; // list of constraint columns attnum - AttNumVec fkey; // fkey list of referenced columns + SmallAttNumVec<5> key; // list of constraint columns attnum + SmallAttNumVec<5> fkey; // fkey list of referenced columns OidVec pfeqop; OidVec ppeqop; OidVec ffeqop; diff --git a/pglablib/QueryGenerator.cpp b/pglablib/QueryGenerator.cpp new file mode 100644 index 0000000..51189c4 --- /dev/null +++ b/pglablib/QueryGenerator.cpp @@ -0,0 +1,22 @@ +#include "QueryGenerator.h" +#include "PgDatabaseCatalog.h" +#include "PgClass.h" +#include "PgNamespace.h" +#include "PgNamespaceContainer.h" + +using namespace Querygen; + +QueryGeneratorFactory::QueryGeneratorFactory(std::shared_ptr catalog) + : m_catalog(catalog) +{} + +UpdatePtr QueryGeneratorFactory::update(QString ns, QString table_name, QString alias) +{ + return std::make_shared(ns, table_name, alias); +} + +UpdatePtr QueryGeneratorFactory::update(const PgClass &table_class, QString alias) +{ + //QString nsname = m_catalog->namespaces()->getByKey(table_class.relnamespace_name) + return update(table_class.relnamespace_name, table_class.name, alias); +} diff --git a/pglablib/QueryGenerator.h b/pglablib/QueryGenerator.h new file mode 100644 index 0000000..c83e17c --- /dev/null +++ b/pglablib/QueryGenerator.h @@ -0,0 +1,60 @@ +#ifndef QUERYGENERATOR_H +#define QUERYGENERATOR_H + +#include +#include + +class PgDatabaseCatalog; +class PgClass; + +namespace Querygen { + + +class QueryGeneratorFactory; +using QueryGeneratorFactoryPtr = std::shared_ptr; + +class Update; +using UpdatePtr = std::shared_ptr; + +class ColumnRef; +using ColumnRefPtr = std::shared_ptr; + +class FQTableName { +public: + QString nsName; + QString tableName; +}; + + +class ColumnRef { +public: + virtual QString getFCQS() const = 0; +}; + + class Update { +public: + Update(QString ns, QString tbl, QString alias) + : table{ns, tbl} + {} + +private: + FQTableName table; + QString tableAlias; +}; + +using UpdatePtr = std::shared_ptr; + + +class QueryGeneratorFactory { +public: + QueryGeneratorFactory(std::shared_ptr catalog); + + UpdatePtr update(QString ns, QString table_name, QString alias = QString()); + UpdatePtr update(const PgClass &table_class, QString alias = QString()); +private: + std::shared_ptr m_catalog; +}; + +} // end namespace Querygen + +#endif // QUERYGENERATOR_H diff --git a/pglablib/SqlFormattingUtils.cpp b/pglablib/SqlFormattingUtils.cpp index 96dc529..eb43dc7 100644 --- a/pglablib/SqlFormattingUtils.cpp +++ b/pglablib/SqlFormattingUtils.cpp @@ -94,7 +94,7 @@ private: QString m_str; }; -QString getColumnNameList(const PgDatabaseCatalog &catalog, Oid relid, const AttNumVec &attnums) +QString getColumnNameList(const PgDatabaseCatalog &catalog, Oid relid, const SmallAttNumVec<5> &attnums) { IdentListString result; diff --git a/pglablib/SqlFormattingUtils.h b/pglablib/SqlFormattingUtils.h index 903f314..4379f04 100644 --- a/pglablib/SqlFormattingUtils.h +++ b/pglablib/SqlFormattingUtils.h @@ -8,6 +8,7 @@ class PgConstraint; class PgDatabaseCatalog; bool identNeedsQuotes(QString ident); +QString quoteIdent(QString ident); QString genFQTableName(const PgDatabaseCatalog &catalog, const PgClass &cls); QString getDropConstraintDefinition(const PgDatabaseCatalog &catalog, const PgConstraint &constraint); diff --git a/pglablib/pglablib.pro b/pglablib/pglablib.pro index 394b68a..093c0f8 100644 --- a/pglablib/pglablib.pro +++ b/pglablib/pglablib.pro @@ -51,7 +51,8 @@ SOURCES += \ ParamListModel.cpp \ util.cpp \ SqlFormattingUtils.cpp \ - PgKeywordList.cpp + PgKeywordList.cpp \ + QueryGenerator.cpp HEADERS += \ Pglablib.h \ @@ -80,7 +81,8 @@ HEADERS += \ util.h \ SqlFormattingUtils.h \ PgCatalogTypes.h \ - PgKeywordList.h + PgKeywordList.h \ + QueryGenerator.h unix { target.path = /usr/lib diff --git a/pglablib/util.cpp b/pglablib/util.cpp index ca97004..c7b43ff 100644 --- a/pglablib/util.cpp +++ b/pglablib/util.cpp @@ -62,9 +62,9 @@ void exportTable(const QTableView *view, QTextStream &out) csv.setSeperator('\t'); csv.setQuote('"'); - const int cols = model->columnCount(); - const int rows = model->rowCount(); - for (int row = 0; row < rows; ++row) { + auto cols = model->columnCount(); + auto rows = model->rowCount(); + for (auto row = 0; row < rows; ++row) { for (int col = 0; col < cols; ++col) { auto idx = model->index(row, col); auto display_text = idx.data(Qt::DisplayRole).toString(); diff --git a/pgsql/Pgsql_Params.cpp b/pgsql/Pgsql_Params.cpp index 9245a8f..fe1eaef 100644 --- a/pgsql/Pgsql_Params.cpp +++ b/pgsql/Pgsql_Params.cpp @@ -31,13 +31,16 @@ Params::Params(Params&& rhs) , m_paramValues(std::move(rhs.m_paramValues)) , m_paramLengths(std::move(rhs.m_paramLengths)) , m_paramFormats(std::move(rhs.m_paramFormats)) -{} +{ + rhs.m_paramValues.clear(); // just in case it was copied instead of moved +} Params& Params::operator=(Params&& rhs) { if (&rhs != this) { m_paramTypes = std::move(rhs.m_paramTypes); m_paramValues = std::move(rhs.m_paramValues); + rhs.m_paramValues.clear(); // just in case it was copied instead of moved m_paramLengths = std::move(rhs.m_paramLengths); m_paramFormats = std::move(rhs.m_paramFormats); } @@ -67,14 +70,31 @@ void Params::add(const QString &s, Oid oid) addText(p, oid); } -void Params::addBinary(const char *data, int length, Oid oid) +void Params::add(const char *data, Oid oid) { + char * p = nullptr; + int len = 0; + if (data) { + len = std::strlen(data); + p = new char[len+1]; + std::memcpy(p, data, len); + p[len] = 0; + } + m_paramTypes.push_back(oid); - m_paramValues.push_back(data); - m_paramLengths.push_back(length); - m_paramFormats.push_back(1); + m_paramValues.push_back(p); + m_paramLengths.push_back(len); + m_paramFormats.push_back(0); } +//void Params::addBinary(const char *data, int length, Oid oid) +//{ +// m_paramTypes.push_back(oid); +// m_paramValues.push_back(data); +// m_paramLengths.push_back(length); +// m_paramFormats.push_back(1); +//} + void Params::clear() { m_paramTypes.clear(); @@ -89,9 +109,14 @@ void Params::copyValues(const t_paramValues &r) const int n = m_paramTypes.size(); m_paramValues.reserve(n); for (int i = 0; i < n; ++i) { - const int len = m_paramLengths[i]; - char * p = new char[len]; - std::memcpy(p, r[i], len); - m_paramValues.push_back(p); + if (r[i]) { + const int len = m_paramLengths[i]; + char * p = new char[len+1]; + std::memcpy(p, r[i], len+1); + m_paramValues.push_back(p); + } + else { + m_paramValues.push_back(nullptr); + } } } diff --git a/pgsql/Pgsql_Params.h b/pgsql/Pgsql_Params.h index 950e86d..5a3a801 100644 --- a/pgsql/Pgsql_Params.h +++ b/pgsql/Pgsql_Params.h @@ -18,13 +18,9 @@ namespace Pgsql { ~Params(); - /** \brief Add a parameter to the list. - * - * The class takes ownership of data and will try to delete[] it. - */ - void addText(const char *data, Oid oid=VARCHAROID); void add(const QString &s, Oid oid=VARCHAROID); - void addBinary(const char *data, int length, Oid oid); + void add(const char *data, Oid oid=VARCHAROID); + //void addBinary(const char *data, int length, Oid oid); void clear(); bool empty() const { return m_paramTypes.empty(); } @@ -51,6 +47,12 @@ namespace Pgsql { t_paramValues m_paramValues; std::vector m_paramLengths; ///< postgresql ignores lengths for text parameters but we will it anyway for efficient copying std::vector m_paramFormats; + + /** \brief Add a parameter to the list. + * + * The class takes ownership of data and will try to delete[] it. + */ + void addText(const char *data, Oid oid=VARCHAROID); }; } // end namespace Pgsql