summaryrefslogtreecommitdiff
path: root/ndb/src/old_files/client/odbc/handles/HandleStmt.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ndb/src/old_files/client/odbc/handles/HandleStmt.cpp')
-rw-r--r--ndb/src/old_files/client/odbc/handles/HandleStmt.cpp823
1 files changed, 823 insertions, 0 deletions
diff --git a/ndb/src/old_files/client/odbc/handles/HandleStmt.cpp b/ndb/src/old_files/client/odbc/handles/HandleStmt.cpp
new file mode 100644
index 00000000000..d33d33dbd5b
--- /dev/null
+++ b/ndb/src/old_files/client/odbc/handles/HandleStmt.cpp
@@ -0,0 +1,823 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <common/OdbcData.hpp>
+#include <codegen/CodeGen.hpp>
+#include "HandleRoot.hpp"
+#include "HandleDbc.hpp"
+#include "HandleStmt.hpp"
+#include "HandleDesc.hpp"
+
+HandleStmt::HandleStmt(HandleDbc* pDbc) :
+ StmtArea(*pDbc),
+ m_dbc(pDbc),
+ m_attrArea(m_attrSpec)
+{
+ m_attrArea.setHandle(this);
+ for (unsigned i = 0; i <= 1; i++) {
+ for (unsigned u = 0; u <= 4; u++) {
+ m_handleDesc[i][u] = 0;
+ }
+ }
+}
+
+HandleStmt::~HandleStmt()
+{
+}
+
+void
+HandleStmt::ctor(Ctx& ctx)
+{
+ for (unsigned u = 1; u <= 4; u++) {
+ HandleDesc** ppDesc = &m_handleDesc[0][u];
+ m_dbc->sqlAllocDesc(ctx, ppDesc);
+ if (! ctx.ok())
+ return;
+ m_descArea[u] = &(*ppDesc)->descArea();
+ m_descArea[u]->setAlloc(Desc_alloc_auto);
+ m_descArea[u]->setUsage((DescUsage)u);
+ }
+}
+
+void
+HandleStmt::dtor(Ctx& ctx)
+{
+ free(ctx);
+ for (unsigned u = 1; u <= 4; u++) {
+ HandleDesc** ppDesc = &m_handleDesc[0][u];
+ if (*ppDesc != 0)
+ m_dbc->sqlFreeDesc(ctx, *ppDesc);
+ *ppDesc = 0;
+ }
+}
+
+// descriptor handles
+
+HandleDesc*
+HandleStmt::getHandleDesc(Ctx& ctx, DescUsage u) const
+{
+ ctx_assert(1 <= u && u <= 4);
+ if (m_handleDesc[1][u] != 0)
+ return m_handleDesc[1][u];
+ return m_handleDesc[0][u];
+}
+
+void
+HandleStmt::setHandleDesc(Ctx& ctx, DescUsage u, SQLPOINTER handle)
+{
+ ctx_assert(1 <= u && u <= 4);
+ if (handle == SQL_NULL_HDESC) {
+ m_handleDesc[1][u] = 0; // revert to implicit
+ m_descArea[u] = &m_handleDesc[0][u]->descArea();
+ return;
+ }
+ HandleDesc* pDesc = getRoot()->findDesc(handle);
+ if (pDesc == 0) {
+ ctx.pushStatus(Sqlstate::_HY024, Error::Gen, "cannot set %s handle to invalid descriptor handle %x", DescArea::nameUsage(u), (unsigned)handle);
+ return;
+ }
+ if (pDesc == m_handleDesc[0][u]) {
+ m_handleDesc[1][u] = 0; // revert to implicit
+ m_descArea[u] = &m_handleDesc[0][u]->descArea();
+ return;
+ }
+ // XXX should check implicit handles on all statements
+ for (unsigned v = 1; v <= 4; v++) {
+ if (pDesc == m_handleDesc[0][v]) {
+ ctx.pushStatus(Sqlstate::_HY024, Error::Gen, "cannot set %s handle to implicitly allocated %s handle", DescArea::nameUsage(u), DescArea::nameUsage((DescUsage)v));
+ return;
+ }
+ }
+ m_handleDesc[1][u] = pDesc;
+ m_descArea[u] = &m_handleDesc[1][u]->descArea();
+}
+
+// allocate and free handles (no valid case)
+
+void
+HandleStmt::sqlAllocHandle(Ctx& ctx, SQLSMALLINT childType, HandleBase** ppChild)
+{
+ ctx.pushStatus(Sqlstate::_HY092, Error::Gen, "inappropriate handle type");
+}
+
+void
+HandleStmt::sqlFreeHandle(Ctx& ctx, SQLSMALLINT childType, HandleBase* ppChild)
+{
+ ctx.pushStatus(Sqlstate::_HY092, Error::Gen, "inappropriate handle type");
+}
+
+// attributes and info
+
+static bool
+ignore_attr(Ctx& ctx, SQLINTEGER attribute)
+{
+ switch (attribute) {
+ case 1211:
+ case 1227:
+ case 1228:
+ ctx_log2(("ignore unknown ADO.NET stmt attribute %d", (int)attribute));
+ return true;
+ }
+ return false;
+}
+
+void
+HandleStmt::sqlSetStmtAttr(Ctx& ctx, SQLINTEGER attribute, SQLPOINTER value, SQLINTEGER stringLength)
+{
+ if (ignore_attr(ctx, attribute))
+ return;
+ baseSetHandleAttr(ctx, m_attrArea, attribute, value, stringLength);
+}
+
+void
+HandleStmt::sqlGetStmtAttr(Ctx& ctx, SQLINTEGER attribute, SQLPOINTER value, SQLINTEGER bufferLength, SQLINTEGER* stringLength)
+{
+ if (ignore_attr(ctx, attribute))
+ return;
+ baseGetHandleAttr(ctx, m_attrArea, attribute, value, bufferLength, stringLength);
+}
+
+void
+HandleStmt::sqlSetStmtOption(Ctx& ctx, SQLUSMALLINT option, SQLUINTEGER value)
+{
+ if (ignore_attr(ctx, option))
+ return;
+ baseSetHandleOption(ctx, m_attrArea, option, value);
+}
+
+void
+HandleStmt::sqlGetStmtOption(Ctx& ctx, SQLUSMALLINT option, SQLPOINTER value)
+{
+ if (ignore_attr(ctx, option))
+ return;
+ baseGetHandleOption(ctx, m_attrArea, option, value);
+}
+
+void
+HandleStmt::sqlGetTypeInfo(Ctx& ctx, SQLSMALLINT dataType)
+{
+ BaseString text;
+ // odbc$typeinfo is a (possible unordered) view matching SQLGetTypeInfo result set
+ text.append("SELECT * FROM odbc$typeinfo");
+ if (dataType != SQL_ALL_TYPES) {
+ switch (dataType) {
+ case SQL_CHAR:
+ case SQL_VARCHAR:
+ case SQL_BINARY:
+ case SQL_VARBINARY:
+ case SQL_SMALLINT:
+ case SQL_INTEGER:
+ case SQL_BIGINT:
+ case SQL_REAL:
+ case SQL_DOUBLE:
+ break;
+ default:
+ // XXX unsupported vs illegal
+ ctx_log1(("sqlGetTypeInfo: unknown data type %d", (int)dataType));
+ break;
+ }
+ text.appfmt(" WHERE data_type = %d", (int)dataType);
+ }
+ // sort signed before unsigned
+ text.append(" ORDER BY data_type, unsigned_attribute, type_name");
+ sqlExecDirect(ctx, (SQLCHAR*)text.c_str(), text.length());
+}
+
+void
+HandleStmt::sqlTables(Ctx& ctx, SQLCHAR* catalogName, SQLSMALLINT nameLength1, SQLCHAR* schemaName, SQLSMALLINT nameLength2, SQLCHAR* tableName, SQLSMALLINT nameLength3, SQLCHAR* tableType, SQLSMALLINT nameLength4)
+{
+ BaseString text;
+ // odbc$tables is a (possibly unordered) view matching SQLTables result set
+ text.append("SELECT * FROM odbc$tables");
+ SQLUINTEGER metadata_id = SQL_FALSE;
+ sqlGetStmtAttr(ctx, SQL_ATTR_METADATA_ID, (SQLPOINTER)&metadata_id, SQL_IS_POINTER, 0);
+ if (! ctx.ok())
+ return;
+ unsigned count = 0;
+ if (catalogName != 0) {
+ OdbcData data;
+ data.copyin(ctx, OdbcData::Sqlchar, (SQLPOINTER)catalogName, nameLength1);
+ if (! ctx.ok())
+ return;
+ text.append(++count == 1 ? " WHERE" : " AND");
+ if (getOdbcVersion(ctx) == 2)
+ text.appfmt(" table_cat = '%s'", data.sqlchar());
+ else if (metadata_id == SQL_TRUE)
+ text.appfmt(" table_cat = '%s'", data.sqlchar()); // XXX UPPER
+ else
+ text.appfmt(" table_cat LIKE '%s'", data.sqlchar());
+ }
+ if (schemaName != 0) {
+ OdbcData data;
+ data.copyin(ctx, OdbcData::Sqlchar, (SQLPOINTER)schemaName, nameLength2);
+ if (! ctx.ok())
+ return;
+ text.append(++count == 1 ? " WHERE" : " AND");
+ if (metadata_id == SQL_TRUE)
+ text.appfmt(" table_schem = '%s'", data.sqlchar()); // XXX UPPER
+ else
+ text.appfmt(" table_schem LIKE '%s'", data.sqlchar());
+ }
+ if (tableName != 0) {
+ OdbcData data;
+ data.copyin(ctx, OdbcData::Sqlchar, (SQLPOINTER)tableName, nameLength3);
+ if (! ctx.ok())
+ return;
+ text.append(++count == 1 ? " WHERE" : " AND");
+ if (metadata_id == SQL_TRUE)
+ text.appfmt(" table_name = '%s'", data.sqlchar()); // XXX UPPER
+ else
+ text.appfmt(" table_name LIKE '%s'", data.sqlchar());
+ }
+ if (tableType != 0) {
+ OdbcData data;
+ data.copyin(ctx, OdbcData::Sqlchar, (SQLPOINTER)tableType, nameLength4);
+ if (! ctx.ok())
+ return;
+ text.append(++count == 1 ? " WHERE" : " AND");
+ text.appfmt(" table_type IN (%s)", data.sqlchar()); // XXX UPPER, quotes
+ }
+ text.append(" ORDER BY table_type, table_cat, table_schem, table_name");
+ sqlExecDirect(ctx, (SQLCHAR*)text.c_str(), text.length());
+}
+
+void
+HandleStmt::sqlColumns(Ctx& ctx, SQLCHAR* catalogName, SQLSMALLINT nameLength1, SQLCHAR* schemaName, SQLSMALLINT nameLength2, SQLCHAR* tableName, SQLSMALLINT nameLength3, SQLCHAR* columnName, SQLSMALLINT nameLength4)
+{
+ BaseString text;
+ // odbc$columns is a (possibly unordered) view matching SQLColumns result set
+ text.append("SELECT * FROM odbc$columns");
+ SQLUINTEGER metadata_id = SQL_FALSE;
+ sqlGetStmtAttr(ctx, SQL_ATTR_METADATA_ID, (SQLPOINTER)&metadata_id, SQL_IS_POINTER, 0);
+ if (! ctx.ok())
+ return;
+ unsigned count = 0;
+ if (catalogName != 0) {
+ OdbcData data;
+ data.copyin(ctx, OdbcData::Sqlchar, (SQLPOINTER)catalogName, nameLength1);
+ if (! ctx.ok())
+ return;
+ text.append(++count == 1 ? " WHERE" : " AND");
+ if (getOdbcVersion(ctx) == 2)
+ text.appfmt(" table_cat = '%s'", data.sqlchar());
+ else if (metadata_id == SQL_TRUE)
+ text.appfmt(" table_cat = '%s'", data.sqlchar()); // XXX UPPER
+ else
+ text.appfmt(" table_cat LIKE '%s'", data.sqlchar());
+ }
+ if (schemaName != 0) {
+ OdbcData data;
+ data.copyin(ctx, OdbcData::Sqlchar, (SQLPOINTER)schemaName, nameLength2);
+ if (! ctx.ok())
+ return;
+ text.append(++count == 1 ? " WHERE" : " AND");
+ if (metadata_id == SQL_TRUE)
+ text.appfmt(" table_schem = '%s'", data.sqlchar()); // XXX UPPER
+ else
+ text.appfmt(" table_schem LIKE '%s'", data.sqlchar());
+ }
+ if (tableName != 0) {
+ OdbcData data;
+ data.copyin(ctx, OdbcData::Sqlchar, (SQLPOINTER)tableName, nameLength3);
+ if (! ctx.ok())
+ return;
+ text.append(++count == 1 ? " WHERE" : " AND");
+ if (metadata_id == SQL_TRUE)
+ text.appfmt(" table_name = '%s'", data.sqlchar()); // XXX UPPER
+ else
+ text.appfmt(" table_name LIKE '%s'", data.sqlchar());
+ }
+ if (columnName != 0) {
+ OdbcData data;
+ data.copyin(ctx, OdbcData::Sqlchar, (SQLPOINTER)columnName, nameLength4);
+ if (! ctx.ok())
+ return;
+ text.append(++count == 1 ? " WHERE" : " AND");
+ if (metadata_id == SQL_TRUE)
+ text.appfmt(" column_name = '%s'", data.sqlchar()); // XXX UPPER
+ else
+ text.appfmt(" column_name LIKE '%s'", data.sqlchar());
+ }
+ text.append(" ORDER BY table_cat, table_schem, table_name, ordinal_position");
+ sqlExecDirect(ctx, (SQLCHAR*)text.c_str(), text.length());
+}
+
+void
+HandleStmt::sqlPrimaryKeys(Ctx& ctx, SQLCHAR* szCatalogName, SQLSMALLINT cbCatalogName, SQLCHAR* szSchemaName, SQLSMALLINT cbSchemaName, SQLCHAR* szTableName, SQLSMALLINT cbTableName)
+{
+ BaseString text;
+ // odbc$primarykeys is a (possible unordered) view matching SQLPrimaryKeys result set
+ text.append("SELECT * FROM odbc$primarykeys");
+ SQLUINTEGER metadata_id = SQL_FALSE;
+ sqlGetStmtAttr(ctx, SQL_ATTR_METADATA_ID, (SQLPOINTER)&metadata_id, SQL_IS_POINTER, 0);
+ if (! ctx.ok())
+ return;
+ unsigned count = 0;
+ if (szCatalogName != 0) {
+ OdbcData data;
+ data.copyin(ctx, OdbcData::Sqlchar, (SQLPOINTER)szCatalogName, cbCatalogName);
+ if (! ctx.ok())
+ return;
+ text.append(++count == 1 ? " WHERE" : " AND");
+ if (getOdbcVersion(ctx) == 2)
+ text.appfmt(" table_cat = '%s'", data.sqlchar());
+ else if (metadata_id == SQL_TRUE)
+ text.appfmt(" table_cat = '%s'", data.sqlchar()); // XXX UPPER
+ else
+ text.appfmt(" table_cat = '%s'", data.sqlchar()); // no pattern
+ }
+ if (szSchemaName != 0) {
+ OdbcData data;
+ data.copyin(ctx, OdbcData::Sqlchar, (SQLPOINTER)szSchemaName, cbSchemaName);
+ if (! ctx.ok())
+ return;
+ text.append(++count == 1 ? " WHERE" : " AND");
+ if (metadata_id == SQL_TRUE)
+ text.appfmt(" table_schem = '%s'", data.sqlchar()); // XXX UPPER
+ else
+ text.appfmt(" table_schem = '%s'", data.sqlchar()); // no pattern
+ }
+ if (szTableName != 0) {
+ OdbcData data;
+ data.copyin(ctx, OdbcData::Sqlchar, (SQLPOINTER)szTableName, cbTableName);
+ if (! ctx.ok())
+ return;
+ text.append(++count == 1 ? " WHERE" : " AND");
+ if (metadata_id == SQL_TRUE)
+ text.appfmt(" table_name = '%s'", data.sqlchar()); // XXX UPPER
+ else
+ text.appfmt(" table_name = '%s'", data.sqlchar()); // no pattern
+ } else { // no null
+ ctx.pushStatus(Sqlstate::_HY009, Error::Gen, "null table name");
+ return;
+ }
+ text.append(" ORDER BY table_cat, table_schem, table_name, key_seq");
+ sqlExecDirect(ctx, (SQLCHAR*)text.c_str(), text.length());
+}
+
+int
+HandleStmt::getOdbcVersion(Ctx& ctx)
+{
+ return m_dbc->getOdbcVersion(ctx);
+}
+
+// prepare and execute
+
+void
+HandleStmt::sqlPrepare(Ctx& ctx, SQLCHAR* statementText, SQLINTEGER textLength)
+{
+ if (m_state == Open) {
+ ctx.pushStatus(Sqlstate::_24000, Error::Gen, "cursor is open");
+ return;
+ }
+ free(ctx);
+ const char* text = reinterpret_cast<char*>(statementText);
+ if (textLength == SQL_NTS) {
+ m_sqlText.assign(text);
+ } else if (textLength >= 0) {
+ m_sqlText.assign(text, textLength);
+ } else {
+ ctx.pushStatus(Sqlstate::_HY090, Error::Gen, "missing SQL text");
+ return;
+ }
+ if (! useSchemaCon(ctx, true))
+ return;
+ CodeGen codegen(*this);
+ codegen.prepare(ctx);
+ useSchemaCon(ctx, false);
+ if (! ctx.ok()) {
+ free(ctx);
+ return;
+ }
+ ctx_log2(("prepared %s statement", m_stmtInfo.getDesc()));
+ m_state = Prepared;
+}
+
+void
+HandleStmt::sqlExecute(Ctx& ctx)
+{
+ if (m_state == Open) {
+ ctx.pushStatus(Sqlstate::_24000, Error::Gen, "cursor is open");
+ return;
+ }
+ StmtType stmtType = m_stmtInfo.getType();
+ switch (stmtType) {
+ case Stmt_type_DDL:
+ if (! useSchemaCon(ctx, true))
+ return;
+ break;
+ case Stmt_type_query:
+ case Stmt_type_DML:
+ if (! useConnection(ctx, true))
+ return;
+ break;
+ default:
+ ctx_assert(false);
+ break;
+ }
+ CodeGen codegen(*this);
+ codegen.execute(ctx);
+ // valid only after execute says M$ XXX create this diag only on demand
+ setFunction(ctx);
+ if (ctx.getCode() == SQL_NEED_DATA) {
+ m_state = NeedData;
+ return;
+ }
+ ctx_log2(("execute: fetched %u tuples from NDB", (unsigned)m_tuplesFetched));
+ switch (stmtType) {
+ case Stmt_type_DDL:
+ codegen.close(ctx);
+ useSchemaCon(ctx, false);
+ m_state = Prepared;
+ break;
+ case Stmt_type_query:
+ if (! ctx.ok()) {
+ codegen.close(ctx);
+ useConnection(ctx, false);
+ m_state = Prepared;
+ } else {
+ m_state = Open;
+ }
+ break;
+ case Stmt_type_DML:
+ codegen.close(ctx);
+ if (m_dbc->autocommit()) {
+ // even if error
+ m_dbc->sqlEndTran(ctx, SQL_COMMIT);
+ } else {
+ m_dbc->uncommitted(true); // uncommitted changes possible
+ }
+ useConnection(ctx, false);
+ m_state = Prepared;
+ break;
+ default:
+ ctx_assert(false);
+ break;
+ }
+}
+
+void
+HandleStmt::sqlExecDirect(Ctx& ctx, SQLCHAR* statementText, SQLINTEGER textLength)
+{
+ sqlPrepare(ctx, statementText, textLength);
+ if (! ctx.ok())
+ return;
+ sqlExecute(ctx);
+}
+
+void
+HandleStmt::sqlFetch(Ctx& ctx)
+{
+ if (m_state != Open) {
+ ctx.pushStatus(Sqlstate::_24000, Error::Gen, "cursor is not open");
+ return;
+ }
+ StmtType stmtType = m_stmtInfo.getType();
+ switch (stmtType) {
+ case Stmt_type_query: {
+ CodeGen codegen(*this);
+ codegen.fetch(ctx);
+ if (! ctx.ok()) {
+ codegen.close(ctx);
+ useConnection(ctx, false);
+ }
+ break;
+ }
+ default:
+ ctx_assert(false);
+ break;
+ }
+}
+
+void
+HandleStmt::sqlRowCount(Ctx& ctx, SQLINTEGER* rowCount)
+{
+ *rowCount = m_rowCount;
+}
+
+void
+HandleStmt::sqlMoreResults(Ctx& ctx)
+{
+ if (m_state == Open) {
+ sqlCloseCursor(ctx);
+ if (! ctx.ok())
+ return;
+ }
+ ctx.setCode(SQL_NO_DATA);
+}
+
+void
+HandleStmt::sqlCancel(Ctx& ctx)
+{
+ if (m_state == NeedData) {
+ CodeGen codegen(*this);
+ codegen.close(ctx);
+ m_state = Prepared;
+ }
+}
+
+void
+HandleStmt::sqlCloseCursor(Ctx& ctx)
+{
+ if (m_state != Open) {
+ ctx.pushStatus(Sqlstate::_24000, Error::Gen, "cursor is not open");
+ return;
+ }
+ ctx_log2(("execute: fetched %u tuples from NDB", (unsigned)m_tuplesFetched));
+ StmtType stmtType = m_stmtInfo.getType();
+ switch (stmtType) {
+ case Stmt_type_query: {
+ CodeGen codegen(*this);
+ codegen.close(ctx);
+ useConnection(ctx, false);
+ m_state = Prepared;
+ m_rowCount = 0;
+ m_tuplesFetched = 0;
+ break;
+ }
+ default:
+ ctx_assert(false);
+ break;
+ }
+}
+
+void
+HandleStmt::sqlGetCursorName(Ctx& ctx, SQLCHAR* cursorName, SQLSMALLINT bufferLength, SQLSMALLINT* nameLength)
+{
+ OdbcData name("SQL_CUR_DUMMY");
+ name.copyout(ctx, cursorName, bufferLength, 0, nameLength);
+}
+
+void
+HandleStmt::sqlSetCursorName(Ctx& ctx, SQLCHAR* cursorName, SQLSMALLINT nameLength)
+{
+}
+
+// special data access
+
+void
+HandleStmt::sqlGetData(Ctx& ctx, SQLUSMALLINT columnNumber, SQLSMALLINT targetType, SQLPOINTER targetValue, SQLINTEGER bufferLength, SQLINTEGER* strlen_or_Ind)
+{
+ if (m_state != Open) {
+ ctx.pushStatus(Sqlstate::_24000, Error::Gen, "cursor is not open");
+ return;
+ }
+ if (bufferLength < 0) {
+ ctx.pushStatus(Sqlstate::_HY090, Error::Gen, "invalid buffer length %d", (int)bufferLength);
+ return;
+ }
+ CodeGen codegen(*this);
+ codegen.sqlGetData(ctx, columnNumber, targetType, targetValue, bufferLength, strlen_or_Ind);
+}
+
+void
+HandleStmt::sqlParamData(Ctx& ctx, SQLPOINTER* value)
+{
+ if (m_state != NeedData) {
+ ctx.pushStatus(Sqlstate::_HY010, Error::Gen, "not expecting data-at-exec");
+ return;
+ }
+ CodeGen codegen(*this);
+ codegen.sqlParamData(ctx, value);
+ if (! ctx.ok())
+ return;
+ sqlExecute(ctx);
+}
+
+void
+HandleStmt::sqlPutData(Ctx& ctx, SQLPOINTER data, SQLINTEGER strlen_or_Ind)
+{
+ if (m_state != NeedData) {
+ ctx.pushStatus(Sqlstate::_HY010, Error::Gen, "not expecting data-at-exec");
+ return;
+ }
+ CodeGen codegen(*this);
+ codegen.sqlPutData(ctx, data, strlen_or_Ind);
+}
+
+// describe statement
+
+void
+HandleStmt::sqlNumParams(Ctx& ctx, SQLSMALLINT* parameterCountPtr)
+{
+ if (m_state == Free) {
+ ctx.pushStatus(Sqlstate::_HY010, Error::Gen, "statement is not prepared");
+ return;
+ }
+ HandleDesc* ipd = getHandleDesc(ctx, Desc_usage_IPD);
+ ipd->sqlGetDescField(ctx, 0, SQL_DESC_COUNT, static_cast<SQLPOINTER>(parameterCountPtr), -1, 0);
+}
+
+void
+HandleStmt::sqlDescribeParam(Ctx& ctx, SQLUSMALLINT ipar, SQLSMALLINT* pfSqlType, SQLUINTEGER* pcbParamDef, SQLSMALLINT* pibScale, SQLSMALLINT* pfNullable)
+{
+ if (m_state == Free) {
+ ctx.pushStatus(Sqlstate::_HY010, Error::Gen, "statement is not prepared");
+ return;
+ }
+ HandleDesc* ipd = getHandleDesc(ctx, Desc_usage_IPD);
+ ipd->sqlGetDescField(ctx, ipar, SQL_DESC_CONCISE_TYPE, static_cast<SQLPOINTER>(pfSqlType), -1, 0);
+ { // XXX fix it
+ OdbcData data((SQLUINTEGER)0);
+ data.copyout(ctx, (SQLPOINTER)pcbParamDef, -1, 0);
+ }
+ { // XXX fix it
+ OdbcData data((SQLSMALLINT)0);
+ data.copyout(ctx, (SQLPOINTER)pibScale, -1, 0);
+ }
+ ipd->sqlGetDescField(ctx, ipar, SQL_DESC_NULLABLE, static_cast<SQLPOINTER>(pfNullable), -1, 0);
+}
+
+void
+HandleStmt::sqlNumResultCols(Ctx& ctx, SQLSMALLINT* columnCount)
+{
+ if (m_state == Free) {
+ ctx.pushStatus(Sqlstate::_HY010, Error::Gen, "statement is not prepared");
+ return;
+ }
+ HandleDesc* const ird = getHandleDesc(ctx, Desc_usage_IRD);
+ ird->sqlGetDescField(ctx, 0, SQL_DESC_COUNT, static_cast<SQLPOINTER>(columnCount), -1, 0);
+ setFunction(ctx); // unixODBC workaround
+}
+
+void
+HandleStmt::sqlDescribeCol(Ctx& ctx, SQLUSMALLINT columnNumber, SQLCHAR* columnName, SQLSMALLINT bufferLength, SQLSMALLINT* nameLength, SQLSMALLINT* dataType, SQLUINTEGER* columnSize, SQLSMALLINT* decimalDigits, SQLSMALLINT* nullable)
+{
+ if (m_state == Free) {
+ ctx.pushStatus(Sqlstate::_HY010, Error::Gen, "statement is not prepared");
+ return;
+ }
+ HandleDesc* const ird = getHandleDesc(ctx, Desc_usage_IRD);
+ ird->sqlGetDescField(ctx, columnNumber, SQL_DESC_NAME, static_cast<SQLPOINTER>(columnName), bufferLength, 0, nameLength);
+ ird->sqlGetDescField(ctx, columnNumber, SQL_DESC_CONCISE_TYPE, static_cast<SQLPOINTER>(dataType), -1, 0);
+ if (! ctx.ok())
+ return;
+ if (columnSize != 0) {
+ switch (*dataType) {
+ case SQL_CHAR:
+ case SQL_VARCHAR:
+ case SQL_BINARY:
+ case SQL_VARBINARY:
+ ird->sqlGetDescField(ctx, columnNumber, SQL_DESC_LENGTH, static_cast<SQLPOINTER>(columnSize), -1, 0);
+ break;
+ case SQL_SMALLINT:
+ *columnSize = 5;
+ break;
+ case SQL_INTEGER:
+ *columnSize = 10;
+ break;
+ case SQL_BIGINT:
+ *columnSize = 20; // XXX 19 for signed
+ break;
+ case SQL_REAL:
+ *columnSize = 7;
+ break;
+ case SQL_DOUBLE:
+ *columnSize = 15;
+ break;
+ case SQL_TYPE_TIMESTAMP:
+ *columnSize = 30;
+ break;
+ default:
+ *columnSize = 0;
+ break;
+ }
+ }
+ if (decimalDigits != 0) {
+ switch (*dataType) {
+ case SQL_SMALLINT:
+ case SQL_INTEGER:
+ case SQL_BIGINT:
+ *decimalDigits = 0;
+ break;
+ case SQL_TYPE_TIMESTAMP:
+ *decimalDigits = 10;
+ break;
+ default:
+ *decimalDigits = 0;
+ break;
+ }
+ }
+ ird->sqlGetDescField(ctx, columnNumber, SQL_DESC_NULLABLE, static_cast<SQLPOINTER>(nullable), -1, 0);
+}
+
+void
+HandleStmt::sqlColAttribute(Ctx& ctx, SQLUSMALLINT columnNumber, SQLUSMALLINT fieldIdentifier, SQLPOINTER characterAttribute, SQLSMALLINT bufferLength, SQLSMALLINT* stringLength, SQLPOINTER numericAttribute)
+{
+ if (m_state == Free) {
+ ctx.pushStatus(Sqlstate::_HY010, Error::Gen, "statement is not prepared");
+ return;
+ }
+ HandleDesc* const ird = getHandleDesc(ctx, Desc_usage_IRD);
+ ird->sqlColAttribute(ctx, columnNumber, fieldIdentifier, characterAttribute, bufferLength, stringLength, numericAttribute);
+}
+
+void
+HandleStmt::sqlColAttributes(Ctx& ctx, SQLUSMALLINT icol, SQLUSMALLINT fdescType, SQLPOINTER rgbDesc, SQLSMALLINT cbDescMax, SQLSMALLINT* pcbDesc, SQLINTEGER* pfDesc)
+{
+ if (m_state == Free) {
+ ctx.pushStatus(Sqlstate::_HY010, Error::Gen, "statement is nor prepared");
+ return;
+ }
+ HandleDesc* const ird = getHandleDesc(ctx, Desc_usage_IRD);
+ ird->sqlColAttributes(ctx, icol, fdescType, rgbDesc, cbDescMax, pcbDesc, pfDesc);
+}
+
+// descriptor manipulation
+
+void
+HandleStmt::sqlBindCol(Ctx& ctx, SQLUSMALLINT columnNumber, SQLSMALLINT targetType, SQLPOINTER targetValue, SQLINTEGER bufferLength, SQLINTEGER* strlen_or_Ind)
+{
+ HandleDesc* const ard = getHandleDesc(ctx, Desc_usage_ARD);
+ DescArea& desc = ard->descArea();
+ if (desc.getCount() < columnNumber) {
+ desc.setCount(ctx, columnNumber);
+ }
+ DescRec& rec = desc.getRecord(columnNumber);
+ rec.setField(ctx, SQL_DESC_TYPE, targetType);
+ rec.setField(ctx, SQL_DESC_CONCISE_TYPE, targetType);
+ rec.setField(ctx, SQL_DESC_DATA_PTR, targetValue);
+ rec.setField(ctx, SQL_DESC_OCTET_LENGTH, bufferLength);
+ rec.setField(ctx, SQL_DESC_OCTET_LENGTH_PTR, strlen_or_Ind);
+ rec.setField(ctx, SQL_DESC_INDICATOR_PTR, strlen_or_Ind);
+}
+
+void
+HandleStmt::sqlBindParameter(Ctx& ctx, SQLUSMALLINT ipar, SQLSMALLINT fParamType, SQLSMALLINT fCType, SQLSMALLINT fSqlType, SQLUINTEGER cbColDef, SQLSMALLINT ibScale, SQLPOINTER rgbValue, SQLINTEGER cbValueMax, SQLINTEGER* pcbValue)
+{
+ HandleDesc* const ipd = getHandleDesc(ctx, Desc_usage_IPD);
+ HandleDesc* const apd = getHandleDesc(ctx, Desc_usage_APD);
+ DescArea& descIPD = ipd->descArea();
+ DescArea& descAPD = apd->descArea();
+ if (ipar < 1) {
+ ctx.pushStatus(Sqlstate::_07009, Error::Gen, "invalid parameter index %u", (unsigned)ipar);
+ return;
+ }
+ if (descIPD.getCount() < ipar) {
+ descIPD.setCount(ctx, ipar);
+ }
+ if (descAPD.getCount() < ipar) {
+ descAPD.setCount(ctx, ipar);
+ }
+ DescRec& recIPD = descIPD.getRecord(ipar);
+ DescRec& recAPD = descAPD.getRecord(ipar);
+ recIPD.setField(ctx, SQL_DESC_PARAMETER_TYPE, fParamType);
+ recAPD.setField(ctx, SQL_DESC_TYPE, fCType);
+ recAPD.setField(ctx, SQL_DESC_CONCISE_TYPE, fCType);
+ recIPD.setField(ctx, SQL_DESC_TYPE, fSqlType);
+ recIPD.setField(ctx, SQL_DESC_CONCISE_TYPE, fSqlType);
+ switch (fSqlType) {
+ case SQL_CHAR: // XXX not complete
+ case SQL_VARCHAR:
+ case SQL_BINARY:
+ case SQL_VARBINARY:
+ recIPD.setField(ctx, SQL_DESC_LENGTH, cbColDef);
+ break;
+ case SQL_DECIMAL:
+ case SQL_NUMERIC:
+ case SQL_FLOAT:
+ case SQL_REAL:
+ case SQL_DOUBLE:
+ recIPD.setField(ctx, SQL_DESC_PRECISION, cbColDef);
+ break;
+ }
+ switch (fSqlType) {
+ case SQL_TYPE_TIME: // XXX not complete
+ case SQL_TYPE_TIMESTAMP:
+ recIPD.setField(ctx, SQL_DESC_PRECISION, ibScale);
+ break;
+ case SQL_NUMERIC:
+ case SQL_DECIMAL:
+ recIPD.setField(ctx, SQL_DESC_SCALE, ibScale);
+ break;
+ }
+ recAPD.setField(ctx, SQL_DESC_DATA_PTR, rgbValue);
+ recAPD.setField(ctx, SQL_DESC_OCTET_LENGTH, cbValueMax);
+ recAPD.setField(ctx, SQL_DESC_OCTET_LENGTH_PTR, pcbValue);
+ recAPD.setField(ctx, SQL_DESC_INDICATOR_PTR, pcbValue);
+}
+
+void
+HandleStmt::sqlBindParam(Ctx& ctx, SQLUSMALLINT parameterNumber, SQLSMALLINT valueType, SQLSMALLINT parameterType, SQLUINTEGER lengthPrecision, SQLSMALLINT parameterScale, SQLPOINTER parameterValue, SQLINTEGER* strLen_or_Ind)
+{
+ sqlBindParameter(ctx, parameterNumber, SQL_PARAM_INPUT_OUTPUT, valueType, parameterType, lengthPrecision, parameterScale, parameterValue, SQL_SETPARAM_VALUE_MAX, strLen_or_Ind);
+}
+
+void
+HandleStmt::sqlSetParam(Ctx& ctx, SQLUSMALLINT parameterNumber, SQLSMALLINT valueType, SQLSMALLINT parameterType, SQLUINTEGER lengthPrecision, SQLSMALLINT parameterScale, SQLPOINTER parameterValue, SQLINTEGER* strLen_or_Ind)
+{
+ sqlBindParameter(ctx, parameterNumber, SQL_PARAM_INPUT_OUTPUT, valueType, parameterType, lengthPrecision, parameterScale, parameterValue, SQL_SETPARAM_VALUE_MAX, strLen_or_Ind);
+}