/* * The contents of this file are subject to the Initial * Developer's Public License Version 1.0 (the "License"); * you may not use this file except in compliance with the * License. You may obtain a copy of the License at * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. * * Software distributed under the License is distributed AS IS, * WITHOUT WARRANTY OF ANY KIND, either express or implied. * See the License for the specific language governing rights * and limitations under the License. * * The Original Code was created by Adriano dos Santos Fernandes * for the Firebird Open Source RDBMS project. * * Copyright (c) 2008 Adriano dos Santos Fernandes * and all contributors signed below. * * All Rights Reserved. * Contributor(s): ______________________________________. */ #include "../jrd/PreparedStatement.h" #include "../jrd/ResultSet.h" #include "../jrd/align.h" #include "../jrd/jrd.h" #include "../jrd/req.h" #include "../dsql/dsql.h" #include "../common/classes/auto.h" #include "../dsql/sqlda_pub.h" #include "../dsql/dsql_proto.h" #include "../jrd/mov_proto.h" #include "../jrd/Attachment.h" using namespace Firebird; namespace { class ParamCmp { public: static int greaterThan(const Jrd::dsql_par* p1, const Jrd::dsql_par* p2) { return p1->par_index > p2->par_index; } }; void dscToMetaItem(const dsc* desc, MsgMetadata::Item& item) { item.finished = true; switch (desc->dsc_dtype) { case dtype_text: item.type = SQL_TEXT; item.charSet = desc->dsc_ttype(); item.length = desc->dsc_length; break; case dtype_varying: item.type = SQL_VARYING; item.charSet = desc->dsc_ttype(); fb_assert(desc->dsc_length >= sizeof(USHORT)); item.length = desc->dsc_length - sizeof(USHORT); break; case dtype_short: item.type = SQL_SHORT; item.scale = desc->dsc_scale; item.length = sizeof(SSHORT); break; case dtype_long: item.type = SQL_LONG; item.scale = desc->dsc_scale; item.length = sizeof(SLONG); break; case dtype_quad: item.type = SQL_QUAD; item.scale = desc->dsc_scale; item.length = sizeof(SLONG) * 2; break; case dtype_int64: item.type = SQL_INT64; item.scale = desc->dsc_scale; item.length = sizeof(SINT64); break; case dtype_real: item.type = SQL_FLOAT; item.length = sizeof(float); break; case dtype_double: item.type = SQL_DOUBLE; item.length = sizeof(double); break; case dtype_sql_date: item.type = SQL_TYPE_DATE; item.length = sizeof(SLONG); break; case dtype_sql_time: item.type = SQL_TYPE_TIME; item.length = sizeof(SLONG); break; case dtype_timestamp: item.type = SQL_TIMESTAMP; item.length = sizeof(SLONG) * 2; break; case dtype_array: item.type = SQL_ARRAY; item.length = sizeof(ISC_QUAD); break; case dtype_blob: item.type = SQL_BLOB; item.length = sizeof(ISC_QUAD); item.subType = desc->dsc_sub_type; item.charSet = desc->getTextType(); break; case dtype_boolean: item.type = SQL_BOOLEAN; item.length = sizeof(UCHAR); break; default: item.finished = false; fb_assert(false); } } } namespace Jrd { // Move data from result set message to user variables. void PreparedStatement::Builder::moveFromResultSet(thread_db* tdbb, ResultSet* rs) const { for (Array::const_iterator i = outputSlots.begin(); i != outputSlots.end(); ++i) { switch (i->type) { case TYPE_SSHORT: *(SSHORT*) i->address = rs->getSmallInt(tdbb, i->number); break; case TYPE_SLONG: *(SLONG*) i->address = rs->getInt(tdbb, i->number); break; case TYPE_SINT64: *(SINT64*) i->address = rs->getBigInt(tdbb, i->number); break; case TYPE_DOUBLE: *(double*) i->address = rs->getDouble(tdbb, i->number); break; case TYPE_STRING: { AbstractString* str = (AbstractString*) i->address; str->replace(0, str->length(), rs->getString(tdbb, i->number)); break; } case TYPE_METANAME: *(MetaName*) i->address = rs->getMetaName(tdbb, i->number); break; default: fb_assert(false); } if (i->specifiedAddress && rs->isNull(i->number)) *i->specifiedAddress = false; } } // Move data from user variables to the request input message. void PreparedStatement::Builder::moveToStatement(thread_db* tdbb, PreparedStatement* stmt) const { for (Array::const_iterator i = inputSlots.begin(); i != inputSlots.end(); ++i) { if (i->specifiedAddress && !*i->specifiedAddress) { stmt->setNull(i->number); continue; } switch (i->type) { case TYPE_SSHORT: stmt->setSmallInt(tdbb, i->number, *(SSHORT*) i->address); break; case TYPE_SLONG: stmt->setInt(tdbb, i->number, *(SLONG*) i->address); break; case TYPE_SINT64: stmt->setBigInt(tdbb, i->number, *(SINT64*) i->address); break; case TYPE_DOUBLE: stmt->setDouble(tdbb, i->number, *(double*) i->address); break; case TYPE_STRING: stmt->setString(tdbb, i->number, *(AbstractString*) i->address); break; case TYPE_METANAME: stmt->setMetaName(tdbb, i->number, *(MetaName*) i->address); break; default: fb_assert(false); } } } PreparedStatement::PreparedStatement(thread_db* tdbb, MemoryPool& pool, Attachment* attachment, jrd_tra* transaction, const string& text, bool isInternalRequest) : PermanentStorage(pool), builder(NULL), inValues(pool), outValues(pool), inMetadata(FB_NEW Firebird::MsgMetadata), outMetadata(FB_NEW Firebird::MsgMetadata), inMessage(pool), outMessage(pool), resultSet(NULL) { init(tdbb, attachment, transaction, text, isInternalRequest); } PreparedStatement::PreparedStatement(thread_db* tdbb, MemoryPool& pool, Attachment* attachment, jrd_tra* transaction, const Builder& aBuilder, bool isInternalRequest) : PermanentStorage(pool), builder(&aBuilder), inValues(pool), outValues(pool), inMetadata(FB_NEW Firebird::MsgMetadata), outMetadata(FB_NEW Firebird::MsgMetadata), inMessage(pool), outMessage(pool), resultSet(NULL) { init(tdbb, attachment, transaction, builder->getText(), isInternalRequest); } PreparedStatement::~PreparedStatement() { thread_db* tdbb = JRD_get_thread_data(); DSQL_free_statement(tdbb, request, DSQL_drop); if (resultSet) resultSet->stmt = NULL; } void PreparedStatement::init(thread_db* tdbb, Attachment* attachment, jrd_tra* transaction, const Firebird::string& text, bool isInternalRequest) { AutoSetRestore autoAttCharset(&attachment->att_charset, (isInternalRequest ? CS_METADATA : attachment->att_charset)); request = NULL; try { const Database& dbb = *tdbb->getDatabase(); const int dialect = isInternalRequest || (dbb.dbb_flags & DBB_DB_SQL_dialect_3) ? SQL_DIALECT_V6 : SQL_DIALECT_V5; request = DSQL_prepare(tdbb, attachment, transaction, text.length(), text.c_str(), dialect, NULL, NULL, isInternalRequest); const DsqlCompiledStatement* statement = request->getStatement(); if (statement->getSendMsg()) parseDsqlMessage(statement->getSendMsg(), inValues, inMetadata, inMessage); if (statement->getReceiveMsg()) parseDsqlMessage(statement->getReceiveMsg(), outValues, outMetadata, outMessage); } catch (const Exception&) { if (request) { DSQL_free_statement(tdbb, request, DSQL_drop); } throw; } } void PreparedStatement::setDesc(thread_db* tdbb, unsigned param, const dsc& value) { fb_assert(param > 0); jrd_req* jrdRequest = getRequest()->req_request; // Setup tdbb info necessary for blobs. AutoSetRestore2 autoRequest( tdbb, &thread_db::getRequest, &thread_db::setRequest, jrdRequest); AutoSetRestore autoRequestTrans(&jrdRequest->req_transaction, tdbb->getTransaction()); MOV_move(tdbb, const_cast(&value), &inValues[(param - 1) * 2]); const dsc* desc = &inValues[(param - 1) * 2 + 1]; fb_assert(desc->dsc_dtype == dtype_short); *reinterpret_cast(desc->dsc_address) = 0; } void PreparedStatement::execute(thread_db* tdbb, jrd_tra* transaction) { fb_assert(resultSet == NULL); if (builder) builder->moveToStatement(tdbb, this); DSQL_execute(tdbb, &transaction, request, inMetadata, inMessage.begin(), NULL, NULL); } void PreparedStatement::open(thread_db* tdbb, jrd_tra* transaction) { fb_assert(resultSet == NULL); if (builder) builder->moveToStatement(tdbb, this); DSQL_open(tdbb, &transaction, request, inMetadata, inMessage.begin(), outMetadata, 0); } ResultSet* PreparedStatement::executeQuery(thread_db* tdbb, jrd_tra* transaction) { fb_assert(resultSet == NULL && request->getStatement()->getReceiveMsg()); if (builder) builder->moveToStatement(tdbb, this); return FB_NEW_POOL(getPool()) ResultSet(tdbb, this, transaction); } unsigned PreparedStatement::executeUpdate(thread_db* tdbb, jrd_tra* transaction) { execute(tdbb, transaction); return getRequest()->req_request->req_records_updated; } int PreparedStatement::getResultCount() const { return outValues.getCount() / 2; } void PreparedStatement::parseDsqlMessage(const dsql_msg* dsqlMsg, Array& values, MsgMetadata* msgMetadata, UCharBuffer& msg) { // hvlad: Parameters in dsqlMsg->msg_parameters almost always linked in descending // order by par_index. The only known exception is EXECUTE BLOCK statement. // To generate correct metadata we must walk params in ascending par_index order. // So store all params in array in an ascending par_index order despite of // order in linked list. // ASF: Input parameters don't come necessarily in ascending or descending order, // so I changed the code to use a SortedArray. // AP: removed assertions for correct parameters order - useless with SortedArray. SortedArray, const dsql_par*, DefaultKeyValue, ParamCmp> params; for (FB_SIZE_T i = 0; i < dsqlMsg->msg_parameters.getCount(); ++i) { dsql_par* par = dsqlMsg->msg_parameters[i]; if (par->par_index) params.add(par); } FB_SIZE_T paramCount = params.getCount(); values.resize(paramCount * 2); msgMetadata->setItemsCount(paramCount); for (FB_SIZE_T i = 0; i < paramCount; ++i) dscToMetaItem(¶ms[i]->par_desc, msgMetadata->getItem(i)); msgMetadata->makeOffsets(); msg.resize(msgMetadata->getMessageLength()); dsc* value = values.begin(); for (FB_SIZE_T i = 0; i < paramCount; ++i) { // value *value = params[i]->par_desc; value->dsc_address = msg.begin() + msgMetadata->getItem(i).offset; ++value; // NULL indicator value->makeShort(0); value->dsc_address = msg.begin() + msgMetadata->getItem(i).nullInd; // set NULL indicator value *((SSHORT*) value->dsc_address) = -1; ++value; } } } // namespace