diff options
Diffstat (limited to 'ext')
747 files changed, 26638 insertions, 10862 deletions
diff --git a/ext/bcmath/bcmath.c b/ext/bcmath/bcmath.c index af3a92681..d14a7a270 100644 --- a/ext/bcmath/bcmath.c +++ b/ext/bcmath/bcmath.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: bcmath.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: bcmath.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -106,7 +106,7 @@ const zend_function_entry bcmath_functions[] = { PHP_FE(bcscale, arginfo_bcscale) PHP_FE(bccomp, arginfo_bccomp) PHP_FE(bcpowmod, arginfo_bcpowmod) - {NULL, NULL, NULL} + PHP_FE_END }; zend_module_entry bcmath_module_entry = { diff --git a/ext/bz2/bz2.c b/ext/bz2/bz2.c index b2b9b6d52..b8a6d7cd3 100644 --- a/ext/bz2/bz2.c +++ b/ext/bz2/bz2.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: bz2.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: bz2.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -107,7 +107,7 @@ static const zend_function_entry bz2_functions[] = { PHP_FE(bzerror, arginfo_bzerror) PHP_FE(bzcompress, arginfo_bzcompress) PHP_FE(bzdecompress, arginfo_bzdecompress) - {NULL, NULL, NULL} + PHP_FE_END }; zend_module_entry bz2_module_entry = { diff --git a/ext/bz2/tests/bug51997.phpt b/ext/bz2/tests/bug51997.phpt new file mode 100644 index 000000000..fea539894 --- /dev/null +++ b/ext/bz2/tests/bug51997.phpt @@ -0,0 +1,24 @@ +--TEST-- +Bug #51997 (SEEK_CUR with 0 value, returns a warning) +--SKIPIF-- +<?php if (!extension_loaded("bz2")) print "skip"; ?> +--FILE-- +<?php + +error_reporting(E_ALL); + +$filename = "testfile.bz2"; +$str = "This is a test string.\n"; +$bz = bzopen($filename, "w"); +bzwrite($bz, $str); +bzclose($bz); + +$bz = bzopen($filename, "r"); +fseek($bz, 0, SEEK_CUR); +print bzread($bz, 10); +print bzread($bz); +bzclose($bz); +unlink($filename); + +--EXPECT-- +This is a test string. diff --git a/ext/calendar/calendar.c b/ext/calendar/calendar.c index 422bfc676..d802b5b69 100644 --- a/ext/calendar/calendar.c +++ b/ext/calendar/calendar.c @@ -18,7 +18,7 @@ | Wez Furlong <wez@thebrainroom.com> | +----------------------------------------------------------------------+ */ -/* $Id: calendar.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: calendar.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -148,7 +148,7 @@ const zend_function_entry calendar_functions[] = { PHP_FE(cal_from_jd, arginfo_cal_from_jd) PHP_FE(cal_days_in_month, arginfo_cal_days_in_month) PHP_FE(cal_info, arginfo_cal_info) - {NULL, NULL, NULL} + PHP_FE_END }; diff --git a/ext/com_dotnet/com_dotnet.c b/ext/com_dotnet/com_dotnet.c index 049d95439..546e8750c 100644 --- a/ext/com_dotnet/com_dotnet.c +++ b/ext/com_dotnet/com_dotnet.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: com_dotnet.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: com_dotnet.c 313827 2011-07-28 10:34:16Z pajoye $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -129,6 +129,9 @@ static HRESULT dotnet_init(char **p_where TSRMLS_DC) char *where = ""; stuff = malloc(sizeof(*stuff)); + if (!stuff) { + return S_FALSE; + } memset(stuff, 0, sizeof(*stuff)); where = "CoCreateInstance"; diff --git a/ext/com_dotnet/com_extension.c b/ext/com_dotnet/com_extension.c index 5034c050e..9cbb5d1b8 100644 --- a/ext/com_dotnet/com_extension.c +++ b/ext/com_dotnet/com_extension.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: com_extension.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: com_extension.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -228,7 +228,7 @@ const zend_function_entry com_dotnet_functions[] = { PHP_FE(com_message_pump, arginfo_com_message_pump) PHP_FE(com_load_typelib, arginfo_com_load_typelib) PHP_FE(com_get_active_object, arginfo_com_get_active_object) - { NULL, NULL, NULL } + PHP_FE_END }; /* {{{ com_dotnet_module_entry diff --git a/ext/com_dotnet/com_persist.c b/ext/com_dotnet/com_persist.c index d0d6ff2dc..a4b01b422 100755 --- a/ext/com_dotnet/com_persist.c +++ b/ext/com_dotnet/com_persist.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: com_persist.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: com_persist.c 313665 2011-07-25 11:42:53Z felipe $ */ /* Infrastructure for working with persistent COM objects. * Implements: IStream* wrapper for PHP streams. @@ -697,7 +697,7 @@ static const zend_function_entry com_persist_helper_methods[] = { CPH_ME(InitNew, NULL) CPH_ME(LoadFromStream, NULL) CPH_ME(SaveToStream, NULL) - {NULL, NULL, NULL} + PHP_FE_END }; static void helper_free_storage(void *obj TSRMLS_DC) diff --git a/ext/ctype/ctype.c b/ext/ctype/ctype.c index 05169619b..b80e1df6e 100644 --- a/ext/ctype/ctype.c +++ b/ext/ctype/ctype.c @@ -106,7 +106,7 @@ static const zend_function_entry ctype_functions[] = { PHP_FE(ctype_space, arginfo_ctype_space) PHP_FE(ctype_upper, arginfo_ctype_upper) PHP_FE(ctype_xdigit, arginfo_ctype_xdigit) - {NULL, NULL, NULL} /* Must be the last line in ctype_functions[] */ + PHP_FE_END }; /* }}} */ diff --git a/ext/curl/interface.c b/ext/curl/interface.c index 0a36e1a8d..23bd2edce 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: interface.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: interface.c 313826 2011-07-28 10:31:34Z pajoye $ */ #define ZEND_INCLUDE_FULL_WINDOWS_HEADERS @@ -307,7 +307,7 @@ const zend_function_entry curl_functions[] = { PHP_FE(curl_multi_getcontent, arginfo_curl_multi_getcontent) PHP_FE(curl_multi_info_read, arginfo_curl_multi_info_read) PHP_FE(curl_multi_close, arginfo_curl_multi_close) - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ @@ -331,6 +331,13 @@ zend_module_entry curl_module_entry = { ZEND_GET_MODULE (curl) #endif +/* {{{ PHP_INI_BEGIN */ +PHP_INI_BEGIN() + PHP_INI_ENTRY("curl.cainfo", "", PHP_INI_SYSTEM, NULL) +PHP_INI_END() +/* }}} */ + +/* }}} */ /* {{{ PHP_MINFO_FUNCTION */ PHP_MINFO_FUNCTION(curl) @@ -458,6 +465,8 @@ PHP_MINIT_FUNCTION(curl) le_curl = zend_register_list_destructors_ex(_php_curl_close, NULL, "curl", module_number); le_curl_multi_handle = zend_register_list_destructors_ex(_php_curl_multi_close, NULL, "curl_multi", module_number); + REGISTER_INI_ENTRIES(); + /* See http://curl.haxx.se/lxr/source/docs/libcurl/symbols-in-versions or curl src/docs/libcurl/symbols-in-versions for a (almost) complete list of options and which version they were introduced */ @@ -581,6 +590,11 @@ PHP_MINIT_FUNCTION(curl) REGISTER_CURL_CONSTANT(CURL_TIMECOND_IFUNMODSINCE); REGISTER_CURL_CONSTANT(CURL_TIMECOND_LASTMOD); +#if LIBCURL_VERSION_NUM > 0x070f04 /* CURLOPT_MAX_RECV_SPEED_LARGE & CURLOPT_MAX_SEND_SPEED_LARGE are available since curl 7.15.5 */ + REGISTER_CURL_CONSTANT(CURLOPT_MAX_RECV_SPEED_LARGE); + REGISTER_CURL_CONSTANT(CURLOPT_MAX_SEND_SPEED_LARGE); +#endif + #if LIBCURL_VERSION_NUM > 0x070a05 /* CURLOPT_HTTPAUTH is available since curl 7.10.6 */ REGISTER_CURL_CONSTANT(CURLOPT_HTTPAUTH); /* http authentication options */ @@ -632,6 +646,10 @@ PHP_MINIT_FUNCTION(curl) #if LIBCURL_VERSION_NUM > 0x071301 REGISTER_CURL_CONSTANT(CURLINFO_CERTINFO); #endif +#if LIBCURL_VERSION_NUM >= 0x071202 + REGISTER_CURL_CONSTANT(CURLINFO_REDIRECT_URL); +#endif + /* cURL protocol constants (curl_version) */ REGISTER_CURL_CONSTANT(CURL_VERSION_IPV6); @@ -803,6 +821,9 @@ PHP_MINIT_FUNCTION(curl) int i, c = CRYPTO_num_locks(); php_curl_openssl_tsl = malloc(c * sizeof(MUTEX_T)); + if (!php_curl_openssl_tsl) { + return FAILURE; + } for (i = 0; i < c; ++i) { php_curl_openssl_tsl[i] = tsrm_mutex_alloc(); @@ -879,6 +900,7 @@ PHP_MSHUTDOWN_FUNCTION(curl) php_curl_openssl_tsl = NULL; } #endif + UNREGISTER_INI_ENTRIES(); return SUCCESS; } /* }}} */ @@ -1427,6 +1449,7 @@ PHP_FUNCTION(curl_init) zval *clone; char *url = NULL; int url_len = 0; + char *cainfo; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &url, &url_len) == FAILURE) { return; @@ -1453,6 +1476,8 @@ PHP_FUNCTION(curl_init) MAKE_STD_ZVAL(clone); ch->clone = clone; + + curl_easy_setopt(ch->cp, CURLOPT_NOPROGRESS, 1); curl_easy_setopt(ch->cp, CURLOPT_VERBOSE, 0); curl_easy_setopt(ch->cp, CURLOPT_ERRORBUFFER, ch->err.str); @@ -1465,6 +1490,12 @@ PHP_FUNCTION(curl_init) curl_easy_setopt(ch->cp, CURLOPT_DNS_USE_GLOBAL_CACHE, 1); curl_easy_setopt(ch->cp, CURLOPT_DNS_CACHE_TIMEOUT, 120); curl_easy_setopt(ch->cp, CURLOPT_MAXREDIRS, 20); /* prevent infinite redirects */ + + cainfo = INI_STR("curl.cainfo"); + if (cainfo && strlen(cainfo) > 0) { + curl_easy_setopt(ch->cp, CURLOPT_CAINFO, cainfo); + } + #if defined(ZTS) curl_easy_setopt(ch->cp, CURLOPT_NOSIGNAL, 1); #endif @@ -1669,6 +1700,13 @@ static int _php_curl_setopt(php_curl *ch, long option, zval **zvalue, zval *retu #endif error = curl_easy_setopt(ch->cp, option, Z_LVAL_PP(zvalue)); break; +#if LIBCURL_VERSION_NUM > 0x070f04 + case CURLOPT_MAX_RECV_SPEED_LARGE: + case CURLOPT_MAX_SEND_SPEED_LARGE: + convert_to_long_ex(zvalue); + error = curl_easy_setopt(ch->cp, option, (curl_off_t)Z_LVAL_PP(zvalue)); + break; +#endif case CURLOPT_FOLLOWLOCATION: convert_to_long_ex(zvalue); if ((PG(open_basedir) && *PG(open_basedir)) || PG(safe_mode)) { @@ -2306,6 +2344,11 @@ PHP_FUNCTION(curl_getinfo) CAAZ("certinfo", listcode); } #endif +#if LIBCURL_VERSION_NUM >= 0x071202 + if (curl_easy_getinfo(ch->cp, CURLINFO_REDIRECT_URL, &s_code) == CURLE_OK) { + CAAS("redirect_url", s_code); + } +#endif if (ch->header.str_len > 0) { CAAS("request_header", ch->header.str); } @@ -2313,7 +2356,11 @@ PHP_FUNCTION(curl_getinfo) switch (option) { case CURLINFO_PRIVATE: case CURLINFO_EFFECTIVE_URL: - case CURLINFO_CONTENT_TYPE: { + case CURLINFO_CONTENT_TYPE: +#if LIBCURL_VERSION_NUM >= 0x071202 + case CURLINFO_REDIRECT_URL: +#endif + { char *s_code = NULL; if (curl_easy_getinfo(ch->cp, option, &s_code) == CURLE_OK && s_code) { diff --git a/ext/curl/tests/bug48207.phpt b/ext/curl/tests/bug48207.phpt index b6caa618c..6ac16f5ea 100644 --- a/ext/curl/tests/bug48207.phpt +++ b/ext/curl/tests/bug48207.phpt @@ -18,7 +18,7 @@ $host = getenv('PHP_CURL_HTTP_REMOTE_SERVER'); if(!empty($host)) { // Use the set Environment variable - $url = "$host/get.php"; + $url = "$host/get.php?test=1"; } else { diff --git a/ext/date/lib/interval.c b/ext/date/lib/interval.c index efd92bd4f..6558d5a8c 100644 --- a/ext/date/lib/interval.c +++ b/ext/date/lib/interval.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: interval.c 298973 2010-05-04 15:11:41Z derick $ */ +/* $Id: interval.c 312235 2011-06-17 16:38:23Z derick $ */ #include "timelib.h" @@ -25,6 +25,7 @@ timelib_rel_time *timelib_diff(timelib_time *one, timelib_time *two) timelib_rel_time *rt; timelib_time *swp; timelib_sll dst_h_corr = 0, dst_m_corr = 0; + timelib_time one_backup, two_backup; rt = timelib_rel_time_ctor(); rt->invert = 0; @@ -45,6 +46,10 @@ timelib_rel_time *timelib_diff(timelib_time *one, timelib_time *two) dst_m_corr = ((two->z - one->z) % 3600) / 60; } + /* Save old TZ info */ + memcpy(&one_backup, one, sizeof(one_backup)); + memcpy(&two_backup, two, sizeof(two_backup)); + timelib_apply_localtime(one, 0); timelib_apply_localtime(two, 0); @@ -58,8 +63,9 @@ timelib_rel_time *timelib_diff(timelib_time *one, timelib_time *two) timelib_do_rel_normalize(rt->invert ? one : two, rt); - timelib_apply_localtime(one, 1); - timelib_apply_localtime(two, 1); + /* Restore old TZ info */ + memcpy(one, &one_backup, sizeof(one_backup)); + memcpy(two, &two_backup, sizeof(two_backup)); return rt; } diff --git a/ext/date/lib/parse_date.c b/ext/date/lib/parse_date.c index 4f60d51f9..0539fa80f 100644 --- a/ext/date/lib/parse_date.c +++ b/ext/date/lib/parse_date.c @@ -1,4 +1,4 @@ -/* Generated by re2c 0.13.5 on Sat Nov 13 14:58:02 2010 */ +/* Generated by re2c 0.13.5 on Sun Jun 5 15:26:42 2011 */ /* +----------------------------------------------------------------------+ | PHP Version 5 | @@ -17,13 +17,14 @@ +----------------------------------------------------------------------+ */ -/* $Id: parse_date.c 305316 2010-11-13 15:01:48Z derick $ */ +/* $Id: parse_date.c 311831 2011-06-05 13:30:01Z bjori $ */ #include "timelib.h" #include <stdio.h> #include <ctype.h> #include <math.h> +#include <assert.h> #ifdef HAVE_STDLIB_H #include <stdlib.h> @@ -719,6 +720,25 @@ const static timelib_tz_lookup_table* zone_search(const char *word, long gmtoffs return first_found_elem; } + for (tp = timelib_timezone_lookup; tp->name; tp++) { + if (tp->full_tz_name && strcasecmp(word, tp->full_tz_name) == 0) { + if (!first_found) { + first_found = 1; + first_found_elem = tp; + if (gmtoffset == -1) { + return tp; + } + } + if (tp->gmtoffset == gmtoffset) { + return tp; + } + } + } + if (first_found) { + return first_found_elem; + } + + /* Still didn't find anything, let's find the zone solely based on * offset/isdst then */ for (fmp = timelib_timezone_fallbackmap; fmp->name; fmp++) { @@ -24717,6 +24737,30 @@ timelib_time* timelib_strtotime(char *s, int len, struct timelib_error_container add_pbf_error(s, "Unexpected data found.", string, begin); \ } +static void timelib_time_reset_fields(timelib_time *time) +{ + assert(time != NULL); + + time->y = 1970; + time->m = 1; + time->d = 1; + time->h = time->i = time->s = 0; + time->f = 0.0; + time->tz_info = NULL; +} + +static void timelib_time_reset_unset_fields(timelib_time *time) +{ + assert(time != NULL); + + if (time->y == TIMELIB_UNSET ) time->y = 1970; + if (time->m == TIMELIB_UNSET ) time->m = 1; + if (time->d == TIMELIB_UNSET ) time->d = 1; + if (time->h == TIMELIB_UNSET ) time->h = 0; + if (time->i == TIMELIB_UNSET ) time->i = 0; + if (time->s == TIMELIB_UNSET ) time->s = 0; + if (time->f == TIMELIB_UNSET ) time->f = 0.0; +} timelib_time *timelib_parse_from_format(char *format, char *string, int len, timelib_error_container **errors, const timelib_tzdb *tzdb) { @@ -24915,23 +24959,11 @@ timelib_time *timelib_parse_from_format(char *format, char *string, int len, tim break; case '!': /* reset all fields to default */ - s->time->y = 1970; - s->time->m = 1; - s->time->d = 1; - s->time->h = s->time->i = s->time->s = 0; - s->time->f = 0.0; - s->time->tz_info = NULL; + timelib_time_reset_fields(s->time); break; /* break intentionally not missing */ case '|': /* reset all fields to default when not set */ - if (s->time->y == TIMELIB_UNSET ) s->time->y = 1970; - if (s->time->m == TIMELIB_UNSET ) s->time->m = 1; - if (s->time->d == TIMELIB_UNSET ) s->time->d = 1; - if (s->time->h == TIMELIB_UNSET ) s->time->h = 0; - if (s->time->i == TIMELIB_UNSET ) s->time->i = 0; - if (s->time->s == TIMELIB_UNSET ) s->time->s = 0; - if (s->time->f == TIMELIB_UNSET ) s->time->f = 0.0; - + timelib_time_reset_unset_fields(s->time); break; /* break intentionally not missing */ case '?': /* random char */ @@ -24963,7 +24995,21 @@ timelib_time *timelib_parse_from_format(char *format, char *string, int len, tim add_pbf_error(s, "Trailing data", string, ptr); } if (*fptr) { - add_pbf_error(s, "Data missing", string, ptr); + /* Trailing | and ! specifiers are valid. */ + while (*fptr) { + switch (*fptr++) { + case '!': /* reset all fields to default */ + timelib_time_reset_fields(s->time); + break; + + case '|': /* reset all fields to default when not set */ + timelib_time_reset_unset_fields(s->time); + break; + + default: + add_pbf_error(s, "Data missing", string, ptr); + } + } } /* clean up a bit */ diff --git a/ext/date/lib/parse_date.c.orig b/ext/date/lib/parse_date.c.orig index 935f3e5a9..9d7cca772 100644 --- a/ext/date/lib/parse_date.c.orig +++ b/ext/date/lib/parse_date.c.orig @@ -1,4 +1,4 @@ -/* Generated by re2c 0.13.5 on Sat Nov 13 14:58:02 2010 */ +/* Generated by re2c 0.13.5 on Sun Jun 5 15:26:42 2011 */ #line 1 "ext/date/lib/parse_date.re" /* +----------------------------------------------------------------------+ @@ -18,13 +18,14 @@ +----------------------------------------------------------------------+ */ -/* $Id: parse_date.c 305316 2010-11-13 15:01:48Z derick $ */ +/* $Id: parse_date.c 311831 2011-06-05 13:30:01Z bjori $ */ #include "timelib.h" #include <stdio.h> #include <ctype.h> #include <math.h> +#include <assert.h> #ifdef HAVE_STDLIB_H #include <stdlib.h> @@ -720,6 +721,25 @@ const static timelib_tz_lookup_table* zone_search(const char *word, long gmtoffs return first_found_elem; } + for (tp = timelib_timezone_lookup; tp->name; tp++) { + if (tp->full_tz_name && strcasecmp(word, tp->full_tz_name) == 0) { + if (!first_found) { + first_found = 1; + first_found_elem = tp; + if (gmtoffset == -1) { + return tp; + } + } + if (tp->gmtoffset == gmtoffset) { + return tp; + } + } + } + if (first_found) { + return first_found_elem; + } + + /* Still didn't find anything, let's find the zone solely based on * offset/isdst then */ for (fmp = timelib_timezone_fallbackmap; fmp->name; fmp++) { @@ -843,11 +863,11 @@ static int scan(Scanner *s) std: s->tok = cursor; s->len = 0; -#line 969 "ext/date/lib/parse_date.re" +#line 989 "ext/date/lib/parse_date.re" -#line 851 "ext/date/lib/parse_date.c" +#line 871 "ext/date/lib/parse_date.c" { YYCTYPE yych; unsigned int yyaccept = 0; @@ -967,7 +987,7 @@ std: } yy2: YYDEBUG(2, *YYCURSOR); -#line 1054 "ext/date/lib/parse_date.re" +#line 1074 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("firstdayof | lastdayof"); TIMELIB_INIT; @@ -983,7 +1003,7 @@ yy2: TIMELIB_DEINIT; return TIMELIB_LF_DAY_OF_MONTH; } -#line 987 "ext/date/lib/parse_date.c" +#line 1007 "ext/date/lib/parse_date.c" yy3: YYDEBUG(3, *YYCURSOR); ++YYCURSOR; @@ -1006,7 +1026,7 @@ yy3: } yy4: YYDEBUG(4, *YYCURSOR); -#line 1636 "ext/date/lib/parse_date.re" +#line 1656 "ext/date/lib/parse_date.re" { int tz_not_found; DEBUG_OUTPUT("tzcorrection | tz"); @@ -1019,7 +1039,7 @@ yy4: TIMELIB_DEINIT; return TIMELIB_TIMEZONE; } -#line 1023 "ext/date/lib/parse_date.c" +#line 1043 "ext/date/lib/parse_date.c" yy5: YYDEBUG(5, *YYCURSOR); yych = *++YYCURSOR; @@ -1330,12 +1350,12 @@ yy12: if (yych <= '9') goto yy1385; yy13: YYDEBUG(13, *YYCURSOR); -#line 1731 "ext/date/lib/parse_date.re" +#line 1751 "ext/date/lib/parse_date.re" { add_error(s, "Unexpected character"); goto std; } -#line 1339 "ext/date/lib/parse_date.c" +#line 1359 "ext/date/lib/parse_date.c" yy14: YYDEBUG(14, *YYCURSOR); yych = *++YYCURSOR; @@ -2392,11 +2412,11 @@ yy49: if (yych <= '9') goto yy55; yy50: YYDEBUG(50, *YYCURSOR); -#line 1720 "ext/date/lib/parse_date.re" +#line 1740 "ext/date/lib/parse_date.re" { goto std; } -#line 2400 "ext/date/lib/parse_date.c" +#line 2420 "ext/date/lib/parse_date.c" yy51: YYDEBUG(51, *YYCURSOR); yych = *++YYCURSOR; @@ -2405,12 +2425,12 @@ yy52: YYDEBUG(52, *YYCURSOR); ++YYCURSOR; YYDEBUG(53, *YYCURSOR); -#line 1725 "ext/date/lib/parse_date.re" +#line 1745 "ext/date/lib/parse_date.re" { s->pos = cursor; s->line++; goto std; } -#line 2414 "ext/date/lib/parse_date.c" +#line 2434 "ext/date/lib/parse_date.c" yy54: YYDEBUG(54, *YYCURSOR); yych = *++YYCURSOR; @@ -2797,7 +2817,7 @@ yy72: if (yych == 's') goto yy74; yy73: YYDEBUG(73, *YYCURSOR); -#line 1704 "ext/date/lib/parse_date.re" +#line 1724 "ext/date/lib/parse_date.re" { timelib_ull i; DEBUG_OUTPUT("relative"); @@ -2812,7 +2832,7 @@ yy73: TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 2816 "ext/date/lib/parse_date.c" +#line 2836 "ext/date/lib/parse_date.c" yy74: YYDEBUG(74, *YYCURSOR); yych = *++YYCURSOR; @@ -3574,7 +3594,7 @@ yy166: } yy167: YYDEBUG(167, *YYCURSOR); -#line 1567 "ext/date/lib/parse_date.re" +#line 1587 "ext/date/lib/parse_date.re" { const timelib_relunit* relunit; DEBUG_OUTPUT("daytext"); @@ -3591,7 +3611,7 @@ yy167: TIMELIB_DEINIT; return TIMELIB_WEEKDAY; } -#line 3595 "ext/date/lib/parse_date.c" +#line 3615 "ext/date/lib/parse_date.c" yy168: YYDEBUG(168, *YYCURSOR); yych = *++YYCURSOR; @@ -4111,7 +4131,7 @@ yy193: } yy194: YYDEBUG(194, *YYCURSOR); -#line 1626 "ext/date/lib/parse_date.re" +#line 1646 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("monthtext"); TIMELIB_INIT; @@ -4120,7 +4140,7 @@ yy194: TIMELIB_DEINIT; return TIMELIB_DATE_TEXT; } -#line 4124 "ext/date/lib/parse_date.c" +#line 4144 "ext/date/lib/parse_date.c" yy195: YYDEBUG(195, *YYCURSOR); ++YYCURSOR; @@ -4171,7 +4191,7 @@ yy198: } yy199: YYDEBUG(199, *YYCURSOR); -#line 1376 "ext/date/lib/parse_date.re" +#line 1396 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("datetextual | datenoyear"); TIMELIB_INIT; @@ -4183,7 +4203,7 @@ yy199: TIMELIB_DEINIT; return TIMELIB_DATE_TEXT; } -#line 4187 "ext/date/lib/parse_date.c" +#line 4207 "ext/date/lib/parse_date.c" yy200: YYDEBUG(200, *YYCURSOR); yyaccept = 6; @@ -4452,7 +4472,7 @@ yy222: } yy223: YYDEBUG(223, *YYCURSOR); -#line 1674 "ext/date/lib/parse_date.re" +#line 1694 "ext/date/lib/parse_date.re" { int tz_not_found; DEBUG_OUTPUT("dateshortwithtimeshort | dateshortwithtimelong | dateshortwithtimelongtz"); @@ -4481,7 +4501,7 @@ yy223: TIMELIB_DEINIT; return TIMELIB_SHORTDATE_WITH_TIME; } -#line 4485 "ext/date/lib/parse_date.c" +#line 4505 "ext/date/lib/parse_date.c" yy224: YYDEBUG(224, *YYCURSOR); yyaccept = 7; @@ -5179,7 +5199,7 @@ yy278: YYDEBUG(278, *YYCURSOR); ++YYCURSOR; YYDEBUG(279, *YYCURSOR); -#line 1650 "ext/date/lib/parse_date.re" +#line 1670 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("dateshortwithtimeshort12 | dateshortwithtimelong12"); TIMELIB_INIT; @@ -5202,7 +5222,7 @@ yy278: TIMELIB_DEINIT; return TIMELIB_SHORTDATE_WITH_TIME; } -#line 5206 "ext/date/lib/parse_date.c" +#line 5226 "ext/date/lib/parse_date.c" yy280: YYDEBUG(280, *YYCURSOR); yych = *++YYCURSOR; @@ -5380,7 +5400,7 @@ yy294: ++YYCURSOR; yy295: YYDEBUG(295, *YYCURSOR); -#line 1350 "ext/date/lib/parse_date.re" +#line 1370 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("datenoday"); TIMELIB_INIT; @@ -5392,7 +5412,7 @@ yy295: TIMELIB_DEINIT; return TIMELIB_DATE_NO_DAY; } -#line 5396 "ext/date/lib/parse_date.c" +#line 5416 "ext/date/lib/parse_date.c" yy296: YYDEBUG(296, *YYCURSOR); yych = *++YYCURSOR; @@ -6612,7 +6632,7 @@ yy362: if (yych <= '9') goto yy365; yy364: YYDEBUG(364, *YYCURSOR); -#line 1490 "ext/date/lib/parse_date.re" +#line 1510 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("pgtextshort"); TIMELIB_INIT; @@ -6624,7 +6644,7 @@ yy364: TIMELIB_DEINIT; return TIMELIB_PG_TEXT; } -#line 6628 "ext/date/lib/parse_date.c" +#line 6648 "ext/date/lib/parse_date.c" yy365: YYDEBUG(365, *YYCURSOR); yych = *++YYCURSOR; @@ -7262,7 +7282,7 @@ yy392: } yy393: YYDEBUG(393, *YYCURSOR); -#line 1546 "ext/date/lib/parse_date.re" +#line 1566 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("ago"); TIMELIB_INIT; @@ -7282,7 +7302,7 @@ yy393: TIMELIB_DEINIT; return TIMELIB_AGO; } -#line 7286 "ext/date/lib/parse_date.c" +#line 7306 "ext/date/lib/parse_date.c" yy394: YYDEBUG(394, *YYCURSOR); yyaccept = 5; @@ -9032,7 +9052,7 @@ yy454: ++YYCURSOR; yy455: YYDEBUG(455, *YYCURSOR); -#line 1260 "ext/date/lib/parse_date.re" +#line 1280 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("iso8601date4 | iso8601date2 | iso8601dateslash | dateslash"); TIMELIB_INIT; @@ -9043,7 +9063,7 @@ yy455: TIMELIB_DEINIT; return TIMELIB_ISO_DATE; } -#line 9047 "ext/date/lib/parse_date.c" +#line 9067 "ext/date/lib/parse_date.c" yy456: YYDEBUG(456, *YYCURSOR); yyaccept = 0; @@ -9603,7 +9623,7 @@ yy475: } yy476: YYDEBUG(476, *YYCURSOR); -#line 1389 "ext/date/lib/parse_date.re" +#line 1409 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("datenoyearrev"); TIMELIB_INIT; @@ -9614,7 +9634,7 @@ yy476: TIMELIB_DEINIT; return TIMELIB_DATE_TEXT; } -#line 9618 "ext/date/lib/parse_date.c" +#line 9638 "ext/date/lib/parse_date.c" yy477: YYDEBUG(477, *YYCURSOR); yyaccept = 10; @@ -9755,7 +9775,7 @@ yy488: YYDEBUG(488, *YYCURSOR); ++YYCURSOR; YYDEBUG(489, *YYCURSOR); -#line 1116 "ext/date/lib/parse_date.re" +#line 1136 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("timetiny12 | timeshort12 | timelong12"); TIMELIB_INIT; @@ -9771,7 +9791,7 @@ yy488: TIMELIB_DEINIT; return TIMELIB_TIME12; } -#line 9775 "ext/date/lib/parse_date.c" +#line 9795 "ext/date/lib/parse_date.c" yy490: YYDEBUG(490, *YYCURSOR); yyaccept = 11; @@ -9784,7 +9804,7 @@ yy490: } yy491: YYDEBUG(491, *YYCURSOR); -#line 1153 "ext/date/lib/parse_date.re" +#line 1173 "ext/date/lib/parse_date.re" { int tz_not_found; DEBUG_OUTPUT("timeshort24 | timelong24 | iso8601long"); @@ -9809,7 +9829,7 @@ yy491: TIMELIB_DEINIT; return TIMELIB_TIME24_WITH_ZONE; } -#line 9813 "ext/date/lib/parse_date.c" +#line 9833 "ext/date/lib/parse_date.c" yy492: YYDEBUG(492, *YYCURSOR); yyaccept = 11; @@ -10119,7 +10139,7 @@ yy523: YYDEBUG(523, *YYCURSOR); ++YYCURSOR; YYDEBUG(524, *YYCURSOR); -#line 1133 "ext/date/lib/parse_date.re" +#line 1153 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("mssqltime"); TIMELIB_INIT; @@ -10138,7 +10158,7 @@ yy523: TIMELIB_DEINIT; return TIMELIB_TIME24_WITH_ZONE; } -#line 10142 "ext/date/lib/parse_date.c" +#line 10162 "ext/date/lib/parse_date.c" yy525: YYDEBUG(525, *YYCURSOR); yyaccept = 11; @@ -10244,7 +10264,7 @@ yy534: if (yych <= '9') goto yy541; yy535: YYDEBUG(535, *YYCURSOR); -#line 1311 "ext/date/lib/parse_date.re" +#line 1331 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("datefull"); TIMELIB_INIT; @@ -10257,7 +10277,7 @@ yy535: TIMELIB_DEINIT; return TIMELIB_DATE_FULL; } -#line 10261 "ext/date/lib/parse_date.c" +#line 10281 "ext/date/lib/parse_date.c" yy536: YYDEBUG(536, *YYCURSOR); yych = *++YYCURSOR; @@ -10994,7 +11014,7 @@ yy605: YYDEBUG(606, *YYCURSOR); ++YYCURSOR; YYDEBUG(607, *YYCURSOR); -#line 1325 "ext/date/lib/parse_date.re" +#line 1345 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("pointed date YYYY"); TIMELIB_INIT; @@ -11005,7 +11025,7 @@ yy605: TIMELIB_DEINIT; return TIMELIB_DATE_FULL_POINTED; } -#line 11009 "ext/date/lib/parse_date.c" +#line 11029 "ext/date/lib/parse_date.c" yy608: YYDEBUG(608, *YYCURSOR); yyaccept = 11; @@ -11041,7 +11061,7 @@ yy611: if (yych <= '9') goto yy605; yy612: YYDEBUG(612, *YYCURSOR); -#line 1337 "ext/date/lib/parse_date.re" +#line 1357 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("pointed date YY"); TIMELIB_INIT; @@ -11053,7 +11073,7 @@ yy612: TIMELIB_DEINIT; return TIMELIB_DATE_FULL_POINTED; } -#line 11057 "ext/date/lib/parse_date.c" +#line 11077 "ext/date/lib/parse_date.c" yy613: YYDEBUG(613, *YYCURSOR); yyaccept = 11; @@ -11694,7 +11714,7 @@ yy656: } yy657: YYDEBUG(657, *YYCURSOR); -#line 1298 "ext/date/lib/parse_date.re" +#line 1318 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("gnudateshort"); TIMELIB_INIT; @@ -11706,7 +11726,7 @@ yy657: TIMELIB_DEINIT; return TIMELIB_ISO_DATE; } -#line 11710 "ext/date/lib/parse_date.c" +#line 11730 "ext/date/lib/parse_date.c" yy658: YYDEBUG(658, *YYCURSOR); yyaccept = 13; @@ -11812,7 +11832,7 @@ yy666: } yy667: YYDEBUG(667, *YYCURSOR); -#line 1245 "ext/date/lib/parse_date.re" +#line 1265 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("americanshort | american"); TIMELIB_INIT; @@ -11826,7 +11846,7 @@ yy667: TIMELIB_DEINIT; return TIMELIB_AMERICAN; } -#line 11830 "ext/date/lib/parse_date.c" +#line 11850 "ext/date/lib/parse_date.c" yy668: YYDEBUG(668, *YYCURSOR); yyaccept = 14; @@ -12059,7 +12079,7 @@ yy700: if (yych <= ':') goto yy704; yy701: YYDEBUG(701, *YYCURSOR); -#line 1516 "ext/date/lib/parse_date.re" +#line 1536 "ext/date/lib/parse_date.re" { int tz_not_found; DEBUG_OUTPUT("clf"); @@ -12079,7 +12099,7 @@ yy701: TIMELIB_DEINIT; return TIMELIB_CLF; } -#line 12083 "ext/date/lib/parse_date.c" +#line 12103 "ext/date/lib/parse_date.c" yy702: YYDEBUG(702, *YYCURSOR); yych = *++YYCURSOR; @@ -12631,7 +12651,7 @@ yy763: } yy764: YYDEBUG(764, *YYCURSOR); -#line 1272 "ext/date/lib/parse_date.re" +#line 1292 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("iso8601date2"); TIMELIB_INIT; @@ -12643,7 +12663,7 @@ yy764: TIMELIB_DEINIT; return TIMELIB_ISO_DATE; } -#line 12647 "ext/date/lib/parse_date.c" +#line 12667 "ext/date/lib/parse_date.c" yy765: YYDEBUG(765, *YYCURSOR); yych = *++YYCURSOR; @@ -12682,7 +12702,7 @@ yy771: YYDEBUG(771, *YYCURSOR); ++YYCURSOR; YYDEBUG(772, *YYCURSOR); -#line 1503 "ext/date/lib/parse_date.re" +#line 1523 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("pgtextreverse"); TIMELIB_INIT; @@ -12694,7 +12714,7 @@ yy771: TIMELIB_DEINIT; return TIMELIB_PG_TEXT; } -#line 12698 "ext/date/lib/parse_date.c" +#line 12718 "ext/date/lib/parse_date.c" yy773: YYDEBUG(773, *YYCURSOR); yych = *++YYCURSOR; @@ -12832,7 +12852,7 @@ yy783: } yy784: YYDEBUG(784, *YYCURSOR); -#line 1537 "ext/date/lib/parse_date.re" +#line 1557 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("year4"); TIMELIB_INIT; @@ -12840,7 +12860,7 @@ yy784: TIMELIB_DEINIT; return TIMELIB_CLF; } -#line 12844 "ext/date/lib/parse_date.c" +#line 12864 "ext/date/lib/parse_date.c" yy785: YYDEBUG(785, *YYCURSOR); yych = *++YYCURSOR; @@ -12991,7 +13011,7 @@ yy793: } yy794: YYDEBUG(794, *YYCURSOR); -#line 1363 "ext/date/lib/parse_date.re" +#line 1383 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("datenodayrev"); TIMELIB_INIT; @@ -13003,7 +13023,7 @@ yy794: TIMELIB_DEINIT; return TIMELIB_DATE_NO_DAY; } -#line 13007 "ext/date/lib/parse_date.c" +#line 13027 "ext/date/lib/parse_date.c" yy795: YYDEBUG(795, *YYCURSOR); yych = *++YYCURSOR; @@ -13218,7 +13238,7 @@ yy814: if (yych <= '7') goto yy817; yy815: YYDEBUG(815, *YYCURSOR); -#line 1471 "ext/date/lib/parse_date.re" +#line 1491 "ext/date/lib/parse_date.re" { timelib_sll w, d; DEBUG_OUTPUT("isoweek"); @@ -13236,7 +13256,7 @@ yy815: TIMELIB_DEINIT; return TIMELIB_ISO_WEEK; } -#line 13240 "ext/date/lib/parse_date.c" +#line 13260 "ext/date/lib/parse_date.c" yy816: YYDEBUG(816, *YYCURSOR); yych = *++YYCURSOR; @@ -13246,7 +13266,7 @@ yy817: YYDEBUG(817, *YYCURSOR); ++YYCURSOR; YYDEBUG(818, *YYCURSOR); -#line 1452 "ext/date/lib/parse_date.re" +#line 1472 "ext/date/lib/parse_date.re" { timelib_sll w, d; DEBUG_OUTPUT("isoweekday"); @@ -13264,7 +13284,7 @@ yy817: TIMELIB_DEINIT; return TIMELIB_ISO_WEEK; } -#line 13268 "ext/date/lib/parse_date.c" +#line 13288 "ext/date/lib/parse_date.c" yy819: YYDEBUG(819, *YYCURSOR); yych = *++YYCURSOR; @@ -13328,7 +13348,7 @@ yy821: } yy822: YYDEBUG(822, *YYCURSOR); -#line 1439 "ext/date/lib/parse_date.re" +#line 1459 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("pgydotd"); TIMELIB_INIT; @@ -13340,7 +13360,7 @@ yy822: TIMELIB_DEINIT; return TIMELIB_PG_YEARDAY; } -#line 13344 "ext/date/lib/parse_date.c" +#line 13364 "ext/date/lib/parse_date.c" yy823: YYDEBUG(823, *YYCURSOR); yych = *++YYCURSOR; @@ -13443,7 +13463,7 @@ yy842: ++YYCURSOR; yy843: YYDEBUG(843, *YYCURSOR); -#line 1413 "ext/date/lib/parse_date.re" +#line 1433 "ext/date/lib/parse_date.re" { int tz_not_found; DEBUG_OUTPUT("xmlrpc | xmlrpcnocolon | soap | wddx | exif"); @@ -13468,7 +13488,7 @@ yy843: TIMELIB_DEINIT; return TIMELIB_XMLRPC_SOAP; } -#line 13472 "ext/date/lib/parse_date.c" +#line 13492 "ext/date/lib/parse_date.c" yy844: YYDEBUG(844, *YYCURSOR); yych = *++YYCURSOR; @@ -13730,7 +13750,7 @@ yy848: } yy849: YYDEBUG(849, *YYCURSOR); -#line 1401 "ext/date/lib/parse_date.re" +#line 1421 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("datenocolon"); TIMELIB_INIT; @@ -13741,7 +13761,7 @@ yy849: TIMELIB_DEINIT; return TIMELIB_DATE_NOCOLON; } -#line 13745 "ext/date/lib/parse_date.c" +#line 13765 "ext/date/lib/parse_date.c" yy850: YYDEBUG(850, *YYCURSOR); yych = *++YYCURSOR; @@ -14661,7 +14681,7 @@ yy973: if (yych <= '9') goto yy996; yy974: YYDEBUG(974, *YYCURSOR); -#line 1285 "ext/date/lib/parse_date.re" +#line 1305 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("gnudateshorter"); TIMELIB_INIT; @@ -14673,7 +14693,7 @@ yy974: TIMELIB_DEINIT; return TIMELIB_ISO_DATE; } -#line 14677 "ext/date/lib/parse_date.c" +#line 14697 "ext/date/lib/parse_date.c" yy975: YYDEBUG(975, *YYCURSOR); yyaccept = 22; @@ -15682,7 +15702,7 @@ yy1066: } yy1068: YYDEBUG(1068, *YYCURSOR); -#line 1179 "ext/date/lib/parse_date.re" +#line 1199 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("gnunocolon"); TIMELIB_INIT; @@ -15704,7 +15724,7 @@ yy1068: TIMELIB_DEINIT; return TIMELIB_GNU_NOCOLON; } -#line 15708 "ext/date/lib/parse_date.c" +#line 15728 "ext/date/lib/parse_date.c" yy1069: YYDEBUG(1069, *YYCURSOR); yych = *++YYCURSOR; @@ -15796,7 +15816,7 @@ yy1075: } yy1076: YYDEBUG(1076, *YYCURSOR); -#line 1225 "ext/date/lib/parse_date.re" +#line 1245 "ext/date/lib/parse_date.re" { int tz_not_found; DEBUG_OUTPUT("iso8601nocolon"); @@ -15815,7 +15835,7 @@ yy1076: TIMELIB_DEINIT; return TIMELIB_ISO_NOCOLON; } -#line 15819 "ext/date/lib/parse_date.c" +#line 15839 "ext/date/lib/parse_date.c" yy1077: YYDEBUG(1077, *YYCURSOR); yyaccept = 25; @@ -16713,7 +16733,7 @@ yy1117: } yy1118: YYDEBUG(1118, *YYCURSOR); -#line 1609 "ext/date/lib/parse_date.re" +#line 1629 "ext/date/lib/parse_date.re" { timelib_sll i; int behavior = 0; @@ -16729,7 +16749,7 @@ yy1118: TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 16733 "ext/date/lib/parse_date.c" +#line 16753 "ext/date/lib/parse_date.c" yy1119: YYDEBUG(1119, *YYCURSOR); ++YYCURSOR; @@ -16780,7 +16800,7 @@ yy1126: YYDEBUG(1126, *YYCURSOR); ++YYCURSOR; YYDEBUG(1127, *YYCURSOR); -#line 1094 "ext/date/lib/parse_date.re" +#line 1114 "ext/date/lib/parse_date.re" { timelib_sll i; int behavior = 0; @@ -16801,7 +16821,7 @@ yy1126: TIMELIB_DEINIT; return TIMELIB_WEEK_DAY_OF_MONTH; } -#line 16805 "ext/date/lib/parse_date.c" +#line 16825 "ext/date/lib/parse_date.c" yy1128: YYDEBUG(1128, *YYCURSOR); yyaccept = 26; @@ -16909,7 +16929,7 @@ yy1141: } yy1142: YYDEBUG(1142, *YYCURSOR); -#line 1585 "ext/date/lib/parse_date.re" +#line 1605 "ext/date/lib/parse_date.re" { timelib_sll i; int behavior = 0; @@ -16932,7 +16952,7 @@ yy1142: TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 16936 "ext/date/lib/parse_date.c" +#line 16956 "ext/date/lib/parse_date.c" yy1143: YYDEBUG(1143, *YYCURSOR); yych = *++YYCURSOR; @@ -19609,7 +19629,7 @@ yy1294: goto yy1298; yy1295: YYDEBUG(1295, *YYCURSOR); -#line 1071 "ext/date/lib/parse_date.re" +#line 1091 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("backof | frontof"); TIMELIB_INIT; @@ -19631,7 +19651,7 @@ yy1295: TIMELIB_DEINIT; return TIMELIB_LF_DAY_OF_MONTH; } -#line 19635 "ext/date/lib/parse_date.c" +#line 19655 "ext/date/lib/parse_date.c" yy1296: YYDEBUG(1296, *YYCURSOR); yyaccept = 28; @@ -21322,7 +21342,7 @@ yy1385: if (yych <= '9') goto yy1385; yy1387: YYDEBUG(1387, *YYCURSOR); -#line 1029 "ext/date/lib/parse_date.re" +#line 1049 "ext/date/lib/parse_date.re" { timelib_ull i; @@ -21346,7 +21366,7 @@ yy1387: TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 21350 "ext/date/lib/parse_date.c" +#line 21370 "ext/date/lib/parse_date.c" yy1388: YYDEBUG(1388, *YYCURSOR); yych = *++YYCURSOR; @@ -21782,7 +21802,7 @@ yy1416: ++YYCURSOR; yy1417: YYDEBUG(1417, *YYCURSOR); -#line 1017 "ext/date/lib/parse_date.re" +#line 1037 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("tomorrow"); TIMELIB_INIT; @@ -21793,7 +21813,7 @@ yy1417: TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 21797 "ext/date/lib/parse_date.c" +#line 21817 "ext/date/lib/parse_date.c" yy1418: YYDEBUG(1418, *YYCURSOR); yych = *++YYCURSOR; @@ -21828,7 +21848,7 @@ yy1419: } yy1420: YYDEBUG(1420, *YYCURSOR); -#line 1007 "ext/date/lib/parse_date.re" +#line 1027 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("midnight | today"); TIMELIB_INIT; @@ -21837,7 +21857,7 @@ yy1420: TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 21841 "ext/date/lib/parse_date.c" +#line 21861 "ext/date/lib/parse_date.c" yy1421: YYDEBUG(1421, *YYCURSOR); yych = *++YYCURSOR; @@ -23849,7 +23869,7 @@ yy1499: } yy1500: YYDEBUG(1500, *YYCURSOR); -#line 986 "ext/date/lib/parse_date.re" +#line 1006 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("now"); TIMELIB_INIT; @@ -23857,7 +23877,7 @@ yy1500: TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 23861 "ext/date/lib/parse_date.c" +#line 23881 "ext/date/lib/parse_date.c" yy1501: YYDEBUG(1501, *YYCURSOR); yych = *++YYCURSOR; @@ -23996,7 +24016,7 @@ yy1507: } yy1508: YYDEBUG(1508, *YYCURSOR); -#line 995 "ext/date/lib/parse_date.re" +#line 1015 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("noon"); TIMELIB_INIT; @@ -24007,7 +24027,7 @@ yy1508: TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 24011 "ext/date/lib/parse_date.c" +#line 24031 "ext/date/lib/parse_date.c" yy1509: YYDEBUG(1509, *YYCURSOR); yyaccept = 0; @@ -24540,7 +24560,7 @@ yy1530: ++YYCURSOR; yy1531: YYDEBUG(1531, *YYCURSOR); -#line 974 "ext/date/lib/parse_date.re" +#line 994 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("yesterday"); TIMELIB_INIT; @@ -24551,7 +24571,7 @@ yy1531: TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 24555 "ext/date/lib/parse_date.c" +#line 24575 "ext/date/lib/parse_date.c" yy1532: YYDEBUG(1532, *YYCURSOR); yyaccept = 0; @@ -24724,7 +24744,7 @@ yy1537: goto yy1531; } } -#line 1735 "ext/date/lib/parse_date.re" +#line 1755 "ext/date/lib/parse_date.re" } @@ -24815,6 +24835,30 @@ timelib_time* timelib_strtotime(char *s, int len, struct timelib_error_container add_pbf_error(s, "Unexpected data found.", string, begin); \ } +static void timelib_time_reset_fields(timelib_time *time) +{ + assert(time != NULL); + + time->y = 1970; + time->m = 1; + time->d = 1; + time->h = time->i = time->s = 0; + time->f = 0.0; + time->tz_info = NULL; +} + +static void timelib_time_reset_unset_fields(timelib_time *time) +{ + assert(time != NULL); + + if (time->y == TIMELIB_UNSET ) time->y = 1970; + if (time->m == TIMELIB_UNSET ) time->m = 1; + if (time->d == TIMELIB_UNSET ) time->d = 1; + if (time->h == TIMELIB_UNSET ) time->h = 0; + if (time->i == TIMELIB_UNSET ) time->i = 0; + if (time->s == TIMELIB_UNSET ) time->s = 0; + if (time->f == TIMELIB_UNSET ) time->f = 0.0; +} timelib_time *timelib_parse_from_format(char *format, char *string, int len, timelib_error_container **errors, const timelib_tzdb *tzdb) { @@ -25013,23 +25057,11 @@ timelib_time *timelib_parse_from_format(char *format, char *string, int len, tim break; case '!': /* reset all fields to default */ - s->time->y = 1970; - s->time->m = 1; - s->time->d = 1; - s->time->h = s->time->i = s->time->s = 0; - s->time->f = 0.0; - s->time->tz_info = NULL; + timelib_time_reset_fields(s->time); break; /* break intentionally not missing */ case '|': /* reset all fields to default when not set */ - if (s->time->y == TIMELIB_UNSET ) s->time->y = 1970; - if (s->time->m == TIMELIB_UNSET ) s->time->m = 1; - if (s->time->d == TIMELIB_UNSET ) s->time->d = 1; - if (s->time->h == TIMELIB_UNSET ) s->time->h = 0; - if (s->time->i == TIMELIB_UNSET ) s->time->i = 0; - if (s->time->s == TIMELIB_UNSET ) s->time->s = 0; - if (s->time->f == TIMELIB_UNSET ) s->time->f = 0.0; - + timelib_time_reset_unset_fields(s->time); break; /* break intentionally not missing */ case '?': /* random char */ @@ -25061,7 +25093,21 @@ timelib_time *timelib_parse_from_format(char *format, char *string, int len, tim add_pbf_error(s, "Trailing data", string, ptr); } if (*fptr) { - add_pbf_error(s, "Data missing", string, ptr); + /* Trailing | and ! specifiers are valid. */ + while (*fptr) { + switch (*fptr++) { + case '!': /* reset all fields to default */ + timelib_time_reset_fields(s->time); + break; + + case '|': /* reset all fields to default when not set */ + timelib_time_reset_unset_fields(s->time); + break; + + default: + add_pbf_error(s, "Data missing", string, ptr); + } + } } /* clean up a bit */ diff --git a/ext/date/lib/parse_date.re b/ext/date/lib/parse_date.re index 4943c36d0..89bf2d697 100644 --- a/ext/date/lib/parse_date.re +++ b/ext/date/lib/parse_date.re @@ -16,13 +16,14 @@ +----------------------------------------------------------------------+ */ -/* $Id: parse_date.re 305316 2010-11-13 15:01:48Z derick $ */ +/* $Id: parse_date.re 311831 2011-06-05 13:30:01Z bjori $ */ #include "timelib.h" #include <stdio.h> #include <ctype.h> #include <math.h> +#include <assert.h> #ifdef HAVE_STDLIB_H #include <stdlib.h> @@ -718,6 +719,25 @@ const static timelib_tz_lookup_table* zone_search(const char *word, long gmtoffs return first_found_elem; } + for (tp = timelib_timezone_lookup; tp->name; tp++) { + if (tp->full_tz_name && strcasecmp(word, tp->full_tz_name) == 0) { + if (!first_found) { + first_found = 1; + first_found_elem = tp; + if (gmtoffset == -1) { + return tp; + } + } + if (tp->gmtoffset == gmtoffset) { + return tp; + } + } + } + if (first_found) { + return first_found_elem; + } + + /* Still didn't find anything, let's find the zone solely based on * offset/isdst then */ for (fmp = timelib_timezone_fallbackmap; fmp->name; fmp++) { @@ -1822,6 +1842,30 @@ timelib_time* timelib_strtotime(char *s, int len, struct timelib_error_container add_pbf_error(s, "Unexpected data found.", string, begin); \ } +static void timelib_time_reset_fields(timelib_time *time) +{ + assert(time != NULL); + + time->y = 1970; + time->m = 1; + time->d = 1; + time->h = time->i = time->s = 0; + time->f = 0.0; + time->tz_info = NULL; +} + +static void timelib_time_reset_unset_fields(timelib_time *time) +{ + assert(time != NULL); + + if (time->y == TIMELIB_UNSET ) time->y = 1970; + if (time->m == TIMELIB_UNSET ) time->m = 1; + if (time->d == TIMELIB_UNSET ) time->d = 1; + if (time->h == TIMELIB_UNSET ) time->h = 0; + if (time->i == TIMELIB_UNSET ) time->i = 0; + if (time->s == TIMELIB_UNSET ) time->s = 0; + if (time->f == TIMELIB_UNSET ) time->f = 0.0; +} timelib_time *timelib_parse_from_format(char *format, char *string, int len, timelib_error_container **errors, const timelib_tzdb *tzdb) { @@ -2020,23 +2064,11 @@ timelib_time *timelib_parse_from_format(char *format, char *string, int len, tim break; case '!': /* reset all fields to default */ - s->time->y = 1970; - s->time->m = 1; - s->time->d = 1; - s->time->h = s->time->i = s->time->s = 0; - s->time->f = 0.0; - s->time->tz_info = NULL; + timelib_time_reset_fields(s->time); break; /* break intentionally not missing */ case '|': /* reset all fields to default when not set */ - if (s->time->y == TIMELIB_UNSET ) s->time->y = 1970; - if (s->time->m == TIMELIB_UNSET ) s->time->m = 1; - if (s->time->d == TIMELIB_UNSET ) s->time->d = 1; - if (s->time->h == TIMELIB_UNSET ) s->time->h = 0; - if (s->time->i == TIMELIB_UNSET ) s->time->i = 0; - if (s->time->s == TIMELIB_UNSET ) s->time->s = 0; - if (s->time->f == TIMELIB_UNSET ) s->time->f = 0.0; - + timelib_time_reset_unset_fields(s->time); break; /* break intentionally not missing */ case '?': /* random char */ @@ -2068,7 +2100,21 @@ timelib_time *timelib_parse_from_format(char *format, char *string, int len, tim add_pbf_error(s, "Trailing data", string, ptr); } if (*fptr) { - add_pbf_error(s, "Data missing", string, ptr); + /* Trailing | and ! specifiers are valid. */ + while (*fptr) { + switch (*fptr++) { + case '!': /* reset all fields to default */ + timelib_time_reset_fields(s->time); + break; + + case '|': /* reset all fields to default when not set */ + timelib_time_reset_unset_fields(s->time); + break; + + default: + add_pbf_error(s, "Data missing", string, ptr); + } + } } /* clean up a bit */ diff --git a/ext/date/lib/parse_tz.c b/ext/date/lib/parse_tz.c index 6239f9e8d..726550286 100644 --- a/ext/date/lib/parse_tz.c +++ b/ext/date/lib/parse_tz.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: parse_tz.c 293036 2010-01-03 09:23:27Z sebastian $ */ +/* $Id: parse_tz.c 311110 2011-05-16 21:29:45Z johannes $ */ #include "timelib.h" @@ -100,6 +100,7 @@ static void read_transistions(const unsigned char **tzf, timelib_tzinfo *tz) cbuffer = (unsigned char*) malloc(tz->timecnt * sizeof(unsigned char)); if (!cbuffer) { + free(buffer); return; } memcpy(cbuffer, *tzf, sizeof(unsigned char) * tz->timecnt); @@ -125,6 +126,7 @@ static void read_types(const unsigned char **tzf, timelib_tzinfo *tz) tz->type = (ttinfo*) malloc(tz->typecnt * sizeof(struct ttinfo)); if (!tz->type) { + free(buffer); return; } @@ -153,6 +155,7 @@ static void read_types(const unsigned char **tzf, timelib_tzinfo *tz) tz->leap_times = (tlinfo*) malloc(tz->leapcnt * sizeof(tlinfo)); if (!tz->leap_times) { + free(leap_buffer); return; } for (i = 0; i < tz->leapcnt; i++) { diff --git a/ext/date/lib/timezonedb.h b/ext/date/lib/timezonedb.h index b4c4e464a..7f2a3a0c6 100644 --- a/ext/date/lib/timezonedb.h +++ b/ext/date/lib/timezonedb.h @@ -1,4 +1,4 @@ -const timelib_tzdb_index_entry timezonedb_idx_builtin[571] = { +const timelib_tzdb_index_entry timezonedb_idx_builtin[573] = { { "Africa/Abidjan" , 0x000000 }, { "Africa/Accra" , 0x000055 }, { "Africa/Addis_Ababa" , 0x0000FD }, @@ -13,566 +13,568 @@ const timelib_tzdb_index_entry timezonedb_idx_builtin[571] = { { "Africa/Brazzaville" , 0x00051C }, { "Africa/Bujumbura" , 0x000571 }, { "Africa/Cairo" , 0x0005B5 }, - { "Africa/Casablanca" , 0x000986 }, - { "Africa/Ceuta" , 0x000A6C }, - { "Africa/Conakry" , 0x000D73 }, - { "Africa/Dakar" , 0x000DDE }, - { "Africa/Dar_es_Salaam" , 0x000E44 }, - { "Africa/Djibouti" , 0x000EB1 }, - { "Africa/Douala" , 0x000F06 }, - { "Africa/El_Aaiun" , 0x000F5B }, - { "Africa/Freetown" , 0x000FC1 }, - { "Africa/Gaborone" , 0x0010D0 }, - { "Africa/Harare" , 0x00112B }, - { "Africa/Johannesburg" , 0x001180 }, - { "Africa/Kampala" , 0x0011EE }, - { "Africa/Khartoum" , 0x00126D }, - { "Africa/Kigali" , 0x001380 }, - { "Africa/Kinshasa" , 0x0013D5 }, - { "Africa/Lagos" , 0x001430 }, - { "Africa/Libreville" , 0x001485 }, - { "Africa/Lome" , 0x0014DA }, - { "Africa/Luanda" , 0x00151E }, - { "Africa/Lubumbashi" , 0x001573 }, - { "Africa/Lusaka" , 0x0015CE }, - { "Africa/Malabo" , 0x001623 }, - { "Africa/Maputo" , 0x001689 }, - { "Africa/Maseru" , 0x0016DE }, - { "Africa/Mbabane" , 0x001746 }, - { "Africa/Mogadishu" , 0x00179C }, - { "Africa/Monrovia" , 0x0017F7 }, - { "Africa/Nairobi" , 0x00185D }, - { "Africa/Ndjamena" , 0x0018DC }, - { "Africa/Niamey" , 0x001948 }, - { "Africa/Nouakchott" , 0x0019BB }, - { "Africa/Ouagadougou" , 0x001A26 }, - { "Africa/Porto-Novo" , 0x001A7B }, - { "Africa/Sao_Tome" , 0x001AE1 }, - { "Africa/Timbuktu" , 0x001B36 }, - { "Africa/Tripoli" , 0x001BA1 }, - { "Africa/Tunis" , 0x001C9B }, - { "Africa/Windhoek" , 0x001DAD }, - { "America/Adak" , 0x001FF4 }, - { "America/Anchorage" , 0x00236A }, - { "America/Anguilla" , 0x0026DE }, - { "America/Antigua" , 0x002733 }, - { "America/Araguaina" , 0x002799 }, - { "America/Argentina/Buenos_Aires" , 0x0028F4 }, - { "America/Argentina/Catamarca" , 0x002AA2 }, - { "America/Argentina/ComodRivadavia" , 0x002C63 }, - { "America/Argentina/Cordoba" , 0x002E09 }, - { "America/Argentina/Jujuy" , 0x002FDE }, - { "America/Argentina/La_Rioja" , 0x003192 }, - { "America/Argentina/Mendoza" , 0x00334A }, - { "America/Argentina/Rio_Gallegos" , 0x00350A }, - { "America/Argentina/Salta" , 0x0036BF }, - { "America/Argentina/San_Juan" , 0x00386B }, - { "America/Argentina/San_Luis" , 0x003A23 }, - { "America/Argentina/Tucuman" , 0x003BE9 }, - { "America/Argentina/Ushuaia" , 0x003DA5 }, - { "America/Aruba" , 0x003F60 }, - { "America/Asuncion" , 0x003FC6 }, - { "America/Atikokan" , 0x0042AB }, - { "America/Atka" , 0x004381 }, - { "America/Bahia" , 0x0046E7 }, - { "America/Bahia_Banderas" , 0x004870 }, - { "America/Barbados" , 0x004AE9 }, - { "America/Belem" , 0x004B83 }, - { "America/Belize" , 0x004C7E }, - { "America/Blanc-Sablon" , 0x004DFA }, - { "America/Boa_Vista" , 0x004EAE }, - { "America/Bogota" , 0x004FB7 }, - { "America/Boise" , 0x005023 }, - { "America/Buenos_Aires" , 0x0053BA }, - { "America/Cambridge_Bay" , 0x005553 }, - { "America/Campo_Grande" , 0x00587B }, - { "America/Cancun" , 0x005B6A }, - { "America/Caracas" , 0x005DAC }, - { "America/Catamarca" , 0x005E13 }, - { "America/Cayenne" , 0x005FB9 }, - { "America/Cayman" , 0x00601B }, - { "America/Chicago" , 0x006070 }, - { "America/Chihuahua" , 0x006587 }, - { "America/Coral_Harbour" , 0x0067F2 }, - { "America/Cordoba" , 0x006884 }, - { "America/Costa_Rica" , 0x006A2A }, - { "America/Cuiaba" , 0x006AB4 }, - { "America/Curacao" , 0x006D92 }, - { "America/Danmarkshavn" , 0x006DF8 }, - { "America/Dawson" , 0x006F3C }, - { "America/Dawson_Creek" , 0x007259 }, - { "America/Denver" , 0x007433 }, - { "America/Detroit" , 0x0077B9 }, - { "America/Dominica" , 0x007B18 }, - { "America/Edmonton" , 0x007B6D }, - { "America/Eirunepe" , 0x007F25 }, - { "America/El_Salvador" , 0x008038 }, - { "America/Ensenada" , 0x0080AD }, - { "America/Fort_Wayne" , 0x008554 }, - { "America/Fortaleza" , 0x008416 }, - { "America/Glace_Bay" , 0x0087BE }, - { "America/Godthab" , 0x008B35 }, - { "America/Goose_Bay" , 0x008DF9 }, - { "America/Grand_Turk" , 0x0092B6 }, - { "America/Grenada" , 0x009565 }, - { "America/Guadeloupe" , 0x0095BA }, - { "America/Guatemala" , 0x00960F }, - { "America/Guayaquil" , 0x009698 }, - { "America/Guyana" , 0x0096F5 }, - { "America/Halifax" , 0x009776 }, - { "America/Havana" , 0x009C8C }, - { "America/Hermosillo" , 0x009FFF }, - { "America/Indiana/Indianapolis" , 0x00A0DD }, - { "America/Indiana/Knox" , 0x00A36E }, - { "America/Indiana/Marengo" , 0x00A705 }, - { "America/Indiana/Petersburg" , 0x00A9AB }, - { "America/Indiana/Tell_City" , 0x00AEF8 }, - { "America/Indiana/Vevay" , 0x00B191 }, - { "America/Indiana/Vincennes" , 0x00B3CC }, - { "America/Indiana/Winamac" , 0x00B680 }, - { "America/Indianapolis" , 0x00AC8E }, - { "America/Inuvik" , 0x00B939 }, - { "America/Iqaluit" , 0x00BC30 }, - { "America/Jamaica" , 0x00BF52 }, - { "America/Jujuy" , 0x00C017 }, - { "America/Juneau" , 0x00C1C1 }, - { "America/Kentucky/Louisville" , 0x00C53F }, - { "America/Kentucky/Monticello" , 0x00C95D }, - { "America/Knox_IN" , 0x00CCE2 }, - { "America/La_Paz" , 0x00D053 }, - { "America/Lima" , 0x00D0BA }, - { "America/Los_Angeles" , 0x00D162 }, - { "America/Louisville" , 0x00D573 }, - { "America/Maceio" , 0x00D968 }, - { "America/Managua" , 0x00DAA2 }, - { "America/Manaus" , 0x00DB55 }, - { "America/Marigot" , 0x00DC57 }, - { "America/Martinique" , 0x00DCAC }, - { "America/Matamoros" , 0x00DD18 }, - { "America/Mazatlan" , 0x00DF71 }, - { "America/Mendoza" , 0x00E1DE }, - { "America/Menominee" , 0x00E392 }, - { "America/Merida" , 0x00E713 }, - { "America/Metlakatla" , 0x00E94E }, - { "America/Mexico_City" , 0x00ECAC }, - { "America/Miquelon" , 0x00EF27 }, - { "America/Moncton" , 0x00F199 }, - { "America/Monterrey" , 0x00F630 }, - { "America/Montevideo" , 0x00F893 }, - { "America/Montreal" , 0x00FBA5 }, - { "America/Montserrat" , 0x0100BB }, - { "America/Nassau" , 0x010110 }, - { "America/New_York" , 0x010455 }, - { "America/Nipigon" , 0x010960 }, - { "America/Nome" , 0x010CB1 }, - { "America/Noronha" , 0x01102F }, - { "America/North_Dakota/Beulah" , 0x01115F }, - { "America/North_Dakota/Center" , 0x0114F3 }, - { "America/North_Dakota/New_Salem" , 0x011887 }, - { "America/Ojinaga" , 0x011C30 }, - { "America/Panama" , 0x011E91 }, - { "America/Pangnirtung" , 0x011EE6 }, - { "America/Paramaribo" , 0x01221C }, - { "America/Phoenix" , 0x0122AE }, - { "America/Port-au-Prince" , 0x01235C }, - { "America/Port_of_Spain" , 0x012577 }, - { "America/Porto_Acre" , 0x012478 }, - { "America/Porto_Velho" , 0x0125CC }, - { "America/Puerto_Rico" , 0x0126C2 }, - { "America/Rainy_River" , 0x01272D }, - { "America/Rankin_Inlet" , 0x012A65 }, - { "America/Recife" , 0x012D4B }, - { "America/Regina" , 0x012E75 }, - { "America/Resolute" , 0x013033 }, - { "America/Rio_Branco" , 0x01332C }, - { "America/Rosario" , 0x01342F }, - { "America/Santa_Isabel" , 0x0135D5 }, - { "America/Santarem" , 0x013978 }, - { "America/Santiago" , 0x013A7D }, - { "America/Santo_Domingo" , 0x013E26 }, - { "America/Sao_Paulo" , 0x013EEC }, - { "America/Scoresbysund" , 0x0141FB }, - { "America/Shiprock" , 0x0144E9 }, - { "America/Sitka" , 0x014878 }, - { "America/St_Barthelemy" , 0x014C00 }, - { "America/St_Johns" , 0x014C55 }, - { "America/St_Kitts" , 0x0151A8 }, - { "America/St_Lucia" , 0x0151FD }, - { "America/St_Thomas" , 0x015252 }, - { "America/St_Vincent" , 0x0152A7 }, - { "America/Swift_Current" , 0x0152FC }, - { "America/Tegucigalpa" , 0x01541D }, - { "America/Thule" , 0x01549C }, - { "America/Thunder_Bay" , 0x0156E3 }, - { "America/Tijuana" , 0x015A2C }, - { "America/Toronto" , 0x015DC5 }, - { "America/Tortola" , 0x0162DC }, - { "America/Vancouver" , 0x016331 }, - { "America/Virgin" , 0x01676E }, - { "America/Whitehorse" , 0x0167C3 }, - { "America/Winnipeg" , 0x016AE0 }, - { "America/Yakutat" , 0x016F20 }, - { "America/Yellowknife" , 0x01728B }, - { "Antarctica/Casey" , 0x01759B }, - { "Antarctica/Davis" , 0x017626 }, - { "Antarctica/DumontDUrville" , 0x0176BD }, - { "Antarctica/Macquarie" , 0x01774F }, - { "Antarctica/Mawson" , 0x0179C9 }, - { "Antarctica/McMurdo" , 0x017A45 }, - { "Antarctica/Palmer" , 0x017D47 }, - { "Antarctica/Rothera" , 0x018063 }, - { "Antarctica/South_Pole" , 0x0180D9 }, - { "Antarctica/Syowa" , 0x0183E1 }, - { "Antarctica/Vostok" , 0x01844F }, - { "Arctic/Longyearbyen" , 0x0184C0 }, - { "Asia/Aden" , 0x0187F2 }, - { "Asia/Almaty" , 0x018847 }, - { "Asia/Amman" , 0x0189C6 }, - { "Asia/Anadyr" , 0x018C86 }, - { "Asia/Aqtau" , 0x018F74 }, - { "Asia/Aqtobe" , 0x019173 }, - { "Asia/Ashgabat" , 0x01932B }, - { "Asia/Ashkhabad" , 0x019448 }, - { "Asia/Baghdad" , 0x019565 }, - { "Asia/Bahrain" , 0x0196DA }, - { "Asia/Baku" , 0x019740 }, - { "Asia/Bangkok" , 0x019A28 }, - { "Asia/Beirut" , 0x019A7D }, - { "Asia/Bishkek" , 0x019D8A }, - { "Asia/Brunei" , 0x019F36 }, - { "Asia/Calcutta" , 0x019F98 }, - { "Asia/Choibalsan" , 0x01A011 }, - { "Asia/Chongqing" , 0x01A18A }, - { "Asia/Chungking" , 0x01A279 }, - { "Asia/Colombo" , 0x01A328 }, - { "Asia/Dacca" , 0x01A3C4 }, - { "Asia/Damascus" , 0x01A46A }, - { "Asia/Dhaka" , 0x01A7BA }, - { "Asia/Dili" , 0x01A860 }, - { "Asia/Dubai" , 0x01A8E9 }, - { "Asia/Dushanbe" , 0x01A93E }, - { "Asia/Gaza" , 0x01AA41 }, - { "Asia/Harbin" , 0x01AD8A }, - { "Asia/Ho_Chi_Minh" , 0x01AE71 }, - { "Asia/Hong_Kong" , 0x01AEE9 }, - { "Asia/Hovd" , 0x01B0AB }, - { "Asia/Irkutsk" , 0x01B223 }, - { "Asia/Istanbul" , 0x01B50A }, - { "Asia/Jakarta" , 0x01B8F7 }, - { "Asia/Jayapura" , 0x01B9A1 }, - { "Asia/Jerusalem" , 0x01BA3D }, - { "Asia/Kabul" , 0x01BD6C }, - { "Asia/Kamchatka" , 0x01BDBD }, - { "Asia/Karachi" , 0x01C0A2 }, - { "Asia/Kashgar" , 0x01C157 }, - { "Asia/Kathmandu" , 0x01C228 }, - { "Asia/Katmandu" , 0x01C28E }, - { "Asia/Kolkata" , 0x01C2F4 }, - { "Asia/Krasnoyarsk" , 0x01C36D }, - { "Asia/Kuala_Lumpur" , 0x01C656 }, - { "Asia/Kuching" , 0x01C713 }, - { "Asia/Kuwait" , 0x01C801 }, - { "Asia/Macao" , 0x01C856 }, - { "Asia/Macau" , 0x01C991 }, - { "Asia/Magadan" , 0x01CACC }, - { "Asia/Makassar" , 0x01CDAF }, - { "Asia/Manila" , 0x01CE73 }, - { "Asia/Muscat" , 0x01CEF8 }, - { "Asia/Nicosia" , 0x01CF4D }, - { "Asia/Novokuznetsk" , 0x01D235 }, - { "Asia/Novosibirsk" , 0x01D538 }, - { "Asia/Omsk" , 0x01D82C }, - { "Asia/Oral" , 0x01DB14 }, - { "Asia/Phnom_Penh" , 0x01DCE4 }, - { "Asia/Pontianak" , 0x01DD5C }, - { "Asia/Pyongyang" , 0x01DE1D }, - { "Asia/Qatar" , 0x01DE8A }, - { "Asia/Qyzylorda" , 0x01DEF0 }, - { "Asia/Rangoon" , 0x01E0C6 }, - { "Asia/Riyadh" , 0x01E13E }, - { "Asia/Saigon" , 0x01E193 }, - { "Asia/Sakhalin" , 0x01E20B }, - { "Asia/Samarkand" , 0x01E50B }, - { "Asia/Seoul" , 0x01E641 }, - { "Asia/Shanghai" , 0x01E6E5 }, - { "Asia/Singapore" , 0x01E7C5 }, - { "Asia/Taipei" , 0x01E87C }, - { "Asia/Tashkent" , 0x01E994 }, - { "Asia/Tbilisi" , 0x01EAC5 }, - { "Asia/Tehran" , 0x01EC7F }, - { "Asia/Tel_Aviv" , 0x01EEED }, - { "Asia/Thimbu" , 0x01F21C }, - { "Asia/Thimphu" , 0x01F282 }, - { "Asia/Tokyo" , 0x01F2E8 }, - { "Asia/Ujung_Pandang" , 0x01F371 }, - { "Asia/Ulaanbaatar" , 0x01F3ED }, - { "Asia/Ulan_Bator" , 0x01F548 }, - { "Asia/Urumqi" , 0x01F695 }, - { "Asia/Vientiane" , 0x01F75C }, - { "Asia/Vladivostok" , 0x01F7D4 }, - { "Asia/Yakutsk" , 0x01FAC1 }, - { "Asia/Yekaterinburg" , 0x01FDA7 }, - { "Asia/Yerevan" , 0x0200B3 }, - { "Atlantic/Azores" , 0x0203B7 }, - { "Atlantic/Bermuda" , 0x0208BA }, - { "Atlantic/Canary" , 0x020B9B }, - { "Atlantic/Cape_Verde" , 0x020E71 }, - { "Atlantic/Faeroe" , 0x020EEA }, - { "Atlantic/Faroe" , 0x02118E }, - { "Atlantic/Jan_Mayen" , 0x021432 }, - { "Atlantic/Madeira" , 0x021764 }, - { "Atlantic/Reykjavik" , 0x021C6D }, - { "Atlantic/South_Georgia" , 0x021E26 }, - { "Atlantic/St_Helena" , 0x02213E }, - { "Atlantic/Stanley" , 0x021E6A }, - { "Australia/ACT" , 0x022193 }, - { "Australia/Adelaide" , 0x0224B0 }, - { "Australia/Brisbane" , 0x0227DC }, - { "Australia/Broken_Hill" , 0x0228A3 }, - { "Australia/Canberra" , 0x022BE1 }, - { "Australia/Currie" , 0x022EFE }, - { "Australia/Darwin" , 0x023231 }, - { "Australia/Eucla" , 0x0232B7 }, - { "Australia/Hobart" , 0x02338C }, - { "Australia/LHI" , 0x0236EA }, - { "Australia/Lindeman" , 0x023985 }, - { "Australia/Lord_Howe" , 0x023A66 }, - { "Australia/Melbourne" , 0x023D11 }, - { "Australia/North" , 0x024036 }, - { "Australia/NSW" , 0x0240AA }, - { "Australia/Perth" , 0x0243C7 }, - { "Australia/Queensland" , 0x02449F }, - { "Australia/South" , 0x02454B }, - { "Australia/Sydney" , 0x024868 }, - { "Australia/Tasmania" , 0x024BA5 }, - { "Australia/Victoria" , 0x024EEA }, - { "Australia/West" , 0x025207 }, - { "Australia/Yancowinna" , 0x0252BD }, - { "Brazil/Acre" , 0x0255DF }, - { "Brazil/DeNoronha" , 0x0256DE }, - { "Brazil/East" , 0x0257FE }, - { "Brazil/West" , 0x025ADB }, - { "Canada/Atlantic" , 0x025BD3 }, - { "Canada/Central" , 0x0260BB }, - { "Canada/East-Saskatchewan" , 0x0269C5 }, - { "Canada/Eastern" , 0x0264D5 }, - { "Canada/Mountain" , 0x026B4E }, - { "Canada/Newfoundland" , 0x026EC4 }, - { "Canada/Pacific" , 0x0273EF }, - { "Canada/Saskatchewan" , 0x027808 }, - { "Canada/Yukon" , 0x027991 }, - { "CET" , 0x027C94 }, - { "Chile/Continental" , 0x027F9D }, - { "Chile/EasterIsland" , 0x028338 }, - { "CST6CDT" , 0x02867A }, - { "Cuba" , 0x0289CB }, - { "EET" , 0x028D3E }, - { "Egypt" , 0x028FF1 }, - { "Eire" , 0x0293C2 }, - { "EST" , 0x0298D3 }, - { "EST5EDT" , 0x029917 }, - { "Etc/GMT" , 0x029C68 }, - { "Etc/GMT+0" , 0x029D34 }, - { "Etc/GMT+1" , 0x029DBE }, - { "Etc/GMT+10" , 0x029E4B }, - { "Etc/GMT+11" , 0x029ED9 }, - { "Etc/GMT+12" , 0x029F67 }, - { "Etc/GMT+2" , 0x02A082 }, - { "Etc/GMT+3" , 0x02A10E }, - { "Etc/GMT+4" , 0x02A19A }, - { "Etc/GMT+5" , 0x02A226 }, - { "Etc/GMT+6" , 0x02A2B2 }, - { "Etc/GMT+7" , 0x02A33E }, - { "Etc/GMT+8" , 0x02A3CA }, - { "Etc/GMT+9" , 0x02A456 }, - { "Etc/GMT-0" , 0x029CF0 }, - { "Etc/GMT-1" , 0x029D78 }, - { "Etc/GMT-10" , 0x029E04 }, - { "Etc/GMT-11" , 0x029E92 }, - { "Etc/GMT-12" , 0x029F20 }, - { "Etc/GMT-13" , 0x029FAE }, - { "Etc/GMT-14" , 0x029FF5 }, - { "Etc/GMT-2" , 0x02A03C }, - { "Etc/GMT-3" , 0x02A0C8 }, - { "Etc/GMT-4" , 0x02A154 }, - { "Etc/GMT-5" , 0x02A1E0 }, - { "Etc/GMT-6" , 0x02A26C }, - { "Etc/GMT-7" , 0x02A2F8 }, - { "Etc/GMT-8" , 0x02A384 }, - { "Etc/GMT-9" , 0x02A410 }, - { "Etc/GMT0" , 0x029CAC }, - { "Etc/Greenwich" , 0x02A49C }, - { "Etc/UCT" , 0x02A4E0 }, - { "Etc/Universal" , 0x02A524 }, - { "Etc/UTC" , 0x02A568 }, - { "Etc/Zulu" , 0x02A5AC }, - { "Europe/Amsterdam" , 0x02A5F0 }, - { "Europe/Andorra" , 0x02AA2E }, - { "Europe/Athens" , 0x02ACAA }, - { "Europe/Belfast" , 0x02AFED }, - { "Europe/Belgrade" , 0x02B524 }, - { "Europe/Berlin" , 0x02B7ED }, - { "Europe/Bratislava" , 0x02BB43 }, - { "Europe/Brussels" , 0x02BE75 }, - { "Europe/Bucharest" , 0x02C2AC }, - { "Europe/Budapest" , 0x02C5D6 }, - { "Europe/Chisinau" , 0x02C949 }, - { "Europe/Copenhagen" , 0x02CCD7 }, - { "Europe/Dublin" , 0x02CFE1 }, - { "Europe/Gibraltar" , 0x02D4F2 }, - { "Europe/Guernsey" , 0x02D949 }, - { "Europe/Helsinki" , 0x02DE80 }, - { "Europe/Isle_of_Man" , 0x02E136 }, - { "Europe/Istanbul" , 0x02E66D }, - { "Europe/Jersey" , 0x02EA5A }, - { "Europe/Kaliningrad" , 0x02EF91 }, - { "Europe/Kiev" , 0x02F2F4 }, - { "Europe/Lisbon" , 0x02F60B }, - { "Europe/Ljubljana" , 0x02FB0F }, - { "Europe/London" , 0x02FDD8 }, - { "Europe/Luxembourg" , 0x03030F }, - { "Europe/Madrid" , 0x030765 }, - { "Europe/Malta" , 0x030B2B }, - { "Europe/Mariehamn" , 0x030EE4 }, - { "Europe/Minsk" , 0x03119A }, - { "Europe/Monaco" , 0x0314A5 }, - { "Europe/Moscow" , 0x0318E0 }, - { "Europe/Nicosia" , 0x031C32 }, - { "Europe/Oslo" , 0x031F1A }, - { "Europe/Paris" , 0x03224C }, - { "Europe/Podgorica" , 0x032692 }, - { "Europe/Prague" , 0x03295B }, - { "Europe/Riga" , 0x032C8D }, - { "Europe/Rome" , 0x032FD2 }, - { "Europe/Samara" , 0x033395 }, - { "Europe/San_Marino" , 0x0336CE }, - { "Europe/Sarajevo" , 0x033A91 }, - { "Europe/Simferopol" , 0x033D5A }, - { "Europe/Skopje" , 0x034085 }, - { "Europe/Sofia" , 0x03434E }, - { "Europe/Stockholm" , 0x034656 }, - { "Europe/Tallinn" , 0x034905 }, - { "Europe/Tirane" , 0x034C3F }, - { "Europe/Tiraspol" , 0x034F45 }, - { "Europe/Uzhgorod" , 0x0352D3 }, - { "Europe/Vaduz" , 0x0355EA }, - { "Europe/Vatican" , 0x03587D }, - { "Europe/Vienna" , 0x035C40 }, - { "Europe/Vilnius" , 0x035F6D }, - { "Europe/Volgograd" , 0x0362AC }, - { "Europe/Warsaw" , 0x0365B5 }, - { "Europe/Zagreb" , 0x036996 }, - { "Europe/Zaporozhye" , 0x036C5F }, - { "Europe/Zurich" , 0x036FA0 }, - { "Factory" , 0x03724F }, - { "GB" , 0x0372C0 }, - { "GB-Eire" , 0x0377F7 }, - { "GMT" , 0x037D2E }, - { "GMT+0" , 0x037DFA }, - { "GMT-0" , 0x037DB6 }, - { "GMT0" , 0x037D72 }, - { "Greenwich" , 0x037E3E }, - { "Hongkong" , 0x037E82 }, - { "HST" , 0x038044 }, - { "Iceland" , 0x038088 }, - { "Indian/Antananarivo" , 0x038241 }, - { "Indian/Chagos" , 0x0382B5 }, - { "Indian/Christmas" , 0x038317 }, - { "Indian/Cocos" , 0x03835B }, - { "Indian/Comoro" , 0x03839F }, - { "Indian/Kerguelen" , 0x0383F4 }, - { "Indian/Mahe" , 0x038449 }, - { "Indian/Maldives" , 0x03849E }, - { "Indian/Mauritius" , 0x0384F3 }, - { "Indian/Mayotte" , 0x038569 }, - { "Indian/Reunion" , 0x0385BE }, - { "Iran" , 0x038613 }, - { "Israel" , 0x038881 }, - { "Jamaica" , 0x038BB0 }, - { "Japan" , 0x038C75 }, - { "Kwajalein" , 0x038CFE }, - { "Libya" , 0x038D61 }, - { "MET" , 0x038E5B }, - { "Mexico/BajaNorte" , 0x039164 }, - { "Mexico/BajaSur" , 0x0394CD }, - { "Mexico/General" , 0x039712 }, - { "MST" , 0x039970 }, - { "MST7MDT" , 0x0399B4 }, - { "Navajo" , 0x039D05 }, - { "NZ" , 0x03A07E }, - { "NZ-CHAT" , 0x03A3FC }, - { "Pacific/Apia" , 0x03A6E4 }, - { "Pacific/Auckland" , 0x03A762 }, - { "Pacific/Chatham" , 0x03AAEE }, - { "Pacific/Chuuk" , 0x03ADE5 }, - { "Pacific/Easter" , 0x03AE3E }, - { "Pacific/Efate" , 0x03B19C }, - { "Pacific/Enderbury" , 0x03B262 }, - { "Pacific/Fakaofo" , 0x03B2D0 }, - { "Pacific/Fiji" , 0x03B314 }, - { "Pacific/Funafuti" , 0x03B39E }, - { "Pacific/Galapagos" , 0x03B3E2 }, - { "Pacific/Gambier" , 0x03B45A }, - { "Pacific/Guadalcanal" , 0x03B4BF }, - { "Pacific/Guam" , 0x03B514 }, - { "Pacific/Honolulu" , 0x03B56A }, - { "Pacific/Johnston" , 0x03B5E1 }, - { "Pacific/Kiritimati" , 0x03B633 }, - { "Pacific/Kosrae" , 0x03B69E }, - { "Pacific/Kwajalein" , 0x03B6FB }, - { "Pacific/Majuro" , 0x03B767 }, - { "Pacific/Marquesas" , 0x03B7C6 }, - { "Pacific/Midway" , 0x03B82D }, - { "Pacific/Nauru" , 0x03B8B7 }, - { "Pacific/Niue" , 0x03B92F }, - { "Pacific/Norfolk" , 0x03B98D }, - { "Pacific/Noumea" , 0x03B9E2 }, - { "Pacific/Pago_Pago" , 0x03BA72 }, - { "Pacific/Palau" , 0x03BAFB }, - { "Pacific/Pitcairn" , 0x03BB3F }, - { "Pacific/Pohnpei" , 0x03BB94 }, - { "Pacific/Ponape" , 0x03BBE9 }, - { "Pacific/Port_Moresby" , 0x03BC2E }, - { "Pacific/Rarotonga" , 0x03BC72 }, - { "Pacific/Saipan" , 0x03BD4E }, - { "Pacific/Samoa" , 0x03BDB1 }, - { "Pacific/Tahiti" , 0x03BE3A }, - { "Pacific/Tarawa" , 0x03BE9F }, - { "Pacific/Tongatapu" , 0x03BEF3 }, - { "Pacific/Truk" , 0x03BF7F }, - { "Pacific/Wake" , 0x03BFC4 }, - { "Pacific/Wallis" , 0x03C014 }, - { "Pacific/Yap" , 0x03C058 }, - { "Poland" , 0x03C09D }, - { "Portugal" , 0x03C47E }, - { "PRC" , 0x03C97A }, - { "PST8PDT" , 0x03CA2B }, - { "ROC" , 0x03CD7C }, - { "ROK" , 0x03CE94 }, - { "Singapore" , 0x03CF38 }, - { "Turkey" , 0x03CFEF }, - { "UCT" , 0x03D3DC }, - { "Universal" , 0x03D420 }, - { "US/Alaska" , 0x03D464 }, - { "US/Aleutian" , 0x03D7CD }, - { "US/Arizona" , 0x03DB33 }, - { "US/Central" , 0x03DBC1 }, - { "US/East-Indiana" , 0x03E5CB }, - { "US/Eastern" , 0x03E0CC }, - { "US/Hawaii" , 0x03E835 }, - { "US/Indiana-Starke" , 0x03E8A6 }, - { "US/Michigan" , 0x03EC17 }, - { "US/Mountain" , 0x03EF4E }, - { "US/Pacific" , 0x03F2C7 }, - { "US/Pacific-New" , 0x03F6CC }, - { "US/Samoa" , 0x03FAD1 }, - { "UTC" , 0x03FB5A }, - { "W-SU" , 0x03FE51 }, - { "WET" , 0x03FB9E }, - { "Zulu" , 0x04018C }, + { "Africa/Casablanca" , 0x000878 }, + { "Africa/Ceuta" , 0x000968 }, + { "Africa/Conakry" , 0x000C6F }, + { "Africa/Dakar" , 0x000CDA }, + { "Africa/Dar_es_Salaam" , 0x000D40 }, + { "Africa/Djibouti" , 0x000DAD }, + { "Africa/Douala" , 0x000E02 }, + { "Africa/El_Aaiun" , 0x000E57 }, + { "Africa/Freetown" , 0x000EBD }, + { "Africa/Gaborone" , 0x000FCC }, + { "Africa/Harare" , 0x001027 }, + { "Africa/Johannesburg" , 0x00107C }, + { "Africa/Kampala" , 0x0010EA }, + { "Africa/Khartoum" , 0x001169 }, + { "Africa/Kigali" , 0x00127C }, + { "Africa/Kinshasa" , 0x0012D1 }, + { "Africa/Lagos" , 0x00132C }, + { "Africa/Libreville" , 0x001381 }, + { "Africa/Lome" , 0x0013D6 }, + { "Africa/Luanda" , 0x00141A }, + { "Africa/Lubumbashi" , 0x00146F }, + { "Africa/Lusaka" , 0x0014CA }, + { "Africa/Malabo" , 0x00151F }, + { "Africa/Maputo" , 0x001585 }, + { "Africa/Maseru" , 0x0015DA }, + { "Africa/Mbabane" , 0x001642 }, + { "Africa/Mogadishu" , 0x001698 }, + { "Africa/Monrovia" , 0x0016F3 }, + { "Africa/Nairobi" , 0x001759 }, + { "Africa/Ndjamena" , 0x0017D8 }, + { "Africa/Niamey" , 0x001844 }, + { "Africa/Nouakchott" , 0x0018B7 }, + { "Africa/Ouagadougou" , 0x001922 }, + { "Africa/Porto-Novo" , 0x001977 }, + { "Africa/Sao_Tome" , 0x0019DD }, + { "Africa/Timbuktu" , 0x001A32 }, + { "Africa/Tripoli" , 0x001A9D }, + { "Africa/Tunis" , 0x001B97 }, + { "Africa/Windhoek" , 0x001CA9 }, + { "America/Adak" , 0x001EF0 }, + { "America/Anchorage" , 0x002266 }, + { "America/Anguilla" , 0x0025DA }, + { "America/Antigua" , 0x00262F }, + { "America/Araguaina" , 0x002695 }, + { "America/Argentina/Buenos_Aires" , 0x0027F0 }, + { "America/Argentina/Catamarca" , 0x00299E }, + { "America/Argentina/ComodRivadavia" , 0x002B5F }, + { "America/Argentina/Cordoba" , 0x002D05 }, + { "America/Argentina/Jujuy" , 0x002EDA }, + { "America/Argentina/La_Rioja" , 0x00308E }, + { "America/Argentina/Mendoza" , 0x003246 }, + { "America/Argentina/Rio_Gallegos" , 0x003406 }, + { "America/Argentina/Salta" , 0x0035BB }, + { "America/Argentina/San_Juan" , 0x003767 }, + { "America/Argentina/San_Luis" , 0x00391F }, + { "America/Argentina/Tucuman" , 0x003AE5 }, + { "America/Argentina/Ushuaia" , 0x003CA1 }, + { "America/Aruba" , 0x003E5C }, + { "America/Asuncion" , 0x003EC2 }, + { "America/Atikokan" , 0x0041A7 }, + { "America/Atka" , 0x00427D }, + { "America/Bahia" , 0x0045E3 }, + { "America/Bahia_Banderas" , 0x00476C }, + { "America/Barbados" , 0x0049E5 }, + { "America/Belem" , 0x004A7F }, + { "America/Belize" , 0x004B7A }, + { "America/Blanc-Sablon" , 0x004CF6 }, + { "America/Boa_Vista" , 0x004DAA }, + { "America/Bogota" , 0x004EB3 }, + { "America/Boise" , 0x004F1F }, + { "America/Buenos_Aires" , 0x0052B6 }, + { "America/Cambridge_Bay" , 0x00544F }, + { "America/Campo_Grande" , 0x005777 }, + { "America/Cancun" , 0x005A66 }, + { "America/Caracas" , 0x005CA8 }, + { "America/Catamarca" , 0x005D0F }, + { "America/Cayenne" , 0x005EB5 }, + { "America/Cayman" , 0x005F17 }, + { "America/Chicago" , 0x005F6C }, + { "America/Chihuahua" , 0x006483 }, + { "America/Coral_Harbour" , 0x0066EE }, + { "America/Cordoba" , 0x006780 }, + { "America/Costa_Rica" , 0x006926 }, + { "America/Cuiaba" , 0x0069B0 }, + { "America/Curacao" , 0x006C8E }, + { "America/Danmarkshavn" , 0x006CF4 }, + { "America/Dawson" , 0x006E38 }, + { "America/Dawson_Creek" , 0x007155 }, + { "America/Denver" , 0x00732F }, + { "America/Detroit" , 0x0076B5 }, + { "America/Dominica" , 0x007A14 }, + { "America/Edmonton" , 0x007A69 }, + { "America/Eirunepe" , 0x007E21 }, + { "America/El_Salvador" , 0x007F34 }, + { "America/Ensenada" , 0x007FA9 }, + { "America/Fort_Wayne" , 0x008450 }, + { "America/Fortaleza" , 0x008312 }, + { "America/Glace_Bay" , 0x0086BA }, + { "America/Godthab" , 0x008A31 }, + { "America/Goose_Bay" , 0x008CF5 }, + { "America/Grand_Turk" , 0x0091B2 }, + { "America/Grenada" , 0x009461 }, + { "America/Guadeloupe" , 0x0094B6 }, + { "America/Guatemala" , 0x00950B }, + { "America/Guayaquil" , 0x009594 }, + { "America/Guyana" , 0x0095F1 }, + { "America/Halifax" , 0x009672 }, + { "America/Havana" , 0x009B88 }, + { "America/Hermosillo" , 0x009EFB }, + { "America/Indiana/Indianapolis" , 0x009FD9 }, + { "America/Indiana/Knox" , 0x00A26A }, + { "America/Indiana/Marengo" , 0x00A601 }, + { "America/Indiana/Petersburg" , 0x00A8A7 }, + { "America/Indiana/Tell_City" , 0x00ADF4 }, + { "America/Indiana/Vevay" , 0x00B08D }, + { "America/Indiana/Vincennes" , 0x00B2C8 }, + { "America/Indiana/Winamac" , 0x00B57C }, + { "America/Indianapolis" , 0x00AB8A }, + { "America/Inuvik" , 0x00B835 }, + { "America/Iqaluit" , 0x00BB2C }, + { "America/Jamaica" , 0x00BE4E }, + { "America/Jujuy" , 0x00BF13 }, + { "America/Juneau" , 0x00C0BD }, + { "America/Kentucky/Louisville" , 0x00C43B }, + { "America/Kentucky/Monticello" , 0x00C859 }, + { "America/Knox_IN" , 0x00CBDE }, + { "America/Kralendijk" , 0x00CF4F }, + { "America/La_Paz" , 0x00CFB5 }, + { "America/Lima" , 0x00D01C }, + { "America/Los_Angeles" , 0x00D0C4 }, + { "America/Louisville" , 0x00D4D5 }, + { "America/Lower_Princes" , 0x00D8CA }, + { "America/Maceio" , 0x00D930 }, + { "America/Managua" , 0x00DA6A }, + { "America/Manaus" , 0x00DB1D }, + { "America/Marigot" , 0x00DC1F }, + { "America/Martinique" , 0x00DC74 }, + { "America/Matamoros" , 0x00DCE0 }, + { "America/Mazatlan" , 0x00DF39 }, + { "America/Mendoza" , 0x00E1A6 }, + { "America/Menominee" , 0x00E35A }, + { "America/Merida" , 0x00E6DB }, + { "America/Metlakatla" , 0x00E916 }, + { "America/Mexico_City" , 0x00EC74 }, + { "America/Miquelon" , 0x00EEEF }, + { "America/Moncton" , 0x00F161 }, + { "America/Monterrey" , 0x00F5F8 }, + { "America/Montevideo" , 0x00F85B }, + { "America/Montreal" , 0x00FB6D }, + { "America/Montserrat" , 0x010083 }, + { "America/Nassau" , 0x0100D8 }, + { "America/New_York" , 0x01041D }, + { "America/Nipigon" , 0x010928 }, + { "America/Nome" , 0x010C79 }, + { "America/Noronha" , 0x010FF7 }, + { "America/North_Dakota/Beulah" , 0x011127 }, + { "America/North_Dakota/Center" , 0x0114BB }, + { "America/North_Dakota/New_Salem" , 0x01184F }, + { "America/Ojinaga" , 0x011BF8 }, + { "America/Panama" , 0x011E59 }, + { "America/Pangnirtung" , 0x011EAE }, + { "America/Paramaribo" , 0x0121E4 }, + { "America/Phoenix" , 0x012276 }, + { "America/Port-au-Prince" , 0x012324 }, + { "America/Port_of_Spain" , 0x01253F }, + { "America/Porto_Acre" , 0x012440 }, + { "America/Porto_Velho" , 0x012594 }, + { "America/Puerto_Rico" , 0x01268A }, + { "America/Rainy_River" , 0x0126F5 }, + { "America/Rankin_Inlet" , 0x012A2D }, + { "America/Recife" , 0x012D13 }, + { "America/Regina" , 0x012E3D }, + { "America/Resolute" , 0x012FFB }, + { "America/Rio_Branco" , 0x0132F4 }, + { "America/Rosario" , 0x0133F7 }, + { "America/Santa_Isabel" , 0x01359D }, + { "America/Santarem" , 0x013940 }, + { "America/Santiago" , 0x013A45 }, + { "America/Santo_Domingo" , 0x013DEE }, + { "America/Sao_Paulo" , 0x013EB4 }, + { "America/Scoresbysund" , 0x0141C3 }, + { "America/Shiprock" , 0x0144B1 }, + { "America/Sitka" , 0x014840 }, + { "America/St_Barthelemy" , 0x014BC8 }, + { "America/St_Johns" , 0x014C1D }, + { "America/St_Kitts" , 0x015170 }, + { "America/St_Lucia" , 0x0151C5 }, + { "America/St_Thomas" , 0x01521A }, + { "America/St_Vincent" , 0x01526F }, + { "America/Swift_Current" , 0x0152C4 }, + { "America/Tegucigalpa" , 0x0153E5 }, + { "America/Thule" , 0x015464 }, + { "America/Thunder_Bay" , 0x0156AB }, + { "America/Tijuana" , 0x0159F4 }, + { "America/Toronto" , 0x015D8D }, + { "America/Tortola" , 0x0162A4 }, + { "America/Vancouver" , 0x0162F9 }, + { "America/Virgin" , 0x016736 }, + { "America/Whitehorse" , 0x01678B }, + { "America/Winnipeg" , 0x016AA8 }, + { "America/Yakutat" , 0x016EE8 }, + { "America/Yellowknife" , 0x017253 }, + { "Antarctica/Casey" , 0x017563 }, + { "Antarctica/Davis" , 0x0175EE }, + { "Antarctica/DumontDUrville" , 0x017685 }, + { "Antarctica/Macquarie" , 0x017717 }, + { "Antarctica/Mawson" , 0x017991 }, + { "Antarctica/McMurdo" , 0x017A0D }, + { "Antarctica/Palmer" , 0x017D0F }, + { "Antarctica/Rothera" , 0x01802B }, + { "Antarctica/South_Pole" , 0x0180A1 }, + { "Antarctica/Syowa" , 0x0183A9 }, + { "Antarctica/Vostok" , 0x018417 }, + { "Arctic/Longyearbyen" , 0x018488 }, + { "Asia/Aden" , 0x0187BA }, + { "Asia/Almaty" , 0x01880F }, + { "Asia/Amman" , 0x01898E }, + { "Asia/Anadyr" , 0x018C4E }, + { "Asia/Aqtau" , 0x018E33 }, + { "Asia/Aqtobe" , 0x019032 }, + { "Asia/Ashgabat" , 0x0191EA }, + { "Asia/Ashkhabad" , 0x019307 }, + { "Asia/Baghdad" , 0x019424 }, + { "Asia/Bahrain" , 0x019599 }, + { "Asia/Baku" , 0x0195FF }, + { "Asia/Bangkok" , 0x0198E7 }, + { "Asia/Beirut" , 0x01993C }, + { "Asia/Bishkek" , 0x019C49 }, + { "Asia/Brunei" , 0x019DF5 }, + { "Asia/Calcutta" , 0x019E57 }, + { "Asia/Choibalsan" , 0x019ED0 }, + { "Asia/Chongqing" , 0x01A049 }, + { "Asia/Chungking" , 0x01A138 }, + { "Asia/Colombo" , 0x01A1E7 }, + { "Asia/Dacca" , 0x01A283 }, + { "Asia/Damascus" , 0x01A329 }, + { "Asia/Dhaka" , 0x01A679 }, + { "Asia/Dili" , 0x01A71F }, + { "Asia/Dubai" , 0x01A7A8 }, + { "Asia/Dushanbe" , 0x01A7FD }, + { "Asia/Gaza" , 0x01A900 }, + { "Asia/Harbin" , 0x01AC49 }, + { "Asia/Ho_Chi_Minh" , 0x01AD30 }, + { "Asia/Hong_Kong" , 0x01ADA8 }, + { "Asia/Hovd" , 0x01AF6A }, + { "Asia/Irkutsk" , 0x01B0E2 }, + { "Asia/Istanbul" , 0x01B2C8 }, + { "Asia/Jakarta" , 0x01B6B5 }, + { "Asia/Jayapura" , 0x01B75F }, + { "Asia/Jerusalem" , 0x01B7FB }, + { "Asia/Kabul" , 0x01BB2A }, + { "Asia/Kamchatka" , 0x01BB7B }, + { "Asia/Karachi" , 0x01BD57 }, + { "Asia/Kashgar" , 0x01BE0C }, + { "Asia/Kathmandu" , 0x01BEDD }, + { "Asia/Katmandu" , 0x01BF43 }, + { "Asia/Kolkata" , 0x01BFA9 }, + { "Asia/Krasnoyarsk" , 0x01C022 }, + { "Asia/Kuala_Lumpur" , 0x01C20A }, + { "Asia/Kuching" , 0x01C2C7 }, + { "Asia/Kuwait" , 0x01C3B5 }, + { "Asia/Macao" , 0x01C40A }, + { "Asia/Macau" , 0x01C545 }, + { "Asia/Magadan" , 0x01C680 }, + { "Asia/Makassar" , 0x01C862 }, + { "Asia/Manila" , 0x01C926 }, + { "Asia/Muscat" , 0x01C9AB }, + { "Asia/Nicosia" , 0x01CA00 }, + { "Asia/Novokuznetsk" , 0x01CCE8 }, + { "Asia/Novosibirsk" , 0x01CEEA }, + { "Asia/Omsk" , 0x01D0D5 }, + { "Asia/Oral" , 0x01D2BC }, + { "Asia/Phnom_Penh" , 0x01D48C }, + { "Asia/Pontianak" , 0x01D504 }, + { "Asia/Pyongyang" , 0x01D5C5 }, + { "Asia/Qatar" , 0x01D632 }, + { "Asia/Qyzylorda" , 0x01D698 }, + { "Asia/Rangoon" , 0x01D86E }, + { "Asia/Riyadh" , 0x01D8E6 }, + { "Asia/Saigon" , 0x01D93B }, + { "Asia/Sakhalin" , 0x01D9B3 }, + { "Asia/Samarkand" , 0x01DBAA }, + { "Asia/Seoul" , 0x01DCE0 }, + { "Asia/Shanghai" , 0x01DD84 }, + { "Asia/Singapore" , 0x01DE64 }, + { "Asia/Taipei" , 0x01DF1B }, + { "Asia/Tashkent" , 0x01E033 }, + { "Asia/Tbilisi" , 0x01E164 }, + { "Asia/Tehran" , 0x01E31E }, + { "Asia/Tel_Aviv" , 0x01E58C }, + { "Asia/Thimbu" , 0x01E8BB }, + { "Asia/Thimphu" , 0x01E921 }, + { "Asia/Tokyo" , 0x01E987 }, + { "Asia/Ujung_Pandang" , 0x01EA10 }, + { "Asia/Ulaanbaatar" , 0x01EA8C }, + { "Asia/Ulan_Bator" , 0x01EBE7 }, + { "Asia/Urumqi" , 0x01ED34 }, + { "Asia/Vientiane" , 0x01EDFB }, + { "Asia/Vladivostok" , 0x01EE73 }, + { "Asia/Yakutsk" , 0x01F05F }, + { "Asia/Yekaterinburg" , 0x01F244 }, + { "Asia/Yerevan" , 0x01F44F }, + { "Atlantic/Azores" , 0x01F753 }, + { "Atlantic/Bermuda" , 0x01FC56 }, + { "Atlantic/Canary" , 0x01FF37 }, + { "Atlantic/Cape_Verde" , 0x02020D }, + { "Atlantic/Faeroe" , 0x020286 }, + { "Atlantic/Faroe" , 0x02052A }, + { "Atlantic/Jan_Mayen" , 0x0207CE }, + { "Atlantic/Madeira" , 0x020B00 }, + { "Atlantic/Reykjavik" , 0x021009 }, + { "Atlantic/South_Georgia" , 0x0211C2 }, + { "Atlantic/St_Helena" , 0x0214D0 }, + { "Atlantic/Stanley" , 0x021206 }, + { "Australia/ACT" , 0x021525 }, + { "Australia/Adelaide" , 0x021842 }, + { "Australia/Brisbane" , 0x021B6E }, + { "Australia/Broken_Hill" , 0x021C35 }, + { "Australia/Canberra" , 0x021F73 }, + { "Australia/Currie" , 0x022290 }, + { "Australia/Darwin" , 0x0225C3 }, + { "Australia/Eucla" , 0x022649 }, + { "Australia/Hobart" , 0x02271E }, + { "Australia/LHI" , 0x022A7C }, + { "Australia/Lindeman" , 0x022D17 }, + { "Australia/Lord_Howe" , 0x022DF8 }, + { "Australia/Melbourne" , 0x0230A3 }, + { "Australia/North" , 0x0233C8 }, + { "Australia/NSW" , 0x02343C }, + { "Australia/Perth" , 0x023759 }, + { "Australia/Queensland" , 0x023831 }, + { "Australia/South" , 0x0238DD }, + { "Australia/Sydney" , 0x023BFA }, + { "Australia/Tasmania" , 0x023F37 }, + { "Australia/Victoria" , 0x02427C }, + { "Australia/West" , 0x024599 }, + { "Australia/Yancowinna" , 0x02464F }, + { "Brazil/Acre" , 0x024971 }, + { "Brazil/DeNoronha" , 0x024A70 }, + { "Brazil/East" , 0x024B90 }, + { "Brazil/West" , 0x024E6D }, + { "Canada/Atlantic" , 0x024F65 }, + { "Canada/Central" , 0x02544D }, + { "Canada/East-Saskatchewan" , 0x025D57 }, + { "Canada/Eastern" , 0x025867 }, + { "Canada/Mountain" , 0x025EE0 }, + { "Canada/Newfoundland" , 0x026256 }, + { "Canada/Pacific" , 0x026781 }, + { "Canada/Saskatchewan" , 0x026B9A }, + { "Canada/Yukon" , 0x026D23 }, + { "CET" , 0x027026 }, + { "Chile/Continental" , 0x02732F }, + { "Chile/EasterIsland" , 0x0276CA }, + { "CST6CDT" , 0x027A0C }, + { "Cuba" , 0x027D5D }, + { "EET" , 0x0280D0 }, + { "Egypt" , 0x028383 }, + { "Eire" , 0x028646 }, + { "EST" , 0x028B57 }, + { "EST5EDT" , 0x028B9B }, + { "Etc/GMT" , 0x028EEC }, + { "Etc/GMT+0" , 0x028FB8 }, + { "Etc/GMT+1" , 0x029042 }, + { "Etc/GMT+10" , 0x0290CF }, + { "Etc/GMT+11" , 0x02915D }, + { "Etc/GMT+12" , 0x0291EB }, + { "Etc/GMT+2" , 0x029306 }, + { "Etc/GMT+3" , 0x029392 }, + { "Etc/GMT+4" , 0x02941E }, + { "Etc/GMT+5" , 0x0294AA }, + { "Etc/GMT+6" , 0x029536 }, + { "Etc/GMT+7" , 0x0295C2 }, + { "Etc/GMT+8" , 0x02964E }, + { "Etc/GMT+9" , 0x0296DA }, + { "Etc/GMT-0" , 0x028F74 }, + { "Etc/GMT-1" , 0x028FFC }, + { "Etc/GMT-10" , 0x029088 }, + { "Etc/GMT-11" , 0x029116 }, + { "Etc/GMT-12" , 0x0291A4 }, + { "Etc/GMT-13" , 0x029232 }, + { "Etc/GMT-14" , 0x029279 }, + { "Etc/GMT-2" , 0x0292C0 }, + { "Etc/GMT-3" , 0x02934C }, + { "Etc/GMT-4" , 0x0293D8 }, + { "Etc/GMT-5" , 0x029464 }, + { "Etc/GMT-6" , 0x0294F0 }, + { "Etc/GMT-7" , 0x02957C }, + { "Etc/GMT-8" , 0x029608 }, + { "Etc/GMT-9" , 0x029694 }, + { "Etc/GMT0" , 0x028F30 }, + { "Etc/Greenwich" , 0x029720 }, + { "Etc/UCT" , 0x029764 }, + { "Etc/Universal" , 0x0297A8 }, + { "Etc/UTC" , 0x0297EC }, + { "Etc/Zulu" , 0x029830 }, + { "Europe/Amsterdam" , 0x029874 }, + { "Europe/Andorra" , 0x029CB2 }, + { "Europe/Athens" , 0x029F2E }, + { "Europe/Belfast" , 0x02A271 }, + { "Europe/Belgrade" , 0x02A7A8 }, + { "Europe/Berlin" , 0x02AA71 }, + { "Europe/Bratislava" , 0x02ADC7 }, + { "Europe/Brussels" , 0x02B0F9 }, + { "Europe/Bucharest" , 0x02B530 }, + { "Europe/Budapest" , 0x02B85A }, + { "Europe/Chisinau" , 0x02BBCD }, + { "Europe/Copenhagen" , 0x02BF5B }, + { "Europe/Dublin" , 0x02C265 }, + { "Europe/Gibraltar" , 0x02C776 }, + { "Europe/Guernsey" , 0x02CBCD }, + { "Europe/Helsinki" , 0x02D104 }, + { "Europe/Isle_of_Man" , 0x02D3BA }, + { "Europe/Istanbul" , 0x02D8F1 }, + { "Europe/Jersey" , 0x02DCDE }, + { "Europe/Kaliningrad" , 0x02E215 }, + { "Europe/Kiev" , 0x02E477 }, + { "Europe/Lisbon" , 0x02E78E }, + { "Europe/Ljubljana" , 0x02EC92 }, + { "Europe/London" , 0x02EF5B }, + { "Europe/Luxembourg" , 0x02F492 }, + { "Europe/Madrid" , 0x02F8E8 }, + { "Europe/Malta" , 0x02FCAE }, + { "Europe/Mariehamn" , 0x030067 }, + { "Europe/Minsk" , 0x03031D }, + { "Europe/Monaco" , 0x030628 }, + { "Europe/Moscow" , 0x030A63 }, + { "Europe/Nicosia" , 0x030CB4 }, + { "Europe/Oslo" , 0x030F9C }, + { "Europe/Paris" , 0x0312CE }, + { "Europe/Podgorica" , 0x031714 }, + { "Europe/Prague" , 0x0319DD }, + { "Europe/Riga" , 0x031D0F }, + { "Europe/Rome" , 0x032054 }, + { "Europe/Samara" , 0x032417 }, + { "Europe/San_Marino" , 0x032647 }, + { "Europe/Sarajevo" , 0x032A0A }, + { "Europe/Simferopol" , 0x032CD3 }, + { "Europe/Skopje" , 0x032FFE }, + { "Europe/Sofia" , 0x0332C7 }, + { "Europe/Stockholm" , 0x0335CF }, + { "Europe/Tallinn" , 0x03387E }, + { "Europe/Tirane" , 0x033BB8 }, + { "Europe/Tiraspol" , 0x033EBE }, + { "Europe/Uzhgorod" , 0x03424C }, + { "Europe/Vaduz" , 0x034563 }, + { "Europe/Vatican" , 0x0347F6 }, + { "Europe/Vienna" , 0x034BB9 }, + { "Europe/Vilnius" , 0x034EE6 }, + { "Europe/Volgograd" , 0x035225 }, + { "Europe/Warsaw" , 0x035425 }, + { "Europe/Zagreb" , 0x035806 }, + { "Europe/Zaporozhye" , 0x035ACF }, + { "Europe/Zurich" , 0x035E10 }, + { "Factory" , 0x0360BF }, + { "GB" , 0x036130 }, + { "GB-Eire" , 0x036667 }, + { "GMT" , 0x036B9E }, + { "GMT+0" , 0x036C6A }, + { "GMT-0" , 0x036C26 }, + { "GMT0" , 0x036BE2 }, + { "Greenwich" , 0x036CAE }, + { "Hongkong" , 0x036CF2 }, + { "HST" , 0x036EB4 }, + { "Iceland" , 0x036EF8 }, + { "Indian/Antananarivo" , 0x0370B1 }, + { "Indian/Chagos" , 0x037125 }, + { "Indian/Christmas" , 0x037187 }, + { "Indian/Cocos" , 0x0371CB }, + { "Indian/Comoro" , 0x03720F }, + { "Indian/Kerguelen" , 0x037264 }, + { "Indian/Mahe" , 0x0372B9 }, + { "Indian/Maldives" , 0x03730E }, + { "Indian/Mauritius" , 0x037363 }, + { "Indian/Mayotte" , 0x0373D9 }, + { "Indian/Reunion" , 0x03742E }, + { "Iran" , 0x037483 }, + { "Israel" , 0x0376F1 }, + { "Jamaica" , 0x037A20 }, + { "Japan" , 0x037AE5 }, + { "Kwajalein" , 0x037B6E }, + { "Libya" , 0x037BD1 }, + { "MET" , 0x037CCB }, + { "Mexico/BajaNorte" , 0x037FD4 }, + { "Mexico/BajaSur" , 0x03833D }, + { "Mexico/General" , 0x038582 }, + { "MST" , 0x0387E0 }, + { "MST7MDT" , 0x038824 }, + { "Navajo" , 0x038B75 }, + { "NZ" , 0x038EEE }, + { "NZ-CHAT" , 0x03926C }, + { "Pacific/Apia" , 0x039554 }, + { "Pacific/Auckland" , 0x0395D2 }, + { "Pacific/Chatham" , 0x03995E }, + { "Pacific/Chuuk" , 0x039C55 }, + { "Pacific/Easter" , 0x039CAE }, + { "Pacific/Efate" , 0x03A00C }, + { "Pacific/Enderbury" , 0x03A0D2 }, + { "Pacific/Fakaofo" , 0x03A140 }, + { "Pacific/Fiji" , 0x03A184 }, + { "Pacific/Funafuti" , 0x03A20E }, + { "Pacific/Galapagos" , 0x03A252 }, + { "Pacific/Gambier" , 0x03A2CA }, + { "Pacific/Guadalcanal" , 0x03A32F }, + { "Pacific/Guam" , 0x03A384 }, + { "Pacific/Honolulu" , 0x03A3DA }, + { "Pacific/Johnston" , 0x03A451 }, + { "Pacific/Kiritimati" , 0x03A4A3 }, + { "Pacific/Kosrae" , 0x03A50E }, + { "Pacific/Kwajalein" , 0x03A56B }, + { "Pacific/Majuro" , 0x03A5D7 }, + { "Pacific/Marquesas" , 0x03A636 }, + { "Pacific/Midway" , 0x03A69D }, + { "Pacific/Nauru" , 0x03A727 }, + { "Pacific/Niue" , 0x03A79F }, + { "Pacific/Norfolk" , 0x03A7FD }, + { "Pacific/Noumea" , 0x03A852 }, + { "Pacific/Pago_Pago" , 0x03A8E2 }, + { "Pacific/Palau" , 0x03A96B }, + { "Pacific/Pitcairn" , 0x03A9AF }, + { "Pacific/Pohnpei" , 0x03AA04 }, + { "Pacific/Ponape" , 0x03AA59 }, + { "Pacific/Port_Moresby" , 0x03AA9E }, + { "Pacific/Rarotonga" , 0x03AAE2 }, + { "Pacific/Saipan" , 0x03ABBE }, + { "Pacific/Samoa" , 0x03AC21 }, + { "Pacific/Tahiti" , 0x03ACAA }, + { "Pacific/Tarawa" , 0x03AD0F }, + { "Pacific/Tongatapu" , 0x03AD63 }, + { "Pacific/Truk" , 0x03ADEF }, + { "Pacific/Wake" , 0x03AE34 }, + { "Pacific/Wallis" , 0x03AE84 }, + { "Pacific/Yap" , 0x03AEC8 }, + { "Poland" , 0x03AF0D }, + { "Portugal" , 0x03B2EE }, + { "PRC" , 0x03B7EA }, + { "PST8PDT" , 0x03B89B }, + { "ROC" , 0x03BBEC }, + { "ROK" , 0x03BD04 }, + { "Singapore" , 0x03BDA8 }, + { "Turkey" , 0x03BE5F }, + { "UCT" , 0x03C24C }, + { "Universal" , 0x03C290 }, + { "US/Alaska" , 0x03C2D4 }, + { "US/Aleutian" , 0x03C63D }, + { "US/Arizona" , 0x03C9A3 }, + { "US/Central" , 0x03CA31 }, + { "US/East-Indiana" , 0x03D43B }, + { "US/Eastern" , 0x03CF3C }, + { "US/Hawaii" , 0x03D6A5 }, + { "US/Indiana-Starke" , 0x03D716 }, + { "US/Michigan" , 0x03DA87 }, + { "US/Mountain" , 0x03DDBE }, + { "US/Pacific" , 0x03E137 }, + { "US/Pacific-New" , 0x03E53C }, + { "US/Samoa" , 0x03E941 }, + { "UTC" , 0x03E9CA }, + { "W-SU" , 0x03ECC1 }, + { "WET" , 0x03EA0E }, + { "Zulu" , 0x03EEFB }, }; /* This is a generated file, do not modify */ -const unsigned char timelib_timezone_db_data_builtin[262608] = { +const unsigned char timelib_timezone_db_data_builtin[257855] = { /* Africa/Abidjan */ @@ -703,7 +705,7 @@ const unsigned char timelib_timezone_db_data_builtin[262608] = { /* Africa/Cairo */ 0x50, 0x48, 0x50, 0x31, 0x01, 0x45, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0xB0, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x09, 0xC8, 0x93, 0xB4, 0xE0, +0x00, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x09, 0xC8, 0x93, 0xB4, 0xE0, 0xC8, 0xFA, 0x7B, 0xD0, 0xC9, 0xFC, 0xEF, 0xE0, 0xCA, 0xC7, 0xE8, 0xD0, 0xCB, 0xCB, 0xAE, 0x60, 0xCC, 0xDF, 0x29, 0xD0, 0xCD, 0xAC, 0xE1, 0xE0, 0xCE, 0xC6, 0xF4, 0xD0, 0xCF, 0x8F, 0x66, 0xE0, 0xD0, 0xA9, 0x79, 0xD0, 0xD1, 0x84, 0x60, 0xE0, 0xD2, 0x8A, 0xAD, 0x50, 0xE8, 0x36, 0x63, 0x60, @@ -734,52 +736,36 @@ const unsigned char timelib_timezone_db_data_builtin[262608] = { 0x43, 0x3C, 0x55, 0xD0, 0x44, 0x51, 0x3E, 0xE0, 0x45, 0x12, 0xFD, 0x50, 0x46, 0x31, 0x20, 0xE0, 0x46, 0xE0, 0x6A, 0x50, 0x48, 0x11, 0x02, 0xE0, 0x48, 0xB7, 0x11, 0xD0, 0x49, 0xF0, 0xE4, 0xE0, 0x4A, 0x8D, 0xB9, 0x50, 0x4B, 0xDA, 0x01, 0x60, 0x4C, 0x61, 0xBD, 0xD0, 0x4C, 0x89, 0x58, 0xE0, -0x4C, 0xA4, 0xFA, 0x50, 0x4D, 0xB9, 0xE3, 0x60, 0x4E, 0x84, 0xDC, 0x50, 0x4F, 0x99, 0xC5, 0x60, -0x50, 0x64, 0xBE, 0x50, 0x51, 0x79, 0xA7, 0x60, 0x52, 0x44, 0xA0, 0x50, 0x53, 0x59, 0x89, 0x60, -0x54, 0x24, 0x82, 0x50, 0x55, 0x39, 0x6B, 0x60, 0x56, 0x04, 0x64, 0x50, 0x57, 0x22, 0x87, 0xE0, -0x57, 0xED, 0x80, 0xD0, 0x59, 0x02, 0x69, 0xE0, 0x59, 0xCD, 0x62, 0xD0, 0x5A, 0xE2, 0x4B, 0xE0, -0x5B, 0xAD, 0x44, 0xD0, 0x5C, 0xC2, 0x2D, 0xE0, 0x5D, 0x8D, 0x26, 0xD0, 0x5E, 0xA2, 0x0F, 0xE0, -0x5F, 0x6D, 0x08, 0xD0, 0x60, 0x8B, 0x2C, 0x60, 0x61, 0x56, 0x25, 0x50, 0x62, 0x6B, 0x0E, 0x60, -0x63, 0x36, 0x07, 0x50, 0x64, 0x4A, 0xF0, 0x60, 0x65, 0x15, 0xE9, 0x50, 0x66, 0x2A, 0xD2, 0x60, -0x66, 0xF5, 0xCB, 0x50, 0x68, 0x0A, 0xB4, 0x60, 0x68, 0xD5, 0xAD, 0x50, 0x69, 0xEA, 0x96, 0x60, -0x6A, 0xB5, 0x8F, 0x50, 0x6B, 0xD3, 0xB2, 0xE0, 0x6C, 0x9E, 0xAB, 0xD0, 0x6D, 0xB3, 0x94, 0xE0, -0x6E, 0x7E, 0x8D, 0xD0, 0x6F, 0x93, 0x76, 0xE0, 0x70, 0x5E, 0x6F, 0xD0, 0x71, 0x73, 0x58, 0xE0, -0x72, 0x3E, 0x51, 0xD0, 0x73, 0x53, 0x3A, 0xE0, 0x74, 0x1E, 0x33, 0xD0, 0x75, 0x3C, 0x57, 0x60, -0x76, 0x07, 0x50, 0x50, 0x77, 0x1C, 0x39, 0x60, 0x77, 0xE7, 0x32, 0x50, 0x78, 0xFC, 0x1B, 0x60, -0x79, 0xC7, 0x14, 0x50, 0x7A, 0xDB, 0xFD, 0x60, 0x7B, 0xA6, 0xF6, 0x50, 0x7C, 0xBB, 0xDF, 0x60, -0x7D, 0x86, 0xD8, 0x50, 0x7E, 0x9B, 0xC1, 0x60, 0x7F, 0x66, 0xBA, 0x50, 0x00, 0x01, 0x00, 0x01, +0x4C, 0xA4, 0xFA, 0x50, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, -0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, -0x00, 0x01, 0x00, 0x01, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, -0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, -0x02, 0x03, 0x02, 0x01, 0x00, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, +0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, -0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, -0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x00, 0x00, 0x2A, 0x30, -0x01, 0x00, 0x00, 0x00, 0x1C, 0x20, 0x00, 0x05, 0x00, 0x00, 0x2A, 0x30, 0x01, 0x00, 0x00, 0x00, -0x1C, 0x20, 0x00, 0x05, 0x45, 0x45, 0x53, 0x54, 0x00, 0x45, 0x45, 0x54, 0x00, 0x00, 0x00, 0x01, -0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB7, 0x2E, 0x88, 0x01, 0x42, 0x57, 0x88, 0x00, 0x00, 0x00, -0x00, +0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x01, 0x00, 0x03, 0x00, 0x00, +0x2A, 0x30, 0x01, 0x00, 0x00, 0x00, 0x1C, 0x20, 0x00, 0x05, 0x00, 0x00, 0x2A, 0x30, 0x01, 0x00, +0x00, 0x00, 0x1C, 0x20, 0x00, 0x05, 0x45, 0x45, 0x53, 0x54, 0x00, 0x45, 0x45, 0x54, 0x00, 0x00, +0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB7, 0x2E, 0x88, 0x01, 0x42, 0x57, 0x88, 0x00, +0x00, 0x00, 0x00, /* Africa/Casablanca */ 0x50, 0x48, 0x50, 0x31, 0x01, 0x4D, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x11, 0x96, 0x51, 0xF9, 0x9C, +0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x11, 0x96, 0x51, 0xF9, 0x9C, 0xC6, 0xFF, 0x14, 0x80, 0xC7, 0x58, 0xAC, 0x70, 0xC7, 0xD9, 0xED, 0x80, 0xD2, 0xA1, 0x32, 0xF0, 0xDB, 0x35, 0xA4, 0x00, 0xDB, 0xEE, 0x27, 0xF0, 0xFB, 0x25, 0x72, 0x40, 0xFB, 0xC2, 0xEF, 0x70, 0x08, 0x6B, 0x84, 0x80, 0x08, 0xC6, 0x6D, 0xF0, 0x0B, 0xE8, 0x0C, 0x00, 0x0C, 0x61, 0x47, 0xF0, 0x0D, 0xC9, 0x3F, 0x80, 0x0E, 0x8E, 0xF2, 0x70, 0x0F, 0xD3, 0x51, 0x80, 0x10, 0x27, 0xA3, 0x70, 0x1A, 0xB7, 0xA6, 0x00, 0x1E, 0x18, 0x6F, 0xF0, 0x48, 0x41, 0xE6, 0x80, 0x48, 0xBB, 0x22, 0x70, 0x4A, 0x23, 0x1A, 0x00, 0x4A, 0x8D, 0xD5, 0x70, 0x4B, 0xDC, 0xC0, 0x80, 0x4C, 0x5D, 0xE5, 0x70, -0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, -0x02, 0x03, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0xFF, 0xFF, 0xF8, 0xE4, 0x00, 0x00, 0x00, -0x00, 0x0E, 0x10, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x0E, 0x10, 0x00, -0x0D, 0x4C, 0x4D, 0x54, 0x00, 0x57, 0x45, 0x53, 0x54, 0x00, 0x57, 0x45, 0x54, 0x00, 0x43, 0x45, -0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBC, 0xAC, 0xC8, 0x01, 0x07, -0x16, 0x42, 0x00, 0x00, 0x00, 0x00, +0x4D, 0x97, 0xB8, 0x80, 0x4E, 0x34, 0x8C, 0xF0, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, +0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x03, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, +0x02, 0x01, 0x02, 0xFF, 0xFF, 0xF8, 0xE4, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x10, 0x01, 0x04, 0x00, +0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x0E, 0x10, 0x00, 0x0D, 0x4C, 0x4D, 0x54, 0x00, 0x57, +0x45, 0x53, 0x54, 0x00, 0x57, 0x45, 0x54, 0x00, 0x43, 0x45, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0xBC, 0xAC, 0xC8, 0x01, 0x07, 0x16, 0x42, 0x00, 0x00, 0x00, 0x00, + /* Africa/Ceuta */ 0x50, 0x48, 0x50, 0x31, 0x01, 0x45, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -2578,7 +2564,7 @@ const unsigned char timelib_timezone_db_data_builtin[262608] = { 0x00, 0x00, 0x0B, 0x4D, 0x61, 0x74, 0x6F, 0x20, 0x47, 0x72, 0x6F, 0x73, 0x73, 0x6F, /* America/Curacao */ -0x50, 0x48, 0x50, 0x31, 0x01, 0x41, 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x50, 0x48, 0x50, 0x31, 0x01, 0x43, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x93, 0x1E, 0x2E, 0x20, 0xF6, 0x98, 0xEC, 0x48, 0x01, 0x02, 0xFF, 0xFF, 0xBF, 0x60, 0x00, 0x00, 0xFF, 0xFF, 0xC0, 0xB8, @@ -4263,6 +4249,15 @@ const unsigned char timelib_timezone_db_data_builtin[262608] = { 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x89, 0x54, 0x40, 0x01, 0x12, 0xA8, 0x80, 0x00, 0x00, 0x00, 0x00, +/* America/Kralendijk */ +0x50, 0x48, 0x50, 0x31, 0x01, 0x42, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x93, 0x1E, 0x2E, 0x20, +0xF6, 0x98, 0xEC, 0x48, 0x01, 0x02, 0xFF, 0xFF, 0xBF, 0x60, 0x00, 0x00, 0xFF, 0xFF, 0xC0, 0xB8, +0x00, 0x04, 0xFF, 0xFF, 0xC7, 0xC0, 0x00, 0x08, 0x4C, 0x4D, 0x54, 0x00, 0x41, 0x4E, 0x54, 0x00, +0x41, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9B, 0xDE, 0xAB, 0x00, 0xAA, +0x79, 0xED, 0x00, 0x00, 0x00, 0x00, + /* America/La_Paz */ 0x50, 0x48, 0x50, 0x31, 0x01, 0x42, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, @@ -4419,6 +4414,15 @@ const unsigned char timelib_timezone_db_data_builtin[262608] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x89, 0x54, 0x40, 0x01, 0x12, 0xA8, 0x80, 0x00, 0x00, 0x00, 0x00, +/* America/Lower_Princes */ +0x50, 0x48, 0x50, 0x31, 0x01, 0x53, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x93, 0x1E, 0x2E, 0x20, +0xF6, 0x98, 0xEC, 0x48, 0x01, 0x02, 0xFF, 0xFF, 0xBF, 0x60, 0x00, 0x00, 0xFF, 0xFF, 0xC0, 0xB8, +0x00, 0x04, 0xFF, 0xFF, 0xC7, 0xC0, 0x00, 0x08, 0x4C, 0x4D, 0x54, 0x00, 0x41, 0x4E, 0x54, 0x00, +0x41, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA4, 0xDF, 0x92, 0x00, 0xB2, +0x74, 0xAD, 0x00, 0x00, 0x00, 0x00, + /* America/Maceio */ 0x50, 0x48, 0x50, 0x31, 0x01, 0x42, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, @@ -6117,8 +6121,8 @@ const unsigned char timelib_timezone_db_data_builtin[262608] = { 0x3F, 0x88, 0xD1, 0xC0, 0x40, 0x53, 0xCA, 0xB0, 0x41, 0x68, 0xB3, 0xC0, 0x42, 0x33, 0xAC, 0xB0, 0x43, 0x48, 0x95, 0xC0, 0x44, 0x13, 0x8E, 0xB0, 0x45, 0x31, 0xB2, 0x40, 0x45, 0xF3, 0x70, 0xB0, 0x47, 0x11, 0x94, 0x40, 0x47, 0xEF, 0x02, 0x30, 0x48, 0xF1, 0x76, 0x40, 0x49, 0xBC, 0x6F, 0x30, -0x4A, 0xD1, 0x58, 0x40, 0x4B, 0xB8, 0x00, 0xB0, 0x4C, 0xB1, 0x3A, 0x40, 0x4D, 0x97, 0xE2, 0xB0, -0x4E, 0x91, 0x1C, 0x40, 0x4F, 0x5C, 0x15, 0x30, 0x50, 0x7A, 0x38, 0xC0, 0x51, 0x3B, 0xF7, 0x30, +0x4A, 0xD1, 0x58, 0x40, 0x4B, 0xB8, 0x00, 0xB0, 0x4C, 0xB1, 0x3A, 0x40, 0x4D, 0xC6, 0x07, 0x30, +0x4E, 0x50, 0x82, 0xC0, 0x4F, 0x5C, 0x15, 0x30, 0x50, 0x7A, 0x38, 0xC0, 0x51, 0x3B, 0xF7, 0x30, 0x52, 0x5A, 0x1A, 0xC0, 0x53, 0x1B, 0xD9, 0x30, 0x54, 0x39, 0xFC, 0xC0, 0x55, 0x04, 0xF5, 0xB0, 0x56, 0x19, 0xDE, 0xC0, 0x56, 0xE4, 0xD7, 0xB0, 0x57, 0xF9, 0xC0, 0xC0, 0x58, 0xC4, 0xB9, 0xB0, 0x59, 0xE2, 0xDD, 0x40, 0x5A, 0xA4, 0x9B, 0xB0, 0x5B, 0xC2, 0xBF, 0x40, 0x5C, 0x84, 0x7D, 0xB0, @@ -7505,7 +7509,7 @@ const unsigned char timelib_timezone_db_data_builtin[262608] = { /* Asia/Anadyr */ 0x50, 0x48, 0x50, 0x31, 0x01, 0x52, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0F, 0xAA, 0x19, 0x1D, 0x9C, +0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0F, 0xAA, 0x19, 0x1D, 0x9C, 0xB5, 0xA3, 0x8C, 0xC0, 0x15, 0x27, 0x1B, 0x30, 0x16, 0x18, 0x4F, 0xA0, 0x17, 0x08, 0x4E, 0xB0, 0x17, 0xF9, 0x91, 0x30, 0x18, 0xE9, 0x90, 0x40, 0x19, 0xDA, 0xC4, 0xB0, 0x1A, 0xCC, 0x15, 0x40, 0x1B, 0xBC, 0x22, 0x60, 0x1C, 0xAC, 0x13, 0x60, 0x1D, 0x9C, 0x04, 0x60, 0x1E, 0x8B, 0xF5, 0x60, @@ -7521,35 +7525,19 @@ const unsigned char timelib_timezone_db_data_builtin[262608] = { 0x40, 0x65, 0x88, 0xE0, 0x41, 0x83, 0x9E, 0x60, 0x42, 0x45, 0x6A, 0xE0, 0x43, 0x63, 0x80, 0x60, 0x44, 0x25, 0x4C, 0xE0, 0x45, 0x43, 0x62, 0x60, 0x46, 0x05, 0x2E, 0xE0, 0x47, 0x23, 0x44, 0x60, 0x47, 0xEE, 0x4B, 0x60, 0x49, 0x03, 0x26, 0x60, 0x49, 0xCE, 0x2D, 0x60, 0x4A, 0xE3, 0x08, 0x60, -0x4B, 0xAE, 0x0F, 0x60, 0x4C, 0xCC, 0x32, 0xF0, 0x4D, 0x8D, 0xFF, 0x70, 0x4E, 0xAC, 0x14, 0xF0, -0x4F, 0x6D, 0xE1, 0x70, 0x50, 0x8B, 0xF6, 0xF0, 0x51, 0x56, 0xFD, 0xF0, 0x52, 0x6B, 0xD8, 0xF0, -0x53, 0x36, 0xDF, 0xF0, 0x54, 0x4B, 0xBA, 0xF0, 0x55, 0x16, 0xC1, 0xF0, 0x56, 0x2B, 0x9C, 0xF0, -0x56, 0xF6, 0xA3, 0xF0, 0x58, 0x14, 0xB9, 0x70, 0x58, 0xD6, 0x85, 0xF0, 0x59, 0xF4, 0x9B, 0x70, -0x5A, 0xB6, 0x67, 0xF0, 0x5B, 0xD4, 0x7D, 0x70, 0x5C, 0x9F, 0x84, 0x70, 0x5D, 0xB4, 0x5F, 0x70, -0x5E, 0x7F, 0x66, 0x70, 0x5F, 0x94, 0x41, 0x70, 0x60, 0x5F, 0x48, 0x70, 0x61, 0x7D, 0x5D, 0xF0, -0x62, 0x3F, 0x2A, 0x70, 0x63, 0x5D, 0x3F, 0xF0, 0x64, 0x1F, 0x0C, 0x70, 0x65, 0x3D, 0x21, 0xF0, -0x66, 0x08, 0x28, 0xF0, 0x67, 0x1D, 0x03, 0xF0, 0x67, 0xE8, 0x0A, 0xF0, 0x68, 0xFC, 0xE5, 0xF0, -0x69, 0xC7, 0xEC, 0xF0, 0x6A, 0xDC, 0xC7, 0xF0, 0x6B, 0xA7, 0xCE, 0xF0, 0x6C, 0xC5, 0xE4, 0x70, -0x6D, 0x87, 0xB0, 0xF0, 0x6E, 0xA5, 0xC6, 0x70, 0x6F, 0x67, 0x92, 0xF0, 0x70, 0x85, 0xA8, 0x70, -0x71, 0x50, 0xAF, 0x70, 0x72, 0x65, 0x8A, 0x70, 0x73, 0x30, 0x91, 0x70, 0x74, 0x45, 0x6C, 0x70, -0x75, 0x10, 0x73, 0x70, 0x76, 0x2E, 0x88, 0xF0, 0x76, 0xF0, 0x55, 0x70, 0x78, 0x0E, 0x6A, 0xF0, -0x78, 0xD0, 0x37, 0x70, 0x79, 0xEE, 0x4C, 0xF0, 0x7A, 0xB0, 0x19, 0x70, 0x7B, 0xCE, 0x2E, 0xF0, -0x7C, 0x99, 0x35, 0xF0, 0x7D, 0xAE, 0x10, 0xF0, 0x7E, 0x79, 0x17, 0xF0, 0x7F, 0x8D, 0xF2, 0xF0, -0x01, 0x03, 0x02, 0x03, 0x04, 0x01, 0x04, 0x01, 0x04, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, -0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x07, 0x08, 0x05, 0x04, 0x01, 0x06, 0x05, 0x06, 0x05, 0x06, +0x4B, 0xAE, 0x0F, 0x60, 0x4C, 0xCC, 0x32, 0xF0, 0x4D, 0x8D, 0xFF, 0x70, 0x01, 0x03, 0x02, 0x03, +0x04, 0x01, 0x04, 0x01, 0x04, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, +0x06, 0x05, 0x07, 0x08, 0x05, 0x04, 0x01, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, -0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x07, 0x08, 0x07, -0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, -0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, -0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, -0x08, 0x07, 0x08, 0x07, 0x08, 0x00, 0x00, 0xA6, 0x64, 0x00, 0x00, 0x00, 0x00, 0xA8, 0xC0, 0x00, -0x04, 0x00, 0x00, 0xC4, 0xE0, 0x01, 0x09, 0x00, 0x00, 0xB6, 0xD0, 0x00, 0x04, 0x00, 0x00, 0xB6, -0xD0, 0x01, 0x09, 0x00, 0x00, 0xA8, 0xC0, 0x00, 0x04, 0x00, 0x00, 0xB6, 0xD0, 0x01, 0x09, 0x00, -0x00, 0xA8, 0xC0, 0x01, 0x09, 0x00, 0x00, 0x9A, 0xB0, 0x00, 0x04, 0x4C, 0x4D, 0x54, 0x00, 0x41, -0x4E, 0x41, 0x54, 0x00, 0x41, 0x4E, 0x41, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEC, 0x21, 0x38, -0x02, 0x21, 0x79, 0xED, 0x00, 0x00, 0x00, 0x16, 0x4D, 0x6F, 0x73, 0x63, 0x6F, 0x77, 0x2B, 0x30, -0x38, 0x20, 0x2D, 0x20, 0x42, 0x65, 0x72, 0x69, 0x6E, 0x67, 0x20, 0x53, 0x65, 0x61, +0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x07, 0x08, 0x05, 0x00, 0x00, 0xA6, 0x64, +0x00, 0x00, 0x00, 0x00, 0xA8, 0xC0, 0x00, 0x04, 0x00, 0x00, 0xC4, 0xE0, 0x01, 0x09, 0x00, 0x00, +0xB6, 0xD0, 0x00, 0x04, 0x00, 0x00, 0xB6, 0xD0, 0x01, 0x09, 0x00, 0x00, 0xA8, 0xC0, 0x00, 0x04, +0x00, 0x00, 0xB6, 0xD0, 0x01, 0x09, 0x00, 0x00, 0xA8, 0xC0, 0x01, 0x09, 0x00, 0x00, 0x9A, 0xB0, +0x00, 0x04, 0x4C, 0x4D, 0x54, 0x00, 0x41, 0x4E, 0x41, 0x54, 0x00, 0x41, 0x4E, 0x41, 0x53, 0x54, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0xEC, 0x21, 0x38, 0x02, 0x21, 0x79, 0xED, 0x00, 0x00, 0x00, 0x16, 0x4D, +0x6F, 0x73, 0x63, 0x6F, 0x77, 0x2B, 0x30, 0x38, 0x20, 0x2D, 0x20, 0x42, 0x65, 0x72, 0x69, 0x6E, +0x67, 0x20, 0x53, 0x65, 0x61, /* Asia/Aqtau */ 0x50, 0x48, 0x50, 0x31, 0x01, 0x4B, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -8175,8 +8163,8 @@ const unsigned char timelib_timezone_db_data_builtin[262608] = { /* Asia/Irkutsk */ 0x50, 0x48, 0x50, 0x31, 0x01, 0x52, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0F, 0xA2, 0x12, 0x0F, 0xB0, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0F, 0xA2, 0x12, 0x0F, 0xB0, 0xB5, 0xA3, 0xD3, 0x10, 0x15, 0x27, 0x61, 0x80, 0x16, 0x18, 0x95, 0xF0, 0x17, 0x08, 0x95, 0x00, 0x17, 0xF9, 0xC9, 0x70, 0x18, 0xE9, 0xC8, 0x80, 0x19, 0xDA, 0xFC, 0xF0, 0x1A, 0xCC, 0x4D, 0x80, 0x1B, 0xBC, 0x5A, 0xA0, 0x1C, 0xAC, 0x4B, 0xA0, 0x1D, 0x9C, 0x3C, 0xA0, 0x1E, 0x8C, 0x2D, 0xA0, @@ -8192,35 +8180,19 @@ const unsigned char timelib_timezone_db_data_builtin[262608] = { 0x40, 0x65, 0xC1, 0x20, 0x41, 0x83, 0xD6, 0xA0, 0x42, 0x45, 0xA3, 0x20, 0x43, 0x63, 0xB8, 0xA0, 0x44, 0x25, 0x85, 0x20, 0x45, 0x43, 0x9A, 0xA0, 0x46, 0x05, 0x67, 0x20, 0x47, 0x23, 0x7C, 0xA0, 0x47, 0xEE, 0x83, 0xA0, 0x49, 0x03, 0x5E, 0xA0, 0x49, 0xCE, 0x65, 0xA0, 0x4A, 0xE3, 0x40, 0xA0, -0x4B, 0xAE, 0x47, 0xA0, 0x4C, 0xCC, 0x5D, 0x20, 0x4D, 0x8E, 0x29, 0xA0, 0x4E, 0xAC, 0x3F, 0x20, -0x4F, 0x6E, 0x0B, 0xA0, 0x50, 0x8C, 0x21, 0x20, 0x51, 0x57, 0x28, 0x20, 0x52, 0x6C, 0x03, 0x20, -0x53, 0x37, 0x0A, 0x20, 0x54, 0x4B, 0xE5, 0x20, 0x55, 0x16, 0xEC, 0x20, 0x56, 0x2B, 0xC7, 0x20, -0x56, 0xF6, 0xCE, 0x20, 0x58, 0x14, 0xE3, 0xA0, 0x58, 0xD6, 0xB0, 0x20, 0x59, 0xF4, 0xC5, 0xA0, -0x5A, 0xB6, 0x92, 0x20, 0x5B, 0xD4, 0xA7, 0xA0, 0x5C, 0x9F, 0xAE, 0xA0, 0x5D, 0xB4, 0x89, 0xA0, -0x5E, 0x7F, 0x90, 0xA0, 0x5F, 0x94, 0x6B, 0xA0, 0x60, 0x5F, 0x72, 0xA0, 0x61, 0x7D, 0x88, 0x20, -0x62, 0x3F, 0x54, 0xA0, 0x63, 0x5D, 0x6A, 0x20, 0x64, 0x1F, 0x36, 0xA0, 0x65, 0x3D, 0x4C, 0x20, -0x66, 0x08, 0x53, 0x20, 0x67, 0x1D, 0x2E, 0x20, 0x67, 0xE8, 0x35, 0x20, 0x68, 0xFD, 0x10, 0x20, -0x69, 0xC8, 0x17, 0x20, 0x6A, 0xDC, 0xF2, 0x20, 0x6B, 0xA7, 0xF9, 0x20, 0x6C, 0xC6, 0x0E, 0xA0, -0x6D, 0x87, 0xDB, 0x20, 0x6E, 0xA5, 0xF0, 0xA0, 0x6F, 0x67, 0xBD, 0x20, 0x70, 0x85, 0xD2, 0xA0, -0x71, 0x50, 0xD9, 0xA0, 0x72, 0x65, 0xB4, 0xA0, 0x73, 0x30, 0xBB, 0xA0, 0x74, 0x45, 0x96, 0xA0, -0x75, 0x10, 0x9D, 0xA0, 0x76, 0x2E, 0xB3, 0x20, 0x76, 0xF0, 0x7F, 0xA0, 0x78, 0x0E, 0x95, 0x20, -0x78, 0xD0, 0x61, 0xA0, 0x79, 0xEE, 0x77, 0x20, 0x7A, 0xB0, 0x43, 0xA0, 0x7B, 0xCE, 0x59, 0x20, -0x7C, 0x99, 0x60, 0x20, 0x7D, 0xAE, 0x3B, 0x20, 0x7E, 0x79, 0x42, 0x20, 0x7F, 0x8E, 0x1D, 0x20, -0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, -0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x02, 0x03, 0x05, 0x04, 0x05, 0x04, 0x05, -0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, -0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, -0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, -0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, +0x4B, 0xAE, 0x47, 0xA0, 0x4C, 0xCC, 0x5D, 0x20, 0x4D, 0x8E, 0x29, 0xA0, 0x01, 0x03, 0x02, 0x03, +0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, +0x05, 0x04, 0x06, 0x07, 0x04, 0x02, 0x03, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, -0x04, 0x05, 0x04, 0x05, 0x04, 0x00, 0x00, 0x61, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x62, 0x70, 0x00, -0x04, 0x00, 0x00, 0x7E, 0x90, 0x01, 0x09, 0x00, 0x00, 0x70, 0x80, 0x00, 0x04, 0x00, 0x00, 0x70, -0x80, 0x00, 0x04, 0x00, 0x00, 0x7E, 0x90, 0x01, 0x09, 0x00, 0x00, 0x70, 0x80, 0x01, 0x09, 0x00, -0x00, 0x62, 0x70, 0x00, 0x04, 0x49, 0x4D, 0x54, 0x00, 0x49, 0x52, 0x4B, 0x54, 0x00, 0x49, 0x52, -0x4B, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0xD9, 0x14, 0xEA, 0x01, 0xB1, 0xDB, 0xB5, 0x00, 0x00, 0x00, 0x17, -0x4D, 0x6F, 0x73, 0x63, 0x6F, 0x77, 0x2B, 0x30, 0x35, 0x20, 0x2D, 0x20, 0x4C, 0x61, 0x6B, 0x65, -0x20, 0x42, 0x61, 0x69, 0x6B, 0x61, 0x6C, +0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x08, 0x00, 0x00, 0x61, 0xD0, +0x00, 0x00, 0x00, 0x00, 0x62, 0x70, 0x00, 0x04, 0x00, 0x00, 0x7E, 0x90, 0x01, 0x09, 0x00, 0x00, +0x70, 0x80, 0x00, 0x04, 0x00, 0x00, 0x70, 0x80, 0x00, 0x04, 0x00, 0x00, 0x7E, 0x90, 0x01, 0x09, +0x00, 0x00, 0x70, 0x80, 0x01, 0x09, 0x00, 0x00, 0x62, 0x70, 0x00, 0x04, 0x00, 0x00, 0x7E, 0x90, +0x00, 0x04, 0x49, 0x4D, 0x54, 0x00, 0x49, 0x52, 0x4B, 0x54, 0x00, 0x49, 0x52, 0x4B, 0x53, 0x54, +0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0xD9, 0x14, 0xEA, 0x01, 0xB1, 0xDB, 0xB5, 0x00, 0x00, 0x00, 0x17, 0x4D, +0x6F, 0x73, 0x63, 0x6F, 0x77, 0x2B, 0x30, 0x35, 0x20, 0x2D, 0x20, 0x4C, 0x61, 0x6B, 0x65, 0x20, +0x42, 0x61, 0x69, 0x6B, 0x61, 0x6C, /* Asia/Istanbul */ 0x50, 0x48, 0x50, 0x31, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -8376,7 +8348,7 @@ const unsigned char timelib_timezone_db_data_builtin[262608] = { /* Asia/Kamchatka */ 0x50, 0x48, 0x50, 0x31, 0x01, 0x52, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0F, 0xA7, 0x52, 0x96, 0xC4, +0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0F, 0xA7, 0x52, 0x96, 0xC4, 0xB5, 0xA3, 0x9A, 0xD0, 0x15, 0x27, 0x29, 0x40, 0x16, 0x18, 0x5D, 0xB0, 0x17, 0x08, 0x5C, 0xC0, 0x17, 0xF9, 0x91, 0x30, 0x18, 0xE9, 0x90, 0x40, 0x19, 0xDA, 0xC4, 0xB0, 0x1A, 0xCC, 0x15, 0x40, 0x1B, 0xBC, 0x22, 0x60, 0x1C, 0xAC, 0x13, 0x60, 0x1D, 0x9C, 0x04, 0x60, 0x1E, 0x8B, 0xF5, 0x60, @@ -8392,35 +8364,18 @@ const unsigned char timelib_timezone_db_data_builtin[262608] = { 0x40, 0x65, 0x88, 0xE0, 0x41, 0x83, 0x9E, 0x60, 0x42, 0x45, 0x6A, 0xE0, 0x43, 0x63, 0x80, 0x60, 0x44, 0x25, 0x4C, 0xE0, 0x45, 0x43, 0x62, 0x60, 0x46, 0x05, 0x2E, 0xE0, 0x47, 0x23, 0x44, 0x60, 0x47, 0xEE, 0x4B, 0x60, 0x49, 0x03, 0x26, 0x60, 0x49, 0xCE, 0x2D, 0x60, 0x4A, 0xE3, 0x08, 0x60, -0x4B, 0xAE, 0x0F, 0x60, 0x4C, 0xCC, 0x32, 0xF0, 0x4D, 0x8D, 0xFF, 0x70, 0x4E, 0xAC, 0x14, 0xF0, -0x4F, 0x6D, 0xE1, 0x70, 0x50, 0x8B, 0xF6, 0xF0, 0x51, 0x56, 0xFD, 0xF0, 0x52, 0x6B, 0xD8, 0xF0, -0x53, 0x36, 0xDF, 0xF0, 0x54, 0x4B, 0xBA, 0xF0, 0x55, 0x16, 0xC1, 0xF0, 0x56, 0x2B, 0x9C, 0xF0, -0x56, 0xF6, 0xA3, 0xF0, 0x58, 0x14, 0xB9, 0x70, 0x58, 0xD6, 0x85, 0xF0, 0x59, 0xF4, 0x9B, 0x70, -0x5A, 0xB6, 0x67, 0xF0, 0x5B, 0xD4, 0x7D, 0x70, 0x5C, 0x9F, 0x84, 0x70, 0x5D, 0xB4, 0x5F, 0x70, -0x5E, 0x7F, 0x66, 0x70, 0x5F, 0x94, 0x41, 0x70, 0x60, 0x5F, 0x48, 0x70, 0x61, 0x7D, 0x5D, 0xF0, -0x62, 0x3F, 0x2A, 0x70, 0x63, 0x5D, 0x3F, 0xF0, 0x64, 0x1F, 0x0C, 0x70, 0x65, 0x3D, 0x21, 0xF0, -0x66, 0x08, 0x28, 0xF0, 0x67, 0x1D, 0x03, 0xF0, 0x67, 0xE8, 0x0A, 0xF0, 0x68, 0xFC, 0xE5, 0xF0, -0x69, 0xC7, 0xEC, 0xF0, 0x6A, 0xDC, 0xC7, 0xF0, 0x6B, 0xA7, 0xCE, 0xF0, 0x6C, 0xC5, 0xE4, 0x70, -0x6D, 0x87, 0xB0, 0xF0, 0x6E, 0xA5, 0xC6, 0x70, 0x6F, 0x67, 0x92, 0xF0, 0x70, 0x85, 0xA8, 0x70, -0x71, 0x50, 0xAF, 0x70, 0x72, 0x65, 0x8A, 0x70, 0x73, 0x30, 0x91, 0x70, 0x74, 0x45, 0x6C, 0x70, -0x75, 0x10, 0x73, 0x70, 0x76, 0x2E, 0x88, 0xF0, 0x76, 0xF0, 0x55, 0x70, 0x78, 0x0E, 0x6A, 0xF0, -0x78, 0xD0, 0x37, 0x70, 0x79, 0xEE, 0x4C, 0xF0, 0x7A, 0xB0, 0x19, 0x70, 0x7B, 0xCE, 0x2E, 0xF0, -0x7C, 0x99, 0x35, 0xF0, 0x7D, 0xAE, 0x10, 0xF0, 0x7E, 0x79, 0x17, 0xF0, 0x7F, 0x8D, 0xF2, 0xF0, -0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, -0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x02, 0x03, 0x05, 0x04, 0x05, 0x04, 0x05, +0x4B, 0xAE, 0x0F, 0x60, 0x4C, 0xCC, 0x32, 0xF0, 0x4D, 0x8D, 0xFF, 0x70, 0x01, 0x03, 0x02, 0x03, +0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, +0x05, 0x04, 0x06, 0x07, 0x04, 0x02, 0x03, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, -0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x06, -0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, -0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, -0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, -0x07, 0x06, 0x07, 0x06, 0x07, 0x00, 0x00, 0x94, 0xBC, 0x00, 0x00, 0x00, 0x00, 0x9A, 0xB0, 0x00, -0x04, 0x00, 0x00, 0xB6, 0xD0, 0x01, 0x09, 0x00, 0x00, 0xA8, 0xC0, 0x00, 0x04, 0x00, 0x00, 0xA8, -0xC0, 0x00, 0x04, 0x00, 0x00, 0xB6, 0xD0, 0x01, 0x09, 0x00, 0x00, 0xA8, 0xC0, 0x01, 0x09, 0x00, -0x00, 0x9A, 0xB0, 0x00, 0x04, 0x4C, 0x4D, 0x54, 0x00, 0x50, 0x45, 0x54, 0x54, 0x00, 0x50, 0x45, -0x54, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0xDA, 0x39, 0xE2, 0x02, 0x04, 0xBD, 0x28, 0x00, 0x00, 0x00, 0x15, -0x4D, 0x6F, 0x73, 0x63, 0x6F, 0x77, 0x2B, 0x30, 0x38, 0x20, 0x2D, 0x20, 0x4B, 0x61, 0x6D, 0x63, -0x68, 0x61, 0x74, 0x6B, 0x61, +0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x00, 0x00, 0x94, 0xBC, +0x00, 0x00, 0x00, 0x00, 0x9A, 0xB0, 0x00, 0x04, 0x00, 0x00, 0xB6, 0xD0, 0x01, 0x09, 0x00, 0x00, +0xA8, 0xC0, 0x00, 0x04, 0x00, 0x00, 0xA8, 0xC0, 0x00, 0x04, 0x00, 0x00, 0xB6, 0xD0, 0x01, 0x09, +0x00, 0x00, 0xA8, 0xC0, 0x01, 0x09, 0x00, 0x00, 0x9A, 0xB0, 0x00, 0x04, 0x4C, 0x4D, 0x54, 0x00, +0x50, 0x45, 0x54, 0x54, 0x00, 0x50, 0x45, 0x54, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, +0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDA, 0x39, 0xE2, 0x02, +0x04, 0xBD, 0x28, 0x00, 0x00, 0x00, 0x15, 0x4D, 0x6F, 0x73, 0x63, 0x6F, 0x77, 0x2B, 0x30, 0x38, +0x20, 0x2D, 0x20, 0x4B, 0x61, 0x6D, 0x63, 0x68, 0x61, 0x74, 0x6B, 0x61, /* Asia/Karachi */ 0x50, 0x48, 0x50, 0x31, 0x01, 0x50, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -8482,8 +8437,8 @@ const unsigned char timelib_timezone_db_data_builtin[262608] = { /* Asia/Krasnoyarsk */ 0x50, 0x48, 0x50, 0x31, 0x01, 0x52, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0F, 0xA1, 0xF9, 0x0D, 0xF8, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0F, 0xA1, 0xF9, 0x0D, 0xF8, 0xB5, 0xA3, 0xE1, 0x20, 0x15, 0x27, 0x6F, 0x90, 0x16, 0x18, 0xA4, 0x00, 0x17, 0x08, 0xA3, 0x10, 0x17, 0xF9, 0xD7, 0x80, 0x18, 0xE9, 0xD6, 0x90, 0x19, 0xDB, 0x0B, 0x00, 0x1A, 0xCC, 0x5B, 0x90, 0x1B, 0xBC, 0x68, 0xB0, 0x1C, 0xAC, 0x59, 0xB0, 0x1D, 0x9C, 0x4A, 0xB0, 0x1E, 0x8C, 0x3B, 0xB0, @@ -8499,35 +8454,19 @@ const unsigned char timelib_timezone_db_data_builtin[262608] = { 0x40, 0x65, 0xCF, 0x30, 0x41, 0x83, 0xE4, 0xB0, 0x42, 0x45, 0xB1, 0x30, 0x43, 0x63, 0xC6, 0xB0, 0x44, 0x25, 0x93, 0x30, 0x45, 0x43, 0xA8, 0xB0, 0x46, 0x05, 0x75, 0x30, 0x47, 0x23, 0x8A, 0xB0, 0x47, 0xEE, 0x91, 0xB0, 0x49, 0x03, 0x6C, 0xB0, 0x49, 0xCE, 0x73, 0xB0, 0x4A, 0xE3, 0x4E, 0xB0, -0x4B, 0xAE, 0x55, 0xB0, 0x4C, 0xCC, 0x6B, 0x30, 0x4D, 0x8E, 0x37, 0xB0, 0x4E, 0xAC, 0x4D, 0x30, -0x4F, 0x6E, 0x19, 0xB0, 0x50, 0x8C, 0x2F, 0x30, 0x51, 0x57, 0x36, 0x30, 0x52, 0x6C, 0x11, 0x30, -0x53, 0x37, 0x18, 0x30, 0x54, 0x4B, 0xF3, 0x30, 0x55, 0x16, 0xFA, 0x30, 0x56, 0x2B, 0xD5, 0x30, -0x56, 0xF6, 0xDC, 0x30, 0x58, 0x14, 0xF1, 0xB0, 0x58, 0xD6, 0xBE, 0x30, 0x59, 0xF4, 0xD3, 0xB0, -0x5A, 0xB6, 0xA0, 0x30, 0x5B, 0xD4, 0xB5, 0xB0, 0x5C, 0x9F, 0xBC, 0xB0, 0x5D, 0xB4, 0x97, 0xB0, -0x5E, 0x7F, 0x9E, 0xB0, 0x5F, 0x94, 0x79, 0xB0, 0x60, 0x5F, 0x80, 0xB0, 0x61, 0x7D, 0x96, 0x30, -0x62, 0x3F, 0x62, 0xB0, 0x63, 0x5D, 0x78, 0x30, 0x64, 0x1F, 0x44, 0xB0, 0x65, 0x3D, 0x5A, 0x30, -0x66, 0x08, 0x61, 0x30, 0x67, 0x1D, 0x3C, 0x30, 0x67, 0xE8, 0x43, 0x30, 0x68, 0xFD, 0x1E, 0x30, -0x69, 0xC8, 0x25, 0x30, 0x6A, 0xDD, 0x00, 0x30, 0x6B, 0xA8, 0x07, 0x30, 0x6C, 0xC6, 0x1C, 0xB0, -0x6D, 0x87, 0xE9, 0x30, 0x6E, 0xA5, 0xFE, 0xB0, 0x6F, 0x67, 0xCB, 0x30, 0x70, 0x85, 0xE0, 0xB0, -0x71, 0x50, 0xE7, 0xB0, 0x72, 0x65, 0xC2, 0xB0, 0x73, 0x30, 0xC9, 0xB0, 0x74, 0x45, 0xA4, 0xB0, -0x75, 0x10, 0xAB, 0xB0, 0x76, 0x2E, 0xC1, 0x30, 0x76, 0xF0, 0x8D, 0xB0, 0x78, 0x0E, 0xA3, 0x30, -0x78, 0xD0, 0x6F, 0xB0, 0x79, 0xEE, 0x85, 0x30, 0x7A, 0xB0, 0x51, 0xB0, 0x7B, 0xCE, 0x67, 0x30, -0x7C, 0x99, 0x6E, 0x30, 0x7D, 0xAE, 0x49, 0x30, 0x7E, 0x79, 0x50, 0x30, 0x7F, 0x8E, 0x2B, 0x30, -0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, -0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x02, 0x03, 0x05, 0x04, 0x05, 0x04, 0x05, -0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, -0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, -0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, -0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, +0x4B, 0xAE, 0x55, 0xB0, 0x4C, 0xCC, 0x6B, 0x30, 0x4D, 0x8E, 0x37, 0xB0, 0x01, 0x03, 0x02, 0x03, +0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, +0x05, 0x04, 0x06, 0x07, 0x04, 0x02, 0x03, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, -0x04, 0x05, 0x04, 0x05, 0x04, 0x00, 0x00, 0x57, 0x08, 0x00, 0x00, 0x00, 0x00, 0x54, 0x60, 0x00, -0x04, 0x00, 0x00, 0x70, 0x80, 0x01, 0x09, 0x00, 0x00, 0x62, 0x70, 0x00, 0x04, 0x00, 0x00, 0x62, -0x70, 0x00, 0x04, 0x00, 0x00, 0x70, 0x80, 0x01, 0x09, 0x00, 0x00, 0x62, 0x70, 0x01, 0x09, 0x00, -0x00, 0x54, 0x60, 0x00, 0x04, 0x4C, 0x4D, 0x54, 0x00, 0x4B, 0x52, 0x41, 0x54, 0x00, 0x4B, 0x52, -0x41, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0xDE, 0xCD, 0xC2, 0x01, 0xA0, 0x4F, 0x85, 0x00, 0x00, 0x00, 0x19, -0x4D, 0x6F, 0x73, 0x63, 0x6F, 0x77, 0x2B, 0x30, 0x34, 0x20, 0x2D, 0x20, 0x59, 0x65, 0x6E, 0x69, -0x73, 0x65, 0x69, 0x20, 0x52, 0x69, 0x76, 0x65, 0x72, +0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x08, 0x00, 0x00, 0x57, 0x08, +0x00, 0x00, 0x00, 0x00, 0x54, 0x60, 0x00, 0x04, 0x00, 0x00, 0x70, 0x80, 0x01, 0x09, 0x00, 0x00, +0x62, 0x70, 0x00, 0x04, 0x00, 0x00, 0x62, 0x70, 0x00, 0x04, 0x00, 0x00, 0x70, 0x80, 0x01, 0x09, +0x00, 0x00, 0x62, 0x70, 0x01, 0x09, 0x00, 0x00, 0x54, 0x60, 0x00, 0x04, 0x00, 0x00, 0x70, 0x80, +0x00, 0x04, 0x4C, 0x4D, 0x54, 0x00, 0x4B, 0x52, 0x41, 0x54, 0x00, 0x4B, 0x52, 0x41, 0x53, 0x54, +0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0xDE, 0xCD, 0xC2, 0x01, 0xA0, 0x4F, 0x85, 0x00, 0x00, 0x00, 0x19, 0x4D, +0x6F, 0x73, 0x63, 0x6F, 0x77, 0x2B, 0x30, 0x34, 0x20, 0x2D, 0x20, 0x59, 0x65, 0x6E, 0x69, 0x73, +0x65, 0x69, 0x20, 0x52, 0x69, 0x76, 0x65, 0x72, /* Asia/Kuala_Lumpur */ 0x50, 0x48, 0x50, 0x31, 0x01, 0x4D, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -8614,8 +8553,8 @@ const unsigned char timelib_timezone_db_data_builtin[262608] = { /* Asia/Magadan */ 0x50, 0x48, 0x50, 0x31, 0x01, 0x52, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0F, 0xAA, 0x19, 0x36, 0xA0, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0F, 0xAA, 0x19, 0x36, 0xA0, 0xB5, 0xA3, 0xA8, 0xE0, 0x15, 0x27, 0x37, 0x50, 0x16, 0x18, 0x6B, 0xC0, 0x17, 0x08, 0x6A, 0xD0, 0x17, 0xF9, 0x9F, 0x40, 0x18, 0xE9, 0x9E, 0x50, 0x19, 0xDA, 0xD2, 0xC0, 0x1A, 0xCC, 0x23, 0x50, 0x1B, 0xBC, 0x30, 0x70, 0x1C, 0xAC, 0x21, 0x70, 0x1D, 0x9C, 0x12, 0x70, 0x1E, 0x8C, 0x03, 0x70, @@ -8631,35 +8570,19 @@ const unsigned char timelib_timezone_db_data_builtin[262608] = { 0x40, 0x65, 0x96, 0xF0, 0x41, 0x83, 0xAC, 0x70, 0x42, 0x45, 0x78, 0xF0, 0x43, 0x63, 0x8E, 0x70, 0x44, 0x25, 0x5A, 0xF0, 0x45, 0x43, 0x70, 0x70, 0x46, 0x05, 0x3C, 0xF0, 0x47, 0x23, 0x52, 0x70, 0x47, 0xEE, 0x59, 0x70, 0x49, 0x03, 0x34, 0x70, 0x49, 0xCE, 0x3B, 0x70, 0x4A, 0xE3, 0x16, 0x70, -0x4B, 0xAE, 0x1D, 0x70, 0x4C, 0xCC, 0x32, 0xF0, 0x4D, 0x8D, 0xFF, 0x70, 0x4E, 0xAC, 0x14, 0xF0, -0x4F, 0x6D, 0xE1, 0x70, 0x50, 0x8B, 0xF6, 0xF0, 0x51, 0x56, 0xFD, 0xF0, 0x52, 0x6B, 0xD8, 0xF0, -0x53, 0x36, 0xDF, 0xF0, 0x54, 0x4B, 0xBA, 0xF0, 0x55, 0x16, 0xC1, 0xF0, 0x56, 0x2B, 0x9C, 0xF0, -0x56, 0xF6, 0xA3, 0xF0, 0x58, 0x14, 0xB9, 0x70, 0x58, 0xD6, 0x85, 0xF0, 0x59, 0xF4, 0x9B, 0x70, -0x5A, 0xB6, 0x67, 0xF0, 0x5B, 0xD4, 0x7D, 0x70, 0x5C, 0x9F, 0x84, 0x70, 0x5D, 0xB4, 0x5F, 0x70, -0x5E, 0x7F, 0x66, 0x70, 0x5F, 0x94, 0x41, 0x70, 0x60, 0x5F, 0x48, 0x70, 0x61, 0x7D, 0x5D, 0xF0, -0x62, 0x3F, 0x2A, 0x70, 0x63, 0x5D, 0x3F, 0xF0, 0x64, 0x1F, 0x0C, 0x70, 0x65, 0x3D, 0x21, 0xF0, -0x66, 0x08, 0x28, 0xF0, 0x67, 0x1D, 0x03, 0xF0, 0x67, 0xE8, 0x0A, 0xF0, 0x68, 0xFC, 0xE5, 0xF0, -0x69, 0xC7, 0xEC, 0xF0, 0x6A, 0xDC, 0xC7, 0xF0, 0x6B, 0xA7, 0xCE, 0xF0, 0x6C, 0xC5, 0xE4, 0x70, -0x6D, 0x87, 0xB0, 0xF0, 0x6E, 0xA5, 0xC6, 0x70, 0x6F, 0x67, 0x92, 0xF0, 0x70, 0x85, 0xA8, 0x70, -0x71, 0x50, 0xAF, 0x70, 0x72, 0x65, 0x8A, 0x70, 0x73, 0x30, 0x91, 0x70, 0x74, 0x45, 0x6C, 0x70, -0x75, 0x10, 0x73, 0x70, 0x76, 0x2E, 0x88, 0xF0, 0x76, 0xF0, 0x55, 0x70, 0x78, 0x0E, 0x6A, 0xF0, -0x78, 0xD0, 0x37, 0x70, 0x79, 0xEE, 0x4C, 0xF0, 0x7A, 0xB0, 0x19, 0x70, 0x7B, 0xCE, 0x2E, 0xF0, -0x7C, 0x99, 0x35, 0xF0, 0x7D, 0xAE, 0x10, 0xF0, 0x7E, 0x79, 0x17, 0xF0, 0x7F, 0x8D, 0xF2, 0xF0, -0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, -0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x02, 0x03, 0x05, 0x04, 0x05, 0x04, 0x05, -0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, -0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, -0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, -0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, +0x4B, 0xAE, 0x1D, 0x70, 0x4C, 0xCC, 0x32, 0xF0, 0x4D, 0x8D, 0xFF, 0x70, 0x01, 0x03, 0x02, 0x03, +0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, +0x05, 0x04, 0x06, 0x07, 0x04, 0x02, 0x03, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, -0x04, 0x05, 0x04, 0x05, 0x04, 0x00, 0x00, 0x8D, 0x60, 0x00, 0x00, 0x00, 0x00, 0x8C, 0xA0, 0x00, -0x04, 0x00, 0x00, 0xA8, 0xC0, 0x01, 0x09, 0x00, 0x00, 0x9A, 0xB0, 0x00, 0x04, 0x00, 0x00, 0x9A, -0xB0, 0x00, 0x04, 0x00, 0x00, 0xA8, 0xC0, 0x01, 0x09, 0x00, 0x00, 0x9A, 0xB0, 0x01, 0x09, 0x00, -0x00, 0x8C, 0xA0, 0x00, 0x04, 0x4C, 0x4D, 0x54, 0x00, 0x4D, 0x41, 0x47, 0x54, 0x00, 0x4D, 0x41, -0x47, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0x38, 0x7A, 0x01, 0xF8, 0xC2, 0xC0, 0x00, 0x00, 0x00, 0x13, -0x4D, 0x6F, 0x73, 0x63, 0x6F, 0x77, 0x2B, 0x30, 0x38, 0x20, 0x2D, 0x20, 0x4D, 0x61, 0x67, 0x61, -0x64, 0x61, 0x6E, +0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x08, 0x00, 0x00, 0x8D, 0x60, +0x00, 0x00, 0x00, 0x00, 0x8C, 0xA0, 0x00, 0x04, 0x00, 0x00, 0xA8, 0xC0, 0x01, 0x09, 0x00, 0x00, +0x9A, 0xB0, 0x00, 0x04, 0x00, 0x00, 0x9A, 0xB0, 0x00, 0x04, 0x00, 0x00, 0xA8, 0xC0, 0x01, 0x09, +0x00, 0x00, 0x9A, 0xB0, 0x01, 0x09, 0x00, 0x00, 0x8C, 0xA0, 0x00, 0x04, 0x00, 0x00, 0xA8, 0xC0, +0x00, 0x04, 0x4C, 0x4D, 0x54, 0x00, 0x4D, 0x41, 0x47, 0x54, 0x00, 0x4D, 0x41, 0x47, 0x53, 0x54, +0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0xE4, 0x38, 0x7A, 0x01, 0xF8, 0xC2, 0xC0, 0x00, 0x00, 0x00, 0x13, 0x4D, +0x6F, 0x73, 0x63, 0x6F, 0x77, 0x2B, 0x30, 0x38, 0x20, 0x2D, 0x20, 0x4D, 0x61, 0x67, 0x61, 0x64, +0x61, 0x6E, /* Asia/Makassar */ 0x50, 0x48, 0x50, 0x31, 0x01, 0x49, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -8746,8 +8669,8 @@ const unsigned char timelib_timezone_db_data_builtin[262608] = { /* Asia/Novokuznetsk */ 0x50, 0x48, 0x50, 0x31, 0x01, 0x52, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x1A, 0xA1, 0xF9, 0x13, 0x40, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x1A, 0xA1, 0xF9, 0x13, 0x40, 0xB5, 0xA3, 0xE1, 0x20, 0x15, 0x27, 0x6F, 0x90, 0x16, 0x18, 0xA4, 0x00, 0x17, 0x08, 0xA3, 0x10, 0x17, 0xF9, 0xD7, 0x80, 0x18, 0xE9, 0xD6, 0x90, 0x19, 0xDB, 0x0B, 0x00, 0x1A, 0xCC, 0x5B, 0x90, 0x1B, 0xBC, 0x68, 0xB0, 0x1C, 0xAC, 0x59, 0xB0, 0x1D, 0x9C, 0x4A, 0xB0, 0x1E, 0x8C, 0x3B, 0xB0, @@ -8763,42 +8686,26 @@ const unsigned char timelib_timezone_db_data_builtin[262608] = { 0x40, 0x65, 0xCF, 0x30, 0x41, 0x83, 0xE4, 0xB0, 0x42, 0x45, 0xB1, 0x30, 0x43, 0x63, 0xC6, 0xB0, 0x44, 0x25, 0x93, 0x30, 0x45, 0x43, 0xA8, 0xB0, 0x46, 0x05, 0x75, 0x30, 0x47, 0x23, 0x8A, 0xB0, 0x47, 0xEE, 0x91, 0xB0, 0x49, 0x03, 0x6C, 0xB0, 0x49, 0xCE, 0x73, 0xB0, 0x4A, 0xE3, 0x4E, 0xB0, -0x4B, 0xAE, 0x55, 0xB0, 0x4C, 0xCC, 0x79, 0x40, 0x4D, 0x8E, 0x45, 0xC0, 0x4E, 0xAC, 0x5B, 0x40, -0x4F, 0x6E, 0x27, 0xC0, 0x50, 0x8C, 0x3D, 0x40, 0x51, 0x57, 0x44, 0x40, 0x52, 0x6C, 0x1F, 0x40, -0x53, 0x37, 0x26, 0x40, 0x54, 0x4C, 0x01, 0x40, 0x55, 0x17, 0x08, 0x40, 0x56, 0x2B, 0xE3, 0x40, -0x56, 0xF6, 0xEA, 0x40, 0x58, 0x14, 0xFF, 0xC0, 0x58, 0xD6, 0xCC, 0x40, 0x59, 0xF4, 0xE1, 0xC0, -0x5A, 0xB6, 0xAE, 0x40, 0x5B, 0xD4, 0xC3, 0xC0, 0x5C, 0x9F, 0xCA, 0xC0, 0x5D, 0xB4, 0xA5, 0xC0, -0x5E, 0x7F, 0xAC, 0xC0, 0x5F, 0x94, 0x87, 0xC0, 0x60, 0x5F, 0x8E, 0xC0, 0x61, 0x7D, 0xA4, 0x40, -0x62, 0x3F, 0x70, 0xC0, 0x63, 0x5D, 0x86, 0x40, 0x64, 0x1F, 0x52, 0xC0, 0x65, 0x3D, 0x68, 0x40, -0x66, 0x08, 0x6F, 0x40, 0x67, 0x1D, 0x4A, 0x40, 0x67, 0xE8, 0x51, 0x40, 0x68, 0xFD, 0x2C, 0x40, -0x69, 0xC8, 0x33, 0x40, 0x6A, 0xDD, 0x0E, 0x40, 0x6B, 0xA8, 0x15, 0x40, 0x6C, 0xC6, 0x2A, 0xC0, -0x6D, 0x87, 0xF7, 0x40, 0x6E, 0xA6, 0x0C, 0xC0, 0x6F, 0x67, 0xD9, 0x40, 0x70, 0x85, 0xEE, 0xC0, -0x71, 0x50, 0xF5, 0xC0, 0x72, 0x65, 0xD0, 0xC0, 0x73, 0x30, 0xD7, 0xC0, 0x74, 0x45, 0xB2, 0xC0, -0x75, 0x10, 0xB9, 0xC0, 0x76, 0x2E, 0xCF, 0x40, 0x76, 0xF0, 0x9B, 0xC0, 0x78, 0x0E, 0xB1, 0x40, -0x78, 0xD0, 0x7D, 0xC0, 0x79, 0xEE, 0x93, 0x40, 0x7A, 0xB0, 0x5F, 0xC0, 0x7B, 0xCE, 0x75, 0x40, -0x7C, 0x99, 0x7C, 0x40, 0x7D, 0xAE, 0x57, 0x40, 0x7E, 0x79, 0x5E, 0x40, 0x7F, 0x8E, 0x39, 0x40, -0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, -0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x02, 0x03, 0x05, 0x04, 0x05, 0x04, 0x05, +0x4B, 0xAE, 0x55, 0xB0, 0x4C, 0xCC, 0x79, 0x40, 0x4D, 0x8E, 0x45, 0xC0, 0x01, 0x03, 0x02, 0x03, +0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, +0x05, 0x04, 0x06, 0x07, 0x04, 0x02, 0x03, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, -0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x08, 0x09, 0x08, -0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, -0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, -0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, -0x09, 0x08, 0x09, 0x08, 0x09, 0x00, 0x00, 0x51, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x54, 0x60, 0x00, -0x04, 0x00, 0x00, 0x70, 0x80, 0x01, 0x09, 0x00, 0x00, 0x62, 0x70, 0x00, 0x04, 0x00, 0x00, 0x62, -0x70, 0x00, 0x04, 0x00, 0x00, 0x70, 0x80, 0x01, 0x09, 0x00, 0x00, 0x62, 0x70, 0x01, 0x09, 0x00, -0x00, 0x54, 0x60, 0x00, 0x04, 0x00, 0x00, 0x62, 0x70, 0x01, 0x0F, 0x00, 0x00, 0x54, 0x60, 0x00, -0x15, 0x4E, 0x4D, 0x54, 0x00, 0x4B, 0x52, 0x41, 0x54, 0x00, 0x4B, 0x52, 0x41, 0x53, 0x54, 0x00, -0x4E, 0x4F, 0x56, 0x53, 0x54, 0x00, 0x4E, 0x4F, 0x56, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xDB, 0x58, 0x58, 0x01, 0x97, 0x96, 0x72, 0x00, 0x00, 0x00, 0x18, 0x4D, 0x6F, 0x73, 0x63, 0x6F, -0x77, 0x2B, 0x30, 0x33, 0x20, 0x2D, 0x20, 0x4E, 0x6F, 0x76, 0x6F, 0x6B, 0x75, 0x7A, 0x6E, 0x65, -0x74, 0x73, 0x6B, +0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x08, 0x09, 0x0A, 0x00, 0x00, 0x51, 0xC0, +0x00, 0x00, 0x00, 0x00, 0x54, 0x60, 0x00, 0x04, 0x00, 0x00, 0x70, 0x80, 0x01, 0x09, 0x00, 0x00, +0x62, 0x70, 0x00, 0x04, 0x00, 0x00, 0x62, 0x70, 0x00, 0x04, 0x00, 0x00, 0x70, 0x80, 0x01, 0x09, +0x00, 0x00, 0x62, 0x70, 0x01, 0x09, 0x00, 0x00, 0x54, 0x60, 0x00, 0x04, 0x00, 0x00, 0x62, 0x70, +0x01, 0x0F, 0x00, 0x00, 0x54, 0x60, 0x00, 0x15, 0x00, 0x00, 0x62, 0x70, 0x00, 0x15, 0x4E, 0x4D, +0x54, 0x00, 0x4B, 0x52, 0x41, 0x54, 0x00, 0x4B, 0x52, 0x41, 0x53, 0x54, 0x00, 0x4E, 0x4F, 0x56, +0x53, 0x54, 0x00, 0x4E, 0x4F, 0x56, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDB, +0x58, 0x58, 0x01, 0x97, 0x96, 0x72, 0x00, 0x00, 0x00, 0x18, 0x4D, 0x6F, 0x73, 0x63, 0x6F, 0x77, +0x2B, 0x30, 0x33, 0x20, 0x2D, 0x20, 0x4E, 0x6F, 0x76, 0x6F, 0x6B, 0x75, 0x7A, 0x6E, 0x65, 0x74, +0x73, 0x6B, /* Asia/Novosibirsk */ 0x50, 0x48, 0x50, 0x31, 0x01, 0x52, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0F, 0xA1, 0xDB, 0x19, 0x24, +0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0F, 0xA1, 0xDB, 0x19, 0x24, 0xB5, 0xA3, 0xE1, 0x20, 0x15, 0x27, 0x6F, 0x90, 0x16, 0x18, 0xA4, 0x00, 0x17, 0x08, 0xA3, 0x10, 0x17, 0xF9, 0xD7, 0x80, 0x18, 0xE9, 0xD6, 0x90, 0x19, 0xDB, 0x0B, 0x00, 0x1A, 0xCC, 0x5B, 0x90, 0x1B, 0xBC, 0x68, 0xB0, 0x1C, 0xAC, 0x59, 0xB0, 0x1D, 0x9C, 0x4A, 0xB0, 0x1E, 0x8C, 0x3B, 0xB0, @@ -8815,40 +8722,23 @@ const unsigned char timelib_timezone_db_data_builtin[262608] = { 0x43, 0x63, 0xD4, 0xC0, 0x44, 0x25, 0xA1, 0x40, 0x45, 0x43, 0xB6, 0xC0, 0x46, 0x05, 0x83, 0x40, 0x47, 0x23, 0x98, 0xC0, 0x47, 0xEE, 0x9F, 0xC0, 0x49, 0x03, 0x7A, 0xC0, 0x49, 0xCE, 0x81, 0xC0, 0x4A, 0xE3, 0x5C, 0xC0, 0x4B, 0xAE, 0x63, 0xC0, 0x4C, 0xCC, 0x79, 0x40, 0x4D, 0x8E, 0x45, 0xC0, -0x4E, 0xAC, 0x5B, 0x40, 0x4F, 0x6E, 0x27, 0xC0, 0x50, 0x8C, 0x3D, 0x40, 0x51, 0x57, 0x44, 0x40, -0x52, 0x6C, 0x1F, 0x40, 0x53, 0x37, 0x26, 0x40, 0x54, 0x4C, 0x01, 0x40, 0x55, 0x17, 0x08, 0x40, -0x56, 0x2B, 0xE3, 0x40, 0x56, 0xF6, 0xEA, 0x40, 0x58, 0x14, 0xFF, 0xC0, 0x58, 0xD6, 0xCC, 0x40, -0x59, 0xF4, 0xE1, 0xC0, 0x5A, 0xB6, 0xAE, 0x40, 0x5B, 0xD4, 0xC3, 0xC0, 0x5C, 0x9F, 0xCA, 0xC0, -0x5D, 0xB4, 0xA5, 0xC0, 0x5E, 0x7F, 0xAC, 0xC0, 0x5F, 0x94, 0x87, 0xC0, 0x60, 0x5F, 0x8E, 0xC0, -0x61, 0x7D, 0xA4, 0x40, 0x62, 0x3F, 0x70, 0xC0, 0x63, 0x5D, 0x86, 0x40, 0x64, 0x1F, 0x52, 0xC0, -0x65, 0x3D, 0x68, 0x40, 0x66, 0x08, 0x6F, 0x40, 0x67, 0x1D, 0x4A, 0x40, 0x67, 0xE8, 0x51, 0x40, -0x68, 0xFD, 0x2C, 0x40, 0x69, 0xC8, 0x33, 0x40, 0x6A, 0xDD, 0x0E, 0x40, 0x6B, 0xA8, 0x15, 0x40, -0x6C, 0xC6, 0x2A, 0xC0, 0x6D, 0x87, 0xF7, 0x40, 0x6E, 0xA6, 0x0C, 0xC0, 0x6F, 0x67, 0xD9, 0x40, -0x70, 0x85, 0xEE, 0xC0, 0x71, 0x50, 0xF5, 0xC0, 0x72, 0x65, 0xD0, 0xC0, 0x73, 0x30, 0xD7, 0xC0, -0x74, 0x45, 0xB2, 0xC0, 0x75, 0x10, 0xB9, 0xC0, 0x76, 0x2E, 0xCF, 0x40, 0x76, 0xF0, 0x9B, 0xC0, -0x78, 0x0E, 0xB1, 0x40, 0x78, 0xD0, 0x7D, 0xC0, 0x79, 0xEE, 0x93, 0x40, 0x7A, 0xB0, 0x5F, 0xC0, -0x7B, 0xCE, 0x75, 0x40, 0x7C, 0x99, 0x7C, 0x40, 0x7D, 0xAE, 0x57, 0x40, 0x7E, 0x79, 0x5E, 0x40, -0x7F, 0x8E, 0x39, 0x40, 0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05, 0x04, -0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x02, 0x03, 0x05, -0x08, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, -0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, -0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, +0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, +0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x02, 0x03, 0x05, 0x08, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, -0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x00, 0x00, 0x4D, 0xBC, 0x00, 0x00, -0x00, 0x00, 0x54, 0x60, 0x00, 0x04, 0x00, 0x00, 0x70, 0x80, 0x01, 0x09, 0x00, 0x00, 0x62, 0x70, -0x00, 0x04, 0x00, 0x00, 0x62, 0x70, 0x00, 0x04, 0x00, 0x00, 0x70, 0x80, 0x01, 0x09, 0x00, 0x00, -0x62, 0x70, 0x01, 0x09, 0x00, 0x00, 0x54, 0x60, 0x00, 0x04, 0x00, 0x00, 0x62, 0x70, 0x01, 0x09, -0x4C, 0x4D, 0x54, 0x00, 0x4E, 0x4F, 0x56, 0x54, 0x00, 0x4E, 0x4F, 0x56, 0x53, 0x54, 0x00, 0x00, -0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0xDD, 0x4D, 0xA5, 0x01, 0x91, 0x2D, 0xD2, 0x00, 0x00, 0x00, 0x17, 0x4D, 0x6F, 0x73, -0x63, 0x6F, 0x77, 0x2B, 0x30, 0x33, 0x20, 0x2D, 0x20, 0x4E, 0x6F, 0x76, 0x6F, 0x73, 0x69, 0x62, -0x69, 0x72, 0x73, 0x6B, +0x04, 0x00, 0x00, 0x4D, 0xBC, 0x00, 0x00, 0x00, 0x00, 0x54, 0x60, 0x00, 0x04, 0x00, 0x00, 0x70, +0x80, 0x01, 0x09, 0x00, 0x00, 0x62, 0x70, 0x00, 0x04, 0x00, 0x00, 0x62, 0x70, 0x00, 0x04, 0x00, +0x00, 0x70, 0x80, 0x01, 0x09, 0x00, 0x00, 0x62, 0x70, 0x01, 0x09, 0x00, 0x00, 0x54, 0x60, 0x00, +0x04, 0x00, 0x00, 0x62, 0x70, 0x01, 0x09, 0x4C, 0x4D, 0x54, 0x00, 0x4E, 0x4F, 0x56, 0x54, 0x00, +0x4E, 0x4F, 0x56, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDD, 0x4D, 0xA5, 0x01, 0x91, 0x2D, 0xD2, +0x00, 0x00, 0x00, 0x17, 0x4D, 0x6F, 0x73, 0x63, 0x6F, 0x77, 0x2B, 0x30, 0x33, 0x20, 0x2D, 0x20, +0x4E, 0x6F, 0x76, 0x6F, 0x73, 0x69, 0x62, 0x69, 0x72, 0x73, 0x6B, /* Asia/Omsk */ 0x50, 0x48, 0x50, 0x31, 0x01, 0x52, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0F, 0xA1, 0xB3, 0x40, 0xB0, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0F, 0xA1, 0xB3, 0x40, 0xB0, 0xB5, 0xA3, 0xEF, 0x30, 0x15, 0x27, 0x7D, 0xA0, 0x16, 0x18, 0xB2, 0x10, 0x17, 0x08, 0xB1, 0x20, 0x17, 0xF9, 0xE5, 0x90, 0x18, 0xE9, 0xE4, 0xA0, 0x19, 0xDB, 0x19, 0x10, 0x1A, 0xCC, 0x69, 0xA0, 0x1B, 0xBC, 0x76, 0xC0, 0x1C, 0xAC, 0x67, 0xC0, 0x1D, 0x9C, 0x58, 0xC0, 0x1E, 0x8C, 0x49, 0xC0, @@ -8864,35 +8754,19 @@ const unsigned char timelib_timezone_db_data_builtin[262608] = { 0x40, 0x65, 0xDD, 0x40, 0x41, 0x83, 0xF2, 0xC0, 0x42, 0x45, 0xBF, 0x40, 0x43, 0x63, 0xD4, 0xC0, 0x44, 0x25, 0xA1, 0x40, 0x45, 0x43, 0xB6, 0xC0, 0x46, 0x05, 0x83, 0x40, 0x47, 0x23, 0x98, 0xC0, 0x47, 0xEE, 0x9F, 0xC0, 0x49, 0x03, 0x7A, 0xC0, 0x49, 0xCE, 0x81, 0xC0, 0x4A, 0xE3, 0x5C, 0xC0, -0x4B, 0xAE, 0x63, 0xC0, 0x4C, 0xCC, 0x79, 0x40, 0x4D, 0x8E, 0x45, 0xC0, 0x4E, 0xAC, 0x5B, 0x40, -0x4F, 0x6E, 0x27, 0xC0, 0x50, 0x8C, 0x3D, 0x40, 0x51, 0x57, 0x44, 0x40, 0x52, 0x6C, 0x1F, 0x40, -0x53, 0x37, 0x26, 0x40, 0x54, 0x4C, 0x01, 0x40, 0x55, 0x17, 0x08, 0x40, 0x56, 0x2B, 0xE3, 0x40, -0x56, 0xF6, 0xEA, 0x40, 0x58, 0x14, 0xFF, 0xC0, 0x58, 0xD6, 0xCC, 0x40, 0x59, 0xF4, 0xE1, 0xC0, -0x5A, 0xB6, 0xAE, 0x40, 0x5B, 0xD4, 0xC3, 0xC0, 0x5C, 0x9F, 0xCA, 0xC0, 0x5D, 0xB4, 0xA5, 0xC0, -0x5E, 0x7F, 0xAC, 0xC0, 0x5F, 0x94, 0x87, 0xC0, 0x60, 0x5F, 0x8E, 0xC0, 0x61, 0x7D, 0xA4, 0x40, -0x62, 0x3F, 0x70, 0xC0, 0x63, 0x5D, 0x86, 0x40, 0x64, 0x1F, 0x52, 0xC0, 0x65, 0x3D, 0x68, 0x40, -0x66, 0x08, 0x6F, 0x40, 0x67, 0x1D, 0x4A, 0x40, 0x67, 0xE8, 0x51, 0x40, 0x68, 0xFD, 0x2C, 0x40, -0x69, 0xC8, 0x33, 0x40, 0x6A, 0xDD, 0x0E, 0x40, 0x6B, 0xA8, 0x15, 0x40, 0x6C, 0xC6, 0x2A, 0xC0, -0x6D, 0x87, 0xF7, 0x40, 0x6E, 0xA6, 0x0C, 0xC0, 0x6F, 0x67, 0xD9, 0x40, 0x70, 0x85, 0xEE, 0xC0, -0x71, 0x50, 0xF5, 0xC0, 0x72, 0x65, 0xD0, 0xC0, 0x73, 0x30, 0xD7, 0xC0, 0x74, 0x45, 0xB2, 0xC0, -0x75, 0x10, 0xB9, 0xC0, 0x76, 0x2E, 0xCF, 0x40, 0x76, 0xF0, 0x9B, 0xC0, 0x78, 0x0E, 0xB1, 0x40, -0x78, 0xD0, 0x7D, 0xC0, 0x79, 0xEE, 0x93, 0x40, 0x7A, 0xB0, 0x5F, 0xC0, 0x7B, 0xCE, 0x75, 0x40, -0x7C, 0x99, 0x7C, 0x40, 0x7D, 0xAE, 0x57, 0x40, 0x7E, 0x79, 0x5E, 0x40, 0x7F, 0x8E, 0x39, 0x40, -0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, -0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x02, 0x03, 0x05, 0x04, 0x05, 0x04, 0x05, -0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, -0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, -0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, -0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, +0x4B, 0xAE, 0x63, 0xC0, 0x4C, 0xCC, 0x79, 0x40, 0x4D, 0x8E, 0x45, 0xC0, 0x01, 0x03, 0x02, 0x03, +0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, +0x05, 0x04, 0x06, 0x07, 0x04, 0x02, 0x03, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, -0x04, 0x05, 0x04, 0x05, 0x04, 0x00, 0x00, 0x44, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x46, 0x50, 0x00, -0x04, 0x00, 0x00, 0x62, 0x70, 0x01, 0x09, 0x00, 0x00, 0x54, 0x60, 0x00, 0x04, 0x00, 0x00, 0x54, -0x60, 0x00, 0x04, 0x00, 0x00, 0x62, 0x70, 0x01, 0x09, 0x00, 0x00, 0x54, 0x60, 0x01, 0x09, 0x00, -0x00, 0x46, 0x50, 0x00, 0x04, 0x4C, 0x4D, 0x54, 0x00, 0x4F, 0x4D, 0x53, 0x54, 0x00, 0x4F, 0x4D, -0x53, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0xDD, 0x40, 0xA0, 0x01, 0x82, 0xA8, 0x60, 0x00, 0x00, 0x00, 0x18, -0x4D, 0x6F, 0x73, 0x63, 0x6F, 0x77, 0x2B, 0x30, 0x33, 0x20, 0x2D, 0x20, 0x77, 0x65, 0x73, 0x74, -0x20, 0x53, 0x69, 0x62, 0x65, 0x72, 0x69, 0x61, +0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x08, 0x00, 0x00, 0x44, 0xD0, +0x00, 0x00, 0x00, 0x00, 0x46, 0x50, 0x00, 0x04, 0x00, 0x00, 0x62, 0x70, 0x01, 0x09, 0x00, 0x00, +0x54, 0x60, 0x00, 0x04, 0x00, 0x00, 0x54, 0x60, 0x00, 0x04, 0x00, 0x00, 0x62, 0x70, 0x01, 0x09, +0x00, 0x00, 0x54, 0x60, 0x01, 0x09, 0x00, 0x00, 0x46, 0x50, 0x00, 0x04, 0x00, 0x00, 0x62, 0x70, +0x00, 0x04, 0x4C, 0x4D, 0x54, 0x00, 0x4F, 0x4D, 0x53, 0x54, 0x00, 0x4F, 0x4D, 0x53, 0x53, 0x54, +0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0xDD, 0x40, 0xA0, 0x01, 0x82, 0xA8, 0x60, 0x00, 0x00, 0x00, 0x18, 0x4D, +0x6F, 0x73, 0x63, 0x6F, 0x77, 0x2B, 0x30, 0x33, 0x20, 0x2D, 0x20, 0x77, 0x65, 0x73, 0x74, 0x20, +0x53, 0x69, 0x62, 0x65, 0x72, 0x69, 0x61, /* Asia/Oral */ 0x50, 0x48, 0x50, 0x31, 0x01, 0x4B, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -9032,7 +8906,7 @@ const unsigned char timelib_timezone_db_data_builtin[262608] = { /* Asia/Sakhalin */ 0x50, 0x48, 0x50, 0x31, 0x01, 0x52, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x17, 0x86, 0xF0, 0xCD, 0xB8, +0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x17, 0x86, 0xF0, 0xCD, 0xB8, 0xC3, 0xCE, 0x85, 0x70, 0xD2, 0x30, 0xB2, 0xF0, 0x15, 0x27, 0x37, 0x50, 0x16, 0x18, 0x6B, 0xC0, 0x17, 0x08, 0x6A, 0xD0, 0x17, 0xF9, 0x9F, 0x40, 0x18, 0xE9, 0x9E, 0x50, 0x19, 0xDA, 0xD2, 0xC0, 0x1A, 0xCC, 0x23, 0x50, 0x1B, 0xBC, 0x30, 0x70, 0x1C, 0xAC, 0x21, 0x70, 0x1D, 0x9C, 0x12, 0x70, @@ -9049,36 +8923,19 @@ const unsigned char timelib_timezone_db_data_builtin[262608] = { 0x43, 0x63, 0x9C, 0x80, 0x44, 0x25, 0x69, 0x00, 0x45, 0x43, 0x7E, 0x80, 0x46, 0x05, 0x4B, 0x00, 0x47, 0x23, 0x60, 0x80, 0x47, 0xEE, 0x67, 0x80, 0x49, 0x03, 0x42, 0x80, 0x49, 0xCE, 0x49, 0x80, 0x4A, 0xE3, 0x24, 0x80, 0x4B, 0xAE, 0x2B, 0x80, 0x4C, 0xCC, 0x41, 0x00, 0x4D, 0x8E, 0x0D, 0x80, -0x4E, 0xAC, 0x23, 0x00, 0x4F, 0x6D, 0xEF, 0x80, 0x50, 0x8C, 0x05, 0x00, 0x51, 0x57, 0x0C, 0x00, -0x52, 0x6B, 0xE7, 0x00, 0x53, 0x36, 0xEE, 0x00, 0x54, 0x4B, 0xC9, 0x00, 0x55, 0x16, 0xD0, 0x00, -0x56, 0x2B, 0xAB, 0x00, 0x56, 0xF6, 0xB2, 0x00, 0x58, 0x14, 0xC7, 0x80, 0x58, 0xD6, 0x94, 0x00, -0x59, 0xF4, 0xA9, 0x80, 0x5A, 0xB6, 0x76, 0x00, 0x5B, 0xD4, 0x8B, 0x80, 0x5C, 0x9F, 0x92, 0x80, -0x5D, 0xB4, 0x6D, 0x80, 0x5E, 0x7F, 0x74, 0x80, 0x5F, 0x94, 0x4F, 0x80, 0x60, 0x5F, 0x56, 0x80, -0x61, 0x7D, 0x6C, 0x00, 0x62, 0x3F, 0x38, 0x80, 0x63, 0x5D, 0x4E, 0x00, 0x64, 0x1F, 0x1A, 0x80, -0x65, 0x3D, 0x30, 0x00, 0x66, 0x08, 0x37, 0x00, 0x67, 0x1D, 0x12, 0x00, 0x67, 0xE8, 0x19, 0x00, -0x68, 0xFC, 0xF4, 0x00, 0x69, 0xC7, 0xFB, 0x00, 0x6A, 0xDC, 0xD6, 0x00, 0x6B, 0xA7, 0xDD, 0x00, -0x6C, 0xC5, 0xF2, 0x80, 0x6D, 0x87, 0xBF, 0x00, 0x6E, 0xA5, 0xD4, 0x80, 0x6F, 0x67, 0xA1, 0x00, -0x70, 0x85, 0xB6, 0x80, 0x71, 0x50, 0xBD, 0x80, 0x72, 0x65, 0x98, 0x80, 0x73, 0x30, 0x9F, 0x80, -0x74, 0x45, 0x7A, 0x80, 0x75, 0x10, 0x81, 0x80, 0x76, 0x2E, 0x97, 0x00, 0x76, 0xF0, 0x63, 0x80, -0x78, 0x0E, 0x79, 0x00, 0x78, 0xD0, 0x45, 0x80, 0x79, 0xEE, 0x5B, 0x00, 0x7A, 0xB0, 0x27, 0x80, -0x7B, 0xCE, 0x3D, 0x00, 0x7C, 0x99, 0x44, 0x00, 0x7D, 0xAE, 0x1F, 0x00, 0x7E, 0x79, 0x26, 0x00, -0x7F, 0x8E, 0x01, 0x00, 0x01, 0x02, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x05, 0x06, -0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x07, 0x08, 0x05, 0x03, 0x04, -0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, -0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, +0x01, 0x02, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, +0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x07, 0x08, 0x05, 0x03, 0x04, 0x06, 0x05, 0x06, 0x05, +0x06, 0x05, 0x06, 0x05, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, -0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, -0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, -0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x07, 0x08, 0x00, 0x00, 0x85, 0xC8, 0x00, 0x00, -0x00, 0x00, 0x7E, 0x90, 0x00, 0x04, 0x00, 0x00, 0x7E, 0x90, 0x00, 0x08, 0x00, 0x00, 0xA8, 0xC0, -0x01, 0x0C, 0x00, 0x00, 0x9A, 0xB0, 0x00, 0x12, 0x00, 0x00, 0x9A, 0xB0, 0x00, 0x12, 0x00, 0x00, -0xA8, 0xC0, 0x01, 0x0C, 0x00, 0x00, 0x9A, 0xB0, 0x01, 0x0C, 0x00, 0x00, 0x8C, 0xA0, 0x00, 0x12, -0x4C, 0x4D, 0x54, 0x00, 0x43, 0x4A, 0x54, 0x00, 0x4A, 0x53, 0x54, 0x00, 0x53, 0x41, 0x4B, 0x53, -0x54, 0x00, 0x53, 0x41, 0x4B, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD0, 0xFE, 0x9A, 0x01, 0xEC, 0x66, -0xB0, 0x00, 0x00, 0x00, 0x1B, 0x4D, 0x6F, 0x73, 0x63, 0x6F, 0x77, 0x2B, 0x30, 0x37, 0x20, 0x2D, -0x20, 0x53, 0x61, 0x6B, 0x68, 0x61, 0x6C, 0x69, 0x6E, 0x20, 0x49, 0x73, 0x6C, 0x61, 0x6E, 0x64, - +0x05, 0x00, 0x00, 0x85, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x90, 0x00, 0x04, 0x00, 0x00, 0x7E, +0x90, 0x00, 0x08, 0x00, 0x00, 0xA8, 0xC0, 0x01, 0x0C, 0x00, 0x00, 0x9A, 0xB0, 0x00, 0x12, 0x00, +0x00, 0x9A, 0xB0, 0x00, 0x12, 0x00, 0x00, 0xA8, 0xC0, 0x01, 0x0C, 0x00, 0x00, 0x9A, 0xB0, 0x01, +0x0C, 0x00, 0x00, 0x8C, 0xA0, 0x00, 0x12, 0x4C, 0x4D, 0x54, 0x00, 0x43, 0x4A, 0x54, 0x00, 0x4A, +0x53, 0x54, 0x00, 0x53, 0x41, 0x4B, 0x53, 0x54, 0x00, 0x53, 0x41, 0x4B, 0x54, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0xD0, 0xFE, 0x9A, 0x01, 0xEC, 0x66, 0xB0, 0x00, 0x00, 0x00, 0x1B, 0x4D, 0x6F, 0x73, 0x63, +0x6F, 0x77, 0x2B, 0x30, 0x37, 0x20, 0x2D, 0x20, 0x53, 0x61, 0x6B, 0x68, 0x61, 0x6C, 0x69, 0x6E, +0x20, 0x49, 0x73, 0x6C, 0x61, 0x6E, 0x64, /* Asia/Samarkand */ 0x50, 0x48, 0x50, 0x31, 0x01, 0x55, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -9425,8 +9282,8 @@ const unsigned char timelib_timezone_db_data_builtin[262608] = { /* Asia/Vladivostok */ 0x50, 0x48, 0x50, 0x31, 0x01, 0x52, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x16, 0xA7, 0x59, 0x47, 0x50, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x16, 0xA7, 0x59, 0x47, 0x50, 0xB5, 0xA3, 0xB6, 0xF0, 0x15, 0x27, 0x45, 0x60, 0x16, 0x18, 0x79, 0xD0, 0x17, 0x08, 0x78, 0xE0, 0x17, 0xF9, 0xAD, 0x50, 0x18, 0xE9, 0xAC, 0x60, 0x19, 0xDA, 0xE0, 0xD0, 0x1A, 0xCC, 0x31, 0x60, 0x1B, 0xBC, 0x3E, 0x80, 0x1C, 0xAC, 0x2F, 0x80, 0x1D, 0x9C, 0x20, 0x80, 0x1E, 0x8C, 0x11, 0x80, @@ -9442,40 +9299,24 @@ const unsigned char timelib_timezone_db_data_builtin[262608] = { 0x40, 0x65, 0xA5, 0x00, 0x41, 0x83, 0xBA, 0x80, 0x42, 0x45, 0x87, 0x00, 0x43, 0x63, 0x9C, 0x80, 0x44, 0x25, 0x69, 0x00, 0x45, 0x43, 0x7E, 0x80, 0x46, 0x05, 0x4B, 0x00, 0x47, 0x23, 0x60, 0x80, 0x47, 0xEE, 0x67, 0x80, 0x49, 0x03, 0x42, 0x80, 0x49, 0xCE, 0x49, 0x80, 0x4A, 0xE3, 0x24, 0x80, -0x4B, 0xAE, 0x2B, 0x80, 0x4C, 0xCC, 0x41, 0x00, 0x4D, 0x8E, 0x0D, 0x80, 0x4E, 0xAC, 0x23, 0x00, -0x4F, 0x6D, 0xEF, 0x80, 0x50, 0x8C, 0x05, 0x00, 0x51, 0x57, 0x0C, 0x00, 0x52, 0x6B, 0xE7, 0x00, -0x53, 0x36, 0xEE, 0x00, 0x54, 0x4B, 0xC9, 0x00, 0x55, 0x16, 0xD0, 0x00, 0x56, 0x2B, 0xAB, 0x00, -0x56, 0xF6, 0xB2, 0x00, 0x58, 0x14, 0xC7, 0x80, 0x58, 0xD6, 0x94, 0x00, 0x59, 0xF4, 0xA9, 0x80, -0x5A, 0xB6, 0x76, 0x00, 0x5B, 0xD4, 0x8B, 0x80, 0x5C, 0x9F, 0x92, 0x80, 0x5D, 0xB4, 0x6D, 0x80, -0x5E, 0x7F, 0x74, 0x80, 0x5F, 0x94, 0x4F, 0x80, 0x60, 0x5F, 0x56, 0x80, 0x61, 0x7D, 0x6C, 0x00, -0x62, 0x3F, 0x38, 0x80, 0x63, 0x5D, 0x4E, 0x00, 0x64, 0x1F, 0x1A, 0x80, 0x65, 0x3D, 0x30, 0x00, -0x66, 0x08, 0x37, 0x00, 0x67, 0x1D, 0x12, 0x00, 0x67, 0xE8, 0x19, 0x00, 0x68, 0xFC, 0xF4, 0x00, -0x69, 0xC7, 0xFB, 0x00, 0x6A, 0xDC, 0xD6, 0x00, 0x6B, 0xA7, 0xDD, 0x00, 0x6C, 0xC5, 0xF2, 0x80, -0x6D, 0x87, 0xBF, 0x00, 0x6E, 0xA5, 0xD4, 0x80, 0x6F, 0x67, 0xA1, 0x00, 0x70, 0x85, 0xB6, 0x80, -0x71, 0x50, 0xBD, 0x80, 0x72, 0x65, 0x98, 0x80, 0x73, 0x30, 0x9F, 0x80, 0x74, 0x45, 0x7A, 0x80, -0x75, 0x10, 0x81, 0x80, 0x76, 0x2E, 0x97, 0x00, 0x76, 0xF0, 0x63, 0x80, 0x78, 0x0E, 0x79, 0x00, -0x78, 0xD0, 0x45, 0x80, 0x79, 0xEE, 0x5B, 0x00, 0x7A, 0xB0, 0x27, 0x80, 0x7B, 0xCE, 0x3D, 0x00, -0x7C, 0x99, 0x44, 0x00, 0x7D, 0xAE, 0x1F, 0x00, 0x7E, 0x79, 0x26, 0x00, 0x7F, 0x8E, 0x01, 0x00, -0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, -0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x02, 0x03, 0x05, 0x04, 0x05, 0x04, 0x05, -0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, -0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, -0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, -0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, +0x4B, 0xAE, 0x2B, 0x80, 0x4C, 0xCC, 0x41, 0x00, 0x4D, 0x8E, 0x0D, 0x80, 0x01, 0x03, 0x02, 0x03, +0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, +0x05, 0x04, 0x06, 0x07, 0x04, 0x02, 0x03, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, -0x04, 0x05, 0x04, 0x05, 0x04, 0x00, 0x00, 0x7B, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x90, 0x00, -0x04, 0x00, 0x00, 0x9A, 0xB0, 0x01, 0x09, 0x00, 0x00, 0x8C, 0xA0, 0x00, 0x04, 0x00, 0x00, 0x8C, -0xA0, 0x00, 0x04, 0x00, 0x00, 0x9A, 0xB0, 0x01, 0x09, 0x00, 0x00, 0x8C, 0xA0, 0x01, 0x0F, 0x00, -0x00, 0x7E, 0x90, 0x00, 0x09, 0x4C, 0x4D, 0x54, 0x00, 0x56, 0x4C, 0x41, 0x54, 0x00, 0x56, 0x4C, -0x41, 0x53, 0x54, 0x00, 0x56, 0x4C, 0x41, 0x53, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCB, 0x32, 0x3A, 0x01, -0xDB, 0xF8, 0xF5, 0x00, 0x00, 0x00, 0x16, 0x4D, 0x6F, 0x73, 0x63, 0x6F, 0x77, 0x2B, 0x30, 0x37, -0x20, 0x2D, 0x20, 0x41, 0x6D, 0x75, 0x72, 0x20, 0x52, 0x69, 0x76, 0x65, 0x72, +0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x08, 0x00, 0x00, 0x7B, 0xB0, +0x00, 0x00, 0x00, 0x00, 0x7E, 0x90, 0x00, 0x04, 0x00, 0x00, 0x9A, 0xB0, 0x01, 0x09, 0x00, 0x00, +0x8C, 0xA0, 0x00, 0x04, 0x00, 0x00, 0x8C, 0xA0, 0x00, 0x04, 0x00, 0x00, 0x9A, 0xB0, 0x01, 0x09, +0x00, 0x00, 0x8C, 0xA0, 0x01, 0x0F, 0x00, 0x00, 0x7E, 0x90, 0x00, 0x09, 0x00, 0x00, 0x9A, 0xB0, +0x00, 0x04, 0x4C, 0x4D, 0x54, 0x00, 0x56, 0x4C, 0x41, 0x54, 0x00, 0x56, 0x4C, 0x41, 0x53, 0x54, +0x00, 0x56, 0x4C, 0x41, 0x53, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, +0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCB, 0x32, 0x3A, 0x01, 0xDB, +0xF8, 0xF5, 0x00, 0x00, 0x00, 0x16, 0x4D, 0x6F, 0x73, 0x63, 0x6F, 0x77, 0x2B, 0x30, 0x37, 0x20, +0x2D, 0x20, 0x41, 0x6D, 0x75, 0x72, 0x20, 0x52, 0x69, 0x76, 0x65, 0x72, /* Asia/Yakutsk */ 0x50, 0x48, 0x50, 0x31, 0x01, 0x52, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0F, 0xA1, 0xDB, 0xEA, 0x70, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0F, 0xA1, 0xDB, 0xEA, 0x70, 0xB5, 0xA3, 0xC5, 0x00, 0x15, 0x27, 0x53, 0x70, 0x16, 0x18, 0x87, 0xE0, 0x17, 0x08, 0x86, 0xF0, 0x17, 0xF9, 0xBB, 0x60, 0x18, 0xE9, 0xBA, 0x70, 0x19, 0xDA, 0xEE, 0xE0, 0x1A, 0xCC, 0x3F, 0x70, 0x1B, 0xBC, 0x4C, 0x90, 0x1C, 0xAC, 0x3D, 0x90, 0x1D, 0x9C, 0x2E, 0x90, 0x1E, 0x8C, 0x1F, 0x90, @@ -9491,40 +9332,24 @@ const unsigned char timelib_timezone_db_data_builtin[262608] = { 0x40, 0x65, 0xB3, 0x10, 0x41, 0x83, 0xC8, 0x90, 0x42, 0x45, 0x95, 0x10, 0x43, 0x63, 0xAA, 0x90, 0x44, 0x25, 0x77, 0x10, 0x45, 0x43, 0x8C, 0x90, 0x46, 0x05, 0x59, 0x10, 0x47, 0x23, 0x6E, 0x90, 0x47, 0xEE, 0x75, 0x90, 0x49, 0x03, 0x50, 0x90, 0x49, 0xCE, 0x57, 0x90, 0x4A, 0xE3, 0x32, 0x90, -0x4B, 0xAE, 0x39, 0x90, 0x4C, 0xCC, 0x4F, 0x10, 0x4D, 0x8E, 0x1B, 0x90, 0x4E, 0xAC, 0x31, 0x10, -0x4F, 0x6D, 0xFD, 0x90, 0x50, 0x8C, 0x13, 0x10, 0x51, 0x57, 0x1A, 0x10, 0x52, 0x6B, 0xF5, 0x10, -0x53, 0x36, 0xFC, 0x10, 0x54, 0x4B, 0xD7, 0x10, 0x55, 0x16, 0xDE, 0x10, 0x56, 0x2B, 0xB9, 0x10, -0x56, 0xF6, 0xC0, 0x10, 0x58, 0x14, 0xD5, 0x90, 0x58, 0xD6, 0xA2, 0x10, 0x59, 0xF4, 0xB7, 0x90, -0x5A, 0xB6, 0x84, 0x10, 0x5B, 0xD4, 0x99, 0x90, 0x5C, 0x9F, 0xA0, 0x90, 0x5D, 0xB4, 0x7B, 0x90, -0x5E, 0x7F, 0x82, 0x90, 0x5F, 0x94, 0x5D, 0x90, 0x60, 0x5F, 0x64, 0x90, 0x61, 0x7D, 0x7A, 0x10, -0x62, 0x3F, 0x46, 0x90, 0x63, 0x5D, 0x5C, 0x10, 0x64, 0x1F, 0x28, 0x90, 0x65, 0x3D, 0x3E, 0x10, -0x66, 0x08, 0x45, 0x10, 0x67, 0x1D, 0x20, 0x10, 0x67, 0xE8, 0x27, 0x10, 0x68, 0xFD, 0x02, 0x10, -0x69, 0xC8, 0x09, 0x10, 0x6A, 0xDC, 0xE4, 0x10, 0x6B, 0xA7, 0xEB, 0x10, 0x6C, 0xC6, 0x00, 0x90, -0x6D, 0x87, 0xCD, 0x10, 0x6E, 0xA5, 0xE2, 0x90, 0x6F, 0x67, 0xAF, 0x10, 0x70, 0x85, 0xC4, 0x90, -0x71, 0x50, 0xCB, 0x90, 0x72, 0x65, 0xA6, 0x90, 0x73, 0x30, 0xAD, 0x90, 0x74, 0x45, 0x88, 0x90, -0x75, 0x10, 0x8F, 0x90, 0x76, 0x2E, 0xA5, 0x10, 0x76, 0xF0, 0x71, 0x90, 0x78, 0x0E, 0x87, 0x10, -0x78, 0xD0, 0x53, 0x90, 0x79, 0xEE, 0x69, 0x10, 0x7A, 0xB0, 0x35, 0x90, 0x7B, 0xCE, 0x4B, 0x10, -0x7C, 0x99, 0x52, 0x10, 0x7D, 0xAE, 0x2D, 0x10, 0x7E, 0x79, 0x34, 0x10, 0x7F, 0x8E, 0x0F, 0x10, -0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, -0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x04, 0x02, 0x03, 0x05, 0x04, 0x05, 0x04, 0x05, -0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, -0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, -0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, -0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, +0x4B, 0xAE, 0x39, 0x90, 0x4C, 0xCC, 0x4F, 0x10, 0x4D, 0x8E, 0x1B, 0x90, 0x01, 0x03, 0x02, 0x03, +0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, +0x05, 0x04, 0x06, 0x07, 0x04, 0x02, 0x03, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, -0x04, 0x05, 0x04, 0x05, 0x04, 0x00, 0x00, 0x79, 0x90, 0x00, 0x00, 0x00, 0x00, 0x70, 0x80, 0x00, -0x04, 0x00, 0x00, 0x8C, 0xA0, 0x01, 0x09, 0x00, 0x00, 0x7E, 0x90, 0x00, 0x04, 0x00, 0x00, 0x7E, -0x90, 0x00, 0x04, 0x00, 0x00, 0x8C, 0xA0, 0x01, 0x09, 0x00, 0x00, 0x7E, 0x90, 0x01, 0x09, 0x00, -0x00, 0x70, 0x80, 0x00, 0x04, 0x4C, 0x4D, 0x54, 0x00, 0x59, 0x41, 0x4B, 0x54, 0x00, 0x59, 0x41, -0x4B, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0xE7, 0xEF, 0x00, 0x01, 0xD8, 0x83, 0x8A, 0x00, 0x00, 0x00, 0x16, -0x4D, 0x6F, 0x73, 0x63, 0x6F, 0x77, 0x2B, 0x30, 0x36, 0x20, 0x2D, 0x20, 0x4C, 0x65, 0x6E, 0x61, -0x20, 0x52, 0x69, 0x76, 0x65, 0x72, +0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x08, 0x00, 0x00, 0x79, 0x90, +0x00, 0x00, 0x00, 0x00, 0x70, 0x80, 0x00, 0x04, 0x00, 0x00, 0x8C, 0xA0, 0x01, 0x09, 0x00, 0x00, +0x7E, 0x90, 0x00, 0x04, 0x00, 0x00, 0x7E, 0x90, 0x00, 0x04, 0x00, 0x00, 0x8C, 0xA0, 0x01, 0x09, +0x00, 0x00, 0x7E, 0x90, 0x01, 0x09, 0x00, 0x00, 0x70, 0x80, 0x00, 0x04, 0x00, 0x00, 0x8C, 0xA0, +0x00, 0x04, 0x4C, 0x4D, 0x54, 0x00, 0x59, 0x41, 0x4B, 0x54, 0x00, 0x59, 0x41, 0x4B, 0x53, 0x54, +0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0xE7, 0xEF, 0x00, 0x01, 0xD8, 0x83, 0x8A, 0x00, 0x00, 0x00, 0x16, 0x4D, +0x6F, 0x73, 0x63, 0x6F, 0x77, 0x2B, 0x30, 0x36, 0x20, 0x2D, 0x20, 0x4C, 0x65, 0x6E, 0x61, 0x20, +0x52, 0x69, 0x76, 0x65, 0x72, /* Asia/Yekaterinburg */ 0x50, 0x48, 0x50, 0x31, 0x01, 0x52, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x1A, 0xA1, 0x12, 0xAD, 0xF0, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x1A, 0xA1, 0x12, 0xAD, 0xF0, 0xB5, 0xA3, 0xFD, 0x40, 0x15, 0x27, 0x8B, 0xB0, 0x16, 0x18, 0xC0, 0x20, 0x17, 0x08, 0xBF, 0x30, 0x17, 0xF9, 0xF3, 0xA0, 0x18, 0xE9, 0xF2, 0xB0, 0x19, 0xDB, 0x27, 0x20, 0x1A, 0xCC, 0x77, 0xB0, 0x1B, 0xBC, 0x84, 0xD0, 0x1C, 0xAC, 0x75, 0xD0, 0x1D, 0x9C, 0x66, 0xD0, 0x1E, 0x8C, 0x57, 0xD0, @@ -9540,37 +9365,21 @@ const unsigned char timelib_timezone_db_data_builtin[262608] = { 0x40, 0x65, 0xEB, 0x50, 0x41, 0x84, 0x00, 0xD0, 0x42, 0x45, 0xCD, 0x50, 0x43, 0x63, 0xE2, 0xD0, 0x44, 0x25, 0xAF, 0x50, 0x45, 0x43, 0xC4, 0xD0, 0x46, 0x05, 0x91, 0x50, 0x47, 0x23, 0xA6, 0xD0, 0x47, 0xEE, 0xAD, 0xD0, 0x49, 0x03, 0x88, 0xD0, 0x49, 0xCE, 0x8F, 0xD0, 0x4A, 0xE3, 0x6A, 0xD0, -0x4B, 0xAE, 0x71, 0xD0, 0x4C, 0xCC, 0x87, 0x50, 0x4D, 0x8E, 0x53, 0xD0, 0x4E, 0xAC, 0x69, 0x50, -0x4F, 0x6E, 0x35, 0xD0, 0x50, 0x8C, 0x4B, 0x50, 0x51, 0x57, 0x52, 0x50, 0x52, 0x6C, 0x2D, 0x50, -0x53, 0x37, 0x34, 0x50, 0x54, 0x4C, 0x0F, 0x50, 0x55, 0x17, 0x16, 0x50, 0x56, 0x2B, 0xF1, 0x50, -0x56, 0xF6, 0xF8, 0x50, 0x58, 0x15, 0x0D, 0xD0, 0x58, 0xD6, 0xDA, 0x50, 0x59, 0xF4, 0xEF, 0xD0, -0x5A, 0xB6, 0xBC, 0x50, 0x5B, 0xD4, 0xD1, 0xD0, 0x5C, 0x9F, 0xD8, 0xD0, 0x5D, 0xB4, 0xB3, 0xD0, -0x5E, 0x7F, 0xBA, 0xD0, 0x5F, 0x94, 0x95, 0xD0, 0x60, 0x5F, 0x9C, 0xD0, 0x61, 0x7D, 0xB2, 0x50, -0x62, 0x3F, 0x7E, 0xD0, 0x63, 0x5D, 0x94, 0x50, 0x64, 0x1F, 0x60, 0xD0, 0x65, 0x3D, 0x76, 0x50, -0x66, 0x08, 0x7D, 0x50, 0x67, 0x1D, 0x58, 0x50, 0x67, 0xE8, 0x5F, 0x50, 0x68, 0xFD, 0x3A, 0x50, -0x69, 0xC8, 0x41, 0x50, 0x6A, 0xDD, 0x1C, 0x50, 0x6B, 0xA8, 0x23, 0x50, 0x6C, 0xC6, 0x38, 0xD0, -0x6D, 0x88, 0x05, 0x50, 0x6E, 0xA6, 0x1A, 0xD0, 0x6F, 0x67, 0xE7, 0x50, 0x70, 0x85, 0xFC, 0xD0, -0x71, 0x51, 0x03, 0xD0, 0x72, 0x65, 0xDE, 0xD0, 0x73, 0x30, 0xE5, 0xD0, 0x74, 0x45, 0xC0, 0xD0, -0x75, 0x10, 0xC7, 0xD0, 0x76, 0x2E, 0xDD, 0x50, 0x76, 0xF0, 0xA9, 0xD0, 0x78, 0x0E, 0xBF, 0x50, -0x78, 0xD0, 0x8B, 0xD0, 0x79, 0xEE, 0xA1, 0x50, 0x7A, 0xB0, 0x6D, 0xD0, 0x7B, 0xCE, 0x83, 0x50, -0x7C, 0x99, 0x8A, 0x50, 0x7D, 0xAE, 0x65, 0x50, 0x7E, 0x79, 0x6C, 0x50, 0x7F, 0x8E, 0x47, 0x50, -0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, -0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x0B, 0x08, 0x09, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, -0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, -0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, -0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, -0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, +0x4B, 0xAE, 0x71, 0xD0, 0x4C, 0xCC, 0x87, 0x50, 0x4D, 0x8E, 0x53, 0xD0, 0x01, 0x03, 0x02, 0x03, +0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, +0x05, 0x04, 0x06, 0x07, 0x0B, 0x08, 0x09, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, -0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x00, 0x00, 0x38, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x38, 0x40, 0x00, -0x04, 0x00, 0x00, 0x54, 0x60, 0x01, 0x09, 0x00, 0x00, 0x46, 0x50, 0x00, 0x04, 0x00, 0x00, 0x46, -0x50, 0x00, 0x04, 0x00, 0x00, 0x54, 0x60, 0x01, 0x09, 0x00, 0x00, 0x46, 0x50, 0x01, 0x09, 0x00, -0x00, 0x38, 0x40, 0x00, 0x04, 0x00, 0x00, 0x54, 0x60, 0x01, 0x0F, 0x00, 0x00, 0x46, 0x50, 0x00, -0x15, 0x00, 0x00, 0x54, 0x60, 0x01, 0x0F, 0x00, 0x00, 0x46, 0x50, 0x00, 0x15, 0x4C, 0x4D, 0x54, -0x00, 0x53, 0x56, 0x45, 0x54, 0x00, 0x53, 0x56, 0x45, 0x53, 0x54, 0x00, 0x59, 0x45, 0x4B, 0x53, -0x54, 0x00, 0x59, 0x45, 0x4B, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, -0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xE0, 0x13, 0x48, 0x01, 0x6F, 0x20, 0x60, 0x00, 0x00, 0x00, 0x11, 0x4D, 0x6F, 0x73, 0x63, 0x6F, -0x77, 0x2B, 0x30, 0x32, 0x20, 0x2D, 0x20, 0x55, 0x72, 0x61, 0x6C, 0x73, +0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0C, 0x00, 0x00, 0x38, 0xD0, +0x00, 0x00, 0x00, 0x00, 0x38, 0x40, 0x00, 0x04, 0x00, 0x00, 0x54, 0x60, 0x01, 0x09, 0x00, 0x00, +0x46, 0x50, 0x00, 0x04, 0x00, 0x00, 0x46, 0x50, 0x00, 0x04, 0x00, 0x00, 0x54, 0x60, 0x01, 0x09, +0x00, 0x00, 0x46, 0x50, 0x01, 0x09, 0x00, 0x00, 0x38, 0x40, 0x00, 0x04, 0x00, 0x00, 0x54, 0x60, +0x01, 0x0F, 0x00, 0x00, 0x46, 0x50, 0x00, 0x15, 0x00, 0x00, 0x54, 0x60, 0x01, 0x0F, 0x00, 0x00, +0x46, 0x50, 0x00, 0x15, 0x00, 0x00, 0x54, 0x60, 0x00, 0x15, 0x4C, 0x4D, 0x54, 0x00, 0x53, 0x56, +0x45, 0x54, 0x00, 0x53, 0x56, 0x45, 0x53, 0x54, 0x00, 0x59, 0x45, 0x4B, 0x53, 0x54, 0x00, 0x59, +0x45, 0x4B, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, +0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, +0x13, 0x48, 0x01, 0x6F, 0x20, 0x60, 0x00, 0x00, 0x00, 0x11, 0x4D, 0x6F, 0x73, 0x63, 0x6F, 0x77, +0x2B, 0x30, 0x32, 0x20, 0x2D, 0x20, 0x55, 0x72, 0x61, 0x6C, 0x73, /* Asia/Yerevan */ 0x50, 0x48, 0x50, 0x31, 0x01, 0x41, 0x4D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -10080,7 +9889,7 @@ const unsigned char timelib_timezone_db_data_builtin[262608] = { /* Atlantic/Stanley */ 0x50, 0x48, 0x50, 0x31, 0x01, 0x46, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x0D, 0x93, 0x44, 0x5F, 0x3C, +0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x0D, 0x93, 0x44, 0x5F, 0x3C, 0xC3, 0x4F, 0x5A, 0xC0, 0xC4, 0x36, 0x03, 0x30, 0xC5, 0x2F, 0x3C, 0xC0, 0xC6, 0x15, 0xE5, 0x30, 0xC7, 0x18, 0x59, 0x40, 0xC7, 0xFF, 0x01, 0xB0, 0xC8, 0xF8, 0x3B, 0x40, 0xC9, 0xDE, 0xE3, 0xB0, 0xCA, 0xD8, 0x1D, 0x40, 0xCB, 0xBE, 0xC5, 0xB0, 0xCC, 0xB7, 0xFF, 0x40, 0xCD, 0x36, 0x81, 0x30, @@ -10098,32 +9907,31 @@ const unsigned char timelib_timezone_db_data_builtin[262608] = { 0x42, 0x61, 0xED, 0x50, 0x43, 0x1A, 0x8D, 0x60, 0x44, 0x41, 0xCF, 0x50, 0x44, 0xFA, 0x6F, 0x60, 0x46, 0x21, 0xB1, 0x50, 0x46, 0xDA, 0x51, 0x60, 0x48, 0x0A, 0xCD, 0xD0, 0x48, 0xC3, 0x6D, 0xE0, 0x49, 0xEA, 0xAF, 0xD0, 0x4A, 0xA3, 0x4F, 0xE0, 0x4B, 0xCA, 0x91, 0xD0, 0x4C, 0x83, 0x31, 0xE0, -0x4D, 0xAA, 0x73, 0xD0, 0x4E, 0x63, 0x13, 0xE0, 0x4F, 0x8A, 0x55, 0xD0, 0x50, 0x42, 0xF5, 0xE0, -0x51, 0x73, 0x72, 0x50, 0x52, 0x22, 0xD7, 0xE0, 0x53, 0x53, 0x54, 0x50, 0x54, 0x0B, 0xF4, 0x60, -0x55, 0x33, 0x36, 0x50, 0x55, 0xEB, 0xD6, 0x60, 0x57, 0x13, 0x18, 0x50, 0x57, 0xCB, 0xB8, 0x60, -0x58, 0xF2, 0xFA, 0x50, 0x59, 0xAB, 0x9A, 0x60, 0x5A, 0xD2, 0xDC, 0x50, 0x5B, 0x8B, 0x7C, 0x60, -0x5C, 0xBB, 0xF8, 0xD0, 0x5D, 0x6B, 0x5E, 0x60, 0x5E, 0x9B, 0xDA, 0xD0, 0x5F, 0x54, 0x7A, 0xE0, -0x60, 0x7B, 0xBC, 0xD0, 0x61, 0x34, 0x5C, 0xE0, 0x62, 0x5B, 0x9E, 0xD0, 0x63, 0x14, 0x3E, 0xE0, -0x64, 0x3B, 0x80, 0xD0, 0x64, 0xF4, 0x20, 0xE0, 0x66, 0x24, 0x9D, 0x50, 0x66, 0xD4, 0x02, 0xE0, -0x68, 0x04, 0x7F, 0x50, 0x68, 0xBD, 0x1F, 0x60, 0x69, 0xE4, 0x61, 0x50, 0x6A, 0x9D, 0x01, 0x60, -0x6B, 0xC4, 0x43, 0x50, 0x6C, 0x7C, 0xE3, 0x60, 0x6D, 0xA4, 0x25, 0x50, 0x6E, 0x5C, 0xC5, 0x60, -0x6F, 0x84, 0x07, 0x50, 0x70, 0x3C, 0xA7, 0x60, 0x71, 0x6D, 0x23, 0xD0, 0x72, 0x1C, 0x89, 0x60, -0x73, 0x4D, 0x05, 0xD0, 0x74, 0x05, 0xA5, 0xE0, 0x75, 0x2C, 0xE7, 0xD0, 0x75, 0xE5, 0x87, 0xE0, -0x77, 0x0C, 0xC9, 0xD0, 0x77, 0xC5, 0x69, 0xE0, 0x78, 0xEC, 0xAB, 0xD0, 0x79, 0xA5, 0x4B, 0xE0, -0x7A, 0xCC, 0x8D, 0xD0, 0x7B, 0x85, 0x2D, 0xE0, 0x7C, 0xB5, 0xAA, 0x50, 0x7D, 0x6E, 0x4A, 0x60, -0x7E, 0x95, 0x8C, 0x50, 0x7F, 0x4E, 0x2C, 0x60, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, -0x02, 0x01, 0x02, 0x01, 0x02, 0x04, 0x03, 0x04, 0x03, 0x04, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, +0x4F, 0x8A, 0x55, 0xD0, 0x50, 0x42, 0xF5, 0xE0, 0x51, 0x73, 0x72, 0x50, 0x52, 0x22, 0xD7, 0xE0, +0x53, 0x53, 0x54, 0x50, 0x54, 0x0B, 0xF4, 0x60, 0x55, 0x33, 0x36, 0x50, 0x55, 0xEB, 0xD6, 0x60, +0x57, 0x13, 0x18, 0x50, 0x57, 0xCB, 0xB8, 0x60, 0x58, 0xF2, 0xFA, 0x50, 0x59, 0xAB, 0x9A, 0x60, +0x5A, 0xD2, 0xDC, 0x50, 0x5B, 0x8B, 0x7C, 0x60, 0x5C, 0xBB, 0xF8, 0xD0, 0x5D, 0x6B, 0x5E, 0x60, +0x5E, 0x9B, 0xDA, 0xD0, 0x5F, 0x54, 0x7A, 0xE0, 0x60, 0x7B, 0xBC, 0xD0, 0x61, 0x34, 0x5C, 0xE0, +0x62, 0x5B, 0x9E, 0xD0, 0x63, 0x14, 0x3E, 0xE0, 0x64, 0x3B, 0x80, 0xD0, 0x64, 0xF4, 0x20, 0xE0, +0x66, 0x24, 0x9D, 0x50, 0x66, 0xD4, 0x02, 0xE0, 0x68, 0x04, 0x7F, 0x50, 0x68, 0xBD, 0x1F, 0x60, +0x69, 0xE4, 0x61, 0x50, 0x6A, 0x9D, 0x01, 0x60, 0x6B, 0xC4, 0x43, 0x50, 0x6C, 0x7C, 0xE3, 0x60, +0x6D, 0xA4, 0x25, 0x50, 0x6E, 0x5C, 0xC5, 0x60, 0x6F, 0x84, 0x07, 0x50, 0x70, 0x3C, 0xA7, 0x60, +0x71, 0x6D, 0x23, 0xD0, 0x72, 0x1C, 0x89, 0x60, 0x73, 0x4D, 0x05, 0xD0, 0x74, 0x05, 0xA5, 0xE0, +0x75, 0x2C, 0xE7, 0xD0, 0x75, 0xE5, 0x87, 0xE0, 0x77, 0x0C, 0xC9, 0xD0, 0x77, 0xC5, 0x69, 0xE0, +0x78, 0xEC, 0xAB, 0xD0, 0x79, 0xA5, 0x4B, 0xE0, 0x7A, 0xCC, 0x8D, 0xD0, 0x7B, 0x85, 0x2D, 0xE0, +0x7C, 0xB5, 0xAA, 0x50, 0x7D, 0x6E, 0x4A, 0x60, 0x7E, 0x95, 0x8C, 0x50, 0x7F, 0x4E, 0x2C, 0x60, +0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x04, 0x03, 0x04, +0x03, 0x04, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, -0x01, 0x02, 0x01, 0xFF, 0xFF, 0xC9, 0xC4, 0x00, 0x00, 0xFF, 0xFF, 0xD5, 0xD0, 0x01, 0x04, 0xFF, -0xFF, 0xC7, 0xC0, 0x00, 0x09, 0xFF, 0xFF, 0xE3, 0xE0, 0x01, 0x04, 0xFF, 0xFF, 0xD5, 0xD0, 0x00, -0x09, 0x53, 0x4D, 0x54, 0x00, 0x46, 0x4B, 0x53, 0x54, 0x00, 0x46, 0x4B, 0x54, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, 0x70, 0xEF, 0x00, 0xBA, 0x62, 0xD8, -0x00, 0x00, 0x00, 0x00, +0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0xFF, 0xFF, 0xC9, 0xC4, 0x00, 0x00, 0xFF, +0xFF, 0xD5, 0xD0, 0x01, 0x04, 0xFF, 0xFF, 0xC7, 0xC0, 0x00, 0x09, 0xFF, 0xFF, 0xE3, 0xE0, 0x01, +0x04, 0xFF, 0xFF, 0xD5, 0xD0, 0x00, 0x09, 0x53, 0x4D, 0x54, 0x00, 0x46, 0x4B, 0x53, 0x54, 0x00, +0x46, 0x4B, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, +0x70, 0xEF, 0x00, 0xBA, 0x62, 0xD8, 0x00, 0x00, 0x00, 0x00, /* Atlantic/St_Helena */ 0x50, 0x48, 0x50, 0x31, 0x01, 0x53, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -11758,8 +11566,8 @@ const unsigned char timelib_timezone_db_data_builtin[262608] = { 0x3F, 0x88, 0xD1, 0xC0, 0x40, 0x53, 0xCA, 0xB0, 0x41, 0x68, 0xB3, 0xC0, 0x42, 0x33, 0xAC, 0xB0, 0x43, 0x48, 0x95, 0xC0, 0x44, 0x13, 0x8E, 0xB0, 0x45, 0x31, 0xB2, 0x40, 0x45, 0xF3, 0x70, 0xB0, 0x47, 0x11, 0x94, 0x40, 0x47, 0xEF, 0x02, 0x30, 0x48, 0xF1, 0x76, 0x40, 0x49, 0xBC, 0x6F, 0x30, -0x4A, 0xD1, 0x58, 0x40, 0x4B, 0xB8, 0x00, 0xB0, 0x4C, 0xB1, 0x3A, 0x40, 0x4D, 0x97, 0xE2, 0xB0, -0x4E, 0x91, 0x1C, 0x40, 0x4F, 0x5C, 0x15, 0x30, 0x50, 0x7A, 0x38, 0xC0, 0x51, 0x3B, 0xF7, 0x30, +0x4A, 0xD1, 0x58, 0x40, 0x4B, 0xB8, 0x00, 0xB0, 0x4C, 0xB1, 0x3A, 0x40, 0x4D, 0xC6, 0x07, 0x30, +0x4E, 0x50, 0x82, 0xC0, 0x4F, 0x5C, 0x15, 0x30, 0x50, 0x7A, 0x38, 0xC0, 0x51, 0x3B, 0xF7, 0x30, 0x52, 0x5A, 0x1A, 0xC0, 0x53, 0x1B, 0xD9, 0x30, 0x54, 0x39, 0xFC, 0xC0, 0x55, 0x04, 0xF5, 0xB0, 0x56, 0x19, 0xDE, 0xC0, 0x56, 0xE4, 0xD7, 0xB0, 0x57, 0xF9, 0xC0, 0xC0, 0x58, 0xC4, 0xB9, 0xB0, 0x59, 0xE2, 0xDD, 0x40, 0x5A, 0xA4, 0x9B, 0xB0, 0x5B, 0xC2, 0xBF, 0x40, 0x5C, 0x84, 0x7D, 0xB0, @@ -11815,7 +11623,7 @@ const unsigned char timelib_timezone_db_data_builtin[262608] = { 0x42, 0x33, 0xAC, 0xB0, 0x43, 0x48, 0x95, 0xC0, 0x44, 0x13, 0x8E, 0xB0, 0x45, 0x31, 0xB2, 0x40, 0x45, 0xF3, 0x70, 0xB0, 0x47, 0x11, 0x94, 0x40, 0x47, 0xEF, 0x02, 0x30, 0x48, 0xF1, 0x76, 0x40, 0x49, 0xBC, 0x6F, 0x30, 0x4A, 0xD1, 0x58, 0x40, 0x4B, 0xB8, 0x00, 0xB0, 0x4C, 0xB1, 0x3A, 0x40, -0x4D, 0x97, 0xE2, 0xB0, 0x4E, 0x91, 0x1C, 0x40, 0x4F, 0x5C, 0x15, 0x30, 0x50, 0x7A, 0x38, 0xC0, +0x4D, 0xC6, 0x07, 0x30, 0x4E, 0x50, 0x82, 0xC0, 0x4F, 0x5C, 0x15, 0x30, 0x50, 0x7A, 0x38, 0xC0, 0x51, 0x3B, 0xF7, 0x30, 0x52, 0x5A, 0x1A, 0xC0, 0x53, 0x1B, 0xD9, 0x30, 0x54, 0x39, 0xFC, 0xC0, 0x55, 0x04, 0xF5, 0xB0, 0x56, 0x19, 0xDE, 0xC0, 0x56, 0xE4, 0xD7, 0xB0, 0x57, 0xF9, 0xC0, 0xC0, 0x58, 0xC4, 0xB9, 0xB0, 0x59, 0xE2, 0xDD, 0x40, 0x5A, 0xA4, 0x9B, 0xB0, 0x5B, 0xC2, 0xBF, 0x40, @@ -12007,7 +11815,7 @@ const unsigned char timelib_timezone_db_data_builtin[262608] = { /* Egypt */ 0x50, 0x48, 0x50, 0x31, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0xB0, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x09, 0xC8, 0x93, 0xB4, 0xE0, +0x00, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x09, 0xC8, 0x93, 0xB4, 0xE0, 0xC8, 0xFA, 0x7B, 0xD0, 0xC9, 0xFC, 0xEF, 0xE0, 0xCA, 0xC7, 0xE8, 0xD0, 0xCB, 0xCB, 0xAE, 0x60, 0xCC, 0xDF, 0x29, 0xD0, 0xCD, 0xAC, 0xE1, 0xE0, 0xCE, 0xC6, 0xF4, 0xD0, 0xCF, 0x8F, 0x66, 0xE0, 0xD0, 0xA9, 0x79, 0xD0, 0xD1, 0x84, 0x60, 0xE0, 0xD2, 0x8A, 0xAD, 0x50, 0xE8, 0x36, 0x63, 0x60, @@ -12038,35 +11846,18 @@ const unsigned char timelib_timezone_db_data_builtin[262608] = { 0x43, 0x3C, 0x55, 0xD0, 0x44, 0x51, 0x3E, 0xE0, 0x45, 0x12, 0xFD, 0x50, 0x46, 0x31, 0x20, 0xE0, 0x46, 0xE0, 0x6A, 0x50, 0x48, 0x11, 0x02, 0xE0, 0x48, 0xB7, 0x11, 0xD0, 0x49, 0xF0, 0xE4, 0xE0, 0x4A, 0x8D, 0xB9, 0x50, 0x4B, 0xDA, 0x01, 0x60, 0x4C, 0x61, 0xBD, 0xD0, 0x4C, 0x89, 0x58, 0xE0, -0x4C, 0xA4, 0xFA, 0x50, 0x4D, 0xB9, 0xE3, 0x60, 0x4E, 0x84, 0xDC, 0x50, 0x4F, 0x99, 0xC5, 0x60, -0x50, 0x64, 0xBE, 0x50, 0x51, 0x79, 0xA7, 0x60, 0x52, 0x44, 0xA0, 0x50, 0x53, 0x59, 0x89, 0x60, -0x54, 0x24, 0x82, 0x50, 0x55, 0x39, 0x6B, 0x60, 0x56, 0x04, 0x64, 0x50, 0x57, 0x22, 0x87, 0xE0, -0x57, 0xED, 0x80, 0xD0, 0x59, 0x02, 0x69, 0xE0, 0x59, 0xCD, 0x62, 0xD0, 0x5A, 0xE2, 0x4B, 0xE0, -0x5B, 0xAD, 0x44, 0xD0, 0x5C, 0xC2, 0x2D, 0xE0, 0x5D, 0x8D, 0x26, 0xD0, 0x5E, 0xA2, 0x0F, 0xE0, -0x5F, 0x6D, 0x08, 0xD0, 0x60, 0x8B, 0x2C, 0x60, 0x61, 0x56, 0x25, 0x50, 0x62, 0x6B, 0x0E, 0x60, -0x63, 0x36, 0x07, 0x50, 0x64, 0x4A, 0xF0, 0x60, 0x65, 0x15, 0xE9, 0x50, 0x66, 0x2A, 0xD2, 0x60, -0x66, 0xF5, 0xCB, 0x50, 0x68, 0x0A, 0xB4, 0x60, 0x68, 0xD5, 0xAD, 0x50, 0x69, 0xEA, 0x96, 0x60, -0x6A, 0xB5, 0x8F, 0x50, 0x6B, 0xD3, 0xB2, 0xE0, 0x6C, 0x9E, 0xAB, 0xD0, 0x6D, 0xB3, 0x94, 0xE0, -0x6E, 0x7E, 0x8D, 0xD0, 0x6F, 0x93, 0x76, 0xE0, 0x70, 0x5E, 0x6F, 0xD0, 0x71, 0x73, 0x58, 0xE0, -0x72, 0x3E, 0x51, 0xD0, 0x73, 0x53, 0x3A, 0xE0, 0x74, 0x1E, 0x33, 0xD0, 0x75, 0x3C, 0x57, 0x60, -0x76, 0x07, 0x50, 0x50, 0x77, 0x1C, 0x39, 0x60, 0x77, 0xE7, 0x32, 0x50, 0x78, 0xFC, 0x1B, 0x60, -0x79, 0xC7, 0x14, 0x50, 0x7A, 0xDB, 0xFD, 0x60, 0x7B, 0xA6, 0xF6, 0x50, 0x7C, 0xBB, 0xDF, 0x60, -0x7D, 0x86, 0xD8, 0x50, 0x7E, 0x9B, 0xC1, 0x60, 0x7F, 0x66, 0xBA, 0x50, 0x00, 0x01, 0x00, 0x01, +0x4C, 0xA4, 0xFA, 0x50, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, -0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, -0x00, 0x01, 0x00, 0x01, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, -0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, -0x02, 0x03, 0x02, 0x01, 0x00, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, -0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, +0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, -0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x00, 0x00, 0x2A, 0x30, -0x01, 0x00, 0x00, 0x00, 0x1C, 0x20, 0x00, 0x05, 0x00, 0x00, 0x2A, 0x30, 0x01, 0x00, 0x00, 0x00, -0x1C, 0x20, 0x00, 0x05, 0x45, 0x45, 0x53, 0x54, 0x00, 0x45, 0x45, 0x54, 0x00, 0x00, 0x00, 0x01, -0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0x54, 0x40, 0x01, 0x12, 0xA8, 0x80, 0x00, 0x00, 0x00, -0x00, +0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x01, 0x00, 0x03, 0x00, 0x00, +0x2A, 0x30, 0x01, 0x00, 0x00, 0x00, 0x1C, 0x20, 0x00, 0x05, 0x00, 0x00, 0x2A, 0x30, 0x01, 0x00, +0x00, 0x00, 0x1C, 0x20, 0x00, 0x05, 0x45, 0x45, 0x53, 0x54, 0x00, 0x45, 0x45, 0x54, 0x00, 0x00, +0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0x54, 0x40, 0x01, 0x12, 0xA8, 0x80, 0x00, +0x00, 0x00, 0x00, /* Eire */ 0x50, 0x48, 0x50, 0x31, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -13688,8 +13479,8 @@ const unsigned char timelib_timezone_db_data_builtin[262608] = { /* Europe/Kaliningrad */ 0x50, 0x48, 0x50, 0x31, 0x01, 0x52, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x1A, 0x9B, 0x0C, 0x17, 0x60, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x4D, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x1A, 0x9B, 0x0C, 0x17, 0x60, 0x9B, 0xD5, 0xDA, 0xF0, 0x9C, 0xD9, 0xAE, 0x90, 0x9D, 0xA4, 0xB5, 0x90, 0x9E, 0xB9, 0x90, 0x90, 0x9F, 0x84, 0x97, 0x90, 0xC8, 0x09, 0x71, 0x90, 0xCC, 0xE7, 0x4B, 0x10, 0xCD, 0xA9, 0x17, 0x90, 0xCE, 0xA2, 0x43, 0x10, 0xCF, 0x92, 0x34, 0x10, 0xD0, 0x82, 0x25, 0x10, 0xD0, 0xFA, 0x01, 0x70, @@ -13709,39 +13500,23 @@ const unsigned char timelib_timezone_db_data_builtin[262608] = { 0x43, 0x64, 0x0D, 0x00, 0x44, 0x25, 0xD9, 0x80, 0x45, 0x43, 0xEF, 0x00, 0x46, 0x05, 0xBB, 0x80, 0x47, 0x23, 0xD1, 0x00, 0x47, 0xEE, 0xD8, 0x00, 0x49, 0x03, 0xB3, 0x00, 0x49, 0xCE, 0xBA, 0x00, 0x4A, 0xE3, 0x95, 0x00, 0x4B, 0xAE, 0x9C, 0x00, 0x4C, 0xCC, 0xB1, 0x80, 0x4D, 0x8E, 0x7E, 0x00, -0x4E, 0xAC, 0x93, 0x80, 0x4F, 0x6E, 0x60, 0x00, 0x50, 0x8C, 0x75, 0x80, 0x51, 0x57, 0x7C, 0x80, -0x52, 0x6C, 0x57, 0x80, 0x53, 0x37, 0x5E, 0x80, 0x54, 0x4C, 0x39, 0x80, 0x55, 0x17, 0x40, 0x80, -0x56, 0x2C, 0x1B, 0x80, 0x56, 0xF7, 0x22, 0x80, 0x58, 0x15, 0x38, 0x00, 0x58, 0xD7, 0x04, 0x80, -0x59, 0xF5, 0x1A, 0x00, 0x5A, 0xB6, 0xE6, 0x80, 0x5B, 0xD4, 0xFC, 0x00, 0x5C, 0xA0, 0x03, 0x00, -0x5D, 0xB4, 0xDE, 0x00, 0x5E, 0x7F, 0xE5, 0x00, 0x5F, 0x94, 0xC0, 0x00, 0x60, 0x5F, 0xC7, 0x00, -0x61, 0x7D, 0xDC, 0x80, 0x62, 0x3F, 0xA9, 0x00, 0x63, 0x5D, 0xBE, 0x80, 0x64, 0x1F, 0x8B, 0x00, -0x65, 0x3D, 0xA0, 0x80, 0x66, 0x08, 0xA7, 0x80, 0x67, 0x1D, 0x82, 0x80, 0x67, 0xE8, 0x89, 0x80, -0x68, 0xFD, 0x64, 0x80, 0x69, 0xC8, 0x6B, 0x80, 0x6A, 0xDD, 0x46, 0x80, 0x6B, 0xA8, 0x4D, 0x80, -0x6C, 0xC6, 0x63, 0x00, 0x6D, 0x88, 0x2F, 0x80, 0x6E, 0xA6, 0x45, 0x00, 0x6F, 0x68, 0x11, 0x80, -0x70, 0x86, 0x27, 0x00, 0x71, 0x51, 0x2E, 0x00, 0x72, 0x66, 0x09, 0x00, 0x73, 0x31, 0x10, 0x00, -0x74, 0x45, 0xEB, 0x00, 0x75, 0x10, 0xF2, 0x00, 0x76, 0x2F, 0x07, 0x80, 0x76, 0xF0, 0xD4, 0x00, -0x78, 0x0E, 0xE9, 0x80, 0x78, 0xD0, 0xB6, 0x00, 0x79, 0xEE, 0xCB, 0x80, 0x7A, 0xB0, 0x98, 0x00, -0x7B, 0xCE, 0xAD, 0x80, 0x7C, 0x99, 0xB4, 0x80, 0x7D, 0xAE, 0x8F, 0x80, 0x7E, 0x79, 0x96, 0x80, -0x7F, 0x8E, 0x71, 0x80, 0x00, 0x01, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, -0x05, 0x04, 0x05, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x08, 0x09, 0x08, 0x09, 0x08, -0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x0A, 0x0B, 0x0C, 0x0D, 0x0A, 0x0B, 0x0A, 0x0B, -0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, +0x00, 0x01, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x05, 0x04, 0x05, 0x07, +0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, +0x09, 0x08, 0x09, 0x08, 0x0A, 0x0B, 0x0C, 0x0D, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, -0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, -0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, -0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, -0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x00, 0x00, 0x1C, 0x20, 0x01, 0x00, 0x00, 0x00, 0x0E, 0x10, -0x00, 0x05, 0x00, 0x00, 0x1C, 0x20, 0x01, 0x00, 0x00, 0x00, 0x0E, 0x10, 0x00, 0x05, 0x00, 0x00, -0x2A, 0x30, 0x01, 0x00, 0x00, 0x00, 0x1C, 0x20, 0x00, 0x05, 0x00, 0x00, 0x38, 0x40, 0x01, 0x09, -0x00, 0x00, 0x2A, 0x30, 0x00, 0x0D, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x0D, 0x00, 0x00, 0x38, 0x40, -0x01, 0x09, 0x00, 0x00, 0x2A, 0x30, 0x01, 0x11, 0x00, 0x00, 0x1C, 0x20, 0x00, 0x16, 0x00, 0x00, -0x2A, 0x30, 0x01, 0x11, 0x00, 0x00, 0x1C, 0x20, 0x00, 0x16, 0x43, 0x45, 0x53, 0x54, 0x00, 0x43, -0x45, 0x54, 0x00, 0x4D, 0x53, 0x44, 0x00, 0x4D, 0x53, 0x4B, 0x00, 0x45, 0x45, 0x53, 0x54, 0x00, -0x45, 0x45, 0x54, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, +0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0A, 0x0B, 0x0E, 0x00, 0x00, 0x1C, +0x20, 0x01, 0x00, 0x00, 0x00, 0x0E, 0x10, 0x00, 0x05, 0x00, 0x00, 0x1C, 0x20, 0x01, 0x00, 0x00, +0x00, 0x0E, 0x10, 0x00, 0x05, 0x00, 0x00, 0x2A, 0x30, 0x01, 0x00, 0x00, 0x00, 0x1C, 0x20, 0x00, +0x05, 0x00, 0x00, 0x38, 0x40, 0x01, 0x09, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x0D, 0x00, 0x00, 0x2A, +0x30, 0x00, 0x0D, 0x00, 0x00, 0x38, 0x40, 0x01, 0x09, 0x00, 0x00, 0x2A, 0x30, 0x01, 0x11, 0x00, +0x00, 0x1C, 0x20, 0x00, 0x16, 0x00, 0x00, 0x2A, 0x30, 0x01, 0x11, 0x00, 0x00, 0x1C, 0x20, 0x00, +0x16, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x16, 0x43, 0x45, 0x53, 0x54, 0x00, 0x43, 0x45, 0x54, 0x00, +0x4D, 0x53, 0x44, 0x00, 0x4D, 0x53, 0x4B, 0x00, 0x45, 0x45, 0x53, 0x54, 0x00, 0x45, 0x45, 0x54, +0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0xDC, 0xD1, 0xF2, 0x01, 0x31, 0xF0, 0x50, 0x00, 0x00, 0x00, 0x17, 0x4D, 0x6F, 0x73, 0x63, -0x6F, 0x77, 0x2D, 0x30, 0x31, 0x20, 0x2D, 0x20, 0x4B, 0x61, 0x6C, 0x69, 0x6E, 0x69, 0x6E, 0x67, -0x72, 0x61, 0x64, +0xDC, 0xD1, 0xF2, 0x01, 0x31, 0xF0, 0x50, 0x00, 0x00, 0x00, 0x17, 0x4D, 0x6F, 0x73, 0x63, 0x6F, +0x77, 0x2D, 0x30, 0x31, 0x20, 0x2D, 0x20, 0x4B, 0x61, 0x6C, 0x69, 0x6E, 0x69, 0x6E, 0x67, 0x72, +0x61, 0x64, /* Europe/Kiev */ 0x50, 0x48, 0x50, 0x31, 0x01, 0x55, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -14377,8 +14152,8 @@ const unsigned char timelib_timezone_db_data_builtin[262608] = { /* Europe/Moscow */ 0x50, 0x48, 0x50, 0x31, 0x01, 0x52, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x1E, 0x9B, 0x5F, 0x1E, 0xD8, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x1E, 0x9B, 0x5F, 0x1E, 0xD8, 0x9D, 0x3E, 0xF2, 0x98, 0x9E, 0x2A, 0xEF, 0x18, 0x9E, 0xF7, 0x39, 0x88, 0x9F, 0x84, 0x58, 0x18, 0xA0, 0xD8, 0x6D, 0x08, 0xA1, 0x00, 0x16, 0x28, 0xA1, 0x3C, 0xA6, 0x40, 0xA4, 0x10, 0x6D, 0xC0, 0xA4, 0x3D, 0x32, 0xB0, 0xA5, 0x15, 0x68, 0xB0, 0xA5, 0x3D, 0x03, 0xC0, 0xA7, 0x1E, 0x45, 0x50, @@ -14397,39 +14172,23 @@ const unsigned char timelib_timezone_db_data_builtin[262608] = { 0x40, 0x66, 0x07, 0x70, 0x41, 0x84, 0x1C, 0xF0, 0x42, 0x45, 0xE9, 0x70, 0x43, 0x63, 0xFE, 0xF0, 0x44, 0x25, 0xCB, 0x70, 0x45, 0x43, 0xE0, 0xF0, 0x46, 0x05, 0xAD, 0x70, 0x47, 0x23, 0xC2, 0xF0, 0x47, 0xEE, 0xC9, 0xF0, 0x49, 0x03, 0xA4, 0xF0, 0x49, 0xCE, 0xAB, 0xF0, 0x4A, 0xE3, 0x86, 0xF0, -0x4B, 0xAE, 0x8D, 0xF0, 0x4C, 0xCC, 0xA3, 0x70, 0x4D, 0x8E, 0x6F, 0xF0, 0x4E, 0xAC, 0x85, 0x70, -0x4F, 0x6E, 0x51, 0xF0, 0x50, 0x8C, 0x67, 0x70, 0x51, 0x57, 0x6E, 0x70, 0x52, 0x6C, 0x49, 0x70, -0x53, 0x37, 0x50, 0x70, 0x54, 0x4C, 0x2B, 0x70, 0x55, 0x17, 0x32, 0x70, 0x56, 0x2C, 0x0D, 0x70, -0x56, 0xF7, 0x14, 0x70, 0x58, 0x15, 0x29, 0xF0, 0x58, 0xD6, 0xF6, 0x70, 0x59, 0xF5, 0x0B, 0xF0, -0x5A, 0xB6, 0xD8, 0x70, 0x5B, 0xD4, 0xED, 0xF0, 0x5C, 0x9F, 0xF4, 0xF0, 0x5D, 0xB4, 0xCF, 0xF0, -0x5E, 0x7F, 0xD6, 0xF0, 0x5F, 0x94, 0xB1, 0xF0, 0x60, 0x5F, 0xB8, 0xF0, 0x61, 0x7D, 0xCE, 0x70, -0x62, 0x3F, 0x9A, 0xF0, 0x63, 0x5D, 0xB0, 0x70, 0x64, 0x1F, 0x7C, 0xF0, 0x65, 0x3D, 0x92, 0x70, -0x66, 0x08, 0x99, 0x70, 0x67, 0x1D, 0x74, 0x70, 0x67, 0xE8, 0x7B, 0x70, 0x68, 0xFD, 0x56, 0x70, -0x69, 0xC8, 0x5D, 0x70, 0x6A, 0xDD, 0x38, 0x70, 0x6B, 0xA8, 0x3F, 0x70, 0x6C, 0xC6, 0x54, 0xF0, -0x6D, 0x88, 0x21, 0x70, 0x6E, 0xA6, 0x36, 0xF0, 0x6F, 0x68, 0x03, 0x70, 0x70, 0x86, 0x18, 0xF0, -0x71, 0x51, 0x1F, 0xF0, 0x72, 0x65, 0xFA, 0xF0, 0x73, 0x31, 0x01, 0xF0, 0x74, 0x45, 0xDC, 0xF0, -0x75, 0x10, 0xE3, 0xF0, 0x76, 0x2E, 0xF9, 0x70, 0x76, 0xF0, 0xC5, 0xF0, 0x78, 0x0E, 0xDB, 0x70, -0x78, 0xD0, 0xA7, 0xF0, 0x79, 0xEE, 0xBD, 0x70, 0x7A, 0xB0, 0x89, 0xF0, 0x7B, 0xCE, 0x9F, 0x70, -0x7C, 0x99, 0xA6, 0x70, 0x7D, 0xAE, 0x81, 0x70, 0x7E, 0x79, 0x88, 0x70, 0x7F, 0x8E, 0x63, 0x70, -0x02, 0x01, 0x02, 0x03, 0x01, 0x03, 0x05, 0x04, 0x05, 0x06, 0x05, 0x04, 0x07, 0x04, 0x05, 0x04, -0x05, 0x04, 0x05, 0x04, 0x05, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, -0x09, 0x08, 0x0A, 0x0B, 0x08, 0x05, 0x04, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, -0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, -0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, -0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, +0x4B, 0xAE, 0x8D, 0xF0, 0x4C, 0xCC, 0xA3, 0x70, 0x4D, 0x8E, 0x6F, 0xF0, 0x02, 0x01, 0x02, 0x03, +0x01, 0x03, 0x05, 0x04, 0x05, 0x06, 0x05, 0x04, 0x07, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, +0x05, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x0A, 0x0B, +0x08, 0x05, 0x04, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, -0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, -0x08, 0x00, 0x00, 0x23, 0x28, 0x00, 0x00, 0x00, 0x00, 0x31, 0x68, 0x01, 0x04, 0x00, 0x00, 0x23, -0x58, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x78, 0x01, 0x08, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x0D, 0x00, -0x00, 0x38, 0x40, 0x01, 0x11, 0x00, 0x00, 0x46, 0x50, 0x01, 0x11, 0x00, 0x00, 0x1C, 0x20, 0x00, -0x15, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x0D, 0x00, 0x00, 0x38, 0x40, 0x01, 0x11, 0x00, 0x00, 0x2A, -0x30, 0x01, 0x19, 0x00, 0x00, 0x1C, 0x20, 0x00, 0x15, 0x4D, 0x4D, 0x54, 0x00, 0x4D, 0x53, 0x54, -0x00, 0x4D, 0x44, 0x53, 0x54, 0x00, 0x4D, 0x53, 0x4B, 0x00, 0x4D, 0x53, 0x44, 0x00, 0x45, 0x45, -0x54, 0x00, 0x45, 0x45, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xDE, 0x65, 0x98, 0x01, 0x4C, 0x01, 0x7D, 0x00, 0x00, 0x00, 0x17, 0x4D, 0x6F, 0x73, 0x63, 0x6F, -0x77, 0x2B, 0x30, 0x30, 0x20, 0x2D, 0x20, 0x77, 0x65, 0x73, 0x74, 0x20, 0x52, 0x75, 0x73, 0x73, -0x69, 0x61, +0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x0C, 0x00, 0x00, 0x23, 0x28, 0x00, 0x00, 0x00, 0x00, +0x31, 0x68, 0x01, 0x04, 0x00, 0x00, 0x23, 0x58, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x78, 0x01, 0x08, +0x00, 0x00, 0x2A, 0x30, 0x00, 0x0D, 0x00, 0x00, 0x38, 0x40, 0x01, 0x11, 0x00, 0x00, 0x46, 0x50, +0x01, 0x11, 0x00, 0x00, 0x1C, 0x20, 0x00, 0x15, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x0D, 0x00, 0x00, +0x38, 0x40, 0x01, 0x11, 0x00, 0x00, 0x2A, 0x30, 0x01, 0x19, 0x00, 0x00, 0x1C, 0x20, 0x00, 0x15, +0x00, 0x00, 0x38, 0x40, 0x00, 0x0D, 0x4D, 0x4D, 0x54, 0x00, 0x4D, 0x53, 0x54, 0x00, 0x4D, 0x44, +0x53, 0x54, 0x00, 0x4D, 0x53, 0x4B, 0x00, 0x4D, 0x53, 0x44, 0x00, 0x45, 0x45, 0x54, 0x00, 0x45, +0x45, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, +0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDE, +0x65, 0x98, 0x01, 0x4C, 0x01, 0x7D, 0x00, 0x00, 0x00, 0x17, 0x4D, 0x6F, 0x73, 0x63, 0x6F, 0x77, +0x2B, 0x30, 0x30, 0x20, 0x2D, 0x20, 0x77, 0x65, 0x73, 0x74, 0x20, 0x52, 0x75, 0x73, 0x73, 0x69, +0x61, /* Europe/Nicosia */ 0x50, 0x48, 0x50, 0x31, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -14827,7 +14586,7 @@ const unsigned char timelib_timezone_db_data_builtin[262608] = { /* Europe/Samara */ 0x50, 0x48, 0x50, 0x31, 0x01, 0x52, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1A, 0xA1, 0x00, 0x26, 0x9C, +0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1A, 0xA1, 0x00, 0x26, 0x9C, 0xB5, 0xA4, 0x0B, 0x50, 0xBE, 0x4C, 0x26, 0xC0, 0x15, 0x27, 0x99, 0xC0, 0x16, 0x18, 0xCE, 0x30, 0x17, 0x08, 0xCD, 0x40, 0x17, 0xFA, 0x01, 0xB0, 0x18, 0xEA, 0x00, 0xC0, 0x19, 0xDB, 0x35, 0x30, 0x1A, 0xCC, 0x85, 0xC0, 0x1B, 0xBC, 0x92, 0xE0, 0x1C, 0xAC, 0x83, 0xE0, 0x1D, 0x9C, 0x74, 0xE0, @@ -14844,39 +14603,23 @@ const unsigned char timelib_timezone_db_data_builtin[262608] = { 0x43, 0x63, 0xF0, 0xE0, 0x44, 0x25, 0xBD, 0x60, 0x45, 0x43, 0xD2, 0xE0, 0x46, 0x05, 0x9F, 0x60, 0x47, 0x23, 0xB4, 0xE0, 0x47, 0xEE, 0xBB, 0xE0, 0x49, 0x03, 0x96, 0xE0, 0x49, 0xCE, 0x9D, 0xE0, 0x4A, 0xE3, 0x78, 0xE0, 0x4B, 0xAE, 0x7F, 0xE0, 0x4C, 0xCC, 0xA3, 0x70, 0x4D, 0x8E, 0x6F, 0xF0, -0x4E, 0xAC, 0x85, 0x70, 0x4F, 0x6E, 0x51, 0xF0, 0x50, 0x8C, 0x67, 0x70, 0x51, 0x57, 0x6E, 0x70, -0x52, 0x6C, 0x49, 0x70, 0x53, 0x37, 0x50, 0x70, 0x54, 0x4C, 0x2B, 0x70, 0x55, 0x17, 0x32, 0x70, -0x56, 0x2C, 0x0D, 0x70, 0x56, 0xF7, 0x14, 0x70, 0x58, 0x15, 0x29, 0xF0, 0x58, 0xD6, 0xF6, 0x70, -0x59, 0xF5, 0x0B, 0xF0, 0x5A, 0xB6, 0xD8, 0x70, 0x5B, 0xD4, 0xED, 0xF0, 0x5C, 0x9F, 0xF4, 0xF0, -0x5D, 0xB4, 0xCF, 0xF0, 0x5E, 0x7F, 0xD6, 0xF0, 0x5F, 0x94, 0xB1, 0xF0, 0x60, 0x5F, 0xB8, 0xF0, -0x61, 0x7D, 0xCE, 0x70, 0x62, 0x3F, 0x9A, 0xF0, 0x63, 0x5D, 0xB0, 0x70, 0x64, 0x1F, 0x7C, 0xF0, -0x65, 0x3D, 0x92, 0x70, 0x66, 0x08, 0x99, 0x70, 0x67, 0x1D, 0x74, 0x70, 0x67, 0xE8, 0x7B, 0x70, -0x68, 0xFD, 0x56, 0x70, 0x69, 0xC8, 0x5D, 0x70, 0x6A, 0xDD, 0x38, 0x70, 0x6B, 0xA8, 0x3F, 0x70, -0x6C, 0xC6, 0x54, 0xF0, 0x6D, 0x88, 0x21, 0x70, 0x6E, 0xA6, 0x36, 0xF0, 0x6F, 0x68, 0x03, 0x70, -0x70, 0x86, 0x18, 0xF0, 0x71, 0x51, 0x1F, 0xF0, 0x72, 0x65, 0xFA, 0xF0, 0x73, 0x31, 0x01, 0xF0, -0x74, 0x45, 0xDC, 0xF0, 0x75, 0x10, 0xE3, 0xF0, 0x76, 0x2E, 0xF9, 0x70, 0x76, 0xF0, 0xC5, 0xF0, -0x78, 0x0E, 0xDB, 0x70, 0x78, 0xD0, 0xA7, 0xF0, 0x79, 0xEE, 0xBD, 0x70, 0x7A, 0xB0, 0x89, 0xF0, -0x7B, 0xCE, 0x9F, 0x70, 0x7C, 0x99, 0xA6, 0x70, 0x7D, 0xAE, 0x81, 0x70, 0x7E, 0x79, 0x88, 0x70, -0x7F, 0x8E, 0x63, 0x70, 0x01, 0x02, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x05, 0x06, -0x05, 0x06, 0x05, 0x06, 0x05, 0x06, 0x05, 0x07, 0x08, 0x07, 0x08, 0x09, 0x08, 0x02, 0x0B, 0x02, +0x01, 0x02, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x05, 0x06, 0x05, 0x06, 0x05, 0x06, +0x05, 0x06, 0x05, 0x07, 0x08, 0x07, 0x08, 0x09, 0x08, 0x02, 0x0B, 0x02, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, -0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, -0x0C, 0x0D, 0x0E, 0x0F, 0x0E, 0x0F, 0x0E, 0x0F, 0x0E, 0x0F, 0x0E, 0x0F, 0x0E, 0x0F, 0x0E, 0x0F, -0x0E, 0x0F, 0x0E, 0x0F, 0x0E, 0x0F, 0x0E, 0x0F, 0x0E, 0x0F, 0x0E, 0x0F, 0x0E, 0x0F, 0x0E, 0x0F, -0x0E, 0x0F, 0x0E, 0x0F, 0x0E, 0x0F, 0x0E, 0x0F, 0x0E, 0x0F, 0x0E, 0x0F, 0x0E, 0x0F, 0x0E, 0x0F, -0x0E, 0x0F, 0x0E, 0x0F, 0x0E, 0x0F, 0x0E, 0x0F, 0x0E, 0x0F, 0x00, 0x00, 0x2F, 0x04, 0x00, 0x00, -0x00, 0x00, 0x2A, 0x30, 0x00, 0x04, 0x00, 0x00, 0x38, 0x40, 0x00, 0x04, 0x00, 0x00, 0x46, 0x50, -0x01, 0x09, 0x00, 0x00, 0x38, 0x40, 0x00, 0x0F, 0x00, 0x00, 0x38, 0x40, 0x00, 0x0F, 0x00, 0x00, -0x46, 0x50, 0x01, 0x09, 0x00, 0x00, 0x38, 0x40, 0x01, 0x09, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x0F, -0x00, 0x00, 0x2A, 0x30, 0x01, 0x09, 0x00, 0x00, 0x1C, 0x20, 0x00, 0x0F, 0x00, 0x00, 0x46, 0x50, -0x01, 0x14, 0x00, 0x00, 0x46, 0x50, 0x01, 0x14, 0x00, 0x00, 0x38, 0x40, 0x00, 0x04, 0x00, 0x00, -0x38, 0x40, 0x01, 0x14, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x04, 0x4C, 0x4D, 0x54, 0x00, 0x53, 0x41, -0x4D, 0x54, 0x00, 0x4B, 0x55, 0x59, 0x53, 0x54, 0x00, 0x4B, 0x55, 0x59, 0x54, 0x00, 0x53, 0x41, -0x4D, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, -0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0xDA, 0x81, 0x7F, 0x01, 0x5F, 0x2E, 0x58, 0x00, 0x00, 0x00, 0x19, -0x4D, 0x6F, 0x73, 0x63, 0x6F, 0x77, 0x20, 0x2D, 0x20, 0x53, 0x61, 0x6D, 0x61, 0x72, 0x61, 0x2C, -0x20, 0x55, 0x64, 0x6D, 0x75, 0x72, 0x74, 0x69, 0x61, +0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0C, 0x0D, 0x0E, 0x0F, +0x0D, 0x00, 0x00, 0x2F, 0x04, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x04, 0x00, 0x00, 0x38, +0x40, 0x00, 0x04, 0x00, 0x00, 0x46, 0x50, 0x01, 0x09, 0x00, 0x00, 0x38, 0x40, 0x00, 0x0F, 0x00, +0x00, 0x38, 0x40, 0x00, 0x0F, 0x00, 0x00, 0x46, 0x50, 0x01, 0x09, 0x00, 0x00, 0x38, 0x40, 0x01, +0x09, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x0F, 0x00, 0x00, 0x2A, 0x30, 0x01, 0x09, 0x00, 0x00, 0x1C, +0x20, 0x00, 0x0F, 0x00, 0x00, 0x46, 0x50, 0x01, 0x14, 0x00, 0x00, 0x46, 0x50, 0x01, 0x14, 0x00, +0x00, 0x38, 0x40, 0x00, 0x04, 0x00, 0x00, 0x38, 0x40, 0x01, 0x14, 0x00, 0x00, 0x2A, 0x30, 0x00, +0x04, 0x4C, 0x4D, 0x54, 0x00, 0x53, 0x41, 0x4D, 0x54, 0x00, 0x4B, 0x55, 0x59, 0x53, 0x54, 0x00, +0x4B, 0x55, 0x59, 0x54, 0x00, 0x53, 0x41, 0x4D, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDA, 0x81, 0x7F, 0x01, +0x5F, 0x2E, 0x58, 0x00, 0x00, 0x00, 0x19, 0x4D, 0x6F, 0x73, 0x63, 0x6F, 0x77, 0x20, 0x2D, 0x20, +0x53, 0x61, 0x6D, 0x61, 0x72, 0x61, 0x2C, 0x20, 0x55, 0x64, 0x6D, 0x75, 0x72, 0x74, 0x69, 0x61, + /* Europe/San_Marino */ 0x50, 0x48, 0x50, 0x31, 0x01, 0x53, 0x4D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -15617,7 +15360,7 @@ const unsigned char timelib_timezone_db_data_builtin[262608] = { /* Europe/Volgograd */ 0x50, 0x48, 0x50, 0x31, 0x01, 0x52, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x19, 0xA1, 0xF5, 0x46, 0xDC, +0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x19, 0xA1, 0xF5, 0x46, 0xDC, 0xAB, 0xD8, 0x86, 0x50, 0xB5, 0xA4, 0x0B, 0x50, 0xF0, 0xB0, 0x4C, 0x40, 0x15, 0x27, 0x99, 0xC0, 0x16, 0x18, 0xCE, 0x30, 0x17, 0x08, 0xCD, 0x40, 0x17, 0xFA, 0x01, 0xB0, 0x18, 0xEA, 0x00, 0xC0, 0x19, 0xDB, 0x35, 0x30, 0x1A, 0xCC, 0x85, 0xC0, 0x1B, 0xBC, 0x92, 0xE0, 0x1C, 0xAC, 0x83, 0xE0, @@ -15633,37 +15376,21 @@ const unsigned char timelib_timezone_db_data_builtin[262608] = { 0x40, 0x66, 0x07, 0x70, 0x41, 0x84, 0x1C, 0xF0, 0x42, 0x45, 0xE9, 0x70, 0x43, 0x63, 0xFE, 0xF0, 0x44, 0x25, 0xCB, 0x70, 0x45, 0x43, 0xE0, 0xF0, 0x46, 0x05, 0xAD, 0x70, 0x47, 0x23, 0xC2, 0xF0, 0x47, 0xEE, 0xC9, 0xF0, 0x49, 0x03, 0xA4, 0xF0, 0x49, 0xCE, 0xAB, 0xF0, 0x4A, 0xE3, 0x86, 0xF0, -0x4B, 0xAE, 0x8D, 0xF0, 0x4C, 0xCC, 0xA3, 0x70, 0x4D, 0x8E, 0x6F, 0xF0, 0x4E, 0xAC, 0x85, 0x70, -0x4F, 0x6E, 0x51, 0xF0, 0x50, 0x8C, 0x67, 0x70, 0x51, 0x57, 0x6E, 0x70, 0x52, 0x6C, 0x49, 0x70, -0x53, 0x37, 0x50, 0x70, 0x54, 0x4C, 0x2B, 0x70, 0x55, 0x17, 0x32, 0x70, 0x56, 0x2C, 0x0D, 0x70, -0x56, 0xF7, 0x14, 0x70, 0x58, 0x15, 0x29, 0xF0, 0x58, 0xD6, 0xF6, 0x70, 0x59, 0xF5, 0x0B, 0xF0, -0x5A, 0xB6, 0xD8, 0x70, 0x5B, 0xD4, 0xED, 0xF0, 0x5C, 0x9F, 0xF4, 0xF0, 0x5D, 0xB4, 0xCF, 0xF0, -0x5E, 0x7F, 0xD6, 0xF0, 0x5F, 0x94, 0xB1, 0xF0, 0x60, 0x5F, 0xB8, 0xF0, 0x61, 0x7D, 0xCE, 0x70, -0x62, 0x3F, 0x9A, 0xF0, 0x63, 0x5D, 0xB0, 0x70, 0x64, 0x1F, 0x7C, 0xF0, 0x65, 0x3D, 0x92, 0x70, -0x66, 0x08, 0x99, 0x70, 0x67, 0x1D, 0x74, 0x70, 0x67, 0xE8, 0x7B, 0x70, 0x68, 0xFD, 0x56, 0x70, -0x69, 0xC8, 0x5D, 0x70, 0x6A, 0xDD, 0x38, 0x70, 0x6B, 0xA8, 0x3F, 0x70, 0x6C, 0xC6, 0x54, 0xF0, -0x6D, 0x88, 0x21, 0x70, 0x6E, 0xA6, 0x36, 0xF0, 0x6F, 0x68, 0x03, 0x70, 0x70, 0x86, 0x18, 0xF0, -0x71, 0x51, 0x1F, 0xF0, 0x72, 0x65, 0xFA, 0xF0, 0x73, 0x31, 0x01, 0xF0, 0x74, 0x45, 0xDC, 0xF0, -0x75, 0x10, 0xE3, 0xF0, 0x76, 0x2E, 0xF9, 0x70, 0x76, 0xF0, 0xC5, 0xF0, 0x78, 0x0E, 0xDB, 0x70, -0x78, 0xD0, 0xA7, 0xF0, 0x79, 0xEE, 0xBD, 0x70, 0x7A, 0xB0, 0x89, 0xF0, 0x7B, 0xCE, 0x9F, 0x70, -0x7C, 0x99, 0xA6, 0x70, 0x7D, 0xAE, 0x81, 0x70, 0x7E, 0x79, 0x88, 0x70, 0x7F, 0x8E, 0x63, 0x70, -0x01, 0x02, 0x03, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x06, 0x07, 0x06, -0x07, 0x06, 0x07, 0x06, 0x08, 0x09, 0x08, 0x09, 0x06, 0x08, 0x0A, 0x08, 0x09, 0x08, 0x09, 0x08, -0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, -0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, -0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, +0x4B, 0xAE, 0x8D, 0xF0, 0x4C, 0xCC, 0xA3, 0x70, 0x4D, 0x8E, 0x6F, 0xF0, 0x01, 0x02, 0x03, 0x05, +0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, +0x08, 0x09, 0x08, 0x09, 0x06, 0x08, 0x0A, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, -0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, -0x09, 0x08, 0x09, 0x08, 0x09, 0x00, 0x00, 0x29, 0xA4, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x30, 0x00, -0x04, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x09, 0x00, 0x00, 0x38, 0x40, 0x00, 0x09, 0x00, 0x00, 0x46, -0x50, 0x01, 0x0E, 0x00, 0x00, 0x38, 0x40, 0x00, 0x14, 0x00, 0x00, 0x38, 0x40, 0x00, 0x14, 0x00, -0x00, 0x46, 0x50, 0x01, 0x0E, 0x00, 0x00, 0x38, 0x40, 0x01, 0x0E, 0x00, 0x00, 0x2A, 0x30, 0x00, -0x14, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x14, 0x4C, 0x4D, 0x54, 0x00, 0x54, 0x53, 0x41, 0x54, 0x00, -0x53, 0x54, 0x41, 0x54, 0x00, 0x56, 0x4F, 0x4C, 0x53, 0x54, 0x00, 0x56, 0x4F, 0x4C, 0x54, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD3, 0xB0, 0xB5, 0x01, 0x56, 0x6E, 0xC2, 0x00, 0x00, -0x00, 0x17, 0x4D, 0x6F, 0x73, 0x63, 0x6F, 0x77, 0x2B, 0x30, 0x30, 0x20, 0x2D, 0x20, 0x43, 0x61, -0x73, 0x70, 0x69, 0x61, 0x6E, 0x20, 0x53, 0x65, 0x61, +0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x06, 0x00, 0x00, 0x29, 0xA4, +0x00, 0x00, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x04, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x09, 0x00, 0x00, +0x38, 0x40, 0x00, 0x09, 0x00, 0x00, 0x46, 0x50, 0x01, 0x0E, 0x00, 0x00, 0x38, 0x40, 0x00, 0x14, +0x00, 0x00, 0x38, 0x40, 0x00, 0x14, 0x00, 0x00, 0x46, 0x50, 0x01, 0x0E, 0x00, 0x00, 0x38, 0x40, +0x01, 0x0E, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x14, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x14, 0x4C, 0x4D, +0x54, 0x00, 0x54, 0x53, 0x41, 0x54, 0x00, 0x53, 0x54, 0x41, 0x54, 0x00, 0x56, 0x4F, 0x4C, 0x53, +0x54, 0x00, 0x56, 0x4F, 0x4C, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, +0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD3, 0xB0, +0xB5, 0x01, 0x56, 0x6E, 0xC2, 0x00, 0x00, 0x00, 0x17, 0x4D, 0x6F, 0x73, 0x63, 0x6F, 0x77, 0x2B, +0x30, 0x30, 0x20, 0x2D, 0x20, 0x43, 0x61, 0x73, 0x70, 0x69, 0x61, 0x6E, 0x20, 0x53, 0x65, 0x61, + /* Europe/Warsaw */ 0x50, 0x48, 0x50, 0x31, 0x01, 0x50, 0x4C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -16968,7 +16695,7 @@ const unsigned char timelib_timezone_db_data_builtin[262608] = { 0x42, 0x33, 0xAC, 0xB0, 0x43, 0x48, 0x95, 0xC0, 0x44, 0x13, 0x8E, 0xB0, 0x45, 0x31, 0xB2, 0x40, 0x45, 0xF3, 0x70, 0xB0, 0x47, 0x11, 0x94, 0x40, 0x47, 0xEF, 0x02, 0x30, 0x48, 0xF1, 0x76, 0x40, 0x49, 0xBC, 0x6F, 0x30, 0x4A, 0xD1, 0x58, 0x40, 0x4B, 0xB8, 0x00, 0xB0, 0x4C, 0xB1, 0x3A, 0x40, -0x4D, 0x97, 0xE2, 0xB0, 0x4E, 0x91, 0x1C, 0x40, 0x4F, 0x5C, 0x15, 0x30, 0x50, 0x7A, 0x38, 0xC0, +0x4D, 0xC6, 0x07, 0x30, 0x4E, 0x50, 0x82, 0xC0, 0x4F, 0x5C, 0x15, 0x30, 0x50, 0x7A, 0x38, 0xC0, 0x51, 0x3B, 0xF7, 0x30, 0x52, 0x5A, 0x1A, 0xC0, 0x53, 0x1B, 0xD9, 0x30, 0x54, 0x39, 0xFC, 0xC0, 0x55, 0x04, 0xF5, 0xB0, 0x56, 0x19, 0xDE, 0xC0, 0x56, 0xE4, 0xD7, 0xB0, 0x57, 0xF9, 0xC0, 0xC0, 0x58, 0xC4, 0xB9, 0xB0, 0x59, 0xE2, 0xDD, 0x40, 0x5A, 0xA4, 0x9B, 0xB0, 0x5B, 0xC2, 0xBF, 0x40, @@ -18387,8 +18114,8 @@ const unsigned char timelib_timezone_db_data_builtin[262608] = { /* W-SU */ 0x50, 0x48, 0x50, 0x31, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x1E, 0x9B, 0x5F, 0x1E, 0xD8, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x1E, 0x9B, 0x5F, 0x1E, 0xD8, 0x9D, 0x3E, 0xF2, 0x98, 0x9E, 0x2A, 0xEF, 0x18, 0x9E, 0xF7, 0x39, 0x88, 0x9F, 0x84, 0x58, 0x18, 0xA0, 0xD8, 0x6D, 0x08, 0xA1, 0x00, 0x16, 0x28, 0xA1, 0x3C, 0xA6, 0x40, 0xA4, 0x10, 0x6D, 0xC0, 0xA4, 0x3D, 0x32, 0xB0, 0xA5, 0x15, 0x68, 0xB0, 0xA5, 0x3D, 0x03, 0xC0, 0xA7, 0x1E, 0x45, 0x50, @@ -18407,37 +18134,21 @@ const unsigned char timelib_timezone_db_data_builtin[262608] = { 0x40, 0x66, 0x07, 0x70, 0x41, 0x84, 0x1C, 0xF0, 0x42, 0x45, 0xE9, 0x70, 0x43, 0x63, 0xFE, 0xF0, 0x44, 0x25, 0xCB, 0x70, 0x45, 0x43, 0xE0, 0xF0, 0x46, 0x05, 0xAD, 0x70, 0x47, 0x23, 0xC2, 0xF0, 0x47, 0xEE, 0xC9, 0xF0, 0x49, 0x03, 0xA4, 0xF0, 0x49, 0xCE, 0xAB, 0xF0, 0x4A, 0xE3, 0x86, 0xF0, -0x4B, 0xAE, 0x8D, 0xF0, 0x4C, 0xCC, 0xA3, 0x70, 0x4D, 0x8E, 0x6F, 0xF0, 0x4E, 0xAC, 0x85, 0x70, -0x4F, 0x6E, 0x51, 0xF0, 0x50, 0x8C, 0x67, 0x70, 0x51, 0x57, 0x6E, 0x70, 0x52, 0x6C, 0x49, 0x70, -0x53, 0x37, 0x50, 0x70, 0x54, 0x4C, 0x2B, 0x70, 0x55, 0x17, 0x32, 0x70, 0x56, 0x2C, 0x0D, 0x70, -0x56, 0xF7, 0x14, 0x70, 0x58, 0x15, 0x29, 0xF0, 0x58, 0xD6, 0xF6, 0x70, 0x59, 0xF5, 0x0B, 0xF0, -0x5A, 0xB6, 0xD8, 0x70, 0x5B, 0xD4, 0xED, 0xF0, 0x5C, 0x9F, 0xF4, 0xF0, 0x5D, 0xB4, 0xCF, 0xF0, -0x5E, 0x7F, 0xD6, 0xF0, 0x5F, 0x94, 0xB1, 0xF0, 0x60, 0x5F, 0xB8, 0xF0, 0x61, 0x7D, 0xCE, 0x70, -0x62, 0x3F, 0x9A, 0xF0, 0x63, 0x5D, 0xB0, 0x70, 0x64, 0x1F, 0x7C, 0xF0, 0x65, 0x3D, 0x92, 0x70, -0x66, 0x08, 0x99, 0x70, 0x67, 0x1D, 0x74, 0x70, 0x67, 0xE8, 0x7B, 0x70, 0x68, 0xFD, 0x56, 0x70, -0x69, 0xC8, 0x5D, 0x70, 0x6A, 0xDD, 0x38, 0x70, 0x6B, 0xA8, 0x3F, 0x70, 0x6C, 0xC6, 0x54, 0xF0, -0x6D, 0x88, 0x21, 0x70, 0x6E, 0xA6, 0x36, 0xF0, 0x6F, 0x68, 0x03, 0x70, 0x70, 0x86, 0x18, 0xF0, -0x71, 0x51, 0x1F, 0xF0, 0x72, 0x65, 0xFA, 0xF0, 0x73, 0x31, 0x01, 0xF0, 0x74, 0x45, 0xDC, 0xF0, -0x75, 0x10, 0xE3, 0xF0, 0x76, 0x2E, 0xF9, 0x70, 0x76, 0xF0, 0xC5, 0xF0, 0x78, 0x0E, 0xDB, 0x70, -0x78, 0xD0, 0xA7, 0xF0, 0x79, 0xEE, 0xBD, 0x70, 0x7A, 0xB0, 0x89, 0xF0, 0x7B, 0xCE, 0x9F, 0x70, -0x7C, 0x99, 0xA6, 0x70, 0x7D, 0xAE, 0x81, 0x70, 0x7E, 0x79, 0x88, 0x70, 0x7F, 0x8E, 0x63, 0x70, -0x02, 0x01, 0x02, 0x03, 0x01, 0x03, 0x05, 0x04, 0x05, 0x06, 0x05, 0x04, 0x07, 0x04, 0x05, 0x04, -0x05, 0x04, 0x05, 0x04, 0x05, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, -0x09, 0x08, 0x0A, 0x0B, 0x08, 0x05, 0x04, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, -0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, -0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, -0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, +0x4B, 0xAE, 0x8D, 0xF0, 0x4C, 0xCC, 0xA3, 0x70, 0x4D, 0x8E, 0x6F, 0xF0, 0x02, 0x01, 0x02, 0x03, +0x01, 0x03, 0x05, 0x04, 0x05, 0x06, 0x05, 0x04, 0x07, 0x04, 0x05, 0x04, 0x05, 0x04, 0x05, 0x04, +0x05, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x0A, 0x0B, +0x08, 0x05, 0x04, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, -0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, -0x08, 0x00, 0x00, 0x23, 0x28, 0x00, 0x00, 0x00, 0x00, 0x31, 0x68, 0x01, 0x04, 0x00, 0x00, 0x23, -0x58, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x78, 0x01, 0x08, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x0D, 0x00, -0x00, 0x38, 0x40, 0x01, 0x11, 0x00, 0x00, 0x46, 0x50, 0x01, 0x11, 0x00, 0x00, 0x1C, 0x20, 0x00, -0x15, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x0D, 0x00, 0x00, 0x38, 0x40, 0x01, 0x11, 0x00, 0x00, 0x2A, -0x30, 0x01, 0x19, 0x00, 0x00, 0x1C, 0x20, 0x00, 0x15, 0x4D, 0x4D, 0x54, 0x00, 0x4D, 0x53, 0x54, -0x00, 0x4D, 0x44, 0x53, 0x54, 0x00, 0x4D, 0x53, 0x4B, 0x00, 0x4D, 0x53, 0x44, 0x00, 0x45, 0x45, -0x54, 0x00, 0x45, 0x45, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, -0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x89, 0x54, 0x40, 0x01, 0x12, 0xA8, 0x80, 0x00, 0x00, 0x00, 0x00, +0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x08, 0x0C, 0x00, 0x00, 0x23, 0x28, 0x00, 0x00, 0x00, 0x00, +0x31, 0x68, 0x01, 0x04, 0x00, 0x00, 0x23, 0x58, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x78, 0x01, 0x08, +0x00, 0x00, 0x2A, 0x30, 0x00, 0x0D, 0x00, 0x00, 0x38, 0x40, 0x01, 0x11, 0x00, 0x00, 0x46, 0x50, +0x01, 0x11, 0x00, 0x00, 0x1C, 0x20, 0x00, 0x15, 0x00, 0x00, 0x2A, 0x30, 0x00, 0x0D, 0x00, 0x00, +0x38, 0x40, 0x01, 0x11, 0x00, 0x00, 0x2A, 0x30, 0x01, 0x19, 0x00, 0x00, 0x1C, 0x20, 0x00, 0x15, +0x00, 0x00, 0x38, 0x40, 0x00, 0x0D, 0x4D, 0x4D, 0x54, 0x00, 0x4D, 0x53, 0x54, 0x00, 0x4D, 0x44, +0x53, 0x54, 0x00, 0x4D, 0x53, 0x4B, 0x00, 0x4D, 0x53, 0x44, 0x00, 0x45, 0x45, 0x54, 0x00, 0x45, +0x45, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, +0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, +0x54, 0x40, 0x01, 0x12, 0xA8, 0x80, 0x00, 0x00, 0x00, 0x00, /* Zulu */ 0x50, 0x48, 0x50, 0x31, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -18446,4 +18157,4 @@ const unsigned char timelib_timezone_db_data_builtin[262608] = { 0x00, 0x00, 0x55, 0x54, 0x43, 0x00, 0x00, 0x00, 0x00, 0x89, 0x54, 0x40, 0x01, 0x12, 0xA8, 0x80, 0x00, 0x00, 0x00, 0x00, }; -const timelib_tzdb timezonedb_builtin = { "2011.4", 571, timezonedb_idx_builtin, timelib_timezone_db_data_builtin }; +const timelib_tzdb timezonedb_builtin = { "2011.8", 573, timezonedb_idx_builtin, timelib_timezone_db_data_builtin }; diff --git a/ext/date/php_date.c b/ext/date/php_date.c index 1a2a1da8d..ad2391fc0 100644 --- a/ext/date/php_date.c +++ b/ext/date/php_date.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: php_date.c 307853 2011-01-30 10:18:12Z stas $ */ +/* $Id: php_date.c 314445 2011-08-07 18:12:52Z gwynne $ */ #include "php.h" #include "php_streams.h" @@ -425,7 +425,7 @@ const zend_function_entry date_functions[] = { PHP_FE(date_sunrise, arginfo_date_sunrise) PHP_FE(date_sunset, arginfo_date_sunset) PHP_FE(date_sun_info, arginfo_date_sun_info) - {NULL, NULL, NULL} + PHP_FE_END }; const zend_function_entry date_funcs_date[] = { @@ -447,7 +447,7 @@ const zend_function_entry date_funcs_date[] = { PHP_ME_MAPPING(setTimestamp, date_timestamp_set, arginfo_date_method_timestamp_set, 0) PHP_ME_MAPPING(getTimestamp, date_timestamp_get, arginfo_date_method_timestamp_get, 0) PHP_ME_MAPPING(diff, date_diff, arginfo_date_method_diff, 0) - {NULL, NULL, NULL} + PHP_FE_END }; const zend_function_entry date_funcs_timezone[] = { @@ -458,19 +458,19 @@ const zend_function_entry date_funcs_timezone[] = { PHP_ME_MAPPING(getLocation, timezone_location_get, arginfo_timezone_method_location_get, 0) PHP_ME_MAPPING(listAbbreviations, timezone_abbreviations_list, arginfo_timezone_abbreviations_list, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) PHP_ME_MAPPING(listIdentifiers, timezone_identifiers_list, arginfo_timezone_identifiers_list, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) - {NULL, NULL, NULL} + PHP_FE_END }; const zend_function_entry date_funcs_interval[] = { PHP_ME(DateInterval, __construct, arginfo_date_interval_construct, ZEND_ACC_CTOR|ZEND_ACC_PUBLIC) PHP_ME_MAPPING(format, date_interval_format, arginfo_date_method_interval_format, 0) PHP_ME_MAPPING(createFromDateString, date_interval_create_from_date_string, arginfo_date_interval_create_from_date_string, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) - {NULL, NULL, NULL} + PHP_FE_END }; const zend_function_entry date_funcs_period[] = { PHP_ME(DatePeriod, __construct, arginfo_date_period_construct, ZEND_ACC_CTOR|ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} + PHP_FE_END }; static char* guess_timezone(const timelib_tzdb *tzdb TSRMLS_DC); @@ -2378,7 +2378,7 @@ static void update_errors_warnings(timelib_error_container *last_errors TSRMLS_D PHPAPI int php_date_initialize(php_date_obj *dateobj, /*const*/ char *time_str, int time_str_len, char *format, zval *timezone_object, int ctor TSRMLS_DC) { timelib_time *now; - timelib_tzinfo *tzi; + timelib_tzinfo *tzi = NULL; timelib_error_container *err = NULL; int type = TIMELIB_ZONETYPE_ID, new_dst; char *new_abbr; @@ -2860,14 +2860,13 @@ PHP_FUNCTION(date_add) if (intobj->diff->invert) { bias = -1; } + memset(&dateobj->time->relative, 0, sizeof(struct timelib_rel_time)); dateobj->time->relative.y = intobj->diff->y * bias; dateobj->time->relative.m = intobj->diff->m * bias; dateobj->time->relative.d = intobj->diff->d * bias; dateobj->time->relative.h = intobj->diff->h * bias; dateobj->time->relative.i = intobj->diff->i * bias; dateobj->time->relative.s = intobj->diff->s * bias; - dateobj->time->relative.weekday = 0; - dateobj->time->relative.have_weekday_relative = 0; } dateobj->time->have_relative = 1; dateobj->time->sse_uptodate = 0; @@ -2907,6 +2906,7 @@ PHP_FUNCTION(date_sub) bias = -1; } + memset(&dateobj->time->relative, 0, sizeof(struct timelib_rel_time)); dateobj->time->relative.y = 0 - (intobj->diff->y * bias); dateobj->time->relative.m = 0 - (intobj->diff->m * bias); dateobj->time->relative.d = 0 - (intobj->diff->d * bias); @@ -2914,8 +2914,6 @@ PHP_FUNCTION(date_sub) dateobj->time->relative.i = 0 - (intobj->diff->i * bias); dateobj->time->relative.s = 0 - (intobj->diff->s * bias); dateobj->time->have_relative = 1; - dateobj->time->relative.weekday = 0; - dateobj->time->relative.have_weekday_relative = 0; dateobj->time->sse_uptodate = 0; timelib_update_ts(dateobj->time, NULL); @@ -3769,7 +3767,7 @@ PHP_METHOD(DatePeriod, __construct) dpobj = zend_object_store_get_object(getThis() TSRMLS_CC); dpobj->current = NULL; - if (isostr_len) { + if (isostr) { date_period_initialize(&(dpobj->start), &(dpobj->end), &(dpobj->interval), &recurrences, isostr, isostr_len TSRMLS_CC); if (dpobj->start == NULL) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "The ISO interval '%s' did not contain a start date.", isostr); diff --git a/ext/date/tests/DateInterval_format.phpt b/ext/date/tests/DateInterval_format.phpt index 8088a0aa7..c144f585a 100644 --- a/ext/date/tests/DateInterval_format.phpt +++ b/ext/date/tests/DateInterval_format.phpt @@ -10,6 +10,7 @@ Daniel Convissor <danielc@php.net> <?php if (!method_exists('DateInterval', 'format')) die("skip: method doesn't exist"); ?> --FILE-- <?php +date_default_timezone_set('UTC'); $date1 = new DateTime('2000-01-01 00:00:00'); $date2 = new DateTime('2001-03-04 04:05:06'); diff --git a/ext/date/tests/DateInterval_format_a.phpt b/ext/date/tests/DateInterval_format_a.phpt index f82a2f6f6..d095db56b 100644 --- a/ext/date/tests/DateInterval_format_a.phpt +++ b/ext/date/tests/DateInterval_format_a.phpt @@ -3,8 +3,13 @@ DateInterval::format(), %a --CREDITS-- Daniel Convissor <danielc@php.net> # TestFest 2010 BKTK +--INI-- +date.timezone=UTC --SKIPIF-- -<?php if (!method_exists('DateInterval', 'format')) die("skip: method doesn't exist"); ?> +<?php +if (!method_exists('DateInterval', 'format')) die("skip: method doesn't exist"); +if (substr(PHP_OS, 0, 3) != 'WIN') die("skip this test is for Windows platforms only"); +?> --XFAIL-- Windows VC6 libs' floor()/ceil() choke on floats --FILE-- diff --git a/ext/date/tests/DateTime_add-dates.phpt b/ext/date/tests/DateTime_add-dates.phpt new file mode 100644 index 000000000..48c821ff9 --- /dev/null +++ b/ext/date/tests/DateTime_add-dates.phpt @@ -0,0 +1,29 @@ +--TEST-- +DateTime::add() -- dates +--CREDITS-- +Daniel Convissor <danielc@php.net> +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_ADD); +require 'DateTime_data-dates.inc'; + +?> +--EXPECT-- +test__7: ADD: 2009-01-07 00:00:00 EST + P+0Y0M7DT0H0M0S = **2009-01-14 00:00:00 EST** +test_years_positive__7_by_0_day: ADD: 2000-02-07 00:00:00 EST + P+7Y0M0DT0H0M0S = **2007-02-07 00:00:00 EST** +test_years_positive__7_by_1_day: ADD: 2000-02-07 00:00:00 EST + P+7Y0M1DT0H0M0S = **2007-02-08 00:00:00 EST** +test_years_positive__6_shy_1_day: ADD: 2000-02-07 00:00:00 EST + P+6Y11M30DT0H0M0S = **2007-02-06 00:00:00 EST** +test_years_positive__7_by_1_month: ADD: 2000-02-07 00:00:00 EST + P+7Y1M0DT0H0M0S = **2007-03-07 00:00:00 EST** +test_years_positive__6_shy_1_month: ADD: 2000-02-07 00:00:00 EST + P+6Y11M0DT0H0M0S = **2007-01-07 00:00:00 EST** +test_years_positive__7_by_1_month_split_newyear: ADD: 1999-12-07 00:00:00 EST + P+7Y1M0DT0H0M0S = **2007-01-07 00:00:00 EST** +test_years_positive__6_shy_1_month_split_newyear: ADD: 2000-01-07 00:00:00 EST + P+6Y11M0DT0H0M0S = **2006-12-07 00:00:00 EST** +test_negative__7: ADD: 2009-01-14 00:00:00 EST + P-0Y0M7DT0H0M0S = **2009-01-07 00:00:00 EST** +test_years_negative__7_by_0_day: ADD: 2007-02-07 00:00:00 EST + P-7Y0M0DT0H0M0S = **2000-02-07 00:00:00 EST** +test_years_negative__7_by_1_day: ADD: 2007-02-08 00:00:00 EST + P-7Y0M1DT0H0M0S = **2000-02-07 00:00:00 EST** +test_years_negative__6_shy_1_day: ADD: 2007-02-06 00:00:00 EST + P-6Y11M28DT0H0M0S = **2000-02-07 00:00:00 EST** +test_years_negative__7_by_1_month: ADD: 2007-03-07 00:00:00 EST + P-7Y1M0DT0H0M0S = **2000-02-07 00:00:00 EST** +test_years_negative__6_shy_1_month: ADD: 2007-01-07 00:00:00 EST + P-6Y11M0DT0H0M0S = **2000-02-07 00:00:00 EST** +test_years_negative__7_by_1_month_split_newyear: ADD: 2007-01-07 00:00:00 EST + P-7Y1M0DT0H0M0S = **1999-12-07 00:00:00 EST** +test_years_negative__6_shy_1_month_split_newyear: ADD: 2006-12-07 00:00:00 EST + P-6Y11M0DT0H0M0S = **2000-01-07 00:00:00 EST** diff --git a/ext/date/tests/DateTime_add-fall-type2-type2.phpt b/ext/date/tests/DateTime_add-fall-type2-type2.phpt new file mode 100644 index 000000000..82cc81c9e --- /dev/null +++ b/ext/date/tests/DateTime_add-fall-type2-type2.phpt @@ -0,0 +1,53 @@ +--TEST-- +DateTime::add() -- fall type2 type2 +--CREDITS-- +Daniel Convissor <danielc@php.net> +--XFAIL-- +Various bugs exist +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_ADD); +require 'DateTime_data-fall-type2-type2.inc'; + +?> +--EXPECT-- +test_time_fall_type2_prev_type2_prev: ADD: 2010-10-04 02:18:48 EDT + P+0Y1M2DT16H19M40S = **2010-11-06 18:38:28 EDT** +test_time_fall_type2_prev_type2_dt: ADD: 2010-11-06 18:38:28 EDT + P+0Y0M0DT5H31M52S = **2010-11-07 00:10:20 EDT** +test_time_fall_type2_prev_type2_redodt: ADD: 2010-11-06 18:38:28 EDT + P+0Y0M0DT6H34M5S = **2010-11-07 01:12:33 EDT** +test_time_fall_type2_prev_type2_redost: ADD: 2010-11-06 18:38:28 EDT + P+0Y0M0DT7H36M16S = **2010-11-07 01:14:44 EST** +test_time_fall_type2_prev_type2_st: ADD: 2010-11-06 18:38:28 EDT + P+0Y0M0DT9H38M27S = **2010-11-07 03:16:55 EST** +test_time_fall_type2_prev_type2_post: ADD: 2010-11-06 18:38:28 EDT + P+0Y0M2DT1H21M31S = **2010-11-08 19:59:59 EST** +test_time_fall_type2_dt_type2_prev: ADD: 2010-11-07 00:10:20 EDT + P-0Y0M0DT5H31M52S = **2010-11-06 18:38:28 EDT** +test_time_fall_type2_dt_type2_dt: ADD: 2010-11-07 00:10:20 EDT + P+0Y0M0DT0H5M15S = **2010-11-07 00:15:35 EDT** +test_time_fall_type2_dt_type2_redodt: ADD: 2010-11-07 00:10:20 EDT + P+0Y0M0DT1H2M13S = **2010-11-07 01:12:33 EDT** +test_time_fall_type2_dt_type2_redost: ADD: 2010-11-07 00:10:20 EDT + P+0Y0M0DT2H4M24S = **2010-11-07 01:14:44 EST** +test_time_fall_type2_dt_type2_st: ADD: 2010-11-07 00:10:20 EDT + P+0Y0M0DT4H6M35S = **2010-11-07 03:16:55 EST** +test_time_fall_type2_dt_type2_post: ADD: 2010-11-07 00:10:20 EDT + P+0Y0M1DT20H49M39S = **2010-11-08 19:59:59 EST** +test_time_fall_type2_redodt_type2_prev: ADD: 2010-11-07 01:12:33 EDT + P-0Y0M0DT6H34M5S = **2010-11-06 18:38:28 EDT** +test_time_fall_type2_redodt_type2_dt: ADD: 2010-11-07 01:12:33 EDT + P-0Y0M0DT1H2M13S = **2010-11-07 00:10:20 EDT** +test_time_fall_type2_redodt_type2_redodt: ADD: 2010-11-07 01:12:33 EDT + P+0Y0M0DT0H3M2S = **2010-11-07 01:15:35 EDT** +test_time_fall_type2_redodt_type2_redost: ADD: 2010-11-07 01:12:33 EDT + P+0Y0M0DT1H2M11S = **2010-11-07 01:14:44 EST** +test_time_fall_type2_redodt_type2_st: ADD: 2010-11-07 01:12:33 EDT + P+0Y0M0DT3H4M22S = **2010-11-07 03:16:55 EST** +test_time_fall_type2_redodt_type2_post: ADD: 2010-11-07 01:12:33 EDT + P+0Y0M1DT19H47M26S = **2010-11-08 19:59:59 EST** +test_time_fall_type2_redost_type2_prev: ADD: 2010-11-07 01:14:44 EST + P-0Y0M0DT7H36M16S = **2010-11-06 18:38:28 EDT** +test_time_fall_type2_redost_type2_dt: ADD: 2010-11-07 01:14:44 EST + P-0Y0M0DT2H4M24S = **2010-11-07 00:10:20 EDT** +test_time_fall_type2_redost_type2_redodt: ADD: 2010-11-07 01:14:44 EST + P-0Y0M0DT1H2M11S = **2010-11-07 01:12:33 EDT** +test_time_fall_type2_redost_type2_redost: ADD: 2010-11-07 01:14:44 EST + P+0Y0M0DT0H2M10S = **2010-11-07 01:16:54 EST** +test_time_fall_type2_redost_type2_st: ADD: 2010-11-07 01:14:44 EST + P+0Y0M0DT2H2M11S = **2010-11-07 03:16:55 EST** +test_time_fall_type2_redost_type2_post: ADD: 2010-11-07 01:14:44 EST + P+0Y0M1DT18H45M15S = **2010-11-08 19:59:59 EST** +test_time_fall_type2_st_type2_prev: ADD: 2010-11-07 03:16:55 EST + P-0Y0M0DT9H38M27S = **2010-11-06 18:38:28 EDT** +test_time_fall_type2_st_type2_dt: ADD: 2010-11-07 03:16:55 EST + P-0Y0M0DT4H6M35S = **2010-11-07 00:10:20 EDT** +test_time_fall_type2_st_type2_redodt: ADD: 2010-11-07 03:16:55 EST + P-0Y0M0DT3H4M22S = **2010-11-07 01:12:33 EDT** +test_time_fall_type2_st_type2_redost: ADD: 2010-11-07 03:16:55 EST + P-0Y0M0DT2H2M11S = **2010-11-07 01:14:44 EST** +test_time_fall_type2_st_type2_st: ADD: 2010-11-07 03:16:55 EST + P+0Y0M0DT2H3M1S = **2010-11-07 05:19:56 EST** +test_time_fall_type2_st_type2_post: ADD: 2010-11-07 03:16:55 EST + P+0Y0M1DT16H43M4S = **2010-11-08 19:59:59 EST** +test_time_fall_type2_post_type2_prev: ADD: 2010-11-08 19:59:59 EST + P-0Y0M2DT1H21M31S = **2010-11-06 18:38:28 EDT** +test_time_fall_type2_post_type2_dt: ADD: 2010-11-08 19:59:59 EST + P-0Y0M1DT20H49M39S = **2010-11-07 00:10:20 EDT** +test_time_fall_type2_post_type2_redodt: ADD: 2010-11-08 19:59:59 EST + P-0Y0M1DT19H47M26S = **2010-11-07 01:12:33 EDT** +test_time_fall_type2_post_type2_redost: ADD: 2010-11-08 19:59:59 EST + P-0Y0M1DT18H45M15S = **2010-11-07 01:14:44 EST** +test_time_fall_type2_post_type2_st: ADD: 2010-11-08 19:59:59 EST + P-0Y0M1DT16H43M4S = **2010-11-07 03:16:55 EST** +test_time_fall_type2_post_type2_post: ADD: 2010-11-08 18:57:55 EST + P+0Y0M0DT1H2M4S = **2010-11-08 19:59:59 EST** +test_time_fall_type2_dtsec_type2_stsec: ADD: 2010-11-07 01:59:59 EDT + P+0Y0M0DT0H0M1S = **2010-11-07 01:00:00 EST** +test_time_fall_type2_stsec_type2_dtsec: ADD: 2010-11-07 01:00:00 EST + P-0Y0M0DT0H0M1S = **2010-11-07 01:59:59 EDT** diff --git a/ext/date/tests/DateTime_add-fall-type2-type3.phpt b/ext/date/tests/DateTime_add-fall-type2-type3.phpt new file mode 100644 index 000000000..077dd565d --- /dev/null +++ b/ext/date/tests/DateTime_add-fall-type2-type3.phpt @@ -0,0 +1,53 @@ +--TEST-- +DateTime::add() -- fall type2 type3 +--CREDITS-- +Daniel Convissor <danielc@php.net> +--XFAIL-- +Various bugs exist +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_ADD); +require 'DateTime_data-fall-type2-type3.inc'; + +?> +--EXPECT-- +test_time_fall_type2_prev_type3_prev: ADD: 2010-10-04 02:18:48 EDT + P+0Y1M2DT16H19M40S = **2010-11-06 18:38:28 EDT** +test_time_fall_type2_prev_type3_dt: ADD: 2010-11-06 18:38:28 EDT + P+0Y0M0DT5H31M52S = **2010-11-07 00:10:20 EDT** +test_time_fall_type2_prev_type3_redodt: ADD: 2010-11-06 18:38:28 EDT + P+0Y0M0DT6H34M5S = **2010-11-07 01:12:33 EDT** +test_time_fall_type2_prev_type3_redost: ADD: 2010-11-06 18:38:28 EDT + P+0Y0M0DT7H36M16S = **2010-11-07 01:14:44 EST** +test_time_fall_type2_prev_type3_st: ADD: 2010-11-06 18:38:28 EDT + P+0Y0M0DT9H38M27S = **2010-11-07 03:16:55 EST** +test_time_fall_type2_prev_type3_post: ADD: 2010-11-06 18:38:28 EDT + P+0Y0M2DT1H21M31S = **2010-11-08 19:59:59 EST** +test_time_fall_type2_dt_type3_prev: ADD: 2010-11-07 00:10:20 EDT + P-0Y0M0DT5H31M52S = **2010-11-06 18:38:28 EDT** +test_time_fall_type2_dt_type3_dt: ADD: 2010-11-07 00:10:20 EDT + P+0Y0M0DT0H5M15S = **2010-11-07 00:15:35 EDT** +test_time_fall_type2_dt_type3_redodt: ADD: 2010-11-07 00:10:20 EDT + P+0Y0M0DT1H2M13S = **2010-11-07 01:12:33 EDT** +test_time_fall_type2_dt_type3_redost: ADD: 2010-11-07 00:10:20 EDT + P+0Y0M0DT2H4M24S = **2010-11-07 01:14:44 EST** +test_time_fall_type2_dt_type3_st: ADD: 2010-11-07 00:10:20 EDT + P+0Y0M0DT4H6M35S = **2010-11-07 03:16:55 EST** +test_time_fall_type2_dt_type3_post: ADD: 2010-11-07 00:10:20 EDT + P+0Y0M1DT20H49M39S = **2010-11-08 19:59:59 EST** +test_time_fall_type2_redodt_type3_prev: ADD: 2010-11-07 01:12:33 EDT + P-0Y0M0DT6H34M5S = **2010-11-06 18:38:28 EDT** +test_time_fall_type2_redodt_type3_dt: ADD: 2010-11-07 01:12:33 EDT + P-0Y0M0DT1H2M13S = **2010-11-07 00:10:20 EDT** +test_time_fall_type2_redodt_type3_redodt: ADD: 2010-11-07 01:12:33 EDT + P+0Y0M0DT0H3M2S = **2010-11-07 01:15:35 EDT** +test_time_fall_type2_redodt_type3_redost: ADD: 2010-11-07 01:12:33 EDT + P+0Y0M0DT1H2M11S = **2010-11-07 01:14:44 EST** +test_time_fall_type2_redodt_type3_st: ADD: 2010-11-07 01:12:33 EDT + P+0Y0M0DT3H4M22S = **2010-11-07 03:16:55 EST** +test_time_fall_type2_redodt_type3_post: ADD: 2010-11-07 01:12:33 EDT + P+0Y0M1DT19H47M26S = **2010-11-08 19:59:59 EST** +test_time_fall_type2_redost_type3_prev: ADD: 2010-11-07 01:14:44 EST + P-0Y0M0DT7H36M16S = **2010-11-06 18:38:28 EDT** +test_time_fall_type2_redost_type3_dt: ADD: 2010-11-07 01:14:44 EST + P-0Y0M0DT2H4M24S = **2010-11-07 00:10:20 EDT** +test_time_fall_type2_redost_type3_redodt: ADD: 2010-11-07 01:14:44 EST + P-0Y0M0DT1H2M11S = **2010-11-07 01:12:33 EDT** +test_time_fall_type2_redost_type3_redost: ADD: 2010-11-07 01:14:44 EST + P+0Y0M0DT0H2M10S = **2010-11-07 01:16:54 EST** +test_time_fall_type2_redost_type3_st: ADD: 2010-11-07 01:14:44 EST + P+0Y0M0DT2H2M11S = **2010-11-07 03:16:55 EST** +test_time_fall_type2_redost_type3_post: ADD: 2010-11-07 01:14:44 EST + P+0Y0M1DT18H45M15S = **2010-11-08 19:59:59 EST** +test_time_fall_type2_st_type3_prev: ADD: 2010-11-07 03:16:55 EST + P-0Y0M0DT9H38M27S = **2010-11-06 18:38:28 EDT** +test_time_fall_type2_st_type3_dt: ADD: 2010-11-07 03:16:55 EST + P-0Y0M0DT4H6M35S = **2010-11-07 00:10:20 EDT** +test_time_fall_type2_st_type3_redodt: ADD: 2010-11-07 03:16:55 EST + P-0Y0M0DT3H4M22S = **2010-11-07 01:12:33 EDT** +test_time_fall_type2_st_type3_redost: ADD: 2010-11-07 03:16:55 EST + P-0Y0M0DT2H2M11S = **2010-11-07 01:14:44 EST** +test_time_fall_type2_st_type3_st: ADD: 2010-11-07 03:16:55 EST + P+0Y0M0DT2H3M1S = **2010-11-07 05:19:56 EST** +test_time_fall_type2_st_type3_post: ADD: 2010-11-07 03:16:55 EST + P+0Y0M1DT16H43M4S = **2010-11-08 19:59:59 EST** +test_time_fall_type2_post_type3_prev: ADD: 2010-11-08 19:59:59 EST + P-0Y0M2DT1H21M31S = **2010-11-06 18:38:28 EDT** +test_time_fall_type2_post_type3_dt: ADD: 2010-11-08 19:59:59 EST + P-0Y0M1DT20H49M39S = **2010-11-07 00:10:20 EDT** +test_time_fall_type2_post_type3_redodt: ADD: 2010-11-08 19:59:59 EST + P-0Y0M1DT19H47M26S = **2010-11-07 01:12:33 EDT** +test_time_fall_type2_post_type3_redost: ADD: 2010-11-08 19:59:59 EST + P-0Y0M1DT18H45M15S = **2010-11-07 01:14:44 EST** +test_time_fall_type2_post_type3_st: ADD: 2010-11-08 19:59:59 EST + P-0Y0M1DT16H43M4S = **2010-11-07 03:16:55 EST** +test_time_fall_type2_post_type3_post: ADD: 2010-11-08 18:57:55 EST + P+0Y0M0DT1H2M4S = **2010-11-08 19:59:59 EST** +test_time_fall_type2_dtsec_type3_stsec: ADD: 2010-11-07 01:59:59 EDT + P+0Y0M0DT0H0M1S = **2010-11-07 01:00:00 EST** +test_time_fall_type2_stsec_type3_dtsec: ADD: 2010-11-07 01:00:00 EST + P-0Y0M0DT0H0M1S = **2010-11-07 01:59:59 EDT** diff --git a/ext/date/tests/DateTime_add-fall-type3-type2.phpt b/ext/date/tests/DateTime_add-fall-type3-type2.phpt new file mode 100644 index 000000000..0588cbf28 --- /dev/null +++ b/ext/date/tests/DateTime_add-fall-type3-type2.phpt @@ -0,0 +1,53 @@ +--TEST-- +DateTime::add() -- fall type3 type2 +--CREDITS-- +Daniel Convissor <danielc@php.net> +--XFAIL-- +Various bugs exist +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_ADD); +require 'DateTime_data-fall-type3-type2.inc'; + +?> +--EXPECT-- +test_time_fall_type3_prev_type2_prev: ADD: 2010-10-04 02:18:48 EDT + P+0Y1M2DT16H19M40S = **2010-11-06 18:38:28 EDT** +test_time_fall_type3_prev_type2_dt: ADD: 2010-11-06 18:38:28 EDT + P+0Y0M0DT5H31M52S = **2010-11-07 00:10:20 EDT** +test_time_fall_type3_prev_type2_redodt: ADD: 2010-11-06 18:38:28 EDT + P+0Y0M0DT6H34M5S = **2010-11-07 01:12:33 EDT** +test_time_fall_type3_prev_type2_redost: ADD: 2010-11-06 18:38:28 EDT + P+0Y0M0DT7H36M16S = **2010-11-07 01:14:44 EST** +test_time_fall_type3_prev_type2_st: ADD: 2010-11-06 18:38:28 EDT + P+0Y0M0DT9H38M27S = **2010-11-07 03:16:55 EST** +test_time_fall_type3_prev_type2_post: ADD: 2010-11-06 18:38:28 EDT + P+0Y0M2DT1H21M31S = **2010-11-08 19:59:59 EST** +test_time_fall_type3_dt_type2_prev: ADD: 2010-11-07 00:10:20 EDT + P-0Y0M0DT5H31M52S = **2010-11-06 18:38:28 EDT** +test_time_fall_type3_dt_type2_dt: ADD: 2010-11-07 00:10:20 EDT + P+0Y0M0DT0H5M15S = **2010-11-07 00:15:35 EDT** +test_time_fall_type3_dt_type2_redodt: ADD: 2010-11-07 00:10:20 EDT + P+0Y0M0DT1H2M13S = **2010-11-07 01:12:33 EDT** +test_time_fall_type3_dt_type2_redost: ADD: 2010-11-07 00:10:20 EDT + P+0Y0M0DT2H4M24S = **2010-11-07 01:14:44 EST** +test_time_fall_type3_dt_type2_st: ADD: 2010-11-07 00:10:20 EDT + P+0Y0M0DT4H6M35S = **2010-11-07 03:16:55 EST** +test_time_fall_type3_dt_type2_post: ADD: 2010-11-07 00:10:20 EDT + P+0Y0M1DT20H49M39S = **2010-11-08 19:59:59 EST** +test_time_fall_type3_redodt_type2_prev: ADD: 2010-11-07 01:12:33 EDT + P-0Y0M0DT6H34M5S = **2010-11-06 18:38:28 EDT** +test_time_fall_type3_redodt_type2_dt: ADD: 2010-11-07 01:12:33 EDT + P-0Y0M0DT1H2M13S = **2010-11-07 00:10:20 EDT** +test_time_fall_type3_redodt_type2_redodt: ADD: 2010-11-07 01:12:33 EDT + P+0Y0M0DT0H3M2S = **2010-11-07 01:15:35 EDT** +test_time_fall_type3_redodt_type2_redost: ADD: 2010-11-07 01:12:33 EDT + P+0Y0M0DT1H2M11S = **2010-11-07 01:14:44 EST** +test_time_fall_type3_redodt_type2_st: ADD: 2010-11-07 01:12:33 EDT + P+0Y0M0DT3H4M22S = **2010-11-07 03:16:55 EST** +test_time_fall_type3_redodt_type2_post: ADD: 2010-11-07 01:12:33 EDT + P+0Y0M1DT19H47M26S = **2010-11-08 19:59:59 EST** +test_time_fall_type3_redost_type2_prev: ADD: 2010-11-07 01:14:44 EST + P-0Y0M0DT7H36M16S = **2010-11-06 18:38:28 EDT** +test_time_fall_type3_redost_type2_dt: ADD: 2010-11-07 01:14:44 EST + P-0Y0M0DT2H4M24S = **2010-11-07 00:10:20 EDT** +test_time_fall_type3_redost_type2_redodt: ADD: 2010-11-07 01:14:44 EST + P-0Y0M0DT1H2M11S = **2010-11-07 01:12:33 EDT** +test_time_fall_type3_redost_type2_redost: ADD: 2010-11-07 01:14:44 EST + P+0Y0M0DT0H2M10S = **2010-11-07 01:16:54 EST** +test_time_fall_type3_redost_type2_st: ADD: 2010-11-07 01:14:44 EST + P+0Y0M0DT2H2M11S = **2010-11-07 03:16:55 EST** +test_time_fall_type3_redost_type2_post: ADD: 2010-11-07 01:14:44 EST + P+0Y0M1DT18H45M15S = **2010-11-08 19:59:59 EST** +test_time_fall_type3_st_type2_prev: ADD: 2010-11-07 03:16:55 EST + P-0Y0M0DT9H38M27S = **2010-11-06 18:38:28 EDT** +test_time_fall_type3_st_type2_dt: ADD: 2010-11-07 03:16:55 EST + P-0Y0M0DT4H6M35S = **2010-11-07 00:10:20 EDT** +test_time_fall_type3_st_type2_redodt: ADD: 2010-11-07 03:16:55 EST + P-0Y0M0DT3H4M22S = **2010-11-07 01:12:33 EDT** +test_time_fall_type3_st_type2_redost: ADD: 2010-11-07 03:16:55 EST + P-0Y0M0DT2H2M11S = **2010-11-07 01:14:44 EST** +test_time_fall_type3_st_type2_st: ADD: 2010-11-07 03:16:55 EST + P+0Y0M0DT2H3M1S = **2010-11-07 05:19:56 EST** +test_time_fall_type3_st_type2_post: ADD: 2010-11-07 03:16:55 EST + P+0Y0M1DT16H43M4S = **2010-11-08 19:59:59 EST** +test_time_fall_type3_post_type2_prev: ADD: 2010-11-08 19:59:59 EST + P-0Y0M2DT1H21M31S = **2010-11-06 18:38:28 EDT** +test_time_fall_type3_post_type2_dt: ADD: 2010-11-08 19:59:59 EST + P-0Y0M1DT20H49M39S = **2010-11-07 00:10:20 EDT** +test_time_fall_type3_post_type2_redodt: ADD: 2010-11-08 19:59:59 EST + P-0Y0M1DT19H47M26S = **2010-11-07 01:12:33 EDT** +test_time_fall_type3_post_type2_redost: ADD: 2010-11-08 19:59:59 EST + P-0Y0M1DT18H45M15S = **2010-11-07 01:14:44 EST** +test_time_fall_type3_post_type2_st: ADD: 2010-11-08 19:59:59 EST + P-0Y0M1DT16H43M4S = **2010-11-07 03:16:55 EST** +test_time_fall_type3_post_type2_post: ADD: 2010-11-08 18:57:55 EST + P+0Y0M0DT1H2M4S = **2010-11-08 19:59:59 EST** +test_time_fall_type3_dtsec_type2_stsec: ADD: 2010-11-07 01:59:59 EDT + P+0Y0M0DT0H0M1S = **2010-11-07 01:00:00 EST** +test_time_fall_type3_stsec_type2_dtsec: ADD: 2010-11-07 01:00:00 EST + P-0Y0M0DT0H0M1S = **2010-11-07 01:59:59 EDT** diff --git a/ext/date/tests/DateTime_add-fall-type3-type3.phpt b/ext/date/tests/DateTime_add-fall-type3-type3.phpt new file mode 100644 index 000000000..24315529a --- /dev/null +++ b/ext/date/tests/DateTime_add-fall-type3-type3.phpt @@ -0,0 +1,53 @@ +--TEST-- +DateTime::add() -- fall type3 type3 +--CREDITS-- +Daniel Convissor <danielc@php.net> +--XFAIL-- +Various bugs exist +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_ADD); +require 'DateTime_data-fall-type3-type3.inc'; + +?> +--EXPECT-- +test_time_fall_type3_prev_type3_prev: ADD: 2010-10-04 02:18:48 EDT + P+0Y1M2DT16H19M40S = **2010-11-06 18:38:28 EDT** +test_time_fall_type3_prev_type3_dt: ADD: 2010-11-06 18:38:28 EDT + P+0Y0M0DT5H31M52S = **2010-11-07 00:10:20 EDT** +test_time_fall_type3_prev_type3_redodt: ADD: 2010-11-06 18:38:28 EDT + P+0Y0M0DT6H34M5S = **2010-11-07 01:12:33 EDT** +test_time_fall_type3_prev_type3_redost: ADD: 2010-11-06 18:38:28 EDT + P+0Y0M0DT7H36M16S = **2010-11-07 01:14:44 EST** +test_time_fall_type3_prev_type3_st: ADD: 2010-11-06 18:38:28 EDT + P+0Y0M0DT9H38M27S = **2010-11-07 03:16:55 EST** +test_time_fall_type3_prev_type3_post: ADD: 2010-11-06 18:38:28 EDT + P+0Y0M2DT1H21M31S = **2010-11-08 19:59:59 EST** +test_time_fall_type3_dt_type3_prev: ADD: 2010-11-07 00:10:20 EDT + P-0Y0M0DT5H31M52S = **2010-11-06 18:38:28 EDT** +test_time_fall_type3_dt_type3_dt: ADD: 2010-11-07 00:10:20 EDT + P+0Y0M0DT0H5M15S = **2010-11-07 00:15:35 EDT** +test_time_fall_type3_dt_type3_redodt: ADD: 2010-11-07 00:10:20 EDT + P+0Y0M0DT1H2M13S = **2010-11-07 01:12:33 EDT** +test_time_fall_type3_dt_type3_redost: ADD: 2010-11-07 00:10:20 EDT + P+0Y0M0DT2H4M24S = **2010-11-07 01:14:44 EST** +test_time_fall_type3_dt_type3_st: ADD: 2010-11-07 00:10:20 EDT + P+0Y0M0DT4H6M35S = **2010-11-07 03:16:55 EST** +test_time_fall_type3_dt_type3_post: ADD: 2010-11-07 00:10:20 EDT + P+0Y0M1DT20H49M39S = **2010-11-08 19:59:59 EST** +test_time_fall_type3_redodt_type3_prev: ADD: 2010-11-07 01:12:33 EDT + P-0Y0M0DT6H34M5S = **2010-11-06 18:38:28 EDT** +test_time_fall_type3_redodt_type3_dt: ADD: 2010-11-07 01:12:33 EDT + P-0Y0M0DT1H2M13S = **2010-11-07 00:10:20 EDT** +test_time_fall_type3_redodt_type3_redodt: ADD: 2010-11-07 01:12:33 EDT + P+0Y0M0DT0H3M2S = **2010-11-07 01:15:35 EDT** +test_time_fall_type3_redodt_type3_redost: ADD: 2010-11-07 01:12:33 EDT + P+0Y0M0DT1H2M11S = **2010-11-07 01:14:44 EST** +test_time_fall_type3_redodt_type3_st: ADD: 2010-11-07 01:12:33 EDT + P+0Y0M0DT3H4M22S = **2010-11-07 03:16:55 EST** +test_time_fall_type3_redodt_type3_post: ADD: 2010-11-07 01:12:33 EDT + P+0Y0M1DT19H47M26S = **2010-11-08 19:59:59 EST** +test_time_fall_type3_redost_type3_prev: ADD: 2010-11-07 01:14:44 EST + P-0Y0M0DT7H36M16S = **2010-11-06 18:38:28 EDT** +test_time_fall_type3_redost_type3_dt: ADD: 2010-11-07 01:14:44 EST + P-0Y0M0DT2H4M24S = **2010-11-07 00:10:20 EDT** +test_time_fall_type3_redost_type3_redodt: ADD: 2010-11-07 01:14:44 EST + P-0Y0M0DT1H2M11S = **2010-11-07 01:12:33 EDT** +test_time_fall_type3_redost_type3_redost: ADD: 2010-11-07 01:14:44 EST + P+0Y0M0DT0H2M10S = **2010-11-07 01:16:54 EST** +test_time_fall_type3_redost_type3_st: ADD: 2010-11-07 01:14:44 EST + P+0Y0M0DT2H2M11S = **2010-11-07 03:16:55 EST** +test_time_fall_type3_redost_type3_post: ADD: 2010-11-07 01:14:44 EST + P+0Y0M1DT18H45M15S = **2010-11-08 19:59:59 EST** +test_time_fall_type3_st_type3_prev: ADD: 2010-11-07 03:16:55 EST + P-0Y0M0DT9H38M27S = **2010-11-06 18:38:28 EDT** +test_time_fall_type3_st_type3_dt: ADD: 2010-11-07 03:16:55 EST + P-0Y0M0DT4H6M35S = **2010-11-07 00:10:20 EDT** +test_time_fall_type3_st_type3_redodt: ADD: 2010-11-07 03:16:55 EST + P-0Y0M0DT3H4M22S = **2010-11-07 01:12:33 EDT** +test_time_fall_type3_st_type3_redost: ADD: 2010-11-07 03:16:55 EST + P-0Y0M0DT2H2M11S = **2010-11-07 01:14:44 EST** +test_time_fall_type3_st_type3_st: ADD: 2010-11-07 03:16:55 EST + P+0Y0M0DT2H3M1S = **2010-11-07 05:19:56 EST** +test_time_fall_type3_st_type3_post: ADD: 2010-11-07 03:16:55 EST + P+0Y0M1DT16H43M4S = **2010-11-08 19:59:59 EST** +test_time_fall_type3_post_type3_prev: ADD: 2010-11-08 19:59:59 EST + P-0Y0M2DT1H21M31S = **2010-11-06 18:38:28 EDT** +test_time_fall_type3_post_type3_dt: ADD: 2010-11-08 19:59:59 EST + P-0Y0M1DT20H49M39S = **2010-11-07 00:10:20 EDT** +test_time_fall_type3_post_type3_redodt: ADD: 2010-11-08 19:59:59 EST + P-0Y0M1DT19H47M26S = **2010-11-07 01:12:33 EDT** +test_time_fall_type3_post_type3_redost: ADD: 2010-11-08 19:59:59 EST + P-0Y0M1DT18H45M15S = **2010-11-07 01:14:44 EST** +test_time_fall_type3_post_type3_st: ADD: 2010-11-08 19:59:59 EST + P-0Y0M1DT16H43M4S = **2010-11-07 03:16:55 EST** +test_time_fall_type3_post_type3_post: ADD: 2010-11-08 18:57:55 EST + P+0Y0M0DT1H2M4S = **2010-11-08 19:59:59 EST** +test_time_fall_type3_dtsec_type3_stsec: ADD: 2010-11-07 01:59:59 EDT + P+0Y0M0DT0H0M1S = **2010-11-07 01:00:00 EST** +test_time_fall_type3_stsec_type3_dtsec: ADD: 2010-11-07 01:00:00 EST + P-0Y0M0DT0H0M1S = **2010-11-07 01:59:59 EDT** diff --git a/ext/date/tests/DateTime_add-february.phpt b/ext/date/tests/DateTime_add-february.phpt new file mode 100644 index 000000000..8e47c0edd --- /dev/null +++ b/ext/date/tests/DateTime_add-february.phpt @@ -0,0 +1,77 @@ +--TEST-- +DateTime::add() -- february +--CREDITS-- +Daniel Convissor <danielc@php.net> +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_ADD); +require 'DateTime_data-february.inc'; + +?> +--EXPECT-- +test_bug_49081__1: ADD: 2010-03-01 00:00:00 EST + P+0Y0M30DT0H0M0S = **2010-03-31 00:00:00 EDT** +test_bug_49081__2: ADD: 2010-03-01 00:00:00 EST + P+0Y1M0DT0H0M0S = **2010-04-01 00:00:00 EDT** +test_bug_49081__3: ADD: 2010-03-31 00:00:00 EDT + P+0Y0M1DT0H0M0S = **2010-04-01 00:00:00 EDT** +test_bug_49081__4: ADD: 2010-03-31 00:00:00 EDT + P+0Y0M29DT0H0M0S = **2010-04-29 00:00:00 EDT** +test_bug_49081__5: ADD: 2010-03-31 00:00:00 EDT + P+0Y0M30DT0H0M0S = **2010-04-30 00:00:00 EDT** +test_bug_49081__6: ADD: 2010-03-30 00:00:00 EDT + P+0Y1M0DT0H0M0S = **2010-04-30 00:00:00 EDT** +test_bug_49081__7: ADD: 2010-03-29 00:00:00 EDT + P+0Y1M1DT0H0M0S = **2010-04-30 00:00:00 EDT** +test_bug_49081__8: ADD: 2010-01-01 00:00:00 EST + P+0Y0M28DT0H0M0S = **2010-01-29 00:00:00 EST** +test_bug_49081__9: ADD: 2010-01-01 00:00:00 EST + P+0Y0M29DT0H0M0S = **2010-01-30 00:00:00 EST** +test_bug_49081__10: ADD: 2010-01-01 00:00:00 EST + P+0Y0M30DT0H0M0S = **2010-01-31 00:00:00 EST** +test_bug_49081__11: ADD: 2010-01-01 00:00:00 EST + P+0Y1M0DT0H0M0S = **2010-02-01 00:00:00 EST** +test_bug_49081__12: ADD: 2010-01-31 00:00:00 EST + P+0Y0M1DT0H0M0S = **2010-02-01 00:00:00 EST** +test_bug_49081__13: ADD: 2010-01-31 00:00:00 EST + P+0Y0M27DT0H0M0S = **2010-02-27 00:00:00 EST** +test_bug_49081__14: ADD: 2010-01-31 00:00:00 EST + P+0Y0M28DT0H0M0S = **2010-02-28 00:00:00 EST** +test_bug_49081__15: ADD: 2010-01-30 00:00:00 EST + P+0Y0M29DT0H0M0S = **2010-02-28 00:00:00 EST** +test_bug_49081__16: ADD: 2010-01-29 00:00:00 EST + P+0Y0M30DT0H0M0S = **2010-02-28 00:00:00 EST** +test_bug_49081__17: ADD: 2010-01-28 00:00:00 EST + P+0Y1M0DT0H0M0S = **2010-02-28 00:00:00 EST** +test_bug_49081__18: ADD: 2010-01-27 00:00:00 EST + P+0Y1M1DT0H0M0S = **2010-02-28 00:00:00 EST** +test_bug_49081__19: ADD: 2010-01-01 00:00:00 EST + P+0Y2M0DT0H0M0S = **2010-03-01 00:00:00 EST** +test_bug_49081__20: ADD: 2010-01-31 00:00:00 EST + P+0Y0M29DT0H0M0S = **2010-03-01 00:00:00 EST** +test_bug_49081__21: ADD: 2010-01-31 00:00:00 EST + P+0Y1M24DT0H0M0S = **2010-03-27 00:00:00 EDT** +test_bug_49081__22: ADD: 2010-01-31 00:00:00 EST + P+0Y1M25DT0H0M0S = **2010-03-28 00:00:00 EDT** +test_bug_49081__23: ADD: 2010-01-31 00:00:00 EST + P+0Y1M26DT0H0M0S = **2010-03-29 00:00:00 EDT** +test_bug_49081__24: ADD: 2010-01-31 00:00:00 EST + P+0Y1M27DT0H0M0S = **2010-03-30 00:00:00 EDT** +test_bug_49081__25: ADD: 2010-01-31 00:00:00 EST + P+0Y2M0DT0H0M0S = **2010-03-31 00:00:00 EDT** +test_bug_49081__26: ADD: 2010-01-30 00:00:00 EST + P+0Y2M1DT0H0M0S = **2010-03-31 00:00:00 EDT** +test_bug_49081__27: ADD: 2009-01-01 00:00:00 EST + P+0Y0M30DT0H0M0S = **2009-01-31 00:00:00 EST** +test_bug_49081__28: ADD: 2010-02-28 00:00:00 EST + P+0Y0M27DT0H0M0S = **2010-03-27 00:00:00 EDT** +test_bug_49081__29: ADD: 2010-02-28 00:00:00 EST + P+0Y1M0DT0H0M0S = **2010-03-28 00:00:00 EDT** +test_bug_49081__30: ADD: 2010-02-28 00:00:00 EST + P+0Y1M1DT0H0M0S = **2010-03-29 00:00:00 EDT** +test_bug_49081__31: ADD: 2010-02-27 00:00:00 EST + P+0Y1M0DT0H0M0S = **2010-03-27 00:00:00 EDT** +test_bug_49081__32: ADD: 2010-02-26 00:00:00 EST + P+0Y1M1DT0H0M0S = **2010-03-27 00:00:00 EDT** +test_bug_49081_negative__1: ADD: 2010-03-31 00:00:00 EDT + P-0Y0M30DT0H0M0S = **2010-03-01 00:00:00 EST** +test_bug_49081_negative__2: ADD: 2010-04-01 00:00:00 EDT + P-0Y1M0DT0H0M0S = **2010-03-01 00:00:00 EST** +test_bug_49081_negative__3: ADD: 2010-04-01 00:00:00 EDT + P-0Y0M1DT0H0M0S = **2010-03-31 00:00:00 EDT** +test_bug_49081_negative__4: ADD: 2010-04-29 00:00:00 EDT + P-0Y0M29DT0H0M0S = **2010-03-31 00:00:00 EDT** +test_bug_49081_negative__5: ADD: 2010-04-30 00:00:00 EDT + P-0Y0M30DT0H0M0S = **2010-03-31 00:00:00 EDT** +test_bug_49081_negative__6: ADD: 2010-04-30 00:00:00 EDT + P-0Y1M0DT0H0M0S = **2010-03-30 00:00:00 EDT** +test_bug_49081_negative__7: ADD: 2010-04-30 00:00:00 EDT + P-0Y1M1DT0H0M0S = **2010-03-29 00:00:00 EDT** +test_bug_49081_negative__8: ADD: 2010-01-29 00:00:00 EST + P-0Y0M28DT0H0M0S = **2010-01-01 00:00:00 EST** +test_bug_49081_negative__9: ADD: 2010-01-30 00:00:00 EST + P-0Y0M29DT0H0M0S = **2010-01-01 00:00:00 EST** +test_bug_49081_negative__10: ADD: 2010-01-31 00:00:00 EST + P-0Y0M30DT0H0M0S = **2010-01-01 00:00:00 EST** +test_bug_49081_negative__11: ADD: 2010-02-01 00:00:00 EST + P-0Y1M0DT0H0M0S = **2010-01-01 00:00:00 EST** +test_bug_49081_negative__12: ADD: 2010-02-01 00:00:00 EST + P-0Y0M1DT0H0M0S = **2010-01-31 00:00:00 EST** +test_bug_49081_negative__13: ADD: 2010-02-27 00:00:00 EST + P-0Y0M27DT0H0M0S = **2010-01-31 00:00:00 EST** +test_bug_49081_negative__14: ADD: 2010-02-28 00:00:00 EST + P-0Y0M28DT0H0M0S = **2010-01-31 00:00:00 EST** +test_bug_49081_negative__15: ADD: 2010-02-28 00:00:00 EST + P-0Y0M29DT0H0M0S = **2010-01-30 00:00:00 EST** +test_bug_49081_negative__16: ADD: 2010-02-28 00:00:00 EST + P-0Y0M30DT0H0M0S = **2010-01-29 00:00:00 EST** +test_bug_49081_negative__17: ADD: 2010-02-28 00:00:00 EST + P-0Y1M0DT0H0M0S = **2010-01-28 00:00:00 EST** +test_bug_49081_negative__18: ADD: 2010-02-28 00:00:00 EST + P-0Y1M1DT0H0M0S = **2010-01-27 00:00:00 EST** +test_bug_49081_negative__19: ADD: 2010-03-01 00:00:00 EST + P-0Y2M0DT0H0M0S = **2010-01-01 00:00:00 EST** +test_bug_49081_negative__20: ADD: 2010-03-01 00:00:00 EST + P-0Y1M1DT0H0M0S = **2010-01-31 00:00:00 EST** +test_bug_49081_negative__21: ADD: 2010-03-27 00:00:00 EDT + P-0Y1M27DT0H0M0S = **2010-01-31 00:00:00 EST** +test_bug_49081_negative__22: ADD: 2010-03-28 00:00:00 EDT + P-0Y1M28DT0H0M0S = **2010-01-31 00:00:00 EST** +test_bug_49081_negative__23: ADD: 2010-03-29 00:00:00 EDT + P-0Y1M29DT0H0M0S = **2010-01-31 00:00:00 EST** +test_bug_49081_negative__24: ADD: 2010-03-30 00:00:00 EDT + P-0Y1M30DT0H0M0S = **2010-01-31 00:00:00 EST** +test_bug_49081_negative__25: ADD: 2010-03-31 00:00:00 EDT + P-0Y2M0DT0H0M0S = **2010-01-31 00:00:00 EST** +test_bug_49081_negative__26: ADD: 2010-03-31 00:00:00 EDT + P-0Y2M1DT0H0M0S = **2010-01-30 00:00:00 EST** +test_bug_49081_negative__27: ADD: 2009-01-31 00:00:00 EST + P-0Y0M30DT0H0M0S = **2009-01-01 00:00:00 EST** +test_bug_49081_negative__28: ADD: 2010-03-27 00:00:00 EDT + P-0Y0M27DT0H0M0S = **2010-02-28 00:00:00 EST** +test_bug_49081_negative__29: ADD: 2010-03-28 00:00:00 EDT + P-0Y1M0DT0H0M0S = **2010-02-28 00:00:00 EST** +test_bug_49081_negative__30: ADD: 2010-03-29 00:00:00 EDT + P-0Y1M1DT0H0M0S = **2010-02-28 00:00:00 EST** +test_bug_49081_negative__31: ADD: 2010-03-27 00:00:00 EDT + P-0Y1M0DT0H0M0S = **2010-02-27 00:00:00 EST** +test_bug_49081_negative__32: ADD: 2010-03-27 00:00:00 EDT + P-0Y1M1DT0H0M0S = **2010-02-26 00:00:00 EST** diff --git a/ext/date/tests/DateTime_add-massive.phpt b/ext/date/tests/DateTime_add-massive.phpt new file mode 100644 index 000000000..ca5bef985 --- /dev/null +++ b/ext/date/tests/DateTime_add-massive.phpt @@ -0,0 +1,15 @@ +--TEST-- +DateTime::add() -- massive +--CREDITS-- +Daniel Convissor <danielc@php.net> +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_ADD); +require 'DateTime_data-massive.inc'; + +?> +--EXPECT-- +test_massive_positive: ADD: -333333-01-01 16:18:02 EST + P+666666Y0M0DT0H0M0S = **333333-01-01 16:18:02 EST** +test_massive_negative: ADD: 333333-01-01 16:18:02 EST + P-666666Y0M0DT0H0M0S = **-333333-01-01 16:18:02 EST** diff --git a/ext/date/tests/DateTime_add-spring-type2-type2.phpt b/ext/date/tests/DateTime_add-spring-type2-type2.phpt new file mode 100644 index 000000000..b64c27476 --- /dev/null +++ b/ext/date/tests/DateTime_add-spring-type2-type2.phpt @@ -0,0 +1,33 @@ +--TEST-- +DateTime::add() -- spring type2 type2 +--CREDITS-- +Daniel Convissor <danielc@php.net> +--XFAIL-- +Various bugs exist +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_ADD); +require 'DateTime_data-spring-type2-type2.inc'; + +?> +--EXPECT-- +test_time_spring_type2_prev_type2_prev: ADD: 2010-02-11 02:18:48 EST + P+0Y1M2DT16H19M40S = **2010-03-13 18:38:28 EST** +test_time_spring_type2_prev_type2_st: ADD: 2010-03-13 18:38:28 EST + P+0Y0M0DT5H31M52S = **2010-03-14 00:10:20 EST** +test_time_spring_type2_prev_type2_dt: ADD: 2010-03-13 18:38:28 EST + P+0Y0M0DT7H38M27S = **2010-03-14 03:16:55 EDT** +test_time_spring_type2_prev_type2_post: ADD: 2010-03-13 18:38:28 EST + P+0Y0M2DT1H21M31S = **2010-03-15 19:59:59 EDT** +test_time_spring_type2_st_type2_prev: ADD: 2010-03-14 00:10:20 EST + P-0Y0M0DT5H31M52S = **2010-03-13 18:38:28 EST** +test_time_spring_type2_st_type2_st: ADD: 2010-03-14 00:10:20 EST + P+0Y0M0DT0H5M15S = **2010-03-14 00:15:35 EST** +test_time_spring_type2_st_type2_dt: ADD: 2010-03-14 00:10:20 EST + P+0Y0M0DT2H6M35S = **2010-03-14 03:16:55 EDT** +test_time_spring_type2_st_type2_post: ADD: 2010-03-14 00:10:20 EST + P+0Y0M1DT18H49M39S = **2010-03-15 19:59:59 EDT** +test_time_spring_type2_dt_type2_prev: ADD: 2010-03-14 03:16:55 EDT + P-0Y0M0DT7H38M27S = **2010-03-13 18:38:28 EST** +test_time_spring_type2_dt_type2_st: ADD: 2010-03-14 03:16:55 EDT + P-0Y0M0DT2H6M35S = **2010-03-14 00:10:20 EST** +test_time_spring_type2_dt_type2_dt: ADD: 2010-03-14 03:16:55 EDT + P+0Y0M0DT2H3M1S = **2010-03-14 05:19:56 EDT** +test_time_spring_type2_dt_type2_post: ADD: 2010-03-14 03:16:55 EDT + P+0Y0M1DT16H43M4S = **2010-03-15 19:59:59 EDT** +test_time_spring_type2_post_type2_prev: ADD: 2010-03-15 19:59:59 EDT + P-0Y0M2DT1H21M31S = **2010-03-13 18:38:28 EST** +test_time_spring_type2_post_type2_st: ADD: 2010-03-15 19:59:59 EDT + P-0Y0M1DT18H49M39S = **2010-03-14 00:10:20 EST** +test_time_spring_type2_post_type2_dt: ADD: 2010-03-15 19:59:59 EDT + P-0Y0M1DT16H43M4S = **2010-03-14 03:16:55 EDT** +test_time_spring_type2_post_type2_post: ADD: 2010-03-15 18:57:55 EDT + P+0Y0M0DT1H2M4S = **2010-03-15 19:59:59 EDT** +test_time_spring_type2_stsec_type2_dtsec: ADD: 2010-03-13 01:59:59 EST + P+0Y0M0DT0H0M1S = **2010-03-15 03:00:00 EDT** +test_time_spring_type2_dtsec_type2_stsec: ADD: 2010-03-15 03:00:00 EDT + P-0Y0M0DT0H0M1S = **2010-03-15 01:59:59 EST** diff --git a/ext/date/tests/DateTime_add-spring-type2-type3.phpt b/ext/date/tests/DateTime_add-spring-type2-type3.phpt new file mode 100644 index 000000000..5544651f2 --- /dev/null +++ b/ext/date/tests/DateTime_add-spring-type2-type3.phpt @@ -0,0 +1,33 @@ +--TEST-- +DateTime::add() -- spring type2 type3 +--CREDITS-- +Daniel Convissor <danielc@php.net> +--XFAIL-- +Various bugs exist +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_ADD); +require 'DateTime_data-spring-type2-type3.inc'; + +?> +--EXPECT-- +test_time_spring_type2_prev_type3_prev: ADD: 2010-02-11 02:18:48 EST + P+0Y1M2DT16H19M40S = **2010-03-13 18:38:28 EST** +test_time_spring_type2_prev_type3_st: ADD: 2010-03-13 18:38:28 EST + P+0Y0M0DT5H31M52S = **2010-03-14 00:10:20 EST** +test_time_spring_type2_prev_type3_dt: ADD: 2010-03-13 18:38:28 EST + P+0Y0M0DT7H38M27S = **2010-03-14 03:16:55 EDT** +test_time_spring_type2_prev_type3_post: ADD: 2010-03-13 18:38:28 EST + P+0Y0M2DT1H21M31S = **2010-03-15 19:59:59 EDT** +test_time_spring_type2_st_type3_prev: ADD: 2010-03-14 00:10:20 EST + P-0Y0M0DT5H31M52S = **2010-03-13 18:38:28 EST** +test_time_spring_type2_st_type3_st: ADD: 2010-03-14 00:10:20 EST + P+0Y0M0DT0H5M15S = **2010-03-14 00:15:35 EST** +test_time_spring_type2_st_type3_dt: ADD: 2010-03-14 00:10:20 EST + P+0Y0M0DT2H6M35S = **2010-03-14 03:16:55 EDT** +test_time_spring_type2_st_type3_post: ADD: 2010-03-14 00:10:20 EST + P+0Y0M1DT18H49M39S = **2010-03-15 19:59:59 EDT** +test_time_spring_type2_dt_type3_prev: ADD: 2010-03-14 03:16:55 EDT + P-0Y0M0DT7H38M27S = **2010-03-13 18:38:28 EST** +test_time_spring_type2_dt_type3_st: ADD: 2010-03-14 03:16:55 EDT + P-0Y0M0DT2H6M35S = **2010-03-14 00:10:20 EST** +test_time_spring_type2_dt_type3_dt: ADD: 2010-03-14 03:16:55 EDT + P+0Y0M0DT2H3M1S = **2010-03-14 05:19:56 EDT** +test_time_spring_type2_dt_type3_post: ADD: 2010-03-14 03:16:55 EDT + P+0Y0M1DT16H43M4S = **2010-03-15 19:59:59 EDT** +test_time_spring_type2_post_type3_prev: ADD: 2010-03-15 19:59:59 EDT + P-0Y0M2DT1H21M31S = **2010-03-13 18:38:28 EST** +test_time_spring_type2_post_type3_st: ADD: 2010-03-15 19:59:59 EDT + P-0Y0M1DT18H49M39S = **2010-03-14 00:10:20 EST** +test_time_spring_type2_post_type3_dt: ADD: 2010-03-15 19:59:59 EDT + P-0Y0M1DT16H43M4S = **2010-03-14 03:16:55 EDT** +test_time_spring_type2_post_type3_post: ADD: 2010-03-15 18:57:55 EDT + P+0Y0M0DT1H2M4S = **2010-03-15 19:59:59 EDT** +test_time_spring_type2_stsec_type3_dtsec: ADD: 2010-03-13 01:59:59 EST + P+0Y0M0DT0H0M1S = **2010-03-15 03:00:00 EDT** +test_time_spring_type2_dtsec_type3_stsec: ADD: 2010-03-15 03:00:00 EDT + P-0Y0M0DT0H0M1S = **2010-03-15 01:59:59 EST** diff --git a/ext/date/tests/DateTime_add-spring-type3-type2.phpt b/ext/date/tests/DateTime_add-spring-type3-type2.phpt new file mode 100644 index 000000000..fe75a5c26 --- /dev/null +++ b/ext/date/tests/DateTime_add-spring-type3-type2.phpt @@ -0,0 +1,33 @@ +--TEST-- +DateTime::add() -- spring type3 type2 +--CREDITS-- +Daniel Convissor <danielc@php.net> +--XFAIL-- +Various bugs exist +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_ADD); +require 'DateTime_data-spring-type3-type2.inc'; + +?> +--EXPECT-- +test_time_spring_type3_prev_type2_prev: ADD: 2010-02-11 02:18:48 EST + P+0Y1M2DT16H19M40S = **2010-03-13 18:38:28 EST** +test_time_spring_type3_prev_type2_st: ADD: 2010-03-13 18:38:28 EST + P+0Y0M0DT5H31M52S = **2010-03-14 00:10:20 EST** +test_time_spring_type3_prev_type2_dt: ADD: 2010-03-13 18:38:28 EST + P+0Y0M0DT7H38M27S = **2010-03-14 03:16:55 EDT** +test_time_spring_type3_prev_type2_post: ADD: 2010-03-13 18:38:28 EST + P+0Y0M2DT1H21M31S = **2010-03-15 19:59:59 EDT** +test_time_spring_type3_st_type2_prev: ADD: 2010-03-14 00:10:20 EST + P-0Y0M0DT5H31M52S = **2010-03-13 18:38:28 EST** +test_time_spring_type3_st_type2_st: ADD: 2010-03-14 00:10:20 EST + P+0Y0M0DT0H5M15S = **2010-03-14 00:15:35 EST** +test_time_spring_type3_st_type2_dt: ADD: 2010-03-14 00:10:20 EST + P+0Y0M0DT2H6M35S = **2010-03-14 03:16:55 EDT** +test_time_spring_type3_st_type2_post: ADD: 2010-03-14 00:10:20 EST + P+0Y0M1DT18H49M39S = **2010-03-15 19:59:59 EDT** +test_time_spring_type3_dt_type2_prev: ADD: 2010-03-14 03:16:55 EDT + P-0Y0M0DT7H38M27S = **2010-03-13 18:38:28 EST** +test_time_spring_type3_dt_type2_st: ADD: 2010-03-14 03:16:55 EDT + P-0Y0M0DT2H6M35S = **2010-03-14 00:10:20 EST** +test_time_spring_type3_dt_type2_dt: ADD: 2010-03-14 03:16:55 EDT + P+0Y0M0DT2H3M1S = **2010-03-14 05:19:56 EDT** +test_time_spring_type3_dt_type2_post: ADD: 2010-03-14 03:16:55 EDT + P+0Y0M1DT16H43M4S = **2010-03-15 19:59:59 EDT** +test_time_spring_type3_post_type2_prev: ADD: 2010-03-15 19:59:59 EDT + P-0Y0M2DT1H21M31S = **2010-03-13 18:38:28 EST** +test_time_spring_type3_post_type2_st: ADD: 2010-03-15 19:59:59 EDT + P-0Y0M1DT18H49M39S = **2010-03-14 00:10:20 EST** +test_time_spring_type3_post_type2_dt: ADD: 2010-03-15 19:59:59 EDT + P-0Y0M1DT16H43M4S = **2010-03-14 03:16:55 EDT** +test_time_spring_type3_post_type2_post: ADD: 2010-03-15 18:57:55 EDT + P+0Y0M0DT1H2M4S = **2010-03-15 19:59:59 EDT** +test_time_spring_type3_stsec_type2_dtsec: ADD: 2010-03-13 01:59:59 EST + P+0Y0M0DT0H0M1S = **2010-03-15 03:00:00 EDT** +test_time_spring_type3_dtsec_type2_stsec: ADD: 2010-03-15 03:00:00 EDT + P-0Y0M0DT0H0M1S = **2010-03-15 01:59:59 EST** diff --git a/ext/date/tests/DateTime_add-spring-type3-type3.phpt b/ext/date/tests/DateTime_add-spring-type3-type3.phpt new file mode 100644 index 000000000..b2a5c3e81 --- /dev/null +++ b/ext/date/tests/DateTime_add-spring-type3-type3.phpt @@ -0,0 +1,33 @@ +--TEST-- +DateTime::add() -- spring type3 type3 +--CREDITS-- +Daniel Convissor <danielc@php.net> +--XFAIL-- +Various bugs exist +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_ADD); +require 'DateTime_data-spring-type3-type3.inc'; + +?> +--EXPECT-- +test_time_spring_type3_prev_type3_prev: ADD: 2010-02-11 02:18:48 EST + P+0Y1M2DT16H19M40S = **2010-03-13 18:38:28 EST** +test_time_spring_type3_prev_type3_st: ADD: 2010-03-13 18:38:28 EST + P+0Y0M0DT5H31M52S = **2010-03-14 00:10:20 EST** +test_time_spring_type3_prev_type3_dt: ADD: 2010-03-13 18:38:28 EST + P+0Y0M0DT7H38M27S = **2010-03-14 03:16:55 EDT** +test_time_spring_type3_prev_type3_post: ADD: 2010-03-13 18:38:28 EST + P+0Y0M2DT1H21M31S = **2010-03-15 19:59:59 EDT** +test_time_spring_type3_st_type3_prev: ADD: 2010-03-14 00:10:20 EST + P-0Y0M0DT5H31M52S = **2010-03-13 18:38:28 EST** +test_time_spring_type3_st_type3_st: ADD: 2010-03-14 00:10:20 EST + P+0Y0M0DT0H5M15S = **2010-03-14 00:15:35 EST** +test_time_spring_type3_st_type3_dt: ADD: 2010-03-14 00:10:20 EST + P+0Y0M0DT2H6M35S = **2010-03-14 03:16:55 EDT** +test_time_spring_type3_st_type3_post: ADD: 2010-03-14 00:10:20 EST + P+0Y0M1DT18H49M39S = **2010-03-15 19:59:59 EDT** +test_time_spring_type3_dt_type3_prev: ADD: 2010-03-14 03:16:55 EDT + P-0Y0M0DT7H38M27S = **2010-03-13 18:38:28 EST** +test_time_spring_type3_dt_type3_st: ADD: 2010-03-14 03:16:55 EDT + P-0Y0M0DT2H6M35S = **2010-03-14 00:10:20 EST** +test_time_spring_type3_dt_type3_dt: ADD: 2010-03-14 03:16:55 EDT + P+0Y0M0DT2H3M1S = **2010-03-14 05:19:56 EDT** +test_time_spring_type3_dt_type3_post: ADD: 2010-03-14 03:16:55 EDT + P+0Y0M1DT16H43M4S = **2010-03-15 19:59:59 EDT** +test_time_spring_type3_post_type3_prev: ADD: 2010-03-15 19:59:59 EDT + P-0Y0M2DT1H21M31S = **2010-03-13 18:38:28 EST** +test_time_spring_type3_post_type3_st: ADD: 2010-03-15 19:59:59 EDT + P-0Y0M1DT18H49M39S = **2010-03-14 00:10:20 EST** +test_time_spring_type3_post_type3_dt: ADD: 2010-03-15 19:59:59 EDT + P-0Y0M1DT16H43M4S = **2010-03-14 03:16:55 EDT** +test_time_spring_type3_post_type3_post: ADD: 2010-03-15 18:57:55 EDT + P+0Y0M0DT1H2M4S = **2010-03-15 19:59:59 EDT** +test_time_spring_type3_stsec_type3_dtsec: ADD: 2010-03-13 01:59:59 EST + P+0Y0M0DT0H0M1S = **2010-03-15 03:00:00 EDT** +test_time_spring_type3_dtsec_type3_stsec: ADD: 2010-03-15 03:00:00 EDT + P-0Y0M0DT0H0M1S = **2010-03-15 01:59:59 EST** diff --git a/ext/date/tests/DateTime_construct-dst-overlap.phpt b/ext/date/tests/DateTime_construct-dst-overlap.phpt new file mode 100644 index 000000000..05ed525ac --- /dev/null +++ b/ext/date/tests/DateTime_construct-dst-overlap.phpt @@ -0,0 +1,15 @@ +--TEST-- +DateTime::__construct() -- fall daylight/standard overlap +--CREDITS-- +Daniel Convissor <danielc@php.net> +--FILE-- +<?php + +date_default_timezone_set('America/New_York'); +// PHP defaults to Daylight Saving Time. Ensure consistency in future. +$d = new DateTime('2011-11-06 01:30:00'); +echo $d->format('P') . "\n"; + +?> +--EXPECT-- +-04:00 diff --git a/ext/date/tests/DateTime_data-absolute.inc b/ext/date/tests/DateTime_data-absolute.inc new file mode 100644 index 000000000..fa3acb88c --- /dev/null +++ b/ext/date/tests/DateTime_data-absolute.inc @@ -0,0 +1,24 @@ +<?php + +/* + * Note: test names match method names in a set of PHPUnit tests + * in a userland package. Please be so kind as to leave them. + */ + +date_default_timezone_set('America/New_York'); + + +/* + * Absolute + */ +echo "test_absolute_7: "; +examine_diff('2009-01-14', '2009-01-07', 'P+0Y0M7DT0H0M0S', 7, true); + +echo "test_absolute_negative_7: "; +examine_diff('2009-01-07', '2009-01-14', 'P+0Y0M7DT0H0M0S', 7, true); + +//14 - 7 = 7 +//7 + 7 = 14 +// +//7 - 14 = -7 +//14 - 7 = 7 diff --git a/ext/date/tests/DateTime_data-dates.inc b/ext/date/tests/DateTime_data-dates.inc new file mode 100644 index 000000000..be608dfcb --- /dev/null +++ b/ext/date/tests/DateTime_data-dates.inc @@ -0,0 +1,64 @@ +<?php + +/* + * Note: test names match method names in a set of PHPUnit tests + * in a userland package. Please be so kind as to leave them. + */ + +date_default_timezone_set('America/New_York'); + + +/* + * Particular days + */ +echo "test__7: "; +examine_diff('2009-01-14', '2009-01-07', 'P+0Y0M7DT0H0M0S', 7); + +echo "test_years_positive__7_by_0_day: "; +examine_diff('2007-02-07', '2000-02-07', 'P+7Y0M0DT0H0M0S', 2557); + +echo "test_years_positive__7_by_1_day: "; +examine_diff('2007-02-08', '2000-02-07', 'P+7Y0M1DT0H0M0S', 2558); + +echo "test_years_positive__6_shy_1_day: "; +examine_diff('2007-02-06', '2000-02-07', 'P+6Y11M30DT0H0M0S', 2556); + +echo "test_years_positive__7_by_1_month: "; +examine_diff('2007-03-07', '2000-02-07', 'P+7Y1M0DT0H0M0S', 2585); + +echo "test_years_positive__6_shy_1_month: "; +examine_diff('2007-01-07', '2000-02-07', 'P+6Y11M0DT0H0M0S', 2526); + +echo "test_years_positive__7_by_1_month_split_newyear: "; +examine_diff('2007-01-07', '1999-12-07', 'P+7Y1M0DT0H0M0S', 2588); + +echo "test_years_positive__6_shy_1_month_split_newyear: "; +examine_diff('2006-12-07', '2000-01-07', 'P+6Y11M0DT0H0M0S', 2526); + + +/* + * Particular days, negative + */ +echo "test_negative__7: "; +examine_diff('2009-01-07', '2009-01-14', 'P-0Y0M7DT0H0M0S', 7); + +echo "test_years_negative__7_by_0_day: "; +examine_diff('2000-02-07', '2007-02-07', 'P-7Y0M0DT0H0M0S', 2557); + +echo "test_years_negative__7_by_1_day: "; +examine_diff('2000-02-07', '2007-02-08', 'P-7Y0M1DT0H0M0S', 2558); + +echo "test_years_negative__6_shy_1_day: "; +examine_diff('2000-02-07', '2007-02-06', 'P-6Y11M28DT0H0M0S', 2556); + +echo "test_years_negative__7_by_1_month: "; +examine_diff('2000-02-07', '2007-03-07', 'P-7Y1M0DT0H0M0S', 2585); + +echo "test_years_negative__6_shy_1_month: "; +examine_diff('2000-02-07', '2007-01-07', 'P-6Y11M0DT0H0M0S', 2526); + +echo "test_years_negative__7_by_1_month_split_newyear: "; +examine_diff('1999-12-07', '2007-01-07', 'P-7Y1M0DT0H0M0S', 2588); + +echo "test_years_negative__6_shy_1_month_split_newyear: "; +examine_diff('2000-01-07', '2006-12-07', 'P-6Y11M0DT0H0M0S', 2526); diff --git a/ext/date/tests/DateTime_diff_add_sub-fall-type2-type2.phpt b/ext/date/tests/DateTime_data-fall-type2-type2.inc index 8b165d85f..c45c059f3 100644 --- a/ext/date/tests/DateTime_diff_add_sub-fall-type2-type2.phpt +++ b/ext/date/tests/DateTime_data-fall-type2-type2.inc @@ -1,10 +1,3 @@ ---TEST-- -DateTime::diff() add() sub() -- fall type2 type2 ---CREDITS-- -Daniel Convissor <danielc@php.net> ---XFAIL-- -PHP < 5.4 has bugs ---FILE-- <?php /* @@ -12,7 +5,6 @@ PHP < 5.4 has bugs * in a userland package. Please be so kind as to leave them. */ -require './examine_diff.inc'; date_default_timezone_set('America/New_York'); @@ -25,6 +17,8 @@ date_default_timezone_set('America/New_York'); * + redost: standard time in the redo period 2010-11-07 01:14:44 EST * + st: standard time on the transition day 2010-11-07 03:16:55 EST * + post: the day after the transition day 2010-11-08 19:59:59 EST + * + dtsec: daylight time 1 sec before change 2010-11-07 01:59:59 EDT + * + stsec: standard time first second 2010-11-07 01:00:00 EST */ echo "test_time_fall_type2_prev_type2_prev: "; $end = new DateTime('2010-11-06 18:38:28 EDT'); // prev, zt2 @@ -206,41 +200,12 @@ $end = new DateTime('2010-11-08 19:59:59 EST'); // post, zt2 $start = new DateTime('2010-11-08 18:57:55 EST'); // sp post, zt2 examine_diff($end, $start, 'P+0Y0M0DT1H2M4S', 0); -?> ---EXPECT-- -test_time_fall_type2_prev_type2_prev: FWD: 2010-11-06 18:38:28 EDT - 2010-10-04 02:18:48 EDT = **P+0Y1M2DT16H19M40S** | BACK: 2010-10-04 02:18:48 EDT + P+0Y1M2DT16H19M40S = **2010-11-06 18:38:28 EDT** | DAYS: **33** -test_time_fall_type2_prev_type2_dt: FWD: 2010-11-07 00:10:20 EDT - 2010-11-06 18:38:28 EDT = **P+0Y0M0DT5H31M52S** | BACK: 2010-11-06 18:38:28 EDT + P+0Y0M0DT5H31M52S = **2010-11-07 00:10:20 EDT** | DAYS: **0** -test_time_fall_type2_prev_type2_redodt: FWD: 2010-11-07 01:12:33 EDT - 2010-11-06 18:38:28 EDT = **P+0Y0M0DT6H34M5S** | BACK: 2010-11-06 18:38:28 EDT + P+0Y0M0DT6H34M5S = **2010-11-07 01:12:33 EDT** | DAYS: **0** -test_time_fall_type2_prev_type2_redost: FWD: 2010-11-07 01:14:44 EST - 2010-11-06 18:38:28 EDT = **P+0Y0M0DT7H36M16S** | BACK: 2010-11-06 18:38:28 EDT + P+0Y0M0DT7H36M16S = **2010-11-07 01:14:44 EST** | DAYS: **0** -test_time_fall_type2_prev_type2_st: FWD: 2010-11-07 03:16:55 EST - 2010-11-06 18:38:28 EDT = **P+0Y0M0DT9H38M27S** | BACK: 2010-11-06 18:38:28 EDT + P+0Y0M0DT9H38M27S = **2010-11-07 03:16:55 EST** | DAYS: **0** -test_time_fall_type2_prev_type2_post: FWD: 2010-11-08 19:59:59 EST - 2010-11-06 18:38:28 EDT = **P+0Y0M2DT1H21M31S** | BACK: 2010-11-06 18:38:28 EDT + P+0Y0M2DT1H21M31S = **2010-11-08 19:59:59 EST** | DAYS: **2** -test_time_fall_type2_dt_type2_prev: FWD: 2010-11-06 18:38:28 EDT - 2010-11-07 00:10:20 EDT = **P-0Y0M0DT5H31M52S** | BACK: 2010-11-07 00:10:20 EDT + P-0Y0M0DT5H31M52S = **2010-11-06 18:38:28 EDT** | DAYS: **0** -test_time_fall_type2_dt_type2_dt: FWD: 2010-11-07 00:15:35 EDT - 2010-11-07 00:10:20 EDT = **P+0Y0M0DT0H5M15S** | BACK: 2010-11-07 00:10:20 EDT + P+0Y0M0DT0H5M15S = **2010-11-07 00:15:35 EDT** | DAYS: **0** -test_time_fall_type2_dt_type2_redodt: FWD: 2010-11-07 01:12:33 EDT - 2010-11-07 00:10:20 EDT = **P+0Y0M0DT1H2M13S** | BACK: 2010-11-07 00:10:20 EDT + P+0Y0M0DT1H2M13S = **2010-11-07 01:12:33 EDT** | DAYS: **0** -test_time_fall_type2_dt_type2_redost: FWD: 2010-11-07 01:14:44 EST - 2010-11-07 00:10:20 EDT = **P+0Y0M0DT2H4M24S** | BACK: 2010-11-07 00:10:20 EDT + P+0Y0M0DT2H4M24S = **2010-11-07 01:14:44 EST** | DAYS: **0** -test_time_fall_type2_dt_type2_st: FWD: 2010-11-07 03:16:55 EST - 2010-11-07 00:10:20 EDT = **P+0Y0M0DT4H6M35S** | BACK: 2010-11-07 00:10:20 EDT + P+0Y0M0DT4H6M35S = **2010-11-07 03:16:55 EST** | DAYS: **0** -test_time_fall_type2_dt_type2_post: FWD: 2010-11-08 19:59:59 EST - 2010-11-07 00:10:20 EDT = **P+0Y0M1DT20H49M39S** | BACK: 2010-11-07 00:10:20 EDT + P+0Y0M1DT20H49M39S = **2010-11-08 19:59:59 EST** | DAYS: **1** -test_time_fall_type2_redodt_type2_prev: FWD: 2010-11-06 18:38:28 EDT - 2010-11-07 01:12:33 EDT = **P-0Y0M0DT6H34M5S** | BACK: 2010-11-07 01:12:33 EDT + P-0Y0M0DT6H34M5S = **2010-11-06 18:38:28 EDT** | DAYS: **0** -test_time_fall_type2_redodt_type2_dt: FWD: 2010-11-07 00:10:20 EDT - 2010-11-07 01:12:33 EDT = **P-0Y0M0DT1H2M13S** | BACK: 2010-11-07 01:12:33 EDT + P-0Y0M0DT1H2M13S = **2010-11-07 00:10:20 EDT** | DAYS: **0** -test_time_fall_type2_redodt_type2_redodt: FWD: 2010-11-07 01:15:35 EDT - 2010-11-07 01:12:33 EDT = **P+0Y0M0DT0H3M2S** | BACK: 2010-11-07 01:12:33 EDT + P+0Y0M0DT0H3M2S = **2010-11-07 01:15:35 EDT** | DAYS: **0** -test_time_fall_type2_redodt_type2_redost: FWD: 2010-11-07 01:14:44 EST - 2010-11-07 01:12:33 EDT = **P+0Y0M0DT1H2M11S** | BACK: 2010-11-07 01:12:33 EDT + P+0Y0M0DT1H2M11S = **2010-11-07 01:14:44 EST** | DAYS: **0** -test_time_fall_type2_redodt_type2_st: FWD: 2010-11-07 03:16:55 EST - 2010-11-07 01:12:33 EDT = **P+0Y0M0DT3H4M22S** | BACK: 2010-11-07 01:12:33 EDT + P+0Y0M0DT3H4M22S = **2010-11-07 03:16:55 EST** | DAYS: **0** -test_time_fall_type2_redodt_type2_post: FWD: 2010-11-08 19:59:59 EST - 2010-11-07 01:12:33 EDT = **P+0Y0M1DT19H47M26S** | BACK: 2010-11-07 01:12:33 EDT + P+0Y0M1DT19H47M26S = **2010-11-08 19:59:59 EST** | DAYS: **1** -test_time_fall_type2_redost_type2_prev: FWD: 2010-11-06 18:38:28 EDT - 2010-11-07 01:14:44 EST = **P-0Y0M0DT7H36M16S** | BACK: 2010-11-07 01:14:44 EST + P-0Y0M0DT7H36M16S = **2010-11-06 18:38:28 EDT** | DAYS: **0** -test_time_fall_type2_redost_type2_dt: FWD: 2010-11-07 00:10:20 EDT - 2010-11-07 01:14:44 EST = **P-0Y0M0DT2H4M24S** | BACK: 2010-11-07 01:14:44 EST + P-0Y0M0DT2H4M24S = **2010-11-07 00:10:20 EDT** | DAYS: **0** -test_time_fall_type2_redost_type2_redodt: FWD: 2010-11-07 01:12:33 EDT - 2010-11-07 01:14:44 EST = **P-0Y0M0DT1H2M11S** | BACK: 2010-11-07 01:14:44 EST + P-0Y0M0DT1H2M11S = **2010-11-07 01:12:33 EDT** | DAYS: **0** -test_time_fall_type2_redost_type2_redost: FWD: 2010-11-07 01:16:54 EST - 2010-11-07 01:14:44 EST = **P+0Y0M0DT0H2M10S** | BACK: 2010-11-07 01:14:44 EST + P+0Y0M0DT0H2M10S = **2010-11-07 01:16:54 EST** | DAYS: **0** -test_time_fall_type2_redost_type2_st: FWD: 2010-11-07 03:16:55 EST - 2010-11-07 01:14:44 EST = **P+0Y0M0DT2H2M11S** | BACK: 2010-11-07 01:14:44 EST + P+0Y0M0DT2H2M11S = **2010-11-07 03:16:55 EST** | DAYS: **0** -test_time_fall_type2_redost_type2_post: FWD: 2010-11-08 19:59:59 EST - 2010-11-07 01:14:44 EST = **P+0Y0M1DT18H45M15S** | BACK: 2010-11-07 01:14:44 EST + P+0Y0M1DT18H45M15S = **2010-11-08 19:59:59 EST** | DAYS: **1** -test_time_fall_type2_st_type2_prev: FWD: 2010-11-06 18:38:28 EDT - 2010-11-07 03:16:55 EST = **P-0Y0M0DT9H38M27S** | BACK: 2010-11-07 03:16:55 EST + P-0Y0M0DT9H38M27S = **2010-11-06 18:38:28 EDT** | DAYS: **0** -test_time_fall_type2_st_type2_dt: FWD: 2010-11-07 00:10:20 EDT - 2010-11-07 03:16:55 EST = **P-0Y0M0DT4H6M35S** | BACK: 2010-11-07 03:16:55 EST + P-0Y0M0DT4H6M35S = **2010-11-07 00:10:20 EDT** | DAYS: **0** -test_time_fall_type2_st_type2_redodt: FWD: 2010-11-07 01:12:33 EDT - 2010-11-07 03:16:55 EST = **P-0Y0M0DT3H4M22S** | BACK: 2010-11-07 03:16:55 EST + P-0Y0M0DT3H4M22S = **2010-11-07 01:12:33 EDT** | DAYS: **0** -test_time_fall_type2_st_type2_redost: FWD: 2010-11-07 01:14:44 EST - 2010-11-07 03:16:55 EST = **P-0Y0M0DT2H2M11S** | BACK: 2010-11-07 03:16:55 EST + P-0Y0M0DT2H2M11S = **2010-11-07 01:14:44 EST** | DAYS: **0** -test_time_fall_type2_st_type2_st: FWD: 2010-11-07 05:19:56 EST - 2010-11-07 03:16:55 EST = **P+0Y0M0DT2H3M1S** | BACK: 2010-11-07 03:16:55 EST + P+0Y0M0DT2H3M1S = **2010-11-07 05:19:56 EST** | DAYS: **0** -test_time_fall_type2_st_type2_post: FWD: 2010-11-08 19:59:59 EST - 2010-11-07 03:16:55 EST = **P+0Y0M1DT16H43M4S** | BACK: 2010-11-07 03:16:55 EST + P+0Y0M1DT16H43M4S = **2010-11-08 19:59:59 EST** | DAYS: **1** -test_time_fall_type2_post_type2_prev: FWD: 2010-11-06 18:38:28 EDT - 2010-11-08 19:59:59 EST = **P-0Y0M2DT1H21M31S** | BACK: 2010-11-08 19:59:59 EST + P-0Y0M2DT1H21M31S = **2010-11-06 18:38:28 EDT** | DAYS: **2** -test_time_fall_type2_post_type2_dt: FWD: 2010-11-07 00:10:20 EDT - 2010-11-08 19:59:59 EST = **P-0Y0M1DT20H49M39S** | BACK: 2010-11-08 19:59:59 EST + P-0Y0M1DT20H49M39S = **2010-11-07 00:10:20 EDT** | DAYS: **1** -test_time_fall_type2_post_type2_redodt: FWD: 2010-11-07 01:12:33 EDT - 2010-11-08 19:59:59 EST = **P-0Y0M1DT19H47M26S** | BACK: 2010-11-08 19:59:59 EST + P-0Y0M1DT19H47M26S = **2010-11-07 01:12:33 EDT** | DAYS: **1** -test_time_fall_type2_post_type2_redost: FWD: 2010-11-07 01:14:44 EST - 2010-11-08 19:59:59 EST = **P-0Y0M1DT18H45M15S** | BACK: 2010-11-08 19:59:59 EST + P-0Y0M1DT18H45M15S = **2010-11-07 01:14:44 EST** | DAYS: **1** -test_time_fall_type2_post_type2_st: FWD: 2010-11-07 03:16:55 EST - 2010-11-08 19:59:59 EST = **P-0Y0M1DT16H43M4S** | BACK: 2010-11-08 19:59:59 EST + P-0Y0M1DT16H43M4S = **2010-11-07 03:16:55 EST** | DAYS: **1** -test_time_fall_type2_post_type2_post: FWD: 2010-11-08 19:59:59 EST - 2010-11-08 18:57:55 EST = **P+0Y0M0DT1H2M4S** | BACK: 2010-11-08 18:57:55 EST + P+0Y0M0DT1H2M4S = **2010-11-08 19:59:59 EST** | DAYS: **0** +echo "test_time_fall_type2_dtsec_type2_stsec: "; +$end = new DateTime('2010-11-07 01:00:00 EST'); // stsec, zt2 +$start = new DateTime('2010-11-07 01:59:59 EDT'); // dtsec, zt2 +examine_diff($end, $start, 'P+0Y0M0DT0H0M1S', 0); + +echo "test_time_fall_type2_stsec_type2_dtsec: "; +$end = new DateTime('2010-11-07 01:59:59 EDT'); // dtsec, zt2 +$start = new DateTime('2010-11-07 01:00:00 EST'); // stsec, zt2 +examine_diff($end, $start, 'P-0Y0M0DT0H0M1S', 0); diff --git a/ext/date/tests/DateTime_diff_add_sub-fall-type2-type3.phpt b/ext/date/tests/DateTime_data-fall-type2-type3.inc index 61eb40668..a62a8b2b4 100644 --- a/ext/date/tests/DateTime_diff_add_sub-fall-type2-type3.phpt +++ b/ext/date/tests/DateTime_data-fall-type2-type3.inc @@ -1,10 +1,3 @@ ---TEST-- -DateTime::diff() add() sub() -- fall type2 type3 ---CREDITS-- -Daniel Convissor <danielc@php.net> ---XFAIL-- -PHP < 5.4 has bugs ---FILE-- <?php /* @@ -12,7 +5,6 @@ PHP < 5.4 has bugs * in a userland package. Please be so kind as to leave them. */ -require './examine_diff.inc'; date_default_timezone_set('America/New_York'); @@ -25,6 +17,8 @@ date_default_timezone_set('America/New_York'); * + redost: standard time in the redo period 2010-11-07 01:14:44 EST * + st: standard time on the transition day 2010-11-07 03:16:55 EST * + post: the day after the transition day 2010-11-08 19:59:59 EST + * + dtsec: daylight time 1 sec before change 2010-11-07 01:59:59 EDT + * + stsec: standard time first second 2010-11-07 01:00:00 EST */ echo "test_time_fall_type2_prev_type3_prev: "; $end = new DateTime('2010-11-06 18:38:28'); // prev, zt3 @@ -218,41 +212,14 @@ $end = new DateTime('2010-11-08 19:59:59'); // post, zt3 $start = new DateTime('2010-11-08 18:57:55 EST'); // sp post, zt2 examine_diff($end, $start, 'P+0Y0M0DT1H2M4S', 0); -?> ---EXPECT-- -test_time_fall_type2_prev_type3_prev: FWD: 2010-11-06 18:38:28 EDT - 2010-10-04 02:18:48 EDT = **P+0Y1M2DT16H19M40S** | BACK: 2010-10-04 02:18:48 EDT + P+0Y1M2DT16H19M40S = **2010-11-06 18:38:28 EDT** | DAYS: **33** -test_time_fall_type2_prev_type3_dt: FWD: 2010-11-07 00:10:20 EDT - 2010-11-06 18:38:28 EDT = **P+0Y0M0DT5H31M52S** | BACK: 2010-11-06 18:38:28 EDT + P+0Y0M0DT5H31M52S = **2010-11-07 00:10:20 EDT** | DAYS: **0** -test_time_fall_type2_prev_type3_redodt: FWD: 2010-11-07 01:12:33 EDT - 2010-11-06 18:38:28 EDT = **P+0Y0M0DT6H34M5S** | BACK: 2010-11-06 18:38:28 EDT + P+0Y0M0DT6H34M5S = **2010-11-07 01:12:33 EDT** | DAYS: **0** -test_time_fall_type2_prev_type3_redost: FWD: 2010-11-07 01:14:44 EST - 2010-11-06 18:38:28 EDT = **P+0Y0M0DT7H36M16S** | BACK: 2010-11-06 18:38:28 EDT + P+0Y0M0DT7H36M16S = **2010-11-07 01:14:44 EST** | DAYS: **0** -test_time_fall_type2_prev_type3_st: FWD: 2010-11-07 03:16:55 EST - 2010-11-06 18:38:28 EDT = **P+0Y0M0DT9H38M27S** | BACK: 2010-11-06 18:38:28 EDT + P+0Y0M0DT9H38M27S = **2010-11-07 03:16:55 EST** | DAYS: **0** -test_time_fall_type2_prev_type3_post: FWD: 2010-11-08 19:59:59 EST - 2010-11-06 18:38:28 EDT = **P+0Y0M2DT1H21M31S** | BACK: 2010-11-06 18:38:28 EDT + P+0Y0M2DT1H21M31S = **2010-11-08 19:59:59 EST** | DAYS: **2** -test_time_fall_type2_dt_type3_prev: FWD: 2010-11-06 18:38:28 EDT - 2010-11-07 00:10:20 EDT = **P-0Y0M0DT5H31M52S** | BACK: 2010-11-07 00:10:20 EDT + P-0Y0M0DT5H31M52S = **2010-11-06 18:38:28 EDT** | DAYS: **0** -test_time_fall_type2_dt_type3_dt: FWD: 2010-11-07 00:15:35 EDT - 2010-11-07 00:10:20 EDT = **P+0Y0M0DT0H5M15S** | BACK: 2010-11-07 00:10:20 EDT + P+0Y0M0DT0H5M15S = **2010-11-07 00:15:35 EDT** | DAYS: **0** -test_time_fall_type2_dt_type3_redodt: FWD: 2010-11-07 01:12:33 EDT - 2010-11-07 00:10:20 EDT = **P+0Y0M0DT1H2M13S** | BACK: 2010-11-07 00:10:20 EDT + P+0Y0M0DT1H2M13S = **2010-11-07 01:12:33 EDT** | DAYS: **0** -test_time_fall_type2_dt_type3_redost: FWD: 2010-11-07 01:14:44 EST - 2010-11-07 00:10:20 EDT = **P+0Y0M0DT2H4M24S** | BACK: 2010-11-07 00:10:20 EDT + P+0Y0M0DT2H4M24S = **2010-11-07 01:14:44 EST** | DAYS: **0** -test_time_fall_type2_dt_type3_st: FWD: 2010-11-07 03:16:55 EST - 2010-11-07 00:10:20 EDT = **P+0Y0M0DT4H6M35S** | BACK: 2010-11-07 00:10:20 EDT + P+0Y0M0DT4H6M35S = **2010-11-07 03:16:55 EST** | DAYS: **0** -test_time_fall_type2_dt_type3_post: FWD: 2010-11-08 19:59:59 EST - 2010-11-07 00:10:20 EDT = **P+0Y0M1DT20H49M39S** | BACK: 2010-11-07 00:10:20 EDT + P+0Y0M1DT20H49M39S = **2010-11-08 19:59:59 EST** | DAYS: **1** -test_time_fall_type2_redodt_type3_prev: FWD: 2010-11-06 18:38:28 EDT - 2010-11-07 01:12:33 EDT = **P-0Y0M0DT6H34M5S** | BACK: 2010-11-07 01:12:33 EDT + P-0Y0M0DT6H34M5S = **2010-11-06 18:38:28 EDT** | DAYS: **0** -test_time_fall_type2_redodt_type3_dt: FWD: 2010-11-07 00:10:20 EDT - 2010-11-07 01:12:33 EDT = **P-0Y0M0DT1H2M13S** | BACK: 2010-11-07 01:12:33 EDT + P-0Y0M0DT1H2M13S = **2010-11-07 00:10:20 EDT** | DAYS: **0** -test_time_fall_type2_redodt_type3_redodt: FWD: 2010-11-07 01:15:35 EDT - 2010-11-07 01:12:33 EDT = **P+0Y0M0DT0H3M2S** | BACK: 2010-11-07 01:12:33 EDT + P+0Y0M0DT0H3M2S = **2010-11-07 01:15:35 EDT** | DAYS: **0** -test_time_fall_type2_redodt_type3_redost: FWD: 2010-11-07 01:14:44 EST - 2010-11-07 01:12:33 EDT = **P+0Y0M0DT1H2M11S** | BACK: 2010-11-07 01:12:33 EDT + P+0Y0M0DT1H2M11S = **2010-11-07 01:14:44 EST** | DAYS: **0** -test_time_fall_type2_redodt_type3_st: FWD: 2010-11-07 03:16:55 EST - 2010-11-07 01:12:33 EDT = **P+0Y0M0DT3H4M22S** | BACK: 2010-11-07 01:12:33 EDT + P+0Y0M0DT3H4M22S = **2010-11-07 03:16:55 EST** | DAYS: **0** -test_time_fall_type2_redodt_type3_post: FWD: 2010-11-08 19:59:59 EST - 2010-11-07 01:12:33 EDT = **P+0Y0M1DT19H47M26S** | BACK: 2010-11-07 01:12:33 EDT + P+0Y0M1DT19H47M26S = **2010-11-08 19:59:59 EST** | DAYS: **1** -test_time_fall_type2_redost_type3_prev: FWD: 2010-11-06 18:38:28 EDT - 2010-11-07 01:14:44 EST = **P-0Y0M0DT7H36M16S** | BACK: 2010-11-07 01:14:44 EST + P-0Y0M0DT7H36M16S = **2010-11-06 18:38:28 EDT** | DAYS: **0** -test_time_fall_type2_redost_type3_dt: FWD: 2010-11-07 00:10:20 EDT - 2010-11-07 01:14:44 EST = **P-0Y0M0DT2H4M24S** | BACK: 2010-11-07 01:14:44 EST + P-0Y0M0DT2H4M24S = **2010-11-07 00:10:20 EDT** | DAYS: **0** -test_time_fall_type2_redost_type3_redodt: FWD: 2010-11-07 01:12:33 EDT - 2010-11-07 01:14:44 EST = **P-0Y0M0DT1H2M11S** | BACK: 2010-11-07 01:14:44 EST + P-0Y0M0DT1H2M11S = **2010-11-07 01:12:33 EDT** | DAYS: **0** -test_time_fall_type2_redost_type3_redost: FWD: 2010-11-07 01:16:54 EST - 2010-11-07 01:14:44 EST = **P+0Y0M0DT0H2M10S** | BACK: 2010-11-07 01:14:44 EST + P+0Y0M0DT0H2M10S = **2010-11-07 01:16:54 EST** | DAYS: **0** -test_time_fall_type2_redost_type3_st: FWD: 2010-11-07 03:16:55 EST - 2010-11-07 01:14:44 EST = **P+0Y0M0DT2H2M11S** | BACK: 2010-11-07 01:14:44 EST + P+0Y0M0DT2H2M11S = **2010-11-07 03:16:55 EST** | DAYS: **0** -test_time_fall_type2_redost_type3_post: FWD: 2010-11-08 19:59:59 EST - 2010-11-07 01:14:44 EST = **P+0Y0M1DT18H45M15S** | BACK: 2010-11-07 01:14:44 EST + P+0Y0M1DT18H45M15S = **2010-11-08 19:59:59 EST** | DAYS: **1** -test_time_fall_type2_st_type3_prev: FWD: 2010-11-06 18:38:28 EDT - 2010-11-07 03:16:55 EST = **P-0Y0M0DT9H38M27S** | BACK: 2010-11-07 03:16:55 EST + P-0Y0M0DT9H38M27S = **2010-11-06 18:38:28 EDT** | DAYS: **0** -test_time_fall_type2_st_type3_dt: FWD: 2010-11-07 00:10:20 EDT - 2010-11-07 03:16:55 EST = **P-0Y0M0DT4H6M35S** | BACK: 2010-11-07 03:16:55 EST + P-0Y0M0DT4H6M35S = **2010-11-07 00:10:20 EDT** | DAYS: **0** -test_time_fall_type2_st_type3_redodt: FWD: 2010-11-07 01:12:33 EDT - 2010-11-07 03:16:55 EST = **P-0Y0M0DT3H4M22S** | BACK: 2010-11-07 03:16:55 EST + P-0Y0M0DT3H4M22S = **2010-11-07 01:12:33 EDT** | DAYS: **0** -test_time_fall_type2_st_type3_redost: FWD: 2010-11-07 01:14:44 EST - 2010-11-07 03:16:55 EST = **P-0Y0M0DT2H2M11S** | BACK: 2010-11-07 03:16:55 EST + P-0Y0M0DT2H2M11S = **2010-11-07 01:14:44 EST** | DAYS: **0** -test_time_fall_type2_st_type3_st: FWD: 2010-11-07 05:19:56 EST - 2010-11-07 03:16:55 EST = **P+0Y0M0DT2H3M1S** | BACK: 2010-11-07 03:16:55 EST + P+0Y0M0DT2H3M1S = **2010-11-07 05:19:56 EST** | DAYS: **0** -test_time_fall_type2_st_type3_post: FWD: 2010-11-08 19:59:59 EST - 2010-11-07 03:16:55 EST = **P+0Y0M1DT16H43M4S** | BACK: 2010-11-07 03:16:55 EST + P+0Y0M1DT16H43M4S = **2010-11-08 19:59:59 EST** | DAYS: **1** -test_time_fall_type2_post_type3_prev: FWD: 2010-11-06 18:38:28 EDT - 2010-11-08 19:59:59 EST = **P-0Y0M2DT1H21M31S** | BACK: 2010-11-08 19:59:59 EST + P-0Y0M2DT1H21M31S = **2010-11-06 18:38:28 EDT** | DAYS: **2** -test_time_fall_type2_post_type3_dt: FWD: 2010-11-07 00:10:20 EDT - 2010-11-08 19:59:59 EST = **P-0Y0M1DT20H49M39S** | BACK: 2010-11-08 19:59:59 EST + P-0Y0M1DT20H49M39S = **2010-11-07 00:10:20 EDT** | DAYS: **1** -test_time_fall_type2_post_type3_redodt: FWD: 2010-11-07 01:12:33 EDT - 2010-11-08 19:59:59 EST = **P-0Y0M1DT19H47M26S** | BACK: 2010-11-08 19:59:59 EST + P-0Y0M1DT19H47M26S = **2010-11-07 01:12:33 EDT** | DAYS: **1** -test_time_fall_type2_post_type3_redost: FWD: 2010-11-07 01:14:44 EST - 2010-11-08 19:59:59 EST = **P-0Y0M1DT18H45M15S** | BACK: 2010-11-08 19:59:59 EST + P-0Y0M1DT18H45M15S = **2010-11-07 01:14:44 EST** | DAYS: **1** -test_time_fall_type2_post_type3_st: FWD: 2010-11-07 03:16:55 EST - 2010-11-08 19:59:59 EST = **P-0Y0M1DT16H43M4S** | BACK: 2010-11-08 19:59:59 EST + P-0Y0M1DT16H43M4S = **2010-11-07 03:16:55 EST** | DAYS: **1** -test_time_fall_type2_post_type3_post: FWD: 2010-11-08 19:59:59 EST - 2010-11-08 18:57:55 EST = **P+0Y0M0DT1H2M4S** | BACK: 2010-11-08 18:57:55 EST + P+0Y0M0DT1H2M4S = **2010-11-08 19:59:59 EST** | DAYS: **0** +echo "test_time_fall_type2_dtsec_type3_stsec: "; +$end = new DateTime('2010-11-07 01:00:00 EST'); // stsec, zt2 +$end->setTimezone(new DateTimeZone('America/New_York')); // zt2 -> zt3 +$start = new DateTime('2010-11-07 01:59:59 EDT'); // dtsec, zt2 +examine_diff($end, $start, 'P+0Y0M0DT0H0M1S', 0); + +echo "test_time_fall_type2_stsec_type3_dtsec: "; +$end = new DateTime('2010-11-07 01:59:59 EDT'); // dtsec, zt2 +$end->setTimezone(new DateTimeZone('America/New_York')); // zt2 -> zt3 +$start = new DateTime('2010-11-07 01:00:00 EST'); // stsec, zt2 +examine_diff($end, $start, 'P-0Y0M0DT0H0M1S', 0); diff --git a/ext/date/tests/DateTime_diff_add_sub-fall-type3-type2.phpt b/ext/date/tests/DateTime_data-fall-type3-type2.inc index fd57a5426..51ef5ff52 100644 --- a/ext/date/tests/DateTime_diff_add_sub-fall-type3-type2.phpt +++ b/ext/date/tests/DateTime_data-fall-type3-type2.inc @@ -1,10 +1,3 @@ ---TEST-- -DateTime::diff() add() sub() -- fall type3 type2 ---CREDITS-- -Daniel Convissor <danielc@php.net> ---XFAIL-- -PHP < 5.4 has bugs ---FILE-- <?php /* @@ -12,7 +5,6 @@ PHP < 5.4 has bugs * in a userland package. Please be so kind as to leave them. */ -require './examine_diff.inc'; date_default_timezone_set('America/New_York'); @@ -25,6 +17,8 @@ date_default_timezone_set('America/New_York'); * + redost: standard time in the redo period 2010-11-07 01:14:44 EST * + st: standard time on the transition day 2010-11-07 03:16:55 EST * + post: the day after the transition day 2010-11-08 19:59:59 EST + * + dtsec: daylight time 1 sec before change 2010-11-07 01:59:59 EDT + * + stsec: standard time first second 2010-11-07 01:00:00 EST */ echo "test_time_fall_type3_prev_type2_prev: "; $end = new DateTime('2010-11-06 18:38:28 EDT'); // prev, zt2 @@ -220,41 +214,14 @@ $end = new DateTime('2010-11-08 19:59:59 EST'); // post, zt2 $start = new DateTime('2010-11-08 18:57:55'); // sp post, zt3 examine_diff($end, $start, 'P+0Y0M0DT1H2M4S', 0); -?> ---EXPECT-- -test_time_fall_type3_prev_type2_prev: FWD: 2010-11-06 18:38:28 EDT - 2010-10-04 02:18:48 EDT = **P+0Y1M2DT16H19M40S** | BACK: 2010-10-04 02:18:48 EDT + P+0Y1M2DT16H19M40S = **2010-11-06 18:38:28 EDT** | DAYS: **33** -test_time_fall_type3_prev_type2_dt: FWD: 2010-11-07 00:10:20 EDT - 2010-11-06 18:38:28 EDT = **P+0Y0M0DT5H31M52S** | BACK: 2010-11-06 18:38:28 EDT + P+0Y0M0DT5H31M52S = **2010-11-07 00:10:20 EDT** | DAYS: **0** -test_time_fall_type3_prev_type2_redodt: FWD: 2010-11-07 01:12:33 EDT - 2010-11-06 18:38:28 EDT = **P+0Y0M0DT6H34M5S** | BACK: 2010-11-06 18:38:28 EDT + P+0Y0M0DT6H34M5S = **2010-11-07 01:12:33 EDT** | DAYS: **0** -test_time_fall_type3_prev_type2_redost: FWD: 2010-11-07 01:14:44 EST - 2010-11-06 18:38:28 EDT = **P+0Y0M0DT7H36M16S** | BACK: 2010-11-06 18:38:28 EDT + P+0Y0M0DT7H36M16S = **2010-11-07 01:14:44 EST** | DAYS: **0** -test_time_fall_type3_prev_type2_st: FWD: 2010-11-07 03:16:55 EST - 2010-11-06 18:38:28 EDT = **P+0Y0M0DT9H38M27S** | BACK: 2010-11-06 18:38:28 EDT + P+0Y0M0DT9H38M27S = **2010-11-07 03:16:55 EST** | DAYS: **0** -test_time_fall_type3_prev_type2_post: FWD: 2010-11-08 19:59:59 EST - 2010-11-06 18:38:28 EDT = **P+0Y0M2DT1H21M31S** | BACK: 2010-11-06 18:38:28 EDT + P+0Y0M2DT1H21M31S = **2010-11-08 19:59:59 EST** | DAYS: **2** -test_time_fall_type3_dt_type2_prev: FWD: 2010-11-06 18:38:28 EDT - 2010-11-07 00:10:20 EDT = **P-0Y0M0DT5H31M52S** | BACK: 2010-11-07 00:10:20 EDT + P-0Y0M0DT5H31M52S = **2010-11-06 18:38:28 EDT** | DAYS: **0** -test_time_fall_type3_dt_type2_dt: FWD: 2010-11-07 00:15:35 EDT - 2010-11-07 00:10:20 EDT = **P+0Y0M0DT0H5M15S** | BACK: 2010-11-07 00:10:20 EDT + P+0Y0M0DT0H5M15S = **2010-11-07 00:15:35 EDT** | DAYS: **0** -test_time_fall_type3_dt_type2_redodt: FWD: 2010-11-07 01:12:33 EDT - 2010-11-07 00:10:20 EDT = **P+0Y0M0DT1H2M13S** | BACK: 2010-11-07 00:10:20 EDT + P+0Y0M0DT1H2M13S = **2010-11-07 01:12:33 EDT** | DAYS: **0** -test_time_fall_type3_dt_type2_redost: FWD: 2010-11-07 01:14:44 EST - 2010-11-07 00:10:20 EDT = **P+0Y0M0DT2H4M24S** | BACK: 2010-11-07 00:10:20 EDT + P+0Y0M0DT2H4M24S = **2010-11-07 01:14:44 EST** | DAYS: **0** -test_time_fall_type3_dt_type2_st: FWD: 2010-11-07 03:16:55 EST - 2010-11-07 00:10:20 EDT = **P+0Y0M0DT4H6M35S** | BACK: 2010-11-07 00:10:20 EDT + P+0Y0M0DT4H6M35S = **2010-11-07 03:16:55 EST** | DAYS: **0** -test_time_fall_type3_dt_type2_post: FWD: 2010-11-08 19:59:59 EST - 2010-11-07 00:10:20 EDT = **P+0Y0M1DT20H49M39S** | BACK: 2010-11-07 00:10:20 EDT + P+0Y0M1DT20H49M39S = **2010-11-08 19:59:59 EST** | DAYS: **1** -test_time_fall_type3_redodt_type2_prev: FWD: 2010-11-06 18:38:28 EDT - 2010-11-07 01:12:33 EDT = **P-0Y0M0DT6H34M5S** | BACK: 2010-11-07 01:12:33 EDT + P-0Y0M0DT6H34M5S = **2010-11-06 18:38:28 EDT** | DAYS: **0** -test_time_fall_type3_redodt_type2_dt: FWD: 2010-11-07 00:10:20 EDT - 2010-11-07 01:12:33 EDT = **P-0Y0M0DT1H2M13S** | BACK: 2010-11-07 01:12:33 EDT + P-0Y0M0DT1H2M13S = **2010-11-07 00:10:20 EDT** | DAYS: **0** -test_time_fall_type3_redodt_type2_redodt: FWD: 2010-11-07 01:15:35 EDT - 2010-11-07 01:12:33 EDT = **P+0Y0M0DT0H3M2S** | BACK: 2010-11-07 01:12:33 EDT + P+0Y0M0DT0H3M2S = **2010-11-07 01:15:35 EDT** | DAYS: **0** -test_time_fall_type3_redodt_type2_redost: FWD: 2010-11-07 01:14:44 EST - 2010-11-07 01:12:33 EDT = **P+0Y0M0DT1H2M11S** | BACK: 2010-11-07 01:12:33 EDT + P+0Y0M0DT1H2M11S = **2010-11-07 01:14:44 EST** | DAYS: **0** -test_time_fall_type3_redodt_type2_st: FWD: 2010-11-07 03:16:55 EST - 2010-11-07 01:12:33 EDT = **P+0Y0M0DT3H4M22S** | BACK: 2010-11-07 01:12:33 EDT + P+0Y0M0DT3H4M22S = **2010-11-07 03:16:55 EST** | DAYS: **0** -test_time_fall_type3_redodt_type2_post: FWD: 2010-11-08 19:59:59 EST - 2010-11-07 01:12:33 EDT = **P+0Y0M1DT19H47M26S** | BACK: 2010-11-07 01:12:33 EDT + P+0Y0M1DT19H47M26S = **2010-11-08 19:59:59 EST** | DAYS: **1** -test_time_fall_type3_redost_type2_prev: FWD: 2010-11-06 18:38:28 EDT - 2010-11-07 01:14:44 EST = **P-0Y0M0DT7H36M16S** | BACK: 2010-11-07 01:14:44 EST + P-0Y0M0DT7H36M16S = **2010-11-06 18:38:28 EDT** | DAYS: **0** -test_time_fall_type3_redost_type2_dt: FWD: 2010-11-07 00:10:20 EDT - 2010-11-07 01:14:44 EST = **P-0Y0M0DT2H4M24S** | BACK: 2010-11-07 01:14:44 EST + P-0Y0M0DT2H4M24S = **2010-11-07 00:10:20 EDT** | DAYS: **0** -test_time_fall_type3_redost_type2_redodt: FWD: 2010-11-07 01:12:33 EDT - 2010-11-07 01:14:44 EST = **P-0Y0M0DT1H2M11S** | BACK: 2010-11-07 01:14:44 EST + P-0Y0M0DT1H2M11S = **2010-11-07 01:12:33 EDT** | DAYS: **0** -test_time_fall_type3_redost_type2_redost: FWD: 2010-11-07 01:16:54 EST - 2010-11-07 01:14:44 EST = **P+0Y0M0DT0H2M10S** | BACK: 2010-11-07 01:14:44 EST + P+0Y0M0DT0H2M10S = **2010-11-07 01:16:54 EST** | DAYS: **0** -test_time_fall_type3_redost_type2_st: FWD: 2010-11-07 03:16:55 EST - 2010-11-07 01:14:44 EST = **P+0Y0M0DT2H2M11S** | BACK: 2010-11-07 01:14:44 EST + P+0Y0M0DT2H2M11S = **2010-11-07 03:16:55 EST** | DAYS: **0** -test_time_fall_type3_redost_type2_post: FWD: 2010-11-08 19:59:59 EST - 2010-11-07 01:14:44 EST = **P+0Y0M1DT18H45M15S** | BACK: 2010-11-07 01:14:44 EST + P+0Y0M1DT18H45M15S = **2010-11-08 19:59:59 EST** | DAYS: **1** -test_time_fall_type3_st_type2_prev: FWD: 2010-11-06 18:38:28 EDT - 2010-11-07 03:16:55 EST = **P-0Y0M0DT9H38M27S** | BACK: 2010-11-07 03:16:55 EST + P-0Y0M0DT9H38M27S = **2010-11-06 18:38:28 EDT** | DAYS: **0** -test_time_fall_type3_st_type2_dt: FWD: 2010-11-07 00:10:20 EDT - 2010-11-07 03:16:55 EST = **P-0Y0M0DT4H6M35S** | BACK: 2010-11-07 03:16:55 EST + P-0Y0M0DT4H6M35S = **2010-11-07 00:10:20 EDT** | DAYS: **0** -test_time_fall_type3_st_type2_redodt: FWD: 2010-11-07 01:12:33 EDT - 2010-11-07 03:16:55 EST = **P-0Y0M0DT3H4M22S** | BACK: 2010-11-07 03:16:55 EST + P-0Y0M0DT3H4M22S = **2010-11-07 01:12:33 EDT** | DAYS: **0** -test_time_fall_type3_st_type2_redost: FWD: 2010-11-07 01:14:44 EST - 2010-11-07 03:16:55 EST = **P-0Y0M0DT2H2M11S** | BACK: 2010-11-07 03:16:55 EST + P-0Y0M0DT2H2M11S = **2010-11-07 01:14:44 EST** | DAYS: **0** -test_time_fall_type3_st_type2_st: FWD: 2010-11-07 05:19:56 EST - 2010-11-07 03:16:55 EST = **P+0Y0M0DT2H3M1S** | BACK: 2010-11-07 03:16:55 EST + P+0Y0M0DT2H3M1S = **2010-11-07 05:19:56 EST** | DAYS: **0** -test_time_fall_type3_st_type2_post: FWD: 2010-11-08 19:59:59 EST - 2010-11-07 03:16:55 EST = **P+0Y0M1DT16H43M4S** | BACK: 2010-11-07 03:16:55 EST + P+0Y0M1DT16H43M4S = **2010-11-08 19:59:59 EST** | DAYS: **1** -test_time_fall_type3_post_type2_prev: FWD: 2010-11-06 18:38:28 EDT - 2010-11-08 19:59:59 EST = **P-0Y0M2DT1H21M31S** | BACK: 2010-11-08 19:59:59 EST + P-0Y0M2DT1H21M31S = **2010-11-06 18:38:28 EDT** | DAYS: **2** -test_time_fall_type3_post_type2_dt: FWD: 2010-11-07 00:10:20 EDT - 2010-11-08 19:59:59 EST = **P-0Y0M1DT20H49M39S** | BACK: 2010-11-08 19:59:59 EST + P-0Y0M1DT20H49M39S = **2010-11-07 00:10:20 EDT** | DAYS: **1** -test_time_fall_type3_post_type2_redodt: FWD: 2010-11-07 01:12:33 EDT - 2010-11-08 19:59:59 EST = **P-0Y0M1DT19H47M26S** | BACK: 2010-11-08 19:59:59 EST + P-0Y0M1DT19H47M26S = **2010-11-07 01:12:33 EDT** | DAYS: **1** -test_time_fall_type3_post_type2_redost: FWD: 2010-11-07 01:14:44 EST - 2010-11-08 19:59:59 EST = **P-0Y0M1DT18H45M15S** | BACK: 2010-11-08 19:59:59 EST + P-0Y0M1DT18H45M15S = **2010-11-07 01:14:44 EST** | DAYS: **1** -test_time_fall_type3_post_type2_st: FWD: 2010-11-07 03:16:55 EST - 2010-11-08 19:59:59 EST = **P-0Y0M1DT16H43M4S** | BACK: 2010-11-08 19:59:59 EST + P-0Y0M1DT16H43M4S = **2010-11-07 03:16:55 EST** | DAYS: **1** -test_time_fall_type3_post_type2_post: FWD: 2010-11-08 19:59:59 EST - 2010-11-08 18:57:55 EST = **P+0Y0M0DT1H2M4S** | BACK: 2010-11-08 18:57:55 EST + P+0Y0M0DT1H2M4S = **2010-11-08 19:59:59 EST** | DAYS: **0** +echo "test_time_fall_type3_dtsec_type2_stsec: "; +$end = new DateTime('2010-11-07 01:00:00 EST'); // stsec, zt2 +$start = new DateTime('2010-11-07 01:59:59 EDT'); // dtsec, zt2 +$start->setTimezone(new DateTimeZone('America/New_York')); // zt2 -> zt3 +examine_diff($end, $start, 'P+0Y0M0DT0H0M1S', 0); + +echo "test_time_fall_type3_stsec_type2_dtsec: "; +$end = new DateTime('2010-11-07 01:59:59 EDT'); // dtsec, zt2 +$start = new DateTime('2010-11-07 01:00:00 EST'); // stsec, zt2 +$start->setTimezone(new DateTimeZone('America/New_York')); // zt2 -> zt3 +examine_diff($end, $start, 'P-0Y0M0DT0H0M1S', 0); diff --git a/ext/date/tests/DateTime_diff_add_sub-fall-type3-type3.phpt b/ext/date/tests/DateTime_data-fall-type3-type3.inc index 9aac2f169..48c1c332a 100644 --- a/ext/date/tests/DateTime_diff_add_sub-fall-type3-type3.phpt +++ b/ext/date/tests/DateTime_data-fall-type3-type3.inc @@ -1,10 +1,3 @@ ---TEST-- -DateTime::diff() add() sub() -- fall type3 type3 ---CREDITS-- -Daniel Convissor <danielc@php.net> ---XFAIL-- -PHP < 5.4 has bugs ---FILE-- <?php /* @@ -12,7 +5,6 @@ PHP < 5.4 has bugs * in a userland package. Please be so kind as to leave them. */ -require './examine_diff.inc'; date_default_timezone_set('America/New_York'); @@ -25,6 +17,8 @@ date_default_timezone_set('America/New_York'); * + redost: standard time in the redo period 2010-11-07 01:14:44 EST, + TZ * + st: standard time on the transition day 2010-11-07 03:16:55 * + post: the day after the transition day 2010-11-08 19:59:59 + * + dtsec: daylight time 1 sec before change 2010-11-07 01:59:59 EDT, + TZ + * + stsec: standard time first second 2010-11-07 01:00:00 EST, + TZ */ echo "test_time_fall_type3_prev_type3_prev: "; $end = new DateTime('2010-11-06 18:38:28'); // prev, zt3 @@ -230,41 +224,16 @@ $end = new DateTime('2010-11-08 19:59:59'); // post, zt3 $start = new DateTime('2010-11-08 18:57:55'); // sp post, zt3 examine_diff($end, $start, 'P+0Y0M0DT1H2M4S', 0); -?> ---EXPECT-- -test_time_fall_type3_prev_type3_prev: FWD: 2010-11-06 18:38:28 EDT - 2010-10-04 02:18:48 EDT = **P+0Y1M2DT16H19M40S** | BACK: 2010-10-04 02:18:48 EDT + P+0Y1M2DT16H19M40S = **2010-11-06 18:38:28 EDT** | DAYS: **33** -test_time_fall_type3_prev_type3_dt: FWD: 2010-11-07 00:10:20 EDT - 2010-11-06 18:38:28 EDT = **P+0Y0M0DT5H31M52S** | BACK: 2010-11-06 18:38:28 EDT + P+0Y0M0DT5H31M52S = **2010-11-07 00:10:20 EDT** | DAYS: **0** -test_time_fall_type3_prev_type3_redodt: FWD: 2010-11-07 01:12:33 EDT - 2010-11-06 18:38:28 EDT = **P+0Y0M0DT6H34M5S** | BACK: 2010-11-06 18:38:28 EDT + P+0Y0M0DT6H34M5S = **2010-11-07 01:12:33 EDT** | DAYS: **0** -test_time_fall_type3_prev_type3_redost: FWD: 2010-11-07 01:14:44 EST - 2010-11-06 18:38:28 EDT = **P+0Y0M0DT7H36M16S** | BACK: 2010-11-06 18:38:28 EDT + P+0Y0M0DT7H36M16S = **2010-11-07 01:14:44 EST** | DAYS: **0** -test_time_fall_type3_prev_type3_st: FWD: 2010-11-07 03:16:55 EST - 2010-11-06 18:38:28 EDT = **P+0Y0M0DT9H38M27S** | BACK: 2010-11-06 18:38:28 EDT + P+0Y0M0DT9H38M27S = **2010-11-07 03:16:55 EST** | DAYS: **0** -test_time_fall_type3_prev_type3_post: FWD: 2010-11-08 19:59:59 EST - 2010-11-06 18:38:28 EDT = **P+0Y0M2DT1H21M31S** | BACK: 2010-11-06 18:38:28 EDT + P+0Y0M2DT1H21M31S = **2010-11-08 19:59:59 EST** | DAYS: **2** -test_time_fall_type3_dt_type3_prev: FWD: 2010-11-06 18:38:28 EDT - 2010-11-07 00:10:20 EDT = **P-0Y0M0DT5H31M52S** | BACK: 2010-11-07 00:10:20 EDT + P-0Y0M0DT5H31M52S = **2010-11-06 18:38:28 EDT** | DAYS: **0** -test_time_fall_type3_dt_type3_dt: FWD: 2010-11-07 00:15:35 EDT - 2010-11-07 00:10:20 EDT = **P+0Y0M0DT0H5M15S** | BACK: 2010-11-07 00:10:20 EDT + P+0Y0M0DT0H5M15S = **2010-11-07 00:15:35 EDT** | DAYS: **0** -test_time_fall_type3_dt_type3_redodt: FWD: 2010-11-07 01:12:33 EDT - 2010-11-07 00:10:20 EDT = **P+0Y0M0DT1H2M13S** | BACK: 2010-11-07 00:10:20 EDT + P+0Y0M0DT1H2M13S = **2010-11-07 01:12:33 EDT** | DAYS: **0** -test_time_fall_type3_dt_type3_redost: FWD: 2010-11-07 01:14:44 EST - 2010-11-07 00:10:20 EDT = **P+0Y0M0DT2H4M24S** | BACK: 2010-11-07 00:10:20 EDT + P+0Y0M0DT2H4M24S = **2010-11-07 01:14:44 EST** | DAYS: **0** -test_time_fall_type3_dt_type3_st: FWD: 2010-11-07 03:16:55 EST - 2010-11-07 00:10:20 EDT = **P+0Y0M0DT4H6M35S** | BACK: 2010-11-07 00:10:20 EDT + P+0Y0M0DT4H6M35S = **2010-11-07 03:16:55 EST** | DAYS: **0** -test_time_fall_type3_dt_type3_post: FWD: 2010-11-08 19:59:59 EST - 2010-11-07 00:10:20 EDT = **P+0Y0M1DT20H49M39S** | BACK: 2010-11-07 00:10:20 EDT + P+0Y0M1DT20H49M39S = **2010-11-08 19:59:59 EST** | DAYS: **1** -test_time_fall_type3_redodt_type3_prev: FWD: 2010-11-06 18:38:28 EDT - 2010-11-07 01:12:33 EDT = **P-0Y0M0DT6H34M5S** | BACK: 2010-11-07 01:12:33 EDT + P-0Y0M0DT6H34M5S = **2010-11-06 18:38:28 EDT** | DAYS: **0** -test_time_fall_type3_redodt_type3_dt: FWD: 2010-11-07 00:10:20 EDT - 2010-11-07 01:12:33 EDT = **P-0Y0M0DT1H2M13S** | BACK: 2010-11-07 01:12:33 EDT + P-0Y0M0DT1H2M13S = **2010-11-07 00:10:20 EDT** | DAYS: **0** -test_time_fall_type3_redodt_type3_redodt: FWD: 2010-11-07 01:15:35 EDT - 2010-11-07 01:12:33 EDT = **P+0Y0M0DT0H3M2S** | BACK: 2010-11-07 01:12:33 EDT + P+0Y0M0DT0H3M2S = **2010-11-07 01:15:35 EDT** | DAYS: **0** -test_time_fall_type3_redodt_type3_redost: FWD: 2010-11-07 01:14:44 EST - 2010-11-07 01:12:33 EDT = **P+0Y0M0DT1H2M11S** | BACK: 2010-11-07 01:12:33 EDT + P+0Y0M0DT1H2M11S = **2010-11-07 01:14:44 EST** | DAYS: **0** -test_time_fall_type3_redodt_type3_st: FWD: 2010-11-07 03:16:55 EST - 2010-11-07 01:12:33 EDT = **P+0Y0M0DT3H4M22S** | BACK: 2010-11-07 01:12:33 EDT + P+0Y0M0DT3H4M22S = **2010-11-07 03:16:55 EST** | DAYS: **0** -test_time_fall_type3_redodt_type3_post: FWD: 2010-11-08 19:59:59 EST - 2010-11-07 01:12:33 EDT = **P+0Y0M1DT19H47M26S** | BACK: 2010-11-07 01:12:33 EDT + P+0Y0M1DT19H47M26S = **2010-11-08 19:59:59 EST** | DAYS: **1** -test_time_fall_type3_redost_type3_prev: FWD: 2010-11-06 18:38:28 EDT - 2010-11-07 01:14:44 EST = **P-0Y0M0DT7H36M16S** | BACK: 2010-11-07 01:14:44 EST + P-0Y0M0DT7H36M16S = **2010-11-06 18:38:28 EDT** | DAYS: **0** -test_time_fall_type3_redost_type3_dt: FWD: 2010-11-07 00:10:20 EDT - 2010-11-07 01:14:44 EST = **P-0Y0M0DT2H4M24S** | BACK: 2010-11-07 01:14:44 EST + P-0Y0M0DT2H4M24S = **2010-11-07 00:10:20 EDT** | DAYS: **0** -test_time_fall_type3_redost_type3_redodt: FWD: 2010-11-07 01:12:33 EDT - 2010-11-07 01:14:44 EST = **P-0Y0M0DT1H2M11S** | BACK: 2010-11-07 01:14:44 EST + P-0Y0M0DT1H2M11S = **2010-11-07 01:12:33 EDT** | DAYS: **0** -test_time_fall_type3_redost_type3_redost: FWD: 2010-11-07 01:16:54 EST - 2010-11-07 01:14:44 EST = **P+0Y0M0DT0H2M10S** | BACK: 2010-11-07 01:14:44 EST + P+0Y0M0DT0H2M10S = **2010-11-07 01:16:54 EST** | DAYS: **0** -test_time_fall_type3_redost_type3_st: FWD: 2010-11-07 03:16:55 EST - 2010-11-07 01:14:44 EST = **P+0Y0M0DT2H2M11S** | BACK: 2010-11-07 01:14:44 EST + P+0Y0M0DT2H2M11S = **2010-11-07 03:16:55 EST** | DAYS: **0** -test_time_fall_type3_redost_type3_post: FWD: 2010-11-08 19:59:59 EST - 2010-11-07 01:14:44 EST = **P+0Y0M1DT18H45M15S** | BACK: 2010-11-07 01:14:44 EST + P+0Y0M1DT18H45M15S = **2010-11-08 19:59:59 EST** | DAYS: **1** -test_time_fall_type3_st_type3_prev: FWD: 2010-11-06 18:38:28 EDT - 2010-11-07 03:16:55 EST = **P-0Y0M0DT9H38M27S** | BACK: 2010-11-07 03:16:55 EST + P-0Y0M0DT9H38M27S = **2010-11-06 18:38:28 EDT** | DAYS: **0** -test_time_fall_type3_st_type3_dt: FWD: 2010-11-07 00:10:20 EDT - 2010-11-07 03:16:55 EST = **P-0Y0M0DT4H6M35S** | BACK: 2010-11-07 03:16:55 EST + P-0Y0M0DT4H6M35S = **2010-11-07 00:10:20 EDT** | DAYS: **0** -test_time_fall_type3_st_type3_redodt: FWD: 2010-11-07 01:12:33 EDT - 2010-11-07 03:16:55 EST = **P-0Y0M0DT3H4M22S** | BACK: 2010-11-07 03:16:55 EST + P-0Y0M0DT3H4M22S = **2010-11-07 01:12:33 EDT** | DAYS: **0** -test_time_fall_type3_st_type3_redost: FWD: 2010-11-07 01:14:44 EST - 2010-11-07 03:16:55 EST = **P-0Y0M0DT2H2M11S** | BACK: 2010-11-07 03:16:55 EST + P-0Y0M0DT2H2M11S = **2010-11-07 01:14:44 EST** | DAYS: **0** -test_time_fall_type3_st_type3_st: FWD: 2010-11-07 05:19:56 EST - 2010-11-07 03:16:55 EST = **P+0Y0M0DT2H3M1S** | BACK: 2010-11-07 03:16:55 EST + P+0Y0M0DT2H3M1S = **2010-11-07 05:19:56 EST** | DAYS: **0** -test_time_fall_type3_st_type3_post: FWD: 2010-11-08 19:59:59 EST - 2010-11-07 03:16:55 EST = **P+0Y0M1DT16H43M4S** | BACK: 2010-11-07 03:16:55 EST + P+0Y0M1DT16H43M4S = **2010-11-08 19:59:59 EST** | DAYS: **1** -test_time_fall_type3_post_type3_prev: FWD: 2010-11-06 18:38:28 EDT - 2010-11-08 19:59:59 EST = **P-0Y0M2DT1H21M31S** | BACK: 2010-11-08 19:59:59 EST + P-0Y0M2DT1H21M31S = **2010-11-06 18:38:28 EDT** | DAYS: **2** -test_time_fall_type3_post_type3_dt: FWD: 2010-11-07 00:10:20 EDT - 2010-11-08 19:59:59 EST = **P-0Y0M1DT20H49M39S** | BACK: 2010-11-08 19:59:59 EST + P-0Y0M1DT20H49M39S = **2010-11-07 00:10:20 EDT** | DAYS: **1** -test_time_fall_type3_post_type3_redodt: FWD: 2010-11-07 01:12:33 EDT - 2010-11-08 19:59:59 EST = **P-0Y0M1DT19H47M26S** | BACK: 2010-11-08 19:59:59 EST + P-0Y0M1DT19H47M26S = **2010-11-07 01:12:33 EDT** | DAYS: **1** -test_time_fall_type3_post_type3_redost: FWD: 2010-11-07 01:14:44 EST - 2010-11-08 19:59:59 EST = **P-0Y0M1DT18H45M15S** | BACK: 2010-11-08 19:59:59 EST + P-0Y0M1DT18H45M15S = **2010-11-07 01:14:44 EST** | DAYS: **1** -test_time_fall_type3_post_type3_st: FWD: 2010-11-07 03:16:55 EST - 2010-11-08 19:59:59 EST = **P-0Y0M1DT16H43M4S** | BACK: 2010-11-08 19:59:59 EST + P-0Y0M1DT16H43M4S = **2010-11-07 03:16:55 EST** | DAYS: **1** -test_time_fall_type3_post_type3_post: FWD: 2010-11-08 19:59:59 EST - 2010-11-08 18:57:55 EST = **P+0Y0M0DT1H2M4S** | BACK: 2010-11-08 18:57:55 EST + P+0Y0M0DT1H2M4S = **2010-11-08 19:59:59 EST** | DAYS: **0** +echo "test_time_fall_type3_dtsec_type3_stsec: "; +$end = new DateTime('2010-11-07 01:00:00 EST'); // stsec, zt2 +$end->setTimezone(new DateTimeZone('America/New_York')); // zt2 -> zt3 +$start = new DateTime('2010-11-07 01:59:59 EDT'); // dtsec, zt2 +$start->setTimezone(new DateTimeZone('America/New_York')); // zt2 -> zt3 +examine_diff($end, $start, 'P+0Y0M0DT0H0M1S', 0); + +echo "test_time_fall_type3_stsec_type3_dtsec: "; +$end = new DateTime('2010-11-07 01:59:59 EDT'); // dtsec, zt2 +$end->setTimezone(new DateTimeZone('America/New_York')); // zt2 -> zt3 +$start = new DateTime('2010-11-07 01:00:00 EST'); // stsec, zt2 +$start->setTimezone(new DateTimeZone('America/New_York')); // zt2 -> zt3 +examine_diff($end, $start, 'P-0Y0M0DT0H0M1S', 0); diff --git a/ext/date/tests/DateTime_data-february.inc b/ext/date/tests/DateTime_data-february.inc new file mode 100644 index 000000000..8c31ef69a --- /dev/null +++ b/ext/date/tests/DateTime_data-february.inc @@ -0,0 +1,208 @@ +<?php + +/* + * Note: test names match method names in a set of PHPUnit tests + * in a userland package. Please be so kind as to leave them. + */ + +date_default_timezone_set('America/New_York'); + + +/* + * Check PHP bug 49081 + */ +echo "test_bug_49081__1: "; +examine_diff('2010-03-31', '2010-03-01', 'P+0Y0M30DT0H0M0S', 30); + +echo "test_bug_49081__2: "; +examine_diff('2010-04-01', '2010-03-01', 'P+0Y1M0DT0H0M0S', 31); + +echo "test_bug_49081__3: "; +examine_diff('2010-04-01', '2010-03-31', 'P+0Y0M1DT0H0M0S', 1); + +echo "test_bug_49081__4: "; +examine_diff('2010-04-29', '2010-03-31', 'P+0Y0M29DT0H0M0S', 29); + +echo "test_bug_49081__5: "; +examine_diff('2010-04-30', '2010-03-31', 'P+0Y0M30DT0H0M0S', 30); + +echo "test_bug_49081__6: "; +examine_diff('2010-04-30', '2010-03-30', 'P+0Y1M0DT0H0M0S', 31); + +echo "test_bug_49081__7: "; +examine_diff('2010-04-30', '2010-03-29', 'P+0Y1M1DT0H0M0S', 32); + +echo "test_bug_49081__8: "; +examine_diff('2010-01-29', '2010-01-01', 'P+0Y0M28DT0H0M0S', 28); + +echo "test_bug_49081__9: "; +examine_diff('2010-01-30', '2010-01-01', 'P+0Y0M29DT0H0M0S', 29); + +echo "test_bug_49081__10: "; +examine_diff('2010-01-31', '2010-01-01', 'P+0Y0M30DT0H0M0S', 30); + +echo "test_bug_49081__11: "; +examine_diff('2010-02-01', '2010-01-01', 'P+0Y1M0DT0H0M0S', 31); + +echo "test_bug_49081__12: "; +examine_diff('2010-02-01', '2010-01-31', 'P+0Y0M1DT0H0M0S', 1); + +echo "test_bug_49081__13: "; +examine_diff('2010-02-27', '2010-01-31', 'P+0Y0M27DT0H0M0S', 27); + +echo "test_bug_49081__14: "; +examine_diff('2010-02-28', '2010-01-31', 'P+0Y0M28DT0H0M0S', 28); + +echo "test_bug_49081__15: "; +examine_diff('2010-02-28', '2010-01-30', 'P+0Y0M29DT0H0M0S', 29); + +echo "test_bug_49081__16: "; +examine_diff('2010-02-28', '2010-01-29', 'P+0Y0M30DT0H0M0S', 30); + +echo "test_bug_49081__17: "; +examine_diff('2010-02-28', '2010-01-28', 'P+0Y1M0DT0H0M0S', 31); + +echo "test_bug_49081__18: "; +examine_diff('2010-02-28', '2010-01-27', 'P+0Y1M1DT0H0M0S', 32); + +echo "test_bug_49081__19: "; +examine_diff('2010-03-01', '2010-01-01', 'P+0Y2M0DT0H0M0S', 59); + +echo "test_bug_49081__20: "; +examine_diff('2010-03-01', '2010-01-31', 'P+0Y0M29DT0H0M0S', 29); + +echo "test_bug_49081__21: "; +examine_diff('2010-03-27', '2010-01-31', 'P+0Y1M24DT0H0M0S', 55); + +echo "test_bug_49081__22: "; +examine_diff('2010-03-28', '2010-01-31', 'P+0Y1M25DT0H0M0S', 56); + +echo "test_bug_49081__23: "; +examine_diff('2010-03-29', '2010-01-31', 'P+0Y1M26DT0H0M0S', 57); + +echo "test_bug_49081__24: "; +examine_diff('2010-03-30', '2010-01-31', 'P+0Y1M27DT0H0M0S', 58); + +echo "test_bug_49081__25: "; +examine_diff('2010-03-31', '2010-01-31', 'P+0Y2M0DT0H0M0S', 59); + +echo "test_bug_49081__26: "; +examine_diff('2010-03-31', '2010-01-30', 'P+0Y2M1DT0H0M0S', 60); + +echo "test_bug_49081__27: "; +examine_diff('2009-01-31', '2009-01-01', 'P+0Y0M30DT0H0M0S', 30); + +echo "test_bug_49081__28: "; +examine_diff('2010-03-27', '2010-02-28', 'P+0Y0M27DT0H0M0S', 27); + +echo "test_bug_49081__29: "; +examine_diff('2010-03-28', '2010-02-28', 'P+0Y1M0DT0H0M0S', 28); + +echo "test_bug_49081__30: "; +examine_diff('2010-03-29', '2010-02-28', 'P+0Y1M1DT0H0M0S', 29); + +echo "test_bug_49081__31: "; +examine_diff('2010-03-27', '2010-02-27', 'P+0Y1M0DT0H0M0S', 28); + +echo "test_bug_49081__32: "; +examine_diff('2010-03-27', '2010-02-26', 'P+0Y1M1DT0H0M0S', 29); + + +/* + * Check PHP bug 49081, negative + */ +echo "test_bug_49081_negative__1: "; +examine_diff('2010-03-01', '2010-03-31', 'P-0Y0M30DT0H0M0S', 30); + +echo "test_bug_49081_negative__2: "; +examine_diff('2010-03-01', '2010-04-01', 'P-0Y1M0DT0H0M0S', 31); + +echo "test_bug_49081_negative__3: "; +examine_diff('2010-03-31', '2010-04-01', 'P-0Y0M1DT0H0M0S', 1); + +echo "test_bug_49081_negative__4: "; +examine_diff('2010-03-31', '2010-04-29', 'P-0Y0M29DT0H0M0S', 29); + +echo "test_bug_49081_negative__5: "; +examine_diff('2010-03-31', '2010-04-30', 'P-0Y0M30DT0H0M0S', 30); + +echo "test_bug_49081_negative__6: "; +examine_diff('2010-03-30', '2010-04-30', 'P-0Y1M0DT0H0M0S', 31); + +echo "test_bug_49081_negative__7: "; +examine_diff('2010-03-29', '2010-04-30', 'P-0Y1M1DT0H0M0S', 32); + +echo "test_bug_49081_negative__8: "; +examine_diff('2010-01-01', '2010-01-29', 'P-0Y0M28DT0H0M0S', 28); + +echo "test_bug_49081_negative__9: "; +examine_diff('2010-01-01', '2010-01-30', 'P-0Y0M29DT0H0M0S', 29); + +echo "test_bug_49081_negative__10: "; +examine_diff('2010-01-01', '2010-01-31', 'P-0Y0M30DT0H0M0S', 30); + +echo "test_bug_49081_negative__11: "; +examine_diff('2010-01-01', '2010-02-01', 'P-0Y1M0DT0H0M0S', 31); + +echo "test_bug_49081_negative__12: "; +examine_diff('2010-01-31', '2010-02-01', 'P-0Y0M1DT0H0M0S', 1); + +echo "test_bug_49081_negative__13: "; +examine_diff('2010-01-31', '2010-02-27', 'P-0Y0M27DT0H0M0S', 27); + +echo "test_bug_49081_negative__14: "; +examine_diff('2010-01-31', '2010-02-28', 'P-0Y0M28DT0H0M0S', 28); + +echo "test_bug_49081_negative__15: "; +examine_diff('2010-01-30', '2010-02-28', 'P-0Y0M29DT0H0M0S', 29); + +echo "test_bug_49081_negative__16: "; +examine_diff('2010-01-29', '2010-02-28', 'P-0Y0M30DT0H0M0S', 30); + +echo "test_bug_49081_negative__17: "; +examine_diff('2010-01-28', '2010-02-28', 'P-0Y1M0DT0H0M0S', 31); + +echo "test_bug_49081_negative__18: "; +examine_diff('2010-01-27', '2010-02-28', 'P-0Y1M1DT0H0M0S', 32); + +echo "test_bug_49081_negative__19: "; +examine_diff('2010-01-01', '2010-03-01', 'P-0Y2M0DT0H0M0S', 59); + +echo "test_bug_49081_negative__20: "; +examine_diff('2010-01-31', '2010-03-01', 'P-0Y1M1DT0H0M0S', 29); + +echo "test_bug_49081_negative__21: "; +examine_diff('2010-01-31', '2010-03-27', 'P-0Y1M27DT0H0M0S', 55); + +echo "test_bug_49081_negative__22: "; +examine_diff('2010-01-31', '2010-03-28', 'P-0Y1M28DT0H0M0S', 56); + +echo "test_bug_49081_negative__23: "; +examine_diff('2010-01-31', '2010-03-29', 'P-0Y1M29DT0H0M0S', 57); + +echo "test_bug_49081_negative__24: "; +examine_diff('2010-01-31', '2010-03-30', 'P-0Y1M30DT0H0M0S', 58); + +echo "test_bug_49081_negative__25: "; +examine_diff('2010-01-31', '2010-03-31', 'P-0Y2M0DT0H0M0S', 59); + +echo "test_bug_49081_negative__26: "; +examine_diff('2010-01-30', '2010-03-31', 'P-0Y2M1DT0H0M0S', 60); + +echo "test_bug_49081_negative__27: "; +examine_diff('2009-01-01', '2009-01-31', 'P-0Y0M30DT0H0M0S', 30); + +echo "test_bug_49081_negative__28: "; +examine_diff('2010-02-28', '2010-03-27', 'P-0Y0M27DT0H0M0S', 27); + +echo "test_bug_49081_negative__29: "; +examine_diff('2010-02-28', '2010-03-28', 'P-0Y1M0DT0H0M0S', 28); + +echo "test_bug_49081_negative__30: "; +examine_diff('2010-02-28', '2010-03-29', 'P-0Y1M1DT0H0M0S', 29); + +echo "test_bug_49081_negative__31: "; +examine_diff('2010-02-27', '2010-03-27', 'P-0Y1M0DT0H0M0S', 28); + +echo "test_bug_49081_negative__32: "; +examine_diff('2010-02-26', '2010-03-27', 'P-0Y1M1DT0H0M0S', 29); diff --git a/ext/date/tests/DateTime_diff_add_sub-massive.phpt b/ext/date/tests/DateTime_data-massive.inc index d9f48a0fd..bf20759ef 100644 --- a/ext/date/tests/DateTime_diff_add_sub-massive.phpt +++ b/ext/date/tests/DateTime_data-massive.inc @@ -1,8 +1,3 @@ ---TEST-- -DateTime::diff() add() sub() -- massive ---CREDITS-- -Daniel Convissor <danielc@php.net> ---FILE-- <?php /* @@ -10,7 +5,6 @@ Daniel Convissor <danielc@php.net> * in a userland package. Please be so kind as to leave them. */ -require './examine_diff.inc'; date_default_timezone_set('America/New_York'); @@ -38,8 +32,3 @@ $start->setDate(333333, 1, 1); $start->setTime(16, 18, 02); examine_diff($end, $start, 'P-666666Y0M0DT0H0M0S', 243494757); - -?> ---EXPECT-- -test_massive_positive: FWD: 333333-01-01 16:18:02 EST - -333333-01-01 16:18:02 EST = **P+666666Y0M0DT0H0M0S** | BACK: -333333-01-01 16:18:02 EST + P+666666Y0M0DT0H0M0S = **333333-01-01 16:18:02 EST** | DAYS: **243494757** -test_massive_negative: FWD: -333333-01-01 16:18:02 EST - 333333-01-01 16:18:02 EST = **P-666666Y0M0DT0H0M0S** | BACK: 333333-01-01 16:18:02 EST + P-666666Y0M0DT0H0M0S = **-333333-01-01 16:18:02 EST** | DAYS: **243494757** diff --git a/ext/date/tests/DateTime_diff_add_sub-spring-type2-type2.phpt b/ext/date/tests/DateTime_data-spring-type2-type2.inc index 2ed280e5f..3556b207b 100644 --- a/ext/date/tests/DateTime_diff_add_sub-spring-type2-type2.phpt +++ b/ext/date/tests/DateTime_data-spring-type2-type2.inc @@ -1,10 +1,3 @@ ---TEST-- -DateTime::diff() add() sub() -- spring type2 type2 ---CREDITS-- -Daniel Convissor <danielc@php.net> ---XFAIL-- -PHP < 5.4 has bugs ---FILE-- <?php /* @@ -12,7 +5,6 @@ PHP < 5.4 has bugs * in a userland package. Please be so kind as to leave them. */ -require './examine_diff.inc'; date_default_timezone_set('America/New_York'); @@ -23,6 +15,8 @@ date_default_timezone_set('America/New_York'); * + st: standard time on transition day 2010-03-14 00:10:20 EST * + dt: daylight time on the transition day 2010-03-14 03:16:55 EDT * + post: the day after the transition day 2010-03-15 19:59:59 EDT + * + stsec: standard time 1 sec before change 2010-03-14 01:59:59 EST + * + dtsec: daylight time first second 2010-03-14 03:00:00 EDT */ echo "test_time_spring_type2_prev_type2_prev: "; $end = new DateTime('2010-03-13 18:38:28 EST'); // prev, zt2 @@ -104,21 +98,12 @@ $end = new DateTime('2010-03-15 19:59:59 EDT'); // post, zt2 $start = new DateTime('2010-03-15 18:57:55 EDT'); // sp post, zt2 examine_diff($end, $start, 'P+0Y0M0DT1H2M4S', 0); -?> ---EXPECT-- -test_time_spring_type2_prev_type2_prev: FWD: 2010-03-13 18:38:28 EST - 2010-02-11 02:18:48 EST = **P+0Y1M2DT16H19M40S** | BACK: 2010-02-11 02:18:48 EST + P+0Y1M2DT16H19M40S = **2010-03-13 18:38:28 EST** | DAYS: **30** -test_time_spring_type2_prev_type2_st: FWD: 2010-03-14 00:10:20 EST - 2010-03-13 18:38:28 EST = **P+0Y0M0DT5H31M52S** | BACK: 2010-03-13 18:38:28 EST + P+0Y0M0DT5H31M52S = **2010-03-14 00:10:20 EST** | DAYS: **0** -test_time_spring_type2_prev_type2_dt: FWD: 2010-03-14 03:16:55 EDT - 2010-03-13 18:38:28 EST = **P+0Y0M0DT7H38M27S** | BACK: 2010-03-13 18:38:28 EST + P+0Y0M0DT7H38M27S = **2010-03-14 03:16:55 EDT** | DAYS: **0** -test_time_spring_type2_prev_type2_post: FWD: 2010-03-15 19:59:59 EDT - 2010-03-13 18:38:28 EST = **P+0Y0M2DT1H21M31S** | BACK: 2010-03-13 18:38:28 EST + P+0Y0M2DT1H21M31S = **2010-03-15 19:59:59 EDT** | DAYS: **2** -test_time_spring_type2_st_type2_prev: FWD: 2010-03-13 18:38:28 EST - 2010-03-14 00:10:20 EST = **P-0Y0M0DT5H31M52S** | BACK: 2010-03-14 00:10:20 EST + P-0Y0M0DT5H31M52S = **2010-03-13 18:38:28 EST** | DAYS: **0** -test_time_spring_type2_st_type2_st: FWD: 2010-03-14 00:15:35 EST - 2010-03-14 00:10:20 EST = **P+0Y0M0DT0H5M15S** | BACK: 2010-03-14 00:10:20 EST + P+0Y0M0DT0H5M15S = **2010-03-14 00:15:35 EST** | DAYS: **0** -test_time_spring_type2_st_type2_dt: FWD: 2010-03-14 03:16:55 EDT - 2010-03-14 00:10:20 EST = **P+0Y0M0DT2H6M35S** | BACK: 2010-03-14 00:10:20 EST + P+0Y0M0DT2H6M35S = **2010-03-14 03:16:55 EDT** | DAYS: **0** -test_time_spring_type2_st_type2_post: FWD: 2010-03-15 19:59:59 EDT - 2010-03-14 00:10:20 EST = **P+0Y0M1DT18H49M39S** | BACK: 2010-03-14 00:10:20 EST + P+0Y0M1DT18H49M39S = **2010-03-15 19:59:59 EDT** | DAYS: **1** -test_time_spring_type2_dt_type2_prev: FWD: 2010-03-13 18:38:28 EST - 2010-03-14 03:16:55 EDT = **P-0Y0M0DT7H38M27S** | BACK: 2010-03-14 03:16:55 EDT + P-0Y0M0DT7H38M27S = **2010-03-13 18:38:28 EST** | DAYS: **0** -test_time_spring_type2_dt_type2_st: FWD: 2010-03-14 00:10:20 EST - 2010-03-14 03:16:55 EDT = **P-0Y0M0DT2H6M35S** | BACK: 2010-03-14 03:16:55 EDT + P-0Y0M0DT2H6M35S = **2010-03-14 00:10:20 EST** | DAYS: **0** -test_time_spring_type2_dt_type2_dt: FWD: 2010-03-14 05:19:56 EDT - 2010-03-14 03:16:55 EDT = **P+0Y0M0DT2H3M1S** | BACK: 2010-03-14 03:16:55 EDT + P+0Y0M0DT2H3M1S = **2010-03-14 05:19:56 EDT** | DAYS: **0** -test_time_spring_type2_dt_type2_post: FWD: 2010-03-15 19:59:59 EDT - 2010-03-14 03:16:55 EDT = **P+0Y0M1DT16H43M4S** | BACK: 2010-03-14 03:16:55 EDT + P+0Y0M1DT16H43M4S = **2010-03-15 19:59:59 EDT** | DAYS: **1** -test_time_spring_type2_post_type2_prev: FWD: 2010-03-13 18:38:28 EST - 2010-03-15 19:59:59 EDT = **P-0Y0M2DT1H21M31S** | BACK: 2010-03-15 19:59:59 EDT + P-0Y0M2DT1H21M31S = **2010-03-13 18:38:28 EST** | DAYS: **2** -test_time_spring_type2_post_type2_st: FWD: 2010-03-14 00:10:20 EST - 2010-03-15 19:59:59 EDT = **P-0Y0M1DT18H49M39S** | BACK: 2010-03-15 19:59:59 EDT + P-0Y0M1DT18H49M39S = **2010-03-14 00:10:20 EST** | DAYS: **1** -test_time_spring_type2_post_type2_dt: FWD: 2010-03-14 03:16:55 EDT - 2010-03-15 19:59:59 EDT = **P-0Y0M1DT16H43M4S** | BACK: 2010-03-15 19:59:59 EDT + P-0Y0M1DT16H43M4S = **2010-03-14 03:16:55 EDT** | DAYS: **1** -test_time_spring_type2_post_type2_post: FWD: 2010-03-15 19:59:59 EDT - 2010-03-15 18:57:55 EDT = **P+0Y0M0DT1H2M4S** | BACK: 2010-03-15 18:57:55 EDT + P+0Y0M0DT1H2M4S = **2010-03-15 19:59:59 EDT** | DAYS: **0** +echo "test_time_spring_type2_stsec_type2_dtsec: "; +$end = new DateTime('2010-03-15 03:00:00 EDT'); // dtsec, zt2 +$start = new DateTime('2010-03-13 01:59:59 EST'); // stsec, zt2 +examine_diff($end, $start, 'P+0Y0M0DT0H0M1S', 0); + +echo "test_time_spring_type2_dtsec_type2_stsec: "; +$end = new DateTime('2010-03-15 01:59:59 EST'); // stsec, zt2 +$start = new DateTime('2010-03-13 03:00:00 EDT'); // dtsec, zt2 +examine_diff($end, $start, 'P-0Y0M0DT0H0M1S', 0); diff --git a/ext/date/tests/DateTime_diff_add_sub-spring-type2-type3.phpt b/ext/date/tests/DateTime_data-spring-type2-type3.inc index d36dcc946..b06825837 100644 --- a/ext/date/tests/DateTime_diff_add_sub-spring-type2-type3.phpt +++ b/ext/date/tests/DateTime_data-spring-type2-type3.inc @@ -1,10 +1,3 @@ ---TEST-- -DateTime::diff() add() sub() -- spring type2 type3 ---CREDITS-- -Daniel Convissor <danielc@php.net> ---XFAIL-- -PHP < 5.4 has bugs ---FILE-- <?php /* @@ -12,7 +5,6 @@ PHP < 5.4 has bugs * in a userland package. Please be so kind as to leave them. */ -require './examine_diff.inc'; date_default_timezone_set('America/New_York'); @@ -23,6 +15,8 @@ date_default_timezone_set('America/New_York'); * + st: standard time on transition day 2010-03-14 00:10:20 EST * + dt: daylight time on the transition day 2010-03-14 03:16:55 EDT * + post: the day after the transition day 2010-03-15 19:59:59 EDT + * + stsec: standard time 1 sec before change 2010-03-14 01:59:59 EST + * + dtsec: daylight time first second 2010-03-14 03:00:00 EDT */ echo "test_time_spring_type2_prev_type3_prev: "; $end = new DateTime('2010-03-13 18:38:28'); // prev, zt3 @@ -104,21 +98,12 @@ $end = new DateTime('2010-03-15 19:59:59'); // post, zt3 $start = new DateTime('2010-03-15 18:57:55 EDT'); // sp post, zt2 examine_diff($end, $start, 'P+0Y0M0DT1H2M4S', 0); -?> ---EXPECT-- -test_time_spring_type2_prev_type3_prev: FWD: 2010-03-13 18:38:28 EST - 2010-02-11 02:18:48 EST = **P+0Y1M2DT16H19M40S** | BACK: 2010-02-11 02:18:48 EST + P+0Y1M2DT16H19M40S = **2010-03-13 18:38:28 EST** | DAYS: **30** -test_time_spring_type2_prev_type3_st: FWD: 2010-03-14 00:10:20 EST - 2010-03-13 18:38:28 EST = **P+0Y0M0DT5H31M52S** | BACK: 2010-03-13 18:38:28 EST + P+0Y0M0DT5H31M52S = **2010-03-14 00:10:20 EST** | DAYS: **0** -test_time_spring_type2_prev_type3_dt: FWD: 2010-03-14 03:16:55 EDT - 2010-03-13 18:38:28 EST = **P+0Y0M0DT7H38M27S** | BACK: 2010-03-13 18:38:28 EST + P+0Y0M0DT7H38M27S = **2010-03-14 03:16:55 EDT** | DAYS: **0** -test_time_spring_type2_prev_type3_post: FWD: 2010-03-15 19:59:59 EDT - 2010-03-13 18:38:28 EST = **P+0Y0M2DT1H21M31S** | BACK: 2010-03-13 18:38:28 EST + P+0Y0M2DT1H21M31S = **2010-03-15 19:59:59 EDT** | DAYS: **2** -test_time_spring_type2_st_type3_prev: FWD: 2010-03-13 18:38:28 EST - 2010-03-14 00:10:20 EST = **P-0Y0M0DT5H31M52S** | BACK: 2010-03-14 00:10:20 EST + P-0Y0M0DT5H31M52S = **2010-03-13 18:38:28 EST** | DAYS: **0** -test_time_spring_type2_st_type3_st: FWD: 2010-03-14 00:15:35 EST - 2010-03-14 00:10:20 EST = **P+0Y0M0DT0H5M15S** | BACK: 2010-03-14 00:10:20 EST + P+0Y0M0DT0H5M15S = **2010-03-14 00:15:35 EST** | DAYS: **0** -test_time_spring_type2_st_type3_dt: FWD: 2010-03-14 03:16:55 EDT - 2010-03-14 00:10:20 EST = **P+0Y0M0DT2H6M35S** | BACK: 2010-03-14 00:10:20 EST + P+0Y0M0DT2H6M35S = **2010-03-14 03:16:55 EDT** | DAYS: **0** -test_time_spring_type2_st_type3_post: FWD: 2010-03-15 19:59:59 EDT - 2010-03-14 00:10:20 EST = **P+0Y0M1DT18H49M39S** | BACK: 2010-03-14 00:10:20 EST + P+0Y0M1DT18H49M39S = **2010-03-15 19:59:59 EDT** | DAYS: **1** -test_time_spring_type2_dt_type3_prev: FWD: 2010-03-13 18:38:28 EST - 2010-03-14 03:16:55 EDT = **P-0Y0M0DT7H38M27S** | BACK: 2010-03-14 03:16:55 EDT + P-0Y0M0DT7H38M27S = **2010-03-13 18:38:28 EST** | DAYS: **0** -test_time_spring_type2_dt_type3_st: FWD: 2010-03-14 00:10:20 EST - 2010-03-14 03:16:55 EDT = **P-0Y0M0DT2H6M35S** | BACK: 2010-03-14 03:16:55 EDT + P-0Y0M0DT2H6M35S = **2010-03-14 00:10:20 EST** | DAYS: **0** -test_time_spring_type2_dt_type3_dt: FWD: 2010-03-14 05:19:56 EDT - 2010-03-14 03:16:55 EDT = **P+0Y0M0DT2H3M1S** | BACK: 2010-03-14 03:16:55 EDT + P+0Y0M0DT2H3M1S = **2010-03-14 05:19:56 EDT** | DAYS: **0** -test_time_spring_type2_dt_type3_post: FWD: 2010-03-15 19:59:59 EDT - 2010-03-14 03:16:55 EDT = **P+0Y0M1DT16H43M4S** | BACK: 2010-03-14 03:16:55 EDT + P+0Y0M1DT16H43M4S = **2010-03-15 19:59:59 EDT** | DAYS: **1** -test_time_spring_type2_post_type3_prev: FWD: 2010-03-13 18:38:28 EST - 2010-03-15 19:59:59 EDT = **P-0Y0M2DT1H21M31S** | BACK: 2010-03-15 19:59:59 EDT + P-0Y0M2DT1H21M31S = **2010-03-13 18:38:28 EST** | DAYS: **2** -test_time_spring_type2_post_type3_st: FWD: 2010-03-14 00:10:20 EST - 2010-03-15 19:59:59 EDT = **P-0Y0M1DT18H49M39S** | BACK: 2010-03-15 19:59:59 EDT + P-0Y0M1DT18H49M39S = **2010-03-14 00:10:20 EST** | DAYS: **1** -test_time_spring_type2_post_type3_dt: FWD: 2010-03-14 03:16:55 EDT - 2010-03-15 19:59:59 EDT = **P-0Y0M1DT16H43M4S** | BACK: 2010-03-15 19:59:59 EDT + P-0Y0M1DT16H43M4S = **2010-03-14 03:16:55 EDT** | DAYS: **1** -test_time_spring_type2_post_type3_post: FWD: 2010-03-15 19:59:59 EDT - 2010-03-15 18:57:55 EDT = **P+0Y0M0DT1H2M4S** | BACK: 2010-03-15 18:57:55 EDT + P+0Y0M0DT1H2M4S = **2010-03-15 19:59:59 EDT** | DAYS: **0** +echo "test_time_spring_type2_stsec_type3_dtsec: "; +$end = new DateTime('2010-03-15 03:00:00'); // dtsec, zt3 +$start = new DateTime('2010-03-13 01:59:59 EST'); // stsec, zt2 +examine_diff($end, $start, 'P+0Y0M0DT0H0M1S', 0); + +echo "test_time_spring_type2_dtsec_type3_stsec: "; +$end = new DateTime('2010-03-15 01:59:59'); // stsec, zt3 +$start = new DateTime('2010-03-13 03:00:00 EDT'); // dtsec, zt2 +examine_diff($end, $start, 'P-0Y0M0DT0H0M1S', 0); diff --git a/ext/date/tests/DateTime_diff_add_sub-spring-type3-type2.phpt b/ext/date/tests/DateTime_data-spring-type3-type2.inc index cf0e6a582..244cd5881 100644 --- a/ext/date/tests/DateTime_diff_add_sub-spring-type3-type2.phpt +++ b/ext/date/tests/DateTime_data-spring-type3-type2.inc @@ -1,10 +1,3 @@ ---TEST-- -DateTime::diff() add() sub() -- spring type3 type2 ---CREDITS-- -Daniel Convissor <danielc@php.net> ---XFAIL-- -PHP < 5.4 has bugs ---FILE-- <?php /* @@ -12,7 +5,6 @@ PHP < 5.4 has bugs * in a userland package. Please be so kind as to leave them. */ -require './examine_diff.inc'; date_default_timezone_set('America/New_York'); @@ -23,6 +15,8 @@ date_default_timezone_set('America/New_York'); * + st: standard time on transition day 2010-03-14 00:10:20 EST * + dt: daylight time on the transition day 2010-03-14 03:16:55 EDT * + post: the day after the transition day 2010-03-15 19:59:59 EDT + * + stsec: standard time 1 sec before change 2010-03-14 01:59:59 EST + * + dtsec: daylight time first second 2010-03-14 03:00:00 EDT */ echo "test_time_spring_type3_prev_type2_prev: "; $end = new DateTime('2010-03-13 18:38:28 EST'); // prev, zt2 @@ -104,21 +98,12 @@ $end = new DateTime('2010-03-15 19:59:59 EDT'); // post, zt2 $start = new DateTime('2010-03-15 18:57:55'); // sp post, zt3 examine_diff($end, $start, 'P+0Y0M0DT1H2M4S', 0); -?> ---EXPECT-- -test_time_spring_type3_prev_type2_prev: FWD: 2010-03-13 18:38:28 EST - 2010-02-11 02:18:48 EST = **P+0Y1M2DT16H19M40S** | BACK: 2010-02-11 02:18:48 EST + P+0Y1M2DT16H19M40S = **2010-03-13 18:38:28 EST** | DAYS: **30** -test_time_spring_type3_prev_type2_st: FWD: 2010-03-14 00:10:20 EST - 2010-03-13 18:38:28 EST = **P+0Y0M0DT5H31M52S** | BACK: 2010-03-13 18:38:28 EST + P+0Y0M0DT5H31M52S = **2010-03-14 00:10:20 EST** | DAYS: **0** -test_time_spring_type3_prev_type2_dt: FWD: 2010-03-14 03:16:55 EDT - 2010-03-13 18:38:28 EST = **P+0Y0M0DT7H38M27S** | BACK: 2010-03-13 18:38:28 EST + P+0Y0M0DT7H38M27S = **2010-03-14 03:16:55 EDT** | DAYS: **0** -test_time_spring_type3_prev_type2_post: FWD: 2010-03-15 19:59:59 EDT - 2010-03-13 18:38:28 EST = **P+0Y0M2DT1H21M31S** | BACK: 2010-03-13 18:38:28 EST + P+0Y0M2DT1H21M31S = **2010-03-15 19:59:59 EDT** | DAYS: **2** -test_time_spring_type3_st_type2_prev: FWD: 2010-03-13 18:38:28 EST - 2010-03-14 00:10:20 EST = **P-0Y0M0DT5H31M52S** | BACK: 2010-03-14 00:10:20 EST + P-0Y0M0DT5H31M52S = **2010-03-13 18:38:28 EST** | DAYS: **0** -test_time_spring_type3_st_type2_st: FWD: 2010-03-14 00:15:35 EST - 2010-03-14 00:10:20 EST = **P+0Y0M0DT0H5M15S** | BACK: 2010-03-14 00:10:20 EST + P+0Y0M0DT0H5M15S = **2010-03-14 00:15:35 EST** | DAYS: **0** -test_time_spring_type3_st_type2_dt: FWD: 2010-03-14 03:16:55 EDT - 2010-03-14 00:10:20 EST = **P+0Y0M0DT2H6M35S** | BACK: 2010-03-14 00:10:20 EST + P+0Y0M0DT2H6M35S = **2010-03-14 03:16:55 EDT** | DAYS: **0** -test_time_spring_type3_st_type2_post: FWD: 2010-03-15 19:59:59 EDT - 2010-03-14 00:10:20 EST = **P+0Y0M1DT18H49M39S** | BACK: 2010-03-14 00:10:20 EST + P+0Y0M1DT18H49M39S = **2010-03-15 19:59:59 EDT** | DAYS: **1** -test_time_spring_type3_dt_type2_prev: FWD: 2010-03-13 18:38:28 EST - 2010-03-14 03:16:55 EDT = **P-0Y0M0DT7H38M27S** | BACK: 2010-03-14 03:16:55 EDT + P-0Y0M0DT7H38M27S = **2010-03-13 18:38:28 EST** | DAYS: **0** -test_time_spring_type3_dt_type2_st: FWD: 2010-03-14 00:10:20 EST - 2010-03-14 03:16:55 EDT = **P-0Y0M0DT2H6M35S** | BACK: 2010-03-14 03:16:55 EDT + P-0Y0M0DT2H6M35S = **2010-03-14 00:10:20 EST** | DAYS: **0** -test_time_spring_type3_dt_type2_dt: FWD: 2010-03-14 05:19:56 EDT - 2010-03-14 03:16:55 EDT = **P+0Y0M0DT2H3M1S** | BACK: 2010-03-14 03:16:55 EDT + P+0Y0M0DT2H3M1S = **2010-03-14 05:19:56 EDT** | DAYS: **0** -test_time_spring_type3_dt_type2_post: FWD: 2010-03-15 19:59:59 EDT - 2010-03-14 03:16:55 EDT = **P+0Y0M1DT16H43M4S** | BACK: 2010-03-14 03:16:55 EDT + P+0Y0M1DT16H43M4S = **2010-03-15 19:59:59 EDT** | DAYS: **1** -test_time_spring_type3_post_type2_prev: FWD: 2010-03-13 18:38:28 EST - 2010-03-15 19:59:59 EDT = **P-0Y0M2DT1H21M31S** | BACK: 2010-03-15 19:59:59 EDT + P-0Y0M2DT1H21M31S = **2010-03-13 18:38:28 EST** | DAYS: **2** -test_time_spring_type3_post_type2_st: FWD: 2010-03-14 00:10:20 EST - 2010-03-15 19:59:59 EDT = **P-0Y0M1DT18H49M39S** | BACK: 2010-03-15 19:59:59 EDT + P-0Y0M1DT18H49M39S = **2010-03-14 00:10:20 EST** | DAYS: **1** -test_time_spring_type3_post_type2_dt: FWD: 2010-03-14 03:16:55 EDT - 2010-03-15 19:59:59 EDT = **P-0Y0M1DT16H43M4S** | BACK: 2010-03-15 19:59:59 EDT + P-0Y0M1DT16H43M4S = **2010-03-14 03:16:55 EDT** | DAYS: **1** -test_time_spring_type3_post_type2_post: FWD: 2010-03-15 19:59:59 EDT - 2010-03-15 18:57:55 EDT = **P+0Y0M0DT1H2M4S** | BACK: 2010-03-15 18:57:55 EDT + P+0Y0M0DT1H2M4S = **2010-03-15 19:59:59 EDT** | DAYS: **0** +echo "test_time_spring_type3_stsec_type2_dtsec: "; +$end = new DateTime('2010-03-15 03:00:00 EDT'); // dtsec, zt2 +$start = new DateTime('2010-03-13 01:59:59'); // stsec, zt3 +examine_diff($end, $start, 'P+0Y0M0DT0H0M1S', 0); + +echo "test_time_spring_type3_dtsec_type2_stsec: "; +$end = new DateTime('2010-03-15 01:59:59 EST'); // stsec, zt2 +$start = new DateTime('2010-03-13 03:00:00'); // dtsec, zt3 +examine_diff($end, $start, 'P-0Y0M0DT0H0M1S', 0); diff --git a/ext/date/tests/DateTime_diff_add_sub-spring-type3-type3.phpt b/ext/date/tests/DateTime_data-spring-type3-type3.inc index afb8cdaf5..d87373c5a 100644 --- a/ext/date/tests/DateTime_diff_add_sub-spring-type3-type3.phpt +++ b/ext/date/tests/DateTime_data-spring-type3-type3.inc @@ -1,10 +1,3 @@ ---TEST-- -DateTime::diff() add() sub() -- spring type3 type3 ---CREDITS-- -Daniel Convissor <danielc@php.net> ---XFAIL-- -PHP < 5.4 has bugs ---FILE-- <?php /* @@ -12,7 +5,6 @@ PHP < 5.4 has bugs * in a userland package. Please be so kind as to leave them. */ -require './examine_diff.inc'; date_default_timezone_set('America/New_York'); @@ -23,6 +15,8 @@ date_default_timezone_set('America/New_York'); * + st: standard time on transition day 2010-03-14 00:10:20 * + dt: daylight time on the transition day 2010-03-14 03:16:55 * + post: the day after the transition day 2010-03-15 19:59:59 + * + stsec: standard time 1 sec before change 2010-03-14 01:59:59 + * + dtsec: daylight time first second 2010-03-14 03:00:00 */ echo "test_time_spring_type3_prev_type3_prev: "; $end = new DateTime('2010-03-13 18:38:28'); // prev, zt3 @@ -104,21 +98,12 @@ $end = new DateTime('2010-03-15 19:59:59'); // post, zt3 $start = new DateTime('2010-03-15 18:57:55'); // sp post, zt3 examine_diff($end, $start, 'P+0Y0M0DT1H2M4S', 0); -?> ---EXPECT-- -test_time_spring_type3_prev_type3_prev: FWD: 2010-03-13 18:38:28 EST - 2010-02-11 02:18:48 EST = **P+0Y1M2DT16H19M40S** | BACK: 2010-02-11 02:18:48 EST + P+0Y1M2DT16H19M40S = **2010-03-13 18:38:28 EST** | DAYS: **30** -test_time_spring_type3_prev_type3_st: FWD: 2010-03-14 00:10:20 EST - 2010-03-13 18:38:28 EST = **P+0Y0M0DT5H31M52S** | BACK: 2010-03-13 18:38:28 EST + P+0Y0M0DT5H31M52S = **2010-03-14 00:10:20 EST** | DAYS: **0** -test_time_spring_type3_prev_type3_dt: FWD: 2010-03-14 03:16:55 EDT - 2010-03-13 18:38:28 EST = **P+0Y0M0DT7H38M27S** | BACK: 2010-03-13 18:38:28 EST + P+0Y0M0DT7H38M27S = **2010-03-14 03:16:55 EDT** | DAYS: **0** -test_time_spring_type3_prev_type3_post: FWD: 2010-03-15 19:59:59 EDT - 2010-03-13 18:38:28 EST = **P+0Y0M2DT1H21M31S** | BACK: 2010-03-13 18:38:28 EST + P+0Y0M2DT1H21M31S = **2010-03-15 19:59:59 EDT** | DAYS: **2** -test_time_spring_type3_st_type3_prev: FWD: 2010-03-13 18:38:28 EST - 2010-03-14 00:10:20 EST = **P-0Y0M0DT5H31M52S** | BACK: 2010-03-14 00:10:20 EST + P-0Y0M0DT5H31M52S = **2010-03-13 18:38:28 EST** | DAYS: **0** -test_time_spring_type3_st_type3_st: FWD: 2010-03-14 00:15:35 EST - 2010-03-14 00:10:20 EST = **P+0Y0M0DT0H5M15S** | BACK: 2010-03-14 00:10:20 EST + P+0Y0M0DT0H5M15S = **2010-03-14 00:15:35 EST** | DAYS: **0** -test_time_spring_type3_st_type3_dt: FWD: 2010-03-14 03:16:55 EDT - 2010-03-14 00:10:20 EST = **P+0Y0M0DT2H6M35S** | BACK: 2010-03-14 00:10:20 EST + P+0Y0M0DT2H6M35S = **2010-03-14 03:16:55 EDT** | DAYS: **0** -test_time_spring_type3_st_type3_post: FWD: 2010-03-15 19:59:59 EDT - 2010-03-14 00:10:20 EST = **P+0Y0M1DT18H49M39S** | BACK: 2010-03-14 00:10:20 EST + P+0Y0M1DT18H49M39S = **2010-03-15 19:59:59 EDT** | DAYS: **1** -test_time_spring_type3_dt_type3_prev: FWD: 2010-03-13 18:38:28 EST - 2010-03-14 03:16:55 EDT = **P-0Y0M0DT7H38M27S** | BACK: 2010-03-14 03:16:55 EDT + P-0Y0M0DT7H38M27S = **2010-03-13 18:38:28 EST** | DAYS: **0** -test_time_spring_type3_dt_type3_st: FWD: 2010-03-14 00:10:20 EST - 2010-03-14 03:16:55 EDT = **P-0Y0M0DT2H6M35S** | BACK: 2010-03-14 03:16:55 EDT + P-0Y0M0DT2H6M35S = **2010-03-14 00:10:20 EST** | DAYS: **0** -test_time_spring_type3_dt_type3_dt: FWD: 2010-03-14 05:19:56 EDT - 2010-03-14 03:16:55 EDT = **P+0Y0M0DT2H3M1S** | BACK: 2010-03-14 03:16:55 EDT + P+0Y0M0DT2H3M1S = **2010-03-14 05:19:56 EDT** | DAYS: **0** -test_time_spring_type3_dt_type3_post: FWD: 2010-03-15 19:59:59 EDT - 2010-03-14 03:16:55 EDT = **P+0Y0M1DT16H43M4S** | BACK: 2010-03-14 03:16:55 EDT + P+0Y0M1DT16H43M4S = **2010-03-15 19:59:59 EDT** | DAYS: **1** -test_time_spring_type3_post_type3_prev: FWD: 2010-03-13 18:38:28 EST - 2010-03-15 19:59:59 EDT = **P-0Y0M2DT1H21M31S** | BACK: 2010-03-15 19:59:59 EDT + P-0Y0M2DT1H21M31S = **2010-03-13 18:38:28 EST** | DAYS: **2** -test_time_spring_type3_post_type3_st: FWD: 2010-03-14 00:10:20 EST - 2010-03-15 19:59:59 EDT = **P-0Y0M1DT18H49M39S** | BACK: 2010-03-15 19:59:59 EDT + P-0Y0M1DT18H49M39S = **2010-03-14 00:10:20 EST** | DAYS: **1** -test_time_spring_type3_post_type3_dt: FWD: 2010-03-14 03:16:55 EDT - 2010-03-15 19:59:59 EDT = **P-0Y0M1DT16H43M4S** | BACK: 2010-03-15 19:59:59 EDT + P-0Y0M1DT16H43M4S = **2010-03-14 03:16:55 EDT** | DAYS: **1** -test_time_spring_type3_post_type3_post: FWD: 2010-03-15 19:59:59 EDT - 2010-03-15 18:57:55 EDT = **P+0Y0M0DT1H2M4S** | BACK: 2010-03-15 18:57:55 EDT + P+0Y0M0DT1H2M4S = **2010-03-15 19:59:59 EDT** | DAYS: **0** +echo "test_time_spring_type3_stsec_type3_dtsec: "; +$end = new DateTime('2010-03-15 03:00:00'); // dtsec, zt3 +$start = new DateTime('2010-03-13 01:59:59'); // stsec, zt3 +examine_diff($end, $start, 'P+0Y0M0DT0H0M1S', 0); + +echo "test_time_spring_type3_dtsec_type3_stsec: "; +$end = new DateTime('2010-03-15 01:59:59'); // stsec, zt3 +$start = new DateTime('2010-03-13 03:00:00'); // dtsec, zt3 +examine_diff($end, $start, 'P-0Y0M0DT0H0M1S', 0); diff --git a/ext/date/tests/DateTime_days-absolute.phpt b/ext/date/tests/DateTime_days-absolute.phpt new file mode 100644 index 000000000..7a150e39a --- /dev/null +++ b/ext/date/tests/DateTime_days-absolute.phpt @@ -0,0 +1,15 @@ +--TEST-- +DateTime::diff() days -- absolute +--CREDITS-- +Daniel Convissor <danielc@php.net> +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_DAYS); +require 'DateTime_data-absolute.inc'; + +?> +--EXPECT-- +test_absolute_7: DAYS: **7** +test_absolute_negative_7: DAYS: **7** diff --git a/ext/date/tests/DateTime_days-dates.phpt b/ext/date/tests/DateTime_days-dates.phpt new file mode 100644 index 000000000..446d56d78 --- /dev/null +++ b/ext/date/tests/DateTime_days-dates.phpt @@ -0,0 +1,29 @@ +--TEST-- +DateTime::diff() days -- dates +--CREDITS-- +Daniel Convissor <danielc@php.net> +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_DAYS); +require 'DateTime_data-dates.inc'; + +?> +--EXPECT-- +test__7: DAYS: **7** +test_years_positive__7_by_0_day: DAYS: **2557** +test_years_positive__7_by_1_day: DAYS: **2558** +test_years_positive__6_shy_1_day: DAYS: **2556** +test_years_positive__7_by_1_month: DAYS: **2585** +test_years_positive__6_shy_1_month: DAYS: **2526** +test_years_positive__7_by_1_month_split_newyear: DAYS: **2588** +test_years_positive__6_shy_1_month_split_newyear: DAYS: **2526** +test_negative__7: DAYS: **7** +test_years_negative__7_by_0_day: DAYS: **2557** +test_years_negative__7_by_1_day: DAYS: **2558** +test_years_negative__6_shy_1_day: DAYS: **2556** +test_years_negative__7_by_1_month: DAYS: **2585** +test_years_negative__6_shy_1_month: DAYS: **2526** +test_years_negative__7_by_1_month_split_newyear: DAYS: **2588** +test_years_negative__6_shy_1_month_split_newyear: DAYS: **2526** diff --git a/ext/date/tests/DateTime_days-fall-type2-type2.phpt b/ext/date/tests/DateTime_days-fall-type2-type2.phpt new file mode 100644 index 000000000..3af156c76 --- /dev/null +++ b/ext/date/tests/DateTime_days-fall-type2-type2.phpt @@ -0,0 +1,51 @@ +--TEST-- +DateTime::diff() days -- fall type2 type2 +--CREDITS-- +Daniel Convissor <danielc@php.net> +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_DAYS); +require 'DateTime_data-fall-type2-type2.inc'; + +?> +--EXPECT-- +test_time_fall_type2_prev_type2_prev: DAYS: **33** +test_time_fall_type2_prev_type2_dt: DAYS: **0** +test_time_fall_type2_prev_type2_redodt: DAYS: **0** +test_time_fall_type2_prev_type2_redost: DAYS: **0** +test_time_fall_type2_prev_type2_st: DAYS: **0** +test_time_fall_type2_prev_type2_post: DAYS: **2** +test_time_fall_type2_dt_type2_prev: DAYS: **0** +test_time_fall_type2_dt_type2_dt: DAYS: **0** +test_time_fall_type2_dt_type2_redodt: DAYS: **0** +test_time_fall_type2_dt_type2_redost: DAYS: **0** +test_time_fall_type2_dt_type2_st: DAYS: **0** +test_time_fall_type2_dt_type2_post: DAYS: **1** +test_time_fall_type2_redodt_type2_prev: DAYS: **0** +test_time_fall_type2_redodt_type2_dt: DAYS: **0** +test_time_fall_type2_redodt_type2_redodt: DAYS: **0** +test_time_fall_type2_redodt_type2_redost: DAYS: **0** +test_time_fall_type2_redodt_type2_st: DAYS: **0** +test_time_fall_type2_redodt_type2_post: DAYS: **1** +test_time_fall_type2_redost_type2_prev: DAYS: **0** +test_time_fall_type2_redost_type2_dt: DAYS: **0** +test_time_fall_type2_redost_type2_redodt: DAYS: **0** +test_time_fall_type2_redost_type2_redost: DAYS: **0** +test_time_fall_type2_redost_type2_st: DAYS: **0** +test_time_fall_type2_redost_type2_post: DAYS: **1** +test_time_fall_type2_st_type2_prev: DAYS: **0** +test_time_fall_type2_st_type2_dt: DAYS: **0** +test_time_fall_type2_st_type2_redodt: DAYS: **0** +test_time_fall_type2_st_type2_redost: DAYS: **0** +test_time_fall_type2_st_type2_st: DAYS: **0** +test_time_fall_type2_st_type2_post: DAYS: **1** +test_time_fall_type2_post_type2_prev: DAYS: **2** +test_time_fall_type2_post_type2_dt: DAYS: **1** +test_time_fall_type2_post_type2_redodt: DAYS: **1** +test_time_fall_type2_post_type2_redost: DAYS: **1** +test_time_fall_type2_post_type2_st: DAYS: **1** +test_time_fall_type2_post_type2_post: DAYS: **0** +test_time_fall_type2_dtsec_type2_stsec: DAYS: **0** +test_time_fall_type2_stsec_type2_dtsec: DAYS: **0** diff --git a/ext/date/tests/DateTime_days-fall-type2-type3.phpt b/ext/date/tests/DateTime_days-fall-type2-type3.phpt new file mode 100644 index 000000000..8a4296b76 --- /dev/null +++ b/ext/date/tests/DateTime_days-fall-type2-type3.phpt @@ -0,0 +1,51 @@ +--TEST-- +DateTime::diff() days -- fall type2 type3 +--CREDITS-- +Daniel Convissor <danielc@php.net> +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_DAYS); +require 'DateTime_data-fall-type2-type3.inc'; + +?> +--EXPECT-- +test_time_fall_type2_prev_type3_prev: DAYS: **33** +test_time_fall_type2_prev_type3_dt: DAYS: **0** +test_time_fall_type2_prev_type3_redodt: DAYS: **0** +test_time_fall_type2_prev_type3_redost: DAYS: **0** +test_time_fall_type2_prev_type3_st: DAYS: **0** +test_time_fall_type2_prev_type3_post: DAYS: **2** +test_time_fall_type2_dt_type3_prev: DAYS: **0** +test_time_fall_type2_dt_type3_dt: DAYS: **0** +test_time_fall_type2_dt_type3_redodt: DAYS: **0** +test_time_fall_type2_dt_type3_redost: DAYS: **0** +test_time_fall_type2_dt_type3_st: DAYS: **0** +test_time_fall_type2_dt_type3_post: DAYS: **1** +test_time_fall_type2_redodt_type3_prev: DAYS: **0** +test_time_fall_type2_redodt_type3_dt: DAYS: **0** +test_time_fall_type2_redodt_type3_redodt: DAYS: **0** +test_time_fall_type2_redodt_type3_redost: DAYS: **0** +test_time_fall_type2_redodt_type3_st: DAYS: **0** +test_time_fall_type2_redodt_type3_post: DAYS: **1** +test_time_fall_type2_redost_type3_prev: DAYS: **0** +test_time_fall_type2_redost_type3_dt: DAYS: **0** +test_time_fall_type2_redost_type3_redodt: DAYS: **0** +test_time_fall_type2_redost_type3_redost: DAYS: **0** +test_time_fall_type2_redost_type3_st: DAYS: **0** +test_time_fall_type2_redost_type3_post: DAYS: **1** +test_time_fall_type2_st_type3_prev: DAYS: **0** +test_time_fall_type2_st_type3_dt: DAYS: **0** +test_time_fall_type2_st_type3_redodt: DAYS: **0** +test_time_fall_type2_st_type3_redost: DAYS: **0** +test_time_fall_type2_st_type3_st: DAYS: **0** +test_time_fall_type2_st_type3_post: DAYS: **1** +test_time_fall_type2_post_type3_prev: DAYS: **2** +test_time_fall_type2_post_type3_dt: DAYS: **1** +test_time_fall_type2_post_type3_redodt: DAYS: **1** +test_time_fall_type2_post_type3_redost: DAYS: **1** +test_time_fall_type2_post_type3_st: DAYS: **1** +test_time_fall_type2_post_type3_post: DAYS: **0** +test_time_fall_type2_dtsec_type3_stsec: DAYS: **0** +test_time_fall_type2_stsec_type3_dtsec: DAYS: **0** diff --git a/ext/date/tests/DateTime_days-fall-type3-type2.phpt b/ext/date/tests/DateTime_days-fall-type3-type2.phpt new file mode 100644 index 000000000..d5456036c --- /dev/null +++ b/ext/date/tests/DateTime_days-fall-type3-type2.phpt @@ -0,0 +1,51 @@ +--TEST-- +DateTime::diff() days -- fall type3 type2 +--CREDITS-- +Daniel Convissor <danielc@php.net> +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_DAYS); +require 'DateTime_data-fall-type3-type2.inc'; + +?> +--EXPECT-- +test_time_fall_type3_prev_type2_prev: DAYS: **33** +test_time_fall_type3_prev_type2_dt: DAYS: **0** +test_time_fall_type3_prev_type2_redodt: DAYS: **0** +test_time_fall_type3_prev_type2_redost: DAYS: **0** +test_time_fall_type3_prev_type2_st: DAYS: **0** +test_time_fall_type3_prev_type2_post: DAYS: **2** +test_time_fall_type3_dt_type2_prev: DAYS: **0** +test_time_fall_type3_dt_type2_dt: DAYS: **0** +test_time_fall_type3_dt_type2_redodt: DAYS: **0** +test_time_fall_type3_dt_type2_redost: DAYS: **0** +test_time_fall_type3_dt_type2_st: DAYS: **0** +test_time_fall_type3_dt_type2_post: DAYS: **1** +test_time_fall_type3_redodt_type2_prev: DAYS: **0** +test_time_fall_type3_redodt_type2_dt: DAYS: **0** +test_time_fall_type3_redodt_type2_redodt: DAYS: **0** +test_time_fall_type3_redodt_type2_redost: DAYS: **0** +test_time_fall_type3_redodt_type2_st: DAYS: **0** +test_time_fall_type3_redodt_type2_post: DAYS: **1** +test_time_fall_type3_redost_type2_prev: DAYS: **0** +test_time_fall_type3_redost_type2_dt: DAYS: **0** +test_time_fall_type3_redost_type2_redodt: DAYS: **0** +test_time_fall_type3_redost_type2_redost: DAYS: **0** +test_time_fall_type3_redost_type2_st: DAYS: **0** +test_time_fall_type3_redost_type2_post: DAYS: **1** +test_time_fall_type3_st_type2_prev: DAYS: **0** +test_time_fall_type3_st_type2_dt: DAYS: **0** +test_time_fall_type3_st_type2_redodt: DAYS: **0** +test_time_fall_type3_st_type2_redost: DAYS: **0** +test_time_fall_type3_st_type2_st: DAYS: **0** +test_time_fall_type3_st_type2_post: DAYS: **1** +test_time_fall_type3_post_type2_prev: DAYS: **2** +test_time_fall_type3_post_type2_dt: DAYS: **1** +test_time_fall_type3_post_type2_redodt: DAYS: **1** +test_time_fall_type3_post_type2_redost: DAYS: **1** +test_time_fall_type3_post_type2_st: DAYS: **1** +test_time_fall_type3_post_type2_post: DAYS: **0** +test_time_fall_type3_dtsec_type2_stsec: DAYS: **0** +test_time_fall_type3_stsec_type2_dtsec: DAYS: **0** diff --git a/ext/date/tests/DateTime_days-fall-type3-type3.phpt b/ext/date/tests/DateTime_days-fall-type3-type3.phpt new file mode 100644 index 000000000..38e4ef50f --- /dev/null +++ b/ext/date/tests/DateTime_days-fall-type3-type3.phpt @@ -0,0 +1,51 @@ +--TEST-- +DateTime::diff() days -- fall type3 type3 +--CREDITS-- +Daniel Convissor <danielc@php.net> +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_DAYS); +require 'DateTime_data-fall-type3-type3.inc'; + +?> +--EXPECT-- +test_time_fall_type3_prev_type3_prev: DAYS: **33** +test_time_fall_type3_prev_type3_dt: DAYS: **0** +test_time_fall_type3_prev_type3_redodt: DAYS: **0** +test_time_fall_type3_prev_type3_redost: DAYS: **0** +test_time_fall_type3_prev_type3_st: DAYS: **0** +test_time_fall_type3_prev_type3_post: DAYS: **2** +test_time_fall_type3_dt_type3_prev: DAYS: **0** +test_time_fall_type3_dt_type3_dt: DAYS: **0** +test_time_fall_type3_dt_type3_redodt: DAYS: **0** +test_time_fall_type3_dt_type3_redost: DAYS: **0** +test_time_fall_type3_dt_type3_st: DAYS: **0** +test_time_fall_type3_dt_type3_post: DAYS: **1** +test_time_fall_type3_redodt_type3_prev: DAYS: **0** +test_time_fall_type3_redodt_type3_dt: DAYS: **0** +test_time_fall_type3_redodt_type3_redodt: DAYS: **0** +test_time_fall_type3_redodt_type3_redost: DAYS: **0** +test_time_fall_type3_redodt_type3_st: DAYS: **0** +test_time_fall_type3_redodt_type3_post: DAYS: **1** +test_time_fall_type3_redost_type3_prev: DAYS: **0** +test_time_fall_type3_redost_type3_dt: DAYS: **0** +test_time_fall_type3_redost_type3_redodt: DAYS: **0** +test_time_fall_type3_redost_type3_redost: DAYS: **0** +test_time_fall_type3_redost_type3_st: DAYS: **0** +test_time_fall_type3_redost_type3_post: DAYS: **1** +test_time_fall_type3_st_type3_prev: DAYS: **0** +test_time_fall_type3_st_type3_dt: DAYS: **0** +test_time_fall_type3_st_type3_redodt: DAYS: **0** +test_time_fall_type3_st_type3_redost: DAYS: **0** +test_time_fall_type3_st_type3_st: DAYS: **0** +test_time_fall_type3_st_type3_post: DAYS: **1** +test_time_fall_type3_post_type3_prev: DAYS: **2** +test_time_fall_type3_post_type3_dt: DAYS: **1** +test_time_fall_type3_post_type3_redodt: DAYS: **1** +test_time_fall_type3_post_type3_redost: DAYS: **1** +test_time_fall_type3_post_type3_st: DAYS: **1** +test_time_fall_type3_post_type3_post: DAYS: **0** +test_time_fall_type3_dtsec_type3_stsec: DAYS: **0** +test_time_fall_type3_stsec_type3_dtsec: DAYS: **0** diff --git a/ext/date/tests/DateTime_days-february.phpt b/ext/date/tests/DateTime_days-february.phpt new file mode 100644 index 000000000..b8107d703 --- /dev/null +++ b/ext/date/tests/DateTime_days-february.phpt @@ -0,0 +1,77 @@ +--TEST-- +DateTime::diff() days -- february +--CREDITS-- +Daniel Convissor <danielc@php.net> +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_DAYS); +require 'DateTime_data-february.inc'; + +?> +--EXPECT-- +test_bug_49081__1: DAYS: **30** +test_bug_49081__2: DAYS: **31** +test_bug_49081__3: DAYS: **1** +test_bug_49081__4: DAYS: **29** +test_bug_49081__5: DAYS: **30** +test_bug_49081__6: DAYS: **31** +test_bug_49081__7: DAYS: **32** +test_bug_49081__8: DAYS: **28** +test_bug_49081__9: DAYS: **29** +test_bug_49081__10: DAYS: **30** +test_bug_49081__11: DAYS: **31** +test_bug_49081__12: DAYS: **1** +test_bug_49081__13: DAYS: **27** +test_bug_49081__14: DAYS: **28** +test_bug_49081__15: DAYS: **29** +test_bug_49081__16: DAYS: **30** +test_bug_49081__17: DAYS: **31** +test_bug_49081__18: DAYS: **32** +test_bug_49081__19: DAYS: **59** +test_bug_49081__20: DAYS: **29** +test_bug_49081__21: DAYS: **55** +test_bug_49081__22: DAYS: **56** +test_bug_49081__23: DAYS: **57** +test_bug_49081__24: DAYS: **58** +test_bug_49081__25: DAYS: **59** +test_bug_49081__26: DAYS: **60** +test_bug_49081__27: DAYS: **30** +test_bug_49081__28: DAYS: **27** +test_bug_49081__29: DAYS: **28** +test_bug_49081__30: DAYS: **29** +test_bug_49081__31: DAYS: **28** +test_bug_49081__32: DAYS: **29** +test_bug_49081_negative__1: DAYS: **30** +test_bug_49081_negative__2: DAYS: **31** +test_bug_49081_negative__3: DAYS: **1** +test_bug_49081_negative__4: DAYS: **29** +test_bug_49081_negative__5: DAYS: **30** +test_bug_49081_negative__6: DAYS: **31** +test_bug_49081_negative__7: DAYS: **32** +test_bug_49081_negative__8: DAYS: **28** +test_bug_49081_negative__9: DAYS: **29** +test_bug_49081_negative__10: DAYS: **30** +test_bug_49081_negative__11: DAYS: **31** +test_bug_49081_negative__12: DAYS: **1** +test_bug_49081_negative__13: DAYS: **27** +test_bug_49081_negative__14: DAYS: **28** +test_bug_49081_negative__15: DAYS: **29** +test_bug_49081_negative__16: DAYS: **30** +test_bug_49081_negative__17: DAYS: **31** +test_bug_49081_negative__18: DAYS: **32** +test_bug_49081_negative__19: DAYS: **59** +test_bug_49081_negative__20: DAYS: **29** +test_bug_49081_negative__21: DAYS: **55** +test_bug_49081_negative__22: DAYS: **56** +test_bug_49081_negative__23: DAYS: **57** +test_bug_49081_negative__24: DAYS: **58** +test_bug_49081_negative__25: DAYS: **59** +test_bug_49081_negative__26: DAYS: **60** +test_bug_49081_negative__27: DAYS: **30** +test_bug_49081_negative__28: DAYS: **27** +test_bug_49081_negative__29: DAYS: **28** +test_bug_49081_negative__30: DAYS: **29** +test_bug_49081_negative__31: DAYS: **28** +test_bug_49081_negative__32: DAYS: **29** diff --git a/ext/date/tests/DateTime_days-massive.phpt b/ext/date/tests/DateTime_days-massive.phpt new file mode 100644 index 000000000..de51a6658 --- /dev/null +++ b/ext/date/tests/DateTime_days-massive.phpt @@ -0,0 +1,15 @@ +--TEST-- +DateTime::diff() days -- massive +--CREDITS-- +Daniel Convissor <danielc@php.net> +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_DAYS); +require 'DateTime_data-massive.inc'; + +?> +--EXPECT-- +test_massive_positive: DAYS: **243494757** +test_massive_negative: DAYS: **243494757** diff --git a/ext/date/tests/DateTime_days-spring-type2-type2.phpt b/ext/date/tests/DateTime_days-spring-type2-type2.phpt new file mode 100644 index 000000000..3f9c35352 --- /dev/null +++ b/ext/date/tests/DateTime_days-spring-type2-type2.phpt @@ -0,0 +1,31 @@ +--TEST-- +DateTime::diff() days -- spring type2 type2 +--CREDITS-- +Daniel Convissor <danielc@php.net> +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_DAYS); +require 'DateTime_data-spring-type2-type2.inc'; + +?> +--EXPECT-- +test_time_spring_type2_prev_type2_prev: DAYS: **30** +test_time_spring_type2_prev_type2_st: DAYS: **0** +test_time_spring_type2_prev_type2_dt: DAYS: **0** +test_time_spring_type2_prev_type2_post: DAYS: **2** +test_time_spring_type2_st_type2_prev: DAYS: **0** +test_time_spring_type2_st_type2_st: DAYS: **0** +test_time_spring_type2_st_type2_dt: DAYS: **0** +test_time_spring_type2_st_type2_post: DAYS: **1** +test_time_spring_type2_dt_type2_prev: DAYS: **0** +test_time_spring_type2_dt_type2_st: DAYS: **0** +test_time_spring_type2_dt_type2_dt: DAYS: **0** +test_time_spring_type2_dt_type2_post: DAYS: **1** +test_time_spring_type2_post_type2_prev: DAYS: **2** +test_time_spring_type2_post_type2_st: DAYS: **1** +test_time_spring_type2_post_type2_dt: DAYS: **1** +test_time_spring_type2_post_type2_post: DAYS: **0** +test_time_spring_type2_stsec_type2_dtsec: DAYS: **0** +test_time_spring_type2_dtsec_type2_stsec: DAYS: **0** diff --git a/ext/date/tests/DateTime_days-spring-type2-type3.phpt b/ext/date/tests/DateTime_days-spring-type2-type3.phpt new file mode 100644 index 000000000..77ac5fdb6 --- /dev/null +++ b/ext/date/tests/DateTime_days-spring-type2-type3.phpt @@ -0,0 +1,31 @@ +--TEST-- +DateTime::diff() days -- spring type2 type3 +--CREDITS-- +Daniel Convissor <danielc@php.net> +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_DAYS); +require 'DateTime_data-spring-type2-type3.inc'; + +?> +--EXPECT-- +test_time_spring_type2_prev_type3_prev: DAYS: **30** +test_time_spring_type2_prev_type3_st: DAYS: **0** +test_time_spring_type2_prev_type3_dt: DAYS: **0** +test_time_spring_type2_prev_type3_post: DAYS: **2** +test_time_spring_type2_st_type3_prev: DAYS: **0** +test_time_spring_type2_st_type3_st: DAYS: **0** +test_time_spring_type2_st_type3_dt: DAYS: **0** +test_time_spring_type2_st_type3_post: DAYS: **1** +test_time_spring_type2_dt_type3_prev: DAYS: **0** +test_time_spring_type2_dt_type3_st: DAYS: **0** +test_time_spring_type2_dt_type3_dt: DAYS: **0** +test_time_spring_type2_dt_type3_post: DAYS: **1** +test_time_spring_type2_post_type3_prev: DAYS: **2** +test_time_spring_type2_post_type3_st: DAYS: **1** +test_time_spring_type2_post_type3_dt: DAYS: **1** +test_time_spring_type2_post_type3_post: DAYS: **0** +test_time_spring_type2_stsec_type3_dtsec: DAYS: **0** +test_time_spring_type2_dtsec_type3_stsec: DAYS: **0** diff --git a/ext/date/tests/DateTime_days-spring-type3-type2.phpt b/ext/date/tests/DateTime_days-spring-type3-type2.phpt new file mode 100644 index 000000000..09aa8d9c1 --- /dev/null +++ b/ext/date/tests/DateTime_days-spring-type3-type2.phpt @@ -0,0 +1,31 @@ +--TEST-- +DateTime::diff() days -- spring type3 type2 +--CREDITS-- +Daniel Convissor <danielc@php.net> +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_DAYS); +require 'DateTime_data-spring-type3-type2.inc'; + +?> +--EXPECT-- +test_time_spring_type3_prev_type2_prev: DAYS: **30** +test_time_spring_type3_prev_type2_st: DAYS: **0** +test_time_spring_type3_prev_type2_dt: DAYS: **0** +test_time_spring_type3_prev_type2_post: DAYS: **2** +test_time_spring_type3_st_type2_prev: DAYS: **0** +test_time_spring_type3_st_type2_st: DAYS: **0** +test_time_spring_type3_st_type2_dt: DAYS: **0** +test_time_spring_type3_st_type2_post: DAYS: **1** +test_time_spring_type3_dt_type2_prev: DAYS: **0** +test_time_spring_type3_dt_type2_st: DAYS: **0** +test_time_spring_type3_dt_type2_dt: DAYS: **0** +test_time_spring_type3_dt_type2_post: DAYS: **1** +test_time_spring_type3_post_type2_prev: DAYS: **2** +test_time_spring_type3_post_type2_st: DAYS: **1** +test_time_spring_type3_post_type2_dt: DAYS: **1** +test_time_spring_type3_post_type2_post: DAYS: **0** +test_time_spring_type3_stsec_type2_dtsec: DAYS: **0** +test_time_spring_type3_dtsec_type2_stsec: DAYS: **0** diff --git a/ext/date/tests/DateTime_days-spring-type3-type3.phpt b/ext/date/tests/DateTime_days-spring-type3-type3.phpt new file mode 100644 index 000000000..f947329de --- /dev/null +++ b/ext/date/tests/DateTime_days-spring-type3-type3.phpt @@ -0,0 +1,31 @@ +--TEST-- +DateTime::diff() days -- spring type3 type3 +--CREDITS-- +Daniel Convissor <danielc@php.net> +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_DAYS); +require 'DateTime_data-spring-type3-type3.inc'; + +?> +--EXPECT-- +test_time_spring_type3_prev_type3_prev: DAYS: **30** +test_time_spring_type3_prev_type3_st: DAYS: **0** +test_time_spring_type3_prev_type3_dt: DAYS: **0** +test_time_spring_type3_prev_type3_post: DAYS: **2** +test_time_spring_type3_st_type3_prev: DAYS: **0** +test_time_spring_type3_st_type3_st: DAYS: **0** +test_time_spring_type3_st_type3_dt: DAYS: **0** +test_time_spring_type3_st_type3_post: DAYS: **1** +test_time_spring_type3_dt_type3_prev: DAYS: **0** +test_time_spring_type3_dt_type3_st: DAYS: **0** +test_time_spring_type3_dt_type3_dt: DAYS: **0** +test_time_spring_type3_dt_type3_post: DAYS: **1** +test_time_spring_type3_post_type3_prev: DAYS: **2** +test_time_spring_type3_post_type3_st: DAYS: **1** +test_time_spring_type3_post_type3_dt: DAYS: **1** +test_time_spring_type3_post_type3_post: DAYS: **0** +test_time_spring_type3_stsec_type3_dtsec: DAYS: **0** +test_time_spring_type3_dtsec_type3_stsec: DAYS: **0** diff --git a/ext/date/tests/DateTime_diff-absolute.phpt b/ext/date/tests/DateTime_diff-absolute.phpt new file mode 100644 index 000000000..5295b3a72 --- /dev/null +++ b/ext/date/tests/DateTime_diff-absolute.phpt @@ -0,0 +1,15 @@ +--TEST-- +DateTime::diff() -- absolute +--CREDITS-- +Daniel Convissor <danielc@php.net> +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_DIFF); +require 'DateTime_data-absolute.inc'; + +?> +--EXPECT-- +test_absolute_7: DIFF: 2009-01-14 00:00:00 EST - 2009-01-07 00:00:00 EST = **P+0Y0M7DT0H0M0S** +test_absolute_negative_7: DIFF: 2009-01-07 00:00:00 EST - 2009-01-14 00:00:00 EST = **P+0Y0M7DT0H0M0S** diff --git a/ext/date/tests/DateTime_diff-dates.phpt b/ext/date/tests/DateTime_diff-dates.phpt new file mode 100644 index 000000000..71c5e1b6c --- /dev/null +++ b/ext/date/tests/DateTime_diff-dates.phpt @@ -0,0 +1,29 @@ +--TEST-- +DateTime::diff() -- dates +--CREDITS-- +Daniel Convissor <danielc@php.net> +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_DIFF); +require 'DateTime_data-dates.inc'; + +?> +--EXPECT-- +test__7: DIFF: 2009-01-14 00:00:00 EST - 2009-01-07 00:00:00 EST = **P+0Y0M7DT0H0M0S** +test_years_positive__7_by_0_day: DIFF: 2007-02-07 00:00:00 EST - 2000-02-07 00:00:00 EST = **P+7Y0M0DT0H0M0S** +test_years_positive__7_by_1_day: DIFF: 2007-02-08 00:00:00 EST - 2000-02-07 00:00:00 EST = **P+7Y0M1DT0H0M0S** +test_years_positive__6_shy_1_day: DIFF: 2007-02-06 00:00:00 EST - 2000-02-07 00:00:00 EST = **P+6Y11M30DT0H0M0S** +test_years_positive__7_by_1_month: DIFF: 2007-03-07 00:00:00 EST - 2000-02-07 00:00:00 EST = **P+7Y1M0DT0H0M0S** +test_years_positive__6_shy_1_month: DIFF: 2007-01-07 00:00:00 EST - 2000-02-07 00:00:00 EST = **P+6Y11M0DT0H0M0S** +test_years_positive__7_by_1_month_split_newyear: DIFF: 2007-01-07 00:00:00 EST - 1999-12-07 00:00:00 EST = **P+7Y1M0DT0H0M0S** +test_years_positive__6_shy_1_month_split_newyear: DIFF: 2006-12-07 00:00:00 EST - 2000-01-07 00:00:00 EST = **P+6Y11M0DT0H0M0S** +test_negative__7: DIFF: 2009-01-07 00:00:00 EST - 2009-01-14 00:00:00 EST = **P-0Y0M7DT0H0M0S** +test_years_negative__7_by_0_day: DIFF: 2000-02-07 00:00:00 EST - 2007-02-07 00:00:00 EST = **P-7Y0M0DT0H0M0S** +test_years_negative__7_by_1_day: DIFF: 2000-02-07 00:00:00 EST - 2007-02-08 00:00:00 EST = **P-7Y0M1DT0H0M0S** +test_years_negative__6_shy_1_day: DIFF: 2000-02-07 00:00:00 EST - 2007-02-06 00:00:00 EST = **P-6Y11M28DT0H0M0S** +test_years_negative__7_by_1_month: DIFF: 2000-02-07 00:00:00 EST - 2007-03-07 00:00:00 EST = **P-7Y1M0DT0H0M0S** +test_years_negative__6_shy_1_month: DIFF: 2000-02-07 00:00:00 EST - 2007-01-07 00:00:00 EST = **P-6Y11M0DT0H0M0S** +test_years_negative__7_by_1_month_split_newyear: DIFF: 1999-12-07 00:00:00 EST - 2007-01-07 00:00:00 EST = **P-7Y1M0DT0H0M0S** +test_years_negative__6_shy_1_month_split_newyear: DIFF: 2000-01-07 00:00:00 EST - 2006-12-07 00:00:00 EST = **P-6Y11M0DT0H0M0S** diff --git a/ext/date/tests/DateTime_diff-fall-type2-type2.phpt b/ext/date/tests/DateTime_diff-fall-type2-type2.phpt new file mode 100644 index 000000000..3e7cf7dbf --- /dev/null +++ b/ext/date/tests/DateTime_diff-fall-type2-type2.phpt @@ -0,0 +1,53 @@ +--TEST-- +DateTime::diff() -- fall type2 type2 +--CREDITS-- +Daniel Convissor <danielc@php.net> +--XFAIL-- +Various bugs exist +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_DIFF); +require 'DateTime_data-fall-type2-type2.inc'; + +?> +--EXPECT-- +test_time_fall_type2_prev_type2_prev: DIFF: 2010-11-06 18:38:28 EDT - 2010-10-04 02:18:48 EDT = **P+0Y1M2DT16H19M40S** +test_time_fall_type2_prev_type2_dt: DIFF: 2010-11-07 00:10:20 EDT - 2010-11-06 18:38:28 EDT = **P+0Y0M0DT5H31M52S** +test_time_fall_type2_prev_type2_redodt: DIFF: 2010-11-07 01:12:33 EDT - 2010-11-06 18:38:28 EDT = **P+0Y0M0DT6H34M5S** +test_time_fall_type2_prev_type2_redost: DIFF: 2010-11-07 01:14:44 EST - 2010-11-06 18:38:28 EDT = **P+0Y0M0DT7H36M16S** +test_time_fall_type2_prev_type2_st: DIFF: 2010-11-07 03:16:55 EST - 2010-11-06 18:38:28 EDT = **P+0Y0M0DT9H38M27S** +test_time_fall_type2_prev_type2_post: DIFF: 2010-11-08 19:59:59 EST - 2010-11-06 18:38:28 EDT = **P+0Y0M2DT1H21M31S** +test_time_fall_type2_dt_type2_prev: DIFF: 2010-11-06 18:38:28 EDT - 2010-11-07 00:10:20 EDT = **P-0Y0M0DT5H31M52S** +test_time_fall_type2_dt_type2_dt: DIFF: 2010-11-07 00:15:35 EDT - 2010-11-07 00:10:20 EDT = **P+0Y0M0DT0H5M15S** +test_time_fall_type2_dt_type2_redodt: DIFF: 2010-11-07 01:12:33 EDT - 2010-11-07 00:10:20 EDT = **P+0Y0M0DT1H2M13S** +test_time_fall_type2_dt_type2_redost: DIFF: 2010-11-07 01:14:44 EST - 2010-11-07 00:10:20 EDT = **P+0Y0M0DT2H4M24S** +test_time_fall_type2_dt_type2_st: DIFF: 2010-11-07 03:16:55 EST - 2010-11-07 00:10:20 EDT = **P+0Y0M0DT4H6M35S** +test_time_fall_type2_dt_type2_post: DIFF: 2010-11-08 19:59:59 EST - 2010-11-07 00:10:20 EDT = **P+0Y0M1DT20H49M39S** +test_time_fall_type2_redodt_type2_prev: DIFF: 2010-11-06 18:38:28 EDT - 2010-11-07 01:12:33 EDT = **P-0Y0M0DT6H34M5S** +test_time_fall_type2_redodt_type2_dt: DIFF: 2010-11-07 00:10:20 EDT - 2010-11-07 01:12:33 EDT = **P-0Y0M0DT1H2M13S** +test_time_fall_type2_redodt_type2_redodt: DIFF: 2010-11-07 01:15:35 EDT - 2010-11-07 01:12:33 EDT = **P+0Y0M0DT0H3M2S** +test_time_fall_type2_redodt_type2_redost: DIFF: 2010-11-07 01:14:44 EST - 2010-11-07 01:12:33 EDT = **P+0Y0M0DT1H2M11S** +test_time_fall_type2_redodt_type2_st: DIFF: 2010-11-07 03:16:55 EST - 2010-11-07 01:12:33 EDT = **P+0Y0M0DT3H4M22S** +test_time_fall_type2_redodt_type2_post: DIFF: 2010-11-08 19:59:59 EST - 2010-11-07 01:12:33 EDT = **P+0Y0M1DT19H47M26S** +test_time_fall_type2_redost_type2_prev: DIFF: 2010-11-06 18:38:28 EDT - 2010-11-07 01:14:44 EST = **P-0Y0M0DT7H36M16S** +test_time_fall_type2_redost_type2_dt: DIFF: 2010-11-07 00:10:20 EDT - 2010-11-07 01:14:44 EST = **P-0Y0M0DT2H4M24S** +test_time_fall_type2_redost_type2_redodt: DIFF: 2010-11-07 01:12:33 EDT - 2010-11-07 01:14:44 EST = **P-0Y0M0DT1H2M11S** +test_time_fall_type2_redost_type2_redost: DIFF: 2010-11-07 01:16:54 EST - 2010-11-07 01:14:44 EST = **P+0Y0M0DT0H2M10S** +test_time_fall_type2_redost_type2_st: DIFF: 2010-11-07 03:16:55 EST - 2010-11-07 01:14:44 EST = **P+0Y0M0DT2H2M11S** +test_time_fall_type2_redost_type2_post: DIFF: 2010-11-08 19:59:59 EST - 2010-11-07 01:14:44 EST = **P+0Y0M1DT18H45M15S** +test_time_fall_type2_st_type2_prev: DIFF: 2010-11-06 18:38:28 EDT - 2010-11-07 03:16:55 EST = **P-0Y0M0DT9H38M27S** +test_time_fall_type2_st_type2_dt: DIFF: 2010-11-07 00:10:20 EDT - 2010-11-07 03:16:55 EST = **P-0Y0M0DT4H6M35S** +test_time_fall_type2_st_type2_redodt: DIFF: 2010-11-07 01:12:33 EDT - 2010-11-07 03:16:55 EST = **P-0Y0M0DT3H4M22S** +test_time_fall_type2_st_type2_redost: DIFF: 2010-11-07 01:14:44 EST - 2010-11-07 03:16:55 EST = **P-0Y0M0DT2H2M11S** +test_time_fall_type2_st_type2_st: DIFF: 2010-11-07 05:19:56 EST - 2010-11-07 03:16:55 EST = **P+0Y0M0DT2H3M1S** +test_time_fall_type2_st_type2_post: DIFF: 2010-11-08 19:59:59 EST - 2010-11-07 03:16:55 EST = **P+0Y0M1DT16H43M4S** +test_time_fall_type2_post_type2_prev: DIFF: 2010-11-06 18:38:28 EDT - 2010-11-08 19:59:59 EST = **P-0Y0M2DT1H21M31S** +test_time_fall_type2_post_type2_dt: DIFF: 2010-11-07 00:10:20 EDT - 2010-11-08 19:59:59 EST = **P-0Y0M1DT20H49M39S** +test_time_fall_type2_post_type2_redodt: DIFF: 2010-11-07 01:12:33 EDT - 2010-11-08 19:59:59 EST = **P-0Y0M1DT19H47M26S** +test_time_fall_type2_post_type2_redost: DIFF: 2010-11-07 01:14:44 EST - 2010-11-08 19:59:59 EST = **P-0Y0M1DT18H45M15S** +test_time_fall_type2_post_type2_st: DIFF: 2010-11-07 03:16:55 EST - 2010-11-08 19:59:59 EST = **P-0Y0M1DT16H43M4S** +test_time_fall_type2_post_type2_post: DIFF: 2010-11-08 19:59:59 EST - 2010-11-08 18:57:55 EST = **P+0Y0M0DT1H2M4S** +test_time_fall_type2_dtsec_type2_stsec: DIFF: 2010-11-07 01:00:00 EST - 2010-11-07 01:59:59 EDT = **P+0Y0M0DT0H0M1S** +test_time_fall_type2_stsec_type2_dtsec: DIFF: 2010-11-07 01:59:59 EDT - 2010-11-07 01:00:00 EST = **P-0Y0M0DT0H0M1S** diff --git a/ext/date/tests/DateTime_diff-fall-type2-type3.phpt b/ext/date/tests/DateTime_diff-fall-type2-type3.phpt new file mode 100644 index 000000000..ec790f02e --- /dev/null +++ b/ext/date/tests/DateTime_diff-fall-type2-type3.phpt @@ -0,0 +1,53 @@ +--TEST-- +DateTime::diff() -- fall type2 type3 +--CREDITS-- +Daniel Convissor <danielc@php.net> +--XFAIL-- +Various bugs exist +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_DIFF); +require 'DateTime_data-fall-type2-type3.inc'; + +?> +--EXPECT-- +test_time_fall_type2_prev_type3_prev: DIFF: 2010-11-06 18:38:28 EDT - 2010-10-04 02:18:48 EDT = **P+0Y1M2DT16H19M40S** +test_time_fall_type2_prev_type3_dt: DIFF: 2010-11-07 00:10:20 EDT - 2010-11-06 18:38:28 EDT = **P+0Y0M0DT5H31M52S** +test_time_fall_type2_prev_type3_redodt: DIFF: 2010-11-07 01:12:33 EDT - 2010-11-06 18:38:28 EDT = **P+0Y0M0DT6H34M5S** +test_time_fall_type2_prev_type3_redost: DIFF: 2010-11-07 01:14:44 EST - 2010-11-06 18:38:28 EDT = **P+0Y0M0DT7H36M16S** +test_time_fall_type2_prev_type3_st: DIFF: 2010-11-07 03:16:55 EST - 2010-11-06 18:38:28 EDT = **P+0Y0M0DT9H38M27S** +test_time_fall_type2_prev_type3_post: DIFF: 2010-11-08 19:59:59 EST - 2010-11-06 18:38:28 EDT = **P+0Y0M2DT1H21M31S** +test_time_fall_type2_dt_type3_prev: DIFF: 2010-11-06 18:38:28 EDT - 2010-11-07 00:10:20 EDT = **P-0Y0M0DT5H31M52S** +test_time_fall_type2_dt_type3_dt: DIFF: 2010-11-07 00:15:35 EDT - 2010-11-07 00:10:20 EDT = **P+0Y0M0DT0H5M15S** +test_time_fall_type2_dt_type3_redodt: DIFF: 2010-11-07 01:12:33 EDT - 2010-11-07 00:10:20 EDT = **P+0Y0M0DT1H2M13S** +test_time_fall_type2_dt_type3_redost: DIFF: 2010-11-07 01:14:44 EST - 2010-11-07 00:10:20 EDT = **P+0Y0M0DT2H4M24S** +test_time_fall_type2_dt_type3_st: DIFF: 2010-11-07 03:16:55 EST - 2010-11-07 00:10:20 EDT = **P+0Y0M0DT4H6M35S** +test_time_fall_type2_dt_type3_post: DIFF: 2010-11-08 19:59:59 EST - 2010-11-07 00:10:20 EDT = **P+0Y0M1DT20H49M39S** +test_time_fall_type2_redodt_type3_prev: DIFF: 2010-11-06 18:38:28 EDT - 2010-11-07 01:12:33 EDT = **P-0Y0M0DT6H34M5S** +test_time_fall_type2_redodt_type3_dt: DIFF: 2010-11-07 00:10:20 EDT - 2010-11-07 01:12:33 EDT = **P-0Y0M0DT1H2M13S** +test_time_fall_type2_redodt_type3_redodt: DIFF: 2010-11-07 01:15:35 EDT - 2010-11-07 01:12:33 EDT = **P+0Y0M0DT0H3M2S** +test_time_fall_type2_redodt_type3_redost: DIFF: 2010-11-07 01:14:44 EST - 2010-11-07 01:12:33 EDT = **P+0Y0M0DT1H2M11S** +test_time_fall_type2_redodt_type3_st: DIFF: 2010-11-07 03:16:55 EST - 2010-11-07 01:12:33 EDT = **P+0Y0M0DT3H4M22S** +test_time_fall_type2_redodt_type3_post: DIFF: 2010-11-08 19:59:59 EST - 2010-11-07 01:12:33 EDT = **P+0Y0M1DT19H47M26S** +test_time_fall_type2_redost_type3_prev: DIFF: 2010-11-06 18:38:28 EDT - 2010-11-07 01:14:44 EST = **P-0Y0M0DT7H36M16S** +test_time_fall_type2_redost_type3_dt: DIFF: 2010-11-07 00:10:20 EDT - 2010-11-07 01:14:44 EST = **P-0Y0M0DT2H4M24S** +test_time_fall_type2_redost_type3_redodt: DIFF: 2010-11-07 01:12:33 EDT - 2010-11-07 01:14:44 EST = **P-0Y0M0DT1H2M11S** +test_time_fall_type2_redost_type3_redost: DIFF: 2010-11-07 01:16:54 EST - 2010-11-07 01:14:44 EST = **P+0Y0M0DT0H2M10S** +test_time_fall_type2_redost_type3_st: DIFF: 2010-11-07 03:16:55 EST - 2010-11-07 01:14:44 EST = **P+0Y0M0DT2H2M11S** +test_time_fall_type2_redost_type3_post: DIFF: 2010-11-08 19:59:59 EST - 2010-11-07 01:14:44 EST = **P+0Y0M1DT18H45M15S** +test_time_fall_type2_st_type3_prev: DIFF: 2010-11-06 18:38:28 EDT - 2010-11-07 03:16:55 EST = **P-0Y0M0DT9H38M27S** +test_time_fall_type2_st_type3_dt: DIFF: 2010-11-07 00:10:20 EDT - 2010-11-07 03:16:55 EST = **P-0Y0M0DT4H6M35S** +test_time_fall_type2_st_type3_redodt: DIFF: 2010-11-07 01:12:33 EDT - 2010-11-07 03:16:55 EST = **P-0Y0M0DT3H4M22S** +test_time_fall_type2_st_type3_redost: DIFF: 2010-11-07 01:14:44 EST - 2010-11-07 03:16:55 EST = **P-0Y0M0DT2H2M11S** +test_time_fall_type2_st_type3_st: DIFF: 2010-11-07 05:19:56 EST - 2010-11-07 03:16:55 EST = **P+0Y0M0DT2H3M1S** +test_time_fall_type2_st_type3_post: DIFF: 2010-11-08 19:59:59 EST - 2010-11-07 03:16:55 EST = **P+0Y0M1DT16H43M4S** +test_time_fall_type2_post_type3_prev: DIFF: 2010-11-06 18:38:28 EDT - 2010-11-08 19:59:59 EST = **P-0Y0M2DT1H21M31S** +test_time_fall_type2_post_type3_dt: DIFF: 2010-11-07 00:10:20 EDT - 2010-11-08 19:59:59 EST = **P-0Y0M1DT20H49M39S** +test_time_fall_type2_post_type3_redodt: DIFF: 2010-11-07 01:12:33 EDT - 2010-11-08 19:59:59 EST = **P-0Y0M1DT19H47M26S** +test_time_fall_type2_post_type3_redost: DIFF: 2010-11-07 01:14:44 EST - 2010-11-08 19:59:59 EST = **P-0Y0M1DT18H45M15S** +test_time_fall_type2_post_type3_st: DIFF: 2010-11-07 03:16:55 EST - 2010-11-08 19:59:59 EST = **P-0Y0M1DT16H43M4S** +test_time_fall_type2_post_type3_post: DIFF: 2010-11-08 19:59:59 EST - 2010-11-08 18:57:55 EST = **P+0Y0M0DT1H2M4S** +test_time_fall_type2_dtsec_type3_stsec: DIFF: 2010-11-07 01:00:00 EST - 2010-11-07 01:59:59 EDT = **P+0Y0M0DT0H0M1S** +test_time_fall_type2_stsec_type3_dtsec: DIFF: 2010-11-07 01:59:59 EDT - 2010-11-07 01:00:00 EST = **P-0Y0M0DT0H0M1S** diff --git a/ext/date/tests/DateTime_diff-fall-type3-type2.phpt b/ext/date/tests/DateTime_diff-fall-type3-type2.phpt new file mode 100644 index 000000000..c9f268ff9 --- /dev/null +++ b/ext/date/tests/DateTime_diff-fall-type3-type2.phpt @@ -0,0 +1,53 @@ +--TEST-- +DateTime::diff() -- fall type3 type2 +--CREDITS-- +Daniel Convissor <danielc@php.net> +--XFAIL-- +Various bugs exist +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_DIFF); +require 'DateTime_data-fall-type3-type2.inc'; + +?> +--EXPECT-- +test_time_fall_type3_prev_type2_prev: DIFF: 2010-11-06 18:38:28 EDT - 2010-10-04 02:18:48 EDT = **P+0Y1M2DT16H19M40S** +test_time_fall_type3_prev_type2_dt: DIFF: 2010-11-07 00:10:20 EDT - 2010-11-06 18:38:28 EDT = **P+0Y0M0DT5H31M52S** +test_time_fall_type3_prev_type2_redodt: DIFF: 2010-11-07 01:12:33 EDT - 2010-11-06 18:38:28 EDT = **P+0Y0M0DT6H34M5S** +test_time_fall_type3_prev_type2_redost: DIFF: 2010-11-07 01:14:44 EST - 2010-11-06 18:38:28 EDT = **P+0Y0M0DT7H36M16S** +test_time_fall_type3_prev_type2_st: DIFF: 2010-11-07 03:16:55 EST - 2010-11-06 18:38:28 EDT = **P+0Y0M0DT9H38M27S** +test_time_fall_type3_prev_type2_post: DIFF: 2010-11-08 19:59:59 EST - 2010-11-06 18:38:28 EDT = **P+0Y0M2DT1H21M31S** +test_time_fall_type3_dt_type2_prev: DIFF: 2010-11-06 18:38:28 EDT - 2010-11-07 00:10:20 EDT = **P-0Y0M0DT5H31M52S** +test_time_fall_type3_dt_type2_dt: DIFF: 2010-11-07 00:15:35 EDT - 2010-11-07 00:10:20 EDT = **P+0Y0M0DT0H5M15S** +test_time_fall_type3_dt_type2_redodt: DIFF: 2010-11-07 01:12:33 EDT - 2010-11-07 00:10:20 EDT = **P+0Y0M0DT1H2M13S** +test_time_fall_type3_dt_type2_redost: DIFF: 2010-11-07 01:14:44 EST - 2010-11-07 00:10:20 EDT = **P+0Y0M0DT2H4M24S** +test_time_fall_type3_dt_type2_st: DIFF: 2010-11-07 03:16:55 EST - 2010-11-07 00:10:20 EDT = **P+0Y0M0DT4H6M35S** +test_time_fall_type3_dt_type2_post: DIFF: 2010-11-08 19:59:59 EST - 2010-11-07 00:10:20 EDT = **P+0Y0M1DT20H49M39S** +test_time_fall_type3_redodt_type2_prev: DIFF: 2010-11-06 18:38:28 EDT - 2010-11-07 01:12:33 EDT = **P-0Y0M0DT6H34M5S** +test_time_fall_type3_redodt_type2_dt: DIFF: 2010-11-07 00:10:20 EDT - 2010-11-07 01:12:33 EDT = **P-0Y0M0DT1H2M13S** +test_time_fall_type3_redodt_type2_redodt: DIFF: 2010-11-07 01:15:35 EDT - 2010-11-07 01:12:33 EDT = **P+0Y0M0DT0H3M2S** +test_time_fall_type3_redodt_type2_redost: DIFF: 2010-11-07 01:14:44 EST - 2010-11-07 01:12:33 EDT = **P+0Y0M0DT1H2M11S** +test_time_fall_type3_redodt_type2_st: DIFF: 2010-11-07 03:16:55 EST - 2010-11-07 01:12:33 EDT = **P+0Y0M0DT3H4M22S** +test_time_fall_type3_redodt_type2_post: DIFF: 2010-11-08 19:59:59 EST - 2010-11-07 01:12:33 EDT = **P+0Y0M1DT19H47M26S** +test_time_fall_type3_redost_type2_prev: DIFF: 2010-11-06 18:38:28 EDT - 2010-11-07 01:14:44 EST = **P-0Y0M0DT7H36M16S** +test_time_fall_type3_redost_type2_dt: DIFF: 2010-11-07 00:10:20 EDT - 2010-11-07 01:14:44 EST = **P-0Y0M0DT2H4M24S** +test_time_fall_type3_redost_type2_redodt: DIFF: 2010-11-07 01:12:33 EDT - 2010-11-07 01:14:44 EST = **P-0Y0M0DT1H2M11S** +test_time_fall_type3_redost_type2_redost: DIFF: 2010-11-07 01:16:54 EST - 2010-11-07 01:14:44 EST = **P+0Y0M0DT0H2M10S** +test_time_fall_type3_redost_type2_st: DIFF: 2010-11-07 03:16:55 EST - 2010-11-07 01:14:44 EST = **P+0Y0M0DT2H2M11S** +test_time_fall_type3_redost_type2_post: DIFF: 2010-11-08 19:59:59 EST - 2010-11-07 01:14:44 EST = **P+0Y0M1DT18H45M15S** +test_time_fall_type3_st_type2_prev: DIFF: 2010-11-06 18:38:28 EDT - 2010-11-07 03:16:55 EST = **P-0Y0M0DT9H38M27S** +test_time_fall_type3_st_type2_dt: DIFF: 2010-11-07 00:10:20 EDT - 2010-11-07 03:16:55 EST = **P-0Y0M0DT4H6M35S** +test_time_fall_type3_st_type2_redodt: DIFF: 2010-11-07 01:12:33 EDT - 2010-11-07 03:16:55 EST = **P-0Y0M0DT3H4M22S** +test_time_fall_type3_st_type2_redost: DIFF: 2010-11-07 01:14:44 EST - 2010-11-07 03:16:55 EST = **P-0Y0M0DT2H2M11S** +test_time_fall_type3_st_type2_st: DIFF: 2010-11-07 05:19:56 EST - 2010-11-07 03:16:55 EST = **P+0Y0M0DT2H3M1S** +test_time_fall_type3_st_type2_post: DIFF: 2010-11-08 19:59:59 EST - 2010-11-07 03:16:55 EST = **P+0Y0M1DT16H43M4S** +test_time_fall_type3_post_type2_prev: DIFF: 2010-11-06 18:38:28 EDT - 2010-11-08 19:59:59 EST = **P-0Y0M2DT1H21M31S** +test_time_fall_type3_post_type2_dt: DIFF: 2010-11-07 00:10:20 EDT - 2010-11-08 19:59:59 EST = **P-0Y0M1DT20H49M39S** +test_time_fall_type3_post_type2_redodt: DIFF: 2010-11-07 01:12:33 EDT - 2010-11-08 19:59:59 EST = **P-0Y0M1DT19H47M26S** +test_time_fall_type3_post_type2_redost: DIFF: 2010-11-07 01:14:44 EST - 2010-11-08 19:59:59 EST = **P-0Y0M1DT18H45M15S** +test_time_fall_type3_post_type2_st: DIFF: 2010-11-07 03:16:55 EST - 2010-11-08 19:59:59 EST = **P-0Y0M1DT16H43M4S** +test_time_fall_type3_post_type2_post: DIFF: 2010-11-08 19:59:59 EST - 2010-11-08 18:57:55 EST = **P+0Y0M0DT1H2M4S** +test_time_fall_type3_dtsec_type2_stsec: DIFF: 2010-11-07 01:00:00 EST - 2010-11-07 01:59:59 EDT = **P+0Y0M0DT0H0M1S** +test_time_fall_type3_stsec_type2_dtsec: DIFF: 2010-11-07 01:59:59 EDT - 2010-11-07 01:00:00 EST = **P-0Y0M0DT0H0M1S** diff --git a/ext/date/tests/DateTime_diff-fall-type3-type3.phpt b/ext/date/tests/DateTime_diff-fall-type3-type3.phpt new file mode 100644 index 000000000..bf25fef62 --- /dev/null +++ b/ext/date/tests/DateTime_diff-fall-type3-type3.phpt @@ -0,0 +1,53 @@ +--TEST-- +DateTime::diff() -- fall type3 type3 +--CREDITS-- +Daniel Convissor <danielc@php.net> +--XFAIL-- +Various bugs exist +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_DIFF); +require 'DateTime_data-fall-type3-type3.inc'; + +?> +--EXPECT-- +test_time_fall_type3_prev_type3_prev: DIFF: 2010-11-06 18:38:28 EDT - 2010-10-04 02:18:48 EDT = **P+0Y1M2DT16H19M40S** +test_time_fall_type3_prev_type3_dt: DIFF: 2010-11-07 00:10:20 EDT - 2010-11-06 18:38:28 EDT = **P+0Y0M0DT5H31M52S** +test_time_fall_type3_prev_type3_redodt: DIFF: 2010-11-07 01:12:33 EDT - 2010-11-06 18:38:28 EDT = **P+0Y0M0DT6H34M5S** +test_time_fall_type3_prev_type3_redost: DIFF: 2010-11-07 01:14:44 EST - 2010-11-06 18:38:28 EDT = **P+0Y0M0DT7H36M16S** +test_time_fall_type3_prev_type3_st: DIFF: 2010-11-07 03:16:55 EST - 2010-11-06 18:38:28 EDT = **P+0Y0M0DT9H38M27S** +test_time_fall_type3_prev_type3_post: DIFF: 2010-11-08 19:59:59 EST - 2010-11-06 18:38:28 EDT = **P+0Y0M2DT1H21M31S** +test_time_fall_type3_dt_type3_prev: DIFF: 2010-11-06 18:38:28 EDT - 2010-11-07 00:10:20 EDT = **P-0Y0M0DT5H31M52S** +test_time_fall_type3_dt_type3_dt: DIFF: 2010-11-07 00:15:35 EDT - 2010-11-07 00:10:20 EDT = **P+0Y0M0DT0H5M15S** +test_time_fall_type3_dt_type3_redodt: DIFF: 2010-11-07 01:12:33 EDT - 2010-11-07 00:10:20 EDT = **P+0Y0M0DT1H2M13S** +test_time_fall_type3_dt_type3_redost: DIFF: 2010-11-07 01:14:44 EST - 2010-11-07 00:10:20 EDT = **P+0Y0M0DT2H4M24S** +test_time_fall_type3_dt_type3_st: DIFF: 2010-11-07 03:16:55 EST - 2010-11-07 00:10:20 EDT = **P+0Y0M0DT4H6M35S** +test_time_fall_type3_dt_type3_post: DIFF: 2010-11-08 19:59:59 EST - 2010-11-07 00:10:20 EDT = **P+0Y0M1DT20H49M39S** +test_time_fall_type3_redodt_type3_prev: DIFF: 2010-11-06 18:38:28 EDT - 2010-11-07 01:12:33 EDT = **P-0Y0M0DT6H34M5S** +test_time_fall_type3_redodt_type3_dt: DIFF: 2010-11-07 00:10:20 EDT - 2010-11-07 01:12:33 EDT = **P-0Y0M0DT1H2M13S** +test_time_fall_type3_redodt_type3_redodt: DIFF: 2010-11-07 01:15:35 EDT - 2010-11-07 01:12:33 EDT = **P+0Y0M0DT0H3M2S** +test_time_fall_type3_redodt_type3_redost: DIFF: 2010-11-07 01:14:44 EST - 2010-11-07 01:12:33 EDT = **P+0Y0M0DT1H2M11S** +test_time_fall_type3_redodt_type3_st: DIFF: 2010-11-07 03:16:55 EST - 2010-11-07 01:12:33 EDT = **P+0Y0M0DT3H4M22S** +test_time_fall_type3_redodt_type3_post: DIFF: 2010-11-08 19:59:59 EST - 2010-11-07 01:12:33 EDT = **P+0Y0M1DT19H47M26S** +test_time_fall_type3_redost_type3_prev: DIFF: 2010-11-06 18:38:28 EDT - 2010-11-07 01:14:44 EST = **P-0Y0M0DT7H36M16S** +test_time_fall_type3_redost_type3_dt: DIFF: 2010-11-07 00:10:20 EDT - 2010-11-07 01:14:44 EST = **P-0Y0M0DT2H4M24S** +test_time_fall_type3_redost_type3_redodt: DIFF: 2010-11-07 01:12:33 EDT - 2010-11-07 01:14:44 EST = **P-0Y0M0DT1H2M11S** +test_time_fall_type3_redost_type3_redost: DIFF: 2010-11-07 01:16:54 EST - 2010-11-07 01:14:44 EST = **P+0Y0M0DT0H2M10S** +test_time_fall_type3_redost_type3_st: DIFF: 2010-11-07 03:16:55 EST - 2010-11-07 01:14:44 EST = **P+0Y0M0DT2H2M11S** +test_time_fall_type3_redost_type3_post: DIFF: 2010-11-08 19:59:59 EST - 2010-11-07 01:14:44 EST = **P+0Y0M1DT18H45M15S** +test_time_fall_type3_st_type3_prev: DIFF: 2010-11-06 18:38:28 EDT - 2010-11-07 03:16:55 EST = **P-0Y0M0DT9H38M27S** +test_time_fall_type3_st_type3_dt: DIFF: 2010-11-07 00:10:20 EDT - 2010-11-07 03:16:55 EST = **P-0Y0M0DT4H6M35S** +test_time_fall_type3_st_type3_redodt: DIFF: 2010-11-07 01:12:33 EDT - 2010-11-07 03:16:55 EST = **P-0Y0M0DT3H4M22S** +test_time_fall_type3_st_type3_redost: DIFF: 2010-11-07 01:14:44 EST - 2010-11-07 03:16:55 EST = **P-0Y0M0DT2H2M11S** +test_time_fall_type3_st_type3_st: DIFF: 2010-11-07 05:19:56 EST - 2010-11-07 03:16:55 EST = **P+0Y0M0DT2H3M1S** +test_time_fall_type3_st_type3_post: DIFF: 2010-11-08 19:59:59 EST - 2010-11-07 03:16:55 EST = **P+0Y0M1DT16H43M4S** +test_time_fall_type3_post_type3_prev: DIFF: 2010-11-06 18:38:28 EDT - 2010-11-08 19:59:59 EST = **P-0Y0M2DT1H21M31S** +test_time_fall_type3_post_type3_dt: DIFF: 2010-11-07 00:10:20 EDT - 2010-11-08 19:59:59 EST = **P-0Y0M1DT20H49M39S** +test_time_fall_type3_post_type3_redodt: DIFF: 2010-11-07 01:12:33 EDT - 2010-11-08 19:59:59 EST = **P-0Y0M1DT19H47M26S** +test_time_fall_type3_post_type3_redost: DIFF: 2010-11-07 01:14:44 EST - 2010-11-08 19:59:59 EST = **P-0Y0M1DT18H45M15S** +test_time_fall_type3_post_type3_st: DIFF: 2010-11-07 03:16:55 EST - 2010-11-08 19:59:59 EST = **P-0Y0M1DT16H43M4S** +test_time_fall_type3_post_type3_post: DIFF: 2010-11-08 19:59:59 EST - 2010-11-08 18:57:55 EST = **P+0Y0M0DT1H2M4S** +test_time_fall_type3_dtsec_type3_stsec: DIFF: 2010-11-07 01:00:00 EST - 2010-11-07 01:59:59 EDT = **P+0Y0M0DT0H0M1S** +test_time_fall_type3_stsec_type3_dtsec: DIFF: 2010-11-07 01:59:59 EDT - 2010-11-07 01:00:00 EST = **P-0Y0M0DT0H0M1S** diff --git a/ext/date/tests/DateTime_diff-february.phpt b/ext/date/tests/DateTime_diff-february.phpt new file mode 100644 index 000000000..7705b12e3 --- /dev/null +++ b/ext/date/tests/DateTime_diff-february.phpt @@ -0,0 +1,77 @@ +--TEST-- +DateTime::diff() -- february +--CREDITS-- +Daniel Convissor <danielc@php.net> +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_DIFF); +require 'DateTime_data-february.inc'; + +?> +--EXPECT-- +test_bug_49081__1: DIFF: 2010-03-31 00:00:00 EDT - 2010-03-01 00:00:00 EST = **P+0Y0M30DT0H0M0S** +test_bug_49081__2: DIFF: 2010-04-01 00:00:00 EDT - 2010-03-01 00:00:00 EST = **P+0Y1M0DT0H0M0S** +test_bug_49081__3: DIFF: 2010-04-01 00:00:00 EDT - 2010-03-31 00:00:00 EDT = **P+0Y0M1DT0H0M0S** +test_bug_49081__4: DIFF: 2010-04-29 00:00:00 EDT - 2010-03-31 00:00:00 EDT = **P+0Y0M29DT0H0M0S** +test_bug_49081__5: DIFF: 2010-04-30 00:00:00 EDT - 2010-03-31 00:00:00 EDT = **P+0Y0M30DT0H0M0S** +test_bug_49081__6: DIFF: 2010-04-30 00:00:00 EDT - 2010-03-30 00:00:00 EDT = **P+0Y1M0DT0H0M0S** +test_bug_49081__7: DIFF: 2010-04-30 00:00:00 EDT - 2010-03-29 00:00:00 EDT = **P+0Y1M1DT0H0M0S** +test_bug_49081__8: DIFF: 2010-01-29 00:00:00 EST - 2010-01-01 00:00:00 EST = **P+0Y0M28DT0H0M0S** +test_bug_49081__9: DIFF: 2010-01-30 00:00:00 EST - 2010-01-01 00:00:00 EST = **P+0Y0M29DT0H0M0S** +test_bug_49081__10: DIFF: 2010-01-31 00:00:00 EST - 2010-01-01 00:00:00 EST = **P+0Y0M30DT0H0M0S** +test_bug_49081__11: DIFF: 2010-02-01 00:00:00 EST - 2010-01-01 00:00:00 EST = **P+0Y1M0DT0H0M0S** +test_bug_49081__12: DIFF: 2010-02-01 00:00:00 EST - 2010-01-31 00:00:00 EST = **P+0Y0M1DT0H0M0S** +test_bug_49081__13: DIFF: 2010-02-27 00:00:00 EST - 2010-01-31 00:00:00 EST = **P+0Y0M27DT0H0M0S** +test_bug_49081__14: DIFF: 2010-02-28 00:00:00 EST - 2010-01-31 00:00:00 EST = **P+0Y0M28DT0H0M0S** +test_bug_49081__15: DIFF: 2010-02-28 00:00:00 EST - 2010-01-30 00:00:00 EST = **P+0Y0M29DT0H0M0S** +test_bug_49081__16: DIFF: 2010-02-28 00:00:00 EST - 2010-01-29 00:00:00 EST = **P+0Y0M30DT0H0M0S** +test_bug_49081__17: DIFF: 2010-02-28 00:00:00 EST - 2010-01-28 00:00:00 EST = **P+0Y1M0DT0H0M0S** +test_bug_49081__18: DIFF: 2010-02-28 00:00:00 EST - 2010-01-27 00:00:00 EST = **P+0Y1M1DT0H0M0S** +test_bug_49081__19: DIFF: 2010-03-01 00:00:00 EST - 2010-01-01 00:00:00 EST = **P+0Y2M0DT0H0M0S** +test_bug_49081__20: DIFF: 2010-03-01 00:00:00 EST - 2010-01-31 00:00:00 EST = **P+0Y0M29DT0H0M0S** +test_bug_49081__21: DIFF: 2010-03-27 00:00:00 EDT - 2010-01-31 00:00:00 EST = **P+0Y1M24DT0H0M0S** +test_bug_49081__22: DIFF: 2010-03-28 00:00:00 EDT - 2010-01-31 00:00:00 EST = **P+0Y1M25DT0H0M0S** +test_bug_49081__23: DIFF: 2010-03-29 00:00:00 EDT - 2010-01-31 00:00:00 EST = **P+0Y1M26DT0H0M0S** +test_bug_49081__24: DIFF: 2010-03-30 00:00:00 EDT - 2010-01-31 00:00:00 EST = **P+0Y1M27DT0H0M0S** +test_bug_49081__25: DIFF: 2010-03-31 00:00:00 EDT - 2010-01-31 00:00:00 EST = **P+0Y2M0DT0H0M0S** +test_bug_49081__26: DIFF: 2010-03-31 00:00:00 EDT - 2010-01-30 00:00:00 EST = **P+0Y2M1DT0H0M0S** +test_bug_49081__27: DIFF: 2009-01-31 00:00:00 EST - 2009-01-01 00:00:00 EST = **P+0Y0M30DT0H0M0S** +test_bug_49081__28: DIFF: 2010-03-27 00:00:00 EDT - 2010-02-28 00:00:00 EST = **P+0Y0M27DT0H0M0S** +test_bug_49081__29: DIFF: 2010-03-28 00:00:00 EDT - 2010-02-28 00:00:00 EST = **P+0Y1M0DT0H0M0S** +test_bug_49081__30: DIFF: 2010-03-29 00:00:00 EDT - 2010-02-28 00:00:00 EST = **P+0Y1M1DT0H0M0S** +test_bug_49081__31: DIFF: 2010-03-27 00:00:00 EDT - 2010-02-27 00:00:00 EST = **P+0Y1M0DT0H0M0S** +test_bug_49081__32: DIFF: 2010-03-27 00:00:00 EDT - 2010-02-26 00:00:00 EST = **P+0Y1M1DT0H0M0S** +test_bug_49081_negative__1: DIFF: 2010-03-01 00:00:00 EST - 2010-03-31 00:00:00 EDT = **P-0Y0M30DT0H0M0S** +test_bug_49081_negative__2: DIFF: 2010-03-01 00:00:00 EST - 2010-04-01 00:00:00 EDT = **P-0Y1M0DT0H0M0S** +test_bug_49081_negative__3: DIFF: 2010-03-31 00:00:00 EDT - 2010-04-01 00:00:00 EDT = **P-0Y0M1DT0H0M0S** +test_bug_49081_negative__4: DIFF: 2010-03-31 00:00:00 EDT - 2010-04-29 00:00:00 EDT = **P-0Y0M29DT0H0M0S** +test_bug_49081_negative__5: DIFF: 2010-03-31 00:00:00 EDT - 2010-04-30 00:00:00 EDT = **P-0Y0M30DT0H0M0S** +test_bug_49081_negative__6: DIFF: 2010-03-30 00:00:00 EDT - 2010-04-30 00:00:00 EDT = **P-0Y1M0DT0H0M0S** +test_bug_49081_negative__7: DIFF: 2010-03-29 00:00:00 EDT - 2010-04-30 00:00:00 EDT = **P-0Y1M1DT0H0M0S** +test_bug_49081_negative__8: DIFF: 2010-01-01 00:00:00 EST - 2010-01-29 00:00:00 EST = **P-0Y0M28DT0H0M0S** +test_bug_49081_negative__9: DIFF: 2010-01-01 00:00:00 EST - 2010-01-30 00:00:00 EST = **P-0Y0M29DT0H0M0S** +test_bug_49081_negative__10: DIFF: 2010-01-01 00:00:00 EST - 2010-01-31 00:00:00 EST = **P-0Y0M30DT0H0M0S** +test_bug_49081_negative__11: DIFF: 2010-01-01 00:00:00 EST - 2010-02-01 00:00:00 EST = **P-0Y1M0DT0H0M0S** +test_bug_49081_negative__12: DIFF: 2010-01-31 00:00:00 EST - 2010-02-01 00:00:00 EST = **P-0Y0M1DT0H0M0S** +test_bug_49081_negative__13: DIFF: 2010-01-31 00:00:00 EST - 2010-02-27 00:00:00 EST = **P-0Y0M27DT0H0M0S** +test_bug_49081_negative__14: DIFF: 2010-01-31 00:00:00 EST - 2010-02-28 00:00:00 EST = **P-0Y0M28DT0H0M0S** +test_bug_49081_negative__15: DIFF: 2010-01-30 00:00:00 EST - 2010-02-28 00:00:00 EST = **P-0Y0M29DT0H0M0S** +test_bug_49081_negative__16: DIFF: 2010-01-29 00:00:00 EST - 2010-02-28 00:00:00 EST = **P-0Y0M30DT0H0M0S** +test_bug_49081_negative__17: DIFF: 2010-01-28 00:00:00 EST - 2010-02-28 00:00:00 EST = **P-0Y1M0DT0H0M0S** +test_bug_49081_negative__18: DIFF: 2010-01-27 00:00:00 EST - 2010-02-28 00:00:00 EST = **P-0Y1M1DT0H0M0S** +test_bug_49081_negative__19: DIFF: 2010-01-01 00:00:00 EST - 2010-03-01 00:00:00 EST = **P-0Y2M0DT0H0M0S** +test_bug_49081_negative__20: DIFF: 2010-01-31 00:00:00 EST - 2010-03-01 00:00:00 EST = **P-0Y1M1DT0H0M0S** +test_bug_49081_negative__21: DIFF: 2010-01-31 00:00:00 EST - 2010-03-27 00:00:00 EDT = **P-0Y1M27DT0H0M0S** +test_bug_49081_negative__22: DIFF: 2010-01-31 00:00:00 EST - 2010-03-28 00:00:00 EDT = **P-0Y1M28DT0H0M0S** +test_bug_49081_negative__23: DIFF: 2010-01-31 00:00:00 EST - 2010-03-29 00:00:00 EDT = **P-0Y1M29DT0H0M0S** +test_bug_49081_negative__24: DIFF: 2010-01-31 00:00:00 EST - 2010-03-30 00:00:00 EDT = **P-0Y1M30DT0H0M0S** +test_bug_49081_negative__25: DIFF: 2010-01-31 00:00:00 EST - 2010-03-31 00:00:00 EDT = **P-0Y2M0DT0H0M0S** +test_bug_49081_negative__26: DIFF: 2010-01-30 00:00:00 EST - 2010-03-31 00:00:00 EDT = **P-0Y2M1DT0H0M0S** +test_bug_49081_negative__27: DIFF: 2009-01-01 00:00:00 EST - 2009-01-31 00:00:00 EST = **P-0Y0M30DT0H0M0S** +test_bug_49081_negative__28: DIFF: 2010-02-28 00:00:00 EST - 2010-03-27 00:00:00 EDT = **P-0Y0M27DT0H0M0S** +test_bug_49081_negative__29: DIFF: 2010-02-28 00:00:00 EST - 2010-03-28 00:00:00 EDT = **P-0Y1M0DT0H0M0S** +test_bug_49081_negative__30: DIFF: 2010-02-28 00:00:00 EST - 2010-03-29 00:00:00 EDT = **P-0Y1M1DT0H0M0S** +test_bug_49081_negative__31: DIFF: 2010-02-27 00:00:00 EST - 2010-03-27 00:00:00 EDT = **P-0Y1M0DT0H0M0S** +test_bug_49081_negative__32: DIFF: 2010-02-26 00:00:00 EST - 2010-03-27 00:00:00 EDT = **P-0Y1M1DT0H0M0S** diff --git a/ext/date/tests/DateTime_diff-massive.phpt b/ext/date/tests/DateTime_diff-massive.phpt new file mode 100644 index 000000000..2199f8486 --- /dev/null +++ b/ext/date/tests/DateTime_diff-massive.phpt @@ -0,0 +1,15 @@ +--TEST-- +DateTime::diff() -- massive +--CREDITS-- +Daniel Convissor <danielc@php.net> +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_DIFF); +require 'DateTime_data-massive.inc'; + +?> +--EXPECT-- +test_massive_positive: DIFF: 333333-01-01 16:18:02 EST - -333333-01-01 16:18:02 EST = **P+666666Y0M0DT0H0M0S** +test_massive_negative: DIFF: -333333-01-01 16:18:02 EST - 333333-01-01 16:18:02 EST = **P-666666Y0M0DT0H0M0S** diff --git a/ext/date/tests/DateTime_diff-spring-type2-type2.phpt b/ext/date/tests/DateTime_diff-spring-type2-type2.phpt new file mode 100644 index 000000000..4c590cd99 --- /dev/null +++ b/ext/date/tests/DateTime_diff-spring-type2-type2.phpt @@ -0,0 +1,33 @@ +--TEST-- +DateTime::diff() -- spring type2 type2 +--CREDITS-- +Daniel Convissor <danielc@php.net> +--XFAIL-- +Various bugs exist +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_DIFF); +require 'DateTime_data-spring-type2-type2.inc'; + +?> +--EXPECT-- +test_time_spring_type2_prev_type2_prev: DIFF: 2010-03-13 18:38:28 EST - 2010-02-11 02:18:48 EST = **P+0Y1M2DT16H19M40S** +test_time_spring_type2_prev_type2_st: DIFF: 2010-03-14 00:10:20 EST - 2010-03-13 18:38:28 EST = **P+0Y0M0DT5H31M52S** +test_time_spring_type2_prev_type2_dt: DIFF: 2010-03-14 03:16:55 EDT - 2010-03-13 18:38:28 EST = **P+0Y0M0DT7H38M27S** +test_time_spring_type2_prev_type2_post: DIFF: 2010-03-15 19:59:59 EDT - 2010-03-13 18:38:28 EST = **P+0Y0M2DT1H21M31S** +test_time_spring_type2_st_type2_prev: DIFF: 2010-03-13 18:38:28 EST - 2010-03-14 00:10:20 EST = **P-0Y0M0DT5H31M52S** +test_time_spring_type2_st_type2_st: DIFF: 2010-03-14 00:15:35 EST - 2010-03-14 00:10:20 EST = **P+0Y0M0DT0H5M15S** +test_time_spring_type2_st_type2_dt: DIFF: 2010-03-14 03:16:55 EDT - 2010-03-14 00:10:20 EST = **P+0Y0M0DT2H6M35S** +test_time_spring_type2_st_type2_post: DIFF: 2010-03-15 19:59:59 EDT - 2010-03-14 00:10:20 EST = **P+0Y0M1DT18H49M39S** +test_time_spring_type2_dt_type2_prev: DIFF: 2010-03-13 18:38:28 EST - 2010-03-14 03:16:55 EDT = **P-0Y0M0DT7H38M27S** +test_time_spring_type2_dt_type2_st: DIFF: 2010-03-14 00:10:20 EST - 2010-03-14 03:16:55 EDT = **P-0Y0M0DT2H6M35S** +test_time_spring_type2_dt_type2_dt: DIFF: 2010-03-14 05:19:56 EDT - 2010-03-14 03:16:55 EDT = **P+0Y0M0DT2H3M1S** +test_time_spring_type2_dt_type2_post: DIFF: 2010-03-15 19:59:59 EDT - 2010-03-14 03:16:55 EDT = **P+0Y0M1DT16H43M4S** +test_time_spring_type2_post_type2_prev: DIFF: 2010-03-13 18:38:28 EST - 2010-03-15 19:59:59 EDT = **P-0Y0M2DT1H21M31S** +test_time_spring_type2_post_type2_st: DIFF: 2010-03-14 00:10:20 EST - 2010-03-15 19:59:59 EDT = **P-0Y0M1DT18H49M39S** +test_time_spring_type2_post_type2_dt: DIFF: 2010-03-14 03:16:55 EDT - 2010-03-15 19:59:59 EDT = **P-0Y0M1DT16H43M4S** +test_time_spring_type2_post_type2_post: DIFF: 2010-03-15 19:59:59 EDT - 2010-03-15 18:57:55 EDT = **P+0Y0M0DT1H2M4S** +test_time_spring_type2_stsec_type2_dtsec: DIFF: 2010-03-15 03:00:00 EDT - 2010-03-13 01:59:59 EST = **P+0Y0M0DT0H0M1S** +test_time_spring_type2_dtsec_type2_stsec: DIFF: 2010-03-15 01:59:59 EST - 2010-03-15 03:00:00 EDT = **P-0Y0M0DT0H0M1S** diff --git a/ext/date/tests/DateTime_diff-spring-type2-type3.phpt b/ext/date/tests/DateTime_diff-spring-type2-type3.phpt new file mode 100644 index 000000000..98dcf7968 --- /dev/null +++ b/ext/date/tests/DateTime_diff-spring-type2-type3.phpt @@ -0,0 +1,33 @@ +--TEST-- +DateTime::diff() -- spring type2 type3 +--CREDITS-- +Daniel Convissor <danielc@php.net> +--XFAIL-- +Various bugs exist +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_DIFF); +require 'DateTime_data-spring-type2-type3.inc'; + +?> +--EXPECT-- +test_time_spring_type2_prev_type3_prev: DIFF: 2010-03-13 18:38:28 EST - 2010-02-11 02:18:48 EST = **P+0Y1M2DT16H19M40S** +test_time_spring_type2_prev_type3_st: DIFF: 2010-03-14 00:10:20 EST - 2010-03-13 18:38:28 EST = **P+0Y0M0DT5H31M52S** +test_time_spring_type2_prev_type3_dt: DIFF: 2010-03-14 03:16:55 EDT - 2010-03-13 18:38:28 EST = **P+0Y0M0DT7H38M27S** +test_time_spring_type2_prev_type3_post: DIFF: 2010-03-15 19:59:59 EDT - 2010-03-13 18:38:28 EST = **P+0Y0M2DT1H21M31S** +test_time_spring_type2_st_type3_prev: DIFF: 2010-03-13 18:38:28 EST - 2010-03-14 00:10:20 EST = **P-0Y0M0DT5H31M52S** +test_time_spring_type2_st_type3_st: DIFF: 2010-03-14 00:15:35 EST - 2010-03-14 00:10:20 EST = **P+0Y0M0DT0H5M15S** +test_time_spring_type2_st_type3_dt: DIFF: 2010-03-14 03:16:55 EDT - 2010-03-14 00:10:20 EST = **P+0Y0M0DT2H6M35S** +test_time_spring_type2_st_type3_post: DIFF: 2010-03-15 19:59:59 EDT - 2010-03-14 00:10:20 EST = **P+0Y0M1DT18H49M39S** +test_time_spring_type2_dt_type3_prev: DIFF: 2010-03-13 18:38:28 EST - 2010-03-14 03:16:55 EDT = **P-0Y0M0DT7H38M27S** +test_time_spring_type2_dt_type3_st: DIFF: 2010-03-14 00:10:20 EST - 2010-03-14 03:16:55 EDT = **P-0Y0M0DT2H6M35S** +test_time_spring_type2_dt_type3_dt: DIFF: 2010-03-14 05:19:56 EDT - 2010-03-14 03:16:55 EDT = **P+0Y0M0DT2H3M1S** +test_time_spring_type2_dt_type3_post: DIFF: 2010-03-15 19:59:59 EDT - 2010-03-14 03:16:55 EDT = **P+0Y0M1DT16H43M4S** +test_time_spring_type2_post_type3_prev: DIFF: 2010-03-13 18:38:28 EST - 2010-03-15 19:59:59 EDT = **P-0Y0M2DT1H21M31S** +test_time_spring_type2_post_type3_st: DIFF: 2010-03-14 00:10:20 EST - 2010-03-15 19:59:59 EDT = **P-0Y0M1DT18H49M39S** +test_time_spring_type2_post_type3_dt: DIFF: 2010-03-14 03:16:55 EDT - 2010-03-15 19:59:59 EDT = **P-0Y0M1DT16H43M4S** +test_time_spring_type2_post_type3_post: DIFF: 2010-03-15 19:59:59 EDT - 2010-03-15 18:57:55 EDT = **P+0Y0M0DT1H2M4S** +test_time_spring_type2_stsec_type3_dtsec: DIFF: 2010-03-15 03:00:00 EDT - 2010-03-13 01:59:59 EST = **P+0Y0M0DT0H0M1S** +test_time_spring_type2_dtsec_type3_stsec: DIFF: 2010-03-15 01:59:59 EST - 2010-03-15 03:00:00 EDT = **P-0Y0M0DT0H0M1S** diff --git a/ext/date/tests/DateTime_diff-spring-type3-type2.phpt b/ext/date/tests/DateTime_diff-spring-type3-type2.phpt new file mode 100644 index 000000000..5a59f78df --- /dev/null +++ b/ext/date/tests/DateTime_diff-spring-type3-type2.phpt @@ -0,0 +1,33 @@ +--TEST-- +DateTime::diff() -- spring type3 type2 +--CREDITS-- +Daniel Convissor <danielc@php.net> +--XFAIL-- +Various bugs exist +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_DIFF); +require 'DateTime_data-spring-type3-type2.inc'; + +?> +--EXPECT-- +test_time_spring_type3_prev_type2_prev: DIFF: 2010-03-13 18:38:28 EST - 2010-02-11 02:18:48 EST = **P+0Y1M2DT16H19M40S** +test_time_spring_type3_prev_type2_st: DIFF: 2010-03-14 00:10:20 EST - 2010-03-13 18:38:28 EST = **P+0Y0M0DT5H31M52S** +test_time_spring_type3_prev_type2_dt: DIFF: 2010-03-14 03:16:55 EDT - 2010-03-13 18:38:28 EST = **P+0Y0M0DT7H38M27S** +test_time_spring_type3_prev_type2_post: DIFF: 2010-03-15 19:59:59 EDT - 2010-03-13 18:38:28 EST = **P+0Y0M2DT1H21M31S** +test_time_spring_type3_st_type2_prev: DIFF: 2010-03-13 18:38:28 EST - 2010-03-14 00:10:20 EST = **P-0Y0M0DT5H31M52S** +test_time_spring_type3_st_type2_st: DIFF: 2010-03-14 00:15:35 EST - 2010-03-14 00:10:20 EST = **P+0Y0M0DT0H5M15S** +test_time_spring_type3_st_type2_dt: DIFF: 2010-03-14 03:16:55 EDT - 2010-03-14 00:10:20 EST = **P+0Y0M0DT2H6M35S** +test_time_spring_type3_st_type2_post: DIFF: 2010-03-15 19:59:59 EDT - 2010-03-14 00:10:20 EST = **P+0Y0M1DT18H49M39S** +test_time_spring_type3_dt_type2_prev: DIFF: 2010-03-13 18:38:28 EST - 2010-03-14 03:16:55 EDT = **P-0Y0M0DT7H38M27S** +test_time_spring_type3_dt_type2_st: DIFF: 2010-03-14 00:10:20 EST - 2010-03-14 03:16:55 EDT = **P-0Y0M0DT2H6M35S** +test_time_spring_type3_dt_type2_dt: DIFF: 2010-03-14 05:19:56 EDT - 2010-03-14 03:16:55 EDT = **P+0Y0M0DT2H3M1S** +test_time_spring_type3_dt_type2_post: DIFF: 2010-03-15 19:59:59 EDT - 2010-03-14 03:16:55 EDT = **P+0Y0M1DT16H43M4S** +test_time_spring_type3_post_type2_prev: DIFF: 2010-03-13 18:38:28 EST - 2010-03-15 19:59:59 EDT = **P-0Y0M2DT1H21M31S** +test_time_spring_type3_post_type2_st: DIFF: 2010-03-14 00:10:20 EST - 2010-03-15 19:59:59 EDT = **P-0Y0M1DT18H49M39S** +test_time_spring_type3_post_type2_dt: DIFF: 2010-03-14 03:16:55 EDT - 2010-03-15 19:59:59 EDT = **P-0Y0M1DT16H43M4S** +test_time_spring_type3_post_type2_post: DIFF: 2010-03-15 19:59:59 EDT - 2010-03-15 18:57:55 EDT = **P+0Y0M0DT1H2M4S** +test_time_spring_type3_stsec_type2_dtsec: DIFF: 2010-03-15 03:00:00 EDT - 2010-03-13 01:59:59 EST = **P+0Y0M0DT0H0M1S** +test_time_spring_type3_dtsec_type2_stsec: DIFF: 2010-03-15 01:59:59 EST - 2010-03-15 03:00:00 EDT = **P-0Y0M0DT0H0M1S** diff --git a/ext/date/tests/DateTime_diff-spring-type3-type3.phpt b/ext/date/tests/DateTime_diff-spring-type3-type3.phpt new file mode 100644 index 000000000..926f299e0 --- /dev/null +++ b/ext/date/tests/DateTime_diff-spring-type3-type3.phpt @@ -0,0 +1,33 @@ +--TEST-- +DateTime::diff() -- spring type3 type3 +--CREDITS-- +Daniel Convissor <danielc@php.net> +--XFAIL-- +Various bugs exist +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_DIFF); +require 'DateTime_data-spring-type3-type3.inc' + +?> +--EXPECT-- +test_time_spring_type3_prev_type3_prev: DIFF: 2010-03-13 18:38:28 EST - 2010-02-11 02:18:48 EST = **P+0Y1M2DT16H19M40S** +test_time_spring_type3_prev_type3_st: DIFF: 2010-03-14 00:10:20 EST - 2010-03-13 18:38:28 EST = **P+0Y0M0DT5H31M52S** +test_time_spring_type3_prev_type3_dt: DIFF: 2010-03-14 03:16:55 EDT - 2010-03-13 18:38:28 EST = **P+0Y0M0DT7H38M27S** +test_time_spring_type3_prev_type3_post: DIFF: 2010-03-15 19:59:59 EDT - 2010-03-13 18:38:28 EST = **P+0Y0M2DT1H21M31S** +test_time_spring_type3_st_type3_prev: DIFF: 2010-03-13 18:38:28 EST - 2010-03-14 00:10:20 EST = **P-0Y0M0DT5H31M52S** +test_time_spring_type3_st_type3_st: DIFF: 2010-03-14 00:15:35 EST - 2010-03-14 00:10:20 EST = **P+0Y0M0DT0H5M15S** +test_time_spring_type3_st_type3_dt: DIFF: 2010-03-14 03:16:55 EDT - 2010-03-14 00:10:20 EST = **P+0Y0M0DT2H6M35S** +test_time_spring_type3_st_type3_post: DIFF: 2010-03-15 19:59:59 EDT - 2010-03-14 00:10:20 EST = **P+0Y0M1DT18H49M39S** +test_time_spring_type3_dt_type3_prev: DIFF: 2010-03-13 18:38:28 EST - 2010-03-14 03:16:55 EDT = **P-0Y0M0DT7H38M27S** +test_time_spring_type3_dt_type3_st: DIFF: 2010-03-14 00:10:20 EST - 2010-03-14 03:16:55 EDT = **P-0Y0M0DT2H6M35S** +test_time_spring_type3_dt_type3_dt: DIFF: 2010-03-14 05:19:56 EDT - 2010-03-14 03:16:55 EDT = **P+0Y0M0DT2H3M1S** +test_time_spring_type3_dt_type3_post: DIFF: 2010-03-15 19:59:59 EDT - 2010-03-14 03:16:55 EDT = **P+0Y0M1DT16H43M4S** +test_time_spring_type3_post_type3_prev: DIFF: 2010-03-13 18:38:28 EST - 2010-03-15 19:59:59 EDT = **P-0Y0M2DT1H21M31S** +test_time_spring_type3_post_type3_st: DIFF: 2010-03-14 00:10:20 EST - 2010-03-15 19:59:59 EDT = **P-0Y0M1DT18H49M39S** +test_time_spring_type3_post_type3_dt: DIFF: 2010-03-14 03:16:55 EDT - 2010-03-15 19:59:59 EDT = **P-0Y0M1DT16H43M4S** +test_time_spring_type3_post_type3_post: DIFF: 2010-03-15 19:59:59 EDT - 2010-03-15 18:57:55 EDT = **P+0Y0M0DT1H2M4S** +test_time_spring_type3_stsec_type2_dtsec: DIFF: 2010-03-15 03:00:00 EDT - 2010-03-13 01:59:59 EST = **P+0Y0M0DT0H0M1S** +test_time_spring_type3_dtsec_type2_stsec: DIFF: 2010-03-15 01:59:59 EST - 2010-03-15 03:00:00 EDT = **P-0Y0M0DT0H0M1S** diff --git a/ext/date/tests/DateTime_diff_add_sub-absolute.phpt b/ext/date/tests/DateTime_diff_add_sub-absolute.phpt deleted file mode 100644 index 25e5a7db8..000000000 --- a/ext/date/tests/DateTime_diff_add_sub-absolute.phpt +++ /dev/null @@ -1,29 +0,0 @@ ---TEST-- -DateTime::diff() add() sub() -- absolute ---CREDITS-- -Daniel Convissor <danielc@php.net> ---FILE-- -<?php - -/* - * Note: test names match method names in a set of PHPUnit tests - * in a userland package. Please be so kind as to leave them. - */ - -require './examine_diff.inc'; -date_default_timezone_set('America/New_York'); - - -/* - * Absolute - */ -echo "test_absolute_7: "; -examine_diff('2009-01-14', '2009-01-07', 'P+0Y0M7DT0H0M0S', 7, true); - -echo "test_absolute_negative_7: "; -examine_diff('2009-01-07', '2009-01-14', 'P+0Y0M7DT0H0M0S', 7, true); - -?> ---EXPECT-- -test_absolute_7: FWD: 2009-01-14 00:00:00 EST - 2009-01-07 00:00:00 EST = **P+0Y0M7DT0H0M0S** | BACK: 2009-01-07 00:00:00 EST + P+0Y0M7DT0H0M0S = **2009-01-14 00:00:00 EST** | DAYS: **7** -test_absolute_negative_7: FWD: 2009-01-07 00:00:00 EST - 2009-01-14 00:00:00 EST = **P+0Y0M7DT0H0M0S** | BACK: 2009-01-14 00:00:00 EST - P+0Y0M7DT0H0M0S = **2009-01-07 00:00:00 EST** | DAYS: **7** diff --git a/ext/date/tests/DateTime_diff_add_sub-dates.phpt b/ext/date/tests/DateTime_diff_add_sub-dates.phpt deleted file mode 100644 index 8fd7c4021..000000000 --- a/ext/date/tests/DateTime_diff_add_sub-dates.phpt +++ /dev/null @@ -1,89 +0,0 @@ ---TEST-- -DateTime::diff() add() sub() -- dates ---CREDITS-- -Daniel Convissor <danielc@php.net> ---FILE-- -<?php - -/* - * Note: test names match method names in a set of PHPUnit tests - * in a userland package. Please be so kind as to leave them. - */ - -require './examine_diff.inc'; -date_default_timezone_set('America/New_York'); - - -/* - * Particular days - */ -echo "test__7: "; -examine_diff('2009-01-14', '2009-01-07', 'P+0Y0M7DT0H0M0S', 7); - -echo "test_years_positive__7_by_0_day: "; -examine_diff('2007-02-07', '2000-02-07', 'P+7Y0M0DT0H0M0S', 2557); - -echo "test_years_positive__7_by_1_day: "; -examine_diff('2007-02-08', '2000-02-07', 'P+7Y0M1DT0H0M0S', 2558); - -echo "test_years_positive__6_shy_1_day: "; -examine_diff('2007-02-06', '2000-02-07', 'P+6Y11M30DT0H0M0S', 2556); - -echo "test_years_positive__7_by_1_month: "; -examine_diff('2007-03-07', '2000-02-07', 'P+7Y1M0DT0H0M0S', 2585); - -echo "test_years_positive__6_shy_1_month: "; -examine_diff('2007-01-07', '2000-02-07', 'P+6Y11M0DT0H0M0S', 2526); - -echo "test_years_positive__7_by_1_month_split_newyear: "; -examine_diff('2007-01-07', '1999-12-07', 'P+7Y1M0DT0H0M0S', 2588); - -echo "test_years_positive__6_shy_1_month_split_newyear: "; -examine_diff('2006-12-07', '2000-01-07', 'P+6Y11M0DT0H0M0S', 2526); - - -/* - * Particular days, negative - */ -echo "test_negative__7: "; -examine_diff('2009-01-07', '2009-01-14', 'P-0Y0M7DT0H0M0S', 7); - -echo "test_years_negative__7_by_0_day: "; -examine_diff('2000-02-07', '2007-02-07', 'P-7Y0M0DT0H0M0S', 2557); - -echo "test_years_negative__7_by_1_day: "; -examine_diff('2000-02-07', '2007-02-08', 'P-7Y0M1DT0H0M0S', 2558); - -echo "test_years_negative__6_shy_1_day: "; -examine_diff('2000-02-07', '2007-02-06', 'P-6Y11M28DT0H0M0S', 2556); - -echo "test_years_negative__7_by_1_month: "; -examine_diff('2000-02-07', '2007-03-07', 'P-7Y1M0DT0H0M0S', 2585); - -echo "test_years_negative__6_shy_1_month: "; -examine_diff('2000-02-07', '2007-01-07', 'P-6Y11M0DT0H0M0S', 2526); - -echo "test_years_negative__7_by_1_month_split_newyear: "; -examine_diff('1999-12-07', '2007-01-07', 'P-7Y1M0DT0H0M0S', 2588); - -echo "test_years_negative__6_shy_1_month_split_newyear: "; -examine_diff('2000-01-07', '2006-12-07', 'P-6Y11M0DT0H0M0S', 2526); - -?> ---EXPECT-- -test__7: FWD: 2009-01-14 00:00:00 EST - 2009-01-07 00:00:00 EST = **P+0Y0M7DT0H0M0S** | BACK: 2009-01-07 00:00:00 EST + P+0Y0M7DT0H0M0S = **2009-01-14 00:00:00 EST** | DAYS: **7** -test_years_positive__7_by_0_day: FWD: 2007-02-07 00:00:00 EST - 2000-02-07 00:00:00 EST = **P+7Y0M0DT0H0M0S** | BACK: 2000-02-07 00:00:00 EST + P+7Y0M0DT0H0M0S = **2007-02-07 00:00:00 EST** | DAYS: **2557** -test_years_positive__7_by_1_day: FWD: 2007-02-08 00:00:00 EST - 2000-02-07 00:00:00 EST = **P+7Y0M1DT0H0M0S** | BACK: 2000-02-07 00:00:00 EST + P+7Y0M1DT0H0M0S = **2007-02-08 00:00:00 EST** | DAYS: **2558** -test_years_positive__6_shy_1_day: FWD: 2007-02-06 00:00:00 EST - 2000-02-07 00:00:00 EST = **P+6Y11M30DT0H0M0S** | BACK: 2000-02-07 00:00:00 EST + P+6Y11M30DT0H0M0S = **2007-02-06 00:00:00 EST** | DAYS: **2556** -test_years_positive__7_by_1_month: FWD: 2007-03-07 00:00:00 EST - 2000-02-07 00:00:00 EST = **P+7Y1M0DT0H0M0S** | BACK: 2000-02-07 00:00:00 EST + P+7Y1M0DT0H0M0S = **2007-03-07 00:00:00 EST** | DAYS: **2585** -test_years_positive__6_shy_1_month: FWD: 2007-01-07 00:00:00 EST - 2000-02-07 00:00:00 EST = **P+6Y11M0DT0H0M0S** | BACK: 2000-02-07 00:00:00 EST + P+6Y11M0DT0H0M0S = **2007-01-07 00:00:00 EST** | DAYS: **2526** -test_years_positive__7_by_1_month_split_newyear: FWD: 2007-01-07 00:00:00 EST - 1999-12-07 00:00:00 EST = **P+7Y1M0DT0H0M0S** | BACK: 1999-12-07 00:00:00 EST + P+7Y1M0DT0H0M0S = **2007-01-07 00:00:00 EST** | DAYS: **2588** -test_years_positive__6_shy_1_month_split_newyear: FWD: 2006-12-07 00:00:00 EST - 2000-01-07 00:00:00 EST = **P+6Y11M0DT0H0M0S** | BACK: 2000-01-07 00:00:00 EST + P+6Y11M0DT0H0M0S = **2006-12-07 00:00:00 EST** | DAYS: **2526** -test_negative__7: FWD: 2009-01-07 00:00:00 EST - 2009-01-14 00:00:00 EST = **P-0Y0M7DT0H0M0S** | BACK: 2009-01-14 00:00:00 EST + P-0Y0M7DT0H0M0S = **2009-01-07 00:00:00 EST** | DAYS: **7** -test_years_negative__7_by_0_day: FWD: 2000-02-07 00:00:00 EST - 2007-02-07 00:00:00 EST = **P-7Y0M0DT0H0M0S** | BACK: 2007-02-07 00:00:00 EST + P-7Y0M0DT0H0M0S = **2000-02-07 00:00:00 EST** | DAYS: **2557** -test_years_negative__7_by_1_day: FWD: 2000-02-07 00:00:00 EST - 2007-02-08 00:00:00 EST = **P-7Y0M1DT0H0M0S** | BACK: 2007-02-08 00:00:00 EST + P-7Y0M1DT0H0M0S = **2000-02-07 00:00:00 EST** | DAYS: **2558** -test_years_negative__6_shy_1_day: FWD: 2000-02-07 00:00:00 EST - 2007-02-06 00:00:00 EST = **P-6Y11M28DT0H0M0S** | BACK: 2007-02-06 00:00:00 EST + P-6Y11M28DT0H0M0S = **2000-02-07 00:00:00 EST** | DAYS: **2556** -test_years_negative__7_by_1_month: FWD: 2000-02-07 00:00:00 EST - 2007-03-07 00:00:00 EST = **P-7Y1M0DT0H0M0S** | BACK: 2007-03-07 00:00:00 EST + P-7Y1M0DT0H0M0S = **2000-02-07 00:00:00 EST** | DAYS: **2585** -test_years_negative__6_shy_1_month: FWD: 2000-02-07 00:00:00 EST - 2007-01-07 00:00:00 EST = **P-6Y11M0DT0H0M0S** | BACK: 2007-01-07 00:00:00 EST + P-6Y11M0DT0H0M0S = **2000-02-07 00:00:00 EST** | DAYS: **2526** -test_years_negative__7_by_1_month_split_newyear: FWD: 1999-12-07 00:00:00 EST - 2007-01-07 00:00:00 EST = **P-7Y1M0DT0H0M0S** | BACK: 2007-01-07 00:00:00 EST + P-7Y1M0DT0H0M0S = **1999-12-07 00:00:00 EST** | DAYS: **2588** -test_years_negative__6_shy_1_month_split_newyear: FWD: 2000-01-07 00:00:00 EST - 2006-12-07 00:00:00 EST = **P-6Y11M0DT0H0M0S** | BACK: 2006-12-07 00:00:00 EST + P-6Y11M0DT0H0M0S = **2000-01-07 00:00:00 EST** | DAYS: **2526** diff --git a/ext/date/tests/DateTime_diff_add_sub-february.phpt b/ext/date/tests/DateTime_diff_add_sub-february.phpt deleted file mode 100644 index 5a2f9d40b..000000000 --- a/ext/date/tests/DateTime_diff_add_sub-february.phpt +++ /dev/null @@ -1,281 +0,0 @@ ---TEST-- -DateTime::diff() add() sub() -- february ---CREDITS-- -Daniel Convissor <danielc@php.net> ---FILE-- -<?php - -/* - * Note: test names match method names in a set of PHPUnit tests - * in a userland package. Please be so kind as to leave them. - */ - -require './examine_diff.inc'; -date_default_timezone_set('America/New_York'); - - -/* - * Check PHP bug 49081 - */ -echo "test_bug_49081__1: "; -examine_diff('2010-03-31', '2010-03-01', 'P+0Y0M30DT0H0M0S', 30); - -echo "test_bug_49081__2: "; -examine_diff('2010-04-01', '2010-03-01', 'P+0Y1M0DT0H0M0S', 31); - -echo "test_bug_49081__3: "; -examine_diff('2010-04-01', '2010-03-31', 'P+0Y0M1DT0H0M0S', 1); - -echo "test_bug_49081__4: "; -examine_diff('2010-04-29', '2010-03-31', 'P+0Y0M29DT0H0M0S', 29); - -echo "test_bug_49081__5: "; -examine_diff('2010-04-30', '2010-03-31', 'P+0Y0M30DT0H0M0S', 30); - -echo "test_bug_49081__6: "; -examine_diff('2010-04-30', '2010-03-30', 'P+0Y1M0DT0H0M0S', 31); - -echo "test_bug_49081__7: "; -examine_diff('2010-04-30', '2010-03-29', 'P+0Y1M1DT0H0M0S', 32); - -echo "test_bug_49081__8: "; -examine_diff('2010-01-29', '2010-01-01', 'P+0Y0M28DT0H0M0S', 28); - -echo "test_bug_49081__9: "; -examine_diff('2010-01-30', '2010-01-01', 'P+0Y0M29DT0H0M0S', 29); - -echo "test_bug_49081__10: "; -examine_diff('2010-01-31', '2010-01-01', 'P+0Y0M30DT0H0M0S', 30); - -echo "test_bug_49081__11: "; -examine_diff('2010-02-01', '2010-01-01', 'P+0Y1M0DT0H0M0S', 31); - -echo "test_bug_49081__12: "; -examine_diff('2010-02-01', '2010-01-31', 'P+0Y0M1DT0H0M0S', 1); - -echo "test_bug_49081__13: "; -examine_diff('2010-02-27', '2010-01-31', 'P+0Y0M27DT0H0M0S', 27); - -echo "test_bug_49081__14: "; -examine_diff('2010-02-28', '2010-01-31', 'P+0Y0M28DT0H0M0S', 28); - -echo "test_bug_49081__15: "; -examine_diff('2010-02-28', '2010-01-30', 'P+0Y0M29DT0H0M0S', 29); - -echo "test_bug_49081__16: "; -examine_diff('2010-02-28', '2010-01-29', 'P+0Y0M30DT0H0M0S', 30); - -echo "test_bug_49081__17: "; -examine_diff('2010-02-28', '2010-01-28', 'P+0Y1M0DT0H0M0S', 31); - -echo "test_bug_49081__18: "; -examine_diff('2010-02-28', '2010-01-27', 'P+0Y1M1DT0H0M0S', 32); - -echo "test_bug_49081__19: "; -examine_diff('2010-03-01', '2010-01-01', 'P+0Y2M0DT0H0M0S', 59); - -echo "test_bug_49081__20: "; -examine_diff('2010-03-01', '2010-01-31', 'P+0Y0M29DT0H0M0S', 29); - -echo "test_bug_49081__21: "; -examine_diff('2010-03-27', '2010-01-31', 'P+0Y1M24DT0H0M0S', 55); - -echo "test_bug_49081__22: "; -examine_diff('2010-03-28', '2010-01-31', 'P+0Y1M25DT0H0M0S', 56); - -echo "test_bug_49081__23: "; -examine_diff('2010-03-29', '2010-01-31', 'P+0Y1M26DT0H0M0S', 57); - -echo "test_bug_49081__24: "; -examine_diff('2010-03-30', '2010-01-31', 'P+0Y1M27DT0H0M0S', 58); - -echo "test_bug_49081__25: "; -examine_diff('2010-03-31', '2010-01-31', 'P+0Y2M0DT0H0M0S', 59); - -echo "test_bug_49081__26: "; -examine_diff('2010-03-31', '2010-01-30', 'P+0Y2M1DT0H0M0S', 60); - -echo "test_bug_49081__27: "; -examine_diff('2009-01-31', '2009-01-01', 'P+0Y0M30DT0H0M0S', 30); - -echo "test_bug_49081__28: "; -examine_diff('2010-03-27', '2010-02-28', 'P+0Y0M27DT0H0M0S', 27); - -echo "test_bug_49081__29: "; -examine_diff('2010-03-28', '2010-02-28', 'P+0Y1M0DT0H0M0S', 28); - -echo "test_bug_49081__30: "; -examine_diff('2010-03-29', '2010-02-28', 'P+0Y1M1DT0H0M0S', 29); - -echo "test_bug_49081__31: "; -examine_diff('2010-03-27', '2010-02-27', 'P+0Y1M0DT0H0M0S', 28); - -echo "test_bug_49081__32: "; -examine_diff('2010-03-27', '2010-02-26', 'P+0Y1M1DT0H0M0S', 29); - - -/* - * Check PHP bug 49081, negative - */ -echo "test_bug_49081_negative__1: "; -examine_diff('2010-03-01', '2010-03-31', 'P-0Y0M30DT0H0M0S', 30); - -echo "test_bug_49081_negative__2: "; -examine_diff('2010-03-01', '2010-04-01', 'P-0Y1M0DT0H0M0S', 31); - -echo "test_bug_49081_negative__3: "; -examine_diff('2010-03-31', '2010-04-01', 'P-0Y0M1DT0H0M0S', 1); - -echo "test_bug_49081_negative__4: "; -examine_diff('2010-03-31', '2010-04-29', 'P-0Y0M29DT0H0M0S', 29); - -echo "test_bug_49081_negative__5: "; -examine_diff('2010-03-31', '2010-04-30', 'P-0Y0M30DT0H0M0S', 30); - -echo "test_bug_49081_negative__6: "; -examine_diff('2010-03-30', '2010-04-30', 'P-0Y1M0DT0H0M0S', 31); - -echo "test_bug_49081_negative__7: "; -examine_diff('2010-03-29', '2010-04-30', 'P-0Y1M1DT0H0M0S', 32); - -echo "test_bug_49081_negative__8: "; -examine_diff('2010-01-01', '2010-01-29', 'P-0Y0M28DT0H0M0S', 28); - -echo "test_bug_49081_negative__9: "; -examine_diff('2010-01-01', '2010-01-30', 'P-0Y0M29DT0H0M0S', 29); - -echo "test_bug_49081_negative__10: "; -examine_diff('2010-01-01', '2010-01-31', 'P-0Y0M30DT0H0M0S', 30); - -echo "test_bug_49081_negative__11: "; -examine_diff('2010-01-01', '2010-02-01', 'P-0Y1M0DT0H0M0S', 31); - -echo "test_bug_49081_negative__12: "; -examine_diff('2010-01-31', '2010-02-01', 'P-0Y0M1DT0H0M0S', 1); - -echo "test_bug_49081_negative__13: "; -examine_diff('2010-01-31', '2010-02-27', 'P-0Y0M27DT0H0M0S', 27); - -echo "test_bug_49081_negative__14: "; -examine_diff('2010-01-31', '2010-02-28', 'P-0Y0M28DT0H0M0S', 28); - -echo "test_bug_49081_negative__15: "; -examine_diff('2010-01-30', '2010-02-28', 'P-0Y0M29DT0H0M0S', 29); - -echo "test_bug_49081_negative__16: "; -examine_diff('2010-01-29', '2010-02-28', 'P-0Y0M30DT0H0M0S', 30); - -echo "test_bug_49081_negative__17: "; -examine_diff('2010-01-28', '2010-02-28', 'P-0Y1M0DT0H0M0S', 31); - -echo "test_bug_49081_negative__18: "; -examine_diff('2010-01-27', '2010-02-28', 'P-0Y1M1DT0H0M0S', 32); - -echo "test_bug_49081_negative__19: "; -examine_diff('2010-01-01', '2010-03-01', 'P-0Y2M0DT0H0M0S', 59); - -echo "test_bug_49081_negative__20: "; -examine_diff('2010-01-31', '2010-03-01', 'P-0Y1M1DT0H0M0S', 29); - -echo "test_bug_49081_negative__21: "; -examine_diff('2010-01-31', '2010-03-27', 'P-0Y1M27DT0H0M0S', 55); - -echo "test_bug_49081_negative__22: "; -examine_diff('2010-01-31', '2010-03-28', 'P-0Y1M28DT0H0M0S', 56); - -echo "test_bug_49081_negative__23: "; -examine_diff('2010-01-31', '2010-03-29', 'P-0Y1M29DT0H0M0S', 57); - -echo "test_bug_49081_negative__24: "; -examine_diff('2010-01-31', '2010-03-30', 'P-0Y1M30DT0H0M0S', 58); - -echo "test_bug_49081_negative__25: "; -examine_diff('2010-01-31', '2010-03-31', 'P-0Y2M0DT0H0M0S', 59); - -echo "test_bug_49081_negative__26: "; -examine_diff('2010-01-30', '2010-03-31', 'P-0Y2M1DT0H0M0S', 60); - -echo "test_bug_49081_negative__27: "; -examine_diff('2009-01-01', '2009-01-31', 'P-0Y0M30DT0H0M0S', 30); - -echo "test_bug_49081_negative__28: "; -examine_diff('2010-02-28', '2010-03-27', 'P-0Y0M27DT0H0M0S', 27); - -echo "test_bug_49081_negative__29: "; -examine_diff('2010-02-28', '2010-03-28', 'P-0Y1M0DT0H0M0S', 28); - -echo "test_bug_49081_negative__30: "; -examine_diff('2010-02-28', '2010-03-29', 'P-0Y1M1DT0H0M0S', 29); - -echo "test_bug_49081_negative__31: "; -examine_diff('2010-02-27', '2010-03-27', 'P-0Y1M0DT0H0M0S', 28); - -echo "test_bug_49081_negative__32: "; -examine_diff('2010-02-26', '2010-03-27', 'P-0Y1M1DT0H0M0S', 29); - -?> ---EXPECT-- -test_bug_49081__1: FWD: 2010-03-31 00:00:00 EDT - 2010-03-01 00:00:00 EST = **P+0Y0M30DT0H0M0S** | BACK: 2010-03-01 00:00:00 EST + P+0Y0M30DT0H0M0S = **2010-03-31 00:00:00 EDT** | DAYS: **30** -test_bug_49081__2: FWD: 2010-04-01 00:00:00 EDT - 2010-03-01 00:00:00 EST = **P+0Y1M0DT0H0M0S** | BACK: 2010-03-01 00:00:00 EST + P+0Y1M0DT0H0M0S = **2010-04-01 00:00:00 EDT** | DAYS: **31** -test_bug_49081__3: FWD: 2010-04-01 00:00:00 EDT - 2010-03-31 00:00:00 EDT = **P+0Y0M1DT0H0M0S** | BACK: 2010-03-31 00:00:00 EDT + P+0Y0M1DT0H0M0S = **2010-04-01 00:00:00 EDT** | DAYS: **1** -test_bug_49081__4: FWD: 2010-04-29 00:00:00 EDT - 2010-03-31 00:00:00 EDT = **P+0Y0M29DT0H0M0S** | BACK: 2010-03-31 00:00:00 EDT + P+0Y0M29DT0H0M0S = **2010-04-29 00:00:00 EDT** | DAYS: **29** -test_bug_49081__5: FWD: 2010-04-30 00:00:00 EDT - 2010-03-31 00:00:00 EDT = **P+0Y0M30DT0H0M0S** | BACK: 2010-03-31 00:00:00 EDT + P+0Y0M30DT0H0M0S = **2010-04-30 00:00:00 EDT** | DAYS: **30** -test_bug_49081__6: FWD: 2010-04-30 00:00:00 EDT - 2010-03-30 00:00:00 EDT = **P+0Y1M0DT0H0M0S** | BACK: 2010-03-30 00:00:00 EDT + P+0Y1M0DT0H0M0S = **2010-04-30 00:00:00 EDT** | DAYS: **31** -test_bug_49081__7: FWD: 2010-04-30 00:00:00 EDT - 2010-03-29 00:00:00 EDT = **P+0Y1M1DT0H0M0S** | BACK: 2010-03-29 00:00:00 EDT + P+0Y1M1DT0H0M0S = **2010-04-30 00:00:00 EDT** | DAYS: **32** -test_bug_49081__8: FWD: 2010-01-29 00:00:00 EST - 2010-01-01 00:00:00 EST = **P+0Y0M28DT0H0M0S** | BACK: 2010-01-01 00:00:00 EST + P+0Y0M28DT0H0M0S = **2010-01-29 00:00:00 EST** | DAYS: **28** -test_bug_49081__9: FWD: 2010-01-30 00:00:00 EST - 2010-01-01 00:00:00 EST = **P+0Y0M29DT0H0M0S** | BACK: 2010-01-01 00:00:00 EST + P+0Y0M29DT0H0M0S = **2010-01-30 00:00:00 EST** | DAYS: **29** -test_bug_49081__10: FWD: 2010-01-31 00:00:00 EST - 2010-01-01 00:00:00 EST = **P+0Y0M30DT0H0M0S** | BACK: 2010-01-01 00:00:00 EST + P+0Y0M30DT0H0M0S = **2010-01-31 00:00:00 EST** | DAYS: **30** -test_bug_49081__11: FWD: 2010-02-01 00:00:00 EST - 2010-01-01 00:00:00 EST = **P+0Y1M0DT0H0M0S** | BACK: 2010-01-01 00:00:00 EST + P+0Y1M0DT0H0M0S = **2010-02-01 00:00:00 EST** | DAYS: **31** -test_bug_49081__12: FWD: 2010-02-01 00:00:00 EST - 2010-01-31 00:00:00 EST = **P+0Y0M1DT0H0M0S** | BACK: 2010-01-31 00:00:00 EST + P+0Y0M1DT0H0M0S = **2010-02-01 00:00:00 EST** | DAYS: **1** -test_bug_49081__13: FWD: 2010-02-27 00:00:00 EST - 2010-01-31 00:00:00 EST = **P+0Y0M27DT0H0M0S** | BACK: 2010-01-31 00:00:00 EST + P+0Y0M27DT0H0M0S = **2010-02-27 00:00:00 EST** | DAYS: **27** -test_bug_49081__14: FWD: 2010-02-28 00:00:00 EST - 2010-01-31 00:00:00 EST = **P+0Y0M28DT0H0M0S** | BACK: 2010-01-31 00:00:00 EST + P+0Y0M28DT0H0M0S = **2010-02-28 00:00:00 EST** | DAYS: **28** -test_bug_49081__15: FWD: 2010-02-28 00:00:00 EST - 2010-01-30 00:00:00 EST = **P+0Y0M29DT0H0M0S** | BACK: 2010-01-30 00:00:00 EST + P+0Y0M29DT0H0M0S = **2010-02-28 00:00:00 EST** | DAYS: **29** -test_bug_49081__16: FWD: 2010-02-28 00:00:00 EST - 2010-01-29 00:00:00 EST = **P+0Y0M30DT0H0M0S** | BACK: 2010-01-29 00:00:00 EST + P+0Y0M30DT0H0M0S = **2010-02-28 00:00:00 EST** | DAYS: **30** -test_bug_49081__17: FWD: 2010-02-28 00:00:00 EST - 2010-01-28 00:00:00 EST = **P+0Y1M0DT0H0M0S** | BACK: 2010-01-28 00:00:00 EST + P+0Y1M0DT0H0M0S = **2010-02-28 00:00:00 EST** | DAYS: **31** -test_bug_49081__18: FWD: 2010-02-28 00:00:00 EST - 2010-01-27 00:00:00 EST = **P+0Y1M1DT0H0M0S** | BACK: 2010-01-27 00:00:00 EST + P+0Y1M1DT0H0M0S = **2010-02-28 00:00:00 EST** | DAYS: **32** -test_bug_49081__19: FWD: 2010-03-01 00:00:00 EST - 2010-01-01 00:00:00 EST = **P+0Y2M0DT0H0M0S** | BACK: 2010-01-01 00:00:00 EST + P+0Y2M0DT0H0M0S = **2010-03-01 00:00:00 EST** | DAYS: **59** -test_bug_49081__20: FWD: 2010-03-01 00:00:00 EST - 2010-01-31 00:00:00 EST = **P+0Y0M29DT0H0M0S** | BACK: 2010-01-31 00:00:00 EST + P+0Y0M29DT0H0M0S = **2010-03-01 00:00:00 EST** | DAYS: **29** -test_bug_49081__21: FWD: 2010-03-27 00:00:00 EDT - 2010-01-31 00:00:00 EST = **P+0Y1M24DT0H0M0S** | BACK: 2010-01-31 00:00:00 EST + P+0Y1M24DT0H0M0S = **2010-03-27 00:00:00 EDT** | DAYS: **55** -test_bug_49081__22: FWD: 2010-03-28 00:00:00 EDT - 2010-01-31 00:00:00 EST = **P+0Y1M25DT0H0M0S** | BACK: 2010-01-31 00:00:00 EST + P+0Y1M25DT0H0M0S = **2010-03-28 00:00:00 EDT** | DAYS: **56** -test_bug_49081__23: FWD: 2010-03-29 00:00:00 EDT - 2010-01-31 00:00:00 EST = **P+0Y1M26DT0H0M0S** | BACK: 2010-01-31 00:00:00 EST + P+0Y1M26DT0H0M0S = **2010-03-29 00:00:00 EDT** | DAYS: **57** -test_bug_49081__24: FWD: 2010-03-30 00:00:00 EDT - 2010-01-31 00:00:00 EST = **P+0Y1M27DT0H0M0S** | BACK: 2010-01-31 00:00:00 EST + P+0Y1M27DT0H0M0S = **2010-03-30 00:00:00 EDT** | DAYS: **58** -test_bug_49081__25: FWD: 2010-03-31 00:00:00 EDT - 2010-01-31 00:00:00 EST = **P+0Y2M0DT0H0M0S** | BACK: 2010-01-31 00:00:00 EST + P+0Y2M0DT0H0M0S = **2010-03-31 00:00:00 EDT** | DAYS: **59** -test_bug_49081__26: FWD: 2010-03-31 00:00:00 EDT - 2010-01-30 00:00:00 EST = **P+0Y2M1DT0H0M0S** | BACK: 2010-01-30 00:00:00 EST + P+0Y2M1DT0H0M0S = **2010-03-31 00:00:00 EDT** | DAYS: **60** -test_bug_49081__27: FWD: 2009-01-31 00:00:00 EST - 2009-01-01 00:00:00 EST = **P+0Y0M30DT0H0M0S** | BACK: 2009-01-01 00:00:00 EST + P+0Y0M30DT0H0M0S = **2009-01-31 00:00:00 EST** | DAYS: **30** -test_bug_49081__28: FWD: 2010-03-27 00:00:00 EDT - 2010-02-28 00:00:00 EST = **P+0Y0M27DT0H0M0S** | BACK: 2010-02-28 00:00:00 EST + P+0Y0M27DT0H0M0S = **2010-03-27 00:00:00 EDT** | DAYS: **27** -test_bug_49081__29: FWD: 2010-03-28 00:00:00 EDT - 2010-02-28 00:00:00 EST = **P+0Y1M0DT0H0M0S** | BACK: 2010-02-28 00:00:00 EST + P+0Y1M0DT0H0M0S = **2010-03-28 00:00:00 EDT** | DAYS: **28** -test_bug_49081__30: FWD: 2010-03-29 00:00:00 EDT - 2010-02-28 00:00:00 EST = **P+0Y1M1DT0H0M0S** | BACK: 2010-02-28 00:00:00 EST + P+0Y1M1DT0H0M0S = **2010-03-29 00:00:00 EDT** | DAYS: **29** -test_bug_49081__31: FWD: 2010-03-27 00:00:00 EDT - 2010-02-27 00:00:00 EST = **P+0Y1M0DT0H0M0S** | BACK: 2010-02-27 00:00:00 EST + P+0Y1M0DT0H0M0S = **2010-03-27 00:00:00 EDT** | DAYS: **28** -test_bug_49081__32: FWD: 2010-03-27 00:00:00 EDT - 2010-02-26 00:00:00 EST = **P+0Y1M1DT0H0M0S** | BACK: 2010-02-26 00:00:00 EST + P+0Y1M1DT0H0M0S = **2010-03-27 00:00:00 EDT** | DAYS: **29** -test_bug_49081_negative__1: FWD: 2010-03-01 00:00:00 EST - 2010-03-31 00:00:00 EDT = **P-0Y0M30DT0H0M0S** | BACK: 2010-03-31 00:00:00 EDT + P-0Y0M30DT0H0M0S = **2010-03-01 00:00:00 EST** | DAYS: **30** -test_bug_49081_negative__2: FWD: 2010-03-01 00:00:00 EST - 2010-04-01 00:00:00 EDT = **P-0Y1M0DT0H0M0S** | BACK: 2010-04-01 00:00:00 EDT + P-0Y1M0DT0H0M0S = **2010-03-01 00:00:00 EST** | DAYS: **31** -test_bug_49081_negative__3: FWD: 2010-03-31 00:00:00 EDT - 2010-04-01 00:00:00 EDT = **P-0Y0M1DT0H0M0S** | BACK: 2010-04-01 00:00:00 EDT + P-0Y0M1DT0H0M0S = **2010-03-31 00:00:00 EDT** | DAYS: **1** -test_bug_49081_negative__4: FWD: 2010-03-31 00:00:00 EDT - 2010-04-29 00:00:00 EDT = **P-0Y0M29DT0H0M0S** | BACK: 2010-04-29 00:00:00 EDT + P-0Y0M29DT0H0M0S = **2010-03-31 00:00:00 EDT** | DAYS: **29** -test_bug_49081_negative__5: FWD: 2010-03-31 00:00:00 EDT - 2010-04-30 00:00:00 EDT = **P-0Y0M30DT0H0M0S** | BACK: 2010-04-30 00:00:00 EDT + P-0Y0M30DT0H0M0S = **2010-03-31 00:00:00 EDT** | DAYS: **30** -test_bug_49081_negative__6: FWD: 2010-03-30 00:00:00 EDT - 2010-04-30 00:00:00 EDT = **P-0Y1M0DT0H0M0S** | BACK: 2010-04-30 00:00:00 EDT + P-0Y1M0DT0H0M0S = **2010-03-30 00:00:00 EDT** | DAYS: **31** -test_bug_49081_negative__7: FWD: 2010-03-29 00:00:00 EDT - 2010-04-30 00:00:00 EDT = **P-0Y1M1DT0H0M0S** | BACK: 2010-04-30 00:00:00 EDT + P-0Y1M1DT0H0M0S = **2010-03-29 00:00:00 EDT** | DAYS: **32** -test_bug_49081_negative__8: FWD: 2010-01-01 00:00:00 EST - 2010-01-29 00:00:00 EST = **P-0Y0M28DT0H0M0S** | BACK: 2010-01-29 00:00:00 EST + P-0Y0M28DT0H0M0S = **2010-01-01 00:00:00 EST** | DAYS: **28** -test_bug_49081_negative__9: FWD: 2010-01-01 00:00:00 EST - 2010-01-30 00:00:00 EST = **P-0Y0M29DT0H0M0S** | BACK: 2010-01-30 00:00:00 EST + P-0Y0M29DT0H0M0S = **2010-01-01 00:00:00 EST** | DAYS: **29** -test_bug_49081_negative__10: FWD: 2010-01-01 00:00:00 EST - 2010-01-31 00:00:00 EST = **P-0Y0M30DT0H0M0S** | BACK: 2010-01-31 00:00:00 EST + P-0Y0M30DT0H0M0S = **2010-01-01 00:00:00 EST** | DAYS: **30** -test_bug_49081_negative__11: FWD: 2010-01-01 00:00:00 EST - 2010-02-01 00:00:00 EST = **P-0Y1M0DT0H0M0S** | BACK: 2010-02-01 00:00:00 EST + P-0Y1M0DT0H0M0S = **2010-01-01 00:00:00 EST** | DAYS: **31** -test_bug_49081_negative__12: FWD: 2010-01-31 00:00:00 EST - 2010-02-01 00:00:00 EST = **P-0Y0M1DT0H0M0S** | BACK: 2010-02-01 00:00:00 EST + P-0Y0M1DT0H0M0S = **2010-01-31 00:00:00 EST** | DAYS: **1** -test_bug_49081_negative__13: FWD: 2010-01-31 00:00:00 EST - 2010-02-27 00:00:00 EST = **P-0Y0M27DT0H0M0S** | BACK: 2010-02-27 00:00:00 EST + P-0Y0M27DT0H0M0S = **2010-01-31 00:00:00 EST** | DAYS: **27** -test_bug_49081_negative__14: FWD: 2010-01-31 00:00:00 EST - 2010-02-28 00:00:00 EST = **P-0Y0M28DT0H0M0S** | BACK: 2010-02-28 00:00:00 EST + P-0Y0M28DT0H0M0S = **2010-01-31 00:00:00 EST** | DAYS: **28** -test_bug_49081_negative__15: FWD: 2010-01-30 00:00:00 EST - 2010-02-28 00:00:00 EST = **P-0Y0M29DT0H0M0S** | BACK: 2010-02-28 00:00:00 EST + P-0Y0M29DT0H0M0S = **2010-01-30 00:00:00 EST** | DAYS: **29** -test_bug_49081_negative__16: FWD: 2010-01-29 00:00:00 EST - 2010-02-28 00:00:00 EST = **P-0Y0M30DT0H0M0S** | BACK: 2010-02-28 00:00:00 EST + P-0Y0M30DT0H0M0S = **2010-01-29 00:00:00 EST** | DAYS: **30** -test_bug_49081_negative__17: FWD: 2010-01-28 00:00:00 EST - 2010-02-28 00:00:00 EST = **P-0Y1M0DT0H0M0S** | BACK: 2010-02-28 00:00:00 EST + P-0Y1M0DT0H0M0S = **2010-01-28 00:00:00 EST** | DAYS: **31** -test_bug_49081_negative__18: FWD: 2010-01-27 00:00:00 EST - 2010-02-28 00:00:00 EST = **P-0Y1M1DT0H0M0S** | BACK: 2010-02-28 00:00:00 EST + P-0Y1M1DT0H0M0S = **2010-01-27 00:00:00 EST** | DAYS: **32** -test_bug_49081_negative__19: FWD: 2010-01-01 00:00:00 EST - 2010-03-01 00:00:00 EST = **P-0Y2M0DT0H0M0S** | BACK: 2010-03-01 00:00:00 EST + P-0Y2M0DT0H0M0S = **2010-01-01 00:00:00 EST** | DAYS: **59** -test_bug_49081_negative__20: FWD: 2010-01-31 00:00:00 EST - 2010-03-01 00:00:00 EST = **P-0Y1M1DT0H0M0S** | BACK: 2010-03-01 00:00:00 EST + P-0Y1M1DT0H0M0S = **2010-01-31 00:00:00 EST** | DAYS: **29** -test_bug_49081_negative__21: FWD: 2010-01-31 00:00:00 EST - 2010-03-27 00:00:00 EDT = **P-0Y1M27DT0H0M0S** | BACK: 2010-03-27 00:00:00 EDT + P-0Y1M27DT0H0M0S = **2010-01-31 00:00:00 EST** | DAYS: **55** -test_bug_49081_negative__22: FWD: 2010-01-31 00:00:00 EST - 2010-03-28 00:00:00 EDT = **P-0Y1M28DT0H0M0S** | BACK: 2010-03-28 00:00:00 EDT + P-0Y1M28DT0H0M0S = **2010-01-31 00:00:00 EST** | DAYS: **56** -test_bug_49081_negative__23: FWD: 2010-01-31 00:00:00 EST - 2010-03-29 00:00:00 EDT = **P-0Y1M29DT0H0M0S** | BACK: 2010-03-29 00:00:00 EDT + P-0Y1M29DT0H0M0S = **2010-01-31 00:00:00 EST** | DAYS: **57** -test_bug_49081_negative__24: FWD: 2010-01-31 00:00:00 EST - 2010-03-30 00:00:00 EDT = **P-0Y1M30DT0H0M0S** | BACK: 2010-03-30 00:00:00 EDT + P-0Y1M30DT0H0M0S = **2010-01-31 00:00:00 EST** | DAYS: **58** -test_bug_49081_negative__25: FWD: 2010-01-31 00:00:00 EST - 2010-03-31 00:00:00 EDT = **P-0Y2M0DT0H0M0S** | BACK: 2010-03-31 00:00:00 EDT + P-0Y2M0DT0H0M0S = **2010-01-31 00:00:00 EST** | DAYS: **59** -test_bug_49081_negative__26: FWD: 2010-01-30 00:00:00 EST - 2010-03-31 00:00:00 EDT = **P-0Y2M1DT0H0M0S** | BACK: 2010-03-31 00:00:00 EDT + P-0Y2M1DT0H0M0S = **2010-01-30 00:00:00 EST** | DAYS: **60** -test_bug_49081_negative__27: FWD: 2009-01-01 00:00:00 EST - 2009-01-31 00:00:00 EST = **P-0Y0M30DT0H0M0S** | BACK: 2009-01-31 00:00:00 EST + P-0Y0M30DT0H0M0S = **2009-01-01 00:00:00 EST** | DAYS: **30** -test_bug_49081_negative__28: FWD: 2010-02-28 00:00:00 EST - 2010-03-27 00:00:00 EDT = **P-0Y0M27DT0H0M0S** | BACK: 2010-03-27 00:00:00 EDT + P-0Y0M27DT0H0M0S = **2010-02-28 00:00:00 EST** | DAYS: **27** -test_bug_49081_negative__29: FWD: 2010-02-28 00:00:00 EST - 2010-03-28 00:00:00 EDT = **P-0Y1M0DT0H0M0S** | BACK: 2010-03-28 00:00:00 EDT + P-0Y1M0DT0H0M0S = **2010-02-28 00:00:00 EST** | DAYS: **28** -test_bug_49081_negative__30: FWD: 2010-02-28 00:00:00 EST - 2010-03-29 00:00:00 EDT = **P-0Y1M1DT0H0M0S** | BACK: 2010-03-29 00:00:00 EDT + P-0Y1M1DT0H0M0S = **2010-02-28 00:00:00 EST** | DAYS: **29** -test_bug_49081_negative__31: FWD: 2010-02-27 00:00:00 EST - 2010-03-27 00:00:00 EDT = **P-0Y1M0DT0H0M0S** | BACK: 2010-03-27 00:00:00 EDT + P-0Y1M0DT0H0M0S = **2010-02-27 00:00:00 EST** | DAYS: **28** -test_bug_49081_negative__32: FWD: 2010-02-26 00:00:00 EST - 2010-03-27 00:00:00 EDT = **P-0Y1M1DT0H0M0S** | BACK: 2010-03-27 00:00:00 EDT + P-0Y1M1DT0H0M0S = **2010-02-26 00:00:00 EST** | DAYS: **29** diff --git a/ext/date/tests/DateTime_sub-dates.phpt b/ext/date/tests/DateTime_sub-dates.phpt new file mode 100644 index 000000000..36ca25c16 --- /dev/null +++ b/ext/date/tests/DateTime_sub-dates.phpt @@ -0,0 +1,29 @@ +--TEST-- +DateTime::sub() -- dates +--CREDITS-- +Daniel Convissor <danielc@php.net> +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_SUB); +require 'DateTime_data-dates.inc'; + +?> +--EXPECT-- +test__7: SUB: 2009-01-14 00:00:00 EST - P+0Y0M7DT0H0M0S = **2009-01-07 00:00:00 EST** +test_years_positive__7_by_0_day: SUB: 2007-02-07 00:00:00 EST - P+7Y0M0DT0H0M0S = **2000-02-07 00:00:00 EST** +test_years_positive__7_by_1_day: SUB: 2007-02-08 00:00:00 EST - P+7Y0M1DT0H0M0S = **2000-02-07 00:00:00 EST** +test_years_positive__6_shy_1_day: SUB: 2007-02-06 00:00:00 EST - P+6Y11M30DT0H0M0S = **2000-02-07 00:00:00 EST** +test_years_positive__7_by_1_month: SUB: 2007-03-07 00:00:00 EST - P+7Y1M0DT0H0M0S = **2000-02-07 00:00:00 EST** +test_years_positive__6_shy_1_month: SUB: 2007-01-07 00:00:00 EST - P+6Y11M0DT0H0M0S = **2000-02-07 00:00:00 EST** +test_years_positive__7_by_1_month_split_newyear: SUB: 2007-01-07 00:00:00 EST - P+7Y1M0DT0H0M0S = **1999-12-07 00:00:00 EST** +test_years_positive__6_shy_1_month_split_newyear: SUB: 2006-12-07 00:00:00 EST - P+6Y11M0DT0H0M0S = **2000-01-07 00:00:00 EST** +test_negative__7: SUB: 2009-01-07 00:00:00 EST - P-0Y0M7DT0H0M0S = **2009-01-14 00:00:00 EST** +test_years_negative__7_by_0_day: SUB: 2000-02-07 00:00:00 EST - P-7Y0M0DT0H0M0S = **2007-02-07 00:00:00 EST** +test_years_negative__7_by_1_day: SUB: 2000-02-07 00:00:00 EST - P-7Y0M1DT0H0M0S = **2007-02-08 00:00:00 EST** +test_years_negative__6_shy_1_day: SUB: 2000-02-07 00:00:00 EST - P-6Y11M28DT0H0M0S = **2007-02-06 00:00:00 EST** +test_years_negative__7_by_1_month: SUB: 2000-02-07 00:00:00 EST - P-7Y1M0DT0H0M0S = **2007-03-07 00:00:00 EST** +test_years_negative__6_shy_1_month: SUB: 2000-02-07 00:00:00 EST - P-6Y11M0DT0H0M0S = **2007-01-07 00:00:00 EST** +test_years_negative__7_by_1_month_split_newyear: SUB: 1999-12-07 00:00:00 EST - P-7Y1M0DT0H0M0S = **2007-01-07 00:00:00 EST** +test_years_negative__6_shy_1_month_split_newyear: SUB: 2000-01-07 00:00:00 EST - P-6Y11M0DT0H0M0S = **2006-12-07 00:00:00 EST** diff --git a/ext/date/tests/DateTime_sub-fall-type2-type2.phpt b/ext/date/tests/DateTime_sub-fall-type2-type2.phpt new file mode 100644 index 000000000..3138e1cd8 --- /dev/null +++ b/ext/date/tests/DateTime_sub-fall-type2-type2.phpt @@ -0,0 +1,53 @@ +--TEST-- +DateTime::sub() -- fall type2 type2 +--CREDITS-- +Daniel Convissor <danielc@php.net> +--XFAIL-- +Various bugs exist +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_SUB); +require 'DateTime_data-fall-type2-type2.inc'; + +?> +--EXPECT-- +test_time_fall_type2_prev_type2_prev: SUB: 2010-11-06 18:38:28 EDT - P+0Y1M2DT16H19M40S = **2010-10-04 02:18:48 EDT** +test_time_fall_type2_prev_type2_dt: SUB: 2010-11-07 00:10:20 EDT - P+0Y0M0DT5H31M52S = **2010-11-06 18:38:28 EDT** +test_time_fall_type2_prev_type2_redodt: SUB: 2010-11-07 01:12:33 EDT - P+0Y0M0DT6H34M5S = **2010-11-06 18:38:28 EDT** +test_time_fall_type2_prev_type2_redost: SUB: 2010-11-07 01:14:44 EST - P+0Y0M0DT7H36M16S = **2010-11-06 18:38:28 EDT** +test_time_fall_type2_prev_type2_st: SUB: 2010-11-07 03:16:55 EST - P+0Y0M0DT9H38M27S = **2010-11-06 18:38:28 EDT** +test_time_fall_type2_prev_type2_post: SUB: 2010-11-08 19:59:59 EST - P+0Y0M2DT1H21M31S = **2010-11-06 18:38:28 EDT** +test_time_fall_type2_dt_type2_prev: SUB: 2010-11-06 18:38:28 EDT - P-0Y0M0DT5H31M52S = **2010-11-07 00:10:20 EDT** +test_time_fall_type2_dt_type2_dt: SUB: 2010-11-07 00:15:35 EDT - P+0Y0M0DT0H5M15S = **2010-11-07 00:10:20 EDT** +test_time_fall_type2_dt_type2_redodt: SUB: 2010-11-07 01:12:33 EDT - P+0Y0M0DT1H2M13S = **2010-11-07 00:10:20 EDT** +test_time_fall_type2_dt_type2_redost: SUB: 2010-11-07 01:14:44 EST - P+0Y0M0DT2H4M24S = **2010-11-07 00:10:20 EDT** +test_time_fall_type2_dt_type2_st: SUB: 2010-11-07 03:16:55 EST - P+0Y0M0DT4H6M35S = **2010-11-07 00:10:20 EDT** +test_time_fall_type2_dt_type2_post: SUB: 2010-11-08 19:59:59 EST - P+0Y0M1DT20H49M39S = **2010-11-07 00:10:20 EDT** +test_time_fall_type2_redodt_type2_prev: SUB: 2010-11-06 18:38:28 EDT - P-0Y0M0DT6H34M5S = **2010-11-07 01:12:33 EDT** +test_time_fall_type2_redodt_type2_dt: SUB: 2010-11-07 00:10:20 EDT - P-0Y0M0DT1H2M13S = **2010-11-07 01:12:33 EDT** +test_time_fall_type2_redodt_type2_redodt: SUB: 2010-11-07 01:15:35 EDT - P+0Y0M0DT0H3M2S = **2010-11-07 01:12:33 EDT** +test_time_fall_type2_redodt_type2_redost: SUB: 2010-11-07 01:14:44 EST - P+0Y0M0DT1H2M11S = **2010-11-07 01:12:33 EDT** +test_time_fall_type2_redodt_type2_st: SUB: 2010-11-07 03:16:55 EST - P+0Y0M0DT3H4M22S = **2010-11-07 01:12:33 EDT** +test_time_fall_type2_redodt_type2_post: SUB: 2010-11-08 19:59:59 EST - P+0Y0M1DT19H47M26S = **2010-11-07 01:12:33 EDT** +test_time_fall_type2_redost_type2_prev: SUB: 2010-11-06 18:38:28 EDT - P-0Y0M0DT7H36M16S = **2010-11-07 01:14:44 EST** +test_time_fall_type2_redost_type2_dt: SUB: 2010-11-07 00:10:20 EDT - P-0Y0M0DT2H4M24S = **2010-11-07 01:14:44 EST** +test_time_fall_type2_redost_type2_redodt: SUB: 2010-11-07 01:12:33 EDT - P-0Y0M0DT1H2M11S = **2010-11-07 01:14:44 EST** +test_time_fall_type2_redost_type2_redost: SUB: 2010-11-07 01:16:54 EST - P+0Y0M0DT0H2M10S = **2010-11-07 01:14:44 EST** +test_time_fall_type2_redost_type2_st: SUB: 2010-11-07 03:16:55 EST - P+0Y0M0DT2H2M11S = **2010-11-07 01:14:44 EST** +test_time_fall_type2_redost_type2_post: SUB: 2010-11-08 19:59:59 EST - P+0Y0M1DT18H45M15S = **2010-11-07 01:14:44 EST** +test_time_fall_type2_st_type2_prev: SUB: 2010-11-06 18:38:28 EDT - P-0Y0M0DT9H38M27S = **2010-11-07 03:16:55 EST** +test_time_fall_type2_st_type2_dt: SUB: 2010-11-07 00:10:20 EDT - P-0Y0M0DT4H6M35S = **2010-11-07 03:16:55 EST** +test_time_fall_type2_st_type2_redodt: SUB: 2010-11-07 01:12:33 EDT - P-0Y0M0DT3H4M22S = **2010-11-07 03:16:55 EST** +test_time_fall_type2_st_type2_redost: SUB: 2010-11-07 01:14:44 EST - P-0Y0M0DT2H2M11S = **2010-11-07 03:16:55 EST** +test_time_fall_type2_st_type2_st: SUB: 2010-11-07 05:19:56 EST - P+0Y0M0DT2H3M1S = **2010-11-07 03:16:55 EST** +test_time_fall_type2_st_type2_post: SUB: 2010-11-08 19:59:59 EST - P+0Y0M1DT16H43M4S = **2010-11-07 03:16:55 EST** +test_time_fall_type2_post_type2_prev: SUB: 2010-11-06 18:38:28 EDT - P-0Y0M2DT1H21M31S = **2010-11-08 19:59:59 EST** +test_time_fall_type2_post_type2_dt: SUB: 2010-11-07 00:10:20 EDT - P-0Y0M1DT20H49M39S = **2010-11-08 19:59:59 EST** +test_time_fall_type2_post_type2_redodt: SUB: 2010-11-07 01:12:33 EDT - P-0Y0M1DT19H47M26S = **2010-11-08 19:59:59 EST** +test_time_fall_type2_post_type2_redost: SUB: 2010-11-07 01:14:44 EST - P-0Y0M1DT18H45M15S = **2010-11-08 19:59:59 EST** +test_time_fall_type2_post_type2_st: SUB: 2010-11-07 03:16:55 EST - P-0Y0M1DT16H43M4S = **2010-11-08 19:59:59 EST** +test_time_fall_type2_post_type2_post: SUB: 2010-11-08 19:59:59 EST - P+0Y0M0DT1H2M4S = **2010-11-08 18:57:55 EST** +test_time_fall_type2_dtsec_type2_stsec: SUB: 2010-11-07 01:00:00 EST - P+0Y0M0DT0H0M1S = **2010-11-07 01:59:59 EDT** +test_time_fall_type2_stsec_type2_dtsec: SUB: 2010-11-07 01:59:59 EDT - P-0Y0M0DT0H0M1S = **2010-11-07 01:00:00 EST** diff --git a/ext/date/tests/DateTime_sub-fall-type2-type3.phpt b/ext/date/tests/DateTime_sub-fall-type2-type3.phpt new file mode 100644 index 000000000..6c2a9e03d --- /dev/null +++ b/ext/date/tests/DateTime_sub-fall-type2-type3.phpt @@ -0,0 +1,53 @@ +--TEST-- +DateTime::sub() -- fall type2 type3 +--CREDITS-- +Daniel Convissor <danielc@php.net> +--XFAIL-- +Various bugs exist +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_SUB); +require 'DateTime_data-fall-type2-type3.inc'; + +?> +--EXPECT-- +test_time_fall_type2_prev_type3_prev: SUB: 2010-11-06 18:38:28 EDT - P+0Y1M2DT16H19M40S = **2010-10-04 02:18:48 EDT** +test_time_fall_type2_prev_type3_dt: SUB: 2010-11-07 00:10:20 EDT - P+0Y0M0DT5H31M52S = **2010-11-06 18:38:28 EDT** +test_time_fall_type2_prev_type3_redodt: SUB: 2010-11-07 01:12:33 EDT - P+0Y0M0DT6H34M5S = **2010-11-06 18:38:28 EDT** +test_time_fall_type2_prev_type3_redost: SUB: 2010-11-07 01:14:44 EST - P+0Y0M0DT7H36M16S = **2010-11-06 18:38:28 EDT** +test_time_fall_type2_prev_type3_st: SUB: 2010-11-07 03:16:55 EST - P+0Y0M0DT9H38M27S = **2010-11-06 18:38:28 EDT** +test_time_fall_type2_prev_type3_post: SUB: 2010-11-08 19:59:59 EST - P+0Y0M2DT1H21M31S = **2010-11-06 18:38:28 EDT** +test_time_fall_type2_dt_type3_prev: SUB: 2010-11-06 18:38:28 EDT - P-0Y0M0DT5H31M52S = **2010-11-07 00:10:20 EDT** +test_time_fall_type2_dt_type3_dt: SUB: 2010-11-07 00:15:35 EDT - P+0Y0M0DT0H5M15S = **2010-11-07 00:10:20 EDT** +test_time_fall_type2_dt_type3_redodt: SUB: 2010-11-07 01:12:33 EDT - P+0Y0M0DT1H2M13S = **2010-11-07 00:10:20 EDT** +test_time_fall_type2_dt_type3_redost: SUB: 2010-11-07 01:14:44 EST - P+0Y0M0DT2H4M24S = **2010-11-07 00:10:20 EDT** +test_time_fall_type2_dt_type3_st: SUB: 2010-11-07 03:16:55 EST - P+0Y0M0DT4H6M35S = **2010-11-07 00:10:20 EDT** +test_time_fall_type2_dt_type3_post: SUB: 2010-11-08 19:59:59 EST - P+0Y0M1DT20H49M39S = **2010-11-07 00:10:20 EDT** +test_time_fall_type2_redodt_type3_prev: SUB: 2010-11-06 18:38:28 EDT - P-0Y0M0DT6H34M5S = **2010-11-07 01:12:33 EDT** +test_time_fall_type2_redodt_type3_dt: SUB: 2010-11-07 00:10:20 EDT - P-0Y0M0DT1H2M13S = **2010-11-07 01:12:33 EDT** +test_time_fall_type2_redodt_type3_redodt: SUB: 2010-11-07 01:15:35 EDT - P+0Y0M0DT0H3M2S = **2010-11-07 01:12:33 EDT** +test_time_fall_type2_redodt_type3_redost: SUB: 2010-11-07 01:14:44 EST - P+0Y0M0DT1H2M11S = **2010-11-07 01:12:33 EDT** +test_time_fall_type2_redodt_type3_st: SUB: 2010-11-07 03:16:55 EST - P+0Y0M0DT3H4M22S = **2010-11-07 01:12:33 EDT** +test_time_fall_type2_redodt_type3_post: SUB: 2010-11-08 19:59:59 EST - P+0Y0M1DT19H47M26S = **2010-11-07 01:12:33 EDT** +test_time_fall_type2_redost_type3_prev: SUB: 2010-11-06 18:38:28 EDT - P-0Y0M0DT7H36M16S = **2010-11-07 01:14:44 EST** +test_time_fall_type2_redost_type3_dt: SUB: 2010-11-07 00:10:20 EDT - P-0Y0M0DT2H4M24S = **2010-11-07 01:14:44 EST** +test_time_fall_type2_redost_type3_redodt: SUB: 2010-11-07 01:12:33 EDT - P-0Y0M0DT1H2M11S = **2010-11-07 01:14:44 EST** +test_time_fall_type2_redost_type3_redost: SUB: 2010-11-07 01:16:54 EST - P+0Y0M0DT0H2M10S = **2010-11-07 01:14:44 EST** +test_time_fall_type2_redost_type3_st: SUB: 2010-11-07 03:16:55 EST - P+0Y0M0DT2H2M11S = **2010-11-07 01:14:44 EST** +test_time_fall_type2_redost_type3_post: SUB: 2010-11-08 19:59:59 EST - P+0Y0M1DT18H45M15S = **2010-11-07 01:14:44 EST** +test_time_fall_type2_st_type3_prev: SUB: 2010-11-06 18:38:28 EDT - P-0Y0M0DT9H38M27S = **2010-11-07 03:16:55 EST** +test_time_fall_type2_st_type3_dt: SUB: 2010-11-07 00:10:20 EDT - P-0Y0M0DT4H6M35S = **2010-11-07 03:16:55 EST** +test_time_fall_type2_st_type3_redodt: SUB: 2010-11-07 01:12:33 EDT - P-0Y0M0DT3H4M22S = **2010-11-07 03:16:55 EST** +test_time_fall_type2_st_type3_redost: SUB: 2010-11-07 01:14:44 EST - P-0Y0M0DT2H2M11S = **2010-11-07 03:16:55 EST** +test_time_fall_type2_st_type3_st: SUB: 2010-11-07 05:19:56 EST - P+0Y0M0DT2H3M1S = **2010-11-07 03:16:55 EST** +test_time_fall_type2_st_type3_post: SUB: 2010-11-08 19:59:59 EST - P+0Y0M1DT16H43M4S = **2010-11-07 03:16:55 EST** +test_time_fall_type2_post_type3_prev: SUB: 2010-11-06 18:38:28 EDT - P-0Y0M2DT1H21M31S = **2010-11-08 19:59:59 EST** +test_time_fall_type2_post_type3_dt: SUB: 2010-11-07 00:10:20 EDT - P-0Y0M1DT20H49M39S = **2010-11-08 19:59:59 EST** +test_time_fall_type2_post_type3_redodt: SUB: 2010-11-07 01:12:33 EDT - P-0Y0M1DT19H47M26S = **2010-11-08 19:59:59 EST** +test_time_fall_type2_post_type3_redost: SUB: 2010-11-07 01:14:44 EST - P-0Y0M1DT18H45M15S = **2010-11-08 19:59:59 EST** +test_time_fall_type2_post_type3_st: SUB: 2010-11-07 03:16:55 EST - P-0Y0M1DT16H43M4S = **2010-11-08 19:59:59 EST** +test_time_fall_type2_post_type3_post: SUB: 2010-11-08 19:59:59 EST - P+0Y0M0DT1H2M4S = **2010-11-08 18:57:55 EST** +test_time_fall_type2_dtsec_type3_stsec: SUB: 2010-11-07 01:00:00 EST - P+0Y0M0DT0H0M1S = **2010-11-07 01:59:59 EDT** +test_time_fall_type2_stsec_type3_dtsec: SUB: 2010-11-07 01:59:59 EDT - P-0Y0M0DT0H0M1S = **2010-11-07 01:00:00 EST** diff --git a/ext/date/tests/DateTime_sub-fall-type3-type2.phpt b/ext/date/tests/DateTime_sub-fall-type3-type2.phpt new file mode 100644 index 000000000..e545ea5e2 --- /dev/null +++ b/ext/date/tests/DateTime_sub-fall-type3-type2.phpt @@ -0,0 +1,53 @@ +--TEST-- +DateTime::sub() -- fall type3 type2 +--CREDITS-- +Daniel Convissor <danielc@php.net> +--XFAIL-- +Various bugs exist +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_SUB); +require 'DateTime_data-fall-type3-type2.inc'; + +?> +--EXPECT-- +test_time_fall_type3_prev_type2_prev: SUB: 2010-11-06 18:38:28 EDT - P+0Y1M2DT16H19M40S = **2010-10-04 02:18:48 EDT** +test_time_fall_type3_prev_type2_dt: SUB: 2010-11-07 00:10:20 EDT - P+0Y0M0DT5H31M52S = **2010-11-06 18:38:28 EDT** +test_time_fall_type3_prev_type2_redodt: SUB: 2010-11-07 01:12:33 EDT - P+0Y0M0DT6H34M5S = **2010-11-06 18:38:28 EDT** +test_time_fall_type3_prev_type2_redost: SUB: 2010-11-07 01:14:44 EST - P+0Y0M0DT7H36M16S = **2010-11-06 18:38:28 EDT** +test_time_fall_type3_prev_type2_st: SUB: 2010-11-07 03:16:55 EST - P+0Y0M0DT9H38M27S = **2010-11-06 18:38:28 EDT** +test_time_fall_type3_prev_type2_post: SUB: 2010-11-08 19:59:59 EST - P+0Y0M2DT1H21M31S = **2010-11-06 18:38:28 EDT** +test_time_fall_type3_dt_type2_prev: SUB: 2010-11-06 18:38:28 EDT - P-0Y0M0DT5H31M52S = **2010-11-07 00:10:20 EDT** +test_time_fall_type3_dt_type2_dt: SUB: 2010-11-07 00:15:35 EDT - P+0Y0M0DT0H5M15S = **2010-11-07 00:10:20 EDT** +test_time_fall_type3_dt_type2_redodt: SUB: 2010-11-07 01:12:33 EDT - P+0Y0M0DT1H2M13S = **2010-11-07 00:10:20 EDT** +test_time_fall_type3_dt_type2_redost: SUB: 2010-11-07 01:14:44 EST - P+0Y0M0DT2H4M24S = **2010-11-07 00:10:20 EDT** +test_time_fall_type3_dt_type2_st: SUB: 2010-11-07 03:16:55 EST - P+0Y0M0DT4H6M35S = **2010-11-07 00:10:20 EDT** +test_time_fall_type3_dt_type2_post: SUB: 2010-11-08 19:59:59 EST - P+0Y0M1DT20H49M39S = **2010-11-07 00:10:20 EDT** +test_time_fall_type3_redodt_type2_prev: SUB: 2010-11-06 18:38:28 EDT - P-0Y0M0DT6H34M5S = **2010-11-07 01:12:33 EDT** +test_time_fall_type3_redodt_type2_dt: SUB: 2010-11-07 00:10:20 EDT - P-0Y0M0DT1H2M13S = **2010-11-07 01:12:33 EDT** +test_time_fall_type3_redodt_type2_redodt: SUB: 2010-11-07 01:15:35 EDT - P+0Y0M0DT0H3M2S = **2010-11-07 01:12:33 EDT** +test_time_fall_type3_redodt_type2_redost: SUB: 2010-11-07 01:14:44 EST - P+0Y0M0DT1H2M11S = **2010-11-07 01:12:33 EDT** +test_time_fall_type3_redodt_type2_st: SUB: 2010-11-07 03:16:55 EST - P+0Y0M0DT3H4M22S = **2010-11-07 01:12:33 EDT** +test_time_fall_type3_redodt_type2_post: SUB: 2010-11-08 19:59:59 EST - P+0Y0M1DT19H47M26S = **2010-11-07 01:12:33 EDT** +test_time_fall_type3_redost_type2_prev: SUB: 2010-11-06 18:38:28 EDT - P-0Y0M0DT7H36M16S = **2010-11-07 01:14:44 EST** +test_time_fall_type3_redost_type2_dt: SUB: 2010-11-07 00:10:20 EDT - P-0Y0M0DT2H4M24S = **2010-11-07 01:14:44 EST** +test_time_fall_type3_redost_type2_redodt: SUB: 2010-11-07 01:12:33 EDT - P-0Y0M0DT1H2M11S = **2010-11-07 01:14:44 EST** +test_time_fall_type3_redost_type2_redost: SUB: 2010-11-07 01:16:54 EST - P+0Y0M0DT0H2M10S = **2010-11-07 01:14:44 EST** +test_time_fall_type3_redost_type2_st: SUB: 2010-11-07 03:16:55 EST - P+0Y0M0DT2H2M11S = **2010-11-07 01:14:44 EST** +test_time_fall_type3_redost_type2_post: SUB: 2010-11-08 19:59:59 EST - P+0Y0M1DT18H45M15S = **2010-11-07 01:14:44 EST** +test_time_fall_type3_st_type2_prev: SUB: 2010-11-06 18:38:28 EDT - P-0Y0M0DT9H38M27S = **2010-11-07 03:16:55 EST** +test_time_fall_type3_st_type2_dt: SUB: 2010-11-07 00:10:20 EDT - P-0Y0M0DT4H6M35S = **2010-11-07 03:16:55 EST** +test_time_fall_type3_st_type2_redodt: SUB: 2010-11-07 01:12:33 EDT - P-0Y0M0DT3H4M22S = **2010-11-07 03:16:55 EST** +test_time_fall_type3_st_type2_redost: SUB: 2010-11-07 01:14:44 EST - P-0Y0M0DT2H2M11S = **2010-11-07 03:16:55 EST** +test_time_fall_type3_st_type2_st: SUB: 2010-11-07 05:19:56 EST - P+0Y0M0DT2H3M1S = **2010-11-07 03:16:55 EST** +test_time_fall_type3_st_type2_post: SUB: 2010-11-08 19:59:59 EST - P+0Y0M1DT16H43M4S = **2010-11-07 03:16:55 EST** +test_time_fall_type3_post_type2_prev: SUB: 2010-11-06 18:38:28 EDT - P-0Y0M2DT1H21M31S = **2010-11-08 19:59:59 EST** +test_time_fall_type3_post_type2_dt: SUB: 2010-11-07 00:10:20 EDT - P-0Y0M1DT20H49M39S = **2010-11-08 19:59:59 EST** +test_time_fall_type3_post_type2_redodt: SUB: 2010-11-07 01:12:33 EDT - P-0Y0M1DT19H47M26S = **2010-11-08 19:59:59 EST** +test_time_fall_type3_post_type2_redost: SUB: 2010-11-07 01:14:44 EST - P-0Y0M1DT18H45M15S = **2010-11-08 19:59:59 EST** +test_time_fall_type3_post_type2_st: SUB: 2010-11-07 03:16:55 EST - P-0Y0M1DT16H43M4S = **2010-11-08 19:59:59 EST** +test_time_fall_type3_post_type2_post: SUB: 2010-11-08 19:59:59 EST - P+0Y0M0DT1H2M4S = **2010-11-08 18:57:55 EST** +test_time_fall_type3_dtsec_type2_stsec: SUB: 2010-11-07 01:00:00 EST - P+0Y0M0DT0H0M1S = **2010-11-07 01:59:59 EDT** +test_time_fall_type3_stsec_type2_dtsec: SUB: 2010-11-07 01:59:59 EDT - P-0Y0M0DT0H0M1S = **2010-11-07 01:00:00 EST** diff --git a/ext/date/tests/DateTime_sub-fall-type3-type3.phpt b/ext/date/tests/DateTime_sub-fall-type3-type3.phpt new file mode 100644 index 000000000..6448b5d84 --- /dev/null +++ b/ext/date/tests/DateTime_sub-fall-type3-type3.phpt @@ -0,0 +1,53 @@ +--TEST-- +DateTime::sub() -- fall type3 type3 +--CREDITS-- +Daniel Convissor <danielc@php.net> +--XFAIL-- +Various bugs exist +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_SUB); +require 'DateTime_data-fall-type3-type3.inc'; + +?> +--EXPECT-- +test_time_fall_type3_prev_type3_prev: SUB: 2010-11-06 18:38:28 EDT - P+0Y1M2DT16H19M40S = **2010-10-04 02:18:48 EDT** +test_time_fall_type3_prev_type3_dt: SUB: 2010-11-07 00:10:20 EDT - P+0Y0M0DT5H31M52S = **2010-11-06 18:38:28 EDT** +test_time_fall_type3_prev_type3_redodt: SUB: 2010-11-07 01:12:33 EDT - P+0Y0M0DT6H34M5S = **2010-11-06 18:38:28 EDT** +test_time_fall_type3_prev_type3_redost: SUB: 2010-11-07 01:14:44 EST - P+0Y0M0DT7H36M16S = **2010-11-06 18:38:28 EDT** +test_time_fall_type3_prev_type3_st: SUB: 2010-11-07 03:16:55 EST - P+0Y0M0DT9H38M27S = **2010-11-06 18:38:28 EDT** +test_time_fall_type3_prev_type3_post: SUB: 2010-11-08 19:59:59 EST - P+0Y0M2DT1H21M31S = **2010-11-06 18:38:28 EDT** +test_time_fall_type3_dt_type3_prev: SUB: 2010-11-06 18:38:28 EDT - P-0Y0M0DT5H31M52S = **2010-11-07 00:10:20 EDT** +test_time_fall_type3_dt_type3_dt: SUB: 2010-11-07 00:15:35 EDT - P+0Y0M0DT0H5M15S = **2010-11-07 00:10:20 EDT** +test_time_fall_type3_dt_type3_redodt: SUB: 2010-11-07 01:12:33 EDT - P+0Y0M0DT1H2M13S = **2010-11-07 00:10:20 EDT** +test_time_fall_type3_dt_type3_redost: SUB: 2010-11-07 01:14:44 EST - P+0Y0M0DT2H4M24S = **2010-11-07 00:10:20 EDT** +test_time_fall_type3_dt_type3_st: SUB: 2010-11-07 03:16:55 EST - P+0Y0M0DT4H6M35S = **2010-11-07 00:10:20 EDT** +test_time_fall_type3_dt_type3_post: SUB: 2010-11-08 19:59:59 EST - P+0Y0M1DT20H49M39S = **2010-11-07 00:10:20 EDT** +test_time_fall_type3_redodt_type3_prev: SUB: 2010-11-06 18:38:28 EDT - P-0Y0M0DT6H34M5S = **2010-11-07 01:12:33 EDT** +test_time_fall_type3_redodt_type3_dt: SUB: 2010-11-07 00:10:20 EDT - P-0Y0M0DT1H2M13S = **2010-11-07 01:12:33 EDT** +test_time_fall_type3_redodt_type3_redodt: SUB: 2010-11-07 01:15:35 EDT - P+0Y0M0DT0H3M2S = **2010-11-07 01:12:33 EDT** +test_time_fall_type3_redodt_type3_redost: SUB: 2010-11-07 01:14:44 EST - P+0Y0M0DT1H2M11S = **2010-11-07 01:12:33 EDT** +test_time_fall_type3_redodt_type3_st: SUB: 2010-11-07 03:16:55 EST - P+0Y0M0DT3H4M22S = **2010-11-07 01:12:33 EDT** +test_time_fall_type3_redodt_type3_post: SUB: 2010-11-08 19:59:59 EST - P+0Y0M1DT19H47M26S = **2010-11-07 01:12:33 EDT** +test_time_fall_type3_redost_type3_prev: SUB: 2010-11-06 18:38:28 EDT - P-0Y0M0DT7H36M16S = **2010-11-07 01:14:44 EST** +test_time_fall_type3_redost_type3_dt: SUB: 2010-11-07 00:10:20 EDT - P-0Y0M0DT2H4M24S = **2010-11-07 01:14:44 EST** +test_time_fall_type3_redost_type3_redodt: SUB: 2010-11-07 01:12:33 EDT - P-0Y0M0DT1H2M11S = **2010-11-07 01:14:44 EST** +test_time_fall_type3_redost_type3_redost: SUB: 2010-11-07 01:16:54 EST - P+0Y0M0DT0H2M10S = **2010-11-07 01:14:44 EST** +test_time_fall_type3_redost_type3_st: SUB: 2010-11-07 03:16:55 EST - P+0Y0M0DT2H2M11S = **2010-11-07 01:14:44 EST** +test_time_fall_type3_redost_type3_post: SUB: 2010-11-08 19:59:59 EST - P+0Y0M1DT18H45M15S = **2010-11-07 01:14:44 EST** +test_time_fall_type3_st_type3_prev: SUB: 2010-11-06 18:38:28 EDT - P-0Y0M0DT9H38M27S = **2010-11-07 03:16:55 EST** +test_time_fall_type3_st_type3_dt: SUB: 2010-11-07 00:10:20 EDT - P-0Y0M0DT4H6M35S = **2010-11-07 03:16:55 EST** +test_time_fall_type3_st_type3_redodt: SUB: 2010-11-07 01:12:33 EDT - P-0Y0M0DT3H4M22S = **2010-11-07 03:16:55 EST** +test_time_fall_type3_st_type3_redost: SUB: 2010-11-07 01:14:44 EST - P-0Y0M0DT2H2M11S = **2010-11-07 03:16:55 EST** +test_time_fall_type3_st_type3_st: SUB: 2010-11-07 05:19:56 EST - P+0Y0M0DT2H3M1S = **2010-11-07 03:16:55 EST** +test_time_fall_type3_st_type3_post: SUB: 2010-11-08 19:59:59 EST - P+0Y0M1DT16H43M4S = **2010-11-07 03:16:55 EST** +test_time_fall_type3_post_type3_prev: SUB: 2010-11-06 18:38:28 EDT - P-0Y0M2DT1H21M31S = **2010-11-08 19:59:59 EST** +test_time_fall_type3_post_type3_dt: SUB: 2010-11-07 00:10:20 EDT - P-0Y0M1DT20H49M39S = **2010-11-08 19:59:59 EST** +test_time_fall_type3_post_type3_redodt: SUB: 2010-11-07 01:12:33 EDT - P-0Y0M1DT19H47M26S = **2010-11-08 19:59:59 EST** +test_time_fall_type3_post_type3_redost: SUB: 2010-11-07 01:14:44 EST - P-0Y0M1DT18H45M15S = **2010-11-08 19:59:59 EST** +test_time_fall_type3_post_type3_st: SUB: 2010-11-07 03:16:55 EST - P-0Y0M1DT16H43M4S = **2010-11-08 19:59:59 EST** +test_time_fall_type3_post_type3_post: SUB: 2010-11-08 19:59:59 EST - P+0Y0M0DT1H2M4S = **2010-11-08 18:57:55 EST** +test_time_fall_type3_dtsec_type3_stsec: SUB: 2010-11-07 01:00:00 EST - P+0Y0M0DT0H0M1S = **2010-11-07 01:59:59 EDT** +test_time_fall_type3_stsec_type3_dtsec: SUB: 2010-11-07 01:59:59 EDT - P-0Y0M0DT0H0M1S = **2010-11-07 01:00:00 EST** diff --git a/ext/date/tests/DateTime_sub-february.phpt b/ext/date/tests/DateTime_sub-february.phpt new file mode 100644 index 000000000..89cba4bde --- /dev/null +++ b/ext/date/tests/DateTime_sub-february.phpt @@ -0,0 +1,77 @@ +--TEST-- +DateTime::sub() -- february +--CREDITS-- +Daniel Convissor <danielc@php.net> +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_SUB); +require 'DateTime_data-february.inc'; + +?> +--EXPECT-- +test_bug_49081__1: SUB: 2010-03-31 00:00:00 EDT - P+0Y0M30DT0H0M0S = **2010-03-01 00:00:00 EST** +test_bug_49081__2: SUB: 2010-04-01 00:00:00 EDT - P+0Y1M0DT0H0M0S = **2010-03-01 00:00:00 EST** +test_bug_49081__3: SUB: 2010-04-01 00:00:00 EDT - P+0Y0M1DT0H0M0S = **2010-03-31 00:00:00 EDT** +test_bug_49081__4: SUB: 2010-04-29 00:00:00 EDT - P+0Y0M29DT0H0M0S = **2010-03-31 00:00:00 EDT** +test_bug_49081__5: SUB: 2010-04-30 00:00:00 EDT - P+0Y0M30DT0H0M0S = **2010-03-31 00:00:00 EDT** +test_bug_49081__6: SUB: 2010-04-30 00:00:00 EDT - P+0Y1M0DT0H0M0S = **2010-03-30 00:00:00 EDT** +test_bug_49081__7: SUB: 2010-04-30 00:00:00 EDT - P+0Y1M1DT0H0M0S = **2010-03-29 00:00:00 EDT** +test_bug_49081__8: SUB: 2010-01-29 00:00:00 EST - P+0Y0M28DT0H0M0S = **2010-01-01 00:00:00 EST** +test_bug_49081__9: SUB: 2010-01-30 00:00:00 EST - P+0Y0M29DT0H0M0S = **2010-01-01 00:00:00 EST** +test_bug_49081__10: SUB: 2010-01-31 00:00:00 EST - P+0Y0M30DT0H0M0S = **2010-01-01 00:00:00 EST** +test_bug_49081__11: SUB: 2010-02-01 00:00:00 EST - P+0Y1M0DT0H0M0S = **2010-01-01 00:00:00 EST** +test_bug_49081__12: SUB: 2010-02-01 00:00:00 EST - P+0Y0M1DT0H0M0S = **2010-01-31 00:00:00 EST** +test_bug_49081__13: SUB: 2010-02-27 00:00:00 EST - P+0Y0M27DT0H0M0S = **2010-01-31 00:00:00 EST** +test_bug_49081__14: SUB: 2010-02-28 00:00:00 EST - P+0Y0M28DT0H0M0S = **2010-01-31 00:00:00 EST** +test_bug_49081__15: SUB: 2010-02-28 00:00:00 EST - P+0Y0M29DT0H0M0S = **2010-01-30 00:00:00 EST** +test_bug_49081__16: SUB: 2010-02-28 00:00:00 EST - P+0Y0M30DT0H0M0S = **2010-01-29 00:00:00 EST** +test_bug_49081__17: SUB: 2010-02-28 00:00:00 EST - P+0Y1M0DT0H0M0S = **2010-01-28 00:00:00 EST** +test_bug_49081__18: SUB: 2010-02-28 00:00:00 EST - P+0Y1M1DT0H0M0S = **2010-01-27 00:00:00 EST** +test_bug_49081__19: SUB: 2010-03-01 00:00:00 EST - P+0Y2M0DT0H0M0S = **2010-01-01 00:00:00 EST** +test_bug_49081__20: SUB: 2010-03-01 00:00:00 EST - P+0Y0M29DT0H0M0S = **2010-01-31 00:00:00 EST** +test_bug_49081__21: SUB: 2010-03-27 00:00:00 EDT - P+0Y1M24DT0H0M0S = **2010-01-31 00:00:00 EST** +test_bug_49081__22: SUB: 2010-03-28 00:00:00 EDT - P+0Y1M25DT0H0M0S = **2010-01-31 00:00:00 EST** +test_bug_49081__23: SUB: 2010-03-29 00:00:00 EDT - P+0Y1M26DT0H0M0S = **2010-01-31 00:00:00 EST** +test_bug_49081__24: SUB: 2010-03-30 00:00:00 EDT - P+0Y1M27DT0H0M0S = **2010-01-31 00:00:00 EST** +test_bug_49081__25: SUB: 2010-03-31 00:00:00 EDT - P+0Y2M0DT0H0M0S = **2010-01-31 00:00:00 EST** +test_bug_49081__26: SUB: 2010-03-31 00:00:00 EDT - P+0Y2M1DT0H0M0S = **2010-01-30 00:00:00 EST** +test_bug_49081__27: SUB: 2009-01-31 00:00:00 EST - P+0Y0M30DT0H0M0S = **2009-01-01 00:00:00 EST** +test_bug_49081__28: SUB: 2010-03-27 00:00:00 EDT - P+0Y0M27DT0H0M0S = **2010-02-28 00:00:00 EST** +test_bug_49081__29: SUB: 2010-03-28 00:00:00 EDT - P+0Y1M0DT0H0M0S = **2010-02-28 00:00:00 EST** +test_bug_49081__30: SUB: 2010-03-29 00:00:00 EDT - P+0Y1M1DT0H0M0S = **2010-02-28 00:00:00 EST** +test_bug_49081__31: SUB: 2010-03-27 00:00:00 EDT - P+0Y1M0DT0H0M0S = **2010-02-27 00:00:00 EST** +test_bug_49081__32: SUB: 2010-03-27 00:00:00 EDT - P+0Y1M1DT0H0M0S = **2010-02-26 00:00:00 EST** +test_bug_49081_negative__1: SUB: 2010-03-01 00:00:00 EST - P-0Y0M30DT0H0M0S = **2010-03-31 00:00:00 EDT** +test_bug_49081_negative__2: SUB: 2010-03-01 00:00:00 EST - P-0Y1M0DT0H0M0S = **2010-04-01 00:00:00 EDT** +test_bug_49081_negative__3: SUB: 2010-03-31 00:00:00 EDT - P-0Y0M1DT0H0M0S = **2010-04-01 00:00:00 EDT** +test_bug_49081_negative__4: SUB: 2010-03-31 00:00:00 EDT - P-0Y0M29DT0H0M0S = **2010-04-29 00:00:00 EDT** +test_bug_49081_negative__5: SUB: 2010-03-31 00:00:00 EDT - P-0Y0M30DT0H0M0S = **2010-04-30 00:00:00 EDT** +test_bug_49081_negative__6: SUB: 2010-03-30 00:00:00 EDT - P-0Y1M0DT0H0M0S = **2010-04-30 00:00:00 EDT** +test_bug_49081_negative__7: SUB: 2010-03-29 00:00:00 EDT - P-0Y1M1DT0H0M0S = **2010-04-30 00:00:00 EDT** +test_bug_49081_negative__8: SUB: 2010-01-01 00:00:00 EST - P-0Y0M28DT0H0M0S = **2010-01-29 00:00:00 EST** +test_bug_49081_negative__9: SUB: 2010-01-01 00:00:00 EST - P-0Y0M29DT0H0M0S = **2010-01-30 00:00:00 EST** +test_bug_49081_negative__10: SUB: 2010-01-01 00:00:00 EST - P-0Y0M30DT0H0M0S = **2010-01-31 00:00:00 EST** +test_bug_49081_negative__11: SUB: 2010-01-01 00:00:00 EST - P-0Y1M0DT0H0M0S = **2010-02-01 00:00:00 EST** +test_bug_49081_negative__12: SUB: 2010-01-31 00:00:00 EST - P-0Y0M1DT0H0M0S = **2010-02-01 00:00:00 EST** +test_bug_49081_negative__13: SUB: 2010-01-31 00:00:00 EST - P-0Y0M27DT0H0M0S = **2010-02-27 00:00:00 EST** +test_bug_49081_negative__14: SUB: 2010-01-31 00:00:00 EST - P-0Y0M28DT0H0M0S = **2010-02-28 00:00:00 EST** +test_bug_49081_negative__15: SUB: 2010-01-30 00:00:00 EST - P-0Y0M29DT0H0M0S = **2010-02-28 00:00:00 EST** +test_bug_49081_negative__16: SUB: 2010-01-29 00:00:00 EST - P-0Y0M30DT0H0M0S = **2010-02-28 00:00:00 EST** +test_bug_49081_negative__17: SUB: 2010-01-28 00:00:00 EST - P-0Y1M0DT0H0M0S = **2010-02-28 00:00:00 EST** +test_bug_49081_negative__18: SUB: 2010-01-27 00:00:00 EST - P-0Y1M1DT0H0M0S = **2010-02-28 00:00:00 EST** +test_bug_49081_negative__19: SUB: 2010-01-01 00:00:00 EST - P-0Y2M0DT0H0M0S = **2010-03-01 00:00:00 EST** +test_bug_49081_negative__20: SUB: 2010-01-31 00:00:00 EST - P-0Y1M1DT0H0M0S = **2010-03-01 00:00:00 EST** +test_bug_49081_negative__21: SUB: 2010-01-31 00:00:00 EST - P-0Y1M27DT0H0M0S = **2010-03-27 00:00:00 EDT** +test_bug_49081_negative__22: SUB: 2010-01-31 00:00:00 EST - P-0Y1M28DT0H0M0S = **2010-03-28 00:00:00 EDT** +test_bug_49081_negative__23: SUB: 2010-01-31 00:00:00 EST - P-0Y1M29DT0H0M0S = **2010-03-29 00:00:00 EDT** +test_bug_49081_negative__24: SUB: 2010-01-31 00:00:00 EST - P-0Y1M30DT0H0M0S = **2010-03-30 00:00:00 EDT** +test_bug_49081_negative__25: SUB: 2010-01-31 00:00:00 EST - P-0Y2M0DT0H0M0S = **2010-03-31 00:00:00 EDT** +test_bug_49081_negative__26: SUB: 2010-01-30 00:00:00 EST - P-0Y2M1DT0H0M0S = **2010-03-31 00:00:00 EDT** +test_bug_49081_negative__27: SUB: 2009-01-01 00:00:00 EST - P-0Y0M30DT0H0M0S = **2009-01-31 00:00:00 EST** +test_bug_49081_negative__28: SUB: 2010-02-28 00:00:00 EST - P-0Y0M27DT0H0M0S = **2010-03-27 00:00:00 EDT** +test_bug_49081_negative__29: SUB: 2010-02-28 00:00:00 EST - P-0Y1M0DT0H0M0S = **2010-03-28 00:00:00 EDT** +test_bug_49081_negative__30: SUB: 2010-02-28 00:00:00 EST - P-0Y1M1DT0H0M0S = **2010-03-29 00:00:00 EDT** +test_bug_49081_negative__31: SUB: 2010-02-27 00:00:00 EST - P-0Y1M0DT0H0M0S = **2010-03-27 00:00:00 EDT** +test_bug_49081_negative__32: SUB: 2010-02-26 00:00:00 EST - P-0Y1M1DT0H0M0S = **2010-03-27 00:00:00 EDT** diff --git a/ext/date/tests/DateTime_sub-massive.phpt b/ext/date/tests/DateTime_sub-massive.phpt new file mode 100644 index 000000000..a0520ecdd --- /dev/null +++ b/ext/date/tests/DateTime_sub-massive.phpt @@ -0,0 +1,15 @@ +--TEST-- +DateTime::sub() -- massive +--CREDITS-- +Daniel Convissor <danielc@php.net> +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_SUB); +require 'DateTime_data-massive.inc'; + +?> +--EXPECT-- +test_massive_positive: SUB: 333333-01-01 16:18:02 EST - P+666666Y0M0DT0H0M0S = **-333333-01-01 16:18:02 EST** +test_massive_negative: SUB: -333333-01-01 16:18:02 EST - P-666666Y0M0DT0H0M0S = **333333-01-01 16:18:02 EST** diff --git a/ext/date/tests/DateTime_sub-spring-type2-type2.phpt b/ext/date/tests/DateTime_sub-spring-type2-type2.phpt new file mode 100644 index 000000000..2b1817d79 --- /dev/null +++ b/ext/date/tests/DateTime_sub-spring-type2-type2.phpt @@ -0,0 +1,33 @@ +--TEST-- +DateTime::sub() -- spring type2 type2 +--CREDITS-- +Daniel Convissor <danielc@php.net> +--XFAIL-- +Various bugs exist +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_SUB); +require 'DateTime_data-spring-type2-type2.inc'; + +?> +--EXPECT-- +test_time_spring_type2_prev_type2_prev: SUB: 2010-03-13 18:38:28 EST - P+0Y1M2DT16H19M40S = **2010-02-11 02:18:48 EST** +test_time_spring_type2_prev_type2_st: SUB: 2010-03-14 00:10:20 EST - P+0Y0M0DT5H31M52S = **2010-03-13 18:38:28 EST** +test_time_spring_type2_prev_type2_dt: SUB: 2010-03-14 03:16:55 EDT - P+0Y0M0DT7H38M27S = **2010-03-13 18:38:28 EST** +test_time_spring_type2_prev_type2_post: SUB: 2010-03-15 19:59:59 EDT - P+0Y0M2DT1H21M31S = **2010-03-13 18:38:28 EST** +test_time_spring_type2_st_type2_prev: SUB: 2010-03-13 18:38:28 EST - P-0Y0M0DT5H31M52S = **2010-03-14 00:10:20 EST** +test_time_spring_type2_st_type2_st: SUB: 2010-03-14 00:15:35 EST - P+0Y0M0DT0H5M15S = **2010-03-14 00:10:20 EST** +test_time_spring_type2_st_type2_dt: SUB: 2010-03-14 03:16:55 EDT - P+0Y0M0DT2H6M35S = **2010-03-14 00:10:20 EST** +test_time_spring_type2_st_type2_post: SUB: 2010-03-15 19:59:59 EDT - P+0Y0M1DT18H49M39S = **2010-03-14 00:10:20 EST** +test_time_spring_type2_dt_type2_prev: SUB: 2010-03-13 18:38:28 EST - P-0Y0M0DT7H38M27S = **2010-03-14 03:16:55 EDT** +test_time_spring_type2_dt_type2_st: SUB: 2010-03-14 00:10:20 EST - P-0Y0M0DT2H6M35S = **2010-03-14 03:16:55 EDT** +test_time_spring_type2_dt_type2_dt: SUB: 2010-03-14 05:19:56 EDT - P+0Y0M0DT2H3M1S = **2010-03-14 03:16:55 EDT** +test_time_spring_type2_dt_type2_post: SUB: 2010-03-15 19:59:59 EDT - P+0Y0M1DT16H43M4S = **2010-03-14 03:16:55 EDT** +test_time_spring_type2_post_type2_prev: SUB: 2010-03-13 18:38:28 EST - P-0Y0M2DT1H21M31S = **2010-03-15 19:59:59 EDT** +test_time_spring_type2_post_type2_st: SUB: 2010-03-14 00:10:20 EST - P-0Y0M1DT18H49M39S = **2010-03-15 19:59:59 EDT** +test_time_spring_type2_post_type2_dt: SUB: 2010-03-14 03:16:55 EDT - P-0Y0M1DT16H43M4S = **2010-03-15 19:59:59 EDT** +test_time_spring_type2_post_type2_post: SUB: 2010-03-15 19:59:59 EDT - P+0Y0M0DT1H2M4S = **2010-03-15 18:57:55 EDT** +test_time_spring_type2_stsec_type2_dtsec: SUB: 2010-03-15 03:00:00 EDT - P+0Y0M0DT0H0M1S = **2010-03-13 01:59:59 EST** +test_time_spring_type2_dtsec_type2_stsec: SUB: 2010-03-15 01:59:59 EST - P-0Y0M0DT0H0M1S = **2010-03-15 03:00:00 EDT** diff --git a/ext/date/tests/DateTime_sub-spring-type2-type3.phpt b/ext/date/tests/DateTime_sub-spring-type2-type3.phpt new file mode 100644 index 000000000..a5c43df91 --- /dev/null +++ b/ext/date/tests/DateTime_sub-spring-type2-type3.phpt @@ -0,0 +1,33 @@ +--TEST-- +DateTime::sub() -- spring type2 type3 +--CREDITS-- +Daniel Convissor <danielc@php.net> +--XFAIL-- +Various bugs exist +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_SUB); +require 'DateTime_data-spring-type2-type3.inc'; + +?> +--EXPECT-- +test_time_spring_type2_prev_type3_prev: SUB: 2010-03-13 18:38:28 EST - P+0Y1M2DT16H19M40S = **2010-02-11 02:18:48 EST** +test_time_spring_type2_prev_type3_st: SUB: 2010-03-14 00:10:20 EST - P+0Y0M0DT5H31M52S = **2010-03-13 18:38:28 EST** +test_time_spring_type2_prev_type3_dt: SUB: 2010-03-14 03:16:55 EDT - P+0Y0M0DT7H38M27S = **2010-03-13 18:38:28 EST** +test_time_spring_type2_prev_type3_post: SUB: 2010-03-15 19:59:59 EDT - P+0Y0M2DT1H21M31S = **2010-03-13 18:38:28 EST** +test_time_spring_type2_st_type3_prev: SUB: 2010-03-13 18:38:28 EST - P-0Y0M0DT5H31M52S = **2010-03-14 00:10:20 EST** +test_time_spring_type2_st_type3_st: SUB: 2010-03-14 00:15:35 EST - P+0Y0M0DT0H5M15S = **2010-03-14 00:10:20 EST** +test_time_spring_type2_st_type3_dt: SUB: 2010-03-14 03:16:55 EDT - P+0Y0M0DT2H6M35S = **2010-03-14 00:10:20 EST** +test_time_spring_type2_st_type3_post: SUB: 2010-03-15 19:59:59 EDT - P+0Y0M1DT18H49M39S = **2010-03-14 00:10:20 EST** +test_time_spring_type2_dt_type3_prev: SUB: 2010-03-13 18:38:28 EST - P-0Y0M0DT7H38M27S = **2010-03-14 03:16:55 EDT** +test_time_spring_type2_dt_type3_st: SUB: 2010-03-14 00:10:20 EST - P-0Y0M0DT2H6M35S = **2010-03-14 03:16:55 EDT** +test_time_spring_type2_dt_type3_dt: SUB: 2010-03-14 05:19:56 EDT - P+0Y0M0DT2H3M1S = **2010-03-14 03:16:55 EDT** +test_time_spring_type2_dt_type3_post: SUB: 2010-03-15 19:59:59 EDT - P+0Y0M1DT16H43M4S = **2010-03-14 03:16:55 EDT** +test_time_spring_type2_post_type3_prev: SUB: 2010-03-13 18:38:28 EST - P-0Y0M2DT1H21M31S = **2010-03-15 19:59:59 EDT** +test_time_spring_type2_post_type3_st: SUB: 2010-03-14 00:10:20 EST - P-0Y0M1DT18H49M39S = **2010-03-15 19:59:59 EDT** +test_time_spring_type2_post_type3_dt: SUB: 2010-03-14 03:16:55 EDT - P-0Y0M1DT16H43M4S = **2010-03-15 19:59:59 EDT** +test_time_spring_type2_post_type3_post: SUB: 2010-03-15 19:59:59 EDT - P+0Y0M0DT1H2M4S = **2010-03-15 18:57:55 EDT** +test_time_spring_type2_stsec_type3_dtsec: SUB: 2010-03-15 03:00:00 EDT - P+0Y0M0DT0H0M1S = **2010-03-13 01:59:59 EST** +test_time_spring_type2_dtsec_type3_stsec: SUB: 2010-03-15 01:59:59 EST - P-0Y0M0DT0H0M1S = **2010-03-15 03:00:00 EDT** diff --git a/ext/date/tests/DateTime_sub-spring-type3-type2.phpt b/ext/date/tests/DateTime_sub-spring-type3-type2.phpt new file mode 100644 index 000000000..bcbbe25c7 --- /dev/null +++ b/ext/date/tests/DateTime_sub-spring-type3-type2.phpt @@ -0,0 +1,33 @@ +--TEST-- +DateTime::sub() -- spring type3 type2 +--CREDITS-- +Daniel Convissor <danielc@php.net> +--XFAIL-- +Various bugs exist +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_SUB); +require 'DateTime_data-spring-type3-type2.inc'; + +?> +--EXPECT-- +test_time_spring_type3_prev_type2_prev: SUB: 2010-03-13 18:38:28 EST - P+0Y1M2DT16H19M40S = **2010-02-11 02:18:48 EST** +test_time_spring_type3_prev_type2_st: SUB: 2010-03-14 00:10:20 EST - P+0Y0M0DT5H31M52S = **2010-03-13 18:38:28 EST** +test_time_spring_type3_prev_type2_dt: SUB: 2010-03-14 03:16:55 EDT - P+0Y0M0DT7H38M27S = **2010-03-13 18:38:28 EST** +test_time_spring_type3_prev_type2_post: SUB: 2010-03-15 19:59:59 EDT - P+0Y0M2DT1H21M31S = **2010-03-13 18:38:28 EST** +test_time_spring_type3_st_type2_prev: SUB: 2010-03-13 18:38:28 EST - P-0Y0M0DT5H31M52S = **2010-03-14 00:10:20 EST** +test_time_spring_type3_st_type2_st: SUB: 2010-03-14 00:15:35 EST - P+0Y0M0DT0H5M15S = **2010-03-14 00:10:20 EST** +test_time_spring_type3_st_type2_dt: SUB: 2010-03-14 03:16:55 EDT - P+0Y0M0DT2H6M35S = **2010-03-14 00:10:20 EST** +test_time_spring_type3_st_type2_post: SUB: 2010-03-15 19:59:59 EDT - P+0Y0M1DT18H49M39S = **2010-03-14 00:10:20 EST** +test_time_spring_type3_dt_type2_prev: SUB: 2010-03-13 18:38:28 EST - P-0Y0M0DT7H38M27S = **2010-03-14 03:16:55 EDT** +test_time_spring_type3_dt_type2_st: SUB: 2010-03-14 00:10:20 EST - P-0Y0M0DT2H6M35S = **2010-03-14 03:16:55 EDT** +test_time_spring_type3_dt_type2_dt: SUB: 2010-03-14 05:19:56 EDT - P+0Y0M0DT2H3M1S = **2010-03-14 03:16:55 EDT** +test_time_spring_type3_dt_type2_post: SUB: 2010-03-15 19:59:59 EDT - P+0Y0M1DT16H43M4S = **2010-03-14 03:16:55 EDT** +test_time_spring_type3_post_type2_prev: SUB: 2010-03-13 18:38:28 EST - P-0Y0M2DT1H21M31S = **2010-03-15 19:59:59 EDT** +test_time_spring_type3_post_type2_st: SUB: 2010-03-14 00:10:20 EST - P-0Y0M1DT18H49M39S = **2010-03-15 19:59:59 EDT** +test_time_spring_type3_post_type2_dt: SUB: 2010-03-14 03:16:55 EDT - P-0Y0M1DT16H43M4S = **2010-03-15 19:59:59 EDT** +test_time_spring_type3_post_type2_post: SUB: 2010-03-15 19:59:59 EDT - P+0Y0M0DT1H2M4S = **2010-03-15 18:57:55 EDT** +test_time_spring_type3_stsec_type2_dtsec: SUB: 2010-03-15 03:00:00 EDT - P+0Y0M0DT0H0M1S = **2010-03-13 01:59:59 EST** +test_time_spring_type3_dtsec_type2_stsec: SUB: 2010-03-15 01:59:59 EST - P-0Y0M0DT0H0M1S = **2010-03-15 03:00:00 EDT** diff --git a/ext/date/tests/DateTime_sub-spring-type3-type3.phpt b/ext/date/tests/DateTime_sub-spring-type3-type3.phpt new file mode 100644 index 000000000..2ed190f74 --- /dev/null +++ b/ext/date/tests/DateTime_sub-spring-type3-type3.phpt @@ -0,0 +1,33 @@ +--TEST-- +DateTime::sub() -- spring type3 type3 +--CREDITS-- +Daniel Convissor <danielc@php.net> +--XFAIL-- +Various bugs exist +--FILE-- +<?php + +require 'examine_diff.inc'; +define('PHPT_DATETIME_SHOW', PHPT_DATETIME_SHOW_SUB); +require 'DateTime_data-spring-type3-type3.inc'; + +?> +--EXPECT-- +test_time_spring_type3_prev_type3_prev: SUB: 2010-03-13 18:38:28 EST - P+0Y1M2DT16H19M40S = **2010-02-11 02:18:48 EST** +test_time_spring_type3_prev_type3_st: SUB: 2010-03-14 00:10:20 EST - P+0Y0M0DT5H31M52S = **2010-03-13 18:38:28 EST** +test_time_spring_type3_prev_type3_dt: SUB: 2010-03-14 03:16:55 EDT - P+0Y0M0DT7H38M27S = **2010-03-13 18:38:28 EST** +test_time_spring_type3_prev_type3_post: SUB: 2010-03-15 19:59:59 EDT - P+0Y0M2DT1H21M31S = **2010-03-13 18:38:28 EST** +test_time_spring_type3_st_type3_prev: SUB: 2010-03-13 18:38:28 EST - P-0Y0M0DT5H31M52S = **2010-03-14 00:10:20 EST** +test_time_spring_type3_st_type3_st: SUB: 2010-03-14 00:15:35 EST - P+0Y0M0DT0H5M15S = **2010-03-14 00:10:20 EST** +test_time_spring_type3_st_type3_dt: SUB: 2010-03-14 03:16:55 EDT - P+0Y0M0DT2H6M35S = **2010-03-14 00:10:20 EST** +test_time_spring_type3_st_type3_post: SUB: 2010-03-15 19:59:59 EDT - P+0Y0M1DT18H49M39S = **2010-03-14 00:10:20 EST** +test_time_spring_type3_dt_type3_prev: SUB: 2010-03-13 18:38:28 EST - P-0Y0M0DT7H38M27S = **2010-03-14 03:16:55 EDT** +test_time_spring_type3_dt_type3_st: SUB: 2010-03-14 00:10:20 EST - P-0Y0M0DT2H6M35S = **2010-03-14 03:16:55 EDT** +test_time_spring_type3_dt_type3_dt: SUB: 2010-03-14 05:19:56 EDT - P+0Y0M0DT2H3M1S = **2010-03-14 03:16:55 EDT** +test_time_spring_type3_dt_type3_post: SUB: 2010-03-15 19:59:59 EDT - P+0Y0M1DT16H43M4S = **2010-03-14 03:16:55 EDT** +test_time_spring_type3_post_type3_prev: SUB: 2010-03-13 18:38:28 EST - P-0Y0M2DT1H21M31S = **2010-03-15 19:59:59 EDT** +test_time_spring_type3_post_type3_st: SUB: 2010-03-14 00:10:20 EST - P-0Y0M1DT18H49M39S = **2010-03-15 19:59:59 EDT** +test_time_spring_type3_post_type3_dt: SUB: 2010-03-14 03:16:55 EDT - P-0Y0M1DT16H43M4S = **2010-03-15 19:59:59 EDT** +test_time_spring_type3_post_type3_post: SUB: 2010-03-15 19:59:59 EDT - P+0Y0M0DT1H2M4S = **2010-03-15 18:57:55 EDT** +test_time_spring_type3_stsec_type3_dtsec: SUB: 2010-03-15 03:00:00 EDT - P+0Y0M0DT0H0M1S = **2010-03-13 01:59:59 EST** +test_time_spring_type3_dtsec_type3_stsec: SUB: 2010-03-15 01:59:59 EST - P-0Y0M0DT0H0M1S = **2010-03-15 03:00:00 EDT** diff --git a/ext/date/tests/bug48187.phpt b/ext/date/tests/bug48187.phpt index 24a295ddd..78c0fb2a6 100644 --- a/ext/date/tests/bug48187.phpt +++ b/ext/date/tests/bug48187.phpt @@ -2,6 +2,8 @@ Bug #48187 (DateTime::diff() corrupting microtime() result) --FILE-- <?php +date_default_timezone_set('UTC'); + // two arbitrary dates $date1 = new DateTime('2005-07-23'); $date2 = new DateTime('2006-02-14'); diff --git a/ext/date/tests/bug51819.phpt b/ext/date/tests/bug51819.phpt index afcb9c7d4..37cab2055 100644 --- a/ext/date/tests/bug51819.phpt +++ b/ext/date/tests/bug51819.phpt @@ -2,6 +2,8 @@ Bug #51819 (Case discrepancy in timezone names cause Uncaught exception and fatal error) --FILE-- <?php +date_default_timezone_set('UTC'); + $aTzAbbr = timezone_abbreviations_list(); $aTz = array(); @@ -22,10 +24,11 @@ foreach ($aTz as $sTz) { $oDateTime = new DateTime($sDate); } catch (Exception $oException) { var_dump($oException->getMessage()); + print_r(DateTime::getLastErrors()); } } var_dump('this should be the only output'); ?> --EXPECTF-- -string(30) "this should be the only output"
\ No newline at end of file +string(30) "this should be the only output" diff --git a/ext/date/tests/bug51994.phpt b/ext/date/tests/bug51994.phpt index e136c8f2b..fb0fe46d8 100644 --- a/ext/date/tests/bug51994.phpt +++ b/ext/date/tests/bug51994.phpt @@ -1,5 +1,7 @@ --TEST-- Bug #51994 (date_parse_from_format is parsing invalid date using 'yz' format) +--XFAIL-- +Bug #51994 isn't fixed yet --FILE-- <?php $trans_date = '10153'; // 152nd day of year 2010 -> 03.06.2010 @@ -35,4 +37,4 @@ array(12) { } ["is_localtime"]=> bool(false) -}
\ No newline at end of file +} diff --git a/ext/date/tests/bug54283.phpt b/ext/date/tests/bug54283.phpt new file mode 100644 index 000000000..780d0fa76 --- /dev/null +++ b/ext/date/tests/bug54283.phpt @@ -0,0 +1,14 @@ +--TEST-- +Bug #54283 (new DatePeriod(NULL) causes crash) +--FILE-- +<?php + +try { + var_dump(new DatePeriod(NULL)); +} catch (Exception $e) { + var_dump($e->getMessage()); +} + +?> +--EXPECTF-- +string(51) "DatePeriod::__construct(): Unknown or bad format ()" diff --git a/ext/date/tests/bug54316.phpt b/ext/date/tests/bug54316.phpt new file mode 100644 index 000000000..a02288cdb --- /dev/null +++ b/ext/date/tests/bug54316.phpt @@ -0,0 +1,28 @@ +--TEST-- +Bug #54316 (DateTime::createFromFormat does not handle trailing '|' correctly) +--INI-- +date.timezone=UTC +--FILE-- +<?php +$dt = DateTime::createFromFormat('Y-m-d|', '2011-02-02'); +var_dump($dt); + +$dt = DateTime::createFromFormat('Y-m-d!', '2011-02-02'); +var_dump($dt); +--EXPECT-- +object(DateTime)#1 (3) { + ["date"]=> + string(19) "2011-02-02 00:00:00" + ["timezone_type"]=> + int(3) + ["timezone"]=> + string(3) "UTC" +} +object(DateTime)#2 (3) { + ["date"]=> + string(19) "1970-01-01 00:00:00" + ["timezone_type"]=> + int(3) + ["timezone"]=> + string(3) "UTC" +} diff --git a/ext/date/tests/bug54340.phpt b/ext/date/tests/bug54340.phpt new file mode 100644 index 000000000..7f00309c9 --- /dev/null +++ b/ext/date/tests/bug54340.phpt @@ -0,0 +1,43 @@ +--TEST-- +Bug #54340 (DateTime::add() method bug) +--INI-- +date.timezone=UTC +--FILE-- +<?php +$interval = new DateInterval('P1D'); + +$dt = new DateTime('first day of January 2011'); +var_dump($dt); + +$dt->add($interval); +var_dump($dt); + +$dt = new DateTime('first day of January 2011'); + +$dt->sub($interval); +var_dump($dt); +--EXPECT-- +object(DateTime)#2 (3) { + ["date"]=> + string(19) "2011-01-01 00:00:00" + ["timezone_type"]=> + int(3) + ["timezone"]=> + string(3) "UTC" +} +object(DateTime)#2 (3) { + ["date"]=> + string(19) "2011-01-02 00:00:00" + ["timezone_type"]=> + int(3) + ["timezone"]=> + string(3) "UTC" +} +object(DateTime)#3 (3) { + ["date"]=> + string(19) "2010-12-31 00:00:00" + ["timezone_type"]=> + int(3) + ["timezone"]=> + string(3) "UTC" +} diff --git a/ext/date/tests/bug55253.phpt b/ext/date/tests/bug55253.phpt new file mode 100755 index 000000000..3c0efc469 --- /dev/null +++ b/ext/date/tests/bug55253.phpt @@ -0,0 +1,47 @@ +--TEST-- +DateTime::add() and sub() result -1 hour on objects with time zone type 2 +--CREDITS-- +Daniel Convissor <danielc@php.net> +--XFAIL-- +Bug 55253 exists +--FILE-- +<?php + +date_default_timezone_set('America/New_York'); + +$interval = new DateInterval('PT2H1M'); + +$date3 = new DateTime('2010-10-04 02:18:48'); +$date2 = new DateTime('2010-10-04 02:18:48 EDT'); + +echo 'Zone Type 3: ' . $date3->format('Y-m-d H:i:s T') . "\n"; +echo 'Zone Type 2: ' . $date2->format('Y-m-d H:i:s T') . "\n"; + +echo $interval->format('Add %h hours %i minutes') . "\n"; +$date3->add($interval); +$date2->add($interval); + +echo 'Zone Type 3: ' . $date3->format('Y-m-d H:i:s T') . "\n"; +echo 'Zone Type 2: ' . $date2->format('Y-m-d H:i:s T') . "\n"; + +// Try subtracting from expected result. +$date3 = new DateTime('2010-10-04 04:19:48'); +$date2 = new DateTime('2010-10-04 04:19:48 EDT'); + +echo $interval->format('Subtract %h hours %i minutes from expected') . "\n"; +$date3->sub($interval); +$date2->sub($interval); + +echo 'Zone Type 3: ' . $date3->format('Y-m-d H:i:s T') . "\n"; +echo 'Zone Type 2: ' . $date2->format('Y-m-d H:i:s T') . "\n"; + +?> +--EXPECT-- +Zone Type 3: 2010-10-04 02:18:48 EDT +Zone Type 2: 2010-10-04 02:18:48 EDT +Add 2 hours 1 minutes +Zone Type 3: 2010-10-04 04:19:48 EDT +Zone Type 2: 2010-10-04 04:19:48 EDT +Subtract 2 hours 1 minutes from expected +Zone Type 3: 2010-10-04 02:18:48 EDT +Zone Type 2: 2010-10-04 02:18:48 EDT diff --git a/ext/date/tests/date_diff.phpt b/ext/date/tests/date_diff.phpt index 31783a884..e01a94e76 100644 --- a/ext/date/tests/date_diff.phpt +++ b/ext/date/tests/date_diff.phpt @@ -1,5 +1,9 @@ --TEST-- Extensive test for date_diff(). +--SKIPIF-- +<?php +if (getenv("SKIP_SLOW_TESTS")) die("skip slow test"); +?> --INI-- date.timezone=UTC --FILE-- diff --git a/ext/date/tests/date_diff1.phpt b/ext/date/tests/date_diff1.phpt new file mode 100644 index 000000000..cf32fcbf3 --- /dev/null +++ b/ext/date/tests/date_diff1.phpt @@ -0,0 +1,48 @@ +--TEST-- +Test for date_diff with timezone abbreviations. +--INI-- +date.timezone=Europe/London +--FILE-- +<?php +$start = new DateTime('2010-10-04 02:18:48 EDT'); +$end = new DateTime('2010-11-06 18:38:28 EDT'); +$int = $start->diff($end); +var_dump($start); +var_dump($end); +var_dump($int); +?> +--EXPECT-- +object(DateTime)#1 (3) { + ["date"]=> + string(19) "2010-10-04 02:18:48" + ["timezone_type"]=> + int(2) + ["timezone"]=> + string(3) "EDT" +} +object(DateTime)#2 (3) { + ["date"]=> + string(19) "2010-11-06 18:38:28" + ["timezone_type"]=> + int(2) + ["timezone"]=> + string(3) "EDT" +} +object(DateInterval)#3 (8) { + ["y"]=> + int(0) + ["m"]=> + int(1) + ["d"]=> + int(2) + ["h"]=> + int(16) + ["i"]=> + int(19) + ["s"]=> + int(40) + ["invert"]=> + int(0) + ["days"]=> + int(33) +} diff --git a/ext/date/tests/examine_diff.inc b/ext/date/tests/examine_diff.inc index 35b4ae333..c3dcba938 100644 --- a/ext/date/tests/examine_diff.inc +++ b/ext/date/tests/examine_diff.inc @@ -1,16 +1,22 @@ <?php /** - * Helper for the DateTime_diff_add_sub* tests + * Helper for the DateTime diff/days/math tests * * @author Daniel Convissor <danielc@analysisandsolutions.com> */ +/**#@+ + * Which test segments should be displayed? + */ +define('PHPT_DATETIME_SHOW_DIFF', 1); +define('PHPT_DATETIME_SHOW_DAYS', 2); +define('PHPT_DATETIME_SHOW_ADD', 3); +define('PHPT_DATETIME_SHOW_SUB', 4); +/**#@-*/ + /** - * Provides a consistent interface for executing date diff tests - * - * Tests the diff() method then passes the resulting - * interval to the add()/sub() method as a double check + * Provides a consistent interface for executing date diff/add/sub tests * * @param string|DateTime $end_date the end date in YYYY-MM-DD format * (can include time HH:MM:SS) or a DateTime object @@ -41,38 +47,33 @@ function examine_diff($end_date, $start_date, $expect_spec, $expect_days, $absol } $end_date = $end->format('Y-m-d H:i:s T'); - $force_sub = false; - if ($absolute) { - $tmp_interval = $start->diff($end); - if ($tmp_interval->format('%r')) { - $force_sub = true; - } + $expect_interval = new DateInterval('P' . substr($expect_spec, 2)); + if (substr($expect_spec, 1, 1) == '-') { + $expect_interval->invert = true; } - $result_interval = $start->diff($end, $absolute); - $result_spec = $result_interval->format('P%R%yY%mM%dDT%hH%iM%sS'); - $result_days = $result_interval->format('%a'); - - // Also make sure add()/sub() works the same way as diff(). - if ($force_sub) { - $start->sub($result_interval); - $sign = '-'; - } else { - $start->add($result_interval); - $sign = '+'; + if (PHPT_DATETIME_SHOW == PHPT_DATETIME_SHOW_DIFF) { + $result_interval = $start->diff($end, $absolute); + $result_spec = $result_interval->format('P%R%yY%mM%dDT%hH%iM%sS'); + echo "DIFF: $end_date - $start_date = **$result_spec**\n"; + // echo "DIFF: $end_date - $start_date = **$expect_spec**\n"; + } + if (PHPT_DATETIME_SHOW == PHPT_DATETIME_SHOW_DAYS) { + $result_interval = $start->diff($end, $absolute); + $result_days = $result_interval->format('%a'); + echo "DAYS: **$result_days**\n"; + // echo "DAYS: **$expect_days**\n"; + } + if (PHPT_DATETIME_SHOW == PHPT_DATETIME_SHOW_ADD) { + $start->add($expect_interval); + $result_end_date = $start->format('Y-m-d H:i:s T'); + echo "ADD: $start_date + $expect_spec = **$result_end_date**\n"; + // echo "ADD: $start_date + $expect_spec = **$end_date**\n"; + } + if (PHPT_DATETIME_SHOW == PHPT_DATETIME_SHOW_SUB) { + $end->sub($expect_interval); + $result_start_date = $end->format('Y-m-d H:i:s T'); + echo "SUB: $end_date - $expect_spec = **$result_start_date**\n"; + // echo "SUB: $end_date - $expect_spec = **$start_date**\n"; } - - $result_end_date = $start->format('Y-m-d H:i:s T'); - - // Leaving this here for making adjustments later. - $expect_full = "FWD: $end_date - $start_date = **$expect_spec** | " - . "BACK: $start_date $sign $expect_spec = **$end_date** | " - . "DAYS: **$expect_days**"; - // echo "$expect_full\n"; - // return; - - $result_full = "FWD: $end_date - $start_date = **$result_spec** | " - . "BACK: $start_date $sign $result_spec = **$result_end_date** | " - . "DAYS: **$result_days**"; - echo "$result_full\n"; } diff --git a/ext/date/tests/gmstrftime_variation22.phpt b/ext/date/tests/gmstrftime_variation22.phpt index c8f51fc89..198941871 100644 --- a/ext/date/tests/gmstrftime_variation22.phpt +++ b/ext/date/tests/gmstrftime_variation22.phpt @@ -46,7 +46,7 @@ foreach($inputs as $key =>$value) { --Preferred date and time representation-- string(2) "%c" -string(24) "Fri Aug 8 08:08:08 2008" +string(31) "Fri 08 Aug 2008 08:08:08 AM GMT" --Preferred date representation-- string(2) "%x" @@ -54,5 +54,5 @@ string(10) "08/08/2008" --Preferred time representation-- string(2) "%X" -string(8) "08:08:08" +string(11) "08:08:08 AM" ===DONE=== diff --git a/ext/date/tests/strftime_variation22.phpt b/ext/date/tests/strftime_variation22.phpt index bd672b417..151a7d206 100644 --- a/ext/date/tests/strftime_variation22.phpt +++ b/ext/date/tests/strftime_variation22.phpt @@ -46,7 +46,7 @@ foreach($inputs as $key =>$value) { --Preferred date and time representation-- string(2) "%c" -string(24) "Fri Aug 8 08:08:08 2008" +string(31) "Fri 08 Aug 2008 08:08:08 AM IST" --Preferred date representation-- string(2) "%x" @@ -54,5 +54,5 @@ string(10) "08/08/2008" --Preferred time representation-- string(2) "%X" -string(8) "08:08:08" +string(11) "08:08:08 AM" ===DONE=== diff --git a/ext/dba/config.m4 b/ext/dba/config.m4 index 83c6354cd..d6a4ac9a6 100644 --- a/ext/dba/config.m4 +++ b/ext/dba/config.m4 @@ -1,5 +1,5 @@ dnl -dnl $Id: config.m4 305894 2010-12-01 19:20:16Z sixd $ +dnl $Id: config.m4 312536 2011-06-27 20:28:30Z bjori $ dnl dnl Suppose we need FlatFile if no support or only CDB is used. @@ -276,7 +276,7 @@ if test "$PHP_DB4" != "no"; then PHP_DBA_STD_BEGIN dbdp4="/usr/local/BerkeleyDB.4." dbdp5="/usr/local/BerkeleyDB.5." - for i in $PHP_DB4 ${dbdp5}0 ${dbdp4}8 ${dbdp4}7 ${dbdp4}6 ${dbdp4}5 ${dbdp4}4 ${dbdp4}3 ${dbdp4}2 ${dbdp4}1 ${dbdp}0 /usr/local /usr; do + for i in $PHP_DB4 ${dbdp5}1 ${dbdp5}0 ${dbdp4}8 ${dbdp4}7 ${dbdp4}6 ${dbdp4}5 ${dbdp4}4 ${dbdp4}3 ${dbdp4}2 ${dbdp4}1 ${dbdp}0 /usr/local /usr; do if test -f "$i/db5/db.h"; then THIS_PREFIX=$i THIS_INCLUDE=$i/db5/db.h @@ -285,6 +285,10 @@ if test "$PHP_DB4" != "no"; then THIS_PREFIX=$i THIS_INCLUDE=$i/db4/db.h break + elif test -f "$i/include/db5.1/db.h"; then + THIS_PREFIX=$i + THIS_INCLUDE=$i/include/db5.1/db.h + break elif test -f "$i/include/db5.0/db.h"; then THIS_PREFIX=$i THIS_INCLUDE=$i/include/db5.0/db.h @@ -323,7 +327,7 @@ if test "$PHP_DB4" != "no"; then break fi done - PHP_DBA_DB_CHECK(4, db-5.0 db-4.8 db-4.7 db-4.6 db-4.5 db-4.4 db-4.3 db-4.2 db-4.1 db-4.0 db-4 db4 db, [(void)db_create((DB**)0, (DB_ENV*)0, 0)]) + PHP_DBA_DB_CHECK(4, db-5.1 db-5.0 db-4.8 db-4.7 db-4.6 db-4.5 db-4.4 db-4.3 db-4.2 db-4.1 db-4.0 db-4 db4 db, [(void)db_create((DB**)0, (DB_ENV*)0, 0)]) fi PHP_DBA_STD_RESULT(db4,Berkeley DB4) diff --git a/ext/dba/dba.c b/ext/dba/dba.c index 8a4540693..558c82c8c 100644 --- a/ext/dba/dba.c +++ b/ext/dba/dba.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: dba.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: dba.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -145,7 +145,7 @@ const zend_function_entry dba_functions[] = { PHP_FE(dba_handlers, arginfo_dba_handlers) PHP_FE(dba_list, arginfo_dba_list) PHP_FE(dba_key_split, arginfo_dba_key_split) - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ diff --git a/ext/dba/dba_db4.c b/ext/dba/dba_db4.c index 483ceb7b1..4cc4d6eb7 100644 --- a/ext/dba/dba_db4.c +++ b/ext/dba/dba_db4.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: dba_db4.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: dba_db4.c 312540 2011-06-27 22:58:59Z sixd $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -46,11 +46,12 @@ static void php_dba_db4_errcall_fcn( #if (DB_VERSION_MAJOR == 5 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR == 8)) /* Bug 51086, Berkeley DB 4.8.26 */ -/* This code suppresses a BDB 4.8 error message that BDB incorrectly emits */ +/* This code suppresses a BDB 4.8+ error message, thus keeping PHP test compatibility */ { char *function = get_active_function_name(TSRMLS_C); if (function && (!strcmp(function,"dba_popen") || !strcmp(function,"dba_open")) - && !strncmp(msg, "fop_read_meta", sizeof("fop_read_meta")-1)) { + && (!strncmp(msg, "fop_read_meta", sizeof("fop_read_meta")-1) + || !strncmp(msg, "BDB0004 fop_read_meta", sizeof("BDB0004 fop_read_meta")-1))) { return; } } diff --git a/ext/dba/dba_flatfile.c b/ext/dba/dba_flatfile.c index e06d18b9f..51d3b6e29 100644 --- a/ext/dba/dba_flatfile.c +++ b/ext/dba/dba_flatfile.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: dba_flatfile.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: dba_flatfile.c 309341 2011-03-17 11:43:05Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -96,7 +96,7 @@ DBA_UPDATE_FUNC(flatfile) return SUCCESS; case 1: php_error_docref1(NULL TSRMLS_CC, key, E_WARNING, "Key already exists"); - return SUCCESS; + return FAILURE; } } diff --git a/ext/dba/dba_inifile.c b/ext/dba/dba_inifile.c index 6ffb78d70..233ebf8c6 100644 --- a/ext/dba/dba_inifile.c +++ b/ext/dba/dba_inifile.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: dba_inifile.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: dba_inifile.c 309341 2011-03-17 11:43:05Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -102,7 +102,7 @@ DBA_UPDATE_FUNC(inifile) return SUCCESS; case 1: php_error_docref1(NULL TSRMLS_CC, key, E_WARNING, "Key already exists"); - return SUCCESS; + return FAILURE; } } diff --git a/ext/dom/attr.c b/ext/dom/attr.c index 0ac622346..ddec8f967 100644 --- a/ext/dom/attr.c +++ b/ext/dom/attr.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: attr.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: attr.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -49,7 +49,7 @@ ZEND_END_ARG_INFO(); const zend_function_entry php_dom_attr_class_functions[] = { PHP_FALIAS(isId, dom_attr_is_id, arginfo_dom_attr_is_id) PHP_ME(domattr, __construct, arginfo_dom_attr_construct, ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} + PHP_FE_END }; /* {{{ proto void DOMAttr::__construct(string name, [string value]); */ diff --git a/ext/dom/cdatasection.c b/ext/dom/cdatasection.c index 33967fe33..c21fa3396 100644 --- a/ext/dom/cdatasection.c +++ b/ext/dom/cdatasection.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: cdatasection.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: cdatasection.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -43,7 +43,7 @@ ZEND_END_ARG_INFO(); const zend_function_entry php_dom_cdatasection_class_functions[] = { PHP_ME(domcdatasection, __construct, arginfo_dom_cdatasection_construct, ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} + PHP_FE_END }; /* {{{ proto void DOMCdataSection::__construct(string value); */ diff --git a/ext/dom/characterdata.c b/ext/dom/characterdata.c index 4b10b4b5e..6a6109462 100644 --- a/ext/dom/characterdata.c +++ b/ext/dom/characterdata.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: characterdata.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: characterdata.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -68,7 +68,7 @@ const zend_function_entry php_dom_characterdata_class_functions[] = { PHP_FALIAS(insertData, dom_characterdata_insert_data, arginfo_dom_characterdata_insert_data) PHP_FALIAS(deleteData, dom_characterdata_delete_data, arginfo_dom_characterdata_delete_data) PHP_FALIAS(replaceData, dom_characterdata_replace_data, arginfo_dom_characterdata_replace_data) - {NULL, NULL, NULL} + PHP_FE_END }; /* {{{ data string diff --git a/ext/dom/document.c b/ext/dom/document.c index 961c5969a..b06b38910 100644 --- a/ext/dom/document.c +++ b/ext/dom/document.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: document.c 307571 2011-01-19 00:22:06Z cataphract $ */ +/* $Id: document.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -229,7 +229,7 @@ const zend_function_entry php_dom_document_class_functions[] = { /* {{{ */ PHP_FALIAS(relaxNGValidateSource, dom_document_relaxNG_validate_xml, arginfo_dom_document_relaxNG_validate_xml) #endif PHP_ME(domdocument, registerNodeClass, arginfo_dom_document_registernodeclass, ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ diff --git a/ext/dom/documentfragment.c b/ext/dom/documentfragment.c index f56dd37a9..42dde4108 100644 --- a/ext/dom/documentfragment.c +++ b/ext/dom/documentfragment.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: documentfragment.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: documentfragment.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -46,7 +46,7 @@ ZEND_END_ARG_INFO(); const zend_function_entry php_dom_documentfragment_class_functions[] = { PHP_ME(domdocumentfragment, __construct, arginfo_dom_documentfragement_construct, ZEND_ACC_PUBLIC) PHP_ME(domdocumentfragment, appendXML, arginfo_dom_documentfragement_appendXML, ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} + PHP_FE_END }; /* {{{ proto void DOMDocumentFragment::__construct(); */ diff --git a/ext/dom/documenttype.c b/ext/dom/documenttype.c index ea9a5d327..0fa76efec 100644 --- a/ext/dom/documenttype.c +++ b/ext/dom/documenttype.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: documenttype.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: documenttype.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -35,7 +35,7 @@ */ const zend_function_entry php_dom_documenttype_class_functions[] = { - {NULL, NULL, NULL} + PHP_FE_END }; /* {{{ name string diff --git a/ext/dom/domconfiguration.c b/ext/dom/domconfiguration.c index 0f3f4b0c5..de3de6adc 100644 --- a/ext/dom/domconfiguration.c +++ b/ext/dom/domconfiguration.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: domconfiguration.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: domconfiguration.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -54,7 +54,7 @@ const zend_function_entry php_dom_domconfiguration_class_functions[] = { PHP_FALIAS(setParameter, dom_domconfiguration_set_parameter, arginfo_dom_configuration_set_parameter) PHP_FALIAS(getParameter, dom_domconfiguration_get_parameter, arginfo_dom_configuration_get_parameter) PHP_FALIAS(canSetParameter, dom_domconfiguration_can_set_parameter, arginfo_dom_configuration_can_set_parameter) - {NULL, NULL, NULL} + PHP_FE_END }; /* {{{ attribute protos, not implemented yet */ diff --git a/ext/dom/domerror.c b/ext/dom/domerror.c index 8ca8ac5af..481cce916 100644 --- a/ext/dom/domerror.c +++ b/ext/dom/domerror.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: domerror.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: domerror.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -36,7 +36,7 @@ */ const zend_function_entry php_dom_domerror_class_functions[] = { - {NULL, NULL, NULL} + PHP_FE_END }; /* {{{ attribute protos, not implemented yet */ diff --git a/ext/dom/domerrorhandler.c b/ext/dom/domerrorhandler.c index 1683c563b..73a8ef50d 100644 --- a/ext/dom/domerrorhandler.c +++ b/ext/dom/domerrorhandler.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: domerrorhandler.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: domerrorhandler.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -42,7 +42,7 @@ ZEND_END_ARG_INFO(); const zend_function_entry php_dom_domerrorhandler_class_functions[] = { PHP_FALIAS(handleError, dom_domerrorhandler_handle_error, arginfo_dom_domerrorhandler_handle_error) - {NULL, NULL, NULL} + PHP_FE_END }; /* {{{ attribute protos, not implemented yet */ diff --git a/ext/dom/domexception.c b/ext/dom/domexception.c index f66abf8e6..18ac78b39 100644 --- a/ext/dom/domexception.c +++ b/ext/dom/domexception.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: domexception.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: domexception.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -37,7 +37,7 @@ extern zend_class_entry *dom_domexception_class_entry; const zend_function_entry php_dom_domexception_class_functions[] = { - {NULL, NULL, NULL} + PHP_FE_END }; void php_dom_throw_error_with_message(int error_code, char *error_message, int strict_error TSRMLS_DC) /* {{{ */ diff --git a/ext/dom/domimplementation.c b/ext/dom/domimplementation.c index b553fca60..28bd39c21 100644 --- a/ext/dom/domimplementation.c +++ b/ext/dom/domimplementation.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: domimplementation.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: domimplementation.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -61,7 +61,7 @@ const zend_function_entry php_dom_domimplementation_class_functions[] = { PHP_ME(domimplementation, hasFeature, arginfo_dom_implementation_has_feature, ZEND_ACC_PUBLIC|ZEND_ACC_ALLOW_STATIC) PHP_ME(domimplementation, createDocumentType, arginfo_dom_implementation_create_documenttype, ZEND_ACC_PUBLIC|ZEND_ACC_ALLOW_STATIC) PHP_ME(domimplementation, createDocument, arginfo_dom_implementation_create_document, ZEND_ACC_PUBLIC|ZEND_ACC_ALLOW_STATIC) - {NULL, NULL, NULL} + PHP_FE_END }; /* {{{ proto boolean dom_domimplementation_has_feature(string feature, string version); diff --git a/ext/dom/domimplementationsource.c b/ext/dom/domimplementationsource.c index eab3428f0..b23185c37 100644 --- a/ext/dom/domimplementationsource.c +++ b/ext/dom/domimplementationsource.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: domimplementationsource.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: domimplementationsource.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -48,7 +48,7 @@ ZEND_END_ARG_INFO(); const zend_function_entry php_dom_domimplementationsource_class_functions[] = { PHP_FALIAS(getDomimplementation, dom_domimplementationsource_get_domimplementation, arginfo_dom_implementationsource_getdomimplementation) PHP_FALIAS(getDomimplementations, dom_domimplementationsource_get_domimplementations, arginfo_dom_implementationsource_getdomimplementations) - {NULL, NULL, NULL} + PHP_FE_END }; /* {{{ attribute protos, not implemented yet */ diff --git a/ext/dom/domlocator.c b/ext/dom/domlocator.c index 38c64d579..b240c41af 100644 --- a/ext/dom/domlocator.c +++ b/ext/dom/domlocator.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: domlocator.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: domlocator.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -36,7 +36,7 @@ */ const zend_function_entry php_dom_domlocator_class_functions[] = { - {NULL, NULL, NULL} + PHP_FE_END }; /* {{{ attribute protos, not implemented yet */ diff --git a/ext/dom/domstringlist.c b/ext/dom/domstringlist.c index 96bb5b12b..b2639ab6c 100644 --- a/ext/dom/domstringlist.c +++ b/ext/dom/domstringlist.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: domstringlist.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: domstringlist.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -42,7 +42,7 @@ ZEND_END_ARG_INFO(); const zend_function_entry php_dom_domstringlist_class_functions[] = { PHP_FALIAS(item, dom_domstringlist_item, arginfo_dom_stringlist_item) - {NULL, NULL, NULL} + PHP_FE_END }; /* {{{ attribute protos, not implemented yet */ diff --git a/ext/dom/element.c b/ext/dom/element.c index 993123f40..512e37a54 100644 --- a/ext/dom/element.c +++ b/ext/dom/element.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: element.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: element.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -146,7 +146,7 @@ const zend_function_entry php_dom_element_class_functions[] = { /* {{{ */ PHP_FALIAS(setIdAttributeNS, dom_element_set_id_attribute_ns, arginfo_dom_element_set_id_attribute_ns) PHP_FALIAS(setIdAttributeNode, dom_element_set_id_attribute_node, arginfo_dom_element_set_id_attribute_node) PHP_ME(domelement, __construct, arginfo_dom_element_construct, ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ diff --git a/ext/dom/entity.c b/ext/dom/entity.c index 6e526a95a..48abaeb9c 100644 --- a/ext/dom/entity.c +++ b/ext/dom/entity.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: entity.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: entity.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -36,7 +36,7 @@ */ const zend_function_entry php_dom_entity_class_functions[] = { - {NULL, NULL, NULL} + PHP_FE_END }; /* {{{ publicId string diff --git a/ext/dom/entityreference.c b/ext/dom/entityreference.c index 4ad19177d..2e8c4b538 100644 --- a/ext/dom/entityreference.c +++ b/ext/dom/entityreference.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: entityreference.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: entityreference.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -42,7 +42,7 @@ ZEND_END_ARG_INFO(); const zend_function_entry php_dom_entityreference_class_functions[] = { PHP_ME(domentityreference, __construct, arginfo_dom_entityreference_construct, ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} + PHP_FE_END }; /* {{{ proto void DOMEntityReference::__construct(string name); */ diff --git a/ext/dom/examples/note.dtd b/ext/dom/examples/note.dtd index 4016eb581..c2d558eee 100644 --- a/ext/dom/examples/note.dtd +++ b/ext/dom/examples/note.dtd @@ -1,6 +1,6 @@ -<?xml version="1.0" ?> +<?xml version="1.0" encoding="utf-8" ?> <!ELEMENT note (to,from,heading,body)> <!ELEMENT to (#PCDATA)> <!ELEMENT from (#PCDATA)> <!ELEMENT heading (#PCDATA)> -<!ELEMENT body (#PCDATA)>
\ No newline at end of file +<!ELEMENT body (#PCDATA)> diff --git a/ext/dom/namednodemap.c b/ext/dom/namednodemap.c index b60c7ca1e..30dff986c 100644 --- a/ext/dom/namednodemap.c +++ b/ext/dom/namednodemap.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: namednodemap.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: namednodemap.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -74,7 +74,7 @@ const zend_function_entry php_dom_namednodemap_class_functions[] = { /* {{{ */ PHP_FALIAS(getNamedItemNS, dom_namednodemap_get_named_item_ns, arginfo_dom_namednodemap_get_named_item_ns) PHP_FALIAS(setNamedItemNS, dom_namednodemap_set_named_item_ns, arginfo_dom_namednodemap_set_named_item_ns) PHP_FALIAS(removeNamedItemNS, dom_namednodemap_remove_named_item_ns, arginfo_dom_namednodemap_remove_named_item_ns) - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ diff --git a/ext/dom/namelist.c b/ext/dom/namelist.c index 85a43bfed..b731ae99a 100644 --- a/ext/dom/namelist.c +++ b/ext/dom/namelist.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: namelist.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: namelist.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -47,7 +47,7 @@ ZEND_END_ARG_INFO(); const zend_function_entry php_dom_namelist_class_functions[] = { PHP_FALIAS(getName, dom_namelist_get_name, arginfo_dom_namelist_get_name) PHP_FALIAS(getNamespaceURI, dom_namelist_get_namespace_uri, arginfo_dom_namelist_get_namespace_uri) - {NULL, NULL, NULL} + PHP_FE_END }; /* {{{ length int diff --git a/ext/dom/node.c b/ext/dom/node.c index c93373a40..a2c9c280a 100644 --- a/ext/dom/node.c +++ b/ext/dom/node.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: node.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: node.c 314493 2011-08-08 12:29:32Z iliaa $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -155,7 +155,7 @@ const zend_function_entry php_dom_node_class_functions[] = { /* {{{ */ PHP_ME(domnode, getLineNo, arginfo_dom_node_getLineNo, ZEND_ACC_PUBLIC) PHP_ME(domnode, C14N, arginfo_dom_node_C14N, ZEND_ACC_PUBLIC) PHP_ME(domnode, C14NFile, arginfo_dom_node_C14NFile, ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ @@ -998,7 +998,7 @@ Since: */ PHP_FUNCTION(dom_node_insert_before) { - zval *id, *node, *ref = NULL, *rv = NULL; + zval *id, *node, *ref = NULL; xmlNodePtr child, new_child, parentp, refp; dom_object *intern, *childobj, *refpobj; int ret, stricterror; @@ -1087,7 +1087,7 @@ PHP_FUNCTION(dom_node_insert_before) xmlUnlinkNode((xmlNodePtr) lastattr); php_libxml_node_free_resource((xmlNodePtr) lastattr TSRMLS_CC); } else { - DOM_RET_OBJ(rv, child, &ret, intern); + DOM_RET_OBJ_EX(child, &ret, intern); return; } } @@ -1129,7 +1129,7 @@ PHP_FUNCTION(dom_node_insert_before) xmlUnlinkNode((xmlNodePtr) lastattr); php_libxml_node_free_resource((xmlNodePtr) lastattr TSRMLS_CC); } else { - DOM_RET_OBJ(rv, child, &ret, intern); + DOM_RET_OBJ_EX(child, &ret, intern); return; } } @@ -1148,7 +1148,7 @@ PHP_FUNCTION(dom_node_insert_before) dom_reconcile_ns(parentp->doc, new_child); - DOM_RET_OBJ(rv, new_child, &ret, intern); + DOM_RET_OBJ_EX(new_child, &ret, intern); } /* }}} end dom_node_insert_before */ @@ -1212,9 +1212,6 @@ PHP_FUNCTION(dom_node_replace_child) } if (foundoldchild) { - xmlNodePtr node; - zval *rv = NULL; - if (newchild->type == XML_DOCUMENT_FRAG_NODE) { xmlNodePtr prevsib, nextsib; prevsib = oldchild->prev; @@ -1232,10 +1229,10 @@ PHP_FUNCTION(dom_node_replace_child) newchildobj->document = intern->document; php_libxml_increment_doc_ref((php_libxml_node_object *)newchildobj, NULL TSRMLS_CC); } - node = xmlReplaceNode(oldchild, newchild); + xmlReplaceNode(oldchild, newchild); dom_reconcile_ns(nodep->doc, newchild); } - DOM_RET_OBJ(rv, oldchild, &ret, intern); + DOM_RET_OBJ_EX(oldchild, &ret, intern); return; } else { php_dom_throw_error(NOT_FOUND_ERR, dom_get_strict_error(intern->document) TSRMLS_CC); @@ -1283,9 +1280,8 @@ PHP_FUNCTION(dom_node_remove_child) while (children) { if (children == child) { - zval *rv = NULL; xmlUnlinkNode(child); - DOM_RET_OBJ(rv, child, &ret, intern); + DOM_RET_OBJ_EX(child, &ret, intern); return; } children = children->next; @@ -1302,7 +1298,7 @@ Since: */ PHP_FUNCTION(dom_node_append_child) { - zval *id, *node, *rv = NULL; + zval *id, *node; xmlNodePtr child, nodep, new_child = NULL; dom_object *intern, *childobj; int ret, stricterror; @@ -1393,7 +1389,7 @@ PHP_FUNCTION(dom_node_append_child) dom_reconcile_ns(nodep->doc, new_child); - DOM_RET_OBJ(rv, new_child, &ret, intern); + DOM_RET_OBJ_EX(new_child, &ret, intern); } /* }}} end dom_node_append_child */ @@ -1431,7 +1427,6 @@ Since: */ PHP_FUNCTION(dom_node_clone_node) { - zval *rv = NULL; zval *id; xmlNode *n, *node; int ret; @@ -1483,7 +1478,7 @@ PHP_FUNCTION(dom_node_clone_node) intern = NULL; } - DOM_RET_OBJ(rv, node, &ret, intern); + DOM_RET_OBJ_EX(node, &ret, intern); } /* }}} end dom_node_clone_node */ diff --git a/ext/dom/nodelist.c b/ext/dom/nodelist.c index 2427498e9..a31b32aa5 100644 --- a/ext/dom/nodelist.c +++ b/ext/dom/nodelist.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: nodelist.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: nodelist.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -43,7 +43,7 @@ ZEND_END_ARG_INFO(); const zend_function_entry php_dom_nodelist_class_functions[] = { PHP_FALIAS(item, dom_nodelist_item, arginfo_dom_nodelist_item) - {NULL, NULL, NULL} + PHP_FE_END }; /* {{{ length int diff --git a/ext/dom/notation.c b/ext/dom/notation.c index 16cc3759f..6951cc443 100644 --- a/ext/dom/notation.c +++ b/ext/dom/notation.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: notation.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: notation.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -35,7 +35,7 @@ */ const zend_function_entry php_dom_notation_class_functions[] = { - {NULL, NULL, NULL} + PHP_FE_END }; /* {{{ attribute protos, not implemented yet */ diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index 5f9480b1d..4e921de0e 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: php_dom.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: php_dom.c 314493 2011-08-08 12:29:32Z iliaa $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -212,7 +212,7 @@ int dom_set_doc_classmap(php_libxml_ref_obj *document, zend_class_entry *basece, zend_hash_init(doc_props->classmap, 0, NULL, NULL, 0); } if (ce) { - return zend_hash_update(doc_props->classmap, basece->name, basece->name_length + 1, &ce, sizeof(ce), NULL); + return zend_hash_update(doc_props->classmap, basece->name, basece->name_length + 1, &ce, sizeof(zend_class_entry *), NULL); } else { zend_hash_del(doc_props->classmap, basece->name, basece->name_length + 1); } @@ -543,7 +543,7 @@ ZEND_END_ARG_INFO() static const zend_function_entry dom_functions[] = { PHP_FE(dom_import_simplexml, arginfo_dom_import_simplexml) - {NULL, NULL, NULL} + PHP_FE_END }; static zend_object_handlers* dom_get_obj_handlers(TSRMLS_D) { @@ -553,7 +553,7 @@ static zend_object_handlers* dom_get_obj_handlers(TSRMLS_D) { static const zend_module_dep dom_deps[] = { ZEND_MOD_REQUIRED("libxml") ZEND_MOD_CONFLICTS("domxml") - {NULL, NULL, NULL} + ZEND_MOD_END }; zend_module_entry dom_module_entry = { /* {{{ */ diff --git a/ext/dom/processinginstruction.c b/ext/dom/processinginstruction.c index f4eb481fe..955f59fc3 100644 --- a/ext/dom/processinginstruction.c +++ b/ext/dom/processinginstruction.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: processinginstruction.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: processinginstruction.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -44,7 +44,7 @@ ZEND_END_ARG_INFO(); const zend_function_entry php_dom_processinginstruction_class_functions[] = { PHP_ME(domprocessinginstruction, __construct, arginfo_dom_processinginstruction_construct, ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} + PHP_FE_END }; /* {{{ proto void DOMProcessingInstruction::__construct(string name, [string value]); */ diff --git a/ext/dom/string_extend.c b/ext/dom/string_extend.c index 752864ebd..9b8de142c 100644 --- a/ext/dom/string_extend.c +++ b/ext/dom/string_extend.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: string_extend.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: string_extend.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -47,7 +47,7 @@ ZEND_END_ARG_INFO(); const zend_function_entry php_dom_string_extend_class_functions[] = { PHP_FALIAS(findOffset16, dom_string_extend_find_offset16, arginfo_dom_string_extend_find_offset16) PHP_FALIAS(findOffset32, dom_string_extend_find_offset32, arginfo_dom_string_extend_find_offset32) - {NULL, NULL, NULL} + PHP_FE_END }; /* {{{ attribute protos, not implemented yet */ diff --git a/ext/dom/tests/DOMDocument_loadHTML_error1.phpt b/ext/dom/tests/DOMDocument_loadHTML_error1.phpt new file mode 100644 index 000000000..c7b5e614d --- /dev/null +++ b/ext/dom/tests/DOMDocument_loadHTML_error1.phpt @@ -0,0 +1,15 @@ +--TEST-- +DOMDocument::loadHTML() should fail if no parameter is given +--CREDITS-- +Knut Urdalen <knut@php.net> +--SKIPIF-- +<?php +require_once('skipif.inc'); +?> +--FILE-- +<?php +$doc = new DOMDocument(); +$doc->loadHTML(); +?> +--EXPECTF-- +Warning: DOMDocument::loadHTML() expects exactly 1 parameter, 0 given in %s on line %d diff --git a/ext/dom/tests/DOMDocument_loadHTML_error2.phpt b/ext/dom/tests/DOMDocument_loadHTML_error2.phpt new file mode 100644 index 000000000..3167c0189 --- /dev/null +++ b/ext/dom/tests/DOMDocument_loadHTML_error2.phpt @@ -0,0 +1,15 @@ +--TEST-- +DOMDocument::loadHTML() should fail if empty string provided as input +--CREDITS-- +Knut Urdalen <knut@php.net> +--SKIPIF-- +<?php +require_once('skipif.inc'); +?> +--FILE-- +<?php +$doc = new DOMDocument(); +$doc->loadHTML(''); +?> +--EXPECTF-- +Warning: DOMDocument::loadHTML(): Empty string supplied as input in %s on line %d diff --git a/ext/dom/tests/DOMDocument_relaxNGValidateSource_basic.phpt b/ext/dom/tests/DOMDocument_relaxNGValidateSource_basic.phpt new file mode 100644 index 000000000..93b9cf717 --- /dev/null +++ b/ext/dom/tests/DOMDocument_relaxNGValidateSource_basic.phpt @@ -0,0 +1,39 @@ +--TEST-- +DOMDocument::relaxNGValidateSource() +--CREDITS-- +Knut Urdalen <knut@php.net> +--SKIPIF-- +<?php +require_once('skipif.inc'); +?> +--FILE-- +<?php +$rng = <<< RNG +<?xml version="1.0" encoding="UTF-8"?> +<grammar ns="" xmlns="http://relaxng.org/ns/structure/1.0" + datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"> + <start> + <element name="apple"> + <element name="pear"> + <data type="NCName"/> + </element> + </element> + </start> +</grammar> +RNG; + +$good_xml = <<< GOOD_XML +<?xml version="1.0"?> +<apple> + <pear>Pear</pear> +</apple> +GOOD_XML; + +$doc = new DOMDocument(); +$doc->loadXML($good_xml); +$result = $doc->relaxNGValidateSource($rng); +var_dump($result); + +?> +--EXPECTF-- +bool(true) diff --git a/ext/dom/tests/DOMDocument_relaxNGValidateSource_error1.phpt b/ext/dom/tests/DOMDocument_relaxNGValidateSource_error1.phpt new file mode 100644 index 000000000..7da71a57a --- /dev/null +++ b/ext/dom/tests/DOMDocument_relaxNGValidateSource_error1.phpt @@ -0,0 +1,41 @@ +--TEST-- +DOMDocument::relaxNGValidateSource() should fail if document doesn't validate +--CREDITS-- +Knut Urdalen <knut@php.net> +--SKIPIF-- +<?php +require_once('skipif.inc'); +?> +--FILE-- +<?php +$rng = <<< RNG +<?xml version="1.0" encoding="UTF-8"?> +<grammar ns="" xmlns="http://relaxng.org/ns/structure/1.0" + datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"> + <start> + <element name="apple"> + <element name="pear"> + <data type="NCName"/> + </element> + </element> + </start> +</grammar> +RNG; + +$bad_xml = <<< BAD_XML +<?xml version="1.0"?> +<apple> + <pear>Pear</pear> + <pear>Pear</pear> +</apple> +BAD_XML; + +$doc = new DOMDocument(); +$doc->loadXML($bad_xml); +$result = $doc->relaxNGValidateSource($rng); +var_dump($result); + +?> +--EXPECTF-- +Warning: DOMDocument::relaxNGValidateSource(): Did not expect element pear there in %s on line %d +bool(false) diff --git a/ext/dom/tests/DOMDocument_relaxNGValidateSource_error2.phpt b/ext/dom/tests/DOMDocument_relaxNGValidateSource_error2.phpt new file mode 100644 index 000000000..d689934f4 --- /dev/null +++ b/ext/dom/tests/DOMDocument_relaxNGValidateSource_error2.phpt @@ -0,0 +1,39 @@ +--TEST-- +DOMDocument::relaxNGValidateSource() should fail on invalid RNG schema +--CREDITS-- +Knut Urdalen <knut@php.net> +--SKIPIF-- +<?php +require_once('skipif.inc'); +?> +--FILE-- +<?php +$rng = <<< RNG +<?xml version="1.0" encoding="UTF-8"?> +<grammar ns="" xmlns="http://relaxng.org/ns/structure/1.0" + datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"> + <start> + <element name="apple"> + </element> + </start> +</grammar> +RNG; + +$xml = <<< XML +<?xml version="1.0"?> +<apple> + <pear>Pear</pear> +</apple> +XML; + +$doc = new DOMDocument(); +$doc->loadXML($xml); +$result = $doc->relaxNGValidateSource($rng); +var_dump($result); + +?> +--EXPECTF-- +Warning: DOMDocument::relaxNGValidateSource(): xmlRelaxNGParseElement: element has no content in %s on line %d + +Warning: DOMDocument::relaxNGValidateSource(): Invalid RelaxNG in %s on line %d +bool(false) diff --git a/ext/dom/tests/DOMDocument_relaxNGValidate_basic.phpt b/ext/dom/tests/DOMDocument_relaxNGValidate_basic.phpt new file mode 100644 index 000000000..76a64425c --- /dev/null +++ b/ext/dom/tests/DOMDocument_relaxNGValidate_basic.phpt @@ -0,0 +1,24 @@ +--TEST-- +DOMDocument::relaxNGValidate() +--CREDITS-- +Knut Urdalen <knut@php.net> +--SKIPIF-- +<?php +require_once('skipif.inc'); +?> +--FILE-- +<?php +$rng = dirname(__FILE__).'/DOMDocument_relaxNGValidate_basic.rng'; +$xml = <<< XML +<?xml version="1.0"?> +<apple> + <pear>Pear</pear> +</apple> +XML; +$doc = new DOMDocument(); +$doc->loadXML($xml); +$result = $doc->relaxNGValidate($rng); +var_dump($result); +?> +--EXPECTF-- +bool(true) diff --git a/ext/dom/tests/DOMDocument_relaxNGValidate_basic.rng b/ext/dom/tests/DOMDocument_relaxNGValidate_basic.rng new file mode 100644 index 000000000..35e351844 --- /dev/null +++ b/ext/dom/tests/DOMDocument_relaxNGValidate_basic.rng @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8"?> +<grammar ns="" xmlns="http://relaxng.org/ns/structure/1.0" + datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"> + <start> + <element name="apple"> + <element name="pear"> + <data type="NCName"/> + </element> + </element> + </start> +</grammar> diff --git a/ext/dom/tests/DOMDocument_relaxNGValidate_error1.phpt b/ext/dom/tests/DOMDocument_relaxNGValidate_error1.phpt new file mode 100644 index 000000000..82957c35b --- /dev/null +++ b/ext/dom/tests/DOMDocument_relaxNGValidate_error1.phpt @@ -0,0 +1,26 @@ +--TEST-- +DOMDocument::relaxNGValidate() should fail if document doesn't validate +--CREDITS-- +Knut Urdalen <knut@php.net> +--SKIPIF-- +<?php +require_once('skipif.inc'); +?> +--FILE-- +<?php +$rng = dirname(__FILE__).'/DOMDocument_relaxNGValidate_basic.rng'; +$xml = <<< XML +<?xml version="1.0"?> +<apple> + <pear>Pear</pear> + <pear>Pear</pear> +</apple> +XML; +$doc = new DOMDocument(); +$doc->loadXML($xml); +$result = $doc->relaxNGValidate($rng); +var_dump($result); +?> +--EXPECTF-- +Warning: DOMDocument::relaxNGValidate(): Did not expect element pear there in %s on line %d +bool(false) diff --git a/ext/dom/tests/DOMDocument_relaxNGValidate_error2.phpt b/ext/dom/tests/DOMDocument_relaxNGValidate_error2.phpt new file mode 100644 index 000000000..85749210b --- /dev/null +++ b/ext/dom/tests/DOMDocument_relaxNGValidate_error2.phpt @@ -0,0 +1,25 @@ +--TEST-- +DOMDocument::relaxNGValidate() should fail on invalid RelaxNG file source +--CREDITS-- +Knut Urdalen <knut@php.net> +--SKIPIF-- +<?php +require_once('skipif.inc'); +?> +--FILE-- +<?php +$rng = dirname(__FILE__).'/foo.rng'; +$xml = <<< XML +<?xml version="1.0"?> +<apple> + <pear>Pear</pear> + <pear>Pear</pear> +</apple> +XML; +$doc = new DOMDocument(); +$doc->loadXML($xml); +$result = $doc->relaxNGValidate($rng); +var_dump($result); +?> +--EXPECTF-- + diff --git a/ext/dom/tests/DOMDocument_validate_on_parse_variation.phpt b/ext/dom/tests/DOMDocument_validate_on_parse_variation.phpt index 6aa390ca9..403e01aa7 100644 --- a/ext/dom/tests/DOMDocument_validate_on_parse_variation.phpt +++ b/ext/dom/tests/DOMDocument_validate_on_parse_variation.phpt @@ -6,17 +6,14 @@ Hans Zaunere --SKIPIF-- <?php require_once('skipif.inc'); - -// need external DTD/XML docs -if( @file_get_contents('http://www.php.net/') === FALSE ) - exit('skip network not available'); ?> --FILE-- <?php require_once('dom_test.inc'); -$XMLStringGood = file_get_contents('http://www.php.net/'); +chdir(__DIR__ . "/../examples"); +$XMLStringGood = file_get_contents('note.xml'); $dom = new DOMDocument; $dom->resolveExternals = TRUE; @@ -27,7 +24,7 @@ $dom->loadXML($XMLStringGood); echo "No Error Report Above\n"; $BogusElement = $dom->createElement('NYPHP','DOMinatrix'); -$Body = $dom->getElementsByTagName('body')->item(0); +$Body = $dom->getElementsByTagName('from')->item(0); $Body->appendChild($BogusElement); $XMLStringBad = $dom->saveXML(); @@ -44,6 +41,6 @@ validateOnParse set to TRUE: Warning: DOMDocument::loadXML(): No declaration for element NYPHP in Entity, line: %d in %s on line %d -Warning: DOMDocument::loadXML(): Element body content does not follow the DTD, expecting (p | h1 | h2 | h3 | h4 | h5 | h6 | div | ul | ol | dl | pre | hr | blockquote | address | fieldset | table | form | noscript | ins | del | script)*, got (div div div div div NYPHP) in Entity, line: %d in %s on line %d +Warning: DOMDocument::loadXML(): Element from was declared #PCDATA but contains non text nodes in Entity, line: %d in %s on line %d Error Report Above diff --git a/ext/dom/tests/DOMImplementation_createDocumentType_basic.phpt b/ext/dom/tests/DOMImplementation_createDocumentType_basic.phpt new file mode 100644 index 000000000..3b19ea461 --- /dev/null +++ b/ext/dom/tests/DOMImplementation_createDocumentType_basic.phpt @@ -0,0 +1,18 @@ +--TEST-- +DOMImplementation::createDocumentType() +--SKIPIF-- +<?php +include('skipif.inc'); +?> +--FILE-- +<?php +$imp = new DOMImplementation(); +$doctype = $imp->createDocumentType("html", + "-//W3C//DTD XHTML 1.0 Strict//EN", + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"); +$doc = $imp->createDocument(null, 'html', $doctype); +echo $doc->saveHTML(); +?> +--EXPECTF-- +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html></html> diff --git a/ext/dom/tests/DOMImplementation_createDocument_basic.phpt b/ext/dom/tests/DOMImplementation_createDocument_basic.phpt new file mode 100644 index 000000000..78d20aeee --- /dev/null +++ b/ext/dom/tests/DOMImplementation_createDocument_basic.phpt @@ -0,0 +1,14 @@ +--TEST-- +DOMImplementation::createDocument() +--SKIPIF-- +<?php +include('skipif.inc'); +?> +--FILE-- +<?php +$x = new DOMImplementation(); +$doc = $x->createDocument(null, 'html'); +echo $doc->saveHTML(); +?> +--EXPECTF-- +<html></html> diff --git a/ext/dom/tests/DOMImplementation_hasFeature_basic.phpt b/ext/dom/tests/DOMImplementation_hasFeature_basic.phpt new file mode 100644 index 000000000..b53e912b7 --- /dev/null +++ b/ext/dom/tests/DOMImplementation_hasFeature_basic.phpt @@ -0,0 +1,15 @@ +--TEST-- +DOMImplementation::hasFeature() +--SKIPIF-- +<?php +include('skipif.inc'); +?> +--FILE-- +<?php +$imp = new DOMImplementation(); +var_dump($imp->hasFeature('Core', '1.0')); +var_dump($imp->hasFeature('XML', '2.0')); +?> +--EXPECTF-- +bool(true) +bool(true) diff --git a/ext/dom/tests/DOMNode_C14NFile_basic.phpt b/ext/dom/tests/DOMNode_C14NFile_basic.phpt new file mode 100644 index 000000000..6af6e3ecb --- /dev/null +++ b/ext/dom/tests/DOMNode_C14NFile_basic.phpt @@ -0,0 +1,38 @@ +--TEST-- +DOMNode::C14NFile() +--SKIPIF-- +<?php +include('skipif.inc'); +?> +--FILE-- +<?php +$xml = <<< XML +<?xml version="1.0" ?> +<books> + <book> + <title>The Grapes of Wrath</title> + <author>John Steinbeck</author> + </book> + <book> + <title>The Pearl</title> + <author>John Steinbeck</author> + </book> +</books> +XML; + +$output = dirname(__FILE__).'/DOMNode_C14NFile_basic.tmp'; +$doc = new DOMDocument(); +$doc->loadXML($xml); +$node = $doc->getElementsByTagName('title')->item(0); +var_dump($node->C14NFile($output)); +$content = file_get_contents($output); +var_dump($content); +?> +--CLEAN-- +<?php +$output = dirname(__FILE__).'/DOMNode_C14NFile_basic.tmp'; +unlink($output); +?> +--EXPECTF-- +int(34) +string(34) "<title>The Grapes of Wrath</title>" diff --git a/ext/dom/tests/DOMNode_C14N_basic.phpt b/ext/dom/tests/DOMNode_C14N_basic.phpt new file mode 100644 index 000000000..52a47a15d --- /dev/null +++ b/ext/dom/tests/DOMNode_C14N_basic.phpt @@ -0,0 +1,29 @@ +--TEST-- +DOMNode::C14N() +--SKIPIF-- +<?php +include('skipif.inc'); +?> +--FILE-- +<?php +$xml = <<< XML +<?xml version="1.0" ?> +<books> + <book> + <title>The Grapes of Wrath</title> + <author>John Steinbeck</author> + </book> + <book> + <title>The Pearl</title> + <author>John Steinbeck</author> + </book> +</books> +XML; + +$doc = new DOMDocument(); +$doc->loadXML($xml); +$node = $doc->getElementsByTagName('title')->item(0); +var_dump($node->C14N()); +?> +--EXPECTF-- +string(34) "<title>The Grapes of Wrath</title>" diff --git a/ext/dom/tests/DOMNode_getLineNo_basic.phpt b/ext/dom/tests/DOMNode_getLineNo_basic.phpt new file mode 100644 index 000000000..3f57681c3 --- /dev/null +++ b/ext/dom/tests/DOMNode_getLineNo_basic.phpt @@ -0,0 +1,19 @@ +--TEST-- +DOMNode::getLineNo() +--SKIPIF-- +<?php +include('skipif.inc'); +?> +--FILE-- +<?php +$file = dirname(__FILE__).'/book.xml'; +$doc = new DOMDocument(); +$doc->load($file); +$nodes = $doc->getElementsByTagName('title'); +foreach($nodes as $node) { + var_dump($node->getLineNo()); +} +?> +--EXPECTF-- +int(4) +int(8) diff --git a/ext/dom/tests/DOMNode_getNodePath_basic.phpt b/ext/dom/tests/DOMNode_getNodePath_basic.phpt new file mode 100644 index 000000000..7c14ffab9 --- /dev/null +++ b/ext/dom/tests/DOMNode_getNodePath_basic.phpt @@ -0,0 +1,19 @@ +--TEST-- +DOMNode::getNodePath() +--SKIPIF-- +<?php +include('skipif.inc'); +?> +--FILE-- +<?php +$file = dirname(__FILE__).'/book.xml'; +$doc = new DOMDocument(); +$doc->load($file); +$nodes = $doc->getElementsByTagName('title'); +foreach($nodes as $node) { + var_dump($node->getNodePath()); +} +?> +--EXPECTF-- +string(20) "/books/book[1]/title" +string(20) "/books/book[2]/title" diff --git a/ext/dom/tests/DOMNode_insertBefore_error1.phpt b/ext/dom/tests/DOMNode_insertBefore_error1.phpt new file mode 100644 index 000000000..d655479d0 --- /dev/null +++ b/ext/dom/tests/DOMNode_insertBefore_error1.phpt @@ -0,0 +1,24 @@ +--TEST-- +DOMNode::insertBefore() should fail if node belongs to another document +--CREDITS-- +Knut Urdalen <knut@php.net> +--SKIPIF-- +<?php require_once('skipif.inc'); ?> +--FILE-- +<?php + +$doc1 = new DOMDocument(); +$doc2 = new DOMDocument(); + +$node_in_doc1 = $doc1->createElement("foo"); +$node_in_doc2 = $doc2->createElement("bar"); + +try { + $node_in_doc2->insertBefore($node_in_doc1); +} catch(DOMException $e) { + echo $e->getMessage(); +} + +?> +--EXPECTF-- +Wrong Document Error diff --git a/ext/dom/tests/bug54601.phpt b/ext/dom/tests/bug54601.phpt new file mode 100644 index 000000000..8a2da2dee --- /dev/null +++ b/ext/dom/tests/bug54601.phpt @@ -0,0 +1,30 @@ +--TEST-- +Segfault when removing the Doctype node +--SKIPIF-- +<?php require_once('skipif.inc'); ?> +--FILE-- +<?php +$xml = <<< XML +<?xml version='1.0' encoding='utf-8' ?> +<!DOCTYPE set PUBLIC "-//OASIS//DTD DocBook XML V5.0//EN" "http://www.docbook.org/xml/5.0/dtd/docbook.dtd" [ +<!ENTITY foo '<foo>footext</foo>'> +<!ENTITY bar '<bar>bartext</bar>'> +]> +<set>&foo;&bar;</set> +XML; + +$doc = new DOMDocument(); +$doc->loadXML($xml, LIBXML_NOENT); +$n = $doc->doctype; +$doc->removeChild($n); +var_dump($n); +print $doc->saveXML(); +?> +===DONE=== +<?php exit(0); ?> +--EXPECTF-- +object(DOMDocumentType)#%d (0) { +} +<?xml version="1.0" encoding="utf-8"?> +<set><foo>footext</foo><bar>bartext</bar></set> +===DONE=== diff --git a/ext/dom/tests/dom004.phpt b/ext/dom/tests/dom004.phpt index 82b7915f6..5b65f24ba 100644 --- a/ext/dom/tests/dom004.phpt +++ b/ext/dom/tests/dom004.phpt @@ -3,7 +3,7 @@ Test 4: Streams Test --SKIPIF-- <?php require_once('skipif.inc'); -array_search('compress.zlib', stream_get_wrappers()) or die('skip compress.zlib wrapper is not available'); +in_array('compress.zlib', stream_get_wrappers()) or die('skip compress.zlib wrapper is not available'); ?> --FILE-- <?php diff --git a/ext/dom/tests/dom005.phpt b/ext/dom/tests/dom005.phpt index 249869eff..715aec402 100644 --- a/ext/dom/tests/dom005.phpt +++ b/ext/dom/tests/dom005.phpt @@ -27,10 +27,7 @@ html files with undeclared entities  </body></html> --- save as HTML <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"> -<html> -<head><title>Hello world</title></head> -<body> +<html><head><title>Hello world</title></head><body> This is a not well-formed<br> html files with undeclared entities -</body> -</html> +</body></html> diff --git a/ext/dom/tests/dom_xinclude.phpt b/ext/dom/tests/dom_xinclude.phpt index bf335d032..686a9e81a 100644 --- a/ext/dom/tests/dom_xinclude.phpt +++ b/ext/dom/tests/dom_xinclude.phpt @@ -3,7 +3,7 @@ Test: Xinclude and Streams --SKIPIF-- <?php require_once('skipif.inc'); -array_search('compress.zlib', stream_get_wrappers()) or die('skip compress.zlib wrapper is not available'); +in_array('compress.zlib', stream_get_wrappers()) or die('skip compress.zlib wrapper is not available'); ?> --FILE-- <?php diff --git a/ext/dom/text.c b/ext/dom/text.c index f6058d9f4..4e761dc36 100644 --- a/ext/dom/text.c +++ b/ext/dom/text.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: text.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: text.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -58,7 +58,7 @@ const zend_function_entry php_dom_text_class_functions[] = { PHP_FALIAS(isElementContentWhitespace, dom_text_is_whitespace_in_element_content, arginfo_dom_text_is_whitespace_in_element_content) PHP_FALIAS(replaceWholeText, dom_text_replace_whole_text, arginfo_dom_text_replace_whole_text) PHP_ME(domtext, __construct, arginfo_dom_text_construct, ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} + PHP_FE_END }; /* {{{ proto void DOMText::__construct([string value]); */ diff --git a/ext/dom/typeinfo.c b/ext/dom/typeinfo.c index 6554fdfdc..839e7ecd5 100644 --- a/ext/dom/typeinfo.c +++ b/ext/dom/typeinfo.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: typeinfo.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: typeinfo.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -36,7 +36,7 @@ */ const zend_function_entry php_dom_typeinfo_class_functions[] = { - {NULL, NULL, NULL} + PHP_FE_END }; /* {{{ attribute protos, not implemented yet */ diff --git a/ext/dom/userdatahandler.c b/ext/dom/userdatahandler.c index 1ccac1eb9..0f1b82f4d 100644 --- a/ext/dom/userdatahandler.c +++ b/ext/dom/userdatahandler.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: userdatahandler.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: userdatahandler.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -37,7 +37,7 @@ const zend_function_entry php_dom_userdatahandler_class_functions[] = { PHP_FALIAS(handle, dom_userdatahandler_handle, NULL) - {NULL, NULL, NULL} + PHP_FE_END }; /* {{{ attribute protos, not implemented yet */ diff --git a/ext/dom/xml_common.h b/ext/dom/xml_common.h index b6998afd2..8f93ed7ec 100644 --- a/ext/dom/xml_common.h +++ b/ext/dom/xml_common.h @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: xml_common.h 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: xml_common.h 314493 2011-08-08 12:29:32Z iliaa $ */ #ifndef PHP_XML_COMMON_H #define PHP_XML_COMMON_H @@ -78,12 +78,14 @@ PHP_DOM_EXPORT xmlNodePtr dom_object_get_node(dom_object *obj); } \ } -#define DOM_RET_OBJ(zval, obj, ret, domobject) \ - if (NULL == (zval = php_dom_create_object(obj, ret, zval, return_value, domobject TSRMLS_CC))) { \ +#define DOM_RET_OBJ_EX(obj, ret, domobject) \ + if (!php_dom_create_object(obj, ret, NULL, return_value, domobject TSRMLS_CC)) { \ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot create required DOM object"); \ RETURN_FALSE; \ } +#define DOM_RET_OBJ(zval, obj, ret, domobject) DOM_RET_OBJ_EX(obj, ret, domobject) + #define DOM_GET_THIS(zval) \ if (NULL == (zval = getThis())) { \ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Underlying object missing"); \ diff --git a/ext/dom/xpath.c b/ext/dom/xpath.c index e9bd75340..6b650577b 100644 --- a/ext/dom/xpath.c +++ b/ext/dom/xpath.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: xpath.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: xpath.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -68,7 +68,7 @@ const zend_function_entry php_dom_xpath_class_functions[] = { PHP_FALIAS(query, dom_xpath_query, arginfo_dom_xpath_query) PHP_FALIAS(evaluate, dom_xpath_evaluate, arginfo_dom_xpath_evaluate) PHP_FALIAS(registerPhpFunctions, dom_xpath_register_php_functions, arginfo_dom_xpath_register_php_functions) - {NULL, NULL, NULL} + PHP_FE_END }; diff --git a/ext/enchant/enchant.c b/ext/enchant/enchant.c index 9ed1e35d2..7b2fb7db6 100755 --- a/ext/enchant/enchant.c +++ b/ext/enchant/enchant.c @@ -16,7 +16,7 @@ | Ilia Alshanetsky <ilia@prohost.org> | +----------------------------------------------------------------------+ - $Id: enchant.c 306939 2011-01-01 02:19:59Z felipe $ + $Id: enchant.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H @@ -146,8 +146,7 @@ function_entry enchant_functions[] = { PHP_FE(enchant_dict_get_error, arginfo_enchant_broker_free_dict) PHP_FE(enchant_dict_describe, arginfo_enchant_broker_free_dict) PHP_FE(enchant_dict_quick_check, arginfo_enchant_dict_quick_check) - - {NULL, NULL, NULL} /* Must be the last line in enchant_functions[] */ + PHP_FE_END }; /* }}} */ @@ -327,7 +326,7 @@ PHP_MINFO_FUNCTION(enchant) #elif defined(HAVE_ENCHANT_BROKER_SET_PARAM) php_info_print_table_row(2, "Libenchant Version", "1.5.0 or later"); #endif - php_info_print_table_row(2, "Revision", "$Revision: 306939 $"); + php_info_print_table_row(2, "Revision", "$Revision: 313665 $"); php_info_print_table_end(); php_info_print_table_start(); diff --git a/ext/ereg/ereg.c b/ext/ereg/ereg.c index 92ccbea53..12f85dca9 100644 --- a/ext/ereg/ereg.c +++ b/ext/ereg/ereg.c @@ -17,7 +17,7 @@ | Jaakko Hyvätti <jaakko@hyvatti.iki.fi> | +----------------------------------------------------------------------+ */ -/* $Id: ereg.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: ereg.c 314398 2011-08-07 05:08:08Z rasmus $ */ #include <stdio.h> #include <ctype.h> @@ -59,7 +59,7 @@ const zend_function_entry ereg_functions[] = { PHP_DEP_FE(split, arginfo_split) PHP_DEP_FE(spliti, arginfo_split) PHP_DEP_FE(sql_regcase, arginfo_sql_regcase) - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ @@ -474,7 +474,7 @@ PHPAPI char *php_ereg_replace(const char *pattern, const char *replace, const ch if (new_l + 1 > buf_len) { buf_len = 1 + buf_len + 2 * new_l; nbuf = emalloc(buf_len); - strcpy(nbuf, buf); + strncpy(nbuf, buf, buf_len-1); efree(buf); buf = nbuf; } @@ -511,7 +511,7 @@ PHPAPI char *php_ereg_replace(const char *pattern, const char *replace, const ch if (new_l + 1 > buf_len) { buf_len = 1 + buf_len + 2 * new_l; nbuf = safe_emalloc(buf_len, sizeof(char), 0); - strcpy(nbuf, buf); + strncpy(nbuf, buf, buf_len-1); efree(buf); buf = nbuf; } @@ -526,7 +526,7 @@ PHPAPI char *php_ereg_replace(const char *pattern, const char *replace, const ch if (new_l + 1 > buf_len) { buf_len = new_l + 1; /* now we know exactly how long it is */ nbuf = safe_emalloc(buf_len, sizeof(char), 0); - strcpy(nbuf, buf); + strncpy(nbuf, buf, buf_len-1); efree(buf); buf = nbuf; } diff --git a/ext/ereg/regex/regerror.c b/ext/ereg/regex/regerror.c index 00009a93c..1c66d4114 100644 --- a/ext/ereg/regex/regerror.c +++ b/ext/ereg/regex/regerror.c @@ -8,6 +8,7 @@ #include "regex.h" #include "utils.h" #include "regerror.ih" +#include "php.h" /* = #define REG_OKAY 0 @@ -74,7 +75,7 @@ size_t errbuf_size) char convbuf[50]; if (errcode == REG_ATOI) - s = regatoi(preg, convbuf); + s = regatoi(preg, convbuf, sizeof(convbuf)); else { for (r = rerrs; r->code >= 0; r++) if (r->code == target) @@ -82,9 +83,9 @@ size_t errbuf_size) if (errcode®_ITOA) { if (r->code >= 0) - (void) strcpy(convbuf, r->name); + (void) strncpy(convbuf, r->name, 50); else - sprintf(convbuf, "REG_0x%x", target); + snprintf(convbuf, sizeof(convbuf), "REG_0x%x", target); assert(strlen(convbuf) < sizeof(convbuf)); s = convbuf; } else @@ -106,12 +107,13 @@ size_t errbuf_size) /* - regatoi - internal routine to implement REG_ATOI - == static char *regatoi(const regex_t *preg, char *localbuf); + == static char *regatoi(const regex_t *preg, char *localbuf, int bufsize); */ static char * -regatoi(preg, localbuf) +regatoi(preg, localbuf, bufsize) const regex_t *preg; char *localbuf; +int bufsize; { register const struct rerr *r; @@ -121,6 +123,6 @@ char *localbuf; if (r->code < 0) return("0"); - sprintf(localbuf, "%d", r->code); + snprintf(localbuf, bufsize, "%d", r->code); return(localbuf); } diff --git a/ext/ereg/regex/regerror.ih b/ext/ereg/regex/regerror.ih index 2cb668c24..5ff158e57 100644 --- a/ext/ereg/regex/regerror.ih +++ b/ext/ereg/regex/regerror.ih @@ -4,7 +4,7 @@ extern "C" { #endif /* === regerror.c === */ -static char *regatoi(const regex_t *preg, char *localbuf); +static char *regatoi(const regex_t *preg, char *localbuf, int bufsize); #ifdef __cplusplus } diff --git a/ext/exif/exif.c b/ext/exif/exif.c index 9a495dff2..e2cf9f78c 100644 --- a/ext/exif/exif.c +++ b/ext/exif/exif.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: exif.c 308362 2011-02-15 14:02:26Z pajoye $ */ +/* $Id: exif.c 314376 2011-08-06 14:47:44Z felipe $ */ /* ToDos * @@ -144,11 +144,11 @@ const zend_function_entry exif_functions[] = { PHP_FE(exif_tagname, arginfo_exif_tagname) PHP_FE(exif_thumbnail, arginfo_exif_thumbnail) PHP_FE(exif_imagetype, arginfo_exif_imagetype) - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ -#define EXIF_VERSION "1.4 $Id: exif.c 308362 2011-02-15 14:02:26Z pajoye $" +#define EXIF_VERSION "1.4 $Id: exif.c 314376 2011-08-06 14:47:44Z felipe $" /* {{{ PHP_MINFO_FUNCTION */ @@ -254,7 +254,7 @@ static const zend_module_dep exif_module_deps[] = { #if EXIF_USE_MBSTRING ZEND_MOD_REQUIRED("mbstring") #endif - {NULL, NULL, NULL} + ZEND_MOD_END }; /* }}} */ @@ -2909,7 +2909,7 @@ static int exif_process_IFD_TAG(image_info_type *ImageInfo, char *dir_entry, cha fgot = php_stream_tell(ImageInfo->infile); if (fgot!=offset_val) { EFREE_IF(outside); - exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Wrong file pointer: 0x%08X != 0x08X", fgot, offset_val); + exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Wrong file pointer: 0x%08X != 0x%08X", fgot, offset_val); return FALSE; } fgot = php_stream_read(ImageInfo->infile, value_ptr, byte_count); diff --git a/ext/fileinfo/fileinfo.c b/ext/fileinfo/fileinfo.c index 4c9665e56..95eca4c0b 100644 --- a/ext/fileinfo/fileinfo.c +++ b/ext/fileinfo/fileinfo.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: fileinfo.c 308327 2011-02-14 15:32:02Z bjori $ */ +/* $Id: fileinfo.c 314584 2011-08-09 05:11:19Z laruence $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -172,7 +172,7 @@ zend_function_entry finfo_class_functions[] = { ZEND_ME_MAPPING(set_flags, finfo_set_flags,arginfo_finfo_method_set_flags, ZEND_ACC_PUBLIC) ZEND_ME_MAPPING(file, finfo_file, arginfo_finfo_method_file, ZEND_ACC_PUBLIC) ZEND_ME_MAPPING(buffer, finfo_buffer, arginfo_finfo_method_buffer, ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ @@ -392,7 +392,7 @@ static void _php_finfo_get_type(INTERNAL_FUNCTION_PARAMETERS, int mode, int mime long options = 0; char *ret_val = NULL, *buffer = NULL; int buffer_len; - struct php_fileinfo *finfo; + struct php_fileinfo *finfo = NULL; zval *zfinfo, *zcontext = NULL; zval *what; char mime_directory[] = "directory"; diff --git a/ext/fileinfo/libmagic.patch b/ext/fileinfo/libmagic.patch index 276bb011c..a834c0039 100644 --- a/ext/fileinfo/libmagic.patch +++ b/ext/fileinfo/libmagic.patch @@ -2020,9 +2020,9 @@ diff -u libmagic.orig/magic.c libmagic/magic.c + if (!stream && inname) { + no_in_stream = 1; +#if PHP_API_VERSION < 20100412 -+ stream = php_stream_open_wrapper(inname, "rb", REPORT_ERRORS|ENFORCE_SAFE_MODE, NULL); ++ stream = php_stream_open_wrapper((char *)inname, "rb", REPORT_ERRORS|ENFORCE_SAFE_MODE, NULL); +#else -+ stream = php_stream_open_wrapper(inname, "rb", REPORT_ERRORS, NULL); ++ stream = php_stream_open_wrapper((char *)inname, "rb", REPORT_ERRORS, NULL); +#endif + } + diff --git a/ext/fileinfo/libmagic/apprentice.c b/ext/fileinfo/libmagic/apprentice.c index 53fa8d5a1..eb1562e46 100644 --- a/ext/fileinfo/libmagic/apprentice.c +++ b/ext/fileinfo/libmagic/apprentice.c @@ -41,6 +41,14 @@ FILE_RCSID("@(#)$File: apprentice.c,v 1.151 2009/03/18 15:19:23 christos Exp $") #include "patchlevel.h" #include <stdlib.h> +#if defined(__hpux) && !defined(HAVE_STRTOULL) +#if SIZEOF_LONG == 8 +# define strtoull strtoul +#else +# define strtoull __strtoull +#endif +#endif + #ifdef PHP_WIN32 #include "win32/unistd.h" #if _MSC_VER <= 1300 diff --git a/ext/fileinfo/libmagic/magic.c b/ext/fileinfo/libmagic/magic.c index a8bf6d888..51cc67209 100644 --- a/ext/fileinfo/libmagic/magic.c +++ b/ext/fileinfo/libmagic/magic.c @@ -291,9 +291,9 @@ file_or_stream(struct magic_set *ms, const char *inname, php_stream *stream) if (!stream && inname) { no_in_stream = 1; #if PHP_API_VERSION < 20100412 - stream = php_stream_open_wrapper(inname, "rb", REPORT_ERRORS|ENFORCE_SAFE_MODE, NULL); + stream = php_stream_open_wrapper((char *)inname, "rb", REPORT_ERRORS|ENFORCE_SAFE_MODE, NULL); #else - stream = php_stream_open_wrapper(inname, "rb", REPORT_ERRORS, NULL); + stream = php_stream_open_wrapper((char *)inname, "rb", REPORT_ERRORS, NULL); #endif } diff --git a/ext/fileinfo/libmagic/softmagic.c b/ext/fileinfo/libmagic/softmagic.c index 0a06d00f8..da65bfcf8 100644 --- a/ext/fileinfo/libmagic/softmagic.c +++ b/ext/fileinfo/libmagic/softmagic.c @@ -1652,7 +1652,6 @@ convert_libmagic_pattern(zval *pattern, int options) char *t; t = (char *) safe_emalloc(Z_STRLEN_P(pattern), 2, 5); - memset(t, '\0', sizeof(t)); t[j++] = '~'; diff --git a/ext/filter/filter.c b/ext/filter/filter.c index cc9c4e339..5005c7607 100644 --- a/ext/filter/filter.c +++ b/ext/filter/filter.c @@ -19,7 +19,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: filter.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: filter.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -96,11 +96,13 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_filter_input_array, 0, 0, 1) ZEND_ARG_INFO(0, type) ZEND_ARG_INFO(0, definition) + ZEND_ARG_INFO(0, add_empty) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_filter_var_array, 0, 0, 1) ZEND_ARG_INFO(0, data) ZEND_ARG_INFO(0, definition) + ZEND_ARG_INFO(0, add_empty) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arginfo_filter_list, 0) @@ -126,7 +128,7 @@ static const zend_function_entry filter_functions[] = { PHP_FE(filter_list, arginfo_filter_list) PHP_FE(filter_has_var, arginfo_filter_has_var) PHP_FE(filter_id, arginfo_filter_id) - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ @@ -316,7 +318,7 @@ PHP_MINFO_FUNCTION(filter) { php_info_print_table_start(); php_info_print_table_row( 2, "Input Validation and Filtering", "enabled" ); - php_info_print_table_row( 2, "Revision", "$Revision: 306939 $"); + php_info_print_table_row( 2, "Revision", "$Revision: 313665 $"); php_info_print_table_end(); DISPLAY_INI_ENTRIES(); @@ -689,7 +691,7 @@ static void php_filter_call(zval **filtered, long filter, zval **filter_args, co } /* }}} */ -static void php_filter_array_handler(zval *input, zval **op, zval *return_value TSRMLS_DC) /* {{{ */ +static void php_filter_array_handler(zval *input, zval **op, zval *return_value, zend_bool add_empty TSRMLS_DC) /* {{{ */ { char *arg_key; uint arg_key_len; @@ -724,7 +726,9 @@ static void php_filter_array_handler(zval *input, zval **op, zval *return_value RETURN_FALSE; } if (zend_hash_find(Z_ARRVAL_P(input), arg_key, arg_key_len, (void **)&tmp) != SUCCESS) { - add_assoc_null_ex(return_value, arg_key, arg_key_len); + if (add_empty) { + add_assoc_null_ex(return_value, arg_key, arg_key_len); + } } else { zval *nval; @@ -821,15 +825,16 @@ PHP_FUNCTION(filter_var) } /* }}} */ -/* {{{ proto mixed filter_input_array(constant type, [, mixed options]]) +/* {{{ proto mixed filter_input_array(constant type, [, mixed options [, bool add_empty]]]) * Returns an array with all arguments defined in 'definition'. */ PHP_FUNCTION(filter_input_array) { long fetch_from; zval *array_input = NULL, **op = NULL; + zend_bool add_empty = 1; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|Z", &fetch_from, &op) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|Zb", &fetch_from, &op, &add_empty) == FAILURE) { return; } @@ -865,18 +870,19 @@ PHP_FUNCTION(filter_input_array) } } - php_filter_array_handler(array_input, op, return_value TSRMLS_CC); + php_filter_array_handler(array_input, op, return_value, add_empty TSRMLS_CC); } /* }}} */ -/* {{{ proto mixed filter_var_array(array data, [, mixed options]]) +/* {{{ proto mixed filter_var_array(array data, [, mixed options [, bool add_empty]]]) * Returns an array with all arguments defined in 'definition'. */ PHP_FUNCTION(filter_var_array) { zval *array_input = NULL, **op = NULL; + zend_bool add_empty = 1; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|Z", &array_input, &op) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|Zb", &array_input, &op, &add_empty) == FAILURE) { return; } @@ -887,7 +893,7 @@ PHP_FUNCTION(filter_var_array) RETURN_FALSE; } - php_filter_array_handler(array_input, op, return_value TSRMLS_CC); + php_filter_array_handler(array_input, op, return_value, add_empty TSRMLS_CC); } /* }}} */ diff --git a/ext/filter/logical_filters.c b/ext/filter/logical_filters.c index 6c98a84d7..777a2157c 100644 --- a/ext/filter/logical_filters.c +++ b/ext/filter/logical_filters.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: logical_filters.c 307678 2011-01-23 16:44:58Z iliaa $ */ +/* $Id: logical_filters.c 311403 2011-05-24 22:34:07Z felipe $ */ #include "php_filter.h" #include "filter_private.h" @@ -682,7 +682,7 @@ void php_filter_validate_ip(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */ RETURN_VALIDATION_FAILED } - if (flags & (FILTER_FLAG_IPV4 || FILTER_FLAG_IPV6)) { + if ((flags & FILTER_FLAG_IPV4) && (flags & FILTER_FLAG_IPV6)) { /* Both formats are cool */ } else if ((flags & FILTER_FLAG_IPV4) && mode == FORMAT_IPV6) { RETURN_VALIDATION_FAILED diff --git a/ext/filter/sanitizing_filters.c b/ext/filter/sanitizing_filters.c index d4293102d..d67920852 100644 --- a/ext/filter/sanitizing_filters.c +++ b/ext/filter/sanitizing_filters.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: sanitizing_filters.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: sanitizing_filters.c 309920 2011-04-03 16:30:31Z iliaa $ */ #include "php_filter.h" #include "filter_private.h" @@ -205,7 +205,11 @@ void php_filter_string(PHP_INPUT_FILTER_PARAM_DECL) if (new_len == 0) { zval_dtor(value); - ZVAL_EMPTY_STRING(value); + if (flags & FILTER_FLAG_EMPTY_STRING_NULL) { + ZVAL_NULL(value); + } else { + ZVAL_EMPTY_STRING(value); + } return; } } @@ -280,6 +284,9 @@ void php_filter_unsafe_raw(PHP_INPUT_FILTER_PARAM_DECL) } php_filter_encode_html(value, enc); + } else if (flags & FILTER_FLAG_EMPTY_STRING_NULL && Z_STRLEN_P(value) == 0) { + zval_dtor(value); + ZVAL_NULL(value); } } /* }}} */ diff --git a/ext/filter/tests/054.phpt b/ext/filter/tests/054.phpt new file mode 100644 index 000000000..c8a5dad75 --- /dev/null +++ b/ext/filter/tests/054.phpt @@ -0,0 +1,26 @@ +--TEST-- +filter_var_array() - using the add_empty option +--SKIPIF-- +<?php if (!extension_loaded("filter")) die("skip"); ?> +--FILE-- +<?php + +$data = array('foo' => 123); + +var_dump( + filter_var_array($data, array('foo' => array('filter' => FILTER_DEFAULT), 'bar' => array('filter' => FILTER_DEFAULT)), false), + filter_var_array($data, array('foo' => array('filter' => FILTER_DEFAULT), 'bar' => array('filter' => FILTER_DEFAULT))) +); + +?> +--EXPECT-- +array(1) { + ["foo"]=> + string(3) "123" +} +array(2) { + ["foo"]=> + string(3) "123" + ["bar"]=> + NULL +} diff --git a/ext/filter/tests/bug52209.phpt b/ext/filter/tests/bug52209.phpt index 49408ea12..bf2ed6cf3 100644 --- a/ext/filter/tests/bug52209.phpt +++ b/ext/filter/tests/bug52209.phpt @@ -2,6 +2,8 @@ Bug #52209 (INPUT_ENV returns NULL for set variables (CLI)) --SKIPIF-- <?php if (!extension_loaded("filter") || !empty($_ENV['PWD'])) die("skip"); ?> +--INI-- +variables_order=GPCSE --FILE-- <?php var_dump(filter_input(INPUT_ENV, 'PWD')); diff --git a/ext/filter/tests/bug53037.phpt b/ext/filter/tests/bug53037.phpt new file mode 100644 index 000000000..4a1e9e318 --- /dev/null +++ b/ext/filter/tests/bug53037.phpt @@ -0,0 +1,14 @@ +--TEST-- +Bug #53037 (FILTER_FLAG_EMPTY_STRING_NULL is not implemented) +--SKIPIF-- +<?php if (!extension_loaded("filter")) die("skip"); ?> +--FILE-- +<?php +var_dump( + filter_var("", FILTER_DEFAULT), + filter_var("", FILTER_DEFAULT, array('flags' => FILTER_FLAG_EMPTY_STRING_NULL)) +); +?> +--EXPECT-- +string(0) "" +NULL diff --git a/ext/ftp/php_ftp.c b/ext/ftp/php_ftp.c index 2859f43bc..3a1325f52 100644 --- a/ext/ftp/php_ftp.c +++ b/ext/ftp/php_ftp.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: php_ftp.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: php_ftp.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -278,7 +278,7 @@ const zend_function_entry php_ftp_functions[] = { PHP_FE(ftp_nb_put, arginfo_ftp_nb_put) PHP_FE(ftp_nb_fput, arginfo_ftp_nb_fput) PHP_FALIAS(ftp_quit, ftp_close, arginfo_ftp_close) - {NULL, NULL, NULL} + PHP_FE_END }; zend_module_entry php_ftp_module_entry = { diff --git a/ext/gd/config.m4 b/ext/gd/config.m4 index 1997594ab..e10cbab31 100644 --- a/ext/gd/config.m4 +++ b/ext/gd/config.m4 @@ -1,5 +1,5 @@ dnl -dnl $Id: config.m4 281216 2009-05-27 08:18:24Z pajoye $ +dnl $Id: config.m4 310964 2011-05-12 08:19:37Z rasmus $ dnl dnl @@ -72,11 +72,11 @@ AC_DEFUN([PHP_GD_JPEG],[ if test "$PHP_JPEG_DIR" != "no"; then for i in $PHP_JPEG_DIR /usr/local /usr; do - test -f $i/$PHP_LIBDIR/libjpeg.$SHLIB_SUFFIX_NAME || test -f $i/$PHP_LIBDIR/libjpeg.a && GD_JPEG_DIR=$i && break + test -f $i/include/jpeglib.h && GD_JPEG_DIR=$i && break done if test -z "$GD_JPEG_DIR"; then - AC_MSG_ERROR([libjpeg.(a|so) not found.]) + AC_MSG_ERROR([jpeglib.h not found.]) fi PHP_CHECK_LIBRARY(jpeg,jpeg_read_header, @@ -97,21 +97,17 @@ AC_DEFUN([PHP_GD_PNG],[ if test "$PHP_PNG_DIR" != "no"; then for i in $PHP_PNG_DIR /usr/local /usr; do - test -f $i/$PHP_LIBDIR/libpng.$SHLIB_SUFFIX_NAME || test -f $i/$PHP_LIBDIR/libpng.a && GD_PNG_DIR=$i && break + test -f $i/include/png.h && GD_PNG_DIR=$i && break done if test -z "$GD_PNG_DIR"; then - AC_MSG_ERROR([libpng.(a|so) not found.]) + AC_MSG_ERROR([png.h not found.]) fi if test "$PHP_ZLIB_DIR" = "no"; then AC_MSG_ERROR([PNG support requires ZLIB. Use --with-zlib-dir=<DIR>]) fi - if test ! -f $GD_PNG_DIR/include/png.h; then - AC_MSG_ERROR([png.h not found.]) - fi - PHP_CHECK_LIBRARY(png,png_write_image, [ PHP_ADD_INCLUDE($GD_PNG_DIR/include) diff --git a/ext/gd/gd.c b/ext/gd/gd.c index 4c5faacec..cc2ae4a16 100644 --- a/ext/gd/gd.c +++ b/ext/gd/gd.c @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: gd.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: gd.c 313665 2011-07-25 11:42:53Z felipe $ */ /* gd 1.2 is copyright 1994, 1995, Quest Protein Database Center, Cold Spring Harbor Labs. */ @@ -1045,7 +1045,7 @@ const zend_function_entry gd_functions[] = { PHP_FE(imagefilter, arginfo_imagefilter) PHP_FE(imageconvolution, arginfo_imageconvolution) - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ diff --git a/ext/gd/tests/imageloadfont_error1.phpt b/ext/gd/tests/imageloadfont_error1.phpt new file mode 100644 index 000000000..16d1a3c3a --- /dev/null +++ b/ext/gd/tests/imageloadfont_error1.phpt @@ -0,0 +1,15 @@ +--TEST-- +Testing that imageloadfont() breaks on non-string first parameter +--CREDITS-- +Neveo Harrison <neveoo [at] gmail [dot] com> #testfest #tek11 +--SKIPIF-- +<?php + if (!extension_loaded("gd")) die("skip GD not present"); +?> +--FILE-- +<?php +var_dump( imageloadfont(array()) ); +?> +--EXPECTF-- +Warning: imageloadfont() expects parameter 1 to be string, array given in %s on line %d +NULL
\ No newline at end of file diff --git a/ext/gd/tests/imageloadfont_error2.phpt b/ext/gd/tests/imageloadfont_error2.phpt new file mode 100644 index 000000000..459cb71f5 --- /dev/null +++ b/ext/gd/tests/imageloadfont_error2.phpt @@ -0,0 +1,15 @@ +--TEST-- +Testing that imageloadfont() breaks on invalid file passed as first argument +--CREDITS-- +Austin Drouare <austin.drouare [at] gmail [dot] com> #testfest #tek11 +--SKIPIF-- +<?php + if (!extension_loaded("gd")) die("skip GD not present"); +?> +--FILE-- +<?php +var_dump( imageloadfont('\src\invalidfile.font') ); +?> +--EXPECTF-- +Warning: imageloadfont(\src\invalidfile.font): failed to open stream: No such file or directory in %s on line %d +bool(false) diff --git a/ext/gettext/gettext.c b/ext/gettext/gettext.c index 8a8f49c41..d315ca46b 100644 --- a/ext/gettext/gettext.c +++ b/ext/gettext/gettext.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: gettext.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: gettext.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -112,7 +112,7 @@ const zend_function_entry php_gettext_functions[] = { #if HAVE_BIND_TEXTDOMAIN_CODESET PHP_NAMED_FE(bind_textdomain_codeset, zif_bind_textdomain_codeset, arginfo_bind_textdomain_codeset) #endif - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ diff --git a/ext/gmp/gmp.c b/ext/gmp/gmp.c index d1301512f..1045d5abd 100644 --- a/ext/gmp/gmp.c +++ b/ext/gmp/gmp.c @@ -280,7 +280,7 @@ const zend_function_entry gmp_functions[] = { ZEND_FE(gmp_popcount, arginfo_gmp_popcount) ZEND_FE(gmp_hamdist, arginfo_gmp_hamdist) ZEND_FE(gmp_nextprime, arginfo_gmp_nextprime) - {NULL, NULL, NULL} /* Must be the last line in gmp_functions[] */ + PHP_FE_END }; /* }}} */ diff --git a/ext/hash/hash.c b/ext/hash/hash.c index 9e5880859..ac1e07303 100644 --- a/ext/hash/hash.c +++ b/ext/hash/hash.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: hash.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: hash.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -1060,7 +1060,7 @@ const zend_function_entry hash_functions[] = { PHP_FE(mhash, arginfo_mhash) #endif - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ diff --git a/ext/iconv/iconv.c b/ext/iconv/iconv.c index fb9d12712..ea27b1944 100644 --- a/ext/iconv/iconv.c +++ b/ext/iconv/iconv.c @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: iconv.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: iconv.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -142,7 +142,7 @@ const zend_function_entry iconv_functions[] = { PHP_FE(iconv_mime_encode, arginfo_iconv_mime_encode) PHP_FE(iconv_mime_decode, arginfo_iconv_mime_decode) PHP_FE(iconv_mime_decode_headers, arginfo_iconv_mime_decode_headers) - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ diff --git a/ext/imap/php_imap.c b/ext/imap/php_imap.c index f10c69ba8..59ce0ce74 100644 --- a/ext/imap/php_imap.c +++ b/ext/imap/php_imap.c @@ -26,7 +26,7 @@ | PHP 4.0 updates: Zeev Suraski <zeev@zend.com> | +----------------------------------------------------------------------+ */ -/* $Id: php_imap.c 307691 2011-01-24 03:52:00Z stas $ */ +/* $Id: php_imap.c 314376 2011-08-06 14:47:44Z felipe $ */ #define IMAP41 @@ -551,14 +551,14 @@ const zend_function_entry imap_functions[] = { PHP_FALIAS(imap_scan, imap_listscan, arginfo_imap_listscan) PHP_FALIAS(imap_create, imap_createmailbox, arginfo_imap_createmailbox) PHP_FALIAS(imap_rename, imap_renamemailbox, arginfo_imap_renamemailbox) - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ /* {{{ imap dependencies */ static const zend_module_dep imap_deps[] = { ZEND_MOD_REQUIRED("standard") - {NULL, NULL, NULL} + ZEND_MOD_END }; /* }}} */ @@ -1233,7 +1233,7 @@ static void php_imap_do_open(INTERNAL_FUNCTION_PARAMETERS, int persistent) IMAPG(imap_password) = estrndup(passwd, passwd_len); #ifdef SET_MAXLOGINTRIALS - if (argc == 5) { + if (argc >= 5) { if (retries < 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING ,"Retries must be greater or equal to 0"); } else { @@ -4299,7 +4299,7 @@ PHP_FUNCTION(imap_mime_header_decode) charset_token = offset; } /* Return the rest of the data as unencoded, as it was either unencoded or was missing separators - which rendered the the remainder of the string impossible for us to decode. */ + which rendered the remainder of the string impossible for us to decode. */ memcpy(text, &string[charset_token], end - charset_token); /* Extract unencoded text from string */ text[end - charset_token] = 0x00; MAKE_STD_ZVAL(myobject); diff --git a/ext/interbase/ibase_query.c b/ext/interbase/ibase_query.c index a510814e5..b72721631 100644 --- a/ext/interbase/ibase_query.c +++ b/ext/interbase/ibase_query.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: ibase_query.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: ibase_query.c 313447 2011-07-19 20:25:51Z mariuz $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -672,14 +672,7 @@ static int _php_ibase_bind(XSQLDA *sqlda, zval ***b_vars, BIND_BUF *buf, /* {{{ if (! force_null) break; case IS_NULL: - - /* complain if this field doesn't allow NULL values */ - if (! (var->sqltype & 1)) { - _php_ibase_module_error("Parameter %d: non-empty value required" TSRMLS_CC, i+1); - rv = FAILURE; - } else { buf[i].sqlind = -1; - } if (var->sqltype & SQL_ARRAY) ++array_cnt; diff --git a/ext/interbase/interbase.c b/ext/interbase/interbase.c index 39964cb0d..a1bed0157 100644 --- a/ext/interbase/interbase.c +++ b/ext/interbase/interbase.c @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: interbase.c 308618 2011-02-24 02:42:38Z felipe $ */ +/* $Id: interbase.c 313830 2011-07-28 10:39:19Z pajoye $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -441,7 +441,7 @@ const zend_function_entry ibase_functions[] = { PHP_FALIAS(fbird_wait_event, ibase_wait_event, arginfo_ibase_wait_event) PHP_FALIAS(fbird_set_event_handler, ibase_set_event_handler, arginfo_ibase_set_event_handler) PHP_FALIAS(fbird_free_event_handler, ibase_free_event_handler, arginfo_ibase_free_event_handler) - {NULL, NULL, NULL} + PHP_FE_END }; zend_module_entry ibase_module_entry = { @@ -998,9 +998,12 @@ static void _php_ibase_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) /* ZEND_REGISTER_RESOURCE(return_value, ib_link, le_link); } else { zend_rsrc_list_entry new_le; - + ib_link = (ibase_db_link *) malloc(sizeof(ibase_db_link)); - + if (!ib_link) { + RETURN_FALSE; + } + /* hash it up */ Z_TYPE(new_le) = le_plink; new_le.ptr = ib_link; diff --git a/ext/interbase/php_ibase_includes.h b/ext/interbase/php_ibase_includes.h index c2c125600..071d9447f 100755 --- a/ext/interbase/php_ibase_includes.h +++ b/ext/interbase/php_ibase_includes.h @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: php_ibase_includes.h 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: php_ibase_includes.h 311341 2011-05-22 19:06:21Z felipe $ */ #ifndef PHP_IBASE_INCLUDES_H #define PHP_IBASE_INCLUDES_H @@ -51,7 +51,7 @@ extern int le_link, le_plink, le_trans; #define LE_PLINK "Firebird/InterBase persistent link" #define LE_TRANS "Firebird/InterBase transaction" -#define IBASE_MSGSIZE 256 +#define IBASE_MSGSIZE 512 #define MAX_ERRMSG (IBASE_MSGSIZE*2) #define IB_DEF_DATE_FMT "%Y-%m-%d" diff --git a/ext/intl/collator/collator_class.c b/ext/intl/collator/collator_class.c index 58a11fe46..38b2e978c 100755 --- a/ext/intl/collator/collator_class.c +++ b/ext/intl/collator/collator_class.c @@ -127,7 +127,7 @@ function_entry Collator_class_functions[] = { PHP_NAMED_FE( getErrorCode, ZEND_FN( collator_get_error_code ), collator_0_args ) PHP_NAMED_FE( getErrorMessage, ZEND_FN( collator_get_error_message ), collator_0_args ) PHP_NAMED_FE( getSortKey, ZEND_FN( collator_get_sort_key ), collator_2_args ) - { NULL, NULL, NULL } + PHP_FE_END }; /* }}} */ diff --git a/ext/intl/collator/collator_compare.c b/ext/intl/collator/collator_compare.c index 4e7b3a7d4..840855661 100755 --- a/ext/intl/collator/collator_compare.c +++ b/ext/intl/collator/collator_compare.c @@ -74,7 +74,9 @@ PHP_FUNCTION( collator_compare ) /* Set error messages. */ intl_errors_set_custom_msg( COLLATOR_ERROR_P( co ), "Error converting first argument to UTF-16", 0 TSRMLS_CC ); - efree( ustr1 ); + if (ustr1) { + efree( ustr1 ); + } RETURN_FALSE; } @@ -88,8 +90,12 @@ PHP_FUNCTION( collator_compare ) /* Set error messages. */ intl_errors_set_custom_msg( COLLATOR_ERROR_P( co ), "Error converting second argument to UTF-16", 0 TSRMLS_CC ); - efree( ustr1 ); - efree( ustr2 ); + if (ustr1) { + efree( ustr1 ); + } + if (ustr2) { + efree( ustr2 ); + } RETURN_FALSE; } diff --git a/ext/intl/dateformat/dateformat_attr.c b/ext/intl/dateformat/dateformat_attr.c index 7c6cef953..6131cedc9 100755 --- a/ext/intl/dateformat/dateformat_attr.c +++ b/ext/intl/dateformat/dateformat_attr.c @@ -276,7 +276,9 @@ PHP_FUNCTION( datefmt_set_pattern ) udat_applyPattern(DATE_FORMAT_OBJECT(dfo), (UBool)is_pattern_localized, svalue, slength); - efree(svalue); + if (svalue) { + efree(svalue); + } INTL_METHOD_CHECK_STATUS(dfo, "Error setting symbol value"); RETURN_TRUE; diff --git a/ext/intl/dateformat/dateformat_class.c b/ext/intl/dateformat/dateformat_class.c index 74c193bc0..eb3f5f4e7 100755 --- a/ext/intl/dateformat/dateformat_class.c +++ b/ext/intl/dateformat/dateformat_class.c @@ -170,7 +170,7 @@ static function_entry IntlDateFormatter_class_functions[] = { PHP_NAMED_FE( localtime, ZEND_FN( datefmt_localtime ), datefmt_parse_args ) PHP_NAMED_FE( getErrorCode, ZEND_FN( datefmt_get_error_code ), arginfo_intldateformatter_getdatetype ) PHP_NAMED_FE( getErrorMessage, ZEND_FN( datefmt_get_error_message ), arginfo_intldateformatter_getdatetype ) - { NULL, NULL, NULL } + PHP_FE_END }; /* }}} */ diff --git a/ext/intl/formatter/formatter_attr.c b/ext/intl/formatter/formatter_attr.c index 46d61fb1f..b306bbede 100755 --- a/ext/intl/formatter/formatter_attr.c +++ b/ext/intl/formatter/formatter_attr.c @@ -233,7 +233,9 @@ PHP_FUNCTION( numfmt_set_text_attribute ) /* Actually set new attribute value. */ unum_setTextAttribute(FORMATTER_OBJECT(nfo), attribute, svalue, slength, &INTL_DATA_ERROR_CODE(nfo)); - efree(svalue); + if (svalue) { + efree(svalue); + } INTL_METHOD_CHECK_STATUS( nfo, "Error setting text attribute" ); RETURN_TRUE; @@ -326,7 +328,9 @@ PHP_FUNCTION( numfmt_set_symbol ) /* Actually set the symbol. */ unum_setSymbol(FORMATTER_OBJECT(nfo), symbol, svalue, slength, &INTL_DATA_ERROR_CODE(nfo)); - efree(svalue); + if (svalue) { + efree(svalue); + } INTL_METHOD_CHECK_STATUS( nfo, "Error setting symbol value" ); RETURN_TRUE; @@ -406,7 +410,9 @@ PHP_FUNCTION( numfmt_set_pattern ) /* TODO: add parse error information */ unum_applyPattern(FORMATTER_OBJECT(nfo), 0, svalue, slength, NULL, &INTL_DATA_ERROR_CODE(nfo)); - efree(svalue); + if (svalue) { + efree(svalue); + } INTL_METHOD_CHECK_STATUS( nfo, "Error setting pattern value" ); RETURN_TRUE; diff --git a/ext/intl/formatter/formatter_class.c b/ext/intl/formatter/formatter_class.c index 07d07b64c..0bb5894f0 100755 --- a/ext/intl/formatter/formatter_class.c +++ b/ext/intl/formatter/formatter_class.c @@ -179,7 +179,7 @@ static function_entry NumberFormatter_class_functions[] = { PHP_NAMED_FE( getLocale, ZEND_FN( numfmt_get_locale ), arginfo_numberformatter_getlocale ) PHP_NAMED_FE( getErrorCode, ZEND_FN( numfmt_get_error_code ), arginfo_numberformatter_getpattern ) PHP_NAMED_FE( getErrorMessage, ZEND_FN( numfmt_get_error_message ), arginfo_numberformatter_getpattern ) - { NULL, NULL, NULL } + PHP_FE_END }; /* }}} */ diff --git a/ext/intl/formatter/formatter_parse.c b/ext/intl/formatter/formatter_parse.c index b73095d3e..cbdde855d 100755 --- a/ext/intl/formatter/formatter_parse.c +++ b/ext/intl/formatter/formatter_parse.c @@ -107,7 +107,9 @@ PHP_FUNCTION( numfmt_parse ) ZVAL_LONG(zposition, position); } - efree(sstr); + if (sstr) { + efree(sstr); + } INTL_METHOD_CHECK_STATUS( nfo, "Number parsing failed" ); } @@ -161,7 +163,9 @@ PHP_FUNCTION( numfmt_parse_currency ) zval_dtor(zposition); ZVAL_LONG(zposition, position); } - efree(sstr); + if (sstr) { + efree(sstr); + } INTL_METHOD_CHECK_STATUS( nfo, "Number parsing failed" ); /* Convert parsed currency to UTF-8 and pass it back to caller. */ diff --git a/ext/intl/grapheme/grapheme_string.c b/ext/intl/grapheme/grapheme_string.c index 1784d028a..719015fe6 100755 --- a/ext/intl/grapheme/grapheme_string.c +++ b/ext/intl/grapheme/grapheme_string.c @@ -84,13 +84,17 @@ PHP_FUNCTION(grapheme_strlen) /* Set error messages. */ intl_error_set_custom_msg( NULL, "Error converting input string to UTF-16", 0 TSRMLS_CC ); - efree( ustring ); + if (ustring) { + efree( ustring ); + } RETURN_NULL(); } ret_len = grapheme_split_string(ustring, ustring_len, NULL, 0 TSRMLS_CC ); - efree( ustring ); + if (ustring) { + efree( ustring ); + } if (ret_len >= 0) { RETVAL_LONG(ret_len); @@ -447,7 +451,9 @@ PHP_FUNCTION(grapheme_substr) /* Set error messages. */ intl_error_set_custom_msg( NULL, "Error converting input string to UTF-16", 0 TSRMLS_CC ); - efree( ustr ); + if (ustr) { + efree( ustr ); + } RETURN_FALSE; } @@ -485,7 +491,9 @@ PHP_FUNCTION(grapheme_substr) intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_substr: start not contained in string", 1 TSRMLS_CC ); - efree(ustr); + if (ustr) { + efree(ustr); + } ubrk_close(bi); RETURN_FALSE; } @@ -499,7 +507,9 @@ PHP_FUNCTION(grapheme_substr) status = U_ZERO_ERROR; intl_convert_utf16_to_utf8((char **)&sub_str, &sub_str_len, ustr + sub_str_start_pos, ustr_len - sub_str_start_pos, &status); - efree( ustr ); + if (ustr) { + efree( ustr ); + } ubrk_close( bi ); if ( U_FAILURE( status ) ) { @@ -509,7 +519,9 @@ PHP_FUNCTION(grapheme_substr) /* Set error messages. */ intl_error_set_custom_msg( NULL, "Error converting output string to UTF-8", 0 TSRMLS_CC ); - efree( sub_str ); + if (sub_str) { + efree( sub_str ); + } RETURN_FALSE; } @@ -665,7 +677,7 @@ PHP_FUNCTION(grapheme_stristr) /* }}} */ /* {{{ grapheme_extract_charcount_iter - grapheme iterator for grapheme_extract MAXCHARS */ -inline int32_t +static inline int32_t grapheme_extract_charcount_iter(UBreakIterator *bi, int32_t csize, unsigned char *pstr, int32_t str_len) { int pos = 0, prev_pos = 0; @@ -702,7 +714,7 @@ grapheme_extract_charcount_iter(UBreakIterator *bi, int32_t csize, unsigned char /* }}} */ /* {{{ grapheme_extract_bytecount_iter - grapheme iterator for grapheme_extract MAXBYTES */ -inline int32_t +static inline int32_t grapheme_extract_bytecount_iter(UBreakIterator *bi, int32_t bsize, unsigned char *pstr, int32_t str_len) { int pos = 0, prev_pos = 0; @@ -736,7 +748,7 @@ grapheme_extract_bytecount_iter(UBreakIterator *bi, int32_t bsize, unsigned char /* }}} */ /* {{{ grapheme_extract_count_iter - grapheme iterator for grapheme_extract COUNT */ -inline int32_t +static inline int32_t grapheme_extract_count_iter(UBreakIterator *bi, int32_t size, unsigned char *pstr, int32_t str_len) { int pos = 0, next_pos = 0; @@ -897,7 +909,9 @@ PHP_FUNCTION(grapheme_extract) ret_pos = (*grapheme_extract_iters[extract_type])(bi, size, pstr, str_len); - efree(ustr); + if (ustr) { + efree(ustr); + } ubrk_close(bi); if ( NULL != next ) { diff --git a/ext/intl/grapheme/grapheme_util.c b/ext/intl/grapheme/grapheme_util.c index 1978d274a..92008554d 100755 --- a/ext/intl/grapheme/grapheme_util.c +++ b/ext/intl/grapheme/grapheme_util.c @@ -170,7 +170,9 @@ grapheme_strrpos_utf16(unsigned char *haystack, int32_t haystack_len, unsigned c /* Set error messages. */ intl_error_set_custom_msg( NULL, "Error converting input string to UTF-16", 0 TSRMLS_CC ); - efree( uhaystack ); + if (uhaystack) { + efree( uhaystack ); + } return -1; } @@ -187,7 +189,9 @@ grapheme_strrpos_utf16(unsigned char *haystack, int32_t haystack_len, unsigned c if ( NULL == puhaystack ) { intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_strpos: Offset not contained in string", 1 TSRMLS_CC ); - efree( uhaystack ); + if (uhaystack) { + efree( uhaystack ); + } ubrk_close (bi); return -1; } @@ -203,8 +207,12 @@ grapheme_strrpos_utf16(unsigned char *haystack, int32_t haystack_len, unsigned c /* Set error messages. */ intl_error_set_custom_msg( NULL, "Error converting input string to UTF-16", 0 TSRMLS_CC ); - efree( uhaystack ); - efree( uneedle ); + if (uhaystack) { + efree( uhaystack ); + } + if (uneedle) { + efree( uneedle ); + } ubrk_close (bi); return -1; } @@ -260,8 +268,12 @@ grapheme_strrpos_utf16(unsigned char *haystack, int32_t haystack_len, unsigned c } exit: - efree( uhaystack ); - efree( uneedle ); + if (uhaystack) { + efree( uhaystack ); + } + if (uneedle) { + efree( uneedle ); + } ubrk_close (bi); return ret_pos; @@ -295,7 +307,9 @@ grapheme_strpos_utf16(unsigned char *haystack, int32_t haystack_len, unsigned ch /* Set error messages. */ intl_error_set_custom_msg( NULL, "Error converting input string to UTF-16", 0 TSRMLS_CC ); - efree( uhaystack ); + if (uhaystack) { + efree( uhaystack ); + } return -1; } @@ -310,8 +324,9 @@ grapheme_strpos_utf16(unsigned char *haystack, int32_t haystack_len, unsigned ch if ( NULL == puhaystack ) { intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_strpos: Offset not contained in string", 1 TSRMLS_CC ); - - efree( uhaystack ); + if (uhaystack) { + efree( uhaystack ); + } ubrk_close (bi); return -1; @@ -332,8 +347,12 @@ grapheme_strpos_utf16(unsigned char *haystack, int32_t haystack_len, unsigned ch /* Set error messages. */ intl_error_set_custom_msg( NULL, "Error converting input string to UTF-16", 0 TSRMLS_CC ); - efree( uhaystack ); - efree( uneedle ); + if (uhaystack) { + efree( uhaystack ); + } + if (uneedle) { + efree( uneedle ); + } ubrk_close (bi); return -1; @@ -347,8 +366,12 @@ grapheme_strpos_utf16(unsigned char *haystack, int32_t haystack_len, unsigned ch *puchar_pos = ubrk_current(bi); - efree( uhaystack ); - efree( uneedle ); + if (uhaystack) { + efree( uhaystack ); + } + if (uneedle) { + efree( uneedle ); + } ubrk_close (bi); return ret_pos; @@ -409,7 +432,7 @@ int grapheme_split_string(const UChar *text, int32_t text_length, int boundary_a /* }}} */ /* {{{ grapheme_count_graphemes */ -inline int32_t +int32_t grapheme_count_graphemes(UBreakIterator *bi, UChar *string, int32_t string_len) { int ret_len = 0; @@ -433,7 +456,7 @@ grapheme_count_graphemes(UBreakIterator *bi, UChar *string, int32_t string_len) /* }}} */ /* {{{ grapheme_memnstr_grapheme: find needle in haystack using grapheme boundaries */ -inline int32_t +int32_t grapheme_memnstr_grapheme(UBreakIterator *bi, UChar *haystack, UChar *needle, int32_t needle_len, UChar *end) { UChar *p = haystack; diff --git a/ext/intl/grapheme/grapheme_util.h b/ext/intl/grapheme/grapheme_util.h index f8207cac5..675c6481b 100755 --- a/ext/intl/grapheme/grapheme_util.h +++ b/ext/intl/grapheme/grapheme_util.h @@ -36,10 +36,10 @@ int grapheme_ascii_check(const unsigned char *day, int32_t len); int grapheme_split_string(const UChar *text, int32_t text_length, int boundary_array[], int boundary_array_len TSRMLS_DC ); -inline int32_t +int32_t grapheme_count_graphemes(UBreakIterator *bi, UChar *string, int32_t string_len); -inline int32_t +int32_t grapheme_memnstr_grapheme(UBreakIterator *bi, UChar *haystack, UChar *needle, int32_t needle_len, UChar *end); inline void *grapheme_memrchr_grapheme(const void *s, int c, int32_t n); diff --git a/ext/intl/idn/idn.c b/ext/intl/idn/idn.c index 728172a8b..e2b4a30d5 100644 --- a/ext/intl/idn/idn.c +++ b/ext/intl/idn/idn.c @@ -15,7 +15,7 @@ | Author: Pierre A. Joye <pierre@php.net> | +----------------------------------------------------------------------+ */ -/* $Id: idn.c 292566 2009-12-23 21:41:05Z stas $ */ +/* $Id: idn.c 314218 2011-08-04 00:59:43Z felipe $ */ /* {{{ includes */ #ifdef HAVE_CONFIG_H @@ -85,7 +85,9 @@ static void php_intl_idn_to(INTERNAL_FUNCTION_PARAMETERS, int mode) /* Set error messages. */ intl_error_set_custom_msg( NULL, "Error converting input string to UTF-16", 0 TSRMLS_CC ); - efree(ustring); + if (ustring) { + efree(ustring); + } RETURN_FALSE; } else { UParseError parse_error; diff --git a/ext/intl/locale/locale_class.c b/ext/intl/locale/locale_class.c index 7cde68892..3e70d1897 100755 --- a/ext/intl/locale/locale_class.c +++ b/ext/intl/locale/locale_class.c @@ -14,7 +14,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: locale_class.c 269153 2008-11-17 11:28:01Z felipe $ */ +/* $Id: locale_class.c 314352 2011-08-06 01:22:27Z felipe $ */ #include <unicode/uloc.h> #include "php_intl.h" @@ -85,7 +85,7 @@ function_entry Locale_class_functions[] = { ZEND_FENTRY( lookup, ZEND_FN( locale_lookup ), locale_4_args, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC ) ZEND_FENTRY( canonicalize, ZEND_FN( locale_canonicalize ), locale_1_arg , ZEND_ACC_PUBLIC|ZEND_ACC_STATIC ) ZEND_FENTRY( acceptFromHttp, ZEND_FN( locale_accept_from_http ), locale_1_arg , ZEND_ACC_PUBLIC|ZEND_ACC_STATIC ) - { NULL, NULL, NULL } + PHP_FE_END }; /* }}} */ diff --git a/ext/intl/msgformat/msgformat_attr.c b/ext/intl/msgformat/msgformat_attr.c index 47350b2b4..cf3466514 100755 --- a/ext/intl/msgformat/msgformat_attr.c +++ b/ext/intl/msgformat/msgformat_attr.c @@ -90,7 +90,9 @@ PHP_FUNCTION( msgfmt_set_pattern ) /* TODO: add parse error information */ umsg_applyPattern(MSG_FORMAT_OBJECT(mfo), spattern, spattern_len, NULL, &INTL_DATA_ERROR_CODE(mfo)); - efree(spattern); + if (spattern) { + efree(spattern); + } INTL_METHOD_CHECK_STATUS(mfo, "Error setting symbol value"); if(mfo->mf_data.orig_format) { diff --git a/ext/intl/msgformat/msgformat_class.c b/ext/intl/msgformat/msgformat_class.c index bd291291e..7ed28df3d 100755 --- a/ext/intl/msgformat/msgformat_class.c +++ b/ext/intl/msgformat/msgformat_class.c @@ -144,7 +144,7 @@ static function_entry MessageFormatter_class_functions[] = { PHP_NAMED_FE( getLocale, ZEND_FN( msgfmt_get_locale ), arginfo_messageformatter_geterrormessage ) PHP_NAMED_FE( getErrorCode, ZEND_FN( msgfmt_get_error_code ), arginfo_messageformatter_geterrormessage ) PHP_NAMED_FE( getErrorMessage, ZEND_FN( msgfmt_get_error_message ), arginfo_messageformatter_geterrormessage ) - { NULL, NULL, NULL } + PHP_FE_END }; /* }}} */ diff --git a/ext/intl/msgformat/msgformat_parse.c b/ext/intl/msgformat/msgformat_parse.c index 61c5213f0..8393d4c6e 100755 --- a/ext/intl/msgformat/msgformat_parse.c +++ b/ext/intl/msgformat/msgformat_parse.c @@ -40,7 +40,9 @@ static void msgfmt_do_parse(MessageFormatter_object *mfo, char *source, int src_ INTL_METHOD_CHECK_STATUS(mfo, "Converting parse string failed"); umsg_parse_helper(MSG_FORMAT_OBJECT(mfo), &count, &fargs, usource, usrc_len, &INTL_DATA_ERROR_CODE(mfo)); - efree(usource); + if (usource) { + efree(usource); + } INTL_METHOD_CHECK_STATUS(mfo, "Parsing failed"); array_init(return_value); diff --git a/ext/intl/normalizer/normalizer_class.c b/ext/intl/normalizer/normalizer_class.c index 75894fffc..c5adf781a 100755 --- a/ext/intl/normalizer/normalizer_class.c +++ b/ext/intl/normalizer/normalizer_class.c @@ -44,7 +44,7 @@ ZEND_END_ARG_INFO() function_entry Normalizer_class_functions[] = { ZEND_FENTRY( normalize, ZEND_FN( normalizer_normalize ), normalizer_3_args, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC ) ZEND_FENTRY( isNormalized, ZEND_FN( normalizer_is_normalized ), normalizer_3_args, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC ) - { NULL, NULL, NULL } + PHP_FE_END }; /* }}} */ diff --git a/ext/intl/normalizer/normalizer_normalize.c b/ext/intl/normalizer/normalizer_normalize.c index 482c4a01e..466ab97e1 100755 --- a/ext/intl/normalizer/normalizer_normalize.c +++ b/ext/intl/normalizer/normalizer_normalize.c @@ -96,7 +96,9 @@ PHP_FUNCTION( normalizer_normalize ) /* Set error messages. */ intl_error_set_custom_msg( NULL, "Error converting input string to UTF-16", 0 TSRMLS_CC ); - efree( uinput ); + if (uinput) { + efree( uinput ); + } RETURN_FALSE; } @@ -220,7 +222,9 @@ PHP_FUNCTION( normalizer_is_normalized ) /* Set error messages. */ intl_error_set_custom_msg( NULL, "Error converting string to UTF-16.", 0 TSRMLS_CC ); - efree( uinput ); + if (uinput) { + efree( uinput ); + } RETURN_FALSE; } diff --git a/ext/intl/php_intl.c b/ext/intl/php_intl.c index a9bfefd14..dc6c0fffa 100755 --- a/ext/intl/php_intl.c +++ b/ext/intl/php_intl.c @@ -487,7 +487,7 @@ zend_function_entry intl_functions[] = { PHP_FE( intl_is_failure, intl_1_arg ) PHP_FE( intl_error_name, intl_1_arg ) - { NULL, NULL, NULL } + PHP_FE_END }; /* }}} */ @@ -543,6 +543,10 @@ PHP_MINIT_FUNCTION( intl ) REGISTER_INI_ENTRIES(); REGISTER_LONG_CONSTANT("INTL_MAX_LOCALE_LEN", INTL_MAX_LOCALE_LEN, CONST_CS); + REGISTER_STRING_CONSTANT("INTL_ICU_VERSION", U_ICU_VERSION, CONST_PERSISTENT | CONST_CS); +#ifdef U_ICU_DATA_VERSION + REGISTER_STRING_CONSTANT("INTL_ICU_DATA_VERSION", U_ICU_DATA_VERSION, CONST_PERSISTENT | CONST_CS); +#endif /* Register 'Collator' PHP class */ collator_register_Collator_class( TSRMLS_C ); @@ -647,6 +651,9 @@ PHP_MINFO_FUNCTION( intl ) php_info_print_table_header( 2, "Internationalization support", "enabled" ); php_info_print_table_row( 2, "version", INTL_MODULE_VERSION ); php_info_print_table_row( 2, "ICU version", U_ICU_VERSION ); +#ifdef U_ICU_DATA_VERSION + php_info_print_table_row( 2, "ICU Data version", U_ICU_DATA_VERSION ); +#endif php_info_print_table_end(); /* For the default locale php.ini setting */ diff --git a/ext/intl/resourcebundle/resourcebundle_class.c b/ext/intl/resourcebundle/resourcebundle_class.c index a8a4bf1c8..3bf3468e3 100644 --- a/ext/intl/resourcebundle/resourcebundle_class.c +++ b/ext/intl/resourcebundle/resourcebundle_class.c @@ -102,7 +102,11 @@ static void resourcebundle_ctor(INTERNAL_FUNCTION_PARAMETERS) INTL_CHECK_LOCALE_LEN_OBJ(locale_len, return_value); - rb->me = ures_open(bundlename, locale, &INTL_DATA_ERROR_CODE(rb)); + if (fallback) { + rb->me = ures_open(bundlename, locale, &INTL_DATA_ERROR_CODE(rb)); + } else { + rb->me = ures_openDirect(bundlename, locale, &INTL_DATA_ERROR_CODE(rb)); + } INTL_CTOR_CHECK_STATUS(rb, "resourcebundle_ctor: Cannot load libICU resource bundle"); @@ -395,7 +399,7 @@ static function_entry ResourceBundle_class_functions[] = { ZEND_NAMED_ME( getLocales, ZEND_FN(resourcebundle_locales), arginfo_resourcebundle_getlocales, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC ) ZEND_NAMED_ME( getErrorCode, ZEND_FN(resourcebundle_get_error_code), arginfo_resourcebundle_get_error_code, ZEND_ACC_PUBLIC ) ZEND_NAMED_ME( getErrorMessage, ZEND_FN(resourcebundle_get_error_message), arginfo_resourcebundle_get_error_message, ZEND_ACC_PUBLIC ) - { NULL, NULL, NULL } + PHP_FE_END }; /* }}} */ diff --git a/ext/intl/resourcebundle/resourcebundle_class.h b/ext/intl/resourcebundle/resourcebundle_class.h index 65330dd16..4755d723b 100644 --- a/ext/intl/resourcebundle/resourcebundle_class.h +++ b/ext/intl/resourcebundle/resourcebundle_class.h @@ -20,6 +20,7 @@ #include <unicode/ures.h> #include <zend.h> +#include "php.h" #include "intl_error.h" diff --git a/ext/intl/tests/intl_icu_data_version_constant.phpt b/ext/intl/tests/intl_icu_data_version_constant.phpt new file mode 100644 index 000000000..ad0121858 --- /dev/null +++ b/ext/intl/tests/intl_icu_data_version_constant.phpt @@ -0,0 +1,10 @@ +--TEST-- +INTL_ICU_DATA_VERSION constant +--SKIPIF-- +<?php if( !extension_loaded( 'intl' ) || version_compare('INTL_ICU_VERSION', '4.4', '<') ) print 'skip'; ?> +--FILE-- +<?php +var_dump(defined("INTL_ICU_DATA_VERSION")); +?> +--EXPECT-- +bool(true) diff --git a/ext/intl/tests/intl_icu_version_constant.phpt b/ext/intl/tests/intl_icu_version_constant.phpt new file mode 100644 index 000000000..13160693c --- /dev/null +++ b/ext/intl/tests/intl_icu_version_constant.phpt @@ -0,0 +1,10 @@ +--TEST-- +INTL_ICU_VERSION constant +--SKIPIF-- +<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?> +--FILE-- +<?php +var_dump(defined("INTL_ICU_VERSION")); +?> +--EXPECT-- +bool(true) diff --git a/ext/intl/tests/resourcebundle_create.phpt b/ext/intl/tests/resourcebundle_create.phpt index 4d96d3eff..2bf4f556a 100644 --- a/ext/intl/tests/resourcebundle_create.phpt +++ b/ext/intl/tests/resourcebundle_create.phpt @@ -57,6 +57,6 @@ ResourceBundle Object -127: U_USING_DEFAULT_WARNING NULL - -127: resourcebundle_ctor: Cannot load libICU resource '%s/resourcebundle' without fallback from en_US to root: U_USING_DEFAULT_WARNING + 2: resourcebundle_ctor: Cannot load libICU resource bundle: U_MISSING_RESOURCE_ERROR NULL 2: resourcebundle_ctor: Cannot load libICU resource bundle: U_MISSING_RESOURCE_ERROR diff --git a/ext/intl/tests/resourcebundle_internal.phpt b/ext/intl/tests/resourcebundle_internal.phpt new file mode 100644 index 000000000..fe90081e7 --- /dev/null +++ b/ext/intl/tests/resourcebundle_internal.phpt @@ -0,0 +1,19 @@ +--TEST-- +Test ResourceBundle::__construct() with internal ICU bundles +--SKIPIF-- +<?php if( !extension_loaded( 'intl' ) || !defined('INTL_ICU_DATA_VERSION') || version_compare(INTL_ICU_DATA_VERSION, '4.4', '<') ) print 'skip'; ?> +--FILE-- +<?php +$b = new ResourceBundle('de_DE', 'ICUDATA-region'); +var_dump($b->get('Countries')->get('DE')); + +$b = new ResourceBundle('icuver', 'ICUDATA'); +var_dump($b->get('ICUVersion') !== NULL); + +$b = new ResourceBundle('supplementalData', 'ICUDATA', false); +var_dump($b->get('cldrVersion') !== NULL); +?> +--EXPECTF-- +string(11) "Deutschland" +bool(true) +bool(true) diff --git a/ext/json/json.c b/ext/json/json.c index 8fa67ed10..56b8e3c3f 100644 --- a/ext/json/json.c +++ b/ext/json/json.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: json.c 308529 2011-02-21 08:09:02Z scottmac $ */ +/* $Id: json.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -60,7 +60,7 @@ static const function_entry json_functions[] = { PHP_FE(json_encode, arginfo_json_encode) PHP_FE(json_decode, arginfo_json_decode) PHP_FE(json_last_error, arginfo_json_last_error) - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ @@ -589,6 +589,8 @@ static PHP_FUNCTION(json_decode) return; } + JSON_G(error_code) = 0; + if (!str_len) { RETURN_NULL(); } diff --git a/ext/json/tests/bug54484.phpt b/ext/json/tests/bug54484.phpt new file mode 100644 index 000000000..d698ab541 --- /dev/null +++ b/ext/json/tests/bug54484.phpt @@ -0,0 +1,25 @@ +--TEST-- +Bug #54484 (Empty string in json_decode doesn't reset json_last_error) +--SKIPIF-- +<?php if (!extension_loaded("json")) print "skip"; ?> +--FILE-- +<?php +json_decode('{"test":"test"}'); +var_dump(json_last_error()); + +json_decode(""); +var_dump(json_last_error()); + + +json_decode("invalid json"); +var_dump(json_last_error()); + + +json_decode(""); +var_dump(json_last_error()); +?> +--EXPECT-- +int(0) +int(0) +int(4) +int(0) diff --git a/ext/ldap/config.m4 b/ext/ldap/config.m4 index 9d8086232..7c674b151 100644 --- a/ext/ldap/config.m4 +++ b/ext/ldap/config.m4 @@ -1,5 +1,5 @@ dnl -dnl $Id: config.m4 242949 2007-09-26 15:44:16Z cvs2svn $ +dnl $Id: config.m4 309398 2011-03-18 18:47:09Z geissert $ dnl AC_DEFUN([PHP_LDAP_CHECKS], [ @@ -50,7 +50,7 @@ AC_DEFUN([PHP_LDAP_SASL_CHECKS], [ SASL_LIB="-L$LDAP_SASL_LIBDIR -lsasl2" fi - PHP_CHECK_LIBRARY(ldap, sasl_version, + PHP_CHECK_LIBRARY(sasl2, sasl_version, [ PHP_ADD_INCLUDE($LDAP_SASL_INCDIR) PHP_ADD_LIBRARY_WITH_PATH(sasl2, $LDAP_SASL_LIBDIR, LDAP_SHARED_LIBADD) diff --git a/ext/ldap/ldap.c b/ext/ldap/ldap.c index 4b609a361..599afccca 100644 --- a/ext/ldap/ldap.c +++ b/ext/ldap/ldap.c @@ -23,7 +23,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: ldap.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: ldap.c 313665 2011-07-25 11:42:53Z felipe $ */ #define IS_EXT_MODULE #ifdef HAVE_CONFIG_H @@ -226,7 +226,7 @@ PHP_MINFO_FUNCTION(ldap) php_info_print_table_start(); php_info_print_table_row(2, "LDAP Support", "enabled"); - php_info_print_table_row(2, "RCS Version", "$Id: ldap.c 306939 2011-01-01 02:19:59Z felipe $"); + php_info_print_table_row(2, "RCS Version", "$Id: ldap.c 313665 2011-07-25 11:42:53Z felipe $"); if (LDAPG(max_links) == -1) { snprintf(tmp, 31, "%ld/unlimited", LDAPG(num_links)); @@ -2506,7 +2506,7 @@ const zend_function_entry ldap_functions[] = { PHP_FE(ldap_8859_to_t61, arginfo_ldap_8859_to_t61) #endif - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c index 7fcfafdc1..ea07e8878 100644 --- a/ext/libxml/libxml.c +++ b/ext/libxml/libxml.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: libxml.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: libxml.c 313665 2011-07-25 11:42:53Z felipe $ */ #define IS_EXT_MODULE @@ -119,7 +119,7 @@ static const zend_function_entry libxml_functions[] = { PHP_FE(libxml_clear_errors, arginfo_libxml_clear_errors) PHP_FE(libxml_get_errors, arginfo_libxml_get_errors) PHP_FE(libxml_disable_entity_loader, arginfo_libxml_disable_entity_loader) - {NULL, NULL, NULL} + PHP_FE_END }; zend_module_entry libxml_module_entry = { @@ -222,6 +222,7 @@ static void php_libxml_node_free_list(xmlNodePtr node TSRMLS_DC) switch (node->type) { /* Skip property freeing for the following types */ case XML_NOTATION_NODE: + case XML_ENTITY_DECL: break; case XML_ENTITY_REF_NODE: php_libxml_node_free_list((xmlNodePtr) node->properties TSRMLS_CC); @@ -233,7 +234,6 @@ static void php_libxml_node_free_list(xmlNodePtr node TSRMLS_DC) case XML_ATTRIBUTE_DECL: case XML_DTD_NODE: case XML_DOCUMENT_TYPE_NODE: - case XML_ENTITY_DECL: case XML_NAMESPACE_DECL: case XML_TEXT_NODE: php_libxml_node_free_list(node->children TSRMLS_CC); @@ -310,9 +310,7 @@ static void *php_libxml_streams_IO_open_wrapper(const char *filename, const char } } - if (LIBXML(stream_context)) { - context = zend_fetch_resource(&LIBXML(stream_context) TSRMLS_CC, -1, "Stream-Context", NULL, 1, php_le_stream_context()); - } + context = php_stream_context_from_zval(LIBXML(stream_context), 0); ret_val = php_stream_open_wrapper_ex(path_to_open, (char *)mode, ENFORCE_SAFE_MODE|REPORT_ERRORS, NULL, context); if (isescaped) { diff --git a/ext/libxml/tests/bug54440.phpt b/ext/libxml/tests/bug54440.phpt new file mode 100644 index 000000000..4074ff9fe --- /dev/null +++ b/ext/libxml/tests/bug54440.phpt @@ -0,0 +1,51 @@ +--TEST-- +Bug #54440: libxml extension ignores default context +--SKIPIF-- +<?php if (!extension_loaded('simplexml')) die('skip simplexml required for this test'); ?> +--FILE-- +<?php + +class TestWrapper { + +function stream_open($path, $mode, $options, &$opened_path) +{ + if ($this->context) + print_r(stream_context_get_options($this->context)); + return false; +} + +function url_stat($path, $flags) +{ +return array(); +} + +} + +stream_wrapper_register("test", "TestWrapper") + or die("Failed to register protocol"); + +$ctx1 = stream_context_create(array('test'=>array('test'=>'test 1'))); +$ctx2 = stream_context_create(array('test'=>array('test'=>'test 2'))); + +stream_context_set_default(stream_context_get_options($ctx1)); +@simplexml_load_file('test://sdfsdf'); + +libxml_set_streams_context($ctx2); +@simplexml_load_file('test://sdfsdf'); +--EXPECT-- +Array +( + [test] => Array + ( + [test] => test 1 + ) + +) +Array +( + [test] => Array + ( + [test] => test 2 + ) + +) diff --git a/ext/mbstring/libmbfl/mbfl/mbfilter.c b/ext/mbstring/libmbfl/mbfl/mbfilter.c index b8b1db268..34e33644b 100644 --- a/ext/mbstring/libmbfl/mbfl/mbfilter.c +++ b/ext/mbstring/libmbfl/mbfl/mbfilter.c @@ -1202,10 +1202,10 @@ mbfl_substr( len = string->len; start = from; end = from + length; - if (encoding->flag & (MBFL_ENCTYPE_WCS2BE | MBFL_ENCTYPE_MWC2LE)) { + if (encoding->flag & (MBFL_ENCTYPE_WCS2BE | MBFL_ENCTYPE_WCS2LE)) { start *= 2; end = start + length*2; - } else if (encoding->flag & (MBFL_ENCTYPE_WCS4BE | MBFL_ENCTYPE_MWC4LE)) { + } else if (encoding->flag & (MBFL_ENCTYPE_WCS4BE | MBFL_ENCTYPE_WCS4LE)) { start *= 4; end = start + length*4; } else if (encoding->mblen_table != NULL) { diff --git a/ext/mbstring/mbstring.c b/ext/mbstring/mbstring.c index 00baa575a..7520ee171 100644 --- a/ext/mbstring/mbstring.c +++ b/ext/mbstring/mbstring.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: mbstring.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: mbstring.c 313665 2011-07-25 11:42:53Z felipe $ */ /* * PHP 4 Multibyte String module "mbstring" @@ -557,7 +557,7 @@ const zend_function_entry mbstring_functions[] = { #if HAVE_MBREGEX PHP_MBREGEX_FUNCTION_ENTRIES #endif - { NULL, NULL, NULL } + PHP_FE_END }; /* }}} */ diff --git a/ext/mbstring/tests/bug54494.phpt b/ext/mbstring/tests/bug54494.phpt new file mode 100644 index 000000000..3d7206c3e --- /dev/null +++ b/ext/mbstring/tests/bug54494.phpt @@ -0,0 +1,52 @@ +--TEST-- +Bug #54494: mb_substr() mishandles UTF-32LE and UCS-2LE +--SKIPIF-- +<?php extension_loaded('mbstring') or die('skip mbstring not available'); ?> +--FILE-- +<?php + +//declare(encoding = 'UTF-8'); +mb_internal_encoding('UTF-8'); + +header('Content-Type: text/plain; charset=UTF-32LE'); + +$stringOr = "hällö wörld\n"; + +$mode = "UTF-32LE"; + +echo "$mode:\n"; + +$string = mb_convert_encoding($stringOr, $mode); +$length = mb_strlen($string, $mode); +echo "Length: ", $length, "\n"; + + +for ($i=0; $i < $length; $i++) { + $t = unpack("H*",mb_substr($string, $i, 1, $mode)); + echo $t[1]; +} +echo "\n"; + + +$mode = "UCS-2LE"; + +echo "$mode:\n"; + +$string = mb_convert_encoding($stringOr, $mode); +$length = mb_strlen($string, $mode); +echo "Length: ", $length, "\n"; + + +for ($i=0; $i < $length; $i++) { + $t = unpack("H*",mb_substr($string, $i, 1, $mode)); + echo $t[1]; +} +echo "\n"; +--EXPECT-- +UTF-32LE: +Length: 12 +68000000e40000006c0000006c000000f60000002000000077000000f6000000720000006c000000640000000a000000 +UCS-2LE: +Length: 12 +6800e4006c006c00f60020007700f60072006c0064000a00 + diff --git a/ext/mcrypt/mcrypt.c b/ext/mcrypt/mcrypt.c index b0e27a86a..7e8ef0df9 100644 --- a/ext/mcrypt/mcrypt.c +++ b/ext/mcrypt/mcrypt.c @@ -16,7 +16,7 @@ | Derick Rethans <derick@derickrethans.nl> | +----------------------------------------------------------------------+ */ -/* $Id: mcrypt.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: mcrypt.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -280,7 +280,7 @@ const zend_function_entry mcrypt_functions[] = { /* {{{ */ PHP_FE(mcrypt_module_get_supported_key_sizes, arginfo_mcrypt_module_get_supported_key_sizes) PHP_FE(mcrypt_module_close, arginfo_mcrypt_module_close) - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ @@ -1394,7 +1394,7 @@ PHP_FUNCTION(mcrypt_create_iv) BYTE *iv_b = (BYTE *) iv; if (php_win32_get_random_bytes(iv_b, (size_t) size) == FAILURE){ efree(iv); - php_error_docref(NULL TSRMLS_CC, E_ERROR, "Could not gather sufficient random data"); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not gather sufficient random data"); RETURN_FALSE; } n = size; diff --git a/ext/mcrypt/tests/bug55169.phpt b/ext/mcrypt/tests/bug55169.phpt new file mode 100644 index 000000000..7bddd5087 --- /dev/null +++ b/ext/mcrypt/tests/bug55169.phpt @@ -0,0 +1,43 @@ +--TEST--
+mcrypt_create_iv
+https://bugs.php.net/bug.php?id=55169
+--CREDIT--
+Ryan Biesemeyer <ryan@yaauie.com>
+--FILE--
+<?php
+for( $i=1; $i<=64; $i = $i*2 ){
+ echo 'Input: '. $i . PHP_EOL;
+ $random = mcrypt_create_iv( $i, MCRYPT_DEV_URANDOM );
+ echo ' Length: ' . strlen( $random ) . PHP_EOL;
+ echo ' Hex: '. bin2hex( $random ) . PHP_EOL;
+ echo PHP_EOL;
+}
+?>
+--EXPECTF--
+Input: 1
+ Length: 1
+ Hex: %x
+
+Input: 2
+ Length: 2
+ Hex: %x
+
+Input: 4
+ Length: 4
+ Hex: %x
+
+Input: 8
+ Length: 8
+ Hex: %x
+
+Input: 16
+ Length: 16
+ Hex: %x
+
+Input: 32
+ Length: 32
+ Hex: %x
+
+Input: 64
+ Length: 64
+ Hex: %x
diff --git a/ext/mssql/php_mssql.c b/ext/mssql/php_mssql.c index d454dc905..8f2272fa4 100644 --- a/ext/mssql/php_mssql.c +++ b/ext/mssql/php_mssql.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: php_mssql.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: php_mssql.c 313835 2011-07-28 11:01:04Z pajoye $ */ #ifdef COMPILE_DL_MSSQL #define HAVE_MSSQL 1 @@ -178,7 +178,7 @@ const zend_function_entry mssql_functions[] = { PHP_FE(mssql_execute, arginfo_mssql_execute) PHP_FE(mssql_free_statement, arginfo_mssql_free_statement) PHP_FE(mssql_guid_string, arginfo_mssql_guid_string) - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ @@ -685,6 +685,13 @@ static void php_mssql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) /* hash it up */ mssql_ptr = (mssql_link *) malloc(sizeof(mssql_link)); + if (!mssql_ptr) { + efree(hashed_details); + dbfreelogin(mssql.login); + dbclose(mssql.link); + RETURN_FALSE; + } + memcpy(mssql_ptr, &mssql, sizeof(mssql_link)); Z_TYPE(new_le) = le_plink; new_le.ptr = mssql_ptr; diff --git a/ext/mysql/php_mysql.c b/ext/mysql/php_mysql.c index 0ecbcf75e..c6cb10a81 100644 --- a/ext/mysql/php_mysql.c +++ b/ext/mysql/php_mysql.c @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: php_mysql.c 308323 2011-02-14 14:05:46Z iliaa $ */ +/* $Id: php_mysql.c 314376 2011-08-06 14:47:44Z felipe $ */ /* TODO: * @@ -320,7 +320,7 @@ static const zend_function_entry mysql_functions[] = { PHP_FALIAS(mysql_dbname, mysql_result, arginfo_mysql_result) PHP_FALIAS(mysql_tablename, mysql_result, arginfo_mysql_result) PHP_FALIAS(mysql_table_name, mysql_result, arginfo_mysql_result) - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ @@ -329,7 +329,7 @@ static const zend_module_dep mysql_deps[] = { #if defined(MYSQL_USE_MYSQLND) ZEND_MOD_REQUIRED("mysqlnd") #endif - {NULL, NULL, NULL} + ZEND_MOD_END }; /* {{{ mysql_module_entry @@ -608,7 +608,7 @@ PHP_RINIT_FUNCTION(mysql) /* }}} */ -#ifdef MYSQL_USE_MYSQLND +#if defined(A0) && defined(MYSQL_USE_MYSQLND) static int php_mysql_persistent_helper(zend_rsrc_list_entry *le TSRMLS_DC) { if (le->type == le_plink) { @@ -637,7 +637,7 @@ PHP_RSHUTDOWN_FUNCTION(mysql) efree(MySG(connect_error)); } -#if defined(A0) && MYSQL_USE_MYSQLND +#if defined(A0) && defined(MYSQL_USE_MYSQLND) zend_hash_apply(&EG(persistent_list), (apply_func_t) php_mysql_persistent_helper TSRMLS_CC); #endif @@ -830,6 +830,9 @@ static void php_mysql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) } /* create the link */ mysql = (php_mysql_conn *) malloc(sizeof(php_mysql_conn)); + if (!mysql) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Out of memory while allocating memory for a persistent link"); + } mysql->active_result_id = 0; #ifdef CLIENT_MULTI_STATEMENTS mysql->multi_query = client_flags & CLIENT_MULTI_STATEMENTS? 1:0; diff --git a/ext/mysqli/mysqli.c b/ext/mysqli/mysqli.c index 74d224c79..466457d1d 100644 --- a/ext/mysqli/mysqli.c +++ b/ext/mysqli/mysqli.c @@ -17,7 +17,7 @@ | Ulf Wendel <uw@php.net> | +----------------------------------------------------------------------+ - $Id: mysqli.c 306939 2011-01-01 02:19:59Z felipe $ + $Id: mysqli.c 314376 2011-08-06 14:47:44Z felipe $ */ #ifdef HAVE_CONFIG_H @@ -865,7 +865,7 @@ PHP_RINIT_FUNCTION(mysqli) } /* }}} */ -#ifdef MYSQLI_USE_MYSQLND +#if defined(A0) && defined(MYSQLI_USE_MYSQLND) static void php_mysqli_persistent_helper_for_every(void *p) { TSRMLS_FETCH(); @@ -896,7 +896,7 @@ PHP_RSHUTDOWN_FUNCTION(mysqli) if (MyG(error_msg)) { efree(MyG(error_msg)); } -#if defined(A0) && MYSQLI_USE_MYSQLND +#if defined(A0) && defined(MYSQLI_USE_MYSQLND) /* psession is being called when the connection is freed - explicitly or implicitly */ zend_hash_apply(&EG(persistent_list), (apply_func_t) php_mysqli_persistent_helper_once TSRMLS_CC); #endif @@ -939,7 +939,7 @@ static const zend_module_dep mysqli_deps[] = { #if defined(MYSQLI_USE_MYSQLND) ZEND_MOD_REQUIRED("mysqlnd") #endif - {NULL, NULL, NULL} + ZEND_MOD_END }; /* {{{ mysqli_module_entry diff --git a/ext/mysqli/mysqli_api.c b/ext/mysqli/mysqli_api.c index 339469fba..4fb4470e9 100644 --- a/ext/mysqli/mysqli_api.c +++ b/ext/mysqli/mysqli_api.c @@ -17,7 +17,7 @@ | Ulf Wendel <uw@php.net> | +----------------------------------------------------------------------+ - $Id: mysqli_api.c 307533 2011-01-17 11:20:54Z kalle $ + $Id: mysqli_api.c 314116 2011-08-02 15:30:58Z andrey $ */ #ifdef HAVE_CONFIG_H @@ -2233,7 +2233,9 @@ PHP_FUNCTION(mysqli_stmt_attr_set) MY_STMT *stmt; zval *mysql_stmt; long mode_in; +#if MYSQL_VERSION_ID >= 50107 my_bool mode_b; +#endif ulong mode; ulong attr; void *mode_p; diff --git a/ext/mysqli/mysqli_fe.c b/ext/mysqli/mysqli_fe.c index 6bab6ad78..8243a1d54 100644 --- a/ext/mysqli/mysqli_fe.c +++ b/ext/mysqli/mysqli_fe.c @@ -17,7 +17,7 @@ | Ulf Wendel <uw@php.net> | +----------------------------------------------------------------------+ - $Id: mysqli_fe.c 306939 2011-01-01 02:19:59Z felipe $ + $Id: mysqli_fe.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H @@ -468,7 +468,7 @@ const zend_function_entry mysqli_functions[] = { PHP_FALIAS(mysqli_send_long_data, mysqli_stmt_send_long_data, NULL) PHP_FALIAS(mysqli_set_opt, mysqli_options, NULL) - {NULL, NULL, NULL} /* Must be the last line in mysqli_functions[] */ + PHP_FE_END }; /* }}} */ diff --git a/ext/mysqli/mysqli_nonapi.c b/ext/mysqli/mysqli_nonapi.c index 2abf5e9a8..8f8401367 100644 --- a/ext/mysqli/mysqli_nonapi.c +++ b/ext/mysqli/mysqli_nonapi.c @@ -17,7 +17,7 @@ | Ulf Wendel <uw@php.net> | +----------------------------------------------------------------------+ - $Id: mysqli_nonapi.c 307223 2011-01-07 14:22:30Z andrey $ + $Id: mysqli_nonapi.c 314838 2011-08-12 14:55:00Z andrey $ */ #ifdef HAVE_CONFIG_H @@ -141,10 +141,12 @@ void mysqli_common_connect(INTERNAL_FUNCTION_PARAMETERS, zend_bool is_real_conne hostname = MyG(default_host); } - if (mysql->mysql && mysqli_resource && (mysqli_resource->status > MYSQLI_STATUS_INITIALIZED || (strlen(SAFE_STR(hostname)) > 2 && !strncasecmp(hostname, "p:", 2)))) { - /* already connected, we should close the connection */ - php_mysqli_close(mysql, MYSQLI_CLOSE_IMPLICIT, mysqli_resource->status TSRMLS_CC); - } + if (mysql->mysql && mysqli_resource && + (mysqli_resource->status > MYSQLI_STATUS_INITIALIZED)) + { + /* already connected, we should close the connection */ + php_mysqli_close(mysql, MYSQLI_CLOSE_IMPLICIT, mysqli_resource->status TSRMLS_CC); + } if (strlen(SAFE_STR(hostname)) > 2 && !strncasecmp(hostname, "p:", 2)) { hostname += 2; @@ -914,7 +916,6 @@ PHP_FUNCTION(mysqli_get_charset) } MYSQLI_FETCH_RESOURCE_CONN(mysql, &mysql_link, MYSQLI_STATUS_VALID); - object_init(return_value); #if !defined(MYSQLI_USE_MYSQLND) mysql_get_character_set_info(mysql->mysql, &cs); @@ -928,6 +929,10 @@ PHP_FUNCTION(mysqli_get_charset) comment = cs.comment; #else cs = mysql->mysql->charset; + if (!cs) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "The connection has no charset associated"); + RETURN_NULL(); + } name = cs->name; collation = cs->collation; minlength = cs->char_minlen; @@ -936,6 +941,7 @@ PHP_FUNCTION(mysqli_get_charset) comment = cs->comment; state = 1; /* all charsets are compiled in */ #endif + object_init(return_value); add_property_string(return_value, "charset", (name) ? (char *)name : "", 1); add_property_string(return_value, "collation",(collation) ? (char *)collation : "", 1); diff --git a/ext/mysqli/mysqli_warning.c b/ext/mysqli/mysqli_warning.c index 09fa5b351..ee815121b 100644 --- a/ext/mysqli/mysqli_warning.c +++ b/ext/mysqli/mysqli_warning.c @@ -197,7 +197,7 @@ PHP_METHOD(mysqli_warning, next) MYSQLI_FETCH_RESOURCE(w, MYSQLI_WARNING *, &mysqli_warning, "mysqli_warning", MYSQLI_STATUS_VALID); - if (w->next) { + if (w && w->next) { w = w->next; ((MYSQLI_RESOURCE *)(obj->ptr))->ptr = w; RETURN_TRUE; diff --git a/ext/mysqli/tests/bug54221.phpt b/ext/mysqli/tests/bug54221.phpt new file mode 100644 index 000000000..78c9a2c40 --- /dev/null +++ b/ext/mysqli/tests/bug54221.phpt @@ -0,0 +1,47 @@ +--TEST-- +Bug #54221 mysqli::get_warnings segfault when used in multi queries +--SKIPIF-- +<?php +require_once('skipif.inc'); +require_once('skipifconnectfailure.inc'); +?> +--INI-- +mysqli.max_links = 1 +mysqli.allow_persistent = Off +mysqli.max_persistent = 0 +mysqli.reconnect = Off +--FILE-- +<?php + include ("connect.inc"); + + $link = mysqli_init(); + if (!my_mysqli_real_connect($link, $host, $user, $passwd, $db, $port, $socket)) { + printf("[002] Connect failed, [%d] %s\n", mysqli_connect_errno(), mysqli_connect_error()); + } + + $create = "CREATE TEMPORARY TABLE IF NOT EXISTS t54221(a int)"; + + $query = "$create;$create;$create;"; + if ($link->multi_query($query)) { + do { + $sth = $link->store_result(); + + if ($link->warning_count) { + $warnings = $link->get_warnings(); + if ($warnings) { + do { + echo "Warning: ".$warnings->errno.": ".$warnings->message."\n"; + } while ($warnings->next()); + } + } + } while ($link->more_results() && $link->next_result()); + } + + mysqli_close($link); + + print "done!"; +?> +--EXPECTF-- +Warning: : +Warning: 1050: Table 't54221' already exists +done! diff --git a/ext/mysqli/tests/bug54674.phpt b/ext/mysqli/tests/bug54674.phpt new file mode 100644 index 000000000..efc67308b --- /dev/null +++ b/ext/mysqli/tests/bug54674.phpt @@ -0,0 +1,31 @@ +--TEST-- +Bug #54674 mysqlnd valid_sjis_(head|tail) is using invalid operator and range. +--SKIPIF-- +<?php +require_once('skipif.inc'); +require_once('skipifconnectfailure.inc'); +?> +--INI-- +mysqli.max_links = 1 +mysqli.allow_persistent = Off +mysqli.max_persistent = 0 +mysqli.reconnect = Off +--FILE-- +<?php + include ("connect.inc"); + + $link = mysqli_init(); + if (!my_mysqli_real_connect($link, $host, $user, $passwd, $db, $port, $socket)) { + printf("[002] Connect failed, [%d] %s\n", mysqli_connect_errno(), mysqli_connect_error()); + } + + $japanese_so = pack('H4', '835c'); + $link->set_charset('sjis'); + var_dump($link->real_escape_string($japanese_so) === $japanese_so); + mysqli_close($link); + + print "done!"; +?> +--EXPECTF-- +bool(true) +done! diff --git a/ext/mysqli/tests/bug55283.phpt b/ext/mysqli/tests/bug55283.phpt new file mode 100644 index 000000000..0611c773c --- /dev/null +++ b/ext/mysqli/tests/bug55283.phpt @@ -0,0 +1,55 @@ +--TEST-- +Bug #55283 (SSL options set by mysqli_ssl_set ignored for MySQLi persistent connections) +--SKIPIF-- +<?php +require_once('skipif.inc'); +require_once('skipifconnectfailure.inc'); +$link = mysqli_init(); +mysqli_ssl_set($link, null, null, null, null, "RC4-MD5"); +if (my_mysqli_real_connect($link, $host, $user, $passwd, $db, $port, null, $flags)) { + $res = $link->query("SHOW STATUS LIKE 'Ssl_cipher'"); + $row = $res->fetch_row(); + if ($row[1] === "") { + die('skip Server started without SSL support'); + } +} +?> +--FILE-- +<?php + include "connect.inc"; + $db1 = new mysqli(); + + + $flags = MYSQLI_CLIENT_SSL; + + $link = mysqli_init(); + mysqli_ssl_set($link, null, null, null, null, "RC4-MD5"); + if (my_mysqli_real_connect($link, 'p:' . $host, $user, $passwd, $db, $port, null, $flags)) { + $r = $link->query("SHOW STATUS LIKE 'Ssl_cipher'"); + var_dump($r->fetch_row()); + } + + /* non-persistent connection */ + $link2 = mysqli_init(); + mysqli_ssl_set($link2, null, null, null, null, "RC4-MD5"); + if (my_mysqli_real_connect($link2, $host, $user, $passwd, $db, $port, null, $flags)) { + $r2 = $link2->query("SHOW STATUS LIKE 'Ssl_cipher'"); + var_dump($r2->fetch_row()); + } + + echo "done\n"; +?> +--EXPECTF-- +array(2) { + [0]=> + string(10) "Ssl_cipher" + [1]=> + string(7) "RC4-MD5" +} +array(2) { + [0]=> + string(10) "Ssl_cipher" + [1]=> + string(7) "RC4-MD5" +} +done
\ No newline at end of file diff --git a/ext/mysqli/tests/mysqli_change_user.phpt b/ext/mysqli/tests/mysqli_change_user.phpt index a9b8ae7ad..bfea423c9 100644 --- a/ext/mysqli/tests/mysqli_change_user.phpt +++ b/ext/mysqli/tests/mysqli_change_user.phpt @@ -83,8 +83,28 @@ require_once('skipifconnectfailure.inc'); mysqli_close($link); + if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) { + printf("[022] Cannot connect to the server using host=%s, user=%s, passwd=***, dbname=%s, port=%s, socket=%s\n", + $host, $user, $db, $port, $socket); + } + + /* silent protocol change if no db which requires workaround in mysqlnd/libmysql + (empty db = no db send with COM_CHANGE_USER) */ + if (true !== ($tmp = mysqli_change_user($link, $user, $passwd, ""))) + printf("[023] Expecting true, got %s/%s\n", gettype($tmp), $tmp); + + if (!$res = mysqli_query($link, 'SELECT database() AS dbname, user() AS user')) + printf("[024] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + $tmp = mysqli_fetch_assoc($res); + mysqli_free_result($res); + + if ($tmp['dbname'] != "") + printf("[025] Expecting database '', got database() '%s'\n", $tmp['dbname']); + + mysqli_close($link); + if (NULL !== ($tmp = @mysqli_change_user($link, $user, $passwd, $db))) - printf("[022] Expecting NULL, got %s/%s\n", gettype($tmp), $tmp); + printf("[026] Expecting NULL, got %s/%s\n", gettype($tmp), $tmp); print "done!"; ?> diff --git a/ext/mysqli/tests/mysqli_last_insert_id.phpt b/ext/mysqli/tests/mysqli_last_insert_id.phpt new file mode 100644 index 000000000..353cb7a8a --- /dev/null +++ b/ext/mysqli/tests/mysqli_last_insert_id.phpt @@ -0,0 +1,194 @@ +--TEST-- +API vs. SQL LAST_INSERT_ID() +--SKIPIF-- +<?php + require_once('skipif.inc'); + require_once('skipifconnectfailure.inc'); +?> +--FILE-- +<?php + /* + CAUTION: the insert_id() API call is not supposed to return + the same value as a call to the LAST_INSERT_ID() SQL function. + It is not necessarily a bug if API and SQL function return different + values. Check the MySQL C API reference manual for details. + */ + require_once("connect.inc"); + + function get_sql_id($link) { + if (!($res = $link->query("SELECT LAST_INSERT_ID() AS _id"))) { + printf("[003] [%d] %s\n", $link->errno, $link->error); + return NULL; + } + $row = $res->fetch_assoc(); + $res->close(); + + return $row['_id']; + } + + if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) + printf("[001] Cannot connect to the server using host=%s, user=%s, passwd=***, dbname=%s, port=%s, socket=%s\n", + $host, $user, $db, $port, $socket); + + if (!$link->query("DROP TABLE IF EXISTS test") || + !$link->query("CREATE TABLE test (id INT auto_increment, label varchar(10) not null, PRIMARY KEY (id))") || + !$link->query("INSERT INTO test (id, label) VALUES (null, 'a')")) { + printf("[002] [%d] %s\n", $link->errno, $link->error); + } + + $api_id = $link->insert_id; + $sql_id = get_sql_id($link); + printf("API: %d, SQL: %d\n", $api_id, $sql_id); + + if ($api_id < 1) + printf("[004] Expecting id > 0 got %d, [%d] %s\n", $api_id, $link->errno, $link->error) ; + if ($api_id != $sql_id) + printf("[005] SQL id %d should be equal to API id %d\n", $sql_id, $api_id); + + // Not an INSERT, API value must become 0 + if (!($res = $link->query("SELECT 1 FROM DUAL"))) + printf("[006] [%d] %s\n", $link->errno, $link->error); + else + $res->close(); + + $api_id = $link->insert_id; + $new_sql_id = get_sql_id($link); + if (0 !== $api_id) { + printf("[007] API id should have been reset to 0 because previous query was SELECT, got API %d, SQL %d\n", + $api_id, $new_sql_id); + } + if ($new_sql_id != $sql_id) { + printf("[008] The servers LAST_INSERT_ID() changed unexpectedly from %d to %d\n", $sql_id, $new_sql_id); + } + + // Insert fails, LAST_INSERT_ID shall not change, API shall return 0 + if ($link->query("INSERT INTO test (id, label) VALUES (null, null)")) { + printf("[009] The INSERT did not fail as planned, [%d] %s\n", $link->errno, $link->error); + } + $api_id = $link->insert_id; + $new_sql_id = get_sql_id($link); + + if (0 !== $api_id) { + printf("[010] API id should have been reset to 0 because previous query was SELECT, got API %d, SQL %d\n", + $api_id, $new_sql_id); + } + if ($new_sql_id != $sql_id) { + printf("[011] The servers LAST_INSERT_ID() changed unexpectedly from %d to %d\n", $sql_id, $new_sql_id); + } + + // Sequence counter pattern... + if (!$link->query("UPDATE test SET id=LAST_INSERT_ID(id+1)")) + printf("[012] [%d] %s\n", $link->errno, $link->error); + + $api_id = $link->insert_id; + $new_sql_id = get_sql_id($link); + if ($api_id < 1) + printf("[013] Expecting id > 0 got %d, [%d] %s\n", $api_id, $link->errno, $link->error) ; + if ($api_id != $new_sql_id) + printf("[014] SQL id %d should be equal to API id %d\n", $new_sql_id, $api_id); + if ($sql_id == $new_sql_id) + printf("[015] SQL id %d should have had changed, got %d\n", $sql_id, $new_sql_id); + + $sql_id = $new_sql_id; + + // Not an INSERT (after UPDATE), API value must become 0 + if (!$link->query("SET @myvar=1")) + printf("[016] [%d] %s\n", $link->errno, $link->error); + + $api_id = $link->insert_id; + $new_sql_id = get_sql_id($link); + if (0 !== $api_id) { + printf("[017] API id should have been reset to 0 because previous query was SET, got API %d, SQL %d\n", + $api_id, $new_sql_id); + } + if ($new_sql_id != $sql_id) { + printf("[018] The servers LAST_INSERT_ID() changed unexpectedly from %d to %d\n", $sql_id, $new_sql_id); + } + + if (!$link->query("INSERT INTO test(id, label) VALUES (LAST_INSERT_ID(id + 1), 'b')")) + printf("[019] [%d] %s\n", $link->errno, $link->error); + + $api_id = $link->insert_id; + $sql_id = get_sql_id($link); + if ($api_id != $sql_id) + printf("[020] SQL id %d should be equal to API id %d\n", $sql_id, $api_id); + + if (!$link->query("INSERT INTO test(label) VALUES ('c')")) + printf("[021] [%d] %s\n", $link->errno, $link->error); + + $api_id = $link->insert_id; + $sql_id = get_sql_id($link); + if ($api_id != $sql_id) + printf("[022] SQL id %d should be equal to API id %d\n", $sql_id, $api_id); + + if (!($res = $link->query("SELECT id, label FROM test ORDER BY id ASC"))) + printf("[023] [%d] %s\n", $link->errno, $link->error); + + printf("Dumping table contents before INSERT...SELECT experiments...\n"); + while ($row = $res->fetch_assoc()) { + printf("id = %d, label = '%s'\n", $row['id'], $row['label']); + } + $res->close(); + + if (!$link->query("INSERT INTO test(label) SELECT CONCAT(label, id) FROM test ORDER BY id ASC")) + printf("[024] [%d] %s\n", $link->errno, $link->error); + + $api_id = $link->insert_id; + $sql_id = get_sql_id($link); + if ($api_id != $sql_id) + printf("[025] SQL id %d should be equal to API id %d\n", $sql_id, $api_id); + + if ($link->query("INSERT INTO test(id, label) SELECT id, CONCAT(label, id) FROM test ORDER BY id ASC")) + printf("[026] INSERT should have failed because of duplicate PK value, [%d] %s\n", $link->errno, $link->error); + + $api_id = $link->insert_id; + $new_sql_id = get_sql_id($link); + if (0 !== $api_id) { + printf("[027] API id should have been reset to 0 because previous query failed, got API %d, SQL %d\n", + $api_id, $new_sql_id); + } + if ($new_sql_id != $sql_id) { + printf("[028] The servers LAST_INSERT_ID() changed unexpectedly from %d to %d\n", $sql_id, $new_sql_id); + } + + /* API insert id will be 101 because of UPDATE, SQL unchanged */ + if (!$link->query(sprintf("INSERT INTO test(id, label) VALUES (%d, 'z') ON DUPLICATE KEY UPDATE id = 101", $sql_id) )) + printf("[029] [%d] %s\n", $link->errno, $link->error); + + $api_id = $link->insert_id; + $new_sql_id = get_sql_id($link); + if ($api_id != 101) + printf("[030] API id should be %d got %d\n", $sql_id, $api_id); + if ($new_sql_id != $sql_id) { + printf("[031] The servers LAST_INSERT_ID() changed unexpectedly from %d to %d\n", $sql_id, $new_sql_id); + } + + if (!($res = $link->query("SELECT id, label FROM test ORDER BY id ASC"))) + printf("[032] [%d] %s\n", $link->errno, $link->error); + + printf("Dumping table contents after INSERT...SELECT...\n"); + while ($row = $res->fetch_assoc()) { + printf("id = %d, label = '%s'\n", $row['id'], $row['label']); + } + $res->close(); + + print "done!"; +?> +--CLEAN-- +<?php + require_once("clean_table.inc"); +?> +--EXPECTF-- +API: %d, SQL: %d +Dumping table contents before INSERT...SELECT experiments... +id = %d, label = 'b' +id = %d, label = 'a' +id = %d, label = 'c' +Dumping table contents after INSERT...SELECT... +id = %d, label = 'b' +id = %d, label = 'a' +id = %d, label = 'c' +id = %d, label = 'a%d' +id = %d, label = 'c%d' +id = 101, label = 'b%d' +done!
\ No newline at end of file diff --git a/ext/mysqli/tests/mysqli_select_db.phpt b/ext/mysqli/tests/mysqli_select_db.phpt index d700a2b55..7527a1313 100644 --- a/ext/mysqli/tests/mysqli_select_db.phpt +++ b/ext/mysqli/tests/mysqli_select_db.phpt @@ -9,6 +9,7 @@ require_once('skipifconnectfailure.inc'); --FILE-- <?php require_once("connect.inc"); + require_once("table.inc"); $tmp = NULL; $link = NULL; @@ -55,18 +56,47 @@ require_once('skipifconnectfailure.inc'); mysqli_free_result($res); } - mysqli_report(MYSQLI_REPORT_OFF); + if (!$link->select_db($db)) + printf("[012] Failed to set '%s' as current DB; [%d] %s\n", $link->errno, $link->error); + + if (!$res = mysqli_query($link, "SELECT DATABASE() AS dbname")) + printf("[013] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + + if (!$row = mysqli_fetch_assoc($res)) + printf("[014] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + + $current_db = $row['dbname']; + + mysqli_report(MYSQLI_REPORT_OFF); mysqli_select_db($link, 'I can not imagine that this database exists'); mysqli_report(MYSQLI_REPORT_ERROR); mysqli_select_db($link, 'I can not imagine that this database exists'); + if (!$res = mysqli_query($link, "SELECT DATABASE() AS dbname")) + printf("[015] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + + if (!$row = mysqli_fetch_assoc($res)) + printf("[016] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + + if (strtolower($row['dbname']) != strtolower($current_db)) + printf("[017] Current DB should not change if set fails\n"); + + + if (!$res = $link->query("SELECT id FROM test WHERE id = 1")) + printf("[018] [%d] %s\n"); + + $row = $res->fetch_assoc(); + $res->free(); + mysqli_close($link); if (NULL !== ($tmp = mysqli_select_db($link, $db))) - printf("[012] Expecting NULL, got %s/%s\n", gettype($tmp), $tmp); + printf("[017] Expecting NULL, got %s/%s\n", gettype($tmp), $tmp); print "done!\n"; ?> +--CLEAN-- +<?php require_once("clean_table.inc"); ?> --EXPECTF-- Warning: mysqli_select_db(): (%d/%d): Unknown database '%s' in %s on line %d diff --git a/ext/mysqli/tests/mysqli_stmt_bind_limits.phpt b/ext/mysqli/tests/mysqli_stmt_bind_limits.phpt new file mode 100644 index 000000000..39c602034 --- /dev/null +++ b/ext/mysqli/tests/mysqli_stmt_bind_limits.phpt @@ -0,0 +1,129 @@ +--TEST-- +Bind limits +--SKIPIF-- +<?php +require_once('skipif.inc'); +require_once('skipifemb.inc'); +require_once('skipifconnectfailure.inc'); +?> +--FILE-- +<?php + require_once("connect.inc"); + + function bind_many($offset, $link, $num_params, $rows, $eval = true) { + + $drop = "DROP TABLE IF EXISTS test"; + $create = "CREATE TABLE test(id INT AUTO_INCREMENT PRIMARY KEY, "; + $insert = "INSERT INTO test"; + $columns = ""; + $values = ""; + $stmt_params = ""; + $params = array(); + for ($i = 0; $i < $num_params; $i++) { + $create .= "col" . $i . " INT, "; + $columns .= "col" . $i . ", "; + $values .= "?, "; + $stmt_params .= '$params[' . $i . '], '; + for ($j = 0; $j < $rows; $j++) + $params[($j * $rows) + $i] = $i; + } + $create = substr($create, 0, -2) . ")"; + + $stmt_types = str_repeat("i", $num_params * $rows); + $stmt_params = substr(str_repeat($stmt_params, $rows), 0, -2); + $values = substr($values, 0, -2); + $insert .= "(" . substr($columns, 0, -2) . ") VALUES "; + $insert .= substr(str_repeat("(" . $values . "), ", $rows), 0, -2); + + $stmt_bind_param = 'return mysqli_stmt_bind_param($stmt, "' . $stmt_types . '", ' . $stmt_params . ');'; + + printf("Testing %d columns with %d rows...\n", $num_params, $rows); + + if (!$link->query($drop) || !$link->query($create)) { + printf("[%03d + 01] [%d] %s\n", $offset, $link->errno, $link->error); + return false; + } + printf("... table created\n"); + + if (!$stmt = $link->prepare($insert)) { + printf("[%03d + 02] [%d] %s\n", $offset, $link->errno, $link->error); + return false; + } + if ($stmt->param_count != $num_params * $rows) { + printf("[%03d + 03] Parameter count should be %d but got %d\n", $offset, $num_params * $rows, $stmt->param_count); + return false; + } + printf("... statement with %d parameters prepared\n", $stmt->param_count); + + if ($eval) { + if (!eval($stmt_bind_param)) { + printf("[%03d + 03] [%d] %s\n", $offset, $stmt->errno, $stmt->error); + return false; + } + } else { + $param_ref = array($stmt_types); + for ($i = 0; $i < $rows; $i++) + for ($j = 0; $j < $num_params; $j++) + $param_ref[] = &$params[($i * $rows) + $j]; + + if (!call_user_func_array(array($stmt, 'bind_param'), $param_ref)) { + printf("[%03d + 03] [%d] %s\n", $offset, $stmt->errno, $stmt->error); + return false; + } + } + if ($stmt->param_count != $num_params * $rows) { + printf("[%03d + 03] Parameter count should be %d but got %d\n", $offset, $num_params * $rows, $stmt->param_count); + return false; + } + + if (!$stmt->execute()) { + printf("[%03d + 04] [%d] %s\n", $offset, $stmt->errno, $stmt->error); + return false; + } + printf("Statement done\n"); + + $stmt->close(); + + if (!($res = $link->query("SELECT * FROM test"))) { + printf("[%03d + 05] [%d] %s\n", $offset, $link->errno, $link->error); + return false; + } + + $row = $res->fetch_row(); + $res->close(); + + for ($i = 0; $i < $num_params; $i++) { + if ($row[$i + 1] != $i) { + printf("[%03d + 06] [%d] %s\n", $offset, $link->errno, $link->error); + } + } + + return true; + } + + if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) { + printf("[001] Cannot connect to the server using host=%s, user=%s, passwd=***, dbname=%s, port=%s, socket=%s\n", + $host, $user, $db, $port, $socket); + } + + var_dump(bind_many(10, $link, 273, 240, true)); + var_dump(bind_many(20, $link, 273, 240, false)); + mysqli_close($link); + print "done!"; +?> +--CLEAN-- +<?php +require_once("clean_table.inc"); +?> +--EXPECTF-- +Testing 273 columns with 240 rows... +... table created +... statement with 65520 parameters prepared +Statement done +bool(true) +Testing 273 columns with 240 rows... +... table created +... statement with 65520 parameters prepared +Statement done +bool(true) +done!
\ No newline at end of file diff --git a/ext/mysqli/tests/mysqli_stmt_bind_param.phpt b/ext/mysqli/tests/mysqli_stmt_bind_param.phpt index 71045789b..d2d825438 100644 --- a/ext/mysqli/tests/mysqli_stmt_bind_param.phpt +++ b/ext/mysqli/tests/mysqli_stmt_bind_param.phpt @@ -304,6 +304,8 @@ require_once('skipifconnectfailure.inc'); func_mysqli_stmt_bind_datatype($link, $engine, "s", "SET('a', 'b')", "a", 870); func_mysqli_stmt_bind_datatype($link, $engine, "s", "SET('a', 'b')", NULL, 880); + if (mysqli_get_server_version($link) >= 50600) + func_mysqli_stmt_bind_datatype($link, $engine, "s", "TIME", "13:27:34.123456", 890, "13:27:34"); $stmt = mysqli_stmt_init($link); if (!mysqli_stmt_prepare($stmt, "INSERT INTO test(id, label) VALUES (?, ?)")) diff --git a/ext/mysqli/tests/mysqli_stmt_bind_result.phpt b/ext/mysqli/tests/mysqli_stmt_bind_result.phpt index 26b39d516..553e71ab6 100644 --- a/ext/mysqli/tests/mysqli_stmt_bind_result.phpt +++ b/ext/mysqli/tests/mysqli_stmt_bind_result.phpt @@ -294,6 +294,9 @@ require_once('skipifconnectfailure.inc'); func_mysqli_stmt_bind_result($link, $engine, "s", "SET('a', 'b')", "a", 1740, $hint_str_or_unicode); func_mysqli_stmt_bind_result($link, $engine, "s", "SET('a', 'b')", NULL, 1760, $hint_str_or_unicode); + if (mysqli_get_server_version($link) >= 50600) + func_mysqli_stmt_bind_result($link, $engine, "s", "TIME", "13:31:34.123456", 1770, "13:31:34"); + /* Check that the function alias exists. It's a deprecated function, but we have not announce the removal so far, therefore we need to check for it */ if (!is_null($tmp = @mysqli_stmt_bind_result())) diff --git a/ext/mysqli/tests/mysqli_stmt_execute_stored_proc_next_result.phpt b/ext/mysqli/tests/mysqli_stmt_execute_stored_proc_next_result.phpt new file mode 100644 index 000000000..1f92ca1f8 --- /dev/null +++ b/ext/mysqli/tests/mysqli_stmt_execute_stored_proc_next_result.phpt @@ -0,0 +1,125 @@ +--TEST-- +mysqli_stmt_execute() - SP, next result +--SKIPIF-- +<?php +require_once('skipif.inc'); +require_once('skipifconnectfailure.inc'); +require_once('connect.inc'); +if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) { + die(sprintf('skip Cannot connect to MySQL, [%d] %s.', mysqli_connect_errno(), mysqli_connect_error())); +} +if (mysqli_get_server_version($link) < 50503) { + die(sprintf('skip Needs MySQL 5.5.3+, found version %d.', mysqli_get_server_version($link))); +} +?> +--FILE-- +<?php + require_once('connect.inc'); + require_once('table.inc'); + + if (!mysqli_query($link, 'DROP PROCEDURE IF EXISTS p')) + printf("[003] [%d] %s.\n", mysqli_errno($link), mysqli_error($link)); + + if (mysqli_real_query($link, 'CREATE PROCEDURE p(IN ver_in VARCHAR(25)) BEGIN SELECT ver_in AS _ver_out; END;')) { + // one result set + if (!$stmt = mysqli_prepare($link, 'CALL p(?)')) + printf("[005] Cannot prepare CALL, [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + + $version = 'myversion'; + if (!mysqli_stmt_bind_param($stmt, 's', $version)) + printf("[006] Cannot bind input parameter, [%d] %s\n", mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt)); + + if (!mysqli_stmt_execute($stmt)) + printf("[007] Cannot execute CALL, [%d] %s\n", mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt)); + + $version = 'unknown'; + if (!mysqli_stmt_bind_result($stmt, $version) || + !mysqli_stmt_fetch($stmt)) + printf("[008] [%d] %s\n", mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt)); + + if ($version !== "myversion") + printf("[009] Results seem wrong, got %s, [%d] %s\n", + $version, + mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt)); + + mysqli_stmt_free_result($stmt); + + printf("[010] More results: %s\n", (mysqli_more_results($link)) ? "yes" : "no"); + printf("[011] Next result: %s\n", (mysqli_next_result($link)) ? "yes" : "no"); + + if (!mysqli_stmt_close($stmt)) + printf("[012] Cannot close statement, [%d] %s\n", mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt)); + + if (!$link->query("SELECT 1")) + printf("[013] [%d] %s\n", $link->errno, $link->error); + + } else { + printf("[004] Cannot create SP, [%d] %s.\n", mysqli_errno($link), mysqli_error($link)); + } + + if (!mysqli_query($link, 'DROP PROCEDURE IF EXISTS p')) + printf("[014] [%d] %s.\n", mysqli_errno($link), mysqli_error($link)); + + if (mysqli_real_query($link, 'CREATE PROCEDURE p(IN ver_in VARCHAR(25)) BEGIN SELECT ver_in AS _ver_out; SELECT 1 AS _more; END;')) { + // two result sets + if (!$stmt = mysqli_prepare($link, 'CALL p(?)')) + printf("[015] Cannot prepare CALL, [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + + $version = 'myversion'; + if (!mysqli_stmt_bind_param($stmt, 's', $version)) + printf("[016] Cannot bind input parameter, [%d] %s\n", mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt)); + + if (!mysqli_stmt_execute($stmt)) + printf("[017] Cannot execute CALL, [%d] %s\n", mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt)); + + $version = NULL; + if (!mysqli_stmt_bind_result($stmt, $version) || + !mysqli_stmt_fetch($stmt)) + printf("[018] [%d] %s\n", mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt)); + + if ($version !== "myversion") + printf("[019] Results seem wrong, got %s, [%d] %s\n", + $version, + mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt)); + + if (!mysqli_more_results($link) || !mysqli_next_result($link)) + printf("[020] [%d] %s\n", $link->errno, $link->error); + + $more = NULL; + if (!mysqli_stmt_bind_result($stmt, $more) || + !mysqli_stmt_fetch($stmt)) + printf("[021] [%d] %s\n", mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt)); + + if ($more !== 1) + printf("[022] Results seem wrong, got %s, [%d] %s\n", + $more, + mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt)); + + if (!mysqli_stmt_close($stmt)) + printf("[023] Cannot close statement, [%d] %s\n", mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt)); + + + if (!$link->query("SELECT 1")) + printf("[024] [%d] %s\n", $link->errno, $link->error); + + } else { + printf("[025] Cannot create SP, [%d] %s.\n", mysqli_errno($link), mysqli_error($link)); + } + + mysqli_close($link); + print "done!"; +?> +--CLEAN-- +<?php +require_once("connect.inc"); +if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) + printf("[c001] [%d] %s\n", mysqli_connect_errno(), mysqli_connect_error()); + +@mysqli_query($link, 'DROP PROCEDURE IF EXISTS p'); + +mysqli_close($link); +?> +--EXPECTF-- +[010] More results: yes +[011] Next result: yes +done!
\ No newline at end of file diff --git a/ext/mysqli/tests/mysqli_stmt_execute_stored_proc_out.phpt b/ext/mysqli/tests/mysqli_stmt_execute_stored_proc_out.phpt new file mode 100644 index 000000000..c6053bcae --- /dev/null +++ b/ext/mysqli/tests/mysqli_stmt_execute_stored_proc_out.phpt @@ -0,0 +1,75 @@ +--TEST-- +mysqli_stmt_execute() - OUT +--SKIPIF-- +<?php +require_once('skipif.inc'); +require_once('skipifconnectfailure.inc'); +require_once('connect.inc'); +if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) { + die(sprintf('skip Cannot connect to MySQL, [%d] %s.', mysqli_connect_errno(), mysqli_connect_error())); +} +if (mysqli_get_server_version($link) < 50503) { + die(sprintf('skip Needs MySQL 5.5.3+, found version %d.', mysqli_get_server_version($link))); +} +/* +if ($IS_MYSQLND) { + die(sprintf("skip WHY ?!")); +} +*/ +?> +--FILE-- +<?php + require_once('connect.inc'); + require_once('table.inc'); + + if (!mysqli_query($link, 'DROP PROCEDURE IF EXISTS p')) + printf("[003] [%d] %s.\n", mysqli_errno($link), mysqli_error($link)); + + if (mysqli_real_query($link, 'CREATE PROCEDURE p(IN ver_in VARCHAR(25), OUT ver_out VARCHAR(25)) BEGIN SELECT ver_in INTO ver_out; END;')) { + if (!$stmt = mysqli_prepare($link, 'CALL p(?, ?)')) + printf("[005] Cannot prepare CALL, [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + + $ver_in = 'myversion'; + $ver_out = ''; + if (!mysqli_stmt_bind_param($stmt, 'ss', $ver_in, $ver_out)) + printf("[006] Cannot bind parameter, [%d] %s\n", mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt)); + + if (!mysqli_stmt_execute($stmt)) + printf("[007] Cannot execute CALL, [%d] %s\n", mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt)); + + printf("[008] More results: %s\n", (mysqli_more_results($link) ? "yes" : "no")); + printf("[009] Next results: %s\n", (mysqli_next_result($link) ? "yes" : "no")); + + if (!mysqli_stmt_bind_result($stmt, $ver_out) || !mysqli_stmt_fetch($stmt)) + printf("[010] [%d] %s\n", mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt)); + + if ("myversion" !== $ver_out) + printf("[011] Results seem wrong got '%s'\n", $ver_out); + + if (!mysqli_stmt_close($stmt)) + printf("[012] Cannot close statement, [%d] %s\n", mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt)); + + if (!$res = $link->query("SELECT 1")) + printf("[013] [%d] %s\n", $link->errno, $link->error); + + } else { + printf("[004] Cannot create SP, [%d] %s.\n", mysqli_errno($link), mysqli_error($link)); + } + + mysqli_close($link); + print "done!"; +?> +--CLEAN-- +<?php +require_once("connect.inc"); +if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) + printf("[c001] [%d] %s\n", mysqli_connect_errno(), mysqli_connect_error()); + +@mysqli_query($link, 'DROP PROCEDURE IF EXISTS p'); + +mysqli_close($link); +?> +--EXPECTF-- +[008] More results: %s +[009] Next results: %s +done!
\ No newline at end of file diff --git a/ext/mysqlnd/config9.m4 b/ext/mysqlnd/config9.m4 index fad2bce82..3c4b5c808 100644 --- a/ext/mysqlnd/config9.m4 +++ b/ext/mysqlnd/config9.m4 @@ -1,11 +1,11 @@ dnl -dnl $Id: config9.m4 304973 2010-10-28 13:46:54Z andrey $ +dnl $Id: config9.m4 309609 2011-03-23 17:14:28Z andrey $ dnl config.m4 for mysqlnd driver -PHP_ARG_ENABLE(disable_mysqlnd_compression_support, whether to disable compressed protocol support in mysqlnd, +PHP_ARG_ENABLE(mysqlnd_compression_support, whether to enable compressed protocol support in mysqlnd, [ --disable-mysqlnd-compression-support - Enable support for the MySQL compressed protocol in mysqlnd], yes) + Disable support for the MySQL compressed protocol in mysqlnd], yes, no) if test -z "$PHP_ZLIB_DIR"; then PHP_ARG_WITH(zlib-dir, for the location of libz, @@ -14,26 +14,22 @@ fi dnl If some extension uses mysqlnd it will get compiled in PHP core if test "$PHP_MYSQLND_ENABLED" = "yes"; then - mysqlnd_sources="mysqlnd.c mysqlnd_charset.c mysqlnd_wireprotocol.c \ - mysqlnd_ps.c mysqlnd_loaddata.c mysqlnd_net.c \ - mysqlnd_ps_codec.c mysqlnd_statistics.c \ + mysqlnd_ps_sources="mysqlnd_ps.c mysqlnd_ps_codec.c" + mysqlnd_base_sources="mysqlnd.c mysqlnd_charset.c mysqlnd_wireprotocol.c \ + mysqlnd_loaddata.c mysqlnd_net.c mysqlnd_statistics.c \ mysqlnd_result.c mysqlnd_result_meta.c mysqlnd_debug.c\ mysqlnd_block_alloc.c php_mysqlnd.c" - PHP_NEW_EXTENSION(mysqlnd, $mysqlnd_sources, no) - PHP_ADD_BUILD_DIR([ext/mysqlnd], 1) - PHP_INSTALL_HEADERS([ext/mysqlnd/]) - - dnl Windows uses config.w32 thus this code is safe for now if test "$PHP_MYSQLND_COMPRESSION_SUPPORT" != "no"; then - if test -z "$PHP_ZLIB_DIR"; then - AC_DEFINE([MYSQLND_COMPRESSION_ENABLED], 1, [Enable compressed protocol support]) - PHP_ADD_LIBRARY_WITH_PATH(z, $PHP_ZLIB_DIR, MYSQLND_SHARED_LIBADD) - MYSQLND_LIBS="$MYSQLND_LIBS -L$PHP_ZLIB_DIR/$PHP_LIBDIR -lz" - fi + AC_DEFINE([MYSQLND_COMPRESSION_WANTED], 1, [Enable compressed protocol support]) fi AC_DEFINE([MYSQLND_SSL_SUPPORTED], 1, [Enable SSL support]) + + mysqlnd_sources="$mysqlnd_base_sources $mysqlnd_ps_sources" + PHP_NEW_EXTENSION(mysqlnd, $mysqlnd_sources, no) + PHP_ADD_BUILD_DIR([ext/mysqlnd], 1) + PHP_INSTALL_HEADERS([ext/mysqlnd/]) fi if test "$PHP_MYSQLND_ENABLED" = "yes" || test "$PHP_MYSQLI" != "no"; then diff --git a/ext/mysqlnd/mysqlnd.c b/ext/mysqlnd/mysqlnd.c index 373224d12..e3f892894 100644 --- a/ext/mysqlnd/mysqlnd.c +++ b/ext/mysqlnd/mysqlnd.c @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: mysqlnd.c 308671 2011-02-25 12:52:21Z andrey $ */ +/* $Id: mysqlnd.c 314740 2011-08-10 14:12:24Z andrey $ */ #include "php.h" #include "mysqlnd.h" #include "mysqlnd_wireprotocol.h" @@ -156,6 +156,7 @@ MYSQLND_METHOD(mysqlnd_conn, free_contents)(MYSQLND * conn TSRMLS_DC) mnd_pefree(conn->unix_socket, pers); conn->unix_socket = NULL; } + DBG_INF_FMT("scheme=%s", conn->scheme); if (conn->scheme) { DBG_INF("Freeing scheme"); mnd_pefree(conn->scheme, pers); @@ -635,7 +636,7 @@ MYSQLND_METHOD(mysqlnd_conn, connect)(MYSQLND * conn, SET_OOM_ERROR(conn->error_info); goto err; /* OOM */ } - DBG_INF_FMT("transport=%s", transport); + DBG_INF_FMT("transport=%s conn->scheme=%s", transport, conn->scheme); conn->scheme = mnd_pestrndup(transport, transport_len, conn->persistent); conn->scheme_len = transport_len; efree(transport); /* allocated by spprintf */ @@ -830,13 +831,13 @@ err: PACKET_FREE(greet_packet); if (errstr) { - DBG_ERR_FMT("[%u] %.64s (trying to connect via %s)", errcode, errstr, conn->scheme); + DBG_ERR_FMT("[%u] %.128s (trying to connect via %s)", errcode, errstr, conn->scheme); SET_CLIENT_ERROR(conn->error_info, errcode? errcode:CR_CONNECTION_ERROR, UNKNOWN_SQLSTATE, errstr); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "[%u] %.64s (trying to connect via %s)", errcode, errstr, conn->scheme); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "[%u] %.128s (trying to connect via %s)", errcode, errstr, conn->scheme); /* no mnd_ since we don't allocate it */ efree(errstr); } - + conn->m->free_contents(conn TSRMLS_CC); MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_CONNECT_FAILURE); DBG_RETURN(FAIL); @@ -877,9 +878,6 @@ PHPAPI MYSQLND * mysqlnd_connect(MYSQLND * conn, object - we are free to kill it! */ conn->m->dtor(conn TSRMLS_CC); - } else { - /* This will also close conn->net->stream if it has been opened */ - conn->m->free_contents(conn TSRMLS_CC); } DBG_RETURN(NULL); } @@ -896,25 +894,18 @@ PHPAPI MYSQLND * mysqlnd_connect(MYSQLND * conn, static enum_func_status MYSQLND_METHOD(mysqlnd_conn, query)(MYSQLND * conn, const char * query, unsigned int query_len TSRMLS_DC) { - enum_func_status ret; + enum_func_status ret = FAIL; DBG_ENTER("mysqlnd_conn::query"); DBG_INF_FMT("conn=%llu query=%s", conn->thread_id, query); - if (PASS != conn->m->simple_command(conn, COM_QUERY, query, query_len, - PROT_LAST /* we will handle the OK packet*/, - FALSE, FALSE TSRMLS_CC)) { - DBG_RETURN(FAIL); - } - CONN_SET_STATE(conn, CONN_QUERY_SENT); - /* - Here read the result set. We don't do it in simple_command because it need - information from the ok packet. We will fetch it ourselves. - */ - ret = conn->m->query_read_result_set_header(conn, NULL TSRMLS_CC); - if (ret == PASS && conn->last_query_type == QUERY_UPSERT && conn->upsert_status.affected_rows) { - MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats, STAT_ROWS_AFFECTED_NORMAL, conn->upsert_status.affected_rows); + if (PASS == conn->m->send_query(conn, query, query_len TSRMLS_CC) && + PASS == conn->m->reap_query(conn TSRMLS_CC)) + { + ret = PASS; + if (conn->last_query_type == QUERY_UPSERT && conn->upsert_status.affected_rows) { + MYSQLND_INC_CONN_STATISTIC_W_VALUE(conn->stats, STAT_ROWS_AFFECTED_NORMAL, conn->upsert_status.affected_rows); + } } - DBG_RETURN(ret); } /* }}} */ @@ -931,7 +922,9 @@ MYSQLND_METHOD(mysqlnd_conn, send_query)(MYSQLND * conn, const char * query, uns ret = conn->m->simple_command(conn, COM_QUERY, query, query_len, PROT_LAST /* we will handle the OK packet*/, FALSE, FALSE TSRMLS_CC); - CONN_SET_STATE(conn, CONN_QUERY_SENT); + if (PASS == ret) { + CONN_SET_STATE(conn, CONN_QUERY_SENT); + } DBG_RETURN(ret); } /* }}} */ @@ -950,6 +943,10 @@ MYSQLND_METHOD(mysqlnd_conn, reap_query)(MYSQLND * conn TSRMLS_DC) DBG_ERR_FMT("Connection not opened, clear or has been closed. State=%u", state); DBG_RETURN(FAIL); } + /* + Here read the result set. We don't do it in simple_command because it need + information from the ok packet. We will fetch it ourselves. + */ DBG_RETURN(conn->m->query_read_result_set_header(conn, NULL TSRMLS_CC)); } /* }}} */ @@ -1291,7 +1288,7 @@ MYSQLND_METHOD(mysqlnd_conn, ssl_set)(MYSQLND * const conn, const char * key, co /* {{{ mysqlnd_conn::escape_string */ static ulong -MYSQLND_METHOD(mysqlnd_conn, escape_string)(const MYSQLND * const conn, char *newstr, const char *escapestr, size_t escapestr_len TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_conn, escape_string)(MYSQLND * const conn, char *newstr, const char *escapestr, size_t escapestr_len TSRMLS_DC) { DBG_ENTER("mysqlnd_conn::escape_string"); DBG_INF_FMT("conn=%llu", conn->thread_id); diff --git a/ext/mysqlnd/mysqlnd.h b/ext/mysqlnd/mysqlnd.h index d85e6200a..571d1f56a 100644 --- a/ext/mysqlnd/mysqlnd.h +++ b/ext/mysqlnd/mysqlnd.h @@ -17,16 +17,17 @@ | Ulf Wendel <uwendel@mysql.com> | +----------------------------------------------------------------------+ */ -/* $Id: mysqlnd.h 308673 2011-02-25 13:11:49Z andrey $ */ +/* $Id: mysqlnd.h 310735 2011-05-03 09:37:53Z andrey $ */ #ifndef MYSQLND_H #define MYSQLND_H -#define MYSQLND_VERSION "mysqlnd 5.0.8-dev - 20102224 - $Revision: 308673 $" +#define MYSQLND_VERSION "mysqlnd 5.0.8-dev - 20102224 - $Revision: 310735 $" #define MYSQLND_VERSION_ID 50008 /* This forces inlining of some accessor functions */ #define MYSQLND_USE_OPTIMISATIONS 0 +#define AUTOCOMMIT_TX_COMMIT_ROLLBACK #define MYSQLND_STRING_TO_INT_CONVERSION /* @@ -50,6 +51,10 @@ #define MYSQLND_DBG_ENABLED 0 #endif +#if defined(MYSQLND_COMPRESSION_WANTED) && defined(HAVE_ZLIB) +#define MYSQLND_COMPRESSION_ENABLED 1 +#endif + #ifdef ZTS #include "TSRM.h" #endif diff --git a/ext/mysqlnd/mysqlnd_charset.c b/ext/mysqlnd/mysqlnd_charset.c index 0e1b387a5..6c8e7a5cc 100644 --- a/ext/mysqlnd/mysqlnd_charset.c +++ b/ext/mysqlnd/mysqlnd_charset.c @@ -325,11 +325,9 @@ static unsigned int mysqlnd_mbcharlen_gbk(unsigned int gbk) /* }}} */ -/* {{{ sjis functions */ -#define valid_sjis_head(c) ((0x81 <= (c) && (c) <= 0x9F) && \ - (0xE0 <= (c) && (c) <= 0xFC)) -#define valid_sjis_tail(c) ((0x40 <= (c) && (c) <= 0x7E) && \ - (0x80 <= (c) && (c) <= 0x7C)) +/* {{{ functions */ +#define valid_sjis_head(c) ((0x81 <= (c) && (c) <= 0x9F) || (0xE0 <= (c) && (c) <= 0xFC)) +#define valid_sjis_tail(c) ((0x40 <= (c) && (c) <= 0x7E) || (0x80 <= (c) && (c) <= 0xFC)) static unsigned int check_mb_sjis(const char *start, const char *end) diff --git a/ext/mysqlnd/mysqlnd_debug.h b/ext/mysqlnd/mysqlnd_debug.h index e412cc730..f6a571d43 100644 --- a/ext/mysqlnd/mysqlnd_debug.h +++ b/ext/mysqlnd/mysqlnd_debug.h @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: mysqlnd_debug.h 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: mysqlnd_debug.h 310768 2011-05-04 19:09:12Z andrey $ */ #ifndef MYSQLND_DEBUG_H #define MYSQLND_DEBUG_H @@ -90,6 +90,15 @@ PHPAPI char * mysqlnd_get_backtrace(uint max_levels, size_t * length TSRMLS_DC); #define DBG_INF_FMT_EX(dbg_obj, ...) do { if (dbg_skip_trace == FALSE) (dbg_obj)->m->log_va((dbg_obj), __LINE__, __FILE__, -1, "info : ", __VA_ARGS__); } while (0) #define DBG_ERR_FMT_EX(dbg_obj, ...) do { if (dbg_skip_trace == FALSE) (dbg_obj)->m->log_va((dbg_obj), __LINE__, __FILE__, -1, "error: ", __VA_ARGS__); } while (0) +#define DBG_BLOCK_ENTER_EX(dbg_obj, block_name) \ + { \ + DBG_ENTER_EX(dbg_obj, (block_name)); + +#define DBG_BLOCK_LEAVE_EX(dbg_obj) \ + DBG_LEAVE_EX((dbg_obj), ;) \ + } \ + + #define DBG_ENTER_EX(dbg_obj, func_name) \ struct timeval __dbg_prof_tp = {0}; \ uint64_t __dbg_prof_start = 0; /* initialization is needed */ \ @@ -103,18 +112,7 @@ PHPAPI char * mysqlnd_get_backtrace(uint max_levels, size_t * length TSRMLS_DC); } \ } while (0); -#define DBG_RETURN_EX(dbg_obj, value) \ - do {\ - if ((dbg_obj)) { \ - uint64_t this_call_duration = 0; \ - if ((dbg_obj)->flags & MYSQLND_DEBUG_PROFILE_CALLS) { \ - DBG_PROFILE_END_TIME(this_call_duration); \ - } \ - (dbg_obj)->m->func_leave((dbg_obj), __LINE__, __FILE__, this_call_duration); \ - } \ - return (value);\ - } while (0) -#define DBG_VOID_RETURN_EX(dbg_obj) \ +#define DBG_LEAVE_EX(dbg_obj, leave) \ do {\ if ((dbg_obj)) { \ uint64_t this_call_duration = 0; \ @@ -123,8 +121,14 @@ PHPAPI char * mysqlnd_get_backtrace(uint max_levels, size_t * length TSRMLS_DC); } \ (dbg_obj)->m->func_leave((dbg_obj), __LINE__, __FILE__, this_call_duration); \ } \ - return;\ - } while (0) + leave \ + } while (0); + +#define DBG_RETURN_EX(dbg_obj, value) DBG_LEAVE_EX(dbg_obj, return (value);) + +#define DBG_VOID_RETURN_EX(dbg_obj) DBG_LEAVE_EX(dbg_obj, return;) + + #else static inline void DBG_INF_EX(MYSQLND_DEBUG * dbg_obj, const char * const msg) {} @@ -132,8 +136,10 @@ static inline void DBG_ERR_EX(MYSQLND_DEBUG * dbg_obj, const char * const msg) { static inline void DBG_INF_FMT_EX(MYSQLND_DEBUG * dbg_obj, ...) {} static inline void DBG_ERR_FMT_EX(MYSQLND_DEBUG * dbg_obj, ...) {} static inline void DBG_ENTER_EX(MYSQLND_DEBUG * dbg_obj, const char * const func_name) {} -#define DBG_RETURN_EX(dbg_obj, value) return (value) -#define DBG_VOID_RETURN_EX(dbg_obj) return +#define DBG_BLOCK_ENTER(bname) { +#define DBG_RETURN_EX(dbg_obj, value) return (value) +#define DBG_VOID_RETURN_EX(dbg_obj) return +#define DBG_BLOCK_LEAVE_EX(dbg_obj) } #endif /* defined(__GNUC__) || (defined(_MSC_VER) && (_MSC_VER >= 1400)) */ @@ -145,8 +151,10 @@ static inline void DBG_ENTER_EX(MYSQLND_DEBUG * dbg_obj, const char * const func #define DBG_ERR_FMT(...) DBG_ERR_FMT_EX(MYSQLND_G(dbg), __VA_ARGS__) #define DBG_ENTER(func_name) DBG_ENTER_EX(MYSQLND_G(dbg), (func_name)) +#define DBG_BLOCK_ENTER(bname) DBG_BLOCK_ENTER_EX(MYSQLND_G(dbg), (bname)) #define DBG_RETURN(value) DBG_RETURN_EX(MYSQLND_G(dbg), (value)) #define DBG_VOID_RETURN DBG_VOID_RETURN_EX(MYSQLND_G(dbg)) +#define DBG_BLOCK_LEAVE DBG_BLOCK_LEAVE_EX(MYSQLND_G(dbg)) #elif MYSQLND_DBG_ENABLED == 0 @@ -157,8 +165,10 @@ static inline void DBG_ERR(const char * const msg) {} static inline void DBG_INF_FMT(const char * const format, ...) {} static inline void DBG_ERR_FMT(const char * const format, ...) {} static inline void DBG_ENTER(const char * const func_name) {} -#define DBG_RETURN(value) return (value) -#define DBG_VOID_RETURN return +#define DBG_BLOCK_ENTER(bname) { +#define DBG_RETURN(value) return (value) +#define DBG_VOID_RETURN return +#define DBG_BLOCK_LEAVE } #endif diff --git a/ext/mysqlnd/mysqlnd_net.c b/ext/mysqlnd/mysqlnd_net.c index f2bd424d2..600a65080 100644 --- a/ext/mysqlnd/mysqlnd_net.c +++ b/ext/mysqlnd/mysqlnd_net.c @@ -24,7 +24,6 @@ #include "mysqlnd_wireprotocol.h" #include "mysqlnd_statistics.h" #include "mysqlnd_debug.h" -#include "mysqlnd_block_alloc.h" #include "ext/standard/sha1.h" #include "php_network.h" #include "zend_ini.h" @@ -907,11 +906,11 @@ mysqlnd_net_init(zend_bool persistent TSRMLS_DC) PHPAPI void mysqlnd_net_free(MYSQLND_NET * const net TSRMLS_DC) { - zend_bool pers = net->persistent; - DBG_ENTER("mysqlnd_net_free"); if (net) { + zend_bool pers = net->persistent; + net->m.free_contents(net TSRMLS_CC); if (net->cmd_buffer.buffer) { DBG_INF("Freeing cmd buffer"); diff --git a/ext/mysqlnd/mysqlnd_priv.h b/ext/mysqlnd/mysqlnd_priv.h index 7ee3d8456..4e2294625 100644 --- a/ext/mysqlnd/mysqlnd_priv.h +++ b/ext/mysqlnd/mysqlnd_priv.h @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: mysqlnd_priv.h 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: mysqlnd_priv.h 311643 2011-05-31 10:35:07Z andrey $ */ #ifndef MYSQLND_PRIV_H #define MYSQLND_PRIV_H @@ -113,7 +113,7 @@ if ((message)) { \ (buf) = mnd_pestrndup((message), (len), (persistent)); \ } else { \ - buf = NULL; \ + (buf) = NULL; \ } \ (buf_len) = (len); \ } @@ -130,22 +130,22 @@ #define SET_EMPTY_ERROR(error_info) \ { \ - error_info.error_no = 0; \ - error_info.error[0] = '\0'; \ - strlcpy(error_info.sqlstate, "00000", sizeof(error_info.sqlstate)); \ + (error_info).error_no = 0; \ + (error_info).error[0] = '\0'; \ + strlcpy((error_info).sqlstate, "00000", sizeof((error_info).sqlstate)); \ } #define SET_CLIENT_ERROR(error_info, a, b, c) \ { \ - error_info.error_no = (a); \ - strlcpy(error_info.sqlstate, (b), sizeof(error_info.sqlstate)); \ - strlcpy(error_info.error, (c), sizeof(error_info.error)); \ + (error_info).error_no = (a); \ + strlcpy((error_info).sqlstate, (b), sizeof((error_info).sqlstate)); \ + strlcpy((error_info).error, (c), sizeof((error_info).error)); \ } -#define SET_OOM_ERROR(error_info) SET_CLIENT_ERROR(error_info, CR_OUT_OF_MEMORY, UNKNOWN_SQLSTATE, mysqlnd_out_of_memory) +#define SET_OOM_ERROR(error_info) SET_CLIENT_ERROR((error_info), CR_OUT_OF_MEMORY, UNKNOWN_SQLSTATE, mysqlnd_out_of_memory) -#define SET_STMT_ERROR(stmt, a, b, c) SET_CLIENT_ERROR(stmt->error_info, a, b, c) +#define SET_STMT_ERROR(stmt, a, b, c) SET_CLIENT_ERROR((stmt)->error_info, a, b, c) #ifdef ZTS @@ -153,7 +153,7 @@ #define CONN_SET_STATE(c, s) (c)->m->set_state((c), (s) TSRMLS_CC) #else #define CONN_GET_STATE(c) ((c)->state) -#define CONN_SET_STATE(c, s) ((c)->state = s) +#define CONN_SET_STATE(c, s) ((c)->state = (s)) #endif diff --git a/ext/mysqlnd/mysqlnd_ps_codec.c b/ext/mysqlnd/mysqlnd_ps_codec.c index d0e84e8dc..4be7ead2d 100644 --- a/ext/mysqlnd/mysqlnd_ps_codec.c +++ b/ext/mysqlnd/mysqlnd_ps_codec.c @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: mysqlnd_ps_codec.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: mysqlnd_ps_codec.c 309655 2011-03-24 16:12:18Z andrey $ */ #include "php.h" #include "mysqlnd.h" #include "mysqlnd_wireprotocol.h" @@ -598,8 +598,7 @@ mysqlnd_stmt_copy_it(zval *** copies, zval *original, unsigned int param_count, /* {{{ mysqlnd_stmt_execute_store_params */ static enum_func_status -mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar **p, - size_t *buf_len, unsigned int null_byte_offset TSRMLS_DC) +mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar **p, size_t *buf_len TSRMLS_DC) { MYSQLND_STMT_DATA * stmt = s->data; unsigned int i = 0; @@ -609,9 +608,37 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar zval **copies = NULL;/* if there are different types */ enum_func_status ret = FAIL; int resend_types_next_time = 0; + size_t null_byte_offset; DBG_ENTER("mysqlnd_stmt_execute_store_params"); + { + unsigned int null_count = (stmt->param_count + 7) / 8; + /* give it some reserved space - 20 bytes */ + if (left < (null_count + 20)) { + unsigned int offset = *p - *buf; + zend_uchar *tmp_buf; + *buf_len = offset + null_count + 20; + tmp_buf = mnd_emalloc(*buf_len); + if (!tmp_buf) { + SET_OOM_ERROR(stmt->error_info); + goto end; + } + memcpy(tmp_buf, *buf, offset); + if (*buf != provided_buffer) { + mnd_efree(*buf); + } + *buf = tmp_buf; + + /* Update our pos pointer */ + *p = *buf + offset; + } + /* put `null` bytes */ + null_byte_offset = *p - *buf; + memset(*p, 0, null_count); + *p += null_count; + } + /* 1. Store type information */ /* check if need to send the types even if stmt->send_types_to_server is 0. This is because @@ -660,6 +687,9 @@ mysqlnd_stmt_execute_store_params(MYSQLND_STMT * s, zend_uchar **buf, zend_uchar goto end; } memcpy(tmp_buf, *buf, offset); + if (*buf != provided_buffer) { + mnd_efree(*buf); + } *buf = tmp_buf; /* Update our pos pointer */ @@ -898,8 +928,6 @@ mysqlnd_stmt_execute_generate_request(MYSQLND_STMT * const s, zend_uchar ** requ zend_uchar *p = stmt->execute_cmd_buffer.buffer, *cmd_buffer = stmt->execute_cmd_buffer.buffer; size_t cmd_buffer_length = stmt->execute_cmd_buffer.length; - unsigned int null_byte_offset, - null_count= (stmt->param_count + 7) / 8; enum_func_status ret; DBG_ENTER("mysqlnd_stmt_execute_generate_request"); @@ -917,12 +945,7 @@ mysqlnd_stmt_execute_generate_request(MYSQLND_STMT * const s, zend_uchar ** requ int1store(p, 1); /* and send 1 for iteration count */ p+= 4; - - null_byte_offset = p - cmd_buffer; - memset(p, 0, null_count); - p += null_count; - - ret = mysqlnd_stmt_execute_store_params(s, &cmd_buffer, &p, &cmd_buffer_length, null_byte_offset TSRMLS_CC); + ret = mysqlnd_stmt_execute_store_params(s, &cmd_buffer, &p, &cmd_buffer_length TSRMLS_CC); *free_buffer = (cmd_buffer != stmt->execute_cmd_buffer.buffer); *request_len = (p - cmd_buffer); diff --git a/ext/mysqlnd/mysqlnd_result.c b/ext/mysqlnd/mysqlnd_result.c index ed4b471bc..582f5c2ed 100644 --- a/ext/mysqlnd/mysqlnd_result.c +++ b/ext/mysqlnd/mysqlnd_result.c @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: mysqlnd_result.c 308540 2011-02-21 16:24:37Z andrey $ */ +/* $Id: mysqlnd_result.c 311122 2011-05-17 09:44:11Z andrey $ */ #include "php.h" #include "mysqlnd.h" #include "mysqlnd_wireprotocol.h" @@ -27,9 +27,7 @@ #include "mysqlnd_result.h" #include "mysqlnd_result_meta.h" #include "mysqlnd_statistics.h" -#include "mysqlnd_charset.h" #include "mysqlnd_debug.h" -#include "ext/standard/basic_functions.h" #define MYSQLND_SILENT @@ -86,11 +84,11 @@ MYSQLND_METHOD(mysqlnd_res, initialize_result_set_rest)(MYSQLND_RES * const resu /* }}} */ -/* {{{ mysqlnd_palloc_zval_ptr_dtor */ -static -void mysqlnd_palloc_zval_ptr_dtor(zval **zv, enum_mysqlnd_res_type type, zend_bool * copy_ctor_called TSRMLS_DC) +/* {{{ mysqlnd_rset_zval_ptr_dtor */ +static void +mysqlnd_rset_zval_ptr_dtor(zval **zv, enum_mysqlnd_res_type type, zend_bool * copy_ctor_called TSRMLS_DC) { - DBG_ENTER("mysqlnd_palloc_zval_ptr_dtor"); + DBG_ENTER("mysqlnd_rset_zval_ptr_dtor"); if (!zv || !*zv) { *copy_ctor_called = FALSE; DBG_ERR_FMT("zv was NULL"); @@ -160,7 +158,7 @@ MYSQLND_METHOD(mysqlnd_res, unbuffered_free_last_data)(MYSQLND_RES * result TSRM DBG_INF_FMT("%u columns to free", result->field_count); for (i = 0; i < result->field_count; i++) { - mysqlnd_palloc_zval_ptr_dtor(&(unbuf->last_row_data[i]), result->type, ©_ctor_called TSRMLS_CC); + mysqlnd_rset_zval_ptr_dtor(&(unbuf->last_row_data[i]), result->type, ©_ctor_called TSRMLS_CC); if (copy_ctor_called) { ++ctor_called_count; } @@ -214,7 +212,7 @@ MYSQLND_METHOD(mysqlnd_res, free_buffered_data)(MYSQLND_RES * result TSRMLS_DC) for (col = field_count - 1; col >= 0; --col) { if (current_row[col]) { zend_bool copy_ctor_called; - mysqlnd_palloc_zval_ptr_dtor(&(current_row[col]), result->type, ©_ctor_called TSRMLS_CC); + mysqlnd_rset_zval_ptr_dtor(&(current_row[col]), result->type, ©_ctor_called TSRMLS_CC); #if MYSQLND_DEBUG_MEMORY DBG_INF_FMT("Copy_ctor_called=%u", copy_ctor_called); #endif @@ -1585,7 +1583,7 @@ MYSQLND_METHOD(mysqlnd_res, fetch_row_c)(MYSQLND_RES * result TSRMLS_DC) } else if (result->m.fetch_row == result->m.fetch_row_normal_unbuffered) { DBG_RETURN(mysqlnd_fetch_row_unbuffered_c(result TSRMLS_CC)); } else { - *((int*)NULL) = 1; + php_error_docref(NULL TSRMLS_CC, E_ERROR, "result->m.fetch_row has invalid value. Report to the developers"); } } DBG_RETURN(ret); diff --git a/ext/mysqlnd/mysqlnd_structs.h b/ext/mysqlnd/mysqlnd_structs.h index a83d5377b..28bbf07a7 100644 --- a/ext/mysqlnd/mysqlnd_structs.h +++ b/ext/mysqlnd/mysqlnd_structs.h @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: mysqlnd_structs.h 308673 2011-02-25 13:11:49Z andrey $ */ +/* $Id: mysqlnd_structs.h 314740 2011-08-10 14:12:24Z andrey $ */ #ifndef MYSQLND_STRUCTS_H #define MYSQLND_STRUCTS_H @@ -338,7 +338,7 @@ struct st_mysqlnd_protocol_methods typedef enum_func_status (*func_mysqlnd_conn__init)(MYSQLND * conn TSRMLS_DC); typedef enum_func_status (*func_mysqlnd_conn__connect)(MYSQLND *conn, const char *host, const char * user, const char * passwd, unsigned int passwd_len, const char * db, unsigned int db_len, unsigned int port, const char * socket_or_pipe, unsigned int mysql_flags TSRMLS_DC); -typedef ulong (*func_mysqlnd_conn__escape_string)(const MYSQLND * const conn, char *newstr, const char *escapestr, size_t escapestr_len TSRMLS_DC); +typedef ulong (*func_mysqlnd_conn__escape_string)(MYSQLND * const conn, char *newstr, const char *escapestr, size_t escapestr_len TSRMLS_DC); typedef enum_func_status (*func_mysqlnd_conn__set_charset)(MYSQLND * const conn, const char * const charset TSRMLS_DC); typedef enum_func_status (*func_mysqlnd_conn__query)(MYSQLND *conn, const char *query, unsigned int query_len TSRMLS_DC); typedef enum_func_status (*func_mysqlnd_conn__send_query)(MYSQLND *conn, const char *query, unsigned int query_len TSRMLS_DC); diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.c b/ext/mysqlnd/mysqlnd_wireprotocol.c index 06ea7dfc0..61582a3aa 100644 --- a/ext/mysqlnd/mysqlnd_wireprotocol.c +++ b/ext/mysqlnd/mysqlnd_wireprotocol.c @@ -24,7 +24,6 @@ #include "mysqlnd_wireprotocol.h" #include "mysqlnd_statistics.h" #include "mysqlnd_debug.h" -#include "mysqlnd_block_alloc.h" #include "ext/standard/sha1.h" #include "zend_ini.h" @@ -2113,11 +2112,10 @@ mysqlnd_protocol_init(zend_bool persistent TSRMLS_DC) PHPAPI void mysqlnd_protocol_free(MYSQLND_PROTOCOL * const protocol TSRMLS_DC) { - zend_bool pers = protocol->persistent; - DBG_ENTER("mysqlnd_protocol_free"); if (protocol) { + zend_bool pers = protocol->persistent; mnd_pefree(protocol, pers); } DBG_VOID_RETURN; diff --git a/ext/mysqlnd/php_mysqlnd.c b/ext/mysqlnd/php_mysqlnd.c index 3b25a72cc..47c546536 100644 --- a/ext/mysqlnd/php_mysqlnd.c +++ b/ext/mysqlnd/php_mysqlnd.c @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: php_mysqlnd.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: php_mysqlnd.c 314376 2011-08-06 14:47:44Z felipe $ */ #include "php.h" #include "php_ini.h" #include "mysqlnd.h" @@ -31,7 +31,7 @@ * Every user visible function must have an entry in mysqlnd_functions[]. */ static zend_function_entry mysqlnd_functions[] = { - {NULL, NULL, NULL} /* Must be the last line in mysqlnd_functions[] */ + PHP_FE_END }; /* }}} */ @@ -266,7 +266,7 @@ static PHP_RSHUTDOWN_FUNCTION(mysqlnd) static const zend_module_dep mysqlnd_deps[] = { ZEND_MOD_REQUIRED("standard") - {NULL, NULL, NULL} + ZEND_MOD_END }; /* {{{ mysqlnd_module_entry diff --git a/ext/oci8/config.m4 b/ext/oci8/config.m4 index 5b7010f35..38c3fa8fa 100644 --- a/ext/oci8/config.m4 +++ b/ext/oci8/config.m4 @@ -1,5 +1,5 @@ dnl -dnl $Id: config.m4 300753 2010-06-25 21:18:09Z sixd $ +dnl $Id: config.m4 309823 2011-03-29 21:37:45Z sixd $ dnl if test -z "$SED"; then @@ -47,48 +47,37 @@ AC_DEFUN([AC_OCI8_CHECK_LIB_DIR],[ ]) AC_DEFUN([AC_OCI8IC_VERSION],[ - AC_MSG_CHECKING([Oracle Instant Client version]) - if test -f $PHP_OCI8_INSTANT_CLIENT/libnnz11.$SHLIB_SUFFIX_NAME; then - if test -f $PHP_OCI8_INSTANT_CLIENT/libclntsh.$SHLIB_SUFFIX_NAME.11.1; then - if test ! -f $PHP_OCI8_INSTANT_CLIENT/libclntsh.$SHLIB_SUFFIX_NAME; then - AC_MSG_ERROR([Link from $PHP_OCI8_INSTANT_CLIENT/libclntsh.$SHLIB_SUFFIX_NAME to libclntsh.$SHLIB_SUFFIX_NAME.11.1 not found]) - fi - OCI8_ORACLE_VERSION=11.1 - else - AC_MSG_ERROR([Oracle Instant Client library version not supported]) - fi - elif test -f $PHP_OCI8_INSTANT_CLIENT/libnnz10.$SHLIB_SUFFIX_NAME; then - if test -f $PHP_OCI8_INSTANT_CLIENT/libclntsh.$SHLIB_SUFFIX_NAME.10.1; then - if test ! -f $PHP_OCI8_INSTANT_CLIENT/libclntsh.$SHLIB_SUFFIX_NAME; then - AC_MSG_ERROR([Link from $PHP_OCI8_INSTANT_CLIENT/libclntsh.$SHLIB_SUFFIX_NAME to libclntsh.$SHLIB_SUFFIX_NAME.10.1 not found]) - fi - OCI8_ORACLE_VERSION=10.1 - else - AC_MSG_ERROR([Oracle Instant Client library version not supported]) + AC_MSG_CHECKING([Oracle Instant Client library version compatibility]) + OCI8_LCS_BASE=$PHP_OCI8_INSTANT_CLIENT/libclntsh.$SHLIB_SUFFIX_NAME + OCI8_LCS=`ls $OCI8_LCS_BASE.*.1 2> /dev/null | tail -1` # Oracle 10g, 11g etc + OCI8_NNZ=`ls $PHP_OCI8_INSTANT_CLIENT/libnnz*.$SHLIB_SUFFIX_NAME 2> /dev/null | tail -1` + if test -f "$OCI8_NNZ" && test -f "$OCI8_LCS"; then + if test ! -f "$OCI8_LCS_BASE"; then + AC_MSG_ERROR([Link from $OCI8_LCS_BASE to $OCI8_LCS_BASE.*.1 not found]) fi + OCI8_ORACLE_VERSION=`echo $OCI8_LCS | $PHP_OCI8_SED -e 's/.*\.\(.*\)\.1$/\1.1/'` else - AC_MSG_ERROR([Oracle Instant Client libraries not found]) + AC_MSG_ERROR([Oracle Instant Client libraries libnnz.$SHLIB_SUFFIX_NAME and libclntsh.$SHLIB_SUFFIX_NAME not found]) fi AC_MSG_RESULT([$OCI8_ORACLE_VERSION]) ]) - AC_DEFUN([AC_OCI8_ORACLE_VERSION],[ - AC_MSG_CHECKING([Oracle version]) + AC_MSG_CHECKING([Oracle library version compatibility]) + OCI8_LCS_BASE=$OCI8_DIR/$OCI8_LIB_DIR/libclntsh.$SHLIB_SUFFIX_NAME + OCI8_LCS=`ls $OCI8_LCS_BASE.*.1 2> /dev/null | tail -1` # Oracle 10g, 11g etc if test -s "$OCI8_DIR/orainst/unix.rgs"; then OCI8_ORACLE_VERSION=`grep '"ocommon"' $OCI8_DIR/orainst/unix.rgs | $PHP_OCI8_SED 's/[ ][ ]*/:/g' | cut -d: -f 6 | cut -c 2-4` test -z "$OCI8_ORACLE_VERSION" && OCI8_ORACLE_VERSION=7.3 - elif test -f $OCI8_DIR/$OCI8_LIB_DIR/libclntsh.$SHLIB_SUFFIX_NAME.11.1; then - OCI8_ORACLE_VERSION=11.1 - elif test -f $OCI8_DIR/$OCI8_LIB_DIR/libclntsh.$SHLIB_SUFFIX_NAME.10.1; then - dnl There is no case for Oracle 10.2. Oracle 10.2 libraries have a 10.1 suffix for drop-in compatibility with Oracle 10.1 - OCI8_ORACLE_VERSION=10.1 - elif test -f $OCI8_DIR/$OCI8_LIB_DIR/libclntsh.$SHLIB_SUFFIX_NAME.9.0; then + elif test -f "$OCI8_LCS"; then + dnl Oracle 10g, 11g etc. The x.2 version libraries are named x.1 for drop in compatibility + OCI8_ORACLE_VERSION=`echo $OCI8_LCS | $PHP_OCI8_SED -e 's/.*\.\(.*\)\.1$/\1.1/'` + elif test -f $OCI8_LCS_BASE.9.0; then dnl There is no case for Oracle 9.2. Oracle 9.2 libraries have a 9.0 suffix for drop-in compatibility with Oracle 9.0 OCI8_ORACLE_VERSION=9.0 - elif test -f $OCI8_DIR/$OCI8_LIB_DIR/libclntsh.$SHLIB_SUFFIX_NAME.8.0; then + elif test -f $OCI8_LCS_BASE.8.0; then OCI8_ORACLE_VERSION=8.1 - elif test -f $OCI8_DIR/$OCI8_LIB_DIR/libclntsh.$SHLIB_SUFFIX_NAME.1.0; then + elif test -f $OCI8_LCS_BASE.1.0; then OCI8_ORACLE_VERSION=8.0 elif test -f $OCI8_DIR/$OCI8_LIB_DIR/libclntsh.a; then if test -f $OCI8_DIR/$OCI8_LIB_DIR/libcore4.a; then @@ -97,7 +86,7 @@ AC_DEFUN([AC_OCI8_ORACLE_VERSION],[ OCI8_ORACLE_VERSION=8.1 fi else - AC_MSG_ERROR(Oracle client libraries not found) + AC_MSG_ERROR(Oracle libclntsh.$SHLIB_SUFFIX_NAME client library not found) fi AC_MSG_RESULT($OCI8_ORACLE_VERSION) ]) @@ -106,8 +95,8 @@ AC_DEFUN([AC_OCI8_ORACLE_VERSION],[ dnl --with-oci8=shared,instantclient,/path/to/client/dir/lib dnl or dnl --with-oci8=shared,/path/to/oracle/home -PHP_ARG_WITH(oci8, for Oracle (OCI8) support, -[ --with-oci8[=DIR] Include Oracle (OCI8) support. DIR defaults to \$ORACLE_HOME. +PHP_ARG_WITH(oci8, for Oracle Database OCI8 support, +[ --with-oci8[=DIR] Include Oracle Database OCI8 support. DIR defaults to \$ORACLE_HOME. Use --with-oci8=instantclient,/path/to/instant/client/lib to use an Oracle Instant Client installation]) @@ -258,12 +247,8 @@ if test "$PHP_OCI8" != "no"; then ]) ;; - 10.1|11.1) - AC_DEFINE(HAVE_OCI_LOB_READ2,1,[ ]) - ;; - *) - AC_MSG_ERROR([Oracle version $OCI8_ORACLE_VERSION is not supported]) + AC_DEFINE(HAVE_OCI_LOB_READ2,1,[ ]) ;; esac @@ -335,16 +320,8 @@ if test "$PHP_OCI8" != "no"; then fi AC_OCI8IC_VERSION($PHP_OCI8_INSTANT_CLIENT) - case $OCI8_ORACLE_VERSION in - 10.1|11.1) - PHP_ADD_LIBRARY(clntsh, 1, OCI8_SHARED_LIBADD) - PHP_ADD_LIBPATH($PHP_OCI8_INSTANT_CLIENT, OCI8_SHARED_LIBADD) - ;; - - *) - AC_MSG_ERROR([Oracle Instant Client version $PHP_OCI8_INSTANT_CLIENT is not supported]) - ;; - esac + PHP_ADD_LIBRARY(clntsh, 1, OCI8_SHARED_LIBADD) + PHP_ADD_LIBPATH($PHP_OCI8_INSTANT_CLIENT, OCI8_SHARED_LIBADD) AC_DEFINE(HAVE_OCI_INSTANT_CLIENT,1,[ ]) AC_DEFINE(HAVE_OCI_LOB_READ2,1,[ ]) diff --git a/ext/oci8/oci8.c b/ext/oci8/oci8.c index e0497bf67..b2a42ed46 100644 --- a/ext/oci8/oci8.c +++ b/ext/oci8/oci8.c @@ -26,7 +26,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: oci8.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: oci8.c 313688 2011-07-25 23:40:57Z sixd $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -37,6 +37,13 @@ #include "php_ini.h" #include "ext/standard/php_smart_str.h" +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif +#ifdef PHP_WIN32 +#include "win32/php_stdint.h" +#endif + #if HAVE_OCI8 #if PHP_MAJOR_VERSION > 5 @@ -51,6 +58,14 @@ #include "php_oci8_int.h" #include "zend_hash.h" +#if defined(HAVE_STDINT_H) || defined(PHP_WIN32) +#define OCI8_INT_TO_PTR(I) ((void *)(intptr_t)(I)) +#define OCI8_PTR_TO_INT(P) ((int)(intptr_t)(P)) +#else +#define OCI8_INT_TO_PTR(I) ((void *)(I)) +#define OCI8_PTR_TO_INT(P) ((int)(P)) +#endif + ZEND_DECLARE_MODULE_GLOBALS(oci) #if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 1) || (PHP_MAJOR_VERSION > 5) /* This "if" allows PECL builds from this file to be portable to older PHP releases */ @@ -447,6 +462,9 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_result, 0, 0, 2) ZEND_ARG_INFO(0, column_number_or_name) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO(arginfo_oci_client_version, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(arginfo_oci_server_version, 0, 0, 1) ZEND_ARG_INFO(0, connection_resource) ZEND_END_ARG_INFO() @@ -681,6 +699,7 @@ static unsigned char arginfo_oci_bind_array_by_name[] = { 3, BYREF_NONE, BYREF_N #define arginfo_oci_password_change NULL #define arginfo_oci_new_cursor NULL #define arginfo_oci_result NULL +#define arginfo_oci_client_version NULL #define arginfo_oci_server_version NULL #define arginfo_oci_statement_type NULL #define arginfo_oci_num_rows NULL @@ -761,6 +780,7 @@ PHP_FUNCTION(oci_num_fields); PHP_FUNCTION(oci_parse); PHP_FUNCTION(oci_new_cursor); PHP_FUNCTION(oci_result); +PHP_FUNCTION(oci_client_version); PHP_FUNCTION(oci_server_version); PHP_FUNCTION(oci_statement_type); PHP_FUNCTION(oci_num_rows); @@ -836,6 +856,7 @@ zend_function_entry php_oci_functions[] = { PHP_FE(oci_parse, arginfo_oci_parse) PHP_FE(oci_new_cursor, arginfo_oci_new_cursor) PHP_FE(oci_result, arginfo_oci_result) + PHP_FE(oci_client_version, arginfo_oci_client_version) PHP_FE(oci_server_version, arginfo_oci_server_version) PHP_FE(oci_statement_type, arginfo_oci_statement_type) PHP_FE(oci_num_rows, arginfo_oci_num_rows) @@ -931,7 +952,11 @@ zend_function_entry php_oci_functions[] = { PHP_FALIAS(ocicollsize, oci_collection_size, arginfo_oci_collection_size) PHP_FALIAS(ocicollmax, oci_collection_max, arginfo_oci_collection_max) PHP_FALIAS(ocicolltrim, oci_collection_trim, arginfo_oci_collection_trim) +#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION == 3 && PHP_RELEASE_VERSION >= 7) || (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 4) || (PHP_MAJOR_VERSION > 5) + PHP_FE_END +#else {NULL,NULL,NULL} +#endif }; static @@ -962,7 +987,11 @@ zend_function_entry php_oci_lob_class_functions[] = { PHP_FALIAS(save, oci_lob_save, arginfo_oci_lob_save_method) PHP_FALIAS(savefile, oci_lob_import, arginfo_oci_lob_import_method) PHP_FALIAS(free, oci_free_descriptor, arginfo_oci_free_descriptor_method) +#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION == 3 && PHP_RELEASE_VERSION >= 7) || (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 4) || (PHP_MAJOR_VERSION > 5) + PHP_FE_END +#else {NULL,NULL,NULL} +#endif }; static @@ -979,7 +1008,11 @@ zend_function_entry php_oci_coll_class_functions[] = { PHP_FALIAS(max, oci_collection_max, arginfo_oci_collection_max_method) PHP_FALIAS(trim, oci_collection_trim, arginfo_oci_collection_trim_method) PHP_FALIAS(free, oci_free_collection, arginfo_oci_collection_free_method) +#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION == 3 && PHP_RELEASE_VERSION >= 7) || (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 4) || (PHP_MAJOR_VERSION > 5) + PHP_FE_END +#else {NULL,NULL,NULL} +#endif }; zend_module_entry oci8_module_entry = { @@ -1042,7 +1075,7 @@ static void php_oci_init_global_handles(TSRMLS_D) #endif if (OCI_G(env) && OCIErrorGet(OCI_G(env), (ub4)1, NULL, &ora_error_code, tmp_buf, (ub4)PHP_OCI_ERRBUF_LEN, (ub4)OCI_HTYPE_ENV) == OCI_SUCCESS - && *tmp_buf) { + && *tmp_buf) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", tmp_buf); } @@ -1295,17 +1328,23 @@ PHP_RSHUTDOWN_FUNCTION(oci) PHP_MINFO_FUNCTION(oci) { char buf[32]; + char *ver; php_info_print_table_start(); php_info_print_table_row(2, "OCI8 Support", "enabled"); php_info_print_table_row(2, "Version", PHP_OCI8_VERSION); - php_info_print_table_row(2, "Revision", "$Revision: 306939 $"); + php_info_print_table_row(2, "Revision", "$Revision: 313688 $"); snprintf(buf, sizeof(buf), "%ld", OCI_G(num_persistent)); php_info_print_table_row(2, "Active Persistent Connections", buf); snprintf(buf, sizeof(buf), "%ld", OCI_G(num_links)); php_info_print_table_row(2, "Active Connections", buf); +#if ((OCI_MAJOR_VERSION > 10) || ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2))) + php_oci_client_get_version(&ver TSRMLS_CC); + php_info_print_table_row(2, "Oracle Run-time Client Library Version", ver); + efree(ver); +#endif #if defined(OCI_MAJOR_VERSION) && defined(OCI_MINOR_VERSION) snprintf(buf, sizeof(buf), "%d.%d", OCI_MAJOR_VERSION, OCI_MINOR_VERSION); #elif defined(PHP_OCI8_ORACLE_VERSION) @@ -1754,13 +1793,13 @@ php_oci_connection *php_oci_do_connect_ex(char *username, int username_len, char php_error_docref(NULL TSRMLS_CC, E_WARNING, "Privileged connect is disabled. Enable oci8.privileged_connect to be able to connect as SYSOPER or SYSDBA"); return NULL; } - /* Disable privileged connections in Safe Mode (N.b. safe mode has been removed in PHP - * 6 anyway) - */ +#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION < 4) || (PHP_MAJOR_VERSION < 5) + /* Safe mode has been removed in PHP 5.4 */ if (PG(safe_mode)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Privileged connect is disabled in Safe Mode"); return NULL; } +#endif } } @@ -1853,7 +1892,7 @@ php_oci_connection *php_oci_do_connect_ex(char *username, int username_len, char int type, link; void *ptr; - link = (int) le->ptr; + link = OCI8_PTR_TO_INT(le->ptr); ptr = zend_list_find(link,&type); if (ptr && (type == le_connection)) { connection = (php_oci_connection *)ptr; @@ -1922,7 +1961,11 @@ php_oci_connection *php_oci_do_connect_ex(char *username, int username_len, char memcmp(tmp->hash_key, hashed_details.c, hashed_details.len) == 0 && zend_list_addref(connection->rsrc_id) == SUCCESS) { /* do nothing */ } else { +#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 3) || (PHP_MAJOR_VERSION > 5) + connection->rsrc_id = zend_list_insert(connection, le_pconnection TSRMLS_CC); +#else connection->rsrc_id = zend_list_insert(connection, le_pconnection); +#endif /* Persistent connections: For old close semantics we artificially * bump up the refcount to prevent the non-persistent destructor * from getting called until request shutdown. The refcount is @@ -2066,7 +2109,11 @@ php_oci_connection *php_oci_do_connect_ex(char *username, int username_len, char new_le.ptr = connection; new_le.type = le_pconnection; connection->used_this_request = 1; +#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 3) || (PHP_MAJOR_VERSION > 5) + connection->rsrc_id = zend_list_insert(connection, le_pconnection TSRMLS_CC); +#else connection->rsrc_id = zend_list_insert(connection, le_pconnection); +#endif /* Persistent connections: For old close semantics we artificially bump up the refcount to * prevent the non-persistent destructor from getting called until request shutdown. The @@ -2079,13 +2126,21 @@ php_oci_connection *php_oci_do_connect_ex(char *username, int username_len, char OCI_G(num_persistent)++; OCI_G(num_links)++; } else if (!exclusive) { +#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 3) || (PHP_MAJOR_VERSION > 5) + connection->rsrc_id = zend_list_insert(connection, le_connection TSRMLS_CC); +#else connection->rsrc_id = zend_list_insert(connection, le_connection); - new_le.ptr = (void *)connection->rsrc_id; +#endif + new_le.ptr = OCI8_INT_TO_PTR(connection->rsrc_id); new_le.type = le_index_ptr; zend_hash_update(&EG(regular_list), connection->hash_key, strlen(connection->hash_key)+1, (void *)&new_le, sizeof(zend_rsrc_list_entry), NULL); OCI_G(num_links)++; } else { +#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 3) || (PHP_MAJOR_VERSION > 5) + connection->rsrc_id = zend_list_insert(connection, le_connection TSRMLS_CC); +#else connection->rsrc_id = zend_list_insert(connection, le_connection); +#endif OCI_G(num_links)++; } @@ -2368,6 +2423,30 @@ int php_oci_password_change(php_oci_connection *connection, char *user, int user return 0; } /* }}} */ + +/* {{{ php_oci_client_get_version() + * + * Get Oracle client library version + */ +void php_oci_client_get_version(char **version TSRMLS_DC) +{ + char version_buff[256]; + sword major_version = 0; + sword minor_version = 0; + sword update_num = 0; + sword patch_num = 0; + sword port_update_num = 0; + +#if ((OCI_MAJOR_VERSION > 10) || ((OCI_MAJOR_VERSION == 10) && (OCI_MINOR_VERSION >= 2))) /* OCIClientVersion only available 10.2 onwards */ + PHP_OCI_CALL(OCIClientVersion, (&major_version, &minor_version, &update_num, &patch_num, &port_update_num)); + snprintf(version_buff, sizeof(version_buff), "%d.%d.%d.%d.%d", major_version, minor_version, update_num, patch_num, port_update_num); +#else + memcpy(version_buff, "Unknown", sizeof("Unknown")); +#endif + *version = estrdup(version_buff); +} /* }}} */ + + /* {{{ php_oci_server_get_version() * * Get Oracle server version @@ -2778,7 +2857,11 @@ static php_oci_spool *php_oci_get_spool(char *username, int username_len, char * } spool_le.ptr = session_pool; spool_le.type = le_psessionpool; +#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 3) || (PHP_MAJOR_VERSION > 5) + zend_list_insert(session_pool, le_psessionpool TSRMLS_CC); +#else zend_list_insert(session_pool, le_psessionpool); +#endif zend_hash_update(&EG(persistent_list), session_pool->spool_hash_key, strlen(session_pool->spool_hash_key)+1,(void *)&spool_le, sizeof(zend_rsrc_list_entry),NULL); } else if (spool_out_le->type == le_psessionpool && strlen(((php_oci_spool *)(spool_out_le->ptr))->spool_hash_key) == spool_hashed_details.len && diff --git a/ext/oci8/oci8_interface.c b/ext/oci8/oci8_interface.c index 63b359dc0..e405f12f4 100644 --- a/ext/oci8/oci8_interface.c +++ b/ext/oci8/oci8_interface.c @@ -25,7 +25,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: oci8_interface.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: oci8_interface.c 312017 2011-06-10 17:38:07Z sixd $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -233,26 +233,37 @@ PHP_FUNCTION(oci_lob_import) int filename_len; if (getThis()) { +#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 3) || (PHP_MAJOR_VERSION > 5) + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "p", &filename, &filename_len) == FAILURE) { +#else if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &filename, &filename_len) == FAILURE) { +#endif return; } } else { +#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 3) || (PHP_MAJOR_VERSION > 5) + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Op", &z_descriptor, oci_lob_class_entry_ptr, &filename, &filename_len) == FAILURE) { +#else if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Os", &z_descriptor, oci_lob_class_entry_ptr, &filename, &filename_len) == FAILURE) { +#endif return; } } +#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION < 4) || (PHP_MAJOR_VERSION < 5) + /* The "p" parsing parameter handles this case in PHP 5.4+ */ if (strlen(filename) != filename_len) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Filename cannot contain null bytes"); RETURN_FALSE; } +#endif if (zend_hash_find(Z_OBJPROP_P(z_descriptor), "descriptor", sizeof("descriptor"), (void **)&tmp) == FAILURE) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to find descriptor property"); RETURN_FALSE; } - + PHP_OCI_ZVAL_TO_DESCRIPTOR(*tmp, descriptor); if (php_oci_lob_import(descriptor, filename TSRMLS_CC)) { @@ -641,12 +652,12 @@ PHP_FUNCTION(oci_lob_erase) if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|ll", &offset, &length) == FAILURE) { return; } - + if (ZEND_NUM_ARGS() > 0 && offset < 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Offset must be greater than or equal to 0"); RETURN_FALSE; } - + if (ZEND_NUM_ARGS() > 1 && length < 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Length must be greater than or equal to 0"); RETURN_FALSE; @@ -656,7 +667,7 @@ PHP_FUNCTION(oci_lob_erase) if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|ll", &z_descriptor, oci_lob_class_entry_ptr, &offset, &length) == FAILURE) { return; } - + if (ZEND_NUM_ARGS() > 1 && offset < 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Offset must be greater than or equal to 0"); RETURN_FALSE; @@ -674,7 +685,7 @@ PHP_FUNCTION(oci_lob_erase) } PHP_OCI_ZVAL_TO_DESCRIPTOR(*tmp, descriptor); - + if (php_oci_lob_erase(descriptor, offset, length, &bytes_erased TSRMLS_CC)) { RETURN_FALSE; } @@ -872,7 +883,11 @@ PHP_FUNCTION(oci_lob_export) ub4 lob_length; if (getThis()) { +#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 3) || (PHP_MAJOR_VERSION > 5) + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "p|ll", &filename, &filename_len, &start, &length) == FAILURE) { +#else if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ll", &filename, &filename_len, &start, &length) == FAILURE) { +#endif return; } @@ -886,7 +901,11 @@ PHP_FUNCTION(oci_lob_export) } } else { +#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 3) || (PHP_MAJOR_VERSION > 5) + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Op|ll", &z_descriptor, oci_lob_class_entry_ptr, &filename, &filename_len, &start, &length) == FAILURE) { +#else if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Os|ll", &z_descriptor, oci_lob_class_entry_ptr, &filename, &filename_len, &start, &length) == FAILURE) { +#endif return; } @@ -900,10 +919,13 @@ PHP_FUNCTION(oci_lob_export) } } +#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION < 4) || (PHP_MAJOR_VERSION < 5) + /* The "p" parsing parameter handles this case in PHP 5.4+ */ if (strlen(filename) != filename_len) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Filename cannot contain null bytes"); RETURN_FALSE; } +#endif if (zend_hash_find(Z_OBJPROP_P(z_descriptor), "descriptor", sizeof("descriptor"), (void **)&tmp) == FAILURE) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to find descriptor property"); @@ -929,15 +951,22 @@ PHP_FUNCTION(oci_lob_export) RETURN_FALSE; } +#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION < 4) || (PHP_MAJOR_VERSION < 5) + /* Safe mode has been removed in PHP 5.4 */ if (PG(safe_mode) && (!php_checkuid(filename, NULL, CHECKUID_CHECK_FILE_AND_DIR))) { RETURN_FALSE; } +#endif if (php_check_open_basedir(filename TSRMLS_CC)) { RETURN_FALSE; } +#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 3) || (PHP_MAJOR_VERSION > 5) + stream = php_stream_open_wrapper_ex(filename, "w", REPORT_ERRORS, NULL, NULL); +#else stream = php_stream_open_wrapper_ex(filename, "w", ENFORCE_SAFE_MODE | REPORT_ERRORS, NULL, NULL); +#endif block_length = PHP_OCI_LOB_BUFFER_SIZE; if (block_length > length) { @@ -1881,11 +1910,13 @@ PHP_FUNCTION(oci_password_change) int user_len, pass_old_len, pass_new_len, dbname_len; php_oci_connection *connection; - /* Disable in Safe Mode */ +#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION < 4) || (PHP_MAJOR_VERSION < 5) + /* Safe mode has been removed in PHP 5.4 */ if (PG(safe_mode)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "is disabled in Safe Mode"); RETURN_FALSE; } +#endif if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "rsss", &z_connection, &user, &user_len, &pass_old, &pass_old_len, &pass_new, &pass_new_len) == SUCCESS) { PHP_OCI_ZVAL_TO_CONNECTION(z_connection, connection); @@ -1971,6 +2002,17 @@ PHP_FUNCTION(oci_result) } /* }}} */ +/* {{{ proto string oci_client_version() + Return a string containing runtime client library version information */ +PHP_FUNCTION(oci_client_version) +{ + char *version = NULL; + + php_oci_client_get_version(&version TSRMLS_CC); + RETURN_STRING(version, 0); +} +/* }}} */ + /* {{{ proto string oci_server_version(resource connection) Return a string containing server version information */ PHP_FUNCTION(oci_server_version) diff --git a/ext/oci8/oci8_lob.c b/ext/oci8/oci8_lob.c index f24294e3e..1045e35c1 100644 --- a/ext/oci8/oci8_lob.c +++ b/ext/oci8/oci8_lob.c @@ -25,7 +25,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: oci8_lob.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: oci8_lob.c 313754 2011-07-27 00:04:23Z sixd $ */ @@ -724,7 +724,12 @@ int php_oci_lob_import (php_oci_descriptor *descriptor, char *filename TSRMLS_DC char buf[8192]; ub4 offset = 1; +#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 3) || (PHP_MAJOR_VERSION > 5) + /* Safe mode has been removed in PHP 5.4 */ + if (php_check_open_basedir(filename TSRMLS_CC)) { +#else if ((PG(safe_mode) && (!php_checkuid(filename, NULL, CHECKUID_CHECK_FILE_AND_DIR))) || php_check_open_basedir(filename TSRMLS_CC)) { +#endif return 1; } @@ -888,7 +893,7 @@ int php_oci_lob_is_equal (php_oci_descriptor *descriptor_first, php_oci_descript /* {{{ php_oci_lob_write_tmp() Create temporary LOB and write data to it */ -int php_oci_lob_write_tmp (php_oci_descriptor *descriptor, ub1 type, char *data, int data_len TSRMLS_DC) +int php_oci_lob_write_tmp (php_oci_descriptor *descriptor, long type, char *data, int data_len TSRMLS_DC) { php_oci_connection *connection = descriptor->connection; OCILobLocator *lob = descriptor->descriptor; @@ -900,7 +905,7 @@ int php_oci_lob_write_tmp (php_oci_descriptor *descriptor, ub1 type, char *data, /* only these two are allowed */ break; default: - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid temporary lob type: %d", type); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid temporary lob type: %ld", type); return 1; break; } @@ -916,7 +921,7 @@ int php_oci_lob_write_tmp (php_oci_descriptor *descriptor, ub1 type, char *data, lob, OCI_DEFAULT, OCI_DEFAULT, - type, + (ub1)type, OCI_ATTR_NOCACHE, OCI_DURATION_SESSION ) diff --git a/ext/oci8/oci8_statement.c b/ext/oci8/oci8_statement.c index e1f3ba4a6..fe5ba8399 100644 --- a/ext/oci8/oci8_statement.c +++ b/ext/oci8/oci8_statement.c @@ -25,7 +25,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: oci8_statement.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: oci8_statement.c 313754 2011-07-27 00:04:23Z sixd $ */ #ifdef HAVE_CONFIG_H @@ -887,7 +887,7 @@ int php_oci_bind_post_exec(void *data TSRMLS_DC) * their reallocation but (i) any IN binds either interned or * not should already be null terminated and (ii) for OUT * binds, php_oci_bind_out_callback() should have allocated a - * new string that can be realloced. + * new string that we can modify here. */ Z_STRVAL_P(bind->zval) = erealloc(Z_STRVAL_P(bind->zval), Z_STRLEN_P(bind->zval)+1); Z_STRVAL_P(bind->zval)[ Z_STRLEN_P(bind->zval) ] = '\0'; @@ -1480,7 +1480,7 @@ int php_oci_bind_array_by_name(php_oci_statement *statement, char *name, int nam name_len, (dvoid *) bindp->array.elements, (sb4) bind->array.max_length, - type, + (ub2)type, (dvoid *)bindp->array.indicators, (ub2 *)bind->array.element_lengths, (ub2 *)0, /* bindp->array.retcodes, */ diff --git a/ext/oci8/package.xml b/ext/oci8/package.xml index 5c15108a3..0bb6eb532 100644 --- a/ext/oci8/package.xml +++ b/ext/oci8/package.xml @@ -33,12 +33,12 @@ http://pear.php.net/dtd/package-2.0.xsd"> <active>no</active> </lead> - <date>2010-12-09</date> - <time>20:00:00</time> + <date>2011-06-10</date> + <time>12:00:00</time> <version> - <release>1.4.5</release> - <api>1.4.5</api> + <release>1.4.6</release> + <api>1.4.6</api> </version> <stability> <release>stable</release> @@ -46,7 +46,8 @@ http://pear.php.net/dtd/package-2.0.xsd"> </stability> <license uri="http://www.php.net/license">PHP</license> <notes> - Protect against null bytes in LOB filenames (http://news.php.net/php.internals/50202) + Added oci_client_version() returning the runtime Oracle client library version + Made OCI8 extension buildable with PHP 5.4-development code </notes> <contents> <dir name="/"> @@ -87,8 +88,20 @@ http://pear.php.net/dtd/package-2.0.xsd"> <file name="bind_empty.phpt" role="test" /> <file name="bind_long.phpt" role="test" /> <file name="bind_long_raw.phpt" role="test" /> + <file name="bind_misccoltypes_errs.phpt" role="test" /> + <file name="bind_misccoltypes.phpt" role="test" /> + <file name="bind_number.phpt" role="test" /> + <file name="bind_query.phpt" role="test" /> <file name="bind_raw.phpt" role="test" /> <file name="bind_rowid.phpt" role="test" /> + <file name="bind_sqltafc.phpt" role="test" /> + <file name="bind_sqltchr_1.phpt" role="test" /> + <file name="bind_sqltchr_2.phpt" role="test" /> + <file name="bind_sqltint.phpt" role="test" /> + <file name="bind_sqltnum.phpt" role="test" /> + <file name="bind_unsupported_1.phpt" role="test" /> + <file name="bind_unsupported_2.phpt" role="test" /> + <file name="bind_unsupported_3.phpt" role="test" /> <file name="bug26133.phpt" role="test" /> <file name="bug27303_1_11gR1.phpt" role="test" /> <file name="bug27303_1.phpt" role="test" /> @@ -125,8 +138,12 @@ http://pear.php.net/dtd/package-2.0.xsd"> <file name="bug46994.phpt" role="test" /> <file name="bug47189.phpt" role="test" /> <file name="bug47281.phpt" role="test" /> + <file name="bug47281_tt.phpt" role="test" /> <file name="bug51253.phpt" role="test" /> - <file name="bug51291.phpt" role="test" /> + <file name="bug51291_1.phpt" role="test" /> + <file name="bug51291_2.phpt" role="test" /> + <file name="clientversion_92.phpt" role="test" /> + <file name="clientversion.phpt" role="test" /> <file name="close.phpt" role="test" /> <file name="coll_001.phpt" role="test" /> <file name="coll_002_func.phpt" role="test" /> @@ -193,15 +210,19 @@ http://pear.php.net/dtd/package-2.0.xsd"> <file name="cursor_bind.phpt" role="test" /> <file name="cursors_old.phpt" role="test" /> <file name="cursors.phpt" role="test" /> + <file name="dbmsoutput.phpt" role="test" /> <file name="debug.phpt" role="test" /> + <file name="default_prefetch0.phpt" role="test" /> <file name="default_prefetch1.phpt" role="test" /> <file name="default_prefetch2.phpt" role="test" /> <file name="default_prefetch.phpt" role="test" /> + <file name="define0.phpt" role="test" /> <file name="define1.phpt" role="test" /> <file name="define2.phpt" role="test" /> <file name="define3.phpt" role="test" /> <file name="define4.phpt" role="test" /> <file name="define5.phpt" role="test" /> + <file name="define6.phpt" role="test" /> <file name="define_old.phpt" role="test" /> <file name="define.phpt" role="test" /> <file name="descriptors.phpt" role="test" /> @@ -225,6 +246,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> <file name="driver_name.phpt" role="test" /> <file name="drop_table.inc" role="test" /> <file name="drop_type.inc" role="test" /> + <file name="dupcolnames.phpt" role="test" /> <file name="edition_1.phpt" role="test" /> <file name="edition_2.phpt" role="test" /> <file name="error1.phpt" role="test" /> @@ -239,6 +261,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> <file name="extauth_02.phpt" role="test" /> <file name="extauth_03.phpt" role="test" /> <file name="extauth_04.phpt" role="test" /> + <file name="fetch_all1.phpt" role="test" /> <file name="fetch_all2.phpt" role="test" /> <file name="fetch_all3.phpt" role="test" /> <file name="fetch_all4.phpt" role="test" /> @@ -249,12 +272,15 @@ http://pear.php.net/dtd/package-2.0.xsd"> <file name="fetch_into1.phpt" role="test" /> <file name="fetch_into2.phpt" role="test" /> <file name="fetch_into.phpt" role="test" /> + <file name="fetch_object_1.phpt" role="test" /> <file name="fetch_object_2.phpt" role="test" /> <file name="fetch_object.phpt" role="test" /> <file name="fetch.phpt" role="test" /> <file name="fetch_row.phpt" role="test" /> + <file name="field_funcs0.phpt" role="test" /> <file name="field_funcs1.phpt" role="test" /> <file name="field_funcs2.phpt" role="test" /> + <file name="field_funcs3.phpt" role="test" /> <file name="field_funcs_old.phpt" role="test" /> <file name="field_funcs.phpt" role="test" /> <file name="function_aliases.phpt" role="test" /> @@ -302,6 +328,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> <file name="lob_041.phpt" role="test" /> <file name="lob_042.phpt" role="test" /> <file name="lob_043.phpt" role="test" /> + <file name="lob_044.phpt" role="test" /> <file name="lob_aliases.phpt" role="test" /> <file name="lob_null.phpt" role="test" /> <file name="lob_temp1.phpt" role="test" /> @@ -309,6 +336,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> <file name="minfo.phpt" role="test" /> <file name="null_byte_1.phpt" role="test" /> <file name="null_byte_2.phpt" role="test" /> + <file name="null_byte_3.phpt" role="test" /> <file name="num.phpt" role="test" /> <file name="oci8safemode.phpt" role="test" /> <file name="oci_execute_segfault.phpt" role="test" /> @@ -333,6 +361,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> <file name="refcur_prefetch_1.phpt" role="test" /> <file name="refcur_prefetch_2.phpt" role="test" /> <file name="refcur_prefetch_3.phpt" role="test" /> + <file name="refcur_prefetch_4.phpt" role="test" /> <file name="reflection1.phpt" role="test" /> <file name="reflection2.phpt" role="test" /> <file name="select_null.phpt" role="test" /> @@ -366,8 +395,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> <required> <php> <min>4.3.9</min> - <max>6.0.0</max> - <exclude>6.0.0</exclude> + <max>5.4.99</max> </php> <pearinstaller> <min>1.4.0b1</min> @@ -382,6 +410,21 @@ http://pear.php.net/dtd/package-2.0.xsd"> <release> <version> + <release>1.4.5</release> + <api>1.4.5</api> + </version> + <stability> + <release>stable</release> + <api>stable</api> + </stability> + <license uri="http://www.php.net/license">PHP</license> + <notes> + Protect against null bytes in LOB filenames (http://news.php.net/php.internals/50202) + </notes> +</release> + +<release> + <version> <release>1.4.4</release> <api>1.4.4</api> </version> @@ -464,7 +507,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> oci_set_action oci_set_client_info oci_set_client_identifier - + These set values that are visible/used by the database. They are useful for tracing, authentication and auditing. @@ -493,7 +536,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> oci_bind_by_name 7. Bug fixes: - PECL bug #16842 (oci_error returns false when NO_DATA_FOUND is raised) + PECL bug #16842 (oci_error returns false when NO_DATA_FOUND is raised) </notes> </release> diff --git a/ext/oci8/php_oci8.h b/ext/oci8/php_oci8.h index 309b1623c..5a5c8d0b7 100644 --- a/ext/oci8/php_oci8.h +++ b/ext/oci8/php_oci8.h @@ -25,7 +25,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: php_oci8.h 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: php_oci8.h 312535 2011-06-27 17:24:55Z sixd $ */ #if HAVE_OCI8 # ifndef PHP_OCI8_H @@ -46,7 +46,7 @@ */ #undef PHP_OCI8_VERSION #endif -#define PHP_OCI8_VERSION "1.4.5" +#define PHP_OCI8_VERSION "1.4.6" extern zend_module_entry oci8_module_entry; #define phpext_oci8_ptr &oci8_module_entry diff --git a/ext/oci8/php_oci8_int.h b/ext/oci8/php_oci8_int.h index 0f3cf0f58..c88284816 100644 --- a/ext/oci8/php_oci8_int.h +++ b/ext/oci8/php_oci8_int.h @@ -25,7 +25,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: php_oci8_int.h 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: php_oci8_int.h 313754 2011-07-27 00:04:23Z sixd $ */ #if HAVE_OCI8 # ifndef PHP_OCI8_INT_H @@ -385,6 +385,7 @@ int php_oci_connection_commit(php_oci_connection * TSRMLS_DC); int php_oci_connection_release(php_oci_connection *connection TSRMLS_DC); int php_oci_password_change(php_oci_connection *, char *, int, char *, int, char *, int TSRMLS_DC); +void php_oci_client_get_version(char ** TSRMLS_DC); int php_oci_server_get_version(php_oci_connection *, char ** TSRMLS_DC); void php_oci_fetch_row(INTERNAL_FUNCTION_PARAMETERS, int, int); @@ -404,7 +405,7 @@ int php_oci_lob_get_buffering (php_oci_descriptor *); int php_oci_lob_copy (php_oci_descriptor *, php_oci_descriptor *, long TSRMLS_DC); int php_oci_lob_close (php_oci_descriptor * TSRMLS_DC); int php_oci_temp_lob_close (php_oci_descriptor * TSRMLS_DC); -int php_oci_lob_write_tmp (php_oci_descriptor *, ub1, char *, int TSRMLS_DC); +int php_oci_lob_write_tmp (php_oci_descriptor *, long, char *, int TSRMLS_DC); void php_oci_lob_free(php_oci_descriptor * TSRMLS_DC); int php_oci_lob_import(php_oci_descriptor *descriptor, char * TSRMLS_DC); int php_oci_lob_append (php_oci_descriptor *, php_oci_descriptor * TSRMLS_DC); diff --git a/ext/oci8/tests/array_bind_001.phpt b/ext/oci8/tests/array_bind_001.phpt index 1310325ad..6a3746c2d 100644 --- a/ext/oci8/tests/array_bind_001.phpt +++ b/ext/oci8/tests/array_bind_001.phpt @@ -61,7 +61,7 @@ echo "Done\n"; --EXPECTF-- Warning: oci_bind_array_by_name(): OCI-21560: argument 3 is null, invalid, or out of range in %s on line %d -Warning: oci_execute(): ORA-01008: not all variables bound in %s on line %d +Warning: oci_execute(): ORA-%r(01008|57000)%r: %s in %s on line %d array(1) { [0]=> string(0) "" diff --git a/ext/oci8/tests/array_bind_002.phpt b/ext/oci8/tests/array_bind_002.phpt index 4c76df42f..82b57dc73 100644 --- a/ext/oci8/tests/array_bind_002.phpt +++ b/ext/oci8/tests/array_bind_002.phpt @@ -61,7 +61,7 @@ echo "Done\n"; --EXPECTF-- Warning: oci_bind_array_by_name(): Maximum array length must be greater than zero in %s on line %d -Warning: oci_execute(): ORA-01008: not all variables bound in %s on line %d +Warning: oci_execute(): ORA-%r(01008|57000)%r: %s in %s on line %d array(5) { [0]=> string(9) "06-DEC-05" diff --git a/ext/oci8/tests/array_bind_003.phpt b/ext/oci8/tests/array_bind_003.phpt index 497e46e49..94bce02e3 100644 --- a/ext/oci8/tests/array_bind_003.phpt +++ b/ext/oci8/tests/array_bind_003.phpt @@ -1,7 +1,10 @@ --TEST-- oci_bind_array_by_name() and invalid values 3 --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/array_bind_004.phpt b/ext/oci8/tests/array_bind_004.phpt index 1ddf85149..1eb1fc7fa 100644 --- a/ext/oci8/tests/array_bind_004.phpt +++ b/ext/oci8/tests/array_bind_004.phpt @@ -1,7 +1,10 @@ --TEST-- oci_bind_array_by_name() and invalid values 4 --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/array_bind_005.phpt b/ext/oci8/tests/array_bind_005.phpt index 58dadc20c..eaa3c4ea2 100644 --- a/ext/oci8/tests/array_bind_005.phpt +++ b/ext/oci8/tests/array_bind_005.phpt @@ -1,7 +1,10 @@ --TEST-- oci_bind_array_by_name() and invalid values 5 --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/array_bind_006.phpt b/ext/oci8/tests/array_bind_006.phpt index e229dd872..f13aca399 100644 --- a/ext/oci8/tests/array_bind_006.phpt +++ b/ext/oci8/tests/array_bind_006.phpt @@ -1,7 +1,10 @@ --TEST-- oci_bind_array_by_name(), SQLT_CHR and default max_length --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/array_bind_007.phpt b/ext/oci8/tests/array_bind_007.phpt index 10c92a8e7..c926bc0ab 100644 --- a/ext/oci8/tests/array_bind_007.phpt +++ b/ext/oci8/tests/array_bind_007.phpt @@ -61,7 +61,7 @@ echo "Done\n"; --EXPECTF-- Warning: oci_bind_array_by_name(): Unknown or unsupported datatype given: -1 in %s on line %d -Warning: oci_execute(): ORA-01008: not all variables bound in %s on line %d +Warning: oci_execute(): ORA-%r(01008|57000)%r: %s in %s on line %d array(5) { [0]=> int(1) diff --git a/ext/oci8/tests/array_bind_008.phpt b/ext/oci8/tests/array_bind_008.phpt index c44304c11..df2c35ccb 100644 --- a/ext/oci8/tests/array_bind_008.phpt +++ b/ext/oci8/tests/array_bind_008.phpt @@ -1,7 +1,10 @@ --TEST-- oci_bind_array_by_name() and invalid values 8 --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php @@ -60,9 +63,9 @@ echo "Done\n"; ?> --EXPECTF-- Warning: oci_execute(): ORA-06550: line %d, column %d: -PLS-00418: array bind type must match PL/SQL table row type -ORA-06550: line %d, column %d: -PL/SQL: Statement ignored in %s on line %d +PLS-00418: %s +ORA-06550: %s +PL/SQL: %s array(5) { [0]=> string(1) "1" diff --git a/ext/oci8/tests/array_bind_010.phpt b/ext/oci8/tests/array_bind_010.phpt index a77ed913e..ede82c97f 100644 --- a/ext/oci8/tests/array_bind_010.phpt +++ b/ext/oci8/tests/array_bind_010.phpt @@ -7,7 +7,7 @@ oci_bind_array_by_name() and invalid values 8 require dirname(__FILE__).'/connect.inc'; -$statement = oci_parse($c, 'SELECT user FROM v$session'); +$statement = oci_parse($c, 'SELECT user FROM all_objects'); $array = Array(1,2,3,4,5); diff --git a/ext/oci8/tests/array_bind_011.phpt b/ext/oci8/tests/array_bind_011.phpt index e8e00a809..9f43d1ac9 100644 --- a/ext/oci8/tests/array_bind_011.phpt +++ b/ext/oci8/tests/array_bind_011.phpt @@ -61,7 +61,7 @@ echo "Done\n"; --EXPECTF-- Warning: oci_bind_array_by_name(): You must provide max length value for empty arrays in %s on line %d -Warning: oci_execute(): ORA-01008: not all variables bound in %s on line %d +Warning: oci_execute(): ORA-%r(01008|57000)%r: %s in %s on line %d array(0) { } Done diff --git a/ext/oci8/tests/array_bind_012.phpt b/ext/oci8/tests/array_bind_012.phpt index 2208f0b3b..38e1701a4 100644 --- a/ext/oci8/tests/array_bind_012.phpt +++ b/ext/oci8/tests/array_bind_012.phpt @@ -7,7 +7,7 @@ oci_bind_array_by_name(), SQLT_CHR, default max_length and empty array require dirname(__FILE__).'/connect.inc'; -$statement = oci_parse($c, 'SELECT user FROM v$session'); +$statement = oci_parse($c, 'SELECT user FROM all_objects'); $array = array(); diff --git a/ext/oci8/tests/array_bind_014.phpt b/ext/oci8/tests/array_bind_014.phpt index bd9fdf133..9ab023635 100644 --- a/ext/oci8/tests/array_bind_014.phpt +++ b/ext/oci8/tests/array_bind_014.phpt @@ -1,7 +1,10 @@ --TEST-- oci_bind_array_by_name() and NUMBERs --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/array_bind_date.phpt b/ext/oci8/tests/array_bind_date.phpt index 63da558f9..926d7cfab 100644 --- a/ext/oci8/tests/array_bind_date.phpt +++ b/ext/oci8/tests/array_bind_date.phpt @@ -1,7 +1,10 @@ --TEST-- oci_bind_array_by_name() and SQLT_ODT --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/array_bind_date1.phpt b/ext/oci8/tests/array_bind_date1.phpt index ebf767570..c8c1f1a5e 100644 --- a/ext/oci8/tests/array_bind_date1.phpt +++ b/ext/oci8/tests/array_bind_date1.phpt @@ -1,7 +1,10 @@ --TEST-- oci_bind_array_by_name() and SQLT_ODT --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/array_bind_float.phpt b/ext/oci8/tests/array_bind_float.phpt index 1aafb2431..5246f6380 100644 --- a/ext/oci8/tests/array_bind_float.phpt +++ b/ext/oci8/tests/array_bind_float.phpt @@ -1,7 +1,10 @@ --TEST-- oci_bind_array_by_name() and SQLT_FLT --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/array_bind_float1.phpt b/ext/oci8/tests/array_bind_float1.phpt index ead85890f..53b551f1c 100644 --- a/ext/oci8/tests/array_bind_float1.phpt +++ b/ext/oci8/tests/array_bind_float1.phpt @@ -1,7 +1,10 @@ --TEST-- oci_bind_array_by_name() and SQLT_FLT --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/array_bind_int.phpt b/ext/oci8/tests/array_bind_int.phpt index 3c8bfd4f5..2f34979f2 100644 --- a/ext/oci8/tests/array_bind_int.phpt +++ b/ext/oci8/tests/array_bind_int.phpt @@ -1,7 +1,10 @@ --TEST-- oci_bind_array_by_name() and SQLT_INT --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/array_bind_int1.phpt b/ext/oci8/tests/array_bind_int1.phpt index 5e06de876..49fea2aa0 100644 --- a/ext/oci8/tests/array_bind_int1.phpt +++ b/ext/oci8/tests/array_bind_int1.phpt @@ -1,7 +1,10 @@ --TEST-- oci_bind_array_by_name() and SQLT_INT --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/array_bind_str.phpt b/ext/oci8/tests/array_bind_str.phpt index c8e1e0cdb..87291bd17 100644 --- a/ext/oci8/tests/array_bind_str.phpt +++ b/ext/oci8/tests/array_bind_str.phpt @@ -1,7 +1,10 @@ --TEST-- -oci_bind_array_by_name() and SQLT_AVC +oci_bind_array_by_name() and SQLT_CHR --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/array_bind_str1.phpt b/ext/oci8/tests/array_bind_str1.phpt index af4dbfe22..3f60ee8c2 100644 --- a/ext/oci8/tests/array_bind_str1.phpt +++ b/ext/oci8/tests/array_bind_str1.phpt @@ -1,7 +1,10 @@ --TEST-- -oci_bind_array_by_name() and SQLT_AVC +oci_bind_array_by_name() and SQLT_CHR --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/b47243_1.phpt b/ext/oci8/tests/b47243_1.phpt index 9f04f302c..291122917 100644 --- a/ext/oci8/tests/b47243_1.phpt +++ b/ext/oci8/tests/b47243_1.phpt @@ -1,7 +1,10 @@ --TEST-- Bug #47243 (Crash on exit with ZTS mode) --SKIPIF-- -<?php if (!extension_loaded('oci8')) die ("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/b47243_2.phpt b/ext/oci8/tests/b47243_2.phpt index 08f5f528c..ae96953d7 100644 --- a/ext/oci8/tests/b47243_2.phpt +++ b/ext/oci8/tests/b47243_2.phpt @@ -1,7 +1,10 @@ --TEST-- Bug #47243 (Crash on exit with ZTS mode) --SKIPIF-- -<?php if (!extension_loaded('oci8')) die ("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/b47243_3.phpt b/ext/oci8/tests/b47243_3.phpt index 0decb3487..cc57c20d6 100644 --- a/ext/oci8/tests/b47243_3.phpt +++ b/ext/oci8/tests/b47243_3.phpt @@ -1,7 +1,10 @@ --TEST-- Bug #47243 (Crash on exit with ZTS mode) --SKIPIF-- -<?php if (!extension_loaded('oci8')) die ("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/bind_char_1.phpt b/ext/oci8/tests/bind_char_1.phpt index 24ab1fc82..a668294d4 100644 --- a/ext/oci8/tests/bind_char_1.phpt +++ b/ext/oci8/tests/bind_char_1.phpt @@ -4,20 +4,14 @@ SELECT oci_bind_by_name with SQLT_AFC aka CHAR <?php if (!extension_loaded('oci8')) die ("skip no oci8 extension"); require(dirname(__FILE__)."/connect.inc"); -$sv = oci_server_version($c); -$sv = preg_match('/Release 1[01]\.2\./', $sv, $matches); -if ($sv !== 1) { - die ("skip expected output only valid when using Oracle 10gR2 or 11gR2 databases"); -} else { - ob_start(); - phpinfo(INFO_MODULES); - $phpinfo = ob_get_clean(); - $iv = preg_match('/Oracle .*Version => 1[1]\./', $phpinfo); - if ($iv != 1) { - die ("skip test expected to work only with Oracle 11g or greater version of client"); - } +if (preg_match('/Release 1[01]\.2\./', oci_server_version($c), $matches) !== 1) { + die("skip expected output only valid when using Oracle 10gR2 or 11gR2 databases"); +} else if (preg_match('/^11\./', oci_client_version()) != 1) { + die("skip test expected to work only with Oracle 11g or greater version of client"); } ?> +--ENV-- +NLS_LANG= --FILE-- <?php @@ -33,10 +27,7 @@ $stmtarray = array( "insert into bind_char_tab values (3, NULL, 'abc ')" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - @oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); // Run Test @@ -206,12 +197,7 @@ $stmtarray = array( "drop table bind_char_tab" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} - -oci_close($c); +oci8_test_sql_execute($c, $stmtarray); echo "Done\n"; diff --git a/ext/oci8/tests/bind_char_1_11gR1.phpt b/ext/oci8/tests/bind_char_1_11gR1.phpt index 60b7142ff..55973a61b 100644 --- a/ext/oci8/tests/bind_char_1_11gR1.phpt +++ b/ext/oci8/tests/bind_char_1_11gR1.phpt @@ -27,10 +27,7 @@ $stmtarray = array( "insert into bind_char_tab values (3, NULL, 'abc ')" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - @oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); // Run Test @@ -200,12 +197,7 @@ $stmtarray = array( "drop table bind_char_tab" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} - -oci_close($c); +oci8_test_sql_execute($c, $stmtarray); echo "Done\n"; diff --git a/ext/oci8/tests/bind_char_2.phpt b/ext/oci8/tests/bind_char_2.phpt index fa2e547db..53785e066 100644 --- a/ext/oci8/tests/bind_char_2.phpt +++ b/ext/oci8/tests/bind_char_2.phpt @@ -4,20 +4,14 @@ SELECT oci_bind_by_name with SQLT_AFC aka CHAR and dates <?php if (!extension_loaded('oci8')) die ("skip no oci8 extension"); require(dirname(__FILE__)."/connect.inc"); -$sv = oci_server_version($c); -$sv = preg_match('/Release 1[01]\.2\./', $sv, $matches); -if ($sv !== 1) { - die ("skip expected output only valid when using Oracle 10gR2 or 11gR2 databases"); -} else { - ob_start(); - phpinfo(INFO_MODULES); - $phpinfo = ob_get_clean(); - $iv = preg_match('/Oracle .*Version => 1[1]\./', $phpinfo); - if ($iv != 1) { - die ("skip test expected to work only with Oracle 11g or greater version of client"); - } +if (preg_match('/Release 1[01]\.2\./', oci_server_version($c), $matches) !== 1) { + die("skip expected output only valid when using Oracle 10gR2 or 11gR2 databases"); +} else if (preg_match('/^11\./', oci_client_version()) != 1) { + die("skip test expected to work only with Oracle 11g or greater version of client"); } ?> +--ENV-- +NLS_LANG= --FILE-- <?php @@ -32,10 +26,7 @@ $stmtarray = array( "insert into bind_char_tab values (1, '2008-04-20')", ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - @oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); // Run Test @@ -95,12 +86,7 @@ $stmtarray = array( "drop table bind_char_tab" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} - -oci_close($c); +oci8_test_sql_execute($c, $stmtarray); echo "Done\n"; diff --git a/ext/oci8/tests/bind_char_2_11gR1.phpt b/ext/oci8/tests/bind_char_2_11gR1.phpt index 68a872fc4..357a716e6 100644 --- a/ext/oci8/tests/bind_char_2_11gR1.phpt +++ b/ext/oci8/tests/bind_char_2_11gR1.phpt @@ -24,10 +24,7 @@ $stmtarray = array( "insert into bind_char_tab values (1, '2008-04-20')", ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - @oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); // Run Test @@ -87,12 +84,7 @@ $stmtarray = array( "drop table bind_char_tab" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} - -oci_close($c); +oci8_test_sql_execute($c, $stmtarray); echo "Done\n"; diff --git a/ext/oci8/tests/bind_char_3.phpt b/ext/oci8/tests/bind_char_3.phpt index 052981ace..a459f616b 100644 --- a/ext/oci8/tests/bind_char_3.phpt +++ b/ext/oci8/tests/bind_char_3.phpt @@ -4,20 +4,14 @@ PL/SQL oci_bind_by_name with SQLT_AFC aka CHAR to CHAR parameter <?php if (!extension_loaded('oci8')) die ("skip no oci8 extension"); require(dirname(__FILE__)."/connect.inc"); -$sv = oci_server_version($c); -$sv = preg_match('/Release 1[01]\.2\./', $sv, $matches); -if ($sv !== 1) { - die ("skip expected output only valid when using Oracle 10gR2 11gR2 databases"); -} else { - ob_start(); - phpinfo(INFO_MODULES); - $phpinfo = ob_get_clean(); - $iv = preg_match('/Oracle .*Version => 1[1]\./', $phpinfo); - if ($iv != 1) { - die ("skip test expected to work only with Oracle 11g or greater version of client"); - } +if (preg_match('/Release 1[01]\.2\./', oci_server_version($c), $matches) !== 1) { + die("skip expected output only valid when using Oracle 10gR2 or 11gR2 databases"); +} else if (preg_match('/^11\./', oci_client_version()) != 1) { + die("skip test expected to work only with Oracle 11g or greater version of client"); } ?> +--ENV-- +NLS_LANG= --FILE-- <?php @@ -28,11 +22,8 @@ require(dirname(__FILE__).'/connect.inc'); $stmtarray = array( "create or replace function bind_char_3_fn(p1 char) return char as begin return p1; end;", ); - -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - @oci_execute($s); -} + +oci8_test_sql_execute($c, $stmtarray); // Run Test @@ -243,18 +234,11 @@ function do_e($s) // Cleanup -//require(dirname(__FILE__).'/drop_table.inc'); - $stmtarray = array( "drop function bind_char_3_fn" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} - -oci_close($c); +oci8_test_sql_execute($c, $stmtarray); echo "Done\n"; diff --git a/ext/oci8/tests/bind_char_3_11gR1.phpt b/ext/oci8/tests/bind_char_3_11gR1.phpt index aaa537119..1e7da47ed 100644 --- a/ext/oci8/tests/bind_char_3_11gR1.phpt +++ b/ext/oci8/tests/bind_char_3_11gR1.phpt @@ -21,10 +21,7 @@ $stmtarray = array( "create or replace function bind_char_3_fn(p1 char) return char as begin return p1; end;", ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - @oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); // Run Test @@ -235,18 +232,11 @@ function do_e($s) // Cleanup -//require(dirname(__FILE__).'/drop_table.inc'); - $stmtarray = array( "drop function bind_char_3_fn" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} - -oci_close($c); +oci8_test_sql_execute($c, $stmtarray); echo "Done\n"; diff --git a/ext/oci8/tests/bind_char_4.phpt b/ext/oci8/tests/bind_char_4.phpt index fd38d902c..34d703c52 100644 --- a/ext/oci8/tests/bind_char_4.phpt +++ b/ext/oci8/tests/bind_char_4.phpt @@ -4,20 +4,14 @@ PL/SQL oci_bind_by_name with SQLT_AFC aka CHAR to VARCHAR2 parameter <?php if (!extension_loaded('oci8')) die ("skip no oci8 extension"); require(dirname(__FILE__)."/connect.inc"); -$sv = oci_server_version($c); -$sv = preg_match('/Release 1[01]\.2\./', $sv, $matches); -if ($sv !== 1) { - die ("skip expected output only valid when using Oracle 10gR2 or 11gR2 databases"); -} else { - ob_start(); - phpinfo(INFO_MODULES); - $phpinfo = ob_get_clean(); - $iv = preg_match('/Oracle .*Version => 1[1]\./', $phpinfo); - if ($iv != 1) { - die ("skip test expected to work only with Oracle 11g or greater version of client"); - } +if (preg_match('/Release 1[01]\.2\./', oci_server_version($c), $matches) !== 1) { + die("skip expected output only valid when using Oracle 10gR2 or 11gR2 databases"); +} else if (preg_match('/^11\./', oci_client_version()) != 1) { + die("skip test expected to work only with Oracle 11g or greater version of client"); } ?> +--ENV-- +NLS_LANG= --FILE-- <?php @@ -31,10 +25,7 @@ $stmtarray = array( "create or replace function bind_char_3_fn(p1 varchar2) return varchar2 as begin return p1; end;", ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - @oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); // Run Test @@ -245,18 +236,11 @@ function do_e($s) // Cleanup -//require(dirname(__FILE__).'/drop_table.inc'); - $stmtarray = array( "drop function bind_char_3_fn" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} - -oci_close($c); +oci8_test_sql_execute($c, $stmtarray); echo "Done\n"; diff --git a/ext/oci8/tests/bind_char_4_11gR1.phpt b/ext/oci8/tests/bind_char_4_11gR1.phpt index c4f7968e1..87b7daba8 100644 --- a/ext/oci8/tests/bind_char_4_11gR1.phpt +++ b/ext/oci8/tests/bind_char_4_11gR1.phpt @@ -23,10 +23,7 @@ $stmtarray = array( "create or replace function bind_char_3_fn(p1 varchar2) return varchar2 as begin return p1; end;", ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - @oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); // Run Test @@ -237,18 +234,11 @@ function do_e($s) // Cleanup -//require(dirname(__FILE__).'/drop_table.inc'); - $stmtarray = array( "drop function bind_char_3_fn" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} - -oci_close($c); +oci8_test_sql_execute($c, $stmtarray); echo "Done\n"; diff --git a/ext/oci8/tests/bind_long.phpt b/ext/oci8/tests/bind_long.phpt index ba6bd4d04..40c579992 100644 --- a/ext/oci8/tests/bind_long.phpt +++ b/ext/oci8/tests/bind_long.phpt @@ -1,7 +1,10 @@ --TEST-- bind LONG field --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php @@ -13,16 +16,37 @@ $stmt = oci_parse($c, "drop table phptestlng"); $stmt = oci_parse($c, "create table phptestlng( id number(10), filetxt long)"); oci_execute($stmt); +echo "Test 1\n"; + $stmt = oci_parse ($c, "insert into phptestlng (id, filetxt) values (:id, :filetxt)"); $i=1; $filetxt = file_get_contents( dirname(__FILE__)."/test.txt"); + +oci_bind_by_name( $stmt, ":id", $i, -1); +oci_bind_by_name( $stmt, ":filetxt", $filetxt, -1, SQLT_LNG); +oci_execute($stmt, OCI_DEFAULT); +oci_commit($c); + +$stmt = oci_parse($c, "SELECT filetxt FROM phptestlng where id = 1"); +oci_execute($stmt); + +$row = oci_fetch_row($stmt); +var_dump(md5($row[0])); +var_dump(strlen($row[0])); + +echo "Test 2 - test multi chunk fetch\n"; + +$stmt = oci_parse ($c, "insert into phptestlng (id, filetxt) values (:id, :filetxt)"); +$i=2; +$filetxt = str_repeat($filetxt, 600); + oci_bind_by_name( $stmt, ":id", $i, -1); oci_bind_by_name( $stmt, ":filetxt", $filetxt, -1, SQLT_LNG); oci_execute($stmt, OCI_DEFAULT); oci_commit($c); -$stmt = oci_parse($c, "SELECT filetxt FROM phptestlng"); +$stmt = oci_parse($c, "SELECT filetxt FROM phptestlng where id = 2"); oci_execute($stmt); $row = oci_fetch_row($stmt); @@ -36,6 +60,10 @@ echo "Done\n"; ?> --EXPECT-- +Test 1 string(32) "5c7c34abf7ea51936785062dbfcaeddc" int(394) +Test 2 - test multi chunk fetch +string(32) "ee2e98b977341cfb8e037066e5fcb909" +int(236400) Done diff --git a/ext/oci8/tests/bind_long_raw.phpt b/ext/oci8/tests/bind_long_raw.phpt index 2a9962eac..e48bbd33e 100644 --- a/ext/oci8/tests/bind_long_raw.phpt +++ b/ext/oci8/tests/bind_long_raw.phpt @@ -1,7 +1,10 @@ --TEST-- bind LONG RAW field --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/bind_misccoltypes.phpt b/ext/oci8/tests/bind_misccoltypes.phpt new file mode 100644 index 000000000..0da8c8bf8 --- /dev/null +++ b/ext/oci8/tests/bind_misccoltypes.phpt @@ -0,0 +1,369 @@ +--TEST-- +Bind miscellaneous column types using default types +--SKIPIF-- +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +if (preg_match('/^1[012]\./', oci_client_version()) != 1) { + die("skip test expected to work only with Oracle 10g or greater version of client"); +} +?> +--FILE-- +<?php + +require(dirname(__FILE__).'/connect.inc'); + +// Initialization + +$stmtarray = array( + + "alter session set nls_date_format = 'DD-MON-YY'", + + "drop table bind_misccoltypes_tab", + + "create table bind_misccoltypes_tab ( + id number, + char_t char(1), + char_t10 char(10), + varchar2_t10 varchar2(10), + number_t number, + number_t92 number(9,2), + number_t6 number(6), + date_t date, + timestamp_t timestamp, + float_t float, + binary_float_t binary_float, + binary_double_t binary_double, + decimal_t decimal, + integer_t integer, + nchar_t nchar(10), + nvarchar2_t10 nvarchar2(10), + varchar_t10 varchar(10) )", +); + +oci8_test_sql_execute($c, $stmtarray); + +function check_col($c, $colname, $id) +{ + $s = oci_parse($c, "select $colname from bind_misccoltypes_tab where id = :id"); + oci_bind_by_name($s, ":id", $id); + oci_execute($s); + oci_fetch_all($s, $r); + var_dump($r); +} + +// Tests + +echo "\nTEST86 insert all ORATYPE values\n"; + +$insert_sql = "INSERT INTO bind_misccoltypes_tab ( id, " + . " char_t, " + . " char_t10, " + . " varchar2_t10, " + . " number_t, " + . " number_t92, " + . " number_t6, " + . " date_t, " + . " timestamp_t, " + . " float_t, " + . " binary_float_t, " + . " binary_double_t, " + . " decimal_t, " + . " integer_t, " + . " nchar_t, " + . " nvarchar2_t10, " + . " varchar_t10) " + . " VALUES (:n1, " + . " :c1, " + . " :c2, " + . " :c3, " + . " :n2, " + . " :n3, " + . " :n4, " + . " to_date(:d1, 'YYYY-MM-DD HH24:MI:SS'), " + . " to_timestamp(:d1, 'YYYY-MM-DD HH24:MI:SS'), " + . " :n5, " + . " :n5, " + . " :n5, " + . " :n1, " + . " :n1, " + . " :c4, " + . " :c5, " + . " :c6) "; + + +$n1 = "86"; +$c1 = "C"; +$c2 = "char10"; +$c3 = "varchar210"; +$n2 = "-123.456"; +$n3 = "789.346"; +$n4 = "123456.023"; +$n5 = "12345678901234567890123456789012345678.723"; +$d1 = "2010-03-29 13:09:15"; +$c4 = "nchar10"; +$c5 = "nvarchar2x"; +$c6 = "varchar"; + +$s = oci_parse($c, $insert_sql); +oci_bind_by_name($s, ":n1", $n1); +oci_bind_by_name($s, ":c1", $c1); +oci_bind_by_name($s, ":c2", $c2); +oci_bind_by_name($s, ":c3", $c3); +oci_bind_by_name($s, ":n2", $n2); +oci_bind_by_name($s, ":n3", $n3); +oci_bind_by_name($s, ":n4", $n4); +oci_bind_by_name($s, ":d1", $d1); +oci_bind_by_name($s, ":n5", $n5); +oci_bind_by_name($s, ":c4", $c4); +oci_bind_by_name($s, ":c5", $c5); +oci_bind_by_name($s, ":c6", $c6); + +oci_execute($s); + +echo "\nTEST87 SELECT all values using DEFINEs\n"; +$select_sql = "select " + . "id, " + . "char_t, " + . "char_t10, " + . "varchar2_t10, " + . "number_t, " + . "number_t92, " + . "number_t6, " + . "date_t, " + . "timestamp_t, " + . "float_t, " + . "binary_float_t, " + . "binary_double_t, " + . "decimal_t, " + . "integer_t, " + . "nchar_t, " + . "nvarchar2_t10, " + . "varchar_t10 " + . "from bind_misccoltypes_tab where id = 86"; + +$s = oci_parse($c, $select_sql); + +oci_define_by_name($s, "ID", $ID); +oci_define_by_name($s, "CHAR_T", $CHAR_T); +oci_define_by_name($s, "CHAR_T10", $CHAR_T10); +oci_define_by_name($s, "VARCHAR2_T10", $VARCHAR2_T10); +oci_define_by_name($s, "NUMBER_T", $NUMBER_T); +oci_define_by_name($s, "NUMBER_T92", $NUMBER_T92); +oci_define_by_name($s, "NUMBER_T6", $NUMBER_T6); +oci_define_by_name($s, "DATE_T", $DATE_T); +oci_define_by_name($s, "TIMESTAMP_T", $TIMESTAMP_T); +oci_define_by_name($s, "FLOAT_T", $FLOAT_T); +oci_define_by_name($s, "BINARY_FLOAT_T", $BINARY_FLOAT_T); +oci_define_by_name($s, "BINARY_DOUBLE_T", $BINARY_DOUBLE_T); +oci_define_by_name($s, "DECIMAL_T", $DECIMAL_T); +oci_define_by_name($s, "INTEGER_T", $INTEGER_T); +oci_define_by_name($s, "NCHAR_T", $NCHAR_T); +oci_define_by_name($s, "NVARCHAR2_T10", $NVARCHAR2_T10); +oci_define_by_name($s, "VARCHAR_T10", $VARCHAR_T10); + +oci_execute($s); + +while (oci_fetch($s)) { + echo "ID is " . "$ID\n"; + echo "CHAR_T is " . "$CHAR_T\n"; + echo "CHAR_T10 is " . "$CHAR_T10\n"; + echo "VARCHAR2_T10 is " . "$VARCHAR2_T10\n"; + echo "NUMBER_T is " . "$NUMBER_T\n"; + echo "NUMBER_T92 is " . "$NUMBER_T92\n"; + echo "NUMBER_T6 is " . "$NUMBER_T6\n"; + echo "DATE_T is " . "$DATE_T\n"; + echo "TIMESTAMP_T is " . "$TIMESTAMP_T\n"; + echo "FLOAT_T is " . "$FLOAT_T\n"; + echo "BINARY_FLOAT_T is " . "$BINARY_FLOAT_T\n"; + echo "BINARY_DOUBLE_T is " . "$BINARY_DOUBLE_T\n"; + echo "DECIMAL_T is " . "$DECIMAL_T\n"; + echo "INTEGER_T is " . "$INTEGER_T\n"; + echo "NCHAR_T is " . "$NCHAR_T\n"; + echo "NVARCHAR2_T10 is " . "$NVARCHAR2_T10\n"; + echo "VARCHAR_T10 is " . "$VARCHAR_T10\n"; +} + +echo "\nTEST52 insert numbers\n"; + +$s = oci_parse($c, "INSERT INTO bind_misccoltypes_tab (id, number_t92) VALUES (52, :n1)"); +$n1 = 3; +oci_bind_by_name($s, ":n1", $n1); +oci_execute($s); + +check_col($c, 'number_t92', 52); + + +echo "\nTEST53 insert numbers \n"; + +$s = oci_parse($c, "INSERT INTO bind_misccoltypes_tab (id, number_t92) VALUES (53, :n1)"); +$n1 = 8.67; +oci_bind_by_name($s, ":n1", $n1); +oci_execute($s); + +check_col($c, 'number_t92', 53); + + +echo "\nTEST54 insert numbers \n"; + +$s = oci_parse($c, "INSERT INTO bind_misccoltypes_tab (id, number_t) VALUES (54, :n1)"); +$n1 = 4.67; +oci_bind_by_name($s, ":n1", $n1); +oci_execute($s); + +check_col($c, 'number_t', 54); + +echo "\nTEST55 insert numbers \n"; + +$s = oci_parse($c, "INSERT INTO bind_misccoltypes_tab (id, number_t) VALUES (55, :n1)"); +$n1 = "7.67"; +oci_bind_by_name($s, ":n1", $n1); +oci_execute($s); + +check_col($c, 'number_t', 55); + +echo "\nTEST56 insert numbers \n"; + +$n1 = -5.67; + +$s = oci_parse($c, "INSERT INTO bind_misccoltypes_tab (id, number_t) VALUES (56, :n1)"); +oci_bind_by_name($s, ":n1", $n1); +oci_execute($s); + +check_col($c, 'number_t', 56); + +echo "\nTEST58 insert a VARCHAR2\n"; + +$s = oci_parse($c, "INSERT INTO bind_misccoltypes_tab (id, varchar2_t10) VALUES (58, :c2)"); +$c2 = "Hood"; +oci_bind_by_name($s, ":c2", $c2); +oci_execute($s); + +check_col($c, 'varchar2_t10', 58); + +echo "\nTEST59 insert a VARCHAR2\n"; + +$s = oci_parse($c, "INSERT INTO bind_misccoltypes_tab (id, char_t10) VALUES (59, :c2)"); +$c2 = "Hood"; +oci_bind_by_name($s, ":c2", $c2); +oci_execute($s); + +check_col($c, 'char_t10', 59); + +echo "\nTEST60 insert a date\n"; + +$s = oci_parse($c, "INSERT INTO bind_misccoltypes_tab (id, date_t) VALUES (60, to_date(:c2, 'YYYY-MM-DD'))"); +$c2 = '2010-04-09'; +oci_bind_by_name($s, ":c2", $c2); +oci_execute($s); + +check_col($c, 'date_t', 60); + + +// Clean up + +$stmtarray = array( + "drop table bind_misccoltypes_tab" +); + +oci8_test_sql_execute($c, $stmtarray); + +oci_close($c); + +?> +===DONE=== +<?php exit(0); ?> +--EXPECTF-- +TEST86 insert all ORATYPE values + +TEST87 SELECT all values using DEFINEs +ID is 86 +CHAR_T is C +CHAR_T10 is char10 +VARCHAR2_T10 is varchar210 +NUMBER_T is -123.456 +NUMBER_T92 is 789.35 +NUMBER_T6 is 123456 +DATE_T is 29-MAR-10 +TIMESTAMP_T is 29-MAR-10 01.09.15.000000 PM +FLOAT_T is 12345678901234567890123456789012345679 +BINARY_FLOAT_T is 1.23456784E+037 +BINARY_DOUBLE_T is 1.2345678901234568E+037 +DECIMAL_T is 86 +INTEGER_T is 86 +NCHAR_T is nchar10 +NVARCHAR2_T10 is nvarchar2x +VARCHAR_T10 is varchar + +TEST52 insert numbers +array(1) { + ["NUMBER_T92"]=> + array(1) { + [0]=> + string(1) "3" + } +} + +TEST53 insert numbers +array(1) { + ["NUMBER_T92"]=> + array(1) { + [0]=> + string(4) "8.67" + } +} + +TEST54 insert numbers +array(1) { + ["NUMBER_T"]=> + array(1) { + [0]=> + string(4) "4.67" + } +} + +TEST55 insert numbers +array(1) { + ["NUMBER_T"]=> + array(1) { + [0]=> + string(4) "7.67" + } +} + +TEST56 insert numbers +array(1) { + ["NUMBER_T"]=> + array(1) { + [0]=> + string(5) "-5.67" + } +} + +TEST58 insert a VARCHAR2 +array(1) { + ["VARCHAR2_T10"]=> + array(1) { + [0]=> + string(4) "Hood" + } +} + +TEST59 insert a VARCHAR2 +array(1) { + ["CHAR_T10"]=> + array(1) { + [0]=> + string(10) "Hood " + } +} + +TEST60 insert a date +array(1) { + ["DATE_T"]=> + array(1) { + [0]=> + string(9) "09-APR-10" + } +} +===DONE=== diff --git a/ext/oci8/tests/bind_misccoltypes_errs.phpt b/ext/oci8/tests/bind_misccoltypes_errs.phpt new file mode 100644 index 000000000..20401fb8e --- /dev/null +++ b/ext/oci8/tests/bind_misccoltypes_errs.phpt @@ -0,0 +1,169 @@ +--TEST-- +Bind miscellaneous column types and generating errors +--SKIPIF-- +<?php if (!extension_loaded('oci8')) die ("skip no oci8 extension"); ?> +--FILE-- +<?php + +require(dirname(__FILE__).'/connect.inc'); + +// Initialization + +$stmtarray = array( + "drop table bind_misccoltypes_errs_tab", + + "create table bind_misccoltypes_errs_tab ( + id number, + char_t char(1), + char_t10 char(10), + varchar2_t10 varchar2(10), + number_t number, + number_t92 number(9,2), + number_t6 number(6), + date_t date, + timestamp_t timestamp, + float_t float, + binary_float_t binary_float, + binary_double_t binary_double, + decimal_t decimal, + integer_t integer, + nchar_t nchar(10), + nvarchar2_t10 nvarchar2(10), + varchar_t10 varchar(10) )", +); + +oci8_test_sql_execute($c, $stmtarray); + +function check_col($c, $colname, $id) +{ + $s = oci_parse($c, "select $colname from bind_misccoltypes_errs_tab where id = :id"); + oci_bind_by_name($s, ":id", $id); + oci_execute($s); + oci_fetch_all($s, $r); + var_dump($r); +} + +// Tests + +echo "\nTest 1 insert numbers \n"; + +$n1 = -23253245.3432467; + +$s = oci_parse($c, "INSERT INTO bind_misccoltypes_errs_tab (id, number_t6) VALUES (1, :n1)"); +oci_bind_by_name($s, ":n1", $n1); +oci_execute($s); + +check_col($c, 'number_t6', 57); + +echo "\nTest 2 insert numbers \n"; + +$n1 = "Hello"; + +$s = oci_parse($c, "INSERT INTO bind_misccoltypes_errs_tab (id, number_t6) VALUES (2, :n1)"); +oci_bind_by_name($s, ":n1", $n1); +oci_execute($s); + +check_col($c, 'number_t6', 57); + +echo "\nTest 3 - too long CHAR\n"; + +$s = oci_parse($c, "INSERT INTO bind_misccoltypes_errs_tab (id, char_t) VALUES (3, :c2)"); +$c2 = "AB"; +oci_bind_by_name($s, ":c2", $c2, -1, SQLT_AFC); +oci_execute($s); + +echo "\nTest 4 - too long VARCHAR2\n"; + +$s = oci_parse($c, "INSERT INTO bind_misccoltypes_errs_tab (id, varchar2_t10) VALUES (4, :c2)"); +$c2 = "AAAAAAAAAAB"; +oci_bind_by_name($s, ":c2", $c2, -1, SQLT_AFC); +oci_execute($s); + +echo "\nTest 5 - invalid number\n"; + +$s = oci_parse($c, "INSERT INTO bind_misccoltypes_errs_tab (id, number_t) VALUES (5, :c2)"); +$c2 = "ABC"; +oci_bind_by_name($s, ":c2", $c2, -1, SQLT_AFC); +oci_execute($s); + +echo "\nTest 6 - insert a VARCHAR2 with SQLT_BIN\n"; + +$s = oci_parse($c, "INSERT INTO bind_misccoltypes_errs_tab (id, varchar2_t10) VALUES (6, :c2)"); +$c2 = "Hood 6"; +oci_bind_by_name($s, ":c2", $c2, -1, SQLT_BIN); +oci_execute($s); + +check_col($c, 'varchar2_t10', 6); + +echo "\nTest 7 - insert a VARCHAR2 with SQLT_LBI\n"; + +$s = oci_parse($c, "INSERT INTO bind_misccoltypes_errs_tab (id, varchar2_t10) VALUES (7, :c2)"); +$c2 = "Hood 7"; +oci_bind_by_name($s, ":c2", $c2, -1, SQLT_LBI); +oci_execute($s); + +check_col($c, 'varchar2_t10', 7); + + +// Clean up + +$stmtarray = array( + "drop table bind_misccoltypes_errs_tab" +); + +oci8_test_sql_execute($c, $stmtarray); + +oci_close($c); + +?> +===DONE=== +<?php exit(0); ?> +--EXPECTF-- +Test 1 insert numbers + +Warning: oci_execute(): ORA-01438: %s in %sbind_misccoltypes_errs.php on line %d +array(1) { + ["NUMBER_T6"]=> + array(0) { + } +} + +Test 2 insert numbers + +Warning: oci_execute(): ORA-01722: %s in %sbind_misccoltypes_errs.php on line %d +array(1) { + ["NUMBER_T6"]=> + array(0) { + } +} + +Test 3 - too long CHAR + +Warning: oci_execute(): ORA-12899: %r(%s "%s"."BIND_MISCCOLTYPES_ERRS_TAB"."CHAR_T" \(%s: 2, %s: 1\)|String data right truncation)%r in %sbind_misccoltypes_errs.php on line %d + +Test 4 - too long VARCHAR2 + +Warning: oci_execute(): ORA-12899: %r(%s "%s"."BIND_MISCCOLTYPES_ERRS_TAB"."VARCHAR2_T10" \(%s: 11, %s: 10\)|%s data right truncation)%r in %sbind_misccoltypes_errs.php on line %d + +Test 5 - invalid number + +Warning: oci_execute(): ORA-01722: %s in %sbind_misccoltypes_errs.php on line %d + +Test 6 - insert a VARCHAR2 with SQLT_BIN + +Warning: oci_execute(): ORA-12899: %r(%s "%s"."BIND_MISCCOLTYPES_ERRS_TAB"."VARCHAR2_T10" \(%s: 12, %s: 10\)|String data right truncation)%r in %sbind_misccoltypes_errs.php on line %d +array(1) { + ["VARCHAR2_T10"]=> + array(0) { + } +} + +Test 7 - insert a VARCHAR2 with SQLT_LBI + +Warning: oci_execute(): ORA-12899: %r(%s "%s"."BIND_MISCCOLTYPES_ERRS_TAB"."VARCHAR2_T10" \(%s: 12, %s: 10\)|String data right truncation)%r in %sbind_misccoltypes_errs.php on line %d +array(1) { + ["VARCHAR2_T10"]=> + array(0) { + } +} +===DONE=== diff --git a/ext/oci8/tests/bind_number.phpt b/ext/oci8/tests/bind_number.phpt new file mode 100644 index 000000000..6412b5f82 --- /dev/null +++ b/ext/oci8/tests/bind_number.phpt @@ -0,0 +1,220 @@ +--TEST-- +Bind with NUMBER column variants +--SKIPIF-- +<?php +if (!extension_loaded('oci8')) die("skip no oci8 extension"); +if (preg_match('/^1[012]\./', oci_client_version()) != 1) { + die("skip test expected to work only with Oracle 10g or greater version of client"); +} +?> +--INI-- +precision = 14 +--FILE-- +<?php + +require(dirname(__FILE__).'/connect.inc'); + +// Initialization + +$stmtarray = array( + "drop table bind_number_tab", + "create table bind_number_tab ( + id number, + number_t6 number(6), + float_t float, + binary_float_t binary_float, + binary_double_t binary_double, + decimal_t decimal, + integer_t integer)" +); + +oci8_test_sql_execute($c, $stmtarray); + +function check_col($c, $colname, $id) +{ + $s = oci_parse($c, "select $colname from bind_number_tab where id = :id"); + oci_bind_by_name($s, ":id", $id); + oci_execute($s); + oci_fetch_all($s, $r); + var_dump($r); +} + +// Run Test + +echo "Test 1 - invalid number\n"; + +$s = oci_parse($c, "INSERT INTO bind_number_tab (id, number_t6) VALUES (1, :n1)"); +$n1 = "Hello"; +oci_bind_by_name($s, ":n1", $n1); +oci_execute($s); + +check_col($c, "number_t6", 1); + +echo "\nTEST66 insert a float\n"; + +$s = oci_parse($c, "INSERT INTO bind_number_tab (id, float_t) VALUES (66, :f1)"); +$f1 = 123.456; +oci_bind_by_name($s, ":f1", $f1); +oci_execute($s); + +check_col($c, 'float_t', 66); + +echo "\nTEST67 insert a binary float\n"; + +$s = oci_parse($c, "INSERT INTO bind_number_tab (id, binary_float_t) VALUES (67, :f1)"); +$f1 = 567.456; +oci_bind_by_name($s, ":f1", $f1); +oci_execute($s); + +check_col($c, 'binary_float_t', 67); + +echo "\nTEST69 insert a binary double\n"; + +$s = oci_parse($c, "INSERT INTO bind_number_tab (id, binary_double_t) VALUES (69, :f1)"); +$f1 = 567.456; +oci_bind_by_name($s, ":f1", $f1); +oci_execute($s); + +check_col($c, 'binary_double_t', 69); + +echo "\nTEST71 insert a decimal\n"; + +$s = oci_parse($c, "INSERT INTO bind_number_tab (id, decimal_t) VALUES (71, :f1)"); +$f1 = 123.789; +oci_bind_by_name($s, ":f1", $f1); +oci_execute($s); + +check_col($c, 'decimal_t', 71); + +echo "\nTEST72 insert a decimal\n"; + +$s = oci_parse($c, "INSERT INTO bind_number_tab (id, decimal_t) VALUES (72, :f1)"); +$f1 = 123.789; +oci_bind_by_name($s, ":f1", $f1, -1, SQLT_NUM); +oci_execute($s); + +check_col($c, 'decimal_t', 72); + +echo "\nTEST73 insert a double\n"; + +$s = oci_parse($c, "INSERT INTO bind_number_tab (id, binary_double_t) VALUES (73, :f1)"); +$f1 = 483.589; +oci_bind_by_name($s, ":f1", $f1); +oci_execute($s); + +check_col($c, 'binary_double_t', 73); + +echo "\nTEST75 insert a INTEGER\n"; + +$s = oci_parse($c, "INSERT INTO bind_number_tab (id, integer_t) VALUES (75, :f1)"); +$f1 = 589; +oci_bind_by_name($s, ":f1", $f1); +oci_execute($s); + +check_col($c, 'integer_t', 75); + +echo "\nTEST76 insert a INTEGER\n"; + +$s = oci_parse($c, "INSERT INTO bind_number_tab (id, integer_t) VALUES (76, :f1)"); +$f1 = 42; +oci_bind_by_name($s, ":f1", $f1, -1, SQLT_INT); +oci_execute($s); + +check_col($c, 'integer_t', 76); + + +// Clean up + +$stmtarray = array( + "drop table bind_number_tab" +); + +oci8_test_sql_execute($c, $stmtarray); + +?> +===DONE=== +<?php exit(0); ?> +--EXPECTF-- +Test 1 - invalid number + +Warning: oci_execute(): ORA-01722: %s in %sbind_number.php on line %d +array(1) { + ["NUMBER_T6"]=> + array(0) { + } +} + +TEST66 insert a float +array(1) { + ["FLOAT_T"]=> + array(1) { + [0]=> + string(7) "123.456" + } +} + +TEST67 insert a binary float +array(1) { + ["BINARY_FLOAT_T"]=> + array(1) { + [0]=> + string(%r15|8%r) "%r(5.67455994E\+002|567.4560)%r" + } +} + +TEST69 insert a binary double +array(1) { + ["BINARY_DOUBLE_T"]=> + array(1) { + [0]=> + string(%r23|16%r) "%r(5.6745600000000002E\+002|567.456000000000)%r" + } +} + +TEST71 insert a decimal +array(1) { + ["DECIMAL_T"]=> + array(1) { + [0]=> + string(3) "124" + } +} + +TEST72 insert a decimal +array(1) { + ["DECIMAL_T"]=> + array(1) { + [0]=> + string(1) "0" + } +} + +TEST73 insert a double +array(1) { + ["BINARY_DOUBLE_T"]=> + array(1) { + [0]=> + string(%r12|16%r) "%r(4.83589E\+002|483.589000000000)%r" + } +} + +TEST75 insert a INTEGER +array(1) { + ["INTEGER_T"]=> + array(1) { + [0]=> + string(3) "589" + } +} + +TEST76 insert a INTEGER +array(1) { + ["INTEGER_T"]=> + array(1) { + [0]=> + string(2) "42" + } +} +===DONE=== + + diff --git a/ext/oci8/tests/bind_query.phpt b/ext/oci8/tests/bind_query.phpt new file mode 100644 index 000000000..e4edc9329 --- /dev/null +++ b/ext/oci8/tests/bind_query.phpt @@ -0,0 +1,78 @@ +--TEST-- +Bind with various WHERE conditions +--SKIPIF-- +<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +--FILE-- +<?php + +require(dirname(__FILE__).'/connect.inc'); + +// Initialization + +$stmtarray = array( + "drop table bind_query_tab", + "create table bind_query_tab (empno number(4), ename varchar2(10), sal number(7,2))", + "insert into bind_query_tab values (7934, 'MILLER', 1300)", + "insert into bind_query_tab values (7902, 'FORD', 3000)" +); + +oci8_test_sql_execute($c, $stmtarray); + +// Run Test + +echo "Test 1\n"; + +$e = 7934; + +$s = oci_parse($c, "select ename from bind_query_tab where empno = :eno"); +oci_bind_by_name( $s, ":eno", $e, -1, SQLT_INT); +oci_execute($s); +var_dump(oci_fetch_row($s)); + +echo "Test 2\n"; + +$v = 1000; +$s = oci_parse($c, 'select ename from bind_query_tab where sal > :v order by ename'); +oci_bind_by_name( $s, ":v", $v); +oci_define_by_name($s, "ENAME", $ename, 20); +oci_execute($s); +while (oci_fetch($s)) { + var_dump($ename); +} + + +echo "Test 3\n"; + +$s = oci_parse($c, 'select ename from bind_query_tab where sal > :v order by ename'); +oci_bind_by_name( $s, ":v", $v); +$v = 2000; +oci_define_by_name($s, "ENAME", $ename, 20); +oci_execute($s); +while (oci_fetch($s)) { + var_dump($ename); +} + + +// Clean up + +$stmtarray = array( + "drop table bind_query_tab" +); + +oci8_test_sql_execute($c, $stmtarray); + +?> +===DONE=== +<?php exit(0); ?> +--EXPECTF-- +Test 1 +array(1) { + [0]=> + string(6) "MILLER" +} +Test 2 +string(4) "FORD" +string(6) "MILLER" +Test 3 +string(4) "FORD" +===DONE=== diff --git a/ext/oci8/tests/bind_raw.phpt b/ext/oci8/tests/bind_raw.phpt index c9087e552..c2e8e0876 100644 --- a/ext/oci8/tests/bind_raw.phpt +++ b/ext/oci8/tests/bind_raw.phpt @@ -1,7 +1,10 @@ --TEST-- bind RAW field --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/bind_rowid.phpt b/ext/oci8/tests/bind_rowid.phpt index f15d8f8bb..122ad5e18 100644 --- a/ext/oci8/tests/bind_rowid.phpt +++ b/ext/oci8/tests/bind_rowid.phpt @@ -17,17 +17,14 @@ function do_query($c) } } -$stmts = array( +$stmtarray = array( "drop table rid_tab", "create table rid_tab (id number, address varchar2(40))", "insert into rid_tab (id, address) values (1, 'original text #1')", "insert into rid_tab (id, address) values (2, 'original text #2')" ); -foreach ($stmts as $q) { - $s = oci_parse($c, $q); - @oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); echo "Initial Data\n"; do_query($c); @@ -54,12 +51,11 @@ do_query($c); // Cleanup -$stmts = array("drop table rid_tab"); +$stmtarray = array( + "drop table rid_tab" +); -foreach ($stmts as $q) { - $s = oci_parse($c, $q); - @oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); echo "Done\n"; diff --git a/ext/oci8/tests/bind_sqltafc.phpt b/ext/oci8/tests/bind_sqltafc.phpt new file mode 100644 index 000000000..8d2ce2ae6 --- /dev/null +++ b/ext/oci8/tests/bind_sqltafc.phpt @@ -0,0 +1,208 @@ +--TEST-- +Bind tests with SQLT_AFC +--SKIPIF-- +<?php if (!extension_loaded('oci8')) die ("skip no oci8 extension"); ?> +--FILE-- +<?php + +require(dirname(__FILE__).'/connect.inc'); + +// Initialization + +$stmtarray = array( + "drop table bind_sqltafc_tab", + "create table bind_sqltafc_tab (id number, char_t char(1), char_t10 char(10), varchar2_t10 varchar2(10), number_t number)", + "insert into bind_sqltafc_tab values (0, 'a', 'abcd', 'efghij', 1.1)" +); + +oci8_test_sql_execute($c, $stmtarray); + +// Run Test + +function q($c, $id) +{ + $s = oci_parse($c, "select * from bind_sqltafc_tab where id = $id"); + oci_execute($s); + oci_fetch_all($s, $r); + var_dump($r); +} + +echo "Test 0 - base table creation without binds\n"; + +q($c, 0); + +echo "\nTest 1 - successful insert\n"; + +$s = oci_parse($c, "INSERT INTO bind_sqltafc_tab (id, char_t, char_t10, varchar2_t10, number_t) VALUES (1, :c2, :c3, :c4, :c5)"); +$c2 = "H"; +$c3 = "AAAAAAAAAA"; // max length allowed in column +$c4 = "BBBBBBBBBB"; // max length allowed in column +$c5 = "123.45"; +oci_bind_by_name($s, ":c2", $c2, -1, SQLT_AFC); +oci_bind_by_name($s, ":c3", $c3, -1, SQLT_AFC); +oci_bind_by_name($s, ":c4", $c4, -1, SQLT_AFC); +oci_bind_by_name($s, ":c5", $c5, -1, SQLT_AFC); +oci_execute($s); + +q($c, 1); + +echo "\nTest 2 - Empty Strings\n"; + +$s = oci_parse($c, "INSERT INTO bind_sqltafc_tab (id, char_t, char_t10, varchar2_t10, number_t) VALUES (5, :c2, :c3, :c4, :c5)"); +$c2 = ""; +$c3 = ""; +$c4 = ""; +$c5 = ""; +oci_bind_by_name($s, ":c2", $c2, -1, SQLT_AFC); +oci_bind_by_name($s, ":c3", $c3, -1, SQLT_AFC); +oci_bind_by_name($s, ":c4", $c4, -1, SQLT_AFC); +oci_bind_by_name($s, ":c5", $c5, -1, SQLT_AFC); +oci_execute($s); + +q($c, 5); + +echo "\nTest 3 - NULLs\n"; + +$s = oci_parse($c, "INSERT INTO bind_sqltafc_tab (id, char_t, char_t10, varchar2_t10, number_t) VALUES (6, :c2, :c3, :c4, :c5)"); +$c2 = null; +$c3 = null; +$c4 = null; +$c5 = null; +oci_bind_by_name($s, ":c2", $c2, -1, SQLT_AFC); +oci_bind_by_name($s, ":c3", $c3, -1, SQLT_AFC); +oci_bind_by_name($s, ":c4", $c4, -1, SQLT_AFC); +oci_bind_by_name($s, ":c5", $c5, -1, SQLT_AFC); +oci_execute($s); + +q($c, 6); + +// Clean up + +$stmtarray = array( + "drop table bind_sqltafc_tab" +); + +oci8_test_sql_execute($c, $stmtarray); + +oci_close($c); + +?> +===DONE=== +<?php exit(0); ?> +--EXPECTF-- +Test 0 - base table creation without binds +array(5) { + ["ID"]=> + array(1) { + [0]=> + string(1) "0" + } + ["CHAR_T"]=> + array(1) { + [0]=> + string(1) "a" + } + ["CHAR_T10"]=> + array(1) { + [0]=> + string(10) "abcd " + } + ["VARCHAR2_T10"]=> + array(1) { + [0]=> + string(6) "efghij" + } + ["NUMBER_T"]=> + array(1) { + [0]=> + string(3) "1.1" + } +} + +Test 1 - successful insert +array(5) { + ["ID"]=> + array(1) { + [0]=> + string(1) "1" + } + ["CHAR_T"]=> + array(1) { + [0]=> + string(1) "H" + } + ["CHAR_T10"]=> + array(1) { + [0]=> + string(10) "AAAAAAAAAA" + } + ["VARCHAR2_T10"]=> + array(1) { + [0]=> + string(10) "BBBBBBBBBB" + } + ["NUMBER_T"]=> + array(1) { + [0]=> + string(6) "123.45" + } +} + +Test 2 - Empty Strings +array(5) { + ["ID"]=> + array(1) { + [0]=> + string(1) "5" + } + ["CHAR_T"]=> + array(1) { + [0]=> + NULL + } + ["CHAR_T10"]=> + array(1) { + [0]=> + NULL + } + ["VARCHAR2_T10"]=> + array(1) { + [0]=> + NULL + } + ["NUMBER_T"]=> + array(1) { + [0]=> + NULL + } +} + +Test 3 - NULLs +array(5) { + ["ID"]=> + array(1) { + [0]=> + string(1) "6" + } + ["CHAR_T"]=> + array(1) { + [0]=> + NULL + } + ["CHAR_T10"]=> + array(1) { + [0]=> + NULL + } + ["VARCHAR2_T10"]=> + array(1) { + [0]=> + NULL + } + ["NUMBER_T"]=> + array(1) { + [0]=> + NULL + } +} +===DONE=== diff --git a/ext/oci8/tests/bind_sqltchr_1.phpt b/ext/oci8/tests/bind_sqltchr_1.phpt new file mode 100644 index 000000000..aabf9bda5 --- /dev/null +++ b/ext/oci8/tests/bind_sqltchr_1.phpt @@ -0,0 +1,228 @@ +--TEST-- +Bind with SQLT_CHR +--SKIPIF-- +<?php if (!extension_loaded('oci8')) die ("skip no oci8 extension"); ?> +--FILE-- +<?php + +require(dirname(__FILE__).'/connect.inc'); + +// Initialization + +$stmtarray = array( + "drop table bind_sqltchr_tab", + + "create table bind_sqltchr_tab ( + id number, + varchar2_t10 varchar2(10), + number_t number, + number_t92 number(9,2))" + +); + +oci8_test_sql_execute($c, $stmtarray); + +function check_col($c, $colname, $id) +{ + $s = oci_parse($c, "select $colname from bind_sqltchr_tab where id = :id"); + oci_bind_by_name($s, ":id", $id); + oci_execute($s); + oci_fetch_all($s, $r); + var_dump($r); +} + +// Run Test + +echo "\nTEST241 bind SQLT_CHR\n"; + +$c2 = "Hood241"; +$s = oci_parse($c, "INSERT INTO bind_sqltchr_tab (id, varchar2_t10) VALUES (241, :c2)"); +oci_bind_by_name($s, ":c2", $c2, -1, SQLT_CHR); +oci_execute($s); + +check_col($c, 'varchar2_t10', 241); + + +echo "\nTEST242 insert numbers SQLT_CHR\n"; + +$s = oci_parse($c, "INSERT INTO bind_sqltchr_tab (id, number_t) VALUES (242, :n1)"); +$n1 = 42; +oci_bind_by_name($s, ":n1", $n1, -1, SQLT_CHR); +oci_execute($s); + +check_col($c, 'number_t', 242); + +echo "\nTEST243 insert numbers, SQLT_CHR\n"; + +$s = oci_parse($c, "INSERT INTO bind_sqltchr_tab (id, number_t) VALUES (243, :n1)"); +$n1 = 42.69; +oci_bind_by_name($s, ":n1", $n1, -1, SQLT_CHR); +oci_execute($s); + +check_col($c, 'number_t', 243); + +echo "\nTEST244 insert numbers with SQLT_CHR\n"; + +$s = oci_parse($c, "INSERT INTO bind_sqltchr_tab (id, number_t) VALUES (244, :n1)"); +$n1 = 0; +oci_bind_by_name($s, ":n1", $n1, -1, SQLT_CHR); +oci_execute($s); + +check_col($c, 'number_t', 244); + +echo "\nTEST245 insert numbers with SQLT_CHR\n"; + +$s = oci_parse($c, "INSERT INTO bind_sqltchr_tab (id, number_t) VALUES (245, :n1)"); +$n1 = -23; +oci_bind_by_name($s, ":n1", $n1, -1, SQLT_CHR); +oci_execute($s); + +check_col($c, 'number_t', 245); + +echo "\nTEST246 insert numbers\n"; + +$s = oci_parse($c, "INSERT INTO bind_sqltchr_tab (id, number_t) VALUES (246, :n1)"); +$n1 = "-23"; +oci_bind_by_name($s, ":n1", $n1, -1, SQLT_CHR); +oci_execute($s); + +check_col($c, 'number_t', 246); + +echo "\nTEST247 insert numbers with SQLT_CHR\n"; + +$s = oci_parse($c, "INSERT INTO bind_sqltchr_tab (id, number_t) VALUES (247, :n1)"); +$n1 = "23"; +oci_bind_by_name($s, ":n1", $n1, -1, SQLT_CHR); +oci_execute($s); + +check_col($c, 'number_t', 247); + +echo "\nTEST248 insert numbers with SQLT_CHR\n"; + +$s = oci_parse($c, "INSERT INTO bind_sqltchr_tab (id, number_t92) VALUES (248, :n1)"); +$n1 = 123.56; +oci_bind_by_name($s, ":n1", $n1, -1, SQLT_CHR); +oci_execute($s); + +check_col($c, 'number_t92', 248); + +echo "\nTEST249 insert numbers with SQLT_CHR\n"; + +$s = oci_parse($c, "INSERT INTO bind_sqltchr_tab (id, number_t92) VALUES (249, :n1)"); +$n1 = "123.56"; +oci_bind_by_name($s, ":n1", $n1, -1, SQLT_CHR); +oci_execute($s); + +check_col($c, 'number_t92', 249); + +echo "\nTEST250 insert numbers with SQLT_CHR\n"; + +$s = oci_parse($c, "INSERT INTO bind_sqltchr_tab (id, number_t92) VALUES (250, :n1)"); +$n1 = ""; +oci_bind_by_name($s, ":n1", $n1, -1, SQLT_CHR); +oci_execute($s); + +check_col($c, 'number_t92', 250); + +// Clean up + +$stmtarray = array( + "drop table bind_sqltchr_tab" +); + +oci8_test_sql_execute($c, $stmtarray); + +?> +===DONE=== +<?php exit(0); ?> +--EXPECTF-- +TEST241 bind SQLT_CHR +array(1) { + ["VARCHAR2_T10"]=> + array(1) { + [0]=> + string(7) "Hood241" + } +} + +TEST242 insert numbers SQLT_CHR +array(1) { + ["NUMBER_T"]=> + array(1) { + [0]=> + string(2) "42" + } +} + +TEST243 insert numbers, SQLT_CHR +array(1) { + ["NUMBER_T"]=> + array(1) { + [0]=> + string(5) "42.69" + } +} + +TEST244 insert numbers with SQLT_CHR +array(1) { + ["NUMBER_T"]=> + array(1) { + [0]=> + string(1) "0" + } +} + +TEST245 insert numbers with SQLT_CHR +array(1) { + ["NUMBER_T"]=> + array(1) { + [0]=> + string(3) "-23" + } +} + +TEST246 insert numbers +array(1) { + ["NUMBER_T"]=> + array(1) { + [0]=> + string(3) "-23" + } +} + +TEST247 insert numbers with SQLT_CHR +array(1) { + ["NUMBER_T"]=> + array(1) { + [0]=> + string(2) "23" + } +} + +TEST248 insert numbers with SQLT_CHR +array(1) { + ["NUMBER_T92"]=> + array(1) { + [0]=> + string(6) "123.56" + } +} + +TEST249 insert numbers with SQLT_CHR +array(1) { + ["NUMBER_T92"]=> + array(1) { + [0]=> + string(6) "123.56" + } +} + +TEST250 insert numbers with SQLT_CHR +array(1) { + ["NUMBER_T92"]=> + array(1) { + [0]=> + NULL + } +} +===DONE=== diff --git a/ext/oci8/tests/bind_sqltchr_2.phpt b/ext/oci8/tests/bind_sqltchr_2.phpt new file mode 100644 index 000000000..47d08d2bc --- /dev/null +++ b/ext/oci8/tests/bind_sqltchr_2.phpt @@ -0,0 +1,50 @@ +--TEST-- +PL/SQL bind with SQLT_CHR +--SKIPIF-- +<?php if (!extension_loaded('oci8')) die ("skip no oci8 extension"); ?> +--FILE-- +<?php + +require(dirname(__FILE__).'/connect.inc'); + +// Initialization + +$stmtarray = array( + "create or replace procedure bind_sqltchr_proc (msg_in in varchar2, msg_out out varchar2) + as + begin + msg_out := upper(msg_in); + end;" + +); + +oci8_test_sql_execute($c, $stmtarray); + +// Run Test + +echo "Test 1 - PL/SQL IN and OUT variables\n"; + +$stmt = oci_parse($c, "BEGIN bind_sqltchr_proc(:a, :b); END;"); +$msg_in = "Cat got your keyboard?"; +oci_bind_by_name($stmt, ":a", $msg_in, -1, SQLT_CHR); +oci_bind_by_name($stmt, ":b", $msg_out, 800, SQLT_CHR); +oci_execute($stmt); +var_dump($msg_in); +var_dump($msg_out); + +// Clean up + +$stmtarray = array( + "drop procedure bind_sqltchr_proc" +); + +oci8_test_sql_execute($c, $stmtarray); + +?> +===DONE=== +<?php exit(0); ?> +--EXPECTF-- +Test 1 - PL/SQL IN and OUT variables +string(22) "Cat got your keyboard?" +string(22) "CAT GOT YOUR KEYBOARD?" +===DONE=== diff --git a/ext/oci8/tests/bind_sqltint.phpt b/ext/oci8/tests/bind_sqltint.phpt new file mode 100644 index 000000000..f01791d3b --- /dev/null +++ b/ext/oci8/tests/bind_sqltint.phpt @@ -0,0 +1,227 @@ +--TEST-- +Bind with SQLT_INT +--SKIPIF-- +<?php if (!extension_loaded('oci8')) die ("skip no oci8 extension"); ?> +--FILE-- +<?php + +require(dirname(__FILE__).'/connect.inc'); + +// Initialization + +$stmtarray = array( + "drop table bind_sqltint_tab", + + "create table bind_sqltint_tab ( + id number, + varchar2_t10 varchar2(10), + number_t number, + number_t92 number(9,2))", + +); + +oci8_test_sql_execute($c, $stmtarray); + +function check_col($c, $colname, $id) +{ + $s = oci_parse($c, "select $colname from bind_sqltint_tab where id = :id"); + oci_bind_by_name($s, ":id", $id); + oci_execute($s); + oci_fetch_all($s, $r); + var_dump($r); +} + +// Run Test + +echo "\nTEST141 wrong bind type SQLT_INT\n"; + +$c2 = "Hood141"; +$s = oci_parse($c, "INSERT INTO bind_sqltint_tab (id, varchar2_t10) VALUES (141, :c2)"); +oci_bind_by_name($s, ":c2", $c2, -1, SQLT_INT); +oci_execute($s); + +check_col($c, 'varchar2_t10', 141); + +echo "\nTEST142 insert numbers SQLT_INT\n"; + +$s = oci_parse($c, "INSERT INTO bind_sqltint_tab (id, number_t) VALUES (142, :n1)"); +$n1 = 42; +oci_bind_by_name($s, ":n1", $n1, -1, SQLT_INT); +oci_execute($s); + +check_col($c, 'number_t', 142); + +echo "\nTEST143 insert numbers, SQLT_INT\n"; + +$s = oci_parse($c, "INSERT INTO bind_sqltint_tab (id, number_t) VALUES (143, :n1)"); +$n1 = 42.69; +oci_bind_by_name($s, ":n1", $n1, -1, SQLT_INT); +oci_execute($s); + +check_col($c, 'number_t', 143); + +echo "\nTEST144 insert numbers with SQLT_INT\n"; + +$s = oci_parse($c, "INSERT INTO bind_sqltint_tab (id, number_t) VALUES (144, :n1)"); +$n1 = 0; +oci_bind_by_name($s, ":n1", $n1, -1, SQLT_INT); +oci_execute($s); + +check_col($c, 'number_t', 144); + +echo "\nTEST145 insert numbers with SQLT_INT\n"; + +$s = oci_parse($c, "INSERT INTO bind_sqltint_tab (id, number_t) VALUES (145, :n1)"); +$n1 = -23; +oci_bind_by_name($s, ":n1", $n1, -1, SQLT_INT); +oci_execute($s); + +check_col($c, 'number_t', 145); + +echo "\nTEST146 insert numbers\n"; + +$s = oci_parse($c, "INSERT INTO bind_sqltint_tab (id, number_t) VALUES (146, :n1)"); +$n1 = "-23"; +oci_bind_by_name($s, ":n1", $n1, -1, SQLT_INT); +oci_execute($s); + +check_col($c, 'number_t', 146); + +echo "\nTEST147 insert numbers with SQLT_INT\n"; + +$s = oci_parse($c, "INSERT INTO bind_sqltint_tab (id, number_t) VALUES (147, :n1)"); +$n1 = "23"; +oci_bind_by_name($s, ":n1", $n1, -1, SQLT_INT); +oci_execute($s); + +check_col($c, 'number_t', 147); + +echo "\nTEST148 insert numbers with SQLT_INT\n"; + +$s = oci_parse($c, "INSERT INTO bind_sqltint_tab (id, number_t92) VALUES (148, :n1)"); +$n1 = 123.56; +oci_bind_by_name($s, ":n1", $n1, -1, SQLT_INT); +oci_execute($s); + +check_col($c, 'number_t92', 148); + +echo "\nTEST149 insert numbers with SQLT_INT\n"; + +$s = oci_parse($c, "INSERT INTO bind_sqltint_tab (id, number_t92) VALUES (149, :n1)"); +$n1 = "123.56"; +oci_bind_by_name($s, ":n1", $n1, -1, SQLT_INT); +oci_execute($s); + +check_col($c, 'number_t92', 149); + +echo "\nTEST150 insert numbers with SQLT_INT\n"; + +$s = oci_parse($c, "INSERT INTO bind_sqltint_tab (id, number_t92) VALUES (150, :n1)"); +$n1 = ""; +oci_bind_by_name($s, ":n1", $n1, -1, SQLT_INT); +oci_execute($s); + +check_col($c, 'number_t92', 150); + +// Clean up + +$stmtarray = array( + "drop table bind_sqltint_tab" +); + +oci8_test_sql_execute($c, $stmtarray); + +?> +===DONE=== +<?php exit(0); ?> +--EXPECTF-- +TEST141 wrong bind type SQLT_INT +array(1) { + ["VARCHAR2_T10"]=> + array(1) { + [0]=> + string(1) "0" + } +} + +TEST142 insert numbers SQLT_INT +array(1) { + ["NUMBER_T"]=> + array(1) { + [0]=> + string(2) "42" + } +} + +TEST143 insert numbers, SQLT_INT +array(1) { + ["NUMBER_T"]=> + array(1) { + [0]=> + string(2) "42" + } +} + +TEST144 insert numbers with SQLT_INT +array(1) { + ["NUMBER_T"]=> + array(1) { + [0]=> + string(1) "0" + } +} + +TEST145 insert numbers with SQLT_INT +array(1) { + ["NUMBER_T"]=> + array(1) { + [0]=> + string(3) "-23" + } +} + +TEST146 insert numbers +array(1) { + ["NUMBER_T"]=> + array(1) { + [0]=> + string(3) "-23" + } +} + +TEST147 insert numbers with SQLT_INT +array(1) { + ["NUMBER_T"]=> + array(1) { + [0]=> + string(2) "23" + } +} + +TEST148 insert numbers with SQLT_INT +array(1) { + ["NUMBER_T92"]=> + array(1) { + [0]=> + string(3) "123" + } +} + +TEST149 insert numbers with SQLT_INT +array(1) { + ["NUMBER_T92"]=> + array(1) { + [0]=> + string(3) "123" + } +} + +TEST150 insert numbers with SQLT_INT +array(1) { + ["NUMBER_T92"]=> + array(1) { + [0]=> + string(1) "0" + } +} +===DONE=== diff --git a/ext/oci8/tests/bind_sqltnum.phpt b/ext/oci8/tests/bind_sqltnum.phpt new file mode 100644 index 000000000..d3828b73e --- /dev/null +++ b/ext/oci8/tests/bind_sqltnum.phpt @@ -0,0 +1,278 @@ +--TEST-- +Bind with SQLT_NUM +--SKIPIF-- +<?php +if (!extension_loaded('oci8')) die("skip no oci8 extension"); +if (preg_match('/^1[012]\./', oci_client_version()) != 1) { + die("skip test expected to work only with Oracle 10g or greater version of client"); +} +?> +--FILE-- +<?php + +require(dirname(__FILE__).'/connect.inc'); + +// Initialization + +$stmtarray = array( + "drop table bind_sqltnum_tab", + + "create table bind_sqltnum_tab ( + id number, + varchar2_t10 varchar2(10), + number_t number, + number_t92 number(9,2))" +); + +oci8_test_sql_execute($c, $stmtarray); + +function check_col($c, $colname, $id) +{ + $s = oci_parse($c, "select $colname from bind_sqltnum_tab where id = :id"); + oci_bind_by_name($s, ":id", $id); + oci_execute($s); + oci_fetch_all($s, $r); + var_dump($r); +} + + +// Run Test + +echo "Test 1 - baseline test\n"; + +$s = oci_parse($c, "INSERT INTO bind_sqltnum_tab (id, varchar2_t10) VALUES (100, :c2)"); +$c2 = "Hood"; +$r = oci_bind_by_name($s, ":c2", $c2, -1); +if (!$r) { + $e = oci_error($s); + var_dump($e); +} +$r = oci_execute($s, OCI_DEFAULT); +if (!$r) { + $e = oci_error($s); + var_dump($e); +} + +$s = oci_parse($c, "select id, varchar2_t10 from bind_sqltnum_tab"); +oci_execute($s); +oci_fetch_all($s, $data); +var_dump($data); + +echo "Test 2 - SQLT_NUM to a VARCHAR2 column\n"; + +$s = oci_parse($c, "INSERT INTO bind_sqltnum_tab (id, varchar2_t10) VALUES (100, :c2)"); +$c2 = "Hood"; +$r = oci_bind_by_name($s, ":c2", $c2, -1, SQLT_NUM); +if (!$r) { + $e = oci_error($s); + var_dump($e['message']); +} +$r = oci_execute($s, OCI_DEFAULT); +if (!$r) { + $e = oci_error($s); + var_dump($e['message']); +} + +echo "\nTEST41 wrong bind type SQLT_NUM\n"; + +$c2 = "Hood41"; +$s = oci_parse($c, "INSERT INTO bind_sqltnum_tab (id, varchar2_t10) VALUES (41, :c2)"); +oci_bind_by_name($s, ":c2", $c2, -1, SQLT_NUM); +oci_execute($s); + +echo "\nTEST42 insert numbers SQLT_NUM\n"; + +$s = oci_parse($c, "INSERT INTO bind_sqltnum_tab (id, number_t) VALUES (42, :n1)"); +$n1 = 42; +oci_bind_by_name($s, ":n1", $n1, -1, SQLT_NUM); +oci_execute($s); + +check_col($c, 'number_t', 42); + +echo "\nTEST43 insert numbers SQLT_NUM\n"; + +$s = oci_parse($c, "INSERT INTO bind_sqltnum_tab (id, number_t) VALUES (43, :n1)"); +$n1 = 42.69; +oci_bind_by_name($s, ":n1", $n1, -1, SQLT_NUM); +oci_execute($s); + +check_col($c, 'number_t', 43); + +echo "\nTEST44\n"; + +$s = oci_parse($c, "INSERT INTO bind_sqltnum_tab (id, number_t) VALUES (44, :n1)"); +$n1 = 0; +oci_bind_by_name($s, ":n1", $n1, -1, SQLT_NUM); +oci_execute($s); + +check_col($c, 'number_t', 44); + +echo "\nTEST45\n"; + +$s = oci_parse($c, "INSERT INTO bind_sqltnum_tab (id, number_t) VALUES (45, :n1)"); +$n1 = -23; +oci_bind_by_name($s, ":n1", $n1, -1, SQLT_NUM); +oci_execute($s); + +check_col($c, 'number_t', 45); + +echo "\nTEST46 insert numbers\n"; + +$s = oci_parse($c, "INSERT INTO bind_sqltnum_tab (id, number_t) VALUES (46, :n1)"); +$n1 = "-23"; +oci_bind_by_name($s, ":n1", $n1, -1, SQLT_NUM); +oci_execute($s); + +check_col($c, 'number_t', 46); + +echo "\nTEST47\n"; + +$s = oci_parse($c, "INSERT INTO bind_sqltnum_tab (id, number_t) VALUES (47, :n1)"); +$n1 = "23"; +oci_bind_by_name($s, ":n1", $n1, -1, SQLT_NUM); +oci_execute($s); + +check_col($c, 'number_t', 47); + +echo "\nTEST48\n"; + +$s = oci_parse($c, "INSERT INTO bind_sqltnum_tab (id, number_t92) VALUES (48, :n1)"); +$n1 = 123.56; +oci_bind_by_name($s, ":n1", $n1, -1, SQLT_NUM); +oci_execute($s); + +check_col($c, 'number_t92', 48); + +echo "\nTEST49\n"; + +$s = oci_parse($c, "INSERT INTO bind_sqltnum_tab (id, number_t92) VALUES (49, :n1)"); +$n1 = "123.56"; +oci_bind_by_name($s, ":n1", $n1, -1, SQLT_NUM); +oci_execute($s); + +check_col($c, 'number_t92', 49); + +echo "\nTEST50\n"; + +$s = oci_parse($c, "INSERT INTO bind_sqltnum_tab (id, number_t92) VALUES (50, :n1)"); +$n1 = ""; +oci_bind_by_name($s, ":n1", $n1, -1, SQLT_NUM); +oci_execute($s); + +check_col($c, 'number_t92', 50); + +// Clean up + +$stmtarray = array( + "drop table bind_sqltnum_tab" +); + +oci8_test_sql_execute($c, $stmtarray); + +?> +===DONE=== +<?php exit(0); ?> +--EXPECTF-- +Test 1 - baseline test +array(2) { + ["ID"]=> + array(1) { + [0]=> + string(3) "100" + } + ["VARCHAR2_T10"]=> + array(1) { + [0]=> + string(4) "Hood" + } +} +Test 2 - SQLT_NUM to a VARCHAR2 column + +Warning: oci_execute(): ORA-12899: %s (%s: 40, %s: 10) in %sbind_sqltnum.php on line %d +string(%d) "ORA-12899: %s" + +TEST41 wrong bind type SQLT_NUM + +Warning: oci_execute(): ORA-12899: %s "%s"."BIND_SQLTNUM_TAB"."VARCHAR2_T10" (%s: 40, %s: 10) in %sbind_sqltnum.php on line %d + +TEST42 insert numbers SQLT_NUM +array(1) { + ["NUMBER_T"]=> + array(1) { + [0]=> + NULL + } +} + +TEST43 insert numbers SQLT_NUM +array(1) { + ["NUMBER_T"]=> + array(1) { + [0]=> + NULL + } +} + +TEST44 +array(1) { + ["NUMBER_T"]=> + array(1) { + [0]=> + string(127) "-000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + } +} + +TEST45 +array(1) { + ["NUMBER_T"]=> + array(1) { + [0]=> + NULL + } +} + +TEST46 insert numbers +array(1) { + ["NUMBER_T"]=> + array(1) { + [0]=> + NULL + } +} + +TEST47 +array(1) { + ["NUMBER_T"]=> + array(1) { + [0]=> + NULL + } +} + +TEST48 +array(1) { + ["NUMBER_T92"]=> + array(1) { + [0]=> + string(1) "0" + } +} + +TEST49 +array(1) { + ["NUMBER_T92"]=> + array(1) { + [0]=> + string(1) "0" + } +} + +TEST50 + +Warning: oci_execute(): ORA-01438: %s in %sbind_sqltnum.php on line %d +array(1) { + ["NUMBER_T92"]=> + array(0) { + } +} +===DONE=== diff --git a/ext/oci8/tests/bind_unsupported_1.phpt b/ext/oci8/tests/bind_unsupported_1.phpt new file mode 100644 index 000000000..544b44678 --- /dev/null +++ b/ext/oci8/tests/bind_unsupported_1.phpt @@ -0,0 +1,58 @@ +--TEST-- +Bind with various unsupported bind types +--SKIPIF-- +<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +--FILE-- +<?php + +require(dirname(__FILE__).'/connect.inc'); + +// These types are defined in oci8.c + +$types = array( + "SQLT_AVC" => SQLT_AVC, + "SQLT_STR" => SQLT_STR, + "SQLT_VCS" => SQLT_VCS, + "SQLT_AVC" => SQLT_AVC, + "SQLT_STR" => SQLT_STR, + "SQLT_LVC" => SQLT_LVC, + "SQLT_FLT" => SQLT_FLT, + "SQLT_UIN" => SQLT_UIN, + "SQLT_ODT" => SQLT_ODT, +); + +foreach ($types as $t => $v) { + + echo "Test - $t\n"; + + $s = oci_parse($c, "select * from dual where dummy = :c1"); + $c1 = "Doug"; + oci_bind_by_name($s, ":c1", $c1, -1, $v); +} + +?> +===DONE=== +<?php exit(0); ?> +--EXPECTF-- +Test - SQLT_AVC + +Warning: oci_bind_by_name(): Unknown or unsupported datatype given: 97 in %sbind_unsupported_1.php on line %d +Test - SQLT_STR + +Warning: oci_bind_by_name(): Unknown or unsupported datatype given: 5 in %sbind_unsupported_1.php on line %d +Test - SQLT_VCS + +Warning: oci_bind_by_name(): Unknown or unsupported datatype given: 9 in %sbind_unsupported_1.php on line %d +Test - SQLT_LVC + +Warning: oci_bind_by_name(): Unknown or unsupported datatype given: 94 in %sbind_unsupported_1.php on line %d +Test - SQLT_FLT + +Warning: oci_bind_by_name(): Unknown or unsupported datatype given: 4 in %sbind_unsupported_1.php on line %d +Test - SQLT_UIN + +Warning: oci_bind_by_name(): Unknown or unsupported datatype given: 68 in %sbind_unsupported_1.php on line %d +Test - SQLT_ODT + +Warning: oci_bind_by_name(): Unknown or unsupported datatype given: 156 in %sbind_unsupported_1.php on line %d +===DONE=== diff --git a/ext/oci8/tests/bind_unsupported_2.phpt b/ext/oci8/tests/bind_unsupported_2.phpt new file mode 100644 index 000000000..d3e5375df --- /dev/null +++ b/ext/oci8/tests/bind_unsupported_2.phpt @@ -0,0 +1,39 @@ +--TEST-- +Bind with various unsupported 10g+ bind types +--SKIPIF-- +<?php +if (!extension_loaded('oci8')) die("skip no oci8 extension"); +if (preg_match('/^1[01]\./', oci_client_version()) !== 1) { + die ("skip expected output only valid for Oracle 10g+ clients"); +} +?> +--FILE-- +<?php + +require(dirname(__FILE__).'/connect.inc'); + +$types = array( + "SQLT_BDOUBLE" => SQLT_BDOUBLE, + "SQLT_BFLOAT" => SQLT_BFLOAT, +); + +foreach ($types as $t => $v) { + + echo "Test - $t\n"; + + $s = oci_parse($c, "select * from dual where dummy = :c1"); + $c1 = "Doug"; + oci_bind_by_name($s, ":c1", $c1, -1, $v); +} + +?> +===DONE=== +<?php exit(0); ?> +--EXPECTF-- +Test - SQLT_BDOUBLE + +Warning: oci_bind_by_name(): Unknown or unsupported datatype given: 22 in %sbind_unsupported_2.php on line %d +Test - SQLT_BFLOAT + +Warning: oci_bind_by_name(): Unknown or unsupported datatype given: 21 in %sbind_unsupported_2.php on line %d +===DONE=== diff --git a/ext/oci8/tests/bind_unsupported_3.phpt b/ext/oci8/tests/bind_unsupported_3.phpt new file mode 100644 index 000000000..5c9dec372 --- /dev/null +++ b/ext/oci8/tests/bind_unsupported_3.phpt @@ -0,0 +1,45 @@ +--TEST-- +Bind with various bind types not supported by TimesTen +--SKIPIF-- +<?php +$target_dbs = array('oracledb' => false, 'timesten' => true); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> +--FILE-- +<?php + +require(dirname(__FILE__).'/connect.inc'); + +$types = array( + "SQLT_CLOB" => SQLT_CLOB, + "SQLT_BLOB" => SQLT_BLOB, + "OCI_B_CLOB" => OCI_B_CLOB, + "OCI_B_BLOB" => OCI_B_BLOB, +); + +foreach ($types as $t => $v) { + + echo "Test - $t\n"; + + $s = oci_parse($c, "select * from dual where dummy = :c1"); + $c1 = "Doug"; + oci_bind_by_name($s, ":c1", $c1, -1, $v); +} + +?> +===DONE=== +<?php exit(0); ?> +--EXPECTF-- +Test - SQLT_CLOB + +Warning: oci_bind_by_name(): Unable to find descriptor property in %sbind_unsupported_3.php on line %d +Test - SQLT_BLOB + +Warning: oci_bind_by_name(): Unable to find descriptor property in %sbind_unsupported_3.php on line %d +Test - OCI_B_CLOB + +Warning: oci_bind_by_name(): Unable to find descriptor property in %sbind_unsupported_3.php on line %d +Test - OCI_B_BLOB + +Warning: oci_bind_by_name(): Unable to find descriptor property in %sbind_unsupported_3.php on line %d +===DONE=== diff --git a/ext/oci8/tests/bug26133.phpt b/ext/oci8/tests/bug26133.phpt index 2463e70c7..59f8a17f3 100644 --- a/ext/oci8/tests/bug26133.phpt +++ b/ext/oci8/tests/bug26133.phpt @@ -14,27 +14,11 @@ $stmtarray = array( "create table bug26133_tab (id number, value number)", ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - $r = @oci_execute($s); - if (!$r) { - $m = oci_error($s); - if (!in_array($m['code'], array( // ignore expected errors - 942 // table or view does not exist - ))) { - echo $stmt . PHP_EOL . $m['message'] . PHP_EOL; - } - } -} - -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); // Run Test -$ora_sql = "INSERT INTO bug26133_tab (id, value) VALUES ('1','1') RETURNING ROWID INTO :v_rowid "; +$ora_sql = "INSERT INTO bug26133_tab (id, value) VALUES (1,'1') RETURNING ROWID INTO :v_rowid "; $statement = OCIParse($c,$ora_sql); $rowid = OCINewDescriptor($c,OCI_D_ROWID); @@ -51,10 +35,7 @@ $stmtarray = array( "drop table bug26133_tab" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); echo "Done\n"; ?> diff --git a/ext/oci8/tests/bug27303_1.phpt b/ext/oci8/tests/bug27303_1.phpt index 6d491966a..028f5b793 100644 --- a/ext/oci8/tests/bug27303_1.phpt +++ b/ext/oci8/tests/bug27303_1.phpt @@ -4,35 +4,27 @@ Bug #27303 (OCIBindByName binds numeric PHP values as characters) <?php if (!extension_loaded('oci8')) die ("skip no oci8 extension"); require(dirname(__FILE__)."/connect.inc"); -$sv = oci_server_version($c); -$sv = preg_match('/Release 1[01]\.2\./', $sv, $matches); -if ($sv !== 1) { - die ("skip expected output only valid when using Oracle 19gR2 or 11gR2 databases"); -} else { - ob_start(); - phpinfo(INFO_MODULES); - $phpinfo = ob_get_clean(); - $iv = preg_match('/Oracle .*Version => 1[1]\./', $phpinfo); - if ($iv != 1) { - die ("skip test expected to work only with Oracle 11g or greater version of client"); - } +if (preg_match('/Release 1[01]\.2\./', oci_server_version($c), $matches) !== 1) { + die("skip expected output only valid when using Oracle 10gR2 or 11gR2 databases"); +} else if (preg_match('/^11\./', oci_client_version()) != 1) { + die("skip test expected to work only with Oracle 11g or greater version of client"); } ?> +--ENV-- +NLS_LANG= --FILE-- <?php -require dirname(__FILE__).'/connect.inc'; +require(dirname(__FILE__).'/connect.inc'); -$create_st = array(); -$create_st[] = "drop sequence myseq"; -$create_st[] = "drop table mytab"; -$create_st[] = "create sequence myseq"; -$create_st[] = "create table mytab (mydata varchar2(20), seqcol number)"; +$stmtarray = array( + "drop sequence myseq", + "drop table mytab", + "create sequence myseq", + "create table mytab (mydata varchar2(20), seqcol number)" +); -foreach ($create_st as $statement) { - $stmt = oci_parse($c, $statement); - @oci_execute($stmt); -} +oci8_test_sql_execute($c, $stmtarray); define('MYLIMIT', 200); @@ -52,14 +44,12 @@ for ($i = 1; $i < MYLIMIT; $i++) { OCICommit($c); -$drop_st = array(); -$drop_st[] = "drop sequence myseq"; -$drop_st[] = "drop table mytab"; +$stmtarray = array( + "drop sequence myseq", + "drop table mytab" +); -foreach ($create_st as $statement) { - $stmt = oci_parse($c, $statement); - oci_execute($stmt); -} +oci8_test_sql_execute($c, $stmtarray); echo "Done\n"; ?> @@ -263,4 +253,4 @@ string(3) "196" string(3) "197" string(3) "198" string(3) "199" -Done
\ No newline at end of file +Done diff --git a/ext/oci8/tests/bug27303_1_11gR1.phpt b/ext/oci8/tests/bug27303_1_11gR1.phpt index fe5c17c3e..20097613f 100644 --- a/ext/oci8/tests/bug27303_1_11gR1.phpt +++ b/ext/oci8/tests/bug27303_1_11gR1.phpt @@ -13,18 +13,16 @@ if ($sv !== 1) { --FILE-- <?php -require dirname(__FILE__).'/connect.inc'; - -$create_st = array(); -$create_st[] = "drop sequence myseq"; -$create_st[] = "drop table mytab"; -$create_st[] = "create sequence myseq"; -$create_st[] = "create table mytab (mydata varchar2(20), seqcol number)"; +require(dirname(__FILE__).'/connect.inc'); -foreach ($create_st as $statement) { - $stmt = oci_parse($c, $statement); - @oci_execute($stmt); -} +$stmtarray = array( + "drop sequence myseq", + "drop table mytab", + "create sequence myseq", + "create table mytab (mydata varchar2(20), seqcol number)" +); + +oci8_test_sql_execute($c, $stmtarray); define('MYLIMIT', 200); @@ -44,14 +42,12 @@ for ($i = 1; $i < MYLIMIT; $i++) { OCICommit($c); -$drop_st = array(); -$drop_st[] = "drop sequence myseq"; -$drop_st[] = "drop table mytab"; +$stmtarray = array( + "drop sequence myseq", + "drop table mytab" +); -foreach ($create_st as $statement) { - $stmt = oci_parse($c, $statement); - oci_execute($stmt); -} +oci8_test_sql_execute($c, $stmtarray); echo "Done\n"; ?> diff --git a/ext/oci8/tests/bug27303_2.phpt b/ext/oci8/tests/bug27303_2.phpt index 3d0c08770..e7564ac21 100644 --- a/ext/oci8/tests/bug27303_2.phpt +++ b/ext/oci8/tests/bug27303_2.phpt @@ -4,35 +4,27 @@ Bug #27303 (OCIBindByName binds numeric PHP values as characters) <?php if (!extension_loaded('oci8')) die ("skip no oci8 extension"); require(dirname(__FILE__)."/connect.inc"); -$sv = oci_server_version($c); -$sv = preg_match('/Release 1[01]\.2\./', $sv, $matches); -if ($sv !== 1) { - die ("skip expected output only valid when using Oracle 10gR2 or 11gR2 database"); -} else { - ob_start(); - phpinfo(INFO_MODULES); - $phpinfo = ob_get_clean(); - $iv = preg_match('/Oracle .*Version => 1[1]\./', $phpinfo); - if ($iv != 1) { - die ("skip test expected to work only with Oracle 11g or greater version of client"); - } +if (preg_match('/Release 1[01]\.2\./', oci_server_version($c), $matches) !== 1) { + die("skip expected output only valid when using Oracle 10gR2 or 11gR2 databases"); +} else if (preg_match('/^11\./', oci_client_version()) != 1) { + die("skip test expected to work only with Oracle 11g or greater version of client"); } ?> +--ENV-- +NLS_LANG= --FILE-- <?php require dirname(__FILE__).'/connect.inc'; -$create_st = array(); -$create_st[] = "drop sequence myseq"; -$create_st[] = "drop table mytab"; -$create_st[] = "create sequence myseq"; -$create_st[] = "create table mytab (mydata varchar2(20), seqcol number)"; +$stmtarray = array( + "drop sequence myseq", + "drop table mytab", + "create sequence myseq", + "create table mytab (mydata varchar2(20), seqcol number)" +); -foreach ($create_st as $statement) { - $stmt = oci_parse($c, $statement); - oci_execute($stmt); -} +oci8_test_sql_execute($c, $stmtarray); define('MYLIMIT', 200); define('INITMYBV', 11); @@ -54,14 +46,12 @@ for ($i = 1; $i < MYLIMIT; $i++) { OCICommit($c); -$drop_st = array(); -$drop_st[] = "drop sequence myseq"; -$drop_st[] = "drop table mytab"; +$stmtarray = array( + "drop sequence myseq", + "drop table mytab" +); -foreach ($create_st as $statement) { - $stmt = oci_parse($c, $statement); - oci_execute($stmt); -} +oci8_test_sql_execute($c, $stmtarray); echo "Done\n"; ?> @@ -265,4 +255,4 @@ string(3) "196" string(3) "197" string(3) "198" string(3) "199" -Done
\ No newline at end of file +Done diff --git a/ext/oci8/tests/bug27303_2_11gR1.phpt b/ext/oci8/tests/bug27303_2_11gR1.phpt index e1daef053..c2b5c433c 100644 --- a/ext/oci8/tests/bug27303_2_11gR1.phpt +++ b/ext/oci8/tests/bug27303_2_11gR1.phpt @@ -15,16 +15,14 @@ if ($sv !== 1) { require dirname(__FILE__).'/connect.inc'; -$create_st = array(); -$create_st[] = "drop sequence myseq"; -$create_st[] = "drop table mytab"; -$create_st[] = "create sequence myseq"; -$create_st[] = "create table mytab (mydata varchar2(20), seqcol number)"; +$stmtarray = array( + "drop sequence myseq", + "drop table mytab", + "create sequence myseq", + "create table mytab (mydata varchar2(20), seqcol number)" +); -foreach ($create_st as $statement) { - $stmt = oci_parse($c, $statement); - oci_execute($stmt); -} +oci8_test_sql_execute($c, $stmtarray); define('MYLIMIT', 200); define('INITMYBV', 11); @@ -46,14 +44,12 @@ for ($i = 1; $i < MYLIMIT; $i++) { OCICommit($c); -$drop_st = array(); -$drop_st[] = "drop sequence myseq"; -$drop_st[] = "drop table mytab"; +$stmtarray = array( + "drop sequence myseq", + "drop table mytab" +); -foreach ($create_st as $statement) { - $stmt = oci_parse($c, $statement); - oci_execute($stmt); -} +oci8_test_sql_execute($c, $stmtarray); echo "Done\n"; ?> diff --git a/ext/oci8/tests/bug27303_4.phpt b/ext/oci8/tests/bug27303_4.phpt index 20dec54da..0dcfed913 100644 --- a/ext/oci8/tests/bug27303_4.phpt +++ b/ext/oci8/tests/bug27303_4.phpt @@ -4,20 +4,14 @@ Bug #27303 (OCIBindByName binds numeric PHP values as characters) <?php if (!extension_loaded('oci8')) die ("skip no oci8 extension"); require(dirname(__FILE__)."/connect.inc"); -$sv = oci_server_version($c); -$sv = preg_match('/Release 1[01]\.2\./', $sv, $matches); -if ($sv !== 1) { - die ("skip expected output only valid when using Oracle 10gR2 or 11gR2 databases"); -} else { - ob_start(); - phpinfo(INFO_MODULES); - $phpinfo = ob_get_clean(); - $iv = preg_match('/Oracle .*Version => 1[1]\./', $phpinfo); - if ($iv != 1) { - die ("skip test expected to work only with Oracle 11g or greater version of client"); - } +if (preg_match('/Release 1[01]\.2\./', oci_server_version($c), $matches) !== 1) { + die("skip expected output only valid when using Oracle 10gR2 or 11gR2 databases"); +} else if (preg_match('/^11\./', oci_client_version()) != 1) { + die("skip test expected to work only with Oracle 11g or greater version of client"); } ?> +--ENV-- +NLS_LANG= --FILE-- <?php diff --git a/ext/oci8/tests/bug32325.phpt b/ext/oci8/tests/bug32325.phpt index 257c6977b..854e5c159 100644 --- a/ext/oci8/tests/bug32325.phpt +++ b/ext/oci8/tests/bug32325.phpt @@ -1,7 +1,10 @@ --TEST-- Bug #32325 (Cannot retrieve collection using OCI8) --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php @@ -13,18 +16,7 @@ $stmtarray = array( "create or replace type bug32325_t as table of number" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - $r = @oci_execute($s); - if (!$r) { - $m = oci_error($s); - if (!in_array($m['code'], array( // ignore expected errors - 942 // table or view does not exist - ))) { - echo $stmt . PHP_EOL . $m['message'] . PHP_EOL; - } - } -} +oci8_test_sql_execute($c, $stmtarray); // Run test @@ -49,10 +41,7 @@ $stmtarray = array( "drop type bug32325_t" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); echo "Done\n"; ?> diff --git a/ext/oci8/tests/bug35973.phpt b/ext/oci8/tests/bug35973.phpt index 81e1e58ba..3f4299ea2 100644 --- a/ext/oci8/tests/bug35973.phpt +++ b/ext/oci8/tests/bug35973.phpt @@ -1,7 +1,10 @@ --TEST-- Bug #35973 (Error ORA-24806 occurs when trying to fetch a NCLOB field) --SKIPIF-- -<?php if (!extension_loaded("oci8")) print "skip"; ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/bug36010.phpt b/ext/oci8/tests/bug36010.phpt index d451f3f2f..d18104687 100644 --- a/ext/oci8/tests/bug36010.phpt +++ b/ext/oci8/tests/bug36010.phpt @@ -1,7 +1,10 @@ --TEST-- Bug #36010 (Crash when executing SQL statment with lob parameter twice) --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/bug36096.phpt b/ext/oci8/tests/bug36096.phpt index 44b3a6ddc..2ec366b46 100644 --- a/ext/oci8/tests/bug36096.phpt +++ b/ext/oci8/tests/bug36096.phpt @@ -20,9 +20,9 @@ if(oci_execute($stmt, OCI_COMMIT_ON_SUCCESS)){ echo "Done\n"; ?> ---EXPECT-- +--EXPECTF-- bool(false) bool(false) -string(5) "'ABC'" +string(%r[53]%r) "%r('ABC'|EXP)%r" string(4) "CHAR" Done diff --git a/ext/oci8/tests/bug36403.phpt b/ext/oci8/tests/bug36403.phpt index 68c5f7b0a..53dae694e 100644 --- a/ext/oci8/tests/bug36403.phpt +++ b/ext/oci8/tests/bug36403.phpt @@ -1,7 +1,12 @@ --TEST-- Bug #36403 (oci_execute no longer supports OCI_DESCRIBE_ONLY) --SKIPIF-- -<?php if (!extension_loaded('oci8')) die ("skip no oci8 extension"); ?> +<?php +if (!extension_loaded('oci8')) die ("skip no oci8 extension"); +if (preg_match('/^1[01]\./', oci_client_version()) != 1) { + die("skip expected output only valid with Oracle 10g or greater version of client"); +} +?> --FILE-- <?php @@ -14,21 +19,7 @@ $stmtarray = array( "create table bug36403_tab (c1 number, col2 number, column3 number, col4 number)" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - $r = @oci_execute($s); - if (!$r) { - $m = oci_error($s); - if (!in_array($m['code'], array( // ignore expected errors - 942 // table or view does not exist - , 2289 // sequence does not exist - , 4080 // trigger does not exist - , 38802 // edition does not exist - ))) { - echo $stmt . PHP_EOL . $m['message'] . PHP_EOL; - } - } -} +oci8_test_sql_execute($c, $stmtarray); // Run Test @@ -48,18 +39,11 @@ $row = oci_fetch_array($s); // Clean up -//require(dirname(__FILE__).'/drop_table.inc'); - $stmtarray = array( "drop table bug36403_tab" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} - -oci_close($c); +oci8_test_sql_execute($c, $stmtarray); ?> ===DONE=== @@ -72,5 +56,5 @@ COL2 C1 Test 2 -Warning: oci_fetch_array(): ORA-24338: %sbug36403.php on line %d +Warning: oci_fetch_array(): ORA-%r(24338|01002)%r: %sbug36403.php on line %d ===DONE=== diff --git a/ext/oci8/tests/bug37220.phpt b/ext/oci8/tests/bug37220.phpt index 6743165b7..fd91dc73d 100644 --- a/ext/oci8/tests/bug37220.phpt +++ b/ext/oci8/tests/bug37220.phpt @@ -1,7 +1,10 @@ --TEST-- Bug #37220 (LOB Type mismatch when using windows & oci8.dll) --SKIPIF-- -<?php if (!extension_loaded("oci8")) print "skip"; ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php @@ -14,10 +17,7 @@ $stmtarray = array( "insert into bug37220_tab values(xmltype('<THETAG myID=\"1234\"></THETAG>'))" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - @oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); // Now let's update the row where myId = 1234 and change the tag // 'THETAG' to 'MYTAG' (mycolumn is an XMLTYPE datatype and @@ -43,7 +43,7 @@ oci_execute($stmt); while ($row = oci_fetch_array($stmt, OCI_ASSOC+OCI_RETURN_NULLS)) { foreach ($row as $item) { - echo $item."\n"; + echo trim($item)."\n"; } echo "\n"; } @@ -54,10 +54,7 @@ $stmtarray = array( "drop table bug37220_tab" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); echo "Done\n"; diff --git a/ext/oci8/tests/bug37581.phpt b/ext/oci8/tests/bug37581.phpt index ec86c5195..1c00f68d3 100644 --- a/ext/oci8/tests/bug37581.phpt +++ b/ext/oci8/tests/bug37581.phpt @@ -1,7 +1,10 @@ --TEST-- Bug #37581 (oci_bind_array_by_name clobbers input array when using SQLT_AFC, AVC) --SKIPIF-- -<?php if (!extension_loaded("oci8")) print "skip"; ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/bug38173.phpt b/ext/oci8/tests/bug38173.phpt index b92df9e39..901f6f7bf 100644 --- a/ext/oci8/tests/bug38173.phpt +++ b/ext/oci8/tests/bug38173.phpt @@ -1,7 +1,10 @@ --TEST-- Bug #38173 (Freeing nested cursors causes OCI8 to segfault) --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/bug40078.phpt b/ext/oci8/tests/bug40078.phpt index 4a234e176..4070221ee 100644 --- a/ext/oci8/tests/bug40078.phpt +++ b/ext/oci8/tests/bug40078.phpt @@ -1,7 +1,10 @@ --TEST-- Bug #40078 (ORA-01405 when fetching NULL values using oci_bind_array_by_name()) --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/bug40415.phpt b/ext/oci8/tests/bug40415.phpt index 1ebc249d3..fcccfe716 100644 --- a/ext/oci8/tests/bug40415.phpt +++ b/ext/oci8/tests/bug40415.phpt @@ -1,7 +1,10 @@ --TEST-- Bug #40415 (Using oci_fetchall with nested cursors) --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/bug41069.phpt b/ext/oci8/tests/bug41069.phpt index b3a1b9969..5daa46d6f 100644 --- a/ext/oci8/tests/bug41069.phpt +++ b/ext/oci8/tests/bug41069.phpt @@ -2,8 +2,8 @@ Bug #41069 (Oracle crash with certain data over a DB-link when prefetch memory limit used - Oracle bug 6039623) --SKIPIF-- <?php -if (!extension_loaded('oci8')) die ("skip no oci8 extension"); -require(dirname(__FILE__).'/details.inc'); +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); if (empty($dbase)) die ("skip requires network connection alias for DB link loopback"); if ($test_drcp) die("skip DRCP does not support shared database links"); ?> @@ -50,12 +50,9 @@ $stmtarray = array( "insert into bug41069_tab (c1, c2, c3, c4, c5, c6, c7, c9, c10, c12, c15) values (113, 'aaaaaaa', 'bbbbbbbbbb', 'cccccc', 'e', 'f', 'dddd', '12/04/2006', '12/04/2006', 2224, 'zzzzzzz')" - ); +); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - @oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); // Run Tests @@ -79,14 +76,9 @@ $c = oci_new_connect($user, $password, $dbase); $stmtarray = array( "drop database link bug41069_dblink", "drop table bug41069_tab" - ); - -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} +); -oci_close($c); +oci8_test_sql_execute($c, $stmtarray); echo "Done\n"; diff --git a/ext/oci8/tests/bug42134.phpt b/ext/oci8/tests/bug42134.phpt index 2b0e3707d..10b7ef160 100644 --- a/ext/oci8/tests/bug42134.phpt +++ b/ext/oci8/tests/bug42134.phpt @@ -1,7 +1,10 @@ --TEST-- Bug #42134 (Collection error for invalid collection name) --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/bug42173.phpt b/ext/oci8/tests/bug42173.phpt index 501ed75cd..d5e2f70c1 100644 --- a/ext/oci8/tests/bug42173.phpt +++ b/ext/oci8/tests/bug42173.phpt @@ -1,7 +1,10 @@ --TEST-- Bug #42173 (TIMESTAMP and INTERVAL query and field functions) --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/bug42496_1.phpt b/ext/oci8/tests/bug42496_1.phpt index 4d7e2c585..abf6cc8c7 100644 --- a/ext/oci8/tests/bug42496_1.phpt +++ b/ext/oci8/tests/bug42496_1.phpt @@ -1,7 +1,11 @@ --TEST-- Bug #42496 (LOB fetch leaks cursors, eventually failing with ORA-1000 maximum open cursors reached) --SKIPIF-- -<?php if (!extension_loaded('oci8')) die ("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +if ($stress_test !== true) die ('skip Slow test not run when $stress_test is FALSE'); +?> --FILE-- <?php @@ -17,10 +21,7 @@ $stmtarray = array( "INSERT INTO bug42496_tab VALUES('test3', 'test3')" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - @oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); // Run Test @@ -48,12 +49,7 @@ $stmtarray = array( "DROP table bug42496_tab" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - @oci_execute($s); -} - -oci_close($c); +oci8_test_sql_execute($c, $stmtarray); ?> --EXPECTF-- diff --git a/ext/oci8/tests/bug42496_2.phpt b/ext/oci8/tests/bug42496_2.phpt index e2800bbd8..8c4b79141 100644 --- a/ext/oci8/tests/bug42496_2.phpt +++ b/ext/oci8/tests/bug42496_2.phpt @@ -1,7 +1,11 @@ --TEST-- Bug #42496 (LOB fetch leaks cursors, eventually failing with ORA-1000 maximum open cursors reached) --SKIPIF-- -<?php if (!extension_loaded('oci8')) die ("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +if ($stress_test !== true) die ('skip Slow test not run when $stress_test is FALSE'); +?> --FILE-- <?php @@ -17,10 +21,7 @@ $stmtarray = array( "INSERT INTO bug42496_tab VALUES('test3', 'test3')" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - @oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); // Run Test @@ -46,12 +47,7 @@ $stmtarray = array( "DROP table bug42496_tab" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - @oci_execute($s); -} - -oci_close($c); +oci8_test_sql_execute($c, $stmtarray); ?> --EXPECTF-- diff --git a/ext/oci8/tests/bug42841.phpt b/ext/oci8/tests/bug42841.phpt index 921c8149d..a86d1ca84 100644 --- a/ext/oci8/tests/bug42841.phpt +++ b/ext/oci8/tests/bug42841.phpt @@ -1,7 +1,10 @@ --TEST-- Bug #42841 (REF CURSOR and oci_new_cursor PHP crash) --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --INI-- oci8.statement_cache_size=20 --FILE-- @@ -15,97 +18,94 @@ $c = oci_connect($user, $password, $dbase); // Initialization $stmtarray = array( - "create or replace procedure bug42841_proc(out_1 out sys_refcursor) is + "create or replace procedure bug42841_proc(out_1 out sys_refcursor) is begin - open out_1 for select 11 from dual union all select 12 from dual union all select 13 from dual; + open out_1 for select 11 from dual union all select 12 from dual union all select 13 from dual; end bug42841_proc;", - "create or replace package bug43449_pkg is - type cursortype is ref Cursor; - function testcursor return cursortype; - end bug43449_pkg;", - - "create or replace package body bug43449_pkg is - function testcursor return cursortype is - retCursor cursorType; - begin - Open retCursor For 'select * from dual'; - return retCursor; - end; - end bug43449_pkg;" + "create or replace package bug43449_pkg is + type cursortype is ref Cursor; + function testcursor return cursortype; + end bug43449_pkg;", + + "create or replace package body bug43449_pkg is + function testcursor return cursortype is + retCursor cursorType; + begin + Open retCursor For 'select * from dual'; + return retCursor; + end; + end bug43449_pkg;" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - @oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); // Main code function do_bug42841($c) { - echo "First attempt\n"; - - $sql = "BEGIN bug42841_proc(:cursor); END;"; - $stmt = oci_parse($c, $sql); - $cursor = oci_new_cursor($c); - oci_bind_by_name($stmt, ":cursor", $cursor, -1, OCI_B_CURSOR); - - oci_execute($stmt, OCI_DEFAULT); - oci_execute($cursor); - - while($row = oci_fetch_array($cursor, OCI_ASSOC + OCI_RETURN_LOBS)) { - $data1[] = $row; - } - - oci_free_statement($stmt); - oci_free_statement($cursor); - var_dump($data1); - - echo "Second attempt\n"; - - $sql = "BEGIN bug42841_proc(:cursor); END;"; - $stmt = oci_parse($c, $sql); - $cursor = oci_new_cursor($c); - oci_bind_by_name($stmt, ":cursor", $cursor, -1, OCI_B_CURSOR); - - oci_execute($stmt, OCI_DEFAULT); - oci_execute($cursor); - - while($row = oci_fetch_array($cursor, OCI_ASSOC + OCI_RETURN_LOBS)) { - $data2[] = $row; - } - - oci_free_statement($stmt); - oci_free_statement($cursor); - var_dump($data2); + echo "First attempt\n"; + + $sql = "BEGIN bug42841_proc(:cursor); END;"; + $stmt = oci_parse($c, $sql); + $cursor = oci_new_cursor($c); + oci_bind_by_name($stmt, ":cursor", $cursor, -1, OCI_B_CURSOR); + + oci_execute($stmt, OCI_DEFAULT); + oci_execute($cursor); + + while($row = oci_fetch_array($cursor, OCI_ASSOC + OCI_RETURN_LOBS)) { + $data1[] = $row; + } + + oci_free_statement($stmt); + oci_free_statement($cursor); + var_dump($data1); + + echo "Second attempt\n"; + + $sql = "BEGIN bug42841_proc(:cursor); END;"; + $stmt = oci_parse($c, $sql); + $cursor = oci_new_cursor($c); + oci_bind_by_name($stmt, ":cursor", $cursor, -1, OCI_B_CURSOR); + + oci_execute($stmt, OCI_DEFAULT); + oci_execute($cursor); + + while($row = oci_fetch_array($cursor, OCI_ASSOC + OCI_RETURN_LOBS)) { + $data2[] = $row; + } + + oci_free_statement($stmt); + oci_free_statement($cursor); + var_dump($data2); } function do_bug43449($c) { - for ($i = 0; $i < 2; $i++) { - var_dump(bug43449_getCur($c)); - } + for ($i = 0; $i < 2; $i++) { + var_dump(bug43449_getCur($c)); + } } function bug43449_getCur($c) -{ - $cur = oci_new_cursor($c); - $stmt = oci_parse($c, 'begin :cur := bug43449_pkg.testcursor; end;'); - oci_bind_by_name($stmt, ':cur', $cur, -1, OCI_B_CURSOR); - oci_execute($stmt, OCI_DEFAULT); - oci_execute($cur, OCI_DEFAULT); - - $ret = array(); - - while (ocifetchinto($cur, $row, OCI_ASSOC)) { - $ret[] = $row; - } - - oci_free_statement($cur); - oci_free_statement($stmt); - return $ret; +{ + $cur = oci_new_cursor($c); + $stmt = oci_parse($c, 'begin :cur := bug43449_pkg.testcursor; end;'); + oci_bind_by_name($stmt, ':cur', $cur, -1, OCI_B_CURSOR); + oci_execute($stmt, OCI_DEFAULT); + oci_execute($cur, OCI_DEFAULT); + + $ret = array(); + + while (ocifetchinto($cur, $row, OCI_ASSOC)) { + $ret[] = $row; + } + + oci_free_statement($cur); + oci_free_statement($stmt); + return $ret; } echo "Test bug 42841: Procedure with OUT cursor parameter\n"; @@ -119,14 +119,11 @@ do_bug43449($c); // Cleanup $stmtarray = array( - "drop procedure bug42841_proc", - "drop package bug43449_pkg" + "drop procedure bug42841_proc", + "drop package bug43449_pkg" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); echo "Done\n"; diff --git a/ext/oci8/tests/bug43492.phpt b/ext/oci8/tests/bug43492.phpt index d28aabfce..e84fa4255 100644 --- a/ext/oci8/tests/bug43492.phpt +++ b/ext/oci8/tests/bug43492.phpt @@ -1,7 +1,10 @@ --TEST-- Bug #43492 (Nested cursor leaks) --SKIPIF-- -<?php if (!extension_loaded('oci8')) die ("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php @@ -22,10 +25,7 @@ $stmtarray = array( "INSERT INTO bug43492_tab VALUES ('J')" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - @oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); /* @@ -66,12 +66,7 @@ $stmtarray = array( "DROP table bug43492_tab" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - @oci_execute($s); -} - -oci_close($c); +oci8_test_sql_execute($c, $stmtarray); ?> --EXPECT-- diff --git a/ext/oci8/tests/bug43492_2.phpt b/ext/oci8/tests/bug43492_2.phpt index fcf96e984..61511cf6d 100644 --- a/ext/oci8/tests/bug43492_2.phpt +++ b/ext/oci8/tests/bug43492_2.phpt @@ -1,7 +1,10 @@ --TEST-- Bug #43492 (Nested cursor leaks after related bug #44206 fixed) --SKIPIF-- -<?php if (!extension_loaded('oci8')) die ("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php @@ -26,10 +29,7 @@ $stmtarray = array( "INSERT INTO bug43492_tab VALUES ('J')" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - @oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); function fetch($c, $i) { $s = ociparse($c, 'select cursor(select * from bug43492_tab) c from bug43492_tab'); @@ -57,12 +57,7 @@ $stmtarray = array( "DROP table bug43492_tab" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - @oci_execute($s); -} - -oci_close($c); +oci8_test_sql_execute($c, $stmtarray); ?> --EXPECT-- diff --git a/ext/oci8/tests/bug43497.phpt b/ext/oci8/tests/bug43497.phpt index 1ea46b40d..600fb047b 100644 --- a/ext/oci8/tests/bug43497.phpt +++ b/ext/oci8/tests/bug43497.phpt @@ -2,13 +2,11 @@ Bug #43497 (OCI8 XML/getClobVal aka temporary LOBs leak UGA memory) --SKIPIF-- <?php -if (!extension_loaded('oci8')) die ("skip no oci8 extension"); -ob_start(); -phpinfo(INFO_MODULES); -$phpinfo = ob_get_clean(); -$ov = preg_match('/Oracle Version => 9/', $phpinfo); -if ($ov === 1) { - die ("skip expected output only valid for Oracle clients from 10g onwards"); +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +if ($stress_test !== true) die ('skip Slow test not run when $stress_test is FALSE'); +if (preg_match('/^1[01]\./', oci_client_version()) != 1) { + die("skip expected output only valid with Oracle 10g or greater version of client"); } ?> --FILE-- diff --git a/ext/oci8/tests/bug43497_92.phpt b/ext/oci8/tests/bug43497_92.phpt index e2cb1ce48..932a863d7 100644 --- a/ext/oci8/tests/bug43497_92.phpt +++ b/ext/oci8/tests/bug43497_92.phpt @@ -1,14 +1,12 @@ --TEST-- Bug #43497 (OCI8 XML/getClobVal aka temporary LOBs leak UGA memory) --SKIPIF-- -<?php -if (!extension_loaded('oci8')) die ("skip no oci8 extension"); -ob_start(); -phpinfo(INFO_MODULES); -$phpinfo = ob_get_clean(); -$ov = preg_match('/Oracle Version => 9.2/', $phpinfo); -if ($ov !== 1) { - die ("skip expected output only valid for Oracle 9.2 clients"); +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +if ($stress_test !== true) die ('skip Slow test not run when $stress_test is FALSE'); +if (preg_match('/Unknown/', oci_client_version()) != 1) { + die("skip expected output only valid with Oracle 9gR2 clients"); } ?> --FILE-- diff --git a/ext/oci8/tests/bug44008.phpt b/ext/oci8/tests/bug44008.phpt index fd10b26b8..faacdc625 100644 --- a/ext/oci8/tests/bug44008.phpt +++ b/ext/oci8/tests/bug44008.phpt @@ -1,7 +1,10 @@ --TEST-- -Bug #44008 (Incorrect usage of OCI-Lob->close doesn't crash PHP) +Bug #44008 (Incorrect usage of OCI-Lob->close crashes PHP) --SKIPIF-- -<?php if (!extension_loaded('oci8')) die ("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php @@ -15,10 +18,7 @@ $stmtarray = array( end;" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - @oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); // Run Test @@ -39,12 +39,7 @@ $stmtarray = array( "drop procedure bug44008_proc" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} - -oci_close($c); +oci8_test_sql_execute($c, $stmtarray); echo "Done\n"; diff --git a/ext/oci8/tests/bug44113.phpt b/ext/oci8/tests/bug44113.phpt index eee21c3ae..aa4f2bb8e 100644 --- a/ext/oci8/tests/bug44113.phpt +++ b/ext/oci8/tests/bug44113.phpt @@ -2,8 +2,8 @@ Bug #44113 (New collection creation can fail with OCI-22303) --SKIPIF-- <?php -if (!extension_loaded('oci8')) die ("skip no oci8 extension"); -require(dirname(__FILE__).'/details.inc'); +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); if ($stress_test !== true) die ('skip Slow test not run when $stress_test is FALSE'); ?> --FILE-- @@ -17,10 +17,7 @@ $stmtarray = array( "create or replace type bug44113_list_t as table of number" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - @oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); // Run Test // The test can take some time to complete and can exceed PHP's test @@ -41,12 +38,7 @@ $stmtarray = array( "drop type bug44113_list_t" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} - -oci_close($c); +oci8_test_sql_execute($c, $stmtarray); echo "Done\n"; diff --git a/ext/oci8/tests/bug44206.phpt b/ext/oci8/tests/bug44206.phpt index e5771e418..63805bf6e 100644 --- a/ext/oci8/tests/bug44206.phpt +++ b/ext/oci8/tests/bug44206.phpt @@ -1,7 +1,10 @@ --TEST-- Bug #44206 (Test if selecting ref cursors leads to ORA-1000 maximum open cursors reached) --SKIPIF-- -<?php if (!extension_loaded('oci8')) die ("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/bug45458.phpt b/ext/oci8/tests/bug45458.phpt index b1dc7720c..b257f1fc0 100644 --- a/ext/oci8/tests/bug45458.phpt +++ b/ext/oci8/tests/bug45458.phpt @@ -11,14 +11,14 @@ require(dirname(__FILE__).'/connect.inc'); echo "Test 1\n"; -$stmt = 'select dummy "a", dummy "20" from dual'; +$stmt = 'select dummy "A", dummy "20" from dual'; $s = oci_parse($c, $stmt); oci_execute($s); $r = oci_fetch_all($s, $data, 0, -1, OCI_FETCHSTATEMENT_BY_ROW); var_dump($data); var_dump($data[0]); -var_dump($data[0]["a"]); +var_dump($data[0]["A"]); var_dump($data[0]["20"]); oci_free_statement($s); @@ -28,9 +28,9 @@ $s = oci_parse($c, $stmt); oci_execute($s); $r = oci_fetch_all($s, $data, 0, -1, OCI_ASSOC); var_dump($data); -var_dump($data["a"]); +var_dump($data["A"]); var_dump($data["20"]); -var_dump($data["a"][0]); +var_dump($data["A"][0]); var_dump($data["20"][0]); oci_free_statement($s); @@ -44,14 +44,14 @@ Test 1 array(1) { [0]=> array(2) { - ["a"]=> + ["A"]=> string(1) "X" [20]=> string(1) "X" } } array(2) { - ["a"]=> + ["A"]=> string(1) "X" [20]=> string(1) "X" @@ -60,7 +60,7 @@ string(1) "X" string(1) "X" Test 2 array(2) { - ["a"]=> + ["A"]=> array(1) { [0]=> string(1) "X" diff --git a/ext/oci8/tests/bug46994.phpt b/ext/oci8/tests/bug46994.phpt index 0504952f6..604b57f26 100644 --- a/ext/oci8/tests/bug46994.phpt +++ b/ext/oci8/tests/bug46994.phpt @@ -1,7 +1,10 @@ --TEST-- Bug #46994 (CLOB size does not update when using CLOB IN OUT param in stored procedure) --SKIPIF-- -<?php if (!extension_loaded('oci8')) die ("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php @@ -22,10 +25,7 @@ $stmtarray = array( end bug46994_proc2;" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - @oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); // Run Test @@ -64,10 +64,7 @@ $stmtarray = array( "drop procedure bug46994_proc2" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); oci_close($c); diff --git a/ext/oci8/tests/bug47189.phpt b/ext/oci8/tests/bug47189.phpt index 073b410fc..c82d2cd73 100644 --- a/ext/oci8/tests/bug47189.phpt +++ b/ext/oci8/tests/bug47189.phpt @@ -1,12 +1,17 @@ --TEST-- Bug #47189 (Multiple oci_fetch_all calls) --SKIPIF-- -<?php if (!extension_loaded('oci8')) die ("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs: different error handling for this undefined behavior +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php require(dirname(__FILE__).'/connect.inc'); +echo "Test 1\n"; + $s = oci_parse($c, "select * from dual"); oci_execute($s); oci_fetch_all($s, $rs, 0, -1, OCI_FETCHSTATEMENT_BY_ROW); @@ -14,6 +19,8 @@ var_dump($rs); oci_fetch_all($s, $rs1, 0, -1, OCI_FETCHSTATEMENT_BY_ROW); var_dump($rs1); +echo "Test 2\n"; + $s = oci_parse($c, "select * from dual"); oci_execute($s); oci_fetch_all($s, $rs, 0, 1, OCI_FETCHSTATEMENT_BY_ROW); @@ -25,6 +32,7 @@ var_dump($rs1); ===DONE=== <?php exit(0); ?> --EXPECTF-- +Test 1 array(1) { [0]=> array(1) { @@ -34,6 +42,7 @@ array(1) { } array(0) { } +Test 2 array(1) { [0]=> array(1) { diff --git a/ext/oci8/tests/bug47281.phpt b/ext/oci8/tests/bug47281.phpt index 710246738..6b94bdef2 100644 --- a/ext/oci8/tests/bug47281.phpt +++ b/ext/oci8/tests/bug47281.phpt @@ -1,7 +1,10 @@ --TEST-- Bug #47281 ($php_errormsg is limited in size of characters) --SKIPIF-- -<?php if (!extension_loaded('oci8')) die ("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --ENV-- NLS_LANG=.AL32UTF8 --FILE-- @@ -19,21 +22,7 @@ $stmtarray = array( end;" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - $r = @oci_execute($s); - if (!$r) { - $m = oci_error($s); - if (!in_array($m['code'], array( // ignore expected errors - 942 // table or view does not exist - , 2289 // sequence does not exist - , 4080 // trigger does not exist - , 38802 // edition does not exist - ))) { - echo $stmt . PHP_EOL . $m['message'] . PHP_EOL; - } - } -} +oci8_test_sql_execute($c, $stmtarray); // Run Test @@ -54,13 +43,10 @@ echo $php_errormsg. "\n"; // Clean up $stmtarray = array( - "drop procedure bug47281_sp" + "drop procedure bug47281_sp" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); ?> ===DONE=== diff --git a/ext/oci8/tests/bug47281_tt.phpt b/ext/oci8/tests/bug47281_tt.phpt new file mode 100644 index 000000000..4b4e92ede --- /dev/null +++ b/ext/oci8/tests/bug47281_tt.phpt @@ -0,0 +1,59 @@ +--TEST-- +Bug #47281 ($php_errormsg is limited in size of characters) +--SKIPIF-- +<?php +$target_dbs = array('oracledb' => false, 'timesten' => true); // test runs on these DBs: shorter message length in TimesTen +require(dirname(__FILE__).'/skipif.inc'); +?> +--ENV-- +NLS_LANG=.AL32UTF8 +--FILE-- +<?php + +require(dirname(__FILE__).'/connect.inc'); + +// Initialization + +$stmtarray = array( + "create or replace procedure bug47281_sp as + begin + raise_application_error(-20000, + 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaBcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccDeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeFggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggghhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhIjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjKlllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllM'); + end;" +); + +oci8_test_sql_execute($c, $stmtarray); + +// Run Test + +echo "Test 1\n"; + +$s = oci_parse($c, 'begin bug47281_sp; end;'); +$r = @oci_execute($s); + +if (!$r) { + $m = oci_error($s); + echo $m['message'], "\n"; +} + +echo "Test 2\n"; + +echo $php_errormsg. "\n"; + +// Clean up + +$stmtarray = array( + "drop procedure bug47281_sp" +); + +oci8_test_sql_execute($c, $stmtarray); + +?> +===DONE=== +<?php exit(0); ?> +--EXPECTF-- +Test 1 +ORA-57000: TT8507: ORA-20000: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +Test 2 +oci_execute(): ORA-57000: TT8507: ORA-20000: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +===DONE=== diff --git a/ext/oci8/tests/bug51253.phpt b/ext/oci8/tests/bug51253.phpt index fea3333cc..a97272f65 100644 --- a/ext/oci8/tests/bug51253.phpt +++ b/ext/oci8/tests/bug51253.phpt @@ -1,7 +1,10 @@ --TEST-- Bug #51253 (oci_bind_array_by_name() array references) --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/bug51291.phpt b/ext/oci8/tests/bug51291_1.phpt index 75851d63b..789b94ea0 100644 --- a/ext/oci8/tests/bug51291.phpt +++ b/ext/oci8/tests/bug51291_1.phpt @@ -138,26 +138,6 @@ if (!$r) { var_dump(oci_error(), oci_error($c), oci_error($s)); } -echo "\nTest 10 - Execute - after successful 2nd query with same statement\n"; - -$s = oci_parse($c, "declare e exception; begin if :bv = 1 then raise e; end if; end;"); -$bv = 1; -oci_bind_by_name($s, ":bv", $bv); -$r = @oci_execute($s, OCI_DEFAULT); -if (!$r) { - var_dump(oci_error(), oci_error($c), oci_error($s)); - $bv = 0; - $r = oci_execute($s, OCI_DEFAULT); - echo "Execute status is "; - if (is_null($r)) echo "null"; - else if ($r === false) echo "false"; - else if ($r === true) echo "true"; - else echo $r; - echo "\n"; - echo "2nd call after successful execute\n"; - var_dump(oci_error(), oci_error($c), oci_error($s)); -} - ?> ===DONE=== <?php exit(0); ?> @@ -187,7 +167,7 @@ array(4) { Test 2 - Parse -Warning: oci_error() expects parameter 1 to be resource, boolean given in %sbug51291.php on line %d +Warning: oci_error() expects parameter 1 to be resource, boolean given in %sbug51291_1.php on line %d bool(false) array(4) { ["code"]=> @@ -202,7 +182,7 @@ array(4) { NULL 2nd call -Warning: oci_error() expects parameter 1 to be resource, boolean given in %sbug51291.php on line %d +Warning: oci_error() expects parameter 1 to be resource, boolean given in %sbug51291_1.php on line %d bool(false) array(4) { ["code"]=> @@ -221,9 +201,9 @@ array(4) { ["code"]=> int(904) ["message"]=> - string(45) "ORA-00904: "DOESNOTEXIST": %s" + string(%d) "ORA-00904:%sDOESNOTEXIST%s" ["offset"]=> - int(7) + int(%d) ["sqltext"]=> string(29) "select doesnotexist from dual" } @@ -232,9 +212,9 @@ array(4) { ["code"]=> int(904) ["message"]=> - string(45) "ORA-00904: "DOESNOTEXIST": %s" + string(%d) "ORA-00904:%sDOESNOTEXIST%s" ["offset"]=> - int(7) + int(%d) ["sqltext"]=> string(29) "select doesnotexist from dual" } @@ -246,9 +226,9 @@ array(4) { ["code"]=> int(904) ["message"]=> - string(45) "ORA-00904: "DOESNOTEXIST": %s" + string(%d) "ORA-00904:%sDOESNOTEXIST%s" ["offset"]=> - int(7) + int(%d) ["sqltext"]=> string(29) "select doesnotexist from dual" } @@ -259,9 +239,9 @@ array(4) { ["code"]=> int(904) ["message"]=> - string(45) "ORA-00904: "DOESNOTEXIST": %s" + string(%d) "ORA-00904:%sDOESNOTEXIST%s" ["offset"]=> - int(7) + int(%d) ["sqltext"]=> string(29) "select doesnotexist from dual" } @@ -273,9 +253,9 @@ array(4) { ["code"]=> int(904) ["message"]=> - string(45) "ORA-00904: "DOESNOTEXIST": %s" + string(%d) "ORA-00904:%sDOESNOTEXIST%s" ["offset"]=> - int(7) + int(%d) ["sqltext"]=> string(29) "select doesnotexist from dual" } @@ -287,9 +267,9 @@ array(4) { ["code"]=> int(904) ["message"]=> - string(45) "ORA-00904: "DOESNOTEXIST": %s" + string(%d) "ORA-00904:%sDOESNOTEXIST%s" ["offset"]=> - int(7) + int(%d) ["sqltext"]=> string(29) "select doesnotexist from dual" } @@ -301,9 +281,9 @@ array(4) { ["code"]=> int(904) ["message"]=> - string(45) "ORA-00904: "DOESNOTEXIST": %s" + string(%d) "ORA-00904:%sDOESNOTEXIST%s" ["offset"]=> - int(7) + int(%d) ["sqltext"]=> string(29) "select doesnotexist from dual" } @@ -315,9 +295,9 @@ array(4) { ["code"]=> int(904) ["message"]=> - string(45) "ORA-00904: "DOESNOTEXIST": %s" + string(%d) "ORA-00904:%sDOESNOTEXIST%s" ["offset"]=> - int(7) + int(%d) ["sqltext"]=> string(29) "select doesnotexist from dual" } @@ -330,9 +310,9 @@ array(4) { ["code"]=> int(904) ["message"]=> - string(45) "ORA-00904: "DOESNOTEXIST": %s" + string(%d) "ORA-00904:%sDOESNOTEXIST%s" ["offset"]=> - int(7) + int(%d) ["sqltext"]=> string(29) "select doesnotexist from dual" } @@ -349,14 +329,14 @@ array(4) { ["code"]=> int(904) ["message"]=> - string(45) "ORA-00904: "DOESNOTEXIST": %s" + string(%d) "ORA-00904:%sDOESNOTEXIST%s" ["offset"]=> - int(7) + int(%d) ["sqltext"]=> string(29) "select doesnotexist from dual" } -Warning: oci_execute(): ORA-00904: "REALLYNOTHERE": %s in %sbug51291.php on line %d +Warning: oci_execute(): ORA-00904: %sREALLYNOTHERE%s in %sbug51291_1.php on line %d Execute status is false 2nd call after unsuccessful execute bool(false) @@ -365,9 +345,9 @@ array(4) { ["code"]=> int(904) ["message"]=> - string(45) "ORA-00904: "DOESNOTEXIST": %s" + string(%d) "ORA-00904:%sDOESNOTEXIST%s" ["offset"]=> - int(7) + int(%d) ["sqltext"]=> string(29) "select doesnotexist from dual" } @@ -375,9 +355,9 @@ array(4) { ["code"]=> int(904) ["message"]=> - string(46) "ORA-00904: "REALLYNOTHERE": %s" + string(%d) "ORA-00904%sREALLYNOTHERE%s" ["offset"]=> - int(7) + int(%d) ["sqltext"]=> string(30) "select reallynothere from dual" } @@ -389,14 +369,14 @@ array(4) { ["code"]=> int(904) ["message"]=> - string(45) "ORA-00904: "DOESNOTEXIST": %s" + string(%d) "ORA-00904:%sDOESNOTEXIST%s" ["offset"]=> - int(7) + int(%d) ["sqltext"]=> string(29) "select doesnotexist from dual" } -Warning: oci_execute(): ORA-00904: "REALLYNOTHERE": %s in %sbug51291.php on line %d +Warning: oci_execute(): ORA-00904: %sREALLYNOTHERE%s in %sbug51291_1.php on line %d Execute status is false 2nd call after unsuccessful execute bool(false) @@ -405,30 +385,10 @@ array(4) { ["code"]=> int(904) ["message"]=> - string(46) "ORA-00904: "REALLYNOTHERE": %s" + string(%d) "ORA-00904%sREALLYNOTHERE%s" ["offset"]=> - int(7) + int(%d) ["sqltext"]=> string(30) "select reallynothere from dual" } - -Test 10 - Execute - after successful 2nd query with same statement -bool(false) -bool(false) -array(4) { - ["code"]=> - int(6510) - ["message"]=> - string(72) "ORA-06510: PL/SQL: %s -ORA-06512: %s" - ["offset"]=> - int(0) - ["sqltext"]=> - string(64) "declare e exception; begin if :bv = 1 then raise e; end if; end;" -} -Execute status is true -2nd call after successful execute -bool(false) -bool(false) -bool(false) ===DONE=== diff --git a/ext/oci8/tests/bug51291_2.phpt b/ext/oci8/tests/bug51291_2.phpt new file mode 100644 index 000000000..d5dbf6f82 --- /dev/null +++ b/ext/oci8/tests/bug51291_2.phpt @@ -0,0 +1,56 @@ +--TEST-- +Bug #51291 (oci_error() doesn't report last error when called two times) +--SKIPIF-- +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs: different error messages from TimesTen +require(dirname(__FILE__).'/skipif.inc'); +?> +--FILE-- +<?php + +require(dirname(__FILE__).'/connect.inc'); + +echo "\nTest 1 - Execute - after successful 2nd query with same statement\n"; + +$s = oci_parse($c, "declare e exception; begin if :bv = 1 then raise e; end if; end;"); +$bv = 1; +oci_bind_by_name($s, ":bv", $bv); +$r = @oci_execute($s, OCI_DEFAULT); +if (!$r) { + var_dump(oci_error(), oci_error($c), oci_error($s)); + $bv = 0; + $r = oci_execute($s, OCI_DEFAULT); + echo "Execute status is "; + if (is_null($r)) echo "null"; + else if ($r === false) echo "false"; + else if ($r === true) echo "true"; + else echo $r; + echo "\n"; + echo "2nd call after successful execute\n"; + var_dump(oci_error(), oci_error($c), oci_error($s)); +} + +?> +===DONE=== +<?php exit(0); ?> +--EXPECTF-- +Test 1 - Execute - after successful 2nd query with same statement +bool(false) +bool(false) +array(4) { + ["code"]=> + int(6510) + ["message"]=> + string(72) "ORA-06510: PL/SQL: %s +ORA-06512: %s" + ["offset"]=> + int(0) + ["sqltext"]=> + string(64) "declare e exception; begin if :bv = 1 then raise e; end if; end;" +} +Execute status is true +2nd call after successful execute +bool(false) +bool(false) +bool(false) +===DONE=== diff --git a/ext/oci8/tests/clientversion.phpt b/ext/oci8/tests/clientversion.phpt new file mode 100644 index 000000000..db70b5aff --- /dev/null +++ b/ext/oci8/tests/clientversion.phpt @@ -0,0 +1,20 @@ +--TEST-- +oci_client_version() +--SKIPIF-- +<?php +if (!extension_loaded('oci8')) die("skip no oci8 extension"); +if (preg_match('/^1[012]\./', oci_client_version()) != 1) { + die("skip test expected to work only with Oracle 10g or greater version of client"); +} +?> +--FILE-- +<?php + +echo oci_client_version(), "\n"; + +?> +===DONE=== +<?php exit(0); ?> +--EXPECTF-- +%d.%d.%d.%d.%d +===DONE=== diff --git a/ext/oci8/tests/clientversion_92.phpt b/ext/oci8/tests/clientversion_92.phpt new file mode 100644 index 000000000..d4b92cd35 --- /dev/null +++ b/ext/oci8/tests/clientversion_92.phpt @@ -0,0 +1,20 @@ +--TEST-- +oci_client_version() for Oracle 9.2 client libraries +--SKIPIF-- +<?php +if (!extension_loaded('oci8')) die("skip no oci8 extension"); +if (preg_match('/Unknown/', oci_client_version()) != 1) { + die("skip test expected to work only with Oracle 9gR2 client libraries"); +} +?> +--FILE-- +<?php + +echo oci_client_version(), "\n"; + +?> +===DONE=== +<?php exit(0); ?> +--EXPECTF-- +Unknown +===DONE=== diff --git a/ext/oci8/tests/coll_001.phpt b/ext/oci8/tests/coll_001.phpt index 57d3cf1b4..625d71a29 100644 --- a/ext/oci8/tests/coll_001.phpt +++ b/ext/oci8/tests/coll_001.phpt @@ -1,7 +1,10 @@ --TEST-- oci_new_collection() --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/coll_002.phpt b/ext/oci8/tests/coll_002.phpt index 6d3051989..cd068ff0b 100644 --- a/ext/oci8/tests/coll_002.phpt +++ b/ext/oci8/tests/coll_002.phpt @@ -1,7 +1,10 @@ --TEST-- oci_new_collection() + free() --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/coll_002_func.phpt b/ext/oci8/tests/coll_002_func.phpt index 58e641ee2..9d8e4cf97 100644 --- a/ext/oci8/tests/coll_002_func.phpt +++ b/ext/oci8/tests/coll_002_func.phpt @@ -1,7 +1,10 @@ --TEST-- oci_new_collection() + free() --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/coll_003.phpt b/ext/oci8/tests/coll_003.phpt index b5236ef6f..d7c52fe8e 100644 --- a/ext/oci8/tests/coll_003.phpt +++ b/ext/oci8/tests/coll_003.phpt @@ -1,7 +1,10 @@ --TEST-- collection methods --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/coll_003_func.phpt b/ext/oci8/tests/coll_003_func.phpt index f5c6dc7f3..58655a55f 100644 --- a/ext/oci8/tests/coll_003_func.phpt +++ b/ext/oci8/tests/coll_003_func.phpt @@ -1,7 +1,10 @@ --TEST-- collection methods --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/coll_004.phpt b/ext/oci8/tests/coll_004.phpt index eb2ac7e26..f71db4d0f 100644 --- a/ext/oci8/tests/coll_004.phpt +++ b/ext/oci8/tests/coll_004.phpt @@ -1,7 +1,10 @@ --TEST-- oci_collection_assign() --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/coll_004_func.phpt b/ext/oci8/tests/coll_004_func.phpt index 57dff2eb3..8f24363e1 100644 --- a/ext/oci8/tests/coll_004_func.phpt +++ b/ext/oci8/tests/coll_004_func.phpt @@ -1,7 +1,10 @@ --TEST-- oci_collection_assign() --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/coll_005.phpt b/ext/oci8/tests/coll_005.phpt index 0f4006996..84084e7dd 100644 --- a/ext/oci8/tests/coll_005.phpt +++ b/ext/oci8/tests/coll_005.phpt @@ -1,7 +1,10 @@ --TEST-- ocinewcollection() --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/coll_006.phpt b/ext/oci8/tests/coll_006.phpt index cf258cbe3..4799eb6b7 100644 --- a/ext/oci8/tests/coll_006.phpt +++ b/ext/oci8/tests/coll_006.phpt @@ -1,7 +1,10 @@ --TEST-- ocinewcollection() + free() --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/coll_006_func.phpt b/ext/oci8/tests/coll_006_func.phpt index 56707d608..3d6560614 100644 --- a/ext/oci8/tests/coll_006_func.phpt +++ b/ext/oci8/tests/coll_006_func.phpt @@ -1,7 +1,10 @@ --TEST-- ocinewcollection() + free() --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/coll_007.phpt b/ext/oci8/tests/coll_007.phpt index 31e10c065..86ce62f79 100644 --- a/ext/oci8/tests/coll_007.phpt +++ b/ext/oci8/tests/coll_007.phpt @@ -1,7 +1,10 @@ --TEST-- collection methods --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/coll_008.phpt b/ext/oci8/tests/coll_008.phpt index 57d44cc9a..431d5d635 100644 --- a/ext/oci8/tests/coll_008.phpt +++ b/ext/oci8/tests/coll_008.phpt @@ -1,7 +1,10 @@ --TEST-- ocicollassign() --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/coll_009.phpt b/ext/oci8/tests/coll_009.phpt index 296d6493e..917af5f2c 100644 --- a/ext/oci8/tests/coll_009.phpt +++ b/ext/oci8/tests/coll_009.phpt @@ -1,7 +1,10 @@ --TEST-- collections and wrong dates --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/coll_009_func.phpt b/ext/oci8/tests/coll_009_func.phpt index 3ed416ce0..0c5b46ddd 100644 --- a/ext/oci8/tests/coll_009_func.phpt +++ b/ext/oci8/tests/coll_009_func.phpt @@ -1,7 +1,10 @@ --TEST-- collections and wrong dates --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/coll_010.phpt b/ext/oci8/tests/coll_010.phpt index 6f72dd16c..da088e919 100644 --- a/ext/oci8/tests/coll_010.phpt +++ b/ext/oci8/tests/coll_010.phpt @@ -1,7 +1,10 @@ --TEST-- collections and nulls --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/coll_010_func.phpt b/ext/oci8/tests/coll_010_func.phpt index 7b63a276f..c5379ac9f 100644 --- a/ext/oci8/tests/coll_010_func.phpt +++ b/ext/oci8/tests/coll_010_func.phpt @@ -1,7 +1,10 @@ --TEST-- collections and nulls --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/coll_011.phpt b/ext/oci8/tests/coll_011.phpt index 7d805d3f7..3e8aa9d6a 100644 --- a/ext/oci8/tests/coll_011.phpt +++ b/ext/oci8/tests/coll_011.phpt @@ -1,7 +1,10 @@ --TEST-- collections and strings --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/coll_011_func.phpt b/ext/oci8/tests/coll_011_func.phpt index dca640e84..3871dd90e 100644 --- a/ext/oci8/tests/coll_011_func.phpt +++ b/ext/oci8/tests/coll_011_func.phpt @@ -1,7 +1,10 @@ --TEST-- collections and strings --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/coll_012.phpt b/ext/oci8/tests/coll_012.phpt index 543dafd90..4b912a80f 100644 --- a/ext/oci8/tests/coll_012.phpt +++ b/ext/oci8/tests/coll_012.phpt @@ -1,7 +1,10 @@ --TEST-- collections and correct dates --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/coll_012_func.phpt b/ext/oci8/tests/coll_012_func.phpt index fd1019e41..574281d14 100644 --- a/ext/oci8/tests/coll_012_func.phpt +++ b/ext/oci8/tests/coll_012_func.phpt @@ -1,7 +1,10 @@ --TEST-- collections and correct dates --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/coll_013.phpt b/ext/oci8/tests/coll_013.phpt index 00d88bb9a..04f549845 100644 --- a/ext/oci8/tests/coll_013.phpt +++ b/ext/oci8/tests/coll_013.phpt @@ -1,7 +1,10 @@ --TEST-- collections and correct dates (2) --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/coll_013_func.phpt b/ext/oci8/tests/coll_013_func.phpt index 0d01bc174..1992b94f6 100644 --- a/ext/oci8/tests/coll_013_func.phpt +++ b/ext/oci8/tests/coll_013_func.phpt @@ -1,7 +1,10 @@ --TEST-- collections and correct dates (2) --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/coll_014.phpt b/ext/oci8/tests/coll_014.phpt index 8458525ae..e89f4a37b 100644 --- a/ext/oci8/tests/coll_014.phpt +++ b/ext/oci8/tests/coll_014.phpt @@ -1,7 +1,10 @@ --TEST-- collections and strings (2) --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/coll_014_func.phpt b/ext/oci8/tests/coll_014_func.phpt index a0fe555b6..7d771633d 100644 --- a/ext/oci8/tests/coll_014_func.phpt +++ b/ext/oci8/tests/coll_014_func.phpt @@ -1,7 +1,10 @@ --TEST-- collections and strings (2) --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/coll_015.phpt b/ext/oci8/tests/coll_015.phpt index 64b0aea10..c422d211b 100644 --- a/ext/oci8/tests/coll_015.phpt +++ b/ext/oci8/tests/coll_015.phpt @@ -1,7 +1,10 @@ --TEST-- collections and numbers (2) --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/coll_015_func.phpt b/ext/oci8/tests/coll_015_func.phpt index eeed7839a..18e21dd93 100644 --- a/ext/oci8/tests/coll_015_func.phpt +++ b/ext/oci8/tests/coll_015_func.phpt @@ -1,7 +1,10 @@ --TEST-- collections and numbers (2) --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/coll_016.phpt b/ext/oci8/tests/coll_016.phpt index c2af9cc22..2e06e2959 100644 --- a/ext/oci8/tests/coll_016.phpt +++ b/ext/oci8/tests/coll_016.phpt @@ -1,7 +1,10 @@ --TEST-- collections and negative/too big element indexes --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/coll_016_func.phpt b/ext/oci8/tests/coll_016_func.phpt index cde26f22f..67830af25 100644 --- a/ext/oci8/tests/coll_016_func.phpt +++ b/ext/oci8/tests/coll_016_func.phpt @@ -1,7 +1,10 @@ --TEST-- collections and negative/too big element indexes --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/coll_017.phpt b/ext/oci8/tests/coll_017.phpt index 42347ba6f..56f509965 100644 --- a/ext/oci8/tests/coll_017.phpt +++ b/ext/oci8/tests/coll_017.phpt @@ -1,7 +1,10 @@ --TEST-- collections and nulls (2) --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/coll_017_func.phpt b/ext/oci8/tests/coll_017_func.phpt index 914844ae6..3440195fe 100644 --- a/ext/oci8/tests/coll_017_func.phpt +++ b/ext/oci8/tests/coll_017_func.phpt @@ -1,7 +1,10 @@ --TEST-- collections and nulls (2) --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/coll_018.phpt b/ext/oci8/tests/coll_018.phpt index f97cc49e7..a3f0b29ce 100644 --- a/ext/oci8/tests/coll_018.phpt +++ b/ext/oci8/tests/coll_018.phpt @@ -1,7 +1,10 @@ --TEST-- Collection trim tests --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/coll_019.phpt b/ext/oci8/tests/coll_019.phpt index 15a673d71..371c802ed 100644 --- a/ext/oci8/tests/coll_019.phpt +++ b/ext/oci8/tests/coll_019.phpt @@ -1,7 +1,13 @@ --TEST-- Test collection Oracle error handling collections and numbers (2) --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/commit_001.phpt b/ext/oci8/tests/commit_001.phpt index 836d2cd1d..806fb193a 100644 --- a/ext/oci8/tests/commit_001.phpt +++ b/ext/oci8/tests/commit_001.phpt @@ -1,7 +1,10 @@ --TEST-- Test OCI_NO_AUTO_COMMIT constant --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/commit_002.phpt b/ext/oci8/tests/commit_002.phpt index 1819fe116..4079ac0c3 100644 --- a/ext/oci8/tests/commit_002.phpt +++ b/ext/oci8/tests/commit_002.phpt @@ -1,7 +1,10 @@ --TEST-- Test oci_commit failure --SKIPIF-- -<?php if (!extension_loaded('oci8')) die ("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php @@ -16,21 +19,7 @@ $stmtarray = array( y int constraint commit_002_tab_check_y check ( y > 0 ) deferrable initially deferred)" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - $r = @oci_execute($s); - if (!$r) { - $m = oci_error($s); - if (!in_array($m['code'], array( // ignore expected errors - 942 // table or view does not exist - , 2289 // sequence does not exist - , 4080 // trigger does not exist - , 38802 // edition does not exist - ))) { - echo $stmt . PHP_EOL . $m['message'] . PHP_EOL; - } - } -} +oci8_test_sql_execute($c, $stmtarray); // Run Test @@ -68,12 +57,7 @@ $stmtarray = array( "drop table commit_002_tab" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} - -oci_close($c); +oci8_test_sql_execute($c, $stmtarray); ?> ===DONE=== diff --git a/ext/oci8/tests/commit_old.phpt b/ext/oci8/tests/commit_old.phpt index 196e0650c..012fc0237 100644 --- a/ext/oci8/tests/commit_old.phpt +++ b/ext/oci8/tests/commit_old.phpt @@ -1,7 +1,10 @@ --TEST-- ocicommit()/ocirollback() --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/conn_attr_1.phpt b/ext/oci8/tests/conn_attr_1.phpt index c7c1b870e..ad508a2ed 100644 --- a/ext/oci8/tests/conn_attr_1.phpt +++ b/ext/oci8/tests/conn_attr_1.phpt @@ -2,24 +2,17 @@ Set and get of connection attributes with all types of connections. --SKIPIF-- <?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); -require(dirname(__FILE__)."/connect.inc"); +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); + if (strcasecmp($user, "system") && strcasecmp($user, "sys")) die("skip needs to be run as a DBA user"); if ($test_drcp) die("skip output might vary with DRCP"); -$sv = oci_server_version($c); -$sv = preg_match('/Release 1[012]\./', $sv, $matches); -if ($sv == 1) { - ob_start(); - phpinfo(INFO_MODULES); - $phpinfo = ob_get_clean(); - $iv = preg_match('/Oracle .*Version => 1[012]\./', $phpinfo); - if ($iv != 1) { - die ("skip test expected to work only with Oracle 10g or greater client "); - } -} -else { - die ("skip test expected to work only with Oracle 10g or greater server"); +if (preg_match('/Release 1[01]\./', oci_server_version($c), $matches) !== 1) { + die("skip expected output only valid when using Oracle 10g or greater database server"); +} else if (preg_match('/^1[01]\./', oci_client_version()) != 1) { + die("skip test expected to work only with Oracle 10g or greater version of client"); } ?> diff --git a/ext/oci8/tests/conn_attr_2.phpt b/ext/oci8/tests/conn_attr_2.phpt index 4765d5eb7..107250352 100644 --- a/ext/oci8/tests/conn_attr_2.phpt +++ b/ext/oci8/tests/conn_attr_2.phpt @@ -1,25 +1,19 @@ --TEST-- Set and get of connection attributes across persistent connections and sysdba connection. --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); -require(dirname(__FILE__)."/connect.inc"); +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); + if (strcasecmp($user, "system") && strcasecmp($user, "sys")) die("skip needs to be run as a DBA user"); if ($test_drcp) die("skip output might vary with DRCP"); -$sv = oci_server_version($c); -$sv = preg_match('/Release 1[012]\./', $sv, $matches); -if ($sv == 1) { - ob_start(); - phpinfo(INFO_MODULES); - $phpinfo = ob_get_clean(); - $iv = preg_match('/Oracle .*Version => 1[012]\./', $phpinfo); - if ($iv != 1) { - die ("skip test expected to work only with Oracle 10g or greater version of client"); - } -} -else { - die ("skip test expected to work only with Oracle 10g or greater version of server"); +if (preg_match('/Release 1[01]\./', oci_server_version($c), $matches) !== 1) { + die("skip expected output only valid when using Oracle 10g or greater database server"); +} else if (preg_match('/^1[01]\./', oci_client_version()) != 1) { + die("skip test expected to work only with Oracle 10g or greater version of client"); } + ?> --INI-- oci8.privileged_connect = On diff --git a/ext/oci8/tests/conn_attr_3.phpt b/ext/oci8/tests/conn_attr_3.phpt index 8b6d92123..be8d3306d 100644 --- a/ext/oci8/tests/conn_attr_3.phpt +++ b/ext/oci8/tests/conn_attr_3.phpt @@ -1,24 +1,17 @@ --TEST-- Set and get of connection attributes with oci_close(). --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); -require(dirname(__FILE__)."/connect.inc"); +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); + if (strcasecmp($user, "system") && strcasecmp($user, "sys")) die("skip needs to be run as a DBA user"); if ($test_drcp) die("skip output might vary with DRCP"); -$sv = oci_server_version($c); -$sv = preg_match('/Release 1[012]\./', $sv, $matches); -if ($sv == 1) { - ob_start(); - phpinfo(INFO_MODULES); - $phpinfo = ob_get_clean(); - $iv = preg_match('/Oracle .*Version => 1[012]\./', $phpinfo); - if ($iv != 1) { - die ("skip test expected to work only with Oracle 10g or greater version of client"); - } -} -else { - die ("skip test expected to work only with Oracle 10g or greater version of server"); +if (preg_match('/Release 1[01]\./', oci_server_version($c), $matches) !== 1) { + die("skip expected output only valid when using Oracle 10g or greater database server"); +} else if (preg_match('/^1[01]\./', oci_client_version()) != 1) { + die("skip test expected to work only with Oracle 10g or greater version of client"); } ?> --FILE-- diff --git a/ext/oci8/tests/conn_attr_4.phpt b/ext/oci8/tests/conn_attr_4.phpt index 9f55f5326..b1a22858a 100644 --- a/ext/oci8/tests/conn_attr_4.phpt +++ b/ext/oci8/tests/conn_attr_4.phpt @@ -1,29 +1,25 @@ --TEST-- Set and get of connection attributes with errors. --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); -require(dirname(__FILE__)."/connect.inc"); +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); + if (strcasecmp($user, "system") && strcasecmp($user, "sys")) die("skip needs to be run as a DBA user"); if ($test_drcp) die("skip output might vary with DRCP"); if ($stress_test !== true) die ('skip Slow test not run when $stress_test is FALSE'); -$sv = oci_server_version($c); -$sv = preg_match('/Release 1[012]\./', $sv, $matches); -if ($sv == 1) { - ob_start(); - phpinfo(INFO_MODULES); - $phpinfo = ob_get_clean(); - $iv = preg_match('/Oracle .*Version => 1[012]\./', $phpinfo); - if ($iv != 1) { - die ("skip test expected to work only with Oracle 10g or greater version of client"); - } -} -else { - die ("skip test expected to work only with Oracle 10g or greater version of server"); +if (preg_match('/Release (11\.2|12)\./', oci_server_version($c), $matches) !== 1) { + // Bug fixed in 11.2 prevents client_info being rest + die("skip expected output only valid when using Oracle 11gR2 or greater database server"); +} else if (preg_match('/^1[01]\./', oci_client_version()) != 1) { + die("skip test expected to work only with Oracle 10g or greater version of client"); } ?> --FILE-- <?php + + require(dirname(__FILE__)."/conn_attr.inc"); $user='testuser'; @@ -56,6 +52,7 @@ var_dump(oci_set_action($c1,'ACTION1')); get_attr($c1,'ACTION'); // Testing with different types of values +// NB. This may diff in 11.1.0.6 due to a bug causing CLIENT_INFO of NULL to be ignored. echo "\nSetting to different values \n"; $values_array = array(1000,NULL,'this is a very huge string with a length > 64 !!!!!this is a very huge string with a length > 64 !!!!!this is a very huge string with a length > 64 !!!!!this is a very huge string with a length > 64 !!!!!'); diff --git a/ext/oci8/tests/conn_attr_5.phpt b/ext/oci8/tests/conn_attr_5.phpt index 9f6b6c724..d694ec06a 100644 --- a/ext/oci8/tests/conn_attr_5.phpt +++ b/ext/oci8/tests/conn_attr_5.phpt @@ -1,24 +1,17 @@ --TEST-- Set and get connection attributes with scope end. --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); -require(dirname(__FILE__)."/connect.inc"); +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); + if (strcasecmp($user, "system") && strcasecmp($user, "sys")) die("skip needs to be run as a DBA user"); if ($test_drcp) die("skip output might vary with DRCP"); -$sv = oci_server_version($c); -$sv = preg_match('/Release 1[012]\./', $sv, $matches); -if ($sv == 1) { - ob_start(); - phpinfo(INFO_MODULES); - $phpinfo = ob_get_clean(); - $iv = preg_match('/Oracle .*Version => 1[012]\./', $phpinfo); - if ($iv != 1) { - die ("skip test expected to work only with Oracle 10g or greater version of client"); - } -} -else { - die ("skip test expected to work only with Oracle 10g or greater version of server"); +if (preg_match('/Release 1[01]\./', oci_server_version($c), $matches) !== 1) { + die("skip expected output only valid when using Oracle 10g or greater database server"); +} else if (preg_match('/^1[01]\./', oci_client_version()) != 1) { + die("skip test expected to work only with Oracle 10g or greater version of client"); } ?> --FILE-- diff --git a/ext/oci8/tests/connect.inc b/ext/oci8/tests/connect.inc index 5e340cf6f..06d134856 100644 --- a/ext/oci8/tests/connect.inc +++ b/ext/oci8/tests/connect.inc @@ -10,9 +10,8 @@ else { } if (!$c) { - echo "connect.inc: Failed to connect as '$user' to '$dbase'\n"; - $e = oci_error(); - echo $e['message']."\n"; + $m = oci_error(); + trigger_error("connect.inc: Failed to connect as '$user' to '$dbase': ". $m['message'], E_USER_ERROR); } ?> diff --git a/ext/oci8/tests/connect_scope1.phpt b/ext/oci8/tests/connect_scope1.phpt index d6d16f17e..d265d1da9 100644 --- a/ext/oci8/tests/connect_scope1.phpt +++ b/ext/oci8/tests/connect_scope1.phpt @@ -19,10 +19,7 @@ if (!empty($dbase)) else $c1 = oci_new_connect($user,$password); -foreach ($stmtarray as $stmt) { - $s1 = oci_parse($c1, $stmt); - @oci_execute($s1); -} +oci8_test_sql_execute($c1, $stmtarray); // Run Test @@ -66,10 +63,7 @@ $stmtarray = array( "drop table connect_scope1_tab" ); -foreach ($stmtarray as $stmt) { - $s1 = oci_parse($c1, $stmt); - oci_execute($s1); -} +oci8_test_sql_execute($c1, $stmtarray); echo "Done\n"; diff --git a/ext/oci8/tests/connect_scope2.phpt b/ext/oci8/tests/connect_scope2.phpt index 7017493f5..7d05c1141 100644 --- a/ext/oci8/tests/connect_scope2.phpt +++ b/ext/oci8/tests/connect_scope2.phpt @@ -19,10 +19,7 @@ if (!empty($dbase)) else $c1 = oci_new_connect($user,$password); -foreach ($stmtarray as $stmt) { - $s1 = oci_parse($c1, $stmt); - @oci_execute($s1); -} +oci8_test_sql_execute($c1, $stmtarray); // Run Test @@ -66,10 +63,7 @@ $stmtarray = array( "drop table connect_scope2_tab" ); -foreach ($stmtarray as $stmt) { - $s1 = oci_parse($c1, $stmt); - oci_execute($s1); -} +oci8_test_sql_execute($c1, $stmtarray); echo "Done\n"; diff --git a/ext/oci8/tests/connect_scope_try1.phpt b/ext/oci8/tests/connect_scope_try1.phpt index a881ea6ea..7f26d4334 100644 --- a/ext/oci8/tests/connect_scope_try1.phpt +++ b/ext/oci8/tests/connect_scope_try1.phpt @@ -21,10 +21,7 @@ if (!empty($dbase)) else $c1 = oci_new_connect($user,$password); -foreach ($stmtarray as $stmt) { - $s1 = oci_parse($c1, $stmt); - @oci_execute($s1); -} +oci8_test_sql_execute($c1, $stmtarray); // Run Test @@ -73,17 +70,14 @@ $stmtarray = array( "drop table scope_try1_tab" ); -foreach ($stmtarray as $stmt) { - $s1 = oci_parse($c1, $stmt); - oci_execute($s1); -} +oci8_test_sql_execute($c1, $stmtarray); echo "Done\n"; ?> --EXPECTF-- Test 1 -Caught Exception: oci_execute(): ORA-00984: %s +Caught Exception: oci_execute(): ORA-%r(00984|57000: TT2957)%r: %s resource(%d) of type (oci8 connection) array(1) { ["C1"]=> diff --git a/ext/oci8/tests/connect_scope_try2.phpt b/ext/oci8/tests/connect_scope_try2.phpt index 9e6b5ce2e..94adb85e9 100644 --- a/ext/oci8/tests/connect_scope_try2.phpt +++ b/ext/oci8/tests/connect_scope_try2.phpt @@ -21,10 +21,7 @@ if (!empty($dbase)) else $c1 = oci_new_connect($user,$password); -foreach ($stmtarray as $stmt) { - $s1 = oci_parse($c1, $stmt); - @oci_execute($s1); -} +oci8_test_sql_execute($c1, $stmtarray); // Run Test @@ -73,17 +70,14 @@ $stmtarray = array( "drop table scope_try2_tab" ); -foreach ($stmtarray as $stmt) { - $s1 = oci_parse($c1, $stmt); - oci_execute($s1); -} +oci8_test_sql_execute($c1, $stmtarray); echo "Done\n"; ?> --EXPECTF-- Test 1 -Caught Exception: oci_execute(): ORA-00984: %s +Caught Exception: oci_execute(): ORA-%r(00984|57000: TT2957)%r: %s resource(%d) of type (oci8 connection) array(1) { ["C1"]=> diff --git a/ext/oci8/tests/connect_scope_try3.phpt b/ext/oci8/tests/connect_scope_try3.phpt index 1cd44b021..e7891a107 100644 --- a/ext/oci8/tests/connect_scope_try3.phpt +++ b/ext/oci8/tests/connect_scope_try3.phpt @@ -21,10 +21,7 @@ if (!empty($dbase)) else $c1 = oci_new_connect($user,$password); -foreach ($stmtarray as $stmt) { - $s1 = oci_parse($c1, $stmt); - @oci_execute($s1); -} +oci8_test_sql_execute($c1, $stmtarray); // Run Test @@ -73,17 +70,14 @@ $stmtarray = array( "drop table scope_try3_tab" ); -foreach ($stmtarray as $stmt) { - $s1 = oci_parse($c1, $stmt); - oci_execute($s1); -} +oci8_test_sql_execute($c1, $stmtarray); echo "Done\n"; ?> --EXPECTF-- Test 1 -Caught Exception: oci_execute(): ORA-00984: %s +Caught Exception: oci_execute(): ORA-%r(00984|57000: TT2957)%r: %s resource(%d) of type (oci8 connection) array(1) { ["C1"]=> diff --git a/ext/oci8/tests/connect_scope_try4.phpt b/ext/oci8/tests/connect_scope_try4.phpt index 9b4cd1f27..40369c67b 100644 --- a/ext/oci8/tests/connect_scope_try4.phpt +++ b/ext/oci8/tests/connect_scope_try4.phpt @@ -21,10 +21,7 @@ if (!empty($dbase)) else $c1 = oci_new_connect($user,$password); -foreach ($stmtarray as $stmt) { - $s1 = oci_parse($c1, $stmt); - @oci_execute($s1); -} +oci8_test_sql_execute($c1, $stmtarray); // Run Test @@ -73,17 +70,14 @@ $stmtarray = array( "drop table scope_try4_tab" ); -foreach ($stmtarray as $stmt) { - $s1 = oci_parse($c1, $stmt); - oci_execute($s1); -} +oci8_test_sql_execute($c1, $stmtarray); echo "Done\n"; ?> --EXPECTF-- Test 1 -Caught Exception: oci_execute(): ORA-00984: %s +Caught Exception: oci_execute(): ORA-%r(00984|57000: TT2957)%r: %s resource(%d) of type (oci8 connection) array(1) { ["C1"]=> diff --git a/ext/oci8/tests/connect_scope_try5.phpt b/ext/oci8/tests/connect_scope_try5.phpt index 121fb33dc..3afc87b91 100644 --- a/ext/oci8/tests/connect_scope_try5.phpt +++ b/ext/oci8/tests/connect_scope_try5.phpt @@ -21,10 +21,7 @@ if (!empty($dbase)) else $c1 = oci_new_connect($user,$password); -foreach ($stmtarray as $stmt) { - $s1 = oci_parse($c1, $stmt); - @oci_execute($s1); -} +oci8_test_sql_execute($c1, $stmtarray); // Run Test @@ -73,17 +70,14 @@ $stmtarray = array( "drop table scope_try5_tab" ); -foreach ($stmtarray as $stmt) { - $s1 = oci_parse($c1, $stmt); - oci_execute($s1); -} +oci8_test_sql_execute($c1, $stmtarray); echo "Done\n"; ?> --EXPECTF-- Test 1 -Caught Exception: oci_execute(): ORA-00984: %s +Caught Exception: oci_execute(): ORA-%r(00984|57000: TT2957)%r: %s resource(%d) of type (oci8 persistent connection) array(1) { ["C1"]=> diff --git a/ext/oci8/tests/connect_scope_try6.phpt b/ext/oci8/tests/connect_scope_try6.phpt index 3347543ab..d7b4edfdb 100644 --- a/ext/oci8/tests/connect_scope_try6.phpt +++ b/ext/oci8/tests/connect_scope_try6.phpt @@ -21,10 +21,7 @@ if (!empty($dbase)) else $c1 = oci_new_connect($user,$password); -foreach ($stmtarray as $stmt) { - $s1 = oci_parse($c1, $stmt); - @oci_execute($s1); -} +oci8_test_sql_execute($c1, $stmtarray); // Run Test @@ -73,17 +70,14 @@ $stmtarray = array( "drop table scope_try6_tab" ); -foreach ($stmtarray as $stmt) { - $s1 = oci_parse($c1, $stmt); - oci_execute($s1); -} +oci8_test_sql_execute($c1, $stmtarray); echo "Done\n"; ?> --EXPECTF-- Test 1 -Caught Exception: oci_execute(): ORA-00984: %s +Caught Exception: oci_execute(): ORA-%r(00984|57000: TT2957)%r: %s resource(%d) of type (oci8 persistent connection) array(1) { ["C1"]=> diff --git a/ext/oci8/tests/connect_without_oracle_home_11.phpt b/ext/oci8/tests/connect_without_oracle_home_11.phpt index be99a8bd6..1620803db 100644 --- a/ext/oci8/tests/connect_without_oracle_home_11.phpt +++ b/ext/oci8/tests/connect_without_oracle_home_11.phpt @@ -10,9 +10,8 @@ $ov = preg_match('/Compile-time ORACLE_HOME/', $phpinfo); if ($ov != 1) { die ("skip Test only valid when OCI8 is built with an ORACLE_HOME"); } -$iv = preg_match('/Oracle .*Version => (11\.2|12)/', $phpinfo); -if ($iv != 1) { - die ("skip tests a feature that works only with Oracle 11gR2 or greater version of client"); +if (preg_match('/^11\.2|12\./', oci_client_version()) != 1) { + die("skip test expected to work only with Oracle 11gR2 or greater version of client"); } ?> --ENV-- diff --git a/ext/oci8/tests/connect_without_oracle_home_old.phpt b/ext/oci8/tests/connect_without_oracle_home_old.phpt index 602d55ff8..5a731337a 100644 --- a/ext/oci8/tests/connect_without_oracle_home_old.phpt +++ b/ext/oci8/tests/connect_without_oracle_home_old.phpt @@ -10,9 +10,8 @@ $ov = preg_match('/Compile-time ORACLE_HOME/', $phpinfo); if ($ov !== 1) { die ("skip Test only valid when OCI8 is built with an ORACLE_HOME"); } -$iv = preg_match('/Oracle .*Version => (10\.2)/', $phpinfo); -if ($iv != 1) { - die ("skip tests a feature that works only with Oracle 10gR2"); +if (preg_match('/^10\.2\./', oci_client_version()) != 1) { + die("skip test expected to work only with Oracle 10gR2 client libraries"); } ?> --ENV-- diff --git a/ext/oci8/tests/connect_without_oracle_home_old_11.phpt b/ext/oci8/tests/connect_without_oracle_home_old_11.phpt index 9bb42e75a..c7cfecf39 100644 --- a/ext/oci8/tests/connect_without_oracle_home_old_11.phpt +++ b/ext/oci8/tests/connect_without_oracle_home_old_11.phpt @@ -10,9 +10,8 @@ $ov = preg_match('/Compile-time ORACLE_HOME/', $phpinfo); if ($ov !== 1) { die ("skip Test only valid when OCI8 is built with an ORACLE_HOME"); } -$iv = preg_match('/Oracle .*Version => (11\.2|12)/', $phpinfo); -if ($iv != 1) { - die ("skip tests a feature that works only with Oracle 11gR2 or greater version of client"); +if (preg_match('/^11\.2|12\./', oci_client_version()) != 1) { + die("skip test expected to work only with Oracle 11gR2 or greater version of client"); } ?> --ENV-- diff --git a/ext/oci8/tests/create_table.inc b/ext/oci8/tests/create_table.inc index afd1fceb9..733598068 100644 --- a/ext/oci8/tests/create_table.inc +++ b/ext/oci8/tests/create_table.inc @@ -1,11 +1,13 @@ <?php - if ($c) { - $ora_sql = "DROP TABLE ".$schema.$table_name; - $statement = oci_parse($c, $ora_sql); - @oci_execute($statement); - - $ora_sql = "CREATE TABLE ".$schema.$table_name." (id NUMBER, value NUMBER, blob BLOB, clob CLOB, string VARCHAR(10))"; - $statement = oci_parse($c, $ora_sql); - oci_execute($statement); - } +if ($c) { + + + $stmtarray = array( + "DROP TABLE ".$schema.$table_name, + "CREATE TABLE ".$schema.$table_name." (id NUMBER, value NUMBER, blob BLOB, clob CLOB, string VARCHAR(10))" + ); + + oci8_test_sql_execute($c, $stmtarray); + +} ?> diff --git a/ext/oci8/tests/cursor_bind.phpt b/ext/oci8/tests/cursor_bind.phpt index c2ce15cb3..740402e7e 100644 --- a/ext/oci8/tests/cursor_bind.phpt +++ b/ext/oci8/tests/cursor_bind.phpt @@ -1,54 +1,34 @@ --TEST-- bind and fetch cursor from a statement --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php -require dirname(__FILE__)."/connect.inc"; - -$drop_table = "DROP TABLE ".$schema.$table_name.""; +require(dirname(__FILE__)."/connect.inc"); -if (!($s = oci_parse($c, $drop_table))) { - die("oci_parse(drop) failed!\n"); -} +// Initialization -@oci_execute($s); - -$create_table = "CREATE TABLE ".$schema.$table_name." (id NUMBER, value VARCHAR(20))"; - -if (!($s = oci_parse($c, $create_table))) { - die("oci_parse(create) failed!\n"); -} - -if (!oci_execute($s)) { - die("oci_execute(create) failed!\n"); -} - -$insert_sql = "INSERT INTO ".$schema.$table_name." (id, value) VALUES (1,1)"; - -if (!($s = oci_parse($c, $insert_sql))) { - die("oci_parse(insert) failed!\n"); -} - -for ($i = 0; $i<3; $i++) { - if (!oci_execute($s)) { - die("oci_execute(insert) failed!\n"); - } -} - -if (!oci_commit($c)) { - die("oci_commit() failed!\n"); -} +$stmtarray = array( + "drop table cursor_bind_tab", + "create table cursor_bind_tab (id NUMBER, value VARCHAR(20))", + "insert into cursor_bind_tab values (1, '1')", + "insert into cursor_bind_tab values (1, '1')", + "insert into cursor_bind_tab values (1, '1')" +); +oci8_test_sql_execute($c, $stmtarray); $sql = " DECLARE TYPE curtype IS REF CURSOR; cursor_var curtype; BEGIN - OPEN cursor_var FOR SELECT id, value FROM ".$schema.$table_name."; - :curs := cursor_var; + OPEN cursor_var FOR SELECT id, value FROM cursor_bind_tab; + :curs := cursor_var; END; "; @@ -65,18 +45,18 @@ var_dump(oci_fetch_row($cursor)); var_dump(oci_fetch_row($cursor)); var_dump(oci_fetch_row($cursor)); -echo "Done\n"; +// Clean up -$drop_table = "DROP TABLE ".$schema.$table_name.""; - -if (!($s = oci_parse($c, $drop_table))) { - die("oci_parse(drop) failed!\n"); -} +$stmtarray = array( + "drop table cursor_bind_tab" +); -@oci_execute($s); +oci8_test_sql_execute($c, $stmtarray); ?> ---EXPECT-- +===DONE=== +<?php exit(0); ?> +--EXPECT-- array(2) { [0]=> string(1) "1" @@ -96,4 +76,4 @@ array(2) { string(1) "1" } bool(false) -Done +===DONE=== diff --git a/ext/oci8/tests/cursor_bind_err.phpt b/ext/oci8/tests/cursor_bind_err.phpt index 33bd04b6d..197aad1d1 100644 --- a/ext/oci8/tests/cursor_bind_err.phpt +++ b/ext/oci8/tests/cursor_bind_err.phpt @@ -1,7 +1,10 @@ --TEST-- binding a cursor (with errors) --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php @@ -17,23 +20,7 @@ $stmtarray = array( "insert into cursor_bind_err_tab (id, value) values (1,1)", ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - $r = @oci_execute($s); - if (!$r) { - $m = oci_error($s); - if (!in_array($m['code'], array( // ignore expected errors - 942 // table or view does not exist - ))) { - echo $stmt . PHP_EOL . $m['message'] . PHP_EOL; - } - } -} - -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); // Run Test @@ -54,10 +41,7 @@ $stmtarray = array( "drop table cursor_bind_err_tab" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); echo "Done\n"; diff --git a/ext/oci8/tests/cursors.phpt b/ext/oci8/tests/cursors.phpt index 22c89c9c5..0919e4411 100644 --- a/ext/oci8/tests/cursors.phpt +++ b/ext/oci8/tests/cursors.phpt @@ -1,7 +1,10 @@ --TEST-- fetching cursor from a statement --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/cursors_old.phpt b/ext/oci8/tests/cursors_old.phpt index 73447c82b..d60e2ff1e 100644 --- a/ext/oci8/tests/cursors_old.phpt +++ b/ext/oci8/tests/cursors_old.phpt @@ -1,7 +1,10 @@ --TEST-- fetching cursor from a statement --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php @@ -17,23 +20,7 @@ $stmtarray = array( "insert into cursors_old_tab (id, value) values (1,1)", ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - $r = @oci_execute($s); - if (!$r) { - $m = oci_error($s); - if (!in_array($m['code'], array( // ignore expected errors - 942 // table or view does not exist - ))) { - echo $stmt . PHP_EOL . $m['message'] . PHP_EOL; - } - } -} - -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); // Run Test @@ -58,10 +45,7 @@ $stmtarray = array( "drop table cursors_old_tab" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); echo "Done\n"; diff --git a/ext/oci8/tests/dbmsoutput.phpt b/ext/oci8/tests/dbmsoutput.phpt new file mode 100644 index 000000000..eaace0fa0 --- /dev/null +++ b/ext/oci8/tests/dbmsoutput.phpt @@ -0,0 +1,750 @@ +--TEST-- +PL/SQL: dbms_output +--SKIPIF-- +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> +--FILE-- +<?php + +require(dirname(__FILE__).'/connect.inc'); + +// Initialization + +$stmtarray = array( + "create or replace procedure dbmsoutput_proc as + begin + dbms_output.put_line('Hello World!'); + end;", + + "create or replace type dorow as table of varchar2(4000)", + + "create or replace function mydofetch return dorow pipelined is + line varchar2(4000); + status integer; + begin + loop + dbms_output.get_line(line, status); + exit when status = 1; + pipe row (line); + end loop; + return; + end;" +); + +oci8_test_sql_execute($c, $stmtarray); + +// Run Test + +// Turn DBMS_OUTPUT on +function setserveroutputon($c) +{ + $s = oci_parse($c, "begin dbms_output.enable(null); end;"); + oci_execute($s); +} + +// Create some output +function createoutput($c, $prefix) +{ + $s = oci_parse($c, "call dbms_output.put_line(:bv1 || ' ' || :bv2 || ' Hello, world! Lots and lots and ... of text')"); + oci_bind_by_name($s, ":bv1", $i, -1, SQLT_INT); + oci_bind_by_name($s, ":bv2", $prefix); + for ($i = 0; $i < 100; ++$i) { + oci_execute($s); + } +} + +// Call dbms_output.get_line() +// Returns an array of DBMS_OUTPUT lines, or false. +function getdbmsoutput_do($c) +{ + $s = oci_parse($c, "begin dbms_output.get_line(:ln, :st); end;"); + oci_bind_by_name($s, ":ln", $ln, 100); + oci_bind_by_name($s, ":st", $st, -1, SQLT_INT); + $res = false; + while (($succ = oci_execute($s)) && !$st) { + $res[] = $ln; // append each line to the array + } + return $res; +} + +function getdbmsoutput_do2($c) +{ + $orignumlines = $numlines = 100; + $s = oci_parse($c, "begin dbms_output.get_lines(:lines, :numlines); end;"); + $r = oci_bind_by_name($s, ":numlines", $numlines); + $res = array(); + while ($numlines >= $orignumlines) { + oci_bind_array_by_name($s, ":lines", $lines, $numlines, 255, SQLT_CHR); + oci_execute($s); + if ($numlines == 0) { + break; + } + $res = array_merge($res, array_slice($lines, 0, $numlines)); + unset($lines); + } + return $res; +} + +function getdbmsoutput_pl($c) +{ + $s = oci_parse($c, "select * from table(mydofetch())"); + oci_execute($s); + $res = false; + while ($row = oci_fetch_array($s, OCI_NUM)) { + $res[] = $row[0]; + } + return $res; +} + +echo "Test 1\n"; + +setserveroutputon($c); // Turn output buffering on + +$s = oci_parse($c, 'call dbmsoutput_proc()'); +oci_execute($s); +var_dump(getdbmsoutput_do($c)); + +echo "Test 2\n"; + +createoutput($c, 'test 2'); +var_dump(getdbmsoutput_do($c)); + +echo "Test 3\n"; + +createoutput($c, 'test 3'); +var_dump(getdbmsoutput_do2($c)); + +echo "Test 4\n"; + +createoutput($c, 'test 4'); +var_dump(getdbmsoutput_pl($c)); + +// Clean up + +$stmtarray = array( + "drop procedure dbmsoutput_proc" +); + +oci8_test_sql_execute($c, $stmtarray); + +?> +===DONE=== +<?php exit(0); ?> +--EXPECTF-- +Test 1 +array(1) { + [0]=> + string(12) "Hello World!" +} +Test 2 +array(100) { + [0]=> + string(52) "0 test 2 Hello, world! Lots and lots and ... of text" + [1]=> + string(52) "1 test 2 Hello, world! Lots and lots and ... of text" + [2]=> + string(52) "2 test 2 Hello, world! Lots and lots and ... of text" + [3]=> + string(52) "3 test 2 Hello, world! Lots and lots and ... of text" + [4]=> + string(52) "4 test 2 Hello, world! Lots and lots and ... of text" + [5]=> + string(52) "5 test 2 Hello, world! Lots and lots and ... of text" + [6]=> + string(52) "6 test 2 Hello, world! Lots and lots and ... of text" + [7]=> + string(52) "7 test 2 Hello, world! Lots and lots and ... of text" + [8]=> + string(52) "8 test 2 Hello, world! Lots and lots and ... of text" + [9]=> + string(52) "9 test 2 Hello, world! Lots and lots and ... of text" + [10]=> + string(53) "10 test 2 Hello, world! Lots and lots and ... of text" + [11]=> + string(53) "11 test 2 Hello, world! Lots and lots and ... of text" + [12]=> + string(53) "12 test 2 Hello, world! Lots and lots and ... of text" + [13]=> + string(53) "13 test 2 Hello, world! Lots and lots and ... of text" + [14]=> + string(53) "14 test 2 Hello, world! Lots and lots and ... of text" + [15]=> + string(53) "15 test 2 Hello, world! Lots and lots and ... of text" + [16]=> + string(53) "16 test 2 Hello, world! Lots and lots and ... of text" + [17]=> + string(53) "17 test 2 Hello, world! Lots and lots and ... of text" + [18]=> + string(53) "18 test 2 Hello, world! Lots and lots and ... of text" + [19]=> + string(53) "19 test 2 Hello, world! Lots and lots and ... of text" + [20]=> + string(53) "20 test 2 Hello, world! Lots and lots and ... of text" + [21]=> + string(53) "21 test 2 Hello, world! Lots and lots and ... of text" + [22]=> + string(53) "22 test 2 Hello, world! Lots and lots and ... of text" + [23]=> + string(53) "23 test 2 Hello, world! Lots and lots and ... of text" + [24]=> + string(53) "24 test 2 Hello, world! Lots and lots and ... of text" + [25]=> + string(53) "25 test 2 Hello, world! Lots and lots and ... of text" + [26]=> + string(53) "26 test 2 Hello, world! Lots and lots and ... of text" + [27]=> + string(53) "27 test 2 Hello, world! Lots and lots and ... of text" + [28]=> + string(53) "28 test 2 Hello, world! Lots and lots and ... of text" + [29]=> + string(53) "29 test 2 Hello, world! Lots and lots and ... of text" + [30]=> + string(53) "30 test 2 Hello, world! Lots and lots and ... of text" + [31]=> + string(53) "31 test 2 Hello, world! Lots and lots and ... of text" + [32]=> + string(53) "32 test 2 Hello, world! Lots and lots and ... of text" + [33]=> + string(53) "33 test 2 Hello, world! Lots and lots and ... of text" + [34]=> + string(53) "34 test 2 Hello, world! Lots and lots and ... of text" + [35]=> + string(53) "35 test 2 Hello, world! Lots and lots and ... of text" + [36]=> + string(53) "36 test 2 Hello, world! Lots and lots and ... of text" + [37]=> + string(53) "37 test 2 Hello, world! Lots and lots and ... of text" + [38]=> + string(53) "38 test 2 Hello, world! Lots and lots and ... of text" + [39]=> + string(53) "39 test 2 Hello, world! Lots and lots and ... of text" + [40]=> + string(53) "40 test 2 Hello, world! Lots and lots and ... of text" + [41]=> + string(53) "41 test 2 Hello, world! Lots and lots and ... of text" + [42]=> + string(53) "42 test 2 Hello, world! Lots and lots and ... of text" + [43]=> + string(53) "43 test 2 Hello, world! Lots and lots and ... of text" + [44]=> + string(53) "44 test 2 Hello, world! Lots and lots and ... of text" + [45]=> + string(53) "45 test 2 Hello, world! Lots and lots and ... of text" + [46]=> + string(53) "46 test 2 Hello, world! Lots and lots and ... of text" + [47]=> + string(53) "47 test 2 Hello, world! Lots and lots and ... of text" + [48]=> + string(53) "48 test 2 Hello, world! Lots and lots and ... of text" + [49]=> + string(53) "49 test 2 Hello, world! Lots and lots and ... of text" + [50]=> + string(53) "50 test 2 Hello, world! Lots and lots and ... of text" + [51]=> + string(53) "51 test 2 Hello, world! Lots and lots and ... of text" + [52]=> + string(53) "52 test 2 Hello, world! Lots and lots and ... of text" + [53]=> + string(53) "53 test 2 Hello, world! Lots and lots and ... of text" + [54]=> + string(53) "54 test 2 Hello, world! Lots and lots and ... of text" + [55]=> + string(53) "55 test 2 Hello, world! Lots and lots and ... of text" + [56]=> + string(53) "56 test 2 Hello, world! Lots and lots and ... of text" + [57]=> + string(53) "57 test 2 Hello, world! Lots and lots and ... of text" + [58]=> + string(53) "58 test 2 Hello, world! Lots and lots and ... of text" + [59]=> + string(53) "59 test 2 Hello, world! Lots and lots and ... of text" + [60]=> + string(53) "60 test 2 Hello, world! Lots and lots and ... of text" + [61]=> + string(53) "61 test 2 Hello, world! Lots and lots and ... of text" + [62]=> + string(53) "62 test 2 Hello, world! Lots and lots and ... of text" + [63]=> + string(53) "63 test 2 Hello, world! Lots and lots and ... of text" + [64]=> + string(53) "64 test 2 Hello, world! Lots and lots and ... of text" + [65]=> + string(53) "65 test 2 Hello, world! Lots and lots and ... of text" + [66]=> + string(53) "66 test 2 Hello, world! Lots and lots and ... of text" + [67]=> + string(53) "67 test 2 Hello, world! Lots and lots and ... of text" + [68]=> + string(53) "68 test 2 Hello, world! Lots and lots and ... of text" + [69]=> + string(53) "69 test 2 Hello, world! Lots and lots and ... of text" + [70]=> + string(53) "70 test 2 Hello, world! Lots and lots and ... of text" + [71]=> + string(53) "71 test 2 Hello, world! Lots and lots and ... of text" + [72]=> + string(53) "72 test 2 Hello, world! Lots and lots and ... of text" + [73]=> + string(53) "73 test 2 Hello, world! Lots and lots and ... of text" + [74]=> + string(53) "74 test 2 Hello, world! Lots and lots and ... of text" + [75]=> + string(53) "75 test 2 Hello, world! Lots and lots and ... of text" + [76]=> + string(53) "76 test 2 Hello, world! Lots and lots and ... of text" + [77]=> + string(53) "77 test 2 Hello, world! Lots and lots and ... of text" + [78]=> + string(53) "78 test 2 Hello, world! Lots and lots and ... of text" + [79]=> + string(53) "79 test 2 Hello, world! Lots and lots and ... of text" + [80]=> + string(53) "80 test 2 Hello, world! Lots and lots and ... of text" + [81]=> + string(53) "81 test 2 Hello, world! Lots and lots and ... of text" + [82]=> + string(53) "82 test 2 Hello, world! Lots and lots and ... of text" + [83]=> + string(53) "83 test 2 Hello, world! Lots and lots and ... of text" + [84]=> + string(53) "84 test 2 Hello, world! Lots and lots and ... of text" + [85]=> + string(53) "85 test 2 Hello, world! Lots and lots and ... of text" + [86]=> + string(53) "86 test 2 Hello, world! Lots and lots and ... of text" + [87]=> + string(53) "87 test 2 Hello, world! Lots and lots and ... of text" + [88]=> + string(53) "88 test 2 Hello, world! Lots and lots and ... of text" + [89]=> + string(53) "89 test 2 Hello, world! Lots and lots and ... of text" + [90]=> + string(53) "90 test 2 Hello, world! Lots and lots and ... of text" + [91]=> + string(53) "91 test 2 Hello, world! Lots and lots and ... of text" + [92]=> + string(53) "92 test 2 Hello, world! Lots and lots and ... of text" + [93]=> + string(53) "93 test 2 Hello, world! Lots and lots and ... of text" + [94]=> + string(53) "94 test 2 Hello, world! Lots and lots and ... of text" + [95]=> + string(53) "95 test 2 Hello, world! Lots and lots and ... of text" + [96]=> + string(53) "96 test 2 Hello, world! Lots and lots and ... of text" + [97]=> + string(53) "97 test 2 Hello, world! Lots and lots and ... of text" + [98]=> + string(53) "98 test 2 Hello, world! Lots and lots and ... of text" + [99]=> + string(53) "99 test 2 Hello, world! Lots and lots and ... of text" +} +Test 3 +array(100) { + [0]=> + string(52) "0 test 3 Hello, world! Lots and lots and ... of text" + [1]=> + string(52) "1 test 3 Hello, world! Lots and lots and ... of text" + [2]=> + string(52) "2 test 3 Hello, world! Lots and lots and ... of text" + [3]=> + string(52) "3 test 3 Hello, world! Lots and lots and ... of text" + [4]=> + string(52) "4 test 3 Hello, world! Lots and lots and ... of text" + [5]=> + string(52) "5 test 3 Hello, world! Lots and lots and ... of text" + [6]=> + string(52) "6 test 3 Hello, world! Lots and lots and ... of text" + [7]=> + string(52) "7 test 3 Hello, world! Lots and lots and ... of text" + [8]=> + string(52) "8 test 3 Hello, world! Lots and lots and ... of text" + [9]=> + string(52) "9 test 3 Hello, world! Lots and lots and ... of text" + [10]=> + string(53) "10 test 3 Hello, world! Lots and lots and ... of text" + [11]=> + string(53) "11 test 3 Hello, world! Lots and lots and ... of text" + [12]=> + string(53) "12 test 3 Hello, world! Lots and lots and ... of text" + [13]=> + string(53) "13 test 3 Hello, world! Lots and lots and ... of text" + [14]=> + string(53) "14 test 3 Hello, world! Lots and lots and ... of text" + [15]=> + string(53) "15 test 3 Hello, world! Lots and lots and ... of text" + [16]=> + string(53) "16 test 3 Hello, world! Lots and lots and ... of text" + [17]=> + string(53) "17 test 3 Hello, world! Lots and lots and ... of text" + [18]=> + string(53) "18 test 3 Hello, world! Lots and lots and ... of text" + [19]=> + string(53) "19 test 3 Hello, world! Lots and lots and ... of text" + [20]=> + string(53) "20 test 3 Hello, world! Lots and lots and ... of text" + [21]=> + string(53) "21 test 3 Hello, world! Lots and lots and ... of text" + [22]=> + string(53) "22 test 3 Hello, world! Lots and lots and ... of text" + [23]=> + string(53) "23 test 3 Hello, world! Lots and lots and ... of text" + [24]=> + string(53) "24 test 3 Hello, world! Lots and lots and ... of text" + [25]=> + string(53) "25 test 3 Hello, world! Lots and lots and ... of text" + [26]=> + string(53) "26 test 3 Hello, world! Lots and lots and ... of text" + [27]=> + string(53) "27 test 3 Hello, world! Lots and lots and ... of text" + [28]=> + string(53) "28 test 3 Hello, world! Lots and lots and ... of text" + [29]=> + string(53) "29 test 3 Hello, world! Lots and lots and ... of text" + [30]=> + string(53) "30 test 3 Hello, world! Lots and lots and ... of text" + [31]=> + string(53) "31 test 3 Hello, world! Lots and lots and ... of text" + [32]=> + string(53) "32 test 3 Hello, world! Lots and lots and ... of text" + [33]=> + string(53) "33 test 3 Hello, world! Lots and lots and ... of text" + [34]=> + string(53) "34 test 3 Hello, world! Lots and lots and ... of text" + [35]=> + string(53) "35 test 3 Hello, world! Lots and lots and ... of text" + [36]=> + string(53) "36 test 3 Hello, world! Lots and lots and ... of text" + [37]=> + string(53) "37 test 3 Hello, world! Lots and lots and ... of text" + [38]=> + string(53) "38 test 3 Hello, world! Lots and lots and ... of text" + [39]=> + string(53) "39 test 3 Hello, world! Lots and lots and ... of text" + [40]=> + string(53) "40 test 3 Hello, world! Lots and lots and ... of text" + [41]=> + string(53) "41 test 3 Hello, world! Lots and lots and ... of text" + [42]=> + string(53) "42 test 3 Hello, world! Lots and lots and ... of text" + [43]=> + string(53) "43 test 3 Hello, world! Lots and lots and ... of text" + [44]=> + string(53) "44 test 3 Hello, world! Lots and lots and ... of text" + [45]=> + string(53) "45 test 3 Hello, world! Lots and lots and ... of text" + [46]=> + string(53) "46 test 3 Hello, world! Lots and lots and ... of text" + [47]=> + string(53) "47 test 3 Hello, world! Lots and lots and ... of text" + [48]=> + string(53) "48 test 3 Hello, world! Lots and lots and ... of text" + [49]=> + string(53) "49 test 3 Hello, world! Lots and lots and ... of text" + [50]=> + string(53) "50 test 3 Hello, world! Lots and lots and ... of text" + [51]=> + string(53) "51 test 3 Hello, world! Lots and lots and ... of text" + [52]=> + string(53) "52 test 3 Hello, world! Lots and lots and ... of text" + [53]=> + string(53) "53 test 3 Hello, world! Lots and lots and ... of text" + [54]=> + string(53) "54 test 3 Hello, world! Lots and lots and ... of text" + [55]=> + string(53) "55 test 3 Hello, world! Lots and lots and ... of text" + [56]=> + string(53) "56 test 3 Hello, world! Lots and lots and ... of text" + [57]=> + string(53) "57 test 3 Hello, world! Lots and lots and ... of text" + [58]=> + string(53) "58 test 3 Hello, world! Lots and lots and ... of text" + [59]=> + string(53) "59 test 3 Hello, world! Lots and lots and ... of text" + [60]=> + string(53) "60 test 3 Hello, world! Lots and lots and ... of text" + [61]=> + string(53) "61 test 3 Hello, world! Lots and lots and ... of text" + [62]=> + string(53) "62 test 3 Hello, world! Lots and lots and ... of text" + [63]=> + string(53) "63 test 3 Hello, world! Lots and lots and ... of text" + [64]=> + string(53) "64 test 3 Hello, world! Lots and lots and ... of text" + [65]=> + string(53) "65 test 3 Hello, world! Lots and lots and ... of text" + [66]=> + string(53) "66 test 3 Hello, world! Lots and lots and ... of text" + [67]=> + string(53) "67 test 3 Hello, world! Lots and lots and ... of text" + [68]=> + string(53) "68 test 3 Hello, world! Lots and lots and ... of text" + [69]=> + string(53) "69 test 3 Hello, world! Lots and lots and ... of text" + [70]=> + string(53) "70 test 3 Hello, world! Lots and lots and ... of text" + [71]=> + string(53) "71 test 3 Hello, world! Lots and lots and ... of text" + [72]=> + string(53) "72 test 3 Hello, world! Lots and lots and ... of text" + [73]=> + string(53) "73 test 3 Hello, world! Lots and lots and ... of text" + [74]=> + string(53) "74 test 3 Hello, world! Lots and lots and ... of text" + [75]=> + string(53) "75 test 3 Hello, world! Lots and lots and ... of text" + [76]=> + string(53) "76 test 3 Hello, world! Lots and lots and ... of text" + [77]=> + string(53) "77 test 3 Hello, world! Lots and lots and ... of text" + [78]=> + string(53) "78 test 3 Hello, world! Lots and lots and ... of text" + [79]=> + string(53) "79 test 3 Hello, world! Lots and lots and ... of text" + [80]=> + string(53) "80 test 3 Hello, world! Lots and lots and ... of text" + [81]=> + string(53) "81 test 3 Hello, world! Lots and lots and ... of text" + [82]=> + string(53) "82 test 3 Hello, world! Lots and lots and ... of text" + [83]=> + string(53) "83 test 3 Hello, world! Lots and lots and ... of text" + [84]=> + string(53) "84 test 3 Hello, world! Lots and lots and ... of text" + [85]=> + string(53) "85 test 3 Hello, world! Lots and lots and ... of text" + [86]=> + string(53) "86 test 3 Hello, world! Lots and lots and ... of text" + [87]=> + string(53) "87 test 3 Hello, world! Lots and lots and ... of text" + [88]=> + string(53) "88 test 3 Hello, world! Lots and lots and ... of text" + [89]=> + string(53) "89 test 3 Hello, world! Lots and lots and ... of text" + [90]=> + string(53) "90 test 3 Hello, world! Lots and lots and ... of text" + [91]=> + string(53) "91 test 3 Hello, world! Lots and lots and ... of text" + [92]=> + string(53) "92 test 3 Hello, world! Lots and lots and ... of text" + [93]=> + string(53) "93 test 3 Hello, world! Lots and lots and ... of text" + [94]=> + string(53) "94 test 3 Hello, world! Lots and lots and ... of text" + [95]=> + string(53) "95 test 3 Hello, world! Lots and lots and ... of text" + [96]=> + string(53) "96 test 3 Hello, world! Lots and lots and ... of text" + [97]=> + string(53) "97 test 3 Hello, world! Lots and lots and ... of text" + [98]=> + string(53) "98 test 3 Hello, world! Lots and lots and ... of text" + [99]=> + string(53) "99 test 3 Hello, world! Lots and lots and ... of text" +} +Test 4 +array(100) { + [0]=> + string(52) "0 test 4 Hello, world! Lots and lots and ... of text" + [1]=> + string(52) "1 test 4 Hello, world! Lots and lots and ... of text" + [2]=> + string(52) "2 test 4 Hello, world! Lots and lots and ... of text" + [3]=> + string(52) "3 test 4 Hello, world! Lots and lots and ... of text" + [4]=> + string(52) "4 test 4 Hello, world! Lots and lots and ... of text" + [5]=> + string(52) "5 test 4 Hello, world! Lots and lots and ... of text" + [6]=> + string(52) "6 test 4 Hello, world! Lots and lots and ... of text" + [7]=> + string(52) "7 test 4 Hello, world! Lots and lots and ... of text" + [8]=> + string(52) "8 test 4 Hello, world! Lots and lots and ... of text" + [9]=> + string(52) "9 test 4 Hello, world! Lots and lots and ... of text" + [10]=> + string(53) "10 test 4 Hello, world! Lots and lots and ... of text" + [11]=> + string(53) "11 test 4 Hello, world! Lots and lots and ... of text" + [12]=> + string(53) "12 test 4 Hello, world! Lots and lots and ... of text" + [13]=> + string(53) "13 test 4 Hello, world! Lots and lots and ... of text" + [14]=> + string(53) "14 test 4 Hello, world! Lots and lots and ... of text" + [15]=> + string(53) "15 test 4 Hello, world! Lots and lots and ... of text" + [16]=> + string(53) "16 test 4 Hello, world! Lots and lots and ... of text" + [17]=> + string(53) "17 test 4 Hello, world! Lots and lots and ... of text" + [18]=> + string(53) "18 test 4 Hello, world! Lots and lots and ... of text" + [19]=> + string(53) "19 test 4 Hello, world! Lots and lots and ... of text" + [20]=> + string(53) "20 test 4 Hello, world! Lots and lots and ... of text" + [21]=> + string(53) "21 test 4 Hello, world! Lots and lots and ... of text" + [22]=> + string(53) "22 test 4 Hello, world! Lots and lots and ... of text" + [23]=> + string(53) "23 test 4 Hello, world! Lots and lots and ... of text" + [24]=> + string(53) "24 test 4 Hello, world! Lots and lots and ... of text" + [25]=> + string(53) "25 test 4 Hello, world! Lots and lots and ... of text" + [26]=> + string(53) "26 test 4 Hello, world! Lots and lots and ... of text" + [27]=> + string(53) "27 test 4 Hello, world! Lots and lots and ... of text" + [28]=> + string(53) "28 test 4 Hello, world! Lots and lots and ... of text" + [29]=> + string(53) "29 test 4 Hello, world! Lots and lots and ... of text" + [30]=> + string(53) "30 test 4 Hello, world! Lots and lots and ... of text" + [31]=> + string(53) "31 test 4 Hello, world! Lots and lots and ... of text" + [32]=> + string(53) "32 test 4 Hello, world! Lots and lots and ... of text" + [33]=> + string(53) "33 test 4 Hello, world! Lots and lots and ... of text" + [34]=> + string(53) "34 test 4 Hello, world! Lots and lots and ... of text" + [35]=> + string(53) "35 test 4 Hello, world! Lots and lots and ... of text" + [36]=> + string(53) "36 test 4 Hello, world! Lots and lots and ... of text" + [37]=> + string(53) "37 test 4 Hello, world! Lots and lots and ... of text" + [38]=> + string(53) "38 test 4 Hello, world! Lots and lots and ... of text" + [39]=> + string(53) "39 test 4 Hello, world! Lots and lots and ... of text" + [40]=> + string(53) "40 test 4 Hello, world! Lots and lots and ... of text" + [41]=> + string(53) "41 test 4 Hello, world! Lots and lots and ... of text" + [42]=> + string(53) "42 test 4 Hello, world! Lots and lots and ... of text" + [43]=> + string(53) "43 test 4 Hello, world! Lots and lots and ... of text" + [44]=> + string(53) "44 test 4 Hello, world! Lots and lots and ... of text" + [45]=> + string(53) "45 test 4 Hello, world! Lots and lots and ... of text" + [46]=> + string(53) "46 test 4 Hello, world! Lots and lots and ... of text" + [47]=> + string(53) "47 test 4 Hello, world! Lots and lots and ... of text" + [48]=> + string(53) "48 test 4 Hello, world! Lots and lots and ... of text" + [49]=> + string(53) "49 test 4 Hello, world! Lots and lots and ... of text" + [50]=> + string(53) "50 test 4 Hello, world! Lots and lots and ... of text" + [51]=> + string(53) "51 test 4 Hello, world! Lots and lots and ... of text" + [52]=> + string(53) "52 test 4 Hello, world! Lots and lots and ... of text" + [53]=> + string(53) "53 test 4 Hello, world! Lots and lots and ... of text" + [54]=> + string(53) "54 test 4 Hello, world! Lots and lots and ... of text" + [55]=> + string(53) "55 test 4 Hello, world! Lots and lots and ... of text" + [56]=> + string(53) "56 test 4 Hello, world! Lots and lots and ... of text" + [57]=> + string(53) "57 test 4 Hello, world! Lots and lots and ... of text" + [58]=> + string(53) "58 test 4 Hello, world! Lots and lots and ... of text" + [59]=> + string(53) "59 test 4 Hello, world! Lots and lots and ... of text" + [60]=> + string(53) "60 test 4 Hello, world! Lots and lots and ... of text" + [61]=> + string(53) "61 test 4 Hello, world! Lots and lots and ... of text" + [62]=> + string(53) "62 test 4 Hello, world! Lots and lots and ... of text" + [63]=> + string(53) "63 test 4 Hello, world! Lots and lots and ... of text" + [64]=> + string(53) "64 test 4 Hello, world! Lots and lots and ... of text" + [65]=> + string(53) "65 test 4 Hello, world! Lots and lots and ... of text" + [66]=> + string(53) "66 test 4 Hello, world! Lots and lots and ... of text" + [67]=> + string(53) "67 test 4 Hello, world! Lots and lots and ... of text" + [68]=> + string(53) "68 test 4 Hello, world! Lots and lots and ... of text" + [69]=> + string(53) "69 test 4 Hello, world! Lots and lots and ... of text" + [70]=> + string(53) "70 test 4 Hello, world! Lots and lots and ... of text" + [71]=> + string(53) "71 test 4 Hello, world! Lots and lots and ... of text" + [72]=> + string(53) "72 test 4 Hello, world! Lots and lots and ... of text" + [73]=> + string(53) "73 test 4 Hello, world! Lots and lots and ... of text" + [74]=> + string(53) "74 test 4 Hello, world! Lots and lots and ... of text" + [75]=> + string(53) "75 test 4 Hello, world! Lots and lots and ... of text" + [76]=> + string(53) "76 test 4 Hello, world! Lots and lots and ... of text" + [77]=> + string(53) "77 test 4 Hello, world! Lots and lots and ... of text" + [78]=> + string(53) "78 test 4 Hello, world! Lots and lots and ... of text" + [79]=> + string(53) "79 test 4 Hello, world! Lots and lots and ... of text" + [80]=> + string(53) "80 test 4 Hello, world! Lots and lots and ... of text" + [81]=> + string(53) "81 test 4 Hello, world! Lots and lots and ... of text" + [82]=> + string(53) "82 test 4 Hello, world! Lots and lots and ... of text" + [83]=> + string(53) "83 test 4 Hello, world! Lots and lots and ... of text" + [84]=> + string(53) "84 test 4 Hello, world! Lots and lots and ... of text" + [85]=> + string(53) "85 test 4 Hello, world! Lots and lots and ... of text" + [86]=> + string(53) "86 test 4 Hello, world! Lots and lots and ... of text" + [87]=> + string(53) "87 test 4 Hello, world! Lots and lots and ... of text" + [88]=> + string(53) "88 test 4 Hello, world! Lots and lots and ... of text" + [89]=> + string(53) "89 test 4 Hello, world! Lots and lots and ... of text" + [90]=> + string(53) "90 test 4 Hello, world! Lots and lots and ... of text" + [91]=> + string(53) "91 test 4 Hello, world! Lots and lots and ... of text" + [92]=> + string(53) "92 test 4 Hello, world! Lots and lots and ... of text" + [93]=> + string(53) "93 test 4 Hello, world! Lots and lots and ... of text" + [94]=> + string(53) "94 test 4 Hello, world! Lots and lots and ... of text" + [95]=> + string(53) "95 test 4 Hello, world! Lots and lots and ... of text" + [96]=> + string(53) "96 test 4 Hello, world! Lots and lots and ... of text" + [97]=> + string(53) "97 test 4 Hello, world! Lots and lots and ... of text" + [98]=> + string(53) "98 test 4 Hello, world! Lots and lots and ... of text" + [99]=> + string(53) "99 test 4 Hello, world! Lots and lots and ... of text" +} +===DONE=== diff --git a/ext/oci8/tests/default_prefetch.phpt b/ext/oci8/tests/default_prefetch.phpt index 47191c858..cc70c6ecb 100644 --- a/ext/oci8/tests/default_prefetch.phpt +++ b/ext/oci8/tests/default_prefetch.phpt @@ -19,23 +19,7 @@ $stmtarray = array( "insert into default_prefetch_tab (id, value) values (1,1)", ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - $r = @oci_execute($s); - if (!$r) { - $m = oci_error($s); - if (!in_array($m['code'], array( // ignore expected errors - 942 // table or view does not exist - ))) { - echo $stmt . PHP_EOL . $m['message'] . PHP_EOL; - } - } -} - -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); // Run Test @@ -59,11 +43,7 @@ $stmtarray = array( "drop table default_prefetch_tab" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} - +oci8_test_sql_execute($c, $stmtarray); echo "Done\n"; ?> diff --git a/ext/oci8/tests/default_prefetch0.phpt b/ext/oci8/tests/default_prefetch0.phpt new file mode 100644 index 000000000..cc70c6ecb --- /dev/null +++ b/ext/oci8/tests/default_prefetch0.phpt @@ -0,0 +1,53 @@ +--TEST-- +oci8.default_prefetch ini option +--SKIPIF-- +<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +--INI-- +oci8.default_prefetch=20 +--FILE-- +<?php + +require(dirname(__FILE__)."/connect.inc"); + +// Initialize + +$stmtarray = array( + "drop table default_prefetch_tab", + "create table default_prefetch_tab (id number, value number)", + "insert into default_prefetch_tab (id, value) values (1,1)", + "insert into default_prefetch_tab (id, value) values (1,1)", + "insert into default_prefetch_tab (id, value) values (1,1)", +); + +oci8_test_sql_execute($c, $stmtarray); + +// Run Test + +$select_sql = "select * from default_prefetch_tab"; + +if (!($s = oci_parse($c, $select_sql))) { + die("oci_parse(select) failed!\n"); +} + +if (!oci_execute($s)) { + die("oci_execute(select) failed!\n"); +} + +var_dump(oci_fetch($s)); + +var_dump(oci_num_rows($s)); + +// Cleanup + +$stmtarray = array( + "drop table default_prefetch_tab" +); + +oci8_test_sql_execute($c, $stmtarray); + +echo "Done\n"; +?> +--EXPECT-- +bool(true) +int(1) +Done diff --git a/ext/oci8/tests/default_prefetch1.phpt b/ext/oci8/tests/default_prefetch1.phpt index bcd66fa38..bc7882936 100644 --- a/ext/oci8/tests/default_prefetch1.phpt +++ b/ext/oci8/tests/default_prefetch1.phpt @@ -19,23 +19,7 @@ $stmtarray = array( "insert into default_prefetch1_tab (id, value) values (1,1)", ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - $r = @oci_execute($s); - if (!$r) { - $m = oci_error($s); - if (!in_array($m['code'], array( // ignore expected errors - 942 // table or view does not exist - ))) { - echo $stmt . PHP_EOL . $m['message'] . PHP_EOL; - } - } -} - -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); // Run Test @@ -59,10 +43,7 @@ $stmtarray = array( "drop table default_prefetch1_tab" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); echo "Done\n"; ?> diff --git a/ext/oci8/tests/default_prefetch2.phpt b/ext/oci8/tests/default_prefetch2.phpt index 7b3f29f29..d8a76dbb0 100644 --- a/ext/oci8/tests/default_prefetch2.phpt +++ b/ext/oci8/tests/default_prefetch2.phpt @@ -19,23 +19,7 @@ $stmtarray = array( "insert into default_prefetch2_tab (id, value) values (1,1)", ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - $r = @oci_execute($s); - if (!$r) { - $m = oci_error($s); - if (!in_array($m['code'], array( // ignore expected errors - 942 // table or view does not exist - ))) { - echo $stmt . PHP_EOL . $m['message'] . PHP_EOL; - } - } -} - -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); // Run Test @@ -60,10 +44,7 @@ $stmtarray = array( "drop table default_prefetch2_tab" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); echo "Done\n"; ?> diff --git a/ext/oci8/tests/define.phpt b/ext/oci8/tests/define.phpt index d99bc7e1a..c6ce7bd9b 100644 --- a/ext/oci8/tests/define.phpt +++ b/ext/oci8/tests/define.phpt @@ -15,18 +15,7 @@ $stmtarray = array( "insert into define_tab (string) values ('some')", ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - $r = @oci_execute($s); - if (!$r) { - $m = oci_error($s); - if (!in_array($m['code'], array( // ignore expected errors - 942 // table or view does not exist - ))) { - echo $stmt . PHP_EOL . $m['message'] . PHP_EOL; - } - } -} +oci8_test_sql_execute($c, $stmtarray); // Run test @@ -49,10 +38,7 @@ $stmtarray = array( "drop table define_tab" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); echo "Done\n"; diff --git a/ext/oci8/tests/define0.phpt b/ext/oci8/tests/define0.phpt new file mode 100644 index 000000000..f2f06e315 --- /dev/null +++ b/ext/oci8/tests/define0.phpt @@ -0,0 +1,61 @@ +--TEST-- +oci_define_by_name() +--SKIPIF-- +<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +--FILE-- +<?php + +require(dirname(__FILE__)."/connect.inc"); + +// Initialize + +$stmtarray = array( + "drop table define0_tab", + "create table define0_tab (string varchar(10))", + "insert into define0_tab (string) values ('some')", +); + +oci8_test_sql_execute($c, $stmtarray); + +// Run test + +$stmt = oci_parse($c, "select string from define0_tab"); + +/* the define MUST be done BEFORE ociexecute! */ + +echo "Test 1\n"; + +$string = ''; +oci_define_by_name($stmt, "STRING", $string, 20); +oci_execute($stmt); +while (oci_fetch($stmt)) { + var_dump($string); +} + +echo "Test 2\n"; + +$string = ''; +$s2 = oci_parse($c, 'select string from define0_tab'); +oci_define_by_name($s2, 'STRING', $string); +oci_execute($s2); +while (oci_fetch($s2)) { + var_dump($string); +} + +// Cleanup + +$stmtarray = array( + "drop table define0_tab" +); + +oci8_test_sql_execute($c, $stmtarray); + +?> +===DONE=== +<?php exit(0); ?> +--EXPECTF-- +Test 1 +string(%d) "some" +Test 2 +string(%d) "some" +===DONE=== diff --git a/ext/oci8/tests/define1.phpt b/ext/oci8/tests/define1.phpt index 341bc9ed8..6e4b74e3b 100644 --- a/ext/oci8/tests/define1.phpt +++ b/ext/oci8/tests/define1.phpt @@ -15,18 +15,7 @@ $stmtarray = array( "insert into define1_tab (string) values ('some')", ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - $r = @oci_execute($s); - if (!$r) { - $m = oci_error($s); - if (!in_array($m['code'], array( // ignore expected errors - 942 // table or view does not exist - ))) { - echo $stmt . PHP_EOL . $m['message'] . PHP_EOL; - } - } -} +oci8_test_sql_execute($c, $stmtarray); // Run test @@ -52,10 +41,7 @@ $stmtarray = array( "drop table define1_tab" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); echo "Done\n"; diff --git a/ext/oci8/tests/define2.phpt b/ext/oci8/tests/define2.phpt index 46e11bf88..c53bebd2e 100644 --- a/ext/oci8/tests/define2.phpt +++ b/ext/oci8/tests/define2.phpt @@ -1,7 +1,10 @@ --TEST-- Test oci_define_by_name types --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php @@ -12,10 +15,7 @@ $stmtarray = array( "create table phptestrawtable( id number(10), fileimage raw(1000))" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - @oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); $stmt = oci_parse ($c, "insert into phptestrawtable (id, fileimage) values (:id, :fileimage)"); $i=1; @@ -68,8 +68,13 @@ while (oci_fetch($stmt)) { echo "file md5:" . md5($fi) . "\n"; } -$stmt = oci_parse($c, "drop table phptestrawtable"); -oci_execute($stmt); +// Cleanup + +$stmtarray = array( + "drop table phptestrawtable" +); + +oci8_test_sql_execute($c, $stmtarray); echo "Done\n"; ?> diff --git a/ext/oci8/tests/define3.phpt b/ext/oci8/tests/define3.phpt index 892e8e380..77714a92f 100644 --- a/ext/oci8/tests/define3.phpt +++ b/ext/oci8/tests/define3.phpt @@ -1,7 +1,10 @@ --TEST-- Test oci_define_by_name() LOB descriptor --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php @@ -12,10 +15,7 @@ $stmtarray = array( "create table phpdefblobtable (id number(10), fileimage blob)" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - @oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); // Load data $stmt = oci_parse ($c, "insert into phpdefblobtable (id, fileimage) values (:id, empty_blob()) returning fileimage into :fileimage"); @@ -75,8 +75,11 @@ while (oci_fetch($stmt)) { echo "file md5:" . md5($fid->load()) . "\n"; } -$stmt = oci_parse($c, "drop table phpdefblobtable"); -oci_execute($stmt); +$stmtarray = array( + "drop table phpdefblobtable" +); + +oci8_test_sql_execute($c, $stmtarray); echo "Done\n"; diff --git a/ext/oci8/tests/define4.phpt b/ext/oci8/tests/define4.phpt index 8d83f73ac..266fd7edd 100644 --- a/ext/oci8/tests/define4.phpt +++ b/ext/oci8/tests/define4.phpt @@ -15,18 +15,7 @@ $stmtarray = array( "insert into define4_tab (value, string) values (1234, 'some')", ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - $r = @oci_execute($s); - if (!$r) { - $m = oci_error($s); - if (!in_array($m['code'], array( // ignore expected errors - 942 // table or view does not exist - ))) { - echo $stmt . PHP_EOL . $m['message'] . PHP_EOL; - } - } -} +oci8_test_sql_execute($c, $stmtarray); // Run test @@ -60,10 +49,7 @@ $stmtarray = array( "drop table define4_tab" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); echo "Done\n"; diff --git a/ext/oci8/tests/define5.phpt b/ext/oci8/tests/define5.phpt index 63541ce9d..68fa01d09 100644 --- a/ext/oci8/tests/define5.phpt +++ b/ext/oci8/tests/define5.phpt @@ -16,18 +16,7 @@ $stmtarray = array( "insert into define5_tab (id, string) values (2, 'thing')", ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - $r = @oci_execute($s); - if (!$r) { - $m = oci_error($s); - if (!in_array($m['code'], array( // ignore expected errors - 942 // table or view does not exist - ))) { - echo $stmt . PHP_EOL . $m['message'] . PHP_EOL; - } - } -} +oci8_test_sql_execute($c, $stmtarray); // Run test @@ -63,10 +52,7 @@ $stmtarray = array( "drop table define5_tab" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); echo "Done\n"; diff --git a/ext/oci8/tests/define6.phpt b/ext/oci8/tests/define6.phpt new file mode 100644 index 000000000..50e23ecf4 --- /dev/null +++ b/ext/oci8/tests/define6.phpt @@ -0,0 +1,138 @@ +--TEST-- +oci_define_by_name tests with REF CURSORs +--SKIPIF-- +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> +--FILE-- +<?php + +require(dirname(__FILE__).'/connect.inc'); + +// Initialization + +$stmtarray = array( + "drop table define6_tab", + "create table define6_tab (id number)", + "insert into define6_tab values (1)" +); + +oci8_test_sql_execute($c, $stmtarray); + +// Run Test + +$sql = +"DECLARE + TYPE curtype IS REF CURSOR; + cursor_var curtype; +BEGIN + OPEN cursor_var FOR SELECT id FROM define6_tab; + :curs := cursor_var; +END;"; + +echo "Test 1 - define last\n"; + +$s1 = oci_parse($c, $sql); +$cursor1 = oci_new_cursor($c); +oci_bind_by_name($s1, ":curs", $cursor1, -1, OCI_B_CURSOR); +oci_execute($s1); +oci_execute($cursor1); +oci_define_by_name($cursor1, 'ID', $id1); +while (oci_fetch_row($cursor1)) { + var_dump($id1); +} + + +echo "Test 2 - define last with preset var\n"; + +$s2 = oci_parse($c, $sql); +$cursor2 = oci_new_cursor($c); +oci_bind_by_name($s2, ":curs", $cursor2, -1, OCI_B_CURSOR); +oci_execute($s2); +oci_execute($cursor2); +$id2 = ''; +oci_define_by_name($cursor2, 'ID', $id2); +while (oci_fetch_row($cursor2)) { + var_dump($id2); +} + + +echo "Test 3 - define before cursor execute\n"; + +$s3 = oci_parse($c, $sql); +$cursor3 = oci_new_cursor($c); +oci_bind_by_name($s3, ":curs", $cursor3, -1, OCI_B_CURSOR); +oci_execute($s3); +oci_define_by_name($cursor3, 'ID', $id3); +oci_execute($cursor3); +while (oci_fetch_row($cursor3)) { + var_dump($id3); +} + + +echo "Test 4 - define before top level execute\n"; + +$s4 = oci_parse($c, $sql); +$cursor4 = oci_new_cursor($c); +oci_bind_by_name($s4, ":curs", $cursor4, -1, OCI_B_CURSOR); +oci_define_by_name($cursor4, 'ID', $id4); +oci_execute($s4); +oci_execute($cursor4); +while (oci_fetch_row($cursor4)) { + var_dump($id4); +} + + +echo "Test 5 - define before bind\n"; + +$s5 = oci_parse($c, $sql); +$cursor5 = oci_new_cursor($c); +oci_define_by_name($cursor5, 'ID', $id5); +oci_bind_by_name($s5, ":curs", $cursor5, -1, OCI_B_CURSOR); +oci_execute($s5); +oci_execute($cursor5); +while (oci_fetch_row($cursor5)) { + var_dump($id5); +} + + +echo "Test 6 - fetch on wrong handle\n"; + +$s6 = oci_parse($c, $sql); +$cursor6 = oci_new_cursor($c); +oci_define_by_name($cursor6, 'ID', $id6); +oci_bind_by_name($s6, ":curs", $cursor6, -1, OCI_B_CURSOR); +oci_execute($s6); +oci_execute($cursor6); +while (oci_fetch_row($s6)) { + var_dump($id6); +} + + +// Clean up + +$stmtarray = array( + "drop table define6_tab" +); + +oci8_test_sql_execute($c, $stmtarray); + +?> +===DONE=== +<?php exit(0); ?> +--EXPECTF-- +Test 1 - define last +NULL +Test 2 - define last with preset var +string(0) "" +Test 3 - define before cursor execute +string(1) "1" +Test 4 - define before top level execute +string(1) "1" +Test 5 - define before bind +string(1) "1" +Test 6 - fetch on wrong handle + +Warning: oci_fetch_row(): ORA-24374: %s in %sdefine6.php on line %d +===DONE=== diff --git a/ext/oci8/tests/define_old.phpt b/ext/oci8/tests/define_old.phpt index 618f9d5f5..f65e6b808 100644 --- a/ext/oci8/tests/define_old.phpt +++ b/ext/oci8/tests/define_old.phpt @@ -15,18 +15,7 @@ $stmtarray = array( "insert into define_old_tab (string) values ('some')", ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - $r = @oci_execute($s); - if (!$r) { - $m = oci_error($s); - if (!in_array($m['code'], array( // ignore expected errors - 942 // table or view does not exist - ))) { - echo $stmt . PHP_EOL . $m['message'] . PHP_EOL; - } - } -} +oci8_test_sql_execute($c, $stmtarray); // Run test @@ -49,11 +38,7 @@ $stmtarray = array( "drop table define_old_tab" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} - +oci8_test_sql_execute($c, $stmtarray); echo "Done\n"; diff --git a/ext/oci8/tests/descriptors.phpt b/ext/oci8/tests/descriptors.phpt index 8be4f3a06..9193fddde 100644 --- a/ext/oci8/tests/descriptors.phpt +++ b/ext/oci8/tests/descriptors.phpt @@ -1,7 +1,10 @@ --TEST-- commit connection after destroying the descriptor --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/details.inc b/ext/oci8/tests/details.inc index 922b8c4f4..4c07e636e 100644 --- a/ext/oci8/tests/details.inc +++ b/ext/oci8/tests/details.inc @@ -3,9 +3,6 @@ /* * Please change $user, $password and $dbase to match your configuration. * - * Set $oracle_on_localhost to TRUE if the Oracle Database is - * installed on your localhost. - * * Set $test_drcp to TRUE if you want to run the Oracle Database * Resident Connection Pooling (DRCP) tests. For these tests to run * successfully, you need a server and client which is Oracle 11g or @@ -14,50 +11,76 @@ * string like hostname:port/service_name:POOLED * * Set $stress_test to TRUE if you want to run some longer/slower/more - * memory intensive tests. + * memory intensive tests. External configuration such as increasing + * the timeout of run-tests.php may also be needed. */ if (file_exists(dirname(__FILE__)."/details_local.inc")) { - include(dirname(__FILE__)."/details_local.inc"); // this file is not part of the source distribution; make it your own local variant of details.inc + include(dirname(__FILE__)."/details_local.inc"); // this file is not part of the source distribution; make it your own local variant of details.inc } else { - if (false !== getenv('PHP_OCI8_TEST_DB')) { - $user = getenv('PHP_OCI8_TEST_USER'); // Database username for tests - $password = getenv('PHP_OCI8_TEST_PASS'); // Password for $user - $dbase = getenv('PHP_OCI8_TEST_DB'); // Database connection string - $test_drcp = getenv('PHP_OCI8_TEST_DRCP'); - if (false !== $test_drcp && 0 == strcasecmp($test_drcp,'TRUE')) { - $test_drcp = TRUE; - } else { - $test_drcp = FALSE; - } - $oracle_on_localhost = getenv('PHP_OCI8_TEST_DB_ON_LOCALHOST'); - if (false !== $oracle_on_localhost && 0 == strcasecmp($oracle_on_localhost,'TRUE')) { - $oracle_on_localhost = TRUE; - } else { - $oracle_on_localhost = FALSE; - } - $stress_test = getenv('PHP_OCI8_STRESS_TEST'); - if (false !== $stress_test && 0 == strcasecmp($stress_test,'TRUE')) { - $stress_test = TRUE; - } else { - $stress_test = FALSE; - } - } else { - $user = "system"; - $password = "oracle"; - $dbase = "localhost/XE"; - $oracle_on_localhost = TRUE; - $test_drcp = FALSE; - $stress_test = FALSE; - } - - /* - * Common object names for scripts to use - */ - - $table_name = "tb".substr(str_replace(Array(".", "-"), "_", php_uname("n")), 0, 5); - $type_name = strtoupper("tp".substr(str_replace(Array(".", "-"), "_", php_uname("n")), 0, 5)); - $schema = ''; + if (false !== getenv('PHP_OCI8_TEST_DB')) { + $user = getenv('PHP_OCI8_TEST_USER'); // Database username for tests + $password = getenv('PHP_OCI8_TEST_PASS'); // Password for $user + $dbase = getenv('PHP_OCI8_TEST_DB'); // Database connection string + $test_drcp = getenv('PHP_OCI8_TEST_DRCP'); + $stress_test = getenv('PHP_OCI8_STRESS_TEST'); + if (false !== $test_drcp && 0 == strcasecmp($test_drcp,'TRUE')) { + $test_drcp = TRUE; + } else { + $test_drcp = FALSE; + } + if (false !== $stress_test && 0 == strcasecmp($stress_test,'TRUE')) { + $stress_test = TRUE; + } else { + $stress_test = FALSE; + } + } else { + $user = "system"; + $password = "oracle"; + $dbase = "localhost/XE"; + $test_drcp = FALSE; + $stress_test = FALSE; + } + + /* + * Common object names for scripts to use + */ + + $table_name = "tb".substr(str_replace(Array(".", "-"), "_", php_uname("n")), 0, 5); + $type_name = strtoupper("tp".substr(str_replace(Array(".", "-"), "_", php_uname("n")), 0, 5)); + $schema = ''; +} + + +/* + * Used for creating/dropping schema objects used by a test + */ + +function oci8_test_sql_execute($c, $stmtarray) +{ + foreach ($stmtarray as $stmt) { + $s = oci_parse($c, $stmt); + if (!$s) { + $m = oci_error($c); + echo $stmt . PHP_EOL . $m['message'] . PHP_EOL; + } + else { + $r = @oci_execute($s); + if (!$r) { + $m = oci_error($s); + if (!in_array($m['code'], array( // ignore expected errors + 942 // table or view does not exist + , 1918 // user does not exist + , 2024 // database link not found + , 2289 // sequence does not exist + , 4080 // trigger does not exist + , 38802 // edition does not exist + ))) { + echo $stmt . PHP_EOL . $m['message'] . PHP_EOL; + } + } + } + } } ?> diff --git a/ext/oci8/tests/drcp_connect1.phpt b/ext/oci8/tests/drcp_connect1.phpt index bf619a4ef..25395dc18 100644 --- a/ext/oci8/tests/drcp_connect1.phpt +++ b/ext/oci8/tests/drcp_connect1.phpt @@ -1,7 +1,10 @@ --TEST-- DRCP: oci_connect() --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs (Calling PL/SQL from SQL is not supported in TimesTen) +require(dirname(__FILE__).'/skipif.inc'); +?> --INI-- oci8.connection_class=test oci8.old_oci_close_semantics=0 @@ -16,10 +19,12 @@ require dirname(__FILE__)."/drcp_functions.inc"; // To verify this, we change the value of a PL/SQL package variable in one // session and query for this through another connection +echo "Test 1a\n"; var_dump($conn1 = oci_connect($user,$password,$dbase)); // Create the package drcp_create_package($conn1); - + +echo "Test 1b\n"; // OCI_CONNECT echo " This is with OCI_CONNECT.....\n"; drcp_select_packagevar($conn1); // Returns 0 @@ -27,12 +32,14 @@ drcp_set_packagevar($conn1,1000); oci_close($conn1); echo " Connection conn1 closed....\n"; +echo "Test 2\n"; // Second connection should return 0 for the package variable. var_dump($conn2 = oci_connect($user,$password,$dbase)); echo " Select with connection 2 \n"; drcp_select_packagevar($conn2); // Returns 0 drcp_set_packagevar($conn2,100); +echo "Test 3\n"; // Third connection. There is no oci_close() for conn2 hence this should // return the value set by conn2. var_dump($conn3 = oci_connect($user,$password,$dbase)); @@ -43,8 +50,9 @@ drcp_select_packagevar($conn3); // Returns 100 oci_close($conn2); oci_close($conn3); +echo "Test 4\n"; // OCI_PCONNECT -echo "\n This is with oci_pconnect().....\n"; +echo " This is with oci_pconnect().....\n"; var_dump($pconn1 = oci_pconnect($user,$password,$dbase)); drcp_set_packagevar($pconn1,1000); oci_close($pconn1); @@ -53,6 +61,7 @@ echo " Connection pconn1 closed....\n"; // Second connection with oci_pconnect should return the same session hence the // value returned is what is set by pconn1 +echo "Test 5\n"; var_dump($pconn2 = oci_pconnect($user,$password,$dbase)); echo " Select with persistent connection 2 \n"; drcp_select_packagevar($pconn2); // Returns 1000 @@ -62,23 +71,28 @@ echo "Done\n"; ?> --EXPECTF-- +Test 1a resource(%d) of type (oci8 connection) +Test 1b This is with OCI_CONNECT..... The value of the package variable is 0 Package variable value set to 1000 Connection conn1 closed.... +Test 2 resource(%d) of type (oci8 connection) Select with connection 2 The value of the package variable is 0 Package variable value set to 100 +Test 3 resource(%d) of type (oci8 connection) Select with connection 3 The value of the package variable is 100 - +Test 4 This is with oci_pconnect()..... resource(%d) of type (oci8 persistent connection) Package variable value set to 1000 Connection pconn1 closed.... +Test 5 resource(%d) of type (oci8 persistent connection) Select with persistent connection 2 The value of the package variable is 1000 diff --git a/ext/oci8/tests/drcp_functions.inc b/ext/oci8/tests/drcp_functions.inc index 26adb21f3..f6b24446c 100644 --- a/ext/oci8/tests/drcp_functions.inc +++ b/ext/oci8/tests/drcp_functions.inc @@ -14,7 +14,7 @@ function drcp_create_table($conn) $dept_values = array("ACCOUNTS","HR","HR","ADMIN","ACCOUNTS","HR", "ACCOUNTS","HR","ACCOUNTS"); for($i=0; $i<8; $i++) { - $insert = "INSERT INTO DRCPTEST VALUES('".$id_values[$i]."','". $name_values[$i]."','".$dept_values[$i]."')"; + $insert = "INSERT INTO DRCPTEST VALUES(".$id_values[$i].",'". $name_values[$i]."','".$dept_values[$i]."')"; $s = oci_parse($conn, $insert); oci_execute($s); } diff --git a/ext/oci8/tests/drcp_privileged.phpt b/ext/oci8/tests/drcp_privileged.phpt index 9af20625e..45b5ee4bd 100644 --- a/ext/oci8/tests/drcp_privileged.phpt +++ b/ext/oci8/tests/drcp_privileged.phpt @@ -5,7 +5,10 @@ DRCP: privileged connect if (!extension_loaded('oci8')) die("skip no oci8 extension"); if (strcasecmp($user, "system") && strcasecmp($user, "sys")) die("skip needs to be run as a DBA user"); require(dirname(__FILE__)."/details.inc"); -if (empty($oracle_on_localhost)) die("skip this test is unlikely to work with remote Oracle - unless an Oracle password file has been created"); +if (preg_match('/Compile-time ORACLE_HOME/', $phpinfo) !== 1) { + // Assume building PHP with an ORACLE_HOME means the tested DB is on the same machine as PHP + die("skip this test is unlikely to work with remote Oracle - unless an Oracle password file has been created"); +} ?> --INI-- oci8.privileged_connect=1 @@ -26,22 +29,22 @@ echo "Done\n"; ?> --EXPECTF-- -Warning: oci_connect(): ORA-01031: insufficient privileges in %s on line %d +Warning: oci_connect(): ORA-01031: %s in %s on line %d bool(false) -Warning: oci_connect(): ORA-01031: insufficient privileges in %s on line %d +Warning: oci_connect(): ORA-01031: %s in %s on line %d bool(false) -Warning: oci_new_connect(): ORA-01031: insufficient privileges in %s on line %d +Warning: oci_new_connect(): ORA-01031: %s in %s on line %d bool(false) -Warning: oci_new_connect(): ORA-01031: insufficient privileges in %s on line %d +Warning: oci_new_connect(): ORA-01031: %s in %s on line %d bool(false) -Warning: oci_pconnect(): ORA-01031: insufficient privileges in %s on line %d +Warning: oci_pconnect(): ORA-01031: %s in %s on line %d bool(false) -Warning: oci_pconnect(): ORA-01031: insufficient privileges in %s on line %d +Warning: oci_pconnect(): ORA-01031: %s in %s on line %d bool(false) Done diff --git a/ext/oci8/tests/driver_name.phpt b/ext/oci8/tests/driver_name.phpt index 187d7e186..bf86e66e7 100644 --- a/ext/oci8/tests/driver_name.phpt +++ b/ext/oci8/tests/driver_name.phpt @@ -7,20 +7,10 @@ require(dirname(__FILE__)."/connect.inc"); if (strcasecmp($user, "system") && strcasecmp($user, "sys")) die("skip needs to be run as a DBA user"); if ($test_drcp) die("skip as Output might vary with DRCP"); -$sv = oci_server_version($c); -$sv = preg_match('/Release (11.2|12)/', $sv, $matches); - -if ($sv == 1) { - ob_start(); - phpinfo(INFO_MODULES); - $phpinfo = ob_get_clean(); - $iv = preg_match('/Oracle .*Version => (11.2|12)/', $phpinfo); - if ($iv != 1) { - die ("skip test expected to work only with Oracle 11g or greater version of client"); - } -} -else { - die ("skip test expected to work only with Oracle 11g or greater version of server"); +if (preg_match('/Release (11\.2|12)/', oci_server_version($c), $matches) !== 1) { + die("skip expected output only valid when using Oracle 11gR2 or greater databases"); +} else if (preg_match('/^(11\.2|12\.)/', oci_client_version()) != 1) { + die("skip test expected to work only with Oracle 11g or greater version of client"); } ?> diff --git a/ext/oci8/tests/dupcolnames.phpt b/ext/oci8/tests/dupcolnames.phpt new file mode 100644 index 000000000..bf0749717 --- /dev/null +++ b/ext/oci8/tests/dupcolnames.phpt @@ -0,0 +1,102 @@ +--TEST-- +SELECT tests with duplicate column anmes +--SKIPIF-- +<?php if (!extension_loaded('oci8')) die ("skip no oci8 extension"); ?> +--FILE-- +<?php + +require(dirname(__FILE__).'/connect.inc'); + +// Initialization + +$stmtarray = array( + "drop table dupcolnames_tab1", + "drop table dupcolnames_tab2", + + "create table dupcolnames_tab1 (c1 number, dupnamecol varchar2(20))", + "create table dupcolnames_tab2 (c2 number, dupnamecol varchar2(20))", + + "insert into dupcolnames_tab1 (c1, dupnamecol) values (1, 'chris')", + "insert into dupcolnames_tab2 (c2, dupnamecol) values (2, 'jones')", +); + +oci8_test_sql_execute($c, $stmtarray); + +// Run Test + +echo "Test 1 - OCI_ASSOC\n"; +$s = oci_parse($c, "select * from dupcolnames_tab1, dupcolnames_tab2"); +oci_execute($s); +while (($r = oci_fetch_array($s, OCI_ASSOC)) != false) { + var_dump($r); +} + + +echo "\nTest 2 - OCI_NUM\n"; +$s = oci_parse($c, "select * from dupcolnames_tab1, dupcolnames_tab2"); +oci_execute($s); +while (($r = oci_fetch_array($s, OCI_NUM)) != false) { + var_dump($r); +} + + +echo "\nTest 3 - OCI_ASSOC+OCI_NUM\n"; +$s = oci_parse($c, "select * from dupcolnames_tab1, dupcolnames_tab2"); +oci_execute($s); +while (($r = oci_fetch_array($s, OCI_ASSOC+OCI_NUM)) != false) { + var_dump($r); +} + +// Clean up + +$stmtarray = array( + "drop table dupcolnames_tab1", + "drop table dupcolnames_tab2", +); + +oci8_test_sql_execute($c, $stmtarray); + +?> +===DONE=== +<?php exit(0); ?> +--EXPECTF-- +Test 1 - OCI_ASSOC +array(3) { + ["C1"]=> + string(1) "1" + ["DUPNAMECOL"]=> + string(5) "jones" + ["C2"]=> + string(1) "2" +} + +Test 2 - OCI_NUM +array(4) { + [0]=> + string(1) "1" + [1]=> + string(5) "chris" + [2]=> + string(1) "2" + [3]=> + string(5) "jones" +} + +Test 3 - OCI_ASSOC+OCI_NUM +array(7) { + [0]=> + string(1) "1" + ["C1"]=> + string(1) "1" + [1]=> + string(5) "chris" + ["DUPNAMECOL"]=> + string(5) "jones" + [2]=> + string(1) "2" + ["C2"]=> + string(1) "2" + [3]=> + string(5) "jones" +} +===DONE=== diff --git a/ext/oci8/tests/edition_1.phpt b/ext/oci8/tests/edition_1.phpt index 9a4b0f3b6..b9c8fd817 100644 --- a/ext/oci8/tests/edition_1.phpt +++ b/ext/oci8/tests/edition_1.phpt @@ -4,26 +4,17 @@ Basic test for setting Oracle 11gR2 "edition" attribute <?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); require(dirname(__FILE__)."/connect.inc"); -if (strcasecmp($user, "system") && strcasecmp($user, "sys")) +if (strcasecmp($user, "system") && strcasecmp($user, "sys")) { die("skip needs to be run as a DBA user"); -if ($test_drcp) +} +if ($test_drcp) { die("skip as Output might vary with DRCP"); - -$sv = oci_server_version($c); -$sv = preg_match('/Release (11\.2|12)/', $sv, $matches); -if ($sv == 1) { - ob_start(); - phpinfo(INFO_MODULES); - $phpinfo = ob_get_clean(); - $iv = preg_match('/Oracle .*Version => (11\.2|12)/', $phpinfo); - if ($iv != 1) { - die ("skip tests a feature that works only with Oracle 11gR2 or greater version of client"); - } } -else { - die ("skip tests a feature that works only with Oracle 11gR2 or greater version of server"); +if (preg_match('/Release (1[1]\.2|12)\./', oci_server_version($c), $matches) !== 1) { + die("skip expected output only valid when using Oracle 11gR2 or greater databases"); +} else if (preg_match('/^(11\.2|12)\./', oci_client_version()) != 1) { + die("skip test expected to work only with Oracle 11gR2 or greater version of client"); } - ?> --FILE-- <?php @@ -62,10 +53,7 @@ $stmtarray = array( "create or replace editioning view view_ed as select name,age,job from edit_tab", ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($conn, $stmt); - @oci_execute($s); -} +oci8_test_sql_execute($conn, $stmtarray); // Check the current edition of the DB and the contents of view_ed. get_edit_attr($conn); diff --git a/ext/oci8/tests/edition_2.phpt b/ext/oci8/tests/edition_2.phpt index f7ab979bc..030e6a673 100644 --- a/ext/oci8/tests/edition_2.phpt +++ b/ext/oci8/tests/edition_2.phpt @@ -9,19 +9,10 @@ if (strcasecmp($user, "system") && strcasecmp($user, "sys")) if ($test_drcp) die("skip as Output might vary with DRCP"); -$sv = oci_server_version($c); -$sv = preg_match('/Release (11\.2|12)/', $sv, $matches); -if ($sv == 1) { - ob_start(); - phpinfo(INFO_MODULES); - $phpinfo = ob_get_clean(); - $iv = preg_match('/Oracle .*Version => (11\.2|12)/', $phpinfo); - if ($iv != 1) { - die ("skip tests a feature that works only with Oracle 11gR2 or greater version of client"); - } -} -else { - die ("skip tests a feature that works only with Oracle 11gR2 or greater version of server"); +if (preg_match('/Release (1[1]\.2|12)\./', oci_server_version($c), $matches) !== 1) { + die("skip expected output only valid when using Oracle 11gR2 or greater databases"); +} else if (preg_match('/^(11\.2|12)\./', oci_client_version()) != 1) { + die("skip test expected to work only with Oracle 11gR2 or greater version of client"); } ?> diff --git a/ext/oci8/tests/error.phpt b/ext/oci8/tests/error.phpt index 743820f90..7fedd0dda 100644 --- a/ext/oci8/tests/error.phpt +++ b/ext/oci8/tests/error.phpt @@ -1,17 +1,20 @@ --TEST-- -oci_error() +oci_error() error message for parsing error --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs: different error messages from TimesTen +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php require dirname(__FILE__)."/connect.inc"; if (!empty($dbase)) { - var_dump(oci_connect($user, $password, $dbase)); + var_dump(oci_connect($user, $password, $dbase)); } else { - var_dump(oci_connect($user, $password)); + var_dump(oci_connect($user, $password)); } var_dump($s = oci_parse($c, "WRONG SYNTAX")); @@ -25,13 +28,13 @@ echo "Done\n"; resource(%s) of type (oci8 connection) resource(%s) of type (oci8 statement) -Warning: oci_execute(): ORA-00900: invalid SQL statement in %s on line %d +Warning: oci_execute(): ORA-00900: %s in %s on line %d bool(false) array(4) { ["code"]=> int(900) ["message"]=> - string(32) "ORA-00900: invalid SQL statement" + string(%d) "ORA-00900: %s" ["offset"]=> int(0) ["sqltext"]=> diff --git a/ext/oci8/tests/error1.phpt b/ext/oci8/tests/error1.phpt index 25a3f09e9..c6fba2e83 100644 --- a/ext/oci8/tests/error1.phpt +++ b/ext/oci8/tests/error1.phpt @@ -12,13 +12,13 @@ echo "Done\n"; ?> --EXPECTF-- -Warning: oci_connect(): ORA-12154: TNS:could not resolve %s in %s on line %d +Warning: oci_connect(): ORA-12154: %s in %s on line %d bool(false) array(4) { ["code"]=> int(12154) ["message"]=> - string(%d) "ORA-12154: TNS:could not resolve %s" + string(%d) "ORA-12154: %s" ["offset"]=> int(0) ["sqltext"]=> diff --git a/ext/oci8/tests/error2.phpt b/ext/oci8/tests/error2.phpt index 13ea6cebe..12eab61a6 100644 --- a/ext/oci8/tests/error2.phpt +++ b/ext/oci8/tests/error2.phpt @@ -1,11 +1,15 @@ --TEST-- Exercise error code for SUCCESS_WITH_INFO --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +if ($stress_test !== true) die ('skip Slow test not run when $stress_test is FALSE'); +?> --FILE-- <?php -require dirname(__FILE__).'/connect.inc'; +require(dirname(__FILE__).'/connect.inc'); ini_set('error_reporting', E_ALL); @@ -20,5 +24,5 @@ echo "Done\n"; ?> --EXPECTF-- -ORA-24344: success with compilation error +ORA-24344: %s Done diff --git a/ext/oci8/tests/error_bind.phpt b/ext/oci8/tests/error_bind.phpt index ad66ad59f..6ee26ee82 100644 --- a/ext/oci8/tests/error_bind.phpt +++ b/ext/oci8/tests/error_bind.phpt @@ -65,6 +65,6 @@ Test 3 - Resource mismatch !! Warning: oci_bind_by_name(): Invalid variable used for bind in %s on line %d -Warning: oci_execute(): ORA-01008: %s on line %d +Warning: oci_execute(): ORA-%r(01008|57000)%r: %s on line %d bool(false) Done diff --git a/ext/oci8/tests/error_old.phpt b/ext/oci8/tests/error_old.phpt index a6889c897..c6f9cd300 100644 --- a/ext/oci8/tests/error_old.phpt +++ b/ext/oci8/tests/error_old.phpt @@ -1,17 +1,20 @@ --TEST-- ocierror() --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs: different error messages from TimesTen +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php require dirname(__FILE__)."/connect.inc"; if (!empty($dbase)) { - var_dump(ocilogon($user, $password, $dbase)); + var_dump(ocilogon($user, $password, $dbase)); } else { - var_dump(ocilogon($user, $password)); + var_dump(ocilogon($user, $password)); } var_dump($s = ociparse($c, "WRONG SYNTAX")); @@ -25,13 +28,13 @@ echo "Done\n"; resource(%s) of type (oci8 connection) resource(%s) of type (oci8 statement) -Warning: ociexecute(): ORA-00900: invalid SQL statement in %s on line %d +Warning: ociexecute(): ORA-00900: %s in %s on line %d bool(false) array(4) { ["code"]=> int(900) ["message"]=> - string(32) "ORA-00900: invalid SQL statement" + string(%d) "ORA-00900: %s" ["offset"]=> int(0) ["sqltext"]=> diff --git a/ext/oci8/tests/error_parse.phpt b/ext/oci8/tests/error_parse.phpt index 8100e3170..458df8715 100644 --- a/ext/oci8/tests/error_parse.phpt +++ b/ext/oci8/tests/error_parse.phpt @@ -104,7 +104,7 @@ array(4) { ["code"]=> int(1756) ["message"]=> - string(48) "ORA-01756: quoted string not properly terminated" + string(48) "ORA-01756: %s" ["offset"]=> int(0) ["sqltext"]=> @@ -115,7 +115,7 @@ array(4) { ["code"]=> int(1756) ["message"]=> - string(48) "ORA-01756: quoted string not properly terminated" + string(48) "ORA-01756: %s" ["offset"]=> int(0) ["sqltext"]=> @@ -126,17 +126,17 @@ array(4) { ["code"]=> int(1756) ["message"]=> - string(48) "ORA-01756: quoted string not properly terminated" + string(48) "ORA-01756: %s" ["offset"]=> int(0) ["sqltext"]=> string(0) "" } No connection: error: bool(false) -Normal connection (take #2): Parse error: ORA-01756: quoted string not properly terminated -New connection (take #2): Parse error: ORA-01756: quoted string not properly terminated -Persistent connection (take #2): Parse error: ORA-01756: quoted string not properly terminated +Normal connection (take #2): Parse error: ORA-01756: %s +New connection (take #2): Parse error: ORA-01756: %s +Persistent connection (take #2): Parse error: ORA-01756: %s Normal connection: New Collection error: OCI-22303: type ""."ABC" not found -New connection (take #3): Parse error: ORA-01756: quoted string not properly terminated -Persistent connection (take #3): Parse error: ORA-01756: quoted string not properly terminated +New connection (take #3): Parse error: ORA-01756: %s +Persistent connection (take #3): Parse error: ORA-01756: %s Done diff --git a/ext/oci8/tests/exec_fetch.phpt b/ext/oci8/tests/exec_fetch.phpt index 83aae69f7..52d515ffc 100644 --- a/ext/oci8/tests/exec_fetch.phpt +++ b/ext/oci8/tests/exec_fetch.phpt @@ -16,9 +16,9 @@ var_dump(oci_fetch_array($stmt)); echo "Done\n"; ?> --EXPECTF-- -Warning: oci_execute(): ORA-00942: table or view does not exist in %s on line %d +Warning: oci_execute(): ORA-00942: %s in %s on line %d bool(false) -Warning: oci_fetch_array(): ORA-24374: define not done before fetch or execute and fetch in %s on line %d +Warning: oci_fetch_array(): ORA-24374: %s in %s on line %d bool(false) Done diff --git a/ext/oci8/tests/fetch.phpt b/ext/oci8/tests/fetch.phpt index 520632494..e48aeefd8 100644 --- a/ext/oci8/tests/fetch.phpt +++ b/ext/oci8/tests/fetch.phpt @@ -17,23 +17,7 @@ $stmtarray = array( "insert into fetch_tab (id, value) values (1,1)", ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - $r = @oci_execute($s); - if (!$r) { - $m = oci_error($s); - if (!in_array($m['code'], array( // ignore expected errors - 942 // table or view does not exist - ))) { - echo $stmt . PHP_EOL . $m['message'] . PHP_EOL; - } - } -} - -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); // Run Test @@ -58,11 +42,7 @@ $stmtarray = array( "drop table fetch_tab" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} - +oci8_test_sql_execute($c, $stmtarray); echo "Done\n"; ?> diff --git a/ext/oci8/tests/fetch_all.phpt b/ext/oci8/tests/fetch_all.phpt index a007bac83..4fc41daad 100644 --- a/ext/oci8/tests/fetch_all.phpt +++ b/ext/oci8/tests/fetch_all.phpt @@ -17,18 +17,7 @@ $stmtarray = array( "insert into fetch_all_tab (id, value) values (1,1)" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - $r = @oci_execute($s); - if (!$r) { - $m = oci_error($s); - if (!in_array($m['code'], array( // ignore expected errors - 942 // table or view does not exist - ))) { - echo $stmt . PHP_EOL . $m['message'] . PHP_EOL; - } - } -} +oci8_test_sql_execute($c, $stmtarray); if (!($s = oci_parse($c, "select * from fetch_all_tab"))) { die("oci_parse(select) failed!\n"); @@ -55,10 +44,7 @@ $stmtarray = array( "drop table fetch_all_tab" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); echo "Done\n"; ?> diff --git a/ext/oci8/tests/fetch_all1.phpt b/ext/oci8/tests/fetch_all1.phpt new file mode 100644 index 000000000..4fc41daad --- /dev/null +++ b/ext/oci8/tests/fetch_all1.phpt @@ -0,0 +1,94 @@ +--TEST-- +oci_fetch_all() +--SKIPIF-- +<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +--FILE-- +<?php + +require(dirname(__FILE__)."/connect.inc"); + +// Initialize + +$stmtarray = array( + "drop table fetch_all_tab", + "create table fetch_all_tab (id number, value number)", + "insert into fetch_all_tab (id, value) values (1,1)", + "insert into fetch_all_tab (id, value) values (1,1)", + "insert into fetch_all_tab (id, value) values (1,1)" +); + +oci8_test_sql_execute($c, $stmtarray); + +if (!($s = oci_parse($c, "select * from fetch_all_tab"))) { + die("oci_parse(select) failed!\n"); +} + +/* oci_fetch_all */ +if (!oci_execute($s)) { + die("oci_execute(select) failed!\n"); +} +var_dump(oci_fetch_all($s, $all)); +var_dump($all); + +/* ocifetchstatement */ +if (!oci_execute($s)) { + die("oci_execute(select) failed!\n"); +} + +var_dump(ocifetchstatement($s, $all)); +var_dump($all); + +// Cleanup + +$stmtarray = array( + "drop table fetch_all_tab" +); + +oci8_test_sql_execute($c, $stmtarray); + +echo "Done\n"; +?> +--EXPECTF-- +int(3) +array(2) { + [%u|b%"ID"]=> + array(3) { + [0]=> + %unicode|string%(1) "1" + [1]=> + %unicode|string%(1) "1" + [2]=> + %unicode|string%(1) "1" + } + [%u|b%"VALUE"]=> + array(3) { + [0]=> + %unicode|string%(1) "1" + [1]=> + %unicode|string%(1) "1" + [2]=> + %unicode|string%(1) "1" + } +} +int(3) +array(2) { + [%u|b%"ID"]=> + array(3) { + [0]=> + %unicode|string%(1) "1" + [1]=> + %unicode|string%(1) "1" + [2]=> + %unicode|string%(1) "1" + } + [%u|b%"VALUE"]=> + array(3) { + [0]=> + %unicode|string%(1) "1" + [1]=> + %unicode|string%(1) "1" + [2]=> + %unicode|string%(1) "1" + } +} +Done diff --git a/ext/oci8/tests/fetch_all2.phpt b/ext/oci8/tests/fetch_all2.phpt index ff44cac70..483e51573 100644 --- a/ext/oci8/tests/fetch_all2.phpt +++ b/ext/oci8/tests/fetch_all2.phpt @@ -1,7 +1,10 @@ --TEST-- oci_fetch_all() - 2 --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/fetch_all3.phpt b/ext/oci8/tests/fetch_all3.phpt index 42fe617dc..1748ea565 100644 --- a/ext/oci8/tests/fetch_all3.phpt +++ b/ext/oci8/tests/fetch_all3.phpt @@ -14,28 +14,12 @@ $stmtarray = array( "create table fetch_all3_tab (id number, value number)", ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - $r = @oci_execute($s); - if (!$r) { - $m = oci_error($s); - if (!in_array($m['code'], array( // ignore expected errors - 942 // table or view does not exist - ))) { - echo $stmt . PHP_EOL . $m['message'] . PHP_EOL; - } - } -} - -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); $insert_sql = "insert into fetch_all3_tab (id, value) values (:idbv,:vbv)"; $s = oci_parse($c, $insert_sql); -oci_bind_by_name($s, ":idbv", $idbv, SQLT_INT); -oci_bind_by_name($s, ":vbv", $vbv, SQLT_INT); +oci_bind_by_name($s, ":idbv", $idbv, -1, SQLT_INT); +oci_bind_by_name($s, ":vbv", $vbv, -1, SQLT_INT); for ($i = 1; $i <= 4; $i++) { $idbv = $i; @@ -137,10 +121,7 @@ $stmtarray = array( "drop table fetch_all3_tab" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); echo "Done\n"; ?> diff --git a/ext/oci8/tests/fetch_all4.phpt b/ext/oci8/tests/fetch_all4.phpt index 9b82262e3..1d3c9677e 100644 --- a/ext/oci8/tests/fetch_all4.phpt +++ b/ext/oci8/tests/fetch_all4.phpt @@ -15,21 +15,7 @@ $stmtarray = array( "insert into fetch_all4_tab values (1, 'abc')" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - $r = @oci_execute($s); - if (!$r) { - $m = oci_error($s); - if (!in_array($m['code'], array( // ignore expected errors - 942 // table or view does not exist - , 2289 // sequence does not exist - , 4080 // trigger does not exist - , 38802 // edition does not exist - ))) { - echo $stmt . PHP_EOL . $m['message'] . PHP_EOL; - } - } -} +oci8_test_sql_execute($c, $stmtarray); // Run Test @@ -56,12 +42,7 @@ $stmtarray = array( "drop table fetch_all4_tab" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} - -oci_close($c); +oci8_test_sql_execute($c, $stmtarray); ?> ===DONE=== diff --git a/ext/oci8/tests/fetch_all5.phpt b/ext/oci8/tests/fetch_all5.phpt index bb9061a44..a6bb3c3f1 100644 --- a/ext/oci8/tests/fetch_all5.phpt +++ b/ext/oci8/tests/fetch_all5.phpt @@ -17,21 +17,7 @@ $stmtarray = array( "insert into fetch_all5_tab values (3, 'ghi')" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - $r = @oci_execute($s); - if (!$r) { - $m = oci_error($s); - if (!in_array($m['code'], array( // ignore expected errors - 942 // table or view does not exist - , 2289 // sequence does not exist - , 4080 // trigger does not exist - , 38802 // edition does not exist - ))) { - echo $stmt . PHP_EOL . $m['message'] . PHP_EOL; - } - } -} +oci8_test_sql_execute($c, $stmtarray); // Run Test @@ -65,10 +51,7 @@ $stmtarray = array( "drop table fetch_all5_tab" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); oci_close($c); diff --git a/ext/oci8/tests/fetch_array.phpt b/ext/oci8/tests/fetch_array.phpt index e2f32483d..db5c6c554 100644 --- a/ext/oci8/tests/fetch_array.phpt +++ b/ext/oci8/tests/fetch_array.phpt @@ -1,7 +1,10 @@ --TEST-- oci_fetch_array() --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php @@ -24,6 +27,8 @@ if (!oci_commit($c)) { die("oci_commit() failed!\n"); } +echo "Test 1\n"; + $select_sql = "SELECT * FROM ".$schema."".$table_name.""; if (!($s = oci_parse($c, $select_sql))) { @@ -37,6 +42,8 @@ while ($row = oci_fetch_array($s)) { var_dump($row); } +echo "Test 2\n"; + if (!oci_execute($s)) { die("oci_execute(select) failed!\n"); } @@ -44,6 +51,8 @@ while ($row = oci_fetch_array($s, OCI_NUM)) { var_dump($row); } +echo "Test 3\n"; + if (!oci_execute($s)) { die("oci_execute(select) failed!\n"); } @@ -51,6 +60,8 @@ while ($row = oci_fetch_array($s, OCI_ASSOC)) { var_dump($row); } +echo "Test 4\n"; + if (!oci_execute($s)) { die("oci_execute(select) failed!\n"); } @@ -58,6 +69,8 @@ while ($row = oci_fetch_array($s, OCI_BOTH)) { var_dump($row); } +echo "Test 5\n"; + if (!oci_execute($s)) { die("oci_execute(select) failed!\n"); } @@ -65,6 +78,8 @@ while ($row = oci_fetch_array($s, OCI_RETURN_LOBS)) { var_dump($row); } +echo "Test 6\n"; + if (!oci_execute($s)) { die("oci_execute(select) failed!\n"); } @@ -72,11 +87,21 @@ while ($row = oci_fetch_array($s, OCI_RETURN_NULLS)) { var_dump($row); } +echo "Test 7\n"; + +if (!oci_execute($s)) { + die("oci_execute(select) failed!\n"); +} +while ($row = oci_fetch_array($s, OCI_NUM+OCI_RETURN_NULLS)) { + var_dump($row); +} + require dirname(__FILE__).'/drop_table.inc'; echo "Done\n"; ?> --EXPECT-- +Test 1 array(10) { [0]=> string(1) "1" @@ -143,6 +168,7 @@ array(10) { ["STRING"]=> NULL } +Test 2 array(2) { [0]=> string(1) "1" @@ -161,6 +187,7 @@ array(2) { [1]=> string(1) "1" } +Test 3 array(2) { ["ID"]=> string(1) "1" @@ -179,6 +206,7 @@ array(2) { ["VALUE"]=> string(1) "1" } +Test 4 array(4) { [0]=> string(1) "1" @@ -209,6 +237,7 @@ array(4) { ["VALUE"]=> string(1) "1" } +Test 5 array(4) { [0]=> string(1) "1" @@ -239,6 +268,7 @@ array(4) { ["VALUE"]=> string(1) "1" } +Test 6 array(10) { [0]=> string(1) "1" @@ -305,4 +335,42 @@ array(10) { ["STRING"]=> NULL } +Test 7 +array(5) { + [0]=> + string(1) "1" + [1]=> + string(1) "1" + [2]=> + NULL + [3]=> + NULL + [4]=> + NULL +} +array(5) { + [0]=> + string(1) "1" + [1]=> + string(1) "1" + [2]=> + NULL + [3]=> + NULL + [4]=> + NULL +} +array(5) { + [0]=> + string(1) "1" + [1]=> + string(1) "1" + [2]=> + NULL + [3]=> + NULL + [4]=> + NULL +} Done + diff --git a/ext/oci8/tests/fetch_assoc.phpt b/ext/oci8/tests/fetch_assoc.phpt index 7dacf2e5b..5c4c91e1d 100644 --- a/ext/oci8/tests/fetch_assoc.phpt +++ b/ext/oci8/tests/fetch_assoc.phpt @@ -1,30 +1,23 @@ --TEST-- oci_fetch_assoc() --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php if (!extension_loaded('oci8')) die ("skip no oci8 extension"); ?> --FILE-- <?php require dirname(__FILE__)."/connect.inc"; -require dirname(__FILE__).'/create_table.inc'; -$insert_sql = "INSERT INTO ".$schema."".$table_name." (id, value) VALUES (1,1)"; +$stmtarray = array( + "drop table fetch_assoc_tab", + "create table fetch_assoc_tab (id number, value number, dummy varchar2(20))", + "insert into fetch_assoc_tab values (1,1,null)", + "insert into fetch_assoc_tab values (1,1,null)", + "insert into fetch_assoc_tab values (1,1,null)" +); -if (!($s = oci_parse($c, $insert_sql))) { - die("oci_parse(insert) failed!\n"); -} - -for ($i = 0; $i<3; $i++) { - if (!oci_execute($s)) { - die("oci_execute(insert) failed!\n"); - } -} +oci8_test_sql_execute($c, $stmtarray); -if (!oci_commit($c)) { - die("oci_commit() failed!\n"); -} - -$select_sql = "SELECT * FROM ".$schema."".$table_name.""; +$select_sql = "select * from fetch_assoc_tab"; if (!($s = oci_parse($c, $select_sql))) { die("oci_parse(select) failed!\n"); @@ -37,46 +30,40 @@ while ($row = oci_fetch_assoc($s)) { var_dump($row); } -require dirname(__FILE__).'/drop_table.inc'; +// Clean up + +$stmtarray = array( + "drop table fetch_assoc_tab" +); + +oci8_test_sql_execute($c, $stmtarray); echo "Done\n"; ?> --EXPECT-- -array(5) { +array(3) { ["ID"]=> string(1) "1" ["VALUE"]=> string(1) "1" - ["BLOB"]=> - NULL - ["CLOB"]=> - NULL - ["STRING"]=> + ["DUMMY"]=> NULL } -array(5) { +array(3) { ["ID"]=> string(1) "1" ["VALUE"]=> string(1) "1" - ["BLOB"]=> - NULL - ["CLOB"]=> - NULL - ["STRING"]=> + ["DUMMY"]=> NULL } -array(5) { +array(3) { ["ID"]=> string(1) "1" ["VALUE"]=> string(1) "1" - ["BLOB"]=> - NULL - ["CLOB"]=> - NULL - ["STRING"]=> + ["DUMMY"]=> NULL } Done diff --git a/ext/oci8/tests/fetch_into.phpt b/ext/oci8/tests/fetch_into.phpt index 17e06e1cf..45a6a8132 100644 --- a/ext/oci8/tests/fetch_into.phpt +++ b/ext/oci8/tests/fetch_into.phpt @@ -17,23 +17,7 @@ $stmtarray = array( "insert into fetch_into_tab (id, value) values (1,1)", ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - $r = @oci_execute($s); - if (!$r) { - $m = oci_error($s); - if (!in_array($m['code'], array( // ignore expected errors - 942 // table or view does not exist - ))) { - echo $stmt . PHP_EOL . $m['message'] . PHP_EOL; - } - } -} - -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); // Run Test @@ -61,11 +45,8 @@ $stmtarray = array( "drop table fetch_into_tab" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} - +oci8_test_sql_execute($c, $stmtarray); + echo "Done\n"; ?> --EXPECTF-- diff --git a/ext/oci8/tests/fetch_into1.phpt b/ext/oci8/tests/fetch_into1.phpt index 263590d14..8f7a6bdeb 100644 --- a/ext/oci8/tests/fetch_into1.phpt +++ b/ext/oci8/tests/fetch_into1.phpt @@ -1,7 +1,10 @@ --TEST-- various ocifetchinto() tests --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/fetch_into2.phpt b/ext/oci8/tests/fetch_into2.phpt index c196d39f6..7bef2c082 100644 --- a/ext/oci8/tests/fetch_into2.phpt +++ b/ext/oci8/tests/fetch_into2.phpt @@ -1,7 +1,10 @@ --TEST-- ocifetchinto() & wrong number of params --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/fetch_object.phpt b/ext/oci8/tests/fetch_object.phpt index 674a88ff2..1c290d5e9 100644 --- a/ext/oci8/tests/fetch_object.phpt +++ b/ext/oci8/tests/fetch_object.phpt @@ -1,7 +1,10 @@ --TEST-- oci_fetch_object() --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php @@ -10,53 +13,39 @@ require(dirname(__FILE__).'/connect.inc'); // Initialization $stmtarray = array( - "drop table fetch_object_tab", - "create table fetch_object_tab (\"caseSensitive\" number, secondcol varchar2(20), anothercol char(15))", - "insert into fetch_object_tab values (123, '1st row col2 string', '1 more text')", - "insert into fetch_object_tab values (456, '2nd row col2 string', '2 more text')", - "insert into fetch_object_tab values (789, '3rd row col2 string', '3 more text')", + "drop table fetch_object_tab", + "create table fetch_object_tab (\"caseSensitive\" number, secondcol varchar2(20), anothercol char(15))", + "insert into fetch_object_tab values (123, '1st row col2 string', '1 more text')", + "insert into fetch_object_tab values (456, '2nd row col2 string', '2 more text')", + "insert into fetch_object_tab values (789, '3rd row col2 string', '3 more text')", ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - $r = @oci_execute($s); - if (!$r) { - $m = oci_error($s); - if (!in_array($m['code'], array( // ignore expected errors - 942 // table or view does not exist - , 2289 // sequence does not exist - , 4080 // trigger does not exist - , 38802 // edition does not exist - ))) { - echo $stmt . PHP_EOL . $m['message'] . PHP_EOL; - } - } -} +oci8_test_sql_execute($c, $stmtarray); // Run Test echo "Test 1\n"; if (!($s = oci_parse($c, 'select * from fetch_object_tab'))) { - die("oci_parse(select) failed!\n"); + die("oci_parse(select) failed!\n"); } if (!oci_execute($s)) { - die("oci_execute(select) failed!\n"); + die("oci_execute(select) failed!\n"); } while ($row = oci_fetch_object($s)) { - var_dump($row); + var_dump($row); } echo "Test 2\n"; if (!($s = oci_parse($c, 'select * from fetch_object_tab'))) { - die("oci_parse(select) failed!\n"); + die("oci_parse(select) failed!\n"); } if (!oci_execute($s)) { - die("oci_execute(select) failed!\n"); + die("oci_execute(select) failed!\n"); } while ($row = oci_fetch_object($s)) { @@ -68,11 +57,11 @@ while ($row = oci_fetch_object($s)) { echo "Test 3\n"; if (!($s = oci_parse($c, 'select * from fetch_object_tab where rownum < 2 order by "caseSensitive"'))) { - die("oci_parse(select) failed!\n"); + die("oci_parse(select) failed!\n"); } if (!oci_execute($s)) { - die("oci_execute(select) failed!\n"); + die("oci_execute(select) failed!\n"); } $row = oci_fetch_object($s); @@ -82,13 +71,10 @@ echo $row->CASESENSITIVE . "\n"; // Clean up $stmtarray = array( - "drop table fetch_object_tab" + "drop table fetch_object_tab" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); ?> ===DONE=== diff --git a/ext/oci8/tests/fetch_object_1.phpt b/ext/oci8/tests/fetch_object_1.phpt new file mode 100644 index 000000000..4f14a5ec0 --- /dev/null +++ b/ext/oci8/tests/fetch_object_1.phpt @@ -0,0 +1,123 @@ +--TEST-- +oci_fetch_object() +--SKIPIF-- +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> +--FILE-- +<?php + +require(dirname(__FILE__).'/connect.inc'); + +// Initialization + +$stmtarray = array( + "drop table fetch_object_tab", + "create table fetch_object_tab (\"caseSensitive\" number, secondcol varchar2(20), anothercol char(15))", + "insert into fetch_object_tab values (123, '1st row col2 string', '1 more text')", + "insert into fetch_object_tab values (456, '2nd row col2 string', '2 more text')", + "insert into fetch_object_tab values (789, '3rd row col2 string', '3 more text')", +); + +oci8_test_sql_execute($c, $stmtarray); + +// Run Test + +echo "Test 1\n"; + +if (!($s = oci_parse($c, 'select * from fetch_object_tab'))) { + die("oci_parse(select) failed!\n"); +} + +if (!oci_execute($s)) { + die("oci_execute(select) failed!\n"); +} + +while ($row = oci_fetch_object($s)) { + var_dump($row); +} + +echo "Test 2\n"; + +if (!($s = oci_parse($c, 'select * from fetch_object_tab'))) { + die("oci_parse(select) failed!\n"); +} + +if (!oci_execute($s)) { + die("oci_execute(select) failed!\n"); +} + +while ($row = oci_fetch_object($s)) { + echo $row->caseSensitive . "\n"; + echo $row->SECONDCOL . "\n"; + echo $row->ANOTHERCOL . "\n"; +} + +echo "Test 3\n"; + +if (!($s = oci_parse($c, 'select * from fetch_object_tab where rownum < 2 order by "caseSensitive"'))) { + die("oci_parse(select) failed!\n"); +} + +if (!oci_execute($s)) { + die("oci_execute(select) failed!\n"); +} + +$row = oci_fetch_object($s); +echo $row->caseSensitive . "\n"; +echo $row->CASESENSITIVE . "\n"; + +// Clean up + +$stmtarray = array( + "drop table fetch_object_tab" +); + +oci8_test_sql_execute($c, $stmtarray); + +?> +===DONE=== +<?php exit(0); ?> +--EXPECTF-- +Test 1 +object(stdClass)#%d (3) { + ["caseSensitive"]=> + string(3) "123" + ["SECONDCOL"]=> + string(19) "1st row col2 string" + ["ANOTHERCOL"]=> + string(15) "1 more text " +} +object(stdClass)#%d (3) { + ["caseSensitive"]=> + string(3) "456" + ["SECONDCOL"]=> + string(19) "2nd row col2 string" + ["ANOTHERCOL"]=> + string(15) "2 more text " +} +object(stdClass)#%d (3) { + ["caseSensitive"]=> + string(3) "789" + ["SECONDCOL"]=> + string(19) "3rd row col2 string" + ["ANOTHERCOL"]=> + string(15) "3 more text " +} +Test 2 +123 +1st row col2 string +1 more text +456 +2nd row col2 string +2 more text +789 +3rd row col2 string +3 more text +Test 3 +123 + +Notice: Undefined property: stdClass::$CASESENSITIVE in %sfetch_object_1.php on line %d + +===DONE=== diff --git a/ext/oci8/tests/fetch_object_2.phpt b/ext/oci8/tests/fetch_object_2.phpt index b078ebb1d..1814446aa 100644 --- a/ext/oci8/tests/fetch_object_2.phpt +++ b/ext/oci8/tests/fetch_object_2.phpt @@ -1,7 +1,10 @@ --TEST-- oci_fetch_object() with CLOB and NULL --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php @@ -10,53 +13,39 @@ require(dirname(__FILE__).'/connect.inc'); // Initialization $stmtarray = array( - "drop table fetch_object_2_tab", - "create table fetch_object_2_tab (col1 number, col2 CLOB, col3 varchar2(15))", - "insert into fetch_object_2_tab values (123, '1st row col2 string', '1 more text')", - "insert into fetch_object_2_tab values (456, '2nd row col2 string', NULL)", - "insert into fetch_object_2_tab values (789, '3rd row col2 string', '3 more text')", + "drop table fetch_object_2_tab", + "create table fetch_object_2_tab (col1 number, col2 CLOB, col3 varchar2(15))", + "insert into fetch_object_2_tab values (123, '1st row col2 string', '1 more text')", + "insert into fetch_object_2_tab values (456, '2nd row col2 string', NULL)", + "insert into fetch_object_2_tab values (789, '3rd row col2 string', '3 more text')", ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - $r = @oci_execute($s); - if (!$r) { - $m = oci_error($s); - if (!in_array($m['code'], array( // ignore expected errors - 942 // table or view does not exist - , 2289 // sequence does not exist - , 4080 // trigger does not exist - , 38802 // edition does not exist - ))) { - echo $stmt . PHP_EOL . $m['message'] . PHP_EOL; - } - } -} +oci8_test_sql_execute($c, $stmtarray); // Run Test echo "Test 1\n"; if (!($s = oci_parse($c, 'select * from fetch_object_2_tab order by 1'))) { - die("oci_parse(select) failed!\n"); + die("oci_parse(select) failed!\n"); } if (!oci_execute($s)) { - die("oci_execute(select) failed!\n"); + die("oci_execute(select) failed!\n"); } while ($row = oci_fetch_object($s)) { - var_dump($row); + var_dump($row); } echo "Test 2\n"; if (!($s = oci_parse($c, 'select * from fetch_object_2_tab order by 1'))) { - die("oci_parse(select) failed!\n"); + die("oci_parse(select) failed!\n"); } if (!oci_execute($s)) { - die("oci_execute(select) failed!\n"); + die("oci_execute(select) failed!\n"); } while ($row = oci_fetch_object($s)) { @@ -68,13 +57,10 @@ while ($row = oci_fetch_object($s)) { // Clean up $stmtarray = array( - "drop table fetch_object_2_tab" + "drop table fetch_object_2_tab" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); ?> ===DONE=== @@ -82,37 +68,37 @@ foreach ($stmtarray as $stmt) { --EXPECTF-- Test 1 object(stdClass)#%d (3) { - [%u|b%"COL1"]=> - %unicode|string%(3) "123" - [%u|b%"COL2"]=> + ["COL1"]=> + string(3) "123" + ["COL2"]=> object(OCI-Lob)#%d (1) { - [%u|b%"descriptor"]=> + ["descriptor"]=> resource(%d) of type (oci8 descriptor) } - [%u|b%"COL3"]=> - %unicode|string%(11) "1 more text" + ["COL3"]=> + string(11) "1 more text" } object(stdClass)#%d (3) { - [%u|b%"COL1"]=> - %unicode|string%(3) "456" - [%u|b%"COL2"]=> + ["COL1"]=> + string(3) "456" + ["COL2"]=> object(OCI-Lob)#%d (1) { - [%u|b%"descriptor"]=> + ["descriptor"]=> resource(%d) of type (oci8 descriptor) } - [%u|b%"COL3"]=> + ["COL3"]=> NULL } object(stdClass)#%d (3) { - [%u|b%"COL1"]=> - %unicode|string%(3) "789" - [%u|b%"COL2"]=> + ["COL1"]=> + string(3) "789" + ["COL2"]=> object(OCI-Lob)#%d (1) { - [%u|b%"descriptor"]=> + ["descriptor"]=> resource(%d) of type (oci8 descriptor) } - [%u|b%"COL3"]=> - %unicode|string%(11) "3 more text" + ["COL3"]=> + string(11) "3 more text" } Test 2 123 diff --git a/ext/oci8/tests/fetch_row.phpt b/ext/oci8/tests/fetch_row.phpt index c6084d4fe..2b28634ab 100644 --- a/ext/oci8/tests/fetch_row.phpt +++ b/ext/oci8/tests/fetch_row.phpt @@ -17,23 +17,7 @@ $stmtarray = array( "insert into fetch_row_tab (id, value) values (1,1)", ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - $r = @oci_execute($s); - if (!$r) { - $m = oci_error($s); - if (!in_array($m['code'], array( // ignore expected errors - 942 // table or view does not exist - ))) { - echo $stmt . PHP_EOL . $m['message'] . PHP_EOL; - } - } -} - -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); // Run Test @@ -54,10 +38,7 @@ $stmtarray = array( "drop table fetch_row_tab" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); echo "Done\n"; diff --git a/ext/oci8/tests/field_funcs.phpt b/ext/oci8/tests/field_funcs.phpt index 18143f6f1..92b77187a 100644 --- a/ext/oci8/tests/field_funcs.phpt +++ b/ext/oci8/tests/field_funcs.phpt @@ -1,7 +1,10 @@ --TEST-- oci_field_*() family --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/field_funcs0.phpt b/ext/oci8/tests/field_funcs0.phpt new file mode 100644 index 000000000..5448fcbc3 --- /dev/null +++ b/ext/oci8/tests/field_funcs0.phpt @@ -0,0 +1,108 @@ +--TEST-- +oci_field_*() family +--SKIPIF-- +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> +--FILE-- +<?php + +require dirname(__FILE__)."/connect.inc"; +require dirname(__FILE__).'/create_table.inc'; + +$insert_sql = "INSERT INTO ".$schema."".$table_name." (id, value) VALUES (1,1)"; + +if (!($s = oci_parse($c, $insert_sql))) { + die("oci_parse(insert) failed!\n"); +} + +for ($i = 0; $i<3; $i++) { + if (!oci_execute($s)) { + die("oci_execute(insert) failed!\n"); + } +} + +if (!oci_commit($c)) { + die("oci_commit() failed!\n"); +} + +$select_sql = "SELECT * FROM ".$schema."".$table_name.""; + +if (!($s = oci_parse($c, $select_sql))) { + die("oci_parse(select) failed!\n"); +} + +if (!oci_execute($s)) { + die("oci_execute(select) failed!\n"); +} + +$row = oci_fetch_array($s, OCI_NUM + OCI_RETURN_NULLS + OCI_RETURN_LOBS); +var_dump($row); + +foreach ($row as $num => $field) { + $num++; + var_dump(oci_field_is_null($s, $num)); + var_dump(oci_field_name($s, $num)); + var_dump(oci_field_type($s, $num)); + var_dump(oci_field_type_raw($s, $num)); + var_dump(oci_field_scale($s, $num)); + var_dump(oci_field_precision($s, $num)); + var_dump(oci_field_size($s, $num)); +} + + +require dirname(__FILE__).'/drop_table.inc'; + +echo "Done\n"; + +?> +--EXPECT-- +array(5) { + [0]=> + string(1) "1" + [1]=> + string(1) "1" + [2]=> + NULL + [3]=> + NULL + [4]=> + NULL +} +bool(false) +string(2) "ID" +string(6) "NUMBER" +int(2) +int(-127) +int(0) +int(22) +bool(false) +string(5) "VALUE" +string(6) "NUMBER" +int(2) +int(-127) +int(0) +int(22) +bool(true) +string(4) "BLOB" +string(4) "BLOB" +int(113) +int(0) +int(0) +int(4000) +bool(true) +string(4) "CLOB" +string(4) "CLOB" +int(112) +int(0) +int(0) +int(4000) +bool(true) +string(6) "STRING" +string(8) "VARCHAR2" +int(1) +int(0) +int(0) +int(10) +Done diff --git a/ext/oci8/tests/field_funcs1.phpt b/ext/oci8/tests/field_funcs1.phpt index 0b4ad76b3..c14ee8957 100644 --- a/ext/oci8/tests/field_funcs1.phpt +++ b/ext/oci8/tests/field_funcs1.phpt @@ -1,5 +1,5 @@ --TEST-- -oci_field_*() family +oci_field_*() family: error cases --SKIPIF-- <?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> --FILE-- @@ -14,40 +14,25 @@ $stmtarray = array( "create table field_funcs1_tab (id number, value number)", "insert into field_funcs1_tab (id, value) values (1,1)", "insert into field_funcs1_tab (id, value) values (1,1)", - "insert into field_funcs1_tab (id, value) values (1,1)", + "insert into field_funcs1_tab (id, value) values (1,1)" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - $r = @oci_execute($s); - if (!$r) { - $m = oci_error($s); - if (!in_array($m['code'], array( // ignore expected errors - 942 // table or view does not exist - ))) { - echo $stmt . PHP_EOL . $m['message'] . PHP_EOL; - } - } -} - -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); // Run Test if (!($s = oci_parse($c, "select * from field_funcs1_tab"))) { - die("oci_parse(select) failed!\n"); + die("oci_parse(select) failed!\n"); } if (!oci_execute($s)) { - die("oci_execute(select) failed!\n"); + die("oci_execute(select) failed!\n"); } $row = oci_fetch_array($s, OCI_NUM + OCI_RETURN_NULLS + OCI_RETURN_LOBS); var_dump($row); +echo "Test 1\n"; var_dump(oci_field_is_null($s, -1)); var_dump(oci_field_name($s, -1)); var_dump(oci_field_type($s, -1)); @@ -56,6 +41,7 @@ var_dump(oci_field_scale($s, -1)); var_dump(oci_field_precision($s, -1)); var_dump(oci_field_size($s, -1)); +echo "Test 2\n"; var_dump(oci_field_is_null($s, "none")); var_dump(oci_field_name($s, "none")); var_dump(oci_field_type($s, "none")); @@ -64,6 +50,7 @@ var_dump(oci_field_scale($s, "none")); var_dump(oci_field_precision($s, "none")); var_dump(oci_field_size($s, "none")); +echo "Test 3\n"; var_dump(oci_field_is_null($c, -1)); var_dump(oci_field_name($c, -1)); var_dump(oci_field_type($c, -1)); @@ -72,6 +59,7 @@ var_dump(oci_field_scale($c, -1)); var_dump(oci_field_precision($c, -1)); var_dump(oci_field_size($c, -1)); +echo "Test 4\n"; var_dump(oci_field_is_null($s, array())); var_dump(oci_field_name($s, array())); var_dump(oci_field_type($s, array())); @@ -89,10 +77,7 @@ $stmtarray = array( "drop table field_funcs1_tab" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); echo "Done\n"; @@ -104,6 +89,7 @@ array(2) { [1]=> %unicode|string%(1) "1" } +Test 1 Warning: oci_field_is_null(): Invalid column index "-1" in %s on line %d bool(false) @@ -125,6 +111,7 @@ bool(false) Warning: oci_field_size(): Invalid column index "-1" in %s on line %d bool(false) +Test 2 Warning: oci_field_is_null(): Invalid column name "none" in %s on line %d bool(false) @@ -146,6 +133,7 @@ bool(false) Warning: oci_field_size(): Invalid column name "none" in %s on line %d bool(false) +Test 3 Warning: oci_field_is_null(): supplied resource is not a valid oci8 statement resource in %s on line %d bool(false) @@ -167,6 +155,7 @@ bool(false) Warning: oci_field_size(): supplied resource is not a valid oci8 statement resource in %s on line %d bool(false) +Test 4 Warning: oci_field_is_null(): Invalid column index "0" in %s on line %d bool(false) diff --git a/ext/oci8/tests/field_funcs3.phpt b/ext/oci8/tests/field_funcs3.phpt new file mode 100644 index 000000000..e51949cf2 --- /dev/null +++ b/ext/oci8/tests/field_funcs3.phpt @@ -0,0 +1,99 @@ +--TEST-- +oci_field_*() family: basic column types +--SKIPIF-- +<?php if (!extension_loaded('oci8')) die ("skip no oci8 extension"); ?> +--FILE-- +<?php + +require(dirname(__FILE__)."/connect.inc"); + +// Initialization +$stmtarray = array( + "drop table field_funcs3_tab", + "create table field_funcs3_tab(c1_c char(2), c2_v varchar2(2), c3_n number, c4_d date)", + "insert into field_funcs3_tab values ('c1', 'c2', 3, '01-Jan-2010')" +); + +$v = oci_server_version($c); +if (strpos($v, 'Oracle TimesTen') === false) { + oci8_test_sql_execute($c, array("alter session set nls_date_format = 'DD-MON-YYYY'")); +} +oci8_test_sql_execute($c, $stmtarray); + +// Run Test + +$select_sql = "select * from field_funcs3_tab"; + +if (!($s = oci_parse($c, $select_sql))) { + die("oci_parse(select) failed!\n"); +} + +if (!oci_execute($s)) { + die("oci_execute(select) failed!\n"); +} + +$row = oci_fetch_array($s, OCI_NUM + OCI_RETURN_NULLS + OCI_RETURN_LOBS); +var_dump($row); + +foreach ($row as $num => $field) { + $num++; + var_dump(oci_field_is_null($s, $num)); + var_dump(oci_field_name($s, $num)); + var_dump(oci_field_type($s, $num)); + var_dump(oci_field_type_raw($s, $num)); + var_dump(oci_field_scale($s, $num)); + var_dump(oci_field_precision($s, $num)); + var_dump(oci_field_size($s, $num)); +} + +// Clean up + +$stmtarray = array( + "drop table field_funcs3_tab" +); + +oci8_test_sql_execute($c, $stmtarray); + +?> +===DONE=== +<?php exit(0); ?> +--EXPECTF-- +array(4) { + [0]=> + string(2) "c1" + [1]=> + string(2) "c2" + [2]=> + string(1) "3" + [3]=> + string(1%r[19]%r) "%r(01-JAN-2010|0001-01-20 10:00:00)%r" +} +bool(false) +string(4) "C1_C" +string(4) "CHAR" +int(96) +int(0) +int(0) +int(2) +bool(false) +string(4) "C2_V" +string(8) "VARCHAR2" +int(1) +int(0) +int(0) +int(2) +bool(false) +string(4) "C3_N" +string(6) "NUMBER" +int(2) +int(-127) +int(0) +int(22) +bool(false) +string(4) "C4_D" +string(4) "DATE" +int(12) +int(0) +int(0) +int(7) +===DONE=== diff --git a/ext/oci8/tests/field_funcs_old.phpt b/ext/oci8/tests/field_funcs_old.phpt index e0d24cd7b..629c5508f 100644 --- a/ext/oci8/tests/field_funcs_old.phpt +++ b/ext/oci8/tests/field_funcs_old.phpt @@ -1,7 +1,10 @@ --TEST-- ocicolumn*() family --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/lob_001.phpt b/ext/oci8/tests/lob_001.phpt Binary files differindex cbcb92e7e..cb94bfae1 100644 --- a/ext/oci8/tests/lob_001.phpt +++ b/ext/oci8/tests/lob_001.phpt diff --git a/ext/oci8/tests/lob_002.phpt b/ext/oci8/tests/lob_002.phpt index ebbef1815..7417f9c12 100644 --- a/ext/oci8/tests/lob_002.phpt +++ b/ext/oci8/tests/lob_002.phpt @@ -1,21 +1,25 @@ --TEST-- oci_lob_write() and friends (with errors) --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php - -require dirname(__FILE__).'/connect.inc'; -require dirname(__FILE__).'/create_table.inc'; - -$ora_sql = "INSERT INTO - ".$schema.$table_name." (blob) - VALUES (empty_blob()) - RETURNING - blob - INTO :v_blob "; - -$statement = oci_parse($c,$ora_sql); + +require(dirname(__FILE__).'/connect.inc'); + +// Initialization + +$stmtarray = array( + "drop table lob_002_tab", + "create table lob_002_tab (id number, b1 BLOB)", +); + +oci8_test_sql_execute($c, $stmtarray); + +$statement = oci_parse($c, "insert into lob_002_tab (id, b1) values (1, empty_blob()) returning b1 INTO :v_blob "); $blob = oci_new_descriptor($c,OCI_D_LOB); oci_bind_by_name($statement,":v_blob", $blob,-1,OCI_B_BLOB); oci_execute($statement, OCI_DEFAULT); @@ -32,7 +36,7 @@ var_dump($blob->flush()); oci_commit($c); -$select_sql = "SELECT blob FROM ".$schema.$table_name.""; +$select_sql = "select b1 from lob_002_tab where id = 1"; $s = oci_parse($c, $select_sql); oci_execute($s); @@ -40,12 +44,17 @@ $row = oci_fetch_array($s, OCI_RETURN_LOBS); var_dump(strlen($row[0])); +// Cleanup -require dirname(__FILE__).'/drop_table.inc'; +$stmtarray = array( + "drop table lob_002_tab" +); -echo "Done\n"; +oci8_test_sql_execute($c, $stmtarray); ?> +===DONE=== +<?php exit(0); ?> --EXPECTF-- object(OCI-Lob)#%d (1) { ["descriptor"]=> @@ -63,4 +72,4 @@ Warning: OCI-Lob::seek() expects parameter 1 to be long, string given in %slob_0 NULL bool(false) int(40004) -Done +===DONE=== diff --git a/ext/oci8/tests/lob_003.phpt b/ext/oci8/tests/lob_003.phpt Binary files differindex 8a492d16c..4775444e2 100644 --- a/ext/oci8/tests/lob_003.phpt +++ b/ext/oci8/tests/lob_003.phpt diff --git a/ext/oci8/tests/lob_004.phpt b/ext/oci8/tests/lob_004.phpt index 32de4a012..3f583c67a 100644 --- a/ext/oci8/tests/lob_004.phpt +++ b/ext/oci8/tests/lob_004.phpt @@ -1,7 +1,10 @@ --TEST-- oci_lob_seek()/rewind()/append() --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/lob_005.phpt b/ext/oci8/tests/lob_005.phpt index e1ac6e534..a1e3c3bd8 100644 --- a/ext/oci8/tests/lob_005.phpt +++ b/ext/oci8/tests/lob_005.phpt @@ -1,7 +1,10 @@ --TEST-- oci_lob_is_equal() --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/lob_006.phpt b/ext/oci8/tests/lob_006.phpt Binary files differindex 3192ebc90..ae6b37c96 100644 --- a/ext/oci8/tests/lob_006.phpt +++ b/ext/oci8/tests/lob_006.phpt diff --git a/ext/oci8/tests/lob_007.phpt b/ext/oci8/tests/lob_007.phpt index 1fe63092c..1ca236ea2 100644 --- a/ext/oci8/tests/lob_007.phpt +++ b/ext/oci8/tests/lob_007.phpt @@ -1,7 +1,10 @@ --TEST-- oci_lob_write()/size()/load() --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/lob_008.phpt b/ext/oci8/tests/lob_008.phpt index a36bb4a34..6f4cc0564 100644 --- a/ext/oci8/tests/lob_008.phpt +++ b/ext/oci8/tests/lob_008.phpt @@ -1,7 +1,10 @@ --TEST-- oci_lob_write()/read()/eof() --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/lob_009.phpt b/ext/oci8/tests/lob_009.phpt index b9f740112..4702e61ac 100644 --- a/ext/oci8/tests/lob_009.phpt +++ b/ext/oci8/tests/lob_009.phpt @@ -1,7 +1,10 @@ --TEST-- oci_lob_import()/read() --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/lob_010.phpt b/ext/oci8/tests/lob_010.phpt index 59f3e145f..3361c4b9b 100644 --- a/ext/oci8/tests/lob_010.phpt +++ b/ext/oci8/tests/lob_010.phpt @@ -1,7 +1,10 @@ --TEST-- oci_lob_save() --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/lob_011.phpt b/ext/oci8/tests/lob_011.phpt index b074e1730..7580d4caf 100644 --- a/ext/oci8/tests/lob_011.phpt +++ b/ext/oci8/tests/lob_011.phpt @@ -1,7 +1,10 @@ --TEST-- oci_lob_copy() --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/lob_012.phpt b/ext/oci8/tests/lob_012.phpt index 2061969bc..99b971a0b 100644 --- a/ext/oci8/tests/lob_012.phpt +++ b/ext/oci8/tests/lob_012.phpt @@ -1,7 +1,10 @@ --TEST-- oci_lob_export() --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/lob_013.phpt b/ext/oci8/tests/lob_013.phpt index c56de5619..556e56dc3 100644 --- a/ext/oci8/tests/lob_013.phpt +++ b/ext/oci8/tests/lob_013.phpt @@ -1,7 +1,10 @@ --TEST-- lob buffering --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/lob_014.phpt b/ext/oci8/tests/lob_014.phpt index 1ba29ee64..2a1a6bed6 100644 --- a/ext/oci8/tests/lob_014.phpt +++ b/ext/oci8/tests/lob_014.phpt @@ -1,7 +1,10 @@ --TEST-- oci_lob_free()/close() --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/lob_015.phpt b/ext/oci8/tests/lob_015.phpt index 297d5b497..b4a19684a 100644 --- a/ext/oci8/tests/lob_015.phpt +++ b/ext/oci8/tests/lob_015.phpt @@ -1,7 +1,10 @@ --TEST-- various tests with wrong param count --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php @@ -45,7 +48,7 @@ Warning: oci_bind_by_name() expects at least 3 parameters, 2 given in %s on line Warning: oci_bind_by_name() expects at least 3 parameters, 1 given in %s on line %d -Warning: oci_execute(): ORA-00932: inconsistent datatypes: expected NUMBER got BLOB in %s on line %d +Warning: oci_execute(): ORA-00932: %s NUMBER %s BLOB in %s on line %d object(OCI-Lob)#%d (1) { ["descriptor"]=> resource(%d) of type (oci8 descriptor) diff --git a/ext/oci8/tests/lob_016.phpt b/ext/oci8/tests/lob_016.phpt index 642e7195e..e0f78e6a9 100644 --- a/ext/oci8/tests/lob_016.phpt +++ b/ext/oci8/tests/lob_016.phpt @@ -1,7 +1,10 @@ --TEST-- returning multiple lobs --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/lob_017.phpt b/ext/oci8/tests/lob_017.phpt index ed12cc468..d27a09052 100644 --- a/ext/oci8/tests/lob_017.phpt +++ b/ext/oci8/tests/lob_017.phpt @@ -1,7 +1,10 @@ --TEST-- returning multiple lobs (using persistent connection) --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/lob_018.phpt b/ext/oci8/tests/lob_018.phpt index 35cec4bd7..352cd5033 100644 --- a/ext/oci8/tests/lob_018.phpt +++ b/ext/oci8/tests/lob_018.phpt @@ -1,21 +1,27 @@ --TEST-- fetching the same lob several times --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php -require dirname(__FILE__).'/connect.inc'; +require(dirname(__FILE__).'/connect.inc'); + +// Initialization + +$stmtarray = array( + "drop table lob_018_tab", + "create table lob_018_tab (mykey number, lob_1 clob)", +); -$drop = "DROP table lob_test"; -$statement = oci_parse($c, $drop); -@oci_execute($statement); +oci8_test_sql_execute($c, $stmtarray); -$create = "CREATE table lob_test(mykey NUMBER, lob_1 CLOB)"; -$statement = oci_parse($c, $create); -oci_execute($statement); +echo "Test 1\n"; -$init = "INSERT INTO lob_test (mykey, lob_1) VALUES(1, EMPTY_CLOB()) RETURNING lob_1 INTO :mylob"; +$init = "insert into lob_018_tab (mykey, lob_1) values(1, empty_clob()) returning lob_1 into :mylob"; $statement = oci_parse($c, $init); $clob = oci_new_descriptor($c, OCI_D_LOB); oci_bind_by_name($statement, ":mylob", $clob, -1, OCI_B_CLOB); @@ -24,7 +30,7 @@ $clob->save("data"); oci_commit($c); -$init = "INSERT INTO lob_test (mykey, lob_1) VALUES(2, EMPTY_CLOB()) RETURNING lob_1 INTO :mylob"; +$init = "insert into lob_018_tab (mykey, lob_1) values(2, empty_clob()) returning lob_1 into :mylob"; $statement = oci_parse($c, $init); $clob = oci_new_descriptor($c, OCI_D_LOB); oci_bind_by_name($statement, ":mylob", $clob, -1, OCI_B_CLOB); @@ -34,7 +40,18 @@ $clob->save("long data"); oci_commit($c); -$query = 'SELECT * FROM lob_test ORDER BY mykey ASC'; +$query = 'select * from lob_018_tab order by mykey asc'; +$statement = oci_parse ($c, $query); +oci_execute($statement, OCI_DEFAULT); + +while ($row = oci_fetch_array($statement, OCI_ASSOC)) { + $result = $row['LOB_1']->load(); + var_dump($result); +} + +echo "Test 2\n"; + +$query = 'select * from lob_018_tab order by mykey desc'; $statement = oci_parse ($c, $query); oci_execute($statement, OCI_DEFAULT); @@ -43,7 +60,18 @@ while ($row = oci_fetch_array($statement, OCI_ASSOC)) { var_dump($result); } -$query = 'SELECT * FROM lob_test ORDER BY mykey DESC'; +echo "Test 3 - bind with SQLT_CLOB (an alias for OCI_B_CLOB)\n"; + +$init = "insert into lob_018_tab (mykey, lob_1) values(3, empty_clob()) returning lob_1 into :mylob"; +$statement = oci_parse($c, $init); +$clob = oci_new_descriptor($c, OCI_D_LOB); +oci_bind_by_name($statement, ":mylob", $clob, -1, SQLT_CLOB); +oci_execute($statement, OCI_DEFAULT); +$clob->save("more stuff"); + +oci_commit($c); + +$query = 'select * from lob_018_tab where mykey = 3'; $statement = oci_parse ($c, $query); oci_execute($statement, OCI_DEFAULT); @@ -52,16 +80,24 @@ while ($row = oci_fetch_array($statement, OCI_ASSOC)) { var_dump($result); } -$drop = "DROP table lob_test"; -$statement = oci_parse($c, $drop); -@oci_execute($statement); +// Cleanup + +$stmtarray = array( + "drop table lob_018_tab" +); -echo "Done\n"; +oci8_test_sql_execute($c, $stmtarray); ?> +===DONE=== +<?php exit(0); ?> --EXPECTF-- +Test 1 string(4) "data" string(9) "long data" +Test 2 string(9) "long data" string(4) "data" -Done +Test 3 - bind with SQLT_CLOB (an alias for OCI_B_CLOB) +string(10) "more stuff" +===DONE=== diff --git a/ext/oci8/tests/lob_019.phpt b/ext/oci8/tests/lob_019.phpt Binary files differindex fb9a3c818..19c21f4a7 100644 --- a/ext/oci8/tests/lob_019.phpt +++ b/ext/oci8/tests/lob_019.phpt diff --git a/ext/oci8/tests/lob_020.phpt b/ext/oci8/tests/lob_020.phpt Binary files differindex 6564dede9..3faa89ff7 100644 --- a/ext/oci8/tests/lob_020.phpt +++ b/ext/oci8/tests/lob_020.phpt diff --git a/ext/oci8/tests/lob_021.phpt b/ext/oci8/tests/lob_021.phpt index 0ae6b377a..b7489271b 100644 --- a/ext/oci8/tests/lob_021.phpt +++ b/ext/oci8/tests/lob_021.phpt @@ -1,7 +1,10 @@ --TEST-- oci_lob_free()/close() --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/lob_022.phpt b/ext/oci8/tests/lob_022.phpt index 5fb9dfab0..94d175b3b 100644 --- a/ext/oci8/tests/lob_022.phpt +++ b/ext/oci8/tests/lob_022.phpt @@ -1,7 +1,10 @@ --TEST-- fetching the same lob several times --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/lob_023.phpt b/ext/oci8/tests/lob_023.phpt index 0c352956d..6416fb45a 100644 --- a/ext/oci8/tests/lob_023.phpt +++ b/ext/oci8/tests/lob_023.phpt @@ -1,7 +1,10 @@ --TEST-- oci_lob_import()/read() --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/lob_024.phpt b/ext/oci8/tests/lob_024.phpt index 9a7f53240..ed9b56727 100644 --- a/ext/oci8/tests/lob_024.phpt +++ b/ext/oci8/tests/lob_024.phpt @@ -1,7 +1,10 @@ --TEST-- oci_lob_load() --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/lob_025.phpt b/ext/oci8/tests/lob_025.phpt index 5b5e845a7..1566944cb 100644 --- a/ext/oci8/tests/lob_025.phpt +++ b/ext/oci8/tests/lob_025.phpt @@ -1,7 +1,10 @@ --TEST-- oci_lob_read() tests --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/lob_026.phpt b/ext/oci8/tests/lob_026.phpt index 157d78a2b..aa4c254dd 100644 --- a/ext/oci8/tests/lob_026.phpt +++ b/ext/oci8/tests/lob_026.phpt @@ -1,7 +1,10 @@ --TEST-- oci_lob_seek()/rewind()/append() --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/lob_027.phpt b/ext/oci8/tests/lob_027.phpt index 8b49b1ac5..49850635c 100644 --- a/ext/oci8/tests/lob_027.phpt +++ b/ext/oci8/tests/lob_027.phpt @@ -1,7 +1,10 @@ --TEST-- oci_lob_truncate() --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/lob_028.phpt b/ext/oci8/tests/lob_028.phpt index 8ac2da3a3..8da7a8af8 100644 --- a/ext/oci8/tests/lob_028.phpt +++ b/ext/oci8/tests/lob_028.phpt @@ -1,7 +1,10 @@ --TEST-- Test descriptor types for oci_new_descriptor() --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/lob_029.phpt b/ext/oci8/tests/lob_029.phpt index 6826f36cd..85b230e90 100644 --- a/ext/oci8/tests/lob_029.phpt +++ b/ext/oci8/tests/lob_029.phpt @@ -1,16 +1,24 @@ --TEST-- reading/writing BFILE LOBs --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); -include "details.inc"; -if (empty($oracle_on_localhost)) die("skip this test won't work with remote Oracle"); +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +ob_start(); +phpinfo(INFO_MODULES); +$phpinfo = ob_get_clean(); +if (preg_match('/Compile-time ORACLE_HOME/', $phpinfo) !== 1) { + // Assume building PHP with an ORACLE_HOME means the tested DB is on the same machine as PHP + die("skip this test won't work with remote Oracle"); +} +if (substr(PHP_OS, 0, 3) == 'WIN') die("skip Test script not ported to Windows"); ?> --FILE-- <?php -require dirname(__FILE__).'/connect.inc'; +require(dirname(__FILE__).'/connect.inc'); -$realdirname = dirname(__FILE__); +$realdirname = "/tmp"; // Use /tmp because a local dir can give ORA-22288 depending on perms $realfilename1 = "oci8bfiletest1.txt"; $fullname1 = $realdirname."/".$realfilename1; $realfilename2 = "oci8bfiletest2.txt"; diff --git a/ext/oci8/tests/lob_030.phpt b/ext/oci8/tests/lob_030.phpt index 86b2956a5..d7fd21b96 100644 --- a/ext/oci8/tests/lob_030.phpt +++ b/ext/oci8/tests/lob_030.phpt @@ -1,7 +1,10 @@ --TEST-- Test piecewise fetch of CLOBs equal to, and larger than PHP_OCI_LOB_BUFFER_SIZE --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/lob_031.phpt b/ext/oci8/tests/lob_031.phpt index a27d53bb3..39d27fd4a 100644 --- a/ext/oci8/tests/lob_031.phpt +++ b/ext/oci8/tests/lob_031.phpt @@ -1,7 +1,10 @@ --TEST-- Test LOB->read(), LOB->seek() and LOB->tell() with nul bytes in data --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/lob_032.phpt b/ext/oci8/tests/lob_032.phpt index 5d6ff6ec9..97b63c984 100644 --- a/ext/oci8/tests/lob_032.phpt +++ b/ext/oci8/tests/lob_032.phpt @@ -1,7 +1,10 @@ --TEST-- oci_lob_write() and friends --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/lob_033.phpt b/ext/oci8/tests/lob_033.phpt index 5647cd9a4..cdce2d086 100644 --- a/ext/oci8/tests/lob_033.phpt +++ b/ext/oci8/tests/lob_033.phpt @@ -1,7 +1,10 @@ --TEST-- various oci_lob_write() error messages --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/lob_034.phpt b/ext/oci8/tests/lob_034.phpt index 6bf4058e7..7561f64ff 100644 --- a/ext/oci8/tests/lob_034.phpt +++ b/ext/oci8/tests/lob_034.phpt @@ -1,7 +1,10 @@ --TEST-- lob buffering - 2 --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/lob_035.phpt b/ext/oci8/tests/lob_035.phpt index 6e1f5a735..37e010ce7 100644 --- a/ext/oci8/tests/lob_035.phpt +++ b/ext/oci8/tests/lob_035.phpt @@ -1,7 +1,10 @@ --TEST-- oci_lob_copy() - 2 --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/lob_036.phpt b/ext/oci8/tests/lob_036.phpt index e72c1cf08..060b1713c 100644 --- a/ext/oci8/tests/lob_036.phpt +++ b/ext/oci8/tests/lob_036.phpt @@ -1,7 +1,10 @@ --TEST-- Exercise cleanup code when LOB buffering is on --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/lob_037.phpt b/ext/oci8/tests/lob_037.phpt index 228f5e812..75db589aa 100644 --- a/ext/oci8/tests/lob_037.phpt +++ b/ext/oci8/tests/lob_037.phpt @@ -1,7 +1,10 @@ --TEST-- Fetching two different lobs and using them after fetch --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/lob_038.phpt b/ext/oci8/tests/lob_038.phpt index 91dac66c0..34b7b48b6 100644 --- a/ext/oci8/tests/lob_038.phpt +++ b/ext/oci8/tests/lob_038.phpt @@ -1,7 +1,10 @@ --TEST-- Array fetch CLOB and BLOB --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/lob_039.phpt b/ext/oci8/tests/lob_039.phpt index 5675f5a92..02d057e2b 100644 --- a/ext/oci8/tests/lob_039.phpt +++ b/ext/oci8/tests/lob_039.phpt @@ -1,7 +1,10 @@ --TEST-- Test CLOB->write() for multiple inserts --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/lob_040.phpt b/ext/oci8/tests/lob_040.phpt index 3f8a73dc5..0a29dc1b9 100644 --- a/ext/oci8/tests/lob_040.phpt +++ b/ext/oci8/tests/lob_040.phpt @@ -1,7 +1,10 @@ --TEST-- Bug #37706 (Test LOB locator reuse. Extends simple test of lob_037.phpt) --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/lob_041.phpt b/ext/oci8/tests/lob_041.phpt index d04b43606..aa1ea98a5 100644 --- a/ext/oci8/tests/lob_041.phpt +++ b/ext/oci8/tests/lob_041.phpt @@ -1,7 +1,10 @@ --TEST-- Check LOBS are valid after statement free --SKIPIF-- -<?php if (!extension_loaded('oci8')) die ("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php @@ -15,10 +18,7 @@ $stmtarray = array( "INSERT INTO lob_041_tab VALUES('test data')" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - @oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); echo "Test 1 - explicit statement close\n"; @@ -60,12 +60,7 @@ $stmtarray = array( "DROP table lob_041_tab" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - @oci_execute($s); -} - -oci_close($c); +oci8_test_sql_execute($c, $stmtarray); ?> diff --git a/ext/oci8/tests/lob_042.phpt b/ext/oci8/tests/lob_042.phpt index 25309d6fc..264c61045 100644 --- a/ext/oci8/tests/lob_042.phpt +++ b/ext/oci8/tests/lob_042.phpt @@ -1,7 +1,10 @@ --TEST-- Check various LOB error messages --SKIPIF-- -<?php if (!extension_loaded('oci8')) die ("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/lob_043.phpt b/ext/oci8/tests/lob_043.phpt index 0280ef6de..ade79a200 100644 --- a/ext/oci8/tests/lob_043.phpt +++ b/ext/oci8/tests/lob_043.phpt @@ -1,8 +1,9 @@ --TEST-- Bug #49560 (LOB resource destructor and refcount test) --SKIPIF-- -<?php if (!extension_loaded('oci8')) die ("skip no oci8 extension"); -require(dirname(__FILE__).'/details.inc'); +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); if ($stress_test !== true) die ('skip Slow test not run when $stress_test is FALSE'); ?> --FILE-- @@ -22,21 +23,7 @@ $stmtarray = array( end;", ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - $r = @oci_execute($s); - if (!$r) { - $m = oci_error($s); - if (!in_array($m['code'], array( // ignore expected errors - 942 // table or view does not exist - , 2289 // sequence does not exist - , 4080 // trigger does not exist - , 38802 // edition does not exist - ))) { - echo $stmt . PHP_EOL . $m['message'] . PHP_EOL; - } - } -} +oci8_test_sql_execute($c, $stmtarray); // Run Test @@ -86,10 +73,7 @@ $stmtarray = array( "drop table lob_043_tab" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); oci_close($c); diff --git a/ext/oci8/tests/lob_044.phpt b/ext/oci8/tests/lob_044.phpt new file mode 100644 index 000000000..28599cc33 --- /dev/null +++ b/ext/oci8/tests/lob_044.phpt @@ -0,0 +1,73 @@ +--TEST-- +oci_lob_truncate() with default parameter value +--SKIPIF-- +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> +--FILE-- +<?php + +require(dirname(__FILE__).'/connect.inc'); + +// Initialization + +$stmtarray = array( + "drop table lob_044_tab", + "create table lob_044_tab (blob BLOB)", +); + +oci8_test_sql_execute($c, $stmtarray); + + +// Run Test + +echo "Test 1 - truncate on insert\n"; + +$s = oci_parse($c, "INSERT INTO lob_044_tab (blob) VALUES (empty_blob()) RETURNING blob INTO :v_blob "); +$blob = oci_new_descriptor($c, OCI_D_LOB); +oci_bind_by_name($s,":v_blob", $blob, -1, OCI_B_BLOB); +oci_execute($s, OCI_DEFAULT); + +var_dump($blob->write("this is a biiiig faaat test string. why are you reading it, I wonder? =)")); +var_dump($blob->seek(0)); +var_dump($blob->read(10000)); +var_dump($blob->truncate()); +var_dump($blob->seek(0)); +var_dump($blob->read(10000)); + +oci_commit($c); + + +// Read it back + +echo "\nTest 2 - read it back\n"; + +$s = oci_parse($c, "SELECT blob FROM lob_044_tab FOR UPDATE"); +oci_execute($s, OCI_DEFAULT); +$row = oci_fetch_array($s); +var_dump($row[0]->read(10000)); + +// Clean up + +$stmtarray = array( + "drop table lob_044_tab" +); + +oci8_test_sql_execute($c, $stmtarray); + +?> +===DONE=== +<?php exit(0); ?> +--EXPECTF-- +Test 1 - truncate on insert +int(72) +bool(true) +string(72) "this is a biiiig faaat test string. why are you reading it, I wonder? =)" +bool(true) +bool(true) +string(0) "" + +Test 2 - read it back +string(0) "" +===DONE=== diff --git a/ext/oci8/tests/lob_aliases.phpt b/ext/oci8/tests/lob_aliases.phpt index faa59bf12..3ece9a589 100644 --- a/ext/oci8/tests/lob_aliases.phpt +++ b/ext/oci8/tests/lob_aliases.phpt @@ -1,7 +1,10 @@ --TEST-- LOB method aliases --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/lob_null.phpt b/ext/oci8/tests/lob_null.phpt index 227ebb89f..be3e53453 100644 --- a/ext/oci8/tests/lob_null.phpt +++ b/ext/oci8/tests/lob_null.phpt @@ -1,7 +1,10 @@ --TEST-- Test null data for CLOBs --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/lob_temp.phpt b/ext/oci8/tests/lob_temp.phpt index cad2d3905..9a07fb5ac 100644 --- a/ext/oci8/tests/lob_temp.phpt +++ b/ext/oci8/tests/lob_temp.phpt @@ -1,7 +1,10 @@ --TEST-- temporary lobs --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/lob_temp1.phpt b/ext/oci8/tests/lob_temp1.phpt index 2482d65f6..e27ea6d47 100644 --- a/ext/oci8/tests/lob_temp1.phpt +++ b/ext/oci8/tests/lob_temp1.phpt @@ -1,7 +1,10 @@ --TEST-- closing temporary lobs --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/null_byte_2.phpt b/ext/oci8/tests/null_byte_2.phpt index b4c9b61ad..01fb87235 100644 --- a/ext/oci8/tests/null_byte_2.phpt +++ b/ext/oci8/tests/null_byte_2.phpt @@ -1,7 +1,10 @@ --TEST-- Null bytes in SQL statements --SKIPIF-- -<?php if (!extension_loaded('oci8')) die ("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --INI-- display_errors = On error_reporting = E_WARNING @@ -19,11 +22,6 @@ oci_execute($s); oci_fetch_all($s, $res); var_dump($res); -echo "Test 2: Invalid use of a null byte\n"; - -$s = oci_parse($c, "select * from du\0al"); -oci_execute($s); - echo "Test 3: Using a null byte in a bind variable name\n"; $s = oci_parse($c, "select * from dual where :bv = 1"); @@ -31,14 +29,6 @@ $bv = 1; oci_bind_by_name($s, ":bv\0:bv", $bv); oci_execute($s); -echo "Test 4: Using a null byte in a bind variable value causing WHERE clause to fail\n"; - -$s = oci_parse($c, "select * from dual where :bv = 'abc'"); -$bv = 'abc\0abc'; -oci_bind_by_name($s, ":bv", $bv); -oci_execute($s); -oci_fetch_all($s, $res); -var_dump($res); ?> ===DONE=== @@ -52,18 +42,9 @@ array(1) { string(1) "X" } } -Test 2: Invalid use of a null byte - -Warning: oci_execute(): ORA-00942: %s in %snull_byte_2.php on line %d Test 3: Using a null byte in a bind variable name Warning: oci_bind_by_name(): ORA-01036: %s in %snull_byte_2.php on line %d Warning: oci_execute(): ORA-01008: %s in %snull_byte_2.php on line %d -Test 4: Using a null byte in a bind variable value causing WHERE clause to fail -array(1) { - ["DUMMY"]=> - array(0) { - } -} ===DONE=== diff --git a/ext/oci8/tests/null_byte_3.phpt b/ext/oci8/tests/null_byte_3.phpt new file mode 100644 index 000000000..73d5c26c0 --- /dev/null +++ b/ext/oci8/tests/null_byte_3.phpt @@ -0,0 +1,42 @@ +--TEST-- +Null bytes in SQL statements +--SKIPIF-- +<?php if (!extension_loaded('oci8')) die ("skip no oci8 extension"); ?> +--INI-- +display_errors = On +error_reporting = E_WARNING +--FILE-- +<?php + +require(dirname(__FILE__).'/connect.inc'); + +// Run Test + +echo "Test 1: Invalid use of a null byte\n"; + +$s = oci_parse($c, "select * from du\0al"); +oci_execute($s); + +echo "Test 2: Using a null byte in a bind variable value causing WHERE clause to fail\n"; + +$s = oci_parse($c, "select * from dual where :bv = 'abc'"); +$bv = 'abc\0abc'; +oci_bind_by_name($s, ":bv", $bv); +oci_execute($s); +oci_fetch_all($s, $res); +var_dump($res); + +?> +===DONE=== +<?php exit(0); ?> +--EXPECTF-- +Test 1: Invalid use of a null byte + +Warning: oci_execute(): ORA-00942: %s in %snull_byte_3.php on line %d +Test 2: Using a null byte in a bind variable value causing WHERE clause to fail +array(1) { + ["DUMMY"]=> + array(0) { + } +} +===DONE=== diff --git a/ext/oci8/tests/num.phpt b/ext/oci8/tests/num.phpt index e9dc6a8ac..0fe85ccdd 100644 --- a/ext/oci8/tests/num.phpt +++ b/ext/oci8/tests/num.phpt @@ -14,23 +14,7 @@ $stmtarray = array( "create table num_tab (id number, value number)", ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - $r = @oci_execute($s); - if (!$r) { - $m = oci_error($s); - if (!in_array($m['code'], array( // ignore expected errors - 942 // table or view does not exist - ))) { - echo $stmt . PHP_EOL . $m['message'] . PHP_EOL; - } - } -} - -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); // Run Test @@ -181,10 +165,7 @@ $stmtarray = array( "drop table num_tab" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); echo "Done\n"; diff --git a/ext/oci8/tests/oci_execute_segfault.phpt b/ext/oci8/tests/oci_execute_segfault.phpt index 9ba7d770f..59eb8013e 100644 --- a/ext/oci8/tests/oci_execute_segfault.phpt +++ b/ext/oci8/tests/oci_execute_segfault.phpt @@ -1,7 +1,10 @@ --TEST-- oci_execute() segfault after repeated bind of LOB descriptor --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/password.phpt b/ext/oci8/tests/password.phpt index 7133d8bae..1738702cb 100644 --- a/ext/oci8/tests/password.phpt +++ b/ext/oci8/tests/password.phpt @@ -11,20 +11,16 @@ if ($test_drcp) die("skip password change not supported in DRCP Mode"); --FILE-- <?php -require(dirname(__FILE__)."/details.inc"); +require(dirname(__FILE__)."/connect.inc"); + +$stmtarray = array( + "drop user testuser cascade", + "create user testuser identified by testuserpwd", + "grant connect, create session to testuser" +); + +oci8_test_sql_execute($c, $stmtarray); -// Create a user we can stuff around with and not affect subsequent tests -$c0 = oci_connect($user, $password, $dbase); -$stmts = array( - "drop user testuser", - "begin - execute immediate 'create user testuser identified by testuserpwd'; - execute immediate 'grant connect, create session to testuser'; - end;"); -foreach ($stmts as $sql) { - $s = oci_parse($c0, $sql); - @oci_execute($s); -} // Connect and change the password $c1 = oci_connect("testuser", "testuserpwd", $dbase); @@ -62,16 +58,19 @@ else { var_dump($c2); } -// Clean up -oci_close($c1); -oci_close($c2); -oci_close($c3); +echo "Done\n"; + +?> +--CLEAN-- +<?php -// Clean up -$s = oci_parse($c0, "drop user cascade testuser"); -@oci_execute($s); +require(dirname(__FILE__)."/connect.inc"); -echo "Done\n"; +$stmtarray = array( + "drop user testuser cascade" +); + +oci8_test_sql_execute($c, $stmtarray); ?> --EXPECTF-- diff --git a/ext/oci8/tests/password_2.phpt b/ext/oci8/tests/password_2.phpt index 71423e717..ceba0bba8 100644 --- a/ext/oci8/tests/password_2.phpt +++ b/ext/oci8/tests/password_2.phpt @@ -11,20 +11,15 @@ if ($test_drcp) die("skip password change not supported in DRCP Mode"); --FILE-- <?php -require(dirname(__FILE__)."/details.inc"); +require(dirname(__FILE__)."/connect.inc"); -// Create a user we can stuff around with and not affect subsequent tests -$c0 = oci_connect($user, $password, $dbase); -$stmts = array( - "drop user testuser", - "begin - execute immediate 'create user testuser identified by testuserpwd'; - execute immediate 'grant connect, create session to testuser'; - end;"); -foreach ($stmts as $sql) { - $s = oci_parse($c0, $sql); - @oci_execute($s); -} +$stmtarray = array( + "drop user testuser cascade", + "create user testuser identified by testuserpwd", + "grant connect, create session to testuser" +); + +oci8_test_sql_execute($c, $stmtarray); // Connect (persistent) and change the password $c1 = oci_pconnect("testuser", "testuserpwd", $dbase); @@ -62,16 +57,19 @@ else { var_dump($c2); } -// Clean up -oci_close($c1); -oci_close($c2); -oci_close($c3); +echo "Done\n"; -// Clean up -$s = oci_parse($c0, "drop user cascade testuser"); -@oci_execute($s); +?> +--CLEAN-- +<?php -echo "Done\n"; +require(dirname(__FILE__)."/connect.inc"); + +$stmtarray = array( + "drop user testuser cascade" +); + +oci8_test_sql_execute($c, $stmtarray); ?> --EXPECTF-- diff --git a/ext/oci8/tests/password_new.phpt b/ext/oci8/tests/password_new.phpt index ba6baa964..8041699f9 100644 --- a/ext/oci8/tests/password_new.phpt +++ b/ext/oci8/tests/password_new.phpt @@ -2,23 +2,16 @@ oci_password_change() --SKIPIF-- <?php -if (!extension_loaded('oci8')) die("skip no oci8 extension"); -require(dirname(__FILE__)."/connect.inc"); +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on thes +require(dirname(__FILE__).'/skipif.inc'); if (empty($dbase)) die ("skip requires database connection string be set"); if ($test_drcp) die("skip password change not supported in DRCP Mode"); // This test is known to fail with Oracle 10.2.0.4 client libraries // connecting to Oracle Database 11 (Oracle bug 6277160, fixed 10.2.0.5) -$sv = oci_server_version($c); -$sv = preg_match('/Release (11|12)\./', $sv, $matches); -if ($sv === 1) { - ob_start(); - phpinfo(INFO_MODULES); - $phpinfo = ob_get_clean(); - $iv = preg_match('/Oracle .*Version => 10/', $phpinfo); - if ($iv === 1) { - die ("skip test known to fail using Oracle 10.2.0.4 client libs connecting to Oracle 11 (6277160)"); - } +if (preg_match('/Release (11|12)\./', oci_server_version($c), $matches) === 1 && + preg_match('/^10\.2\.0\.[1234]/', oci_client_version()) === 1) { + die ("skip test known to fail using Oracle 10.2.0.4 client libs connecting to Oracle 11 (6277160)"); } ?> --FILE-- diff --git a/ext/oci8/tests/password_old.phpt b/ext/oci8/tests/password_old.phpt index abcaeb1e5..3ff726d2b 100644 --- a/ext/oci8/tests/password_old.phpt +++ b/ext/oci8/tests/password_old.phpt @@ -2,24 +2,19 @@ ocipasswordchange() --SKIPIF-- <?php -if (!extension_loaded('oci8')) die("skip no oci8 extension"); -require(dirname(__FILE__)."/connect.inc"); +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on thes +require(dirname(__FILE__).'/skipif.inc'); if (empty($dbase)) die ("skip requires database connection string be set"); if ($test_drcp) die("skip password change not supported in DRCP Mode"); // This test is known to fail with Oracle 10.2.0.4 client libraries // connecting to Oracle Database 11 (Oracle bug 6277160, fixed 10.2.0.5) -$sv = oci_server_version($c); -$sv = preg_match('/Release (11|12)\./', $sv, $matches); -if ($sv === 1) { - ob_start(); - phpinfo(INFO_MODULES); - $phpinfo = ob_get_clean(); - $iv = preg_match('/Oracle .*Version => 10/', $phpinfo); - if ($iv === 1) { - die ("skip test known to fail using Oracle 10.2.0.4 client libs connecting to Oracle 11 (6277160)"); - } +if (preg_match('/Release (11|12)\./', oci_server_version($c), $matches) === 1 && + preg_match('/^10\.2\.0\.[1234]/', oci_client_version()) === 1) { + die ("skip test known to fail using Oracle 10.2.0.4 client libs connecting to Oracle 11 (6277160)"); } + + ?> --FILE-- <?php diff --git a/ext/oci8/tests/pecl_bug10194.phpt b/ext/oci8/tests/pecl_bug10194.phpt index 4714fadcd..6a9044b1a 100644 --- a/ext/oci8/tests/pecl_bug10194.phpt +++ b/ext/oci8/tests/pecl_bug10194.phpt @@ -1,7 +1,11 @@ --TEST-- PECL Bug #10194 (segfault in Instant Client when memory_limit is reached inside the callback) --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +if ($stress_test !== true) die ('skip Test not run when $stress_test is FALSE'); +?> --INI-- memory_limit=10M --FILE-- diff --git a/ext/oci8/tests/pecl_bug10194_blob.phpt b/ext/oci8/tests/pecl_bug10194_blob.phpt index 4c6aa4f1c..75632ce47 100644 --- a/ext/oci8/tests/pecl_bug10194_blob.phpt +++ b/ext/oci8/tests/pecl_bug10194_blob.phpt @@ -1,22 +1,23 @@ --TEST-- PECL Bug #10194 (segfault in Instant Client when memory_limit is reached inside the callback) --SKIPIF-- -<?php -if (!extension_loaded('oci8')) die("skip no oci8 extension"); +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); if (PHP_INT_SIZE != 4) die("skip this test is for 32bit platforms only"); -?> +if ($stress_test !== true) die ('skip Test not run when $stress_test is FALSE'); +?> --INI-- memory_limit=3M --FILE-- <?php // This test is dependent on the behavior of the memory manager - -require dirname(__FILE__).'/connect.inc'; -require dirname(__FILE__).'/create_table.inc'; + +require(dirname(__FILE__).'/connect.inc'); +require(dirname(__FILE__).'/create_table.inc'); -$ora_sql = "INSERT INTO ".$schema.$table_name." (blob) - VALUES (empty_blob())"; +$ora_sql = "INSERT INTO ".$schema.$table_name." (blob) VALUES (empty_blob())"; $statement = oci_parse($c,$ora_sql); oci_execute($statement); @@ -30,7 +31,7 @@ $row = oci_fetch_assoc($statement); $string = str_repeat("test", 32768*4*4); for ($i = 0; $i < 8; $i++) { - $row['BLOB']->write($string); + $row['BLOB']->write($string); } oci_commit($c); @@ -48,7 +49,7 @@ require dirname(__FILE__).'/drop_table.inc'; echo "Done\n"; ?> ---EXPECTF-- +--EXPECTF-- Before load() Fatal error: Allowed memory size of %d bytes exhausted%s(tried to allocate %d bytes) in %s on line %d diff --git a/ext/oci8/tests/pecl_bug10194_blob_64.phpt b/ext/oci8/tests/pecl_bug10194_blob_64.phpt index 433d586a4..da7ec592f 100644 --- a/ext/oci8/tests/pecl_bug10194_blob_64.phpt +++ b/ext/oci8/tests/pecl_bug10194_blob_64.phpt @@ -2,8 +2,10 @@ PECL Bug #10194 (segfault in Instant Client when memory_limit is reached inside the callback) --SKIPIF-- <?php -if (!extension_loaded('oci8')) die("skip no oci8 extension"); +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on thes +require(dirname(__FILE__).'/skipif.inc'); if (PHP_INT_SIZE != 8) die("skip this test is for 64bit platforms only"); +if ($stress_test !== true) die ('skip Test not run when $stress_test is FALSE'); ?> --INI-- memory_limit=6M diff --git a/ext/oci8/tests/pecl_bug16035.phpt b/ext/oci8/tests/pecl_bug16035.phpt index fc91bc91b..29ff6439d 100644 --- a/ext/oci8/tests/pecl_bug16035.phpt +++ b/ext/oci8/tests/pecl_bug16035.phpt @@ -1,5 +1,5 @@ --TEST-- -PECL Bug #16035 (Crash with Oracle 10.2 connecting with a character set but ORACLE_HOME isn't set) +PECL Bug #16035 (Crash with Oracle 10.2 connecting with a character set but ORACLE_HOME is not set) --SKIPIF-- <?php if (!extension_loaded('oci8')) die ("skip no oci8 extension"); @@ -23,4 +23,7 @@ oci_connect('abc', 'def', 'ghi', 'jkl'); <?php exit(0); ?> --EXPECTF-- Warning: oci_connect(): OCIEnvNlsCreate() failed. There is something wrong with your system - please check that ORACLE_HOME and %s are set and point to the right directories in %s on line %d + +Warning: oci_connect(): Error while trying to retrieve text for error ORA-01804 + in %specl_bug16035.php on line %d ===DONE=== diff --git a/ext/oci8/tests/pecl_bug16842.phpt b/ext/oci8/tests/pecl_bug16842.phpt index d796d2506..dbf7e6bb8 100644 --- a/ext/oci8/tests/pecl_bug16842.phpt +++ b/ext/oci8/tests/pecl_bug16842.phpt @@ -1,7 +1,10 @@ --TEST-- PECL Bug #16842 (NO_DATA_FOUND exception is a warning) --SKIPIF-- -<?php if (!extension_loaded('oci8')) die ("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --INI-- error_reporting = E_WARNING --FILE-- @@ -39,15 +42,15 @@ Raises NO_DATA_FOUND Warning: oci_execute(): OCI_NO_DATA in %s on line 11 bool(false) array(4) { - [%u|b%"code"]=> + ["code"]=> int(1403) - [%u|b%"message"]=> - %unicode|string%(45) "ORA-01403: %s + ["message"]=> + string(%d) "ORA-01403: %s ORA-06512: at line 1" - [%u|b%"offset"]=> + ["offset"]=> int(0) - [%u|b%"sqltext"]=> - %unicode|string%(31) "begin raise NO_DATA_FOUND; end;" + ["sqltext"]=> + string(31) "begin raise NO_DATA_FOUND; end;" } Test 2 Raises ZERO_DIVIDE @@ -56,14 +59,14 @@ Warning: oci_execute(): ORA-01476: %s ORA-06512: at line 1 in %s on line 19 bool(false) array(4) { - [%u|b%"code"]=> + ["code"]=> int(1476) - [%u|b%"message"]=> - %unicode|string%(56) "ORA-01476: %s + ["message"]=> + string(%d) "ORA-01476: %s ORA-06512: at line 1" - [%u|b%"offset"]=> + ["offset"]=> int(0) - [%u|b%"sqltext"]=> - %unicode|string%(29) "begin raise ZERO_DIVIDE; end;" + ["sqltext"]=> + string(29) "begin raise ZERO_DIVIDE; end;" } ===DONE=== diff --git a/ext/oci8/tests/pecl_bug8816.phpt b/ext/oci8/tests/pecl_bug8816.phpt index c369711fc..71771b210 100644 --- a/ext/oci8/tests/pecl_bug8816.phpt +++ b/ext/oci8/tests/pecl_bug8816.phpt @@ -1,7 +1,10 @@ --TEST-- PECL Bug #8816 (issue in php_oci_statement_fetch with more than one piecewise column) --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php diff --git a/ext/oci8/tests/prefetch.phpt b/ext/oci8/tests/prefetch.phpt index 26762601d..b163a89e9 100644 --- a/ext/oci8/tests/prefetch.phpt +++ b/ext/oci8/tests/prefetch.phpt @@ -17,23 +17,7 @@ $stmtarray = array( "insert into prefetch_tab (id, value) values (1,1)", ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - $r = @oci_execute($s); - if (!$r) { - $m = oci_error($s); - if (!in_array($m['code'], array( // ignore expected errors - 942 // table or view does not exist - ))) { - echo $stmt . PHP_EOL . $m['message'] . PHP_EOL; - } - } -} - -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); // Run Test @@ -58,10 +42,7 @@ $stmtarray = array( "drop table prefetch_tab" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); echo "Done\n"; ?> diff --git a/ext/oci8/tests/prefetch_old.phpt b/ext/oci8/tests/prefetch_old.phpt index c2ac8fe84..ac43771c6 100644 --- a/ext/oci8/tests/prefetch_old.phpt +++ b/ext/oci8/tests/prefetch_old.phpt @@ -15,23 +15,7 @@ $stmtarray = array( "insert into prefetch_old_tab (id, value) values (1,1)", ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - $r = @oci_execute($s); - if (!$r) { - $m = oci_error($s); - if (!in_array($m['code'], array( // ignore expected errors - 942 // table or view does not exist - ))) { - echo $stmt . PHP_EOL . $m['message'] . PHP_EOL; - } - } -} - -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); // Run Test @@ -61,10 +45,7 @@ $stmtarray = array( "drop table prefetch_old_tab" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); echo "Done\n"; ?> diff --git a/ext/oci8/tests/refcur_prefetch_1.phpt b/ext/oci8/tests/refcur_prefetch_1.phpt index 904e4da1f..ea09fbcd9 100644 --- a/ext/oci8/tests/refcur_prefetch_1.phpt +++ b/ext/oci8/tests/refcur_prefetch_1.phpt @@ -4,19 +4,10 @@ Prefetch with REF cursor. Test different values for prefetch with oci_set_prefet <?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); if (!extension_loaded('oci8')) die("skip no oci8 extension"); require(dirname(__FILE__)."/connect.inc"); -ob_start(); -phpinfo(INFO_MODULES); -$phpinfo = ob_get_clean(); -$iv = preg_match('/Oracle .*Version => (11\.2|12\.)/', $phpinfo); -if ($iv == 1) { - $sv = oci_server_version($c); - $sv = preg_match('/Release 1[012]\./', $sv, $matches); - if ($sv != 1) { - die ("skip expected output only valid when using Oracle 10g or greater server"); - } -} -else { - die ("skip expected output only valid when using Oracle 11.2 or greater client"); +if (preg_match('/Release 1[012]\./', oci_server_version($c), $matches) !== 1) { + die("skip expected output only valid when using Oracle 10g or greater databases"); +} else if (preg_match('/^(11\.2|12)\./', oci_client_version()) != 1) { + die("skip test expected to work only with Oracle 11gR2 or greater version of client"); } ?> --FILE-- @@ -45,16 +36,7 @@ $stmtarray = array( end refcurpkg;" ); -foreach($stmtarray as $stmt) { - $s = oci_parse($c,$stmt); - $r = @oci_execute($s); - if (!$r) { - $msg = oci_error($s); - if ($msg['code'] != 942) { - echo $msg['message'],"\n"; - } - } -} +oci8_test_sql_execute($c, $stmtarray); // Insert 500 rows into the table. $insert_sql = "INSERT INTO refcurtest (c1, c2) VALUES (:c1,:c2)"; @@ -94,7 +76,7 @@ function fetch_frm_php($c,$cur1,$value) { oci_execute($s1); oci_set_prefetch($cur1,$value); oci_execute($cur1); - echo "Fetch Row from PHP\n"; + echo "Fetch Row from PHP\n"; var_dump(oci_fetch_row($cur1)); } @@ -106,14 +88,14 @@ function fetch_frm_plsql($c,$cur1) { if (!oci_bind_by_name($s2,":curs1",$cur1,-1,SQLT_RSET)) { die("oci_bind_by_name(sql2) failed!\n"); } - if (!oci_bind_by_name($s2,":c1",$c1,SQLT_INT)) { + if (!oci_bind_by_name($s2,":c1",$c1,-1,SQLT_INT)) { die("oci_bind_by_name(sql2) failed!\n"); } - if (!oci_bind_by_name($s2,":c2",$c2,SQLT_AFC)) { + if (!oci_bind_by_name($s2,":c2",$c2,20,SQLT_CHR)) { die("oci_bind_by_name(sql2) failed!\n"); } oci_execute($s2); - echo "Fetch Row from PL/SQL\n"; + echo "Fetch Row from PL/SQL\n"; var_dump($c1); var_dump($c2); } @@ -125,132 +107,125 @@ $stmtarray = array( "drop table refcurtest" ); -foreach($stmtarray as $stmt) { - $s = oci_parse($c,$stmt); - $r = @oci_execute($s); - if (!$r) { - $msg = oci_error($s); - echo $msg['message'],"\n"; - } -} -oci_close($c); +oci8_test_sql_execute($c, $stmtarray); + echo "Done\n"; ?> --EXPECTF-- ----------------------------------------------- Test with Prefetch value set to 0 ----------------------------------------------- -Fetch Row from PHP +Fetch Row from PHP array(2) { [0]=> - %unicode|string%(%d) "0" + string(%d) "0" [1]=> - %unicode|string%(%d) "test0" + string(%d) "test0" } -Fetch Row from PL/SQL -%unicode|string%(%d) "1" -%unicode|string%(%d) "test1" +Fetch Row from PL/SQL +int(1) +string(%d) "test1" ----------------------------------------------- Test with Prefetch value set to 1 ----------------------------------------------- -Fetch Row from PHP +Fetch Row from PHP array(2) { [0]=> - %unicode|string%(%d) "0" + string(%d) "0" [1]=> - %unicode|string%(%d) "test0" + string(%d) "test0" } -Fetch Row from PL/SQL -%unicode|string%(%d) "2" -%unicode|string%(%d) "test2" +Fetch Row from PL/SQL +int(2) +string(%d) "test2" ----------------------------------------------- Test with Prefetch value set to 501 ----------------------------------------------- -Fetch Row from PHP +Fetch Row from PHP array(2) { [0]=> - %unicode|string%(%d) "0" + string(%d) "0" [1]=> - %unicode|string%(%d) "test0" + string(%d) "test0" } Warning: oci_execute(): ORA-01002: %s ORA-06512: at "%s.REFCURPKG", line %d ORA-06512: at line %d in %s on line %d -Fetch Row from PL/SQL -NULL +Fetch Row from PL/SQL +int(0) NULL ----------------------------------------------- Test with Prefetch value set to 499 ----------------------------------------------- -Fetch Row from PHP +Fetch Row from PHP array(2) { [0]=> - %unicode|string%(%d) "0" + string(%d) "0" [1]=> - %unicode|string%(%d) "test0" + string(%d) "test0" } -Fetch Row from PL/SQL -%unicode|string%(%d) "500" -%unicode|string%(%d) "test500" +Fetch Row from PL/SQL +int(500) +string(%d) "test500" ----------------------------------------------- Test with Prefetch value set to 250 ----------------------------------------------- -Fetch Row from PHP +Fetch Row from PHP array(2) { [0]=> - %unicode|string%(%d) "0" + string(%d) "0" [1]=> - %unicode|string%(%d) "test0" + string(%d) "test0" } -Fetch Row from PL/SQL -%unicode|string%(%d) "251" -%unicode|string%(%d) "test251" +Fetch Row from PL/SQL +int(251) +string(%d) "test251" ----------------------------------------------- Test with Prefetch value set to 12345 ----------------------------------------------- -Fetch Row from PHP +Fetch Row from PHP array(2) { [0]=> - %unicode|string%(%d) "0" + string(%d) "0" [1]=> - %unicode|string%(%d) "test0" + string(%d) "test0" } Warning: oci_execute(): ORA-01002: %s ORA-06512: at "%s.REFCURPKG", line %d ORA-06512: at line %d in %s on line %d -Fetch Row from PL/SQL -NULL +Fetch Row from PL/SQL +int(0) NULL ----------------------------------------------- Test with Prefetch value set to -12345 ----------------------------------------------- Warning: oci_set_prefetch(): Number of rows to be prefetched has to be greater than or equal to 0 in %s on line %d -Fetch Row from PHP +Fetch Row from PHP array(2) { [0]=> - %unicode|string%(%d) "0" + string(%d) "0" [1]=> - %unicode|string%(%d) "test0" + string(%d) "test0" } -Fetch Row from PL/SQL -%unicode|string%(%d) "101" -%unicode|string%(%d) "test101" +Fetch Row from PL/SQL +int(101) +string(%d) "test101" ----------------------------------------------- Test with Prefetch value set to -1 ----------------------------------------------- Warning: oci_set_prefetch(): Number of rows to be prefetched has to be greater than or equal to 0 in %s on line %d -Fetch Row from PHP +Fetch Row from PHP array(2) { [0]=> - %unicode|string%(%d) "0" + string(%d) "0" [1]=> - %unicode|string%(%d) "test0" + string(%d) "test0" } -Fetch Row from PL/SQL -%unicode|string%(%d) "101" -%unicode|string%(%d) "test101" +Fetch Row from PL/SQL +int(101) +string(%d) "test101" Done diff --git a/ext/oci8/tests/refcur_prefetch_2.phpt b/ext/oci8/tests/refcur_prefetch_2.phpt index 751ffa78f..8d6525107 100644 --- a/ext/oci8/tests/refcur_prefetch_2.phpt +++ b/ext/oci8/tests/refcur_prefetch_2.phpt @@ -4,19 +4,10 @@ Prefetch with REF cursor. Test No 2 <?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); if (!extension_loaded('oci8')) die("skip no oci8 extension"); require(dirname(__FILE__)."/connect.inc"); -ob_start(); -phpinfo(INFO_MODULES); -$phpinfo = ob_get_clean(); -$iv = preg_match('/Oracle .*Version => (11\.2|12\.)/', $phpinfo); -if ($iv == 1) { - $sv = oci_server_version($c); - $sv = preg_match('/Release 1[012]\./', $sv, $matches); - if ($sv != 1) { - die ("skip expected output only valid when using Oracle 10g or greater server"); - } -} -else { - die ("skip expected output only valid when using Oracle 11.1 or greater client"); +if (preg_match('/Release 1[012]\./', oci_server_version($c), $matches) !== 1) { + die("skip expected output only valid when using Oracle 10g or greater databases"); +} else if (preg_match('/^(11\.2|12)\./', oci_client_version()) != 1) { + die("skip test expected to work only with Oracle 11gR2 or greater version of client"); } ?> --FILE-- @@ -45,16 +36,7 @@ $stmtarray = array( end refcurpkg;" ); -foreach($stmtarray as $stmt) { - $s = oci_parse($c,$stmt); - $r = @oci_execute($s); - if (!$r) { - $msg = oci_error($s); - if ($msg['code'] != 942) { - echo $msg['message'],"\n"; - } - } -} +oci8_test_sql_execute($c, $stmtarray); // Insert 500 rows into the table. $insert_sql = "INSERT INTO refcurtest (c1, c2) VALUES (:c1,:c2)"; @@ -85,13 +67,13 @@ if (!oci_bind_by_name($s1,":cur1",$cur1,-1,SQLT_RSET)) { $sql2 = "begin refcurpkg.fetch_ref_cur(:curs1,:c1,:c2); end;"; $s2 = oci_parse($c,$sql2); -if (!oci_bind_by_name($s2,":curs1",$cur1,-1,SQLT_RSET)) { +if (!oci_bind_by_name($s2, ":curs1", $cur1, -1, SQLT_RSET)) { die("oci_bind_by_name(sql2) failed!\n"); } -if (!oci_bind_by_name($s2,":c1",$c1,SQLT_INT)) { +if (!oci_bind_by_name($s2, ":c1", $c1, -1, SQLT_INT)) { die("oci_bind_by_name(sql2) failed!\n"); } -if (!oci_bind_by_name($s2,":c2",$c2,SQLT_AFC)) { +if (!oci_bind_by_name($s2, ":c2", $c2, 20, SQLT_CHR)) { die("oci_bind_by_name(sql2) failed!\n"); } @@ -124,7 +106,7 @@ if (!oci_bind_by_name($s1,":cur1",$cur1,-1,SQLT_RSET)) { die("oci_bind_by_name(sql1) failed!\n"); } -echo "Fetch Row from PHP\n"; +echo "Fetch Row from PHP\n"; oci_execute($s1); oci_execute($cur1); var_dump(oci_fetch_row($cur1)); @@ -135,55 +117,10 @@ if (!oci_bind_by_name($s2,":curs1",$cur1,-1,SQLT_RSET)) { die("oci_bind_by_name(sql2) failed!\n"); } oci_execute($s2); -echo "Fetch Row from PL/SQL\n"; -var_dump($c1); -var_dump($c2); - -echo "------Test 3 - Set Prefetch after PL/SQL fetch ----------\n"; -$cur1 = oci_new_cursor($c); -// Fetch from PL/SQL -if (!oci_bind_by_name($s2,":curs1",$cur1,-1,SQLT_RSET)) { - die("oci_bind_by_name(sql2) failed!\n"); -} -oci_execute($s2); -echo "Fetch Row from PL/SQL\n"; -var_dump($c1); -var_dump($c2); - -// Fetch from PHP -echo "Fetch Row from PHP\n"; -if (!oci_bind_by_name($s1,":cur1",$cur1,-1,SQLT_RSET)) { - die("oci_bind_by_name(sql1) failed!\n"); -} -oci_set_prefetch($cur1,5); -oci_execute($s1); -oci_execute($cur1); -var_dump(oci_fetch_row($cur1)); - -echo "------Test 4- Overwrite prefetch-----------\n"; -// Fetch from PHP -$cur1 = oci_new_cursor($c); -if (!oci_bind_by_name($s1,":cur1",$cur1,-1,SQLT_RSET)) { - die("oci_bind_by_name(sql1) failed!\n"); -} -echo "Fetch Row from PHP\n"; -oci_execute($s1); -oci_execute($cur1); -var_dump(oci_fetch_row($cur1)); -oci_set_prefetch($cur1,5); -oci_set_prefetch($cur1,0); -oci_set_prefetch($cur1,100); - -// Fetch from PL/SQL -if (!oci_bind_by_name($s2,":curs1",$cur1,-1,SQLT_RSET)) { - die("oci_bind_by_name(sql2) failed!\n"); -} -oci_execute($s2); -echo "Fetch Row from PL/SQL\n"; +echo "Fetch Row from PL/SQL\n"; var_dump($c1); var_dump($c2); - function print_roundtrips($c) { $sql_stmt = "select value from v\$mystat a,v\$statname c where a.statistic#=c.statistic# and c.name='SQL*Net roundtrips to/from client'"; @@ -201,117 +138,83 @@ $stmtarray = array( "drop table refcurtest" ); -foreach($stmtarray as $stmt) { - $s = oci_parse($c,$stmt); - $r = @oci_execute($s); - if (!$r) { - $msg = oci_error($s); - echo $msg['message'],"\n"; - } -} +oci8_test_sql_execute($c, $stmtarray); -oci_close($c); echo "Done\n"; ?> --EXPECTF-- ------Test 1- Check Roundtrips with prefetch 0 and 5 ----------- array(2) { [0]=> - %unicode|string%(%d) "0" + string(1) "0" [1]=> - %unicode|string%(%d) "test0" + string(5) "test0" } array(2) { [0]=> - %unicode|string%(%d) "1" + string(1) "1" [1]=> - %unicode|string%(%d) "test1" + string(5) "test1" } array(2) { [0]=> - %unicode|string%(%d) "2" + string(1) "2" [1]=> - %unicode|string%(%d) "test2" + string(5) "test2" } array(2) { [0]=> - %unicode|string%(%d) "3" + string(1) "3" [1]=> - %unicode|string%(%d) "test3" + string(5) "test3" } array(2) { [0]=> - %unicode|string%(%d) "4" + string(1) "4" [1]=> - %unicode|string%(%d) "test4" + string(5) "test4" } Number of roundtrips made with prefetch count 0 for 5 rows is 6 array(2) { [0]=> - %unicode|string%(%d) "5" + string(1) "5" [1]=> - %unicode|string%(%d) "test5" + string(5) "test5" } array(2) { [0]=> - %unicode|string%(%d) "6" + string(1) "6" [1]=> - %unicode|string%(%d) "test6" + string(5) "test6" } array(2) { [0]=> - %unicode|string%(%d) "7" + string(1) "7" [1]=> - %unicode|string%(%d) "test7" + string(5) "test7" } array(2) { [0]=> - %unicode|string%(%d) "8" + string(1) "8" [1]=> - %unicode|string%(%d) "test8" + string(5) "test8" } array(2) { [0]=> - %unicode|string%(%d) "9" + string(1) "9" [1]=> - %unicode|string%(%d) "test9" + string(5) "test9" } Number of roundtrips made with prefetch count 5 for 5 rows is 2 ------Test 2 - Set Prefetch before PL/SQL fetch ---------- -Fetch Row from PHP -array(2) { - [0]=> - %unicode|string%(%d) "0" - [1]=> - %unicode|string%(%d) "test0" -} -Fetch Row from PL/SQL -%unicode|string%(%d) "101" -%unicode|string%(%d) "test101" -------Test 3 - Set Prefetch after PL/SQL fetch ---------- - -Warning: oci_execute(): ORA-01001: %s -ORA-06512: at "SYSTEM.REFCURPKG", line %d -ORA-06512: at line %d in %s on line %d -Fetch Row from PL/SQL -%unicode|string%(%d) "101" -%unicode|string%(%d) "test101" -Fetch Row from PHP -array(2) { - [0]=> - %unicode|string%(%d) "0" - [1]=> - %unicode|string%(%d) "test0" -} -------Test 4- Overwrite prefetch----------- -Fetch Row from PHP +Fetch Row from PHP array(2) { [0]=> - %unicode|string%(%d) "0" + string(1) "0" [1]=> - %unicode|string%(%d) "test0" + string(5) "test0" } -Fetch Row from PL/SQL -%unicode|string%(%d) "101" -%unicode|string%(%d) "test101" +Fetch Row from PL/SQL +int(101) +string(%d) "test101" Done diff --git a/ext/oci8/tests/refcur_prefetch_3.phpt b/ext/oci8/tests/refcur_prefetch_3.phpt index 0666a96e9..8c0414042 100644 --- a/ext/oci8/tests/refcur_prefetch_3.phpt +++ b/ext/oci8/tests/refcur_prefetch_3.phpt @@ -6,19 +6,10 @@ oci8.default_prefetch=5 <?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); if (!extension_loaded('oci8')) die("skip no oci8 extension"); require(dirname(__FILE__)."/connect.inc"); -ob_start(); -phpinfo(INFO_MODULES); -$phpinfo = ob_get_clean(); -$iv = preg_match('/Oracle .*Version => (11\.2|12\.)/', $phpinfo); -if ($iv == 1) { - $sv = oci_server_version($c); - $sv = preg_match('/Release (11\.2|12\.)/', $sv, $matches); - if ($sv != 1) { - die ("skip expected output only valid when using Oracle 11.2 or greater server"); - } -} -else { - die ("skip expected output only valid when using Oracle 11.2 or greater client"); +if (preg_match('/Release (11\.2|12)\./', oci_server_version($c), $matches) !== 1) { + die("skip expected output only valid when using Oracle 11gR2 or greater databases"); +} else if (preg_match('/^(11\.2|12)\./', oci_client_version()) != 1) { + die("skip test expected to work only with Oracle 11gR2 or greater version of client"); } ?> @@ -32,16 +23,8 @@ $stmtarray = array( "create table nescurtest(c1 varchar2(10))" ); -foreach($stmtarray as $stmt) { - $s = oci_parse($c,$stmt); - $r = @oci_execute($s); - if (!$r) { - $msg = oci_error($s); - if ($msg['code'] !=942) { - echo $msg['message'],"\n"; - } - } -} +oci8_test_sql_execute($c, $stmtarray); + // Insert 500 rows into the table. $insert_sql = "INSERT INTO nescurtest (c1) VALUES (:c1)"; if (!($s = oci_parse($c, $insert_sql))) { @@ -92,15 +75,8 @@ $stmtarray = array( "drop table nescurtest" ); -foreach($stmtarray as $stmt) { - $s = oci_parse($c,$stmt); - $r = @oci_execute($s); - if (!$r) { - $msg = oci_error($s); - echo $msg['message'],"\n"; - } -} -oci_close($c); +oci8_test_sql_execute($c, $stmtarray); + echo "Done\n"; ?> --EXPECTF-- diff --git a/ext/oci8/tests/refcur_prefetch_4.phpt b/ext/oci8/tests/refcur_prefetch_4.phpt new file mode 100644 index 000000000..d24398c00 --- /dev/null +++ b/ext/oci8/tests/refcur_prefetch_4.phpt @@ -0,0 +1,176 @@ +--TEST-- +Prefetch with REF cursor. Test No 4 +--SKIPIF-- +<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); +if (!extension_loaded('oci8')) die("skip no oci8 extension"); +require(dirname(__FILE__)."/connect.inc"); +if (preg_match('/Release 1[012]\./', oci_server_version($c), $matches) !== 1) { + die("skip expected output only valid when using Oracle 10g or greater databases"); +} else if (preg_match('/^(11\.2|12)\./', oci_client_version()) != 1) { + die("skip test expected to work only with Oracle 11gR2 or greater version of client"); +} +?> +--FILE-- +<?php +require dirname(__FILE__)."/connect.inc"; + +// Creates the necessary package and tables. +$stmtarray = array( + "DROP TABLE refcurtest", + + "CREATE TABLE refcurtest (c1 NUMBER, c2 VARCHAR(20))", + + "CREATE or REPLACE PACKAGE refcurpkg is + type refcursortype is ref cursor; + procedure open_ref_cur(cur1 out refcursortype); + procedure fetch_ref_cur(cur1 in refcursortype, c1 out number, c2 out varchar2); + end refcurpkg;", + + "CREATE or REPLACE PACKAGE body refcurpkg is + procedure open_ref_cur(cur1 out refcursortype) is + begin + open cur1 for select * from refcurtest order by c1; + end open_ref_cur; + procedure fetch_ref_cur(cur1 in refcursortype, c1 out number, c2 out varchar2) is + begin + fetch cur1 into c1,c2; + end fetch_ref_cur; + end refcurpkg;" + ); + +oci8_test_sql_execute($c, $stmtarray); + +// Insert 500 rows into the table. +$insert_sql = "INSERT INTO refcurtest (c1, c2) VALUES (:c1,:c2)"; +if (!($s = oci_parse($c, $insert_sql))) { + die("oci_parse(insert) failed!\n"); +} + +for ($i = 0; $i <= 500; $i++) { + $val2 = 'test'.$i; + oci_bind_by_name($s,':c1',$i); + oci_bind_by_name($s,':c2',$val2); + if (!oci_execute($s)) { + die("oci_execute(insert) failed!\n"); + } +} + +// Steps to Fetch from PHP . For every sub-test,the cursor is bound and then executed. + +$sql1 = "begin refcurpkg.open_ref_cur(:cur1); end;"; +$s1 = oci_parse($c,$sql1); +$cur1 = oci_new_cursor($c); +if (!oci_bind_by_name($s1,":cur1",$cur1,-1,SQLT_RSET)) { + die("oci_bind_by_name(sql1) failed!\n"); +} + + +// Steps to Fetch from PL/SQL . For every sub-test,the cursor is bound and then executed. + +$sql2 = "begin refcurpkg.fetch_ref_cur(:curs1,:c1,:c2); end;"; +$s2 = oci_parse($c,$sql2); +if (!oci_bind_by_name($s2, ":curs1", $cur1, -1, SQLT_RSET)) { + die("oci_bind_by_name(sql2) failed!\n"); +} +if (!oci_bind_by_name($s2, ":c1", $c1, -1, SQLT_INT)) { + die("oci_bind_by_name(sql2) failed!\n"); +} +if (!oci_bind_by_name($s2, ":c2", $c2, 20, SQLT_CHR)) { + die("oci_bind_by_name(sql2) failed!\n"); +} + + +echo "------Test 1 - Set Prefetch after PL/SQL fetch ----------\n"; +$cur1 = oci_new_cursor($c); +// Fetch from PL/SQL +if (!oci_bind_by_name($s2,":curs1",$cur1,-1,SQLT_RSET)) { + die("oci_bind_by_name(sql2) failed!\n"); +} +oci_execute($s2); +echo "Fetch Row from PL/SQL\n"; +var_dump($c1); +var_dump($c2); + +// Fetch from PHP +echo "Fetch Row from PHP\n"; +if (!oci_bind_by_name($s1,":cur1",$cur1,-1,SQLT_RSET)) { + die("oci_bind_by_name(sql1) failed!\n"); +} +oci_set_prefetch($cur1,5); +oci_execute($s1); +oci_execute($cur1); +var_dump(oci_fetch_row($cur1)); + +echo "------Test 2- Overwrite prefetch-----------\n"; +// Fetch from PHP +$cur1 = oci_new_cursor($c); +if (!oci_bind_by_name($s1,":cur1",$cur1,-1,SQLT_RSET)) { + die("oci_bind_by_name(sql1) failed!\n"); +} +echo "Fetch Row from PHP\n"; +oci_execute($s1); +oci_execute($cur1); +var_dump(oci_fetch_row($cur1)); +oci_set_prefetch($cur1,5); +oci_set_prefetch($cur1,0); +oci_set_prefetch($cur1,100); + +// Fetch from PL/SQL +if (!oci_bind_by_name($s2,":curs1",$cur1,-1,SQLT_RSET)) { + die("oci_bind_by_name(sql2) failed!\n"); +} +oci_execute($s2); +echo "Fetch Row from PL/SQL\n"; +var_dump($c1); +var_dump($c2); + + +function print_roundtrips($c) { + $sql_stmt = "select value from v\$mystat a,v\$statname c where + a.statistic#=c.statistic# and c.name='SQL*Net roundtrips to/from client'"; + $s = oci_parse($c,$sql_stmt); + oci_define_by_name($s,"VALUE",$value); + oci_execute($s); + oci_fetch($s); + return $value; +} + +// Clean up here + +$stmtarray = array( + "drop package refcurpkg", + "drop table refcurtest" +); + +oci8_test_sql_execute($c, $stmtarray); + +echo "Done\n"; +?> +--EXPECTF-- +------Test 1 - Set Prefetch after PL/SQL fetch ---------- + +Warning: oci_execute(): ORA-01001: %s +ORA-06512: at "SYSTEM.REFCURPKG", line %d +ORA-06512: at line %d in %s on line %d +Fetch Row from PL/SQL +int(0) +NULL +Fetch Row from PHP +array(2) { + [0]=> + string(1) "0" + [1]=> + string(5) "test0" +} +------Test 2- Overwrite prefetch----------- +Fetch Row from PHP +array(2) { + [0]=> + string(1) "0" + [1]=> + string(5) "test0" +} +Fetch Row from PL/SQL +int(101) +string(%d) "test101" +Done diff --git a/ext/oci8/tests/select_null.phpt b/ext/oci8/tests/select_null.phpt index 87c5b815f..450c81a87 100644 --- a/ext/oci8/tests/select_null.phpt +++ b/ext/oci8/tests/select_null.phpt @@ -15,11 +15,11 @@ var_dump(oci_fetch_array($stmt, OCI_RETURN_NULLS)); echo "Done\n"; ?> ---EXPECT-- +--EXPECTF-- array(2) { [0]=> NULL - ["NULL"]=> + ["%r(NULL|EXP)%r"]=> NULL } Done diff --git a/ext/oci8/tests/serverversion.phpt b/ext/oci8/tests/serverversion.phpt index bf32fe4b5..6cbb5aaad 100644 --- a/ext/oci8/tests/serverversion.phpt +++ b/ext/oci8/tests/serverversion.phpt @@ -25,6 +25,6 @@ echo "Done\n"; ?> --EXPECTF-- resource(%d) of type (oci8 connection) -string(%d) "%s" -string(%d) "%s" +string(%d) "Oracle %s" +string(%d) "Oracle %s" Done diff --git a/ext/oci8/tests/skipif.inc b/ext/oci8/tests/skipif.inc index ed0992c8d..0ae2876e2 100644 --- a/ext/oci8/tests/skipif.inc +++ b/ext/oci8/tests/skipif.inc @@ -2,9 +2,17 @@ if (!extension_loaded('oci8')) die("skip oci8 extension is not available\n"); -/* - * Remove or comment this line to run tests - * - * */ -die("skip change default login/password\n"); +// Check the test file is valid for this DB type +require(dirname(__FILE__).'/connect.inc'); +$v = oci_server_version($c); +if (strpos($v, 'Oracle Database') !== false) + $dbtype = 'oracledb'; +else if (strpos($v, 'Oracle TimesTen') !== false) + $dbtype = 'timesten'; +else { + die("skip Unknown database type $v\n"); +} +if (!array_key_exists($dbtype, $target_dbs) || $target_dbs[$dbtype] !== true) + die("skip Test not valid for $v\n"); + ?> diff --git a/ext/oci8/tests/statement_cache.phpt b/ext/oci8/tests/statement_cache.phpt index 19e69d4c2..4c48d3f1d 100644 --- a/ext/oci8/tests/statement_cache.phpt +++ b/ext/oci8/tests/statement_cache.phpt @@ -1,10 +1,15 @@ --TEST-- statement cache --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => true); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +?> --FILE-- <?php +// Note: with TimesTen, the column will be called "EXP" + require dirname(__FILE__)."/connect.inc"; $pc = oci_pconnect($user, $password, $dbase); @@ -23,13 +28,13 @@ echo "Done\n"; array(2) { [0]=> string(1) "4" - ["1+3"]=> + ["%r(1\+3|EXP)%r"]=> string(1) "4" } array(2) { [0]=> string(1) "4" - ["1+3"]=> + ["%r(1\+3|EXP)%r"]=> string(1) "4" } Done diff --git a/ext/oci8/tests/xmltype_01.phpt b/ext/oci8/tests/xmltype_01.phpt index a9458c83d..21aca6cc1 100644 --- a/ext/oci8/tests/xmltype_01.phpt +++ b/ext/oci8/tests/xmltype_01.phpt @@ -1,8 +1,10 @@ --TEST-- Basic XMLType test --SKIPIF-- -<?php if (!extension_loaded('oci8')) die("skip no oci8 extension"); ?> -<?php if (!extension_loaded("simplexml")) die("skip no simplexml extension"); ?> +<?php +if (!extension_loaded("simplexml")) die("skip no simplexml extension"); +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); --FILE-- <?php @@ -10,7 +12,7 @@ require(dirname(__FILE__)."/connect.inc"); // Initialization -$stmts = array( +$stmtarray = array( "drop table xtt", "create table xtt (xt_id number, xt_spec xmltype) @@ -31,16 +33,7 @@ $stmts = array( </Xt>'))" ); -foreach ($stmts as $q) { - $s = oci_parse($c, $q); - $r = @oci_execute($s); - if (!$r) { - $m = oci_error($s); - if ($m['code'] != 942) { // table or view doesn't exist - echo $m['message'], "\n"; - } - } -} +oci8_test_sql_execute($c, $stmtarray); function do_query($c) { @@ -78,14 +71,11 @@ $data = do_query($c); // Cleanup -$stmts = array( +$stmtarray = array( "drop table xtt", ); -foreach ($stmts as $q) { - $s = oci_parse($c, $q); - @oci_execute($s); -} +oci8_test_sql_execute($c, $stmtarray); echo "Done\n"; diff --git a/ext/oci8/tests/xmltype_02.phpt b/ext/oci8/tests/xmltype_02.phpt index 9b6fa8ad3..28e9401ba 100644 --- a/ext/oci8/tests/xmltype_02.phpt +++ b/ext/oci8/tests/xmltype_02.phpt @@ -1,7 +1,11 @@ --TEST-- Basic XMLType test #2 --SKIPIF-- -<?php if (!extension_loaded('oci8')) die ("skip no oci8 extension"); ?> +<?php +$target_dbs = array('oracledb' => true, 'timesten' => false); // test runs on these DBs +require(dirname(__FILE__).'/skipif.inc'); +if (!extension_loaded("simplexml")) die ("skip no simplexml extension"); +?> --FILE-- <?php @@ -14,20 +18,7 @@ $stmtarray = array( "create table xmltype_02_tab (warehouse_id number, warehouse_spec xmltype)", ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - $r = @oci_execute($s); - if (!$r) { - $m = oci_error($s); - if (!in_array($m['code'], array( // ignore expected errors - 942 // table or view does not exist - , 2289 // sequence does not exist - , 4080 // trigger does not exist - ))) { - echo $stmt . PHP_EOL . $m['message'] . PHP_EOL; - } - } -} +oci8_test_sql_execute($c, $stmtarray); // Run Test @@ -109,18 +100,11 @@ $row[0]->free(); // Clean up -//require(dirname(__FILE__).'/drop_table.inc'); - $stmtarray = array( "drop table xmltype_02_tab" ); -foreach ($stmtarray as $stmt) { - $s = oci_parse($c, $stmt); - oci_execute($s); -} - -oci_close($c); +oci8_test_sql_execute($c, $stmtarray); ?> ===DONE=== @@ -130,68 +114,68 @@ Test 1 Insert new XML data using a temporary CLOB array(1) { [0]=> object(OCI-Lob)#%d (1) { - [%u|b%"descriptor"]=> + ["descriptor"]=> resource(%d) of type (oci8 descriptor) } } Test 2 Manipulate the data using SimpleXML object(SimpleXMLElement)#%d (10) { - [%u|b%"WarehouseId"]=> - %unicode|string%(1) "1" - [%u|b%"WarehouseName"]=> - %unicode|string%(16) "Southlake, Texas" - [%u|b%"Building"]=> - %unicode|string%(5) "Owned" - [%u|b%"Area"]=> - %unicode|string%(5) "25000" - [%u|b%"Docks"]=> - %unicode|string%(1) "2" - [%u|b%"DockType"]=> - %unicode|string%(9) "Rear load" - [%u|b%"WaterAccess"]=> - %unicode|string%(4) "true" - [%u|b%"RailAccess"]=> - %unicode|string%(1) "N" - [%u|b%"Parking"]=> - %unicode|string%(6) "Street" - [%u|b%"VClearance"]=> - %unicode|string%(2) "10" + ["WarehouseId"]=> + string(1) "1" + ["WarehouseName"]=> + string(16) "Southlake, Texas" + ["Building"]=> + string(5) "Owned" + ["Area"]=> + string(5) "25000" + ["Docks"]=> + string(1) "2" + ["DockType"]=> + string(9) "Rear load" + ["WaterAccess"]=> + string(4) "true" + ["RailAccess"]=> + string(1) "N" + ["Parking"]=> + string(6) "Street" + ["VClearance"]=> + string(2) "10" } object(SimpleXMLElement)#%d (10) { - [%u|b%"WarehouseId"]=> - %unicode|string%(1) "1" - [%u|b%"WarehouseName"]=> - %unicode|string%(16) "Southlake, Texas" - [%u|b%"Building"]=> - %unicode|string%(5) "Owned" - [%u|b%"Area"]=> - %unicode|string%(5) "25000" - [%u|b%"Docks"]=> - %unicode|string%(1) "1" - [%u|b%"DockType"]=> - %unicode|string%(9) "Rear load" - [%u|b%"WaterAccess"]=> - %unicode|string%(4) "true" - [%u|b%"RailAccess"]=> - %unicode|string%(1) "N" - [%u|b%"Parking"]=> - %unicode|string%(6) "Street" - [%u|b%"VClearance"]=> - %unicode|string%(2) "10" + ["WarehouseId"]=> + string(1) "1" + ["WarehouseName"]=> + string(16) "Southlake, Texas" + ["Building"]=> + string(5) "Owned" + ["Area"]=> + string(5) "25000" + ["Docks"]=> + string(1) "1" + ["DockType"]=> + string(9) "Rear load" + ["WaterAccess"]=> + string(4) "true" + ["RailAccess"]=> + string(1) "N" + ["Parking"]=> + string(6) "Street" + ["VClearance"]=> + string(2) "10" } Test 3: Update changes using a temporary CLOB -%unicode|string%(331) "<?xml version="1.0"?> +string(%d) "<?xml version="1.0"?> <Warehouse> -<WarehouseId>1</WarehouseId> -<WarehouseName>Southlake, Texas</WarehouseName> -<Building>Owned</Building> -<Area>25000</Area> -<Docks>1</Docks> -<DockType>Rear load</DockType> -<WaterAccess>true</WaterAccess> -<RailAccess>N</RailAccess> -<Parking>Street</Parking> -<VClearance>10</VClearance> +%sWarehouseId>1</WarehouseId> +%sWarehouseName>Southlake, Texas</WarehouseName> +%sBuilding>Owned</Building> +%sArea>25000</Area> +%sDocks>1</Docks> +%sDockType>Rear load</DockType> +%sWaterAccess>true</WaterAccess> +%sRailAccess>N</RailAccess> +%sParking>Street</Parking> +%sVClearance>10</VClearance> </Warehouse> " ===DONE===
\ No newline at end of file diff --git a/ext/odbc/php_odbc.c b/ext/odbc/php_odbc.c index 94ed51fb1..70d15e344 100644 --- a/ext/odbc/php_odbc.c +++ b/ext/odbc/php_odbc.c @@ -20,7 +20,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: php_odbc.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: php_odbc.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -383,7 +383,7 @@ const zend_function_entry odbc_functions[] = { #endif PHP_FALIAS(odbc_do, odbc_exec, arginfo_odbc_exec) PHP_FALIAS(odbc_field_precision, odbc_field_len, arginfo_odbc_field_len) - { NULL, NULL, NULL } + PHP_FE_END }; /* }}} */ diff --git a/ext/openssl/config0.m4 b/ext/openssl/config0.m4 index ee5e85c29..1a956666d 100644 --- a/ext/openssl/config0.m4 +++ b/ext/openssl/config0.m4 @@ -1,5 +1,5 @@ dnl -dnl $Id: config0.m4 226663 2007-01-07 18:38:22Z iliaa $ +dnl $Id: config0.m4 309398 2011-03-18 18:47:09Z geissert $ dnl PHP_ARG_WITH(openssl, for OpenSSL support, @@ -17,6 +17,7 @@ if test "$PHP_OPENSSL" != "no"; then fi AC_CHECK_LIB(ssl, DSA_get_default_method, AC_DEFINE(HAVE_DSA_DEFAULT_METHOD, 1, [OpenSSL 0.9.7 or later])) + AC_CHECK_LIB(crypto, X509_free, AC_DEFINE(HAVE_DSA_DEFAULT_METHOD, 1, [OpenSSL 0.9.7 or later])) PHP_SETUP_OPENSSL(OPENSSL_SHARED_LIBADD, [ diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index a17eb0e5b..ce96c645d 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -20,7 +20,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: openssl.c 308534 2011-02-21 12:47:38Z pajoye $ */ +/* $Id: openssl.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -439,7 +439,7 @@ const zend_function_entry openssl_functions[] = { PHP_FE(openssl_random_pseudo_bytes, arginfo_openssl_random_pseudo_bytes) PHP_FE(openssl_error_string, arginfo_openssl_error_string) - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ @@ -988,8 +988,8 @@ PHP_MINIT_FUNCTION(openssl) ERR_load_crypto_strings(); ERR_load_EVP_strings(); - /* register a resource id number with openSSL so that we can map SSL -> stream structures in - * openSSL callbacks */ + /* register a resource id number with OpenSSL so that we can map SSL -> stream structures in + * OpenSSL callbacks */ ssl_stream_data_index = SSL_get_ex_new_index(0, "PHP stream index", NULL, NULL, NULL); REGISTER_STRING_CONSTANT("OPENSSL_VERSION_TEXT", OPENSSL_VERSION_TEXT, CONST_CS|CONST_PERSISTENT); @@ -1074,7 +1074,9 @@ PHP_MINIT_FUNCTION(openssl) php_stream_xport_register("ssl", php_openssl_ssl_socket_factory TSRMLS_CC); php_stream_xport_register("sslv3", php_openssl_ssl_socket_factory TSRMLS_CC); +#ifndef OPENSSL_NO_SSL2 php_stream_xport_register("sslv2", php_openssl_ssl_socket_factory TSRMLS_CC); +#endif php_stream_xport_register("tls", php_openssl_ssl_socket_factory TSRMLS_CC); /* override the default tcp socket provider */ @@ -1109,7 +1111,9 @@ PHP_MSHUTDOWN_FUNCTION(openssl) php_unregister_url_stream_wrapper("ftps" TSRMLS_CC); php_stream_xport_unregister("ssl" TSRMLS_CC); +#ifndef OPENSSL_NO_SSL2 php_stream_xport_unregister("sslv2" TSRMLS_CC); +#endif php_stream_xport_unregister("sslv3" TSRMLS_CC); php_stream_xport_unregister("tls" TSRMLS_CC); @@ -4704,7 +4708,11 @@ PHP_FUNCTION(openssl_encrypt) outlen = data_len + EVP_CIPHER_block_size(cipher_type); outbuf = emalloc(outlen + 1); - EVP_EncryptInit(&cipher_ctx, cipher_type, key, (unsigned char *)iv); + EVP_EncryptInit(&cipher_ctx, cipher_type, NULL, NULL); + if (password_len > keylen) { + EVP_CIPHER_CTX_set_key_length(&cipher_ctx, password_len); + } + EVP_EncryptInit_ex(&cipher_ctx, NULL, NULL, key, (unsigned char *)iv); EVP_EncryptUpdate(&cipher_ctx, outbuf, &i, (unsigned char *)data, data_len); outlen = i; if (EVP_EncryptFinal(&cipher_ctx, (unsigned char *)outbuf + i, &i)) { @@ -4784,7 +4792,11 @@ PHP_FUNCTION(openssl_decrypt) outlen = data_len + EVP_CIPHER_block_size(cipher_type); outbuf = emalloc(outlen + 1); - EVP_DecryptInit(&cipher_ctx, cipher_type, key, (unsigned char *)iv); + EVP_DecryptInit(&cipher_ctx, cipher_type, NULL, NULL); + if (password_len > keylen) { + EVP_CIPHER_CTX_set_key_length(&cipher_ctx, password_len); + } + EVP_DecryptInit_ex(&cipher_ctx, NULL, NULL, key, (unsigned char *)iv); EVP_DecryptUpdate(&cipher_ctx, outbuf, &i, (unsigned char *)data, data_len); outlen = i; if (EVP_DecryptFinal(&cipher_ctx, (unsigned char *)outbuf + i, &i)) { @@ -4896,10 +4908,6 @@ PHP_FUNCTION(openssl_random_pseudo_bytes) buffer = emalloc(buffer_length + 1); -#ifdef WINDOWS - RAND_screen(); -#endif - if ((strong_result = RAND_pseudo_bytes(buffer, buffer_length)) < 0) { efree(buffer); RETURN_FALSE; diff --git a/ext/openssl/tests/bug54992-ca.pem b/ext/openssl/tests/bug54992-ca.pem new file mode 100644 index 000000000..0fdbb2f90 --- /dev/null +++ b/ext/openssl/tests/bug54992-ca.pem @@ -0,0 +1,42 @@ +-----BEGIN CERTIFICATE----- +MIIHZzCCBU+gAwIBAgIBATANBgkqhkiG9w0BAQQFADCByzEpMCcGA1UEAxMgQ2F0 +YXBocmFjdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxCzAJBgNVBAYTAlBUMQ8wDQYD +VQQHEwZMaXNib2ExETAPBgNVBAgTCFBvcnR1Z2FsMSkwJwYDVQQKEyBDYXRhcGhy +YWN0IENlcnRpZmljYXRlIEF1dGhvcml0eTEcMBoGA1UECxMTQ2VydGlmaWNhdGUg +U2lnbmluZzEkMCIGCSqGSIb3DQEJARYVQ2F0YXBocmFjdEBuZXRjYWJvLnB0MB4X +DTAzMTIwNTAwMTExOVoXDTE4MTIwMTAwMTExOVowgcsxKTAnBgNVBAMTIENhdGFw +aHJhY3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MQswCQYDVQQGEwJQVDEPMA0GA1UE +BxMGTGlzYm9hMREwDwYDVQQIEwhQb3J0dWdhbDEpMCcGA1UEChMgQ2F0YXBocmFj +dCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHDAaBgNVBAsTE0NlcnRpZmljYXRlIFNp +Z25pbmcxJDAiBgkqhkiG9w0BCQEWFUNhdGFwaHJhY3RAbmV0Y2Fiby5wdDCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANg+noZuxtWdxmZjxanJGEpzmDYu +Uko9OHdmhVr3UU+z04a9JFT7aH5wuwrnpadNy1u9CqrSHVWFEtSmOMOH8QYzIy4C +qCjFPSJR5UQjxpxTZeXaTvfhKI9n0LMSqc7I68HkP5MF64N3Z2cRdYvM4U6R5ERD +Xw2LiRpii/+J2cezgi/Nw3vS4hZlWDWMkttfBd0HKSwxxN7OlPcjyzoTVhQgSISV +Zvd3kwENTWD7s3EnnPRtMiW8Vzcjd8eSTCSjuGBG/8NnI44amLo7gSWocCJ2os69 +CJgiqMpp0tLT8cJm0mQUBk0o9gBS7l1GPpgq5fwWG+DmoLIHrKjxpuI5v2DW23gx +yimXSyiD1GX0JLlTqZ+klM7Mv7ptnigRXA8F5f4GbVzBlGM1L1EERd8orsSmzPEA +S6puHdlNzjcx00glp1UoAs6+tV39eW/fjiP493biPcar0pNO8QWfRSqPsgy6/qKN +m7x2DoSdTbRgCalBMp57xYCUHIETZvlewGKnQD1Tj9FlbzvOnH6r52gj5U/5r3pn +E4DshILn/qtdRwd/2Dwx/KSyBJznU7Yu0vEeMwQioZ6YFH1FnC4229lHYCN6ByVw +UE7OMH7n0A8SUN8flxr2X7MmWpQsMrgVfrAjufmFwUaeIRq9X3wihDYw0MYP0brU +x5ONmY+VA93gLdStAgMBAAGjggFSMIIBTjASBgNVHRMBAf8ECDAGAQH/AgEBMB0G +A1UdDgQWBBQj+82/Y4YWpR8kIi0obJULkqmBwTCB+AYDVR0jBIHwMIHtgBQj+82/ +Y4YWpR8kIi0obJULkqmBwaGB0aSBzjCByzEpMCcGA1UEAxMgQ2F0YXBocmFjdCBD +ZXJ0aWZpY2F0ZSBBdXRob3JpdHkxCzAJBgNVBAYTAlBUMQ8wDQYDVQQHEwZMaXNi +b2ExETAPBgNVBAgTCFBvcnR1Z2FsMSkwJwYDVQQKEyBDYXRhcGhyYWN0IENlcnRp +ZmljYXRlIEF1dGhvcml0eTEcMBoGA1UECxMTQ2VydGlmaWNhdGUgU2lnbmluZzEk +MCIGCSqGSIb3DQEJARYVQ2F0YXBocmFjdEBuZXRjYWJvLnB0ggEBMAsGA1UdDwQE +AwIBBjARBglghkgBhvhCAQEEBAMCAAcwDQYJKoZIhvcNAQEEBQADggIBAKN6pRY1 +8GwQx378ukmw4pzvODlee5IKSPRT92hfLKNGNUAMu2LFo+bjItpilhSvR4aklRvh +5RBoVE8ejEdZXsz0HobMUUcL9IemaRwBCWHPii7Y3zX2J1FUiS/KmWhrYvw5hb1P +P83f/kxdWhxD+MbwuGc2I/6WgfsRyzevQsxdJgElQvNGkOXsC56pEXm2ChVoLbZL +sZX0zPa5ZzXByQGwXl9eqOkV7fdNKulJPcLPOs/y1cAfcxXrDYHpqBGf9nb14p3C +NaWXFhvq9Khk/QiWKSO4QarPlYS4H0Sl6tp7zBaE+dZHAjci2mSTraUf7q61kqoJ +g/ZA3qupd1rR67NzN+6x/TJmIq0G3GUxnDNNqNAHvS4YJx8g4Ji0F3Qoz3CgKnc4 +HsneYQ/LCLq2pDjsffnLI88MBGbfHZDjdj2nowwX76W/6PPutD5IR/kOmHEaX1TJ +/Ff99bVV4HwNF3GPwmKPmHpw3hB9A/xG9aiQRcYs0reXoYeQ+8nyCGmu41LweFyV +1WVwWJ/MHgdtzJZHdPjeXKMWQzOx3AS3TCc31oi4IEo4NgNigcuvl0qgUcwDRXBI +HZm4f7npm7xiES8BSoq5PIVCj8EXJd4b7Gk6dHGJGO+APaw3kYKqfqg5+AN1e4a5 +x3onNvWhjcwDGgcs/xAfVJIUucEqpC5h0pZq +-----END CERTIFICATE----- diff --git a/ext/openssl/tests/bug54992.pem b/ext/openssl/tests/bug54992.pem new file mode 100644 index 000000000..0675450a3 --- /dev/null +++ b/ext/openssl/tests/bug54992.pem @@ -0,0 +1,47 @@ +-----BEGIN CERTIFICATE----- +MIIFizCCA3OgAwIBAgIBGTANBgkqhkiG9w0BAQUFADCByzEpMCcGA1UEAxMgQ2F0 +YXBocmFjdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxCzAJBgNVBAYTAlBUMQ8wDQYD +VQQHEwZMaXNib2ExETAPBgNVBAgTCFBvcnR1Z2FsMSkwJwYDVQQKEyBDYXRhcGhy +YWN0IENlcnRpZmljYXRlIEF1dGhvcml0eTEcMBoGA1UECxMTQ2VydGlmaWNhdGUg +U2lnbmluZzEkMCIGCSqGSIb3DQEJARYVQ2F0YXBocmFjdEBuZXRjYWJvLnB0MB4X +DTExMDYwNzIzNTIwM1oXDTE4MTIwMTAwMTExOVowWjEXMBUGA1UEAxMOYnVnNTQ5 +OTIubG9jYWwxCzAJBgNVBAYTAlBUMQ8wDQYDVQQHEwZMaXNib2ExDzANBgNVBAgT +Bkxpc2JvYTEQMA4GA1UEChMHcGhwLm5ldDCBnzANBgkqhkiG9w0BAQEFAAOBjQAw +gYkCgYEAtUAVQKTgpUPgtFOJ3w3kDJETS45tWeT96kUg1NeYLKW+jNbFhxPoPJv7 +XhfemCaqh2tbq1cdYW906Wp1L+eNQvdTYA2IQG4EQBUlmfyIakOIMsN/RizVkF09 +vlNQwTpaMpqTv7wB8vvwbxb9jbC2ZhQUBEg6PIn18dSstbM9FZ0CAwEAAaOCAWww +ggFoMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFCysG9r7vXtfHa38AUZeCM6tgH9c +MIH4BgNVHSMEgfAwge2AFCP7zb9jhhalHyQiLShslQuSqYHBoYHRpIHOMIHLMSkw +JwYDVQQDEyBDYXRhcGhyYWN0IENlcnRpZmljYXRlIEF1dGhvcml0eTELMAkGA1UE +BhMCUFQxDzANBgNVBAcTBkxpc2JvYTERMA8GA1UECBMIUG9ydHVnYWwxKTAnBgNV +BAoTIENhdGFwaHJhY3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MRwwGgYDVQQLExND +ZXJ0aWZpY2F0ZSBTaWduaW5nMSQwIgYJKoZIhvcNAQkBFhVDYXRhcGhyYWN0QG5l +dGNhYm8ucHSCAQEwCwYDVR0PBAQDAgXgMBEGCWCGSAGG+EIBAQQEAwIGQDAeBglg +hkgBhvhCAQ0EERYPeGNhIGNlcnRpZmljYXRlMA0GCSqGSIb3DQEBBQUAA4ICAQAT +M7Id7nBSvaDXuStLunfeV0WPAh3DkKWCxw9YK0MjK7E/K5xEiYaWWbz9zuHEcKrN +MuflSdYVPXTqvD6mHLFNptOgzG6YMOO+rAAEYB5HZ/PYTO6UWAdSLlS96DpA4SS3 +Qwmrc0eXe1p4U8noEN+N3+rAbetjOuvnLG/cpoQGcA8Mws84B/elzjRne5C8N1rF +Tvdb3bqIqvP1thuPfyh/uIKSQb5ZusHvj7ZBkEs+zQLBRnCcDK4ETXFM0TcKSPar +d11tve/91BqqemwlA+ntVrVTgi/pnw4wuWxa3GOVmeEeWgtv3063wZ3lGv/72PCh +gSjxoCoVLaLPTbC/iG2a5+ca2HcF0TjfJqYNCgosgRGlm5IunvuIv+g5jLcZcDSO +hMw+HzyF8GlDF166YRRb9nUL6AtBisdEw6uQW1vQFRRQS4SGMoArSBw2EBqd7Kvf +ruCMcrkudC8vbWQHMETEvhAXdAjgsIxLeGCPh0/8mtES1Lnr0TWIrM9evPJkKACj +f6CyIASkIDZKFf5JwuUh02qvuNLr/QRELfI1NnA1aTYMQQWWOVCBffu4ce+NPdtl +Uh1vRwWAWI0Zjszw3kUk2vHLbSXeD3bU7gP3IFa1X8XsXBW2SH+BfpNWHUilHj1I +bX+zqjfaRWDJuZqB9y6iTCu8DfBtbMiTUGcI/Rs9wQ== +-----END CERTIFICATE----- +-----BEGIN RSA PRIVATE KEY----- +MIICXgIBAAKBgQC1QBVApOClQ+C0U4nfDeQMkRNLjm1Z5P3qRSDU15gspb6M1sWH +E+g8m/teF96YJqqHa1urVx1hb3TpanUv541C91NgDYhAbgRAFSWZ/IhqQ4gyw39G +LNWQXT2+U1DBOloympO/vAHy+/BvFv2NsLZmFBQESDo8ifXx1Ky1sz0VnQIDAQAB +AoGBALUEnHUkdgv4P7o5WJACAomedqPWSlYmgoVvpvuLmrq0ihuFAGAIvL+TlTgD +JNfWfiejTDlSVtCSDTR1kzZVztitfXDxRkWEjGtFjMhk/DJkql3w10SUtcqCiWqw +/XknyPHZ7A+w7Fu5KRO2LoSIze2ZLKvCfP/M/pLR2fTKGTHtAkEA2NreT1GUnvzj +u1lb2J0nTZbSQHvEkfpEej9akl0Bc5UkskenEsiXE3cJYA1TbEGSqYCmt23x3Rd2 +FYxm6MwV6wJBANX34ZuUOllsS0FJPbkEAps3M4s59daQSFiEkQc5XjPgVB0xVV7s +OEBlGkM3eqcCUOMnMI8L9wfBk49sELZCeJcCQQC/y/TL2q/EXo9c6I/faj+U1Exp +VA5rvhpKtTX6NeBOxh6Kv+z5JAja4nGcTqz2FpkM6giKO+erUFDUhjWOuNK5AkEA +xkmHnCRLxp8jRodXWeQrfigz7ixydLsVMGL5+9XgRPb5PGyBjwwePR70raH2Wls9 +FqU0zPvrnBZ6Zwlgm2cSVQJAPLYA51Z9piajbTuggpioQ5qbUEDkJjmYHbm8eJnK +h5NW/EtCk4SBxAc+8ElPrvJjtZyOPWfm4vZF5sDKtC3Fkg== +-----END RSA PRIVATE KEY----- diff --git a/ext/openssl/tests/bug54992.phpt b/ext/openssl/tests/bug54992.phpt new file mode 100644 index 000000000..d3a06310e --- /dev/null +++ b/ext/openssl/tests/bug54992.phpt @@ -0,0 +1,44 @@ +--TEST-- +Bug #54992: Stream not closed and error not returned when SSL CN_match fails +--SKIPIF-- +<?php +if (!extension_loaded("openssl")) die("skip"); +if (!function_exists('pcntl_fork')) die("skip no fork"); +--FILE-- +<?php +$context = stream_context_create(); + +stream_context_set_option($context, 'ssl', 'local_cert', "./bug54992.pem"); +stream_context_set_option($context, 'ssl', 'allow_self_signed', true); +$server = stream_socket_server('ssl://127.0.0.1:64321', $errno, $errstr, + STREAM_SERVER_BIND|STREAM_SERVER_LISTEN, $context); + + +$pid = pcntl_fork(); +if ($pid == -1) { + die('could not fork'); +} else if ($pid) { + $contextC = stream_context_create( + array( + 'ssl' => array( + 'verify_peer' => true, + 'cafile' => 'bug54992-ca.pem', + 'CN_match' => 'buga_buga', + ) + ) + ); + var_dump(stream_socket_client("ssl://127.0.0.1:64321", $errno, $errstr, 1, + STREAM_CLIENT_CONNECT, $contextC)); +} else { + @pcntl_wait($status); + @stream_socket_accept($server, 1); +} +--EXPECTF-- +Warning: stream_socket_client(): Peer certificate CN=`bug54992.local' did not match expected CN=`buga_buga' in %s on line %d + +Warning: stream_socket_client(): Failed to enable crypto in %s on line %d + +Warning: stream_socket_client(): unable to connect to ssl://127.0.0.1:64321 (Unknown error) in %s on line %d +bool(false) + + diff --git a/ext/openssl/xp_ssl.c b/ext/openssl/xp_ssl.c index 1cfdf1017..edbfe3f3d 100644 --- a/ext/openssl/xp_ssl.c +++ b/ext/openssl/xp_ssl.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: xp_ssl.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: xp_ssl.c 313616 2011-07-23 01:29:44Z scottmac $ */ #include "php.h" #include "ext/standard/file.h" @@ -145,7 +145,7 @@ static int handle_ssl_error(php_stream *stream, int nr_bytes, zend_bool is_init default: do { - // NULL is automatically added + /* NULL is automatically added */ ERR_error_string_n(ecode, esbuf, sizeof(esbuf)); if (ebuf.c) { smart_str_appendc(&ebuf, '\n'); @@ -204,6 +204,36 @@ static size_t php_openssl_sockop_write(php_stream *stream, const char *buf, size return didwrite; } +static void php_openssl_stream_wait_for_data(php_stream *stream, php_netstream_data_t *sock TSRMLS_DC) +{ + int retval; + struct timeval *ptimeout; + + if (sock->socket == -1) { + return; + } + + sock->timeout_event = 0; + + if (sock->timeout.tv_sec == -1) + ptimeout = NULL; + else + ptimeout = &sock->timeout; + + while(1) { + retval = php_pollfd_for(sock->socket, PHP_POLLREADABLE, ptimeout); + + if (retval == 0) + sock->timeout_event = 1; + + if (retval >= 0) + break; + + if (php_socket_errno() != EINTR) + break; + } +} + static size_t php_openssl_sockop_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) { php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract; @@ -213,6 +243,13 @@ static size_t php_openssl_sockop_read(php_stream *stream, char *buf, size_t coun int retry = 1; do { + if (sslsock->s.is_blocked) { + php_openssl_stream_wait_for_data(stream, &(sslsock->s) TSRMLS_CC); + if (sslsock->s.timeout_event) { + break; + } + /* there is no guarantee that there is application data available but something is there */ + } nr_bytes = SSL_read(sslsock->ssl_handle, buf, count); if (nr_bytes <= 0) { @@ -329,9 +366,14 @@ static inline int php_openssl_setup_crypto(php_stream *stream, method = SSLv23_client_method(); break; case STREAM_CRYPTO_METHOD_SSLv2_CLIENT: +#ifdef OPENSSL_NO_SSL2 + php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSLv2 support is not compiled into the OpenSSL library PHP is linked against"); + return -1; +#else sslsock->is_client = 1; method = SSLv2_client_method(); break; +#endif case STREAM_CRYPTO_METHOD_SSLv3_CLIENT: sslsock->is_client = 1; method = SSLv3_client_method(); @@ -349,9 +391,14 @@ static inline int php_openssl_setup_crypto(php_stream *stream, method = SSLv3_server_method(); break; case STREAM_CRYPTO_METHOD_SSLv2_SERVER: +#ifdef OPENSSL_NO_SSL2 + php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSLv2 support is not compiled into the OpenSSL library PHP is linked against"); + return -1; +#else sslsock->is_client = 0; method = SSLv2_server_method(); break; +#endif case STREAM_CRYPTO_METHOD_TLS_SERVER: sslsock->is_client = 0; method = TLSv1_server_method(); @@ -505,6 +552,7 @@ static inline int php_openssl_enable_crypto(php_stream *stream, if (FAILURE == php_openssl_apply_verification_policy(sslsock->ssl_handle, peer_cert, stream TSRMLS_CC)) { SSL_shutdown(sslsock->ssl_handle); + n = -1; } else { sslsock->ssl_active = 1; @@ -912,8 +960,13 @@ php_stream *php_openssl_ssl_socket_factory(const char *proto, long protolen, sslsock->enable_on_connect = 1; sslsock->method = STREAM_CRYPTO_METHOD_SSLv23_CLIENT; } else if (strncmp(proto, "sslv2", protolen) == 0) { +#ifdef OPENSSL_NO_SSL2 + php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSLv2 support is not compiled into the OpenSSL library PHP is linked against"); + return NULL; +#else sslsock->enable_on_connect = 1; sslsock->method = STREAM_CRYPTO_METHOD_SSLv2_CLIENT; +#endif } else if (strncmp(proto, "sslv3", protolen) == 0) { sslsock->enable_on_connect = 1; sslsock->method = STREAM_CRYPTO_METHOD_SSLv3_CLIENT; diff --git a/ext/pcntl/pcntl.c b/ext/pcntl/pcntl.c index b2e411849..ad8c29ed9 100755 --- a/ext/pcntl/pcntl.c +++ b/ext/pcntl/pcntl.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: pcntl.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: pcntl.c 313665 2011-07-25 11:42:53Z felipe $ */ #define PCNTL_DEBUG 0 @@ -172,7 +172,7 @@ const zend_function_entry pcntl_functions[] = { PHP_FE(pcntl_sigwaitinfo, arginfo_pcntl_sigwaitinfo) PHP_FE(pcntl_sigtimedwait, arginfo_pcntl_sigtimedwait) #endif - {NULL, NULL, NULL} + PHP_FE_END }; zend_module_entry pcntl_module_entry = { diff --git a/ext/pcntl/tests/pcntl_exec.phpt b/ext/pcntl/tests/pcntl_exec.phpt index 756fc959c..9d2ec1dcf 100644 --- a/ext/pcntl/tests/pcntl_exec.phpt +++ b/ext/pcntl/tests/pcntl_exec.phpt @@ -1,7 +1,10 @@ --TEST-- pcntl_exec() --SKIPIF-- -<?php if (!getenv("TEST_PHP_EXECUTABLE") || !is_executable(getenv("TEST_PHP_EXECUTABLE"))) die("skip TEST_PHP_EXECUTABLE not set"); ?> +<?php +if (!extension_loaded("pcntl")) print "skip"; +if (!getenv("TEST_PHP_EXECUTABLE") || !is_executable(getenv("TEST_PHP_EXECUTABLE"))) die("skip TEST_PHP_EXECUTABLE not set"); +?> --FILE-- <?php echo "ok\n"; diff --git a/ext/pcntl/tests/pcntl_exec_2.phpt b/ext/pcntl/tests/pcntl_exec_2.phpt index d1672d2a3..02b5e22aa 100644 --- a/ext/pcntl/tests/pcntl_exec_2.phpt +++ b/ext/pcntl/tests/pcntl_exec_2.phpt @@ -1,7 +1,12 @@ --TEST-- pcntl_exec() 2 --SKIPIF-- -<?php if (!getenv("TEST_PHP_EXECUTABLE") || !is_executable(getenv("TEST_PHP_EXECUTABLE"))) die("skip TEST_PHP_EXECUTABLE not set"); ?> +<?php + +if (!extension_loaded("pcntl")) print "skip"; +if (!getenv("TEST_PHP_EXECUTABLE") || !is_executable(getenv("TEST_PHP_EXECUTABLE"))) die("skip TEST_PHP_EXECUTABLE not set"); + +?> --FILE-- <?php if (getenv("PCNTL_EXEC_TEST_IS_CHILD")) { diff --git a/ext/pcntl/tests/pcntl_exec_3.phpt b/ext/pcntl/tests/pcntl_exec_3.phpt index 1017593b5..d27c1c996 100644 --- a/ext/pcntl/tests/pcntl_exec_3.phpt +++ b/ext/pcntl/tests/pcntl_exec_3.phpt @@ -1,5 +1,7 @@ --TEST-- pcntl_exec() 3 +--SKIPIF-- +<?php if (!extension_loaded("pcntl")) print "skip"; ?> --FILE-- <?php var_dump(pcntl_exec()); diff --git a/ext/pcntl/tests/pcntl_fork_basic.phpt b/ext/pcntl/tests/pcntl_fork_basic.phpt index 82759ba32..b1e2a9bf6 100644 --- a/ext/pcntl/tests/pcntl_fork_basic.phpt +++ b/ext/pcntl/tests/pcntl_fork_basic.phpt @@ -11,7 +11,7 @@ Francesco Fullone ff@ideato.it ?> --FILE-- <?php -echo "*** Test by calling method or function with its expected arguments, first print the child PID and the the father ***\n"; +echo "*** Test by calling method or function with its expected arguments, first print the child PID and the father ***\n"; $pid = pcntl_fork(); if ($pid > 0) { @@ -22,6 +22,6 @@ if ($pid > 0) { } ?> --EXPECTF-- -*** Test by calling method or function with its expected arguments, first print the child PID and the the father *** +*** Test by calling method or function with its expected arguments, first print the child PID and the father *** int(0) int(%d) diff --git a/ext/pcntl/tests/pcntl_signal.phpt b/ext/pcntl/tests/pcntl_signal.phpt index 977f26fbc..3f894fa3f 100644 --- a/ext/pcntl/tests/pcntl_signal.phpt +++ b/ext/pcntl/tests/pcntl_signal.phpt @@ -1,6 +1,7 @@ --TEST-- pcntl_signal() --SKIPIF-- +<?php if (!extension_loaded("pcntl")) print "skip"; ?> <?php if (!extension_loaded("posix")) die("skip posix extension not available"); ?> --FILE-- <?php diff --git a/ext/pcntl/tests/pcntl_wait.phpt b/ext/pcntl/tests/pcntl_wait.phpt index 266bb399e..c304c8431 100644 --- a/ext/pcntl/tests/pcntl_wait.phpt +++ b/ext/pcntl/tests/pcntl_wait.phpt @@ -1,8 +1,8 @@ --TEST-- pcntl_wait() --SKIPIF-- +<?php if (!extension_loaded("pcntl")) print "skip"; ?> <?php if (!extension_loaded("posix")) die("skip posix extension not available"); ?> -<?php --FILE-- <?php $pid = pcntl_fork(); diff --git a/ext/pcre/pcrelib/ChangeLog b/ext/pcre/pcrelib/ChangeLog index 32cd0029b..8a3014036 100644 --- a/ext/pcre/pcrelib/ChangeLog +++ b/ext/pcre/pcrelib/ChangeLog @@ -1,6 +1,40 @@ ChangeLog for PCRE ------------------ +Version 8.12 15-Jan-2011 +------------------------ + +1. Fixed some typos in the markup of the man pages, and wrote a script that + checks for such things as part of the documentation building process. + +2. On a big-endian 64-bit system, pcregrep did not correctly process the + --match-limit and --recursion-limit options (added for 8.11). In + particular, this made one of the standard tests fail. (The integer value + went into the wrong half of a long int.) + +3. If the --colour option was given to pcregrep with -v (invert match), it + did strange things, either producing crazy output, or crashing. It should, + of course, ignore a request for colour when reporting lines that do not + match. + +4. Another pcregrep bug caused similar problems if --colour was specified with + -M (multiline) and the pattern match finished with a line ending. + +5. In pcregrep, when a pattern that ended with a literal newline sequence was + matched in multiline mode, the following line was shown as part of the + match. This seems wrong, so I have changed it. + +6. Another pcregrep bug in multiline mode, when --colour was specified, caused + the check for further matches in the same line (so they could be coloured) + to overrun the end of the current line. If another match was found, it was + incorrectly shown (and then shown again when found in the next line). + +7. If pcregrep was compiled under Windows, there was a reference to the + function pcregrep_exit() before it was defined. I am assuming this was + the cause of the "error C2371: 'pcregrep_exit' : redefinition;" that was + reported by a user. I've moved the definition above the reference. + + Version 8.11 10-Dec-2010 ------------------------ diff --git a/ext/pcre/pcrelib/NEWS b/ext/pcre/pcrelib/NEWS index fdc4ba1e6..5f2b29bd2 100644 --- a/ext/pcre/pcrelib/NEWS +++ b/ext/pcre/pcrelib/NEWS @@ -1,6 +1,13 @@ News about PCRE releases ------------------------ +Release 8.12 15-Jan-2011 +------------------------ + +This release fixes some bugs in pcregrep, one of which caused the tests to fail +on 64-bit big-endian systems. There are no changes to the code of the library. + + Release 8.11 10-Dec-2010 ------------------------ diff --git a/ext/pcre/pcrelib/config.h b/ext/pcre/pcrelib/config.h index b7e02b326..46e0625fa 100644 --- a/ext/pcre/pcrelib/config.h +++ b/ext/pcre/pcrelib/config.h @@ -282,7 +282,7 @@ them both to 0; an emulation function will be used. */ #define PACKAGE_NAME "PCRE" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "PCRE 8.11" +#define PACKAGE_STRING "PCRE 8.12" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "pcre" @@ -291,7 +291,7 @@ them both to 0; an emulation function will be used. */ #define PACKAGE_URL "" /* Define to the version of this package. */ -#define PACKAGE_VERSION "8.11" +#define PACKAGE_VERSION "8.12" /* If you are compiling for a system other than a Unix-like system or @@ -347,7 +347,7 @@ them both to 0; an emulation function will be used. */ /* Version number of package */ #ifndef VERSION -#define VERSION "8.11" +#define VERSION "8.12" #endif /* Define to empty if `const' does not conform to ANSI C. */ diff --git a/ext/pcre/pcrelib/doc/pcre.txt b/ext/pcre/pcrelib/doc/pcre.txt index 5d769dfb0..ac4254ec9 100644 --- a/ext/pcre/pcrelib/doc/pcre.txt +++ b/ext/pcre/pcrelib/doc/pcre.txt @@ -6512,6 +6512,7 @@ SAVING AND RE-USING PRECOMPILED PCRE PATTERNS SAVING A COMPILED PATTERN + The value returned by pcre_compile() points to a single block of memory that holds the compiled pattern and associated data. You can find the length of this block in bytes by calling pcre_fullinfo() with an argu- diff --git a/ext/pcre/pcrelib/pcre.h b/ext/pcre/pcrelib/pcre.h index ec454ee60..21c467372 100644 --- a/ext/pcre/pcrelib/pcre.h +++ b/ext/pcre/pcrelib/pcre.h @@ -42,9 +42,9 @@ POSSIBILITY OF SUCH DAMAGE. /* The current PCRE version information. */ #define PCRE_MAJOR 8 -#define PCRE_MINOR 11 +#define PCRE_MINOR 12 #define PCRE_PRERELEASE -#define PCRE_DATE 2010-12-10 +#define PCRE_DATE 2011-01-15 /* When an application links to a PCRE DLL in Windows, the symbols that are imported have to be identified as such. When building PCRE, the appropriate diff --git a/ext/pcre/pcrelib/testdata/grepoutput b/ext/pcre/pcrelib/testdata/grepoutput index da7a37027..a0392509c 100644 --- a/ext/pcre/pcrelib/testdata/grepoutput +++ b/ext/pcre/pcrelib/testdata/grepoutput @@ -2,15 +2,19 @@ PATTERN at the start of a line. In the middle of a line, PATTERN appears. Check up on PATTERN near the end. +RC=0 ---------------------------- Test 2 ------------------------------ PATTERN at the start of a line. +RC=0 ---------------------------- Test 3 ------------------------------ 7:PATTERN at the start of a line. 8:In the middle of a line, PATTERN appears. 10:This pattern is in lower case. 608:Check up on PATTERN near the end. +RC=0 ---------------------------- Test 4 ------------------------------ 4 +RC=0 ---------------------------- Test 5 ------------------------------ ./testdata/grepinput:7:PATTERN at the start of a line. ./testdata/grepinput:8:In the middle of a line, PATTERN appears. @@ -19,6 +23,7 @@ PATTERN at the start of a line. ./testdata/grepinputx:3:Here is the pattern again. ./testdata/grepinputx:5:Pattern ./testdata/grepinputx:42:This line contains pattern not on a line by itself. +RC=0 ---------------------------- Test 6 ------------------------------ 7:PATTERN at the start of a line. 8:In the middle of a line, PATTERN appears. @@ -27,11 +32,14 @@ PATTERN at the start of a line. 3:Here is the pattern again. 5:Pattern 42:This line contains pattern not on a line by itself. +RC=0 ---------------------------- Test 7 ------------------------------ ./testdata/grepinput ./testdata/grepinputx +RC=0 ---------------------------- Test 8 ------------------------------ ./testdata/grepinput +RC=0 ---------------------------- Test 9 ------------------------------ RC=0 ---------------------------- Test 10 ----------------------------- @@ -78,33 +86,43 @@ RC=1 40:twenty 41: 43:This is the last line of this file. +RC=0 ---------------------------- Test 12 ----------------------------- Pattern +RC=0 ---------------------------- Test 13 ----------------------------- Here is the pattern again. That time it was on a line by itself. This line contains pattern not on a line by itself. +RC=0 ---------------------------- Test 14 ----------------------------- ./testdata/grepinputx:To pat or not to pat, that is the question. +RC=0 ---------------------------- Test 15 ----------------------------- pcregrep: Error in command-line regex at offset 4: nothing to repeat +RC=2 ---------------------------- Test 16 ----------------------------- pcregrep: Failed to open ./testdata/nonexistfile: No such file or directory +RC=2 ---------------------------- Test 17 ----------------------------- features should be added at the end, because some of the tests involve the output of line numbers, and we don't want these to change. +RC=0 ---------------------------- Test 18 ----------------------------- 4:features should be added at the end, because some of the tests involve the output of line numbers, and we don't want these to change. 583:brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. ------------------------------------------------------------------------------- +RC=0 ---------------------------- Test 19 ----------------------------- Pattern +RC=0 ---------------------------- Test 20 ----------------------------- 10:complete pair of lines 16:complete pair of lines +RC=0 ---------------------------- Test 21 ----------------------------- 24:four 25-five @@ -115,6 +133,7 @@ of lines 35-fifteen 36-sixteen 37-seventeen +RC=0 ---------------------------- Test 22 ----------------------------- 21-one 22-two @@ -125,6 +144,7 @@ of lines 32-twelve 33-thirteen 34:fourteen +RC=0 ---------------------------- Test 23 ----------------------------- one two @@ -141,6 +161,7 @@ fourteen fifteen sixteen seventeen +RC=0 ---------------------------- Test 24 ----------------------------- four five @@ -162,6 +183,7 @@ twenty This line contains pattern not on a line by itself. This is the last line of this file. +RC=0 ---------------------------- Test 25 ----------------------------- 15- 16-complete pair @@ -183,6 +205,7 @@ This is the last line of this file. 32-twelve 33-thirteen 34:fourteen +RC=0 ---------------------------- Test 26 ----------------------------- complete pair @@ -213,6 +236,7 @@ twenty This line contains pattern not on a line by itself. This is the last line of this file. +RC=0 ---------------------------- Test 27 ----------------------------- four five @@ -234,6 +258,7 @@ twenty This line contains pattern not on a line by itself. This is the last line of this file. +RC=0 ---------------------------- Test 28 ----------------------------- 14-of lines all by themselves. 15- @@ -256,6 +281,7 @@ This is the last line of this file. 32-twelve 33-thirteen 34:fourteen +RC=0 ---------------------------- Test 29 ----------------------------- of lines all by themselves. @@ -287,6 +313,7 @@ twenty This line contains pattern not on a line by itself. This is the last line of this file. +RC=0 ---------------------------- Test 30 ----------------------------- ./testdata/grepinput-4-features should be added at the end, because some of the tests involve the ./testdata/grepinput-5-output of line numbers, and we don't want these to change. @@ -311,6 +338,7 @@ This is the last line of this file. ./testdata/grepinputx-40-twenty ./testdata/grepinputx-41- ./testdata/grepinputx:42:This line contains pattern not on a line by itself. +RC=0 ---------------------------- Test 31 ----------------------------- ./testdata/grepinput:7:PATTERN at the start of a line. ./testdata/grepinput:8:In the middle of a line, PATTERN appears. @@ -332,8 +360,10 @@ This is the last line of this file. -- ./testdata/grepinputx:42:This line contains pattern not on a line by itself. ./testdata/grepinputx-43-This is the last line of this file. +RC=0 ---------------------------- Test 32 ----------------------------- ./testdata/grepinputx +RC=0 ---------------------------- Test 33 ----------------------------- pcregrep: Failed to open ./testdata/grepnonexist: No such file or directory RC=2 @@ -343,6 +373,7 @@ RC=2 ./testdata/grepinputx RC=0 ---------------------------- Test 36 ----------------------------- +./testdata/grepinput3 ./testdata/grepinput8 ./testdata/grepinputx RC=0 @@ -351,99 +382,214 @@ aaaaa0 aaaaa2 RC=0 ======== STDERR ======== -pcregrep: pcre_exec() error -8 while matching this text: +pcregrep: pcre_exec() gave error -8 while matching this text: + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -pcregrep: error -8 means that a resource limit was exceeded -pcregrep: check your regex for nested unlimited loops -pcregrep: pcre_exec() error -8 while matching this text: + +pcregrep: pcre_exec() gave error -8 while matching this text: + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + +pcregrep: Error -8 or -21 means that a resource limit was exceeded. +pcregrep: Check your regex for nested unlimited loops. ---------------------------- Test 38 ------------------------------ This line contains a binary zero here > +RC=0 ---------------------------- Test 39 ------------------------------ This is a line before the binary zero. This line contains a binary zero here > +RC=0 ---------------------------- Test 40 ------------------------------ This line contains a binary zero here > This is a line after the binary zero. +RC=0 ---------------------------- Test 41 ------------------------------ before the binary zero after the binary zero ----------------------------- Test 41 ------------------------------ +RC=0 +---------------------------- Test 42 ------------------------------ ./testdata/grepinput:595:before the binary zero ./testdata/grepinput:597:after the binary zero ----------------------------- Test 42 ------------------------------ +RC=0 +---------------------------- Test 43 ------------------------------ 595:before 595:zero 596:zero 597:after 597:zero ----------------------------- Test 43 ------------------------------ +RC=0 +---------------------------- Test 44 ------------------------------ 595:before 595:zero 596:zero 597:zero ----------------------------- Test 44 ------------------------------ +RC=0 +---------------------------- Test 45 ------------------------------ 10:pattern 595:binary 596:binary 597:binary ----------------------------- Test 45 ------------------------------ -pcregrep: Error in 2nd command-line regex at offset 9: missing ) +RC=0 ---------------------------- Test 46 ------------------------------ -AB.VE +pcregrep: Error in 2nd command-line regex at offset 9: missing ) +RC=2 ---------------------------- Test 47 ------------------------------ +AB.VE +RC=0 +---------------------------- Test 48 ------------------------------ ABOVE the elephant AB.VE AB.VE the turtle ----------------------------- Test 48 ------------------------------ +RC=0 +---------------------------- Test 49 ------------------------------ ABOVE the elephant AB.VE AB.VE the turtle PUT NEW DATA ABOVE THIS LINE. ----------------------------- Test 49 ------------------------------ +RC=0 ---------------------------- Test 50 ------------------------------ +RC=1 +---------------------------- Test 51 ------------------------------ over the lazy dog. This time it jumps and jumps and jumps. ----------------------------- Test 51 ------------------------------ +RC=0 +---------------------------- Test 52 ------------------------------ fox [1;31mjumps[00m This time it [1;31mjumps[00m and [1;31mjumps[00m and [1;31mjumps[00m. ----------------------------- Test 52 ------------------------------ +RC=0 +---------------------------- Test 53 ------------------------------ 36972,6 36990,4 37024,4 37066,5 37083,4 ----------------------------- Test 53 ------------------------------ +RC=0 +---------------------------- Test 54 ------------------------------ 595:15,6 595:33,4 596:28,4 597:15,5 597:32,4 ----------------------------- Test 54 ----------------------------- +RC=0 +---------------------------- Test 55 ----------------------------- Here is the [1;31mpattern[00m again. That time it was on a [1;31mline by itself[00m. This line contains [1;31mpattern[00m not on a [1;31mline by itself[00m. ----------------------------- Test 55 ----------------------------- +RC=0 +---------------------------- Test 56 ----------------------------- ./testdata/grepinput:456 +./testdata/grepinput3:0 ./testdata/grepinput8:0 ./testdata/grepinputv:1 ./testdata/grepinputx:0 ----------------------------- Test 56 ----------------------------- +RC=0 +---------------------------- Test 57 ----------------------------- ./testdata/grepinput:456 ./testdata/grepinputv:1 ----------------------------- Test 57 ----------------------------- -PATTERN at the start of a line. -In the middle of a line, PATTERN appears. -Check up on PATTERN near the end. +RC=0 ---------------------------- Test 58 ----------------------------- PATTERN at the start of a line. In the middle of a line, PATTERN appears. Check up on PATTERN near the end. +RC=0 ---------------------------- Test 59 ----------------------------- PATTERN at the start of a line. In the middle of a line, PATTERN appears. Check up on PATTERN near the end. +RC=0 ---------------------------- Test 60 ----------------------------- PATTERN at the start of a line. In the middle of a line, PATTERN appears. Check up on PATTERN near the end. +RC=0 +---------------------------- Test 61 ----------------------------- +PATTERN at the start of a line. +In the middle of a line, PATTERN appears. +Check up on PATTERN near the end. +RC=0 +---------------------------- Test 62 ----------------------------- +pcregrep: pcre_exec() gave error -8 while matching text that starts: + +This is a file of miscellaneous text that is used as test data for checking +that the pcregrep command is working correctly. The file must be more than 24K +long so that it needs more than a single read + +pcregrep: Error -8 or -21 means that a resource limit was exceeded. +pcregrep: Check your regex for nested unlimited loops. +RC=1 +---------------------------- Test 63 ----------------------------- +pcregrep: pcre_exec() gave error -21 while matching text that starts: + +This is a file of miscellaneous text that is used as test data for checking +that the pcregrep command is working correctly. The file must be more than 24K +long so that it needs more than a single read + +pcregrep: Error -8 or -21 means that a resource limit was exceeded. +pcregrep: Check your regex for nested unlimited loops. +RC=1 +---------------------------- Test 64 ------------------------------ +appears +RC=0 +---------------------------- Test 65 ------------------------------ +pear +RC=0 +---------------------------- Test 66 ------------------------------ +RC=0 +---------------------------- Test 67 ------------------------------ +RC=0 +---------------------------- Test 68 ------------------------------ +pear +RC=0 +---------------------------- Test 69 ----------------------------- +1:This is a second file of input for the pcregrep tests. +2: +4: +5:Pattern +6:That time it was on a line by itself. +7: +8:To pat or not to pat, that is the question. +9: +10:complete pair +11:of lines +12: +13:That was a complete pair +14:of lines all by themselves. +15: +16:complete pair +17:of lines +18: +19:And there they were again, to check line numbers. +20: +21:one +22:two +23:three +24:four +25:five +26:six +27:seven +28:eight +29:nine +30:ten +31:eleven +32:twelve +33:thirteen +34:fourteen +35:fifteen +36:sixteen +37:seventeen +38:eighteen +39:nineteen +40:twenty +41: +43:This is the last line of this file. +RC=0 +---------------------------- Test 70 ----------------------------- +[1;31mtriple: t1_txt s1_tag s_txt p_tag p_txt o_tag o_txt + +[00m[1;31mtriple: t3_txt s2_tag s_txt p_tag p_txt o_tag o_txt + +[00m[1;31mtriple: t4_txt s1_tag s_txt p_tag p_txt o_tag o_txt + +[00m[1;31mtriple: t6_txt s2_tag s_txt p_tag p_txt o_tag o_txt + +[00mRC=0 diff --git a/ext/pcre/pcrelib/testdata/testinput1 b/ext/pcre/pcrelib/testdata/testinput1 index d999d2948..97e5c366e 100644 --- a/ext/pcre/pcrelib/testdata/testinput1 +++ b/ext/pcre/pcrelib/testdata/testinput1 @@ -4073,4 +4073,10 @@ ** Failers XABX +/[\x00-\xff\s]+/ + \x0a\x0b\x0c\x0d + +/^\c/ + ? + /-- End of testinput1 --/ diff --git a/ext/pcre/pcrelib/testdata/testinput10 b/ext/pcre/pcrelib/testdata/testinput10 index 99afab88b..7210cc5f8 100644 --- a/ext/pcre/pcrelib/testdata/testinput10 +++ b/ext/pcre/pcrelib/testdata/testinput10 @@ -132,4 +132,6 @@ is required for these tests. --/ /[[:^alpha:]\S]+/8WB +/abc(d|e)(*THEN)x(123(*THEN)4|567(b|q)(*THEN)xx)/B + /-- End of testinput10 --/ diff --git a/ext/pcre/pcrelib/testdata/testinput2 b/ext/pcre/pcrelib/testdata/testinput2 index cc94bed4e..7cc876165 100644 --- a/ext/pcre/pcrelib/testdata/testinput2 +++ b/ext/pcre/pcrelib/testdata/testinput2 @@ -5,7 +5,7 @@ either because PCRE can't be compatible, or there is a possible Perl bug. --/ -/-- Originally, the Perl 5.10 and 5.11 things were in here too, but now I have +/-- Originally, the Perl >= 5.10 things were in here too, but now I have separated many (most?) of them out into test 11. However, there may still be some that were overlooked. --/ @@ -2346,6 +2346,15 @@ a random value. /Ix a\nb a\r\nb a\x85b + +/(*ANY).*/g + abc\r\ndef + +/(*ANYCRLF).*/g + abc\r\ndef + +/(*CRLF).*/g + abc\r\ndef /a\Rb/I<bsr_anycrlf> a\rb @@ -2575,6 +2584,12 @@ a random value. /Ix abc\Y abcxypqr abcxypqr\Y + +/(*NO_START_OPT)xyz/C + abcxyz + +/xyz/CY + abcxyz /^"((?(?=[a])[^"])|b)*"$/C "ab" @@ -3170,9 +3185,9 @@ a random value. /Ix xxxxabcde\P xxxxabcde\P\P -/-- This is not in the Perl 5.10 test because Perl seems currently to be broken - and not behaving as specified in that it *does* bumpalong after hitting - (*COMMIT). --/ +/-- This is not in the Perl >= 5.10 test because Perl seems currently to be + broken and not behaving as specified in that it *does* bumpalong after + hitting (*COMMIT). --/ /(?1)(A(*COMMIT)|B)D/ ABD @@ -3216,7 +3231,7 @@ a random value. /Ix /^(?&t)*(?(DEFINE)(?<t>.))$/BZ -/ -- The first four of these are not in the Perl 5.10 test because Perl +/ -- The first four of these are not in the Perl >= 5.10 test because Perl documents that the use of \K in assertions is "not well defined". The last is here because Perl gives the match as "b" rather than "ab". I believe this to be a Perl bug. --/ @@ -3464,22 +3479,22 @@ with \Y. ---/ abcde /A\NB./BZ - ACBD - ** Failers - A\nB - ACB\n + ACBD + *** Failers + A\nB + ACB\n /A\NB./sBZ - ACBD - ACB\n - ** Failers - A\nB + ACBD + ACB\n + *** Failers + A\nB /A\NB/<crlf> - A\nB - A\rB - ** Failers - A\r\nB + A\nB + A\rB + ** Failers + A\r\nB /\R+b/BZ @@ -3491,4 +3506,68 @@ with \Y. ---/ /\s*\R/BZ +/-- Perl treats this one differently, not failing the second string. I believe + that is a bug in Perl. --/ + +/^((abc|abcx)(*THEN)y|abcd)/ + abcd + *** Failers + abcxy + +/(?<=abc)def/ + abc\P\P + +/abc$/ + abc + abc\P + abc\P\P + +/abc$/m + abc + abc\n + abc\P\P + abc\n\P\P + abc\P + abc\n\P + +/abc\z/ + abc + abc\P + abc\P\P + +/abc\Z/ + abc + abc\P + abc\P\P + +/abc\b/ + abc + abc\P + abc\P\P + +/abc\B/ + abc + abc\P + abc\P\P + +/.+/ + abc\>0 + abc\>1 + abc\>2 + abc\>3 + abc\>4 + abc\>-4 + +/^\cÄ£/ + +/(?P<abn>(?P=abn)xxx)/BZ + +/(a\1z)/BZ + +/(?P<abn>(?P=abn)(?<badstufxxx)/BZ + +/(?P<abn>(?P=axn)xxx)/BZ + +/(?P<abn>(?P=axn)xxx)(?<axn>yy)/BZ + /-- End of testinput2 --/ diff --git a/ext/pcre/pcrelib/testdata/testinput4 b/ext/pcre/pcrelib/testdata/testinput4 index 12f4c7e3b..e2bae423b 100644 --- a/ext/pcre/pcrelib/testdata/testinput4 +++ b/ext/pcre/pcrelib/testdata/testinput4 @@ -463,7 +463,8 @@ /a\Cb/8 aXb a\nb - *** Failers + +/a\C\Cb/8 a\x{100}b /[z-\x{100}]/8i @@ -640,4 +641,7 @@ a\x{c0}aaaa/ a\x{c0}a\x{c0}aaa/ +/A*/g8 + AAB\x{123}BAA + /-- End of testinput4 --/ diff --git a/ext/pcre/pcrelib/testdata/testinput6 b/ext/pcre/pcrelib/testdata/testinput6 index e923e0099..503a5bc76 100644 --- a/ext/pcre/pcrelib/testdata/testinput6 +++ b/ext/pcre/pcrelib/testdata/testinput6 @@ -1,5 +1,5 @@ /-- This set of tests is for Unicode property support. It is compatible with - Perl 5.10, but not 5.8 because it tests some extra properties that are + Perl >= 5.10, but not 5.8 because it tests some extra properties that are not in the earlier release. --/ /^\pC\pL\pM\pN\pP\pS\pZ</8 diff --git a/ext/pcre/pcrelib/testdata/testinput7 b/ext/pcre/pcrelib/testdata/testinput7 index 5d2731192..66748873c 100644 --- a/ext/pcre/pcrelib/testdata/testinput7 +++ b/ext/pcre/pcrelib/testdata/testinput7 @@ -4411,6 +4411,9 @@ abc\Y abcxypqr abcxypqr\Y + +/(*NO_START_OPT)xyz/C + abcxyz /(?C)ab/ ab @@ -4560,4 +4563,48 @@ /^(?(?!a(*SKIP)b))/ ac +/(?<=abc)def/ + abc\P\P + +/abc$/ + abc + abc\P + abc\P\P + +/abc$/m + abc + abc\n + abc\P\P + abc\n\P\P + abc\P + abc\n\P + +/abc\z/ + abc + abc\P + abc\P\P + +/abc\Z/ + abc + abc\P + abc\P\P + +/abc\b/ + abc + abc\P + abc\P\P + +/abc\B/ + abc + abc\P + abc\P\P + +/.+/ + abc\>0 + abc\>1 + abc\>2 + abc\>3 + abc\>4 + abc\>-4 + /-- End of testinput7 --/ diff --git a/ext/pcre/pcrelib/testdata/testinput8 b/ext/pcre/pcrelib/testdata/testinput8 index 1c6f684b4..55d2fd3c0 100644 --- a/ext/pcre/pcrelib/testdata/testinput8 +++ b/ext/pcre/pcrelib/testdata/testinput8 @@ -63,6 +63,9 @@ à ÃÃà ÃÃÃ\? + \xe1\x88 + \P\xe1\x88 + \P\P\xe1\x88 /a.b/8 acb @@ -685,4 +688,16 @@ xxxxabcde\P xxxxabcde\P\P +/\bthe cat\b/8 + the cat\P + the cat\P\P + +/a+/8 + a\x{123}aa\>1 + a\x{123}aa\>2 + a\x{123}aa\>3 + a\x{123}aa\>4 + a\x{123}aa\>5 + a\x{123}aa\>6 + /-- End of testinput8 --/ diff --git a/ext/pcre/pcrelib/testdata/testoutput1 b/ext/pcre/pcrelib/testdata/testoutput1 index 2fd033cc3..6ab67a725 100644 --- a/ext/pcre/pcrelib/testdata/testoutput1 +++ b/ext/pcre/pcrelib/testdata/testoutput1 @@ -6658,4 +6658,12 @@ No match XABX No match +/[\x00-\xff\s]+/ + \x0a\x0b\x0c\x0d + 0: \x0a\x0b\x0c\x0d + +/^\c/ + ? + 0: ? + /-- End of testinput1 --/ diff --git a/ext/pcre/pcrelib/testdata/testoutput10 b/ext/pcre/pcrelib/testdata/testoutput10 index 4994584dd..b88474c7b 100644 --- a/ext/pcre/pcrelib/testdata/testoutput10 +++ b/ext/pcre/pcrelib/testdata/testoutput10 @@ -707,4 +707,33 @@ Memory allocation (code space): 40 18 End ------------------------------------------------------------------ +/abc(d|e)(*THEN)x(123(*THEN)4|567(b|q)(*THEN)xx)/B +------------------------------------------------------------------ + 0 79 Bra + 3 abc + 9 7 CBra 1 + 14 d + 16 5 Alt + 19 e + 21 12 Ket + 24 *THEN 24 + 27 x + 29 16 CBra 2 + 34 123 + 40 *THEN 11 + 43 4 + 45 31 Alt + 48 567 + 54 7 CBra 3 + 59 b + 61 5 Alt + 64 q + 66 12 Ket + 69 *THEN 24 + 72 xx + 76 47 Ket + 79 79 Ket + 82 End +------------------------------------------------------------------ + /-- End of testinput10 --/ diff --git a/ext/pcre/pcrelib/testdata/testoutput2 b/ext/pcre/pcrelib/testdata/testoutput2 index 2baa6e923..531d617f0 100644 --- a/ext/pcre/pcrelib/testdata/testoutput2 +++ b/ext/pcre/pcrelib/testdata/testoutput2 @@ -5,7 +5,7 @@ either because PCRE can't be compatible, or there is a possible Perl bug. --/ -/-- Originally, the Perl 5.10 and 5.11 things were in here too, but now I have +/-- Originally, the Perl >= 5.10 things were in here too, but now I have separated many (most?) of them out into test 11. However, there may still be some that were overlooked. --/ @@ -8787,6 +8787,27 @@ No match No match a\x85b No match + +/(*ANY).*/g + abc\r\ndef + 0: abc + 0: + 0: def + 0: + +/(*ANYCRLF).*/g + abc\r\ndef + 0: abc + 0: + 0: def + 0: + +/(*CRLF).*/g + abc\r\ndef + 0: abc + 0: + 0: def + 0: /a\Rb/I<bsr_anycrlf> Capturing subpattern count = 0 @@ -9273,6 +9294,30 @@ No match +0 ^ x +0 ^ x No match + +/(*NO_START_OPT)xyz/C + abcxyz +--->abcxyz ++15 ^ x ++15 ^ x ++15 ^ x ++15 ^ x ++16 ^^ y ++17 ^ ^ z ++18 ^ ^ + 0: xyz + +/xyz/CY + abcxyz +--->abcxyz + +0 ^ x + +0 ^ x + +0 ^ x + +0 ^ x + +1 ^^ y + +2 ^ ^ z + +3 ^ ^ + 0: xyz /^"((?(?=[a])[^"])|b)*"$/C "ab" @@ -10459,9 +10504,9 @@ Partial match: abca xxxxabcde\P\P Partial match: abcde -/-- This is not in the Perl 5.10 test because Perl seems currently to be broken - and not behaving as specified in that it *does* bumpalong after hitting - (*COMMIT). --/ +/-- This is not in the Perl >= 5.10 test because Perl seems currently to be + broken and not behaving as specified in that it *does* bumpalong after + hitting (*COMMIT). --/ /(?1)(A(*COMMIT)|B)D/ ABD @@ -10664,7 +10709,7 @@ No match End ------------------------------------------------------------------ -/ -- The first four of these are not in the Perl 5.10 test because Perl +/ -- The first four of these are not in the Perl >= 5.10 test because Perl documents that the use of \K in assertions is "not well defined". The last is here because Perl gives the match as "b" rather than "ab". I believe this to be a Perl bug. --/ @@ -11043,13 +11088,13 @@ No match Ket End ------------------------------------------------------------------ - ACBD + ACBD 0: ACBD - ** Failers + *** Failers No match - A\nB + A\nB No match - ACB\n + ACB\n No match /A\NB./sBZ @@ -11062,23 +11107,23 @@ No match Ket End ------------------------------------------------------------------ - ACBD + ACBD 0: ACBD - ACB\n + ACB\n 0: ACB\x0a - ** Failers + *** Failers No match - A\nB + A\nB No match /A\NB/<crlf> - A\nB + A\nB 0: A\x0aB - A\rB + A\rB 0: A\x0dB - ** Failers + ** Failers No match - A\r\nB + A\r\nB No match /\R+b/BZ @@ -11126,4 +11171,138 @@ No match End ------------------------------------------------------------------ +/-- Perl treats this one differently, not failing the second string. I believe + that is a bug in Perl. --/ + +/^((abc|abcx)(*THEN)y|abcd)/ + abcd + 0: abcd + 1: abcd + *** Failers +No match + abcxy +No match + +/(?<=abc)def/ + abc\P\P +Partial match: abc + +/abc$/ + abc + 0: abc + abc\P + 0: abc + abc\P\P +Partial match: abc + +/abc$/m + abc + 0: abc + abc\n + 0: abc + abc\P\P +Partial match: abc + abc\n\P\P + 0: abc + abc\P + 0: abc + abc\n\P + 0: abc + +/abc\z/ + abc + 0: abc + abc\P + 0: abc + abc\P\P +Partial match: abc + +/abc\Z/ + abc + 0: abc + abc\P + 0: abc + abc\P\P +Partial match: abc + +/abc\b/ + abc + 0: abc + abc\P + 0: abc + abc\P\P +Partial match: abc + +/abc\B/ + abc +No match + abc\P +Partial match: abc + abc\P\P +Partial match: abc + +/.+/ + abc\>0 + 0: abc + abc\>1 + 0: bc + abc\>2 + 0: c + abc\>3 +No match + abc\>4 +Error -24 + abc\>-4 +Error -24 + +/^\cÄ£/ +Failed: \c must be followed by an ASCII character at offset 3 + +/(?P<abn>(?P=abn)xxx)/BZ +------------------------------------------------------------------ + Bra + Once + CBra 1 + \1 + xxx + Ket + Ket + Ket + End +------------------------------------------------------------------ + +/(a\1z)/BZ +------------------------------------------------------------------ + Bra + Once + CBra 1 + a + \1 + z + Ket + Ket + Ket + End +------------------------------------------------------------------ + +/(?P<abn>(?P=abn)(?<badstufxxx)/BZ +Failed: syntax error in subpattern name (missing terminator) at offset 29 + +/(?P<abn>(?P=axn)xxx)/BZ +Failed: reference to non-existent subpattern at offset 15 + +/(?P<abn>(?P=axn)xxx)(?<axn>yy)/BZ +------------------------------------------------------------------ + Bra + CBra 1 + \2 + xxx + Ket + CBra 2 + yy + Ket + Ket + End +------------------------------------------------------------------ + /-- End of testinput2 --/ diff --git a/ext/pcre/pcrelib/testdata/testoutput4 b/ext/pcre/pcrelib/testdata/testoutput4 index 128afe438..4591026d4 100644 --- a/ext/pcre/pcrelib/testdata/testoutput4 +++ b/ext/pcre/pcrelib/testdata/testoutput4 @@ -802,10 +802,10 @@ No match 0: aXb a\nb 0: a\x{0a}b - *** Failers -No match + +/a\C\Cb/8 a\x{100}b -No match + 0: a\x{100}b /[z-\x{100}]/8i z @@ -1119,4 +1119,13 @@ No match 0: a\x{c0}a\x{c0} 1: a\x{c0} +/A*/g8 + AAB\x{123}BAA + 0: AA + 0: + 0: + 0: + 0: AA + 0: + /-- End of testinput4 --/ diff --git a/ext/pcre/pcrelib/testdata/testoutput6 b/ext/pcre/pcrelib/testdata/testoutput6 index e000083b0..6a9ec839d 100644 --- a/ext/pcre/pcrelib/testdata/testoutput6 +++ b/ext/pcre/pcrelib/testdata/testoutput6 @@ -1,5 +1,5 @@ /-- This set of tests is for Unicode property support. It is compatible with - Perl 5.10, but not 5.8 because it tests some extra properties that are + Perl >= 5.10, but not 5.8 because it tests some extra properties that are not in the earlier release. --/ /^\pC\pL\pM\pN\pP\pS\pZ</8 diff --git a/ext/pcre/pcrelib/testdata/testoutput7 b/ext/pcre/pcrelib/testdata/testoutput7 index 2aab80d74..123ba82c4 100644 --- a/ext/pcre/pcrelib/testdata/testoutput7 +++ b/ext/pcre/pcrelib/testdata/testoutput7 @@ -7319,6 +7319,18 @@ No match +0 ^ x +0 ^ x No match + +/(*NO_START_OPT)xyz/C + abcxyz +--->abcxyz ++15 ^ x ++15 ^ x ++15 ^ x ++15 ^ x ++16 ^^ y ++17 ^ ^ z ++18 ^ ^ + 0: xyz /(?C)ab/ ab @@ -7610,4 +7622,79 @@ Error -16 ac Error -16 +/(?<=abc)def/ + abc\P\P +Partial match: abc + +/abc$/ + abc + 0: abc + abc\P + 0: abc + abc\P\P +Partial match: abc + +/abc$/m + abc + 0: abc + abc\n + 0: abc + abc\P\P +Partial match: abc + abc\n\P\P + 0: abc + abc\P + 0: abc + abc\n\P + 0: abc + +/abc\z/ + abc + 0: abc + abc\P + 0: abc + abc\P\P +Partial match: abc + +/abc\Z/ + abc + 0: abc + abc\P + 0: abc + abc\P\P +Partial match: abc + +/abc\b/ + abc + 0: abc + abc\P + 0: abc + abc\P\P +Partial match: abc + +/abc\B/ + abc +No match + abc\P +Partial match: abc + abc\P\P +Partial match: abc + +/.+/ + abc\>0 + 0: abc + 1: ab + 2: a + abc\>1 + 0: bc + 1: b + abc\>2 + 0: c + abc\>3 +No match + abc\>4 +Error -24 + abc\>-4 +Error -24 + /-- End of testinput7 --/ diff --git a/ext/pcre/pcrelib/testdata/testoutput8 b/ext/pcre/pcrelib/testdata/testoutput8 index 0cc87d79d..f4f5343ef 100644 --- a/ext/pcre/pcrelib/testdata/testoutput8 +++ b/ext/pcre/pcrelib/testdata/testoutput8 @@ -105,6 +105,12 @@ Error -10 Error -10 ÃÃÃ\? No match + \xe1\x88 +Error -10 + \P\xe1\x88 +Error -10 + \P\P\xe1\x88 +Error -25 /a.b/8 acb @@ -1320,4 +1326,26 @@ Partial match: abc1 xxxxabcde\P\P Partial match: abcde +/\bthe cat\b/8 + the cat\P + 0: the cat + the cat\P\P +Partial match: the cat + +/a+/8 + a\x{123}aa\>1 + 0: aa + 1: a + a\x{123}aa\>2 +Error -11 + a\x{123}aa\>3 + 0: aa + 1: a + a\x{123}aa\>4 + 0: a + a\x{123}aa\>5 +No match + a\x{123}aa\>6 +Error -24 + /-- End of testinput8 --/ diff --git a/ext/pcre/php_pcre.c b/ext/pcre/php_pcre.c index 1efbec323..6c616ec20 100644 --- a/ext/pcre/php_pcre.c +++ b/ext/pcre/php_pcre.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: php_pcre.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: php_pcre.c 314349 2011-08-05 22:39:40Z rasmus $ */ #include "php.h" #include "php_ini.h" @@ -115,7 +115,7 @@ static PHP_GSHUTDOWN_FUNCTION(pcre) /* {{{ */ /* }}} */ PHP_INI_BEGIN() - STD_PHP_INI_ENTRY("pcre.backtrack_limit", "100000", PHP_INI_ALL, OnUpdateLong, backtrack_limit, zend_pcre_globals, pcre_globals) + STD_PHP_INI_ENTRY("pcre.backtrack_limit", "1000000", PHP_INI_ALL, OnUpdateLong, backtrack_limit, zend_pcre_globals, pcre_globals) STD_PHP_INI_ENTRY("pcre.recursion_limit", "100000", PHP_INI_ALL, OnUpdateLong, recursion_limit, zend_pcre_globals, pcre_globals) PHP_INI_END() @@ -644,6 +644,7 @@ PHPAPI void php_pcre_match_impl(pcre_cache_entry *pce, char *subject, int subjec if (pcre_get_substring_list(subject, offsets, count, &stringlist) < 0) { efree(subpat_names); efree(offsets); + if (match_sets) efree(match_sets); php_error_docref(NULL TSRMLS_CC, E_WARNING, "Get subpatterns list failed"); RETURN_FALSE; } @@ -1894,7 +1895,7 @@ static const zend_function_entry pcre_functions[] = { PHP_FE(preg_quote, arginfo_preg_quote) PHP_FE(preg_grep, arginfo_preg_grep) PHP_FE(preg_last_error, arginfo_preg_last_error) - {NULL, NULL, NULL} + PHP_FE_END }; zend_module_entry pcre_module_entry = { diff --git a/ext/pcre/upgrade-pcre.php b/ext/pcre/upgrade-pcre.php index 338b2079f..a48f8d760 100644 --- a/ext/pcre/upgrade-pcre.php +++ b/ext/pcre/upgrade-pcre.php @@ -103,6 +103,11 @@ foreach ($diff as $file) { // the config.h needs special care $prepend_config_h = ' #include <php_compat.h> + +#ifndef PHP_WIN32 +# include <php_config.h> +#endif + #undef PACKAGE_NAME #undef PACKAGE_VERSION #undef PACKAGE_TARNAME diff --git a/ext/pdo/pdo.c b/ext/pdo/pdo.c index 372eaee20..e2947df0a 100755 --- a/ext/pdo/pdo.c +++ b/ext/pdo/pdo.c @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: pdo.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: pdo.c 314450 2011-08-07 23:46:00Z iliaa $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -125,7 +125,7 @@ ZEND_END_ARG_INFO() /* {{{ pdo_functions[] */ const zend_function_entry pdo_functions[] = { PHP_FE(pdo_drivers, arginfo_pdo_drivers) - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ @@ -135,7 +135,7 @@ static const zend_module_dep pdo_deps[] = { #ifdef HAVE_SPL ZEND_MOD_REQUIRED("spl") #endif - {NULL, NULL, NULL} + ZEND_MOD_END }; #endif /* }}} */ @@ -191,7 +191,7 @@ PDO_API int php_pdo_register_driver(pdo_driver_t *driver) } return zend_hash_add(&pdo_driver_hash, (char*)driver->driver_name, driver->driver_name_len, - (void**)&driver, sizeof(driver), NULL); + (void**)&driver, sizeof(pdo_driver_t *), NULL); } PDO_API void php_pdo_unregister_driver(pdo_driver_t *driver) diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c index a355491ed..5adb15833 100755 --- a/ext/pdo/pdo_dbh.c +++ b/ext/pdo/pdo_dbh.c @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: pdo_dbh.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: pdo_dbh.c 314450 2011-08-07 23:46:00Z iliaa $ */ /* The PDO Database Handle Class */ @@ -57,7 +57,7 @@ void pdo_raise_impl_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *sqlstate pdo_err = &stmt->error_code; } - strcpy(*pdo_err, sqlstate); + strncpy(*pdo_err, sqlstate, 6); /* hash sqlstate to error messages */ msg = pdo_sqlstate_state_to_description(*pdo_err); diff --git a/ext/pdo/pdo_sql_parser.c b/ext/pdo/pdo_sql_parser.c index 3898ec8b1..1fa56e50b 100644 --- a/ext/pdo/pdo_sql_parser.c +++ b/ext/pdo/pdo_sql_parser.c @@ -1,4 +1,4 @@ -/* Generated by re2c 0.13.6.dev on Thu Nov 13 14:47:06 2008 */ +/* Generated by re2c 0.13.5 on Sat Jun 4 18:42:25 2011 */ /* +----------------------------------------------------------------------+ | PHP Version 5 | @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: pdo_sql_parser.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: pdo_sql_parser.c 311812 2011-06-04 22:54:40Z felipe $ */ #include "php.h" #include "php_pdo_driver.h" @@ -29,7 +29,7 @@ #define PDO_PARSER_EOI 4 #define RET(i) {s->cur = cursor; return i; } -#define SKIP_ONE(i) {s->cur = s->tok + 1; return 1; } +#define SKIP_ONE(i) {s->cur = s->tok + 1; return i; } #define YYCTYPE unsigned char #define YYCURSOR cursor @@ -51,26 +51,31 @@ static int scan(Scanner *s) { YYCTYPE yych; + unsigned int yyaccept = 0; if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2); yych = *YYCURSOR; switch (yych) { - case 0x00: goto yy11; + case 0x00: goto yy13; case '"': goto yy2; case '\'': goto yy4; + case '-': goto yy10; + case '/': goto yy8; case ':': goto yy5; case '?': goto yy6; - default: goto yy8; + default: goto yy11; } yy2: + yyaccept = 0; yych = *(YYMARKER = ++YYCURSOR); - if (yych >= 0x01) goto yy26; + if (yych >= 0x01) goto yy43; yy3: { SKIP_ONE(PDO_PARSER_TEXT); } yy4: + yyaccept = 0; yych = *(YYMARKER = ++YYCURSOR); if (yych <= 0x00) goto yy3; - goto yy20; + goto yy37; yy5: yych = *++YYCURSOR; switch (yych) { @@ -136,49 +141,146 @@ yy5: case 'w': case 'x': case 'y': - case 'z': goto yy16; + case 'z': goto yy33; case ':': - case '?': goto yy13; + case '?': goto yy30; default: goto yy3; } yy6: ++YYCURSOR; switch ((yych = *YYCURSOR)) { case ':': - case '?': goto yy13; + case '?': goto yy30; default: goto yy7; } yy7: { RET(PDO_PARSER_BIND_POS); } yy8: ++YYCURSOR; + switch ((yych = *YYCURSOR)) { + case '*': goto yy20; + default: goto yy12; + } +yy9: + { RET(PDO_PARSER_TEXT); } +yy10: + yych = *++YYCURSOR; + switch (yych) { + case '-': goto yy15; + default: goto yy12; + } +yy11: + ++YYCURSOR; if (YYLIMIT <= YYCURSOR) YYFILL(1); yych = *YYCURSOR; +yy12: switch (yych) { case 0x00: case '"': case '\'': case ':': - case '?': goto yy10; - default: goto yy8; + case '?': goto yy9; + default: goto yy11; } -yy10: - { RET(PDO_PARSER_TEXT); } -yy11: +yy13: ++YYCURSOR; { RET(PDO_PARSER_EOI); } -yy13: +yy15: ++YYCURSOR; if (YYLIMIT <= YYCURSOR) YYFILL(1); yych = *YYCURSOR; switch (yych) { + case 0x00: + case '"': + case '\'': case ':': - case '?': goto yy13; + case '?': goto yy18; + case '\n': + case '\r': goto yy11; default: goto yy15; } -yy15: +yy17: + { RET(PDO_PARSER_TEXT); } +yy18: + ++YYCURSOR; + if (YYLIMIT <= YYCURSOR) YYFILL(1); + yych = *YYCURSOR; + switch (yych) { + case '\n': + case '\r': goto yy17; + default: goto yy18; + } +yy20: + yyaccept = 1; + YYMARKER = ++YYCURSOR; + if (YYLIMIT <= YYCURSOR) YYFILL(1); + yych = *YYCURSOR; + switch (yych) { + case 0x00: + case '"': + case '\'': + case ':': + case '?': goto yy22; + case '*': goto yy24; + default: goto yy20; + } +yy22: + ++YYCURSOR; + if (YYLIMIT <= YYCURSOR) YYFILL(1); + yych = *YYCURSOR; + switch (yych) { + case '*': goto yy27; + default: goto yy22; + } +yy24: + yyaccept = 1; + YYMARKER = ++YYCURSOR; + if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2); + yych = *YYCURSOR; + switch (yych) { + case 0x00: + case '"': + case '\'': + case ':': + case '?': goto yy22; + case '*': goto yy24; + case '/': goto yy26; + default: goto yy20; + } +yy26: + yych = *++YYCURSOR; + switch (yych) { + case 0x00: + case '"': + case '\'': + case ':': + case '?': goto yy17; + default: goto yy11; + } +yy27: + ++YYCURSOR; + if (YYLIMIT <= YYCURSOR) YYFILL(1); + yych = *YYCURSOR; + switch (yych) { + case '*': goto yy27; + case '/': goto yy29; + default: goto yy22; + } +yy29: + yych = *++YYCURSOR; + goto yy17; +yy30: + ++YYCURSOR; + if (YYLIMIT <= YYCURSOR) YYFILL(1); + yych = *YYCURSOR; + switch (yych) { + case ':': + case '?': goto yy30; + default: goto yy32; + } +yy32: { RET(PDO_PARSER_TEXT); } -yy16: +yy33: ++YYCURSOR; if (YYLIMIT <= YYCURSOR) YYFILL(1); yych = *YYCURSOR; @@ -245,52 +347,55 @@ yy16: case 'w': case 'x': case 'y': - case 'z': goto yy16; - default: goto yy18; + case 'z': goto yy33; + default: goto yy35; } -yy18: +yy35: { RET(PDO_PARSER_BIND); } -yy19: +yy36: ++YYCURSOR; if (YYLIMIT <= YYCURSOR) YYFILL(1); yych = *YYCURSOR; -yy20: +yy37: switch (yych) { - case 0x00: goto yy21; - case '\'': goto yy23; - case '\\': goto yy22; - default: goto yy19; + case 0x00: goto yy38; + case '\'': goto yy40; + case '\\': goto yy39; + default: goto yy36; } -yy21: +yy38: YYCURSOR = YYMARKER; - goto yy3; -yy22: + switch (yyaccept) { + case 0: goto yy3; + case 1: goto yy9; + } +yy39: ++YYCURSOR; if (YYLIMIT <= YYCURSOR) YYFILL(1); yych = *YYCURSOR; - if (yych <= 0x00) goto yy21; - goto yy19; -yy23: + if (yych <= 0x00) goto yy38; + goto yy36; +yy40: ++YYCURSOR; { RET(PDO_PARSER_TEXT); } -yy25: +yy42: ++YYCURSOR; if (YYLIMIT <= YYCURSOR) YYFILL(1); yych = *YYCURSOR; -yy26: +yy43: switch (yych) { - case 0x00: goto yy21; - case '"': goto yy28; - case '\\': goto yy27; - default: goto yy25; + case 0x00: goto yy38; + case '"': goto yy45; + case '\\': goto yy44; + default: goto yy42; } -yy27: +yy44: ++YYCURSOR; if (YYLIMIT <= YYCURSOR) YYFILL(1); yych = *YYCURSOR; - if (yych <= 0x00) goto yy21; - goto yy25; -yy28: + if (yych <= 0x00) goto yy38; + goto yy42; +yy45: ++YYCURSOR; { RET(PDO_PARSER_TEXT); } } diff --git a/ext/pdo/pdo_sql_parser.c.orig b/ext/pdo/pdo_sql_parser.c.orig index 7b0ec46ad..c6e8b0787 100644 --- a/ext/pdo/pdo_sql_parser.c.orig +++ b/ext/pdo/pdo_sql_parser.c.orig @@ -1,4 +1,4 @@ -/* Generated by re2c 0.13.6.dev on Thu Nov 13 14:47:06 2008 */ +/* Generated by re2c 0.13.5 on Sat Jun 4 18:42:25 2011 */ #line 1 "ext/pdo/pdo_sql_parser.re" /* +----------------------------------------------------------------------+ @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: pdo_sql_parser.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: pdo_sql_parser.c 311812 2011-06-04 22:54:40Z felipe $ */ #include "php.h" #include "php_pdo_driver.h" @@ -30,7 +30,7 @@ #define PDO_PARSER_EOI 4 #define RET(i) {s->cur = cursor; return i; } -#define SKIP_ONE(i) {s->cur = s->tok + 1; return 1; } +#define SKIP_ONE(i) {s->cur = s->tok + 1; return i; } #define YYCTYPE unsigned char #define YYCURSOR cursor @@ -47,35 +47,40 @@ static int scan(Scanner *s) char *cursor = s->cur; s->tok = cursor; - #line 55 "ext/pdo/pdo_sql_parser.re" + #line 56 "ext/pdo/pdo_sql_parser.re" #line 55 "ext/pdo/pdo_sql_parser.c" { YYCTYPE yych; + unsigned int yyaccept = 0; if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2); yych = *YYCURSOR; switch (yych) { - case 0x00: goto yy11; + case 0x00: goto yy13; case '"': goto yy2; case '\'': goto yy4; + case '-': goto yy10; + case '/': goto yy8; case ':': goto yy5; case '?': goto yy6; - default: goto yy8; + default: goto yy11; } yy2: + yyaccept = 0; yych = *(YYMARKER = ++YYCURSOR); - if (yych >= 0x01) goto yy26; + if (yych >= 0x01) goto yy43; yy3: -#line 63 "ext/pdo/pdo_sql_parser.re" +#line 64 "ext/pdo/pdo_sql_parser.re" { SKIP_ONE(PDO_PARSER_TEXT); } -#line 75 "ext/pdo/pdo_sql_parser.c" +#line 79 "ext/pdo/pdo_sql_parser.c" yy4: + yyaccept = 0; yych = *(YYMARKER = ++YYCURSOR); if (yych <= 0x00) goto yy3; - goto yy20; + goto yy37; yy5: yych = *++YYCURSOR; switch (yych) { @@ -141,57 +146,156 @@ yy5: case 'w': case 'x': case 'y': - case 'z': goto yy16; + case 'z': goto yy33; case ':': - case '?': goto yy13; + case '?': goto yy30; default: goto yy3; } yy6: ++YYCURSOR; switch ((yych = *YYCURSOR)) { case ':': - case '?': goto yy13; + case '?': goto yy30; default: goto yy7; } yy7: -#line 62 "ext/pdo/pdo_sql_parser.re" +#line 63 "ext/pdo/pdo_sql_parser.re" { RET(PDO_PARSER_BIND_POS); } -#line 160 "ext/pdo/pdo_sql_parser.c" +#line 165 "ext/pdo/pdo_sql_parser.c" yy8: ++YYCURSOR; + switch ((yych = *YYCURSOR)) { + case '*': goto yy20; + default: goto yy12; + } +yy9: +#line 66 "ext/pdo/pdo_sql_parser.re" + { RET(PDO_PARSER_TEXT); } +#line 175 "ext/pdo/pdo_sql_parser.c" +yy10: + yych = *++YYCURSOR; + switch (yych) { + case '-': goto yy15; + default: goto yy12; + } +yy11: + ++YYCURSOR; if (YYLIMIT <= YYCURSOR) YYFILL(1); yych = *YYCURSOR; +yy12: switch (yych) { case 0x00: case '"': case '\'': case ':': - case '?': goto yy10; - default: goto yy8; + case '?': goto yy9; + default: goto yy11; } -yy10: -#line 64 "ext/pdo/pdo_sql_parser.re" - { RET(PDO_PARSER_TEXT); } -#line 176 "ext/pdo/pdo_sql_parser.c" -yy11: +yy13: ++YYCURSOR; -#line 65 "ext/pdo/pdo_sql_parser.re" +#line 67 "ext/pdo/pdo_sql_parser.re" { RET(PDO_PARSER_EOI); } -#line 181 "ext/pdo/pdo_sql_parser.c" -yy13: +#line 199 "ext/pdo/pdo_sql_parser.c" +yy15: ++YYCURSOR; if (YYLIMIT <= YYCURSOR) YYFILL(1); yych = *YYCURSOR; switch (yych) { + case 0x00: + case '"': + case '\'': case ':': - case '?': goto yy13; + case '?': goto yy18; + case '\n': + case '\r': goto yy11; default: goto yy15; } -yy15: -#line 60 "ext/pdo/pdo_sql_parser.re" +yy17: +#line 65 "ext/pdo/pdo_sql_parser.re" + { RET(PDO_PARSER_TEXT); } +#line 217 "ext/pdo/pdo_sql_parser.c" +yy18: + ++YYCURSOR; + if (YYLIMIT <= YYCURSOR) YYFILL(1); + yych = *YYCURSOR; + switch (yych) { + case '\n': + case '\r': goto yy17; + default: goto yy18; + } +yy20: + yyaccept = 1; + YYMARKER = ++YYCURSOR; + if (YYLIMIT <= YYCURSOR) YYFILL(1); + yych = *YYCURSOR; + switch (yych) { + case 0x00: + case '"': + case '\'': + case ':': + case '?': goto yy22; + case '*': goto yy24; + default: goto yy20; + } +yy22: + ++YYCURSOR; + if (YYLIMIT <= YYCURSOR) YYFILL(1); + yych = *YYCURSOR; + switch (yych) { + case '*': goto yy27; + default: goto yy22; + } +yy24: + yyaccept = 1; + YYMARKER = ++YYCURSOR; + if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2); + yych = *YYCURSOR; + switch (yych) { + case 0x00: + case '"': + case '\'': + case ':': + case '?': goto yy22; + case '*': goto yy24; + case '/': goto yy26; + default: goto yy20; + } +yy26: + yych = *++YYCURSOR; + switch (yych) { + case 0x00: + case '"': + case '\'': + case ':': + case '?': goto yy17; + default: goto yy11; + } +yy27: + ++YYCURSOR; + if (YYLIMIT <= YYCURSOR) YYFILL(1); + yych = *YYCURSOR; + switch (yych) { + case '*': goto yy27; + case '/': goto yy29; + default: goto yy22; + } +yy29: + yych = *++YYCURSOR; + goto yy17; +yy30: + ++YYCURSOR; + if (YYLIMIT <= YYCURSOR) YYFILL(1); + yych = *YYCURSOR; + switch (yych) { + case ':': + case '?': goto yy30; + default: goto yy32; + } +yy32: +#line 61 "ext/pdo/pdo_sql_parser.re" { RET(PDO_PARSER_TEXT); } -#line 194 "ext/pdo/pdo_sql_parser.c" -yy16: +#line 298 "ext/pdo/pdo_sql_parser.c" +yy33: ++YYCURSOR; if (YYLIMIT <= YYCURSOR) YYFILL(1); yych = *YYCURSOR; @@ -258,62 +362,65 @@ yy16: case 'w': case 'x': case 'y': - case 'z': goto yy16; - default: goto yy18; + case 'z': goto yy33; + default: goto yy35; } -yy18: -#line 61 "ext/pdo/pdo_sql_parser.re" +yy35: +#line 62 "ext/pdo/pdo_sql_parser.re" { RET(PDO_PARSER_BIND); } -#line 268 "ext/pdo/pdo_sql_parser.c" -yy19: +#line 372 "ext/pdo/pdo_sql_parser.c" +yy36: ++YYCURSOR; if (YYLIMIT <= YYCURSOR) YYFILL(1); yych = *YYCURSOR; -yy20: +yy37: switch (yych) { - case 0x00: goto yy21; - case '\'': goto yy23; - case '\\': goto yy22; - default: goto yy19; + case 0x00: goto yy38; + case '\'': goto yy40; + case '\\': goto yy39; + default: goto yy36; } -yy21: +yy38: YYCURSOR = YYMARKER; - goto yy3; -yy22: + switch (yyaccept) { + case 0: goto yy3; + case 1: goto yy9; + } +yy39: ++YYCURSOR; if (YYLIMIT <= YYCURSOR) YYFILL(1); yych = *YYCURSOR; - if (yych <= 0x00) goto yy21; - goto yy19; -yy23: + if (yych <= 0x00) goto yy38; + goto yy36; +yy40: ++YYCURSOR; -#line 59 "ext/pdo/pdo_sql_parser.re" +#line 60 "ext/pdo/pdo_sql_parser.re" { RET(PDO_PARSER_TEXT); } -#line 293 "ext/pdo/pdo_sql_parser.c" -yy25: +#line 400 "ext/pdo/pdo_sql_parser.c" +yy42: ++YYCURSOR; if (YYLIMIT <= YYCURSOR) YYFILL(1); yych = *YYCURSOR; -yy26: +yy43: switch (yych) { - case 0x00: goto yy21; - case '"': goto yy28; - case '\\': goto yy27; - default: goto yy25; + case 0x00: goto yy38; + case '"': goto yy45; + case '\\': goto yy44; + default: goto yy42; } -yy27: +yy44: ++YYCURSOR; if (YYLIMIT <= YYCURSOR) YYFILL(1); yych = *YYCURSOR; - if (yych <= 0x00) goto yy21; - goto yy25; -yy28: + if (yych <= 0x00) goto yy38; + goto yy42; +yy45: ++YYCURSOR; -#line 58 "ext/pdo/pdo_sql_parser.re" +#line 59 "ext/pdo/pdo_sql_parser.re" { RET(PDO_PARSER_TEXT); } -#line 315 "ext/pdo/pdo_sql_parser.c" +#line 422 "ext/pdo/pdo_sql_parser.c" } -#line 66 "ext/pdo/pdo_sql_parser.re" +#line 68 "ext/pdo/pdo_sql_parser.re" } diff --git a/ext/pdo/pdo_sql_parser.re b/ext/pdo/pdo_sql_parser.re index c56934de2..55ae5e38d 100644 --- a/ext/pdo/pdo_sql_parser.re +++ b/ext/pdo/pdo_sql_parser.re @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: pdo_sql_parser.re 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: pdo_sql_parser.re 314451 2011-08-08 00:07:54Z iliaa $ */ #include "php.h" #include "php_pdo_driver.h" @@ -28,7 +28,7 @@ #define PDO_PARSER_EOI 4 #define RET(i) {s->cur = cursor; return i; } -#define SKIP_ONE(i) {s->cur = s->tok + 1; return 1; } +#define SKIP_ONE(i) {s->cur = s->tok + 1; return i; } #define YYCTYPE unsigned char #define YYCURSOR cursor @@ -48,6 +48,7 @@ static int scan(Scanner *s) /*!re2c BINDCHR = [:][a-zA-Z0-9_]+; QUESTION = [?]; + COMMENTS = ("/*"([^*]+|[*]+[^/*])*[*]*"*/"|"--"[^\r\n]*); SPECIALS = [:?"']; MULTICHAR = [:?]; EOF = [\000]; @@ -61,6 +62,7 @@ static int scan(Scanner *s) BINDCHR { RET(PDO_PARSER_BIND); } QUESTION { RET(PDO_PARSER_BIND_POS); } SPECIALS { SKIP_ONE(PDO_PARSER_TEXT); } + COMMENTS { RET(PDO_PARSER_TEXT); } (ANYNOEOF\SPECIALS)+ { RET(PDO_PARSER_TEXT); } EOF { RET(PDO_PARSER_EOI); } */ @@ -211,7 +213,7 @@ safe: param->param_type TSRMLS_CC)) { /* bork */ ret = -1; - strcpy(stmt->error_code, stmt->dbh->error_code); + strncpy(stmt->error_code, stmt->dbh->error_code, 6); if (buf) { efree(buf); } @@ -234,6 +236,9 @@ safe: plc->freeq = 0; break; + case IS_BOOL: + convert_to_long(param->parameter); + case IS_LONG: case IS_DOUBLE: convert_to_string(param->parameter); @@ -242,8 +247,6 @@ safe: plc->freeq = 0; break; - case IS_BOOL: - convert_to_long(param->parameter); default: convert_to_string(param->parameter); if (!stmt->dbh->methods->quoter(stmt->dbh, Z_STRVAL_P(param->parameter), @@ -251,7 +254,7 @@ safe: param->param_type TSRMLS_CC)) { /* bork */ ret = -1; - strcpy(stmt->error_code, stmt->dbh->error_code); + strncpy(stmt->error_code, stmt->dbh->error_code, 6); goto clean_up; } plc->freeq = 1; diff --git a/ext/pdo/pdo_stmt.c b/ext/pdo/pdo_stmt.c index cb1a01562..04cdad030 100755 --- a/ext/pdo/pdo_stmt.c +++ b/ext/pdo/pdo_stmt.c @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: pdo_stmt.c 308569 2011-02-22 15:48:25Z iliaa $ */ +/* $Id: pdo_stmt.c 312258 2011-06-18 15:56:14Z felipe $ */ /* The PDO Statement Handle Class */ @@ -38,6 +38,9 @@ #include "php_memory_streams.h" /* {{{ arginfo */ +ZEND_BEGIN_ARG_INFO(arginfo_pdostatement__void, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(arginfo_pdostatement_execute, 0, 0, 0) ZEND_ARG_INFO(0, bound_input_params) /* array */ ZEND_END_ARG_INFO() @@ -349,7 +352,10 @@ static int really_register_bound_param(struct pdo_bound_param_data *param, pdo_s /* if you prepare and then execute passing an array of params keyed by names, * then this will trigger, and we don't want that */ if (param->paramno == -1) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Did not found column name '%s' in the defined columns; it will not be bound", param->name); + char *tmp; + spprintf(&tmp, 0, "Did not find column name '%s' in the defined columns; it will not be bound", param->name); + pdo_raise_impl_error(stmt->dbh, stmt, "HY000", tmp TSRMLS_CC); + efree(tmp); } } @@ -2218,22 +2224,22 @@ const zend_function_entry pdo_dbstmt_functions[] = { PHP_ME(PDOStatement, bindParam, arginfo_pdostatement_bindparam, ZEND_ACC_PUBLIC) PHP_ME(PDOStatement, bindColumn, arginfo_pdostatement_bindcolumn, ZEND_ACC_PUBLIC) PHP_ME(PDOStatement, bindValue, arginfo_pdostatement_bindvalue, ZEND_ACC_PUBLIC) - PHP_ME(PDOStatement, rowCount, NULL, ZEND_ACC_PUBLIC) + PHP_ME(PDOStatement, rowCount, arginfo_pdostatement__void, ZEND_ACC_PUBLIC) PHP_ME(PDOStatement, fetchColumn, arginfo_pdostatement_fetchcolumn, ZEND_ACC_PUBLIC) PHP_ME(PDOStatement, fetchAll, arginfo_pdostatement_fetchall, ZEND_ACC_PUBLIC) PHP_ME(PDOStatement, fetchObject, arginfo_pdostatement_fetchobject, ZEND_ACC_PUBLIC) - PHP_ME(PDOStatement, errorCode, NULL, ZEND_ACC_PUBLIC) - PHP_ME(PDOStatement, errorInfo, NULL, ZEND_ACC_PUBLIC) + PHP_ME(PDOStatement, errorCode, arginfo_pdostatement__void, ZEND_ACC_PUBLIC) + PHP_ME(PDOStatement, errorInfo, arginfo_pdostatement__void, ZEND_ACC_PUBLIC) PHP_ME(PDOStatement, setAttribute, arginfo_pdostatement_setattribute, ZEND_ACC_PUBLIC) PHP_ME(PDOStatement, getAttribute, arginfo_pdostatement_getattribute, ZEND_ACC_PUBLIC) - PHP_ME(PDOStatement, columnCount, NULL, ZEND_ACC_PUBLIC) + PHP_ME(PDOStatement, columnCount, arginfo_pdostatement__void, ZEND_ACC_PUBLIC) PHP_ME(PDOStatement, getColumnMeta, arginfo_pdostatement_getcolumnmeta, ZEND_ACC_PUBLIC) PHP_ME(PDOStatement, setFetchMode, arginfo_pdostatement_setfetchmode, ZEND_ACC_PUBLIC) - PHP_ME(PDOStatement, nextRowset, NULL, ZEND_ACC_PUBLIC) - PHP_ME(PDOStatement, closeCursor, NULL, ZEND_ACC_PUBLIC) - PHP_ME(PDOStatement, debugDumpParams, NULL, ZEND_ACC_PUBLIC) - PHP_ME(PDOStatement, __wakeup, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) - PHP_ME(PDOStatement, __sleep, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) + PHP_ME(PDOStatement, nextRowset, arginfo_pdostatement__void, ZEND_ACC_PUBLIC) + PHP_ME(PDOStatement, closeCursor, arginfo_pdostatement__void, ZEND_ACC_PUBLIC) + PHP_ME(PDOStatement, debugDumpParams, arginfo_pdostatement__void, ZEND_ACC_PUBLIC) + PHP_ME(PDOStatement, __wakeup, arginfo_pdostatement__void, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) + PHP_ME(PDOStatement, __sleep, arginfo_pdostatement__void, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) {NULL, NULL, NULL} }; diff --git a/ext/pdo/php_pdo_int.h b/ext/pdo/php_pdo_int.h index 20ca89f6a..03c717958 100755 --- a/ext/pdo/php_pdo_int.h +++ b/ext/pdo/php_pdo_int.h @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: php_pdo_int.h 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: php_pdo_int.h 314450 2011-08-07 23:46:00Z iliaa $ */ /* Stuff private to the PDO extension and not for consumption by PDO drivers * */ @@ -58,7 +58,7 @@ extern pdo_driver_t *pdo_find_driver(const char *name, int namelen); extern void pdo_handle_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt TSRMLS_DC); #define PDO_DBH_CLEAR_ERR() do { \ - strcpy(dbh->error_code, PDO_ERR_NONE); \ + strncpy(dbh->error_code, PDO_ERR_NONE, sizeof(PDO_ERR_NONE)); \ if (dbh->query_stmt) { \ dbh->query_stmt = NULL; \ zend_objects_store_del_ref(&dbh->query_stmt_zval TSRMLS_CC); \ diff --git a/ext/pdo_dblib/config.m4 b/ext/pdo_dblib/config.m4 index 5068b8765..6a164a8de 100644 --- a/ext/pdo_dblib/config.m4 +++ b/ext/pdo_dblib/config.m4 @@ -1,5 +1,5 @@ dnl -dnl $Id: config.m4 291414 2009-11-29 06:13:22Z rasmus $ +dnl $Id: config.m4 311041 2011-05-15 05:49:34Z rasmus $ dnl PHP_ARG_WITH(pdo-dblib, for PDO_DBLIB support via FreeTDS, diff --git a/ext/pdo_dblib/dblib_stmt.c b/ext/pdo_dblib/dblib_stmt.c index 4b71ab7e0..1f5af7fa7 100644 --- a/ext/pdo_dblib/dblib_stmt.c +++ b/ext/pdo_dblib/dblib_stmt.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: dblib_stmt.c 308972 2011-03-06 13:36:44Z felipe $ */ +/* $Id: dblib_stmt.c 312860 2011-07-03 19:01:42Z felipe $ */ #ifdef HAVE_CONFIG_H # include "config.h" @@ -25,6 +25,7 @@ #include "php.h" #include "php_ini.h" +#include "ext/standard/php_string.h" #include "ext/standard/info.h" #include "pdo/php_pdo.h" #include "pdo/php_pdo_driver.h" @@ -38,7 +39,7 @@ static void free_rows(pdo_dblib_stmt *S TSRMLS_DC) for (i = 0; i < S->nrows; i++) { for (j = 0; j < S->ncols; j++) { - pdo_dblib_colval *val = &S->rows[i] + j; + pdo_dblib_colval *val = &S->rows[i*S->ncols] + j; if (val->data) { efree(val->data); val->data = NULL; @@ -173,6 +174,23 @@ static int pdo_dblib_stmt_execute(pdo_stmt_t *stmt TSRMLS_DC) val->len = spprintf(&val->data, 0, "%.4f", money_value); } break; +#ifdef SQLUNIQUE + case SQLUNIQUE: { +#else + case 36: { /* FreeTDS hack, also used by ext/mssql */ +#endif + val->len = 36+1; + val->data = emalloc(val->len + 1); + + /* uniqueidentifier is a 16-byte binary number, convert to 32 char hex string */ +#ifdef SQLUNIQUE + val->len = dbconvert(NULL, SQLUNIQUE, dbdata(H->link, i+1), dbdatlen(H->link, i+1), SQLCHAR, val->data, val->len); +#else + val->len = dbconvert(NULL, 36, dbdata(H->link, i+1), dbdatlen(H->link, i+1), SQLCHAR, val->data, val->len); +#endif + php_strtoupper(val->data, val->len); + break; + } default: if (dbwillconvert(S->cols[i].coltype, SQLCHAR)) { val->len = 32 + (2 * dbdatlen(H->link, i+1)); diff --git a/ext/pdo_dblib/pdo_dblib.c b/ext/pdo_dblib/pdo_dblib.c index 1b64b76c7..d189c33a2 100644 --- a/ext/pdo_dblib/pdo_dblib.c +++ b/ext/pdo_dblib/pdo_dblib.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: pdo_dblib.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: pdo_dblib.c 314376 2011-08-06 14:47:44Z felipe $ */ #ifdef HAVE_CONFIG_H # include "config.h" @@ -36,13 +36,13 @@ ZEND_DECLARE_MODULE_GLOBALS(dblib) static PHP_GINIT_FUNCTION(dblib); const zend_function_entry pdo_dblib_functions[] = { - {NULL, NULL, NULL} + PHP_FE_END }; #if ZEND_MODULE_API_NO >= 20050922 static const zend_module_dep pdo_dblib_deps[] = { ZEND_MOD_REQUIRED("pdo") - {NULL, NULL, NULL} + ZEND_MOD_END }; #endif diff --git a/ext/pdo_firebird/config.m4 b/ext/pdo_firebird/config.m4 index e878bfb85..875f6fcb1 100644 --- a/ext/pdo_firebird/config.m4 +++ b/ext/pdo_firebird/config.m4 @@ -1,5 +1,5 @@ dnl -dnl $Id: config.m4 291414 2009-11-29 06:13:22Z rasmus $ +dnl $Id: config.m4 311041 2011-05-15 05:49:34Z rasmus $ dnl PHP_ARG_WITH(pdo-firebird,for Firebird support for PDO, diff --git a/ext/pdo_firebird/firebird_driver.c b/ext/pdo_firebird/firebird_driver.c index 542f88a79..0efd6a158 100644 --- a/ext/pdo_firebird/firebird_driver.c +++ b/ext/pdo_firebird/firebird_driver.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: firebird_driver.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: firebird_driver.c 312225 2011-06-17 02:00:20Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -232,6 +232,7 @@ static long firebird_handle_doer(pdo_dbh_t *dbh, const char *sql, long sql_len T /* TODO no placeholders in exec() for now */ in_sqlda.version = out_sqlda.version = PDO_FB_SQLDA_VERSION; in_sqlda.sqld = out_sqlda.sqld = 0; + out_sqlda.sqln = 1; /* allocate and prepare statement */ if (!firebird_alloc_prepare_stmt(dbh, sql, sql_len, &out_sqlda, &stmt, 0 TSRMLS_CC)) { diff --git a/ext/pdo_firebird/firebird_statement.c b/ext/pdo_firebird/firebird_statement.c index 06c252fd0..562b047a9 100644 --- a/ext/pdo_firebird/firebird_statement.c +++ b/ext/pdo_firebird/firebird_statement.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: firebird_statement.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: firebird_statement.c 312225 2011-06-17 02:00:20Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -191,7 +191,7 @@ static int firebird_stmt_describe(pdo_stmt_t *stmt, int colno TSRMLS_DC) /* {{{ char *cp; /* allocate storage for the column */ - var->sqlind = (void*)emalloc(var->sqllen + 2*sizeof(short)); + var->sqlind = (void*)ecalloc(1, var->sqllen + 2*sizeof(short)); var->sqldata = &((char*)var->sqlind)[sizeof(short)]; colname_len = (S->H->fetch_table_names && var->relname_length) diff --git a/ext/pdo_firebird/pdo_firebird.c b/ext/pdo_firebird/pdo_firebird.c index bb5ae306c..8dd35aff7 100644 --- a/ext/pdo_firebird/pdo_firebird.c +++ b/ext/pdo_firebird/pdo_firebird.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: pdo_firebird.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: pdo_firebird.c 314376 2011-08-06 14:47:44Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -31,7 +31,7 @@ #include "php_pdo_firebird_int.h" const zend_function_entry pdo_firebird_functions[] = { /* {{{ */ - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ @@ -40,7 +40,7 @@ const zend_function_entry pdo_firebird_functions[] = { /* {{{ */ #if ZEND_MODULE_API_NO >= 20050922 static const zend_module_dep pdo_firebird_deps[] = { ZEND_MOD_REQUIRED("pdo") - {NULL, NULL, NULL} + ZEND_MOD_END }; #endif /* }}} */ diff --git a/ext/pdo_mysql/config.m4 b/ext/pdo_mysql/config.m4 index 2eb2f8262..48d7573af 100755 --- a/ext/pdo_mysql/config.m4 +++ b/ext/pdo_mysql/config.m4 @@ -1,4 +1,4 @@ -dnl $Id: config.m4 297583 2010-04-06 13:42:21Z tony2001 $ +dnl $Id: config.m4 311041 2011-05-15 05:49:34Z rasmus $ dnl config.m4 for extension pdo_mysql dnl vim: se ts=2 sw=2 et: diff --git a/ext/pdo_mysql/mysql_driver.c b/ext/pdo_mysql/mysql_driver.c index b9b2b5535..ff161f7f6 100755 --- a/ext/pdo_mysql/mysql_driver.c +++ b/ext/pdo_mysql/mysql_driver.c @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: mysql_driver.c 307529 2011-01-17 09:54:22Z kalle $ */ +/* $Id: mysql_driver.c 310239 2011-04-15 14:24:40Z rrichards $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -625,6 +625,9 @@ static int pdo_mysql_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_ char *default_file = NULL, *default_group = NULL; long compress = 0; #endif +#if HAVE_MYSQL_STMT_PREPARE || PDO_USE_MYSLQND + char *ssl_key = NULL, *ssl_cert = NULL, *ssl_ca = NULL, *ssl_capath = NULL, *ssl_cipher = NULL; +#endif H->buffered = pdo_attr_lval(driver_options, PDO_MYSQL_ATTR_USE_BUFFERED_QUERY, 1 TSRMLS_CC); H->emulate_prepare = pdo_attr_lval(driver_options, @@ -709,6 +712,32 @@ static int pdo_mysql_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_ } } #endif +#if HAVE_MYSQL_STMT_PREPARE || PDO_USE_MYSLQND + ssl_key = pdo_attr_strval(driver_options, PDO_MYSQL_ATTR_SSL_KEY, NULL TSRMLS_CC); + ssl_cert = pdo_attr_strval(driver_options, PDO_MYSQL_ATTR_SSL_CERT, NULL TSRMLS_CC); + ssl_ca = pdo_attr_strval(driver_options, PDO_MYSQL_ATTR_SSL_CA, NULL TSRMLS_CC); + ssl_capath = pdo_attr_strval(driver_options, PDO_MYSQL_ATTR_SSL_CAPATH, NULL TSRMLS_CC); + ssl_cipher = pdo_attr_strval(driver_options, PDO_MYSQL_ATTR_SSL_CIPHER, NULL TSRMLS_CC); + + if (ssl_key || ssl_cert || ssl_ca || ssl_capath || ssl_cipher) { + mysql_ssl_set(H->server, ssl_key, ssl_cert, ssl_ca, ssl_capath, ssl_cipher); + if (ssl_key) { + efree(ssl_key); + } + if (ssl_cert) { + efree(ssl_cert); + } + if (ssl_ca) { + efree(ssl_ca); + } + if (ssl_capath) { + efree(ssl_capath); + } + if (ssl_cipher) { + efree(ssl_cipher); + } + } +#endif } #ifdef PDO_MYSQL_HAS_CHARSET diff --git a/ext/pdo_mysql/mysql_statement.c b/ext/pdo_mysql/mysql_statement.c index 49e7296ea..c2a593f1d 100755 --- a/ext/pdo_mysql/mysql_statement.c +++ b/ext/pdo_mysql/mysql_statement.c @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: mysql_statement.c 307478 2011-01-14 14:57:57Z johannes $ */ +/* $Id: mysql_statement.c 311088 2011-05-16 15:37:39Z johannes $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -656,7 +656,11 @@ static int pdo_mysql_stmt_fetch(pdo_stmt_t *stmt, #endif /* PDO_USE_MYSQLND */ if ((S->current_data = mysql_fetch_row(S->result)) == NULL) { - if (mysql_errno(S->H->server)) { +#if PDO_USE_MYSQLND + if (S->result->unbuf && !S->result->unbuf->eof_reached && mysql_errno(S->H->server)) { +#else + if (!S->result->eof && mysql_errno(S->H->server)) { +#endif pdo_mysql_error_stmt(stmt); } PDO_DBG_RETURN(0); diff --git a/ext/pdo_mysql/pdo_mysql.c b/ext/pdo_mysql/pdo_mysql.c index a09133f23..85829e208 100755 --- a/ext/pdo_mysql/pdo_mysql.c +++ b/ext/pdo_mysql/pdo_mysql.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: pdo_mysql.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: pdo_mysql.c 314376 2011-08-06 14:47:44Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -79,7 +79,11 @@ static PHP_MINIT_FUNCTION(pdo_mysql) REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_DIRECT_QUERY", (long)PDO_MYSQL_ATTR_DIRECT_QUERY); REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_FOUND_ROWS", (long)PDO_MYSQL_ATTR_FOUND_ROWS); REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_IGNORE_SPACE", (long)PDO_MYSQL_ATTR_IGNORE_SPACE); - + REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_SSL_KEY", (long)PDO_MYSQL_ATTR_SSL_KEY); + REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_SSL_CERT", (long)PDO_MYSQL_ATTR_SSL_CERT); + REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_SSL_CA", (long)PDO_MYSQL_ATTR_SSL_CA); + REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_SSL_CAPATH", (long)PDO_MYSQL_ATTR_SSL_CAPATH); + REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_SSL_CIPHER", (long)PDO_MYSQL_ATTR_SSL_CIPHER); return php_pdo_register_driver(&pdo_mysql_driver); } /* }}} */ @@ -166,7 +170,7 @@ static PHP_GINIT_FUNCTION(pdo_mysql) /* {{{ pdo_mysql_functions[] */ const zend_function_entry pdo_mysql_functions[] = { - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ @@ -177,7 +181,7 @@ static const zend_module_dep pdo_mysql_deps[] = { #ifdef PDO_USE_MYSQLND ZEND_MOD_REQUIRED("mysqlnd") #endif - {NULL, NULL, NULL} + ZEND_MOD_END }; #endif /* }}} */ diff --git a/ext/pdo_mysql/php_pdo_mysql_int.h b/ext/pdo_mysql/php_pdo_mysql_int.h index efc103e8c..7a3c89995 100755 --- a/ext/pdo_mysql/php_pdo_mysql_int.h +++ b/ext/pdo_mysql/php_pdo_mysql_int.h @@ -18,14 +18,13 @@ +----------------------------------------------------------------------+ */ -/* $Id: php_pdo_mysql_int.h 307529 2011-01-17 09:54:22Z kalle $ */ +/* $Id: php_pdo_mysql_int.h 310850 2011-05-09 11:34:17Z johannes $ */ #ifndef PHP_PDO_MYSQL_INT_H #define PHP_PDO_MYSQL_INT_H #if defined(PDO_USE_MYSQLND) # include "ext/mysqlnd/mysqlnd.h" -# include "ext/mysql/mysql_mysqlnd.h" # include "ext/mysqlnd/mysqlnd_libmysql_compat.h" # define PDO_MYSQL_PARAM_BIND MYSQLND_PARAM_BIND #else @@ -168,7 +167,12 @@ enum { #endif PDO_MYSQL_ATTR_DIRECT_QUERY, PDO_MYSQL_ATTR_FOUND_ROWS, - PDO_MYSQL_ATTR_IGNORE_SPACE + PDO_MYSQL_ATTR_IGNORE_SPACE, + PDO_MYSQL_ATTR_SSL_KEY, + PDO_MYSQL_ATTR_SSL_CERT, + PDO_MYSQL_ATTR_SSL_CA, + PDO_MYSQL_ATTR_SSL_CAPATH, + PDO_MYSQL_ATTR_SSL_CIPHER }; #endif diff --git a/ext/pdo_mysql/tests/bug53782.phpt b/ext/pdo_mysql/tests/bug53782.phpt new file mode 100644 index 000000000..4f81cceef --- /dev/null +++ b/ext/pdo_mysql/tests/bug53782.phpt @@ -0,0 +1,40 @@ +--TEST-- +PDO MySQL Bug #53782 (foreach throws irrelevant exception) +--SKIPIF-- +<?php +if (!extension_loaded('pdo') || !extension_loaded('pdo_mysql')) die('skip not loaded'); +require dirname(__FILE__) . '/config.inc'; +require dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc'; +PDOTest::skip(); +?> +--FILE-- +<?php +require dirname(__FILE__) . '/config.inc'; +require dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc'; +$conn = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt'); + +$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + +$res = $conn->query('SELECT 0'); + +try { + $conn->query('ERROR'); +} catch (PDOException $e) { + echo "Caught: ".$e->getMessage()."\n"; +} + +foreach ($res as $k => $v) { + echo "Value: $v[0]\n"; +} + +echo "DONE"; +?> +--CLEAN-- +<?php +require dirname(__FILE__) . '/mysql_pdo_test.inc'; +MySQLPDOTest::dropTestTable(); +?> +--EXPECTF-- +Caught: SQLSTATE[42000]: %s +Value: 0 +DONE diff --git a/ext/pdo_mysql/tests/bug54929.phpt b/ext/pdo_mysql/tests/bug54929.phpt new file mode 100644 index 000000000..29fb44182 --- /dev/null +++ b/ext/pdo_mysql/tests/bug54929.phpt @@ -0,0 +1,74 @@ +--TEST-- +Bug #54929 (Parse error with single quote in sql comment (pdo-mysql)) +--SKIPIF-- +<?php +require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc'); +require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc'); +MySQLPDOTest::skip(); + +?> +--FILE-- +<?php + +require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'mysql_pdo_test.inc'); + +$pdodb = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt'); + + +function testQuery($query) { + global $pdodb; + $stmt = $pdodb->prepare($query); + + if (!$stmt->execute(array("foo"))) { + var_dump($stmt->errorInfo()); + } else{ + var_dump($stmt->fetch(PDO::FETCH_ASSOC)); + } +} + +testQuery("/* ' */ select ? as f1 /* ' */"); +testQuery("/* '-- */ select ? as f1 /* *' */"); +testQuery("/* ' */ select ? as f1 --';"); +testQuery("/* ' */ select ? as f1 -- 'a;"); +testQuery("/*'**/ select ? as f1 /* ' */"); +testQuery("/*'***/ select ? as f1 /* ' */"); +testQuery("/*'**a ***b / **** +****** +**/ select ? as f1 /* ' */"); + +?> +--EXPECTF-- +array(1) { + ["f1"]=> + string(3) "foo" +} +array(1) { + ["f1"]=> + string(3) "foo" +} + +Warning: PDOStatement::execute(): SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '--'' at line 1 in %s on line %d +array(3) { + [0]=> + string(5) "42000" + [1]=> + int(1064) + [2]=> + string(149) "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '--'' at line 1" +} +array(1) { + ["f1"]=> + string(3) "foo" +} +array(1) { + ["f1"]=> + string(3) "foo" +} +array(1) { + ["f1"]=> + string(3) "foo" +} +array(1) { + ["f1"]=> + string(3) "foo" +} diff --git a/ext/pdo_mysql/tests/mysql_pdo_test.inc b/ext/pdo_mysql/tests/mysql_pdo_test.inc index a3ffd5b9c..0af2e6df6 100644 --- a/ext/pdo_mysql/tests/mysql_pdo_test.inc +++ b/ext/pdo_mysql/tests/mysql_pdo_test.inc @@ -155,6 +155,7 @@ class MySQLPDOTest extends PDOTest { phpinfo(); $tmp = ob_get_contents(); ob_end_clean(); + $tmp = stristr($tmp, "PDO Driver for MySQL => enabled"); return (bool)preg_match('/Client API version.*mysqlnd/', $tmp); } diff --git a/ext/pdo_mysql/tests/pdo_mysql_class_constants.phpt b/ext/pdo_mysql/tests/pdo_mysql_class_constants.phpt index 9200d7bbd..e99385799 100644 --- a/ext/pdo_mysql/tests/pdo_mysql_class_constants.phpt +++ b/ext/pdo_mysql/tests/pdo_mysql_class_constants.phpt @@ -15,13 +15,18 @@ require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'skipif.inc'); 'MYSQL_ATTR_FOUND_ROWS' => true, 'MYSQL_ATTR_IGNORE_SPACE' => true, 'MYSQL_ATTR_INIT_COMMAND' => true, + "MYSQL_ATTR_SSL_KEY" => true, + "MYSQL_ATTR_SSL_CERT" => true, + "MYSQL_ATTR_SSL_CA" => true, + "MYSQL_ATTR_SSL_CAPATH" => true, + "MYSQL_ATTR_SSL_CIPHER" => true, ); if (!MySQLPDOTest::isPDOMySQLnd()) { - $expected['MYSQL_ATTR_MAX_BUFFER_SIZE'] = true; - $expected['MYSQL_ATTR_READ_DEFAULT_FILE'] = true; - $expected['MYSQL_ATTR_READ_DEFAULT_GROUP'] = true; - $expected['MYSQL_ATTR_COMPRESS'] = true; + $expected['MYSQL_ATTR_MAX_BUFFER_SIZE'] = true; + $expected['MYSQL_ATTR_READ_DEFAULT_FILE'] = true; + $expected['MYSQL_ATTR_READ_DEFAULT_GROUP'] = true; + $expected['MYSQL_ATTR_COMPRESS'] = true; } /* diff --git a/ext/pdo_oci/config.m4 b/ext/pdo_oci/config.m4 index 0fd2027a2..607124fb4 100755 --- a/ext/pdo_oci/config.m4 +++ b/ext/pdo_oci/config.m4 @@ -1,4 +1,4 @@ -dnl $Id: config.m4 294487 2010-02-04 01:12:14Z johannes $ +dnl $Id: config.m4 311041 2011-05-15 05:49:34Z rasmus $ dnl config.m4 for extension pdo_oci dnl vim:et:sw=2:ts=2: @@ -44,8 +44,10 @@ PHP_ARG_WITH(pdo-oci, Oracle OCI support for PDO, [ --with-pdo-oci[=DIR] PDO: Oracle OCI support. DIR defaults to \$ORACLE_HOME. Use --with-pdo-oci=instantclient,prefix,version for an Oracle Instant Client SDK. - For Linux with 10.2.0.3 RPMs (for example) use: - --with-pdo-oci=instantclient,/usr,10.2.0.3]) + For example on Linux with 11.2 RPMs use: + --with-pdo-oci=instantclient,/usr,11.2 + With 10.2 RPMs use: + --with-pdo-oci=instantclient,/usr,10.2.0.4]) if test "$PHP_PDO_OCI" != "no"; then @@ -71,29 +73,42 @@ You need to tell me where to find your Oracle Instant Client SDK, or set ORACLE_ fi if test "instantclient" = "`echo $PDO_OCI_DIR | cut -d, -f1`" ; then + AC_CHECK_SIZEOF(long int, 4) + if test "$ac_cv_sizeof_long_int" = "4" ; then + PDO_OCI_CLIENT_DIR="client" + else + PDO_OCI_CLIENT_DIR="client64" + fi PDO_OCI_IC_PREFIX="`echo $PDO_OCI_DIR | cut -d, -f2`" PDO_OCI_IC_VERS="`echo $PDO_OCI_DIR | cut -d, -f3`" + if test -n "$PDO_OCI_IC_VERS"; then + PDO_OCI_IC_MAJ_VER="`echo $PDO_OCI_IC_VERS | cut -d. -f1`" + if test "$PDO_OCI_IC_MAJ_VER" -ge 11; then + # From 11.1.0.7 the RPM path only has an X.Y component + PDO_OCI_IC_VERS="`echo $PDO_OCI_IC_VERS | cut -d. -f1-2`" + fi + fi AC_MSG_CHECKING([for oci.h]) - if test -f $PDO_OCI_IC_PREFIX/include/oracle/$PDO_OCI_IC_VERS/client/oci.h ; then - PHP_ADD_INCLUDE($PDO_OCI_IC_PREFIX/include/oracle/$PDO_OCI_IC_VERS/client) - AC_MSG_RESULT($PDO_OCI_IC_PREFIX/include/oracle/$PDO_OCI_IC_VERS/client) - elif test -f $PDO_OCI_IC_PREFIX/lib/oracle/$PDO_OCI_IC_VERS/client/include/oci.h ; then - PHP_ADD_INCLUDE($PDO_OCI_IC_PREFIX/lib/oracle/$PDO_OCI_IC_VERS/client/include) - AC_MSG_RESULT($PDO_OCI_IC_PREFIX/lib/oracle/$PDO_OCI_IC_VERS/client/include) + if test -f $PDO_OCI_IC_PREFIX/include/oracle/$PDO_OCI_IC_VERS/$PDO_OCI_CLIENT_DIR/oci.h ; then + PHP_ADD_INCLUDE($PDO_OCI_IC_PREFIX/include/oracle/$PDO_OCI_IC_VERS/$PDO_OCI_CLIENT_DIR) + AC_MSG_RESULT($PDO_OCI_IC_PREFIX/include/oracle/$PDO_OCI_IC_VERS/$PDO_OCI_CLIENT_DIR) + elif test -f $PDO_OCI_IC_PREFIX/lib/oracle/$PDO_OCI_IC_VERS/$PDO_OCI_CLIENT_DIR/include/oci.h ; then + PHP_ADD_INCLUDE($PDO_OCI_IC_PREFIX/lib/oracle/$PDO_OCI_IC_VERS/$PDO_OCI_CLIENT_DIR/include) + AC_MSG_RESULT($PDO_OCI_IC_PREFIX/lib/oracle/$PDO_OCI_IC_VERS/$PDO_OCI_CLIENT_DIR/include) elif test -f $PDO_OCI_IC_PREFIX/sdk/include/oci.h ; then PHP_ADD_INCLUDE($PDO_OCI_IC_PREFIX/sdk/include) AC_MSG_RESULT($PDO_OCI_IC_PREFIX/sdk/include) - elif test -f $PDO_OCI_IC_PREFIX/client/include/oci.h ; then - PHP_ADD_INCLUDE($PDO_OCI_IC_PREFIX/client/include) - AC_MSG_RESULT($PDO_OCI_IC_PREFIX/client/include) + elif test -f $PDO_OCI_IC_PREFIX/$PDO_OCI_CLIENT_DIR/include/oci.h ; then + PHP_ADD_INCLUDE($PDO_OCI_IC_PREFIX/$PDO_OCI_CLIENT_DIR/include) + AC_MSG_RESULT($PDO_OCI_IC_PREFIX/$PDO_OCI_CLIENT_DIR/include) else AC_MSG_ERROR([I'm too dumb to figure out where the include dir is in your Instant Client install]) fi - if test -f "$PDO_OCI_IC_PREFIX/lib/oracle/$PDO_OCI_IC_VERS/client/lib/libclntsh.so" ; then - PDO_OCI_LIB_DIR="$PDO_OCI_IC_PREFIX/lib/oracle/$PDO_OCI_IC_VERS/client/lib" - elif test -f "$PDO_OCI_IC_PREFIX/client/lib/libclntsh.so" ; then - PDO_OCI_LIB_DIR="$PDO_OCI_IC_PREFIX/client/lib" - elif test -f "$PDO_OCI_IC_PREFIX/libclntsh.so" ; then + if test -f "$PDO_OCI_IC_PREFIX/lib/oracle/$PDO_OCI_IC_VERS/$PDO_OCI_CLIENT_DIR/lib/libclntsh.$SHLIB_SUFFIX_NAME" ; then + PDO_OCI_LIB_DIR="$PDO_OCI_IC_PREFIX/lib/oracle/$PDO_OCI_IC_VERS/$PDO_OCI_CLIENT_DIR/lib" + elif test -f "$PDO_OCI_IC_PREFIX/$PDO_OCI_CLIENT_DIR/lib/libclntsh.$SHLIB_SUFFIX_NAME" ; then + PDO_OCI_LIB_DIR="$PDO_OCI_IC_PREFIX/$PDO_OCI_CLIENT_DIR/lib" + elif test -f "$PDO_OCI_IC_PREFIX/libclntsh.$SHLIB_SUFFIX_NAME" ; then PDO_OCI_LIB_DIR="$PDO_OCI_IC_PREFIX" else AC_MSG_ERROR([I'm too dumb to figure out where the libraries are in your Instant Client install]) diff --git a/ext/pdo_oci/pdo_oci.c b/ext/pdo_oci/pdo_oci.c index 35826e029..ebd5591cd 100755 --- a/ext/pdo_oci/pdo_oci.c +++ b/ext/pdo_oci/pdo_oci.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: pdo_oci.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: pdo_oci.c 314376 2011-08-06 14:47:44Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -32,7 +32,7 @@ /* {{{ pdo_oci_functions[] */ const zend_function_entry pdo_oci_functions[] = { - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ @@ -41,7 +41,7 @@ const zend_function_entry pdo_oci_functions[] = { #if ZEND_MODULE_API_NO >= 20050922 static const zend_module_dep pdo_oci_deps[] = { ZEND_MOD_REQUIRED("pdo") - {NULL, NULL, NULL} + ZEND_MOD_END }; #endif diff --git a/ext/pdo_odbc/config.m4 b/ext/pdo_odbc/config.m4 index c846d13b6..90086e356 100755 --- a/ext/pdo_odbc/config.m4 +++ b/ext/pdo_odbc/config.m4 @@ -1,4 +1,4 @@ -dnl $Id: config.m4 291414 2009-11-29 06:13:22Z rasmus $ +dnl $Id: config.m4 311041 2011-05-15 05:49:34Z rasmus $ dnl config.m4 for extension pdo_odbc dnl vim:et:sw=2:ts=2: diff --git a/ext/pdo_odbc/odbc_driver.c b/ext/pdo_odbc/odbc_driver.c index c1498896f..8eea0c66a 100755 --- a/ext/pdo_odbc/odbc_driver.c +++ b/ext/pdo_odbc/odbc_driver.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: odbc_driver.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: odbc_driver.c 312506 2011-06-27 01:36:39Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -224,7 +224,7 @@ static long odbc_handle_doer(pdo_dbh_t *dbh, const char *sql, long sql_len TSRML { pdo_odbc_db_handle *H = (pdo_odbc_db_handle *)dbh->driver_data; RETCODE rc; - long row_count = -1; + SQLLEN row_count = -1; PDO_ODBC_HSTMT stmt; rc = SQLAllocHandle(SQL_HANDLE_STMT, H->dbc, &stmt); diff --git a/ext/pdo_odbc/odbc_stmt.c b/ext/pdo_odbc/odbc_stmt.c index 4a59cc641..fb7605486 100755 --- a/ext/pdo_odbc/odbc_stmt.c +++ b/ext/pdo_odbc/odbc_stmt.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: odbc_stmt.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: odbc_stmt.c 312506 2011-06-27 01:36:39Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -164,7 +164,7 @@ static int odbc_stmt_execute(pdo_stmt_t *stmt TSRMLS_DC) RETCODE rc; pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; char *buf = NULL; - long row_count = -1; + SQLLEN row_count = -1; if (stmt->executed) { SQLCloseCursor(S->stmt); diff --git a/ext/pdo_odbc/pdo_odbc.c b/ext/pdo_odbc/pdo_odbc.c index 56d46b50a..046bf4c3a 100755 --- a/ext/pdo_odbc/pdo_odbc.c +++ b/ext/pdo_odbc/pdo_odbc.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: pdo_odbc.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: pdo_odbc.c 314376 2011-08-06 14:47:44Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -40,7 +40,7 @@ const function_entry pdo_odbc_functions[] = { #if ZEND_MODULE_API_NO >= 20050922 static const zend_module_dep pdo_odbc_deps[] = { ZEND_MOD_REQUIRED("pdo") - {NULL, NULL, NULL} + ZEND_MOD_END }; #endif /* }}} */ @@ -98,6 +98,9 @@ PHP_MINIT_FUNCTION(pdo_odbc) char *instance = INI_STR("pdo_odbc.db2_instance_name"); if (instance) { char *env = malloc(sizeof("DB2INSTANCE=") + strlen(instance)); + if (!env) { + return FAILURE; + } strcpy(env, "DB2INSTANCE="); strcat(env, instance); putenv(env); diff --git a/ext/pdo_odbc/php_pdo_odbc_int.h b/ext/pdo_odbc/php_pdo_odbc_int.h index 26fc75063..2f9a6cc60 100755 --- a/ext/pdo_odbc/php_pdo_odbc_int.h +++ b/ext/pdo_odbc/php_pdo_odbc_int.h @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: php_pdo_odbc_int.h 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: php_pdo_odbc_int.h 312506 2011-06-27 01:36:39Z felipe $ */ #ifdef PHP_WIN32 # define PDO_ODBC_TYPE "Win32" @@ -136,7 +136,7 @@ typedef struct { typedef struct { char *data; unsigned long datalen; - long fetched_len; + SQLLEN fetched_len; SWORD coltype; char colname[128]; unsigned is_long; diff --git a/ext/pdo_pgsql/config.m4 b/ext/pdo_pgsql/config.m4 index f75d79196..329cdc365 100644 --- a/ext/pdo_pgsql/config.m4 +++ b/ext/pdo_pgsql/config.m4 @@ -1,4 +1,4 @@ -dnl $Id: config.m4 291414 2009-11-29 06:13:22Z rasmus $ +dnl $Id: config.m4 311041 2011-05-15 05:49:34Z rasmus $ dnl config.m4 for extension pdo_pgsql dnl vim:et:sw=2:ts=2: @@ -69,7 +69,8 @@ if test "$PHP_PDO_PGSQL" != "no"; then AC_DEFINE(HAVE_PDO_PGSQL,1,[Whether to build PostgreSQL for PDO support or not]) AC_MSG_CHECKING([for openssl dependencies]) - if grep -q openssl $PGSQL_INCLUDE/libpq-fe.h ; then + grep openssl $PGSQL_INCLUDE/libpq-fe.h >/dev/null 2>&1 + if test $? -eq 0 ; then AC_MSG_RESULT([yes]) dnl First try to find pkg-config AC_PATH_PROG(PKG_CONFIG, pkg-config, no) diff --git a/ext/pdo_pgsql/pdo_pgsql.c b/ext/pdo_pgsql/pdo_pgsql.c index 268bd3765..3ad08ad23 100644 --- a/ext/pdo_pgsql/pdo_pgsql.c +++ b/ext/pdo_pgsql/pdo_pgsql.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: pdo_pgsql.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: pdo_pgsql.c 314376 2011-08-06 14:47:44Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -50,7 +50,7 @@ const zend_function_entry pdo_pgsql_functions[] = { #if ZEND_MODULE_API_NO >= 20050922 static const zend_module_dep pdo_pgsql_deps[] = { ZEND_MOD_REQUIRED("pdo") - {NULL, NULL, NULL} + ZEND_MOD_END }; #endif /* }}} */ @@ -67,8 +67,8 @@ zend_module_entry pdo_pgsql_module_entry = { pdo_pgsql_functions, PHP_MINIT(pdo_pgsql), PHP_MSHUTDOWN(pdo_pgsql), - PHP_RINIT(pdo_pgsql), - PHP_RSHUTDOWN(pdo_pgsql), + NULL, + NULL, PHP_MINFO(pdo_pgsql), "1.0.2", STANDARD_MODULE_PROPERTIES @@ -106,24 +106,6 @@ PHP_MSHUTDOWN_FUNCTION(pdo_pgsql) } /* }}} */ -/* {{{ PHP_RINIT_FUNCTION - */ -PHP_RINIT_FUNCTION(pdo_pgsql) -{ - /* php_pdo_register_driver(&pdo_pgsql_driver); */ - return SUCCESS; -} -/* }}} */ - -/* {{{ PHP_MSHUTDOWN_FUNCTION - */ -PHP_RSHUTDOWN_FUNCTION(pdo_pgsql) -{ - /* php_pdo_unregister_driver(&pdo_pgsql_driver); */ - return SUCCESS; -} -/* }}} */ - /* {{{ PHP_MINFO_FUNCTION */ PHP_MINFO_FUNCTION(pdo_pgsql) @@ -134,7 +116,7 @@ PHP_MINFO_FUNCTION(pdo_pgsql) php_info_print_table_row(2, "PostgreSQL(libpq) Version", PG_VERSION); #endif php_info_print_table_row(2, "Module version", pdo_pgsql_module_entry.version); - php_info_print_table_row(2, "Revision", " $Id: pdo_pgsql.c 306939 2011-01-01 02:19:59Z felipe $ "); + php_info_print_table_row(2, "Revision", " $Id: pdo_pgsql.c 314376 2011-08-06 14:47:44Z felipe $ "); php_info_print_table_end(); } diff --git a/ext/pdo_pgsql/pgsql_driver.c b/ext/pdo_pgsql/pgsql_driver.c index e1b8895d8..44df82b27 100644 --- a/ext/pdo_pgsql/pgsql_driver.c +++ b/ext/pdo_pgsql/pgsql_driver.c @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: pgsql_driver.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: pgsql_driver.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -533,7 +533,7 @@ static PHP_METHOD(PDO, pgsqlCopyFromArray) spprintf(&query, 0, "COPY %s FROM STDIN DELIMITERS E'%c' WITH NULL AS E'%s'", table_name, (pg_delim_len ? *pg_delim : '\t'), (pg_null_as_len ? pg_null_as : "\\\\N")); } - // Obtain db Handle + /* Obtain db Handle */ H = (pdo_pgsql_db_handle *)dbh->driver_data; while ((pgsql_result = PQgetResult(H->server))) { @@ -625,7 +625,7 @@ static PHP_METHOD(PDO, pgsqlCopyFromFile) return; } - // Obtain db Handler + /* Obtain db Handler */ dbh = zend_object_store_get_object(getThis() TSRMLS_CC); PDO_CONSTRUCT_CHECK; @@ -975,7 +975,7 @@ static const zend_function_entry dbh_methods[] = { PHP_ME(PDO, pgsqlCopyFromFile, NULL, ZEND_ACC_PUBLIC) PHP_ME(PDO, pgsqlCopyToArray, NULL, ZEND_ACC_PUBLIC) PHP_ME(PDO, pgsqlCopyToFile, NULL, ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} + PHP_FE_END }; static const zend_function_entry *pdo_pgsql_get_driver_methods(pdo_dbh_t *dbh, int kind TSRMLS_DC) diff --git a/ext/pdo_pgsql/php_pdo_pgsql.h b/ext/pdo_pgsql/php_pdo_pgsql.h index 781a6f15d..ccc4fc9c6 100644 --- a/ext/pdo_pgsql/php_pdo_pgsql.h +++ b/ext/pdo_pgsql/php_pdo_pgsql.h @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: php_pdo_pgsql.h 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: php_pdo_pgsql.h 311631 2011-05-31 08:59:32Z iliaa $ */ #ifndef PHP_PDO_PGSQL_H #define PHP_PDO_PGSQL_H @@ -32,8 +32,6 @@ extern zend_module_entry pdo_pgsql_module_entry; PHP_MINIT_FUNCTION(pdo_pgsql); PHP_MSHUTDOWN_FUNCTION(pdo_pgsql); -PHP_RINIT_FUNCTION(pdo_pgsql); -PHP_RSHUTDOWN_FUNCTION(pdo_pgsql); PHP_MINFO_FUNCTION(pdo_pgsql); #endif /* PHP_PDO_PGSQL_H */ diff --git a/ext/pdo_pgsql/tests/is_in_transaction.phpt b/ext/pdo_pgsql/tests/is_in_transaction.phpt deleted file mode 100644 index 99ff56162..000000000 --- a/ext/pdo_pgsql/tests/is_in_transaction.phpt +++ /dev/null @@ -1,66 +0,0 @@ ---TEST-- -PDO PgSQL isInTransaction ---SKIPIF-- -<?php # vim:se ft=php: -if (!extension_loaded('pdo') || !extension_loaded('pdo_pgsql')) die('skip not loaded'); -require dirname(__FILE__) . '/config.inc'; -require dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc'; -PDOTest::skip(); -?> ---FILE-- -<?php -require dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc'; -$db = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt'); -$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); -$db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false); - -$db->exec('CREATE TABLE test (a integer not null primary key, b text)'); - -$db->beginTransaction(); -try { -echo "Test PDO::PGSQL_TRANSACTION_INTRANS\n"; -var_dump($db->inTransaction()); - -$stmt = $db->prepare("INSERT INTO test (a, b) values (?, ?)"); -$stmt->bindValue(1, 1); -$stmt->bindValue(2, "test insert"); -$stmt->execute(); - -$db->commit(); - -echo "Test PDO::PGSQL_TRANSACTION_IDLE\n"; -var_dump($db->inTransaction()); - -$db->beginTransaction(); - -try { -$stmt = $db->prepare("INSERT INTO test (a, b) values (?, ?)"); -$stmt->bindValue(1, "error"); -$stmt->bindValue(2, "test insert"); -$stmt->execute(); -} catch (Exception $e) { - /* We catch the exception because the execute will give error and we must test the PDO::PGSQL_TRANSACTION_ERROR */ - echo "Test PDO::PGSQL_TRANSACTION_INERROR\n"; - var_dump($db->inTransaction()); - $db->rollBack(); -} - -echo "Test PDO::PGSQL_TRANSACTION_IDLE\n"; -var_dump($db->inTransaction()); - -} catch (Exception $e) { - /* catch exceptions so that we can show the relative error */ - echo "Exception! at line ", $e->getLine(), "\n"; - var_dump($e->getMessage()); -} - -?> ---EXPECT-- -Test PDO::PGSQL_TRANSACTION_INTRANS -int(2) -Test PDO::PGSQL_TRANSACTION_IDLE -int(0) -Test PDO::PGSQL_TRANSACTION_INERROR -int(3) -Test PDO::PGSQL_TRANSACTION_IDLE -int(0) diff --git a/ext/pdo_sqlite/config.m4 b/ext/pdo_sqlite/config.m4 index eceb94d8b..2ddf8d8a4 100644 --- a/ext/pdo_sqlite/config.m4 +++ b/ext/pdo_sqlite/config.m4 @@ -1,4 +1,4 @@ -dnl $Id: config.m4 291414 2009-11-29 06:13:22Z rasmus $ +dnl $Id: config.m4 311041 2011-05-15 05:49:34Z rasmus $ dnl config.m4 for extension pdo_sqlite dnl vim:et:sw=2:ts=2: diff --git a/ext/pdo_sqlite/pdo_sqlite.c b/ext/pdo_sqlite/pdo_sqlite.c index d8275b71c..9ac167a8b 100644 --- a/ext/pdo_sqlite/pdo_sqlite.c +++ b/ext/pdo_sqlite/pdo_sqlite.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: pdo_sqlite.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: pdo_sqlite.c 314376 2011-08-06 14:47:44Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -35,7 +35,7 @@ /* {{{ pdo_sqlite_functions[] */ const zend_function_entry pdo_sqlite_functions[] = { - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ @@ -44,7 +44,7 @@ const zend_function_entry pdo_sqlite_functions[] = { #if ZEND_MODULE_API_NO >= 20050922 static const zend_module_dep pdo_sqlite_deps[] = { ZEND_MOD_REQUIRED("pdo") - {NULL, NULL, NULL} + ZEND_MOD_END }; #endif /* }}} */ diff --git a/ext/pdo_sqlite/sqlite_driver.c b/ext/pdo_sqlite/sqlite_driver.c index 8a731fc93..2eb78fc63 100644 --- a/ext/pdo_sqlite/sqlite_driver.c +++ b/ext/pdo_sqlite/sqlite_driver.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: sqlite_driver.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: sqlite_driver.c 314451 2011-08-08 00:07:54Z iliaa $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -47,33 +47,33 @@ int _pdo_sqlite_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *file, int li } einfo->errmsg = pestrdup((char*)sqlite3_errmsg(H->db), dbh->is_persistent); } else { /* no error */ - strcpy(*pdo_err, PDO_ERR_NONE); + strncpy(*pdo_err, PDO_ERR_NONE, sizeof(PDO_ERR_NONE)); return 0; } switch (einfo->errcode) { case SQLITE_NOTFOUND: - strcpy(*pdo_err, "42S02"); + strncpy(*pdo_err, "42S02", sizeof("42S02")); break; case SQLITE_INTERRUPT: - strcpy(*pdo_err, "01002"); + strncpy(*pdo_err, "01002", sizeof("01002")); break; case SQLITE_NOLFS: - strcpy(*pdo_err, "HYC00"); + strncpy(*pdo_err, "HYC00", sizeof("HYC00")); break; case SQLITE_TOOBIG: - strcpy(*pdo_err, "22001"); + strncpy(*pdo_err, "22001", sizeof("22001")); break; case SQLITE_CONSTRAINT: - strcpy(*pdo_err, "23000"); + strncpy(*pdo_err, "23000", sizeof("23000")); break; case SQLITE_ERROR: default: - strcpy(*pdo_err, "HY000"); + strncpy(*pdo_err, "HY000", sizeof("HY000")); break; } @@ -593,7 +593,7 @@ static PHP_METHOD(SQLite, sqliteCreateAggregate) static const zend_function_entry dbh_methods[] = { PHP_ME(SQLite, sqliteCreateFunction, NULL, ZEND_ACC_PUBLIC) PHP_ME(SQLite, sqliteCreateAggregate, NULL, ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} + PHP_FE_END }; static const zend_function_entry *get_driver_methods(pdo_dbh_t *dbh, int kind TSRMLS_DC) diff --git a/ext/pdo_sqlite/tests/pdo_sqlite_createaggregate_002.phpt b/ext/pdo_sqlite/tests/pdo_sqlite_createaggregate_002.phpt new file mode 100644 index 000000000..671e4b345 --- /dev/null +++ b/ext/pdo_sqlite/tests/pdo_sqlite_createaggregate_002.phpt @@ -0,0 +1,17 @@ +--TEST-- +PDO_sqlite: Testing invalid callback for sqliteCreateAggregate() +--SKIPIF-- +<?php if (!extension_loaded('pdo_sqlite')) print 'skip not loaded'; ?> +--FILE-- +<?php + +$pdo = new PDO('sqlite::memory:'); + +$pdo->sqliteCreateAggregate('foo', 'a', ''); +$pdo->sqliteCreateAggregate('foo', 'strlen', ''); + +?> +--EXPECTF-- +Warning: PDO::sqliteCreateAggregate(): function 'a' is not callable in %s on line %d + +Warning: PDO::sqliteCreateAggregate(): function '' is not callable in %s on line %d diff --git a/ext/pdo_sqlite/tests/pdo_sqlite_get_attribute.phpt b/ext/pdo_sqlite/tests/pdo_sqlite_get_attribute.phpt new file mode 100644 index 000000000..d6e095d54 --- /dev/null +++ b/ext/pdo_sqlite/tests/pdo_sqlite_get_attribute.phpt @@ -0,0 +1,15 @@ +--TEST-- +PDO_sqlite: Testing getAttribute() +--SKIPIF-- +<?php if (!extension_loaded('pdo_sqlite')) print 'skip not loaded'; ?> +--FILE-- +<?php + +$pdo = new PDO('sqlite::memory:'); +var_dump($pdo->getAttribute(PDO::ATTR_SERVER_VERSION)); +var_dump($pdo->getAttribute(PDO::ATTR_CLIENT_VERSION)); + +?> +--EXPECTF-- +string(%d) "%s" +string(%d) "%s" diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c index 1cbe98680..d6dea8e6f 100644 --- a/ext/pgsql/pgsql.c +++ b/ext/pgsql/pgsql.c @@ -20,7 +20,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: pgsql.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: pgsql.c 313665 2011-07-25 11:42:53Z felipe $ */ #include <stdlib.h> @@ -697,7 +697,7 @@ const zend_function_entry pgsql_functions[] = { PHP_FALIAS(pg_clientencoding, pg_client_encoding, arginfo_pg_client_encoding) PHP_FALIAS(pg_setclientencoding, pg_set_client_encoding, arginfo_pg_set_client_encoding) #endif - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ diff --git a/ext/phar/dirstream.c b/ext/phar/dirstream.c index f0fe1be45..4b6fd586f 100644 --- a/ext/phar/dirstream.c +++ b/ext/phar/dirstream.c @@ -503,7 +503,7 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, char *url_from, int mode, in return 0; } - if ((e = phar_get_entry_info_dir(phar, resource->path + 1, strlen(resource->path + 1), 0, &error, 1 TSRMLS_CC))) { + if (phar_get_entry_info_dir(phar, resource->path + 1, strlen(resource->path + 1), 0, &error, 1 TSRMLS_CC)) { /* entry exists as a file */ php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot create directory \"%s\" in phar \"%s\", file already exists", resource->path+1, resource->host); php_url_free(resource); diff --git a/ext/phar/phar.c b/ext/phar/phar.c index 2a69b59d6..46a8e34e1 100644 --- a/ext/phar/phar.c +++ b/ext/phar/phar.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: phar.c 307915 2011-02-01 14:01:00Z iliaa $ */ +/* $Id: phar.c 314419 2011-08-07 11:13:27Z laruence $ */ #define PHAR_MAIN 1 #include "phar_internal.h" @@ -667,7 +667,7 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char php_uint32 manifest_len, manifest_count, manifest_flags, manifest_index, tmp_len, sig_flags; php_uint16 manifest_ver; long offset; - int register_alias, sig_len, temp_alias = 0; + int sig_len, register_alias = 0, temp_alias = 0; char *signature = NULL; if (pphar) { @@ -3292,8 +3292,8 @@ ZEND_GET_MODULE(phar) * * Every user visible function must have an entry in phar_functions[]. */ -function_entry phar_functions[] = { - {NULL, NULL, NULL} /* Must be the last line in phar_functions[] */ +zend_function_entry phar_functions[] = { + PHP_FE_END }; /* }}}*/ @@ -3393,6 +3393,7 @@ static zend_op_array *phar_compile_file(zend_file_handle *file_handle, int type res = phar_orig_compile_file(file_handle, type TSRMLS_CC); } zend_catch { failed = 1; + res = NULL; } zend_end_try(); if (name) { @@ -3668,7 +3669,7 @@ PHP_MINFO_FUNCTION(phar) /* {{{ */ php_info_print_table_header(2, "Phar: PHP Archive support", "enabled"); php_info_print_table_row(2, "Phar EXT version", PHP_PHAR_VERSION); php_info_print_table_row(2, "Phar API version", PHP_PHAR_API_VERSION); - php_info_print_table_row(2, "SVN revision", "$Revision: 307915 $"); + php_info_print_table_row(2, "SVN revision", "$Revision: 314419 $"); php_info_print_table_row(2, "Phar-based phar archives", "enabled"); php_info_print_table_row(2, "Tar-based phar archives", "enabled"); php_info_print_table_row(2, "ZIP-based phar archives", "enabled"); @@ -3721,7 +3722,7 @@ static const zend_module_dep phar_deps[] = { #if HAVE_SPL ZEND_MOD_REQUIRED("spl") #endif - {NULL, NULL, NULL} + ZEND_MOD_END }; zend_module_entry phar_module_entry = { diff --git a/ext/phar/phar_object.c b/ext/phar/phar_object.c index e5ffe0887..f7dbc209c 100644 --- a/ext/phar/phar_object.c +++ b/ext/phar/phar_object.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: phar_object.c 309222 2011-03-14 14:12:42Z felipe $ */ +/* $Id: phar_object.c 314653 2011-08-09 14:11:56Z iliaa $ */ #include "phar_internal.h" #include "func_interceptors.h" @@ -628,7 +628,7 @@ carry_on: } return; - } else if (SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), fname, fname_len, (void **)&pphar)) { + } else if (PHAR_GLOBALS->phar_fname_map.arBuckets && SUCCESS == zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), fname, fname_len, (void **)&pphar)) { goto carry_on; } else if (PHAR_G(manifest_cached) && SUCCESS == zend_hash_find(&cached_phars, fname, fname_len, (void **)&pphar)) { if (SUCCESS == phar_copy_on_write(pphar TSRMLS_CC)) { @@ -661,7 +661,7 @@ PHP_METHOD(Phar, webPhar) char *fname, *basename, *path_info, *mime_type = NULL, *entry, *pt; int fname_len, entry_len, code, index_php_len = 0, not_cgi; phar_archive_data *phar = NULL; - phar_entry_info *info; + phar_entry_info *info = NULL; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!s!saz", &alias, &alias_len, &index_php, &index_php_len, &f404, &f404_len, &mimeoverride, &rewrite) == FAILURE) { return; @@ -888,7 +888,7 @@ PHP_METHOD(Phar, webPhar) zend_bailout(); } else { - char *tmp, sa; + char *tmp = NULL, sa = '\0'; sapi_header_line ctr = {0}; ctr.response_code = 301; ctr.line_len = sizeof("HTTP/1.1 301 Moved Permanently")+1; @@ -1073,6 +1073,9 @@ PHP_METHOD(Phar, mungServer) */ PHP_METHOD(Phar, interceptFileFuncs) { + if (zend_parse_parameters_none() == FAILURE) { + return; + } phar_intercept_functions(TSRMLS_C); } /* }}} */ @@ -1150,6 +1153,9 @@ PHP_METHOD(Phar, loadPhar) * Returns the api version */ PHP_METHOD(Phar, apiVersion) { + if (zend_parse_parameters_none() == FAILURE) { + return; + } RETURN_STRINGL(PHP_PHAR_API_VERSION, sizeof(PHP_PHAR_API_VERSION)-1, 1); } /* }}}*/ @@ -1192,6 +1198,9 @@ PHP_METHOD(Phar, canCompress) * Returns whether phar extension supports writing and creating phars */ PHP_METHOD(Phar, canWrite) { + if (zend_parse_parameters_none() == FAILURE) { + return; + } RETURN_BOOL(!PHAR_G(readonly)); } /* }}} */ @@ -1399,6 +1408,10 @@ PHP_METHOD(Phar, __construct) */ PHP_METHOD(Phar, getSupportedSignatures) { + if (zend_parse_parameters_none() == FAILURE) { + return; + } + array_init(return_value); add_next_index_stringl(return_value, "MD5", 3, 1); @@ -1422,6 +1435,10 @@ PHP_METHOD(Phar, getSupportedSignatures) */ PHP_METHOD(Phar, getSupportedCompression) { + if (zend_parse_parameters_none() == FAILURE) { + return; + } + array_init(return_value); phar_request_initialize(TSRMLS_C); @@ -1647,11 +1664,14 @@ static int phar_build(zend_object_iterator *iter, void *puser TSRMLS_DC) /* {{{ } test = expand_filepath(fname, NULL TSRMLS_CC); + efree(fname); if (test) { - efree(fname); fname = test; fname_len = strlen(fname); + } else { + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Could not resolve file path"); + return ZEND_HASH_APPLY_STOP; } save = fname; @@ -1677,6 +1697,11 @@ static int phar_build(zend_object_iterator *iter, void *puser TSRMLS_DC) /* {{{ #else fname = expand_filepath(intern->file_name, NULL TSRMLS_CC); #endif + if (!fname) { + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Could not resolve file path"); + return ZEND_HASH_APPLY_STOP; + } + fname_len = strlen(fname); save = fname; goto phar_spl_fileinfo; @@ -1694,6 +1719,14 @@ static int phar_build(zend_object_iterator *iter, void *puser TSRMLS_DC) /* {{{ phar_spl_fileinfo: if (base_len) { temp = expand_filepath(base, NULL TSRMLS_CC); + if (!temp) { + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Could not resolve file path"); + if (save) { + efree(save); + } + return ZEND_HASH_APPLY_STOP; + } + base = temp; base_len = strlen(base); @@ -2085,6 +2118,10 @@ PHP_METHOD(Phar, buildFromIterator) PHP_METHOD(Phar, count) { PHAR_ARCHIVE_OBJECT(); + + if (zend_parse_parameters_none() == FAILURE) { + return; + } RETURN_LONG(zend_hash_num_elements(&phar_obj->arc.archive->manifest)); } @@ -2707,6 +2744,10 @@ PHP_METHOD(Phar, convertToData) PHP_METHOD(Phar, isCompressed) { PHAR_ARCHIVE_OBJECT(); + + if (zend_parse_parameters_none() == FAILURE) { + return; + } if (phar_obj->arc.archive->flags & PHAR_FILE_COMPRESSED_GZ) { RETURN_LONG(PHAR_ENT_COMPRESSED_GZ); @@ -2727,6 +2768,10 @@ PHP_METHOD(Phar, isWritable) { php_stream_statbuf ssb; PHAR_ARCHIVE_OBJECT(); + + if (zend_parse_parameters_none() == FAILURE) { + return; + } if (!phar_obj->arc.archive->is_writeable) { RETURN_FALSE; @@ -2801,6 +2846,10 @@ PHP_METHOD(Phar, delete) PHP_METHOD(Phar, getAlias) { PHAR_ARCHIVE_OBJECT(); + + if (zend_parse_parameters_none() == FAILURE) { + return; + } if (phar_obj->arc.archive->alias && phar_obj->arc.archive->alias != phar_obj->arc.archive->fname) { RETURN_STRINGL(phar_obj->arc.archive->alias, phar_obj->arc.archive->alias_len, 1); @@ -2814,6 +2863,10 @@ PHP_METHOD(Phar, getAlias) PHP_METHOD(Phar, getPath) { PHAR_ARCHIVE_OBJECT(); + + if (zend_parse_parameters_none() == FAILURE) { + return; + } RETURN_STRINGL(phar_obj->arc.archive->fname, phar_obj->arc.archive->fname_len, 1); } @@ -2926,6 +2979,10 @@ valid_alias: PHP_METHOD(Phar, getVersion) { PHAR_ARCHIVE_OBJECT(); + + if (zend_parse_parameters_none() == FAILURE) { + return; + } RETURN_STRING(phar_obj->arc.archive->version, 1); } @@ -2937,6 +2994,10 @@ PHP_METHOD(Phar, getVersion) PHP_METHOD(Phar, startBuffering) { PHAR_ARCHIVE_OBJECT(); + + if (zend_parse_parameters_none() == FAILURE) { + return; + } phar_obj->arc.archive->donotflush = 1; } @@ -2948,6 +3009,10 @@ PHP_METHOD(Phar, startBuffering) PHP_METHOD(Phar, isBuffering) { PHAR_ARCHIVE_OBJECT(); + + if (zend_parse_parameters_none() == FAILURE) { + return; + } RETURN_BOOL(phar_obj->arc.archive->donotflush); } @@ -2961,6 +3026,10 @@ PHP_METHOD(Phar, stopBuffering) char *error; PHAR_ARCHIVE_OBJECT(); + + if (zend_parse_parameters_none() == FAILURE) { + return; + } if (PHAR_G(readonly) && !phar_obj->arc.archive->is_data) { zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, @@ -3192,6 +3261,10 @@ PHP_METHOD(Phar, setSignatureAlgorithm) PHP_METHOD(Phar, getSignature) { PHAR_ARCHIVE_OBJECT(); + + if (zend_parse_parameters_none() == FAILURE) { + return; + } if (phar_obj->arc.archive->signature) { char *unknown; @@ -3232,6 +3305,10 @@ PHP_METHOD(Phar, getSignature) PHP_METHOD(Phar, getModified) { PHAR_ARCHIVE_OBJECT(); + + if (zend_parse_parameters_none() == FAILURE) { + return; + } RETURN_BOOL(phar_obj->arc.archive->is_modified); } @@ -3490,6 +3567,10 @@ PHP_METHOD(Phar, decompressFiles) { char *error; PHAR_ARCHIVE_OBJECT(); + + if (zend_parse_parameters_none() == FAILURE) { + return; + } if (PHAR_G(readonly) && !phar_obj->arc.archive->is_data) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, @@ -4007,6 +4088,10 @@ PHP_METHOD(Phar, getStub) phar_entry_info *stub; PHAR_ARCHIVE_OBJECT(); + + if (zend_parse_parameters_none() == FAILURE) { + return; + } if (phar_obj->arc.archive->is_tar || phar_obj->arc.archive->is_zip) { @@ -4014,7 +4099,10 @@ PHP_METHOD(Phar, getStub) if (phar_obj->arc.archive->fp && !phar_obj->arc.archive->is_brandnew && !(stub->flags & PHAR_ENT_COMPRESSION_MASK)) { fp = phar_obj->arc.archive->fp; } else { - fp = php_stream_open_wrapper(phar_obj->arc.archive->fname, "rb", 0, NULL); + if (!(fp = php_stream_open_wrapper(phar_obj->arc.archive->fname, "rb", 0, NULL))) { + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "phar error: unable to open phar \"%s\"", phar_obj->arc.archive->fname); + return; + } if (stub->flags & PHAR_ENT_COMPRESSION_MASK) { char *filter_name; @@ -4103,6 +4191,10 @@ PHP_METHOD(Phar, hasMetadata) PHP_METHOD(Phar, getMetadata) { PHAR_ARCHIVE_OBJECT(); + + if (zend_parse_parameters_none() == FAILURE) { + return; + } if (phar_obj->arc.archive->metadata) { if (phar_obj->arc.archive->is_persistent) { @@ -4594,6 +4686,10 @@ PHP_METHOD(PharFileInfo, __destruct) PHP_METHOD(PharFileInfo, getCompressedSize) { PHAR_ENTRY_OBJECT(); + + if (zend_parse_parameters_none() == FAILURE) { + return; + } RETURN_LONG(entry_obj->ent.entry->compressed_filesize); } @@ -4632,6 +4728,10 @@ PHP_METHOD(PharFileInfo, isCompressed) PHP_METHOD(PharFileInfo, getCRC32) { PHAR_ENTRY_OBJECT(); + + if (zend_parse_parameters_none() == FAILURE) { + return; + } if (entry_obj->ent.entry->is_dir) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, \ @@ -4654,6 +4754,10 @@ PHP_METHOD(PharFileInfo, getCRC32) PHP_METHOD(PharFileInfo, isCRCChecked) { PHAR_ENTRY_OBJECT(); + + if (zend_parse_parameters_none() == FAILURE) { + return; + } RETURN_BOOL(entry_obj->ent.entry->is_crc_checked); } @@ -4665,6 +4769,10 @@ PHP_METHOD(PharFileInfo, isCRCChecked) PHP_METHOD(PharFileInfo, getPharFlags) { PHAR_ENTRY_OBJECT(); + + if (zend_parse_parameters_none() == FAILURE) { + return; + } RETURN_LONG(entry_obj->ent.entry->flags & ~(PHAR_ENT_PERM_MASK|PHAR_ENT_COMPRESSION_MASK)); } @@ -4739,6 +4847,10 @@ PHP_METHOD(PharFileInfo, chmod) PHP_METHOD(PharFileInfo, hasMetadata) { PHAR_ENTRY_OBJECT(); + + if (zend_parse_parameters_none() == FAILURE) { + return; + } RETURN_BOOL(entry_obj->ent.entry->metadata != NULL); } @@ -4750,6 +4862,10 @@ PHP_METHOD(PharFileInfo, hasMetadata) PHP_METHOD(PharFileInfo, getMetadata) { PHAR_ENTRY_OBJECT(); + + if (zend_parse_parameters_none() == FAILURE) { + return; + } if (entry_obj->ent.entry->metadata) { if (entry_obj->ent.entry->is_persistent) { @@ -4827,6 +4943,10 @@ PHP_METHOD(PharFileInfo, delMetadata) char *error; PHAR_ENTRY_OBJECT(); + + if (zend_parse_parameters_none() == FAILURE) { + return; + } if (PHAR_G(readonly) && !entry_obj->ent.entry->phar->is_data) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "Write operations disabled by the php.ini setting phar.readonly"); @@ -4881,6 +5001,10 @@ PHP_METHOD(PharFileInfo, getContent) phar_entry_info *link; PHAR_ENTRY_OBJECT(); + + if (zend_parse_parameters_none() == FAILURE) { + return; + } if (entry_obj->ent.entry->is_dir) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, @@ -5055,6 +5179,10 @@ PHP_METHOD(PharFileInfo, decompress) { char *error; PHAR_ENTRY_OBJECT(); + + if (zend_parse_parameters_none() == FAILURE) { + return; + } if (entry_obj->ent.entry->is_dir) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, \ @@ -5140,6 +5268,17 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_createDS, 0, 0, 0) ZEND_END_ARG_INFO() PHAR_ARG_INFO +ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_cancompress, 0, 0, 0) + ZEND_ARG_INFO(0, method) +ZEND_END_ARG_INFO() + +PHAR_ARG_INFO +ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_isvalidpharfilename, 0, 0, 1) + ZEND_ARG_INFO(0, filename) + ZEND_ARG_INFO(0, executable) +ZEND_END_ARG_INFO() + +PHAR_ARG_INFO ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_loadPhar, 0, 0, 1) ZEND_ARG_INFO(0, filename) ZEND_ARG_INFO(0, alias) @@ -5295,6 +5434,11 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_isff, 0, 0, 1) ZEND_ARG_INFO(0, fileformat) ZEND_END_ARG_INFO() +PHAR_ARG_INFO +ZEND_BEGIN_ARG_INFO(arginfo_phar__void, 0) +ZEND_END_ARG_INFO() + + #endif /* HAVE_SPL */ zend_function_entry php_archive_methods[] = { @@ -5302,35 +5446,35 @@ zend_function_entry php_archive_methods[] = { PHP_ME(Phar, __construct, arginfo_phar___construct, ZEND_ACC_PRIVATE) #else PHP_ME(Phar, __construct, arginfo_phar___construct, ZEND_ACC_PUBLIC) - PHP_ME(Phar, __destruct, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Phar, __destruct, arginfo_phar__void, ZEND_ACC_PUBLIC) PHP_ME(Phar, addEmptyDir, arginfo_phar_emptydir, ZEND_ACC_PUBLIC) PHP_ME(Phar, addFile, arginfo_phar_addfile, ZEND_ACC_PUBLIC) PHP_ME(Phar, addFromString, arginfo_phar_fromstring, ZEND_ACC_PUBLIC) PHP_ME(Phar, buildFromDirectory, arginfo_phar_fromdir, ZEND_ACC_PUBLIC) PHP_ME(Phar, buildFromIterator, arginfo_phar_build, ZEND_ACC_PUBLIC) PHP_ME(Phar, compressFiles, arginfo_phar_comp, ZEND_ACC_PUBLIC) - PHP_ME(Phar, decompressFiles, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Phar, decompressFiles, arginfo_phar__void, ZEND_ACC_PUBLIC) PHP_ME(Phar, compress, arginfo_phar_comps, ZEND_ACC_PUBLIC) PHP_ME(Phar, decompress, arginfo_phar_decomp, ZEND_ACC_PUBLIC) PHP_ME(Phar, convertToExecutable, arginfo_phar_conv, ZEND_ACC_PUBLIC) PHP_ME(Phar, convertToData, arginfo_phar_conv, ZEND_ACC_PUBLIC) PHP_ME(Phar, copy, arginfo_phar_copy, ZEND_ACC_PUBLIC) - PHP_ME(Phar, count, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Phar, count, arginfo_phar__void, ZEND_ACC_PUBLIC) PHP_ME(Phar, delete, arginfo_phar_delete, ZEND_ACC_PUBLIC) - PHP_ME(Phar, delMetadata, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Phar, delMetadata, arginfo_phar__void, ZEND_ACC_PUBLIC) PHP_ME(Phar, extractTo, arginfo_phar_extract, ZEND_ACC_PUBLIC) - PHP_ME(Phar, getAlias, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Phar, getPath, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Phar, getMetadata, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Phar, getModified, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Phar, getSignature, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Phar, getStub, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Phar, getVersion, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Phar, hasMetadata, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Phar, isBuffering, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Phar, isCompressed, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Phar, getAlias, arginfo_phar__void, ZEND_ACC_PUBLIC) + PHP_ME(Phar, getPath, arginfo_phar__void, ZEND_ACC_PUBLIC) + PHP_ME(Phar, getMetadata, arginfo_phar__void, ZEND_ACC_PUBLIC) + PHP_ME(Phar, getModified, arginfo_phar__void, ZEND_ACC_PUBLIC) + PHP_ME(Phar, getSignature, arginfo_phar__void, ZEND_ACC_PUBLIC) + PHP_ME(Phar, getStub, arginfo_phar__void, ZEND_ACC_PUBLIC) + PHP_ME(Phar, getVersion, arginfo_phar__void, ZEND_ACC_PUBLIC) + PHP_ME(Phar, hasMetadata, arginfo_phar__void, ZEND_ACC_PUBLIC) + PHP_ME(Phar, isBuffering, arginfo_phar__void, ZEND_ACC_PUBLIC) + PHP_ME(Phar, isCompressed, arginfo_phar__void, ZEND_ACC_PUBLIC) PHP_ME(Phar, isFileFormat, arginfo_phar_isff, ZEND_ACC_PUBLIC) - PHP_ME(Phar, isWritable, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Phar, isWritable, arginfo_phar__void, ZEND_ACC_PUBLIC) PHP_ME(Phar, offsetExists, arginfo_phar_offsetExists, ZEND_ACC_PUBLIC) PHP_ME(Phar, offsetGet, arginfo_phar_offsetExists, ZEND_ACC_PUBLIC) PHP_ME(Phar, offsetSet, arginfo_phar_offsetSet, ZEND_ACC_PUBLIC) @@ -5340,18 +5484,18 @@ zend_function_entry php_archive_methods[] = { PHP_ME(Phar, setMetadata, arginfo_phar_setMetadata, ZEND_ACC_PUBLIC) PHP_ME(Phar, setSignatureAlgorithm, arginfo_phar_setSigAlgo, ZEND_ACC_PUBLIC) PHP_ME(Phar, setStub, arginfo_phar_setStub, ZEND_ACC_PUBLIC) - PHP_ME(Phar, startBuffering, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Phar, stopBuffering, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Phar, startBuffering, arginfo_phar__void, ZEND_ACC_PUBLIC) + PHP_ME(Phar, stopBuffering, arginfo_phar__void, ZEND_ACC_PUBLIC) #endif /* static member functions */ - PHP_ME(Phar, apiVersion, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL) - PHP_ME(Phar, canCompress, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL) - PHP_ME(Phar, canWrite, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL) + PHP_ME(Phar, apiVersion, arginfo_phar__void, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL) + PHP_ME(Phar, canCompress, arginfo_phar_cancompress, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL) + PHP_ME(Phar, canWrite, arginfo_phar__void, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL) PHP_ME(Phar, createDefaultStub, arginfo_phar_createDS, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL) - PHP_ME(Phar, getSupportedCompression,NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL) - PHP_ME(Phar, getSupportedSignatures,NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL) - PHP_ME(Phar, interceptFileFuncs, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL) - PHP_ME(Phar, isValidPharFilename, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL) + PHP_ME(Phar, getSupportedCompression,arginfo_phar__void, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL) + PHP_ME(Phar, getSupportedSignatures,arginfo_phar__void, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL) + PHP_ME(Phar, interceptFileFuncs, arginfo_phar__void, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL) + PHP_ME(Phar, isValidPharFilename, arginfo_phar_isvalidpharfilename, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL) PHP_ME(Phar, loadPhar, arginfo_phar_loadPhar, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL) PHP_ME(Phar, mapPhar, arginfo_phar_mapPhar, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL) PHP_ME(Phar, running, arginfo_phar_running, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL) @@ -5359,7 +5503,7 @@ zend_function_entry php_archive_methods[] = { PHP_ME(Phar, mungServer, arginfo_phar_mungServer, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL) PHP_ME(Phar, unlinkArchive, arginfo_phar_ua, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL) PHP_ME(Phar, webPhar, arginfo_phar_webPhar, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL) - {NULL, NULL, NULL} + PHP_FE_END }; #if HAVE_SPL @@ -5375,26 +5519,26 @@ ZEND_END_ARG_INFO() zend_function_entry php_entry_methods[] = { PHP_ME(PharFileInfo, __construct, arginfo_entry___construct, ZEND_ACC_PUBLIC) - PHP_ME(PharFileInfo, __destruct, NULL, ZEND_ACC_PUBLIC) + PHP_ME(PharFileInfo, __destruct, arginfo_phar__void, ZEND_ACC_PUBLIC) PHP_ME(PharFileInfo, chmod, arginfo_entry_chmod, ZEND_ACC_PUBLIC) PHP_ME(PharFileInfo, compress, arginfo_phar_comp, ZEND_ACC_PUBLIC) - PHP_ME(PharFileInfo, decompress, NULL, ZEND_ACC_PUBLIC) - PHP_ME(PharFileInfo, delMetadata, NULL, ZEND_ACC_PUBLIC) - PHP_ME(PharFileInfo, getCompressedSize, NULL, ZEND_ACC_PUBLIC) - PHP_ME(PharFileInfo, getCRC32, NULL, ZEND_ACC_PUBLIC) - PHP_ME(PharFileInfo, getContent, NULL, ZEND_ACC_PUBLIC) - PHP_ME(PharFileInfo, getMetadata, NULL, ZEND_ACC_PUBLIC) - PHP_ME(PharFileInfo, getPharFlags, NULL, ZEND_ACC_PUBLIC) - PHP_ME(PharFileInfo, hasMetadata, NULL, ZEND_ACC_PUBLIC) + PHP_ME(PharFileInfo, decompress, arginfo_phar__void, ZEND_ACC_PUBLIC) + PHP_ME(PharFileInfo, delMetadata, arginfo_phar__void, ZEND_ACC_PUBLIC) + PHP_ME(PharFileInfo, getCompressedSize, arginfo_phar__void, ZEND_ACC_PUBLIC) + PHP_ME(PharFileInfo, getCRC32, arginfo_phar__void, ZEND_ACC_PUBLIC) + PHP_ME(PharFileInfo, getContent, arginfo_phar__void, ZEND_ACC_PUBLIC) + PHP_ME(PharFileInfo, getMetadata, arginfo_phar__void, ZEND_ACC_PUBLIC) + PHP_ME(PharFileInfo, getPharFlags, arginfo_phar__void, ZEND_ACC_PUBLIC) + PHP_ME(PharFileInfo, hasMetadata, arginfo_phar__void, ZEND_ACC_PUBLIC) PHP_ME(PharFileInfo, isCompressed, arginfo_phar_compo, ZEND_ACC_PUBLIC) - PHP_ME(PharFileInfo, isCRCChecked, NULL, ZEND_ACC_PUBLIC) + PHP_ME(PharFileInfo, isCRCChecked, arginfo_phar__void, ZEND_ACC_PUBLIC) PHP_ME(PharFileInfo, setMetadata, arginfo_phar_setMetadata, ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} + PHP_FE_END }; #endif /* HAVE_SPL */ zend_function_entry phar_exception_methods[] = { - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ diff --git a/ext/phar/stream.c b/ext/phar/stream.c index f9bf3f3e0..cf2c53620 100644 --- a/ext/phar/stream.c +++ b/ext/phar/stream.c @@ -421,6 +421,8 @@ static int phar_stream_seek(php_stream *stream, off_t offset, int whence, off_t case SEEK_SET : temp = data->zero + offset; break; + default : + temp = 0; } if (temp > data->zero + (off_t) entry->uncompressed_filesize) { *newoffset = -1; diff --git a/ext/phar/tests/bug52013.phpt b/ext/phar/tests/bug52013.phpt index f4635a466..5ee37e7d4 100644 --- a/ext/phar/tests/bug52013.phpt +++ b/ext/phar/tests/bug52013.phpt @@ -6,7 +6,10 @@ http://bugs.php.net/bug.php?id=52013 --CREDITS-- Frederic Hardy frederic.hardy@mageekbox.net --SKIPIF-- -<?php if (!extension_loaded("phar")) die("skip"); ?> +<?php +if (!extension_loaded("phar")) die("skip"); +if (!extension_loaded("zlib")) die("skip test needs zlib extension enabled to compress archives with gzip"); +?> --INI-- phar.require_hash=0 phar.readonly=0 diff --git a/ext/phar/tests/bug54395.phpt b/ext/phar/tests/bug54395.phpt new file mode 100644 index 000000000..091ed326a --- /dev/null +++ b/ext/phar/tests/bug54395.phpt @@ -0,0 +1,16 @@ +--TEST-- +Bug #54395 (Phar::mount() crashes when calling with wrong parameters) +--SKIPIF-- +<?php if (!extension_loaded("phar")) die("skip"); ?> +--FILE-- +<?php + +try { + phar::mount(1,1); +} catch (Exception $e) { + var_dump($e->getMessage()); +} + +?> +--EXPECTF-- +string(25) "Mounting of 1 to 1 failed" diff --git a/ext/phar/util.c b/ext/phar/util.c index 9bc704d89..983ca38d1 100644 --- a/ext/phar/util.c +++ b/ext/phar/util.c @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: util.c 306941 2011-01-01 02:48:19Z felipe $ */ +/* $Id: util.c 314419 2011-08-07 11:13:27Z laruence $ */ #include "phar_internal.h" #ifdef PHAR_HASH_OK @@ -154,6 +154,9 @@ int phar_seek_efp(phar_entry_info *entry, off_t offset, int whence, off_t positi case SEEK_SET: temp = eoffset + offset; break; + default: + temp = 0; + break; } if (temp > eoffset + (off_t) entry->uncompressed_filesize) { @@ -1203,7 +1206,7 @@ int phar_get_archive(phar_archive_data **archive, char *fname, int fname_len, ch phar_archive_data *fd, **fd_ptr; char *my_realpath, *save; int save_len; - ulong fhash, ahash; + ulong fhash, ahash = 0; phar_request_initialize(TSRMLS_C); diff --git a/ext/phar/zip.c b/ext/phar/zip.c index fa3642f5c..a8ac63aeb 100644 --- a/ext/phar/zip.c +++ b/ext/phar/zip.c @@ -411,6 +411,9 @@ foundit: now = php_stream_tell(fp); pefree(entry.filename, entry.is_persistent); sigfile = php_stream_fopen_tmpfile(); + if (!sigfile) { + PHAR_ZIP_FAIL("couldn't open temporary file"); + } php_stream_seek(fp, 0, SEEK_SET); /* copy file contents + local headers and zip comment, if any, to be hashed for signature */ diff --git a/ext/posix/posix.c b/ext/posix/posix.c index b04ef9ab4..18aa87fda 100644 --- a/ext/posix/posix.c +++ b/ext/posix/posix.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: posix.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: posix.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -301,7 +301,7 @@ const zend_function_entry posix_functions[] = { PHP_FE(posix_initgroups, arginfo_posix_initgroups) #endif - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ @@ -310,7 +310,7 @@ const zend_function_entry posix_functions[] = { static PHP_MINFO_FUNCTION(posix) { php_info_print_table_start(); - php_info_print_table_row(2, "Revision", "$Revision: 306939 $"); + php_info_print_table_row(2, "Revision", "$Revision: 313665 $"); php_info_print_table_end(); } /* }}} */ diff --git a/ext/pspell/pspell.c b/ext/pspell/pspell.c index c1757c40e..ff01c6679 100644 --- a/ext/pspell/pspell.c +++ b/ext/pspell/pspell.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: pspell.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: pspell.c 313665 2011-07-25 11:42:53Z felipe $ */ #define IS_EXT_MODULE @@ -198,7 +198,7 @@ static const zend_function_entry pspell_functions[] = { PHP_FE(pspell_config_data_dir, arginfo_pspell_config_data_dir) PHP_FE(pspell_config_repl, arginfo_pspell_config_repl) PHP_FE(pspell_config_save_repl, arginfo_pspell_config_save_repl) - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ diff --git a/ext/readline/readline.c b/ext/readline/readline.c index ab9cfe70a..1d2341a3e 100644 --- a/ext/readline/readline.c +++ b/ext/readline/readline.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: readline.c 307343 2011-01-10 18:19:02Z iliaa $ */ +/* $Id: readline.c 313831 2011-07-28 10:42:45Z pajoye $ */ /* {{{ includes & prototypes */ @@ -143,7 +143,7 @@ static const zend_function_entry php_readline_functions[] = { PHP_FE(readline_redisplay, arginfo_readline_redisplay) PHP_FE(readline_on_new_line, arginfo_readline_on_new_line) #endif - {NULL, NULL, NULL} + PHP_FE_END }; zend_module_entry readline_module_entry = { @@ -465,6 +465,9 @@ static char **_readline_completion_cb(const char *text, int start, int end) matches = rl_completion_matches(text,_readline_command_generator); } else { matches = malloc(sizeof(char *) * 2); + if (!matches) { + return NULL; + } matches[0] = strdup(""); matches[1] = '\0'; } @@ -505,7 +508,10 @@ PHP_FUNCTION(readline_completion_function) zval_copy_ctor(_readline_completion); rl_attempted_completion_function = _readline_completion_cb; - + if (rl_attempted_completion_function == NULL) { + efree(name); + RETURN_FALSE; + } RETURN_TRUE; } diff --git a/ext/recode/recode.c b/ext/recode/recode.c index 971477794..6eaba2a43 100644 --- a/ext/recode/recode.c +++ b/ext/recode/recode.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: recode.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: recode.c 313665 2011-07-25 11:42:53Z felipe $ */ /* {{{ includes & prototypes */ @@ -84,7 +84,7 @@ static const zend_function_entry php_recode_functions[] = { PHP_FE(recode_string, arginfo_recode_string) PHP_FE(recode_file, arginfo_recode_file) PHP_FALIAS(recode, recode_string, arginfo_recode_string) - {NULL, NULL, NULL} + PHP_FE_END }; zend_module_entry recode_module_entry = { @@ -135,7 +135,7 @@ PHP_MINFO_FUNCTION(recode) { php_info_print_table_start(); php_info_print_table_row(2, "Recode Support", "enabled"); - php_info_print_table_row(2, "Revision", "$Revision: 306939 $"); + php_info_print_table_row(2, "Revision", "$Revision: 313665 $"); php_info_print_table_end(); } diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 8c0474179..a4d8cb71d 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -20,7 +20,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: php_reflection.c 307971 2011-02-03 12:45:30Z cataphract $ */ +/* $Id: php_reflection.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -1085,13 +1085,18 @@ static void _extension_string(string *str, zend_module_entry *module, char *inde /* Is there a better way of doing this? */ while (func->fname) { - if (zend_hash_find(EG(function_table), func->fname, strlen(func->fname) + 1, (void**) &fptr) == FAILURE) { + int fname_len = strlen(func->fname); + char *lc_name = zend_str_tolower_dup(func->fname, fname_len); + + if (zend_hash_find(EG(function_table), lc_name, fname_len+1, (void**) &fptr) == FAILURE) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Internal error: Cannot find extension function %s in global function table", func->fname); func++; + efree(lc_name); continue; } _function_string(str, fptr, NULL, " " TSRMLS_CC); + efree(lc_name); func++; } string_printf(str, "%s }\n", indent); @@ -4849,16 +4854,21 @@ ZEND_METHOD(reflection_extension, getFunctions) /* Is there a better way of doing this? */ while (func->fname) { - if (zend_hash_find(EG(function_table), func->fname, strlen(func->fname) + 1, (void**) &fptr) == FAILURE) { + int fname_len = strlen(func->fname); + char *lc_name = zend_str_tolower_dup(func->fname, fname_len); + + if (zend_hash_find(EG(function_table), lc_name, fname_len + 1, (void**) &fptr) == FAILURE) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Internal error: Cannot find extension function %s in global function table", func->fname); func++; + efree(lc_name); continue; } ALLOC_ZVAL(function); reflection_function_factory(fptr, NULL, function TSRMLS_CC); - add_assoc_zval_ex(return_value, func->fname, strlen(func->fname)+1, function); + add_assoc_zval_ex(return_value, func->fname, fname_len+1, function); func++; + efree(lc_name); } } } @@ -5057,7 +5067,7 @@ ZEND_METHOD(reflection_extension, info) /* {{{ method tables */ static const zend_function_entry reflection_exception_functions[] = { - {NULL, NULL, NULL} + PHP_FE_END }; ZEND_BEGIN_ARG_INFO(arginfo_reflection__void, 0) @@ -5076,13 +5086,13 @@ ZEND_END_ARG_INFO() static const zend_function_entry reflection_functions[] = { ZEND_ME(reflection, getModifierNames, arginfo_reflection_getModifierNames, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) ZEND_ME(reflection, export, arginfo_reflection_export, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) - {NULL, NULL, NULL} + PHP_FE_END }; static const zend_function_entry reflector_functions[] = { ZEND_FENTRY(export, NULL, NULL, ZEND_ACC_STATIC|ZEND_ACC_ABSTRACT|ZEND_ACC_PUBLIC) ZEND_ABSTRACT_ME(reflector, __toString, arginfo_reflection__void) - {NULL, NULL, NULL} + PHP_FE_END }; ZEND_BEGIN_ARG_INFO_EX(arginfo_reflection_function_export, 0, 0, 1) @@ -5124,7 +5134,7 @@ static const zend_function_entry reflection_function_abstract_functions[] = { ZEND_ME(reflection_function, getStartLine, arginfo_reflection__void, 0) ZEND_ME(reflection_function, getStaticVariables, arginfo_reflection__void, 0) ZEND_ME(reflection_function, returnsReference, arginfo_reflection__void, 0) - {NULL, NULL, NULL} + PHP_FE_END }; static const zend_function_entry reflection_function_functions[] = { @@ -5134,7 +5144,7 @@ static const zend_function_entry reflection_function_functions[] = { ZEND_ME(reflection_function, isDisabled, arginfo_reflection__void, 0) ZEND_ME(reflection_function, invoke, arginfo_reflection_function_invoke, 0) ZEND_ME(reflection_function, invokeArgs, arginfo_reflection_function_invokeArgs, 0) - {NULL, NULL, NULL} + PHP_FE_END }; ZEND_BEGIN_ARG_INFO_EX(arginfo_reflection_method_export, 0, 0, 2) @@ -5180,7 +5190,7 @@ static const zend_function_entry reflection_method_functions[] = { ZEND_ME(reflection_method, getDeclaringClass, arginfo_reflection__void, 0) ZEND_ME(reflection_method, getPrototype, arginfo_reflection__void, 0) ZEND_ME(reflection_property, setAccessible, arginfo_reflection_method_setAccessible, 0) - {NULL, NULL, NULL} + PHP_FE_END }; @@ -5300,7 +5310,7 @@ static const zend_function_entry reflection_class_functions[] = { ZEND_ME(reflection_class, inNamespace, arginfo_reflection__void, 0) ZEND_ME(reflection_class, getNamespaceName, arginfo_reflection__void, 0) ZEND_ME(reflection_class, getShortName, arginfo_reflection__void, 0) - {NULL, NULL, NULL} + PHP_FE_END }; @@ -5316,7 +5326,7 @@ ZEND_END_ARG_INFO() static const zend_function_entry reflection_object_functions[] = { ZEND_ME(reflection_object, export, arginfo_reflection_object_export, ZEND_ACC_STATIC|ZEND_ACC_PUBLIC) ZEND_ME(reflection_object, __construct, arginfo_reflection_object___construct, 0) - {NULL, NULL, NULL} + PHP_FE_END }; @@ -5361,7 +5371,7 @@ static const zend_function_entry reflection_property_functions[] = { ZEND_ME(reflection_property, getDeclaringClass, arginfo_reflection__void, 0) ZEND_ME(reflection_property, getDocComment, arginfo_reflection__void, 0) ZEND_ME(reflection_property, setAccessible, arginfo_reflection_property_setAccessible, 0) - {NULL, NULL, NULL} + PHP_FE_END }; ZEND_BEGIN_ARG_INFO_EX(arginfo_reflection_parameter_export, 0, 0, 2) @@ -5391,7 +5401,7 @@ static const zend_function_entry reflection_parameter_functions[] = { ZEND_ME(reflection_parameter, isOptional, arginfo_reflection__void, 0) ZEND_ME(reflection_parameter, isDefaultValueAvailable, arginfo_reflection__void, 0) ZEND_ME(reflection_parameter, getDefaultValue, arginfo_reflection__void, 0) - {NULL, NULL, NULL} + PHP_FE_END }; ZEND_BEGIN_ARG_INFO_EX(arginfo_reflection_extension_export, 0, 0, 1) @@ -5417,12 +5427,12 @@ static const zend_function_entry reflection_extension_functions[] = { ZEND_ME(reflection_extension, getClassNames, arginfo_reflection__void, 0) ZEND_ME(reflection_extension, getDependencies, arginfo_reflection__void, 0) ZEND_ME(reflection_extension, info, arginfo_reflection__void, 0) - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ const zend_function_entry reflection_ext_functions[] = { /* {{{ */ - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ static zend_object_handlers *zend_std_obj_handlers; @@ -5535,7 +5545,7 @@ PHP_MINFO_FUNCTION(reflection) /* {{{ */ php_info_print_table_start(); php_info_print_table_header(2, "Reflection", "enabled"); - php_info_print_table_row(2, "Version", "$Revision: 307971 $"); + php_info_print_table_row(2, "Version", "$Revision: 313665 $"); php_info_print_table_end(); } /* }}} */ @@ -5549,7 +5559,7 @@ zend_module_entry reflection_module_entry = { /* {{{ */ NULL, NULL, PHP_MINFO(reflection), - "$Revision: 307971 $", + "$Revision: 313665 $", STANDARD_MODULE_PROPERTIES }; /* }}} */ diff --git a/ext/session/config.w32 b/ext/session/config.w32 index 379538ce5..5c65651d9 100644 --- a/ext/session/config.w32 +++ b/ext/session/config.w32 @@ -1,4 +1,4 @@ -// $Id: config.w32 306344 2010-12-13 18:43:10Z pajoye $ +// $Id: config.w32 312200 2011-06-16 01:21:01Z pajoye $ // vim:ft=javascript ARG_ENABLE("session", "session support", "yes"); @@ -6,5 +6,5 @@ ARG_ENABLE("session", "session support", "yes"); if (PHP_SESSION == "yes") { EXTENSION("session", "session.c mod_files.c mod_mm.c mod_user.c", false /* never shared */); AC_DEFINE("HAVE_PHP_SESSION", 1, "Session support"); - PHP_INSTALL_HEADERS("ext/session/", "mod_mm.h"); + PHP_INSTALL_HEADERS("ext/session/", "mod_mm.h php_session.h mod_files.h mod_user.h"); } diff --git a/ext/session/session.c b/ext/session/session.c index b7a2451b9..432048dc7 100644 --- a/ext/session/session.c +++ b/ext/session/session.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: session.c 307671 2011-01-23 10:02:06Z pajoye $ */ +/* $Id: session.c 314376 2011-08-06 14:47:44Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -2119,7 +2119,7 @@ static const zend_function_entry session_functions[] = { PHP_FE(session_get_cookie_params, arginfo_session_void) PHP_FE(session_write_close, arginfo_session_void) PHP_FALIAS(session_commit, session_write_close, arginfo_session_void) - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ @@ -2283,7 +2283,7 @@ static PHP_MINFO_FUNCTION(session) /* {{{ */ static const zend_module_dep session_deps[] = { /* {{{ */ ZEND_MOD_OPTIONAL("hash") ZEND_MOD_REQUIRED("spl") - {NULL, NULL, NULL} + ZEND_MOD_END }; /* }}} */ diff --git a/ext/session/tests/008-php4.2.3.phpt b/ext/session/tests/008-php4.2.3.phpt index 1b26900c8..9464ecded 100644 --- a/ext/session/tests/008-php4.2.3.phpt +++ b/ext/session/tests/008-php4.2.3.phpt @@ -15,7 +15,6 @@ track_errors=1 log_errors=0 html_errors=0 display_errors=1 -error_reporting=2039; session.serialize_handler=php session.save_handler=files precision=14 @@ -59,12 +58,18 @@ session_destroy(); ?> --EXPECTF-- Deprecated: Directive 'register_long_arrays' is deprecated in PHP 5.3 and greater in Unknown on line 0 + +Deprecated: Function session_register() is deprecated in %s on line %d + +Notice: Undefined variable: c in %s on line %d NULL session_write_close(): Your script possibly relies on a session side-effect which existed until PHP 4.2.3. Please be advised that the session extension does not consider global variables as a source of data, unless register_globals is enabled. You can disable this functionality and this warning by setting session.bug_compat_42 or session.bug_compat_warn to off, respectively array(1) { ["c"]=> float(3.14) } + +Notice: Undefined variable: c in %s on line %d NULL array(1) { ["c"]=> diff --git a/ext/session/tests/bug32330.phpt b/ext/session/tests/bug32330.phpt index c0d3370e0..98d442ae5 100644 --- a/ext/session/tests/bug32330.phpt +++ b/ext/session/tests/bug32330.phpt @@ -6,7 +6,7 @@ Bug #32330 (session_destroy, "Failed to initialize storage module", custom sessi session.use_trans_sid=0 session.use_cookies=1 session.name=sid -session.save_path=/ +session.save_path=/tmp session.gc_probability=1 session.gc_divisor=1 --FILE-- @@ -68,17 +68,17 @@ $_SESSION['E'] = 'F'; ?> --EXPECTF-- -open: path = /, name = sid +open: path = /tmp, name = sid read: id = %s gc: maxlifetime = %d write: id = %s, data = A|s:1:"B"; close -open: path = /, name = sid +open: path = /tmp, name = sid read: id = %s gc: maxlifetime = %d destroy: id = %s close -open: path = /, name = sid +open: path = /tmp, name = sid read: id = %s gc: maxlifetime = %d write: id = %s, data = E|s:1:"F"; diff --git a/ext/session/tests/session_encode_basic.phpt b/ext/session/tests/session_encode_basic.phpt index f74bddd9c..b087f7441 100644 --- a/ext/session/tests/session_encode_basic.phpt +++ b/ext/session/tests/session_encode_basic.phpt @@ -120,7 +120,7 @@ string(13) "data|d:-10.5;" string(20) "data|d:123456789000;" -- Iteration 8 -- -string(86) "data|d:1.2345678899999999145113427164344339914681114578343112953007221221923828125E-9;" +string(29) "data|d:1.2345678899999999E-9;" -- Iteration 9 -- string(11) "data|d:0.5;" diff --git a/ext/session/tests/session_encode_error2.phpt b/ext/session/tests/session_encode_error2.phpt index 24758500c..2c31ceb51 100644 --- a/ext/session/tests/session_encode_error2.phpt +++ b/ext/session/tests/session_encode_error2.phpt @@ -220,7 +220,7 @@ bool(true) -- Iteration 20 -- bool(true) -string(33) "Hello World!|s:12:"Hello World!";" +bool(false) bool(true) -- Iteration 21 -- diff --git a/ext/session/tests/skipif.inc b/ext/session/tests/skipif.inc index 6e3eae086..e63f963c5 100644 --- a/ext/session/tests/skipif.inc +++ b/ext/session/tests/skipif.inc @@ -18,7 +18,7 @@ if ($save_path) { $save_path = substr($save_path, ++$p); } if (!@is_writable($save_path)) { - die("skip\n"); + die("skip session.save_path $save_path is not writable\n"); } } } diff --git a/ext/shmop/shmop.c b/ext/shmop/shmop.c index c85676c88..b176ae501 100644 --- a/ext/shmop/shmop.c +++ b/ext/shmop/shmop.c @@ -16,7 +16,7 @@ | Ilia Alshanetsky <ilia@prohost.org> | +----------------------------------------------------------------------+ */ -/* $Id: shmop.c 309032 2011-03-08 18:24:04Z felipe $ */ +/* $Id: shmop.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -87,7 +87,7 @@ const zend_function_entry shmop_functions[] = { PHP_FE(shmop_size, arginfo_shmop_size) PHP_FE(shmop_write, arginfo_shmop_write) PHP_FE(shmop_delete, arginfo_shmop_delete) - {NULL, NULL, NULL} /* Must be the last line in shmop_functions[] */ + PHP_FE_END }; /* }}} */ diff --git a/ext/simplexml/simplexml.c b/ext/simplexml/simplexml.c index 75eb742c0..888a6c0fd 100644 --- a/ext/simplexml/simplexml.c +++ b/ext/simplexml/simplexml.c @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: simplexml.c 308262 2011-02-11 21:10:48Z felipe $ */ +/* $Id: simplexml.c 314376 2011-08-06 14:47:44Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -1264,9 +1264,8 @@ SXE_METHOD(xpath) result = retval->nodesetval; - array_init(return_value); - if (result != NULL) { + array_init(return_value); for (i = 0; i < result->nodeNr; ++i) { nodeptr = result->nodeTab[i]; if (nodeptr->type == XML_TEXT_NODE || nodeptr->type == XML_ELEMENT_NODE || nodeptr->type == XML_ATTRIBUTE_NODE) { @@ -1287,6 +1286,8 @@ SXE_METHOD(xpath) add_next_index_zval(return_value, value); } } + } else { + RETVAL_FALSE; } xmlXPathFreeObject(retval); @@ -2515,14 +2516,14 @@ const zend_function_entry simplexml_functions[] = { /* {{{ */ PHP_FE(simplexml_load_file, arginfo_simplexml_load_file) PHP_FE(simplexml_load_string, arginfo_simplexml_load_string) PHP_FE(simplexml_import_dom, arginfo_simplexml_import_dom) - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ static const zend_module_dep simplexml_deps[] = { /* {{{ */ ZEND_MOD_REQUIRED("libxml") ZEND_MOD_REQUIRED("spl") - {NULL, NULL, NULL} + ZEND_MOD_END }; /* }}} */ @@ -2562,7 +2563,7 @@ static const zend_function_entry sxe_functions[] = { /* {{{ */ SXE_ME(addAttribute, arginfo_simplexmlelement_addchild, ZEND_ACC_PUBLIC) SXE_ME(__toString, arginfo_simplexmlelement__void, ZEND_ACC_PUBLIC) SXE_ME(count, arginfo_simplexmlelement__void, ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ @@ -2608,7 +2609,7 @@ PHP_MINFO_FUNCTION(simplexml) { php_info_print_table_start(); php_info_print_table_header(2, "Simplexml support", "enabled"); - php_info_print_table_row(2, "Revision", "$Revision: 308262 $"); + php_info_print_table_row(2, "Revision", "$Revision: 314376 $"); php_info_print_table_row(2, "Schema support", #ifdef LIBXML_SCHEMAS_ENABLED "enabled"); diff --git a/ext/simplexml/sxe.c b/ext/simplexml/sxe.c index 8bc5bcb33..815bea1db 100755 --- a/ext/simplexml/sxe.c +++ b/ext/simplexml/sxe.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: sxe.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: sxe.c 312258 2011-06-18 15:56:14Z felipe $ */ #ifdef HAVE_CONFIG_H # include "config.h" @@ -41,6 +41,10 @@ zend_class_entry *ce_SimpleXMLElement; PHP_METHOD(ce_SimpleXMLIterator, rewind) { php_sxe_iterator iter; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } iter.sxe = php_sxe_fetch_object(getThis() TSRMLS_CC); ce_SimpleXMLElement->iterator_funcs.funcs->rewind((zend_object_iterator*)&iter TSRMLS_CC); @@ -52,6 +56,10 @@ PHP_METHOD(ce_SimpleXMLIterator, rewind) PHP_METHOD(ce_SimpleXMLIterator, valid) { php_sxe_object *sxe = php_sxe_fetch_object(getThis() TSRMLS_CC); + + if (zend_parse_parameters_none() == FAILURE) { + return; + } RETURN_BOOL(sxe->iter.data); } @@ -62,6 +70,10 @@ PHP_METHOD(ce_SimpleXMLIterator, valid) PHP_METHOD(ce_SimpleXMLIterator, current) { php_sxe_object *sxe = php_sxe_fetch_object(getThis() TSRMLS_CC); + + if (zend_parse_parameters_none() == FAILURE) { + return; + } if (!sxe->iter.data) { return; /* return NULL */ @@ -78,6 +90,10 @@ PHP_METHOD(ce_SimpleXMLIterator, key) xmlNodePtr curnode; php_sxe_object *intern; php_sxe_object *sxe = php_sxe_fetch_object(getThis() TSRMLS_CC); + + if (zend_parse_parameters_none() == FAILURE) { + return; + } if (!sxe->iter.data) { RETURN_FALSE; @@ -98,6 +114,10 @@ PHP_METHOD(ce_SimpleXMLIterator, key) PHP_METHOD(ce_SimpleXMLIterator, next) { php_sxe_iterator iter; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } iter.sxe = php_sxe_fetch_object(getThis() TSRMLS_CC); ce_SimpleXMLElement->iterator_funcs.funcs->move_forward((zend_object_iterator*)&iter TSRMLS_CC); @@ -111,6 +131,10 @@ PHP_METHOD(ce_SimpleXMLIterator, hasChildren) php_sxe_object *sxe = php_sxe_fetch_object(getThis() TSRMLS_CC); php_sxe_object *child; xmlNodePtr node; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } if (!sxe->iter.data || sxe->iter.type == SXE_ITER_ATTRLIST) { RETURN_FALSE; @@ -133,6 +157,10 @@ PHP_METHOD(ce_SimpleXMLIterator, hasChildren) PHP_METHOD(ce_SimpleXMLIterator, getChildren) { php_sxe_object *sxe = php_sxe_fetch_object(getThis() TSRMLS_CC); + + if (zend_parse_parameters_none() == FAILURE) { + return; + } if (!sxe->iter.data || sxe->iter.type == SXE_ITER_ATTRLIST) { return; /* return NULL */ @@ -140,14 +168,19 @@ PHP_METHOD(ce_SimpleXMLIterator, getChildren) RETURN_ZVAL(sxe->iter.data, 1, 0); } +/* {{{ arginfo */ +ZEND_BEGIN_ARG_INFO(arginfo_simplexmliterator__void, 0) +ZEND_END_ARG_INFO() +/* }}} */ + static const zend_function_entry funcs_SimpleXMLIterator[] = { - PHP_ME(ce_SimpleXMLIterator, rewind, NULL, ZEND_ACC_PUBLIC) - PHP_ME(ce_SimpleXMLIterator, valid, NULL, ZEND_ACC_PUBLIC) - PHP_ME(ce_SimpleXMLIterator, current, NULL, ZEND_ACC_PUBLIC) - PHP_ME(ce_SimpleXMLIterator, key, NULL, ZEND_ACC_PUBLIC) - PHP_ME(ce_SimpleXMLIterator, next, NULL, ZEND_ACC_PUBLIC) - PHP_ME(ce_SimpleXMLIterator, hasChildren, NULL, ZEND_ACC_PUBLIC) - PHP_ME(ce_SimpleXMLIterator, getChildren, NULL, ZEND_ACC_PUBLIC) + PHP_ME(ce_SimpleXMLIterator, rewind, arginfo_simplexmliterator__void, ZEND_ACC_PUBLIC) + PHP_ME(ce_SimpleXMLIterator, valid, arginfo_simplexmliterator__void, ZEND_ACC_PUBLIC) + PHP_ME(ce_SimpleXMLIterator, current, arginfo_simplexmliterator__void, ZEND_ACC_PUBLIC) + PHP_ME(ce_SimpleXMLIterator, key, arginfo_simplexmliterator__void, ZEND_ACC_PUBLIC) + PHP_ME(ce_SimpleXMLIterator, next, arginfo_simplexmliterator__void, ZEND_ACC_PUBLIC) + PHP_ME(ce_SimpleXMLIterator, hasChildren, arginfo_simplexmliterator__void, ZEND_ACC_PUBLIC) + PHP_ME(ce_SimpleXMLIterator, getChildren, arginfo_simplexmliterator__void, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; /* }}} */ diff --git a/ext/skeleton/skeleton.c b/ext/skeleton/skeleton.c index 42685757d..920354a06 100644 --- a/ext/skeleton/skeleton.c +++ b/ext/skeleton/skeleton.c @@ -23,7 +23,7 @@ static int le_extname; const zend_function_entry extname_functions[] = { PHP_FE(confirm_extname_compiled, NULL) /* For testing, remove later. */ /* __function_entries_here__ */ - {NULL, NULL, NULL} /* Must be the last line in extname_functions[] */ + PHP_FE_END /* Must be the last line in extname_functions[] */ }; /* }}} */ diff --git a/ext/snmp/snmp.c b/ext/snmp/snmp.c index 861f2c296..2c2a1138f 100644 --- a/ext/snmp/snmp.c +++ b/ext/snmp/snmp.c @@ -20,7 +20,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: snmp.c 307876 2011-01-31 11:34:12Z lytboris $ */ +/* $Id: snmp.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -331,7 +331,7 @@ const zend_function_entry snmp_functions[] = { PHP_FE(snmp_get_valueretrieval, arginfo_snmp_get_valueretrieval) PHP_FE(snmp_read_mib, arginfo_snmp_read_mib) - {NULL,NULL,NULL} + PHP_FE_END }; /* }}} */ @@ -1160,7 +1160,7 @@ PHP_FUNCTION(snmp2_set) /* {{{ proto void php_snmpv3(INTERNAL_FUNCTION_PARAMETERS, int st) * * Generic SNMPv3 object fetcher -* From here is passed on the the common internal object fetcher. +* From here is passed on the common internal object fetcher. * * st=SNMP_CMD_GET snmp3_get() - query an agent and return a single value. * st=SNMP_CMD_GETNEXT snmp3_getnext() - query an agent and return the next single value. diff --git a/ext/soap/php_encoding.c b/ext/soap/php_encoding.c index 16981fa17..b6f0fda32 100644 --- a/ext/soap/php_encoding.c +++ b/ext/soap/php_encoding.c @@ -17,7 +17,7 @@ | Dmitry Stogov <dmitry@zend.com> | +----------------------------------------------------------------------+ */ -/* $Id: php_encoding.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: php_encoding.c 314737 2011-08-10 13:44:48Z dmitry $ */ #include <time.h> @@ -114,6 +114,26 @@ static void set_ns_and_type(xmlNodePtr node, encodeTypePtr type); } \ } +#define CHECK_XML_NULL(xml) \ + { \ + xmlAttrPtr null; \ + if (!xml) { \ + zval *ret; \ + ALLOC_INIT_ZVAL(ret); \ + ZVAL_NULL(ret); \ + return ret; \ + } \ + if (xml->properties) { \ + null = get_attribute(xml->properties, "nil"); \ + if (null) { \ + zval *ret; \ + ALLOC_INIT_ZVAL(ret); \ + ZVAL_NULL(ret); \ + return ret; \ + } \ + } \ + } + #define FIND_ZVAL_NULL(zval, xml, style) \ { \ if (!zval || Z_TYPE_P(zval) == IS_NULL) { \ @@ -338,6 +358,19 @@ static zend_bool soap_check_zval_ref(zval *data, xmlNodePtr node TSRMLS_DC) { return 0; } +static zval* soap_find_xml_ref(xmlNodePtr node TSRMLS_DC) +{ + zval **data_ptr; + + if (SOAP_GLOBAL(ref_map) && + zend_hash_index_find(SOAP_GLOBAL(ref_map), (ulong)node, (void**)&data_ptr) == SUCCESS) { + Z_SET_ISREF_PP(data_ptr); + Z_ADDREF_PP(data_ptr); + return *data_ptr; + } + return NULL; +} + static zend_bool soap_check_xml_ref(zval **data, xmlNodePtr node TSRMLS_DC) { zval **data_ptr; @@ -1513,6 +1546,11 @@ static zval *to_zval_object_ex(encodeTypePtr type, xmlNodePtr data, zend_class_e sdlType->encode->details.sdl_type->kind != XSD_TYPEKIND_LIST && sdlType->encode->details.sdl_type->kind != XSD_TYPEKIND_UNION) { + CHECK_XML_NULL(data); + if ((ret = soap_find_xml_ref(data TSRMLS_CC)) != NULL) { + return ret; + } + if (ce != ZEND_STANDARD_CLASS_DEF_PTR && sdlType->encode->to_zval == sdl_guess_convert_zval && sdlType->encode->details.sdl_type != NULL && @@ -1526,7 +1564,6 @@ static zval *to_zval_object_ex(encodeTypePtr type, xmlNodePtr data, zend_class_e } else { ret = master_to_zval_int(sdlType->encode, data); } - FIND_XML_NULL(data, ret); if (soap_check_xml_ref(&ret, data TSRMLS_CC)) { return ret; } @@ -2984,6 +3021,9 @@ static xmlNodePtr to_xml_datetime_ex(encodeTypePtr type, zval *data, char *forma timestamp = Z_LVAL_P(data); ta = php_localtime_r(×tamp, &tmbuf); /*ta = php_gmtime_r(×tamp, &tmbuf);*/ + if (!ta) { + soap_error1(E_ERROR, "Encoding: Invalid timestamp %ld", Z_LVAL_P(data)); + } buf = (char *) emalloc(buf_len); while ((real_len = strftime(buf, buf_len, format, ta)) == buf_len || real_len == 0) { diff --git a/ext/soap/soap.c b/ext/soap/soap.c index 6975e2b98..d1660dc04 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -17,7 +17,7 @@ | Dmitry Stogov <dmitry@zend.com> | +----------------------------------------------------------------------+ */ -/* $Id: soap.c 307975 2011-02-03 13:33:10Z iliaa $ */ +/* $Id: soap.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -279,6 +279,9 @@ PHP_METHOD(SoapHeader, SoapHeader); /* {{{ arginfo */ #ifdef ZEND_ENGINE_2 +ZEND_BEGIN_ARG_INFO(arginfo_soap__void, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(arginfo_soapparam_soapparam, 0, 0, 2) ZEND_ARG_INFO(0, data) ZEND_ARG_INFO(0, name) @@ -457,15 +460,15 @@ unsigned char arginfo_soapclient___soapcall[] = { 5, BYREF_NONE, BYREF_NONE, BYR static const zend_function_entry soap_functions[] = { PHP_FE(use_soap_error_handler, arginfo_soap_use_soap_error_handler) PHP_FE(is_soap_fault, arginfo_soap_is_soap_fault) - {NULL, NULL, NULL} + PHP_FE_END }; static const zend_function_entry soap_fault_functions[] = { SOAP_CTOR(SoapFault, SoapFault, arginfo_soapfault_soapfault, 0) #ifdef ZEND_ENGINE_2 - PHP_ME(SoapFault, __toString, NULL, 0) + PHP_ME(SoapFault, __toString, arginfo_soap__void, 0) #endif - {NULL, NULL, NULL} + PHP_FE_END }; static const zend_function_entry soap_server_functions[] = { @@ -478,7 +481,7 @@ static const zend_function_entry soap_server_functions[] = { PHP_ME(SoapServer, handle, arginfo_soapserver_handle, 0) PHP_ME(SoapServer, fault, arginfo_soapserver_fault, 0) PHP_ME(SoapServer, addSoapHeader, arginfo_soapserver_addsoapheader, 0) - {NULL, NULL, NULL} + PHP_FE_END }; static const zend_function_entry soap_client_functions[] = { @@ -495,22 +498,22 @@ static const zend_function_entry soap_client_functions[] = { PHP_ME(SoapClient, __setCookie, arginfo_soapclient___setcookie, 0) PHP_ME(SoapClient, __setLocation, arginfo_soapclient___setlocation, 0) PHP_ME(SoapClient, __setSoapHeaders, arginfo_soapclient___setsoapheaders, 0) - {NULL, NULL, NULL} + PHP_FE_END }; static const zend_function_entry soap_var_functions[] = { SOAP_CTOR(SoapVar, SoapVar, arginfo_soapvar_soapvar, 0) - {NULL, NULL, NULL} + PHP_FE_END }; static const zend_function_entry soap_param_functions[] = { SOAP_CTOR(SoapParam, SoapParam, arginfo_soapparam_soapparam, 0) - {NULL, NULL, NULL} + PHP_FE_END }; static const zend_function_entry soap_header_functions[] = { SOAP_CTOR(SoapHeader, SoapHeader, arginfo_soapheader_soapheader, 0) - {NULL, NULL, NULL} + PHP_FE_END }; zend_module_entry soap_module_entry = { @@ -1213,9 +1216,11 @@ PHP_METHOD(SoapServer, SoapServer) zval **tmp; if (zend_hash_find(ht, "soap_version", sizeof("soap_version"), (void**)&tmp) == SUCCESS) { - if (Z_TYPE_PP(tmp) == IS_LONG || - (Z_LVAL_PP(tmp) == SOAP_1_1 && Z_LVAL_PP(tmp) == SOAP_1_2)) { + if (Z_TYPE_PP(tmp) == IS_LONG && + (Z_LVAL_PP(tmp) == SOAP_1_1 || Z_LVAL_PP(tmp) == SOAP_1_2)) { version = Z_LVAL_PP(tmp); + } else { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "'soap_version' option must be SOAP_1_1 or SOAP_1_2"); } } @@ -2282,7 +2287,7 @@ static void soap_error_handler(int error_num, const char *error_filename, const _old_http_response_code = SG(sapi_headers).http_response_code; _old_http_status_line = SG(sapi_headers).http_status_line; - if (!SOAP_GLOBAL(use_soap_error_handler)) { + if (!SOAP_GLOBAL(use_soap_error_handler) || !EG(objects_store).object_buckets) { call_old_error_handler(error_num, error_filename, error_lineno, format, args); return; } diff --git a/ext/soap/tests/bugs/bug55323.phpt b/ext/soap/tests/bugs/bug55323.phpt new file mode 100644 index 000000000..7855dd845 --- /dev/null +++ b/ext/soap/tests/bugs/bug55323.phpt @@ -0,0 +1,45 @@ +--TEST-- +Bug #55323 (SoapClient segmentation fault when XSD_TYPEKIND_EXTENSION contains itself) +--SKIPIF-- +<?php require_once('skipif.inc'); ?> +--FILE-- +<?php +ini_set("soap.wsdl_cache_enabled",0); +$timestamp = "2011-07-30T03:25:00-05:00"; +$wsdl = dirname(__FILE__)."/bug55323.wsdl"; + +class TestSoapClient extends SoapClient { + + function __construct($wsdl, $options) { + parent::__construct($wsdl, $options); + } + + function __doRequest($request, $location, $action, $version, $one_way = 0) { + return <<<EOF +<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://test.com/soap/v3/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"> + <SOAP-ENV:Body> + <ns1:getObjectResponse> + <getObjectReturn xsi:type="ns1:Customer" id="ref1"> + <accountId xsi:type="xsd:int">1234</accountId> + <parent href="#ref1"/> + </getObjectReturn> + </ns1:getObjectResponse> + </SOAP-ENV:Body> +</SOAP-ENV:Envelope> +EOF; + } + +} + +$soapClient = new TestSoapClient($wsdl, + array('trace' => 1, 'exceptions' => 0)); +$result = $soapClient->getObject(); +var_dump($result); +?> +--EXPECTF-- +object(stdClass)#%d (2) { + ["accountId"]=> + int(1234) + ["parent"]=> + *RECURSION* +} diff --git a/ext/soap/tests/bugs/bug55323.wsdl b/ext/soap/tests/bugs/bug55323.wsdl new file mode 100644 index 000000000..c260d34f4 --- /dev/null +++ b/ext/soap/tests/bugs/bug55323.wsdl @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="UTF-8"?> +<definitions xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://test.com/soap/v3/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap-enc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" name="slApi" targetNamespace="http://test.com/soap/v3/"> + <types> + <xsd:schema xmlns="http://www.w3.org/2001/XMLSchema" + targetNamespace="http://test.com/soap/v3/" + xmlns:tns="http://test.com/soap/v3/" + xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" + xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" + elementFormDefault="qualified"> + <complexType name="Customer"> + <complexContent> + <extension base="tns:Interface"> + <sequence> + <element minOccurs="0" maxOccurs="1" nillable="true" name="accountId" type="int"/> + <element minOccurs="0" maxOccurs="1" name="parent" type="tns:Customer"/> + </sequence> + </extension> + </complexContent> + </complexType> + <complexType name="Interface" abstract="true"/> + </xsd:schema> + </types> + <message name="getObject"/> + <message name="getObjectResponse"> + <part name="getObjectReturn" type="tns:Customer"/> + </message> + <portType name="CustomerPortType"> + <operation name="getObject"> + <input message="tns:getObject"/> + <output message="tns:getObjectResponse"/> + </operation> + </portType> + <binding name="CustomerBinding" type="tns:CustomerPortType"> + <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/> + <operation name="getObject"> + <soap:operation soapAction="http://test.com/soap/v3/CustomerAction"/> + <input> + <soap:body namespace="http://test.com/soap/v3/" use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/> + </input> + <output> + <soap:body namespace="http://test.com/soap/v3/" use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/> + </output> + </operation> + </binding> + <service name="CustomerService"> + <port name="CustomerPort" binding="tns:CustomerBinding"> + <soap:address location="test://"/> + </port> + </service> +</definitions> diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index 9e06b2871..dab8e3840 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -19,7 +19,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: sockets.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: sockets.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -300,7 +300,7 @@ const zend_function_entry sockets_functions[] = { PHP_FALIAS(socket_getopt, socket_get_option, arginfo_socket_get_option) PHP_FALIAS(socket_setopt, socket_set_option, arginfo_socket_set_option) - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ @@ -394,16 +394,13 @@ static int php_open_listen_sock(php_socket **php_sock, int port, int backlog TSR } /* }}} */ -static int php_accept_connect(php_socket *in_sock, php_socket **new_sock, struct sockaddr *la TSRMLS_DC) /* {{{ */ +static int php_accept_connect(php_socket *in_sock, php_socket **new_sock, struct sockaddr *la, socklen_t *la_len TSRMLS_DC) /* {{{ */ { - socklen_t salen; php_socket *out_sock = (php_socket*)emalloc(sizeof(php_socket)); *new_sock = out_sock; - salen = sizeof(*la); - out_sock->blocking = 1; - out_sock->bsd_socket = accept(in_sock->bsd_socket, la, &salen); + out_sock->bsd_socket = accept(in_sock->bsd_socket, la, la_len); if (IS_INVALID_SOCKET(out_sock)) { PHP_SOCKET_ERROR(out_sock, "unable to accept incoming connection", errno); @@ -411,6 +408,10 @@ static int php_accept_connect(php_socket *in_sock, php_socket **new_sock, struct return 0; } + out_sock->error = 0; + out_sock->blocking = 1; + out_sock->type = la->sa_family; + return 1; } /* }}} */ @@ -880,9 +881,10 @@ PHP_FUNCTION(socket_create_listen) Accepts a connection on the listening socket fd */ PHP_FUNCTION(socket_accept) { - zval *arg1; - php_socket *php_sock, *new_sock; - struct sockaddr_in sa; + zval *arg1; + php_socket *php_sock, *new_sock; + php_sockaddr_storage sa; + socklen_t sa_len = sizeof(sa); if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &arg1) == FAILURE) { return; @@ -890,13 +892,10 @@ PHP_FUNCTION(socket_accept) ZEND_FETCH_RESOURCE(php_sock, php_socket *, &arg1, -1, le_socket_name, le_socket); - if (!php_accept_connect(php_sock, &new_sock, (struct sockaddr *) &sa TSRMLS_CC)) { + if (!php_accept_connect(php_sock, &new_sock, (struct sockaddr*)&sa, &sa_len TSRMLS_CC)) { RETURN_FALSE; } - new_sock->error = 0; - new_sock->blocking = 1; - ZEND_REGISTER_RESOURCE(return_value, new_sock, le_socket); } /* }}} */ @@ -917,8 +916,10 @@ PHP_FUNCTION(socket_set_nonblock) if (php_set_sock_blocking(php_sock->bsd_socket, 0 TSRMLS_CC) == SUCCESS) { php_sock->blocking = 0; RETURN_TRUE; + } else { + PHP_SOCKET_ERROR(php_sock, "unable to set nonblocking mode", errno); + RETURN_FALSE; } - RETURN_FALSE; } /* }}} */ @@ -938,8 +939,10 @@ PHP_FUNCTION(socket_set_block) if (php_set_sock_blocking(php_sock->bsd_socket, 1 TSRMLS_CC) == SUCCESS) { php_sock->blocking = 1; RETURN_TRUE; + } else { + PHP_SOCKET_ERROR(php_sock, "unable to set blocking mode", errno); + RETURN_FALSE; } - RETURN_FALSE; } /* }}} */ @@ -1333,6 +1336,11 @@ PHP_FUNCTION(socket_connect) break; case AF_UNIX: + if (addr_len >= sizeof(s_un.sun_path)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Path too long"); + RETURN_FALSE; + } + memset(&s_un, 0, sizeof(struct sockaddr_un)); s_un.sun_family = AF_UNIX; diff --git a/ext/sockets/tests/bug51958.phpt b/ext/sockets/tests/bug51958.phpt new file mode 100644 index 000000000..afccd6ce2 --- /dev/null +++ b/ext/sockets/tests/bug51958.phpt @@ -0,0 +1,22 @@ +--TEST--
+Bug #51958: socket_accept() fails on IPv6 server sockets
+--SKIPIF--
+<?php
+if (!extension_loaded('sockets')) {
+ die('skip sockets extension not available.');
+}
+if (!defined('IPPROTO_IPV6')) {
+ die('skip IPv6 not available.');
+}
+if (PHP_OS != "WINNT")
+ die('skip test relies Winsock\'s error code for WSAEWOULDBLOCK/EAGAIN');
+--FILE--
+<?php
+$listenfd = socket_create(AF_INET6, SOCK_STREAM, SOL_TCP);
+socket_bind($listenfd, "::1", 13579);
+socket_listen($listenfd);
+socket_set_nonblock($listenfd);
+$connfd = @socket_accept($listenfd);
+echo socket_last_error();
+--EXPECT--
+10035
diff --git a/ext/sockets/tests/socket_strerror.phpt b/ext/sockets/tests/socket_strerror.phpt index d1759c582..52e7a0a48 100644 --- a/ext/sockets/tests/socket_strerror.phpt +++ b/ext/sockets/tests/socket_strerror.phpt @@ -154,4 +154,4 @@ string(20) "Key has been revoked" string(27) "Key was rejected by service" string(10) "Owner died" string(21) "State not recoverable" -string(17) "Unknown error 132" +string(%d) "%s" diff --git a/ext/spl/internal/appenditerator.inc b/ext/spl/internal/appenditerator.inc index 5db080425..28e32b15f 100755 --- a/ext/spl/internal/appenditerator.inc +++ b/ext/spl/internal/appenditerator.inc @@ -31,7 +31,7 @@ class AppendIterator implements OuterIterator * @param $it Iterator to append * * If the current state is invalid but the appended iterator is valid - * the the AppendIterator itself becomes valid. However there will be no + * the AppendIterator itself becomes valid. However there will be no * call to $it->rewind(). Also if the current state is invalid the inner * ArrayIterator will be rewound und forwarded to the appended element. */ @@ -119,4 +119,4 @@ class AppendIterator implements OuterIterator } } -?>
\ No newline at end of file +?> diff --git a/ext/spl/internal/cachingiterator.inc b/ext/spl/internal/cachingiterator.inc index 7262564f2..33258ab95 100755 --- a/ext/spl/internal/cachingiterator.inc +++ b/ext/spl/internal/cachingiterator.inc @@ -86,7 +86,7 @@ class CachingIterator implements OuterIterator $this->it->next(); } - /** @return whether teh iterator is valid + /** @return whether the iterator is valid */ function valid() { @@ -154,4 +154,4 @@ class CachingIterator implements OuterIterator } } -?>
\ No newline at end of file +?> diff --git a/ext/spl/internal/regexiterator.inc b/ext/spl/internal/regexiterator.inc index 8b4ffe9da..96b8f8112 100755 --- a/ext/spl/internal/regexiterator.inc +++ b/ext/spl/internal/regexiterator.inc @@ -19,7 +19,7 @@ */ class RegexIterator extends FilterIterator { - const USE_KEY = 0x00000001; /**< If present in $flags the the key is + const USE_KEY = 0x00000001; /**< If present in $flags the key is used rather then the current value. */ const MATCH = 0; /**< Mode: Executed a plain match only */ diff --git a/ext/spl/php_spl.c b/ext/spl/php_spl.c index 965d0d3af..8e172efde 100755 --- a/ext/spl/php_spl.c +++ b/ext/spl/php_spl.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: php_spl.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: php_spl.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -877,7 +877,7 @@ const zend_function_entry spl_functions[] = { PHP_FE(iterator_count, arginfo_iterator) PHP_FE(iterator_apply, arginfo_iterator_apply) #endif /* SPL_ITERATORS_H */ - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ diff --git a/ext/spl/spl_array.c b/ext/spl/spl_array.c index 99a098228..be24d48ed 100755 --- a/ext/spl/spl_array.c +++ b/ext/spl/spl_array.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: spl_array.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: spl_array.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H # include "config.h" @@ -1929,7 +1929,7 @@ static const zend_function_entry spl_funcs_ArrayObject[] = { SPL_ME(Array, exchangeArray, arginfo_array_exchangeArray, ZEND_ACC_PUBLIC) SPL_ME(Array, setIteratorClass, arginfo_array_setIteratorClass, ZEND_ACC_PUBLIC) SPL_ME(Array, getIteratorClass, arginfo_array_void, ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} + PHP_FE_END }; static const zend_function_entry spl_funcs_ArrayIterator[] = { @@ -1958,13 +1958,13 @@ static const zend_function_entry spl_funcs_ArrayIterator[] = { SPL_ME(Array, next, arginfo_array_void, ZEND_ACC_PUBLIC) SPL_ME(Array, valid, arginfo_array_void, ZEND_ACC_PUBLIC) SPL_ME(Array, seek, arginfo_array_seek, ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} + PHP_FE_END }; static const zend_function_entry spl_funcs_RecursiveArrayIterator[] = { SPL_ME(Array, hasChildren, arginfo_array_void, ZEND_ACC_PUBLIC) SPL_ME(Array, getChildren, arginfo_array_void, ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ diff --git a/ext/spl/spl_directory.c b/ext/spl/spl_directory.c index b91fa5e52..b7560915c 100755 --- a/ext/spl/spl_directory.c +++ b/ext/spl/spl_directory.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: spl_directory.c 309035 2011-03-08 19:56:29Z felipe $ */ +/* $Id: spl_directory.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H # include "config.h" @@ -48,6 +48,8 @@ /* declare the class handlers */ static zend_object_handlers spl_filesystem_object_handlers; +/* includes handler to validate object state when retrieving methods */ +static zend_object_handlers spl_filesystem_object_check_handlers; /* decalre the class entry */ PHPAPI zend_class_entry *spl_ce_SplFileInfo; @@ -162,6 +164,16 @@ static zend_object_value spl_filesystem_object_new(zend_class_entry *class_type } /* }}} */ +/* {{{ spl_filesystem_object_new_ex */ +static zend_object_value spl_filesystem_object_new_check(zend_class_entry *class_type TSRMLS_DC) +{ + zend_object_value ret = spl_filesystem_object_new_ex(class_type, NULL TSRMLS_CC); + ret.handlers = &spl_filesystem_object_check_handlers; + return ret; +} +/* }}} */ + + PHPAPI char* spl_filesystem_object_get_path(spl_filesystem_object *intern, int *len TSRMLS_DC) /* {{{ */ { #ifdef HAVE_GLOB @@ -233,8 +245,12 @@ static void spl_filesystem_dir_open(spl_filesystem_object* intern, char *path TS intern->u.dir.index = 0; if (EG(exception) || intern->u.dir.dirp == NULL) { - /* throw exception: should've been already happened */ intern->u.dir.entry.d_name[0] = '\0'; + if (!EG(exception)) { + /* open failed w/out notice (turned to exception due to EH_THROW) */ + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 + TSRMLS_CC, "Failed to open directory \"%s\"", path); + } } else { do { spl_filesystem_dir_read(intern TSRMLS_CC); @@ -613,6 +629,19 @@ static HashTable* spl_filesystem_object_get_debug_info(zval *obj, int *is_temp T } /* }}} */ +zend_function *spl_filesystem_object_get_method_check(zval **object_ptr, char *method, int method_len TSRMLS_DC) /* {{{ */ +{ + spl_filesystem_object *fsobj = zend_object_store_get_object(*object_ptr TSRMLS_CC); + + if (fsobj->u.dir.entry.d_name[0] == '\0' && fsobj->orig_path == NULL) { + method = "_bad_state_ex"; + method_len = sizeof("_bad_state_ex") - 1; + } + + return zend_get_std_object_handlers()->get_method(object_ptr, method, method_len TSRMLS_CC); +} +/* }}} */ + #define DIT_CTOR_FLAGS 0x00000001 #define DIT_CTOR_GLOB 0x00000002 @@ -1350,6 +1379,15 @@ SPL_METHOD(SplFileInfo, getPathInfo) } /* }}} */ +/* {{{ */ +SPL_METHOD(SplFileInfo, _bad_state_ex) +{ + zend_throw_exception_ex(spl_ce_LogicException, 0 TSRMLS_CC, + "The parent constructor was not called: the object is in an " + "invalid state "); +} +/* }}} */ + /* {{{ proto void FilesystemIterator::__construct(string path [, int flags]) Cronstructs a new dir iterator from a path. */ SPL_METHOD(FilesystemIterator, __construct) @@ -1887,8 +1925,9 @@ static const zend_function_entry spl_SplFileInfo_functions[] = { SPL_ME(SplFileInfo, openFile, arginfo_info_openFile, ZEND_ACC_PUBLIC) SPL_ME(SplFileInfo, setFileClass, arginfo_info_optinalFileClass, ZEND_ACC_PUBLIC) SPL_ME(SplFileInfo, setInfoClass, arginfo_info_optinalFileClass, ZEND_ACC_PUBLIC) + SPL_ME(SplFileInfo, _bad_state_ex, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) SPL_MA(SplFileInfo, __toString, SplFileInfo, getPathname, arginfo_splfileinfo_void, ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} + PHP_FE_END }; ZEND_BEGIN_ARG_INFO(arginfo_dir___construct, 0) @@ -1914,7 +1953,7 @@ static const zend_function_entry spl_DirectoryIterator_functions[] = { SPL_ME(DirectoryIterator, next, arginfo_splfileinfo_void, ZEND_ACC_PUBLIC) SPL_ME(DirectoryIterator, seek, arginfo_dir_it_seek, ZEND_ACC_PUBLIC) SPL_MA(DirectoryIterator, __toString, DirectoryIterator, getFilename, arginfo_splfileinfo_void, ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} + PHP_FE_END }; ZEND_BEGIN_ARG_INFO_EX(arginfo_r_dir___construct, 0, 0, 1) @@ -1938,7 +1977,7 @@ static const zend_function_entry spl_FilesystemIterator_functions[] = { SPL_ME(FilesystemIterator, current, arginfo_splfileinfo_void, ZEND_ACC_PUBLIC) SPL_ME(FilesystemIterator, getFlags, arginfo_splfileinfo_void, ZEND_ACC_PUBLIC) SPL_ME(FilesystemIterator, setFlags, arginfo_r_dir_setFlags, ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} + PHP_FE_END }; static const zend_function_entry spl_RecursiveDirectoryIterator_functions[] = { @@ -1947,14 +1986,14 @@ static const zend_function_entry spl_RecursiveDirectoryIterator_functions[] = { SPL_ME(RecursiveDirectoryIterator, getChildren, arginfo_splfileinfo_void, ZEND_ACC_PUBLIC) SPL_ME(RecursiveDirectoryIterator, getSubPath, arginfo_splfileinfo_void, ZEND_ACC_PUBLIC) SPL_ME(RecursiveDirectoryIterator, getSubPathname,arginfo_splfileinfo_void, ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} + PHP_FE_END }; #ifdef HAVE_GLOB static const zend_function_entry spl_GlobIterator_functions[] = { SPL_ME(GlobIterator, __construct, arginfo_r_dir___construct, ZEND_ACC_PUBLIC) SPL_ME(GlobIterator, count, arginfo_splfileinfo_void, ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} + PHP_FE_END }; #endif /* }}} */ @@ -2205,17 +2244,24 @@ SPL_METHOD(SplFileObject, __construct) zend_replace_error_handling(EH_THROW, spl_ce_RuntimeException, &error_handling TSRMLS_CC); - intern->u.file.open_mode = "r"; - intern->u.file.open_mode_len = 1; + intern->u.file.open_mode = NULL; + intern->u.file.open_mode_len = 0; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|sbr", &intern->file_name, &intern->file_name_len, &intern->u.file.open_mode, &intern->u.file.open_mode_len, - &use_include_path, &intern->u.file.zcontext) == FAILURE) { + &use_include_path, &intern->u.file.zcontext) == FAILURE) { + intern->u.file.open_mode = NULL; + intern->file_name = NULL; zend_restore_error_handling(&error_handling TSRMLS_CC); return; } + if (intern->u.file.open_mode == NULL) { + intern->u.file.open_mode = "r"; + intern->u.file.open_mode_len = 1; + } + if (spl_filesystem_file_open(intern, use_include_path, 0 TSRMLS_CC) == SUCCESS) { tmp_path_len = strlen(intern->u.file.stream->orig_path); @@ -2862,7 +2908,7 @@ static const zend_function_entry spl_SplFileObject_functions[] = { /* mappings */ SPL_MA(SplFileObject, getCurrentLine, SplFileObject, fgets, arginfo_splfileinfo_void, ZEND_ACC_PUBLIC) SPL_MA(SplFileObject, __toString, SplFileObject, current, arginfo_splfileinfo_void, ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} + PHP_FE_END }; ZEND_BEGIN_ARG_INFO_EX(arginfo_temp_file_object___construct, 0, 0, 0) @@ -2871,7 +2917,7 @@ ZEND_END_ARG_INFO() static const zend_function_entry spl_SplTempFileObject_functions[] = { SPL_ME(SplTempFileObject, __construct, arginfo_temp_file_object___construct, ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ @@ -2911,13 +2957,16 @@ PHP_MINIT_FUNCTION(spl_directory) REGISTER_SPL_SUB_CLASS_EX(RecursiveDirectoryIterator, FilesystemIterator, spl_filesystem_object_new, spl_RecursiveDirectoryIterator_functions); REGISTER_SPL_IMPLEMENTS(RecursiveDirectoryIterator, RecursiveIterator); + + memcpy(&spl_filesystem_object_check_handlers, &spl_filesystem_object_handlers, sizeof(zend_object_handlers)); + spl_filesystem_object_check_handlers.get_method = spl_filesystem_object_get_method_check; #ifdef HAVE_GLOB - REGISTER_SPL_SUB_CLASS_EX(GlobIterator, FilesystemIterator, spl_filesystem_object_new, spl_GlobIterator_functions); + REGISTER_SPL_SUB_CLASS_EX(GlobIterator, FilesystemIterator, spl_filesystem_object_new_check, spl_GlobIterator_functions); REGISTER_SPL_IMPLEMENTS(GlobIterator, Countable); #endif - REGISTER_SPL_SUB_CLASS_EX(SplFileObject, SplFileInfo, spl_filesystem_object_new, spl_SplFileObject_functions); + REGISTER_SPL_SUB_CLASS_EX(SplFileObject, SplFileInfo, spl_filesystem_object_new_check, spl_SplFileObject_functions); REGISTER_SPL_IMPLEMENTS(SplFileObject, RecursiveIterator); REGISTER_SPL_IMPLEMENTS(SplFileObject, SeekableIterator); @@ -2926,7 +2975,7 @@ PHP_MINIT_FUNCTION(spl_directory) REGISTER_SPL_CLASS_CONST_LONG(SplFileObject, "SKIP_EMPTY", SPL_FILE_OBJECT_SKIP_EMPTY); REGISTER_SPL_CLASS_CONST_LONG(SplFileObject, "READ_CSV", SPL_FILE_OBJECT_READ_CSV); - REGISTER_SPL_SUB_CLASS_EX(SplTempFileObject, SplFileObject, spl_filesystem_object_new, spl_SplTempFileObject_functions); + REGISTER_SPL_SUB_CLASS_EX(SplTempFileObject, SplFileObject, spl_filesystem_object_new_check, spl_SplTempFileObject_functions); return SUCCESS; } /* }}} */ diff --git a/ext/spl/spl_dllist.c b/ext/spl/spl_dllist.c index da7effcd4..7db3885c2 100644 --- a/ext/spl/spl_dllist.c +++ b/ext/spl/spl_dllist.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: spl_dllist.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: spl_dllist.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H # include "config.h" @@ -1191,7 +1191,7 @@ ZEND_END_ARG_INFO() static const zend_function_entry spl_funcs_SplQueue[] = { SPL_MA(SplQueue, enqueue, SplDoublyLinkedList, push, arginfo_dllist_push, ZEND_ACC_PUBLIC) SPL_MA(SplQueue, dequeue, SplDoublyLinkedList, shift, arginfo_dllist_void, ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} + PHP_FE_END }; static const zend_function_entry spl_funcs_SplDoublyLinkedList[] = { @@ -1215,7 +1215,7 @@ static const zend_function_entry spl_funcs_SplDoublyLinkedList[] = { SPL_ME(SplDoublyLinkedList, next, arginfo_dllist_void, ZEND_ACC_PUBLIC) SPL_ME(SplDoublyLinkedList, prev, arginfo_dllist_void, ZEND_ACC_PUBLIC) SPL_ME(SplDoublyLinkedList, valid, arginfo_dllist_void, ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ diff --git a/ext/spl/spl_fixedarray.c b/ext/spl/spl_fixedarray.c index 71a12befe..54b457b50 100644 --- a/ext/spl/spl_fixedarray.c +++ b/ext/spl/spl_fixedarray.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: spl_fixedarray.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: spl_fixedarray.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -152,19 +152,23 @@ static HashTable* spl_fixedarray_object_get_properties(zval *obj TSRMLS_DC) /* { spl_fixedarray_object *intern = (spl_fixedarray_object*)zend_object_store_get_object(obj TSRMLS_CC); int i = 0; - if (intern->array) { + if (intern->array && !GC_G(gc_active)) { + int j = zend_hash_num_elements(intern->std.properties); + for (i = 0; i < intern->array->size; i++) { if (intern->array->elements[i]) { zend_hash_index_update(intern->std.properties, i, (void *)&intern->array->elements[i], sizeof(zval *), NULL); Z_ADDREF_P(intern->array->elements[i]); } else { - if (GC_G(gc_active)) { - return NULL; - } zend_hash_index_update(intern->std.properties, i, (void *)&EG(uninitialized_zval_ptr), sizeof(zval *), NULL); Z_ADDREF_P(EG(uninitialized_zval_ptr)); } } + if (j > intern->array->size) { + for (i = intern->array->size; i < j; ++i) { + zend_hash_index_del(intern->std.properties, i); + } + } } return intern->std.properties; @@ -1069,7 +1073,7 @@ static zend_function_entry spl_funcs_SplFixedArray[] = { /* {{{ */ SPL_ME(SplFixedArray, key, arginfo_splfixedarray_void, ZEND_ACC_PUBLIC) SPL_ME(SplFixedArray, next, arginfo_splfixedarray_void, ZEND_ACC_PUBLIC) SPL_ME(SplFixedArray, valid, arginfo_splfixedarray_void, ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ diff --git a/ext/spl/spl_iterators.c b/ext/spl/spl_iterators.c index e69eb8102..3ef0595c3 100755 --- a/ext/spl/spl_iterators.c +++ b/ext/spl/spl_iterators.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: spl_iterators.c 308503 2011-02-20 16:09:50Z felipe $ */ +/* $Id: spl_iterators.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H # include "config.h" @@ -67,7 +67,7 @@ ZEND_END_ARG_INFO() const zend_function_entry spl_funcs_RecursiveIterator[] = { SPL_ABSTRACT_ME(RecursiveIterator, hasChildren, arginfo_recursive_it_void) SPL_ABSTRACT_ME(RecursiveIterator, getChildren, arginfo_recursive_it_void) - {NULL, NULL, NULL} + PHP_FE_END }; typedef enum { @@ -125,6 +125,17 @@ typedef struct _spl_recursive_it_iterator { static zend_object_handlers spl_handlers_rec_it_it; static zend_object_handlers spl_handlers_dual_it; +#define SPL_FETCH_AND_CHECK_DUAL_IT(var, objzval) \ + do { \ + spl_dual_it_object *it = zend_object_store_get_object((objzval) TSRMLS_CC); \ + if (it->dit_type == DIT_Unknown) { \ + zend_throw_exception_ex(spl_ce_LogicException, 0 TSRMLS_CC, \ + "The object is in an invalid state as the parent constructor was not called"); \ + return; \ + } \ + (var) = it; \ + } while (0) + static void spl_recursive_it_dtor(zend_object_iterator *_iter TSRMLS_DC) { spl_recursive_it_iterator *iter = (spl_recursive_it_iterator*)_iter; @@ -360,6 +371,10 @@ next_step: static void spl_recursive_it_rewind_ex(spl_recursive_it_object *object, zval *zthis TSRMLS_DC) { zend_object_iterator *sub_iter; + + if (!object->iterators) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "The %s instance wasn't initialized properly", Z_OBJCE_P(zthis)->name); + } while (object->level) { sub_iter = object->iterators[object->level].iterator; @@ -402,6 +417,10 @@ static zend_object_iterator *spl_recursive_it_get_iterator(zend_class_entry *ce, } iterator = emalloc(sizeof(spl_recursive_it_iterator)); object = (spl_recursive_it_object*)zend_object_store_get_object(zobject TSRMLS_CC); + if (object->iterators == NULL) { + zend_error(E_ERROR, "The object to be iterated is in an invalid state: " + "the parent constructor has not been called"); + } Z_ADDREF_P(zobject); iterator->intern.data = (void*)object; @@ -957,7 +976,7 @@ static const zend_function_entry spl_funcs_RecursiveIteratorIterator[] = { SPL_ME(RecursiveIteratorIterator, nextElement, arginfo_recursive_it_void, ZEND_ACC_PUBLIC) SPL_ME(RecursiveIteratorIterator, setMaxDepth, arginfo_recursive_it_setMaxDepth, ZEND_ACC_PUBLIC) SPL_ME(RecursiveIteratorIterator, getMaxDepth, arginfo_recursive_it_void, ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} + PHP_FE_END }; static void spl_recursive_tree_iterator_get_prefix(spl_recursive_it_object *object, zval *return_value TSRMLS_DC) @@ -1235,7 +1254,7 @@ static const zend_function_entry spl_funcs_RecursiveTreeIterator[] = { SPL_ME(RecursiveTreeIterator, setPrefixPart, arginfo_recursive_tree_it_setPrefixPart, ZEND_ACC_PUBLIC) SPL_ME(RecursiveTreeIterator, getEntry, arginfo_recursive_it_void, ZEND_ACC_PUBLIC) SPL_ME(RecursiveTreeIterator, getPostfix, arginfo_recursive_it_void, ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} + PHP_FE_END }; #if MBO_0 @@ -1512,12 +1531,12 @@ SPL_METHOD(FilterIterator, __construct) SPL_METHOD(dual_it, getInnerIterator) { spl_dual_it_object *intern; - - intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); if (zend_parse_parameters_none() == FAILURE) { return; } + + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); if (intern->inner.zobject) { RETVAL_ZVAL(intern->inner.zobject, 1, 0); @@ -1616,13 +1635,13 @@ static inline void spl_dual_it_next(spl_dual_it_object *intern, int do_free TSRM SPL_METHOD(dual_it, rewind) { spl_dual_it_object *intern; - - intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); if (zend_parse_parameters_none() == FAILURE) { return; } + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); + spl_dual_it_rewind(intern TSRMLS_CC); spl_dual_it_fetch(intern, 1 TSRMLS_CC); } /* }}} */ @@ -1640,7 +1659,7 @@ SPL_METHOD(dual_it, valid) return; } - intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); RETURN_BOOL(intern->current.data); } /* }}} */ @@ -1661,7 +1680,7 @@ SPL_METHOD(dual_it, key) return; } - intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); if (intern->current.data) { if (intern->current.key_type == HASH_KEY_IS_STRING) { @@ -1689,7 +1708,7 @@ SPL_METHOD(dual_it, current) return; } - intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); if (intern->current.data) { RETVAL_ZVAL(intern->current.data, 1, 0); @@ -1710,7 +1729,7 @@ SPL_METHOD(dual_it, next) return; } - intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); spl_dual_it_next(intern, 1 TSRMLS_CC); spl_dual_it_fetch(intern, 1 TSRMLS_CC); @@ -1759,7 +1778,7 @@ SPL_METHOD(FilterIterator, rewind) return; } - intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); spl_filter_it_rewind(getThis(), intern TSRMLS_CC); } /* }}} */ @@ -1773,7 +1792,7 @@ SPL_METHOD(FilterIterator, next) return; } - intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); spl_filter_it_next(getThis(), intern TSRMLS_CC); } /* }}} */ @@ -1795,7 +1814,7 @@ SPL_METHOD(RecursiveFilterIterator, hasChildren) return; } - intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); zend_call_method_with_0_params(&intern->inner.zobject, intern->inner.ce, NULL, "haschildren", &retval); if (retval) { @@ -1816,7 +1835,7 @@ SPL_METHOD(RecursiveFilterIterator, getChildren) return; } - intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); zend_call_method_with_0_params(&intern->inner.zobject, intern->inner.ce, NULL, "getchildren", &retval); if (!EG(exception) && retval) { @@ -1846,7 +1865,7 @@ SPL_METHOD(RegexIterator, __construct) Match (string)current() against regular expression */ SPL_METHOD(RegexIterator, accept) { - spl_dual_it_object *intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + spl_dual_it_object *intern; char *subject, tmp[32], *result; int subject_len, use_copy, count = 0, result_len; zval subject_copy, zcount, *replacement; @@ -1855,6 +1874,8 @@ SPL_METHOD(RegexIterator, accept) return; } + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); + if (intern->current.data == NULL) { RETURN_FALSE; } @@ -1946,12 +1967,14 @@ SPL_METHOD(RegexIterator, accept) Returns current operation mode */ SPL_METHOD(RegexIterator, getMode) { - spl_dual_it_object *intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + spl_dual_it_object *intern; if (zend_parse_parameters_none() == FAILURE) { return; } + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); + RETURN_LONG(intern->u.regex.mode); } /* }}} */ @@ -1959,7 +1982,7 @@ SPL_METHOD(RegexIterator, getMode) Set new operation mode */ SPL_METHOD(RegexIterator, setMode) { - spl_dual_it_object *intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + spl_dual_it_object *intern; long mode; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &mode) == FAILURE) { @@ -1970,6 +1993,8 @@ SPL_METHOD(RegexIterator, setMode) zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0 TSRMLS_CC, "Illegal mode %ld", mode); return;/* NULL */ } + + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); intern->u.regex.mode = mode; } /* }}} */ @@ -1978,12 +2003,14 @@ SPL_METHOD(RegexIterator, setMode) Returns current operation flags */ SPL_METHOD(RegexIterator, getFlags) { - spl_dual_it_object *intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + spl_dual_it_object *intern; if (zend_parse_parameters_none() == FAILURE) { return; } + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); + RETURN_LONG(intern->u.regex.flags); } /* }}} */ @@ -1991,12 +2018,14 @@ SPL_METHOD(RegexIterator, getFlags) Set operation flags */ SPL_METHOD(RegexIterator, setFlags) { - spl_dual_it_object *intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + spl_dual_it_object *intern; long flags; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &flags) == FAILURE) { return; } + + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); intern->u.regex.flags = flags; } /* }}} */ @@ -2005,11 +2034,13 @@ SPL_METHOD(RegexIterator, setFlags) Returns current PREG flags (if in use or NULL) */ SPL_METHOD(RegexIterator, getPregFlags) { - spl_dual_it_object *intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + spl_dual_it_object *intern; if (zend_parse_parameters_none() == FAILURE) { return; } + + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); if (intern->u.regex.use_flags) { RETURN_LONG(intern->u.regex.preg_flags); @@ -2022,12 +2053,14 @@ SPL_METHOD(RegexIterator, getPregFlags) Set PREG flags */ SPL_METHOD(RegexIterator, setPregFlags) { - spl_dual_it_object *intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + spl_dual_it_object *intern; long preg_flags; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &preg_flags) == FAILURE) { return; } + + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); intern->u.regex.preg_flags = preg_flags; intern->u.regex.use_flags = 1; @@ -2051,7 +2084,7 @@ SPL_METHOD(RecursiveRegexIterator, getChildren) return; } - intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); zend_call_method_with_0_params(&intern->inner.zobject, intern->inner.ce, NULL, "getchildren", &retval); if (!EG(exception)) { @@ -2156,7 +2189,7 @@ static const zend_function_entry spl_funcs_FilterIterator[] = { SPL_ME(FilterIterator, next, arginfo_recursive_it_void, ZEND_ACC_PUBLIC) SPL_ME(dual_it, getInnerIterator, arginfo_recursive_it_void, ZEND_ACC_PUBLIC) SPL_ABSTRACT_ME(FilterIterator, accept, arginfo_recursive_it_void) - {NULL, NULL, NULL} + PHP_FE_END }; ZEND_BEGIN_ARG_INFO(arginfo_parent_it___construct, 0) @@ -2167,13 +2200,13 @@ static const zend_function_entry spl_funcs_RecursiveFilterIterator[] = { SPL_ME(RecursiveFilterIterator, __construct, arginfo_parent_it___construct, ZEND_ACC_PUBLIC) SPL_ME(RecursiveFilterIterator, hasChildren, arginfo_recursive_it_void, ZEND_ACC_PUBLIC) SPL_ME(RecursiveFilterIterator, getChildren, arginfo_recursive_it_void, ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} + PHP_FE_END }; static const zend_function_entry spl_funcs_ParentIterator[] = { SPL_ME(ParentIterator, __construct, arginfo_parent_it___construct, ZEND_ACC_PUBLIC) SPL_MA(ParentIterator, accept, RecursiveFilterIterator, hasChildren, arginfo_recursive_it_void, ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} + PHP_FE_END }; #if HAVE_PCRE || HAVE_BUNDLED_PCRE @@ -2206,7 +2239,7 @@ static const zend_function_entry spl_funcs_RegexIterator[] = { SPL_ME(RegexIterator, setFlags, arginfo_regex_it_set_flags, ZEND_ACC_PUBLIC) SPL_ME(RegexIterator, getPregFlags, arginfo_recursive_it_void, ZEND_ACC_PUBLIC) SPL_ME(RegexIterator, setPregFlags, arginfo_regex_it_set_preg_flags, ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} + PHP_FE_END }; ZEND_BEGIN_ARG_INFO_EX(arginfo_rec_regex_it___construct, 0, 0, 2) @@ -2221,7 +2254,7 @@ static const zend_function_entry spl_funcs_RecursiveRegexIterator[] = { SPL_ME(RecursiveRegexIterator, __construct, arginfo_rec_regex_it___construct, ZEND_ACC_PUBLIC) SPL_ME(RecursiveFilterIterator, hasChildren, arginfo_recursive_it_void, ZEND_ACC_PUBLIC) SPL_ME(RecursiveRegexIterator, getChildren, arginfo_recursive_it_void, ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} + PHP_FE_END }; #endif @@ -2288,7 +2321,7 @@ SPL_METHOD(LimitIterator, rewind) { spl_dual_it_object *intern; - intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); spl_dual_it_rewind(intern TSRMLS_CC); spl_limit_it_seek(intern, intern->u.limit.offset TSRMLS_CC); } /* }}} */ @@ -2299,7 +2332,7 @@ SPL_METHOD(LimitIterator, valid) { spl_dual_it_object *intern; - intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); /* RETURN_BOOL(spl_limit_it_valid(intern TSRMLS_CC) == SUCCESS);*/ RETURN_BOOL((intern->u.limit.count == -1 || intern->current.pos < intern->u.limit.offset + intern->u.limit.count) && intern->current.data); @@ -2311,7 +2344,7 @@ SPL_METHOD(LimitIterator, next) { spl_dual_it_object *intern; - intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); spl_dual_it_next(intern, 1 TSRMLS_CC); if (intern->u.limit.count == -1 || intern->current.pos < intern->u.limit.offset + intern->u.limit.count) { @@ -2330,7 +2363,7 @@ SPL_METHOD(LimitIterator, seek) return; } - intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); spl_limit_it_seek(intern, pos TSRMLS_CC); RETURN_LONG(intern->current.pos); } /* }}} */ @@ -2340,7 +2373,7 @@ SPL_METHOD(LimitIterator, seek) SPL_METHOD(LimitIterator, getPosition) { spl_dual_it_object *intern; - intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); RETURN_LONG(intern->current.pos); } /* }}} */ @@ -2350,7 +2383,7 @@ ZEND_END_ARG_INFO(); static const zend_function_entry spl_funcs_SeekableIterator[] = { SPL_ABSTRACT_ME(SeekableIterator, seek, arginfo_seekable_it_seek) - {NULL, NULL, NULL} + PHP_FE_END }; ZEND_BEGIN_ARG_INFO_EX(arginfo_limit_it___construct, 0, 0, 1) @@ -2373,7 +2406,7 @@ static const zend_function_entry spl_funcs_LimitIterator[] = { SPL_ME(LimitIterator, seek, arginfo_limit_it_seek, ZEND_ACC_PUBLIC) SPL_ME(LimitIterator, getPosition, arginfo_recursive_it_void, ZEND_ACC_PUBLIC) SPL_ME(dual_it, getInnerIterator, arginfo_recursive_it_void, ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} + PHP_FE_END }; static inline int spl_caching_it_valid(spl_dual_it_object *intern TSRMLS_DC) @@ -2495,7 +2528,7 @@ SPL_METHOD(CachingIterator, rewind) return; } - intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); spl_caching_it_rewind(intern TSRMLS_CC); } /* }}} */ @@ -2510,7 +2543,7 @@ SPL_METHOD(CachingIterator, valid) return; } - intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); RETURN_BOOL(spl_caching_it_valid(intern TSRMLS_CC) == SUCCESS); } /* }}} */ @@ -2525,7 +2558,7 @@ SPL_METHOD(CachingIterator, next) return; } - intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); spl_caching_it_next(intern TSRMLS_CC); } /* }}} */ @@ -2540,7 +2573,7 @@ SPL_METHOD(CachingIterator, hasNext) return; } - intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); RETURN_BOOL(spl_caching_it_has_next(intern TSRMLS_CC) == SUCCESS); } /* }}} */ @@ -2551,7 +2584,7 @@ SPL_METHOD(CachingIterator, __toString) { spl_dual_it_object *intern; - intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); if (!(intern->u.caching.flags & (CIT_CALL_TOSTRING|CIT_TOSTRING_USE_KEY|CIT_TOSTRING_USE_CURRENT|CIT_TOSTRING_USE_INNER))) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "%s does not fetch string value (see CachingIterator::__construct)", Z_OBJCE_P(getThis())->name); @@ -2586,7 +2619,7 @@ SPL_METHOD(CachingIterator, offsetSet) uint nKeyLength; zval *value; - intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); if (!(intern->u.caching.flags & CIT_FULL_CACHE)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "%s does not use a full cache (see CachingIterator::__construct)", Z_OBJCE_P(getThis())->name); @@ -2611,7 +2644,7 @@ SPL_METHOD(CachingIterator, offsetGet) uint nKeyLength; zval **value; - intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); if (!(intern->u.caching.flags & CIT_FULL_CACHE)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "%s does not use a full cache (see CachingIterator::__construct)", Z_OBJCE_P(getThis())->name); @@ -2639,7 +2672,7 @@ SPL_METHOD(CachingIterator, offsetUnset) char *arKey; uint nKeyLength; - intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); if (!(intern->u.caching.flags & CIT_FULL_CACHE)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "%s does not use a full cache (see CachingIterator::__construct)", Z_OBJCE_P(getThis())->name); @@ -2662,7 +2695,7 @@ SPL_METHOD(CachingIterator, offsetExists) char *arKey; uint nKeyLength; - intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); if (!(intern->u.caching.flags & CIT_FULL_CACHE)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "%s does not use a full cache (see CachingIterator::__construct)", Z_OBJCE_P(getThis())->name); @@ -2687,7 +2720,7 @@ SPL_METHOD(CachingIterator, getCache) return; } - intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); if (!(intern->u.caching.flags & CIT_FULL_CACHE)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "%v does not use a full cache (see CachingIterator::__construct)", Z_OBJCE_P(getThis())->name); @@ -2708,7 +2741,7 @@ SPL_METHOD(CachingIterator, getFlags) return; } - intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); RETURN_LONG(intern->u.caching.flags); } @@ -2721,7 +2754,7 @@ SPL_METHOD(CachingIterator, setFlags) spl_dual_it_object *intern; long flags; - intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &flags) == FAILURE) { return; @@ -2757,7 +2790,7 @@ SPL_METHOD(CachingIterator, count) return; } - intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); if (!(intern->u.caching.flags & CIT_FULL_CACHE)) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC, "%v does not use a full cache (see CachingIterator::__construct)", Z_OBJCE_P(getThis())->name); @@ -2804,7 +2837,7 @@ static const zend_function_entry spl_funcs_CachingIterator[] = { SPL_ME(CachingIterator, offsetExists, arginfo_caching_it_offsetGet, ZEND_ACC_PUBLIC) SPL_ME(CachingIterator, getCache, arginfo_recursive_it_void, ZEND_ACC_PUBLIC) SPL_ME(CachingIterator, count, arginfo_recursive_it_void, ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} + PHP_FE_END }; /* {{{ proto void RecursiveCachingIterator::__construct(RecursiveIterator it [, flags = CIT_CALL_TOSTRING]) @@ -2824,7 +2857,7 @@ SPL_METHOD(RecursiveCachingIterator, hasChildren) return; } - intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); RETURN_BOOL(intern->u.caching.zchildren); } /* }}} */ @@ -2839,7 +2872,7 @@ SPL_METHOD(RecursiveCachingIterator, getChildren) return; } - intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); if (intern->u.caching.zchildren) { RETURN_ZVAL(intern->u.caching.zchildren, 1, 0); @@ -2857,7 +2890,7 @@ static const zend_function_entry spl_funcs_RecursiveCachingIterator[] = { SPL_ME(RecursiveCachingIterator, __construct, arginfo_caching_rec_it___construct, ZEND_ACC_PUBLIC) SPL_ME(RecursiveCachingIterator, hasChildren, arginfo_recursive_it_void, ZEND_ACC_PUBLIC) SPL_ME(RecursiveCachingIterator, getChildren, arginfo_recursive_it_void, ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} + PHP_FE_END }; /* {{{ proto void IteratorIterator::__construct(Traversable it) @@ -2879,7 +2912,7 @@ static const zend_function_entry spl_funcs_IteratorIterator[] = { SPL_ME(dual_it, current, arginfo_recursive_it_void, ZEND_ACC_PUBLIC) SPL_ME(dual_it, next, arginfo_recursive_it_void, ZEND_ACC_PUBLIC) SPL_ME(dual_it, getInnerIterator, arginfo_recursive_it_void, ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} + PHP_FE_END }; /* {{{ proto void NoRewindIterator::__construct(Iterator it) @@ -2909,7 +2942,7 @@ SPL_METHOD(NoRewindIterator, valid) return; } - intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); RETURN_BOOL(intern->inner.iterator->funcs->valid(intern->inner.iterator TSRMLS_CC) == SUCCESS); } /* }}} */ @@ -2923,7 +2956,7 @@ SPL_METHOD(NoRewindIterator, key) return; } - intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); if (intern->inner.iterator->funcs->get_current_key) { char *str_key; @@ -2955,7 +2988,7 @@ SPL_METHOD(NoRewindIterator, current) return; } - intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); intern->inner.iterator->funcs->get_current_data(intern->inner.iterator, &data TSRMLS_CC); if (data && *data) { RETURN_ZVAL(*data, 1, 0); @@ -2972,7 +3005,7 @@ SPL_METHOD(NoRewindIterator, next) return; } - intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); intern->inner.iterator->funcs->move_forward(intern->inner.iterator TSRMLS_CC); } /* }}} */ @@ -2988,7 +3021,7 @@ static const zend_function_entry spl_funcs_NoRewindIterator[] = { SPL_ME(NoRewindIterator, current, arginfo_recursive_it_void, ZEND_ACC_PUBLIC) SPL_ME(NoRewindIterator, next, arginfo_recursive_it_void, ZEND_ACC_PUBLIC) SPL_ME(dual_it, getInnerIterator, arginfo_recursive_it_void, ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} + PHP_FE_END }; /* {{{ proto void InfiniteIterator::__construct(Iterator it) @@ -3008,7 +3041,7 @@ SPL_METHOD(InfiniteIterator, next) return; } - intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); spl_dual_it_next(intern, 1 TSRMLS_CC); if (spl_dual_it_valid(intern TSRMLS_CC) == SUCCESS) { @@ -3024,7 +3057,7 @@ SPL_METHOD(InfiniteIterator, next) static const zend_function_entry spl_funcs_InfiniteIterator[] = { SPL_ME(InfiniteIterator, __construct, arginfo_norewind_it___construct, ZEND_ACC_PUBLIC) SPL_ME(InfiniteIterator, next, arginfo_recursive_it_void, ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} + PHP_FE_END }; /* {{{ proto void EmptyIterator::rewind() @@ -3081,7 +3114,7 @@ static const zend_function_entry spl_funcs_EmptyIterator[] = { SPL_ME(EmptyIterator, key, arginfo_recursive_it_void, ZEND_ACC_PUBLIC) SPL_ME(EmptyIterator, current, arginfo_recursive_it_void, ZEND_ACC_PUBLIC) SPL_ME(EmptyIterator, next, arginfo_recursive_it_void, ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} + PHP_FE_END }; int spl_append_it_next_iterator(spl_dual_it_object *intern TSRMLS_DC) /* {{{*/ @@ -3147,9 +3180,7 @@ SPL_METHOD(AppendIterator, append) spl_dual_it_object *intern; zval *it; - intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); - - APPENDIT_CHECK_CTOR(intern); + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "O", &it, zend_ce_iterator) == FAILURE) { return; @@ -3177,7 +3208,7 @@ SPL_METHOD(AppendIterator, rewind) return; } - intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); intern->u.append.iterator->funcs->rewind(intern->u.append.iterator TSRMLS_CC); if (spl_append_it_next_iterator(intern TSRMLS_CC) == SUCCESS) { @@ -3195,7 +3226,7 @@ SPL_METHOD(AppendIterator, valid) return; } - intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); RETURN_BOOL(intern->current.data); } /* }}} */ @@ -3210,7 +3241,7 @@ SPL_METHOD(AppendIterator, next) return; } - intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); spl_append_it_next(intern TSRMLS_CC); } /* }}} */ @@ -3225,7 +3256,7 @@ SPL_METHOD(AppendIterator, getIteratorIndex) return; } - intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); APPENDIT_CHECK_CTOR(intern); spl_array_iterator_key(intern->u.append.zarrayit, return_value TSRMLS_CC); @@ -3241,9 +3272,8 @@ SPL_METHOD(AppendIterator, getArrayIterator) return; } - intern = (spl_dual_it_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); - APPENDIT_CHECK_CTOR(intern); RETURN_ZVAL(intern->u.append.zarrayit, 1, 0); } /* }}} */ @@ -3262,7 +3292,7 @@ static const zend_function_entry spl_funcs_AppendIterator[] = { SPL_ME(dual_it, getInnerIterator, arginfo_recursive_it_void, ZEND_ACC_PUBLIC) SPL_ME(AppendIterator, getIteratorIndex, arginfo_recursive_it_void, ZEND_ACC_PUBLIC) SPL_ME(AppendIterator, getArrayIterator, arginfo_recursive_it_void, ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} + PHP_FE_END }; PHPAPI int spl_iterator_apply(zval *obj, spl_iterator_apply_func_t apply_func, void *puser TSRMLS_DC) @@ -3276,6 +3306,7 @@ PHPAPI int spl_iterator_apply(zval *obj, spl_iterator_apply_func_t apply_func, v goto done; } + iter->index = 0; if (iter->funcs->rewind) { iter->funcs->rewind(iter TSRMLS_CC); if (EG(exception)) { @@ -3290,6 +3321,7 @@ PHPAPI int spl_iterator_apply(zval *obj, spl_iterator_apply_func_t apply_func, v if (apply_func(iter, puser TSRMLS_CC) == ZEND_HASH_APPLY_STOP || EG(exception)) { goto done; } + iter->index++; iter->funcs->move_forward(iter TSRMLS_CC); if (EG(exception)) { goto done; @@ -3452,12 +3484,12 @@ PHP_FUNCTION(iterator_apply) static const zend_function_entry spl_funcs_OuterIterator[] = { SPL_ABSTRACT_ME(OuterIterator, getInnerIterator, arginfo_recursive_it_void) - {NULL, NULL, NULL} + PHP_FE_END }; static const zend_function_entry spl_funcs_Countable[] = { SPL_ABSTRACT_ME(Countable, count, arginfo_recursive_it_void) - {NULL, NULL, NULL} + PHP_FE_END }; /* {{{ PHP_MINIT_FUNCTION(spl_iterators) diff --git a/ext/spl/tests/DirectoryIterator_getGroup_basic.phpt b/ext/spl/tests/DirectoryIterator_getGroup_basic.phpt index 58387ccce..9cc9fadbe 100644 --- a/ext/spl/tests/DirectoryIterator_getGroup_basic.phpt +++ b/ext/spl/tests/DirectoryIterator_getGroup_basic.phpt @@ -8,23 +8,19 @@ Daniel Londero <daniel.londero@gmail.com> Francesco Trucchia <ft@ideato.it> Jacopo Romei <jacopo@sviluppoagile.it> #Test Fest Cesena (Italy) on 2009-06-20
---SKIPIF--
-<?php
-if (substr(PHP_OS, 0, 3) == 'WIN') die("skip this test not for Windows platforms");
-?>
--FILE--
<?php
- -shell_exec('mkdir test_dir_ptfi'); -$dir = new DirectoryIterator('test_dir_ptfi'); -$result = shell_exec('ls -lnd test_dir_ptfi | cut -d" " -f 4'); - -var_dump($dir->getGroup() == $result); - +$dirname = 'DirectoryIterator_getGroup_basic'; +mkdir($dirname); +$dir = new DirectoryIterator($dirname); +$expected = filegroup($dirname); +$actual = $dir->getGroup(); +var_dump($expected == $actual); ?> --CLEAN-- <?php -rmdir('test_dir_ptfi'); +$dirname = 'DirectoryIterator_getGroup_basic'; +rmdir($dirname); ?>
--EXPECTF--
bool(true) diff --git a/ext/spl/tests/DirectoryIterator_getOwner_basic.phpt b/ext/spl/tests/DirectoryIterator_getOwner_basic.phpt index e342dcdb6..c5e9f7c2c 100644 --- a/ext/spl/tests/DirectoryIterator_getOwner_basic.phpt +++ b/ext/spl/tests/DirectoryIterator_getOwner_basic.phpt @@ -1,29 +1,26 @@ ---TEST--
-SPL: Spl Directory Iterator test getOwner
---CREDITS--
+--TEST-- +SPL: DirectoryIterator test getOwner +--CREDITS-- Cesare D'Amico <cesare.damico@gruppovolta.it> Andrea Giorgini <agiorg@gmail.com> Filippo De Santis <fd@ideato.it> Daniel Londero <daniel.londero@gmail.com> Francesco Trucchia <ft@ideato.it> Jacopo Romei <jacopo@sviluppoagile.it> -#Test Fest Cesena (Italy) on 2009-06-20
---SKIPIF--
-<?php
-if (substr(PHP_OS, 0, 3) == 'WIN') die("skip this test not for Windows platforms");
-?>
---FILE--
-<?php
- -shell_exec('mkdir test_dir_ptfi'); -$dir = new DirectoryIterator('test_dir_ptfi'); -$result = shell_exec('ls -lnd test_dir_ptfi | cut -d" " -f 3'); -var_dump($dir->getOwner() == $result); - +#Test Fest Cesena (Italy) on 2009-06-20 +--FILE-- +<?php +$dirname = 'DirectoryIterator_getOwner_basic'; +mkdir($dirname); +$dir = new DirectoryIterator($dirname); +$expected = fileowner($dirname); +$actual = $dir->getOwner(); +var_dump($expected == $actual); ?> --CLEAN-- <?php -rmdir('test_dir_ptfi'); -?>
---EXPECTF--
+$dirname = 'DirectoryIterator_getOwner_basic'; +rmdir($dirname); +?> +--EXPECTF-- bool(true) diff --git a/ext/spl/tests/SplFileInfo_getGroup_basic.phpt b/ext/spl/tests/SplFileInfo_getGroup_basic.phpt index 7b0528d7d..c5808c57d 100644 --- a/ext/spl/tests/SplFileInfo_getGroup_basic.phpt +++ b/ext/spl/tests/SplFileInfo_getGroup_basic.phpt @@ -1,30 +1,26 @@ ---TEST--
-SPL: Spl File Info test getGroup
---CREDITS--
+--TEST-- +SPL: SplFileInfo test getGroup +--CREDITS-- Cesare D'Amico <cesare.damico@gruppovolta.it> Andrea Giorgini <agiorg@gmail.com> Filippo De Santis <fd@ideato.it> Daniel Londero <daniel.londero@gmail.com> Francesco Trucchia <ft@ideato.it> Jacopo Romei <jacopo@sviluppoagile.it> -#Test Fest Cesena (Italy) on 2009-06-20
---SKIPIF--
-<?php
-if (substr(PHP_OS, 0, 3) == 'WIN') die("skip this test not for Windows platforms");
-?>
---FILE--
-<?php
- -//file -touch ('test_file_ptfi'); -$fileInfo = new SplFileInfo('test_file_ptfi'); -$result = shell_exec('ls -ln test_file_ptfi | cut -d" " -f 4'); -var_dump($fileInfo->getGroup() == $result); - +#Test Fest Cesena (Italy) on 2009-06-20 +--FILE-- +<?php +$filename = basename(__FILE__, 'phpt').'tmp'; +touch($filename); +$fileInfo = new SplFileInfo($filename); +$expected = filegroup($filename); +$actual = $fileInfo->getGroup(); +var_dump($expected == $actual); ?> --CLEAN-- <?php -unlink('test_file_ptfi'); -?>
---EXPECTF--
+$filename = basename(__FILE__, 'phpt').'tmp'; +unlink($filename); +?> +--EXPECTF-- bool(true) diff --git a/ext/spl/tests/SplFileInfo_getOwner_basic.phpt b/ext/spl/tests/SplFileInfo_getOwner_basic.phpt index 50f79430c..790dcc69b 100644 --- a/ext/spl/tests/SplFileInfo_getOwner_basic.phpt +++ b/ext/spl/tests/SplFileInfo_getOwner_basic.phpt @@ -1,30 +1,26 @@ ---TEST--
-SPL: Spl File Info test getOwner
---CREDITS--
+--TEST-- +SPL: SplFileInfo test getOwner +--CREDITS-- Cesare D'Amico <cesare.damico@gruppovolta.it> Andrea Giorgini <agiorg@gmail.com> Filippo De Santis <fd@ideato.it> Daniel Londero <daniel.londero@gmail.com> Francesco Trucchia <ft@ideato.it> Jacopo Romei <jacopo@sviluppoagile.it> -#Test Fest Cesena (Italy) on 2009-06-20
---SKIPIF--
-<?php
-if (substr(PHP_OS, 0, 3) == 'WIN') die("skip this test not for Windows platforms");
-?>
---FILE--
-<?php
- -//file -touch ('test_file_ptfi'); -$fileInfo = new SplFileInfo('test_file_ptfi'); -$result = shell_exec('ls -ln test_file_ptfi | cut -d" " -f 3'); -var_dump($fileInfo->getOwner() == $result); - +#Test Fest Cesena (Italy) on 2009-06-20 +--FILE-- +<?php +$filename = basename(__FILE__, 'phpt').'tmp'; +touch($filename); +$fileInfo = new SplFileInfo($filename); +$expected = fileowner($filename); +$actual = $fileInfo->getOwner(); +var_dump($expected == $actual); ?> --CLEAN-- <?php -unlink('test_file_ptfi'); -?>
---EXPECTF--
+$filename = basename(__FILE__, 'phpt').'tmp'; +unlink($filename); +?> +--EXPECTF-- bool(true) diff --git a/ext/spl/tests/bug54281.phpt b/ext/spl/tests/bug54281.phpt new file mode 100644 index 000000000..d42d9e585 --- /dev/null +++ b/ext/spl/tests/bug54281.phpt @@ -0,0 +1,15 @@ +--TEST-- +Bug #54281 (Crash in spl_recursive_it_rewind_ex) +--FILE-- +<?php + +class RecursiveArrayIteratorIterator extends RecursiveIteratorIterator { + function __construct($it, $max_depth) { } +} +$it = new RecursiveArrayIteratorIterator(new RecursiveArrayIterator(array()), 2); + +foreach($it as $k=>$v) { } + +?> +--EXPECTF-- +Fatal error: RecursiveIteratorIterator::rewind(): The RecursiveArrayIteratorIterator instance wasn't initialized properly in %s on line %d diff --git a/ext/spl/tests/bug54291.phpt b/ext/spl/tests/bug54291.phpt new file mode 100644 index 000000000..b8f596e02 --- /dev/null +++ b/ext/spl/tests/bug54291.phpt @@ -0,0 +1,13 @@ +--TEST-- +Bug #54291 (Crash iterating DirectoryIterator for dir name starting with \0) +--FILE-- +<?php +$dir = new DirectoryIterator("\x00/abc"); +$dir->isFile(); +--EXPECTF-- +Fatal error: Uncaught exception 'UnexpectedValueException' with message 'Failed to open directory ""' in %s:%d +Stack trace: +#0 %s(%d): DirectoryIterator->__construct('?/abc') +#1 {main} + thrown in %s on line %d + diff --git a/ext/spl/tests/bug54292.phpt b/ext/spl/tests/bug54292.phpt new file mode 100644 index 000000000..d9175f7e6 --- /dev/null +++ b/ext/spl/tests/bug54292.phpt @@ -0,0 +1,14 @@ +--TEST-- +Bug #54292 (Wrong parameter causes crash in SplFileObject::__construct()) +--FILE-- +<?php + +try { + new SplFileObject('foo', array()); +} catch (Exception $e) { + var_dump($e->getMessage()); +} + +?> +--EXPECTF-- +string(74) "SplFileObject::__construct() expects parameter 2 to be string, array given" diff --git a/ext/spl/tests/bug54384.phpt b/ext/spl/tests/bug54384.phpt new file mode 100644 index 000000000..a1ce7edff --- /dev/null +++ b/ext/spl/tests/bug54384.phpt @@ -0,0 +1,171 @@ +--TEST-- +Bug #54384: Several SPL classes crash when the parent constructor is not called +--FILE-- +<?php + +function test($f) { + try { + $f(); + echo "ran normally (unexpected)\n\n"; + } catch (LogicException $e) { + echo "exception (expected)\n"; + } +} + +echo "IteratorIterator... "; +class IteratorIteratorTest extends IteratorIterator { + function __construct(){} +} +test( function() { + $o = new IteratorIteratorTest; + $o->rewind(); +} ); + +echo "FilterIterator... "; +class FilterIteratorTest extends FilterIterator { + function __construct(){} + function accept(){} +} +test( function() { + $o = new FilterIteratorTest; + $o->rewind(); +} ); + +echo "RecursiveFilterIterator... "; +class RecursiveFilterIteratorTest extends RecursiveFilterIterator { + function __construct(){} + function accept(){} +} +test( function() { +$o = new RecursiveFilterIteratorTest; +$o->hasChildren(); +} ); + +echo "ParentIterator... "; +class ParentIteratorTest extends ParentIterator { + function __construct(){} +} +test ( function() { +$o = new ParentIteratorTest; +$o->accept(); +} ); + +echo "LimitIterator... "; +class LimitIteratorTest extends LimitIterator { + function __construct(){} +} +test ( function() { +$o = new LimitIteratorTest; +$o->rewind(); +} ); + +echo "CachingIterator... "; +class CachingIteratorTest extends CachingIterator { + function __construct(){} +} +test ( function() { +$o = new CachingIteratorTest; +$o->rewind(); +} ); + +echo "RecursiveCachingIterator... "; +class RecursiveCachingIteratorTest extends RecursiveCachingIterator { + function __construct(){} +} +test ( function() { +$o = new RecursiveCachingIteratorTest; +$o->rewind(); +} ); + +echo "NoRewindIterator... "; +class NoRewindIteratorTest extends NoRewindIterator { + function __construct(){} +} +test ( function() { +$o = new NoRewindIteratorTest; +$o->valid(); +} ); + +echo "RegexIterator... "; +class RegexIteratorTest extends RegexIterator { + function __construct(){} +} +test ( function() { +$o = new RegexIteratorTest; +$o->rewind(); +} ); + +echo "RecursiveRegexIterator... "; +class RecursiveRegexIteratorTest extends RecursiveRegexIterator { + function __construct(){} +} +test ( function() { +$o = new RecursiveRegexIteratorTest; +$o->hasChildren(); +} ); + +echo "GlobIterator... "; +class GlobIteratorTest extends GlobIterator { + function __construct(){} +} +test ( function() { +$o = new GlobIteratorTest; +$o->count(); +} ); + +echo "SplFileObject... "; +class SplFileObjectTest extends SplFileObject { + function __construct(){} +} +test ( function() { +$o = new SplFileObjectTest; +$o->rewind(); +} ); + +echo "SplTempFileObject... "; +class SplTempFileObjectTest extends SplTempFileObject { + function __construct(){} +} +test ( function() { +$o = new SplTempFileObjectTest; +$o->rewind(); +} ); + +echo "AppendIterator... "; +class AppendIteratorTest extends AppendIterator { + function __construct(){} +} +test ( function() { +$o = new AppendIteratorTest; +foreach ($o as $a) { +echo $a,"\n"; +} +} ); + +echo "InfiniteIterator... "; +class InfiniteIteratorTest extends InfiniteIterator { + function __construct(){} +} +test ( function() { +$o = new InfiniteIteratorTest; +foreach ($o as $a) { +echo $a,"\n"; +} +} ); + +--EXPECT-- +IteratorIterator... exception (expected) +FilterIterator... exception (expected) +RecursiveFilterIterator... exception (expected) +ParentIterator... exception (expected) +LimitIterator... exception (expected) +CachingIterator... exception (expected) +RecursiveCachingIterator... exception (expected) +NoRewindIterator... exception (expected) +RegexIterator... exception (expected) +RecursiveRegexIterator... exception (expected) +GlobIterator... exception (expected) +SplFileObject... exception (expected) +SplTempFileObject... exception (expected) +AppendIterator... exception (expected) +InfiniteIterator... exception (expected) diff --git a/ext/spl/tests/bug54970.phpt b/ext/spl/tests/bug54970.phpt new file mode 100644 index 000000000..62b1eedb5 --- /dev/null +++ b/ext/spl/tests/bug54970.phpt @@ -0,0 +1,33 @@ +--TEST-- +Bug #54970 (SplFixedArray::setSize() isn't resizing) +--FILE-- +<?php + +$fa = new SplFixedArray(2); +$fa[0] = 'Hello'; +$fa[1] = 'World'; +$fa->setSize(3); +$fa[2] = '!'; +var_dump($fa); +$fa->setSize(2); +var_dump($fa); +var_dump($fa->getSize()); + + +?> +--EXPECTF-- +object(SplFixedArray)#%d (3) { + [0]=> + string(5) "Hello" + [1]=> + string(5) "World" + [2]=> + string(1) "!" +} +object(SplFixedArray)#%d (2) { + [0]=> + string(5) "Hello" + [1]=> + string(5) "World" +} +int(2) diff --git a/ext/spl/tests/bug54971.phpt b/ext/spl/tests/bug54971.phpt new file mode 100644 index 000000000..166613b43 --- /dev/null +++ b/ext/spl/tests/bug54971.phpt @@ -0,0 +1,45 @@ +--TEST-- +Bug #54971 (Wrong result when using iterator_to_array with use_keys on true) +--FILE-- +<?php + +$source = <<<XML +<root> +<node>val1</node> +<node>val2</node> +</root> +XML; + + +$doc = new DOMDocument(); +$doc->loadXML($source); + +$xpath = new DOMXPath($doc); +$items = $xpath->query('//node'); + +print_r(iterator_to_array($items, false)); +print_r(iterator_to_array($items, true)); +?> +--EXPECT-- +Array +( + [0] => DOMElement Object + ( + ) + + [1] => DOMElement Object + ( + ) + +) +Array +( + [0] => DOMElement Object + ( + ) + + [1] => DOMElement Object + ( + ) + +) diff --git a/ext/spl/tests/iterator_031.phpt b/ext/spl/tests/iterator_031.phpt index 458f071b7..40342f4bb 100755 --- a/ext/spl/tests/iterator_031.phpt +++ b/ext/spl/tests/iterator_031.phpt @@ -56,7 +56,7 @@ try { $ap->append($it); } -catch(BadMethodCallException $e) +catch(LogicException $e) { echo $e->getMessage() . "\n"; } @@ -90,7 +90,7 @@ MyArrayIterator::rewind 1=>2 MyAppendIterator::__construct MyAppendIterator::append -Classes derived from AppendIterator must call AppendIterator::__construct() +The object is in an invalid state as the parent constructor was not called AppendIterator::getIterator() must be called exactly once per instance MyAppendIterator::append MyArrayIterator::rewind diff --git a/ext/sqlite/config.m4 b/ext/sqlite/config.m4 index 4f7b72c9e..552ec69aa 100644 --- a/ext/sqlite/config.m4 +++ b/ext/sqlite/config.m4 @@ -1,4 +1,4 @@ -dnl $Id: config.m4 291414 2009-11-29 06:13:22Z rasmus $ +dnl $Id: config.m4 311041 2011-05-15 05:49:34Z rasmus $ dnl config.m4 for extension sqlite dnl vim:et:ts=2:sw=2 diff --git a/ext/sqlite3/libsqlite/php-sqlite3-changes.patch b/ext/sqlite3/libsqlite/php-sqlite3-changes.patch index 50b2e4c51..c97de1b7f 100644 --- a/ext/sqlite3/libsqlite/php-sqlite3-changes.patch +++ b/ext/sqlite3/libsqlite/php-sqlite3-changes.patch @@ -1,5 +1,5 @@ ---- sqlite3.c 2008-11-22 15:31:05.000000000 +0000 -+++ sqlite3.c 2008-11-22 15:32:21.000000000 +0000 +--- sqlite3.c.orig 2011-04-17 10:54:01.000000000 -0700 ++++ sqlite3.c 2011-05-12 22:57:02.000000000 -0700 @@ -1,3 +1,7 @@ +#if defined(_MSC_VER) && _MSC_VER < 1300 +#pragma optimize("", off) @@ -7,27 +7,8 @@ + /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite - ** version 3.6.6. By combining all the individual C code files into this -@@ -397,16 +401,12 @@ - ** If none of the above are defined, then set SQLITE_SYSTEM_MALLOC as - ** the default. - */ --#if defined(SQLITE_SYSTEM_MALLOC)+defined(SQLITE_MEMDEBUG)+\ -- defined(SQLITE_MEMORY_SIZE)+defined(SQLITE_MMAP_HEAP_SIZE)+\ -- defined(SQLITE_POW2_MEMORY_SIZE)>1 -+#if defined(SQLITE_SYSTEM_MALLOC)+defined(SQLITE_MEMDEBUG)+defined(SQLITE_MEMORY_SIZE)+defined(SQLITE_MMAP_HEAP_SIZE)+defined(SQLITE_POW2_MEMORY_SIZE)>1 - # error "At most one of the following compile-time configuration options\ - is allows: SQLITE_SYSTEM_MALLOC, SQLITE_MEMDEBUG, SQLITE_MEMORY_SIZE,\ - SQLITE_MMAP_HEAP_SIZE, SQLITE_POW2_MEMORY_SIZE" - #endif --#if defined(SQLITE_SYSTEM_MALLOC)+defined(SQLITE_MEMDEBUG)+\ -- defined(SQLITE_MEMORY_SIZE)+defined(SQLITE_MMAP_HEAP_SIZE)+\ -- defined(SQLITE_POW2_MEMORY_SIZE)==0 -+#if defined(SQLITE_SYSTEM_MALLOC)+defined(SQLITE_MEMDEBUG)+defined(SQLITE_MEMORY_SIZE)+defined(SQLITE_MMAP_HEAP_SIZE)+defined(SQLITE_POW2_MEMORY_SIZE)==0 - # define SQLITE_SYSTEM_MALLOC 1 - #endif - -@@ -98713,3 +98713,7 @@ + ** version 3.7.6.2. By combining all the individual C code files into this +@@ -125956,3 +125960,7 @@ #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */ /************** End of fts3_icu.c ********************************************/ diff --git a/ext/sqlite3/libsqlite/sqlite3.c b/ext/sqlite3/libsqlite/sqlite3.c index 51b0a6a58..d187e66d2 100644 --- a/ext/sqlite3/libsqlite/sqlite3.c +++ b/ext/sqlite3/libsqlite/sqlite3.c @@ -4,8 +4,8 @@ /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite -** version 3.7.4. By combining all the individual C code files into this -** single large file, the entire code can be compiled as a one translation +** version 3.7.7.1. By combining all the individual C code files into this +** single large file, the entire code can be compiled as a single translation ** unit. This allows many compilers to do optimizations that would not be ** possible if the files were compiled separately. Performance improvements ** of 5% or more are commonly seen when SQLite is compiled as a single @@ -204,7 +204,7 @@ /* ** The maximum number of attached databases. This must be between 0 -** and 30. The upper bound on 30 is because a 32-bit integer bitmap +** and 62. The upper bound on 62 is because a 64-bit integer bitmap ** is used internally to track attached databases. */ #ifndef SQLITE_MAX_ATTACHED @@ -654,9 +654,9 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.7.4" -#define SQLITE_VERSION_NUMBER 3007004 -#define SQLITE_SOURCE_ID "2010-12-07 20:14:09 a586a4deeb25330037a49df295b36aaf624d0f45" +#define SQLITE_VERSION "3.7.7.1" +#define SQLITE_VERSION_NUMBER 3007007 +#define SQLITE_SOURCE_ID "2011-06-28 17:39:05 af0d91adf497f5f36ec3813f04235a6e195a605f" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -857,7 +857,7 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**); ** argument. ^If the callback function of the 3rd argument to ** sqlite3_exec() is not NULL, then it is invoked for each result row ** coming out of the evaluated SQL statements. ^The 4th argument to -** to sqlite3_exec() is relayed through to the 1st argument of each +** sqlite3_exec() is relayed through to the 1st argument of each ** callback invocation. ^If the callback pointer to sqlite3_exec() ** is NULL, then no callback is ever invoked and result rows are ** ignored. @@ -922,7 +922,8 @@ SQLITE_API int sqlite3_exec( ** ** New error codes may be added in future versions of SQLite. ** -** See also: [SQLITE_IOERR_READ | extended result codes] +** See also: [SQLITE_IOERR_READ | extended result codes], +** [sqlite3_vtab_on_conflict()] [SQLITE_ROLLBACK | result codes]. */ #define SQLITE_OK 0 /* Successful result */ /* beginning-of-error-codes */ @@ -937,7 +938,7 @@ SQLITE_API int sqlite3_exec( #define SQLITE_INTERRUPT 9 /* Operation terminated by sqlite3_interrupt()*/ #define SQLITE_IOERR 10 /* Some kind of disk I/O error occurred */ #define SQLITE_CORRUPT 11 /* The database disk image is malformed */ -#define SQLITE_NOTFOUND 12 /* NOT USED. Table or record not found */ +#define SQLITE_NOTFOUND 12 /* Unknown opcode in sqlite3_file_control() */ #define SQLITE_FULL 13 /* Insertion failed because database is full */ #define SQLITE_CANTOPEN 14 /* Unable to open the database file */ #define SQLITE_PROTOCOL 15 /* Database lock protocol error */ @@ -999,17 +1000,21 @@ SQLITE_API int sqlite3_exec( #define SQLITE_IOERR_SHMOPEN (SQLITE_IOERR | (18<<8)) #define SQLITE_IOERR_SHMSIZE (SQLITE_IOERR | (19<<8)) #define SQLITE_IOERR_SHMLOCK (SQLITE_IOERR | (20<<8)) +#define SQLITE_IOERR_SHMMAP (SQLITE_IOERR | (21<<8)) +#define SQLITE_IOERR_SEEK (SQLITE_IOERR | (22<<8)) #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) #define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8)) +#define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8)) +#define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8)) +#define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8)) /* ** CAPI3REF: Flags For File Open Operations ** ** These bit values are intended for use in the ** 3rd parameter to the [sqlite3_open_v2()] interface and -** in the 4th parameter to the xOpen method of the -** [sqlite3_vfs] object. +** in the 4th parameter to the [sqlite3_vfs.xOpen] method. */ #define SQLITE_OPEN_READONLY 0x00000001 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_READWRITE 0x00000002 /* Ok for sqlite3_open_v2() */ @@ -1017,6 +1022,7 @@ SQLITE_API int sqlite3_exec( #define SQLITE_OPEN_DELETEONCLOSE 0x00000008 /* VFS only */ #define SQLITE_OPEN_EXCLUSIVE 0x00000010 /* VFS only */ #define SQLITE_OPEN_AUTOPROXY 0x00000020 /* VFS only */ +#define SQLITE_OPEN_URI 0x00000040 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_MAIN_DB 0x00000100 /* VFS only */ #define SQLITE_OPEN_TEMP_DB 0x00000200 /* VFS only */ #define SQLITE_OPEN_TRANSIENT_DB 0x00000400 /* VFS only */ @@ -1030,6 +1036,8 @@ SQLITE_API int sqlite3_exec( #define SQLITE_OPEN_PRIVATECACHE 0x00040000 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_WAL 0x00080000 /* VFS only */ +/* Reserved: 0x00F00000 */ + /* ** CAPI3REF: Device Characteristics ** @@ -1125,17 +1133,18 @@ struct sqlite3_file { /* ** CAPI3REF: OS Interface File Virtual Methods Object ** -** Every file opened by the [sqlite3_vfs] xOpen method populates an +** Every file opened by the [sqlite3_vfs.xOpen] method populates an ** [sqlite3_file] object (or, more commonly, a subclass of the ** [sqlite3_file] object) with a pointer to an instance of this object. ** This object defines the methods used to perform various operations ** against the open file represented by the [sqlite3_file] object. ** -** If the xOpen method sets the sqlite3_file.pMethods element +** If the [sqlite3_vfs.xOpen] method sets the sqlite3_file.pMethods element ** to a non-NULL pointer, then the sqlite3_io_methods.xClose method -** may be invoked even if the xOpen reported that it failed. The -** only way to prevent a call to xClose following a failed xOpen -** is for the xOpen to set the sqlite3_file.pMethods element to NULL. +** may be invoked even if the [sqlite3_vfs.xOpen] reported that it failed. The +** only way to prevent a call to xClose following a failed [sqlite3_vfs.xOpen] +** is for the [sqlite3_vfs.xOpen] to set the sqlite3_file.pMethods element +** to NULL. ** ** The flags argument to xSync may be one of [SQLITE_SYNC_NORMAL] or ** [SQLITE_SYNC_FULL]. The first choice is the normal fsync(). @@ -1169,7 +1178,9 @@ struct sqlite3_file { ** core reserves all opcodes less than 100 for its own use. ** A [SQLITE_FCNTL_LOCKSTATE | list of opcodes] less than 100 is available. ** Applications that define a custom xFileControl method should use opcodes -** greater than 100 to avoid conflicts. +** greater than 100 to avoid conflicts. VFS implementations should +** return [SQLITE_NOTFOUND] for file control opcodes that they do not +** recognize. ** ** The xSectorSize() method returns the sector size of the ** device that underlies the file. The sector size is the @@ -1262,6 +1273,21 @@ struct sqlite3_io_methods { ** for the nominated database. Allocating database file space in large ** chunks (say 1MB at a time), may reduce file-system fragmentation and ** improve performance on some systems. +** +** The [SQLITE_FCNTL_FILE_POINTER] opcode is used to obtain a pointer +** to the [sqlite3_file] object associated with a particular database +** connection. See the [sqlite3_file_control()] documentation for +** additional information. +** +** ^(The [SQLITE_FCNTL_SYNC_OMITTED] opcode is generated internally by +** SQLite and sent to all VFSes in place of a call to the xSync method +** when the database connection has [PRAGMA synchronous] set to OFF.)^ +** Some specialized VFSes need this signal in order to operate correctly +** when [PRAGMA synchronous | PRAGMA synchronous=OFF] is set, but most +** VFSes do not need this signal and should silently ignore this opcode. +** Applications should not call [sqlite3_file_control()] with this +** opcode as doing so may disrupt the operation of the specialized VFSes +** that do require it. */ #define SQLITE_FCNTL_LOCKSTATE 1 #define SQLITE_GET_LOCKPROXYFILE 2 @@ -1270,6 +1296,7 @@ struct sqlite3_io_methods { #define SQLITE_FCNTL_SIZE_HINT 5 #define SQLITE_FCNTL_CHUNK_SIZE 6 #define SQLITE_FCNTL_FILE_POINTER 7 +#define SQLITE_FCNTL_SYNC_OMITTED 8 /* @@ -1289,7 +1316,8 @@ typedef struct sqlite3_mutex sqlite3_mutex; ** ** An instance of the sqlite3_vfs object defines the interface between ** the SQLite core and the underlying operating system. The "vfs" -** in the name of the object stands for "virtual file system". +** in the name of the object stands for "virtual file system". See +** the [VFS | VFS documentation] for further information. ** ** The value of the iVersion field is initially 1 but may be larger in ** future versions of SQLite. Additional fields may be appended to this @@ -1318,6 +1346,7 @@ typedef struct sqlite3_mutex sqlite3_mutex; ** The zName field holds the name of the VFS module. The name must ** be unique across all VFS modules. ** +** [[sqlite3_vfs.xOpen]] ** ^SQLite guarantees that the zFilename parameter to xOpen ** is either a NULL pointer or string obtained ** from xFullPathname() with an optional suffix added. @@ -1395,6 +1424,7 @@ typedef struct sqlite3_mutex sqlite3_mutex; ** element will be valid after xOpen returns regardless of the success ** or failure of the xOpen call. ** +** [[sqlite3_vfs.xAccess]] ** ^The flags argument to xAccess() may be [SQLITE_ACCESS_EXISTS] ** to test for the existence of a file, or [SQLITE_ACCESS_READWRITE] to ** test whether a file is readable and writable, or [SQLITE_ACCESS_READ] @@ -1419,16 +1449,29 @@ typedef struct sqlite3_mutex sqlite3_mutex; ** method returns a Julian Day Number for the current date and time as ** a floating point value. ** ^The xCurrentTimeInt64() method returns, as an integer, the Julian -** Day Number multipled by 86400000 (the number of milliseconds in +** Day Number multiplied by 86400000 (the number of milliseconds in ** a 24-hour day). ** ^SQLite will use the xCurrentTimeInt64() method to get the current ** date and time if that method is available (if iVersion is 2 or ** greater and the function pointer is not NULL) and will fall back ** to xCurrentTime() if xCurrentTimeInt64() is unavailable. +** +** ^The xSetSystemCall(), xGetSystemCall(), and xNestSystemCall() interfaces +** are not used by the SQLite core. These optional interfaces are provided +** by some VFSes to facilitate testing of the VFS code. By overriding +** system calls with functions under its control, a test program can +** simulate faults and error conditions that would otherwise be difficult +** or impossible to induce. The set of system calls that can be overridden +** varies from one VFS to another, and from one version of the same VFS to the +** next. Applications that use these interfaces must be prepared for any +** or all of these interfaces to be NULL or for their behavior to change +** from one release to the next. Applications must not attempt to access +** any of these methods if the iVersion of the VFS is less than 3. */ typedef struct sqlite3_vfs sqlite3_vfs; +typedef void (*sqlite3_syscall_ptr)(void); struct sqlite3_vfs { - int iVersion; /* Structure version number (currently 2) */ + int iVersion; /* Structure version number (currently 3) */ int szOsFile; /* Size of subclassed sqlite3_file */ int mxPathname; /* Maximum file pathname length */ sqlite3_vfs *pNext; /* Next registered VFS */ @@ -1454,6 +1497,13 @@ struct sqlite3_vfs { int (*xCurrentTimeInt64)(sqlite3_vfs*, sqlite3_int64*); /* ** The methods above are in versions 1 and 2 of the sqlite_vfs object. + ** Those below are for version 3 and greater. + */ + int (*xSetSystemCall)(sqlite3_vfs*, const char *zName, sqlite3_syscall_ptr); + sqlite3_syscall_ptr (*xGetSystemCall)(sqlite3_vfs*, const char *zName); + const char *(*xNextSystemCall)(sqlite3_vfs*, const char *zName); + /* + ** The methods above are in versions 1 through 3 of the sqlite_vfs object. ** New fields may be appended in figure versions. The iVersion ** value will increment whenever this happens. */ @@ -1621,9 +1671,9 @@ SQLITE_API int sqlite3_os_end(void); ** implementation of an application-defined [sqlite3_os_init()]. ** ** The first argument to sqlite3_config() is an integer -** [SQLITE_CONFIG_SINGLETHREAD | configuration option] that determines +** [configuration option] that determines ** what property of SQLite is to be configured. Subsequent arguments -** vary depending on the [SQLITE_CONFIG_SINGLETHREAD | configuration option] +** vary depending on the [configuration option] ** in the first argument. ** ** ^When a configuration option is set, sqlite3_config() returns [SQLITE_OK]. @@ -1638,17 +1688,12 @@ SQLITE_API int sqlite3_config(int, ...); ** The sqlite3_db_config() interface is used to make configuration ** changes to a [database connection]. The interface is similar to ** [sqlite3_config()] except that the changes apply to a single -** [database connection] (specified in the first argument). The -** sqlite3_db_config() interface should only be used immediately after -** the database connection is created using [sqlite3_open()], -** [sqlite3_open16()], or [sqlite3_open_v2()]. +** [database connection] (specified in the first argument). ** ** The second argument to sqlite3_db_config(D,V,...) is the -** configuration verb - an integer code that indicates what -** aspect of the [database connection] is being configured. -** The only choice for this value is [SQLITE_DBCONFIG_LOOKASIDE]. -** New verbs are likely to be added in future releases of SQLite. -** Additional arguments depend on the verb. +** [SQLITE_DBCONFIG_LOOKASIDE | configuration verb] - an integer code +** that indicates what aspect of the [database connection] is being configured. +** Subsequent arguments vary depending on the configuration verb. ** ** ^Calls to sqlite3_db_config() return SQLITE_OK if and only if ** the call is considered successful. @@ -1738,6 +1783,7 @@ struct sqlite3_mem_methods { /* ** CAPI3REF: Configuration Options +** KEYWORDS: {configuration option} ** ** These constants are the available integer configuration options that ** can be passed as the first argument to the [sqlite3_config()] interface. @@ -1750,7 +1796,7 @@ struct sqlite3_mem_methods { ** is invoked. ** ** <dl> -** <dt>SQLITE_CONFIG_SINGLETHREAD</dt> +** [[SQLITE_CONFIG_SINGLETHREAD]] <dt>SQLITE_CONFIG_SINGLETHREAD</dt> ** <dd>There are no arguments to this option. ^This option sets the ** [threading mode] to Single-thread. In other words, it disables ** all mutexing and puts SQLite into a mode where it can only be used @@ -1761,7 +1807,7 @@ struct sqlite3_mem_methods { ** [SQLITE_ERROR] if called with the SQLITE_CONFIG_SINGLETHREAD ** configuration option.</dd> ** -** <dt>SQLITE_CONFIG_MULTITHREAD</dt> +** [[SQLITE_CONFIG_MULTITHREAD]] <dt>SQLITE_CONFIG_MULTITHREAD</dt> ** <dd>There are no arguments to this option. ^This option sets the ** [threading mode] to Multi-thread. In other words, it disables ** mutexing on [database connection] and [prepared statement] objects. @@ -1775,7 +1821,7 @@ struct sqlite3_mem_methods { ** [sqlite3_config()] will return [SQLITE_ERROR] if called with the ** SQLITE_CONFIG_MULTITHREAD configuration option.</dd> ** -** <dt>SQLITE_CONFIG_SERIALIZED</dt> +** [[SQLITE_CONFIG_SERIALIZED]] <dt>SQLITE_CONFIG_SERIALIZED</dt> ** <dd>There are no arguments to this option. ^This option sets the ** [threading mode] to Serialized. In other words, this option enables ** all mutexes including the recursive @@ -1791,7 +1837,7 @@ struct sqlite3_mem_methods { ** [sqlite3_config()] will return [SQLITE_ERROR] if called with the ** SQLITE_CONFIG_SERIALIZED configuration option.</dd> ** -** <dt>SQLITE_CONFIG_MALLOC</dt> +** [[SQLITE_CONFIG_MALLOC]] <dt>SQLITE_CONFIG_MALLOC</dt> ** <dd> ^(This option takes a single argument which is a pointer to an ** instance of the [sqlite3_mem_methods] structure. The argument specifies ** alternative low-level memory allocation routines to be used in place of @@ -1799,7 +1845,7 @@ struct sqlite3_mem_methods { ** its own private copy of the content of the [sqlite3_mem_methods] structure ** before the [sqlite3_config()] call returns.</dd> ** -** <dt>SQLITE_CONFIG_GETMALLOC</dt> +** [[SQLITE_CONFIG_GETMALLOC]] <dt>SQLITE_CONFIG_GETMALLOC</dt> ** <dd> ^(This option takes a single argument which is a pointer to an ** instance of the [sqlite3_mem_methods] structure. The [sqlite3_mem_methods] ** structure is filled with the currently defined memory allocation routines.)^ @@ -1807,7 +1853,7 @@ struct sqlite3_mem_methods { ** routines with a wrapper that simulations memory allocation failure or ** tracks memory usage, for example. </dd> ** -** <dt>SQLITE_CONFIG_MEMSTATUS</dt> +** [[SQLITE_CONFIG_MEMSTATUS]] <dt>SQLITE_CONFIG_MEMSTATUS</dt> ** <dd> ^This option takes single argument of type int, interpreted as a ** boolean, which enables or disables the collection of memory allocation ** statistics. ^(When memory allocation statistics are disabled, the @@ -1823,10 +1869,10 @@ struct sqlite3_mem_methods { ** allocation statistics are disabled by default. ** </dd> ** -** <dt>SQLITE_CONFIG_SCRATCH</dt> +** [[SQLITE_CONFIG_SCRATCH]] <dt>SQLITE_CONFIG_SCRATCH</dt> ** <dd> ^This option specifies a static memory buffer that SQLite can use for ** scratch memory. There are three arguments: A pointer an 8-byte -** aligned memory buffer from which the scrach allocations will be +** aligned memory buffer from which the scratch allocations will be ** drawn, the size of each scratch allocation (sz), ** and the maximum number of scratch allocations (N). The sz ** argument must be a multiple of 16. @@ -1839,9 +1885,9 @@ struct sqlite3_mem_methods { ** scratch memory beyond what is provided by this configuration option, then ** [sqlite3_malloc()] will be used to obtain the memory needed.</dd> ** -** <dt>SQLITE_CONFIG_PAGECACHE</dt> +** [[SQLITE_CONFIG_PAGECACHE]] <dt>SQLITE_CONFIG_PAGECACHE</dt> ** <dd> ^This option specifies a static memory buffer that SQLite can use for -** the database page cache with the default page cache implemenation. +** the database page cache with the default page cache implementation. ** This configuration should not be used if an application-define page ** cache implementation is loaded using the SQLITE_CONFIG_PCACHE option. ** There are three arguments to this option: A pointer to 8-byte aligned @@ -1860,7 +1906,7 @@ struct sqlite3_mem_methods { ** be aligned to an 8-byte boundary or subsequent behavior of SQLite ** will be undefined.</dd> ** -** <dt>SQLITE_CONFIG_HEAP</dt> +** [[SQLITE_CONFIG_HEAP]] <dt>SQLITE_CONFIG_HEAP</dt> ** <dd> ^This option specifies a static memory buffer that SQLite will use ** for all of its dynamic memory allocation needs beyond those provided ** for by [SQLITE_CONFIG_SCRATCH] and [SQLITE_CONFIG_PAGECACHE]. @@ -1873,9 +1919,11 @@ struct sqlite3_mem_methods { ** [SQLITE_ENABLE_MEMSYS5] are defined, then the alternative memory ** allocator is engaged to handle all of SQLites memory allocation needs. ** The first pointer (the memory pointer) must be aligned to an 8-byte -** boundary or subsequent behavior of SQLite will be undefined.</dd> +** boundary or subsequent behavior of SQLite will be undefined. +** The minimum allocation size is capped at 2^12. Reasonable values +** for the minimum allocation size are 2^5 through 2^8.</dd> ** -** <dt>SQLITE_CONFIG_MUTEX</dt> +** [[SQLITE_CONFIG_MUTEX]] <dt>SQLITE_CONFIG_MUTEX</dt> ** <dd> ^(This option takes a single argument which is a pointer to an ** instance of the [sqlite3_mutex_methods] structure. The argument specifies ** alternative low-level mutex routines to be used in place @@ -1887,7 +1935,7 @@ struct sqlite3_mem_methods { ** [sqlite3_config()] with the SQLITE_CONFIG_MUTEX configuration option will ** return [SQLITE_ERROR].</dd> ** -** <dt>SQLITE_CONFIG_GETMUTEX</dt> +** [[SQLITE_CONFIG_GETMUTEX]] <dt>SQLITE_CONFIG_GETMUTEX</dt> ** <dd> ^(This option takes a single argument which is a pointer to an ** instance of the [sqlite3_mutex_methods] structure. The ** [sqlite3_mutex_methods] @@ -1900,7 +1948,7 @@ struct sqlite3_mem_methods { ** [sqlite3_config()] with the SQLITE_CONFIG_GETMUTEX configuration option will ** return [SQLITE_ERROR].</dd> ** -** <dt>SQLITE_CONFIG_LOOKASIDE</dt> +** [[SQLITE_CONFIG_LOOKASIDE]] <dt>SQLITE_CONFIG_LOOKASIDE</dt> ** <dd> ^(This option takes two arguments that determine the default ** memory allocation for the lookaside memory allocator on each ** [database connection]. The first argument is the @@ -1910,18 +1958,18 @@ struct sqlite3_mem_methods { ** verb to [sqlite3_db_config()] can be used to change the lookaside ** configuration on individual connections.)^ </dd> ** -** <dt>SQLITE_CONFIG_PCACHE</dt> +** [[SQLITE_CONFIG_PCACHE]] <dt>SQLITE_CONFIG_PCACHE</dt> ** <dd> ^(This option takes a single argument which is a pointer to ** an [sqlite3_pcache_methods] object. This object specifies the interface ** to a custom page cache implementation.)^ ^SQLite makes a copy of the ** object and uses it for page cache memory allocations.</dd> ** -** <dt>SQLITE_CONFIG_GETPCACHE</dt> +** [[SQLITE_CONFIG_GETPCACHE]] <dt>SQLITE_CONFIG_GETPCACHE</dt> ** <dd> ^(This option takes a single argument which is a pointer to an ** [sqlite3_pcache_methods] object. SQLite copies of the current ** page cache implementation into that object.)^ </dd> ** -** <dt>SQLITE_CONFIG_LOG</dt> +** [[SQLITE_CONFIG_LOG]] <dt>SQLITE_CONFIG_LOG</dt> ** <dd> ^The SQLITE_CONFIG_LOG option takes two arguments: a pointer to a ** function with a call signature of void(*)(void*,int,const char*), ** and a pointer to void. ^If the function pointer is not NULL, it is @@ -1939,6 +1987,18 @@ struct sqlite3_mem_methods { ** In a multi-threaded application, the application-defined logger ** function must be threadsafe. </dd> ** +** [[SQLITE_CONFIG_URI]] <dt>SQLITE_CONFIG_URI +** <dd> This option takes a single argument of type int. If non-zero, then +** URI handling is globally enabled. If the parameter is zero, then URI handling +** is globally disabled. If URI handling is globally enabled, all filenames +** passed to [sqlite3_open()], [sqlite3_open_v2()], [sqlite3_open16()] or +** specified as part of [ATTACH] commands are interpreted as URIs, regardless +** of whether or not the [SQLITE_OPEN_URI] flag is set when the database +** connection is opened. If it is globally disabled, filenames are +** only interpreted as URIs if the SQLITE_OPEN_URI flag is set when the +** database connection is opened. By default, URI handling is globally +** disabled. The default value may be changed by compiling with the +** [SQLITE_USE_URI] symbol defined. ** </dl> */ #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ @@ -1957,6 +2017,7 @@ struct sqlite3_mem_methods { #define SQLITE_CONFIG_PCACHE 14 /* sqlite3_pcache_methods* */ #define SQLITE_CONFIG_GETPCACHE 15 /* sqlite3_pcache_methods* */ #define SQLITE_CONFIG_LOG 16 /* xFunc, void* */ +#define SQLITE_CONFIG_URI 17 /* int */ /* ** CAPI3REF: Database Connection Configuration Options @@ -1976,7 +2037,7 @@ struct sqlite3_mem_methods { ** <dd> ^This option takes three additional arguments that determine the ** [lookaside memory allocator] configuration for the [database connection]. ** ^The first argument (the third parameter to [sqlite3_db_config()] is a -** pointer to an memory buffer to use for lookaside memory. +** pointer to a memory buffer to use for lookaside memory. ** ^The first argument after the SQLITE_DBCONFIG_LOOKASIDE verb ** may be NULL in which case SQLite will allocate the ** lookaside buffer itself using [sqlite3_malloc()]. ^The second argument is the @@ -1994,9 +2055,31 @@ struct sqlite3_mem_methods { ** memory is in use leaves the configuration unchanged and returns ** [SQLITE_BUSY].)^</dd> ** +** <dt>SQLITE_DBCONFIG_ENABLE_FKEY</dt> +** <dd> ^This option is used to enable or disable the enforcement of +** [foreign key constraints]. There should be two additional arguments. +** The first argument is an integer which is 0 to disable FK enforcement, +** positive to enable FK enforcement or negative to leave FK enforcement +** unchanged. The second parameter is a pointer to an integer into which +** is written 0 or 1 to indicate whether FK enforcement is off or on +** following this call. The second parameter may be a NULL pointer, in +** which case the FK enforcement setting is not reported back. </dd> +** +** <dt>SQLITE_DBCONFIG_ENABLE_TRIGGER</dt> +** <dd> ^This option is used to enable or disable [CREATE TRIGGER | triggers]. +** There should be two additional arguments. +** The first argument is an integer which is 0 to disable triggers, +** positive to enable triggers or negative to leave the setting unchanged. +** The second parameter is a pointer to an integer into which +** is written 0 or 1 to indicate whether triggers are disabled or enabled +** following this call. The second parameter may be a NULL pointer, in +** which case the trigger setting is not reported back. </dd> +** ** </dl> */ -#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ +#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ +#define SQLITE_DBCONFIG_ENABLE_FKEY 1002 /* int int* */ +#define SQLITE_DBCONFIG_ENABLE_TRIGGER 1003 /* int int* */ /* @@ -2020,13 +2103,17 @@ SQLITE_API int sqlite3_extended_result_codes(sqlite3*, int onoff); ** ** ^This routine returns the [rowid] of the most recent ** successful [INSERT] into the database from the [database connection] -** in the first argument. ^If no successful [INSERT]s +** in the first argument. ^As of SQLite version 3.7.7, this routines +** records the last insert rowid of both ordinary tables and [virtual tables]. +** ^If no successful [INSERT]s ** have ever occurred on that database connection, zero is returned. ** -** ^(If an [INSERT] occurs within a trigger, then the [rowid] of the inserted -** row is returned by this routine as long as the trigger is running. -** But once the trigger terminates, the value returned by this routine -** reverts to the last value inserted before the trigger fired.)^ +** ^(If an [INSERT] occurs within a trigger or within a [virtual table] +** method, then this routine will return the [rowid] of the inserted +** row as long as the trigger or virtual table method is running. +** But once the trigger or virtual table method ends, the value returned +** by this routine reverts to what it was before the trigger or virtual +** table method began.)^ ** ** ^An [INSERT] that fails due to a constraint violation is not a ** successful [INSERT] and does not change the value returned by this @@ -2389,7 +2476,7 @@ SQLITE_API void sqlite3_free_table(char **result); ** NULL pointer if [sqlite3_malloc()] is unable to allocate enough ** memory to hold the resulting string. ** -** ^(In sqlite3_snprintf() routine is similar to "snprintf()" from +** ^(The sqlite3_snprintf() routine is similar to "snprintf()" from ** the standard C library. The result is written into the ** buffer supplied as the second parameter whose size is given by ** the first parameter. Note that the order of the @@ -2408,6 +2495,8 @@ SQLITE_API void sqlite3_free_table(char **result); ** the zero terminator. So the longest string that can be completely ** written will be n-1 characters. ** +** ^The sqlite3_vsnprintf() routine is a varargs version of sqlite3_snprintf(). +** ** These routines all implement some additional formatting ** options that are useful for constructing SQL statements. ** All of the usual printf() formatting options apply. In addition, there @@ -2471,6 +2560,7 @@ SQLITE_API void sqlite3_free_table(char **result); SQLITE_API char *sqlite3_mprintf(const char*,...); SQLITE_API char *sqlite3_vmprintf(const char*, va_list); SQLITE_API char *sqlite3_snprintf(int,char*,const char*, ...); +SQLITE_API char *sqlite3_vsnprintf(int,char*,const char*, va_list); /* ** CAPI3REF: Memory Allocation Subsystem @@ -2595,7 +2685,7 @@ SQLITE_API void sqlite3_randomness(int N, void *P); /* ** CAPI3REF: Compile-Time Authorization Callbacks ** -** ^This routine registers a authorizer callback with a particular +** ^This routine registers an authorizer callback with a particular ** [database connection], supplied in the first argument. ** ^The authorizer callback is invoked as SQL statements are being compiled ** by [sqlite3_prepare()] or its variants [sqlite3_prepare_v2()], @@ -2686,6 +2776,9 @@ SQLITE_API int sqlite3_set_authorizer( ** to signal SQLite whether or not the action is permitted. See the ** [sqlite3_set_authorizer | authorizer documentation] for additional ** information. +** +** Note that SQLITE_IGNORE is also used as a [SQLITE_ROLLBACK | return code] +** from the [sqlite3_vtab_on_conflict()] interface. */ #define SQLITE_DENY 1 /* Abort the SQL statement with an error */ #define SQLITE_IGNORE 2 /* Don't allow access, but don't generate an error */ @@ -2808,7 +2901,7 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); /* ** CAPI3REF: Opening A New Database Connection ** -** ^These routines open an SQLite database file whose name is given by the +** ^These routines open an SQLite database file as specified by the ** filename argument. ^The filename argument is interpreted as UTF-8 for ** sqlite3_open() and sqlite3_open_v2() and as UTF-16 in the native byte ** order for sqlite3_open16(). ^(A [database connection] handle is usually @@ -2835,7 +2928,7 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); ** sqlite3_open_v2() can take one of ** the following three values, optionally combined with the ** [SQLITE_OPEN_NOMUTEX], [SQLITE_OPEN_FULLMUTEX], [SQLITE_OPEN_SHAREDCACHE], -** and/or [SQLITE_OPEN_PRIVATECACHE] flags:)^ +** [SQLITE_OPEN_PRIVATECACHE], and/or [SQLITE_OPEN_URI] flags:)^ ** ** <dl> ** ^(<dt>[SQLITE_OPEN_READONLY]</dt> @@ -2848,15 +2941,14 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); ** case the database must already exist, otherwise an error is returned.</dd>)^ ** ** ^(<dt>[SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]</dt> -** <dd>The database is opened for reading and writing, and is creates it if +** <dd>The database is opened for reading and writing, and is created if ** it does not already exist. This is the behavior that is always used for ** sqlite3_open() and sqlite3_open16().</dd>)^ ** </dl> ** ** If the 3rd parameter to sqlite3_open_v2() is not one of the -** combinations shown above or one of the combinations shown above combined -** with the [SQLITE_OPEN_NOMUTEX], [SQLITE_OPEN_FULLMUTEX], -** [SQLITE_OPEN_SHAREDCACHE] and/or [SQLITE_OPEN_PRIVATECACHE] flags, +** combinations shown above optionally combined with other +** [SQLITE_OPEN_READONLY | SQLITE_OPEN_* bits] ** then the behavior is undefined. ** ** ^If the [SQLITE_OPEN_NOMUTEX] flag is set, then the database connection @@ -2871,6 +2963,11 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); ** [SQLITE_OPEN_PRIVATECACHE] flag causes the database connection to not ** participate in [shared cache mode] even if it is enabled. ** +** ^The fourth parameter to sqlite3_open_v2() is the name of the +** [sqlite3_vfs] object that defines the operating system interface that +** the new database connection should use. ^If the fourth parameter is +** a NULL pointer then the default [sqlite3_vfs] object is used. +** ** ^If the filename is ":memory:", then a private, temporary in-memory database ** is created for the connection. ^This in-memory database will vanish when ** the database connection is closed. Future versions of SQLite might @@ -2883,10 +2980,111 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); ** on-disk database will be created. ^This private database will be ** automatically deleted as soon as the database connection is closed. ** -** ^The fourth parameter to sqlite3_open_v2() is the name of the -** [sqlite3_vfs] object that defines the operating system interface that -** the new database connection should use. ^If the fourth parameter is -** a NULL pointer then the default [sqlite3_vfs] object is used. +** [[URI filenames in sqlite3_open()]] <h3>URI Filenames</h3> +** +** ^If [URI filename] interpretation is enabled, and the filename argument +** begins with "file:", then the filename is interpreted as a URI. ^URI +** filename interpretation is enabled if the [SQLITE_OPEN_URI] flag is +** set in the fourth argument to sqlite3_open_v2(), or if it has +** been enabled globally using the [SQLITE_CONFIG_URI] option with the +** [sqlite3_config()] method or by the [SQLITE_USE_URI] compile-time option. +** As of SQLite version 3.7.7, URI filename interpretation is turned off +** by default, but future releases of SQLite might enable URI filename +** interpretation by default. See "[URI filenames]" for additional +** information. +** +** URI filenames are parsed according to RFC 3986. ^If the URI contains an +** authority, then it must be either an empty string or the string +** "localhost". ^If the authority is not an empty string or "localhost", an +** error is returned to the caller. ^The fragment component of a URI, if +** present, is ignored. +** +** ^SQLite uses the path component of the URI as the name of the disk file +** which contains the database. ^If the path begins with a '/' character, +** then it is interpreted as an absolute path. ^If the path does not begin +** with a '/' (meaning that the authority section is omitted from the URI) +** then the path is interpreted as a relative path. +** ^On windows, the first component of an absolute path +** is a drive specification (e.g. "C:"). +** +** [[core URI query parameters]] +** The query component of a URI may contain parameters that are interpreted +** either by SQLite itself, or by a [VFS | custom VFS implementation]. +** SQLite interprets the following three query parameters: +** +** <ul> +** <li> <b>vfs</b>: ^The "vfs" parameter may be used to specify the name of +** a VFS object that provides the operating system interface that should +** be used to access the database file on disk. ^If this option is set to +** an empty string the default VFS object is used. ^Specifying an unknown +** VFS is an error. ^If sqlite3_open_v2() is used and the vfs option is +** present, then the VFS specified by the option takes precedence over +** the value passed as the fourth parameter to sqlite3_open_v2(). +** +** <li> <b>mode</b>: ^(The mode parameter may be set to either "ro", "rw" or +** "rwc". Attempting to set it to any other value is an error)^. +** ^If "ro" is specified, then the database is opened for read-only +** access, just as if the [SQLITE_OPEN_READONLY] flag had been set in the +** third argument to sqlite3_prepare_v2(). ^If the mode option is set to +** "rw", then the database is opened for read-write (but not create) +** access, as if SQLITE_OPEN_READWRITE (but not SQLITE_OPEN_CREATE) had +** been set. ^Value "rwc" is equivalent to setting both +** SQLITE_OPEN_READWRITE and SQLITE_OPEN_CREATE. ^If sqlite3_open_v2() is +** used, it is an error to specify a value for the mode parameter that is +** less restrictive than that specified by the flags passed as the third +** parameter. +** +** <li> <b>cache</b>: ^The cache parameter may be set to either "shared" or +** "private". ^Setting it to "shared" is equivalent to setting the +** SQLITE_OPEN_SHAREDCACHE bit in the flags argument passed to +** sqlite3_open_v2(). ^Setting the cache parameter to "private" is +** equivalent to setting the SQLITE_OPEN_PRIVATECACHE bit. +** ^If sqlite3_open_v2() is used and the "cache" parameter is present in +** a URI filename, its value overrides any behaviour requested by setting +** SQLITE_OPEN_PRIVATECACHE or SQLITE_OPEN_SHAREDCACHE flag. +** </ul> +** +** ^Specifying an unknown parameter in the query component of a URI is not an +** error. Future versions of SQLite might understand additional query +** parameters. See "[query parameters with special meaning to SQLite]" for +** additional information. +** +** [[URI filename examples]] <h3>URI filename examples</h3> +** +** <table border="1" align=center cellpadding=5> +** <tr><th> URI filenames <th> Results +** <tr><td> file:data.db <td> +** Open the file "data.db" in the current directory. +** <tr><td> file:/home/fred/data.db<br> +** file:///home/fred/data.db <br> +** file://localhost/home/fred/data.db <br> <td> +** Open the database file "/home/fred/data.db". +** <tr><td> file://darkstar/home/fred/data.db <td> +** An error. "darkstar" is not a recognized authority. +** <tr><td style="white-space:nowrap"> +** file:///C:/Documents%20and%20Settings/fred/Desktop/data.db +** <td> Windows only: Open the file "data.db" on fred's desktop on drive +** C:. Note that the %20 escaping in this example is not strictly +** necessary - space characters can be used literally +** in URI filenames. +** <tr><td> file:data.db?mode=ro&cache=private <td> +** Open file "data.db" in the current directory for read-only access. +** Regardless of whether or not shared-cache mode is enabled by +** default, use a private cache. +** <tr><td> file:/home/fred/data.db?vfs=unix-nolock <td> +** Open file "/home/fred/data.db". Use the special VFS "unix-nolock". +** <tr><td> file:data.db?mode=readonly <td> +** An error. "readonly" is not a valid option for the "mode" parameter. +** </table> +** +** ^URI hexadecimal escape sequences (%HH) are supported within the path and +** query components of a URI. A hexadecimal escape sequence consists of a +** percent sign - "%" - followed by exactly two hexadecimal digits +** specifying an octet value. ^Before the path or query components of a +** URI filename are interpreted, they are encoded using UTF-8 and all +** hexadecimal escape sequences replaced by a single byte containing the +** corresponding octet. If this process generates an invalid UTF-8 encoding, +** the results are undefined. ** ** <b>Note to Windows users:</b> The encoding used for the filename argument ** of sqlite3_open() and sqlite3_open_v2() must be UTF-8, not whatever @@ -2910,6 +3108,26 @@ SQLITE_API int sqlite3_open_v2( ); /* +** CAPI3REF: Obtain Values For URI Parameters +** +** This is a utility routine, useful to VFS implementations, that checks +** to see if a database file was a URI that contained a specific query +** parameter, and if so obtains the value of the query parameter. +** +** The zFilename argument is the filename pointer passed into the xOpen() +** method of a VFS implementation. The zParam argument is the name of the +** query parameter we seek. This routine returns the value of the zParam +** parameter if it exists. If the parameter does not exist, this routine +** returns a NULL pointer. +** +** If the zFilename argument to this function is not a pointer that SQLite +** passed into the xOpen VFS method, then the behavior of this routine +** is undefined and probably undesirable. +*/ +SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam); + + +/* ** CAPI3REF: Error Codes And Messages ** ** ^The sqlite3_errcode() interface returns the numeric [result code] or @@ -3024,43 +3242,45 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); ** Additional information is available at [limits | Limits in SQLite]. ** ** <dl> -** ^(<dt>SQLITE_LIMIT_LENGTH</dt> +** [[SQLITE_LIMIT_LENGTH]] ^(<dt>SQLITE_LIMIT_LENGTH</dt> ** <dd>The maximum size of any string or BLOB or table row, in bytes.<dd>)^ ** -** ^(<dt>SQLITE_LIMIT_SQL_LENGTH</dt> +** [[SQLITE_LIMIT_SQL_LENGTH]] ^(<dt>SQLITE_LIMIT_SQL_LENGTH</dt> ** <dd>The maximum length of an SQL statement, in bytes.</dd>)^ ** -** ^(<dt>SQLITE_LIMIT_COLUMN</dt> +** [[SQLITE_LIMIT_COLUMN]] ^(<dt>SQLITE_LIMIT_COLUMN</dt> ** <dd>The maximum number of columns in a table definition or in the ** result set of a [SELECT] or the maximum number of columns in an index ** or in an ORDER BY or GROUP BY clause.</dd>)^ ** -** ^(<dt>SQLITE_LIMIT_EXPR_DEPTH</dt> +** [[SQLITE_LIMIT_EXPR_DEPTH]] ^(<dt>SQLITE_LIMIT_EXPR_DEPTH</dt> ** <dd>The maximum depth of the parse tree on any expression.</dd>)^ ** -** ^(<dt>SQLITE_LIMIT_COMPOUND_SELECT</dt> +** [[SQLITE_LIMIT_COMPOUND_SELECT]] ^(<dt>SQLITE_LIMIT_COMPOUND_SELECT</dt> ** <dd>The maximum number of terms in a compound SELECT statement.</dd>)^ ** -** ^(<dt>SQLITE_LIMIT_VDBE_OP</dt> +** [[SQLITE_LIMIT_VDBE_OP]] ^(<dt>SQLITE_LIMIT_VDBE_OP</dt> ** <dd>The maximum number of instructions in a virtual machine program ** used to implement an SQL statement. This limit is not currently ** enforced, though that might be added in some future release of ** SQLite.</dd>)^ ** -** ^(<dt>SQLITE_LIMIT_FUNCTION_ARG</dt> +** [[SQLITE_LIMIT_FUNCTION_ARG]] ^(<dt>SQLITE_LIMIT_FUNCTION_ARG</dt> ** <dd>The maximum number of arguments on a function.</dd>)^ ** -** ^(<dt>SQLITE_LIMIT_ATTACHED</dt> +** [[SQLITE_LIMIT_ATTACHED]] ^(<dt>SQLITE_LIMIT_ATTACHED</dt> ** <dd>The maximum number of [ATTACH | attached databases].)^</dd> ** +** [[SQLITE_LIMIT_LIKE_PATTERN_LENGTH]] ** ^(<dt>SQLITE_LIMIT_LIKE_PATTERN_LENGTH</dt> ** <dd>The maximum length of the pattern argument to the [LIKE] or ** [GLOB] operators.</dd>)^ ** +** [[SQLITE_LIMIT_VARIABLE_NUMBER]] ** ^(<dt>SQLITE_LIMIT_VARIABLE_NUMBER</dt> ** <dd>The maximum index number of any [parameter] in an SQL statement.)^ ** -** ^(<dt>SQLITE_LIMIT_TRIGGER_DEPTH</dt> +** [[SQLITE_LIMIT_TRIGGER_DEPTH]] ^(<dt>SQLITE_LIMIT_TRIGGER_DEPTH</dt> ** <dd>The maximum depth of recursion for triggers.</dd>)^ ** </dl> */ @@ -3198,13 +3418,30 @@ SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt); ** CAPI3REF: Determine If An SQL Statement Writes The Database ** ** ^The sqlite3_stmt_readonly(X) interface returns true (non-zero) if -** the [prepared statement] X is [SELECT] statement and false (zero) if -** X is an [INSERT], [UPDATE], [DELETE], CREATE, DROP, [ANALYZE], -** [ALTER], or [REINDEX] statement. -** If X is a NULL pointer or any other kind of statement, including but -** not limited to [ATTACH], [DETACH], [COMMIT], [ROLLBACK], [RELEASE], -** [SAVEPOINT], [PRAGMA], or [VACUUM] the result of sqlite3_stmt_readonly(X) is -** undefined. +** and only if the [prepared statement] X makes no direct changes to +** the content of the database file. +** +** Note that [application-defined SQL functions] or +** [virtual tables] might change the database indirectly as a side effect. +** ^(For example, if an application defines a function "eval()" that +** calls [sqlite3_exec()], then the following SQL statement would +** change the database file through side-effects: +** +** <blockquote><pre> +** SELECT eval('DELETE FROM t1') FROM t2; +** </pre></blockquote> +** +** But because the [SELECT] statement does not change the database file +** directly, sqlite3_stmt_readonly() would still return true.)^ +** +** ^Transaction control statements such as [BEGIN], [COMMIT], [ROLLBACK], +** [SAVEPOINT], and [RELEASE] cause sqlite3_stmt_readonly() to return true, +** since the statements themselves do not actually modify the database but +** rather they control the timing of when other statements modify the +** database. ^The [ATTACH] and [DETACH] statements also cause +** sqlite3_stmt_readonly() to return true since, while those statements +** change the configuration of a database connection, they do not make +** changes to the content of the database files on disk. */ SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); @@ -3224,7 +3461,7 @@ SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); ** whether or not it requires a protected sqlite3_value. ** ** The terms "protected" and "unprotected" refer to whether or not -** a mutex is held. A internal mutex is held for a protected +** a mutex is held. An internal mutex is held for a protected ** sqlite3_value object but no mutex is held for an unprotected ** sqlite3_value object. If SQLite is compiled to be single-threaded ** (with [SQLITE_THREADSAFE=0] and with [sqlite3_threadsafe()] returning 0) @@ -3448,7 +3685,9 @@ SQLITE_API int sqlite3_column_count(sqlite3_stmt *pStmt); ** column number. ^The leftmost column is number 0. ** ** ^The returned string pointer is valid until either the [prepared statement] -** is destroyed by [sqlite3_finalize()] or until the next call to +** is destroyed by [sqlite3_finalize()] or until the statement is automatically +** reprepared by the first call to [sqlite3_step()] for a particular run +** or until the next call to ** sqlite3_column_name() or sqlite3_column_name16() on the same column. ** ** ^If sqlite3_malloc() fails during the processing of either routine @@ -3474,7 +3713,9 @@ SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt*, int N); ** the database name, the _table_ routines return the table name, and ** the origin_ routines return the column name. ** ^The returned string is valid until the [prepared statement] is destroyed -** using [sqlite3_finalize()] or until the same information is requested +** using [sqlite3_finalize()] or until the statement is automatically +** reprepared by the first call to [sqlite3_step()] for a particular run +** or until the same information is requested ** again in a different encoding. ** ** ^The names returned are the original un-aliased names of the @@ -3568,7 +3809,7 @@ SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int); ** ^[SQLITE_BUSY] means that the database engine was unable to acquire the ** database locks it needs to do its job. ^If the statement is a [COMMIT] ** or occurs outside of an explicit transaction, then you can retry the -** statement. If the statement is not a [COMMIT] and occurs within a +** statement. If the statement is not a [COMMIT] and occurs within an ** explicit transaction then you should rollback the transaction before ** continuing. ** @@ -3598,13 +3839,17 @@ SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int); ** be the case that the same database connection is being used by two or ** more threads at the same moment in time. ** -** For all versions of SQLite up to and including 3.6.23.1, it was required -** after sqlite3_step() returned anything other than [SQLITE_ROW] that -** [sqlite3_reset()] be called before any subsequent invocation of -** sqlite3_step(). Failure to invoke [sqlite3_reset()] in this way would -** result in an [SQLITE_MISUSE] return from sqlite3_step(). But after -** version 3.6.23.1, sqlite3_step() began calling [sqlite3_reset()] -** automatically in this circumstance rather than returning [SQLITE_MISUSE]. +** For all versions of SQLite up to and including 3.6.23.1, a call to +** [sqlite3_reset()] was required after sqlite3_step() returned anything +** other than [SQLITE_ROW] before any subsequent invocation of +** sqlite3_step(). Failure to reset the prepared statement using +** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from +** sqlite3_step(). But after version 3.6.23.1, sqlite3_step() began +** calling [sqlite3_reset()] automatically in this circumstance rather +** than returning [SQLITE_MISUSE]. This is not considered a compatibility +** break because any application that ever receives an SQLITE_MISUSE error +** is broken by definition. The [SQLITE_OMIT_AUTORESET] compile-time option +** can be used to restore the legacy behavior. ** ** <b>Goofy Interface Alert:</b> In the legacy interface, the sqlite3_step() ** API always returns a generic error code, [SQLITE_ERROR], following any @@ -3843,7 +4088,7 @@ SQLITE_API sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol); ** CAPI3REF: Destroy A Prepared Statement Object ** ** ^The sqlite3_finalize() function is called to delete a [prepared statement]. -** ^If the most recent evaluation of the statement encountered no errors or +** ^If the most recent evaluation of the statement encountered no errors ** or if the statement is never been evaluated, then sqlite3_finalize() returns ** SQLITE_OK. ^If the most recent evaluation of statement S failed, then ** sqlite3_finalize(S) returns the appropriate [error code] or @@ -3902,7 +4147,7 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt); ** are used to add SQL functions or aggregates or to redefine the behavior ** of existing SQL functions or aggregates. The only differences between ** these routines are the text encoding expected for -** the the second parameter (the name of the function being created) +** the second parameter (the name of the function being created) ** and the presence or absence of a destructor callback for ** the application data pointer. ** @@ -3941,16 +4186,16 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt); ** ^(The fifth parameter is an arbitrary pointer. The implementation of the ** function can gain access to this pointer using [sqlite3_user_data()].)^ ** -** ^The seventh, eighth and ninth parameters, xFunc, xStep and xFinal, are +** ^The sixth, seventh and eighth parameters, xFunc, xStep and xFinal, are ** pointers to C-language functions that implement the SQL function or ** aggregate. ^A scalar SQL function requires an implementation of the xFunc ** callback only; NULL pointers must be passed as the xStep and xFinal ** parameters. ^An aggregate SQL function requires an implementation of xStep ** and xFinal and NULL pointer must be passed for xFunc. ^To delete an existing -** SQL function or aggregate, pass NULL poiners for all three function +** SQL function or aggregate, pass NULL pointers for all three function ** callbacks. ** -** ^(If the tenth parameter to sqlite3_create_function_v2() is not NULL, +** ^(If the ninth parameter to sqlite3_create_function_v2() is not NULL, ** then it is destructor for the application data pointer. ** The destructor is invoked when the function is deleted, either by being ** overloaded or when the database connection closes.)^ @@ -4054,7 +4299,7 @@ SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int6 ** The xFunc (for scalar functions) or xStep (for aggregates) parameters ** to [sqlite3_create_function()] and [sqlite3_create_function16()] ** define callbacks that implement the SQL functions and aggregates. -** The 4th parameter to these callbacks is an array of pointers to +** The 3rd parameter to these callbacks is an array of pointers to ** [protected sqlite3_value] objects. There is one [sqlite3_value] object for ** each parameter to the SQL function. These routines are used to ** extract values from the [sqlite3_value] objects. @@ -4381,7 +4626,7 @@ SQLITE_API void sqlite3_result_zeroblob(sqlite3_context*, int n); ** ^The [SQLITE_UTF16_ALIGNED] value for eTextRep forces strings to begin ** on an even byte address. ** -** ^The fourth argument, pArg, is a application data pointer that is passed +** ^The fourth argument, pArg, is an application data pointer that is passed ** through as the first argument to the collating function callback. ** ** ^The fifth argument, xCallback, is a pointer to the collating function. @@ -4397,7 +4642,7 @@ SQLITE_API void sqlite3_result_zeroblob(sqlite3_context*, int n); ** by the eTextRep argument. The collating function must return an ** integer that is negative, zero, or positive ** if the first string is less than, equal to, or greater than the second, -** respectively. A collating function must alway return the same answer +** respectively. A collating function must always return the same answer ** given the same inputs. If two or more collating functions are registered ** to the same collation name (using different eTextRep values) then all ** must give an equivalent answer when invoked with equivalent strings. @@ -4809,7 +5054,7 @@ SQLITE_API int sqlite3_release_memory(int); ** <li> Memory accounting is disabled using a combination of the ** [sqlite3_config]([SQLITE_CONFIG_MEMSTATUS],...) start-time option and ** the [SQLITE_DEFAULT_MEMSTATUS] compile-time option. -** <li> An alternative page cache implementation is specifed using +** <li> An alternative page cache implementation is specified using ** [sqlite3_config]([SQLITE_CONFIG_PCACHE],...). ** <li> The page cache allocates from its own memory pool supplied ** by [sqlite3_config]([SQLITE_CONFIG_PAGECACHE],...) rather than @@ -5030,7 +5275,7 @@ typedef struct sqlite3_module sqlite3_module; ** CAPI3REF: Virtual Table Object ** KEYWORDS: sqlite3_module {virtual table module} ** -** This structure, sometimes called a a "virtual table module", +** This structure, sometimes called a "virtual table module", ** defines the implementation of a [virtual tables]. ** This structure consists mostly of methods for the module. ** @@ -5070,6 +5315,11 @@ struct sqlite3_module { void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), void **ppArg); int (*xRename)(sqlite3_vtab *pVtab, const char *zNew); + /* The methods above are in version 1 of the sqlite_module object. Those + ** below are for version 2 and greater. */ + int (*xSavepoint)(sqlite3_vtab *pVTab, int); + int (*xRelease)(sqlite3_vtab *pVTab, int); + int (*xRollbackTo)(sqlite3_vtab *pVTab, int); }; /* @@ -5342,7 +5592,7 @@ typedef struct sqlite3_blob sqlite3_blob; ** This is true if any column of the row is changed, even a column ** other than the one the BLOB handle is open on.)^ ** ^Calls to [sqlite3_blob_read()] and [sqlite3_blob_write()] for -** a expired BLOB handle fail with an return code of [SQLITE_ABORT]. +** an expired BLOB handle fail with a return code of [SQLITE_ABORT]. ** ^(Changes written into a BLOB prior to the BLOB expiring are not ** rolled back by the expiration of the BLOB. Such changes will eventually ** commit if the transaction continues to completion.)^ @@ -5752,7 +6002,7 @@ struct sqlite3_mutex_methods { ** ** ^If the argument to sqlite3_mutex_held() is a NULL pointer then ** the routine should return 1. This seems counter-intuitive since -** clearly the mutex cannot be held if it does not exist. But the +** clearly the mutex cannot be held if it does not exist. But ** the reason the mutex does not exist is because the build is not ** using mutexes. And we do not want the assert() containing the ** call to sqlite3_mutex_held() to fail, so a non-zero return is @@ -5782,7 +6032,8 @@ SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex*); #define SQLITE_MUTEX_STATIC_OPEN 4 /* sqlite3BtreeOpen() */ #define SQLITE_MUTEX_STATIC_PRNG 5 /* sqlite3_random() */ #define SQLITE_MUTEX_STATIC_LRU 6 /* lru page list */ -#define SQLITE_MUTEX_STATIC_LRU2 7 /* lru page list */ +#define SQLITE_MUTEX_STATIC_LRU2 7 /* NOT USED */ +#define SQLITE_MUTEX_STATIC_PMEM 7 /* sqlite3PageMalloc() */ /* ** CAPI3REF: Retrieve the mutex for a database connection @@ -5874,7 +6125,8 @@ SQLITE_API int sqlite3_test_control(int op, ...); #define SQLITE_TESTCTRL_ISKEYWORD 16 #define SQLITE_TESTCTRL_PGHDRSZ 17 #define SQLITE_TESTCTRL_SCRATCHMALLOC 18 -#define SQLITE_TESTCTRL_LAST 18 +#define SQLITE_TESTCTRL_LOCALTIME_FAULT 19 +#define SQLITE_TESTCTRL_LAST 19 /* ** CAPI3REF: SQLite Runtime Status @@ -5883,7 +6135,7 @@ SQLITE_API int sqlite3_test_control(int op, ...); ** about the performance of SQLite, and optionally to reset various ** highwater marks. ^The first argument is an integer code for ** the specific parameter to measure. ^(Recognized integer codes -** are of the form [SQLITE_STATUS_MEMORY_USED | SQLITE_STATUS_...].)^ +** are of the form [status parameters | SQLITE_STATUS_...].)^ ** ^The current value of the parameter is returned into *pCurrent. ** ^The highest recorded value is returned in *pHighwater. ^If the ** resetFlag is true, then the highest record value is reset after @@ -5910,12 +6162,13 @@ SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetF /* ** CAPI3REF: Status Parameters +** KEYWORDS: {status parameters} ** ** These integer constants designate various run-time status parameters ** that can be returned by [sqlite3_status()]. ** ** <dl> -** ^(<dt>SQLITE_STATUS_MEMORY_USED</dt> +** [[SQLITE_STATUS_MEMORY_USED]] ^(<dt>SQLITE_STATUS_MEMORY_USED</dt> ** <dd>This parameter is the current amount of memory checked out ** using [sqlite3_malloc()], either directly or indirectly. The ** figure includes calls made to [sqlite3_malloc()] by the application @@ -5925,22 +6178,24 @@ SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetF ** this parameter. The amount returned is the sum of the allocation ** sizes as reported by the xSize method in [sqlite3_mem_methods].</dd>)^ ** -** ^(<dt>SQLITE_STATUS_MALLOC_SIZE</dt> +** [[SQLITE_STATUS_MALLOC_SIZE]] ^(<dt>SQLITE_STATUS_MALLOC_SIZE</dt> ** <dd>This parameter records the largest memory allocation request ** handed to [sqlite3_malloc()] or [sqlite3_realloc()] (or their ** internal equivalents). Only the value returned in the ** *pHighwater parameter to [sqlite3_status()] is of interest. ** The value written into the *pCurrent parameter is undefined.</dd>)^ ** -** ^(<dt>SQLITE_STATUS_MALLOC_COUNT</dt> -** <dd>This parameter records the number of separate memory allocations.</dd>)^ +** [[SQLITE_STATUS_MALLOC_COUNT]] ^(<dt>SQLITE_STATUS_MALLOC_COUNT</dt> +** <dd>This parameter records the number of separate memory allocations +** currently checked out.</dd>)^ ** -** ^(<dt>SQLITE_STATUS_PAGECACHE_USED</dt> +** [[SQLITE_STATUS_PAGECACHE_USED]] ^(<dt>SQLITE_STATUS_PAGECACHE_USED</dt> ** <dd>This parameter returns the number of pages used out of the ** [pagecache memory allocator] that was configured using ** [SQLITE_CONFIG_PAGECACHE]. The ** value returned is in pages, not in bytes.</dd>)^ ** +** [[SQLITE_STATUS_PAGECACHE_OVERFLOW]] ** ^(<dt>SQLITE_STATUS_PAGECACHE_OVERFLOW</dt> ** <dd>This parameter returns the number of bytes of page cache ** allocation which could not be satisfied by the [SQLITE_CONFIG_PAGECACHE] @@ -5950,13 +6205,13 @@ SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetF ** [SQLITE_CONFIG_PAGECACHE]) and allocations that overflowed because ** no space was left in the page cache.</dd>)^ ** -** ^(<dt>SQLITE_STATUS_PAGECACHE_SIZE</dt> +** [[SQLITE_STATUS_PAGECACHE_SIZE]] ^(<dt>SQLITE_STATUS_PAGECACHE_SIZE</dt> ** <dd>This parameter records the largest memory allocation request ** handed to [pagecache memory allocator]. Only the value returned in the ** *pHighwater parameter to [sqlite3_status()] is of interest. ** The value written into the *pCurrent parameter is undefined.</dd>)^ ** -** ^(<dt>SQLITE_STATUS_SCRATCH_USED</dt> +** [[SQLITE_STATUS_SCRATCH_USED]] ^(<dt>SQLITE_STATUS_SCRATCH_USED</dt> ** <dd>This parameter returns the number of allocations used out of the ** [scratch memory allocator] configured using ** [SQLITE_CONFIG_SCRATCH]. The value returned is in allocations, not @@ -5964,7 +6219,7 @@ SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetF ** outstanding at time, this parameter also reports the number of threads ** using scratch memory at the same time.</dd>)^ ** -** ^(<dt>SQLITE_STATUS_SCRATCH_OVERFLOW</dt> +** [[SQLITE_STATUS_SCRATCH_OVERFLOW]] ^(<dt>SQLITE_STATUS_SCRATCH_OVERFLOW</dt> ** <dd>This parameter returns the number of bytes of scratch memory ** allocation which could not be satisfied by the [SQLITE_CONFIG_SCRATCH] ** buffer and where forced to overflow to [sqlite3_malloc()]. The values @@ -5974,13 +6229,13 @@ SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetF ** slots were available. ** </dd>)^ ** -** ^(<dt>SQLITE_STATUS_SCRATCH_SIZE</dt> +** [[SQLITE_STATUS_SCRATCH_SIZE]] ^(<dt>SQLITE_STATUS_SCRATCH_SIZE</dt> ** <dd>This parameter records the largest memory allocation request ** handed to [scratch memory allocator]. Only the value returned in the ** *pHighwater parameter to [sqlite3_status()] is of interest. ** The value written into the *pCurrent parameter is undefined.</dd>)^ ** -** ^(<dt>SQLITE_STATUS_PARSER_STACK</dt> +** [[SQLITE_STATUS_PARSER_STACK]] ^(<dt>SQLITE_STATUS_PARSER_STACK</dt> ** <dd>This parameter records the deepest parser stack. It is only ** meaningful if SQLite is compiled with [YYTRACKMAXSTACKDEPTH].</dd>)^ ** </dl> @@ -6005,9 +6260,9 @@ SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetF ** about a single [database connection]. ^The first argument is the ** database connection object to be interrogated. ^The second argument ** is an integer constant, taken from the set of -** [SQLITE_DBSTATUS_LOOKASIDE_USED | SQLITE_DBSTATUS_*] macros, that +** [SQLITE_DBSTATUS options], that ** determines the parameter to interrogate. The set of -** [SQLITE_DBSTATUS_LOOKASIDE_USED | SQLITE_DBSTATUS_*] macros is likely +** [SQLITE_DBSTATUS options] is likely ** to grow in future releases of SQLite. ** ** ^The current value of the requested parameter is written into *pCur @@ -6024,6 +6279,7 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r /* ** CAPI3REF: Status Parameters for database connections +** KEYWORDS: {SQLITE_DBSTATUS options} ** ** These constants are the available integer "verbs" that can be passed as ** the second argument to the [sqlite3_db_status()] interface. @@ -6035,16 +6291,37 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r ** if a discontinued or unsupported verb is invoked. ** ** <dl> -** ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_USED</dt> +** [[SQLITE_DBSTATUS_LOOKASIDE_USED]] ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_USED</dt> ** <dd>This parameter returns the number of lookaside memory slots currently ** checked out.</dd>)^ ** -** ^(<dt>SQLITE_DBSTATUS_CACHE_USED</dt> +** [[SQLITE_DBSTATUS_LOOKASIDE_HIT]] ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_HIT</dt> +** <dd>This parameter returns the number malloc attempts that were +** satisfied using lookaside memory. Only the high-water value is meaningful; +** the current value is always zero.)^ +** +** [[SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE]] +** ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE</dt> +** <dd>This parameter returns the number malloc attempts that might have +** been satisfied using lookaside memory but failed due to the amount of +** memory requested being larger than the lookaside slot size. +** Only the high-water value is meaningful; +** the current value is always zero.)^ +** +** [[SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL]] +** ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL</dt> +** <dd>This parameter returns the number malloc attempts that might have +** been satisfied using lookaside memory but failed due to all lookaside +** memory already being in use. +** Only the high-water value is meaningful; +** the current value is always zero.)^ +** +** [[SQLITE_DBSTATUS_CACHE_USED]] ^(<dt>SQLITE_DBSTATUS_CACHE_USED</dt> ** <dd>This parameter returns the approximate number of of bytes of heap ** memory used by all pager caches associated with the database connection.)^ ** ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_USED is always 0. ** -** ^(<dt>SQLITE_DBSTATUS_SCHEMA_USED</dt> +** [[SQLITE_DBSTATUS_SCHEMA_USED]] ^(<dt>SQLITE_DBSTATUS_SCHEMA_USED</dt> ** <dd>This parameter returns the approximate number of of bytes of heap ** memory used to store the schema for all databases associated ** with the connection - main, temp, and any [ATTACH]-ed databases.)^ @@ -6053,7 +6330,7 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r ** [shared cache mode] being enabled. ** ^The highwater mark associated with SQLITE_DBSTATUS_SCHEMA_USED is always 0. ** -** ^(<dt>SQLITE_DBSTATUS_STMT_USED</dt> +** [[SQLITE_DBSTATUS_STMT_USED]] ^(<dt>SQLITE_DBSTATUS_STMT_USED</dt> ** <dd>This parameter returns the approximate number of of bytes of heap ** and lookaside memory used by all prepared statements associated with ** the database connection.)^ @@ -6061,18 +6338,21 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r ** </dd> ** </dl> */ -#define SQLITE_DBSTATUS_LOOKASIDE_USED 0 -#define SQLITE_DBSTATUS_CACHE_USED 1 -#define SQLITE_DBSTATUS_SCHEMA_USED 2 -#define SQLITE_DBSTATUS_STMT_USED 3 -#define SQLITE_DBSTATUS_MAX 3 /* Largest defined DBSTATUS */ +#define SQLITE_DBSTATUS_LOOKASIDE_USED 0 +#define SQLITE_DBSTATUS_CACHE_USED 1 +#define SQLITE_DBSTATUS_SCHEMA_USED 2 +#define SQLITE_DBSTATUS_STMT_USED 3 +#define SQLITE_DBSTATUS_LOOKASIDE_HIT 4 +#define SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE 5 +#define SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL 6 +#define SQLITE_DBSTATUS_MAX 6 /* Largest defined DBSTATUS */ /* ** CAPI3REF: Prepared Statement Status ** ** ^(Each prepared statement maintains various -** [SQLITE_STMTSTATUS_SORT | counters] that measure the number +** [SQLITE_STMTSTATUS counters] that measure the number ** of times it has performed specific operations.)^ These counters can ** be used to monitor the performance characteristics of the prepared ** statements. For example, if the number of table steps greatly exceeds @@ -6083,7 +6363,7 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r ** ^(This interface is used to retrieve and reset counter values from ** a [prepared statement]. The first argument is the prepared statement ** object to be interrogated. The second argument -** is an integer code for a specific [SQLITE_STMTSTATUS_SORT | counter] +** is an integer code for a specific [SQLITE_STMTSTATUS counter] ** to be interrogated.)^ ** ^The current value of the requested counter is returned. ** ^If the resetFlg is true, then the counter is reset to zero after this @@ -6095,24 +6375,25 @@ SQLITE_API int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg); /* ** CAPI3REF: Status Parameters for prepared statements +** KEYWORDS: {SQLITE_STMTSTATUS counter} {SQLITE_STMTSTATUS counters} ** ** These preprocessor macros define integer codes that name counter ** values associated with the [sqlite3_stmt_status()] interface. ** The meanings of the various counters are as follows: ** ** <dl> -** <dt>SQLITE_STMTSTATUS_FULLSCAN_STEP</dt> +** [[SQLITE_STMTSTATUS_FULLSCAN_STEP]] <dt>SQLITE_STMTSTATUS_FULLSCAN_STEP</dt> ** <dd>^This is the number of times that SQLite has stepped forward in ** a table as part of a full table scan. Large numbers for this counter ** may indicate opportunities for performance improvement through ** careful use of indices.</dd> ** -** <dt>SQLITE_STMTSTATUS_SORT</dt> +** [[SQLITE_STMTSTATUS_SORT]] <dt>SQLITE_STMTSTATUS_SORT</dt> ** <dd>^This is the number of sort operations that have occurred. ** A non-zero value in this counter may indicate an opportunity to ** improvement performance through careful use of indices.</dd> ** -** <dt>SQLITE_STMTSTATUS_AUTOINDEX</dt> +** [[SQLITE_STMTSTATUS_AUTOINDEX]] <dt>SQLITE_STMTSTATUS_AUTOINDEX</dt> ** <dd>^This is the number of rows inserted into transient indices that ** were created automatically in order to help joins run faster. ** A non-zero value in this counter may indicate an opportunity to @@ -6163,6 +6444,7 @@ typedef struct sqlite3_pcache sqlite3_pcache; ** the application may discard the parameter after the call to ** [sqlite3_config()] returns.)^ ** +** [[the xInit() page cache method]] ** ^(The xInit() method is called once for each effective ** call to [sqlite3_initialize()])^ ** (usually only once during the lifetime of the process). ^(The xInit() @@ -6173,6 +6455,7 @@ typedef struct sqlite3_pcache sqlite3_pcache; ** built-in default page cache is used instead of the application defined ** page cache.)^ ** +** [[the xShutdown() page cache method]] ** ^The xShutdown() method is called by [sqlite3_shutdown()]. ** It can be used to clean up ** any outstanding resources before process shutdown, if required. @@ -6187,17 +6470,20 @@ typedef struct sqlite3_pcache sqlite3_pcache; ** ^SQLite will never invoke xInit() more than once without an intervening ** call to xShutdown(). ** +** [[the xCreate() page cache methods]] ** ^SQLite invokes the xCreate() method to construct a new cache instance. ** SQLite will typically create one cache instance for each open database file, ** though this is not guaranteed. ^The ** first parameter, szPage, is the size in bytes of the pages that must ** be allocated by the cache. ^szPage will not be a power of two. ^szPage ** will the page size of the database file that is to be cached plus an -** increment (here called "R") of about 100 or 200. SQLite will use the +** increment (here called "R") of less than 250. SQLite will use the ** extra R bytes on each page to store metadata about the underlying ** database page on disk. The value of R depends ** on the SQLite version, the target platform, and how SQLite was compiled. -** ^R is constant for a particular build of SQLite. ^The second argument to +** ^(R is constant for a particular build of SQLite. Except, there are two +** distinct values of R when SQLite is compiled with the proprietary +** ZIPVFS extension.)^ ^The second argument to ** xCreate(), bPurgeable, is true if the cache being created will ** be used to cache database pages of a file stored on disk, or ** false if it is used for an in-memory database. The cache implementation @@ -6209,6 +6495,7 @@ typedef struct sqlite3_pcache sqlite3_pcache; ** ^Hence, a cache created with bPurgeable false will ** never contain any unpinned pages. ** +** [[the xCachesize() page cache method]] ** ^(The xCachesize() method may be called at any time by SQLite to set the ** suggested maximum cache-size (number of pages stored by) the cache ** instance passed as the first argument. This is the value configured using @@ -6216,20 +6503,22 @@ typedef struct sqlite3_pcache sqlite3_pcache; ** parameter, the implementation is not required to do anything with this ** value; it is advisory only. ** +** [[the xPagecount() page cache methods]] ** The xPagecount() method must return the number of pages currently ** stored in the cache, both pinned and unpinned. ** +** [[the xFetch() page cache methods]] ** The xFetch() method locates a page in the cache and returns a pointer to ** the page, or a NULL pointer. ** A "page", in this context, means a buffer of szPage bytes aligned at an ** 8-byte boundary. The page to be fetched is determined by the key. ^The -** mimimum key value is 1. After it has been retrieved using xFetch, the page +** minimum key value is 1. After it has been retrieved using xFetch, the page ** is considered to be "pinned". ** ** If the requested page is already in the page cache, then the page cache ** implementation must return a pointer to the page buffer with its content ** intact. If the requested page is not already in the cache, then the -** behavior of the cache implementation should use the value of the createFlag +** cache implementation should use the value of the createFlag ** parameter to help it determined what action to take: ** ** <table border=1 width=85% align=center> @@ -6247,6 +6536,7 @@ typedef struct sqlite3_pcache sqlite3_pcache; ** attempt to unpin one or more cache pages by spilling the content of ** pinned pages to disk and synching the operating system disk cache. ** +** [[the xUnpin() page cache method]] ** ^xUnpin() is called by SQLite with a pointer to a currently pinned page ** as its second argument. If the third parameter, discard, is non-zero, ** then the page must be evicted from the cache. @@ -6259,6 +6549,7 @@ typedef struct sqlite3_pcache sqlite3_pcache; ** call to xUnpin() unpins the page regardless of the number of prior calls ** to xFetch(). ** +** [[the xRekey() page cache methods]] ** The xRekey() method is used to change the key value associated with the ** page passed as the second argument. If the cache ** previously contains an entry associated with newKey, it must be @@ -6271,6 +6562,7 @@ typedef struct sqlite3_pcache sqlite3_pcache; ** of these pages are pinned, they are implicitly unpinned, meaning that ** they can be safely discarded. ** +** [[the xDestroy() page cache method]] ** ^The xDestroy() method is used to delete a cache allocated by xCreate(). ** All resources associated with the specified cache should be freed. ^After ** calling the xDestroy() method, SQLite considers the [sqlite3_pcache*] @@ -6313,11 +6605,12 @@ typedef struct sqlite3_backup sqlite3_backup; ** ** See Also: [Using the SQLite Online Backup API] ** -** ^Exclusive access is required to the destination database for the -** duration of the operation. ^However the source database is only -** read-locked while it is actually being read; it is not locked -** continuously for the entire backup operation. ^Thus, the backup may be -** performed on a live source database without preventing other users from +** ^SQLite holds a write transaction open on the destination database file +** for the duration of the backup operation. +** ^The source database is read-locked only while it is being read; +** it is not locked continuously for the entire backup operation. +** ^Thus, the backup may be performed on a live source database without +** preventing other database connections from ** reading or writing to the source database while the backup is underway. ** ** ^(To perform a backup operation: @@ -6332,7 +6625,7 @@ typedef struct sqlite3_backup sqlite3_backup; ** There should be exactly one call to sqlite3_backup_finish() for each ** successful call to sqlite3_backup_init(). ** -** <b>sqlite3_backup_init()</b> +** [[sqlite3_backup_init()]] <b>sqlite3_backup_init()</b> ** ** ^The D and N arguments to sqlite3_backup_init(D,N,S,M) are the ** [database connection] associated with the destination database @@ -6344,11 +6637,11 @@ typedef struct sqlite3_backup sqlite3_backup; ** sqlite3_backup_init(D,N,S,M) identify the [database connection] ** and database name of the source database, respectively. ** ^The source and destination [database connections] (parameters S and D) -** must be different or else sqlite3_backup_init(D,N,S,M) will file with +** must be different or else sqlite3_backup_init(D,N,S,M) will fail with ** an error. ** ** ^If an error occurs within sqlite3_backup_init(D,N,S,M), then NULL is -** returned and an error code and error message are store3d in the +** returned and an error code and error message are stored in the ** destination [database connection] D. ** ^The error code and message for the failed call to sqlite3_backup_init() ** can be retrieved using the [sqlite3_errcode()], [sqlite3_errmsg()], and/or @@ -6359,13 +6652,13 @@ typedef struct sqlite3_backup sqlite3_backup; ** sqlite3_backup_finish() functions to perform the specified backup ** operation. ** -** <b>sqlite3_backup_step()</b> +** [[sqlite3_backup_step()]] <b>sqlite3_backup_step()</b> ** ** ^Function sqlite3_backup_step(B,N) will copy up to N pages between ** the source and destination databases specified by [sqlite3_backup] object B. ** ^If N is negative, all remaining source pages are copied. ** ^If sqlite3_backup_step(B,N) successfully copies N pages and there -** are still more pages to be copied, then the function resturns [SQLITE_OK]. +** are still more pages to be copied, then the function returns [SQLITE_OK]. ** ^If sqlite3_backup_step(B,N) successfully finishes copying all pages ** from source to destination, then it returns [SQLITE_DONE]. ** ^If an error occurs while running sqlite3_backup_step(B,N), @@ -6379,7 +6672,7 @@ typedef struct sqlite3_backup sqlite3_backup; ** <li> the destination database was opened read-only, or ** <li> the destination database is using write-ahead-log journaling ** and the destination and source page sizes differ, or -** <li> The destination database is an in-memory database and the +** <li> the destination database is an in-memory database and the ** destination and source page sizes differ. ** </ol>)^ ** @@ -6416,7 +6709,7 @@ typedef struct sqlite3_backup sqlite3_backup; ** by the backup operation, then the backup database is automatically ** updated at the same time. ** -** <b>sqlite3_backup_finish()</b> +** [[sqlite3_backup_finish()]] <b>sqlite3_backup_finish()</b> ** ** When sqlite3_backup_step() has returned [SQLITE_DONE], or when the ** application wishes to abandon the backup operation, the application @@ -6439,7 +6732,8 @@ typedef struct sqlite3_backup sqlite3_backup; ** is not a permanent error and does not affect the return value of ** sqlite3_backup_finish(). ** -** <b>sqlite3_backup_remaining(), sqlite3_backup_pagecount()</b> +** [[sqlite3_backup__remaining()]] [[sqlite3_backup_pagecount()]] +** <b>sqlite3_backup_remaining() and sqlite3_backup_pagecount()</b> ** ** ^Each call to sqlite3_backup_step() sets two values inside ** the [sqlite3_backup] object: the number of pages still to be backed @@ -6710,7 +7004,8 @@ SQLITE_API void *sqlite3_wal_hook( ** from SQL. ** ** ^Every new [database connection] defaults to having the auto-checkpoint -** enabled with a threshold of 1000 pages. The use of this interface +** enabled with a threshold of 1000 or [SQLITE_DEFAULT_WAL_AUTOCHECKPOINT] +** pages. The use of this interface ** is only necessary if the default setting is found to be suboptimal ** for a particular application. */ @@ -6729,10 +7024,190 @@ SQLITE_API int sqlite3_wal_autocheckpoint(sqlite3 *db, int N); ** from SQL. ^The [sqlite3_wal_autocheckpoint()] interface and the ** [wal_autocheckpoint pragma] can be used to cause this interface to be ** run whenever the WAL reaches a certain size threshold. +** +** See also: [sqlite3_wal_checkpoint_v2()] */ SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb); /* +** CAPI3REF: Checkpoint a database +** +** Run a checkpoint operation on WAL database zDb attached to database +** handle db. The specific operation is determined by the value of the +** eMode parameter: +** +** <dl> +** <dt>SQLITE_CHECKPOINT_PASSIVE<dd> +** Checkpoint as many frames as possible without waiting for any database +** readers or writers to finish. Sync the db file if all frames in the log +** are checkpointed. This mode is the same as calling +** sqlite3_wal_checkpoint(). The busy-handler callback is never invoked. +** +** <dt>SQLITE_CHECKPOINT_FULL<dd> +** This mode blocks (calls the busy-handler callback) until there is no +** database writer and all readers are reading from the most recent database +** snapshot. It then checkpoints all frames in the log file and syncs the +** database file. This call blocks database writers while it is running, +** but not database readers. +** +** <dt>SQLITE_CHECKPOINT_RESTART<dd> +** This mode works the same way as SQLITE_CHECKPOINT_FULL, except after +** checkpointing the log file it blocks (calls the busy-handler callback) +** until all readers are reading from the database file only. This ensures +** that the next client to write to the database file restarts the log file +** from the beginning. This call blocks database writers while it is running, +** but not database readers. +** </dl> +** +** If pnLog is not NULL, then *pnLog is set to the total number of frames in +** the log file before returning. If pnCkpt is not NULL, then *pnCkpt is set to +** the total number of checkpointed frames (including any that were already +** checkpointed when this function is called). *pnLog and *pnCkpt may be +** populated even if sqlite3_wal_checkpoint_v2() returns other than SQLITE_OK. +** If no values are available because of an error, they are both set to -1 +** before returning to communicate this to the caller. +** +** All calls obtain an exclusive "checkpoint" lock on the database file. If +** any other process is running a checkpoint operation at the same time, the +** lock cannot be obtained and SQLITE_BUSY is returned. Even if there is a +** busy-handler configured, it will not be invoked in this case. +** +** The SQLITE_CHECKPOINT_FULL and RESTART modes also obtain the exclusive +** "writer" lock on the database file. If the writer lock cannot be obtained +** immediately, and a busy-handler is configured, it is invoked and the writer +** lock retried until either the busy-handler returns 0 or the lock is +** successfully obtained. The busy-handler is also invoked while waiting for +** database readers as described above. If the busy-handler returns 0 before +** the writer lock is obtained or while waiting for database readers, the +** checkpoint operation proceeds from that point in the same way as +** SQLITE_CHECKPOINT_PASSIVE - checkpointing as many frames as possible +** without blocking any further. SQLITE_BUSY is returned in this case. +** +** If parameter zDb is NULL or points to a zero length string, then the +** specified operation is attempted on all WAL databases. In this case the +** values written to output parameters *pnLog and *pnCkpt are undefined. If +** an SQLITE_BUSY error is encountered when processing one or more of the +** attached WAL databases, the operation is still attempted on any remaining +** attached databases and SQLITE_BUSY is returned to the caller. If any other +** error occurs while processing an attached database, processing is abandoned +** and the error code returned to the caller immediately. If no error +** (SQLITE_BUSY or otherwise) is encountered while processing the attached +** databases, SQLITE_OK is returned. +** +** If database zDb is the name of an attached database that is not in WAL +** mode, SQLITE_OK is returned and both *pnLog and *pnCkpt set to -1. If +** zDb is not NULL (or a zero length string) and is not the name of any +** attached database, SQLITE_ERROR is returned to the caller. +*/ +SQLITE_API int sqlite3_wal_checkpoint_v2( + sqlite3 *db, /* Database handle */ + const char *zDb, /* Name of attached database (or NULL) */ + int eMode, /* SQLITE_CHECKPOINT_* value */ + int *pnLog, /* OUT: Size of WAL log in frames */ + int *pnCkpt /* OUT: Total number of frames checkpointed */ +); + +/* +** CAPI3REF: Checkpoint operation parameters +** +** These constants can be used as the 3rd parameter to +** [sqlite3_wal_checkpoint_v2()]. See the [sqlite3_wal_checkpoint_v2()] +** documentation for additional information about the meaning and use of +** each of these values. +*/ +#define SQLITE_CHECKPOINT_PASSIVE 0 +#define SQLITE_CHECKPOINT_FULL 1 +#define SQLITE_CHECKPOINT_RESTART 2 + +/* +** CAPI3REF: Virtual Table Interface Configuration +** +** This function may be called by either the [xConnect] or [xCreate] method +** of a [virtual table] implementation to configure +** various facets of the virtual table interface. +** +** If this interface is invoked outside the context of an xConnect or +** xCreate virtual table method then the behavior is undefined. +** +** At present, there is only one option that may be configured using +** this function. (See [SQLITE_VTAB_CONSTRAINT_SUPPORT].) Further options +** may be added in the future. +*/ +SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...); + +/* +** CAPI3REF: Virtual Table Configuration Options +** +** These macros define the various options to the +** [sqlite3_vtab_config()] interface that [virtual table] implementations +** can use to customize and optimize their behavior. +** +** <dl> +** <dt>SQLITE_VTAB_CONSTRAINT_SUPPORT +** <dd>Calls of the form +** [sqlite3_vtab_config](db,SQLITE_VTAB_CONSTRAINT_SUPPORT,X) are supported, +** where X is an integer. If X is zero, then the [virtual table] whose +** [xCreate] or [xConnect] method invoked [sqlite3_vtab_config()] does not +** support constraints. In this configuration (which is the default) if +** a call to the [xUpdate] method returns [SQLITE_CONSTRAINT], then the entire +** statement is rolled back as if [ON CONFLICT | OR ABORT] had been +** specified as part of the users SQL statement, regardless of the actual +** ON CONFLICT mode specified. +** +** If X is non-zero, then the virtual table implementation guarantees +** that if [xUpdate] returns [SQLITE_CONSTRAINT], it will do so before +** any modifications to internal or persistent data structures have been made. +** If the [ON CONFLICT] mode is ABORT, FAIL, IGNORE or ROLLBACK, SQLite +** is able to roll back a statement or database transaction, and abandon +** or continue processing the current SQL statement as appropriate. +** If the ON CONFLICT mode is REPLACE and the [xUpdate] method returns +** [SQLITE_CONSTRAINT], SQLite handles this as if the ON CONFLICT mode +** had been ABORT. +** +** Virtual table implementations that are required to handle OR REPLACE +** must do so within the [xUpdate] method. If a call to the +** [sqlite3_vtab_on_conflict()] function indicates that the current ON +** CONFLICT policy is REPLACE, the virtual table implementation should +** silently replace the appropriate rows within the xUpdate callback and +** return SQLITE_OK. Or, if this is not possible, it may return +** SQLITE_CONSTRAINT, in which case SQLite falls back to OR ABORT +** constraint handling. +** </dl> +*/ +#define SQLITE_VTAB_CONSTRAINT_SUPPORT 1 + +/* +** CAPI3REF: Determine The Virtual Table Conflict Policy +** +** This function may only be called from within a call to the [xUpdate] method +** of a [virtual table] implementation for an INSERT or UPDATE operation. ^The +** value returned is one of [SQLITE_ROLLBACK], [SQLITE_IGNORE], [SQLITE_FAIL], +** [SQLITE_ABORT], or [SQLITE_REPLACE], according to the [ON CONFLICT] mode +** of the SQL statement that triggered the call to the [xUpdate] method of the +** [virtual table]. +*/ +SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *); + +/* +** CAPI3REF: Conflict resolution modes +** +** These constants are returned by [sqlite3_vtab_on_conflict()] to +** inform a [virtual table] implementation what the [ON CONFLICT] mode +** is for the SQL statement being evaluated. +** +** Note that the [SQLITE_IGNORE] constant is also used as a potential +** return value from the [sqlite3_set_authorizer()] callback and that +** [SQLITE_ABORT] is also a [result code]. +*/ +#define SQLITE_ROLLBACK 1 +/* #define SQLITE_IGNORE 2 // Also used by sqlite3_authorizer() callback */ +#define SQLITE_FAIL 3 +/* #define SQLITE_ABORT 4 // Also an error code */ +#define SQLITE_REPLACE 5 + + + +/* ** Undo the hack that converts floating point types to integer for ** builds on processors without floating point support. */ @@ -7397,6 +7872,7 @@ typedef struct TriggerPrg TriggerPrg; typedef struct TriggerStep TriggerStep; typedef struct UnpackedRecord UnpackedRecord; typedef struct VTable VTable; +typedef struct VtabCtx VtabCtx; typedef struct Walker Walker; typedef struct WherePlan WherePlan; typedef struct WhereInfo WhereInfo; @@ -7450,21 +7926,10 @@ typedef struct WhereLevel WhereLevel; typedef struct Btree Btree; typedef struct BtCursor BtCursor; typedef struct BtShared BtShared; -typedef struct BtreeMutexArray BtreeMutexArray; - -/* -** This structure records all of the Btrees that need to hold -** a mutex before we enter sqlite3VdbeExec(). The Btrees are -** are placed in aBtree[] in order of aBtree[]->pBt. That way, -** we can always lock and unlock them all quickly. -*/ -struct BtreeMutexArray { - int nMutex; - Btree *aBtree[SQLITE_MAX_ATTACHED+1]; -}; SQLITE_PRIVATE int sqlite3BtreeOpen( + sqlite3_vfs *pVfs, /* VFS to use with this b-tree */ const char *zFilename, /* Name of database file to open */ sqlite3 *db, /* Associated database connection */ Btree **ppBtree, /* Return open Btree* here */ @@ -7498,7 +7963,7 @@ SQLITE_PRIVATE int sqlite3BtreeSetAutoVacuum(Btree *, int); SQLITE_PRIVATE int sqlite3BtreeGetAutoVacuum(Btree *); SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree*,int); SQLITE_PRIVATE int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster); -SQLITE_PRIVATE int sqlite3BtreeCommitPhaseTwo(Btree*); +SQLITE_PRIVATE int sqlite3BtreeCommitPhaseTwo(Btree*, int); SQLITE_PRIVATE int sqlite3BtreeCommit(Btree*); SQLITE_PRIVATE int sqlite3BtreeRollback(Btree*); SQLITE_PRIVATE int sqlite3BtreeBeginStmt(Btree*,int); @@ -7618,7 +8083,7 @@ SQLITE_PRIVATE void sqlite3BtreeCursorList(Btree*); #endif #ifndef SQLITE_OMIT_WAL -SQLITE_PRIVATE int sqlite3BtreeCheckpoint(Btree*); +SQLITE_PRIVATE int sqlite3BtreeCheckpoint(Btree*, int, int *, int *); #endif /* @@ -7635,30 +8100,28 @@ SQLITE_PRIVATE void sqlite3BtreeEnterAll(sqlite3*); #endif #if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE +SQLITE_PRIVATE int sqlite3BtreeSharable(Btree*); SQLITE_PRIVATE void sqlite3BtreeLeave(Btree*); SQLITE_PRIVATE void sqlite3BtreeEnterCursor(BtCursor*); SQLITE_PRIVATE void sqlite3BtreeLeaveCursor(BtCursor*); SQLITE_PRIVATE void sqlite3BtreeLeaveAll(sqlite3*); -SQLITE_PRIVATE void sqlite3BtreeMutexArrayEnter(BtreeMutexArray*); -SQLITE_PRIVATE void sqlite3BtreeMutexArrayLeave(BtreeMutexArray*); -SQLITE_PRIVATE void sqlite3BtreeMutexArrayInsert(BtreeMutexArray*, Btree*); #ifndef NDEBUG /* These routines are used inside assert() statements only. */ SQLITE_PRIVATE int sqlite3BtreeHoldsMutex(Btree*); SQLITE_PRIVATE int sqlite3BtreeHoldsAllMutexes(sqlite3*); +SQLITE_PRIVATE int sqlite3SchemaMutexHeld(sqlite3*,int,Schema*); #endif #else +# define sqlite3BtreeSharable(X) 0 # define sqlite3BtreeLeave(X) # define sqlite3BtreeEnterCursor(X) # define sqlite3BtreeLeaveCursor(X) # define sqlite3BtreeLeaveAll(X) -# define sqlite3BtreeMutexArrayEnter(X) -# define sqlite3BtreeMutexArrayLeave(X) -# define sqlite3BtreeMutexArrayInsert(X,Y) # define sqlite3BtreeHoldsMutex(X) 1 # define sqlite3BtreeHoldsAllMutexes(X) 1 +# define sqlite3SchemaMutexHeld(X,Y,Z) 1 #endif @@ -7777,7 +8240,7 @@ typedef struct VdbeOpList VdbeOpList; #define P4_KEYINFO (-6) /* P4 is a pointer to a KeyInfo structure */ #define P4_VDBEFUNC (-7) /* P4 is a pointer to a VdbeFunc structure */ #define P4_MEM (-8) /* P4 is a pointer to a Mem* structure */ -#define P4_TRANSIENT (-9) /* P4 is a pointer to a transient string */ +#define P4_TRANSIENT 0 /* P4 is a pointer to a transient string */ #define P4_VTAB (-10) /* P4 is a pointer to an sqlite3_vtab structure */ #define P4_MPRINTF (-11) /* P4 is a string obtained from sqlite3_mprintf() */ #define P4_REAL (-12) /* P4 is a 64-bit floating point value */ @@ -8027,6 +8490,7 @@ SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe*,int,int,int,int); SQLITE_PRIVATE int sqlite3VdbeAddOp4(Vdbe*,int,int,int,int,const char *zP4,int); SQLITE_PRIVATE int sqlite3VdbeAddOp4Int(Vdbe*,int,int,int,int,int); SQLITE_PRIVATE int sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp); +SQLITE_PRIVATE void sqlite3VdbeAddParseSchemaOp(Vdbe*,int,char*); SQLITE_PRIVATE void sqlite3VdbeChangeP1(Vdbe*, int addr, int P1); SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe*, int addr, int P2); SQLITE_PRIVATE void sqlite3VdbeChangeP3(Vdbe*, int addr, int P3); @@ -8040,7 +8504,7 @@ SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Vdbe*); SQLITE_PRIVATE void sqlite3VdbeRunOnlyOnce(Vdbe*); SQLITE_PRIVATE void sqlite3VdbeDelete(Vdbe*); SQLITE_PRIVATE void sqlite3VdbeDeleteObject(sqlite3*,Vdbe*); -SQLITE_PRIVATE void sqlite3VdbeMakeReady(Vdbe*,int,int,int,int,int,int); +SQLITE_PRIVATE void sqlite3VdbeMakeReady(Vdbe*,Parse*); SQLITE_PRIVATE int sqlite3VdbeFinalize(Vdbe*); SQLITE_PRIVATE void sqlite3VdbeResolveLabel(Vdbe*, int); SQLITE_PRIVATE int sqlite3VdbeCurrentAddr(Vdbe*); @@ -8049,6 +8513,7 @@ SQLITE_PRIVATE int sqlite3VdbeAssertMayAbort(Vdbe *, int); SQLITE_PRIVATE void sqlite3VdbeTrace(Vdbe*,FILE*); #endif SQLITE_PRIVATE void sqlite3VdbeResetStepResult(Vdbe*); +SQLITE_PRIVATE void sqlite3VdbeRewind(Vdbe*); SQLITE_PRIVATE int sqlite3VdbeReset(Vdbe*); SQLITE_PRIVATE void sqlite3VdbeSetNumCols(Vdbe*,int); SQLITE_PRIVATE int sqlite3VdbeSetColName(Vdbe*, int, int, const char *, void(*)(void*)); @@ -8228,7 +8693,7 @@ SQLITE_PRIVATE int sqlite3PagerOpenSavepoint(Pager *pPager, int n); SQLITE_PRIVATE int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint); SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager); -SQLITE_PRIVATE int sqlite3PagerCheckpoint(Pager *pPager); +SQLITE_PRIVATE int sqlite3PagerCheckpoint(Pager *pPager, int, int*, int*); SQLITE_PRIVATE int sqlite3PagerWalSupported(Pager *pPager); SQLITE_PRIVATE int sqlite3PagerWalCallback(Pager *pPager); SQLITE_PRIVATE int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen); @@ -8812,9 +9277,24 @@ struct Db { /* ** An instance of the following structure stores a database schema. +** +** Most Schema objects are associated with a Btree. The exception is +** the Schema for the TEMP databaes (sqlite3.aDb[1]) which is free-standing. +** In shared cache mode, a single Schema object can be shared by multiple +** Btrees that refer to the same underlying BtShared object. +** +** Schema objects are automatically deallocated when the last Btree that +** references them is destroyed. The TEMP Schema is manually freed by +** sqlite3_close(). +* +** A thread must be holding a mutex on the corresponding Btree in order +** to access Schema content. This implies that the thread must also be +** holding a mutex on the sqlite3 connection pointer that owns the Btree. +** For a TEMP Schema, only the connection mutex is required. */ struct Schema { int schema_cookie; /* Database schema version number for this file */ + int iGeneration; /* Generation counter. Incremented with each change */ Hash tblHash; /* All tables indexed by name */ Hash idxHash; /* All (named) indices indexed by name */ Hash trigHash; /* All triggers indexed by name */ @@ -8881,6 +9361,7 @@ struct Lookaside { u8 bMalloced; /* True if pStart obtained from sqlite3_malloc() */ int nOut; /* Number of buffers currently checked out */ int mxOut; /* Highwater mark for nOut */ + int anStat[3]; /* 0: hits. 1: size misses. 2: full misses */ LookasideSlot *pFree; /* List of available buffers */ void *pStart; /* First byte of available memory space */ void *pEnd; /* First byte past end of available space */ @@ -8930,7 +9411,7 @@ struct sqlite3 { int nDb; /* Number of backends currently in use */ Db *aDb; /* All backends */ int flags; /* Miscellaneous flags. See below */ - int openFlags; /* Flags passed to sqlite3_vfs.xOpen() */ + unsigned int openFlags; /* Flags passed to sqlite3_vfs.xOpen() */ int errCode; /* Most recent error code (SQLITE_*) */ int errMask; /* & result codes with this before returning */ u8 autoCommit; /* The auto-commit flag. */ @@ -8939,6 +9420,7 @@ struct sqlite3 { u8 dfltLockMode; /* Default locking-mode for attached dbs */ signed char nextAutovac; /* Autovac setting after VACUUM if >=0 */ u8 suppressErr; /* Do not issue error messages if true */ + u8 vtabOnConflict; /* Value to return for s3_vtab_on_conflict() */ int nextPagesize; /* Pagesize after VACUUM if >0 */ int nTable; /* Number of tables in the database */ CollSeq *pDfltColl; /* The default collating sequence (BINARY) */ @@ -8959,6 +9441,7 @@ struct sqlite3 { struct Vdbe *pVdbe; /* List of active virtual machines */ int activeVdbeCnt; /* Number of VDBEs currently executing */ int writeVdbeCnt; /* Number of active VDBEs that are writing */ + int vdbeExecCnt; /* Number of nested calls to VdbeExec() */ void (*xTrace)(void*,const char*); /* Trace function */ void *pTraceArg; /* Argument to the trace function */ void (*xProfile)(void*,const char*,u64); /* Profiling function */ @@ -8996,7 +9479,7 @@ struct sqlite3 { #endif #ifndef SQLITE_OMIT_VIRTUALTABLE Hash aModule; /* populated by sqlite3_create_module() */ - Table *pVTab; /* vtab with active Connect/Create method */ + VtabCtx *pVtabCtx; /* Context for active vtab connect/create */ VTable **aVTrans; /* Virtual tables with open transactions */ int nVTrans; /* Allocated size of aVTrans */ VTable *pDisconnect; /* Disconnect these in next sqlite3_prepare() */ @@ -9066,6 +9549,7 @@ struct sqlite3 { #define SQLITE_AutoIndex 0x08000000 /* Enable automatic indexes */ #define SQLITE_PreferBuiltin 0x10000000 /* Preference to built-in funcs */ #define SQLITE_LoadExtension 0x20000000 /* Enable load_extension */ +#define SQLITE_EnableTrigger 0x40000000 /* True to enable triggers */ /* ** Bits of the sqlite3.flags field that are used by the @@ -9079,6 +9563,7 @@ struct sqlite3 { #define SQLITE_IndexCover 0x10 /* Disable index covering table */ #define SQLITE_GroupByOrder 0x20 /* Disable GROUPBY cover of ORDERBY */ #define SQLITE_FactorOutConst 0x40 /* Disable factoring out constants */ +#define SQLITE_IdxRealAsInt 0x80 /* Store REAL as INT in indices */ #define SQLITE_OptMask 0xff /* Mask of all disablable opts */ /* @@ -9324,7 +9809,7 @@ struct CollSeq { ** schema is shared, as the implementation often stores the database ** connection handle passed to it via the xConnect() or xCreate() method ** during initialization internally. This database connection handle may -** then used by the virtual table implementation to access real tables +** then be used by the virtual table implementation to access real tables ** within the database. So that they appear as part of the callers ** transaction, these accesses need to be made via the same database ** connection as that used to execute SQL operations on the virtual table. @@ -9358,6 +9843,8 @@ struct VTable { Module *pMod; /* Pointer to module implementation */ sqlite3_vtab *pVtab; /* Pointer to vtab instance */ int nRef; /* Number of pointers to this structure */ + u8 bConstraint; /* True if constraints are supported */ + int iSavepoint; /* Depth of the SAVEPOINT stack */ VTable *pNext; /* Next in linked list (see above) */ }; @@ -9602,6 +10089,7 @@ struct Index { int tnum; /* Page containing root of this index in database file */ u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ u8 autoIndex; /* True if is automatically created (ex: by UNIQUE) */ + u8 bUnordered; /* Use this index for == or IN queries only */ char *zColAff; /* String defining the affinity of each column */ Index *pNext; /* The next index associated with the same table */ Schema *pSchema; /* Schema containing this index */ @@ -9765,7 +10253,7 @@ struct Expr { u16 flags; /* Various flags. EP_* See below */ union { char *zToken; /* Token value. Zero terminated and dequoted */ - int iValue; /* Integer value if EP_IntValue */ + int iValue; /* Non-negative integer value if EP_IntValue */ } u; /* If the EP_TokenOnly flag is set in the Expr.flags mask, then no @@ -10266,6 +10754,15 @@ struct TriggerPrg { }; /* +** The yDbMask datatype for the bitmask of all attached databases. +*/ +#if SQLITE_MAX_ATTACHED>30 + typedef sqlite3_uint64 yDbMask; +#else + typedef unsigned int yDbMask; +#endif + +/* ** An SQL parser context. A copy of this structure is passed through ** the parser and down into all the parser action routine in order to ** carry around information that is global to the entire parse. @@ -10313,8 +10810,8 @@ struct Parse { int iReg; /* Reg with value of this column. 0 means none. */ int lru; /* Least recently used entry has the smallest value */ } aColCache[SQLITE_N_COLCACHE]; /* One for each column cache entry */ - u32 writeMask; /* Start a write transaction on these databases */ - u32 cookieMask; /* Bitmask of schema verified databases */ + yDbMask writeMask; /* Start a write transaction on these databases */ + yDbMask cookieMask; /* Bitmask of schema verified databases */ u8 isMultiWrite; /* True if statement may affect/insert multiple rows */ u8 mayAbort; /* True if statement may throw an ABORT exception */ int cookieGoto; /* Address of OP_Goto to cookie verifier subroutine */ @@ -10342,9 +10839,8 @@ struct Parse { ** each recursion */ int nVar; /* Number of '?' variables seen in the SQL so far */ - int nVarExpr; /* Number of used slots in apVarExpr[] */ - int nVarExprAlloc; /* Number of allocated slots in apVarExpr[] */ - Expr **apVarExpr; /* Pointers to :aaa and $aaaa wildcard expressions */ + int nzVar; /* Number of available slots in azVar[] */ + char **azVar; /* Pointers to names of parameters */ Vdbe *pReprepare; /* VM being reprepared (sqlite3Reprepare()) */ int nAlias; /* Number of aliased result set columns */ int nAliasAlloc; /* Number of allocated slots for aAlias[] */ @@ -10536,6 +11032,7 @@ struct Sqlite3Config { int bMemstat; /* True to enable memory status */ int bCoreMutex; /* True to enable core mutexing */ int bFullMutex; /* True to enable full mutexing */ + int bOpenUri; /* True to interpret filenames as URIs */ int mxStrlen; /* Maximum string length */ int szLookaside; /* Default lookaside buffer size */ int nLookaside; /* Default lookaside buffer count */ @@ -10564,6 +11061,7 @@ struct Sqlite3Config { int nRefInitMutex; /* Number of users of pInitMutex */ void (*xLog)(void*,int,const char*); /* Function for logging */ void *pLogArg; /* First argument to xLog() */ + int bLocaltimeFault; /* True to fail localtime() calls */ }; /* @@ -10785,6 +11283,8 @@ SQLITE_PRIVATE void sqlite3AddColumnType(Parse*,Token*); SQLITE_PRIVATE void sqlite3AddDefaultValue(Parse*,ExprSpan*); SQLITE_PRIVATE void sqlite3AddCollateType(Parse*, Token*); SQLITE_PRIVATE void sqlite3EndTable(Parse*,Token*,Token*,Select*); +SQLITE_PRIVATE int sqlite3ParseUri(const char*,const char*,unsigned int*, + sqlite3_vfs**,char**,char **); SQLITE_PRIVATE Bitvec *sqlite3BitvecCreate(u32); SQLITE_PRIVATE int sqlite3BitvecTest(Bitvec*, u32); @@ -10884,6 +11384,7 @@ SQLITE_PRIVATE void sqlite3PrngRestoreState(void); SQLITE_PRIVATE void sqlite3PrngResetState(void); SQLITE_PRIVATE void sqlite3RollbackAll(sqlite3*); SQLITE_PRIVATE void sqlite3CodeVerifySchema(Parse*, int); +SQLITE_PRIVATE void sqlite3CodeVerifyNamedSchema(Parse*, const char *zDb); SQLITE_PRIVATE void sqlite3BeginTransaction(Parse*, int); SQLITE_PRIVATE void sqlite3CommitTransaction(Parse*); SQLITE_PRIVATE void sqlite3RollbackTransaction(Parse*); @@ -10988,7 +11489,7 @@ SQLITE_PRIVATE int sqlite3GetInt32(const char *, int*); SQLITE_PRIVATE int sqlite3Atoi(const char*); SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *pData, int nChar); SQLITE_PRIVATE int sqlite3Utf8CharLen(const char *pData, int nByte); -SQLITE_PRIVATE int sqlite3Utf8Read(const u8*, const u8**); +SQLITE_PRIVATE u32 sqlite3Utf8Read(const u8*, const u8**); /* ** Routines to read and write variable-length integers. These used to @@ -11034,6 +11535,7 @@ SQLITE_PRIVATE char sqlite3ExprAffinity(Expr *pExpr); SQLITE_PRIVATE int sqlite3Atoi64(const char*, i64*, int, u8); SQLITE_PRIVATE void sqlite3Error(sqlite3*, int, const char*,...); SQLITE_PRIVATE void *sqlite3HexToBlob(sqlite3*, const char *z, int n); +SQLITE_PRIVATE u8 sqlite3HexToInt(int h); SQLITE_PRIVATE int sqlite3TwoPartName(Parse *, Token *, Token *, Token **); SQLITE_PRIVATE const char *sqlite3ErrStr(int); SQLITE_PRIVATE int sqlite3ReadSchema(Parse *pParse); @@ -11045,6 +11547,16 @@ SQLITE_PRIVATE Expr *sqlite3ExprSetCollByToken(Parse *pParse, Expr*, Token*); SQLITE_PRIVATE int sqlite3CheckCollSeq(Parse *, CollSeq *); SQLITE_PRIVATE int sqlite3CheckObjectName(Parse *, const char *); SQLITE_PRIVATE void sqlite3VdbeSetChanges(sqlite3 *, int); +SQLITE_PRIVATE int sqlite3AddInt64(i64*,i64); +SQLITE_PRIVATE int sqlite3SubInt64(i64*,i64); +SQLITE_PRIVATE int sqlite3MulInt64(i64*,i64); +SQLITE_PRIVATE int sqlite3AbsInt32(int); +#ifdef SQLITE_ENABLE_8_3_NAMES +SQLITE_PRIVATE void sqlite3FileSuffix3(const char*, char*); +#else +# define sqlite3FileSuffix3(X,Y) +#endif +SQLITE_PRIVATE u8 sqlite3GetBoolean(const char *z); SQLITE_PRIVATE const void *sqlite3ValueText(sqlite3_value*, u8); SQLITE_PRIVATE int sqlite3ValueBytes(sqlite3_value*, u8); @@ -11069,7 +11581,7 @@ SQLITE_PRIVATE SQLITE_WSD FuncDefHash sqlite3GlobalFunctions; SQLITE_PRIVATE int sqlite3PendingByte; #endif #endif -SQLITE_PRIVATE void sqlite3RootPageMoved(Db*, int, int); +SQLITE_PRIVATE void sqlite3RootPageMoved(sqlite3*, int, int, int); SQLITE_PRIVATE void sqlite3Reindex(Parse*, Token*, Token*); SQLITE_PRIVATE void sqlite3AlterFunctions(void); SQLITE_PRIVATE void sqlite3AlterRenameTable(Parse*, SrcList*, Token*); @@ -11096,7 +11608,7 @@ SQLITE_PRIVATE void sqlite3DefaultRowEst(Index*); SQLITE_PRIVATE void sqlite3RegisterLikeFunctions(sqlite3*, int); SQLITE_PRIVATE int sqlite3IsLikeFunction(sqlite3*,Expr*,int*,char*); SQLITE_PRIVATE void sqlite3MinimumFileFormat(Parse*, int, int); -SQLITE_PRIVATE void sqlite3SchemaFree(void *); +SQLITE_PRIVATE void sqlite3SchemaClear(void *); SQLITE_PRIVATE Schema *sqlite3SchemaGet(sqlite3 *, Btree *); SQLITE_PRIVATE int sqlite3SchemaToIndex(sqlite3 *db, Schema *); SQLITE_PRIVATE KeyInfo *sqlite3IndexKeyinfo(Parse *, Index *); @@ -11154,6 +11666,7 @@ SQLITE_PRIVATE int sqlite3Utf8To8(unsigned char*); # define sqlite3VtabLock(X) # define sqlite3VtabUnlock(X) # define sqlite3VtabUnlockList(X) +# define sqlite3VtabSavepoint(X, Y, Z) SQLITE_OK #else SQLITE_PRIVATE void sqlite3VtabClear(sqlite3 *db, Table*); SQLITE_PRIVATE int sqlite3VtabSync(sqlite3 *db, char **); @@ -11162,6 +11675,7 @@ SQLITE_PRIVATE int sqlite3VtabCommit(sqlite3 *db); SQLITE_PRIVATE void sqlite3VtabLock(VTable *); SQLITE_PRIVATE void sqlite3VtabUnlock(VTable *); SQLITE_PRIVATE void sqlite3VtabUnlockList(sqlite3*); +SQLITE_PRIVATE int sqlite3VtabSavepoint(sqlite3 *, int, int); # define sqlite3VtabInSync(db) ((db)->nVTrans>0 && (db)->aVTrans==0) #endif SQLITE_PRIVATE void sqlite3VtabMakeWritable(Parse*,Table*); @@ -11183,7 +11697,7 @@ SQLITE_PRIVATE CollSeq *sqlite3BinaryCompareCollSeq(Parse *, Expr *, Expr *); SQLITE_PRIVATE int sqlite3TempInMemory(const sqlite3*); SQLITE_PRIVATE VTable *sqlite3GetVTable(sqlite3*, Table*); SQLITE_PRIVATE const char *sqlite3JournalModename(int); -SQLITE_PRIVATE int sqlite3Checkpoint(sqlite3*, int); +SQLITE_PRIVATE int sqlite3Checkpoint(sqlite3*, int, int, int*, int*); SQLITE_PRIVATE int sqlite3WalDefaultHook(void*,sqlite3*,const char*,int); /* Declarations for functions in fkey.c. All of these are replaced by @@ -11468,7 +11982,9 @@ SQLITE_PRIVATE const unsigned char sqlite3CtypeMap[256] = { }; #endif - +#ifndef SQLITE_USE_URI +# define SQLITE_USE_URI 0 +#endif /* ** The following singleton contains the global configuration for @@ -11478,6 +11994,7 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = { SQLITE_DEFAULT_MEMSTATUS, /* bMemstat */ 1, /* bCoreMutex */ SQLITE_THREADSAFE==1, /* bFullMutex */ + SQLITE_USE_URI, /* bOpenUri */ 0x7ffffffe, /* mxStrlen */ 100, /* szLookaside */ 500, /* nLookaside */ @@ -11505,6 +12022,7 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = { 0, /* nRefInitMutex */ 0, /* xLog */ 0, /* pLogArg */ + 0, /* bLocaltimeFault */ }; @@ -11731,6 +12249,9 @@ static const char * const azCompileOpt[] = { #ifdef SQLITE_OMIT_AUTOMATIC_INDEX "OMIT_AUTOMATIC_INDEX", #endif +#ifdef SQLITE_OMIT_AUTORESET + "OMIT_AUTORESET", +#endif #ifdef SQLITE_OMIT_AUTOVACUUM "OMIT_AUTOVACUUM", #endif @@ -12006,16 +12527,14 @@ typedef unsigned char Bool; ** ** Every cursor that the virtual machine has open is represented by an ** instance of the following structure. -** -** If the VdbeCursor.isTriggerRow flag is set it means that this cursor is -** really a single row that represents the NEW or OLD pseudo-table of -** a row trigger. The data for the row is stored in VdbeCursor.pData and -** the rowid is in VdbeCursor.iKey. */ struct VdbeCursor { BtCursor *pCursor; /* The cursor structure of the backend */ + Btree *pBt; /* Separate file holding temporary table */ + KeyInfo *pKeyInfo; /* Info about index keys needed by index cursors */ int iDb; /* Index of cursor database in db->aDb[] (or -1) */ - i64 lastRowid; /* Last rowid from a Next or NextIdx operation */ + int pseudoTableReg; /* Register holding pseudotable content. */ + int nField; /* Number of fields in the header */ Bool zeroed; /* True if zeroed out and ready for reuse */ Bool rowidIsValid; /* True if lastRowid is valid */ Bool atFirst; /* True if pointing to first entry */ @@ -12025,14 +12544,11 @@ struct VdbeCursor { Bool isTable; /* True if a table requiring integer keys */ Bool isIndex; /* True if an index containing keys only - no data */ Bool isOrdered; /* True if the underlying table is BTREE_UNORDERED */ - i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */ - Btree *pBt; /* Separate file holding temporary table */ - int pseudoTableReg; /* Register holding pseudotable content. */ - KeyInfo *pKeyInfo; /* Info about index keys needed by index cursors */ - int nField; /* Number of fields in the header */ - i64 seqCount; /* Sequence counter */ sqlite3_vtab_cursor *pVtabCursor; /* The cursor for a virtual table */ const sqlite3_module *pModule; /* Module for cursor pVtabCursor */ + i64 seqCount; /* Sequence counter */ + i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */ + i64 lastRowid; /* Last rowid from a Next or NextIdx operation */ /* Result of last sqlite3BtreeMoveto() done by an OP_NotExists or ** OP_IsUnique opcode on this cursor. */ @@ -12104,25 +12620,19 @@ struct VdbeFrame { /* ** Internally, the vdbe manipulates nearly all SQL values as Mem ** structures. Each Mem struct may cache multiple representations (string, -** integer etc.) of the same value. A value (and therefore Mem structure) -** has the following properties: -** -** Each value has a manifest type. The manifest type of the value stored -** in a Mem struct is returned by the MemType(Mem*) macro. The type is -** one of SQLITE_NULL, SQLITE_INTEGER, SQLITE_REAL, SQLITE_TEXT or -** SQLITE_BLOB. +** integer etc.) of the same value. */ struct Mem { + sqlite3 *db; /* The associated database connection */ + char *z; /* String or BLOB value */ + double r; /* Real value */ union { - i64 i; /* Integer value. */ + i64 i; /* Integer value used when MEM_Int is set in flags */ int nZero; /* Used when bit MEM_Zero is set in flags */ FuncDef *pDef; /* Used only when flags==MEM_Agg */ RowSet *pRowSet; /* Used only when flags==MEM_RowSet */ VdbeFrame *pFrame; /* Used when flags==MEM_Frame */ } u; - double r; /* Real value */ - sqlite3 *db; /* The associated database connection */ - char *z; /* String or BLOB value */ int n; /* Number of characters in string value, excluding '\0' */ u16 flags; /* Some combination of MEM_Null, MEM_Str, MEM_Dyn, etc. */ u8 type; /* One of SQLITE_NULL, SQLITE_TEXT, SQLITE_INTEGER, etc */ @@ -12146,9 +12656,6 @@ struct Mem { ** database (see below for exceptions). If the MEM_Term flag is also ** set, then the string is nul terminated. The MEM_Int and MEM_Real ** flags may coexist with the MEM_Str flag. -** -** Multiple of these values can appear in Mem.flags. But only one -** at a time can appear in Mem.type. */ #define MEM_Null 0x0001 /* Value is NULL */ #define MEM_Str 0x0002 /* Value is a string */ @@ -12232,22 +12739,10 @@ struct sqlite3_context { }; /* -** A Set structure is used for quick testing to see if a value -** is part of a small set. Sets are used to implement code like -** this: -** x.y IN ('hi','hoo','hum') -*/ -typedef struct Set Set; -struct Set { - Hash hash; /* A set is just a hash table */ - HashElem *prev; /* Previously accessed hash elemen */ -}; - -/* ** An instance of the virtual machine. This structure contains the complete ** state of the virtual machine. ** -** The "sqlite3_stmt" structure pointer that is returned by sqlite3_compile() +** The "sqlite3_stmt" structure pointer that is returned by sqlite3_prepare() ** is really a pointer to an instance of this structure. ** ** The Vdbe.inVtabMethod variable is set to non-zero for the duration of @@ -12260,31 +12755,31 @@ struct Set { */ struct Vdbe { sqlite3 *db; /* The database connection that owns this statement */ - Vdbe *pPrev,*pNext; /* Linked list of VDBEs with the same Vdbe.db */ + Op *aOp; /* Space to hold the virtual machine's program */ + Mem *aMem; /* The memory locations */ + Mem **apArg; /* Arguments to currently executing user function */ + Mem *aColName; /* Column names to return */ + Mem *pResultSet; /* Pointer to an array of results */ + int nMem; /* Number of memory locations currently allocated */ int nOp; /* Number of instructions in the program */ int nOpAlloc; /* Number of slots allocated for aOp[] */ - Op *aOp; /* Space to hold the virtual machine's program */ int nLabel; /* Number of labels used */ int nLabelAlloc; /* Number of slots allocated in aLabel[] */ int *aLabel; /* Space to hold the labels */ - Mem **apArg; /* Arguments to currently executing user function */ - Mem *aColName; /* Column names to return */ - Mem *pResultSet; /* Pointer to an array of results */ u16 nResColumn; /* Number of columns in one row of the result set */ u16 nCursor; /* Number of slots in apCsr[] */ + u32 magic; /* Magic number for sanity checking */ + char *zErrMsg; /* Error message written here */ + Vdbe *pPrev,*pNext; /* Linked list of VDBEs with the same Vdbe.db */ VdbeCursor **apCsr; /* One element of this array for each open cursor */ - u8 errorAction; /* Recovery action to do in case of an error */ - u8 okVar; /* True if azVar[] has been initialized */ - ynVar nVar; /* Number of entries in aVar[] */ Mem *aVar; /* Values for the OP_Variable opcode. */ char **azVar; /* Name of variables */ - u32 magic; /* Magic number for sanity checking */ - int nMem; /* Number of memory locations currently allocated */ - Mem *aMem; /* The memory locations */ + ynVar nVar; /* Number of entries in aVar[] */ + ynVar nzVar; /* Number of entries in azVar[] */ u32 cacheCtr; /* VdbeCursor row cache generation counter */ int pc; /* The program counter */ int rc; /* Value to return */ - char *zErrMsg; /* Error message written here */ + u8 errorAction; /* Recovery action to do in case of an error */ u8 explain; /* True if EXPLAIN present on SQL command */ u8 changeCntOn; /* True to update the change-counter */ u8 expired; /* True if the VM needs to be recompiled */ @@ -12295,15 +12790,17 @@ struct Vdbe { u8 readOnly; /* True for read-only statements */ u8 isPrepareV2; /* True if prepared with prepare_v2() */ int nChange; /* Number of db changes made since last reset */ - int btreeMask; /* Bitmask of db->aDb[] entries referenced */ - i64 startTime; /* Time when query started - used for profiling */ - BtreeMutexArray aMutex; /* An array of Btree used here and needing locks */ + yDbMask btreeMask; /* Bitmask of db->aDb[] entries referenced */ + yDbMask lockMask; /* Subset of btreeMask that requires a lock */ + int iStatement; /* Statement number (or 0 if has not opened stmt) */ int aCounter[3]; /* Counters used by sqlite3_stmt_status() */ - char *zSql; /* Text of the SQL statement that generated this */ - void *pFree; /* Free this when deleting the vdbe */ +#ifndef SQLITE_OMIT_TRACE + i64 startTime; /* Time when query started - used for profiling */ +#endif i64 nFkConstraint; /* Number of imm. FK constraints this VM */ i64 nStmtDefCons; /* Number of def. constraints when stmt started */ - int iStatement; /* Statement number (or 0 if has not opened stmt) */ + char *zSql; /* Text of the SQL statement that generated this */ + void *pFree; /* Free this when deleting the vdbe */ #ifdef SQLITE_DEBUG FILE *trace; /* Write an execution trace here, if not NULL */ #endif @@ -12379,6 +12876,14 @@ SQLITE_PRIVATE void sqlite3VdbeFrameDelete(VdbeFrame*); SQLITE_PRIVATE int sqlite3VdbeFrameRestore(VdbeFrame *); SQLITE_PRIVATE void sqlite3VdbeMemStoreType(Mem *pMem); +#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE>0 +SQLITE_PRIVATE void sqlite3VdbeEnter(Vdbe*); +SQLITE_PRIVATE void sqlite3VdbeLeave(Vdbe*); +#else +# define sqlite3VdbeEnter(X) +# define sqlite3VdbeLeave(X) +#endif + #ifdef SQLITE_DEBUG SQLITE_PRIVATE void sqlite3VdbeMemPrepareToChange(Vdbe*,Mem*); #endif @@ -12389,12 +12894,6 @@ SQLITE_PRIVATE int sqlite3VdbeCheckFk(Vdbe *, int); # define sqlite3VdbeCheckFk(p,i) 0 #endif -#ifndef SQLITE_OMIT_SHARED_CACHE -SQLITE_PRIVATE void sqlite3VdbeMutexArrayEnter(Vdbe *p); -#else -# define sqlite3VdbeMutexArrayEnter(p) -#endif - SQLITE_PRIVATE int sqlite3VdbeMemTranslate(Mem*, u8); #ifdef SQLITE_DEBUG SQLITE_PRIVATE void sqlite3VdbePrintSql(Vdbe*); @@ -12513,6 +13012,22 @@ SQLITE_API int sqlite3_db_status( break; } + case SQLITE_DBSTATUS_LOOKASIDE_HIT: + case SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE: + case SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL: { + testcase( op==SQLITE_DBSTATUS_LOOKASIDE_HIT ); + testcase( op==SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE ); + testcase( op==SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL ); + assert( (op-SQLITE_DBSTATUS_LOOKASIDE_HIT)>=0 ); + assert( (op-SQLITE_DBSTATUS_LOOKASIDE_HIT)<3 ); + *pCurrent = 0; + *pHighwater = db->lookaside.anStat[op - SQLITE_DBSTATUS_LOOKASIDE_HIT]; + if( resetFlag ){ + db->lookaside.anStat[op - SQLITE_DBSTATUS_LOOKASIDE_HIT] = 0; + } + break; + } + /* ** Return an approximation for the amount of memory currently used ** by all pagers associated with the given database connection. The @@ -12544,6 +13059,7 @@ SQLITE_API int sqlite3_db_status( int i; /* Used to iterate through schemas */ int nByte = 0; /* Used to accumulate return value */ + sqlite3BtreeEnterAll(db); db->pnBytesFreed = &nByte; for(i=0; i<db->nDb; i++){ Schema *pSchema = db->aDb[i].pSchema; @@ -12570,6 +13086,7 @@ SQLITE_API int sqlite3_db_status( } } db->pnBytesFreed = 0; + sqlite3BtreeLeaveAll(db); *pHighwater = 0; *pCurrent = nByte; @@ -12656,22 +13173,6 @@ SQLITE_API int sqlite3_db_status( #ifndef SQLITE_OMIT_DATETIME_FUNCS -/* -** On recent Windows platforms, the localtime_s() function is available -** as part of the "Secure CRT". It is essentially equivalent to -** localtime_r() available under most POSIX platforms, except that the -** order of the parameters is reversed. -** -** See http://msdn.microsoft.com/en-us/library/a442x3ye(VS.80).aspx. -** -** If the user has not indicated to use localtime_r() or localtime_s() -** already, check for an MSVC build environment that provides -** localtime_s(). -*/ -#if !defined(HAVE_LOCALTIME_R) && !defined(HAVE_LOCALTIME_S) && \ - defined(_MSC_VER) && defined(_CRT_INSECURE_DEPRECATE) -#define HAVE_LOCALTIME_S 1 -#endif /* ** A structure for holding a single date and time. @@ -13017,15 +13518,83 @@ static void clearYMD_HMS_TZ(DateTime *p){ p->validTZ = 0; } +/* +** On recent Windows platforms, the localtime_s() function is available +** as part of the "Secure CRT". It is essentially equivalent to +** localtime_r() available under most POSIX platforms, except that the +** order of the parameters is reversed. +** +** See http://msdn.microsoft.com/en-us/library/a442x3ye(VS.80).aspx. +** +** If the user has not indicated to use localtime_r() or localtime_s() +** already, check for an MSVC build environment that provides +** localtime_s(). +*/ +#if !defined(HAVE_LOCALTIME_R) && !defined(HAVE_LOCALTIME_S) && \ + defined(_MSC_VER) && defined(_CRT_INSECURE_DEPRECATE) +#define HAVE_LOCALTIME_S 1 +#endif + #ifndef SQLITE_OMIT_LOCALTIME /* -** Compute the difference (in milliseconds) -** between localtime and UTC (a.k.a. GMT) -** for the time value p where p is in UTC. +** The following routine implements the rough equivalent of localtime_r() +** using whatever operating-system specific localtime facility that +** is available. This routine returns 0 on success and +** non-zero on any kind of error. +** +** If the sqlite3GlobalConfig.bLocaltimeFault variable is true then this +** routine will always fail. */ -static sqlite3_int64 localtimeOffset(DateTime *p){ +static int osLocaltime(time_t *t, struct tm *pTm){ + int rc; +#if (!defined(HAVE_LOCALTIME_R) || !HAVE_LOCALTIME_R) \ + && (!defined(HAVE_LOCALTIME_S) || !HAVE_LOCALTIME_S) + struct tm *pX; + sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); + sqlite3_mutex_enter(mutex); + pX = localtime(t); +#ifndef SQLITE_OMIT_BUILTIN_TEST + if( sqlite3GlobalConfig.bLocaltimeFault ) pX = 0; +#endif + if( pX ) *pTm = *pX; + sqlite3_mutex_leave(mutex); + rc = pX==0; +#else +#ifndef SQLITE_OMIT_BUILTIN_TEST + if( sqlite3GlobalConfig.bLocaltimeFault ) return 1; +#endif +#if defined(HAVE_LOCALTIME_R) && HAVE_LOCALTIME_R + rc = localtime_r(t, pTm)==0; +#else + rc = localtime_s(pTm, t); +#endif /* HAVE_LOCALTIME_R */ +#endif /* HAVE_LOCALTIME_R || HAVE_LOCALTIME_S */ + return rc; +} +#endif /* SQLITE_OMIT_LOCALTIME */ + + +#ifndef SQLITE_OMIT_LOCALTIME +/* +** Compute the difference (in milliseconds) between localtime and UTC +** (a.k.a. GMT) for the time value p where p is in UTC. If no error occurs, +** return this value and set *pRc to SQLITE_OK. +** +** Or, if an error does occur, set *pRc to SQLITE_ERROR. The returned value +** is undefined in this case. +*/ +static sqlite3_int64 localtimeOffset( + DateTime *p, /* Date at which to calculate offset */ + sqlite3_context *pCtx, /* Write error here if one occurs */ + int *pRc /* OUT: Error code. SQLITE_OK or ERROR */ +){ DateTime x, y; time_t t; + struct tm sLocal; + + /* Initialize the contents of sLocal to avoid a compiler warning. */ + memset(&sLocal, 0, sizeof(sLocal)); + x = *p; computeYMD_HMS(&x); if( x.Y<1971 || x.Y>=2038 ){ @@ -13043,47 +13612,23 @@ static sqlite3_int64 localtimeOffset(DateTime *p){ x.validJD = 0; computeJD(&x); t = (time_t)(x.iJD/1000 - 21086676*(i64)10000); -#ifdef HAVE_LOCALTIME_R - { - struct tm sLocal; - localtime_r(&t, &sLocal); - y.Y = sLocal.tm_year + 1900; - y.M = sLocal.tm_mon + 1; - y.D = sLocal.tm_mday; - y.h = sLocal.tm_hour; - y.m = sLocal.tm_min; - y.s = sLocal.tm_sec; - } -#elif defined(HAVE_LOCALTIME_S) && HAVE_LOCALTIME_S - { - struct tm sLocal; - localtime_s(&sLocal, &t); - y.Y = sLocal.tm_year + 1900; - y.M = sLocal.tm_mon + 1; - y.D = sLocal.tm_mday; - y.h = sLocal.tm_hour; - y.m = sLocal.tm_min; - y.s = sLocal.tm_sec; - } -#else - { - struct tm *pTm; - sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); - pTm = localtime(&t); - y.Y = pTm->tm_year + 1900; - y.M = pTm->tm_mon + 1; - y.D = pTm->tm_mday; - y.h = pTm->tm_hour; - y.m = pTm->tm_min; - y.s = pTm->tm_sec; - sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); + if( osLocaltime(&t, &sLocal) ){ + sqlite3_result_error(pCtx, "local time unavailable", -1); + *pRc = SQLITE_ERROR; + return 0; } -#endif + y.Y = sLocal.tm_year + 1900; + y.M = sLocal.tm_mon + 1; + y.D = sLocal.tm_mday; + y.h = sLocal.tm_hour; + y.m = sLocal.tm_min; + y.s = sLocal.tm_sec; y.validYMD = 1; y.validHMS = 1; y.validJD = 0; y.validTZ = 0; computeJD(&y); + *pRc = SQLITE_OK; return y.iJD - x.iJD; } #endif /* SQLITE_OMIT_LOCALTIME */ @@ -13107,9 +13652,12 @@ static sqlite3_int64 localtimeOffset(DateTime *p){ ** localtime ** utc ** -** Return 0 on success and 1 if there is any kind of error. +** Return 0 on success and 1 if there is any kind of error. If the error +** is in a system call (i.e. localtime()), then an error message is written +** to context pCtx. If the error is an unrecognized modifier, no error is +** written to pCtx. */ -static int parseModifier(const char *zMod, DateTime *p){ +static int parseModifier(sqlite3_context *pCtx, const char *zMod, DateTime *p){ int rc = 1; int n; double r; @@ -13129,9 +13677,8 @@ static int parseModifier(const char *zMod, DateTime *p){ */ if( strcmp(z, "localtime")==0 ){ computeJD(p); - p->iJD += localtimeOffset(p); + p->iJD += localtimeOffset(p, pCtx, &rc); clearYMD_HMS_TZ(p); - rc = 0; } break; } @@ -13152,11 +13699,12 @@ static int parseModifier(const char *zMod, DateTime *p){ else if( strcmp(z, "utc")==0 ){ sqlite3_int64 c1; computeJD(p); - c1 = localtimeOffset(p); - p->iJD -= c1; - clearYMD_HMS_TZ(p); - p->iJD += c1 - localtimeOffset(p); - rc = 0; + c1 = localtimeOffset(p, pCtx, &rc); + if( rc==SQLITE_OK ){ + p->iJD -= c1; + clearYMD_HMS_TZ(p); + p->iJD += c1 - localtimeOffset(p, pCtx, &rc); + } } #endif break; @@ -13337,9 +13885,8 @@ static int isDate( } } for(i=1; i<argc; i++){ - if( (z = sqlite3_value_text(argv[i]))==0 || parseModifier((char*)z, p) ){ - return 1; - } + z = sqlite3_value_text(argv[i]); + if( z==0 || parseModifier(context, (char*)z, p) ) return 1; } return 0; } @@ -15681,7 +16228,7 @@ static SQLITE_WSD struct Mem5Global { */ u8 *aCtrl; -} mem5 = { 0 }; +} mem5; /* ** Access the static variable through a macro for SQLITE_OMIT_WSD @@ -15996,7 +16543,7 @@ static int memsys5Roundup(int n){ */ static int memsys5Log(int iValue){ int iLog; - for(iLog=0; (1<<iLog)<iValue; iLog++); + for(iLog=0; (iLog<(int)((sizeof(int)*8)-1)) && (1<<iLog)<iValue; iLog++); return iLog; } @@ -16027,6 +16574,7 @@ static int memsys5Init(void *NotUsed){ zByte = (u8*)sqlite3GlobalConfig.pHeap; assert( zByte!=0 ); /* sqlite3_config() does not allow otherwise */ + /* boundaries on sqlite3GlobalConfig.mnReq are enforced in sqlite3_config() */ nMinLog = memsys5Log(sqlite3GlobalConfig.mnReq); mem5.szAtom = (1<<nMinLog); while( (int)sizeof(Mem5Link)>mem5.szAtom ){ @@ -16530,11 +17078,16 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){ struct sqlite3_mutex { HMTX mutex; /* Mutex controlling the lock */ int id; /* Mutex type */ - int nRef; /* Number of references */ - TID owner; /* Thread holding this mutex */ +#ifdef SQLITE_DEBUG + int trace; /* True to trace changes */ +#endif }; -#define OS2_MUTEX_INITIALIZER 0,0,0,0 +#ifdef SQLITE_DEBUG +#define SQLITE3_MUTEX_INITIALIZER { 0, 0, 0 } +#else +#define SQLITE3_MUTEX_INITIALIZER { 0, 0 } +#endif /* ** Initialize and deinitialize the mutex subsystem. @@ -16550,11 +17103,14 @@ static int os2MutexEnd(void){ return SQLITE_OK; } ** to sqlite3_mutex_alloc() is one of these integer constants: ** ** <ul> -** <li> SQLITE_MUTEX_FAST 0 -** <li> SQLITE_MUTEX_RECURSIVE 1 -** <li> SQLITE_MUTEX_STATIC_MASTER 2 -** <li> SQLITE_MUTEX_STATIC_MEM 3 -** <li> SQLITE_MUTEX_STATIC_PRNG 4 +** <li> SQLITE_MUTEX_FAST +** <li> SQLITE_MUTEX_RECURSIVE +** <li> SQLITE_MUTEX_STATIC_MASTER +** <li> SQLITE_MUTEX_STATIC_MEM +** <li> SQLITE_MUTEX_STATIC_MEM2 +** <li> SQLITE_MUTEX_STATIC_PRNG +** <li> SQLITE_MUTEX_STATIC_LRU +** <li> SQLITE_MUTEX_STATIC_LRU2 ** </ul> ** ** The first two constants cause sqlite3_mutex_alloc() to create @@ -16568,7 +17124,7 @@ static int os2MutexEnd(void){ return SQLITE_OK; } ** might return such a mutex in response to SQLITE_MUTEX_FAST. ** ** The other allowed parameters to sqlite3_mutex_alloc() each return -** a pointer to a static preexisting mutex. Three static mutexes are +** a pointer to a static preexisting mutex. Six static mutexes are ** used by the current version of SQLite. Future versions of SQLite ** may add additional static mutexes. Static mutexes are for internal ** use by SQLite only. Applications that use SQLite mutexes should @@ -16598,13 +17154,13 @@ static sqlite3_mutex *os2MutexAlloc(int iType){ } default: { static volatile int isInit = 0; - static sqlite3_mutex staticMutexes[] = { - { OS2_MUTEX_INITIALIZER, }, - { OS2_MUTEX_INITIALIZER, }, - { OS2_MUTEX_INITIALIZER, }, - { OS2_MUTEX_INITIALIZER, }, - { OS2_MUTEX_INITIALIZER, }, - { OS2_MUTEX_INITIALIZER, }, + static sqlite3_mutex staticMutexes[6] = { + SQLITE3_MUTEX_INITIALIZER, + SQLITE3_MUTEX_INITIALIZER, + SQLITE3_MUTEX_INITIALIZER, + SQLITE3_MUTEX_INITIALIZER, + SQLITE3_MUTEX_INITIALIZER, + SQLITE3_MUTEX_INITIALIZER, }; if ( !isInit ){ APIRET rc; @@ -16650,9 +17206,14 @@ static sqlite3_mutex *os2MutexAlloc(int iType){ ** SQLite is careful to deallocate every mutex that it allocates. */ static void os2MutexFree(sqlite3_mutex *p){ - if( p==0 ) return; - assert( p->nRef==0 ); +#ifdef SQLITE_DEBUG + TID tid; + PID pid; + ULONG ulCount; + DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount); + assert( ulCount==0 ); assert( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE ); +#endif DosCloseMutexSem( p->mutex ); sqlite3_free( p ); } @@ -16667,26 +17228,29 @@ static int os2MutexHeld(sqlite3_mutex *p){ PID pid; ULONG ulCount; PTIB ptib; - if( p!=0 ) { - DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount); - } else { - DosGetInfoBlocks(&ptib, NULL); - tid = ptib->tib_ptib2->tib2_ultid; - } - return p==0 || (p->nRef!=0 && p->owner==tid); + DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount); + if( ulCount==0 || ( ulCount>1 && p->id!=SQLITE_MUTEX_RECURSIVE ) ) + return 0; + DosGetInfoBlocks(&ptib, NULL); + return tid==ptib->tib_ptib2->tib2_ultid; } static int os2MutexNotheld(sqlite3_mutex *p){ TID tid; PID pid; ULONG ulCount; PTIB ptib; - if( p!= 0 ) { - DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount); - } else { - DosGetInfoBlocks(&ptib, NULL); - tid = ptib->tib_ptib2->tib2_ultid; - } - return p==0 || p->nRef==0 || p->owner!=tid; + DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount); + if( ulCount==0 ) + return 1; + DosGetInfoBlocks(&ptib, NULL); + return tid!=ptib->tib_ptib2->tib2_ultid; +} +static void os2MutexTrace(sqlite3_mutex *p, char *pAction){ + TID tid; + PID pid; + ULONG ulCount; + DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount); + printf("%s mutex %p (%d) with nRef=%ld\n", pAction, (void*)p, p->trace, ulCount); } #endif @@ -16702,32 +17266,21 @@ static int os2MutexNotheld(sqlite3_mutex *p){ ** more than once, the behavior is undefined. */ static void os2MutexEnter(sqlite3_mutex *p){ - TID tid; - PID holder1; - ULONG holder2; - if( p==0 ) return; assert( p->id==SQLITE_MUTEX_RECURSIVE || os2MutexNotheld(p) ); DosRequestMutexSem(p->mutex, SEM_INDEFINITE_WAIT); - DosQueryMutexSem(p->mutex, &holder1, &tid, &holder2); - p->owner = tid; - p->nRef++; +#ifdef SQLITE_DEBUG + if( p->trace ) os2MutexTrace(p, "enter"); +#endif } static int os2MutexTry(sqlite3_mutex *p){ - int rc; - TID tid; - PID holder1; - ULONG holder2; - if( p==0 ) return SQLITE_OK; + int rc = SQLITE_BUSY; assert( p->id==SQLITE_MUTEX_RECURSIVE || os2MutexNotheld(p) ); - if( DosRequestMutexSem(p->mutex, SEM_IMMEDIATE_RETURN) == NO_ERROR) { - DosQueryMutexSem(p->mutex, &holder1, &tid, &holder2); - p->owner = tid; - p->nRef++; + if( DosRequestMutexSem(p->mutex, SEM_IMMEDIATE_RETURN) == NO_ERROR ) { rc = SQLITE_OK; - } else { - rc = SQLITE_BUSY; +#ifdef SQLITE_DEBUG + if( p->trace ) os2MutexTrace(p, "try"); +#endif } - return rc; } @@ -16738,16 +17291,11 @@ static int os2MutexTry(sqlite3_mutex *p){ ** is not currently allocated. SQLite will never do either. */ static void os2MutexLeave(sqlite3_mutex *p){ - TID tid; - PID holder1; - ULONG holder2; - if( p==0 ) return; - assert( p->nRef>0 ); - DosQueryMutexSem(p->mutex, &holder1, &tid, &holder2); - assert( p->owner==tid ); - p->nRef--; - assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE ); + assert( os2MutexHeld(p) ); DosReleaseMutexSem(p->mutex); +#ifdef SQLITE_DEBUG + if( p->trace ) os2MutexTrace(p, "leave"); +#endif } SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){ @@ -16762,6 +17310,9 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){ #ifdef SQLITE_DEBUG os2MutexHeld, os2MutexNotheld +#else + 0, + 0 #endif }; @@ -16871,7 +17422,7 @@ static int pthreadMutexEnd(void){ return SQLITE_OK; } ** <li> SQLITE_MUTEX_STATIC_MEM2 ** <li> SQLITE_MUTEX_STATIC_PRNG ** <li> SQLITE_MUTEX_STATIC_LRU -** <li> SQLITE_MUTEX_STATIC_LRU2 +** <li> SQLITE_MUTEX_STATIC_PMEM ** </ul> ** ** The first two constants cause sqlite3_mutex_alloc() to create @@ -17281,7 +17832,7 @@ static int winMutexEnd(void){ ** <li> SQLITE_MUTEX_STATIC_MEM2 ** <li> SQLITE_MUTEX_STATIC_PRNG ** <li> SQLITE_MUTEX_STATIC_LRU -** <li> SQLITE_MUTEX_STATIC_LRU2 +** <li> SQLITE_MUTEX_STATIC_PMEM ** </ul> ** ** The first two constants cause sqlite3_mutex_alloc() to create @@ -17405,7 +17956,7 @@ static int winMutexTry(sqlite3_mutex *p){ #endif #ifdef SQLITE_DEBUG if( rc==SQLITE_OK && p->trace ){ - printf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef); + printf("try mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef); } #endif return rc; @@ -17724,7 +18275,7 @@ static int mallocWithAlarm(int n, void **pp){ sqlite3StatusSet(SQLITE_STATUS_MALLOC_SIZE, n); if( mem0.alarmCallback!=0 ){ int nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); - if( nUsed+nFull >= mem0.alarmThreshold ){ + if( nUsed >= mem0.alarmThreshold - nFull ){ mem0.nearlyFull = 1; sqlite3MallocAlarm(nFull); }else{ @@ -17862,7 +18413,7 @@ SQLITE_PRIVATE void sqlite3ScratchFree(void *p){ pSlot->pNext = mem0.pScratchFree; mem0.pScratchFree = pSlot; mem0.nScratchFree++; - assert( mem0.nScratchFree<=sqlite3GlobalConfig.nScratch ); + assert( mem0.nScratchFree <= (u32)sqlite3GlobalConfig.nScratch ); sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_USED, -1); sqlite3_mutex_leave(mem0.mutex); }else{ @@ -17965,7 +18516,7 @@ SQLITE_PRIVATE void sqlite3DbFree(sqlite3 *db, void *p){ ** Change the size of an existing memory allocation */ SQLITE_PRIVATE void *sqlite3Realloc(void *pOld, int nBytes){ - int nOld, nNew; + int nOld, nNew, nDiff; void *pNew; if( pOld==0 ){ return sqlite3Malloc(nBytes); /* IMP: R-28354-25769 */ @@ -17988,9 +18539,10 @@ SQLITE_PRIVATE void *sqlite3Realloc(void *pOld, int nBytes){ }else if( sqlite3GlobalConfig.bMemstat ){ sqlite3_mutex_enter(mem0.mutex); sqlite3StatusSet(SQLITE_STATUS_MALLOC_SIZE, nBytes); - if( sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED)+nNew-nOld >= - mem0.alarmThreshold ){ - sqlite3MallocAlarm(nNew-nOld); + nDiff = nNew - nOld; + if( sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED) >= + mem0.alarmThreshold-nDiff ){ + sqlite3MallocAlarm(nDiff); } assert( sqlite3MemdebugHasType(pOld, MEMTYPE_HEAP) ); assert( sqlite3MemdebugNoType(pOld, ~MEMTYPE_HEAP) ); @@ -18074,14 +18626,20 @@ SQLITE_PRIVATE void *sqlite3DbMallocRaw(sqlite3 *db, int n){ if( db->mallocFailed ){ return 0; } - if( db->lookaside.bEnabled && n<=db->lookaside.sz - && (pBuf = db->lookaside.pFree)!=0 ){ - db->lookaside.pFree = pBuf->pNext; - db->lookaside.nOut++; - if( db->lookaside.nOut>db->lookaside.mxOut ){ - db->lookaside.mxOut = db->lookaside.nOut; + if( db->lookaside.bEnabled ){ + if( n>db->lookaside.sz ){ + db->lookaside.anStat[1]++; + }else if( (pBuf = db->lookaside.pFree)==0 ){ + db->lookaside.anStat[2]++; + }else{ + db->lookaside.pFree = pBuf->pNext; + db->lookaside.nOut++; + db->lookaside.anStat[0]++; + if( db->lookaside.nOut>db->lookaside.mxOut ){ + db->lookaside.mxOut = db->lookaside.nOut; + } + return (void*)pBuf; } - return (void*)pBuf; } } #else @@ -18630,7 +19188,11 @@ SQLITE_PRIVATE void sqlite3VXPrintf( v = va_arg(ap,int); } if( v<0 ){ - longvalue = -v; + if( v==SMALLEST_INT64 ){ + longvalue = ((u64)1)<<63; + }else{ + longvalue = -v; + } prefix = '-'; }else{ longvalue = v; @@ -18993,6 +19555,7 @@ SQLITE_PRIVATE void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){ return; } }else{ + char *zOld = (p->zText==p->zBase ? 0 : p->zText); i64 szNew = p->nChar; szNew += N + 1; if( szNew > p->mxAlloc ){ @@ -19003,13 +19566,12 @@ SQLITE_PRIVATE void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){ p->nAlloc = (int)szNew; } if( p->useMalloc==1 ){ - zNew = sqlite3DbMallocRaw(p->db, p->nAlloc ); + zNew = sqlite3DbRealloc(p->db, zOld, p->nAlloc); }else{ - zNew = sqlite3_malloc(p->nAlloc); + zNew = sqlite3_realloc(zOld, p->nAlloc); } if( zNew ){ - memcpy(zNew, p->zText, p->nChar); - sqlite3StrAccumReset(p); + if( zOld==0 ) memcpy(zNew, p->zText, p->nChar); p->zText = zNew; }else{ p->mallocFailed = 1; @@ -19164,21 +19726,28 @@ SQLITE_API char *sqlite3_mprintf(const char *zFormat, ...){ ** current locale settings. This is important for SQLite because we ** are not able to use a "," as the decimal point in place of "." as ** specified by some locales. +** +** Oops: The first two arguments of sqlite3_snprintf() are backwards +** from the snprintf() standard. Unfortunately, it is too late to change +** this without breaking compatibility, so we just have to live with the +** mistake. +** +** sqlite3_vsnprintf() is the varargs version. */ -SQLITE_API char *sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){ - char *z; - va_list ap; +SQLITE_API char *sqlite3_vsnprintf(int n, char *zBuf, const char *zFormat, va_list ap){ StrAccum acc; - - if( n<=0 ){ - return zBuf; - } + if( n<=0 ) return zBuf; sqlite3StrAccumInit(&acc, zBuf, n, 0); acc.useMalloc = 0; - va_start(ap,zFormat); sqlite3VXPrintf(&acc, 0, zFormat, ap); + return sqlite3StrAccumFinish(&acc); +} +SQLITE_API char *sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){ + char *z; + va_list ap; + va_start(ap,zFormat); + z = sqlite3_vsnprintf(n, zBuf, zFormat, ap); va_end(ap); - z = sqlite3StrAccumFinish(&acc); return z; } @@ -19558,11 +20127,11 @@ static const unsigned char sqlite3Utf8Trans1[] = { || (c&0xFFFFF800)==0xD800 \ || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \ } -SQLITE_PRIVATE int sqlite3Utf8Read( +SQLITE_PRIVATE u32 sqlite3Utf8Read( const unsigned char *zIn, /* First byte of UTF-8 character */ const unsigned char **pzNext /* Write first byte past UTF-8 char here */ ){ - int c; + unsigned int c; /* Same as READ_UTF8() above but without the zTerm parameter. ** For this routine, we assume the UTF8 string is always zero-terminated. @@ -19805,15 +20374,15 @@ SQLITE_PRIVATE int sqlite3Utf8CharLen(const char *zIn, int nByte){ ** This has the effect of making sure that the string is well-formed ** UTF-8. Miscoded characters are removed. ** -** The translation is done in-place (since it is impossible for the -** correct UTF-8 encoding to be longer than a malformed encoding). +** The translation is done in-place and aborted if the output +** overruns the input. */ SQLITE_PRIVATE int sqlite3Utf8To8(unsigned char *zIn){ unsigned char *zOut = zIn; unsigned char *zStart = zIn; u32 c; - while( zIn[0] ){ + while( zIn[0] && zOut<=zIn ){ c = sqlite3Utf8Read(zIn, (const u8**)&zIn); if( c!=0xfffd ){ WRITE_UTF8(zOut, c); @@ -19982,8 +20551,8 @@ SQLITE_PRIVATE void sqlite3UtfSelfTest(void){ */ #ifdef SQLITE_COVERAGE_TEST SQLITE_PRIVATE void sqlite3Coverage(int x){ - static int dummy = 0; - dummy += x; + static unsigned dummy = 0; + dummy += (unsigned)x; } #endif @@ -20397,14 +20966,17 @@ static int compare2pow63(const char *zNum, int incr){ /* -** Convert zNum to a 64-bit signed integer and write -** the value of the integer into *pNum. -** If zNum is exactly 9223372036854665808, return 2. -** This is a special case as the context will determine -** if it is too big (used as a negative). -** If zNum is not an integer or is an integer that -** is too large to be expressed with 64 bits, -** then return 1. Otherwise return 0. +** Convert zNum to a 64-bit signed integer. +** +** If the zNum value is representable as a 64-bit twos-complement +** integer, then write that value into *pNum and return 0. +** +** If zNum is exactly 9223372036854665808, return 2. This special +** case is broken out because while 9223372036854665808 cannot be a +** signed 64-bit integer, its negative -9223372036854665808 can be. +** +** If zNum is too big for a 64-bit integer and is not +** 9223372036854665808 then return 1. ** ** length is the number of bytes in the string (bytes, not characters). ** The string is not necessarily zero-terminated. The encoding is @@ -20412,7 +20984,7 @@ static int compare2pow63(const char *zNum, int incr){ */ SQLITE_PRIVATE int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc){ int incr = (enc==SQLITE_UTF8?1:2); - i64 v = 0; + u64 u = 0; int neg = 0; /* assume positive */ int i; int c = 0; @@ -20420,20 +20992,26 @@ SQLITE_PRIVATE int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc const char *zEnd = zNum + length; if( enc==SQLITE_UTF16BE ) zNum++; while( zNum<zEnd && sqlite3Isspace(*zNum) ) zNum+=incr; - if( zNum>=zEnd ) goto do_atoi_calc; - if( *zNum=='-' ){ - neg = 1; - zNum+=incr; - }else if( *zNum=='+' ){ - zNum+=incr; + if( zNum<zEnd ){ + if( *zNum=='-' ){ + neg = 1; + zNum+=incr; + }else if( *zNum=='+' ){ + zNum+=incr; + } } -do_atoi_calc: zStart = zNum; while( zNum<zEnd && zNum[0]=='0' ){ zNum+=incr; } /* Skip leading zeros. */ for(i=0; &zNum[i]<zEnd && (c=zNum[i])>='0' && c<='9'; i+=incr){ - v = v*10 + c - '0'; + u = u*10 + c - '0'; + } + if( u>LARGEST_INT64 ){ + *pNum = SMALLEST_INT64; + }else if( neg ){ + *pNum = -(i64)u; + }else{ + *pNum = (i64)u; } - *pNum = neg ? -v : v; testcase( i==18 ); testcase( i==19 ); testcase( i==20 ); @@ -20443,14 +21021,25 @@ do_atoi_calc: return 1; }else if( i<19*incr ){ /* Less than 19 digits, so we know that it fits in 64 bits */ + assert( u<=LARGEST_INT64 ); return 0; }else{ - /* 19-digit numbers must be no larger than 9223372036854775807 if positive - ** or 9223372036854775808 if negative. Note that 9223372036854665808 - ** is 2^63. Return 1 if to large */ - c=compare2pow63(zNum, incr); - if( c==0 && neg==0 ) return 2; /* too big, exactly 9223372036854665808 */ - return c<neg ? 0 : 1; + /* zNum is a 19-digit numbers. Compare it against 9223372036854775808. */ + c = compare2pow63(zNum, incr); + if( c<0 ){ + /* zNum is less than 9223372036854775808 so it fits */ + assert( u<=LARGEST_INT64 ); + return 0; + }else if( c>0 ){ + /* zNum is greater than 9223372036854775808 so it overflows */ + return 1; + }else{ + /* zNum is exactly 9223372036854775808. Fits if negative. The + ** special case 2 overflow if positive */ + assert( u-1==LARGEST_INT64 ); + assert( (*pNum)==SMALLEST_INT64 ); + return neg ? 0 : 2; + } } } @@ -20919,13 +21508,12 @@ SQLITE_PRIVATE void sqlite3Put4byte(unsigned char *p, u32 v){ -#if !defined(SQLITE_OMIT_BLOB_LITERAL) || defined(SQLITE_HAS_CODEC) /* ** Translate a single byte of Hex into an integer. ** This routine only works if h really is a valid hexadecimal ** character: 0..9a..fA..F */ -static u8 hexToInt(int h){ +SQLITE_PRIVATE u8 sqlite3HexToInt(int h){ assert( (h>='0' && h<='9') || (h>='a' && h<='f') || (h>='A' && h<='F') ); #ifdef SQLITE_ASCII h += 9*(1&(h>>6)); @@ -20935,7 +21523,6 @@ static u8 hexToInt(int h){ #endif return (u8)(h & 0xf); } -#endif /* !SQLITE_OMIT_BLOB_LITERAL || SQLITE_HAS_CODEC */ #if !defined(SQLITE_OMIT_BLOB_LITERAL) || defined(SQLITE_HAS_CODEC) /* @@ -20952,7 +21539,7 @@ SQLITE_PRIVATE void *sqlite3HexToBlob(sqlite3 *db, const char *z, int n){ n--; if( zBlob ){ for(i=0; i<n; i+=2){ - zBlob[i/2] = (hexToInt(z[i])<<4) | hexToInt(z[i+1]); + zBlob[i/2] = (sqlite3HexToInt(z[i])<<4) | sqlite3HexToInt(z[i+1]); } zBlob[i/2] = 0; } @@ -21017,6 +21604,100 @@ SQLITE_PRIVATE int sqlite3SafetyCheckSickOrOk(sqlite3 *db){ } } +/* +** Attempt to add, substract, or multiply the 64-bit signed value iB against +** the other 64-bit signed integer at *pA and store the result in *pA. +** Return 0 on success. Or if the operation would have resulted in an +** overflow, leave *pA unchanged and return 1. +*/ +SQLITE_PRIVATE int sqlite3AddInt64(i64 *pA, i64 iB){ + i64 iA = *pA; + testcase( iA==0 ); testcase( iA==1 ); + testcase( iB==-1 ); testcase( iB==0 ); + if( iB>=0 ){ + testcase( iA>0 && LARGEST_INT64 - iA == iB ); + testcase( iA>0 && LARGEST_INT64 - iA == iB - 1 ); + if( iA>0 && LARGEST_INT64 - iA < iB ) return 1; + *pA += iB; + }else{ + testcase( iA<0 && -(iA + LARGEST_INT64) == iB + 1 ); + testcase( iA<0 && -(iA + LARGEST_INT64) == iB + 2 ); + if( iA<0 && -(iA + LARGEST_INT64) > iB + 1 ) return 1; + *pA += iB; + } + return 0; +} +SQLITE_PRIVATE int sqlite3SubInt64(i64 *pA, i64 iB){ + testcase( iB==SMALLEST_INT64+1 ); + if( iB==SMALLEST_INT64 ){ + testcase( (*pA)==(-1) ); testcase( (*pA)==0 ); + if( (*pA)>=0 ) return 1; + *pA -= iB; + return 0; + }else{ + return sqlite3AddInt64(pA, -iB); + } +} +#define TWOPOWER32 (((i64)1)<<32) +#define TWOPOWER31 (((i64)1)<<31) +SQLITE_PRIVATE int sqlite3MulInt64(i64 *pA, i64 iB){ + i64 iA = *pA; + i64 iA1, iA0, iB1, iB0, r; + + iA1 = iA/TWOPOWER32; + iA0 = iA % TWOPOWER32; + iB1 = iB/TWOPOWER32; + iB0 = iB % TWOPOWER32; + if( iA1*iB1 != 0 ) return 1; + assert( iA1*iB0==0 || iA0*iB1==0 ); + r = iA1*iB0 + iA0*iB1; + testcase( r==(-TWOPOWER31)-1 ); + testcase( r==(-TWOPOWER31) ); + testcase( r==TWOPOWER31 ); + testcase( r==TWOPOWER31-1 ); + if( r<(-TWOPOWER31) || r>=TWOPOWER31 ) return 1; + r *= TWOPOWER32; + if( sqlite3AddInt64(&r, iA0*iB0) ) return 1; + *pA = r; + return 0; +} + +/* +** Compute the absolute value of a 32-bit signed integer, of possible. Or +** if the integer has a value of -2147483648, return +2147483647 +*/ +SQLITE_PRIVATE int sqlite3AbsInt32(int x){ + if( x>=0 ) return x; + if( x==(int)0x80000000 ) return 0x7fffffff; + return -x; +} + +#ifdef SQLITE_ENABLE_8_3_NAMES +/* +** If SQLITE_ENABLE_8_3_NAME is set at compile-time and if the database +** filename in zBaseFilename is a URI with the "8_3_names=1" parameter and +** if filename in z[] has a suffix (a.k.a. "extension") that is longer than +** three characters, then shorten the suffix on z[] to be the last three +** characters of the original suffix. +** +** Examples: +** +** test.db-journal => test.nal +** test.db-wal => test.wal +** test.db-shm => test.shm +*/ +SQLITE_PRIVATE void sqlite3FileSuffix3(const char *zBaseFilename, char *z){ + const char *zOk; + zOk = sqlite3_uri_parameter(zBaseFilename, "8_3_names"); + if( zOk && sqlite3GetBoolean(zOk) ){ + int i, sz; + sz = sqlite3Strlen30(z); + for(i=sz-1; i>0 && z[i]!='/' && z[i]!='.'; i--){} + if( z[i]=='.' && ALWAYS(sz>i+4) ) memcpy(&z[i+1], &z[sz-3], 4); + } +} +#endif + /************** End of util.c ************************************************/ /************** Begin file hash.c ********************************************/ /* @@ -21715,20 +22396,35 @@ SQLITE_API int sqlite3_open_file_count = 0; /************** End of os_common.h *******************************************/ /************** Continuing where we left off in os_os2.c *********************/ +/* Forward references */ +typedef struct os2File os2File; /* The file structure */ +typedef struct os2ShmNode os2ShmNode; /* A shared descritive memory node */ +typedef struct os2ShmLink os2ShmLink; /* A connection to shared-memory */ + /* ** The os2File structure is subclass of sqlite3_file specific for the OS/2 ** protability layer. */ -typedef struct os2File os2File; struct os2File { const sqlite3_io_methods *pMethod; /* Always the first entry */ HFILE h; /* Handle for accessing the file */ - char* pathToDel; /* Name of file to delete on close, NULL if not */ - unsigned char locktype; /* Type of lock currently held on this file */ + int flags; /* Flags provided to os2Open() */ + int locktype; /* Type of lock currently held on this file */ + int szChunk; /* Chunk size configured by FCNTL_CHUNK_SIZE */ + char *zFullPathCp; /* Full path name of this file */ + os2ShmLink *pShmLink; /* Instance of shared memory on this file */ }; #define LOCK_TIMEOUT 10L /* the default locking timeout */ +/* +** Missing from some versions of the OS/2 toolkit - +** used to allocate from high memory if possible +*/ +#ifndef OBJ_ANY +# define OBJ_ANY 0x00000400 +#endif + /***************************************************************************** ** The next group of routines implement the I/O methods specified ** by the sqlite3_io_methods object. @@ -21738,21 +22434,24 @@ struct os2File { ** Close a file. */ static int os2Close( sqlite3_file *id ){ - APIRET rc = NO_ERROR; - os2File *pFile; - if( id && (pFile = (os2File*)id) != 0 ){ - OSTRACE(( "CLOSE %d\n", pFile->h )); - rc = DosClose( pFile->h ); - pFile->locktype = NO_LOCK; - if( pFile->pathToDel != NULL ){ - rc = DosForceDelete( (PSZ)pFile->pathToDel ); - free( pFile->pathToDel ); - pFile->pathToDel = NULL; - } - id = 0; - OpenCounter( -1 ); - } + APIRET rc; + os2File *pFile = (os2File*)id; + assert( id!=0 ); + OSTRACE(( "CLOSE %d (%s)\n", pFile->h, pFile->zFullPathCp )); + + rc = DosClose( pFile->h ); + + if( pFile->flags & SQLITE_OPEN_DELETEONCLOSE ) + DosForceDelete( (PSZ)pFile->zFullPathCp ); + + free( pFile->zFullPathCp ); + pFile->zFullPathCp = NULL; + pFile->locktype = NO_LOCK; + pFile->h = (HFILE)-1; + pFile->flags = 0; + + OpenCounter( -1 ); return rc == NO_ERROR ? SQLITE_OK : SQLITE_IOERR; } @@ -21825,10 +22524,21 @@ static int os2Write( ** Truncate an open file to a specified size */ static int os2Truncate( sqlite3_file *id, i64 nByte ){ - APIRET rc = NO_ERROR; + APIRET rc; os2File *pFile = (os2File*)id; + assert( id!=0 ); OSTRACE(( "TRUNCATE %d %lld\n", pFile->h, nByte )); SimulateIOError( return SQLITE_IOERR_TRUNCATE ); + + /* If the user has configured a chunk-size for this file, truncate the + ** file so that it consists of an integer number of chunks (i.e. the + ** actual file size after the operation may be larger than the requested + ** size). + */ + if( pFile->szChunk ){ + nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk; + } + rc = DosSetFileSize( pFile->h, nByte ); return rc == NO_ERROR ? SQLITE_OK : SQLITE_IOERR_TRUNCATE; } @@ -22192,8 +22902,22 @@ static int os2FileControl(sqlite3_file *id, int op, void *pArg){ ((os2File*)id)->h, ((os2File*)id)->locktype )); return SQLITE_OK; } + case SQLITE_FCNTL_CHUNK_SIZE: { + ((os2File*)id)->szChunk = *(int*)pArg; + return SQLITE_OK; + } + case SQLITE_FCNTL_SIZE_HINT: { + sqlite3_int64 sz = *(sqlite3_int64*)pArg; + SimulateIOErrorBenign(1); + os2Truncate(id, sz); + SimulateIOErrorBenign(0); + return SQLITE_OK; + } + case SQLITE_FCNTL_SYNC_OMITTED: { + return SQLITE_OK; + } } - return SQLITE_ERROR; + return SQLITE_NOTFOUND; } /* @@ -22207,6 +22931,7 @@ static int os2FileControl(sqlite3_file *id, int op, void *pArg){ ** same for both. */ static int os2SectorSize(sqlite3_file *id){ + UNUSED_PARAMETER(id); return SQLITE_DEFAULT_SECTOR_SIZE; } @@ -22214,7 +22939,8 @@ static int os2SectorSize(sqlite3_file *id){ ** Return a vector of device characteristics. */ static int os2DeviceCharacteristics(sqlite3_file *id){ - return 0; + UNUSED_PARAMETER(id); + return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN; } @@ -22301,26 +23027,682 @@ char *convertCpPathToUtf8( const char *in ){ return out; } + +#ifndef SQLITE_OMIT_WAL + +/* +** Use main database file for interprocess locking. If un-defined +** a separate file is created for this purpose. The file will be +** used only to set file locks. There will be no data written to it. +*/ +#define SQLITE_OS2_NO_WAL_LOCK_FILE + +#if 0 +static void _ERR_TRACE( const char *fmt, ... ) { + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + fflush(stderr); +} +#define ERR_TRACE(rc, msg) \ + if( (rc) != SQLITE_OK ) _ERR_TRACE msg; +#else +#define ERR_TRACE(rc, msg) +#endif + +/* +** Helper functions to obtain and relinquish the global mutex. The +** global mutex is used to protect os2ShmNodeList. +** +** Function os2ShmMutexHeld() is used to assert() that the global mutex +** is held when required. This function is only used as part of assert() +** statements. e.g. +** +** os2ShmEnterMutex() +** assert( os2ShmMutexHeld() ); +** os2ShmLeaveMutex() +*/ +static void os2ShmEnterMutex(void){ + sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); +} +static void os2ShmLeaveMutex(void){ + sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); +} +#ifdef SQLITE_DEBUG +static int os2ShmMutexHeld(void) { + return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); +} +int GetCurrentProcessId(void) { + PPIB pib; + DosGetInfoBlocks(NULL, &pib); + return (int)pib->pib_ulpid; +} +#endif + +/* +** Object used to represent a the shared memory area for a single log file. +** When multiple threads all reference the same log-summary, each thread has +** its own os2File object, but they all point to a single instance of this +** object. In other words, each log-summary is opened only once per process. +** +** os2ShmMutexHeld() must be true when creating or destroying +** this object or while reading or writing the following fields: +** +** nRef +** pNext +** +** The following fields are read-only after the object is created: +** +** szRegion +** hLockFile +** shmBaseName +** +** Either os2ShmNode.mutex must be held or os2ShmNode.nRef==0 and +** os2ShmMutexHeld() is true when reading or writing any other field +** in this structure. +** +*/ +struct os2ShmNode { + sqlite3_mutex *mutex; /* Mutex to access this object */ + os2ShmNode *pNext; /* Next in list of all os2ShmNode objects */ + + int szRegion; /* Size of shared-memory regions */ + + int nRegion; /* Size of array apRegion */ + void **apRegion; /* Array of pointers to shared-memory regions */ + + int nRef; /* Number of os2ShmLink objects pointing to this */ + os2ShmLink *pFirst; /* First os2ShmLink object pointing to this */ + + HFILE hLockFile; /* File used for inter-process memory locking */ + char shmBaseName[1]; /* Name of the memory object !!! must last !!! */ +}; + + +/* +** Structure used internally by this VFS to record the state of an +** open shared memory connection. +** +** The following fields are initialized when this object is created and +** are read-only thereafter: +** +** os2Shm.pShmNode +** os2Shm.id +** +** All other fields are read/write. The os2Shm.pShmNode->mutex must be held +** while accessing any read/write fields. +*/ +struct os2ShmLink { + os2ShmNode *pShmNode; /* The underlying os2ShmNode object */ + os2ShmLink *pNext; /* Next os2Shm with the same os2ShmNode */ + u32 sharedMask; /* Mask of shared locks held */ + u32 exclMask; /* Mask of exclusive locks held */ +#ifdef SQLITE_DEBUG + u8 id; /* Id of this connection with its os2ShmNode */ +#endif +}; + + +/* +** A global list of all os2ShmNode objects. +** +** The os2ShmMutexHeld() must be true while reading or writing this list. +*/ +static os2ShmNode *os2ShmNodeList = NULL; + +/* +** Constants used for locking +*/ +#ifdef SQLITE_OS2_NO_WAL_LOCK_FILE +#define OS2_SHM_BASE (PENDING_BYTE + 0x10000) /* first lock byte */ +#else +#define OS2_SHM_BASE ((22+SQLITE_SHM_NLOCK)*4) /* first lock byte */ +#endif + +#define OS2_SHM_DMS (OS2_SHM_BASE+SQLITE_SHM_NLOCK) /* deadman switch */ + +/* +** Apply advisory locks for all n bytes beginning at ofst. +*/ +#define _SHM_UNLCK 1 /* no lock */ +#define _SHM_RDLCK 2 /* shared lock, no wait */ +#define _SHM_WRLCK 3 /* exlusive lock, no wait */ +#define _SHM_WRLCK_WAIT 4 /* exclusive lock, wait */ +static int os2ShmSystemLock( + os2ShmNode *pNode, /* Apply locks to this open shared-memory segment */ + int lockType, /* _SHM_UNLCK, _SHM_RDLCK, _SHM_WRLCK or _SHM_WRLCK_WAIT */ + int ofst, /* Offset to first byte to be locked/unlocked */ + int nByte /* Number of bytes to lock or unlock */ +){ + APIRET rc; + FILELOCK area; + ULONG mode, timeout; + + /* Access to the os2ShmNode object is serialized by the caller */ + assert( sqlite3_mutex_held(pNode->mutex) || pNode->nRef==0 ); + + mode = 1; /* shared lock */ + timeout = 0; /* no wait */ + area.lOffset = ofst; + area.lRange = nByte; + + switch( lockType ) { + case _SHM_WRLCK_WAIT: + timeout = (ULONG)-1; /* wait forever */ + case _SHM_WRLCK: + mode = 0; /* exclusive lock */ + case _SHM_RDLCK: + rc = DosSetFileLocks(pNode->hLockFile, + NULL, &area, timeout, mode); + break; + /* case _SHM_UNLCK: */ + default: + rc = DosSetFileLocks(pNode->hLockFile, + &area, NULL, 0, 0); + break; + } + + OSTRACE(("SHM-LOCK %d %s %s 0x%08lx\n", + pNode->hLockFile, + rc==SQLITE_OK ? "ok" : "failed", + lockType==_SHM_UNLCK ? "Unlock" : "Lock", + rc)); + + ERR_TRACE(rc, ("os2ShmSystemLock: %d %s\n", rc, pNode->shmBaseName)) + + return ( rc == 0 ) ? SQLITE_OK : SQLITE_BUSY; +} + +/* +** Find an os2ShmNode in global list or allocate a new one, if not found. +** +** This is not a VFS shared-memory method; it is a utility function called +** by VFS shared-memory methods. +*/ +static int os2OpenSharedMemory( os2File *fd, int szRegion ) { + os2ShmLink *pLink; + os2ShmNode *pNode; + int cbShmName, rc = SQLITE_OK; + char shmName[CCHMAXPATH + 30]; +#ifndef SQLITE_OS2_NO_WAL_LOCK_FILE + ULONG action; +#endif + + /* We need some additional space at the end to append the region number */ + cbShmName = sprintf(shmName, "\\SHAREMEM\\%s", fd->zFullPathCp ); + if( cbShmName >= CCHMAXPATH-8 ) + return SQLITE_IOERR_SHMOPEN; + + /* Replace colon in file name to form a valid shared memory name */ + shmName[10+1] = '!'; + + /* Allocate link object (we free it later in case of failure) */ + pLink = sqlite3_malloc( sizeof(*pLink) ); + if( !pLink ) + return SQLITE_NOMEM; + + /* Access node list */ + os2ShmEnterMutex(); + + /* Find node by it's shared memory base name */ + for( pNode = os2ShmNodeList; + pNode && stricmp(shmName, pNode->shmBaseName) != 0; + pNode = pNode->pNext ) ; + + /* Not found: allocate a new node */ + if( !pNode ) { + pNode = sqlite3_malloc( sizeof(*pNode) + cbShmName ); + if( pNode ) { + memset(pNode, 0, sizeof(*pNode) ); + pNode->szRegion = szRegion; + pNode->hLockFile = (HFILE)-1; + strcpy(pNode->shmBaseName, shmName); + +#ifdef SQLITE_OS2_NO_WAL_LOCK_FILE + if( DosDupHandle(fd->h, &pNode->hLockFile) != 0 ) { +#else + sprintf(shmName, "%s-lck", fd->zFullPathCp); + if( DosOpen((PSZ)shmName, &pNode->hLockFile, &action, 0, FILE_NORMAL, + OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW, + OPEN_ACCESS_READWRITE | OPEN_SHARE_DENYNONE | + OPEN_FLAGS_NOINHERIT | OPEN_FLAGS_FAIL_ON_ERROR, + NULL) != 0 ) { +#endif + sqlite3_free(pNode); + rc = SQLITE_IOERR; + } else { + pNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); + if( !pNode->mutex ) { + sqlite3_free(pNode); + rc = SQLITE_NOMEM; + } + } + } else { + rc = SQLITE_NOMEM; + } + + if( rc == SQLITE_OK ) { + pNode->pNext = os2ShmNodeList; + os2ShmNodeList = pNode; + } else { + pNode = NULL; + } + } else if( pNode->szRegion != szRegion ) { + rc = SQLITE_IOERR_SHMSIZE; + pNode = NULL; + } + + if( pNode ) { + sqlite3_mutex_enter(pNode->mutex); + + memset(pLink, 0, sizeof(*pLink)); + + pLink->pShmNode = pNode; + pLink->pNext = pNode->pFirst; + pNode->pFirst = pLink; + pNode->nRef++; + + fd->pShmLink = pLink; + + sqlite3_mutex_leave(pNode->mutex); + + } else { + /* Error occured. Free our link object. */ + sqlite3_free(pLink); + } + + os2ShmLeaveMutex(); + + ERR_TRACE(rc, ("os2OpenSharedMemory: %d %s\n", rc, fd->zFullPathCp)) + + return rc; +} + +/* +** Purge the os2ShmNodeList list of all entries with nRef==0. +** +** This is not a VFS shared-memory method; it is a utility function called +** by VFS shared-memory methods. +*/ +static void os2PurgeShmNodes( int deleteFlag ) { + os2ShmNode *pNode; + os2ShmNode **ppNode; + + os2ShmEnterMutex(); + + ppNode = &os2ShmNodeList; + + while( *ppNode ) { + pNode = *ppNode; + + if( pNode->nRef == 0 ) { + *ppNode = pNode->pNext; + + if( pNode->apRegion ) { + /* Prevent other processes from resizing the shared memory */ + os2ShmSystemLock(pNode, _SHM_WRLCK_WAIT, OS2_SHM_DMS, 1); + + while( pNode->nRegion-- ) { +#ifdef SQLITE_DEBUG + int rc = +#endif + DosFreeMem(pNode->apRegion[pNode->nRegion]); + + OSTRACE(("SHM-PURGE pid-%d unmap region=%d %s\n", + (int)GetCurrentProcessId(), pNode->nRegion, + rc == 0 ? "ok" : "failed")); + } + + /* Allow other processes to resize the shared memory */ + os2ShmSystemLock(pNode, _SHM_UNLCK, OS2_SHM_DMS, 1); + + sqlite3_free(pNode->apRegion); + } + + DosClose(pNode->hLockFile); + +#ifndef SQLITE_OS2_NO_WAL_LOCK_FILE + if( deleteFlag ) { + char fileName[CCHMAXPATH]; + /* Skip "\\SHAREMEM\\" */ + sprintf(fileName, "%s-lck", pNode->shmBaseName + 10); + /* restore colon */ + fileName[1] = ':'; + + DosForceDelete(fileName); + } +#endif + + sqlite3_mutex_free(pNode->mutex); + + sqlite3_free(pNode); + + } else { + ppNode = &pNode->pNext; + } + } + + os2ShmLeaveMutex(); +} + +/* +** This function is called to obtain a pointer to region iRegion of the +** shared-memory associated with the database file id. Shared-memory regions +** are numbered starting from zero. Each shared-memory region is szRegion +** bytes in size. +** +** If an error occurs, an error code is returned and *pp is set to NULL. +** +** Otherwise, if the bExtend parameter is 0 and the requested shared-memory +** region has not been allocated (by any client, including one running in a +** separate process), then *pp is set to NULL and SQLITE_OK returned. If +** bExtend is non-zero and the requested shared-memory region has not yet +** been allocated, it is allocated by this function. +** +** If the shared-memory region has already been allocated or is allocated by +** this call as described above, then it is mapped into this processes +** address space (if it is not already), *pp is set to point to the mapped +** memory and SQLITE_OK returned. +*/ +static int os2ShmMap( + sqlite3_file *id, /* Handle open on database file */ + int iRegion, /* Region to retrieve */ + int szRegion, /* Size of regions */ + int bExtend, /* True to extend block if necessary */ + void volatile **pp /* OUT: Mapped memory */ +){ + PVOID pvTemp; + void **apRegion; + os2ShmNode *pNode; + int n, rc = SQLITE_OK; + char shmName[CCHMAXPATH]; + os2File *pFile = (os2File*)id; + + *pp = NULL; + + if( !pFile->pShmLink ) + rc = os2OpenSharedMemory( pFile, szRegion ); + + if( rc == SQLITE_OK ) { + pNode = pFile->pShmLink->pShmNode ; + + sqlite3_mutex_enter(pNode->mutex); + + assert( szRegion==pNode->szRegion ); + + /* Unmapped region ? */ + if( iRegion >= pNode->nRegion ) { + /* Prevent other processes from resizing the shared memory */ + os2ShmSystemLock(pNode, _SHM_WRLCK_WAIT, OS2_SHM_DMS, 1); + + apRegion = sqlite3_realloc( + pNode->apRegion, (iRegion + 1) * sizeof(apRegion[0])); + + if( apRegion ) { + pNode->apRegion = apRegion; + + while( pNode->nRegion <= iRegion ) { + sprintf(shmName, "%s-%u", + pNode->shmBaseName, pNode->nRegion); + + if( DosGetNamedSharedMem(&pvTemp, (PSZ)shmName, + PAG_READ | PAG_WRITE) != NO_ERROR ) { + if( !bExtend ) + break; + + if( DosAllocSharedMem(&pvTemp, (PSZ)shmName, szRegion, + PAG_READ | PAG_WRITE | PAG_COMMIT | OBJ_ANY) != NO_ERROR && + DosAllocSharedMem(&pvTemp, (PSZ)shmName, szRegion, + PAG_READ | PAG_WRITE | PAG_COMMIT) != NO_ERROR ) { + rc = SQLITE_NOMEM; + break; + } + } + + apRegion[pNode->nRegion++] = pvTemp; + } + + /* zero out remaining entries */ + for( n = pNode->nRegion; n <= iRegion; n++ ) + pNode->apRegion[n] = NULL; + + /* Return this region (maybe zero) */ + *pp = pNode->apRegion[iRegion]; + } else { + rc = SQLITE_NOMEM; + } + + /* Allow other processes to resize the shared memory */ + os2ShmSystemLock(pNode, _SHM_UNLCK, OS2_SHM_DMS, 1); + + } else { + /* Region has been mapped previously */ + *pp = pNode->apRegion[iRegion]; + } + + sqlite3_mutex_leave(pNode->mutex); + } + + ERR_TRACE(rc, ("os2ShmMap: %s iRgn = %d, szRgn = %d, bExt = %d : %d\n", + pFile->zFullPathCp, iRegion, szRegion, bExtend, rc)) + + return rc; +} + +/* +** Close a connection to shared-memory. Delete the underlying +** storage if deleteFlag is true. +** +** If there is no shared memory associated with the connection then this +** routine is a harmless no-op. +*/ +static int os2ShmUnmap( + sqlite3_file *id, /* The underlying database file */ + int deleteFlag /* Delete shared-memory if true */ +){ + os2File *pFile = (os2File*)id; + os2ShmLink *pLink = pFile->pShmLink; + + if( pLink ) { + int nRef = -1; + os2ShmLink **ppLink; + os2ShmNode *pNode = pLink->pShmNode; + + sqlite3_mutex_enter(pNode->mutex); + + for( ppLink = &pNode->pFirst; + *ppLink && *ppLink != pLink; + ppLink = &(*ppLink)->pNext ) ; + + assert(*ppLink); + + if( *ppLink ) { + *ppLink = pLink->pNext; + nRef = --pNode->nRef; + } else { + ERR_TRACE(1, ("os2ShmUnmap: link not found ! %s\n", + pNode->shmBaseName)) + } + + pFile->pShmLink = NULL; + sqlite3_free(pLink); + + sqlite3_mutex_leave(pNode->mutex); + + if( nRef == 0 ) + os2PurgeShmNodes( deleteFlag ); + } + + return SQLITE_OK; +} + +/* +** Change the lock state for a shared-memory segment. +** +** Note that the relationship between SHAREd and EXCLUSIVE locks is a little +** different here than in posix. In xShmLock(), one can go from unlocked +** to shared and back or from unlocked to exclusive and back. But one may +** not go from shared to exclusive or from exclusive to shared. +*/ +static int os2ShmLock( + sqlite3_file *id, /* Database file holding the shared memory */ + int ofst, /* First lock to acquire or release */ + int n, /* Number of locks to acquire or release */ + int flags /* What to do with the lock */ +){ + u32 mask; /* Mask of locks to take or release */ + int rc = SQLITE_OK; /* Result code */ + os2File *pFile = (os2File*)id; + os2ShmLink *p = pFile->pShmLink; /* The shared memory being locked */ + os2ShmLink *pX; /* For looping over all siblings */ + os2ShmNode *pShmNode = p->pShmNode; /* Our node */ + + assert( ofst>=0 && ofst+n<=SQLITE_SHM_NLOCK ); + assert( n>=1 ); + assert( flags==(SQLITE_SHM_LOCK | SQLITE_SHM_SHARED) + || flags==(SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE) + || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED) + || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) ); + assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 ); + + mask = (u32)((1U<<(ofst+n)) - (1U<<ofst)); + assert( n>1 || mask==(1<<ofst) ); + + + sqlite3_mutex_enter(pShmNode->mutex); + + if( flags & SQLITE_SHM_UNLOCK ){ + u32 allMask = 0; /* Mask of locks held by siblings */ + + /* See if any siblings hold this same lock */ + for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ + if( pX==p ) continue; + assert( (pX->exclMask & (p->exclMask|p->sharedMask))==0 ); + allMask |= pX->sharedMask; + } + + /* Unlock the system-level locks */ + if( (mask & allMask)==0 ){ + rc = os2ShmSystemLock(pShmNode, _SHM_UNLCK, ofst+OS2_SHM_BASE, n); + }else{ + rc = SQLITE_OK; + } + + /* Undo the local locks */ + if( rc==SQLITE_OK ){ + p->exclMask &= ~mask; + p->sharedMask &= ~mask; + } + }else if( flags & SQLITE_SHM_SHARED ){ + u32 allShared = 0; /* Union of locks held by connections other than "p" */ + + /* Find out which shared locks are already held by sibling connections. + ** If any sibling already holds an exclusive lock, go ahead and return + ** SQLITE_BUSY. + */ + for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ + if( (pX->exclMask & mask)!=0 ){ + rc = SQLITE_BUSY; + break; + } + allShared |= pX->sharedMask; + } + + /* Get shared locks at the system level, if necessary */ + if( rc==SQLITE_OK ){ + if( (allShared & mask)==0 ){ + rc = os2ShmSystemLock(pShmNode, _SHM_RDLCK, ofst+OS2_SHM_BASE, n); + }else{ + rc = SQLITE_OK; + } + } + + /* Get the local shared locks */ + if( rc==SQLITE_OK ){ + p->sharedMask |= mask; + } + }else{ + /* Make sure no sibling connections hold locks that will block this + ** lock. If any do, return SQLITE_BUSY right away. + */ + for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ + if( (pX->exclMask & mask)!=0 || (pX->sharedMask & mask)!=0 ){ + rc = SQLITE_BUSY; + break; + } + } + + /* Get the exclusive locks at the system level. Then if successful + ** also mark the local connection as being locked. + */ + if( rc==SQLITE_OK ){ + rc = os2ShmSystemLock(pShmNode, _SHM_WRLCK, ofst+OS2_SHM_BASE, n); + if( rc==SQLITE_OK ){ + assert( (p->sharedMask & mask)==0 ); + p->exclMask |= mask; + } + } + } + + sqlite3_mutex_leave(pShmNode->mutex); + + OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %03x,%03x %s\n", + p->id, (int)GetCurrentProcessId(), p->sharedMask, p->exclMask, + rc ? "failed" : "ok")); + + ERR_TRACE(rc, ("os2ShmLock: ofst = %d, n = %d, flags = 0x%x -> %d \n", + ofst, n, flags, rc)) + + return rc; +} + +/* +** Implement a memory barrier or memory fence on shared memory. +** +** All loads and stores begun before the barrier must complete before +** any load or store begun after the barrier. +*/ +static void os2ShmBarrier( + sqlite3_file *id /* Database file holding the shared memory */ +){ + UNUSED_PARAMETER(id); + os2ShmEnterMutex(); + os2ShmLeaveMutex(); +} + +#else +# define os2ShmMap 0 +# define os2ShmLock 0 +# define os2ShmBarrier 0 +# define os2ShmUnmap 0 +#endif /* #ifndef SQLITE_OMIT_WAL */ + + /* ** This vector defines all the methods that can operate on an ** sqlite3_file for os2. */ static const sqlite3_io_methods os2IoMethod = { - 1, /* iVersion */ - os2Close, - os2Read, - os2Write, - os2Truncate, - os2Sync, - os2FileSize, - os2Lock, - os2Unlock, - os2CheckReservedLock, - os2FileControl, - os2SectorSize, - os2DeviceCharacteristics + 2, /* iVersion */ + os2Close, /* xClose */ + os2Read, /* xRead */ + os2Write, /* xWrite */ + os2Truncate, /* xTruncate */ + os2Sync, /* xSync */ + os2FileSize, /* xFileSize */ + os2Lock, /* xLock */ + os2Unlock, /* xUnlock */ + os2CheckReservedLock, /* xCheckReservedLock */ + os2FileControl, /* xFileControl */ + os2SectorSize, /* xSectorSize */ + os2DeviceCharacteristics, /* xDeviceCharacteristics */ + os2ShmMap, /* xShmMap */ + os2ShmLock, /* xShmLock */ + os2ShmBarrier, /* xShmBarrier */ + os2ShmUnmap /* xShmUnmap */ }; + /*************************************************************************** ** Here ends the I/O methods that form the sqlite3_io_methods object. ** @@ -22332,50 +23714,57 @@ static const sqlite3_io_methods os2IoMethod = { ** hold at pVfs->mxPathname characters. */ static int getTempname(int nBuf, char *zBuf ){ - static const unsigned char zChars[] = + static const char zChars[] = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789"; int i, j; - char zTempPathBuf[3]; - PSZ zTempPath = (PSZ)&zTempPathBuf; - if( sqlite3_temp_directory ){ - zTempPath = sqlite3_temp_directory; - }else{ - if( DosScanEnv( (PSZ)"TEMP", &zTempPath ) ){ - if( DosScanEnv( (PSZ)"TMP", &zTempPath ) ){ - if( DosScanEnv( (PSZ)"TMPDIR", &zTempPath ) ){ - ULONG ulDriveNum = 0, ulDriveMap = 0; - DosQueryCurrentDisk( &ulDriveNum, &ulDriveMap ); - sprintf( (char*)zTempPath, "%c:", (char)( 'A' + ulDriveNum - 1 ) ); - } - } - } + PSZ zTempPathCp; + char zTempPath[CCHMAXPATH]; + ULONG ulDriveNum, ulDriveMap; + + /* It's odd to simulate an io-error here, but really this is just + ** using the io-error infrastructure to test that SQLite handles this + ** function failing. + */ + SimulateIOError( return SQLITE_IOERR ); + + if( sqlite3_temp_directory ) { + sqlite3_snprintf(CCHMAXPATH-30, zTempPath, "%s", sqlite3_temp_directory); + } else if( DosScanEnv( (PSZ)"TEMP", &zTempPathCp ) == NO_ERROR || + DosScanEnv( (PSZ)"TMP", &zTempPathCp ) == NO_ERROR || + DosScanEnv( (PSZ)"TMPDIR", &zTempPathCp ) == NO_ERROR ) { + char *zTempPathUTF = convertCpPathToUtf8( (char *)zTempPathCp ); + sqlite3_snprintf(CCHMAXPATH-30, zTempPath, "%s", zTempPathUTF); + free( zTempPathUTF ); + } else if( DosQueryCurrentDisk( &ulDriveNum, &ulDriveMap ) == NO_ERROR ) { + zTempPath[0] = (char)('A' + ulDriveNum - 1); + zTempPath[1] = ':'; + zTempPath[2] = '\0'; + } else { + zTempPath[0] = '\0'; } + /* Strip off a trailing slashes or backslashes, otherwise we would get * * multiple (back)slashes which causes DosOpen() to fail. * * Trailing spaces are not allowed, either. */ j = sqlite3Strlen30(zTempPath); - while( j > 0 && ( zTempPath[j-1] == '\\' || zTempPath[j-1] == '/' - || zTempPath[j-1] == ' ' ) ){ + while( j > 0 && ( zTempPath[j-1] == '\\' || zTempPath[j-1] == '/' || + zTempPath[j-1] == ' ' ) ){ j--; } zTempPath[j] = '\0'; - if( !sqlite3_temp_directory ){ - char *zTempPathUTF = convertCpPathToUtf8( zTempPath ); - sqlite3_snprintf( nBuf-30, zBuf, - "%s\\"SQLITE_TEMP_FILE_PREFIX, zTempPathUTF ); - free( zTempPathUTF ); - }else{ - sqlite3_snprintf( nBuf-30, zBuf, - "%s\\"SQLITE_TEMP_FILE_PREFIX, zTempPath ); - } - j = sqlite3Strlen30( zBuf ); + + /* We use 20 bytes to randomize the name */ + sqlite3_snprintf(nBuf-22, zBuf, + "%s\\"SQLITE_TEMP_FILE_PREFIX, zTempPath); + j = sqlite3Strlen30(zBuf); sqlite3_randomness( 20, &zBuf[j] ); for( i = 0; i < 20; i++, j++ ){ - zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; + zBuf[j] = zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; } zBuf[j] = 0; + OSTRACE(( "TEMP FILENAME: %s\n", zBuf )); return SQLITE_OK; } @@ -22395,8 +23784,8 @@ static int os2FullPathname( char *zRelativeCp = convertUtf8PathToCp( zRelative ); char zFullCp[CCHMAXPATH] = "\0"; char *zFullUTF; - APIRET rc = DosQueryPathInfo( zRelativeCp, FIL_QUERYFULLNAME, zFullCp, - CCHMAXPATH ); + APIRET rc = DosQueryPathInfo( (PSZ)zRelativeCp, FIL_QUERYFULLNAME, + zFullCp, CCHMAXPATH ); free( zRelativeCp ); zFullUTF = convertCpPathToUtf8( zFullCp ); sqlite3_snprintf( nFull, zFull, zFullUTF ); @@ -22410,100 +23799,127 @@ static int os2FullPathname( */ static int os2Open( sqlite3_vfs *pVfs, /* Not used */ - const char *zName, /* Name of the file */ + const char *zName, /* Name of the file (UTF-8) */ sqlite3_file *id, /* Write the SQLite file handle here */ int flags, /* Open mode flags */ int *pOutFlags /* Status return flags */ ){ HFILE h; - ULONG ulFileAttribute = FILE_NORMAL; ULONG ulOpenFlags = 0; ULONG ulOpenMode = 0; + ULONG ulAction = 0; + ULONG rc; os2File *pFile = (os2File*)id; - APIRET rc = NO_ERROR; - ULONG ulAction; + const char *zUtf8Name = zName; char *zNameCp; - char zTmpname[CCHMAXPATH+1]; /* Buffer to hold name of temp file */ + char zTmpname[CCHMAXPATH]; + + int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE); + int isCreate = (flags & SQLITE_OPEN_CREATE); + int isReadWrite = (flags & SQLITE_OPEN_READWRITE); +#ifndef NDEBUG + int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE); + int isReadonly = (flags & SQLITE_OPEN_READONLY); + int eType = (flags & 0xFFFFFF00); + int isOpenJournal = (isCreate && ( + eType==SQLITE_OPEN_MASTER_JOURNAL + || eType==SQLITE_OPEN_MAIN_JOURNAL + || eType==SQLITE_OPEN_WAL + )); +#endif + + UNUSED_PARAMETER(pVfs); + assert( id!=0 ); + + /* Check the following statements are true: + ** + ** (a) Exactly one of the READWRITE and READONLY flags must be set, and + ** (b) if CREATE is set, then READWRITE must also be set, and + ** (c) if EXCLUSIVE is set, then CREATE must also be set. + ** (d) if DELETEONCLOSE is set, then CREATE must also be set. + */ + assert((isReadonly==0 || isReadWrite==0) && (isReadWrite || isReadonly)); + assert(isCreate==0 || isReadWrite); + assert(isExclusive==0 || isCreate); + assert(isDelete==0 || isCreate); + + /* The main DB, main journal, WAL file and master journal are never + ** automatically deleted. Nor are they ever temporary files. */ + assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_DB ); + assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_JOURNAL ); + assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MASTER_JOURNAL ); + assert( (!isDelete && zName) || eType!=SQLITE_OPEN_WAL ); + + /* Assert that the upper layer has set one of the "file-type" flags. */ + assert( eType==SQLITE_OPEN_MAIN_DB || eType==SQLITE_OPEN_TEMP_DB + || eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_TEMP_JOURNAL + || eType==SQLITE_OPEN_SUBJOURNAL || eType==SQLITE_OPEN_MASTER_JOURNAL + || eType==SQLITE_OPEN_TRANSIENT_DB || eType==SQLITE_OPEN_WAL + ); + + memset( pFile, 0, sizeof(*pFile) ); + pFile->h = (HFILE)-1; /* If the second argument to this function is NULL, generate a ** temporary file name to use */ - if( !zName ){ - int rc = getTempname(CCHMAXPATH+1, zTmpname); + if( !zUtf8Name ){ + assert(isDelete && !isOpenJournal); + rc = getTempname(CCHMAXPATH, zTmpname); if( rc!=SQLITE_OK ){ return rc; } - zName = zTmpname; + zUtf8Name = zTmpname; } - - memset( pFile, 0, sizeof(*pFile) ); - - OSTRACE(( "OPEN want %d\n", flags )); - - if( flags & SQLITE_OPEN_READWRITE ){ + if( isReadWrite ){ ulOpenMode |= OPEN_ACCESS_READWRITE; - OSTRACE(( "OPEN read/write\n" )); }else{ ulOpenMode |= OPEN_ACCESS_READONLY; - OSTRACE(( "OPEN read only\n" )); - } - - if( flags & SQLITE_OPEN_CREATE ){ - ulOpenFlags |= OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW; - OSTRACE(( "OPEN open new/create\n" )); - }else{ - ulOpenFlags |= OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW; - OSTRACE(( "OPEN open existing\n" )); } - if( flags & SQLITE_OPEN_MAIN_DB ){ - ulOpenMode |= OPEN_SHARE_DENYNONE; - OSTRACE(( "OPEN share read/write\n" )); - }else{ - ulOpenMode |= OPEN_SHARE_DENYWRITE; - OSTRACE(( "OPEN share read only\n" )); - } + /* Open in random access mode for possibly better speed. Allow full + ** sharing because file locks will provide exclusive access when needed. + ** The handle should not be inherited by child processes and we don't + ** want popups from the critical error handler. + */ + ulOpenMode |= OPEN_FLAGS_RANDOM | OPEN_SHARE_DENYNONE | + OPEN_FLAGS_NOINHERIT | OPEN_FLAGS_FAIL_ON_ERROR; - if( flags & SQLITE_OPEN_DELETEONCLOSE ){ - char pathUtf8[CCHMAXPATH]; -#ifdef NDEBUG /* when debugging we want to make sure it is deleted */ - ulFileAttribute = FILE_HIDDEN; -#endif - os2FullPathname( pVfs, zName, CCHMAXPATH, pathUtf8 ); - pFile->pathToDel = convertUtf8PathToCp( pathUtf8 ); - OSTRACE(( "OPEN hidden/delete on close file attributes\n" )); + /* SQLITE_OPEN_EXCLUSIVE is used to make sure that a new file is + ** created. SQLite doesn't use it to indicate "exclusive access" + ** as it is usually understood. + */ + if( isExclusive ){ + /* Creates a new file, only if it does not already exist. */ + /* If the file exists, it fails. */ + ulOpenFlags |= OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_FAIL_IF_EXISTS; + }else if( isCreate ){ + /* Open existing file, or create if it doesn't exist */ + ulOpenFlags |= OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS; }else{ - pFile->pathToDel = NULL; - OSTRACE(( "OPEN normal file attribute\n" )); + /* Opens a file, only if it exists. */ + ulOpenFlags |= OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS; } - /* always open in random access mode for possibly better speed */ - ulOpenMode |= OPEN_FLAGS_RANDOM; - ulOpenMode |= OPEN_FLAGS_FAIL_ON_ERROR; - ulOpenMode |= OPEN_FLAGS_NOINHERIT; - - zNameCp = convertUtf8PathToCp( zName ); + zNameCp = convertUtf8PathToCp( zUtf8Name ); rc = DosOpen( (PSZ)zNameCp, &h, &ulAction, 0L, - ulFileAttribute, + FILE_NORMAL, ulOpenFlags, ulOpenMode, (PEAOP2)NULL ); free( zNameCp ); + if( rc != NO_ERROR ){ - OSTRACE(( "OPEN Invalid handle rc=%d: zName=%s, ulAction=%#lx, ulAttr=%#lx, ulFlags=%#lx, ulMode=%#lx\n", - rc, zName, ulAction, ulFileAttribute, ulOpenFlags, ulOpenMode )); - if( pFile->pathToDel ) - free( pFile->pathToDel ); - pFile->pathToDel = NULL; - if( flags & SQLITE_OPEN_READWRITE ){ - OSTRACE(( "OPEN %d Invalid handle\n", - ((flags | SQLITE_OPEN_READONLY) & ~SQLITE_OPEN_READWRITE) )); + OSTRACE(( "OPEN Invalid handle rc=%d: zName=%s, ulAction=%#lx, ulFlags=%#lx, ulMode=%#lx\n", + rc, zUtf8Name, ulAction, ulOpenFlags, ulOpenMode )); + + if( isReadWrite ){ return os2Open( pVfs, zName, id, - ((flags | SQLITE_OPEN_READONLY) & ~SQLITE_OPEN_READWRITE), + ((flags|SQLITE_OPEN_READONLY)&~(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE)), pOutFlags ); }else{ return SQLITE_CANTOPEN; @@ -22511,11 +23927,15 @@ static int os2Open( } if( pOutFlags ){ - *pOutFlags = flags & SQLITE_OPEN_READWRITE ? SQLITE_OPEN_READWRITE : SQLITE_OPEN_READONLY; + *pOutFlags = isReadWrite ? SQLITE_OPEN_READWRITE : SQLITE_OPEN_READONLY; } + os2FullPathname( pVfs, zUtf8Name, sizeof( zTmpname ), zTmpname ); + pFile->zFullPathCp = convertUtf8PathToCp( zTmpname ); pFile->pMethod = &os2IoMethod; + pFile->flags = flags; pFile->h = h; + OpenCounter(+1); OSTRACE(( "OPEN %d pOutFlags=%d\n", pFile->h, pOutFlags )); return SQLITE_OK; @@ -22529,13 +23949,16 @@ static int os2Delete( const char *zFilename, /* Name of file to delete */ int syncDir /* Not used on os2 */ ){ - APIRET rc = NO_ERROR; - char *zFilenameCp = convertUtf8PathToCp( zFilename ); + APIRET rc; + char *zFilenameCp; SimulateIOError( return SQLITE_IOERR_DELETE ); + zFilenameCp = convertUtf8PathToCp( zFilename ); rc = DosDelete( (PSZ)zFilenameCp ); free( zFilenameCp ); OSTRACE(( "DELETE \"%s\"\n", zFilename )); - return rc == NO_ERROR ? SQLITE_OK : SQLITE_IOERR_DELETE; + return (rc == NO_ERROR || + rc == ERROR_FILE_NOT_FOUND || + rc == ERROR_PATH_NOT_FOUND ) ? SQLITE_OK : SQLITE_IOERR_DELETE; } /* @@ -22547,30 +23970,42 @@ static int os2Access( int flags, /* Type of test to make on this file */ int *pOut /* Write results here */ ){ + APIRET rc; FILESTATUS3 fsts3ConfigInfo; - APIRET rc = NO_ERROR; - char *zFilenameCp = convertUtf8PathToCp( zFilename ); + char *zFilenameCp; - memset( &fsts3ConfigInfo, 0, sizeof(fsts3ConfigInfo) ); + UNUSED_PARAMETER(pVfs); + SimulateIOError( return SQLITE_IOERR_ACCESS; ); + + zFilenameCp = convertUtf8PathToCp( zFilename ); rc = DosQueryPathInfo( (PSZ)zFilenameCp, FIL_STANDARD, &fsts3ConfigInfo, sizeof(FILESTATUS3) ); free( zFilenameCp ); OSTRACE(( "ACCESS fsts3ConfigInfo.attrFile=%d flags=%d rc=%d\n", fsts3ConfigInfo.attrFile, flags, rc )); + switch( flags ){ - case SQLITE_ACCESS_READ: case SQLITE_ACCESS_EXISTS: - rc = (rc == NO_ERROR); - OSTRACE(( "ACCESS %s access of read and exists rc=%d\n", zFilename, rc)); + /* For an SQLITE_ACCESS_EXISTS query, treat a zero-length file + ** as if it does not exist. + */ + if( fsts3ConfigInfo.cbFile == 0 ) + rc = ERROR_FILE_NOT_FOUND; + break; + case SQLITE_ACCESS_READ: break; case SQLITE_ACCESS_READWRITE: - rc = (rc == NO_ERROR) && ( (fsts3ConfigInfo.attrFile & FILE_READONLY) == 0 ); - OSTRACE(( "ACCESS %s access of read/write rc=%d\n", zFilename, rc )); + if( fsts3ConfigInfo.attrFile & FILE_READONLY ) + rc = ERROR_ACCESS_DENIED; break; default: + rc = ERROR_FILE_NOT_FOUND; assert( !"Invalid flags argument" ); } - *pOut = rc; + + *pOut = (rc == NO_ERROR); + OSTRACE(( "ACCESS %s flags %d: rc=%d\n", zFilename, flags, *pOut )); + return SQLITE_OK; } @@ -22585,11 +24020,10 @@ static int os2Access( ** within the shared library, and closing the shared library. */ static void *os2DlOpen(sqlite3_vfs *pVfs, const char *zFilename){ - UCHAR loadErr[256]; HMODULE hmod; APIRET rc; char *zFilenameCp = convertUtf8PathToCp(zFilename); - rc = DosLoadModule((PSZ)loadErr, sizeof(loadErr), zFilenameCp, &hmod); + rc = DosLoadModule(NULL, 0, (PSZ)zFilenameCp, &hmod); free(zFilenameCp); return rc != NO_ERROR ? 0 : (void*)hmod; } @@ -22600,19 +24034,19 @@ static void *os2DlOpen(sqlite3_vfs *pVfs, const char *zFilename){ static void os2DlError(sqlite3_vfs *pVfs, int nBuf, char *zBufOut){ /* no-op */ } -static void *os2DlSym(sqlite3_vfs *pVfs, void *pHandle, const char *zSymbol){ +static void (*os2DlSym(sqlite3_vfs *pVfs, void *pHandle, const char *zSymbol))(void){ PFN pfn; APIRET rc; - rc = DosQueryProcAddr((HMODULE)pHandle, 0L, zSymbol, &pfn); + rc = DosQueryProcAddr((HMODULE)pHandle, 0L, (PSZ)zSymbol, &pfn); if( rc != NO_ERROR ){ /* if the symbol itself was not found, search again for the same * symbol with an extra underscore, that might be needed depending * on the calling convention */ char _zSymbol[256] = "_"; - strncat(_zSymbol, zSymbol, 255); - rc = DosQueryProcAddr((HMODULE)pHandle, 0L, _zSymbol, &pfn); + strncat(_zSymbol, zSymbol, 254); + rc = DosQueryProcAddr((HMODULE)pHandle, 0L, (PSZ)_zSymbol, &pfn); } - return rc != NO_ERROR ? 0 : (void*)pfn; + return rc != NO_ERROR ? 0 : (void(*)(void))pfn; } static void os2DlClose(sqlite3_vfs *pVfs, void *pHandle){ DosFreeModule((HMODULE)pHandle); @@ -22634,54 +24068,39 @@ static int os2Randomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf ){ n = nBuf; memset(zBuf, 0, nBuf); #else - int sizeofULong = sizeof(ULONG); - if( (int)sizeof(DATETIME) <= nBuf - n ){ - DATETIME x; - DosGetDateTime(&x); - memcpy(&zBuf[n], &x, sizeof(x)); - n += sizeof(x); - } - - if( sizeofULong <= nBuf - n ){ - PPIB ppib; - DosGetInfoBlocks(NULL, &ppib); - memcpy(&zBuf[n], &ppib->pib_ulpid, sizeofULong); - n += sizeofULong; - } - - if( sizeofULong <= nBuf - n ){ - PTIB ptib; - DosGetInfoBlocks(&ptib, NULL); - memcpy(&zBuf[n], &ptib->tib_ptib2->tib2_ultid, sizeofULong); - n += sizeofULong; - } - - /* if we still haven't filled the buffer yet the following will */ - /* grab everything once instead of making several calls for a single item */ - if( sizeofULong <= nBuf - n ){ - ULONG ulSysInfo[QSV_MAX]; - DosQuerySysInfo(1L, QSV_MAX, ulSysInfo, sizeofULong * QSV_MAX); - - memcpy(&zBuf[n], &ulSysInfo[QSV_MS_COUNT - 1], sizeofULong); - n += sizeofULong; - - if( sizeofULong <= nBuf - n ){ - memcpy(&zBuf[n], &ulSysInfo[QSV_TIMER_INTERVAL - 1], sizeofULong); - n += sizeofULong; - } - if( sizeofULong <= nBuf - n ){ - memcpy(&zBuf[n], &ulSysInfo[QSV_TIME_LOW - 1], sizeofULong); - n += sizeofULong; - } - if( sizeofULong <= nBuf - n ){ - memcpy(&zBuf[n], &ulSysInfo[QSV_TIME_HIGH - 1], sizeofULong); - n += sizeofULong; - } - if( sizeofULong <= nBuf - n ){ - memcpy(&zBuf[n], &ulSysInfo[QSV_TOTAVAILMEM - 1], sizeofULong); - n += sizeofULong; - } - } + int i; + PPIB ppib; + PTIB ptib; + DATETIME dt; + static unsigned c = 0; + /* Ordered by variation probability */ + static ULONG svIdx[6] = { QSV_MS_COUNT, QSV_TIME_LOW, + QSV_MAXPRMEM, QSV_MAXSHMEM, + QSV_TOTAVAILMEM, QSV_TOTRESMEM }; + + /* 8 bytes; timezone and weekday don't increase the randomness much */ + if( (int)sizeof(dt)-3 <= nBuf - n ){ + c += 0x0100; + DosGetDateTime(&dt); + dt.year = (USHORT)((dt.year - 1900) | c); + memcpy(&zBuf[n], &dt, sizeof(dt)-3); + n += sizeof(dt)-3; + } + + /* 4 bytes; PIDs and TIDs are 16 bit internally, so combine them */ + if( (int)sizeof(ULONG) <= nBuf - n ){ + DosGetInfoBlocks(&ptib, &ppib); + *(PULONG)&zBuf[n] = MAKELONG(ppib->pib_ulpid, + ptib->tib_ptib2->tib2_ultid); + n += sizeof(ULONG); + } + + /* Up to 6 * 4 bytes; variables depend on the system state */ + for( i = 0; i < 6 && (int)sizeof(ULONG) <= nBuf - n; i++ ){ + DosQuerySysInfo(svIdx[i], svIdx[i], + (PULONG)&zBuf[n], sizeof(ULONG)); + n += sizeof(ULONG); + } #endif return n; @@ -22709,46 +24128,98 @@ SQLITE_API int sqlite3_current_time = 0; #endif /* -** Find the current time (in Universal Coordinated Time). Write the -** current time and date as a Julian Day number into *prNow and -** return 0. Return 1 if the time and date cannot be found. +** Find the current time (in Universal Coordinated Time). Write into *piNow +** the current time and date as a Julian Day number times 86_400_000. In +** other words, write into *piNow the number of milliseconds since the Julian +** epoch of noon in Greenwich on November 24, 4714 B.C according to the +** proleptic Gregorian calendar. +** +** On success, return 0. Return 1 if the time and date cannot be found. */ -int os2CurrentTime( sqlite3_vfs *pVfs, double *prNow ){ - double now; - SHORT minute; /* needs to be able to cope with negative timezone offset */ - USHORT second, hour, - day, month, year; +static int os2CurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *piNow){ +#ifdef SQLITE_TEST + static const sqlite3_int64 unixEpoch = 24405875*(sqlite3_int64)8640000; +#endif + int year, month, datepart, timepart; + DATETIME dt; DosGetDateTime( &dt ); - second = (USHORT)dt.seconds; - minute = (SHORT)dt.minutes + dt.timezone; - hour = (USHORT)dt.hours; - day = (USHORT)dt.day; - month = (USHORT)dt.month; - year = (USHORT)dt.year; + + year = dt.year; + month = dt.month; /* Calculations from http://www.astro.keele.ac.uk/~rno/Astronomy/hjd.html - http://www.astro.keele.ac.uk/~rno/Astronomy/hjd-0.1.c */ - /* Calculate the Julian days */ - now = day - 32076 + + ** http://www.astro.keele.ac.uk/~rno/Astronomy/hjd-0.1.c + ** Calculate the Julian days + */ + datepart = (int)dt.day - 32076 + 1461*(year + 4800 + (month - 14)/12)/4 + 367*(month - 2 - (month - 14)/12*12)/12 - 3*((year + 4900 + (month - 14)/12)/100)/4; - /* Add the fractional hours, mins and seconds */ - now += (hour + 12.0)/24.0; - now += minute/1440.0; - now += second/86400.0; - *prNow = now; + /* Time in milliseconds, hours to noon added */ + timepart = 12*3600*1000 + dt.hundredths*10 + dt.seconds*1000 + + ((int)dt.minutes + dt.timezone)*60*1000 + dt.hours*3600*1000; + + *piNow = (sqlite3_int64)datepart*86400*1000 + timepart; + #ifdef SQLITE_TEST if( sqlite3_current_time ){ - *prNow = sqlite3_current_time/86400.0 + 2440587.5; + *piNow = 1000*(sqlite3_int64)sqlite3_current_time + unixEpoch; } #endif + + UNUSED_PARAMETER(pVfs); return 0; } +/* +** Find the current time (in Universal Coordinated Time). Write the +** current time and date as a Julian Day number into *prNow and +** return 0. Return 1 if the time and date cannot be found. +*/ +static int os2CurrentTime( sqlite3_vfs *pVfs, double *prNow ){ + int rc; + sqlite3_int64 i; + rc = os2CurrentTimeInt64(pVfs, &i); + if( !rc ){ + *prNow = i/86400000.0; + } + return rc; +} + +/* +** The idea is that this function works like a combination of +** GetLastError() and FormatMessage() on windows (or errno and +** strerror_r() on unix). After an error is returned by an OS +** function, SQLite calls this function with zBuf pointing to +** a buffer of nBuf bytes. The OS layer should populate the +** buffer with a nul-terminated UTF-8 encoded error message +** describing the last IO error to have occurred within the calling +** thread. +** +** If the error message is too large for the supplied buffer, +** it should be truncated. The return value of xGetLastError +** is zero if the error message fits in the buffer, or non-zero +** otherwise (if the message was truncated). If non-zero is returned, +** then it is not necessary to include the nul-terminator character +** in the output buffer. +** +** Not supplying an error message will have no adverse effect +** on SQLite. It is fine to have an implementation that never +** returns an error message: +** +** int xGetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ +** assert(zBuf[0]=='\0'); +** return 0; +** } +** +** However if an error message is supplied, it will be incorporated +** by sqlite into the error message available to the user using +** sqlite3_errmsg(), possibly making IO errors easier to debug. +*/ static int os2GetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ + assert(zBuf[0]=='\0'); return 0; } @@ -22757,7 +24228,7 @@ static int os2GetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ */ SQLITE_API int sqlite3_os_init(void){ static sqlite3_vfs os2Vfs = { - 1, /* iVersion */ + 3, /* iVersion */ sizeof(os2File), /* szOsFile */ CCHMAXPATH, /* mxPathname */ 0, /* pNext */ @@ -22776,9 +24247,14 @@ SQLITE_API int sqlite3_os_init(void){ os2Sleep, /* xSleep */ os2CurrentTime, /* xCurrentTime */ os2GetLastError, /* xGetLastError */ + os2CurrentTimeInt64, /* xCurrentTimeInt64 */ + 0, /* xSetSystemCall */ + 0, /* xGetSystemCall */ + 0 /* xNextSystemCall */ }; sqlite3_vfs_register(&os2Vfs, 1); initUconvObjects(); +/* sqlite3OSTrace = 1; */ return SQLITE_OK; } SQLITE_API int sqlite3_os_end(void){ @@ -22909,7 +24385,9 @@ SQLITE_API int sqlite3_os_end(void){ #include <unistd.h> #include <sys/time.h> #include <errno.h> +#ifndef SQLITE_OMIT_WAL #include <sys/mman.h> +#endif #if SQLITE_ENABLE_LOCKING_STYLE # include <sys/ioctl.h> @@ -22926,6 +24404,10 @@ SQLITE_API int sqlite3_os_end(void){ # include <sys/mount.h> #endif +#ifdef HAVE_UTIME +# include <utime.h> +#endif + /* ** Allowed values of unixFile.fsFlags */ @@ -22993,10 +24475,10 @@ struct unixFile { int h; /* The file descriptor */ int dirfd; /* File descriptor for the directory */ unsigned char eFileLock; /* The type of lock held on this fd */ + unsigned char ctrlFlags; /* Behavioral bits. UNIXFILE_* flags */ int lastErrno; /* The unix errno from last I/O error */ void *lockingContext; /* Locking style specific state */ UnixUnusedFd *pUnused; /* Pre-allocated UnixUnusedFd */ - int fileFlags; /* Miscellanous flags */ const char *zPath; /* Name of the file */ unixShm *pShm; /* Shared memory segment information */ int szChunk; /* Configured by FCNTL_CHUNK_SIZE */ @@ -23031,9 +24513,10 @@ struct unixFile { }; /* -** The following macros define bits in unixFile.fileFlags +** Allowed values for the unixFile.ctrlFlags bitmask: */ -#define SQLITE_WHOLE_FILE_LOCKING 0x0001 /* Use whole-file locking */ +#define UNIXFILE_EXCL 0x01 /* Connections from one process only */ +#define UNIXFILE_RDONLY 0x02 /* Connection is read only */ /* ** Include code that is common to all os_*.c files @@ -23263,25 +24746,216 @@ SQLITE_API int sqlite3_open_file_count = 0; #endif /* +** The threadid macro resolves to the thread-id or to 0. Used for +** testing and debugging only. +*/ +#if SQLITE_THREADSAFE +#define threadid pthread_self() +#else +#define threadid 0 +#endif + +/* +** Different Unix systems declare open() in different ways. Same use +** open(const char*,int,mode_t). Others use open(const char*,int,...). +** The difference is important when using a pointer to the function. +** +** The safest way to deal with the problem is to always use this wrapper +** which always has the same well-defined interface. +*/ +static int posixOpen(const char *zFile, int flags, int mode){ + return open(zFile, flags, mode); +} + +/* +** Many system calls are accessed through pointer-to-functions so that +** they may be overridden at runtime to facilitate fault injection during +** testing and sandboxing. The following array holds the names and pointers +** to all overrideable system calls. +*/ +static struct unix_syscall { + const char *zName; /* Name of the sytem call */ + sqlite3_syscall_ptr pCurrent; /* Current value of the system call */ + sqlite3_syscall_ptr pDefault; /* Default value */ +} aSyscall[] = { + { "open", (sqlite3_syscall_ptr)posixOpen, 0 }, +#define osOpen ((int(*)(const char*,int,int))aSyscall[0].pCurrent) + + { "close", (sqlite3_syscall_ptr)close, 0 }, +#define osClose ((int(*)(int))aSyscall[1].pCurrent) + + { "access", (sqlite3_syscall_ptr)access, 0 }, +#define osAccess ((int(*)(const char*,int))aSyscall[2].pCurrent) + + { "getcwd", (sqlite3_syscall_ptr)getcwd, 0 }, +#define osGetcwd ((char*(*)(char*,size_t))aSyscall[3].pCurrent) + + { "stat", (sqlite3_syscall_ptr)stat, 0 }, +#define osStat ((int(*)(const char*,struct stat*))aSyscall[4].pCurrent) + +/* ** The DJGPP compiler environment looks mostly like Unix, but it ** lacks the fcntl() system call. So redefine fcntl() to be something ** that always succeeds. This means that locking does not occur under ** DJGPP. But it is DOS - what did you expect? */ #ifdef __DJGPP__ -# define fcntl(A,B,C) 0 + { "fstat", 0, 0 }, +#define osFstat(a,b,c) 0 +#else + { "fstat", (sqlite3_syscall_ptr)fstat, 0 }, +#define osFstat ((int(*)(int,struct stat*))aSyscall[5].pCurrent) #endif -/* -** The threadid macro resolves to the thread-id or to 0. Used for -** testing and debugging only. -*/ -#if SQLITE_THREADSAFE -#define threadid pthread_self() + { "ftruncate", (sqlite3_syscall_ptr)ftruncate, 0 }, +#define osFtruncate ((int(*)(int,off_t))aSyscall[6].pCurrent) + + { "fcntl", (sqlite3_syscall_ptr)fcntl, 0 }, +#define osFcntl ((int(*)(int,int,...))aSyscall[7].pCurrent) + + { "read", (sqlite3_syscall_ptr)read, 0 }, +#define osRead ((ssize_t(*)(int,void*,size_t))aSyscall[8].pCurrent) + +#if defined(USE_PREAD) || SQLITE_ENABLE_LOCKING_STYLE + { "pread", (sqlite3_syscall_ptr)pread, 0 }, #else -#define threadid 0 + { "pread", (sqlite3_syscall_ptr)0, 0 }, +#endif +#define osPread ((ssize_t(*)(int,void*,size_t,off_t))aSyscall[9].pCurrent) + +#if defined(USE_PREAD64) + { "pread64", (sqlite3_syscall_ptr)pread64, 0 }, +#else + { "pread64", (sqlite3_syscall_ptr)0, 0 }, #endif +#define osPread64 ((ssize_t(*)(int,void*,size_t,off_t))aSyscall[10].pCurrent) + { "write", (sqlite3_syscall_ptr)write, 0 }, +#define osWrite ((ssize_t(*)(int,const void*,size_t))aSyscall[11].pCurrent) + +#if defined(USE_PREAD) || SQLITE_ENABLE_LOCKING_STYLE + { "pwrite", (sqlite3_syscall_ptr)pwrite, 0 }, +#else + { "pwrite", (sqlite3_syscall_ptr)0, 0 }, +#endif +#define osPwrite ((ssize_t(*)(int,const void*,size_t,off_t))\ + aSyscall[12].pCurrent) + +#if defined(USE_PREAD64) + { "pwrite64", (sqlite3_syscall_ptr)pwrite64, 0 }, +#else + { "pwrite64", (sqlite3_syscall_ptr)0, 0 }, +#endif +#define osPwrite64 ((ssize_t(*)(int,const void*,size_t,off_t))\ + aSyscall[13].pCurrent) + +#if SQLITE_ENABLE_LOCKING_STYLE + { "fchmod", (sqlite3_syscall_ptr)fchmod, 0 }, +#else + { "fchmod", (sqlite3_syscall_ptr)0, 0 }, +#endif +#define osFchmod ((int(*)(int,mode_t))aSyscall[14].pCurrent) + +#if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE + { "fallocate", (sqlite3_syscall_ptr)posix_fallocate, 0 }, +#else + { "fallocate", (sqlite3_syscall_ptr)0, 0 }, +#endif +#define osFallocate ((int(*)(int,off_t,off_t))aSyscall[15].pCurrent) + +}; /* End of the overrideable system calls */ + +/* +** This is the xSetSystemCall() method of sqlite3_vfs for all of the +** "unix" VFSes. Return SQLITE_OK opon successfully updating the +** system call pointer, or SQLITE_NOTFOUND if there is no configurable +** system call named zName. +*/ +static int unixSetSystemCall( + sqlite3_vfs *pNotUsed, /* The VFS pointer. Not used */ + const char *zName, /* Name of system call to override */ + sqlite3_syscall_ptr pNewFunc /* Pointer to new system call value */ +){ + unsigned int i; + int rc = SQLITE_NOTFOUND; + + UNUSED_PARAMETER(pNotUsed); + if( zName==0 ){ + /* If no zName is given, restore all system calls to their default + ** settings and return NULL + */ + rc = SQLITE_OK; + for(i=0; i<sizeof(aSyscall)/sizeof(aSyscall[0]); i++){ + if( aSyscall[i].pDefault ){ + aSyscall[i].pCurrent = aSyscall[i].pDefault; + } + } + }else{ + /* If zName is specified, operate on only the one system call + ** specified. + */ + for(i=0; i<sizeof(aSyscall)/sizeof(aSyscall[0]); i++){ + if( strcmp(zName, aSyscall[i].zName)==0 ){ + if( aSyscall[i].pDefault==0 ){ + aSyscall[i].pDefault = aSyscall[i].pCurrent; + } + rc = SQLITE_OK; + if( pNewFunc==0 ) pNewFunc = aSyscall[i].pDefault; + aSyscall[i].pCurrent = pNewFunc; + break; + } + } + } + return rc; +} + +/* +** Return the value of a system call. Return NULL if zName is not a +** recognized system call name. NULL is also returned if the system call +** is currently undefined. +*/ +static sqlite3_syscall_ptr unixGetSystemCall( + sqlite3_vfs *pNotUsed, + const char *zName +){ + unsigned int i; + + UNUSED_PARAMETER(pNotUsed); + for(i=0; i<sizeof(aSyscall)/sizeof(aSyscall[0]); i++){ + if( strcmp(zName, aSyscall[i].zName)==0 ) return aSyscall[i].pCurrent; + } + return 0; +} + +/* +** Return the name of the first system call after zName. If zName==NULL +** then return the name of the first system call. Return NULL if zName +** is the last system call or if zName is not the name of a valid +** system call. +*/ +static const char *unixNextSystemCall(sqlite3_vfs *p, const char *zName){ + int i = -1; + + UNUSED_PARAMETER(p); + if( zName ){ + for(i=0; i<ArraySize(aSyscall)-1; i++){ + if( strcmp(zName, aSyscall[i].zName)==0 ) break; + } + } + for(i++; i<ArraySize(aSyscall); i++){ + if( aSyscall[i].pCurrent!=0 ) return aSyscall[i].zName; + } + return 0; +} + +/* +** Retry open() calls that fail due to EINTR +*/ +static int robust_open(const char *z, int f, int m){ + int rc; + do{ rc = osOpen(z,f,m); }while( rc<0 && errno==EINTR ); + return rc; +} /* ** Helper functions to obtain and relinquish the global mutex. The @@ -23346,7 +25020,7 @@ static int lockTrace(int fd, int op, struct flock *p){ }else if( op==F_SETLK ){ zOpName = "SETLK"; }else{ - s = fcntl(fd, op, p); + s = osFcntl(fd, op, p); sqlite3DebugPrintf("fcntl unknown %d %d %d\n", fd, op, s); return s; } @@ -23360,7 +25034,7 @@ static int lockTrace(int fd, int op, struct flock *p){ assert( 0 ); } assert( p->l_whence==SEEK_SET ); - s = fcntl(fd, op, p); + s = osFcntl(fd, op, p); savedErrno = errno; sqlite3DebugPrintf("fcntl %d %d %s %s %d %d %d %d\n", threadid, fd, zOpName, zType, (int)p->l_start, (int)p->l_len, @@ -23368,7 +25042,7 @@ static int lockTrace(int fd, int op, struct flock *p){ if( s==(-1) && op==F_SETLK && (p->l_type==F_RDLCK || p->l_type==F_WRLCK) ){ struct flock l2; l2 = *p; - fcntl(fd, F_GETLK, &l2); + osFcntl(fd, F_GETLK, &l2); if( l2.l_type==F_RDLCK ){ zType = "RDLCK"; }else if( l2.l_type==F_WRLCK ){ @@ -23384,10 +25058,18 @@ static int lockTrace(int fd, int op, struct flock *p){ errno = savedErrno; return s; } -#define fcntl lockTrace +#undef osFcntl +#define osFcntl lockTrace #endif /* SQLITE_LOCK_TRACE */ - +/* +** Retry ftruncate() calls that fail due to EINTR +*/ +static int robust_ftruncate(int h, sqlite3_int64 sz){ + int rc; + do{ rc = osFtruncate(h,sz); }while( rc<0 && errno==EINTR ); + return rc; +} /* ** This routine translates a standard POSIX errno code into something @@ -23401,9 +25083,22 @@ static int lockTrace(int fd, int op, struct flock *p){ */ static int sqliteErrorFromPosixError(int posixError, int sqliteIOErr) { switch (posixError) { +#if 0 + /* At one point this code was not commented out. In theory, this branch + ** should never be hit, as this function should only be called after + ** a locking-related function (i.e. fcntl()) has returned non-zero with + ** the value of errno as the first argument. Since a system call has failed, + ** errno should be non-zero. + ** + ** Despite this, if errno really is zero, we still don't want to return + ** SQLITE_OK. The system call failed, and *some* SQLite error should be + ** propagated back to the caller. Commenting this branch out means errno==0 + ** will be handled by the "default:" case below. + */ case 0: return SQLITE_OK; - +#endif + case EAGAIN: case ETIMEDOUT: case EBUSY: @@ -23425,8 +25120,15 @@ static int sqliteErrorFromPosixError(int posixError, int sqliteIOErr) { case EPERM: return SQLITE_PERM; + /* EDEADLK is only possible if a call to fcntl(F_SETLKW) is made. And + ** this module never makes such a call. And the code in SQLite itself + ** asserts that SQLITE_IOERR_BLOCKED is never returned. For these reasons + ** this case is also commented out. If the system does set errno to EDEADLK, + ** the default SQLITE_IOERR_XXX code will be returned. */ +#if 0 case EDEADLK: return SQLITE_IOERR_BLOCKED; +#endif #if EOPNOTSUPP!=ENOTSUP case EOPNOTSUPP: @@ -23709,14 +25411,15 @@ struct unixFileId { struct unixInodeInfo { struct unixFileId fileId; /* The lookup key */ int nShared; /* Number of SHARED locks held */ - int eFileLock; /* One of SHARED_LOCK, RESERVED_LOCK etc. */ + unsigned char eFileLock; /* One of SHARED_LOCK, RESERVED_LOCK etc. */ + unsigned char bProcessLock; /* An exclusive process lock is held */ int nRef; /* Number of pointers to this structure */ unixShmNode *pShmNode; /* Shared memory associated with this inode */ int nLock; /* Number of outstanding file locks */ UnixUnusedFd *pUnused; /* Unused file descriptors to close */ unixInodeInfo *pNext; /* List of all unixInodeInfo objects */ unixInodeInfo *pPrev; /* .... doubly linked */ -#if defined(SQLITE_ENABLE_LOCKING_STYLE) +#if SQLITE_ENABLE_LOCKING_STYLE unsigned long long sharedByte; /* for AFP simulated shared lock */ #endif #if OS_VXWORKS @@ -23731,33 +25434,108 @@ struct unixInodeInfo { static unixInodeInfo *inodeList = 0; /* -** Close all file descriptors accumuated in the unixInodeInfo->pUnused list. -** If all such file descriptors are closed without error, the list is -** cleared and SQLITE_OK returned. ** -** Otherwise, if an error occurs, then successfully closed file descriptor -** entries are removed from the list, and SQLITE_IOERR_CLOSE returned. -** not deleted and SQLITE_IOERR_CLOSE returned. +** This function - unixLogError_x(), is only ever called via the macro +** unixLogError(). +** +** It is invoked after an error occurs in an OS function and errno has been +** set. It logs a message using sqlite3_log() containing the current value of +** errno and, if possible, the human-readable equivalent from strerror() or +** strerror_r(). +** +** The first argument passed to the macro should be the error code that +** will be returned to SQLite (e.g. SQLITE_IOERR_DELETE, SQLITE_CANTOPEN). +** The two subsequent arguments should be the name of the OS function that +** failed (e.g. "unlink", "open") and the the associated file-system path, +** if any. +*/ +#define unixLogError(a,b,c) unixLogErrorAtLine(a,b,c,__LINE__) +static int unixLogErrorAtLine( + int errcode, /* SQLite error code */ + const char *zFunc, /* Name of OS function that failed */ + const char *zPath, /* File path associated with error */ + int iLine /* Source line number where error occurred */ +){ + char *zErr; /* Message from strerror() or equivalent */ + int iErrno = errno; /* Saved syscall error number */ + + /* If this is not a threadsafe build (SQLITE_THREADSAFE==0), then use + ** the strerror() function to obtain the human-readable error message + ** equivalent to errno. Otherwise, use strerror_r(). + */ +#if SQLITE_THREADSAFE && defined(HAVE_STRERROR_R) + char aErr[80]; + memset(aErr, 0, sizeof(aErr)); + zErr = aErr; + + /* If STRERROR_R_CHAR_P (set by autoconf scripts) or __USE_GNU is defined, + ** assume that the system provides the the GNU version of strerror_r() that + ** returns a pointer to a buffer containing the error message. That pointer + ** may point to aErr[], or it may point to some static storage somewhere. + ** Otherwise, assume that the system provides the POSIX version of + ** strerror_r(), which always writes an error message into aErr[]. + ** + ** If the code incorrectly assumes that it is the POSIX version that is + ** available, the error message will often be an empty string. Not a + ** huge problem. Incorrectly concluding that the GNU version is available + ** could lead to a segfault though. + */ +#if defined(STRERROR_R_CHAR_P) || defined(__USE_GNU) + zErr = +# endif + strerror_r(iErrno, aErr, sizeof(aErr)-1); + +#elif SQLITE_THREADSAFE + /* This is a threadsafe build, but strerror_r() is not available. */ + zErr = ""; +#else + /* Non-threadsafe build, use strerror(). */ + zErr = strerror(iErrno); +#endif + + assert( errcode!=SQLITE_OK ); + if( zPath==0 ) zPath = ""; + sqlite3_log(errcode, + "os_unix.c:%d: (%d) %s(%s) - %s", + iLine, iErrno, zFunc, zPath, zErr + ); + + return errcode; +} + +/* +** Close a file descriptor. +** +** We assume that close() almost always works, since it is only in a +** very sick application or on a very sick platform that it might fail. +** If it does fail, simply leak the file descriptor, but do log the +** error. +** +** Note that it is not safe to retry close() after EINTR since the +** file descriptor might have already been reused by another thread. +** So we don't even try to recover from an EINTR. Just log the error +** and move on. +*/ +static void robust_close(unixFile *pFile, int h, int lineno){ + if( osClose(h) ){ + unixLogErrorAtLine(SQLITE_IOERR_CLOSE, "close", + pFile ? pFile->zPath : 0, lineno); + } +} + +/* +** Close all file descriptors accumuated in the unixInodeInfo->pUnused list. */ -static int closePendingFds(unixFile *pFile){ - int rc = SQLITE_OK; +static void closePendingFds(unixFile *pFile){ unixInodeInfo *pInode = pFile->pInode; - UnixUnusedFd *pError = 0; UnixUnusedFd *p; UnixUnusedFd *pNext; for(p=pInode->pUnused; p; p=pNext){ pNext = p->pNext; - if( close(p->fd) ){ - pFile->lastErrno = errno; - rc = SQLITE_IOERR_CLOSE; - p->pNext = pError; - pError = p; - }else{ - sqlite3_free(p); - } + robust_close(pFile, p->fd, __LINE__); + sqlite3_free(p); } - pInode->pUnused = pError; - return rc; + pInode->pUnused = 0; } /* @@ -23769,7 +25547,7 @@ static int closePendingFds(unixFile *pFile){ static void releaseInodeInfo(unixFile *pFile){ unixInodeInfo *pInode = pFile->pInode; assert( unixMutexHeld() ); - if( pInode ){ + if( ALWAYS(pInode) ){ pInode->nRef--; if( pInode->nRef==0 ){ assert( pInode->pShmNode==0 ); @@ -23816,7 +25594,7 @@ static int findInodeInfo( ** create a unique name for the file. */ fd = pFile->h; - rc = fstat(fd, &statbuf); + rc = osFstat(fd, &statbuf); if( rc!=0 ){ pFile->lastErrno = errno; #ifdef EOVERFLOW @@ -23837,12 +25615,12 @@ static int findInodeInfo( ** the first page of the database, no damage is done. */ if( statbuf.st_size==0 && (pFile->fsFlags & SQLITE_FSFLAGS_IS_MSDOS)!=0 ){ - rc = write(fd, "S", 1); + do{ rc = osWrite(fd, "S", 1); }while( rc<0 && errno==EINTR ); if( rc!=1 ){ pFile->lastErrno = errno; return SQLITE_IOERR; } - rc = fstat(fd, &statbuf); + rc = osFstat(fd, &statbuf); if( rc!=0 ){ pFile->lastErrno = errno; return SQLITE_IOERR; @@ -23905,16 +25683,15 @@ static int unixCheckReservedLock(sqlite3_file *id, int *pResOut){ /* Otherwise see if some other process holds it. */ #ifndef __DJGPP__ - if( !reserved ){ + if( !reserved && !pFile->pInode->bProcessLock ){ struct flock lock; lock.l_whence = SEEK_SET; lock.l_start = RESERVED_BYTE; lock.l_len = 1; lock.l_type = F_WRLCK; - if (-1 == fcntl(pFile->h, F_GETLK, &lock)) { - int tErrno = errno; - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_CHECKRESERVEDLOCK); - pFile->lastErrno = tErrno; + if( osFcntl(pFile->h, F_GETLK, &lock) ){ + rc = SQLITE_IOERR_CHECKRESERVEDLOCK; + pFile->lastErrno = errno; } else if( lock.l_type!=F_UNLCK ){ reserved = 1; } @@ -23929,6 +25706,53 @@ static int unixCheckReservedLock(sqlite3_file *id, int *pResOut){ } /* +** Attempt to set a system-lock on the file pFile. The lock is +** described by pLock. +** +** If the pFile was opened read/write from unix-excl, then the only lock +** ever obtained is an exclusive lock, and it is obtained exactly once +** the first time any lock is attempted. All subsequent system locking +** operations become no-ops. Locking operations still happen internally, +** in order to coordinate access between separate database connections +** within this process, but all of that is handled in memory and the +** operating system does not participate. +** +** This function is a pass-through to fcntl(F_SETLK) if pFile is using +** any VFS other than "unix-excl" or if pFile is opened on "unix-excl" +** and is read-only. +** +** Zero is returned if the call completes successfully, or -1 if a call +** to fcntl() fails. In this case, errno is set appropriately (by fcntl()). +*/ +static int unixFileLock(unixFile *pFile, struct flock *pLock){ + int rc; + unixInodeInfo *pInode = pFile->pInode; + assert( unixMutexHeld() ); + assert( pInode!=0 ); + if( ((pFile->ctrlFlags & UNIXFILE_EXCL)!=0 || pInode->bProcessLock) + && ((pFile->ctrlFlags & UNIXFILE_RDONLY)==0) + ){ + if( pInode->bProcessLock==0 ){ + struct flock lock; + assert( pInode->nLock==0 ); + lock.l_whence = SEEK_SET; + lock.l_start = SHARED_FIRST; + lock.l_len = SHARED_SIZE; + lock.l_type = F_WRLCK; + rc = osFcntl(pFile->h, F_SETLK, &lock); + if( rc<0 ) return rc; + pInode->bProcessLock = 1; + pInode->nLock++; + }else{ + rc = 0; + } + }else{ + rc = osFcntl(pFile->h, F_SETLK, pLock); + } + return rc; +} + +/* ** Lock the file with the lock specified by parameter eFileLock - one ** of the following: ** @@ -23995,7 +25819,6 @@ static int unixLock(sqlite3_file *id, int eFileLock){ unixFile *pFile = (unixFile*)id; unixInodeInfo *pInode = pFile->pInode; struct flock lock; - int s = 0; int tErrno = 0; assert( pFile ); @@ -24064,11 +25887,10 @@ static int unixLock(sqlite3_file *id, int eFileLock){ ){ lock.l_type = (eFileLock==SHARED_LOCK?F_RDLCK:F_WRLCK); lock.l_start = PENDING_BYTE; - s = fcntl(pFile->h, F_SETLK, &lock); - if( s==(-1) ){ + if( unixFileLock(pFile, &lock) ){ tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); - if( IS_LOCK_ERROR(rc) ){ + if( rc!=SQLITE_BUSY ){ pFile->lastErrno = tErrno; } goto end_lock; @@ -24082,33 +25904,31 @@ static int unixLock(sqlite3_file *id, int eFileLock){ if( eFileLock==SHARED_LOCK ){ assert( pInode->nShared==0 ); assert( pInode->eFileLock==0 ); + assert( rc==SQLITE_OK ); /* Now get the read-lock */ lock.l_start = SHARED_FIRST; lock.l_len = SHARED_SIZE; - if( (s = fcntl(pFile->h, F_SETLK, &lock))==(-1) ){ + if( unixFileLock(pFile, &lock) ){ tErrno = errno; + rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); } + /* Drop the temporary PENDING lock */ lock.l_start = PENDING_BYTE; lock.l_len = 1L; lock.l_type = F_UNLCK; - if( fcntl(pFile->h, F_SETLK, &lock)!=0 ){ - if( s != -1 ){ - /* This could happen with a network mount */ - tErrno = errno; - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); - if( IS_LOCK_ERROR(rc) ){ - pFile->lastErrno = tErrno; - } - goto end_lock; - } + if( unixFileLock(pFile, &lock) && rc==SQLITE_OK ){ + /* This could happen with a network mount */ + tErrno = errno; + rc = SQLITE_IOERR_UNLOCK; } - if( s==(-1) ){ - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); - if( IS_LOCK_ERROR(rc) ){ + + if( rc ){ + if( rc!=SQLITE_BUSY ){ pFile->lastErrno = tErrno; } + goto end_lock; }else{ pFile->eFileLock = SHARED_LOCK; pInode->nLock++; @@ -24125,22 +25945,20 @@ static int unixLock(sqlite3_file *id, int eFileLock){ */ assert( 0!=pFile->eFileLock ); lock.l_type = F_WRLCK; - switch( eFileLock ){ - case RESERVED_LOCK: - lock.l_start = RESERVED_BYTE; - break; - case EXCLUSIVE_LOCK: - lock.l_start = SHARED_FIRST; - lock.l_len = SHARED_SIZE; - break; - default: - assert(0); + + assert( eFileLock==RESERVED_LOCK || eFileLock==EXCLUSIVE_LOCK ); + if( eFileLock==RESERVED_LOCK ){ + lock.l_start = RESERVED_BYTE; + lock.l_len = 1L; + }else{ + lock.l_start = SHARED_FIRST; + lock.l_len = SHARED_SIZE; } - s = fcntl(pFile->h, F_SETLK, &lock); - if( s==(-1) ){ + + if( unixFileLock(pFile, &lock) ){ tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); - if( IS_LOCK_ERROR(rc) ){ + if( rc!=SQLITE_BUSY ){ pFile->lastErrno = tErrno; } } @@ -24205,13 +26023,12 @@ static void setPendingFd(unixFile *pFile){ ** around a bug in BSD NFS lockd (also seen on MacOSX 10.3+) that fails to ** remove the write lock on a region when a read lock is set. */ -static int _posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ +static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ unixFile *pFile = (unixFile*)id; unixInodeInfo *pInode; struct flock lock; int rc = SQLITE_OK; int h; - int tErrno; /* Error code from system call errors */ assert( pFile ); OSTRACE(("UNLOCK %d %d was %d(%d,%d) pid=%d (unix)\n", pFile->h, eFileLock, @@ -24259,16 +26076,23 @@ static int _posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ ** 4: [RRRR.] */ if( eFileLock==SHARED_LOCK ){ + +#if !defined(__APPLE__) || !SQLITE_ENABLE_LOCKING_STYLE + (void)handleNFSUnlock; + assert( handleNFSUnlock==0 ); +#endif +#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE if( handleNFSUnlock ){ + int tErrno; /* Error code from system call errors */ off_t divSize = SHARED_SIZE - 1; lock.l_type = F_UNLCK; lock.l_whence = SEEK_SET; lock.l_start = SHARED_FIRST; lock.l_len = divSize; - if( fcntl(h, F_SETLK, &lock)==(-1) ){ + if( unixFileLock(pFile, &lock)==(-1) ){ tErrno = errno; - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); + rc = SQLITE_IOERR_UNLOCK; if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; } @@ -24278,7 +26102,7 @@ static int _posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ lock.l_whence = SEEK_SET; lock.l_start = SHARED_FIRST; lock.l_len = divSize; - if( fcntl(h, F_SETLK, &lock)==(-1) ){ + if( unixFileLock(pFile, &lock)==(-1) ){ tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_RDLOCK); if( IS_LOCK_ERROR(rc) ){ @@ -24290,25 +26114,30 @@ static int _posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ lock.l_whence = SEEK_SET; lock.l_start = SHARED_FIRST+divSize; lock.l_len = SHARED_SIZE-divSize; - if( fcntl(h, F_SETLK, &lock)==(-1) ){ + if( unixFileLock(pFile, &lock)==(-1) ){ tErrno = errno; - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); + rc = SQLITE_IOERR_UNLOCK; if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; } goto end_unlock; } - }else{ + }else +#endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */ + { lock.l_type = F_RDLCK; lock.l_whence = SEEK_SET; lock.l_start = SHARED_FIRST; lock.l_len = SHARED_SIZE; - if( fcntl(h, F_SETLK, &lock)==(-1) ){ - tErrno = errno; - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_RDLOCK); - if( IS_LOCK_ERROR(rc) ){ - pFile->lastErrno = tErrno; - } + if( unixFileLock(pFile, &lock) ){ + /* In theory, the call to unixFileLock() cannot fail because another + ** process is holding an incompatible lock. If it does, this + ** indicates that the other process is not following the locking + ** protocol. If this happens, return SQLITE_IOERR_RDLOCK. Returning + ** SQLITE_BUSY would confuse the upper layer (in practice it causes + ** an assert to fail). */ + rc = SQLITE_IOERR_RDLOCK; + pFile->lastErrno = errno; goto end_unlock; } } @@ -24317,14 +26146,11 @@ static int _posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ lock.l_whence = SEEK_SET; lock.l_start = PENDING_BYTE; lock.l_len = 2L; assert( PENDING_BYTE+1==RESERVED_BYTE ); - if( fcntl(h, F_SETLK, &lock)!=(-1) ){ + if( unixFileLock(pFile, &lock)==0 ){ pInode->eFileLock = SHARED_LOCK; }else{ - tErrno = errno; - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); - if( IS_LOCK_ERROR(rc) ){ - pFile->lastErrno = tErrno; - } + rc = SQLITE_IOERR_UNLOCK; + pFile->lastErrno = errno; goto end_unlock; } } @@ -24341,14 +26167,11 @@ static int _posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ SimulateIOErrorBenign(1); SimulateIOError( h=(-1) ) SimulateIOErrorBenign(0); - if( fcntl(h, F_SETLK, &lock)!=(-1) ){ + if( unixFileLock(pFile, &lock)==0 ){ pInode->eFileLock = NO_LOCK; }else{ - tErrno = errno; - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); - if( IS_LOCK_ERROR(rc) ){ - pFile->lastErrno = tErrno; - } + rc = SQLITE_IOERR_UNLOCK; + pFile->lastErrno = errno; pInode->eFileLock = NO_LOCK; pFile->eFileLock = NO_LOCK; } @@ -24361,10 +26184,7 @@ static int _posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ pInode->nLock--; assert( pInode->nLock>=0 ); if( pInode->nLock==0 ){ - int rc2 = closePendingFds(pFile); - if( rc==SQLITE_OK ){ - rc = rc2; - } + closePendingFds(pFile); } } @@ -24382,7 +26202,7 @@ end_unlock: ** the requested locking level, this routine is a no-op. */ static int unixUnlock(sqlite3_file *id, int eFileLock){ - return _posixUnlock(id, eFileLock, 0); + return posixUnlock(id, eFileLock, 0); } /* @@ -24397,37 +26217,27 @@ static int unixUnlock(sqlite3_file *id, int eFileLock){ */ static int closeUnixFile(sqlite3_file *id){ unixFile *pFile = (unixFile*)id; - if( pFile ){ - if( pFile->dirfd>=0 ){ - int err = close(pFile->dirfd); - if( err ){ - pFile->lastErrno = errno; - return SQLITE_IOERR_DIR_CLOSE; - }else{ - pFile->dirfd=-1; - } - } - if( pFile->h>=0 ){ - int err = close(pFile->h); - if( err ){ - pFile->lastErrno = errno; - return SQLITE_IOERR_CLOSE; - } - } + if( pFile->dirfd>=0 ){ + robust_close(pFile, pFile->dirfd, __LINE__); + pFile->dirfd=-1; + } + if( pFile->h>=0 ){ + robust_close(pFile, pFile->h, __LINE__); + pFile->h = -1; + } #if OS_VXWORKS - if( pFile->pId ){ - if( pFile->isDelete ){ - unlink(pFile->pId->zCanonicalName); - } - vxworksReleaseFileId(pFile->pId); - pFile->pId = 0; + if( pFile->pId ){ + if( pFile->isDelete ){ + unlink(pFile->pId->zCanonicalName); } -#endif - OSTRACE(("CLOSE %-3d\n", pFile->h)); - OpenCounter(-1); - sqlite3_free(pFile->pUnused); - memset(pFile, 0, sizeof(unixFile)); + vxworksReleaseFileId(pFile->pId); + pFile->pId = 0; } +#endif + OSTRACE(("CLOSE %-3d\n", pFile->h)); + OpenCounter(-1); + sqlite3_free(pFile->pUnused); + memset(pFile, 0, sizeof(unixFile)); return SQLITE_OK; } @@ -24436,22 +26246,25 @@ static int closeUnixFile(sqlite3_file *id){ */ static int unixClose(sqlite3_file *id){ int rc = SQLITE_OK; - if( id ){ - unixFile *pFile = (unixFile *)id; - unixUnlock(id, NO_LOCK); - unixEnterMutex(); - if( pFile->pInode && pFile->pInode->nLock ){ - /* If there are outstanding locks, do not actually close the file just - ** yet because that would clear those locks. Instead, add the file - ** descriptor to pInode->pUnused list. It will be automatically closed - ** when the last lock is cleared. - */ - setPendingFd(pFile); - } - releaseInodeInfo(pFile); - rc = closeUnixFile(id); - unixLeaveMutex(); + unixFile *pFile = (unixFile *)id; + unixUnlock(id, NO_LOCK); + unixEnterMutex(); + + /* unixFile.pInode is always valid here. Otherwise, a different close + ** routine (e.g. nolockClose()) would be called instead. + */ + assert( pFile->pInode->nLock>0 || pFile->pInode->bProcessLock==0 ); + if( ALWAYS(pFile->pInode) && pFile->pInode->nLock ){ + /* If there are outstanding locks, do not actually close the file just + ** yet because that would clear those locks. Instead, add the file + ** descriptor to pInode->pUnused list. It will be automatically closed + ** when the last lock is cleared. + */ + setPendingFd(pFile); } + releaseInodeInfo(pFile); + rc = closeUnixFile(id); + unixLeaveMutex(); return rc; } @@ -24554,7 +26367,7 @@ static int dotlockCheckReservedLock(sqlite3_file *id, int *pResOut) { }else{ /* The lock is held if and only if the lockfile exists */ const char *zLockFile = (const char*)pFile->lockingContext; - reserved = access(zLockFile, 0)==0; + reserved = osAccess(zLockFile, 0)==0; } OSTRACE(("TEST WR-LOCK %d %d %d (dotlock)\n", pFile->h, rc, reserved)); *pResOut = reserved; @@ -24600,15 +26413,17 @@ static int dotlockLock(sqlite3_file *id, int eFileLock) { */ if( pFile->eFileLock > NO_LOCK ){ pFile->eFileLock = eFileLock; -#if !OS_VXWORKS /* Always update the timestamp on the old file */ +#ifdef HAVE_UTIME + utime(zLockFile, NULL); +#else utimes(zLockFile, NULL); #endif return SQLITE_OK; } /* grab an exclusive lock */ - fd = open(zLockFile,O_RDONLY|O_CREAT|O_EXCL,0600); + fd = robust_open(zLockFile,O_RDONLY|O_CREAT|O_EXCL,0600); if( fd<0 ){ /* failed to open/create the file, someone else may have stolen the lock */ int tErrno = errno; @@ -24622,10 +26437,7 @@ static int dotlockLock(sqlite3_file *id, int eFileLock) { } return rc; } - if( close(fd) ){ - pFile->lastErrno = errno; - rc = SQLITE_IOERR_CLOSE; - } + robust_close(pFile, fd, __LINE__); /* got it, set the type and return ok */ pFile->eFileLock = eFileLock; @@ -24669,7 +26481,7 @@ static int dotlockUnlock(sqlite3_file *id, int eFileLock) { int rc = 0; int tErrno = errno; if( ENOENT != tErrno ){ - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); + rc = SQLITE_IOERR_UNLOCK; } if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; @@ -24714,6 +26526,20 @@ static int dotlockClose(sqlite3_file *id) { #if SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS /* +** Retry flock() calls that fail with EINTR +*/ +#ifdef EINTR +static int robust_flock(int fd, int op){ + int rc; + do{ rc = flock(fd,op); }while( rc<0 && errno==EINTR ); + return rc; +} +#else +# define robust_flock(a,b) flock(a,b) +#endif + + +/* ** This routine checks if there is a RESERVED lock held on the specified ** file by this or any other process. If such a lock is held, set *pResOut ** to a non-zero value otherwise *pResOut is set to zero. The return value @@ -24736,14 +26562,14 @@ static int flockCheckReservedLock(sqlite3_file *id, int *pResOut){ /* Otherwise see if some other process holds it. */ if( !reserved ){ /* attempt to get the lock */ - int lrc = flock(pFile->h, LOCK_EX | LOCK_NB); + int lrc = robust_flock(pFile->h, LOCK_EX | LOCK_NB); if( !lrc ){ /* got the lock, unlock it */ - lrc = flock(pFile->h, LOCK_UN); + lrc = robust_flock(pFile->h, LOCK_UN); if ( lrc ) { int tErrno = errno; /* unlock failed with an error */ - lrc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); + lrc = SQLITE_IOERR_UNLOCK; if( IS_LOCK_ERROR(lrc) ){ pFile->lastErrno = tErrno; rc = lrc; @@ -24816,7 +26642,7 @@ static int flockLock(sqlite3_file *id, int eFileLock) { /* grab an exclusive lock */ - if (flock(pFile->h, LOCK_EX | LOCK_NB)) { + if (robust_flock(pFile->h, LOCK_EX | LOCK_NB)) { int tErrno = errno; /* didn't get, must be busy */ rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); @@ -24865,21 +26691,12 @@ static int flockUnlock(sqlite3_file *id, int eFileLock) { } /* no, really, unlock. */ - int rc = flock(pFile->h, LOCK_UN); - if (rc) { - int r, tErrno = errno; - r = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); - if( IS_LOCK_ERROR(r) ){ - pFile->lastErrno = tErrno; - } + if( robust_flock(pFile->h, LOCK_UN) ){ #ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS - if( (r & SQLITE_IOERR) == SQLITE_IOERR ){ - r = SQLITE_BUSY; - } + return SQLITE_OK; #endif /* SQLITE_IGNORE_FLOCK_LOCK_ERRORS */ - - return r; - } else { + return SQLITE_IOERR_UNLOCK; + }else{ pFile->eFileLock = NO_LOCK; return SQLITE_OK; } @@ -25503,7 +27320,7 @@ static int afpUnlock(sqlite3_file *id, int eFileLock) { pInode->nLock--; assert( pInode->nLock>=0 ); if( pInode->nLock==0 ){ - rc = closePendingFds(pFile); + closePendingFds(pFile); } } } @@ -25560,7 +27377,7 @@ static int afpClose(sqlite3_file *id) { ** the requested locking level, this routine is a no-op. */ static int nfsUnlock(sqlite3_file *id, int eFileLock){ - return _posixUnlock(id, eFileLock, 1); + return posixUnlock(id, eFileLock, 1); } #endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */ @@ -25602,10 +27419,10 @@ static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){ #endif TIMER_START; #if defined(USE_PREAD) - got = pread(id->h, pBuf, cnt, offset); + do{ got = osPread(id->h, pBuf, cnt, offset); }while( got<0 && errno==EINTR ); SimulateIOError( got = -1 ); #elif defined(USE_PREAD64) - got = pread64(id->h, pBuf, cnt, offset); + do{ got = osPread64(id->h, pBuf, cnt, offset); }while( got<0 && errno==EINTR); SimulateIOError( got = -1 ); #else newOffset = lseek(id->h, offset, SEEK_SET); @@ -25618,7 +27435,7 @@ static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){ } return -1; } - got = read(id->h, pBuf, cnt); + do{ got = osRead(id->h, pBuf, cnt); }while( got<0 && errno==EINTR ); #endif TIMER_END; if( got<0 ){ @@ -25680,11 +27497,12 @@ static int seekAndWrite(unixFile *id, i64 offset, const void *pBuf, int cnt){ #endif TIMER_START; #if defined(USE_PREAD) - got = pwrite(id->h, pBuf, cnt, offset); + do{ got = osPwrite(id->h, pBuf, cnt, offset); }while( got<0 && errno==EINTR ); #elif defined(USE_PREAD64) - got = pwrite64(id->h, pBuf, cnt, offset); + do{ got = osPwrite64(id->h, pBuf, cnt, offset);}while( got<0 && errno==EINTR); #else newOffset = lseek(id->h, offset, SEEK_SET); + SimulateIOError( newOffset-- ); if( newOffset!=offset ){ if( newOffset == -1 ){ ((unixFile*)id)->lastErrno = errno; @@ -25693,7 +27511,7 @@ static int seekAndWrite(unixFile *id, i64 offset, const void *pBuf, int cnt){ } return -1; } - got = write(id->h, pBuf, cnt); + do{ got = osWrite(id->h, pBuf, cnt); }while( got<0 && errno==EINTR ); #endif TIMER_END; if( got<0 ){ @@ -25760,7 +27578,7 @@ static int unixWrite( SimulateDiskfullError(( wrote=0, amt=1 )); if( amt>0 ){ - if( wrote<0 ){ + if( wrote<0 && pFile->lastErrno!=ENOSPC ){ /* lastErrno set by seekAndWrite */ return SQLITE_IOERR_WRITE; }else{ @@ -25861,7 +27679,7 @@ static int full_fsync(int fd, int fullSync, int dataOnly){ rc = SQLITE_OK; #elif HAVE_FULLFSYNC if( fullSync ){ - rc = fcntl(fd, F_FULLFSYNC, 0); + rc = osFcntl(fd, F_FULLFSYNC, 0); }else{ rc = 1; } @@ -25933,10 +27751,9 @@ static int unixSync(sqlite3_file *id, int flags){ SimulateIOError( rc=1 ); if( rc ){ pFile->lastErrno = errno; - return SQLITE_IOERR_FSYNC; + return unixLogError(SQLITE_IOERR_FSYNC, "full_fsync", pFile->zPath); } if( pFile->dirfd>=0 ){ - int err; OSTRACE(("DIRSYNC %-3d (have_fullfsync=%d fullsync=%d)\n", pFile->dirfd, HAVE_FULLFSYNC, isFullsync)); #ifndef SQLITE_DISABLE_DIRSYNC @@ -25955,13 +27772,9 @@ static int unixSync(sqlite3_file *id, int flags){ /* return SQLITE_IOERR; */ } #endif - err = close(pFile->dirfd); /* Only need to sync once, so close the */ - if( err==0 ){ /* directory when we are done */ - pFile->dirfd = -1; - }else{ - pFile->lastErrno = errno; - rc = SQLITE_IOERR_DIR_CLOSE; - } + /* Only need to sync once, so close the directory when we are done */ + robust_close(pFile, pFile->dirfd, __LINE__); + pFile->dirfd = -1; } return rc; } @@ -25984,10 +27797,10 @@ static int unixTruncate(sqlite3_file *id, i64 nByte){ nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk; } - rc = ftruncate(pFile->h, (off_t)nByte); + rc = robust_ftruncate(pFile->h, (off_t)nByte); if( rc ){ pFile->lastErrno = errno; - return SQLITE_IOERR_TRUNCATE; + return unixLogError(SQLITE_IOERR_TRUNCATE, "ftruncate", pFile->zPath); }else{ #ifndef NDEBUG /* If we are doing a normal write to a database file (as opposed to @@ -26013,7 +27826,7 @@ static int unixFileSize(sqlite3_file *id, i64 *pSize){ int rc; struct stat buf; assert( id ); - rc = fstat(((unixFile*)id)->h, &buf); + rc = osFstat(((unixFile*)id)->h, &buf); SimulateIOError( rc=1 ); if( rc!=0 ){ ((unixFile*)id)->lastErrno = errno; @@ -26054,14 +27867,20 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){ i64 nSize; /* Required file size */ struct stat buf; /* Used to hold return values of fstat() */ - if( fstat(pFile->h, &buf) ) return SQLITE_IOERR_FSTAT; + if( osFstat(pFile->h, &buf) ) return SQLITE_IOERR_FSTAT; nSize = ((nByte+pFile->szChunk-1) / pFile->szChunk) * pFile->szChunk; if( nSize>(i64)buf.st_size ){ + #if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE - if( posix_fallocate(pFile->h, buf.st_size, nSize-buf.st_size) ){ - return SQLITE_IOERR_WRITE; - } + /* The code below is handling the return value of osFallocate() + ** correctly. posix_fallocate() is defined to "returns zero on success, + ** or an error number on failure". See the manpage for details. */ + int err; + do{ + err = osFallocate(pFile->h, buf.st_size, nSize-buf.st_size); + }while( err==EINTR ); + if( err ) return SQLITE_IOERR_WRITE; #else /* If the OS does not have posix_fallocate(), fake it. First use ** ftruncate() to set the file size, then write a single byte to @@ -26071,18 +27890,17 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){ */ int nBlk = buf.st_blksize; /* File-system block size */ i64 iWrite; /* Next offset to write to */ - int nWrite; /* Return value from seekAndWrite() */ - if( ftruncate(pFile->h, nSize) ){ + if( robust_ftruncate(pFile->h, nSize) ){ pFile->lastErrno = errno; - return SQLITE_IOERR_TRUNCATE; + return unixLogError(SQLITE_IOERR_TRUNCATE, "ftruncate", pFile->zPath); } iWrite = ((buf.st_size + 2*nBlk - 1)/nBlk)*nBlk-1; - do { - nWrite = seekAndWrite(pFile, iWrite, "", 1); + while( iWrite<nSize ){ + int nWrite = seekAndWrite(pFile, iWrite, "", 1); + if( nWrite!=1 ) return SQLITE_IOERR_WRITE; iWrite += nBlk; - } while( nWrite==1 && iWrite<nSize ); - if( nWrite!=1 ) return SQLITE_IOERR_WRITE; + } #endif } } @@ -26127,8 +27945,11 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){ return proxyFileControl(id,op,pArg); } #endif /* SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) */ + case SQLITE_FCNTL_SYNC_OMITTED: { + return SQLITE_OK; /* A no-op */ + } } - return SQLITE_ERROR; + return SQLITE_NOTFOUND; } /* @@ -26192,7 +28013,8 @@ struct unixShmNode { char *zFilename; /* Name of the mmapped file */ int h; /* Open file descriptor */ int szRegion; /* Size of shared-memory regions */ - int nRegion; /* Size of array apRegion */ + u16 nRegion; /* Size of array apRegion */ + u8 isReadonly; /* True if read-only */ char **apRegion; /* Array of mapped shared-memory regions */ int nRef; /* Number of unixShm objects pointing to this */ unixShm *pFirst; /* All unixShm objects pointing to this */ @@ -26257,15 +28079,17 @@ static int unixShmSystemLock( /* Locks are within range */ assert( n>=1 && n<SQLITE_SHM_NLOCK ); - /* Initialize the locking parameters */ - memset(&f, 0, sizeof(f)); - f.l_type = lockType; - f.l_whence = SEEK_SET; - f.l_start = ofst; - f.l_len = n; + if( pShmNode->h>=0 ){ + /* Initialize the locking parameters */ + memset(&f, 0, sizeof(f)); + f.l_type = lockType; + f.l_whence = SEEK_SET; + f.l_start = ofst; + f.l_len = n; - rc = fcntl(pShmNode->h, F_SETLK, &f); - rc = (rc!=(-1)) ? SQLITE_OK : SQLITE_BUSY; + rc = osFcntl(pShmNode->h, F_SETLK, &f); + rc = (rc!=(-1)) ? SQLITE_OK : SQLITE_BUSY; + } /* Update the global lock state and do debug tracing */ #ifdef SQLITE_DEBUG @@ -26320,10 +28144,17 @@ static void unixShmPurge(unixFile *pFd){ assert( p->pInode==pFd->pInode ); if( p->mutex ) sqlite3_mutex_free(p->mutex); for(i=0; i<p->nRegion; i++){ - munmap(p->apRegion[i], p->szRegion); + if( p->h>=0 ){ + munmap(p->apRegion[i], p->szRegion); + }else{ + sqlite3_free(p->apRegion[i]); + } } sqlite3_free(p->apRegion); - if( p->h>=0 ) close(p->h); + if( p->h>=0 ){ + robust_close(pFd, p->h, __LINE__); + p->h = -1; + } p->pInode->pShmNode = 0; sqlite3_free(p); } @@ -26357,6 +28188,12 @@ static void unixShmPurge(unixFile *pFd){ ** When opening a new shared-memory file, if no other instances of that ** file are currently open, in this process or in other processes, then ** the file must be truncated to zero length or have its header cleared. +** +** If the original database file (pDbFd) is using the "unix-excl" VFS +** that means that an exclusive lock is held on the database file and +** that no other processes are able to read or write the database. In +** that case, we do not really need shared memory. No shared memory +** file is created. The shared memory will be simulated with heap memory. */ static int unixOpenSharedMemory(unixFile *pDbFd){ struct unixShm *p = 0; /* The connection to be opened */ @@ -26386,7 +28223,7 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ ** with the same permissions. The actual permissions the file is created ** with are subject to the current umask setting. */ - if( fstat(pDbFd->h, &sStat) ){ + if( osFstat(pDbFd->h, &sStat) && pInode->bProcessLock==0 ){ rc = SQLITE_IOERR_FSTAT; goto shm_open_err; } @@ -26409,6 +28246,7 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ (u32)sStat.st_ino, (u32)sStat.st_dev); #else sqlite3_snprintf(nShmFilename, zShmFilename, "%s-shm", pDbFd->zPath); + sqlite3FileSuffix3(pDbFd->zPath, zShmFilename); #endif pShmNode->h = -1; pDbFd->pInode->pShmNode = pShmNode; @@ -26419,25 +28257,37 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ goto shm_open_err; } - pShmNode->h = open(zShmFilename, O_RDWR|O_CREAT, (sStat.st_mode & 0777)); - if( pShmNode->h<0 ){ - rc = SQLITE_CANTOPEN_BKPT; - goto shm_open_err; - } - - /* Check to see if another process is holding the dead-man switch. - ** If not, truncate the file to zero length. - */ - rc = SQLITE_OK; - if( unixShmSystemLock(pShmNode, F_WRLCK, UNIX_SHM_DMS, 1)==SQLITE_OK ){ - if( ftruncate(pShmNode->h, 0) ){ - rc = SQLITE_IOERR_SHMOPEN; + if( pInode->bProcessLock==0 ){ + pShmNode->h = robust_open(zShmFilename, O_RDWR|O_CREAT, + (sStat.st_mode & 0777)); + if( pShmNode->h<0 ){ + const char *zRO; + zRO = sqlite3_uri_parameter(pDbFd->zPath, "readonly_shm"); + if( zRO && sqlite3GetBoolean(zRO) ){ + pShmNode->h = robust_open(zShmFilename, O_RDONLY, + (sStat.st_mode & 0777)); + pShmNode->isReadonly = 1; + } + if( pShmNode->h<0 ){ + rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zShmFilename); + goto shm_open_err; + } } + + /* Check to see if another process is holding the dead-man switch. + ** If not, truncate the file to zero length. + */ + rc = SQLITE_OK; + if( unixShmSystemLock(pShmNode, F_WRLCK, UNIX_SHM_DMS, 1)==SQLITE_OK ){ + if( robust_ftruncate(pShmNode->h, 0) ){ + rc = unixLogError(SQLITE_IOERR_SHMOPEN, "ftruncate", zShmFilename); + } + } + if( rc==SQLITE_OK ){ + rc = unixShmSystemLock(pShmNode, F_RDLCK, UNIX_SHM_DMS, 1); + } + if( rc ) goto shm_open_err; } - if( rc==SQLITE_OK ){ - rc = unixShmSystemLock(pShmNode, F_RDLCK, UNIX_SHM_DMS, 1); - } - if( rc ) goto shm_open_err; } /* Make the new connection a child of the unixShmNode */ @@ -26511,6 +28361,9 @@ static int unixShmMap( pShmNode = p->pShmNode; sqlite3_mutex_enter(pShmNode->mutex); assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 ); + assert( pShmNode->pInode==pDbFd->pInode ); + assert( pShmNode->h>=0 || pDbFd->pInode->bProcessLock==1 ); + assert( pShmNode->h<0 || pDbFd->pInode->bProcessLock==0 ); if( pShmNode->nRegion<=iRegion ){ char **apNew; /* New apRegion[] array */ @@ -26519,27 +28372,30 @@ static int unixShmMap( pShmNode->szRegion = szRegion; - /* The requested region is not mapped into this processes address space. - ** Check to see if it has been allocated (i.e. if the wal-index file is - ** large enough to contain the requested region). - */ - if( fstat(pShmNode->h, &sStat) ){ - rc = SQLITE_IOERR_SHMSIZE; - goto shmpage_out; - } - - if( sStat.st_size<nByte ){ - /* The requested memory region does not exist. If bExtend is set to - ** false, exit early. *pp will be set to NULL and SQLITE_OK returned. - ** - ** Alternatively, if bExtend is true, use ftruncate() to allocate - ** the requested memory region. + if( pShmNode->h>=0 ){ + /* The requested region is not mapped into this processes address space. + ** Check to see if it has been allocated (i.e. if the wal-index file is + ** large enough to contain the requested region). */ - if( !bExtend ) goto shmpage_out; - if( ftruncate(pShmNode->h, nByte) ){ + if( osFstat(pShmNode->h, &sStat) ){ rc = SQLITE_IOERR_SHMSIZE; goto shmpage_out; } + + if( sStat.st_size<nByte ){ + /* The requested memory region does not exist. If bExtend is set to + ** false, exit early. *pp will be set to NULL and SQLITE_OK returned. + ** + ** Alternatively, if bExtend is true, use ftruncate() to allocate + ** the requested memory region. + */ + if( !bExtend ) goto shmpage_out; + if( robust_ftruncate(pShmNode->h, nByte) ){ + rc = unixLogError(SQLITE_IOERR_SHMSIZE, "ftruncate", + pShmNode->zFilename); + goto shmpage_out; + } + } } /* Map the requested memory region into this processes address space. */ @@ -26552,12 +28408,23 @@ static int unixShmMap( } pShmNode->apRegion = apNew; while(pShmNode->nRegion<=iRegion){ - void *pMem = mmap(0, szRegion, PROT_READ|PROT_WRITE, - MAP_SHARED, pShmNode->h, pShmNode->nRegion*szRegion - ); - if( pMem==MAP_FAILED ){ - rc = SQLITE_IOERR; - goto shmpage_out; + void *pMem; + if( pShmNode->h>=0 ){ + pMem = mmap(0, szRegion, + pShmNode->isReadonly ? PROT_READ : PROT_READ|PROT_WRITE, + MAP_SHARED, pShmNode->h, pShmNode->nRegion*szRegion + ); + if( pMem==MAP_FAILED ){ + rc = unixLogError(SQLITE_IOERR_SHMMAP, "mmap", pShmNode->zFilename); + goto shmpage_out; + } + }else{ + pMem = sqlite3_malloc(szRegion); + if( pMem==0 ){ + rc = SQLITE_NOMEM; + goto shmpage_out; + } + memset(pMem, 0, szRegion); } pShmNode->apRegion[pShmNode->nRegion] = pMem; pShmNode->nRegion++; @@ -26570,6 +28437,7 @@ shmpage_out: }else{ *pp = 0; } + if( pShmNode->isReadonly && rc==SQLITE_OK ) rc = SQLITE_READONLY; sqlite3_mutex_leave(pShmNode->mutex); return rc; } @@ -26604,6 +28472,8 @@ static int unixShmLock( || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED) || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) ); assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 ); + assert( pShmNode->h>=0 || pDbFd->pInode->bProcessLock==1 ); + assert( pShmNode->h<0 || pDbFd->pInode->bProcessLock==0 ); mask = (1<<(ofst+n)) - (1<<ofst); assert( n>1 || mask==(1<<ofst) ); @@ -26741,7 +28611,7 @@ static int unixShmUnmap( assert( pShmNode->nRef>0 ); pShmNode->nRef--; if( pShmNode->nRef==0 ){ - if( deleteFlag ) unlink(pShmNode->zFilename); + if( deleteFlag && pShmNode->h>=0 ) unlink(pShmNode->zFilename); unixShmPurge(pDbFd); } unixLeaveMutex(); @@ -26982,7 +28852,7 @@ static const sqlite3_io_methods *autolockIoFinderImpl( lockInfo.l_start = 0; lockInfo.l_whence = SEEK_SET; lockInfo.l_type = F_RDLCK; - if( fcntl(pNew->h, F_GETLK, &lockInfo)!=-1 ) { + if( osFcntl(pNew->h, F_GETLK, &lockInfo)!=-1 ) { if( strcmp(fsInfo.f_fstypename, "nfs")==0 ){ return &nfsIoMethods; } else { @@ -27024,7 +28894,7 @@ static const sqlite3_io_methods *autolockIoFinderImpl( lockInfo.l_start = 0; lockInfo.l_whence = SEEK_SET; lockInfo.l_type = F_RDLCK; - if( fcntl(pNew->h, F_GETLK, &lockInfo)!=-1 ) { + if( osFcntl(pNew->h, F_GETLK, &lockInfo)!=-1 ) { return &posixIoMethods; }else{ return &semIoMethods; @@ -27058,7 +28928,8 @@ static int fillInUnixFile( sqlite3_file *pId, /* Write to the unixFile structure here */ const char *zFilename, /* Name of the file being opened */ int noLock, /* Omit locking if true */ - int isDelete /* Delete on close if true */ + int isDelete, /* Delete on close if true */ + int isReadOnly /* True if the file is opened read-only */ ){ const sqlite3_io_methods *pLockingStyle; unixFile *pNew = (unixFile *)pId; @@ -27085,8 +28956,15 @@ static int fillInUnixFile( OSTRACE(("OPEN %-3d %s\n", h, zFilename)); pNew->h = h; pNew->dirfd = dirfd; - pNew->fileFlags = 0; pNew->zPath = zFilename; + if( memcmp(pVfs->zName,"unix-excl",10)==0 ){ + pNew->ctrlFlags = UNIXFILE_EXCL; + }else{ + pNew->ctrlFlags = 0; + } + if( isReadOnly ){ + pNew->ctrlFlags |= UNIXFILE_RDONLY; + } #if OS_VXWORKS pNew->pId = vxworksFindFileId(zFilename); @@ -27134,7 +29012,7 @@ static int fillInUnixFile( ** implicit assumption here is that if fstat() fails, things are in ** such bad shape that dropping a lock or two doesn't matter much. */ - close(h); + robust_close(pNew, h, __LINE__); h = -1; } unixLeaveMutex(); @@ -27160,7 +29038,7 @@ static int fillInUnixFile( rc = findInodeInfo(pNew, &pNew->pInode); if( rc!=SQLITE_OK ){ sqlite3_free(pNew->lockingContext); - close(h); + robust_close(pNew, h, __LINE__); h = -1; } unixLeaveMutex(); @@ -27211,7 +29089,7 @@ static int fillInUnixFile( pNew->lastErrno = 0; #if OS_VXWORKS if( rc!=SQLITE_OK ){ - if( h>=0 ) close(h); + if( h>=0 ) robust_close(pNew, h, __LINE__); h = -1; unlink(zFilename); isDelete = 0; @@ -27219,8 +29097,8 @@ static int fillInUnixFile( pNew->isDelete = isDelete; #endif if( rc!=SQLITE_OK ){ - if( dirfd>=0 ) close(dirfd); /* silent leak if fail, already in error */ - if( h>=0 ) close(h); + if( dirfd>=0 ) robust_close(pNew, dirfd, __LINE__); + if( h>=0 ) robust_close(pNew, h, __LINE__); }else{ pNew->pMethod = pLockingStyle; OpenCounter(+1); @@ -27247,16 +29125,16 @@ static int openDirectory(const char *zFilename, int *pFd){ for(ii=(int)strlen(zDirname); ii>1 && zDirname[ii]!='/'; ii--); if( ii>0 ){ zDirname[ii] = '\0'; - fd = open(zDirname, O_RDONLY|O_BINARY, 0); + fd = robust_open(zDirname, O_RDONLY|O_BINARY, 0); if( fd>=0 ){ #ifdef FD_CLOEXEC - fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | FD_CLOEXEC); + osFcntl(fd, F_SETFD, osFcntl(fd, F_GETFD, 0) | FD_CLOEXEC); #endif OSTRACE(("OPENDIR %-3d %s\n", fd, zDirname)); } } *pFd = fd; - return (fd>=0?SQLITE_OK:SQLITE_CANTOPEN_BKPT); + return (fd>=0?SQLITE_OK:unixLogError(SQLITE_CANTOPEN_BKPT, "open", zDirname)); } /* @@ -27280,9 +29158,9 @@ static const char *unixTempFileDir(void){ if( !azDirs[1] ) azDirs[1] = getenv("TMPDIR"); for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); zDir=azDirs[i++]){ if( zDir==0 ) continue; - if( stat(zDir, &buf) ) continue; + if( osStat(zDir, &buf) ) continue; if( !S_ISDIR(buf.st_mode) ) continue; - if( access(zDir, 07) ) continue; + if( osAccess(zDir, 07) ) continue; break; } return zDir; @@ -27325,7 +29203,7 @@ static int unixGetTempname(int nBuf, char *zBuf){ zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; } zBuf[j] = 0; - }while( access(zBuf,0)==0 ); + }while( osAccess(zBuf,0)==0 ); return SQLITE_OK; } @@ -27413,6 +29291,11 @@ static UnixUnusedFd *findReusableFd(const char *zPath, int flags){ ** corresponding database file and sets *pMode to this value. Whenever ** possible, WAL and journal files are created using the same permissions ** as the associated database file. +** +** If the SQLITE_ENABLE_8_3_NAMES option is enabled, then the +** original filename is unavailable. But 8_3_NAMES is only used for +** FAT filesystems and permissions do not matter there, so just use +** the default permissions. */ static int findCreateFileMode( const char *zPath, /* Path of file (possibly) being created */ @@ -27420,6 +29303,7 @@ static int findCreateFileMode( mode_t *pMode /* OUT: Permissions to open file with */ ){ int rc = SQLITE_OK; /* Return Code */ + *pMode = SQLITE_DEFAULT_FILE_PERMISSIONS; if( flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL) ){ char zDb[MAX_PATHNAME+1]; /* Database file path */ int nDb; /* Number of valid bytes in zDb */ @@ -27431,15 +29315,15 @@ static int findCreateFileMode( ** ** "<path to db>-journal" ** "<path to db>-wal" - ** "<path to db>-journal-NNNN" - ** "<path to db>-wal-NNNN" + ** "<path to db>-journalNN" + ** "<path to db>-walNN" ** - ** where NNNN is a 4 digit decimal number. The NNNN naming schemes are + ** where NN is a 4 digit decimal number. The NN naming schemes are ** used by the test_multiplex.c module. */ nDb = sqlite3Strlen30(zPath) - 1; - while( nDb>0 && zPath[nDb]!='l' ) nDb--; - nDb -= ((flags & SQLITE_OPEN_WAL) ? 3 : 7); + while( nDb>0 && zPath[nDb]!='-' ) nDb--; + if( nDb==0 ) return SQLITE_OK; memcpy(zDb, zPath, nDb); zDb[nDb] = '\0'; @@ -27450,8 +29334,6 @@ static int findCreateFileMode( } }else if( flags & SQLITE_OPEN_DELETEONCLOSE ){ *pMode = 0600; - }else{ - *pMode = SQLITE_DEFAULT_FILE_PERMISSIONS; } return rc; } @@ -27586,7 +29468,7 @@ static int unixOpen( assert( eType==SQLITE_OPEN_WAL || eType==SQLITE_OPEN_MAIN_JOURNAL ); return rc; } - fd = open(zName, openFlags, openMode); + fd = robust_open(zName, openFlags, openMode); OSTRACE(("OPENX %-3d %s 0%o\n", fd, zName, openFlags)); if( fd<0 && errno!=EISDIR && isReadWrite && !isExclusive ){ /* Failed to open the file for read/write access. Try read-only. */ @@ -27594,10 +29476,11 @@ static int unixOpen( openFlags &= ~(O_RDWR|O_CREAT); flags |= SQLITE_OPEN_READONLY; openFlags |= O_RDONLY; - fd = open(zName, openFlags, openMode); + isReadonly = 1; + fd = robust_open(zName, openFlags, openMode); } if( fd<0 ){ - rc = SQLITE_CANTOPEN_BKPT; + rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zName); goto open_finished; } } @@ -27632,13 +29515,13 @@ static int unixOpen( ** it would not be safe to close as this would release any locks held ** on the file by this process. */ assert( eType!=SQLITE_OPEN_MAIN_DB ); - close(fd); /* silently leak if fail, already in error */ + robust_close(p, fd, __LINE__); goto open_finished; } } #ifdef FD_CLOEXEC - fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | FD_CLOEXEC); + osFcntl(fd, F_SETFD, osFcntl(fd, F_GETFD, 0) | FD_CLOEXEC); #endif noLock = eType!=SQLITE_OPEN_MAIN_DB; @@ -27648,8 +29531,8 @@ static int unixOpen( struct statfs fsInfo; if( fstatfs(fd, &fsInfo) == -1 ){ ((unixFile*)pFile)->lastErrno = errno; - if( dirfd>=0 ) close(dirfd); /* silently leak if fail, in error */ - close(fd); /* silently leak if fail, in error */ + if( dirfd>=0 ) robust_close(p, dirfd, __LINE__); + robust_close(p, fd, __LINE__); return SQLITE_IOERR_ACCESS; } if (0 == strncmp("msdos", fsInfo.f_fstypename, 5)) { @@ -27681,16 +29564,17 @@ static int unixOpen( ** the same file are working. */ p->lastErrno = errno; if( dirfd>=0 ){ - close(dirfd); /* silently leak if fail, in error */ + robust_close(p, dirfd, __LINE__); } - close(fd); /* silently leak if fail, in error */ + robust_close(p, fd, __LINE__); rc = SQLITE_IOERR_ACCESS; goto open_finished; } useProxy = !(fsInfo.f_flags&MNT_LOCAL); } if( useProxy ){ - rc = fillInUnixFile(pVfs, fd, dirfd, pFile, zPath, noLock, isDelete); + rc = fillInUnixFile(pVfs, fd, dirfd, pFile, zPath, noLock, + isDelete, isReadonly); if( rc==SQLITE_OK ){ rc = proxyTransformUnixFile((unixFile*)pFile, ":auto:"); if( rc!=SQLITE_OK ){ @@ -27707,7 +29591,8 @@ static int unixOpen( } #endif - rc = fillInUnixFile(pVfs, fd, dirfd, pFile, zPath, noLock, isDelete); + rc = fillInUnixFile(pVfs, fd, dirfd, pFile, zPath, noLock, + isDelete, isReadonly); open_finished: if( rc!=SQLITE_OK ){ sqlite3_free(p->pUnused); @@ -27729,7 +29614,7 @@ static int unixDelete( UNUSED_PARAMETER(NotUsed); SimulateIOError(return SQLITE_IOERR_DELETE); if( unlink(zPath)==(-1) && errno!=ENOENT ){ - return SQLITE_IOERR_DELETE; + return unixLogError(SQLITE_IOERR_DELETE, "unlink", zPath); } #ifndef SQLITE_DISABLE_DIRSYNC if( dirSync ){ @@ -27742,11 +29627,9 @@ static int unixDelete( if( fsync(fd) ) #endif { - rc = SQLITE_IOERR_DIR_FSYNC; - } - if( close(fd)&&!rc ){ - rc = SQLITE_IOERR_DIR_CLOSE; + rc = unixLogError(SQLITE_IOERR_DIR_FSYNC, "fsync", zPath); } + robust_close(0, fd, __LINE__); } } #endif @@ -27786,7 +29669,7 @@ static int unixAccess( default: assert(!"Invalid flags argument"); } - *pResOut = (access(zPath, amode)==0); + *pResOut = (osAccess(zPath, amode)==0); if( flags==SQLITE_ACCESS_EXISTS && *pResOut ){ struct stat buf; if( 0==stat(zPath, &buf) && buf.st_size==0 ){ @@ -27828,8 +29711,8 @@ static int unixFullPathname( sqlite3_snprintf(nOut, zOut, "%s", zPath); }else{ int nCwd; - if( getcwd(zOut, nOut-1)==0 ){ - return SQLITE_CANTOPEN_BKPT; + if( osGetcwd(zOut, nOut-1)==0 ){ + return unixLogError(SQLITE_CANTOPEN_BKPT, "getcwd", zPath); } nCwd = (int)strlen(zOut); sqlite3_snprintf(nOut-nCwd, &zOut[nCwd], "/%s", zPath); @@ -27923,7 +29806,7 @@ static int unixRandomness(sqlite3_vfs *NotUsed, int nBuf, char *zBuf){ #if !defined(SQLITE_TEST) { int pid, fd; - fd = open("/dev/urandom", O_RDONLY); + fd = robust_open("/dev/urandom", O_RDONLY, 0); if( fd<0 ){ time_t t; time(&t); @@ -27933,8 +29816,8 @@ static int unixRandomness(sqlite3_vfs *NotUsed, int nBuf, char *zBuf){ assert( sizeof(t)+sizeof(pid)<=(size_t)nBuf ); nBuf = sizeof(t) + sizeof(pid); }else{ - nBuf = read(fd, zBuf, nBuf); - close(fd); + do{ nBuf = osRead(fd, zBuf, nBuf); }while( nBuf<0 && errno==EINTR ); + robust_close(0, fd, __LINE__); } } #endif @@ -28332,17 +30215,17 @@ static int proxyCreateUnixFile( } } if( fd<0 ){ - fd = open(path, openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS); + fd = robust_open(path, openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS); terrno = errno; if( fd<0 && errno==ENOENT && islockfile ){ if( proxyCreateLockPath(path) == SQLITE_OK ){ - fd = open(path, openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS); + fd = robust_open(path, openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS); } } } if( fd<0 ){ openFlags = O_RDONLY; - fd = open(path, openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS); + fd = robust_open(path, openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS); terrno = errno; } if( fd<0 ){ @@ -28366,18 +30249,20 @@ static int proxyCreateUnixFile( } memset(pNew, 0, sizeof(unixFile)); pNew->openFlags = openFlags; + memset(&dummyVfs, 0, sizeof(dummyVfs)); dummyVfs.pAppData = (void*)&autolockIoFinder; + dummyVfs.zName = "dummy"; pUnused->fd = fd; pUnused->flags = openFlags; pNew->pUnused = pUnused; - rc = fillInUnixFile(&dummyVfs, fd, dirfd, (sqlite3_file*)pNew, path, 0, 0); + rc = fillInUnixFile(&dummyVfs, fd, dirfd, (sqlite3_file*)pNew, path, 0, 0, 0); if( rc==SQLITE_OK ){ *ppFile = pNew; return SQLITE_OK; } end_create_proxy: - close(fd); /* silently leak fd if error, we're already in error */ + robust_close(pNew, fd, __LINE__); sqlite3_free(pNew); sqlite3_free(pUnused); return rc; @@ -28397,18 +30282,19 @@ extern int gethostuuid(uuid_t id, const struct timespec *wait); ** bytes of writable memory. */ static int proxyGetHostID(unsigned char *pHostID, int *pError){ - struct timespec timeout = {1, 0}; /* 1 sec timeout */ - assert(PROXY_HOSTIDLEN == sizeof(uuid_t)); memset(pHostID, 0, PROXY_HOSTIDLEN); #if defined(__MAX_OS_X_VERSION_MIN_REQUIRED)\ && __MAC_OS_X_VERSION_MIN_REQUIRED<1050 - if( gethostuuid(pHostID, &timeout) ){ - int err = errno; - if( pError ){ - *pError = err; + { + static const struct timespec timeout = {1, 0}; /* 1 sec timeout */ + if( gethostuuid(pHostID, &timeout) ){ + int err = errno; + if( pError ){ + *pError = err; + } + return SQLITE_IOERR; } - return SQLITE_IOERR; } #endif #ifdef SQLITE_TEST @@ -28455,18 +30341,19 @@ static int proxyBreakConchLock(unixFile *pFile, uuid_t myHostID){ goto end_breaklock; } /* read the conch content */ - readLen = pread(conchFile->h, buf, PROXY_MAXCONCHLEN, 0); + readLen = osPread(conchFile->h, buf, PROXY_MAXCONCHLEN, 0); if( readLen<PROXY_PATHINDEX ){ sqlite3_snprintf(sizeof(errmsg),errmsg,"read error (len %d)",(int)readLen); goto end_breaklock; } /* write it out to the temporary break file */ - fd = open(tPath, (O_RDWR|O_CREAT|O_EXCL), SQLITE_DEFAULT_FILE_PERMISSIONS); + fd = robust_open(tPath, (O_RDWR|O_CREAT|O_EXCL), + SQLITE_DEFAULT_FILE_PERMISSIONS); if( fd<0 ){ sqlite3_snprintf(sizeof(errmsg), errmsg, "create failed (%d)", errno); goto end_breaklock; } - if( pwrite(fd, buf, readLen, 0) != (ssize_t)readLen ){ + if( osPwrite(fd, buf, readLen, 0) != (ssize_t)readLen ){ sqlite3_snprintf(sizeof(errmsg), errmsg, "write failed (%d)", errno); goto end_breaklock; } @@ -28476,7 +30363,7 @@ static int proxyBreakConchLock(unixFile *pFile, uuid_t myHostID){ } rc = 0; fprintf(stderr, "broke stale lock on %s\n", cPath); - close(conchFile->h); + robust_close(pFile, conchFile->h, __LINE__); conchFile->h = fd; conchFile->openFlags = O_RDWR | O_CREAT; @@ -28484,7 +30371,7 @@ end_breaklock: if( rc ){ if( fd>=0 ){ unlink(tPath); - close(fd); + robust_close(pFile, fd, __LINE__); } fprintf(stderr, "failed to break stale lock on %s, %s\n", cPath, errmsg); } @@ -28512,7 +30399,7 @@ static int proxyConchLock(unixFile *pFile, uuid_t myHostID, int lockType){ * 3rd try: break the lock unless the mod time has changed. */ struct stat buf; - if( fstat(conchFile->h, &buf) ){ + if( osFstat(conchFile->h, &buf) ){ pFile->lastErrno = errno; return SQLITE_IOERR_LOCK; } @@ -28531,7 +30418,7 @@ static int proxyConchLock(unixFile *pFile, uuid_t myHostID, int lockType){ if( nTries==2 ){ char tBuf[PROXY_MAXCONCHLEN]; - int len = pread(conchFile->h, tBuf, PROXY_MAXCONCHLEN, 0); + int len = osPread(conchFile->h, tBuf, PROXY_MAXCONCHLEN, 0); if( len<0 ){ pFile->lastErrno = errno; return SQLITE_IOERR_LOCK; @@ -28693,7 +30580,7 @@ static int proxyTakeConch(unixFile *pFile){ strlcpy(&writeBuffer[PROXY_PATHINDEX], tempLockPath, MAXPATHLEN); } writeSize = PROXY_PATHINDEX + strlen(&writeBuffer[PROXY_PATHINDEX]); - ftruncate(conchFile->h, writeSize); + robust_ftruncate(conchFile->h, writeSize); rc = unixWrite((sqlite3_file *)conchFile, writeBuffer, writeSize, 0); fsync(conchFile->h); /* If we created a new conch file (not just updated the contents of a @@ -28701,15 +30588,18 @@ static int proxyTakeConch(unixFile *pFile){ */ if( rc==SQLITE_OK && createConch ){ struct stat buf; - int err = fstat(pFile->h, &buf); + int err = osFstat(pFile->h, &buf); if( err==0 ){ mode_t cmode = buf.st_mode&(S_IRUSR|S_IWUSR | S_IRGRP|S_IWGRP | S_IROTH|S_IWOTH); /* try to match the database file R/W permissions, ignore failure */ #ifndef SQLITE_PROXY_DEBUG - fchmod(conchFile->h, cmode); + osFchmod(conchFile->h, cmode); #else - if( fchmod(conchFile->h, cmode)!=0 ){ + do{ + rc = osFchmod(conchFile->h, cmode); + }while( rc==(-1) && errno==EINTR ); + if( rc!=0 ){ int code = errno; fprintf(stderr, "fchmod %o FAILED with %d %s\n", cmode, code, strerror(code)); @@ -28730,17 +30620,10 @@ static int proxyTakeConch(unixFile *pFile){ OSTRACE(("TRANSPROXY: CLOSE %d\n", pFile->h)); if( rc==SQLITE_OK && pFile->openFlags ){ if( pFile->h>=0 ){ -#ifdef STRICT_CLOSE_ERROR - if( close(pFile->h) ){ - pFile->lastErrno = errno; - return SQLITE_IOERR_CLOSE; - } -#else - close(pFile->h); /* silently leak fd if fail */ -#endif + robust_close(pFile, pFile->h, __LINE__); } pFile->h = -1; - int fd = open(pCtx->dbPath, pFile->openFlags, + int fd = robust_open(pCtx->dbPath, pFile->openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS); OSTRACE(("TRANSPROXY: OPEN %d\n", fd)); if( fd>=0 ){ @@ -28966,7 +30849,7 @@ static int proxyTransformUnixFile(unixFile *pFile, const char *path) { struct stat conchInfo; int goLockless = 0; - if( stat(pCtx->conchFilePath, &conchInfo) == -1 ) { + if( osStat(pCtx->conchFilePath, &conchInfo) == -1 ) { int err = errno; if( (err==ENOENT) && (statfs(dbPath, &fsInfo) != -1) ){ goLockless = (fsInfo.f_flags&MNT_RDONLY) == MNT_RDONLY; @@ -29251,7 +31134,7 @@ SQLITE_API int sqlite3_os_init(void){ ** that filesystem time. */ #define UNIXVFS(VFSNAME, FINDER) { \ - 2, /* iVersion */ \ + 3, /* iVersion */ \ sizeof(unixFile), /* szOsFile */ \ MAX_PATHNAME, /* mxPathname */ \ 0, /* pNext */ \ @@ -29270,6 +31153,9 @@ SQLITE_API int sqlite3_os_init(void){ unixCurrentTime, /* xCurrentTime */ \ unixGetLastError, /* xGetLastError */ \ unixCurrentTimeInt64, /* xCurrentTimeInt64 */ \ + unixSetSystemCall, /* xSetSystemCall */ \ + unixGetSystemCall, /* xGetSystemCall */ \ + unixNextSystemCall, /* xNextSystemCall */ \ } /* @@ -29287,6 +31173,7 @@ SQLITE_API int sqlite3_os_init(void){ #endif UNIXVFS("unix-none", nolockIoFinder ), UNIXVFS("unix-dotfile", dotlockIoFinder ), + UNIXVFS("unix-excl", posixIoFinder ), #if OS_VXWORKS UNIXVFS("unix-namedsem", semIoFinder ), #endif @@ -29304,6 +31191,10 @@ SQLITE_API int sqlite3_os_init(void){ }; unsigned int i; /* Loop counter */ + /* Double-check that the aSyscall[] array has been constructed + ** correctly. See ticket [bb3a86e890c8e96ab] */ + assert( ArraySize(aSyscall)==16 ); + /* Register all VFSes defined in the aVfs[] array */ for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){ sqlite3_vfs_register(&aVfs[i], i==0); @@ -29650,6 +31541,7 @@ struct winFile { #endif }; + /* ** Forward prototypes. */ @@ -29817,7 +31709,7 @@ SQLITE_API char *sqlite3_win32_mbcs_to_utf8(const char *zFilename){ ** Convert UTF-8 to multibyte character string. Space to hold the ** returned string is obtained from malloc(). */ -static char *utf8ToMbcs(const char *zFilename){ +SQLITE_API char *sqlite3_win32_utf8_to_mbcs(const char *zFilename){ char *zFilenameMbcs; WCHAR *zTmpWide; @@ -29830,6 +31722,109 @@ static char *utf8ToMbcs(const char *zFilename){ return zFilenameMbcs; } + +/* +** The return value of getLastErrorMsg +** is zero if the error message fits in the buffer, or non-zero +** otherwise (if the message was truncated). +*/ +static int getLastErrorMsg(int nBuf, char *zBuf){ + /* FormatMessage returns 0 on failure. Otherwise it + ** returns the number of TCHARs written to the output + ** buffer, excluding the terminating null char. + */ + DWORD error = GetLastError(); + DWORD dwLen = 0; + char *zOut = 0; + + if( isNT() ){ + WCHAR *zTempWide = NULL; + dwLen = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + error, + 0, + (LPWSTR) &zTempWide, + 0, + 0); + if( dwLen > 0 ){ + /* allocate a buffer and convert to UTF8 */ + zOut = unicodeToUtf8(zTempWide); + /* free the system buffer allocated by FormatMessage */ + LocalFree(zTempWide); + } +/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed. +** Since the ASCII version of these Windows API do not exist for WINCE, +** it's important to not reference them for WINCE builds. +*/ +#if SQLITE_OS_WINCE==0 + }else{ + char *zTemp = NULL; + dwLen = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + error, + 0, + (LPSTR) &zTemp, + 0, + 0); + if( dwLen > 0 ){ + /* allocate a buffer and convert to UTF8 */ + zOut = sqlite3_win32_mbcs_to_utf8(zTemp); + /* free the system buffer allocated by FormatMessage */ + LocalFree(zTemp); + } +#endif + } + if( 0 == dwLen ){ + sqlite3_snprintf(nBuf, zBuf, "OsError 0x%x (%u)", error, error); + }else{ + /* copy a maximum of nBuf chars to output buffer */ + sqlite3_snprintf(nBuf, zBuf, "%s", zOut); + /* free the UTF8 buffer */ + free(zOut); + } + return 0; +} + +/* +** +** This function - winLogErrorAtLine() - is only ever called via the macro +** winLogError(). +** +** This routine is invoked after an error occurs in an OS function. +** It logs a message using sqlite3_log() containing the current value of +** error code and, if possible, the human-readable equivalent from +** FormatMessage. +** +** The first argument passed to the macro should be the error code that +** will be returned to SQLite (e.g. SQLITE_IOERR_DELETE, SQLITE_CANTOPEN). +** The two subsequent arguments should be the name of the OS function that +** failed and the the associated file-system path, if any. +*/ +#define winLogError(a,b,c) winLogErrorAtLine(a,b,c,__LINE__) +static int winLogErrorAtLine( + int errcode, /* SQLite error code */ + const char *zFunc, /* Name of OS function that failed */ + const char *zPath, /* File path associated with error */ + int iLine /* Source line number where error occurred */ +){ + char zMsg[500]; /* Human readable error text */ + int i; /* Loop counter */ + DWORD iErrno = GetLastError(); /* Error code */ + + zMsg[0] = 0; + getLastErrorMsg(sizeof(zMsg), zMsg); + assert( errcode!=SQLITE_OK ); + if( zPath==0 ) zPath = ""; + for(i=0; zMsg[i] && zMsg[i]!='\r' && zMsg[i]!='\n'; i++){} + zMsg[i] = 0; + sqlite3_log(errcode, + "os_win.c:%d: (%d) %s(%s) - %s", + iLine, iErrno, zFunc, zPath, zMsg + ); + + return errcode; +} + #if SQLITE_OS_WINCE /************************************************************************* ** This section contains code for WinCE only. @@ -29906,6 +31901,7 @@ static BOOL winceCreateLock(const char *zFilename, winFile *pFile){ pFile->hMutex = CreateMutexW(NULL, FALSE, zName); if (!pFile->hMutex){ pFile->lastErrno = GetLastError(); + winLogError(SQLITE_ERROR, "winceCreateLock1", zFilename); free(zName); return FALSE; } @@ -29937,6 +31933,7 @@ static BOOL winceCreateLock(const char *zFilename, winFile *pFile){ /* If mapping failed, close the shared memory handle and erase it */ if (!pFile->shared){ pFile->lastErrno = GetLastError(); + winLogError(SQLITE_ERROR, "winceCreateLock2", zFilename); CloseHandle(pFile->hShared); pFile->hShared = NULL; } @@ -30182,6 +32179,7 @@ static int seekWinFile(winFile *pFile, sqlite3_int64 iOffset){ dwRet = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN); if( (dwRet==INVALID_SET_FILE_POINTER && GetLastError()!=NO_ERROR) ){ pFile->lastErrno = GetLastError(); + winLogError(SQLITE_IOERR_SEEK, "seekWinFile", pFile->zPath); return 1; } @@ -30227,7 +32225,8 @@ static int winClose(sqlite3_file *id){ #endif OSTRACE(("CLOSE %d %s\n", pFile->h, rc ? "ok" : "failed")); OpenCounter(-1); - return rc ? SQLITE_OK : SQLITE_IOERR; + return rc ? SQLITE_OK + : winLogError(SQLITE_IOERR_CLOSE, "winClose", pFile->zPath); } /* @@ -30253,7 +32252,7 @@ static int winRead( } if( !ReadFile(pFile->h, pBuf, amt, &nRead, 0) ){ pFile->lastErrno = GetLastError(); - return SQLITE_IOERR_READ; + return winLogError(SQLITE_IOERR_READ, "winRead", pFile->zPath); } if( nRead<(DWORD)amt ){ /* Unread parts of the buffer must be zero-filled */ @@ -30301,10 +32300,11 @@ static int winWrite( } if( rc ){ - if( pFile->lastErrno==ERROR_HANDLE_DISK_FULL ){ + if( ( pFile->lastErrno==ERROR_HANDLE_DISK_FULL ) + || ( pFile->lastErrno==ERROR_DISK_FULL )){ return SQLITE_FULL; } - return SQLITE_IOERR_WRITE; + return winLogError(SQLITE_IOERR_WRITE, "winWrite", pFile->zPath); } return SQLITE_OK; } @@ -30332,10 +32332,10 @@ static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){ /* SetEndOfFile() returns non-zero when successful, or zero when it fails. */ if( seekWinFile(pFile, nByte) ){ - rc = SQLITE_IOERR_TRUNCATE; + rc = winLogError(SQLITE_IOERR_TRUNCATE, "winTruncate1", pFile->zPath); }else if( 0==SetEndOfFile(pFile->h) ){ pFile->lastErrno = GetLastError(); - rc = SQLITE_IOERR_TRUNCATE; + rc = winLogError(SQLITE_IOERR_TRUNCATE, "winTruncate2", pFile->zPath); } OSTRACE(("TRUNCATE %d %lld %s\n", pFile->h, nByte, rc ? "failed" : "ok")); @@ -30357,6 +32357,7 @@ SQLITE_API int sqlite3_fullsync_count = 0; static int winSync(sqlite3_file *id, int flags){ #if !defined(NDEBUG) || !defined(SQLITE_NO_SYNC) || defined(SQLITE_DEBUG) winFile *pFile = (winFile*)id; + BOOL rc; #else UNUSED_PARAMETER(id); #endif @@ -30369,32 +32370,33 @@ static int winSync(sqlite3_file *id, int flags){ OSTRACE(("SYNC %d lock=%d\n", pFile->h, pFile->locktype)); + /* Unix cannot, but some systems may return SQLITE_FULL from here. This + ** line is to test that doing so does not cause any problems. + */ + SimulateDiskfullError( return SQLITE_FULL ); + #ifndef SQLITE_TEST UNUSED_PARAMETER(flags); #else - if( flags & SQLITE_SYNC_FULL ){ + if( (flags&0x0F)==SQLITE_SYNC_FULL ){ sqlite3_fullsync_count++; } sqlite3_sync_count++; #endif - /* Unix cannot, but some systems may return SQLITE_FULL from here. This - ** line is to test that doing so does not cause any problems. - */ - SimulateDiskfullError( return SQLITE_FULL ); - SimulateIOError( return SQLITE_IOERR; ); - /* If we compiled with the SQLITE_NO_SYNC flag, then syncing is a ** no-op */ #ifdef SQLITE_NO_SYNC return SQLITE_OK; #else - if( FlushFileBuffers(pFile->h) ){ + rc = FlushFileBuffers(pFile->h); + SimulateIOError( rc=FALSE ); + if( rc ){ return SQLITE_OK; }else{ pFile->lastErrno = GetLastError(); - return SQLITE_IOERR; + return winLogError(SQLITE_IOERR_FSYNC, "winSync", pFile->zPath); } #endif } @@ -30415,7 +32417,7 @@ static int winFileSize(sqlite3_file *id, sqlite3_int64 *pSize){ && ((error = GetLastError()) != NO_ERROR) ) { pFile->lastErrno = error; - return SQLITE_IOERR_FSTAT; + return winLogError(SQLITE_IOERR_FSTAT, "winFileSize", pFile->zPath); } *pSize = (((sqlite3_int64)upperBits)<<32) + lowerBits; return SQLITE_OK; @@ -30454,6 +32456,7 @@ static int getReadLock(winFile *pFile){ } if( res == 0 ){ pFile->lastErrno = GetLastError(); + /* No need to log a failure to lock */ } return res; } @@ -30472,8 +32475,9 @@ static int unlockReadLock(winFile *pFile){ res = UnlockFile(pFile->h, SHARED_FIRST + pFile->sharedLockByte, 0, 1, 0); #endif } - if( res == 0 ){ + if( res==0 && GetLastError()!=ERROR_NOT_LOCKED ){ pFile->lastErrno = GetLastError(); + winLogError(SQLITE_IOERR_UNLOCK, "unlockReadLock", pFile->zPath); } return res; } @@ -30674,7 +32678,7 @@ static int winUnlock(sqlite3_file *id, int locktype){ if( locktype==SHARED_LOCK && !getReadLock(pFile) ){ /* This should never happen. We should always be able to ** reacquire the read lock */ - rc = SQLITE_IOERR_UNLOCK; + rc = winLogError(SQLITE_IOERR_UNLOCK, "winUnlock", pFile->zPath); } } if( type>=RESERVED_LOCK ){ @@ -30714,8 +32718,11 @@ static int winFileControl(sqlite3_file *id, int op, void *pArg){ SimulateIOErrorBenign(0); return SQLITE_OK; } + case SQLITE_FCNTL_SYNC_OMITTED: { + return SQLITE_OK; + } } - return SQLITE_ERROR; + return SQLITE_NOTFOUND; } /* @@ -30986,6 +32993,7 @@ static int winOpenSharedMemory(winFile *pDbFd){ memset(pNew, 0, sizeof(*pNew)); pNew->zFilename = (char*)&pNew[1]; sqlite3_snprintf(nName+15, pNew->zFilename, "%s-shm", pDbFd->zPath); + sqlite3FileSuffix3(pDbFd->zPath, pNew->zFilename); /* Look to see if there is an existing winShmNode that can be used. ** If no matching winShmNode currently exists, create a new one. @@ -31028,7 +33036,7 @@ static int winOpenSharedMemory(winFile *pDbFd){ if( winShmSystemLock(pShmNode, _SHM_WRLCK, WIN_SHM_DMS, 1)==SQLITE_OK ){ rc = winTruncate((sqlite3_file *)&pShmNode->hFile, 0); if( rc!=SQLITE_OK ){ - rc = SQLITE_IOERR_SHMOPEN; + rc = winLogError(SQLITE_IOERR_SHMOPEN, "winOpenShm", pDbFd->zPath); } } if( rc==SQLITE_OK ){ @@ -31287,7 +33295,7 @@ static int winShmMap( */ rc = winFileSize((sqlite3_file *)&pShmNode->hFile, &sz); if( rc!=SQLITE_OK ){ - rc = SQLITE_IOERR_SHMSIZE; + rc = winLogError(SQLITE_IOERR_SHMSIZE, "winShmMap1", pDbFd->zPath); goto shmpage_out; } @@ -31301,7 +33309,7 @@ static int winShmMap( if( !isWrite ) goto shmpage_out; rc = winTruncate((sqlite3_file *)&pShmNode->hFile, nByte); if( rc!=SQLITE_OK ){ - rc = SQLITE_IOERR_SHMSIZE; + rc = winLogError(SQLITE_IOERR_SHMSIZE, "winShmMap2", pDbFd->zPath); goto shmpage_out; } } @@ -31338,7 +33346,7 @@ static int winShmMap( } if( !pMap ){ pShmNode->lastErrno = GetLastError(); - rc = SQLITE_IOERR; + rc = winLogError(SQLITE_IOERR_SHMMAP, "winShmMap3", pDbFd->zPath); if( hMap ) CloseHandle(hMap); goto shmpage_out; } @@ -31420,7 +33428,7 @@ static void *convertUtf8Filename(const char *zFilename){ */ #if SQLITE_OS_WINCE==0 }else{ - zConverted = utf8ToMbcs(zFilename); + zConverted = sqlite3_win32_utf8_to_mbcs(zFilename); #endif } /* caller will handle out of memory */ @@ -31501,68 +33509,6 @@ static int getTempname(int nBuf, char *zBuf){ } /* -** The return value of getLastErrorMsg -** is zero if the error message fits in the buffer, or non-zero -** otherwise (if the message was truncated). -*/ -static int getLastErrorMsg(int nBuf, char *zBuf){ - /* FormatMessage returns 0 on failure. Otherwise it - ** returns the number of TCHARs written to the output - ** buffer, excluding the terminating null char. - */ - DWORD error = GetLastError(); - DWORD dwLen = 0; - char *zOut = 0; - - if( isNT() ){ - WCHAR *zTempWide = NULL; - dwLen = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, - error, - 0, - (LPWSTR) &zTempWide, - 0, - 0); - if( dwLen > 0 ){ - /* allocate a buffer and convert to UTF8 */ - zOut = unicodeToUtf8(zTempWide); - /* free the system buffer allocated by FormatMessage */ - LocalFree(zTempWide); - } -/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed. -** Since the ASCII version of these Windows API do not exist for WINCE, -** it's important to not reference them for WINCE builds. -*/ -#if SQLITE_OS_WINCE==0 - }else{ - char *zTemp = NULL; - dwLen = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, - error, - 0, - (LPSTR) &zTemp, - 0, - 0); - if( dwLen > 0 ){ - /* allocate a buffer and convert to UTF8 */ - zOut = sqlite3_win32_mbcs_to_utf8(zTemp); - /* free the system buffer allocated by FormatMessage */ - LocalFree(zTemp); - } -#endif - } - if( 0 == dwLen ){ - sqlite3_snprintf(nBuf, zBuf, "OsError 0x%x (%u)", error, error); - }else{ - /* copy a maximum of nBuf chars to output buffer */ - sqlite3_snprintf(nBuf, zBuf, "%s", zOut); - /* free the UTF8 buffer */ - free(zOut); - } - return 0; -} - -/* ** Open a file. */ static int winOpen( @@ -31733,6 +33679,7 @@ static int winOpen( if( h==INVALID_HANDLE_VALUE ){ pFile->lastErrno = GetLastError(); + winLogError(SQLITE_CANTOPEN, "winOpen", zUtf8Name); free(zConverted); if( isReadWrite ){ return winOpen(pVfs, zName, id, @@ -31836,7 +33783,8 @@ static int winDelete( "ok" : "failed" )); return ( (rc == INVALID_FILE_ATTRIBUTES) - && (error == ERROR_FILE_NOT_FOUND)) ? SQLITE_OK : SQLITE_IOERR_DELETE; + && (error == ERROR_FILE_NOT_FOUND)) ? SQLITE_OK : + winLogError(SQLITE_IOERR_DELETE, "winDelete", zFilename); } /* @@ -31876,6 +33824,7 @@ static int winAccess( } }else{ if( GetLastError()!=ERROR_FILE_NOT_FOUND ){ + winLogError(SQLITE_IOERR_ACCESS, "winAccess", zFilename); free(zConverted); return SQLITE_IOERR_ACCESS; }else{ @@ -31940,6 +33889,13 @@ static int winFullPathname( void *zConverted; char *zOut; + /* If this path name begins with "/X:", where "X" is any alphabetic + ** character, discard the initial "/" from the pathname. + */ + if( zRelative[0]=='/' && sqlite3Isalpha(zRelative[1]) && zRelative[2]==':' ){ + zRelative++; + } + /* It's odd to simulate an io-error here, but really this is just ** using the io-error infrastructure to test that SQLite handles this ** function failing. This function could fail if, for example, the @@ -32274,7 +34230,7 @@ static int winGetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ */ SQLITE_API int sqlite3_os_init(void){ static sqlite3_vfs winVfs = { - 2, /* iVersion */ + 3, /* iVersion */ sizeof(winFile), /* szOsFile */ MAX_PATH, /* mxPathname */ 0, /* pNext */ @@ -32293,6 +34249,9 @@ SQLITE_API int sqlite3_os_init(void){ winCurrentTime, /* xCurrentTime */ winGetLastError, /* xGetLastError */ winCurrentTimeInt64, /* xCurrentTimeInt64 */ + 0, /* xSetSystemCall */ + 0, /* xGetSystemCall */ + 0, /* xNextSystemCall */ }; #ifndef SQLITE_OMIT_WAL @@ -32977,6 +34936,13 @@ SQLITE_PRIVATE int sqlite3PcacheFetch( } if( pPg ){ int rc; +#ifdef SQLITE_LOG_CACHE_SPILL + sqlite3_log(SQLITE_FULL, + "spill page %d making room for %d - cache used: %d/%d", + pPg->pgno, pgno, + sqlite3GlobalConfig.pcache.xPagecount(pCache->pCache), + pCache->nMax); +#endif rc = pCache->xStress(pCache->pStress, pPg); if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ){ return rc; @@ -33335,6 +35301,38 @@ SQLITE_PRIVATE void sqlite3PcacheIterateDirty(PCache *pCache, void (*xIter)(PgHd typedef struct PCache1 PCache1; typedef struct PgHdr1 PgHdr1; typedef struct PgFreeslot PgFreeslot; +typedef struct PGroup PGroup; + +/* Each page cache (or PCache) belongs to a PGroup. A PGroup is a set +** of one or more PCaches that are able to recycle each others unpinned +** pages when they are under memory pressure. A PGroup is an instance of +** the following object. +** +** This page cache implementation works in one of two modes: +** +** (1) Every PCache is the sole member of its own PGroup. There is +** one PGroup per PCache. +** +** (2) There is a single global PGroup that all PCaches are a member +** of. +** +** Mode 1 uses more memory (since PCache instances are not able to rob +** unused pages from other PCaches) but it also operates without a mutex, +** and is therefore often faster. Mode 2 requires a mutex in order to be +** threadsafe, but is able recycle pages more efficient. +** +** For mode (1), PGroup.mutex is NULL. For mode (2) there is only a single +** PGroup which is the pcache1.grp global variable and its mutex is +** SQLITE_MUTEX_STATIC_LRU. +*/ +struct PGroup { + sqlite3_mutex *mutex; /* MUTEX_STATIC_LRU or NULL */ + int nMaxPage; /* Sum of nMax for purgeable caches */ + int nMinPage; /* Sum of nMin for purgeable caches */ + int mxPinned; /* nMaxpage + 10 - nMinPage */ + int nCurrentPage; /* Number of purgeable pages allocated */ + PgHdr1 *pLruHead, *pLruTail; /* LRU list of unpinned pages */ +}; /* Each page cache is an instance of the following object. Every ** open database file (including each in-memory database and each @@ -33348,16 +35346,17 @@ struct PCache1 { /* Cache configuration parameters. Page size (szPage) and the purgeable ** flag (bPurgeable) are set when the cache is created. nMax may be ** modified at any time by a call to the pcache1CacheSize() method. - ** The global mutex must be held when accessing nMax. + ** The PGroup mutex must be held when accessing nMax. */ + PGroup *pGroup; /* PGroup this cache belongs to */ int szPage; /* Size of allocated pages in bytes */ int bPurgeable; /* True if cache is purgeable */ unsigned int nMin; /* Minimum number of pages reserved */ unsigned int nMax; /* Configured "cache_size" value */ + unsigned int n90pct; /* nMax*9/10 */ /* Hash table of all pages. The following variables may only be accessed - ** when the accessor is holding the global mutex (see pcache1EnterMutex() - ** and pcache1LeaveMutex()). + ** when the accessor is holding the PGroup mutex. */ unsigned int nRecyclable; /* Number of pages in the LRU list */ unsigned int nPage; /* Total number of pages in apHash */ @@ -33393,21 +35392,27 @@ struct PgFreeslot { ** Global data used by this cache. */ static SQLITE_WSD struct PCacheGlobal { - sqlite3_mutex *mutex; /* static mutex MUTEX_STATIC_LRU */ - - int nMaxPage; /* Sum of nMaxPage for purgeable caches */ - int nMinPage; /* Sum of nMinPage for purgeable caches */ - int nCurrentPage; /* Number of purgeable pages allocated */ - PgHdr1 *pLruHead, *pLruTail; /* LRU list of unpinned pages */ - - /* Variables related to SQLITE_CONFIG_PAGECACHE settings. */ - int szSlot; /* Size of each free slot */ - int nSlot; /* The number of pcache slots */ - int nFreeSlot; /* Number of unused pcache slots */ - int nReserve; /* Try to keep nFreeSlot above this */ - void *pStart, *pEnd; /* Bounds of pagecache malloc range */ - PgFreeslot *pFree; /* Free page blocks */ - int isInit; /* True if initialized */ + PGroup grp; /* The global PGroup for mode (2) */ + + /* Variables related to SQLITE_CONFIG_PAGECACHE settings. The + ** szSlot, nSlot, pStart, pEnd, nReserve, and isInit values are all + ** fixed at sqlite3_initialize() time and do not require mutex protection. + ** The nFreeSlot and pFree values do require mutex protection. + */ + int isInit; /* True if initialized */ + int szSlot; /* Size of each free slot */ + int nSlot; /* The number of pcache slots */ + int nReserve; /* Try to keep nFreeSlot above this */ + void *pStart, *pEnd; /* Bounds of pagecache malloc range */ + /* Above requires no mutex. Use mutex below for variable that follow. */ + sqlite3_mutex *mutex; /* Mutex for accessing the following: */ + int nFreeSlot; /* Number of unused pcache slots */ + PgFreeslot *pFree; /* Free page blocks */ + /* The following value requires a mutex to change. We skip the mutex on + ** reading because (1) most platforms read a 32-bit integer atomically and + ** (2) even if an incorrect value is read, no great harm is done since this + ** is really just an optimization. */ + int bUnderPressure; /* True if low on PAGECACHE memory */ } pcache1_g; /* @@ -33433,10 +35438,10 @@ static SQLITE_WSD struct PCacheGlobal { #define PAGE_TO_PGHDR1(c, p) (PgHdr1*)(((char*)p) + c->szPage) /* -** Macros to enter and leave the global LRU mutex. +** Macros to enter and leave the PCache LRU mutex. */ -#define pcache1EnterMutex() sqlite3_mutex_enter(pcache1.mutex) -#define pcache1LeaveMutex() sqlite3_mutex_leave(pcache1.mutex) +#define pcache1EnterMutex(X) sqlite3_mutex_enter((X)->mutex) +#define pcache1LeaveMutex(X) sqlite3_mutex_leave((X)->mutex) /******************************************************************************/ /******** Page Allocation/SQLITE_CONFIG_PCACHE Related Functions **************/ @@ -33446,6 +35451,9 @@ static SQLITE_WSD struct PCacheGlobal { ** supplied to use for the page-cache by passing the SQLITE_CONFIG_PAGECACHE ** verb to sqlite3_config(). Parameter pBuf points to an allocation large ** enough to contain 'n' buffers of 'sz' bytes each. +** +** This routine is called from sqlite3_initialize() and so it is guaranteed +** to be serialized already. There is no need for further mutexing. */ SQLITE_PRIVATE void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){ if( pcache1.isInit ){ @@ -33456,6 +35464,7 @@ SQLITE_PRIVATE void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){ pcache1.nReserve = n>90 ? 10 : (n/10 + 1); pcache1.pStart = pBuf; pcache1.pFree = 0; + pcache1.bUnderPressure = 0; while( n-- ){ p = (PgFreeslot*)pBuf; p->pNext = pcache1.pFree; @@ -33471,32 +35480,36 @@ SQLITE_PRIVATE void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){ ** configured using sqlite3_config(SQLITE_CONFIG_PAGECACHE) option. If no ** such buffer exists or there is no space left in it, this function falls ** back to sqlite3Malloc(). +** +** Multiple threads can run this routine at the same time. Global variables +** in pcache1 need to be protected via mutex. */ static void *pcache1Alloc(int nByte){ - void *p; - assert( sqlite3_mutex_held(pcache1.mutex) ); + void *p = 0; + assert( sqlite3_mutex_notheld(pcache1.grp.mutex) ); sqlite3StatusSet(SQLITE_STATUS_PAGECACHE_SIZE, nByte); - if( nByte<=pcache1.szSlot && pcache1.pFree ){ - assert( pcache1.isInit ); + if( nByte<=pcache1.szSlot ){ + sqlite3_mutex_enter(pcache1.mutex); p = (PgHdr1 *)pcache1.pFree; - pcache1.pFree = pcache1.pFree->pNext; - pcache1.nFreeSlot--; - assert( pcache1.nFreeSlot>=0 ); - sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, 1); - }else{ - - /* Allocate a new buffer using sqlite3Malloc. Before doing so, exit the - ** global pcache mutex and unlock the pager-cache object pCache. This is - ** so that if the attempt to allocate a new buffer causes the the - ** configured soft-heap-limit to be breached, it will be possible to - ** reclaim memory from this pager-cache. + if( p ){ + pcache1.pFree = pcache1.pFree->pNext; + pcache1.nFreeSlot--; + pcache1.bUnderPressure = pcache1.nFreeSlot<pcache1.nReserve; + assert( pcache1.nFreeSlot>=0 ); + sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, 1); + } + sqlite3_mutex_leave(pcache1.mutex); + } + if( p==0 ){ + /* Memory is not available in the SQLITE_CONFIG_PAGECACHE pool. Get + ** it from sqlite3Malloc instead. */ - pcache1LeaveMutex(); p = sqlite3Malloc(nByte); - pcache1EnterMutex(); if( p ){ int sz = sqlite3MallocSize(p); + sqlite3_mutex_enter(pcache1.mutex); sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, sz); + sqlite3_mutex_leave(pcache1.mutex); } sqlite3MemdebugSetType(p, MEMTYPE_PCACHE); } @@ -33507,22 +35520,26 @@ static void *pcache1Alloc(int nByte){ ** Free an allocated buffer obtained from pcache1Alloc(). */ static void pcache1Free(void *p){ - assert( sqlite3_mutex_held(pcache1.mutex) ); if( p==0 ) return; if( p>=pcache1.pStart && p<pcache1.pEnd ){ PgFreeslot *pSlot; + sqlite3_mutex_enter(pcache1.mutex); sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, -1); pSlot = (PgFreeslot*)p; pSlot->pNext = pcache1.pFree; pcache1.pFree = pSlot; pcache1.nFreeSlot++; + pcache1.bUnderPressure = pcache1.nFreeSlot<pcache1.nReserve; assert( pcache1.nFreeSlot<=pcache1.nSlot ); + sqlite3_mutex_leave(pcache1.mutex); }else{ int iSize; assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) ); sqlite3MemdebugSetType(p, MEMTYPE_HEAP); iSize = sqlite3MallocSize(p); + sqlite3_mutex_enter(pcache1.mutex); sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, -iSize); + sqlite3_mutex_leave(pcache1.mutex); sqlite3_free(p); } } @@ -33532,7 +35549,6 @@ static void pcache1Free(void *p){ ** Return the size of a pcache allocation */ static int pcache1MemSize(void *p){ - assert( sqlite3_mutex_held(pcache1.mutex) ); if( p>=pcache1.pStart && p<pcache1.pEnd ){ return pcache1.szSlot; }else{ @@ -33556,7 +35572,7 @@ static PgHdr1 *pcache1AllocPage(PCache1 *pCache){ if( pPg ){ p = PAGE_TO_PGHDR1(pCache, pPg); if( pCache->bPurgeable ){ - pcache1.nCurrentPage++; + pCache->pGroup->nCurrentPage++; } }else{ p = 0; @@ -33573,8 +35589,9 @@ static PgHdr1 *pcache1AllocPage(PCache1 *pCache){ */ static void pcache1FreePage(PgHdr1 *p){ if( ALWAYS(p) ){ - if( p->pCache->bPurgeable ){ - pcache1.nCurrentPage--; + PCache1 *pCache = p->pCache; + if( pCache->bPurgeable ){ + pCache->pGroup->nCurrentPage--; } pcache1Free(PGHDR1_TO_PAGE(p)); } @@ -33586,20 +35603,14 @@ static void pcache1FreePage(PgHdr1 *p){ ** exists, this function falls back to sqlite3Malloc(). */ SQLITE_PRIVATE void *sqlite3PageMalloc(int sz){ - void *p; - pcache1EnterMutex(); - p = pcache1Alloc(sz); - pcache1LeaveMutex(); - return p; + return pcache1Alloc(sz); } /* ** Free an allocated buffer obtained from sqlite3PageMalloc(). */ SQLITE_PRIVATE void sqlite3PageFree(void *p){ - pcache1EnterMutex(); pcache1Free(p); - pcache1LeaveMutex(); } @@ -33620,9 +35631,8 @@ SQLITE_PRIVATE void sqlite3PageFree(void *p){ ** the heap even further. */ static int pcache1UnderMemoryPressure(PCache1 *pCache){ - assert( sqlite3_mutex_held(pcache1.mutex) ); if( pcache1.nSlot && pCache->szPage<=pcache1.szSlot ){ - return pcache1.nFreeSlot<pcache1.nReserve; + return pcache1.bUnderPressure; }else{ return sqlite3HeapNearlyFull(); } @@ -33635,25 +35645,25 @@ static int pcache1UnderMemoryPressure(PCache1 *pCache){ ** This function is used to resize the hash table used by the cache passed ** as the first argument. ** -** The global mutex must be held when this function is called. +** The PCache mutex must be held when this function is called. */ static int pcache1ResizeHash(PCache1 *p){ PgHdr1 **apNew; unsigned int nNew; unsigned int i; - assert( sqlite3_mutex_held(pcache1.mutex) ); + assert( sqlite3_mutex_held(p->pGroup->mutex) ); nNew = p->nHash*2; if( nNew<256 ){ nNew = 256; } - pcache1LeaveMutex(); + pcache1LeaveMutex(p->pGroup); if( p->nHash ){ sqlite3BeginBenignMalloc(); } apNew = (PgHdr1 **)sqlite3_malloc(sizeof(PgHdr1 *)*nNew); if( p->nHash ){ sqlite3EndBenignMalloc(); } - pcache1EnterMutex(); + pcache1EnterMutex(p->pGroup); if( apNew ){ memset(apNew, 0, sizeof(PgHdr1 *)*nNew); for(i=0; i<p->nHash; i++){ @@ -33676,25 +35686,33 @@ static int pcache1ResizeHash(PCache1 *p){ /* ** This function is used internally to remove the page pPage from the -** global LRU list, if is part of it. If pPage is not part of the global +** PGroup LRU list, if is part of it. If pPage is not part of the PGroup ** LRU list, then this function is a no-op. ** -** The global mutex must be held when this function is called. +** The PGroup mutex must be held when this function is called. +** +** If pPage is NULL then this routine is a no-op. */ static void pcache1PinPage(PgHdr1 *pPage){ - assert( sqlite3_mutex_held(pcache1.mutex) ); - if( pPage && (pPage->pLruNext || pPage==pcache1.pLruTail) ){ + PCache1 *pCache; + PGroup *pGroup; + + if( pPage==0 ) return; + pCache = pPage->pCache; + pGroup = pCache->pGroup; + assert( sqlite3_mutex_held(pGroup->mutex) ); + if( pPage->pLruNext || pPage==pGroup->pLruTail ){ if( pPage->pLruPrev ){ pPage->pLruPrev->pLruNext = pPage->pLruNext; } if( pPage->pLruNext ){ pPage->pLruNext->pLruPrev = pPage->pLruPrev; } - if( pcache1.pLruHead==pPage ){ - pcache1.pLruHead = pPage->pLruNext; + if( pGroup->pLruHead==pPage ){ + pGroup->pLruHead = pPage->pLruNext; } - if( pcache1.pLruTail==pPage ){ - pcache1.pLruTail = pPage->pLruPrev; + if( pGroup->pLruTail==pPage ){ + pGroup->pLruTail = pPage->pLruPrev; } pPage->pLruNext = 0; pPage->pLruPrev = 0; @@ -33707,13 +35725,14 @@ static void pcache1PinPage(PgHdr1 *pPage){ ** Remove the page supplied as an argument from the hash table ** (PCache1.apHash structure) that it is currently stored in. ** -** The global mutex must be held when this function is called. +** The PGroup mutex must be held when this function is called. */ static void pcache1RemoveFromHash(PgHdr1 *pPage){ unsigned int h; PCache1 *pCache = pPage->pCache; PgHdr1 **pp; + assert( sqlite3_mutex_held(pCache->pGroup->mutex) ); h = pPage->iKey % pCache->nHash; for(pp=&pCache->apHash[h]; (*pp)!=pPage; pp=&(*pp)->pNext); *pp = (*pp)->pNext; @@ -33722,13 +35741,14 @@ static void pcache1RemoveFromHash(PgHdr1 *pPage){ } /* -** If there are currently more than pcache.nMaxPage pages allocated, try -** to recycle pages to reduce the number allocated to pcache.nMaxPage. +** If there are currently more than nMaxPage pages allocated, try +** to recycle pages to reduce the number allocated to nMaxPage. */ -static void pcache1EnforceMaxPage(void){ - assert( sqlite3_mutex_held(pcache1.mutex) ); - while( pcache1.nCurrentPage>pcache1.nMaxPage && pcache1.pLruTail ){ - PgHdr1 *p = pcache1.pLruTail; +static void pcache1EnforceMaxPage(PGroup *pGroup){ + assert( sqlite3_mutex_held(pGroup->mutex) ); + while( pGroup->nCurrentPage>pGroup->nMaxPage && pGroup->pLruTail ){ + PgHdr1 *p = pGroup->pLruTail; + assert( p->pCache->pGroup==pGroup ); pcache1PinPage(p); pcache1RemoveFromHash(p); pcache1FreePage(p); @@ -33740,15 +35760,15 @@ static void pcache1EnforceMaxPage(void){ ** greater than or equal to iLimit. Any pinned pages that meet this ** criteria are unpinned before they are discarded. ** -** The global mutex must be held when this function is called. +** The PCache mutex must be held when this function is called. */ static void pcache1TruncateUnsafe( - PCache1 *pCache, - unsigned int iLimit + PCache1 *pCache, /* The cache to truncate */ + unsigned int iLimit /* Drop pages with this pgno or larger */ ){ - TESTONLY( unsigned int nPage = 0; ) /* Used to assert pCache->nPage is correct */ + TESTONLY( unsigned int nPage = 0; ) /* To assert pCache->nPage is correct */ unsigned int h; - assert( sqlite3_mutex_held(pcache1.mutex) ); + assert( sqlite3_mutex_held(pCache->pGroup->mutex) ); for(h=0; h<pCache->nHash; h++){ PgHdr1 **pp = &pCache->apHash[h]; PgHdr1 *pPage; @@ -33778,8 +35798,10 @@ static int pcache1Init(void *NotUsed){ assert( pcache1.isInit==0 ); memset(&pcache1, 0, sizeof(pcache1)); if( sqlite3GlobalConfig.bCoreMutex ){ - pcache1.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_LRU); + pcache1.grp.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_LRU); + pcache1.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_PMEM); } + pcache1.grp.mxPinned = 10; pcache1.isInit = 1; return SQLITE_OK; } @@ -33801,18 +35823,47 @@ static void pcache1Shutdown(void *NotUsed){ ** Allocate a new cache. */ static sqlite3_pcache *pcache1Create(int szPage, int bPurgeable){ - PCache1 *pCache; + PCache1 *pCache; /* The newly created page cache */ + PGroup *pGroup; /* The group the new page cache will belong to */ + int sz; /* Bytes of memory required to allocate the new cache */ + + /* + ** The seperateCache variable is true if each PCache has its own private + ** PGroup. In other words, separateCache is true for mode (1) where no + ** mutexing is required. + ** + ** * Always use a unified cache (mode-2) if ENABLE_MEMORY_MANAGEMENT + ** + ** * Always use a unified cache in single-threaded applications + ** + ** * Otherwise (if multi-threaded and ENABLE_MEMORY_MANAGEMENT is off) + ** use separate caches (mode-1) + */ +#if defined(SQLITE_ENABLE_MEMORY_MANAGEMENT) || SQLITE_THREADSAFE==0 + const int separateCache = 0; +#else + int separateCache = sqlite3GlobalConfig.bCoreMutex>0; +#endif - pCache = (PCache1 *)sqlite3_malloc(sizeof(PCache1)); + sz = sizeof(PCache1) + sizeof(PGroup)*separateCache; + pCache = (PCache1 *)sqlite3_malloc(sz); if( pCache ){ - memset(pCache, 0, sizeof(PCache1)); + memset(pCache, 0, sz); + if( separateCache ){ + pGroup = (PGroup*)&pCache[1]; + pGroup->mxPinned = 10; + }else{ + pGroup = &pcache1.grp; + } + pCache->pGroup = pGroup; pCache->szPage = szPage; pCache->bPurgeable = (bPurgeable ? 1 : 0); if( bPurgeable ){ pCache->nMin = 10; - pcache1EnterMutex(); - pcache1.nMinPage += pCache->nMin; - pcache1LeaveMutex(); + pcache1EnterMutex(pGroup); + pGroup->nMinPage += pCache->nMin; + pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage; + pcache1LeaveMutex(pGroup); } } return (sqlite3_pcache *)pCache; @@ -33826,11 +35877,14 @@ static sqlite3_pcache *pcache1Create(int szPage, int bPurgeable){ static void pcache1Cachesize(sqlite3_pcache *p, int nMax){ PCache1 *pCache = (PCache1 *)p; if( pCache->bPurgeable ){ - pcache1EnterMutex(); - pcache1.nMaxPage += (nMax - pCache->nMax); + PGroup *pGroup = pCache->pGroup; + pcache1EnterMutex(pGroup); + pGroup->nMaxPage += (nMax - pCache->nMax); + pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage; pCache->nMax = nMax; - pcache1EnforceMaxPage(); - pcache1LeaveMutex(); + pCache->n90pct = pCache->nMax*9/10; + pcache1EnforceMaxPage(pGroup); + pcache1LeaveMutex(pGroup); } } @@ -33839,9 +35893,10 @@ static void pcache1Cachesize(sqlite3_pcache *p, int nMax){ */ static int pcache1Pagecount(sqlite3_pcache *p){ int n; - pcache1EnterMutex(); - n = ((PCache1 *)p)->nPage; - pcache1LeaveMutex(); + PCache1 *pCache = (PCache1*)p; + pcache1EnterMutex(pCache->pGroup); + n = pCache->nPage; + pcache1LeaveMutex(pCache->pGroup); return n; } @@ -33900,30 +35955,49 @@ static int pcache1Pagecount(sqlite3_pcache *p){ ** 5. Otherwise, allocate and return a new page buffer. */ static void *pcache1Fetch(sqlite3_pcache *p, unsigned int iKey, int createFlag){ - unsigned int nPinned; + int nPinned; PCache1 *pCache = (PCache1 *)p; + PGroup *pGroup; PgHdr1 *pPage = 0; assert( pCache->bPurgeable || createFlag!=1 ); - pcache1EnterMutex(); - if( createFlag==1 ) sqlite3BeginBenignMalloc(); + assert( pCache->bPurgeable || pCache->nMin==0 ); + assert( pCache->bPurgeable==0 || pCache->nMin==10 ); + assert( pCache->nMin==0 || pCache->bPurgeable ); + pcache1EnterMutex(pGroup = pCache->pGroup); - /* Search the hash table for an existing entry. */ + /* Step 1: Search the hash table for an existing entry. */ if( pCache->nHash>0 ){ unsigned int h = iKey % pCache->nHash; for(pPage=pCache->apHash[h]; pPage&&pPage->iKey!=iKey; pPage=pPage->pNext); } + /* Step 2: Abort if no existing page is found and createFlag is 0 */ if( pPage || createFlag==0 ){ pcache1PinPage(pPage); goto fetch_out; } - /* Step 3 of header comment. */ + /* The pGroup local variable will normally be initialized by the + ** pcache1EnterMutex() macro above. But if SQLITE_MUTEX_OMIT is defined, + ** then pcache1EnterMutex() is a no-op, so we have to initialize the + ** local variable here. Delaying the initialization of pGroup is an + ** optimization: The common case is to exit the module before reaching + ** this point. + */ +#ifdef SQLITE_MUTEX_OMIT + pGroup = pCache->pGroup; +#endif + + + /* Step 3: Abort if createFlag is 1 but the cache is nearly full */ nPinned = pCache->nPage - pCache->nRecyclable; + assert( nPinned>=0 ); + assert( pGroup->mxPinned == pGroup->nMaxPage + 10 - pGroup->nMinPage ); + assert( pCache->n90pct == pCache->nMax*9/10 ); if( createFlag==1 && ( - nPinned>=(pcache1.nMaxPage+pCache->nMin-pcache1.nMinPage) - || nPinned>=(pCache->nMax * 9 / 10) + nPinned>=pGroup->mxPinned + || nPinned>=(int)pCache->n90pct || pcache1UnderMemoryPressure(pCache) )){ goto fetch_out; @@ -33933,20 +36007,22 @@ static void *pcache1Fetch(sqlite3_pcache *p, unsigned int iKey, int createFlag){ goto fetch_out; } - /* Step 4. Try to recycle a page buffer if appropriate. */ - if( pCache->bPurgeable && pcache1.pLruTail && ( + /* Step 4. Try to recycle a page. */ + if( pCache->bPurgeable && pGroup->pLruTail && ( (pCache->nPage+1>=pCache->nMax) - || pcache1.nCurrentPage>=pcache1.nMaxPage + || pGroup->nCurrentPage>=pGroup->nMaxPage || pcache1UnderMemoryPressure(pCache) )){ - pPage = pcache1.pLruTail; + PCache1 *pOtherCache; + pPage = pGroup->pLruTail; pcache1RemoveFromHash(pPage); pcache1PinPage(pPage); - if( pPage->pCache->szPage!=pCache->szPage ){ + if( (pOtherCache = pPage->pCache)->szPage!=pCache->szPage ){ pcache1FreePage(pPage); pPage = 0; }else{ - pcache1.nCurrentPage -= (pPage->pCache->bPurgeable - pCache->bPurgeable); + pGroup->nCurrentPage -= + (pOtherCache->bPurgeable - pCache->bPurgeable); } } @@ -33954,7 +36030,11 @@ static void *pcache1Fetch(sqlite3_pcache *p, unsigned int iKey, int createFlag){ ** attempt to allocate a new one. */ if( !pPage ){ + if( createFlag==1 ) sqlite3BeginBenignMalloc(); + pcache1LeaveMutex(pGroup); pPage = pcache1AllocPage(pCache); + pcache1EnterMutex(pGroup); + if( createFlag==1 ) sqlite3EndBenignMalloc(); } if( pPage ){ @@ -33973,8 +36053,7 @@ fetch_out: if( pPage && iKey>pCache->iMaxKey ){ pCache->iMaxKey = iKey; } - if( createFlag==1 ) sqlite3EndBenignMalloc(); - pcache1LeaveMutex(); + pcache1LeaveMutex(pGroup); return (pPage ? PGHDR1_TO_PAGE(pPage) : 0); } @@ -33987,37 +36066,34 @@ fetch_out: static void pcache1Unpin(sqlite3_pcache *p, void *pPg, int reuseUnlikely){ PCache1 *pCache = (PCache1 *)p; PgHdr1 *pPage = PAGE_TO_PGHDR1(pCache, pPg); + PGroup *pGroup = pCache->pGroup; assert( pPage->pCache==pCache ); - pcache1EnterMutex(); + pcache1EnterMutex(pGroup); /* It is an error to call this function if the page is already - ** part of the global LRU list. + ** part of the PGroup LRU list. */ assert( pPage->pLruPrev==0 && pPage->pLruNext==0 ); - assert( pcache1.pLruHead!=pPage && pcache1.pLruTail!=pPage ); + assert( pGroup->pLruHead!=pPage && pGroup->pLruTail!=pPage ); - if( reuseUnlikely || pcache1.nCurrentPage>pcache1.nMaxPage ){ + if( reuseUnlikely || pGroup->nCurrentPage>pGroup->nMaxPage ){ pcache1RemoveFromHash(pPage); pcache1FreePage(pPage); }else{ - /* Add the page to the global LRU list. Normally, the page is added to - ** the head of the list (last page to be recycled). However, if the - ** reuseUnlikely flag passed to this function is true, the page is added - ** to the tail of the list (first page to be recycled). - */ - if( pcache1.pLruHead ){ - pcache1.pLruHead->pLruPrev = pPage; - pPage->pLruNext = pcache1.pLruHead; - pcache1.pLruHead = pPage; + /* Add the page to the PGroup LRU list. */ + if( pGroup->pLruHead ){ + pGroup->pLruHead->pLruPrev = pPage; + pPage->pLruNext = pGroup->pLruHead; + pGroup->pLruHead = pPage; }else{ - pcache1.pLruTail = pPage; - pcache1.pLruHead = pPage; + pGroup->pLruTail = pPage; + pGroup->pLruHead = pPage; } pCache->nRecyclable++; } - pcache1LeaveMutex(); + pcache1LeaveMutex(pCache->pGroup); } /* @@ -34036,7 +36112,7 @@ static void pcache1Rekey( assert( pPage->iKey==iOld ); assert( pPage->pCache==pCache ); - pcache1EnterMutex(); + pcache1EnterMutex(pCache->pGroup); h = iOld%pCache->nHash; pp = &pCache->apHash[h]; @@ -34053,7 +36129,7 @@ static void pcache1Rekey( pCache->iMaxKey = iNew; } - pcache1LeaveMutex(); + pcache1LeaveMutex(pCache->pGroup); } /* @@ -34065,12 +36141,12 @@ static void pcache1Rekey( */ static void pcache1Truncate(sqlite3_pcache *p, unsigned int iLimit){ PCache1 *pCache = (PCache1 *)p; - pcache1EnterMutex(); + pcache1EnterMutex(pCache->pGroup); if( iLimit<=pCache->iMaxKey ){ pcache1TruncateUnsafe(pCache, iLimit); pCache->iMaxKey = iLimit-1; } - pcache1LeaveMutex(); + pcache1LeaveMutex(pCache->pGroup); } /* @@ -34080,13 +36156,15 @@ static void pcache1Truncate(sqlite3_pcache *p, unsigned int iLimit){ */ static void pcache1Destroy(sqlite3_pcache *p){ PCache1 *pCache = (PCache1 *)p; + PGroup *pGroup = pCache->pGroup; assert( pCache->bPurgeable || (pCache->nMax==0 && pCache->nMin==0) ); - pcache1EnterMutex(); + pcache1EnterMutex(pGroup); pcache1TruncateUnsafe(pCache, 0); - pcache1.nMaxPage -= pCache->nMax; - pcache1.nMinPage -= pCache->nMin; - pcache1EnforceMaxPage(); - pcache1LeaveMutex(); + pGroup->nMaxPage -= pCache->nMax; + pGroup->nMinPage -= pCache->nMin; + pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage; + pcache1EnforceMaxPage(pGroup); + pcache1LeaveMutex(pGroup); sqlite3_free(pCache->apHash); sqlite3_free(pCache); } @@ -34125,16 +36203,18 @@ SQLITE_PRIVATE void sqlite3PCacheSetDefault(void){ */ SQLITE_PRIVATE int sqlite3PcacheReleaseMemory(int nReq){ int nFree = 0; + assert( sqlite3_mutex_notheld(pcache1.grp.mutex) ); + assert( sqlite3_mutex_notheld(pcache1.mutex) ); if( pcache1.pStart==0 ){ PgHdr1 *p; - pcache1EnterMutex(); - while( (nReq<0 || nFree<nReq) && ((p=pcache1.pLruTail)!=0) ){ + pcache1EnterMutex(&pcache1.grp); + while( (nReq<0 || nFree<nReq) && ((p=pcache1.grp.pLruTail)!=0) ){ nFree += pcache1MemSize(PGHDR1_TO_PAGE(p)); pcache1PinPage(p); pcache1RemoveFromHash(p); pcache1FreePage(p); } - pcache1LeaveMutex(); + pcache1LeaveMutex(&pcache1.grp); } return nFree; } @@ -34153,12 +36233,12 @@ SQLITE_PRIVATE void sqlite3PcacheStats( ){ PgHdr1 *p; int nRecyclable = 0; - for(p=pcache1.pLruHead; p; p=p->pLruNext){ + for(p=pcache1.grp.pLruHead; p; p=p->pLruNext){ nRecyclable++; } - *pnCurrent = pcache1.nCurrentPage; - *pnMax = pcache1.nMaxPage; - *pnMin = pcache1.nMinPage; + *pnCurrent = pcache1.grp.nCurrentPage; + *pnMax = pcache1.grp.nMaxPage; + *pnMin = pcache1.grp.nMinPage; *pnRecyclable = nRecyclable; } #endif @@ -34633,22 +36713,23 @@ SQLITE_PRIVATE int sqlite3RowSetTest(RowSet *pRowSet, u8 iBatch, sqlite3_int64 i #ifdef SQLITE_OMIT_WAL -# define sqlite3WalOpen(x,y,z) 0 -# define sqlite3WalClose(w,x,y,z) 0 -# define sqlite3WalBeginReadTransaction(y,z) 0 +# define sqlite3WalOpen(x,y,z) 0 +# define sqlite3WalLimit(x,y) +# define sqlite3WalClose(w,x,y,z) 0 +# define sqlite3WalBeginReadTransaction(y,z) 0 # define sqlite3WalEndReadTransaction(z) -# define sqlite3WalRead(v,w,x,y,z) 0 -# define sqlite3WalDbsize(y) 0 -# define sqlite3WalBeginWriteTransaction(y) 0 -# define sqlite3WalEndWriteTransaction(x) 0 -# define sqlite3WalUndo(x,y,z) 0 +# define sqlite3WalRead(v,w,x,y,z) 0 +# define sqlite3WalDbsize(y) 0 +# define sqlite3WalBeginWriteTransaction(y) 0 +# define sqlite3WalEndWriteTransaction(x) 0 +# define sqlite3WalUndo(x,y,z) 0 # define sqlite3WalSavepoint(y,z) -# define sqlite3WalSavepointUndo(y,z) 0 -# define sqlite3WalFrames(u,v,w,x,y,z) 0 -# define sqlite3WalCheckpoint(u,v,w,x) 0 -# define sqlite3WalCallback(z) 0 -# define sqlite3WalExclusiveMode(y,z) 0 -# define sqlite3WalHeapMemory(z) 0 +# define sqlite3WalSavepointUndo(y,z) 0 +# define sqlite3WalFrames(u,v,w,x,y,z) 0 +# define sqlite3WalCheckpoint(r,s,t,u,v,w,x,y,z) 0 +# define sqlite3WalCallback(z) 0 +# define sqlite3WalExclusiveMode(y,z) 0 +# define sqlite3WalHeapMemory(z) 0 #else #define WAL_SAVEPOINT_NDATA 4 @@ -34659,9 +36740,12 @@ SQLITE_PRIVATE int sqlite3RowSetTest(RowSet *pRowSet, u8 iBatch, sqlite3_int64 i typedef struct Wal Wal; /* Open and close a connection to a write-ahead log. */ -SQLITE_PRIVATE int sqlite3WalOpen(sqlite3_vfs*, sqlite3_file*, const char *zName, int, Wal**); +SQLITE_PRIVATE int sqlite3WalOpen(sqlite3_vfs*, sqlite3_file*, const char *, int, i64, Wal**); SQLITE_PRIVATE int sqlite3WalClose(Wal *pWal, int sync_flags, int, u8 *); +/* Set the limiting size of a WAL file. */ +SQLITE_PRIVATE void sqlite3WalLimit(Wal*, i64); + /* Used by readers to open (lock) and close (unlock) a snapshot. A ** snapshot is like a read-transaction. It is the state of the database ** at an instant in time. sqlite3WalOpenSnapshot gets a read lock and @@ -34699,9 +36783,14 @@ SQLITE_PRIVATE int sqlite3WalFrames(Wal *pWal, int, PgHdr *, Pgno, int, int); /* Copy pages from the log to the database file */ SQLITE_PRIVATE int sqlite3WalCheckpoint( Wal *pWal, /* Write-ahead log connection */ + int eMode, /* One of PASSIVE, FULL and RESTART */ + int (*xBusy)(void*), /* Function to call when busy */ + void *pBusyArg, /* Context argument for xBusyHandler */ int sync_flags, /* Flags to sync db file with (or 0) */ int nBuf, /* Size of buffer nBuf */ - u8 *zBuf /* Temporary buffer to use */ + u8 *zBuf, /* Temporary buffer to use */ + int *pnLog, /* OUT: Number of frames in WAL */ + int *pnCkpt /* OUT: Number of backfilled frames in WAL */ ); /* Return the value to pass to a sqlite3_wal_hook callback, the @@ -37183,15 +39272,21 @@ static int pager_truncate(Pager *pPager, Pgno nPage){ && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN) ){ i64 currentSize, newSize; + int szPage = pPager->pageSize; assert( pPager->eLock==EXCLUSIVE_LOCK ); /* TODO: Is it safe to use Pager.dbFileSize here? */ rc = sqlite3OsFileSize(pPager->fd, ¤tSize); - newSize = pPager->pageSize*(i64)nPage; + newSize = szPage*(i64)nPage; if( rc==SQLITE_OK && currentSize!=newSize ){ if( currentSize>newSize ){ rc = sqlite3OsTruncate(pPager->fd, newSize); }else{ - rc = sqlite3OsWrite(pPager->fd, "", 1, newSize-1); + char *pTmp = pPager->pTmpSpace; + memset(pTmp, 0, szPage); + testcase( (newSize-szPage) < currentSize ); + testcase( (newSize-szPage) == currentSize ); + testcase( (newSize-szPage) > currentSize ); + rc = sqlite3OsWrite(pPager->fd, pTmp, szPage, newSize-szPage); } if( rc==SQLITE_OK ){ pPager->dbFileSize = nPage; @@ -37455,10 +39550,10 @@ end_playback: rc = readMasterJournal(pPager->jfd, zMaster, pPager->pVfs->mxPathname+1); testcase( rc!=SQLITE_OK ); } - if( rc==SQLITE_OK && !pPager->noSync + if( rc==SQLITE_OK && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN) ){ - rc = sqlite3OsSync(pPager->fd, pPager->syncFlags); + rc = sqlite3PagerSync(pPager); } if( rc==SQLITE_OK ){ rc = pager_end_transaction(pPager, zMaster[0]!='\0'); @@ -37551,6 +39646,28 @@ static int readDbPage(PgHdr *pPg){ return rc; } +/* +** Update the value of the change-counter at offsets 24 and 92 in +** the header and the sqlite version number at offset 96. +** +** This is an unconditional update. See also the pager_incr_changecounter() +** routine which only updates the change-counter if the update is actually +** needed, as determined by the pPager->changeCountDone state variable. +*/ +static void pager_write_changecounter(PgHdr *pPg){ + u32 change_counter; + + /* Increment the value just read and write it back to byte 24. */ + change_counter = sqlite3Get4byte((u8*)pPg->pPager->dbFileVers)+1; + put32bits(((char*)pPg->pData)+24, change_counter); + + /* Also store the SQLite version number in bytes 96..99 and in + ** bytes 92..95 store the change counter for which the version number + ** is valid. */ + put32bits(((char*)pPg->pData)+92, change_counter); + put32bits(((char*)pPg->pData)+96, SQLITE_VERSION_NUMBER); +} + #ifndef SQLITE_OMIT_WAL /* ** This function is invoked once for each page that has already been @@ -37626,6 +39743,9 @@ static int pagerRollbackWal(Pager *pPager){ ** the contents of the list of pages headed by pList (connected by pDirty), ** this function notifies any active backup processes that the pages have ** changed. +** +** The list of pages passed into this routine is always sorted by page number. +** Hence, if page 1 appears anywhere on the list, it will be the first page. */ static int pagerWalFrames( Pager *pPager, /* Pager object */ @@ -37635,8 +39755,32 @@ static int pagerWalFrames( int syncFlags /* Flags to pass to OsSync() (or 0) */ ){ int rc; /* Return code */ +#if defined(SQLITE_DEBUG) || defined(SQLITE_CHECK_PAGES) + PgHdr *p; /* For looping over pages */ +#endif assert( pPager->pWal ); +#ifdef SQLITE_DEBUG + /* Verify that the page list is in accending order */ + for(p=pList; p && p->pDirty; p=p->pDirty){ + assert( p->pgno < p->pDirty->pgno ); + } +#endif + + if( isCommit ){ + /* If a WAL transaction is being committed, there is no point in writing + ** any pages with page numbers greater than nTruncate into the WAL file. + ** They will never be read by any client. So remove them from the pDirty + ** list here. */ + PgHdr *p; + PgHdr **ppNext = &pList; + for(p=pList; (*ppNext = p); p=p->pDirty){ + if( p->pgno<=nTruncate ) ppNext = &p->pDirty; + } + assert( pList ); + } + + if( pList->pgno==1 ) pager_write_changecounter(pList); rc = sqlite3WalFrames(pPager->pWal, pPager->pageSize, pList, nTruncate, isCommit, syncFlags ); @@ -37648,9 +39792,9 @@ static int pagerWalFrames( } #ifdef SQLITE_CHECK_PAGES - { - PgHdr *p; - for(p=pList; p; p=p->pDirty) pager_set_pagehash(p); + pList = sqlite3PcacheDirtyList(pPager->pPCache); + for(p=pList; p; p=p->pDirty){ + pager_set_pagehash(p); } #endif @@ -38675,6 +40819,7 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){ char *pData; /* Data to write */ assert( (pList->flags&PGHDR_NEED_SYNC)==0 ); + if( pList->pgno==1 ) pager_write_changecounter(pList); /* Encode the database */ CODEC2(pPager, pList->pData, pgno, 6, return SQLITE_NOMEM, pData); @@ -38949,6 +41094,8 @@ SQLITE_PRIVATE int sqlite3PagerOpen( int noReadlock = (flags & PAGER_NO_READLOCK)!=0; /* True to omit read-lock */ int pcacheSize = sqlite3PcacheSize(); /* Bytes to allocate for PCache */ u32 szPageDflt = SQLITE_DEFAULT_PAGE_SIZE; /* Default page size */ + const char *zUri = 0; /* URI args to copy */ + int nUri = 0; /* Number of bytes of URI args at *zUri */ /* Figure out how much space is required for each journal file-handle ** (there are two of them, the main journal and the sub-journal). This @@ -38979,6 +41126,7 @@ SQLITE_PRIVATE int sqlite3PagerOpen( ** leave both nPathname and zPathname set to 0. */ if( zFilename && zFilename[0] ){ + const char *z; nPathname = pVfs->mxPathname+1; zPathname = sqlite3Malloc(nPathname*2); if( zPathname==0 ){ @@ -38987,6 +41135,12 @@ SQLITE_PRIVATE int sqlite3PagerOpen( zPathname[0] = 0; /* Make sure initialized even if FullPathname() fails */ rc = sqlite3OsFullPathname(pVfs, zFilename, nPathname, zPathname); nPathname = sqlite3Strlen30(zPathname); + z = zUri = &zFilename[sqlite3Strlen30(zFilename)+1]; + while( *z ){ + z += sqlite3Strlen30(z)+1; + z += sqlite3Strlen30(z)+1; + } + nUri = &z[1] - zUri; if( rc==SQLITE_OK && nPathname+8>pVfs->mxPathname ){ /* This branch is taken when the journal path required by ** the database being opened will be more than pVfs->mxPathname @@ -39019,7 +41173,7 @@ SQLITE_PRIVATE int sqlite3PagerOpen( ROUND8(pcacheSize) + /* PCache object */ ROUND8(pVfs->szOsFile) + /* The main db file */ journalFileSize * 2 + /* The two journal files */ - nPathname + 1 + /* zFilename */ + nPathname + 1 + nUri + /* zFilename */ nPathname + 8 + 1 /* zJournal */ #ifndef SQLITE_OMIT_WAL + nPathname + 4 + 1 /* zWal */ @@ -39041,14 +41195,17 @@ SQLITE_PRIVATE int sqlite3PagerOpen( /* Fill in the Pager.zFilename and Pager.zJournal buffers, if required. */ if( zPathname ){ assert( nPathname>0 ); - pPager->zJournal = (char*)(pPtr += nPathname + 1); + pPager->zJournal = (char*)(pPtr += nPathname + 1 + nUri); memcpy(pPager->zFilename, zPathname, nPathname); + memcpy(&pPager->zFilename[nPathname+1], zUri, nUri); memcpy(pPager->zJournal, zPathname, nPathname); memcpy(&pPager->zJournal[nPathname], "-journal", 8); + sqlite3FileSuffix3(pPager->zFilename, pPager->zJournal); #ifndef SQLITE_OMIT_WAL pPager->zWal = &pPager->zJournal[nPathname+8+1]; memcpy(pPager->zWal, zPathname, nPathname); memcpy(&pPager->zWal[nPathname], "-wal", 4); + sqlite3FileSuffix3(pPager->zFilename, pPager->zWal); #endif sqlite3_free(zPathname); } @@ -40195,7 +42352,13 @@ SQLITE_PRIVATE void sqlite3PagerDontWrite(PgHdr *pPg){ /* ** This routine is called to increment the value of the database file ** change-counter, stored as a 4-byte big-endian integer starting at -** byte offset 24 of the pager file. +** byte offset 24 of the pager file. The secondary change counter at +** 92 is also updated, as is the SQLite version number at offset 96. +** +** But this only happens if the pPager->changeCountDone flag is false. +** To avoid excess churning of page 1, the update only happens once. +** See also the pager_write_changecounter() routine that does an +** unconditional update of the change counters. ** ** If the isDirectMode flag is zero, then this is done by calling ** sqlite3PagerWrite() on page 1, then modifying the contents of the @@ -40236,7 +42399,6 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){ if( !pPager->changeCountDone && pPager->dbSize>0 ){ PgHdr *pPgHdr; /* Reference to page 1 */ - u32 change_counter; /* Initial value of change-counter field */ assert( !pPager->tempFile && isOpen(pPager->fd) ); @@ -40254,16 +42416,8 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){ } if( rc==SQLITE_OK ){ - /* Increment the value just read and write it back to byte 24. */ - change_counter = sqlite3Get4byte((u8*)pPager->dbFileVers); - change_counter++; - put32bits(((char*)pPgHdr->pData)+24, change_counter); - - /* Also store the SQLite version number in bytes 96..99 and in - ** bytes 92..95 store the change counter for which the version number - ** is valid. */ - put32bits(((char*)pPgHdr->pData)+92, change_counter); - put32bits(((char*)pPgHdr->pData)+96, SQLITE_VERSION_NUMBER); + /* Actually do the update of the change counter */ + pager_write_changecounter(pPgHdr); /* If running in direct mode, write the contents of page 1 to the file. */ if( DIRECT_MODE ){ @@ -40295,12 +42449,13 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){ ** function returns SQLITE_OK. Otherwise, an IO error code is returned. */ SQLITE_PRIVATE int sqlite3PagerSync(Pager *pPager){ - int rc; /* Return code */ - assert( !MEMDB ); - if( pPager->noSync ){ - rc = SQLITE_OK; - }else{ + int rc = SQLITE_OK; + if( !pPager->noSync ){ + assert( !MEMDB ); rc = sqlite3OsSync(pPager->fd, pPager->syncFlags); + }else if( isOpen(pPager->fd) ){ + assert( !MEMDB ); + sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SYNC_OMITTED, (void *)&rc); } return rc; } @@ -40387,11 +42542,21 @@ SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne( }else{ if( pagerUseWal(pPager) ){ PgHdr *pList = sqlite3PcacheDirtyList(pPager->pPCache); - if( pList ){ + PgHdr *pPageOne = 0; + if( pList==0 ){ + /* Must have at least one page for the WAL commit flag. + ** Ticket [2d1a5c67dfc2363e44f29d9bbd57f] 2011-05-18 */ + rc = sqlite3PagerGet(pPager, 1, &pPageOne); + pList = pPageOne; + pList->pDirty = 0; + } + assert( rc==SQLITE_OK ); + if( ALWAYS(pList) ){ rc = pagerWalFrames(pPager, pList, pPager->dbSize, 1, (pPager->fullSync ? pPager->syncFlags : 0) ); } + sqlite3PagerUnref(pPageOne); if( rc==SQLITE_OK ){ sqlite3PcacheCleanAll(pPager->pPCache); } @@ -40519,8 +42684,8 @@ SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne( } /* Finally, sync the database file. */ - if( !pPager->noSync && !noSync ){ - rc = sqlite3OsSync(pPager->fd, pPager->syncFlags); + if( !noSync ){ + rc = sqlite3PagerSync(pPager); } IOTRACE(("DBSYNC %p\n", pPager)) } @@ -40632,7 +42797,17 @@ SQLITE_PRIVATE int sqlite3PagerRollback(Pager *pPager){ rc2 = pager_end_transaction(pPager, pPager->setMaster); if( rc==SQLITE_OK ) rc = rc2; }else if( !isOpen(pPager->jfd) || pPager->eState==PAGER_WRITER_LOCKED ){ + int eState = pPager->eState; rc = pager_end_transaction(pPager, 0); + if( !MEMDB && eState>PAGER_WRITER_LOCKED ){ + /* This can happen using journal_mode=off. Move the pager to the error + ** state to indicate that the contents of the cache may not be trusted. + ** Any active readers will get SQLITE_ABORT. + */ + pPager->errCode = SQLITE_ABORT; + pPager->eState = PAGER_ERROR; + return rc; + } }else{ rc = pager_playback(pPager, 0); } @@ -41239,6 +43414,7 @@ SQLITE_PRIVATE int sqlite3PagerOkToChangeJournalMode(Pager *pPager){ SQLITE_PRIVATE i64 sqlite3PagerJournalSizeLimit(Pager *pPager, i64 iLimit){ if( iLimit>=-1 ){ pPager->journalSizeLimit = iLimit; + sqlite3WalLimit(pPager->pWal, iLimit); } return pPager->journalSizeLimit; } @@ -41255,14 +43431,20 @@ SQLITE_PRIVATE sqlite3_backup **sqlite3PagerBackupPtr(Pager *pPager){ #ifndef SQLITE_OMIT_WAL /* -** This function is called when the user invokes "PRAGMA checkpoint". +** This function is called when the user invokes "PRAGMA wal_checkpoint", +** "PRAGMA wal_blocking_checkpoint" or calls the sqlite3_wal_checkpoint() +** or wal_blocking_checkpoint() API functions. +** +** Parameter eMode is one of SQLITE_CHECKPOINT_PASSIVE, FULL or RESTART. */ -SQLITE_PRIVATE int sqlite3PagerCheckpoint(Pager *pPager){ +SQLITE_PRIVATE int sqlite3PagerCheckpoint(Pager *pPager, int eMode, int *pnLog, int *pnCkpt){ int rc = SQLITE_OK; if( pPager->pWal ){ - u8 *zBuf = (u8 *)pPager->pTmpSpace; - rc = sqlite3WalCheckpoint(pPager->pWal, pPager->ckptSyncFlags, - pPager->pageSize, zBuf); + rc = sqlite3WalCheckpoint(pPager->pWal, eMode, + pPager->xBusyHandler, pPager->pBusyHandlerArg, + pPager->ckptSyncFlags, pPager->pageSize, (u8 *)pPager->pTmpSpace, + pnLog, pnCkpt + ); } return rc; } @@ -41290,8 +43472,8 @@ static int pagerExclusiveLock(Pager *pPager){ assert( pPager->eLock==SHARED_LOCK || pPager->eLock==EXCLUSIVE_LOCK ); rc = pagerLockDb(pPager, EXCLUSIVE_LOCK); if( rc!=SQLITE_OK ){ - /* If the attempt to grab the pending lock failed, release the - ** exclusive lock that may have been obtained instead. */ + /* If the attempt to grab the exclusive lock failed, release the + ** pending lock that may have been obtained instead. */ pagerUnlockDb(pPager, SHARED_LOCK); } @@ -41324,7 +43506,8 @@ static int pagerOpenWal(Pager *pPager){ */ if( rc==SQLITE_OK ){ rc = sqlite3WalOpen(pPager->pVfs, - pPager->fd, pPager->zWal, pPager->exclusiveMode, &pPager->pWal + pPager->fd, pPager->zWal, pPager->exclusiveMode, + pPager->journalSizeLimit, &pPager->pWal ); } @@ -41856,6 +44039,7 @@ struct Wal { sqlite3_file *pDbFd; /* File handle for the database file */ sqlite3_file *pWalFd; /* File handle for WAL file */ u32 iCallback; /* Value to pass to log callback (or 0) */ + i64 mxWalSize; /* Truncate WAL to this size upon reset */ int nWiData; /* Size of array apWiData */ volatile u32 **apWiData; /* Pointer to wal-index content in memory */ u32 szPage; /* Database page size */ @@ -41863,7 +44047,7 @@ struct Wal { u8 exclusiveMode; /* Non-zero if connection is in exclusive mode */ u8 writeLock; /* True if in a write transaction */ u8 ckptLock; /* True if holding a checkpoint lock */ - u8 readOnly; /* True if the WAL file is open read-only */ + u8 readOnly; /* WAL_RDWR, WAL_RDONLY, or WAL_SHM_RDONLY */ WalIndexHdr hdr; /* Wal-index header for current transaction */ const char *zWalName; /* Name of WAL file */ u32 nCkpt; /* Checkpoint sequence counter in the wal-header */ @@ -41880,6 +44064,13 @@ struct Wal { #define WAL_HEAPMEMORY_MODE 2 /* +** Possible values for WAL.readOnly +*/ +#define WAL_RDWR 0 /* Normal read/write connection */ +#define WAL_RDONLY 1 /* The WAL file is readonly */ +#define WAL_SHM_RDONLY 2 /* The SHM file is readonly */ + +/* ** Each page of the wal-index mapping contains a hash-table made up of ** an array of HASHTABLE_NSLOT elements of the following type. */ @@ -41902,14 +44093,14 @@ typedef u16 ht_slot; */ struct WalIterator { int iPrior; /* Last result returned from the iterator */ - int nSegment; /* Size of the aSegment[] array */ + int nSegment; /* Number of entries in aSegment[] */ struct WalSegment { int iNext; /* Next slot in aIndex[] not yet returned */ ht_slot *aIndex; /* i0, i1, i2... such that aPgno[iN] ascend */ u32 *aPgno; /* Array of page numbers. */ - int nEntry; /* Max size of aPgno[] and aIndex[] arrays */ + int nEntry; /* Nr. of entries in aPgno[] and aIndex[] */ int iZero; /* Frame number associated with aPgno[0] */ - } aSegment[1]; /* One for every 32KB page in the WAL */ + } aSegment[1]; /* One for every 32KB page in the wal-index */ }; /* @@ -41972,6 +44163,10 @@ static int walIndexPage(Wal *pWal, int iPage, volatile u32 **ppPage){ rc = sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ, pWal->writeLock, (void volatile **)&pWal->apWiData[iPage] ); + if( rc==SQLITE_READONLY ){ + pWal->readOnly |= WAL_SHM_RDONLY; + rc = SQLITE_OK; + } } } @@ -42678,6 +44873,7 @@ SQLITE_PRIVATE int sqlite3WalOpen( sqlite3_file *pDbFd, /* The open database file */ const char *zWalName, /* Name of the WAL file */ int bNoShm, /* True to run in heap-memory mode */ + i64 mxWalSize, /* Truncate WAL to this size on reset */ Wal **ppWal /* OUT: Allocated Wal handle */ ){ int rc; /* Return Code */ @@ -42710,6 +44906,7 @@ SQLITE_PRIVATE int sqlite3WalOpen( pRet->pWalFd = (sqlite3_file *)&pRet[1]; pRet->pDbFd = pDbFd; pRet->readLock = -1; + pRet->mxWalSize = mxWalSize; pRet->zWalName = zWalName; pRet->exclusiveMode = (bNoShm ? WAL_HEAPMEMORY_MODE: WAL_NORMAL_MODE); @@ -42717,7 +44914,7 @@ SQLITE_PRIVATE int sqlite3WalOpen( flags = (SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_WAL); rc = sqlite3OsOpen(pVfs, zWalName, pRet->pWalFd, flags, &flags); if( rc==SQLITE_OK && flags&SQLITE_OPEN_READONLY ){ - pRet->readOnly = 1; + pRet->readOnly = WAL_RDONLY; } if( rc!=SQLITE_OK ){ @@ -42732,6 +44929,13 @@ SQLITE_PRIVATE int sqlite3WalOpen( } /* +** Change the size to which the WAL file is trucated on each reset. +*/ +SQLITE_PRIVATE void sqlite3WalLimit(Wal *pWal, i64 iLimit){ + if( pWal ) pWal->mxWalSize = iLimit; +} + +/* ** Find the smallest page number out of all pages held in the WAL that ** has not been returned by any prior invocation of this method on the ** same WalIterator object. Write into *piFrame the frame index where @@ -42773,9 +44977,29 @@ static int walIteratorNext( /* ** This function merges two sorted lists into a single sorted list. +** +** aLeft[] and aRight[] are arrays of indices. The sort key is +** aContent[aLeft[]] and aContent[aRight[]]. Upon entry, the following +** is guaranteed for all J<K: +** +** aContent[aLeft[J]] < aContent[aLeft[K]] +** aContent[aRight[J]] < aContent[aRight[K]] +** +** This routine overwrites aRight[] with a new (probably longer) sequence +** of indices such that the aRight[] contains every index that appears in +** either aLeft[] or the old aRight[] and such that the second condition +** above is still met. +** +** The aContent[aLeft[X]] values will be unique for all X. And the +** aContent[aRight[X]] values will be unique too. But there might be +** one or more combinations of X and Y such that +** +** aLeft[X]!=aRight[Y] && aContent[aLeft[X]] == aContent[aRight[Y]] +** +** When that happens, omit the aLeft[X] and use the aRight[Y] index. */ static void walMerge( - u32 *aContent, /* Pages in wal */ + const u32 *aContent, /* Pages in wal - keys for the sort */ ht_slot *aLeft, /* IN: Left hand input list */ int nLeft, /* IN: Elements in array *paLeft */ ht_slot **paRight, /* IN/OUT: Right hand input list */ @@ -42815,10 +45039,24 @@ static void walMerge( } /* -** Sort the elements in list aList, removing any duplicates. +** Sort the elements in list aList using aContent[] as the sort key. +** Remove elements with duplicate keys, preferring to keep the +** larger aList[] values. +** +** The aList[] entries are indices into aContent[]. The values in +** aList[] are to be sorted so that for all J<K: +** +** aContent[aList[J]] < aContent[aList[K]] +** +** For any X and Y such that +** +** aContent[aList[X]] == aContent[aList[Y]] +** +** Keep the larger of the two values aList[X] and aList[Y] and discard +** the smaller. */ static void walMergesort( - u32 *aContent, /* Pages in wal */ + const u32 *aContent, /* Pages in wal */ ht_slot *aBuffer, /* Buffer of at least *pnList items to use */ ht_slot *aList, /* IN/OUT: List to sort */ int *pnList /* IN/OUT: Number of elements in aList[] */ @@ -42883,6 +45121,7 @@ static void walIteratorFree(WalIterator *p){ /* ** Construct a WalInterator object that can be used to loop over all ** pages in the WAL in ascending order. The caller must hold the checkpoint +** lock. ** ** On success, make *pp point to the newly allocated WalInterator object ** return SQLITE_OK. Otherwise, return an error code. If this routine @@ -42968,6 +45207,34 @@ static int walIteratorInit(Wal *pWal, WalIterator **pp){ } /* +** Attempt to obtain the exclusive WAL lock defined by parameters lockIdx and +** n. If the attempt fails and parameter xBusy is not NULL, then it is a +** busy-handler function. Invoke it and retry the lock until either the +** lock is successfully obtained or the busy-handler returns 0. +*/ +static int walBusyLock( + Wal *pWal, /* WAL connection */ + int (*xBusy)(void*), /* Function to call when busy */ + void *pBusyArg, /* Context argument for xBusyHandler */ + int lockIdx, /* Offset of first byte to lock */ + int n /* Number of bytes to lock */ +){ + int rc; + do { + rc = walLockExclusive(pWal, lockIdx, n); + }while( xBusy && rc==SQLITE_BUSY && xBusy(pBusyArg) ); + return rc; +} + +/* +** The cache of the wal-index header must be valid to call this function. +** Return the page-size in bytes used by the database. +*/ +static int walPagesize(Wal *pWal){ + return (pWal->hdr.szPage&0xfe00) + ((pWal->hdr.szPage&0x0001)<<16); +} + +/* ** Copy as much content as we can from the WAL back into the database file ** in response to an sqlite3_wal_checkpoint() request or the equivalent. ** @@ -43000,8 +45267,10 @@ static int walIteratorInit(Wal *pWal, WalIterator **pp){ */ static int walCheckpoint( Wal *pWal, /* Wal connection */ + int eMode, /* One of PASSIVE, FULL or RESTART */ + int (*xBusyCall)(void*), /* Function to call when busy */ + void *pBusyArg, /* Context argument for xBusyHandler */ int sync_flags, /* Flags for OsSync() (or 0) */ - int nBuf, /* Size of zBuf in bytes */ u8 *zBuf /* Temporary buffer to use */ ){ int rc; /* Return code */ @@ -43013,11 +45282,13 @@ static int walCheckpoint( u32 mxPage; /* Max database page to write */ int i; /* Loop counter */ volatile WalCkptInfo *pInfo; /* The checkpoint status information */ + int (*xBusy)(void*) = 0; /* Function to call when waiting for locks */ - szPage = (pWal->hdr.szPage&0xfe00) + ((pWal->hdr.szPage&0x0001)<<16); + szPage = walPagesize(pWal); testcase( szPage<=32768 ); testcase( szPage>=65536 ); - if( pWal->hdr.mxFrame==0 ) return SQLITE_OK; + pInfo = walCkptInfo(pWal); + if( pInfo->nBackfill>=pWal->hdr.mxFrame ) return SQLITE_OK; /* Allocate the iterator */ rc = walIteratorInit(pWal, &pIter); @@ -43026,11 +45297,7 @@ static int walCheckpoint( } assert( pIter ); - /*** TODO: Move this test out to the caller. Make it an assert() here ***/ - if( szPage!=nBuf ){ - rc = SQLITE_CORRUPT_BKPT; - goto walcheckpoint_out; - } + if( eMode!=SQLITE_CHECKPOINT_PASSIVE ) xBusy = xBusyCall; /* Compute in mxSafeFrame the index of the last frame of the WAL that is ** safe to write into the database. Frames beyond mxSafeFrame might @@ -43039,17 +45306,17 @@ static int walCheckpoint( */ mxSafeFrame = pWal->hdr.mxFrame; mxPage = pWal->hdr.nPage; - pInfo = walCkptInfo(pWal); for(i=1; i<WAL_NREADER; i++){ u32 y = pInfo->aReadMark[i]; - if( mxSafeFrame>=y ){ + if( mxSafeFrame>y ){ assert( y<=pWal->hdr.mxFrame ); - rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1); + rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(i), 1); if( rc==SQLITE_OK ){ pInfo->aReadMark[i] = READMARK_NOT_USED; walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); }else if( rc==SQLITE_BUSY ){ mxSafeFrame = y; + xBusy = 0; }else{ goto walcheckpoint_out; } @@ -43057,7 +45324,7 @@ static int walCheckpoint( } if( pInfo->nBackfill<mxSafeFrame - && (rc = walLockExclusive(pWal, WAL_READ_LOCK(0), 1))==SQLITE_OK + && (rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(0), 1))==SQLITE_OK ){ i64 nSize; /* Current size of database file */ u32 nBackfill = pInfo->nBackfill; @@ -43110,13 +45377,32 @@ static int walCheckpoint( /* Release the reader lock held while backfilling */ walUnlockExclusive(pWal, WAL_READ_LOCK(0), 1); - }else if( rc==SQLITE_BUSY ){ + } + + if( rc==SQLITE_BUSY ){ /* Reset the return code so as not to report a checkpoint failure - ** just because active readers prevent any backfill. - */ + ** just because there are active readers. */ rc = SQLITE_OK; } + /* If this is an SQLITE_CHECKPOINT_RESTART operation, and the entire wal + ** file has been copied into the database file, then block until all + ** readers have finished using the wal file. This ensures that the next + ** process to write to the database restarts the wal file. + */ + if( rc==SQLITE_OK && eMode!=SQLITE_CHECKPOINT_PASSIVE ){ + assert( pWal->writeLock ); + if( pInfo->nBackfill<pWal->hdr.mxFrame ){ + rc = SQLITE_BUSY; + }else if( eMode==SQLITE_CHECKPOINT_RESTART ){ + assert( mxSafeFrame==pWal->hdr.mxFrame ); + rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(1), WAL_NREADER-1); + if( rc==SQLITE_OK ){ + walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1); + } + } + } + walcheckpoint_out: walIteratorFree(pIter); return rc; @@ -43148,7 +45434,9 @@ SQLITE_PRIVATE int sqlite3WalClose( if( pWal->exclusiveMode==WAL_NORMAL_MODE ){ pWal->exclusiveMode = WAL_EXCLUSIVE_MODE; } - rc = sqlite3WalCheckpoint(pWal, sync_flags, nBuf, zBuf); + rc = sqlite3WalCheckpoint( + pWal, SQLITE_CHECKPOINT_PASSIVE, 0, 0, sync_flags, nBuf, zBuf, 0, 0 + ); if( rc==SQLITE_OK ){ isDelete = 1; } @@ -43267,21 +45555,28 @@ static int walIndexReadHdr(Wal *pWal, int *pChanged){ ** with a writer. So get a WRITE lock and try again. */ assert( badHdr==0 || pWal->writeLock==0 ); - if( badHdr && SQLITE_OK==(rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1)) ){ - pWal->writeLock = 1; - if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){ - badHdr = walIndexTryHdr(pWal, pChanged); - if( badHdr ){ - /* If the wal-index header is still malformed even while holding - ** a WRITE lock, it can only mean that the header is corrupted and - ** needs to be reconstructed. So run recovery to do exactly that. - */ - rc = walIndexRecover(pWal); - *pChanged = 1; + if( badHdr ){ + if( pWal->readOnly & WAL_SHM_RDONLY ){ + if( SQLITE_OK==(rc = walLockShared(pWal, WAL_WRITE_LOCK)) ){ + walUnlockShared(pWal, WAL_WRITE_LOCK); + rc = SQLITE_READONLY_RECOVERY; + } + }else if( SQLITE_OK==(rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1)) ){ + pWal->writeLock = 1; + if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){ + badHdr = walIndexTryHdr(pWal, pChanged); + if( badHdr ){ + /* If the wal-index header is still malformed even while holding + ** a WRITE lock, it can only mean that the header is corrupted and + ** needs to be reconstructed. So run recovery to do exactly that. + */ + rc = walIndexRecover(pWal); + *pChanged = 1; + } } + pWal->writeLock = 0; + walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1); } - pWal->writeLock = 0; - walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1); } /* If the header is read successfully, check the version number to make @@ -43360,10 +45655,31 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ assert( pWal->readLock<0 ); /* Not currently locked */ - /* Take steps to avoid spinning forever if there is a protocol error. */ + /* Take steps to avoid spinning forever if there is a protocol error. + ** + ** Circumstances that cause a RETRY should only last for the briefest + ** instances of time. No I/O or other system calls are done while the + ** locks are held, so the locks should not be held for very long. But + ** if we are unlucky, another process that is holding a lock might get + ** paged out or take a page-fault that is time-consuming to resolve, + ** during the few nanoseconds that it is holding the lock. In that case, + ** it might take longer than normal for the lock to free. + ** + ** After 5 RETRYs, we begin calling sqlite3OsSleep(). The first few + ** calls to sqlite3OsSleep() have a delay of 1 microsecond. Really this + ** is more of a scheduler yield than an actual delay. But on the 10th + ** an subsequent retries, the delays start becoming longer and longer, + ** so that on the 100th (and last) RETRY we delay for 21 milliseconds. + ** The total delay time before giving up is less than 1 second. + */ if( cnt>5 ){ - if( cnt>100 ) return SQLITE_PROTOCOL; - sqlite3OsSleep(pWal->pVfs, 1); + int nDelay = 1; /* Pause time in microseconds */ + if( cnt>100 ){ + VVA_ONLY( pWal->lockError = 1; ) + return SQLITE_PROTOCOL; + } + if( cnt>=10 ) nDelay = (cnt-9)*238; /* Max delay 21ms. Total delay 996ms */ + sqlite3OsSleep(pWal->pVfs, nDelay); } if( !useWal ){ @@ -43445,22 +45761,11 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ mxI = i; } } - if( mxI==0 ){ - /* If we get here, it means that all of the aReadMark[] entries between - ** 1 and WAL_NREADER-1 are zero. Try to initialize aReadMark[1] to - ** be mxFrame, then retry. - */ - rc = walLockExclusive(pWal, WAL_READ_LOCK(1), 1); - if( rc==SQLITE_OK ){ - pInfo->aReadMark[1] = pWal->hdr.mxFrame; - walUnlockExclusive(pWal, WAL_READ_LOCK(1), 1); - rc = WAL_RETRY; - }else if( rc==SQLITE_BUSY ){ - rc = WAL_RETRY; - } - return rc; - }else{ - if( mxReadMark < pWal->hdr.mxFrame ){ + /* There was once an "if" here. The extra "{" is to preserve indentation. */ + { + if( (pWal->readOnly & WAL_SHM_RDONLY)==0 + && (mxReadMark<pWal->hdr.mxFrame || mxI==0) + ){ for(i=1; i<WAL_NREADER; i++){ rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1); if( rc==SQLITE_OK ){ @@ -43473,6 +45778,10 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ } } } + if( mxI==0 ){ + assert( rc==SQLITE_BUSY || (pWal->readOnly & WAL_SHM_RDONLY)!=0 ); + return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTLOCK; + } rc = walLockShared(pWal, WAL_READ_LOCK(mxI)); if( rc ){ @@ -43533,6 +45842,10 @@ SQLITE_PRIVATE int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){ do{ rc = walTryBeginRead(pWal, pChanged, 0, ++cnt); }while( rc==WAL_RETRY ); + testcase( (rc&0xff)==SQLITE_BUSY ); + testcase( (rc&0xff)==SQLITE_IOERR ); + testcase( rc==SQLITE_PROTOCOL ); + testcase( rc==SQLITE_OK ); return rc; } @@ -43850,6 +46163,8 @@ static int walRestartLog(Wal *pWal){ volatile WalCkptInfo *pInfo = walCkptInfo(pWal); assert( pInfo->nBackfill==pWal->hdr.mxFrame ); if( pInfo->nBackfill>0 ){ + u32 salt1; + sqlite3_randomness(4, &salt1); rc = walLockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1); if( rc==SQLITE_OK ){ /* If all readers are using WAL_READ_LOCK(0) (in other words if no @@ -43864,10 +46179,28 @@ static int walRestartLog(Wal *pWal){ */ int i; /* Loop counter */ u32 *aSalt = pWal->hdr.aSalt; /* Big-endian salt values */ + + /* Limit the size of WAL file if the journal_size_limit PRAGMA is + ** set to a non-negative value. Log errors encountered + ** during the truncation attempt. */ + if( pWal->mxWalSize>=0 ){ + i64 sz; + int rx; + sqlite3BeginBenignMalloc(); + rx = sqlite3OsFileSize(pWal->pWalFd, &sz); + if( rx==SQLITE_OK && (sz > pWal->mxWalSize) ){ + rx = sqlite3OsTruncate(pWal->pWalFd, pWal->mxWalSize); + } + sqlite3EndBenignMalloc(); + if( rx ){ + sqlite3_log(rx, "cannot limit WAL size: %s", pWal->zWalName); + } + } + pWal->nCkpt++; pWal->hdr.mxFrame = 0; sqlite3Put4byte((u8*)&aSalt[0], 1 + sqlite3Get4byte((u8*)&aSalt[0])); - sqlite3_randomness(4, &aSalt[1]); + aSalt[1] = salt1; walIndexWriteHdr(pWal); pInfo->nBackfill = 0; for(i=1; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED; @@ -43884,6 +46217,10 @@ static int walRestartLog(Wal *pWal){ int notUsed; rc = walTryBeginRead(pWal, ¬Used, 1, ++cnt); }while( rc==WAL_RETRY ); + assert( (rc&0xff)!=SQLITE_BUSY ); /* BUSY not possible when useWal==1 */ + testcase( (rc&0xff)==SQLITE_IOERR ); + testcase( rc==SQLITE_PROTOCOL ); + testcase( rc==SQLITE_OK ); } return rc; } @@ -44063,18 +46400,29 @@ SQLITE_PRIVATE int sqlite3WalFrames( ** ** Obtain a CHECKPOINT lock and then backfill as much information as ** we can from WAL into the database. +** +** If parameter xBusy is not NULL, it is a pointer to a busy-handler +** callback. In this case this function runs a blocking checkpoint. */ SQLITE_PRIVATE int sqlite3WalCheckpoint( Wal *pWal, /* Wal connection */ + int eMode, /* PASSIVE, FULL or RESTART */ + int (*xBusy)(void*), /* Function to call when busy */ + void *pBusyArg, /* Context argument for xBusyHandler */ int sync_flags, /* Flags to sync db file with (or 0) */ int nBuf, /* Size of temporary buffer */ - u8 *zBuf /* Temporary buffer to use */ + u8 *zBuf, /* Temporary buffer to use */ + int *pnLog, /* OUT: Number of frames in WAL */ + int *pnCkpt /* OUT: Number of backfilled frames in WAL */ ){ int rc; /* Return code */ int isChanged = 0; /* True if a new wal-index header is loaded */ + int eMode2 = eMode; /* Mode to pass to walCheckpoint() */ assert( pWal->ckptLock==0 ); + assert( pWal->writeLock==0 ); + if( pWal->readOnly ) return SQLITE_READONLY; WALTRACE(("WAL%p: checkpoint begins\n", pWal)); rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1); if( rc ){ @@ -44085,11 +46433,45 @@ SQLITE_PRIVATE int sqlite3WalCheckpoint( } pWal->ckptLock = 1; + /* If this is a blocking-checkpoint, then obtain the write-lock as well + ** to prevent any writers from running while the checkpoint is underway. + ** This has to be done before the call to walIndexReadHdr() below. + ** + ** If the writer lock cannot be obtained, then a passive checkpoint is + ** run instead. Since the checkpointer is not holding the writer lock, + ** there is no point in blocking waiting for any readers. Assuming no + ** other error occurs, this function will return SQLITE_BUSY to the caller. + */ + if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){ + rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_WRITE_LOCK, 1); + if( rc==SQLITE_OK ){ + pWal->writeLock = 1; + }else if( rc==SQLITE_BUSY ){ + eMode2 = SQLITE_CHECKPOINT_PASSIVE; + rc = SQLITE_OK; + } + } + + /* Read the wal-index header. */ + if( rc==SQLITE_OK ){ + rc = walIndexReadHdr(pWal, &isChanged); + } + /* Copy data from the log to the database file. */ - rc = walIndexReadHdr(pWal, &isChanged); if( rc==SQLITE_OK ){ - rc = walCheckpoint(pWal, sync_flags, nBuf, zBuf); + if( pWal->hdr.mxFrame && walPagesize(pWal)!=nBuf ){ + rc = SQLITE_CORRUPT_BKPT; + }else{ + rc = walCheckpoint(pWal, eMode2, xBusy, pBusyArg, sync_flags, zBuf); + } + + /* If no error occurred, set the output variables. */ + if( rc==SQLITE_OK || rc==SQLITE_BUSY ){ + if( pnLog ) *pnLog = (int)pWal->hdr.mxFrame; + if( pnCkpt ) *pnCkpt = (int)(walCkptInfo(pWal)->nBackfill); + } } + if( isChanged ){ /* If a new wal-index header was loaded before the checkpoint was ** performed, then the pager-cache associated with pWal is now @@ -44101,10 +46483,11 @@ SQLITE_PRIVATE int sqlite3WalCheckpoint( } /* Release the locks. */ + sqlite3WalEndWriteTransaction(pWal); walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1); pWal->ckptLock = 0; WALTRACE(("WAL%p: checkpoint %s\n", pWal, rc ? "failed" : "ok")); - return rc; + return (rc==SQLITE_OK && eMode!=eMode2 ? SQLITE_BUSY : rc); } /* Return the value to pass to a sqlite3_wal_hook callback, the @@ -44433,7 +46816,7 @@ SQLITE_PRIVATE int sqlite3WalHeapMemory(Wal *pWal){ /* The following value is the maximum cell size assuming a maximum page ** size give above. */ -#define MX_CELL_SIZE(pBt) (pBt->pageSize-8) +#define MX_CELL_SIZE(pBt) ((int)(pBt->pageSize-8)) /* The maximum number of cells on a single page of the database. This ** assumes a minimum cell size of 6 bytes (4 bytes for the cell itself @@ -44551,7 +46934,7 @@ struct BtLock { ** All fields in this structure are accessed under sqlite3.mutex. ** The pBt pointer itself may not be changed while there exists cursors ** in the referenced BtShared that point back to this Btree since those -** cursors have to do go through this Btree to find their BtShared and +** cursors have to go through this Btree to find their BtShared and ** they often do so without holding sqlite3.mutex. */ struct Btree { @@ -44629,19 +47012,19 @@ struct BtShared { u8 autoVacuum; /* True if auto-vacuum is enabled */ u8 incrVacuum; /* True if incr-vacuum is enabled */ #endif + u8 inTransaction; /* Transaction state */ + u8 doNotUseWAL; /* If true, do not open write-ahead-log file */ u16 maxLocal; /* Maximum local payload in non-LEAFDATA tables */ u16 minLocal; /* Minimum local payload in non-LEAFDATA tables */ u16 maxLeaf; /* Maximum local payload in a LEAFDATA table */ u16 minLeaf; /* Minimum local payload in a LEAFDATA table */ - u8 inTransaction; /* Transaction state */ - u8 doNotUseWAL; /* If true, do not open write-ahead-log file */ u32 pageSize; /* Total number of bytes on a page */ u32 usableSize; /* Number of usable bytes on each page */ int nTransaction; /* Number of open transactions (read + write) */ u32 nPage; /* Number of pages in the database */ void *pSchema; /* Pointer to space allocated by sqlite3BtreeSchema() */ void (*xFreeSchema)(void*); /* Destructor for BtShared.pSchema */ - sqlite3_mutex *mutex; /* Non-recursive mutex required to access this struct */ + sqlite3_mutex *mutex; /* Non-recursive mutex required to access this object */ Bitvec *pHasContent; /* Set of pages moved to free-list this transaction */ #ifndef SQLITE_OMIT_SHARED_CACHE int nRef; /* Number of references to this structure */ @@ -44661,8 +47044,8 @@ struct BtShared { */ typedef struct CellInfo CellInfo; struct CellInfo { - u8 *pCell; /* Pointer to the start of cell content */ i64 nKey; /* The key for INTKEY tables, or number of bytes in key */ + u8 *pCell; /* Pointer to the start of cell content */ u32 nData; /* Number of bytes of data */ u32 nPayload; /* Total amount of payload */ u16 nHeader; /* Size of the cell content header in bytes */ @@ -44704,20 +47087,20 @@ struct BtCursor { Pgno pgnoRoot; /* The root page of this tree */ sqlite3_int64 cachedRowid; /* Next rowid cache. 0 means not valid */ CellInfo info; /* A parse of the cell we are pointing at */ + i64 nKey; /* Size of pKey, or last integer key */ + void *pKey; /* Saved key that was cursor's last known position */ + int skipNext; /* Prev() is noop if negative. Next() is noop if positive */ u8 wrFlag; /* True if writable */ u8 atLast; /* Cursor pointing to the last entry */ u8 validNKey; /* True if info.nKey is valid */ u8 eState; /* One of the CURSOR_XXX constants (see below) */ - void *pKey; /* Saved key that was cursor's last known position */ - i64 nKey; /* Size of pKey, or last integer key */ - int skipNext; /* Prev() is noop if negative. Next() is noop if positive */ #ifndef SQLITE_OMIT_INCRBLOB - u8 isIncrblobHandle; /* True if this cursor is an incr. io handle */ Pgno *aOverflow; /* Cache of overflow page locations */ + u8 isIncrblobHandle; /* True if this cursor is an incr. io handle */ #endif i16 iPage; /* Index of current page in apPage */ - MemPage *apPage[BTCURSOR_MAX_DEPTH]; /* Pages from root to current page */ u16 aiIdx[BTCURSOR_MAX_DEPTH]; /* Current index in apPage[i] */ + MemPage *apPage[BTCURSOR_MAX_DEPTH]; /* Pages from root to current page */ }; /* @@ -44882,12 +47265,13 @@ static void lockBtreeMutex(Btree *p){ ** clear the p->locked boolean. */ static void unlockBtreeMutex(Btree *p){ + BtShared *pBt = p->pBt; assert( p->locked==1 ); - assert( sqlite3_mutex_held(p->pBt->mutex) ); + assert( sqlite3_mutex_held(pBt->mutex) ); assert( sqlite3_mutex_held(p->db->mutex) ); - assert( p->db==p->pBt->db ); + assert( p->db==pBt->db ); - sqlite3_mutex_leave(p->pBt->mutex); + sqlite3_mutex_leave(pBt->mutex); p->locked = 0; } @@ -45028,30 +47412,11 @@ SQLITE_PRIVATE void sqlite3BtreeLeaveCursor(BtCursor *pCur){ */ SQLITE_PRIVATE void sqlite3BtreeEnterAll(sqlite3 *db){ int i; - Btree *p, *pLater; + Btree *p; assert( sqlite3_mutex_held(db->mutex) ); for(i=0; i<db->nDb; i++){ p = db->aDb[i].pBt; - assert( !p || (p->locked==0 && p->sharable) || p->pBt->db==p->db ); - if( p && p->sharable ){ - p->wantToLock++; - if( !p->locked ){ - assert( p->wantToLock==1 ); - while( p->pPrev ) p = p->pPrev; - /* Reason for ALWAYS: There must be at least on unlocked Btree in - ** the chain. Otherwise the !p->locked test above would have failed */ - while( p->locked && ALWAYS(p->pNext) ) p = p->pNext; - for(pLater = p->pNext; pLater; pLater=pLater->pNext){ - if( pLater->locked ){ - unlockBtreeMutex(pLater); - } - } - while( p ){ - lockBtreeMutex(p); - p = p->pNext; - } - } - } + if( p ) sqlite3BtreeEnter(p); } } SQLITE_PRIVATE void sqlite3BtreeLeaveAll(sqlite3 *db){ @@ -45060,16 +47425,18 @@ SQLITE_PRIVATE void sqlite3BtreeLeaveAll(sqlite3 *db){ assert( sqlite3_mutex_held(db->mutex) ); for(i=0; i<db->nDb; i++){ p = db->aDb[i].pBt; - if( p && p->sharable ){ - assert( p->wantToLock>0 ); - p->wantToLock--; - if( p->wantToLock==0 ){ - unlockBtreeMutex(p); - } - } + if( p ) sqlite3BtreeLeave(p); } } +/* +** Return true if a particular Btree requires a lock. Return FALSE if +** no lock is ever required since it is not sharable. +*/ +SQLITE_PRIVATE int sqlite3BtreeSharable(Btree *p){ + return p->sharable; +} + #ifndef NDEBUG /* ** Return true if the current thread holds the database connection @@ -45094,97 +47461,42 @@ SQLITE_PRIVATE int sqlite3BtreeHoldsAllMutexes(sqlite3 *db){ } #endif /* NDEBUG */ +#ifndef NDEBUG /* -** Add a new Btree pointer to a BtreeMutexArray. -** if the pointer can possibly be shared with -** another database connection. +** Return true if the correct mutexes are held for accessing the +** db->aDb[iDb].pSchema structure. The mutexes required for schema +** access are: ** -** The pointers are kept in sorted order by pBtree->pBt. That -** way when we go to enter all the mutexes, we can enter them -** in order without every having to backup and retry and without -** worrying about deadlock. +** (1) The mutex on db +** (2) if iDb!=1, then the mutex on db->aDb[iDb].pBt. ** -** The number of shared btrees will always be small (usually 0 or 1) -** so an insertion sort is an adequate algorithm here. +** If pSchema is not NULL, then iDb is computed from pSchema and +** db using sqlite3SchemaToIndex(). */ -SQLITE_PRIVATE void sqlite3BtreeMutexArrayInsert(BtreeMutexArray *pArray, Btree *pBtree){ - int i, j; - BtShared *pBt; - if( pBtree==0 || pBtree->sharable==0 ) return; -#ifndef NDEBUG - { - for(i=0; i<pArray->nMutex; i++){ - assert( pArray->aBtree[i]!=pBtree ); - } - } -#endif - assert( pArray->nMutex>=0 ); - assert( pArray->nMutex<ArraySize(pArray->aBtree)-1 ); - pBt = pBtree->pBt; - for(i=0; i<pArray->nMutex; i++){ - assert( pArray->aBtree[i]!=pBtree ); - if( pArray->aBtree[i]->pBt>pBt ){ - for(j=pArray->nMutex; j>i; j--){ - pArray->aBtree[j] = pArray->aBtree[j-1]; - } - pArray->aBtree[i] = pBtree; - pArray->nMutex++; - return; - } - } - pArray->aBtree[pArray->nMutex++] = pBtree; +SQLITE_PRIVATE int sqlite3SchemaMutexHeld(sqlite3 *db, int iDb, Schema *pSchema){ + Btree *p; + assert( db!=0 ); + if( pSchema ) iDb = sqlite3SchemaToIndex(db, pSchema); + assert( iDb>=0 && iDb<db->nDb ); + if( !sqlite3_mutex_held(db->mutex) ) return 0; + if( iDb==1 ) return 1; + p = db->aDb[iDb].pBt; + assert( p!=0 ); + return p->sharable==0 || p->locked==1; } +#endif /* NDEBUG */ +#else /* SQLITE_THREADSAFE>0 above. SQLITE_THREADSAFE==0 below */ /* -** Enter the mutex of every btree in the array. This routine is -** called at the beginning of sqlite3VdbeExec(). The mutexes are -** exited at the end of the same function. +** The following are special cases for mutex enter routines for use +** in single threaded applications that use shared cache. Except for +** these two routines, all mutex operations are no-ops in that case and +** are null #defines in btree.h. +** +** If shared cache is disabled, then all btree mutex routines, including +** the ones below, are no-ops and are null #defines in btree.h. */ -SQLITE_PRIVATE void sqlite3BtreeMutexArrayEnter(BtreeMutexArray *pArray){ - int i; - for(i=0; i<pArray->nMutex; i++){ - Btree *p = pArray->aBtree[i]; - /* Some basic sanity checking */ - assert( i==0 || pArray->aBtree[i-1]->pBt<p->pBt ); - assert( !p->locked || p->wantToLock>0 ); - /* We should already hold a lock on the database connection */ - assert( sqlite3_mutex_held(p->db->mutex) ); - - /* The Btree is sharable because only sharable Btrees are entered - ** into the array in the first place. */ - assert( p->sharable ); - - p->wantToLock++; - if( !p->locked ){ - lockBtreeMutex(p); - } - } -} - -/* -** Leave the mutex of every btree in the group. -*/ -SQLITE_PRIVATE void sqlite3BtreeMutexArrayLeave(BtreeMutexArray *pArray){ - int i; - for(i=0; i<pArray->nMutex; i++){ - Btree *p = pArray->aBtree[i]; - /* Some basic sanity checking */ - assert( i==0 || pArray->aBtree[i-1]->pBt<p->pBt ); - assert( p->locked ); - assert( p->wantToLock>0 ); - - /* We should already hold a lock on the database connection */ - assert( sqlite3_mutex_held(p->db->mutex) ); - - p->wantToLock--; - if( p->wantToLock==0 ){ - unlockBtreeMutex(p); - } - } -} - -#else SQLITE_PRIVATE void sqlite3BtreeEnter(Btree *p){ p->pBt->db = p->db; } @@ -45991,6 +48303,7 @@ static void ptrmapPut(BtShared *pBt, Pgno key, u8 eType, Pgno parent, int *pRC){ *pRC = SQLITE_CORRUPT_BKPT; goto ptrmap_exit; } + assert( offset <= (int)pBt->usableSize-5 ); pPtrmap = (u8 *)sqlite3PagerGetData(pDbPage); if( eType!=pPtrmap[offset] || get4byte(&pPtrmap[offset+1])!=parent ){ @@ -46030,6 +48343,11 @@ static int ptrmapGet(BtShared *pBt, Pgno key, u8 *pEType, Pgno *pPgno){ pPtrmap = (u8 *)sqlite3PagerGetData(pDbPage); offset = PTRMAP_PTROFFSET(iPtrmap, key); + if( offset<0 ){ + sqlite3PagerUnref(pDbPage); + return SQLITE_CORRUPT_BKPT; + } + assert( offset <= (int)pBt->usableSize-5 ); assert( pEType!=0 ); *pEType = pPtrmap[offset]; if( pPgno ) *pPgno = get4byte(&pPtrmap[offset+1]); @@ -46054,6 +48372,8 @@ static int ptrmapGet(BtShared *pBt, Pgno key, u8 *pEType, Pgno *pPgno){ */ #define findCell(P,I) \ ((P)->aData + ((P)->maskPage & get2byte(&(P)->aData[(P)->cellOffset+2*(I)]))) +#define findCellv2(D,M,O,I) (D+(M&get2byte(D+(O+2*(I))))) + /* ** This a more complex version of findCell() that works for @@ -46121,14 +48441,9 @@ static void btreeParseCellPtr( /* This is the (easy) common case where the entire payload fits ** on the local page. No overflow is required. */ - int nSize; /* Total size of cell content in bytes */ - nSize = nPayload + n; + if( (pInfo->nSize = (u16)(n+nPayload))<4 ) pInfo->nSize = 4; pInfo->nLocal = (u16)nPayload; pInfo->iOverflow = 0; - if( (nSize & ~3)==0 ){ - nSize = 4; /* Minimum cell size is 4 */ - } - pInfo->nSize = (u16)nSize; }else{ /* If the payload will not fit completely on the local page, we have ** to decide how much to store locally and how much to spill onto @@ -46436,7 +48751,7 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){ */ top -= nByte; put2byte(&data[hdr+5], top); - assert( top+nByte <= pPage->pBt->usableSize ); + assert( top+nByte <= (int)pPage->pBt->usableSize ); *pIdx = top; return SQLITE_OK; } @@ -46457,7 +48772,7 @@ static int freeSpace(MemPage *pPage, int start, int size){ assert( pPage->pBt!=0 ); assert( sqlite3PagerIswriteable(pPage->pDbPage) ); assert( start>=pPage->hdrOffset+6+pPage->childPtrSize ); - assert( (start + size)<=pPage->pBt->usableSize ); + assert( (start + size) <= (int)pPage->pBt->usableSize ); assert( sqlite3_mutex_held(pPage->pBt->mutex) ); assert( size>=0 ); /* Minimum cell size is 4 */ @@ -46500,7 +48815,7 @@ static int freeSpace(MemPage *pPage, int start, int size){ while( (pbegin = get2byte(&data[addr]))>0 ){ int pnext, psize, x; assert( pbegin>addr ); - assert( pbegin<=pPage->pBt->usableSize-4 ); + assert( pbegin <= (int)pPage->pBt->usableSize-4 ); pnext = get2byte(&data[pbegin]); psize = get2byte(&data[pbegin+2]); if( pbegin + psize + 3 >= pnext && pnext>0 ){ @@ -46896,13 +49211,13 @@ static int btreeInvokeBusyHandler(void *pArg){ ** to problems with locking. */ SQLITE_PRIVATE int sqlite3BtreeOpen( + sqlite3_vfs *pVfs, /* VFS to use for this b-tree */ const char *zFilename, /* Name of the file containing the BTree database */ sqlite3 *db, /* Associated database handle */ Btree **ppBtree, /* Pointer to new Btree object written here */ int flags, /* Options */ int vfsFlags /* Flags passed through to sqlite3_vfs.xOpen() */ ){ - sqlite3_vfs *pVfs; /* The VFS to use for this btree */ BtShared *pBt = 0; /* Shared part of btree structure */ Btree *p; /* Handle to return */ sqlite3_mutex *mutexOpen = 0; /* Prevents a race condition. Ticket #3537 */ @@ -46924,6 +49239,7 @@ SQLITE_PRIVATE int sqlite3BtreeOpen( #endif assert( db!=0 ); + assert( pVfs!=0 ); assert( sqlite3_mutex_held(db->mutex) ); assert( (flags&0xff)==flags ); /* flags fit in 8 bits */ @@ -46942,7 +49258,6 @@ SQLITE_PRIVATE int sqlite3BtreeOpen( if( (vfsFlags & SQLITE_OPEN_MAIN_DB)!=0 && (isMemdb || isTempDb) ){ vfsFlags = (vfsFlags & ~SQLITE_OPEN_MAIN_DB) | SQLITE_OPEN_TEMP_DB; } - pVfs = db->pVfs; p = sqlite3MallocZero(sizeof(Btree)); if( !p ){ return SQLITE_NOMEM; @@ -47335,7 +49650,6 @@ SQLITE_PRIVATE int sqlite3BtreeSyncDisabled(Btree *p){ return rc; } -#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) || !defined(SQLITE_OMIT_VACUUM) /* ** Change the default pages size and the number of reserved bytes per page. ** Or, if the page size has already been fixed, return SQLITE_READONLY @@ -47390,6 +49704,7 @@ SQLITE_PRIVATE int sqlite3BtreeGetPageSize(Btree *p){ return p->pBt->pageSize; } +#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) || !defined(SQLITE_OMIT_VACUUM) /* ** Return the number of bytes of space at the end of every page that ** are intentually left unused. This is the "reserved" space that is @@ -47589,7 +49904,7 @@ static int lockBtree(BtShared *pBt){ pageSize-usableSize); return rc; } - if( nPageHeader>nPageFile ){ + if( (pBt->db->flags & SQLITE_RecoveryMode)==0 && nPage>nPageFile ){ rc = SQLITE_CORRUPT_BKPT; goto page1_init_failed; } @@ -48368,10 +50683,21 @@ static void btreeEndTransaction(Btree *p){ ** the rollback journal (which causes the transaction to commit) and ** drop locks. ** +** Normally, if an error occurs while the pager layer is attempting to +** finalize the underlying journal file, this function returns an error and +** the upper layer will attempt a rollback. However, if the second argument +** is non-zero then this b-tree transaction is part of a multi-file +** transaction. In this case, the transaction has already been committed +** (by deleting a master journal file) and the caller will ignore this +** functions return code. So, even if an error occurs in the pager layer, +** reset the b-tree objects internal state to indicate that the write +** transaction has been closed. This is quite safe, as the pager will have +** transitioned to the error state. +** ** This will release the write lock on the database file. If there ** are no active cursors, it also releases the read lock. */ -SQLITE_PRIVATE int sqlite3BtreeCommitPhaseTwo(Btree *p){ +SQLITE_PRIVATE int sqlite3BtreeCommitPhaseTwo(Btree *p, int bCleanup){ if( p->inTrans==TRANS_NONE ) return SQLITE_OK; sqlite3BtreeEnter(p); @@ -48386,7 +50712,7 @@ SQLITE_PRIVATE int sqlite3BtreeCommitPhaseTwo(Btree *p){ assert( pBt->inTransaction==TRANS_WRITE ); assert( pBt->nTransaction>0 ); rc = sqlite3PagerCommitPhaseTwo(pBt->pPager); - if( rc!=SQLITE_OK ){ + if( rc!=SQLITE_OK && bCleanup==0 ){ sqlite3BtreeLeave(p); return rc; } @@ -48406,7 +50732,7 @@ SQLITE_PRIVATE int sqlite3BtreeCommit(Btree *p){ sqlite3BtreeEnter(p); rc = sqlite3BtreeCommitPhaseOne(p, 0); if( rc==SQLITE_OK ){ - rc = sqlite3BtreeCommitPhaseTwo(p); + rc = sqlite3BtreeCommitPhaseTwo(p, 0); } sqlite3BtreeLeave(p); return rc; @@ -49642,7 +51968,7 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked( } assert( pCur->apPage[0]->intKey || pIdxKey ); for(;;){ - int lwr, upr; + int lwr, upr, idx; Pgno chldPg; MemPage *pPage = pCur->apPage[pCur->iPage]; int c; @@ -49658,14 +51984,14 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked( lwr = 0; upr = pPage->nCell-1; if( biasRight ){ - pCur->aiIdx[pCur->iPage] = (u16)upr; + pCur->aiIdx[pCur->iPage] = (u16)(idx = upr); }else{ - pCur->aiIdx[pCur->iPage] = (u16)((upr+lwr)/2); + pCur->aiIdx[pCur->iPage] = (u16)(idx = (upr+lwr)/2); } for(;;){ - int idx = pCur->aiIdx[pCur->iPage]; /* Index of current cell in pPage */ u8 *pCell; /* Pointer to current cell in pPage */ + assert( idx==pCur->aiIdx[pCur->iPage] ); pCur->info.nSize = 0; pCell = findCell(pPage, idx) + pPage->childPtrSize; if( pPage->intKey ){ @@ -49748,7 +52074,7 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked( if( lwr>upr ){ break; } - pCur->aiIdx[pCur->iPage] = (u16)((lwr+upr)/2); + pCur->aiIdx[pCur->iPage] = (u16)(idx = (lwr+upr)/2); } assert( lwr==upr+1 ); assert( pPage->isInit ); @@ -50016,7 +52342,7 @@ static int allocateBtreePage( goto end_allocate_page; } - k = get4byte(&pTrunk->aData[4]); + k = get4byte(&pTrunk->aData[4]); /* # of leaves on this trunk page */ if( k==0 && !searchList ){ /* The trunk has no leaves and the list is not being searched. ** So extract the trunk page itself and use it as the newly @@ -50101,19 +52427,13 @@ static int allocateBtreePage( u32 closest; Pgno iPage; unsigned char *aData = pTrunk->aData; - rc = sqlite3PagerWrite(pTrunk->pDbPage); - if( rc ){ - goto end_allocate_page; - } if( nearby>0 ){ u32 i; int dist; closest = 0; - dist = get4byte(&aData[8]) - nearby; - if( dist<0 ) dist = -dist; + dist = sqlite3AbsInt32(get4byte(&aData[8]) - nearby); for(i=1; i<k; i++){ - int d2 = get4byte(&aData[8+i*4]) - nearby; - if( d2<0 ) d2 = -d2; + int d2 = sqlite3AbsInt32(get4byte(&aData[8+i*4]) - nearby); if( d2<dist ){ closest = i; dist = d2; @@ -50136,11 +52456,12 @@ static int allocateBtreePage( TRACE(("ALLOCATE: %d was leaf %d of %d on trunk %d" ": %d more free pages\n", *pPgno, closest+1, k, pTrunk->pgno, n-1)); + rc = sqlite3PagerWrite(pTrunk->pDbPage); + if( rc ) goto end_allocate_page; if( closest<k-1 ){ memcpy(&aData[8+closest*4], &aData[4+k*4], 4); } put4byte(&aData[4], k-1); - assert( sqlite3PagerIswriteable(pTrunk->pDbPage) ); noContent = !btreeGetHasContent(pBt, *pPgno); rc = btreeGetPage(pBt, *pPgno, ppPage, noContent); if( rc==SQLITE_OK ){ @@ -50209,6 +52530,7 @@ end_allocate_page: }else{ *ppPage = 0; } + assert( rc!=SQLITE_OK || sqlite3PagerIswriteable((*ppPage)->pDbPage) ); return rc; } @@ -50585,10 +52907,10 @@ static int fillInCell( ** "sz" must be the number of bytes in the cell. */ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){ - int i; /* Loop counter */ u32 pc; /* Offset to cell content of cell being deleted */ u8 *data; /* pPage->aData */ u8 *ptr; /* Used to move bytes around within data[] */ + u8 *endPtr; /* End of loop */ int rc; /* The return code */ int hdr; /* Beginning of the header. 0 most pages. 100 page 1 */ @@ -50613,9 +52935,11 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){ *pRC = rc; return; } - for(i=idx+1; i<pPage->nCell; i++, ptr+=2){ - ptr[0] = ptr[2]; - ptr[1] = ptr[3]; + endPtr = &data[pPage->cellOffset + 2*pPage->nCell - 2]; + assert( (SQLITE_PTR_TO_INT(ptr)&1)==0 ); /* ptr is always 2-byte aligned */ + while( ptr<endPtr ){ + *(u16*)ptr = *(u16*)&ptr[2]; + ptr += 2; } pPage->nCell--; put2byte(&data[hdr+3], pPage->nCell); @@ -50655,6 +52979,7 @@ static void insertCell( int cellOffset; /* Address of first cell pointer in data[] */ u8 *data; /* The content of the whole page */ u8 *ptr; /* Used for moving information around in data[] */ + u8 *endPtr; /* End of the loop */ int nSkip = (iChild ? 4 : 0); @@ -50698,16 +53023,19 @@ static void insertCell( /* The allocateSpace() routine guarantees the following two properties ** if it returns success */ assert( idx >= end+2 ); - assert( idx+sz <= pPage->pBt->usableSize ); + assert( idx+sz <= (int)pPage->pBt->usableSize ); pPage->nCell++; pPage->nFree -= (u16)(2 + sz); memcpy(&data[idx+nSkip], pCell+nSkip, sz-nSkip); if( iChild ){ put4byte(&data[idx], iChild); } - for(j=end, ptr=&data[j]; j>ins; j-=2, ptr-=2){ - ptr[0] = ptr[-2]; - ptr[1] = ptr[-1]; + ptr = &data[end]; + endPtr = &data[ins]; + assert( (SQLITE_PTR_TO_INT(ptr)&1)==0 ); /* ptr is always 2-byte aligned */ + while( ptr>endPtr ){ + *(u16*)ptr = *(u16*)&ptr[-2]; + ptr -= 2; } put2byte(&data[ins], idx); put2byte(&data[pPage->hdrOffset+3], pPage->nCell); @@ -50741,7 +53069,8 @@ static void assemblePage( assert( pPage->nOverflow==0 ); assert( sqlite3_mutex_held(pPage->pBt->mutex) ); - assert( nCell>=0 && nCell<=MX_CELL(pPage->pBt) && MX_CELL(pPage->pBt)<=10921); + assert( nCell>=0 && nCell<=(int)MX_CELL(pPage->pBt) + && (int)MX_CELL(pPage->pBt)<=10921); assert( sqlite3PagerIswriteable(pPage->pDbPage) ); /* Check that the page has just been zeroed by zeroPage() */ @@ -50751,10 +53080,11 @@ static void assemblePage( pCellptr = &data[pPage->cellOffset + nCell*2]; cellbody = nUsable; for(i=nCell-1; i>=0; i--){ + u16 sz = aSize[i]; pCellptr -= 2; - cellbody -= aSize[i]; + cellbody -= sz; put2byte(pCellptr, cellbody); - memcpy(&data[cellbody], apCell[i], aSize[i]); + memcpy(&data[cellbody], apCell[i], sz); } put2byte(&data[hdr+3], nCell); put2byte(&data[hdr+5], cellbody); @@ -50955,7 +53285,7 @@ static void copyNodeContent(MemPage *pFrom, MemPage *pTo, int *pRC){ assert( pFrom->isInit ); assert( pFrom->nFree>=iToHdr ); - assert( get2byte(&aFrom[iFromHdr+5])<=pBt->usableSize ); + assert( get2byte(&aFrom[iFromHdr+5]) <= (int)pBt->usableSize ); /* Copy the b-tree node content from page pFrom to page pTo. */ iData = get2byte(&aFrom[iFromHdr+5]); @@ -51208,12 +53538,24 @@ static int balance_nonroot( memcpy(pOld->aData, apOld[i]->aData, pBt->pageSize); limit = pOld->nCell+pOld->nOverflow; - for(j=0; j<limit; j++){ - assert( nCell<nMaxCells ); - apCell[nCell] = findOverflowCell(pOld, j); - szCell[nCell] = cellSizePtr(pOld, apCell[nCell]); - nCell++; - } + if( pOld->nOverflow>0 ){ + for(j=0; j<limit; j++){ + assert( nCell<nMaxCells ); + apCell[nCell] = findOverflowCell(pOld, j); + szCell[nCell] = cellSizePtr(pOld, apCell[nCell]); + nCell++; + } + }else{ + u8 *aData = pOld->aData; + u16 maskPage = pOld->maskPage; + u16 cellOffset = pOld->cellOffset; + for(j=0; j<limit; j++){ + assert( nCell<nMaxCells ); + apCell[nCell] = findCellv2(aData, maskPage, cellOffset, j); + szCell[nCell] = cellSizePtr(pOld, apCell[nCell]); + nCell++; + } + } if( i<nOld-1 && !leafData){ u16 sz = (u16)szNew[i]; u8 *pTemp; @@ -51222,7 +53564,7 @@ static int balance_nonroot( pTemp = &aSpace1[iSpace1]; iSpace1 += sz; assert( sz<=pBt->maxLocal+23 ); - assert( iSpace1<=pBt->pageSize ); + assert( iSpace1 <= (int)pBt->pageSize ); memcpy(pTemp, apDiv[i], sz); apCell[nCell] = pTemp+leafCorrection; assert( leafCorrection==0 || leafCorrection==4 ); @@ -51387,9 +53729,7 @@ static int balance_nonroot( } } if( minI>i ){ - int t; MemPage *pT; - t = apNew[i]->pgno; pT = apNew[i]; apNew[i] = apNew[minI]; apNew[minI] = pT; @@ -51468,7 +53808,7 @@ static int balance_nonroot( } iOvflSpace += sz; assert( sz<=pBt->maxLocal+23 ); - assert( iOvflSpace<=pBt->pageSize ); + assert( iOvflSpace <= (int)pBt->pageSize ); insertCell(pParent, nxDiv, pCell, sz, pTemp, pNew->pgno, &rc); if( rc!=SQLITE_OK ) goto balance_cleanup; assert( sqlite3PagerIswriteable(pParent->pDbPage) ); @@ -51913,7 +54253,7 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( rc = fillInCell(pPage, newCell, pKey, nKey, pData, nData, nZero, &szNew); if( rc ) goto end_insert; assert( szNew==cellSizePtr(pPage, newCell) ); - assert( szNew<=MX_CELL_SIZE(pBt) ); + assert( szNew <= MX_CELL_SIZE(pBt) ); idx = pCur->aiIdx[pCur->iPage]; if( loc==0 ){ u16 szOld; @@ -52053,7 +54393,7 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur){ pCell = findCell(pLeaf, pLeaf->nCell-1); nCell = cellSizePtr(pLeaf, pCell); - assert( MX_CELL_SIZE(pBt)>=nCell ); + assert( MX_CELL_SIZE(pBt) >= nCell ); allocateTempSpace(pBt); pTmp = pBt->pTmpSpace; @@ -53140,8 +55480,10 @@ SQLITE_PRIVATE int sqlite3BtreeIsInTrans(Btree *p){ ** ** Return SQLITE_LOCKED if this or any other connection has an open ** transaction on the shared-cache the argument Btree is connected to. +** +** Parameter eMode is one of SQLITE_CHECKPOINT_PASSIVE, FULL or RESTART. */ -SQLITE_PRIVATE int sqlite3BtreeCheckpoint(Btree *p){ +SQLITE_PRIVATE int sqlite3BtreeCheckpoint(Btree *p, int eMode, int *pnLog, int *pnCkpt){ int rc = SQLITE_OK; if( p ){ BtShared *pBt = p->pBt; @@ -53149,7 +55491,7 @@ SQLITE_PRIVATE int sqlite3BtreeCheckpoint(Btree *p){ if( pBt->inTransaction!=TRANS_NONE ){ rc = SQLITE_LOCKED; }else{ - rc = sqlite3PagerCheckpoint(pBt->pPager); + rc = sqlite3PagerCheckpoint(pBt->pPager, eMode, pnLog, pnCkpt); } sqlite3BtreeLeave(p); } @@ -53189,7 +55531,7 @@ SQLITE_PRIVATE int sqlite3BtreeIsInBackup(Btree *p){ ** ** Just before the shared-btree is closed, the function passed as the ** xFree argument when the memory allocation was made is invoked on the -** blob of allocated memory. This function should not call sqlite3_free() +** blob of allocated memory. The xFree function should not call sqlite3_free() ** on the memory, the btree layer does that. */ SQLITE_PRIVATE void *sqlite3BtreeSchema(Btree *p, int nBytes, void(*xFree)(void *)){ @@ -53462,6 +55804,16 @@ static Btree *findBtree(sqlite3 *pErrorDb, sqlite3 *pDb, const char *zDb){ } /* +** Attempt to set the page size of the destination to match the page size +** of the source. +*/ +static int setDestPgsz(sqlite3_backup *p){ + int rc; + rc = sqlite3BtreeSetPageSize(p->pDest,sqlite3BtreeGetPageSize(p->pSrc),-1,0); + return rc; +} + +/* ** Create an sqlite3_backup process to copy the contents of zSrcDb from ** connection handle pSrcDb to zDestDb in pDestDb. If successful, return ** a pointer to the new sqlite3_backup object. @@ -53514,10 +55866,11 @@ SQLITE_API sqlite3_backup *sqlite3_backup_init( p->iNext = 1; p->isAttached = 0; - if( 0==p->pSrc || 0==p->pDest ){ - /* One (or both) of the named databases did not exist. An error has - ** already been written into the pDestDb handle. All that is left - ** to do here is free the sqlite3_backup structure. + if( 0==p->pSrc || 0==p->pDest || setDestPgsz(p)==SQLITE_NOMEM ){ + /* One (or both) of the named databases did not exist or an OOM + ** error was hit. The error has already been written into the + ** pDestDb handle. All that is left to do here is free the + ** sqlite3_backup structure. */ sqlite3_free(p); p = 0; @@ -53552,6 +55905,10 @@ static int backupOnePage(sqlite3_backup *p, Pgno iSrcPg, const u8 *zSrcData){ int nDestPgsz = sqlite3BtreeGetPageSize(p->pDest); const int nCopy = MIN(nSrcPgsz, nDestPgsz); const i64 iEnd = (i64)iSrcPg*(i64)nSrcPgsz; +#ifdef SQLITE_HAS_CODEC + int nSrcReserve = sqlite3BtreeGetReserve(p->pSrc); + int nDestReserve = sqlite3BtreeGetReserve(p->pDest); +#endif int rc = SQLITE_OK; i64 iOff; @@ -53570,11 +55927,22 @@ static int backupOnePage(sqlite3_backup *p, Pgno iSrcPg, const u8 *zSrcData){ #ifdef SQLITE_HAS_CODEC /* Backup is not possible if the page size of the destination is changing - ** a a codec is in use. + ** and a codec is in use. */ if( nSrcPgsz!=nDestPgsz && sqlite3PagerGetCodec(pDestPager)!=0 ){ rc = SQLITE_READONLY; } + + /* Backup is not possible if the number of bytes of reserve space differ + ** between source and destination. If there is a difference, try to + ** fix the destination to agree with the source. If that is not possible, + ** then the backup cannot proceed. + */ + if( nSrcReserve!=nDestReserve ){ + u32 newPgsz = nSrcPgsz; + rc = sqlite3PagerSetPagesize(pDestPager, &newPgsz, nSrcReserve); + if( rc==SQLITE_OK && newPgsz!=nSrcPgsz ) rc = SQLITE_READONLY; + } #endif /* This loop runs once for each destination page spanned by the source @@ -53734,7 +56102,7 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){ int nDestTruncate; if( p->pDestDb ){ - sqlite3ResetInternalSchema(p->pDestDb, 0); + sqlite3ResetInternalSchema(p->pDestDb, -1); } /* Set nDestTruncate to the final number of pages in the destination @@ -53774,32 +56142,46 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){ */ const i64 iSize = (i64)pgszSrc * (i64)nSrcPage; sqlite3_file * const pFile = sqlite3PagerFile(pDestPager); + i64 iOff; + i64 iEnd; assert( pFile ); assert( (i64)nDestTruncate*(i64)pgszDest >= iSize || ( nDestTruncate==(int)(PENDING_BYTE_PAGE(p->pDest->pBt)-1) && iSize>=PENDING_BYTE && iSize<=PENDING_BYTE+pgszDest )); - if( SQLITE_OK==(rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 1)) - && SQLITE_OK==(rc = backupTruncateFile(pFile, iSize)) - && SQLITE_OK==(rc = sqlite3PagerSync(pDestPager)) + + /* This call ensures that all data required to recreate the original + ** database has been stored in the journal for pDestPager and the + ** journal synced to disk. So at this point we may safely modify + ** the database file in any way, knowing that if a power failure + ** occurs, the original database will be reconstructed from the + ** journal file. */ + rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 1); + + /* Write the extra pages and truncate the database file as required. */ + iEnd = MIN(PENDING_BYTE + pgszDest, iSize); + for( + iOff=PENDING_BYTE+pgszSrc; + rc==SQLITE_OK && iOff<iEnd; + iOff+=pgszSrc ){ - i64 iOff; - i64 iEnd = MIN(PENDING_BYTE + pgszDest, iSize); - for( - iOff=PENDING_BYTE+pgszSrc; - rc==SQLITE_OK && iOff<iEnd; - iOff+=pgszSrc - ){ - PgHdr *pSrcPg = 0; - const Pgno iSrcPg = (Pgno)((iOff/pgszSrc)+1); - rc = sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg); - if( rc==SQLITE_OK ){ - u8 *zData = sqlite3PagerGetData(pSrcPg); - rc = sqlite3OsWrite(pFile, zData, pgszSrc, iOff); - } - sqlite3PagerUnref(pSrcPg); + PgHdr *pSrcPg = 0; + const Pgno iSrcPg = (Pgno)((iOff/pgszSrc)+1); + rc = sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg); + if( rc==SQLITE_OK ){ + u8 *zData = sqlite3PagerGetData(pSrcPg); + rc = sqlite3OsWrite(pFile, zData, pgszSrc, iOff); } + sqlite3PagerUnref(pSrcPg); + } + if( rc==SQLITE_OK ){ + rc = backupTruncateFile(pFile, iSize); + } + + /* Sync the database file to disk. */ + if( rc==SQLITE_OK ){ + rc = sqlite3PagerSync(pDestPager); } }else{ rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 0); @@ -53807,7 +56189,7 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){ /* Finish committing the transaction to the destination database. */ if( SQLITE_OK==rc - && SQLITE_OK==(rc = sqlite3BtreeCommitPhaseTwo(p->pDest)) + && SQLITE_OK==(rc = sqlite3BtreeCommitPhaseTwo(p->pDest, 0)) ){ rc = SQLITE_DONE; } @@ -53821,7 +56203,7 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){ if( bCloseTrans ){ TESTONLY( int rc2 ); TESTONLY( rc2 = ) sqlite3BtreeCommitPhaseOne(p->pSrc, 0); - TESTONLY( rc2 |= ) sqlite3BtreeCommitPhaseTwo(p->pSrc); + TESTONLY( rc2 |= ) sqlite3BtreeCommitPhaseTwo(p->pSrc, 0); assert( rc2==SQLITE_OK ); } @@ -53926,7 +56308,11 @@ SQLITE_PRIVATE void sqlite3BackupUpdate(sqlite3_backup *pBackup, Pgno iPage, con ** has been modified by a transaction on the source pager. Copy ** the new data into the backup. */ - int rc = backupOnePage(p, iPage, aData); + int rc; + assert( p->pDestDb ); + sqlite3_mutex_enter(p->pDestDb->mutex); + rc = backupOnePage(p, iPage, aData); + sqlite3_mutex_leave(p->pDestDb->mutex); assert( rc!=SQLITE_BUSY && rc!=SQLITE_LOCKED ); if( rc!=SQLITE_OK ){ p->rc = rc; @@ -54369,7 +56755,7 @@ SQLITE_PRIVATE i64 sqlite3VdbeIntValue(Mem *pMem){ }else if( flags & MEM_Real ){ return doubleToInt64(pMem->r); }else if( flags & (MEM_Str|MEM_Blob) ){ - i64 value; + i64 value = 0; assert( pMem->z || pMem->n==0 ); testcase( pMem->z==0 ); sqlite3Atoi64(pMem->z, &value, pMem->n, pMem->enc); @@ -55079,11 +57465,19 @@ SQLITE_PRIVATE int sqlite3ValueFromExpr( /* This branch happens for multiple negative signs. Ex: -(-5) */ if( SQLITE_OK==sqlite3ValueFromExpr(db,pExpr->pLeft,enc,affinity,&pVal) ){ sqlite3VdbeMemNumerify(pVal); - pVal->u.i = -1 * pVal->u.i; - /* (double)-1 In case of SQLITE_OMIT_FLOATING_POINT... */ - pVal->r = (double)-1 * pVal->r; + if( pVal->u.i==SMALLEST_INT64 ){ + pVal->flags &= MEM_Int; + pVal->flags |= MEM_Real; + pVal->r = (double)LARGEST_INT64; + }else{ + pVal->u.i = -pVal->u.i; + } + pVal->r = -pVal->r; sqlite3ValueApplyAffinity(pVal, affinity, enc); } + }else if( op==TK_NULL ){ + pVal = sqlite3ValueNew(db); + if( pVal==0 ) goto no_mem; } #ifndef SQLITE_OMIT_BLOB_LITERAL else if( op==TK_BLOB ){ @@ -55310,7 +57704,6 @@ SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3){ pOp->p3 = p3; pOp->p4.p = 0; pOp->p4type = P4_NOTUSED; - p->expired = 0; #ifdef SQLITE_DEBUG pOp->zComment = 0; if( sqlite3VdbeAddopTrace ) sqlite3VdbePrintOp(0, i, &p->aOp[i]); @@ -55350,6 +57743,20 @@ SQLITE_PRIVATE int sqlite3VdbeAddOp4( } /* +** Add an OP_ParseSchema opcode. This routine is broken out from +** sqlite3VdbeAddOp4() since it needs to also local all btrees. +** +** The zWhere string must have been obtained from sqlite3_malloc(). +** This routine will take ownership of the allocated memory. +*/ +SQLITE_PRIVATE void sqlite3VdbeAddParseSchemaOp(Vdbe *p, int iDb, char *zWhere){ + int j; + int addr = sqlite3VdbeAddOp3(p, OP_ParseSchema, iDb, 0, 0); + sqlite3VdbeChangeP4(p, addr, zWhere, P4_DYNAMIC); + for(j=0; j<p->db->nDb; j++) sqlite3VdbeUsesBtree(p, j); +} + +/* ** Add an opcode that includes the p4 value as an integer. */ SQLITE_PRIVATE int sqlite3VdbeAddOp4Int( @@ -55562,7 +57969,7 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){ pOp->opflags = sqlite3OpcodeProperty[opcode]; if( opcode==OP_Function || opcode==OP_AggStep ){ if( pOp->p5>nMaxArgs ) nMaxArgs = pOp->p5; - }else if( opcode==OP_Transaction && pOp->p2!=0 ){ + }else if( (opcode==OP_Transaction && pOp->p2!=0) || opcode==OP_Vacuum ){ p->readOnly = 0; #ifndef SQLITE_OMIT_VIRTUALTABLE }else if( opcode==OP_VUpdate ){ @@ -55611,7 +58018,7 @@ SQLITE_PRIVATE VdbeOp *sqlite3VdbeTakeOpArray(Vdbe *p, int *pnOp, int *pnMaxArg) assert( aOp && !p->db->mallocFailed ); /* Check that sqlite3VdbeUsesBtree() was not called on this VM */ - assert( p->aMutex.nMutex==0 ); + assert( p->btreeMask==0 ); resolveP2Values(p, pnMaxArg); *pnOp = p->nOp; @@ -55713,6 +58120,7 @@ SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe *p, u8 val){ ** the address of the next instruction to be coded. */ SQLITE_PRIVATE void sqlite3VdbeJumpHere(Vdbe *p, int addr){ + assert( addr>=0 ); sqlite3VdbeChangeP2(p, addr, p->nOp); } @@ -56098,22 +58506,80 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){ /* ** Declare to the Vdbe that the BTree object at db->aDb[i] is used. ** -** The prepared statement has to know in advance which Btree objects -** will be used so that it can acquire mutexes on them all in sorted -** order (via sqlite3VdbeMutexArrayEnter(). Mutexes are acquired -** in order (and released in reverse order) to avoid deadlocks. +** The prepared statements need to know in advance the complete set of +** attached databases that they will be using. A mask of these databases +** is maintained in p->btreeMask and is used for locking and other purposes. */ SQLITE_PRIVATE void sqlite3VdbeUsesBtree(Vdbe *p, int i){ - int mask; - assert( i>=0 && i<p->db->nDb && i<sizeof(u32)*8 ); + assert( i>=0 && i<p->db->nDb && i<(int)sizeof(yDbMask)*8 ); assert( i<(int)sizeof(p->btreeMask)*8 ); - mask = ((u32)1)<<i; - if( (p->btreeMask & mask)==0 ){ - p->btreeMask |= mask; - sqlite3BtreeMutexArrayInsert(&p->aMutex, p->db->aDb[i].pBt); + p->btreeMask |= ((yDbMask)1)<<i; + if( i!=1 && sqlite3BtreeSharable(p->db->aDb[i].pBt) ){ + p->lockMask |= ((yDbMask)1)<<i; } } +#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE>0 +/* +** If SQLite is compiled to support shared-cache mode and to be threadsafe, +** this routine obtains the mutex associated with each BtShared structure +** that may be accessed by the VM passed as an argument. In doing so it also +** sets the BtShared.db member of each of the BtShared structures, ensuring +** that the correct busy-handler callback is invoked if required. +** +** If SQLite is not threadsafe but does support shared-cache mode, then +** sqlite3BtreeEnter() is invoked to set the BtShared.db variables +** of all of BtShared structures accessible via the database handle +** associated with the VM. +** +** If SQLite is not threadsafe and does not support shared-cache mode, this +** function is a no-op. +** +** The p->btreeMask field is a bitmask of all btrees that the prepared +** statement p will ever use. Let N be the number of bits in p->btreeMask +** corresponding to btrees that use shared cache. Then the runtime of +** this routine is N*N. But as N is rarely more than 1, this should not +** be a problem. +*/ +SQLITE_PRIVATE void sqlite3VdbeEnter(Vdbe *p){ + int i; + yDbMask mask; + sqlite3 *db; + Db *aDb; + int nDb; + if( p->lockMask==0 ) return; /* The common case */ + db = p->db; + aDb = db->aDb; + nDb = db->nDb; + for(i=0, mask=1; i<nDb; i++, mask += mask){ + if( i!=1 && (mask & p->lockMask)!=0 && ALWAYS(aDb[i].pBt!=0) ){ + sqlite3BtreeEnter(aDb[i].pBt); + } + } +} +#endif + +#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE>0 +/* +** Unlock all of the btrees previously locked by a call to sqlite3VdbeEnter(). +*/ +SQLITE_PRIVATE void sqlite3VdbeLeave(Vdbe *p){ + int i; + yDbMask mask; + sqlite3 *db; + Db *aDb; + int nDb; + if( p->lockMask==0 ) return; /* The common case */ + db = p->db; + aDb = db->aDb; + nDb = db->nDb; + for(i=0, mask=1; i<nDb; i++, mask += mask){ + if( i!=1 && (mask & p->lockMask)!=0 && ALWAYS(aDb[i].pBt!=0) ){ + sqlite3BtreeLeave(aDb[i].pBt); + } + } +} +#endif #if defined(VDBE_PROFILE) || defined(SQLITE_DEBUG) /* @@ -56480,44 +58946,88 @@ static void *allocSpace( } /* -** Prepare a virtual machine for execution. This involves things such +** Rewind the VDBE back to the beginning in preparation for +** running it. +*/ +SQLITE_PRIVATE void sqlite3VdbeRewind(Vdbe *p){ +#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) + int i; +#endif + assert( p!=0 ); + assert( p->magic==VDBE_MAGIC_INIT ); + + /* There should be at least one opcode. + */ + assert( p->nOp>0 ); + + /* Set the magic to VDBE_MAGIC_RUN sooner rather than later. */ + p->magic = VDBE_MAGIC_RUN; + +#ifdef SQLITE_DEBUG + for(i=1; i<p->nMem; i++){ + assert( p->aMem[i].db==p->db ); + } +#endif + p->pc = -1; + p->rc = SQLITE_OK; + p->errorAction = OE_Abort; + p->magic = VDBE_MAGIC_RUN; + p->nChange = 0; + p->cacheCtr = 1; + p->minWriteFileFormat = 255; + p->iStatement = 0; + p->nFkConstraint = 0; +#ifdef VDBE_PROFILE + for(i=0; i<p->nOp; i++){ + p->aOp[i].cnt = 0; + p->aOp[i].cycles = 0; + } +#endif +} + +/* +** Prepare a virtual machine for execution for the first time after +** creating the virtual machine. This involves things such ** as allocating stack space and initializing the program counter. ** After the VDBE has be prepped, it can be executed by one or more ** calls to sqlite3VdbeExec(). ** -** This is the only way to move a VDBE from VDBE_MAGIC_INIT to -** VDBE_MAGIC_RUN. +** This function may be called exact once on a each virtual machine. +** After this routine is called the VM has been "packaged" and is ready +** to run. After this routine is called, futher calls to +** sqlite3VdbeAddOp() functions are prohibited. This routine disconnects +** the Vdbe from the Parse object that helped generate it so that the +** the Vdbe becomes an independent entity and the Parse object can be +** destroyed. ** -** This function may be called more than once on a single virtual machine. -** The first call is made while compiling the SQL statement. Subsequent -** calls are made as part of the process of resetting a statement to be -** re-executed (from a call to sqlite3_reset()). The nVar, nMem, nCursor -** and isExplain parameters are only passed correct values the first time -** the function is called. On subsequent calls, from sqlite3_reset(), nVar -** is passed -1 and nMem, nCursor and isExplain are all passed zero. +** Use the sqlite3VdbeRewind() procedure to restore a virtual machine back +** to its initial state after it has been run. */ SQLITE_PRIVATE void sqlite3VdbeMakeReady( Vdbe *p, /* The VDBE */ - int nVar, /* Number of '?' see in the SQL statement */ - int nMem, /* Number of memory cells to allocate */ - int nCursor, /* Number of cursors to allocate */ - int nArg, /* Maximum number of args in SubPrograms */ - int isExplain, /* True if the EXPLAIN keywords is present */ - int usesStmtJournal /* True to set Vdbe.usesStmtJournal */ + Parse *pParse /* Parsing context */ ){ - int n; - sqlite3 *db = p->db; + sqlite3 *db; /* The database connection */ + int nVar; /* Number of parameters */ + int nMem; /* Number of VM memory registers */ + int nCursor; /* Number of cursors required */ + int nArg; /* Number of arguments in subprograms */ + int n; /* Loop counter */ + u8 *zCsr; /* Memory available for allocation */ + u8 *zEnd; /* First byte past allocated memory */ + int nByte; /* How much extra memory is needed */ assert( p!=0 ); - assert( p->magic==VDBE_MAGIC_INIT ); - - /* There should be at least one opcode. - */ assert( p->nOp>0 ); - - /* Set the magic to VDBE_MAGIC_RUN sooner rather than later. */ - p->magic = VDBE_MAGIC_RUN; - + assert( pParse!=0 ); + assert( p->magic==VDBE_MAGIC_INIT ); + db = p->db; + assert( db->mallocFailed==0 ); + nVar = pParse->nVar; + nMem = pParse->nMem; + nCursor = pParse->nTab; + nArg = pParse->nMaxArg; + /* For each cursor required, also allocate a memory cell. Memory ** cells (nMem+1-nCursor)..nMem, inclusive, will never be used by ** the vdbe program. Instead they are used to allocate space for @@ -56530,91 +59040,69 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady( nMem += nCursor; /* Allocate space for memory registers, SQL variables, VDBE cursors and - ** an array to marshal SQL function arguments in. This is only done the - ** first time this function is called for a given VDBE, not when it is - ** being called from sqlite3_reset() to reset the virtual machine. - */ - if( nVar>=0 && ALWAYS(db->mallocFailed==0) ){ - u8 *zCsr = (u8 *)&p->aOp[p->nOp]; /* Memory avaliable for alloation */ - u8 *zEnd = (u8 *)&p->aOp[p->nOpAlloc]; /* First byte past available mem */ - int nByte; /* How much extra memory needed */ - - resolveP2Values(p, &nArg); - p->usesStmtJournal = (u8)usesStmtJournal; - if( isExplain && nMem<10 ){ - nMem = 10; - } - memset(zCsr, 0, zEnd-zCsr); - zCsr += (zCsr - (u8*)0)&7; - assert( EIGHT_BYTE_ALIGNMENT(zCsr) ); - - /* Memory for registers, parameters, cursor, etc, is allocated in two - ** passes. On the first pass, we try to reuse unused space at the - ** end of the opcode array. If we are unable to satisfy all memory - ** requirements by reusing the opcode array tail, then the second - ** pass will fill in the rest using a fresh allocation. - ** - ** This two-pass approach that reuses as much memory as possible from - ** the leftover space at the end of the opcode array can significantly - ** reduce the amount of memory held by a prepared statement. - */ - do { - nByte = 0; - p->aMem = allocSpace(p->aMem, nMem*sizeof(Mem), &zCsr, zEnd, &nByte); - p->aVar = allocSpace(p->aVar, nVar*sizeof(Mem), &zCsr, zEnd, &nByte); - p->apArg = allocSpace(p->apArg, nArg*sizeof(Mem*), &zCsr, zEnd, &nByte); - p->azVar = allocSpace(p->azVar, nVar*sizeof(char*), &zCsr, zEnd, &nByte); - p->apCsr = allocSpace(p->apCsr, nCursor*sizeof(VdbeCursor*), - &zCsr, zEnd, &nByte); - if( nByte ){ - p->pFree = sqlite3DbMallocZero(db, nByte); - } - zCsr = p->pFree; - zEnd = &zCsr[nByte]; - }while( nByte && !db->mallocFailed ); + ** an array to marshal SQL function arguments in. + */ + zCsr = (u8*)&p->aOp[p->nOp]; /* Memory avaliable for allocation */ + zEnd = (u8*)&p->aOp[p->nOpAlloc]; /* First byte past end of zCsr[] */ - p->nCursor = (u16)nCursor; - if( p->aVar ){ - p->nVar = (ynVar)nVar; - for(n=0; n<nVar; n++){ - p->aVar[n].flags = MEM_Null; - p->aVar[n].db = db; - } + resolveP2Values(p, &nArg); + p->usesStmtJournal = (u8)(pParse->isMultiWrite && pParse->mayAbort); + if( pParse->explain && nMem<10 ){ + nMem = 10; + } + memset(zCsr, 0, zEnd-zCsr); + zCsr += (zCsr - (u8*)0)&7; + assert( EIGHT_BYTE_ALIGNMENT(zCsr) ); + p->expired = 0; + + /* Memory for registers, parameters, cursor, etc, is allocated in two + ** passes. On the first pass, we try to reuse unused space at the + ** end of the opcode array. If we are unable to satisfy all memory + ** requirements by reusing the opcode array tail, then the second + ** pass will fill in the rest using a fresh allocation. + ** + ** This two-pass approach that reuses as much memory as possible from + ** the leftover space at the end of the opcode array can significantly + ** reduce the amount of memory held by a prepared statement. + */ + do { + nByte = 0; + p->aMem = allocSpace(p->aMem, nMem*sizeof(Mem), &zCsr, zEnd, &nByte); + p->aVar = allocSpace(p->aVar, nVar*sizeof(Mem), &zCsr, zEnd, &nByte); + p->apArg = allocSpace(p->apArg, nArg*sizeof(Mem*), &zCsr, zEnd, &nByte); + p->azVar = allocSpace(p->azVar, nVar*sizeof(char*), &zCsr, zEnd, &nByte); + p->apCsr = allocSpace(p->apCsr, nCursor*sizeof(VdbeCursor*), + &zCsr, zEnd, &nByte); + if( nByte ){ + p->pFree = sqlite3DbMallocZero(db, nByte); } - if( p->aMem ){ - p->aMem--; /* aMem[] goes from 1..nMem */ - p->nMem = nMem; /* not from 0..nMem-1 */ - for(n=1; n<=nMem; n++){ - p->aMem[n].flags = MEM_Null; - p->aMem[n].db = db; - } + zCsr = p->pFree; + zEnd = &zCsr[nByte]; + }while( nByte && !db->mallocFailed ); + + p->nCursor = (u16)nCursor; + if( p->aVar ){ + p->nVar = (ynVar)nVar; + for(n=0; n<nVar; n++){ + p->aVar[n].flags = MEM_Null; + p->aVar[n].db = db; } } -#ifdef SQLITE_DEBUG - for(n=1; n<p->nMem; n++){ - assert( p->aMem[n].db==db ); + if( p->azVar ){ + p->nzVar = pParse->nzVar; + memcpy(p->azVar, pParse->azVar, p->nzVar*sizeof(p->azVar[0])); + memset(pParse->azVar, 0, pParse->nzVar*sizeof(pParse->azVar[0])); } -#endif - - p->pc = -1; - p->rc = SQLITE_OK; - p->errorAction = OE_Abort; - p->explain |= isExplain; - p->magic = VDBE_MAGIC_RUN; - p->nChange = 0; - p->cacheCtr = 1; - p->minWriteFileFormat = 255; - p->iStatement = 0; - p->nFkConstraint = 0; -#ifdef VDBE_PROFILE - { - int i; - for(i=0; i<p->nOp; i++){ - p->aOp[i].cnt = 0; - p->aOp[i].cycles = 0; + if( p->aMem ){ + p->aMem--; /* aMem[] goes from 1..nMem */ + p->nMem = nMem; /* not from 0..nMem-1 */ + for(n=1; n<=nMem; n++){ + p->aMem[n].flags = MEM_Null; + p->aMem[n].db = db; } } -#endif + p->explain = pParse->explain; + sqlite3VdbeRewind(p); } /* @@ -56671,7 +59159,7 @@ SQLITE_PRIVATE int sqlite3VdbeFrameRestore(VdbeFrame *pFrame){ */ static void closeAllCursors(Vdbe *p){ if( p->pFrame ){ - VdbeFrame *pFrame = p->pFrame; + VdbeFrame *pFrame; for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent); sqlite3VdbeFrameRestore(pFrame); } @@ -56857,7 +59345,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){ for(i=0; rc==SQLITE_OK && i<db->nDb; i++){ Btree *pBt = db->aDb[i].pBt; if( pBt ){ - rc = sqlite3BtreeCommitPhaseTwo(pBt); + rc = sqlite3BtreeCommitPhaseTwo(pBt, 0); } } if( rc==SQLITE_OK ){ @@ -56888,6 +59376,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){ if( !zMaster ){ return SQLITE_NOMEM; } + sqlite3FileSuffix3(zMainFile, zMaster); rc = sqlite3OsAccess(pVfs, zMaster, SQLITE_ACCESS_EXISTS, &res); }while( rc==SQLITE_OK && res ); if( rc==SQLITE_OK ){ @@ -56989,7 +59478,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){ for(i=0; i<db->nDb; i++){ Btree *pBt = db->aDb[i].pBt; if( pBt ){ - sqlite3BtreeCommitPhaseTwo(pBt); + sqlite3BtreeCommitPhaseTwo(pBt, 1); } } sqlite3EndBenignMalloc(); @@ -57102,6 +59591,15 @@ SQLITE_PRIVATE int sqlite3VdbeCloseStatement(Vdbe *p, int eOp){ db->nStatement--; p->iStatement = 0; + if( rc==SQLITE_OK ){ + if( eOp==SAVEPOINT_ROLLBACK ){ + rc = sqlite3VtabSavepoint(db, SAVEPOINT_ROLLBACK, iSavepoint); + } + if( rc==SQLITE_OK ){ + rc = sqlite3VtabSavepoint(db, SAVEPOINT_RELEASE, iSavepoint); + } + } + /* If the statement transaction is being rolled back, also restore the ** database handles deferred constraint counter to the value it had when ** the statement transaction was opened. */ @@ -57113,33 +59611,6 @@ SQLITE_PRIVATE int sqlite3VdbeCloseStatement(Vdbe *p, int eOp){ } /* -** If SQLite is compiled to support shared-cache mode and to be threadsafe, -** this routine obtains the mutex associated with each BtShared structure -** that may be accessed by the VM passed as an argument. In doing so it -** sets the BtShared.db member of each of the BtShared structures, ensuring -** that the correct busy-handler callback is invoked if required. -** -** If SQLite is not threadsafe but does support shared-cache mode, then -** sqlite3BtreeEnterAll() is invoked to set the BtShared.db variables -** of all of BtShared structures accessible via the database handle -** associated with the VM. Of course only a subset of these structures -** will be accessed by the VM, and we could use Vdbe.btreeMask to figure -** that subset out, but there is no advantage to doing so. -** -** If SQLite is not threadsafe and does not support shared-cache mode, this -** function is a no-op. -*/ -#ifndef SQLITE_OMIT_SHARED_CACHE -SQLITE_PRIVATE void sqlite3VdbeMutexArrayEnter(Vdbe *p){ -#if SQLITE_THREADSAFE - sqlite3BtreeMutexArrayEnter(&p->aMutex); -#else - sqlite3BtreeEnterAll(p->db); -#endif -} -#endif - -/* ** This function is called when a transaction opened by the database ** handle associated with the VM passed as an argument is about to be ** committed. If there are outstanding deferred foreign key constraint @@ -57211,7 +59682,7 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){ int isSpecialError; /* Set to true if a 'special' error */ /* Lock all btrees used by the statement */ - sqlite3VdbeMutexArrayEnter(p); + sqlite3VdbeEnter(p); /* Check for one of the special errors */ mrc = p->rc & 0xff; @@ -57262,17 +59733,22 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){ && db->writeVdbeCnt==(p->readOnly==0) ){ if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){ - if( sqlite3VdbeCheckFk(p, 1) ){ - sqlite3BtreeMutexArrayLeave(&p->aMutex); - return SQLITE_ERROR; + rc = sqlite3VdbeCheckFk(p, 1); + if( rc!=SQLITE_OK ){ + if( NEVER(p->readOnly) ){ + sqlite3VdbeLeave(p); + return SQLITE_ERROR; + } + rc = SQLITE_CONSTRAINT; + }else{ + /* The auto-commit flag is true, the vdbe program was successful + ** or hit an 'OR FAIL' constraint and there are no deferred foreign + ** key constraints to hold up the transaction. This means a commit + ** is required. */ + rc = vdbeCommit(db, p); } - /* The auto-commit flag is true, the vdbe program was successful - ** or hit an 'OR FAIL' constraint and there are no deferred foreign - ** key constraints to hold up the transaction. This means a commit - ** is required. */ - rc = vdbeCommit(db, p); - if( rc==SQLITE_BUSY ){ - sqlite3BtreeMutexArrayLeave(&p->aMutex); + if( rc==SQLITE_BUSY && p->readOnly ){ + sqlite3VdbeLeave(p); return SQLITE_BUSY; }else if( rc!=SQLITE_OK ){ p->rc = rc; @@ -57303,17 +59779,11 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){ ** do so. If this operation returns an error, and the current statement ** error code is SQLITE_OK or SQLITE_CONSTRAINT, then promote the ** current statement error code. - ** - ** Note that sqlite3VdbeCloseStatement() can only fail if eStatementOp - ** is SAVEPOINT_ROLLBACK. But if p->rc==SQLITE_OK then eStatementOp - ** must be SAVEPOINT_RELEASE. Hence the NEVER(p->rc==SQLITE_OK) in - ** the following code. */ if( eStatementOp ){ rc = sqlite3VdbeCloseStatement(p, eStatementOp); if( rc ){ - assert( eStatementOp==SAVEPOINT_ROLLBACK ); - if( NEVER(p->rc==SQLITE_OK) || p->rc==SQLITE_CONSTRAINT ){ + if( p->rc==SQLITE_OK || p->rc==SQLITE_CONSTRAINT ){ p->rc = rc; sqlite3DbFree(db, p->zErrMsg); p->zErrMsg = 0; @@ -57339,12 +59809,12 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){ /* Rollback or commit any schema changes that occurred. */ if( p->rc!=SQLITE_OK && db->flags&SQLITE_InternChanges ){ - sqlite3ResetInternalSchema(db, 0); + sqlite3ResetInternalSchema(db, -1); db->flags = (db->flags | SQLITE_InternChanges); } /* Release the locks */ - sqlite3BtreeMutexArrayLeave(&p->aMutex); + sqlite3VdbeLeave(p); } /* We have successfully halted and closed the VM. Record this fact. */ @@ -57370,7 +59840,7 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){ } assert( db->activeVdbeCnt>0 || db->autoCommit==0 || db->nStatement==0 ); - return SQLITE_OK; + return (p->rc==SQLITE_BUSY ? SQLITE_BUSY : SQLITE_OK); } @@ -57506,6 +59976,7 @@ SQLITE_PRIVATE void sqlite3VdbeDeleteAuxData(VdbeFunc *pVdbeFunc, int mask){ */ SQLITE_PRIVATE void sqlite3VdbeDeleteObject(sqlite3 *db, Vdbe *p){ SubProgram *pSub, *pNext; + int i; assert( p->db==0 || p->db==db ); releaseMemArray(p->aVar, p->nVar); releaseMemArray(p->aColName, p->nResColumn*COLNAME_N); @@ -57514,6 +59985,7 @@ SQLITE_PRIVATE void sqlite3VdbeDeleteObject(sqlite3 *db, Vdbe *p){ vdbeFreeOpArray(db, pSub->aOp, pSub->nOp); sqlite3DbFree(db, pSub); } + for(i=p->nzVar-1; i>=0; i--) sqlite3DbFree(db, p->azVar[i]); vdbeFreeOpArray(db, p->aOp, p->nOp); sqlite3DbFree(db, p->aLabel); sqlite3DbFree(db, p->aColName); @@ -57646,7 +60118,13 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialType(Mem *pMem, int file_format){ if( file_format>=4 && (i&1)==i ){ return 8+(u32)i; } - u = i<0 ? -i : i; + if( i<0 ){ + if( i<(-MAX_6BYTE) ) return 6; + /* Previous test prevents: u = -(-9223372036854775808) */ + u = -i; + }else{ + u = i; + } if( u<=127 ) return 1; if( u<=32767 ) return 2; if( u<=8388607 ) return 3; @@ -57953,7 +60431,7 @@ SQLITE_PRIVATE UnpackedRecord *sqlite3VdbeRecordUnpack( idx += getVarint32(&aKey[idx], serial_type); pMem->enc = pKeyInfo->enc; pMem->db = pKeyInfo->db; - pMem->flags = 0; + /* pMem->flags = 0; // sqlite3VdbeSerialGet() will set this for us */ pMem->zMalloc = 0; d += sqlite3VdbeSerialGet(&aKey[d], serial_type, pMem); pMem++; @@ -57968,6 +60446,7 @@ SQLITE_PRIVATE UnpackedRecord *sqlite3VdbeRecordUnpack( ** This routine destroys a UnpackedRecord object. */ SQLITE_PRIVATE void sqlite3VdbeDeleteUnpackedRecord(UnpackedRecord *p){ +#ifdef SQLITE_DEBUG int i; Mem *pMem; @@ -57981,6 +60460,7 @@ SQLITE_PRIVATE void sqlite3VdbeDeleteUnpackedRecord(UnpackedRecord *p){ */ if( NEVER(pMem->zMalloc) ) sqlite3VdbeMemRelease(pMem); } +#endif if( p->flags & UNPACKED_NEED_FREE ){ sqlite3DbFree(p->pKeyInfo->db, p); } @@ -58034,7 +60514,7 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompare( /* Compilers may complain that mem1.u.i is potentially uninitialized. ** We could initialize it, as shown here, to silence those complaints. - ** But in fact, mem1.u.i will never actually be used initialized, and doing + ** But in fact, mem1.u.i will never actually be used uninitialized, and doing ** the unnecessary initialization has a measurable negative performance ** impact, since this routine is a very high runner. And so, we choose ** to ignore the compiler warnings and leave this variable uninitialized. @@ -58416,7 +60896,7 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt){ Vdbe *v = (Vdbe*)pStmt; sqlite3_mutex_enter(v->db->mutex); rc = sqlite3VdbeReset(v); - sqlite3VdbeMakeReady(v, -1, 0, 0, 0, 0, 0); + sqlite3VdbeRewind(v); assert( (rc & (v->db->errMask))==rc ); rc = sqlite3ApiExit(v->db, rc); sqlite3_mutex_leave(v->db->mutex); @@ -58659,11 +61139,30 @@ static int sqlite3Step(Vdbe *p){ assert(p); if( p->magic!=VDBE_MAGIC_RUN ){ /* We used to require that sqlite3_reset() be called before retrying - ** sqlite3_step() after any error. But after 3.6.23, we changed this - ** so that sqlite3_reset() would be called automatically instead of - ** throwing the error. + ** sqlite3_step() after any error or after SQLITE_DONE. But beginning + ** with version 3.7.0, we changed this so that sqlite3_reset() would + ** be called automatically instead of throwing the SQLITE_MISUSE error. + ** This "automatic-reset" change is not technically an incompatibility, + ** since any application that receives an SQLITE_MISUSE is broken by + ** definition. + ** + ** Nevertheless, some published applications that were originally written + ** for version 3.6.23 or earlier do in fact depend on SQLITE_MISUSE + ** returns, and the so were broken by the automatic-reset change. As a + ** a work-around, the SQLITE_OMIT_AUTORESET compile-time restores the + ** legacy behavior of returning SQLITE_MISUSE for cases where the + ** previous sqlite3_step() returned something other than a SQLITE_LOCKED + ** or SQLITE_BUSY error. */ +#ifdef SQLITE_OMIT_AUTORESET + if( p->rc==SQLITE_BUSY || p->rc==SQLITE_LOCKED ){ + sqlite3_reset((sqlite3_stmt*)p); + }else{ + return SQLITE_MISUSE_BKPT; + } +#else sqlite3_reset((sqlite3_stmt*)p); +#endif } /* Check that malloc() has not failed. If it has, return early. */ @@ -58705,7 +61204,9 @@ static int sqlite3Step(Vdbe *p){ }else #endif /* SQLITE_OMIT_EXPLAIN */ { + db->vdbeExecCnt++; rc = sqlite3VdbeExec(p); + db->vdbeExecCnt--; } #ifndef SQLITE_OMIT_TRACE @@ -58753,6 +61254,14 @@ end_of_step: } /* +** The maximum number of times that a statement will try to reparse +** itself before giving up and returning SQLITE_SCHEMA. +*/ +#ifndef SQLITE_MAX_SCHEMA_RETRY +# define SQLITE_MAX_SCHEMA_RETRY 5 +#endif + +/* ** This is the top-level implementation of sqlite3_step(). Call ** sqlite3Step() to do most of the work. If a schema error occurs, ** call sqlite3Reprepare() and try again. @@ -58770,7 +61279,7 @@ SQLITE_API int sqlite3_step(sqlite3_stmt *pStmt){ db = v->db; sqlite3_mutex_enter(db->mutex); while( (rc = sqlite3Step(v))==SQLITE_SCHEMA - && cnt++ < 5 + && cnt++ < SQLITE_MAX_SCHEMA_RETRY && (rc2 = rc = sqlite3Reprepare(v))==SQLITE_OK ){ sqlite3_reset(pStmt); v->expired = 0; @@ -58975,13 +61484,11 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt){ */ static Mem *columnMem(sqlite3_stmt *pStmt, int i){ Vdbe *pVm; - int vals; Mem *pOut; pVm = (Vdbe *)pStmt; if( pVm && pVm->pResultSet!=0 && i<pVm->nResColumn && i>=0 ){ sqlite3_mutex_enter(pVm->db->mutex); - vals = sqlite3_data_count(pStmt); pOut = &pVm->pResultSet[i]; }else{ /* If the value passed as the second argument is out of range, return @@ -58999,7 +61506,11 @@ static Mem *columnMem(sqlite3_stmt *pStmt, int i){ #if defined(SQLITE_DEBUG) && defined(__GNUC__) __attribute__((aligned(8))) #endif - = {{0}, (double)0, 0, "", 0, MEM_Null, SQLITE_NULL, 0, 0, 0 }; + = {0, "", (double)0, {0}, 0, MEM_Null, SQLITE_NULL, 0, +#ifdef SQLITE_DEBUG + 0, 0, /* pScopyFrom, pFiller */ +#endif + 0, 0 }; if( pVm && ALWAYS(pVm->db) ){ sqlite3_mutex_enter(pVm->db->mutex); @@ -59459,32 +61970,6 @@ SQLITE_API int sqlite3_bind_parameter_count(sqlite3_stmt *pStmt){ } /* -** Create a mapping from variable numbers to variable names -** in the Vdbe.azVar[] array, if such a mapping does not already -** exist. -*/ -static void createVarMap(Vdbe *p){ - if( !p->okVar ){ - int j; - Op *pOp; - sqlite3_mutex_enter(p->db->mutex); - /* The race condition here is harmless. If two threads call this - ** routine on the same Vdbe at the same time, they both might end - ** up initializing the Vdbe.azVar[] array. That is a little extra - ** work but it results in the same answer. - */ - for(j=0, pOp=p->aOp; j<p->nOp; j++, pOp++){ - if( pOp->opcode==OP_Variable ){ - assert( pOp->p1>0 && pOp->p1<=p->nVar ); - p->azVar[pOp->p1-1] = pOp->p4.z; - } - } - p->okVar = 1; - sqlite3_mutex_leave(p->db->mutex); - } -} - -/* ** Return the name of a wildcard parameter. Return NULL if the index ** is out of range or if the wildcard is unnamed. ** @@ -59492,10 +61977,9 @@ static void createVarMap(Vdbe *p){ */ SQLITE_API const char *sqlite3_bind_parameter_name(sqlite3_stmt *pStmt, int i){ Vdbe *p = (Vdbe*)pStmt; - if( p==0 || i<1 || i>p->nVar ){ + if( p==0 || i<1 || i>p->nzVar ){ return 0; } - createVarMap(p); return p->azVar[i-1]; } @@ -59509,9 +61993,8 @@ SQLITE_PRIVATE int sqlite3VdbeParameterIndex(Vdbe *p, const char *zName, int nNa if( p==0 ){ return 0; } - createVarMap(p); if( zName ){ - for(i=0; i<p->nVar; i++){ + for(i=0; i<p->nzVar; i++){ const char *z = p->azVar[i]; if( z && memcmp(z,zName,nName)==0 && z[nName]==0 ){ return i+1; @@ -59662,9 +62145,12 @@ static int findNextHostParameter(const char *zSql, int *pnToken){ } /* -** Return a pointer to a string in memory obtained form sqlite3DbMalloc() which -** holds a copy of zRawSql but with host parameters expanded to their -** current bindings. +** This function returns a pointer to a nul-terminated string in memory +** obtained from sqlite3DbMalloc(). If sqlite3.vdbeExecCnt is 1, then the +** string contains a copy of zRawSql but with host parameters expanded to +** their current bindings. Or, if sqlite3.vdbeExecCnt is greater than 1, +** then the returned string holds a copy of zRawSql with "-- " prepended +** to each line of text. ** ** The calling function is responsible for making sure the memory returned ** is eventually freed. @@ -59695,63 +62181,72 @@ SQLITE_PRIVATE char *sqlite3VdbeExpandSql( sqlite3StrAccumInit(&out, zBase, sizeof(zBase), db->aLimit[SQLITE_LIMIT_LENGTH]); out.db = db; - while( zRawSql[0] ){ - n = findNextHostParameter(zRawSql, &nToken); - assert( n>0 ); - sqlite3StrAccumAppend(&out, zRawSql, n); - zRawSql += n; - assert( zRawSql[0] || nToken==0 ); - if( nToken==0 ) break; - if( zRawSql[0]=='?' ){ - if( nToken>1 ){ - assert( sqlite3Isdigit(zRawSql[1]) ); - sqlite3GetInt32(&zRawSql[1], &idx); + if( db->vdbeExecCnt>1 ){ + while( *zRawSql ){ + const char *zStart = zRawSql; + while( *(zRawSql++)!='\n' && *zRawSql ); + sqlite3StrAccumAppend(&out, "-- ", 3); + sqlite3StrAccumAppend(&out, zStart, (int)(zRawSql-zStart)); + } + }else{ + while( zRawSql[0] ){ + n = findNextHostParameter(zRawSql, &nToken); + assert( n>0 ); + sqlite3StrAccumAppend(&out, zRawSql, n); + zRawSql += n; + assert( zRawSql[0] || nToken==0 ); + if( nToken==0 ) break; + if( zRawSql[0]=='?' ){ + if( nToken>1 ){ + assert( sqlite3Isdigit(zRawSql[1]) ); + sqlite3GetInt32(&zRawSql[1], &idx); + }else{ + idx = nextIndex; + } }else{ - idx = nextIndex; - } - }else{ - assert( zRawSql[0]==':' || zRawSql[0]=='$' || zRawSql[0]=='@' ); - testcase( zRawSql[0]==':' ); - testcase( zRawSql[0]=='$' ); - testcase( zRawSql[0]=='@' ); - idx = sqlite3VdbeParameterIndex(p, zRawSql, nToken); - assert( idx>0 ); - } - zRawSql += nToken; - nextIndex = idx + 1; - assert( idx>0 && idx<=p->nVar ); - pVar = &p->aVar[idx-1]; - if( pVar->flags & MEM_Null ){ - sqlite3StrAccumAppend(&out, "NULL", 4); - }else if( pVar->flags & MEM_Int ){ - sqlite3XPrintf(&out, "%lld", pVar->u.i); - }else if( pVar->flags & MEM_Real ){ - sqlite3XPrintf(&out, "%!.15g", pVar->r); - }else if( pVar->flags & MEM_Str ){ + assert( zRawSql[0]==':' || zRawSql[0]=='$' || zRawSql[0]=='@' ); + testcase( zRawSql[0]==':' ); + testcase( zRawSql[0]=='$' ); + testcase( zRawSql[0]=='@' ); + idx = sqlite3VdbeParameterIndex(p, zRawSql, nToken); + assert( idx>0 ); + } + zRawSql += nToken; + nextIndex = idx + 1; + assert( idx>0 && idx<=p->nVar ); + pVar = &p->aVar[idx-1]; + if( pVar->flags & MEM_Null ){ + sqlite3StrAccumAppend(&out, "NULL", 4); + }else if( pVar->flags & MEM_Int ){ + sqlite3XPrintf(&out, "%lld", pVar->u.i); + }else if( pVar->flags & MEM_Real ){ + sqlite3XPrintf(&out, "%!.15g", pVar->r); + }else if( pVar->flags & MEM_Str ){ #ifndef SQLITE_OMIT_UTF16 - u8 enc = ENC(db); - if( enc!=SQLITE_UTF8 ){ - Mem utf8; - memset(&utf8, 0, sizeof(utf8)); - utf8.db = db; - sqlite3VdbeMemSetStr(&utf8, pVar->z, pVar->n, enc, SQLITE_STATIC); - sqlite3VdbeChangeEncoding(&utf8, SQLITE_UTF8); - sqlite3XPrintf(&out, "'%.*q'", utf8.n, utf8.z); - sqlite3VdbeMemRelease(&utf8); - }else + u8 enc = ENC(db); + if( enc!=SQLITE_UTF8 ){ + Mem utf8; + memset(&utf8, 0, sizeof(utf8)); + utf8.db = db; + sqlite3VdbeMemSetStr(&utf8, pVar->z, pVar->n, enc, SQLITE_STATIC); + sqlite3VdbeChangeEncoding(&utf8, SQLITE_UTF8); + sqlite3XPrintf(&out, "'%.*q'", utf8.n, utf8.z); + sqlite3VdbeMemRelease(&utf8); + }else #endif - { - sqlite3XPrintf(&out, "'%.*q'", pVar->n, pVar->z); - } - }else if( pVar->flags & MEM_Zero ){ - sqlite3XPrintf(&out, "zeroblob(%d)", pVar->u.nZero); - }else{ - assert( pVar->flags & MEM_Blob ); - sqlite3StrAccumAppend(&out, "x'", 2); - for(i=0; i<pVar->n; i++){ - sqlite3XPrintf(&out, "%02x", pVar->z[i]&0xff); + { + sqlite3XPrintf(&out, "'%.*q'", pVar->n, pVar->z); + } + }else if( pVar->flags & MEM_Zero ){ + sqlite3XPrintf(&out, "zeroblob(%d)", pVar->u.nZero); + }else{ + assert( pVar->flags & MEM_Blob ); + sqlite3StrAccumAppend(&out, "x'", 2); + for(i=0; i<pVar->n; i++){ + sqlite3XPrintf(&out, "%02x", pVar->z[i]&0xff); + } + sqlite3StrAccumAppend(&out, "'", 1); } - sqlite3StrAccumAppend(&out, "'", 1); } } return sqlite3StrAccumFinish(&out); @@ -60401,7 +62896,7 @@ SQLITE_PRIVATE int sqlite3VdbeExec( Op *pOp; /* Current operation */ int rc = SQLITE_OK; /* Value to return */ sqlite3 *db = p->db; /* The database */ - u8 resetSchemaOnFault = 0; /* Reset schema after an error if true */ + u8 resetSchemaOnFault = 0; /* Reset schema after an error if positive */ u8 encoding = ENC(db); /* The database encoding */ #ifndef SQLITE_OMIT_PROGRESS_CALLBACK int checkProgress; /* True if progress callbacks are enabled */ @@ -60414,6 +62909,7 @@ SQLITE_PRIVATE int sqlite3VdbeExec( Mem *pOut = 0; /* Output operand */ int iCompare = 0; /* Result of last OP_Compare operation */ int *aPermute = 0; /* Permutation of columns for OP_Compare */ + i64 lastRowid = db->lastRowid; /* Saved value of the last insert ROWID */ #ifdef VDBE_PROFILE u64 start; /* CPU clock count at start of opcode */ int origPc; /* Program counter at start of opcode */ @@ -60461,8 +62957,10 @@ SQLITE_PRIVATE int sqlite3VdbeExec( int n; } ag; struct OP_ShiftRight_stack_vars { - i64 a; - i64 b; + i64 iA; + u64 uA; + i64 iB; + u8 op; } ah; struct OP_Ge_stack_vars { int res; /* Result of the comparison of pIn1 against pIn3 */ @@ -60564,6 +63062,7 @@ SQLITE_PRIVATE int sqlite3VdbeExec( } au; struct OP_VerifyCookie_stack_vars { int iMeta; + int iGen; Btree *pBt; } av; struct OP_OpenWrite_stack_vars { @@ -60755,25 +63254,30 @@ SQLITE_PRIVATE int sqlite3VdbeExec( struct OP_AggFinal_stack_vars { Mem *pMem; } cc; + struct OP_Checkpoint_stack_vars { + int i; /* Loop counter */ + int aRes[3]; /* Results */ + Mem *pMem; /* Write results here */ + } cd; struct OP_JournalMode_stack_vars { Btree *pBt; /* Btree to change journal mode of */ Pager *pPager; /* Pager associated with pBt */ int eNew; /* New journal mode */ int eOld; /* The old journal mode */ const char *zFilename; /* Name of database file for pPager */ - } cd; + } ce; struct OP_IncrVacuum_stack_vars { Btree *pBt; - } ce; + } cf; struct OP_VBegin_stack_vars { VTable *pVTab; - } cf; + } cg; struct OP_VOpen_stack_vars { VdbeCursor *pCur; sqlite3_vtab_cursor *pVtabCursor; sqlite3_vtab *pVtab; sqlite3_module *pModule; - } cg; + } ch; struct OP_VFilter_stack_vars { int nArg; int iQuery; @@ -60786,23 +63290,23 @@ SQLITE_PRIVATE int sqlite3VdbeExec( int res; int i; Mem **apArg; - } ch; + } ci; struct OP_VColumn_stack_vars { sqlite3_vtab *pVtab; const sqlite3_module *pModule; Mem *pDest; sqlite3_context sContext; - } ci; + } cj; struct OP_VNext_stack_vars { sqlite3_vtab *pVtab; const sqlite3_module *pModule; int res; VdbeCursor *pCur; - } cj; + } ck; struct OP_VRename_stack_vars { sqlite3_vtab *pVtab; Mem *pName; - } ck; + } cl; struct OP_VUpdate_stack_vars { sqlite3_vtab *pVtab; sqlite3_module *pModule; @@ -60811,16 +63315,17 @@ SQLITE_PRIVATE int sqlite3VdbeExec( sqlite_int64 rowid; Mem **apArg; Mem *pX; - } cl; + } cm; struct OP_Trace_stack_vars { char *zTrace; - } cm; + char *z; + } cn; } u; /* End automatically generated code ********************************************************************/ assert( p->magic==VDBE_MAGIC_RUN ); /* sqlite3_step() verifies this */ - sqlite3VdbeMutexArrayEnter(p); + sqlite3VdbeEnter(p); if( p->rc==SQLITE_NOMEM ){ /* This happens if a malloc() inside a call to sqlite3_column_text() or ** sqlite3_column_text16() failed. */ @@ -61047,7 +63552,7 @@ case OP_Yield: { /* in1 */ /* Opcode: HaltIfNull P1 P2 P3 P4 * ** -** Check the value in register P3. If is is NULL then Halt using +** Check the value in register P3. If it is NULL then Halt using ** parameter P1, P2, and P4 as if this were a Halt instruction. If the ** value in register P3 is not NULL, then this routine is a no-op. */ @@ -61084,6 +63589,7 @@ case OP_Halt: { p->nFrame--; sqlite3VdbeSetChanges(db, p->nChange); pc = sqlite3VdbeFrameRestore(pFrame); + lastRowid = db->lastRowid; if( pOp->p2==OE_Ignore ){ /* Instruction pc is the OP_Program that invoked the sub-program ** currently being halted. If the p2 instruction of this OP_Halt @@ -61239,6 +63745,7 @@ case OP_Variable: { /* out2-prerelease */ #endif /* local variables moved into u.ab */ assert( pOp->p1>0 && pOp->p1<=p->nVar ); + assert( pOp->p4.z==0 || pOp->p4.z==p->azVar[pOp->p1-1] ); u.ab.pVar = &p->aVar[pOp->p1 - 1]; if( sqlite3VdbeMemTooBig(u.ab.pVar) ){ goto too_big; @@ -61507,19 +64014,12 @@ case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */ u.af.iA = pIn1->u.i; u.af.iB = pIn2->u.i; switch( pOp->opcode ){ - case OP_Add: u.af.iB += u.af.iA; break; - case OP_Subtract: u.af.iB -= u.af.iA; break; - case OP_Multiply: u.af.iB *= u.af.iA; break; + case OP_Add: if( sqlite3AddInt64(&u.af.iB,u.af.iA) ) goto fp_math; break; + case OP_Subtract: if( sqlite3SubInt64(&u.af.iB,u.af.iA) ) goto fp_math; break; + case OP_Multiply: if( sqlite3MulInt64(&u.af.iB,u.af.iA) ) goto fp_math; break; case OP_Divide: { if( u.af.iA==0 ) goto arithmetic_result_is_null; - /* Dividing the largest possible negative 64-bit integer (1<<63) by - ** -1 returns an integer too large to store in a 64-bit data-type. On - ** some architectures, the value overflows to (1<<63). On others, - ** a SIGFPE is issued. The following statement normalizes this - ** behavior so that all architectures behave as if integer - ** overflow occurred. - */ - if( u.af.iA==-1 && u.af.iB==SMALLEST_INT64 ) u.af.iA = 1; + if( u.af.iA==-1 && u.af.iB==SMALLEST_INT64 ) goto fp_math; u.af.iB /= u.af.iA; break; } @@ -61533,6 +64033,7 @@ case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */ pOut->u.i = u.af.iB; MemSetTypeFlag(pOut, MEM_Int); }else{ +fp_math: u.af.rA = sqlite3VdbeRealValue(pIn1); u.af.rB = sqlite3VdbeRealValue(pIn2); switch( pOp->opcode ){ @@ -61662,16 +64163,9 @@ case OP_Function: { assert( pOp[-1].opcode==OP_CollSeq ); u.ag.ctx.pColl = pOp[-1].p4.pColl; } + db->lastRowid = lastRowid; (*u.ag.ctx.pFunc->xFunc)(&u.ag.ctx, u.ag.n, u.ag.apVal); /* IMP: R-24505-23230 */ - if( db->mallocFailed ){ - /* Even though a malloc() has failed, the implementation of the - ** user function may have called an sqlite3_result_XXX() function - ** to return a value. The following call releases any resources - ** associated with such a value. - */ - sqlite3VdbeMemRelease(&u.ag.ctx.s); - goto no_mem; - } + lastRowid = db->lastRowid; /* If any auxiliary data functions have been called by this user function, ** immediately call the destructor for any non-static values. @@ -61682,6 +64176,16 @@ case OP_Function: { pOp->p4type = P4_VDBEFUNC; } + if( db->mallocFailed ){ + /* Even though a malloc() has failed, the implementation of the + ** user function may have called an sqlite3_result_XXX() function + ** to return a value. The following call releases any resources + ** associated with such a value. + */ + sqlite3VdbeMemRelease(&u.ag.ctx.s); + goto no_mem; + } + /* If the function returned an error, throw an exception */ if( u.ag.ctx.isError ){ sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(&u.ag.ctx.s)); @@ -61694,6 +64198,15 @@ case OP_Function: { if( sqlite3VdbeMemTooBig(pOut) ){ goto too_big; } + +#if 0 + /* The app-defined function has done something that as caused this + ** statement to expire. (Perhaps the function called sqlite3_exec() + ** with a CREATE TABLE statement.) + */ + if( p->expired ) rc = SQLITE_ABORT; +#endif + REGISTER_TRACE(pOp->p3, pOut); UPDATE_MAX_BLOBSIZE(pOut); break; @@ -61730,8 +64243,10 @@ case OP_BitOr: /* same as TK_BITOR, in1, in2, out3 */ case OP_ShiftLeft: /* same as TK_LSHIFT, in1, in2, out3 */ case OP_ShiftRight: { /* same as TK_RSHIFT, in1, in2, out3 */ #if 0 /* local variables moved into u.ah */ - i64 a; - i64 b; + i64 iA; + u64 uA; + i64 iB; + u8 op; #endif /* local variables moved into u.ah */ pIn1 = &aMem[pOp->p1]; @@ -61741,16 +64256,38 @@ case OP_ShiftRight: { /* same as TK_RSHIFT, in1, in2, out3 */ sqlite3VdbeMemSetNull(pOut); break; } - u.ah.a = sqlite3VdbeIntValue(pIn2); - u.ah.b = sqlite3VdbeIntValue(pIn1); - switch( pOp->opcode ){ - case OP_BitAnd: u.ah.a &= u.ah.b; break; - case OP_BitOr: u.ah.a |= u.ah.b; break; - case OP_ShiftLeft: u.ah.a <<= u.ah.b; break; - default: assert( pOp->opcode==OP_ShiftRight ); - u.ah.a >>= u.ah.b; break; + u.ah.iA = sqlite3VdbeIntValue(pIn2); + u.ah.iB = sqlite3VdbeIntValue(pIn1); + u.ah.op = pOp->opcode; + if( u.ah.op==OP_BitAnd ){ + u.ah.iA &= u.ah.iB; + }else if( u.ah.op==OP_BitOr ){ + u.ah.iA |= u.ah.iB; + }else if( u.ah.iB!=0 ){ + assert( u.ah.op==OP_ShiftRight || u.ah.op==OP_ShiftLeft ); + + /* If shifting by a negative amount, shift in the other direction */ + if( u.ah.iB<0 ){ + assert( OP_ShiftRight==OP_ShiftLeft+1 ); + u.ah.op = 2*OP_ShiftLeft + 1 - u.ah.op; + u.ah.iB = u.ah.iB>(-64) ? -u.ah.iB : 64; + } + + if( u.ah.iB>=64 ){ + u.ah.iA = (u.ah.iA>=0 || u.ah.op==OP_ShiftLeft) ? 0 : -1; + }else{ + memcpy(&u.ah.uA, &u.ah.iA, sizeof(u.ah.uA)); + if( u.ah.op==OP_ShiftLeft ){ + u.ah.uA <<= u.ah.iB; + }else{ + u.ah.uA >>= u.ah.iB; + /* Sign-extend on a right shift of a negative number */ + if( u.ah.iA<0 ) u.ah.uA |= ((((u64)0xffffffff)<<32)|0xffffffff) << (64-u.ah.iB); + } + memcpy(&u.ah.iA, &u.ah.uA, sizeof(u.ah.iA)); + } } - pOut->u.i = u.ah.a; + pOut->u.i = u.ah.iA; MemSetTypeFlag(pOut, MEM_Int); break; } @@ -61952,7 +64489,7 @@ case OP_ToReal: { /* same as TK_TO_REAL, in1 */ ** If SQLITE_NULLEQ is set in P5 then the result of comparison is always either ** true or false and is never NULL. If both operands are NULL then the result ** of comparison is false. If either operand is NULL then the result is true. -** If neither operand is NULL the the result is the same as it would be if +** If neither operand is NULL the result is the same as it would be if ** the SQLITE_NULLEQ flag were omitted from P5. */ /* Opcode: Eq P1 P2 P3 P4 P5 @@ -61964,7 +64501,7 @@ case OP_ToReal: { /* same as TK_TO_REAL, in1 */ ** If SQLITE_NULLEQ is set in P5 then the result of comparison is always either ** true or false and is never NULL. If both operands are NULL then the result ** of comparison is true. If either operand is NULL then the result is false. -** If neither operand is NULL the the result is the same as it would be if +** If neither operand is NULL the result is the same as it would be if ** the SQLITE_NULLEQ flag were omitted from P5. */ /* Opcode: Le P1 P2 P3 P4 P5 @@ -62002,7 +64539,7 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ pIn3 = &aMem[pOp->p3]; u.ai.flags1 = pIn1->flags; u.ai.flags3 = pIn3->flags; - if( (pIn1->flags | pIn3->flags)&MEM_Null ){ + if( (u.ai.flags1 | u.ai.flags3)&MEM_Null ){ /* One or both operands are NULL */ if( pOp->p5 & SQLITE_NULLEQ ){ /* If SQLITE_NULLEQ is set (which will only happen if the operator is @@ -62010,7 +64547,7 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ ** or not both operands are null. */ assert( pOp->opcode==OP_Eq || pOp->opcode==OP_Ne ); - u.ai.res = (pIn1->flags & pIn3->flags & MEM_Null)==0; + u.ai.res = (u.ai.flags1 & u.ai.flags3 & MEM_Null)==0; }else{ /* SQLITE_NULLEQ is clear and at least one operand is NULL, ** then the result is always NULL. @@ -62249,13 +64786,13 @@ case OP_BitNot: { /* same as TK_BITNOT, in1, out2 */ /* Opcode: If P1 P2 P3 * * ** -** Jump to P2 if the value in register P1 is true. The value is +** Jump to P2 if the value in register P1 is true. The value ** is considered true if it is numeric and non-zero. If the value ** in P1 is NULL then take the jump if P3 is true. */ /* Opcode: IfNot P1 P2 P3 * * ** -** Jump to P2 if the value in register P1 is False. The value is +** Jump to P2 if the value in register P1 is False. The value ** is considered true if it has a numeric value of zero. If the value ** in P1 is NULL then take the jump if P3 is true. */ @@ -62690,7 +65227,6 @@ case OP_MakeRecord: { */ u.ao.nData = 0; /* Number of bytes of data space */ u.ao.nHdr = 0; /* Number of bytes of header space */ - u.ao.nByte = 0; /* Data space required for this record */ u.ao.nZero = 0; /* Number of zero bytes at the end of the record */ u.ao.nField = pOp->p1; u.ao.zAffinity = pOp->p4.z; @@ -62837,6 +65373,17 @@ case OP_Savepoint: { }else{ u.aq.nName = sqlite3Strlen30(u.aq.zName); +#ifndef SQLITE_OMIT_VIRTUALTABLE + /* This call is Ok even if this savepoint is actually a transaction + ** savepoint (and therefore should not prompt xSavepoint()) callbacks. + ** If this is a transaction savepoint being opened, it is guaranteed + ** that the db->aVTrans[] array is empty. */ + assert( db->autoCommit==0 || db->nVTrans==0 ); + rc = sqlite3VtabSavepoint(db, SAVEPOINT_BEGIN, + db->nStatement+db->nSavepoint); + if( rc!=SQLITE_OK ) goto abort_due_to_error; +#endif + /* Create a new savepoint structure. */ u.aq.pNew = sqlite3DbMallocRaw(db, sizeof(Savepoint)+u.aq.nName+1); if( u.aq.pNew ){ @@ -62915,7 +65462,7 @@ case OP_Savepoint: { } if( u.aq.p1==SAVEPOINT_ROLLBACK && (db->flags&SQLITE_InternChanges)!=0 ){ sqlite3ExpirePreparedStatements(db); - sqlite3ResetInternalSchema(db, 0); + sqlite3ResetInternalSchema(db, -1); db->flags = (db->flags | SQLITE_InternChanges); } } @@ -62943,6 +65490,11 @@ case OP_Savepoint: { }else{ db->nDeferredCons = u.aq.pSavepoint->nDeferredCons; } + + if( !isTransaction ){ + rc = sqlite3VtabSavepoint(db, u.aq.p1, u.aq.iSavepoint); + if( rc!=SQLITE_OK ) goto abort_due_to_error; + } } } @@ -63059,7 +65611,7 @@ case OP_Transaction: { #endif /* local variables moved into u.as */ assert( pOp->p1>=0 && pOp->p1<db->nDb ); - assert( (p->btreeMask & (1<<pOp->p1))!=0 ); + assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 ); u.as.pBt = db->aDb[pOp->p1].pBt; if( u.as.pBt ){ @@ -63082,7 +65634,11 @@ case OP_Transaction: { db->nStatement++; p->iStatement = db->nSavepoint + db->nStatement; } - rc = sqlite3BtreeBeginStmt(u.as.pBt, p->iStatement); + + rc = sqlite3VtabSavepoint(db, SAVEPOINT_BEGIN, p->iStatement-1); + if( rc==SQLITE_OK ){ + rc = sqlite3BtreeBeginStmt(u.as.pBt, p->iStatement); + } /* Store the current value of the database handles deferred constraint ** counter. If the statement transaction needs to be rolled back, @@ -63117,7 +65673,7 @@ case OP_ReadCookie: { /* out2-prerelease */ assert( pOp->p3<SQLITE_N_BTREE_META ); assert( u.at.iDb>=0 && u.at.iDb<db->nDb ); assert( db->aDb[u.at.iDb].pBt!=0 ); - assert( (p->btreeMask & (1<<u.at.iDb))!=0 ); + assert( (p->btreeMask & (((yDbMask)1)<<u.at.iDb))!=0 ); sqlite3BtreeGetMeta(db->aDb[u.at.iDb].pBt, u.at.iCookie, (u32 *)&u.at.iMeta); pOut->u.i = u.at.iMeta; @@ -63140,9 +65696,10 @@ case OP_SetCookie: { /* in3 */ #endif /* local variables moved into u.au */ assert( pOp->p2<SQLITE_N_BTREE_META ); assert( pOp->p1>=0 && pOp->p1<db->nDb ); - assert( (p->btreeMask & (1<<pOp->p1))!=0 ); + assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 ); u.au.pDb = &db->aDb[pOp->p1]; assert( u.au.pDb->pBt!=0 ); + assert( sqlite3SchemaMutexHeld(db, pOp->p1, 0) ); pIn3 = &aMem[pOp->p3]; sqlite3VdbeMemIntegerify(pIn3); /* See note about index shifting on OP_ReadCookie */ @@ -63164,10 +65721,12 @@ case OP_SetCookie: { /* in3 */ break; } -/* Opcode: VerifyCookie P1 P2 * +/* Opcode: VerifyCookie P1 P2 P3 * * ** ** Check the value of global database parameter number 0 (the -** schema version) and make sure it is equal to P2. +** schema version) and make sure it is equal to P2 and that the +** generation counter on the local schema parse equals P3. +** ** P1 is the database number which is 0 for the main database file ** and 1 for the file holding temporary tables and some higher number ** for auxiliary databases. @@ -63183,17 +65742,21 @@ case OP_SetCookie: { /* in3 */ case OP_VerifyCookie: { #if 0 /* local variables moved into u.av */ int iMeta; + int iGen; Btree *pBt; #endif /* local variables moved into u.av */ + assert( pOp->p1>=0 && pOp->p1<db->nDb ); - assert( (p->btreeMask & (1<<pOp->p1))!=0 ); + assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 ); + assert( sqlite3SchemaMutexHeld(db, pOp->p1, 0) ); u.av.pBt = db->aDb[pOp->p1].pBt; if( u.av.pBt ){ sqlite3BtreeGetMeta(u.av.pBt, BTREE_SCHEMA_VERSION, (u32 *)&u.av.iMeta); + u.av.iGen = db->aDb[pOp->p1].pSchema->iGeneration; }else{ - u.av.iMeta = 0; + u.av.iGen = u.av.iMeta = 0; } - if( u.av.iMeta!=pOp->p2 ){ + if( u.av.iMeta!=pOp->p2 || u.av.iGen!=pOp->p3 ){ sqlite3DbFree(db, p->zErrMsg); p->zErrMsg = sqlite3DbStrDup(db, "database schema has changed"); /* If the schema-cookie from the database file matches the cookie @@ -63213,7 +65776,7 @@ case OP_VerifyCookie: { sqlite3ResetInternalSchema(db, pOp->p1); } - sqlite3ExpirePreparedStatements(db); + p->expired = 1; rc = SQLITE_SCHEMA; } break; @@ -63291,12 +65854,13 @@ case OP_OpenWrite: { u.aw.p2 = pOp->p2; u.aw.iDb = pOp->p3; assert( u.aw.iDb>=0 && u.aw.iDb<db->nDb ); - assert( (p->btreeMask & (1<<u.aw.iDb))!=0 ); + assert( (p->btreeMask & (((yDbMask)1)<<u.aw.iDb))!=0 ); u.aw.pDb = &db->aDb[u.aw.iDb]; u.aw.pX = u.aw.pDb->pBt; assert( u.aw.pX!=0 ); if( pOp->opcode==OP_OpenWrite ){ u.aw.wrFlag = 1; + assert( sqlite3SchemaMutexHeld(db, u.aw.iDb, 0) ); if( u.aw.pDb->pSchema->file_format < p->minWriteFileFormat ){ p->minWriteFileFormat = u.aw.pDb->pSchema->file_format; } @@ -63395,7 +65959,7 @@ case OP_OpenEphemeral: { u.ax.pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1); if( u.ax.pCx==0 ) goto no_mem; u.ax.pCx->nullRow = 1; - rc = sqlite3BtreeOpen(0, db, &u.ax.pCx->pBt, + rc = sqlite3BtreeOpen(db->pVfs, 0, db, &u.ax.pCx->pBt, BTREE_OMIT_JOURNAL | BTREE_SINGLE | pOp->p5, vfsFlags); if( rc==SQLITE_OK ){ rc = sqlite3BtreeBeginTrans(u.ax.pCx->pBt, 1); @@ -63882,7 +66446,7 @@ case OP_IsUnique: { /* jump, in3 */ /* Opcode: NotExists P1 P2 P3 * * ** -** Use the content of register P3 as a integer key. If a record +** Use the content of register P3 as an integer key. If a record ** with that key does not exist in table of P1, then jump to P2. ** If the record does exist, then fall through. The cursor is left ** pointing to the record if it exists. @@ -63960,7 +66524,7 @@ case OP_Sequence: { /* out2-prerelease */ ** If P3>0 then P3 is a register in the root frame of this VDBE that holds ** the largest previously generated record number. No new record numbers are ** allowed to be less than this value. When this value reaches its maximum, -** a SQLITE_FULL error is generated. The P3 register is updated with the ' +** an SQLITE_FULL error is generated. The P3 register is updated with the ' ** generated record number. This P3 mechanism is used to help implement the ** AUTOINCREMENT feature. */ @@ -63996,7 +66560,6 @@ case OP_NewRowid: { /* out2-prerelease */ ** and try again, up to 100 times. */ assert( u.be.pC->isTable ); - u.be.cnt = 0; #ifdef SQLITE_32BIT_ROWID # define MAX_ROWID 0x7fffffff @@ -64070,7 +66633,7 @@ case OP_NewRowid: { /* out2-prerelease */ assert( pOp->p3==0 ); /* We cannot be in random rowid mode if this is ** an AUTOINCREMENT table. */ /* on the first attempt, simply do one more than previous */ - u.be.v = db->lastRowid; + u.be.v = lastRowid; u.be.v &= (MAX_ROWID>>1); /* ensure doesn't go negative */ u.be.v++; /* ensure non-zero */ u.be.cnt = 0; @@ -64182,7 +66745,7 @@ case OP_InsertInt: { } if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++; - if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = u.bf.iKey; + if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = lastRowid = u.bf.iKey; if( u.bf.pData->flags & MEM_Null ){ u.bf.pData->z = 0; u.bf.pData->n = 0; @@ -64603,7 +67166,7 @@ case OP_Next: { /* jump */ /* Opcode: IdxInsert P1 P2 P3 * P5 ** -** Register P2 holds a SQL index key made using the +** Register P2 holds an SQL index key made using the ** MakeRecord instructions. This opcode writes that key ** into the index P1. Data for the entry is nil. ** @@ -64829,14 +67392,16 @@ case OP_Destroy: { /* out2-prerelease */ }else{ u.br.iDb = pOp->p3; assert( u.br.iCnt==1 ); - assert( (p->btreeMask & (1<<u.br.iDb))!=0 ); + assert( (p->btreeMask & (((yDbMask)1)<<u.br.iDb))!=0 ); rc = sqlite3BtreeDropTable(db->aDb[u.br.iDb].pBt, pOp->p1, &u.br.iMoved); pOut->flags = MEM_Int; pOut->u.i = u.br.iMoved; #ifndef SQLITE_OMIT_AUTOVACUUM if( rc==SQLITE_OK && u.br.iMoved!=0 ){ - sqlite3RootPageMoved(&db->aDb[u.br.iDb], u.br.iMoved, pOp->p1); - resetSchemaOnFault = 1; + sqlite3RootPageMoved(db, u.br.iDb, u.br.iMoved, pOp->p1); + /* All OP_Destroy operations occur on the same btree */ + assert( resetSchemaOnFault==0 || resetSchemaOnFault==u.br.iDb+1 ); + resetSchemaOnFault = u.br.iDb+1; } #endif } @@ -64867,7 +67432,7 @@ case OP_Clear: { #endif /* local variables moved into u.bs */ u.bs.nChange = 0; - assert( (p->btreeMask & (1<<pOp->p2))!=0 ); + assert( (p->btreeMask & (((yDbMask)1)<<pOp->p2))!=0 ); rc = sqlite3BtreeClearTable( db->aDb[pOp->p2].pBt, pOp->p1, (pOp->p3 ? &u.bs.nChange : 0) ); @@ -64914,7 +67479,7 @@ case OP_CreateTable: { /* out2-prerelease */ u.bt.pgno = 0; assert( pOp->p1>=0 && pOp->p1<db->nDb ); - assert( (p->btreeMask & (1<<pOp->p1))!=0 ); + assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 ); u.bt.pDb = &db->aDb[pOp->p1]; assert( u.bt.pDb->pBt!=0 ); if( pOp->opcode==OP_CreateTable ){ @@ -64928,14 +67493,10 @@ case OP_CreateTable: { /* out2-prerelease */ break; } -/* Opcode: ParseSchema P1 P2 * P4 * +/* Opcode: ParseSchema P1 * * P4 * ** ** Read and parse all entries from the SQLITE_MASTER table of database P1 -** that match the WHERE clause P4. P2 is the "force" flag. Always do -** the parsing if P2 is true. If P2 is false, then this routine is a -** no-op if the schema is not currently loaded. In other words, if P2 -** is false, the SQLITE_MASTER table is only parsed if the rest of the -** schema is already loaded into the symbol table. +** that match the WHERE clause P4. ** ** This opcode invokes the parser to create a new virtual machine, ** then runs the new virtual machine. It is thus a re-entrant opcode. @@ -64948,33 +67509,20 @@ case OP_ParseSchema: { InitData initData; #endif /* local variables moved into u.bu */ + /* Any prepared statement that invokes this opcode will hold mutexes + ** on every btree. This is a prerequisite for invoking + ** sqlite3InitCallback(). + */ +#ifdef SQLITE_DEBUG + for(u.bu.iDb=0; u.bu.iDb<db->nDb; u.bu.iDb++){ + assert( u.bu.iDb==1 || sqlite3BtreeHoldsMutex(db->aDb[u.bu.iDb].pBt) ); + } +#endif + u.bu.iDb = pOp->p1; assert( u.bu.iDb>=0 && u.bu.iDb<db->nDb ); - - /* If pOp->p2 is 0, then this opcode is being executed to read a - ** single row, for example the row corresponding to a new index - ** created by this VDBE, from the sqlite_master table. It only - ** does this if the corresponding in-memory schema is currently - ** loaded. Otherwise, the new index definition can be loaded along - ** with the rest of the schema when it is required. - ** - ** Although the mutex on the BtShared object that corresponds to - ** database u.bu.iDb (the database containing the sqlite_master table - ** read by this instruction) is currently held, it is necessary to - ** obtain the mutexes on all attached databases before checking if - ** the schema of u.bu.iDb is loaded. This is because, at the start of - ** the sqlite3_exec() call below, SQLite will invoke - ** sqlite3BtreeEnterAll(). If all mutexes are not already held, the - ** u.bu.iDb mutex may be temporarily released to avoid deadlock. If - ** this happens, then some other thread may delete the in-memory - ** schema of database u.bu.iDb before the SQL statement runs. The schema - ** will not be reloaded becuase the db->init.busy flag is set. This - ** can result in a "no such table: sqlite_master" or "malformed - ** database schema" error being returned to the user. - */ - assert( sqlite3BtreeHoldsMutex(db->aDb[u.bu.iDb].pBt) ); - sqlite3BtreeEnterAll(db); - if( pOp->p2 || DbHasProperty(db, u.bu.iDb, DB_SchemaLoaded) ){ + assert( DbHasProperty(db, u.bu.iDb, DB_SchemaLoaded) ); + /* Used to be a conditional */ { u.bu.zMaster = SCHEMA_TABLE(u.bu.iDb); u.bu.initData.db = db; u.bu.initData.iDb = pOp->p1; @@ -64995,7 +67543,6 @@ case OP_ParseSchema: { db->init.busy = 0; } } - sqlite3BtreeLeaveAll(db); if( rc==SQLITE_NOMEM ){ goto no_mem; } @@ -65098,7 +67645,7 @@ case OP_IntegrityCk: { } u.bv.aRoot[u.bv.j] = 0; assert( pOp->p5<db->nDb ); - assert( (p->btreeMask & (1<<pOp->p5))!=0 ); + assert( (p->btreeMask & (((yDbMask)1)<<pOp->p5))!=0 ); u.bv.z = sqlite3BtreeIntegrityCheck(db->aDb[pOp->p5].pBt, u.bv.aRoot, u.bv.nRoot, (int)u.bv.pnErr->u.i, &u.bv.nErr); sqlite3DbFree(db, u.bv.aRoot); @@ -65324,7 +67871,7 @@ case OP_Program: { /* jump */ p->nFrame++; u.by.pFrame->pParent = p->pFrame; - u.by.pFrame->lastRowid = db->lastRowid; + u.by.pFrame->lastRowid = lastRowid; u.by.pFrame->nChange = p->nChange; p->nChange = 0; p->pFrame = u.by.pFrame; @@ -65538,7 +68085,9 @@ case OP_AggStep: { sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(&u.cb.ctx.s)); rc = u.cb.ctx.isError; } + sqlite3VdbeMemRelease(&u.cb.ctx.s); + break; } @@ -65574,13 +68123,38 @@ case OP_AggFinal: { } #ifndef SQLITE_OMIT_WAL -/* Opcode: Checkpoint P1 * * * * +/* Opcode: Checkpoint P1 P2 P3 * * ** ** Checkpoint database P1. This is a no-op if P1 is not currently in -** WAL mode. +** WAL mode. Parameter P2 is one of SQLITE_CHECKPOINT_PASSIVE, FULL +** or RESTART. Write 1 or 0 into mem[P3] if the checkpoint returns +** SQLITE_BUSY or not, respectively. Write the number of pages in the +** WAL after the checkpoint into mem[P3+1] and the number of pages +** in the WAL that have been checkpointed after the checkpoint +** completes into mem[P3+2]. However on an error, mem[P3+1] and +** mem[P3+2] are initialized to -1. */ case OP_Checkpoint: { - rc = sqlite3Checkpoint(db, pOp->p1); +#if 0 /* local variables moved into u.cd */ + int i; /* Loop counter */ + int aRes[3]; /* Results */ + Mem *pMem; /* Write results here */ +#endif /* local variables moved into u.cd */ + + u.cd.aRes[0] = 0; + u.cd.aRes[1] = u.cd.aRes[2] = -1; + assert( pOp->p2==SQLITE_CHECKPOINT_PASSIVE + || pOp->p2==SQLITE_CHECKPOINT_FULL + || pOp->p2==SQLITE_CHECKPOINT_RESTART + ); + rc = sqlite3Checkpoint(db, pOp->p1, pOp->p2, &u.cd.aRes[1], &u.cd.aRes[2]); + if( rc==SQLITE_BUSY ){ + rc = SQLITE_OK; + u.cd.aRes[0] = 1; + } + for(u.cd.i=0, u.cd.pMem = &aMem[pOp->p3]; u.cd.i<3; u.cd.i++, u.cd.pMem++){ + sqlite3VdbeMemSetInt64(u.cd.pMem, (i64)u.cd.aRes[u.cd.i]); + } break; }; #endif @@ -65598,110 +68172,91 @@ case OP_Checkpoint: { ** Write a string containing the final journal-mode to register P2. */ case OP_JournalMode: { /* out2-prerelease */ -#if 0 /* local variables moved into u.cd */ +#if 0 /* local variables moved into u.ce */ Btree *pBt; /* Btree to change journal mode of */ Pager *pPager; /* Pager associated with pBt */ int eNew; /* New journal mode */ int eOld; /* The old journal mode */ const char *zFilename; /* Name of database file for pPager */ -#endif /* local variables moved into u.cd */ +#endif /* local variables moved into u.ce */ - u.cd.eNew = pOp->p3; - assert( u.cd.eNew==PAGER_JOURNALMODE_DELETE - || u.cd.eNew==PAGER_JOURNALMODE_TRUNCATE - || u.cd.eNew==PAGER_JOURNALMODE_PERSIST - || u.cd.eNew==PAGER_JOURNALMODE_OFF - || u.cd.eNew==PAGER_JOURNALMODE_MEMORY - || u.cd.eNew==PAGER_JOURNALMODE_WAL - || u.cd.eNew==PAGER_JOURNALMODE_QUERY + u.ce.eNew = pOp->p3; + assert( u.ce.eNew==PAGER_JOURNALMODE_DELETE + || u.ce.eNew==PAGER_JOURNALMODE_TRUNCATE + || u.ce.eNew==PAGER_JOURNALMODE_PERSIST + || u.ce.eNew==PAGER_JOURNALMODE_OFF + || u.ce.eNew==PAGER_JOURNALMODE_MEMORY + || u.ce.eNew==PAGER_JOURNALMODE_WAL + || u.ce.eNew==PAGER_JOURNALMODE_QUERY ); assert( pOp->p1>=0 && pOp->p1<db->nDb ); - /* This opcode is used in two places: PRAGMA journal_mode and ATTACH. - ** In PRAGMA journal_mode, the sqlite3VdbeUsesBtree() routine is called - ** when the statment is prepared and so p->aMutex.nMutex>0. All mutexes - ** are already acquired. But when used in ATTACH, sqlite3VdbeUsesBtree() - ** is not called when the statement is prepared because it requires the - ** iDb index of the database as a parameter, and the database has not - ** yet been attached so that index is unavailable. We have to wait - ** until runtime (now) to get the mutex on the newly attached database. - ** No other mutexes are required by the ATTACH command so this is safe - ** to do. - */ - assert( (p->btreeMask & (1<<pOp->p1))!=0 || p->aMutex.nMutex==0 ); - if( p->aMutex.nMutex==0 ){ - /* This occurs right after ATTACH. Get a mutex on the newly ATTACHed - ** database. */ - sqlite3VdbeUsesBtree(p, pOp->p1); - sqlite3VdbeMutexArrayEnter(p); - } - - u.cd.pBt = db->aDb[pOp->p1].pBt; - u.cd.pPager = sqlite3BtreePager(u.cd.pBt); - u.cd.eOld = sqlite3PagerGetJournalMode(u.cd.pPager); - if( u.cd.eNew==PAGER_JOURNALMODE_QUERY ) u.cd.eNew = u.cd.eOld; - if( !sqlite3PagerOkToChangeJournalMode(u.cd.pPager) ) u.cd.eNew = u.cd.eOld; + u.ce.pBt = db->aDb[pOp->p1].pBt; + u.ce.pPager = sqlite3BtreePager(u.ce.pBt); + u.ce.eOld = sqlite3PagerGetJournalMode(u.ce.pPager); + if( u.ce.eNew==PAGER_JOURNALMODE_QUERY ) u.ce.eNew = u.ce.eOld; + if( !sqlite3PagerOkToChangeJournalMode(u.ce.pPager) ) u.ce.eNew = u.ce.eOld; #ifndef SQLITE_OMIT_WAL - u.cd.zFilename = sqlite3PagerFilename(u.cd.pPager); + u.ce.zFilename = sqlite3PagerFilename(u.ce.pPager); /* Do not allow a transition to journal_mode=WAL for a database ** in temporary storage or if the VFS does not support shared memory */ - if( u.cd.eNew==PAGER_JOURNALMODE_WAL - && (u.cd.zFilename[0]==0 /* Temp file */ - || !sqlite3PagerWalSupported(u.cd.pPager)) /* No shared-memory support */ + if( u.ce.eNew==PAGER_JOURNALMODE_WAL + && (u.ce.zFilename[0]==0 /* Temp file */ + || !sqlite3PagerWalSupported(u.ce.pPager)) /* No shared-memory support */ ){ - u.cd.eNew = u.cd.eOld; + u.ce.eNew = u.ce.eOld; } - if( (u.cd.eNew!=u.cd.eOld) - && (u.cd.eOld==PAGER_JOURNALMODE_WAL || u.cd.eNew==PAGER_JOURNALMODE_WAL) + if( (u.ce.eNew!=u.ce.eOld) + && (u.ce.eOld==PAGER_JOURNALMODE_WAL || u.ce.eNew==PAGER_JOURNALMODE_WAL) ){ if( !db->autoCommit || db->activeVdbeCnt>1 ){ rc = SQLITE_ERROR; sqlite3SetString(&p->zErrMsg, db, "cannot change %s wal mode from within a transaction", - (u.cd.eNew==PAGER_JOURNALMODE_WAL ? "into" : "out of") + (u.ce.eNew==PAGER_JOURNALMODE_WAL ? "into" : "out of") ); break; }else{ - if( u.cd.eOld==PAGER_JOURNALMODE_WAL ){ + if( u.ce.eOld==PAGER_JOURNALMODE_WAL ){ /* If leaving WAL mode, close the log file. If successful, the call ** to PagerCloseWal() checkpoints and deletes the write-ahead-log ** file. An EXCLUSIVE lock may still be held on the database file ** after a successful return. */ - rc = sqlite3PagerCloseWal(u.cd.pPager); + rc = sqlite3PagerCloseWal(u.ce.pPager); if( rc==SQLITE_OK ){ - sqlite3PagerSetJournalMode(u.cd.pPager, u.cd.eNew); + sqlite3PagerSetJournalMode(u.ce.pPager, u.ce.eNew); } - }else if( u.cd.eOld==PAGER_JOURNALMODE_MEMORY ){ + }else if( u.ce.eOld==PAGER_JOURNALMODE_MEMORY ){ /* Cannot transition directly from MEMORY to WAL. Use mode OFF ** as an intermediate */ - sqlite3PagerSetJournalMode(u.cd.pPager, PAGER_JOURNALMODE_OFF); + sqlite3PagerSetJournalMode(u.ce.pPager, PAGER_JOURNALMODE_OFF); } /* Open a transaction on the database file. Regardless of the journal ** mode, this transaction always uses a rollback journal. */ - assert( sqlite3BtreeIsInTrans(u.cd.pBt)==0 ); + assert( sqlite3BtreeIsInTrans(u.ce.pBt)==0 ); if( rc==SQLITE_OK ){ - rc = sqlite3BtreeSetVersion(u.cd.pBt, (u.cd.eNew==PAGER_JOURNALMODE_WAL ? 2 : 1)); + rc = sqlite3BtreeSetVersion(u.ce.pBt, (u.ce.eNew==PAGER_JOURNALMODE_WAL ? 2 : 1)); } } } #endif /* ifndef SQLITE_OMIT_WAL */ if( rc ){ - u.cd.eNew = u.cd.eOld; + u.ce.eNew = u.ce.eOld; } - u.cd.eNew = sqlite3PagerSetJournalMode(u.cd.pPager, u.cd.eNew); + u.ce.eNew = sqlite3PagerSetJournalMode(u.ce.pPager, u.ce.eNew); pOut = &aMem[pOp->p2]; pOut->flags = MEM_Str|MEM_Static|MEM_Term; - pOut->z = (char *)sqlite3JournalModename(u.cd.eNew); + pOut->z = (char *)sqlite3JournalModename(u.ce.eNew); pOut->n = sqlite3Strlen30(pOut->z); pOut->enc = SQLITE_UTF8; sqlite3VdbeChangeEncoding(pOut, encoding); @@ -65730,14 +68285,14 @@ case OP_Vacuum: { ** P2. Otherwise, fall through to the next instruction. */ case OP_IncrVacuum: { /* jump */ -#if 0 /* local variables moved into u.ce */ +#if 0 /* local variables moved into u.cf */ Btree *pBt; -#endif /* local variables moved into u.ce */ +#endif /* local variables moved into u.cf */ assert( pOp->p1>=0 && pOp->p1<db->nDb ); - assert( (p->btreeMask & (1<<pOp->p1))!=0 ); - u.ce.pBt = db->aDb[pOp->p1].pBt; - rc = sqlite3BtreeIncrVacuum(u.ce.pBt); + assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 ); + u.cf.pBt = db->aDb[pOp->p1].pBt; + rc = sqlite3BtreeIncrVacuum(u.cf.pBt); if( rc==SQLITE_DONE ){ pc = pOp->p2 - 1; rc = SQLITE_OK; @@ -65784,7 +68339,7 @@ case OP_TableLock: { if( isWriteLock || 0==(db->flags&SQLITE_ReadUncommitted) ){ int p1 = pOp->p1; assert( p1>=0 && p1<db->nDb ); - assert( (p->btreeMask & (1<<p1))!=0 ); + assert( (p->btreeMask & (((yDbMask)1)<<p1))!=0 ); assert( isWriteLock==0 || isWriteLock==1 ); rc = sqlite3BtreeLockTable(db->aDb[p1].pBt, pOp->p2, isWriteLock); if( (rc&0xFF)==SQLITE_LOCKED ){ @@ -65807,12 +68362,12 @@ case OP_TableLock: { ** code will be set to SQLITE_LOCKED. */ case OP_VBegin: { -#if 0 /* local variables moved into u.cf */ +#if 0 /* local variables moved into u.cg */ VTable *pVTab; -#endif /* local variables moved into u.cf */ - u.cf.pVTab = pOp->p4.pVtab; - rc = sqlite3VtabBegin(db, u.cf.pVTab); - if( u.cf.pVTab ) importVtabErrMsg(p, u.cf.pVTab->pVtab); +#endif /* local variables moved into u.cg */ + u.cg.pVTab = pOp->p4.pVtab; + rc = sqlite3VtabBegin(db, u.cg.pVTab); + if( u.cg.pVTab ) importVtabErrMsg(p, u.cg.pVTab->pVtab); break; } #endif /* SQLITE_OMIT_VIRTUALTABLE */ @@ -65851,32 +68406,32 @@ case OP_VDestroy: { ** table and stores that cursor in P1. */ case OP_VOpen: { -#if 0 /* local variables moved into u.cg */ +#if 0 /* local variables moved into u.ch */ VdbeCursor *pCur; sqlite3_vtab_cursor *pVtabCursor; sqlite3_vtab *pVtab; sqlite3_module *pModule; -#endif /* local variables moved into u.cg */ +#endif /* local variables moved into u.ch */ - u.cg.pCur = 0; - u.cg.pVtabCursor = 0; - u.cg.pVtab = pOp->p4.pVtab->pVtab; - u.cg.pModule = (sqlite3_module *)u.cg.pVtab->pModule; - assert(u.cg.pVtab && u.cg.pModule); - rc = u.cg.pModule->xOpen(u.cg.pVtab, &u.cg.pVtabCursor); - importVtabErrMsg(p, u.cg.pVtab); + u.ch.pCur = 0; + u.ch.pVtabCursor = 0; + u.ch.pVtab = pOp->p4.pVtab->pVtab; + u.ch.pModule = (sqlite3_module *)u.ch.pVtab->pModule; + assert(u.ch.pVtab && u.ch.pModule); + rc = u.ch.pModule->xOpen(u.ch.pVtab, &u.ch.pVtabCursor); + importVtabErrMsg(p, u.ch.pVtab); if( SQLITE_OK==rc ){ /* Initialize sqlite3_vtab_cursor base class */ - u.cg.pVtabCursor->pVtab = u.cg.pVtab; + u.ch.pVtabCursor->pVtab = u.ch.pVtab; /* Initialise vdbe cursor object */ - u.cg.pCur = allocateCursor(p, pOp->p1, 0, -1, 0); - if( u.cg.pCur ){ - u.cg.pCur->pVtabCursor = u.cg.pVtabCursor; - u.cg.pCur->pModule = u.cg.pVtabCursor->pVtab->pModule; + u.ch.pCur = allocateCursor(p, pOp->p1, 0, -1, 0); + if( u.ch.pCur ){ + u.ch.pCur->pVtabCursor = u.ch.pVtabCursor; + u.ch.pCur->pModule = u.ch.pVtabCursor->pVtab->pModule; }else{ db->mallocFailed = 1; - u.cg.pModule->xClose(u.cg.pVtabCursor); + u.ch.pModule->xClose(u.ch.pVtabCursor); } } break; @@ -65903,7 +68458,7 @@ case OP_VOpen: { ** A jump is made to P2 if the result set after filtering would be empty. */ case OP_VFilter: { /* jump */ -#if 0 /* local variables moved into u.ch */ +#if 0 /* local variables moved into u.ci */ int nArg; int iQuery; const sqlite3_module *pModule; @@ -65915,45 +68470,45 @@ case OP_VFilter: { /* jump */ int res; int i; Mem **apArg; -#endif /* local variables moved into u.ch */ +#endif /* local variables moved into u.ci */ - u.ch.pQuery = &aMem[pOp->p3]; - u.ch.pArgc = &u.ch.pQuery[1]; - u.ch.pCur = p->apCsr[pOp->p1]; - assert( memIsValid(u.ch.pQuery) ); - REGISTER_TRACE(pOp->p3, u.ch.pQuery); - assert( u.ch.pCur->pVtabCursor ); - u.ch.pVtabCursor = u.ch.pCur->pVtabCursor; - u.ch.pVtab = u.ch.pVtabCursor->pVtab; - u.ch.pModule = u.ch.pVtab->pModule; + u.ci.pQuery = &aMem[pOp->p3]; + u.ci.pArgc = &u.ci.pQuery[1]; + u.ci.pCur = p->apCsr[pOp->p1]; + assert( memIsValid(u.ci.pQuery) ); + REGISTER_TRACE(pOp->p3, u.ci.pQuery); + assert( u.ci.pCur->pVtabCursor ); + u.ci.pVtabCursor = u.ci.pCur->pVtabCursor; + u.ci.pVtab = u.ci.pVtabCursor->pVtab; + u.ci.pModule = u.ci.pVtab->pModule; /* Grab the index number and argc parameters */ - assert( (u.ch.pQuery->flags&MEM_Int)!=0 && u.ch.pArgc->flags==MEM_Int ); - u.ch.nArg = (int)u.ch.pArgc->u.i; - u.ch.iQuery = (int)u.ch.pQuery->u.i; + assert( (u.ci.pQuery->flags&MEM_Int)!=0 && u.ci.pArgc->flags==MEM_Int ); + u.ci.nArg = (int)u.ci.pArgc->u.i; + u.ci.iQuery = (int)u.ci.pQuery->u.i; /* Invoke the xFilter method */ { - u.ch.res = 0; - u.ch.apArg = p->apArg; - for(u.ch.i = 0; u.ch.i<u.ch.nArg; u.ch.i++){ - u.ch.apArg[u.ch.i] = &u.ch.pArgc[u.ch.i+1]; - sqlite3VdbeMemStoreType(u.ch.apArg[u.ch.i]); + u.ci.res = 0; + u.ci.apArg = p->apArg; + for(u.ci.i = 0; u.ci.i<u.ci.nArg; u.ci.i++){ + u.ci.apArg[u.ci.i] = &u.ci.pArgc[u.ci.i+1]; + sqlite3VdbeMemStoreType(u.ci.apArg[u.ci.i]); } p->inVtabMethod = 1; - rc = u.ch.pModule->xFilter(u.ch.pVtabCursor, u.ch.iQuery, pOp->p4.z, u.ch.nArg, u.ch.apArg); + rc = u.ci.pModule->xFilter(u.ci.pVtabCursor, u.ci.iQuery, pOp->p4.z, u.ci.nArg, u.ci.apArg); p->inVtabMethod = 0; - importVtabErrMsg(p, u.ch.pVtab); + importVtabErrMsg(p, u.ci.pVtab); if( rc==SQLITE_OK ){ - u.ch.res = u.ch.pModule->xEof(u.ch.pVtabCursor); + u.ci.res = u.ci.pModule->xEof(u.ci.pVtabCursor); } - if( u.ch.res ){ + if( u.ci.res ){ pc = pOp->p2 - 1; } } - u.ch.pCur->nullRow = 0; + u.ci.pCur->nullRow = 0; break; } @@ -65967,51 +68522,51 @@ case OP_VFilter: { /* jump */ ** P1 cursor is pointing to into register P3. */ case OP_VColumn: { -#if 0 /* local variables moved into u.ci */ +#if 0 /* local variables moved into u.cj */ sqlite3_vtab *pVtab; const sqlite3_module *pModule; Mem *pDest; sqlite3_context sContext; -#endif /* local variables moved into u.ci */ +#endif /* local variables moved into u.cj */ VdbeCursor *pCur = p->apCsr[pOp->p1]; assert( pCur->pVtabCursor ); assert( pOp->p3>0 && pOp->p3<=p->nMem ); - u.ci.pDest = &aMem[pOp->p3]; - memAboutToChange(p, u.ci.pDest); + u.cj.pDest = &aMem[pOp->p3]; + memAboutToChange(p, u.cj.pDest); if( pCur->nullRow ){ - sqlite3VdbeMemSetNull(u.ci.pDest); + sqlite3VdbeMemSetNull(u.cj.pDest); break; } - u.ci.pVtab = pCur->pVtabCursor->pVtab; - u.ci.pModule = u.ci.pVtab->pModule; - assert( u.ci.pModule->xColumn ); - memset(&u.ci.sContext, 0, sizeof(u.ci.sContext)); + u.cj.pVtab = pCur->pVtabCursor->pVtab; + u.cj.pModule = u.cj.pVtab->pModule; + assert( u.cj.pModule->xColumn ); + memset(&u.cj.sContext, 0, sizeof(u.cj.sContext)); /* The output cell may already have a buffer allocated. Move - ** the current contents to u.ci.sContext.s so in case the user-function + ** the current contents to u.cj.sContext.s so in case the user-function ** can use the already allocated buffer instead of allocating a ** new one. */ - sqlite3VdbeMemMove(&u.ci.sContext.s, u.ci.pDest); - MemSetTypeFlag(&u.ci.sContext.s, MEM_Null); + sqlite3VdbeMemMove(&u.cj.sContext.s, u.cj.pDest); + MemSetTypeFlag(&u.cj.sContext.s, MEM_Null); - rc = u.ci.pModule->xColumn(pCur->pVtabCursor, &u.ci.sContext, pOp->p2); - importVtabErrMsg(p, u.ci.pVtab); - if( u.ci.sContext.isError ){ - rc = u.ci.sContext.isError; + rc = u.cj.pModule->xColumn(pCur->pVtabCursor, &u.cj.sContext, pOp->p2); + importVtabErrMsg(p, u.cj.pVtab); + if( u.cj.sContext.isError ){ + rc = u.cj.sContext.isError; } /* Copy the result of the function to the P3 register. We ** do this regardless of whether or not an error occurred to ensure any - ** dynamic allocation in u.ci.sContext.s (a Mem struct) is released. + ** dynamic allocation in u.cj.sContext.s (a Mem struct) is released. */ - sqlite3VdbeChangeEncoding(&u.ci.sContext.s, encoding); - sqlite3VdbeMemMove(u.ci.pDest, &u.ci.sContext.s); - REGISTER_TRACE(pOp->p3, u.ci.pDest); - UPDATE_MAX_BLOBSIZE(u.ci.pDest); + sqlite3VdbeChangeEncoding(&u.cj.sContext.s, encoding); + sqlite3VdbeMemMove(u.cj.pDest, &u.cj.sContext.s); + REGISTER_TRACE(pOp->p3, u.cj.pDest); + UPDATE_MAX_BLOBSIZE(u.cj.pDest); - if( sqlite3VdbeMemTooBig(u.ci.pDest) ){ + if( sqlite3VdbeMemTooBig(u.cj.pDest) ){ goto too_big; } break; @@ -66026,22 +68581,22 @@ case OP_VColumn: { ** the end of its result set, then fall through to the next instruction. */ case OP_VNext: { /* jump */ -#if 0 /* local variables moved into u.cj */ +#if 0 /* local variables moved into u.ck */ sqlite3_vtab *pVtab; const sqlite3_module *pModule; int res; VdbeCursor *pCur; -#endif /* local variables moved into u.cj */ +#endif /* local variables moved into u.ck */ - u.cj.res = 0; - u.cj.pCur = p->apCsr[pOp->p1]; - assert( u.cj.pCur->pVtabCursor ); - if( u.cj.pCur->nullRow ){ + u.ck.res = 0; + u.ck.pCur = p->apCsr[pOp->p1]; + assert( u.ck.pCur->pVtabCursor ); + if( u.ck.pCur->nullRow ){ break; } - u.cj.pVtab = u.cj.pCur->pVtabCursor->pVtab; - u.cj.pModule = u.cj.pVtab->pModule; - assert( u.cj.pModule->xNext ); + u.ck.pVtab = u.ck.pCur->pVtabCursor->pVtab; + u.ck.pModule = u.ck.pVtab->pModule; + assert( u.ck.pModule->xNext ); /* Invoke the xNext() method of the module. There is no way for the ** underlying implementation to return an error if one occurs during @@ -66050,14 +68605,14 @@ case OP_VNext: { /* jump */ ** some other method is next invoked on the save virtual table cursor. */ p->inVtabMethod = 1; - rc = u.cj.pModule->xNext(u.cj.pCur->pVtabCursor); + rc = u.ck.pModule->xNext(u.ck.pCur->pVtabCursor); p->inVtabMethod = 0; - importVtabErrMsg(p, u.cj.pVtab); + importVtabErrMsg(p, u.ck.pVtab); if( rc==SQLITE_OK ){ - u.cj.res = u.cj.pModule->xEof(u.cj.pCur->pVtabCursor); + u.ck.res = u.ck.pModule->xEof(u.ck.pCur->pVtabCursor); } - if( !u.cj.res ){ + if( !u.ck.res ){ /* If there is data, jump to P2 */ pc = pOp->p2 - 1; } @@ -66073,19 +68628,19 @@ case OP_VNext: { /* jump */ ** in register P1 is passed as the zName argument to the xRename method. */ case OP_VRename: { -#if 0 /* local variables moved into u.ck */ +#if 0 /* local variables moved into u.cl */ sqlite3_vtab *pVtab; Mem *pName; -#endif /* local variables moved into u.ck */ +#endif /* local variables moved into u.cl */ - u.ck.pVtab = pOp->p4.pVtab->pVtab; - u.ck.pName = &aMem[pOp->p1]; - assert( u.ck.pVtab->pModule->xRename ); - assert( memIsValid(u.ck.pName) ); - REGISTER_TRACE(pOp->p1, u.ck.pName); - assert( u.ck.pName->flags & MEM_Str ); - rc = u.ck.pVtab->pModule->xRename(u.ck.pVtab, u.ck.pName->z); - importVtabErrMsg(p, u.ck.pVtab); + u.cl.pVtab = pOp->p4.pVtab->pVtab; + u.cl.pName = &aMem[pOp->p1]; + assert( u.cl.pVtab->pModule->xRename ); + assert( memIsValid(u.cl.pName) ); + REGISTER_TRACE(pOp->p1, u.cl.pName); + assert( u.cl.pName->flags & MEM_Str ); + rc = u.cl.pVtab->pModule->xRename(u.cl.pVtab, u.cl.pName->z); + importVtabErrMsg(p, u.cl.pVtab); p->expired = 0; break; @@ -66117,7 +68672,7 @@ case OP_VRename: { ** is set to the value of the rowid for the row just inserted. */ case OP_VUpdate: { -#if 0 /* local variables moved into u.cl */ +#if 0 /* local variables moved into u.cm */ sqlite3_vtab *pVtab; sqlite3_module *pModule; int nArg; @@ -66125,29 +68680,43 @@ case OP_VUpdate: { sqlite_int64 rowid; Mem **apArg; Mem *pX; -#endif /* local variables moved into u.cl */ +#endif /* local variables moved into u.cm */ - u.cl.pVtab = pOp->p4.pVtab->pVtab; - u.cl.pModule = (sqlite3_module *)u.cl.pVtab->pModule; - u.cl.nArg = pOp->p2; + assert( pOp->p2==1 || pOp->p5==OE_Fail || pOp->p5==OE_Rollback + || pOp->p5==OE_Abort || pOp->p5==OE_Ignore || pOp->p5==OE_Replace + ); + u.cm.pVtab = pOp->p4.pVtab->pVtab; + u.cm.pModule = (sqlite3_module *)u.cm.pVtab->pModule; + u.cm.nArg = pOp->p2; assert( pOp->p4type==P4_VTAB ); - if( ALWAYS(u.cl.pModule->xUpdate) ){ - u.cl.apArg = p->apArg; - u.cl.pX = &aMem[pOp->p3]; - for(u.cl.i=0; u.cl.i<u.cl.nArg; u.cl.i++){ - assert( memIsValid(u.cl.pX) ); - memAboutToChange(p, u.cl.pX); - sqlite3VdbeMemStoreType(u.cl.pX); - u.cl.apArg[u.cl.i] = u.cl.pX; - u.cl.pX++; - } - rc = u.cl.pModule->xUpdate(u.cl.pVtab, u.cl.nArg, u.cl.apArg, &u.cl.rowid); - importVtabErrMsg(p, u.cl.pVtab); + if( ALWAYS(u.cm.pModule->xUpdate) ){ + u8 vtabOnConflict = db->vtabOnConflict; + u.cm.apArg = p->apArg; + u.cm.pX = &aMem[pOp->p3]; + for(u.cm.i=0; u.cm.i<u.cm.nArg; u.cm.i++){ + assert( memIsValid(u.cm.pX) ); + memAboutToChange(p, u.cm.pX); + sqlite3VdbeMemStoreType(u.cm.pX); + u.cm.apArg[u.cm.i] = u.cm.pX; + u.cm.pX++; + } + db->vtabOnConflict = pOp->p5; + rc = u.cm.pModule->xUpdate(u.cm.pVtab, u.cm.nArg, u.cm.apArg, &u.cm.rowid); + db->vtabOnConflict = vtabOnConflict; + importVtabErrMsg(p, u.cm.pVtab); if( rc==SQLITE_OK && pOp->p1 ){ - assert( u.cl.nArg>1 && u.cl.apArg[0] && (u.cl.apArg[0]->flags&MEM_Null) ); - db->lastRowid = u.cl.rowid; + assert( u.cm.nArg>1 && u.cm.apArg[0] && (u.cm.apArg[0]->flags&MEM_Null) ); + db->lastRowid = lastRowid = u.cm.rowid; + } + if( rc==SQLITE_CONSTRAINT && pOp->p4.pVtab->bConstraint ){ + if( pOp->p5==OE_Ignore ){ + rc = SQLITE_OK; + }else{ + p->errorAction = ((pOp->p5==OE_Replace) ? OE_Abort : pOp->p5); + } + }else{ + p->nChange++; } - p->nChange++; } break; } @@ -66197,23 +68766,23 @@ case OP_MaxPgcnt: { /* out2-prerelease */ ** the UTF-8 string contained in P4 is emitted on the trace callback. */ case OP_Trace: { -#if 0 /* local variables moved into u.cm */ +#if 0 /* local variables moved into u.cn */ char *zTrace; -#endif /* local variables moved into u.cm */ + char *z; +#endif /* local variables moved into u.cn */ - u.cm.zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql); - if( u.cm.zTrace ){ - if( db->xTrace ){ - char *z = sqlite3VdbeExpandSql(p, u.cm.zTrace); - db->xTrace(db->pTraceArg, z); - sqlite3DbFree(db, z); - } + if( db->xTrace && (u.cn.zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql))!=0 ){ + u.cn.z = sqlite3VdbeExpandSql(p, u.cn.zTrace); + db->xTrace(db->pTraceArg, u.cn.z); + sqlite3DbFree(db, u.cn.z); + } #ifdef SQLITE_DEBUG - if( (db->flags & SQLITE_SqlTrace)!=0 ){ - sqlite3DebugPrintf("SQL-trace: %s\n", u.cm.zTrace); - } -#endif /* SQLITE_DEBUG */ + if( (db->flags & SQLITE_SqlTrace)!=0 + && (u.cn.zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql))!=0 + ){ + sqlite3DebugPrintf("SQL-trace: %s\n", u.cn.zTrace); } +#endif /* SQLITE_DEBUG */ break; } #endif @@ -66289,13 +68858,16 @@ vdbe_error_halt: sqlite3VdbeHalt(p); if( rc==SQLITE_IOERR_NOMEM ) db->mallocFailed = 1; rc = SQLITE_ERROR; - if( resetSchemaOnFault ) sqlite3ResetInternalSchema(db, 0); + if( resetSchemaOnFault>0 ){ + sqlite3ResetInternalSchema(db, resetSchemaOnFault-1); + } /* This is the only way out of this procedure. We have to ** release the mutexes on btrees that were acquired at the ** top. */ vdbe_return: - sqlite3BtreeMutexArrayLeave(&p->aMutex); + db->lastRowid = lastRowid; + sqlite3VdbeLeave(p); return rc; /* Jump to here if a string or blob larger than SQLITE_MAX_LENGTH @@ -66604,6 +69176,7 @@ SQLITE_API int sqlite3_blob_open( /* Configure the OP_VerifyCookie */ sqlite3VdbeChangeP1(v, 1, iDb); sqlite3VdbeChangeP2(v, 1, pTab->pSchema->schema_cookie); + sqlite3VdbeChangeP3(v, 1, pTab->pSchema->iGeneration); /* Make sure a mutex is held on the table to be accessed */ sqlite3VdbeUsesBtree(v, iDb); @@ -66634,7 +69207,10 @@ SQLITE_API int sqlite3_blob_open( sqlite3VdbeChangeP4(v, 3+flags, SQLITE_INT_TO_PTR(pTab->nCol+1),P4_INT32); sqlite3VdbeChangeP2(v, 7, pTab->nCol); if( !db->mallocFailed ){ - sqlite3VdbeMakeReady(v, 1, 1, 1, 0, 0, 0); + pParse->nVar = 1; + pParse->nMem = 1; + pParse->nTab = 1; + sqlite3VdbeMakeReady(v, pParse); } } @@ -68738,7 +71314,7 @@ SQLITE_PRIVATE Expr *sqlite3ExprSetCollByToken(Parse *pParse, Expr *pExpr, Token SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr){ CollSeq *pColl = 0; Expr *p = pExpr; - while( ALWAYS(p) ){ + while( p ){ int op; pColl = p->pColl; if( pColl ) break; @@ -69035,6 +71611,7 @@ SQLITE_PRIVATE Expr *sqlite3ExprAlloc( if( op!=TK_INTEGER || pToken->z==0 || sqlite3GetInt32(pToken->z, &iValue)==0 ){ nExtra = pToken->n+1; + assert( iValue>=0 ); } } pNew = sqlite3DbMallocZero(db, sizeof(Expr)+nExtra); @@ -69200,53 +71777,53 @@ SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr){ /* Wildcard of the form "?". Assign the next variable number */ assert( z[0]=='?' ); pExpr->iColumn = (ynVar)(++pParse->nVar); - }else if( z[0]=='?' ){ - /* Wildcard of the form "?nnn". Convert "nnn" to an integer and - ** use it as the variable number */ - i64 i; - int bOk = 0==sqlite3Atoi64(&z[1], &i, sqlite3Strlen30(&z[1]), SQLITE_UTF8); - pExpr->iColumn = (ynVar)i; - testcase( i==0 ); - testcase( i==1 ); - testcase( i==db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]-1 ); - testcase( i==db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ); - if( bOk==0 || i<1 || i>db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ){ - sqlite3ErrorMsg(pParse, "variable number must be between ?1 and ?%d", - db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]); - } - if( i>pParse->nVar ){ - pParse->nVar = (int)i; - } }else{ - /* Wildcards like ":aaa", "$aaa" or "@aaa". Reuse the same variable - ** number as the prior appearance of the same name, or if the name - ** has never appeared before, reuse the same variable number - */ - int i; - u32 n; - n = sqlite3Strlen30(z); - for(i=0; i<pParse->nVarExpr; i++){ - Expr *pE = pParse->apVarExpr[i]; - assert( pE!=0 ); - if( memcmp(pE->u.zToken, z, n)==0 && pE->u.zToken[n]==0 ){ - pExpr->iColumn = pE->iColumn; - break; + ynVar x = 0; + u32 n = sqlite3Strlen30(z); + if( z[0]=='?' ){ + /* Wildcard of the form "?nnn". Convert "nnn" to an integer and + ** use it as the variable number */ + i64 i; + int bOk = 0==sqlite3Atoi64(&z[1], &i, n-1, SQLITE_UTF8); + pExpr->iColumn = x = (ynVar)i; + testcase( i==0 ); + testcase( i==1 ); + testcase( i==db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]-1 ); + testcase( i==db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ); + if( bOk==0 || i<1 || i>db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ){ + sqlite3ErrorMsg(pParse, "variable number must be between ?1 and ?%d", + db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]); + x = 0; + } + if( i>pParse->nVar ){ + pParse->nVar = (int)i; } + }else{ + /* Wildcards like ":aaa", "$aaa" or "@aaa". Reuse the same variable + ** number as the prior appearance of the same name, or if the name + ** has never appeared before, reuse the same variable number + */ + ynVar i; + for(i=0; i<pParse->nzVar; i++){ + if( pParse->azVar[i] && memcmp(pParse->azVar[i],z,n+1)==0 ){ + pExpr->iColumn = x = (ynVar)i+1; + break; + } + } + if( x==0 ) x = pExpr->iColumn = (ynVar)(++pParse->nVar); } - if( i>=pParse->nVarExpr ){ - pExpr->iColumn = (ynVar)(++pParse->nVar); - if( pParse->nVarExpr>=pParse->nVarExprAlloc-1 ){ - pParse->nVarExprAlloc += pParse->nVarExprAlloc + 10; - pParse->apVarExpr = - sqlite3DbReallocOrFree( - db, - pParse->apVarExpr, - pParse->nVarExprAlloc*sizeof(pParse->apVarExpr[0]) - ); + if( x>0 ){ + if( x>pParse->nzVar ){ + char **a; + a = sqlite3DbRealloc(db, pParse->azVar, x*sizeof(a[0])); + if( a==0 ) return; /* Error reported through db->mallocFailed */ + pParse->azVar = a; + memset(&a[pParse->nzVar], 0, (x-pParse->nzVar)*sizeof(a[0])); + pParse->nzVar = x; } - if( !db->mallocFailed ){ - assert( pParse->apVarExpr!=0 ); - pParse->apVarExpr[pParse->nVarExpr++] = pExpr; + if( z[0]!='?' || pParse->azVar[x-1]==0 ){ + sqlite3DbFree(db, pParse->azVar[x-1]); + pParse->azVar[x-1] = sqlite3DbStrNDup(db, z, n); } } } @@ -69260,6 +71837,8 @@ SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr){ */ SQLITE_PRIVATE void sqlite3ExprDelete(sqlite3 *db, Expr *p){ if( p==0 ) return; + /* Sanity check: Assert that the IntValue is non-negative if it exists */ + assert( !ExprHasProperty(p, EP_IntValue) || p->u.iValue>=0 ); if( !ExprHasAnyProperty(p, EP_TokenOnly) ){ sqlite3ExprDelete(db, p->pLeft); sqlite3ExprDelete(db, p->pRight); @@ -69844,16 +72423,17 @@ SQLITE_PRIVATE int sqlite3ExprIsConstantOrFunction(Expr *p){ */ SQLITE_PRIVATE int sqlite3ExprIsInteger(Expr *p, int *pValue){ int rc = 0; + + /* If an expression is an integer literal that fits in a signed 32-bit + ** integer, then the EP_IntValue flag will have already been set */ + assert( p->op!=TK_INTEGER || (p->flags & EP_IntValue)!=0 + || sqlite3GetInt32(p->u.zToken, &rc)==0 ); + if( p->flags & EP_IntValue ){ *pValue = p->u.iValue; return 1; } switch( p->op ){ - case TK_INTEGER: { - rc = sqlite3GetInt32(p->u.zToken, pValue); - assert( rc==0 ); - break; - } case TK_UPLUS: { rc = sqlite3ExprIsInteger(p->pLeft, pValue); break; @@ -69868,13 +72448,6 @@ SQLITE_PRIVATE int sqlite3ExprIsInteger(Expr *p, int *pValue){ } default: break; } - if( rc ){ - assert( ExprHasAnyProperty(p, EP_Reduced|EP_TokenOnly) - || (p->flags2 & EP2_MallocedToken)==0 ); - p->op = TK_INTEGER; - p->flags |= EP_IntValue; - p->u.iValue = *pValue; - } return rc; } @@ -70599,6 +73172,7 @@ static void codeInteger(Parse *pParse, Expr *pExpr, int negFlag, int iMem){ Vdbe *v = pParse->pVdbe; if( pExpr->flags & EP_IntValue ){ int i = pExpr->u.iValue; + assert( i>=0 ); if( negFlag ) i = -i; sqlite3VdbeAddOp2(v, OP_Integer, i, iMem); }else{ @@ -70609,7 +73183,7 @@ static void codeInteger(Parse *pParse, Expr *pExpr, int negFlag, int iMem){ c = sqlite3Atoi64(z, &value, sqlite3Strlen30(z), SQLITE_UTF8); if( c==0 || (c==2 && negFlag) ){ char *zV; - if( negFlag ){ value = -value; } + if( negFlag ){ value = c==2 ? SMALLEST_INT64 : -value; } zV = dup8bytes(v, (char*)&value); sqlite3VdbeAddOp4(v, OP_Int64, 0, iMem, 0, zV, P4_INT64); }else{ @@ -70993,7 +73567,9 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target) assert( pExpr->u.zToken[0]!=0 ); sqlite3VdbeAddOp2(v, OP_Variable, pExpr->iColumn, target); if( pExpr->u.zToken[1]!=0 ){ - sqlite3VdbeChangeP4(v, -1, pExpr->u.zToken, 0); + assert( pExpr->u.zToken[0]=='?' + || strcmp(pExpr->u.zToken, pParse->azVar[pExpr->iColumn-1])==0 ); + sqlite3VdbeChangeP4(v, -1, pParse->azVar[pExpr->iColumn-1], P4_STATIC); } break; } @@ -71897,6 +74473,7 @@ SQLITE_PRIVATE void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int exprCodeBetween(pParse, pExpr, dest, 1, jumpIfNull); break; } +#ifndef SQLITE_OMIT_SUBQUERY case TK_IN: { int destIfFalse = sqlite3VdbeMakeLabel(v); int destIfNull = jumpIfNull ? dest : destIfFalse; @@ -71905,6 +74482,7 @@ SQLITE_PRIVATE void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int sqlite3VdbeResolveLabel(v, destIfFalse); break; } +#endif default: { r1 = sqlite3ExprCodeTemp(pParse, pExpr, ®Free1); sqlite3VdbeAddOp3(v, OP_If, r1, dest, jumpIfNull!=0); @@ -72038,6 +74616,7 @@ SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int exprCodeBetween(pParse, pExpr, dest, 0, jumpIfNull); break; } +#ifndef SQLITE_OMIT_SUBQUERY case TK_IN: { if( jumpIfNull ){ sqlite3ExprCodeIN(pParse, pExpr, dest, dest); @@ -72048,6 +74627,7 @@ SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int } break; } +#endif default: { r1 = sqlite3ExprCodeTemp(pParse, pExpr, ®Free1); sqlite3VdbeAddOp3(v, OP_IfNot, r1, dest, jumpIfNull!=0); @@ -72759,19 +75339,35 @@ static void reloadTableSchema(Parse *pParse, Table *pTab, const char *zName){ /* Reload the table, index and permanent trigger schemas. */ zWhere = sqlite3MPrintf(pParse->db, "tbl_name=%Q", zName); if( !zWhere ) return; - sqlite3VdbeAddOp4(v, OP_ParseSchema, iDb, 0, 0, zWhere, P4_DYNAMIC); + sqlite3VdbeAddParseSchemaOp(v, iDb, zWhere); #ifndef SQLITE_OMIT_TRIGGER /* Now, if the table is not stored in the temp database, reload any temp ** triggers. Don't use IN(...) in case SQLITE_OMIT_SUBQUERY is defined. */ if( (zWhere=whereTempTriggers(pParse, pTab))!=0 ){ - sqlite3VdbeAddOp4(v, OP_ParseSchema, 1, 0, 0, zWhere, P4_DYNAMIC); + sqlite3VdbeAddParseSchemaOp(v, 1, zWhere); } #endif } /* +** Parameter zName is the name of a table that is about to be altered +** (either with ALTER TABLE ... RENAME TO or ALTER TABLE ... ADD COLUMN). +** If the table is a system table, this function leaves an error message +** in pParse->zErr (system tables may not be altered) and returns non-zero. +** +** Or, if zName is not a system table, zero is returned. +*/ +static int isSystemTable(Parse *pParse, const char *zName){ + if( sqlite3Strlen30(zName)>6 && 0==sqlite3StrNICmp(zName, "sqlite_", 7) ){ + sqlite3ErrorMsg(pParse, "table %s may not be altered", zName); + return 1; + } + return 0; +} + +/* ** Generate code to implement the "ALTER TABLE xxx RENAME TO yyy" ** command. */ @@ -72821,14 +75417,11 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable( /* Make sure it is not a system table being altered, or a reserved name ** that the table is being renamed to. */ - if( sqlite3Strlen30(pTab->zName)>6 - && 0==sqlite3StrNICmp(pTab->zName, "sqlite_", 7) - ){ - sqlite3ErrorMsg(pParse, "table %s may not be altered", pTab->zName); + if( SQLITE_OK!=isSystemTable(pParse, pTab->zName) ){ goto exit_rename_table; } - if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){ - goto exit_rename_table; + if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){ goto + exit_rename_table; } #ifndef SQLITE_OMIT_VIEW @@ -73160,6 +75753,9 @@ SQLITE_PRIVATE void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){ sqlite3ErrorMsg(pParse, "Cannot add a column to a view"); goto exit_begin_add_column; } + if( SQLITE_OK!=isSystemTable(pParse, pTab->zName) ){ + goto exit_begin_add_column; + } assert( pTab->addColOffset>0 ); iDb = sqlite3SchemaToIndex(db, pTab->pSchema); @@ -73247,7 +75843,8 @@ static void openStatTable( Parse *pParse, /* Parsing context */ int iDb, /* The database we are looking in */ int iStatCur, /* Open the sqlite_stat1 table on this cursor */ - const char *zWhere /* Delete entries associated with this table */ + const char *zWhere, /* Delete entries for this table or index */ + const char *zWhereType /* Either "tbl" or "idx" */ ){ static const struct { const char *zName; @@ -73292,7 +75889,7 @@ static void openStatTable( sqlite3TableLock(pParse, iDb, aRoot[i], 1, zTab); if( zWhere ){ sqlite3NestedParse(pParse, - "DELETE FROM %Q.%s WHERE tbl=%Q", pDb->zName, zTab, zWhere + "DELETE FROM %Q.%s WHERE %s=%Q", pDb->zName, zTab, zWhereType, zWhere ); }else{ /* The sqlite_stat[12] table already exists. Delete all rows. */ @@ -73316,6 +75913,7 @@ static void openStatTable( static void analyzeOneTable( Parse *pParse, /* Parser context */ Table *pTab, /* Table whose indices are to be analyzed */ + Index *pOnlyIdx, /* If not NULL, only analyze this one index */ int iStatCur, /* Index of VdbeCursor that writes the sqlite_stat1 table */ int iMem /* Available memory locations begin here */ ){ @@ -73326,8 +75924,7 @@ static void analyzeOneTable( int i; /* Loop counter */ int topOfLoop; /* The top of the loop */ int endOfLoop; /* The end of the loop */ - int addr = 0; /* The address of an instruction */ - int jZeroRows = 0; /* Jump from here if number of rows is zero */ + int jZeroRows = -1; /* Jump from here if number of rows is zero */ int iDb; /* Index of database containing pTab */ int regTabname = iMem++; /* Register containing table name */ int regIdxname = iMem++; /* Register containing index name */ @@ -73338,6 +75935,7 @@ static void analyzeOneTable( int regRowid = iMem++; /* Rowid for the inserted record */ #ifdef SQLITE_ENABLE_STAT2 + int addr = 0; /* Instruction address */ int regTemp2 = iMem++; /* Temporary use register */ int regSamplerecno = iMem++; /* Index of next sample to record */ int regRecno = iMem++; /* Current sample index */ @@ -73360,6 +75958,7 @@ static void analyzeOneTable( assert( sqlite3BtreeHoldsAllMutexes(db) ); iDb = sqlite3SchemaToIndex(db, pTab->pSchema); assert( iDb>=0 ); + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); #ifndef SQLITE_OMIT_AUTHORIZATION if( sqlite3AuthCheck(pParse, SQLITE_ANALYZE, pTab->zName, 0, db->aDb[iDb].zName ) ){ @@ -73373,9 +75972,12 @@ static void analyzeOneTable( iIdxCur = pParse->nTab++; sqlite3VdbeAddOp4(v, OP_String8, 0, regTabname, 0, pTab->zName, 0); for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - int nCol = pIdx->nColumn; - KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx); + int nCol; + KeyInfo *pKey; + if( pOnlyIdx && pOnlyIdx!=pIdx ) continue; + nCol = pIdx->nColumn; + pKey = sqlite3IndexKeyinfo(pParse, pIdx); if( iMem+1+(nCol*2)>pParse->nMem ){ pParse->nMem = iMem+1+(nCol*2); } @@ -73446,9 +76048,10 @@ static void analyzeOneTable( sqlite3VdbeAddOp2(v, OP_AddImm, iMem, 1); for(i=0; i<nCol; i++){ + CollSeq *pColl; sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, regCol); -#ifdef SQLITE_ENABLE_STAT2 if( i==0 ){ +#ifdef SQLITE_ENABLE_STAT2 /* Check if the record that cursor iIdxCur points to contains a ** value that should be stored in the sqlite_stat2 table. If so, ** store it. */ @@ -73477,12 +76080,17 @@ static void analyzeOneTable( sqlite3VdbeJumpHere(v, ne); sqlite3VdbeAddOp2(v, OP_AddImm, regRecno, 1); - } #endif - sqlite3VdbeAddOp3(v, OP_Ne, regCol, 0, iMem+nCol+i+1); - /**** TODO: add collating sequence *****/ - sqlite3VdbeChangeP5(v, SQLITE_JUMPIFNULL); + /* Always record the very first row */ + sqlite3VdbeAddOp1(v, OP_IfNot, iMem+1); + } + assert( pIdx->azColl!=0 ); + assert( pIdx->azColl[i]!=0 ); + pColl = sqlite3LocateCollSeq(pParse, pIdx->azColl[i]); + sqlite3VdbeAddOp4(v, OP_Ne, regCol, 0, iMem+nCol+i+1, + (char*)pColl, P4_COLLSEQ); + sqlite3VdbeChangeP5(v, SQLITE_NULLEQ); } if( db->mallocFailed ){ /* If a malloc failure has occurred, then the result of the expression @@ -73493,7 +76101,11 @@ static void analyzeOneTable( } sqlite3VdbeAddOp2(v, OP_Goto, 0, endOfLoop); for(i=0; i<nCol; i++){ - sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-(nCol*2)); + int addr2 = sqlite3VdbeCurrentAddr(v) - (nCol*2); + if( i==0 ){ + sqlite3VdbeJumpHere(v, addr2-1); /* Set jump dest for the OP_IfNot */ + } + sqlite3VdbeJumpHere(v, addr2); /* Set jump dest for the OP_Ne */ sqlite3VdbeAddOp2(v, OP_AddImm, iMem+i+1, 1); sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, iMem+nCol+i+1); } @@ -73522,7 +76134,7 @@ static void analyzeOneTable( ** is never possible. */ sqlite3VdbeAddOp2(v, OP_SCopy, iMem, regSampleno); - if( jZeroRows==0 ){ + if( jZeroRows<0 ){ jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, iMem); } for(i=0; i<nCol; i++){ @@ -73548,10 +76160,10 @@ static void analyzeOneTable( VdbeComment((v, "%s", pTab->zName)); sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regSampleno); sqlite3VdbeAddOp1(v, OP_Close, iIdxCur); + jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, regSampleno); }else{ - assert( jZeroRows>0 ); - addr = sqlite3VdbeAddOp0(v, OP_Goto); sqlite3VdbeJumpHere(v, jZeroRows); + jZeroRows = sqlite3VdbeAddOp0(v, OP_Goto); } sqlite3VdbeAddOp2(v, OP_Null, 0, regIdxname); sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regRec, "aaa", 0); @@ -73559,9 +76171,7 @@ static void analyzeOneTable( sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regRec, regRowid); sqlite3VdbeChangeP5(v, OPFLAG_APPEND); if( pParse->nMem<regRec ) pParse->nMem = regRec; - if( jZeroRows ){ - sqlite3VdbeJumpHere(v, addr); - } + sqlite3VdbeJumpHere(v, jZeroRows); } /* @@ -73588,20 +76198,22 @@ static void analyzeDatabase(Parse *pParse, int iDb){ sqlite3BeginWriteOperation(pParse, 0, iDb); iStatCur = pParse->nTab; pParse->nTab += 2; - openStatTable(pParse, iDb, iStatCur, 0); + openStatTable(pParse, iDb, iStatCur, 0, 0); iMem = pParse->nMem+1; + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){ Table *pTab = (Table*)sqliteHashData(k); - analyzeOneTable(pParse, pTab, iStatCur, iMem); + analyzeOneTable(pParse, pTab, 0, iStatCur, iMem); } loadAnalysis(pParse, iDb); } /* ** Generate code that will do an analysis of a single table in -** a database. +** a database. If pOnlyIdx is not NULL then it is a single index +** in pTab that should be analyzed. */ -static void analyzeTable(Parse *pParse, Table *pTab){ +static void analyzeTable(Parse *pParse, Table *pTab, Index *pOnlyIdx){ int iDb; int iStatCur; @@ -73611,8 +76223,12 @@ static void analyzeTable(Parse *pParse, Table *pTab){ sqlite3BeginWriteOperation(pParse, 0, iDb); iStatCur = pParse->nTab; pParse->nTab += 2; - openStatTable(pParse, iDb, iStatCur, pTab->zName); - analyzeOneTable(pParse, pTab, iStatCur, pParse->nMem+1); + if( pOnlyIdx ){ + openStatTable(pParse, iDb, iStatCur, pOnlyIdx->zName, "idx"); + }else{ + openStatTable(pParse, iDb, iStatCur, pTab->zName, "tbl"); + } + analyzeOneTable(pParse, pTab, pOnlyIdx, iStatCur, pParse->nMem+1); loadAnalysis(pParse, iDb); } @@ -73634,6 +76250,7 @@ SQLITE_PRIVATE void sqlite3Analyze(Parse *pParse, Token *pName1, Token *pName2){ int i; char *z, *zDb; Table *pTab; + Index *pIdx; Token *pTableName; /* Read the database schema. If an error occurs, leave an error message @@ -73658,11 +76275,12 @@ SQLITE_PRIVATE void sqlite3Analyze(Parse *pParse, Token *pName1, Token *pName2){ }else{ z = sqlite3NameFromToken(db, pName1); if( z ){ - pTab = sqlite3LocateTable(pParse, 0, z, 0); - sqlite3DbFree(db, z); - if( pTab ){ - analyzeTable(pParse, pTab); + if( (pIdx = sqlite3FindIndex(db, z, 0))!=0 ){ + analyzeTable(pParse, pIdx->pTable, pIdx); + }else if( (pTab = sqlite3LocateTable(pParse, 0, z, 0))!=0 ){ + analyzeTable(pParse, pTab, 0); } + sqlite3DbFree(db, z); } } }else{ @@ -73672,11 +76290,12 @@ SQLITE_PRIVATE void sqlite3Analyze(Parse *pParse, Token *pName1, Token *pName2){ zDb = db->aDb[iDb].zName; z = sqlite3NameFromToken(db, pTableName); if( z ){ - pTab = sqlite3LocateTable(pParse, 0, z, zDb); - sqlite3DbFree(db, z); - if( pTab ){ - analyzeTable(pParse, pTab); + if( (pIdx = sqlite3FindIndex(db, z, zDb))!=0 ){ + analyzeTable(pParse, pIdx->pTable, pIdx); + }else if( (pTab = sqlite3LocateTable(pParse, 0, z, zDb))!=0 ){ + analyzeTable(pParse, pTab, 0); } + sqlite3DbFree(db, z); } } } @@ -73738,6 +76357,10 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){ if( pIndex==0 ) break; pIndex->aiRowEst[i] = v; if( *z==' ' ) z++; + if( memcmp(z, "unordered", 10)==0 ){ + pIndex->bUnordered = 1; + break; + } } return 0; } @@ -73792,9 +76415,9 @@ SQLITE_PRIVATE int sqlite3AnalysisLoad(sqlite3 *db, int iDb){ assert( iDb>=0 && iDb<db->nDb ); assert( db->aDb[iDb].pBt!=0 ); - assert( sqlite3BtreeHoldsMutex(db->aDb[iDb].pBt) ); /* Clear any prior statistics */ + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); for(i=sqliteHashFirst(&db->aDb[iDb].pSchema->idxHash);i;i=sqliteHashNext(i)){ Index *pIdx = sqliteHashData(i); sqlite3DefaultRowEst(pIdx); @@ -73839,8 +76462,11 @@ SQLITE_PRIVATE int sqlite3AnalysisLoad(sqlite3 *db, int iDb){ if( rc==SQLITE_OK ){ while( sqlite3_step(pStmt)==SQLITE_ROW ){ - char *zIndex = (char *)sqlite3_column_text(pStmt, 0); - Index *pIdx = sqlite3FindIndex(db, zIndex, sInfo.zDatabase); + char *zIndex; /* Index name */ + Index *pIdx; /* Pointer to the index object */ + + zIndex = (char *)sqlite3_column_text(pStmt, 0); + pIdx = zIndex ? sqlite3FindIndex(db, zIndex, sInfo.zDatabase) : 0; if( pIdx ){ int iSample = sqlite3_column_int(pStmt, 1); if( iSample<SQLITE_INDEX_SAMPLES && iSample>=0 ){ @@ -73974,8 +76600,12 @@ static void attachFunc( sqlite3 *db = sqlite3_context_db_handle(context); const char *zName; const char *zFile; + char *zPath = 0; + char *zErr = 0; + unsigned int flags; Db *aNew; char *zErrDyn = 0; + sqlite3_vfs *pVfs; UNUSED_PARAMETER(NotUsed); @@ -74028,8 +76658,18 @@ static void attachFunc( ** it to obtain the database schema. At this point the schema may ** or may not be initialised. */ - rc = sqlite3BtreeOpen(zFile, db, &aNew->pBt, 0, - db->openFlags | SQLITE_OPEN_MAIN_DB); + flags = db->openFlags; + rc = sqlite3ParseUri(db->pVfs->zName, zFile, &flags, &pVfs, &zPath, &zErr); + if( rc!=SQLITE_OK ){ + if( rc==SQLITE_NOMEM ) db->mallocFailed = 1; + sqlite3_result_error(context, zErr, -1); + sqlite3_free(zErr); + return; + } + assert( pVfs ); + flags |= SQLITE_OPEN_MAIN_DB; + rc = sqlite3BtreeOpen(pVfs, zPath, db, &aNew->pBt, 0, flags); + sqlite3_free( zPath ); db->nDb++; if( rc==SQLITE_CONSTRAINT ){ rc = SQLITE_ERROR; @@ -74080,7 +76720,9 @@ static void attachFunc( case SQLITE_NULL: /* No key specified. Use the key from the main database */ sqlite3CodecGetKey(db, 0, (void**)&zKey, &nKey); - rc = sqlite3CodecAttach(db, db->nDb-1, zKey, nKey); + if( nKey>0 || sqlite3BtreeGetReserve(db->aDb[0].pBt)>0 ){ + rc = sqlite3CodecAttach(db, db->nDb-1, zKey, nKey); + } break; } } @@ -74104,7 +76746,7 @@ static void attachFunc( db->aDb[iDb].pBt = 0; db->aDb[iDb].pSchema = 0; } - sqlite3ResetInternalSchema(db, 0); + sqlite3ResetInternalSchema(db, -1); db->nDb = iDb; if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){ db->mallocFailed = 1; @@ -74176,7 +76818,7 @@ static void detachFunc( sqlite3BtreeClose(pDb->pBt); pDb->pBt = 0; pDb->pSchema = 0; - sqlite3ResetInternalSchema(db, 0); + sqlite3ResetInternalSchema(db, -1); return; detach_error: @@ -74216,9 +76858,11 @@ static void codeAttach( #ifndef SQLITE_OMIT_AUTHORIZATION if( pAuthArg ){ - char *zAuthArg = pAuthArg->u.zToken; - if( NEVER(zAuthArg==0) ){ - goto attach_end; + char *zAuthArg; + if( pAuthArg->op==TK_STRING ){ + zAuthArg = pAuthArg->u.zToken; + }else{ + zAuthArg = 0; } rc = sqlite3AuthCheck(pParse, type, zAuthArg, 0, 0); if(rc!=SQLITE_OK ){ @@ -74844,7 +77488,7 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){ ** on each used database. */ if( pParse->cookieGoto>0 ){ - u32 mask; + yDbMask mask; int iDb; sqlite3VdbeJumpHere(v, pParse->cookieGoto-1); for(iDb=0, mask=1; iDb<db->nDb; mask<<=1, iDb++){ @@ -74852,7 +77496,10 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){ sqlite3VdbeUsesBtree(v, iDb); sqlite3VdbeAddOp2(v,OP_Transaction, iDb, (mask & pParse->writeMask)!=0); if( db->init.busy==0 ){ - sqlite3VdbeAddOp2(v,OP_VerifyCookie, iDb, pParse->cookieValue[iDb]); + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); + sqlite3VdbeAddOp3(v, OP_VerifyCookie, + iDb, pParse->cookieValue[iDb], + db->aDb[iDb].pSchema->iGeneration); } } #ifndef SQLITE_OMIT_VIRTUALTABLE @@ -74893,9 +77540,7 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){ /* A minimum of one cursor is required if autoincrement is used * See ticket [a696379c1f08866] */ if( pParse->pAinc!=0 && pParse->nTab==0 ) pParse->nTab = 1; - sqlite3VdbeMakeReady(v, pParse->nVar, pParse->nMem, - pParse->nTab, pParse->nMaxArg, pParse->explain, - pParse->isMultiWrite && pParse->mayAbort); + sqlite3VdbeMakeReady(v, pParse); pParse->rc = SQLITE_DONE; pParse->colNamesSet = 0; }else{ @@ -74965,9 +77610,12 @@ SQLITE_PRIVATE Table *sqlite3FindTable(sqlite3 *db, const char *zName, const cha int nName; assert( zName!=0 ); nName = sqlite3Strlen30(zName); + /* All mutexes are required for schema access. Make sure we hold them. */ + assert( zDatabase!=0 || sqlite3BtreeHoldsAllMutexes(db) ); for(i=OMIT_TEMPDB; i<db->nDb; i++){ int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */ if( zDatabase!=0 && sqlite3StrICmp(zDatabase, db->aDb[j].zName) ) continue; + assert( sqlite3SchemaMutexHeld(db, j, 0) ); p = sqlite3HashFind(&db->aDb[j].pSchema->tblHash, zName, nName); if( p ) break; } @@ -75027,11 +77675,14 @@ SQLITE_PRIVATE Index *sqlite3FindIndex(sqlite3 *db, const char *zName, const cha Index *p = 0; int i; int nName = sqlite3Strlen30(zName); + /* All mutexes are required for schema access. Make sure we hold them. */ + assert( zDb!=0 || sqlite3BtreeHoldsAllMutexes(db) ); for(i=OMIT_TEMPDB; i<db->nDb; i++){ int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */ Schema *pSchema = db->aDb[j].pSchema; assert( pSchema ); if( zDb && sqlite3StrICmp(zDb, db->aDb[j].zName) ) continue; + assert( sqlite3SchemaMutexHeld(db, j, 0) ); p = sqlite3HashFind(&pSchema->idxHash, zName, nName); if( p ) break; } @@ -75058,11 +77709,13 @@ static void freeIndex(sqlite3 *db, Index *p){ SQLITE_PRIVATE void sqlite3UnlinkAndDeleteIndex(sqlite3 *db, int iDb, const char *zIdxName){ Index *pIndex; int len; - Hash *pHash = &db->aDb[iDb].pSchema->idxHash; + Hash *pHash; + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); + pHash = &db->aDb[iDb].pSchema->idxHash; len = sqlite3Strlen30(zIdxName); pIndex = sqlite3HashInsert(pHash, zIdxName, len, 0); - if( pIndex ){ + if( ALWAYS(pIndex) ){ if( pIndex->pTable->pIndex==pIndex ){ pIndex->pTable->pIndex = pIndex->pNext; }else{ @@ -75087,26 +77740,42 @@ SQLITE_PRIVATE void sqlite3UnlinkAndDeleteIndex(sqlite3 *db, int iDb, const char ** if there were schema changes during the transaction or if a ** schema-cookie mismatch occurs. ** -** If iDb==0 then reset the internal schema tables for all database -** files. If iDb>=1 then reset the internal schema for only the +** If iDb<0 then reset the internal schema tables for all database +** files. If iDb>=0 then reset the internal schema for only the ** single file indicated. */ SQLITE_PRIVATE void sqlite3ResetInternalSchema(sqlite3 *db, int iDb){ int i, j; - assert( iDb>=0 && iDb<db->nDb ); + assert( iDb<db->nDb ); - if( iDb==0 ){ - sqlite3BtreeEnterAll(db); + if( iDb>=0 ){ + /* Case 1: Reset the single schema identified by iDb */ + Db *pDb = &db->aDb[iDb]; + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); + assert( pDb->pSchema!=0 ); + sqlite3SchemaClear(pDb->pSchema); + + /* If any database other than TEMP is reset, then also reset TEMP + ** since TEMP might be holding triggers that reference tables in the + ** other database. + */ + if( iDb!=1 ){ + pDb = &db->aDb[1]; + assert( pDb->pSchema!=0 ); + sqlite3SchemaClear(pDb->pSchema); + } + return; } - for(i=iDb; i<db->nDb; i++){ + /* Case 2 (from here to the end): Reset all schemas for all attached + ** databases. */ + assert( iDb<0 ); + sqlite3BtreeEnterAll(db); + for(i=0; i<db->nDb; i++){ Db *pDb = &db->aDb[i]; if( pDb->pSchema ){ - assert(i==1 || (pDb->pBt && sqlite3BtreeHoldsMutex(pDb->pBt))); - sqlite3SchemaFree(pDb->pSchema); + sqlite3SchemaClear(pDb->pSchema); } - if( iDb>0 ) return; } - assert( iDb==0 ); db->flags &= ~SQLITE_InternChanges; sqlite3VtabUnlockList(db); sqlite3BtreeLeaveAll(db); @@ -75192,6 +77861,7 @@ SQLITE_PRIVATE void sqlite3DeleteTable(sqlite3 *db, Table *pTable){ TESTONLY ( Index *pOld = ) sqlite3HashInsert( &pIndex->pSchema->idxHash, zName, sqlite3Strlen30(zName), 0 ); + assert( db==0 || sqlite3SchemaMutexHeld(db, 0, pIndex->pSchema) ); assert( pOld==pIndex || pOld==0 ); } freeIndex(db, pIndex); @@ -75226,6 +77896,7 @@ SQLITE_PRIVATE void sqlite3UnlinkAndDeleteTable(sqlite3 *db, int iDb, const char assert( db!=0 ); assert( iDb>=0 && iDb<db->nDb ); assert( zTabName ); + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); testcase( zTabName[0]==0 ); /* Zero-length table names are allowed */ pDb = &db->aDb[iDb]; p = sqlite3HashInsert(&pDb->pSchema->tblHash, zTabName, @@ -75480,6 +78151,9 @@ SQLITE_PRIVATE void sqlite3StartTable( if( pTable ){ if( !noErr ){ sqlite3ErrorMsg(pParse, "table %T already exists", pName); + }else{ + assert( !db->init.busy ); + sqlite3CodeVerifySchema(pParse, iDb); } goto begin_table_error; } @@ -75510,6 +78184,7 @@ SQLITE_PRIVATE void sqlite3StartTable( */ #ifndef SQLITE_OMIT_AUTOINCREMENT if( !pParse->nested && strcmp(zName, "sqlite_sequence")==0 ){ + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); pTable->pSchema->pSeqTab = pTable; } #endif @@ -75970,6 +78645,7 @@ SQLITE_PRIVATE void sqlite3ChangeCookie(Parse *pParse, int iDb){ int r1 = sqlite3GetTempReg(pParse); sqlite3 *db = pParse->db; Vdbe *v = pParse->pVdbe; + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); sqlite3VdbeAddOp2(v, OP_Integer, db->aDb[iDb].pSchema->schema_cookie+1, r1); sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_SCHEMA_VERSION, r1); sqlite3ReleaseTempReg(pParse, r1); @@ -76077,7 +78753,7 @@ static char *createTableStmt(sqlite3 *db, Table *p){ zSep = zSep2; identPut(zStmt, &k, pCol->zName); assert( pCol->affinity-SQLITE_AFF_TEXT >= 0 ); - assert( pCol->affinity-SQLITE_AFF_TEXT < sizeof(azType)/sizeof(azType[0]) ); + assert( pCol->affinity-SQLITE_AFF_TEXT < ArraySize(azType) ); testcase( pCol->affinity==SQLITE_AFF_TEXT ); testcase( pCol->affinity==SQLITE_AFF_NONE ); testcase( pCol->affinity==SQLITE_AFF_NUMERIC ); @@ -76272,6 +78948,7 @@ SQLITE_PRIVATE void sqlite3EndTable( */ if( p->tabFlags & TF_Autoincrement ){ Db *pDb = &db->aDb[iDb]; + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); if( pDb->pSchema->pSeqTab==0 ){ sqlite3NestedParse(pParse, "CREATE TABLE %Q.sqlite_sequence(name,seq)", @@ -76282,8 +78959,8 @@ SQLITE_PRIVATE void sqlite3EndTable( #endif /* Reparse everything to update our internal data structures */ - sqlite3VdbeAddOp4(v, OP_ParseSchema, iDb, 0, 0, - sqlite3MPrintf(db, "tbl_name='%q'",p->zName), P4_DYNAMIC); + sqlite3VdbeAddParseSchemaOp(v, iDb, + sqlite3MPrintf(db, "tbl_name='%q'", p->zName)); } @@ -76292,6 +78969,7 @@ SQLITE_PRIVATE void sqlite3EndTable( if( db->init.busy ){ Table *pOld; Schema *pSchema = p->pSchema; + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); pOld = sqlite3HashInsert(&pSchema->tblHash, p->zName, sqlite3Strlen30(p->zName),p); if( pOld ){ @@ -76476,6 +79154,7 @@ SQLITE_PRIVATE int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){ pSelTab->nCol = 0; pSelTab->aCol = 0; sqlite3DeleteTable(db, pSelTab); + assert( sqlite3SchemaMutexHeld(db, 0, pTable->pSchema) ); pTable->pSchema->flags |= DB_UnresetViews; }else{ pTable->nCol = 0; @@ -76496,6 +79175,7 @@ SQLITE_PRIVATE int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){ */ static void sqliteViewResetAll(sqlite3 *db, int idx){ HashElem *i; + assert( sqlite3SchemaMutexHeld(db, idx, 0) ); if( !DbHasProperty(db, idx, DB_UnresetViews) ) return; for(i=sqliteHashFirst(&db->aDb[idx].pSchema->tblHash); i;i=sqliteHashNext(i)){ Table *pTab = sqliteHashData(i); @@ -76529,10 +79209,13 @@ static void sqliteViewResetAll(sqlite3 *db, int idx){ ** in order to be certain that we got the right one. */ #ifndef SQLITE_OMIT_AUTOVACUUM -SQLITE_PRIVATE void sqlite3RootPageMoved(Db *pDb, int iFrom, int iTo){ +SQLITE_PRIVATE void sqlite3RootPageMoved(sqlite3 *db, int iDb, int iFrom, int iTo){ HashElem *pElem; Hash *pHash; + Db *pDb; + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); + pDb = &db->aDb[iDb]; pHash = &pDb->pSchema->tblHash; for(pElem=sqliteHashFirst(pHash); pElem; pElem=sqliteHashNext(pElem)){ Table *pTab = sqliteHashData(pElem); @@ -76658,6 +79341,7 @@ SQLITE_PRIVATE void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, if( noErr ) db->suppressErr--; if( pTab==0 ){ + if( noErr ) sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].zDatabase); goto exit_drop_table; } iDb = sqlite3SchemaToIndex(db, pTab->pSchema); @@ -76906,6 +79590,7 @@ SQLITE_PRIVATE void sqlite3CreateForeignKey( pFKey->aAction[0] = (u8)(flags & 0xff); /* ON DELETE action */ pFKey->aAction[1] = (u8)((flags >> 8 ) & 0xff); /* ON UPDATE action */ + assert( sqlite3SchemaMutexHeld(db, 0, p->pSchema) ); pNextTo = (FKey *)sqlite3HashInsert(&p->pSchema->fkeyHash, pFKey->zTo, sqlite3Strlen30(pFKey->zTo), (void *)pFKey ); @@ -77175,6 +79860,9 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex( if( sqlite3FindIndex(db, zName, pDb->zName)!=0 ){ if( !ifNotExist ){ sqlite3ErrorMsg(pParse, "index %s already exists", zName); + }else{ + assert( !db->init.busy ); + sqlite3CodeVerifySchema(pParse, iDb); } goto exit_create_index; } @@ -77261,6 +79949,7 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex( pIndex->onError = (u8)onError; pIndex->autoIndex = (u8)(pName==0); pIndex->pSchema = db->aDb[iDb].pSchema; + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); /* Check to see if we should honor DESC requests on index columns */ @@ -77390,6 +80079,7 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex( */ if( db->init.busy ){ Index *p; + assert( sqlite3SchemaMutexHeld(db, 0, pIndex->pSchema) ); p = sqlite3HashInsert(&pIndex->pSchema->idxHash, pIndex->zName, sqlite3Strlen30(pIndex->zName), pIndex); @@ -77467,9 +80157,8 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex( if( pTblName ){ sqlite3RefillIndex(pParse, pIndex, iMem); sqlite3ChangeCookie(pParse, iDb); - sqlite3VdbeAddOp4(v, OP_ParseSchema, iDb, 0, 0, - sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName), - P4_DYNAMIC); + sqlite3VdbeAddParseSchemaOp(v, iDb, + sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName)); sqlite3VdbeAddOp1(v, OP_Expire, 0); } } @@ -77566,6 +80255,8 @@ SQLITE_PRIVATE void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists if( pIndex==0 ){ if( !ifExists ){ sqlite3ErrorMsg(pParse, "no such index: %S", pName, 0); + }else{ + sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].zDatabase); } pParse->checkSchema = 1; goto exit_drop_index; @@ -78089,7 +80780,7 @@ SQLITE_PRIVATE int sqlite3OpenTempDatabase(Parse *pParse){ SQLITE_OPEN_DELETEONCLOSE | SQLITE_OPEN_TEMP_DB; - rc = sqlite3BtreeOpen(0, db, &pBt, 0, flags); + rc = sqlite3BtreeOpen(db->pVfs, 0, db, &pBt, 0, flags); if( rc!=SQLITE_OK ){ sqlite3ErrorMsg(pParse, "unable to open a temporary database " "file for storing temporary tables"); @@ -78138,12 +80829,13 @@ SQLITE_PRIVATE void sqlite3CodeVerifySchema(Parse *pParse, int iDb){ } if( iDb>=0 ){ sqlite3 *db = pToplevel->db; - int mask; + yDbMask mask; assert( iDb<db->nDb ); assert( db->aDb[iDb].pBt!=0 || iDb==1 ); assert( iDb<SQLITE_MAX_ATTACHED+2 ); - mask = 1<<iDb; + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); + mask = ((yDbMask)1)<<iDb; if( (pToplevel->cookieMask & mask)==0 ){ pToplevel->cookieMask |= mask; pToplevel->cookieValue[iDb] = db->aDb[iDb].pSchema->schema_cookie; @@ -78155,6 +80847,21 @@ SQLITE_PRIVATE void sqlite3CodeVerifySchema(Parse *pParse, int iDb){ } /* +** If argument zDb is NULL, then call sqlite3CodeVerifySchema() for each +** attached database. Otherwise, invoke it for the database named zDb only. +*/ +SQLITE_PRIVATE void sqlite3CodeVerifyNamedSchema(Parse *pParse, const char *zDb){ + sqlite3 *db = pParse->db; + int i; + for(i=0; i<db->nDb; i++){ + Db *pDb = &db->aDb[i]; + if( pDb->pBt && (!zDb || 0==sqlite3StrICmp(zDb, pDb->zName)) ){ + sqlite3CodeVerifySchema(pParse, i); + } + } +} + +/* ** Generate VDBE code that prepares for doing an operation that ** might change the database. ** @@ -78170,7 +80877,7 @@ SQLITE_PRIVATE void sqlite3CodeVerifySchema(Parse *pParse, int iDb){ SQLITE_PRIVATE void sqlite3BeginWriteOperation(Parse *pParse, int setStatement, int iDb){ Parse *pToplevel = sqlite3ParseToplevel(pParse); sqlite3CodeVerifySchema(pParse, iDb); - pToplevel->writeMask |= 1<<iDb; + pToplevel->writeMask |= ((yDbMask)1)<<iDb; pToplevel->isMultiWrite |= setStatement; } @@ -78270,6 +80977,7 @@ static void reindexDatabases(Parse *pParse, char const *zColl){ HashElem *k; /* For looping over tables in pDb */ Table *pTab; /* A table in the database */ + assert( sqlite3BtreeHoldsAllMutexes(db) ); /* Needed for schema access */ for(iDb=0, pDb=db->aDb; iDb<db->nDb; iDb++, pDb++){ assert( pDb!=0 ); for(k=sqliteHashFirst(&pDb->pSchema->tblHash); k; k=sqliteHashNext(k)){ @@ -78788,12 +81496,12 @@ SQLITE_PRIVATE FuncDef *sqlite3FindFunction( /* ** Free all resources held by the schema structure. The void* argument points ** at a Schema struct. This function does not call sqlite3DbFree(db, ) on the -** pointer itself, it just cleans up subsiduary resources (i.e. the contents +** pointer itself, it just cleans up subsidiary resources (i.e. the contents ** of the schema hash tables). ** ** The Schema.cache_size variable is not cleared. */ -SQLITE_PRIVATE void sqlite3SchemaFree(void *p){ +SQLITE_PRIVATE void sqlite3SchemaClear(void *p){ Hash temp1; Hash temp2; HashElem *pElem; @@ -78815,7 +81523,10 @@ SQLITE_PRIVATE void sqlite3SchemaFree(void *p){ sqlite3HashClear(&temp1); sqlite3HashClear(&pSchema->fkeyHash); pSchema->pSeqTab = 0; - pSchema->flags &= ~DB_SchemaLoaded; + if( pSchema->flags & DB_SchemaLoaded ){ + pSchema->iGeneration++; + pSchema->flags &= ~DB_SchemaLoaded; + } } /* @@ -78825,7 +81536,7 @@ SQLITE_PRIVATE void sqlite3SchemaFree(void *p){ SQLITE_PRIVATE Schema *sqlite3SchemaGet(sqlite3 *db, Btree *pBt){ Schema * p; if( pBt ){ - p = (Schema *)sqlite3BtreeSchema(pBt, sizeof(Schema), sqlite3SchemaFree); + p = (Schema *)sqlite3BtreeSchema(pBt, sizeof(Schema), sqlite3SchemaClear); }else{ p = (Schema *)sqlite3DbMallocZero(0, sizeof(Schema)); } @@ -78859,9 +81570,18 @@ SQLITE_PRIVATE Schema *sqlite3SchemaGet(sqlite3 *db, Btree *pBt){ */ /* -** Look up every table that is named in pSrc. If any table is not found, -** add an error message to pParse->zErrMsg and return NULL. If all tables -** are found, return a pointer to the last table. +** While a SrcList can in general represent multiple tables and subqueries +** (as in the FROM clause of a SELECT statement) in this case it contains +** the name of a single table, as one might find in an INSERT, DELETE, +** or UPDATE statement. Look up that table in the symbol table and +** return a pointer. Set an error message and return NULL if the table +** name is not found or if any other error occurs. +** +** The following fields are initialized appropriate in pSrc: +** +** pSrc->a[0].pTab Pointer to the Table object +** pSrc->a[0].pIndex Pointer to the INDEXED BY index, if there is one +** */ SQLITE_PRIVATE Table *sqlite3SrcListLookup(Parse *pParse, SrcList *pSrc){ struct SrcList_item *pItem = pSrc->a; @@ -79236,6 +81956,7 @@ SQLITE_PRIVATE void sqlite3DeleteFrom( const char *pVTab = (const char *)sqlite3GetVTable(db, pTab); sqlite3VtabMakeWritable(pParse, pTab); sqlite3VdbeAddOp4(v, OP_VUpdate, 0, 1, iRowid, pVTab, P4_VTAB); + sqlite3VdbeChangeP5(v, OE_Abort); sqlite3MayAbort(pParse); }else #endif @@ -79380,7 +82101,7 @@ SQLITE_PRIVATE void sqlite3GenerateRowDelete( sqlite3GenerateRowIndexDelete(pParse, pTab, iCur, 0); sqlite3VdbeAddOp2(v, OP_Delete, iCur, (count?OPFLAG_NCHANGE:0)); if( count ){ - sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_STATIC); + sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_TRANSIENT); } } @@ -79470,8 +82191,14 @@ SQLITE_PRIVATE int sqlite3GenerateIndexKey( } } if( doMakeRec ){ + const char *zAff; + if( pTab->pSelect || (pParse->db->flags & SQLITE_IdxRealAsInt)!=0 ){ + zAff = 0; + }else{ + zAff = sqlite3IndexAffinityStr(v, pIdx); + } sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol+1, regOut); - sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v, pIdx), 0); + sqlite3VdbeChangeP4(v, -1, zAff, P4_TRANSIENT); } sqlite3ReleaseTempRange(pParse, regBase, nCol+1); return regBase; @@ -79983,10 +82710,10 @@ struct compareInfo { ** whereas only characters less than 0x80 do in ASCII. */ #if defined(SQLITE_EBCDIC) -# define sqlite3Utf8Read(A,C) (*(A++)) -# define GlogUpperToLower(A) A = sqlite3UpperToLower[A] +# define sqlite3Utf8Read(A,C) (*(A++)) +# define GlogUpperToLower(A) A = sqlite3UpperToLower[A] #else -# define GlogUpperToLower(A) if( A<0x80 ){ A = sqlite3UpperToLower[A]; } +# define GlogUpperToLower(A) if( !((A)&~0x7f) ){ A = sqlite3UpperToLower[A]; } #endif static const struct compareInfo globInfo = { '*', '?', '[', 0 }; @@ -80029,9 +82756,9 @@ static int patternCompare( const u8 *zPattern, /* The glob pattern */ const u8 *zString, /* The string to compare against the glob */ const struct compareInfo *pInfo, /* Information about how to do the compare */ - const int esc /* The escape character */ + u32 esc /* The escape character */ ){ - int c, c2; + u32 c, c2; int invert; int seen; u8 matchOne = pInfo->matchOne; @@ -80085,7 +82812,7 @@ static int patternCompare( return 0; } }else if( c==matchSet ){ - int prior_c = 0; + u32 prior_c = 0; assert( esc==0 ); /* This only occurs for GLOB, not LIKE */ seen = 0; invert = 0; @@ -80161,7 +82888,7 @@ static void likeFunc( sqlite3_value **argv ){ const unsigned char *zA, *zB; - int escape = 0; + u32 escape = 0; int nPat; sqlite3 *db = sqlite3_context_db_handle(context); @@ -80252,6 +82979,21 @@ static void sourceidFunc( } /* +** Implementation of the sqlite_log() function. This is a wrapper around +** sqlite3_log(). The return value is NULL. The function exists purely for +** its side-effects. +*/ +static void errlogFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + UNUSED_PARAMETER(argc); + UNUSED_PARAMETER(context); + sqlite3_log(sqlite3_value_int(argv[0]), "%s", sqlite3_value_text(argv[1])); +} + +/* ** Implementation of the sqlite_compileoption_used() function. ** The result is an integer that identifies if the compiler option ** was used to build SQLite. @@ -80716,13 +83458,8 @@ static void sumStep(sqlite3_context *context, int argc, sqlite3_value **argv){ if( type==SQLITE_INTEGER ){ i64 v = sqlite3_value_int64(argv[0]); p->rSum += v; - if( (p->approx|p->overflow)==0 ){ - i64 iNewSum = p->iSum + v; - int s1 = (int)(p->iSum >> (sizeof(i64)*8-1)); - int s2 = (int)(v >> (sizeof(i64)*8-1)); - int s3 = (int)(iNewSum >> (sizeof(i64)*8-1)); - p->overflow = ((s1&s2&~s3) | (~s1&~s2&s3))?1:0; - p->iSum = iNewSum; + if( (p->approx|p->overflow)==0 && sqlite3AddInt64(&p->iSum, v) ){ + p->overflow = 1; } }else{ p->rSum += sqlite3_value_double(argv[0]); @@ -80927,9 +83664,9 @@ SQLITE_PRIVATE void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive) }else{ pInfo = (struct compareInfo*)&likeInfoNorm; } - sqlite3CreateFunc(db, "like", 2, SQLITE_ANY, pInfo, likeFunc, 0, 0, 0); - sqlite3CreateFunc(db, "like", 3, SQLITE_ANY, pInfo, likeFunc, 0, 0, 0); - sqlite3CreateFunc(db, "glob", 2, SQLITE_ANY, + sqlite3CreateFunc(db, "like", 2, SQLITE_UTF8, pInfo, likeFunc, 0, 0, 0); + sqlite3CreateFunc(db, "like", 3, SQLITE_UTF8, pInfo, likeFunc, 0, 0, 0); + sqlite3CreateFunc(db, "glob", 2, SQLITE_UTF8, (struct compareInfo*)&globInfo, likeFunc, 0, 0, 0); setLikeOptFlag(db, "glob", SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE); setLikeOptFlag(db, "like", @@ -81023,6 +83760,7 @@ SQLITE_PRIVATE void sqlite3RegisterGlobalFunctions(void){ FUNCTION(nullif, 2, 0, 1, nullifFunc ), FUNCTION(sqlite_version, 0, 0, 0, versionFunc ), FUNCTION(sqlite_source_id, 0, 0, 0, sourceidFunc ), + FUNCTION(sqlite_log, 2, 0, 0, errlogFunc ), #ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS FUNCTION(sqlite_compileoption_used,1, 0, 0, compileoptionusedFunc ), FUNCTION(sqlite_compileoption_get, 1, 0, 0, compileoptiongetFunc ), @@ -81461,19 +84199,31 @@ static void fkLookupParent( /* If the parent table is the same as the child table, and we are about ** to increment the constraint-counter (i.e. this is an INSERT operation), ** then check if the row being inserted matches itself. If so, do not - ** increment the constraint-counter. */ + ** increment the constraint-counter. + ** + ** If any of the parent-key values are NULL, then the row cannot match + ** itself. So set JUMPIFNULL to make sure we do the OP_Found if any + ** of the parent-key values are NULL (at this point it is known that + ** none of the child key values are). + */ if( pTab==pFKey->pFrom && nIncr==1 ){ int iJump = sqlite3VdbeCurrentAddr(v) + nCol + 1; for(i=0; i<nCol; i++){ int iChild = aiCol[i]+1+regData; int iParent = pIdx->aiColumn[i]+1+regData; + assert( aiCol[i]!=pTab->iPKey ); + if( pIdx->aiColumn[i]==pTab->iPKey ){ + /* The parent key is a composite key that includes the IPK column */ + iParent = regData; + } sqlite3VdbeAddOp3(v, OP_Ne, iChild, iJump, iParent); + sqlite3VdbeChangeP5(v, SQLITE_JUMPIFNULL); } sqlite3VdbeAddOp2(v, OP_Goto, 0, iOk); } sqlite3VdbeAddOp3(v, OP_MakeRecord, regTemp, nCol, regRec); - sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v, pIdx), 0); + sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v,pIdx), P4_TRANSIENT); sqlite3VdbeAddOp4Int(v, OP_Found, iCur, iOk, regRec, 0); sqlite3ReleaseTempReg(pParse, regRec); @@ -81762,7 +84512,6 @@ SQLITE_PRIVATE void sqlite3FkCheck( int regNew /* New row data is stored here */ ){ sqlite3 *db = pParse->db; /* Database handle */ - Vdbe *v; /* VM to write code to */ FKey *pFKey; /* Used to iterate through FKs */ int iDb; /* Index of database containing pTab */ const char *zDb; /* Name of database containing pTab */ @@ -81774,7 +84523,6 @@ SQLITE_PRIVATE void sqlite3FkCheck( /* If foreign-keys are disabled, this function is a no-op. */ if( (db->flags&SQLITE_ForeignKeys)==0 ) return; - v = sqlite3GetVdbe(pParse); iDb = sqlite3SchemaToIndex(db, pTab->pSchema); zDb = db->aDb[iDb].zName; @@ -82231,6 +84979,7 @@ SQLITE_PRIVATE void sqlite3FkDelete(sqlite3 *db, Table *pTab){ FKey *pFKey; /* Iterator variable */ FKey *pNext; /* Copy of pFKey->pNextFrom */ + assert( db==0 || sqlite3SchemaMutexHeld(db, 0, pTab->pSchema) ); for(pFKey=pTab->pFKey; pFKey; pFKey=pNext){ /* Remove the FK from the fkeyHash hash table. */ @@ -82390,7 +85139,7 @@ SQLITE_PRIVATE void sqlite3TableAffinityStr(Vdbe *v, Table *pTab){ pTab->zColAff = zColAff; } - sqlite3VdbeChangeP4(v, -1, pTab->zColAff, 0); + sqlite3VdbeChangeP4(v, -1, pTab->zColAff, P4_TRANSIENT); } /* @@ -82504,6 +85253,7 @@ SQLITE_PRIVATE void sqlite3AutoincrementBegin(Parse *pParse){ for(p = pParse->pAinc; p; p = p->pNext){ pDb = &db->aDb[p->iDb]; memId = p->regCtr; + assert( sqlite3SchemaMutexHeld(db, 0, pDb->pSchema) ); sqlite3OpenTable(pParse, 0, p->iDb, pDb->pSchema->pSeqTab, OP_OpenRead); addr = sqlite3VdbeCurrentAddr(v); sqlite3VdbeAddOp4(v, OP_String8, 0, memId-1, 0, p->pTab->zName, 0); @@ -82554,6 +85304,7 @@ SQLITE_PRIVATE void sqlite3AutoincrementEnd(Parse *pParse){ int memId = p->regCtr; iRec = sqlite3GetTempReg(pParse); + assert( sqlite3SchemaMutexHeld(db, 0, pDb->pSchema) ); sqlite3OpenTable(pParse, 0, p->iDb, pDb->pSchema->pSeqTab, OP_OpenWrite); j1 = sqlite3VdbeAddOp1(v, OP_NotNull, memId+1); j2 = sqlite3VdbeAddOp0(v, OP_Rewind); @@ -82732,7 +85483,6 @@ SQLITE_PRIVATE void sqlite3Insert( int regIns; /* Block of regs holding rowid+data being inserted */ int regRowid; /* registers holding insert rowid */ int regData; /* register holding first column to insert */ - int regRecord; /* Holds the assemblied row record */ int regEof = 0; /* Register recording end of SELECT data */ int *aRegIdx = 0; /* One register allocated to each index */ @@ -83061,7 +85811,6 @@ SQLITE_PRIVATE void sqlite3Insert( /* Allocate registers for holding the rowid of the new row, ** the content of the new row, and the assemblied row record. */ - regRecord = ++pParse->nMem; regRowid = regIns = pParse->nMem+1; pParse->nMem += pTab->nCol + 1; if( IsVirtual(pTab) ){ @@ -83236,6 +85985,7 @@ SQLITE_PRIVATE void sqlite3Insert( const char *pVTab = (const char *)sqlite3GetVTable(db, pTab); sqlite3VtabMakeWritable(pParse, pTab); sqlite3VdbeAddOp4(v, OP_VUpdate, 1, pTab->nCol+2, regIns, pVTab, P4_VTAB); + sqlite3VdbeChangeP5(v, onError==OE_Default ? OE_Abort : onError); sqlite3MayAbort(pParse); }else #endif @@ -83455,7 +86205,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( case OE_Rollback: case OE_Fail: { char *zMsg; - j1 = sqlite3VdbeAddOp3(v, OP_HaltIfNull, + sqlite3VdbeAddOp3(v, OP_HaltIfNull, SQLITE_CONSTRAINT, onError, regData+i); zMsg = sqlite3MPrintf(pParse->db, "%s.%s may not be NULL", pTab->zName, pTab->aCol[i].zName); @@ -83595,7 +86345,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( } sqlite3VdbeAddOp2(v, OP_SCopy, regRowid, regIdx+i); sqlite3VdbeAddOp3(v, OP_MakeRecord, regIdx, pIdx->nColumn+1, aRegIdx[iCur]); - sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v, pIdx), 0); + sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v, pIdx), P4_TRANSIENT); sqlite3ExprCacheAffinityChange(pParse, regIdx, pIdx->nColumn+1); /* Find out what action to take in case there is an indexing conflict */ @@ -83735,7 +86485,7 @@ SQLITE_PRIVATE void sqlite3CompleteInsertion( } sqlite3VdbeAddOp3(v, OP_Insert, baseCur, regRec, regRowid); if( !pParse->nested ){ - sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_STATIC); + sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_TRANSIENT); } sqlite3VdbeChangeP5(v, pik_flags); } @@ -84001,6 +86751,18 @@ static int xferOptimization( return 0; /* Tables have different CHECK constraints. Ticket #2252 */ } #endif +#ifndef SQLITE_OMIT_FOREIGN_KEY + /* Disallow the transfer optimization if the destination table constains + ** any foreign key constraints. This is more restrictive than necessary. + ** But the main beneficiary of the transfer optimization is the VACUUM + ** command, and the VACUUM command disables foreign key constraints. So + ** the extra complication to make this rule less restrictive is probably + ** not worth the effort. Ticket [6284df89debdfa61db8073e062908af0c9b6118e] + */ + if( (pParse->db->flags & SQLITE_ForeignKeys)!=0 && pDest->pFKey!=0 ){ + return 0; + } +#endif /* If we get this far, it means either: ** @@ -84739,6 +87501,11 @@ struct sqlite3_api_routines { # define sqlite3_complete16 0 #endif +#ifdef SQLITE_OMIT_DECLTYPE +# define sqlite3_column_decltype16 0 +# define sqlite3_column_decltype 0 +#endif + #ifdef SQLITE_OMIT_PROGRESS_CALLBACK # define sqlite3_progress_handler 0 #endif @@ -85328,10 +88095,6 @@ SQLITE_PRIVATE void sqlite3AutoLoadExtensions(sqlite3 *db){ ** This file contains code used to implement the PRAGMA command. */ -/* Ignore this whole file if pragmas are disabled -*/ -#if !defined(SQLITE_OMIT_PRAGMA) - /* ** Interpret the given string as a safety level. Return 0 for OFF, ** 1 for ON or NORMAL and 2 for FULL. Return 1 for an empty or @@ -85364,10 +88127,16 @@ static u8 getSafetyLevel(const char *z){ /* ** Interpret the given string as a boolean value. */ -static u8 getBoolean(const char *z){ +SQLITE_PRIVATE u8 sqlite3GetBoolean(const char *z){ return getSafetyLevel(z)&1; } +/* The sqlite3GetBoolean() function is used by other modules but the +** remainder of this file is specific to PRAGMA processing. So omit +** the rest of the file if PRAGMAs are omitted from the build. +*/ +#if !defined(SQLITE_OMIT_PRAGMA) + /* ** Interpret the given string as a locking mode value. */ @@ -85430,7 +88199,7 @@ static int invalidateTempStorage(Parse *pParse){ } sqlite3BtreeClose(db->aDb[1].pBt); db->aDb[1].pBt = 0; - sqlite3ResetInternalSchema(db, 0); + sqlite3ResetInternalSchema(db, -1); } return SQLITE_OK; } @@ -85534,7 +88303,7 @@ static int flagPragma(Parse *pParse, const char *zLeft, const char *zRight){ mask &= ~(SQLITE_ForeignKeys); } - if( getBoolean(zRight) ){ + if( sqlite3GetBoolean(zRight) ){ db->flags |= mask; }else{ db->flags &= ~mask; @@ -85699,11 +88468,11 @@ SQLITE_PRIVATE void sqlite3Pragma( sqlite3VdbeChangeP1(v, addr+1, iDb); sqlite3VdbeChangeP1(v, addr+6, SQLITE_DEFAULT_CACHE_SIZE); }else{ - int size = sqlite3Atoi(zRight); - if( size<0 ) size = -size; + int size = sqlite3AbsInt32(sqlite3Atoi(zRight)); sqlite3BeginWriteOperation(pParse, 0, iDb); sqlite3VdbeAddOp2(v, OP_Integer, size, 1); sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_DEFAULT_CACHE_SIZE, 1); + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); pDb->pSchema->cache_size = size; sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size); } @@ -85748,7 +88517,7 @@ SQLITE_PRIVATE void sqlite3Pragma( int b = -1; assert( pBt!=0 ); if( zRight ){ - b = getBoolean(zRight); + b = sqlite3GetBoolean(zRight); } if( pId2->n==0 && b>=0 ){ int ii; @@ -86006,11 +88775,11 @@ SQLITE_PRIVATE void sqlite3Pragma( */ if( sqlite3StrICmp(zLeft,"cache_size")==0 ){ if( sqlite3ReadSchema(pParse) ) goto pragma_out; + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); if( !zRight ){ returnSingleInt(pParse, "cache_size", pDb->pSchema->cache_size); }else{ - int size = sqlite3Atoi(zRight); - if( size<0 ) size = -size; + int size = sqlite3AbsInt32(sqlite3Atoi(zRight)); pDb->pSchema->cache_size = size; sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size); } @@ -86348,7 +89117,7 @@ SQLITE_PRIVATE void sqlite3Pragma( #ifndef NDEBUG if( sqlite3StrICmp(zLeft, "parser_trace")==0 ){ if( zRight ){ - if( getBoolean(zRight) ){ + if( sqlite3GetBoolean(zRight) ){ sqlite3ParserTrace(stderr, "parser: "); }else{ sqlite3ParserTrace(0, 0); @@ -86362,7 +89131,7 @@ SQLITE_PRIVATE void sqlite3Pragma( */ if( sqlite3StrICmp(zLeft, "case_sensitive_like")==0 ){ if( zRight ){ - sqlite3RegisterLikeFunctions(db, getBoolean(zRight)); + sqlite3RegisterLikeFunctions(db, sqlite3GetBoolean(zRight)); } }else @@ -86427,6 +89196,7 @@ SQLITE_PRIVATE void sqlite3Pragma( ** Begin by filling registers 2, 3, ... with the root pages numbers ** for all tables and indices in the database. */ + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); pTbls = &db->aDb[i].pSchema->tblHash; for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){ Table *pTab = sqliteHashData(x); @@ -86492,7 +89262,7 @@ SQLITE_PRIVATE void sqlite3Pragma( addr = sqlite3VdbeAddOpList(v, ArraySize(idxErr), idxErr); sqlite3VdbeChangeP4(v, addr+1, "rowid ", P4_STATIC); sqlite3VdbeChangeP4(v, addr+3, " missing from index ", P4_STATIC); - sqlite3VdbeChangeP4(v, addr+4, pIdx->zName, P4_STATIC); + sqlite3VdbeChangeP4(v, addr+4, pIdx->zName, P4_TRANSIENT); sqlite3VdbeJumpHere(v, addr+9); sqlite3VdbeJumpHere(v, jmp2); } @@ -86522,7 +89292,7 @@ SQLITE_PRIVATE void sqlite3Pragma( sqlite3VdbeJumpHere(v, addr+4); sqlite3VdbeChangeP4(v, addr+6, "wrong # of entries in index ", P4_STATIC); - sqlite3VdbeChangeP4(v, addr+7, pIdx->zName, P4_STATIC); + sqlite3VdbeChangeP4(v, addr+7, pIdx->zName, P4_TRANSIENT); } } } @@ -86701,13 +89471,29 @@ SQLITE_PRIVATE void sqlite3Pragma( #ifndef SQLITE_OMIT_WAL /* - ** PRAGMA [database.]wal_checkpoint + ** PRAGMA [database.]wal_checkpoint = passive|full|restart ** ** Checkpoint the database. */ if( sqlite3StrICmp(zLeft, "wal_checkpoint")==0 ){ + int iBt = (pId2->z?iDb:SQLITE_MAX_ATTACHED); + int eMode = SQLITE_CHECKPOINT_PASSIVE; + if( zRight ){ + if( sqlite3StrICmp(zRight, "full")==0 ){ + eMode = SQLITE_CHECKPOINT_FULL; + }else if( sqlite3StrICmp(zRight, "restart")==0 ){ + eMode = SQLITE_CHECKPOINT_RESTART; + } + } if( sqlite3ReadSchema(pParse) ) goto pragma_out; - sqlite3VdbeAddOp3(v, OP_Checkpoint, pId2->z?iDb:SQLITE_MAX_ATTACHED, 0, 0); + sqlite3VdbeSetNumCols(v, 3); + pParse->nMem = 3; + sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "busy", SQLITE_STATIC); + sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "log", SQLITE_STATIC); + sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "checkpointed", SQLITE_STATIC); + + sqlite3VdbeAddOp3(v, OP_Checkpoint, iBt, eMode, 1); + sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 3); }else /* @@ -86858,7 +89644,7 @@ static void corruptSchema( "%s - %s", *pData->pzErrMsg, zExtra); } } - pData->rc = db->mallocFailed ? SQLITE_NOMEM : SQLITE_CORRUPT; + pData->rc = db->mallocFailed ? SQLITE_NOMEM : SQLITE_CORRUPT_BKPT; } /* @@ -86965,7 +89751,7 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){ int meta[5]; InitData initData; char const *zMasterSchema; - char const *zMasterName = SCHEMA_TABLE(iDb); + char const *zMasterName; int openedTransaction = 0; /* @@ -87102,9 +89888,8 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){ pDb->pSchema->enc = ENC(db); if( pDb->pSchema->cache_size==0 ){ - size = meta[BTREE_DEFAULT_CACHE_SIZE-1]; + size = sqlite3AbsInt32(meta[BTREE_DEFAULT_CACHE_SIZE-1]); if( size==0 ){ size = SQLITE_DEFAULT_CACHE_SIZE; } - if( size<0 ) size = -size; pDb->pSchema->cache_size = size; sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size); } @@ -87163,7 +89948,7 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){ } if( db->mallocFailed ){ rc = SQLITE_NOMEM; - sqlite3ResetInternalSchema(db, 0); + sqlite3ResetInternalSchema(db, -1); } if( rc==SQLITE_OK || (db->flags&SQLITE_RecoveryMode)){ /* Black magic: If the SQLITE_RecoveryMode flag is set, then consider @@ -87295,7 +90080,9 @@ static void schemaIsValid(Parse *pParse){ ** value stored as part of the in-memory schema representation, ** set Parse.rc to SQLITE_SCHEMA. */ sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&cookie); + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); if( cookie!=db->aDb[iDb].pSchema->schema_cookie ){ + sqlite3ResetInternalSchema(db, iDb); pParse->rc = SQLITE_SCHEMA; } @@ -87437,9 +90224,6 @@ static int sqlite3Prepare( if( pParse->checkSchema ){ schemaIsValid(pParse); } - if( pParse->rc==SQLITE_SCHEMA ){ - sqlite3ResetInternalSchema(db, 0); - } if( db->mallocFailed ){ pParse->rc = SQLITE_NOMEM; } @@ -87608,7 +90392,7 @@ SQLITE_API int sqlite3_prepare_v2( */ static int sqlite3Prepare16( sqlite3 *db, /* Database handle. */ - const void *zSql, /* UTF-8 encoded SQL statement. */ + const void *zSql, /* UTF-16 encoded SQL statement. */ int nBytes, /* Length of zSql in bytes. */ int saveSqlFlag, /* True to save SQL text into the sqlite3_stmt */ sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */ @@ -87658,7 +90442,7 @@ static int sqlite3Prepare16( */ SQLITE_API int sqlite3_prepare16( sqlite3 *db, /* Database handle. */ - const void *zSql, /* UTF-8 encoded SQL statement. */ + const void *zSql, /* UTF-16 encoded SQL statement. */ int nBytes, /* Length of zSql in bytes. */ sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */ const void **pzTail /* OUT: End of parsed string */ @@ -87670,7 +90454,7 @@ SQLITE_API int sqlite3_prepare16( } SQLITE_API int sqlite3_prepare16_v2( sqlite3 *db, /* Database handle. */ - const void *zSql, /* UTF-8 encoded SQL statement. */ + const void *zSql, /* UTF-16 encoded SQL statement. */ int nBytes, /* Length of zSql in bytes. */ sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */ const void **pzTail /* OUT: End of parsed string */ @@ -88493,6 +91277,22 @@ static void explainTempTable(Parse *pParse, const char *zUsage){ } /* +** Assign expression b to lvalue a. A second, no-op, version of this macro +** is provided when SQLITE_OMIT_EXPLAIN is defined. This allows the code +** in sqlite3Select() to assign values to structure member variables that +** only exist if SQLITE_OMIT_EXPLAIN is not defined without polluting the +** code with #ifndef directives. +*/ +# define explainSetInteger(a, b) a = b + +#else +/* No-op versions of the explainXXX() functions and macros. */ +# define explainTempTable(y,z) +# define explainSetInteger(y,z) +#endif + +#if !defined(SQLITE_OMIT_EXPLAIN) && !defined(SQLITE_OMIT_COMPOUND_SELECT) +/* ** Unless an "EXPLAIN QUERY PLAN" command is being processed, this function ** is a no-op. Otherwise, it adds a single row of output to the EQP result, ** where the caption is of one of the two forms: @@ -88523,21 +91323,9 @@ static void explainComposite( sqlite3VdbeAddOp4(v, OP_Explain, pParse->iSelectId, 0, 0, zMsg, P4_DYNAMIC); } } - -/* -** Assign expression b to lvalue a. A second, no-op, version of this macro -** is provided when SQLITE_OMIT_EXPLAIN is defined. This allows the code -** in sqlite3Select() to assign values to structure member variables that -** only exist if SQLITE_OMIT_EXPLAIN is not defined without polluting the -** code with #ifndef directives. -*/ -# define explainSetInteger(a, b) a = b - #else /* No-op versions of the explainXXX() functions and macros. */ -# define explainTempTable(y,z) # define explainComposite(v,w,x,y,z) -# define explainSetInteger(y,z) #endif /* @@ -90338,6 +93126,9 @@ static void substSelect( ** appear as unmodified result columns in the outer query. But ** have other optimizations in mind to deal with that case. ** +** (21) The subquery does not use LIMIT or the outer query is not +** DISTINCT. (See ticket [752e1646fc]). +** ** In this routine, the "p" parameter is a pointer to the outer query. ** The subquery is p->pSrc->a[iFrom]. isAgg is true if the outer query ** uses aggregates and subqueryIsAgg is true if the subquery uses aggregates. @@ -90406,6 +93197,9 @@ static int flattenSubquery( } if( isAgg && pSub->pOrderBy ) return 0; /* Restriction (16) */ if( pSub->pLimit && p->pWhere ) return 0; /* Restriction (19) */ + if( pSub->pLimit && (p->selFlags & SF_Distinct)!=0 ){ + return 0; /* Restriction (21) */ + } /* OBSOLETE COMMENT 1: ** Restriction 3: If the subquery is a join, make sure the subquery is @@ -91299,6 +94093,32 @@ static void updateAccumulator(Parse *pParse, AggInfo *pAggInfo){ } /* +** Add a single OP_Explain instruction to the VDBE to explain a simple +** count(*) query ("SELECT count(*) FROM pTab"). +*/ +#ifndef SQLITE_OMIT_EXPLAIN +static void explainSimpleCount( + Parse *pParse, /* Parse context */ + Table *pTab, /* Table being queried */ + Index *pIdx /* Index used to optimize scan, or NULL */ +){ + if( pParse->explain==2 ){ + char *zEqp = sqlite3MPrintf(pParse->db, "SCAN TABLE %s %s%s(~%d rows)", + pTab->zName, + pIdx ? "USING COVERING INDEX " : "", + pIdx ? pIdx->zName : "", + pTab->nRowEst + ); + sqlite3VdbeAddOp4( + pParse->pVdbe, OP_Explain, pParse->iSelectId, 0, 0, zEqp, P4_DYNAMIC + ); + } +} +#else +# define explainSimpleCount(a,b,c) +#endif + +/* ** Generate code for the SELECT statement given in the p argument. ** ** The results are distributed in various ways depending on the @@ -91889,11 +94709,13 @@ SQLITE_PRIVATE int sqlite3Select( ** and pKeyInfo to the KeyInfo structure required to navigate the ** index. ** + ** (2011-04-15) Do not do a full scan of an unordered index. + ** ** In practice the KeyInfo structure will not be used. It is only ** passed to keep OP_OpenRead happy. */ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ - if( !pBest || pIdx->nColumn<pBest->nColumn ){ + if( pIdx->bUnordered==0 && (!pBest || pIdx->nColumn<pBest->nColumn) ){ pBest = pIdx; } } @@ -91909,6 +94731,7 @@ SQLITE_PRIVATE int sqlite3Select( } sqlite3VdbeAddOp2(v, OP_Count, iCsr, sAggInfo.aFunc[0].iMem); sqlite3VdbeAddOp1(v, OP_Close, iCsr); + explainSimpleCount(pParse, pTab, pBest); }else #endif /* SQLITE_OMIT_BTREECOUNT */ { @@ -92367,6 +95190,7 @@ SQLITE_PRIVATE Trigger *sqlite3TriggerList(Parse *pParse, Table *pTab){ if( pTmpSchema!=pTab->pSchema ){ HashElem *p; + assert( sqlite3SchemaMutexHeld(pParse->db, 0, pTmpSchema) ); for(p=sqliteHashFirst(&pTmpSchema->trigHash); p; p=sqliteHashNext(p)){ Trigger *pTrig = (Trigger *)sqliteHashData(p); if( pTrig->pTabSchema==pTab->pSchema @@ -92478,10 +95302,14 @@ SQLITE_PRIVATE void sqlite3BeginTrigger( if( !zName || SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){ goto trigger_cleanup; } + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); if( sqlite3HashFind(&(db->aDb[iDb].pSchema->trigHash), zName, sqlite3Strlen30(zName)) ){ if( !noErr ){ sqlite3ErrorMsg(pParse, "trigger %T already exists", pName); + }else{ + assert( !db->init.busy ); + sqlite3CodeVerifySchema(pParse, iDb); } goto trigger_cleanup; } @@ -92575,7 +95403,6 @@ SQLITE_PRIVATE void sqlite3FinishTrigger( int iDb; /* Database containing the trigger */ Token nameToken; /* Trigger name for error reporting */ - pTrig = pParse->pNewTrigger; pParse->pNewTrigger = 0; if( NEVER(pParse->nErr) || !pTrig ) goto triggerfinish_cleanup; zName = pTrig->zName; @@ -92610,14 +95437,14 @@ SQLITE_PRIVATE void sqlite3FinishTrigger( pTrig->table, z); sqlite3DbFree(db, z); sqlite3ChangeCookie(pParse, iDb); - sqlite3VdbeAddOp4(v, OP_ParseSchema, iDb, 0, 0, sqlite3MPrintf( - db, "type='trigger' AND name='%q'", zName), P4_DYNAMIC - ); + sqlite3VdbeAddParseSchemaOp(v, iDb, + sqlite3MPrintf(db, "type='trigger' AND name='%q'", zName)); } if( db->init.busy ){ Trigger *pLink = pTrig; Hash *pHash = &db->aDb[iDb].pSchema->trigHash; + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); pTrig = sqlite3HashInsert(pHash, zName, sqlite3Strlen30(zName), pTrig); if( pTrig ){ db->mallocFailed = 1; @@ -92799,15 +95626,19 @@ SQLITE_PRIVATE void sqlite3DropTrigger(Parse *pParse, SrcList *pName, int noErr) zDb = pName->a[0].zDatabase; zName = pName->a[0].zName; nName = sqlite3Strlen30(zName); + assert( zDb!=0 || sqlite3BtreeHoldsAllMutexes(db) ); for(i=OMIT_TEMPDB; i<db->nDb; i++){ int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */ if( zDb && sqlite3StrICmp(db->aDb[j].zName, zDb) ) continue; + assert( sqlite3SchemaMutexHeld(db, j, 0) ); pTrigger = sqlite3HashFind(&(db->aDb[j].pSchema->trigHash), zName, nName); if( pTrigger ) break; } if( !pTrigger ){ if( !noErr ){ sqlite3ErrorMsg(pParse, "no such trigger: %S", pName, 0); + }else{ + sqlite3CodeVerifyNamedSchema(pParse, zDb); } pParse->checkSchema = 1; goto drop_trigger_cleanup; @@ -92875,7 +95706,7 @@ SQLITE_PRIVATE void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){ sqlite3BeginWriteOperation(pParse, 0, iDb); sqlite3OpenMasterTable(pParse, iDb); base = sqlite3VdbeAddOpList(v, ArraySize(dropTrigger), dropTrigger); - sqlite3VdbeChangeP4(v, base+1, pTrigger->zName, 0); + sqlite3VdbeChangeP4(v, base+1, pTrigger->zName, P4_TRANSIENT); sqlite3VdbeChangeP4(v, base+4, "trigger", P4_STATIC); sqlite3ChangeCookie(pParse, iDb); sqlite3VdbeAddOp2(v, OP_Close, 0, 0); @@ -92890,8 +95721,11 @@ SQLITE_PRIVATE void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){ ** Remove a trigger from the hash tables of the sqlite* pointer. */ SQLITE_PRIVATE void sqlite3UnlinkAndDeleteTrigger(sqlite3 *db, int iDb, const char *zName){ - Hash *pHash = &(db->aDb[iDb].pSchema->trigHash); Trigger *pTrigger; + Hash *pHash; + + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); + pHash = &(db->aDb[iDb].pSchema->trigHash); pTrigger = sqlite3HashInsert(pHash, zName, sqlite3Strlen30(zName), 0); if( ALWAYS(pTrigger) ){ if( pTrigger->pSchema==pTrigger->pTabSchema ){ @@ -92937,8 +95771,12 @@ SQLITE_PRIVATE Trigger *sqlite3TriggersExist( int *pMask /* OUT: Mask of TRIGGER_BEFORE|TRIGGER_AFTER */ ){ int mask = 0; - Trigger *pList = sqlite3TriggerList(pParse, pTab); + Trigger *pList = 0; Trigger *p; + + if( (pParse->db->flags & SQLITE_EnableTrigger)!=0 ){ + pList = sqlite3TriggerList(pParse, pTab); + } assert( pList==0 || IsVirtual(pTab)==0 ); for(p=pList; p; p=p->pNext){ if( p->op==op && checkColumnOverlap(p->pColumns, pChanges) ){ @@ -93433,7 +96271,8 @@ static void updateVirtualTable( ExprList *pChanges, /* The columns to change in the UPDATE statement */ Expr *pRowidExpr, /* Expression used to recompute the rowid */ int *aXRef, /* Mapping from columns of pTab to entries in pChanges */ - Expr *pWhere /* WHERE clause of the UPDATE statement */ + Expr *pWhere, /* WHERE clause of the UPDATE statement */ + int onError /* ON CONFLICT strategy */ ); #endif /* SQLITE_OMIT_VIRTUALTABLE */ @@ -93538,7 +96377,6 @@ SQLITE_PRIVATE void sqlite3Update( int regNew; int regOld = 0; int regRowSet = 0; /* Rowset of rows to be updated */ - int regRec; /* Register used for new table record to insert */ memset(&sContext, 0, sizeof(sContext)); db = pParse->db; @@ -93654,7 +96492,7 @@ SQLITE_PRIVATE void sqlite3Update( } for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ int reg; - if( chngRowid ){ + if( hasFK || chngRowid ){ reg = ++pParse->nMem; }else{ reg = 0; @@ -93678,7 +96516,7 @@ SQLITE_PRIVATE void sqlite3Update( /* Virtual tables must be handled separately */ if( IsVirtual(pTab) ){ updateVirtualTable(pParse, pTabList, pTab, pChanges, pRowidExpr, aXRef, - pWhere); + pWhere, onError); pWhere = 0; pTabList = 0; goto update_cleanup; @@ -93696,7 +96534,6 @@ SQLITE_PRIVATE void sqlite3Update( } regNew = pParse->nMem + 1; pParse->nMem += pTab->nCol; - regRec = ++pParse->nMem; /* Start the view context. */ if( isView ){ @@ -93806,7 +96643,7 @@ SQLITE_PRIVATE void sqlite3Update( pTrigger, pChanges, 0, TRIGGER_BEFORE|TRIGGER_AFTER, pTab, onError ); for(i=0; i<pTab->nCol; i++){ - if( aXRef[i]<0 || oldmask==0xffffffff || (oldmask & (1<<i)) ){ + if( aXRef[i]<0 || oldmask==0xffffffff || (i<32 && (oldmask & (1<<i))) ){ sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, i, regOld+i); }else{ sqlite3VdbeAddOp2(v, OP_Null, 0, regOld+i); @@ -94009,7 +96846,8 @@ static void updateVirtualTable( ExprList *pChanges, /* The columns to change in the UPDATE statement */ Expr *pRowid, /* Expression used to recompute the rowid */ int *aXRef, /* Mapping from columns of pTab to entries in pChanges */ - Expr *pWhere /* WHERE clause of the UPDATE statement */ + Expr *pWhere, /* WHERE clause of the UPDATE statement */ + int onError /* ON CONFLICT strategy */ ){ Vdbe *v = pParse->pVdbe; /* Virtual machine under construction */ ExprList *pEList = 0; /* The result set of the SELECT statement */ @@ -94066,6 +96904,7 @@ static void updateVirtualTable( } sqlite3VtabMakeWritable(pParse, pTab); sqlite3VdbeAddOp4(v, OP_VUpdate, 0, pTab->nCol+2, iReg, pVTab, P4_VTAB); + sqlite3VdbeChangeP5(v, onError==OE_Default ? OE_Abort : onError); sqlite3MayAbort(pParse); sqlite3VdbeAddOp2(v, OP_Next, ephemTab, addr+1); sqlite3VdbeJumpHere(v, addr); @@ -94413,10 +97252,13 @@ end_of_vacuum: pDb->pSchema = 0; } - sqlite3ResetInternalSchema(db, 0); + /* This both clears the schemas and reduces the size of the db->aDb[] + ** array. */ + sqlite3ResetInternalSchema(db, -1); return rc; } + #endif /* SQLITE_OMIT_VACUUM && SQLITE_OMIT_ATTACH */ /************** End of vacuum.c **********************************************/ @@ -94437,6 +97279,18 @@ end_of_vacuum: #ifndef SQLITE_OMIT_VIRTUALTABLE /* +** Before a virtual table xCreate() or xConnect() method is invoked, the +** sqlite3.pVtabCtx member variable is set to point to an instance of +** this struct allocated on the stack. It is used by the implementation of +** the sqlite3_declare_vtab() and sqlite3_vtab_config() APIs, both of which +** are invoked only from within xCreate and xConnect methods. +*/ +struct VtabCtx { + Table *pTab; + VTable *pVTable; +}; + +/* ** The actual function that does the work of creating a new module. ** This function implements the sqlite3_create_module() and ** sqlite3_create_module_v2() interfaces. @@ -94464,13 +97318,13 @@ static int createModule( pMod->xDestroy = xDestroy; pDel = (Module *)sqlite3HashInsert(&db->aModule, zCopy, nName, (void*)pMod); if( pDel && pDel->xDestroy ){ + sqlite3ResetInternalSchema(db, -1); pDel->xDestroy(pDel->pAux); } sqlite3DbFree(db, pDel); if( pDel==pMod ){ db->mallocFailed = 1; } - sqlite3ResetInternalSchema(db, 0); }else if( xDestroy ){ xDestroy(pAux); } @@ -94567,10 +97421,9 @@ static VTable *vtabDisconnectAll(sqlite3 *db, Table *p){ ** that contains table p is held by the caller. See header comments ** above function sqlite3VtabUnlockList() for an explanation of why ** this makes it safe to access the sqlite3.pDisconnect list of any - ** database connection that may have an entry in the p->pVTable list. */ - assert( db==0 || - sqlite3BtreeHoldsMutex(db->aDb[sqlite3SchemaToIndex(db, p->pSchema)].pBt) - ); + ** database connection that may have an entry in the p->pVTable list. + */ + assert( db==0 || sqlite3SchemaMutexHeld(db, 0, p->pSchema) ); while( pVTable ){ sqlite3 *db2 = pVTable->db; @@ -94794,7 +97647,7 @@ SQLITE_PRIVATE void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){ sqlite3VdbeAddOp2(v, OP_Expire, 0, 0); zWhere = sqlite3MPrintf(db, "name='%q' AND type='table'", pTab->zName); - sqlite3VdbeAddOp4(v, OP_ParseSchema, iDb, 1, 0, zWhere, P4_DYNAMIC); + sqlite3VdbeAddParseSchemaOp(v, iDb, zWhere); sqlite3VdbeAddOp4(v, OP_VCreate, iDb, 0, 0, pTab->zName, sqlite3Strlen30(pTab->zName) + 1); } @@ -94809,6 +97662,7 @@ SQLITE_PRIVATE void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){ Schema *pSchema = pTab->pSchema; const char *zName = pTab->zName; int nName = sqlite3Strlen30(zName); + assert( sqlite3SchemaMutexHeld(db, 0, pSchema) ); pOld = sqlite3HashInsert(&pSchema->tblHash, zName, nName, pTab); if( pOld ){ db->mallocFailed = 1; @@ -94856,6 +97710,7 @@ static int vtabCallConstructor( int (*xConstruct)(sqlite3*,void*,int,const char*const*,sqlite3_vtab**,char**), char **pzErr ){ + VtabCtx sCtx; VTable *pVTable; int rc; const char *const*azArg = (const char *const*)pTab->azModuleArg; @@ -94875,12 +97730,14 @@ static int vtabCallConstructor( pVTable->db = db; pVTable->pMod = pMod; - assert( !db->pVTab ); - assert( xConstruct ); - db->pVTab = pTab; - /* Invoke the virtual table constructor */ + assert( &db->pVtabCtx ); + assert( xConstruct ); + sCtx.pTab = pTab; + sCtx.pVTable = pVTable; + db->pVtabCtx = &sCtx; rc = xConstruct(db, pMod->pAux, nArg, azArg, &pVTable->pVtab, &zErr); + db->pVtabCtx = 0; if( rc==SQLITE_NOMEM ) db->mallocFailed = 1; if( SQLITE_OK!=rc ){ @@ -94896,7 +97753,7 @@ static int vtabCallConstructor( ** the sqlite3_vtab object if successful. */ pVTable->pVtab->pModule = pMod->pModule; pVTable->nRef = 1; - if( db->pVTab ){ + if( sCtx.pTab ){ const char *zFormat = "vtable constructor did not declare schema: %s"; *pzErr = sqlite3MPrintf(db, zFormat, pTab->zName); sqlite3VtabUnlock(pVTable); @@ -94944,7 +97801,6 @@ static int vtabCallConstructor( } sqlite3DbFree(db, zModuleName); - db->pVTab = 0; return rc; } @@ -94985,11 +97841,11 @@ SQLITE_PRIVATE int sqlite3VtabCallConnect(Parse *pParse, Table *pTab){ return rc; } - /* -** Add the virtual table pVTab to the array sqlite3.aVTrans[]. +** Grow the db->aVTrans[] array so that there is room for at least one +** more v-table. Return SQLITE_NOMEM if a malloc fails, or SQLITE_OK otherwise. */ -static int addToVTrans(sqlite3 *db, VTable *pVTab){ +static int growVTrans(sqlite3 *db){ const int ARRAY_INCR = 5; /* Grow the sqlite3.aVTrans array if required */ @@ -95004,10 +97860,17 @@ static int addToVTrans(sqlite3 *db, VTable *pVTab){ db->aVTrans = aVTrans; } + return SQLITE_OK; +} + +/* +** Add the virtual table pVTab to the array sqlite3.aVTrans[]. Space should +** have already been reserved using growVTrans(). +*/ +static void addToVTrans(sqlite3 *db, VTable *pVTab){ /* Add pVtab to the end of sqlite3.aVTrans */ db->aVTrans[db->nVTrans++] = pVTab; sqlite3VtabLock(pVTab); - return SQLITE_OK; } /* @@ -95045,7 +97908,10 @@ SQLITE_PRIVATE int sqlite3VtabCallCreate(sqlite3 *db, int iDb, const char *zTab, /* Justification of ALWAYS(): The xConstructor method is required to ** create a valid sqlite3_vtab if it returns SQLITE_OK. */ if( rc==SQLITE_OK && ALWAYS(sqlite3GetVTable(db, pTab)) ){ - rc = addToVTrans(db, sqlite3GetVTable(db, pTab)); + rc = growVTrans(db); + if( rc==SQLITE_OK ){ + addToVTrans(db, sqlite3GetVTable(db, pTab)); + } } return rc; @@ -95064,8 +97930,7 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ char *zErr = 0; sqlite3_mutex_enter(db->mutex); - pTab = db->pVTab; - if( !pTab ){ + if( !db->pVtabCtx || !(pTab = db->pVtabCtx->pTab) ){ sqlite3Error(db, SQLITE_MISUSE, 0); sqlite3_mutex_leave(db->mutex); return SQLITE_MISUSE_BKPT; @@ -95092,7 +97957,7 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ pParse->pNewTable->nCol = 0; pParse->pNewTable->aCol = 0; } - db->pVTab = 0; + db->pVtabCtx->pTab = 0; }else{ sqlite3Error(db, SQLITE_ERROR, (zErr ? "%s" : 0), zErr); sqlite3DbFree(db, zErr); @@ -95162,6 +98027,7 @@ static void callFinaliser(sqlite3 *db, int offset){ x = *(int (**)(sqlite3_vtab *))((char *)p->pModule + offset); if( x ) x(p); } + pVTab->iSavepoint = 0; sqlite3VtabUnlock(pVTab); } sqlite3DbFree(db, db->aVTrans); @@ -95244,7 +98110,6 @@ SQLITE_PRIVATE int sqlite3VtabBegin(sqlite3 *db, VTable *pVTab){ if( pModule->xBegin ){ int i; - /* If pVtab is already in the aVTrans array, return early */ for(i=0; i<db->nVTrans; i++){ if( db->aVTrans[i]==pVTab ){ @@ -95252,10 +98117,62 @@ SQLITE_PRIVATE int sqlite3VtabBegin(sqlite3 *db, VTable *pVTab){ } } - /* Invoke the xBegin method */ - rc = pModule->xBegin(pVTab->pVtab); + /* Invoke the xBegin method. If successful, add the vtab to the + ** sqlite3.aVTrans[] array. */ + rc = growVTrans(db); if( rc==SQLITE_OK ){ - rc = addToVTrans(db, pVTab); + rc = pModule->xBegin(pVTab->pVtab); + if( rc==SQLITE_OK ){ + addToVTrans(db, pVTab); + } + } + } + return rc; +} + +/* +** Invoke either the xSavepoint, xRollbackTo or xRelease method of all +** virtual tables that currently have an open transaction. Pass iSavepoint +** as the second argument to the virtual table method invoked. +** +** If op is SAVEPOINT_BEGIN, the xSavepoint method is invoked. If it is +** SAVEPOINT_ROLLBACK, the xRollbackTo method. Otherwise, if op is +** SAVEPOINT_RELEASE, then the xRelease method of each virtual table with +** an open transaction is invoked. +** +** If any virtual table method returns an error code other than SQLITE_OK, +** processing is abandoned and the error returned to the caller of this +** function immediately. If all calls to virtual table methods are successful, +** SQLITE_OK is returned. +*/ +SQLITE_PRIVATE int sqlite3VtabSavepoint(sqlite3 *db, int op, int iSavepoint){ + int rc = SQLITE_OK; + + assert( op==SAVEPOINT_RELEASE||op==SAVEPOINT_ROLLBACK||op==SAVEPOINT_BEGIN ); + assert( iSavepoint>=0 ); + if( db->aVTrans ){ + int i; + for(i=0; rc==SQLITE_OK && i<db->nVTrans; i++){ + VTable *pVTab = db->aVTrans[i]; + const sqlite3_module *pMod = pVTab->pMod->pModule; + if( pMod->iVersion>=2 ){ + int (*xMethod)(sqlite3_vtab *, int); + switch( op ){ + case SAVEPOINT_BEGIN: + xMethod = pMod->xSavepoint; + pVTab->iSavepoint = iSavepoint+1; + break; + case SAVEPOINT_ROLLBACK: + xMethod = pMod->xRollbackTo; + break; + default: + xMethod = pMod->xRelease; + break; + } + if( xMethod && pVTab->iSavepoint>iSavepoint ){ + rc = xMethod(db->aVTrans[i]->pVtab, iSavepoint); + } + } } } return rc; @@ -95359,6 +98276,57 @@ SQLITE_PRIVATE void sqlite3VtabMakeWritable(Parse *pParse, Table *pTab){ } } +/* +** Return the ON CONFLICT resolution mode in effect for the virtual +** table update operation currently in progress. +** +** The results of this routine are undefined unless it is called from +** within an xUpdate method. +*/ +SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *db){ + static const unsigned char aMap[] = { + SQLITE_ROLLBACK, SQLITE_ABORT, SQLITE_FAIL, SQLITE_IGNORE, SQLITE_REPLACE + }; + assert( OE_Rollback==1 && OE_Abort==2 && OE_Fail==3 ); + assert( OE_Ignore==4 && OE_Replace==5 ); + assert( db->vtabOnConflict>=1 && db->vtabOnConflict<=5 ); + return (int)aMap[db->vtabOnConflict-1]; +} + +/* +** Call from within the xCreate() or xConnect() methods to provide +** the SQLite core with additional information about the behavior +** of the virtual table being implemented. +*/ +SQLITE_API int sqlite3_vtab_config(sqlite3 *db, int op, ...){ + va_list ap; + int rc = SQLITE_OK; + + sqlite3_mutex_enter(db->mutex); + + va_start(ap, op); + switch( op ){ + case SQLITE_VTAB_CONSTRAINT_SUPPORT: { + VtabCtx *p = db->pVtabCtx; + if( !p ){ + rc = SQLITE_MISUSE_BKPT; + }else{ + assert( p->pTab==0 || (p->pTab->tabFlags & TF_Virtual)!=0 ); + p->pVTable->bConstraint = (u8)va_arg(ap, int); + } + break; + } + default: + rc = SQLITE_MISUSE_BKPT; + break; + } + va_end(ap); + + if( rc!=SQLITE_OK ) sqlite3Error(db, rc, 0); + sqlite3_mutex_leave(db->mutex); + return rc; +} + #endif /* SQLITE_OMIT_VIRTUALTABLE */ /************** End of vtab.c ************************************************/ @@ -95382,6 +98350,7 @@ SQLITE_PRIVATE void sqlite3VtabMakeWritable(Parse *pParse, Table *pTab){ ** indices, you might also think of this module as the "query optimizer". */ + /* ** Trace output macros */ @@ -95481,6 +98450,11 @@ struct WhereTerm { #define TERM_ORINFO 0x10 /* Need to free the WhereTerm.u.pOrInfo object */ #define TERM_ANDINFO 0x20 /* Need to free the WhereTerm.u.pAndInfo obj */ #define TERM_OR_OK 0x40 /* Used during OR-clause processing */ +#ifdef SQLITE_ENABLE_STAT2 +# define TERM_VNULL 0x80 /* Manufactured x>NULL or x<=NULL term */ +#else +# define TERM_VNULL 0x00 /* Disabled if not using stat2 */ +#endif /* ** An instance of the following structure holds all information about a @@ -95574,6 +98548,7 @@ struct WhereCost { #define WO_ISNULL 0x080 #define WO_OR 0x100 /* Two or more OR-connected terms */ #define WO_AND 0x200 /* Two or more AND-connected terms */ +#define WO_NOOP 0x800 /* This term does not restrict search space */ #define WO_ALL 0xfff /* Mask of all possible WO_* values */ #define WO_SINGLE 0x0ff /* Mask of all non-compound WO_* values */ @@ -95756,7 +98731,7 @@ static void whereSplit(WhereClause *pWC, Expr *pExpr, int op){ */ static Bitmask getMask(WhereMaskSet *pMaskSet, int iCursor){ int i; - assert( pMaskSet->n<=sizeof(Bitmask)*8 ); + assert( pMaskSet->n<=(int)sizeof(Bitmask)*8 ); for(i=0; i<pMaskSet->n; i++){ if( pMaskSet->ix[i]==iCursor ){ return ((Bitmask)1)<<i; @@ -96424,7 +99399,7 @@ static void exprAnalyzeOrTerm( }else{ sqlite3ExprListDelete(db, pList); } - pTerm->eOperator = 0; /* case 1 trumps case 2 */ + pTerm->eOperator = WO_NOOP; /* case 1 trumps case 2 */ } } } @@ -96688,6 +99663,47 @@ static void exprAnalyze( } #endif /* SQLITE_OMIT_VIRTUALTABLE */ +#ifdef SQLITE_ENABLE_STAT2 + /* When sqlite_stat2 histogram data is available an operator of the + ** form "x IS NOT NULL" can sometimes be evaluated more efficiently + ** as "x>NULL" if x is not an INTEGER PRIMARY KEY. So construct a + ** virtual term of that form. + ** + ** Note that the virtual term must be tagged with TERM_VNULL. This + ** TERM_VNULL tag will suppress the not-null check at the beginning + ** of the loop. Without the TERM_VNULL flag, the not-null check at + ** the start of the loop will prevent any results from being returned. + */ + if( pExpr->op==TK_NOTNULL + && pExpr->pLeft->op==TK_COLUMN + && pExpr->pLeft->iColumn>=0 + ){ + Expr *pNewExpr; + Expr *pLeft = pExpr->pLeft; + int idxNew; + WhereTerm *pNewTerm; + + pNewExpr = sqlite3PExpr(pParse, TK_GT, + sqlite3ExprDup(db, pLeft, 0), + sqlite3PExpr(pParse, TK_NULL, 0, 0, 0), 0); + + idxNew = whereClauseInsert(pWC, pNewExpr, + TERM_VIRTUAL|TERM_DYNAMIC|TERM_VNULL); + if( idxNew ){ + pNewTerm = &pWC->a[idxNew]; + pNewTerm->prereqRight = 0; + pNewTerm->leftCursor = pLeft->iTable; + pNewTerm->u.leftColumn = pLeft->iColumn; + pNewTerm->eOperator = WO_GT; + pNewTerm->iParent = idxTerm; + pTerm = &pWC->a[idxTerm]; + pTerm->nChild = 1; + pTerm->wtFlags |= TERM_COPIED; + pNewTerm->prereqAll = pTerm->prereqAll; + } + } +#endif /* SQLITE_ENABLE_STAT2 */ + /* Prevent ON clause terms of a LEFT JOIN from being used to drive ** an index for tables to the left of the join. */ @@ -96740,6 +99756,7 @@ static int isSortingIndex( int base, /* Cursor number for the table to be sorted */ ExprList *pOrderBy, /* The ORDER BY clause */ int nEqCol, /* Number of index columns with == constraints */ + int wsFlags, /* Index usages flags */ int *pbRev /* Set to 1 if ORDER BY is DESC */ ){ int i, j; /* Loop counters */ @@ -96845,11 +99862,14 @@ static int isSortingIndex( return 1; } if( pIdx->onError!=OE_None && i==pIdx->nColumn + && (wsFlags & WHERE_COLUMN_NULL)==0 && !referencesOtherTables(pOrderBy, pMaskSet, j, base) ){ /* All terms of this index match some prefix of the ORDER BY clause ** and the index is UNIQUE and no terms on the tail of the ORDER BY ** clause reference other tables in a join. If this is all true then - ** the order by clause is superfluous. */ + ** the order by clause is superfluous. Not that if the matching + ** condition is IS NULL then the result is not necessarily unique + ** even on a UNIQUE index, so disallow those cases. */ return 1; } return 0; @@ -97086,7 +100106,7 @@ static void bestAutomaticIndex( pWCEnd = &pWC->a[pWC->nTerm]; for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){ if( termCanDriveIndex(pTerm, pSrc, notReady) ){ - WHERETRACE(("auto-index reduces cost from %.2f to %.2f\n", + WHERETRACE(("auto-index reduces cost from %.1f to %.1f\n", pCost->rCost, costTempIdx)); pCost->rCost = costTempIdx; pCost->plan.nRow = logN + 1; @@ -97207,7 +100227,7 @@ static void constructAutomaticIndex( idxCols |= cMask; pIdx->aiColumn[n] = pTerm->u.leftColumn; pColl = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight); - pIdx->azColl[n] = pColl->zName; + pIdx->azColl[n] = ALWAYS(pColl) ? pColl->zName : "BINARY"; n++; } } @@ -97565,11 +100585,18 @@ static void bestVirtualIndex( /* ** Argument pIdx is a pointer to an index structure that has an array of ** SQLITE_INDEX_SAMPLES evenly spaced samples of the first indexed column -** stored in Index.aSample. The domain of values stored in said column -** may be thought of as divided into (SQLITE_INDEX_SAMPLES+1) regions. -** Region 0 contains all values smaller than the first sample value. Region -** 1 contains values larger than or equal to the value of the first sample, -** but smaller than the value of the second. And so on. +** stored in Index.aSample. These samples divide the domain of values stored +** the index into (SQLITE_INDEX_SAMPLES+1) regions. +** Region 0 contains all values less than the first sample value. Region +** 1 contains values between the first and second samples. Region 2 contains +** values between samples 2 and 3. And so on. Region SQLITE_INDEX_SAMPLES +** contains values larger than the last sample. +** +** If the index contains many duplicates of a single value, then it is +** possible that two or more adjacent samples can hold the same value. +** When that is the case, the smallest possible region code is returned +** when roundUp is false and the largest possible region code is returned +** when roundUp is true. ** ** If successful, this function determines which of the regions value ** pVal lies in, sets *piRegion to the region index (a value between 0 @@ -97582,8 +100609,10 @@ static int whereRangeRegion( Parse *pParse, /* Database connection */ Index *pIdx, /* Index to consider domain of */ sqlite3_value *pVal, /* Value to consider */ + int roundUp, /* Return largest valid region if true */ int *piRegion /* OUT: Region of domain in which value lies */ ){ + assert( roundUp==0 || roundUp==1 ); if( ALWAYS(pVal) ){ IndexSample *aSample = pIdx->aSample; int i = 0; @@ -97593,7 +100622,17 @@ static int whereRangeRegion( double r = sqlite3_value_double(pVal); for(i=0; i<SQLITE_INDEX_SAMPLES; i++){ if( aSample[i].eType==SQLITE_NULL ) continue; - if( aSample[i].eType>=SQLITE_TEXT || aSample[i].u.r>r ) break; + if( aSample[i].eType>=SQLITE_TEXT ) break; + if( roundUp ){ + if( aSample[i].u.r>r ) break; + }else{ + if( aSample[i].u.r>=r ) break; + } + } + }else if( eType==SQLITE_NULL ){ + i = 0; + if( roundUp ){ + while( i<SQLITE_INDEX_SAMPLES && aSample[i].eType==SQLITE_NULL ) i++; } }else{ sqlite3 *db = pParse->db; @@ -97624,7 +100663,7 @@ static int whereRangeRegion( n = sqlite3ValueBytes(pVal, pColl->enc); for(i=0; i<SQLITE_INDEX_SAMPLES; i++){ - int r; + int c; int eSampletype = aSample[i].eType; if( eSampletype==SQLITE_NULL || eSampletype<eType ) continue; if( (eSampletype!=eType) ) break; @@ -97638,14 +100677,14 @@ static int whereRangeRegion( assert( db->mallocFailed ); return SQLITE_NOMEM; } - r = pColl->xCmp(pColl->pUser, nSample, zSample, n, z); + c = pColl->xCmp(pColl->pUser, nSample, zSample, n, z); sqlite3DbFree(db, zSample); }else #endif { - r = pColl->xCmp(pColl->pUser, aSample[i].nByte, aSample[i].u.z, n, z); + c = pColl->xCmp(pColl->pUser, aSample[i].nByte, aSample[i].u.z, n, z); } - if( r>0 ) break; + if( c-roundUp>=0 ) break; } } @@ -97679,10 +100718,9 @@ static int valueFromExpr( u8 aff, sqlite3_value **pp ){ - /* The evalConstExpr() function will have already converted any TK_VARIABLE - ** expression involved in an comparison into a TK_REGISTER. */ - assert( pExpr->op!=TK_VARIABLE ); - if( pExpr->op==TK_REGISTER && pExpr->op2==TK_VARIABLE ){ + if( pExpr->op==TK_VARIABLE + || (pExpr->op==TK_REGISTER && pExpr->op2==TK_VARIABLE) + ){ int iVar = pExpr->iColumn; sqlite3VdbeSetVarmask(pParse->pVdbe, iVar); /* IMP: R-23257-02778 */ *pp = sqlite3VdbeGetValue(pParse->pReprepare, iVar, aff); @@ -97729,9 +100767,9 @@ static int valueFromExpr( ** constraints. ** ** In the absence of sqlite_stat2 ANALYZE data, each range inequality -** reduces the search space by 2/3rds. Hence a single constraint (x>?) -** results in a return of 33 and a range constraint (x>? AND x<?) results -** in a return of 11. +** reduces the search space by 3/4ths. Hence a single constraint (x>?) +** results in a return of 25 and a range constraint (x>? AND x<?) results +** in a return of 6. */ static int whereRangeScanEst( Parse *pParse, /* Parsing & code generating context */ @@ -97751,15 +100789,21 @@ static int whereRangeScanEst( int iEst; int iLower = 0; int iUpper = SQLITE_INDEX_SAMPLES; + int roundUpUpper = 0; + int roundUpLower = 0; u8 aff = p->pTable->aCol[p->aiColumn[0]].affinity; if( pLower ){ Expr *pExpr = pLower->pExpr->pRight; rc = valueFromExpr(pParse, pExpr, aff, &pLowerVal); + assert( pLower->eOperator==WO_GT || pLower->eOperator==WO_GE ); + roundUpLower = (pLower->eOperator==WO_GT) ?1:0; } if( rc==SQLITE_OK && pUpper ){ Expr *pExpr = pUpper->pExpr->pRight; rc = valueFromExpr(pParse, pExpr, aff, &pUpperVal); + assert( pUpper->eOperator==WO_LT || pUpper->eOperator==WO_LE ); + roundUpUpper = (pUpper->eOperator==WO_LE) ?1:0; } if( rc!=SQLITE_OK || (pLowerVal==0 && pUpperVal==0) ){ @@ -97767,28 +100811,29 @@ static int whereRangeScanEst( sqlite3ValueFree(pUpperVal); goto range_est_fallback; }else if( pLowerVal==0 ){ - rc = whereRangeRegion(pParse, p, pUpperVal, &iUpper); + rc = whereRangeRegion(pParse, p, pUpperVal, roundUpUpper, &iUpper); if( pLower ) iLower = iUpper/2; }else if( pUpperVal==0 ){ - rc = whereRangeRegion(pParse, p, pLowerVal, &iLower); + rc = whereRangeRegion(pParse, p, pLowerVal, roundUpLower, &iLower); if( pUpper ) iUpper = (iLower + SQLITE_INDEX_SAMPLES + 1)/2; }else{ - rc = whereRangeRegion(pParse, p, pUpperVal, &iUpper); + rc = whereRangeRegion(pParse, p, pUpperVal, roundUpUpper, &iUpper); if( rc==SQLITE_OK ){ - rc = whereRangeRegion(pParse, p, pLowerVal, &iLower); + rc = whereRangeRegion(pParse, p, pLowerVal, roundUpLower, &iLower); } } + WHERETRACE(("range scan regions: %d..%d\n", iLower, iUpper)); iEst = iUpper - iLower; testcase( iEst==SQLITE_INDEX_SAMPLES ); assert( iEst<=SQLITE_INDEX_SAMPLES ); if( iEst<1 ){ - iEst = 1; + *piEst = 50/SQLITE_INDEX_SAMPLES; + }else{ + *piEst = (iEst*100)/SQLITE_INDEX_SAMPLES; } - sqlite3ValueFree(pLowerVal); sqlite3ValueFree(pUpperVal); - *piEst = (iEst * 100)/SQLITE_INDEX_SAMPLES; return rc; } range_est_fallback: @@ -97798,22 +100843,156 @@ range_est_fallback: UNUSED_PARAMETER(nEq); #endif assert( pLower || pUpper ); - if( pLower && pUpper ){ - *piEst = 11; + *piEst = 100; + if( pLower && (pLower->wtFlags & TERM_VNULL)==0 ) *piEst /= 4; + if( pUpper ) *piEst /= 4; + return rc; +} + +#ifdef SQLITE_ENABLE_STAT2 +/* +** Estimate the number of rows that will be returned based on +** an equality constraint x=VALUE and where that VALUE occurs in +** the histogram data. This only works when x is the left-most +** column of an index and sqlite_stat2 histogram data is available +** for that index. When pExpr==NULL that means the constraint is +** "x IS NULL" instead of "x=VALUE". +** +** Write the estimated row count into *pnRow and return SQLITE_OK. +** If unable to make an estimate, leave *pnRow unchanged and return +** non-zero. +** +** This routine can fail if it is unable to load a collating sequence +** required for string comparison, or if unable to allocate memory +** for a UTF conversion required for comparison. The error is stored +** in the pParse structure. +*/ +static int whereEqualScanEst( + Parse *pParse, /* Parsing & code generating context */ + Index *p, /* The index whose left-most column is pTerm */ + Expr *pExpr, /* Expression for VALUE in the x=VALUE constraint */ + double *pnRow /* Write the revised row estimate here */ +){ + sqlite3_value *pRhs = 0; /* VALUE on right-hand side of pTerm */ + int iLower, iUpper; /* Range of histogram regions containing pRhs */ + u8 aff; /* Column affinity */ + int rc; /* Subfunction return code */ + double nRowEst; /* New estimate of the number of rows */ + + assert( p->aSample!=0 ); + aff = p->pTable->aCol[p->aiColumn[0]].affinity; + if( pExpr ){ + rc = valueFromExpr(pParse, pExpr, aff, &pRhs); + if( rc ) goto whereEqualScanEst_cancel; }else{ - *piEst = 33; + pRhs = sqlite3ValueNew(pParse->db); + } + if( pRhs==0 ) return SQLITE_NOTFOUND; + rc = whereRangeRegion(pParse, p, pRhs, 0, &iLower); + if( rc ) goto whereEqualScanEst_cancel; + rc = whereRangeRegion(pParse, p, pRhs, 1, &iUpper); + if( rc ) goto whereEqualScanEst_cancel; + WHERETRACE(("equality scan regions: %d..%d\n", iLower, iUpper)); + if( iLower>=iUpper ){ + nRowEst = p->aiRowEst[0]/(SQLITE_INDEX_SAMPLES*2); + if( nRowEst<*pnRow ) *pnRow = nRowEst; + }else{ + nRowEst = (iUpper-iLower)*p->aiRowEst[0]/SQLITE_INDEX_SAMPLES; + *pnRow = nRowEst; + } + +whereEqualScanEst_cancel: + sqlite3ValueFree(pRhs); + return rc; +} +#endif /* defined(SQLITE_ENABLE_STAT2) */ + +#ifdef SQLITE_ENABLE_STAT2 +/* +** Estimate the number of rows that will be returned based on +** an IN constraint where the right-hand side of the IN operator +** is a list of values. Example: +** +** WHERE x IN (1,2,3,4) +** +** Write the estimated row count into *pnRow and return SQLITE_OK. +** If unable to make an estimate, leave *pnRow unchanged and return +** non-zero. +** +** This routine can fail if it is unable to load a collating sequence +** required for string comparison, or if unable to allocate memory +** for a UTF conversion required for comparison. The error is stored +** in the pParse structure. +*/ +static int whereInScanEst( + Parse *pParse, /* Parsing & code generating context */ + Index *p, /* The index whose left-most column is pTerm */ + ExprList *pList, /* The value list on the RHS of "x IN (v1,v2,v3,...)" */ + double *pnRow /* Write the revised row estimate here */ +){ + sqlite3_value *pVal = 0; /* One value from list */ + int iLower, iUpper; /* Range of histogram regions containing pRhs */ + u8 aff; /* Column affinity */ + int rc = SQLITE_OK; /* Subfunction return code */ + double nRowEst; /* New estimate of the number of rows */ + int nSpan = 0; /* Number of histogram regions spanned */ + int nSingle = 0; /* Histogram regions hit by a single value */ + int nNotFound = 0; /* Count of values that are not constants */ + int i; /* Loop counter */ + u8 aSpan[SQLITE_INDEX_SAMPLES+1]; /* Histogram regions that are spanned */ + u8 aSingle[SQLITE_INDEX_SAMPLES+1]; /* Histogram regions hit once */ + + assert( p->aSample!=0 ); + aff = p->pTable->aCol[p->aiColumn[0]].affinity; + memset(aSpan, 0, sizeof(aSpan)); + memset(aSingle, 0, sizeof(aSingle)); + for(i=0; i<pList->nExpr; i++){ + sqlite3ValueFree(pVal); + rc = valueFromExpr(pParse, pList->a[i].pExpr, aff, &pVal); + if( rc ) break; + if( pVal==0 || sqlite3_value_type(pVal)==SQLITE_NULL ){ + nNotFound++; + continue; + } + rc = whereRangeRegion(pParse, p, pVal, 0, &iLower); + if( rc ) break; + rc = whereRangeRegion(pParse, p, pVal, 1, &iUpper); + if( rc ) break; + if( iLower>=iUpper ){ + aSingle[iLower] = 1; + }else{ + assert( iLower>=0 && iUpper<=SQLITE_INDEX_SAMPLES ); + while( iLower<iUpper ) aSpan[iLower++] = 1; + } + } + if( rc==SQLITE_OK ){ + for(i=nSpan=0; i<=SQLITE_INDEX_SAMPLES; i++){ + if( aSpan[i] ){ + nSpan++; + }else if( aSingle[i] ){ + nSingle++; + } + } + nRowEst = (nSpan*2+nSingle)*p->aiRowEst[0]/(2*SQLITE_INDEX_SAMPLES) + + nNotFound*p->aiRowEst[1]; + if( nRowEst > p->aiRowEst[0] ) nRowEst = p->aiRowEst[0]; + *pnRow = nRowEst; + WHERETRACE(("IN row estimate: nSpan=%d, nSingle=%d, nNotFound=%d, est=%g\n", + nSpan, nSingle, nNotFound, nRowEst)); } + sqlite3ValueFree(pVal); return rc; } +#endif /* defined(SQLITE_ENABLE_STAT2) */ /* -** Find the query plan for accessing a particular table. Write the +** Find the best query plan for accessing a particular table. Write the ** best query plan and its cost into the WhereCost object supplied as the ** last parameter. ** ** The lowest cost plan wins. The cost is an estimate of the amount of -** CPU and disk I/O need to process the request using the selected plan. +** CPU and disk I/O needed to process the requested result. ** Factors that influence cost include: ** ** * The estimated number of rows that will be retrieved. (The @@ -97832,7 +101011,7 @@ range_est_fallback: ** ** If a NOT INDEXED clause (pSrc->notIndexed!=0) was attached to the table ** in the SELECT statement, then no indexes are considered. However, the -** selected plan may still take advantage of the tables built-in rowid +** selected plan may still take advantage of the built-in rowid primary key ** index. */ static void bestBtreeIndex( @@ -97875,9 +101054,11 @@ static void bestBtreeIndex( wsFlagMask = ~(WHERE_ROWID_EQ|WHERE_ROWID_RANGE); eqTermMask = idxEqTermMask; }else{ - /* There is no INDEXED BY clause. Create a fake Index object to - ** represent the primary key */ - Index *pFirst; /* Any other index on the table */ + /* There is no INDEXED BY clause. Create a fake Index object in local + ** variable sPk to represent the rowid primary key index. Make this + ** fake index the first in a chain of Index objects with all of the real + ** indices to follow */ + Index *pFirst; /* First of real indices on the table */ memset(&sPk, 0, sizeof(Index)); sPk.nColumn = 1; sPk.aiColumn = &aiColumnPk; @@ -97888,6 +101069,8 @@ static void bestBtreeIndex( aiRowEstPk[1] = 1; pFirst = pSrc->pTab->pIndex; if( pSrc->notIndexed==0 ){ + /* The real indices of the table are only considered if the + ** NOT INDEXED qualifier is omitted from the FROM clause */ sPk.pNext = pFirst; } pProbe = &sPk; @@ -97904,16 +101087,19 @@ static void bestBtreeIndex( const unsigned int * const aiRowEst = pProbe->aiRowEst; double cost; /* Cost of using pProbe */ double nRow; /* Estimated number of rows in result set */ + double log10N; /* base-10 logarithm of nRow (inexact) */ int rev; /* True to scan in reverse order */ int wsFlags = 0; Bitmask used = 0; /* The following variables are populated based on the properties of - ** scan being evaluated. They are then used to determine the expected + ** index being evaluated. They are then used to determine the expected ** cost and number of rows returned. ** ** nEq: ** Number of equality terms that can be implemented using the index. + ** In other words, the number of initial fields in the index that + ** are used in == or IN or NOT NULL constraints of the WHERE clause. ** ** nInMul: ** The "in-multiplier". This is an estimate of how many seek operations @@ -97937,7 +101123,9 @@ static void bestBtreeIndex( ** ** bInEst: ** Set to true if there was at least one "x IN (SELECT ...)" term used - ** in determining the value of nInMul. + ** in determining the value of nInMul. Note that the RHS of the + ** IN operator must be a SELECT, not a value list, for this variable + ** to be true. ** ** estBound: ** An estimate on the amount of the table that must be searched. A @@ -97945,8 +101133,8 @@ static void bestBtreeIndex( ** might reduce this to a value less than 100 to indicate that only ** a fraction of the table needs searching. In the absence of ** sqlite_stat2 ANALYZE data, a single inequality reduces the search - ** space to 1/3rd its original size. So an x>? constraint reduces - ** estBound to 33. Two constraints (x>? AND x<?) reduce estBound to 11. + ** space to 1/4rd its original size. So an x>? constraint reduces + ** estBound to 25. Two constraints (x>? AND x<?) reduce estBound to 6. ** ** bSort: ** Boolean. True if there is an ORDER BY clause that will require an @@ -97954,25 +101142,31 @@ static void bestBtreeIndex( ** correctly order records). ** ** bLookup: - ** Boolean. True if for each index entry visited a lookup on the - ** corresponding table b-tree is required. This is always false - ** for the rowid index. For other indexes, it is true unless all the - ** columns of the table used by the SELECT statement are present in - ** the index (such an index is sometimes described as a covering index). + ** Boolean. True if a table lookup is required for each index entry + ** visited. In other words, true if this is not a covering index. + ** This is always false for the rowid primary key index of a table. + ** For other indexes, it is true unless all the columns of the table + ** used by the SELECT statement are present in the index (such an + ** index is sometimes described as a covering index). ** For example, given the index on (a, b), the second of the following - ** two queries requires table b-tree lookups, but the first does not. + ** two queries requires table b-tree lookups in order to find the value + ** of column c, but the first does not because columns a and b are + ** both available in the index. ** ** SELECT a, b FROM tbl WHERE a = 1; ** SELECT a, b, c FROM tbl WHERE a = 1; */ - int nEq; - int bInEst = 0; - int nInMul = 1; - int estBound = 100; - int nBound = 0; /* Number of range constraints seen */ - int bSort = 0; - int bLookup = 0; - WhereTerm *pTerm; /* A single term of the WHERE clause */ + int nEq; /* Number of == or IN terms matching index */ + int bInEst = 0; /* True if "x IN (SELECT...)" seen */ + int nInMul = 1; /* Number of distinct equalities to lookup */ + int estBound = 100; /* Estimated reduction in search space */ + int nBound = 0; /* Number of range constraints seen */ + int bSort = 0; /* True if external sort required */ + int bLookup = 0; /* True if not a covering index */ + WhereTerm *pTerm; /* A single term of the WHERE clause */ +#ifdef SQLITE_ENABLE_STAT2 + WhereTerm *pFirstTerm = 0; /* First term matching the index */ +#endif /* Determine the values of nEq and nInMul */ for(nEq=0; nEq<pProbe->nColumn; nEq++){ @@ -97984,19 +101178,24 @@ static void bestBtreeIndex( Expr *pExpr = pTerm->pExpr; wsFlags |= WHERE_COLUMN_IN; if( ExprHasProperty(pExpr, EP_xIsSelect) ){ + /* "x IN (SELECT ...)": Assume the SELECT returns 25 rows */ nInMul *= 25; bInEst = 1; - }else if( ALWAYS(pExpr->x.pList) ){ - nInMul *= pExpr->x.pList->nExpr + 1; + }else if( ALWAYS(pExpr->x.pList && pExpr->x.pList->nExpr) ){ + /* "x IN (value, value, ...)" */ + nInMul *= pExpr->x.pList->nExpr; } }else if( pTerm->eOperator & WO_ISNULL ){ wsFlags |= WHERE_COLUMN_NULL; } +#ifdef SQLITE_ENABLE_STAT2 + if( nEq==0 && pProbe->aSample ) pFirstTerm = pTerm; +#endif used |= pTerm->prereqRight; } /* Determine the value of estBound. */ - if( nEq<pProbe->nColumn ){ + if( nEq<pProbe->nColumn && pProbe->bUnordered==0 ){ int j = pProbe->aiColumn[nEq]; if( findTerm(pWC, iCur, j, notReady, WO_LT|WO_LE|WO_GT|WO_GE, pIdx) ){ WhereTerm *pTop = findTerm(pWC, iCur, j, notReady, WO_LT|WO_LE, pIdx); @@ -98027,8 +101226,10 @@ static void bestBtreeIndex( ** in wsFlags. Otherwise, if there is an ORDER BY clause but the index ** will scan rows in a different order, set the bSort variable. */ if( pOrderBy ){ - if( (wsFlags & (WHERE_COLUMN_IN|WHERE_COLUMN_NULL))==0 - && isSortingIndex(pParse,pWC->pMaskSet,pProbe,iCur,pOrderBy,nEq,&rev) + if( (wsFlags & WHERE_COLUMN_IN)==0 + && pProbe->bUnordered==0 + && isSortingIndex(pParse, pWC->pMaskSet, pProbe, iCur, pOrderBy, + nEq, wsFlags, &rev) ){ wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE|WHERE_ORDERBY; wsFlags |= (rev ? WHERE_REVERSE : 0); @@ -98059,8 +101260,8 @@ static void bestBtreeIndex( } /* - ** Estimate the number of rows of output. For an IN operator, - ** do not let the estimate exceed half the rows in the table. + ** Estimate the number of rows of output. For an "x IN (SELECT...)" + ** constraint, do not let the estimate exceed half the rows in the table. */ nRow = (double)(aiRowEst[nEq] * nInMul); if( bInEst && nRow*2>aiRowEst[0] ){ @@ -98068,31 +101269,90 @@ static void bestBtreeIndex( nInMul = (int)(nRow / aiRowEst[nEq]); } - /* Assume constant cost to access a row and logarithmic cost to - ** do a binary search. Hence, the initial cost is the number of output - ** rows plus log2(table-size) times the number of binary searches. +#ifdef SQLITE_ENABLE_STAT2 + /* If the constraint is of the form x=VALUE and histogram + ** data is available for column x, then it might be possible + ** to get a better estimate on the number of rows based on + ** VALUE and how common that value is according to the histogram. */ - cost = nRow + nInMul*estLog(aiRowEst[0]); + if( nRow>(double)1 && nEq==1 && pFirstTerm!=0 ){ + if( pFirstTerm->eOperator & (WO_EQ|WO_ISNULL) ){ + testcase( pFirstTerm->eOperator==WO_EQ ); + testcase( pFirstTerm->eOperator==WO_ISNULL ); + whereEqualScanEst(pParse, pProbe, pFirstTerm->pExpr->pRight, &nRow); + }else if( pFirstTerm->eOperator==WO_IN && bInEst==0 ){ + whereInScanEst(pParse, pProbe, pFirstTerm->pExpr->x.pList, &nRow); + } + } +#endif /* SQLITE_ENABLE_STAT2 */ - /* Adjust the number of rows and the cost downward to reflect rows + /* Adjust the number of output rows and downward to reflect rows ** that are excluded by range constraints. */ nRow = (nRow * (double)estBound) / (double)100; - cost = (cost * (double)estBound) / (double)100; - - /* Add in the estimated cost of sorting the result + if( nRow<1 ) nRow = 1; + + /* Experiments run on real SQLite databases show that the time needed + ** to do a binary search to locate a row in a table or index is roughly + ** log10(N) times the time to move from one row to the next row within + ** a table or index. The actual times can vary, with the size of + ** records being an important factor. Both moves and searches are + ** slower with larger records, presumably because fewer records fit + ** on one page and hence more pages have to be fetched. + ** + ** The ANALYZE command and the sqlite_stat1 and sqlite_stat2 tables do + ** not give us data on the relative sizes of table and index records. + ** So this computation assumes table records are about twice as big + ** as index records */ - if( bSort ){ - cost += cost*estLog(cost); + if( (wsFlags & WHERE_NOT_FULLSCAN)==0 ){ + /* The cost of a full table scan is a number of move operations equal + ** to the number of rows in the table. + ** + ** We add an additional 4x penalty to full table scans. This causes + ** the cost function to err on the side of choosing an index over + ** choosing a full scan. This 4x full-scan penalty is an arguable + ** decision and one which we expect to revisit in the future. But + ** it seems to be working well enough at the moment. + */ + cost = aiRowEst[0]*4; + }else{ + log10N = estLog(aiRowEst[0]); + cost = nRow; + if( pIdx ){ + if( bLookup ){ + /* For an index lookup followed by a table lookup: + ** nInMul index searches to find the start of each index range + ** + nRow steps through the index + ** + nRow table searches to lookup the table entry using the rowid + */ + cost += (nInMul + nRow)*log10N; + }else{ + /* For a covering index: + ** nInMul index searches to find the initial entry + ** + nRow steps through the index + */ + cost += nInMul*log10N; + } + }else{ + /* For a rowid primary key lookup: + ** nInMult table searches to find the initial entry for each range + ** + nRow steps through the table + */ + cost += nInMul*log10N; + } } - /* If all information can be taken directly from the index, we avoid - ** doing table lookups. This reduces the cost by half. (Not really - - ** this needs to be fixed.) + /* Add in the estimated cost of sorting the result. Actual experimental + ** measurements of sorting performance in SQLite show that sorting time + ** adds C*N*log10(N) to the cost, where N is the number of rows to be + ** sorted and C is a factor between 1.95 and 4.3. We will split the + ** difference and select C of 3.0. */ - if( pIdx && bLookup==0 ){ - cost /= (double)2; + if( bSort ){ + cost += nRow*estLog(nRow)*3; } + /**** Cost of using this index has now been computed ****/ /* If there are additional constraints on this table that cannot @@ -98133,15 +101393,19 @@ static void bestBtreeIndex( } }else if( pTerm->eOperator & (WO_LT|WO_LE|WO_GT|WO_GE) ){ if( nSkipRange ){ - /* Ignore the first nBound range constraints since the index + /* Ignore the first nSkipRange range constraints since the index ** has already accounted for these */ nSkipRange--; }else{ /* Assume each additional range constraint reduces the result - ** set size by a factor of 3 */ + ** set size by a factor of 3. Indexed range constraints reduce + ** the search space by a larger factor: 4. We make indexed range + ** more selective intentionally because of the subjective + ** observation that indexed range constraints really are more + ** selective in practice, on average. */ nRow /= 3; } - }else{ + }else if( pTerm->eOperator!=WO_NOOP ){ /* Any other expression lowers the output row count by half */ nRow /= 2; } @@ -98152,10 +101416,10 @@ static void bestBtreeIndex( WHERETRACE(( "%s(%s): nEq=%d nInMul=%d estBound=%d bSort=%d bLookup=%d wsFlags=0x%x\n" - " notReady=0x%llx nRow=%.2f cost=%.2f used=0x%llx\n", + " notReady=0x%llx log10N=%.1f nRow=%.1f cost=%.1f used=0x%llx\n", pSrc->pTab->zName, (pIdx ? pIdx->zName : "ipk"), nEq, nInMul, estBound, bSort, bLookup, wsFlags, - notReady, nRow, cost, used + notReady, log10N, nRow, cost, used )); /* If this index is the best we have seen so far, then record this @@ -98979,7 +102243,9 @@ static Bitmask codeOneLoopStart( if( pRangeStart ){ Expr *pRight = pRangeStart->pExpr->pRight; sqlite3ExprCode(pParse, pRight, regBase+nEq); - sqlite3ExprCodeIsNullJump(v, pRight, regBase+nEq, addrNxt); + if( (pRangeStart->wtFlags & TERM_VNULL)==0 ){ + sqlite3ExprCodeIsNullJump(v, pRight, regBase+nEq, addrNxt); + } if( zStartAff ){ if( sqlite3CompareAffinity(pRight, zStartAff[nEq])==SQLITE_AFF_NONE){ /* Since the comparison is to be performed with no conversions @@ -99018,7 +102284,9 @@ static Bitmask codeOneLoopStart( Expr *pRight = pRangeEnd->pExpr->pRight; sqlite3ExprCacheRemove(pParse, regBase+nEq, 1); sqlite3ExprCode(pParse, pRight, regBase+nEq); - sqlite3ExprCodeIsNullJump(v, pRight, regBase+nEq, addrNxt); + if( (pRangeEnd->wtFlags & TERM_VNULL)==0 ){ + sqlite3ExprCodeIsNullJump(v, pRight, regBase+nEq, addrNxt); + } if( zEndAff ){ if( sqlite3CompareAffinity(pRight, zEndAff[nEq])==SQLITE_AFF_NONE){ /* Since the comparison is to be performed with no conversions @@ -99076,7 +102344,13 @@ static Bitmask codeOneLoopStart( /* Record the instruction used to terminate the loop. Disable ** WHERE clause terms made redundant by the index range scan. */ - pLevel->op = bRev ? OP_Prev : OP_Next; + if( pLevel->plan.wsFlags & WHERE_UNIQUE ){ + pLevel->op = OP_Noop; + }else if( bRev ){ + pLevel->op = OP_Prev; + }else{ + pLevel->op = OP_Next; + } pLevel->p1 = iIdxCur; }else @@ -99122,7 +102396,6 @@ static Bitmask codeOneLoopStart( ** */ WhereClause *pOrWc; /* The OR-clause broken out into subterms */ - WhereTerm *pFinal; /* Final subterm within the OR-clause. */ SrcList *pOrTab; /* Shortened table list or OR-clause generation */ int regReturn = ++pParse->nMem; /* Register used with OP_Gosub */ @@ -99138,7 +102411,6 @@ static Bitmask codeOneLoopStart( assert( pTerm->eOperator==WO_OR ); assert( (pTerm->wtFlags & TERM_ORINFO)!=0 ); pOrWc = &pTerm->u.pOrInfo->wc; - pFinal = &pOrWc->a[pOrWc->nTerm-1]; pLevel->op = OP_Return; pLevel->p1 = regReturn; @@ -99247,7 +102519,6 @@ static Bitmask codeOneLoopStart( ** the use of indices become tests that are evaluated against each row of ** the relevant input tables. */ - k = 0; for(pTerm=pWC->a, j=pWC->nTerm; j>0; j--, pTerm++){ Expr *pE; testcase( pTerm->wtFlags & TERM_VIRTUAL ); /* IMP: R-30575-11662 */ @@ -99265,7 +102536,6 @@ static Bitmask codeOneLoopStart( continue; } sqlite3ExprIfFalse(pParse, pE, addrCont, SQLITE_JUMPIFNULL); - k = 1; pTerm->wtFlags |= TERM_CODED; } @@ -99573,8 +102843,6 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( ** clause. */ notReady = ~(Bitmask)0; - pTabItem = pTabList->a; - pLevel = pWInfo->a; andFlags = ~0; WHERETRACE(("*** Optimizer Start ***\n")); for(i=iFrom=0, pLevel=pWInfo->a; i<nTabList; i++, pLevel++){ @@ -99685,8 +102953,8 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( ** (1) The table must not depend on other tables that have not ** yet run. ** - ** (2) A full-table-scan plan cannot supercede another plan unless - ** it is an "optimal" plan as defined above. + ** (2) A full-table-scan plan cannot supercede indexed plan unless + ** the full-table-scan is an "optimal" plan as defined above. ** ** (3) All tables have an INDEXED BY clause or this table lacks an ** INDEXED BY clause or this table uses the specific @@ -99702,6 +102970,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( */ if( (sCost.used¬Ready)==0 /* (1) */ && (bestJ<0 || (notIndexed&m)!=0 /* (2) */ + || (bestPlan.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 || (sCost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0) && (nUnconstrained==0 || pTabItem->pIndex==0 /* (3) */ || NEVER((sCost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0)) @@ -104105,13 +107374,12 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){ testcase( z[0]=='x' ); testcase( z[0]=='X' ); if( z[1]=='\'' ){ *tokenType = TK_BLOB; - for(i=2; (c=z[i])!=0 && c!='\''; i++){ - if( !sqlite3Isxdigit(c) ){ - *tokenType = TK_ILLEGAL; - } + for(i=2; sqlite3Isxdigit(z[i]); i++){} + if( z[i]!='\'' || i%2 ){ + *tokenType = TK_ILLEGAL; + while( z[i] && z[i]!='\'' ){ i++; } } - if( i%2 || !c ) *tokenType = TK_ILLEGAL; - if( c ) i++; + if( z[i] ) i++; return i; } /* Otherwise fall through to the next case */ @@ -104164,9 +107432,8 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzEr assert( pParse->pNewTable==0 ); assert( pParse->pNewTrigger==0 ); assert( pParse->nVar==0 ); - assert( pParse->nVarExpr==0 ); - assert( pParse->nVarExprAlloc==0 ); - assert( pParse->apVarExpr==0 ); + assert( pParse->nzVar==0 ); + assert( pParse->azVar==0 ); enableLookaside = db->lookaside.bEnabled; if( db->lookaside.pStart ) db->lookaside.bEnabled = 1; while( !db->mallocFailed && zSql[i]!=0 ){ @@ -104260,7 +107527,8 @@ abort_parse: } sqlite3DeleteTrigger(db, pParse->pNewTrigger); - sqlite3DbFree(db, pParse->apVarExpr); + for(i=pParse->nzVar-1; i>=0; i--) sqlite3DbFree(db, pParse->azVar[i]); + sqlite3DbFree(db, pParse->azVar); sqlite3DbFree(db, pParse->aAlias); while( pParse->pAinc ){ AutoincInfo *p = pParse->pAinc; @@ -105029,6 +108297,13 @@ SQLITE_API int sqlite3_config(int op, ...){ sqlite3GlobalConfig.nHeap = va_arg(ap, int); sqlite3GlobalConfig.mnReq = va_arg(ap, int); + if( sqlite3GlobalConfig.mnReq<1 ){ + sqlite3GlobalConfig.mnReq = 1; + }else if( sqlite3GlobalConfig.mnReq>(1<<12) ){ + /* cap min request size at 2^12 */ + sqlite3GlobalConfig.mnReq = (1<<12); + } + if( sqlite3GlobalConfig.pHeap==0 ){ /* If the heap pointer is NULL, then restore the malloc implementation ** back to NULL pointers too. This will cause the malloc to go @@ -105073,6 +108348,11 @@ SQLITE_API int sqlite3_config(int op, ...){ break; } + case SQLITE_CONFIG_URI: { + sqlite3GlobalConfig.bOpenUri = va_arg(ap, int); + break; + } + default: { rc = SQLITE_ERROR; break; @@ -105162,14 +108442,42 @@ SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){ va_start(ap, op); switch( op ){ case SQLITE_DBCONFIG_LOOKASIDE: { - void *pBuf = va_arg(ap, void*); /* IMP: R-21112-12275 */ + void *pBuf = va_arg(ap, void*); /* IMP: R-26835-10964 */ int sz = va_arg(ap, int); /* IMP: R-47871-25994 */ int cnt = va_arg(ap, int); /* IMP: R-04460-53386 */ rc = setupLookaside(db, pBuf, sz, cnt); break; } default: { + static const struct { + int op; /* The opcode */ + u32 mask; /* Mask of the bit in sqlite3.flags to set/clear */ + } aFlagOp[] = { + { SQLITE_DBCONFIG_ENABLE_FKEY, SQLITE_ForeignKeys }, + { SQLITE_DBCONFIG_ENABLE_TRIGGER, SQLITE_EnableTrigger }, + }; + unsigned int i; rc = SQLITE_ERROR; /* IMP: R-42790-23372 */ + for(i=0; i<ArraySize(aFlagOp); i++){ + if( aFlagOp[i].op==op ){ + int onoff = va_arg(ap, int); + int *pRes = va_arg(ap, int*); + int oldFlags = db->flags; + if( onoff>0 ){ + db->flags |= aFlagOp[i].mask; + }else if( onoff==0 ){ + db->flags &= ~aFlagOp[i].mask; + } + if( oldFlags!=db->flags ){ + sqlite3ExpirePreparedStatements(db); + } + if( pRes ){ + *pRes = (db->flags & aFlagOp[i].mask)!=0; + } + rc = SQLITE_OK; + break; + } + } break; } } @@ -105306,7 +108614,8 @@ SQLITE_API int sqlite3_close(sqlite3 *db){ } sqlite3_mutex_enter(db->mutex); - sqlite3ResetInternalSchema(db, 0); + /* Force xDestroy calls on all virtual tables */ + sqlite3ResetInternalSchema(db, -1); /* If a transaction is open, the ResetInternalSchema() call above ** will not have called the xDisconnect() method on any virtual @@ -105349,7 +108658,7 @@ SQLITE_API int sqlite3_close(sqlite3 *db){ } } } - sqlite3ResetInternalSchema(db, 0); + sqlite3ResetInternalSchema(db, -1); /* Tell the code in notify.c that the connection no longer holds any ** locks and does not require any further unlock-notify callbacks. @@ -105440,7 +108749,7 @@ SQLITE_PRIVATE void sqlite3RollbackAll(sqlite3 *db){ if( db->flags&SQLITE_InternChanges ){ sqlite3ExpirePreparedStatements(db); - sqlite3ResetInternalSchema(db, 0); + sqlite3ResetInternalSchema(db, -1); } /* Any deferred constraint violations have now been resolved. */ @@ -105470,7 +108779,7 @@ SQLITE_PRIVATE const char *sqlite3ErrStr(int rc){ /* SQLITE_INTERRUPT */ "interrupted", /* SQLITE_IOERR */ "disk I/O error", /* SQLITE_CORRUPT */ "database disk image is malformed", - /* SQLITE_NOTFOUND */ 0, + /* SQLITE_NOTFOUND */ "unknown operation", /* SQLITE_FULL */ "database or disk is full", /* SQLITE_CANTOPEN */ "unable to open database file", /* SQLITE_PROTOCOL */ "locking protocol", @@ -105509,7 +108818,7 @@ static int sqliteDefaultBusyCallback( { 1, 2, 5, 10, 15, 20, 25, 25, 25, 50, 50, 100 }; static const u8 totals[] = { 0, 1, 3, 8, 18, 33, 53, 78, 103, 128, 178, 228 }; -# define NDELAY (sizeof(delays)/sizeof(delays[0])) +# define NDELAY ArraySize(delays) sqlite3 *db = (sqlite3 *)ptr; int timeout = db->busyTimeout; int delay, prior; @@ -105994,19 +109303,33 @@ SQLITE_API void *sqlite3_wal_hook( #endif } - /* -** Checkpoint database zDb. If zDb is NULL, or if the buffer zDb points -** to contains a zero-length string, all attached databases are -** checkpointed. +** Checkpoint database zDb. */ -SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb){ +SQLITE_API int sqlite3_wal_checkpoint_v2( + sqlite3 *db, /* Database handle */ + const char *zDb, /* Name of attached database (or NULL) */ + int eMode, /* SQLITE_CHECKPOINT_* value */ + int *pnLog, /* OUT: Size of WAL log in frames */ + int *pnCkpt /* OUT: Total number of frames checkpointed */ +){ #ifdef SQLITE_OMIT_WAL return SQLITE_OK; #else int rc; /* Return code */ int iDb = SQLITE_MAX_ATTACHED; /* sqlite3.aDb[] index of db to checkpoint */ + /* Initialize the output variables to -1 in case an error occurs. */ + if( pnLog ) *pnLog = -1; + if( pnCkpt ) *pnCkpt = -1; + + assert( SQLITE_CHECKPOINT_FULL>SQLITE_CHECKPOINT_PASSIVE ); + assert( SQLITE_CHECKPOINT_FULL<SQLITE_CHECKPOINT_RESTART ); + assert( SQLITE_CHECKPOINT_PASSIVE+2==SQLITE_CHECKPOINT_RESTART ); + if( eMode<SQLITE_CHECKPOINT_PASSIVE || eMode>SQLITE_CHECKPOINT_RESTART ){ + return SQLITE_MISUSE; + } + sqlite3_mutex_enter(db->mutex); if( zDb && zDb[0] ){ iDb = sqlite3FindDbName(db, zDb); @@ -106015,7 +109338,7 @@ SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb){ rc = SQLITE_ERROR; sqlite3Error(db, SQLITE_ERROR, "unknown database: %s", zDb); }else{ - rc = sqlite3Checkpoint(db, iDb); + rc = sqlite3Checkpoint(db, iDb, eMode, pnLog, pnCkpt); sqlite3Error(db, rc, 0); } rc = sqlite3ApiExit(db, rc); @@ -106024,6 +109347,16 @@ SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb){ #endif } + +/* +** Checkpoint database zDb. If zDb is NULL, or if the buffer zDb points +** to contains a zero-length string, all attached databases are +** checkpointed. +*/ +SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb){ + return sqlite3_wal_checkpoint_v2(db, zDb, SQLITE_CHECKPOINT_PASSIVE, 0, 0); +} + #ifndef SQLITE_OMIT_WAL /* ** Run a checkpoint on database iDb. This is a no-op if database iDb is @@ -106041,20 +109374,31 @@ SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb){ ** If iDb is passed SQLITE_MAX_ATTACHED, then all attached databases are ** checkpointed. If an error is encountered it is returned immediately - ** no attempt is made to checkpoint any remaining databases. +** +** Parameter eMode is one of SQLITE_CHECKPOINT_PASSIVE, FULL or RESTART. */ -SQLITE_PRIVATE int sqlite3Checkpoint(sqlite3 *db, int iDb){ +SQLITE_PRIVATE int sqlite3Checkpoint(sqlite3 *db, int iDb, int eMode, int *pnLog, int *pnCkpt){ int rc = SQLITE_OK; /* Return code */ int i; /* Used to iterate through attached dbs */ + int bBusy = 0; /* True if SQLITE_BUSY has been encountered */ assert( sqlite3_mutex_held(db->mutex) ); + assert( !pnLog || *pnLog==-1 ); + assert( !pnCkpt || *pnCkpt==-1 ); for(i=0; i<db->nDb && rc==SQLITE_OK; i++){ if( i==iDb || iDb==SQLITE_MAX_ATTACHED ){ - rc = sqlite3BtreeCheckpoint(db->aDb[i].pBt); + rc = sqlite3BtreeCheckpoint(db->aDb[i].pBt, eMode, pnLog, pnCkpt); + pnLog = 0; + pnCkpt = 0; + if( rc==SQLITE_BUSY ){ + bBusy = 1; + rc = SQLITE_OK; + } } } - return rc; + return (rc==SQLITE_OK && bBusy) ? SQLITE_BUSY : rc; } #endif /* SQLITE_OMIT_WAL */ @@ -106307,8 +109651,8 @@ static const int aHardLimit[] = { #if SQLITE_MAX_FUNCTION_ARG<0 || SQLITE_MAX_FUNCTION_ARG>1000 # error SQLITE_MAX_FUNCTION_ARG must be between 0 and 1000 #endif -#if SQLITE_MAX_ATTACHED<0 || SQLITE_MAX_ATTACHED>30 -# error SQLITE_MAX_ATTACHED must be between 0 and 30 +#if SQLITE_MAX_ATTACHED<0 || SQLITE_MAX_ATTACHED>62 +# error SQLITE_MAX_ATTACHED must be between 0 and 62 #endif #if SQLITE_MAX_LIKE_PATTERN_LENGTH<1 # error SQLITE_MAX_LIKE_PATTERN_LENGTH must be at least 1 @@ -106369,6 +109713,236 @@ SQLITE_API int sqlite3_limit(sqlite3 *db, int limitId, int newLimit){ } /* +** This function is used to parse both URIs and non-URI filenames passed by the +** user to API functions sqlite3_open() or sqlite3_open_v2(), and for database +** URIs specified as part of ATTACH statements. +** +** The first argument to this function is the name of the VFS to use (or +** a NULL to signify the default VFS) if the URI does not contain a "vfs=xxx" +** query parameter. The second argument contains the URI (or non-URI filename) +** itself. When this function is called the *pFlags variable should contain +** the default flags to open the database handle with. The value stored in +** *pFlags may be updated before returning if the URI filename contains +** "cache=xxx" or "mode=xxx" query parameters. +** +** If successful, SQLITE_OK is returned. In this case *ppVfs is set to point to +** the VFS that should be used to open the database file. *pzFile is set to +** point to a buffer containing the name of the file to open. It is the +** responsibility of the caller to eventually call sqlite3_free() to release +** this buffer. +** +** If an error occurs, then an SQLite error code is returned and *pzErrMsg +** may be set to point to a buffer containing an English language error +** message. It is the responsibility of the caller to eventually release +** this buffer by calling sqlite3_free(). +*/ +SQLITE_PRIVATE int sqlite3ParseUri( + const char *zDefaultVfs, /* VFS to use if no "vfs=xxx" query option */ + const char *zUri, /* Nul-terminated URI to parse */ + unsigned int *pFlags, /* IN/OUT: SQLITE_OPEN_XXX flags */ + sqlite3_vfs **ppVfs, /* OUT: VFS to use */ + char **pzFile, /* OUT: Filename component of URI */ + char **pzErrMsg /* OUT: Error message (if rc!=SQLITE_OK) */ +){ + int rc = SQLITE_OK; + unsigned int flags = *pFlags; + const char *zVfs = zDefaultVfs; + char *zFile; + char c; + int nUri = sqlite3Strlen30(zUri); + + assert( *pzErrMsg==0 ); + + if( ((flags & SQLITE_OPEN_URI) || sqlite3GlobalConfig.bOpenUri) + && nUri>=5 && memcmp(zUri, "file:", 5)==0 + ){ + char *zOpt; + int eState; /* Parser state when parsing URI */ + int iIn; /* Input character index */ + int iOut = 0; /* Output character index */ + int nByte = nUri+2; /* Bytes of space to allocate */ + + /* Make sure the SQLITE_OPEN_URI flag is set to indicate to the VFS xOpen + ** method that there may be extra parameters following the file-name. */ + flags |= SQLITE_OPEN_URI; + + for(iIn=0; iIn<nUri; iIn++) nByte += (zUri[iIn]=='&'); + zFile = sqlite3_malloc(nByte); + if( !zFile ) return SQLITE_NOMEM; + + /* Discard the scheme and authority segments of the URI. */ + if( zUri[5]=='/' && zUri[6]=='/' ){ + iIn = 7; + while( zUri[iIn] && zUri[iIn]!='/' ) iIn++; + + if( iIn!=7 && (iIn!=16 || memcmp("localhost", &zUri[7], 9)) ){ + *pzErrMsg = sqlite3_mprintf("invalid uri authority: %.*s", + iIn-7, &zUri[7]); + rc = SQLITE_ERROR; + goto parse_uri_out; + } + }else{ + iIn = 5; + } + + /* Copy the filename and any query parameters into the zFile buffer. + ** Decode %HH escape codes along the way. + ** + ** Within this loop, variable eState may be set to 0, 1 or 2, depending + ** on the parsing context. As follows: + ** + ** 0: Parsing file-name. + ** 1: Parsing name section of a name=value query parameter. + ** 2: Parsing value section of a name=value query parameter. + */ + eState = 0; + while( (c = zUri[iIn])!=0 && c!='#' ){ + iIn++; + if( c=='%' + && sqlite3Isxdigit(zUri[iIn]) + && sqlite3Isxdigit(zUri[iIn+1]) + ){ + int octet = (sqlite3HexToInt(zUri[iIn++]) << 4); + octet += sqlite3HexToInt(zUri[iIn++]); + + assert( octet>=0 && octet<256 ); + if( octet==0 ){ + /* This branch is taken when "%00" appears within the URI. In this + ** case we ignore all text in the remainder of the path, name or + ** value currently being parsed. So ignore the current character + ** and skip to the next "?", "=" or "&", as appropriate. */ + while( (c = zUri[iIn])!=0 && c!='#' + && (eState!=0 || c!='?') + && (eState!=1 || (c!='=' && c!='&')) + && (eState!=2 || c!='&') + ){ + iIn++; + } + continue; + } + c = octet; + }else if( eState==1 && (c=='&' || c=='=') ){ + if( zFile[iOut-1]==0 ){ + /* An empty option name. Ignore this option altogether. */ + while( zUri[iIn] && zUri[iIn]!='#' && zUri[iIn-1]!='&' ) iIn++; + continue; + } + if( c=='&' ){ + zFile[iOut++] = '\0'; + }else{ + eState = 2; + } + c = 0; + }else if( (eState==0 && c=='?') || (eState==2 && c=='&') ){ + c = 0; + eState = 1; + } + zFile[iOut++] = c; + } + if( eState==1 ) zFile[iOut++] = '\0'; + zFile[iOut++] = '\0'; + zFile[iOut++] = '\0'; + + /* Check if there were any options specified that should be interpreted + ** here. Options that are interpreted here include "vfs" and those that + ** correspond to flags that may be passed to the sqlite3_open_v2() + ** method. */ + zOpt = &zFile[sqlite3Strlen30(zFile)+1]; + while( zOpt[0] ){ + int nOpt = sqlite3Strlen30(zOpt); + char *zVal = &zOpt[nOpt+1]; + int nVal = sqlite3Strlen30(zVal); + + if( nOpt==3 && memcmp("vfs", zOpt, 3)==0 ){ + zVfs = zVal; + }else{ + struct OpenMode { + const char *z; + int mode; + } *aMode = 0; + char *zModeType = 0; + int mask = 0; + int limit = 0; + + if( nOpt==5 && memcmp("cache", zOpt, 5)==0 ){ + static struct OpenMode aCacheMode[] = { + { "shared", SQLITE_OPEN_SHAREDCACHE }, + { "private", SQLITE_OPEN_PRIVATECACHE }, + { 0, 0 } + }; + + mask = SQLITE_OPEN_SHAREDCACHE|SQLITE_OPEN_PRIVATECACHE; + aMode = aCacheMode; + limit = mask; + zModeType = "cache"; + } + if( nOpt==4 && memcmp("mode", zOpt, 4)==0 ){ + static struct OpenMode aOpenMode[] = { + { "ro", SQLITE_OPEN_READONLY }, + { "rw", SQLITE_OPEN_READWRITE }, + { "rwc", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE }, + { 0, 0 } + }; + + mask = SQLITE_OPEN_READONLY|SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE; + aMode = aOpenMode; + limit = mask & flags; + zModeType = "access"; + } + + if( aMode ){ + int i; + int mode = 0; + for(i=0; aMode[i].z; i++){ + const char *z = aMode[i].z; + if( nVal==sqlite3Strlen30(z) && 0==memcmp(zVal, z, nVal) ){ + mode = aMode[i].mode; + break; + } + } + if( mode==0 ){ + *pzErrMsg = sqlite3_mprintf("no such %s mode: %s", zModeType, zVal); + rc = SQLITE_ERROR; + goto parse_uri_out; + } + if( mode>limit ){ + *pzErrMsg = sqlite3_mprintf("%s mode not allowed: %s", + zModeType, zVal); + rc = SQLITE_PERM; + goto parse_uri_out; + } + flags = (flags & ~mask) | mode; + } + } + + zOpt = &zVal[nVal+1]; + } + + }else{ + zFile = sqlite3_malloc(nUri+2); + if( !zFile ) return SQLITE_NOMEM; + memcpy(zFile, zUri, nUri); + zFile[nUri] = '\0'; + zFile[nUri+1] = '\0'; + } + + *ppVfs = sqlite3_vfs_find(zVfs); + if( *ppVfs==0 ){ + *pzErrMsg = sqlite3_mprintf("no such vfs: %s", zVfs); + rc = SQLITE_ERROR; + } + parse_uri_out: + if( rc!=SQLITE_OK ){ + sqlite3_free(zFile); + zFile = 0; + } + *pFlags = flags; + *pzFile = zFile; + return rc; +} + + +/* ** This routine does the work of opening a database on behalf of ** sqlite3_open() and sqlite3_open16(). The database filename "zFilename" ** is UTF-8 encoded. @@ -106376,12 +109950,14 @@ SQLITE_API int sqlite3_limit(sqlite3 *db, int limitId, int newLimit){ static int openDatabase( const char *zFilename, /* Database filename UTF-8 encoded */ sqlite3 **ppDb, /* OUT: Returned database handle */ - unsigned flags, /* Operational flags */ + unsigned int flags, /* Operational flags */ const char *zVfs /* Name of the VFS to use */ ){ - sqlite3 *db; - int rc; - int isThreadsafe; + sqlite3 *db; /* Store allocated handle here */ + int rc; /* Return code */ + int isThreadsafe; /* True for threadsafe connections */ + char *zOpen = 0; /* Filename argument to pass to BtreeOpen() */ + char *zErrMsg = 0; /* Error message from sqlite3ParseUri() */ *ppDb = 0; #ifndef SQLITE_OMIT_AUTOINIT @@ -106405,7 +109981,7 @@ static int openDatabase( testcase( (1<<(flags&7))==0x02 ); /* READONLY */ testcase( (1<<(flags&7))==0x04 ); /* READWRITE */ testcase( (1<<(flags&7))==0x40 ); /* READWRITE | CREATE */ - if( ((1<<(flags&7)) & 0x46)==0 ) return SQLITE_MISUSE; + if( ((1<<(flags&7)) & 0x46)==0 ) return SQLITE_MISUSE_BKPT; if( sqlite3GlobalConfig.bCoreMutex==0 ){ isThreadsafe = 0; @@ -106427,7 +110003,8 @@ static int openDatabase( ** The SQLITE_OPEN_NOMUTEX and SQLITE_OPEN_FULLMUTEX flags were ** dealt with in the previous code block. Besides these, the only ** valid input flags for sqlite3_open_v2() are SQLITE_OPEN_READONLY, - ** SQLITE_OPEN_READWRITE, and SQLITE_OPEN_CREATE. Silently mask + ** SQLITE_OPEN_READWRITE, SQLITE_OPEN_CREATE, SQLITE_OPEN_SHAREDCACHE, + ** SQLITE_OPEN_PRIVATECACHE, and some reserved bits. Silently mask ** off all other flags. */ flags &= ~( SQLITE_OPEN_DELETEONCLOSE | @@ -106466,7 +110043,7 @@ static int openDatabase( db->autoCommit = 1; db->nextAutovac = -1; db->nextPagesize = 0; - db->flags |= SQLITE_ShortColNames | SQLITE_AutoIndex + db->flags |= SQLITE_ShortColNames | SQLITE_AutoIndex | SQLITE_EnableTrigger #if SQLITE_DEFAULT_FILE_FORMAT<4 | SQLITE_LegacyFileFmt #endif @@ -106476,19 +110053,15 @@ static int openDatabase( #if SQLITE_DEFAULT_RECURSIVE_TRIGGERS | SQLITE_RecTriggers #endif +#if defined(SQLITE_DEFAULT_FOREIGN_KEYS) && SQLITE_DEFAULT_FOREIGN_KEYS + | SQLITE_ForeignKeys +#endif ; sqlite3HashInit(&db->aCollSeq); #ifndef SQLITE_OMIT_VIRTUALTABLE sqlite3HashInit(&db->aModule); #endif - db->pVfs = sqlite3_vfs_find(zVfs); - if( !db->pVfs ){ - rc = SQLITE_ERROR; - sqlite3Error(db, rc, "no such vfs: %s", zVfs); - goto opendb_out; - } - /* Add the default collation sequence BINARY. BINARY works for both UTF-8 ** and UTF-16, so add a version for each to avoid any unnecessary ** conversions. The only error that can occur here is a malloc() failure. @@ -106511,9 +110084,18 @@ static int openDatabase( createCollation(db, "NOCASE", SQLITE_UTF8, SQLITE_COLL_NOCASE, 0, nocaseCollatingFunc, 0); - /* Open the backend database driver */ + /* Parse the filename/URI argument. */ db->openFlags = flags; - rc = sqlite3BtreeOpen(zFilename, db, &db->aDb[0].pBt, 0, + rc = sqlite3ParseUri(zVfs, zFilename, &flags, &db->pVfs, &zOpen, &zErrMsg); + if( rc!=SQLITE_OK ){ + if( rc==SQLITE_NOMEM ) db->mallocFailed = 1; + sqlite3Error(db, rc, zErrMsg ? "%s" : 0, zErrMsg); + sqlite3_free(zErrMsg); + goto opendb_out; + } + + /* Open the backend database driver */ + rc = sqlite3BtreeOpen(db->pVfs, zOpen, db, &db->aDb[0].pBt, 0, flags | SQLITE_OPEN_MAIN_DB); if( rc!=SQLITE_OK ){ if( rc==SQLITE_IOERR_NOMEM ){ @@ -106606,6 +110188,7 @@ static int openDatabase( sqlite3_wal_autocheckpoint(db, SQLITE_DEFAULT_WAL_AUTOCHECKPOINT); opendb_out: + sqlite3_free(zOpen); if( db ){ assert( db->mutex!=0 || isThreadsafe==0 || sqlite3GlobalConfig.bFullMutex==0 ); sqlite3_mutex_leave(db->mutex); @@ -106637,7 +110220,7 @@ SQLITE_API int sqlite3_open_v2( int flags, /* Flags */ const char *zVfs /* Name of VFS module to use */ ){ - return openDatabase(filename, ppDb, flags, zVfs); + return openDatabase(filename, ppDb, (unsigned int)flags, zVfs); } #ifndef SQLITE_OMIT_UTF16 @@ -107015,6 +110598,8 @@ SQLITE_API int sqlite3_file_control(sqlite3 *db, const char *zDbName, int op, vo rc = SQLITE_OK; }else if( fd->pMethods ){ rc = sqlite3OsFileControl(fd, op, pArg); + }else{ + rc = SQLITE_NOTFOUND; } sqlite3BtreeLeave(pBtree); } @@ -107240,12 +110825,45 @@ SQLITE_API int sqlite3_test_control(int op, ...){ break; } + /* sqlite3_test_control(SQLITE_TESTCTRL_LOCALTIME_FAULT, int onoff); + ** + ** If parameter onoff is non-zero, configure the wrappers so that all + ** subsequent calls to localtime() and variants fail. If onoff is zero, + ** undo this setting. + */ + case SQLITE_TESTCTRL_LOCALTIME_FAULT: { + sqlite3GlobalConfig.bLocaltimeFault = va_arg(ap, int); + break; + } + } va_end(ap); #endif /* SQLITE_OMIT_BUILTIN_TEST */ return rc; } +/* +** This is a utility routine, useful to VFS implementations, that checks +** to see if a database file was a URI that contained a specific query +** parameter, and if so obtains the value of the query parameter. +** +** The zFilename argument is the filename pointer passed into the xOpen() +** method of a VFS implementation. The zParam argument is the name of the +** query parameter we seek. This routine returns the value of the zParam +** parameter if it exists. If the parameter does not exist, this routine +** returns a NULL pointer. +*/ +SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam){ + zFilename += sqlite3Strlen30(zFilename) + 1; + while( zFilename[0] ){ + int x = strcmp(zFilename, zParam); + zFilename += sqlite3Strlen30(zFilename) + 1; + if( x==0 ) return zFilename; + zFilename += sqlite3Strlen30(zFilename) + 1; + } + return 0; +} + /************** End of main.c ************************************************/ /************** Begin file notify.c ******************************************/ /* @@ -107875,12 +111493,6 @@ SQLITE_PRIVATE void sqlite3ConnectionClosed(sqlite3 *db){ ** into a single segment. */ -#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) - -#if defined(SQLITE_ENABLE_FTS3) && !defined(SQLITE_CORE) -# define SQLITE_CORE 1 -#endif - /************** Include fts3Int.h in the middle of fts3.c ********************/ /************** Begin file fts3Int.h *****************************************/ /* @@ -107896,7 +111508,6 @@ SQLITE_PRIVATE void sqlite3ConnectionClosed(sqlite3 *db){ ****************************************************************************** ** */ - #ifndef _FTSINT_H #define _FTSINT_H @@ -107904,6 +111515,16 @@ SQLITE_PRIVATE void sqlite3ConnectionClosed(sqlite3 *db){ # define NDEBUG 1 #endif +/* +** FTS4 is really an extension for FTS3. It is enabled using the +** SQLITE_ENABLE_FTS3 macro. But to avoid confusion we also all +** the SQLITE_ENABLE_FTS4 macro to serve as an alisse for SQLITE_ENABLE_FTS3. +*/ +#if defined(SQLITE_ENABLE_FTS4) && !defined(SQLITE_ENABLE_FTS3) +# define SQLITE_ENABLE_FTS3 +#endif + +#ifdef SQLITE_ENABLE_FTS3 /************** Include fts3_tokenizer.h in the middle of fts3Int.h **********/ /************** Begin file fts3_tokenizer.h **********************************/ /* @@ -108202,6 +111823,11 @@ SQLITE_PRIVATE Fts3HashElem *sqlite3Fts3HashFindElem(const Fts3Hash *, const voi */ #define SizeofArray(X) ((int)(sizeof(X)/sizeof(X[0]))) + +#ifndef MIN +# define MIN(x,y) ((x)<(y)?(x):(y)) +#endif + /* ** Maximum length of a varint encoded integer. The varint format is different ** from that used by SQLite, so the maximum length is 10, not 9. @@ -108209,6 +111835,24 @@ SQLITE_PRIVATE Fts3HashElem *sqlite3Fts3HashFindElem(const Fts3Hash *, const voi #define FTS3_VARINT_MAX 10 /* +** FTS4 virtual tables may maintain multiple indexes - one index of all terms +** in the document set and zero or more prefix indexes. All indexes are stored +** as one or more b+-trees in the %_segments and %_segdir tables. +** +** It is possible to determine which index a b+-tree belongs to based on the +** value stored in the "%_segdir.level" column. Given this value L, the index +** that the b+-tree belongs to is (L<<10). In other words, all b+-trees with +** level values between 0 and 1023 (inclusive) belong to index 0, all levels +** between 1024 and 2047 to index 1, and so on. +** +** It is considered impossible for an index to use more than 1024 levels. In +** theory though this may happen, but only after at least +** (FTS3_MERGE_COUNT^1024) separate flushes of the pending-terms tables. +*/ +#define FTS3_SEGDIR_MAXLEVEL 1024 +#define FTS3_SEGDIR_MAXLEVEL_STR "1024" + +/* ** The testcase() macro is only used by the amalgamation. If undefined, ** make it a no-op. */ @@ -108247,22 +111891,43 @@ typedef unsigned char u8; /* 1-byte (or larger) unsigned integer */ typedef short int i16; /* 2-byte (or larger) signed integer */ typedef unsigned int u32; /* 4-byte unsigned integer */ typedef sqlite3_uint64 u64; /* 8-byte unsigned integer */ + /* ** Macro used to suppress compiler warnings for unused parameters. */ #define UNUSED_PARAMETER(x) (void)(x) + +/* +** Activate assert() only if SQLITE_TEST is enabled. +*/ +#if !defined(NDEBUG) && !defined(SQLITE_DEBUG) +# define NDEBUG 1 #endif +/* +** The TESTONLY macro is used to enclose variable declarations or +** other bits of code that are needed to support the arguments +** within testcase() and assert() macros. +*/ +#if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST) +# define TESTONLY(X) X +#else +# define TESTONLY(X) +#endif + +#endif /* SQLITE_AMALGAMATION */ + typedef struct Fts3Table Fts3Table; typedef struct Fts3Cursor Fts3Cursor; typedef struct Fts3Expr Fts3Expr; typedef struct Fts3Phrase Fts3Phrase; typedef struct Fts3PhraseToken Fts3PhraseToken; +typedef struct Fts3Doclist Fts3Doclist; typedef struct Fts3SegFilter Fts3SegFilter; typedef struct Fts3DeferredToken Fts3DeferredToken; typedef struct Fts3SegReader Fts3SegReader; -typedef struct Fts3SegReaderArray Fts3SegReaderArray; +typedef struct Fts3MultiSegReader Fts3MultiSegReader; /* ** A connection to a fulltext index is an instance of the following @@ -108283,26 +111948,51 @@ struct Fts3Table { /* Precompiled statements used by the implementation. Each of these ** statements is run and reset within a single virtual table API call. */ - sqlite3_stmt *aStmt[24]; + sqlite3_stmt *aStmt[27]; + + char *zReadExprlist; + char *zWriteExprlist; int nNodeSize; /* Soft limit for node size */ u8 bHasStat; /* True if %_stat table exists */ u8 bHasDocsize; /* True if %_docsize table exists */ + u8 bDescIdx; /* True if doclists are in reverse order */ int nPgsz; /* Page size for host database */ char *zSegmentsTbl; /* Name of %_segments table */ sqlite3_blob *pSegments; /* Blob handle open on %_segments table */ - /* The following hash table is used to buffer pending index updates during + /* TODO: Fix the first paragraph of this comment. + ** + ** The following hash table is used to buffer pending index updates during ** transactions. Variable nPendingData estimates the memory size of the ** pending data, including hash table overhead, but not malloc overhead. ** When nPendingData exceeds nMaxPendingData, the buffer is flushed ** automatically. Variable iPrevDocid is the docid of the most recently ** inserted record. + ** + ** A single FTS4 table may have multiple full-text indexes. For each index + ** there is an entry in the aIndex[] array. Index 0 is an index of all the + ** terms that appear in the document set. Each subsequent index in aIndex[] + ** is an index of prefixes of a specific length. + */ + int nIndex; /* Size of aIndex[] */ + struct Fts3Index { + int nPrefix; /* Prefix length (0 for main terms index) */ + Fts3Hash hPending; /* Pending terms table for this index */ + } *aIndex; + int nMaxPendingData; /* Max pending data before flush to disk */ + int nPendingData; /* Current bytes of pending data */ + sqlite_int64 iPrevDocid; /* Docid of most recently inserted document */ + +#if defined(SQLITE_DEBUG) + /* State variables used for validating that the transaction control + ** methods of the virtual table are called at appropriate times. These + ** values do not contribution to the FTS computation; they are used for + ** verifying the SQLite core. */ - int nMaxPendingData; - int nPendingData; - sqlite_int64 iPrevDocid; - Fts3Hash pendingTerms; + int inTransaction; /* True after xBegin but before xCommit/xRollback */ + int mxSavepoint; /* Largest valid xSavepoint integer */ +#endif }; /* @@ -108323,8 +112013,10 @@ struct Fts3Cursor { char *pNextId; /* Pointer into the body of aDoclist */ char *aDoclist; /* List of docids for full-text queries */ int nDoclist; /* Size of buffer at aDoclist */ + u8 bDesc; /* True to sort in descending order */ int eEvalmode; /* An FTS3_EVAL_XX constant */ int nRowAvg; /* Average size of database rows, in pages */ + sqlite3_int64 nDoc; /* Documents in table */ int isMatchinfoNeeded; /* True when aMatchinfo[] needs filling in */ u32 *aMatchinfo; /* Information about most recent match */ @@ -108355,47 +112047,70 @@ struct Fts3Cursor { #define FTS3_DOCID_SEARCH 1 /* Lookup by rowid on %_content table */ #define FTS3_FULLTEXT_SEARCH 2 /* Full-text index search */ + +struct Fts3Doclist { + char *aAll; /* Array containing doclist (or NULL) */ + int nAll; /* Size of a[] in bytes */ + char *pNextDocid; /* Pointer to next docid */ + + sqlite3_int64 iDocid; /* Current docid (if pList!=0) */ + int bFreeList; /* True if pList should be sqlite3_free()d */ + char *pList; /* Pointer to position list following iDocid */ + int nList; /* Length of position list */ +} doclist; + /* ** A "phrase" is a sequence of one or more tokens that must match in ** sequence. A single token is the base case and the most common case. ** For a sequence of tokens contained in double-quotes (i.e. "one two three") ** nToken will be the number of tokens in the string. -** -** The nDocMatch and nMatch variables contain data that may be used by the -** matchinfo() function. They are populated when the full-text index is -** queried for hits on the phrase. If one or more tokens in the phrase -** are deferred, the nDocMatch and nMatch variables are populated based -** on the assumption that the */ struct Fts3PhraseToken { char *z; /* Text of the token */ int n; /* Number of bytes in buffer z */ int isPrefix; /* True if token ends with a "*" character */ - int bFulltext; /* True if full-text index was used */ - Fts3SegReaderArray *pArray; /* Segment-reader for this token */ + + /* Variables above this point are populated when the expression is + ** parsed (by code in fts3_expr.c). Below this point the variables are + ** used when evaluating the expression. */ Fts3DeferredToken *pDeferred; /* Deferred token object for this token */ + Fts3MultiSegReader *pSegcsr; /* Segment-reader for this token */ }; struct Fts3Phrase { - /* Variables populated by fts3_expr.c when parsing a MATCH expression */ + /* Cache of doclist for this phrase. */ + Fts3Doclist doclist; + int bIncr; /* True if doclist is loaded incrementally */ + int iDoclistToken; + + /* Variables below this point are populated by fts3_expr.c when parsing + ** a MATCH expression. Everything above is part of the evaluation phase. + */ int nToken; /* Number of tokens in the phrase */ int iColumn; /* Index of column this phrase must match */ - int isNot; /* Phrase prefixed by unary not (-) operator */ Fts3PhraseToken aToken[1]; /* One entry for each token in the phrase */ }; /* ** A tree of these objects forms the RHS of a MATCH operator. ** -** If Fts3Expr.eType is either FTSQUERY_NEAR or FTSQUERY_PHRASE and isLoaded -** is true, then aDoclist points to a malloced buffer, size nDoclist bytes, -** containing the results of the NEAR or phrase query in FTS3 doclist -** format. As usual, the initial "Length" field found in doclists stored -** on disk is omitted from this buffer. +** If Fts3Expr.eType is FTSQUERY_PHRASE and isLoaded is true, then aDoclist +** points to a malloced buffer, size nDoclist bytes, containing the results +** of this phrase query in FTS3 doclist format. As usual, the initial +** "Length" field found in doclists stored on disk is omitted from this +** buffer. +** +** Variable aMI is used only for FTSQUERY_NEAR nodes to store the global +** matchinfo data. If it is not NULL, it points to an array of size nCol*3, +** where nCol is the number of columns in the queried FTS table. The array +** is populated as follows: +** +** aMI[iCol*3 + 0] = Undefined +** aMI[iCol*3 + 1] = Number of occurrences +** aMI[iCol*3 + 2] = Number of rows containing at least one instance ** -** Variable pCurrent always points to the start of a docid field within -** aDoclist. Since the doclist is usually scanned in docid order, this can -** be used to accelerate seeking to the required docid within the doclist. +** The aMI array is allocated using sqlite3_malloc(). It should be freed +** when the expression node is. */ struct Fts3Expr { int eType; /* One of the FTSQUERY_XXX values defined below */ @@ -108405,12 +112120,13 @@ struct Fts3Expr { Fts3Expr *pRight; /* Right operand */ Fts3Phrase *pPhrase; /* Valid if eType==FTSQUERY_PHRASE */ - int isLoaded; /* True if aDoclist/nDoclist are initialized. */ - char *aDoclist; /* Buffer containing doclist */ - int nDoclist; /* Size of aDoclist in bytes */ + /* The following are used by the fts3_eval.c module. */ + sqlite3_int64 iDocid; /* Current docid */ + u8 bEof; /* True this expression is at EOF already */ + u8 bStart; /* True if iDocid is valid */ + u8 bDeferred; /* True if this expression is entirely deferred */ - sqlite3_int64 iCurrent; - char *pCurrent; + u32 *aMI; }; /* @@ -108438,16 +112154,12 @@ SQLITE_PRIVATE void sqlite3Fts3PendingTermsClear(Fts3Table *); SQLITE_PRIVATE int sqlite3Fts3Optimize(Fts3Table *); SQLITE_PRIVATE int sqlite3Fts3SegReaderNew(int, sqlite3_int64, sqlite3_int64, sqlite3_int64, const char *, int, Fts3SegReader**); -SQLITE_PRIVATE int sqlite3Fts3SegReaderPending(Fts3Table*,const char*,int,int,Fts3SegReader**); +SQLITE_PRIVATE int sqlite3Fts3SegReaderPending( + Fts3Table*,int,const char*,int,int,Fts3SegReader**); SQLITE_PRIVATE void sqlite3Fts3SegReaderFree(Fts3SegReader *); -SQLITE_PRIVATE int sqlite3Fts3SegReaderIterate( - Fts3Table *, Fts3SegReader **, int, Fts3SegFilter *, - int (*)(Fts3Table *, void *, char *, int, char *, int), void * -); -SQLITE_PRIVATE int sqlite3Fts3SegReaderCost(Fts3Cursor *, Fts3SegReader *, int *); -SQLITE_PRIVATE int sqlite3Fts3AllSegdirs(Fts3Table*, sqlite3_stmt **); +SQLITE_PRIVATE int sqlite3Fts3AllSegdirs(Fts3Table*, int, int, sqlite3_stmt **); SQLITE_PRIVATE int sqlite3Fts3ReadLock(Fts3Table *); -SQLITE_PRIVATE int sqlite3Fts3ReadBlock(Fts3Table*, sqlite3_int64, char **, int*); +SQLITE_PRIVATE int sqlite3Fts3ReadBlock(Fts3Table*, sqlite3_int64, char **, int*, int*); SQLITE_PRIVATE int sqlite3Fts3SelectDoctotal(Fts3Table *, sqlite3_stmt **); SQLITE_PRIVATE int sqlite3Fts3SelectDocsize(Fts3Table *, sqlite3_int64, sqlite3_stmt **); @@ -108456,15 +112168,25 @@ SQLITE_PRIVATE void sqlite3Fts3FreeDeferredTokens(Fts3Cursor *); SQLITE_PRIVATE int sqlite3Fts3DeferToken(Fts3Cursor *, Fts3PhraseToken *, int); SQLITE_PRIVATE int sqlite3Fts3CacheDeferredDoclists(Fts3Cursor *); SQLITE_PRIVATE void sqlite3Fts3FreeDeferredDoclists(Fts3Cursor *); -SQLITE_PRIVATE char *sqlite3Fts3DeferredDoclist(Fts3DeferredToken *, int *); - SQLITE_PRIVATE void sqlite3Fts3SegmentsClose(Fts3Table *); +/* Special values interpreted by sqlite3SegReaderCursor() */ +#define FTS3_SEGCURSOR_PENDING -1 +#define FTS3_SEGCURSOR_ALL -2 + +SQLITE_PRIVATE int sqlite3Fts3SegReaderStart(Fts3Table*, Fts3MultiSegReader*, Fts3SegFilter*); +SQLITE_PRIVATE int sqlite3Fts3SegReaderStep(Fts3Table *, Fts3MultiSegReader *); +SQLITE_PRIVATE void sqlite3Fts3SegReaderFinish(Fts3MultiSegReader *); + +SQLITE_PRIVATE int sqlite3Fts3SegReaderCursor( + Fts3Table *, int, int, const char *, int, int, int, Fts3MultiSegReader *); + /* Flags allowed as part of the 4th argument to SegmentReaderIterate() */ #define FTS3_SEGMENT_REQUIRE_POS 0x00000001 #define FTS3_SEGMENT_IGNORE_EMPTY 0x00000002 #define FTS3_SEGMENT_COLUMN_FILTER 0x00000004 #define FTS3_SEGMENT_PREFIX 0x00000008 +#define FTS3_SEGMENT_SCAN 0x00000010 /* Type passed as 4th argument to SegmentReaderIterate() */ struct Fts3SegFilter { @@ -108474,17 +112196,38 @@ struct Fts3SegFilter { int flags; }; +struct Fts3MultiSegReader { + /* Used internally by sqlite3Fts3SegReaderXXX() calls */ + Fts3SegReader **apSegment; /* Array of Fts3SegReader objects */ + int nSegment; /* Size of apSegment array */ + int nAdvance; /* How many seg-readers to advance */ + Fts3SegFilter *pFilter; /* Pointer to filter object */ + char *aBuffer; /* Buffer to merge doclists in */ + int nBuffer; /* Allocated size of aBuffer[] in bytes */ + + int iColFilter; /* If >=0, filter for this column */ + int bRestart; + + /* Used by fts3.c only. */ + int nCost; /* Cost of running iterator */ + int bLookup; /* True if a lookup of a single entry. */ + + /* Output values. Valid only after Fts3SegReaderStep() returns SQLITE_ROW. */ + char *zTerm; /* Pointer to term buffer */ + int nTerm; /* Size of zTerm in bytes */ + char *aDoclist; /* Pointer to doclist buffer */ + int nDoclist; /* Size of aDoclist[] in bytes */ +}; + /* fts3.c */ SQLITE_PRIVATE int sqlite3Fts3PutVarint(char *, sqlite3_int64); SQLITE_PRIVATE int sqlite3Fts3GetVarint(const char *, sqlite_int64 *); SQLITE_PRIVATE int sqlite3Fts3GetVarint32(const char *, int *); SQLITE_PRIVATE int sqlite3Fts3VarintLen(sqlite3_uint64); SQLITE_PRIVATE void sqlite3Fts3Dequote(char *); +SQLITE_PRIVATE void sqlite3Fts3DoclistPrev(int,char*,int,char**,sqlite3_int64*,int*,u8*); -SQLITE_PRIVATE char *sqlite3Fts3FindPositions(Fts3Expr *, sqlite3_int64, int); -SQLITE_PRIVATE int sqlite3Fts3ExprLoadDoclist(Fts3Cursor *, Fts3Expr *); -SQLITE_PRIVATE int sqlite3Fts3ExprLoadFtDoclist(Fts3Cursor *, Fts3Expr *, char **, int *); -SQLITE_PRIVATE int sqlite3Fts3ExprNearTrim(Fts3Expr *, Fts3Expr *, int); +SQLITE_PRIVATE int sqlite3Fts3EvalPhraseStats(Fts3Cursor *, Fts3Expr *, u32 *); /* fts3_tokenizer.c */ SQLITE_PRIVATE const char *sqlite3Fts3NextToken(const char *, int *); @@ -108508,12 +112251,45 @@ SQLITE_PRIVATE int sqlite3Fts3ExprParse(sqlite3_tokenizer *, SQLITE_PRIVATE void sqlite3Fts3ExprFree(Fts3Expr *); #ifdef SQLITE_TEST SQLITE_PRIVATE int sqlite3Fts3ExprInitTestInterface(sqlite3 *db); +SQLITE_PRIVATE int sqlite3Fts3InitTerm(sqlite3 *db); #endif +/* fts3_aux.c */ +SQLITE_PRIVATE int sqlite3Fts3InitAux(sqlite3 *db); + +SQLITE_PRIVATE int sqlite3Fts3TermSegReaderCursor( + Fts3Cursor *pCsr, /* Virtual table cursor handle */ + const char *zTerm, /* Term to query for */ + int nTerm, /* Size of zTerm in bytes */ + int isPrefix, /* True for a prefix search */ + Fts3MultiSegReader **ppSegcsr /* OUT: Allocated seg-reader cursor */ +); + +SQLITE_PRIVATE void sqlite3Fts3EvalPhraseCleanup(Fts3Phrase *); + +SQLITE_PRIVATE int sqlite3Fts3EvalStart(Fts3Cursor *, Fts3Expr *, int); +SQLITE_PRIVATE int sqlite3Fts3EvalNext(Fts3Cursor *pCsr); + +SQLITE_PRIVATE int sqlite3Fts3MsrIncrStart( + Fts3Table*, Fts3MultiSegReader*, int, const char*, int); +SQLITE_PRIVATE int sqlite3Fts3MsrIncrNext( + Fts3Table *, Fts3MultiSegReader *, sqlite3_int64 *, char **, int *); +SQLITE_PRIVATE char *sqlite3Fts3EvalPhrasePoslist(Fts3Cursor *, Fts3Expr *, int iCol); +SQLITE_PRIVATE int sqlite3Fts3MsrOvfl(Fts3Cursor *, Fts3MultiSegReader *, int *); +SQLITE_PRIVATE int sqlite3Fts3MsrIncrRestart(Fts3MultiSegReader *pCsr); + +SQLITE_PRIVATE int sqlite3Fts3DeferredTokenList(Fts3DeferredToken *, char **, int *); + +#endif /* SQLITE_ENABLE_FTS3 */ #endif /* _FTSINT_H */ /************** End of fts3Int.h *********************************************/ /************** Continuing where we left off in fts3.c ***********************/ +#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) + +#if defined(SQLITE_ENABLE_FTS3) && !defined(SQLITE_CORE) +# define SQLITE_CORE 1 +#endif #ifndef SQLITE_CORE @@ -108627,17 +112403,31 @@ static void fts3GetDeltaVarint(char **pp, sqlite3_int64 *pVal){ } /* -** As long as *pp has not reached its end (pEnd), then do the same -** as fts3GetDeltaVarint(): read a single varint and add it to *pVal. -** But if we have reached the end of the varint, just set *pp=0 and -** leave *pVal unchanged. +** When this function is called, *pp points to the first byte following a +** varint that is part of a doclist (or position-list, or any other list +** of varints). This function moves *pp to point to the start of that varint, +** and sets *pVal by the varint value. +** +** Argument pStart points to the first byte of the doclist that the +** varint is part of. */ -static void fts3GetDeltaVarint2(char **pp, char *pEnd, sqlite3_int64 *pVal){ - if( *pp>=pEnd ){ - *pp = 0; - }else{ - fts3GetDeltaVarint(pp, pVal); - } +static void fts3GetReverseVarint( + char **pp, + char *pStart, + sqlite3_int64 *pVal +){ + sqlite3_int64 iVal; + char *p = *pp; + + /* Pointer p now points at the first byte past the varint we are + ** interested in. So, unless the doclist is corrupt, the 0x80 bit is + ** clear on character p[-1]. */ + for(p = (*pp)-2; p>=pStart && *p&0x80; p--); + p++; + *pp = p; + + sqlite3Fts3GetVarint(p, &iVal); + *pVal = iVal; } /* @@ -108655,6 +112445,8 @@ static int fts3DisconnectMethod(sqlite3_vtab *pVtab){ sqlite3_finalize(p->aStmt[i]); } sqlite3_free(p->zSegmentsTbl); + sqlite3_free(p->zReadExprlist); + sqlite3_free(p->zWriteExprlist); /* Invoke the tokenizer destructor to free the tokenizer. */ p->pTokenizer->pModule->xDestroy(p->pTokenizer); @@ -108729,6 +112521,8 @@ static void fts3DeclareVtab(int *pRc, Fts3Table *p){ char *zSql; /* SQL statement passed to declare_vtab() */ char *zCols; /* List of user defined columns */ + sqlite3_vtab_config(p->db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1); + /* Create a list of user columns for the virtual table */ zCols = sqlite3_mprintf("%Q, ", p->azColumn[0]); for(i=1; zCols && i<p->nColumn; i++){ @@ -108834,6 +112628,9 @@ static void fts3DatabasePageSize(int *pRc, Fts3Table *p){ sqlite3_step(pStmt); p->nPgsz = sqlite3_column_int(pStmt, 0); rc = sqlite3_finalize(pStmt); + }else if( rc==SQLITE_AUTH ){ + p->nPgsz = 1024; + rc = SQLITE_OK; } } assert( p->nPgsz>0 || rc!=SQLITE_OK ); @@ -108873,6 +112670,194 @@ static int fts3IsSpecialColumn( } /* +** Append the output of a printf() style formatting to an existing string. +*/ +static void fts3Appendf( + int *pRc, /* IN/OUT: Error code */ + char **pz, /* IN/OUT: Pointer to string buffer */ + const char *zFormat, /* Printf format string to append */ + ... /* Arguments for printf format string */ +){ + if( *pRc==SQLITE_OK ){ + va_list ap; + char *z; + va_start(ap, zFormat); + z = sqlite3_vmprintf(zFormat, ap); + if( z && *pz ){ + char *z2 = sqlite3_mprintf("%s%s", *pz, z); + sqlite3_free(z); + z = z2; + } + if( z==0 ) *pRc = SQLITE_NOMEM; + va_end(ap); + sqlite3_free(*pz); + *pz = z; + } +} + +/* +** Return a copy of input string zInput enclosed in double-quotes (") and +** with all double quote characters escaped. For example: +** +** fts3QuoteId("un \"zip\"") -> "un \"\"zip\"\"" +** +** The pointer returned points to memory obtained from sqlite3_malloc(). It +** is the callers responsibility to call sqlite3_free() to release this +** memory. +*/ +static char *fts3QuoteId(char const *zInput){ + int nRet; + char *zRet; + nRet = 2 + strlen(zInput)*2 + 1; + zRet = sqlite3_malloc(nRet); + if( zRet ){ + int i; + char *z = zRet; + *(z++) = '"'; + for(i=0; zInput[i]; i++){ + if( zInput[i]=='"' ) *(z++) = '"'; + *(z++) = zInput[i]; + } + *(z++) = '"'; + *(z++) = '\0'; + } + return zRet; +} + +/* +** Return a list of comma separated SQL expressions that could be used +** in a SELECT statement such as the following: +** +** SELECT <list of expressions> FROM %_content AS x ... +** +** to return the docid, followed by each column of text data in order +** from left to write. If parameter zFunc is not NULL, then instead of +** being returned directly each column of text data is passed to an SQL +** function named zFunc first. For example, if zFunc is "unzip" and the +** table has the three user-defined columns "a", "b", and "c", the following +** string is returned: +** +** "docid, unzip(x.'a'), unzip(x.'b'), unzip(x.'c')" +** +** The pointer returned points to a buffer allocated by sqlite3_malloc(). It +** is the responsibility of the caller to eventually free it. +** +** If *pRc is not SQLITE_OK when this function is called, it is a no-op (and +** a NULL pointer is returned). Otherwise, if an OOM error is encountered +** by this function, NULL is returned and *pRc is set to SQLITE_NOMEM. If +** no error occurs, *pRc is left unmodified. +*/ +static char *fts3ReadExprList(Fts3Table *p, const char *zFunc, int *pRc){ + char *zRet = 0; + char *zFree = 0; + char *zFunction; + int i; + + if( !zFunc ){ + zFunction = ""; + }else{ + zFree = zFunction = fts3QuoteId(zFunc); + } + fts3Appendf(pRc, &zRet, "docid"); + for(i=0; i<p->nColumn; i++){ + fts3Appendf(pRc, &zRet, ",%s(x.'c%d%q')", zFunction, i, p->azColumn[i]); + } + sqlite3_free(zFree); + return zRet; +} + +/* +** Return a list of N comma separated question marks, where N is the number +** of columns in the %_content table (one for the docid plus one for each +** user-defined text column). +** +** If argument zFunc is not NULL, then all but the first question mark +** is preceded by zFunc and an open bracket, and followed by a closed +** bracket. For example, if zFunc is "zip" and the FTS3 table has three +** user-defined text columns, the following string is returned: +** +** "?, zip(?), zip(?), zip(?)" +** +** The pointer returned points to a buffer allocated by sqlite3_malloc(). It +** is the responsibility of the caller to eventually free it. +** +** If *pRc is not SQLITE_OK when this function is called, it is a no-op (and +** a NULL pointer is returned). Otherwise, if an OOM error is encountered +** by this function, NULL is returned and *pRc is set to SQLITE_NOMEM. If +** no error occurs, *pRc is left unmodified. +*/ +static char *fts3WriteExprList(Fts3Table *p, const char *zFunc, int *pRc){ + char *zRet = 0; + char *zFree = 0; + char *zFunction; + int i; + + if( !zFunc ){ + zFunction = ""; + }else{ + zFree = zFunction = fts3QuoteId(zFunc); + } + fts3Appendf(pRc, &zRet, "?"); + for(i=0; i<p->nColumn; i++){ + fts3Appendf(pRc, &zRet, ",%s(?)", zFunction); + } + sqlite3_free(zFree); + return zRet; +} + +static int fts3GobbleInt(const char **pp, int *pnOut){ + const char *p = *pp; + int nInt = 0; + for(p=*pp; p[0]>='0' && p[0]<='9'; p++){ + nInt = nInt * 10 + (p[0] - '0'); + } + if( p==*pp ) return SQLITE_ERROR; + *pnOut = nInt; + *pp = p; + return SQLITE_OK; +} + + +static int fts3PrefixParameter( + const char *zParam, /* ABC in prefix=ABC parameter to parse */ + int *pnIndex, /* OUT: size of *apIndex[] array */ + struct Fts3Index **apIndex, /* OUT: Array of indexes for this table */ + struct Fts3Index **apFree /* OUT: Free this with sqlite3_free() */ +){ + struct Fts3Index *aIndex; + int nIndex = 1; + + if( zParam && zParam[0] ){ + const char *p; + nIndex++; + for(p=zParam; *p; p++){ + if( *p==',' ) nIndex++; + } + } + + aIndex = sqlite3_malloc(sizeof(struct Fts3Index) * nIndex); + *apIndex = *apFree = aIndex; + *pnIndex = nIndex; + if( !aIndex ){ + return SQLITE_NOMEM; + } + + memset(aIndex, 0, sizeof(struct Fts3Index) * nIndex); + if( zParam ){ + const char *p = zParam; + int i; + for(i=1; i<nIndex; i++){ + int nPrefix; + if( fts3GobbleInt(&p, &nPrefix) ) return SQLITE_ERROR; + aIndex[i].nPrefix = nPrefix; + p++; + } + } + + return SQLITE_OK; +} + +/* ** This function is the implementation of both the xConnect and xCreate ** methods of the FTS3 virtual table. ** @@ -108904,10 +112889,20 @@ static int fts3InitVtab( int nDb; /* Bytes required to hold database name */ int nName; /* Bytes required to hold table name */ int isFts4 = (argv[0][3]=='4'); /* True for FTS4, false for FTS3 */ - int bNoDocsize = 0; /* True to omit %_docsize table */ const char **aCol; /* Array of column names */ sqlite3_tokenizer *pTokenizer = 0; /* Tokenizer for this table */ + int nIndex; /* Size of aIndex[] array */ + struct Fts3Index *aIndex; /* Array of indexes for this table */ + struct Fts3Index *aFree = 0; /* Free this before returning */ + + /* The results of parsing supported FTS4 key=value options: */ + int bNoDocsize = 0; /* True to omit %_docsize table */ + int bDescIdx = 0; /* True to store descending indexes */ + char *zPrefix = 0; /* Prefix parameter value (or NULL) */ + char *zCompress = 0; /* compress=? parameter (or NULL) */ + char *zUncompress = 0; /* uncompress=? parameter (or NULL) */ + assert( strlen(argv[0])==4 ); assert( (sqlite3_strnicmp(argv[0], "fts4", 4)==0 && isFts4) || (sqlite3_strnicmp(argv[0], "fts3", 4)==0 && !isFts4) @@ -108947,22 +112942,72 @@ static int fts3InitVtab( /* Check if it is an FTS4 special argument. */ else if( isFts4 && fts3IsSpecialColumn(z, &nKey, &zVal) ){ + struct Fts4Option { + const char *zOpt; + int nOpt; + char **pzVar; + } aFts4Opt[] = { + { "matchinfo", 9, 0 }, /* 0 -> MATCHINFO */ + { "prefix", 6, 0 }, /* 1 -> PREFIX */ + { "compress", 8, 0 }, /* 2 -> COMPRESS */ + { "uncompress", 10, 0 }, /* 3 -> UNCOMPRESS */ + { "order", 5, 0 } /* 4 -> ORDER */ + }; + + int iOpt; if( !zVal ){ rc = SQLITE_NOMEM; - goto fts3_init_out; - } - if( nKey==9 && 0==sqlite3_strnicmp(z, "matchinfo", 9) ){ - if( strlen(zVal)==4 && 0==sqlite3_strnicmp(zVal, "fts3", 4) ){ - bNoDocsize = 1; - }else{ - *pzErr = sqlite3_mprintf("unrecognized matchinfo: %s", zVal); + }else{ + for(iOpt=0; iOpt<SizeofArray(aFts4Opt); iOpt++){ + struct Fts4Option *pOp = &aFts4Opt[iOpt]; + if( nKey==pOp->nOpt && !sqlite3_strnicmp(z, pOp->zOpt, pOp->nOpt) ){ + break; + } + } + if( iOpt==SizeofArray(aFts4Opt) ){ + *pzErr = sqlite3_mprintf("unrecognized parameter: %s", z); rc = SQLITE_ERROR; + }else{ + switch( iOpt ){ + case 0: /* MATCHINFO */ + if( strlen(zVal)!=4 || sqlite3_strnicmp(zVal, "fts3", 4) ){ + *pzErr = sqlite3_mprintf("unrecognized matchinfo: %s", zVal); + rc = SQLITE_ERROR; + } + bNoDocsize = 1; + break; + + case 1: /* PREFIX */ + sqlite3_free(zPrefix); + zPrefix = zVal; + zVal = 0; + break; + + case 2: /* COMPRESS */ + sqlite3_free(zCompress); + zCompress = zVal; + zVal = 0; + break; + + case 3: /* UNCOMPRESS */ + sqlite3_free(zUncompress); + zUncompress = zVal; + zVal = 0; + break; + + case 4: /* ORDER */ + if( (strlen(zVal)!=3 || sqlite3_strnicmp(zVal, "asc", 3)) + && (strlen(zVal)!=4 || sqlite3_strnicmp(zVal, "desc", 3)) + ){ + *pzErr = sqlite3_mprintf("unrecognized order: %s", zVal); + rc = SQLITE_ERROR; + } + bDescIdx = (zVal[0]=='d' || zVal[0]=='D'); + break; + } } - }else{ - *pzErr = sqlite3_mprintf("unrecognized parameter: %s", z); - rc = SQLITE_ERROR; + sqlite3_free(zVal); } - sqlite3_free(zVal); } /* Otherwise, the argument is a column name. */ @@ -108986,10 +113031,17 @@ static int fts3InitVtab( } assert( pTokenizer ); + rc = fts3PrefixParameter(zPrefix, &nIndex, &aIndex, &aFree); + if( rc==SQLITE_ERROR ){ + assert( zPrefix ); + *pzErr = sqlite3_mprintf("error parsing prefix parameter: %s", zPrefix); + } + if( rc!=SQLITE_OK ) goto fts3_init_out; /* Allocate and populate the Fts3Table structure. */ - nByte = sizeof(Fts3Table) + /* Fts3Table */ + nByte = sizeof(Fts3Table) + /* Fts3Table */ nCol * sizeof(char *) + /* azColumn */ + nIndex * sizeof(struct Fts3Index) + /* aIndex */ nName + /* zName */ nDb + /* zDb */ nString; /* Space for azColumn strings */ @@ -109004,14 +113056,22 @@ static int fts3InitVtab( p->nPendingData = 0; p->azColumn = (char **)&p[1]; p->pTokenizer = pTokenizer; - p->nNodeSize = 1000; p->nMaxPendingData = FTS3_MAX_PENDING_DATA; p->bHasDocsize = (isFts4 && bNoDocsize==0); p->bHasStat = isFts4; - fts3HashInit(&p->pendingTerms, FTS3_HASH_STRING, 1); + p->bDescIdx = bDescIdx; + TESTONLY( p->inTransaction = -1 ); + TESTONLY( p->mxSavepoint = -1 ); + + p->aIndex = (struct Fts3Index *)&p->azColumn[nCol]; + memcpy(p->aIndex, aIndex, sizeof(struct Fts3Index) * nIndex); + p->nIndex = nIndex; + for(i=0; i<nIndex; i++){ + fts3HashInit(&p->aIndex[i].hPending, FTS3_HASH_STRING, 1); + } /* Fill in the zName and zDb fields of the vtab structure. */ - zCsr = (char *)&p->azColumn[nCol]; + zCsr = (char *)&p->aIndex[nIndex]; p->zName = zCsr; memcpy(zCsr, argv[2], nName); zCsr += nName; @@ -109022,7 +113082,7 @@ static int fts3InitVtab( /* Fill in the azColumn array */ for(iCol=0; iCol<nCol; iCol++){ char *z; - int n; + int n = 0; z = (char *)sqlite3Fts3NextToken(aCol[iCol], &n); memcpy(zCsr, z, n); zCsr[n] = '\0'; @@ -109032,6 +113092,15 @@ static int fts3InitVtab( assert( zCsr <= &((char *)p)[nByte] ); } + if( (zCompress==0)!=(zUncompress==0) ){ + char const *zMiss = (zCompress==0 ? "compress" : "uncompress"); + rc = SQLITE_ERROR; + *pzErr = sqlite3_mprintf("missing %s parameter in fts4 constructor", zMiss); + } + p->zReadExprlist = fts3ReadExprList(p, zUncompress, &rc); + p->zWriteExprlist = fts3WriteExprList(p, zCompress, &rc); + if( rc!=SQLITE_OK ) goto fts3_init_out; + /* If this is an xCreate call, create the underlying tables in the ** database. TODO: For xConnect(), it could verify that said tables exist. */ @@ -109040,16 +113109,18 @@ static int fts3InitVtab( } /* Figure out the page-size for the database. This is required in order to - ** estimate the cost of loading large doclists from the database (see - ** function sqlite3Fts3SegReaderCost() for details). - */ + ** estimate the cost of loading large doclists from the database. */ fts3DatabasePageSize(&rc, p); + p->nNodeSize = p->nPgsz-35; /* Declare the table schema to SQLite. */ fts3DeclareVtab(&rc, p); fts3_init_out: - + sqlite3_free(zPrefix); + sqlite3_free(aFree); + sqlite3_free(zCompress); + sqlite3_free(zUncompress); sqlite3_free((void *)aCol); if( rc!=SQLITE_OK ){ if( p ){ @@ -109058,6 +113129,7 @@ fts3_init_out: pTokenizer->pModule->xDestroy(pTokenizer); } }else{ + assert( p->pSegments==0 ); *ppVTab = &p->base; } return rc; @@ -109143,6 +113215,23 @@ static int fts3BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){ pInfo->aConstraintUsage[iCons].argvIndex = 1; pInfo->aConstraintUsage[iCons].omit = 1; } + + /* Regardless of the strategy selected, FTS can deliver rows in rowid (or + ** docid) order. Both ascending and descending are possible. + */ + if( pInfo->nOrderBy==1 ){ + struct sqlite3_index_orderby *pOrder = &pInfo->aOrderBy[0]; + if( pOrder->iColumn<0 || pOrder->iColumn==p->nColumn+1 ){ + if( pOrder->desc ){ + pInfo->idxStr = "DESC"; + }else{ + pInfo->idxStr = "ASC"; + } + pInfo->orderByConsumed = 1; + } + } + + assert( p->pSegments==0 ); return SQLITE_OK; } @@ -109178,6 +113267,7 @@ static int fts3CloseMethod(sqlite3_vtab_cursor *pCursor){ sqlite3Fts3FreeDeferredTokens(pCsr); sqlite3_free(pCsr->aDoclist); sqlite3_free(pCsr->aMatchinfo); + assert( ((Fts3Table *)pCsr->base.pVtab)->pSegments==0 ); sqlite3_free(pCsr); return SQLITE_OK; } @@ -109189,8 +113279,8 @@ static int fts3CloseMethod(sqlite3_vtab_cursor *pCursor){ */ static int fts3CursorSeek(sqlite3_context *pContext, Fts3Cursor *pCsr){ if( pCsr->isRequireSeek ){ - pCsr->isRequireSeek = 0; sqlite3_bind_int64(pCsr->pStmt, 1, pCsr->iPrevId); + pCsr->isRequireSeek = 0; if( SQLITE_ROW==sqlite3_step(pCsr->pStmt) ){ return SQLITE_OK; }else{ @@ -109200,7 +113290,7 @@ static int fts3CursorSeek(sqlite3_context *pContext, Fts3Cursor *pCsr){ ** table is missing a row that is present in the full-text index. ** The data structures are corrupt. */ - rc = SQLITE_CORRUPT; + rc = SQLITE_CORRUPT_VTAB; } pCsr->isEof = 1; if( pContext ){ @@ -109260,7 +113350,7 @@ static int fts3ScanInteriorNode( zCsr += sqlite3Fts3GetVarint(zCsr, &iChild); zCsr += sqlite3Fts3GetVarint(zCsr, &iChild); if( zCsr>zEnd ){ - return SQLITE_CORRUPT; + return SQLITE_CORRUPT_VTAB; } while( zCsr<zEnd && (piFirst || piLast) ){ @@ -109278,7 +113368,7 @@ static int fts3ScanInteriorNode( zCsr += sqlite3Fts3GetVarint32(zCsr, &nSuffix); if( nPrefix<0 || nSuffix<0 || &zCsr[nSuffix]>zEnd ){ - rc = SQLITE_CORRUPT; + rc = SQLITE_CORRUPT_VTAB; goto finish_scan; } if( nPrefix+nSuffix>nAlloc ){ @@ -109371,7 +113461,7 @@ static int fts3SelectLeaf( int nBlob; /* Size of zBlob in bytes */ if( piLeaf && piLeaf2 && (*piLeaf!=*piLeaf2) ){ - rc = sqlite3Fts3ReadBlock(p, *piLeaf, &zBlob, &nBlob); + rc = sqlite3Fts3ReadBlock(p, *piLeaf, &zBlob, &nBlob, 0); if( rc==SQLITE_OK ){ rc = fts3SelectLeaf(p, zTerm, nTerm, zBlob, nBlob, piLeaf, 0); } @@ -109381,7 +113471,7 @@ static int fts3SelectLeaf( } if( rc==SQLITE_OK ){ - rc = sqlite3Fts3ReadBlock(p, piLeaf ? *piLeaf : *piLeaf2, &zBlob, &nBlob); + rc = sqlite3Fts3ReadBlock(p, piLeaf?*piLeaf:*piLeaf2, &zBlob, &nBlob, 0); } if( rc==SQLITE_OK ){ rc = fts3SelectLeaf(p, zTerm, nTerm, zBlob, nBlob, piLeaf, piLeaf2); @@ -109757,7 +113847,19 @@ static int fts3PoslistPhraseMerge( } /* -** Merge two position-lists as required by the NEAR operator. +** Merge two position-lists as required by the NEAR operator. The argument +** position lists correspond to the left and right phrases of an expression +** like: +** +** "phrase 1" NEAR "phrase number 2" +** +** Position list *pp1 corresponds to the left-hand side of the NEAR +** expression and *pp2 to the right. As usual, the indexes in the position +** lists are the offsets of the last token in each phrase (tokens "1" and "2" +** in the example above). +** +** The output position list - written to *pp - is a copy of *pp2 with those +** entries that are not sufficiently NEAR entries in *pp1 removed. */ static int fts3PoslistNearMerge( char **pp, /* Output buffer */ @@ -109770,226 +113872,181 @@ static int fts3PoslistNearMerge( char *p1 = *pp1; char *p2 = *pp2; - if( !pp ){ - if( fts3PoslistPhraseMerge(0, nRight, 0, 0, pp1, pp2) ) return 1; - *pp1 = p1; - *pp2 = p2; - return fts3PoslistPhraseMerge(0, nLeft, 0, 0, pp2, pp1); + char *pTmp1 = aTmp; + char *pTmp2; + char *aTmp2; + int res = 1; + + fts3PoslistPhraseMerge(&pTmp1, nRight, 0, 0, pp1, pp2); + aTmp2 = pTmp2 = pTmp1; + *pp1 = p1; + *pp2 = p2; + fts3PoslistPhraseMerge(&pTmp2, nLeft, 1, 0, pp2, pp1); + if( pTmp1!=aTmp && pTmp2!=aTmp2 ){ + fts3PoslistMerge(pp, &aTmp, &aTmp2); + }else if( pTmp1!=aTmp ){ + fts3PoslistCopy(pp, &aTmp); + }else if( pTmp2!=aTmp2 ){ + fts3PoslistCopy(pp, &aTmp2); }else{ - char *pTmp1 = aTmp; - char *pTmp2; - char *aTmp2; - int res = 1; - - fts3PoslistPhraseMerge(&pTmp1, nRight, 0, 0, pp1, pp2); - aTmp2 = pTmp2 = pTmp1; - *pp1 = p1; - *pp2 = p2; - fts3PoslistPhraseMerge(&pTmp2, nLeft, 1, 0, pp2, pp1); - if( pTmp1!=aTmp && pTmp2!=aTmp2 ){ - fts3PoslistMerge(pp, &aTmp, &aTmp2); - }else if( pTmp1!=aTmp ){ - fts3PoslistCopy(pp, &aTmp); - }else if( pTmp2!=aTmp2 ){ - fts3PoslistCopy(pp, &aTmp2); + res = 0; + } + + return res; +} + +/* +** A pointer to an instance of this structure is used as the context +** argument to sqlite3Fts3SegReaderIterate() +*/ +typedef struct TermSelect TermSelect; +struct TermSelect { + int isReqPos; + char *aaOutput[16]; /* Malloc'd output buffer */ + int anOutput[16]; /* Size of output in bytes */ +}; + + +static void fts3GetDeltaVarint3( + char **pp, + char *pEnd, + int bDescIdx, + sqlite3_int64 *pVal +){ + if( *pp>=pEnd ){ + *pp = 0; + }else{ + sqlite3_int64 iVal; + *pp += sqlite3Fts3GetVarint(*pp, &iVal); + if( bDescIdx ){ + *pVal -= iVal; }else{ - res = 0; + *pVal += iVal; } + } +} - return res; +static void fts3PutDeltaVarint3( + char **pp, /* IN/OUT: Output pointer */ + int bDescIdx, /* True for descending docids */ + sqlite3_int64 *piPrev, /* IN/OUT: Previous value written to list */ + int *pbFirst, /* IN/OUT: True after first int written */ + sqlite3_int64 iVal /* Write this value to the list */ +){ + sqlite3_int64 iWrite; + if( bDescIdx==0 || *pbFirst==0 ){ + iWrite = iVal - *piPrev; + }else{ + iWrite = *piPrev - iVal; } + assert( *pbFirst || *piPrev==0 ); + assert( *pbFirst==0 || iWrite>0 ); + *pp += sqlite3Fts3PutVarint(*pp, iWrite); + *piPrev = iVal; + *pbFirst = 1; } -/* -** Values that may be used as the first parameter to fts3DoclistMerge(). -*/ -#define MERGE_NOT 2 /* D + D -> D */ -#define MERGE_AND 3 /* D + D -> D */ -#define MERGE_OR 4 /* D + D -> D */ -#define MERGE_POS_OR 5 /* P + P -> P */ -#define MERGE_PHRASE 6 /* P + P -> D */ -#define MERGE_POS_PHRASE 7 /* P + P -> P */ -#define MERGE_NEAR 8 /* P + P -> D */ -#define MERGE_POS_NEAR 9 /* P + P -> P */ +#define COMPARE_DOCID(i1, i2) ((bDescIdx?-1:1) * (i1-i2)) -/* -** Merge the two doclists passed in buffer a1 (size n1 bytes) and a2 -** (size n2 bytes). The output is written to pre-allocated buffer aBuffer, -** which is guaranteed to be large enough to hold the results. The number -** of bytes written to aBuffer is stored in *pnBuffer before returning. -** -** If successful, SQLITE_OK is returned. Otherwise, if a malloc error -** occurs while allocating a temporary buffer as part of the merge operation, -** SQLITE_NOMEM is returned. -*/ -static int fts3DoclistMerge( - int mergetype, /* One of the MERGE_XXX constants */ - int nParam1, /* Used by MERGE_NEAR and MERGE_POS_NEAR */ - int nParam2, /* Used by MERGE_NEAR and MERGE_POS_NEAR */ - char *aBuffer, /* Pre-allocated output buffer */ - int *pnBuffer, /* OUT: Bytes written to aBuffer */ - char *a1, /* Buffer containing first doclist */ - int n1, /* Size of buffer a1 */ - char *a2, /* Buffer containing second doclist */ - int n2, /* Size of buffer a2 */ - int *pnDoc /* OUT: Number of docids in output */ +static int fts3DoclistOrMerge( + int bDescIdx, /* True if arguments are desc */ + char *a1, int n1, /* First doclist */ + char *a2, int n2, /* Second doclist */ + char **paOut, int *pnOut /* OUT: Malloc'd doclist */ ){ sqlite3_int64 i1 = 0; sqlite3_int64 i2 = 0; sqlite3_int64 iPrev = 0; - - char *p = aBuffer; - char *p1 = a1; - char *p2 = a2; char *pEnd1 = &a1[n1]; char *pEnd2 = &a2[n2]; - int nDoc = 0; - - assert( mergetype==MERGE_OR || mergetype==MERGE_POS_OR - || mergetype==MERGE_AND || mergetype==MERGE_NOT - || mergetype==MERGE_PHRASE || mergetype==MERGE_POS_PHRASE - || mergetype==MERGE_NEAR || mergetype==MERGE_POS_NEAR - ); - - if( !aBuffer ){ - *pnBuffer = 0; - return SQLITE_NOMEM; - } - - /* Read the first docid from each doclist */ - fts3GetDeltaVarint2(&p1, pEnd1, &i1); - fts3GetDeltaVarint2(&p2, pEnd2, &i2); - - switch( mergetype ){ - case MERGE_OR: - case MERGE_POS_OR: - while( p1 || p2 ){ - if( p2 && p1 && i1==i2 ){ - fts3PutDeltaVarint(&p, &iPrev, i1); - if( mergetype==MERGE_POS_OR ) fts3PoslistMerge(&p, &p1, &p2); - fts3GetDeltaVarint2(&p1, pEnd1, &i1); - fts3GetDeltaVarint2(&p2, pEnd2, &i2); - }else if( !p2 || (p1 && i1<i2) ){ - fts3PutDeltaVarint(&p, &iPrev, i1); - if( mergetype==MERGE_POS_OR ) fts3PoslistCopy(&p, &p1); - fts3GetDeltaVarint2(&p1, pEnd1, &i1); - }else{ - fts3PutDeltaVarint(&p, &iPrev, i2); - if( mergetype==MERGE_POS_OR ) fts3PoslistCopy(&p, &p2); - fts3GetDeltaVarint2(&p2, pEnd2, &i2); - } - } - break; - - case MERGE_AND: - while( p1 && p2 ){ - if( i1==i2 ){ - fts3PutDeltaVarint(&p, &iPrev, i1); - fts3GetDeltaVarint2(&p1, pEnd1, &i1); - fts3GetDeltaVarint2(&p2, pEnd2, &i2); - nDoc++; - }else if( i1<i2 ){ - fts3GetDeltaVarint2(&p1, pEnd1, &i1); - }else{ - fts3GetDeltaVarint2(&p2, pEnd2, &i2); - } - } - break; - - case MERGE_NOT: - while( p1 ){ - if( p2 && i1==i2 ){ - fts3GetDeltaVarint2(&p1, pEnd1, &i1); - fts3GetDeltaVarint2(&p2, pEnd2, &i2); - }else if( !p2 || i1<i2 ){ - fts3PutDeltaVarint(&p, &iPrev, i1); - fts3GetDeltaVarint2(&p1, pEnd1, &i1); - }else{ - fts3GetDeltaVarint2(&p2, pEnd2, &i2); - } - } - break; + char *p1 = a1; + char *p2 = a2; + char *p; + char *aOut; + int bFirstOut = 0; - case MERGE_POS_PHRASE: - case MERGE_PHRASE: { - char **ppPos = (mergetype==MERGE_PHRASE ? 0 : &p); - while( p1 && p2 ){ - if( i1==i2 ){ - char *pSave = p; - sqlite3_int64 iPrevSave = iPrev; - fts3PutDeltaVarint(&p, &iPrev, i1); - if( 0==fts3PoslistPhraseMerge(ppPos, nParam1, 0, 1, &p1, &p2) ){ - p = pSave; - iPrev = iPrevSave; - }else{ - nDoc++; - } - fts3GetDeltaVarint2(&p1, pEnd1, &i1); - fts3GetDeltaVarint2(&p2, pEnd2, &i2); - }else if( i1<i2 ){ - fts3PoslistCopy(0, &p1); - fts3GetDeltaVarint2(&p1, pEnd1, &i1); - }else{ - fts3PoslistCopy(0, &p2); - fts3GetDeltaVarint2(&p2, pEnd2, &i2); - } - } - break; + *paOut = 0; + *pnOut = 0; + aOut = sqlite3_malloc(n1+n2); + if( !aOut ) return SQLITE_NOMEM; + + p = aOut; + fts3GetDeltaVarint3(&p1, pEnd1, 0, &i1); + fts3GetDeltaVarint3(&p2, pEnd2, 0, &i2); + while( p1 || p2 ){ + sqlite3_int64 iDiff = COMPARE_DOCID(i1, i2); + + if( p2 && p1 && iDiff==0 ){ + fts3PutDeltaVarint3(&p, bDescIdx, &iPrev, &bFirstOut, i1); + fts3PoslistMerge(&p, &p1, &p2); + fts3GetDeltaVarint3(&p1, pEnd1, bDescIdx, &i1); + fts3GetDeltaVarint3(&p2, pEnd2, bDescIdx, &i2); + }else if( !p2 || (p1 && iDiff<0) ){ + fts3PutDeltaVarint3(&p, bDescIdx, &iPrev, &bFirstOut, i1); + fts3PoslistCopy(&p, &p1); + fts3GetDeltaVarint3(&p1, pEnd1, bDescIdx, &i1); + }else{ + fts3PutDeltaVarint3(&p, bDescIdx, &iPrev, &bFirstOut, i2); + fts3PoslistCopy(&p, &p2); + fts3GetDeltaVarint3(&p2, pEnd2, bDescIdx, &i2); } + } - default: assert( mergetype==MERGE_POS_NEAR || mergetype==MERGE_NEAR ); { - char *aTmp = 0; - char **ppPos = 0; - - if( mergetype==MERGE_POS_NEAR ){ - ppPos = &p; - aTmp = sqlite3_malloc(2*(n1+n2+1)); - if( !aTmp ){ - return SQLITE_NOMEM; - } - } - - while( p1 && p2 ){ - if( i1==i2 ){ - char *pSave = p; - sqlite3_int64 iPrevSave = iPrev; - fts3PutDeltaVarint(&p, &iPrev, i1); + *paOut = aOut; + *pnOut = (p-aOut); + return SQLITE_OK; +} - if( !fts3PoslistNearMerge(ppPos, aTmp, nParam1, nParam2, &p1, &p2) ){ - iPrev = iPrevSave; - p = pSave; - } +static void fts3DoclistPhraseMerge( + int bDescIdx, /* True if arguments are desc */ + int nDist, /* Distance from left to right (1=adjacent) */ + char *aLeft, int nLeft, /* Left doclist */ + char *aRight, int *pnRight /* IN/OUT: Right/output doclist */ +){ + sqlite3_int64 i1 = 0; + sqlite3_int64 i2 = 0; + sqlite3_int64 iPrev = 0; + char *pEnd1 = &aLeft[nLeft]; + char *pEnd2 = &aRight[*pnRight]; + char *p1 = aLeft; + char *p2 = aRight; + char *p; + int bFirstOut = 0; + char *aOut = aRight; + + assert( nDist>0 ); + + p = aOut; + fts3GetDeltaVarint3(&p1, pEnd1, 0, &i1); + fts3GetDeltaVarint3(&p2, pEnd2, 0, &i2); + + while( p1 && p2 ){ + sqlite3_int64 iDiff = COMPARE_DOCID(i1, i2); + if( iDiff==0 ){ + char *pSave = p; + sqlite3_int64 iPrevSave = iPrev; + int bFirstOutSave = bFirstOut; - fts3GetDeltaVarint2(&p1, pEnd1, &i1); - fts3GetDeltaVarint2(&p2, pEnd2, &i2); - }else if( i1<i2 ){ - fts3PoslistCopy(0, &p1); - fts3GetDeltaVarint2(&p1, pEnd1, &i1); - }else{ - fts3PoslistCopy(0, &p2); - fts3GetDeltaVarint2(&p2, pEnd2, &i2); - } - } - sqlite3_free(aTmp); - break; + fts3PutDeltaVarint3(&p, bDescIdx, &iPrev, &bFirstOut, i1); + if( 0==fts3PoslistPhraseMerge(&p, nDist, 0, 1, &p1, &p2) ){ + p = pSave; + iPrev = iPrevSave; + bFirstOut = bFirstOutSave; + } + fts3GetDeltaVarint3(&p1, pEnd1, bDescIdx, &i1); + fts3GetDeltaVarint3(&p2, pEnd2, bDescIdx, &i2); + }else if( iDiff<0 ){ + fts3PoslistCopy(0, &p1); + fts3GetDeltaVarint3(&p1, pEnd1, bDescIdx, &i1); + }else{ + fts3PoslistCopy(0, &p2); + fts3GetDeltaVarint3(&p2, pEnd2, bDescIdx, &i2); } } - if( pnDoc ) *pnDoc = nDoc; - *pnBuffer = (int)(p-aBuffer); - return SQLITE_OK; + *pnRight = p - aOut; } -/* -** A pointer to an instance of this structure is used as the context -** argument to sqlite3Fts3SegReaderIterate() -*/ -typedef struct TermSelect TermSelect; -struct TermSelect { - int isReqPos; - char *aaOutput[16]; /* Malloc'd output buffer */ - int anOutput[16]; /* Size of output in bytes */ -}; /* ** Merge all doclists in the TermSelect.aaOutput[] array into a single @@ -110000,8 +114057,7 @@ struct TermSelect { ** the responsibility of the caller to free any doclists left in the ** TermSelect.aaOutput[] array. */ -static int fts3TermSelectMerge(TermSelect *pTS){ - int mergetype = (pTS->isReqPos ? MERGE_POS_OR : MERGE_OR); +static int fts3TermSelectMerge(Fts3Table *p, TermSelect *pTS){ char *aOut = 0; int nOut = 0; int i; @@ -110016,15 +114072,17 @@ static int fts3TermSelectMerge(TermSelect *pTS){ nOut = pTS->anOutput[i]; pTS->aaOutput[i] = 0; }else{ - int nNew = nOut + pTS->anOutput[i]; - char *aNew = sqlite3_malloc(nNew); - if( !aNew ){ + int nNew; + char *aNew; + + int rc = fts3DoclistOrMerge(p->bDescIdx, + pTS->aaOutput[i], pTS->anOutput[i], aOut, nOut, &aNew, &nNew + ); + if( rc!=SQLITE_OK ){ sqlite3_free(aOut); - return SQLITE_NOMEM; + return rc; } - fts3DoclistMerge(mergetype, 0, 0, - aNew, &nNew, pTS->aaOutput[i], pTS->anOutput[i], aOut, nOut, 0 - ); + sqlite3_free(pTS->aaOutput[i]); sqlite3_free(aOut); pTS->aaOutput[i] = 0; @@ -110060,9 +114118,7 @@ static int fts3TermSelectCb( if( pTS->aaOutput[0]==0 ){ /* If this is the first term selected, copy the doclist to the output - ** buffer using memcpy(). TODO: Add a way to transfer control of the - ** aDoclist buffer from the caller so as to avoid the memcpy(). - */ + ** buffer using memcpy(). */ pTS->aaOutput[0] = sqlite3_malloc(nDoclist); pTS->anOutput[0] = nDoclist; if( pTS->aaOutput[0] ){ @@ -110071,203 +114127,232 @@ static int fts3TermSelectCb( return SQLITE_NOMEM; } }else{ - int mergetype = (pTS->isReqPos ? MERGE_POS_OR : MERGE_OR); char *aMerge = aDoclist; int nMerge = nDoclist; int iOut; for(iOut=0; iOut<SizeofArray(pTS->aaOutput); iOut++){ - char *aNew; - int nNew; if( pTS->aaOutput[iOut]==0 ){ assert( iOut>0 ); pTS->aaOutput[iOut] = aMerge; pTS->anOutput[iOut] = nMerge; break; - } + }else{ + char *aNew; + int nNew; - nNew = nMerge + pTS->anOutput[iOut]; - aNew = sqlite3_malloc(nNew); - if( !aNew ){ - if( aMerge!=aDoclist ){ - sqlite3_free(aMerge); + int rc = fts3DoclistOrMerge(p->bDescIdx, aMerge, nMerge, + pTS->aaOutput[iOut], pTS->anOutput[iOut], &aNew, &nNew + ); + if( rc!=SQLITE_OK ){ + if( aMerge!=aDoclist ) sqlite3_free(aMerge); + return rc; } - return SQLITE_NOMEM; - } - fts3DoclistMerge(mergetype, 0, 0, aNew, &nNew, - pTS->aaOutput[iOut], pTS->anOutput[iOut], aMerge, nMerge, 0 - ); - - if( iOut>0 ) sqlite3_free(aMerge); - sqlite3_free(pTS->aaOutput[iOut]); - pTS->aaOutput[iOut] = 0; - aMerge = aNew; - nMerge = nNew; - if( (iOut+1)==SizeofArray(pTS->aaOutput) ){ - pTS->aaOutput[iOut] = aMerge; - pTS->anOutput[iOut] = nMerge; + if( aMerge!=aDoclist ) sqlite3_free(aMerge); + sqlite3_free(pTS->aaOutput[iOut]); + pTS->aaOutput[iOut] = 0; + + aMerge = aNew; + nMerge = nNew; + if( (iOut+1)==SizeofArray(pTS->aaOutput) ){ + pTS->aaOutput[iOut] = aMerge; + pTS->anOutput[iOut] = nMerge; + } } } } return SQLITE_OK; } -static int fts3DeferredTermSelect( - Fts3DeferredToken *pToken, /* Phrase token */ - int isTermPos, /* True to include positions */ - int *pnOut, /* OUT: Size of list */ - char **ppOut /* OUT: Body of list */ -){ - char *aSource; - int nSource; - - aSource = sqlite3Fts3DeferredDoclist(pToken, &nSource); - if( !aSource ){ - *pnOut = 0; - *ppOut = 0; - }else if( isTermPos ){ - *ppOut = sqlite3_malloc(nSource); - if( !*ppOut ) return SQLITE_NOMEM; - memcpy(*ppOut, aSource, nSource); - *pnOut = nSource; - }else{ - sqlite3_int64 docid; - *pnOut = sqlite3Fts3GetVarint(aSource, &docid); - *ppOut = sqlite3_malloc(*pnOut); - if( !*ppOut ) return SQLITE_NOMEM; - sqlite3Fts3PutVarint(*ppOut, docid); +/* +** Append SegReader object pNew to the end of the pCsr->apSegment[] array. +*/ +static int fts3SegReaderCursorAppend( + Fts3MultiSegReader *pCsr, + Fts3SegReader *pNew +){ + if( (pCsr->nSegment%16)==0 ){ + Fts3SegReader **apNew; + int nByte = (pCsr->nSegment + 16)*sizeof(Fts3SegReader*); + apNew = (Fts3SegReader **)sqlite3_realloc(pCsr->apSegment, nByte); + if( !apNew ){ + sqlite3Fts3SegReaderFree(pNew); + return SQLITE_NOMEM; + } + pCsr->apSegment = apNew; } - + pCsr->apSegment[pCsr->nSegment++] = pNew; return SQLITE_OK; } -/* -** An Fts3SegReaderArray is used to store an array of Fts3SegReader objects. -** Elements are added to the array using fts3SegReaderArrayAdd(). -*/ -struct Fts3SegReaderArray { - int nSegment; /* Number of valid entries in apSegment[] */ - int nAlloc; /* Allocated size of apSegment[] */ - int nCost; /* The cost of executing SegReaderIterate() */ - Fts3SegReader *apSegment[1]; /* Array of seg-reader objects */ -}; +static int fts3SegReaderCursor( + Fts3Table *p, /* FTS3 table handle */ + int iIndex, /* Index to search (from 0 to p->nIndex-1) */ + int iLevel, /* Level of segments to scan */ + const char *zTerm, /* Term to query for */ + int nTerm, /* Size of zTerm in bytes */ + int isPrefix, /* True for a prefix search */ + int isScan, /* True to scan from zTerm to EOF */ + Fts3MultiSegReader *pCsr /* Cursor object to populate */ +){ + int rc = SQLITE_OK; + int rc2; + sqlite3_stmt *pStmt = 0; + /* If iLevel is less than 0 and this is not a scan, include a seg-reader + ** for the pending-terms. If this is a scan, then this call must be being + ** made by an fts4aux module, not an FTS table. In this case calling + ** Fts3SegReaderPending might segfault, as the data structures used by + ** fts4aux are not completely populated. So it's easiest to filter these + ** calls out here. */ + if( iLevel<0 && p->aIndex ){ + Fts3SegReader *pSeg = 0; + rc = sqlite3Fts3SegReaderPending(p, iIndex, zTerm, nTerm, isPrefix, &pSeg); + if( rc==SQLITE_OK && pSeg ){ + rc = fts3SegReaderCursorAppend(pCsr, pSeg); + } + } -/* -** Free an Fts3SegReaderArray object. Also free all seg-readers in the -** array (using sqlite3Fts3SegReaderFree()). -*/ -static void fts3SegReaderArrayFree(Fts3SegReaderArray *pArray){ - if( pArray ){ - int i; - for(i=0; i<pArray->nSegment; i++){ - sqlite3Fts3SegReaderFree(pArray->apSegment[i]); + if( iLevel!=FTS3_SEGCURSOR_PENDING ){ + if( rc==SQLITE_OK ){ + rc = sqlite3Fts3AllSegdirs(p, iIndex, iLevel, &pStmt); + } + + while( rc==SQLITE_OK && SQLITE_ROW==(rc = sqlite3_step(pStmt)) ){ + Fts3SegReader *pSeg = 0; + + /* Read the values returned by the SELECT into local variables. */ + sqlite3_int64 iStartBlock = sqlite3_column_int64(pStmt, 1); + sqlite3_int64 iLeavesEndBlock = sqlite3_column_int64(pStmt, 2); + sqlite3_int64 iEndBlock = sqlite3_column_int64(pStmt, 3); + int nRoot = sqlite3_column_bytes(pStmt, 4); + char const *zRoot = sqlite3_column_blob(pStmt, 4); + + /* If zTerm is not NULL, and this segment is not stored entirely on its + ** root node, the range of leaves scanned can be reduced. Do this. */ + if( iStartBlock && zTerm ){ + sqlite3_int64 *pi = (isPrefix ? &iLeavesEndBlock : 0); + rc = fts3SelectLeaf(p, zTerm, nTerm, zRoot, nRoot, &iStartBlock, pi); + if( rc!=SQLITE_OK ) goto finished; + if( isPrefix==0 && isScan==0 ) iLeavesEndBlock = iStartBlock; + } + + rc = sqlite3Fts3SegReaderNew(pCsr->nSegment+1, + iStartBlock, iLeavesEndBlock, iEndBlock, zRoot, nRoot, &pSeg + ); + if( rc!=SQLITE_OK ) goto finished; + rc = fts3SegReaderCursorAppend(pCsr, pSeg); } - sqlite3_free(pArray); } + + finished: + rc2 = sqlite3_reset(pStmt); + if( rc==SQLITE_DONE ) rc = rc2; + + return rc; } -static int fts3SegReaderArrayAdd( - Fts3SegReaderArray **ppArray, - Fts3SegReader *pNew +/* +** Set up a cursor object for iterating through a full-text index or a +** single level therein. +*/ +SQLITE_PRIVATE int sqlite3Fts3SegReaderCursor( + Fts3Table *p, /* FTS3 table handle */ + int iIndex, /* Index to search (from 0 to p->nIndex-1) */ + int iLevel, /* Level of segments to scan */ + const char *zTerm, /* Term to query for */ + int nTerm, /* Size of zTerm in bytes */ + int isPrefix, /* True for a prefix search */ + int isScan, /* True to scan from zTerm to EOF */ + Fts3MultiSegReader *pCsr /* Cursor object to populate */ ){ - Fts3SegReaderArray *pArray = *ppArray; + assert( iIndex>=0 && iIndex<p->nIndex ); + assert( iLevel==FTS3_SEGCURSOR_ALL + || iLevel==FTS3_SEGCURSOR_PENDING + || iLevel>=0 + ); + assert( iLevel<FTS3_SEGDIR_MAXLEVEL ); + assert( FTS3_SEGCURSOR_ALL<0 && FTS3_SEGCURSOR_PENDING<0 ); + assert( isPrefix==0 || isScan==0 ); - if( !pArray || pArray->nAlloc==pArray->nSegment ){ - int nNew = (pArray ? pArray->nAlloc+16 : 16); - pArray = (Fts3SegReaderArray *)sqlite3_realloc(pArray, - sizeof(Fts3SegReaderArray) + (nNew-1) * sizeof(Fts3SegReader*) - ); - if( !pArray ){ - sqlite3Fts3SegReaderFree(pNew); - return SQLITE_NOMEM; - } - if( nNew==16 ){ - pArray->nSegment = 0; - pArray->nCost = 0; - } - pArray->nAlloc = nNew; - *ppArray = pArray; - } + /* "isScan" is only set to true by the ft4aux module, an ordinary + ** full-text tables. */ + assert( isScan==0 || p->aIndex==0 ); - pArray->apSegment[pArray->nSegment++] = pNew; - return SQLITE_OK; + memset(pCsr, 0, sizeof(Fts3MultiSegReader)); + + return fts3SegReaderCursor( + p, iIndex, iLevel, zTerm, nTerm, isPrefix, isScan, pCsr + ); } -static int fts3TermSegReaderArray( +static int fts3SegReaderCursorAddZero( + Fts3Table *p, + const char *zTerm, + int nTerm, + Fts3MultiSegReader *pCsr +){ + return fts3SegReaderCursor(p, 0, FTS3_SEGCURSOR_ALL, zTerm, nTerm, 0, 0,pCsr); +} + + +SQLITE_PRIVATE int sqlite3Fts3TermSegReaderCursor( Fts3Cursor *pCsr, /* Virtual table cursor handle */ const char *zTerm, /* Term to query for */ int nTerm, /* Size of zTerm in bytes */ int isPrefix, /* True for a prefix search */ - Fts3SegReaderArray **ppArray /* OUT: Allocated seg-reader array */ + Fts3MultiSegReader **ppSegcsr /* OUT: Allocated seg-reader cursor */ ){ - Fts3Table *p = (Fts3Table *)pCsr->base.pVtab; - int rc; /* Return code */ - Fts3SegReaderArray *pArray = 0; /* Array object to build */ - Fts3SegReader *pReader = 0; /* Seg-reader to add to pArray */ - sqlite3_stmt *pStmt = 0; /* SQL statement to scan %_segdir table */ - int iAge = 0; /* Used to assign ages to segments */ + Fts3MultiSegReader *pSegcsr; /* Object to allocate and return */ + int rc = SQLITE_NOMEM; /* Return code */ - /* Allocate a seg-reader to scan the pending terms, if any. */ - rc = sqlite3Fts3SegReaderPending(p, zTerm, nTerm, isPrefix, &pReader); - if( rc==SQLITE_OK && pReader ) { - rc = fts3SegReaderArrayAdd(&pArray, pReader); - } + pSegcsr = sqlite3_malloc(sizeof(Fts3MultiSegReader)); + if( pSegcsr ){ + int i; + int bFound = 0; /* True once an index has been found */ + Fts3Table *p = (Fts3Table *)pCsr->base.pVtab; - /* Loop through the entire %_segdir table. For each segment, create a - ** Fts3SegReader to iterate through the subset of the segment leaves - ** that may contain a term that matches zTerm/nTerm. For non-prefix - ** searches, this is always a single leaf. For prefix searches, this - ** may be a contiguous block of leaves. - */ - if( rc==SQLITE_OK ){ - rc = sqlite3Fts3AllSegdirs(p, &pStmt); - } - while( rc==SQLITE_OK && SQLITE_ROW==(rc = sqlite3_step(pStmt)) ){ - Fts3SegReader *pNew = 0; - int nRoot = sqlite3_column_bytes(pStmt, 4); - char const *zRoot = sqlite3_column_blob(pStmt, 4); - if( sqlite3_column_int64(pStmt, 1)==0 ){ - /* The entire segment is stored on the root node (which must be a - ** leaf). Do not bother inspecting any data in this case, just - ** create a Fts3SegReader to scan the single leaf. - */ - rc = sqlite3Fts3SegReaderNew(iAge, 0, 0, 0, zRoot, nRoot, &pNew); - }else{ - sqlite3_int64 i1; /* First leaf that may contain zTerm */ - sqlite3_int64 i2; /* Final leaf that may contain zTerm */ - rc = fts3SelectLeaf(p, zTerm, nTerm, zRoot, nRoot, &i1, (isPrefix?&i2:0)); - if( isPrefix==0 ) i2 = i1; - if( rc==SQLITE_OK ){ - rc = sqlite3Fts3SegReaderNew(iAge, i1, i2, 0, 0, 0, &pNew); + if( isPrefix ){ + for(i=1; bFound==0 && i<p->nIndex; i++){ + if( p->aIndex[i].nPrefix==nTerm ){ + bFound = 1; + rc = sqlite3Fts3SegReaderCursor( + p, i, FTS3_SEGCURSOR_ALL, zTerm, nTerm, 0, 0, pSegcsr); + pSegcsr->bLookup = 1; + } } - } - assert( (pNew==0)==(rc!=SQLITE_OK) ); - /* If a new Fts3SegReader was allocated, add it to the array. */ - if( rc==SQLITE_OK ){ - rc = fts3SegReaderArrayAdd(&pArray, pNew); + for(i=1; bFound==0 && i<p->nIndex; i++){ + if( p->aIndex[i].nPrefix==nTerm+1 ){ + bFound = 1; + rc = sqlite3Fts3SegReaderCursor( + p, i, FTS3_SEGCURSOR_ALL, zTerm, nTerm, 1, 0, pSegcsr + ); + if( rc==SQLITE_OK ){ + rc = fts3SegReaderCursorAddZero(p, zTerm, nTerm, pSegcsr); + } + } + } } - if( rc==SQLITE_OK ){ - rc = sqlite3Fts3SegReaderCost(pCsr, pNew, &pArray->nCost); + + if( bFound==0 ){ + rc = sqlite3Fts3SegReaderCursor( + p, 0, FTS3_SEGCURSOR_ALL, zTerm, nTerm, isPrefix, 0, pSegcsr + ); + pSegcsr->bLookup = !isPrefix; } - iAge++; } - if( rc==SQLITE_DONE ){ - rc = sqlite3_reset(pStmt); - }else{ - sqlite3_reset(pStmt); - } - if( rc!=SQLITE_OK ){ - fts3SegReaderArrayFree(pArray); - pArray = 0; - } - *ppArray = pArray; + *ppSegcsr = pSegcsr; return rc; } +static void fts3SegReaderCursorFree(Fts3MultiSegReader *pSegcsr){ + sqlite3Fts3SegReaderFinish(pSegcsr); + sqlite3_free(pSegcsr); +} + /* ** This function retreives the doclist for the specified term (or term ** prefix) from the database. @@ -110288,11 +114373,11 @@ static int fts3TermSelect( char **ppOut /* OUT: Malloced result buffer */ ){ int rc; /* Return code */ - Fts3SegReaderArray *pArray; /* Seg-reader array for this term */ - TermSelect tsc; /* Context object for fts3TermSelectCb() */ - Fts3SegFilter filter; /* Segment term filter configuration */ + Fts3MultiSegReader *pSegcsr; /* Seg-reader cursor for this term */ + TermSelect tsc; /* Context object for fts3TermSelectCb() */ + Fts3SegFilter filter; /* Segment term filter configuration */ - pArray = pTok->pArray; + pSegcsr = pTok->pSegcsr; memset(&tsc, 0, sizeof(TermSelect)); tsc.isReqPos = isReqPos; @@ -110304,14 +114389,19 @@ static int fts3TermSelect( filter.zTerm = pTok->z; filter.nTerm = pTok->n; - rc = sqlite3Fts3SegReaderIterate(p, pArray->apSegment, pArray->nSegment, - &filter, fts3TermSelectCb, (void *)&tsc - ); - if( rc==SQLITE_OK ){ - rc = fts3TermSelectMerge(&tsc); + rc = sqlite3Fts3SegReaderStart(p, pSegcsr, &filter); + while( SQLITE_OK==rc + && SQLITE_ROW==(rc = sqlite3Fts3SegReaderStep(p, pSegcsr)) + ){ + rc = fts3TermSelectCb(p, (void *)&tsc, + pSegcsr->zTerm, pSegcsr->nTerm, pSegcsr->aDoclist, pSegcsr->nDoclist + ); } if( rc==SQLITE_OK ){ + rc = fts3TermSelectMerge(p, &tsc); + } + if( rc==SQLITE_OK ){ *ppOut = tsc.aaOutput[0]; *pnOut = tsc.anOutput[0]; }else{ @@ -110321,8 +114411,8 @@ static int fts3TermSelect( } } - fts3SegReaderArrayFree(pArray); - pTok->pArray = 0; + fts3SegReaderCursorFree(pSegcsr); + pTok->pSegcsr = 0; return rc; } @@ -110360,662 +114450,6 @@ static int fts3DoclistCountDocids(int isPoslist, char *aList, int nList){ } /* -** Call sqlite3Fts3DeferToken() for each token in the expression pExpr. -*/ -static int fts3DeferExpression(Fts3Cursor *pCsr, Fts3Expr *pExpr){ - int rc = SQLITE_OK; - if( pExpr ){ - rc = fts3DeferExpression(pCsr, pExpr->pLeft); - if( rc==SQLITE_OK ){ - rc = fts3DeferExpression(pCsr, pExpr->pRight); - } - if( pExpr->eType==FTSQUERY_PHRASE ){ - int iCol = pExpr->pPhrase->iColumn; - int i; - for(i=0; rc==SQLITE_OK && i<pExpr->pPhrase->nToken; i++){ - Fts3PhraseToken *pToken = &pExpr->pPhrase->aToken[i]; - if( pToken->pDeferred==0 ){ - rc = sqlite3Fts3DeferToken(pCsr, pToken, iCol); - } - } - } - } - return rc; -} - -/* -** This function removes the position information from a doclist. When -** called, buffer aList (size *pnList bytes) contains a doclist that includes -** position information. This function removes the position information so -** that aList contains only docids, and adjusts *pnList to reflect the new -** (possibly reduced) size of the doclist. -*/ -static void fts3DoclistStripPositions( - char *aList, /* IN/OUT: Buffer containing doclist */ - int *pnList /* IN/OUT: Size of doclist in bytes */ -){ - if( aList ){ - char *aEnd = &aList[*pnList]; /* Pointer to one byte after EOF */ - char *p = aList; /* Input cursor */ - char *pOut = aList; /* Output cursor */ - - while( p<aEnd ){ - sqlite3_int64 delta; - p += sqlite3Fts3GetVarint(p, &delta); - fts3PoslistCopy(0, &p); - pOut += sqlite3Fts3PutVarint(pOut, delta); - } - - *pnList = (int)(pOut - aList); - } -} - -/* -** Return a DocList corresponding to the phrase *pPhrase. -** -** If this function returns SQLITE_OK, but *pnOut is set to a negative value, -** then no tokens in the phrase were looked up in the full-text index. This -** is only possible when this function is called from within xFilter(). The -** caller should assume that all documents match the phrase. The actual -** filtering will take place in xNext(). -*/ -static int fts3PhraseSelect( - Fts3Cursor *pCsr, /* Virtual table cursor handle */ - Fts3Phrase *pPhrase, /* Phrase to return a doclist for */ - int isReqPos, /* True if output should contain positions */ - char **paOut, /* OUT: Pointer to malloc'd result buffer */ - int *pnOut /* OUT: Size of buffer at *paOut */ -){ - char *pOut = 0; - int nOut = 0; - int rc = SQLITE_OK; - int ii; - int iCol = pPhrase->iColumn; - int isTermPos = (pPhrase->nToken>1 || isReqPos); - Fts3Table *p = (Fts3Table *)pCsr->base.pVtab; - int isFirst = 1; - - int iPrevTok = 0; - int nDoc = 0; - - /* If this is an xFilter() evaluation, create a segment-reader for each - ** phrase token. Or, if this is an xNext() or snippet/offsets/matchinfo - ** evaluation, only create segment-readers if there are no Fts3DeferredToken - ** objects attached to the phrase-tokens. - */ - for(ii=0; ii<pPhrase->nToken; ii++){ - Fts3PhraseToken *pTok = &pPhrase->aToken[ii]; - if( pTok->pArray==0 ){ - if( (pCsr->eEvalmode==FTS3_EVAL_FILTER) - || (pCsr->eEvalmode==FTS3_EVAL_NEXT && pCsr->pDeferred==0) - || (pCsr->eEvalmode==FTS3_EVAL_MATCHINFO && pTok->bFulltext) - ){ - rc = fts3TermSegReaderArray( - pCsr, pTok->z, pTok->n, pTok->isPrefix, &pTok->pArray - ); - if( rc!=SQLITE_OK ) return rc; - } - } - } - - for(ii=0; ii<pPhrase->nToken; ii++){ - Fts3PhraseToken *pTok; /* Token to find doclist for */ - int iTok = 0; /* The token being queried this iteration */ - char *pList = 0; /* Pointer to token doclist */ - int nList = 0; /* Size of buffer at pList */ - - /* Select a token to process. If this is an xFilter() call, then tokens - ** are processed in order from least to most costly. Otherwise, tokens - ** are processed in the order in which they occur in the phrase. - */ - if( pCsr->eEvalmode==FTS3_EVAL_MATCHINFO ){ - assert( isReqPos ); - iTok = ii; - pTok = &pPhrase->aToken[iTok]; - if( pTok->bFulltext==0 ) continue; - }else if( pCsr->eEvalmode==FTS3_EVAL_NEXT || isReqPos ){ - iTok = ii; - pTok = &pPhrase->aToken[iTok]; - }else{ - int nMinCost = 0x7FFFFFFF; - int jj; - - /* Find the remaining token with the lowest cost. */ - for(jj=0; jj<pPhrase->nToken; jj++){ - Fts3SegReaderArray *pArray = pPhrase->aToken[jj].pArray; - if( pArray && pArray->nCost<nMinCost ){ - iTok = jj; - nMinCost = pArray->nCost; - } - } - pTok = &pPhrase->aToken[iTok]; - - /* This branch is taken if it is determined that loading the doclist - ** for the next token would require more IO than loading all documents - ** currently identified by doclist pOut/nOut. No further doclists will - ** be loaded from the full-text index for this phrase. - */ - if( nMinCost>nDoc && ii>0 ){ - rc = fts3DeferExpression(pCsr, pCsr->pExpr); - break; - } - } - - if( pCsr->eEvalmode==FTS3_EVAL_NEXT && pTok->pDeferred ){ - rc = fts3DeferredTermSelect(pTok->pDeferred, isTermPos, &nList, &pList); - }else{ - if( pTok->pArray ){ - rc = fts3TermSelect(p, pTok, iCol, isTermPos, &nList, &pList); - } - pTok->bFulltext = 1; - } - assert( rc!=SQLITE_OK || pCsr->eEvalmode || pTok->pArray==0 ); - if( rc!=SQLITE_OK ) break; - - if( isFirst ){ - pOut = pList; - nOut = nList; - if( pCsr->eEvalmode==FTS3_EVAL_FILTER && pPhrase->nToken>1 ){ - nDoc = fts3DoclistCountDocids(1, pOut, nOut); - } - isFirst = 0; - iPrevTok = iTok; - }else{ - /* Merge the new term list and the current output. */ - char *aLeft, *aRight; - int nLeft, nRight; - int nDist; - int mt; - - /* If this is the final token of the phrase, and positions were not - ** requested by the caller, use MERGE_PHRASE instead of POS_PHRASE. - ** This drops the position information from the output list. - */ - mt = MERGE_POS_PHRASE; - if( ii==pPhrase->nToken-1 && !isReqPos ) mt = MERGE_PHRASE; - - assert( iPrevTok!=iTok ); - if( iPrevTok<iTok ){ - aLeft = pOut; - nLeft = nOut; - aRight = pList; - nRight = nList; - nDist = iTok-iPrevTok; - iPrevTok = iTok; - }else{ - aRight = pOut; - nRight = nOut; - aLeft = pList; - nLeft = nList; - nDist = iPrevTok-iTok; - } - pOut = aRight; - fts3DoclistMerge( - mt, nDist, 0, pOut, &nOut, aLeft, nLeft, aRight, nRight, &nDoc - ); - sqlite3_free(aLeft); - } - assert( nOut==0 || pOut!=0 ); - } - - if( rc==SQLITE_OK ){ - if( ii!=pPhrase->nToken ){ - assert( pCsr->eEvalmode==FTS3_EVAL_FILTER && isReqPos==0 ); - fts3DoclistStripPositions(pOut, &nOut); - } - *paOut = pOut; - *pnOut = nOut; - }else{ - sqlite3_free(pOut); - } - return rc; -} - -/* -** This function merges two doclists according to the requirements of a -** NEAR operator. -** -** Both input doclists must include position information. The output doclist -** includes position information if the first argument to this function -** is MERGE_POS_NEAR, or does not if it is MERGE_NEAR. -*/ -static int fts3NearMerge( - int mergetype, /* MERGE_POS_NEAR or MERGE_NEAR */ - int nNear, /* Parameter to NEAR operator */ - int nTokenLeft, /* Number of tokens in LHS phrase arg */ - char *aLeft, /* Doclist for LHS (incl. positions) */ - int nLeft, /* Size of LHS doclist in bytes */ - int nTokenRight, /* As nTokenLeft */ - char *aRight, /* As aLeft */ - int nRight, /* As nRight */ - char **paOut, /* OUT: Results of merge (malloced) */ - int *pnOut /* OUT: Sized of output buffer */ -){ - char *aOut; /* Buffer to write output doclist to */ - int rc; /* Return code */ - - assert( mergetype==MERGE_POS_NEAR || MERGE_NEAR ); - - aOut = sqlite3_malloc(nLeft+nRight+1); - if( aOut==0 ){ - rc = SQLITE_NOMEM; - }else{ - rc = fts3DoclistMerge(mergetype, nNear+nTokenRight, nNear+nTokenLeft, - aOut, pnOut, aLeft, nLeft, aRight, nRight, 0 - ); - if( rc!=SQLITE_OK ){ - sqlite3_free(aOut); - aOut = 0; - } - } - - *paOut = aOut; - return rc; -} - -/* -** This function is used as part of the processing for the snippet() and -** offsets() functions. -** -** Both pLeft and pRight are expression nodes of type FTSQUERY_PHRASE. Both -** have their respective doclists (including position information) loaded -** in Fts3Expr.aDoclist/nDoclist. This function removes all entries from -** each doclist that are not within nNear tokens of a corresponding entry -** in the other doclist. -*/ -SQLITE_PRIVATE int sqlite3Fts3ExprNearTrim(Fts3Expr *pLeft, Fts3Expr *pRight, int nNear){ - int rc; /* Return code */ - - assert( pLeft->eType==FTSQUERY_PHRASE ); - assert( pRight->eType==FTSQUERY_PHRASE ); - assert( pLeft->isLoaded && pRight->isLoaded ); - - if( pLeft->aDoclist==0 || pRight->aDoclist==0 ){ - sqlite3_free(pLeft->aDoclist); - sqlite3_free(pRight->aDoclist); - pRight->aDoclist = 0; - pLeft->aDoclist = 0; - rc = SQLITE_OK; - }else{ - char *aOut; /* Buffer in which to assemble new doclist */ - int nOut; /* Size of buffer aOut in bytes */ - - rc = fts3NearMerge(MERGE_POS_NEAR, nNear, - pLeft->pPhrase->nToken, pLeft->aDoclist, pLeft->nDoclist, - pRight->pPhrase->nToken, pRight->aDoclist, pRight->nDoclist, - &aOut, &nOut - ); - if( rc!=SQLITE_OK ) return rc; - sqlite3_free(pRight->aDoclist); - pRight->aDoclist = aOut; - pRight->nDoclist = nOut; - - rc = fts3NearMerge(MERGE_POS_NEAR, nNear, - pRight->pPhrase->nToken, pRight->aDoclist, pRight->nDoclist, - pLeft->pPhrase->nToken, pLeft->aDoclist, pLeft->nDoclist, - &aOut, &nOut - ); - sqlite3_free(pLeft->aDoclist); - pLeft->aDoclist = aOut; - pLeft->nDoclist = nOut; - } - return rc; -} - - -/* -** Allocate an Fts3SegReaderArray for each token in the expression pExpr. -** The allocated objects are stored in the Fts3PhraseToken.pArray member -** variables of each token structure. -*/ -static int fts3ExprAllocateSegReaders( - Fts3Cursor *pCsr, /* FTS3 table */ - Fts3Expr *pExpr, /* Expression to create seg-readers for */ - int *pnExpr /* OUT: Number of AND'd expressions */ -){ - int rc = SQLITE_OK; /* Return code */ - - assert( pCsr->eEvalmode==FTS3_EVAL_FILTER ); - if( pnExpr && pExpr->eType!=FTSQUERY_AND ){ - (*pnExpr)++; - pnExpr = 0; - } - - if( pExpr->eType==FTSQUERY_PHRASE ){ - Fts3Phrase *pPhrase = pExpr->pPhrase; - int ii; - - for(ii=0; rc==SQLITE_OK && ii<pPhrase->nToken; ii++){ - Fts3PhraseToken *pTok = &pPhrase->aToken[ii]; - if( pTok->pArray==0 ){ - rc = fts3TermSegReaderArray( - pCsr, pTok->z, pTok->n, pTok->isPrefix, &pTok->pArray - ); - } - } - }else{ - rc = fts3ExprAllocateSegReaders(pCsr, pExpr->pLeft, pnExpr); - if( rc==SQLITE_OK ){ - rc = fts3ExprAllocateSegReaders(pCsr, pExpr->pRight, pnExpr); - } - } - return rc; -} - -/* -** Free the Fts3SegReaderArray objects associated with each token in the -** expression pExpr. In other words, this function frees the resources -** allocated by fts3ExprAllocateSegReaders(). -*/ -static void fts3ExprFreeSegReaders(Fts3Expr *pExpr){ - if( pExpr ){ - Fts3Phrase *pPhrase = pExpr->pPhrase; - if( pPhrase ){ - int kk; - for(kk=0; kk<pPhrase->nToken; kk++){ - fts3SegReaderArrayFree(pPhrase->aToken[kk].pArray); - pPhrase->aToken[kk].pArray = 0; - } - } - fts3ExprFreeSegReaders(pExpr->pLeft); - fts3ExprFreeSegReaders(pExpr->pRight); - } -} - -/* -** Return the sum of the costs of all tokens in the expression pExpr. This -** function must be called after Fts3SegReaderArrays have been allocated -** for all tokens using fts3ExprAllocateSegReaders(). -*/ -static int fts3ExprCost(Fts3Expr *pExpr){ - int nCost; /* Return value */ - if( pExpr->eType==FTSQUERY_PHRASE ){ - Fts3Phrase *pPhrase = pExpr->pPhrase; - int ii; - nCost = 0; - for(ii=0; ii<pPhrase->nToken; ii++){ - Fts3SegReaderArray *pArray = pPhrase->aToken[ii].pArray; - if( pArray ){ - nCost += pPhrase->aToken[ii].pArray->nCost; - } - } - }else{ - nCost = fts3ExprCost(pExpr->pLeft) + fts3ExprCost(pExpr->pRight); - } - return nCost; -} - -/* -** The following is a helper function (and type) for fts3EvalExpr(). It -** must be called after Fts3SegReaders have been allocated for every token -** in the expression. See the context it is called from in fts3EvalExpr() -** for further explanation. -*/ -typedef struct ExprAndCost ExprAndCost; -struct ExprAndCost { - Fts3Expr *pExpr; - int nCost; -}; -static void fts3ExprAssignCosts( - Fts3Expr *pExpr, /* Expression to create seg-readers for */ - ExprAndCost **ppExprCost /* OUT: Write to *ppExprCost */ -){ - if( pExpr->eType==FTSQUERY_AND ){ - fts3ExprAssignCosts(pExpr->pLeft, ppExprCost); - fts3ExprAssignCosts(pExpr->pRight, ppExprCost); - }else{ - (*ppExprCost)->pExpr = pExpr; - (*ppExprCost)->nCost = fts3ExprCost(pExpr); - (*ppExprCost)++; - } -} - -/* -** Evaluate the full-text expression pExpr against FTS3 table pTab. Store -** the resulting doclist in *paOut and *pnOut. This routine mallocs for -** the space needed to store the output. The caller is responsible for -** freeing the space when it has finished. -** -** This function is called in two distinct contexts: -** -** * From within the virtual table xFilter() method. In this case, the -** output doclist contains entries for all rows in the table, based on -** data read from the full-text index. -** -** In this case, if the query expression contains one or more tokens that -** are very common, then the returned doclist may contain a superset of -** the documents that actually match the expression. -** -** * From within the virtual table xNext() method. This call is only made -** if the call from within xFilter() found that there were very common -** tokens in the query expression and did return a superset of the -** matching documents. In this case the returned doclist contains only -** entries that correspond to the current row of the table. Instead of -** reading the data for each token from the full-text index, the data is -** already available in-memory in the Fts3PhraseToken.pDeferred structures. -** See fts3EvalDeferred() for how it gets there. -** -** In the first case above, Fts3Cursor.doDeferred==0. In the second (if it is -** required) Fts3Cursor.doDeferred==1. -** -** If the SQLite invokes the snippet(), offsets() or matchinfo() function -** as part of a SELECT on an FTS3 table, this function is called on each -** individual phrase expression in the query. If there were very common tokens -** found in the xFilter() call, then this function is called once for phrase -** for each row visited, and the returned doclist contains entries for the -** current row only. Otherwise, if there were no very common tokens, then this -** function is called once only for each phrase in the query and the returned -** doclist contains entries for all rows of the table. -** -** Fts3Cursor.doDeferred==1 when this function is called on phrases as a -** result of a snippet(), offsets() or matchinfo() invocation. -*/ -static int fts3EvalExpr( - Fts3Cursor *p, /* Virtual table cursor handle */ - Fts3Expr *pExpr, /* Parsed fts3 expression */ - char **paOut, /* OUT: Pointer to malloc'd result buffer */ - int *pnOut, /* OUT: Size of buffer at *paOut */ - int isReqPos /* Require positions in output buffer */ -){ - int rc = SQLITE_OK; /* Return code */ - - /* Zero the output parameters. */ - *paOut = 0; - *pnOut = 0; - - if( pExpr ){ - assert( pExpr->eType==FTSQUERY_NEAR || pExpr->eType==FTSQUERY_OR - || pExpr->eType==FTSQUERY_AND || pExpr->eType==FTSQUERY_NOT - || pExpr->eType==FTSQUERY_PHRASE - ); - assert( pExpr->eType==FTSQUERY_PHRASE || isReqPos==0 ); - - if( pExpr->eType==FTSQUERY_PHRASE ){ - rc = fts3PhraseSelect(p, pExpr->pPhrase, - isReqPos || (pExpr->pParent && pExpr->pParent->eType==FTSQUERY_NEAR), - paOut, pnOut - ); - fts3ExprFreeSegReaders(pExpr); - }else if( p->eEvalmode==FTS3_EVAL_FILTER && pExpr->eType==FTSQUERY_AND ){ - ExprAndCost *aExpr = 0; /* Array of AND'd expressions and costs */ - int nExpr = 0; /* Size of aExpr[] */ - char *aRet = 0; /* Doclist to return to caller */ - int nRet = 0; /* Length of aRet[] in bytes */ - int nDoc = 0x7FFFFFFF; - - assert( !isReqPos ); - - rc = fts3ExprAllocateSegReaders(p, pExpr, &nExpr); - if( rc==SQLITE_OK ){ - assert( nExpr>1 ); - aExpr = sqlite3_malloc(sizeof(ExprAndCost) * nExpr); - if( !aExpr ) rc = SQLITE_NOMEM; - } - if( rc==SQLITE_OK ){ - int ii; /* Used to iterate through expressions */ - - fts3ExprAssignCosts(pExpr, &aExpr); - aExpr -= nExpr; - for(ii=0; ii<nExpr; ii++){ - char *aNew; - int nNew; - int jj; - ExprAndCost *pBest = 0; - - for(jj=0; jj<nExpr; jj++){ - ExprAndCost *pCand = &aExpr[jj]; - if( pCand->pExpr && (pBest==0 || pCand->nCost<pBest->nCost) ){ - pBest = pCand; - } - } - - if( pBest->nCost>nDoc ){ - rc = fts3DeferExpression(p, p->pExpr); - break; - }else{ - rc = fts3EvalExpr(p, pBest->pExpr, &aNew, &nNew, 0); - if( rc!=SQLITE_OK ) break; - pBest->pExpr = 0; - if( ii==0 ){ - aRet = aNew; - nRet = nNew; - nDoc = fts3DoclistCountDocids(0, aRet, nRet); - }else{ - fts3DoclistMerge( - MERGE_AND, 0, 0, aRet, &nRet, aRet, nRet, aNew, nNew, &nDoc - ); - sqlite3_free(aNew); - } - } - } - } - - if( rc==SQLITE_OK ){ - *paOut = aRet; - *pnOut = nRet; - }else{ - assert( *paOut==0 ); - sqlite3_free(aRet); - } - sqlite3_free(aExpr); - fts3ExprFreeSegReaders(pExpr); - - }else{ - char *aLeft; - char *aRight; - int nLeft; - int nRight; - - assert( pExpr->eType==FTSQUERY_NEAR - || pExpr->eType==FTSQUERY_OR - || pExpr->eType==FTSQUERY_NOT - || (pExpr->eType==FTSQUERY_AND && p->eEvalmode==FTS3_EVAL_NEXT) - ); - - if( 0==(rc = fts3EvalExpr(p, pExpr->pRight, &aRight, &nRight, isReqPos)) - && 0==(rc = fts3EvalExpr(p, pExpr->pLeft, &aLeft, &nLeft, isReqPos)) - ){ - switch( pExpr->eType ){ - case FTSQUERY_NEAR: { - Fts3Expr *pLeft; - Fts3Expr *pRight; - int mergetype = MERGE_NEAR; - if( pExpr->pParent && pExpr->pParent->eType==FTSQUERY_NEAR ){ - mergetype = MERGE_POS_NEAR; - } - pLeft = pExpr->pLeft; - while( pLeft->eType==FTSQUERY_NEAR ){ - pLeft=pLeft->pRight; - } - pRight = pExpr->pRight; - assert( pRight->eType==FTSQUERY_PHRASE ); - assert( pLeft->eType==FTSQUERY_PHRASE ); - - rc = fts3NearMerge(mergetype, pExpr->nNear, - pLeft->pPhrase->nToken, aLeft, nLeft, - pRight->pPhrase->nToken, aRight, nRight, - paOut, pnOut - ); - sqlite3_free(aLeft); - break; - } - - case FTSQUERY_OR: { - /* Allocate a buffer for the output. The maximum size is the - ** sum of the sizes of the two input buffers. The +1 term is - ** so that a buffer of zero bytes is never allocated - this can - ** cause fts3DoclistMerge() to incorrectly return SQLITE_NOMEM. - */ - char *aBuffer = sqlite3_malloc(nRight+nLeft+1); - rc = fts3DoclistMerge(MERGE_OR, 0, 0, aBuffer, pnOut, - aLeft, nLeft, aRight, nRight, 0 - ); - *paOut = aBuffer; - sqlite3_free(aLeft); - break; - } - - default: { - assert( FTSQUERY_NOT==MERGE_NOT && FTSQUERY_AND==MERGE_AND ); - fts3DoclistMerge(pExpr->eType, 0, 0, aLeft, pnOut, - aLeft, nLeft, aRight, nRight, 0 - ); - *paOut = aLeft; - break; - } - } - } - sqlite3_free(aRight); - } - } - - assert( rc==SQLITE_OK || *paOut==0 ); - return rc; -} - -/* -** This function is called from within xNext() for each row visited by -** an FTS3 query. If evaluating the FTS3 query expression within xFilter() -** was able to determine the exact set of matching rows, this function sets -** *pbRes to true and returns SQLITE_IO immediately. -** -** Otherwise, if evaluating the query expression within xFilter() returned a -** superset of the matching documents instead of an exact set (this happens -** when the query includes very common tokens and it is deemed too expensive to -** load their doclists from disk), this function tests if the current row -** really does match the FTS3 query. -** -** If an error occurs, an SQLite error code is returned. Otherwise, SQLITE_OK -** is returned and *pbRes is set to true if the current row matches the -** FTS3 query (and should be included in the results returned to SQLite), or -** false otherwise. -*/ -static int fts3EvalDeferred( - Fts3Cursor *pCsr, /* FTS3 cursor pointing at row to test */ - int *pbRes /* OUT: Set to true if row is a match */ -){ - int rc = SQLITE_OK; - if( pCsr->pDeferred==0 ){ - *pbRes = 1; - }else{ - rc = fts3CursorSeek(0, pCsr); - if( rc==SQLITE_OK ){ - sqlite3Fts3FreeDeferredDoclists(pCsr); - rc = sqlite3Fts3CacheDeferredDoclists(pCsr); - } - if( rc==SQLITE_OK ){ - char *a = 0; - int n = 0; - rc = fts3EvalExpr(pCsr, pCsr->pExpr, &a, &n, 0); - assert( n>=0 ); - *pbRes = (n>0); - sqlite3_free(a); - } - } - return rc; -} - -/* ** Advance the cursor to the next row in the %_content table that ** matches the search criteria. For a MATCH search, this will be ** the next row that matches. For a full-table scan, this will be @@ -111027,31 +114461,20 @@ static int fts3EvalDeferred( ** subsequently to determine whether or not an EOF was hit. */ static int fts3NextMethod(sqlite3_vtab_cursor *pCursor){ - int res; - int rc = SQLITE_OK; /* Return code */ + int rc; Fts3Cursor *pCsr = (Fts3Cursor *)pCursor; - - pCsr->eEvalmode = FTS3_EVAL_NEXT; - do { - if( pCsr->aDoclist==0 ){ - if( SQLITE_ROW!=sqlite3_step(pCsr->pStmt) ){ - pCsr->isEof = 1; - rc = sqlite3_reset(pCsr->pStmt); - break; - } - pCsr->iPrevId = sqlite3_column_int64(pCsr->pStmt, 0); + if( pCsr->eSearch==FTS3_DOCID_SEARCH || pCsr->eSearch==FTS3_FULLSCAN_SEARCH ){ + if( SQLITE_ROW!=sqlite3_step(pCsr->pStmt) ){ + pCsr->isEof = 1; + rc = sqlite3_reset(pCsr->pStmt); }else{ - if( pCsr->pNextId>=&pCsr->aDoclist[pCsr->nDoclist] ){ - pCsr->isEof = 1; - break; - } - sqlite3_reset(pCsr->pStmt); - fts3GetDeltaVarint(&pCsr->pNextId, &pCsr->iPrevId); - pCsr->isRequireSeek = 1; - pCsr->isMatchinfoNeeded = 1; + pCsr->iPrevId = sqlite3_column_int64(pCsr->pStmt, 0); + rc = SQLITE_OK; } - }while( SQLITE_OK==(rc = fts3EvalDeferred(pCsr, &res)) && res==0 ); - + }else{ + rc = sqlite3Fts3EvalNext((Fts3Cursor *)pCursor); + } + assert( ((Fts3Table *)pCsr->base.pVtab)->pSegments==0 ); return rc; } @@ -111078,11 +114501,7 @@ static int fts3FilterMethod( int nVal, /* Number of elements in apVal */ sqlite3_value **apVal /* Arguments for the indexing scheme */ ){ - const char *azSql[] = { - "SELECT * FROM %Q.'%q_content' WHERE docid = ?", /* non-full-table-scan */ - "SELECT * FROM %Q.'%q_content'", /* full-table-scan */ - }; - int rc; /* Return code */ + int rc; char *zSql; /* SQL statement used to access %_content */ Fts3Table *p = (Fts3Table *)pCursor->pVtab; Fts3Cursor *pCsr = (Fts3Cursor *)pCursor; @@ -111101,6 +114520,13 @@ static int fts3FilterMethod( sqlite3Fts3ExprFree(pCsr->pExpr); memset(&pCursor[1], 0, sizeof(Fts3Cursor)-sizeof(sqlite3_vtab_cursor)); + if( idxStr ){ + pCsr->bDesc = (idxStr[0]=='D'); + }else{ + pCsr->bDesc = p->bDescIdx; + } + pCsr->eSearch = (i16)idxNum; + if( idxNum!=FTS3_DOCID_SEARCH && idxNum!=FTS3_FULLSCAN_SEARCH ){ int iCol = idxNum-FTS3_FULLTEXT_SEARCH; const char *zQuery = (const char *)sqlite3_value_text(apVal[0]); @@ -111114,8 +114540,8 @@ static int fts3FilterMethod( ); if( rc!=SQLITE_OK ){ if( rc==SQLITE_ERROR ){ - p->base.zErrMsg = sqlite3_mprintf("malformed MATCH expression: [%s]", - zQuery); + static const char *zErr = "malformed MATCH expression: [%s]"; + p->base.zErrMsg = sqlite3_mprintf(zErr, zQuery); } return rc; } @@ -111123,7 +114549,8 @@ static int fts3FilterMethod( rc = sqlite3Fts3ReadLock(p); if( rc!=SQLITE_OK ) return rc; - rc = fts3EvalExpr(pCsr, pCsr->pExpr, &pCsr->aDoclist, &pCsr->nDoclist, 0); + rc = sqlite3Fts3EvalStart(pCsr, pCsr->pExpr, 1); + sqlite3Fts3SegmentsClose(p); if( rc!=SQLITE_OK ) return rc; pCsr->pNextId = pCsr->aDoclist; @@ -111135,19 +114562,24 @@ static int fts3FilterMethod( ** full-text query or docid lookup, the statement retrieves a single ** row by docid. */ - zSql = sqlite3_mprintf(azSql[idxNum==FTS3_FULLSCAN_SEARCH], p->zDb, p->zName); - if( !zSql ){ - rc = SQLITE_NOMEM; + if( idxNum==FTS3_FULLSCAN_SEARCH ){ + const char *zSort = (pCsr->bDesc ? "DESC" : "ASC"); + const char *zTmpl = "SELECT %s FROM %Q.'%q_content' AS x ORDER BY docid %s"; + zSql = sqlite3_mprintf(zTmpl, p->zReadExprlist, p->zDb, p->zName, zSort); }else{ - rc = sqlite3_prepare_v2(p->db, zSql, -1, &pCsr->pStmt, 0); - sqlite3_free(zSql); + const char *zTmpl = "SELECT %s FROM %Q.'%q_content' AS x WHERE docid = ?"; + zSql = sqlite3_mprintf(zTmpl, p->zReadExprlist, p->zDb, p->zName); } - if( rc==SQLITE_OK && idxNum==FTS3_DOCID_SEARCH ){ + if( !zSql ) return SQLITE_NOMEM; + rc = sqlite3_prepare_v2(p->db, zSql, -1, &pCsr->pStmt, 0); + sqlite3_free(zSql); + if( rc!=SQLITE_OK ) return rc; + + if( idxNum==FTS3_DOCID_SEARCH ){ rc = sqlite3_bind_value(pCsr->pStmt, 1, apVal[0]); + if( rc!=SQLITE_OK ) return rc; } - pCsr->eSearch = (i16)idxNum; - if( rc!=SQLITE_OK ) return rc; return fts3NextMethod(pCursor); } @@ -111167,16 +114599,7 @@ static int fts3EofMethod(sqlite3_vtab_cursor *pCursor){ */ static int fts3RowidMethod(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){ Fts3Cursor *pCsr = (Fts3Cursor *) pCursor; - if( pCsr->aDoclist ){ - *pRowid = pCsr->iPrevId; - }else{ - /* This branch runs if the query is implemented using a full-table scan - ** (not using the full-text index). In this case grab the rowid from the - ** SELECT statement. - */ - assert( pCsr->isRequireSeek==0 ); - *pRowid = sqlite3_column_int64(pCsr->pStmt, 0); - } + *pRowid = pCsr->iPrevId; return SQLITE_OK; } @@ -111189,7 +114612,7 @@ static int fts3ColumnMethod( sqlite3_context *pContext, /* Context for sqlite3_result_xxx() calls */ int iCol /* Index of column to read value from */ ){ - int rc; /* Return Code */ + int rc = SQLITE_OK; /* Return Code */ Fts3Cursor *pCsr = (Fts3Cursor *) pCursor; Fts3Table *p = (Fts3Table *)pCursor->pVtab; @@ -111200,21 +114623,20 @@ static int fts3ColumnMethod( /* This call is a request for the "docid" column. Since "docid" is an ** alias for "rowid", use the xRowid() method to obtain the value. */ - sqlite3_int64 iRowid; - rc = fts3RowidMethod(pCursor, &iRowid); - sqlite3_result_int64(pContext, iRowid); + sqlite3_result_int64(pContext, pCsr->iPrevId); }else if( iCol==p->nColumn ){ /* The extra column whose name is the same as the table. ** Return a blob which is a pointer to the cursor. */ sqlite3_result_blob(pContext, &pCsr, sizeof(pCsr), SQLITE_TRANSIENT); - rc = SQLITE_OK; }else{ rc = fts3CursorSeek(0, pCsr); if( rc==SQLITE_OK ){ sqlite3_result_value(pContext, sqlite3_column_value(pCsr->pStmt, iCol+1)); } } + + assert( ((Fts3Table *)pCsr->base.pVtab)->pSegments==0 ); return rc; } @@ -111246,8 +114668,13 @@ static int fts3SyncMethod(sqlite3_vtab *pVtab){ ** Implementation of xBegin() method. This is a no-op. */ static int fts3BeginMethod(sqlite3_vtab *pVtab){ + TESTONLY( Fts3Table *p = (Fts3Table*)pVtab ); UNUSED_PARAMETER(pVtab); - assert( ((Fts3Table *)pVtab)->nPendingData==0 ); + assert( p->pSegments==0 ); + assert( p->nPendingData==0 ); + assert( p->inTransaction!=1 ); + TESTONLY( p->inTransaction = 1 ); + TESTONLY( p->mxSavepoint = -1; ); return SQLITE_OK; } @@ -111257,8 +114684,13 @@ static int fts3BeginMethod(sqlite3_vtab *pVtab){ ** by fts3SyncMethod(). */ static int fts3CommitMethod(sqlite3_vtab *pVtab){ + TESTONLY( Fts3Table *p = (Fts3Table*)pVtab ); UNUSED_PARAMETER(pVtab); - assert( ((Fts3Table *)pVtab)->nPendingData==0 ); + assert( p->nPendingData==0 ); + assert( p->inTransaction!=0 ); + assert( p->pSegments==0 ); + TESTONLY( p->inTransaction = 0 ); + TESTONLY( p->mxSavepoint = -1; ); return SQLITE_OK; } @@ -111267,86 +114699,31 @@ static int fts3CommitMethod(sqlite3_vtab *pVtab){ ** hash-table. Any changes made to the database are reverted by SQLite. */ static int fts3RollbackMethod(sqlite3_vtab *pVtab){ - sqlite3Fts3PendingTermsClear((Fts3Table *)pVtab); + Fts3Table *p = (Fts3Table*)pVtab; + sqlite3Fts3PendingTermsClear(p); + assert( p->inTransaction!=0 ); + TESTONLY( p->inTransaction = 0 ); + TESTONLY( p->mxSavepoint = -1; ); return SQLITE_OK; } /* -** Load the doclist associated with expression pExpr to pExpr->aDoclist. -** The loaded doclist contains positions as well as the document ids. -** This is used by the matchinfo(), snippet() and offsets() auxillary -** functions. +** When called, *ppPoslist must point to the byte immediately following the +** end of a position-list. i.e. ( (*ppPoslist)[-1]==POS_END ). This function +** moves *ppPoslist so that it instead points to the first byte of the +** same position list. */ -SQLITE_PRIVATE int sqlite3Fts3ExprLoadDoclist(Fts3Cursor *pCsr, Fts3Expr *pExpr){ - int rc; - assert( pExpr->eType==FTSQUERY_PHRASE && pExpr->pPhrase ); - assert( pCsr->eEvalmode==FTS3_EVAL_NEXT ); - rc = fts3EvalExpr(pCsr, pExpr, &pExpr->aDoclist, &pExpr->nDoclist, 1); - return rc; -} +static void fts3ReversePoslist(char *pStart, char **ppPoslist){ + char *p = &(*ppPoslist)[-2]; + char c; -SQLITE_PRIVATE int sqlite3Fts3ExprLoadFtDoclist( - Fts3Cursor *pCsr, - Fts3Expr *pExpr, - char **paDoclist, - int *pnDoclist -){ - int rc; - assert( pCsr->eEvalmode==FTS3_EVAL_NEXT ); - assert( pExpr->eType==FTSQUERY_PHRASE && pExpr->pPhrase ); - pCsr->eEvalmode = FTS3_EVAL_MATCHINFO; - rc = fts3EvalExpr(pCsr, pExpr, paDoclist, pnDoclist, 1); - pCsr->eEvalmode = FTS3_EVAL_NEXT; - return rc; -} - -/* -** After ExprLoadDoclist() (see above) has been called, this function is -** used to iterate/search through the position lists that make up the doclist -** stored in pExpr->aDoclist. -*/ -SQLITE_PRIVATE char *sqlite3Fts3FindPositions( - Fts3Expr *pExpr, /* Access this expressions doclist */ - sqlite3_int64 iDocid, /* Docid associated with requested pos-list */ - int iCol /* Column of requested pos-list */ -){ - assert( pExpr->isLoaded ); - if( pExpr->aDoclist ){ - char *pEnd = &pExpr->aDoclist[pExpr->nDoclist]; - char *pCsr = pExpr->pCurrent; - - assert( pCsr ); - while( pCsr<pEnd ){ - if( pExpr->iCurrent<iDocid ){ - fts3PoslistCopy(0, &pCsr); - if( pCsr<pEnd ){ - fts3GetDeltaVarint(&pCsr, &pExpr->iCurrent); - } - pExpr->pCurrent = pCsr; - }else{ - if( pExpr->iCurrent==iDocid ){ - int iThis = 0; - if( iCol<0 ){ - /* If iCol is negative, return a pointer to the start of the - ** position-list (instead of a pointer to the start of a list - ** of offsets associated with a specific column). - */ - return pCsr; - } - while( iThis<iCol ){ - fts3ColumnlistCopy(0, &pCsr); - if( *pCsr==0x00 ) return 0; - pCsr++; - pCsr += sqlite3Fts3GetVarint32(pCsr, &iThis); - } - if( iCol==iThis && (*pCsr&0xFE) ) return pCsr; - } - return 0; - } - } + while( p>pStart && (c=*p--)==0 ); + while( p>pStart && (*p & 0x80) | c ){ + c = *p--; } - - return 0; + if( p>pStart ){ p = &p[2]; } + while( *p++&0x80 ); + *ppPoslist = p; } /* @@ -111579,8 +114956,34 @@ static int fts3RenameMethod( return rc; } +static int fts3SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){ + UNUSED_PARAMETER(iSavepoint); + assert( ((Fts3Table *)pVtab)->inTransaction ); + assert( ((Fts3Table *)pVtab)->mxSavepoint < iSavepoint ); + TESTONLY( ((Fts3Table *)pVtab)->mxSavepoint = iSavepoint ); + return fts3SyncMethod(pVtab); +} +static int fts3ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){ + TESTONLY( Fts3Table *p = (Fts3Table*)pVtab ); + UNUSED_PARAMETER(iSavepoint); + UNUSED_PARAMETER(pVtab); + assert( p->inTransaction ); + assert( p->mxSavepoint >= iSavepoint ); + TESTONLY( p->mxSavepoint = iSavepoint-1 ); + return SQLITE_OK; +} +static int fts3RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){ + Fts3Table *p = (Fts3Table*)pVtab; + UNUSED_PARAMETER(iSavepoint); + assert( p->inTransaction ); + assert( p->mxSavepoint >= iSavepoint ); + TESTONLY( p->mxSavepoint = iSavepoint ); + sqlite3Fts3PendingTermsClear(p); + return SQLITE_OK; +} + static const sqlite3_module fts3Module = { - /* iVersion */ 0, + /* iVersion */ 2, /* xCreate */ fts3CreateMethod, /* xConnect */ fts3ConnectMethod, /* xBestIndex */ fts3BestIndexMethod, @@ -111600,6 +115003,9 @@ static const sqlite3_module fts3Module = { /* xRollback */ fts3RollbackMethod, /* xFindFunction */ fts3FindFunctionMethod, /* xRename */ fts3RenameMethod, + /* xSavepoint */ fts3SavepointMethod, + /* xRelease */ fts3ReleaseMethod, + /* xRollbackTo */ fts3RollbackToMethod, }; /* @@ -111646,6 +115052,14 @@ SQLITE_PRIVATE int sqlite3Fts3Init(sqlite3 *db){ sqlite3Fts3IcuTokenizerModule(&pIcu); #endif +#ifdef SQLITE_TEST + rc = sqlite3Fts3InitTerm(db); + if( rc!=SQLITE_OK ) return rc; +#endif + + rc = sqlite3Fts3InitAux(db); + if( rc!=SQLITE_OK ) return rc; + sqlite3Fts3SimpleTokenizerModule(&pSimple); sqlite3Fts3PorterTokenizerModule(&pPorter); @@ -111718,9 +115132,1785 @@ SQLITE_API int sqlite3_extension_init( } #endif + +/* +** Allocate an Fts3MultiSegReader for each token in the expression headed +** by pExpr. +** +** An Fts3SegReader object is a cursor that can seek or scan a range of +** entries within a single segment b-tree. An Fts3MultiSegReader uses multiple +** Fts3SegReader objects internally to provide an interface to seek or scan +** within the union of all segments of a b-tree. Hence the name. +** +** If the allocated Fts3MultiSegReader just seeks to a single entry in a +** segment b-tree (if the term is not a prefix or it is a prefix for which +** there exists prefix b-tree of the right length) then it may be traversed +** and merged incrementally. Otherwise, it has to be merged into an in-memory +** doclist and then traversed. +*/ +static void fts3EvalAllocateReaders( + Fts3Cursor *pCsr, + Fts3Expr *pExpr, + int *pnToken, /* OUT: Total number of tokens in phrase. */ + int *pnOr, /* OUT: Total number of OR nodes in expr. */ + int *pRc +){ + if( pExpr && SQLITE_OK==*pRc ){ + if( pExpr->eType==FTSQUERY_PHRASE ){ + int i; + int nToken = pExpr->pPhrase->nToken; + *pnToken += nToken; + for(i=0; i<nToken; i++){ + Fts3PhraseToken *pToken = &pExpr->pPhrase->aToken[i]; + int rc = sqlite3Fts3TermSegReaderCursor(pCsr, + pToken->z, pToken->n, pToken->isPrefix, &pToken->pSegcsr + ); + if( rc!=SQLITE_OK ){ + *pRc = rc; + return; + } + } + assert( pExpr->pPhrase->iDoclistToken==0 ); + pExpr->pPhrase->iDoclistToken = -1; + }else{ + *pnOr += (pExpr->eType==FTSQUERY_OR); + fts3EvalAllocateReaders(pCsr, pExpr->pLeft, pnToken, pnOr, pRc); + fts3EvalAllocateReaders(pCsr, pExpr->pRight, pnToken, pnOr, pRc); + } + } +} + +static void fts3EvalPhraseMergeToken( + Fts3Table *pTab, + Fts3Phrase *p, + int iToken, + char *pList, + int nList +){ + assert( iToken!=p->iDoclistToken ); + + if( pList==0 ){ + sqlite3_free(p->doclist.aAll); + p->doclist.aAll = 0; + p->doclist.nAll = 0; + } + + else if( p->iDoclistToken<0 ){ + p->doclist.aAll = pList; + p->doclist.nAll = nList; + } + + else if( p->doclist.aAll==0 ){ + sqlite3_free(pList); + } + + else { + char *pLeft; + char *pRight; + int nLeft; + int nRight; + int nDiff; + + if( p->iDoclistToken<iToken ){ + pLeft = p->doclist.aAll; + nLeft = p->doclist.nAll; + pRight = pList; + nRight = nList; + nDiff = iToken - p->iDoclistToken; + }else{ + pRight = p->doclist.aAll; + nRight = p->doclist.nAll; + pLeft = pList; + nLeft = nList; + nDiff = p->iDoclistToken - iToken; + } + + fts3DoclistPhraseMerge(pTab->bDescIdx, nDiff, pLeft, nLeft, pRight,&nRight); + sqlite3_free(pLeft); + p->doclist.aAll = pRight; + p->doclist.nAll = nRight; + } + + if( iToken>p->iDoclistToken ) p->iDoclistToken = iToken; +} + +static int fts3EvalPhraseLoad( + Fts3Cursor *pCsr, + Fts3Phrase *p +){ + Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab; + int iToken; + int rc = SQLITE_OK; + + for(iToken=0; rc==SQLITE_OK && iToken<p->nToken; iToken++){ + Fts3PhraseToken *pToken = &p->aToken[iToken]; + assert( pToken->pDeferred==0 || pToken->pSegcsr==0 ); + + if( pToken->pSegcsr ){ + int nThis = 0; + char *pThis = 0; + rc = fts3TermSelect(pTab, pToken, p->iColumn, 1, &nThis, &pThis); + if( rc==SQLITE_OK ){ + fts3EvalPhraseMergeToken(pTab, p, iToken, pThis, nThis); + } + } + assert( pToken->pSegcsr==0 ); + } + + return rc; +} + +static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){ + int iToken; + int rc = SQLITE_OK; + + int nMaxUndeferred = pPhrase->iDoclistToken; + char *aPoslist = 0; + int nPoslist = 0; + int iPrev = -1; + + assert( pPhrase->doclist.bFreeList==0 ); + + for(iToken=0; rc==SQLITE_OK && iToken<pPhrase->nToken; iToken++){ + Fts3PhraseToken *pToken = &pPhrase->aToken[iToken]; + Fts3DeferredToken *pDeferred = pToken->pDeferred; + + if( pDeferred ){ + char *pList; + int nList; + rc = sqlite3Fts3DeferredTokenList(pDeferred, &pList, &nList); + if( rc!=SQLITE_OK ) return rc; + + if( pList==0 ){ + sqlite3_free(aPoslist); + pPhrase->doclist.pList = 0; + pPhrase->doclist.nList = 0; + return SQLITE_OK; + + }else if( aPoslist==0 ){ + aPoslist = pList; + nPoslist = nList; + + }else{ + char *aOut = pList; + char *p1 = aPoslist; + char *p2 = aOut; + + assert( iPrev>=0 ); + fts3PoslistPhraseMerge(&aOut, iToken-iPrev, 0, 1, &p1, &p2); + sqlite3_free(aPoslist); + aPoslist = pList; + nPoslist = aOut - aPoslist; + if( nPoslist==0 ){ + sqlite3_free(aPoslist); + pPhrase->doclist.pList = 0; + pPhrase->doclist.nList = 0; + return SQLITE_OK; + } + } + iPrev = iToken; + } + } + + if( iPrev>=0 ){ + if( nMaxUndeferred<0 ){ + pPhrase->doclist.pList = aPoslist; + pPhrase->doclist.nList = nPoslist; + pPhrase->doclist.iDocid = pCsr->iPrevId; + pPhrase->doclist.bFreeList = 1; + }else{ + int nDistance; + char *p1; + char *p2; + char *aOut; + + if( nMaxUndeferred>iPrev ){ + p1 = aPoslist; + p2 = pPhrase->doclist.pList; + nDistance = nMaxUndeferred - iPrev; + }else{ + p1 = pPhrase->doclist.pList; + p2 = aPoslist; + nDistance = iPrev - nMaxUndeferred; + } + + aOut = (char *)sqlite3_malloc(nPoslist+8); + if( !aOut ){ + sqlite3_free(aPoslist); + return SQLITE_NOMEM; + } + + pPhrase->doclist.pList = aOut; + if( fts3PoslistPhraseMerge(&aOut, nDistance, 0, 1, &p1, &p2) ){ + pPhrase->doclist.bFreeList = 1; + pPhrase->doclist.nList = (aOut - pPhrase->doclist.pList); + }else{ + sqlite3_free(aOut); + pPhrase->doclist.pList = 0; + pPhrase->doclist.nList = 0; + } + sqlite3_free(aPoslist); + } + } + + return SQLITE_OK; +} + +/* +** This function is called for each Fts3Phrase in a full-text query +** expression to initialize the mechanism for returning rows. Once this +** function has been called successfully on an Fts3Phrase, it may be +** used with fts3EvalPhraseNext() to iterate through the matching docids. +*/ +static int fts3EvalPhraseStart(Fts3Cursor *pCsr, int bOptOk, Fts3Phrase *p){ + int rc; + Fts3PhraseToken *pFirst = &p->aToken[0]; + Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab; + + if( pCsr->bDesc==pTab->bDescIdx + && bOptOk==1 + && p->nToken==1 + && pFirst->pSegcsr + && pFirst->pSegcsr->bLookup + ){ + /* Use the incremental approach. */ + int iCol = (p->iColumn >= pTab->nColumn ? -1 : p->iColumn); + rc = sqlite3Fts3MsrIncrStart( + pTab, pFirst->pSegcsr, iCol, pFirst->z, pFirst->n); + p->bIncr = 1; + + }else{ + /* Load the full doclist for the phrase into memory. */ + rc = fts3EvalPhraseLoad(pCsr, p); + p->bIncr = 0; + } + + assert( rc!=SQLITE_OK || p->nToken<1 || p->aToken[0].pSegcsr==0 || p->bIncr ); + return rc; +} + +/* +** This function is used to iterate backwards (from the end to start) +** through doclists. +*/ +SQLITE_PRIVATE void sqlite3Fts3DoclistPrev( + int bDescIdx, /* True if the doclist is desc */ + char *aDoclist, /* Pointer to entire doclist */ + int nDoclist, /* Length of aDoclist in bytes */ + char **ppIter, /* IN/OUT: Iterator pointer */ + sqlite3_int64 *piDocid, /* IN/OUT: Docid pointer */ + int *pnList, /* IN/OUT: List length pointer */ + u8 *pbEof /* OUT: End-of-file flag */ +){ + char *p = *ppIter; + + assert( nDoclist>0 ); + assert( *pbEof==0 ); + assert( p || *piDocid==0 ); + assert( !p || (p>aDoclist && p<&aDoclist[nDoclist]) ); + + if( p==0 ){ + sqlite3_int64 iDocid = 0; + char *pNext = 0; + char *pDocid = aDoclist; + char *pEnd = &aDoclist[nDoclist]; + int iMul = 1; + + while( pDocid<pEnd ){ + sqlite3_int64 iDelta; + pDocid += sqlite3Fts3GetVarint(pDocid, &iDelta); + iDocid += (iMul * iDelta); + pNext = pDocid; + fts3PoslistCopy(0, &pDocid); + while( pDocid<pEnd && *pDocid==0 ) pDocid++; + iMul = (bDescIdx ? -1 : 1); + } + + *pnList = pEnd - pNext; + *ppIter = pNext; + *piDocid = iDocid; + }else{ + int iMul = (bDescIdx ? -1 : 1); + sqlite3_int64 iDelta; + fts3GetReverseVarint(&p, aDoclist, &iDelta); + *piDocid -= (iMul * iDelta); + + if( p==aDoclist ){ + *pbEof = 1; + }else{ + char *pSave = p; + fts3ReversePoslist(aDoclist, &p); + *pnList = (pSave - p); + } + *ppIter = p; + } +} + +/* +** Attempt to move the phrase iterator to point to the next matching docid. +** If an error occurs, return an SQLite error code. Otherwise, return +** SQLITE_OK. +** +** If there is no "next" entry and no error occurs, then *pbEof is set to +** 1 before returning. Otherwise, if no error occurs and the iterator is +** successfully advanced, *pbEof is set to 0. +*/ +static int fts3EvalPhraseNext( + Fts3Cursor *pCsr, + Fts3Phrase *p, + u8 *pbEof +){ + int rc = SQLITE_OK; + Fts3Doclist *pDL = &p->doclist; + Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab; + + if( p->bIncr ){ + assert( p->nToken==1 ); + assert( pDL->pNextDocid==0 ); + rc = sqlite3Fts3MsrIncrNext(pTab, p->aToken[0].pSegcsr, + &pDL->iDocid, &pDL->pList, &pDL->nList + ); + if( rc==SQLITE_OK && !pDL->pList ){ + *pbEof = 1; + } + }else if( pCsr->bDesc!=pTab->bDescIdx && pDL->nAll ){ + sqlite3Fts3DoclistPrev(pTab->bDescIdx, pDL->aAll, pDL->nAll, + &pDL->pNextDocid, &pDL->iDocid, &pDL->nList, pbEof + ); + pDL->pList = pDL->pNextDocid; + }else{ + char *pIter; /* Used to iterate through aAll */ + char *pEnd = &pDL->aAll[pDL->nAll]; /* 1 byte past end of aAll */ + if( pDL->pNextDocid ){ + pIter = pDL->pNextDocid; + }else{ + pIter = pDL->aAll; + } + + if( pIter>=pEnd ){ + /* We have already reached the end of this doclist. EOF. */ + *pbEof = 1; + }else{ + sqlite3_int64 iDelta; + pIter += sqlite3Fts3GetVarint(pIter, &iDelta); + if( pTab->bDescIdx==0 || pDL->pNextDocid==0 ){ + pDL->iDocid += iDelta; + }else{ + pDL->iDocid -= iDelta; + } + pDL->pList = pIter; + fts3PoslistCopy(0, &pIter); + pDL->nList = (pIter - pDL->pList); + + /* pIter now points just past the 0x00 that terminates the position- + ** list for document pDL->iDocid. However, if this position-list was + ** edited in place by fts3EvalNearTrim2(), then pIter may not actually + ** point to the start of the next docid value. The following line deals + ** with this case by advancing pIter past the zero-padding added by + ** fts3EvalNearTrim2(). */ + while( pIter<pEnd && *pIter==0 ) pIter++; + + pDL->pNextDocid = pIter; + assert( pIter>=&pDL->aAll[pDL->nAll] || *pIter ); + *pbEof = 0; + } + } + + return rc; +} + +static void fts3EvalStartReaders( + Fts3Cursor *pCsr, + Fts3Expr *pExpr, + int bOptOk, + int *pRc +){ + if( pExpr && SQLITE_OK==*pRc ){ + if( pExpr->eType==FTSQUERY_PHRASE ){ + int i; + int nToken = pExpr->pPhrase->nToken; + for(i=0; i<nToken; i++){ + if( pExpr->pPhrase->aToken[i].pDeferred==0 ) break; + } + pExpr->bDeferred = (i==nToken); + *pRc = fts3EvalPhraseStart(pCsr, bOptOk, pExpr->pPhrase); + }else{ + fts3EvalStartReaders(pCsr, pExpr->pLeft, bOptOk, pRc); + fts3EvalStartReaders(pCsr, pExpr->pRight, bOptOk, pRc); + pExpr->bDeferred = (pExpr->pLeft->bDeferred && pExpr->pRight->bDeferred); + } + } +} + +typedef struct Fts3TokenAndCost Fts3TokenAndCost; +struct Fts3TokenAndCost { + Fts3Phrase *pPhrase; /* The phrase the token belongs to */ + int iToken; /* Position of token in phrase */ + Fts3PhraseToken *pToken; /* The token itself */ + Fts3Expr *pRoot; + int nOvfl; + int iCol; /* The column the token must match */ +}; + +static void fts3EvalTokenCosts( + Fts3Cursor *pCsr, + Fts3Expr *pRoot, + Fts3Expr *pExpr, + Fts3TokenAndCost **ppTC, + Fts3Expr ***ppOr, + int *pRc +){ + if( *pRc==SQLITE_OK && pExpr ){ + if( pExpr->eType==FTSQUERY_PHRASE ){ + Fts3Phrase *pPhrase = pExpr->pPhrase; + int i; + for(i=0; *pRc==SQLITE_OK && i<pPhrase->nToken; i++){ + Fts3TokenAndCost *pTC = (*ppTC)++; + pTC->pPhrase = pPhrase; + pTC->iToken = i; + pTC->pRoot = pRoot; + pTC->pToken = &pPhrase->aToken[i]; + pTC->iCol = pPhrase->iColumn; + *pRc = sqlite3Fts3MsrOvfl(pCsr, pTC->pToken->pSegcsr, &pTC->nOvfl); + } + }else if( pExpr->eType!=FTSQUERY_NOT ){ + if( pExpr->eType==FTSQUERY_OR ){ + pRoot = pExpr->pLeft; + **ppOr = pRoot; + (*ppOr)++; + } + fts3EvalTokenCosts(pCsr, pRoot, pExpr->pLeft, ppTC, ppOr, pRc); + if( pExpr->eType==FTSQUERY_OR ){ + pRoot = pExpr->pRight; + **ppOr = pRoot; + (*ppOr)++; + } + fts3EvalTokenCosts(pCsr, pRoot, pExpr->pRight, ppTC, ppOr, pRc); + } + } +} + +static int fts3EvalAverageDocsize(Fts3Cursor *pCsr, int *pnPage){ + if( pCsr->nRowAvg==0 ){ + /* The average document size, which is required to calculate the cost + ** of each doclist, has not yet been determined. Read the required + ** data from the %_stat table to calculate it. + ** + ** Entry 0 of the %_stat table is a blob containing (nCol+1) FTS3 + ** varints, where nCol is the number of columns in the FTS3 table. + ** The first varint is the number of documents currently stored in + ** the table. The following nCol varints contain the total amount of + ** data stored in all rows of each column of the table, from left + ** to right. + */ + int rc; + Fts3Table *p = (Fts3Table*)pCsr->base.pVtab; + sqlite3_stmt *pStmt; + sqlite3_int64 nDoc = 0; + sqlite3_int64 nByte = 0; + const char *pEnd; + const char *a; + + rc = sqlite3Fts3SelectDoctotal(p, &pStmt); + if( rc!=SQLITE_OK ) return rc; + a = sqlite3_column_blob(pStmt, 0); + assert( a ); + + pEnd = &a[sqlite3_column_bytes(pStmt, 0)]; + a += sqlite3Fts3GetVarint(a, &nDoc); + while( a<pEnd ){ + a += sqlite3Fts3GetVarint(a, &nByte); + } + if( nDoc==0 || nByte==0 ){ + sqlite3_reset(pStmt); + return SQLITE_CORRUPT_VTAB; + } + + pCsr->nDoc = nDoc; + pCsr->nRowAvg = (int)(((nByte / nDoc) + p->nPgsz) / p->nPgsz); + assert( pCsr->nRowAvg>0 ); + rc = sqlite3_reset(pStmt); + if( rc!=SQLITE_OK ) return rc; + } + + *pnPage = pCsr->nRowAvg; + return SQLITE_OK; +} + +static int fts3EvalSelectDeferred( + Fts3Cursor *pCsr, + Fts3Expr *pRoot, + Fts3TokenAndCost *aTC, + int nTC +){ + int nDocSize = 0; + int nDocEst = 0; + int rc = SQLITE_OK; + Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab; + int ii; + + int nOvfl = 0; + int nTerm = 0; + + for(ii=0; ii<nTC; ii++){ + if( aTC[ii].pRoot==pRoot ){ + nOvfl += aTC[ii].nOvfl; + nTerm++; + } + } + if( nOvfl==0 || nTerm<2 ) return SQLITE_OK; + + rc = fts3EvalAverageDocsize(pCsr, &nDocSize); + + for(ii=0; ii<nTerm && rc==SQLITE_OK; ii++){ + int jj; + Fts3TokenAndCost *pTC = 0; + + for(jj=0; jj<nTC; jj++){ + if( aTC[jj].pToken && aTC[jj].pRoot==pRoot + && (!pTC || aTC[jj].nOvfl<pTC->nOvfl) + ){ + pTC = &aTC[jj]; + } + } + assert( pTC ); + + /* At this point pTC points to the cheapest remaining token. */ + if( ii==0 ){ + if( pTC->nOvfl ){ + nDocEst = (pTC->nOvfl * pTab->nPgsz + pTab->nPgsz) / 10; + }else{ + Fts3PhraseToken *pToken = pTC->pToken; + int nList = 0; + char *pList = 0; + rc = fts3TermSelect(pTab, pToken, pTC->iCol, 1, &nList, &pList); + assert( rc==SQLITE_OK || pList==0 ); + + if( rc==SQLITE_OK ){ + nDocEst = fts3DoclistCountDocids(1, pList, nList); + fts3EvalPhraseMergeToken(pTab, pTC->pPhrase, pTC->iToken,pList,nList); + } + } + }else{ + if( pTC->nOvfl>=(nDocEst*nDocSize) ){ + Fts3PhraseToken *pToken = pTC->pToken; + rc = sqlite3Fts3DeferToken(pCsr, pToken, pTC->iCol); + fts3SegReaderCursorFree(pToken->pSegcsr); + pToken->pSegcsr = 0; + } + nDocEst = 1 + (nDocEst/4); + } + pTC->pToken = 0; + } + + return rc; +} + +SQLITE_PRIVATE int sqlite3Fts3EvalStart(Fts3Cursor *pCsr, Fts3Expr *pExpr, int bOptOk){ + Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab; + int rc = SQLITE_OK; + int nToken = 0; + int nOr = 0; + + /* Allocate a MultiSegReader for each token in the expression. */ + fts3EvalAllocateReaders(pCsr, pExpr, &nToken, &nOr, &rc); + + /* Call fts3EvalPhraseStart() on all phrases in the expression. TODO: + ** This call will eventually also be responsible for determining which + ** tokens are 'deferred' until the document text is loaded into memory. + ** + ** Each token in each phrase is dealt with using one of the following + ** three strategies: + ** + ** 1. Entire doclist loaded into memory as part of the + ** fts3EvalStartReaders() call. + ** + ** 2. Doclist loaded into memory incrementally, as part of each + ** sqlite3Fts3EvalNext() call. + ** + ** 3. Token doclist is never loaded. Instead, documents are loaded into + ** memory and scanned for the token as part of the sqlite3Fts3EvalNext() + ** call. This is known as a "deferred" token. + */ + + /* If bOptOk is true, check if there are any tokens that should be deferred. + */ + if( rc==SQLITE_OK && bOptOk && nToken>1 && pTab->bHasStat ){ + Fts3TokenAndCost *aTC; + Fts3Expr **apOr; + aTC = (Fts3TokenAndCost *)sqlite3_malloc( + sizeof(Fts3TokenAndCost) * nToken + + sizeof(Fts3Expr *) * nOr * 2 + ); + apOr = (Fts3Expr **)&aTC[nToken]; + + if( !aTC ){ + rc = SQLITE_NOMEM; + }else{ + int ii; + Fts3TokenAndCost *pTC = aTC; + Fts3Expr **ppOr = apOr; + + fts3EvalTokenCosts(pCsr, 0, pExpr, &pTC, &ppOr, &rc); + nToken = pTC-aTC; + nOr = ppOr-apOr; + + if( rc==SQLITE_OK ){ + rc = fts3EvalSelectDeferred(pCsr, 0, aTC, nToken); + for(ii=0; rc==SQLITE_OK && ii<nOr; ii++){ + rc = fts3EvalSelectDeferred(pCsr, apOr[ii], aTC, nToken); + } + } + + sqlite3_free(aTC); + } + } + + fts3EvalStartReaders(pCsr, pExpr, bOptOk, &rc); + return rc; +} + +static void fts3EvalZeroPoslist(Fts3Phrase *pPhrase){ + if( pPhrase->doclist.bFreeList ){ + sqlite3_free(pPhrase->doclist.pList); + } + pPhrase->doclist.pList = 0; + pPhrase->doclist.nList = 0; + pPhrase->doclist.bFreeList = 0; +} + +static int fts3EvalNearTrim2( + int nNear, + char *aTmp, /* Temporary space to use */ + char **paPoslist, /* IN/OUT: Position list */ + int *pnToken, /* IN/OUT: Tokens in phrase of *paPoslist */ + Fts3Phrase *pPhrase /* The phrase object to trim the doclist of */ +){ + int nParam1 = nNear + pPhrase->nToken; + int nParam2 = nNear + *pnToken; + int nNew; + char *p2; + char *pOut; + int res; + + assert( pPhrase->doclist.pList ); + + p2 = pOut = pPhrase->doclist.pList; + res = fts3PoslistNearMerge( + &pOut, aTmp, nParam1, nParam2, paPoslist, &p2 + ); + if( res ){ + nNew = (pOut - pPhrase->doclist.pList) - 1; + assert( pPhrase->doclist.pList[nNew]=='\0' ); + assert( nNew<=pPhrase->doclist.nList && nNew>0 ); + memset(&pPhrase->doclist.pList[nNew], 0, pPhrase->doclist.nList - nNew); + pPhrase->doclist.nList = nNew; + *paPoslist = pPhrase->doclist.pList; + *pnToken = pPhrase->nToken; + } + + return res; +} + +static int fts3EvalNearTest(Fts3Expr *pExpr, int *pRc){ + int res = 1; + + /* The following block runs if pExpr is the root of a NEAR query. + ** For example, the query: + ** + ** "w" NEAR "x" NEAR "y" NEAR "z" + ** + ** which is represented in tree form as: + ** + ** | + ** +--NEAR--+ <-- root of NEAR query + ** | | + ** +--NEAR--+ "z" + ** | | + ** +--NEAR--+ "y" + ** | | + ** "w" "x" + ** + ** The right-hand child of a NEAR node is always a phrase. The + ** left-hand child may be either a phrase or a NEAR node. There are + ** no exceptions to this. + */ + if( *pRc==SQLITE_OK + && pExpr->eType==FTSQUERY_NEAR + && pExpr->bEof==0 + && (pExpr->pParent==0 || pExpr->pParent->eType!=FTSQUERY_NEAR) + ){ + Fts3Expr *p; + int nTmp = 0; /* Bytes of temp space */ + char *aTmp; /* Temp space for PoslistNearMerge() */ + + /* Allocate temporary working space. */ + for(p=pExpr; p->pLeft; p=p->pLeft){ + nTmp += p->pRight->pPhrase->doclist.nList; + } + nTmp += p->pPhrase->doclist.nList; + aTmp = sqlite3_malloc(nTmp*2); + if( !aTmp ){ + *pRc = SQLITE_NOMEM; + res = 0; + }else{ + char *aPoslist = p->pPhrase->doclist.pList; + int nToken = p->pPhrase->nToken; + + for(p=p->pParent;res && p && p->eType==FTSQUERY_NEAR; p=p->pParent){ + Fts3Phrase *pPhrase = p->pRight->pPhrase; + int nNear = p->nNear; + res = fts3EvalNearTrim2(nNear, aTmp, &aPoslist, &nToken, pPhrase); + } + + aPoslist = pExpr->pRight->pPhrase->doclist.pList; + nToken = pExpr->pRight->pPhrase->nToken; + for(p=pExpr->pLeft; p && res; p=p->pLeft){ + int nNear = p->pParent->nNear; + Fts3Phrase *pPhrase = ( + p->eType==FTSQUERY_NEAR ? p->pRight->pPhrase : p->pPhrase + ); + res = fts3EvalNearTrim2(nNear, aTmp, &aPoslist, &nToken, pPhrase); + } + } + + sqlite3_free(aTmp); + } + + return res; +} + +/* +** This macro is used by the fts3EvalNext() function. The two arguments are +** 64-bit docid values. If the current query is "ORDER BY docid ASC", then +** the macro returns (i1 - i2). Or if it is "ORDER BY docid DESC", then +** it returns (i2 - i1). This allows the same code to be used for merging +** doclists in ascending or descending order. +*/ +#define DOCID_CMP(i1, i2) ((pCsr->bDesc?-1:1) * (i1-i2)) + +static void fts3EvalNext( + Fts3Cursor *pCsr, + Fts3Expr *pExpr, + int *pRc +){ + if( *pRc==SQLITE_OK ){ + assert( pExpr->bEof==0 ); + pExpr->bStart = 1; + + switch( pExpr->eType ){ + case FTSQUERY_NEAR: + case FTSQUERY_AND: { + Fts3Expr *pLeft = pExpr->pLeft; + Fts3Expr *pRight = pExpr->pRight; + assert( !pLeft->bDeferred || !pRight->bDeferred ); + if( pLeft->bDeferred ){ + fts3EvalNext(pCsr, pRight, pRc); + pExpr->iDocid = pRight->iDocid; + pExpr->bEof = pRight->bEof; + }else if( pRight->bDeferred ){ + fts3EvalNext(pCsr, pLeft, pRc); + pExpr->iDocid = pLeft->iDocid; + pExpr->bEof = pLeft->bEof; + }else{ + fts3EvalNext(pCsr, pLeft, pRc); + fts3EvalNext(pCsr, pRight, pRc); + + while( !pLeft->bEof && !pRight->bEof && *pRc==SQLITE_OK ){ + sqlite3_int64 iDiff = DOCID_CMP(pLeft->iDocid, pRight->iDocid); + if( iDiff==0 ) break; + if( iDiff<0 ){ + fts3EvalNext(pCsr, pLeft, pRc); + }else{ + fts3EvalNext(pCsr, pRight, pRc); + } + } + + pExpr->iDocid = pLeft->iDocid; + pExpr->bEof = (pLeft->bEof || pRight->bEof); + } + break; + } + + case FTSQUERY_OR: { + Fts3Expr *pLeft = pExpr->pLeft; + Fts3Expr *pRight = pExpr->pRight; + sqlite3_int64 iCmp = DOCID_CMP(pLeft->iDocid, pRight->iDocid); + + assert( pLeft->bStart || pLeft->iDocid==pRight->iDocid ); + assert( pRight->bStart || pLeft->iDocid==pRight->iDocid ); + + if( pRight->bEof || (pLeft->bEof==0 && iCmp<0) ){ + fts3EvalNext(pCsr, pLeft, pRc); + }else if( pLeft->bEof || (pRight->bEof==0 && iCmp>0) ){ + fts3EvalNext(pCsr, pRight, pRc); + }else{ + fts3EvalNext(pCsr, pLeft, pRc); + fts3EvalNext(pCsr, pRight, pRc); + } + + pExpr->bEof = (pLeft->bEof && pRight->bEof); + iCmp = DOCID_CMP(pLeft->iDocid, pRight->iDocid); + if( pRight->bEof || (pLeft->bEof==0 && iCmp<0) ){ + pExpr->iDocid = pLeft->iDocid; + }else{ + pExpr->iDocid = pRight->iDocid; + } + + break; + } + + case FTSQUERY_NOT: { + Fts3Expr *pLeft = pExpr->pLeft; + Fts3Expr *pRight = pExpr->pRight; + + if( pRight->bStart==0 ){ + fts3EvalNext(pCsr, pRight, pRc); + assert( *pRc!=SQLITE_OK || pRight->bStart ); + } + + fts3EvalNext(pCsr, pLeft, pRc); + if( pLeft->bEof==0 ){ + while( !*pRc + && !pRight->bEof + && DOCID_CMP(pLeft->iDocid, pRight->iDocid)>0 + ){ + fts3EvalNext(pCsr, pRight, pRc); + } + } + pExpr->iDocid = pLeft->iDocid; + pExpr->bEof = pLeft->bEof; + break; + } + + default: { + Fts3Phrase *pPhrase = pExpr->pPhrase; + fts3EvalZeroPoslist(pPhrase); + *pRc = fts3EvalPhraseNext(pCsr, pPhrase, &pExpr->bEof); + pExpr->iDocid = pPhrase->doclist.iDocid; + break; + } + } + } +} + +static int fts3EvalDeferredTest(Fts3Cursor *pCsr, Fts3Expr *pExpr, int *pRc){ + int bHit = 1; + if( *pRc==SQLITE_OK ){ + switch( pExpr->eType ){ + case FTSQUERY_NEAR: + case FTSQUERY_AND: + bHit = ( + fts3EvalDeferredTest(pCsr, pExpr->pLeft, pRc) + && fts3EvalDeferredTest(pCsr, pExpr->pRight, pRc) + && fts3EvalNearTest(pExpr, pRc) + ); + + /* If the NEAR expression does not match any rows, zero the doclist for + ** all phrases involved in the NEAR. This is because the snippet(), + ** offsets() and matchinfo() functions are not supposed to recognize + ** any instances of phrases that are part of unmatched NEAR queries. + ** For example if this expression: + ** + ** ... MATCH 'a OR (b NEAR c)' + ** + ** is matched against a row containing: + ** + ** 'a b d e' + ** + ** then any snippet() should ony highlight the "a" term, not the "b" + ** (as "b" is part of a non-matching NEAR clause). + */ + if( bHit==0 + && pExpr->eType==FTSQUERY_NEAR + && (pExpr->pParent==0 || pExpr->pParent->eType!=FTSQUERY_NEAR) + ){ + Fts3Expr *p; + for(p=pExpr; p->pPhrase==0; p=p->pLeft){ + if( p->pRight->iDocid==pCsr->iPrevId ){ + fts3EvalZeroPoslist(p->pRight->pPhrase); + } + } + if( p->iDocid==pCsr->iPrevId ){ + fts3EvalZeroPoslist(p->pPhrase); + } + } + + break; + + case FTSQUERY_OR: { + int bHit1 = fts3EvalDeferredTest(pCsr, pExpr->pLeft, pRc); + int bHit2 = fts3EvalDeferredTest(pCsr, pExpr->pRight, pRc); + bHit = bHit1 || bHit2; + break; + } + + case FTSQUERY_NOT: + bHit = ( + fts3EvalDeferredTest(pCsr, pExpr->pLeft, pRc) + && !fts3EvalDeferredTest(pCsr, pExpr->pRight, pRc) + ); + break; + + default: { + if( pCsr->pDeferred + && (pExpr->iDocid==pCsr->iPrevId || pExpr->bDeferred) + ){ + Fts3Phrase *pPhrase = pExpr->pPhrase; + assert( pExpr->bDeferred || pPhrase->doclist.bFreeList==0 ); + if( pExpr->bDeferred ){ + fts3EvalZeroPoslist(pPhrase); + } + *pRc = fts3EvalDeferredPhrase(pCsr, pPhrase); + bHit = (pPhrase->doclist.pList!=0); + pExpr->iDocid = pCsr->iPrevId; + }else{ + bHit = (pExpr->bEof==0 && pExpr->iDocid==pCsr->iPrevId); + } + break; + } + } + } + return bHit; +} + +/* +** Return 1 if both of the following are true: +** +** 1. *pRc is SQLITE_OK when this function returns, and +** +** 2. After scanning the current FTS table row for the deferred tokens, +** it is determined that the row does not match the query. +** +** Or, if no error occurs and it seems the current row does match the FTS +** query, return 0. +*/ +static int fts3EvalLoadDeferred(Fts3Cursor *pCsr, int *pRc){ + int rc = *pRc; + int bMiss = 0; + if( rc==SQLITE_OK ){ + if( pCsr->pDeferred ){ + rc = fts3CursorSeek(0, pCsr); + if( rc==SQLITE_OK ){ + rc = sqlite3Fts3CacheDeferredDoclists(pCsr); + } + } + bMiss = (0==fts3EvalDeferredTest(pCsr, pCsr->pExpr, &rc)); + sqlite3Fts3FreeDeferredDoclists(pCsr); + *pRc = rc; + } + return (rc==SQLITE_OK && bMiss); +} + +/* +** Advance to the next document that matches the FTS expression in +** Fts3Cursor.pExpr. +*/ +SQLITE_PRIVATE int sqlite3Fts3EvalNext(Fts3Cursor *pCsr){ + int rc = SQLITE_OK; /* Return Code */ + Fts3Expr *pExpr = pCsr->pExpr; + assert( pCsr->isEof==0 ); + if( pExpr==0 ){ + pCsr->isEof = 1; + }else{ + do { + if( pCsr->isRequireSeek==0 ){ + sqlite3_reset(pCsr->pStmt); + } + assert( sqlite3_data_count(pCsr->pStmt)==0 ); + fts3EvalNext(pCsr, pExpr, &rc); + pCsr->isEof = pExpr->bEof; + pCsr->isRequireSeek = 1; + pCsr->isMatchinfoNeeded = 1; + pCsr->iPrevId = pExpr->iDocid; + }while( pCsr->isEof==0 && fts3EvalLoadDeferred(pCsr, &rc) ); + } + return rc; +} + +/* +** Restart interation for expression pExpr so that the next call to +** sqlite3Fts3EvalNext() visits the first row. Do not allow incremental +** loading or merging of phrase doclists for this iteration. +** +** If *pRc is other than SQLITE_OK when this function is called, it is +** a no-op. If an error occurs within this function, *pRc is set to an +** SQLite error code before returning. +*/ +static void fts3EvalRestart( + Fts3Cursor *pCsr, + Fts3Expr *pExpr, + int *pRc +){ + if( pExpr && *pRc==SQLITE_OK ){ + Fts3Phrase *pPhrase = pExpr->pPhrase; + + if( pPhrase ){ + fts3EvalZeroPoslist(pPhrase); + if( pPhrase->bIncr ){ + assert( pPhrase->nToken==1 ); + assert( pPhrase->aToken[0].pSegcsr ); + sqlite3Fts3MsrIncrRestart(pPhrase->aToken[0].pSegcsr); + *pRc = fts3EvalPhraseStart(pCsr, 0, pPhrase); + } + + pPhrase->doclist.pNextDocid = 0; + pPhrase->doclist.iDocid = 0; + } + + pExpr->iDocid = 0; + pExpr->bEof = 0; + pExpr->bStart = 0; + + fts3EvalRestart(pCsr, pExpr->pLeft, pRc); + fts3EvalRestart(pCsr, pExpr->pRight, pRc); + } +} + +/* +** After allocating the Fts3Expr.aMI[] array for each phrase in the +** expression rooted at pExpr, the cursor iterates through all rows matched +** by pExpr, calling this function for each row. This function increments +** the values in Fts3Expr.aMI[] according to the position-list currently +** found in Fts3Expr.pPhrase->doclist.pList for each of the phrase +** expression nodes. +*/ +static void fts3EvalUpdateCounts(Fts3Expr *pExpr){ + if( pExpr ){ + Fts3Phrase *pPhrase = pExpr->pPhrase; + if( pPhrase && pPhrase->doclist.pList ){ + int iCol = 0; + char *p = pPhrase->doclist.pList; + + assert( *p ); + while( 1 ){ + u8 c = 0; + int iCnt = 0; + while( 0xFE & (*p | c) ){ + if( (c&0x80)==0 ) iCnt++; + c = *p++ & 0x80; + } + + /* aMI[iCol*3 + 1] = Number of occurrences + ** aMI[iCol*3 + 2] = Number of rows containing at least one instance + */ + pExpr->aMI[iCol*3 + 1] += iCnt; + pExpr->aMI[iCol*3 + 2] += (iCnt>0); + if( *p==0x00 ) break; + p++; + p += sqlite3Fts3GetVarint32(p, &iCol); + } + } + + fts3EvalUpdateCounts(pExpr->pLeft); + fts3EvalUpdateCounts(pExpr->pRight); + } +} + +/* +** Expression pExpr must be of type FTSQUERY_PHRASE. +** +** If it is not already allocated and populated, this function allocates and +** populates the Fts3Expr.aMI[] array for expression pExpr. If pExpr is part +** of a NEAR expression, then it also allocates and populates the same array +** for all other phrases that are part of the NEAR expression. +** +** SQLITE_OK is returned if the aMI[] array is successfully allocated and +** populated. Otherwise, if an error occurs, an SQLite error code is returned. +*/ +static int fts3EvalGatherStats( + Fts3Cursor *pCsr, /* Cursor object */ + Fts3Expr *pExpr /* FTSQUERY_PHRASE expression */ +){ + int rc = SQLITE_OK; /* Return code */ + + assert( pExpr->eType==FTSQUERY_PHRASE ); + if( pExpr->aMI==0 ){ + Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab; + Fts3Expr *pRoot; /* Root of NEAR expression */ + Fts3Expr *p; /* Iterator used for several purposes */ + + sqlite3_int64 iPrevId = pCsr->iPrevId; + sqlite3_int64 iDocid; + u8 bEof; + + /* Find the root of the NEAR expression */ + pRoot = pExpr; + while( pRoot->pParent && pRoot->pParent->eType==FTSQUERY_NEAR ){ + pRoot = pRoot->pParent; + } + iDocid = pRoot->iDocid; + bEof = pRoot->bEof; + assert( pRoot->bStart ); + + /* Allocate space for the aMSI[] array of each FTSQUERY_PHRASE node */ + for(p=pRoot; p; p=p->pLeft){ + Fts3Expr *pE = (p->eType==FTSQUERY_PHRASE?p:p->pRight); + assert( pE->aMI==0 ); + pE->aMI = (u32 *)sqlite3_malloc(pTab->nColumn * 3 * sizeof(u32)); + if( !pE->aMI ) return SQLITE_NOMEM; + memset(pE->aMI, 0, pTab->nColumn * 3 * sizeof(u32)); + } + + fts3EvalRestart(pCsr, pRoot, &rc); + + while( pCsr->isEof==0 && rc==SQLITE_OK ){ + + do { + /* Ensure the %_content statement is reset. */ + if( pCsr->isRequireSeek==0 ) sqlite3_reset(pCsr->pStmt); + assert( sqlite3_data_count(pCsr->pStmt)==0 ); + + /* Advance to the next document */ + fts3EvalNext(pCsr, pRoot, &rc); + pCsr->isEof = pRoot->bEof; + pCsr->isRequireSeek = 1; + pCsr->isMatchinfoNeeded = 1; + pCsr->iPrevId = pRoot->iDocid; + }while( pCsr->isEof==0 + && pRoot->eType==FTSQUERY_NEAR + && fts3EvalLoadDeferred(pCsr, &rc) + ); + + if( rc==SQLITE_OK && pCsr->isEof==0 ){ + fts3EvalUpdateCounts(pRoot); + } + } + + pCsr->isEof = 0; + pCsr->iPrevId = iPrevId; + + if( bEof ){ + pRoot->bEof = bEof; + }else{ + /* Caution: pRoot may iterate through docids in ascending or descending + ** order. For this reason, even though it seems more defensive, the + ** do loop can not be written: + ** + ** do {...} while( pRoot->iDocid<iDocid && rc==SQLITE_OK ); + */ + fts3EvalRestart(pCsr, pRoot, &rc); + do { + fts3EvalNext(pCsr, pRoot, &rc); + assert( pRoot->bEof==0 ); + }while( pRoot->iDocid!=iDocid && rc==SQLITE_OK ); + fts3EvalLoadDeferred(pCsr, &rc); + } + } + return rc; +} + +/* +** This function is used by the matchinfo() module to query a phrase +** expression node for the following information: +** +** 1. The total number of occurrences of the phrase in each column of +** the FTS table (considering all rows), and +** +** 2. For each column, the number of rows in the table for which the +** column contains at least one instance of the phrase. +** +** If no error occurs, SQLITE_OK is returned and the values for each column +** written into the array aiOut as follows: +** +** aiOut[iCol*3 + 1] = Number of occurrences +** aiOut[iCol*3 + 2] = Number of rows containing at least one instance +** +** Caveats: +** +** * If a phrase consists entirely of deferred tokens, then all output +** values are set to the number of documents in the table. In other +** words we assume that very common tokens occur exactly once in each +** column of each row of the table. +** +** * If a phrase contains some deferred tokens (and some non-deferred +** tokens), count the potential occurrence identified by considering +** the non-deferred tokens instead of actual phrase occurrences. +** +** * If the phrase is part of a NEAR expression, then only phrase instances +** that meet the NEAR constraint are included in the counts. +*/ +SQLITE_PRIVATE int sqlite3Fts3EvalPhraseStats( + Fts3Cursor *pCsr, /* FTS cursor handle */ + Fts3Expr *pExpr, /* Phrase expression */ + u32 *aiOut /* Array to write results into (see above) */ +){ + Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab; + int rc = SQLITE_OK; + int iCol; + + if( pExpr->bDeferred && pExpr->pParent->eType!=FTSQUERY_NEAR ){ + assert( pCsr->nDoc>0 ); + for(iCol=0; iCol<pTab->nColumn; iCol++){ + aiOut[iCol*3 + 1] = (u32)pCsr->nDoc; + aiOut[iCol*3 + 2] = (u32)pCsr->nDoc; + } + }else{ + rc = fts3EvalGatherStats(pCsr, pExpr); + if( rc==SQLITE_OK ){ + assert( pExpr->aMI ); + for(iCol=0; iCol<pTab->nColumn; iCol++){ + aiOut[iCol*3 + 1] = pExpr->aMI[iCol*3 + 1]; + aiOut[iCol*3 + 2] = pExpr->aMI[iCol*3 + 2]; + } + } + } + + return rc; +} + +/* +** The expression pExpr passed as the second argument to this function +** must be of type FTSQUERY_PHRASE. +** +** The returned value is either NULL or a pointer to a buffer containing +** a position-list indicating the occurrences of the phrase in column iCol +** of the current row. +** +** More specifically, the returned buffer contains 1 varint for each +** occurence of the phrase in the column, stored using the normal (delta+2) +** compression and is terminated by either an 0x01 or 0x00 byte. For example, +** if the requested column contains "a b X c d X X" and the position-list +** for 'X' is requested, the buffer returned may contain: +** +** 0x04 0x05 0x03 0x01 or 0x04 0x05 0x03 0x00 +** +** This function works regardless of whether or not the phrase is deferred, +** incremental, or neither. +*/ +SQLITE_PRIVATE char *sqlite3Fts3EvalPhrasePoslist( + Fts3Cursor *pCsr, /* FTS3 cursor object */ + Fts3Expr *pExpr, /* Phrase to return doclist for */ + int iCol /* Column to return position list for */ +){ + Fts3Phrase *pPhrase = pExpr->pPhrase; + Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab; + char *pIter = pPhrase->doclist.pList; + int iThis; + + assert( iCol>=0 && iCol<pTab->nColumn ); + if( !pIter + || pExpr->bEof + || pExpr->iDocid!=pCsr->iPrevId + || (pPhrase->iColumn<pTab->nColumn && pPhrase->iColumn!=iCol) + ){ + return 0; + } + + assert( pPhrase->doclist.nList>0 ); + if( *pIter==0x01 ){ + pIter++; + pIter += sqlite3Fts3GetVarint32(pIter, &iThis); + }else{ + iThis = 0; + } + while( iThis<iCol ){ + fts3ColumnlistCopy(0, &pIter); + if( *pIter==0x00 ) return 0; + pIter++; + pIter += sqlite3Fts3GetVarint32(pIter, &iThis); + } + + return ((iCol==iThis)?pIter:0); +} + +/* +** Free all components of the Fts3Phrase structure that were allocated by +** the eval module. Specifically, this means to free: +** +** * the contents of pPhrase->doclist, and +** * any Fts3MultiSegReader objects held by phrase tokens. +*/ +SQLITE_PRIVATE void sqlite3Fts3EvalPhraseCleanup(Fts3Phrase *pPhrase){ + if( pPhrase ){ + int i; + sqlite3_free(pPhrase->doclist.aAll); + fts3EvalZeroPoslist(pPhrase); + memset(&pPhrase->doclist, 0, sizeof(Fts3Doclist)); + for(i=0; i<pPhrase->nToken; i++){ + fts3SegReaderCursorFree(pPhrase->aToken[i].pSegcsr); + pPhrase->aToken[i].pSegcsr = 0; + } + } +} + #endif /************** End of fts3.c ************************************************/ +/************** Begin file fts3_aux.c ****************************************/ +/* +** 2011 Jan 27 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +****************************************************************************** +** +*/ +#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) + + +typedef struct Fts3auxTable Fts3auxTable; +typedef struct Fts3auxCursor Fts3auxCursor; + +struct Fts3auxTable { + sqlite3_vtab base; /* Base class used by SQLite core */ + Fts3Table *pFts3Tab; +}; + +struct Fts3auxCursor { + sqlite3_vtab_cursor base; /* Base class used by SQLite core */ + Fts3MultiSegReader csr; /* Must be right after "base" */ + Fts3SegFilter filter; + char *zStop; + int nStop; /* Byte-length of string zStop */ + int isEof; /* True if cursor is at EOF */ + sqlite3_int64 iRowid; /* Current rowid */ + + int iCol; /* Current value of 'col' column */ + int nStat; /* Size of aStat[] array */ + struct Fts3auxColstats { + sqlite3_int64 nDoc; /* 'documents' values for current csr row */ + sqlite3_int64 nOcc; /* 'occurrences' values for current csr row */ + } *aStat; +}; + +/* +** Schema of the terms table. +*/ +#define FTS3_TERMS_SCHEMA "CREATE TABLE x(term, col, documents, occurrences)" + +/* +** This function does all the work for both the xConnect and xCreate methods. +** These tables have no persistent representation of their own, so xConnect +** and xCreate are identical operations. +*/ +static int fts3auxConnectMethod( + sqlite3 *db, /* Database connection */ + void *pUnused, /* Unused */ + int argc, /* Number of elements in argv array */ + const char * const *argv, /* xCreate/xConnect argument array */ + sqlite3_vtab **ppVtab, /* OUT: New sqlite3_vtab object */ + char **pzErr /* OUT: sqlite3_malloc'd error message */ +){ + char const *zDb; /* Name of database (e.g. "main") */ + char const *zFts3; /* Name of fts3 table */ + int nDb; /* Result of strlen(zDb) */ + int nFts3; /* Result of strlen(zFts3) */ + int nByte; /* Bytes of space to allocate here */ + int rc; /* value returned by declare_vtab() */ + Fts3auxTable *p; /* Virtual table object to return */ + + UNUSED_PARAMETER(pUnused); + + /* The user should specify a single argument - the name of an fts3 table. */ + if( argc!=4 ){ + *pzErr = sqlite3_mprintf( + "wrong number of arguments to fts4aux constructor" + ); + return SQLITE_ERROR; + } + + zDb = argv[1]; + nDb = strlen(zDb); + zFts3 = argv[3]; + nFts3 = strlen(zFts3); + + rc = sqlite3_declare_vtab(db, FTS3_TERMS_SCHEMA); + if( rc!=SQLITE_OK ) return rc; + + nByte = sizeof(Fts3auxTable) + sizeof(Fts3Table) + nDb + nFts3 + 2; + p = (Fts3auxTable *)sqlite3_malloc(nByte); + if( !p ) return SQLITE_NOMEM; + memset(p, 0, nByte); + + p->pFts3Tab = (Fts3Table *)&p[1]; + p->pFts3Tab->zDb = (char *)&p->pFts3Tab[1]; + p->pFts3Tab->zName = &p->pFts3Tab->zDb[nDb+1]; + p->pFts3Tab->db = db; + p->pFts3Tab->nIndex = 1; + + memcpy((char *)p->pFts3Tab->zDb, zDb, nDb); + memcpy((char *)p->pFts3Tab->zName, zFts3, nFts3); + sqlite3Fts3Dequote((char *)p->pFts3Tab->zName); + + *ppVtab = (sqlite3_vtab *)p; + return SQLITE_OK; +} + +/* +** This function does the work for both the xDisconnect and xDestroy methods. +** These tables have no persistent representation of their own, so xDisconnect +** and xDestroy are identical operations. +*/ +static int fts3auxDisconnectMethod(sqlite3_vtab *pVtab){ + Fts3auxTable *p = (Fts3auxTable *)pVtab; + Fts3Table *pFts3 = p->pFts3Tab; + int i; + + /* Free any prepared statements held */ + for(i=0; i<SizeofArray(pFts3->aStmt); i++){ + sqlite3_finalize(pFts3->aStmt[i]); + } + sqlite3_free(pFts3->zSegmentsTbl); + sqlite3_free(p); + return SQLITE_OK; +} + +#define FTS4AUX_EQ_CONSTRAINT 1 +#define FTS4AUX_GE_CONSTRAINT 2 +#define FTS4AUX_LE_CONSTRAINT 4 + +/* +** xBestIndex - Analyze a WHERE and ORDER BY clause. +*/ +static int fts3auxBestIndexMethod( + sqlite3_vtab *pVTab, + sqlite3_index_info *pInfo +){ + int i; + int iEq = -1; + int iGe = -1; + int iLe = -1; + + UNUSED_PARAMETER(pVTab); + + /* This vtab delivers always results in "ORDER BY term ASC" order. */ + if( pInfo->nOrderBy==1 + && pInfo->aOrderBy[0].iColumn==0 + && pInfo->aOrderBy[0].desc==0 + ){ + pInfo->orderByConsumed = 1; + } + + /* Search for equality and range constraints on the "term" column. */ + for(i=0; i<pInfo->nConstraint; i++){ + if( pInfo->aConstraint[i].usable && pInfo->aConstraint[i].iColumn==0 ){ + int op = pInfo->aConstraint[i].op; + if( op==SQLITE_INDEX_CONSTRAINT_EQ ) iEq = i; + if( op==SQLITE_INDEX_CONSTRAINT_LT ) iLe = i; + if( op==SQLITE_INDEX_CONSTRAINT_LE ) iLe = i; + if( op==SQLITE_INDEX_CONSTRAINT_GT ) iGe = i; + if( op==SQLITE_INDEX_CONSTRAINT_GE ) iGe = i; + } + } + + if( iEq>=0 ){ + pInfo->idxNum = FTS4AUX_EQ_CONSTRAINT; + pInfo->aConstraintUsage[iEq].argvIndex = 1; + pInfo->estimatedCost = 5; + }else{ + pInfo->idxNum = 0; + pInfo->estimatedCost = 20000; + if( iGe>=0 ){ + pInfo->idxNum += FTS4AUX_GE_CONSTRAINT; + pInfo->aConstraintUsage[iGe].argvIndex = 1; + pInfo->estimatedCost /= 2; + } + if( iLe>=0 ){ + pInfo->idxNum += FTS4AUX_LE_CONSTRAINT; + pInfo->aConstraintUsage[iLe].argvIndex = 1 + (iGe>=0); + pInfo->estimatedCost /= 2; + } + } + + return SQLITE_OK; +} + +/* +** xOpen - Open a cursor. +*/ +static int fts3auxOpenMethod(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCsr){ + Fts3auxCursor *pCsr; /* Pointer to cursor object to return */ + + UNUSED_PARAMETER(pVTab); + + pCsr = (Fts3auxCursor *)sqlite3_malloc(sizeof(Fts3auxCursor)); + if( !pCsr ) return SQLITE_NOMEM; + memset(pCsr, 0, sizeof(Fts3auxCursor)); + + *ppCsr = (sqlite3_vtab_cursor *)pCsr; + return SQLITE_OK; +} + +/* +** xClose - Close a cursor. +*/ +static int fts3auxCloseMethod(sqlite3_vtab_cursor *pCursor){ + Fts3Table *pFts3 = ((Fts3auxTable *)pCursor->pVtab)->pFts3Tab; + Fts3auxCursor *pCsr = (Fts3auxCursor *)pCursor; + + sqlite3Fts3SegmentsClose(pFts3); + sqlite3Fts3SegReaderFinish(&pCsr->csr); + sqlite3_free((void *)pCsr->filter.zTerm); + sqlite3_free(pCsr->zStop); + sqlite3_free(pCsr->aStat); + sqlite3_free(pCsr); + return SQLITE_OK; +} + +static int fts3auxGrowStatArray(Fts3auxCursor *pCsr, int nSize){ + if( nSize>pCsr->nStat ){ + struct Fts3auxColstats *aNew; + aNew = (struct Fts3auxColstats *)sqlite3_realloc(pCsr->aStat, + sizeof(struct Fts3auxColstats) * nSize + ); + if( aNew==0 ) return SQLITE_NOMEM; + memset(&aNew[pCsr->nStat], 0, + sizeof(struct Fts3auxColstats) * (nSize - pCsr->nStat) + ); + pCsr->aStat = aNew; + pCsr->nStat = nSize; + } + return SQLITE_OK; +} + +/* +** xNext - Advance the cursor to the next row, if any. +*/ +static int fts3auxNextMethod(sqlite3_vtab_cursor *pCursor){ + Fts3auxCursor *pCsr = (Fts3auxCursor *)pCursor; + Fts3Table *pFts3 = ((Fts3auxTable *)pCursor->pVtab)->pFts3Tab; + int rc; + + /* Increment our pretend rowid value. */ + pCsr->iRowid++; + + for(pCsr->iCol++; pCsr->iCol<pCsr->nStat; pCsr->iCol++){ + if( pCsr->aStat[pCsr->iCol].nDoc>0 ) return SQLITE_OK; + } + + rc = sqlite3Fts3SegReaderStep(pFts3, &pCsr->csr); + if( rc==SQLITE_ROW ){ + int i = 0; + int nDoclist = pCsr->csr.nDoclist; + char *aDoclist = pCsr->csr.aDoclist; + int iCol; + + int eState = 0; + + if( pCsr->zStop ){ + int n = (pCsr->nStop<pCsr->csr.nTerm) ? pCsr->nStop : pCsr->csr.nTerm; + int mc = memcmp(pCsr->zStop, pCsr->csr.zTerm, n); + if( mc<0 || (mc==0 && pCsr->csr.nTerm>pCsr->nStop) ){ + pCsr->isEof = 1; + return SQLITE_OK; + } + } + + if( fts3auxGrowStatArray(pCsr, 2) ) return SQLITE_NOMEM; + memset(pCsr->aStat, 0, sizeof(struct Fts3auxColstats) * pCsr->nStat); + iCol = 0; + + while( i<nDoclist ){ + sqlite3_int64 v = 0; + + i += sqlite3Fts3GetVarint(&aDoclist[i], &v); + switch( eState ){ + /* State 0. In this state the integer just read was a docid. */ + case 0: + pCsr->aStat[0].nDoc++; + eState = 1; + iCol = 0; + break; + + /* State 1. In this state we are expecting either a 1, indicating + ** that the following integer will be a column number, or the + ** start of a position list for column 0. + ** + ** The only difference between state 1 and state 2 is that if the + ** integer encountered in state 1 is not 0 or 1, then we need to + ** increment the column 0 "nDoc" count for this term. + */ + case 1: + assert( iCol==0 ); + if( v>1 ){ + pCsr->aStat[1].nDoc++; + } + eState = 2; + /* fall through */ + + case 2: + if( v==0 ){ /* 0x00. Next integer will be a docid. */ + eState = 0; + }else if( v==1 ){ /* 0x01. Next integer will be a column number. */ + eState = 3; + }else{ /* 2 or greater. A position. */ + pCsr->aStat[iCol+1].nOcc++; + pCsr->aStat[0].nOcc++; + } + break; + + /* State 3. The integer just read is a column number. */ + default: assert( eState==3 ); + iCol = (int)v; + if( fts3auxGrowStatArray(pCsr, iCol+2) ) return SQLITE_NOMEM; + pCsr->aStat[iCol+1].nDoc++; + eState = 2; + break; + } + } + + pCsr->iCol = 0; + rc = SQLITE_OK; + }else{ + pCsr->isEof = 1; + } + return rc; +} + +/* +** xFilter - Initialize a cursor to point at the start of its data. +*/ +static int fts3auxFilterMethod( + sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */ + int idxNum, /* Strategy index */ + const char *idxStr, /* Unused */ + int nVal, /* Number of elements in apVal */ + sqlite3_value **apVal /* Arguments for the indexing scheme */ +){ + Fts3auxCursor *pCsr = (Fts3auxCursor *)pCursor; + Fts3Table *pFts3 = ((Fts3auxTable *)pCursor->pVtab)->pFts3Tab; + int rc; + int isScan; + + UNUSED_PARAMETER(nVal); + UNUSED_PARAMETER(idxStr); + + assert( idxStr==0 ); + assert( idxNum==FTS4AUX_EQ_CONSTRAINT || idxNum==0 + || idxNum==FTS4AUX_LE_CONSTRAINT || idxNum==FTS4AUX_GE_CONSTRAINT + || idxNum==(FTS4AUX_LE_CONSTRAINT|FTS4AUX_GE_CONSTRAINT) + ); + isScan = (idxNum!=FTS4AUX_EQ_CONSTRAINT); + + /* In case this cursor is being reused, close and zero it. */ + testcase(pCsr->filter.zTerm); + sqlite3Fts3SegReaderFinish(&pCsr->csr); + sqlite3_free((void *)pCsr->filter.zTerm); + sqlite3_free(pCsr->aStat); + memset(&pCsr->csr, 0, ((u8*)&pCsr[1]) - (u8*)&pCsr->csr); + + pCsr->filter.flags = FTS3_SEGMENT_REQUIRE_POS|FTS3_SEGMENT_IGNORE_EMPTY; + if( isScan ) pCsr->filter.flags |= FTS3_SEGMENT_SCAN; + + if( idxNum&(FTS4AUX_EQ_CONSTRAINT|FTS4AUX_GE_CONSTRAINT) ){ + const unsigned char *zStr = sqlite3_value_text(apVal[0]); + if( zStr ){ + pCsr->filter.zTerm = sqlite3_mprintf("%s", zStr); + pCsr->filter.nTerm = sqlite3_value_bytes(apVal[0]); + if( pCsr->filter.zTerm==0 ) return SQLITE_NOMEM; + } + } + if( idxNum&FTS4AUX_LE_CONSTRAINT ){ + int iIdx = (idxNum&FTS4AUX_GE_CONSTRAINT) ? 1 : 0; + pCsr->zStop = sqlite3_mprintf("%s", sqlite3_value_text(apVal[iIdx])); + pCsr->nStop = sqlite3_value_bytes(apVal[iIdx]); + if( pCsr->zStop==0 ) return SQLITE_NOMEM; + } + + rc = sqlite3Fts3SegReaderCursor(pFts3, 0, FTS3_SEGCURSOR_ALL, + pCsr->filter.zTerm, pCsr->filter.nTerm, 0, isScan, &pCsr->csr + ); + if( rc==SQLITE_OK ){ + rc = sqlite3Fts3SegReaderStart(pFts3, &pCsr->csr, &pCsr->filter); + } + + if( rc==SQLITE_OK ) rc = fts3auxNextMethod(pCursor); + return rc; +} + +/* +** xEof - Return true if the cursor is at EOF, or false otherwise. +*/ +static int fts3auxEofMethod(sqlite3_vtab_cursor *pCursor){ + Fts3auxCursor *pCsr = (Fts3auxCursor *)pCursor; + return pCsr->isEof; +} + +/* +** xColumn - Return a column value. +*/ +static int fts3auxColumnMethod( + sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */ + sqlite3_context *pContext, /* Context for sqlite3_result_xxx() calls */ + int iCol /* Index of column to read value from */ +){ + Fts3auxCursor *p = (Fts3auxCursor *)pCursor; + + assert( p->isEof==0 ); + if( iCol==0 ){ /* Column "term" */ + sqlite3_result_text(pContext, p->csr.zTerm, p->csr.nTerm, SQLITE_TRANSIENT); + }else if( iCol==1 ){ /* Column "col" */ + if( p->iCol ){ + sqlite3_result_int(pContext, p->iCol-1); + }else{ + sqlite3_result_text(pContext, "*", -1, SQLITE_STATIC); + } + }else if( iCol==2 ){ /* Column "documents" */ + sqlite3_result_int64(pContext, p->aStat[p->iCol].nDoc); + }else{ /* Column "occurrences" */ + sqlite3_result_int64(pContext, p->aStat[p->iCol].nOcc); + } + + return SQLITE_OK; +} + +/* +** xRowid - Return the current rowid for the cursor. +*/ +static int fts3auxRowidMethod( + sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */ + sqlite_int64 *pRowid /* OUT: Rowid value */ +){ + Fts3auxCursor *pCsr = (Fts3auxCursor *)pCursor; + *pRowid = pCsr->iRowid; + return SQLITE_OK; +} + +/* +** Register the fts3aux module with database connection db. Return SQLITE_OK +** if successful or an error code if sqlite3_create_module() fails. +*/ +SQLITE_PRIVATE int sqlite3Fts3InitAux(sqlite3 *db){ + static const sqlite3_module fts3aux_module = { + 0, /* iVersion */ + fts3auxConnectMethod, /* xCreate */ + fts3auxConnectMethod, /* xConnect */ + fts3auxBestIndexMethod, /* xBestIndex */ + fts3auxDisconnectMethod, /* xDisconnect */ + fts3auxDisconnectMethod, /* xDestroy */ + fts3auxOpenMethod, /* xOpen */ + fts3auxCloseMethod, /* xClose */ + fts3auxFilterMethod, /* xFilter */ + fts3auxNextMethod, /* xNext */ + fts3auxEofMethod, /* xEof */ + fts3auxColumnMethod, /* xColumn */ + fts3auxRowidMethod, /* xRowid */ + 0, /* xUpdate */ + 0, /* xBegin */ + 0, /* xSync */ + 0, /* xCommit */ + 0, /* xRollback */ + 0, /* xFindFunction */ + 0, /* xRename */ + 0, /* xSavepoint */ + 0, /* xRelease */ + 0 /* xRollbackTo */ + }; + int rc; /* Return code */ + + rc = sqlite3_create_module(db, "fts4aux", &fts3aux_module, 0); + return rc; +} + +#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */ + +/************** End of fts3_aux.c ********************************************/ /************** Begin file fts3_expr.c ***************************************/ /* ** 2008 Nov 28 @@ -111802,12 +116992,21 @@ SQLITE_API int sqlite3_fts3_enable_parentheses = 0; #define SQLITE_FTS3_DEFAULT_NEAR_PARAM 10 +/* +** isNot: +** This variable is used by function getNextNode(). When getNextNode() is +** called, it sets ParseContext.isNot to true if the 'next node' is a +** FTSQUERY_PHRASE with a unary "-" attached to it. i.e. "mysql" in the +** FTS3 query "sqlite -mysql". Otherwise, ParseContext.isNot is set to +** zero. +*/ typedef struct ParseContext ParseContext; struct ParseContext { sqlite3_tokenizer *pTokenizer; /* Tokenizer module */ const char **azCol; /* Array of column names for fts3 table */ int nCol; /* Number of entries in azCol[] */ int iDefaultCol; /* Default column to query */ + int isNot; /* True if getNextNode() sees a unary - */ sqlite3_context *pCtx; /* Write error message here */ int nNest; /* Number of nested brackets */ }; @@ -111893,7 +117092,7 @@ static int getNextToken( iEnd++; } if( !sqlite3_fts3_enable_parentheses && iStart>0 && z[iStart-1]=='-' ){ - pRet->pPhrase->isNot = 1; + pParse->isNot = 1; } } nConsumed = iEnd; @@ -111945,36 +117144,55 @@ static int getNextString( char *zTemp = 0; int nTemp = 0; + const int nSpace = sizeof(Fts3Expr) + sizeof(Fts3Phrase); + int nToken = 0; + + /* The final Fts3Expr data structure, including the Fts3Phrase, + ** Fts3PhraseToken structures token buffers are all stored as a single + ** allocation so that the expression can be freed with a single call to + ** sqlite3_free(). Setting this up requires a two pass approach. + ** + ** The first pass, in the block below, uses a tokenizer cursor to iterate + ** through the tokens in the expression. This pass uses fts3ReallocOrFree() + ** to assemble data in two dynamic buffers: + ** + ** Buffer p: Points to the Fts3Expr structure, followed by the Fts3Phrase + ** structure, followed by the array of Fts3PhraseToken + ** structures. This pass only populates the Fts3PhraseToken array. + ** + ** Buffer zTemp: Contains copies of all tokens. + ** + ** The second pass, in the block that begins "if( rc==SQLITE_DONE )" below, + ** appends buffer zTemp to buffer p, and fills in the Fts3Expr and Fts3Phrase + ** structures. + */ rc = pModule->xOpen(pTokenizer, zInput, nInput, &pCursor); if( rc==SQLITE_OK ){ int ii; pCursor->pTokenizer = pTokenizer; for(ii=0; rc==SQLITE_OK; ii++){ - const char *zToken; - int nToken, iBegin, iEnd, iPos; - rc = pModule->xNext(pCursor, &zToken, &nToken, &iBegin, &iEnd, &iPos); + const char *zByte; + int nByte, iBegin, iEnd, iPos; + rc = pModule->xNext(pCursor, &zByte, &nByte, &iBegin, &iEnd, &iPos); if( rc==SQLITE_OK ){ - int nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase); - p = fts3ReallocOrFree(p, nByte+ii*sizeof(Fts3PhraseToken)); - zTemp = fts3ReallocOrFree(zTemp, nTemp + nToken); - if( !p || !zTemp ){ - goto no_mem; - } - if( ii==0 ){ - memset(p, 0, nByte); - p->pPhrase = (Fts3Phrase *)&p[1]; - } - p->pPhrase = (Fts3Phrase *)&p[1]; - memset(&p->pPhrase->aToken[ii], 0, sizeof(Fts3PhraseToken)); - p->pPhrase->nToken = ii+1; - p->pPhrase->aToken[ii].n = nToken; - memcpy(&zTemp[nTemp], zToken, nToken); - nTemp += nToken; - if( iEnd<nInput && zInput[iEnd]=='*' ){ - p->pPhrase->aToken[ii].isPrefix = 1; - }else{ - p->pPhrase->aToken[ii].isPrefix = 0; - } + Fts3PhraseToken *pToken; + + p = fts3ReallocOrFree(p, nSpace + ii*sizeof(Fts3PhraseToken)); + if( !p ) goto no_mem; + + zTemp = fts3ReallocOrFree(zTemp, nTemp + nByte); + if( !zTemp ) goto no_mem; + + assert( nToken==ii ); + pToken = &((Fts3Phrase *)(&p[1]))->aToken[ii]; + memset(pToken, 0, sizeof(Fts3PhraseToken)); + + memcpy(&zTemp[nTemp], zByte, nByte); + nTemp += nByte; + + pToken->n = nByte; + pToken->isPrefix = (iEnd<nInput && zInput[iEnd]=='*'); + nToken = ii+1; } } @@ -111984,28 +117202,24 @@ static int getNextString( if( rc==SQLITE_DONE ){ int jj; - char *zNew = NULL; - int nNew = 0; - int nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase); - nByte += (p?(p->pPhrase->nToken-1):0) * sizeof(Fts3PhraseToken); - p = fts3ReallocOrFree(p, nByte + nTemp); - if( !p ){ - goto no_mem; - } - if( zTemp ){ - zNew = &(((char *)p)[nByte]); - memcpy(zNew, zTemp, nTemp); - }else{ - memset(p, 0, nByte+nTemp); - } + char *zBuf = 0; + + p = fts3ReallocOrFree(p, nSpace + nToken*sizeof(Fts3PhraseToken) + nTemp); + if( !p ) goto no_mem; + memset(p, 0, (char *)&(((Fts3Phrase *)&p[1])->aToken[0])-(char *)p); + p->eType = FTSQUERY_PHRASE; p->pPhrase = (Fts3Phrase *)&p[1]; + p->pPhrase->iColumn = pParse->iDefaultCol; + p->pPhrase->nToken = nToken; + + zBuf = (char *)&p->pPhrase->aToken[nToken]; + memcpy(zBuf, zTemp, nTemp); + sqlite3_free(zTemp); + for(jj=0; jj<p->pPhrase->nToken; jj++){ - p->pPhrase->aToken[jj].z = &zNew[nNew]; - nNew += p->pPhrase->aToken[jj].n; + p->pPhrase->aToken[jj].z = zBuf; + zBuf += p->pPhrase->aToken[jj].n; } - sqlite3_free(zTemp); - p->eType = FTSQUERY_PHRASE; - p->pPhrase->iColumn = pParse->iDefaultCol; rc = SQLITE_OK; } @@ -112062,6 +117276,8 @@ static int getNextNode( const char *zInput = z; int nInput = n; + pParse->isNot = 0; + /* Skip over any whitespace before checking for a keyword, an open or ** close bracket, or a quoted string. */ @@ -112280,7 +117496,7 @@ static int fts3ExprParse( int isPhrase; if( !sqlite3_fts3_enable_parentheses - && p->eType==FTSQUERY_PHRASE && p->pPhrase->isNot + && p->eType==FTSQUERY_PHRASE && pParse->isNot ){ /* Create an implicit NOT operator. */ Fts3Expr *pNot = fts3MallocZero(sizeof(Fts3Expr)); @@ -112298,7 +117514,6 @@ static int fts3ExprParse( p = pPrev; }else{ int eType = p->eType; - assert( eType!=FTSQUERY_PHRASE || !p->pPhrase->isNot ); isPhrase = (eType==FTSQUERY_PHRASE || p->pLeft); /* The isRequirePhrase variable is set to true if a phrase or @@ -112461,9 +117676,11 @@ SQLITE_PRIVATE int sqlite3Fts3ExprParse( */ SQLITE_PRIVATE void sqlite3Fts3ExprFree(Fts3Expr *p){ if( p ){ + assert( p->eType==FTSQUERY_PHRASE || p->pPhrase==0 ); sqlite3Fts3ExprFree(p->pLeft); sqlite3Fts3ExprFree(p->pRight); - sqlite3_free(p->aDoclist); + sqlite3Fts3EvalPhraseCleanup(p->pPhrase); + sqlite3_free(p->aMI); sqlite3_free(p); } } @@ -112520,7 +117737,7 @@ static char *exprToString(Fts3Expr *pExpr, char *zBuf){ Fts3Phrase *pPhrase = pExpr->pPhrase; int i; zBuf = sqlite3_mprintf( - "%zPHRASE %d %d", zBuf, pPhrase->iColumn, pPhrase->isNot); + "%zPHRASE %d 0", zBuf, pPhrase->iColumn); for(i=0; zBuf && i<pPhrase->nToken; i++){ zBuf = sqlite3_mprintf("%z %.*s%s", zBuf, pPhrase->aToken[i].n, pPhrase->aToken[i].z, @@ -113067,7 +118284,6 @@ SQLITE_PRIVATE void *sqlite3Fts3HashInsert( - /* ** Class derived from sqlite3_tokenizer */ @@ -113707,12 +118923,12 @@ SQLITE_PRIVATE void sqlite3Fts3PorterTokenizerModule( ** * The FTS3 module is being built into the core of ** SQLite (in which case SQLITE_ENABLE_FTS3 is defined). */ -#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) - #ifndef SQLITE_CORE SQLITE_EXTENSION_INIT1 #endif +#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) + /* ** Implementation of the SQL scalar function for accessing the underlying @@ -113836,7 +119052,7 @@ SQLITE_PRIVATE int sqlite3Fts3InitTokenizer( ){ int rc; char *z = (char *)zArg; - int n; + int n = 0; char *zCopy; char *zEnd; /* Pointer to nul-term of zCopy */ sqlite3_tokenizer_module *m; @@ -114201,7 +119417,6 @@ SQLITE_PRIVATE int sqlite3Fts3InitHashTable( - typedef struct simple_tokenizer { sqlite3_tokenizer base; char delim[128]; /* flag ASCII delimiters */ @@ -114438,14 +119653,40 @@ SQLITE_PRIVATE void sqlite3Fts3SimpleTokenizerModule( */ #define FTS3_NODE_PADDING (FTS3_VARINT_MAX*2) +/* +** Under certain circumstances, b-tree nodes (doclists) can be loaded into +** memory incrementally instead of all at once. This can be a big performance +** win (reduced IO and CPU) if SQLite stops calling the virtual table xNext() +** method before retrieving all query results (as may happen, for example, +** if a query has a LIMIT clause). +** +** Incremental loading is used for b-tree nodes FTS3_NODE_CHUNK_THRESHOLD +** bytes and larger. Nodes are loaded in chunks of FTS3_NODE_CHUNKSIZE bytes. +** The code is written so that the hard lower-limit for each of these values +** is 1. Clearly such small values would be inefficient, but can be useful +** for testing purposes. +** +** If this module is built with SQLITE_TEST defined, these constants may +** be overridden at runtime for testing purposes. File fts3_test.c contains +** a Tcl interface to read and write the values. +*/ +#ifdef SQLITE_TEST +int test_fts3_node_chunksize = (4*1024); +int test_fts3_node_chunk_threshold = (4*1024)*4; +# define FTS3_NODE_CHUNKSIZE test_fts3_node_chunksize +# define FTS3_NODE_CHUNK_THRESHOLD test_fts3_node_chunk_threshold +#else +# define FTS3_NODE_CHUNKSIZE (4*1024) +# define FTS3_NODE_CHUNK_THRESHOLD (FTS3_NODE_CHUNKSIZE*4) +#endif + typedef struct PendingList PendingList; typedef struct SegmentNode SegmentNode; typedef struct SegmentWriter SegmentWriter; /* -** Data structure used while accumulating terms in the pending-terms hash -** table. The hash table entry maps from term (a string) to a malloc'd -** instance of this structure. +** An instance of the following data structure is used to build doclists +** incrementally. See function fts3PendingListAppend() for details. */ struct PendingList { int nData; @@ -114476,7 +119717,6 @@ struct Fts3DeferredToken { ** ** sqlite3Fts3SegReaderNew() ** sqlite3Fts3SegReaderFree() -** sqlite3Fts3SegReaderCost() ** sqlite3Fts3SegReaderIterate() ** ** Methods used to manipulate Fts3SegReader structures: @@ -114495,6 +119735,9 @@ struct Fts3SegReader { char *aNode; /* Pointer to node data (or NULL) */ int nNode; /* Size of buffer at aNode (or 0) */ + int nPopulate; /* If >0, bytes of buffer aNode[] loaded */ + sqlite3_blob *pBlob; /* If not NULL, blob handle to read node */ + Fts3HashElem **ppNextElem; /* Variables set by fts3SegReaderNext(). These may be read directly @@ -114508,8 +119751,11 @@ struct Fts3SegReader { char *aDoclist; /* Pointer to doclist of current entry */ int nDoclist; /* Size of doclist in current entry */ - /* The following variables are used to iterate through the current doclist */ + /* The following variables are used by fts3SegReaderNextDocid() to iterate + ** through the current doclist (aDoclist/nDoclist). + */ char *pOffsetList; + int nOffsetList; /* For descending pending seg-readers only */ sqlite3_int64 iDocid; }; @@ -114547,6 +119793,14 @@ struct SegmentWriter { ** fts3NodeAddTerm() ** fts3NodeWrite() ** fts3NodeFree() +** +** When a b+tree is written to the database (either as a result of a merge +** or the pending-terms table being flushed), leaves are written into the +** database file as soon as they are completely populated. The interior of +** the tree is assembled in memory and written out only once all leaves have +** been populated and stored. This is Ok, as the b+-tree fanout is usually +** very large, meaning that the interior of the tree consumes relatively +** little memory. */ struct SegmentNode { SegmentNode *pParent; /* Parent node (or NULL for root node) */ @@ -114577,10 +119831,10 @@ struct SegmentNode { #define SQL_NEXT_SEGMENTS_ID 10 #define SQL_INSERT_SEGDIR 11 #define SQL_SELECT_LEVEL 12 -#define SQL_SELECT_ALL_LEVEL 13 +#define SQL_SELECT_LEVEL_RANGE 13 #define SQL_SELECT_LEVEL_COUNT 14 -#define SQL_SELECT_SEGDIR_COUNT_MAX 15 -#define SQL_DELETE_SEGDIR_BY_LEVEL 16 +#define SQL_SELECT_SEGDIR_MAX_LEVEL 15 +#define SQL_DELETE_SEGDIR_LEVEL 16 #define SQL_DELETE_SEGMENTS_RANGE 17 #define SQL_CONTENT_INSERT 18 #define SQL_DELETE_DOCSIZE 19 @@ -114589,6 +119843,11 @@ struct SegmentNode { #define SQL_SELECT_DOCTOTAL 22 #define SQL_REPLACE_DOCTOTAL 23 +#define SQL_SELECT_ALL_PREFIX_LEVEL 24 +#define SQL_DELETE_ALL_TERMS_SEGDIR 25 + +#define SQL_DELETE_SEGDIR_RANGE 26 + /* ** This function is used to obtain an SQLite prepared statement handle ** for the statement identified by the second argument. If successful, @@ -114614,7 +119873,7 @@ static int fts3SqlStmt( /* 4 */ "DELETE FROM %Q.'%q_segdir'", /* 5 */ "DELETE FROM %Q.'%q_docsize'", /* 6 */ "DELETE FROM %Q.'%q_stat'", -/* 7 */ "SELECT * FROM %Q.'%q_content' WHERE rowid=?", +/* 7 */ "SELECT %s FROM %Q.'%q_content' AS x WHERE rowid=?", /* 8 */ "SELECT (SELECT max(idx) FROM %Q.'%q_segdir' WHERE level = ?) + 1", /* 9 */ "INSERT INTO %Q.'%q_segments'(blockid, block) VALUES(?, ?)", /* 10 */ "SELECT coalesce((SELECT max(blockid) FROM %Q.'%q_segments') + 1, 1)", @@ -114624,19 +119883,25 @@ static int fts3SqlStmt( /* 12 */ "SELECT idx, start_block, leaves_end_block, end_block, root " "FROM %Q.'%q_segdir' WHERE level = ? ORDER BY idx ASC", /* 13 */ "SELECT idx, start_block, leaves_end_block, end_block, root " - "FROM %Q.'%q_segdir' ORDER BY level DESC, idx ASC", + "FROM %Q.'%q_segdir' WHERE level BETWEEN ? AND ?" + "ORDER BY level DESC, idx ASC", /* 14 */ "SELECT count(*) FROM %Q.'%q_segdir' WHERE level = ?", -/* 15 */ "SELECT count(*), max(level) FROM %Q.'%q_segdir'", +/* 15 */ "SELECT max(level) FROM %Q.'%q_segdir' WHERE level BETWEEN ? AND ?", /* 16 */ "DELETE FROM %Q.'%q_segdir' WHERE level = ?", /* 17 */ "DELETE FROM %Q.'%q_segments' WHERE blockid BETWEEN ? AND ?", -/* 18 */ "INSERT INTO %Q.'%q_content' VALUES(%z)", +/* 18 */ "INSERT INTO %Q.'%q_content' VALUES(%s)", /* 19 */ "DELETE FROM %Q.'%q_docsize' WHERE docid = ?", /* 20 */ "REPLACE INTO %Q.'%q_docsize' VALUES(?,?)", /* 21 */ "SELECT size FROM %Q.'%q_docsize' WHERE docid=?", /* 22 */ "SELECT value FROM %Q.'%q_stat' WHERE id=0", /* 23 */ "REPLACE INTO %Q.'%q_stat' VALUES(0,?)", +/* 24 */ "", +/* 25 */ "", + +/* 26 */ "DELETE FROM %Q.'%q_segdir' WHERE level BETWEEN ? AND ?", + }; int rc = SQLITE_OK; sqlite3_stmt *pStmt; @@ -114648,20 +119913,9 @@ static int fts3SqlStmt( if( !pStmt ){ char *zSql; if( eStmt==SQL_CONTENT_INSERT ){ - int i; /* Iterator variable */ - char *zVarlist; /* The "?, ?, ..." string */ - zVarlist = (char *)sqlite3_malloc(2*p->nColumn+2); - if( !zVarlist ){ - *pp = 0; - return SQLITE_NOMEM; - } - zVarlist[0] = '?'; - zVarlist[p->nColumn*2+1] = '\0'; - for(i=1; i<=p->nColumn; i++){ - zVarlist[i*2-1] = ','; - zVarlist[i*2] = '?'; - } - zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName, zVarlist); + zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName, p->zWriteExprlist); + }else if( eStmt==SQL_SELECT_CONTENT_BY_ROWID ){ + zSql = sqlite3_mprintf(azSql[eStmt], p->zReadExprlist, p->zDb, p->zName); }else{ zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName); } @@ -114702,9 +119956,9 @@ static int fts3SelectDocsize( sqlite3_bind_int64(pStmt, 1, iDocid); } rc = sqlite3_step(pStmt); - if( rc!=SQLITE_ROW ){ + if( rc!=SQLITE_ROW || sqlite3_column_type(pStmt, 0)!=SQLITE_BLOB ){ rc = sqlite3_reset(pStmt); - if( rc==SQLITE_OK ) rc = SQLITE_CORRUPT; + if( rc==SQLITE_OK ) rc = SQLITE_CORRUPT_VTAB; pStmt = 0; }else{ rc = SQLITE_OK; @@ -114803,8 +120057,35 @@ SQLITE_PRIVATE int sqlite3Fts3ReadLock(Fts3Table *p){ ** 3: end_block ** 4: root */ -SQLITE_PRIVATE int sqlite3Fts3AllSegdirs(Fts3Table *p, sqlite3_stmt **ppStmt){ - return fts3SqlStmt(p, SQL_SELECT_ALL_LEVEL, ppStmt, 0); +SQLITE_PRIVATE int sqlite3Fts3AllSegdirs( + Fts3Table *p, /* FTS3 table */ + int iIndex, /* Index for p->aIndex[] */ + int iLevel, /* Level to select */ + sqlite3_stmt **ppStmt /* OUT: Compiled statement */ +){ + int rc; + sqlite3_stmt *pStmt = 0; + + assert( iLevel==FTS3_SEGCURSOR_ALL || iLevel>=0 ); + assert( iLevel<FTS3_SEGDIR_MAXLEVEL ); + assert( iIndex>=0 && iIndex<p->nIndex ); + + if( iLevel<0 ){ + /* "SELECT * FROM %_segdir WHERE level BETWEEN ? AND ? ORDER BY ..." */ + rc = fts3SqlStmt(p, SQL_SELECT_LEVEL_RANGE, &pStmt, 0); + if( rc==SQLITE_OK ){ + sqlite3_bind_int(pStmt, 1, iIndex*FTS3_SEGDIR_MAXLEVEL); + sqlite3_bind_int(pStmt, 2, (iIndex+1)*FTS3_SEGDIR_MAXLEVEL-1); + } + }else{ + /* "SELECT * FROM %_segdir WHERE level = ? ORDER BY ..." */ + rc = fts3SqlStmt(p, SQL_SELECT_LEVEL, &pStmt, 0); + if( rc==SQLITE_OK ){ + sqlite3_bind_int(pStmt, 1, iLevel+iIndex*FTS3_SEGDIR_MAXLEVEL); + } + } + *ppStmt = pStmt; + return rc; } @@ -114917,6 +120198,47 @@ static int fts3PendingListAppend( } /* +** Free a PendingList object allocated by fts3PendingListAppend(). +*/ +static void fts3PendingListDelete(PendingList *pList){ + sqlite3_free(pList); +} + +/* +** Add an entry to one of the pending-terms hash tables. +*/ +static int fts3PendingTermsAddOne( + Fts3Table *p, + int iCol, + int iPos, + Fts3Hash *pHash, /* Pending terms hash table to add entry to */ + const char *zToken, + int nToken +){ + PendingList *pList; + int rc = SQLITE_OK; + + pList = (PendingList *)fts3HashFind(pHash, zToken, nToken); + if( pList ){ + p->nPendingData -= (pList->nData + nToken + sizeof(Fts3HashElem)); + } + if( fts3PendingListAppend(&pList, p->iPrevDocid, iCol, iPos, &rc) ){ + if( pList==fts3HashInsert(pHash, zToken, nToken, pList) ){ + /* Malloc failed while inserting the new entry. This can only + ** happen if there was no previous entry for this token. + */ + assert( 0==fts3HashFind(pHash, zToken, nToken) ); + sqlite3_free(pList); + rc = SQLITE_NOMEM; + } + } + if( rc==SQLITE_OK ){ + p->nPendingData += (pList->nData + nToken + sizeof(Fts3HashElem)); + } + return rc; +} + +/* ** Tokenize the nul-terminated string zText and add all tokens to the ** pending-terms hash-table. The docid used is that currently stored in ** p->iPrevDocid, and the column is specified by argument iCol. @@ -114946,6 +120268,14 @@ static int fts3PendingTermsAdd( assert( pTokenizer && pModule ); + /* If the user has inserted a NULL value, this function may be called with + ** zText==0. In this case, add zero token entries to the hash table and + ** return early. */ + if( zText==0 ){ + *pnWord = 0; + return SQLITE_OK; + } + rc = pModule->xOpen(pTokenizer, zText, -1, &pCsr); if( rc!=SQLITE_OK ){ return rc; @@ -114956,8 +120286,7 @@ static int fts3PendingTermsAdd( while( SQLITE_OK==rc && SQLITE_OK==(rc = xNext(pCsr, &zToken, &nToken, &iStart, &iEnd, &iPos)) ){ - PendingList *pList; - + int i; if( iPos>=nWord ) nWord = iPos+1; /* Positions cannot be negative; we use -1 as a terminator internally. @@ -114968,22 +120297,19 @@ static int fts3PendingTermsAdd( break; } - pList = (PendingList *)fts3HashFind(&p->pendingTerms, zToken, nToken); - if( pList ){ - p->nPendingData -= (pList->nData + nToken + sizeof(Fts3HashElem)); - } - if( fts3PendingListAppend(&pList, p->iPrevDocid, iCol, iPos, &rc) ){ - if( pList==fts3HashInsert(&p->pendingTerms, zToken, nToken, pList) ){ - /* Malloc failed while inserting the new entry. This can only - ** happen if there was no previous entry for this token. - */ - assert( 0==fts3HashFind(&p->pendingTerms, zToken, nToken) ); - sqlite3_free(pList); - rc = SQLITE_NOMEM; - } - } - if( rc==SQLITE_OK ){ - p->nPendingData += (pList->nData + nToken + sizeof(Fts3HashElem)); + /* Add the term to the terms index */ + rc = fts3PendingTermsAddOne( + p, iCol, iPos, &p->aIndex[0].hPending, zToken, nToken + ); + + /* Add the term to each of the prefix indexes that it is not too + ** short for. */ + for(i=1; rc==SQLITE_OK && i<p->nIndex; i++){ + struct Fts3Index *pIndex = &p->aIndex[i]; + if( nToken<pIndex->nPrefix ) continue; + rc = fts3PendingTermsAddOne( + p, iCol, iPos, &pIndex->hPending, zToken, pIndex->nPrefix + ); } } @@ -115013,14 +120339,19 @@ static int fts3PendingTermsDocid(Fts3Table *p, sqlite_int64 iDocid){ } /* -** Discard the contents of the pending-terms hash table. +** Discard the contents of the pending-terms hash tables. */ SQLITE_PRIVATE void sqlite3Fts3PendingTermsClear(Fts3Table *p){ - Fts3HashElem *pElem; - for(pElem=fts3HashFirst(&p->pendingTerms); pElem; pElem=fts3HashNext(pElem)){ - sqlite3_free(fts3HashData(pElem)); + int i; + for(i=0; i<p->nIndex; i++){ + Fts3HashElem *pElem; + Fts3Hash *pHash = &p->aIndex[i].hPending; + for(pElem=fts3HashFirst(pHash); pElem; pElem=fts3HashNext(pElem)){ + PendingList *pList = (PendingList *)fts3HashData(pElem); + fts3PendingListDelete(pList); + } + fts3HashClear(pHash); } - fts3HashClear(&p->pendingTerms); p->nPendingData = 0; } @@ -115036,11 +120367,9 @@ static int fts3InsertTerms(Fts3Table *p, sqlite3_value **apVal, u32 *aSz){ int i; /* Iterator variable */ for(i=2; i<p->nColumn+2; i++){ const char *zText = (const char *)sqlite3_value_text(apVal[i]); - if( zText ){ - int rc = fts3PendingTermsAdd(p, zText, i-2, &aSz[i-2]); - if( rc!=SQLITE_OK ){ - return rc; - } + int rc = fts3PendingTermsAdd(p, zText, i-2, &aSz[i-2]); + if( rc!=SQLITE_OK ){ + return rc; } aSz[p->nColumn] += sqlite3_value_bytes(apVal[i]); } @@ -115145,14 +120474,14 @@ static int fts3DeleteAll(Fts3Table *p){ static void fts3DeleteTerms( int *pRC, /* Result code */ Fts3Table *p, /* The FTS table to delete from */ - sqlite3_value **apVal, /* apVal[] contains the docid to be deleted */ + sqlite3_value *pRowid, /* The docid to be deleted */ u32 *aSz /* Sizes of deleted document written here */ ){ int rc; sqlite3_stmt *pSelect; if( *pRC ) return; - rc = fts3SqlStmt(p, SQL_SELECT_CONTENT_BY_ROWID, &pSelect, apVal); + rc = fts3SqlStmt(p, SQL_SELECT_CONTENT_BY_ROWID, &pSelect, &pRowid); if( rc==SQLITE_OK ){ if( SQLITE_ROW==sqlite3_step(pSelect) ){ int i; @@ -115178,7 +120507,7 @@ static void fts3DeleteTerms( ** Forward declaration to account for the circular dependency between ** functions fts3SegmentMerge() and fts3AllocateSegdirIdx(). */ -static int fts3SegmentMerge(Fts3Table *, int); +static int fts3SegmentMerge(Fts3Table *, int, int); /* ** This function allocates a new level iLevel index in the segdir table. @@ -115195,7 +120524,12 @@ static int fts3SegmentMerge(Fts3Table *, int); ** If successful, *piIdx is set to the allocated index slot and SQLITE_OK ** returned. Otherwise, an SQLite error code is returned. */ -static int fts3AllocateSegdirIdx(Fts3Table *p, int iLevel, int *piIdx){ +static int fts3AllocateSegdirIdx( + Fts3Table *p, + int iIndex, /* Index for p->aIndex */ + int iLevel, + int *piIdx +){ int rc; /* Return Code */ sqlite3_stmt *pNextIdx; /* Query for next idx at level iLevel */ int iNext = 0; /* Result of query pNextIdx */ @@ -115203,7 +120537,7 @@ static int fts3AllocateSegdirIdx(Fts3Table *p, int iLevel, int *piIdx){ /* Set variable iNext to the next available segdir index at level iLevel. */ rc = fts3SqlStmt(p, SQL_NEXT_SEGMENT_INDEX, &pNextIdx, 0); if( rc==SQLITE_OK ){ - sqlite3_bind_int(pNextIdx, 1, iLevel); + sqlite3_bind_int(pNextIdx, 1, iIndex*FTS3_SEGDIR_MAXLEVEL + iLevel); if( SQLITE_ROW==sqlite3_step(pNextIdx) ){ iNext = sqlite3_column_int(pNextIdx, 0); } @@ -115217,7 +120551,7 @@ static int fts3AllocateSegdirIdx(Fts3Table *p, int iLevel, int *piIdx){ ** if iNext is less than FTS3_MERGE_COUNT, allocate index iNext. */ if( iNext>=FTS3_MERGE_COUNT ){ - rc = fts3SegmentMerge(p, iLevel); + rc = fts3SegmentMerge(p, iIndex, iLevel); *piIdx = 0; }else{ *piIdx = iNext; @@ -115258,7 +120592,8 @@ SQLITE_PRIVATE int sqlite3Fts3ReadBlock( Fts3Table *p, /* FTS3 table handle */ sqlite3_int64 iBlockid, /* Access the row with blockid=$iBlockid */ char **paBlob, /* OUT: Blob data in malloc'd buffer */ - int *pnBlob /* OUT: Size of blob data */ + int *pnBlob, /* OUT: Size of blob data */ + int *pnLoad /* OUT: Bytes actually loaded */ ){ int rc; /* Return code */ @@ -115279,11 +120614,16 @@ SQLITE_PRIVATE int sqlite3Fts3ReadBlock( if( rc==SQLITE_OK ){ int nByte = sqlite3_blob_bytes(p->pSegments); + *pnBlob = nByte; if( paBlob ){ char *aByte = sqlite3_malloc(nByte + FTS3_NODE_PADDING); if( !aByte ){ rc = SQLITE_NOMEM; }else{ + if( pnLoad && nByte>(FTS3_NODE_CHUNK_THRESHOLD) ){ + nByte = FTS3_NODE_CHUNKSIZE; + *pnLoad = nByte; + } rc = sqlite3_blob_read(p->pSegments, aByte, nByte, 0); memset(&aByte[nByte], 0, FTS3_NODE_PADDING); if( rc!=SQLITE_OK ){ @@ -115293,7 +120633,6 @@ SQLITE_PRIVATE int sqlite3Fts3ReadBlock( } *paBlob = aByte; } - *pnBlob = nByte; } return rc; @@ -115307,13 +120646,55 @@ SQLITE_PRIVATE void sqlite3Fts3SegmentsClose(Fts3Table *p){ sqlite3_blob_close(p->pSegments); p->pSegments = 0; } + +static int fts3SegReaderIncrRead(Fts3SegReader *pReader){ + int nRead; /* Number of bytes to read */ + int rc; /* Return code */ + + nRead = MIN(pReader->nNode - pReader->nPopulate, FTS3_NODE_CHUNKSIZE); + rc = sqlite3_blob_read( + pReader->pBlob, + &pReader->aNode[pReader->nPopulate], + nRead, + pReader->nPopulate + ); + + if( rc==SQLITE_OK ){ + pReader->nPopulate += nRead; + memset(&pReader->aNode[pReader->nPopulate], 0, FTS3_NODE_PADDING); + if( pReader->nPopulate==pReader->nNode ){ + sqlite3_blob_close(pReader->pBlob); + pReader->pBlob = 0; + pReader->nPopulate = 0; + } + } + return rc; +} + +static int fts3SegReaderRequire(Fts3SegReader *pReader, char *pFrom, int nByte){ + int rc = SQLITE_OK; + assert( !pReader->pBlob + || (pFrom>=pReader->aNode && pFrom<&pReader->aNode[pReader->nNode]) + ); + while( pReader->pBlob && rc==SQLITE_OK + && (pFrom - pReader->aNode + nByte)>pReader->nPopulate + ){ + rc = fts3SegReaderIncrRead(pReader); + } + return rc; +} /* ** Move the iterator passed as the first argument to the next term in the ** segment. If successful, SQLITE_OK is returned. If there is no next term, ** SQLITE_DONE. Otherwise, an SQLite error code. */ -static int fts3SegReaderNext(Fts3Table *p, Fts3SegReader *pReader){ +static int fts3SegReaderNext( + Fts3Table *p, + Fts3SegReader *pReader, + int bIncr +){ + int rc; /* Return code of various sub-routines */ char *pNext; /* Cursor variable */ int nPrefix; /* Number of bytes in term prefix */ int nSuffix; /* Number of bytes in term suffix */ @@ -115325,7 +120706,6 @@ static int fts3SegReaderNext(Fts3Table *p, Fts3SegReader *pReader){ } if( !pNext || pNext>=&pReader->aNode[pReader->nNode] ){ - int rc; /* Return code from Fts3ReadBlock() */ if( fts3SegReaderIsPending(pReader) ){ Fts3HashElem *pElem = *(pReader->ppNextElem); @@ -115345,6 +120725,8 @@ static int fts3SegReaderNext(Fts3Table *p, Fts3SegReader *pReader){ if( !fts3SegReaderIsRootOnly(pReader) ){ sqlite3_free(pReader->aNode); + sqlite3_blob_close(pReader->pBlob); + pReader->pBlob = 0; } pReader->aNode = 0; @@ -115356,21 +120738,31 @@ static int fts3SegReaderNext(Fts3Table *p, Fts3SegReader *pReader){ } rc = sqlite3Fts3ReadBlock( - p, ++pReader->iCurrentBlock, &pReader->aNode, &pReader->nNode + p, ++pReader->iCurrentBlock, &pReader->aNode, &pReader->nNode, + (bIncr ? &pReader->nPopulate : 0) ); if( rc!=SQLITE_OK ) return rc; + assert( pReader->pBlob==0 ); + if( bIncr && pReader->nPopulate<pReader->nNode ){ + pReader->pBlob = p->pSegments; + p->pSegments = 0; + } pNext = pReader->aNode; } + + assert( !fts3SegReaderIsPending(pReader) ); + + rc = fts3SegReaderRequire(pReader, pNext, FTS3_VARINT_MAX*2); + if( rc!=SQLITE_OK ) return rc; /* Because of the FTS3_NODE_PADDING bytes of padding, the following is - ** safe (no risk of overread) even if the node data is corrupted. - */ + ** safe (no risk of overread) even if the node data is corrupted. */ pNext += sqlite3Fts3GetVarint32(pNext, &nPrefix); pNext += sqlite3Fts3GetVarint32(pNext, &nSuffix); if( nPrefix<0 || nSuffix<=0 || &pNext[nSuffix]>&pReader->aNode[pReader->nNode] ){ - return SQLITE_CORRUPT; + return SQLITE_CORRUPT_VTAB; } if( nPrefix+nSuffix>pReader->nTermAlloc ){ @@ -115382,6 +120774,10 @@ static int fts3SegReaderNext(Fts3Table *p, Fts3SegReader *pReader){ pReader->zTerm = zNew; pReader->nTermAlloc = nNew; } + + rc = fts3SegReaderRequire(pReader, pNext, nSuffix+FTS3_VARINT_MAX); + if( rc!=SQLITE_OK ) return rc; + memcpy(&pReader->zTerm[nPrefix], pNext, nSuffix); pReader->nTerm = nPrefix+nSuffix; pNext += nSuffix; @@ -115394,9 +120790,9 @@ static int fts3SegReaderNext(Fts3Table *p, Fts3SegReader *pReader){ ** of these statements is untrue, then the data structure is corrupt. */ if( &pReader->aDoclist[pReader->nDoclist]>&pReader->aNode[pReader->nNode] - || pReader->aDoclist[pReader->nDoclist-1] + || (pReader->nPopulate==0 && pReader->aDoclist[pReader->nDoclist-1]) ){ - return SQLITE_CORRUPT; + return SQLITE_CORRUPT_VTAB; } return SQLITE_OK; } @@ -115405,12 +120801,26 @@ static int fts3SegReaderNext(Fts3Table *p, Fts3SegReader *pReader){ ** Set the SegReader to point to the first docid in the doclist associated ** with the current term. */ -static void fts3SegReaderFirstDocid(Fts3SegReader *pReader){ - int n; +static int fts3SegReaderFirstDocid(Fts3Table *pTab, Fts3SegReader *pReader){ + int rc = SQLITE_OK; assert( pReader->aDoclist ); assert( !pReader->pOffsetList ); - n = sqlite3Fts3GetVarint(pReader->aDoclist, &pReader->iDocid); - pReader->pOffsetList = &pReader->aDoclist[n]; + if( pTab->bDescIdx && fts3SegReaderIsPending(pReader) ){ + u8 bEof = 0; + pReader->iDocid = 0; + pReader->nOffsetList = 0; + sqlite3Fts3DoclistPrev(0, + pReader->aDoclist, pReader->nDoclist, &pReader->pOffsetList, + &pReader->iDocid, &pReader->nOffsetList, &bEof + ); + }else{ + rc = fts3SegReaderRequire(pReader, pReader->aDoclist, FTS3_VARINT_MAX); + if( rc==SQLITE_OK ){ + int n = sqlite3Fts3GetVarint(pReader->aDoclist, &pReader->iDocid); + pReader->pOffsetList = &pReader->aDoclist[n]; + } + } + return rc; } /* @@ -115423,122 +120833,125 @@ static void fts3SegReaderFirstDocid(Fts3SegReader *pReader){ ** *pnOffsetList is set to the length of the set of column-offset ** lists, not including the nul-terminator byte. For example: */ -static void fts3SegReaderNextDocid( - Fts3SegReader *pReader, - char **ppOffsetList, - int *pnOffsetList +static int fts3SegReaderNextDocid( + Fts3Table *pTab, + Fts3SegReader *pReader, /* Reader to advance to next docid */ + char **ppOffsetList, /* OUT: Pointer to current position-list */ + int *pnOffsetList /* OUT: Length of *ppOffsetList in bytes */ ){ + int rc = SQLITE_OK; char *p = pReader->pOffsetList; char c = 0; - /* Pointer p currently points at the first byte of an offset list. The - ** following two lines advance it to point one byte past the end of - ** the same offset list. - */ - while( *p | c ) c = *p++ & 0x80; - p++; - - /* If required, populate the output variables with a pointer to and the - ** size of the previous offset-list. - */ - if( ppOffsetList ){ - *ppOffsetList = pReader->pOffsetList; - *pnOffsetList = (int)(p - pReader->pOffsetList - 1); - } + assert( p ); - /* If there are no more entries in the doclist, set pOffsetList to - ** NULL. Otherwise, set Fts3SegReader.iDocid to the next docid and - ** Fts3SegReader.pOffsetList to point to the next offset list before - ** returning. - */ - if( p>=&pReader->aDoclist[pReader->nDoclist] ){ - pReader->pOffsetList = 0; + if( pTab->bDescIdx && fts3SegReaderIsPending(pReader) ){ + /* A pending-terms seg-reader for an FTS4 table that uses order=desc. + ** Pending-terms doclists are always built up in ascending order, so + ** we have to iterate through them backwards here. */ + u8 bEof = 0; + if( ppOffsetList ){ + *ppOffsetList = pReader->pOffsetList; + *pnOffsetList = pReader->nOffsetList - 1; + } + sqlite3Fts3DoclistPrev(0, + pReader->aDoclist, pReader->nDoclist, &p, &pReader->iDocid, + &pReader->nOffsetList, &bEof + ); + if( bEof ){ + pReader->pOffsetList = 0; + }else{ + pReader->pOffsetList = p; + } }else{ - sqlite3_int64 iDelta; - pReader->pOffsetList = p + sqlite3Fts3GetVarint(p, &iDelta); - pReader->iDocid += iDelta; + char *pEnd = &pReader->aDoclist[pReader->nDoclist]; + + /* Pointer p currently points at the first byte of an offset list. The + ** following block advances it to point one byte past the end of + ** the same offset list. */ + while( 1 ){ + + /* The following line of code (and the "p++" below the while() loop) is + ** normally all that is required to move pointer p to the desired + ** position. The exception is if this node is being loaded from disk + ** incrementally and pointer "p" now points to the first byte passed + ** the populated part of pReader->aNode[]. + */ + while( *p | c ) c = *p++ & 0x80; + assert( *p==0 ); + + if( pReader->pBlob==0 || p<&pReader->aNode[pReader->nPopulate] ) break; + rc = fts3SegReaderIncrRead(pReader); + if( rc!=SQLITE_OK ) return rc; + } + p++; + + /* If required, populate the output variables with a pointer to and the + ** size of the previous offset-list. + */ + if( ppOffsetList ){ + *ppOffsetList = pReader->pOffsetList; + *pnOffsetList = (int)(p - pReader->pOffsetList - 1); + } + + while( p<pEnd && *p==0 ) p++; + + /* If there are no more entries in the doclist, set pOffsetList to + ** NULL. Otherwise, set Fts3SegReader.iDocid to the next docid and + ** Fts3SegReader.pOffsetList to point to the next offset list before + ** returning. + */ + if( p>=pEnd ){ + pReader->pOffsetList = 0; + }else{ + rc = fts3SegReaderRequire(pReader, p, FTS3_VARINT_MAX); + if( rc==SQLITE_OK ){ + sqlite3_int64 iDelta; + pReader->pOffsetList = p + sqlite3Fts3GetVarint(p, &iDelta); + if( pTab->bDescIdx ){ + pReader->iDocid -= iDelta; + }else{ + pReader->iDocid += iDelta; + } + } + } } + + return SQLITE_OK; } -/* -** This function is called to estimate the amount of data that will be -** loaded from the disk If SegReaderIterate() is called on this seg-reader, -** in units of average document size. -** -** This can be used as follows: If the caller has a small doclist that -** contains references to N documents, and is considering merging it with -** a large doclist (size X "average documents"), it may opt not to load -** the large doclist if X>N. -*/ -SQLITE_PRIVATE int sqlite3Fts3SegReaderCost( - Fts3Cursor *pCsr, /* FTS3 cursor handle */ - Fts3SegReader *pReader, /* Segment-reader handle */ - int *pnCost /* IN/OUT: Number of bytes read */ + +SQLITE_PRIVATE int sqlite3Fts3MsrOvfl( + Fts3Cursor *pCsr, + Fts3MultiSegReader *pMsr, + int *pnOvfl ){ Fts3Table *p = (Fts3Table*)pCsr->base.pVtab; - int rc = SQLITE_OK; /* Return code */ - int nCost = 0; /* Cost in bytes to return */ - int pgsz = p->nPgsz; /* Database page size */ + int nOvfl = 0; + int ii; + int rc = SQLITE_OK; + int pgsz = p->nPgsz; - /* If this seg-reader is reading the pending-terms table, or if all data - ** for the segment is stored on the root page of the b-tree, then the cost - ** is zero. In this case all required data is already in main memory. - */ - if( p->bHasStat - && !fts3SegReaderIsPending(pReader) - && !fts3SegReaderIsRootOnly(pReader) - ){ - int nBlob = 0; - sqlite3_int64 iBlock; + assert( p->bHasStat ); + assert( pgsz>0 ); - if( pCsr->nRowAvg==0 ){ - /* The average document size, which is required to calculate the cost - ** of each doclist, has not yet been determined. Read the required - ** data from the %_stat table to calculate it. - ** - ** Entry 0 of the %_stat table is a blob containing (nCol+1) FTS3 - ** varints, where nCol is the number of columns in the FTS3 table. - ** The first varint is the number of documents currently stored in - ** the table. The following nCol varints contain the total amount of - ** data stored in all rows of each column of the table, from left - ** to right. - */ - sqlite3_stmt *pStmt; - rc = fts3SqlStmt(p, SQL_SELECT_DOCTOTAL, &pStmt, 0); - if( rc ) return rc; - if( sqlite3_step(pStmt)==SQLITE_ROW ){ - sqlite3_int64 nDoc = 0; - sqlite3_int64 nByte = 0; - const char *a = sqlite3_column_blob(pStmt, 0); - if( a ){ - const char *pEnd = &a[sqlite3_column_bytes(pStmt, 0)]; - a += sqlite3Fts3GetVarint(a, &nDoc); - while( a<pEnd ){ - a += sqlite3Fts3GetVarint(a, &nByte); - } + for(ii=0; rc==SQLITE_OK && ii<pMsr->nSegment; ii++){ + Fts3SegReader *pReader = pMsr->apSegment[ii]; + if( !fts3SegReaderIsPending(pReader) + && !fts3SegReaderIsRootOnly(pReader) + ){ + sqlite3_int64 jj; + for(jj=pReader->iStartBlock; jj<=pReader->iLeafEndBlock; jj++){ + int nBlob; + rc = sqlite3Fts3ReadBlock(p, jj, 0, &nBlob, 0); + if( rc!=SQLITE_OK ) break; + if( (nBlob+35)>pgsz ){ + nOvfl += (nBlob + 34)/pgsz; } - - pCsr->nRowAvg = (int)(((nByte / nDoc) + pgsz - 1) / pgsz); - } - rc = sqlite3_reset(pStmt); - if( rc!=SQLITE_OK || pCsr->nRowAvg==0 ) return rc; - } - - /* Assume that a blob flows over onto overflow pages if it is larger - ** than (pgsz-35) bytes in size (the file-format documentation - ** confirms this). - */ - for(iBlock=pReader->iStartBlock; iBlock<=pReader->iLeafEndBlock; iBlock++){ - rc = sqlite3Fts3ReadBlock(p, iBlock, 0, &nBlob); - if( rc!=SQLITE_OK ) break; - if( (nBlob+35)>pgsz ){ - int nOvfl = (nBlob + 34)/pgsz; - nCost += ((nOvfl + pCsr->nRowAvg - 1)/pCsr->nRowAvg); } } } - - *pnCost += nCost; + *pnOvfl = nOvfl; return rc; } @@ -115551,6 +120964,7 @@ SQLITE_PRIVATE void sqlite3Fts3SegReaderFree(Fts3SegReader *pReader){ sqlite3_free(pReader->zTerm); if( !fts3SegReaderIsRootOnly(pReader) ){ sqlite3_free(pReader->aNode); + sqlite3_blob_close(pReader->pBlob); } } sqlite3_free(pReader); @@ -115627,24 +121041,42 @@ static int fts3CompareElemByTerm(const void *lhs, const void *rhs){ /* ** This function is used to allocate an Fts3SegReader that iterates through ** a subset of the terms stored in the Fts3Table.pendingTerms array. +** +** If the isPrefixIter parameter is zero, then the returned SegReader iterates +** through each term in the pending-terms table. Or, if isPrefixIter is +** non-zero, it iterates through each term and its prefixes. For example, if +** the pending terms hash table contains the terms "sqlite", "mysql" and +** "firebird", then the iterator visits the following 'terms' (in the order +** shown): +** +** f fi fir fire fireb firebi firebir firebird +** m my mys mysq mysql +** s sq sql sqli sqlit sqlite +** +** Whereas if isPrefixIter is zero, the terms visited are: +** +** firebird mysql sqlite */ SQLITE_PRIVATE int sqlite3Fts3SegReaderPending( Fts3Table *p, /* Virtual table handle */ + int iIndex, /* Index for p->aIndex */ const char *zTerm, /* Term to search for */ int nTerm, /* Size of buffer zTerm */ - int isPrefix, /* True for a term-prefix query */ + int bPrefix, /* True for a prefix iterator */ Fts3SegReader **ppReader /* OUT: SegReader for pending-terms */ ){ Fts3SegReader *pReader = 0; /* Fts3SegReader object to return */ Fts3HashElem **aElem = 0; /* Array of term hash entries to scan */ int nElem = 0; /* Size of array at aElem */ int rc = SQLITE_OK; /* Return Code */ + Fts3Hash *pHash; - if( isPrefix ){ + pHash = &p->aIndex[iIndex].hPending; + if( bPrefix ){ int nAlloc = 0; /* Size of allocated array at aElem */ Fts3HashElem *pE = 0; /* Iterator variable */ - for(pE=fts3HashFirst(&p->pendingTerms); pE; pE=fts3HashNext(pE)){ + for(pE=fts3HashFirst(pHash); pE; pE=fts3HashNext(pE)){ char *zKey = (char *)fts3HashKey(pE); int nKey = fts3HashKeysize(pE); if( nTerm==0 || (nKey>=nTerm && 0==memcmp(zKey, zTerm, nTerm)) ){ @@ -115661,6 +121093,7 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderPending( } aElem = aElem2; } + aElem[nElem++] = pE; } } @@ -115674,7 +121107,9 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderPending( } }else{ - Fts3HashElem *pE = fts3HashFindElem(&p->pendingTerms, zTerm, nTerm); + /* The query is a simple term lookup that matches at most one term in + ** the index. All that is required is a straight hash-lookup. */ + Fts3HashElem *pE = fts3HashFindElem(pHash, zTerm, nTerm); if( pE ){ aElem = &pE; nElem = 1; @@ -115694,49 +121129,13 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderPending( } } - if( isPrefix ){ + if( bPrefix ){ sqlite3_free(aElem); } *ppReader = pReader; return rc; } - -/* -** The second argument to this function is expected to be a statement of -** the form: -** -** SELECT -** idx, -- col 0 -** start_block, -- col 1 -** leaves_end_block, -- col 2 -** end_block, -- col 3 -** root -- col 4 -** FROM %_segdir ... -** -** This function allocates and initializes a Fts3SegReader structure to -** iterate through the terms stored in the segment identified by the -** current row that pStmt is pointing to. -** -** If successful, the Fts3SegReader is left pointing to the first term -** in the segment and SQLITE_OK is returned. Otherwise, an SQLite error -** code is returned. -*/ -static int fts3SegReaderNew( - sqlite3_stmt *pStmt, /* See above */ - int iAge, /* Segment "age". */ - Fts3SegReader **ppReader /* OUT: Allocated Fts3SegReader */ -){ - return sqlite3Fts3SegReaderNew(iAge, - sqlite3_column_int64(pStmt, 1), - sqlite3_column_int64(pStmt, 2), - sqlite3_column_int64(pStmt, 3), - sqlite3_column_blob(pStmt, 4), - sqlite3_column_bytes(pStmt, 4), - ppReader - ); -} - /* ** Compare the entries pointed to by two Fts3SegReader structures. ** Comparison is as follows: @@ -115794,6 +121193,18 @@ static int fts3SegReaderDoclistCmp(Fts3SegReader *pLhs, Fts3SegReader *pRhs){ assert( pLhs->aNode && pRhs->aNode ); return rc; } +static int fts3SegReaderDoclistCmpRev(Fts3SegReader *pLhs, Fts3SegReader *pRhs){ + int rc = (pLhs->pOffsetList==0)-(pRhs->pOffsetList==0); + if( rc==0 ){ + if( pLhs->iDocid==pRhs->iDocid ){ + rc = pRhs->iIdx - pLhs->iIdx; + }else{ + rc = (pLhs->iDocid < pRhs->iDocid) ? 1 : -1; + } + } + assert( pLhs->aNode && pRhs->aNode ); + return rc; +} /* ** Compare the term that the Fts3SegReader object passed as the first argument @@ -116322,16 +121733,16 @@ static void fts3SegWriterFree(SegmentWriter *pWriter){ ** The first value in the apVal[] array is assumed to contain an integer. ** This function tests if there exist any documents with docid values that ** are different from that integer. i.e. if deleting the document with docid -** apVal[0] would mean the FTS3 table were empty. +** pRowid would mean the FTS3 table were empty. ** ** If successful, *pisEmpty is set to true if the table is empty except for -** document apVal[0], or false otherwise, and SQLITE_OK is returned. If an +** document pRowid, or false otherwise, and SQLITE_OK is returned. If an ** error occurs, an SQLite error code is returned. */ -static int fts3IsEmpty(Fts3Table *p, sqlite3_value **apVal, int *pisEmpty){ +static int fts3IsEmpty(Fts3Table *p, sqlite3_value *pRowid, int *pisEmpty){ sqlite3_stmt *pStmt; int rc; - rc = fts3SqlStmt(p, SQL_IS_EMPTY, &pStmt, apVal); + rc = fts3SqlStmt(p, SQL_IS_EMPTY, &pStmt, &pRowid); if( rc==SQLITE_OK ){ if( SQLITE_ROW==sqlite3_step(pStmt) ){ *pisEmpty = sqlite3_column_int(pStmt, 0); @@ -116342,40 +121753,30 @@ static int fts3IsEmpty(Fts3Table *p, sqlite3_value **apVal, int *pisEmpty){ } /* -** Set *pnSegment to the number of segments of level iLevel in the database. +** Set *pnMax to the largest segment level in the database for the index +** iIndex. ** -** Return SQLITE_OK if successful, or an SQLite error code if not. -*/ -static int fts3SegmentCount(Fts3Table *p, int iLevel, int *pnSegment){ - sqlite3_stmt *pStmt; - int rc; - - assert( iLevel>=0 ); - rc = fts3SqlStmt(p, SQL_SELECT_LEVEL_COUNT, &pStmt, 0); - if( rc!=SQLITE_OK ) return rc; - sqlite3_bind_int(pStmt, 1, iLevel); - if( SQLITE_ROW==sqlite3_step(pStmt) ){ - *pnSegment = sqlite3_column_int(pStmt, 0); - } - return sqlite3_reset(pStmt); -} - -/* -** Set *pnSegment to the total number of segments in the database. Set -** *pnMax to the largest segment level in the database (segment levels -** are stored in the 'level' column of the %_segdir table). +** Segment levels are stored in the 'level' column of the %_segdir table. ** ** Return SQLITE_OK if successful, or an SQLite error code if not. */ -static int fts3SegmentCountMax(Fts3Table *p, int *pnSegment, int *pnMax){ +static int fts3SegmentMaxLevel(Fts3Table *p, int iIndex, int *pnMax){ sqlite3_stmt *pStmt; int rc; + assert( iIndex>=0 && iIndex<p->nIndex ); - rc = fts3SqlStmt(p, SQL_SELECT_SEGDIR_COUNT_MAX, &pStmt, 0); + /* Set pStmt to the compiled version of: + ** + ** SELECT max(level) FROM %Q.'%q_segdir' WHERE level BETWEEN ? AND ? + ** + ** (1024 is actually the value of macro FTS3_SEGDIR_PREFIXLEVEL_STR). + */ + rc = fts3SqlStmt(p, SQL_SELECT_SEGDIR_MAX_LEVEL, &pStmt, 0); if( rc!=SQLITE_OK ) return rc; + sqlite3_bind_int(pStmt, 1, iIndex*FTS3_SEGDIR_MAXLEVEL); + sqlite3_bind_int(pStmt, 2, (iIndex+1)*FTS3_SEGDIR_MAXLEVEL - 1); if( SQLITE_ROW==sqlite3_step(pStmt) ){ - *pnSegment = sqlite3_column_int(pStmt, 0); - *pnMax = sqlite3_column_int(pStmt, 1); + *pnMax = sqlite3_column_int(pStmt, 0); } return sqlite3_reset(pStmt); } @@ -116396,6 +121797,7 @@ static int fts3SegmentCountMax(Fts3Table *p, int *pnSegment, int *pnMax){ */ static int fts3DeleteSegdir( Fts3Table *p, /* Virtual table handle */ + int iIndex, /* Index for p->aIndex */ int iLevel, /* Level of %_segdir entries to delete */ Fts3SegReader **apSegment, /* Array of SegReader objects */ int nReader /* Size of array apSegment */ @@ -116418,15 +121820,23 @@ static int fts3DeleteSegdir( return rc; } - if( iLevel>=0 ){ - rc = fts3SqlStmt(p, SQL_DELETE_SEGDIR_BY_LEVEL, &pDelete, 0); + assert( iLevel>=0 || iLevel==FTS3_SEGCURSOR_ALL ); + if( iLevel==FTS3_SEGCURSOR_ALL ){ + rc = fts3SqlStmt(p, SQL_DELETE_SEGDIR_RANGE, &pDelete, 0); if( rc==SQLITE_OK ){ - sqlite3_bind_int(pDelete, 1, iLevel); - sqlite3_step(pDelete); - rc = sqlite3_reset(pDelete); + sqlite3_bind_int(pDelete, 1, iIndex*FTS3_SEGDIR_MAXLEVEL); + sqlite3_bind_int(pDelete, 2, (iIndex+1) * FTS3_SEGDIR_MAXLEVEL - 1); } }else{ - fts3SqlExec(&rc, p, SQL_DELETE_ALL_SEGDIR, 0); + rc = fts3SqlStmt(p, SQL_DELETE_SEGDIR_LEVEL, &pDelete, 0); + if( rc==SQLITE_OK ){ + sqlite3_bind_int(pDelete, 1, iIndex*FTS3_SEGDIR_MAXLEVEL + iLevel); + } + } + + if( rc==SQLITE_OK ){ + sqlite3_step(pDelete); + rc = sqlite3_reset(pDelete); } return rc; @@ -116476,84 +121886,105 @@ static void fts3ColumnFilter( } /* -** sqlite3Fts3SegReaderIterate() callback used when merging multiple -** segments to create a single, larger segment. +** Cache data in the Fts3MultiSegReader.aBuffer[] buffer (overwriting any +** existing data). Grow the buffer if required. +** +** If successful, return SQLITE_OK. Otherwise, if an OOM error is encountered +** trying to resize the buffer, return SQLITE_NOMEM. */ -static int fts3MergeCallback( - Fts3Table *p, /* FTS3 Virtual table handle */ - void *pContext, /* Pointer to SegmentWriter* to write with */ - char *zTerm, /* Term to write to the db */ - int nTerm, /* Number of bytes in zTerm */ - char *aDoclist, /* Doclist associated with zTerm */ - int nDoclist /* Number of bytes in doclist */ +static int fts3MsrBufferData( + Fts3MultiSegReader *pMsr, /* Multi-segment-reader handle */ + char *pList, + int nList ){ - SegmentWriter **ppW = (SegmentWriter **)pContext; - return fts3SegWriterAdd(p, ppW, 1, zTerm, nTerm, aDoclist, nDoclist); -} + if( nList>pMsr->nBuffer ){ + char *pNew; + pMsr->nBuffer = nList*2; + pNew = (char *)sqlite3_realloc(pMsr->aBuffer, pMsr->nBuffer); + if( !pNew ) return SQLITE_NOMEM; + pMsr->aBuffer = pNew; + } -/* -** sqlite3Fts3SegReaderIterate() callback used when flushing the contents -** of the pending-terms hash table to the database. -*/ -static int fts3FlushCallback( - Fts3Table *p, /* FTS3 Virtual table handle */ - void *pContext, /* Pointer to SegmentWriter* to write with */ - char *zTerm, /* Term to write to the db */ - int nTerm, /* Number of bytes in zTerm */ - char *aDoclist, /* Doclist associated with zTerm */ - int nDoclist /* Number of bytes in doclist */ -){ - SegmentWriter **ppW = (SegmentWriter **)pContext; - return fts3SegWriterAdd(p, ppW, 0, zTerm, nTerm, aDoclist, nDoclist); + memcpy(pMsr->aBuffer, pList, nList); + return SQLITE_OK; } -/* -** This function is used to iterate through a contiguous set of terms -** stored in the full-text index. It merges data contained in one or -** more segments to support this. -** -** The second argument is passed an array of pointers to SegReader objects -** allocated with sqlite3Fts3SegReaderNew(). This function merges the range -** of terms selected by each SegReader. If a single term is present in -** more than one segment, the associated doclists are merged. For each -** term and (possibly merged) doclist in the merged range, the callback -** function xFunc is invoked with its arguments set as follows. -** -** arg 0: Copy of 'p' parameter passed to this function -** arg 1: Copy of 'pContext' parameter passed to this function -** arg 2: Pointer to buffer containing term -** arg 3: Size of arg 2 buffer in bytes -** arg 4: Pointer to buffer containing doclist -** arg 5: Size of arg 2 buffer in bytes -** -** The 4th argument to this function is a pointer to a structure of type -** Fts3SegFilter, defined in fts3Int.h. The contents of this structure -** further restrict the range of terms that callbacks are made for and -** modify the behaviour of this function. See comments above structure -** definition for details. -*/ -SQLITE_PRIVATE int sqlite3Fts3SegReaderIterate( +SQLITE_PRIVATE int sqlite3Fts3MsrIncrNext( Fts3Table *p, /* Virtual table handle */ - Fts3SegReader **apSegment, /* Array of Fts3SegReader objects */ - int nSegment, /* Size of apSegment array */ - Fts3SegFilter *pFilter, /* Restrictions on range of iteration */ - int (*xFunc)(Fts3Table *, void *, char *, int, char *, int), /* Callback */ - void *pContext /* Callback context (2nd argument) */ -){ - int i; /* Iterator variable */ - char *aBuffer = 0; /* Buffer to merge doclists in */ - int nAlloc = 0; /* Allocated size of aBuffer buffer */ - int rc = SQLITE_OK; /* Return code */ + Fts3MultiSegReader *pMsr, /* Multi-segment-reader handle */ + sqlite3_int64 *piDocid, /* OUT: Docid value */ + char **paPoslist, /* OUT: Pointer to position list */ + int *pnPoslist /* OUT: Size of position list in bytes */ +){ + int nMerge = pMsr->nAdvance; + Fts3SegReader **apSegment = pMsr->apSegment; + int (*xCmp)(Fts3SegReader *, Fts3SegReader *) = ( + p->bDescIdx ? fts3SegReaderDoclistCmpRev : fts3SegReaderDoclistCmp + ); - int isIgnoreEmpty = (pFilter->flags & FTS3_SEGMENT_IGNORE_EMPTY); - int isRequirePos = (pFilter->flags & FTS3_SEGMENT_REQUIRE_POS); - int isColFilter = (pFilter->flags & FTS3_SEGMENT_COLUMN_FILTER); - int isPrefix = (pFilter->flags & FTS3_SEGMENT_PREFIX); + if( nMerge==0 ){ + *paPoslist = 0; + return SQLITE_OK; + } - /* If there are zero segments, this function is a no-op. This scenario - ** comes about only when reading from an empty database. - */ - if( nSegment==0 ) goto finished; + while( 1 ){ + Fts3SegReader *pSeg; + pSeg = pMsr->apSegment[0]; + + if( pSeg->pOffsetList==0 ){ + *paPoslist = 0; + break; + }else{ + int rc; + char *pList; + int nList; + int j; + sqlite3_int64 iDocid = apSegment[0]->iDocid; + + rc = fts3SegReaderNextDocid(p, apSegment[0], &pList, &nList); + j = 1; + while( rc==SQLITE_OK + && j<nMerge + && apSegment[j]->pOffsetList + && apSegment[j]->iDocid==iDocid + ){ + rc = fts3SegReaderNextDocid(p, apSegment[j], 0, 0); + j++; + } + if( rc!=SQLITE_OK ) return rc; + fts3SegReaderSort(pMsr->apSegment, nMerge, j, xCmp); + + if( pMsr->iColFilter>=0 ){ + fts3ColumnFilter(pMsr->iColFilter, &pList, &nList); + } + + if( nList>0 ){ + if( fts3SegReaderIsPending(apSegment[0]) ){ + rc = fts3MsrBufferData(pMsr, pList, nList+1); + if( rc!=SQLITE_OK ) return rc; + *paPoslist = pMsr->aBuffer; + assert( (pMsr->aBuffer[nList] & 0xFE)==0x00 ); + }else{ + *paPoslist = pList; + } + *piDocid = iDocid; + *pnPoslist = nList; + break; + } + } + } + + return SQLITE_OK; +} + +static int fts3SegReaderStart( + Fts3Table *p, /* Virtual table handle */ + Fts3MultiSegReader *pCsr, /* Cursor object */ + const char *zTerm, /* Term searched for (or NULL) */ + int nTerm /* Length of zTerm in bytes */ +){ + int i; + int nSeg = pCsr->nSegment; /* If the Fts3SegFilter defines a specific term (or term prefix) to search ** for, then advance each segment iterator until it points to a term of @@ -116561,21 +121992,143 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderIterate( ** unnecessary merge/sort operations for the case where single segment ** b-tree leaf nodes contain more than one term. */ - for(i=0; i<nSegment; i++){ - int nTerm = pFilter->nTerm; - const char *zTerm = pFilter->zTerm; - Fts3SegReader *pSeg = apSegment[i]; + for(i=0; pCsr->bRestart==0 && i<pCsr->nSegment; i++){ + Fts3SegReader *pSeg = pCsr->apSegment[i]; do { - rc = fts3SegReaderNext(p, pSeg); - if( rc!=SQLITE_OK ) goto finished; + int rc = fts3SegReaderNext(p, pSeg, 0); + if( rc!=SQLITE_OK ) return rc; }while( zTerm && fts3SegReaderTermCmp(pSeg, zTerm, nTerm)<0 ); } + fts3SegReaderSort(pCsr->apSegment, nSeg, nSeg, fts3SegReaderCmp); + + return SQLITE_OK; +} + +SQLITE_PRIVATE int sqlite3Fts3SegReaderStart( + Fts3Table *p, /* Virtual table handle */ + Fts3MultiSegReader *pCsr, /* Cursor object */ + Fts3SegFilter *pFilter /* Restrictions on range of iteration */ +){ + pCsr->pFilter = pFilter; + return fts3SegReaderStart(p, pCsr, pFilter->zTerm, pFilter->nTerm); +} + +SQLITE_PRIVATE int sqlite3Fts3MsrIncrStart( + Fts3Table *p, /* Virtual table handle */ + Fts3MultiSegReader *pCsr, /* Cursor object */ + int iCol, /* Column to match on. */ + const char *zTerm, /* Term to iterate through a doclist for */ + int nTerm /* Number of bytes in zTerm */ +){ + int i; + int rc; + int nSegment = pCsr->nSegment; + int (*xCmp)(Fts3SegReader *, Fts3SegReader *) = ( + p->bDescIdx ? fts3SegReaderDoclistCmpRev : fts3SegReaderDoclistCmp + ); + + assert( pCsr->pFilter==0 ); + assert( zTerm && nTerm>0 ); + + /* Advance each segment iterator until it points to the term zTerm/nTerm. */ + rc = fts3SegReaderStart(p, pCsr, zTerm, nTerm); + if( rc!=SQLITE_OK ) return rc; + + /* Determine how many of the segments actually point to zTerm/nTerm. */ + for(i=0; i<nSegment; i++){ + Fts3SegReader *pSeg = pCsr->apSegment[i]; + if( !pSeg->aNode || fts3SegReaderTermCmp(pSeg, zTerm, nTerm) ){ + break; + } + } + pCsr->nAdvance = i; + + /* Advance each of the segments to point to the first docid. */ + for(i=0; i<pCsr->nAdvance; i++){ + rc = fts3SegReaderFirstDocid(p, pCsr->apSegment[i]); + if( rc!=SQLITE_OK ) return rc; + } + fts3SegReaderSort(pCsr->apSegment, i, i, xCmp); + + assert( iCol<0 || iCol<p->nColumn ); + pCsr->iColFilter = iCol; + + return SQLITE_OK; +} + +/* +** This function is called on a MultiSegReader that has been started using +** sqlite3Fts3MsrIncrStart(). One or more calls to MsrIncrNext() may also +** have been made. Calling this function puts the MultiSegReader in such +** a state that if the next two calls are: +** +** sqlite3Fts3SegReaderStart() +** sqlite3Fts3SegReaderStep() +** +** then the entire doclist for the term is available in +** MultiSegReader.aDoclist/nDoclist. +*/ +SQLITE_PRIVATE int sqlite3Fts3MsrIncrRestart(Fts3MultiSegReader *pCsr){ + int i; /* Used to iterate through segment-readers */ + + assert( pCsr->zTerm==0 ); + assert( pCsr->nTerm==0 ); + assert( pCsr->aDoclist==0 ); + assert( pCsr->nDoclist==0 ); + + pCsr->nAdvance = 0; + pCsr->bRestart = 1; + for(i=0; i<pCsr->nSegment; i++){ + pCsr->apSegment[i]->pOffsetList = 0; + pCsr->apSegment[i]->nOffsetList = 0; + pCsr->apSegment[i]->iDocid = 0; + } + + return SQLITE_OK; +} - fts3SegReaderSort(apSegment, nSegment, nSegment, fts3SegReaderCmp); - while( apSegment[0]->aNode ){ - int nTerm = apSegment[0]->nTerm; - char *zTerm = apSegment[0]->zTerm; - int nMerge = 1; + +SQLITE_PRIVATE int sqlite3Fts3SegReaderStep( + Fts3Table *p, /* Virtual table handle */ + Fts3MultiSegReader *pCsr /* Cursor object */ +){ + int rc = SQLITE_OK; + + int isIgnoreEmpty = (pCsr->pFilter->flags & FTS3_SEGMENT_IGNORE_EMPTY); + int isRequirePos = (pCsr->pFilter->flags & FTS3_SEGMENT_REQUIRE_POS); + int isColFilter = (pCsr->pFilter->flags & FTS3_SEGMENT_COLUMN_FILTER); + int isPrefix = (pCsr->pFilter->flags & FTS3_SEGMENT_PREFIX); + int isScan = (pCsr->pFilter->flags & FTS3_SEGMENT_SCAN); + + Fts3SegReader **apSegment = pCsr->apSegment; + int nSegment = pCsr->nSegment; + Fts3SegFilter *pFilter = pCsr->pFilter; + int (*xCmp)(Fts3SegReader *, Fts3SegReader *) = ( + p->bDescIdx ? fts3SegReaderDoclistCmpRev : fts3SegReaderDoclistCmp + ); + + if( pCsr->nSegment==0 ) return SQLITE_OK; + + do { + int nMerge; + int i; + + /* Advance the first pCsr->nAdvance entries in the apSegment[] array + ** forward. Then sort the list in order of current term again. + */ + for(i=0; i<pCsr->nAdvance; i++){ + rc = fts3SegReaderNext(p, apSegment[i], 0); + if( rc!=SQLITE_OK ) return rc; + } + fts3SegReaderSort(apSegment, nSegment, pCsr->nAdvance, fts3SegReaderCmp); + pCsr->nAdvance = 0; + + /* If all the seg-readers are at EOF, we're finished. return SQLITE_OK. */ + assert( rc==SQLITE_OK ); + if( apSegment[0]->aNode==0 ) break; + + pCsr->nTerm = apSegment[0]->nTerm; + pCsr->zTerm = apSegment[0]->zTerm; /* If this is a prefix-search, and if the term that apSegment[0] points ** to does not share a suffix with pFilter->zTerm/nTerm, then all @@ -116584,53 +122137,62 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderIterate( ** Similarly, if this is a search for an exact match, and the first term ** of segment apSegment[0] is not a match, exit early. */ - if( pFilter->zTerm ){ - if( nTerm<pFilter->nTerm - || (!isPrefix && nTerm>pFilter->nTerm) - || memcmp(zTerm, pFilter->zTerm, pFilter->nTerm) - ){ - goto finished; + if( pFilter->zTerm && !isScan ){ + if( pCsr->nTerm<pFilter->nTerm + || (!isPrefix && pCsr->nTerm>pFilter->nTerm) + || memcmp(pCsr->zTerm, pFilter->zTerm, pFilter->nTerm) + ){ + break; } } + nMerge = 1; while( nMerge<nSegment && apSegment[nMerge]->aNode - && apSegment[nMerge]->nTerm==nTerm - && 0==memcmp(zTerm, apSegment[nMerge]->zTerm, nTerm) + && apSegment[nMerge]->nTerm==pCsr->nTerm + && 0==memcmp(pCsr->zTerm, apSegment[nMerge]->zTerm, pCsr->nTerm) ){ nMerge++; } assert( isIgnoreEmpty || (isRequirePos && !isColFilter) ); - if( nMerge==1 && !isIgnoreEmpty ){ - Fts3SegReader *p0 = apSegment[0]; - rc = xFunc(p, pContext, zTerm, nTerm, p0->aDoclist, p0->nDoclist); - if( rc!=SQLITE_OK ) goto finished; + if( nMerge==1 + && !isIgnoreEmpty + && (p->bDescIdx==0 || fts3SegReaderIsPending(apSegment[0])==0) + ){ + pCsr->nDoclist = apSegment[0]->nDoclist; + if( fts3SegReaderIsPending(apSegment[0]) ){ + rc = fts3MsrBufferData(pCsr, apSegment[0]->aDoclist, pCsr->nDoclist); + pCsr->aDoclist = pCsr->aBuffer; + }else{ + pCsr->aDoclist = apSegment[0]->aDoclist; + } + if( rc==SQLITE_OK ) rc = SQLITE_ROW; }else{ int nDoclist = 0; /* Size of doclist */ sqlite3_int64 iPrev = 0; /* Previous docid stored in doclist */ /* The current term of the first nMerge entries in the array ** of Fts3SegReader objects is the same. The doclists must be merged - ** and a single term added to the new segment. + ** and a single term returned with the merged doclist. */ for(i=0; i<nMerge; i++){ - fts3SegReaderFirstDocid(apSegment[i]); + fts3SegReaderFirstDocid(p, apSegment[i]); } - fts3SegReaderSort(apSegment, nMerge, nMerge, fts3SegReaderDoclistCmp); + fts3SegReaderSort(apSegment, nMerge, nMerge, xCmp); while( apSegment[0]->pOffsetList ){ int j; /* Number of segments that share a docid */ char *pList; int nList; int nByte; sqlite3_int64 iDocid = apSegment[0]->iDocid; - fts3SegReaderNextDocid(apSegment[0], &pList, &nList); + fts3SegReaderNextDocid(p, apSegment[0], &pList, &nList); j = 1; while( j<nMerge && apSegment[j]->pOffsetList && apSegment[j]->iDocid==iDocid ){ - fts3SegReaderNextDocid(apSegment[j], 0, 0); + fts3SegReaderNextDocid(p, apSegment[j], 0, 0); j++; } @@ -116639,53 +122201,67 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderIterate( } if( !isIgnoreEmpty || nList>0 ){ - nByte = sqlite3Fts3VarintLen(iDocid-iPrev) + (isRequirePos?nList+1:0); - if( nDoclist+nByte>nAlloc ){ + + /* Calculate the 'docid' delta value to write into the merged + ** doclist. */ + sqlite3_int64 iDelta; + if( p->bDescIdx && nDoclist>0 ){ + iDelta = iPrev - iDocid; + }else{ + iDelta = iDocid - iPrev; + } + assert( iDelta>0 || (nDoclist==0 && iDelta==iDocid) ); + assert( nDoclist>0 || iDelta==iDocid ); + + nByte = sqlite3Fts3VarintLen(iDelta) + (isRequirePos?nList+1:0); + if( nDoclist+nByte>pCsr->nBuffer ){ char *aNew; - nAlloc = nDoclist+nByte*2; - aNew = sqlite3_realloc(aBuffer, nAlloc); + pCsr->nBuffer = (nDoclist+nByte)*2; + aNew = sqlite3_realloc(pCsr->aBuffer, pCsr->nBuffer); if( !aNew ){ - rc = SQLITE_NOMEM; - goto finished; + return SQLITE_NOMEM; } - aBuffer = aNew; + pCsr->aBuffer = aNew; } - nDoclist += sqlite3Fts3PutVarint(&aBuffer[nDoclist], iDocid-iPrev); + nDoclist += sqlite3Fts3PutVarint(&pCsr->aBuffer[nDoclist], iDelta); iPrev = iDocid; if( isRequirePos ){ - memcpy(&aBuffer[nDoclist], pList, nList); + memcpy(&pCsr->aBuffer[nDoclist], pList, nList); nDoclist += nList; - aBuffer[nDoclist++] = '\0'; + pCsr->aBuffer[nDoclist++] = '\0'; } } - fts3SegReaderSort(apSegment, nMerge, j, fts3SegReaderDoclistCmp); + fts3SegReaderSort(apSegment, nMerge, j, xCmp); } - if( nDoclist>0 ){ - rc = xFunc(p, pContext, zTerm, nTerm, aBuffer, nDoclist); - if( rc!=SQLITE_OK ) goto finished; + pCsr->aDoclist = pCsr->aBuffer; + pCsr->nDoclist = nDoclist; + rc = SQLITE_ROW; } } + pCsr->nAdvance = nMerge; + }while( rc==SQLITE_OK ); - /* If there is a term specified to filter on, and this is not a prefix - ** search, return now. The callback that corresponds to the required - ** term (if such a term exists in the index) has already been made. - */ - if( pFilter->zTerm && !isPrefix ){ - goto finished; - } + return rc; +} - for(i=0; i<nMerge; i++){ - rc = fts3SegReaderNext(p, apSegment[i]); - if( rc!=SQLITE_OK ) goto finished; + +SQLITE_PRIVATE void sqlite3Fts3SegReaderFinish( + Fts3MultiSegReader *pCsr /* Cursor object */ +){ + if( pCsr ){ + int i; + for(i=0; i<pCsr->nSegment; i++){ + sqlite3Fts3SegReaderFree(pCsr->apSegment[i]); } - fts3SegReaderSort(apSegment, nSegment, nMerge, fts3SegReaderCmp); - } + sqlite3_free(pCsr->apSegment); + sqlite3_free(pCsr->aBuffer); - finished: - sqlite3_free(aBuffer); - return rc; + pCsr->nSegment = 0; + pCsr->apSegment = 0; + pCsr->aBuffer = 0; + } } /* @@ -116699,157 +122275,91 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderIterate( ** Otherwise, if successful, SQLITE_OK is returned. If an error occurs, ** an SQLite error code is returned. */ -static int fts3SegmentMerge(Fts3Table *p, int iLevel){ - int i; /* Iterator variable */ +static int fts3SegmentMerge(Fts3Table *p, int iIndex, int iLevel){ int rc; /* Return code */ - int iIdx; /* Index of new segment */ - int iNewLevel = 0; /* Level to create new segment at */ - sqlite3_stmt *pStmt = 0; - SegmentWriter *pWriter = 0; - int nSegment = 0; /* Number of segments being merged */ - Fts3SegReader **apSegment = 0; /* Array of Segment iterators */ - Fts3SegReader *pPending = 0; /* Iterator for pending-terms */ + int iIdx = 0; /* Index of new segment */ + int iNewLevel = 0; /* Level/index to create new segment at */ + SegmentWriter *pWriter = 0; /* Used to write the new, merged, segment */ Fts3SegFilter filter; /* Segment term filter condition */ + Fts3MultiSegReader csr; /* Cursor to iterate through level(s) */ + int bIgnoreEmpty = 0; /* True to ignore empty segments */ - if( iLevel<0 ){ + assert( iLevel==FTS3_SEGCURSOR_ALL + || iLevel==FTS3_SEGCURSOR_PENDING + || iLevel>=0 + ); + assert( iLevel<FTS3_SEGDIR_MAXLEVEL ); + assert( iIndex>=0 && iIndex<p->nIndex ); + + rc = sqlite3Fts3SegReaderCursor(p, iIndex, iLevel, 0, 0, 1, 0, &csr); + if( rc!=SQLITE_OK || csr.nSegment==0 ) goto finished; + + if( iLevel==FTS3_SEGCURSOR_ALL ){ /* This call is to merge all segments in the database to a single ** segment. The level of the new segment is equal to the the numerically - ** greatest segment level currently present in the database. The index - ** of the new segment is always 0. - */ - iIdx = 0; - rc = sqlite3Fts3SegReaderPending(p, 0, 0, 1, &pPending); - if( rc!=SQLITE_OK ) goto finished; - rc = fts3SegmentCountMax(p, &nSegment, &iNewLevel); - if( rc!=SQLITE_OK ) goto finished; - nSegment += (pPending!=0); - if( nSegment<=1 ){ - return SQLITE_DONE; + ** greatest segment level currently present in the database for this + ** index. The idx of the new segment is always 0. */ + if( csr.nSegment==1 ){ + rc = SQLITE_DONE; + goto finished; } + rc = fts3SegmentMaxLevel(p, iIndex, &iNewLevel); + bIgnoreEmpty = 1; + + }else if( iLevel==FTS3_SEGCURSOR_PENDING ){ + iNewLevel = iIndex * FTS3_SEGDIR_MAXLEVEL; + rc = fts3AllocateSegdirIdx(p, iIndex, 0, &iIdx); }else{ - /* This call is to merge all segments at level iLevel. Find the next + /* This call is to merge all segments at level iLevel. find the next ** available segment index at level iLevel+1. The call to ** fts3AllocateSegdirIdx() will merge the segments at level iLevel+1 to - ** a single iLevel+2 segment if necessary. - */ - iNewLevel = iLevel+1; - rc = fts3AllocateSegdirIdx(p, iNewLevel, &iIdx); - if( rc!=SQLITE_OK ) goto finished; - rc = fts3SegmentCount(p, iLevel, &nSegment); - if( rc!=SQLITE_OK ) goto finished; - } - assert( nSegment>0 ); - assert( iNewLevel>=0 ); - - /* Allocate space for an array of pointers to segment iterators. */ - apSegment = (Fts3SegReader**)sqlite3_malloc(sizeof(Fts3SegReader *)*nSegment); - if( !apSegment ){ - rc = SQLITE_NOMEM; - goto finished; + ** a single iLevel+2 segment if necessary. */ + rc = fts3AllocateSegdirIdx(p, iIndex, iLevel+1, &iIdx); + iNewLevel = iIndex * FTS3_SEGDIR_MAXLEVEL + iLevel+1; } - memset(apSegment, 0, sizeof(Fts3SegReader *)*nSegment); - - /* Allocate a Fts3SegReader structure for each segment being merged. A - ** Fts3SegReader stores the state data required to iterate through all - ** entries on all leaves of a single segment. - */ - assert( SQL_SELECT_LEVEL+1==SQL_SELECT_ALL_LEVEL); - rc = fts3SqlStmt(p, SQL_SELECT_LEVEL+(iLevel<0), &pStmt, 0); - if( rc!=SQLITE_OK ) goto finished; - sqlite3_bind_int(pStmt, 1, iLevel); - for(i=0; SQLITE_ROW==(sqlite3_step(pStmt)); i++){ - rc = fts3SegReaderNew(pStmt, i, &apSegment[i]); - if( rc!=SQLITE_OK ){ - goto finished; - } - } - rc = sqlite3_reset(pStmt); - if( pPending ){ - apSegment[i] = pPending; - pPending = 0; - } - pStmt = 0; if( rc!=SQLITE_OK ) goto finished; + assert( csr.nSegment>0 ); + assert( iNewLevel>=(iIndex*FTS3_SEGDIR_MAXLEVEL) ); + assert( iNewLevel<((iIndex+1)*FTS3_SEGDIR_MAXLEVEL) ); memset(&filter, 0, sizeof(Fts3SegFilter)); filter.flags = FTS3_SEGMENT_REQUIRE_POS; - filter.flags |= (iLevel<0 ? FTS3_SEGMENT_IGNORE_EMPTY : 0); - rc = sqlite3Fts3SegReaderIterate(p, apSegment, nSegment, - &filter, fts3MergeCallback, (void *)&pWriter - ); + filter.flags |= (bIgnoreEmpty ? FTS3_SEGMENT_IGNORE_EMPTY : 0); + + rc = sqlite3Fts3SegReaderStart(p, &csr, &filter); + while( SQLITE_OK==rc ){ + rc = sqlite3Fts3SegReaderStep(p, &csr); + if( rc!=SQLITE_ROW ) break; + rc = fts3SegWriterAdd(p, &pWriter, 1, + csr.zTerm, csr.nTerm, csr.aDoclist, csr.nDoclist); + } if( rc!=SQLITE_OK ) goto finished; + assert( pWriter ); - rc = fts3DeleteSegdir(p, iLevel, apSegment, nSegment); - if( rc==SQLITE_OK ){ - rc = fts3SegWriterFlush(p, pWriter, iNewLevel, iIdx); + if( iLevel!=FTS3_SEGCURSOR_PENDING ){ + rc = fts3DeleteSegdir(p, iIndex, iLevel, csr.apSegment, csr.nSegment); + if( rc!=SQLITE_OK ) goto finished; } + rc = fts3SegWriterFlush(p, pWriter, iNewLevel, iIdx); finished: fts3SegWriterFree(pWriter); - if( apSegment ){ - for(i=0; i<nSegment; i++){ - sqlite3Fts3SegReaderFree(apSegment[i]); - } - sqlite3_free(apSegment); - } - sqlite3Fts3SegReaderFree(pPending); - sqlite3_reset(pStmt); + sqlite3Fts3SegReaderFinish(&csr); return rc; } /* -** Flush the contents of pendingTerms to a level 0 segment. +** Flush the contents of pendingTerms to level 0 segments. */ SQLITE_PRIVATE int sqlite3Fts3PendingTermsFlush(Fts3Table *p){ - int rc; /* Return Code */ - int idx; /* Index of new segment created */ - SegmentWriter *pWriter = 0; /* Used to write the segment */ - Fts3SegReader *pReader = 0; /* Used to iterate through the hash table */ - - /* Allocate a SegReader object to iterate through the contents of the - ** pending-terms table. If an error occurs, or if there are no terms - ** in the pending-terms table, return immediately. - */ - rc = sqlite3Fts3SegReaderPending(p, 0, 0, 1, &pReader); - if( rc!=SQLITE_OK || pReader==0 ){ - return rc; - } - - /* Determine the next index at level 0. If level 0 is already full, this - ** call may merge all existing level 0 segments into a single level 1 - ** segment. - */ - rc = fts3AllocateSegdirIdx(p, 0, &idx); - - /* If no errors have occured, iterate through the contents of the - ** pending-terms hash table using the Fts3SegReader iterator. The callback - ** writes each term (along with its doclist) to the database via the - ** SegmentWriter handle pWriter. - */ - if( rc==SQLITE_OK ){ - void *c = (void *)&pWriter; /* SegReaderIterate() callback context */ - Fts3SegFilter f; /* SegReaderIterate() parameters */ - - memset(&f, 0, sizeof(Fts3SegFilter)); - f.flags = FTS3_SEGMENT_REQUIRE_POS; - rc = sqlite3Fts3SegReaderIterate(p, &pReader, 1, &f, fts3FlushCallback, c); - } - assert( pWriter || rc!=SQLITE_OK ); - - /* If no errors have occured, flush the SegmentWriter object to the - ** database. Then delete the SegmentWriter and Fts3SegReader objects - ** allocated by this function. - */ - if( rc==SQLITE_OK ){ - rc = fts3SegWriterFlush(p, pWriter, 0, idx); - } - fts3SegWriterFree(pWriter); - sqlite3Fts3SegReaderFree(pReader); - - if( rc==SQLITE_OK ){ - sqlite3Fts3PendingTermsClear(p); + int rc = SQLITE_OK; + int i; + for(i=0; rc==SQLITE_OK && i<p->nIndex; i++){ + rc = fts3SegmentMerge(p, i, FTS3_SEGCURSOR_PENDING); + if( rc==SQLITE_DONE ) rc = SQLITE_OK; } + sqlite3Fts3PendingTermsClear(p); return rc; } @@ -117001,6 +122511,23 @@ static void fts3UpdateDocTotals( sqlite3_free(a); } +static int fts3DoOptimize(Fts3Table *p, int bReturnDone){ + int i; + int bSeenDone = 0; + int rc = SQLITE_OK; + for(i=0; rc==SQLITE_OK && i<p->nIndex; i++){ + rc = fts3SegmentMerge(p, i, FTS3_SEGCURSOR_ALL); + if( rc==SQLITE_DONE ){ + bSeenDone = 1; + rc = SQLITE_OK; + } + } + sqlite3Fts3SegmentsClose(p); + sqlite3Fts3PendingTermsClear(p); + + return (rc==SQLITE_OK && bReturnDone && bSeenDone) ? SQLITE_DONE : rc; +} + /* ** Handle a 'special' INSERT of the form: ** @@ -117017,12 +122544,7 @@ static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){ if( !zVal ){ return SQLITE_NOMEM; }else if( nVal==8 && 0==sqlite3_strnicmp(zVal, "optimize", 8) ){ - rc = fts3SegmentMerge(p, -1); - if( rc==SQLITE_DONE ){ - rc = SQLITE_OK; - }else{ - sqlite3Fts3PendingTermsClear(p); - } + rc = fts3DoOptimize(p, 0); #ifdef SQLITE_TEST }else if( nVal>9 && 0==sqlite3_strnicmp(zVal, "nodesize=", 9) ){ p->nNodeSize = atoi(&zVal[9]); @@ -117035,57 +122557,19 @@ static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){ rc = SQLITE_ERROR; } - sqlite3Fts3SegmentsClose(p); return rc; } /* -** Return the deferred doclist associated with deferred token pDeferred. -** This function assumes that sqlite3Fts3CacheDeferredDoclists() has already -** been called to allocate and populate the doclist. -*/ -SQLITE_PRIVATE char *sqlite3Fts3DeferredDoclist(Fts3DeferredToken *pDeferred, int *pnByte){ - if( pDeferred->pList ){ - *pnByte = pDeferred->pList->nData; - return pDeferred->pList->aData; - } - *pnByte = 0; - return 0; -} - -/* -** Helper fucntion for FreeDeferredDoclists(). This function removes all -** references to deferred doclists from within the tree of Fts3Expr -** structures headed by -*/ -static void fts3DeferredDoclistClear(Fts3Expr *pExpr){ - if( pExpr ){ - fts3DeferredDoclistClear(pExpr->pLeft); - fts3DeferredDoclistClear(pExpr->pRight); - if( pExpr->isLoaded ){ - sqlite3_free(pExpr->aDoclist); - pExpr->isLoaded = 0; - pExpr->aDoclist = 0; - pExpr->nDoclist = 0; - pExpr->pCurrent = 0; - pExpr->iCurrent = 0; - } - } -} - -/* ** Delete all cached deferred doclists. Deferred doclists are cached ** (allocated) by the sqlite3Fts3CacheDeferredDoclists() function. */ SQLITE_PRIVATE void sqlite3Fts3FreeDeferredDoclists(Fts3Cursor *pCsr){ Fts3DeferredToken *pDef; for(pDef=pCsr->pDeferred; pDef; pDef=pDef->pNext){ - sqlite3_free(pDef->pList); + fts3PendingListDelete(pDef->pList); pDef->pList = 0; } - if( pCsr->pDeferred ){ - fts3DeferredDoclistClear(pCsr->pExpr); - } } /* @@ -117097,7 +122581,7 @@ SQLITE_PRIVATE void sqlite3Fts3FreeDeferredTokens(Fts3Cursor *pCsr){ Fts3DeferredToken *pNext; for(pDef=pCsr->pDeferred; pDef; pDef=pNext){ pNext = pDef->pNext; - sqlite3_free(pDef->pList); + fts3PendingListDelete(pDef->pList); sqlite3_free(pDef); } pCsr->pDeferred = 0; @@ -117162,6 +122646,33 @@ SQLITE_PRIVATE int sqlite3Fts3CacheDeferredDoclists(Fts3Cursor *pCsr){ return rc; } +SQLITE_PRIVATE int sqlite3Fts3DeferredTokenList( + Fts3DeferredToken *p, + char **ppData, + int *pnData +){ + char *pRet; + int nSkip; + sqlite3_int64 dummy; + + *ppData = 0; + *pnData = 0; + + if( p->pList==0 ){ + return SQLITE_OK; + } + + pRet = (char *)sqlite3_malloc(p->pList->nData); + if( !pRet ) return SQLITE_NOMEM; + + nSkip = sqlite3Fts3GetVarint(p->pList->aData, &dummy); + *pnData = p->pList->nData - nSkip; + *ppData = pRet; + + memcpy(pRet, &p->pList->aData[nSkip], *pnData); + return SQLITE_OK; +} + /* ** Add an entry for token pToken to the pCsr->pDeferred list. */ @@ -117187,6 +122698,40 @@ SQLITE_PRIVATE int sqlite3Fts3DeferToken( return SQLITE_OK; } +/* +** SQLite value pRowid contains the rowid of a row that may or may not be +** present in the FTS3 table. If it is, delete it and adjust the contents +** of subsiduary data structures accordingly. +*/ +static int fts3DeleteByRowid( + Fts3Table *p, + sqlite3_value *pRowid, + int *pnDoc, + u32 *aSzDel +){ + int isEmpty = 0; + int rc = fts3IsEmpty(p, pRowid, &isEmpty); + if( rc==SQLITE_OK ){ + if( isEmpty ){ + /* Deleting this row means the whole table is empty. In this case + ** delete the contents of all three tables and throw away any + ** data in the pendingTerms hash table. */ + rc = fts3DeleteAll(p); + *pnDoc = *pnDoc - 1; + }else{ + sqlite3_int64 iRemove = sqlite3_value_int64(pRowid); + rc = fts3PendingTermsDocid(p, iRemove); + fts3DeleteTerms(&rc, p, pRowid, aSzDel); + fts3SqlExec(&rc, p, SQL_DELETE_CONTENT, &pRowid); + if( sqlite3_changes(p->db) ) *pnDoc = *pnDoc - 1; + if( p->bHasDocsize ){ + fts3SqlExec(&rc, p, SQL_DELETE_DOCSIZE, &pRowid); + } + } + } + + return rc; +} /* ** This function does the work for the xUpdate method of FTS3 virtual @@ -117202,49 +122747,97 @@ SQLITE_PRIVATE int sqlite3Fts3UpdateMethod( int rc = SQLITE_OK; /* Return Code */ int isRemove = 0; /* True for an UPDATE or DELETE */ sqlite3_int64 iRemove = 0; /* Rowid removed by UPDATE or DELETE */ - u32 *aSzIns; /* Sizes of inserted documents */ + u32 *aSzIns = 0; /* Sizes of inserted documents */ u32 *aSzDel; /* Sizes of deleted documents */ int nChng = 0; /* Net change in number of documents */ + int bInsertDone = 0; assert( p->pSegments==0 ); + /* Check for a "special" INSERT operation. One of the form: + ** + ** INSERT INTO xyz(xyz) VALUES('command'); + */ + if( nArg>1 + && sqlite3_value_type(apVal[0])==SQLITE_NULL + && sqlite3_value_type(apVal[p->nColumn+2])!=SQLITE_NULL + ){ + rc = fts3SpecialInsert(p, apVal[p->nColumn+2]); + goto update_out; + } + /* Allocate space to hold the change in document sizes */ aSzIns = sqlite3_malloc( sizeof(aSzIns[0])*(p->nColumn+1)*2 ); - if( aSzIns==0 ) return SQLITE_NOMEM; + if( aSzIns==0 ){ + rc = SQLITE_NOMEM; + goto update_out; + } aSzDel = &aSzIns[p->nColumn+1]; memset(aSzIns, 0, sizeof(aSzIns[0])*(p->nColumn+1)*2); - /* If this is a DELETE or UPDATE operation, remove the old record. */ - if( sqlite3_value_type(apVal[0])!=SQLITE_NULL ){ - int isEmpty = 0; - rc = fts3IsEmpty(p, apVal, &isEmpty); - if( rc==SQLITE_OK ){ - if( isEmpty ){ - /* Deleting this row means the whole table is empty. In this case - ** delete the contents of all three tables and throw away any - ** data in the pendingTerms hash table. - */ - rc = fts3DeleteAll(p); + /* If this is an INSERT operation, or an UPDATE that modifies the rowid + ** value, then this operation requires constraint handling. + ** + ** If the on-conflict mode is REPLACE, this means that the existing row + ** should be deleted from the database before inserting the new row. Or, + ** if the on-conflict mode is other than REPLACE, then this method must + ** detect the conflict and return SQLITE_CONSTRAINT before beginning to + ** modify the database file. + */ + if( nArg>1 ){ + /* Find the value object that holds the new rowid value. */ + sqlite3_value *pNewRowid = apVal[3+p->nColumn]; + if( sqlite3_value_type(pNewRowid)==SQLITE_NULL ){ + pNewRowid = apVal[1]; + } + + if( sqlite3_value_type(pNewRowid)!=SQLITE_NULL && ( + sqlite3_value_type(apVal[0])==SQLITE_NULL + || sqlite3_value_int64(apVal[0])!=sqlite3_value_int64(pNewRowid) + )){ + /* The new rowid is not NULL (in this case the rowid will be + ** automatically assigned and there is no chance of a conflict), and + ** the statement is either an INSERT or an UPDATE that modifies the + ** rowid column. So if the conflict mode is REPLACE, then delete any + ** existing row with rowid=pNewRowid. + ** + ** Or, if the conflict mode is not REPLACE, insert the new record into + ** the %_content table. If we hit the duplicate rowid constraint (or any + ** other error) while doing so, return immediately. + ** + ** This branch may also run if pNewRowid contains a value that cannot + ** be losslessly converted to an integer. In this case, the eventual + ** call to fts3InsertData() (either just below or further on in this + ** function) will return SQLITE_MISMATCH. If fts3DeleteByRowid is + ** invoked, it will delete zero rows (since no row will have + ** docid=$pNewRowid if $pNewRowid is not an integer value). + */ + if( sqlite3_vtab_on_conflict(p->db)==SQLITE_REPLACE ){ + rc = fts3DeleteByRowid(p, pNewRowid, &nChng, aSzDel); }else{ - isRemove = 1; - iRemove = sqlite3_value_int64(apVal[0]); - rc = fts3PendingTermsDocid(p, iRemove); - fts3DeleteTerms(&rc, p, apVal, aSzDel); - fts3SqlExec(&rc, p, SQL_DELETE_CONTENT, apVal); - if( p->bHasDocsize ){ - fts3SqlExec(&rc, p, SQL_DELETE_DOCSIZE, apVal); - } - nChng--; + rc = fts3InsertData(p, apVal, pRowid); + bInsertDone = 1; } } - }else if( sqlite3_value_type(apVal[p->nColumn+2])!=SQLITE_NULL ){ - sqlite3_free(aSzIns); - return fts3SpecialInsert(p, apVal[p->nColumn+2]); + } + if( rc!=SQLITE_OK ){ + goto update_out; + } + + /* If this is a DELETE or UPDATE operation, remove the old record. */ + if( sqlite3_value_type(apVal[0])!=SQLITE_NULL ){ + assert( sqlite3_value_type(apVal[0])==SQLITE_INTEGER ); + rc = fts3DeleteByRowid(p, apVal[0], &nChng, aSzDel); + isRemove = 1; + iRemove = sqlite3_value_int64(apVal[0]); } /* If this is an INSERT or UPDATE operation, insert the new record. */ if( nArg>1 && rc==SQLITE_OK ){ - rc = fts3InsertData(p, apVal, pRowid); + if( bInsertDone==0 ){ + rc = fts3InsertData(p, apVal, pRowid); + if( rc==SQLITE_CONSTRAINT ) rc = SQLITE_CORRUPT_VTAB; + } if( rc==SQLITE_OK && (!isRemove || *pRowid!=iRemove) ){ rc = fts3PendingTermsDocid(p, *pRowid); } @@ -117261,6 +122854,7 @@ SQLITE_PRIVATE int sqlite3Fts3UpdateMethod( fts3UpdateDocTotals(&rc, p, aSzIns, aSzDel, nChng); } + update_out: sqlite3_free(aSzIns); sqlite3Fts3SegmentsClose(p); return rc; @@ -117275,12 +122869,10 @@ SQLITE_PRIVATE int sqlite3Fts3Optimize(Fts3Table *p){ int rc; rc = sqlite3_exec(p->db, "SAVEPOINT fts3", 0, 0, 0); if( rc==SQLITE_OK ){ - rc = fts3SegmentMerge(p, -1); - if( rc==SQLITE_OK ){ - rc = sqlite3_exec(p->db, "RELEASE fts3", 0, 0, 0); - if( rc==SQLITE_OK ){ - sqlite3Fts3PendingTermsClear(p); - } + rc = fts3DoOptimize(p, 1); + if( rc==SQLITE_OK || rc==SQLITE_DONE ){ + int rc2 = sqlite3_exec(p->db, "RELEASE fts3", 0, 0, 0); + if( rc2!=SQLITE_OK ) rc = rc2; }else{ sqlite3_exec(p->db, "ROLLBACK TO fts3", 0, 0, 0); sqlite3_exec(p->db, "RELEASE fts3", 0, 0, 0); @@ -117470,92 +123062,24 @@ static int fts3ExprIterate( } /* -** The argument to this function is always a phrase node. Its doclist -** (Fts3Expr.aDoclist[]) and the doclists associated with all phrase nodes -** to the left of this one in the query tree have already been loaded. -** -** If this phrase node is part of a series of phrase nodes joined by -** NEAR operators (and is not the left-most of said series), then elements are -** removed from the phrases doclist consistent with the NEAR restriction. If -** required, elements may be removed from the doclists of phrases to the -** left of this one that are part of the same series of NEAR operator -** connected phrases. -** -** If an OOM error occurs, SQLITE_NOMEM is returned. Otherwise, SQLITE_OK. -*/ -static int fts3ExprNearTrim(Fts3Expr *pExpr){ - int rc = SQLITE_OK; - Fts3Expr *pParent = pExpr->pParent; - - assert( pExpr->eType==FTSQUERY_PHRASE ); - while( rc==SQLITE_OK - && pParent - && pParent->eType==FTSQUERY_NEAR - && pParent->pRight==pExpr - ){ - /* This expression (pExpr) is the right-hand-side of a NEAR operator. - ** Find the expression to the left of the same operator. - */ - int nNear = pParent->nNear; - Fts3Expr *pLeft = pParent->pLeft; - - if( pLeft->eType!=FTSQUERY_PHRASE ){ - assert( pLeft->eType==FTSQUERY_NEAR ); - assert( pLeft->pRight->eType==FTSQUERY_PHRASE ); - pLeft = pLeft->pRight; - } - - rc = sqlite3Fts3ExprNearTrim(pLeft, pExpr, nNear); - - pExpr = pLeft; - pParent = pExpr->pParent; - } - - return rc; -} - -/* ** This is an fts3ExprIterate() callback used while loading the doclists ** for each phrase into Fts3Expr.aDoclist[]/nDoclist. See also ** fts3ExprLoadDoclists(). */ -static int fts3ExprLoadDoclistsCb1(Fts3Expr *pExpr, int iPhrase, void *ctx){ +static int fts3ExprLoadDoclistsCb(Fts3Expr *pExpr, int iPhrase, void *ctx){ int rc = SQLITE_OK; + Fts3Phrase *pPhrase = pExpr->pPhrase; LoadDoclistCtx *p = (LoadDoclistCtx *)ctx; UNUSED_PARAMETER(iPhrase); p->nPhrase++; - p->nToken += pExpr->pPhrase->nToken; - - if( pExpr->isLoaded==0 ){ - rc = sqlite3Fts3ExprLoadDoclist(p->pCsr, pExpr); - pExpr->isLoaded = 1; - if( rc==SQLITE_OK ){ - rc = fts3ExprNearTrim(pExpr); - } - } + p->nToken += pPhrase->nToken; return rc; } /* -** This is an fts3ExprIterate() callback used while loading the doclists -** for each phrase into Fts3Expr.aDoclist[]/nDoclist. See also -** fts3ExprLoadDoclists(). -*/ -static int fts3ExprLoadDoclistsCb2(Fts3Expr *pExpr, int iPhrase, void *ctx){ - UNUSED_PARAMETER(iPhrase); - UNUSED_PARAMETER(ctx); - if( pExpr->aDoclist ){ - pExpr->pCurrent = pExpr->aDoclist; - pExpr->iCurrent = 0; - pExpr->pCurrent += sqlite3Fts3GetVarint(pExpr->pCurrent, &pExpr->iCurrent); - } - return SQLITE_OK; -} - -/* ** Load the doclists for each phrase in the query associated with FTS3 cursor ** pCsr. ** @@ -117573,10 +123097,7 @@ static int fts3ExprLoadDoclists( int rc; /* Return Code */ LoadDoclistCtx sCtx = {0,0,0}; /* Context for fts3ExprIterate() */ sCtx.pCsr = pCsr; - rc = fts3ExprIterate(pCsr->pExpr, fts3ExprLoadDoclistsCb1, (void *)&sCtx); - if( rc==SQLITE_OK ){ - (void)fts3ExprIterate(pCsr->pExpr, fts3ExprLoadDoclistsCb2, 0); - } + rc = fts3ExprIterate(pCsr->pExpr, fts3ExprLoadDoclistsCb, (void *)&sCtx); if( pnPhrase ) *pnPhrase = sCtx.nPhrase; if( pnToken ) *pnToken = sCtx.nToken; return rc; @@ -117727,7 +123248,7 @@ static int fts3SnippetFindPositions(Fts3Expr *pExpr, int iPhrase, void *ctx){ pPhrase->nToken = pExpr->pPhrase->nToken; - pCsr = sqlite3Fts3FindPositions(pExpr, p->pCsr->iPrevId, p->iCol); + pCsr = sqlite3Fts3EvalPhrasePoslist(p->pCsr, pExpr, p->iCol); if( pCsr ){ int iFirst = 0; pPhrase->pList = pCsr; @@ -118084,26 +123605,6 @@ static int fts3ColumnlistCount(char **ppCollist){ return nEntry; } -static void fts3LoadColumnlistCounts(char **pp, u32 *aOut, int isGlobal){ - char *pCsr = *pp; - while( *pCsr ){ - int nHit; - sqlite3_int64 iCol = 0; - if( *pCsr==0x01 ){ - pCsr++; - pCsr += sqlite3Fts3GetVarint(pCsr, &iCol); - } - nHit = fts3ColumnlistCount(&pCsr); - assert( nHit>0 ); - if( isGlobal ){ - aOut[iCol*3+1]++; - } - aOut[iCol*3] += nHit; - } - pCsr++; - *pp = pCsr; -} - /* ** fts3ExprIterate() callback used to collect the "global" matchinfo stats ** for a single query. @@ -118137,48 +123638,9 @@ static int fts3ExprGlobalHitsCb( void *pCtx /* Pointer to MatchInfo structure */ ){ MatchInfo *p = (MatchInfo *)pCtx; - Fts3Cursor *pCsr = p->pCursor; - char *pIter; - char *pEnd; - char *pFree = 0; - u32 *aOut = &p->aMatchinfo[3*iPhrase*p->nCol]; - - assert( pExpr->isLoaded ); - assert( pExpr->eType==FTSQUERY_PHRASE ); - - if( pCsr->pDeferred ){ - Fts3Phrase *pPhrase = pExpr->pPhrase; - int ii; - for(ii=0; ii<pPhrase->nToken; ii++){ - if( pPhrase->aToken[ii].bFulltext ) break; - } - if( ii<pPhrase->nToken ){ - int nFree = 0; - int rc = sqlite3Fts3ExprLoadFtDoclist(pCsr, pExpr, &pFree, &nFree); - if( rc!=SQLITE_OK ) return rc; - pIter = pFree; - pEnd = &pFree[nFree]; - }else{ - int iCol; /* Column index */ - for(iCol=0; iCol<p->nCol; iCol++){ - aOut[iCol*3 + 1] = (u32)p->nDoc; - aOut[iCol*3 + 2] = (u32)p->nDoc; - } - return SQLITE_OK; - } - }else{ - pIter = pExpr->aDoclist; - pEnd = &pExpr->aDoclist[pExpr->nDoclist]; - } - - /* Fill in the global hit count matrix row for this phrase. */ - while( pIter<pEnd ){ - while( *pIter++ & 0x80 ); /* Skip past docid. */ - fts3LoadColumnlistCounts(&pIter, &aOut[1], 1); - } - - sqlite3_free(pFree); - return SQLITE_OK; + return sqlite3Fts3EvalPhraseStats( + p->pCursor, pExpr, &p->aMatchinfo[3*iPhrase*p->nCol] + ); } /* @@ -118192,17 +123654,16 @@ static int fts3ExprLocalHitsCb( void *pCtx /* Pointer to MatchInfo structure */ ){ MatchInfo *p = (MatchInfo *)pCtx; + int iStart = iPhrase * p->nCol * 3; + int i; - if( pExpr->aDoclist ){ + for(i=0; i<p->nCol; i++){ char *pCsr; - int iStart = iPhrase * p->nCol * 3; - int i; - - for(i=0; i<p->nCol; i++) p->aMatchinfo[iStart+i*3] = 0; - - pCsr = sqlite3Fts3FindPositions(pExpr, p->pCursor->iPrevId, -1); + pCsr = sqlite3Fts3EvalPhrasePoslist(p->pCursor, pExpr, i); if( pCsr ){ - fts3LoadColumnlistCounts(&pCsr, &p->aMatchinfo[iStart], 0); + p->aMatchinfo[iStart+i*3] = fts3ColumnlistCount(&pCsr); + }else{ + p->aMatchinfo[iStart+i*3] = 0; } } @@ -118268,9 +123729,11 @@ static int fts3MatchinfoSelectDoctotal( if( rc!=SQLITE_OK ) return rc; } pStmt = *ppStmt; + assert( sqlite3_data_count(pStmt)==1 ); a = sqlite3_column_blob(pStmt, 0); a += sqlite3Fts3GetVarint(a, &nDoc); + if( nDoc==0 ) return SQLITE_CORRUPT_VTAB; *pnDoc = (u32)nDoc; if( paLen ) *paLen = a; @@ -118286,9 +123749,8 @@ static int fts3MatchinfoSelectDoctotal( typedef struct LcsIterator LcsIterator; struct LcsIterator { Fts3Expr *pExpr; /* Pointer to phrase expression */ - char *pRead; /* Cursor used to iterate through aDoclist */ int iPosOffset; /* Tokens count up to end of this phrase */ - int iCol; /* Current column number */ + char *pRead; /* Cursor used to iterate through aDoclist */ int iPos; /* Current position */ }; @@ -118319,17 +123781,10 @@ static int fts3LcsIteratorAdvance(LcsIterator *pIter){ int rc = 0; pRead += sqlite3Fts3GetVarint(pRead, &iRead); - if( iRead==0 ){ - pIter->iCol = LCS_ITERATOR_FINISHED; + if( iRead==0 || iRead==1 ){ + pRead = 0; rc = 1; }else{ - if( iRead==1 ){ - pRead += sqlite3Fts3GetVarint(pRead, &iRead); - pIter->iCol = (int)iRead; - pIter->iPos = pIter->iPosOffset; - pRead += sqlite3Fts3GetVarint(pRead, &iRead); - rc = 1; - } pIter->iPos += (int)(iRead-2); } @@ -118361,42 +123816,34 @@ static int fts3MatchinfoLcs(Fts3Cursor *pCsr, MatchInfo *pInfo){ if( !aIter ) return SQLITE_NOMEM; memset(aIter, 0, sizeof(LcsIterator) * pCsr->nPhrase); (void)fts3ExprIterate(pCsr->pExpr, fts3MatchinfoLcsCb, (void*)aIter); + for(i=0; i<pInfo->nPhrase; i++){ LcsIterator *pIter = &aIter[i]; nToken -= pIter->pExpr->pPhrase->nToken; pIter->iPosOffset = nToken; - pIter->pRead = sqlite3Fts3FindPositions(pIter->pExpr, pCsr->iPrevId, -1); - if( pIter->pRead ){ - pIter->iPos = pIter->iPosOffset; - fts3LcsIteratorAdvance(&aIter[i]); - }else{ - pIter->iCol = LCS_ITERATOR_FINISHED; - } } for(iCol=0; iCol<pInfo->nCol; iCol++){ int nLcs = 0; /* LCS value for this column */ int nLive = 0; /* Number of iterators in aIter not at EOF */ - /* Loop through the iterators in aIter[]. Set nLive to the number of - ** iterators that point to a position-list corresponding to column iCol. - */ for(i=0; i<pInfo->nPhrase; i++){ - assert( aIter[i].iCol>=iCol ); - if( aIter[i].iCol==iCol ) nLive++; + LcsIterator *pIt = &aIter[i]; + pIt->pRead = sqlite3Fts3EvalPhrasePoslist(pCsr, pIt->pExpr, iCol); + if( pIt->pRead ){ + pIt->iPos = pIt->iPosOffset; + fts3LcsIteratorAdvance(&aIter[i]); + nLive++; + } } - /* The following loop runs until all iterators in aIter[] have finished - ** iterating through positions in column iCol. Exactly one of the - ** iterators is advanced each time the body of the loop is run. - */ while( nLive>0 ){ LcsIterator *pAdv = 0; /* The iterator to advance by one position */ int nThisLcs = 0; /* LCS for the current iterator positions */ for(i=0; i<pInfo->nPhrase; i++){ LcsIterator *pIter = &aIter[i]; - if( iCol!=pIter->iCol ){ + if( pIter->pRead==0 ){ /* This iterator is already at EOF for this column. */ nThisLcs = 0; }else{ @@ -118462,7 +123909,7 @@ static int fts3MatchinfoValues( case FTS3_MATCHINFO_NDOC: if( bGlobal ){ - sqlite3_int64 nDoc; + sqlite3_int64 nDoc = 0; rc = fts3MatchinfoSelectDoctotal(pTab, &pSelect, &nDoc, 0); pInfo->aMatchinfo[0] = (u32)nDoc; } @@ -118477,9 +123924,11 @@ static int fts3MatchinfoValues( if( rc==SQLITE_OK ){ int iCol; for(iCol=0; iCol<pInfo->nCol; iCol++){ + u32 iVal; sqlite3_int64 nToken; a += sqlite3Fts3GetVarint(a, &nToken); - pInfo->aMatchinfo[iCol] = (u32)(((u32)(nToken&0xffffffff)+nDoc/2)/nDoc); + iVal = (u32)(((u32)(nToken&0xffffffff)+nDoc/2)/nDoc); + pInfo->aMatchinfo[iCol] = iVal; } } } @@ -118716,6 +124165,7 @@ struct TermOffset { }; struct TermOffsetCtx { + Fts3Cursor *pCsr; int iCol; /* Column of table to populate aTerm for */ int iTerm; sqlite3_int64 iDocid; @@ -118733,7 +124183,7 @@ static int fts3ExprTermOffsetInit(Fts3Expr *pExpr, int iPhrase, void *ctx){ int iPos = 0; /* First position in position-list */ UNUSED_PARAMETER(iPhrase); - pList = sqlite3Fts3FindPositions(pExpr, p->iDocid, p->iCol); + pList = sqlite3Fts3EvalPhrasePoslist(p->pCsr, pExpr, p->iCol); nTerm = pExpr->pPhrase->nToken; if( pList ){ fts3GetDeltaPosition(&pList, &iPos); @@ -118786,6 +124236,7 @@ SQLITE_PRIVATE void sqlite3Fts3Offsets( goto offsets_out; } sCtx.iDocid = pCsr->iPrevId; + sCtx.pCsr = pCsr; /* Loop through the table columns, appending offset information to ** string-buffer res for each column. @@ -118861,7 +124312,7 @@ SQLITE_PRIVATE void sqlite3Fts3Offsets( ); rc = fts3StringAppend(&res, aBuffer, -1); }else if( rc==SQLITE_DONE ){ - rc = SQLITE_CORRUPT; + rc = SQLITE_CORRUPT_VTAB; } } } @@ -119449,17 +124900,17 @@ nodeAcquire( if( pNode && iNode==1 ){ pRtree->iDepth = readInt16(pNode->zData); if( pRtree->iDepth>RTREE_MAX_DEPTH ){ - rc = SQLITE_CORRUPT; + rc = SQLITE_CORRUPT_VTAB; } } /* If no error has occurred so far, check if the "number of entries" ** field on the node is too large. If so, set the return code to - ** SQLITE_CORRUPT. + ** SQLITE_CORRUPT_VTAB. */ if( pNode && rc==SQLITE_OK ){ if( NCELL(pNode)>((pRtree->iNodeSize-4)/pRtree->nBytesPerCell) ){ - rc = SQLITE_CORRUPT; + rc = SQLITE_CORRUPT_VTAB; } } @@ -119467,7 +124918,7 @@ nodeAcquire( if( pNode!=0 ){ nodeHashInsert(pRtree, pNode); }else{ - rc = SQLITE_CORRUPT; + rc = SQLITE_CORRUPT_VTAB; } *ppNode = pNode; }else{ @@ -119994,7 +125445,7 @@ static int nodeRowidIndex( return SQLITE_OK; } } - return SQLITE_CORRUPT; + return SQLITE_CORRUPT_VTAB; } /* @@ -120200,7 +125651,7 @@ static int rtreeFilter( rc = SQLITE_NOMEM; }else{ memset(pCsr->aConstraint, 0, sizeof(RtreeConstraint)*argc); - assert( (idxStr==0 && argc==0) || strlen(idxStr)==argc*2 ); + assert( (idxStr==0 && argc==0) || (int)strlen(idxStr)==argc*2 ); for(ii=0; ii<argc; ii++){ RtreeConstraint *p = &pCsr->aConstraint[ii]; p->op = idxStr[ii*2]; @@ -120285,7 +125736,7 @@ static int rtreeFilter( */ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ int rc = SQLITE_OK; - int ii, cCol; + int ii; int iIdx = 0; char zIdxStr[RTREE_MAX_DIMENSIONS*8+1]; @@ -120293,7 +125744,7 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ UNUSED_PARAMETER(tab); assert( pIdxInfo->idxStr==0 ); - for(ii=0; ii<pIdxInfo->nConstraint; ii++){ + for(ii=0; ii<pIdxInfo->nConstraint && iIdx<(int)(sizeof(zIdxStr)-1); ii++){ struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[ii]; if( p->usable && p->iColumn==0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ ){ @@ -120317,9 +125768,7 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ } if( p->usable && (p->iColumn>0 || p->op==SQLITE_INDEX_CONSTRAINT_MATCH) ){ - int j, opmsk; - static const unsigned char compatible[] = { 0, 0, 1, 1, 2, 2 }; - u8 op = 0; + u8 op; switch( p->op ){ case SQLITE_INDEX_CONSTRAINT_EQ: op = RTREE_EQ; break; case SQLITE_INDEX_CONSTRAINT_GT: op = RTREE_GT; break; @@ -120331,37 +125780,10 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ op = RTREE_MATCH; break; } - assert( op!=0 ); - - /* Make sure this particular constraint has not been used before. - ** If it has been used before, ignore it. - ** - ** A <= or < can be used if there is a prior >= or >. - ** A >= or > can be used if there is a prior < or <=. - ** A <= or < is disqualified if there is a prior <=, <, or ==. - ** A >= or > is disqualified if there is a prior >=, >, or ==. - ** A == is disqualifed if there is any prior constraint. - */ - assert( compatible[RTREE_EQ & 7]==0 ); - assert( compatible[RTREE_LT & 7]==1 ); - assert( compatible[RTREE_LE & 7]==1 ); - assert( compatible[RTREE_GT & 7]==2 ); - assert( compatible[RTREE_GE & 7]==2 ); - cCol = p->iColumn - 1 + 'a'; - opmsk = compatible[op & 7]; - for(j=0; j<iIdx; j+=2){ - if( zIdxStr[j+1]==cCol && (compatible[zIdxStr[j] & 7] & opmsk)!=0 ){ - op = 0; - break; - } - } - if( op ){ - assert( iIdx<sizeof(zIdxStr)-1 ); - zIdxStr[iIdx++] = op; - zIdxStr[iIdx++] = cCol; - pIdxInfo->aConstraintUsage[ii].argvIndex = (iIdx/2); - pIdxInfo->aConstraintUsage[ii].omit = 1; - } + zIdxStr[iIdx++] = op; + zIdxStr[iIdx++] = p->iColumn - 1 + 'a'; + pIdxInfo->aConstraintUsage[ii].argvIndex = (iIdx/2); + pIdxInfo->aConstraintUsage[ii].omit = 1; } } @@ -120382,7 +125804,7 @@ static float cellArea(Rtree *pRtree, RtreeCell *p){ float area = 1.0; int ii; for(ii=0; ii<(pRtree->nDim*2); ii+=2){ - area = area * (DCOORD(p->aCoord[ii+1]) - DCOORD(p->aCoord[ii])); + area = (float)(area * (DCOORD(p->aCoord[ii+1]) - DCOORD(p->aCoord[ii]))); } return area; } @@ -120395,7 +125817,7 @@ static float cellMargin(Rtree *pRtree, RtreeCell *p){ float margin = 0.0; int ii; for(ii=0; ii<(pRtree->nDim*2); ii+=2){ - margin += (DCOORD(p->aCoord[ii+1]) - DCOORD(p->aCoord[ii])); + margin += (float)(DCOORD(p->aCoord[ii+1]) - DCOORD(p->aCoord[ii])); } return margin; } @@ -120480,7 +125902,7 @@ static float cellOverlap( o = 0.0; break; }else{ - o = o * (x2-x1); + o = o * (float)(x2-x1); } } overlap += o; @@ -120499,12 +125921,12 @@ static float cellOverlapEnlargement( int nCell, int iExclude ){ - float before; - float after; + double before; + double after; before = cellOverlap(pRtree, p, aCell, nCell, iExclude); cellUnion(pRtree, p, pInsert); after = cellOverlap(pRtree, p, aCell, nCell, iExclude); - return after-before; + return (float)(after-before); } #endif @@ -120526,11 +125948,11 @@ static int ChooseLeaf( for(ii=0; rc==SQLITE_OK && ii<(pRtree->iDepth-iHeight); ii++){ int iCell; - sqlite3_int64 iBest; + sqlite3_int64 iBest = 0; - float fMinGrowth; - float fMinArea; - float fMinOverlap; + float fMinGrowth = 0.0; + float fMinArea = 0.0; + float fMinOverlap = 0.0; int nCell = NCELL(pNode); RtreeCell cell; @@ -120618,7 +126040,7 @@ static int AdjustTree( int iCell; if( nodeParentIndex(pRtree, p, &iCell) ){ - return SQLITE_CORRUPT; + return SQLITE_CORRUPT_VTAB; } nodeGetCell(pRtree, pParent, iCell, &cell); @@ -120960,9 +126382,9 @@ static int splitNodeStartree( int *aSpare; int ii; - int iBestDim; - int iBestSplit; - float fBestMargin; + int iBestDim = 0; + int iBestSplit = 0; + float fBestMargin = 0.0; int nByte = (pRtree->nDim+1)*(sizeof(int*)+nCell*sizeof(int)); @@ -120984,9 +126406,9 @@ static int splitNodeStartree( for(ii=0; ii<pRtree->nDim; ii++){ float margin = 0.0; - float fBestOverlap; - float fBestArea; - int iBestLeft; + float fBestOverlap = 0.0; + float fBestArea = 0.0; + int iBestLeft = 0; int nLeft; for( @@ -121290,7 +126712,7 @@ static int fixLeafParent(Rtree *pRtree, RtreeNode *pLeaf){ } rc = sqlite3_reset(pRtree->pReadParent); if( rc==SQLITE_OK ) rc = rc2; - if( rc==SQLITE_OK && !pChild->pParent ) rc = SQLITE_CORRUPT; + if( rc==SQLITE_OK && !pChild->pParent ) rc = SQLITE_CORRUPT_VTAB; pChild = pChild->pParent; } return rc; @@ -121301,7 +126723,7 @@ static int deleteCell(Rtree *, RtreeNode *, int, int); static int removeNode(Rtree *pRtree, RtreeNode *pNode, int iHeight){ int rc; int rc2; - RtreeNode *pParent; + RtreeNode *pParent = 0; int iCell; assert( pNode->nRef==1 ); @@ -121449,19 +126871,19 @@ static int Reinsert( } aOrder[ii] = ii; for(iDim=0; iDim<pRtree->nDim; iDim++){ - aCenterCoord[iDim] += DCOORD(aCell[ii].aCoord[iDim*2]); - aCenterCoord[iDim] += DCOORD(aCell[ii].aCoord[iDim*2+1]); + aCenterCoord[iDim] += (float)DCOORD(aCell[ii].aCoord[iDim*2]); + aCenterCoord[iDim] += (float)DCOORD(aCell[ii].aCoord[iDim*2+1]); } } for(iDim=0; iDim<pRtree->nDim; iDim++){ - aCenterCoord[iDim] = aCenterCoord[iDim]/((float)nCell*2.0); + aCenterCoord[iDim] = (float)(aCenterCoord[iDim]/((float)nCell*2.0)); } for(ii=0; ii<nCell; ii++){ aDistance[ii] = 0.0; for(iDim=0; iDim<pRtree->nDim; iDim++){ - float coord = DCOORD(aCell[ii].aCoord[iDim*2+1]) - - DCOORD(aCell[ii].aCoord[iDim*2]); + float coord = (float)(DCOORD(aCell[ii].aCoord[iDim*2+1]) - + DCOORD(aCell[ii].aCoord[iDim*2])); aDistance[ii] += (coord-aCenterCoord[iDim])*(coord-aCenterCoord[iDim]); } } @@ -121560,10 +126982,10 @@ static int reinsertNodeContent(Rtree *pRtree, RtreeNode *pNode){ /* Find a node to store this cell in. pNode->iNode currently contains ** the height of the sub-tree headed by the cell. */ - rc = ChooseLeaf(pRtree, &cell, pNode->iNode, &pInsert); + rc = ChooseLeaf(pRtree, &cell, (int)pNode->iNode, &pInsert); if( rc==SQLITE_OK ){ int rc2; - rc = rtreeInsertCell(pRtree, pInsert, &cell, pNode->iNode); + rc = rtreeInsertCell(pRtree, pInsert, &cell, (int)pNode->iNode); rc2 = nodeRelease(pRtree, pInsert); if( rc==SQLITE_OK ){ rc = rc2; @@ -121587,113 +127009,119 @@ static int newRowid(Rtree *pRtree, i64 *piRowid){ } /* -** The xUpdate method for rtree module virtual tables. +** Remove the entry with rowid=iDelete from the r-tree structure. */ -static int rtreeUpdate( - sqlite3_vtab *pVtab, - int nData, - sqlite3_value **azData, - sqlite_int64 *pRowid -){ - Rtree *pRtree = (Rtree *)pVtab; - int rc = SQLITE_OK; +static int rtreeDeleteRowid(Rtree *pRtree, sqlite3_int64 iDelete){ + int rc; /* Return code */ + RtreeNode *pLeaf; /* Leaf node containing record iDelete */ + int iCell; /* Index of iDelete cell in pLeaf */ + RtreeNode *pRoot; /* Root node of rtree structure */ - rtreeReference(pRtree); - assert(nData>=1); + /* Obtain a reference to the root node to initialise Rtree.iDepth */ + rc = nodeAcquire(pRtree, 1, 0, &pRoot); - /* If azData[0] is not an SQL NULL value, it is the rowid of a - ** record to delete from the r-tree table. The following block does - ** just that. + /* Obtain a reference to the leaf node that contains the entry + ** about to be deleted. */ - if( sqlite3_value_type(azData[0])!=SQLITE_NULL ){ - i64 iDelete; /* The rowid to delete */ - RtreeNode *pLeaf; /* Leaf node containing record iDelete */ - int iCell; /* Index of iDelete cell in pLeaf */ - RtreeNode *pRoot; - - /* Obtain a reference to the root node to initialise Rtree.iDepth */ - rc = nodeAcquire(pRtree, 1, 0, &pRoot); + if( rc==SQLITE_OK ){ + rc = findLeafNode(pRtree, iDelete, &pLeaf); + } - /* Obtain a reference to the leaf node that contains the entry - ** about to be deleted. - */ + /* Delete the cell in question from the leaf node. */ + if( rc==SQLITE_OK ){ + int rc2; + rc = nodeRowidIndex(pRtree, pLeaf, iDelete, &iCell); if( rc==SQLITE_OK ){ - iDelete = sqlite3_value_int64(azData[0]); - rc = findLeafNode(pRtree, iDelete, &pLeaf); + rc = deleteCell(pRtree, pLeaf, iCell, 0); } - - /* Delete the cell in question from the leaf node. */ + rc2 = nodeRelease(pRtree, pLeaf); if( rc==SQLITE_OK ){ - int rc2; - rc = nodeRowidIndex(pRtree, pLeaf, iDelete, &iCell); - if( rc==SQLITE_OK ){ - rc = deleteCell(pRtree, pLeaf, iCell, 0); - } - rc2 = nodeRelease(pRtree, pLeaf); - if( rc==SQLITE_OK ){ - rc = rc2; - } + rc = rc2; } + } - /* Delete the corresponding entry in the <rtree>_rowid table. */ - if( rc==SQLITE_OK ){ - sqlite3_bind_int64(pRtree->pDeleteRowid, 1, iDelete); - sqlite3_step(pRtree->pDeleteRowid); - rc = sqlite3_reset(pRtree->pDeleteRowid); - } + /* Delete the corresponding entry in the <rtree>_rowid table. */ + if( rc==SQLITE_OK ){ + sqlite3_bind_int64(pRtree->pDeleteRowid, 1, iDelete); + sqlite3_step(pRtree->pDeleteRowid); + rc = sqlite3_reset(pRtree->pDeleteRowid); + } - /* Check if the root node now has exactly one child. If so, remove - ** it, schedule the contents of the child for reinsertion and - ** reduce the tree height by one. - ** - ** This is equivalent to copying the contents of the child into - ** the root node (the operation that Gutman's paper says to perform - ** in this scenario). - */ - if( rc==SQLITE_OK && pRtree->iDepth>0 && NCELL(pRoot)==1 ){ - int rc2; - RtreeNode *pChild; - i64 iChild = nodeGetRowid(pRtree, pRoot, 0); - rc = nodeAcquire(pRtree, iChild, pRoot, &pChild); - if( rc==SQLITE_OK ){ - rc = removeNode(pRtree, pChild, pRtree->iDepth-1); - } - rc2 = nodeRelease(pRtree, pChild); - if( rc==SQLITE_OK ) rc = rc2; - if( rc==SQLITE_OK ){ - pRtree->iDepth--; - writeInt16(pRoot->zData, pRtree->iDepth); - pRoot->isDirty = 1; - } + /* Check if the root node now has exactly one child. If so, remove + ** it, schedule the contents of the child for reinsertion and + ** reduce the tree height by one. + ** + ** This is equivalent to copying the contents of the child into + ** the root node (the operation that Gutman's paper says to perform + ** in this scenario). + */ + if( rc==SQLITE_OK && pRtree->iDepth>0 && NCELL(pRoot)==1 ){ + int rc2; + RtreeNode *pChild; + i64 iChild = nodeGetRowid(pRtree, pRoot, 0); + rc = nodeAcquire(pRtree, iChild, pRoot, &pChild); + if( rc==SQLITE_OK ){ + rc = removeNode(pRtree, pChild, pRtree->iDepth-1); } - - /* Re-insert the contents of any underfull nodes removed from the tree. */ - for(pLeaf=pRtree->pDeleted; pLeaf; pLeaf=pRtree->pDeleted){ - if( rc==SQLITE_OK ){ - rc = reinsertNodeContent(pRtree, pLeaf); - } - pRtree->pDeleted = pLeaf->pNext; - sqlite3_free(pLeaf); + rc2 = nodeRelease(pRtree, pChild); + if( rc==SQLITE_OK ) rc = rc2; + if( rc==SQLITE_OK ){ + pRtree->iDepth--; + writeInt16(pRoot->zData, pRtree->iDepth); + pRoot->isDirty = 1; } + } - /* Release the reference to the root node. */ + /* Re-insert the contents of any underfull nodes removed from the tree. */ + for(pLeaf=pRtree->pDeleted; pLeaf; pLeaf=pRtree->pDeleted){ if( rc==SQLITE_OK ){ - rc = nodeRelease(pRtree, pRoot); - }else{ - nodeRelease(pRtree, pRoot); + rc = reinsertNodeContent(pRtree, pLeaf); } + pRtree->pDeleted = pLeaf->pNext; + sqlite3_free(pLeaf); } - /* If the azData[] array contains more than one element, elements - ** (azData[2]..azData[argc-1]) contain a new record to insert into - ** the r-tree structure. + /* Release the reference to the root node. */ + if( rc==SQLITE_OK ){ + rc = nodeRelease(pRtree, pRoot); + }else{ + nodeRelease(pRtree, pRoot); + } + + return rc; +} + +/* +** The xUpdate method for rtree module virtual tables. +*/ +static int rtreeUpdate( + sqlite3_vtab *pVtab, + int nData, + sqlite3_value **azData, + sqlite_int64 *pRowid +){ + Rtree *pRtree = (Rtree *)pVtab; + int rc = SQLITE_OK; + RtreeCell cell; /* New cell to insert if nData>1 */ + int bHaveRowid = 0; /* Set to 1 after new rowid is determined */ + + rtreeReference(pRtree); + assert(nData>=1); + + /* Constraint handling. A write operation on an r-tree table may return + ** SQLITE_CONSTRAINT for two reasons: + ** + ** 1. A duplicate rowid value, or + ** 2. The supplied data violates the "x2>=x1" constraint. + ** + ** In the first case, if the conflict-handling mode is REPLACE, then + ** the conflicting row can be removed before proceeding. In the second + ** case, SQLITE_CONSTRAINT must be returned regardless of the + ** conflict-handling mode specified by the user. */ - if( rc==SQLITE_OK && nData>1 ){ - /* Insert a new record into the r-tree */ - RtreeCell cell; + if( nData>1 ){ int ii; - RtreeNode *pLeaf; /* Populate the cell.aCoord[] array. The first coordinate is azData[3]. */ assert( nData==(pRtree->nDim*2 + 3) ); @@ -121717,18 +127145,49 @@ static int rtreeUpdate( } } - /* Figure out the rowid of the new row. */ - if( sqlite3_value_type(azData[2])==SQLITE_NULL ){ - rc = newRowid(pRtree, &cell.iRowid); - }else{ + /* If a rowid value was supplied, check if it is already present in + ** the table. If so, the constraint has failed. */ + if( sqlite3_value_type(azData[2])!=SQLITE_NULL ){ cell.iRowid = sqlite3_value_int64(azData[2]); - sqlite3_bind_int64(pRtree->pReadRowid, 1, cell.iRowid); - if( SQLITE_ROW==sqlite3_step(pRtree->pReadRowid) ){ - sqlite3_reset(pRtree->pReadRowid); - rc = SQLITE_CONSTRAINT; - goto constraint; + if( sqlite3_value_type(azData[0])==SQLITE_NULL + || sqlite3_value_int64(azData[0])!=cell.iRowid + ){ + int steprc; + sqlite3_bind_int64(pRtree->pReadRowid, 1, cell.iRowid); + steprc = sqlite3_step(pRtree->pReadRowid); + rc = sqlite3_reset(pRtree->pReadRowid); + if( SQLITE_ROW==steprc ){ + if( sqlite3_vtab_on_conflict(pRtree->db)==SQLITE_REPLACE ){ + rc = rtreeDeleteRowid(pRtree, cell.iRowid); + }else{ + rc = SQLITE_CONSTRAINT; + goto constraint; + } + } } - rc = sqlite3_reset(pRtree->pReadRowid); + bHaveRowid = 1; + } + } + + /* If azData[0] is not an SQL NULL value, it is the rowid of a + ** record to delete from the r-tree table. The following block does + ** just that. + */ + if( sqlite3_value_type(azData[0])!=SQLITE_NULL ){ + rc = rtreeDeleteRowid(pRtree, sqlite3_value_int64(azData[0])); + } + + /* If the azData[] array contains more than one element, elements + ** (azData[2]..azData[argc-1]) contain a new record to insert into + ** the r-tree structure. + */ + if( rc==SQLITE_OK && nData>1 ){ + /* Insert the new record into the r-tree */ + RtreeNode *pLeaf; + + /* Figure out the rowid of the new row. */ + if( bHaveRowid==0 ){ + rc = newRowid(pRtree, &cell.iRowid); } *pRowid = cell.iRowid; @@ -121773,7 +127232,7 @@ static int rtreeRename(sqlite3_vtab *pVtab, const char *zNewName){ } static sqlite3_module rtreeModule = { - 0, /* iVersion */ + 0, /* iVersion */ rtreeCreate, /* xCreate - create a table */ rtreeConnect, /* xConnect - connect to an existing table */ rtreeBestIndex, /* xBestIndex - Determine search strategy */ @@ -121792,7 +127251,10 @@ static sqlite3_module rtreeModule = { 0, /* xCommit - commit transaction */ 0, /* xRollback - rollback transaction */ 0, /* xFindFunction - function overloading */ - rtreeRename /* xRename - rename the table */ + rtreeRename, /* xRename - rename the table */ + 0, /* xSavepoint */ + 0, /* xRelease */ + 0 /* xRollbackTo */ }; static int rtreeSqlInit( @@ -121912,7 +127374,7 @@ static int getNodeSize( int rc; char *zSql; if( isCreate ){ - int iPageSize; + int iPageSize = 0; zSql = sqlite3_mprintf("PRAGMA %Q.page_size", pRtree->zDb); rc = getIntFromStmt(db, zSql, &iPageSize); if( rc==SQLITE_OK ){ @@ -121969,6 +127431,8 @@ static int rtreeInit( return SQLITE_ERROR; } + sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1); + /* Allocate the sqlite3_vtab structure */ nDb = strlen(argv[1]); nName = strlen(argv[2]); @@ -122065,7 +127529,7 @@ static void rtreenode(sqlite3_context *ctx, int nArg, sqlite3_value **apArg){ int jj; nodeGetCell(&tree, &node, ii, &cell); - sqlite3_snprintf(512-nCell,&zCell[nCell],"%d", cell.iRowid); + sqlite3_snprintf(512-nCell,&zCell[nCell],"%lld", cell.iRowid); nCell = strlen(zCell); for(jj=0; jj<tree.nDim*2; jj++){ sqlite3_snprintf(512-nCell,&zCell[nCell]," %f",(double)cell.aCoord[jj].f); @@ -122211,7 +127675,7 @@ SQLITE_API int sqlite3_extension_init( ** May you share freely, never taking more than you give. ** ************************************************************************* -** $Id: sqlite3.c 306223 2010-12-11 14:57:34Z iliaa $ +** $Id: sqlite3.c 314586 2011-08-09 07:31:34Z scottmac $ ** ** This file implements an integration between the ICU library ** ("International Components for Unicode", an open-source library @@ -122446,6 +127910,8 @@ static void icuRegexpFunc(sqlite3_context *p, int nArg, sqlite3_value **apArg){ UBool res; const UChar *zString = sqlite3_value_text16(apArg[1]); + (void)nArg; /* Unused parameter */ + /* If the left hand side of the regexp operator is NULL, ** then the result is also NULL. */ @@ -122674,7 +128140,7 @@ SQLITE_PRIVATE int sqlite3IcuInit(sqlite3 *db){ int rc = SQLITE_OK; int i; - for(i=0; rc==SQLITE_OK && i<(sizeof(scalars)/sizeof(struct IcuScalar)); i++){ + for(i=0; rc==SQLITE_OK && i<(int)(sizeof(scalars)/sizeof(scalars[0])); i++){ struct IcuScalar *p = &scalars[i]; rc = sqlite3_create_function( db, p->zName, p->nArg, p->enc, p->pContext, p->xFunc, 0, 0 @@ -122711,10 +128177,7 @@ SQLITE_API int sqlite3_extension_init( ** ************************************************************************* ** This file implements a tokenizer for fts3 based on the ICU library. -** -** $Id: sqlite3.c 306223 2010-12-11 14:57:34Z iliaa $ */ - #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) #ifdef SQLITE_ENABLE_ICU diff --git a/ext/sqlite3/libsqlite/sqlite3.h b/ext/sqlite3/libsqlite/sqlite3.h index 47ef2562a..ed9edbd20 100644 --- a/ext/sqlite3/libsqlite/sqlite3.h +++ b/ext/sqlite3/libsqlite/sqlite3.h @@ -107,9 +107,9 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.7.4" -#define SQLITE_VERSION_NUMBER 3007004 -#define SQLITE_SOURCE_ID "2010-12-07 20:14:09 a586a4deeb25330037a49df295b36aaf624d0f45" +#define SQLITE_VERSION "3.7.7.1" +#define SQLITE_VERSION_NUMBER 3007007 +#define SQLITE_SOURCE_ID "2011-06-28 17:39:05 af0d91adf497f5f36ec3813f04235a6e195a605f" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -310,7 +310,7 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**); ** argument. ^If the callback function of the 3rd argument to ** sqlite3_exec() is not NULL, then it is invoked for each result row ** coming out of the evaluated SQL statements. ^The 4th argument to -** to sqlite3_exec() is relayed through to the 1st argument of each +** sqlite3_exec() is relayed through to the 1st argument of each ** callback invocation. ^If the callback pointer to sqlite3_exec() ** is NULL, then no callback is ever invoked and result rows are ** ignored. @@ -375,7 +375,8 @@ SQLITE_API int sqlite3_exec( ** ** New error codes may be added in future versions of SQLite. ** -** See also: [SQLITE_IOERR_READ | extended result codes] +** See also: [SQLITE_IOERR_READ | extended result codes], +** [sqlite3_vtab_on_conflict()] [SQLITE_ROLLBACK | result codes]. */ #define SQLITE_OK 0 /* Successful result */ /* beginning-of-error-codes */ @@ -390,7 +391,7 @@ SQLITE_API int sqlite3_exec( #define SQLITE_INTERRUPT 9 /* Operation terminated by sqlite3_interrupt()*/ #define SQLITE_IOERR 10 /* Some kind of disk I/O error occurred */ #define SQLITE_CORRUPT 11 /* The database disk image is malformed */ -#define SQLITE_NOTFOUND 12 /* NOT USED. Table or record not found */ +#define SQLITE_NOTFOUND 12 /* Unknown opcode in sqlite3_file_control() */ #define SQLITE_FULL 13 /* Insertion failed because database is full */ #define SQLITE_CANTOPEN 14 /* Unable to open the database file */ #define SQLITE_PROTOCOL 15 /* Database lock protocol error */ @@ -452,17 +453,21 @@ SQLITE_API int sqlite3_exec( #define SQLITE_IOERR_SHMOPEN (SQLITE_IOERR | (18<<8)) #define SQLITE_IOERR_SHMSIZE (SQLITE_IOERR | (19<<8)) #define SQLITE_IOERR_SHMLOCK (SQLITE_IOERR | (20<<8)) +#define SQLITE_IOERR_SHMMAP (SQLITE_IOERR | (21<<8)) +#define SQLITE_IOERR_SEEK (SQLITE_IOERR | (22<<8)) #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) #define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8)) +#define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8)) +#define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8)) +#define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8)) /* ** CAPI3REF: Flags For File Open Operations ** ** These bit values are intended for use in the ** 3rd parameter to the [sqlite3_open_v2()] interface and -** in the 4th parameter to the xOpen method of the -** [sqlite3_vfs] object. +** in the 4th parameter to the [sqlite3_vfs.xOpen] method. */ #define SQLITE_OPEN_READONLY 0x00000001 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_READWRITE 0x00000002 /* Ok for sqlite3_open_v2() */ @@ -470,6 +475,7 @@ SQLITE_API int sqlite3_exec( #define SQLITE_OPEN_DELETEONCLOSE 0x00000008 /* VFS only */ #define SQLITE_OPEN_EXCLUSIVE 0x00000010 /* VFS only */ #define SQLITE_OPEN_AUTOPROXY 0x00000020 /* VFS only */ +#define SQLITE_OPEN_URI 0x00000040 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_MAIN_DB 0x00000100 /* VFS only */ #define SQLITE_OPEN_TEMP_DB 0x00000200 /* VFS only */ #define SQLITE_OPEN_TRANSIENT_DB 0x00000400 /* VFS only */ @@ -483,6 +489,8 @@ SQLITE_API int sqlite3_exec( #define SQLITE_OPEN_PRIVATECACHE 0x00040000 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_WAL 0x00080000 /* VFS only */ +/* Reserved: 0x00F00000 */ + /* ** CAPI3REF: Device Characteristics ** @@ -578,17 +586,18 @@ struct sqlite3_file { /* ** CAPI3REF: OS Interface File Virtual Methods Object ** -** Every file opened by the [sqlite3_vfs] xOpen method populates an +** Every file opened by the [sqlite3_vfs.xOpen] method populates an ** [sqlite3_file] object (or, more commonly, a subclass of the ** [sqlite3_file] object) with a pointer to an instance of this object. ** This object defines the methods used to perform various operations ** against the open file represented by the [sqlite3_file] object. ** -** If the xOpen method sets the sqlite3_file.pMethods element +** If the [sqlite3_vfs.xOpen] method sets the sqlite3_file.pMethods element ** to a non-NULL pointer, then the sqlite3_io_methods.xClose method -** may be invoked even if the xOpen reported that it failed. The -** only way to prevent a call to xClose following a failed xOpen -** is for the xOpen to set the sqlite3_file.pMethods element to NULL. +** may be invoked even if the [sqlite3_vfs.xOpen] reported that it failed. The +** only way to prevent a call to xClose following a failed [sqlite3_vfs.xOpen] +** is for the [sqlite3_vfs.xOpen] to set the sqlite3_file.pMethods element +** to NULL. ** ** The flags argument to xSync may be one of [SQLITE_SYNC_NORMAL] or ** [SQLITE_SYNC_FULL]. The first choice is the normal fsync(). @@ -622,7 +631,9 @@ struct sqlite3_file { ** core reserves all opcodes less than 100 for its own use. ** A [SQLITE_FCNTL_LOCKSTATE | list of opcodes] less than 100 is available. ** Applications that define a custom xFileControl method should use opcodes -** greater than 100 to avoid conflicts. +** greater than 100 to avoid conflicts. VFS implementations should +** return [SQLITE_NOTFOUND] for file control opcodes that they do not +** recognize. ** ** The xSectorSize() method returns the sector size of the ** device that underlies the file. The sector size is the @@ -715,6 +726,21 @@ struct sqlite3_io_methods { ** for the nominated database. Allocating database file space in large ** chunks (say 1MB at a time), may reduce file-system fragmentation and ** improve performance on some systems. +** +** The [SQLITE_FCNTL_FILE_POINTER] opcode is used to obtain a pointer +** to the [sqlite3_file] object associated with a particular database +** connection. See the [sqlite3_file_control()] documentation for +** additional information. +** +** ^(The [SQLITE_FCNTL_SYNC_OMITTED] opcode is generated internally by +** SQLite and sent to all VFSes in place of a call to the xSync method +** when the database connection has [PRAGMA synchronous] set to OFF.)^ +** Some specialized VFSes need this signal in order to operate correctly +** when [PRAGMA synchronous | PRAGMA synchronous=OFF] is set, but most +** VFSes do not need this signal and should silently ignore this opcode. +** Applications should not call [sqlite3_file_control()] with this +** opcode as doing so may disrupt the operation of the specialized VFSes +** that do require it. */ #define SQLITE_FCNTL_LOCKSTATE 1 #define SQLITE_GET_LOCKPROXYFILE 2 @@ -723,6 +749,7 @@ struct sqlite3_io_methods { #define SQLITE_FCNTL_SIZE_HINT 5 #define SQLITE_FCNTL_CHUNK_SIZE 6 #define SQLITE_FCNTL_FILE_POINTER 7 +#define SQLITE_FCNTL_SYNC_OMITTED 8 /* @@ -742,7 +769,8 @@ typedef struct sqlite3_mutex sqlite3_mutex; ** ** An instance of the sqlite3_vfs object defines the interface between ** the SQLite core and the underlying operating system. The "vfs" -** in the name of the object stands for "virtual file system". +** in the name of the object stands for "virtual file system". See +** the [VFS | VFS documentation] for further information. ** ** The value of the iVersion field is initially 1 but may be larger in ** future versions of SQLite. Additional fields may be appended to this @@ -771,6 +799,7 @@ typedef struct sqlite3_mutex sqlite3_mutex; ** The zName field holds the name of the VFS module. The name must ** be unique across all VFS modules. ** +** [[sqlite3_vfs.xOpen]] ** ^SQLite guarantees that the zFilename parameter to xOpen ** is either a NULL pointer or string obtained ** from xFullPathname() with an optional suffix added. @@ -848,6 +877,7 @@ typedef struct sqlite3_mutex sqlite3_mutex; ** element will be valid after xOpen returns regardless of the success ** or failure of the xOpen call. ** +** [[sqlite3_vfs.xAccess]] ** ^The flags argument to xAccess() may be [SQLITE_ACCESS_EXISTS] ** to test for the existence of a file, or [SQLITE_ACCESS_READWRITE] to ** test whether a file is readable and writable, or [SQLITE_ACCESS_READ] @@ -872,16 +902,29 @@ typedef struct sqlite3_mutex sqlite3_mutex; ** method returns a Julian Day Number for the current date and time as ** a floating point value. ** ^The xCurrentTimeInt64() method returns, as an integer, the Julian -** Day Number multipled by 86400000 (the number of milliseconds in +** Day Number multiplied by 86400000 (the number of milliseconds in ** a 24-hour day). ** ^SQLite will use the xCurrentTimeInt64() method to get the current ** date and time if that method is available (if iVersion is 2 or ** greater and the function pointer is not NULL) and will fall back ** to xCurrentTime() if xCurrentTimeInt64() is unavailable. +** +** ^The xSetSystemCall(), xGetSystemCall(), and xNestSystemCall() interfaces +** are not used by the SQLite core. These optional interfaces are provided +** by some VFSes to facilitate testing of the VFS code. By overriding +** system calls with functions under its control, a test program can +** simulate faults and error conditions that would otherwise be difficult +** or impossible to induce. The set of system calls that can be overridden +** varies from one VFS to another, and from one version of the same VFS to the +** next. Applications that use these interfaces must be prepared for any +** or all of these interfaces to be NULL or for their behavior to change +** from one release to the next. Applications must not attempt to access +** any of these methods if the iVersion of the VFS is less than 3. */ typedef struct sqlite3_vfs sqlite3_vfs; +typedef void (*sqlite3_syscall_ptr)(void); struct sqlite3_vfs { - int iVersion; /* Structure version number (currently 2) */ + int iVersion; /* Structure version number (currently 3) */ int szOsFile; /* Size of subclassed sqlite3_file */ int mxPathname; /* Maximum file pathname length */ sqlite3_vfs *pNext; /* Next registered VFS */ @@ -907,6 +950,13 @@ struct sqlite3_vfs { int (*xCurrentTimeInt64)(sqlite3_vfs*, sqlite3_int64*); /* ** The methods above are in versions 1 and 2 of the sqlite_vfs object. + ** Those below are for version 3 and greater. + */ + int (*xSetSystemCall)(sqlite3_vfs*, const char *zName, sqlite3_syscall_ptr); + sqlite3_syscall_ptr (*xGetSystemCall)(sqlite3_vfs*, const char *zName); + const char *(*xNextSystemCall)(sqlite3_vfs*, const char *zName); + /* + ** The methods above are in versions 1 through 3 of the sqlite_vfs object. ** New fields may be appended in figure versions. The iVersion ** value will increment whenever this happens. */ @@ -1074,9 +1124,9 @@ SQLITE_API int sqlite3_os_end(void); ** implementation of an application-defined [sqlite3_os_init()]. ** ** The first argument to sqlite3_config() is an integer -** [SQLITE_CONFIG_SINGLETHREAD | configuration option] that determines +** [configuration option] that determines ** what property of SQLite is to be configured. Subsequent arguments -** vary depending on the [SQLITE_CONFIG_SINGLETHREAD | configuration option] +** vary depending on the [configuration option] ** in the first argument. ** ** ^When a configuration option is set, sqlite3_config() returns [SQLITE_OK]. @@ -1091,17 +1141,12 @@ SQLITE_API int sqlite3_config(int, ...); ** The sqlite3_db_config() interface is used to make configuration ** changes to a [database connection]. The interface is similar to ** [sqlite3_config()] except that the changes apply to a single -** [database connection] (specified in the first argument). The -** sqlite3_db_config() interface should only be used immediately after -** the database connection is created using [sqlite3_open()], -** [sqlite3_open16()], or [sqlite3_open_v2()]. +** [database connection] (specified in the first argument). ** ** The second argument to sqlite3_db_config(D,V,...) is the -** configuration verb - an integer code that indicates what -** aspect of the [database connection] is being configured. -** The only choice for this value is [SQLITE_DBCONFIG_LOOKASIDE]. -** New verbs are likely to be added in future releases of SQLite. -** Additional arguments depend on the verb. +** [SQLITE_DBCONFIG_LOOKASIDE | configuration verb] - an integer code +** that indicates what aspect of the [database connection] is being configured. +** Subsequent arguments vary depending on the configuration verb. ** ** ^Calls to sqlite3_db_config() return SQLITE_OK if and only if ** the call is considered successful. @@ -1191,6 +1236,7 @@ struct sqlite3_mem_methods { /* ** CAPI3REF: Configuration Options +** KEYWORDS: {configuration option} ** ** These constants are the available integer configuration options that ** can be passed as the first argument to the [sqlite3_config()] interface. @@ -1203,7 +1249,7 @@ struct sqlite3_mem_methods { ** is invoked. ** ** <dl> -** <dt>SQLITE_CONFIG_SINGLETHREAD</dt> +** [[SQLITE_CONFIG_SINGLETHREAD]] <dt>SQLITE_CONFIG_SINGLETHREAD</dt> ** <dd>There are no arguments to this option. ^This option sets the ** [threading mode] to Single-thread. In other words, it disables ** all mutexing and puts SQLite into a mode where it can only be used @@ -1214,7 +1260,7 @@ struct sqlite3_mem_methods { ** [SQLITE_ERROR] if called with the SQLITE_CONFIG_SINGLETHREAD ** configuration option.</dd> ** -** <dt>SQLITE_CONFIG_MULTITHREAD</dt> +** [[SQLITE_CONFIG_MULTITHREAD]] <dt>SQLITE_CONFIG_MULTITHREAD</dt> ** <dd>There are no arguments to this option. ^This option sets the ** [threading mode] to Multi-thread. In other words, it disables ** mutexing on [database connection] and [prepared statement] objects. @@ -1228,7 +1274,7 @@ struct sqlite3_mem_methods { ** [sqlite3_config()] will return [SQLITE_ERROR] if called with the ** SQLITE_CONFIG_MULTITHREAD configuration option.</dd> ** -** <dt>SQLITE_CONFIG_SERIALIZED</dt> +** [[SQLITE_CONFIG_SERIALIZED]] <dt>SQLITE_CONFIG_SERIALIZED</dt> ** <dd>There are no arguments to this option. ^This option sets the ** [threading mode] to Serialized. In other words, this option enables ** all mutexes including the recursive @@ -1244,7 +1290,7 @@ struct sqlite3_mem_methods { ** [sqlite3_config()] will return [SQLITE_ERROR] if called with the ** SQLITE_CONFIG_SERIALIZED configuration option.</dd> ** -** <dt>SQLITE_CONFIG_MALLOC</dt> +** [[SQLITE_CONFIG_MALLOC]] <dt>SQLITE_CONFIG_MALLOC</dt> ** <dd> ^(This option takes a single argument which is a pointer to an ** instance of the [sqlite3_mem_methods] structure. The argument specifies ** alternative low-level memory allocation routines to be used in place of @@ -1252,7 +1298,7 @@ struct sqlite3_mem_methods { ** its own private copy of the content of the [sqlite3_mem_methods] structure ** before the [sqlite3_config()] call returns.</dd> ** -** <dt>SQLITE_CONFIG_GETMALLOC</dt> +** [[SQLITE_CONFIG_GETMALLOC]] <dt>SQLITE_CONFIG_GETMALLOC</dt> ** <dd> ^(This option takes a single argument which is a pointer to an ** instance of the [sqlite3_mem_methods] structure. The [sqlite3_mem_methods] ** structure is filled with the currently defined memory allocation routines.)^ @@ -1260,7 +1306,7 @@ struct sqlite3_mem_methods { ** routines with a wrapper that simulations memory allocation failure or ** tracks memory usage, for example. </dd> ** -** <dt>SQLITE_CONFIG_MEMSTATUS</dt> +** [[SQLITE_CONFIG_MEMSTATUS]] <dt>SQLITE_CONFIG_MEMSTATUS</dt> ** <dd> ^This option takes single argument of type int, interpreted as a ** boolean, which enables or disables the collection of memory allocation ** statistics. ^(When memory allocation statistics are disabled, the @@ -1276,10 +1322,10 @@ struct sqlite3_mem_methods { ** allocation statistics are disabled by default. ** </dd> ** -** <dt>SQLITE_CONFIG_SCRATCH</dt> +** [[SQLITE_CONFIG_SCRATCH]] <dt>SQLITE_CONFIG_SCRATCH</dt> ** <dd> ^This option specifies a static memory buffer that SQLite can use for ** scratch memory. There are three arguments: A pointer an 8-byte -** aligned memory buffer from which the scrach allocations will be +** aligned memory buffer from which the scratch allocations will be ** drawn, the size of each scratch allocation (sz), ** and the maximum number of scratch allocations (N). The sz ** argument must be a multiple of 16. @@ -1292,9 +1338,9 @@ struct sqlite3_mem_methods { ** scratch memory beyond what is provided by this configuration option, then ** [sqlite3_malloc()] will be used to obtain the memory needed.</dd> ** -** <dt>SQLITE_CONFIG_PAGECACHE</dt> +** [[SQLITE_CONFIG_PAGECACHE]] <dt>SQLITE_CONFIG_PAGECACHE</dt> ** <dd> ^This option specifies a static memory buffer that SQLite can use for -** the database page cache with the default page cache implemenation. +** the database page cache with the default page cache implementation. ** This configuration should not be used if an application-define page ** cache implementation is loaded using the SQLITE_CONFIG_PCACHE option. ** There are three arguments to this option: A pointer to 8-byte aligned @@ -1313,7 +1359,7 @@ struct sqlite3_mem_methods { ** be aligned to an 8-byte boundary or subsequent behavior of SQLite ** will be undefined.</dd> ** -** <dt>SQLITE_CONFIG_HEAP</dt> +** [[SQLITE_CONFIG_HEAP]] <dt>SQLITE_CONFIG_HEAP</dt> ** <dd> ^This option specifies a static memory buffer that SQLite will use ** for all of its dynamic memory allocation needs beyond those provided ** for by [SQLITE_CONFIG_SCRATCH] and [SQLITE_CONFIG_PAGECACHE]. @@ -1326,9 +1372,11 @@ struct sqlite3_mem_methods { ** [SQLITE_ENABLE_MEMSYS5] are defined, then the alternative memory ** allocator is engaged to handle all of SQLites memory allocation needs. ** The first pointer (the memory pointer) must be aligned to an 8-byte -** boundary or subsequent behavior of SQLite will be undefined.</dd> +** boundary or subsequent behavior of SQLite will be undefined. +** The minimum allocation size is capped at 2^12. Reasonable values +** for the minimum allocation size are 2^5 through 2^8.</dd> ** -** <dt>SQLITE_CONFIG_MUTEX</dt> +** [[SQLITE_CONFIG_MUTEX]] <dt>SQLITE_CONFIG_MUTEX</dt> ** <dd> ^(This option takes a single argument which is a pointer to an ** instance of the [sqlite3_mutex_methods] structure. The argument specifies ** alternative low-level mutex routines to be used in place @@ -1340,7 +1388,7 @@ struct sqlite3_mem_methods { ** [sqlite3_config()] with the SQLITE_CONFIG_MUTEX configuration option will ** return [SQLITE_ERROR].</dd> ** -** <dt>SQLITE_CONFIG_GETMUTEX</dt> +** [[SQLITE_CONFIG_GETMUTEX]] <dt>SQLITE_CONFIG_GETMUTEX</dt> ** <dd> ^(This option takes a single argument which is a pointer to an ** instance of the [sqlite3_mutex_methods] structure. The ** [sqlite3_mutex_methods] @@ -1353,7 +1401,7 @@ struct sqlite3_mem_methods { ** [sqlite3_config()] with the SQLITE_CONFIG_GETMUTEX configuration option will ** return [SQLITE_ERROR].</dd> ** -** <dt>SQLITE_CONFIG_LOOKASIDE</dt> +** [[SQLITE_CONFIG_LOOKASIDE]] <dt>SQLITE_CONFIG_LOOKASIDE</dt> ** <dd> ^(This option takes two arguments that determine the default ** memory allocation for the lookaside memory allocator on each ** [database connection]. The first argument is the @@ -1363,18 +1411,18 @@ struct sqlite3_mem_methods { ** verb to [sqlite3_db_config()] can be used to change the lookaside ** configuration on individual connections.)^ </dd> ** -** <dt>SQLITE_CONFIG_PCACHE</dt> +** [[SQLITE_CONFIG_PCACHE]] <dt>SQLITE_CONFIG_PCACHE</dt> ** <dd> ^(This option takes a single argument which is a pointer to ** an [sqlite3_pcache_methods] object. This object specifies the interface ** to a custom page cache implementation.)^ ^SQLite makes a copy of the ** object and uses it for page cache memory allocations.</dd> ** -** <dt>SQLITE_CONFIG_GETPCACHE</dt> +** [[SQLITE_CONFIG_GETPCACHE]] <dt>SQLITE_CONFIG_GETPCACHE</dt> ** <dd> ^(This option takes a single argument which is a pointer to an ** [sqlite3_pcache_methods] object. SQLite copies of the current ** page cache implementation into that object.)^ </dd> ** -** <dt>SQLITE_CONFIG_LOG</dt> +** [[SQLITE_CONFIG_LOG]] <dt>SQLITE_CONFIG_LOG</dt> ** <dd> ^The SQLITE_CONFIG_LOG option takes two arguments: a pointer to a ** function with a call signature of void(*)(void*,int,const char*), ** and a pointer to void. ^If the function pointer is not NULL, it is @@ -1392,6 +1440,18 @@ struct sqlite3_mem_methods { ** In a multi-threaded application, the application-defined logger ** function must be threadsafe. </dd> ** +** [[SQLITE_CONFIG_URI]] <dt>SQLITE_CONFIG_URI +** <dd> This option takes a single argument of type int. If non-zero, then +** URI handling is globally enabled. If the parameter is zero, then URI handling +** is globally disabled. If URI handling is globally enabled, all filenames +** passed to [sqlite3_open()], [sqlite3_open_v2()], [sqlite3_open16()] or +** specified as part of [ATTACH] commands are interpreted as URIs, regardless +** of whether or not the [SQLITE_OPEN_URI] flag is set when the database +** connection is opened. If it is globally disabled, filenames are +** only interpreted as URIs if the SQLITE_OPEN_URI flag is set when the +** database connection is opened. By default, URI handling is globally +** disabled. The default value may be changed by compiling with the +** [SQLITE_USE_URI] symbol defined. ** </dl> */ #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ @@ -1410,6 +1470,7 @@ struct sqlite3_mem_methods { #define SQLITE_CONFIG_PCACHE 14 /* sqlite3_pcache_methods* */ #define SQLITE_CONFIG_GETPCACHE 15 /* sqlite3_pcache_methods* */ #define SQLITE_CONFIG_LOG 16 /* xFunc, void* */ +#define SQLITE_CONFIG_URI 17 /* int */ /* ** CAPI3REF: Database Connection Configuration Options @@ -1429,7 +1490,7 @@ struct sqlite3_mem_methods { ** <dd> ^This option takes three additional arguments that determine the ** [lookaside memory allocator] configuration for the [database connection]. ** ^The first argument (the third parameter to [sqlite3_db_config()] is a -** pointer to an memory buffer to use for lookaside memory. +** pointer to a memory buffer to use for lookaside memory. ** ^The first argument after the SQLITE_DBCONFIG_LOOKASIDE verb ** may be NULL in which case SQLite will allocate the ** lookaside buffer itself using [sqlite3_malloc()]. ^The second argument is the @@ -1447,9 +1508,31 @@ struct sqlite3_mem_methods { ** memory is in use leaves the configuration unchanged and returns ** [SQLITE_BUSY].)^</dd> ** +** <dt>SQLITE_DBCONFIG_ENABLE_FKEY</dt> +** <dd> ^This option is used to enable or disable the enforcement of +** [foreign key constraints]. There should be two additional arguments. +** The first argument is an integer which is 0 to disable FK enforcement, +** positive to enable FK enforcement or negative to leave FK enforcement +** unchanged. The second parameter is a pointer to an integer into which +** is written 0 or 1 to indicate whether FK enforcement is off or on +** following this call. The second parameter may be a NULL pointer, in +** which case the FK enforcement setting is not reported back. </dd> +** +** <dt>SQLITE_DBCONFIG_ENABLE_TRIGGER</dt> +** <dd> ^This option is used to enable or disable [CREATE TRIGGER | triggers]. +** There should be two additional arguments. +** The first argument is an integer which is 0 to disable triggers, +** positive to enable triggers or negative to leave the setting unchanged. +** The second parameter is a pointer to an integer into which +** is written 0 or 1 to indicate whether triggers are disabled or enabled +** following this call. The second parameter may be a NULL pointer, in +** which case the trigger setting is not reported back. </dd> +** ** </dl> */ -#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ +#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ +#define SQLITE_DBCONFIG_ENABLE_FKEY 1002 /* int int* */ +#define SQLITE_DBCONFIG_ENABLE_TRIGGER 1003 /* int int* */ /* @@ -1473,13 +1556,17 @@ SQLITE_API int sqlite3_extended_result_codes(sqlite3*, int onoff); ** ** ^This routine returns the [rowid] of the most recent ** successful [INSERT] into the database from the [database connection] -** in the first argument. ^If no successful [INSERT]s +** in the first argument. ^As of SQLite version 3.7.7, this routines +** records the last insert rowid of both ordinary tables and [virtual tables]. +** ^If no successful [INSERT]s ** have ever occurred on that database connection, zero is returned. ** -** ^(If an [INSERT] occurs within a trigger, then the [rowid] of the inserted -** row is returned by this routine as long as the trigger is running. -** But once the trigger terminates, the value returned by this routine -** reverts to the last value inserted before the trigger fired.)^ +** ^(If an [INSERT] occurs within a trigger or within a [virtual table] +** method, then this routine will return the [rowid] of the inserted +** row as long as the trigger or virtual table method is running. +** But once the trigger or virtual table method ends, the value returned +** by this routine reverts to what it was before the trigger or virtual +** table method began.)^ ** ** ^An [INSERT] that fails due to a constraint violation is not a ** successful [INSERT] and does not change the value returned by this @@ -1842,7 +1929,7 @@ SQLITE_API void sqlite3_free_table(char **result); ** NULL pointer if [sqlite3_malloc()] is unable to allocate enough ** memory to hold the resulting string. ** -** ^(In sqlite3_snprintf() routine is similar to "snprintf()" from +** ^(The sqlite3_snprintf() routine is similar to "snprintf()" from ** the standard C library. The result is written into the ** buffer supplied as the second parameter whose size is given by ** the first parameter. Note that the order of the @@ -1861,6 +1948,8 @@ SQLITE_API void sqlite3_free_table(char **result); ** the zero terminator. So the longest string that can be completely ** written will be n-1 characters. ** +** ^The sqlite3_vsnprintf() routine is a varargs version of sqlite3_snprintf(). +** ** These routines all implement some additional formatting ** options that are useful for constructing SQL statements. ** All of the usual printf() formatting options apply. In addition, there @@ -1924,6 +2013,7 @@ SQLITE_API void sqlite3_free_table(char **result); SQLITE_API char *sqlite3_mprintf(const char*,...); SQLITE_API char *sqlite3_vmprintf(const char*, va_list); SQLITE_API char *sqlite3_snprintf(int,char*,const char*, ...); +SQLITE_API char *sqlite3_vsnprintf(int,char*,const char*, va_list); /* ** CAPI3REF: Memory Allocation Subsystem @@ -2048,7 +2138,7 @@ SQLITE_API void sqlite3_randomness(int N, void *P); /* ** CAPI3REF: Compile-Time Authorization Callbacks ** -** ^This routine registers a authorizer callback with a particular +** ^This routine registers an authorizer callback with a particular ** [database connection], supplied in the first argument. ** ^The authorizer callback is invoked as SQL statements are being compiled ** by [sqlite3_prepare()] or its variants [sqlite3_prepare_v2()], @@ -2139,6 +2229,9 @@ SQLITE_API int sqlite3_set_authorizer( ** to signal SQLite whether or not the action is permitted. See the ** [sqlite3_set_authorizer | authorizer documentation] for additional ** information. +** +** Note that SQLITE_IGNORE is also used as a [SQLITE_ROLLBACK | return code] +** from the [sqlite3_vtab_on_conflict()] interface. */ #define SQLITE_DENY 1 /* Abort the SQL statement with an error */ #define SQLITE_IGNORE 2 /* Don't allow access, but don't generate an error */ @@ -2261,7 +2354,7 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); /* ** CAPI3REF: Opening A New Database Connection ** -** ^These routines open an SQLite database file whose name is given by the +** ^These routines open an SQLite database file as specified by the ** filename argument. ^The filename argument is interpreted as UTF-8 for ** sqlite3_open() and sqlite3_open_v2() and as UTF-16 in the native byte ** order for sqlite3_open16(). ^(A [database connection] handle is usually @@ -2288,7 +2381,7 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); ** sqlite3_open_v2() can take one of ** the following three values, optionally combined with the ** [SQLITE_OPEN_NOMUTEX], [SQLITE_OPEN_FULLMUTEX], [SQLITE_OPEN_SHAREDCACHE], -** and/or [SQLITE_OPEN_PRIVATECACHE] flags:)^ +** [SQLITE_OPEN_PRIVATECACHE], and/or [SQLITE_OPEN_URI] flags:)^ ** ** <dl> ** ^(<dt>[SQLITE_OPEN_READONLY]</dt> @@ -2301,15 +2394,14 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); ** case the database must already exist, otherwise an error is returned.</dd>)^ ** ** ^(<dt>[SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]</dt> -** <dd>The database is opened for reading and writing, and is creates it if +** <dd>The database is opened for reading and writing, and is created if ** it does not already exist. This is the behavior that is always used for ** sqlite3_open() and sqlite3_open16().</dd>)^ ** </dl> ** ** If the 3rd parameter to sqlite3_open_v2() is not one of the -** combinations shown above or one of the combinations shown above combined -** with the [SQLITE_OPEN_NOMUTEX], [SQLITE_OPEN_FULLMUTEX], -** [SQLITE_OPEN_SHAREDCACHE] and/or [SQLITE_OPEN_PRIVATECACHE] flags, +** combinations shown above optionally combined with other +** [SQLITE_OPEN_READONLY | SQLITE_OPEN_* bits] ** then the behavior is undefined. ** ** ^If the [SQLITE_OPEN_NOMUTEX] flag is set, then the database connection @@ -2324,6 +2416,11 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); ** [SQLITE_OPEN_PRIVATECACHE] flag causes the database connection to not ** participate in [shared cache mode] even if it is enabled. ** +** ^The fourth parameter to sqlite3_open_v2() is the name of the +** [sqlite3_vfs] object that defines the operating system interface that +** the new database connection should use. ^If the fourth parameter is +** a NULL pointer then the default [sqlite3_vfs] object is used. +** ** ^If the filename is ":memory:", then a private, temporary in-memory database ** is created for the connection. ^This in-memory database will vanish when ** the database connection is closed. Future versions of SQLite might @@ -2336,10 +2433,111 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); ** on-disk database will be created. ^This private database will be ** automatically deleted as soon as the database connection is closed. ** -** ^The fourth parameter to sqlite3_open_v2() is the name of the -** [sqlite3_vfs] object that defines the operating system interface that -** the new database connection should use. ^If the fourth parameter is -** a NULL pointer then the default [sqlite3_vfs] object is used. +** [[URI filenames in sqlite3_open()]] <h3>URI Filenames</h3> +** +** ^If [URI filename] interpretation is enabled, and the filename argument +** begins with "file:", then the filename is interpreted as a URI. ^URI +** filename interpretation is enabled if the [SQLITE_OPEN_URI] flag is +** set in the fourth argument to sqlite3_open_v2(), or if it has +** been enabled globally using the [SQLITE_CONFIG_URI] option with the +** [sqlite3_config()] method or by the [SQLITE_USE_URI] compile-time option. +** As of SQLite version 3.7.7, URI filename interpretation is turned off +** by default, but future releases of SQLite might enable URI filename +** interpretation by default. See "[URI filenames]" for additional +** information. +** +** URI filenames are parsed according to RFC 3986. ^If the URI contains an +** authority, then it must be either an empty string or the string +** "localhost". ^If the authority is not an empty string or "localhost", an +** error is returned to the caller. ^The fragment component of a URI, if +** present, is ignored. +** +** ^SQLite uses the path component of the URI as the name of the disk file +** which contains the database. ^If the path begins with a '/' character, +** then it is interpreted as an absolute path. ^If the path does not begin +** with a '/' (meaning that the authority section is omitted from the URI) +** then the path is interpreted as a relative path. +** ^On windows, the first component of an absolute path +** is a drive specification (e.g. "C:"). +** +** [[core URI query parameters]] +** The query component of a URI may contain parameters that are interpreted +** either by SQLite itself, or by a [VFS | custom VFS implementation]. +** SQLite interprets the following three query parameters: +** +** <ul> +** <li> <b>vfs</b>: ^The "vfs" parameter may be used to specify the name of +** a VFS object that provides the operating system interface that should +** be used to access the database file on disk. ^If this option is set to +** an empty string the default VFS object is used. ^Specifying an unknown +** VFS is an error. ^If sqlite3_open_v2() is used and the vfs option is +** present, then the VFS specified by the option takes precedence over +** the value passed as the fourth parameter to sqlite3_open_v2(). +** +** <li> <b>mode</b>: ^(The mode parameter may be set to either "ro", "rw" or +** "rwc". Attempting to set it to any other value is an error)^. +** ^If "ro" is specified, then the database is opened for read-only +** access, just as if the [SQLITE_OPEN_READONLY] flag had been set in the +** third argument to sqlite3_prepare_v2(). ^If the mode option is set to +** "rw", then the database is opened for read-write (but not create) +** access, as if SQLITE_OPEN_READWRITE (but not SQLITE_OPEN_CREATE) had +** been set. ^Value "rwc" is equivalent to setting both +** SQLITE_OPEN_READWRITE and SQLITE_OPEN_CREATE. ^If sqlite3_open_v2() is +** used, it is an error to specify a value for the mode parameter that is +** less restrictive than that specified by the flags passed as the third +** parameter. +** +** <li> <b>cache</b>: ^The cache parameter may be set to either "shared" or +** "private". ^Setting it to "shared" is equivalent to setting the +** SQLITE_OPEN_SHAREDCACHE bit in the flags argument passed to +** sqlite3_open_v2(). ^Setting the cache parameter to "private" is +** equivalent to setting the SQLITE_OPEN_PRIVATECACHE bit. +** ^If sqlite3_open_v2() is used and the "cache" parameter is present in +** a URI filename, its value overrides any behaviour requested by setting +** SQLITE_OPEN_PRIVATECACHE or SQLITE_OPEN_SHAREDCACHE flag. +** </ul> +** +** ^Specifying an unknown parameter in the query component of a URI is not an +** error. Future versions of SQLite might understand additional query +** parameters. See "[query parameters with special meaning to SQLite]" for +** additional information. +** +** [[URI filename examples]] <h3>URI filename examples</h3> +** +** <table border="1" align=center cellpadding=5> +** <tr><th> URI filenames <th> Results +** <tr><td> file:data.db <td> +** Open the file "data.db" in the current directory. +** <tr><td> file:/home/fred/data.db<br> +** file:///home/fred/data.db <br> +** file://localhost/home/fred/data.db <br> <td> +** Open the database file "/home/fred/data.db". +** <tr><td> file://darkstar/home/fred/data.db <td> +** An error. "darkstar" is not a recognized authority. +** <tr><td style="white-space:nowrap"> +** file:///C:/Documents%20and%20Settings/fred/Desktop/data.db +** <td> Windows only: Open the file "data.db" on fred's desktop on drive +** C:. Note that the %20 escaping in this example is not strictly +** necessary - space characters can be used literally +** in URI filenames. +** <tr><td> file:data.db?mode=ro&cache=private <td> +** Open file "data.db" in the current directory for read-only access. +** Regardless of whether or not shared-cache mode is enabled by +** default, use a private cache. +** <tr><td> file:/home/fred/data.db?vfs=unix-nolock <td> +** Open file "/home/fred/data.db". Use the special VFS "unix-nolock". +** <tr><td> file:data.db?mode=readonly <td> +** An error. "readonly" is not a valid option for the "mode" parameter. +** </table> +** +** ^URI hexadecimal escape sequences (%HH) are supported within the path and +** query components of a URI. A hexadecimal escape sequence consists of a +** percent sign - "%" - followed by exactly two hexadecimal digits +** specifying an octet value. ^Before the path or query components of a +** URI filename are interpreted, they are encoded using UTF-8 and all +** hexadecimal escape sequences replaced by a single byte containing the +** corresponding octet. If this process generates an invalid UTF-8 encoding, +** the results are undefined. ** ** <b>Note to Windows users:</b> The encoding used for the filename argument ** of sqlite3_open() and sqlite3_open_v2() must be UTF-8, not whatever @@ -2363,6 +2561,26 @@ SQLITE_API int sqlite3_open_v2( ); /* +** CAPI3REF: Obtain Values For URI Parameters +** +** This is a utility routine, useful to VFS implementations, that checks +** to see if a database file was a URI that contained a specific query +** parameter, and if so obtains the value of the query parameter. +** +** The zFilename argument is the filename pointer passed into the xOpen() +** method of a VFS implementation. The zParam argument is the name of the +** query parameter we seek. This routine returns the value of the zParam +** parameter if it exists. If the parameter does not exist, this routine +** returns a NULL pointer. +** +** If the zFilename argument to this function is not a pointer that SQLite +** passed into the xOpen VFS method, then the behavior of this routine +** is undefined and probably undesirable. +*/ +SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam); + + +/* ** CAPI3REF: Error Codes And Messages ** ** ^The sqlite3_errcode() interface returns the numeric [result code] or @@ -2477,43 +2695,45 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); ** Additional information is available at [limits | Limits in SQLite]. ** ** <dl> -** ^(<dt>SQLITE_LIMIT_LENGTH</dt> +** [[SQLITE_LIMIT_LENGTH]] ^(<dt>SQLITE_LIMIT_LENGTH</dt> ** <dd>The maximum size of any string or BLOB or table row, in bytes.<dd>)^ ** -** ^(<dt>SQLITE_LIMIT_SQL_LENGTH</dt> +** [[SQLITE_LIMIT_SQL_LENGTH]] ^(<dt>SQLITE_LIMIT_SQL_LENGTH</dt> ** <dd>The maximum length of an SQL statement, in bytes.</dd>)^ ** -** ^(<dt>SQLITE_LIMIT_COLUMN</dt> +** [[SQLITE_LIMIT_COLUMN]] ^(<dt>SQLITE_LIMIT_COLUMN</dt> ** <dd>The maximum number of columns in a table definition or in the ** result set of a [SELECT] or the maximum number of columns in an index ** or in an ORDER BY or GROUP BY clause.</dd>)^ ** -** ^(<dt>SQLITE_LIMIT_EXPR_DEPTH</dt> +** [[SQLITE_LIMIT_EXPR_DEPTH]] ^(<dt>SQLITE_LIMIT_EXPR_DEPTH</dt> ** <dd>The maximum depth of the parse tree on any expression.</dd>)^ ** -** ^(<dt>SQLITE_LIMIT_COMPOUND_SELECT</dt> +** [[SQLITE_LIMIT_COMPOUND_SELECT]] ^(<dt>SQLITE_LIMIT_COMPOUND_SELECT</dt> ** <dd>The maximum number of terms in a compound SELECT statement.</dd>)^ ** -** ^(<dt>SQLITE_LIMIT_VDBE_OP</dt> +** [[SQLITE_LIMIT_VDBE_OP]] ^(<dt>SQLITE_LIMIT_VDBE_OP</dt> ** <dd>The maximum number of instructions in a virtual machine program ** used to implement an SQL statement. This limit is not currently ** enforced, though that might be added in some future release of ** SQLite.</dd>)^ ** -** ^(<dt>SQLITE_LIMIT_FUNCTION_ARG</dt> +** [[SQLITE_LIMIT_FUNCTION_ARG]] ^(<dt>SQLITE_LIMIT_FUNCTION_ARG</dt> ** <dd>The maximum number of arguments on a function.</dd>)^ ** -** ^(<dt>SQLITE_LIMIT_ATTACHED</dt> +** [[SQLITE_LIMIT_ATTACHED]] ^(<dt>SQLITE_LIMIT_ATTACHED</dt> ** <dd>The maximum number of [ATTACH | attached databases].)^</dd> ** +** [[SQLITE_LIMIT_LIKE_PATTERN_LENGTH]] ** ^(<dt>SQLITE_LIMIT_LIKE_PATTERN_LENGTH</dt> ** <dd>The maximum length of the pattern argument to the [LIKE] or ** [GLOB] operators.</dd>)^ ** +** [[SQLITE_LIMIT_VARIABLE_NUMBER]] ** ^(<dt>SQLITE_LIMIT_VARIABLE_NUMBER</dt> ** <dd>The maximum index number of any [parameter] in an SQL statement.)^ ** -** ^(<dt>SQLITE_LIMIT_TRIGGER_DEPTH</dt> +** [[SQLITE_LIMIT_TRIGGER_DEPTH]] ^(<dt>SQLITE_LIMIT_TRIGGER_DEPTH</dt> ** <dd>The maximum depth of recursion for triggers.</dd>)^ ** </dl> */ @@ -2651,13 +2871,30 @@ SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt); ** CAPI3REF: Determine If An SQL Statement Writes The Database ** ** ^The sqlite3_stmt_readonly(X) interface returns true (non-zero) if -** the [prepared statement] X is [SELECT] statement and false (zero) if -** X is an [INSERT], [UPDATE], [DELETE], CREATE, DROP, [ANALYZE], -** [ALTER], or [REINDEX] statement. -** If X is a NULL pointer or any other kind of statement, including but -** not limited to [ATTACH], [DETACH], [COMMIT], [ROLLBACK], [RELEASE], -** [SAVEPOINT], [PRAGMA], or [VACUUM] the result of sqlite3_stmt_readonly(X) is -** undefined. +** and only if the [prepared statement] X makes no direct changes to +** the content of the database file. +** +** Note that [application-defined SQL functions] or +** [virtual tables] might change the database indirectly as a side effect. +** ^(For example, if an application defines a function "eval()" that +** calls [sqlite3_exec()], then the following SQL statement would +** change the database file through side-effects: +** +** <blockquote><pre> +** SELECT eval('DELETE FROM t1') FROM t2; +** </pre></blockquote> +** +** But because the [SELECT] statement does not change the database file +** directly, sqlite3_stmt_readonly() would still return true.)^ +** +** ^Transaction control statements such as [BEGIN], [COMMIT], [ROLLBACK], +** [SAVEPOINT], and [RELEASE] cause sqlite3_stmt_readonly() to return true, +** since the statements themselves do not actually modify the database but +** rather they control the timing of when other statements modify the +** database. ^The [ATTACH] and [DETACH] statements also cause +** sqlite3_stmt_readonly() to return true since, while those statements +** change the configuration of a database connection, they do not make +** changes to the content of the database files on disk. */ SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); @@ -2677,7 +2914,7 @@ SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); ** whether or not it requires a protected sqlite3_value. ** ** The terms "protected" and "unprotected" refer to whether or not -** a mutex is held. A internal mutex is held for a protected +** a mutex is held. An internal mutex is held for a protected ** sqlite3_value object but no mutex is held for an unprotected ** sqlite3_value object. If SQLite is compiled to be single-threaded ** (with [SQLITE_THREADSAFE=0] and with [sqlite3_threadsafe()] returning 0) @@ -2901,7 +3138,9 @@ SQLITE_API int sqlite3_column_count(sqlite3_stmt *pStmt); ** column number. ^The leftmost column is number 0. ** ** ^The returned string pointer is valid until either the [prepared statement] -** is destroyed by [sqlite3_finalize()] or until the next call to +** is destroyed by [sqlite3_finalize()] or until the statement is automatically +** reprepared by the first call to [sqlite3_step()] for a particular run +** or until the next call to ** sqlite3_column_name() or sqlite3_column_name16() on the same column. ** ** ^If sqlite3_malloc() fails during the processing of either routine @@ -2927,7 +3166,9 @@ SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt*, int N); ** the database name, the _table_ routines return the table name, and ** the origin_ routines return the column name. ** ^The returned string is valid until the [prepared statement] is destroyed -** using [sqlite3_finalize()] or until the same information is requested +** using [sqlite3_finalize()] or until the statement is automatically +** reprepared by the first call to [sqlite3_step()] for a particular run +** or until the same information is requested ** again in a different encoding. ** ** ^The names returned are the original un-aliased names of the @@ -3021,7 +3262,7 @@ SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int); ** ^[SQLITE_BUSY] means that the database engine was unable to acquire the ** database locks it needs to do its job. ^If the statement is a [COMMIT] ** or occurs outside of an explicit transaction, then you can retry the -** statement. If the statement is not a [COMMIT] and occurs within a +** statement. If the statement is not a [COMMIT] and occurs within an ** explicit transaction then you should rollback the transaction before ** continuing. ** @@ -3051,13 +3292,17 @@ SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int); ** be the case that the same database connection is being used by two or ** more threads at the same moment in time. ** -** For all versions of SQLite up to and including 3.6.23.1, it was required -** after sqlite3_step() returned anything other than [SQLITE_ROW] that -** [sqlite3_reset()] be called before any subsequent invocation of -** sqlite3_step(). Failure to invoke [sqlite3_reset()] in this way would -** result in an [SQLITE_MISUSE] return from sqlite3_step(). But after -** version 3.6.23.1, sqlite3_step() began calling [sqlite3_reset()] -** automatically in this circumstance rather than returning [SQLITE_MISUSE]. +** For all versions of SQLite up to and including 3.6.23.1, a call to +** [sqlite3_reset()] was required after sqlite3_step() returned anything +** other than [SQLITE_ROW] before any subsequent invocation of +** sqlite3_step(). Failure to reset the prepared statement using +** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from +** sqlite3_step(). But after version 3.6.23.1, sqlite3_step() began +** calling [sqlite3_reset()] automatically in this circumstance rather +** than returning [SQLITE_MISUSE]. This is not considered a compatibility +** break because any application that ever receives an SQLITE_MISUSE error +** is broken by definition. The [SQLITE_OMIT_AUTORESET] compile-time option +** can be used to restore the legacy behavior. ** ** <b>Goofy Interface Alert:</b> In the legacy interface, the sqlite3_step() ** API always returns a generic error code, [SQLITE_ERROR], following any @@ -3296,7 +3541,7 @@ SQLITE_API sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol); ** CAPI3REF: Destroy A Prepared Statement Object ** ** ^The sqlite3_finalize() function is called to delete a [prepared statement]. -** ^If the most recent evaluation of the statement encountered no errors or +** ^If the most recent evaluation of the statement encountered no errors ** or if the statement is never been evaluated, then sqlite3_finalize() returns ** SQLITE_OK. ^If the most recent evaluation of statement S failed, then ** sqlite3_finalize(S) returns the appropriate [error code] or @@ -3355,7 +3600,7 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt); ** are used to add SQL functions or aggregates or to redefine the behavior ** of existing SQL functions or aggregates. The only differences between ** these routines are the text encoding expected for -** the the second parameter (the name of the function being created) +** the second parameter (the name of the function being created) ** and the presence or absence of a destructor callback for ** the application data pointer. ** @@ -3394,16 +3639,16 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt); ** ^(The fifth parameter is an arbitrary pointer. The implementation of the ** function can gain access to this pointer using [sqlite3_user_data()].)^ ** -** ^The seventh, eighth and ninth parameters, xFunc, xStep and xFinal, are +** ^The sixth, seventh and eighth parameters, xFunc, xStep and xFinal, are ** pointers to C-language functions that implement the SQL function or ** aggregate. ^A scalar SQL function requires an implementation of the xFunc ** callback only; NULL pointers must be passed as the xStep and xFinal ** parameters. ^An aggregate SQL function requires an implementation of xStep ** and xFinal and NULL pointer must be passed for xFunc. ^To delete an existing -** SQL function or aggregate, pass NULL poiners for all three function +** SQL function or aggregate, pass NULL pointers for all three function ** callbacks. ** -** ^(If the tenth parameter to sqlite3_create_function_v2() is not NULL, +** ^(If the ninth parameter to sqlite3_create_function_v2() is not NULL, ** then it is destructor for the application data pointer. ** The destructor is invoked when the function is deleted, either by being ** overloaded or when the database connection closes.)^ @@ -3507,7 +3752,7 @@ SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int6 ** The xFunc (for scalar functions) or xStep (for aggregates) parameters ** to [sqlite3_create_function()] and [sqlite3_create_function16()] ** define callbacks that implement the SQL functions and aggregates. -** The 4th parameter to these callbacks is an array of pointers to +** The 3rd parameter to these callbacks is an array of pointers to ** [protected sqlite3_value] objects. There is one [sqlite3_value] object for ** each parameter to the SQL function. These routines are used to ** extract values from the [sqlite3_value] objects. @@ -3834,7 +4079,7 @@ SQLITE_API void sqlite3_result_zeroblob(sqlite3_context*, int n); ** ^The [SQLITE_UTF16_ALIGNED] value for eTextRep forces strings to begin ** on an even byte address. ** -** ^The fourth argument, pArg, is a application data pointer that is passed +** ^The fourth argument, pArg, is an application data pointer that is passed ** through as the first argument to the collating function callback. ** ** ^The fifth argument, xCallback, is a pointer to the collating function. @@ -3850,7 +4095,7 @@ SQLITE_API void sqlite3_result_zeroblob(sqlite3_context*, int n); ** by the eTextRep argument. The collating function must return an ** integer that is negative, zero, or positive ** if the first string is less than, equal to, or greater than the second, -** respectively. A collating function must alway return the same answer +** respectively. A collating function must always return the same answer ** given the same inputs. If two or more collating functions are registered ** to the same collation name (using different eTextRep values) then all ** must give an equivalent answer when invoked with equivalent strings. @@ -4262,7 +4507,7 @@ SQLITE_API int sqlite3_release_memory(int); ** <li> Memory accounting is disabled using a combination of the ** [sqlite3_config]([SQLITE_CONFIG_MEMSTATUS],...) start-time option and ** the [SQLITE_DEFAULT_MEMSTATUS] compile-time option. -** <li> An alternative page cache implementation is specifed using +** <li> An alternative page cache implementation is specified using ** [sqlite3_config]([SQLITE_CONFIG_PCACHE],...). ** <li> The page cache allocates from its own memory pool supplied ** by [sqlite3_config]([SQLITE_CONFIG_PAGECACHE],...) rather than @@ -4483,7 +4728,7 @@ typedef struct sqlite3_module sqlite3_module; ** CAPI3REF: Virtual Table Object ** KEYWORDS: sqlite3_module {virtual table module} ** -** This structure, sometimes called a a "virtual table module", +** This structure, sometimes called a "virtual table module", ** defines the implementation of a [virtual tables]. ** This structure consists mostly of methods for the module. ** @@ -4523,6 +4768,11 @@ struct sqlite3_module { void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), void **ppArg); int (*xRename)(sqlite3_vtab *pVtab, const char *zNew); + /* The methods above are in version 1 of the sqlite_module object. Those + ** below are for version 2 and greater. */ + int (*xSavepoint)(sqlite3_vtab *pVTab, int); + int (*xRelease)(sqlite3_vtab *pVTab, int); + int (*xRollbackTo)(sqlite3_vtab *pVTab, int); }; /* @@ -4795,7 +5045,7 @@ typedef struct sqlite3_blob sqlite3_blob; ** This is true if any column of the row is changed, even a column ** other than the one the BLOB handle is open on.)^ ** ^Calls to [sqlite3_blob_read()] and [sqlite3_blob_write()] for -** a expired BLOB handle fail with an return code of [SQLITE_ABORT]. +** an expired BLOB handle fail with a return code of [SQLITE_ABORT]. ** ^(Changes written into a BLOB prior to the BLOB expiring are not ** rolled back by the expiration of the BLOB. Such changes will eventually ** commit if the transaction continues to completion.)^ @@ -5205,7 +5455,7 @@ struct sqlite3_mutex_methods { ** ** ^If the argument to sqlite3_mutex_held() is a NULL pointer then ** the routine should return 1. This seems counter-intuitive since -** clearly the mutex cannot be held if it does not exist. But the +** clearly the mutex cannot be held if it does not exist. But ** the reason the mutex does not exist is because the build is not ** using mutexes. And we do not want the assert() containing the ** call to sqlite3_mutex_held() to fail, so a non-zero return is @@ -5235,7 +5485,8 @@ SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex*); #define SQLITE_MUTEX_STATIC_OPEN 4 /* sqlite3BtreeOpen() */ #define SQLITE_MUTEX_STATIC_PRNG 5 /* sqlite3_random() */ #define SQLITE_MUTEX_STATIC_LRU 6 /* lru page list */ -#define SQLITE_MUTEX_STATIC_LRU2 7 /* lru page list */ +#define SQLITE_MUTEX_STATIC_LRU2 7 /* NOT USED */ +#define SQLITE_MUTEX_STATIC_PMEM 7 /* sqlite3PageMalloc() */ /* ** CAPI3REF: Retrieve the mutex for a database connection @@ -5327,7 +5578,8 @@ SQLITE_API int sqlite3_test_control(int op, ...); #define SQLITE_TESTCTRL_ISKEYWORD 16 #define SQLITE_TESTCTRL_PGHDRSZ 17 #define SQLITE_TESTCTRL_SCRATCHMALLOC 18 -#define SQLITE_TESTCTRL_LAST 18 +#define SQLITE_TESTCTRL_LOCALTIME_FAULT 19 +#define SQLITE_TESTCTRL_LAST 19 /* ** CAPI3REF: SQLite Runtime Status @@ -5336,7 +5588,7 @@ SQLITE_API int sqlite3_test_control(int op, ...); ** about the performance of SQLite, and optionally to reset various ** highwater marks. ^The first argument is an integer code for ** the specific parameter to measure. ^(Recognized integer codes -** are of the form [SQLITE_STATUS_MEMORY_USED | SQLITE_STATUS_...].)^ +** are of the form [status parameters | SQLITE_STATUS_...].)^ ** ^The current value of the parameter is returned into *pCurrent. ** ^The highest recorded value is returned in *pHighwater. ^If the ** resetFlag is true, then the highest record value is reset after @@ -5363,12 +5615,13 @@ SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetF /* ** CAPI3REF: Status Parameters +** KEYWORDS: {status parameters} ** ** These integer constants designate various run-time status parameters ** that can be returned by [sqlite3_status()]. ** ** <dl> -** ^(<dt>SQLITE_STATUS_MEMORY_USED</dt> +** [[SQLITE_STATUS_MEMORY_USED]] ^(<dt>SQLITE_STATUS_MEMORY_USED</dt> ** <dd>This parameter is the current amount of memory checked out ** using [sqlite3_malloc()], either directly or indirectly. The ** figure includes calls made to [sqlite3_malloc()] by the application @@ -5378,22 +5631,24 @@ SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetF ** this parameter. The amount returned is the sum of the allocation ** sizes as reported by the xSize method in [sqlite3_mem_methods].</dd>)^ ** -** ^(<dt>SQLITE_STATUS_MALLOC_SIZE</dt> +** [[SQLITE_STATUS_MALLOC_SIZE]] ^(<dt>SQLITE_STATUS_MALLOC_SIZE</dt> ** <dd>This parameter records the largest memory allocation request ** handed to [sqlite3_malloc()] or [sqlite3_realloc()] (or their ** internal equivalents). Only the value returned in the ** *pHighwater parameter to [sqlite3_status()] is of interest. ** The value written into the *pCurrent parameter is undefined.</dd>)^ ** -** ^(<dt>SQLITE_STATUS_MALLOC_COUNT</dt> -** <dd>This parameter records the number of separate memory allocations.</dd>)^ +** [[SQLITE_STATUS_MALLOC_COUNT]] ^(<dt>SQLITE_STATUS_MALLOC_COUNT</dt> +** <dd>This parameter records the number of separate memory allocations +** currently checked out.</dd>)^ ** -** ^(<dt>SQLITE_STATUS_PAGECACHE_USED</dt> +** [[SQLITE_STATUS_PAGECACHE_USED]] ^(<dt>SQLITE_STATUS_PAGECACHE_USED</dt> ** <dd>This parameter returns the number of pages used out of the ** [pagecache memory allocator] that was configured using ** [SQLITE_CONFIG_PAGECACHE]. The ** value returned is in pages, not in bytes.</dd>)^ ** +** [[SQLITE_STATUS_PAGECACHE_OVERFLOW]] ** ^(<dt>SQLITE_STATUS_PAGECACHE_OVERFLOW</dt> ** <dd>This parameter returns the number of bytes of page cache ** allocation which could not be satisfied by the [SQLITE_CONFIG_PAGECACHE] @@ -5403,13 +5658,13 @@ SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetF ** [SQLITE_CONFIG_PAGECACHE]) and allocations that overflowed because ** no space was left in the page cache.</dd>)^ ** -** ^(<dt>SQLITE_STATUS_PAGECACHE_SIZE</dt> +** [[SQLITE_STATUS_PAGECACHE_SIZE]] ^(<dt>SQLITE_STATUS_PAGECACHE_SIZE</dt> ** <dd>This parameter records the largest memory allocation request ** handed to [pagecache memory allocator]. Only the value returned in the ** *pHighwater parameter to [sqlite3_status()] is of interest. ** The value written into the *pCurrent parameter is undefined.</dd>)^ ** -** ^(<dt>SQLITE_STATUS_SCRATCH_USED</dt> +** [[SQLITE_STATUS_SCRATCH_USED]] ^(<dt>SQLITE_STATUS_SCRATCH_USED</dt> ** <dd>This parameter returns the number of allocations used out of the ** [scratch memory allocator] configured using ** [SQLITE_CONFIG_SCRATCH]. The value returned is in allocations, not @@ -5417,7 +5672,7 @@ SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetF ** outstanding at time, this parameter also reports the number of threads ** using scratch memory at the same time.</dd>)^ ** -** ^(<dt>SQLITE_STATUS_SCRATCH_OVERFLOW</dt> +** [[SQLITE_STATUS_SCRATCH_OVERFLOW]] ^(<dt>SQLITE_STATUS_SCRATCH_OVERFLOW</dt> ** <dd>This parameter returns the number of bytes of scratch memory ** allocation which could not be satisfied by the [SQLITE_CONFIG_SCRATCH] ** buffer and where forced to overflow to [sqlite3_malloc()]. The values @@ -5427,13 +5682,13 @@ SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetF ** slots were available. ** </dd>)^ ** -** ^(<dt>SQLITE_STATUS_SCRATCH_SIZE</dt> +** [[SQLITE_STATUS_SCRATCH_SIZE]] ^(<dt>SQLITE_STATUS_SCRATCH_SIZE</dt> ** <dd>This parameter records the largest memory allocation request ** handed to [scratch memory allocator]. Only the value returned in the ** *pHighwater parameter to [sqlite3_status()] is of interest. ** The value written into the *pCurrent parameter is undefined.</dd>)^ ** -** ^(<dt>SQLITE_STATUS_PARSER_STACK</dt> +** [[SQLITE_STATUS_PARSER_STACK]] ^(<dt>SQLITE_STATUS_PARSER_STACK</dt> ** <dd>This parameter records the deepest parser stack. It is only ** meaningful if SQLite is compiled with [YYTRACKMAXSTACKDEPTH].</dd>)^ ** </dl> @@ -5458,9 +5713,9 @@ SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetF ** about a single [database connection]. ^The first argument is the ** database connection object to be interrogated. ^The second argument ** is an integer constant, taken from the set of -** [SQLITE_DBSTATUS_LOOKASIDE_USED | SQLITE_DBSTATUS_*] macros, that +** [SQLITE_DBSTATUS options], that ** determines the parameter to interrogate. The set of -** [SQLITE_DBSTATUS_LOOKASIDE_USED | SQLITE_DBSTATUS_*] macros is likely +** [SQLITE_DBSTATUS options] is likely ** to grow in future releases of SQLite. ** ** ^The current value of the requested parameter is written into *pCur @@ -5477,6 +5732,7 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r /* ** CAPI3REF: Status Parameters for database connections +** KEYWORDS: {SQLITE_DBSTATUS options} ** ** These constants are the available integer "verbs" that can be passed as ** the second argument to the [sqlite3_db_status()] interface. @@ -5488,16 +5744,37 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r ** if a discontinued or unsupported verb is invoked. ** ** <dl> -** ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_USED</dt> +** [[SQLITE_DBSTATUS_LOOKASIDE_USED]] ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_USED</dt> ** <dd>This parameter returns the number of lookaside memory slots currently ** checked out.</dd>)^ ** -** ^(<dt>SQLITE_DBSTATUS_CACHE_USED</dt> +** [[SQLITE_DBSTATUS_LOOKASIDE_HIT]] ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_HIT</dt> +** <dd>This parameter returns the number malloc attempts that were +** satisfied using lookaside memory. Only the high-water value is meaningful; +** the current value is always zero.)^ +** +** [[SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE]] +** ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE</dt> +** <dd>This parameter returns the number malloc attempts that might have +** been satisfied using lookaside memory but failed due to the amount of +** memory requested being larger than the lookaside slot size. +** Only the high-water value is meaningful; +** the current value is always zero.)^ +** +** [[SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL]] +** ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL</dt> +** <dd>This parameter returns the number malloc attempts that might have +** been satisfied using lookaside memory but failed due to all lookaside +** memory already being in use. +** Only the high-water value is meaningful; +** the current value is always zero.)^ +** +** [[SQLITE_DBSTATUS_CACHE_USED]] ^(<dt>SQLITE_DBSTATUS_CACHE_USED</dt> ** <dd>This parameter returns the approximate number of of bytes of heap ** memory used by all pager caches associated with the database connection.)^ ** ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_USED is always 0. ** -** ^(<dt>SQLITE_DBSTATUS_SCHEMA_USED</dt> +** [[SQLITE_DBSTATUS_SCHEMA_USED]] ^(<dt>SQLITE_DBSTATUS_SCHEMA_USED</dt> ** <dd>This parameter returns the approximate number of of bytes of heap ** memory used to store the schema for all databases associated ** with the connection - main, temp, and any [ATTACH]-ed databases.)^ @@ -5506,7 +5783,7 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r ** [shared cache mode] being enabled. ** ^The highwater mark associated with SQLITE_DBSTATUS_SCHEMA_USED is always 0. ** -** ^(<dt>SQLITE_DBSTATUS_STMT_USED</dt> +** [[SQLITE_DBSTATUS_STMT_USED]] ^(<dt>SQLITE_DBSTATUS_STMT_USED</dt> ** <dd>This parameter returns the approximate number of of bytes of heap ** and lookaside memory used by all prepared statements associated with ** the database connection.)^ @@ -5514,18 +5791,21 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r ** </dd> ** </dl> */ -#define SQLITE_DBSTATUS_LOOKASIDE_USED 0 -#define SQLITE_DBSTATUS_CACHE_USED 1 -#define SQLITE_DBSTATUS_SCHEMA_USED 2 -#define SQLITE_DBSTATUS_STMT_USED 3 -#define SQLITE_DBSTATUS_MAX 3 /* Largest defined DBSTATUS */ +#define SQLITE_DBSTATUS_LOOKASIDE_USED 0 +#define SQLITE_DBSTATUS_CACHE_USED 1 +#define SQLITE_DBSTATUS_SCHEMA_USED 2 +#define SQLITE_DBSTATUS_STMT_USED 3 +#define SQLITE_DBSTATUS_LOOKASIDE_HIT 4 +#define SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE 5 +#define SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL 6 +#define SQLITE_DBSTATUS_MAX 6 /* Largest defined DBSTATUS */ /* ** CAPI3REF: Prepared Statement Status ** ** ^(Each prepared statement maintains various -** [SQLITE_STMTSTATUS_SORT | counters] that measure the number +** [SQLITE_STMTSTATUS counters] that measure the number ** of times it has performed specific operations.)^ These counters can ** be used to monitor the performance characteristics of the prepared ** statements. For example, if the number of table steps greatly exceeds @@ -5536,7 +5816,7 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r ** ^(This interface is used to retrieve and reset counter values from ** a [prepared statement]. The first argument is the prepared statement ** object to be interrogated. The second argument -** is an integer code for a specific [SQLITE_STMTSTATUS_SORT | counter] +** is an integer code for a specific [SQLITE_STMTSTATUS counter] ** to be interrogated.)^ ** ^The current value of the requested counter is returned. ** ^If the resetFlg is true, then the counter is reset to zero after this @@ -5548,24 +5828,25 @@ SQLITE_API int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg); /* ** CAPI3REF: Status Parameters for prepared statements +** KEYWORDS: {SQLITE_STMTSTATUS counter} {SQLITE_STMTSTATUS counters} ** ** These preprocessor macros define integer codes that name counter ** values associated with the [sqlite3_stmt_status()] interface. ** The meanings of the various counters are as follows: ** ** <dl> -** <dt>SQLITE_STMTSTATUS_FULLSCAN_STEP</dt> +** [[SQLITE_STMTSTATUS_FULLSCAN_STEP]] <dt>SQLITE_STMTSTATUS_FULLSCAN_STEP</dt> ** <dd>^This is the number of times that SQLite has stepped forward in ** a table as part of a full table scan. Large numbers for this counter ** may indicate opportunities for performance improvement through ** careful use of indices.</dd> ** -** <dt>SQLITE_STMTSTATUS_SORT</dt> +** [[SQLITE_STMTSTATUS_SORT]] <dt>SQLITE_STMTSTATUS_SORT</dt> ** <dd>^This is the number of sort operations that have occurred. ** A non-zero value in this counter may indicate an opportunity to ** improvement performance through careful use of indices.</dd> ** -** <dt>SQLITE_STMTSTATUS_AUTOINDEX</dt> +** [[SQLITE_STMTSTATUS_AUTOINDEX]] <dt>SQLITE_STMTSTATUS_AUTOINDEX</dt> ** <dd>^This is the number of rows inserted into transient indices that ** were created automatically in order to help joins run faster. ** A non-zero value in this counter may indicate an opportunity to @@ -5616,6 +5897,7 @@ typedef struct sqlite3_pcache sqlite3_pcache; ** the application may discard the parameter after the call to ** [sqlite3_config()] returns.)^ ** +** [[the xInit() page cache method]] ** ^(The xInit() method is called once for each effective ** call to [sqlite3_initialize()])^ ** (usually only once during the lifetime of the process). ^(The xInit() @@ -5626,6 +5908,7 @@ typedef struct sqlite3_pcache sqlite3_pcache; ** built-in default page cache is used instead of the application defined ** page cache.)^ ** +** [[the xShutdown() page cache method]] ** ^The xShutdown() method is called by [sqlite3_shutdown()]. ** It can be used to clean up ** any outstanding resources before process shutdown, if required. @@ -5640,17 +5923,20 @@ typedef struct sqlite3_pcache sqlite3_pcache; ** ^SQLite will never invoke xInit() more than once without an intervening ** call to xShutdown(). ** +** [[the xCreate() page cache methods]] ** ^SQLite invokes the xCreate() method to construct a new cache instance. ** SQLite will typically create one cache instance for each open database file, ** though this is not guaranteed. ^The ** first parameter, szPage, is the size in bytes of the pages that must ** be allocated by the cache. ^szPage will not be a power of two. ^szPage ** will the page size of the database file that is to be cached plus an -** increment (here called "R") of about 100 or 200. SQLite will use the +** increment (here called "R") of less than 250. SQLite will use the ** extra R bytes on each page to store metadata about the underlying ** database page on disk. The value of R depends ** on the SQLite version, the target platform, and how SQLite was compiled. -** ^R is constant for a particular build of SQLite. ^The second argument to +** ^(R is constant for a particular build of SQLite. Except, there are two +** distinct values of R when SQLite is compiled with the proprietary +** ZIPVFS extension.)^ ^The second argument to ** xCreate(), bPurgeable, is true if the cache being created will ** be used to cache database pages of a file stored on disk, or ** false if it is used for an in-memory database. The cache implementation @@ -5662,6 +5948,7 @@ typedef struct sqlite3_pcache sqlite3_pcache; ** ^Hence, a cache created with bPurgeable false will ** never contain any unpinned pages. ** +** [[the xCachesize() page cache method]] ** ^(The xCachesize() method may be called at any time by SQLite to set the ** suggested maximum cache-size (number of pages stored by) the cache ** instance passed as the first argument. This is the value configured using @@ -5669,20 +5956,22 @@ typedef struct sqlite3_pcache sqlite3_pcache; ** parameter, the implementation is not required to do anything with this ** value; it is advisory only. ** +** [[the xPagecount() page cache methods]] ** The xPagecount() method must return the number of pages currently ** stored in the cache, both pinned and unpinned. ** +** [[the xFetch() page cache methods]] ** The xFetch() method locates a page in the cache and returns a pointer to ** the page, or a NULL pointer. ** A "page", in this context, means a buffer of szPage bytes aligned at an ** 8-byte boundary. The page to be fetched is determined by the key. ^The -** mimimum key value is 1. After it has been retrieved using xFetch, the page +** minimum key value is 1. After it has been retrieved using xFetch, the page ** is considered to be "pinned". ** ** If the requested page is already in the page cache, then the page cache ** implementation must return a pointer to the page buffer with its content ** intact. If the requested page is not already in the cache, then the -** behavior of the cache implementation should use the value of the createFlag +** cache implementation should use the value of the createFlag ** parameter to help it determined what action to take: ** ** <table border=1 width=85% align=center> @@ -5700,6 +5989,7 @@ typedef struct sqlite3_pcache sqlite3_pcache; ** attempt to unpin one or more cache pages by spilling the content of ** pinned pages to disk and synching the operating system disk cache. ** +** [[the xUnpin() page cache method]] ** ^xUnpin() is called by SQLite with a pointer to a currently pinned page ** as its second argument. If the third parameter, discard, is non-zero, ** then the page must be evicted from the cache. @@ -5712,6 +6002,7 @@ typedef struct sqlite3_pcache sqlite3_pcache; ** call to xUnpin() unpins the page regardless of the number of prior calls ** to xFetch(). ** +** [[the xRekey() page cache methods]] ** The xRekey() method is used to change the key value associated with the ** page passed as the second argument. If the cache ** previously contains an entry associated with newKey, it must be @@ -5724,6 +6015,7 @@ typedef struct sqlite3_pcache sqlite3_pcache; ** of these pages are pinned, they are implicitly unpinned, meaning that ** they can be safely discarded. ** +** [[the xDestroy() page cache method]] ** ^The xDestroy() method is used to delete a cache allocated by xCreate(). ** All resources associated with the specified cache should be freed. ^After ** calling the xDestroy() method, SQLite considers the [sqlite3_pcache*] @@ -5766,11 +6058,12 @@ typedef struct sqlite3_backup sqlite3_backup; ** ** See Also: [Using the SQLite Online Backup API] ** -** ^Exclusive access is required to the destination database for the -** duration of the operation. ^However the source database is only -** read-locked while it is actually being read; it is not locked -** continuously for the entire backup operation. ^Thus, the backup may be -** performed on a live source database without preventing other users from +** ^SQLite holds a write transaction open on the destination database file +** for the duration of the backup operation. +** ^The source database is read-locked only while it is being read; +** it is not locked continuously for the entire backup operation. +** ^Thus, the backup may be performed on a live source database without +** preventing other database connections from ** reading or writing to the source database while the backup is underway. ** ** ^(To perform a backup operation: @@ -5785,7 +6078,7 @@ typedef struct sqlite3_backup sqlite3_backup; ** There should be exactly one call to sqlite3_backup_finish() for each ** successful call to sqlite3_backup_init(). ** -** <b>sqlite3_backup_init()</b> +** [[sqlite3_backup_init()]] <b>sqlite3_backup_init()</b> ** ** ^The D and N arguments to sqlite3_backup_init(D,N,S,M) are the ** [database connection] associated with the destination database @@ -5797,11 +6090,11 @@ typedef struct sqlite3_backup sqlite3_backup; ** sqlite3_backup_init(D,N,S,M) identify the [database connection] ** and database name of the source database, respectively. ** ^The source and destination [database connections] (parameters S and D) -** must be different or else sqlite3_backup_init(D,N,S,M) will file with +** must be different or else sqlite3_backup_init(D,N,S,M) will fail with ** an error. ** ** ^If an error occurs within sqlite3_backup_init(D,N,S,M), then NULL is -** returned and an error code and error message are store3d in the +** returned and an error code and error message are stored in the ** destination [database connection] D. ** ^The error code and message for the failed call to sqlite3_backup_init() ** can be retrieved using the [sqlite3_errcode()], [sqlite3_errmsg()], and/or @@ -5812,13 +6105,13 @@ typedef struct sqlite3_backup sqlite3_backup; ** sqlite3_backup_finish() functions to perform the specified backup ** operation. ** -** <b>sqlite3_backup_step()</b> +** [[sqlite3_backup_step()]] <b>sqlite3_backup_step()</b> ** ** ^Function sqlite3_backup_step(B,N) will copy up to N pages between ** the source and destination databases specified by [sqlite3_backup] object B. ** ^If N is negative, all remaining source pages are copied. ** ^If sqlite3_backup_step(B,N) successfully copies N pages and there -** are still more pages to be copied, then the function resturns [SQLITE_OK]. +** are still more pages to be copied, then the function returns [SQLITE_OK]. ** ^If sqlite3_backup_step(B,N) successfully finishes copying all pages ** from source to destination, then it returns [SQLITE_DONE]. ** ^If an error occurs while running sqlite3_backup_step(B,N), @@ -5832,7 +6125,7 @@ typedef struct sqlite3_backup sqlite3_backup; ** <li> the destination database was opened read-only, or ** <li> the destination database is using write-ahead-log journaling ** and the destination and source page sizes differ, or -** <li> The destination database is an in-memory database and the +** <li> the destination database is an in-memory database and the ** destination and source page sizes differ. ** </ol>)^ ** @@ -5869,7 +6162,7 @@ typedef struct sqlite3_backup sqlite3_backup; ** by the backup operation, then the backup database is automatically ** updated at the same time. ** -** <b>sqlite3_backup_finish()</b> +** [[sqlite3_backup_finish()]] <b>sqlite3_backup_finish()</b> ** ** When sqlite3_backup_step() has returned [SQLITE_DONE], or when the ** application wishes to abandon the backup operation, the application @@ -5892,7 +6185,8 @@ typedef struct sqlite3_backup sqlite3_backup; ** is not a permanent error and does not affect the return value of ** sqlite3_backup_finish(). ** -** <b>sqlite3_backup_remaining(), sqlite3_backup_pagecount()</b> +** [[sqlite3_backup__remaining()]] [[sqlite3_backup_pagecount()]] +** <b>sqlite3_backup_remaining() and sqlite3_backup_pagecount()</b> ** ** ^Each call to sqlite3_backup_step() sets two values inside ** the [sqlite3_backup] object: the number of pages still to be backed @@ -6163,7 +6457,8 @@ SQLITE_API void *sqlite3_wal_hook( ** from SQL. ** ** ^Every new [database connection] defaults to having the auto-checkpoint -** enabled with a threshold of 1000 pages. The use of this interface +** enabled with a threshold of 1000 or [SQLITE_DEFAULT_WAL_AUTOCHECKPOINT] +** pages. The use of this interface ** is only necessary if the default setting is found to be suboptimal ** for a particular application. */ @@ -6182,10 +6477,190 @@ SQLITE_API int sqlite3_wal_autocheckpoint(sqlite3 *db, int N); ** from SQL. ^The [sqlite3_wal_autocheckpoint()] interface and the ** [wal_autocheckpoint pragma] can be used to cause this interface to be ** run whenever the WAL reaches a certain size threshold. +** +** See also: [sqlite3_wal_checkpoint_v2()] */ SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb); /* +** CAPI3REF: Checkpoint a database +** +** Run a checkpoint operation on WAL database zDb attached to database +** handle db. The specific operation is determined by the value of the +** eMode parameter: +** +** <dl> +** <dt>SQLITE_CHECKPOINT_PASSIVE<dd> +** Checkpoint as many frames as possible without waiting for any database +** readers or writers to finish. Sync the db file if all frames in the log +** are checkpointed. This mode is the same as calling +** sqlite3_wal_checkpoint(). The busy-handler callback is never invoked. +** +** <dt>SQLITE_CHECKPOINT_FULL<dd> +** This mode blocks (calls the busy-handler callback) until there is no +** database writer and all readers are reading from the most recent database +** snapshot. It then checkpoints all frames in the log file and syncs the +** database file. This call blocks database writers while it is running, +** but not database readers. +** +** <dt>SQLITE_CHECKPOINT_RESTART<dd> +** This mode works the same way as SQLITE_CHECKPOINT_FULL, except after +** checkpointing the log file it blocks (calls the busy-handler callback) +** until all readers are reading from the database file only. This ensures +** that the next client to write to the database file restarts the log file +** from the beginning. This call blocks database writers while it is running, +** but not database readers. +** </dl> +** +** If pnLog is not NULL, then *pnLog is set to the total number of frames in +** the log file before returning. If pnCkpt is not NULL, then *pnCkpt is set to +** the total number of checkpointed frames (including any that were already +** checkpointed when this function is called). *pnLog and *pnCkpt may be +** populated even if sqlite3_wal_checkpoint_v2() returns other than SQLITE_OK. +** If no values are available because of an error, they are both set to -1 +** before returning to communicate this to the caller. +** +** All calls obtain an exclusive "checkpoint" lock on the database file. If +** any other process is running a checkpoint operation at the same time, the +** lock cannot be obtained and SQLITE_BUSY is returned. Even if there is a +** busy-handler configured, it will not be invoked in this case. +** +** The SQLITE_CHECKPOINT_FULL and RESTART modes also obtain the exclusive +** "writer" lock on the database file. If the writer lock cannot be obtained +** immediately, and a busy-handler is configured, it is invoked and the writer +** lock retried until either the busy-handler returns 0 or the lock is +** successfully obtained. The busy-handler is also invoked while waiting for +** database readers as described above. If the busy-handler returns 0 before +** the writer lock is obtained or while waiting for database readers, the +** checkpoint operation proceeds from that point in the same way as +** SQLITE_CHECKPOINT_PASSIVE - checkpointing as many frames as possible +** without blocking any further. SQLITE_BUSY is returned in this case. +** +** If parameter zDb is NULL or points to a zero length string, then the +** specified operation is attempted on all WAL databases. In this case the +** values written to output parameters *pnLog and *pnCkpt are undefined. If +** an SQLITE_BUSY error is encountered when processing one or more of the +** attached WAL databases, the operation is still attempted on any remaining +** attached databases and SQLITE_BUSY is returned to the caller. If any other +** error occurs while processing an attached database, processing is abandoned +** and the error code returned to the caller immediately. If no error +** (SQLITE_BUSY or otherwise) is encountered while processing the attached +** databases, SQLITE_OK is returned. +** +** If database zDb is the name of an attached database that is not in WAL +** mode, SQLITE_OK is returned and both *pnLog and *pnCkpt set to -1. If +** zDb is not NULL (or a zero length string) and is not the name of any +** attached database, SQLITE_ERROR is returned to the caller. +*/ +SQLITE_API int sqlite3_wal_checkpoint_v2( + sqlite3 *db, /* Database handle */ + const char *zDb, /* Name of attached database (or NULL) */ + int eMode, /* SQLITE_CHECKPOINT_* value */ + int *pnLog, /* OUT: Size of WAL log in frames */ + int *pnCkpt /* OUT: Total number of frames checkpointed */ +); + +/* +** CAPI3REF: Checkpoint operation parameters +** +** These constants can be used as the 3rd parameter to +** [sqlite3_wal_checkpoint_v2()]. See the [sqlite3_wal_checkpoint_v2()] +** documentation for additional information about the meaning and use of +** each of these values. +*/ +#define SQLITE_CHECKPOINT_PASSIVE 0 +#define SQLITE_CHECKPOINT_FULL 1 +#define SQLITE_CHECKPOINT_RESTART 2 + +/* +** CAPI3REF: Virtual Table Interface Configuration +** +** This function may be called by either the [xConnect] or [xCreate] method +** of a [virtual table] implementation to configure +** various facets of the virtual table interface. +** +** If this interface is invoked outside the context of an xConnect or +** xCreate virtual table method then the behavior is undefined. +** +** At present, there is only one option that may be configured using +** this function. (See [SQLITE_VTAB_CONSTRAINT_SUPPORT].) Further options +** may be added in the future. +*/ +SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...); + +/* +** CAPI3REF: Virtual Table Configuration Options +** +** These macros define the various options to the +** [sqlite3_vtab_config()] interface that [virtual table] implementations +** can use to customize and optimize their behavior. +** +** <dl> +** <dt>SQLITE_VTAB_CONSTRAINT_SUPPORT +** <dd>Calls of the form +** [sqlite3_vtab_config](db,SQLITE_VTAB_CONSTRAINT_SUPPORT,X) are supported, +** where X is an integer. If X is zero, then the [virtual table] whose +** [xCreate] or [xConnect] method invoked [sqlite3_vtab_config()] does not +** support constraints. In this configuration (which is the default) if +** a call to the [xUpdate] method returns [SQLITE_CONSTRAINT], then the entire +** statement is rolled back as if [ON CONFLICT | OR ABORT] had been +** specified as part of the users SQL statement, regardless of the actual +** ON CONFLICT mode specified. +** +** If X is non-zero, then the virtual table implementation guarantees +** that if [xUpdate] returns [SQLITE_CONSTRAINT], it will do so before +** any modifications to internal or persistent data structures have been made. +** If the [ON CONFLICT] mode is ABORT, FAIL, IGNORE or ROLLBACK, SQLite +** is able to roll back a statement or database transaction, and abandon +** or continue processing the current SQL statement as appropriate. +** If the ON CONFLICT mode is REPLACE and the [xUpdate] method returns +** [SQLITE_CONSTRAINT], SQLite handles this as if the ON CONFLICT mode +** had been ABORT. +** +** Virtual table implementations that are required to handle OR REPLACE +** must do so within the [xUpdate] method. If a call to the +** [sqlite3_vtab_on_conflict()] function indicates that the current ON +** CONFLICT policy is REPLACE, the virtual table implementation should +** silently replace the appropriate rows within the xUpdate callback and +** return SQLITE_OK. Or, if this is not possible, it may return +** SQLITE_CONSTRAINT, in which case SQLite falls back to OR ABORT +** constraint handling. +** </dl> +*/ +#define SQLITE_VTAB_CONSTRAINT_SUPPORT 1 + +/* +** CAPI3REF: Determine The Virtual Table Conflict Policy +** +** This function may only be called from within a call to the [xUpdate] method +** of a [virtual table] implementation for an INSERT or UPDATE operation. ^The +** value returned is one of [SQLITE_ROLLBACK], [SQLITE_IGNORE], [SQLITE_FAIL], +** [SQLITE_ABORT], or [SQLITE_REPLACE], according to the [ON CONFLICT] mode +** of the SQL statement that triggered the call to the [xUpdate] method of the +** [virtual table]. +*/ +SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *); + +/* +** CAPI3REF: Conflict resolution modes +** +** These constants are returned by [sqlite3_vtab_on_conflict()] to +** inform a [virtual table] implementation what the [ON CONFLICT] mode +** is for the SQL statement being evaluated. +** +** Note that the [SQLITE_IGNORE] constant is also used as a potential +** return value from the [sqlite3_set_authorizer()] callback and that +** [SQLITE_ABORT] is also a [result code]. +*/ +#define SQLITE_ROLLBACK 1 +/* #define SQLITE_IGNORE 2 // Also used by sqlite3_authorizer() callback */ +#define SQLITE_FAIL 3 +/* #define SQLITE_ABORT 4 // Also an error code */ +#define SQLITE_REPLACE 5 + + + +/* ** Undo the hack that converts floating point types to integer for ** builds on processors without floating point support. */ diff --git a/ext/sqlite3/sqlite3.c b/ext/sqlite3/sqlite3.c index 059f560ea..aa01d89cc 100644 --- a/ext/sqlite3/sqlite3.c +++ b/ext/sqlite3/sqlite3.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: sqlite3.c 307203 2011-01-07 01:11:16Z felipe $ */ +/* $Id: sqlite3.c 314749 2011-08-10 15:30:07Z iliaa $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -89,7 +89,7 @@ zend_class_entry *php_sqlite3_sc_entry; zend_class_entry *php_sqlite3_stmt_entry; zend_class_entry *php_sqlite3_result_entry; -/* {{{ proto bool SQLite3::open(String filename [, int Flags [, string Encryption Key]]) +/* {{{ proto void SQLite3::open(String filename [, int Flags [, string Encryption Key]]) Opens a SQLite 3 Database, if the build includes encryption then it will attempt to use the key. */ PHP_METHOD(sqlite3, open) { @@ -1816,7 +1816,7 @@ static zend_function_entry php_sqlite3_class_methods[] = { PHP_ME(sqlite3, enableExceptions, argingo_sqlite3_enableexceptions, ZEND_ACC_PUBLIC) /* Aliases */ PHP_MALIAS(sqlite3, __construct, open, arginfo_sqlite3_open, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ @@ -1831,7 +1831,7 @@ static zend_function_entry php_sqlite3_stmt_class_methods[] = { PHP_ME(sqlite3stmt, bindValue, arginfo_sqlite3stmt_bindvalue, ZEND_ACC_PUBLIC) PHP_ME(sqlite3stmt, readOnly, arginfo_sqlite3_void, ZEND_ACC_PUBLIC) PHP_ME(sqlite3stmt, __construct, arginfo_sqlite3stmt_construct, ZEND_ACC_PRIVATE|ZEND_ACC_CTOR) - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ @@ -1844,7 +1844,7 @@ static zend_function_entry php_sqlite3_result_class_methods[] = { PHP_ME(sqlite3result, reset, arginfo_sqlite3_void, ZEND_ACC_PUBLIC) PHP_ME(sqlite3result, finalize, arginfo_sqlite3_void, ZEND_ACC_PUBLIC) PHP_ME(sqlite3result, __construct, arginfo_sqlite3_void, ZEND_ACC_PRIVATE|ZEND_ACC_CTOR) - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ @@ -2006,7 +2006,7 @@ static zend_object_value php_sqlite3_object_new(zend_class_entry *class_type TSR /* Allocate memory for it */ intern = emalloc(sizeof(php_sqlite3_db_object)); - memset(&intern->zo, 0, sizeof(php_sqlite3_db_object)); + memset(intern, 0, sizeof(php_sqlite3_db_object)); intern->exception = 0; /* Need to keep track of things to free */ @@ -2030,7 +2030,7 @@ static zend_object_value php_sqlite3_stmt_object_new(zend_class_entry *class_typ /* Allocate memory for it */ intern = emalloc(sizeof(php_sqlite3_stmt)); - memset(&intern->zo, 0, sizeof(php_sqlite3_stmt)); + memset(intern, 0, sizeof(php_sqlite3_stmt)); intern->db_obj_zval = NULL; @@ -2052,7 +2052,7 @@ static zend_object_value php_sqlite3_result_object_new(zend_class_entry *class_t /* Allocate memory for it */ intern = emalloc(sizeof(php_sqlite3_result)); - memset(&intern->zo, 0, sizeof(php_sqlite3_result)); + memset(intern, 0, sizeof(php_sqlite3_result)); intern->complete = 0; intern->is_prepared_statement = 0; diff --git a/ext/standard/array.c b/ext/standard/array.c index 030bb0e4f..f0ed03b0c 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -21,7 +21,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: array.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: array.c 309986 2011-04-06 10:23:06Z aharvey $ */ #include "php.h" #include "php_ini.h" @@ -606,7 +606,7 @@ static int php_array_user_compare(const void *a, const void *b TSRMLS_DC) /* {{{ /* Clear FCI cache otherwise : for example the same or other array with * (partly) the same key values has been sorted with uasort() or - * other sorting function the comparison is cached, however the the name + * other sorting function the comparison is cached, however the name * of the function for comparison is not respected. see bug #28739 AND #33295 * * Following defines will assist in backup / restore values. */ @@ -1688,28 +1688,32 @@ PHP_FUNCTION(range) } } else if (Z_TYPE_P(zlow) == IS_DOUBLE || Z_TYPE_P(zhigh) == IS_DOUBLE || is_step_double) { - double low, high; + double low, high, value; + long i; double_str: convert_to_double(zlow); convert_to_double(zhigh); low = Z_DVAL_P(zlow); high = Z_DVAL_P(zhigh); + i = 0; if (low > high) { /* Negative steps */ if (low - high < step || step <= 0) { err = 1; goto err; } - for (; low >= (high - DOUBLE_DRIFT_FIX); low -= step) { - add_next_index_double(return_value, low); + + for (value = low; value >= (high - DOUBLE_DRIFT_FIX); value = low - (++i * step)) { + add_next_index_double(return_value, value); } } else if (high > low) { /* Positive steps */ if (high - low < step || step <= 0) { err = 1; goto err; } - for (; low <= (high + DOUBLE_DRIFT_FIX); low += step) { - add_next_index_double(return_value, low); + + for (value = low; value <= (high + DOUBLE_DRIFT_FIX); value = low + (++i * step)) { + add_next_index_double(return_value, value); } } else { add_next_index_double(return_value, low); diff --git a/ext/standard/assert.c b/ext/standard/assert.c index 778628613..c0f8cd4b5 100644 --- a/ext/standard/assert.c +++ b/ext/standard/assert.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: assert.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: assert.c 311451 2011-05-26 18:17:43Z iliaa $ */ /* {{{ includes */ #include "php.h" @@ -253,8 +253,8 @@ PHP_FUNCTION(assert_options) case ASSERT_ACTIVE: oldint = ASSERTG(active); if (ac == 2) { - convert_to_long_ex(value); - ASSERTG(active) = Z_LVAL_PP(value); + convert_to_string_ex(value); + zend_alter_ini_entry_ex("assert.active", sizeof("assert.active"), Z_STRVAL_PP(value), Z_STRLEN_PP(value), PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0 TSRMLS_CC); } RETURN_LONG(oldint); break; @@ -262,8 +262,8 @@ PHP_FUNCTION(assert_options) case ASSERT_BAIL: oldint = ASSERTG(bail); if (ac == 2) { - convert_to_long_ex(value); - ASSERTG(bail) = Z_LVAL_PP(value); + convert_to_string_ex(value); + zend_alter_ini_entry_ex("assert.bail", sizeof("assert.bail"), Z_STRVAL_PP(value), Z_STRLEN_PP(value), PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0 TSRMLS_CC); } RETURN_LONG(oldint); break; @@ -271,8 +271,8 @@ PHP_FUNCTION(assert_options) case ASSERT_QUIET_EVAL: oldint = ASSERTG(quiet_eval); if (ac == 2) { - convert_to_long_ex(value); - ASSERTG(quiet_eval) = Z_LVAL_PP(value); + convert_to_string_ex(value); + zend_alter_ini_entry_ex("assert.quiet_eval", sizeof("assert.quiet_eval"), Z_STRVAL_PP(value), Z_STRLEN_PP(value), PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0 TSRMLS_CC); } RETURN_LONG(oldint); break; @@ -280,8 +280,8 @@ PHP_FUNCTION(assert_options) case ASSERT_WARNING: oldint = ASSERTG(warning); if (ac == 2) { - convert_to_long_ex(value); - ASSERTG(warning) = Z_LVAL_PP(value); + convert_to_string_ex(value); + zend_alter_ini_entry_ex("assert.warning", sizeof("assert.warning"), Z_STRVAL_PP(value), Z_STRLEN_PP(value), PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0 TSRMLS_CC); } RETURN_LONG(oldint); break; diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index de8a2cea7..6df993858 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: basic_functions.c 308127 2011-02-08 16:29:34Z cataphract $ */ +/* $Id: basic_functions.c 314452 2011-08-08 00:47:40Z laruence $ */ #include "php.h" #include "php_streams.h" @@ -3360,7 +3360,7 @@ const zend_function_entry basic_functions[] = { /* {{{ */ PHP_FE(sys_get_temp_dir, arginfo_sys_get_temp_dir) - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ @@ -3401,7 +3401,7 @@ PHP_INI_END() static const zend_module_dep standard_deps[] = { /* {{{ */ ZEND_MOD_OPTIONAL("session") - {NULL, NULL, NULL} + ZEND_MOD_END }; /* }}} */ @@ -3803,6 +3803,7 @@ PHP_RSHUTDOWN_FUNCTION(basic) /* {{{ */ } PHP_RSHUTDOWN(user_filters)(SHUTDOWN_FUNC_ARGS_PASSTHRU); + PHP_RSHUTDOWN(browscap)(SHUTDOWN_FUNC_ARGS_PASSTHRU); BG(page_uid) = -1; BG(page_gid) = -1; @@ -4257,7 +4258,8 @@ PHP_FUNCTION(getopt) /* Get argv from the global symbol table. We calculate argc ourselves * in order to be on the safe side, even though it is also available * from the symbol table. */ - if ((zend_hash_find(HASH_OF(PG(http_globals)[TRACK_VARS_SERVER]), "argv", sizeof("argv"), (void **) &args) != FAILURE || + if (PG(http_globals)[TRACK_VARS_SERVER] && + (zend_hash_find(HASH_OF(PG(http_globals)[TRACK_VARS_SERVER]), "argv", sizeof("argv"), (void **) &args) != FAILURE || zend_hash_find(&EG(symbol_table), "argv", sizeof("argv"), (void **) &args) != FAILURE) && Z_TYPE_PP(args) == IS_ARRAY ) { int pos = 0; @@ -4316,10 +4318,6 @@ PHP_FUNCTION(getopt) memset(opts, 0, count * sizeof(opt_struct)); - if (!opts) { - RETURN_FALSE; - } - /* Reset the array indexes. */ zend_hash_internal_pointer_reset(Z_ARRVAL_P(p_longopts)); @@ -4676,7 +4674,7 @@ PHP_FUNCTION(error_log) opt_err = erropt; } - if (opt_err == 3) { + if (opt_err == 3 && opt) { if (strlen(opt) != opt_len) { RETURN_FALSE; } diff --git a/ext/standard/basic_functions.h b/ext/standard/basic_functions.h index 337b003bd..248a4aa53 100644 --- a/ext/standard/basic_functions.h +++ b/ext/standard/basic_functions.h @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: basic_functions.h 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: basic_functions.h 310691 2011-05-01 18:37:20Z cataphract $ */ #ifndef BASIC_FUNCTIONS_H #define BASIC_FUNCTIONS_H @@ -140,6 +140,7 @@ PHP_FUNCTION(stream_bucket_append); PHP_FUNCTION(stream_bucket_new); PHP_MINIT_FUNCTION(user_filters); PHP_RSHUTDOWN_FUNCTION(user_filters); +PHP_RSHUTDOWN_FUNCTION(browscap); /* Left for BC (not binary safe!) */ PHPAPI int _php_error_log(int opt_err, char *message, char *opt, char *headers TSRMLS_DC); diff --git a/ext/standard/browscap.c b/ext/standard/browscap.c index 9a3672d8c..68b5c5c8c 100644 --- a/ext/standard/browscap.c +++ b/ext/standard/browscap.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: browscap.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: browscap.c 311764 2011-06-03 09:39:45Z cataphract $ */ #include "php.h" #include "php_browscap.h" @@ -27,18 +27,51 @@ #include "zend_ini_scanner.h" #include "zend_globals.h" -static HashTable browser_hash; -static zval *current_section; -static char *current_section_name; +typedef struct { + HashTable *htab; + zval *current_section; + char *current_section_name; + char filename[MAXPATHLEN]; +} browser_data; + +/* browser data defined in startup phase, eagerly loaded in MINIT */ +static browser_data global_bdata = {0}; + +/* browser data defined in activation phase, lazily loaded in get_browser. + * Per request and per thread, if applicable */ +ZEND_BEGIN_MODULE_GLOBALS(browscap) + browser_data activation_bdata; +ZEND_END_MODULE_GLOBALS(browscap) + +ZEND_DECLARE_MODULE_GLOBALS(browscap); + +#ifdef ZTS +#define BROWSCAP_G(v) TSRMG(browscap_globals_id, zend_browscap_globals *, v) +#else +#define BROWSCAP_G(v) (browscap_globals.v) +#endif #define DEFAULT_SECTION_NAME "Default Browser Capability Settings" /* OBJECTS_FIXME: This whole extension needs going through. The use of objects looks pretty broken here */ -static void browscap_entry_dtor(zval **zvalue) /* {{{ */ +static void browscap_entry_dtor_request(zval **zvalue) /* {{{ */ { if (Z_TYPE_PP(zvalue) == IS_ARRAY) { zend_hash_destroy(Z_ARRVAL_PP(zvalue)); + efree(Z_ARRVAL_PP(zvalue)); + } else if (Z_TYPE_PP(zvalue) == IS_STRING) { + if (Z_STRVAL_PP(zvalue)) { + efree(Z_STRVAL_PP(zvalue)); + } + } + efree(*zvalue); +} +/* }}} */ + +static void browscap_entry_dtor_persistent(zval **zvalue) /* {{{ */ { + if (Z_TYPE_PP(zvalue) == IS_ARRAY) { + zend_hash_destroy(Z_ARRVAL_PP(zvalue)); free(Z_ARRVAL_PP(zvalue)); } else if (Z_TYPE_PP(zvalue) == IS_STRING) { if (Z_STRVAL_PP(zvalue)) { @@ -49,16 +82,16 @@ static void browscap_entry_dtor(zval **zvalue) /* {{{ */ } /* }}} */ -static void convert_browscap_pattern(zval *pattern) /* {{{ */ +static void convert_browscap_pattern(zval *pattern, int persistent) /* {{{ */ { int i, j=0; char *t; php_strtolower(Z_STRVAL_P(pattern), Z_STRLEN_P(pattern)); - t = (char *) safe_pemalloc(Z_STRLEN_P(pattern), 2, 5, 1); + t = (char *) safe_pemalloc(Z_STRLEN_P(pattern), 2, 5, persistent); - t[j++] = '§'; + t[j++] = '\xA7'; /* section sign */ t[j++] = '^'; for (i=0; i<Z_STRLEN_P(pattern); i++, j++) { @@ -86,9 +119,9 @@ static void convert_browscap_pattern(zval *pattern) /* {{{ */ t[j++] = '\\'; t[j] = ')'; break; - case '§': + case '\xA7': t[j++] = '\\'; - t[j] = '§'; + t[j] = '\xA7'; break; default: t[j] = Z_STRVAL_P(pattern)[i]; @@ -97,7 +130,7 @@ static void convert_browscap_pattern(zval *pattern) /* {{{ */ } t[j++] = '$'; - t[j++] = '§'; + t[j++] = '\xA7'; t[j]=0; Z_STRVAL_P(pattern) = t; @@ -107,26 +140,31 @@ static void convert_browscap_pattern(zval *pattern) /* {{{ */ static void php_browscap_parser_cb(zval *arg1, zval *arg2, zval *arg3, int callback_type, void *arg TSRMLS_DC) /* {{{ */ { + browser_data *bdata = arg; + int persistent = bdata->htab->persistent; + if (!arg1) { return; } switch (callback_type) { case ZEND_INI_PARSER_ENTRY: - if (current_section && arg2) { + if (bdata->current_section && arg2) { zval *new_property; char *new_key; /* parent entry can not be same as current section -> causes infinite loop! */ if (!strcasecmp(Z_STRVAL_P(arg1), "parent") && - current_section_name != NULL && - !strcasecmp(current_section_name, Z_STRVAL_P(arg2)) + bdata->current_section_name != NULL && + !strcasecmp(bdata->current_section_name, Z_STRVAL_P(arg2)) ) { - zend_error(E_CORE_ERROR, "Invalid browscap ini file: 'Parent' value cannot be same as the section name: %s (in file %s)", current_section_name, INI_STR("browscap")); + zend_error(E_CORE_ERROR, "Invalid browscap ini file: " + "'Parent' value cannot be same as the section name: %s " + "(in file %s)", bdata->current_section_name, INI_STR("browscap")); return; } - new_property = (zval *) pemalloc(sizeof(zval), 1); + new_property = (zval *) pemalloc(sizeof(zval), persistent); INIT_PZVAL(new_property); Z_TYPE_P(new_property) = IS_STRING; @@ -135,7 +173,7 @@ static void php_browscap_parser_cb(zval *arg1, zval *arg2, zval *arg3, int callb (Z_STRLEN_P(arg2) == 3 && !strncasecmp(Z_STRVAL_P(arg2), "yes", sizeof("yes") - 1)) || (Z_STRLEN_P(arg2) == 4 && !strncasecmp(Z_STRVAL_P(arg2), "true", sizeof("true") - 1)) ) { - Z_STRVAL_P(new_property) = zend_strndup("1", 1); + Z_STRVAL_P(new_property) = pestrndup("1", 1, persistent); Z_STRLEN_P(new_property) = 1; } else if ( (Z_STRLEN_P(arg2) == 2 && !strncasecmp(Z_STRVAL_P(arg2), "no", sizeof("no") - 1)) || @@ -143,16 +181,17 @@ static void php_browscap_parser_cb(zval *arg1, zval *arg2, zval *arg3, int callb (Z_STRLEN_P(arg2) == 4 && !strncasecmp(Z_STRVAL_P(arg2), "none", sizeof("none") - 1)) || (Z_STRLEN_P(arg2) == 5 && !strncasecmp(Z_STRVAL_P(arg2), "false", sizeof("false") - 1)) ) { - Z_STRVAL_P(new_property) = zend_strndup("", 0); + Z_STRVAL_P(new_property) = pestrndup("", 0, persistent); Z_STRLEN_P(new_property) = 0; } else { /* Other than true/false setting */ - Z_STRVAL_P(new_property) = zend_strndup(Z_STRVAL_P(arg2), Z_STRLEN_P(arg2)); + Z_STRVAL_P(new_property) = pestrndup(Z_STRVAL_P(arg2), + Z_STRLEN_P(arg2), persistent); Z_STRLEN_P(new_property) = Z_STRLEN_P(arg2); } - new_key = zend_strndup(Z_STRVAL_P(arg1), Z_STRLEN_P(arg1)); + new_key = pestrndup(Z_STRVAL_P(arg1), Z_STRLEN_P(arg1), persistent); zend_str_tolower(new_key, Z_STRLEN_P(arg1)); - zend_hash_update(Z_ARRVAL_P(current_section), new_key, Z_STRLEN_P(arg1) + 1, &new_property, sizeof(zval *), NULL); - free(new_key); + zend_hash_update(Z_ARRVAL_P(bdata->current_section), new_key, Z_STRLEN_P(arg1) + 1, &new_property, sizeof(zval *), NULL); + pefree(new_key, persistent); } break; case ZEND_INI_PARSER_SECTION: { @@ -161,23 +200,27 @@ static void php_browscap_parser_cb(zval *arg1, zval *arg2, zval *arg3, int callb HashTable *section_properties; /*printf("'%s' (%d)\n",$1.value.str.val,$1.value.str.len + 1);*/ - current_section = (zval *) pemalloc(sizeof(zval), 1); - INIT_PZVAL(current_section); - processed = (zval *) pemalloc(sizeof(zval), 1); + bdata->current_section = (zval *) pemalloc(sizeof(zval), persistent); + INIT_PZVAL(bdata->current_section); + processed = (zval *) pemalloc(sizeof(zval), persistent); INIT_PZVAL(processed); - unprocessed = (zval *) pemalloc(sizeof(zval), 1); + unprocessed = (zval *) pemalloc(sizeof(zval), persistent); INIT_PZVAL(unprocessed); - section_properties = (HashTable *) pemalloc(sizeof(HashTable), 1); - zend_hash_init(section_properties, 0, NULL, (dtor_func_t) browscap_entry_dtor, 1); - Z_ARRVAL_P(current_section) = section_properties; - Z_TYPE_P(current_section) = IS_ARRAY; - if (current_section_name) { - free(current_section_name); + section_properties = (HashTable *) pemalloc(sizeof(HashTable), persistent); + zend_hash_init(section_properties, 0, NULL, + (dtor_func_t) (persistent?browscap_entry_dtor_persistent + :browscap_entry_dtor_request), + persistent); + Z_ARRVAL_P(bdata->current_section) = section_properties; + Z_TYPE_P(bdata->current_section) = IS_ARRAY; + if (bdata->current_section_name) { + pefree(bdata->current_section_name, persistent); } - current_section_name = zend_strndup(Z_STRVAL_P(arg1), Z_STRLEN_P(arg1)); + bdata->current_section_name = pestrndup(Z_STRVAL_P(arg1), + Z_STRLEN_P(arg1), persistent); - zend_hash_update(&browser_hash, Z_STRVAL_P(arg1), Z_STRLEN_P(arg1) + 1, (void *) ¤t_section, sizeof(zval *), NULL); + zend_hash_update(bdata->htab, Z_STRVAL_P(arg1), Z_STRLEN_P(arg1) + 1, (void *) &bdata->current_section, sizeof(zval *), NULL); Z_STRVAL_P(processed) = Z_STRVAL_P(arg1); Z_STRLEN_P(processed) = Z_STRLEN_P(arg1); @@ -185,9 +228,9 @@ static void php_browscap_parser_cb(zval *arg1, zval *arg2, zval *arg3, int callb Z_STRVAL_P(unprocessed) = Z_STRVAL_P(arg1); Z_STRLEN_P(unprocessed) = Z_STRLEN_P(arg1); Z_TYPE_P(unprocessed) = IS_STRING; - Z_STRVAL_P(unprocessed) = zend_strndup(Z_STRVAL_P(unprocessed), Z_STRLEN_P(unprocessed)); + Z_STRVAL_P(unprocessed) = pestrndup(Z_STRVAL_P(unprocessed), Z_STRLEN_P(unprocessed), persistent); - convert_browscap_pattern(processed); + convert_browscap_pattern(processed, persistent); zend_hash_update(section_properties, "browser_name_regex", sizeof("browser_name_regex"), (void *) &processed, sizeof(zval *), NULL); zend_hash_update(section_properties, "browser_name_pattern", sizeof("browser_name_pattern"), (void *) &unprocessed, sizeof(zval *), NULL); } @@ -196,45 +239,132 @@ static void php_browscap_parser_cb(zval *arg1, zval *arg2, zval *arg3, int callb } /* }}} */ -PHP_MINIT_FUNCTION(browscap) /* {{{ */ +static int browscap_read_file(char *filename, browser_data *browdata, int persistent TSRMLS_DC) /* {{{ */ { - char *browscap = INI_STR("browscap"); + zend_file_handle fh = {0}; + + if (filename == NULL || filename[0] == '\0') { + return FAILURE; + } + + browdata->htab = pemalloc(sizeof *browdata->htab, persistent); + if (browdata->htab == NULL) { + return FAILURE; + } - if (browscap && browscap[0]) { - zend_file_handle fh; - memset(&fh, 0, sizeof(fh)); + if (zend_hash_init_ex(browdata->htab, 0, NULL, + (dtor_func_t) (persistent?browscap_entry_dtor_persistent + :browscap_entry_dtor_request), + persistent, 0) == FAILURE) { + pefree(browdata->htab, persistent); + browdata->htab = NULL; + return FAILURE; + } + + fh.handle.fp = VCWD_FOPEN(filename, "r"); + fh.opened_path = NULL; + fh.free_filename = 0; + if (!fh.handle.fp) { + zend_hash_destroy(browdata->htab); + pefree(browdata->htab, persistent); + browdata->htab = NULL; + zend_error(E_CORE_WARNING, "Cannot open '%s' for reading", filename); + return FAILURE; + } + fh.filename = filename; + Z_TYPE(fh) = ZEND_HANDLE_FP; + browdata->current_section_name = NULL; + zend_parse_ini_file(&fh, 1, ZEND_INI_SCANNER_RAW, + (zend_ini_parser_cb_t) php_browscap_parser_cb, browdata TSRMLS_CC); + if (browdata->current_section_name != NULL) { + pefree(browdata->current_section_name, persistent); + browdata->current_section_name = NULL; + } + + return SUCCESS; +} +/* }}} */ + +#ifdef ZTS +static void browscap_globals_ctor(zend_browscap_globals *browscap_globals TSRMLS_DC) /* {{{ */ +{ + browscap_globals->activation_bdata.htab = NULL; + browscap_globals->activation_bdata.current_section = NULL; + browscap_globals->activation_bdata.current_section_name = NULL; + browscap_globals->activation_bdata.filename[0] = '\0'; +} +/* }}} */ +#endif + +static void browscap_bdata_dtor(browser_data *bdata, int persistent TSRMLS_DC) /* {{{ */ +{ + if (bdata->htab != NULL) { + zend_hash_destroy(bdata->htab); + pefree(bdata->htab, persistent); + bdata->htab = NULL; + } + bdata->filename[0] = '\0'; + /* current_section_* are only used during parsing */ +} +/* }}} */ - if (zend_hash_init_ex(&browser_hash, 0, NULL, (dtor_func_t) browscap_entry_dtor, 1, 0) == FAILURE) { +/* {{{ PHP_INI_MH + */ +PHP_INI_MH(OnChangeBrowscap) +{ + if (stage == PHP_INI_STAGE_STARTUP) { + /* value handled in browscap.c's MINIT */ + return SUCCESS; + } else if (stage == PHP_INI_STAGE_ACTIVATE) { + browser_data *bdata = &BROWSCAP_G(activation_bdata); + if (bdata->filename[0] != '\0') { + browscap_bdata_dtor(bdata, 0 TSRMLS_CC); + } + if (VCWD_REALPATH(new_value, bdata->filename) == NULL) { return FAILURE; } + return SUCCESS; + } + + return FAILURE; +} +/* }}} */ - fh.handle.fp = VCWD_FOPEN(browscap, "r"); - fh.opened_path = NULL; - fh.free_filename = 0; - if (!fh.handle.fp) { - zend_error(E_CORE_WARNING, "Cannot open '%s' for reading", browscap); +PHP_MINIT_FUNCTION(browscap) /* {{{ */ +{ + char *browscap = INI_STR("browscap"); + +#ifdef ZTS + ts_allocate_id(&browscap_globals_id, sizeof(browser_data), + (ts_allocate_ctor)browscap_globals_ctor, NULL); +#endif + /* ctor call not really needed for non-ZTS */ + + if (browscap && browscap[0]) { + if (browscap_read_file(browscap, &global_bdata, 1 TSRMLS_CC) == FAILURE) { return FAILURE; } - fh.filename = browscap; - Z_TYPE(fh) = ZEND_HANDLE_FP; - current_section_name = NULL; - zend_parse_ini_file(&fh, 1, ZEND_INI_SCANNER_RAW, (zend_ini_parser_cb_t) php_browscap_parser_cb, &browser_hash TSRMLS_CC); - if (current_section_name) { - free(current_section_name); - current_section_name = NULL; - } } return SUCCESS; } /* }}} */ -PHP_MSHUTDOWN_FUNCTION(browscap) /* {{{ */ +PHP_RSHUTDOWN_FUNCTION(browscap) /* {{{ */ { - char *browscap = INI_STR("browscap"); - if (browscap && browscap[0]) { - zend_hash_destroy(&browser_hash); + browser_data *bdata = &BROWSCAP_G(activation_bdata); + if (bdata->filename[0] != '\0') { + browscap_bdata_dtor(bdata, 0 TSRMLS_CC); } + + return SUCCESS; +} +/* }}} */ + +PHP_MSHUTDOWN_FUNCTION(browscap) /* {{{ */ +{ + browscap_bdata_dtor(&global_bdata, 1 TSRMLS_CC); + return SUCCESS; } /* }}} */ @@ -331,11 +461,21 @@ PHP_FUNCTION(get_browser) zval **agent, **z_agent_name, **http_user_agent; zval *found_browser_entry, *tmp_copy; char *lookup_browser_name; - char *browscap = INI_STR("browscap"); + browser_data *bdata; - if (!browscap || !browscap[0]) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "browscap ini directive not set"); - RETURN_FALSE; + if (BROWSCAP_G(activation_bdata).filename[0] != '\0') { + bdata = &BROWSCAP_G(activation_bdata); + if (bdata->htab == NULL) { /* not initialized yet */ + if (browscap_read_file(bdata->filename, bdata, 0 TSRMLS_CC) == FAILURE) { + RETURN_FALSE; + } + } + } else { + if (!global_bdata.htab) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "browscap ini directive not set"); + RETURN_FALSE; + } + bdata = &global_bdata; } if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!b", &agent_name, &agent_name_len, &return_array) == FAILURE) { @@ -357,13 +497,13 @@ PHP_FUNCTION(get_browser) lookup_browser_name = estrndup(agent_name, agent_name_len); php_strtolower(lookup_browser_name, agent_name_len); - if (zend_hash_find(&browser_hash, lookup_browser_name, agent_name_len + 1, (void **) &agent) == FAILURE) { + if (zend_hash_find(bdata->htab, lookup_browser_name, agent_name_len + 1, (void **) &agent) == FAILURE) { found_browser_entry = NULL; - zend_hash_apply_with_arguments(&browser_hash TSRMLS_CC, (apply_func_args_t) browser_reg_compare, 3, lookup_browser_name, agent_name_len, &found_browser_entry); + zend_hash_apply_with_arguments(bdata->htab TSRMLS_CC, (apply_func_args_t) browser_reg_compare, 3, lookup_browser_name, agent_name_len, &found_browser_entry); if (found_browser_entry) { agent = &found_browser_entry; - } else if (zend_hash_find(&browser_hash, DEFAULT_SECTION_NAME, sizeof(DEFAULT_SECTION_NAME), (void **) &agent) == FAILURE) { + } else if (zend_hash_find(bdata->htab, DEFAULT_SECTION_NAME, sizeof(DEFAULT_SECTION_NAME), (void **) &agent) == FAILURE) { efree(lookup_browser_name); RETURN_FALSE; } @@ -379,7 +519,7 @@ PHP_FUNCTION(get_browser) } while (zend_hash_find(Z_ARRVAL_PP(agent), "parent", sizeof("parent"), (void **) &z_agent_name) == SUCCESS) { - if (zend_hash_find(&browser_hash, Z_STRVAL_PP(z_agent_name), Z_STRLEN_PP(z_agent_name) + 1, (void **)&agent) == FAILURE) { + if (zend_hash_find(bdata->htab, Z_STRVAL_PP(z_agent_name), Z_STRLEN_PP(z_agent_name) + 1, (void **)&agent) == FAILURE) { break; } diff --git a/ext/standard/credits.c b/ext/standard/credits.c index 21272edc1..d4bf4116b 100644 --- a/ext/standard/credits.c +++ b/ext/standard/credits.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: credits.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: credits.c 311822 2011-06-05 06:57:13Z philip $ */ #include "php.h" #include "info.h" @@ -96,7 +96,7 @@ PHPAPI void php_print_credits(int flag TSRMLS_DC) /* {{{ */ php_info_print_table_colspan_header(2, "PHP Documentation"); CREDIT_LINE("Authors", "Mehdi Achour, Friedhelm Betz, Antony Dovgal, Nuno Lopes, Hannes Magnusson, Georg Richter, Damien Seguy, Jakub Vrana"); CREDIT_LINE("Editor", "Philip Olson"); - CREDIT_LINE("User Note Maintainers", "Friedhelm Betz, Etienne Kneuss, Nuno Lopes, Hannes Magnusson, Felipe Pena, Maciek Sokolewicz, Daniel P. Brown"); + CREDIT_LINE("User Note Maintainers", "Daniel P. Brown, Thiago Henrique Pojda"); CREDIT_LINE("Other Contributors", "Previously active authors, editors and other contributors are listed in the manual."); php_info_print_table_end(); } diff --git a/ext/standard/crypt.c b/ext/standard/crypt.c index 77e459d9f..cda330b5d 100644 --- a/ext/standard/crypt.c +++ b/ext/standard/crypt.c @@ -19,7 +19,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: crypt.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: crypt.c 314641 2011-08-09 12:16:58Z laruence $ */ #include <stdlib.h> @@ -170,15 +170,17 @@ PHP_FUNCTION(crypt) /* The automatic salt generation covers standard DES, md5-crypt and Blowfish (simple) */ if (!*salt) { #if PHP_MD5_CRYPT - strcpy(salt, "$1$"); + strncpy(salt, "$1$", PHP_MAX_SALT_LEN); php_to64(&salt[3], PHP_CRYPT_RAND, 4); php_to64(&salt[7], PHP_CRYPT_RAND, 4); - strcpy(&salt[11], "$"); + strncpy(&salt[11], "$", PHP_MAX_SALT_LEN - 11); #elif PHP_STD_DES_CRYPT php_to64(&salt[0], PHP_CRYPT_RAND, 2); salt[2] = '\0'; #endif salt_in_len = strlen(salt); + } else { + salt_in_len = MIN(PHP_MAX_SALT_LEN, salt_in_len); } /* Windows (win32/crypt) has a stripped down version of libxcrypt and @@ -240,7 +242,7 @@ PHP_FUNCTION(crypt) } else if ( salt[0] == '$' && salt[1] == '2' && - salt[2] == 'a' && + salt[2] >= 'a' && salt[2] <= 'z' && salt[3] == '$' && salt[4] >= '0' && salt[4] <= '3' && salt[5] >= '0' && salt[5] <= '9' && diff --git a/ext/standard/crypt_blowfish.c b/ext/standard/crypt_blowfish.c index 37160842e..fb9577208 100644 --- a/ext/standard/crypt_blowfish.c +++ b/ext/standard/crypt_blowfish.c @@ -1,28 +1,39 @@ +/* $Id: crypt_blowfish.c 313406 2011-07-18 21:26:29Z pajoye $ */ /* - $Id: crypt_blowfish.c 295339 2010-02-21 23:47:14Z pajoye $ -*/ -/* + * The crypt_blowfish homepage is: + * + * http://www.openwall.com/crypt/ + * * This code comes from John the Ripper password cracker, with reentrant * and crypt(3) interfaces added, but optimizations specific to password * cracking removed. * - * Written by Solar Designer <solar at openwall.com> in 1998-2002 and - * placed in the public domain. + * Written by Solar Designer <solar at openwall.com> in 1998-2011. + * No copyright is claimed, and the software is hereby placed in the public + * domain. In case this attempt to disclaim copyright and place the software + * in the public domain is deemed null and void, then the software is + * Copyright (c) 1998-2011 Solar Designer and it is hereby released to the + * general public under the following terms: * - * There's absolutely no warranty. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted. + * + * There's ABSOLUTELY NO WARRANTY, express or implied. * * It is my intent that you should be able to use this on your system, - * as a part of a software package, or anywhere else to improve security, + * as part of a software package, or anywhere else to improve security, * ensure compatibility, or for any other purpose. I would appreciate * it if you give credit where it is due and keep your modifications in * the public domain as well, but I don't require that in order to let * you place this code and any modifications you make under a license * of your choice. * - * This implementation is compatible with OpenBSD bcrypt.c (version 2a) - * by Niels Provos <provos at citi.umich.edu>, and uses some of his + * This implementation is mostly compatible with OpenBSD's bcrypt.c (prefix + * "$2a$") by Niels Provos <provos at citi.umich.edu>, and uses some of his * ideas. The password hashing algorithm was designed by David Mazieres - * <dm at lcs.mit.edu>. + * <dm at lcs.mit.edu>. For more information on the level of compatibility, + * please refer to the comments in BF_set_key() below and to the crypt(3) + * man page included in the crypt_blowfish tarball. * * There's a paper on the algorithm that explains its design decisions: * @@ -40,16 +51,8 @@ #define __set_errno(val) errno = (val) #endif - -#ifndef __const -#ifdef __GNUC__ -#define __CONST __const -#else -#define __CONST -#endif -#else -#define __CONST __const -#endif +/* Just to make sure the prototypes match the actual definitions */ +#include "crypt_blowfish.h" #ifdef __i386__ #define BF_ASM 0 @@ -63,6 +66,7 @@ #endif typedef unsigned int BF_word; +typedef signed int BF_word_signed; /* Number of Blowfish rounds, this is also hardcoded into a few places */ #define BF_N 16 @@ -370,35 +374,21 @@ static unsigned char BF_atoi64[0x60] = { 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 64, 64, 64, 64, 64 }; -/* - * This may be optimized out if built with function inlining and no BF_ASM. - */ -static void clean(void *data, int size) -{ -#if BF_ASM - extern void _BF_clean(void *data); -#endif - memset(data, 0, size); -#if BF_ASM - _BF_clean(data); -#endif -} - #define BF_safe_atoi64(dst, src) \ { \ tmp = (unsigned char)(src); \ - if (tmp == '$') break; \ + if (tmp == '$') break; /* PHP hack */ \ if ((unsigned int)(tmp -= 0x20) >= 0x60) return -1; \ tmp = BF_atoi64[tmp]; \ if (tmp > 63) return -1; \ (dst) = tmp; \ } -static int BF_decode(BF_word *dst, __CONST char *src, int size) +static int BF_decode(BF_word *dst, const char *src, int size) { unsigned char *dptr = (unsigned char *)dst; unsigned char *end = dptr + size; - unsigned char *sptr = (unsigned char *)src; + const unsigned char *sptr = (const unsigned char *)src; unsigned int tmp, c1, c2, c3, c4; do { @@ -415,16 +405,16 @@ static int BF_decode(BF_word *dst, __CONST char *src, int size) *dptr++ = ((c3 & 0x03) << 6) | c4; } while (dptr < end); - while (dptr < end) + while (dptr < end) /* PHP hack */ *dptr++ = 0; return 0; } -static void BF_encode(char *dst, __CONST BF_word *src, int size) +static void BF_encode(char *dst, const BF_word *src, int size) { - unsigned char *sptr = (unsigned char *)src; - unsigned char *end = sptr + size; + const unsigned char *sptr = (const unsigned char *)src; + const unsigned char *end = sptr + size; unsigned char *dptr = (unsigned char *)dst; unsigned int c1, c2; @@ -555,32 +545,117 @@ static void BF_swap(BF_word *x, int count) } while (ptr < &data.ctx.S[3][0xFF]); #endif -static void BF_set_key(__CONST char *key, BF_key expanded, BF_key initial) +static void BF_set_key(const char *key, BF_key expanded, BF_key initial, + unsigned char flags) { - __CONST char *ptr = key; - int i, j; - BF_word tmp; + const char *ptr = key; + unsigned int bug, i, j; + BF_word safety, sign, diff, tmp[2]; + +/* + * There was a sign extension bug in older revisions of this function. While + * we would have liked to simply fix the bug and move on, we have to provide + * a backwards compatibility feature (essentially the bug) for some systems and + * a safety measure for some others. The latter is needed because for certain + * multiple inputs to the buggy algorithm there exist easily found inputs to + * the correct algorithm that produce the same hash. Thus, we optionally + * deviate from the correct algorithm just enough to avoid such collisions. + * While the bug itself affected the majority of passwords containing + * characters with the 8th bit set (although only a percentage of those in a + * collision-producing way), the anti-collision safety measure affects + * only a subset of passwords containing the '\xff' character (not even all of + * those passwords, just some of them). This character is not found in valid + * UTF-8 sequences and is rarely used in popular 8-bit character encodings. + * Thus, the safety measure is unlikely to cause much annoyance, and is a + * reasonable tradeoff to use when authenticating against existing hashes that + * are not reliably known to have been computed with the correct algorithm. + * + * We use an approach that tries to minimize side-channel leaks of password + * information - that is, we mostly use fixed-cost bitwise operations instead + * of branches or table lookups. (One conditional branch based on password + * length remains. It is not part of the bug aftermath, though, and is + * difficult and possibly unreasonable to avoid given the use of C strings by + * the caller, which results in similar timing leaks anyway.) + * + * For actual implementation, we set an array index in the variable "bug" + * (0 means no bug, 1 means sign extension bug emulation) and a flag in the + * variable "safety" (bit 16 is set when the safety measure is requested). + * Valid combinations of settings are: + * + * Prefix "$2a$": bug = 0, safety = 0x10000 + * Prefix "$2x$": bug = 1, safety = 0 + * Prefix "$2y$": bug = 0, safety = 0 + */ + bug = (unsigned int)flags & 1; + safety = ((BF_word)flags & 2) << 15; + + sign = diff = 0; for (i = 0; i < BF_N + 2; i++) { - tmp = 0; + tmp[0] = tmp[1] = 0; for (j = 0; j < 4; j++) { - tmp <<= 8; - tmp |= *ptr; - - if (!*ptr) ptr = key; else ptr++; + tmp[0] <<= 8; + tmp[0] |= (unsigned char)*ptr; /* correct */ + tmp[1] <<= 8; + tmp[1] |= (BF_word_signed)(signed char)*ptr; /* bug */ +/* + * Sign extension in the first char has no effect - nothing to overwrite yet, + * and those extra 24 bits will be fully shifted out of the 32-bit word. For + * chars 2, 3, 4 in each four-char block, we set bit 7 of "sign" if sign + * extension in tmp[1] occurs. Once this flag is set, it remains set. + */ + if (j) + sign |= tmp[1] & 0x80; + if (!*ptr) + ptr = key; + else + ptr++; } + diff |= tmp[0] ^ tmp[1]; /* Non-zero on any differences */ - expanded[i] = tmp; - initial[i] = BF_init_state.P[i] ^ tmp; + expanded[i] = tmp[bug]; + initial[i] = BF_init_state.P[i] ^ tmp[bug]; } + +/* + * At this point, "diff" is zero iff the correct and buggy algorithms produced + * exactly the same result. If so and if "sign" is non-zero, which indicates + * that there was a non-benign sign extension, this means that we have a + * collision between the correctly computed hash for this password and a set of + * passwords that could be supplied to the buggy algorithm. Our safety measure + * is meant to protect from such many-buggy to one-correct collisions, by + * deviating from the correct algorithm in such cases. Let's check for this. + */ + diff |= diff >> 16; /* still zero iff exact match */ + diff &= 0xffff; /* ditto */ + diff += 0xffff; /* bit 16 set iff "diff" was non-zero (on non-match) */ + sign <<= 9; /* move the non-benign sign extension flag to bit 16 */ + sign &= ~diff & safety; /* action needed? */ + +/* + * If we have determined that we need to deviate from the correct algorithm, + * flip bit 16 in initial expanded key. (The choice of 16 is arbitrary, but + * let's stick to it now. It came out of the approach we used above, and it's + * not any worse than any other choice we could make.) + * + * It is crucial that we don't do the same to the expanded key used in the main + * Eksblowfish loop. By doing it to only one of these two, we deviate from a + * state that could be directly specified by a password to the buggy algorithm + * (and to the fully correct one as well, but that's a side-effect). + */ + initial[0] ^= sign; } -char *php_crypt_blowfish_rn(__CONST char *key, __CONST char *setting, - char *output, int size) +static char *BF_crypt(const char *key, const char *setting, + char *output, int size, + BF_word min) { #if BF_ASM extern void _BF_body_r(BF_ctx *ctx); #endif + static const unsigned char flags_by_subtype[26] = + {2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 0}; struct { BF_ctx ctx; BF_key expanded_key; @@ -602,7 +677,8 @@ char *php_crypt_blowfish_rn(__CONST char *key, __CONST char *setting, if (setting[0] != '$' || setting[1] != '2' || - setting[2] != 'a' || + setting[2] < 'a' || setting[2] > 'z' || + !flags_by_subtype[(unsigned int)(unsigned char)setting[2] - 'a'] || setting[3] != '$' || setting[4] < '0' || setting[4] > '3' || setting[5] < '0' || setting[5] > '9' || @@ -613,15 +689,14 @@ char *php_crypt_blowfish_rn(__CONST char *key, __CONST char *setting, } count = (BF_word)1 << ((setting[4] - '0') * 10 + (setting[5] - '0')); - if (count < 16 || BF_decode(data.binary.salt, &setting[7], 16)) { - clean(data.binary.salt, sizeof(data.binary.salt)); + if (count < min || BF_decode(data.binary.salt, &setting[7], 16)) { __set_errno(EINVAL); return NULL; } - BF_swap(data.binary.salt, 4); - BF_set_key(key, data.expanded_key, data.ctx.P); + BF_set_key(key, data.expanded_key, data.ctx.P, + flags_by_subtype[(unsigned int)(unsigned char)setting[2] - 'a']); memcpy(data.ctx.S, BF_init_state.S, sizeof(data.ctx.S)); @@ -651,51 +726,33 @@ char *php_crypt_blowfish_rn(__CONST char *key, __CONST char *setting, } while (ptr < &data.ctx.S[3][0xFF]); do { - data.ctx.P[0] ^= data.expanded_key[0]; - data.ctx.P[1] ^= data.expanded_key[1]; - data.ctx.P[2] ^= data.expanded_key[2]; - data.ctx.P[3] ^= data.expanded_key[3]; - data.ctx.P[4] ^= data.expanded_key[4]; - data.ctx.P[5] ^= data.expanded_key[5]; - data.ctx.P[6] ^= data.expanded_key[6]; - data.ctx.P[7] ^= data.expanded_key[7]; - data.ctx.P[8] ^= data.expanded_key[8]; - data.ctx.P[9] ^= data.expanded_key[9]; - data.ctx.P[10] ^= data.expanded_key[10]; - data.ctx.P[11] ^= data.expanded_key[11]; - data.ctx.P[12] ^= data.expanded_key[12]; - data.ctx.P[13] ^= data.expanded_key[13]; - data.ctx.P[14] ^= data.expanded_key[14]; - data.ctx.P[15] ^= data.expanded_key[15]; - data.ctx.P[16] ^= data.expanded_key[16]; - data.ctx.P[17] ^= data.expanded_key[17]; - - BF_body(); - - tmp1 = data.binary.salt[0]; - tmp2 = data.binary.salt[1]; - tmp3 = data.binary.salt[2]; - tmp4 = data.binary.salt[3]; - data.ctx.P[0] ^= tmp1; - data.ctx.P[1] ^= tmp2; - data.ctx.P[2] ^= tmp3; - data.ctx.P[3] ^= tmp4; - data.ctx.P[4] ^= tmp1; - data.ctx.P[5] ^= tmp2; - data.ctx.P[6] ^= tmp3; - data.ctx.P[7] ^= tmp4; - data.ctx.P[8] ^= tmp1; - data.ctx.P[9] ^= tmp2; - data.ctx.P[10] ^= tmp3; - data.ctx.P[11] ^= tmp4; - data.ctx.P[12] ^= tmp1; - data.ctx.P[13] ^= tmp2; - data.ctx.P[14] ^= tmp3; - data.ctx.P[15] ^= tmp4; - data.ctx.P[16] ^= tmp1; - data.ctx.P[17] ^= tmp2; - - BF_body(); + int done; + + for (i = 0; i < BF_N + 2; i += 2) { + data.ctx.P[i] ^= data.expanded_key[i]; + data.ctx.P[i + 1] ^= data.expanded_key[i + 1]; + } + + done = 0; + do { + BF_body(); + if (done) + break; + done = 1; + + tmp1 = data.binary.salt[0]; + tmp2 = data.binary.salt[1]; + tmp3 = data.binary.salt[2]; + tmp4 = data.binary.salt[3]; + for (i = 0; i < BF_N; i += 4) { + data.ctx.P[i] ^= tmp1; + data.ctx.P[i + 1] ^= tmp2; + data.ctx.P[i + 2] ^= tmp3; + data.ctx.P[i + 3] ^= tmp4; + } + data.ctx.P[16] ^= tmp1; + data.ctx.P[17] ^= tmp2; + } while (1); } while (--count); for (i = 0; i < 6; i += 2) { @@ -721,19 +778,114 @@ char *php_crypt_blowfish_rn(__CONST char *key, __CONST char *setting, BF_encode(&output[7 + 22], data.binary.output, 23); output[7 + 22 + 31] = '\0'; -/* Overwrite the most obvious sensitive data we have on the stack. Note - * that this does not guarantee there's no sensitive data left on the - * stack and/or in registers; I'm not aware of portable code that does. */ - clean(&data, sizeof(data)); - return output; } -char *php_crypt_gensalt_blowfish_rn(unsigned long count, - __CONST char *input, int size, char *output, int output_size) +static int _crypt_output_magic(const char *setting, char *output, int size) +{ + if (size < 3) + return -1; + + output[0] = '*'; + output[1] = '0'; + output[2] = '\0'; + + if (setting[0] == '*' && setting[1] == '0') + output[1] = '1'; + + return 0; +} + +/* + * Please preserve the runtime self-test. It serves two purposes at once: + * + * 1. We really can't afford the risk of producing incompatible hashes e.g. + * when there's something like gcc bug 26587 again, whereas an application or + * library integrating this code might not also integrate our external tests or + * it might not run them after every build. Even if it does, the miscompile + * might only occur on the production build, but not on a testing build (such + * as because of different optimization settings). It is painful to recover + * from incorrectly-computed hashes - merely fixing whatever broke is not + * enough. Thus, a proactive measure like this self-test is needed. + * + * 2. We don't want to leave sensitive data from our actual password hash + * computation on the stack or in registers. Previous revisions of the code + * would do explicit cleanups, but simply running the self-test after hash + * computation is more reliable. + * + * The performance cost of this quick self-test is around 0.6% at the "$2a$08" + * setting. + */ +char *php_crypt_blowfish_rn(const char *key, const char *setting, + char *output, int size) +{ + const char *test_key = "8b \xd0\xc1\xd2\xcf\xcc\xd8"; + const char *test_setting = "$2a$00$abcdefghijklmnopqrstuu"; + static const char * const test_hash[2] = + {"VUrPmXD6q/nVSSp7pNDhCR9071IfIRe\0\x55", /* $2x$ */ + "i1D709vfamulimlGcq0qq3UvuUasvEa\0\x55"}; /* $2a$, $2y$ */ + char *retval; + const char *p; + int save_errno, ok; + struct { + char s[7 + 22 + 1]; + char o[7 + 22 + 31 + 1 + 1 + 1]; + } buf; + +/* Hash the supplied password */ + _crypt_output_magic(setting, output, size); + retval = BF_crypt(key, setting, output, size, 16); + save_errno = errno; + +/* + * Do a quick self-test. It is important that we make both calls to BF_crypt() + * from the same scope such that they likely use the same stack locations, + * which makes the second call overwrite the first call's sensitive data on the + * stack and makes it more likely that any alignment related issues would be + * detected by the self-test. + */ + memcpy(buf.s, test_setting, sizeof(buf.s)); + if (retval) + buf.s[2] = setting[2]; + memset(buf.o, 0x55, sizeof(buf.o)); + buf.o[sizeof(buf.o) - 1] = 0; + p = BF_crypt(test_key, buf.s, buf.o, sizeof(buf.o) - (1 + 1), 1); + + ok = (p == buf.o && + !memcmp(p, buf.s, 7 + 22) && + !memcmp(p + (7 + 22), + test_hash[(unsigned int)(unsigned char)buf.s[2] & 1], + 31 + 1 + 1 + 1)); + + { + const char *k = "\xff\xa3" "34" "\xff\xff\xff\xa3" "345"; + BF_key ae, ai, ye, yi; + BF_set_key(k, ae, ai, 2); /* $2a$ */ + BF_set_key(k, ye, yi, 4); /* $2y$ */ + ai[0] ^= 0x10000; /* undo the safety (for comparison) */ + ok = ok && ai[0] == 0xdb9c59bc && ye[17] == 0x33343500 && + !memcmp(ae, ye, sizeof(ae)) && + !memcmp(ai, yi, sizeof(ai)); + } + + __set_errno(save_errno); + if (ok) + return retval; + +/* Should not happen */ + _crypt_output_magic(setting, output, size); + __set_errno(EINVAL); /* pretend we don't support this hash type */ + return NULL; +} + +#if 0 +char *_crypt_gensalt_blowfish_rn(const char *prefix, unsigned long count, + const char *input, int size, char *output, int output_size) { if (size < 16 || output_size < 7 + 22 + 1 || - (count && (count < 4 || count > 31))) { + (count && (count < 4 || count > 31)) || + prefix[0] != '$' || prefix[1] != '2' || + (prefix[2] != 'a' && prefix[2] != 'y')) { if (output_size > 0) output[0] = '\0'; __set_errno((output_size < 7 + 22 + 1) ? ERANGE : EINVAL); return NULL; @@ -743,14 +895,15 @@ char *php_crypt_gensalt_blowfish_rn(unsigned long count, output[0] = '$'; output[1] = '2'; - output[2] = 'a'; + output[2] = prefix[2]; output[3] = '$'; output[4] = '0' + count / 10; output[5] = '0' + count % 10; output[6] = '$'; - BF_encode(&output[7], (BF_word *)input, 16); + BF_encode(&output[7], (const BF_word *)input, 16); output[7 + 22] = '\0'; return output; } +#endif diff --git a/ext/standard/crypt_blowfish.h b/ext/standard/crypt_blowfish.h new file mode 100644 index 000000000..da374730e --- /dev/null +++ b/ext/standard/crypt_blowfish.h @@ -0,0 +1,32 @@ +/* $Id$ */
+/*
+ * Written by Solar Designer <solar at openwall.com> in 2000-2011.
+ * No copyright is claimed, and the software is hereby placed in the public
+ * domain. In case this attempt to disclaim copyright and place the software
+ * in the public domain is deemed null and void, then the software is
+ * Copyright (c) 2000-2011 Solar Designer and it is hereby released to the
+ * general public under the following terms:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted.
+ *
+ * There's ABSOLUTELY NO WARRANTY, express or implied.
+ *
+ * See crypt_blowfish.c for more information.
+ */
+
+#ifndef _CRYPT_BLOWFISH_H
+#define _CRYPT_BLOWFISH_H
+
+#if 0
+extern int _crypt_output_magic(const char *setting, char *output, int size);
+#endif
+extern char *php_crypt_blowfish_rn(const char *key, const char *setting,
+ char *output, int size);
+#if 0
+extern char *_crypt_gensalt_blowfish_rn(const char *prefix,
+ unsigned long count,
+ const char *input, int size, char *output, int output_size);
+#endif
+
+#endif
diff --git a/ext/standard/crypt_sha256.c b/ext/standard/crypt_sha256.c index f9daed909..0923aae30 100644 --- a/ext/standard/crypt_sha256.c +++ b/ext/standard/crypt_sha256.c @@ -395,9 +395,10 @@ char * php_sha256_crypt_r(const char *key, const char *salt, char *buffer, int b } if ((salt - (char *) 0) % __alignof__(uint32_t) != 0) { - char *tmp = (char *) alloca(salt_len + __alignof__(uint32_t)); + char *tmp = (char *) alloca(salt_len + 1 + __alignof__(uint32_t)); salt = copied_salt = memcpy(tmp + __alignof__(uint32_t) - (tmp - (char *) 0) % __alignof__ (uint32_t), salt, salt_len); + copied_salt[salt_len] = 0; } /* Prepare for the real work. */ diff --git a/ext/standard/crypt_sha512.c b/ext/standard/crypt_sha512.c index f78ff0398..cbc97a328 100644 --- a/ext/standard/crypt_sha512.c +++ b/ext/standard/crypt_sha512.c @@ -430,9 +430,9 @@ php_sha512_crypt_r(const char *key, const char *salt, char *buffer, int buflen) } if ((salt - (char *) 0) % __alignof__ (uint64_t) != 0) { - char *tmp = (char *) alloca(salt_len + __alignof__(uint64_t)); - + char *tmp = (char *) alloca(salt_len + 1 + __alignof__(uint64_t)); salt = copied_salt = memcpy(tmp + __alignof__(uint64_t) - (tmp - (char *) 0) % __alignof__(uint64_t), salt, salt_len); + copied_salt[salt_len] = 0; } /* Prepare for the real work. */ diff --git a/ext/standard/dl.c b/ext/standard/dl.c index 79d0d20a1..81b12838d 100644 --- a/ext/standard/dl.c +++ b/ext/standard/dl.c @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: dl.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: dl.c 311444 2011-05-26 14:37:13Z pajoye $ */ #include "php.h" #include "dl.h" @@ -148,7 +148,7 @@ PHPAPI int php_load_extension(char *filename, int type, int start_now TSRMLS_DC) if (!handle) { #if PHP_WIN32 char *err = GET_DL_ERROR(); - if (err) { + if (err && (*err != "")) { php_error_docref(NULL TSRMLS_CC, error_type, "Unable to load dynamic library '%s' - %s", libpath, err); LocalFree(err); } else { diff --git a/ext/standard/dns.c b/ext/standard/dns.c index 0db3480e5..8453ad887 100644 --- a/ext/standard/dns.c +++ b/ext/standard/dns.c @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: dns.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: dns.c 314766 2011-08-10 17:40:56Z rasmus $ */ /* {{{ includes */ #include "php.h" @@ -62,6 +62,16 @@ #define AF_INET 2 /* internetwork: UDP, TCP, etc. */ #endif +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 255 +#endif + +/* For the local hostname obtained via gethostname which is different from the + dns-related MAXHOSTNAMELEN constant above */ +#ifndef HOST_NAME_MAX +#define HOST_NAME_MAX 255 +#endif + #include "php_dns.h" /* type compat */ @@ -118,7 +128,7 @@ static char *php_gethostbyname(char *name); Get the host name of the current machine */ PHP_FUNCTION(gethostname) { - char buf[4096]; + char buf[HOST_NAME_MAX]; if (zend_parse_parameters_none() == FAILURE) { return; @@ -794,12 +804,14 @@ PHP_FUNCTION(dns_get_record) #if defined(HAVE_DNS_SEARCH) handle = dns_open(NULL); if (handle == NULL) { + zval_dtor(return_value); RETURN_FALSE; } #elif defined(HAVE_RES_NSEARCH) memset(&state, 0, sizeof(state)); if (res_ninit(handle)) { - RETURN_FALSE; + zval_dtor(return_value); + RETURN_FALSE; } #else res_init(); diff --git a/ext/standard/file.c b/ext/standard/file.c index 3bf1c47bb..091eb4390 100644 --- a/ext/standard/file.c +++ b/ext/standard/file.c @@ -21,7 +21,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: file.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: file.c 312285 2011-06-19 14:50:44Z felipe $ */ /* Synced with php 3.0 revision 1.218 1999-06-16 [ssb] */ @@ -1498,20 +1498,20 @@ PHP_FUNCTION(umask) { long arg1 = 0; int oldumask; - int arg_count = ZEND_NUM_ARGS(); - + oldumask = umask(077); if (BG(umask) == -1) { BG(umask) = oldumask; } + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &arg1) == FAILURE) { + RETURN_FALSE; + } - if (arg_count == 0) { + if (ZEND_NUM_ARGS() == 0) { umask(oldumask); } else { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &arg1) == FAILURE) { - RETURN_FALSE; - } umask(arg1); } @@ -2196,30 +2196,17 @@ PHPAPI void php_fgetcsv(php_stream *stream, char delimiter, char enclosure, char char *comp_end, *hunk_begin; tptr = temp; - - /* 1. Strip any leading space */ - for (;;) { - inc_len = (bptr < limit ? (*bptr == '\0' ? 1: php_mblen(bptr, limit - bptr)): 0); - switch (inc_len) { - case -2: - case -1: - inc_len = 1; - php_mblen(NULL, 0); - break; - case 0: - goto quit_loop_1; - case 1: - if (!isspace((int)*(unsigned char *)bptr) || *bptr == delimiter) { - goto quit_loop_1; - } - break; - default: - goto quit_loop_1; + inc_len = (bptr < limit ? (*bptr == '\0' ? 1: php_mblen(bptr, limit - bptr)): 0); + if (inc_len == 1) { + char *tmp = bptr; + while (isspace((int)*(unsigned char *)tmp)) { + tmp++; + } + if (*tmp == enclosure) { + bptr = tmp; } - bptr += inc_len; } - quit_loop_1: if (first_field && bptr == line_end) { add_next_index_null(return_value); break; @@ -2618,6 +2605,9 @@ PHP_FUNCTION(fnmatch) Returns directory path used for temporary files */ PHP_FUNCTION(sys_get_temp_dir) { + if (zend_parse_parameters_none() == FAILURE) { + return; + } RETURN_STRING((char *)php_get_temporary_directory(), 1); } /* }}} */ diff --git a/ext/standard/filters.c b/ext/standard/filters.c index 4ffda5d51..80ce98f34 100644 --- a/ext/standard/filters.c +++ b/ext/standard/filters.c @@ -20,7 +20,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: filters.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: filters.c 311407 2011-05-24 23:49:26Z felipe $ */ #include "php.h" #include "php_globals.h" @@ -1050,20 +1050,16 @@ static php_conv_err_t php_conv_qprint_decode_convert(php_conv_qprint_decode *ins } } /* break is missing intentionally */ - case 2: { - unsigned int nbl; - + case 2: { if (icnt <= 0) { goto out; } - nbl = (*ps >= 'A' ? *ps - 0x37 : *ps - 0x30); - if (nbl > 15) { + if (!isxdigit((int) *ps)) { err = PHP_CONV_ERR_INVALID_SEQ; goto out; } - next_char = (next_char << 4) | nbl; - + next_char = (next_char << 4) | (*ps >= 'A' ? *ps - 0x37 : *ps - 0x30); scan_stat++; ps++, icnt--; if (scan_stat != 3) { diff --git a/ext/standard/head.c b/ext/standard/head.c index c29e95490..3ad04b04a 100644 --- a/ext/standard/head.c +++ b/ext/standard/head.c @@ -15,7 +15,7 @@ | Author: Rasmus Lerdorf <rasmus@lerdorf.on.ca> | +----------------------------------------------------------------------+ */ -/* $Id: head.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: head.c 314486 2011-08-08 12:10:27Z iliaa $ */ #include <stdio.h> #include "php.h" @@ -115,10 +115,9 @@ PHPAPI int php_setcookie(char *name, int name_len, char *value, int value_len, t /* * MSIE doesn't delete a cookie when you set it to a null value * so in order to force cookies to be deleted, even on MSIE, we - * pick an expiry date 1 year and 1 second in the past + * pick an expiry date in the past */ - time_t t = time(NULL) - 31536001; - dt = php_format_date("D, d-M-Y H:i:s T", sizeof("D, d-M-Y H:i:s T")-1, t, 0 TSRMLS_CC); + dt = php_format_date("D, d-M-Y H:i:s T", sizeof("D, d-M-Y H:i:s T")-1, 1, 0 TSRMLS_CC); snprintf(cookie, len + 100, "Set-Cookie: %s=deleted; expires=%s", name, dt); efree(dt); } else { @@ -129,7 +128,7 @@ PHPAPI int php_setcookie(char *name, int name_len, char *value, int value_len, t dt = php_format_date("D, d-M-Y H:i:s T", sizeof("D, d-M-Y H:i:s T")-1, expires, 0 TSRMLS_CC); /* check to make sure that the year does not exceed 4 digits in length */ p = zend_memrchr(dt, '-', strlen(dt)); - if (*(p + 5) != ' ') { + if (!p || *(p + 5) != ' ') { efree(dt); efree(cookie); efree(encoded_value); diff --git a/ext/standard/http_fopen_wrapper.c b/ext/standard/http_fopen_wrapper.c index 678e711f7..2fd25e0da 100644 --- a/ext/standard/http_fopen_wrapper.c +++ b/ext/standard/http_fopen_wrapper.c @@ -19,7 +19,7 @@ | Sara Golemon <pollita@php.net> | +----------------------------------------------------------------------+ */ -/* $Id: http_fopen_wrapper.c 307815 2011-01-28 10:33:47Z dmitry $ */ +/* $Id: http_fopen_wrapper.c 314641 2011-08-09 12:16:58Z laruence $ */ #include "php.h" #include "php_globals.h" @@ -330,7 +330,7 @@ finish: scratch_len = strlen(path) + 29 + Z_STRLEN_PP(tmpzval); scratch = emalloc(scratch_len); strlcpy(scratch, Z_STRVAL_PP(tmpzval), Z_STRLEN_PP(tmpzval) + 1); - strcat(scratch, " "); + strncat(scratch, " ", 1); } } } @@ -344,7 +344,7 @@ finish: if (!scratch) { scratch_len = strlen(path) + 29 + protocol_version_len; scratch = emalloc(scratch_len); - strcpy(scratch, "GET "); + strncpy(scratch, "GET ", scratch_len); } /* Should we send the entire path in the request line, default to no. */ @@ -545,7 +545,7 @@ finish: /* if the user has configured who they are, send a From: line */ { char *from_address = php_ini_string("from", sizeof("from"), 0); - if (((have_header & HTTP_HEADER_FROM) == 0) && from_address[0] != '\0') { + if (((have_header & HTTP_HEADER_FROM) == 0) && from_address && from_address[0] != '\0') { if (snprintf(scratch, scratch_len, "From: %s\r\n", from_address) > 0) php_stream_write(stream, scratch, strlen(scratch)); } @@ -631,7 +631,6 @@ finish: } php_stream_write(stream, "\r\n", sizeof("\r\n")-1); php_stream_write(stream, Z_STRVAL_PP(tmpzval), Z_STRLEN_PP(tmpzval)); - php_stream_write(stream, "\r\n\r\n", sizeof("\r\n\r\n")-1); } else { php_stream_write(stream, "\r\n", sizeof("\r\n")-1); } diff --git a/ext/standard/image.c b/ext/standard/image.c index 9654ed7e5..fc17ff67e 100644 --- a/ext/standard/image.c +++ b/ext/standard/image.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: image.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: image.c 310980 2011-05-13 05:06:48Z scottmac $ */ #include "php.h" #include <stdio.h> @@ -51,7 +51,7 @@ PHPAPI const char php_sig_jp2[12] = {(char)0x00, (char)0x00, (char)0x00, (char)0 (char)0x6a, (char)0x50, (char)0x20, (char)0x20, (char)0x0d, (char)0x0a, (char)0x87, (char)0x0a}; PHPAPI const char php_sig_iff[4] = {'F','O','R','M'}; -PHPAPI const char php_sig_ico[3] = {(char)0x00, (char)0x00, (char)0x01}; +PHPAPI const char php_sig_ico[4] = {(char)0x00, (char)0x00, (char)0x01, (char)0x00}; /* REMEMBER TO ADD MIME-TYPE TO FUNCTION php_image_type_to_mime_type */ /* PCX must check first 64bytes and byte 0=0x0a and byte2 < 0x06 */ @@ -1265,7 +1265,7 @@ PHPAPI int php_getimagetype(php_stream * stream, char *filetype TSRMLS_DC) return IMAGE_FILETYPE_TIFF_MM; } else if (!memcmp(filetype, php_sig_iff, 4)) { return IMAGE_FILETYPE_IFF; - } else if (!memcmp(filetype, php_sig_ico, 3)) { + } else if (!memcmp(filetype, php_sig_ico, 4)) { return IMAGE_FILETYPE_ICO; } diff --git a/ext/standard/info.c b/ext/standard/info.c index f33e537e9..f7b697773 100644 --- a/ext/standard/info.c +++ b/ext/standard/info.c @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: info.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: info.c 313538 2011-07-21 14:49:55Z pajoye $ */ #include "php.h" #include "php_ini.h" @@ -289,7 +289,7 @@ char* php_get_windows_name() major = "Windows Server 2008"; } } else - if ( osvi.dwMinorVersion == 2 ) { + if ( osvi.dwMinorVersion == 1 ) { if( osvi.wProductType == VER_NT_WORKSTATION ) { major = "Windows 7"; } else { diff --git a/ext/standard/link_win32.c b/ext/standard/link_win32.c index 143b5be4a..37a5fd252 100644 --- a/ext/standard/link_win32.c +++ b/ext/standard/link_win32.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: link_win32.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: link_win32.c 313176 2011-07-12 15:15:17Z pajoye $ */ #ifdef PHP_WIN32 #include "php.h" @@ -69,6 +69,7 @@ PHP_FUNCTION(readlink) if (php_sys_readlink(link, target, MAXPATHLEN) == -1) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "readlink failed to read the symbolic link (%s), error %d)", link, GetLastError()); + RETURN_FALSE; } RETURN_STRING(target, 1); } diff --git a/ext/standard/math.c b/ext/standard/math.c index 8333fe7c5..b656fafae 100644 --- a/ext/standard/math.c +++ b/ext/standard/math.c @@ -13,13 +13,13 @@ | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Jim Winstead <jimw@php.net> | - | Stig Sæther Bakken <ssb@php.net> | + | Stig Sæther Bakken <ssb@php.net> | | Zeev Suraski <zeev@zend.com> | | PHP 4.0 patches by Thies C. Arntzen <thies@thieso.net> | +----------------------------------------------------------------------+ */ -/* $Id: math.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: math.c 312074 2011-06-12 00:56:18Z cataphract $ */ #include "php.h" #include "php_math.h" @@ -92,6 +92,18 @@ static inline double php_intpow10(int power) { } /* }}} */ +/* {{{ php_math_is_finite */ +static inline int php_math_is_finite(double value) { +#if defined(PHP_WIN32) + return _finite(value); +#elif defined(isfinite) + return isfinite(value); +#else + return value == value && (value == 0. || value * 2. != value); +#endif +} +/* }}} */ + /* {{{ php_round_helper Actually performs the rounding of a value to integer in a certain mode */ static inline double php_round_helper(double value, int mode) { @@ -129,11 +141,11 @@ PHPAPI double _php_math_round(double value, int places, int mode) { double tmp_value; int precision_places; - if ((precision_places = php_intlog10abs(value)) > 0) { - precision_places = 14 - php_intlog10abs(value); - } else { - precision_places = 14; + if (!php_math_is_finite(value)) { + return value; } + + precision_places = 14 - php_intlog10abs(value); f1 = php_intpow10(abs(places)); diff --git a/ext/standard/php_crypt_r.c b/ext/standard/php_crypt_r.c index 08c713fe3..a1e0f3229 100644 --- a/ext/standard/php_crypt_r.c +++ b/ext/standard/php_crypt_r.c @@ -1,4 +1,4 @@ -/* $Id: php_crypt_r.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: php_crypt_r.c 314438 2011-08-07 16:10:34Z rasmus $ */ /* +----------------------------------------------------------------------+ | PHP Version 5 | @@ -94,7 +94,8 @@ void _crypt_extended_init_r(void) if (!initialized) { #ifdef PHP_WIN32 InterlockedIncrement(&initialized); -#elif (defined(__GNUC__) && (__GNUC__ >= 4 && __GNUC_MINOR__ >= 2)) +#elif (defined(__GNUC__) && !defined(__hpux) && (__GNUC__ > 4 || \ + (__GNUC__ == 4 && (__GNUC_MINOR__ > 1 || (__GNUC_MINOR__ == 1 && __GNUC_PATCHLEVEL__ > 1))))) __sync_fetch_and_add(&initialized, 1); #elif defined(HAVE_ATOMIC_H) /* Solaris 10 defines atomic API within */ membar_producer(); @@ -197,7 +198,7 @@ char * php_md5_crypt_r(const char *pw, const char *salt, char *out) { goto _destroyCtx1; } - dwHashLen = pwl + sl + pwl; + dwHashLen = 16; CryptGetHashParam(ctx1, HP_HASHVAL, final, &dwHashLen, 0); /* MD5(pw,salt,pw). Valid. */ @@ -381,7 +382,7 @@ char * php_md5_crypt_r(const char *pw, const char *salt, char *out) /* Now make the output string */ memcpy(passwd, MD5_MAGIC, MD5_MAGIC_LEN); strlcpy(passwd + MD5_MAGIC_LEN, sp, sl + 1); - strcat(passwd, "$"); + strlcat(passwd, "$", 1); PHP_MD5Final(final, &ctx); diff --git a/ext/standard/php_crypt_r.h b/ext/standard/php_crypt_r.h index 7c73718b2..f153ac322 100644 --- a/ext/standard/php_crypt_r.h +++ b/ext/standard/php_crypt_r.h @@ -1,4 +1,4 @@ -/* $Id: php_crypt_r.h 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: php_crypt_r.h 313406 2011-07-18 21:26:29Z pajoye $ */ /* +----------------------------------------------------------------------+ | PHP Version 5 | @@ -46,9 +46,9 @@ PHPAPI char *php_crypt_r (const char *__key, const char *__salt, struct php_cryp #define MD5_HASH_MAX_LEN 120 +#include "crypt_blowfish.h" + extern char * php_md5_crypt_r(const char *pw, const char *salt, char *out); -extern char * php_crypt_blowfish_rn(__CONST char *key, __CONST char *setting, - char *output, int size); extern char * php_sha512_crypt_r (const char *key, const char *salt, char *buffer, int buflen); extern char * php_sha256_crypt_r (const char *key, const char *salt, char *buffer, int buflen); diff --git a/ext/standard/proc_open.c b/ext/standard/proc_open.c index ecf5e35d1..b9411aebc 100644 --- a/ext/standard/proc_open.c +++ b/ext/standard/proc_open.c @@ -15,7 +15,7 @@ | Author: Wez Furlong <wez@thebrainroom.com> | +----------------------------------------------------------------------+ */ -/* $Id: proc_open.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: proc_open.c 314641 2011-08-09 12:16:58Z laruence $ */ #if 0 && (defined(__linux__) || defined(sun) || defined(__IRIX__)) # define _BSD_SOURCE /* linux wants this when XOPEN mode is on */ @@ -183,8 +183,8 @@ static php_process_env_t _php_array_to_envp(zval *environment, int is_persistent l = string_length + el_len + 1; memcpy(p, string_key, string_length); - strcat(p, "="); - strcat(p, data); + strncat(p, "=", 1); + strncat(p, data, el_len); #ifndef PHP_WIN32 *ep = p; diff --git a/ext/standard/string.c b/ext/standard/string.c index a3a3c84e0..9d9e5b3f4 100644 --- a/ext/standard/string.c +++ b/ext/standard/string.c @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: string.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: string.c 310401 2011-04-21 01:51:24Z pierrick $ */ /* Synced with php 3.0 revision 1.193 1999-06-16 [ssb] */ @@ -2352,20 +2352,35 @@ PHP_FUNCTION(substr_replace) zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(str), &pos_str); while (zend_hash_get_current_data_ex(Z_ARRVAL_PP(str), (void **) &tmp_str, &pos_str) == SUCCESS) { - convert_to_string_ex(tmp_str); + zval *orig_str; + zval dummy; + if(Z_TYPE_PP(tmp_str) != IS_STRING) { + dummy = **tmp_str; + orig_str = &dummy; + zval_copy_ctor(orig_str); + convert_to_string(orig_str); + } else { + orig_str = *tmp_str; + } if (Z_TYPE_PP(from) == IS_ARRAY) { if (SUCCESS == zend_hash_get_current_data_ex(Z_ARRVAL_PP(from), (void **) &tmp_from, &pos_from)) { - convert_to_long_ex(tmp_from); + if(Z_TYPE_PP(tmp_from) != IS_LONG) { + zval dummy = **tmp_from; + zval_copy_ctor(&dummy); + convert_to_long(&dummy); + f = Z_LVAL(dummy); + } else { + f = Z_LVAL_PP(tmp_from); + } - f = Z_LVAL_PP(tmp_from); if (f < 0) { - f = Z_STRLEN_PP(tmp_str) + f; + f = Z_STRLEN_P(orig_str) + f; if (f < 0) { f = 0; } - } else if (f > Z_STRLEN_PP(tmp_str)) { - f = Z_STRLEN_PP(tmp_str); + } else if (f > Z_STRLEN_P(orig_str)) { + f = Z_STRLEN_P(orig_str); } zend_hash_move_forward_ex(Z_ARRVAL_PP(from), &pos_from); } else { @@ -2374,72 +2389,92 @@ PHP_FUNCTION(substr_replace) } else { f = Z_LVAL_PP(from); if (f < 0) { - f = Z_STRLEN_PP(tmp_str) + f; + f = Z_STRLEN_P(orig_str) + f; if (f < 0) { f = 0; } - } else if (f > Z_STRLEN_PP(tmp_str)) { - f = Z_STRLEN_PP(tmp_str); + } else if (f > Z_STRLEN_P(orig_str)) { + f = Z_STRLEN_P(orig_str); } } if (argc > 3 && Z_TYPE_PP(len) == IS_ARRAY) { if (SUCCESS == zend_hash_get_current_data_ex(Z_ARRVAL_PP(len), (void **) &tmp_len, &pos_len)) { - convert_to_long_ex(tmp_len); - - l = Z_LVAL_PP(tmp_len); + if(Z_TYPE_PP(tmp_len) != IS_LONG) { + zval dummy = **tmp_len; + zval_copy_ctor(&dummy); + convert_to_long(&dummy); + l = Z_LVAL(dummy); + } else { + l = Z_LVAL_PP(tmp_len); + } zend_hash_move_forward_ex(Z_ARRVAL_PP(len), &pos_len); } else { - l = Z_STRLEN_PP(tmp_str); + l = Z_STRLEN_P(orig_str); } } else if (argc > 3) { l = Z_LVAL_PP(len); } else { - l = Z_STRLEN_PP(tmp_str); + l = Z_STRLEN_P(orig_str); } if (l < 0) { - l = (Z_STRLEN_PP(tmp_str) - f) + l; + l = (Z_STRLEN_P(orig_str) - f) + l; if (l < 0) { l = 0; } } - if ((f + l) > Z_STRLEN_PP(tmp_str)) { - l = Z_STRLEN_PP(tmp_str) - f; + if ((f + l) > Z_STRLEN_P(orig_str)) { + l = Z_STRLEN_P(orig_str) - f; } - result_len = Z_STRLEN_PP(tmp_str) - l; + result_len = Z_STRLEN_P(orig_str) - l; if (Z_TYPE_PP(repl) == IS_ARRAY) { if (SUCCESS == zend_hash_get_current_data_ex(Z_ARRVAL_PP(repl), (void **) &tmp_repl, &pos_repl)) { - convert_to_string_ex(tmp_repl); - result_len += Z_STRLEN_PP(tmp_repl); + zval *repl_str; + zval zrepl; + if(Z_TYPE_PP(tmp_repl) != IS_STRING) { + zrepl = **tmp_repl; + repl_str = &zrepl; + zval_copy_ctor(repl_str); + convert_to_string(repl_str); + } else { + repl_str = *tmp_repl; + } + + result_len += Z_STRLEN_P(repl_str); zend_hash_move_forward_ex(Z_ARRVAL_PP(repl), &pos_repl); result = emalloc(result_len + 1); - memcpy(result, Z_STRVAL_PP(tmp_str), f); - memcpy((result + f), Z_STRVAL_PP(tmp_repl), Z_STRLEN_PP(tmp_repl)); - memcpy((result + f + Z_STRLEN_PP(tmp_repl)), Z_STRVAL_PP(tmp_str) + f + l, Z_STRLEN_PP(tmp_str) - f - l); + memcpy(result, Z_STRVAL_P(orig_str), f); + memcpy((result + f), Z_STRVAL_P(repl_str), Z_STRLEN_P(repl_str)); + memcpy((result + f + Z_STRLEN_P(repl_str)), Z_STRVAL_P(orig_str) + f + l, Z_STRLEN_P(orig_str) - f - l); + if(Z_TYPE_PP(tmp_repl) != IS_STRING) { + zval_dtor(repl_str); + } } else { result = emalloc(result_len + 1); - memcpy(result, Z_STRVAL_PP(tmp_str), f); - memcpy((result + f), Z_STRVAL_PP(tmp_str) + f + l, Z_STRLEN_PP(tmp_str) - f - l); + memcpy(result, Z_STRVAL_P(orig_str), f); + memcpy((result + f), Z_STRVAL_P(orig_str) + f + l, Z_STRLEN_P(orig_str) - f - l); } } else { result_len += Z_STRLEN_PP(repl); result = emalloc(result_len + 1); - memcpy(result, Z_STRVAL_PP(tmp_str), f); + memcpy(result, Z_STRVAL_P(orig_str), f); memcpy((result + f), Z_STRVAL_PP(repl), Z_STRLEN_PP(repl)); - memcpy((result + f + Z_STRLEN_PP(repl)), Z_STRVAL_PP(tmp_str) + f + l, Z_STRLEN_PP(tmp_str) - f - l); + memcpy((result + f + Z_STRLEN_PP(repl)), Z_STRVAL_P(orig_str) + f + l, Z_STRLEN_P(orig_str) - f - l); } result[result_len] = '\0'; add_next_index_stringl(return_value, result, result_len, 0); - + if(Z_TYPE_PP(tmp_str) != IS_STRING) { + zval_dtor(orig_str); + } zend_hash_move_forward_ex(Z_ARRVAL_PP(str), &pos_str); } /*while*/ } /* if */ diff --git a/ext/standard/tests/array/array_shift_variation5.phpt b/ext/standard/tests/array/array_shift_variation5.phpt index 2ac15da6b..578b870d9 100644 --- a/ext/standard/tests/array/array_shift_variation5.phpt +++ b/ext/standard/tests/array/array_shift_variation5.phpt @@ -9,7 +9,7 @@ Test array_shift() function : usage variations - call recursively /* * Use the result of one call to array_shift - * as the the $stack argument of another call to array_shift() + * as the $stack argument of another call to array_shift() * When done in one statement causes strict error messages. */ @@ -42,4 +42,4 @@ string(4) "zero" -- Correct Method: -- string(4) "zero" -Done
\ No newline at end of file +Done diff --git a/ext/standard/tests/array/bug48484.phpt b/ext/standard/tests/array/bug48484.phpt index 006c3cc98..0b4afe277 100644 --- a/ext/standard/tests/array/bug48484.phpt +++ b/ext/standard/tests/array/bug48484.phpt @@ -6,11 +6,3 @@ var_dump(array_product(array())); ?> --EXPECT-- int(1) ---TEST-- -Bug 48484 (array_product() always returns 0 for an empty array) ---FILE-- -<?php -var_dump(array_product(array())); -?> ---EXPECT-- -int(1) diff --git a/ext/standard/tests/array/bug54459.phpt b/ext/standard/tests/array/bug54459.phpt new file mode 100644 index 000000000..e46cfcb14 --- /dev/null +++ b/ext/standard/tests/array/bug54459.phpt @@ -0,0 +1,215 @@ +--TEST-- +Bug #54459 (Range function accuracy) +--INI-- +precision=14 +--FILE-- +<?php +foreach (range(90, 100, .1) as $i => $v){ + echo $i, ' = ', $v, PHP_EOL; +} +foreach (range("90", "100", .1) as $i => $v){ + echo $i, ' = ', $v, PHP_EOL; +} +--EXPECT-- +0 = 90 +1 = 90.1 +2 = 90.2 +3 = 90.3 +4 = 90.4 +5 = 90.5 +6 = 90.6 +7 = 90.7 +8 = 90.8 +9 = 90.9 +10 = 91 +11 = 91.1 +12 = 91.2 +13 = 91.3 +14 = 91.4 +15 = 91.5 +16 = 91.6 +17 = 91.7 +18 = 91.8 +19 = 91.9 +20 = 92 +21 = 92.1 +22 = 92.2 +23 = 92.3 +24 = 92.4 +25 = 92.5 +26 = 92.6 +27 = 92.7 +28 = 92.8 +29 = 92.9 +30 = 93 +31 = 93.1 +32 = 93.2 +33 = 93.3 +34 = 93.4 +35 = 93.5 +36 = 93.6 +37 = 93.7 +38 = 93.8 +39 = 93.9 +40 = 94 +41 = 94.1 +42 = 94.2 +43 = 94.3 +44 = 94.4 +45 = 94.5 +46 = 94.6 +47 = 94.7 +48 = 94.8 +49 = 94.9 +50 = 95 +51 = 95.1 +52 = 95.2 +53 = 95.3 +54 = 95.4 +55 = 95.5 +56 = 95.6 +57 = 95.7 +58 = 95.8 +59 = 95.9 +60 = 96 +61 = 96.1 +62 = 96.2 +63 = 96.3 +64 = 96.4 +65 = 96.5 +66 = 96.6 +67 = 96.7 +68 = 96.8 +69 = 96.9 +70 = 97 +71 = 97.1 +72 = 97.2 +73 = 97.3 +74 = 97.4 +75 = 97.5 +76 = 97.6 +77 = 97.7 +78 = 97.8 +79 = 97.9 +80 = 98 +81 = 98.1 +82 = 98.2 +83 = 98.3 +84 = 98.4 +85 = 98.5 +86 = 98.6 +87 = 98.7 +88 = 98.8 +89 = 98.9 +90 = 99 +91 = 99.1 +92 = 99.2 +93 = 99.3 +94 = 99.4 +95 = 99.5 +96 = 99.6 +97 = 99.7 +98 = 99.8 +99 = 99.9 +100 = 100 +0 = 90 +1 = 90.1 +2 = 90.2 +3 = 90.3 +4 = 90.4 +5 = 90.5 +6 = 90.6 +7 = 90.7 +8 = 90.8 +9 = 90.9 +10 = 91 +11 = 91.1 +12 = 91.2 +13 = 91.3 +14 = 91.4 +15 = 91.5 +16 = 91.6 +17 = 91.7 +18 = 91.8 +19 = 91.9 +20 = 92 +21 = 92.1 +22 = 92.2 +23 = 92.3 +24 = 92.4 +25 = 92.5 +26 = 92.6 +27 = 92.7 +28 = 92.8 +29 = 92.9 +30 = 93 +31 = 93.1 +32 = 93.2 +33 = 93.3 +34 = 93.4 +35 = 93.5 +36 = 93.6 +37 = 93.7 +38 = 93.8 +39 = 93.9 +40 = 94 +41 = 94.1 +42 = 94.2 +43 = 94.3 +44 = 94.4 +45 = 94.5 +46 = 94.6 +47 = 94.7 +48 = 94.8 +49 = 94.9 +50 = 95 +51 = 95.1 +52 = 95.2 +53 = 95.3 +54 = 95.4 +55 = 95.5 +56 = 95.6 +57 = 95.7 +58 = 95.8 +59 = 95.9 +60 = 96 +61 = 96.1 +62 = 96.2 +63 = 96.3 +64 = 96.4 +65 = 96.5 +66 = 96.6 +67 = 96.7 +68 = 96.8 +69 = 96.9 +70 = 97 +71 = 97.1 +72 = 97.2 +73 = 97.3 +74 = 97.4 +75 = 97.5 +76 = 97.6 +77 = 97.7 +78 = 97.8 +79 = 97.9 +80 = 98 +81 = 98.1 +82 = 98.2 +83 = 98.3 +84 = 98.4 +85 = 98.5 +86 = 98.6 +87 = 98.7 +88 = 98.8 +89 = 98.9 +90 = 99 +91 = 99.1 +92 = 99.2 +93 = 99.3 +94 = 99.4 +95 = 99.5 +96 = 99.6 +97 = 99.7 +98 = 99.8 +99 = 99.9 +100 = 100 diff --git a/ext/standard/tests/class_object/is_subclass_of_variation_001.phpt b/ext/standard/tests/class_object/is_subclass_of_variation_001.phpt index f11183d48..14aaa3347 100644 --- a/ext/standard/tests/class_object/is_subclass_of_variation_001.phpt +++ b/ext/standard/tests/class_object/is_subclass_of_variation_001.phpt @@ -150,21 +150,17 @@ Arg value bool(false) Arg value -Error: 2 - Unknown class passed as parameter, %s(79) bool(false) Arg value -Error: 2 - Unknown class passed as parameter, %s(79) bool(false) Arg value string In __autoload(string) -Error: 2 - Unknown class passed as parameter, %s(79) bool(false) Arg value String In __autoload(String) -Error: 2 - Unknown class passed as parameter, %s(79) bool(false) Arg value diff --git a/ext/standard/tests/class_object/is_subclass_of_variation_004.phpt b/ext/standard/tests/class_object/is_subclass_of_variation_004.phpt index 72a02a0b2..2f46c4a8d 100644 --- a/ext/standard/tests/class_object/is_subclass_of_variation_004.phpt +++ b/ext/standard/tests/class_object/is_subclass_of_variation_004.phpt @@ -150,21 +150,17 @@ Arg value bool(false) Arg value -Error: 2 - Unknown class passed as parameter, %s(79) bool(false) Arg value -Error: 2 - Unknown class passed as parameter, %s(79) bool(false) Arg value string In __autoload(string) -Error: 2 - Unknown class passed as parameter, %s(79) bool(false) Arg value String In __autoload(String) -Error: 2 - Unknown class passed as parameter, %s(79) bool(false) Arg value diff --git a/ext/standard/tests/file/001.phpt b/ext/standard/tests/file/001.phpt index e3768b19d..d604699ac 100644 --- a/ext/standard/tests/file/001.phpt +++ b/ext/standard/tests/file/001.phpt @@ -5,6 +5,7 @@ File type functions if (substr(PHP_OS, 0, 3) == 'WIN') { die('skip no symlinks on Windows'); } +if (getenv("SKIP_SLOW_TESTS")) die("skip slow test"); ?> --FILE-- <?php diff --git a/ext/standard/tests/file/005_variation.phpt b/ext/standard/tests/file/005_variation.phpt index e46df865f..7e5eedc2b 100644 --- a/ext/standard/tests/file/005_variation.phpt +++ b/ext/standard/tests/file/005_variation.phpt @@ -5,6 +5,9 @@ Test fileatime(), filemtime(), filectime() & touch() functions : usage variation if (substr(PHP_OS, 0, 3) == 'WIN') { die('skip Do not run on Windows'); } +if (getenv("SKIP_SLOW_TESTS")) { + die("skip slow test"); +} ?> --FILE-- <?php diff --git a/ext/standard/tests/file/005_variation2.phpt b/ext/standard/tests/file/005_variation2.phpt index d70ce1251..d14a9bddd 100644 --- a/ext/standard/tests/file/005_variation2.phpt +++ b/ext/standard/tests/file/005_variation2.phpt @@ -49,11 +49,17 @@ function stat_fn( $filename ) { echo "*** Testing fileattime(), filemtime(), filectime() & touch() : usage variations ***\n"; echo "\n*** testing touch ***\n"; -var_dump(touch(NULL)); -var_dump(touch(false)); -var_dump(touch('')); -var_dump(touch(' ')); -var_dump(touch('|')); +$a = touch(NULL); +$b = touch(false); +$c = touch(''); +$d = touch(' '); +$e = touch('|'); + +var_dump($a); +var_dump($b); +var_dump($c); +var_dump($d); +var_dump($e); echo "\n*** testing file info ***"; stat_fn(NULL); @@ -72,13 +78,13 @@ echo "Done"; *** testing touch *** -Warning: touch(): Unable to create file because %s in %s on line %d -bool(false) +Warning: touch(): Unable to create file because No such file or directory in %s on line %d -Warning: touch(): Unable to create file because %s in %s on line %d -bool(false) +Warning: touch(): Unable to create file because No such file or directory in %s on line %d -Warning: touch(): Unable to create file because %s in %s on line %d +Warning: touch(): Unable to create file because No such file or directory in %s on line %d +bool(false) +bool(false) bool(false) bool(true) bool(true) diff --git a/ext/standard/tests/file/bug39863.phpt b/ext/standard/tests/file/bug39863.phpt index 520a46412..6913655ee 100644 --- a/ext/standard/tests/file/bug39863.phpt +++ b/ext/standard/tests/file/bug39863.phpt @@ -16,8 +16,6 @@ else { ?> ===DONE=== <?php exit(0); ?> ---XFAIL-- -Needs bug #39863 fixed --EXPECT-- PASS ===DONE=== diff --git a/ext/standard/tests/file/bug53848.phpt b/ext/standard/tests/file/bug53848.phpt new file mode 100644 index 000000000..016d59d0c --- /dev/null +++ b/ext/standard/tests/file/bug53848.phpt @@ -0,0 +1,25 @@ +--TEST-- +Bug #53848 (fgetcsv removes leading spaces from fields) +--FILE-- +<?php +$file = dirname(__FILE__) . "/bug39538.csv"; +@unlink($file); +file_put_contents($file, "a,b\n c, d"); +$fp = fopen($file, "r"); +while ($l = fgetcsv($fp)) var_dump($l); +fclose($fp); +@unlink($file); +?> +--EXPECT-- +array(2) { + [0]=> + string(1) "a" + [1]=> + string(1) "b" +} +array(2) { + [0]=> + string(3) " c" + [1]=> + string(3) " d" +} diff --git a/ext/standard/tests/file/copy_variation4.phpt b/ext/standard/tests/file/copy_variation4.phpt Binary files differindex 32756c1ed..0a965fd54 100644 --- a/ext/standard/tests/file/copy_variation4.phpt +++ b/ext/standard/tests/file/copy_variation4.phpt diff --git a/ext/standard/tests/file/file_put_contents_variation8.phpt b/ext/standard/tests/file/file_put_contents_variation8.phpt Binary files differindex c35ace47b..79586ada2 100644 --- a/ext/standard/tests/file/file_put_contents_variation8.phpt +++ b/ext/standard/tests/file/file_put_contents_variation8.phpt diff --git a/ext/standard/tests/file/fread_socket_variation1.phpt b/ext/standard/tests/file/fread_socket_variation1.phpt index bd3d23ac5..50ee79bbf 100644 --- a/ext/standard/tests/file/fread_socket_variation1.phpt +++ b/ext/standard/tests/file/fread_socket_variation1.phpt @@ -1,5 +1,9 @@ --TEST-- Testing fread() on a TCP server socket +--SKIPIF-- +<?php +if (getenv("SKIP_SLOW_TESTS")) die("skip slow test"); +?> --FILE-- <?php diff --git a/ext/standard/tests/file/fscanf_variation39.phpt b/ext/standard/tests/file/fscanf_variation39.phpt index f0d406c58..1b17015bc 100644 --- a/ext/standard/tests/file/fscanf_variation39.phpt +++ b/ext/standard/tests/file/fscanf_variation39.phpt @@ -1,5 +1,11 @@ --TEST-- Test fscanf() function: usage variations - unsigned int formats with integer values +--SKIPIF-- +<?php +if (PHP_INT_SIZE != 4) { + die("skip this test is for 32bit platform only"); +} +?> --FILE-- <?php diff --git a/ext/standard/tests/file/fscanf_variation55.phpt b/ext/standard/tests/file/fscanf_variation55.phpt index 9b02d136d..1777f797b 100644 --- a/ext/standard/tests/file/fscanf_variation55.phpt +++ b/ext/standard/tests/file/fscanf_variation55.phpt @@ -1,5 +1,11 @@ --TEST-- Test fscanf() function: usage variations - tracking file pointer while reading +--SKIPIF-- +<?php +if (PHP_INT_SIZE != 4) { + die("skip this test is for 32bit platform only"); +} +?> --FILE-- <?php diff --git a/ext/standard/tests/file/lstat_stat_variation9.phpt b/ext/standard/tests/file/lstat_stat_variation9.phpt index b3c1281e6..63c6ff8a7 100644 --- a/ext/standard/tests/file/lstat_stat_variation9.phpt +++ b/ext/standard/tests/file/lstat_stat_variation9.phpt @@ -36,7 +36,7 @@ fclose($file_handle); $old_stat = stat($dirname); -/* now delete teh surdir and file and record the stat */ +/* now delete the surdir and file and record the stat */ unlink("$dirname/lstat_stat_variation9a.tmp"); rmdir("$dirname/lstat_stat_variation9_subdir"); diff --git a/ext/standard/tests/file/readfile_variation10.phpt b/ext/standard/tests/file/readfile_variation10.phpt Binary files differindex 2caa2de1d..76f1d2dbf 100644 --- a/ext/standard/tests/file/readfile_variation10.phpt +++ b/ext/standard/tests/file/readfile_variation10.phpt diff --git a/ext/standard/tests/filters/bug50363.phpt b/ext/standard/tests/filters/bug50363.phpt new file mode 100644 index 000000000..3395edebc --- /dev/null +++ b/ext/standard/tests/filters/bug50363.phpt @@ -0,0 +1,17 @@ +--TEST-- +Bug #50363 (Invalid parsing in convert.quoted-printable-decode filter) +--FILE-- +<?php + +$foo = "Sauvegarder=C3=A9ussi(e) n=C3=A3o N=C3=83O\n"; +$foo .= "Sauvegarder=c3=a9ussi(e) n=c3=a3o N=c3=83O\n"; // Does not work! +$b = fopen('php://temp', 'w+'); +stream_filter_append($b, 'convert.quoted-printable-decode', STREAM_FILTER_WRITE); +fwrite($b, $foo); +rewind($b); +fpassthru($b); + +?> +--EXPECTF-- +Sauvegarderéussi(e) não NÃO +Sauvegarderéussi(e) não NÃO diff --git a/ext/standard/tests/general_functions/ini_get_all.phpt b/ext/standard/tests/general_functions/ini_get_all.phpt index a13b0a475..60cd38a72 100644 --- a/ext/standard/tests/general_functions/ini_get_all.phpt +++ b/ext/standard/tests/general_functions/ini_get_all.phpt @@ -1,7 +1,7 @@ --TEST-- ini_get_all() tests --INI-- -pcre.backtrack_limit=100000 +pcre.backtrack_limit=1000000 pcre.recursion_limit=100000 --SKIPIF-- <?php if (!extension_loaded("reflection")) die("skip"); ?> @@ -34,9 +34,9 @@ array(2) { ["pcre.backtrack_limit"]=> array(3) { ["global_value"]=> - string(6) "100000" + string(7) "1000000" ["local_value"]=> - string(6) "100000" + string(7) "1000000" ["access"]=> int(7) } @@ -52,7 +52,7 @@ array(2) { } array(2) { ["pcre.backtrack_limit"]=> - string(6) "100000" + string(7) "1000000" ["pcre.recursion_limit"]=> string(6) "100000" } diff --git a/ext/standard/tests/general_functions/proc_open02.phpt b/ext/standard/tests/general_functions/proc_open02.phpt index 3cba15e9a..3406f6806 100644 --- a/ext/standard/tests/general_functions/proc_open02.phpt +++ b/ext/standard/tests/general_functions/proc_open02.phpt @@ -4,6 +4,7 @@ proc_open <?php if (!is_executable('/bin/sleep')) echo 'skip no sleep'; if (!is_executable('/usr/bin/nohup')) echo 'skip no nohup'; +if (getenv('SKIP_SLOW_TESTS')) echo 'skip slow test'; ?> --FILE-- <?php diff --git a/ext/standard/tests/general_functions/var_export_basic9.phpt b/ext/standard/tests/general_functions/var_export_basic9.phpt new file mode 100644 index 000000000..3c9706edf --- /dev/null +++ b/ext/standard/tests/general_functions/var_export_basic9.phpt @@ -0,0 +1,11 @@ +--TEST-- +Bug #55082: var_export() doesn't escape properties properly +--FILE-- +<?php + $x = new stdClass(); + $x->{'\'\\'} = 7; + echo var_export($x); +--EXPECT-- +stdClass::__set_state(array( + '\'\\' => 7, +)) diff --git a/ext/standard/tests/math/mt_rand_variation1.phpt b/ext/standard/tests/math/mt_rand_variation1.phpt index f2ba9bc34..aa4371611 100644 --- a/ext/standard/tests/math/mt_rand_variation1.phpt +++ b/ext/standard/tests/math/mt_rand_variation1.phpt @@ -38,7 +38,7 @@ $inputs = array( // float data /*6*/ 10.5, -10.5, - 12.3456789000e10, + 12.3456789000E8, 12.3456789000E-10, .5, @@ -79,7 +79,7 @@ $inputs = array( $iterator = 1; foreach($inputs as $input) { echo "\n-- Iteration $iterator --\n"; - var_dump(mt_rand($input, 100)); + var_dump(mt_rand($input, mt_getrandmax())); $iterator++; }; fclose($fp); diff --git a/ext/standard/tests/math/mt_rand_variation2.phpt b/ext/standard/tests/math/mt_rand_variation2.phpt index 28b2304a3..2174a349e 100644 --- a/ext/standard/tests/math/mt_rand_variation2.phpt +++ b/ext/standard/tests/math/mt_rand_variation2.phpt @@ -79,7 +79,7 @@ $inputs = array( $iterator = 1; foreach($inputs as $input) { echo "\n-- Iteration $iterator --\n"; - var_dump(mt_rand(100, $input)); + var_dump(mt_rand(-1 * mt_getrandmax(), $input)); $iterator++; }; fclose($fp); diff --git a/ext/standard/tests/misc/time_nanosleep_basic.phpt b/ext/standard/tests/misc/time_nanosleep_basic.phpt index 799c57209..3f20b00f7 100644 --- a/ext/standard/tests/misc/time_nanosleep_basic.phpt +++ b/ext/standard/tests/misc/time_nanosleep_basic.phpt @@ -1,7 +1,9 @@ --TEST-- time_nanosleep — Delay for a number of seconds and nanoseconds --SKIPIF-- -<?php if (!function_exists('time_nanosleep')) die("skip"); ?> +<?php if (!function_exists('time_nanosleep')) die("skip"); +if (getenv("SKIP_SLOW_TESTS")) die("skip slow test"); +?> --CREDITS-- Àlex Corretgé - alex@corretge.cat --FILE-- diff --git a/ext/standard/tests/network/gethostbynamel_error.phpt b/ext/standard/tests/network/gethostbynamel_error.phpt index 4a13bf021..7aa00e537 100644 --- a/ext/standard/tests/network/gethostbynamel_error.phpt +++ b/ext/standard/tests/network/gethostbynamel_error.phpt @@ -19,11 +19,6 @@ echo "\n-- Testing gethostbynamel() function with more than expected no. of argu $hostname = 'string_val'; $extra_arg = 10; var_dump( gethostbynamel($hostname, $extra_arg) ); - -echo "\n-- Testing gethostbynamel() with an unknown host --\n"; -$hostname = 'unknownhost_zzz_xxx_yyy.'; -var_dump( gethostbynamel($hostname) ); - echo "Done"; ?> --EXPECTF-- @@ -38,7 +33,4 @@ NULL Warning: gethostbynamel() expects exactly 1 parameter, 2 given in %s on line %d NULL - --- Testing gethostbynamel() with an unknown host -- -bool(false) Done diff --git a/ext/standard/tests/php_ini_loaded_file.phpt b/ext/standard/tests/php_ini_loaded_file.phpt index b443c5f70..7958eb127 100644 --- a/ext/standard/tests/php_ini_loaded_file.phpt +++ b/ext/standard/tests/php_ini_loaded_file.phpt @@ -1,9 +1,11 @@ --TEST-- -Check the php_ini_loaded_file() function. No file is loaded in test, so false ins returned +Check the php_ini_loaded_file() function --CREDITS-- Sebastian Schürmann sschuermann@chip.de Testfest 2009 Munich +--INI-- +precision=12 --FILE-- <?php var_dump(php_ini_loaded_file()); diff --git a/ext/standard/tests/serialize/001.phpt b/ext/standard/tests/serialize/001.phpt index 21a2d6c53..600c9b706 100644 --- a/ext/standard/tests/serialize/001.phpt +++ b/ext/standard/tests/serialize/001.phpt @@ -1,5 +1,7 @@ --TEST-- serialize()/unserialize()/var_dump() +--INI-- +serialize_precision=100 --FILE-- <?php class t diff --git a/ext/standard/tests/serialize/serialization_arrays_001.phpt b/ext/standard/tests/serialize/serialization_arrays_001.phpt index ff5f34c5b..51acfc4b4 100644 --- a/ext/standard/tests/serialize/serialization_arrays_001.phpt +++ b/ext/standard/tests/serialize/serialization_arrays_001.phpt @@ -1,5 +1,7 @@ --TEST-- Test serialize() & unserialize() functions: arrays (circular references) +--INI-- +serialize_precision=100 --FILE-- <?php /* Prototype : proto string serialize(mixed variable) diff --git a/ext/standard/tests/serialize/serialization_miscTypes_001.phpt b/ext/standard/tests/serialize/serialization_miscTypes_001.phpt Binary files differindex 276913524..038068249 100644 --- a/ext/standard/tests/serialize/serialization_miscTypes_001.phpt +++ b/ext/standard/tests/serialize/serialization_miscTypes_001.phpt diff --git a/ext/standard/tests/serialize/serialization_objects_001.phpt b/ext/standard/tests/serialize/serialization_objects_001.phpt Binary files differindex aafb26629..f85b89e10 100644 --- a/ext/standard/tests/serialize/serialization_objects_001.phpt +++ b/ext/standard/tests/serialize/serialization_objects_001.phpt diff --git a/ext/standard/tests/serialize/serialization_objects_002.phpt b/ext/standard/tests/serialize/serialization_objects_002.phpt Binary files differindex 95c2dfb68..fbd9e2612 100644 --- a/ext/standard/tests/serialize/serialization_objects_002.phpt +++ b/ext/standard/tests/serialize/serialization_objects_002.phpt diff --git a/ext/standard/tests/serialize/serialization_objects_003.phpt b/ext/standard/tests/serialize/serialization_objects_003.phpt index c20590b79..2313ffab8 100644 --- a/ext/standard/tests/serialize/serialization_objects_003.phpt +++ b/ext/standard/tests/serialize/serialization_objects_003.phpt @@ -1,5 +1,7 @@ --TEST-- Test serialize() & unserialize() functions: objects (abstract classes) +--INI-- +serialize_precision=100 --FILE-- <?php /* Prototype : proto string serialize(mixed variable) @@ -64,4 +66,4 @@ object(extendName)#%d (3) { string(18) "s:10:"extendName";" string(10) "extendName" -Done
\ No newline at end of file +Done diff --git a/ext/standard/tests/streams/bug54623.phpt b/ext/standard/tests/streams/bug54623.phpt new file mode 100644 index 000000000..cd83854f4 --- /dev/null +++ b/ext/standard/tests/streams/bug54623.phpt @@ -0,0 +1,17 @@ +--TEST-- +Bug #54623: Segfault when when writing to a persistent socket after closing a copy of the socket +--FILE-- +<?php +$sock = pfsockopen('udp://127.0.0.1', '63844'); +var_dump((int)$sock); +fwrite($sock, "1"); +$sock2 = pfsockopen('udp://127.0.0.1', '63844'); +var_dump((int)$sock2); +fwrite($sock2, "2"); +fclose($sock2); +fwrite($sock, "3"); +--EXPECTF-- +int(%d) +int(%d) + +Warning: fwrite(): %d is not a valid stream resource in %s on line %d diff --git a/ext/standard/tests/streams/bug54946.phpt b/ext/standard/tests/streams/bug54946.phpt new file mode 100644 index 000000000..b3fa73df5 --- /dev/null +++ b/ext/standard/tests/streams/bug54946.phpt @@ -0,0 +1,40 @@ +--TEST-- +Bug#54946 stream_get_contents infinite loop +--FILE-- +<?php +$filename = tempnam(sys_get_temp_dir(), "phpbug"); + +$stream = fopen($filename, "w"); // w or a +$retval = stream_get_contents($stream, 1, 1); + +var_dump($retval); +unlink($filename); + + + +$filename = tempnam(sys_get_temp_dir(), "phpbug2"); + +$stream = fopen($filename, "a"); +$retval = stream_get_contents($stream, 1, 1); + +var_dump($retval); +unlink($filename); + + + +$filename = tempnam(sys_get_temp_dir(), "phpbug3"); + +$stream = fopen($filename, "a"); +fseek($stream, 1); +$retval = stream_get_contents($stream, 1); + +var_dump($retval); +unlink($filename); +?> +===DONE=== +--EXPECT-- +string(0) "" +string(0) "" +string(0) "" +===DONE=== + diff --git a/ext/standard/tests/strings/006.phpt b/ext/standard/tests/strings/006.phpt index fdfd58c02..afb5d24db 100644 --- a/ext/standard/tests/strings/006.phpt +++ b/ext/standard/tests/strings/006.phpt @@ -1,7 +1,5 @@ --TEST-- highlight_file() and output buffer ---SKIPIF-- -<?php if( substr(PHP_OS, 0, 3) == "WIN") die('skip Non windows test');?> --INI-- log_errors_max_len=4096 --FILE-- diff --git a/ext/standard/tests/strings/bug54238.phpt b/ext/standard/tests/strings/bug54238.phpt new file mode 100644 index 000000000..0f60098ff --- /dev/null +++ b/ext/standard/tests/strings/bug54238.phpt @@ -0,0 +1,25 @@ +--TEST-- +Bug #54238 (use-after-free in substr_replace()) +--INI-- +error_reporting=E_ALL&~E_NOTICE +--FILE-- +<?php +$f = array(array('A', 'A')); + +$z = substr_replace($f, $f, $f, 1); +var_dump($z, $f); +?> +--EXPECT-- +array(1) { + [0]=> + string(9) "AArrayray" +} +array(1) { + [0]=> + array(2) { + [0]=> + string(1) "A" + [1]=> + string(1) "A" + } +} diff --git a/ext/standard/tests/strings/006-win32.phpt b/ext/standard/tests/strings/bug54332.phpt index b78fc1ada..122b387ec 100644 --- a/ext/standard/tests/strings/006-win32.phpt +++ b/ext/standard/tests/strings/bug54332.phpt @@ -1,23 +1,8 @@ --TEST-- -highlight_file() and output buffer ---SKIPIF-- -<?php if( substr(PHP_OS, 0, 3) != "WIN") die('skip Windows only test');?> ---INI-- -log_errors_max_len=4096 +Bug #54332 (Crash in zend_mm_check_ptr // Heap corruption) --FILE-- <?php - -$file = str_repeat("A", 1024); - -var_dump(highlight_file($file, true)); -var_dump(ob_get_contents()); - +echo number_format(1e300, 2006, '', ' ') . "\n"; ?> -===DONE=== ---EXPECTF-- -Warning: highlight_file(AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA): failed to open stream: No such file or directory in %s on line %d - -Warning: highlight_file(): Failed opening 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' for highlighting in %s on line %d -bool(false) -bool(false) -===DONE=== +--EXPECT-- +1 000 000 000 000 000 052 504 760 255 204 420 248 704 468 581 108 159 154 915 854 115 511 802 457 988 908 195 786 371 375 080 447 864 043 704 443 832 883 878 176 942 523 235 360 430 575 644 792 184 786 706 982 848 387 200 926 575 803 737 830 233 794 788 090 059 368 953 234 970 799 945 081 119 038 967 640 880 074 652 742 780 142 494 579 258 788 820 056 842 838 115 669 472 196 386 865 459 400 540 16000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 diff --git a/ext/standard/tests/strings/bug54721.phpt b/ext/standard/tests/strings/bug54721.phpt new file mode 100644 index 000000000..3851df154 --- /dev/null +++ b/ext/standard/tests/strings/bug54721.phpt @@ -0,0 +1,20 @@ +--TEST-- +Bug #54721 (Different Hashes on Windows, BSD and Linux on wrong Salt size) +--FILE-- +<?php +echo crypt("", '$1$dW0.is5.$10CH101gGOr1677ZYd517.') . "\n"; +echo crypt("b", '$1$dW0.is5.$10CH101gGOr1677ZYd517.') . "\n"; +echo crypt("bu", '$1$dW0.is5.$10CH101gGOr1677ZYd517.') . "\n"; +echo crypt("bug", '$1$dW0.is5.$10CH101gGOr1677ZYd517.') . "\n"; +echo crypt("pass", '$1$dW0.is5.$10CH101gGOr1677ZYd517.') . "\n"; +echo crypt("buged", '$1$dW0.is5.$10CH101gGOr1677ZYd517.') . "\n"; +echo crypt("aaaaaaaaaaaaaaaaaaaaaaaaa ", '$1$dW0.is5.$10CH101gGOr1677ZYd517.') . "\n"; +?> +--EXPECT-- +$1$dW0.is5.$I0iqTYHPzkP4YnRgnXxZW0 +$1$dW0.is5.$KaspRpPQ9U7Xb5Vv5c.WE/ +$1$dW0.is5.$X9G1x/Ep8zYQSrU4/lKUg. +$1$dW0.is5.$wE5Rz/HxPtDMfqil6kK980 +$1$dW0.is5.$2E4/ZDY1vr73HqLl1bLs9. +$1$dW0.is5.$lvGhphTQwqgKxWhWwYERr1 +$1$dW0.is5.$XzsWcLSBj2BvhOKH0xdpZ0 diff --git a/ext/standard/tests/strings/crypt_blowfish.phpt b/ext/standard/tests/strings/crypt_blowfish.phpt new file mode 100644 index 000000000..cce09c151 --- /dev/null +++ b/ext/standard/tests/strings/crypt_blowfish.phpt @@ -0,0 +1,50 @@ +--TEST-- +Official blowfish tests (http://cvsweb.openwall.com/cgi/cvsweb.cgi/Owl/packages/glibc/crypt_blowfish/wrapper.c) +--SKIPIF-- +<?php +if (!function_exists('crypt') || !defined("CRYPT_BLOWFISH")) { + die("SKIP crypt()-blowfish is not available"); +} +?> +--FILE-- +<?php + +$tests =array( + array('$2a$05$CCCCCCCCCCCCCCCCCCCCC.E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW', 'U*U'), + array('$2a$05$CCCCCCCCCCCCCCCCCCCCC.VGOzA784oUp/Z0DY336zx7pLYAy0lwK', 'U*U*'), + array('$2a$05$XXXXXXXXXXXXXXXXXXXXXOAcXxm9kjPGEMsLznoKqmqw7tc8WCx4a', 'U*U*U'), + array('$2a$05$abcdefghijklmnopqrstuu5s2v8.iXieOjg/.AySBTTZIIVFJeBui', '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789chars after 72 are ignored'), + array('$2x$05$/OK.fbVrR/bpIqNJ5ianF.CE5elHaaO4EbggVDjb8P19RukzXSM3e', "\xa3"), + array('$2a$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq', "\xa3"), + array('$2x$05$6bNw2HLQYeqHYyBfLMsv/OiwqTymGIGzFsA4hOTWebfehXHNprcAS', "\xd1\x91"), + array('$2x$05$6bNw2HLQYeqHYyBfLMsv/O9LIGgn8OMzuDoHfof8AQimSGfcSWxnS', "\xd0\xc1\xd2\xcf\xcc\xd8"), + array('$2a$05$/OK.fbVrR/bpIqNJ5ianF.swQOIzjOiJ9GHEPuhEkvqrUyvWhEMx6', "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaachars after 72 are ignored as usual"), + array('$2a$05$/OK.fbVrR/bpIqNJ5ianF.R9xrDjiycxMbQE2bp.vgqlYpW5wx2yy', "\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55"), + array('$2a$05$/OK.fbVrR/bpIqNJ5ianF.9tQZzcJfm3uj2NvJ/n5xkhpqLrMpWCe', "\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff\x55\xaa\xff"), + array('$2a$05$CCCCCCCCCCCCCCCCCCCCC.7uG0VCzI2bS7j6ymqJi9CdcdxiRTWNy', ''), + +); +$i=0; +foreach($tests as $test) { + if(crypt($test[1], $test[0]) == $test[0]) { + echo "$i. OK\n"; + } else { + echo "$i. Not OK: $test[0] ".crypt($test[1], $test[0])."\n"; + } + $i++; +} + +?> +--EXPECT-- +0. OK +1. OK +2. OK +3. OK +4. OK +5. OK +6. OK +7. OK +8. OK +9. OK +10. OK +11. OK
\ No newline at end of file diff --git a/ext/standard/tests/strings/crypt_variation1.phpt b/ext/standard/tests/strings/crypt_variation1.phpt new file mode 100644 index 000000000..6e0d3fe12 --- /dev/null +++ b/ext/standard/tests/strings/crypt_variation1.phpt @@ -0,0 +1,23 @@ +--TEST-- +crypt() function - long salt +--SKIPIF-- +<?php +if (!function_exists('crypt')) { + die("SKIP crypt() is not available"); +} +?> +--FILE-- +<?php + +$b = str_repeat("A", 124); +echo crypt("A", "$5$" . $b)."\n"; +$b = str_repeat("A", 125); +echo crypt("A", "$5$" . $b)."\n"; +$b = str_repeat("A", 4096); +echo crypt("A", "$5$" . $b)."\n"; + +?> +--EXPECTF-- +$5$AAAAAAAAAAAAAAAA$frotiiztWZiwcncxnY5tWG9Ida2WOZEximjLXCleQu6 +$5$AAAAAAAAAAAAAAAA$frotiiztWZiwcncxnY5tWG9Ida2WOZEximjLXCleQu6 +$5$AAAAAAAAAAAAAAAA$frotiiztWZiwcncxnY5tWG9Ida2WOZEximjLXCleQu6 diff --git a/ext/standard/tests/strings/htmlentities_html4.phpt b/ext/standard/tests/strings/htmlentities_html4.phpt index 3f700e828..d7bff707f 100644 --- a/ext/standard/tests/strings/htmlentities_html4.phpt +++ b/ext/standard/tests/strings/htmlentities_html4.phpt @@ -1,5 +1,9 @@ --TEST-- htmlentities() conformance check (HTML 4) +--SKIPIF-- +<?php +if (getenv("SKIP_SLOW_TESTS")) die("skip slow test"); +?> --FILE-- <?php function utf32_utf8($k) { diff --git a/ext/standard/tests/strings/printf_64bit.phpt b/ext/standard/tests/strings/printf_64bit.phpt index e2e8b2273..d0b7aaff4 100755 --- a/ext/standard/tests/strings/printf_64bit.phpt +++ b/ext/standard/tests/strings/printf_64bit.phpt @@ -671,7 +671,7 @@ Array *** Output for precision value more than maximum *** Notice: printf(): Requested precision of 988 digits was truncated to PHP maximum of %d digits in %s on line %d -12345678900.0000000000000000000000000000000000000000 +12345678900.0000000000%d *** Output for invalid width(-15) specifier *** 15s diff --git a/ext/standard/tests/strings/sscanf_basic6.phpt b/ext/standard/tests/strings/sscanf_basic6.phpt index 4506c4ba9..6efdd0b68 100644 --- a/ext/standard/tests/strings/sscanf_basic6.phpt +++ b/ext/standard/tests/strings/sscanf_basic6.phpt @@ -1,5 +1,11 @@ --TEST-- Test sscanf() function : basic functionality - unsigned format +--SKIPIF-- +<?php +if (PHP_INT_SIZE != 4) { + die("skip this test is for 32bit platform only"); +} +?> --FILE-- <?php diff --git a/ext/standard/tests/url/bug54180.phpt b/ext/standard/tests/url/bug54180.phpt new file mode 100644 index 000000000..2e64e27d0 --- /dev/null +++ b/ext/standard/tests/url/bug54180.phpt @@ -0,0 +1,32 @@ +--TEST-- +Bug #54180 (parse_url() incorrectly parses path when ? in fragment) +--FILE-- +<?php + +var_dump(parse_url("http://example.com/path/script.html?t=1#fragment?data")); +var_dump(parse_url("http://example.com/path/script.html#fragment?data")); + +?> +--EXPECTF-- +array(5) { + ["scheme"]=> + string(4) "http" + ["host"]=> + string(11) "example.com" + ["path"]=> + string(17) "/path/script.html" + ["query"]=> + string(3) "t=1" + ["fragment"]=> + string(13) "fragment?data" +} +array(4) { + ["scheme"]=> + string(4) "http" + ["host"]=> + string(11) "example.com" + ["path"]=> + string(17) "/path/script.html" + ["fragment"]=> + string(13) "fragment?data" +} diff --git a/ext/standard/tests/url/bug55399.phpt b/ext/standard/tests/url/bug55399.phpt new file mode 100644 index 000000000..619c08da6 --- /dev/null +++ b/ext/standard/tests/url/bug55399.phpt @@ -0,0 +1,10 @@ +--TEST-- +Bug #55399 (parse_url() incorrectly treats ':' as a valid path) +--FILE-- +<?php + +var_dump(parse_url(":")); + +?> +--EXPECT-- +bool(false) diff --git a/ext/standard/url.c b/ext/standard/url.c index 5446c87ac..22a8471be 100644 --- a/ext/standard/url.c +++ b/ext/standard/url.c @@ -15,7 +15,7 @@ | Author: Jim Winstead <jimw@php.net> | +----------------------------------------------------------------------+ */ -/* $Id: url.c 309175 2011-03-13 17:14:18Z pierrick $ */ +/* $Id: url.c 314783 2011-08-11 13:01:52Z iliaa $ */ #include <stdlib.h> #include <string.h> @@ -197,6 +197,10 @@ PHPAPI php_url *php_url_parse_ex(char const *str, int length) efree(ret); return NULL; } + } else if (p == pp && *pp == '\0') { + STR_FREE(ret->scheme); + efree(ret); + return NULL; } else { goto just_path; } @@ -316,6 +320,10 @@ PHPAPI php_url *php_url_parse_ex(char const *str, int length) pp = strchr(s, '#'); if (pp && pp < p) { + if (pp - s) { + ret->path = estrndup(s, (pp-s)); + php_replace_controlchars_ex(ret->path, (pp - s)); + } p = pp; goto label_parse; } diff --git a/ext/standard/url_scanner_ex.c b/ext/standard/url_scanner_ex.c index cd4ca9e91..d22ee057d 100644 --- a/ext/standard/url_scanner_ex.c +++ b/ext/standard/url_scanner_ex.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: url_scanner_ex.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: url_scanner_ex.c 313832 2011-07-28 10:52:45Z pajoye $ */ #include "php.h" @@ -56,9 +56,12 @@ static PHP_INI_MH(OnUpdateTags) if (ctx->tags) zend_hash_destroy(ctx->tags); - else + else { ctx->tags = malloc(sizeof(HashTable)); - + if (!ctx->tags) { + return FAILURE; + } + } zend_hash_init(ctx->tags, 0, NULL, NULL, 1); for (key = php_strtok_r(tmp, ",", &lasts); diff --git a/ext/standard/url_scanner_ex.c.orig b/ext/standard/url_scanner_ex.c.orig index cbc3f57bc..1ffa3698f 100644 --- a/ext/standard/url_scanner_ex.c.orig +++ b/ext/standard/url_scanner_ex.c.orig @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: url_scanner_ex.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: url_scanner_ex.c 313832 2011-07-28 10:52:45Z pajoye $ */ #include "php.h" @@ -57,9 +57,12 @@ static PHP_INI_MH(OnUpdateTags) if (ctx->tags) zend_hash_destroy(ctx->tags); - else + else { ctx->tags = malloc(sizeof(HashTable)); - + if (!ctx->tags) { + return FAILURE; + } + } zend_hash_init(ctx->tags, 0, NULL, NULL, 1); for (key = php_strtok_r(tmp, ",", &lasts); diff --git a/ext/standard/url_scanner_ex.re b/ext/standard/url_scanner_ex.re index 7609bd59a..c3bb39540 100644 --- a/ext/standard/url_scanner_ex.re +++ b/ext/standard/url_scanner_ex.re @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: url_scanner_ex.re 296107 2010-03-12 10:28:59Z jani $ */ +/* $Id: url_scanner_ex.re 313832 2011-07-28 10:52:45Z pajoye $ */ #include "php.h" @@ -55,9 +55,13 @@ static PHP_INI_MH(OnUpdateTags) if (ctx->tags) zend_hash_destroy(ctx->tags); - else + else { ctx->tags = malloc(sizeof(HashTable)); - + if (!ctx->tags) { + return FAILURE; + } + } + zend_hash_init(ctx->tags, 0, NULL, NULL, 1); for (key = php_strtok_r(tmp, ",", &lasts); diff --git a/ext/standard/user_filters.c b/ext/standard/user_filters.c index acd414277..449c42db1 100644 --- a/ext/standard/user_filters.c +++ b/ext/standard/user_filters.c @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: user_filters.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: user_filters.c 314641 2011-08-09 12:16:58Z laruence $ */ #include "php.h" #include "php_globals.h" @@ -71,7 +71,7 @@ static const zend_function_entry user_filter_class_funcs[] = { PHP_NAMED_FE(filter, PHP_FN(user_filter_nop), arginfo_php_user_filter_filter) PHP_NAMED_FE(onCreate, PHP_FN(user_filter_nop), arginfo_php_user_filter_onCreate) PHP_NAMED_FE(onClose, PHP_FN(user_filter_nop), arginfo_php_user_filter_onClose) - { NULL, NULL, NULL } + PHP_FE_END }; static zend_class_entry user_filter_class_entry; @@ -311,7 +311,7 @@ static php_stream_filter *user_filter_factory_create(const char *filtername, period = wildcard + (period - filtername); while (period) { *period = '\0'; - strcat(wildcard, ".*"); + strncat(wildcard, ".*", 2); if (SUCCESS == zend_hash_find(BG(user_filter_map), wildcard, strlen(wildcard) + 1, (void**)&fdat)) { period = NULL; } else { diff --git a/ext/standard/var.c b/ext/standard/var.c index 633b5685d..c8a89b746 100644 --- a/ext/standard/var.c +++ b/ext/standard/var.c @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: var.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: var.c 314403 2011-08-07 06:04:11Z pierrick $ */ /* {{{ includes */ @@ -242,7 +242,6 @@ PHPAPI void php_debug_zval_dump(zval **struc, int level TSRMLS_DC) /* {{{ */ HashTable *myht = NULL; char *class_name; zend_uint class_name_len; - zend_class_entry *ce; int (*zval_element_dump_func)(zval** TSRMLS_DC, int, va_list, zend_hash_key*); int is_temp = 0; @@ -283,7 +282,6 @@ PHPAPI void php_debug_zval_dump(zval **struc, int level TSRMLS_DC) /* {{{ */ PUTS("*RECURSION*\n"); return; } - ce = Z_OBJCE_PP(struc); if (Z_OBJ_HANDLER_PP(struc, get_class_name)) { Z_OBJ_HANDLER_PP(struc, get_class_name)(*struc, &class_name, &class_name_len, 0 TSRMLS_CC); php_printf("%sobject(%s)#%d (%d) refcount(%u){\n", COMMON, class_name, Z_OBJ_HANDLE_PP(struc), myht ? zend_hash_num_elements(myht) : 0, Z_REFCOUNT_PP(struc)); @@ -387,18 +385,26 @@ static int php_object_element_export(zval **zv TSRMLS_DC, int num_args, va_list { int level; smart_str *buf; - char *prop_name, *class_name; level = va_arg(args, int); buf = va_arg(args, smart_str *); buffer_append_spaces(buf, level + 2); if (hash_key->nKeyLength != 0) { - zend_unmangle_property_name(hash_key->arKey, hash_key->nKeyLength - 1, &class_name, &prop_name); + char *class_name, /* ignored, but must be passed to unmangle */ + *pname, + *pname_esc; + int pname_esc_len; + + zend_unmangle_property_name(hash_key->arKey, hash_key->nKeyLength - 1, + &class_name, &pname); + pname_esc = php_addcslashes(pname, strlen(pname), &pname_esc_len, 0, + "'\\", 2 TSRMLS_CC); smart_str_appendc(buf, '\''); - smart_str_appends(buf, prop_name); + smart_str_appendl(buf, pname_esc, pname_esc_len); smart_str_appendc(buf, '\''); + efree(pname_esc); } else { smart_str_append_long(buf, hash_key->h); } diff --git a/ext/sybase_ct/config.m4 b/ext/sybase_ct/config.m4 index f335c4cd3..b24f378f4 100644 --- a/ext/sybase_ct/config.m4 +++ b/ext/sybase_ct/config.m4 @@ -1,5 +1,5 @@ dnl -dnl $Id: config.m4 302918 2010-08-31 12:28:30Z thekid $ +dnl $Id: config.m4 312059 2011-06-11 18:55:15Z thekid $ dnl PHP_ARG_WITH(sybase-ct, for Sybase-CT support, @@ -15,7 +15,7 @@ if test "$PHP_SYBASE_CT" != "no"; then AC_DEFINE(HAVE_SYBASE_CT,1,[ ]) PHP_NEW_EXTENSION(sybase_ct, php_sybase_ct.c, $ext_shared) PHP_SUBST(SYBASE_CT_SHARED_LIBADD) - + if test "$PHP_SYBASE_CT" = "yes"; then SYBASE_CT_INCDIR=/home/sybase/include SYBASE_CT_LIBDIR=/home/sybase/lib @@ -24,17 +24,37 @@ if test "$PHP_SYBASE_CT" != "no"; then SYBASE_CT_LIBDIR=$PHP_SYBASE_CT/lib fi + dnl Determine whether we're building 64 or 32 bit... + AC_CHECK_SIZEOF(long int, 4) + AC_MSG_CHECKING([checking if we're on a 64-bit platform]) + if test "$ac_cv_sizeof_long_int" = "4"; then + AC_MSG_RESULT([no]) + PHP_SYBASE_64=no + else + AC_MSG_RESULT([yes]) + PHP_SYBASE_64=yes + fi + + + AC_MSG_CHECKING([Checking for ctpublic.h]) if test -f $SYBASE_CT_INCDIR/ctpublic.h; then + AC_MSG_RESULT([found in $SYBASE_CT_INCDIR]) PHP_ADD_INCLUDE($SYBASE_CT_INCDIR) else AC_MSG_ERROR([ctpublic.h missing!]) fi - + + AC_MSG_CHECKING([Checking Sybase libdir]) + AC_MSG_RESULT([Have $SYBASE_CT_LIBDIR]) + + AC_MSG_CHECKING([Checking for Sybase platform libraries]) + PHP_ADD_LIBPATH($SYBASE_CT_LIBDIR, SYBASE_CT_SHARED_LIBADD) if test -f $SYBASE_CT_INCDIR/tds.h || test -f $SYBASE_CT_INCDIR/tds_sysdep_public.h; then PHP_ADD_LIBRARY(ct,, SYBASE_CT_SHARED_LIBADD) SYBASE_CT_LIBS="-L$SYBASE_CT_LIBDIR -lct" - elif test -f $SYBASE_CT_INCDIR/libsybct64.so; then + AC_MSG_RESULT([FreeTDS: $SYBASE_CT_LIBS]) + elif test -f $SYBASE_CT_LIBDIR/libsybct64.so && test $PHP_SYBASE_64 = "yes"; then PHP_ADD_LIBRARY(sybcs64,, SYBASE_CT_SHARED_LIBADD) PHP_ADD_LIBRARY(sybct64,, SYBASE_CT_SHARED_LIBADD) PHP_ADD_LIBRARY(sybcomn64,, SYBASE_CT_SHARED_LIBADD) @@ -46,6 +66,7 @@ if test "$PHP_SYBASE_CT" != "no"; then *) CFLAGS="${CFLAGS} -DSYB_LP64" ;; # esac SYBASE_CT_LIBS="-L$SYBASE_CT_LIBDIR -lsybcs64 -lsybct64 -lsybcomn64 -lsybintl64" + AC_MSG_RESULT([Sybase64: $SYBASE_CT_LIBS]) PHP_CHECK_LIBRARY(sybtcl64, netg_errstr, [ PHP_ADD_LIBRARY(sybtcl64,,SYBASE_CT_SHARED_LIBADD) @@ -57,13 +78,14 @@ if test "$PHP_SYBASE_CT" != "no"; then PHP_CHECK_LIBRARY(insck64, insck__getVdate, [PHP_ADD_LIBRARY(insck64,, SYBASE_CT_SHARED_LIBADD)],[],[-L$SYBASE_CT_LIBDIR]) PHP_CHECK_LIBRARY(insck64, bsd_tcp, [PHP_ADD_LIBRARY(insck64,, SYBASE_CT_SHARED_LIBADD)],[],[-L$SYBASE_CT_LIBDIR]) - elif test -f $SYBASE_CT_INCDIR/libsybct.so; then + elif test -f $SYBASE_CT_LIBDIR/libsybct.so; then PHP_ADD_LIBRARY(sybcs,, SYBASE_CT_SHARED_LIBADD) PHP_ADD_LIBRARY(sybct,, SYBASE_CT_SHARED_LIBADD) PHP_ADD_LIBRARY(sybcomn,, SYBASE_CT_SHARED_LIBADD) PHP_ADD_LIBRARY(sybintl,, SYBASE_CT_SHARED_LIBADD) SYBASE_CT_LIBS="-L$SYBASE_CT_LIBDIR -lsybcs -lsybct -lsybcomn -lsybintl" + AC_MSG_RESULT([Sybase32 syb-prefix: $SYBASE_CT_LIBS]) PHP_CHECK_LIBRARY(sybtcl, netg_errstr, [ PHP_ADD_LIBRARY(sybtcl,,SYBASE_CT_SHARED_LIBADD) @@ -82,6 +104,7 @@ if test "$PHP_SYBASE_CT" != "no"; then PHP_ADD_LIBRARY(intl,, SYBASE_CT_SHARED_LIBADD) SYBASE_CT_LIBS="-L$SYBASE_CT_LIBDIR -lcs -lct -lcomn -lintl" + AC_MSG_RESULT([Sybase32 default: $SYBASE_CT_LIBS]) PHP_CHECK_LIBRARY(tcl, netg_errstr, [ PHP_ADD_LIBRARY(tcl,,SYBASE_CT_SHARED_LIBADD) diff --git a/ext/sybase_ct/php_sybase_ct.c b/ext/sybase_ct/php_sybase_ct.c index d881f6201..256ef31d3 100644 --- a/ext/sybase_ct/php_sybase_ct.c +++ b/ext/sybase_ct/php_sybase_ct.c @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: php_sybase_ct.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: php_sybase_ct.c 313903 2011-07-28 21:16:51Z pajoye $ */ #ifdef HAVE_CONFIG_H @@ -204,7 +204,7 @@ const zend_function_entry sybase_functions[] = { PHP_FALIAS(mssql_set_message_handler, sybase_set_message_handler, arginfo_sybase_set_message_handler) PHP_FALIAS(mssql_deadlock_retry_count, sybase_deadlock_retry_count, arginfo_sybase_deadlock_retry_count) #endif - {NULL, NULL, NULL} + PHP_FE_END }; zend_module_entry sybase_module_entry = { @@ -388,7 +388,7 @@ static CS_RETCODE CS_PUBLIC _client_message_handler(CS_CONTEXT *context, CS_CONN TSRMLS_FETCH(); if (CS_SEVERITY(errmsg->msgnumber) >= SybCtG(min_client_severity)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase: Client message: %s (severity %d)", errmsg->msgstring, CS_SEVERITY(errmsg->msgnumber)); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Sybase: Client message: %s (severity %ld)", errmsg->msgstring, (long)CS_SEVERITY(errmsg->msgnumber)); } STR_FREE(SybCtG(server_message)); SybCtG(server_message) = estrdup(errmsg->msgstring); @@ -728,7 +728,7 @@ static int php_sybase_do_connect_internal(sybase_link *sybase, char *host, char static void php_sybase_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { - char *user, *passwd, *host, *charset, *appname; + char *user = NULL, *passwd = NULL, *host = NULL, *charset = NULL, *appname = NULL; char *hashed_details; int hashed_details_length, len; zend_bool new = 0; @@ -777,6 +777,10 @@ static void php_sybase_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) } sybase_ptr = (sybase_link *) malloc(sizeof(sybase_link)); + if (!sybase_ptr) { + efree(hashed_details); + RETURN_FALSE; + } if (!php_sybase_do_connect_internal(sybase_ptr, host, user, passwd, charset, appname TSRMLS_CC)) { free(sybase_ptr); efree(hashed_details); diff --git a/ext/sysvmsg/sysvmsg.c b/ext/sysvmsg/sysvmsg.c index f45b13576..21ab6f713 100644 --- a/ext/sysvmsg/sysvmsg.c +++ b/ext/sysvmsg/sysvmsg.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: sysvmsg.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: sysvmsg.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -95,7 +95,7 @@ const zend_function_entry sysvmsg_functions[] = { PHP_FE(msg_stat_queue, arginfo_msg_stat_queue) PHP_FE(msg_set_queue, arginfo_msg_set_queue) PHP_FE(msg_queue_exists, arginfo_msg_queue_exists) - {NULL, NULL, NULL} /* Must be the last line in sysvmsg_functions[] */ + PHP_FE_END }; /* }}} */ @@ -145,7 +145,7 @@ PHP_MINFO_FUNCTION(sysvmsg) { php_info_print_table_start(); php_info_print_table_row(2, "sysvmsg support", "enabled"); - php_info_print_table_row(2, "Revision", "$Revision: 306939 $"); + php_info_print_table_row(2, "Revision", "$Revision: 313665 $"); php_info_print_table_end(); } /* }}} */ diff --git a/ext/sysvmsg/tests/006.phpt b/ext/sysvmsg/tests/006.phpt index 44074fae7..675e5e114 100644 --- a/ext/sysvmsg/tests/006.phpt +++ b/ext/sysvmsg/tests/006.phpt @@ -37,7 +37,7 @@ bool(true) bool(true) bool(true) bool(false) -Sending/receiving '%d': +Sending/receiving '%s': bool(true) bool(true) bool(true) diff --git a/ext/sysvsem/sysvsem.c b/ext/sysvsem/sysvsem.c index 31d88c6c6..72e6aa2ac 100644 --- a/ext/sysvsem/sysvsem.c +++ b/ext/sysvsem/sysvsem.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: sysvsem.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: sysvsem.c 313665 2011-07-25 11:42:53Z felipe $ */ /* Latest update build anc tested on Linux 2.2.14 * @@ -84,7 +84,7 @@ const zend_function_entry sysvsem_functions[] = { PHP_FE(sem_acquire, arginfo_sem_acquire) PHP_FE(sem_release, arginfo_sem_release) PHP_FE(sem_remove, arginfo_sem_remove) - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ diff --git a/ext/sysvshm/sysvshm.c b/ext/sysvshm/sysvshm.c index d91ec2979..2d1156536 100644 --- a/ext/sysvshm/sysvshm.c +++ b/ext/sysvshm/sysvshm.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: sysvshm.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: sysvshm.c 313665 2011-07-25 11:42:53Z felipe $ */ /* This has been built and tested on Linux 2.2.14 * @@ -86,7 +86,7 @@ const zend_function_entry sysvshm_functions[] = { PHP_FE(shm_has_var, arginfo_shm_has_var) PHP_FE(shm_get_var, arginfo_shm_get_var) PHP_FE(shm_remove_var, arginfo_shm_remove_var) - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ diff --git a/ext/tidy/tidy.c b/ext/tidy/tidy.c index ddc009ebd..de3f0bd62 100644 --- a/ext/tidy/tidy.c +++ b/ext/tidy/tidy.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: tidy.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: tidy.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -404,7 +404,7 @@ static const zend_function_entry tidy_functions[] = { PHP_FE(tidy_get_html, arginfo_tidy_get_html) PHP_FE(tidy_get_body, arginfo_tidy_get_body) PHP_FE(ob_tidyhandler, arginfo_ob_tidyhandler) - {NULL, NULL, NULL} + PHP_FE_END }; static const zend_function_entry tidy_funcs_doc[] = { @@ -429,7 +429,7 @@ static const zend_function_entry tidy_funcs_doc[] = { TIDY_METHOD_MAP(html, tidy_get_html, NULL) TIDY_METHOD_MAP(body, tidy_get_body, NULL) TIDY_DOC_ME(__construct, NULL) - {NULL, NULL, NULL} + PHP_FE_END }; static const zend_function_entry tidy_funcs_node[] = { @@ -443,7 +443,7 @@ static const zend_function_entry tidy_funcs_node[] = { TIDY_NODE_ME(isPhp, NULL) TIDY_NODE_ME(getParent, NULL) TIDY_NODE_PRIVATE_ME(__construct, NULL) - {NULL, NULL, NULL} + PHP_FE_END }; static zend_class_entry *tidy_ce_doc, *tidy_ce_node; @@ -985,6 +985,10 @@ static void php_tidy_create_node(INTERNAL_FUNCTION_PARAMETERS, tidy_base_nodetyp case is_body_node: node = tidyGetBody(obj->ptdoc->doc); break; + + default: + RETURN_NULL(); + break; } if (!node) { @@ -1088,7 +1092,7 @@ static PHP_MINFO_FUNCTION(tidy) php_info_print_table_start(); php_info_print_table_header(2, "Tidy support", "enabled"); php_info_print_table_row(2, "libTidy Release", (char *)tidyReleaseDate()); - php_info_print_table_row(2, "Extension Version", PHP_TIDY_MODULE_VERSION " ($Id: tidy.c 306939 2011-01-01 02:19:59Z felipe $)"); + php_info_print_table_row(2, "Extension Version", PHP_TIDY_MODULE_VERSION " ($Id: tidy.c 313665 2011-07-25 11:42:53Z felipe $)"); php_info_print_table_end(); DISPLAY_INI_ENTRIES(); diff --git a/ext/tokenizer/tokenizer.c b/ext/tokenizer/tokenizer.c index 1a072eef1..596b91ee7 100644 --- a/ext/tokenizer/tokenizer.c +++ b/ext/tokenizer/tokenizer.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: tokenizer.c 308761 2011-02-28 15:18:27Z iliaa $ */ +/* $Id: tokenizer.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -52,7 +52,7 @@ ZEND_END_ARG_INFO() const zend_function_entry tokenizer_functions[] = { PHP_FE(token_get_all, arginfo_token_get_all) PHP_FE(token_name, arginfo_token_name) - {NULL, NULL, NULL} /* Must be the last line in tokenizer_functions[] */ + PHP_FE_END }; /* }}} */ diff --git a/ext/wddx/wddx.c b/ext/wddx/wddx.c index 8bb7d627f..619ebc255 100644 --- a/ext/wddx/wddx.c +++ b/ext/wddx/wddx.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: wddx.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: wddx.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -141,7 +141,7 @@ const zend_function_entry wddx_functions[] = { PHP_FE(wddx_packet_end, arginfo_wddx_packet_end) PHP_FE(wddx_add_vars, arginfo_wddx_add_vars) PHP_FE(wddx_deserialize, arginfo_wddx_deserialize) - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ diff --git a/ext/xml/xml.c b/ext/xml/xml.c index c2ef494d7..a360c3ce8 100644 --- a/ext/xml/xml.c +++ b/ext/xml/xml.c @@ -18,7 +18,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: xml.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: xml.c 314641 2011-08-09 12:16:58Z laruence $ */ #define IS_EXT_MODULE @@ -237,13 +237,13 @@ const zend_function_entry xml_functions[] = { PHP_FE(xml_parser_get_option, arginfo_xml_parser_get_option) PHP_FE(utf8_encode, arginfo_utf8_encode) PHP_FE(utf8_decode, arginfo_utf8_decode) - {NULL, NULL, NULL} + PHP_FE_END }; #ifdef LIBXML_EXPAT_COMPAT static const zend_module_dep xml_deps[] = { ZEND_MOD_REQUIRED("libxml") - {NULL, NULL, NULL} + ZEND_MOD_END }; #endif @@ -1050,7 +1050,7 @@ void _xml_characterDataHandler(void *userData, const XML_Char *s, int len) if (zend_hash_find(Z_ARRVAL_PP(parser->ctag),"value",sizeof("value"),(void **) &myval) == SUCCESS) { int newlen = Z_STRLEN_PP(myval) + decoded_len; Z_STRVAL_PP(myval) = erealloc(Z_STRVAL_PP(myval),newlen+1); - strcpy(Z_STRVAL_PP(myval) + Z_STRLEN_PP(myval),decoded_value); + strncpy(Z_STRVAL_PP(myval) + Z_STRLEN_PP(myval), decoded_value, decoded_len + 1); Z_STRLEN_PP(myval) += decoded_len; efree(decoded_value); } else { @@ -1070,7 +1070,7 @@ void _xml_characterDataHandler(void *userData, const XML_Char *s, int len) if (zend_hash_find(Z_ARRVAL_PP(curtag),"value",sizeof("value"),(void **) &myval) == SUCCESS) { int newlen = Z_STRLEN_PP(myval) + decoded_len; Z_STRVAL_PP(myval) = erealloc(Z_STRVAL_PP(myval),newlen+1); - strcpy(Z_STRVAL_PP(myval) + Z_STRLEN_PP(myval),decoded_value); + strncpy(Z_STRVAL_PP(myval) + Z_STRLEN_PP(myval), decoded_value, decoded_len + 1); Z_STRLEN_PP(myval) += decoded_len; efree(decoded_value); return; diff --git a/ext/xmlreader/php_xmlreader.c b/ext/xmlreader/php_xmlreader.c index 705dfd646..2fd5c9978 100644 --- a/ext/xmlreader/php_xmlreader.c +++ b/ext/xmlreader/php_xmlreader.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: php_xmlreader.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: php_xmlreader.c 314376 2011-08-06 14:47:44Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -320,7 +320,7 @@ static xmlRelaxNGPtr _xmlreader_get_relaxNG(char *source, int source_len, int ty static const zend_module_dep xmlreader_deps[] = { ZEND_MOD_REQUIRED("libxml") - {NULL, NULL, NULL} + ZEND_MOD_END }; /* {{{ xmlreader_module_entry @@ -860,7 +860,7 @@ PHP_METHOD(xmlreader, next) /* }}} */ /* {{{ proto boolean XMLReader::open(string URI [, string encoding [, int options]]) -Sets the URI that the the XMLReader will parse. */ +Sets the URI that the XMLReader will parse. */ PHP_METHOD(xmlreader, open) { zval *id; @@ -1021,7 +1021,7 @@ PHP_METHOD(xmlreader, setParserProperty) /* }}} */ /* {{{ proto boolean XMLReader::setRelaxNGSchema(string filename) -Sets the string that the the XMLReader will parse. */ +Sets the string that the XMLReader will parse. */ PHP_METHOD(xmlreader, setRelaxNGSchema) { php_xmlreader_set_relaxng_schema(INTERNAL_FUNCTION_PARAM_PASSTHRU, XMLREADER_LOAD_FILE); @@ -1029,7 +1029,7 @@ PHP_METHOD(xmlreader, setRelaxNGSchema) /* }}} */ /* {{{ proto boolean XMLReader::setRelaxNGSchemaSource(string source) -Sets the string that the the XMLReader will parse. */ +Sets the string that the XMLReader will parse. */ PHP_METHOD(xmlreader, setRelaxNGSchemaSource) { php_xmlreader_set_relaxng_schema(INTERNAL_FUNCTION_PARAM_PASSTHRU, XMLREADER_LOAD_STRING); @@ -1043,7 +1043,7 @@ XMLPUBFUN int XMLCALL */ /* {{{ proto boolean XMLReader::XML(string source [, string encoding [, int options]]) -Sets the string that the the XMLReader will parse. */ +Sets the string that the XMLReader will parse. */ PHP_METHOD(xmlreader, XML) { zval *id; @@ -1092,9 +1092,7 @@ PHP_METHOD(xmlreader, XML) uri = (char *) xmlCanonicPath((const xmlChar *) resolved_path); } reader = xmlNewTextReader(inputbfr, uri); - if (uri) { - xmlFree(uri); - } + if (reader != NULL) { #if LIBXML_VERSION >= 20628 ret = xmlTextReaderSetup(reader, NULL, uri, encoding, options); @@ -1108,11 +1106,20 @@ PHP_METHOD(xmlreader, XML) } intern->input = inputbfr; intern->ptr = reader; + + if (uri) { + xmlFree(uri); + } + return; } } } + if (uri) { + xmlFree(uri); + } + if (inputbfr) { xmlFreeParserInputBuffer(inputbfr); } @@ -1299,7 +1306,7 @@ static const zend_function_entry xmlreader_functions[] = { PHP_ME(xmlreader, setRelaxNGSchemaSource, arginfo_xmlreader_setRelaxNGSchemaSource, ZEND_ACC_PUBLIC) PHP_ME(xmlreader, XML, arginfo_xmlreader_XML, ZEND_ACC_PUBLIC|ZEND_ACC_ALLOW_STATIC) PHP_ME(xmlreader, expand, arginfo_xmlreader_expand, ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} + PHP_FE_END }; /* {{{ PHP_MINIT_FUNCTION diff --git a/ext/xmlrpc/xmlrpc-epi-php.c b/ext/xmlrpc/xmlrpc-epi-php.c index 1013bc9ab..b14b5584b 100644 --- a/ext/xmlrpc/xmlrpc-epi-php.c +++ b/ext/xmlrpc/xmlrpc-epi-php.c @@ -51,7 +51,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: xmlrpc-epi-php.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: xmlrpc-epi-php.c 313665 2011-07-25 11:42:53Z felipe $ */ /********************************************************************** * BUGS: * @@ -158,7 +158,7 @@ const zend_function_entry xmlrpc_functions[] = { PHP_FE(xmlrpc_parse_method_descriptions, arginfo_xmlrpc_parse_method_descriptions) PHP_FE(xmlrpc_server_add_introspection_data, arginfo_xmlrpc_server_add_introspection_data) PHP_FE(xmlrpc_server_register_introspection_callback, arginfo_xmlrpc_server_register_introspection_callback) - {NULL, NULL, NULL} + PHP_FE_END }; zend_module_entry xmlrpc_module_entry = { diff --git a/ext/xmlwriter/php_xmlwriter.c b/ext/xmlwriter/php_xmlwriter.c index 676d7bcfe..e57009e1a 100644 --- a/ext/xmlwriter/php_xmlwriter.c +++ b/ext/xmlwriter/php_xmlwriter.c @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: php_xmlwriter.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: php_xmlwriter.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -528,7 +528,7 @@ static const zend_function_entry xmlwriter_functions[] = { #endif PHP_FE(xmlwriter_output_memory, arginfo_xmlwriter_output_memory) PHP_FE(xmlwriter_flush, arginfo_xmlwriter_flush) - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ diff --git a/ext/xsl/php_xsl.c b/ext/xsl/php_xsl.c index 2d473f412..b484303d5 100644 --- a/ext/xsl/php_xsl.c +++ b/ext/xsl/php_xsl.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: php_xsl.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: php_xsl.c 314376 2011-08-06 14:47:44Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -35,13 +35,13 @@ static zend_object_handlers xsl_object_handlers; * Every user visible function must have an entry in xsl_functions[]. */ const zend_function_entry xsl_functions[] = { - {NULL, NULL, NULL} /* Must be the last line in xsl_functions[] */ + PHP_FE_END }; /* }}} */ static const zend_module_dep xsl_deps[] = { ZEND_MOD_REQUIRED("libxml") - {NULL, NULL, NULL} + ZEND_MOD_END }; /* {{{ xsl_module_entry diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c index fdbe58222..f86fa6310 100644 --- a/ext/zip/php_zip.c +++ b/ext/zip/php_zip.c @@ -16,7 +16,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: php_zip.c 308107 2011-02-07 16:20:16Z pajoye $ */ +/* $Id: php_zip.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -494,6 +494,28 @@ static char * php_zipobj_get_zip_comment(struct zip *za, int *len TSRMLS_DC) /* #else #define GLOB_FLAGMASK (~0) #endif +#ifndef GLOB_BRACE +# define GLOB_BRACE 0 +#endif +#ifndef GLOB_MARK +# define GLOB_MARK 0 +#endif +#ifndef GLOB_NOSORT +# define GLOB_NOSORT 0 +#endif +#ifndef GLOB_NOCHECK +# define GLOB_NOCHECK 0 +#endif +#ifndef GLOB_NOESCAPE +# define GLOB_NOESCAPE 0 +#endif +#ifndef GLOB_ERR +# define GLOB_ERR 0 +#endif + +/* This is used for checking validity of passed flags (passing invalid flags causes segfault in glob()!! */ +#define GLOB_AVAILABLE_FLAGS (0 | GLOB_BRACE | GLOB_MARK | GLOB_NOSORT | GLOB_NOCHECK | GLOB_NOESCAPE | GLOB_ERR | GLOB_ONLYDIR) + #endif /* }}} */ int php_zip_glob(char *pattern, int pattern_len, long flags, zval *return_value TSRMLS_DC) /* {{{ */ @@ -508,6 +530,16 @@ int php_zip_glob(char *pattern, int pattern_len, long flags, zval *return_value glob_t globbuf; int n; int ret; + + if (pattern_len >= MAXPATHLEN) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Pattern exceeds the maximum allowed length of %d characters", MAXPATHLEN); + return -1; + } + + if ((GLOB_AVAILABLE_FLAGS & flags) != flags) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "At least one of the passed flags is invalid or not supported on this platform"); + return -1; + } #ifdef ZTS if (!IS_ABSOLUTE_PATH(pattern, pattern_len)) { @@ -750,8 +782,7 @@ static const zend_function_entry zip_functions[] = { PHP_FE(zip_entry_name, arginfo_zip_entry_name) PHP_FE(zip_entry_compressedsize, arginfo_zip_entry_compressedsize) PHP_FE(zip_entry_compressionmethod, arginfo_zip_entry_compressionmethod) - - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ @@ -2841,7 +2872,7 @@ static PHP_MINFO_FUNCTION(zip) php_info_print_table_start(); php_info_print_table_row(2, "Zip", "enabled"); - php_info_print_table_row(2, "Extension Version","$Id: php_zip.c 308107 2011-02-07 16:20:16Z pajoye $"); + php_info_print_table_row(2, "Extension Version","$Id: php_zip.c 313665 2011-07-25 11:42:53Z felipe $"); php_info_print_table_row(2, "Zip version", PHP_ZIP_VERSION_STRING); php_info_print_table_row(2, "Libzip version", "0.9.0"); diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c index 137477a27..584c54c3a 100644 --- a/ext/zlib/zlib.c +++ b/ext/zlib/zlib.c @@ -19,7 +19,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: zlib.c 306939 2011-01-01 02:19:59Z felipe $ */ +/* $Id: zlib.c 313665 2011-07-25 11:42:53Z felipe $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -214,7 +214,7 @@ static const zend_function_entry php_zlib_functions[] = { PHP_FE(gzencode, arginfo_gzencode) PHP_FE(ob_gzhandler, arginfo_ob_gzhandler) PHP_FE(zlib_get_coding_type, arginfo_zlib_get_coding_type) - {NULL, NULL, NULL} + PHP_FE_END }; /* }}} */ |