DBD-Firebird
view release on metacpan or search on metacpan
*/
#include "Firebird.h"
#include <stdint.h>
#ifndef _MSC_VER
#include <inttypes.h>
#endif
DBISTATE_DECLARE;
#define ERRBUFSIZE 255
#define IB_SQLtimeformat(xxh, format, sv) \
do { \
STRLEN len; \
char *frmt = NULL; \
char *buf = SvPV(sv, len); \
if (len < 2 || len > 30) break; \
Newx(frmt, len + 1, char); \
strcpy(frmt, buf); \
if (format) Safefree(format); \
format = frmt; \
} while (0)
#define IB_alloc_sqlda(sqlda, n) \
do { \
short len = n; \
char *tmp; \
if (sqlda) \
{ \
Safefree(sqlda); \
sqlda = NULL; \
} \
Newxz(tmp, XSQLDA_LENGTH(len), char); \
sqlda = (XSQLDA*)tmp; \
sqlda->sqln = len; \
sqlda->version = SQLDA_OK_VERSION; \
} while (0)
#ifndef is_ascii_string
#warning "Using built-in implementation of is_ascii_string."
#warning "Upgrading perl to 5.12 is suggested."
// for perl before 5.12.0 RC1
// taken straight from the perl source
bool is_ascii_string(const U8 *s, STRLEN len) {
const U8* const send = s + (len ? len : strlen((const char *)s));
const U8* x = s;
for (; x < send; ++x) {
if (!UTF8_IS_INVARIANT(*x))
break;
}
return x == send;
}
#endif
int create_cursor_name(SV *sth, imp_sth_t *imp_sth)
{
ISC_STATUS status[ISC_STATUS_LENGTH];
#define CURSOR_NAME_LEN 22
Newxz(imp_sth->cursor_name, CURSOR_NAME_LEN, char);
snprintf(imp_sth->cursor_name, CURSOR_NAME_LEN, "perl%16.16X", (uint32_t)imp_sth->stmt);
isc_dsql_set_cursor_name(status, &(imp_sth->stmt), imp_sth->cursor_name, 0);
if (ib_error_check(sth, status))
return FALSE;
return TRUE;
}
void maybe_upgrade_to_utf8(imp_dbh_t *imp_dbh, SV *sv) {
if (imp_dbh->ib_enable_utf8) {
U8 *p;
STRLEN len;
p = (U8*)SvPV(sv, len);
if (!is_ascii_string(p, len)
&& is_utf8_string(p, len)) {
SvUTF8_on(sv);
}
}
}
void dbd_init(dbistate_t *dbistate)
{
DBISTATE_INIT;
}
void ib_cleanup_st_prepare (imp_sth_t *imp_sth)
{
FREE_SETNULL(imp_sth->in_sqlda);
FREE_SETNULL(imp_sth->out_sqlda);
FREE_SETNULL(imp_sth->dateformat);
FREE_SETNULL(imp_sth->timeformat);
FREE_SETNULL(imp_sth->timestampformat);
}
void ib_cleanup_st_execute (imp_sth_t *imp_sth)
{
if (imp_sth->in_sqlda)
{
int i;
XSQLVAR *var = imp_sth->in_sqlda->sqlvar;
for (i = 0; i < imp_sth->in_sqlda->sqln; i++, var++)
{
Safefree(var->sqldata);
var->sqldata = NULL;
if (var->sqlind)
*(var->sqlind) = -1; /* isNULL */
}
}
}
/* lower level error handling */
void do_error(SV *h, int rc, char *what)
{
D_imp_xxh(h);
SV *errstr = DBIc_ERRSTR(imp_xxh);
sv_setiv(DBIc_ERR(imp_xxh), (IV)rc);
sv_setpv(errstr, what);
if (ib_error_check(sth, status))
{
ib_cleanup_st_prepare(imp_sth);
return;
}
/* realloc in_sqlda and rebind if not enough XSQLVAR for bind params */
if (imp_sth->in_sqlda->sqld > imp_sth->in_sqlda->sqln)
{
IB_alloc_sqlda(imp_sth->in_sqlda, imp_sth->in_sqlda->sqld);
if (imp_sth->in_sqlda == NULL)
{
do_error(sth, 1, "Fail to reallocate in_slqda");
ib_cleanup_st_prepare(imp_sth);
return;
}
else
{
isc_dsql_describe_bind(status, &(imp_sth->stmt), 1, imp_sth->in_sqlda);
if (ib_error_check(sth, status))
{
ib_cleanup_st_prepare(imp_sth);
return;
}
}
}
DBI_TRACE_imp_xxh(imp_sth, 3, (DBIc_LOGPIO(imp_sth), "dbd_preparse: describe_bind passed.\n"
"dbd_preparse: exit; in_sqlda: sqld: %d, sqln: %d.\n",
imp_sth->in_sqlda->sqld, imp_sth->in_sqlda->sqln));
DBIc_NUM_PARAMS(imp_sth) = imp_sth->in_sqlda->sqld;
}
int dbd_st_prepare(SV *sth, imp_sth_t *imp_sth, char *statement, SV *attribs)
{
D_imp_dbh_from_sth;
ISC_STATUS status[ISC_STATUS_LENGTH];
int i;
short dtype;
static char stmt_info[1];
char info_buffer[20], count_item;
XSQLVAR *var;
DBI_TRACE_imp_xxh(imp_sth, 2, (DBIc_LOGPIO(imp_sth), "Enter dbd_st_prepare\n"));
if (!DBIc_ACTIVE(imp_dbh))
{
do_error(sth, -1, "Database disconnected");
return FALSE;
}
/* init values */
count_item = 0;
imp_sth->count_item = 0;
imp_sth->affected = -1;
imp_sth->in_sqlda = NULL;
imp_sth->out_sqlda = NULL;
imp_sth->cursor_name = NULL;
imp_sth->dateformat = NULL;
imp_sth->timestampformat = NULL;
imp_sth->timeformat = NULL;
/* double linked list */
imp_sth->prev_sth = NULL;
imp_sth->next_sth = NULL;
if (attribs)
{
SV **svp;
if ((svp = DBD_ATTRIB_GET_SVP(attribs, "ib_time_all", 11)) != NULL)
{
IB_SQLtimeformat(sth, imp_sth->dateformat, *svp);
IB_SQLtimeformat(sth, imp_sth->timestampformat, *svp);
IB_SQLtimeformat(sth, imp_sth->timeformat, *svp);
}
if ((svp = DBD_ATTRIB_GET_SVP(attribs, "ib_dateformat", 13)) != NULL)
IB_SQLtimeformat(sth, imp_sth->dateformat, *svp);
if ((svp = DBD_ATTRIB_GET_SVP(attribs, "ib_timestampformat", 18)) != NULL)
IB_SQLtimeformat(sth, imp_sth->timestampformat, *svp);
if ((svp = DBD_ATTRIB_GET_SVP(attribs, "ib_timeformat", 13)) != NULL)
IB_SQLtimeformat(sth, imp_sth->timeformat, *svp);
}
/* allocate 1 XSQLVAR to in_sqlda */
IB_alloc_sqlda(imp_sth->in_sqlda, 1);
if (imp_sth->in_sqlda == NULL)
{
do_error(sth, 2, "Fail to allocate in_sqlda");
return FALSE;
}
/* allocate 1 XSQLVAR to out_sqlda */
IB_alloc_sqlda(imp_sth->out_sqlda, 1);
if (imp_sth->out_sqlda == NULL)
{
do_error(sth, 2, "Fail to allocate out_sqlda");
ib_cleanup_st_prepare(imp_sth);
return FALSE;
}
/* init statement handle */
isc_dsql_alloc_statement2(status, &(imp_dbh->db), &(imp_sth->stmt));
if (ib_error_check(sth, status))
{
ib_cleanup_st_prepare(imp_sth);
return FALSE;
}
DBI_TRACE_imp_xxh(imp_sth, 3, (DBIc_LOGPIO(imp_sth), "dbd_st_prepare: sqldialect: %d.\n", imp_dbh->sqldialect));
if (!imp_dbh->tr)
}
else if (imp_sth->out_sqlda->sqld == 0) /* not a select statement */
{
Safefree(imp_sth->out_sqlda);
imp_sth->out_sqlda = NULL;
}
if (imp_sth->out_sqlda)
{
for (i = 0, var = imp_sth->out_sqlda->sqlvar;
i < imp_sth->out_sqlda->sqld;
i++, var++)
{
dtype = (var->sqltype & ~1);
var->sqlind = NULL;
DBI_TRACE_imp_xxh(imp_sth, 3, (DBIc_LOGPIO(imp_sth), "dbd_st_prepare: field type: %d.\n", dtype));
/* Alloc space for sqldata */
Newx(var->sqldata,
var->sqllen + (dtype == SQL_VARYING ? sizeof(short) : 0),
ISC_SCHAR);
/* Nullable? */
if (var->sqltype & 1)
Newx(var->sqlind, 1, short);
}
}
/* statment is valid -> insert into linked list (at begin) */
imp_sth->next_sth = imp_dbh->first_sth;
if (imp_dbh->first_sth == NULL)
imp_dbh->last_sth = imp_sth;
else
imp_dbh->first_sth->prev_sth = imp_sth;
imp_dbh->first_sth = imp_sth;
DBI_TRACE_imp_xxh(imp_sth, 3, (DBIc_LOGPIO(imp_sth), "dbd_st_prepare: sth inserted into linked list.\n"));
/* tell DBI that we have a real statement handle now */
DBIc_IMPSET_on(imp_sth);
return TRUE;
}
int dbd_st_finish_internal(SV *sth, imp_sth_t *imp_sth, int honour_auto_commit)
{
D_imp_dbh_from_sth;
ISC_STATUS status[ISC_STATUS_LENGTH];
DBI_TRACE_imp_xxh(imp_sth, 2, (DBIc_LOGPIO(imp_sth), "dbd_st_finish\n"));
if (!DBIc_ACTIVE(imp_sth)) /* already finished */
{
DBI_TRACE_imp_xxh(imp_sth, 3, (DBIc_LOGPIO(imp_sth), "dbd_st_finish: nothing to do (not active)\n"));
return TRUE;
}
/* Close the cursor, not drop the statement! */
if (imp_sth->type != isc_info_sql_stmt_exec_procedure) {
isc_dsql_free_statement(status, (isc_stmt_handle *)&(imp_sth->stmt), DSQL_close);
/* Ignore errors when closing already closed cursor (sqlcode -501).
May happen when closing "select * from sample" statement, which was
closed by the server because of a "drop table sample" statement.
There is no point to error-out here, since nothing bad has happened --
the statement is closed, just without we knowing. There is no resource
leak and the user can't and needs not do anything.
*/
if ((status[0] == 1) && (status[1] > 0)) {
long sqlcode = isc_sqlcode(status);
if (sqlcode != -501) {
if (ib_error_check(sth, status))
return FALSE;
}
else
{
DBI_TRACE_imp_xxh(imp_sth, 3, (DBIc_LOGPIO(imp_sth), "dbd_st_finish: ignoring error -501 from isc_dsql_free_statement.\n"));
}
}
DBI_TRACE_imp_xxh(imp_sth, 3, (DBIc_LOGPIO(imp_sth), "dbd_st_finish: isc_dsql_free_statement passed.\n"));
}
/* set statement to inactive - must be before ib_commit_transaction 'cos
commit can call dbd_st_finish function again */
DBIc_ACTIVE_off(imp_sth);
if ( imp_sth->param_values != NULL )
hv_clear(imp_sth->param_values);
/* if AutoCommit on */
if (DBIc_has(imp_dbh, DBIcf_AutoCommit) && honour_auto_commit)
{
DBI_TRACE_imp_xxh(imp_sth, 4, (DBIc_LOGPIO(imp_sth), "dbd_st_finish: Trying to call ib_commit_transaction.\n"));
if (!ib_commit_transaction(sth, imp_dbh))
{
DBI_TRACE_imp_xxh(imp_sth, 4, (DBIc_LOGPIO(imp_sth), "dbd_st_finish: Call ib_commit_transaction finished returned FALSE.\n"));
return FALSE;
}
DBI_TRACE_imp_xxh(imp_sth, 4, (DBIc_LOGPIO(imp_sth), "dbd_st_finish: Call ib_commit_transaction succeeded.\n"));
}
return TRUE;
}
int dbd_st_finish(SV *sth, imp_sth_t *imp_sth)
{
return dbd_st_finish_internal(sth, imp_sth, TRUE);
}
int dbd_st_execute(SV *sth, imp_sth_t *imp_sth)
{
D_imp_dbh_from_sth;
ISC_STATUS status[ISC_STATUS_LENGTH];
int result = -2;
int row_count = 0;
if (DBIc_ACTIVE(imp_sth))
dbd_st_finish_internal( sth, imp_sth, TRUE);
{
DBI_TRACE_imp_xxh(imp_sth, 3, (DBIc_LOGPIO(imp_sth), "dbd_st_execute: calling isc_dsql_execute..\n"));
/* check for valid in_sqlda */
if (!imp_sth->in_sqlda)
return FALSE;
isc_dsql_execute(status, &(imp_dbh->tr), &(imp_sth->stmt),
imp_dbh->sqldialect,
imp_sth->in_sqlda->sqld > 0 ? imp_sth->in_sqlda: NULL);
if (ib_error_check(sth, status))
{
ib_cleanup_st_execute(imp_sth);
/* rollback any active transaction */
if (DBIc_has(imp_dbh, DBIcf_AutoCommit) && imp_dbh->tr)
ib_commit_transaction(sth, imp_dbh);
return result;
}
DBI_TRACE_imp_xxh(imp_sth, 3, (DBIc_LOGPIO(imp_sth), "dbd_st_execute: isc_dsql_execute succeed.\n"));
}
if (imp_sth->count_item)
{
//PerlIO_printf(PerlIO_stderr(), "calculating row count\n");
row_count = ib_rows(sth, &(imp_sth->stmt), imp_sth->count_item);
if (row_count <= -2)
ib_cleanup_st_execute(imp_sth);
else
result = imp_sth->affected = row_count;
}
else if (imp_sth->type == isc_info_sql_stmt_select)
result = row_count = imp_sth->affected = 0;
else
result = -1;
/* Jika AutoCommit On, commit_transaction() (bukan retaining),
* dan reset imp_dbh->tr == 0L
* For SELECT statement, commit_transaction() is called after fetch,
* or within finish()
*/
if (DBIc_has(imp_dbh, DBIcf_AutoCommit)
&& imp_sth->type != isc_info_sql_stmt_select
&& imp_sth->type != isc_info_sql_stmt_select_for_upd
&& imp_sth->type != isc_info_sql_stmt_exec_procedure)
{
DBI_TRACE_imp_xxh(imp_sth, 3, (DBIc_LOGPIO(imp_sth), "dbd_st_execute: calling ib_commit_transaction..\n"));
if (!ib_commit_transaction(sth, imp_dbh))
{
ib_cleanup_st_execute(imp_sth);
return result;
}
DBI_TRACE_imp_xxh(imp_sth, 3, (DBIc_LOGPIO(imp_sth), "dbd_st_execute: ib_commit_transaction succeed.\n"));
}
/* Declare a unique cursor for this query */
if (imp_sth->type == isc_info_sql_stmt_select_for_upd)
{
/* We free the cursor_name buffer in dbd_st_destroy. */
if (!create_cursor_name(sth, imp_sth))
{
ib_cleanup_st_execute(imp_sth);
return result;
}
}
switch (imp_sth->type)
{
case isc_info_sql_stmt_select:
case isc_info_sql_stmt_select_for_upd:
case isc_info_sql_stmt_exec_procedure:
DBIc_NUM_FIELDS(imp_sth) = (imp_sth->out_sqlda)?
imp_sth->out_sqlda->sqld: 0;
DBIc_ACTIVE_on(imp_sth);
break;
}
DBI_TRACE_imp_xxh(imp_sth, 3, (DBIc_LOGPIO(imp_sth), "dbd_st_execute: row count: %d.\n"
"dbd_st_execute: count_item: %d.\n",
row_count, imp_sth->count_item));
return result;
}
unsigned get_charset_bytes_per_char(const ISC_SHORT subtype, SV *sth);
/* from out_sqlda to AV */
AV *dbd_st_fetch(SV *sth, imp_sth_t *imp_sth)
{
D_imp_dbh_from_sth; /* declare imp_dbh from sth */
ISC_STATUS fetch = 0;
ISC_STATUS status[ISC_STATUS_LENGTH];
int chopBlanks; /* chopBlanks ? */
AV *av; /* array buffer */
SV *sv, **svp; /* buffers */
XSQLVAR *var; /* working pointer XSQLVAR */
int i; /* loop */
short dtype;
DBI_TRACE_imp_xxh(imp_sth, 2, (DBIc_LOGPIO(imp_sth), "dbd_st_fetch\n"));
if (!DBIc_ACTIVE(imp_sth))
{
do_error(sth, 0, "no statement executing (perhaps you need to call execute first)\n");
return Nullav;
}
chopBlanks = DBIc_is(imp_sth, DBIcf_ChopBlanks);
av = DBIS->get_fbav(imp_sth);
svp = AvARRAY(av);
/*
* if it's an execute procedure, we've already got the
* output from the isc_dsql_execute2() call in dbd_st_execute().
*/
if (imp_sth->type != isc_info_sql_stmt_exec_procedure)
{
fetch = isc_dsql_fetch(status, &(imp_sth->stmt), imp_dbh->sqldialect,
imp_sth->out_sqlda);
if (ib_error_check(sth, status))
return Nullav;
/*
* Code 100 means we've reached the end of the set
* of rows that the SELECT will return.
*/
DBI_TRACE_imp_xxh(imp_sth, 3, (DBIc_LOGPIO(imp_sth), "dbd_st_fetch: fetch result: %ld\n", fetch));
if (imp_sth->affected < 0)
imp_sth->affected = 0;
if (fetch == 100)
{
/* close the cursor */
isc_dsql_free_statement(status, &(imp_sth->stmt), DSQL_close);
if (ib_error_check(sth, status))
return Nullav;
DBI_TRACE_imp_xxh(imp_sth, 3, (DBIc_LOGPIO(imp_sth), "isc_dsql_free_statement succeed.\n"));
DBIc_ACTIVE_off(imp_sth); /* dbd_st_finish is no longer needed */
/* if AutoCommit on XXX. what to return if fails? */
if (DBIc_has(imp_dbh, DBIcf_AutoCommit))
{
if (!ib_commit_transaction(sth, imp_dbh))
return Nullav;
DBI_TRACE_imp_xxh(imp_sth, 3, (DBIc_LOGPIO(imp_sth), "fetch ends: ib_commit_transaction succeed.\n"));
}
return Nullav;
}
else if (fetch != 0) /* something bad */
{ do_error(sth, 0, "Fetch error");
DBIc_ACTIVE_off(imp_sth);
return Nullav;
}
} /* !exec_procedure */
else
{
/* we only fetch one row for exec procedure */
if (imp_sth->affected)
return Nullav;
}
var = imp_sth->out_sqlda->sqlvar;
for (i = 0; i < imp_sth->out_sqlda->sqld; i++, var++)
{
sv = svp[i];
dtype = var->sqltype & ~1;
if ((var->sqltype & 1) && (*(var->sqlind) == -1))
/* if nullable field */
{
/* isNULL */
SvOK_off(sv);
}
else
{
/*
* Got a non-null field. Got to pass it back to the
* application, which means some datatype dependant code.
*/
switch (dtype)
{
#ifdef SQL_BOOLEAN
case SQL_BOOLEAN:
FB_BOOLEAN b = (*((FB_BOOLEAN *) (var->sqldata)));
#ifdef sv_set_bool
sv_set_bool(sv, b == FB_TRUE);
#else
#ifdef sv_setbool
/* Clean up after ourselves. */
isc_close_blob(status, &blob_handle);
if (ib_error_check(sth, status))
return FALSE;
if ( blob_type == isc_blob_text
|| var->sqlsubtype == isc_blob_text )
maybe_upgrade_to_utf8(imp_dbh, sv);
break;
}
case SQL_ARRAY:
#ifdef ARRAY_SUPPORT
!!! NOT IMPLEMENTED YET !!!
#else
sv_setpvn(sv, "** array **", 11);
#endif
break;
default:
sv_setpvn(sv, "** unknown **", 13);
}
/*
* I use the column's alias name because in the absence
* of an alias, it contains the column name anyway.
* Only if the alias AND the column names are zero-length
* do I want to use a generic "COLUMN%d" header.
* This happens, for example, when the column is a
* computed field and the query doesn't use an AS clause
* to label the column.
*/
/*
if (var->aliasname_length > 0)
{
sv_setpvn(sv, var->aliasname, var->aliasname_length));
}
else
{
char s[20];
snprintf(s, sizeof(s), "COLUMN%d", i);
sv_setpvn(sv, s, strlen(s));
}
*/
}
}
imp_sth->affected += 1;
return av;
}
void dbd_st_destroy(SV *sth, imp_sth_t *imp_sth)
{
D_imp_dbh_from_sth;
ISC_STATUS status[ISC_STATUS_LENGTH];
DBI_TRACE_imp_xxh(imp_dbh, 2, (DBIc_LOGPIO(imp_dbh), "dbd_st_destroy\n"));
/* freeing cursor name */
FREE_SETNULL(imp_sth->cursor_name);
if ( imp_sth->param_values != NULL ) {
hv_undef(imp_sth->param_values);
imp_sth->param_values = NULL;
}
/* freeing in_sqlda */
if (imp_sth->in_sqlda)
{
int i;
XSQLVAR *var = imp_sth->in_sqlda->sqlvar;
DBI_TRACE_imp_xxh(imp_dbh, 3, (DBIc_LOGPIO(imp_dbh), "dbd_st_destroy: found in_sqlda..\n"));
for (i = 0; i < imp_sth->in_sqlda->sqld; i++, var++)
{
FREE_SETNULL(var->sqldata);
FREE_SETNULL(var->sqlind);
}
DBI_TRACE_imp_xxh(imp_dbh, 3, (DBIc_LOGPIO(imp_dbh), "dbd_st_destroy: freeing in_sqlda..\n"));
Safefree(imp_sth->in_sqlda);
imp_sth->in_sqlda = NULL;
}
/* freeing out_sqlda */
if (imp_sth->out_sqlda)
{
int i;
XSQLVAR *var = imp_sth->out_sqlda->sqlvar;
for (i = 0; i < imp_sth->out_sqlda->sqld; i++, var++)
{
FREE_SETNULL(var->sqldata);
FREE_SETNULL(var->sqlind);
}
Safefree(imp_sth->out_sqlda);
imp_sth->out_sqlda = NULL;
}
/* free all other resources */
FREE_SETNULL(imp_sth->dateformat);
FREE_SETNULL(imp_sth->timeformat);
FREE_SETNULL(imp_sth->timestampformat);
/* Drop the statement */
if (imp_sth->stmt)
{
isc_dsql_free_statement(status, &(imp_sth->stmt), DSQL_drop);
if (ib_error_check(sth, status))
{
DBI_TRACE_imp_xxh(imp_dbh, 3, (DBIc_LOGPIO(imp_dbh), "dbd_st_destroy: isc_dsql_free_statement failed.\n"));
}
else
DBI_TRACE_imp_xxh(imp_dbh, 3, (DBIc_LOGPIO(imp_dbh), "dbd_st_destroy: isc_dsql_free_statement succeeded.\n"));
imp_sth->stmt = 0L;
}
/* remove sth from linked list */
result = newRV_inc(sv_2mortal((SV*)av));
while(--i >= 0)
av_store(av, i, newSViv(imp_sth->out_sqlda->sqlvar[i].sqlscale));
}
/**************************************************************************/
else if (kl==9 && strEQ(key, "PRECISION"))
{
AV *av;
if (!imp_sth->in_sqlda || !imp_sth->out_sqlda)
return Nullsv;
av = newAV();
result = newRV_inc(sv_2mortal((SV*)av));
while(--i >= 0)
av_store(av, i, newSViv(imp_sth->out_sqlda->sqlvar[i].sqllen));
}
/**************************************************************************/
else if (kl==4 && strEQ(key, "NAME"))
{
AV *av;
if (!imp_sth->in_sqlda || !imp_sth->out_sqlda)
return Nullsv;
av = newAV();
result = newRV_inc(sv_2mortal((SV*)av));
while(--i >= 0)
{
if (imp_sth->out_sqlda->sqlvar[i].aliasname_length > 0)
{
av_store(av, i,
newSVpvn(imp_sth->out_sqlda->sqlvar[i].aliasname,
imp_sth->out_sqlda->sqlvar[i].aliasname_length));
}
else
{
char s[20];
snprintf(s, sizeof(s), "COLUMN%d", i);
av_store(av, i, newSVpvn(s, strlen(s)));
}
}
}
/**************************************************************************/
else if (kl==8 && strEQ(key, "NULLABLE"))
{
AV *av;
if (!imp_sth->in_sqlda || !imp_sth->out_sqlda)
return Nullsv;
av = newAV();
result = newRV_inc(sv_2mortal((SV*)av));
while(--i >= 0)
av_store(av, i, boolSV((imp_sth->out_sqlda->sqlvar[i].sqltype & 1) != 0));
}
/**************************************************************************/
else if (kl==10 && strEQ(key, "CursorName"))
{
if (imp_sth->cursor_name == NULL)
return Nullsv;
result = newSVpv(imp_sth->cursor_name, strlen(imp_sth->cursor_name));
}
/**************************************************************************/
else if (kl==11 && strEQ(key, "ParamValues"))
{
if (imp_sth->param_values == NULL)
return Nullsv;
result = newRV_inc((SV*)imp_sth->param_values);
}
else
return Nullsv;
if (cacheit)
{ /* cache for next time (via DBI quick_FETCH) */
SV **svp = hv_fetch((HV*)SvRV(sth), key, kl, 1);
sv_free(*svp);
*svp = result;
(void)SvREFCNT_inc(result); /* so sv_2mortal won't free it */
}
return sv_2mortal(result);
}
int dbd_st_STORE_attrib(SV *sth, imp_sth_t *imp_sth, SV *keysv, SV *valuesv)
{
STRLEN kl;
char *key = SvPV(keysv, kl);
DBI_TRACE_imp_xxh(imp_sth, 2, (DBIc_LOGPIO(imp_sth), "dbd_st_STORE - %s\n", key));
return FALSE;
}
int dbd_discon_all(SV *drh, imp_drh_t *imp_drh)
{
dTHR;
/* The disconnect_all concept is flawed and needs more work */
if (!SvTRUE(perl_get_sv("DBI::PERL_ENDING", 0)))
{
sv_setiv(DBIc_ERR(imp_drh), (IV)1);
sv_setpv(DBIc_ERRSTR(imp_drh), (char*)"disconnect_all not implemented");
(void)DBIh_EVENT2(drh, ERROR_event, DBIc_ERR(imp_drh), DBIc_ERRSTR(imp_drh));
return FALSE;
}
if (PL_perl_destruct_level)
PL_perl_destruct_level = 0;
return FALSE;
}
int ib_blob_write(SV *sth, imp_sth_t *imp_sth, XSQLVAR *var, SV *value)
{
D_imp_dbh_from_sth;
isc_blob_handle handle = 0;
ISC_STATUS status[ISC_STATUS_LENGTH];
STRLEN total_length;
char *p, *seg, *string;
int is_text_blob, seg_len;
( run in 0.553 second using v1.01-cache-2.11-cpan-39bf76dae61 )