DBD-SQLcipher

 view release on metacpan or  search on metacpan

dbdimp.c  view on Meta::CPAN

    return rc;
}

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 (extended)
            rc = sqlite3_extended_errcode(*db);
        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;

dbdimp.c  view on Meta::CPAN

    }
    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;

    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, "sqlite_open_flags", 17)) {
            val = hv_fetch(hv, "sqlite_open_flags", 17, 0);
            flag = (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/SQLcipher.pm */
    }
    DBIc_IMPSET_on(imp_dbh);

    imp_dbh->unicode                   = FALSE;
    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;

    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 SQLcipher.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)) {
                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 SQLcipher.xsi */
            }
        }
    }
    else if (DBIc_is(imp_dbh, DBIcf_BegunWork)) {
        const char *sql = statement;
        _skip_whitespaces(sql);
        if (_starts_with_commit(sql)) {
            DBIc_off(imp_dbh, DBIcf_BegunWork);
            DBIc_on(imp_dbh,  DBIcf_AutoCommit);
        }
        else if (_starts_with_rollback(sql)) {
            DBIc_off(imp_dbh, DBIcf_BegunWork);
            DBIc_on(imp_dbh,  DBIcf_AutoCommit);
        }
    }

    rc = sqlite_exec(dbh, statement);
    if (rc != SQLITE_OK) {
        sqlite_error(dbh, rc, sqlite3_errmsg(imp_dbh->db));
        return -2;
    }

    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");

dbdimp.c  view on Meta::CPAN

    imp_dbh->aggregates = (AV *)NULL;

    sv_setsv(imp_dbh->collation_needed_callback, &PL_sv_undef);
    SvREFCNT_dec(imp_dbh->collation_needed_callback);
    imp_dbh->collation_needed_callback = (SV *)NULL;

    return TRUE;
}

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 (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/SQLcipher.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/SQLcipher.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/SQLcipher.pm */
    }
    if (&extra) {
        imp_sth->unprepared_statements = extra;
    }
    else {
        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 SQLcipher.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 SQLcipher.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 SQLcipher.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)) {
                DBIc_on(imp_dbh,  DBIcf_BegunWork);
                DBIc_off(imp_dbh, DBIcf_AutoCommit);
            }

dbdimp.c  view on Meta::CPAN

    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 SQLcipher.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);
                sv_setpvn(AvARRAY(av)[i], sqlite3_column_blob(imp_sth->stmt, i), 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 SQLcipher.xsi */
    }

    return TRUE;
}

int
sqlite_st_finish(SV *sth, imp_sth_t *imp_sth)

dbdimp.c  view on Meta::CPAN

        }
    }
    SvREFCNT_dec((SV*)imp_sth->params);
    SvREFCNT_dec((SV*)imp_sth->col_types);
    DBIc_IMPSET_off(imp_sth);
}

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 (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, &notnull, &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)));
    }

    return retsv;
}

dbdimp.c  view on Meta::CPAN

    _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);

dbdimp.c  view on Meta::CPAN

{
    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 ) {
        /* SQLcipher 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 {

dbdimp.c  view on Meta::CPAN

    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

dbdimp.c  view on Meta::CPAN

    n_retval = call_pv(argv[0], G_SCALAR);
    SPAGAIN;

    /* store a copy of the returned coderef into the tokenizer structure */
    if (n_retval != 1) {
        warn("tokenizer_Create returned %d arguments", n_retval);
    }
    retval = POPs;
    t->coderef   = newSVsv(retval);
    *ppTokenizer = &t->base;

    PUTBACK;
    FREETMPS;
    LEAVE;

    return SQLITE_OK;
}

/*
** Destroy a tokenizer
*/
static int perl_tokenizer_Destroy(sqlite3_tokenizer *pTokenizer){
    dTHX;
    perl_tokenizer *t = (perl_tokenizer *) pTokenizer;
    sv_free(t->coderef);
    sqlite3_free(t);
    return SQLITE_OK;
}

/*
** Prepare to begin tokenizing a particular string.  The input
** string to be tokenized is supposed to be pInput[0..nBytes-1] ..
** except that nBytes passed by fts3 is -1 (don't know why) !
** This is passed to the tokenizer instance, which then returns a
** closure implementing the cursor (so the cursor is again a coderef).
*/
static int perl_tokenizer_Open(
    sqlite3_tokenizer *pTokenizer,       /* Tokenizer object */
    const char *pInput, int nBytes,      /* Input buffer */
    sqlite3_tokenizer_cursor **ppCursor  /* OUT: Created tokenizer cursor */
){
    dTHX;
    dSP;
    dMY_CXT;
    U32 flags;
    SV *perl_string;
    int n_retval;

    perl_tokenizer *t = (perl_tokenizer *)pTokenizer;

    /* allocate and initialize the cursor struct */
    perl_tokenizer_cursor *c;
    c = (perl_tokenizer_cursor *) sqlite3_malloc(sizeof(*c));
    memset(c, 0, sizeof(*c));
    *ppCursor = &c->base;

    /* flags for creating the Perl SV containing the input string */
    flags = SVs_TEMP; /* will call sv_2mortal */

    /* special handling if working with utf8 strings */
    if (MY_CXT.last_dbh_is_unicode) {

        /* data to keep track of byte offsets */
        c->lastByteOffset = c->pInput = pInput;
        c->lastCharOffset = 0;

        /* string passed to Perl needs to be flagged as utf8 */
        flags |= SVf_UTF8;
    }

    ENTER;
    SAVETMPS;

    /* build a Perl copy of the input string */
    if (nBytes < 0) { /* we get -1 from fts3. Don't know why ! */
        nBytes = strlen(pInput);
    }
    perl_string = newSVpvn_flags(pInput, nBytes, flags);

    /* call the tokenizer coderef */
    PUSHMARK(SP);
    XPUSHs(perl_string);
    PUTBACK;
    n_retval = call_sv(t->coderef, G_SCALAR);
    SPAGAIN;

    /* store the cursor coderef returned by the tokenizer */
    if (n_retval != 1) {
        warn("tokenizer returned %d arguments", n_retval);
    }
    c->coderef = newSVsv(POPs);

    PUTBACK;
    FREETMPS;
    LEAVE;
    return SQLITE_OK;
}

/*
** Close a tokenization cursor previously opened by a call to
** perl_tokenizer_Open() above.
*/
static int perl_tokenizer_Close(sqlite3_tokenizer_cursor *pCursor){
    perl_tokenizer_cursor *c = (perl_tokenizer_cursor *) pCursor;

    dTHX;
    sv_free(c->coderef);
    if (c->pToken) sqlite3_free(c->pToken);
    sqlite3_free(c);
    return SQLITE_OK;
}

/*
** Extract the next token from a tokenization cursor.  The cursor must
** have been opened by a prior call to perl_tokenizer_Open().
*/
static int perl_tokenizer_Next(
    sqlite3_tokenizer_cursor *pCursor,  /* Cursor returned by perl_tokenizer_Open */
    const char **ppToken,               /* OUT: *ppToken is the token text */
    int *pnBytes,                       /* OUT: Number of bytes in token */
    int *piStartOffset,                 /* OUT: Starting offset of token */

dbdimp.c  view on Meta::CPAN

        warn("vtab->OPEN() method returned %d vals instead of 1", count);
        SP -= count;
        goto cleanup;

    }
    perl_cursor = POPs;
    if ( !sv_isobject(perl_cursor) ) {
        warn("vtab->OPEN() method did not return a blessed cursor");
        goto cleanup;
    }

    /* everything went OK */
    rc = SQLITE_OK;

 cleanup:

    if (rc == SQLITE_OK) {
        cursor->perl_cursor_obj = SvREFCNT_inc(perl_cursor);
        *ppCursor = &cursor->base;
    }
    else {
        sqlite3_free(cursor);
    }

    PUTBACK;
    FREETMPS;
    LEAVE;

    return rc;
}

static int perl_vt_Close(sqlite3_vtab_cursor *pVtabCursor){
    dTHX;
    dSP;
    perl_vtab_cursor *perl_pVTabCursor;

    ENTER;
    SAVETMPS;

    /* Note : there is no explicit call to a CLOSE() method; if
       needed, the Perl class can implement a DESTROY() method */

    perl_pVTabCursor = (perl_vtab_cursor *) pVtabCursor;
    SvREFCNT_dec(perl_pVTabCursor->perl_cursor_obj);
    sqlite3_free(perl_pVTabCursor);

    PUTBACK;
    FREETMPS;
    LEAVE;

    return SQLITE_OK;
}

static int perl_vt_Filter( sqlite3_vtab_cursor *pVtabCursor, 
                           int idxNum, const char *idxStr,
                           int argc, sqlite3_value **argv ){
    dTHX;
    dSP;
    dMY_CXT;
    int i, count;
    int is_unicode = MY_CXT.last_dbh_is_unicode;

    ENTER;
    SAVETMPS;

    /* call the FILTER() method with ($idxNum, $idxStr, @args) */
    PUSHMARK(SP);
    XPUSHs(((perl_vtab_cursor *) pVtabCursor)->perl_cursor_obj);
    XPUSHs(sv_2mortal(newSViv(idxNum)));
    XPUSHs(sv_2mortal(newSVpv(idxStr, 0)));
    for(i = 0; i < argc; i++) {
        XPUSHs(stacked_sv_from_sqlite3_value(aTHX_ argv[i], is_unicode));
    }
    PUTBACK;
    count = call_method("FILTER", G_VOID);
    SPAGAIN;
    SP -= count;

    PUTBACK;
    FREETMPS;
    LEAVE;

    return SQLITE_OK;
}


static int perl_vt_Next(sqlite3_vtab_cursor *pVtabCursor){
    dTHX;
    dSP;
    int count;

    ENTER;
    SAVETMPS;

    /* call the next() method */
    PUSHMARK(SP);
    XPUSHs(((perl_vtab_cursor *) pVtabCursor)->perl_cursor_obj);
    PUTBACK;
    count = call_method ("NEXT", G_VOID);
    SPAGAIN;
    SP -= count;

    PUTBACK;
    FREETMPS;
    LEAVE;

    return SQLITE_OK;
}

static int perl_vt_Eof(sqlite3_vtab_cursor *pVtabCursor){
    dTHX;
    dSP;
    int count, eof;

    ENTER;
    SAVETMPS;

    /* call the eof() method */
    PUSHMARK(SP);
    XPUSHs(((perl_vtab_cursor *) pVtabCursor)->perl_cursor_obj);
    PUTBACK;
    count = call_method ("EOF", G_SCALAR);
    SPAGAIN;
    if (count != 1) {
        warn("cursor->EOF() method returned %d vals instead of 1", count);
        SP -= count;
    }
    else {
        SV *sv = POPs;     /* need 2 lines, because this doesn't work :        */
        eof = SvTRUE(sv);  /* eof = SvTRUE(POPs); # I don't understand why :-( */
    }

dbdimp.c  view on Meta::CPAN

    PUTBACK;
    count = call_method ("COLUMN", G_SCALAR);
    SPAGAIN;
    if (count != 1) {
        warn("cursor->COLUMN() method returned %d vals instead of 1", count);
        SP -= count;
        sqlite3_result_error(context, "column error", 12);
    }
    else {
        SV *result = POPs;
        sqlite_set_result(aTHX_ context, result, 0 );
        rc = SQLITE_OK;
    }

    PUTBACK;
    FREETMPS;
    LEAVE;

    return rc;
}

static int perl_vt_Rowid( sqlite3_vtab_cursor *pVtabCursor,
                          sqlite3_int64 *pRowid ){
    dTHX;
    dSP;
    int count;
    int rc = SQLITE_ERROR;

    ENTER;
    SAVETMPS;

    /* call the rowid() method */
    PUSHMARK(SP);
    XPUSHs(((perl_vtab_cursor *) pVtabCursor)->perl_cursor_obj);
    PUTBACK;
    count = call_method ("ROWID", G_SCALAR);
    SPAGAIN;
    if (count != 1) {
        warn("cursor->ROWID() returned %d vals instead of 1", count);
        SP -= count;
    }
    else {
        *pRowid =POPi;
        rc = SQLITE_OK;
    }

    PUTBACK;
    FREETMPS;
    LEAVE;

    return rc;
}

static int perl_vt_Update( sqlite3_vtab *pVTab, 
                           int argc, sqlite3_value **argv, 
                           sqlite3_int64 *pRowid ){
    dTHX;
    dSP;
    dMY_CXT;
    int count, i;
    int is_unicode = MY_CXT.last_dbh_is_unicode;
    int rc = SQLITE_ERROR;
    SV *rowidsv;

    ENTER;
    SAVETMPS;

    /* call the _SQLITE_UPDATE() method */
    PUSHMARK(SP);
    XPUSHs(((perl_vtab *) pVTab)->perl_vtab_obj);
    for(i = 0; i < argc; i++) {
        XPUSHs(stacked_sv_from_sqlite3_value(aTHX_ argv[i], is_unicode));
    }
    PUTBACK;
    count = call_method ("_SQLITE_UPDATE", G_SCALAR);
    SPAGAIN;
    if (count != 1) {
        warn("cursor->_SQLITE_UPDATE() returned %d vals instead of 1", count);
        SP -= count;
    }
    else {
        if (argc > 1 && sqlite3_value_type(argv[0]) == SQLITE_NULL
                      && sqlite3_value_type(argv[1]) == SQLITE_NULL) {
            /* this was an insert without any given rowid, so the result of
               the method call must be passed in *pRowid*/
            rowidsv = POPs;
            if (!SvOK(rowidsv))
                *pRowid = 0;
            else if (SvUOK(rowidsv))
                *pRowid = SvUV(rowidsv);
            else if (SvIOK(rowidsv))
                *pRowid = SvIV(rowidsv);
            else
                *pRowid = (sqlite3_int64)SvNV(rowidsv);
        }
        rc = SQLITE_OK;
    }


    PUTBACK;
    FREETMPS;
    LEAVE;

    return rc;
}

static int perl_vt_Begin(sqlite3_vtab *pVTab){
  return _call_perl_vtab_method(pVTab, "BEGIN_TRANSACTION", 0);
}

static int perl_vt_Sync(sqlite3_vtab *pVTab){
  return _call_perl_vtab_method(pVTab, "SYNC_TRANSACTION", 0);
}

static int perl_vt_Commit(sqlite3_vtab *pVTab){
  return _call_perl_vtab_method(pVTab, "COMMIT_TRANSACTION", 0);
}

static int perl_vt_Rollback(sqlite3_vtab *pVTab){
  return _call_perl_vtab_method(pVTab, "ROLLBACK_TRANSACTION", 0);
}

static int perl_vt_FindFunction(sqlite3_vtab *pVTab, 
                       int nArg, const char *zName,
                       void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
                       void **ppArg){
    dTHX;
    dSP;
    dMY_CXT;
    int count;
    int is_overloaded = 0;
    char *func_name   = sqlite3_mprintf("%s\t%d", zName, nArg);
    STRLEN len        = strlen(func_name);
    HV *functions     = ((perl_vtab *) pVTab)->functions;
    SV* coderef       = NULL;
    SV** val;
    SV *result;

    ENTER;
    SAVETMPS;

    /* check if that function was already in cache */
    if (hv_exists(functions, func_name, len)) {
        val = hv_fetch(functions, func_name, len, FALSE);
        if (val && SvOK(*val)) {
            coderef = *val;
        }
    }
    else {
        /* call the FIND_FUNCTION() method */
        PUSHMARK(SP);
        XPUSHs(((perl_vtab *) pVTab)->perl_vtab_obj);
        XPUSHs(sv_2mortal(newSViv(nArg)));
        XPUSHs(sv_2mortal(newSVpv(zName, 0)));
        PUTBACK;
        count = call_method ("FIND_FUNCTION", G_SCALAR);
        SPAGAIN;
        if (count != 1) {
            warn("vtab->FIND_FUNCTION() method returned %d vals instead of 1", count);
            SP -= count;
            goto cleanup;
        }
        result = POPs;
        if (SvTRUE(result)) {
            /* the coderef must be valid for the lifetime of pVTab, so
               make a copy */
            coderef = newSVsv(result);
        }                

        /* store result in cache */
        hv_store(functions, func_name, len, coderef ? coderef : &PL_sv_undef, 0);
    }

    /* return function information for sqlite3 within *pxFunc and *ppArg */
    is_overloaded = coderef && SvTRUE(coderef);
    if (is_overloaded) {
        *pxFunc = MY_CXT.last_dbh_is_unicode ? sqlite_db_func_dispatcher_unicode
                                          : sqlite_db_func_dispatcher_no_unicode;
        *ppArg = coderef;
    }

 cleanup:
    PUTBACK;
    FREETMPS;
    LEAVE;
    sqlite3_free(func_name);
    return is_overloaded;
}


static int perl_vt_Rename(sqlite3_vtab *pVTab, const char *zNew){
    dTHX;
    dSP;
    int count;
    int rc = SQLITE_ERROR;

    ENTER;
    SAVETMPS;

    PUSHMARK(SP);
    XPUSHs(((perl_vtab *) pVTab)->perl_vtab_obj);
    XPUSHs(sv_2mortal(newSVpv(zNew, 0)));
    PUTBACK;
    count = call_method("RENAME", G_SCALAR);
    SPAGAIN;
    if (count != 1) {
        warn("vtab->RENAME() returned %d args instead of 1", count);
        SP -= count;
    }
    else {
        rc = POPi;
    }

    PUTBACK;
    FREETMPS;
    LEAVE;

    return rc;
}

static int perl_vt_Savepoint(sqlite3_vtab *pVTab, int point){
    return _call_perl_vtab_method(pVTab, "SAVEPOINT", point);
}

static int perl_vt_Release(sqlite3_vtab *pVTab, int point){
    return _call_perl_vtab_method(pVTab, "RELEASE", point);
}

static int perl_vt_RollbackTo(sqlite3_vtab *pVTab, int point){
    return _call_perl_vtab_method(pVTab, "ROLLBACK_TO", point);
}

static sqlite3_module perl_vt_Module = {
    1,                    /* iVersion */
    perl_vt_Create,       /* xCreate */
    perl_vt_Connect,      /* xConnect */
    perl_vt_BestIndex,    /* xBestIndex */
    perl_vt_Disconnect,   /* xDisconnect */



( run in 0.815 second using v1.01-cache-2.11-cpan-f5b5a18a01a )