diff options
Diffstat (limited to 'ndb/src/old_files/client/odbc/handles/HandleStmt.cpp')
-rw-r--r-- | ndb/src/old_files/client/odbc/handles/HandleStmt.cpp | 823 |
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); +} |