mirror of
https://github.com/DBD-SQLite/DBD-SQLite
synced 2025-06-07 06:08:38 -04:00
Replace “string_unicode” boolean with “string_mode” enum.
Issue #78 and issue #68: This introduces additional, more robust schemas for translating strings between SQLite and Perl.
This commit is contained in:
parent
aa90eee3a0
commit
9dc75eaead
20 changed files with 406 additions and 176 deletions
|
@ -5,6 +5,13 @@ MODULE = DBD::SQLite PACKAGE = DBD::SQLite::Constants
|
||||||
|
|
||||||
PROTOTYPES: ENABLE
|
PROTOTYPES: ENABLE
|
||||||
|
|
||||||
|
BOOT:
|
||||||
|
newCONSTSUB( gv_stashpv("DBD::SQLite::Constants", FALSE), "DBD_SQLITE_STRING_MODE_PV", newSVuv(DBD_SQLITE_STRING_MODE_PV) );
|
||||||
|
newCONSTSUB( gv_stashpv("DBD::SQLite::Constants", FALSE), "DBD_SQLITE_STRING_MODE_BYTES", newSVuv(DBD_SQLITE_STRING_MODE_BYTES) );
|
||||||
|
newCONSTSUB( gv_stashpv("DBD::SQLite::Constants", FALSE), "DBD_SQLITE_STRING_MODE_UNICODE_NAIVE", newSVuv(DBD_SQLITE_STRING_MODE_UNICODE_NAIVE) );
|
||||||
|
newCONSTSUB( gv_stashpv("DBD::SQLite::Constants", FALSE), "DBD_SQLITE_STRING_MODE_UNICODE_FALLBACK", newSVuv(DBD_SQLITE_STRING_MODE_UNICODE_FALLBACK) );
|
||||||
|
newCONSTSUB( gv_stashpv("DBD::SQLite::Constants", FALSE), "DBD_SQLITE_STRING_MODE_UNICODE_STRICT", newSVuv(DBD_SQLITE_STRING_MODE_UNICODE_STRICT) );
|
||||||
|
|
||||||
#if SQLITE_VERSION_NUMBER >= 3034000
|
#if SQLITE_VERSION_NUMBER >= 3034000
|
||||||
|
|
||||||
IV
|
IV
|
||||||
|
|
312
dbdimp.c
312
dbdimp.c
|
@ -239,11 +239,11 @@ void
|
||||||
init_cxt() {
|
init_cxt() {
|
||||||
dTHX;
|
dTHX;
|
||||||
MY_CXT_INIT;
|
MY_CXT_INIT;
|
||||||
MY_CXT.last_dbh_is_unicode = 0;
|
MY_CXT.last_dbh_string_mode = DBD_SQLITE_STRING_MODE_PV;
|
||||||
}
|
}
|
||||||
|
|
||||||
SV *
|
SV *
|
||||||
stacked_sv_from_sqlite3_value(pTHX_ sqlite3_value *value, int is_unicode)
|
stacked_sv_from_sqlite3_value(pTHX_ sqlite3_value *value, dbd_sqlite_string_mode_t string_mode)
|
||||||
{
|
{
|
||||||
STRLEN len;
|
STRLEN len;
|
||||||
sqlite_int64 iv;
|
sqlite_int64 iv;
|
||||||
|
@ -271,9 +271,7 @@ stacked_sv_from_sqlite3_value(pTHX_ sqlite3_value *value, int is_unicode)
|
||||||
case SQLITE_TEXT:
|
case SQLITE_TEXT:
|
||||||
len = sqlite3_value_bytes(value);
|
len = sqlite3_value_bytes(value);
|
||||||
sv = newSVpvn((const char *)sqlite3_value_text(value), len);
|
sv = newSVpvn((const char *)sqlite3_value_text(value), len);
|
||||||
if (is_unicode) {
|
DBD_SQLITE_UTF8_DECODE_IF_NEEDED(sv, string_mode);
|
||||||
SvUTF8_on(sv);
|
|
||||||
}
|
|
||||||
return sv_2mortal(sv);
|
return sv_2mortal(sv);
|
||||||
case SQLITE_BLOB:
|
case SQLITE_BLOB:
|
||||||
len = sqlite3_value_bytes(value);
|
len = sqlite3_value_bytes(value);
|
||||||
|
@ -431,6 +429,44 @@ sqlite_discon_all(SV *drh, imp_drh_t *imp_drh)
|
||||||
return FALSE; /* no way to do this */
|
return FALSE; /* no way to do this */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define _croak_invalid_value(name, value) \
|
||||||
|
croak("Invalid value (%s) given for %s", value, name);
|
||||||
|
|
||||||
|
/* Like SvUV but croaks on anything other than an unsigned int. */
|
||||||
|
static inline int
|
||||||
|
my_SvUV_strict(pTHX_ SV *input, const char* name)
|
||||||
|
{
|
||||||
|
if (SvUOK(input)) {
|
||||||
|
return SvUV(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* pv = SvPVbyte_nolen(input);
|
||||||
|
|
||||||
|
UV uv;
|
||||||
|
int numtype = grok_number(pv, strlen(pv), &uv);
|
||||||
|
|
||||||
|
/* Anything else is invalid: */
|
||||||
|
if (numtype != IS_NUMBER_IN_UV) _croak_invalid_value(name, pv);
|
||||||
|
|
||||||
|
return uv;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline dbd_sqlite_string_mode_t
|
||||||
|
_extract_sqlite_string_mode_from_sv( pTHX_ SV* input )
|
||||||
|
{
|
||||||
|
if (SvOK(input)) {
|
||||||
|
UV val = my_SvUV_strict(aTHX_ input, "sqlite_string_mode");
|
||||||
|
|
||||||
|
if (val >= _DBD_SQLITE_STRING_MODE_COUNT) {
|
||||||
|
_croak_invalid_value("sqlite_string_mode", SvPVbyte_nolen(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DBD_SQLITE_STRING_MODE_PV;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
sqlite_db_login6(SV *dbh, imp_dbh_t *imp_dbh, char *dbname, char *user, char *pass, SV *attr)
|
sqlite_db_login6(SV *dbh, imp_dbh_t *imp_dbh, char *dbname, char *user, char *pass, SV *attr)
|
||||||
{
|
{
|
||||||
|
@ -440,7 +476,7 @@ sqlite_db_login6(SV *dbh, imp_dbh_t *imp_dbh, char *dbname, char *user, char *pa
|
||||||
SV **val;
|
SV **val;
|
||||||
int extended = 0;
|
int extended = 0;
|
||||||
int flag = 0;
|
int flag = 0;
|
||||||
int unicode = 0;
|
dbd_sqlite_string_mode_t string_mode = DBD_SQLITE_STRING_MODE_PV;
|
||||||
|
|
||||||
sqlite_trace(dbh, imp_dbh, 3, form("login '%s' (version %s)", dbname, sqlite3_version));
|
sqlite_trace(dbh, imp_dbh, 3, form("login '%s' (version %s)", dbname, sqlite3_version));
|
||||||
|
|
||||||
|
@ -463,13 +499,24 @@ sqlite_db_login6(SV *dbh, imp_dbh_t *imp_dbh, char *dbname, char *user, char *pa
|
||||||
hv_stores(hv, "ReadOnly", newSViv(1));
|
hv_stores(hv, "ReadOnly", newSViv(1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* sqlite_unicode should be detected earlier, to register default functions correctly */
|
|
||||||
if (hv_exists(hv, "sqlite_unicode", 14)) {
|
/* sqlite_string_mode should be detected earlier, to register default functions correctly */
|
||||||
|
|
||||||
|
SV** string_mode_svp = hv_fetchs(hv, "sqlite_string_mode", 0);
|
||||||
|
if (string_mode_svp != NULL && SvOK(*string_mode_svp)) {
|
||||||
|
string_mode = _extract_sqlite_string_mode_from_sv(aTHX_ *string_mode_svp);
|
||||||
|
|
||||||
|
/* Legacy alternatives to sqlite_string_mode: */
|
||||||
|
} else if (hv_exists(hv, "sqlite_unicode", 14)) {
|
||||||
val = hv_fetch(hv, "sqlite_unicode", 14, 0);
|
val = hv_fetch(hv, "sqlite_unicode", 14, 0);
|
||||||
unicode = (val && SvOK(*val)) ? SvIV(*val) : 0;
|
if ( (val && SvOK(*val)) ? SvIV(*val) : 0 ) {
|
||||||
|
string_mode = DBD_SQLITE_STRING_MODE_UNICODE_NAIVE;
|
||||||
|
}
|
||||||
} else if (hv_exists(hv, "unicode", 7)) {
|
} else if (hv_exists(hv, "unicode", 7)) {
|
||||||
val = hv_fetch(hv, "unicode", 7, 0);
|
val = hv_fetch(hv, "unicode", 7, 0);
|
||||||
unicode = (val && SvOK(*val)) ? SvIV(*val) : 0;
|
if ( (val && SvOK(*val)) ? SvIV(*val) : 0 ) {
|
||||||
|
string_mode = DBD_SQLITE_STRING_MODE_UNICODE_NAIVE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rc = sqlite_open2(dbname, &(imp_dbh->db), flag, extended);
|
rc = sqlite_open2(dbname, &(imp_dbh->db), flag, extended);
|
||||||
|
@ -478,7 +525,7 @@ sqlite_db_login6(SV *dbh, imp_dbh_t *imp_dbh, char *dbname, char *user, char *pa
|
||||||
}
|
}
|
||||||
DBIc_IMPSET_on(imp_dbh);
|
DBIc_IMPSET_on(imp_dbh);
|
||||||
|
|
||||||
imp_dbh->unicode = unicode;
|
imp_dbh->string_mode = string_mode;
|
||||||
imp_dbh->functions = newAV();
|
imp_dbh->functions = newAV();
|
||||||
imp_dbh->aggregates = newAV();
|
imp_dbh->aggregates = newAV();
|
||||||
imp_dbh->collation_needed_callback = newSVsv( &PL_sv_undef );
|
imp_dbh->collation_needed_callback = newSVsv( &PL_sv_undef );
|
||||||
|
@ -546,12 +593,7 @@ sqlite_db_do_sv(SV *dbh, imp_dbh_t *imp_dbh, SV *sv_statement)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* sqlite3_prepare wants an utf8-encoded SQL statement */
|
/* sqlite3_prepare wants an utf8-encoded SQL statement */
|
||||||
if (imp_dbh->unicode) {
|
DBD_SQLITE_PREP_SV_FOR_SQLITE(sv_statement, imp_dbh->string_mode);
|
||||||
sv_utf8_upgrade(sv_statement);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
sv_utf8_downgrade(sv_statement, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
statement = SvPV_nolen(sv_statement);
|
statement = SvPV_nolen(sv_statement);
|
||||||
|
|
||||||
|
@ -738,6 +780,10 @@ sqlite_db_destroy(SV *dbh, imp_dbh_t *imp_dbh)
|
||||||
DBIc_IMPSET_off(imp_dbh);
|
DBIc_IMPSET_off(imp_dbh);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define _warn_deprecated_if_possible(old, new) \
|
||||||
|
if (DBIc_has(imp_dbh, DBIcf_WARN)) \
|
||||||
|
warn("\"%s\" attribute will be deprecated. Use \"%s\" instead.", old, new);
|
||||||
|
|
||||||
int
|
int
|
||||||
sqlite_db_STORE_attrib(SV *dbh, imp_dbh_t *imp_dbh, SV *keysv, SV *valuesv)
|
sqlite_db_STORE_attrib(SV *dbh, imp_dbh_t *imp_dbh, SV *keysv, SV *valuesv)
|
||||||
{
|
{
|
||||||
|
@ -790,26 +836,33 @@ sqlite_db_STORE_attrib(SV *dbh, imp_dbh_t *imp_dbh, SV *keysv, SV *valuesv)
|
||||||
imp_dbh->prefer_numeric_type = !(! SvTRUE(valuesv));
|
imp_dbh->prefer_numeric_type = !(! SvTRUE(valuesv));
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
if (strEQ(key, "sqlite_unicode")) {
|
|
||||||
|
if (strEQ(key, "sqlite_string_mode")) {
|
||||||
|
dbd_sqlite_string_mode_t string_mode = _extract_sqlite_string_mode_from_sv(aTHX_ valuesv);
|
||||||
|
|
||||||
|
#if PERL_UNICODE_DOES_NOT_WORK_WELL
|
||||||
|
if (string_mode & DBD_SQLITE_STRING_MODE_UNICODE_ANY) {
|
||||||
|
sqlite_trace(dbh, imp_dbh, 3, form("Unicode support is disabled for this version of perl."));
|
||||||
|
string_mode = DBD_SQLITE_STRING_MODE_PV;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
imp_dbh->string_mode = string_mode;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strEQ(key, "sqlite_unicode") || strEQ(key, "unicode")) {
|
||||||
|
_warn_deprecated_if_possible(key, "sqlite_string_mode");
|
||||||
#if PERL_UNICODE_DOES_NOT_WORK_WELL
|
#if PERL_UNICODE_DOES_NOT_WORK_WELL
|
||||||
sqlite_trace(dbh, imp_dbh, 3, form("Unicode support is disabled for this version of perl."));
|
sqlite_trace(dbh, imp_dbh, 3, form("Unicode support is disabled for this version of perl."));
|
||||||
imp_dbh->unicode = 0;
|
imp_dbh->string_mode = DBD_SQLITE_STRING_MODE_PV;
|
||||||
#else
|
#else
|
||||||
imp_dbh->unicode = !(! SvTRUE(valuesv));
|
imp_dbh->string_mode = SvTRUE(valuesv) ? DBD_SQLITE_STRING_MODE_UNICODE_NAIVE : DBD_SQLITE_STRING_MODE_PV;
|
||||||
#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
|
#endif
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -837,22 +890,18 @@ sqlite_db_FETCH_attrib(SV *dbh, imp_dbh_t *imp_dbh, SV *keysv)
|
||||||
if (strEQ(key, "sqlite_prefer_numeric_type")) {
|
if (strEQ(key, "sqlite_prefer_numeric_type")) {
|
||||||
return sv_2mortal(newSViv(imp_dbh->prefer_numeric_type ? 1 : 0));
|
return sv_2mortal(newSViv(imp_dbh->prefer_numeric_type ? 1 : 0));
|
||||||
}
|
}
|
||||||
if (strEQ(key, "sqlite_unicode")) {
|
|
||||||
#if PERL_UNICODE_DOES_NOT_WORK_WELL
|
if (strEQ(key, "sqlite_string_mode")) {
|
||||||
sqlite_trace(dbh, imp_dbh, 3, "Unicode support is disabled for this version of perl.");
|
return sv_2mortal(newSVuv(imp_dbh->string_mode));
|
||||||
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))
|
if (strEQ(key, "sqlite_unicode") || strEQ(key, "unicode")) {
|
||||||
warn("\"unicode\" attribute will be deprecated. Use \"sqlite_unicode\" instead.");
|
_warn_deprecated_if_possible(key, "sqlite_string_mode");
|
||||||
#if PERL_UNICODE_DOES_NOT_WORK_WELL
|
#if PERL_UNICODE_DOES_NOT_WORK_WELL
|
||||||
sqlite_trace(dbh, imp_dbh, 3, "Unicode support is disabled for this version of perl.");
|
sqlite_trace(dbh, imp_dbh, 3, "Unicode support is disabled for this version of perl.");
|
||||||
return sv_2mortal(newSViv(0));
|
return sv_2mortal(newSViv(0));
|
||||||
#else
|
#else
|
||||||
return sv_2mortal(newSViv(imp_dbh->unicode ? 1 : 0));
|
return sv_2mortal(newSViv(imp_dbh->string_mode == DBD_SQLITE_STRING_MODE_UNICODE_NAIVE ? 1 : 0));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -885,7 +934,7 @@ sqlite_st_prepare_sv(SV *sth, imp_sth_t *imp_sth, SV *sv_statement, SV *attribs)
|
||||||
stmt_list_s * new_stmt;
|
stmt_list_s * new_stmt;
|
||||||
D_imp_dbh_from_sth;
|
D_imp_dbh_from_sth;
|
||||||
|
|
||||||
MY_CXT.last_dbh_is_unicode = imp_dbh->unicode;
|
MY_CXT.last_dbh_string_mode = imp_dbh->string_mode;
|
||||||
|
|
||||||
if (!DBIc_ACTIVE(imp_dbh)) {
|
if (!DBIc_ACTIVE(imp_dbh)) {
|
||||||
sqlite_error(sth, -2, "attempt to prepare on inactive database handle");
|
sqlite_error(sth, -2, "attempt to prepare on inactive database handle");
|
||||||
|
@ -893,12 +942,7 @@ sqlite_st_prepare_sv(SV *sth, imp_sth_t *imp_sth, SV *sv_statement, SV *attribs)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* sqlite3_prepare wants an utf8-encoded SQL statement */
|
/* sqlite3_prepare wants an utf8-encoded SQL statement */
|
||||||
if (imp_dbh->unicode) {
|
DBD_SQLITE_PREP_SV_FOR_SQLITE(sv_statement, imp_dbh->string_mode);
|
||||||
sv_utf8_upgrade(sv_statement);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
sv_utf8_downgrade(sv_statement, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
statement = SvPV_nolen(sv_statement);
|
statement = SvPV_nolen(sv_statement);
|
||||||
|
|
||||||
|
@ -1012,12 +1056,15 @@ sqlite_st_execute(SV *sth, imp_sth_t *imp_sth)
|
||||||
const char *data;
|
const char *data;
|
||||||
int numtype = 0;
|
int numtype = 0;
|
||||||
|
|
||||||
if (imp_dbh->unicode) {
|
if (imp_dbh->string_mode & DBD_SQLITE_STRING_MODE_UNICODE_ANY) {
|
||||||
data = SvPVutf8(value, len);
|
data = SvPVutf8(value, len);
|
||||||
}
|
}
|
||||||
else {
|
else if (imp_dbh->string_mode == DBD_SQLITE_STRING_MODE_BYTES) {
|
||||||
data = SvPVbyte(value, len);
|
data = SvPVbyte(value, len);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
data = SvPV(value, len);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* XXX: For backward compatibility, it'd be better to
|
* XXX: For backward compatibility, it'd be better to
|
||||||
|
@ -1228,11 +1275,9 @@ sqlite_st_fetch(SV *sth, imp_sth_t *imp_sth)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sv_setpvn(AvARRAY(av)[i], val, len);
|
sv_setpvn(AvARRAY(av)[i], val, len);
|
||||||
if (imp_dbh->unicode) {
|
|
||||||
SvUTF8_on(AvARRAY(av)[i]);
|
DBD_SQLITE_UTF8_DECODE_IF_NEEDED(AvARRAY(av)[i], imp_dbh->string_mode);
|
||||||
} else {
|
|
||||||
SvUTF8_off(AvARRAY(av)[i]);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case SQLITE_BLOB:
|
case SQLITE_BLOB:
|
||||||
sqlite_trace(sth, imp_sth, 5, form("fetch column %d as blob", i));
|
sqlite_trace(sth, imp_sth, 5, form("fetch column %d as blob", i));
|
||||||
|
@ -1404,8 +1449,9 @@ sqlite_st_FETCH_attrib(SV *sth, imp_sth_t *imp_sth, SV *keysv)
|
||||||
/* if (dot) drop table name from field name */
|
/* if (dot) drop table name from field name */
|
||||||
/* fieldname = ++dot; */
|
/* fieldname = ++dot; */
|
||||||
SV *sv_fieldname = newSVpv(fieldname, 0);
|
SV *sv_fieldname = newSVpv(fieldname, 0);
|
||||||
if (imp_dbh->unicode)
|
|
||||||
SvUTF8_on(sv_fieldname);
|
DBD_SQLITE_UTF8_DECODE_IF_NEEDED(sv_fieldname, imp_dbh->string_mode);
|
||||||
|
|
||||||
av_store(av, n, sv_fieldname);
|
av_store(av, n, sv_fieldname);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1699,7 +1745,7 @@ sqlite_db_busy_timeout(pTHX_ SV *dbh, SV *timeout )
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
sqlite_db_func_dispatcher(int is_unicode, sqlite3_context *context, int argc, sqlite3_value **value)
|
sqlite_db_func_dispatcher(dbd_sqlite_string_mode_t string_mode, sqlite3_context *context, int argc, sqlite3_value **value)
|
||||||
{
|
{
|
||||||
dTHX;
|
dTHX;
|
||||||
dSP;
|
dSP;
|
||||||
|
@ -1714,7 +1760,7 @@ sqlite_db_func_dispatcher(int is_unicode, sqlite3_context *context, int argc, sq
|
||||||
|
|
||||||
PUSHMARK(SP);
|
PUSHMARK(SP);
|
||||||
for ( i=0; i < argc; i++ ) {
|
for ( i=0; i < argc; i++ ) {
|
||||||
XPUSHs(stacked_sv_from_sqlite3_value(aTHX_ value[i], is_unicode));
|
XPUSHs(stacked_sv_from_sqlite3_value(aTHX_ value[i], string_mode));
|
||||||
}
|
}
|
||||||
PUTBACK;
|
PUTBACK;
|
||||||
|
|
||||||
|
@ -1745,17 +1791,46 @@ sqlite_db_func_dispatcher(int is_unicode, sqlite3_context *context, int argc, sq
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
sqlite_db_func_dispatcher_unicode(sqlite3_context *context, int argc, sqlite3_value **value)
|
sqlite_db_func_dispatcher_unicode_naive(sqlite3_context *context, int argc, sqlite3_value **value)
|
||||||
{
|
{
|
||||||
sqlite_db_func_dispatcher(1, context, argc, value);
|
sqlite_db_func_dispatcher(DBD_SQLITE_STRING_MODE_UNICODE_NAIVE, context, argc, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
sqlite_db_func_dispatcher_no_unicode(sqlite3_context *context, int argc, sqlite3_value **value)
|
sqlite_db_func_dispatcher_unicode_fallback(sqlite3_context *context, int argc, sqlite3_value **value)
|
||||||
{
|
{
|
||||||
sqlite_db_func_dispatcher(0, context, argc, value);
|
sqlite_db_func_dispatcher(DBD_SQLITE_STRING_MODE_UNICODE_FALLBACK, context, argc, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sqlite_db_func_dispatcher_unicode_strict(sqlite3_context *context, int argc, sqlite3_value **value)
|
||||||
|
{
|
||||||
|
sqlite_db_func_dispatcher(DBD_SQLITE_STRING_MODE_UNICODE_STRICT, context, argc, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sqlite_db_func_dispatcher_bytes(sqlite3_context *context, int argc, sqlite3_value **value)
|
||||||
|
{
|
||||||
|
sqlite_db_func_dispatcher(DBD_SQLITE_STRING_MODE_BYTES, context, argc, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sqlite_db_func_dispatcher_pv(sqlite3_context *context, int argc, sqlite3_value **value)
|
||||||
|
{
|
||||||
|
sqlite_db_func_dispatcher(DBD_SQLITE_STRING_MODE_PV, context, argc, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef void (*dispatch_func_t)(sqlite3_context*, int, sqlite3_value**);
|
||||||
|
|
||||||
|
static dispatch_func_t _FUNC_DISPATCHER[_DBD_SQLITE_STRING_MODE_COUNT] = {
|
||||||
|
sqlite_db_func_dispatcher_pv,
|
||||||
|
sqlite_db_func_dispatcher_bytes,
|
||||||
|
NULL, NULL,
|
||||||
|
sqlite_db_func_dispatcher_unicode_naive,
|
||||||
|
sqlite_db_func_dispatcher_unicode_fallback,
|
||||||
|
sqlite_db_func_dispatcher_unicode_strict,
|
||||||
|
};
|
||||||
|
|
||||||
int
|
int
|
||||||
sqlite_db_create_function(pTHX_ SV *dbh, const char *name, int argc, SV *func, int flags)
|
sqlite_db_create_function(pTHX_ SV *dbh, const char *name, int argc, SV *func, int flags)
|
||||||
{
|
{
|
||||||
|
@ -1777,8 +1852,7 @@ sqlite_db_create_function(pTHX_ SV *dbh, const char *name, int argc, SV *func, i
|
||||||
/* warn("create_function %s with %d args\n", name, argc); */
|
/* warn("create_function %s with %d args\n", name, argc); */
|
||||||
rc = sqlite3_create_function( imp_dbh->db, name, argc, SQLITE_UTF8|flags,
|
rc = sqlite3_create_function( imp_dbh->db, name, argc, SQLITE_UTF8|flags,
|
||||||
func_sv,
|
func_sv,
|
||||||
imp_dbh->unicode ? sqlite_db_func_dispatcher_unicode
|
_FUNC_DISPATCHER[imp_dbh->string_mode],
|
||||||
: sqlite_db_func_dispatcher_no_unicode,
|
|
||||||
NULL, NULL );
|
NULL, NULL );
|
||||||
if ( rc != SQLITE_OK ) {
|
if ( rc != SQLITE_OK ) {
|
||||||
sqlite_error(dbh, rc, form("sqlite_create_function failed with error %s", sqlite3_errmsg(imp_dbh->db)));
|
sqlite_error(dbh, rc, form("sqlite_create_function failed with error %s", sqlite3_errmsg(imp_dbh->db)));
|
||||||
|
@ -1943,7 +2017,7 @@ sqlite_db_aggr_step_dispatcher(sqlite3_context *context,
|
||||||
{
|
{
|
||||||
dTHX;
|
dTHX;
|
||||||
dSP;
|
dSP;
|
||||||
int i, is_unicode = 0; /* TODO : find out from db handle */
|
int i, string_mode = DBD_SQLITE_STRING_MODE_PV; /* TODO : find out from db handle */
|
||||||
aggrInfo *aggr;
|
aggrInfo *aggr;
|
||||||
|
|
||||||
aggr = sqlite3_aggregate_context(context, sizeof (aggrInfo));
|
aggr = sqlite3_aggregate_context(context, sizeof (aggrInfo));
|
||||||
|
@ -1965,7 +2039,7 @@ sqlite_db_aggr_step_dispatcher(sqlite3_context *context,
|
||||||
PUSHMARK(SP);
|
PUSHMARK(SP);
|
||||||
XPUSHs( sv_2mortal( newSVsv( aggr->aggr_inst ) ));
|
XPUSHs( sv_2mortal( newSVsv( aggr->aggr_inst ) ));
|
||||||
for ( i=0; i < argc; i++ ) {
|
for ( i=0; i < argc; i++ ) {
|
||||||
XPUSHs(stacked_sv_from_sqlite3_value(aTHX_ value[i], is_unicode));
|
XPUSHs(stacked_sv_from_sqlite3_value(aTHX_ value[i], string_mode));
|
||||||
}
|
}
|
||||||
PUTBACK;
|
PUTBACK;
|
||||||
|
|
||||||
|
@ -2081,71 +2155,78 @@ sqlite_db_create_aggregate(pTHX_ SV *dbh, const char *name, int argc, SV *aggr_p
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define SQLITE_DB_COLLATION_BASE(func, sv1, sv2) STMT_START { \
|
||||||
|
int cmp = 0; \
|
||||||
|
int n_retval, i; \
|
||||||
|
\
|
||||||
|
ENTER; \
|
||||||
|
SAVETMPS; \
|
||||||
|
PUSHMARK(SP); \
|
||||||
|
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; \
|
||||||
|
} STMT_END
|
||||||
|
|
||||||
int
|
int
|
||||||
sqlite_db_collation_dispatcher(void *func, int len1, const void *string1,
|
sqlite_db_collation_dispatcher(void *func, int len1, const void *string1,
|
||||||
int len2, const void *string2)
|
int len2, const void *string2)
|
||||||
{
|
{
|
||||||
dTHX;
|
dTHX;
|
||||||
dSP;
|
dSP;
|
||||||
int cmp = 0;
|
|
||||||
int n_retval, i;
|
|
||||||
|
|
||||||
ENTER;
|
SQLITE_DB_COLLATION_BASE(func, newSVpvn( string1, len1), newSVpvn( string2, len2));
|
||||||
SAVETMPS;
|
|
||||||
PUSHMARK(SP);
|
|
||||||
XPUSHs( sv_2mortal( newSVpvn( string1, len1) ) );
|
|
||||||
XPUSHs( sv_2mortal( newSVpvn( string2, len2) ) );
|
|
||||||
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
|
int
|
||||||
sqlite_db_collation_dispatcher_utf8(void *func, int len1, const void *string1,
|
sqlite_db_collation_dispatcher_utf8_naive(void *func, int len1, const void *string1,
|
||||||
int len2, const void *string2)
|
int len2, const void *string2)
|
||||||
{
|
{
|
||||||
dTHX;
|
dTHX;
|
||||||
dSP;
|
dSP;
|
||||||
int cmp = 0;
|
|
||||||
int n_retval, i;
|
|
||||||
SV *sv1, *sv2;
|
|
||||||
|
|
||||||
ENTER;
|
SQLITE_DB_COLLATION_BASE(func, newSVpvn_flags( string1, len1, SVf_UTF8), newSVpvn_flags( string2, len2, SVf_UTF8));
|
||||||
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_collation_dispatcher_utf8_fallback(void *func, int len1, const void *string1,
|
||||||
|
int len2, const void *string2)
|
||||||
|
{
|
||||||
|
dTHX;
|
||||||
|
dSP;
|
||||||
|
|
||||||
|
SV* sv1 = newSVpvn( string1, len1);
|
||||||
|
SV* sv2 = newSVpvn( string2, len2);
|
||||||
|
|
||||||
|
DBD_SQLITE_UTF8_DECODE_WITH_FALLBACK(sv1);
|
||||||
|
DBD_SQLITE_UTF8_DECODE_WITH_FALLBACK(sv2);
|
||||||
|
|
||||||
|
SQLITE_DB_COLLATION_BASE(func, sv1, sv2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef int (*collation_dispatch_func_t)(void *, int, const void *, int, const void *);
|
||||||
|
|
||||||
|
static collation_dispatch_func_t _COLLATION_DISPATCHER[_DBD_SQLITE_STRING_MODE_COUNT] = {
|
||||||
|
sqlite_db_collation_dispatcher,
|
||||||
|
sqlite_db_collation_dispatcher,
|
||||||
|
NULL, NULL,
|
||||||
|
sqlite_db_collation_dispatcher_utf8_naive,
|
||||||
|
sqlite_db_collation_dispatcher_utf8_fallback,
|
||||||
|
sqlite_db_collation_dispatcher_utf8_fallback,
|
||||||
|
};
|
||||||
|
|
||||||
int
|
int
|
||||||
sqlite_db_create_collation(pTHX_ SV *dbh, const char *name, SV *func)
|
sqlite_db_create_collation(pTHX_ SV *dbh, const char *name, SV *func)
|
||||||
{
|
{
|
||||||
|
@ -2181,8 +2262,7 @@ sqlite_db_create_collation(pTHX_ SV *dbh, const char *name, SV *func)
|
||||||
rv = sqlite3_create_collation(
|
rv = sqlite3_create_collation(
|
||||||
imp_dbh->db, name, SQLITE_UTF8,
|
imp_dbh->db, name, SQLITE_UTF8,
|
||||||
func_sv,
|
func_sv,
|
||||||
imp_dbh->unicode ? sqlite_db_collation_dispatcher_utf8
|
_COLLATION_DISPATCHER[imp_dbh->string_mode]
|
||||||
: sqlite_db_collation_dispatcher
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if ( rv != SQLITE_OK ) {
|
if ( rv != SQLITE_OK ) {
|
||||||
|
|
55
dbdimp.h
55
dbdimp.h
|
@ -7,8 +7,24 @@
|
||||||
|
|
||||||
#define MY_CXT_KEY "DBD::SQLite::_guts" XS_VERSION
|
#define MY_CXT_KEY "DBD::SQLite::_guts" XS_VERSION
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
DBD_SQLITE_STRING_MODE_PV,
|
||||||
|
DBD_SQLITE_STRING_MODE_BYTES,
|
||||||
|
|
||||||
|
/* Leave space here so that we can use DBD_SQLITE_STRING_MODE_UNICODE_ANY
|
||||||
|
as a means of checking for any unicode mode. */
|
||||||
|
|
||||||
|
DBD_SQLITE_STRING_MODE_UNICODE_NAIVE = 4,
|
||||||
|
DBD_SQLITE_STRING_MODE_UNICODE_FALLBACK,
|
||||||
|
DBD_SQLITE_STRING_MODE_UNICODE_STRICT,
|
||||||
|
|
||||||
|
_DBD_SQLITE_STRING_MODE_COUNT,
|
||||||
|
} dbd_sqlite_string_mode_t;
|
||||||
|
|
||||||
|
#define DBD_SQLITE_STRING_MODE_UNICODE_ANY DBD_SQLITE_STRING_MODE_UNICODE_NAIVE
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int last_dbh_is_unicode;
|
dbd_sqlite_string_mode_t last_dbh_string_mode;
|
||||||
} my_cxt_t;
|
} my_cxt_t;
|
||||||
|
|
||||||
#define PERL_UNICODE_DOES_NOT_WORK_WELL \
|
#define PERL_UNICODE_DOES_NOT_WORK_WELL \
|
||||||
|
@ -22,6 +38,41 @@ typedef struct {
|
||||||
#define sqlite3_int64 sqlite_int64
|
#define sqlite3_int64 sqlite_int64
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define DBD_SQLITE_UTF8_DECODE_NAIVE(sv) SvUTF8_on(sv)
|
||||||
|
|
||||||
|
#define DBD_SQLITE_UTF8_DECODE_CHECKED(sv, onfail) ( \
|
||||||
|
is_utf8_string((U8*) SvPVX(sv), SvCUR(sv)) \
|
||||||
|
? SvUTF8_on(sv) \
|
||||||
|
: onfail("Received invalid UTF-8 from SQLite; cannot decode!") \
|
||||||
|
)
|
||||||
|
|
||||||
|
#define DBD_SQLITE_UTF8_DECODE_WITH_FALLBACK(sv) ( \
|
||||||
|
DBD_SQLITE_UTF8_DECODE_CHECKED(sv, warn) \
|
||||||
|
)
|
||||||
|
|
||||||
|
#define DBD_SQLITE_UTF8_DECODE_STRICT(sv) ( \
|
||||||
|
DBD_SQLITE_UTF8_DECODE_CHECKED(sv, croak) \
|
||||||
|
)
|
||||||
|
|
||||||
|
#define DBD_SQLITE_PREP_SV_FOR_SQLITE(sv, string_mode) STMT_START { \
|
||||||
|
if (string_mode & DBD_SQLITE_STRING_MODE_UNICODE_ANY) { \
|
||||||
|
sv_utf8_upgrade(sv); \
|
||||||
|
} \
|
||||||
|
else if (string_mode == DBD_SQLITE_STRING_MODE_BYTES) { \
|
||||||
|
sv_utf8_downgrade(sv, 0); \
|
||||||
|
} \
|
||||||
|
} STMT_END
|
||||||
|
|
||||||
|
#define DBD_SQLITE_UTF8_DECODE_IF_NEEDED(sv, string_mode) ( \
|
||||||
|
string_mode == DBD_SQLITE_STRING_MODE_UNICODE_NAIVE \
|
||||||
|
? DBD_SQLITE_UTF8_DECODE_NAIVE(sv) \
|
||||||
|
: string_mode == DBD_SQLITE_STRING_MODE_UNICODE_FALLBACK \
|
||||||
|
? DBD_SQLITE_UTF8_DECODE_WITH_FALLBACK(sv) \
|
||||||
|
: string_mode == DBD_SQLITE_STRING_MODE_UNICODE_STRICT \
|
||||||
|
? DBD_SQLITE_UTF8_DECODE_STRICT(sv) \
|
||||||
|
: 0 \
|
||||||
|
)
|
||||||
|
|
||||||
/* A linked list of statements prepared by this module */
|
/* A linked list of statements prepared by this module */
|
||||||
typedef struct stmt_list_s stmt_list_s;
|
typedef struct stmt_list_s stmt_list_s;
|
||||||
|
|
||||||
|
@ -41,7 +92,7 @@ struct imp_dbh_st {
|
||||||
dbih_dbc_t com;
|
dbih_dbc_t com;
|
||||||
/* sqlite specific bits */
|
/* sqlite specific bits */
|
||||||
sqlite3 *db;
|
sqlite3 *db;
|
||||||
bool unicode;
|
dbd_sqlite_string_mode_t string_mode;
|
||||||
bool handle_binary_nulls;
|
bool handle_binary_nulls;
|
||||||
int timeout;
|
int timeout;
|
||||||
AV *functions;
|
AV *functions;
|
||||||
|
|
|
@ -94,6 +94,30 @@ static int perl_tokenizer_Open(
|
||||||
SV *perl_string;
|
SV *perl_string;
|
||||||
int n_retval;
|
int n_retval;
|
||||||
|
|
||||||
|
/* build a Perl copy of the input string */
|
||||||
|
if (nBytes < 0) { /* we get -1 from fts3. Don't know why ! */
|
||||||
|
nBytes = strlen(pInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SVs_TEMP will call sv_2mortal */
|
||||||
|
perl_string = newSVpvn_flags(pInput, nBytes, SVs_TEMP);
|
||||||
|
|
||||||
|
switch (MY_CXT.last_dbh_string_mode) {
|
||||||
|
DBD_SQLITE_STRING_MODE_UNICODE_NAIVE:
|
||||||
|
DBD_SQLITE_UTF8_DECODE_NAIVE(perl_string);
|
||||||
|
break;
|
||||||
|
|
||||||
|
DBD_SQLITE_STRING_MODE_UNICODE_FALLBACK:
|
||||||
|
DBD_SQLITE_STRING_MODE_UNICODE_STRICT:
|
||||||
|
DBD_SQLITE_UTF8_DECODE_WITH_FALLBACK(perl_string);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBD_SQLITE_UTF8_DECODE_IF_NEEDED(perl_string, MY_CXT.last_dbh_string_mode);
|
||||||
|
|
||||||
perl_tokenizer *t = (perl_tokenizer *)pTokenizer;
|
perl_tokenizer *t = (perl_tokenizer *)pTokenizer;
|
||||||
|
|
||||||
/* allocate and initialize the cursor struct */
|
/* allocate and initialize the cursor struct */
|
||||||
|
@ -102,29 +126,17 @@ static int perl_tokenizer_Open(
|
||||||
memset(c, 0, sizeof(*c));
|
memset(c, 0, sizeof(*c));
|
||||||
*ppCursor = &c->base;
|
*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 */
|
/* special handling if working with utf8 strings */
|
||||||
if (MY_CXT.last_dbh_is_unicode) {
|
if (MY_CXT.last_dbh_string_mode & DBD_SQLITE_STRING_MODE_UNICODE_ANY) {
|
||||||
|
|
||||||
/* data to keep track of byte offsets */
|
/* data to keep track of byte offsets */
|
||||||
c->lastByteOffset = c->pInput = pInput;
|
c->lastByteOffset = c->pInput = pInput;
|
||||||
c->lastCharOffset = 0;
|
c->lastCharOffset = 0;
|
||||||
|
|
||||||
/* string passed to Perl needs to be flagged as utf8 */
|
|
||||||
flags |= SVf_UTF8;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ENTER;
|
ENTER;
|
||||||
SAVETMPS;
|
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 */
|
/* call the tokenizer coderef */
|
||||||
PUSHMARK(SP);
|
PUSHMARK(SP);
|
||||||
XPUSHs(perl_string);
|
XPUSHs(perl_string);
|
||||||
|
|
|
@ -432,7 +432,7 @@ static int perl_vt_Filter( sqlite3_vtab_cursor *pVtabCursor,
|
||||||
dSP;
|
dSP;
|
||||||
dMY_CXT;
|
dMY_CXT;
|
||||||
int i, count;
|
int i, count;
|
||||||
int is_unicode = MY_CXT.last_dbh_is_unicode;
|
dbd_sqlite_string_mode_t string_mode = MY_CXT.last_dbh_string_mode;
|
||||||
|
|
||||||
ENTER;
|
ENTER;
|
||||||
SAVETMPS;
|
SAVETMPS;
|
||||||
|
@ -443,7 +443,7 @@ static int perl_vt_Filter( sqlite3_vtab_cursor *pVtabCursor,
|
||||||
XPUSHs(sv_2mortal(newSViv(idxNum)));
|
XPUSHs(sv_2mortal(newSViv(idxNum)));
|
||||||
XPUSHs(sv_2mortal(newSVpv(idxStr, 0)));
|
XPUSHs(sv_2mortal(newSVpv(idxStr, 0)));
|
||||||
for(i = 0; i < argc; i++) {
|
for(i = 0; i < argc; i++) {
|
||||||
XPUSHs(stacked_sv_from_sqlite3_value(aTHX_ argv[i], is_unicode));
|
XPUSHs(stacked_sv_from_sqlite3_value(aTHX_ argv[i], string_mode));
|
||||||
}
|
}
|
||||||
PUTBACK;
|
PUTBACK;
|
||||||
count = call_method("FILTER", G_VOID);
|
count = call_method("FILTER", G_VOID);
|
||||||
|
@ -588,7 +588,7 @@ static int perl_vt_Update( sqlite3_vtab *pVTab,
|
||||||
dSP;
|
dSP;
|
||||||
dMY_CXT;
|
dMY_CXT;
|
||||||
int count, i;
|
int count, i;
|
||||||
int is_unicode = MY_CXT.last_dbh_is_unicode;
|
dbd_sqlite_string_mode_t string_mode = MY_CXT.last_dbh_string_mode;
|
||||||
int rc = SQLITE_ERROR;
|
int rc = SQLITE_ERROR;
|
||||||
SV *rowidsv;
|
SV *rowidsv;
|
||||||
|
|
||||||
|
@ -599,7 +599,7 @@ static int perl_vt_Update( sqlite3_vtab *pVTab,
|
||||||
PUSHMARK(SP);
|
PUSHMARK(SP);
|
||||||
XPUSHs(((perl_vtab *) pVTab)->perl_vtab_obj);
|
XPUSHs(((perl_vtab *) pVTab)->perl_vtab_obj);
|
||||||
for(i = 0; i < argc; i++) {
|
for(i = 0; i < argc; i++) {
|
||||||
XPUSHs(stacked_sv_from_sqlite3_value(aTHX_ argv[i], is_unicode));
|
XPUSHs(stacked_sv_from_sqlite3_value(aTHX_ argv[i], string_mode));
|
||||||
}
|
}
|
||||||
PUTBACK;
|
PUTBACK;
|
||||||
count = call_method ("_SQLITE_UPDATE", G_SCALAR);
|
count = call_method ("_SQLITE_UPDATE", G_SCALAR);
|
||||||
|
@ -704,8 +704,9 @@ static int perl_vt_FindFunction(sqlite3_vtab *pVTab,
|
||||||
/* return function information for sqlite3 within *pxFunc and *ppArg */
|
/* return function information for sqlite3 within *pxFunc and *ppArg */
|
||||||
is_overloaded = coderef && SvTRUE(coderef);
|
is_overloaded = coderef && SvTRUE(coderef);
|
||||||
if (is_overloaded) {
|
if (is_overloaded) {
|
||||||
*pxFunc = MY_CXT.last_dbh_is_unicode ? sqlite_db_func_dispatcher_unicode
|
|
||||||
: sqlite_db_func_dispatcher_no_unicode;
|
*pxFunc = _FUNC_DISPATCHER[MY_CXT.last_dbh_string_mode];
|
||||||
|
|
||||||
*ppArg = coderef;
|
*ppArg = coderef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1622,22 +1622,39 @@ Your sweet spot probably lies somewhere in between.
|
||||||
Returns the version of the SQLite library which B<DBD::SQLite> is using,
|
Returns the version of the SQLite library which B<DBD::SQLite> is using,
|
||||||
e.g., "3.26.0". Can only be read.
|
e.g., "3.26.0". Can only be read.
|
||||||
|
|
||||||
=item sqlite_unicode
|
=item sqlite_string_mode
|
||||||
|
|
||||||
If set to a true value, B<DBD::SQLite> will turn the UTF-8 flag on for all
|
SQLite strings are simple arrays of bytes, but Perl strings can store any
|
||||||
text strings coming out of the database (this feature is currently disabled
|
arbitrary Unicode code point. Thus, DBD::SQLite has to adopt some method
|
||||||
for perl < 5.8.5). For more details on the UTF-8 flag see
|
of translating between those two models. This parameter defines that
|
||||||
L<perlunicode>. The default is for the UTF-8 flag to be turned off.
|
translation.
|
||||||
|
|
||||||
Also note that due to some bizarreness in SQLite's type system (see
|
Accepted values are the following constants:
|
||||||
|
|
||||||
|
=over
|
||||||
|
|
||||||
|
=item * DBD_SQLITE_STRING_MODE_BYTES: All strings are assumed to
|
||||||
|
represent bytes. A Perl string that contains any code point above 255
|
||||||
|
will trigger an exception. This is appropriate for Latin-1 strings,
|
||||||
|
binary data, pre-encoded UTF-8 strings, etc.
|
||||||
|
|
||||||
|
=item * DBD_SQLITE_STRING_MODE_UNICODE_FALLBACK: All Perl strings are encoded
|
||||||
|
to UTF-8 before being given to SQLite. Perl will B<try> to decode SQLite
|
||||||
|
strings as UTF-8 when giving them to Perl. Should any such string not be
|
||||||
|
valid UTF-8, a warning is thrown, and the string is left undecoded.
|
||||||
|
|
||||||
|
This is appropriate for strings that are decoded to characters via,
|
||||||
|
e.g., L<Encode/decode>.
|
||||||
|
|
||||||
|
Also note that, due to some bizarreness in SQLite's type system (see
|
||||||
L<https://www.sqlite.org/datatype3.html>), if you want to retain
|
L<https://www.sqlite.org/datatype3.html>), if you want to retain
|
||||||
blob-style behavior for B<some> columns under C<< $dbh->{sqlite_unicode} = 1
|
blob-style behavior for B<some> columns under DBD_SQLITE_STRING_MODE_UNICODE
|
||||||
>> (say, to store images in the database), you have to state so
|
(say, to store images in the database), you have to state so
|
||||||
explicitly using the 3-argument form of L<DBI/bind_param> when doing
|
explicitly using the 3-argument form of L<DBI/bind_param> when doing
|
||||||
updates:
|
updates:
|
||||||
|
|
||||||
use DBI qw(:sql_types);
|
use DBI qw(:sql_types);
|
||||||
$dbh->{sqlite_unicode} = 1;
|
$dbh->{string_mode} = DBD_SQLITE_STRING_MODE_UNICODE;
|
||||||
my $sth = $dbh->prepare("INSERT INTO mytable (blobcolumn) VALUES (?)");
|
my $sth = $dbh->prepare("INSERT INTO mytable (blobcolumn) VALUES (?)");
|
||||||
|
|
||||||
# Binary_data will be stored as is.
|
# Binary_data will be stored as is.
|
||||||
|
@ -1645,9 +1662,31 @@ updates:
|
||||||
|
|
||||||
Defining the column type as C<BLOB> in the DDL is B<not> sufficient.
|
Defining the column type as C<BLOB> in the DDL is B<not> sufficient.
|
||||||
|
|
||||||
This attribute was originally named as C<unicode>, and renamed to
|
=item * DBD_SQLITE_STRING_MODE_UNICODE_STRICT: Like
|
||||||
C<sqlite_unicode> for integrity since version 1.26_06. Old C<unicode>
|
DBD_SQLITE_STRING_MODE_UNICODE_FALLBACK but usually throws an exception
|
||||||
attribute is still accessible but will be deprecated in the near future.
|
rather than a warning if SQLite sends invalid UTF-8. (In Perl callbacks
|
||||||
|
from SQLite we still warn instead.)
|
||||||
|
|
||||||
|
=item * DBD_SQLITE_STRING_MODULE_UNICODE_NAIVE: Like
|
||||||
|
DBD_SQLITE_STRING_MODE_UNICODE_FALLBACK but uses a "naïve" UTF-8 decoding
|
||||||
|
method that forgoes validation. This is marginally faster than a validated
|
||||||
|
decode, but it can also B<corrupt> B<Perl> B<itself!>
|
||||||
|
|
||||||
|
=item * DBD_SQLITE_STRING_MODE_PV (default, but B<DO> B<NOT> B<USE>): Like
|
||||||
|
DBD_SQLITE_STRING_MODE_BYTES, but when translating Perl strings to SQLite
|
||||||
|
the Perl string's internal byte buffer is given to SQLite. B<This> B<is>
|
||||||
|
B<bad>, but it's been the default for many years, and changing that would
|
||||||
|
break existing applications.
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=item C<sqlite_unicode> or C<unicode> (deprecated)
|
||||||
|
|
||||||
|
If truthy, equivalent to setting C<sqlite_string_mode> to
|
||||||
|
DBD_SQLITE_STRING_MODE_UNICODE_NAIVE; if falsy, equivalent to
|
||||||
|
DBD_SQLITE_STRING_MODE_PV.
|
||||||
|
|
||||||
|
Prefer C<sqlite_string_mode> in all new code.
|
||||||
|
|
||||||
=item sqlite_allow_multiple_statements
|
=item sqlite_allow_multiple_statements
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,11 @@ use warnings;
|
||||||
use base 'Exporter';
|
use base 'Exporter';
|
||||||
use DBD::SQLite;
|
use DBD::SQLite;
|
||||||
our @EXPORT_OK = (
|
our @EXPORT_OK = (
|
||||||
|
'DBD_SQLITE_STRING_MODE_PV',
|
||||||
|
'DBD_SQLITE_STRING_MODE_BYTES',
|
||||||
|
'DBD_SQLITE_STRING_MODE_UNICODE_NAIVE',
|
||||||
|
'DBD_SQLITE_STRING_MODE_UNICODE_FALLBACK',
|
||||||
|
'DBD_SQLITE_STRING_MODE_UNICODE_STRICT',
|
||||||
# allowed_return_values_from_sqlite3_txn_state
|
# allowed_return_values_from_sqlite3_txn_state
|
||||||
qw/
|
qw/
|
||||||
SQLITE_TXN_NONE
|
SQLITE_TXN_NONE
|
||||||
|
|
|
@ -7,6 +7,8 @@ use SQLiteTest qw/connect_ok @CALL_FUNCS/;
|
||||||
use Test::More;
|
use Test::More;
|
||||||
use if -d ".git", "Test::FailWarnings";
|
use if -d ".git", "Test::FailWarnings";
|
||||||
|
|
||||||
|
my $unicode_opt = DBD::SQLite::Constants::DBD_SQLITE_STRING_MODE_UNICODE_STRICT;
|
||||||
|
|
||||||
my $show_diag = 0;
|
my $show_diag = 0;
|
||||||
foreach my $call_func (@CALL_FUNCS) {
|
foreach my $call_func (@CALL_FUNCS) {
|
||||||
|
|
||||||
|
@ -30,9 +32,9 @@ foreach my $call_func (@CALL_FUNCS) {
|
||||||
skip( 'Unicode is not supported before 5.8.5', 2 );
|
skip( 'Unicode is not supported before 5.8.5', 2 );
|
||||||
}
|
}
|
||||||
my $file = 'foo'.$$;
|
my $file = 'foo'.$$;
|
||||||
my $dbh = DBI->connect( "dbi:SQLite:dbname=$file;sqlite_unicode=1", '', '' );
|
my $dbh = DBI->connect( "dbi:SQLite:dbname=$file;sqlite_string_mode=$unicode_opt", '', '' );
|
||||||
isa_ok( $dbh, 'DBI::db' );
|
isa_ok( $dbh, 'DBI::db' );
|
||||||
is( $dbh->{sqlite_unicode}, 1, 'Unicode is on' );
|
is( $dbh->{sqlite_string_mode}, $unicode_opt, 'Unicode is on' );
|
||||||
$dbh->disconnect;
|
$dbh->disconnect;
|
||||||
unlink $file;
|
unlink $file;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,8 @@ use SQLiteTest;
|
||||||
use Test::More;
|
use Test::More;
|
||||||
use if -d ".git", "Test::FailWarnings";
|
use if -d ".git", "Test::FailWarnings";
|
||||||
|
|
||||||
|
my $unicode_opt = DBD::SQLite::Constants::DBD_SQLITE_STRING_MODE_UNICODE_STRICT;
|
||||||
|
|
||||||
BEGIN { requires_unicode_support() }
|
BEGIN { requires_unicode_support() }
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -51,7 +53,7 @@ ok(
|
||||||
my ($textback, $bytesback);
|
my ($textback, $bytesback);
|
||||||
SCOPE: {
|
SCOPE: {
|
||||||
my $dbh = connect_ok( dbfile => 'foo', RaiseError => 1 );
|
my $dbh = connect_ok( dbfile => 'foo', RaiseError => 1 );
|
||||||
is( $dbh->{sqlite_unicode}, 0, 'Unicode is off' );
|
is( $dbh->{sqlite_string_mode}, DBD::SQLite::Constants::DBD_SQLITE_STRING_MODE_PV, 'default string mode is pv' );
|
||||||
ok(
|
ok(
|
||||||
$dbh->do("CREATE TABLE table1 (a TEXT, b BLOB)"),
|
$dbh->do("CREATE TABLE table1 (a TEXT, b BLOB)"),
|
||||||
'CREATE TABLE',
|
'CREATE TABLE',
|
||||||
|
@ -73,8 +75,8 @@ SCOPE: {
|
||||||
|
|
||||||
# Start over but now activate Unicode support.
|
# Start over but now activate Unicode support.
|
||||||
SCOPE: {
|
SCOPE: {
|
||||||
my $dbh = connect_ok( dbfile => 'foo', sqlite_unicode => 1 );
|
my $dbh = connect_ok( dbfile => 'foo', sqlite_string_mode => $unicode_opt );
|
||||||
is( $dbh->{sqlite_unicode}, 1, 'Unicode is on' );
|
is( $dbh->{sqlite_string_mode}, $unicode_opt, 'Unicode is on' );
|
||||||
|
|
||||||
($textback, $bytesback) = database_roundtrip($dbh, $utfstring, $bytestring);
|
($textback, $bytesback) = database_roundtrip($dbh, $utfstring, $bytestring);
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,8 @@ use if -d ".git", "Test::FailWarnings";
|
||||||
use Encode qw/decode/;
|
use Encode qw/decode/;
|
||||||
use DBD::SQLite;
|
use DBD::SQLite;
|
||||||
|
|
||||||
|
my $unicode_opt = DBD::SQLite::Constants::DBD_SQLITE_STRING_MODE_UNICODE_STRICT;
|
||||||
|
|
||||||
BEGIN { requires_unicode_support(); }
|
BEGIN { requires_unicode_support(); }
|
||||||
|
|
||||||
BEGIN {
|
BEGIN {
|
||||||
|
@ -71,10 +73,10 @@ is($DBD::SQLite::COLLATION{foo}, \&by_num_desc, "overridden collation");
|
||||||
|
|
||||||
foreach my $call_func (@CALL_FUNCS) {
|
foreach my $call_func (@CALL_FUNCS) {
|
||||||
|
|
||||||
for my $use_unicode (0, 1) {
|
for my $unicode_opt ( DBD::SQLite::Constants::DBD_SQLITE_STRING_MODE_BYTES, DBD::SQLite::Constants::DBD_SQLITE_STRING_MODE_UNICODE_STRICT) {
|
||||||
|
|
||||||
# connect
|
# connect
|
||||||
my $dbh = connect_ok( RaiseError => 1, sqlite_unicode => $use_unicode );
|
my $dbh = connect_ok( RaiseError => 1, sqlite_string_mode => $unicode_opt );
|
||||||
|
|
||||||
# populate test data
|
# populate test data
|
||||||
my @words = qw{
|
my @words = qw{
|
||||||
|
@ -84,7 +86,7 @@ foreach my $call_func (@CALL_FUNCS) {
|
||||||
HAT hâôer
|
HAT hâôer
|
||||||
féôu fêôe fèöe ferme
|
féôu fêôe fèöe ferme
|
||||||
};
|
};
|
||||||
if ($use_unicode) {
|
if ($unicode_opt != DBD::SQLite::Constants::DBD_SQLITE_STRING_MODE_BYTES) {
|
||||||
utf8::upgrade($_) foreach @words;
|
utf8::upgrade($_) foreach @words;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,8 @@ use if -d ".git", "Test::FailWarnings";
|
||||||
use File::Temp ();
|
use File::Temp ();
|
||||||
use File::Spec::Functions ':ALL';
|
use File::Spec::Functions ':ALL';
|
||||||
|
|
||||||
|
my $unicode_opt = DBD::SQLite::Constants::DBD_SQLITE_STRING_MODE_UNICODE_STRICT;
|
||||||
|
|
||||||
BEGIN { requires_unicode_support() }
|
BEGIN { requires_unicode_support() }
|
||||||
|
|
||||||
my $dir = File::Temp::tempdir( CLEANUP => 1 );
|
my $dir = File::Temp::tempdir( CLEANUP => 1 );
|
||||||
|
@ -55,7 +57,7 @@ foreach my $subdir ( 'longascii', 'adatb
|
||||||
my $dbh = DBI->connect("dbi:SQLite:dbname=$dbfile", undef, undef, {
|
my $dbh = DBI->connect("dbi:SQLite:dbname=$dbfile", undef, undef, {
|
||||||
RaiseError => 1,
|
RaiseError => 1,
|
||||||
PrintError => 0,
|
PrintError => 0,
|
||||||
sqlite_unicode => 1,
|
sqlite_string_mode => $unicode_opt,
|
||||||
} );
|
} );
|
||||||
isa_ok( $dbh, 'DBI::db' );
|
isa_ok( $dbh, 'DBI::db' );
|
||||||
};
|
};
|
||||||
|
@ -67,7 +69,7 @@ foreach my $subdir ( 'longascii', 'adatb
|
||||||
my $dbh = DBI->connect("dbi:SQLite:dbname=$dbfile", undef, undef, {
|
my $dbh = DBI->connect("dbi:SQLite:dbname=$dbfile", undef, undef, {
|
||||||
RaiseError => 1,
|
RaiseError => 1,
|
||||||
PrintError => 0,
|
PrintError => 0,
|
||||||
sqlite_unicode => 1,
|
sqlite_string_mode => $unicode_opt,
|
||||||
} );
|
} );
|
||||||
isa_ok( $dbh, 'DBI::db' );
|
isa_ok( $dbh, 'DBI::db' );
|
||||||
};
|
};
|
||||||
|
|
|
@ -31,10 +31,10 @@ use DBD::SQLite;
|
||||||
|
|
||||||
foreach my $call_func (@CALL_FUNCS) {
|
foreach my $call_func (@CALL_FUNCS) {
|
||||||
|
|
||||||
for my $use_unicode (0, 1) {
|
for my $string_mode (DBD::SQLite::Constants::DBD_SQLITE_STRING_MODE_BYTES, DBD::SQLite::Constants::DBD_SQLITE_STRING_MODE_UNICODE_STRICT) {
|
||||||
|
|
||||||
# connect
|
# connect
|
||||||
my $dbh = connect_ok( RaiseError => 1, sqlite_unicode => $use_unicode );
|
my $dbh = connect_ok( RaiseError => 1, sqlite_string_mode => $string_mode );
|
||||||
|
|
||||||
# The following tests are about ordering, so don't reverse!
|
# The following tests are about ordering, so don't reverse!
|
||||||
if ($dbh->selectrow_array('PRAGMA reverse_unordered_selects')) {
|
if ($dbh->selectrow_array('PRAGMA reverse_unordered_selects')) {
|
||||||
|
@ -43,7 +43,7 @@ foreach my $call_func (@CALL_FUNCS) {
|
||||||
|
|
||||||
# populate test data
|
# populate test data
|
||||||
my @vals = @words;
|
my @vals = @words;
|
||||||
if ($use_unicode) {
|
if ($string_mode == DBD::SQLite::Constants::DBD_SQLITE_STRING_MODE_BYTES) {
|
||||||
utf8::upgrade($_) foreach @vals;
|
utf8::upgrade($_) foreach @vals;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -67,10 +67,10 @@ sub locale_tokenizer { # see also: Search::Tokenizer
|
||||||
|
|
||||||
use DBD::SQLite;
|
use DBD::SQLite;
|
||||||
|
|
||||||
for my $use_unicode (0, 1) {
|
for my $string_mode (DBD::SQLite::Constants::DBD_SQLITE_STRING_MODE_BYTES, DBD::SQLite::Constants::DBD_SQLITE_STRING_MODE_UNICODE_STRICT) {
|
||||||
|
|
||||||
# connect
|
# connect
|
||||||
my $dbh = connect_ok( RaiseError => 1, sqlite_unicode => $use_unicode );
|
my $dbh = connect_ok( RaiseError => 1, sqlite_string_mode => $string_mode );
|
||||||
|
|
||||||
for my $fts (qw/fts3 fts4/) {
|
for my $fts (qw/fts3 fts4/) {
|
||||||
next if $fts eq 'fts4' && !has_sqlite('3.7.4');
|
next if $fts eq 'fts4' && !has_sqlite('3.7.4');
|
||||||
|
@ -102,7 +102,7 @@ for my $use_unicode (0, 1) {
|
||||||
my ($query, @expected) = @$t;
|
my ($query, @expected) = @$t;
|
||||||
@expected = map {$doc_ids[$_]} @expected;
|
@expected = map {$doc_ids[$_]} @expected;
|
||||||
my $results = $dbh->selectcol_arrayref($sql, undef, $query);
|
my $results = $dbh->selectcol_arrayref($sql, undef, $query);
|
||||||
is_deeply($results, \@expected, "$query ($fts, unicode=$use_unicode)");
|
is_deeply($results, \@expected, "$query ($fts, string_mode=$string_mode)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,11 +5,13 @@ use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
use lib "t/lib";
|
use lib "t/lib";
|
||||||
use SQLiteTest;
|
use SQLiteTest;
|
||||||
|
use DBD::SQLite::Constants;
|
||||||
use Test::More;
|
use Test::More;
|
||||||
use if -d ".git", "Test::FailWarnings";
|
use if -d ".git", "Test::FailWarnings";
|
||||||
|
|
||||||
{
|
{
|
||||||
my $dbh = connect_ok( dbfile => 'foo', RaiseError => 1 );
|
my $dbh = connect_ok( dbfile => 'foo', RaiseError => 1 );
|
||||||
|
$dbh->{sqlite_string_mode} = DBD::SQLite::Constants::DBD_SQLITE_STRING_MODE_BYTES;
|
||||||
|
|
||||||
my $tbl_name = "\xe9p\xe9e";
|
my $tbl_name = "\xe9p\xe9e";
|
||||||
utf8::encode $tbl_name;
|
utf8::encode $tbl_name;
|
||||||
|
|
|
@ -5,10 +5,12 @@ use SQLiteTest;
|
||||||
use Test::More;
|
use Test::More;
|
||||||
use if -d ".git", "Test::FailWarnings";
|
use if -d ".git", "Test::FailWarnings";
|
||||||
|
|
||||||
|
my $unicode_opt = DBD::SQLite::Constants::DBD_SQLITE_STRING_MODE_UNICODE_STRICT;
|
||||||
|
|
||||||
BEGIN { requires_unicode_support(); }
|
BEGIN { requires_unicode_support(); }
|
||||||
|
|
||||||
my $dbh = connect_ok( sqlite_unicode => 1 );
|
my $dbh = connect_ok( sqlite_string_mode => $unicode_opt );
|
||||||
is( $dbh->{sqlite_unicode}, 1, 'Unicode is on' );
|
is( $dbh->{sqlite_string_mode}, $unicode_opt, 'string mode is unicode/strict' );
|
||||||
|
|
||||||
ok( $dbh->do(<<'END_SQL'), 'CREATE TABLE' );
|
ok( $dbh->do(<<'END_SQL'), 'CREATE TABLE' );
|
||||||
CREATE TABLE foo (
|
CREATE TABLE foo (
|
||||||
|
|
|
@ -5,10 +5,12 @@ use SQLiteTest;
|
||||||
use Test::More;
|
use Test::More;
|
||||||
use if -d ".git", "Test::FailWarnings";
|
use if -d ".git", "Test::FailWarnings";
|
||||||
|
|
||||||
|
my $unicode_opt = DBD::SQLite::Constants::DBD_SQLITE_STRING_MODE_UNICODE_STRICT;
|
||||||
|
|
||||||
BEGIN { requires_unicode_support() }
|
BEGIN { requires_unicode_support() }
|
||||||
|
|
||||||
foreach my $call_func (@CALL_FUNCS) {
|
foreach my $call_func (@CALL_FUNCS) {
|
||||||
my $dbh = connect_ok( sqlite_unicode => 1 );
|
my $dbh = connect_ok( sqlite_string_mode => $unicode_opt );
|
||||||
ok($dbh->$call_func( "perl_uc", 1, \&perl_uc, "create_function" ));
|
ok($dbh->$call_func( "perl_uc", 1, \&perl_uc, "create_function" ));
|
||||||
|
|
||||||
ok( $dbh->do(<<'END_SQL'), 'CREATE TABLE' );
|
ok( $dbh->do(<<'END_SQL'), 'CREATE TABLE' );
|
||||||
|
|
|
@ -6,9 +6,11 @@ use Test::More;
|
||||||
use if -d ".git", "Test::FailWarnings";
|
use if -d ".git", "Test::FailWarnings";
|
||||||
use DBI qw/:sql_types/;
|
use DBI qw/:sql_types/;
|
||||||
|
|
||||||
|
use DBD::SQLite::Constants;
|
||||||
|
|
||||||
BEGIN{ requires_unicode_support(); }
|
BEGIN{ requires_unicode_support(); }
|
||||||
|
|
||||||
my $dbh = connect_ok(sqlite_unicode => 1);
|
my $dbh = connect_ok(sqlite_string_mode => DBD::SQLite::Constants::DBD_SQLITE_STRING_MODE_UNICODE_STRICT);
|
||||||
$dbh->do('create table test1 (id integer, b blob)');
|
$dbh->do('create table test1 (id integer, b blob)');
|
||||||
|
|
||||||
my $blob = "\x{82}\x{A0}";
|
my $blob = "\x{82}\x{A0}";
|
||||||
|
|
|
@ -6,6 +6,8 @@ use Test::More;
|
||||||
use if -d ".git", "Test::FailWarnings";
|
use if -d ".git", "Test::FailWarnings";
|
||||||
use Encode;
|
use Encode;
|
||||||
|
|
||||||
|
use DBD::SQLite::Constants;
|
||||||
|
|
||||||
BEGIN { requires_unicode_support() }
|
BEGIN { requires_unicode_support() }
|
||||||
|
|
||||||
unicode_test("\x{263A}"); # (decoded) smiley character
|
unicode_test("\x{263A}"); # (decoded) smiley character
|
||||||
|
@ -20,7 +22,7 @@ sub unicode_test {
|
||||||
|
|
||||||
{ # tests for an environment where everything is encoded
|
{ # tests for an environment where everything is encoded
|
||||||
|
|
||||||
my $dbh = connect_ok(sqlite_unicode => 0);
|
my $dbh = connect_ok(sqlite_string_mode => DBD::SQLite::Constants::DBD_SQLITE_STRING_MODE_BYTES);
|
||||||
$dbh->do("pragma foreign_keys = on");
|
$dbh->do("pragma foreign_keys = on");
|
||||||
my $unicode_quoted = $dbh->quote_identifier($unicode_encoded);
|
my $unicode_quoted = $dbh->quote_identifier($unicode_encoded);
|
||||||
$dbh->do("create table $unicode_quoted (id, $unicode_quoted primary key)");
|
$dbh->do("create table $unicode_quoted (id, $unicode_quoted primary key)");
|
||||||
|
@ -87,7 +89,7 @@ sub unicode_test {
|
||||||
}
|
}
|
||||||
|
|
||||||
{ # tests for an environment where everything is decoded
|
{ # tests for an environment where everything is decoded
|
||||||
my $dbh = connect_ok(sqlite_unicode => 1);
|
my $dbh = connect_ok(sqlite_string_mode => DBD::SQLite::Constants::DBD_SQLITE_STRING_MODE_UNICODE_STRICT);
|
||||||
$dbh->do("pragma foreign_keys = on");
|
$dbh->do("pragma foreign_keys = on");
|
||||||
my $unicode_quoted = $dbh->quote_identifier($unicode);
|
my $unicode_quoted = $dbh->quote_identifier($unicode);
|
||||||
$dbh->do("create table $unicode_quoted (id, $unicode_quoted primary key)");
|
$dbh->do("create table $unicode_quoted (id, $unicode_quoted primary key)");
|
||||||
|
|
|
@ -11,8 +11,8 @@ use if -d ".git", "Test::FailWarnings";
|
||||||
|
|
||||||
BEGIN { requires_unicode_support() }
|
BEGIN { requires_unicode_support() }
|
||||||
|
|
||||||
my $dbh = connect_ok( sqlite_unicode => 1 );
|
my $dbh = connect_ok( sqlite_string_mode => DBD::SQLite::Constants::DBD_SQLITE_STRING_MODE_UNICODE_NAIVE );
|
||||||
is( $dbh->{sqlite_unicode}, 1, 'Unicode is on' );
|
is( $dbh->{sqlite_string_mode}, DBD::SQLite::Constants::DBD_SQLITE_STRING_MODE_UNICODE_NAIVE, 'Unicode is on' );
|
||||||
|
|
||||||
ok( $dbh->do(<<'END_SQL'), 'CREATE TABLE' );
|
ok( $dbh->do(<<'END_SQL'), 'CREATE TABLE' );
|
||||||
CREATE TABLE foo (
|
CREATE TABLE foo (
|
||||||
|
|
|
@ -17,6 +17,14 @@ my %shorter_tags = (
|
||||||
compile_time_library_version_numbers => 'version',
|
compile_time_library_version_numbers => 'version',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
my @dbd_sqlite_constants = (
|
||||||
|
'DBD_SQLITE_STRING_MODE_PV',
|
||||||
|
'DBD_SQLITE_STRING_MODE_BYTES',
|
||||||
|
'DBD_SQLITE_STRING_MODE_UNICODE_NAIVE',
|
||||||
|
'DBD_SQLITE_STRING_MODE_UNICODE_FALLBACK',
|
||||||
|
'DBD_SQLITE_STRING_MODE_UNICODE_STRICT',
|
||||||
|
);
|
||||||
|
|
||||||
my %constants = extract_constants();
|
my %constants = extract_constants();
|
||||||
write_inc(%constants);
|
write_inc(%constants);
|
||||||
write_pm(%constants);
|
write_pm(%constants);
|
||||||
|
@ -34,8 +42,13 @@ MODULE = DBD::SQLite PACKAGE = DBD::SQLite::Constants
|
||||||
|
|
||||||
PROTOTYPES: ENABLE
|
PROTOTYPES: ENABLE
|
||||||
|
|
||||||
|
BOOT:
|
||||||
END
|
END
|
||||||
|
|
||||||
|
for my $constsub (@dbd_sqlite_constants) {
|
||||||
|
print {$fh} qq< newCONSTSUB( gv_stashpv("DBD::SQLite::Constants", FALSE), "$constsub", newSVuv($constsub) );\n>
|
||||||
|
}
|
||||||
|
|
||||||
for my $tag (sort grep !/^_/, keys %constants) {
|
for my $tag (sort grep !/^_/, keys %constants) {
|
||||||
_write_tag($fh, $tag, $constants{$tag});
|
_write_tag($fh, $tag, $constants{$tag});
|
||||||
}
|
}
|
||||||
|
@ -144,6 +157,10 @@ use DBD::SQLite;
|
||||||
our \@EXPORT_OK = (
|
our \@EXPORT_OK = (
|
||||||
END
|
END
|
||||||
|
|
||||||
|
for my $const (@dbd_sqlite_constants) {
|
||||||
|
print {$fh} " '$const',\n";
|
||||||
|
}
|
||||||
|
|
||||||
for my $tag (sort keys %constants) {
|
for my $tag (sort keys %constants) {
|
||||||
print $fh <<"END";
|
print $fh <<"END";
|
||||||
# $tag
|
# $tag
|
||||||
|
|
Loading…
Add table
Reference in a new issue