2008-03-13 17:37:20 +01:00
|
|
|
/*
|
|
|
|
* 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 <adrianosf@uol.com.br>
|
|
|
|
* and all contributors signed below.
|
|
|
|
*
|
|
|
|
* All Rights Reserved.
|
|
|
|
* Contributor(s): ______________________________________.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "../jrd/PreparedStatement.h"
|
|
|
|
#include "../jrd/ResultSet.h"
|
|
|
|
#include "../jrd/align.h"
|
2009-12-05 15:21:52 +01:00
|
|
|
#include "../jrd/intl.h"
|
2008-03-13 17:37:20 +01:00
|
|
|
#include "../jrd/jrd.h"
|
|
|
|
#include "../jrd/dsc.h"
|
2009-10-21 02:42:38 +02:00
|
|
|
#include "../jrd/req.h"
|
2008-03-13 17:37:20 +01:00
|
|
|
#include "../dsql/dsql.h"
|
2009-10-21 02:42:38 +02:00
|
|
|
#include "../common/classes/auto.h"
|
2008-03-13 17:37:20 +01:00
|
|
|
#include "../dsql/sqlda_pub.h"
|
|
|
|
#include "../dsql/dsql_proto.h"
|
2009-10-21 02:42:38 +02:00
|
|
|
#include "../jrd/mov_proto.h"
|
2009-12-25 10:55:05 +01:00
|
|
|
#include "../jrd/Attachment.h"
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2008-03-13 17:37:20 +01:00
|
|
|
|
|
|
|
namespace Jrd {
|
|
|
|
|
|
|
|
|
2010-01-02 22:46:11 +01:00
|
|
|
// Move data from result set message to user variables.
|
|
|
|
void PreparedStatement::Builder::moveFromResultSet(thread_db* tdbb, ResultSet* rs) const
|
2009-12-31 18:08:07 +01:00
|
|
|
{
|
2010-01-02 22:46:11 +01:00
|
|
|
for (Array<Slot>::const_iterator i = outputSlots.begin(); i != outputSlots.end(); ++i)
|
2009-12-31 18:08:07 +01:00
|
|
|
{
|
2010-01-02 22:46:11 +01:00
|
|
|
dsc* desc = &rs->getDesc(i->number);
|
2009-12-31 18:08:07 +01:00
|
|
|
|
2010-01-02 22:46:11 +01:00
|
|
|
switch (i->type)
|
2009-12-31 18:08:07 +01:00
|
|
|
{
|
2010-01-02 22:46:11 +01:00
|
|
|
case TYPE_SLONG:
|
|
|
|
*(SLONG*) i->address = MOV_get_long(desc, 0);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TYPE_STRING:
|
|
|
|
{
|
|
|
|
string* str = (string*) i->address;
|
|
|
|
*str = MOV_make_string2(tdbb, desc, CS_NONE);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case TYPE_METANAME:
|
|
|
|
{
|
|
|
|
MetaName* str = (MetaName*) i->address;
|
|
|
|
*str = MOV_make_string2(tdbb, desc, CS_METADATA);
|
|
|
|
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<Slot>::const_iterator i = inputSlots.begin(); i != inputSlots.end(); ++i)
|
|
|
|
{
|
|
|
|
if (i->specifiedAddress && !*i->specifiedAddress)
|
|
|
|
{
|
|
|
|
stmt->setNull(i->number);
|
2009-12-31 18:08:07 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
dsc desc;
|
|
|
|
|
2010-01-02 22:46:11 +01:00
|
|
|
switch (i->type)
|
2009-12-31 18:08:07 +01:00
|
|
|
{
|
|
|
|
case TYPE_SLONG:
|
2010-01-02 22:46:11 +01:00
|
|
|
desc.makeLong(0, (SLONG*) i->address);
|
2009-12-31 18:08:07 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case TYPE_STRING:
|
|
|
|
{
|
2010-01-02 22:46:11 +01:00
|
|
|
string* str = (string*) i->address;
|
2009-12-31 18:08:07 +01:00
|
|
|
desc.makeText(str->length(), CS_NONE, (UCHAR*) str->c_str());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case TYPE_METANAME:
|
|
|
|
{
|
2010-01-02 22:46:11 +01:00
|
|
|
MetaName* str = (MetaName*) i->address;
|
|
|
|
desc.makeText(str->length(), CS_METADATA, (UCHAR*) str->c_str());
|
2009-12-31 18:08:07 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
fb_assert(false);
|
|
|
|
}
|
|
|
|
|
2010-01-02 22:46:11 +01:00
|
|
|
stmt->setDesc(tdbb, i->number, desc);
|
2009-12-31 18:08:07 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-10-31 23:35:48 +01:00
|
|
|
PreparedStatement::PreparedStatement(thread_db* tdbb, MemoryPool& pool,
|
2009-12-05 15:21:52 +01:00
|
|
|
Attachment* attachment, jrd_tra* transaction, const string& text,
|
|
|
|
bool isInternalRequest)
|
2009-10-31 23:35:48 +01:00
|
|
|
: PermanentStorage(pool),
|
2009-12-31 18:08:07 +01:00
|
|
|
builder(NULL),
|
2009-10-21 02:42:38 +02:00
|
|
|
inValues(pool),
|
|
|
|
outValues(pool),
|
|
|
|
inBlr(pool),
|
|
|
|
outBlr(pool),
|
|
|
|
inMessage(pool),
|
|
|
|
outMessage(pool),
|
2008-03-13 17:37:20 +01:00
|
|
|
resultSet(NULL)
|
2009-12-31 18:08:07 +01:00
|
|
|
{
|
|
|
|
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),
|
|
|
|
inBlr(pool),
|
|
|
|
outBlr(pool),
|
|
|
|
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)
|
2008-03-13 17:37:20 +01:00
|
|
|
{
|
2009-12-05 15:21:52 +01:00
|
|
|
AutoSetRestore<SSHORT> autoAttCharset(&attachment->att_charset,
|
|
|
|
(isInternalRequest ? CS_METADATA : attachment->att_charset));
|
|
|
|
|
2008-03-13 17:37:20 +01:00
|
|
|
request = DSQL_allocate_statement(tdbb, attachment);
|
2008-03-14 16:59:12 +01:00
|
|
|
try
|
2008-03-13 17:37:20 +01:00
|
|
|
{
|
2008-04-10 15:06:34 +02:00
|
|
|
const Database& dbb = *tdbb->getDatabase();
|
2010-01-03 19:23:22 +01:00
|
|
|
const int dialect = isInternalRequest || (dbb.dbb_flags & DBB_DB_SQL_dialect_3) ?
|
|
|
|
SQL_DIALECT_V6 : SQL_DIALECT_V5;
|
2008-03-13 17:37:20 +01:00
|
|
|
|
2008-03-14 16:59:12 +01:00
|
|
|
DSQL_prepare(tdbb, transaction, &request, text.length(), text.c_str(), dialect,
|
2009-12-05 15:21:52 +01:00
|
|
|
0, NULL, 0, NULL, isInternalRequest);
|
2008-03-13 17:37:20 +01:00
|
|
|
|
2009-12-21 00:41:48 +01:00
|
|
|
const DsqlCompiledStatement* statement = request->getStatement();
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-22 16:36:10 +01:00
|
|
|
if (statement->getSendMsg())
|
|
|
|
parseDsqlMessage(statement->getSendMsg(), inValues, inBlr, inMessage);
|
2009-12-20 22:01:10 +01:00
|
|
|
|
2009-12-22 16:36:10 +01:00
|
|
|
if (statement->getReceiveMsg())
|
|
|
|
parseDsqlMessage(statement->getReceiveMsg(), outValues, outBlr, outMessage);
|
2008-03-14 16:59:12 +01:00
|
|
|
}
|
2009-10-31 23:35:48 +01:00
|
|
|
catch (const Exception&)
|
2008-03-14 16:59:12 +01:00
|
|
|
{
|
|
|
|
DSQL_free_statement(tdbb, request, DSQL_drop);
|
|
|
|
throw;
|
2008-03-13 17:37:20 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-12-19 16:00:18 +01:00
|
|
|
void PreparedStatement::setDesc(thread_db* tdbb, unsigned param, const dsc& value)
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2009-12-19 16:00:18 +01:00
|
|
|
fb_assert(param > 0);
|
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
// Setup tdbb info necessary for blobs.
|
|
|
|
AutoSetRestore2<jrd_req*, thread_db> autoRequest(
|
|
|
|
tdbb, &thread_db::getRequest, &thread_db::setRequest, getRequest()->req_request);
|
|
|
|
AutoSetRestore<jrd_tra*> autoRequestTrans(&getRequest()->req_request->req_transaction,
|
|
|
|
tdbb->getTransaction());
|
|
|
|
|
|
|
|
MOV_move(tdbb, const_cast<dsc*>(&value), &inValues[(param - 1) * 2]);
|
|
|
|
|
|
|
|
const dsc* desc = &inValues[(param - 1) * 2 + 1];
|
|
|
|
fb_assert(desc->dsc_dtype == dtype_short);
|
|
|
|
*reinterpret_cast<SSHORT*>(desc->dsc_address) = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-03-13 17:37:20 +01:00
|
|
|
void PreparedStatement::execute(thread_db* tdbb, jrd_tra* transaction)
|
|
|
|
{
|
2008-03-15 18:55:27 +01:00
|
|
|
fb_assert(resultSet == NULL);
|
2009-12-31 18:08:07 +01:00
|
|
|
|
|
|
|
if (builder)
|
|
|
|
builder->moveToStatement(tdbb, this);
|
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
DSQL_execute(tdbb, &transaction, request, inBlr.getCount(), inBlr.begin(), 0,
|
|
|
|
inMessage.getCount(), inMessage.begin(), 0, NULL, /*0,*/ 0, NULL);
|
2008-03-13 17:37:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ResultSet* PreparedStatement::executeQuery(thread_db* tdbb, jrd_tra* transaction)
|
|
|
|
{
|
2009-12-22 16:36:10 +01:00
|
|
|
fb_assert(resultSet == NULL && request->getStatement()->getReceiveMsg());
|
2009-12-31 18:08:07 +01:00
|
|
|
|
|
|
|
if (builder)
|
|
|
|
builder->moveToStatement(tdbb, this);
|
|
|
|
|
2008-04-01 17:41:09 +02:00
|
|
|
return FB_NEW(getPool()) ResultSet(tdbb, this, transaction);
|
2008-03-13 17:37:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-12-19 16:00:18 +01:00
|
|
|
unsigned PreparedStatement::executeUpdate(thread_db* tdbb, jrd_tra* transaction)
|
2008-03-13 17:37:20 +01:00
|
|
|
{
|
2009-12-19 16:00:18 +01:00
|
|
|
execute(tdbb, transaction);
|
|
|
|
return request->req_request->req_records_updated;
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-12-19 16:00:18 +01:00
|
|
|
int PreparedStatement::getResultCount() const
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2009-12-19 16:00:18 +01:00
|
|
|
return outValues.getCount() / 2;
|
2008-03-13 17:37:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-12-22 16:36:10 +01:00
|
|
|
void PreparedStatement::parseDsqlMessage(const dsql_msg* dsqlMsg, Array<dsc>& values,
|
2009-10-31 23:35:48 +01:00
|
|
|
UCharBuffer& blr, UCharBuffer& msg)
|
2008-04-09 17:56:49 +02:00
|
|
|
{
|
2009-10-21 02:42:38 +02:00
|
|
|
// hvlad: Parameters in dsqlMsg->msg_parameters almost always linked in descending
|
2008-12-05 02:20:14 +01:00
|
|
|
// order by par_index. The only known exception is EXECUTE BLOCK statement.
|
|
|
|
// To generate correct BLR we must walk params in ascending par_index order.
|
|
|
|
// So store all params in array in an ascending par_index order despite of
|
2008-08-11 00:11:17 +02:00
|
|
|
// order in linked list.
|
2009-10-30 11:50:59 +01:00
|
|
|
// ASF: Input parameters don't come necessarily in ascending or descending order,
|
2009-10-21 02:42:38 +02:00
|
|
|
// so I changed the code to use a SortedArray.
|
|
|
|
|
2009-12-19 16:00:18 +01:00
|
|
|
SortedArray<const dsql_par*, EmptyStorage<const dsql_par*>, const dsql_par*,
|
|
|
|
DefaultKeyValue<const dsql_par*>, ParamCmp> params;
|
2008-04-09 17:56:49 +02:00
|
|
|
|
2009-12-19 19:50:38 +01:00
|
|
|
for (size_t i = 0; i < dsqlMsg->msg_parameters.getCount(); ++i)
|
2008-04-10 15:06:34 +02:00
|
|
|
{
|
2009-12-19 19:50:38 +01:00
|
|
|
dsql_par* par = dsqlMsg->msg_parameters[i];
|
2008-04-09 17:56:49 +02:00
|
|
|
if (par->par_index)
|
2009-10-21 02:42:38 +02:00
|
|
|
params.add(par);
|
2008-04-09 17:56:49 +02:00
|
|
|
}
|
|
|
|
|
2008-04-10 15:06:34 +02:00
|
|
|
size_t msgLength = 0;
|
2009-12-19 16:00:18 +01:00
|
|
|
size_t paramCount = params.getCount();
|
2008-12-05 02:20:14 +01:00
|
|
|
|
2009-12-20 10:19:15 +01:00
|
|
|
for (size_t i = 0; i < paramCount; ++i)
|
2008-04-09 17:56:49 +02:00
|
|
|
{
|
|
|
|
const dsql_par* par = params[i];
|
2009-12-19 16:00:18 +01:00
|
|
|
|
2008-04-10 15:06:34 +02:00
|
|
|
#ifdef DEV_BUILD
|
2008-08-11 00:11:17 +02:00
|
|
|
// Verify that par_index is in ascending order
|
2008-04-10 15:06:34 +02:00
|
|
|
if (i < paramCount - 1)
|
|
|
|
{
|
2008-08-11 00:11:17 +02:00
|
|
|
fb_assert(par->par_index < params[i + 1]->par_index);
|
2008-04-10 15:06:34 +02:00
|
|
|
}
|
|
|
|
#endif
|
2009-12-19 16:00:18 +01:00
|
|
|
|
2008-04-09 17:56:49 +02:00
|
|
|
if (type_alignments[par->par_desc.dsc_dtype])
|
|
|
|
msgLength = FB_ALIGN(msgLength, type_alignments[par->par_desc.dsc_dtype]);
|
|
|
|
msgLength += par->par_desc.dsc_length;
|
2009-12-19 16:00:18 +01:00
|
|
|
|
|
|
|
// NULL flag
|
2008-04-09 17:56:49 +02:00
|
|
|
msgLength = FB_ALIGN(msgLength, type_alignments[dtype_short]);
|
|
|
|
msgLength += sizeof(SSHORT);
|
|
|
|
}
|
|
|
|
|
|
|
|
paramCount *= 2;
|
|
|
|
|
|
|
|
blr.add(blr_version5);
|
|
|
|
blr.add(blr_begin);
|
|
|
|
blr.add(blr_message);
|
|
|
|
blr.add(0);
|
|
|
|
blr.add(paramCount);
|
|
|
|
blr.add(paramCount >> 8);
|
|
|
|
|
|
|
|
values.resize(paramCount);
|
|
|
|
msg.resize(msgLength);
|
|
|
|
|
|
|
|
msgLength = 0;
|
2008-04-10 04:35:30 +02:00
|
|
|
dsc* value = values.begin();
|
|
|
|
|
2009-12-20 10:19:15 +01:00
|
|
|
for (size_t i = 0; i < paramCount / 2; ++i)
|
2008-04-09 17:56:49 +02:00
|
|
|
{
|
|
|
|
const dsql_par* par = params[i];
|
|
|
|
|
|
|
|
if (type_alignments[par->par_desc.dsc_dtype])
|
|
|
|
msgLength = FB_ALIGN(msgLength, type_alignments[par->par_desc.dsc_dtype]);
|
|
|
|
*value = par->par_desc;
|
|
|
|
value->dsc_address = msg.begin() + msgLength;
|
|
|
|
msgLength += par->par_desc.dsc_length;
|
|
|
|
|
|
|
|
generateBlr(value, blr);
|
2009-12-19 16:00:18 +01:00
|
|
|
++value;
|
2008-04-09 17:56:49 +02:00
|
|
|
|
|
|
|
// Calculate the NULL indicator offset
|
|
|
|
if (type_alignments[dtype_short])
|
|
|
|
msgLength = FB_ALIGN(msgLength, type_alignments[dtype_short]);
|
|
|
|
value->makeShort(0);
|
|
|
|
value->dsc_address = msg.begin() + msgLength;
|
|
|
|
// set NULL indicator value
|
2008-04-10 04:35:30 +02:00
|
|
|
*((SSHORT*) value->dsc_address) = -1;
|
2008-04-09 17:56:49 +02:00
|
|
|
msgLength += sizeof(SSHORT);
|
|
|
|
|
|
|
|
// Generate BLR for the NULL indicator
|
|
|
|
generateBlr(value, blr);
|
2009-12-19 16:00:18 +01:00
|
|
|
++value;
|
2008-04-09 17:56:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
blr.add(blr_end);
|
|
|
|
}
|
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-10-31 23:35:48 +01:00
|
|
|
void PreparedStatement::generateBlr(const dsc* desc, UCharBuffer& blr)
|
2008-03-13 17:37:20 +01:00
|
|
|
{
|
|
|
|
USHORT length = 0;
|
|
|
|
|
|
|
|
switch (desc->dsc_dtype)
|
|
|
|
{
|
|
|
|
case dtype_text:
|
|
|
|
blr.add(blr_text2);
|
|
|
|
blr.add(desc->dsc_ttype());
|
|
|
|
blr.add(desc->dsc_ttype() >> 8);
|
|
|
|
length = desc->dsc_length;
|
|
|
|
blr.add(length);
|
|
|
|
blr.add(length >> 8);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case dtype_varying:
|
|
|
|
blr.add(blr_varying2);
|
|
|
|
blr.add(desc->dsc_ttype());
|
|
|
|
blr.add(desc->dsc_ttype() >> 8);
|
|
|
|
fb_assert(desc->dsc_length >= sizeof(USHORT));
|
|
|
|
length = desc->dsc_length - sizeof(USHORT);
|
|
|
|
blr.add(length);
|
|
|
|
blr.add(length >> 8);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case dtype_short:
|
|
|
|
blr.add(blr_short);
|
|
|
|
blr.add(desc->dsc_scale);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case dtype_long:
|
|
|
|
blr.add(blr_long);
|
|
|
|
blr.add(desc->dsc_scale);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case dtype_quad:
|
|
|
|
blr.add(blr_quad);
|
|
|
|
blr.add(desc->dsc_scale);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case dtype_int64:
|
|
|
|
blr.add(blr_int64);
|
|
|
|
blr.add(desc->dsc_scale);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case dtype_real:
|
|
|
|
blr.add(blr_float);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case dtype_double:
|
|
|
|
blr.add(blr_double);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case dtype_sql_date:
|
|
|
|
blr.add(blr_sql_date);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case dtype_sql_time:
|
|
|
|
blr.add(blr_sql_time);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case dtype_timestamp:
|
|
|
|
blr.add(blr_timestamp);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case dtype_array:
|
|
|
|
blr.add(blr_quad);
|
|
|
|
blr.add(0);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case dtype_blob:
|
|
|
|
blr.add(blr_blob2);
|
|
|
|
blr.add(desc->dsc_sub_type);
|
|
|
|
blr.add(desc->dsc_sub_type >> 8);
|
|
|
|
blr.add(desc->getTextType());
|
|
|
|
blr.add(desc->getTextType() >> 8);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
fb_assert(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace
|