/* -*- mode: c++; indent-tabs-mode: nil -*- */
/*
    QC_SQLStatement.qpp

    Qore Programming Language

    Copyright (C) 2003 - 2023 Qore Technologies, s.r.o.

    Permission is hereby granted, free of charge, to any person obtaining a
    copy of this software and associated documentation files (the "Software"),
    to deal in the Software without restriction, including without limitation
    the rights to use, copy, modify, merge, publish, distribute, sublicense,
    and/or sell copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    DEALINGS IN THE SOFTWARE.

    Note that the Qore library is released under a choice of three open-source
    licenses: MIT (as above), LGPL 2+, or GPL 2+; see README-LICENSE for more
    information.
*/

#include <qore/Qore.h>
#include "qore/intern/QC_SQLStatement.h"
#include "qore/intern/QC_Datasource.h"
#include "qore/intern/QC_DatasourcePool.h"

extern QoreClass* QC_ABSTRACTDATASOURCE;

//! The SQLStatement class provides the most flexibilty for executing SQL on a database server
/** This class allows statements to be executed and result sets to be iteratively returned in all formats supported by %Qore (single row at a time as a hash or blocks of rows as either hashes of lists or lists of hashes). The same flexibility of choosing the output format for result sets also applies to output values from stored procedure or function execution, for example.

    This class does not differentiate between executing select statements, stored procedures, functions, or other SQL code; the transaction lock for Datasource objects and a dedicated connection for DatasourcePool objects is allocated to the object when the connection is necessary, and it is not automatically released by this class. The transaction lock or connection must be manually released by calling SQLStatement::commit() or SQLStatement::rollback() (or the methods with the same name in the parent Datasource or DatasourcePool object).

    Here is an example executing a select statement:
    @code{.py}
{
    SQLStatement stmt(ds);
    # release transaction lock on exit
    on_exit stmt.commit();
    stmt.prepare("select * from table");
    while (stmt.next()) {
        hash<auto> row = stmt.fetchRow();
        do_something(row);
    }
}
    @endcode

    @note
    - Most commands are executed implicitly; for example, in the example above there is no call to SQLStatement::exec() as it is executed implicitly in the initial call to SQLStatement::next().
    - Current column values in query results iterated with SQLStatement::next() as above can also be dereferenced directly from the SQLStatement object by using the column name in lower case as a member name (using SQLStatement::memberGate(), see that method for an example)
    - Query results can also be returned in blocks using SQLStatement::fetchRows() and SQLStatement::fetchColumns() ("rows" and "columns" in this case refer to the output data format; also using these methods there is no need to call SQLStatement::next())
    - When using an SQLStatement object with a DatasourcePool, the statement will be automatically closed when the connection is returned to the pool (for example, by committing or rolling back the transaction).

    The following methods are useful when executing all statements:
    - SQLStatement::prepare()
    - SQLStatement::prepareRaw()
    - SQLStatement::bind()
    - SQLStatement::bindArgs()
    - SQLStatement::bindPlaceholders()
    - SQLStatement::bindPlaceholdersArgs()
    - SQLStatement::bindValues()
    - SQLStatement::bindValuesArgs()
    - SQLStatement::exec()
    - SQLStatement::execArgs()
    - SQLStatement::beginTransaction()
    - SQLStatement::commit()
    - SQLStatement::rollback()
    - SQLStatement::close()
    - SQLStatement::getSQL()
    - SQLStatement::active()

    The following methods are useful when executing select statements:
    - SQLStatement::next()
    - SQLStatement::fetchRow()
    - SQLStatement::fetchRows()
    - SQLStatement::fetchColumns()

    The following methods are useful when executing stored procedures, functions, or other non-select SQL statements:
    - SQLStatement::getOutput()
    - SQLStatement::getOutputRows()

    @note This class is not available with the @ref PO_NO_DATABASE parse option
 */
qclass SQLStatement [dom=DATABASE; arg=QoreSQLStatement* stmt; ns=Qore::SQL; vparent=AbstractSQLStatement];

//! Creates the SQLStatement object based on the given Datasource object that provides the connection to the database
/** This method will throw an exception only if the object passed as an argument uses a driver that does not support the prepared statement interface added in %Qore version 0.8.1.

    @param ds The Datasource object to use for the DB connection for the SQLStatement object

    @par Example:
    @code{.py}
SQLStatement stmt(db);
    @endcode

    @throw SQLSTATEMENT-ERROR the DBI driver for the given object does not support the prepared statement API

    @since %Qore 0.8.1
 */
SQLStatement::constructor(Datasource[ManagedDatasource] ds) {
   ReferenceHolder<ManagedDatasource> ds_holder(ds, xsink);

   if (!ds->getDriver()->hasStatementAPI()) {
      xsink->raiseException("SQLSTATEMENT-ERROR", "DBI driver '%s' does not support the prepared statement API", ds->getDriver()->getName());
      return;
   }

   self->setPrivate(CID_SQLSTATEMENT, new QoreSQLStatement(ds));
}

//! Creates the SQLStatement object based on the given DatasourcePool object that provides the connection to the database
/** This method will throw an exception only if the object passed as an argument uses a driver that does not support the prepared statement interface added in %Qore version 0.8.1.

    @param dsp The DatasourcePool object to use for the DB connection for the SQLStatement object

    @par Example:
    @code{.py}
SQLStatement stmt(db);
    @endcode

    @throw SQLSTATEMENT-ERROR the DBI driver for the given object does not support the prepared statement API

    @since %Qore 0.8.1
 */
SQLStatement::constructor(DatasourcePool[DatasourcePool] dsp) {
   ReferenceHolder<DatasourcePool> dsp_holder(dsp, xsink);

   if (!dsp->getDriver()->hasStatementAPI()) {
      xsink->raiseException("SQLSTATEMENT-ERROR", "DBI driver '%s' does not support the prepared statement API", dsp->getDriver()->getName());
      return;
   }

   self->setPrivate(CID_SQLSTATEMENT, new QoreSQLStatement(dsp));
}

//! Closes the statement if it is open and destroys the object
/**
    @par Example:
    @code{.py}
delete stmt;
    @endcode
 */
SQLStatement::destructor() {
   stmt->close(xsink);
   stmt->deref(xsink);
}

//! Throws an exception; objects of this class cannot be copied
/**
    @throw SQLSTATEMENT-COPY-ERROR SQLStatement objects cannot be copied
 */
SQLStatement::copy() {
   xsink->raiseException("SQLSTATEMENT-COPY-ERROR", "SQLStatement objects cannot be copied");
   //self->setPrivate(CID_SQLSTATEMENT, new SQLStatement(*old));
}

//! Saves an SQL statement that will be prepared and executed later, along with optional arguments
/** The statement is actually only prepared when used for the first time, this is so that SQLStatement objects created with DatasourcePool objects use the DatasourcePool more efficiently, as many drivers require the actual DB API prepare call to be made on the same connection as the connection the statement will be executed on as well.

    @note This method parses the SQL string for placeholders and bind by value tokens (<tt>%%v</tt>); for a version of this method that does not parse the SQL string for placeholders and bind by value tokens, see SQLStatement::prepareRaw().

    @param sql The SQL string to prepare for execution on the DB server

    @par Example:
    @code{.py}
stmt.prepare("select * from table where id = %v");
    @endcode
 */
nothing SQLStatement::prepare(string sql, ...) {
    ReferenceHolder<QoreListNode> l(xsink);

    if (num_args(args) > 1)
        l = args->copyListFrom(1);

    stmt->prepare(*sql, *l, xsink);
}

//! Saves an SQL statement that will be prepared and executed later
/** The statement is actually only prepared when used for the first time, this is so that SQLStatement objects created with DatasourcePool objects use the DatasourcePool more efficiently, as many drivers require the actual DB API prepare call to be made on the same connection as the connection the statement will be executed on as well.

    @note This method does not parse the SQL string for placeholders and bind by value tokens (<tt>%%v</tt>); for a version of this method that does parse the SQL string for placeholders and bind by value tokens, see SQLStatement::prepare().

    @param sql The SQL string to prepare for execution on the DB server

    @par Example:
    @code{.py}
stmt.prepareRaw("select * from table");
    @endcode
 */
nothing SQLStatement::prepareRaw(string sql) {
   stmt->prepareRaw(*sql, xsink);
}

//! Binds placeholder buffer specifications and values to buffers defined in SQLStatement::prepare()
/** If the statement has not previously been prepared with the DB API, it will be implicitly prepared by this method call. This means that this call will cause a connection to be dedicated from a DatasourcePool object or the transaction lock to be grabbed with a Datasource object, depending on the argument to SQLStatement::constructor().

    Arguments to buffer specifications must be given in the same order as declared in the string given to the SQLStatement::prepare() method.

    Any arguments previously bound will be released when this call is made.

    @note You can also bind directly when calling SQLStatement::exec() or SQLStatement::execArgs() as a shortcut as well, in which case it's not necessary to make an extra call to this method.

    @param ... Arguments to placeholder specifications (if required by the underlying DBI driver) and bind by value arguments

    @par Example:
    @code{.py}
stmt.prepare("insert into table (id, name) values (%v, %v)");
foreach hash<auto> h in (l) {
    stmt.bind(h.id, h.name);
    stmt.exec();
}
    @endcode

    @throw SQLSTATEMENT-ERROR No %SQL has been set with SQLStatement::prepare()

    @note Exceptions could be thrown by the DBI driver when the statement is prepared or when attempting to bind the given arguments to buffer specifications; see the relevant DBI driver docs for more information

    @see SQLStatement::bindArgs(), SQLStatement::bindPlaceholders(), SQLStatement::bindPlaceholdersArgs(), SQLStatement::bindValues(), and SQLStatement::bindValuesArgs()
 */
nothing SQLStatement::bind(...) {
   stmt->bind(*args, xsink);
}

//! Binds placeholder buffer specifications and values given as a list in the single argument to the method to buffers defined in SQLStatement::prepare()
/** If the statement has not previously been prepared with the DB API, it will be implicitly prepared by this method call. This means that this call will cause a connection to be dedicated from a DatasourcePool object or the transaction lock to be grabbed with a Datasource object, depending on the argument to SQLStatement::constructor().

    Arguments to buffer specifications must be given in the same order as declared in the string given to the SQLStatement::prepare() method.

    Any arguments previously bound will be released when this call is made.

    @note You can also bind directly when calling SQLStatement::exec() or SQLStatement::execArgs() as a shortcut as well, in which case it's not necessary to make an extra call to this method.

    @param vargs Arguments to placeholder specifications (if required by the underlying DBI driver) and bind by value arguments

    @par Example:
    @code{.py}
stmt.prepare("insert into table (id, name) values (%v, %v)");
foreach hash<auto> h in (l) {
    list<auto> args = (h.id, h.name);
    stmt.bindArgs(args);
    stmt.exec();
}
    @endcode

    @throw SQLSTATEMENT-ERROR No %SQL has been set with SQLStatement::prepare()

    @note Exceptions could be thrown by the DBI driver when the statement is prepared or when attempting to bind the given arguments to buffer specifications; see the relevant DBI driver docs for more information

    @see SQLStatement::bind(), SQLStatement::bindPlaceholders(), SQLStatement::bindPlaceholdersArgs(), SQLStatement::bindValues(), and SQLStatement::bindValuesArgs()
 */
nothing SQLStatement::bindArgs(softlist<auto> vargs) {
   stmt->bind(*vargs, xsink);
}

//! Binds placeholder buffer specifications to buffers defined in SQLStatement::prepare()
/** If the statement has not previously been prepared with the DB API, it will be implicitly prepared by this method call. This means that this call will cause a connection to be dedicated from a DatasourcePool object or the transaction lock to be grabbed with a Datasource object, depending on the argument to SQLStatement::constructor().

    Arguments to buffer specifications must be given in the same order as declared in the string given to the SQLStatement::prepare() method. Only placeholder buffer specifications will be processed; value buffer specifications will be skipped by this method.

    Any buffer specifications previously defined will be released when this call is made.

    @note You can also bind buffer specifications directly when calling SQLStatement::exec() or SQLStatement::execArgs() as a shortcut as well, in which case it's not necessary to make an extra call to this method.\n\n
    Not all DBI drivers require binding placeholders specification.

    @param ... Arguments to placeholder specifications (if required by the underlying DBI driver)

    @par Example:
    @code{.py}
stmt.prepare("begin select sysdate into :sd from dual", Type::Date); end;
stmt.bindPlaceholders(Type::Date);
date d = stmt.getOutput().sd;
    @endcode

    @throw SQLSTATEMENT-ERROR No %SQL has been set with SQLStatement::prepare()

    @note Exceptions could be thrown by the DBI driver when the statement is prepared or when attempting to bind the given arguments to buffer specifications; see the relevant DBI driver docs for more information

    @see SQLStatement::bind(), SQLStatement::bindArgs(), SQLStatement::bindPlaceholdersArgs(), SQLStatement::bindValues(), and SQLStatement::bindValuesArgs()
 */
nothing SQLStatement::bindPlaceholders(...) {
   stmt->bindPlaceholders(*args, xsink);
}

//! Binds placeholder buffer specifications given as a list in the single argument to the method to buffers defined in SQLStatement::prepare()
/** If the statement has not previously been prepared with the DB API, it will be implicitly prepared by this method call. This means that this call will cause a connection to be dedicated from a DatasourcePool object or the transaction lock to be grabbed with a Datasource object, depending on the argument to SQLStatement::constructor().

    Arguments to buffer specifications must be given in the same order as declared in the string given to the SQLStatement::prepare() method. Only placeholder buffer specifications will be processed; value buffer specifications will be skipped by this method.

    Any buffer specifications previously defined will be released when this call is made.

    @note You can also bind buffer specifications directly when calling SQLStatement::exec() or SQLStatement::execArgs() as a shortcut as well, in which case it's not necessary to make an extra call to this method.\n\n
    Not all DBI drivers require binding placeholders specification.

    @param vargs Arguments to placeholder specifications (if required by the underlying DBI driver)

    @par Example:
    @code{.py}
stmt.prepare("begin select sysdate into :sd from dual", Type::Date); end;
list<auto> l = list(Type::Date);
stmt.bindPlaceholdersArgs(l);
date d = stmt.getOutput().sd;
    @endcode

    @throw SQLSTATEMENT-ERROR No %SQL has been set with SQLStatement::prepare()

    @note Exceptions could be thrown by the DBI driver when the statement is prepared or when attempting to bind the given arguments to buffer specifications; see the relevant DBI driver docs for more information

    @see SQLStatement::bind(), SQLStatement::bindArgs(), SQLStatement::bindPlaceholders(), SQLStatement::bindValues(), and SQLStatement::bindValuesArgs()
 */
nothing SQLStatement::bindPlaceholdersArgs(softlist<auto> vargs) {
   stmt->bindPlaceholders(*vargs, xsink);
}

//! Binds values to value buffer specifications to buffers defined in SQLStatement::prepare()
/** If the statement has not previously been prepared with the DB API, it will be implicitly prepared by this method call. This means that this call will cause a connection to be dedicated from a DatasourcePool object or the transaction lock to be grabbed with a Datasource object, depending on the argument to SQLStatement::constructor().

    Arguments to buffer specifications must be given in the same order as declared in the string given to the SQLStatement::prepare() method.

    Any values previously bound will be released when this call is made.

    @note You can also bind directly when calling SQLStatement::exec() or SQLStatement::execArgs() as a shortcut as well, in which case it's not necessary to make an extra call to this method.

    @param ... Arguments to bind by value arguments

    @par Example:
    @code{.py}
stmt.prepare("insert into table (id, name) values (%v, %v)");
foreach hash<auto> h in (l) {
    stmt.bindValues(h.id, h.name);
    stmt.exec();
}
    @endcode

    @throw SQLSTATEMENT-ERROR No %SQL has been set with SQLStatement::prepare()

    @note Exceptions could be thrown by the DBI driver when the statement is prepared or when attempting to bind the given arguments to buffer specifications; see the relevant DBI driver docs for more information

    @see SQLStatement::bind(), SQLStatement::bindArgs(), SQLStatement::bindPlaceholders(), SQLStatement::bindPlaceholdersArgs(), and SQLStatement::bindValuesArgs().
 */
nothing SQLStatement::bindValues(...) {
   stmt->bindValues(*args, xsink);
}

//! Binds values to value buffer specifications given as a list in the single argument to the method to value buffers defined in SQLStatement::prepare()
/** If the statement has not previously been prepared with the DB API, it will be implicitly prepared by this method call. This means that this call will cause a connection to be dedicated from a DatasourcePool object or the transaction lock to be grabbed with a Datasource object, depending on the argument to SQLStatement::constructor().

    Arguments to buffer specifications must be given in the same order as declared in the string given to the SQLStatement::prepare() method.

    Any values previously bound will be released when this call is made.

    @note You can also bind directly when calling SQLStatement::exec() or SQLStatement::execArgs() as a shortcut as well, in which case it's not necessary to make an extra call to this method.

    @param vargs Arguments to bind by value arguments

    @par Example:
    @code{.py}
stmt.prepare("insert into table (id, name) values (%v, %v)");
foreach hash<auto> h in (l) {
    list<auto> args = (h.id, h.name);
    stmt.bindValuesArgs(args);
    stmt.exec();
}
    @endcode

    @throw SQLSTATEMENT-ERROR No %SQL has been set with SQLStatement::prepare()

    @note Exceptions could be thrown by the DBI driver when the statement is prepared or when attempting to bind the given arguments to buffer specifications; see the relevant DBI driver docs for more information
 */
nothing SQLStatement::bindValuesArgs(softlist<auto> vargs) {
   stmt->bindValues(*vargs, xsink);
}

//! Executes the bound statement with any bound buffers, also optionally allows binding placeholder buffer specifications and values to buffers defined in SQLStatement::prepare() before executing the statement
/** If the statement has not previously been prepared with the DB API, it will be implicitly prepared by this method call. This means that this call will cause a connection to be dedicated from a DatasourcePool object or the transaction lock to be grabbed with a Datasource object, depending on the argument to SQLStatement::constructor().

    Optional arguments to buffer specifications must be given in the same order as declared in the string given to the SQLStatement::prepare() method.

    If bind arguments are provided, any arguments previously bound will be released when this call is made.

    After calling this method to execute the statement, to retrieve information about the call or output values bound in the call, call SQLStatement::affectedRows(), SQLStatement::getOutput(), or SQLStatement::getOutputRows() as needed.

    To retrieve rows from a select statement call either SQLStatement::next() and SQLStatement::fetchRow(), or SQLStatement::fetchRows() or SQLStatement::fetchColumns() as needed.

    @param ... Optional arguments to placeholder specifications (if required by the underlying DBI driver) and bind by value arguments can be given in the call to the method; if present, arguments are bound before the statement is executed

    @par Example:
    @code{.py}
stmt.prepare("insert into table (id, name) values (%v, %v)");
foreach hash<auto> h in (l) {
    stmt.exec(h.id, h.name);
}
    @endcode

    @throw SQLSTATEMENT-ERROR No %SQL has been set with SQLStatement::prepare(); the SQLStatement uses a DatasourcePool an the statement was prepared on another connection

    @note Exceptions could be thrown by the DBI driver when the statement is prepared or when attempting to bind the given arguments to buffer specifications or when the statement is executed; see the relevant DBI driver docs for more information

    @see SQLStatement::execArgs()
 */
nothing SQLStatement::exec(...) {
   stmt->exec(args, xsink);
}

//! Executes the bound statement with any bound buffers, also optionally allows binding placeholder buffer specifications and values given as a list in the single argument to the method to buffers defined in SQLStatement::prepare()
/** If the statement has not previously been prepared with the DB API, it will be implicitly prepared by this method call. This means that this call will cause a connection to be dedicated from a DatasourcePool object or the transaction lock to be grabbed with a Datasource object, depending on the argument to SQLStatement::constructor().

    Optional arguments to buffer specifications must be given in the same order as declared in the string given to the SQLStatement::prepare() method.

    If bind arguments are provided, any arguments previously bound will be released when this call is made.

    After calling this method to execute the statement, to retrieve information about the call or output values bound in the call, call SQLStatement::affectedRows(), SQLStatement::getOutput(), or SQLStatement::getOutputRows() as needed.

    To retrieve rows from a select statement call either SQLStatement::next() and SQLStatement::fetchRow(), or SQLStatement::fetchRows() or SQLStatement::fetchColumns() as needed.

    @param vargs Optional arguments to placeholder specifications (if required by the underlying DBI driver) and bind by value arguments can be given in the call to the method; if present, arguments are bound before the statement is executed

    @par Example:
    @code{.py}
stmt.prepare("insert into table (id, name) values (%v, %v)");
foreach hash<auto> h in (l) {
    list<auto> args = (h.id, h.name);
    stmt.execArgs(args);
}
    @endcode

    @throw SQLSTATEMENT-ERROR No %SQL has been set with SQLStatement::prepare() or SQLStatement::prepareRaw(); the SQLStatement uses a DatasourcePool an the statement was prepared on another connection

    @note Exceptions could be thrown by the DBI driver when the statement is prepared or when attempting to bind the given arguments to buffer specifications or when the statement is executed; see the relevant DBI driver docs for more information

    @see SQLStatement::exec()
 */
nothing SQLStatement::execArgs(softlist<auto> vargs) {
   stmt->exec(vargs, xsink);
}

//! Returns the number of rows affected by the last call to SQLStatement::exec()
/** @return the number of rows affected by the last call to SQLStatement::exec()

    @par Example:
    @code{.py}
int rc = stmt.affectedRows();
    @endcode

    @throw SQLSTATEMENT-ERROR No %SQL has been set with SQLStatement::prepare() or SQLStatement::prepareRaw()

    @note Exceptions could be thrown by the DBI driver when the statement is prepared or when attempting to bind the given arguments to buffer specifications or when the statement is executed; see the relevant DBI driver docs for more information
 */
int SQLStatement::affectedRows() {
   return stmt->affectedRows(xsink);
}

//! Retrieves output buffers as a hash; result sets will be returned as hashes of lists
/** @return Returns a hash of output buffers; result sets will be returned as hashes of lists. Each key in the hash is the same as the name given to the placeholder specification in the call to SQLStatement::prepare() or SQLStatement::prepareRaw()

    @par Example:
    @code{.py}
hash<auto> h = stmt.getOutput();
    @endcode

    @throw SQLSTATEMENT-ERROR No %SQL has been set with SQLStatement::prepare() or SQLStatement::prepareRaw()

    @note Exceptions could be thrown by the DBI driver when the statement is prepared or when attempting to bind the given arguments to buffer specifications or when the statement is executed or when output values are retrieved; see the relevant DBI driver docs for more information
 */
hash<auto> SQLStatement::getOutput() {
   return stmt->getOutput(xsink);
}

//! Retrieves output buffers as a hash; result sets will be returned as lists of hashes
/** @return Retrieves output buffers as a hash; result sets will be returned as lists of hashes. Each key in the hash is the same as the name given to the placeholder specification in the call to SQLStatement::prepare() or SQLStatement::prepareRaw()

    @par Example:
    @code{.py}
hash<auto> h = stmt.getOutputRows();
    @endcode

    @throw SQLSTATEMENT-ERROR No %SQL has been set with SQLStatement::prepare() or SQLStatement::prepareRaw()

    @note Exceptions could be thrown by the DBI driver when the statement is prepared or when attempting to bind the given arguments to buffer specifications or when the statement is executed or when output values are retrieved; see the relevant DBI driver docs for more information
 */
hash<auto> SQLStatement::getOutputRows() {
   return stmt->getOutputRows(xsink);
}

//! Performs an explicit define operation on the SQLStatement
/** It is not encessary to call this method manually; define operations are implicitly executed when needed when retrieving values from a select statement

    @par Example:
    @code{.py}
{
    SQLStatement stmt(ds);
    # release transaction lock on exit
    on_exit stmt.commit();
    stmt.prepare("select * from table");
    stmt.exec();
    stmt.define();
    # note that the SQLStatement::next() would implicitly execute exec() and define()
    while (stmt.next()) {
        hash<auto> row = stmt.fetchRow();
        do_something(row);
    }
}
    @endcode
 */
nothing SQLStatement::define() {
   stmt->define(xsink);
}

//! Closes the statement if it is open, however this method does not release the connection or transaction lock
/**
    @par Example:
    @code{.py}
stmt.close();
    @endcode
 */
nothing SQLStatement::close() {
   stmt->close(xsink);
}

//! Commits the transaction, releases the connection or the transaction lock according to the object used in the SQLStatement::constructor(), and closes the SQLStatement
/**
    @par Example:
    @code{.py}
stmt.commit();
    @endcode

    @note For possible exceptions; see DBI driver docs for the commit() method
 */
nothing SQLStatement::commit() {
   stmt->commit(xsink);
}

//! Closes the SQLStatement, performs a transaction rollback, and releases the connection or the transaction lock according to the object used in the SQLStatement::constructor(), and closes the SQLStatement
/**
    @par Example:
    @code{.py}
stmt.rollback();
    @endcode

    @note For possible exceptions; see DBI driver docs for the rollback() method
 */
nothing SQLStatement::rollback() {
   stmt->rollback(xsink);
}

//! Manually starts a transaction and allocates a connection or grabs the transaction lock according to the object used in the SQLStatement::constructor()
/**
    @par Example:
    @code{.py}
stmt.beginTransaction();
    @endcode
 */
nothing SQLStatement::beginTransaction() {
   stmt->beginTransaction(xsink);
}

//! Increments the row pointer when retrieving rows from a select statement; returns @ref True if there is a row to retrieve, @ref False if not
/** If this method returns True, then call SQLStatement::fetchRow() afterwards to retrieve the row

    @return @ref True if there is a row to retrieve, @ref False if not (no more rows to be retrieved)

    @par Example:
    @code{.py}
while (stmt.next()) {
    hash<auto> h = stmt.fetchRow();
}
    @endcode

    @throw SQLSTATEMENT-ERROR No %SQL has been set with SQLStatement::prepare() or SQLStatement::prepareRaw()

    @note Exceptions could be thrown by the DBI driver when the statement is prepared or when attempting to bind the given arguments to buffer specifications or when the statement is executed; see the relevant DBI driver docs for more information
 */
bool SQLStatement::next() {
   return stmt->next(xsink);
}

//! returns @ref True "True" if the object is currently pointing at a valid element, @ref False "False" if not (use when iterating with SQLStatement::next())
/** @return @ref True "True" if the object is currently pointing at a valid element, @ref False "False" if not

    @par Example:
    @code{.py}
if (i.valid())
    printf("current value: %y\n", i.getValue());
    @endcode
 */
bool SQLStatement::valid() [flags=CONSTANT] {
   return stmt->valid();
}

//! Retrieves the current row as a hash where the keys are the column names and the values are the column values
/** Use with SQLStatement::next() to iterate through the results of a select statement one row at a time

    @return the current row as a hash where the keys are the column names and the values are the column values

    @par Example:
    @code{.py}
while (stmt.next()) {
    hash<auto> h = stmt.fetchRow();
}
    @endcode

    @throw SQLSTATEMENT-ERROR No %SQL has been set with SQLStatement::prepare() or SQLStatement::prepareRaw()

    @note Exceptions could be thrown by the DBI driver when the statement is prepared or when attempting to bind the given arguments to buffer specifications or when the statement is executed or when row values are retrieved; see the relevant DBI driver docs for more information
 */
*hash<auto> SQLStatement::fetchRow() {
   return stmt->fetchRow(xsink);
}

//! Retrieves the current row as a hash where the keys are the column names and the values are the column values
/** Use with SQLStatement::next() to iterate through the results of a select statement one row at a time

    @return the current row as a hash where the keys are the column names and the values are the column values

    @par Example:
    @code{.py}
while (stmt.next()) {
    hash<auto> h = stmt.getValue();
}
    @endcode

    @throw SQLSTATEMENT-ERROR No %SQL has been set with SQLStatement::prepare() or SQLStatement::prepareRaw()

    @note
    - Equivalent to SQLStatement::fetchRow()
    - Exceptions could be thrown by the DBI driver when the statement is prepared or when attempting to bind the given arguments to buffer specifications or when the statement is executed or when row values are retrieved; see the relevant DBI driver docs for more information

    @since %Qore 0.8.5
 */
*hash<auto> SQLStatement::getValue() {
   return stmt->fetchRow(xsink);
}

//! Retrieves a block of rows as a list of hashes with the maximum number of rows determined by the argument passed; automatically advances the row pointer; with this call it is not necessary to call SQLStatement::next()
/** If the argument passed is omitted or less than or equal to zero, then all available rows from the current row position are retrieved, also if fewer rows are available than requested then only the rows available are retrieved.

    If no more rows are available then an empty list is returned.

    @param rows The maximum number of rows to retrieve, if this argument is omitted, negative, or equal to zero, then all available rows from the current row position are retrieved

    @par Example:
    @code{.py}
list<auto> l = stmt.fetchRows(-1);
    @endcode

    @throw SQLSTATEMENT-ERROR No %SQL has been set with SQLStatement::prepare() or SQLStatement::prepareRaw()

    @note
    - There is no need to call SQLStatement::next() when calling this method; the method automatically iterates through the given number of rows
    - Exceptions could be thrown by the DBI driver when the statement is prepared or when attempting to bind the given arguments to buffer specifications or when the statement is executed or when row values are retrieved; see the relevant DBI driver docs for more information
 */
list<auto> SQLStatement::fetchRows(softint rows = -1) {
   return stmt->fetchRows((int)rows, xsink);
}

//! Retrieves a block of rows as a hash of lists with the maximum number of rows determined by the argument passed; automatically advances the row pointer; with this call it is not necessary to call SQLStatement::next().
/** If the argument passed is omitted or less than or equal to zero, then all available rows from the current row position are retrieved, also if fewer rows are available than requested then only the rows available are retrieved.

    @param rows The maximum number of rows to retrieve, if this argument is omitted, negative, or equal to zero, then all available rows from the current row position are retrieved

    @return a hash (giving column names) of lists (giving row values for each column) of data returned; each list will have at most \a rows elements (unless \a rows is negative, in which case all available rows are returned).  If the total number of rows available is less than \a rows (if \a rows is positive), then the last data returned by this method may return short lists.  If no more rows are available, then an empty hash is returned

    @par Example:
    @code{.py}
hash<auto> h = stmt.fetchColumns(-1);
    @endcode

    @throw SQLSTATEMENT-ERROR No %SQL has been set with SQLStatement::prepare() or SQLStatement::prepareRaw()

    @note
    - There is no need to call SQLStatement::next() when calling this method; the method automatically iterates through the given number of rows
    - Exceptions could be thrown by the DBI driver when the statement is prepared or when attempting to bind the given arguments to buffer specifications or when the statement is executed or when row values are retrieved; see the relevant DBI driver docs for more information
 */
hash<auto> SQLStatement::fetchColumns(softint rows = -1) {
   return stmt->fetchColumns((int)rows, xsink);
}

//! Describes columns in the statement result.
/**
    @return a hash with (<i>column_name</i>: <i>description_hash</i>) format, where each <i>description_hash</i> has the following keys:
    - \c "name": (string) the column name
    - \c "type": (integer) the column type code (as returned by <value>::typeCode())
    - \c "maxsize": (integer) the maximum size of the column
    - \c "native_type": (string) the database-specific name of the type
    - \c "internal_id": (integer) the database-specific type code of the type
 */
hash<auto> SQLStatement::describe() {
    return stmt->describe(xsink);
}

//! Returns the current SQL string set with the call to SQLStatement::prepare() or SQLStatement::prepareRaw() or @ref nothing if no SQL has been set
/** @return Returns the current SQL string set with the call to SQLStatement::prepare() or SQLStatement::prepareRaw() or @ref nothing if no SQL has been set

    @par Example:
    @code{.py}
*string sql = stmt.getSQL();
    @endcode
 */
*string SQLStatement::getSQL() {
   return stmt->getSQL(xsink);
}

//! Returns @ref True if the object is currently active and has a connection or transaction lock allocated to it, or @ref False if not
/** @return @ref True if the object is currently active and has a connection or transaction lock allocated to it, or @ref False if not

    @par Example:
    @code{.py}
if (stmt.active())
    stmt.commit();
    @endcode
 */
bool SQLStatement::active() [flags=CONSTANT] {
   return stmt->active();
}

//! This method allows SQLStatement objects to be dereferenced directly as a hash for the current row being iterated, as memberGate methods are called implicitly when an unknown member is accessed from outside the class.
/** @param key the column name for the value to retrieve

    @return the current column value of the given row

    @par Example:
    @code{.py}
SQLStatement stmt(ds);
# release transaction lock on exit
on_exit stmt.commit();
stmt.prepare("select name, birthdate from table");
while (stmt.next()) {
    printf("name: %s birthdate: %y", stmt.name, stmt.birthdate);
}
    @endcode

    @throw SQLSTATEMENT-ERROR No %SQL has been set with SQLStatement::prepare() or SQLStatement::prepareRaw()
    @throw SQLSTATEMENT-ITERATION-ERROR Cannot dereference statement iteration context; make sure and call SQLStatement::next() before trying to dereference the current row being iterated
    @throw ENCODING-CONVERSION-ERROR this error is thrown if the given key cannot be converted to the @ref default_encoding "default character encoding"
    @throw SQLSTATEMENT-COLUMN-ERROR The given column name does not exist in the current row data

    @note Column values can only be dereferenced using the automatic SQLStatement::memberGate() method while iterating a result set with SQLStatement::next(); the hash that will be dereferenced is equivalent to that returned by SQLStatement::fetchRow()

    @since %Qore 0.8.6
 */
auto SQLStatement::memberGate(string key) [flags=RET_VALUE_ONLY] {
   ReferenceHolder<QoreHashNode> h(stmt->fetchRow(xsink), xsink);
   if (*xsink)
      return QoreValue();
   if (!h)
      return xsink->raiseException("SQLSTATEMENT-ITERATION-ERROR", "Cannot dereference statement iteration context to get current '%s' column value; make sure and call SQLStatement::next() before trying to dereference the current row being iterated", key->c_str());

   bool exists = false;
   QoreValue rv = h->getKeyValueExistence(key, exists, xsink);
   if (*xsink)
      return QoreValue();

   if (!exists)
      return xsink->raiseException("SQLSTATEMENT-COLUMN-ERROR", "The given column name ('%s') does not exist in the current row data", key->c_str());

   return rv.refSelf();
}

//! Returns @ref True if the current thread is in a transaction (i.e. holds the transaction lock), @ref False if not
/** @return @ref True if the current thread is in a transaction (i.e. holds the transaction lock), @ref False if not

    @par Example:
    @code{.py}
bool b = stmt.currentThreadInTransaction();
    @endcode

    @since %Qore 0.8.12
 */
bool SQLStatement::currentThreadInTransaction() [flags=CONSTANT] {
   return stmt->currentThreadInTransaction(xsink);
}
