DBD-SQLeet
view release on metacpan or search on metacpan
int
_sqlite_open(pTHX_ SV *dbh, const char *dbname, sqlite3 **db, int flags, int extended)
{
int rc;
if (flags) {
rc = sqlite3_open_v2(dbname, db, flags, NULL);
} else {
rc = sqlite3_open(dbname, db);
}
if ( rc != SQLITE_OK ) {
#if SQLITE_VERSION_NUMBER >= 3006005
if (extended)
rc = sqlite3_extended_errcode(*db);
#endif
sqlite_error(dbh, rc, sqlite3_errmsg(*db));
if (*db) sqlite3_close(*db);
}
return rc;
}
static int
sqlite_type_to_odbc_type(int type)
{
switch(type) {
case SQLITE_INTEGER: return SQL_INTEGER;
case SQLITE_FLOAT: return SQL_DOUBLE;
case SQLITE_TEXT: return SQL_VARCHAR;
case SQLITE_BLOB: return SQL_BLOB;
case SQLITE_NULL: return SQL_UNKNOWN_TYPE;
default: return SQL_UNKNOWN_TYPE;
}
}
static int
sqlite_type_from_odbc_type(int type)
{
switch(type) {
case SQL_UNKNOWN_TYPE:
return SQLITE_NULL;
case SQL_INTEGER:
case SQL_SMALLINT:
case SQL_TINYINT:
case SQL_BIGINT:
return SQLITE_INTEGER;
case SQL_FLOAT:
case SQL_REAL:
case SQL_DOUBLE:
return SQLITE_FLOAT;
case SQL_BLOB:
return SQLITE_BLOB;
default:
return SQLITE_TEXT;
}
}
void
init_cxt() {
dTHX;
MY_CXT_INIT;
MY_CXT.last_dbh_is_unicode = 0;
}
SV *
stacked_sv_from_sqlite3_value(pTHX_ sqlite3_value *value, int is_unicode)
{
STRLEN len;
sqlite_int64 iv;
int type = sqlite3_value_type(value);
SV *sv;
switch(type) {
case SQLITE_INTEGER:
iv = sqlite3_value_int64(value);
if ( iv >= IV_MIN && iv <= IV_MAX ) {
/* ^^^ compile-time constant (= true) when IV == int64 */
return sv_2mortal(newSViv((IV)iv));
}
else if ( iv >= 0 && iv <= UV_MAX ) {
/* warn("integer overflow, cast to UV"); */
return sv_2mortal(newSVuv((UV)iv));
}
else {
/* warn("integer overflow, cast to NV"); */
return sv_2mortal(newSVnv((NV)iv));
}
case SQLITE_FLOAT:
return sv_2mortal(newSVnv(sqlite3_value_double(value)));
break;
case SQLITE_TEXT:
len = sqlite3_value_bytes(value);
sv = newSVpvn((const char *)sqlite3_value_text(value), len);
if (is_unicode) {
SvUTF8_on(sv);
}
return sv_2mortal(sv);
case SQLITE_BLOB:
len = sqlite3_value_bytes(value);
return sv_2mortal(newSVpvn(sqlite3_value_blob(value), len));
default:
return &PL_sv_undef;
}
}
static void
sqlite_set_result(pTHX_ sqlite3_context *context, SV *result, int is_error)
{
STRLEN len;
char *s;
sqlite3_int64 iv;
if ( is_error ) {
s = SvPV(result, len);
sqlite3_result_error( context, s, len );
return;
}
/* warn("result: %s\n", SvPV_nolen(result)); */
if ( !SvOK(result) ) {
sqlite3_result_null( context );
} else if( SvIOK_UV(result) ) {
if ((UV)(sqlite3_int64)UV_MAX == UV_MAX)
sqlite3_result_int64( context, (sqlite3_int64)SvUV(result));
else {
s = SvPV(result, len);
sqlite3_result_text( context, s, len, SQLITE_TRANSIENT );
}
} else if ( !_sqlite_atoi64(SvPV(result, len), &iv) ) {
sqlite3_result_int64( context, iv );
} else if ( SvNOK(result) && ( sizeof(NV) == sizeof(double) || SvNVX(result) == (double) SvNVX(result) ) ) {
sqlite3_result_double( context, SvNV(result));
} else {
s = SvPV(result, len);
sqlite3_result_text( context, s, len, SQLITE_TRANSIENT );
}
}
/*
* see also sqlite3IsNumber, sqlite3_int64 type definition,
* applyNumericAffinity, sqlite3Atoi64, etc from sqlite3.c
*/
static int
sqlite_is_number(pTHX_ const char *v, int sql_type)
{
sqlite3_int64 iv;
const char *z = v;
const char *d = v;
int neg;
char tmp[22];
strncpy(tmp, d, z - d + 1);
c = memcmp(tmp, "922337203685477580", 18);
if (c == 0) {
c = tmp[18] - '7' - neg;
}
if (c > 0) maybe_int = FALSE;
}
if (*z == '.') {
maybe_int = FALSE;
z++;
if (!isdigit(*z)) return 0;
while (isdigit(*z)) { precision++; z++; }
}
if (*z == 'e' || *z == 'E') {
maybe_int = FALSE;
z++;
if (*z == '+' || *z == '-') { z++; }
if (!isdigit(*z)) return 0;
while (isdigit(*z)) { z++; }
}
if (*z && !isdigit(*z)) return 0;
if (maybe_int && digit) {
if (!_sqlite_atoi64(v, &iv)) return 1;
}
if (sql_type != SQLITE_INTEGER) {
sprintf(format, (has_plus ? "+%%.%df" : "%%.%df"), precision);
if (strEQ(form(format, atof(v)), v)) return 2;
}
return 0;
}
/*-----------------------------------------------------*
* DBD Methods
*-----------------------------------------------------*/
void
sqlite_init(dbistate_t *dbistate)
{
dTHX;
DBISTATE_INIT; /* Initialize the DBI macros */
}
int
sqlite_discon_all(SV *drh, imp_drh_t *imp_drh)
{
dTHX;
return FALSE; /* no way to do this */
}
int
sqlite_db_login6(SV *dbh, imp_dbh_t *imp_dbh, char *dbname, char *user, char *pass, SV *attr)
{
dTHX;
int rc;
HV *hv;
SV **val;
int extended = 0;
int flag = 0;
int unicode = 0;
sqlite_trace(dbh, imp_dbh, 3, form("login '%s' (version %s)", dbname, sqlite3_version));
if (SvROK(attr)) {
hv = (HV*)SvRV(attr);
if (hv_exists(hv, "sqlite_extended_result_codes", 28)) {
val = hv_fetch(hv, "sqlite_extended_result_codes", 28, 0);
extended = (val && SvOK(*val)) ? !(!SvTRUE(*val)) : 0;
}
if (hv_exists(hv, "ReadOnly", 8)) {
val = hv_fetch(hv, "ReadOnly", 8, 0);
if ((val && SvOK(*val)) ? SvIV(*val) : 0) {
flag |= SQLITE_OPEN_READONLY;
}
}
if (hv_exists(hv, "sqlite_open_flags", 17)) {
val = hv_fetch(hv, "sqlite_open_flags", 17, 0);
flag |= (val && SvOK(*val)) ? SvIV(*val) : 0;
if (flag & SQLITE_OPEN_READONLY) {
hv_stores(hv, "ReadOnly", newSViv(1));
}
}
/* sqlite_unicode should be detected earlier, to register default functions correctly */
if (hv_exists(hv, "sqlite_unicode", 14)) {
val = hv_fetch(hv, "sqlite_unicode", 14, 0);
unicode = (val && SvOK(*val)) ? SvIV(*val) : 0;
} else if (hv_exists(hv, "unicode", 7)) {
val = hv_fetch(hv, "unicode", 7, 0);
unicode = (val && SvOK(*val)) ? SvIV(*val) : 0;
}
}
rc = sqlite_open2(dbname, &(imp_dbh->db), flag, extended);
if ( rc != SQLITE_OK ) {
return FALSE; /* -> undef in lib/DBD/SQLeet.pm */
}
DBIc_IMPSET_on(imp_dbh);
imp_dbh->unicode = unicode;
imp_dbh->functions = newAV();
imp_dbh->aggregates = newAV();
imp_dbh->collation_needed_callback = newSVsv( &PL_sv_undef );
imp_dbh->timeout = SQL_TIMEOUT;
imp_dbh->handle_binary_nulls = FALSE;
imp_dbh->allow_multiple_statements = FALSE;
imp_dbh->use_immediate_transaction = TRUE;
imp_dbh->see_if_its_a_number = FALSE;
imp_dbh->extended_result_codes = extended;
imp_dbh->stmt_list = NULL;
imp_dbh->began_transaction = FALSE;
sqlite3_busy_timeout(imp_dbh->db, SQL_TIMEOUT);
#if 0
/*
** As of 1.26_06 foreign keys support was enabled by default,
** but with further discussion, we agreed to follow what
** sqlite team does, i.e. wait until the team think it
** reasonable to enable the support by default, as they have
** larger users and will allocate enough time for people to
** get used to the foreign keys. However, we should say it loud
** that sometime in the (near?) future, this feature may break
** your applications (and it actually broke applications).
** Let everyone be prepared.
*/
sqlite_exec(dbh, "PRAGMA foreign_keys = ON");
#endif
#if 0
/*
** Enable this to see if you (wrongly) expect an implicit order
** of return values from a SELECT statement without ORDER BY.
*/
sqlite_exec(dbh, "PRAGMA reverse_unordered_selects = ON");
#endif
DBIc_ACTIVE_on(imp_dbh);
return TRUE;
}
int
sqlite_db_do_sv(SV *dbh, imp_dbh_t *imp_dbh, SV *sv_statement)
{
dTHX;
int rc = 0;
int i;
char *statement;
if (!DBIc_ACTIVE(imp_dbh)) {
sqlite_error(dbh, -2, "attempt to do on inactive database handle");
return -2; /* -> undef in SQLeet.xsi */
}
/* sqlite3_prepare wants an utf8-encoded SQL statement */
if (imp_dbh->unicode) {
sv_utf8_upgrade(sv_statement);
}
statement = SvPV_nolen(sv_statement);
sqlite_trace(dbh, imp_dbh, 3, form("do statement: %s", statement));
croak_if_db_is_null();
if (sqlite3_get_autocommit(imp_dbh->db)) {
const char *sql = statement;
_skip_whitespaces(sql);
if (_starts_with_begin(sql)) {
if (DBIc_is(imp_dbh, DBIcf_AutoCommit)) {
if (!DBIc_is(imp_dbh, DBIcf_BegunWork)) {
imp_dbh->began_transaction = TRUE;
DBIc_on(imp_dbh, DBIcf_BegunWork);
DBIc_off(imp_dbh, DBIcf_AutoCommit);
}
}
}
else if (!DBIc_is(imp_dbh, DBIcf_AutoCommit)) {
sqlite_trace(dbh, imp_dbh, 3, "BEGIN TRAN");
if (imp_dbh->use_immediate_transaction) {
rc = sqlite_exec(dbh, "BEGIN IMMEDIATE TRANSACTION");
} else {
rc = sqlite_exec(dbh, "BEGIN TRANSACTION");
}
if (rc != SQLITE_OK) {
return -2; /* -> undef in SQLeet.xsi */
}
}
}
rc = sqlite_exec(dbh, statement);
if (rc != SQLITE_OK) {
sqlite_error(dbh, rc, sqlite3_errmsg(imp_dbh->db));
return -2;
}
if (DBIc_is(imp_dbh, DBIcf_BegunWork) && sqlite3_get_autocommit(imp_dbh->db)) {
if (imp_dbh->began_transaction) {
DBIc_off(imp_dbh, DBIcf_BegunWork);
DBIc_on(imp_dbh, DBIcf_AutoCommit);
}
}
return sqlite3_changes(imp_dbh->db);
}
int
sqlite_db_commit(SV *dbh, imp_dbh_t *imp_dbh)
{
dTHX;
int rc;
if (!DBIc_ACTIVE(imp_dbh)) {
sqlite_error(dbh, -2, "attempt to commit on inactive database handle");
return FALSE;
}
void
sqlite_db_destroy(SV *dbh, imp_dbh_t *imp_dbh)
{
dTHX;
if (DBIc_ACTIVE(imp_dbh)) {
sqlite_db_disconnect(dbh, imp_dbh);
}
DBIc_IMPSET_off(imp_dbh);
}
int
sqlite_db_STORE_attrib(SV *dbh, imp_dbh_t *imp_dbh, SV *keysv, SV *valuesv)
{
dTHX;
char *key = SvPV_nolen(keysv);
int rc;
croak_if_db_is_null();
if (strEQ(key, "AutoCommit")) {
if (SvTRUE(valuesv)) {
/* commit tran? */
if ( DBIc_ACTIVE(imp_dbh) && (!DBIc_is(imp_dbh, DBIcf_AutoCommit)) && (!sqlite3_get_autocommit(imp_dbh->db)) ) {
sqlite_trace(dbh, imp_dbh, 3, "COMMIT TRAN");
rc = sqlite_exec(dbh, "COMMIT TRANSACTION");
if (rc != SQLITE_OK) {
return TRUE; /* XXX: is this correct? */
}
}
}
DBIc_set(imp_dbh, DBIcf_AutoCommit, SvTRUE(valuesv));
return TRUE;
}
#if SQLITE_VERSION_NUMBER >= 3007011
if (strEQ(key, "ReadOnly")) {
if (SvTRUE(valuesv) && !sqlite3_db_readonly(imp_dbh->db, "main")) {
sqlite_error(dbh, 0, "ReadOnly is set but it's only advisory");
}
return FALSE;
}
#endif
if (strEQ(key, "sqlite_allow_multiple_statements")) {
imp_dbh->allow_multiple_statements = !(! SvTRUE(valuesv));
return TRUE;
}
if (strEQ(key, "sqlite_use_immediate_transaction")) {
imp_dbh->use_immediate_transaction = !(! SvTRUE(valuesv));
return TRUE;
}
if (strEQ(key, "sqlite_see_if_its_a_number")) {
imp_dbh->see_if_its_a_number = !(! SvTRUE(valuesv));
return TRUE;
}
if (strEQ(key, "sqlite_extended_result_codes")) {
imp_dbh->extended_result_codes = !(! SvTRUE(valuesv));
sqlite3_extended_result_codes(imp_dbh->db, imp_dbh->extended_result_codes);
return TRUE;
}
if (strEQ(key, "sqlite_unicode")) {
#if PERL_UNICODE_DOES_NOT_WORK_WELL
sqlite_trace(dbh, imp_dbh, 3, form("Unicode support is disabled for this version of perl."));
imp_dbh->unicode = 0;
#else
imp_dbh->unicode = !(! SvTRUE(valuesv));
#endif
return TRUE;
}
if (strEQ(key, "unicode")) {
if (DBIc_has(imp_dbh, DBIcf_WARN))
warn("\"unicode\" attribute will be deprecated. Use \"sqlite_unicode\" instead.");
#if PERL_UNICODE_DOES_NOT_WORK_WELL
sqlite_trace(dbh, imp_dbh, 3, form("Unicode support is disabled for this version of perl."));
imp_dbh->unicode = 0;
#else
imp_dbh->unicode = !(! SvTRUE(valuesv));
#endif
return TRUE;
}
return FALSE;
}
SV *
sqlite_db_FETCH_attrib(SV *dbh, imp_dbh_t *imp_dbh, SV *keysv)
{
dTHX;
char *key = SvPV_nolen(keysv);
if (strEQ(key, "sqlite_version")) {
return sv_2mortal(newSVpv(sqlite3_version, 0));
}
if (strEQ(key, "sqlite_allow_multiple_statements")) {
return sv_2mortal(newSViv(imp_dbh->allow_multiple_statements ? 1 : 0));
}
if (strEQ(key, "sqlite_use_immediate_transaction")) {
return sv_2mortal(newSViv(imp_dbh->use_immediate_transaction ? 1 : 0));
}
if (strEQ(key, "sqlite_see_if_its_a_number")) {
return sv_2mortal(newSViv(imp_dbh->see_if_its_a_number ? 1 : 0));
}
if (strEQ(key, "sqlite_extended_result_codes")) {
return sv_2mortal(newSViv(imp_dbh->extended_result_codes ? 1 : 0));
}
if (strEQ(key, "sqlite_unicode")) {
#if PERL_UNICODE_DOES_NOT_WORK_WELL
sqlite_trace(dbh, imp_dbh, 3, "Unicode support is disabled for this version of perl.");
return sv_2mortal(newSViv(0));
#else
return sv_2mortal(newSViv(imp_dbh->unicode ? 1 : 0));
#endif
}
if (strEQ(key, "unicode")) {
if (DBIc_has(imp_dbh, DBIcf_WARN))
warn("\"unicode\" attribute will be deprecated. Use \"sqlite_unicode\" instead.");
#if PERL_UNICODE_DOES_NOT_WORK_WELL
sqlite_trace(dbh, imp_dbh, 3, "Unicode support is disabled for this version of perl.");
return sv_2mortal(newSViv(0));
#else
return sv_2mortal(newSViv(imp_dbh->unicode ? 1 : 0));
#endif
}
return NULL;
}
SV *
sqlite_db_last_insert_id(SV *dbh, imp_dbh_t *imp_dbh, SV *catalog, SV *schema, SV *table, SV *field, SV *attr)
{
dTHX;
if (!DBIc_ACTIVE(imp_dbh)) {
sqlite_error(dbh, -2, "attempt to get last inserted id on inactive database handle");
return FALSE;
}
croak_if_db_is_null();
return sv_2mortal(newSViv((IV)sqlite3_last_insert_rowid(imp_dbh->db)));
}
int
sqlite_st_prepare_sv(SV *sth, imp_sth_t *imp_sth, SV *sv_statement, SV *attribs)
{
dTHX;
dMY_CXT;
int rc = 0;
const char *extra;
char *statement;
stmt_list_s * new_stmt;
D_imp_dbh_from_sth;
MY_CXT.last_dbh_is_unicode = imp_dbh->unicode;
if (!DBIc_ACTIVE(imp_dbh)) {
sqlite_error(sth, -2, "attempt to prepare on inactive database handle");
return FALSE; /* -> undef in lib/DBD/SQLeet.pm */
}
/* sqlite3_prepare wants an utf8-encoded SQL statement */
if (imp_dbh->unicode) {
sv_utf8_upgrade(sv_statement);
}
statement = SvPV_nolen(sv_statement);
#if 0
if (*statement == '\0') {
sqlite_error(sth, -2, "attempt to prepare empty statement");
return FALSE; /* -> undef in lib/DBD/SQLeet.pm */
}
#endif
sqlite_trace(sth, imp_sth, 3, form("prepare statement: %s", statement));
imp_sth->nrow = -1;
imp_sth->retval = SQLITE_OK;
imp_sth->params = newAV();
imp_sth->col_types = newAV();
croak_if_db_is_null();
/* COMPAT: sqlite3_prepare_v2 is only available for 3003009 or newer */
rc = sqlite3_prepare_v2(imp_dbh->db, statement, -1, &(imp_sth->stmt), &extra);
if (rc != SQLITE_OK) {
sqlite_error(sth, rc, sqlite3_errmsg(imp_dbh->db));
if (imp_sth->stmt) {
rc = sqlite3_finalize(imp_sth->stmt);
imp_sth->stmt = NULL;
if (rc != SQLITE_OK) {
sqlite_error(sth, rc, sqlite3_errmsg(imp_dbh->db));
}
}
return FALSE; /* -> undef in lib/DBD/SQLeet.pm */
}
if (imp_dbh->allow_multiple_statements) {
imp_sth->unprepared_statements = savepv(extra);
}
else {
if (imp_dbh->allow_multiple_statements)
Safefree(imp_sth->unprepared_statements);
imp_sth->unprepared_statements = NULL;
}
/* Add the statement to the front of the list to keep track of
statements that might need to be finalized later on disconnect */
new_stmt = (stmt_list_s *) sqlite3_malloc( sizeof(stmt_list_s) );
new_stmt->stmt = imp_sth->stmt;
new_stmt->prev = imp_dbh->stmt_list;
imp_dbh->stmt_list = new_stmt;
DBIc_NUM_PARAMS(imp_sth) = sqlite3_bind_parameter_count(imp_sth->stmt);
DBIc_NUM_FIELDS(imp_sth) = sqlite3_column_count(imp_sth->stmt);
DBIc_IMPSET_on(imp_sth);
return TRUE;
}
int
sqlite_st_rows(SV *sth, imp_sth_t *imp_sth)
{
return imp_sth->nrow;
}
int
sqlite_st_execute(SV *sth, imp_sth_t *imp_sth)
{
dTHX;
D_imp_dbh_from_sth;
int rc = 0;
int num_params = DBIc_NUM_PARAMS(imp_sth);
int i;
sqlite3_int64 iv;
if (!DBIc_ACTIVE(imp_dbh)) {
sqlite_error(sth, -2, "attempt to execute on inactive database handle");
return -2; /* -> undef in SQLeet.xsi */
}
if (!imp_sth->stmt) return 0;
croak_if_db_is_null();
croak_if_stmt_is_null();
/* COMPAT: sqlite3_sql is only available for 3006000 or newer */
sqlite_trace(sth, imp_sth, 3, form("executing %s", sqlite3_sql(imp_sth->stmt)));
if (DBIc_ACTIVE(imp_sth)) {
sqlite_trace(sth, imp_sth, 3, "execute still active, reset");
imp_sth->retval = sqlite3_reset(imp_sth->stmt);
if (imp_sth->retval != SQLITE_OK) {
sqlite_error(sth, imp_sth->retval, sqlite3_errmsg(imp_dbh->db));
return -2; /* -> undef in SQLeet.xsi */
}
}
for (i = 0; i < num_params; i++) {
SV **pvalue = av_fetch(imp_sth->params, 2*i, 0);
SV **sql_type_sv = av_fetch(imp_sth->params, 2*i+1, 0);
SV *value = pvalue ? *pvalue : &PL_sv_undef;
int sql_type = sqlite_type_from_odbc_type(sql_type_sv ? SvIV(*sql_type_sv) : SQL_UNKNOWN_TYPE);
sqlite_trace(sth, imp_sth, 4, form("bind %d type %d as %s", i, sql_type, SvPV_nolen_undef_ok(value)));
if (!SvOK(value)) {
sqlite_trace(sth, imp_sth, 5, "binding null");
rc = sqlite3_bind_null(imp_sth->stmt, i+1);
}
else if (sql_type == SQLITE_BLOB) {
STRLEN len;
char * data = SvPVbyte(value, len);
rc = sqlite3_bind_blob(imp_sth->stmt, i+1, data, len, SQLITE_TRANSIENT);
}
else {
STRLEN len;
const char *data;
int numtype = 0;
if (imp_dbh->unicode) {
sv_utf8_upgrade(value);
}
data = SvPV(value, len);
/*
* XXX: For backward compatibility, it'd be better to
* accept a value like " 4" as an integer for an integer
* type column (see t/19_bindparam.t), at least when
* we explicitly specify its type. However, we should
* keep spaces when we just guess.
*
* see_if_its_a_number should be ignored if an explicit
* SQL type is set via bind_param().
*/
if (sql_type == SQLITE_NULL && imp_dbh->see_if_its_a_number) {
numtype = sqlite_is_number(aTHX_ data, SQLITE_NULL);
}
else if (sql_type == SQLITE_INTEGER || sql_type == SQLITE_FLOAT) {
numtype = sqlite_is_number(aTHX_ data, sql_type);
}
if (numtype == 1 && !_sqlite_atoi64(data, &iv)) {
rc = sqlite3_bind_int64(imp_sth->stmt, i+1, iv);
}
else if (numtype == 2 && sql_type != SQLITE_INTEGER) {
rc = sqlite3_bind_double(imp_sth->stmt, i+1, atof(data));
}
else {
if (sql_type == SQLITE_INTEGER || sql_type == SQLITE_FLOAT) {
/*
* die on datatype mismatch did more harm than good
* especially when DBIC heavily depends on this
* explicit type specification
*/
if (DBIc_has(imp_dbh, DBIcf_PrintWarn))
warn(
"datatype mismatch: bind param (%d) %s as %s",
i, SvPV_nolen_undef_ok(value),
(sql_type == SQLITE_INTEGER ? "integer" : "float")
);
}
rc = sqlite3_bind_text(imp_sth->stmt, i+1, data, len, SQLITE_TRANSIENT);
}
}
if (rc != SQLITE_OK) {
sqlite_error(sth, rc, sqlite3_errmsg(imp_dbh->db));
return -4; /* -> undef in SQLeet.xsi */
}
}
if (sqlite3_get_autocommit(imp_dbh->db)) {
/* COMPAT: sqlite3_sql is only available for 3006000 or newer */
const char *sql = sqlite3_sql(imp_sth->stmt);
_skip_whitespaces(sql);
if (_starts_with_begin(sql)) {
if (DBIc_is(imp_dbh, DBIcf_AutoCommit)) {
if (!DBIc_is(imp_dbh, DBIcf_BegunWork)) {
imp_dbh->began_transaction = TRUE;
}
sqlite_trace(sth, imp_sth, 6, form("numFields == %d, nrow == %d", numFields, imp_sth->nrow));
if (!DBIc_ACTIVE(imp_sth)) {
return Nullav;
}
if (imp_sth->retval == SQLITE_DONE) {
sqlite_st_finish(sth, imp_sth);
return Nullav;
}
if (imp_sth->retval != SQLITE_ROW) {
/* error */
sqlite_error(sth, imp_sth->retval, sqlite3_errmsg(imp_dbh->db));
sqlite_st_finish(sth, imp_sth);
return Nullav; /* -> undef in SQLeet.xsi */
}
imp_sth->nrow++;
av = DBIc_DBISTATE((imp_xxh_t *)imp_sth)->get_fbav(imp_sth);
for (i = 0; i < numFields; i++) {
int len;
char * val;
int col_type = sqlite3_column_type(imp_sth->stmt, i);
SV **sql_type = av_fetch(imp_sth->col_types, i, 0);
if (sql_type && SvOK(*sql_type)) {
if (SvIV(*sql_type)) {
col_type = sqlite_type_from_odbc_type(SvIV(*sql_type));
}
}
switch(col_type) {
case SQLITE_INTEGER:
sqlite_trace(sth, imp_sth, 5, form("fetch column %d as integer", i));
iv = sqlite3_column_int64(imp_sth->stmt, i);
if ( iv >= IV_MIN && iv <= IV_MAX ) {
sv_setiv(AvARRAY(av)[i], (IV)iv);
}
else {
val = (char*)sqlite3_column_text(imp_sth->stmt, i);
sv_setpv(AvARRAY(av)[i], val);
SvUTF8_off(AvARRAY(av)[i]);
}
break;
case SQLITE_FLOAT:
/* fetching as float may lose precision info in the perl world */
sqlite_trace(sth, imp_sth, 5, form("fetch column %d as float", i));
sv_setnv(AvARRAY(av)[i], sqlite3_column_double(imp_sth->stmt, i));
break;
case SQLITE_TEXT:
sqlite_trace(sth, imp_sth, 5, form("fetch column %d as text", i));
val = (char*)sqlite3_column_text(imp_sth->stmt, i);
len = sqlite3_column_bytes(imp_sth->stmt, i);
if (chopBlanks) {
while((len > 0) && (val[len-1] == ' ')) {
len--;
}
}
sv_setpvn(AvARRAY(av)[i], val, len);
if (imp_dbh->unicode) {
SvUTF8_on(AvARRAY(av)[i]);
} else {
SvUTF8_off(AvARRAY(av)[i]);
}
break;
case SQLITE_BLOB:
sqlite_trace(sth, imp_sth, 5, form("fetch column %d as blob", i));
len = sqlite3_column_bytes(imp_sth->stmt, i);
val = (char*)sqlite3_column_blob(imp_sth->stmt, i);
sv_setpvn(AvARRAY(av)[i], len ? val : "", len);
SvUTF8_off(AvARRAY(av)[i]);
break;
default:
sqlite_trace(sth, imp_sth, 5, form("fetch column %d as default", i));
sv_setsv(AvARRAY(av)[i], &PL_sv_undef);
SvUTF8_off(AvARRAY(av)[i]);
break;
}
SvSETMAGIC(AvARRAY(av)[i]);
}
imp_sth->retval = sqlite3_step(imp_sth->stmt);
return av;
}
int
sqlite_st_finish3(SV *sth, imp_sth_t *imp_sth, int is_destroy)
{
dTHX;
D_imp_dbh_from_sth;
croak_if_db_is_null();
croak_if_stmt_is_null();
/* warn("finish statement\n"); */
if (!DBIc_ACTIVE(imp_sth))
return TRUE;
DBIc_ACTIVE_off(imp_sth);
av_clear(imp_sth->col_types);
if (!DBIc_ACTIVE(imp_dbh)) /* no longer connected */
return TRUE;
if (is_destroy) {
return TRUE;
}
if ((imp_sth->retval = sqlite3_reset(imp_sth->stmt)) != SQLITE_OK) {
sqlite_error(sth, imp_sth->retval, sqlite3_errmsg(imp_dbh->db));
return FALSE; /* -> &sv_no (or void) in SQLeet.xsi */
}
return TRUE;
}
int
}
int
sqlite_st_blob_read(SV *sth, imp_sth_t *imp_sth,
int field, long offset, long len, SV *destrv, long destoffset)
{
return 0;
}
int
sqlite_st_STORE_attrib(SV *sth, imp_sth_t *imp_sth, SV *keysv, SV *valuesv)
{
dTHX;
/* char *key = SvPV_nolen(keysv); */
return FALSE;
}
SV *
sqlite_st_FETCH_attrib(SV *sth, imp_sth_t *imp_sth, SV *keysv)
{
dTHX;
D_imp_dbh_from_sth;
char *key = SvPV_nolen(keysv);
SV *retsv = NULL;
int i,n;
croak_if_db_is_null();
croak_if_stmt_is_null();
if (!DBIc_ACTIVE(imp_dbh)) {
sqlite_error(sth, -2, "attempt to fetch on inactive database handle");
return FALSE;
}
if (strEQ(key, "sqlite_unprepared_statements")) {
return sv_2mortal(newSVpv(imp_sth->unprepared_statements, 0));
}
/*
if (!DBIc_ACTIVE(imp_sth)) {
return NULL;
}
*/
/* warn("fetch: %s\n", key); */
i = DBIc_NUM_FIELDS(imp_sth);
if (strEQ(key, "NAME")) {
AV *av = newAV();
/* warn("Fetch NAME fields: %d\n", i); */
av_extend(av, i);
retsv = sv_2mortal(newRV_noinc((SV*)av));
for (n = 0; n < i; n++) {
/* warn("Fetch col name %d\n", n); */
const char *fieldname = sqlite3_column_name(imp_sth->stmt, n);
if (fieldname) {
/* warn("Name [%d]: %s\n", n, fieldname); */
/* char *dot = instr(fieldname, "."); */
/* if (dot) drop table name from field name */
/* fieldname = ++dot; */
SV *sv_fieldname = newSVpv(fieldname, 0);
if (imp_dbh->unicode)
SvUTF8_on(sv_fieldname);
av_store(av, n, sv_fieldname);
}
}
}
else if (strEQ(key, "PRECISION")) {
AV *av = newAV();
retsv = sv_2mortal(newRV_noinc((SV*)av));
}
else if (strEQ(key, "TYPE")) {
AV *av = newAV();
av_extend(av, i);
retsv = sv_2mortal(newRV_noinc((SV*)av));
for (n = 0; n < i; n++) {
const char *fieldtype = sqlite3_column_decltype(imp_sth->stmt, n);
int type = sqlite3_column_type(imp_sth->stmt, n);
/* warn("got type: %d = %s\n", type, fieldtype); */
type = sqlite_type_to_odbc_type(type);
/* av_store(av, n, newSViv(type)); */
if (fieldtype)
av_store(av, n, newSVpv(fieldtype, 0));
else
av_store(av, n, newSVpv("VARCHAR", 0));
}
}
else if (strEQ(key, "NULLABLE")) {
AV *av = newAV();
av_extend(av, i);
retsv = sv_2mortal(newRV_noinc((SV*)av));
#if defined(SQLITE_ENABLE_COLUMN_METADATA)
for (n = 0; n < i; n++) {
const char *database = sqlite3_column_database_name(imp_sth->stmt, n);
const char *tablename = sqlite3_column_table_name(imp_sth->stmt, n);
const char *fieldname = sqlite3_column_name(imp_sth->stmt, n);
const char *datatype, *collseq;
int notnull, primary, autoinc;
int rc = sqlite3_table_column_metadata(imp_dbh->db, database, tablename, fieldname, &datatype, &collseq, ¬null, &primary, &autoinc);
if (rc != SQLITE_OK) {
sqlite_error(sth, rc, sqlite3_errmsg(imp_dbh->db));
av_store(av, n, newSViv(2)); /* SQL_NULLABLE_UNKNOWN */
}
else {
av_store(av, n, newSViv(!notnull));
}
}
#endif
}
else if (strEQ(key, "SCALE")) {
AV *av = newAV();
retsv = sv_2mortal(newRV_noinc((SV*)av));
}
else if (strEQ(key, "NUM_OF_FIELDS")) {
retsv = sv_2mortal(newSViv(i));
}
else if (strEQ(key, "NUM_OF_PARAMS")) {
retsv = sv_2mortal(newSViv(sqlite3_bind_parameter_count(imp_sth->stmt)));
}
else if (strEQ(key, "ParamValues")) {
HV *hv = newHV();
int num_params = DBIc_NUM_PARAMS(imp_sth);
_stores_dbstatus(SQLITE_DBSTATUS_CACHE_WRITE, "cache_write");
#endif
return hv;
}
HV *
_sqlite_st_status(pTHX_ SV* sth, int reset)
{
D_imp_sth(sth);
HV *hv = newHV();
#if SQLITE_VERSION_NUMBER >= 3006004
_stores_ststatus(SQLITE_STMTSTATUS_FULLSCAN_STEP, "fullscan_step");
_stores_ststatus(SQLITE_STMTSTATUS_SORT, "sort");
#endif
#if SQLITE_VERSION_NUMBER >= 3007000
_stores_ststatus(SQLITE_STMTSTATUS_AUTOINDEX, "autoindex");
#endif
return hv;
}
SV *
sqlite_db_filename(pTHX_ SV *dbh)
{
D_imp_dbh(dbh);
const char *filename;
if (!imp_dbh->db) {
return &PL_sv_undef;
}
croak_if_db_is_null();
#if SQLITE_VERSION_NUMBER >= 3007010
filename = sqlite3_db_filename(imp_dbh->db, "main");
#endif
return filename ? newSVpv(filename, 0) : &PL_sv_undef;
}
int
sqlite_db_busy_timeout(pTHX_ SV *dbh, SV *timeout )
{
D_imp_dbh(dbh);
croak_if_db_is_null();
if (timeout && SvIOK(timeout)) {
imp_dbh->timeout = SvIV(timeout);
if (!DBIc_ACTIVE(imp_dbh)) {
sqlite_error(dbh, -2, "attempt to set busy timeout on inactive database handle");
return -2;
}
sqlite3_busy_timeout(imp_dbh->db, imp_dbh->timeout);
}
return imp_dbh->timeout;
}
static void
sqlite_db_func_dispatcher(int is_unicode, sqlite3_context *context, int argc, sqlite3_value **value)
{
dTHX;
dSP;
int count;
int i;
SV *func;
func = sqlite3_user_data(context);
ENTER;
SAVETMPS;
PUSHMARK(SP);
for ( i=0; i < argc; i++ ) {
XPUSHs(stacked_sv_from_sqlite3_value(aTHX_ value[i], is_unicode));
}
PUTBACK;
count = call_sv(func, G_SCALAR|G_EVAL);
SPAGAIN;
/* Check for an error */
if (SvTRUE(ERRSV) ) {
sqlite_set_result(aTHX_ context, ERRSV, 1);
POPs;
} else if ( count != 1 ) {
SV *err = sv_2mortal(newSVpvf( "function should return 1 argument, got %d",
count ));
sqlite_set_result(aTHX_ context, err, 1);
/* Clear the stack */
for ( i=0; i < count; i++ ) {
POPs;
}
} else {
sqlite_set_result(aTHX_ context, POPs, 0 );
}
PUTBACK;
FREETMPS;
LEAVE;
}
static void
sqlite_db_func_dispatcher_unicode(sqlite3_context *context, int argc, sqlite3_value **value)
{
sqlite_db_func_dispatcher(1, context, argc, value);
}
static void
sqlite_db_func_dispatcher_no_unicode(sqlite3_context *context, int argc, sqlite3_value **value)
{
sqlite_db_func_dispatcher(0, context, argc, value);
}
int
sqlite_db_create_function(pTHX_ SV *dbh, const char *name, int argc, SV *func, int flags)
{
D_imp_dbh(dbh);
int rc;
SV *func_sv;
if (!DBIc_ACTIVE(imp_dbh)) {
sqlite_error(dbh, -2, "attempt to create function on inactive database handle");
return FALSE;
}
/* Copy the function reference */
func_sv = newSVsv(func);
av_push( imp_dbh->functions, func_sv );
croak_if_db_is_null();
/* warn("create_function %s with %d args\n", name, argc); */
rc = sqlite3_create_function( imp_dbh->db, name, argc, SQLITE_UTF8|flags,
func_sv,
imp_dbh->unicode ? sqlite_db_func_dispatcher_unicode
: sqlite_db_func_dispatcher_no_unicode,
NULL, NULL );
if ( rc != SQLITE_OK ) {
sqlite_error(dbh, rc, form("sqlite_create_function failed with error %s", sqlite3_errmsg(imp_dbh->db)));
return FALSE;
}
return TRUE;
}
#ifndef SQLITE_OMIT_LOAD_EXTENSION
int
sqlite_db_enable_load_extension(pTHX_ SV *dbh, int onoff)
{
D_imp_dbh(dbh);
int rc;
if (!DBIc_ACTIVE(imp_dbh)) {
sqlite_error(dbh, -2, "attempt to enable load extension on inactive database handle");
return FALSE;
}
croak_if_db_is_null();
/* COMPAT: sqlite3_enable_load_extension is only available for 3003006 or newer */
rc = sqlite3_enable_load_extension( imp_dbh->db, onoff );
if ( rc != SQLITE_OK ) {
sqlite_error(dbh, rc, form("sqlite_enable_load_extension failed with error %s", sqlite3_errmsg(imp_dbh->db)));
return FALSE;
}
return TRUE;
}
int
sqlite_db_load_extension(pTHX_ SV *dbh, const char *file, const char *proc)
{
D_imp_dbh(dbh);
int rc;
if (!DBIc_ACTIVE(imp_dbh)) {
sqlite_error(dbh, -2, "attempt to load extension on inactive database handle");
return FALSE;
}
croak_if_db_is_null();
/* COMPAT: sqlite3_load_extension is only available for 3003006 or newer */
rc = sqlite3_load_extension( imp_dbh->db, file, proc, NULL );
if ( rc != SQLITE_OK ) {
sqlite_error(dbh, rc, form("sqlite_load_extension failed with error %s", sqlite3_errmsg(imp_dbh->db)));
return FALSE;
}
return TRUE;
}
#endif
HV*
sqlite_db_table_column_metadata(pTHX_ SV *dbh, SV *dbname, SV *tablename, SV *columnname)
{
D_imp_dbh(dbh);
{
dSP;
SV *pkg = NULL;
int count = 0;
aggr_info->err = NULL;
aggr_info->aggr_inst = NULL;
pkg = sqlite3_user_data(context);
if ( !pkg )
return;
ENTER;
SAVETMPS;
PUSHMARK(SP);
XPUSHs( sv_2mortal( newSVsv(pkg) ) );
PUTBACK;
count = call_method ("new", G_EVAL|G_SCALAR);
SPAGAIN;
aggr_info->inited = 1;
if ( SvTRUE( ERRSV ) ) {
aggr_info->err = newSVpvf("error during aggregator's new(): %s",
SvPV_nolen (ERRSV));
POPs;
} else if ( count != 1 ) {
int i;
aggr_info->err = newSVpvf("new() should return one value, got %d",
count );
/* Clear the stack */
for ( i=0; i < count; i++ ) {
POPs;
}
} else {
SV *aggr = POPs;
if ( SvROK(aggr) ) {
aggr_info->aggr_inst = newSVsv(aggr);
} else{
aggr_info->err = newSVpvf( "new() should return a blessed reference" );
}
}
PUTBACK;
FREETMPS;
LEAVE;
return;
}
static void
sqlite_db_aggr_step_dispatcher(sqlite3_context *context,
int argc, sqlite3_value **value)
{
dTHX;
dSP;
int i, is_unicode = 0; /* TODO : find out from db handle */
aggrInfo *aggr;
aggr = sqlite3_aggregate_context(context, sizeof (aggrInfo));
if ( !aggr )
return;
ENTER;
SAVETMPS;
/* initialize on first step */
if ( !aggr->inited ) {
sqlite_db_aggr_new_dispatcher(aTHX_ context, aggr);
}
if ( aggr->err || !aggr->aggr_inst )
goto cleanup;
PUSHMARK(SP);
XPUSHs( sv_2mortal( newSVsv( aggr->aggr_inst ) ));
for ( i=0; i < argc; i++ ) {
XPUSHs(stacked_sv_from_sqlite3_value(aTHX_ value[i], is_unicode));
}
PUTBACK;
call_method ("step", G_SCALAR|G_EVAL|G_DISCARD);
/* Check for an error */
if (SvTRUE(ERRSV) ) {
aggr->err = newSVpvf("error during aggregator's step(): %s",
SvPV_nolen(ERRSV));
POPs;
}
cleanup:
FREETMPS;
LEAVE;
}
static void
sqlite_db_aggr_finalize_dispatcher( sqlite3_context *context )
{
dTHX;
dSP;
aggrInfo *aggr, myAggr;
int count = 0;
aggr = sqlite3_aggregate_context(context, 0);
ENTER;
SAVETMPS;
if ( !aggr ) {
/* SQLite seems to refuse to create a context structure
from finalize() */
aggr = &myAggr;
aggr->aggr_inst = NULL;
aggr->err = NULL;
sqlite_db_aggr_new_dispatcher(aTHX_ context, aggr);
}
if ( ! aggr->err && aggr->aggr_inst ) {
PUSHMARK(SP);
XPUSHs( sv_2mortal( newSVsv( aggr->aggr_inst )) );
PUTBACK;
count = call_method( "finalize", G_SCALAR|G_EVAL );
SPAGAIN;
if ( SvTRUE(ERRSV) ) {
aggr->err = newSVpvf("error during aggregator's finalize(): %s",
SvPV_nolen(ERRSV) ) ;
POPs;
} else if ( count != 1 ) {
int i;
aggr->err = newSVpvf("finalize() should return 1 value, got %d",
count );
/* Clear the stack */
for ( i=0; i<count; i++ ) {
POPs;
}
} else {
ENTER;
SAVETMPS;
PUSHMARK(SP);
sv1 = newSVpvn(string1, len1);
SvUTF8_on(sv1);
sv2 = newSVpvn(string2, len2);
SvUTF8_on(sv2);
XPUSHs( sv_2mortal( sv1 ) );
XPUSHs( sv_2mortal( sv2 ) );
PUTBACK;
n_retval = call_sv(func, G_SCALAR);
SPAGAIN;
if (n_retval != 1) {
warn("collation function returned %d arguments", n_retval);
}
for(i = 0; i < n_retval; i++) {
cmp = POPi;
}
PUTBACK;
FREETMPS;
LEAVE;
return cmp;
}
int
sqlite_db_create_collation(pTHX_ SV *dbh, const char *name, SV *func)
{
D_imp_dbh(dbh);
int rv, rv2;
void *aa = "aa";
void *zz = "zz";
SV *func_sv = newSVsv(func);
if (!DBIc_ACTIVE(imp_dbh)) {
sqlite_error(dbh, -2, "attempt to create collation on inactive database handle");
return FALSE;
}
croak_if_db_is_null();
/* Check that this is a proper collation function */
rv = sqlite_db_collation_dispatcher(func_sv, 2, aa, 2, aa);
if (rv != 0) {
sqlite_trace(dbh, imp_dbh, 3, form("improper collation function: %s(aa, aa) returns %d!", name, rv));
}
rv = sqlite_db_collation_dispatcher(func_sv, 2, aa, 2, zz);
rv2 = sqlite_db_collation_dispatcher(func_sv, 2, zz, 2, aa);
if (rv2 != (rv * -1)) {
sqlite_trace(dbh, imp_dbh, 3, form("improper collation function: '%s' is not symmetric", name));
}
/* Copy the func reference so that it can be deallocated at disconnect */
av_push( imp_dbh->functions, func_sv );
/* Register the func within sqlite3 */
rv = sqlite3_create_collation(
imp_dbh->db, name, SQLITE_UTF8,
func_sv,
imp_dbh->unicode ? sqlite_db_collation_dispatcher_utf8
: sqlite_db_collation_dispatcher
);
if ( rv != SQLITE_OK ) {
sqlite_error(dbh, rv, form("sqlite_create_collation failed with error %s", sqlite3_errmsg(imp_dbh->db)));
return FALSE;
}
return TRUE;
}
void
sqlite_db_collation_needed_dispatcher(
void *dbh,
sqlite3* db, /* unused */
int eTextRep, /* unused */
const char* collation_name
)
{
dTHX;
dSP;
D_imp_dbh(dbh);
ENTER;
SAVETMPS;
PUSHMARK(SP);
XPUSHs( dbh );
XPUSHs( sv_2mortal( newSVpv( collation_name, 0) ) );
PUTBACK;
call_sv( imp_dbh->collation_needed_callback, G_VOID );
SPAGAIN;
PUTBACK;
FREETMPS;
LEAVE;
}
void
sqlite_db_collation_needed(pTHX_ SV *dbh, SV *callback)
{
D_imp_dbh(dbh);
if (!DBIc_ACTIVE(imp_dbh)) {
sqlite_error(dbh, -2, "attempt to see if collation is needed on inactive database handle");
return;
}
croak_if_db_is_null();
/* remember the callback within the dbh */
sv_setsv(imp_dbh->collation_needed_callback, callback);
/* Register the func within sqlite3 */
(void) sqlite3_collation_needed( imp_dbh->db,
(void*) (SvOK(callback) ? dbh : NULL),
sqlite_db_collation_needed_dispatcher );
}
int
( run in 0.557 second using v1.01-cache-2.11-cpan-39bf76dae61 )