diff options
author | Ondřej Surý <ondrej@sury.org> | 2014-05-05 11:21:23 +0200 |
---|---|---|
committer | Ondřej Surý <ondrej@sury.org> | 2014-05-05 11:21:23 +0200 |
commit | 4bbffbee21093458feadd96f93b96d4627461cff (patch) | |
tree | d316b17d64aede352ae0a336c23238b8756004e6 /ext | |
parent | 9566c3fcaf4cfaa866ea395ee5d1a480785fef0d (diff) | |
download | php-4bbffbee21093458feadd96f93b96d4627461cff.tar.gz |
New upstream version 5.6.0~beta2+dfsgupstream/5.6.0_beta2+dfsg
Diffstat (limited to 'ext')
160 files changed, 5975 insertions, 2531 deletions
diff --git a/ext/com_dotnet/com_olechar.c b/ext/com_dotnet/com_olechar.c index a3e81978b..bf42b4fff 100644 --- a/ext/com_dotnet/com_olechar.c +++ b/ext/com_dotnet/com_olechar.c @@ -46,7 +46,13 @@ PHP_COM_DOTNET_API OLECHAR *php_com_string_to_olestring(char *string, uint strin if (string_len > 0) { olestring = (OLECHAR*)safe_emalloc(string_len, sizeof(OLECHAR), 0); + /* XXX if that's a real multibyte string, olestring is obviously allocated excessively. + This should be fixed by reallocating the olestring, but as emalloc is used, that doesn't + matter much. */ ok = MultiByteToWideChar(codepage, flags, string, string_len, olestring, string_len); + if (ok > 0 && ok < string_len) { + olestring[ok] = '\0'; + } } else { ok = FALSE; olestring = (OLECHAR*)emalloc(sizeof(OLECHAR)); diff --git a/ext/com_dotnet/com_variant.c b/ext/com_dotnet/com_variant.c index 4e044b20b..3cc057127 100644 --- a/ext/com_dotnet/com_variant.c +++ b/ext/com_dotnet/com_variant.c @@ -154,13 +154,17 @@ PHP_COM_DOTNET_API void php_com_variant_from_zval(VARIANT *v, zval *z, int codep case IS_STRING: V_VT(v) = VT_BSTR; olestring = php_com_string_to_olestring(Z_STRVAL_P(z), Z_STRLEN_P(z), codepage TSRMLS_CC); - V_BSTR(v) = SysAllocStringByteLen((char*)olestring, Z_STRLEN_P(z) * sizeof(OLECHAR)); + if (CP_UTF8 == codepage) { + V_BSTR(v) = SysAllocStringByteLen((char*)olestring, wcslen(olestring) * sizeof(OLECHAR)); + } else { + V_BSTR(v) = SysAllocStringByteLen((char*)olestring, Z_STRLEN_P(z) * sizeof(OLECHAR)); + } efree(olestring); break; case IS_RESOURCE: case IS_CONSTANT: - case IS_CONSTANT_ARRAY: + case IS_CONSTANT_AST: default: V_VT(v) = VT_NULL; break; diff --git a/ext/com_dotnet/tests/bug66431_0.phpt b/ext/com_dotnet/tests/bug66431_0.phpt new file mode 100644 index 000000000..daac32853 --- /dev/null +++ b/ext/com_dotnet/tests/bug66431_0.phpt @@ -0,0 +1,42 @@ +--TEST-- +Bug #66431 Special Character via COM Interface (CP_UTF8), Scripting.FileSystemObject +--SKIPIF-- +<?php +if (!extension_loaded("com_dotnet")){ echo "skip COM/.Net support not present"; } +?> +--FILE-- +<?php + +$text= "Xin chào cộng đồng PHP"; +$fpath = str_replace("/", "\\", dirname(__FILE__) . "/bug66431.txt"); + +$fso = new COM("Scripting.FileSystemObject"); +$fh = $fso->OpenTextFile($fpath, 2, true); +$fh->Write($text); +$fh->Close(); + +$check_text = file_get_contents($fpath); + +$result = ($check_text == $text); + +var_dump($result); + +if (!$result) { + echo "Expected: '$check_text'\n"; + echo "Have: '$text'\n"; +} + +?> +===DONE=== +--CLEAN-- +<?php + +$fpath = str_replace("/", "\\", dirname(__FILE__) . "/bug66431.txt"); + +if (file_exists($fpath)) { + unlink($fpath); +} +?> +--EXPECT-- +bool(true) +===DONE=== diff --git a/ext/com_dotnet/tests/bug66431_1.phpt b/ext/com_dotnet/tests/bug66431_1.phpt new file mode 100644 index 000000000..d97011c2b --- /dev/null +++ b/ext/com_dotnet/tests/bug66431_1.phpt @@ -0,0 +1,60 @@ +--TEST-- +Bug #66431 Special Character via COM Interface (CP_UTF8), Application.Word +--SKIPIF-- +<?php +if (!extension_loaded("com_dotnet")){ echo "skip COM/.Net support not present"; } + +try { + new COM("word.application", NULL, CP_UTF8); +} catch (Exception $e) { + die('skip ' . $e->getMessage(); +} + +?> +--FILE-- +<?php + +$text= "Xin chào cộng đồng PHP"; +$fpath = str_replace("/", "\\", dirname(__FILE__) . "/bug66431.docx"); + +com_load_typelib('Word.Application'); + +$Wrd = new COM("word.application", NULL, CP_UTF8); +$Wrd->Documents->Add(); +$Wrd->Selection->TypeText($text); +$Wrd->ActiveDocument->SaveAs($fpath); +$Wrd->ActiveDocument->Close(false); +$Wrd->Application->Quit(); +unset($Wrd); + +$Wrd = new COM("word.application", NULL, CP_UTF8); +$Wrd->Documents->Open($fpath, NULL, false); +$check_text = $Wrd->ActiveDocument->Range($Wrd->ActiveDocument->Sentences(1)->Start, $Wrd->ActiveDocument->Sentences(1)->End)->Text; +$Wrd->ActiveDocument->Close(false); +$Wrd->Application->Quit(); +unset($Wrd); + +/* trim the returned text as we'll get windows eol from a word doc. */ +$result = (trim($check_text) == $text); + +var_dump($result); + +if (!$result) { + echo "Expected: '$check_text'\n"; + echo "Have: '$text'\n"; +} + +?> +===DONE=== +--CLEAN-- +<?php + +$fpath = str_replace("/", "\\", dirname(__FILE__) . "/bug66431.docx"); + +if (file_exists($fpath)) { + unlink($fpath); +} +?> +--EXPECT-- +bool(true) +===DONE=== diff --git a/ext/curl/interface.c b/ext/curl/interface.c index 1995f23a0..f8b04295d 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -2345,25 +2345,34 @@ static int _php_curl_setopt(php_curl *ch, long option, zval **zvalue TSRMLS_DC) case CURLOPT_WRITEHEADER: { FILE *fp = NULL; int type; - void * what; + void *what = NULL; - what = zend_fetch_resource(zvalue TSRMLS_CC, -1, "File-Handle", &type, 1, php_file_le_stream(), php_file_le_pstream()); - if (!what) { - return FAILURE; - } + if (Z_TYPE_PP(zvalue) != IS_NULL) { + what = zend_fetch_resource(zvalue TSRMLS_CC, -1, "File-Handle", &type, 1, php_file_le_stream(), php_file_le_pstream()); + if (!what) { + return FAILURE; + } - if (FAILURE == php_stream_cast((php_stream *) what, PHP_STREAM_AS_STDIO, (void *) &fp, REPORT_ERRORS)) { - return FAILURE; - } + if (FAILURE == php_stream_cast((php_stream *) what, PHP_STREAM_AS_STDIO, (void *) &fp, REPORT_ERRORS)) { + return FAILURE; + } - if (!fp) { - return FAILURE; + if (!fp) { + return FAILURE; + } } error = CURLE_OK; switch (option) { case CURLOPT_FILE: - if (((php_stream *) what)->mode[0] != 'r' || ((php_stream *) what)->mode[1] == '+') { + if (!what) { + if (ch->handlers->write->stream) { + Z_DELREF_P(ch->handlers->write->stream); + ch->handlers->write->stream = NULL; + } + ch->handlers->write->fp = NULL; + ch->handlers->write->method = PHP_CURL_STDOUT; + } else if (((php_stream *) what)->mode[0] != 'r' || ((php_stream *) what)->mode[1] == '+') { if (ch->handlers->write->stream) { Z_DELREF_P(ch->handlers->write->stream); } @@ -2377,7 +2386,14 @@ static int _php_curl_setopt(php_curl *ch, long option, zval **zvalue TSRMLS_DC) } break; case CURLOPT_WRITEHEADER: - if (((php_stream *) what)->mode[0] != 'r' || ((php_stream *) what)->mode[1] == '+') { + if (!what) { + if (ch->handlers->write_header->stream) { + Z_DELREF_P(ch->handlers->write_header->stream); + ch->handlers->write_header->stream = NULL; + } + ch->handlers->write_header->fp = NULL; + ch->handlers->write_header->method = PHP_CURL_IGNORE; + } else if (((php_stream *) what)->mode[0] != 'r' || ((php_stream *) what)->mode[1] == '+') { if (ch->handlers->write_header->stream) { Z_DELREF_P(ch->handlers->write_header->stream); } @@ -2391,16 +2407,30 @@ static int _php_curl_setopt(php_curl *ch, long option, zval **zvalue TSRMLS_DC) } break; case CURLOPT_INFILE: - if (ch->handlers->read->stream) { - Z_DELREF_P(ch->handlers->read->stream); + if (!what) { + if (ch->handlers->read->stream) { + Z_DELREF_P(ch->handlers->read->stream); + ch->handlers->read->stream = NULL; + } + ch->handlers->read->fp = NULL; + ch->handlers->read->fd = 0; + } else { + if (ch->handlers->read->stream) { + Z_DELREF_P(ch->handlers->read->stream); + } + Z_ADDREF_PP(zvalue); + ch->handlers->read->fp = fp; + ch->handlers->read->fd = Z_LVAL_PP(zvalue); + ch->handlers->read->stream = *zvalue; } - Z_ADDREF_PP(zvalue); - ch->handlers->read->fp = fp; - ch->handlers->read->fd = Z_LVAL_PP(zvalue); - ch->handlers->read->stream = *zvalue; break; case CURLOPT_STDERR: - if (((php_stream *) what)->mode[0] != 'r' || ((php_stream *) what)->mode[1] == '+') { + if (!what) { + if (ch->handlers->std_err) { + zval_ptr_dtor(&ch->handlers->std_err); + ch->handlers->std_err = NULL; + } + } else if (((php_stream *) what)->mode[0] != 'r' || ((php_stream *) what)->mode[1] == '+') { if (ch->handlers->std_err) { zval_ptr_dtor(&ch->handlers->std_err); } diff --git a/ext/curl/multi.c b/ext/curl/multi.c index e5c026a51..9fbea5518 100644 --- a/ext/curl/multi.c +++ b/ext/curl/multi.c @@ -248,6 +248,8 @@ PHP_FUNCTION(curl_multi_getcontent) smart_str_0(&ch->handlers->write->buf); RETURN_STRINGL(ch->handlers->write->buf.c, ch->handlers->write->buf.len, 1); } + + RETURN_EMPTY_STRING(); } /* }}} */ diff --git a/ext/curl/tests/bug54798.phpt b/ext/curl/tests/bug54798.phpt index f633a8ab2..afd98cb87 100644 --- a/ext/curl/tests/bug54798.phpt +++ b/ext/curl/tests/bug54798.phpt @@ -53,19 +53,13 @@ foreach($options_to_check as $option) { } ?> +===DONE=== --CLEAN-- <?php @unlink(dirname(__FILE__) . '/bug54798.tmp'); ?> --EXPECTF-- -Warning: curl_exec(): CURLOPT_STDERR resource has gone away, resetting to stderr in %sbug54798.php on line %d -* About to connect() %a -* Closing connection %d -Ok for CURLOPT_STDERR - -Warning: curl_exec(): CURLOPT_WRITEHEADER resource has gone away, resetting to default in %sbug54798.php on line 24 -Ok for CURLOPT_WRITEHEADER - -Warning: curl_exec(): CURLOPT_FILE resource has gone away, resetting to default in %sbug54798.php on line 24 +%a +%aOk for CURLOPT_STDERR +%aOk for CURLOPT_WRITEHEADER %aOk for CURLOPT_FILE - -Warning: curl_exec(): CURLOPT_INFILE resource has gone away, resetting to default in %sbug54798.php on line %d -Ok for CURLOPT_INFILE +%aOk for CURLOPT_INFILE +===DONE=== diff --git a/ext/curl/tests/bug64267.phpt b/ext/curl/tests/bug64267.phpt new file mode 100644 index 000000000..1b115588f --- /dev/null +++ b/ext/curl/tests/bug64267.phpt @@ -0,0 +1,35 @@ +--TEST-- +Bug #64267 (CURLOPT_INFILE doesn't allow reset) +--SKIPIF-- +<?php +extension_loaded("curl") or die("skip need ext/curl"); +?> +--FILE-- +<?php + +echo "TEST\n"; + +$c = curl_init("http://google.com"); +$f = fopen(__FILE__,"r"); +var_dump(curl_setopt_array($c, [ + CURLOPT_RETURNTRANSFER => true, + CURLOPT_UPLOAD => true, + CURLOPT_INFILE => $f, + CURLOPT_INFILESIZE => filesize(__FILE__) +])); +fclose($f); +var_dump(curl_setopt_array($c, [ + CURLOPT_UPLOAD => false, + CURLOPT_INFILE => null, + CURLOPT_INFILESIZE => 0, +])); +curl_exec($c); +var_dump(curl_getinfo($c, CURLINFO_RESPONSE_CODE)); +?> +===DONE=== +--EXPECTF-- +TEST +bool(true) +bool(true) +int(30%d) +===DONE=== diff --git a/ext/curl/tests/curl_basic_008.phpt b/ext/curl/tests/curl_basic_008.phpt index 29e334370..4f0a29d7f 100644 --- a/ext/curl/tests/curl_basic_008.phpt +++ b/ext/curl/tests/curl_basic_008.phpt @@ -25,5 +25,5 @@ curl_close($ch); ?> --EXPECTF-- -%unicode|string%(%d) "%r(Couldn't resolve host|Could not resolve host:)%r %Swww.%s" +%s resolve%s int(6) diff --git a/ext/curl/tests/curl_basic_010.phpt b/ext/curl/tests/curl_basic_010.phpt index 0fc2fe665..72a591ff1 100644 --- a/ext/curl/tests/curl_basic_010.phpt +++ b/ext/curl/tests/curl_basic_010.phpt @@ -26,5 +26,5 @@ curl_close($ch); ?> --EXPECTF-- -%unicode|string%(%d) "%r(Couldn't resolve proxy|Could not resolve proxy:|Could not resolve host:)%r %s" +%unicode|string%(%d) "%r(Couldn't resolve proxy|Could not resolve proxy:|Could not resolve host:|Could not resolve:)%r %s" int(5) diff --git a/ext/curl/tests/curl_reset.phpt b/ext/curl/tests/curl_reset.phpt index c78a8e095..3572e6954 100644 --- a/ext/curl/tests/curl_reset.phpt +++ b/ext/curl/tests/curl_reset.phpt @@ -36,5 +36,8 @@ unlink($test_file); unlink($log_file); ?> + +===DONE=== --EXPECT-- testtest +===DONE=== diff --git a/ext/date/php_date.c b/ext/date/php_date.c index 5852bb2ca..f81f87e4c 100644 --- a/ext/date/php_date.c +++ b/ext/date/php_date.c @@ -2729,7 +2729,9 @@ PHP_METHOD(DateTime, __construct) zend_replace_error_handling(EH_THROW, NULL, &error_handling TSRMLS_CC); if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sO!", &time_str, &time_str_len, &timezone_object, date_ce_timezone)) { - php_date_initialize(zend_object_store_get_object(getThis() TSRMLS_CC), time_str, time_str_len, NULL, timezone_object, 1 TSRMLS_CC); + if (!php_date_initialize(zend_object_store_get_object(getThis() TSRMLS_CC), time_str, time_str_len, NULL, timezone_object, 1 TSRMLS_CC)) { + ZVAL_NULL(getThis()); + } } zend_restore_error_handling(&error_handling TSRMLS_CC); } @@ -2814,6 +2816,10 @@ static int php_date_initialize_from_hash(zval **return_value, php_date_obj **dat tzi = php_date_parse_tzfile(Z_STRVAL_PP(z_timezone), DATE_TIMEZONEDB TSRMLS_CC); + if (tzi == NULL) { + return 0; + } + ALLOC_INIT_ZVAL(tmp_obj); tzobj = zend_object_store_get_object(php_date_instantiate(date_ce_timezone, tmp_obj TSRMLS_CC) TSRMLS_CC); tzobj->type = TIMELIB_ZONETYPE_ID; diff --git a/ext/date/tests/bug66721.phpt b/ext/date/tests/bug66721.phpt new file mode 100644 index 000000000..480671243 --- /dev/null +++ b/ext/date/tests/bug66721.phpt @@ -0,0 +1,11 @@ +--TEST-- +Test for bug #66721: __wakeup of DateTime segfaults when invalid object data is supplied +--CREDITS-- +Boro Sitnikovski <buritomath@yahoo.com> +--FILE-- +<?php +$y = 'O:8:"DateTime":3:{s:4:"date";s:19:"2014-02-15 02:00:51";s:13:"timezone_type";i:3;s:8:"timezone";s:10:"1234567890";}'; +var_dump(unserialize($y)); +?> +--EXPECTF-- +Fatal error: Invalid serialization data for DateTime object in %s on line %d diff --git a/ext/date/tests/bug67118.phpt b/ext/date/tests/bug67118.phpt new file mode 100644 index 000000000..637175764 --- /dev/null +++ b/ext/date/tests/bug67118.phpt @@ -0,0 +1,27 @@ +--TEST-- +Bug #67118 php-cgi crashes regularly on IIS 7 +--INI-- +date.timezone=Europe/Berlin +--FILE-- +<?php +class mydt extends datetime +{ + public function __construct($time = 'now', $tz = NULL, $format = NULL) + { + if (!empty($tz) && !is_object($tz)) { + $tz = new DateTimeZone($tz); + } + + @parent::__construct($time, $tz); + } + +}; + +new mydt("Funktionsansvarig rådgivning och juridik", "UTC"); +--EXPECTF-- +Fatal error: Uncaught exception 'Exception' with message 'DateTime::__construct(): Failed to parse time string (Funktionsansvarig rådgivning och juridik) at position 0 (F): The timezone could not be found in the database' in %sbug67118.php:%d +Stack trace: +#0 %sbug67118.php(%d): DateTime->__construct('Funktionsansvar...', Object(DateTimeZone)) +#1 %sbug67118.php(%d): mydt->__construct('Funktionsansvar...', 'UTC') +#2 {main} + thrown in %sbug67118.php on line %d diff --git a/ext/dom/documenttype.c b/ext/dom/documenttype.c index a94cc31d7..9042457e8 100644 --- a/ext/dom/documenttype.c +++ b/ext/dom/documenttype.c @@ -188,8 +188,7 @@ int dom_documenttype_internal_subset_read(dom_object *obj, zval **retval TSRMLS_ { xmlDtdPtr dtdptr; - xmlDtd *intsubset; - xmlOutputBuffer *buff = NULL; + xmlDtdPtr intsubset; dtdptr = (xmlDtdPtr) dom_object_get_node(obj); @@ -200,22 +199,37 @@ int dom_documenttype_internal_subset_read(dom_object *obj, zval **retval TSRMLS_ ALLOC_ZVAL(*retval); - if (dtdptr->doc != NULL && ((intsubset = dtdptr->doc->intSubset) != NULL)) { - buff = xmlAllocOutputBuffer(NULL); - if (buff != NULL) { - xmlNodeDumpOutput (buff, NULL, (xmlNodePtr) intsubset, 0, 0, NULL); - xmlOutputBufferFlush(buff); + if (dtdptr->doc != NULL && ((intsubset = xmlGetIntSubset(dtdptr->doc)) != NULL) && intsubset->children != NULL) { + smart_str ret_buf = {0}; + xmlNodePtr cur = intsubset->children; + + while (cur != NULL) { + xmlOutputBuffer *buff = xmlAllocOutputBuffer(NULL); + + if (buff != NULL) { + xmlNodeDumpOutput (buff, NULL, cur, 0, 0, NULL); + xmlOutputBufferFlush(buff); + #ifdef LIBXML2_NEW_BUFFER - ZVAL_STRINGL(*retval, xmlOutputBufferGetContent(buff), xmlOutputBufferGetSize(buff), 1); + smart_str_appendl(&ret_buf, xmlOutputBufferGetContent(buff), xmlOutputBufferGetSize(buff)); #else - ZVAL_STRINGL(*retval, buff->buffer->content, buff->buffer->use, 1); + smart_str_appendl(&ret_buf, buff->buffer->content, buff->buffer->use); #endif - (void)xmlOutputBufferClose(buff); + + (void)xmlOutputBufferClose(buff); + } + + cur = cur->next; + } + + if (ret_buf.len) { + ZVAL_STRINGL(*retval, ret_buf.c, ret_buf.len, 1); + smart_str_free(&ret_buf); return SUCCESS; } } - ZVAL_EMPTY_STRING(*retval); + ZVAL_NULL(*retval); return SUCCESS; diff --git a/ext/dom/tests/DOMDocumentType_basic_001.phpt b/ext/dom/tests/DOMDocumentType_basic_001.phpt index 8991ed97d..6648a146f 100644 --- a/ext/dom/tests/DOMDocumentType_basic_001.phpt +++ b/ext/dom/tests/DOMDocumentType_basic_001.phpt @@ -43,6 +43,6 @@ print 'notation: '.$notation->nodeName."\n"; publicId: -//OASIS//DTD DocBook XML//EN systemId: docbookx.dtd name: chapter -internalSubset: <!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML//EN" "docbookx.dtd"> +internalSubset: entity: logo -notation: gif
\ No newline at end of file +notation: gif diff --git a/ext/dom/tests/bug67081.phpt b/ext/dom/tests/bug67081.phpt new file mode 100644 index 000000000..56c2c8e58 --- /dev/null +++ b/ext/dom/tests/bug67081.phpt @@ -0,0 +1,43 @@ +--TEST-- +Bug #67081 DOMDocumentType->internalSubset returns entire DOCTYPE tag, not only the subset +--SKIPIF-- +<?php +require_once('skipif.inc'); +?> +--FILE-- +<?php + $domDocument = new DOMDocument(); + $domDocument->load(dirname(__FILE__) . DIRECTORY_SEPARATOR . "bug67081_0.xml"); + var_dump($domDocument->doctype->internalSubset); + + $domDocument = new DOMDocument(); + $domDocument->load(dirname(__FILE__) . DIRECTORY_SEPARATOR . "bug67081_1.xml"); + var_dump($domDocument->doctype->internalSubset); + + $domDocument = new DOMDocument(); + $domDocument->load(dirname(__FILE__) . DIRECTORY_SEPARATOR . "bug67081_2.xml"); + var_dump($domDocument->doctype->internalSubset); + + $domDocument = new DOMDocument(); + $domDocument->load(dirname(__FILE__) . DIRECTORY_SEPARATOR . "dom.xml"); + var_dump($domDocument->doctype->internalSubset); +?> +===DONE=== +--EXPECT-- +string(19) "<!ELEMENT a EMPTY> +" +string(38) "<!ELEMENT a EMPTY> +<!ELEMENT b EMPTY> +" +NULL +string(277) "<!ENTITY % incent SYSTEM "dom.ent"> +<!ENTITY amp "&#38;"> +<!ENTITY gt ">"> +<!ENTITY % coreattrs "title CDATA #IMPLIED"> +<!ENTITY % attrs "%coreattrs;"> +<!ATTLIST foo bar CDATA #IMPLIED> +<!ELEMENT foo (#PCDATA)> +<!ELEMENT root (foo)+> +<!ATTLIST th title CDATA #IMPLIED> +" +===DONE=== diff --git a/ext/dom/tests/bug67081_0.xml b/ext/dom/tests/bug67081_0.xml new file mode 100644 index 000000000..604eea57b --- /dev/null +++ b/ext/dom/tests/bug67081_0.xml @@ -0,0 +1,6 @@ +<?xml version="1.0"?> +<!DOCTYPE a [ + <!ELEMENT a EMPTY> +]> +<a></a> + diff --git a/ext/dom/tests/bug67081_1.xml b/ext/dom/tests/bug67081_1.xml new file mode 100644 index 000000000..7ae542e97 --- /dev/null +++ b/ext/dom/tests/bug67081_1.xml @@ -0,0 +1,7 @@ +<?xml version="1.0"?> +<!DOCTYPE a [ + <!ELEMENT a EMPTY> + <!ELEMENT b EMPTY> +]> +<a></a> + diff --git a/ext/dom/tests/bug67081_2.xml b/ext/dom/tests/bug67081_2.xml new file mode 100644 index 000000000..c10af0966 --- /dev/null +++ b/ext/dom/tests/bug67081_2.xml @@ -0,0 +1,5 @@ +<?xml version="1.0"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<a></a> + diff --git a/ext/fileinfo/config.m4 b/ext/fileinfo/config.m4 index 4f3404125..a11dbf8da 100644 --- a/ext/fileinfo/config.m4 +++ b/ext/fileinfo/config.m4 @@ -13,6 +13,41 @@ if test "$PHP_FILEINFO" != "no"; then libmagic/is_tar.c libmagic/magic.c libmagic/print.c \ libmagic/readcdf.c libmagic/readelf.c libmagic/softmagic.c" + AC_MSG_CHECKING([for strcasestr]) + AC_TRY_RUN([ +#include <string.h> +#include <strings.h> +#include <stdlib.h> + +int main(void) +{ + char *s0, *s1, *ret; + + s0 = (char *) malloc(42); + s1 = (char *) malloc(8); + + memset(s0, 'X', 42); + s0[24] = 'Y'; + s0[26] = 'Z'; + s0[41] = '\0'; + memset(s1, 'x', 8); + s1[0] = 'y'; + s1[2] = 'Z'; + s1[7] = '\0'; + + ret = strcasestr(s0, s1); + + return !(NULL != ret); +} + ],[ + dnl using the platform implementation + AC_MSG_RESULT(yes) + ],[ + AC_MSG_RESULT(no) + AC_MSG_NOTICE(using libmagic strcasestr implementation) + libmagic_sources="$libmagic_sources libmagic/strcasestr.c" + ]) + PHP_NEW_EXTENSION(fileinfo, fileinfo.c $libmagic_sources, $ext_shared,,-I@ext_srcdir@/libmagic) PHP_ADD_BUILD_DIR($ext_builddir/libmagic) diff --git a/ext/fileinfo/libmagic.patch b/ext/fileinfo/libmagic.patch index a9af5398f..fa708b6c8 100644 --- a/ext/fileinfo/libmagic.patch +++ b/ext/fileinfo/libmagic.patch @@ -2543,7 +2543,7 @@ diff -u libmagic.orig/print.c libmagic/print.c } diff -u libmagic.orig/readcdf.c libmagic/readcdf.c --- libmagic.orig/readcdf.c Tue Jan 7 04:13:42 2014 -+++ libmagic/readcdf.c Fri Feb 21 00:21:27 2014 ++++ libmagic/readcdf.c Thu Apr 24 20:07:51 2014 @@ -30,7 +30,11 @@ #endif @@ -2567,7 +2567,7 @@ diff -u libmagic.orig/readcdf.c libmagic/readcdf.c private const char * cdf_app_to_mime(const char *vbuf, const struct nv *nv) { -@@ -91,7 +99,7 @@ +@@ -91,12 +99,14 @@ { size_t i; cdf_timestamp_t tp; @@ -2576,7 +2576,14 @@ diff -u libmagic.orig/readcdf.c libmagic/readcdf.c char buf[64]; const char *str = NULL; const char *s; -@@ -162,8 +170,12 @@ + int len; + ++ memset(&ts, 0, sizeof(ts)); ++ + for (i = 0; i < count; i++) { + cdf_print_property_name(buf, sizeof(buf), info[i].pi_id); + switch (info[i].pi_type) { +@@ -162,8 +172,12 @@ case CDF_FILETIME: tp = info[i].pi_tp; if (tp != 0) { @@ -2591,7 +2598,7 @@ diff -u libmagic.orig/readcdf.c libmagic/readcdf.c cdf_print_elapsed_time(tbuf, sizeof(tbuf), tp); if (NOTMIME(ms) && file_printf(ms, -@@ -171,8 +183,11 @@ +@@ -171,8 +185,11 @@ return -1; } else { char *c, *ec; @@ -3279,3 +3286,4 @@ diff -u libmagic.orig/softmagic.c libmagic/softmagic.c break; } case FILE_INDIRECT: + diff --git a/ext/fileinfo/libmagic/readcdf.c b/ext/fileinfo/libmagic/readcdf.c index 66a18dbb8..1600625eb 100644 --- a/ext/fileinfo/libmagic/readcdf.c +++ b/ext/fileinfo/libmagic/readcdf.c @@ -105,6 +105,8 @@ cdf_file_property_info(struct magic_set *ms, const cdf_property_info_t *info, const char *s; int len; + memset(&ts, 0, sizeof(ts)); + for (i = 0; i < count; i++) { cdf_print_property_name(buf, sizeof(buf), info[i].pi_id); switch (info[i].pi_type) { diff --git a/ext/fileinfo/libmagic/strcasestr.c b/ext/fileinfo/libmagic/strcasestr.c new file mode 100644 index 000000000..546ed3f96 --- /dev/null +++ b/ext/fileinfo/libmagic/strcasestr.c @@ -0,0 +1,82 @@ +/* $NetBSD: strcasestr.c,v 1.3 2005/11/29 03:12:00 christos Exp $ */ + +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +__RCSID("$NetBSD: strcasestr.c,v 1.3 2005/11/29 03:12:00 christos Exp $"); +__RCSID("$NetBSD: strncasecmp.c,v 1.2 2007/06/04 18:19:27 christos Exp $"); +#endif /* LIBC_SCCS and not lint */ + +#include <assert.h> +#include <ctype.h> +#include <string.h> + +static int +_strncasecmp(const char *s1, const char *s2, size_t n) +{ + if (n != 0) { + const unsigned char *us1 = (const unsigned char *)s1, + *us2 = (const unsigned char *)s2; + + do { + if (tolower(*us1) != tolower(*us2++)) + return tolower(*us1) - tolower(*--us2); + if (*us1++ == '\0') + break; + } while (--n != 0); + } + return 0; +} + +/* + * Find the first occurrence of find in s, ignore case. + */ +char * +strcasestr(const char *s, const char *find) +{ + char c, sc; + size_t len; + + if ((c = *find++) != 0) { + c = tolower((unsigned char)c); + len = strlen(find); + do { + do { + if ((sc = *s++) == 0) + return (NULL); + } while ((char)tolower((unsigned char)sc) != c); + } while (_strncasecmp(s, find, len) != 0); + s--; + } + return (char *)(intptr_t)(s); +} diff --git a/ext/fileinfo/tests/finfo_file_002.phpt b/ext/fileinfo/tests/finfo_file_002.phpt index feb4010f7..5cce7f3f9 100644 --- a/ext/fileinfo/tests/finfo_file_002.phpt +++ b/ext/fileinfo/tests/finfo_file_002.phpt @@ -18,7 +18,7 @@ ksort($results); var_dump($results); ?> --EXPECTF-- -array(8) { +array(9) { ["%s/resources/dir.zip"]=> string(15) "application/zip" ["%s/resources/test.awk"]=> @@ -35,4 +35,6 @@ array(8) { string(15) "application/pdf" ["%s/resources/test.png"]=> string(9) "image/png" + ["%s/resources/test.ppt"]=> + string(29) "application/vnd.ms-powerpoint" } diff --git a/ext/fileinfo/tests/resources/test.ppt b/ext/fileinfo/tests/resources/test.ppt Binary files differnew file mode 100644 index 000000000..713004c03 --- /dev/null +++ b/ext/fileinfo/tests/resources/test.ppt diff --git a/ext/gmp/gmp.c b/ext/gmp/gmp.c index d34d78f01..cd50896cc 100644 --- a/ext/gmp/gmp.c +++ b/ext/gmp/gmp.c @@ -575,22 +575,20 @@ static int gmp_serialize(zval *object, unsigned char **buffer, zend_uint *buf_le mpz_ptr gmpnum = GET_GMP_FROM_ZVAL(object); smart_str buf = {0}; zval zv, *zv_ptr = &zv; - php_serialize_data_t *serialize_data = (php_serialize_data_t *) data; - - PHP_VAR_SERIALIZE_INIT(*serialize_data); + php_serialize_data_t serialize_data = (php_serialize_data_t) data; + PHP_VAR_SERIALIZE_INIT(serialize_data); INIT_PZVAL(zv_ptr); - + gmp_strval(zv_ptr, gmpnum, 10); - php_var_serialize(&buf, &zv_ptr, serialize_data TSRMLS_CC); + php_var_serialize(&buf, &zv_ptr, &serialize_data TSRMLS_CC); zval_dtor(zv_ptr); Z_ARRVAL_P(zv_ptr) = zend_std_get_properties(object TSRMLS_CC); Z_TYPE_P(zv_ptr) = IS_ARRAY; - php_var_serialize(&buf, &zv_ptr, serialize_data TSRMLS_CC); - - PHP_VAR_SERIALIZE_DESTROY(*serialize_data); + php_var_serialize(&buf, &zv_ptr, &serialize_data TSRMLS_CC); + PHP_VAR_SERIALIZE_DESTROY(serialize_data); *buffer = (unsigned char *) buf.c; *buf_len = buf.len; @@ -604,17 +602,16 @@ static int gmp_unserialize(zval **object, zend_class_entry *ce, const unsigned c const unsigned char *p, *max; zval zv, *zv_ptr = &zv; int retval = FAILURE; - php_unserialize_data_t *unserialize_data = (php_unserialize_data_t *) data; - - PHP_VAR_UNSERIALIZE_INIT(*unserialize_data); + php_unserialize_data_t unserialize_data = (php_unserialize_data_t) data; + PHP_VAR_UNSERIALIZE_INIT(unserialize_data); gmp_create_ex(*object, &gmpnum TSRMLS_CC); p = buf; max = buf + buf_len; INIT_ZVAL(zv); - if (!php_var_unserialize(&zv_ptr, &p, max, unserialize_data TSRMLS_CC) + if (!php_var_unserialize(&zv_ptr, &p, max, &unserialize_data TSRMLS_CC) || Z_TYPE_P(zv_ptr) != IS_STRING || convert_to_gmp(gmpnum, zv_ptr, 10 TSRMLS_CC) == FAILURE ) { @@ -624,7 +621,7 @@ static int gmp_unserialize(zval **object, zend_class_entry *ce, const unsigned c zval_dtor(&zv); INIT_ZVAL(zv); - if (!php_var_unserialize(&zv_ptr, &p, max, unserialize_data TSRMLS_CC) + if (!php_var_unserialize(&zv_ptr, &p, max, &unserialize_data TSRMLS_CC) || Z_TYPE_P(zv_ptr) != IS_ARRAY ) { zend_throw_exception(NULL, "Could not unserialize properties", 0 TSRMLS_CC); @@ -641,7 +638,7 @@ static int gmp_unserialize(zval **object, zend_class_entry *ce, const unsigned c retval = SUCCESS; exit: zval_dtor(&zv); - PHP_VAR_UNSERIALIZE_DESTROY(*unserialize_data); + PHP_VAR_UNSERIALIZE_DESTROY(unserialize_data); return retval; } /* }}} */ diff --git a/ext/interbase/config.m4 b/ext/interbase/config.m4 index dfd850b00..6aa1b368c 100644 --- a/ext/interbase/config.m4 +++ b/ext/interbase/config.m4 @@ -1,7 +1,3 @@ -dnl -dnl $Id$ -dnl - PHP_ARG_WITH(interbase,for InterBase support, [ --with-interbase[=DIR] Include InterBase support. DIR is the InterBase base install directory [/usr/interbase]]) diff --git a/ext/interbase/config.w32 b/ext/interbase/config.w32 index 551212546..89cd67219 100644 --- a/ext/interbase/config.w32 +++ b/ext/interbase/config.w32 @@ -1,4 +1,3 @@ -// $Id$ // vim:ft=javascript ARG_WITH("interbase", "InterBase support", "no"); diff --git a/ext/interbase/ibase_blobs.c b/ext/interbase/ibase_blobs.c index 7c27ccf32..94f32f87e 100644 --- a/ext/interbase/ibase_blobs.c +++ b/ext/interbase/ibase_blobs.c @@ -16,8 +16,6 @@ +----------------------------------------------------------------------+ */ -/* $Id$ */ - #ifdef HAVE_CONFIG_H #include "config.h" #endif diff --git a/ext/interbase/ibase_events.c b/ext/interbase/ibase_events.c index 09cdbfa40..ac3c328a0 100644 --- a/ext/interbase/ibase_events.c +++ b/ext/interbase/ibase_events.c @@ -16,8 +16,6 @@ +----------------------------------------------------------------------+ */ -/* $Id$ */ - #ifdef HAVE_CONFIG_H #include "config.h" #endif diff --git a/ext/interbase/ibase_query.c b/ext/interbase/ibase_query.c index 53eb7a885..892745044 100644 --- a/ext/interbase/ibase_query.c +++ b/ext/interbase/ibase_query.c @@ -16,8 +16,6 @@ +----------------------------------------------------------------------+ */ -/* $Id$ */ - #ifdef HAVE_CONFIG_H #include "config.h" #endif diff --git a/ext/interbase/ibase_service.c b/ext/interbase/ibase_service.c index bd6d4ad1a..e4e190d50 100644 --- a/ext/interbase/ibase_service.c +++ b/ext/interbase/ibase_service.c @@ -16,8 +16,6 @@ +----------------------------------------------------------------------+ */ -/* $Id$ */ - #ifdef HAVE_CONFIG_H #include "config.h" #endif diff --git a/ext/interbase/interbase.c b/ext/interbase/interbase.c index c78c52260..8a88efcca 100644 --- a/ext/interbase/interbase.c +++ b/ext/interbase/interbase.c @@ -18,8 +18,6 @@ +----------------------------------------------------------------------+ */ -/* $Id$ */ - #ifdef HAVE_CONFIG_H #include "config.h" #endif diff --git a/ext/interbase/php_ibase_includes.h b/ext/interbase/php_ibase_includes.h index 71990a5e4..c994a92da 100644 --- a/ext/interbase/php_ibase_includes.h +++ b/ext/interbase/php_ibase_includes.h @@ -18,8 +18,6 @@ +----------------------------------------------------------------------+ */ -/* $Id$ */ - #ifndef PHP_IBASE_INCLUDES_H #define PHP_IBASE_INCLUDES_H diff --git a/ext/interbase/php_ibase_udf.c b/ext/interbase/php_ibase_udf.c index 497a86244..e96b73429 100644 --- a/ext/interbase/php_ibase_udf.c +++ b/ext/interbase/php_ibase_udf.c @@ -16,8 +16,6 @@ +----------------------------------------------------------------------+ */ -/* $Id$ */ - /** * This UDF library adds the ability to call PHP functions from SQL * statements. Because of SQL's strong typing, you will have to declare diff --git a/ext/interbase/php_interbase.h b/ext/interbase/php_interbase.h index 044896ac4..0518b586f 100644 --- a/ext/interbase/php_interbase.h +++ b/ext/interbase/php_interbase.h @@ -18,8 +18,6 @@ +----------------------------------------------------------------------+ */ -/* $Id$ */ - #ifndef PHP_INTERBASE_H #define PHP_INTERBASE_H diff --git a/ext/interbase/tests/interbase.inc b/ext/interbase/tests/interbase.inc index 42eb6e591..fcf053213 100644 --- a/ext/interbase/tests/interbase.inc +++ b/ext/interbase/tests/interbase.inc @@ -1,4 +1,4 @@ -<?php /* $Id$ */ +<?php srand((double)microtime()*1000000); diff --git a/ext/interbase/tests/skipif.inc b/ext/interbase/tests/skipif.inc index 8f903a5ea..11d0d80bc 100644 --- a/ext/interbase/tests/skipif.inc +++ b/ext/interbase/tests/skipif.inc @@ -1,4 +1,4 @@ -<?php /* $Id$ */ +<?php if (!extension_loaded("interbase")) print "skip interbase extension not available"; require("interbase.inc"); diff --git a/ext/intl/tests/breakiter_getLocale_basic.phpt b/ext/intl/tests/breakiter_getLocale_basic.phpt index b0112cc84..2f5f586f1 100644 --- a/ext/intl/tests/breakiter_getLocale_basic.phpt +++ b/ext/intl/tests/breakiter_getLocale_basic.phpt @@ -1,9 +1,8 @@ --TEST-- IntlBreakIterator::getLocale(): basic test --SKIPIF-- -<?php -if (!extension_loaded('intl')) - die('skip intl extension not enabled'); +<?php if (!extension_loaded('intl')) die('skip intl extension not enabled'); ?> +<?php if (version_compare(INTL_ICU_VERSION, '53.1') >= 0) die('skip for ICU < 53.1'); ?> --FILE-- <?php ini_set("intl.error_level", E_WARNING); @@ -18,4 +17,4 @@ var_dump($bi->getLocale(1)); --EXPECT-- string(4) "root" string(4) "root" -==DONE==
\ No newline at end of file +==DONE== diff --git a/ext/intl/tests/breakiter_getLocale_basic2.phpt b/ext/intl/tests/breakiter_getLocale_basic2.phpt new file mode 100644 index 000000000..60972fa91 --- /dev/null +++ b/ext/intl/tests/breakiter_getLocale_basic2.phpt @@ -0,0 +1,20 @@ +--TEST-- +IntlBreakIterator::getLocale(): basic test +--SKIPIF-- +<?php if (!extension_loaded('intl')) die('skip intl extension not enabled'); ?> +<?php if (version_compare(INTL_ICU_VERSION, '53.1') < 0) die('skip for ICU >= 53.1'); ?> +--FILE-- +<?php +ini_set("intl.error_level", E_WARNING); +ini_set("intl.default_locale", "pt_PT"); + +$bi = IntlBreakIterator::createSentenceInstance('pt'); + +var_dump($bi->getLocale(0)); +var_dump($bi->getLocale(1)); +?> +==DONE== +--EXPECT-- +string(4) "root" +string(2) "pt" +==DONE== diff --git a/ext/intl/tests/bug62070.phpt b/ext/intl/tests/bug62070.phpt index 3ab0078d7..645c32d9a 100644 --- a/ext/intl/tests/bug62070.phpt +++ b/ext/intl/tests/bug62070.phpt @@ -1,11 +1,9 @@ --TEST-- Bug #62070: Collator::getSortKey() returns garbage --SKIPIF-- -<?php -if (!extension_loaded('intl')) - die('skip intl extension not enabled'); -if (version_compare(INTL_ICU_VERSION, '49') < 0) - die('skip ICU >= 49 only'); +<?php if (!extension_loaded('intl')) die('skip intl extension not enabled'); ?> +<?php if (version_compare(INTL_ICU_VERSION, '49') < 0) die('skip for ICU >= 49'); ?> +<?php if (version_compare(INTL_ICU_VERSION, '53.1') >= 0) die('skip for ICU < 53.1'); ?> --FILE-- <?php $s1 = 'Hello'; diff --git a/ext/intl/tests/bug62070_2.phpt b/ext/intl/tests/bug62070_2.phpt new file mode 100644 index 000000000..63f510793 --- /dev/null +++ b/ext/intl/tests/bug62070_2.phpt @@ -0,0 +1,15 @@ +--TEST-- +Bug #62070: Collator::getSortKey() returns garbage +--SKIPIF-- +<?php if (!extension_loaded('intl')) die('skip intl extension not enabled'); ?> +<?php if (version_compare(INTL_ICU_VERSION, '53.1') < 0) die('skip for ICU >= 53.1'); ?> +--FILE-- +<?php +$s1 = 'Hello'; + +$coll = collator_create('en_US'); +$res = collator_get_sort_key($coll, $s1); + +echo urlencode($res); +--EXPECT-- +71%3F%3FE%01%09%01%DC%08 diff --git a/ext/intl/tests/collator_create2.phpt b/ext/intl/tests/collator_create2.phpt index 90d71b057..5d9f35741 100644 --- a/ext/intl/tests/collator_create2.phpt +++ b/ext/intl/tests/collator_create2.phpt @@ -1,8 +1,9 @@ --TEST-- -create() icu >= 4.8 +create() icu >= 4.8 && icu < 53.1 --SKIPIF-- <?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?> -<?php if(version_compare(INTL_ICU_VERSION, '4.8') < 0) print 'skip'; ?> +<?php if(version_compare(INTL_ICU_VERSION, '4.8') < 0) print 'skip for ICU >= 4.8'; ?> +<?php if (version_compare(INTL_ICU_VERSION, '53.1') >= 0) die('skip for ICU < 53.1'); ?> --FILE-- <?php diff --git a/ext/intl/tests/collator_create3.phpt b/ext/intl/tests/collator_create3.phpt new file mode 100644 index 000000000..c602e794c --- /dev/null +++ b/ext/intl/tests/collator_create3.phpt @@ -0,0 +1,82 @@ +--TEST-- +create() icu >= 53.1 +--SKIPIF-- +<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?> +<?php if (version_compare(INTL_ICU_VERSION, '53.1') < 0) die('skip for ICU >= 53.1'); ?> +--FILE-- +<?php + +/* + * Try creating collator with different locales + * with Procedural and Object methods. + */ + +function ut_main() +{ + $res_str = ''; + + $locales = array( + 'EN-US-ODESSA', + 'UK_UA_ODESSA', + 'uk-ua_CALIFORNIA@currency=;currency=GRN', + '', + 'root', + 'uk@currency=EURO', + '1234567891113151719212325272931333537394143454749515357596163656769717375777981838587899193959799' + ); + + foreach( $locales as $locale ) + { + // Create Collator with the current locale. + $coll = ut_coll_create( $locale ); + if( !is_object($coll) ) + { + $res_str .= "Error creating collator with '$locale' locale: " . + intl_get_error_message() . "\n"; + continue; + } + + // Get the requested, valid and actual locales. + $vloc = ut_coll_get_locale( $coll, Locale::VALID_LOCALE ); + $aloc = ut_coll_get_locale( $coll, Locale::ACTUAL_LOCALE ); + + // Show them. + $res_str .= "Locale: '$locale'\n" . + " ULOC_REQUESTED_LOCALE = '$locale'\n" . + " ULOC_VALID_LOCALE = '$vloc'\n" . + " ULOC_ACTUAL_LOCALE = '$aloc'\n"; + } + + return $res_str; +} + +include_once( 'ut_common.inc' ); +ut_run(); + +?> +--EXPECTF-- +Locale: 'EN-US-ODESSA' + ULOC_REQUESTED_LOCALE = 'EN-US-ODESSA' + ULOC_VALID_LOCALE = 'en_US' + ULOC_ACTUAL_LOCALE = 'root' +Locale: 'UK_UA_ODESSA' + ULOC_REQUESTED_LOCALE = 'UK_UA_ODESSA' + ULOC_VALID_LOCALE = 'uk' + ULOC_ACTUAL_LOCALE = 'uk' +Locale: 'uk-ua_CALIFORNIA@currency=;currency=GRN' + ULOC_REQUESTED_LOCALE = 'uk-ua_CALIFORNIA@currency=;currency=GRN' + ULOC_VALID_LOCALE = 'root' + ULOC_ACTUAL_LOCALE = 'root' +Locale: '' + ULOC_REQUESTED_LOCALE = '' + ULOC_VALID_LOCALE = '%s' + ULOC_ACTUAL_LOCALE = '%s' +Locale: 'root' + ULOC_REQUESTED_LOCALE = 'root' + ULOC_VALID_LOCALE = 'root' + ULOC_ACTUAL_LOCALE = 'root' +Locale: 'uk@currency=EURO' + ULOC_REQUESTED_LOCALE = 'uk@currency=EURO' + ULOC_VALID_LOCALE = 'uk' + ULOC_ACTUAL_LOCALE = 'uk' +Error creating collator with '1234567891113151719212325272931333537394143454749515357596163656769717375777981838587899193959799' locale: Locale string too long, should be no longer than 80 characters: U_ILLEGAL_ARGUMENT_ERROR diff --git a/ext/intl/tests/collator_get_sort_key_variant2.phpt b/ext/intl/tests/collator_get_sort_key_variant2.phpt index bb09256d6..3c526fbc3 100644 --- a/ext/intl/tests/collator_get_sort_key_variant2.phpt +++ b/ext/intl/tests/collator_get_sort_key_variant2.phpt @@ -3,6 +3,7 @@ collator_get_sort_key() --SKIPIF-- <?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?> <?php if (version_compare(INTL_ICU_VERSION, '51.2') < 0) die('skip for ICU >= 51.2'); ?> +<?php if (version_compare(INTL_ICU_VERSION, '53.1') >= 0) die('skip for ICU < 53.1'); ?> --FILE-- <?php diff --git a/ext/intl/tests/collator_get_sort_key_variant3.phpt b/ext/intl/tests/collator_get_sort_key_variant3.phpt new file mode 100644 index 000000000..cc2a23b2b --- /dev/null +++ b/ext/intl/tests/collator_get_sort_key_variant3.phpt @@ -0,0 +1,98 @@ +--TEST-- +collator_get_sort_key() +--SKIPIF-- +<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?> +<?php if (version_compare(INTL_ICU_VERSION, '53.1') < 0) die('skip for ICU >= 53.1'); ?> +--FILE-- +<?php + +/* + * Get sort keys using various locales + */ +function sort_arrays( $locale, $data ) +{ + $res_str = ''; + + $coll = ut_coll_create( $locale ); + + foreach($data as $value) { + $res_val = ut_coll_get_sort_key( $coll, $value ); + $res_str .= "source: ".$value."\n". + "key: ".bin2hex($res_val)."\n"; + } + + return $res_str; +} + + +function ut_main() +{ + $res_str = ''; + + // Regular strings keys + $test_params = array( + 'abc', 'abd', 'aaa', + 'аа', 'а', 'z', + '', null , '3', + 'y' , 'i' , 'k' + ); + + $res_str .= sort_arrays( 'en_US', $test_params ); + + // Sort a non-ASCII array using ru_RU locale. + $test_params = array( + 'абг', 'абв', 'жжж', 'эюя' + ); + + $res_str .= sort_arrays( 'ru_RU', $test_params ); + + // Sort an array using Lithuanian locale. + $res_str .= sort_arrays( 'lt_LT', $test_params ); + + return $res_str . "\n"; +} + +include_once( 'ut_common.inc' ); +ut_run(); +?> +--EXPECT-- +source: abc +key: 292b2d01070107 +source: abd +key: 292b2f01070107 +source: aaa +key: 29292901070107 +source: аа +key: 5e090901060106 +source: а +key: 5e0901050105 +source: z +key: 5b01050105 +source: +key: 0101 +source: +key: 0101 +source: 3 +key: 1a01050105 +source: y +key: 5901050105 +source: i +key: 3901050105 +source: k +key: 3d01050105 +source: абг +key: 2809131701070107 +source: абв +key: 2809131501070107 +source: жжж +key: 2833333301070107 +source: эюя +key: 28cdd1d501070107 +source: абг +key: 5e09131701070107 +source: абв +key: 5e09131501070107 +source: жжж +key: 5e33333301070107 +source: эюя +key: 5ecdd1d501070107 diff --git a/ext/intl/tests/formatter_format3.phpt b/ext/intl/tests/formatter_format3.phpt index d52d15999..b9d6eb5c3 100644 --- a/ext/intl/tests/formatter_format3.phpt +++ b/ext/intl/tests/formatter_format3.phpt @@ -1,8 +1,9 @@ --TEST-- -numfmt_format() icu >= 52.1 +numfmt_format() icu >= 52.1 && icu < 53.1 --SKIPIF-- <?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?> -<?php if(version_compare(INTL_ICU_VERSION, '52.1') < 0) print 'skip'; ?> +<?php if(version_compare(INTL_ICU_VERSION, '52.1') < 0) print 'skip for ICU >= 52.1'; ?> +<?php if (version_compare(INTL_ICU_VERSION, '53.1') >= 0) die('skip for ICU < 53.1'); ?> --FILE-- <?php diff --git a/ext/intl/tests/formatter_format4.phpt b/ext/intl/tests/formatter_format4.phpt new file mode 100644 index 000000000..88d457bdb --- /dev/null +++ b/ext/intl/tests/formatter_format4.phpt @@ -0,0 +1,130 @@ +--TEST-- +numfmt_format() icu >= 53.1 +--SKIPIF-- +<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?> +<?php if (version_compare(INTL_ICU_VERSION, '53.1') < 0) die('skip for ICU >= 53.1'); ?> +--FILE-- +<?php + +/* + * Format a number using misc locales/patterns. + */ + +/* + * TODO: doesn't pass on ICU 3.6 because 'ru' and 'de' locales changed + * currency and percent formatting. + */ + +function ut_main() +{ + $styles = array( + NumberFormatter::PATTERN_DECIMAL => '##.#####################', + NumberFormatter::DECIMAL => '', + NumberFormatter::CURRENCY => '', + NumberFormatter::PERCENT => '', + NumberFormatter::SCIENTIFIC => '', + NumberFormatter::SPELLOUT => '@@@@@@@', + NumberFormatter::ORDINAL => '', + NumberFormatter::DURATION => '', + NumberFormatter::PATTERN_RULEBASED => '#####.###', + 1234999, // bad one + ); + + $integer = array( + NumberFormatter::ORDINAL => '', + NumberFormatter::DURATION => '', + ); + $locales = array( + 'en_US', + 'ru_UA', + 'de', + 'fr', + 'en_UK' + ); + + $str_res = ''; + $number = 1234567.891234567890000; + + foreach( $locales as $locale ) + { + $str_res .= "\nLocale is: $locale\n"; + foreach( $styles as $style => $pattern ) + { + $fmt = ut_nfmt_create( $locale, $style, $pattern ); + + if(!$fmt) { + $str_res .= "Bad formatter!\n"; + continue; + } + $str_res .= dump( isset($integer[$style])?ut_nfmt_format( $fmt, $number, NumberFormatter::TYPE_INT32):ut_nfmt_format( $fmt, $number ) ) . "\n"; + } + } + return $str_res; +} + +include_once( 'ut_common.inc' ); + +// Run the test +ut_run(); + +?> +--EXPECTREGEX-- +Locale is: en_US +'1234567.89123457' +'1,234,567.891' +'\$1,234,567.89' +'123,456,789%' +'1.23456789123457E6' +'one million,? two hundred (and )?thirty-four thousand,? five hundred (and )?sixty-seven point eight nine one two three four five seven' +'1,234,567(th|ᵗʰ)' +'342:56:07' +'#####.###' +'USD1,234,567.89' + +Locale is: ru_UA +'1234567,89123457' +'1 234 567,891' +'1 234 567,89 ?(грн\.|₴)' +'123 456 789 ?%' +'1,23456789123457E6' +'один миллион двести тридцать четыре тысяч пятьсот шестьдесят семь запятая восемь девять один два три четыре пять семь' +'1 234 567.?' +'1 234 567' +'#####.###' +'1 234 567,89 UAH' + +Locale is: de +'1234567,89123457' +'1.234.567,891' +'(¤ )?1.234.567,89( ¤)?' +'123\.456\.789 %' +'1,23456789123457E6' +'eine Million zweihundertvierunddreißigtausendfünfhundertsiebenundsechzig Komma acht neun eins zwei drei vier fünf sieben' +'1.234.567.?' +'1.234.567' +'#####.###' +'1.234.567,89 ¤¤' + +Locale is: fr +'1234567,89123457' +'1 234 567,891' +'1 234 567,89 ¤' +'123 456 789 ?%' +'1,23456789123457E6' +'un million deux cent trente-quatre mille cinq cent soixante-sept virgule huit neuf un deux trois quatre cinq sept' +'1 234 567e' +'1 234 567' +'#####.###' +'1 234 567,89 ¤¤' + +Locale is: en_UK +'1234567.89123457' +'1,234,567.891' +'¤1,234,567.89' +'123,456,789%' +'1.23456789123457E6' +'one million,? two hundred (and )?thirty-four thousand,? five hundred (and )?sixty-seven point eight nine one two three four five seven' +'1,234,567(th|ᵗʰ)' +'342:56:07' +'#####.###' +'¤¤1,234,567.89' diff --git a/ext/intl/tests/locale_get_display_name4.phpt b/ext/intl/tests/locale_get_display_name4.phpt index 470674997..4b3255c0f 100644 --- a/ext/intl/tests/locale_get_display_name4.phpt +++ b/ext/intl/tests/locale_get_display_name4.phpt @@ -1,8 +1,9 @@ --TEST-- -locale_get_display_name() icu >= 52.1 +locale_get_display_name() icu >= 52.1 && icu < 53.1 --SKIPIF-- <?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?> <?php if (version_compare(INTL_ICU_VERSION, '52.1') < 0) die('skip for ICU >= 52.1'); ?> +<?php if (version_compare(INTL_ICU_VERSION, '53.1') >= 0) die('skip for ICU < 53.1'); ?> --FILE-- <?php diff --git a/ext/intl/tests/locale_get_display_name5.phpt b/ext/intl/tests/locale_get_display_name5.phpt new file mode 100644 index 000000000..b633b1f62 --- /dev/null +++ b/ext/intl/tests/locale_get_display_name5.phpt @@ -0,0 +1,342 @@ +--TEST-- +locale_get_display_name() icu >= 53.1 +--SKIPIF-- +<?php if( !extension_loaded( 'intl' ) ) print 'skip'; ?> +<?php if (version_compare(INTL_ICU_VERSION, '53.1') < 0) die('skip for ICU >= 53.1'); ?> +--FILE-- +<?php + +/* + * Try getting the display_name for different locales + * with Procedural and Object methods. + */ + +function ut_main() +{ + $res_str=''; + + $disp_locales=array('en','fr','de'); + + $locales = array( + 'sl_IT_nedis_KIRTI', + 'sl_IT_nedis-a-kirti-x-xyz', + 'sl_IT_rozaj', + 'sl_IT_NEDIS_ROJAZ_1901', + 'i-enochian', + 'zh-hakka', + 'zh-wuu', + 'i-tay', + 'sgn-BE-nl', + 'sgn-CH-de', + 'sl_IT_rozaj@currency=EUR', + 'uk-ua_CALIFORNIA@currency=;currency=GRN', + 'root', + 'uk@currency=EURO', + 'Hindi', +//Simple language subtag + 'de', + 'fr', + 'ja', + 'i-enochian', //(example of a grandfathered tag) +//Language subtag plus Script subtag: + 'zh-Hant', + 'zh-Hans', + 'sr-Cyrl', + 'sr-Latn', +//Language-Script-Region + 'zh-Hans-CN', + 'sr-Latn-CS', +//Language-Variant + 'sl-rozaj', + 'sl-nedis', +//Language-Region-Variant + 'de-CH-1901', + 'sl-IT-nedis', +//Language-Script-Region-Variant + 'sl-Latn-IT-nedis', +//Language-Region: + 'de-DE', + 'en-US', + 'es-419', +//Private use subtags: + 'de-CH-x-phonebk', + 'az-Arab-x-AZE-derbend', +//Extended language subtags + 'zh-min', + 'zh-min-nan-Hant-CN', +//Private use registry values + 'x-whatever', + 'qaa-Qaaa-QM-x-southern', + 'sr-Latn-QM', + 'sr-Qaaa-CS', +/*Tags that use extensions (examples ONLY: extensions MUST be defined + by revision or update to this document or by RFC): */ + 'en-US-u-islamCal', + 'zh-CN-a-myExt-x-private', + 'en-a-myExt-b-another', +//Some Invalid Tags: + 'de-419-DE', + 'a-DE', + 'ar-a-aaa-b-bbb-a-ccc' + ); + + + $res_str = ''; + + foreach( $locales as $locale ) + { + $res_str .= "locale='$locale'\n"; + foreach( $disp_locales as $disp_locale ) + { + $scr = ut_loc_get_display_name( $locale ,$disp_locale ); + $scr = str_replace(array('(', ')'), '#', $scr); + $res_str .= "disp_locale=$disp_locale : display_name=$scr"; + $res_str .= "\n"; + } + $res_str .= "-----------------\n"; + } + + return $res_str; + +} + +include_once( 'ut_common.inc' ); +ut_run(); + +?> +--EXPECTREGEX-- +locale='sl_IT_nedis_KIRTI' +disp_locale=en : display_name=Slovenian #Italy, NEDIS_KIRTI# +disp_locale=fr : display_name=slovène #Italie, NEDIS_KIRTI# +disp_locale=de : display_name=Slowenisch #Italien, NEDIS_KIRTI# +----------------- +locale='sl_IT_nedis-a-kirti-x-xyz' +disp_locale=en : display_name=Slovenian #Italy, NEDIS_A_KIRTI_X_XYZ# +disp_locale=fr : display_name=slovène #Italie, NEDIS_A_KIRTI_X_XYZ# +disp_locale=de : display_name=Slowenisch #Italien, NEDIS_A_KIRTI_X_XYZ# +----------------- +locale='sl_IT_rozaj' +disp_locale=en : display_name=Slovenian #Italy, Resian# +disp_locale=fr : display_name=slovène #Italie, dialecte de Resia# +disp_locale=de : display_name=Slowenisch #Italien, (ROZAJ|Resianisch)# +----------------- +locale='sl_IT_NEDIS_ROJAZ_1901' +disp_locale=en : display_name=Slovenian #Italy, NEDIS_ROJAZ_1901# +disp_locale=fr : display_name=slovène #Italie, NEDIS_ROJAZ_1901# +disp_locale=de : display_name=Slowenisch #Italien, NEDIS_ROJAZ_1901# +----------------- +locale='i-enochian' +disp_locale=en : display_name=i-enochian #Private-Use=i-enochian# +disp_locale=fr : display_name=i-enochian #Usage privé=i-enochian# +disp_locale=de : display_name=i-enochian #Privatnutzung=i-enochian# +----------------- +locale='zh-hakka' +disp_locale=en : display_name=Chinese( #HAKKA#)? +disp_locale=fr : display_name=chinois( #HAKKA#)? +disp_locale=de : display_name=Chinesisch( #HAKKA#)? +----------------- +locale='zh-wuu' +disp_locale=en : display_name=Chinese #WUU# +disp_locale=fr : display_name=chinois #WUU# +disp_locale=de : display_name=Chinesisch #WUU# +----------------- +locale='i-tay' +disp_locale=en : display_name=i-tay +disp_locale=fr : display_name=i-tay +disp_locale=de : display_name=i-tay +----------------- +locale='sgn-BE-nl' +disp_locale=en : display_name=sgn #Belgium, NL# +disp_locale=fr : display_name=sgn #Belgique, NL# +disp_locale=de : display_name=sgn #Belgien, NL# +----------------- +locale='sgn-CH-de' +disp_locale=en : display_name=sgn #Switzerland, DE# +disp_locale=fr : display_name=sgn #Suisse, DE# +disp_locale=de : display_name=sgn #Schweiz, DE# +----------------- +locale='sl_IT_rozaj@currency=EUR' +disp_locale=en : display_name=Slovenian #Italy, Resian, [Cc]urrency=Euro# +disp_locale=fr : display_name=slovène #Italie, dialecte de Resia, [Dd]evise=euro# +disp_locale=de : display_name=Slowenisch #Italien, (ROZAJ|Resianisch), Währung=Euro# +----------------- +locale='uk-ua_CALIFORNIA@currency=;currency=GRN' +disp_locale=en : display_name= +disp_locale=fr : display_name= +disp_locale=de : display_name= +----------------- +locale='root' +disp_locale=en : display_name=Root +disp_locale=fr : display_name=racine +disp_locale=de : display_name=[Rr]oot +----------------- +locale='uk@currency=EURO' +disp_locale=en : display_name=Ukrainian #[Cc]urrency=EURO# +disp_locale=fr : display_name=ukrainien #[Dd]evise=EURO# +disp_locale=de : display_name=Ukrainisch #Währung=EURO# +----------------- +locale='Hindi' +disp_locale=en : display_name=hindi +disp_locale=fr : display_name=hindi +disp_locale=de : display_name=hindi +----------------- +locale='de' +disp_locale=en : display_name=German +disp_locale=fr : display_name=allemand +disp_locale=de : display_name=Deutsch +----------------- +locale='fr' +disp_locale=en : display_name=French +disp_locale=fr : display_name=français +disp_locale=de : display_name=Französisch +----------------- +locale='ja' +disp_locale=en : display_name=Japanese +disp_locale=fr : display_name=japonais +disp_locale=de : display_name=Japanisch +----------------- +locale='i-enochian' +disp_locale=en : display_name=i-enochian #Private-Use=i-enochian# +disp_locale=fr : display_name=i-enochian #Usage privé=i-enochian# +disp_locale=de : display_name=i-enochian #Privatnutzung=i-enochian# +----------------- +locale='zh-Hant' +disp_locale=en : display_name=Chinese #Traditional# +disp_locale=fr : display_name=chinois #traditionnel# +disp_locale=de : display_name=Chinesisch #Traditionell# +----------------- +locale='zh-Hans' +disp_locale=en : display_name=Chinese #Simplified# +disp_locale=fr : display_name=chinois #simplifié# +disp_locale=de : display_name=Chinesisch #Vereinfacht# +----------------- +locale='sr-Cyrl' +disp_locale=en : display_name=Serbian #Cyrillic# +disp_locale=fr : display_name=serbe #cyrillique# +disp_locale=de : display_name=Serbisch #Kyrillisch# +----------------- +locale='sr-Latn' +disp_locale=en : display_name=Serbian #Latin# +disp_locale=fr : display_name=serbe #latin# +disp_locale=de : display_name=Serbisch #Lateinisch# +----------------- +locale='zh-Hans-CN' +disp_locale=en : display_name=Chinese #Simplified, China# +disp_locale=fr : display_name=chinois #simplifié, Chine# +disp_locale=de : display_name=Chinesisch #Vereinfacht, China# +----------------- +locale='sr-Latn-CS' +disp_locale=en : display_name=Serbian #Latin, Serbia# +disp_locale=fr : display_name=serbe #latin, Serbie# +disp_locale=de : display_name=Serbisch #Lateinisch, Serbien# +----------------- +locale='sl-rozaj' +disp_locale=en : display_name=Slovenian #Resian# +disp_locale=fr : display_name=slovène #dialecte de Resia# +disp_locale=de : display_name=Slowenisch( #(ROZAJ|Resianisch)#)? +----------------- +locale='sl-nedis' +disp_locale=en : display_name=Slovenian #Natisone dialect# +disp_locale=fr : display_name=slovène #dialecte de Natisone# +disp_locale=de : display_name=Slowenisch #Natisone-Dialekt# +----------------- +locale='de-CH-1901' +disp_locale=en : display_name=German #Switzerland, Traditional German orthography# +disp_locale=fr : display_name=allemand #Suisse, orthographe allemande traditionnelle# +disp_locale=de : display_name=Deutsch #Schweiz, (1901|[aA]lte deutsche Rechtschreibung)# +----------------- +locale='sl-IT-nedis' +disp_locale=en : display_name=Slovenian #Italy, Natisone dialect# +disp_locale=fr : display_name=slovène #Italie, dialecte de Natisone# +disp_locale=de : display_name=Slowenisch #Italien, (NEDIS|Natisone-Dialekt)# +----------------- +locale='sl-Latn-IT-nedis' +disp_locale=en : display_name=Slovenian #Latin, Italy, Natisone dialect# +disp_locale=fr : display_name=slovène #latin, Italie, dialecte de Natisone# +disp_locale=de : display_name=Slowenisch #Lateinisch, Italien, (NEDIS|Natisone-Dialekt)# +----------------- +locale='de-DE' +disp_locale=en : display_name=German #Germany# +disp_locale=fr : display_name=allemand #Allemagne# +disp_locale=de : display_name=Deutsch #Deutschland# +----------------- +locale='en-US' +disp_locale=en : display_name=English #United States# +disp_locale=fr : display_name=anglais #États-Unis# +disp_locale=de : display_name=Englisch #Vereinigte Staaten# +----------------- +locale='es-419' +disp_locale=en : display_name=Spanish #Latin America# +disp_locale=fr : display_name=espagnol #Amérique latine# +disp_locale=de : display_name=Spanisch #Lateinamerika# +----------------- +locale='de-CH-x-phonebk' +disp_locale=en : display_name=German #Switzerland, Private-Use=phonebk# +disp_locale=fr : display_name=allemand #Suisse, Usage privé=phonebk# +disp_locale=de : display_name=Deutsch #Schweiz, Privatnutzung=phonebk# +----------------- +locale='az-Arab-x-AZE-derbend' +disp_locale=en : display_name=Azerbaijani #Arabic, Private-Use=aze-derbend# +disp_locale=fr : display_name=azéri #arabe, Usage privé=aze-derbend# +disp_locale=de : display_name=Aserbaidschanisch #Arabisch, Privatnutzung=aze-derbend# +----------------- +locale='zh-min' +disp_locale=en : display_name=Chinese #MIN# +disp_locale=fr : display_name=chinois #MIN# +disp_locale=de : display_name=Chinesisch #MIN# +----------------- +locale='zh-min-nan-Hant-CN' +disp_locale=en : display_name=Chinese #MIN, NAN_HANT_CN# +disp_locale=fr : display_name=chinois #MIN, NAN_HANT_CN# +disp_locale=de : display_name=Chinesisch #MIN, NAN_HANT_CN# +----------------- +locale='x-whatever' +disp_locale=en : display_name=x-whatever #Private-Use=whatever# +disp_locale=fr : display_name=x-whatever #Usage privé=whatever# +disp_locale=de : display_name=x-whatever #Privatnutzung=whatever# +----------------- +locale='qaa-Qaaa-QM-x-southern' +disp_locale=en : display_name=qaa #Qaaa, QM, Private-Use=southern# +disp_locale=fr : display_name=qaa #Qaaa, QM, Usage privé=southern# +disp_locale=de : display_name=qaa #Qaaa, QM, Privatnutzung=southern# +----------------- +locale='sr-Latn-QM' +disp_locale=en : display_name=Serbian #Latin, QM# +disp_locale=fr : display_name=serbe #latin, QM# +disp_locale=de : display_name=Serbisch #Lateinisch, QM# +----------------- +locale='sr-Qaaa-CS' +disp_locale=en : display_name=Serbian #Qaaa, Serbia# +disp_locale=fr : display_name=serbe #Qaaa, Serbie# +disp_locale=de : display_name=Serbisch #Qaaa, Serbien# +----------------- +locale='en-US-u-islamCal' +disp_locale=en : display_name=English #United States, attribute=islamcal# +disp_locale=fr : display_name=anglais #États-Unis, attribute=islamcal# +disp_locale=de : display_name=Englisch #Vereinigte Staaten, attribute=islamcal# +----------------- +locale='zh-CN-a-myExt-x-private' +disp_locale=en : display_name=Chinese #China, a=myext, Private-Use=private# +disp_locale=fr : display_name=chinois #Chine, a=myext, Usage privé=private# +disp_locale=de : display_name=Chinesisch #China, a=myext, Privatnutzung=private# +----------------- +locale='en-a-myExt-b-another' +disp_locale=en : display_name=English #a=myext, b=another# +disp_locale=fr : display_name=anglais #a=myext, b=another# +disp_locale=de : display_name=Englisch #a=myext, b=another# +----------------- +locale='de-419-DE' +disp_locale=en : display_name=German #Latin America, DE# +disp_locale=fr : display_name=allemand #Amérique latine, DE# +disp_locale=de : display_name=Deutsch #Lateinamerika, DE# +----------------- +locale='a-DE' +disp_locale=en : display_name=a #Germany# +disp_locale=fr : display_name=a #Allemagne# +disp_locale=de : display_name=a #Deutschland# +----------------- +locale='ar-a-aaa-b-bbb-a-ccc' +disp_locale=en : display_name=Arabic #a=aaa, b=bbb# +disp_locale=fr : display_name=arabe #a=aaa, b=bbb# +disp_locale=de : display_name=Arabisch #a=aaa, b=bbb# +----------------- diff --git a/ext/intl/tests/timezone_getCanonicalID_variant1.phpt b/ext/intl/tests/timezone_getCanonicalID_variant1.phpt index 92a7f0737..d600792d7 100644 --- a/ext/intl/tests/timezone_getCanonicalID_variant1.phpt +++ b/ext/intl/tests/timezone_getCanonicalID_variant1.phpt @@ -1,9 +1,8 @@ --TEST-- IntlTimeZone::getCanonicalID(): second argument --SKIPIF-- -<?php -if (!extension_loaded('intl')) - die('skip intl extension not enabled'); +<?php if (!extension_loaded('intl')) die('skip intl extension not enabled'); ?> +<?php if (version_compare(INTL_ICU_VERSION, '53.1') >= 0) die('skip for ICU < 53.1'); ?> --FILE-- <?php ini_set("intl.error_level", E_WARNING); @@ -21,4 +20,4 @@ string(13) "Europe/Lisbon" bool(true) string(0) "" bool(false) -==DONE==
\ No newline at end of file +==DONE==
diff --git a/ext/intl/tests/timezone_getCanonicalID_variant1_2.phpt b/ext/intl/tests/timezone_getCanonicalID_variant1_2.phpt new file mode 100644 index 000000000..b60924332 --- /dev/null +++ b/ext/intl/tests/timezone_getCanonicalID_variant1_2.phpt @@ -0,0 +1,26 @@ +--TEST-- +IntlTimeZone::getCanonicalID(): second argument +--SKIPIF-- +<?php if (!extension_loaded('intl')) die('skip intl extension not enabled'); ?> +<?php if (version_compare(INTL_ICU_VERSION, '53.1') < 0) die('skip for ICU >= 53.1'); ?> +--FILE-- +<?php +ini_set("intl.error_level", E_WARNING); + +var_dump(IntlTimeZone::getCanonicalID('Portugal', $isSystemId)); +var_dump($isSystemId); + +/* A valid custom time zone ID has the following syntax: GMT[+|-]hh[[:]mm] */ +var_dump(IntlTimeZone::getCanonicalID('GMT +01:25', $isSystemId)); +var_dump($isSystemId); + +?> +==DONE== +--EXPECTF-- +string(13) "Europe/Lisbon" +bool(true) + +Warning: IntlTimeZone::getCanonicalID(): intltz_get_canonical_id: error obtaining canonical ID in %stimezone_getCanonicalID_variant1_2.php on line %d +bool(false) +bool(true) +==DONE== diff --git a/ext/ldap/ldap.c b/ext/ldap/ldap.c index f5583cd8c..50055a25a 100644 --- a/ext/ldap/ldap.c +++ b/ext/ldap/ldap.c @@ -405,6 +405,16 @@ PHP_FUNCTION(ldap_bind) RETURN_FALSE; } + if (ldap_bind_dn != NULL && memchr(ldap_bind_dn, '\0', ldap_bind_dnlen) != NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "DN contains a null byte"); + RETURN_FALSE; + } + + if (ldap_bind_pw != NULL && memchr(ldap_bind_pw, '\0', ldap_bind_pwlen) != NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Password contains a null byte"); + RETURN_FALSE; + } + ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link); if ((rc = ldap_bind_s(ld->link, ldap_bind_dn, ldap_bind_pw, LDAP_AUTH_SIMPLE)) != LDAP_SUCCESS) { diff --git a/ext/mbstring/tests/zend_multibyte-08.phpt b/ext/mbstring/tests/zend_multibyte-08.phpt index 9e5b75ead..561ad6403 100644 --- a/ext/mbstring/tests/zend_multibyte-08.phpt +++ b/ext/mbstring/tests/zend_multibyte-08.phpt @@ -1,5 +1,8 @@ --TEST-- zend multibyte (8) +--SKIPIF-- +--XFAIL-- +https://bugs.php.net/bug.php?id=66582 - still leaks memory which causes fail in debug mode --INI-- zend.multibyte=On zend.script_encoding=ISO-8859-1 diff --git a/ext/mbstring/tests/zend_multibyte-10.phpt b/ext/mbstring/tests/zend_multibyte-10.phpt index 138b4899f..b4f85e6f3 100644 --- a/ext/mbstring/tests/zend_multibyte-10.phpt +++ b/ext/mbstring/tests/zend_multibyte-10.phpt @@ -1,5 +1,8 @@ --TEST-- zend multibyte (10) +--SKIPIF-- +--XFAIL-- +https://bugs.php.net/bug.php?id=66582 - still leaks memory which causes fail in debug mode --INI-- zend.multibyte=1 --FILE-- diff --git a/ext/mbstring/tests/zend_multibyte-11.phpt b/ext/mbstring/tests/zend_multibyte-11.phpt index 6844d0637..eadbb2156 100644 --- a/ext/mbstring/tests/zend_multibyte-11.phpt +++ b/ext/mbstring/tests/zend_multibyte-11.phpt @@ -1,5 +1,8 @@ --TEST-- zend multibyte (11) +--SKIPIF-- +--XFAIL-- +https://bugs.php.net/bug.php?id=66582 - still leaks memory which causes fail in debug mode --INI-- zend.multibyte=1 --FILE-- diff --git a/ext/mysql/php_mysql.c b/ext/mysql/php_mysql.c index daf712cd0..56e48a0a6 100644 --- a/ext/mysql/php_mysql.c +++ b/ext/mysql/php_mysql.c @@ -877,7 +877,7 @@ static void php_mysql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) #ifndef MYSQL_USE_MYSQLND mysql->conn = mysql_init(NULL); #else - mysql->conn = mysql_init(persistent); + mysql->conn = mysqlnd_init(MYSQLND_CLIENT_KNOWS_RSET_COPY_DATA, persistent); #endif if (connect_timeout != -1) { @@ -886,7 +886,7 @@ static void php_mysql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) #ifndef MYSQL_USE_MYSQLND if (mysql_real_connect(mysql->conn, host, user, passwd, NULL, port, socket, client_flags)==NULL) #else - if (mysqlnd_connect(mysql->conn, host, user, passwd, passwd_len, NULL, 0, port, socket, client_flags TSRMLS_CC) == NULL) + if (mysqlnd_connect(mysql->conn, host, user, passwd, passwd_len, NULL, 0, port, socket, client_flags, MYSQLND_CLIENT_KNOWS_RSET_COPY_DATA TSRMLS_CC) == NULL) #endif { /* Populate connect error globals so that the error functions can read them */ @@ -934,7 +934,7 @@ static void php_mysql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) #ifndef MYSQL_USE_MYSQLND if (mysql_real_connect(mysql->conn, host, user, passwd, NULL, port, socket, client_flags)==NULL) #else - if (mysqlnd_connect(mysql->conn, host, user, passwd, passwd_len, NULL, 0, port, socket, client_flags TSRMLS_CC) == NULL) + if (mysqlnd_connect(mysql->conn, host, user, passwd, passwd_len, NULL, 0, port, socket, client_flags, MYSQLND_CLIENT_KNOWS_RSET_COPY_DATA TSRMLS_CC) == NULL) #endif { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Link to server lost, unable to reconnect"); @@ -996,7 +996,7 @@ static void php_mysql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) #ifndef MYSQL_USE_MYSQLND mysql->conn = mysql_init(NULL); #else - mysql->conn = mysql_init(persistent); + mysql->conn = mysqlnd_init(MYSQLND_CLIENT_KNOWS_RSET_COPY_DATA, persistent); #endif if (!mysql->conn) { MySG(connect_error) = estrdup("OOM"); @@ -1013,7 +1013,7 @@ static void php_mysql_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) #ifndef MYSQL_USE_MYSQLND if (mysql_real_connect(mysql->conn, host, user, passwd, NULL, port, socket, client_flags)==NULL) #else - if (mysqlnd_connect(mysql->conn, host, user, passwd, passwd_len, NULL, 0, port, socket, client_flags TSRMLS_CC) == NULL) + if (mysqlnd_connect(mysql->conn, host, user, passwd, passwd_len, NULL, 0, port, socket, client_flags, MYSQLND_CLIENT_KNOWS_RSET_COPY_DATA TSRMLS_CC) == NULL) #endif { /* Populate connect error globals so that the error functions can read them */ diff --git a/ext/mysqli/mysqli.c b/ext/mysqli/mysqli.c index c819b976e..b23d7e313 100644 --- a/ext/mysqli/mysqli.c +++ b/ext/mysqli/mysqli.c @@ -721,6 +721,7 @@ PHP_MINIT_FUNCTION(mysqli) REGISTER_LONG_CONSTANT("MYSQLI_USE_RESULT", MYSQLI_USE_RESULT, CONST_CS | CONST_PERSISTENT); #if defined (MYSQLI_USE_MYSQLND) REGISTER_LONG_CONSTANT("MYSQLI_ASYNC", MYSQLI_ASYNC, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("MYSQLI_STORE_RESULT_COPY_DATA", MYSQLI_STORE_RESULT_COPY_DATA, CONST_CS | CONST_PERSISTENT); #endif /* for mysqli_fetch_assoc */ diff --git a/ext/mysqli/mysqli_api.c b/ext/mysqli/mysqli_api.c index 70bdfb3d5..309d9b9f0 100644 --- a/ext/mysqli/mysqli_api.c +++ b/ext/mysqli/mysqli_api.c @@ -1525,7 +1525,7 @@ void php_mysqli_init(INTERNAL_FUNCTION_PARAMETERS) We create always persistent, as if the user want to connecto to p:somehost, we can't convert the handle then */ - if (!(mysql->mysql = mysql_init(TRUE))) + if (!(mysql->mysql = mysqlnd_init(MYSQLND_CLIENT_KNOWS_RSET_COPY_DATA, TRUE))) #endif { efree(mysql); @@ -2605,7 +2605,7 @@ PHP_FUNCTION(mysqli_stmt_sqlstate) } /* }}} */ -/* {{{ proto object mysqli_store_result(object link) +/* {{{ proto object mysqli_store_result(object link [, flags]) Buffer result set on client */ PHP_FUNCTION(mysqli_store_result) { @@ -2613,13 +2613,19 @@ PHP_FUNCTION(mysqli_store_result) MYSQL_RES *result; zval *mysql_link; MYSQLI_RESOURCE *mysqli_resource; + long flags = 0; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) { + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O|l", &mysql_link, mysqli_link_class_entry, &flags) == FAILURE) { return; } MYSQLI_FETCH_RESOURCE_CONN(mysql, &mysql_link, MYSQLI_STATUS_VALID); - - if (!(result = mysql_store_result(mysql->mysql))) { +#if MYSQLI_USE_MYSQLND + result = flags & MYSQLI_STORE_RESULT_COPY_DATA? mysqlnd_store_result_ofs(mysql->mysql) : mysqlnd_store_result(mysql->mysql); +#else + result = mysql_store_result(mysql->mysql); +#endif + if (!result) { MYSQLI_REPORT_MYSQL_ERROR(mysql->mysql); RETURN_FALSE; } diff --git a/ext/mysqli/mysqli_fe.c b/ext/mysqli/mysqli_fe.c index 3d31b8183..e099fe719 100644 --- a/ext/mysqli/mysqli_fe.c +++ b/ext/mysqli/mysqli_fe.c @@ -142,6 +142,17 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_mysqli_rollback, 0, 0, 0) ZEND_ARG_INFO(0, name) ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_mysqli_store_result, 0, 0, 1) + MYSQLI_ZEND_ARG_OBJ_INFO_LINK() + ZEND_ARG_INFO(0, flags) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_store_result, 0, 0, 0) + ZEND_ARG_INFO(0, flags) +ZEND_END_ARG_INFO() + + ZEND_BEGIN_ARG_INFO_EX(arginfo_mysqli_change_user, 0, 0, 4) MYSQLI_ZEND_ARG_OBJ_INFO_LINK() ZEND_ARG_INFO(0, user) @@ -498,7 +509,7 @@ const zend_function_entry mysqli_functions[] = { PHP_FE(mysqli_sqlstate, arginfo_mysqli_only_link) PHP_FE(mysqli_ssl_set, arginfo_mysqli_ssl_set) PHP_FE(mysqli_stat, arginfo_mysqli_only_link) - PHP_FE(mysqli_store_result, arginfo_mysqli_only_link) + PHP_FE(mysqli_store_result, arginfo_mysqli_store_result) PHP_FE(mysqli_thread_id, arginfo_mysqli_only_link) PHP_FE(mysqli_thread_safe, arginfo_mysqli_no_params) PHP_FE(mysqli_use_result, arginfo_mysqli_only_link) @@ -568,7 +579,7 @@ const zend_function_entry mysqli_link_methods[] = { PHP_FALIAS(ssl_set, mysqli_ssl_set, arginfo_class_mysqli_ssl_set) PHP_FALIAS(stat, mysqli_stat, arginfo_mysqli_no_params) PHP_FALIAS(stmt_init, mysqli_stmt_init, arginfo_mysqli_no_params) - PHP_FALIAS(store_result, mysqli_store_result, arginfo_mysqli_no_params) + PHP_FALIAS(store_result, mysqli_store_result, arginfo_class_store_result) PHP_FALIAS(thread_safe, mysqli_thread_safe, arginfo_mysqli_no_params) PHP_FALIAS(use_result, mysqli_use_result, arginfo_mysqli_no_params) PHP_FALIAS(refresh,mysqli_refresh, arginfo_class_mysqli_refresh) diff --git a/ext/mysqli/mysqli_nonapi.c b/ext/mysqli/mysqli_nonapi.c index de9b3bf54..00cbcf204 100644 --- a/ext/mysqli/mysqli_nonapi.c +++ b/ext/mysqli/mysqli_nonapi.c @@ -217,7 +217,7 @@ void mysqli_common_connect(INTERNAL_FUNCTION_PARAMETERS, zend_bool is_real_conne #if !defined(MYSQLI_USE_MYSQLND) if (!(mysql->mysql = mysql_init(NULL))) { #else - if (!(mysql->mysql = mysqlnd_init(persistent))) { + if (!(mysql->mysql = mysqlnd_init(MYSQLND_CLIENT_KNOWS_RSET_COPY_DATA, persistent))) { #endif goto err; } @@ -240,7 +240,7 @@ void mysqli_common_connect(INTERNAL_FUNCTION_PARAMETERS, zend_bool is_real_conne if (mysql_real_connect(mysql->mysql, hostname, username, passwd, dbname, port, socket, flags) == NULL) #else if (mysqlnd_connect(mysql->mysql, hostname, username, passwd, passwd_len, dbname, dbname_len, - port, socket, flags TSRMLS_CC) == NULL) + port, socket, flags, MYSQLND_CLIENT_KNOWS_RSET_COPY_DATA TSRMLS_CC) == NULL) #endif { /* Save error messages - for mysqli_connect_error() & mysqli_connect_errno() */ @@ -575,7 +575,7 @@ PHP_FUNCTION(mysqli_query) php_error_docref(NULL TSRMLS_CC, E_WARNING, "Empty query"); RETURN_FALSE; } - if ((resultmode & ~MYSQLI_ASYNC) != MYSQLI_USE_RESULT && (resultmode & ~MYSQLI_ASYNC) != MYSQLI_STORE_RESULT) { + if ((resultmode & ~MYSQLI_ASYNC) != MYSQLI_USE_RESULT && (resultmode & ~(MYSQLI_ASYNC | MYSQLI_STORE_RESULT_COPY_DATA)) != MYSQLI_STORE_RESULT) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid value for resultmode"); RETURN_FALSE; } @@ -609,9 +609,14 @@ PHP_FUNCTION(mysqli_query) RETURN_TRUE; } - switch (resultmode) { + switch (resultmode & ~(MYSQLI_ASYNC | MYSQLI_STORE_RESULT_COPY_DATA)) { case MYSQLI_STORE_RESULT: - result = mysql_store_result(mysql->mysql); +#ifdef MYSQLI_USE_MYSQLND + if (resultmode & MYSQLI_STORE_RESULT_COPY_DATA) { + result = mysqlnd_store_result_ofs(mysql->mysql); + } else +#endif + result = mysql_store_result(mysql->mysql); break; case MYSQLI_USE_RESULT: result = mysql_use_result(mysql->mysql); diff --git a/ext/mysqli/mysqli_priv.h b/ext/mysqli/mysqli_priv.h index 190572b68..e28caebf9 100644 --- a/ext/mysqli/mysqli_priv.h +++ b/ext/mysqli/mysqli_priv.h @@ -114,9 +114,11 @@ PHP_MYSQLI_EXPORT(zend_object_value) mysqli_objects_new(zend_class_entry * TSRML #define MYSQLI_USE_RESULT 1 #ifdef MYSQLI_USE_MYSQLND #define MYSQLI_ASYNC 8 +#define MYSQLI_STORE_RESULT_COPY_DATA 16 #else /* libmysql */ #define MYSQLI_ASYNC 0 +#define MYSQLI_STORE_RESULT_OFS 0 #endif /* for mysqli_fetch_assoc */ diff --git a/ext/mysqli/tests/bug66043.phpt b/ext/mysqli/tests/bug66043.phpt index d0e8b1c3d..52e42b617 100644 --- a/ext/mysqli/tests/bug66043.phpt +++ b/ext/mysqli/tests/bug66043.phpt @@ -12,7 +12,9 @@ require_once('skipifconnectfailure.inc'); --FILE-- <?php require 'connect.inc'; -$db = new mysqli($host, $user, $passwd, 'mysql'); +if (!$db = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) { + printf("[001] Connect failed, [%d] %s\n", mysqli_connect_errno(), mysqli_connect_error()); +} $stmt = $db->stmt_init(); $stmt->prepare("SELECT User FROM user WHERE password=\"\""); diff --git a/ext/mysqli/tests/bug66762.phpt b/ext/mysqli/tests/bug66762.phpt index 2b8a92c7f..cf1309e5a 100644 --- a/ext/mysqli/tests/bug66762.phpt +++ b/ext/mysqli/tests/bug66762.phpt @@ -9,7 +9,9 @@ require_once('skipifconnectfailure.inc'); <?php require_once("connect.inc"); - $mysqli = new mysqli($host, $user, $passwd, $db); + if (!$mysqli = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) { + printf("[001] Connect failed, [%d] %s\n", mysqli_connect_errno(), mysqli_connect_error()); + } $read_stmt = $mysqli->prepare("SELECT 1"); @@ -17,7 +19,6 @@ require_once('skipifconnectfailure.inc'); unset($mysqli); var_dump($read_stmt->bind_result($data)); - ?> done! --EXPECT-- diff --git a/ext/mysqli/tests/mysqli_change_user.phpt b/ext/mysqli/tests/mysqli_change_user.phpt index bfea423c9..7a4530f0d 100644 --- a/ext/mysqli/tests/mysqli_change_user.phpt +++ b/ext/mysqli/tests/mysqli_change_user.phpt @@ -32,79 +32,91 @@ require_once('skipifconnectfailure.inc'); printf("[006] Cannot connect to the server using host=%s, user=%s, passwd=***, dbname=%s, port=%s, socket=%s\n", $host, $user, $db, $port, $socket); - if (false !== ($tmp = mysqli_change_user($link, $user . '_unknown_really', $passwd . 'non_empty', $db))) + if (false !== ($tmp = @mysqli_change_user($link, $user . '_unknown_really', $passwd . 'non_empty', $db))) printf("[007] Expecting false, got %s/%s\n", gettype($tmp), $tmp); - if (false !== ($tmp = mysqli_change_user($link, $user, $passwd . '_unknown_really', $db))) - printf("[008] Expecting false, got %s/%s\n", gettype($tmp), $tmp); + if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) + printf("[008] Cannot connect to the server using host=%s, user=%s, passwd=***, dbname=%s, port=%s, socket=%s\n", + $host, $user, $db, $port, $socket); - if (false !== ($tmp = mysqli_change_user($link, $user, $passwd, $db . '_unknown_really'))) + if (false !== ($tmp = @mysqli_change_user($link, $user, $passwd . '_unknown_really', $db))) printf("[009] Expecting false, got %s/%s\n", gettype($tmp), $tmp); - if (!mysqli_query($link, 'SET @mysqli_change_user_test_var=1')) - printf("[010] Failed to set test variable: [%d] %s\n", mysqli_errno($link), mysqli_error($link)); - - if (!$res = mysqli_query($link, 'SELECT @mysqli_change_user_test_var AS test_var')) - printf("[011] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); - $tmp = mysqli_fetch_assoc($res); - mysqli_free_result($res); - if (1 != $tmp['test_var']) - printf("[012] Cannot set test variable\n"); - - if (true !== ($tmp = mysqli_change_user($link, $user, $passwd, $db))) - printf("[013] Expecting true, got %s/%s\n", gettype($tmp), $tmp); - - if (!$res = mysqli_query($link, 'SELECT database() AS dbname, user() AS user')) - printf("[014] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); - $tmp = mysqli_fetch_assoc($res); - mysqli_free_result($res); - - if (substr($tmp['user'], 0, strlen($user)) !== $user) - printf("[015] Expecting user %s, got user() %s\n", $user, $tmp['user']); - if ($tmp['dbname'] != $db) - printf("[016] Expecting database %s, got database() %s\n", $db, $tmp['dbname']); + if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) + printf("[010] Cannot connect to the server using host=%s, user=%s, passwd=***, dbname=%s, port=%s, socket=%s\n", + $host, $user, $db, $port, $socket); - if (!$res = mysqli_query($link, 'SELECT @mysqli_change_user_test_var AS test_var')) - printf("[017] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); - $tmp = mysqli_fetch_assoc($res); - mysqli_free_result($res); - if (NULL !== $tmp['test_var']) - printf("[019] Test variable is still set!\n"); + if (false !== ($tmp = @mysqli_change_user($link, $user, $passwd, $db . '_unknown_really'))) + printf("[011] Expecting false, got %s/%s\n", gettype($tmp), $tmp); mysqli_close($link); if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) { - printf("[020] Cannot connect to the server using host=%s, user=%s, passwd=***, dbname=%s, port=%s, socket=%s\n", + printf("[012] Cannot connect to the server using host=%s, user=%s, passwd=***, dbname=%s, port=%s, socket=%s\n", $host, $user, $db, $port, $socket); } - if (false !== ($tmp = mysqli_change_user($link, str_repeat('user', 16384), str_repeat('pass', 16384), str_repeat('dbase', 16384)))) - printf("[021] Expecting false, got %s/%s\n", gettype($tmp), $tmp); + if (false !== ($tmp = @mysqli_change_user($link, str_repeat('user', 16384), str_repeat('pass', 16384), str_repeat('dbase', 16384)))) + printf("[013] Expecting false, got %s/%s\n", gettype($tmp), $tmp); 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", + printf("[014] 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); + printf("[015] 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)); + printf("[016] [%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']); + printf("[017] Expecting database '', got database() '%s'\n", $tmp['dbname']); mysqli_close($link); if (NULL !== ($tmp = @mysqli_change_user($link, $user, $passwd, $db))) - printf("[026] Expecting NULL, got %s/%s\n", gettype($tmp), $tmp); + printf("[018] Expecting NULL, got %s/%s\n", gettype($tmp), $tmp); + + if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) + printf("[019] Cannot connect to the server using host=%s, user=%s, passwd=***, dbname=%s, port=%s, socket=%s\n", + $host, $user, $db, $port, $socket); + + if (!mysqli_query($link, 'SET @mysqli_change_user_test_var=1')) + printf("[020] Failed to set test variable: [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + + if (!$res = mysqli_query($link, 'SELECT @mysqli_change_user_test_var AS test_var')) + printf("[021] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + $tmp = mysqli_fetch_assoc($res); + mysqli_free_result($res); + if (1 != $tmp['test_var']) + printf("[022] Cannot set test variable\n"); + + if (true !== ($tmp = mysqli_change_user($link, $user, $passwd, $db))) + 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 (substr($tmp['user'], 0, strlen($user)) !== $user) + printf("[025] Expecting user %s, got user() %s\n", $user, $tmp['user']); + if ($tmp['dbname'] != $db) + printf("[026] Expecting database %s, got database() %s\n", $db, $tmp['dbname']); + + if (!$res = mysqli_query($link, 'SELECT @mysqli_change_user_test_var AS test_var')) + printf("[027] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + $tmp = mysqli_fetch_assoc($res); + mysqli_free_result($res); + if (NULL !== $tmp['test_var']) + printf("[028] Test variable is still set!\n"); print "done!"; ?> diff --git a/ext/mysqli/tests/mysqli_change_user_new.phpt b/ext/mysqli/tests/mysqli_change_user_new.phpt new file mode 100644 index 000000000..a87afa84a --- /dev/null +++ b/ext/mysqli/tests/mysqli_change_user_new.phpt @@ -0,0 +1,44 @@ +--TEST-- +mysqli_change_user(), MySQL 5.6+ +--SKIPIF-- +<?php +require_once('skipif.inc'); +require_once('skipifemb.inc'); +require_once('skipifconnectfailure.inc'); + +if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) + die(sprintf("SKIP Cannot connect to the server using host=%s, user=%s, passwd=***, dbname=%s, port=%s, socket=%s\n", + $host, $user, $db, $port, $socket)); + +if (mysqli_get_server_version($link) < 50600) + die("SKIP For MySQL >= 5.6.0"); +?> +--FILE-- +<?php + require_once("connect.inc"); + + $tmp = NULL; + $link = NULL; + + 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); + + /* Pre 5.6: link remains useable */ + if (false !== ($tmp = @mysqli_change_user($link, $user . '_unknown_really', $passwd . 'non_empty', $db))) + printf("[002] Expecting false, got %s/%s\n", gettype($tmp), $tmp); + + if (!$res = mysqli_query($link, 'SELECT 1 AS _one')) + printf("[003] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + + var_dump($res->fetch_assoc()); + + print "done!"; +?> +--EXPECTF-- +Warning: mysqli_query(): MySQL server has gone away in %s on line %d + +Warning: mysqli_query(): Error reading result set's header in %s on line %d +[003] [2006] MySQL server has gone away + +Fatal error: Call to a member function fetch_assoc() on a non-object in %s on line %d
\ No newline at end of file diff --git a/ext/mysqli/tests/mysqli_change_user_old.phpt b/ext/mysqli/tests/mysqli_change_user_old.phpt new file mode 100644 index 000000000..ddb49cd18 --- /dev/null +++ b/ext/mysqli/tests/mysqli_change_user_old.phpt @@ -0,0 +1,119 @@ +--TEST-- +mysqli_change_user(), MySQL < 5.6 +--SKIPIF-- +<?php +require_once('skipif.inc'); +require_once('skipifemb.inc'); +require_once('skipifconnectfailure.inc'); + +if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) + die(sprintf("SKIP Cannot connect to the server using host=%s, user=%s, passwd=***, dbname=%s, port=%s, socket=%s\n", + $host, $user, $db, $port, $socket)); + +if (mysqli_get_server_version($link) >= 50600) + die("SKIP For MySQL < 5.6.0"); +?> +--FILE-- +<?php + require_once("connect.inc"); + + $tmp = NULL; + $link = NULL; + + if (!is_null($tmp = @mysqli_change_user())) + printf("[001] Expecting NULL, got %s/%s\n", gettype($tmp), $tmp); + + if (!is_null($tmp = @mysqli_change_user($link))) + printf("[002] Expecting NULL, got %s/%s\n", gettype($tmp), $tmp); + + if (!is_null($tmp = @mysqli_change_user($link, $link))) + printf("[003] Expecting NULL, got %s/%s\n", gettype($tmp), $tmp); + + if (!is_null($tmp = @mysqli_change_user($link, $link, $link))) + printf("[004] Expecting NULL, got %s/%s\n", gettype($tmp), $tmp); + + if (!is_null($tmp = @mysqli_change_user($link, $link, $link, $link, $link))) + printf("[005] Expecting NULL, got %s/%s\n", gettype($tmp), $tmp); + + if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) + printf("[006] Cannot connect to the server using host=%s, user=%s, passwd=***, dbname=%s, port=%s, socket=%s\n", + $host, $user, $db, $port, $socket); + + if (false !== ($tmp = mysqli_change_user($link, $user . '_unknown_really', $passwd . 'non_empty', $db))) + printf("[007] Expecting false, got %s/%s\n", gettype($tmp), $tmp); + + if (false !== ($tmp = mysqli_change_user($link, $user, $passwd . '_unknown_really', $db))) + printf("[008] Expecting false, got %s/%s\n", gettype($tmp), $tmp); + + if (false !== ($tmp = mysqli_change_user($link, $user, $passwd, $db . '_unknown_really'))) + printf("[009] Expecting false, got %s/%s\n", gettype($tmp), $tmp); + + if (!mysqli_query($link, 'SET @mysqli_change_user_test_var=1')) + printf("[010] Failed to set test variable: [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + + if (!$res = mysqli_query($link, 'SELECT @mysqli_change_user_test_var AS test_var')) + printf("[011] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + $tmp = mysqli_fetch_assoc($res); + mysqli_free_result($res); + if (1 != $tmp['test_var']) + printf("[012] Cannot set test variable\n"); + + if (true !== ($tmp = mysqli_change_user($link, $user, $passwd, $db))) + printf("[013] Expecting true, got %s/%s\n", gettype($tmp), $tmp); + + if (!$res = mysqli_query($link, 'SELECT database() AS dbname, user() AS user')) + printf("[014] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + $tmp = mysqli_fetch_assoc($res); + mysqli_free_result($res); + + if (substr($tmp['user'], 0, strlen($user)) !== $user) + printf("[015] Expecting user %s, got user() %s\n", $user, $tmp['user']); + if ($tmp['dbname'] != $db) + printf("[016] Expecting database %s, got database() %s\n", $db, $tmp['dbname']); + + if (!$res = mysqli_query($link, 'SELECT @mysqli_change_user_test_var AS test_var')) + printf("[017] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + $tmp = mysqli_fetch_assoc($res); + mysqli_free_result($res); + if (NULL !== $tmp['test_var']) + printf("[019] Test variable is still set!\n"); + + mysqli_close($link); + + if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) { + printf("[020] Cannot connect to the server using host=%s, user=%s, passwd=***, dbname=%s, port=%s, socket=%s\n", + $host, $user, $db, $port, $socket); + } + + if (false !== ($tmp = mysqli_change_user($link, str_repeat('user', 16384), str_repeat('pass', 16384), str_repeat('dbase', 16384)))) + printf("[021] Expecting false, got %s/%s\n", gettype($tmp), $tmp); + + 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("[026] Expecting NULL, got %s/%s\n", gettype($tmp), $tmp); + + print "done!"; +?> +--EXPECTF-- +done!
\ No newline at end of file diff --git a/ext/mysqli/tests/mysqli_change_user_oo.phpt b/ext/mysqli/tests/mysqli_change_user_oo.phpt index 61444ae23..7ed2d0893 100644 --- a/ext/mysqli/tests/mysqli_change_user_oo.phpt +++ b/ext/mysqli/tests/mysqli_change_user_oo.phpt @@ -10,6 +10,9 @@ require_once('table.inc'); if (!$IS_MYSQLND && (mysqli_get_server_version($link) < 50118 && mysqli_get_server_version($link) > 50100)) { die("skip Your MySQL Server version has a known bug that will cause a crash"); } + +if (mysqli_get_server_version($link) >= 50600) + die("SKIP For MySQL < 5.6.0"); ?> --FILE-- <?php diff --git a/ext/mysqli/tests/mysqli_class_mysqli_reflection.phpt b/ext/mysqli/tests/mysqli_class_mysqli_reflection.phpt index 63ec7ca3c..8f6c24900 100644 --- a/ext/mysqli/tests/mysqli_class_mysqli_reflection.phpt +++ b/ext/mysqli/tests/mysqli_class_mysqli_reflection.phpt @@ -1140,9 +1140,16 @@ isInternal: yes isUserDefined: no returnsReference: no Modifiers: 256 -Number of Parameters: 0 +Number of Parameters: 1 Number of Required Parameters: 0 +Inspecting parameter 'flags' of method 'store_result' +isArray: no +allowsNull: no +isPassedByReference: no +isOptional: yes +isDefaultValueAvailable: no + Inspecting method 'thread_safe' isFinal: no isAbstract: no diff --git a/ext/mysqli/tests/mysqli_constants.phpt b/ext/mysqli/tests/mysqli_constants.phpt index 7c6dacd39..bed9d5341 100644 --- a/ext/mysqli/tests/mysqli_constants.phpt +++ b/ext/mysqli/tests/mysqli_constants.phpt @@ -108,6 +108,10 @@ require_once('skipifconnectfailure.inc'); $expected_constants['MYSQLI_OPT_INT_AND_FLOAT_NATIVE'] = true; } + if ($IS_MYSQLND && defined('MYSQLI_STORE_RESULT_COPY_DATA')) { + $expected_constants['MYSQLI_STORE_RESULT_COPY_DATA'] = true; + } + if ($IS_MYSQLND || defined('MYSQLI_REFRESH_BACKUP_LOG')) { $expected_constants['MYSQLI_REFRESH_BACKUP_LOG'] = true; } diff --git a/ext/mysqli/tests/mysqli_fetch_all.phpt b/ext/mysqli/tests/mysqli_fetch_all.phpt index 63b6ad284..854b8160f 100644 --- a/ext/mysqli/tests/mysqli_fetch_all.phpt +++ b/ext/mysqli/tests/mysqli_fetch_all.phpt @@ -299,6 +299,26 @@ if (!function_exists('mysqli_fetch_all')) if (null !== ($tmp = mysqli_fetch_array($res, MYSQLI_ASSOC))) printf("[015] Expecting NULL, got %s/%s\n", gettype($tmp), $tmp); + if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) { + printf("[016] Cannot connect to the server using host=%s, user=%s, passwd=***, dbname=%s, port=%s, socket=%s\n", + $host, $user, $db, $port, $socket); + } + + if (!$res = mysqli_real_query($link, "SELECT 1 AS _one")) + printf("[017] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + + /* on mysqlnd level this would not be allowed */ + if (!is_object($res = mysqli_use_result($link))) + printf("[018] Expecting object, got %s/%s. [%d] %s\n", + gettype($res), $res, mysqli_errno($link), mysqli_error($link)); + + $rows = mysqli_fetch_all($res, MYSQLI_ASSOC); + if (!is_array($rows) || (count($rows) > 1) || !isset($rows[0]['_one']) || ($rows[0]['_one'] != 1)) { + printf("[019] Results seem wrong, dumping\n"); + var_dump($rows); + } + + print "done!"; ?> --CLEAN-- diff --git a/ext/mysqli/tests/mysqli_fetch_lengths.phpt b/ext/mysqli/tests/mysqli_fetch_lengths.phpt index 1abc61170..6d0b698ee 100644 --- a/ext/mysqli/tests/mysqli_fetch_lengths.phpt +++ b/ext/mysqli/tests/mysqli_fetch_lengths.phpt @@ -10,8 +10,10 @@ require_once('skipifconnectfailure.inc'); <?php require_once("connect.inc"); - if (!$mysqli = new mysqli($host, $user, $passwd, $db, $port, $socket)) - printf("[001] Cannot connect\n"); + if (!$mysqli = 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 (!is_null($tmp = @mysqli_fetch_lengths())) printf("[001] Expecting NULL, got %s/%s\n", gettype($tmp), $tmp); diff --git a/ext/mysqli/tests/mysqli_poll.phpt b/ext/mysqli/tests/mysqli_poll.phpt index dd4f9b971..8a49ba3f8 100644 --- a/ext/mysqli/tests/mysqli_poll.phpt +++ b/ext/mysqli/tests/mysqli_poll.phpt @@ -52,6 +52,13 @@ if (!$IS_MYSQLND) if (0 !== ($tmp = (mysqli_poll($read, $error, $reject, 0, 1)))) printf("[009] Expecting int/0 got %s/%s\n", gettype($tmp), var_export($tmp, true)); + $read = $error = $reject = array($link); + if (false !== ($tmp = (mysqli_poll($read, $error, $reject, -1, 1)))) + printf("[010] Expecting false got %s/%s\n", gettype($tmp), var_export($tmp, true)); + + $read = $error = $reject = array($link); + if (false !== ($tmp = (mysqli_poll($read, $error, $reject, 0, -1)))) + printf("[011] Expecting false got %s/%s\n", gettype($tmp), var_export($tmp, true)); function poll_async($offset, $link, $links, $errors, $reject, $exp_ready, $use_oo_syntax) { @@ -90,14 +97,14 @@ if (!$IS_MYSQLND) $links = array($link); $errors = array($link); $reject = array($link); - poll_async(10, $link, $links, $errors, $reject, 0, false); + poll_async(12, $link, $links, $errors, $reject, 0, false); mysqli_close($link); $link = get_connection(); $links = array($link); $errors = array($link); $reject = array($link); - poll_async(11, $link, $links, $errors, $reject, 0, true); + poll_async(13, $link, $links, $errors, $reject, 0, true); mysqli_close($link); // Connections on which no query has been send - 2 @@ -106,7 +113,7 @@ if (!$IS_MYSQLND) $links = array($link, $link); $errors = array($link, $link); $reject = array(); - poll_async(12, $link, $links, $errors, $reject, 0, false); + poll_async(14, $link, $links, $errors, $reject, 0, false); // Connections on which no query has been send - 3 // Difference: pass two connections @@ -114,7 +121,7 @@ if (!$IS_MYSQLND) $links = array($link, get_connection()); $errors = array($link, $link); $reject = array(); - poll_async(13, $link, $links, $errors, $reject, 0, false); + poll_async(15, $link, $links, $errors, $reject, 0, false); // Reference mess... $link = get_connection(); @@ -122,16 +129,20 @@ if (!$IS_MYSQLND) $errors = array($link); $ref_errors =& $errors; $reject = array(); - poll_async(14, $link, $links, $ref_errors, $reject, 0, false); + poll_async(16, $link, $links, $ref_errors, $reject, 0, false); print "done!"; ?> --EXPECTF-- -[010 + 6] Rejecting thread %d: 0/ -[011 + 6] Rejecting thread %d: 0/ -[012 + 6] Rejecting thread %d: 0/ + +Warning: mysqli_poll(): Negative values passed for sec and/or usec in %s on line %d + +Warning: mysqli_poll(): Negative values passed for sec and/or usec in %s on line %d [012 + 6] Rejecting thread %d: 0/ [013 + 6] Rejecting thread %d: 0/ -[013 + 6] Rejecting thread %d: 0/ [014 + 6] Rejecting thread %d: 0/ -done! +[014 + 6] Rejecting thread %d: 0/ +[015 + 6] Rejecting thread %d: 0/ +[015 + 6] Rejecting thread %d: 0/ +[016 + 6] Rejecting thread %d: 0/ +done!
\ No newline at end of file diff --git a/ext/mysqli/tests/mysqli_poll_kill.phpt b/ext/mysqli/tests/mysqli_poll_kill.phpt index b068d64e8..c69a25111 100644 --- a/ext/mysqli/tests/mysqli_poll_kill.phpt +++ b/ext/mysqli/tests/mysqli_poll_kill.phpt @@ -90,6 +90,7 @@ if (!$IS_MYSQLND) // Yes, 1 - fetch OK packet of kill! $processed = 0; + $begin = microtime(true); do { $links = array($link, $link); $errors = array($link, $link); @@ -106,9 +107,14 @@ if (!$IS_MYSQLND) break; } + if (FALSE === $ready) { + printf("[013] MySQLi indicates some error\n"); + break; + } + if (!empty($reject)) { foreach ($reject as $mysqli) { - printf("[013] Rejecting thread %d: %s/%s\n", + printf("[014] Rejecting thread %d: %s/%s\n", mysqli_thread_id($mysqli), mysqli_errno($mysqli), mysqli_error($mysqli)); @@ -121,11 +127,16 @@ if (!$IS_MYSQLND) printf("Fetching from thread %d...\n", mysqli_thread_id($mysqli)); var_dump(mysqli_fetch_assoc($res)); } else if (mysqli_errno($mysqli) > 0) { - printf("[014] %d/%s\n", mysqli_errno($mysqli), mysqli_error($mysqli)); + printf("[015] %d/%s\n", mysqli_errno($mysqli), mysqli_error($mysqli)); } $processed++; } + if ((microtime(true) - $begin) > 5) { + printf("[016] Pulling the emergency break after 5s, something is wrong...\n"); + break; + } + } while ($processed < 2); @@ -137,17 +148,17 @@ if (!$IS_MYSQLND) // Sleep 0.1s to ensure the KILL gets recognized usleep(100000); if (false !== ($tmp = mysqli_query($link, "SELECT 1 AS 'processed before killed'", MYSQLI_ASYNC | MYSQLI_USE_RESULT))) - printf("[015] Expecting boolean/false got %s/%s\n", gettype($tmp), var_export($tmp, true)); + printf("[017] Expecting boolean/false got %s/%s\n", gettype($tmp), var_export($tmp, true)); $links = array($link); $errors = array($link); $reject = array($link); if (0 !== ($tmp = (mysqli_poll($links, $errors, $reject, 0, 10000)))) - printf("[016] Expecting int/0 got %s/%s\n", gettype($tmp), var_export($tmp, true)); + printf("[018] Expecting int/0 got %s/%s\n", gettype($tmp), var_export($tmp, true)); if (!is_array($links) || empty($links)) - printf("[017] Expecting non-empty array got %s/%s\n", gettype($links), var_export($links, true)); + printf("[019] Expecting non-empty array got %s/%s\n", gettype($links), var_export($links, true)); else foreach ($links as $link) { if (is_object($res = mysqli_reap_async_query($link))) { @@ -156,16 +167,16 @@ if (!$IS_MYSQLND) mysqli_free_result($res); } else if ($link->errno > 0) { // But you are supposed to handle the error the way its shown here! - printf("[018] Error: %d/%s\n", $link->errno, $link->error); + printf("[020] Error: %d/%s\n", $link->errno, $link->error); } } // None of these will indicate an error, check errno on the list of returned connections! if (!is_array($errors) || !empty($errors)) - printf("[019] Expecting non-empty array got %s/%s\n", gettype($errors), var_export($errors, true)); + printf("[021] Expecting non-empty array got %s/%s\n", gettype($errors), var_export($errors, true)); if (!is_array($reject) || !empty($reject)) - printf("[020] Expecting empty array got %s/%s\n", gettype($reject), var_export($reject, true)); + printf("[021] Expecting empty array got %s/%s\n", gettype($reject), var_export($reject, true)); mysqli_close($link); diff --git a/ext/mysqli/tests/mysqli_poll_mixing_insert_select.phpt b/ext/mysqli/tests/mysqli_poll_mixing_insert_select.phpt index 9c02cf976..9068f6f70 100644 --- a/ext/mysqli/tests/mysqli_poll_mixing_insert_select.phpt +++ b/ext/mysqli/tests/mysqli_poll_mixing_insert_select.phpt @@ -80,13 +80,17 @@ if (!$IS_MYSQLND) if (0 == count($poll_links)) break; - if (0 == ($num_ready = mysqli_poll($poll_links, $poll_errors, $poll_reject, 0, 200000))) + if (0 === ($num_ready = mysqli_poll($poll_links, $poll_errors, $poll_reject, 0, 200000))) continue; if (!empty($poll_errors)) { die(var_dump($poll_errors)); } + if (FALSE === $num_ready) { + die("Some mysqli indicated error"); + } + foreach ($poll_links as $link) { $thread_id = mysqli_thread_id($link); $links[$thread_id]['processed'] = true; diff --git a/ext/mysqli/tests/mysqli_reap_async_query.phpt b/ext/mysqli/tests/mysqli_reap_async_query.phpt new file mode 100644 index 000000000..e3858e617 --- /dev/null +++ b/ext/mysqli/tests/mysqli_reap_async_query.phpt @@ -0,0 +1,97 @@ +--TEST-- +mysqli_reap_async_query() +--SKIPIF-- +<?php +require_once('skipif.inc'); +require_once('skipifemb.inc'); +require_once('connect.inc'); +require_once('skipifconnectfailure.inc'); + +if (!$IS_MYSQLND) + die("skip mysqlnd only feature, compile PHP using --with-mysqli=mysqlnd"); +?> +--FILE-- +<?php + require_once('connect.inc'); + + function get_connection() { + global $host, $user, $passwd, $db, $port, $socket; + + if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) + printf("[001] [%d] %s\n", mysqli_connect_errno(), mysqli_connect_error()); + return $link; + } + + if (!$link = get_connection()) + printf("[001] [%d] %s\n", mysqli_connect_errno(), mysqli_connect_error()); + + if (NULL !== ($tmp = @mysqli_reap_async_query())) + printf("[002] Expecting NULL got %s\n", var_export($tmp, true)); + + $l = array($link); + if (NULL !== ($tmp = @mysqli_reap_async_query($l))) + printf("[003] Expecting NULL got %s\n", var_export($tmp, true)); + + if (NULL !== ($tmp = @mysqli_reap_async_query($link, $link))) + printf("[004] Expecting NULL got %s\n", var_export($tmp, true)); + + + function poll_async($offset, $link, $links, $errors, $reject, $exp_ready, $use_oo_syntax) { + + if ($exp_ready !== ($tmp = mysqli_poll($links, $errors, $reject, 0, 1000))) + printf("[%03d + 1] There should be %d links ready to read from, %d ready\n", + $offset, $exp_ready, $tmp); + + foreach ($links as $mysqli) { + if ($use_oo_syntax) { + $res = $mysqli->reap_async_query(); + } else { + $res = mysqli_reap_async_query($mysqli); + } + if (is_object($res)) { + printf("[%03d + 2] %s\n", $offset, var_export($res->fetch_assoc(), true)); + } else if (mysqli_errno($mysqli) > 0) { + printf("[%03d + 3] Error indicated through links array: %d/%s", + $offset, mysqli_errno($mysqli), mysqli_error($mysqli)); + } else { + printf("[%03d + 4] Cannot fetch and no error set - non resultset query (no SELECT)!\n", $offset); + } + } + + foreach ($errors as $mysqli) + printf("[%03d + 5] Error on %d: %d/%s\n", + $offset, mysqli_thread_id($mysqli), mysqli_errno($mysqli), mysqli_error($mysqli)); + + foreach ($reject as $mysqli) + printf("[%03d + 6] Rejecting thread %d: %d/%s\n", + $offset, mysqli_thread_id($mysqli), mysqli_errno($mysqli), mysqli_error($mysqli)); + + } + + // Connections on which no query has been send - 1 + $link = get_connection(); + $link->query("SELECT 1 AS _one", MYSQLI_ASYNC | MYSQLI_STORE_RESULT); + $links = array($link); + $errors = array($link); + $reject = array($link); + poll_async(12, $link, $links, $errors, $reject, 1, false); + mysqli_close($link); + + $link = get_connection(); + $link->query("SELECT 2 AS _two", MYSQLI_ASYNC | MYSQLI_USE_RESULT); + $links = array($link); + $errors = array($link); + $reject = array($link); + poll_async(13, $link, $links, $errors, $reject, 1, true); + mysqli_close($link); + + print "done!"; +?> +--EXPECTF-- +[012 + 2] array ( + '_one' => '1', +) +[013 + 2] array ( + '_two' => '2', +) +done! diff --git a/ext/mysqli/tests/mysqli_release_savepoint.phpt b/ext/mysqli/tests/mysqli_release_savepoint.phpt new file mode 100644 index 000000000..ba4c56438 --- /dev/null +++ b/ext/mysqli/tests/mysqli_release_savepoint.phpt @@ -0,0 +1,84 @@ +--TEST-- +mysqli_release_savepoint() +--SKIPIF-- +<?php +require_once('skipif.inc'); +require_once('skipifemb.inc'); +require_once('skipifconnectfailure.inc'); + +require_once('connect.inc'); +if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) + die(sprintf("Cannot connect, [%d] %s", mysqli_connect_errno(), mysqli_connect_error())); + +if (!have_innodb($link)) + die(sprintf("Needs InnoDB support, [%d] %s", $link->errno, $link->error)); +?> +--FILE-- +<?php + require_once("connect.inc"); + /* {{{ proto bool mysqli_release_savepoint(object link, string name) */ + $tmp = NULL; + $link = NULL; + + if (!is_null($tmp = @mysqli_release_savepoint())) + printf("[001] Expecting NULL, got %s/%s\n", gettype($tmp), $tmp); + + if (!is_null($tmp = @mysqli_release_savepoint($link))) + printf("[002] Expecting NULL, got %s/%s\n", gettype($tmp), $tmp); + + if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) + printf("[003] Cannot connect to the server using host=%s, user=%s, passwd=***, dbname=%s, port=%s, socket=%s\n", + $host, $user, $db, $port, $socket); + + $name = array(); + if (!is_null($tmp = @mysqli_release_savepoint($link, $name))) + printf("[004] Expecting NULL, got %s/%s\n", gettype($tmp), $tmp); + + if (!is_null($tmp = @mysqli_release_savepoint($link, 'foo', $link))) + printf("[005] Expecting NULL, got %s/%s\n", gettype($tmp), $tmp); + + if (false !== ($tmp = mysqli_release_savepoint($link, ''))) + printf("[006] Expecting false, got %s/%s\n", gettype($tmp), $tmp); + + if (!mysqli_query($link, 'DROP TABLE IF EXISTS test')) + printf("[007] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + + if (!mysqli_query($link, 'CREATE TABLE test(id INT) ENGINE = InnoDB')) + printf("[008] Cannot create test table, [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + + if (true !== ($tmp = mysqli_autocommit($link, false))) + printf("[009] Cannot turn off autocommit, expecting true, got %s/%s\n", gettype($tmp), $tmp); + + /* note that there is no savepoint my... */ + if (true !== ($tmp = mysqli_release_savepoint($link, 'my'))) + printf("[010] Got %s - [%d] %s\n", var_dump($tmp, true), mysqli_errno($link), mysqli_error($link)); + + if (!mysqli_query($link, 'INSERT INTO test(id) VALUES (1)')) + printf("[011] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + + $tmp = mysqli_commit($link); + if ($tmp !== true) + printf("[012] Expecting boolean/true, got %s/%s\n", gettype($tmp), $tmp); + + if (true !== ($tmp = mysqli_savepoint($link, 'my'))) + printf("[013] Got %s - [%d] %s\n", var_dump($tmp, true), mysqli_errno($link), mysqli_error($link)); + + $res = mysqli_query($link, "SELECT * FROM test"); + var_dump($res->fetch_assoc()); + + if (true !== ($tmp = mysqli_release_savepoint($link, 'my'))) + printf("[014] Got %s - [%d] %s\n", var_dump($tmp, true), mysqli_errno($link), mysqli_error($link)); + + print "done!"; +?> +--CLEAN-- +<?php + require_once("clean_table.inc"); +?> +--EXPECTF-- +Warning: mysqli_release_savepoint(): Savepoint name cannot be empty in %s on line %d +array(1) { + ["id"]=> + string(1) "1" +} +done!
\ No newline at end of file diff --git a/ext/mysqli/tests/mysqli_report.phpt b/ext/mysqli/tests/mysqli_report.phpt index f5d77e38b..4d2d3553d 100644 --- a/ext/mysqli/tests/mysqli_report.phpt +++ b/ext/mysqli/tests/mysqli_report.phpt @@ -43,8 +43,6 @@ require_once('skipifconnectfailure.inc'); mysqli_multi_query($link, "BAR; FOO;"); mysqli_query($link, "FOO"); - /* This might work if you accept anonymous users in your setup */ - mysqli_change_user($link, "0123456789-10-456789-20-456789-30-456789-40-456789-50-456789-60-456789-70-456789-80-456789-90-456789", "password", $db); mysqli_kill($link, -1); // mysqli_ping() cannot be tested, because one would need to cause an error inside the C function to test it @@ -61,7 +59,6 @@ require_once('skipifconnectfailure.inc'); mysqli_multi_query($link, "BAR; FOO;"); mysqli_query($link, "FOO"); - mysqli_change_user($link, "This might work if you accept anonymous users in your setup", "password", $db); mysqli_kill($link, -1); mysqli_prepare($link, "FOO"); mysqli_real_query($link, "FOO"); @@ -291,8 +288,6 @@ Warning: mysqli_multi_query(): (%d/%d): You have an error in your SQL syntax; ch Warning: mysqli_query(): (%d/%d): 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 'FOO' at line 1 in %s on line %d -Warning: mysqli_change_user(): (%d/%d): Access denied for user '%s'@'%s' (using password: %s) in %s on line %d - Warning: mysqli_kill(): processid should have positive value in %s on line %d Warning: mysqli_prepare(): (%d/%d): 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 'FOO' at line 1 in %s on line %d diff --git a/ext/mysqli/tests/mysqli_report_new.phpt b/ext/mysqli/tests/mysqli_report_new.phpt new file mode 100644 index 000000000..5cf5ca8e3 --- /dev/null +++ b/ext/mysqli/tests/mysqli_report_new.phpt @@ -0,0 +1,50 @@ +--TEST-- +mysqli_report(), change user, MySQL 5.6+ +--SKIPIF-- +<?php +require_once('skipif.inc'); +require_once('skipifemb.inc'); +require_once('skipifconnectfailure.inc'); + +if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) + die(sprintf("SKIP Cannot connect to the server using host=%s, user=%s, passwd=***, dbname=%s, port=%s, socket=%s\n", + $host, $user, $db, $port, $socket)); + +if (mysqli_get_server_version($link) < 50600) + die("SKIP For MySQL >= 5.6.0"); + +?> +--FILE-- +<?php + require_once("connect.inc"); + + $tmp = NULL; + $link = NULL; + + require('table.inc'); + + /* + Internal macro MYSQL_REPORT_ERROR + */ + mysqli_report(MYSQLI_REPORT_ERROR); + + mysqli_change_user($link, "0123456789-10-456789-20-456789-30-456789-40-456789-50-456789-60-456789-70-456789-80-456789-90-456789", "password", $db); + + mysqli_report(MYSQLI_REPORT_OFF); + + 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); + + mysqli_change_user($link, "This might work if you accept anonymous users in your setup", "password", $db); + + print "done!"; +?> +--CLEAN-- +<?php + require_once("clean_table.inc"); +?> +--EXPECTF-- + +Warning: mysqli_change_user(): (%d/%d): Access denied for user '%s'@'%s' (using password: %s) in %s on line %d +done!
\ No newline at end of file diff --git a/ext/mysqli/tests/mysqli_report_wo_ps.phpt b/ext/mysqli/tests/mysqli_report_wo_ps.phpt index cc57511b5..dae81b21c 100644 --- a/ext/mysqli/tests/mysqli_report_wo_ps.phpt +++ b/ext/mysqli/tests/mysqli_report_wo_ps.phpt @@ -1,10 +1,17 @@ --TEST-- -mysqli_report() +mysqli_report(), MySQL < 5.6 --SKIPIF-- <?php require_once('skipif.inc'); require_once('skipifemb.inc'); require_once('skipifconnectfailure.inc'); + +if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) + die(sprintf("SKIP Cannot connect to the server using host=%s, user=%s, passwd=***, dbname=%s, port=%s, socket=%s\n", + $host, $user, $db, $port, $socket)); + +if (mysqli_get_server_version($link) >= 50600) + die("SKIP For MySQL < 5.6.0"); ?> --FILE-- <?php diff --git a/ext/mysqli/tests/mysqli_savepoint.phpt b/ext/mysqli/tests/mysqli_savepoint.phpt new file mode 100644 index 000000000..5775b0eae --- /dev/null +++ b/ext/mysqli/tests/mysqli_savepoint.phpt @@ -0,0 +1,72 @@ +--TEST-- +mysqli_savepoint() +--SKIPIF-- +<?php +require_once('skipif.inc'); +require_once('skipifemb.inc'); +require_once('skipifconnectfailure.inc'); + +require_once('connect.inc'); +if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) + die(sprintf("Cannot connect, [%d] %s", mysqli_connect_errno(), mysqli_connect_error())); + +if (!have_innodb($link)) + die(sprintf("Needs InnoDB support, [%d] %s", $link->errno, $link->error)); +?> +--FILE-- +<?php + require_once("connect.inc"); + /* {{{ proto bool mysqli_savepoint(object link, string name) */ + $tmp = NULL; + $link = NULL; + + if (!is_null($tmp = @mysqli_savepoint())) + printf("[001] Expecting NULL, got %s/%s\n", gettype($tmp), $tmp); + + if (!is_null($tmp = @mysqli_savepoint($link))) + printf("[002] Expecting NULL, got %s/%s\n", gettype($tmp), $tmp); + + if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) + printf("[003] Cannot connect to the server using host=%s, user=%s, passwd=***, dbname=%s, port=%s, socket=%s\n", + $host, $user, $db, $port, $socket); + + $name = array(); + if (!is_null($tmp = @mysqli_savepoint($link, $name))) + printf("[004] Expecting NULL, got %s/%s\n", gettype($tmp), $tmp); + + if (!is_null($tmp = @mysqli_savepoint($link, 'foo', $link))) + printf("[005] Expecting NULL, got %s/%s\n", gettype($tmp), $tmp); + + if (false !== ($tmp = mysqli_savepoint($link, ''))) + printf("[006] Expecting false, got %s/%s\n", gettype($tmp), $tmp); + + if (!mysqli_query($link, 'DROP TABLE IF EXISTS test')) + printf("[007] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + + if (!mysqli_query($link, 'CREATE TABLE test(id INT) ENGINE = InnoDB')) + printf("[008] Cannot create test table, [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + + if (true !== ($tmp = mysqli_autocommit($link, false))) + printf("[009] Cannot turn off autocommit, expecting true, got %s/%s\n", gettype($tmp), $tmp); + + /* overrule autocommit */ + if (true !== ($tmp = mysqli_savepoint($link, 'my'))) + printf("[010] Got %s - [%d] %s\n", var_dump($tmp, true), mysqli_errno($link), mysqli_error($link)); + + if (!mysqli_query($link, 'INSERT INTO test(id) VALUES (1)')) + printf("[011] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + + $tmp = mysqli_rollback($link); + if ($tmp !== true) + printf("[012] Expecting boolean/true, got %s/%s\n", gettype($tmp), $tmp); + + print "done!"; +?> +--CLEAN-- +<?php + require_once("clean_table.inc"); +?> +--EXPECTF-- + +Warning: mysqli_savepoint(): Savepoint name cannot be empty in %s on line %d +done! diff --git a/ext/mysqli/tests/mysqli_stmt_get_warnings.phpt b/ext/mysqli/tests/mysqli_stmt_get_warnings.phpt index 2b0e150de..69755865e 100644 --- a/ext/mysqli/tests/mysqli_stmt_get_warnings.phpt +++ b/ext/mysqli/tests/mysqli_stmt_get_warnings.phpt @@ -17,7 +17,7 @@ if (!mysqli_query($link, "DROP TABLE IF EXISTS test") || !mysqli_query($link, "CREATE TABLE test(id SMALLINT)")) die(sprintf("skip [%d] %s\n", $link->errno, $link->error)); -if (!@mysqli_query("INSERT INTO test(id) VALUES (100001)")) +if (!@mysqli_query($link, "SET sql_mode=''") || !@mysqli_query($link, "INSERT INTO test(id) VALUES (100001)")) die("skip Strict sql mode seems to be active. We won't get a warning to check for."); mysqli_query($link, "DROP TABLE IF EXISTS test"); @@ -43,54 +43,57 @@ mysqli_query($link, "DROP TABLE IF EXISTS test"); if (NULL !== ($tmp = mysqli_stmt_get_warnings($stmt))) printf("[004] Expecting NULL, got %s/%s\n", gettype($tmp), $tmp); - if (!mysqli_stmt_prepare($stmt, "DROP TABLE IF EXISTS test") || !mysqli_stmt_execute($stmt)) + if (!mysqli_stmt_prepare($stmt, "SET sql_mode=''") || !mysqli_stmt_execute($stmt)) printf("[005] [%d] %s\n", mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt)); + if (!mysqli_stmt_prepare($stmt, "DROP TABLE IF EXISTS test") || !mysqli_stmt_execute($stmt)) + printf("[006] [%d] %s\n", mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt)); + if (false !== ($tmp = mysqli_stmt_get_warnings($stmt))) - printf("[006] Expecting boolean/false, got %s/%s\n", gettype($tmp), $tmp); + printf("[007] Expecting boolean/false, got %s/%s\n", gettype($tmp), $tmp); if (!mysqli_stmt_prepare($stmt, "CREATE TABLE test(id SMALLINT, label CHAR(1))") || !mysqli_stmt_execute($stmt)) - printf("[007] [%d] %s\n", mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt)); + printf("[008] [%d] %s\n", mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt)); if (false !== ($tmp = mysqli_stmt_get_warnings($stmt))) - printf("[008] Expecting boolean/false, got %s/%s\n", gettype($tmp), $tmp); + printf("[009] Expecting boolean/false, got %s/%s\n", gettype($tmp), $tmp); if (!mysqli_stmt_prepare($stmt, "INSERT INTO test(id, label) VALUES (100000, 'a'), (100001, 'b')") || !mysqli_stmt_execute($stmt)) - printf("[009] [%d] %s\n", mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt)); + printf("[010] [%d] %s\n", mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt)); if (!is_object($warning = mysqli_stmt_get_warnings($stmt))) - printf("[010] Expecting mysqli_warning object, got %s/%s\n", gettype($warning), $warning); + printf("[011] Expecting mysqli_warning object, got %s/%s\n", gettype($warning), $warning); if ('mysqli_warning' !== get_class($warning)) - printf("[011] Expecting object of type mysqli_warning got type '%s'", get_class($warning)); + printf("[012] Expecting object of type mysqli_warning got type '%s'", get_class($warning)); if (!method_exists($warning, 'next')) - printf("[012] Object mysqli_warning seems to lack method next()\n"); + printf("[013] Object mysqli_warning seems to lack method next()\n"); $i = 0; do { if ('' == $warning->message) - printf("[013 - %d] Message should not be empty\n", $i); + printf("[014 - %d] Message should not be empty\n", $i); if ('' == $warning->sqlstate) - printf("[014 - %d] SQL State should not be empty\n", $i); + printf("[015 - %d] SQL State should not be empty\n", $i); if (0 == $warning->errno) - printf("[015 - %d] Error number should not be zero\n", $i); + printf("[016 - %d] Error number should not be zero\n", $i); $i++; } while ($warning->next()); if (2 != $i) - printf("[016] Expected 2 warnings, got %d warnings\n", $i); + printf("[017] Expected 2 warnings, got %d warnings\n", $i); mysqli_stmt_close($stmt); if (NULL !== ($tmp = mysqli_stmt_get_warnings($stmt))) - printf("[015] Expecting NULL, got %s/%s\n", gettype($tmp), $tmp); + printf("[018] Expecting NULL, got %s/%s\n", gettype($tmp), $tmp); mysqli_close($link); print "done!"; diff --git a/ext/mysqli/tests/mysqli_store_result_buffered_c.phpt b/ext/mysqli/tests/mysqli_store_result_buffered_c.phpt new file mode 100644 index 000000000..2dec703b9 --- /dev/null +++ b/ext/mysqli/tests/mysqli_store_result_buffered_c.phpt @@ -0,0 +1,41 @@ +--TEST-- +mysqli_store_result() +--SKIPIF-- +<?php +require_once('skipif.inc'); +require_once('skipifemb.inc'); +require_once('skipifconnectfailure.inc'); +?> +--INI-- +mysqlnd.debug=d:t:O,/tmp/mysqlnd.trace +--FILE-- +<?php + require_once("connect.inc"); + + $tmp = NULL; + $link = NULL; + + + require('table.inc'); + + if (!$res = mysqli_real_query($link, "SELECT id, label FROM test ORDER BY id")) + printf("[003] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + + if (!is_object($res = mysqli_store_result($link, MYSQLI_STORE_RESULT_COPY_DATA))) + printf("[004] Expecting object, got %s/%s. [%d] %s\n", + gettype($res), $res, mysqli_errno($link), mysqli_error($link)); + + if (true !== ($tmp = mysqli_data_seek($res, 2))) + printf("[005] Expecting boolean/true, got %s/%s. [%d] %s\n", + gettype($tmp), $tmp, mysqli_errno($link), mysqli_error($link)); + + mysqli_free_result($res); + + print "done!"; +?> +--CLEAN-- +<?php + require_once("clean_table.inc"); +?> +--EXPECTF-- +done! diff --git a/ext/mysqli/tests/mysqli_store_result_copy.phpt b/ext/mysqli/tests/mysqli_store_result_copy.phpt new file mode 100644 index 000000000..304300459 --- /dev/null +++ b/ext/mysqli/tests/mysqli_store_result_copy.phpt @@ -0,0 +1,285 @@ +--TEST-- +mysqli_store_result() +--SKIPIF-- +<?php +require_once('skipif.inc'); +require_once('skipifemb.inc'); +require_once('skipifconnectfailure.inc'); +if (!$IS_MYSQLND) { + die("SKIP mysqlnd only test"); +} +?> +--INI-- +mysqlnd.debug=d:t:O,/tmp/mysqlnd.trace +mysqlnd.net_read_buffer_size=1 +mysqlnd.mempool_default_size=1 +mysqlnd.fetch_data_copy=0 +--FILE-- +<?php + require_once("connect.inc"); + + $tmp = NULL; + $link = NULL; + + require('table.inc'); + + if (!$res = mysqli_real_query($link, "SELECT id, label FROM test ORDER BY id")) + printf("[003] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + + if (!is_object($res = mysqli_store_result($link, MYSQLI_STORE_RESULT_COPY_DATA))) + printf("[004] Expecting object, got %s/%s. [%d] %s\n", + gettype($res), $res, mysqli_errno($link), mysqli_error($link)); + + if (true !== ($tmp = mysqli_data_seek($res, 2))) + printf("[005] Expecting boolean/true, got %s/%s. [%d] %s\n", + gettype($tmp), $tmp, mysqli_errno($link), mysqli_error($link)); + + var_dump($res->fetch_assoc()); + + if (true !== ($tmp = mysqli_data_seek($res, 0))) + printf("[006] Expecting boolean/true, got %s/%s. [%d] %s\n", + gettype($tmp), $tmp, mysqli_errno($link), mysqli_error($link)); + + while ($row = $res->fetch_assoc()) { + printf("id = %d, label = %s\n", $row['id'], $row['label']); + } + + mysqli_free_result($res); + mysqli_close($link); + + if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) { + printf("[007] Cannot connect to the server using host=%s, user=%s, passwd=***, dbname=%s, port=%s, socket=%s\n", + $host, $user, $db, $port, $socket); + } + + + if (!$res = mysqli_real_query($link, "SELECT id, label FROM test ORDER BY id")) + printf("[008] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + + if (!is_object($res = mysqli_store_result($link, MYSQLI_STORE_RESULT_COPY_DATA))) + printf("[009] Expecting object, got %s/%s. [%d] %s\n", + gettype($res), $res, mysqli_errno($link), mysqli_error($link)); + + $no_result = 0; + for ($i = 0; $i < 1000; $i++) { + $idx = mt_rand(-100, 100); + if (true === @mysqli_data_seek($res, $idx)) { + $row = $res->fetch_assoc(); + if (!isset($row['id']) || !isset($row['label'])) { + printf("[010] Brute force seek %d returned %d\n", $idx, var_export($row, true)); + } + } else { + $no_result++; + } + } + printf("No result: %d\n", $no_result); + + /* implicit free, implicit store */ + /* meta and fetch lenghts code */ + if (!$res = mysqli_query($link, "SELECT CONCAT(id, id) AS _c, label FROM test ORDER BY id DESC LIMIT 2")) + printf("[011] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + + printf("Default\n"); + var_dump(mysqli_fetch_lengths($res)); + $fields = $res->fetch_fields(); + while ($row = $res->fetch_assoc()) { + var_dump(mysqli_fetch_lengths($res)); + } + var_dump(mysqli_fetch_lengths($res)); + + if (!$res = mysqli_real_query($link, "SELECT CONCAT(id, id) AS _c, label FROM test ORDER BY id DESC LIMIT 2")) + printf("[012] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + + if (!is_object($res = mysqli_store_result($link, MYSQLI_STORE_RESULT_COPY_DATA))) + printf("[013] Expecting object, got %s/%s. [%d] %s\n", + gettype($res), $res, mysqli_errno($link), mysqli_error($link)); + + printf("Copy\n"); + var_dump(mysqli_fetch_lengths($res)); + $fields_copy = $res->fetch_fields(); + while ($row = $res->fetch_assoc()) { + var_dump(mysqli_fetch_lengths($res)); + } + var_dump(mysqli_fetch_lengths($res)); + + /* There's no need for in-depth testing here. If you want in-depth switch mysqlnd + globally to copy mode and run all the tests */ + foreach ($fields as $k => $field_info) { + if ($fields_copy[$k] != $field_info) { + printf("[014] Metadata seems to differ, dumping\n"); + var_dump($field_info); + var_dump($fields_copy[$k]); + } + } + + /* fetch all */ + + if (!$res = mysqli_real_query($link, "SELECT id, label FROM test ORDER BY id DESC LIMIT 2")) + printf("[015] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + + if (!is_object($res = mysqli_store_result($link, MYSQLI_STORE_RESULT_COPY_DATA))) + printf("[016] Expecting object, got %s/%s. [%d] %s\n", + gettype($res), $res, mysqli_errno($link), mysqli_error($link)); + + foreach (mysqli_fetch_all($res, MYSQLI_ASSOC) as $row) { + printf("id = %d label = %s\n", $row['id'], $row['label']); + } + + if (!$res = mysqli_real_query($link, "SELECT id, label FROM test ORDER BY id DESC LIMIT 2")) + printf("[017] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + + if (!is_object($res = mysqli_store_result($link, MYSQLI_STORE_RESULT_COPY_DATA))) + printf("[018] Expecting object, got %s/%s. [%d] %s\n", + gettype($res), $res, mysqli_errno($link), mysqli_error($link)); + + /* provoke out of sync */ + if (!mysqli_real_query($link, "SELECT id, label FROM test ORDER BY id DESC LIMIT 2")) + printf("[019] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + + var_dump($res->fetch_assoc()); + + if (!$res = mysqli_real_query($link, "SELECT id, label FROM test ORDER BY id ASC LIMIT 2")) + printf("[020] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + + if (!is_object($res = mysqli_store_result($link, MYSQLI_STORE_RESULT_COPY_DATA))) + printf("[021] Expecting object, got %s/%s. [%d] %s\n", + gettype($res), $res, mysqli_errno($link), mysqli_error($link)); + + /* user conn killed, res associated with conn, fetch from res */ + unset($link); + var_dump($res->fetch_assoc()); + + + 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); + } + + if (!$res = mysqli_real_query($link, "INSERT INTO test(id, label) VALUES (100, 'z')")) + printf("[023] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + + mysqli_store_result($link, MYSQLI_STORE_RESULT_COPY_DATA); + + if (mysqli_get_server_version($link) > 50000) { + // let's try to play with stored procedures + mysqli_real_query($link, 'DROP PROCEDURE IF EXISTS p'); + if (mysqli_real_query($link, 'CREATE PROCEDURE p(OUT ver_param VARCHAR(25)) READS SQL DATA BEGIN SELECT id FROM test WHERE id >= 100 ORDER BY id; SELECT id + 1, label FROM test WHERE id > 0 AND id < 3 ORDER BY id; SELECT VERSION() INTO ver_param; +END;')) { + mysqli_multi_query($link, "CALL p(@version)"); + do { + if ($res = $link->store_result(MYSQLI_STORE_RESULT_COPY_DATA)) { + printf("---\n"); + var_dump($res->fetch_all()); + $res->free(); + } else { + if ($link->errno) { + echo "Store failed: (" . $link->errno . ") " . $link->error; + } + } + } while ($link->more_results() && $link->next_result()); + mysqli_real_query($link, 'SELECT @version AS p_version'); + $res = mysqli_store_result($link, MYSQLI_STORE_RESULT_COPY_DATA); + + $tmp = mysqli_fetch_assoc($res); + if (!is_array($tmp) || empty($tmp) || !isset($tmp['p_version']) || ('' == $tmp['p_version'])) { + printf("[024] Expecting array [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + var_dump($tmp); + } + + mysqli_free_result($res); + } else { + printf("[025] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + } + } + + print "done!"; +?> +--CLEAN-- +<?php + require_once("clean_table.inc"); +?> +--EXPECTF-- +array(2) { + ["id"]=> + string(1) "3" + ["label"]=> + string(1) "c" +} +id = 1, label = a +id = 2, label = b +id = 3, label = c +id = 4, label = d +id = 5, label = e +id = 6, label = f +No result: %d +Default +bool(false) +array(2) { + [0]=> + int(2) + [1]=> + int(1) +} +array(2) { + [0]=> + int(2) + [1]=> + int(1) +} +bool(false) +Copy +bool(false) +array(2) { + [0]=> + int(2) + [1]=> + int(1) +} +array(2) { + [0]=> + int(2) + [1]=> + int(1) +} +bool(false) +id = 6 label = f +id = 5 label = e +array(2) { + ["id"]=> + string(1) "6" + ["label"]=> + string(1) "f" +} +[020] [2014] %s +array(2) { + ["id"]=> + string(1) "6" + ["label"]=> + string(1) "f" +} +--- +array(1) { + [0]=> + array(1) { + [0]=> + string(3) "100" + } +} +--- +array(2) { + [0]=> + array(2) { + [0]=> + string(1) "2" + [1]=> + string(1) "a" + } + [1]=> + array(2) { + [0]=> + string(1) "3" + [1]=> + string(1) "b" + } +} +done!
\ No newline at end of file diff --git a/ext/mysqlnd/mysqlnd.c b/ext/mysqlnd/mysqlnd.c index 61be5ca17..893a144c4 100644 --- a/ext/mysqlnd/mysqlnd.c +++ b/ext/mysqlnd/mysqlnd.c @@ -1111,7 +1111,8 @@ PHPAPI MYSQLND * mysqlnd_connect(MYSQLND * conn_handle, const char * db, unsigned int db_len, unsigned int port, const char * socket_or_pipe, - unsigned int mysql_flags + unsigned int mysql_flags, + unsigned int client_api_flags TSRMLS_DC) { enum_func_status ret = FAIL; @@ -1122,7 +1123,7 @@ PHPAPI MYSQLND * mysqlnd_connect(MYSQLND * conn_handle, if (!conn_handle) { self_alloced = TRUE; - if (!(conn_handle = mysqlnd_init(FALSE))) { + if (!(conn_handle = mysqlnd_init(client_api_flags, FALSE))) { /* OOM */ DBG_RETURN(NULL); } @@ -2570,6 +2571,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, store_result)(MYSQLND_CONN_DATA * const conn, if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) { do { + unsigned int f = flags; if (!conn->current_result) { break; } @@ -2583,7 +2585,24 @@ MYSQLND_METHOD(mysqlnd_conn_data, store_result)(MYSQLND_CONN_DATA * const conn, MYSQLND_INC_CONN_STATISTIC(conn->stats, STAT_BUFFERED_SETS); - result = conn->current_result->m.store_result(conn->current_result, conn, 0 TSRMLS_CC); + /* overwrite */ + if ((conn->m->get_client_api_capabilities(conn TSRMLS_CC) & MYSQLND_CLIENT_KNOWS_RSET_COPY_DATA)) { + if (MYSQLND_G(fetch_data_copy)) { + f &= ~MYSQLND_STORE_NO_COPY; + f |= MYSQLND_STORE_COPY; + } + } else { + /* if for some reason PDO borks something */ + if (!(f & (MYSQLND_STORE_NO_COPY | MYSQLND_STORE_COPY))) { + f |= MYSQLND_STORE_COPY; + } + } + if (!(f & (MYSQLND_STORE_NO_COPY | MYSQLND_STORE_COPY))) { + SET_CLIENT_ERROR(*conn->error_info, CR_UNKNOWN_ERROR, UNKNOWN_SQLSTATE, "Unknown fetch mode"); + DBG_ERR("Unknown fetch mode"); + break; + } + result = conn->current_result->m.store_result(conn->current_result, conn, f TSRMLS_CC); if (!result) { conn->current_result->m.free_result(conn->current_result, TRUE TSRMLS_CC); } @@ -2890,6 +2909,32 @@ MYSQLND_METHOD(mysqlnd_conn_data, tx_savepoint_release)(MYSQLND_CONN_DATA * conn /* }}} */ +/* {{{ mysqlnd_conn_data::negotiate_client_api_capabilities */ +static unsigned int +MYSQLND_METHOD(mysqlnd_conn_data, negotiate_client_api_capabilities)(MYSQLND_CONN_DATA * const conn, const unsigned int flags TSRMLS_DC) +{ + unsigned int ret = 0; + DBG_ENTER("mysqlnd_conn_data::negotiate_client_api_capabilities"); + if (conn) { + ret = conn->client_api_capabilities; + conn->client_api_capabilities = flags; + } + + DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ mysqlnd_conn_data::get_client_api_capabilities */ +static unsigned int +MYSQLND_METHOD(mysqlnd_conn_data, get_client_api_capabilities)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC) +{ + DBG_ENTER("mysqlnd_conn_data::get_client_api_capabilities"); + DBG_RETURN(conn? conn->client_api_capabilities : 0); +} +/* }}} */ + + /* {{{ mysqlnd_conn_data::local_tx_start */ static enum_func_status MYSQLND_METHOD(mysqlnd_conn_data, local_tx_start)(MYSQLND_CONN_DATA * conn, size_t this_func TSRMLS_DC) @@ -3018,7 +3063,10 @@ MYSQLND_CLASS_METHODS_START(mysqlnd_conn_data) MYSQLND_METHOD(mysqlnd_conn_data, simple_command_send_request), MYSQLND_METHOD(mysqlnd_conn_data, fetch_auth_plugin_by_name), - MYSQLND_METHOD(mysqlnd_conn_data, set_client_option_2d) + MYSQLND_METHOD(mysqlnd_conn_data, set_client_option_2d), + + MYSQLND_METHOD(mysqlnd_conn_data, negotiate_client_api_capabilities), + MYSQLND_METHOD(mysqlnd_conn_data, get_client_api_capabilities) MYSQLND_CLASS_METHODS_END; @@ -3097,11 +3145,14 @@ MYSQLND_CLASS_METHODS_END; /* {{{ _mysqlnd_init */ PHPAPI MYSQLND * -_mysqlnd_init(zend_bool persistent TSRMLS_DC) +_mysqlnd_init(unsigned int flags, zend_bool persistent TSRMLS_DC) { MYSQLND * ret; DBG_ENTER("mysqlnd_init"); ret = MYSQLND_CLASS_METHOD_TABLE_NAME(mysqlnd_object_factory).get_connection(persistent TSRMLS_CC); + if (ret && ret->data) { + ret->data->m->negotiate_client_api_capabilities(ret->data, flags TSRMLS_CC); + } DBG_RETURN(ret); } /* }}} */ diff --git a/ext/mysqlnd/mysqlnd.h b/ext/mysqlnd/mysqlnd.h index 551c5f34a..f373ea5dd 100644 --- a/ext/mysqlnd/mysqlnd.h +++ b/ext/mysqlnd/mysqlnd.h @@ -85,15 +85,16 @@ PHPAPI const MYSQLND_CHARSET * mysqlnd_find_charset_name(const char * const char /* Connect */ -#define mysqlnd_init(persistent) _mysqlnd_init((persistent) TSRMLS_CC) -PHPAPI MYSQLND * _mysqlnd_init(zend_bool persistent TSRMLS_DC); +#define mysqlnd_init(client_flags, persistent) _mysqlnd_init((client_flags), (persistent) TSRMLS_CC) +PHPAPI MYSQLND * _mysqlnd_init(unsigned int client_flags, zend_bool persistent TSRMLS_DC); PHPAPI MYSQLND * mysqlnd_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 + unsigned int mysql_flags, + unsigned int client_api_flags TSRMLS_DC); #define mysqlnd_change_user(conn, user, passwd, db, silent) ((conn)->data)->m->change_user((conn)->data, (user), (passwd), (db), (silent), strlen((passwd)) TSRMLS_CC) @@ -282,6 +283,7 @@ ZEND_BEGIN_MODULE_GLOBALS(mysqlnd) long debug_calloc_fail_threshold; long debug_realloc_fail_threshold; char * sha256_server_public_key; + zend_bool fetch_data_copy; ZEND_END_MODULE_GLOBALS(mysqlnd) PHPAPI ZEND_EXTERN_MODULE_GLOBALS(mysqlnd) diff --git a/ext/mysqlnd/mysqlnd_enum_n_def.h b/ext/mysqlnd/mysqlnd_enum_n_def.h index d77199857..e1fc5f8f0 100644 --- a/ext/mysqlnd/mysqlnd_enum_n_def.h +++ b/ext/mysqlnd/mysqlnd_enum_n_def.h @@ -620,6 +620,16 @@ enum php_mysqlnd_server_command #define MYSQLND_STORE_NO_COPY 2 #define MYSQLND_STORE_COPY 4 +enum mysqlnd_buffered_type +{ + MYSQLND_BUFFERED_TYPE_ZVAL = 1, + MYSQLND_BUFFERED_TYPE_C +}; + + +#define MYSQLND_CLIENT_NO_FLAG 0 +#define MYSQLND_CLIENT_KNOWS_RSET_COPY_DATA 1 + #endif /* MYSQLND_ENUM_N_DEF_H */ diff --git a/ext/mysqlnd/mysqlnd_ext_plugin.c b/ext/mysqlnd/mysqlnd_ext_plugin.c index 706111346..13f1e294d 100644 --- a/ext/mysqlnd/mysqlnd_ext_plugin.c +++ b/ext/mysqlnd/mysqlnd_ext_plugin.c @@ -84,14 +84,26 @@ PHPAPI void ** _mysqlnd_plugin_get_plugin_result_unbuffered_data(const MYSQLND_R /* {{{ _mysqlnd_plugin_get_plugin_result_buffered_data */ -PHPAPI void ** _mysqlnd_plugin_get_plugin_result_buffered_data(const MYSQLND_RES_BUFFERED * result, unsigned int plugin_id TSRMLS_DC) +PHPAPI void ** _mysqlnd_plugin_get_plugin_result_buffered_data_zval(const MYSQLND_RES_BUFFERED_ZVAL * result, unsigned int plugin_id TSRMLS_DC) { DBG_ENTER("_mysqlnd_plugin_get_plugin_result_data"); DBG_INF_FMT("plugin_id=%u", plugin_id); if (!result || plugin_id >= mysqlnd_plugin_count()) { return NULL; } - DBG_RETURN((void *)((char *)result + sizeof(MYSQLND_RES_BUFFERED) + plugin_id * sizeof(void *))); + DBG_RETURN((void *)((char *)result + sizeof(MYSQLND_RES_BUFFERED_ZVAL) + plugin_id * sizeof(void *))); +} +/* }}} */ + +/* {{{ _mysqlnd_plugin_get_plugin_result_buffered_data */ +PHPAPI void ** _mysqlnd_plugin_get_plugin_result_buffered_data_c(const MYSQLND_RES_BUFFERED_C * result, unsigned int plugin_id TSRMLS_DC) +{ + DBG_ENTER("_mysqlnd_plugin_get_plugin_result_data"); + DBG_INF_FMT("plugin_id=%u", plugin_id); + if (!result || plugin_id >= mysqlnd_plugin_count()) { + return NULL; + } + DBG_RETURN((void *)((char *)result + sizeof(MYSQLND_RES_BUFFERED_C) + plugin_id * sizeof(void *))); } /* }}} */ diff --git a/ext/mysqlnd/mysqlnd_ext_plugin.h b/ext/mysqlnd/mysqlnd_ext_plugin.h index 6f15eee8c..d4a9d6cfc 100644 --- a/ext/mysqlnd/mysqlnd_ext_plugin.h +++ b/ext/mysqlnd/mysqlnd_ext_plugin.h @@ -34,9 +34,11 @@ PHPAPI void ** _mysqlnd_plugin_get_plugin_result_data(const MYSQLND_RES * result PHPAPI void ** _mysqlnd_plugin_get_plugin_result_unbuffered_data(const MYSQLND_RES_UNBUFFERED * result, unsigned int plugin_id TSRMLS_DC); #define mysqlnd_plugin_get_plugin_result_unbuffered_data(r, p_id) _mysqlnd_plugin_get_plugin_result_unbuffered_data((r), (p_id) TSRMLS_CC) -PHPAPI void ** _mysqlnd_plugin_get_plugin_result_buffered_data(const MYSQLND_RES_BUFFERED * result, unsigned int plugin_id TSRMLS_DC); -#define mysqlnd_plugin_get_plugin_result_buffered_data(r, p_id) _mysqlnd_plugin_get_plugin_result_buffered_data((r), (p_id) TSRMLS_CC) +PHPAPI void ** _mysqlnd_plugin_get_plugin_result_buffered_data_zval(const MYSQLND_RES_BUFFERED_ZVAL * result, unsigned int plugin_id TSRMLS_DC); +#define mysqlnd_plugin_get_plugin_result_buffered_data_zval(r, p_id) _mysqlnd_plugin_get_plugin_result_buffered_data_zval((r), (p_id) TSRMLS_CC) +PHPAPI void ** _mysqlnd_plugin_get_plugin_result_buffered_data_c(const MYSQLND_RES_BUFFERED_C * result, unsigned int plugin_id TSRMLS_DC); +#define mysqlnd_plugin_get_plugin_result_buffered_data_c(r, p_id) _mysqlnd_plugin_get_plugin_result_buffered_data_c((r), (p_id) TSRMLS_CC) PHPAPI void ** _mysqlnd_plugin_get_plugin_stmt_data(const MYSQLND_STMT * stmt, unsigned int plugin_id TSRMLS_DC); #define mysqlnd_plugin_get_plugin_stmt_data(s, p_id) _mysqlnd_plugin_get_plugin_stmt_data((s), (p_id) TSRMLS_CC) diff --git a/ext/mysqlnd/mysqlnd_ps.c b/ext/mysqlnd/mysqlnd_ps.c index fc30bddcb..bee8e1d0e 100644 --- a/ext/mysqlnd/mysqlnd_ps.c +++ b/ext/mysqlnd/mysqlnd_ps.c @@ -89,36 +89,39 @@ MYSQLND_METHOD(mysqlnd_stmt, store_result)(MYSQLND_STMT * const s TSRMLS_DC) result->type = MYSQLND_RES_PS_BUF; /* result->m.row_decoder = php_mysqlnd_rowp_read_binary_protocol; */ - result->stored_data = mysqlnd_result_buffered_init(result->field_count, TRUE, result->persistent TSRMLS_CC); + result->stored_data = (MYSQLND_RES_BUFFERED *) mysqlnd_result_buffered_zval_init(result->field_count, TRUE, result->persistent TSRMLS_CC); if (!result->stored_data) { SET_OOM_ERROR(*conn->error_info); DBG_RETURN(NULL); } - ret = result->m.store_result_fetch_data(conn, result, result->meta, TRUE TSRMLS_CC); + ret = result->m.store_result_fetch_data(conn, result, result->meta, &result->stored_data->row_buffers, TRUE TSRMLS_CC); result->stored_data->m.fetch_row = mysqlnd_stmt_fetch_row_buffered; if (PASS == ret) { /* Overflow ? */ - MYSQLND_RES_BUFFERED * set = result->stored_data; - if (set->row_count) { - /* don't try to allocate more than possible - mnd_XXalloc expects size_t, and it can have narrower range than uint64_t */ - if (set->row_count * result->meta->field_count * sizeof(zval *) > SIZE_MAX) { - SET_OOM_ERROR(*conn->error_info); - DBG_RETURN(NULL); - } - /* if pecalloc is used valgrind barks gcc version 4.3.1 20080507 (prerelease) [gcc-4_3-branch revision 135036] (SUSE Linux) */ - set->data = mnd_emalloc((size_t)(set->row_count * result->meta->field_count * sizeof(zval *))); - if (!set->data) { - SET_OOM_ERROR(*conn->error_info); - DBG_RETURN(NULL); + if (result->stored_data->type == MYSQLND_BUFFERED_TYPE_ZVAL) { + MYSQLND_RES_BUFFERED_ZVAL * set = (MYSQLND_RES_BUFFERED_ZVAL *) result->stored_data; + if (result->stored_data->row_count) { + /* don't try to allocate more than possible - mnd_XXalloc expects size_t, and it can have narrower range than uint64_t */ + if (result->stored_data->row_count * result->meta->field_count * sizeof(zval *) > SIZE_MAX) { + SET_OOM_ERROR(*conn->error_info); + DBG_RETURN(NULL); + } + /* if pecalloc is used valgrind barks gcc version 4.3.1 20080507 (prerelease) [gcc-4_3-branch revision 135036] (SUSE Linux) */ + set->data = mnd_emalloc((size_t)(result->stored_data->row_count * result->meta->field_count * sizeof(zval *))); + if (!set->data) { + SET_OOM_ERROR(*conn->error_info); + DBG_RETURN(NULL); + } + memset(set->data, 0, (size_t)(result->stored_data->row_count * result->meta->field_count * sizeof(zval *))); } - memset(set->data, 0, (size_t)(set->row_count * result->meta->field_count * sizeof(zval *))); + /* Position at the first row */ + set->data_cursor = set->data; + } else if (result->stored_data->type == MYSQLND_BUFFERED_TYPE_ZVAL) { + /*TODO*/ } - /* Position at the first row */ - set->data_cursor = set->data; - /* libmysql API docs say it should be so for SELECT statements */ stmt->upsert_status->affected_rows = stmt->result->stored_data->row_count; @@ -187,7 +190,7 @@ MYSQLND_METHOD(mysqlnd_stmt, get_result)(MYSQLND_STMT * const s TSRMLS_DC) break; } - if ((result = result->m.store_result(result, conn, TRUE TSRMLS_CC))) { + if ((result = result->m.store_result(result, conn, MYSQLND_STORE_PS | MYSQLND_STORE_NO_COPY TSRMLS_CC))) { stmt->upsert_status->affected_rows = result->stored_data->row_count; stmt->state = MYSQLND_STMT_PREPARED; result->type = MYSQLND_RES_PS_BUF; @@ -733,7 +736,6 @@ mysqlnd_stmt_fetch_row_buffered(MYSQLND_RES * result, void * param, unsigned int { MYSQLND_STMT * s = (MYSQLND_STMT *) param; MYSQLND_STMT_DATA * stmt = s? s->data:NULL; - MYSQLND_RES_BUFFERED *set = result->stored_data; const MYSQLND_RES_METADATA * const meta = result->meta; unsigned int field_count = meta->field_count; @@ -742,81 +744,86 @@ mysqlnd_stmt_fetch_row_buffered(MYSQLND_RES * result, void * param, unsigned int DBG_INF_FMT("stmt=%lu", stmt != NULL ? stmt->stmt_id : 0L); /* If we haven't read everything */ - if (set->data_cursor && - (set->data_cursor - set->data) < (set->row_count * field_count)) - { - /* The user could have skipped binding - don't crash*/ - if (stmt->result_bind) { - unsigned int i; - zval **current_row = set->data_cursor; - - if (NULL == current_row[0]) { - uint64_t row_num = (set->data_cursor - set->data) / field_count; - enum_func_status rc = result->stored_data->m.row_decoder(set->row_buffers[row_num], - current_row, - meta->field_count, - meta->fields, - result->conn->options->int_and_float_native, - result->conn->stats TSRMLS_CC); - if (PASS != rc) { - DBG_RETURN(FAIL); - } - set->initialized_rows++; - if (stmt->update_max_length) { - for (i = 0; i < result->field_count; i++) { - /* - NULL fields are 0 length, 0 is not more than 0 - String of zero size, definitely can't be the next max_length. - Thus for NULL and zero-length we are quite efficient. - */ - if (Z_TYPE_P(current_row[i]) >= IS_STRING) { - unsigned long len = Z_STRLEN_P(current_row[i]); - if (meta->fields[i].max_length < len) { - meta->fields[i].max_length = len; + if (result->stored_data->type == MYSQLND_BUFFERED_TYPE_ZVAL) { + MYSQLND_RES_BUFFERED_ZVAL * set = (MYSQLND_RES_BUFFERED_ZVAL *) result->stored_data; + if (set->data_cursor && + (set->data_cursor - set->data) < (result->stored_data->row_count * field_count)) + { + /* The user could have skipped binding - don't crash*/ + if (stmt->result_bind) { + unsigned int i; + zval **current_row = set->data_cursor; + + if (NULL == current_row[0]) { + uint64_t row_num = (set->data_cursor - set->data) / field_count; + enum_func_status rc = result->stored_data->m.row_decoder(result->stored_data->row_buffers[row_num], + current_row, + meta->field_count, + meta->fields, + result->conn->options->int_and_float_native, + result->conn->stats TSRMLS_CC); + if (PASS != rc) { + DBG_RETURN(FAIL); + } + result->stored_data->initialized_rows++; + if (stmt->update_max_length) { + for (i = 0; i < result->field_count; i++) { + /* + NULL fields are 0 length, 0 is not more than 0 + String of zero size, definitely can't be the next max_length. + Thus for NULL and zero-length we are quite efficient. + */ + if (Z_TYPE_P(current_row[i]) >= IS_STRING) { + unsigned long len = Z_STRLEN_P(current_row[i]); + if (meta->fields[i].max_length < len) { + meta->fields[i].max_length = len; + } } } } } - } - for (i = 0; i < result->field_count; i++) { - /* Clean what we copied last time */ + for (i = 0; i < result->field_count; i++) { + /* Clean what we copied last time */ #ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF - if (stmt->result_bind[i].zv) { - zval_dtor(stmt->result_bind[i].zv); - } + if (stmt->result_bind[i].zv) { + zval_dtor(stmt->result_bind[i].zv); + } #endif - /* copy the type */ - if (stmt->result_bind[i].bound == TRUE) { - DBG_INF_FMT("i=%u type=%u", i, Z_TYPE_P(current_row[i])); - if (Z_TYPE_P(current_row[i]) != IS_NULL) { - /* - Copy the value. - Pre-condition is that the zvals in the result_bind buffer - have been ZVAL_NULL()-ed or to another simple type - (int, double, bool but not string). Because of the reference - counting the user can't delete the strings the variables point to. - */ - - Z_TYPE_P(stmt->result_bind[i].zv) = Z_TYPE_P(current_row[i]); - stmt->result_bind[i].zv->value = current_row[i]->value; + /* copy the type */ + if (stmt->result_bind[i].bound == TRUE) { + DBG_INF_FMT("i=%u type=%u", i, Z_TYPE_P(current_row[i])); + if (Z_TYPE_P(current_row[i]) != IS_NULL) { + /* + Copy the value. + Pre-condition is that the zvals in the result_bind buffer + have been ZVAL_NULL()-ed or to another simple type + (int, double, bool but not string). Because of the reference + counting the user can't delete the strings the variables point to. + */ + + Z_TYPE_P(stmt->result_bind[i].zv) = Z_TYPE_P(current_row[i]); + stmt->result_bind[i].zv->value = current_row[i]->value; #ifndef WE_DONT_COPY_IN_BUFFERED_AND_UNBUFFERED_BECAUSEOF_IS_REF - zval_copy_ctor(stmt->result_bind[i].zv); + zval_copy_ctor(stmt->result_bind[i].zv); #endif - } else { - ZVAL_NULL(stmt->result_bind[i].zv); + } else { + ZVAL_NULL(stmt->result_bind[i].zv); + } } } } + set->data_cursor += field_count; + *fetched_anything = TRUE; + /* buffered result sets don't have a connection */ + MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_PS_BUF); + DBG_INF("row fetched"); + } else { + set->data_cursor = NULL; + DBG_INF("no more data"); } - set->data_cursor += field_count; - *fetched_anything = TRUE; - /* buffered result sets don't have a connection */ - MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_PS_BUF); - DBG_INF("row fetched"); - } else { - set->data_cursor = NULL; - DBG_INF("no more data"); + } else if (result->stored_data->type == MYSQLND_BUFFERED_TYPE_C) { + /*TODO*/ } DBG_INF("PASS"); DBG_RETURN(PASS); diff --git a/ext/mysqlnd/mysqlnd_result.c b/ext/mysqlnd/mysqlnd_result.c index fc6f48581..0112a1394 100644 --- a/ext/mysqlnd/mysqlnd_result.c +++ b/ext/mysqlnd/mysqlnd_result.c @@ -32,29 +32,30 @@ #define MYSQLND_SILENT - -/* {{{ mysqlnd_result_buffered::initialize_result_set_rest */ +/* {{{ mysqlnd_result_buffered_zval::initialize_result_set_rest */ static enum_func_status -MYSQLND_METHOD(mysqlnd_result_buffered, initialize_result_set_rest)(MYSQLND_RES_BUFFERED * const result, MYSQLND_RES_METADATA * const meta, - MYSQLND_STATS * stats, zend_bool int_and_float_native TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_result_buffered_zval, initialize_result_set_rest)(MYSQLND_RES_BUFFERED * const result, MYSQLND_RES_METADATA * const meta, + MYSQLND_STATS * stats, zend_bool int_and_float_native TSRMLS_DC) { unsigned int i; - zval **data_cursor = result->data; - zval **data_begin = result->data; - unsigned int field_count = meta->field_count; - uint64_t row_count = result->row_count; enum_func_status ret = PASS; - DBG_ENTER("mysqlnd_result_buffered::initialize_result_set_rest"); + const unsigned int field_count = meta->field_count; + const uint64_t row_count = result->row_count; + enum_func_status rc; + + zval **data_begin = ((MYSQLND_RES_BUFFERED_ZVAL *) result)->data; + zval **data_cursor = data_begin; + + DBG_ENTER("mysqlnd_result_buffered_zval::initialize_result_set_rest"); if (!data_cursor || row_count == result->initialized_rows) { DBG_RETURN(ret); } while ((data_cursor - data_begin) < (int)(row_count * field_count)) { if (NULL == data_cursor[0]) { - enum_func_status rc = result->m.row_decoder( - result->row_buffers[(data_cursor - data_begin) / field_count], + rc = result->m.row_decoder(result->row_buffers[(data_cursor - data_begin) / field_count], data_cursor, - meta->field_count, + field_count, meta->fields, int_and_float_native, stats TSRMLS_CC); @@ -63,7 +64,7 @@ MYSQLND_METHOD(mysqlnd_result_buffered, initialize_result_set_rest)(MYSQLND_RES_ break; } result->initialized_rows++; - for (i = 0; i < meta->field_count; i++) { + for (i = 0; i < field_count; i++) { /* NULL fields are 0 length, 0 is not more than 0 String of zero size, definitely can't be the next max_length. @@ -84,6 +85,62 @@ MYSQLND_METHOD(mysqlnd_result_buffered, initialize_result_set_rest)(MYSQLND_RES_ /* }}} */ +/* {{{ mysqlnd_result_buffered_c::initialize_result_set_rest */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_result_buffered_c, initialize_result_set_rest)(MYSQLND_RES_BUFFERED * const result, MYSQLND_RES_METADATA * const meta, + MYSQLND_STATS * stats, zend_bool int_and_float_native TSRMLS_DC) +{ + unsigned int i; + enum_func_status ret = PASS; + const unsigned int field_count = meta->field_count; + const uint64_t row_count = result->row_count; + enum_func_status rc; + DBG_ENTER("mysqlnd_result_buffered_c::initialize_result_set_rest"); + + if (result->initialized_rows < row_count) { + zend_uchar * initialized = ((MYSQLND_RES_BUFFERED_C *) result)->initialized; + zval ** current_row = mnd_emalloc(field_count * sizeof(zval *)); + + if (!current_row) { + DBG_RETURN(FAIL); + } + + for (i = 0; i < result->row_count; i++) { + /* (i / 8) & the_bit_for_i*/ + if (initialized[i >> 3] & (1 << (i & 7))) { + continue; + } + + rc = result->m.row_decoder(result->row_buffers[i], current_row, field_count, meta->fields, int_and_float_native, stats TSRMLS_CC); + + if (rc != PASS) { + ret = FAIL; + break; + } + result->initialized_rows++; + initialized[i >> 3] |= (1 << (i & 7)); + for (i = 0; i < field_count; i++) { + /* + NULL fields are 0 length, 0 is not more than 0 + String of zero size, definitely can't be the next max_length. + Thus for NULL and zero-length we are quite efficient. + */ + if (Z_TYPE_P(current_row[i]) >= IS_STRING) { + unsigned long len = Z_STRLEN_P(current_row[i]); + if (meta->fields[i].max_length < len) { + meta->fields[i].max_length = len; + } + } + zval_ptr_dtor(¤t_row[i]); + } + } + mnd_efree(current_row); + } + DBG_RETURN(ret); +} +/* }}} */ + + /* {{{ 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) @@ -205,25 +262,23 @@ MYSQLND_METHOD(mysqlnd_result_unbuffered, free_result)(MYSQLND_RES_UNBUFFERED * /* }}} */ -/* {{{ mysqlnd_result_buffered::free_result */ +/* {{{ mysqlnd_result_buffered_zval::free_result */ static void -MYSQLND_METHOD(mysqlnd_result_buffered, free_result)(MYSQLND_RES_BUFFERED * const set TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_result_buffered_zval, free_result)(MYSQLND_RES_BUFFERED_ZVAL * const set TSRMLS_DC) { - unsigned int field_count = set->field_count; - int64_t row; + zval ** data = set->data; - DBG_ENTER("mysqlnd_res::free_buffered_data"); - DBG_INF_FMT("Freeing "MYSQLND_LLU_SPEC" row(s)", set->row_count); + DBG_ENTER("mysqlnd_result_buffered_zval::free_result"); - if (set->data) { + set->data = NULL; /* prevent double free if following loop is interrupted */ + if (data) { unsigned int copy_on_write_performed = 0; unsigned int copy_on_write_saved = 0; - zval **data = set->data; - set->data = NULL; /* prevent double free if following loop is interrupted */ - + unsigned int field_count = set->field_count; + int64_t row; + for (row = set->row_count - 1; row >= 0; row--) { zval **current_row = data + row * field_count; - MYSQLND_MEMORY_POOL_CHUNK *current_buffer = set->row_buffers[row]; int64_t col; if (current_row != NULL) { @@ -239,13 +294,48 @@ MYSQLND_METHOD(mysqlnd_result_buffered, free_result)(MYSQLND_RES_BUFFERED * cons } } } - current_buffer->free_chunk(current_buffer TSRMLS_CC); } - MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_COPY_ON_WRITE_PERFORMED, copy_on_write_performed, STAT_COPY_ON_WRITE_SAVED, copy_on_write_saved); mnd_efree(data); } + set->data_cursor = NULL; + DBG_VOID_RETURN; +} +/* }}} */ + + +/* {{{ mysqlnd_result_buffered_c::free_result */ +static void +MYSQLND_METHOD(mysqlnd_result_buffered_c, free_result)(MYSQLND_RES_BUFFERED_C * const set TSRMLS_DC) +{ + DBG_ENTER("mysqlnd_result_buffered_c::free_result"); + mnd_pefree(set->initialized, set->persistent); + set->initialized = NULL; + DBG_VOID_RETURN; +} +/* }}} */ + + +/* {{{ mysqlnd_result_buffered::free_result */ +static void +MYSQLND_METHOD(mysqlnd_result_buffered, free_result)(MYSQLND_RES_BUFFERED * const set TSRMLS_DC) +{ + int64_t row; + + DBG_ENTER("mysqlnd_result_buffered::free_result"); + DBG_INF_FMT("Freeing "MYSQLND_LLU_SPEC" row(s)", set->row_count); + + if (set->type == MYSQLND_BUFFERED_TYPE_ZVAL) { + MYSQLND_METHOD(mysqlnd_result_buffered_zval, free_result)((MYSQLND_RES_BUFFERED_ZVAL *) set TSRMLS_CC); + } if (set->type == MYSQLND_BUFFERED_TYPE_C) { + MYSQLND_METHOD(mysqlnd_result_buffered_c, free_result)((MYSQLND_RES_BUFFERED_C *) set TSRMLS_CC); + } + + for (row = set->row_count - 1; row >= 0; row--) { + MYSQLND_MEMORY_POOL_CHUNK *current_buffer = set->row_buffers[row]; + current_buffer->free_chunk(current_buffer TSRMLS_CC); + } if (set->lengths) { mnd_pefree(set->lengths, set->persistent); @@ -253,8 +343,8 @@ MYSQLND_METHOD(mysqlnd_result_buffered, free_result)(MYSQLND_RES_BUFFERED * cons } if (set->row_buffers) { - mnd_efree(set->row_buffers); - set->row_buffers = NULL; + mnd_pefree(set->row_buffers, 0); + set->row_buffers = NULL; } if (set->result_set_memory_pool) { @@ -263,7 +353,6 @@ MYSQLND_METHOD(mysqlnd_result_buffered, free_result)(MYSQLND_RES_BUFFERED * cons } - set->data_cursor = NULL; set->row_count = 0; mnd_pefree(set, set->persistent); @@ -590,31 +679,49 @@ mysqlnd_query_read_result_set_header(MYSQLND_CONN_DATA * conn, MYSQLND_STMT * s completeness. */ static unsigned long * -MYSQLND_METHOD(mysqlnd_result_buffered, fetch_lengths)(MYSQLND_RES_BUFFERED * const result TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_result_buffered_zval, fetch_lengths)(MYSQLND_RES_BUFFERED * const result TSRMLS_DC) { - unsigned int i; - zval **previous_row; - MYSQLND_RES_BUFFERED *set = result; - + const MYSQLND_RES_BUFFERED_ZVAL * set = (MYSQLND_RES_BUFFERED_ZVAL *) result; /* If: - unbuffered result - first row has not been read - last_row has been read */ + DBG_ENTER("mysqlnd_result_buffered_zval::fetch_lengths"); + if (set->data_cursor == NULL || set->data_cursor == set->data || - ((set->data_cursor - set->data) > (set->row_count * result->field_count) )) + ((set->data_cursor - set->data) > (result->row_count * result->field_count) )) { - return NULL;/* No rows or no more rows */ + DBG_INF("EOF"); + DBG_RETURN(NULL);/* No rows or no more rows */ } + DBG_INF("non NULL"); + DBG_RETURN(result->lengths); +} +/* }}} */ - previous_row = set->data_cursor - result->field_count; - for (i = 0; i < result->field_count; i++) { - result->lengths[i] = (Z_TYPE_P(previous_row[i]) == IS_NULL)? 0:Z_STRLEN_P(previous_row[i]); - } - return result->lengths; +/* {{{ mysqlnd_result_buffered_c::fetch_lengths */ +/* + Do lazy initialization for buffered results. As PHP strings have + length inside, this function makes not much sense in the context + of PHP, to be called as separate function. But let's have it for + completeness. +*/ +static unsigned long * +MYSQLND_METHOD(mysqlnd_result_buffered_c, fetch_lengths)(MYSQLND_RES_BUFFERED * const result TSRMLS_DC) +{ + const MYSQLND_RES_BUFFERED_C * set = (MYSQLND_RES_BUFFERED_C *) result; + DBG_ENTER("mysqlnd_result_buffered_c::fetch_lengths"); + + if (set->current_row > set->row_count || set->current_row == 0) { + DBG_INF("EOF"); + DBG_RETURN(NULL); /* No more rows, or no fetched row */ + } + DBG_INF("non NULL"); + DBG_RETURN(result->lengths); } /* }}} */ @@ -767,7 +874,7 @@ MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row_c)(MYSQLND_RES * result, voi /* {{{ mysqlnd_result_unbuffered::fetch_row */ static enum_func_status -MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row)(MYSQLND_RES * result, void * param, unsigned int flags, zend_bool * fetched_anything TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row)(MYSQLND_RES * result, void * param, const unsigned int flags, zend_bool * fetched_anything TSRMLS_DC) { enum_func_status ret; zval *row = (zval *) param; @@ -944,20 +1051,111 @@ static enum_func_status MYSQLND_METHOD(mysqlnd_result_buffered, fetch_row_c)(MYSQLND_RES * result, void * param, unsigned int flags, zend_bool * fetched_anything TSRMLS_DC) { MYSQLND_ROW_C * row = (MYSQLND_ROW_C *) param; - MYSQLND_RES_BUFFERED * set = result->stored_data; const MYSQLND_RES_METADATA * const meta = result->meta; unsigned int field_count = meta->field_count; enum_func_status ret = FAIL; - DBG_ENTER("mysqlnd_result_buffered::fetch_row_c"); + if (result->stored_data->type == MYSQLND_BUFFERED_TYPE_ZVAL) { + MYSQLND_RES_BUFFERED_ZVAL * set = (MYSQLND_RES_BUFFERED_ZVAL *) result->stored_data; + + /* If we haven't read everything */ + if (set->data_cursor && + (set->data_cursor - set->data) < (result->stored_data->row_count * field_count)) + { + zval **current_row = set->data_cursor; + unsigned int i; + + if (NULL == current_row[0]) { + uint64_t row_num = (set->data_cursor - set->data) / field_count; + enum_func_status rc = set->m.row_decoder(set->row_buffers[row_num], + current_row, + field_count, + meta->fields, + result->conn->options->int_and_float_native, + result->conn->stats TSRMLS_CC); + if (rc != PASS) { + DBG_RETURN(FAIL); + } + set->initialized_rows++; + for (i = 0; i < field_count; i++) { + /* + NULL fields are 0 length, 0 is not more than 0 + String of zero size, definitely can't be the next max_length. + Thus for NULL and zero-length we are quite efficient. + */ + if (Z_TYPE_P(current_row[i]) >= IS_STRING) { + unsigned long len = Z_STRLEN_P(current_row[i]); + if (meta->fields[i].max_length < len) { + meta->fields[i].max_length = len; + } + } + } + } + +/* BEGIN difference between normal normal fetch and _c */ + /* there is no conn handle in this function thus we can't set OOM in error_info */ + *row = mnd_malloc(field_count * sizeof(char *)); + if (*row) { + for (i = 0; i < field_count; i++) { + zval * data = current_row[i]; + + set->lengths[i] = (Z_TYPE_P(data) == IS_NULL)? 0:Z_STRLEN_P(data); + + if (Z_TYPE_P(data) != IS_NULL) { + convert_to_string(data); + (*row)[i] = Z_STRVAL_P(data); + } else { + (*row)[i] = NULL; + } + } + set->data_cursor += field_count; + MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_BUF); + } else { + SET_OOM_ERROR(*result->conn->error_info); + } +/* END difference between normal normal fetch and _c */ + + *fetched_anything = *row? TRUE:FALSE; + ret = *row? PASS:FAIL; + } else { + set->data_cursor = NULL; + DBG_INF("EOF reached"); + *fetched_anything = FALSE; + ret = PASS; + } + } else if (result->stored_data->type == MYSQLND_BUFFERED_TYPE_C) { + /* + We don't support _C with pdo because it uses the data in a different way - just references it. + We will either leak or give nirvana pointers + */ + *fetched_anything = FALSE; + DBG_RETURN(FAIL); + } + DBG_INF_FMT("ret=PASS fetched=%u", *fetched_anything); + DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ mysqlnd_result_buffered_zval::fetch_row */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_result_buffered_zval, fetch_row)(MYSQLND_RES * result, void * param, const unsigned int flags, zend_bool * fetched_anything TSRMLS_DC) +{ + zval * row = (zval *) param; + const MYSQLND_RES_METADATA * const meta = result->meta; + unsigned int field_count = meta->field_count; + enum_func_status ret = FAIL; + MYSQLND_RES_BUFFERED_ZVAL * set = (MYSQLND_RES_BUFFERED_ZVAL *) result->stored_data; + + DBG_ENTER("mysqlnd_result_buffered_zval::fetch_row"); + /* If we haven't read everything */ if (set->data_cursor && (set->data_cursor - set->data) < (set->row_count * field_count)) { - zval **current_row = set->data_cursor; - MYSQLND_FIELD * field = meta->fields; unsigned int i; + zval **current_row = set->data_cursor; if (NULL == current_row[0]) { uint64_t row_num = (set->data_cursor - set->data) / field_count; @@ -979,30 +1177,44 @@ MYSQLND_METHOD(mysqlnd_result_buffered, fetch_row_c)(MYSQLND_RES * result, void */ if (Z_TYPE_P(current_row[i]) >= IS_STRING) { unsigned long len = Z_STRLEN_P(current_row[i]); - if (field->max_length < len) { - field->max_length = len; + if (meta->fields[i].max_length < len) { + meta->fields[i].max_length = len; } } } } -/* BEGIN difference between normal normal fetch and _c */ - /* there is no conn handle in this function thus we can't set OOM in error_info */ - *row = mnd_malloc(field_count * sizeof(char *)); - if (ret) { - for (i = 0; i < field_count; i++) { - zval * data = current_row[i]; + for (i = 0; i < field_count; i++) { + zval * data = current_row[i]; - if (Z_TYPE_P(data) != IS_NULL) { - convert_to_string(data); - (*row)[i] = Z_STRVAL_P(data); + set->lengths[i] = (Z_TYPE_P(data) == IS_NULL)? 0:Z_STRLEN_P(data); + + if (flags & MYSQLND_FETCH_NUM) { + Z_ADDREF_P(data); + zend_hash_next_index_insert(Z_ARRVAL_P(row), &data, sizeof(zval *), NULL); + } + if (flags & MYSQLND_FETCH_ASSOC) { + /* zend_hash_quick_update needs length + trailing zero */ + /* QQ: Error handling ? */ + /* + zend_hash_quick_update does not check, as add_assoc_zval_ex do, whether + the index is a numeric and convert it to it. This however means constant + hashing of the column name, which is not needed as it can be precomputed. + */ + Z_ADDREF_P(data); + if (meta->zend_hash_keys[i].is_numeric == FALSE) { + zend_hash_quick_update(Z_ARRVAL_P(row), + meta->fields[i].name, + meta->fields[i].name_length + 1, + meta->zend_hash_keys[i].key, + (void *) &data, sizeof(zval *), NULL); } else { - (*row)[i] = NULL; + zend_hash_index_update(Z_ARRVAL_P(row), + meta->zend_hash_keys[i].key, + (void *) &data, sizeof(zval *), NULL); } } } -/* END difference between normal normal fetch and _c */ - set->data_cursor += field_count; MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_BUF); *fetched_anything = TRUE; @@ -1019,38 +1231,45 @@ MYSQLND_METHOD(mysqlnd_result_buffered, fetch_row_c)(MYSQLND_RES * result, void /* }}} */ -/* {{{ mysqlnd_result_buffered::fetch_row */ +/* {{{ mysqlnd_result_buffered_c::fetch_row */ static enum_func_status -MYSQLND_METHOD(mysqlnd_result_buffered, fetch_row)(MYSQLND_RES * result, void * param, unsigned int flags, zend_bool * fetched_anything TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_result_buffered_c, fetch_row)(MYSQLND_RES * result, void * param, const unsigned int flags, zend_bool * fetched_anything TSRMLS_DC) { zval * row = (zval *) param; - MYSQLND_RES_BUFFERED * set = result->stored_data; const MYSQLND_RES_METADATA * const meta = result->meta; unsigned int field_count = meta->field_count; enum_func_status ret = FAIL; - DBG_ENTER("mysqlnd_result_buffered::fetch_row"); + MYSQLND_RES_BUFFERED_C * set = (MYSQLND_RES_BUFFERED_C *) result->stored_data; + + DBG_ENTER("mysqlnd_result_buffered_c::fetch_row"); /* If we haven't read everything */ - if (set->data_cursor && - (set->data_cursor - set->data) < (set->row_count * field_count)) - { - zval **current_row = set->data_cursor; - MYSQLND_FIELD * field = meta->fields; + if (set->current_row < set->row_count) { + zval **current_row; + enum_func_status rc; unsigned int i; - if (NULL == current_row[0]) { - uint64_t row_num = (set->data_cursor - set->data) / field_count; - enum_func_status rc = set->m.row_decoder(set->row_buffers[row_num], - current_row, - field_count, - meta->fields, - result->conn->options->int_and_float_native, - result->conn->stats TSRMLS_CC); - if (rc != PASS) { - DBG_RETURN(FAIL); - } + current_row = mnd_emalloc(field_count * sizeof(zval *)); + if (!current_row) { + SET_OOM_ERROR(*result->conn->error_info); + DBG_RETURN(FAIL); + } + + rc = result->stored_data->m.row_decoder(result->stored_data->row_buffers[set->current_row], + current_row, + field_count, + meta->fields, + result->conn->options->int_and_float_native, + result->conn->stats TSRMLS_CC); + if (rc != PASS) { + DBG_RETURN(FAIL); + } + if (!(set->initialized[set->current_row >> 3] & (1 << (set->current_row & 7)))) { + set->initialized[set->current_row >> 3] |= (1 << (set->current_row & 7)); /* mark initialized */ + set->initialized_rows++; + for (i = 0; i < field_count; i++) { /* NULL fields are 0 length, 0 is not more than 0 @@ -1059,16 +1278,18 @@ MYSQLND_METHOD(mysqlnd_result_buffered, fetch_row)(MYSQLND_RES * result, void * */ if (Z_TYPE_P(current_row[i]) >= IS_STRING) { unsigned long len = Z_STRLEN_P(current_row[i]); - if (field->max_length < len) { - field->max_length = len; + if (meta->fields[i].max_length < len) { + meta->fields[i].max_length = len; } } } } - for (i = 0; i < field_count; i++, field++) { + for (i = 0; i < field_count; i++) { zval * data = current_row[i]; + set->lengths[i] = (Z_TYPE_P(data) == IS_NULL)? 0:Z_STRLEN_P(data); + if (flags & MYSQLND_FETCH_NUM) { Z_ADDREF_P(data); zend_hash_next_index_insert(Z_ARRVAL_P(row), &data, sizeof(zval *), NULL); @@ -1084,8 +1305,8 @@ MYSQLND_METHOD(mysqlnd_result_buffered, fetch_row)(MYSQLND_RES * result, void * Z_ADDREF_P(data); if (meta->zend_hash_keys[i].is_numeric == FALSE) { zend_hash_quick_update(Z_ARRVAL_P(row), - field->name, - field->name_length + 1, + meta->fields[i].name, + meta->fields[i].name_length + 1, meta->zend_hash_keys[i].key, (void *) &data, sizeof(zval *), NULL); } else { @@ -1094,17 +1315,28 @@ MYSQLND_METHOD(mysqlnd_result_buffered, fetch_row)(MYSQLND_RES * result, void * (void *) &data, sizeof(zval *), NULL); } } + /* + This will usually not destroy anything but decref. + However, if neither NUM nor ASSOC is set we will free memory cleanly and won't leak. + It also simplifies the handling of Z_ADDREF_P because we don't need to check if only + either NUM or ASSOC is set but not both. + */ + zval_ptr_dtor(&data); } - set->data_cursor += field_count; + mnd_efree(current_row); + set->current_row++; MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_BUF); *fetched_anything = TRUE; ret = PASS; } else { - set->data_cursor = NULL; - DBG_INF("EOF reached"); + if (set->current_row == set->row_count) { + set->current_row = set->row_count + 1; + } + DBG_INF_FMT("EOF reached. current_row=%llu", (unsigned long long) set->current_row); *fetched_anything = FALSE; ret = PASS; } + DBG_INF_FMT("ret=PASS fetched=%u", *fetched_anything); DBG_RETURN(ret); } @@ -1113,7 +1345,7 @@ MYSQLND_METHOD(mysqlnd_result_buffered, fetch_row)(MYSQLND_RES * result, void * /* {{{ mysqlnd_res::fetch_row */ static enum_func_status -MYSQLND_METHOD(mysqlnd_res, fetch_row)(MYSQLND_RES * result, void * param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_res, fetch_row)(MYSQLND_RES * result, void * param, const unsigned int flags, zend_bool *fetched_anything TSRMLS_DC) { const mysqlnd_fetch_row_func f = result->stored_data? result->stored_data->m.fetch_row:(result->unbuf? result->unbuf->m.fetch_row:NULL); if (f) { @@ -1131,6 +1363,7 @@ MYSQLND_METHOD(mysqlnd_res, fetch_row)(MYSQLND_RES * result, void * param, unsig enum_func_status MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data)(MYSQLND_CONN_DATA * const conn, MYSQLND_RES * result, MYSQLND_RES_METADATA * meta, + MYSQLND_MEMORY_POOL_CHUNK ***row_buffers, zend_bool binary_protocol TSRMLS_DC) { enum_func_status ret; @@ -1142,13 +1375,13 @@ MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data)(MYSQLND_CONN_DATA * const c set = result->stored_data; - if (!set) { + if (!set || !row_buffers) { ret = FAIL; goto end; } if (free_rows) { - set->row_buffers = mnd_emalloc((size_t)(free_rows * sizeof(MYSQLND_MEMORY_POOL_CHUNK *))); - if (!set->row_buffers) { + *row_buffers = mnd_pemalloc((size_t)(free_rows * sizeof(MYSQLND_MEMORY_POOL_CHUNK *)), 0); + if (!*row_buffers) { SET_OOM_ERROR(*conn->error_info); ret = FAIL; goto end; @@ -1184,16 +1417,16 @@ MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data)(MYSQLND_CONN_DATA * const c ret = FAIL; goto end; } - new_row_buffers = mnd_erealloc(set->row_buffers, (size_t)(total_allocated_rows * sizeof(MYSQLND_MEMORY_POOL_CHUNK *))); + new_row_buffers = mnd_perealloc(*row_buffers, (size_t)(total_allocated_rows * sizeof(MYSQLND_MEMORY_POOL_CHUNK *)), 0); if (!new_row_buffers) { SET_OOM_ERROR(*conn->error_info); ret = FAIL; goto end; } - set->row_buffers = new_row_buffers; + *row_buffers = new_row_buffers; } free_rows--; - set->row_buffers[set->row_count] = row_packet->row_buffer; + (*row_buffers)[set->row_count] = row_packet->row_buffer; set->row_count++; @@ -1228,7 +1461,7 @@ MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data)(MYSQLND_CONN_DATA * const c ret = FAIL; goto end; } - set->row_buffers = mnd_erealloc(set->row_buffers, (size_t) (set->row_count * sizeof(MYSQLND_MEMORY_POOL_CHUNK *))); + *row_buffers = mnd_perealloc(*row_buffers, (size_t) (set->row_count * sizeof(MYSQLND_MEMORY_POOL_CHUNK *)), 0); } if (conn->upsert_status->server_status & SERVER_MORE_RESULTS_EXISTS) { @@ -1247,7 +1480,7 @@ MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data)(MYSQLND_CONN_DATA * const c ret == PASS? "PASS":"FAIL", (uint) set->row_count, conn->upsert_status->warning_count, conn->upsert_status->server_status); end: PACKET_FREE(row_packet); - + DBG_INF_FMT("rows=%llu", (unsigned long long)result->stored_data->row_count); DBG_RETURN(ret); } /* }}} */ @@ -1260,6 +1493,7 @@ MYSQLND_METHOD(mysqlnd_res, store_result)(MYSQLND_RES * result, const unsigned int flags TSRMLS_DC) { enum_func_status ret; + MYSQLND_MEMORY_POOL_CHUNK ***row_buffers = NULL; DBG_ENTER("mysqlnd_res::store_result"); @@ -1270,13 +1504,23 @@ MYSQLND_METHOD(mysqlnd_res, store_result)(MYSQLND_RES * result, CONN_SET_STATE(conn, CONN_FETCHING_DATA); - result->stored_data = mysqlnd_result_buffered_init(result->field_count, flags & MYSQLND_STORE_PS, result->persistent TSRMLS_CC); - if (!result->stored_data) { - SET_OOM_ERROR(*conn->error_info); - DBG_RETURN(NULL); + if (flags & MYSQLND_STORE_NO_COPY) { + result->stored_data = (MYSQLND_RES_BUFFERED *) mysqlnd_result_buffered_zval_init(result->field_count, flags & MYSQLND_STORE_PS, result->persistent TSRMLS_CC); + if (!result->stored_data) { + SET_OOM_ERROR(*conn->error_info); + DBG_RETURN(NULL); + } + row_buffers = &result->stored_data->row_buffers; + } else if (flags & MYSQLND_STORE_COPY) { + result->stored_data = (MYSQLND_RES_BUFFERED *) mysqlnd_result_buffered_c_init(result->field_count, flags & MYSQLND_STORE_PS, result->persistent TSRMLS_CC); + if (!result->stored_data) { + SET_OOM_ERROR(*conn->error_info); + DBG_RETURN(NULL); + } + row_buffers = &result->stored_data->row_buffers; } + ret = result->m.store_result_fetch_data(conn, result, result->meta, row_buffers, flags & MYSQLND_STORE_PS TSRMLS_CC); - ret = result->m.store_result_fetch_data(conn, result, result->meta, flags & MYSQLND_STORE_PS TSRMLS_CC); if (FAIL == ret) { if (result->stored_data) { COPY_CLIENT_ERROR(*conn->error_info, result->stored_data->error_info); @@ -1286,24 +1530,30 @@ MYSQLND_METHOD(mysqlnd_res, store_result)(MYSQLND_RES * result, DBG_RETURN(NULL); } else { /* Overflow ? */ - MYSQLND_RES_METADATA * meta = result->meta; - MYSQLND_RES_BUFFERED * set = result->stored_data; - if (set->row_count) { - /* don't try to allocate more than possible - mnd_XXalloc expects size_t, and it can have narrower range than uint64_t */ - if (set->row_count * meta->field_count * sizeof(zval *) > SIZE_MAX) { - SET_OOM_ERROR(*conn->error_info); - DBG_RETURN(NULL); - } - /* if pecalloc is used valgrind barks gcc version 4.3.1 20080507 (prerelease) [gcc-4_3-branch revision 135036] (SUSE Linux) */ - set->data = mnd_emalloc((size_t)(set->row_count * meta->field_count * sizeof(zval *))); - if (!set->data) { - SET_OOM_ERROR(*conn->error_info); - DBG_RETURN(NULL); + if (flags & MYSQLND_STORE_NO_COPY) { + MYSQLND_RES_METADATA * meta = result->meta; + MYSQLND_RES_BUFFERED_ZVAL * set = (MYSQLND_RES_BUFFERED_ZVAL *) result->stored_data; + if (set->row_count) { + /* don't try to allocate more than possible - mnd_XXalloc expects size_t, and it can have narrower range than uint64_t */ + if (set->row_count * meta->field_count * sizeof(zval *) > SIZE_MAX) { + SET_OOM_ERROR(*conn->error_info); + DBG_RETURN(NULL); + } + /* if pecalloc is used valgrind barks gcc version 4.3.1 20080507 (prerelease) [gcc-4_3-branch revision 135036] (SUSE Linux) */ + set->data = mnd_emalloc((size_t)(set->row_count * meta->field_count * sizeof(zval *))); + if (!set->data) { + SET_OOM_ERROR(*conn->error_info); + DBG_RETURN(NULL); + } + memset(set->data, 0, (size_t)(set->row_count * meta->field_count * sizeof(zval *))); } - memset(set->data, 0, (size_t)(set->row_count * meta->field_count * sizeof(zval *))); + /* Position at the first row */ + set->data_cursor = set->data; + } else if (flags & MYSQLND_STORE_COPY) { + MYSQLND_RES_BUFFERED_C * set = (MYSQLND_RES_BUFFERED_C *) result->stored_data; + set->current_row = 0; + set->initialized = mnd_pecalloc((set->row_count / 8) + 1, sizeof(zend_uchar), set->persistent); /* +1 for safety */ } - /* Position at the first row */ - set->data_cursor = set->data; } /* libmysql's documentation says it should be so for SELECT statements */ @@ -1370,19 +1620,37 @@ MYSQLND_METHOD(mysqlnd_res, data_seek)(MYSQLND_RES * const result, const uint64_ /* }}} */ -/* {{{ mysqlnd_result_buffered::data_seek */ +/* {{{ mysqlnd_result_buffered_zval::data_seek */ static enum_func_status -MYSQLND_METHOD(mysqlnd_result_buffered, data_seek)(MYSQLND_RES_BUFFERED * const result, const uint64_t row TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_result_buffered_zval, data_seek)(MYSQLND_RES_BUFFERED * const result, const uint64_t row TSRMLS_DC) { - DBG_ENTER("mysqlnd_result_buffered::data_seek"); + MYSQLND_RES_BUFFERED_ZVAL * set = (MYSQLND_RES_BUFFERED_ZVAL *) result; + DBG_ENTER("mysqlnd_result_buffered_zval::data_seek"); /* libmysql just moves to the end, it does traversing of a linked list */ - if (row >= result->row_count) { - result->data_cursor = NULL; + if (row >= set->row_count) { + set->data_cursor = NULL; } else { - result->data_cursor = result->data + row * result->field_count; + set->data_cursor = set->data + row * result->field_count; } + DBG_RETURN(PASS); +} +/* }}} */ + +/* {{{ mysqlnd_result_buffered_c::data_seek */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_result_buffered_c, data_seek)(MYSQLND_RES_BUFFERED * const result, const uint64_t row TSRMLS_DC) +{ + MYSQLND_RES_BUFFERED_C * set = (MYSQLND_RES_BUFFERED_C *) result; + DBG_ENTER("mysqlnd_result_buffered_c::data_seek"); + + /* libmysql just moves to the end, it does traversing of a linked list */ + if (row >= set->row_count) { + set->current_row = set->row_count; + } else { + set->current_row = row; + } DBG_RETURN(PASS); } /* }}} */ @@ -1539,7 +1807,7 @@ MYSQLND_METHOD(mysqlnd_res, field_tell)(const MYSQLND_RES * const result TSRMLS_ /* {{{ mysqlnd_res::fetch_into */ static void -MYSQLND_METHOD(mysqlnd_res, fetch_into)(MYSQLND_RES * result, unsigned int flags, +MYSQLND_METHOD(mysqlnd_res, fetch_into)(MYSQLND_RES * result, const unsigned int flags, zval *return_value, enum_mysqlnd_extension extension TSRMLS_DC ZEND_FILE_LINE_DC) { @@ -1585,7 +1853,7 @@ MYSQLND_METHOD(mysqlnd_res, fetch_row_c)(MYSQLND_RES * result TSRMLS_DC) MYSQLND_ROW_C ret = NULL; DBG_ENTER("mysqlnd_res::fetch_row_c"); - if (result->stored_data && result->stored_data->m.fetch_row == MYSQLND_METHOD(mysqlnd_result_buffered, fetch_row)) { + if (result->stored_data && result->stored_data->m.fetch_row == MYSQLND_METHOD(mysqlnd_result_buffered_zval, fetch_row)) { MYSQLND_METHOD(mysqlnd_result_buffered, fetch_row_c)(result, (void *) &ret, 0, &fetched_anything TSRMLS_CC); } else if (result->unbuf && result->unbuf->m.fetch_row == MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row)) { MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row_c)(result, (void *) &ret, 0, &fetched_anything TSRMLS_CC); @@ -1600,7 +1868,7 @@ MYSQLND_METHOD(mysqlnd_res, fetch_row_c)(MYSQLND_RES * result TSRMLS_DC) /* {{{ mysqlnd_res::fetch_all */ static void -MYSQLND_METHOD(mysqlnd_res, fetch_all)(MYSQLND_RES * result, unsigned int flags, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC) +MYSQLND_METHOD(mysqlnd_res, fetch_all)(MYSQLND_RES * result, const unsigned int flags, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC) { zval *row; ulong i = 0; @@ -1713,12 +1981,12 @@ MYSQLND_CLASS_METHODS_END; MYSQLND_CLASS_METHODS_START(mysqlnd_result_buffered) - MYSQLND_METHOD(mysqlnd_result_buffered, fetch_row), + NULL, /* fetch_row */ NULL, /* row_decoder */ MYSQLND_METHOD(mysqlnd_result_buffered, num_rows), - MYSQLND_METHOD(mysqlnd_result_buffered, fetch_lengths), - MYSQLND_METHOD(mysqlnd_result_buffered, data_seek), - MYSQLND_METHOD(mysqlnd_result_buffered, initialize_result_set_rest), + NULL, /* fetch_lengths */ + NULL, /* data_seek */ + NULL, /* initialize_result_set_rest */ MYSQLND_METHOD(mysqlnd_result_buffered, free_result) MYSQLND_CLASS_METHODS_END; @@ -1778,7 +2046,7 @@ mysqlnd_result_unbuffered_init(unsigned int field_count, zend_bool ps, zend_bool ret->m.fetch_lengths = NULL; /* makes no sense */ ret->m.row_decoder = php_mysqlnd_rowp_read_binary_protocol; } else { - ret->m.row_decoder = php_mysqlnd_rowp_read_text_protocol; + ret->m.row_decoder = php_mysqlnd_rowp_read_text_protocol_zval; } DBG_RETURN(ret); @@ -1786,14 +2054,57 @@ mysqlnd_result_unbuffered_init(unsigned int field_count, zend_bool ps, zend_bool /* }}} */ -/* {{{ mysqlnd_result_buffered_init */ -PHPAPI MYSQLND_RES_BUFFERED * -mysqlnd_result_buffered_init(unsigned int field_count, zend_bool ps, zend_bool persistent TSRMLS_DC) +/* {{{ mysqlnd_result_buffered_zval_init */ +PHPAPI MYSQLND_RES_BUFFERED_ZVAL * +mysqlnd_result_buffered_zval_init(unsigned int field_count, zend_bool ps, zend_bool persistent TSRMLS_DC) +{ + size_t alloc_size = sizeof(MYSQLND_RES_BUFFERED_ZVAL) + mysqlnd_plugin_count() * sizeof(void *); + MYSQLND_RES_BUFFERED_ZVAL * ret = mnd_pecalloc(1, alloc_size, persistent); + + DBG_ENTER("mysqlnd_result_buffered_zval_init"); + + if (!ret) { + DBG_RETURN(NULL); + } + if (!(ret->lengths = mnd_pecalloc(field_count, sizeof(unsigned long), persistent))) { + mnd_pefree(ret, persistent); + DBG_RETURN(NULL); + } + if (!(ret->result_set_memory_pool = mysqlnd_mempool_create(MYSQLND_G(mempool_default_size) TSRMLS_CC))) { + mnd_efree(ret->lengths); + mnd_pefree(ret, persistent); + DBG_RETURN(NULL); + } + + ret->persistent = persistent; + ret->field_count= field_count; + ret->ps = ps; + ret->m = *mysqlnd_result_buffered_get_methods(); + ret->type = MYSQLND_BUFFERED_TYPE_ZVAL; + + if (ps) { + ret->m.fetch_lengths = NULL; /* makes no sense */ + ret->m.row_decoder = php_mysqlnd_rowp_read_binary_protocol; + } else { + ret->m.row_decoder = php_mysqlnd_rowp_read_text_protocol_zval; + } + ret->m.fetch_row = MYSQLND_METHOD(mysqlnd_result_buffered_zval, fetch_row); + ret->m.fetch_lengths = MYSQLND_METHOD(mysqlnd_result_buffered_zval, fetch_lengths); + ret->m.data_seek = MYSQLND_METHOD(mysqlnd_result_buffered_zval, data_seek); + ret->m.initialize_result_set_rest = MYSQLND_METHOD(mysqlnd_result_buffered_zval, initialize_result_set_rest); + DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ mysqlnd_result_buffered_c_init */ +PHPAPI MYSQLND_RES_BUFFERED_C * +mysqlnd_result_buffered_c_init(unsigned int field_count, zend_bool ps, zend_bool persistent TSRMLS_DC) { - size_t alloc_size = sizeof(MYSQLND_RES_BUFFERED) + mysqlnd_plugin_count() * sizeof(void *); - MYSQLND_RES_BUFFERED * ret = mnd_pecalloc(1, alloc_size, persistent); + size_t alloc_size = sizeof(MYSQLND_RES_BUFFERED_C) + mysqlnd_plugin_count() * sizeof(void *); + MYSQLND_RES_BUFFERED_C * ret = mnd_pecalloc(1, alloc_size, persistent); - DBG_ENTER("mysqlnd_result_buffered_init"); + DBG_ENTER("mysqlnd_result_buffered_c_init"); if (!ret) { DBG_RETURN(NULL); @@ -1812,13 +2123,18 @@ mysqlnd_result_buffered_init(unsigned int field_count, zend_bool ps, zend_bool p ret->field_count= field_count; ret->ps = ps; ret->m = *mysqlnd_result_buffered_get_methods(); + ret->type = MYSQLND_BUFFERED_TYPE_C; if (ps) { ret->m.fetch_lengths = NULL; /* makes no sense */ ret->m.row_decoder = php_mysqlnd_rowp_read_binary_protocol; } else { - ret->m.row_decoder = php_mysqlnd_rowp_read_text_protocol; + ret->m.row_decoder = php_mysqlnd_rowp_read_text_protocol_c; } + ret->m.fetch_row = MYSQLND_METHOD(mysqlnd_result_buffered_c, fetch_row); + ret->m.fetch_lengths = MYSQLND_METHOD(mysqlnd_result_buffered_c, fetch_lengths); + ret->m.data_seek = MYSQLND_METHOD(mysqlnd_result_buffered_c, data_seek); + ret->m.initialize_result_set_rest = MYSQLND_METHOD(mysqlnd_result_buffered_c, initialize_result_set_rest); DBG_RETURN(ret); } diff --git a/ext/mysqlnd/mysqlnd_result.h b/ext/mysqlnd/mysqlnd_result.h index 0a54ea045..f7e22a1d8 100644 --- a/ext/mysqlnd/mysqlnd_result.h +++ b/ext/mysqlnd/mysqlnd_result.h @@ -25,7 +25,8 @@ PHPAPI MYSQLND_RES * mysqlnd_result_init(unsigned int field_count, zend_bool persistent TSRMLS_DC); PHPAPI MYSQLND_RES_UNBUFFERED * mysqlnd_result_unbuffered_init(unsigned int field_count, zend_bool ps, zend_bool persistent TSRMLS_DC); -PHPAPI MYSQLND_RES_BUFFERED * mysqlnd_result_buffered_init(unsigned int field_count, zend_bool ps, zend_bool persistent TSRMLS_DC); +PHPAPI MYSQLND_RES_BUFFERED_ZVAL * mysqlnd_result_buffered_zval_init(unsigned int field_count, zend_bool ps, zend_bool persistent TSRMLS_DC); +PHPAPI MYSQLND_RES_BUFFERED_C * mysqlnd_result_buffered_c_init(unsigned int field_count, zend_bool ps, zend_bool persistent TSRMLS_DC); enum_func_status mysqlnd_query_read_result_set_header(MYSQLND_CONN_DATA * conn, MYSQLND_STMT * stmt TSRMLS_DC); diff --git a/ext/mysqlnd/mysqlnd_reverse_api.c b/ext/mysqlnd/mysqlnd_reverse_api.c index b461b4942..d8b1a4151 100644 --- a/ext/mysqlnd/mysqlnd_reverse_api.c +++ b/ext/mysqlnd/mysqlnd_reverse_api.c @@ -68,7 +68,7 @@ mysqlnd_reverse_api_register_api(MYSQLND_REVERSE_API * apiext TSRMLS_DC) /* {{{ zval_to_mysqlnd */ PHPAPI MYSQLND * -zval_to_mysqlnd(zval * zv TSRMLS_DC) +zval_to_mysqlnd(zval * zv, const unsigned int client_api_capabilities, unsigned int * save_client_api_capabilities TSRMLS_DC) { MYSQLND * retval; MYSQLND_REVERSE_API ** elem; @@ -80,6 +80,9 @@ zval_to_mysqlnd(zval * zv TSRMLS_DC) if ((*elem)->conversion_cb) { retval = (*elem)->conversion_cb(zv TSRMLS_CC); if (retval) { + if (retval->data) { + *save_client_api_capabilities = retval->data->m->negotiate_client_api_capabilities(retval->data, client_api_capabilities TSRMLS_CC); + } return retval; } } diff --git a/ext/mysqlnd/mysqlnd_reverse_api.h b/ext/mysqlnd/mysqlnd_reverse_api.h index 028a1f6b1..79c0feb60 100644 --- a/ext/mysqlnd/mysqlnd_reverse_api.h +++ b/ext/mysqlnd/mysqlnd_reverse_api.h @@ -34,8 +34,7 @@ PHPAPI void mysqlnd_reverse_api_end(TSRMLS_D); PHPAPI HashTable * mysqlnd_reverse_api_get_api_list(TSRMLS_D); PHPAPI void mysqlnd_reverse_api_register_api(MYSQLND_REVERSE_API * apiext TSRMLS_DC); - -PHPAPI MYSQLND * zval_to_mysqlnd(zval * zv TSRMLS_DC); +PHPAPI MYSQLND * zval_to_mysqlnd(zval * zv, const unsigned int client_api_capabilities, unsigned int * save_client_api_capabilities TSRMLS_DC); #endif /* MYSQLND_REVERSE_API_H */ diff --git a/ext/mysqlnd/mysqlnd_structs.h b/ext/mysqlnd/mysqlnd_structs.h index 65f1c57a0..66920e47b 100644 --- a/ext/mysqlnd/mysqlnd_structs.h +++ b/ext/mysqlnd/mysqlnd_structs.h @@ -235,7 +235,9 @@ typedef struct st_mysqlnd_param_bind MYSQLND_PARAM_BIND; typedef struct st_mysqlnd_result_bind MYSQLND_RESULT_BIND; typedef struct st_mysqlnd_result_metadata MYSQLND_RES_METADATA; -typedef struct st_mysqlnd_buffered_result MYSQLND_RES_BUFFERED; +typedef struct st_mysqlnd_buffered_result_parent MYSQLND_RES_BUFFERED; +typedef struct st_mysqlnd_buffered_result_zval MYSQLND_RES_BUFFERED_ZVAL; +typedef struct st_mysqlnd_buffered_result_c MYSQLND_RES_BUFFERED_C; typedef struct st_mysqlnd_unbuffered_result MYSQLND_RES_UNBUFFERED; typedef struct st_mysqlnd_debug MYSQLND_DEBUG; @@ -244,7 +246,7 @@ typedef struct st_mysqlnd_debug MYSQLND_DEBUG; typedef MYSQLND_RES* (*mysqlnd_stmt_use_or_store_func)(MYSQLND_STMT * const TSRMLS_DC); typedef enum_func_status (*mysqlnd_fetch_row_func)(MYSQLND_RES *result, void * param, - unsigned int flags, + const unsigned int flags, zend_bool * fetched_anything TSRMLS_DC); @@ -500,6 +502,11 @@ typedef struct st_mysqlnd_authentication_plugin * (*func_mysqlnd_conn_data__fetc typedef enum_func_status (*func_mysqlnd_conn_data__set_client_option_2d)(MYSQLND_CONN_DATA * const conn, enum mysqlnd_option option, const char * const key, const char * const value TSRMLS_DC); + +typedef unsigned int (*func_mysqlnd_conn_data__negotiate_client_api_capabilities)(MYSQLND_CONN_DATA * const conn, const unsigned int flags TSRMLS_DC); +typedef unsigned int (*func_mysqlnd_conn_data__get_client_api_capabilities)(const MYSQLND_CONN_DATA * const conn TSRMLS_DC); + + struct st_mysqlnd_conn_data_methods { func_mysqlnd_conn_data__init init; @@ -591,6 +598,9 @@ struct st_mysqlnd_conn_data_methods func_mysqlnd_conn_data__fetch_auth_plugin_by_name fetch_auth_plugin_by_name; func_mysqlnd_conn_data__set_client_option_2d set_client_option_2d; + + func_mysqlnd_conn_data__negotiate_client_api_capabilities negotiate_client_api_capabilities; + func_mysqlnd_conn_data__get_client_api_capabilities get_client_api_capabilities; }; @@ -615,9 +625,9 @@ typedef enum_func_status (*func_mysqlnd_res__row_decoder)(MYSQLND_MEMORY_POOL_CH typedef MYSQLND_RES * (*func_mysqlnd_res__use_result)(MYSQLND_RES * const result, zend_bool ps_protocol TSRMLS_DC); typedef MYSQLND_RES * (*func_mysqlnd_res__store_result)(MYSQLND_RES * result, MYSQLND_CONN_DATA * const conn, const unsigned int flags TSRMLS_DC); -typedef void (*func_mysqlnd_res__fetch_into)(MYSQLND_RES *result, unsigned int flags, zval *return_value, enum_mysqlnd_extension ext TSRMLS_DC ZEND_FILE_LINE_DC); +typedef void (*func_mysqlnd_res__fetch_into)(MYSQLND_RES *result, const unsigned int flags, zval *return_value, enum_mysqlnd_extension ext TSRMLS_DC ZEND_FILE_LINE_DC); typedef MYSQLND_ROW_C (*func_mysqlnd_res__fetch_row_c)(MYSQLND_RES *result TSRMLS_DC); -typedef void (*func_mysqlnd_res__fetch_all)(MYSQLND_RES *result, unsigned int flags, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC); +typedef void (*func_mysqlnd_res__fetch_all)(MYSQLND_RES *result, const unsigned int flags, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC); typedef void (*func_mysqlnd_res__fetch_field_data)(MYSQLND_RES *result, unsigned int offset, zval *return_value TSRMLS_DC); typedef uint64_t (*func_mysqlnd_res__num_rows)(const MYSQLND_RES * const result TSRMLS_DC); typedef unsigned int (*func_mysqlnd_res__num_fields)(const MYSQLND_RES * const result TSRMLS_DC); @@ -631,7 +641,7 @@ typedef const MYSQLND_FIELD *(*func_mysqlnd_res__fetch_fields)(MYSQLND_RES * con typedef enum_func_status (*func_mysqlnd_res__read_result_metadata)(MYSQLND_RES * result, MYSQLND_CONN_DATA * conn TSRMLS_DC); typedef unsigned long * (*func_mysqlnd_res__fetch_lengths)(MYSQLND_RES * const result TSRMLS_DC); -typedef enum_func_status (*func_mysqlnd_res__store_result_fetch_data)(MYSQLND_CONN_DATA * const conn, MYSQLND_RES * result, MYSQLND_RES_METADATA *meta, zend_bool binary_protocol TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_res__store_result_fetch_data)(MYSQLND_CONN_DATA * const conn, MYSQLND_RES * result, MYSQLND_RES_METADATA * meta, MYSQLND_MEMORY_POOL_CHUNK *** row_buffers, zend_bool binary_protocol TSRMLS_DC); typedef void (*func_mysqlnd_res__free_result_buffers)(MYSQLND_RES * result TSRMLS_DC); /* private */ typedef enum_func_status (*func_mysqlnd_res__free_result)(MYSQLND_RES * result, zend_bool implicit TSRMLS_DC); @@ -947,9 +957,6 @@ struct st_mysqlnd_connection_data /* Temporal storage for mysql_query */ unsigned int field_count; - /* persistent connection */ - zend_bool persistent; - /* options */ MYSQLND_OPTIONS * options; MYSQLND_OPTIONS options_impl; @@ -957,7 +964,12 @@ struct st_mysqlnd_connection_data /* stats */ MYSQLND_STATS * stats; + unsigned int client_api_capabilities; + struct st_mysqlnd_conn_data_methods * m; + + /* persistent connection */ + zend_bool persistent; }; @@ -991,31 +1003,51 @@ struct st_mysqlnd_result_metadata }; -struct st_mysqlnd_buffered_result +#define def_mysqlnd_buffered_result_parent \ + MYSQLND_MEMORY_POOL_CHUNK **row_buffers; \ + uint64_t row_count; \ + uint64_t initialized_rows; \ + \ + /* Column lengths of current row - both buffered and unbuffered. For buffered results it duplicates the data found in **data */ \ + unsigned long *lengths; \ + \ + MYSQLND_MEMORY_POOL *result_set_memory_pool; \ + \ + unsigned int references; \ + \ + MYSQLND_ERROR_INFO error_info; \ + \ + unsigned int field_count; \ + zend_bool ps; \ + zend_bool persistent; \ + struct st_mysqlnd_result_buffered_methods m; \ + enum mysqlnd_buffered_type type; \ + void * unused1; \ + void * unused2; \ + void * unused3 + + +struct st_mysqlnd_buffered_result_parent { - zval **data; - zval **data_cursor; - MYSQLND_MEMORY_POOL_CHUNK **row_buffers; - uint64_t row_count; - uint64_t initialized_rows; + def_mysqlnd_buffered_result_parent; +}; - /* - Column lengths of current row - both buffered and unbuffered. - For buffered results it duplicates the data found in **data - */ - unsigned long *lengths; - MYSQLND_MEMORY_POOL *result_set_memory_pool; +struct st_mysqlnd_buffered_result_zval +{ + def_mysqlnd_buffered_result_parent; - unsigned int references; + zval **data; + zval **data_cursor; +}; - MYSQLND_ERROR_INFO error_info; - unsigned int field_count; - zend_bool ps; - zend_bool persistent; +struct st_mysqlnd_buffered_result_c +{ + def_mysqlnd_buffered_result_parent; - struct st_mysqlnd_result_buffered_methods m; + zend_uchar *initialized; /* every row is a single bit */ + uint64_t current_row; }; diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.c b/ext/mysqlnd/mysqlnd_wireprotocol.c index dfdba4084..ecce71d9c 100644 --- a/ext/mysqlnd/mysqlnd_wireprotocol.c +++ b/ext/mysqlnd/mysqlnd_wireprotocol.c @@ -1572,13 +1572,13 @@ php_mysqlnd_rowp_read_binary_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zv } /* }}} */ - /* {{{ php_mysqlnd_rowp_read_text_protocol */ enum_func_status -php_mysqlnd_rowp_read_text_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields, +php_mysqlnd_rowp_read_text_protocol_aux(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields, unsigned int field_count, const MYSQLND_FIELD * fields_metadata, - zend_bool as_int_or_float, MYSQLND_STATS * stats TSRMLS_DC) + zend_bool as_int_or_float, zend_bool copy_data, MYSQLND_STATS * stats TSRMLS_DC) { + unsigned int i; zend_bool last_field_was_string = FALSE; zval **current_field, **end_field, **start_field; @@ -1586,7 +1586,7 @@ php_mysqlnd_rowp_read_text_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval size_t data_size = row_buffer->app; zend_uchar * bit_area = (zend_uchar*) row_buffer->ptr + data_size + 1; /* we allocate from here */ - DBG_ENTER("php_mysqlnd_rowp_read_text_protocol"); + DBG_ENTER("php_mysqlnd_rowp_read_text_protocol_aux"); if (!fields) { DBG_RETURN(FAIL); @@ -1608,7 +1608,7 @@ php_mysqlnd_rowp_read_text_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval /* php_mysqlnd_net_field_length() call should be after *this_field_len_pos = p; */ unsigned long len = php_mysqlnd_net_field_length(&p); - if (current_field > start_field && last_field_was_string) { + if (copy_data == FALSE && current_field > start_field && last_field_was_string) { /* Normal queries: We have to put \0 now to the end of the previous field, if it was @@ -1733,22 +1733,22 @@ php_mysqlnd_rowp_read_text_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval p -= len; if (Z_TYPE_PP(current_field) == IS_LONG) { bit_area += 1 + sprintf((char *)start, "%ld", Z_LVAL_PP(current_field)); - ZVAL_STRINGL(*current_field, (char *) start, bit_area - start - 1, 0); + ZVAL_STRINGL(*current_field, (char *) start, bit_area - start - 1, copy_data); } else if (Z_TYPE_PP(current_field) == IS_STRING){ memcpy(bit_area, Z_STRVAL_PP(current_field), Z_STRLEN_PP(current_field)); bit_area += Z_STRLEN_PP(current_field); *bit_area++ = '\0'; zval_dtor(*current_field); - ZVAL_STRINGL(*current_field, (char *) start, bit_area - start - 1, 0); + ZVAL_STRINGL(*current_field, (char *) start, bit_area - start - 1, copy_data); } } else { - ZVAL_STRINGL(*current_field, (char *)p, len, 0); + ZVAL_STRINGL(*current_field, (char *)p, len, copy_data); } p += len; last_field_was_string = TRUE; } } - if (last_field_was_string) { + if (copy_data == FALSE && last_field_was_string) { /* Normal queries: The buffer has one more byte at the end, because we need it */ row_buffer->ptr[data_size] = '\0'; } @@ -1758,6 +1758,36 @@ php_mysqlnd_rowp_read_text_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval /* }}} */ +/* {{{ php_mysqlnd_rowp_read_text_protocol_zval */ +enum_func_status +php_mysqlnd_rowp_read_text_protocol_zval(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields, + unsigned int field_count, const MYSQLND_FIELD * fields_metadata, + zend_bool as_int_or_float, MYSQLND_STATS * stats TSRMLS_DC) +{ + enum_func_status ret; + DBG_ENTER("php_mysqlnd_rowp_read_text_protocol_zval"); + ret = php_mysqlnd_rowp_read_text_protocol_aux(row_buffer, fields, field_count, fields_metadata, as_int_or_float, FALSE, stats TSRMLS_CC); + DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ php_mysqlnd_rowp_read_text_protocol_c */ +enum_func_status +php_mysqlnd_rowp_read_text_protocol_c(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields, + unsigned int field_count, const MYSQLND_FIELD * fields_metadata, + zend_bool as_int_or_float, MYSQLND_STATS * stats TSRMLS_DC) +{ + enum_func_status ret; + DBG_ENTER("php_mysqlnd_rowp_read_text_protocol_c"); + ret = php_mysqlnd_rowp_read_text_protocol_aux(row_buffer, fields, field_count, fields_metadata, as_int_or_float, TRUE, stats TSRMLS_CC); + DBG_RETURN(ret); +} +/* }}} */ + + + + /* {{{ php_mysqlnd_rowp_read */ /* if normal statements => packet->fields is created by this function, diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.h b/ext/mysqlnd/mysqlnd_wireprotocol.h index be71d0650..7e8b9624a 100644 --- a/ext/mysqlnd/mysqlnd_wireprotocol.h +++ b/ext/mysqlnd/mysqlnd_wireprotocol.h @@ -312,7 +312,11 @@ enum_func_status php_mysqlnd_rowp_read_binary_protocol(MYSQLND_MEMORY_POOL_CHUNK zend_bool as_int_or_float, MYSQLND_STATS * stats TSRMLS_DC); -enum_func_status php_mysqlnd_rowp_read_text_protocol(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields, +enum_func_status php_mysqlnd_rowp_read_text_protocol_zval(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields, + unsigned int field_count, const MYSQLND_FIELD * fields_metadata, + zend_bool as_int_or_float, MYSQLND_STATS * stats TSRMLS_DC); + +enum_func_status php_mysqlnd_rowp_read_text_protocol_c(MYSQLND_MEMORY_POOL_CHUNK * row_buffer, zval ** fields, unsigned int field_count, const MYSQLND_FIELD * fields_metadata, zend_bool as_int_or_float, MYSQLND_STATS * stats TSRMLS_DC); diff --git a/ext/mysqlnd/php_mysqlnd.c b/ext/mysqlnd/php_mysqlnd.c index 1a4e67f5e..c2c53604e 100644 --- a/ext/mysqlnd/php_mysqlnd.c +++ b/ext/mysqlnd/php_mysqlnd.c @@ -111,7 +111,7 @@ mysqlnd_minfo_dump_api_plugins(smart_str * buffer TSRMLS_DC) MYSQLND_REVERSE_API **ext; for (zend_hash_internal_pointer_reset_ex(ht, &pos); - zend_hash_get_current_data_ex(ht, (void **) &ext, &pos); + zend_hash_get_current_data_ex(ht, (void **) &ext, &pos) == SUCCESS; zend_hash_move_forward_ex(ht, &pos) ) { if (buffer->len) { @@ -209,6 +209,7 @@ static PHP_GINIT_FUNCTION(mysqlnd) mysqlnd_globals->debug_calloc_fail_threshold = -1; mysqlnd_globals->debug_realloc_fail_threshold = -1; mysqlnd_globals->sha256_server_public_key = NULL; + mysqlnd_globals->fetch_data_copy = FALSE; } /* }}} */ @@ -227,8 +228,8 @@ static PHP_INI_MH(OnUpdateNetCmdBufferSize) /* {{{ PHP_INI_BEGIN */ PHP_INI_BEGIN() - STD_PHP_INI_BOOLEAN("mysqlnd.collect_statistics", "1", PHP_INI_ALL, OnUpdateBool, collect_statistics, zend_mysqlnd_globals, mysqlnd_globals) - STD_PHP_INI_BOOLEAN("mysqlnd.collect_memory_statistics", "0", PHP_INI_SYSTEM, OnUpdateBool, collect_memory_statistics, zend_mysqlnd_globals, mysqlnd_globals) + STD_PHP_INI_BOOLEAN("mysqlnd.collect_statistics", "1", PHP_INI_ALL, OnUpdateBool, collect_statistics, zend_mysqlnd_globals, mysqlnd_globals) + STD_PHP_INI_BOOLEAN("mysqlnd.collect_memory_statistics","0",PHP_INI_SYSTEM, OnUpdateBool, collect_memory_statistics, zend_mysqlnd_globals, mysqlnd_globals) STD_PHP_INI_ENTRY("mysqlnd.debug", NULL, PHP_INI_SYSTEM, OnUpdateString, debug, zend_mysqlnd_globals, mysqlnd_globals) STD_PHP_INI_ENTRY("mysqlnd.trace_alloc", NULL, PHP_INI_SYSTEM, OnUpdateString, trace_alloc_settings, zend_mysqlnd_globals, mysqlnd_globals) STD_PHP_INI_ENTRY("mysqlnd.net_cmd_buffer_size", MYSQLND_NET_CMD_BUFFER_MIN_SIZE_STR, PHP_INI_ALL, OnUpdateNetCmdBufferSize, net_cmd_buffer_size, zend_mysqlnd_globals, mysqlnd_globals) @@ -237,7 +238,7 @@ PHP_INI_BEGIN() STD_PHP_INI_ENTRY("mysqlnd.log_mask", "0", PHP_INI_ALL, OnUpdateLong, log_mask, zend_mysqlnd_globals, mysqlnd_globals) STD_PHP_INI_ENTRY("mysqlnd.mempool_default_size","16000", PHP_INI_ALL, OnUpdateLong, mempool_default_size, zend_mysqlnd_globals, mysqlnd_globals) STD_PHP_INI_ENTRY("mysqlnd.sha256_server_public_key",NULL, PHP_INI_PERDIR, OnUpdateString, sha256_server_public_key, zend_mysqlnd_globals, mysqlnd_globals) - + STD_PHP_INI_BOOLEAN("mysqlnd.fetch_data_copy", "0", PHP_INI_ALL, OnUpdateBool, fetch_data_copy, zend_mysqlnd_globals, mysqlnd_globals) #if PHP_DEBUG STD_PHP_INI_ENTRY("mysqlnd.debug_emalloc_fail_threshold","-1", PHP_INI_SYSTEM, OnUpdateLong, debug_emalloc_fail_threshold, zend_mysqlnd_globals, mysqlnd_globals) STD_PHP_INI_ENTRY("mysqlnd.debug_ecalloc_fail_threshold","-1", PHP_INI_SYSTEM, OnUpdateLong, debug_ecalloc_fail_threshold, zend_mysqlnd_globals, mysqlnd_globals) diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 55f856be5..faa3641eb 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -2209,8 +2209,10 @@ static void accel_fast_zval_ptr_dtor(zval **zval_ptr) #else switch (Z_TYPE_P(zvalue) & ~IS_CONSTANT_INDEX) { #endif - case IS_ARRAY: - case IS_CONSTANT_ARRAY: { +#if ZEND_EXTENSION_API_NO <= PHP_5_5_API_NO + case IS_CONSTANT_ARRAY: +#endif + case IS_ARRAY: { TSRMLS_FETCH(); #if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO diff --git a/ext/opcache/zend_accelerator_module.c b/ext/opcache/zend_accelerator_module.c index 53cf205f5..1620c0562 100644 --- a/ext/opcache/zend_accelerator_module.c +++ b/ext/opcache/zend_accelerator_module.c @@ -779,7 +779,7 @@ static ZEND_FUNCTION(opcache_compile_file) op_array = persistent_compile_file(&handle, ZEND_INCLUDE TSRMLS_CC); } zend_catch { EG(current_execute_data) = orig_execute_data; - zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME " could not compile file %s" TSRMLS_CC, handle.filename); + zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME " could not compile file %s", handle.filename); } zend_end_try(); if(op_array != NULL) { diff --git a/ext/opcache/zend_accelerator_util_funcs.c b/ext/opcache/zend_accelerator_util_funcs.c index 6204290ef..ae33e765c 100644 --- a/ext/opcache/zend_accelerator_util_funcs.c +++ b/ext/opcache/zend_accelerator_util_funcs.c @@ -237,19 +237,21 @@ static zend_ast *zend_ast_clone(zend_ast *ast TSRMLS_DC) if ((Z_TYPE_P(ast->u.val) & IS_CONSTANT_TYPE_MASK) >= IS_ARRAY) { switch ((Z_TYPE_P(ast->u.val) & IS_CONSTANT_TYPE_MASK)) { case IS_STRING: - case IS_CONSTANT: + case IS_CONSTANT: Z_STRVAL_P(node->u.val) = (char *) interned_estrndup(Z_STRVAL_P(ast->u.val), Z_STRLEN_P(ast->u.val)); break; case IS_ARRAY: - case IS_CONSTANT_ARRAY: +#if ZEND_EXTENSION_API_NO <= PHP_5_5_API_NO + case IS_CONSTANT_ARRAY: +#endif if (ast->u.val->value.ht && ast->u.val->value.ht != &EG(symbol_table)) { ALLOC_HASHTABLE(node->u.val->value.ht); zend_hash_clone_zval(node->u.val->value.ht, ast->u.val->value.ht, 0); } break; - case IS_CONSTANT_AST: - Z_AST_P(node->u.val) = zend_ast_clone(Z_AST_P(ast->u.val) TSRMLS_CC); - break; + case IS_CONSTANT_AST: + Z_AST_P(node->u.val) = zend_ast_clone(Z_AST_P(ast->u.val) TSRMLS_CC); + break; } } } else { @@ -295,20 +297,22 @@ static inline zval* zend_clone_zval(zval *src, int bind TSRMLS_DC) switch ((Z_TYPE_P(ret) & ~IS_CONSTANT_INDEX)) { #endif case IS_STRING: - case IS_CONSTANT: + case IS_CONSTANT: Z_STRVAL_P(ret) = (char *) interned_estrndup(Z_STRVAL_P(ret), Z_STRLEN_P(ret)); break; case IS_ARRAY: - case IS_CONSTANT_ARRAY: +#if ZEND_EXTENSION_API_NO <= PHP_5_5_API_NO + case IS_CONSTANT_ARRAY: +#endif if (ret->value.ht && ret->value.ht != &EG(symbol_table)) { ALLOC_HASHTABLE(ret->value.ht); zend_hash_clone_zval(ret->value.ht, src->value.ht, 0); } break; #if ZEND_EXTENSION_API_NO > PHP_5_5_X_API_NO - case IS_CONSTANT_AST: - Z_AST_P(ret) = zend_ast_clone(Z_AST_P(ret) TSRMLS_CC); - break; + case IS_CONSTANT_AST: + Z_AST_P(ret) = zend_ast_clone(Z_AST_P(ret) TSRMLS_CC); + break; #endif } } @@ -417,20 +421,22 @@ static void zend_hash_clone_zval(HashTable *ht, HashTable *source, int bind) switch ((Z_TYPE_P((zval*)p->pDataPtr) & ~IS_CONSTANT_INDEX)) { #endif case IS_STRING: - case IS_CONSTANT: + case IS_CONSTANT: Z_STRVAL_P(ppz) = (char *) interned_estrndup(Z_STRVAL_P((zval*)p->pDataPtr), Z_STRLEN_P((zval*)p->pDataPtr)); break; case IS_ARRAY: - case IS_CONSTANT_ARRAY: +#if ZEND_EXTENSION_API_NO <= PHP_5_5_API_NO + case IS_CONSTANT_ARRAY: +#endif if (((zval*)p->pDataPtr)->value.ht && ((zval*)p->pDataPtr)->value.ht != &EG(symbol_table)) { ALLOC_HASHTABLE(ppz->value.ht); zend_hash_clone_zval(ppz->value.ht, ((zval*)p->pDataPtr)->value.ht, 0); } break; #if ZEND_EXTENSION_API_NO > PHP_5_5_X_API_NO - case IS_CONSTANT_AST: - Z_AST_P(ppz) = zend_ast_clone(Z_AST_P(ppz) TSRMLS_CC); - break; + case IS_CONSTANT_AST: + Z_AST_P(ppz) = zend_ast_clone(Z_AST_P(ppz) TSRMLS_CC); + break; #endif } } diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index 47f8f8831..17f8e8798 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -174,7 +174,9 @@ static void zend_persist_zval(zval *z TSRMLS_DC) zend_accel_store_interned_string(z->value.str.val, z->value.str.len + 1); break; case IS_ARRAY: +#if ZEND_EXTENSION_API_NO <= PHP_5_5_API_NO case IS_CONSTANT_ARRAY: +#endif zend_accel_store(z->value.ht, sizeof(HashTable)); zend_hash_persist(z->value.ht, (zend_persist_func_t) zend_persist_zval_ptr, sizeof(zval**) TSRMLS_CC); break; diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c index c899ec8ac..a01d834a4 100644 --- a/ext/opcache/zend_persist_calc.c +++ b/ext/opcache/zend_persist_calc.c @@ -129,7 +129,9 @@ static uint zend_persist_zval_calc(zval *z TSRMLS_DC) ADD_INTERNED_STRING(Z_STRVAL_P(z), Z_STRLEN_P(z) + 1); break; case IS_ARRAY: +#if ZEND_EXTENSION_API_NO <= PHP_5_5_API_NO case IS_CONSTANT_ARRAY: +#endif ADD_DUP_SIZE(z->value.ht, sizeof(HashTable)); ADD_SIZE(zend_hash_persist_calc(z->value.ht, (int (*)(void* TSRMLS_DC)) zend_persist_zval_ptr_calc, sizeof(zval**) TSRMLS_CC)); break; diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index 1781cc177..8badbf7e0 100755 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -4865,6 +4865,7 @@ PHP_FUNCTION(openssl_seal) if (!EVP_EncryptInit(&ctx,cipher,NULL,NULL)) { RETVAL_FALSE; + EVP_CIPHER_CTX_cleanup(&ctx); goto clean_exit; } @@ -4875,10 +4876,12 @@ PHP_FUNCTION(openssl_seal) #endif /* allocate one byte extra to make room for \0 */ buf = emalloc(data_len + EVP_CIPHER_CTX_block_size(&ctx)); + EVP_CIPHER_CTX_cleanup(&ctx); if (!EVP_SealInit(&ctx, cipher, eks, eksl, NULL, pkeys, nkeys) || !EVP_SealUpdate(&ctx, buf, &len1, (unsigned char *)data, data_len)) { RETVAL_FALSE; efree(buf); + EVP_CIPHER_CTX_cleanup(&ctx); goto clean_exit; } @@ -4911,6 +4914,7 @@ PHP_FUNCTION(openssl_seal) efree(buf); } RETVAL_LONG(len1 + len2); + EVP_CIPHER_CTX_cleanup(&ctx); clean_exit: for (i=0; i<nkeys; i++) { @@ -4969,25 +4973,21 @@ PHP_FUNCTION(openssl_open) if (EVP_OpenInit(&ctx, cipher, (unsigned char *)ekey, ekey_len, NULL, pkey) && EVP_OpenUpdate(&ctx, buf, &len1, (unsigned char *)data, data_len)) { if (!EVP_OpenFinal(&ctx, buf + len1, &len2) || (len1 + len2 == 0)) { efree(buf); - if (keyresource == -1) { - EVP_PKEY_free(pkey); - } - RETURN_FALSE; + RETVAL_FALSE; + } else { + zval_dtor(opendata); + buf[len1 + len2] = '\0'; + ZVAL_STRINGL(opendata, erealloc(buf, len1 + len2 + 1), len1 + len2, 0); + RETVAL_TRUE; } } else { efree(buf); - if (keyresource == -1) { - EVP_PKEY_free(pkey); - } - RETURN_FALSE; + RETVAL_FALSE; } if (keyresource == -1) { EVP_PKEY_free(pkey); } - zval_dtor(opendata); - buf[len1 + len2] = '\0'; - ZVAL_STRINGL(opendata, erealloc(buf, len1 + len2 + 1), len1 + len2, 0); - RETURN_TRUE; + EVP_CIPHER_CTX_cleanup(&ctx); } /* }}} */ diff --git a/ext/openssl/xp_ssl.c b/ext/openssl/xp_ssl.c index e251d2124..c6a91570c 100644 --- a/ext/openssl/xp_ssl.c +++ b/ext/openssl/xp_ssl.c @@ -20,6 +20,10 @@ /* $Id$ */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include "php.h" #include "ext/standard/file.h" #include "ext/standard/url.h" diff --git a/ext/pdo/config.m4 b/ext/pdo/config.m4 index f8515ddb1..786c70382 100755 --- a/ext/pdo/config.m4 +++ b/ext/pdo/config.m4 @@ -60,7 +60,7 @@ for more detail on this issue. ifdef([PHP_INSTALL_HEADERS], [ dnl Sadly, this is a complete NOP for pecl extensions - PHP_INSTALL_HEADERS(ext/pdo, [php_pdo.h php_pdo_driver.h]) + PHP_INSTALL_HEADERS(ext/pdo, [php_pdo.h php_pdo_driver.h php_pdo_error.h]) ]) dnl so we always include the known-good working hack. diff --git a/ext/pdo/config.w32 b/ext/pdo/config.w32 index c85f2e55e..b02659098 100755 --- a/ext/pdo/config.w32 +++ b/ext/pdo/config.w32 @@ -6,5 +6,5 @@ ARG_ENABLE("pdo", "Enable PHP Data Objects support", "no"); if (PHP_PDO != "no") { EXTENSION('pdo', 'pdo.c pdo_dbh.c pdo_stmt.c pdo_sql_parser.c pdo_sqlstate.c', false /* force static, PHP_PDO_SHARED is broken yet somehow */); ADD_EXTENSION_DEP('pdo', 'spl', true); - PHP_INSTALL_HEADERS("ext/pdo", "php_pdo.h php_pdo_driver.h"); + PHP_INSTALL_HEADERS("ext/pdo", "php_pdo.h php_pdo_driver.h php_pdo_error.h"); } diff --git a/ext/pdo_firebird/config.m4 b/ext/pdo_firebird/config.m4 index a89ab2aea..f9188a09b 100644 --- a/ext/pdo_firebird/config.m4 +++ b/ext/pdo_firebird/config.m4 @@ -1,7 +1,3 @@ -dnl -dnl $Id$ -dnl - PHP_ARG_WITH(pdo-firebird,for Firebird support for PDO, [ --with-pdo-firebird[=DIR] PDO: Firebird support. DIR is the Firebird base install directory [/opt/firebird]]) diff --git a/ext/pdo_firebird/config.w32 b/ext/pdo_firebird/config.w32 index 9cf5ac133..dc3ea0d17 100644 --- a/ext/pdo_firebird/config.w32 +++ b/ext/pdo_firebird/config.w32 @@ -1,4 +1,3 @@ -// $Id$ // vim:ft=javascript ARG_WITH("pdo-firebird", "Firebird support for PDO", "no"); diff --git a/ext/pdo_firebird/firebird_driver.c b/ext/pdo_firebird/firebird_driver.c index 72595de9f..1327861fe 100644 --- a/ext/pdo_firebird/firebird_driver.c +++ b/ext/pdo_firebird/firebird_driver.c @@ -16,8 +16,6 @@ +----------------------------------------------------------------------+ */ -/* $Id$ */ - #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -569,7 +567,7 @@ static int firebird_handle_get_attribute(pdo_dbh_t *dbh, long attr, zval *val TS #else HMODULE l = GetModuleHandle("fbclient"); - if (!l && !(l = GetModuleHandle("gds32"))) { + if (!l) { break; } info_func = (info_func_t)GetProcAddress(l, "isc_get_client_version"); @@ -577,9 +575,7 @@ static int firebird_handle_get_attribute(pdo_dbh_t *dbh, long attr, zval *val TS if (info_func) { info_func(tmp); ZVAL_STRING(val,tmp,1); - } else { - ZVAL_STRING(val,"Firebird 1.0/Interbase 6",1); - } + } #else ZVAL_NULL(val); #endif diff --git a/ext/pdo_firebird/firebird_statement.c b/ext/pdo_firebird/firebird_statement.c index efb232bff..5eb2e977e 100644 --- a/ext/pdo_firebird/firebird_statement.c +++ b/ext/pdo_firebird/firebird_statement.c @@ -16,8 +16,6 @@ +----------------------------------------------------------------------+ */ -/* $Id$ */ - #ifdef HAVE_CONFIG_H #include "config.h" #endif diff --git a/ext/pdo_firebird/pdo_firebird.c b/ext/pdo_firebird/pdo_firebird.c index 9de904885..d59aa53fa 100644 --- a/ext/pdo_firebird/pdo_firebird.c +++ b/ext/pdo_firebird/pdo_firebird.c @@ -16,8 +16,6 @@ +----------------------------------------------------------------------+ */ -/* $Id$ */ - #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -37,21 +35,15 @@ const zend_function_entry pdo_firebird_functions[] = { /* {{{ */ /* {{{ pdo_firebird_deps */ -#if ZEND_MODULE_API_NO >= 20050922 static const zend_module_dep pdo_firebird_deps[] = { ZEND_MOD_REQUIRED("pdo") ZEND_MOD_END }; -#endif /* }}} */ zend_module_entry pdo_firebird_module_entry = { /* {{{ */ -#if ZEND_MODULE_API_NO >= 20050922 STANDARD_MODULE_HEADER_EX, NULL, pdo_firebird_deps, -#else - STANDARD_MODULE_HEADER, -#endif "PDO_Firebird", pdo_firebird_functions, PHP_MINIT(pdo_firebird), diff --git a/ext/pdo_firebird/php_pdo_firebird.h b/ext/pdo_firebird/php_pdo_firebird.h index 3816497c2..f7e9f17f1 100644 --- a/ext/pdo_firebird/php_pdo_firebird.h +++ b/ext/pdo_firebird/php_pdo_firebird.h @@ -16,8 +16,6 @@ +----------------------------------------------------------------------+ */ -/* $Id$ */ - #ifndef PHP_PDO_FIREBIRD_H #define PHP_PDO_FIREBIRD_H diff --git a/ext/pdo_firebird/php_pdo_firebird_int.h b/ext/pdo_firebird/php_pdo_firebird_int.h index b186eeb47..15004b8d3 100644 --- a/ext/pdo_firebird/php_pdo_firebird_int.h +++ b/ext/pdo_firebird/php_pdo_firebird_int.h @@ -16,8 +16,6 @@ +----------------------------------------------------------------------+ */ -/* $Id$ */ - #ifndef PHP_PDO_FIREBIRD_INT_H #define PHP_PDO_FIREBIRD_INT_H diff --git a/ext/pdo_mysql/mysql_driver.c b/ext/pdo_mysql/mysql_driver.c index 825fe2647..581766b38 100644 --- a/ext/pdo_mysql/mysql_driver.c +++ b/ext/pdo_mysql/mysql_driver.c @@ -37,7 +37,7 @@ #include "zend_exceptions.h" #if defined(PDO_USE_MYSQLND) -# define pdo_mysql_init(persistent) mysqlnd_init(persistent) +# define pdo_mysql_init(persistent) mysqlnd_init(MYSQLND_CLIENT_NO_FLAG, persistent) #else # define pdo_mysql_init(persistent) mysql_init(NULL) #endif @@ -752,7 +752,7 @@ static int pdo_mysql_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_ } if (mysqlnd_connect(H->server, host, dbh->username, dbh->password, password_len, dbname, dbname_len, - port, unix_socket, connect_opts TSRMLS_CC) == NULL) { + port, unix_socket, connect_opts, MYSQLND_CLIENT_NO_FLAG TSRMLS_CC) == NULL) { #else if (mysql_real_connect(H->server, host, dbh->username, dbh->password, dbname, port, unix_socket, connect_opts) == NULL) { #endif diff --git a/ext/pdo_odbc/odbc_stmt.c b/ext/pdo_odbc/odbc_stmt.c index 8b0ccf34e..1d275cd65 100644 --- a/ext/pdo_odbc/odbc_stmt.c +++ b/ext/pdo_odbc/odbc_stmt.c @@ -279,7 +279,7 @@ static int odbc_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data *p pdo_odbc_stmt *S = (pdo_odbc_stmt*)stmt->driver_data; RETCODE rc; SWORD sqltype = 0, ctype = 0, scale = 0, nullable = 0; - UDWORD precision = 0; + SQLULEN precision = 0; pdo_odbc_param *P; /* we're only interested in parameters for prepared SQL right now */ @@ -551,7 +551,7 @@ static int odbc_stmt_describe(pdo_stmt_t *stmt, int colno TSRMLS_DC) struct pdo_column_data *col = &stmt->columns[colno]; RETCODE rc; SWORD colnamelen; - SDWORD colsize; + SQLULEN colsize; SQLLEN displaysize; rc = SQLDescribeCol(S->stmt, colno+1, S->cols[colno].colname, diff --git a/ext/pdo_odbc/php_pdo_odbc_int.h b/ext/pdo_odbc/php_pdo_odbc_int.h index 87b2f9100..5e42951f8 100644 --- a/ext/pdo_odbc/php_pdo_odbc_int.h +++ b/ext/pdo_odbc/php_pdo_odbc_int.h @@ -157,7 +157,7 @@ typedef struct { } pdo_odbc_stmt; typedef struct { - SQLINTEGER len; + SQLLEN len; SQLSMALLINT paramtype; char *outbuf; unsigned is_unicode:1; diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c index d36901d8c..d867f433b 100644 --- a/ext/pgsql/pgsql.c +++ b/ext/pgsql/pgsql.c @@ -5829,9 +5829,7 @@ PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, con php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Detected broken meta data. Missing 'is enum'"); err = 1; } - if (!err && (Z_TYPE_PP(val) == IS_ARRAY || - Z_TYPE_PP(val) == IS_OBJECT || - Z_TYPE_PP(val) == IS_CONSTANT_ARRAY)) { + if (!err && (Z_TYPE_PP(val) == IS_ARRAY || Z_TYPE_PP(val) == IS_OBJECT)) { php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Expects scalar values as field values"); err = 1; } diff --git a/ext/phar/phar_object.c b/ext/phar/phar_object.c index 99907f86d..407edda0f 100644 --- a/ext/phar/phar_object.c +++ b/ext/phar/phar_object.c @@ -1934,10 +1934,12 @@ PHP_METHOD(Phar, buildFromIterator) */ PHP_METHOD(Phar, count) { + /* mode can be ignored, maximum depth is 1 */ + long mode; PHAR_ARCHIVE_OBJECT(); - if (zend_parse_parameters_none() == FAILURE) { - return; + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &mode) == FAILURE) { + RETURN_FALSE; } RETURN_LONG(zend_hash_num_elements(&phar_obj->arc.archive->manifest)); diff --git a/ext/phar/phar_path_check.c b/ext/phar/phar_path_check.c index db97efcbe..ebf4aac58 100644 --- a/ext/phar/phar_path_check.c +++ b/ext/phar/phar_path_check.c @@ -46,60 +46,91 @@ loop: #line 47 "ext/phar/phar_path_check.c" { YYCTYPE yych; + unsigned int yyaccept = 0; if ((YYLIMIT - YYCURSOR) < 4) YYFILL(4); yych = *YYCURSOR; - if (yych <= '.') { - if (yych <= '\n') { - if (yych <= 0x00) goto yy13; - if (yych <= '\t') goto yy10; - goto yy12; + if (yych <= '[') { + if (yych <= ')') { + if (yych <= '\t') { + if (yych <= 0x00) goto yy21; + goto yy23; + } else { + if (yych <= '\n') goto yy2; + if (yych <= 0x19) goto yy23; + goto yy11; + } } else { - if (yych <= 0x19) goto yy10; - if (yych == '*') goto yy6; - goto yy15; + if (yych <= '/') { + if (yych <= '*') goto yy7; + if (yych <= '.') goto yy11; + goto yy3; + } else { + if (yych == '?') goto yy9; + goto yy11; + } } } else { - if (yych <= '?') { - if (yych <= '/') goto yy2; - if (yych <= '>') goto yy15; - goto yy8; + if (yych <= 0xEC) { + if (yych <= 0xC1) { + if (yych <= '\\') goto yy5; + if (yych <= 0x7F) goto yy11; + goto yy23; + } else { + if (yych <= 0xDF) goto yy12; + if (yych <= 0xE0) goto yy14; + goto yy15; + } } else { - if (yych == '\\') goto yy4; - if (yych <= 0x7F) goto yy15; - goto yy10; + if (yych <= 0xF0) { + if (yych <= 0xED) goto yy16; + if (yych <= 0xEF) goto yy17; + goto yy18; + } else { + if (yych <= 0xF3) goto yy19; + if (yych <= 0xF4) goto yy20; + goto yy23; + } } } yy2: - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= '-') goto yy3; - if (yych <= '.') goto yy16; - if (yych <= '/') goto yy18; + YYCURSOR = YYMARKER; + if (yyaccept <= 0) { + goto yy4; + } else { + goto yy13; + } yy3: -#line 93 "ext/phar/phar_path_check.re" + yyaccept = 0; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= '-') goto yy4; + if (yych <= '.') goto yy35; + if (yych <= '/') goto yy36; +yy4: +#line 88 "ext/phar/phar_path_check.re" { goto loop; } -#line 84 "ext/phar/phar_path_check.c" -yy4: +#line 115 "ext/phar/phar_path_check.c" +yy5: ++YYCURSOR; -#line 60 "ext/phar/phar_path_check.re" +#line 72 "ext/phar/phar_path_check.re" { *error = "back-slash"; return pcr_err_back_slash; } -#line 92 "ext/phar/phar_path_check.c" -yy6: +#line 123 "ext/phar/phar_path_check.c" +yy7: ++YYCURSOR; -#line 64 "ext/phar/phar_path_check.re" +#line 76 "ext/phar/phar_path_check.re" { *error = "star"; return pcr_err_star; } -#line 100 "ext/phar/phar_path_check.c" -yy8: +#line 131 "ext/phar/phar_path_check.c" +yy9: ++YYCURSOR; -#line 68 "ext/phar/phar_path_check.re" +#line 80 "ext/phar/phar_path_check.re" { if (**s == '/') { (*s)++; @@ -108,22 +139,66 @@ yy8: *error = NULL; return pcr_use_query; } -#line 112 "ext/phar/phar_path_check.c" -yy10: - ++YYCURSOR; +#line 143 "ext/phar/phar_path_check.c" yy11: -#line 76 "ext/phar/phar_path_check.re" + yych = *++YYCURSOR; + goto yy4; +yy12: + ++YYCURSOR; + if ((yych = *YYCURSOR) <= 0x7F) goto yy13; + if (yych <= 0xBF) goto yy26; +yy13: +#line 104 "ext/phar/phar_path_check.re" { *error ="illegal character"; return pcr_err_illegal_char; } -#line 121 "ext/phar/phar_path_check.c" -yy12: - yych = *++YYCURSOR; - goto yy11; -yy13: +#line 157 "ext/phar/phar_path_check.c" +yy14: + yyaccept = 1; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= 0x9F) goto yy13; + if (yych <= 0xBF) goto yy34; + goto yy13; +yy15: + yyaccept = 1; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= 0x7F) goto yy13; + if (yych <= 0xBF) goto yy33; + goto yy13; +yy16: + yyaccept = 1; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= 0x7F) goto yy13; + if (yych <= 0x9F) goto yy32; + goto yy13; +yy17: + yyaccept = 1; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= 0x7F) goto yy13; + if (yych <= 0xBF) goto yy31; + goto yy13; +yy18: + yyaccept = 1; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= 0x8F) goto yy13; + if (yych <= 0xBF) goto yy29; + goto yy13; +yy19: + yyaccept = 1; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= 0x7F) goto yy13; + if (yych <= 0xBF) goto yy27; + goto yy13; +yy20: + yyaccept = 1; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= 0x7F) goto yy13; + if (yych <= 0x8F) goto yy24; + goto yy13; +yy21: ++YYCURSOR; -#line 80 "ext/phar/phar_path_check.re" +#line 91 "ext/phar/phar_path_check.re" { if (**s == '/') { (*s)++; @@ -137,49 +212,93 @@ yy13: *error = NULL; return pcr_is_ok; } -#line 141 "ext/phar/phar_path_check.c" -yy15: +#line 216 "ext/phar/phar_path_check.c" +yy23: yych = *++YYCURSOR; - goto yy3; -yy16: + goto yy13; +yy24: yych = *++YYCURSOR; - if (yych <= 0x00) goto yy21; - if (yych <= '-') goto yy17; - if (yych <= '.') goto yy20; - if (yych <= '/') goto yy21; -yy17: - YYCURSOR = YYMARKER; - goto yy3; -yy18: + if (yych <= 0x7F) goto yy2; + if (yych >= 0xC0) goto yy2; + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy2; + if (yych >= 0xC0) goto yy2; +yy26: + yych = *++YYCURSOR; + goto yy4; +yy27: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy2; + if (yych >= 0xC0) goto yy2; + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy2; + if (yych <= 0xBF) goto yy26; + goto yy2; +yy29: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy2; + if (yych >= 0xC0) goto yy2; + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy2; + if (yych <= 0xBF) goto yy26; + goto yy2; +yy31: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy2; + if (yych <= 0xBF) goto yy26; + goto yy2; +yy32: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy2; + if (yych <= 0xBF) goto yy26; + goto yy2; +yy33: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy2; + if (yych <= 0xBF) goto yy26; + goto yy2; +yy34: + yych = *++YYCURSOR; + if (yych <= 0x7F) goto yy2; + if (yych <= 0xBF) goto yy26; + goto yy2; +yy35: + yych = *++YYCURSOR; + if (yych <= 0x00) goto yy39; + if (yych <= '-') goto yy2; + if (yych <= '.') goto yy38; + if (yych <= '/') goto yy39; + goto yy2; +yy36: ++YYCURSOR; -#line 48 "ext/phar/phar_path_check.re" +#line 60 "ext/phar/phar_path_check.re" { *error = "double slash"; return pcr_err_double_slash; } -#line 161 "ext/phar/phar_path_check.c" -yy20: +#line 280 "ext/phar/phar_path_check.c" +yy38: yych = *++YYCURSOR; - if (yych <= 0x00) goto yy23; - if (yych == '/') goto yy23; - goto yy17; -yy21: + if (yych <= 0x00) goto yy41; + if (yych == '/') goto yy41; + goto yy2; +yy39: ++YYCURSOR; -#line 56 "ext/phar/phar_path_check.re" +#line 68 "ext/phar/phar_path_check.re" { *error = "current directory reference"; return pcr_err_curr_dir; } -#line 174 "ext/phar/phar_path_check.c" -yy23: +#line 293 "ext/phar/phar_path_check.c" +yy41: ++YYCURSOR; -#line 52 "ext/phar/phar_path_check.re" +#line 64 "ext/phar/phar_path_check.re" { *error = "upper directory reference"; return pcr_err_up_dir; } -#line 182 "ext/phar/phar_path_check.c" +#line 301 "ext/phar/phar_path_check.c" } -#line 96 "ext/phar/phar_path_check.re" +#line 108 "ext/phar/phar_path_check.re" } diff --git a/ext/phar/phar_path_check.re b/ext/phar/phar_path_check.re index e43b3f846..df6407687 100644 --- a/ext/phar/phar_path_check.re +++ b/ext/phar/phar_path_check.re @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | phar php single-file executable PHP extension | +----------------------------------------------------------------------+ - | Copyright (c) 2007-2013 The PHP Group | + | Copyright (c) 2007-2014 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | @@ -42,7 +42,19 @@ phar_path_check_result phar_path_check(char **s, int *len, const char **error) loop: /*!re2c END = "\x00"; -ILL = [\x01-\x19\x80-\xFF]; +UTF8T = [\x80-\xBF] ; +UTF8_1 = [\x1A-\x7F] ; +UTF8_2 = [\xC2-\xDF] UTF8T ; +UTF8_3A = "\xE0" [\xA0-\xBF] UTF8T ; +UTF8_3B = [\xE1-\xEC] UTF8T{2} ; +UTF8_3C = "\xED" [\x80-\x9F] UTF8T ; +UTF8_3D = [\xEE-\xEF] UTF8T{2} ; +UTF8_3 = UTF8_3A | UTF8_3B | UTF8_3C | UTF8_3D ; +UTF8_4A = "\xF0"[\x90-\xBF] UTF8T{2} ; +UTF8_4B = [\xF1-\xF3] UTF8T{3} ; +UTF8_4C = "\xF4" [\x80-\x8F] UTF8T{2} ; +UTF8_4 = UTF8_4A | UTF8_4B | UTF8_4C ; +UTF8 = UTF8_1 | UTF8_2 | UTF8_3 | UTF8_4 ; EOS = "/" | END; ANY = .; "//" { @@ -73,9 +85,8 @@ ANY = .; *error = NULL; return pcr_use_query; } -ILL { - *error ="illegal character"; - return pcr_err_illegal_char; +UTF8 { + goto loop; } END { if (**s == '/') { @@ -91,7 +102,8 @@ END { return pcr_is_ok; } ANY { - goto loop; + *error ="illegal character"; + return pcr_err_illegal_char; } */ } diff --git a/ext/phar/tests/create_new_phar.phpt b/ext/phar/tests/create_new_phar.phpt index ec57c2721..26794095e 100644 --- a/ext/phar/tests/create_new_phar.phpt +++ b/ext/phar/tests/create_new_phar.phpt @@ -9,8 +9,13 @@ phar.require_hash=1 <?php file_put_contents('phar://' . dirname(__FILE__) . '/' . basename(__FILE__, '.php') . '.phar.php/a.php', - 'brand new!'); + "brand new!\n"); include 'phar://' . dirname(__FILE__) . '/' . basename(__FILE__, '.php') . '.phar.php/a.php'; + +$fileName = "ChineseFile\xE5\x84\xB7\xE9\xBB\x91.php"; +file_put_contents('phar://' . dirname(__FILE__) . '/' . basename(__FILE__, '.php') . '.phar.php/$fileName.php', + 'Text in utf8 file.'); +include 'phar://' . dirname(__FILE__) . '/' . basename(__FILE__, '.php') . '.phar.php/$fileName.php'; ?> ===DONE=== @@ -18,4 +23,5 @@ include 'phar://' . dirname(__FILE__) . '/' . basename(__FILE__, '.php') . '.pha <?php unlink(dirname(__FILE__) . '/' . basename(__FILE__, '.clean.php') . '.phar.php'); ?> --EXPECT-- brand new! +Text in utf8 file. ===DONE=== diff --git a/ext/phar/tests/create_path_error.phpt b/ext/phar/tests/create_path_error.phpt index d3fc03586..886ba8192 100644 --- a/ext/phar/tests/create_path_error.phpt +++ b/ext/phar/tests/create_path_error.phpt @@ -22,15 +22,28 @@ var_dump(file_get_contents($pname . '/b.php')); function error_handler($errno, $errmsg) { - echo "Error: $errmsg\n"; + echo "Error: $errmsg"; } set_error_handler('error_handler'); -$checks = array('/', '.', '../', 'a/..', 'a/', 'b//a.php'); +$count = 0; +$checks = array( + '/', '.', '../', 'a/..', 'a/', 'b//a.php', + "Font\xE5\x84\xB7\xE9\xBB\x91pro.ttf", //two valid multi-byte characters + "\xF0\x9F\x98\x8D.ttf", // valid 4 byte char - smiling face with heart-shaped eyes + "Font\xE9\xBBpro.ttf", //Invalid multi-byte character - missing last byte + "Font\xBB\x91pro.ttf", //Invalid multi-byte character - missing first byte + "Font\xC0\xAFpro.ttf", //Invalid multi-byte character - invalid first byte + "Font\xF0\x80\x90\x90pro.ttf", //Invalid multi-byte character - surrogate pair code point + "\xFC\x81\x81\x81\x81pro.ttf", //RFC 3629 limited char points to 0000-10FFFF aka 5 byte utf-8 not valid +); foreach($checks as $check) { + $count++; + echo "$count:"; file_put_contents($pname . '/' . $check, "error"); + echo "\n"; } $phar = new Phar($fname); @@ -54,9 +67,19 @@ foreach($checks as $check) --EXPECTF-- string(5) "query" string(5) "query" -Error: file_put_contents(phar://%s//): failed to open stream: phar error: file "" in phar "%s" cannot be empty -Error: file_put_contents(phar://%s/.): failed to open stream: phar error: file "" in phar "%s" cannot be empty -Error: file_put_contents(phar://%s/../): failed to open stream: phar error: file "" in phar "%s" cannot be empty -Error: file_put_contents(phar://%s/a/..): failed to open stream: phar error: file "" in phar "%s" cannot be empty +1:Error: file_put_contents(phar://%s//): failed to open stream: phar error: file "" in phar "%s" cannot be empty +2:Error: file_put_contents(phar://%s/.): failed to open stream: phar error: file "" in phar "%s" cannot be empty +3:Error: file_put_contents(phar://%s/../): failed to open stream: phar error: file "" in phar "%s" cannot be empty +4:Error: file_put_contents(phar://%s/a/..): failed to open stream: phar error: file "" in phar "%s" cannot be empty +5: +6: +7: +8: +9:Error: file_put_contents(phar:///%s): failed to open stream: phar error: invalid path "%s" contains illegal character +10:Error: file_put_contents(phar:///%s): failed to open stream: phar error: invalid path "%s" contains illegal character +11:Error: file_put_contents(phar:///%s): failed to open stream: phar error: invalid path "%s" contains illegal character +12:Error: file_put_contents(phar:///%s): failed to open stream: phar error: invalid path "%s" contains illegal character +13:Error: file_put_contents(phar:///%s): failed to open stream: phar error: invalid path "%s" contains illegal character Exception: Entry a does not exist and cannot be created: phar error: invalid path "a" contains illegal character ===DONE=== + diff --git a/ext/posix/tests/posix_kill_basic.phpt b/ext/posix/tests/posix_kill_basic.phpt index c995031af..b68d3cb5d 100644 --- a/ext/posix/tests/posix_kill_basic.phpt +++ b/ext/posix/tests/posix_kill_basic.phpt @@ -34,6 +34,6 @@ Basic test of POSIX getpgid(), kill(), get_last_error() and strerror() functions Kill pid=%d bool(false) int(%d) -string(%d) %sNo such process%s +string(%d) %s%rNo such process|Operation not permitted%r%s ===DONE==== -
\ No newline at end of file + diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index a24153dc2..703e14113 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -733,7 +733,7 @@ static void _parameter_string(string *str, zend_function *fptr, struct _zend_arg *zv = *precv->op2.zv; zval_copy_ctor(zv); INIT_PZVAL(zv); - zval_update_constant_ex(&zv, (void*)1, fptr->common.scope TSRMLS_CC); + zval_update_constant_ex(&zv, 1, fptr->common.scope TSRMLS_CC); if (Z_TYPE_P(zv) == IS_BOOL) { if (Z_LVAL_P(zv)) { string_write(str, "true", sizeof("true")-1); @@ -2599,7 +2599,7 @@ ZEND_METHOD(reflection_parameter, getDefaultValue) if (!IS_CONSTANT_TYPE(Z_TYPE_P(return_value))) { zval_copy_ctor(return_value); } - zval_update_constant_ex(&return_value, (void*)0, param->fptr->common.scope TSRMLS_CC); + zval_update_constant_ex(&return_value, 0, param->fptr->common.scope TSRMLS_CC); } /* }}} */ @@ -3411,7 +3411,7 @@ static void add_class_vars(zend_class_entry *ce, int statics, zval *return_value /* this is necessary to make it able to work with default array * properties, returned to user */ if (IS_CONSTANT_TYPE(Z_TYPE_P(prop_copy))) { - zval_update_constant(&prop_copy, (void *) 1 TSRMLS_CC); + zval_update_constant(&prop_copy, 1 TSRMLS_CC); } add_assoc_zval(return_value, key, prop_copy); diff --git a/ext/session/mod_files.c b/ext/session/mod_files.c index 47140436e..33e177c5d 100644 --- a/ext/session/mod_files.c +++ b/ext/session/mod_files.c @@ -50,6 +50,12 @@ #define FILE_PREFIX "sess_" +#ifdef PHP_WIN32 +# ifndef O_NOFOLLOW +# define O_NOFOLLOW 0 +# endif +#endif + typedef struct { int fd; char *lastkey; @@ -115,6 +121,7 @@ static void ps_files_close(ps_files *data) static void ps_files_open(ps_files *data, const char *key TSRMLS_DC) { char buf[MAXPATHLEN]; + struct stat sbuf; if (data->fd < 0 || !data->lastkey || strcmp(key, data->lastkey)) { if (data->lastkey) { @@ -135,24 +142,26 @@ static void ps_files_open(ps_files *data, const char *key TSRMLS_DC) data->lastkey = estrdup(key); + /* O_NOFOLLOW to prevent us from following evil symlinks */ +#ifdef O_NOFOLLOW + data->fd = VCWD_OPEN_MODE(buf, O_CREAT | O_RDWR | O_BINARY | O_NOFOLLOW, data->filemode); +#else + /* Check to make sure that the opened file is not outside of allowable dirs. + This is not 100% safe but it's hard to do something better without O_NOFOLLOW */ + if(PG(open_basedir) && lstat(buf, &sbuf) == 0 && S_ISLNK(sbuf.st_mode) && php_check_open_basedir(buf TSRMLS_CC)) { + return; + } data->fd = VCWD_OPEN_MODE(buf, O_CREAT | O_RDWR | O_BINARY, data->filemode); +#endif if (data->fd != -1) { #ifndef PHP_WIN32 - /* check to make sure that the opened file is not a symlink, linking to data outside of allowable dirs */ - if (PG(open_basedir)) { - struct stat sbuf; - - if (fstat(data->fd, &sbuf)) { - close(data->fd); - data->fd = -1; - return; - } - if (S_ISLNK(sbuf.st_mode) && php_check_open_basedir(buf TSRMLS_CC)) { - close(data->fd); - data->fd = -1; - return; - } + /* check that this session file was created by us or root – we + don't want to end up accepting the sessions of another webapp */ + if (fstat(data->fd, &sbuf) || (sbuf.st_uid != 0 && sbuf.st_uid != getuid() && sbuf.st_uid != geteuid())) { + close(data->fd); + data->fd = -1; + return; } #endif flock(data->fd, LOCK_EX); diff --git a/ext/soap/php_encoding.c b/ext/soap/php_encoding.c index 32e88510e..b225a2d67 100644 --- a/ext/soap/php_encoding.c +++ b/ext/soap/php_encoding.c @@ -154,10 +154,8 @@ encode defaultEncoding[] = { {{IS_BOOL, XSD_BOOLEAN_STRING, XSD_NAMESPACE, NULL}, to_zval_bool, to_xml_bool}, {{IS_CONSTANT, XSD_STRING_STRING, XSD_NAMESPACE, NULL}, to_zval_string, to_xml_string}, {{IS_ARRAY, SOAP_ENC_ARRAY_STRING, SOAP_1_1_ENC_NAMESPACE, NULL}, to_zval_array, guess_array_map}, - {{IS_CONSTANT_ARRAY, SOAP_ENC_ARRAY_STRING, SOAP_1_1_ENC_NAMESPACE, NULL}, to_zval_array, to_xml_array}, {{IS_OBJECT, SOAP_ENC_OBJECT_STRING, SOAP_1_1_ENC_NAMESPACE, NULL}, to_zval_object, to_xml_object}, {{IS_ARRAY, SOAP_ENC_ARRAY_STRING, SOAP_1_2_ENC_NAMESPACE, NULL}, to_zval_array, guess_array_map}, - {{IS_CONSTANT_ARRAY, SOAP_ENC_ARRAY_STRING, SOAP_1_2_ENC_NAMESPACE, NULL}, to_zval_array, to_xml_array}, {{IS_OBJECT, SOAP_ENC_OBJECT_STRING, SOAP_1_2_ENC_NAMESPACE, NULL}, to_zval_object, to_xml_object}, {{XSD_STRING, XSD_STRING_STRING, XSD_NAMESPACE, NULL}, to_zval_string, to_xml_string}, diff --git a/ext/spl/README b/ext/spl/README index b2aeb596d..28373a3ed 100755 --- a/ext/spl/README +++ b/ext/spl/README @@ -4,4 +4,4 @@ code in the file spl.php or in the corresponding .inc file in the examples subdirectory. Based on the internal implementations or the files in the examples subdirectory there are also some .php files to experiment with. -For more information look at: http://php.net/~helly/php/ext/spl +For more information look at: http://php.net/manual/en/book.spl.php diff --git a/ext/spl/spl.php b/ext/spl/spl.php index 3638a5a2b..c276f1f2e 100755 --- a/ext/spl/spl.php +++ b/ext/spl/spl.php @@ -145,9 +145,6 @@ * - Debug session 2 <a href="http://talks.somabo.de/200509_toronto_iterator_debug_session_1.pps">[pps]</a>, <a href="http://talks.somabo.de/200509_toronto_iterator_debug_session_1.pdf">[pdf]</a>, <a href="http://taks.somabo.de/200411_php_conference_frankfrurt_iterator_debug_session.swf">[swf]</a> * - Debug session 3 <a href="http://talks.somabo.de/200509_toronto_iterator_debug_session_2.pps">[pps]</a>, <a href="http://talks.somabo.de/200509_toronto_iterator_debug_session_2.pdf">[pdf]</a> * - * You can download this documentation as a chm file - * <a href="http://php.net/~helly/php/ext/spl/spl.chm">here</a>. - * * (c) Marcus Boerger, 2003 - 2007 */ diff --git a/ext/spl/spl_observer.c b/ext/spl/spl_observer.c index 1a706f764..91830ab00 100644 --- a/ext/spl/spl_observer.c +++ b/ext/spl/spl_observer.c @@ -26,6 +26,7 @@ #include "php.h" #include "php_ini.h" #include "ext/standard/info.h" +#include "ext/standard/php_array.h" #include "ext/standard/php_var.h" #include "ext/standard/php_smart_str.h" #include "zend_interfaces.h" @@ -621,11 +622,27 @@ SPL_METHOD(SplObjectStorage, contains) SPL_METHOD(SplObjectStorage, count) { spl_SplObjectStorage *intern = (spl_SplObjectStorage*)zend_object_store_get_object(getThis() TSRMLS_CC); - - if (zend_parse_parameters_none() == FAILURE) { + long mode = COUNT_NORMAL; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &mode) == FAILURE) { return; } - + + if (mode == COUNT_RECURSIVE) { + long ret = zend_hash_num_elements(&intern->storage); + HashPosition position; + zval *element; + + for (zend_hash_internal_pointer_reset_ex(&intern->storage, &position); + zend_hash_get_current_data_ex(&intern->storage, (void**) &element, &position) == SUCCESS; + zend_hash_move_forward_ex(&intern->storage, &position)) { + ret += php_count_recursive(element, mode TSRMLS_CC); + } + + RETURN_LONG(ret); + return; + } + RETURN_LONG(zend_hash_num_elements(&intern->storage)); } /* }}} */ diff --git a/ext/sqlite3/libsqlite/sqlite3.c b/ext/sqlite3/libsqlite/sqlite3.c index d0057a618..e5aa96f6b 100644 --- a/ext/sqlite3/libsqlite/sqlite3.c +++ b/ext/sqlite3/libsqlite/sqlite3.c @@ -1,6 +1,6 @@ /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite -** version 3.8.3.1. By combining all the individual C code files into this +** version 3.8.4.3. 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 @@ -25,6 +25,93 @@ #ifndef SQLITE_API # define SQLITE_API #endif +/************** Begin file sqliteInt.h ***************************************/ +/* +** 2001 September 15 +** +** 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. +** +************************************************************************* +** Internal interface definitions for SQLite. +** +*/ +#ifndef _SQLITEINT_H_ +#define _SQLITEINT_H_ + +/* +** These #defines should enable >2GB file support on POSIX if the +** underlying operating system supports it. If the OS lacks +** large file support, or if the OS is windows, these should be no-ops. +** +** Ticket #2739: The _LARGEFILE_SOURCE macro must appear before any +** system #includes. Hence, this block of code must be the very first +** code in all source files. +** +** Large file support can be disabled using the -DSQLITE_DISABLE_LFS switch +** on the compiler command line. This is necessary if you are compiling +** on a recent machine (ex: Red Hat 7.2) but you want your code to work +** on an older machine (ex: Red Hat 6.0). If you compile on Red Hat 7.2 +** without this option, LFS is enable. But LFS does not exist in the kernel +** in Red Hat 6.0, so the code won't work. Hence, for maximum binary +** portability you should omit LFS. +** +** The previous paragraph was written in 2005. (This paragraph is written +** on 2008-11-28.) These days, all Linux kernels support large files, so +** you should probably leave LFS enabled. But some embedded platforms might +** lack LFS in which case the SQLITE_DISABLE_LFS macro might still be useful. +** +** Similar is true for Mac OS X. LFS is only supported on Mac OS X 9 and later. +*/ +#ifndef SQLITE_DISABLE_LFS +# define _LARGE_FILE 1 +# ifndef _FILE_OFFSET_BITS +# define _FILE_OFFSET_BITS 64 +# endif +# define _LARGEFILE_SOURCE 1 +#endif + +/* +** For MinGW, check to see if we can include the header file containing its +** version information, among other things. Normally, this internal MinGW +** header file would [only] be included automatically by other MinGW header +** files; however, the contained version information is now required by this +** header file to work around binary compatibility issues (see below) and +** this is the only known way to reliably obtain it. This entire #if block +** would be completely unnecessary if there was any other way of detecting +** MinGW via their preprocessor (e.g. if they customized their GCC to define +** some MinGW-specific macros). When compiling for MinGW, either the +** _HAVE_MINGW_H or _HAVE__MINGW_H (note the extra underscore) macro must be +** defined; otherwise, detection of conditions specific to MinGW will be +** disabled. +*/ +#if defined(_HAVE_MINGW_H) +# include "mingw.h" +#elif defined(_HAVE__MINGW_H) +# include "_mingw.h" +#endif + +/* +** For MinGW version 4.x (and higher), check to see if the _USE_32BIT_TIME_T +** define is required to maintain binary compatibility with the MSVC runtime +** library in use (e.g. for Windows XP). +*/ +#if !defined(_USE_32BIT_TIME_T) && !defined(_USE_64BIT_TIME_T) && \ + defined(_WIN32) && !defined(_WIN64) && \ + defined(__MINGW_MAJOR_VERSION) && __MINGW_MAJOR_VERSION >= 4 && \ + defined(__MSVCRT__) +# define _USE_32BIT_TIME_T +#endif + +/* The public SQLite interface. The _FILE_OFFSET_BITS macro must appear +** first in QNX. Also, the _USE_32BIT_TIME_T macro must appear first for +** MinGW. +*/ +/************** Include sqlite3.h in the middle of sqliteInt.h ***************/ /************** Begin file sqlite3.h *****************************************/ /* ** 2001 September 15 @@ -135,9 +222,9 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.8.3.1" -#define SQLITE_VERSION_NUMBER 3008003 -#define SQLITE_SOURCE_ID "2014-02-11 14:52:19 ea3317a4803d71d88183b29f1d3086f46d68a00e" +#define SQLITE_VERSION "3.8.4.3" +#define SQLITE_VERSION_NUMBER 3008004 +#define SQLITE_SOURCE_ID "2014-04-03 16:53:12 a611fa96c4a848614efe899130359c9f6fb889c3" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -6150,7 +6237,8 @@ SQLITE_API int sqlite3_test_control(int op, ...); #define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 #define SQLITE_TESTCTRL_EXPLAIN_STMT 19 #define SQLITE_TESTCTRL_NEVER_CORRUPT 20 -#define SQLITE_TESTCTRL_LAST 20 +#define SQLITE_TESTCTRL_VDBE_COVERAGE 21 +#define SQLITE_TESTCTRL_LAST 21 /* ** CAPI3REF: SQLite Runtime Status @@ -7413,50 +7501,7 @@ struct sqlite3_rtree_geometry { /************** End of sqlite3.h *********************************************/ -/************** Begin file sqliteInt.h ***************************************/ -/* -** 2001 September 15 -** -** 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. -** -************************************************************************* -** Internal interface definitions for SQLite. -** -*/ -#ifndef _SQLITEINT_H_ -#define _SQLITEINT_H_ - -/* -** These #defines should enable >2GB file support on POSIX if the -** underlying operating system supports it. If the OS lacks -** large file support, or if the OS is windows, these should be no-ops. -** -** Ticket #2739: The _LARGEFILE_SOURCE macro must appear before any -** system #includes. Hence, this block of code must be the very first -** code in all source files. -** -** Large file support can be disabled using the -DSQLITE_DISABLE_LFS switch -** on the compiler command line. This is necessary if you are compiling -** on a recent machine (ex: Red Hat 7.2) but you want your code to work -** on an older machine (ex: Red Hat 6.0). If you compile on Red Hat 7.2 -** without this option, LFS is enable. But LFS does not exist in the kernel -** in Red Hat 6.0, so the code won't work. Hence, for maximum binary -** portability you should omit LFS. -** -** Similar is true for Mac OS X. LFS is only supported on Mac OS X 9 and later. -*/ -#ifndef SQLITE_DISABLE_LFS -# define _LARGE_FILE 1 -# ifndef _FILE_OFFSET_BITS -# define _FILE_OFFSET_BITS 64 -# endif -# define _LARGEFILE_SOURCE 1 -#endif +/************** Continuing where we left off in sqliteInt.h ******************/ /* ** Include the configuration header output by 'configure' if we're using the @@ -8843,8 +8888,6 @@ SQLITE_PRIVATE const void *sqlite3BtreeKeyFetch(BtCursor*, u32 *pAmt); SQLITE_PRIVATE const void *sqlite3BtreeDataFetch(BtCursor*, u32 *pAmt); SQLITE_PRIVATE int sqlite3BtreeDataSize(BtCursor*, u32 *pSize); SQLITE_PRIVATE int sqlite3BtreeData(BtCursor*, u32 offset, u32 amt, void*); -SQLITE_PRIVATE void sqlite3BtreeSetCachedRowid(BtCursor*, sqlite3_int64); -SQLITE_PRIVATE sqlite3_int64 sqlite3BtreeGetCachedRowid(BtCursor*); SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck(Btree*, int *aRoot, int nRoot, int, int*); SQLITE_PRIVATE struct Pager *sqlite3BtreePager(Btree*); @@ -8984,9 +9027,12 @@ struct VdbeOp { char *zComment; /* Comment to improve readability */ #endif #ifdef VDBE_PROFILE - int cnt; /* Number of times this instruction was executed */ + u32 cnt; /* Number of times this instruction was executed */ u64 cycles; /* Total time spent executing this instruction */ #endif +#ifdef SQLITE_VDBE_COVERAGE + int iSrcLine; /* Source-code line that generated this opcode */ +#endif }; typedef struct VdbeOp VdbeOp; @@ -9096,62 +9142,62 @@ typedef struct VdbeOpList VdbeOpList; #define OP_Gosub 17 #define OP_Return 18 #define OP_Not 19 /* same as TK_NOT, synopsis: r[P2]= !r[P1] */ -#define OP_Yield 20 -#define OP_HaltIfNull 21 /* synopsis: if r[P3] null then halt */ -#define OP_Halt 22 -#define OP_Integer 23 /* synopsis: r[P2]=P1 */ -#define OP_Int64 24 /* synopsis: r[P2]=P4 */ -#define OP_String 25 /* synopsis: r[P2]='P4' (len=P1) */ -#define OP_Null 26 /* synopsis: r[P2..P3]=NULL */ -#define OP_Blob 27 /* synopsis: r[P2]=P4 (len=P1) */ -#define OP_Variable 28 /* synopsis: r[P2]=parameter(P1,P4) */ -#define OP_Move 29 /* synopsis: r[P2@P3]=r[P1@P3] */ -#define OP_Copy 30 /* synopsis: r[P2@P3+1]=r[P1@P3+1] */ -#define OP_SCopy 31 /* synopsis: r[P2]=r[P1] */ -#define OP_ResultRow 32 /* synopsis: output=r[P1@P2] */ -#define OP_CollSeq 33 -#define OP_AddImm 34 /* synopsis: r[P1]=r[P1]+P2 */ -#define OP_MustBeInt 35 -#define OP_RealAffinity 36 -#define OP_Permutation 37 -#define OP_Compare 38 -#define OP_Jump 39 -#define OP_Once 40 -#define OP_If 41 -#define OP_IfNot 42 -#define OP_Column 43 /* synopsis: r[P3]=PX */ -#define OP_Affinity 44 /* synopsis: affinity(r[P1@P2]) */ -#define OP_MakeRecord 45 /* synopsis: r[P3]=mkrec(r[P1@P2]) */ -#define OP_Count 46 /* synopsis: r[P2]=count() */ -#define OP_ReadCookie 47 -#define OP_SetCookie 48 -#define OP_VerifyCookie 49 -#define OP_OpenRead 50 /* synopsis: root=P2 iDb=P3 */ -#define OP_OpenWrite 51 /* synopsis: root=P2 iDb=P3 */ -#define OP_OpenAutoindex 52 /* synopsis: nColumn=P2 */ -#define OP_OpenEphemeral 53 /* synopsis: nColumn=P2 */ -#define OP_SorterOpen 54 -#define OP_OpenPseudo 55 /* synopsis: content in r[P2@P3] */ -#define OP_Close 56 -#define OP_SeekLt 57 /* synopsis: key=r[P3@P4] */ -#define OP_SeekLe 58 /* synopsis: key=r[P3@P4] */ -#define OP_SeekGe 59 /* synopsis: key=r[P3@P4] */ -#define OP_SeekGt 60 /* synopsis: key=r[P3@P4] */ -#define OP_Seek 61 /* synopsis: intkey=r[P2] */ -#define OP_NoConflict 62 /* synopsis: key=r[P3@P4] */ -#define OP_NotFound 63 /* synopsis: key=r[P3@P4] */ -#define OP_Found 64 /* synopsis: key=r[P3@P4] */ -#define OP_NotExists 65 /* synopsis: intkey=r[P3] */ -#define OP_Sequence 66 /* synopsis: r[P2]=rowid */ -#define OP_NewRowid 67 /* synopsis: r[P2]=rowid */ -#define OP_Insert 68 /* synopsis: intkey=r[P3] data=r[P2] */ -#define OP_InsertInt 69 /* synopsis: intkey=P3 data=r[P2] */ -#define OP_Delete 70 +#define OP_InitCoroutine 20 +#define OP_EndCoroutine 21 +#define OP_Yield 22 +#define OP_HaltIfNull 23 /* synopsis: if r[P3]=null halt */ +#define OP_Halt 24 +#define OP_Integer 25 /* synopsis: r[P2]=P1 */ +#define OP_Int64 26 /* synopsis: r[P2]=P4 */ +#define OP_String 27 /* synopsis: r[P2]='P4' (len=P1) */ +#define OP_Null 28 /* synopsis: r[P2..P3]=NULL */ +#define OP_SoftNull 29 /* synopsis: r[P1]=NULL */ +#define OP_Blob 30 /* synopsis: r[P2]=P4 (len=P1) */ +#define OP_Variable 31 /* synopsis: r[P2]=parameter(P1,P4) */ +#define OP_Move 32 /* synopsis: r[P2@P3]=r[P1@P3] */ +#define OP_Copy 33 /* synopsis: r[P2@P3+1]=r[P1@P3+1] */ +#define OP_SCopy 34 /* synopsis: r[P2]=r[P1] */ +#define OP_ResultRow 35 /* synopsis: output=r[P1@P2] */ +#define OP_CollSeq 36 +#define OP_AddImm 37 /* synopsis: r[P1]=r[P1]+P2 */ +#define OP_MustBeInt 38 +#define OP_RealAffinity 39 +#define OP_Permutation 40 +#define OP_Compare 41 +#define OP_Jump 42 +#define OP_Once 43 +#define OP_If 44 +#define OP_IfNot 45 +#define OP_Column 46 /* synopsis: r[P3]=PX */ +#define OP_Affinity 47 /* synopsis: affinity(r[P1@P2]) */ +#define OP_MakeRecord 48 /* synopsis: r[P3]=mkrec(r[P1@P2]) */ +#define OP_Count 49 /* synopsis: r[P2]=count() */ +#define OP_ReadCookie 50 +#define OP_SetCookie 51 +#define OP_OpenRead 52 /* synopsis: root=P2 iDb=P3 */ +#define OP_OpenWrite 53 /* synopsis: root=P2 iDb=P3 */ +#define OP_OpenAutoindex 54 /* synopsis: nColumn=P2 */ +#define OP_OpenEphemeral 55 /* synopsis: nColumn=P2 */ +#define OP_SorterOpen 56 +#define OP_OpenPseudo 57 /* synopsis: P3 columns in r[P2] */ +#define OP_Close 58 +#define OP_SeekLT 59 +#define OP_SeekLE 60 +#define OP_SeekGE 61 +#define OP_SeekGT 62 +#define OP_Seek 63 /* synopsis: intkey=r[P2] */ +#define OP_NoConflict 64 /* synopsis: key=r[P3@P4] */ +#define OP_NotFound 65 /* synopsis: key=r[P3@P4] */ +#define OP_Found 66 /* synopsis: key=r[P3@P4] */ +#define OP_NotExists 67 /* synopsis: intkey=r[P3] */ +#define OP_Sequence 68 /* synopsis: r[P2]=rowid */ +#define OP_NewRowid 69 /* synopsis: r[P2]=rowid */ +#define OP_Insert 70 /* synopsis: intkey=r[P3] data=r[P2] */ #define OP_Or 71 /* same as TK_OR, synopsis: r[P3]=(r[P1] || r[P2]) */ #define OP_And 72 /* same as TK_AND, synopsis: r[P3]=(r[P1] && r[P2]) */ -#define OP_ResetCount 73 -#define OP_SorterCompare 74 /* synopsis: if key(P1)!=rtrim(r[P3],P4) goto P2 */ -#define OP_SorterData 75 /* synopsis: r[P2]=data */ +#define OP_InsertInt 73 /* synopsis: intkey=P3 data=r[P2] */ +#define OP_Delete 74 +#define OP_ResetCount 75 #define OP_IsNull 76 /* same as TK_ISNULL, synopsis: if r[P1]==NULL goto P2 */ #define OP_NotNull 77 /* same as TK_NOTNULL, synopsis: if r[P1]!=NULL goto P2 */ #define OP_Ne 78 /* same as TK_NE, synopsis: if r[P1]!=r[P3] goto P2 */ @@ -9160,7 +9206,7 @@ typedef struct VdbeOpList VdbeOpList; #define OP_Le 81 /* same as TK_LE, synopsis: if r[P1]<=r[P3] goto P2 */ #define OP_Lt 82 /* same as TK_LT, synopsis: if r[P1]<r[P3] goto P2 */ #define OP_Ge 83 /* same as TK_GE, synopsis: if r[P1]>=r[P3] goto P2 */ -#define OP_RowKey 84 /* synopsis: r[P2]=key */ +#define OP_SorterCompare 84 /* synopsis: if key(P1)!=rtrim(r[P3],P4) goto P2 */ #define OP_BitAnd 85 /* same as TK_BITAND, synopsis: r[P3]=r[P1]&r[P2] */ #define OP_BitOr 86 /* same as TK_BITOR, synopsis: r[P3]=r[P1]|r[P2] */ #define OP_ShiftLeft 87 /* same as TK_LSHIFT, synopsis: r[P3]=r[P2]<<r[P1] */ @@ -9171,64 +9217,68 @@ typedef struct VdbeOpList VdbeOpList; #define OP_Divide 92 /* same as TK_SLASH, synopsis: r[P3]=r[P2]/r[P1] */ #define OP_Remainder 93 /* same as TK_REM, synopsis: r[P3]=r[P2]%r[P1] */ #define OP_Concat 94 /* same as TK_CONCAT, synopsis: r[P3]=r[P2]+r[P1] */ -#define OP_RowData 95 /* synopsis: r[P2]=data */ +#define OP_SorterData 95 /* synopsis: r[P2]=data */ #define OP_BitNot 96 /* same as TK_BITNOT, synopsis: r[P1]= ~r[P1] */ #define OP_String8 97 /* same as TK_STRING, synopsis: r[P2]='P4' */ -#define OP_Rowid 98 /* synopsis: r[P2]=rowid */ -#define OP_NullRow 99 -#define OP_Last 100 -#define OP_SorterSort 101 -#define OP_Sort 102 -#define OP_Rewind 103 -#define OP_SorterInsert 104 -#define OP_IdxInsert 105 /* synopsis: key=r[P2] */ -#define OP_IdxDelete 106 /* synopsis: key=r[P2@P3] */ -#define OP_IdxRowid 107 /* synopsis: r[P2]=rowid */ -#define OP_IdxLT 108 /* synopsis: key=r[P3@P4] */ -#define OP_IdxGE 109 /* synopsis: key=r[P3@P4] */ -#define OP_Destroy 110 -#define OP_Clear 111 -#define OP_CreateIndex 112 /* synopsis: r[P2]=root iDb=P1 */ -#define OP_CreateTable 113 /* synopsis: r[P2]=root iDb=P1 */ -#define OP_ParseSchema 114 -#define OP_LoadAnalysis 115 -#define OP_DropTable 116 -#define OP_DropIndex 117 -#define OP_DropTrigger 118 -#define OP_IntegrityCk 119 -#define OP_RowSetAdd 120 /* synopsis: rowset(P1)=r[P2] */ -#define OP_RowSetRead 121 /* synopsis: r[P3]=rowset(P1) */ -#define OP_RowSetTest 122 /* synopsis: if r[P3] in rowset(P1) goto P2 */ -#define OP_Program 123 -#define OP_Param 124 -#define OP_FkCounter 125 /* synopsis: fkctr[P1]+=P2 */ -#define OP_FkIfZero 126 /* synopsis: if fkctr[P1]==0 goto P2 */ -#define OP_MemMax 127 /* synopsis: r[P1]=max(r[P1],r[P2]) */ -#define OP_IfPos 128 /* synopsis: if r[P1]>0 goto P2 */ -#define OP_IfNeg 129 /* synopsis: if r[P1]<0 goto P2 */ -#define OP_IfZero 130 /* synopsis: r[P1]+=P3, if r[P1]==0 goto P2 */ -#define OP_AggFinal 131 /* synopsis: accum=r[P1] N=P2 */ -#define OP_IncrVacuum 132 +#define OP_RowKey 98 /* synopsis: r[P2]=key */ +#define OP_RowData 99 /* synopsis: r[P2]=data */ +#define OP_Rowid 100 /* synopsis: r[P2]=rowid */ +#define OP_NullRow 101 +#define OP_Last 102 +#define OP_SorterSort 103 +#define OP_Sort 104 +#define OP_Rewind 105 +#define OP_SorterInsert 106 +#define OP_IdxInsert 107 /* synopsis: key=r[P2] */ +#define OP_IdxDelete 108 /* synopsis: key=r[P2@P3] */ +#define OP_IdxRowid 109 /* synopsis: r[P2]=rowid */ +#define OP_IdxLE 110 /* synopsis: key=r[P3@P4] */ +#define OP_IdxGT 111 /* synopsis: key=r[P3@P4] */ +#define OP_IdxLT 112 /* synopsis: key=r[P3@P4] */ +#define OP_IdxGE 113 /* synopsis: key=r[P3@P4] */ +#define OP_Destroy 114 +#define OP_Clear 115 +#define OP_CreateIndex 116 /* synopsis: r[P2]=root iDb=P1 */ +#define OP_CreateTable 117 /* synopsis: r[P2]=root iDb=P1 */ +#define OP_ParseSchema 118 +#define OP_LoadAnalysis 119 +#define OP_DropTable 120 +#define OP_DropIndex 121 +#define OP_DropTrigger 122 +#define OP_IntegrityCk 123 +#define OP_RowSetAdd 124 /* synopsis: rowset(P1)=r[P2] */ +#define OP_RowSetRead 125 /* synopsis: r[P3]=rowset(P1) */ +#define OP_RowSetTest 126 /* synopsis: if r[P3] in rowset(P1) goto P2 */ +#define OP_Program 127 +#define OP_Param 128 +#define OP_FkCounter 129 /* synopsis: fkctr[P1]+=P2 */ +#define OP_FkIfZero 130 /* synopsis: if fkctr[P1]==0 goto P2 */ +#define OP_MemMax 131 /* synopsis: r[P1]=max(r[P1],r[P2]) */ +#define OP_IfPos 132 /* synopsis: if r[P1]>0 goto P2 */ #define OP_Real 133 /* same as TK_FLOAT, synopsis: r[P2]=P4 */ -#define OP_Expire 134 -#define OP_TableLock 135 /* synopsis: iDb=P1 root=P2 write=P3 */ -#define OP_VBegin 136 -#define OP_VCreate 137 -#define OP_VDestroy 138 -#define OP_VOpen 139 -#define OP_VColumn 140 /* synopsis: r[P3]=vcolumn(P2) */ -#define OP_VNext 141 -#define OP_VRename 142 +#define OP_IfNeg 134 /* synopsis: if r[P1]<0 goto P2 */ +#define OP_IfZero 135 /* synopsis: r[P1]+=P3, if r[P1]==0 goto P2 */ +#define OP_AggFinal 136 /* synopsis: accum=r[P1] N=P2 */ +#define OP_IncrVacuum 137 +#define OP_Expire 138 +#define OP_TableLock 139 /* synopsis: iDb=P1 root=P2 write=P3 */ +#define OP_VBegin 140 +#define OP_VCreate 141 +#define OP_VDestroy 142 #define OP_ToText 143 /* same as TK_TO_TEXT */ #define OP_ToBlob 144 /* same as TK_TO_BLOB */ #define OP_ToNumeric 145 /* same as TK_TO_NUMERIC */ #define OP_ToInt 146 /* same as TK_TO_INT */ #define OP_ToReal 147 /* same as TK_TO_REAL */ -#define OP_Pagecount 148 -#define OP_MaxPgcnt 149 -#define OP_Trace 150 -#define OP_Noop 151 -#define OP_Explain 152 +#define OP_VOpen 148 +#define OP_VColumn 149 /* synopsis: r[P3]=vcolumn(P2) */ +#define OP_VNext 150 +#define OP_VRename 151 +#define OP_Pagecount 152 +#define OP_MaxPgcnt 153 +#define OP_Init 154 /* synopsis: Start at P2 */ +#define OP_Noop 155 +#define OP_Explain 156 /* Properties such as "out2" or "jump" that are specified in @@ -9245,24 +9295,24 @@ typedef struct VdbeOpList VdbeOpList; #define OPFLG_INITIALIZER {\ /* 0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,\ /* 8 */ 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00,\ -/* 16 */ 0x01, 0x01, 0x04, 0x24, 0x04, 0x10, 0x00, 0x02,\ -/* 24 */ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x20,\ -/* 32 */ 0x00, 0x00, 0x04, 0x05, 0x04, 0x00, 0x00, 0x01,\ -/* 40 */ 0x01, 0x05, 0x05, 0x00, 0x00, 0x00, 0x02, 0x02,\ -/* 48 */ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ -/* 56 */ 0x00, 0x11, 0x11, 0x11, 0x11, 0x08, 0x11, 0x11,\ -/* 64 */ 0x11, 0x11, 0x02, 0x02, 0x00, 0x00, 0x00, 0x4c,\ +/* 16 */ 0x01, 0x01, 0x04, 0x24, 0x01, 0x04, 0x05, 0x10,\ +/* 24 */ 0x00, 0x02, 0x02, 0x02, 0x02, 0x00, 0x02, 0x02,\ +/* 32 */ 0x00, 0x00, 0x20, 0x00, 0x00, 0x04, 0x05, 0x04,\ +/* 40 */ 0x00, 0x00, 0x01, 0x01, 0x05, 0x05, 0x00, 0x00,\ +/* 48 */ 0x00, 0x02, 0x02, 0x10, 0x00, 0x00, 0x00, 0x00,\ +/* 56 */ 0x00, 0x00, 0x00, 0x11, 0x11, 0x11, 0x11, 0x08,\ +/* 64 */ 0x11, 0x11, 0x11, 0x11, 0x02, 0x02, 0x00, 0x4c,\ /* 72 */ 0x4c, 0x00, 0x00, 0x00, 0x05, 0x05, 0x15, 0x15,\ /* 80 */ 0x15, 0x15, 0x15, 0x15, 0x00, 0x4c, 0x4c, 0x4c,\ /* 88 */ 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x00,\ -/* 96 */ 0x24, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01,\ -/* 104 */ 0x08, 0x08, 0x00, 0x02, 0x01, 0x01, 0x02, 0x00,\ -/* 112 */ 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ -/* 120 */ 0x0c, 0x45, 0x15, 0x01, 0x02, 0x00, 0x01, 0x08,\ -/* 128 */ 0x05, 0x05, 0x05, 0x00, 0x01, 0x02, 0x00, 0x00,\ -/* 136 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04,\ -/* 144 */ 0x04, 0x04, 0x04, 0x04, 0x02, 0x02, 0x00, 0x00,\ -/* 152 */ 0x00,} +/* 96 */ 0x24, 0x02, 0x00, 0x00, 0x02, 0x00, 0x01, 0x01,\ +/* 104 */ 0x01, 0x01, 0x08, 0x08, 0x00, 0x02, 0x01, 0x01,\ +/* 112 */ 0x01, 0x01, 0x02, 0x00, 0x02, 0x02, 0x00, 0x00,\ +/* 120 */ 0x00, 0x00, 0x00, 0x00, 0x0c, 0x45, 0x15, 0x01,\ +/* 128 */ 0x02, 0x00, 0x01, 0x08, 0x05, 0x02, 0x05, 0x05,\ +/* 136 */ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,\ +/* 144 */ 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x01, 0x00,\ +/* 152 */ 0x02, 0x02, 0x01, 0x00, 0x00,} /************** End of opcodes.h *********************************************/ /************** Continuing where we left off in vdbe.h ***********************/ @@ -9278,7 +9328,7 @@ SQLITE_PRIVATE int sqlite3VdbeAddOp2(Vdbe*,int,int,int); 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 int sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp, int iLineno); SQLITE_PRIVATE void sqlite3VdbeAddParseSchemaOp(Vdbe*,int,char*); SQLITE_PRIVATE void sqlite3VdbeChangeP1(Vdbe*, u32 addr, int P1); SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe*, u32 addr, int P2); @@ -9319,9 +9369,12 @@ SQLITE_PRIVATE char *sqlite3VdbeExpandSql(Vdbe*, const char*); #endif SQLITE_PRIVATE void sqlite3VdbeRecordUnpack(KeyInfo*,int,const void*,UnpackedRecord*); -SQLITE_PRIVATE int sqlite3VdbeRecordCompare(int,const void*,UnpackedRecord*); +SQLITE_PRIVATE int sqlite3VdbeRecordCompare(int,const void*,const UnpackedRecord*,int); SQLITE_PRIVATE UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(KeyInfo *, char *, int, char **); +typedef int (*RecordCompare)(int,const void*,const UnpackedRecord*,int); +SQLITE_PRIVATE RecordCompare sqlite3VdbeFindCompare(UnpackedRecord*); + #ifndef SQLITE_OMIT_TRIGGER SQLITE_PRIVATE void sqlite3VdbeLinkSubProgram(Vdbe *, SubProgram *); #endif @@ -9349,6 +9402,43 @@ SQLITE_PRIVATE void sqlite3VdbeNoopComment(Vdbe*, const char*, ...); # define VdbeModuleComment(X) #endif +/* +** The VdbeCoverage macros are used to set a coverage testing point +** for VDBE branch instructions. The coverage testing points are line +** numbers in the sqlite3.c source file. VDBE branch coverage testing +** only works with an amalagmation build. That's ok since a VDBE branch +** coverage build designed for testing the test suite only. No application +** should ever ship with VDBE branch coverage measuring turned on. +** +** VdbeCoverage(v) // Mark the previously coded instruction +** // as a branch +** +** VdbeCoverageIf(v, conditional) // Mark previous if conditional true +** +** VdbeCoverageAlwaysTaken(v) // Previous branch is always taken +** +** VdbeCoverageNeverTaken(v) // Previous branch is never taken +** +** Every VDBE branch operation must be tagged with one of the macros above. +** If not, then when "make test" is run with -DSQLITE_VDBE_COVERAGE and +** -DSQLITE_DEBUG then an ALWAYS() will fail in the vdbeTakeBranch() +** routine in vdbe.c, alerting the developer to the missed tag. +*/ +#ifdef SQLITE_VDBE_COVERAGE +SQLITE_PRIVATE void sqlite3VdbeSetLineNumber(Vdbe*,int); +# define VdbeCoverage(v) sqlite3VdbeSetLineNumber(v,__LINE__) +# define VdbeCoverageIf(v,x) if(x)sqlite3VdbeSetLineNumber(v,__LINE__) +# define VdbeCoverageAlwaysTaken(v) sqlite3VdbeSetLineNumber(v,2); +# define VdbeCoverageNeverTaken(v) sqlite3VdbeSetLineNumber(v,1); +# define VDBE_OFFSET_LINENO(x) (__LINE__+x) +#else +# define VdbeCoverage(v) +# define VdbeCoverageIf(v,x) +# define VdbeCoverageAlwaysTaken(v) +# define VdbeCoverageNeverTaken(v) +# define VDBE_OFFSET_LINENO(x) 0 +#endif + #endif /************** End of vdbe.h ************************************************/ @@ -10406,8 +10496,7 @@ struct sqlite3 { ** Return true if it OK to factor constant expressions into the initialization ** code. The argument is a Parse object for the code generator. */ -#define ConstFactorOk(P) \ - ((P)->cookieGoto>0 && OptimizationEnabled((P)->db,SQLITE_FactorOutConst)) +#define ConstFactorOk(P) ((P)->okConstFactor) /* ** Possible values for the sqlite.magic field. @@ -10633,10 +10722,16 @@ struct CollSeq { /* ** Additional bit values that can be ORed with an affinity without ** changing the affinity. +** +** The SQLITE_NOTNULL flag is a combination of NULLEQ and JUMPIFNULL. +** It causes an assert() to fire if either operand to a comparison +** operator is NULL. It is added to certain comparison operators to +** prove that the operands are always NOT NULL. */ #define SQLITE_JUMPIFNULL 0x08 /* jumps if either operand is NULL */ #define SQLITE_STOREP2 0x10 /* Store result in reg[P2] rather than jump */ #define SQLITE_NULLEQ 0x80 /* NULL=NULL */ +#define SQLITE_NOTNULL 0x88 /* Assert that operands are never NULL */ /* ** An object of this type is created for each virtual table present in @@ -10895,19 +10990,19 @@ struct KeyInfo { ** ** This structure holds a record that has already been disassembled ** into its constituent fields. +** +** The r1 and r2 member variables are only used by the optimized comparison +** functions vdbeRecordCompareInt() and vdbeRecordCompareString(). */ struct UnpackedRecord { KeyInfo *pKeyInfo; /* Collation and sort-order information */ u16 nField; /* Number of entries in apMem[] */ - u8 flags; /* Boolean settings. UNPACKED_... below */ + i8 default_rc; /* Comparison result if keys are equal */ Mem *aMem; /* Values */ + int r1; /* Value to return if (lhs > rhs) */ + int r2; /* Value to return if (rhs < lhs) */ }; -/* -** Allowed values of UnpackedRecord.flags -*/ -#define UNPACKED_INCRKEY 0x01 /* Make this key an epsilon larger */ -#define UNPACKED_PREFIX_MATCH 0x02 /* A prefix match is considered OK */ /* ** Each SQL index is represented in memory by an @@ -11327,8 +11422,8 @@ typedef u64 Bitmask; ** contains more than 63 columns and the 64-th or later column is used. */ struct SrcList { - u8 nSrc; /* Number of tables or subqueries in the FROM clause */ - u8 nAlloc; /* Number of entries allocated in a[] below */ + int nSrc; /* Number of tables or subqueries in the FROM clause */ + u32 nAlloc; /* Number of entries allocated in a[] below */ struct SrcList_item { Schema *pSchema; /* Schema to which this item is fixed */ char *zDatabase; /* Name of database holding this table */ @@ -11338,6 +11433,7 @@ struct SrcList { Select *pSelect; /* A SELECT statement used in place of a table name */ int addrFillSub; /* Address of subroutine to manifest a subquery */ int regReturn; /* Register holding return address of addrFillSub */ + int regResult; /* Registers holding results of a co-routine */ u8 jointype; /* Type of join between this able and the previous */ unsigned notIndexed :1; /* True if there is a NOT INDEXED clause */ unsigned isCorrelated :1; /* True if sub-query is correlated */ @@ -11466,7 +11562,6 @@ struct Select { ExprList *pOrderBy; /* The ORDER BY clause */ Select *pPrior; /* Prior select in a compound select statement */ Select *pNext; /* Next select to the left in a compound */ - Select *pRightmost; /* Right-most select in a compound select statement */ Expr *pLimit; /* LIMIT expression. NULL means not used. */ Expr *pOffset; /* OFFSET expression. NULL means not used. */ With *pWith; /* WITH clause attached to this select. Or NULL. */ @@ -11484,10 +11579,11 @@ struct Select { #define SF_HasTypeInfo 0x0020 /* FROM subqueries have Table metadata */ #define SF_UseSorter 0x0040 /* Sort using a sorter */ #define SF_Values 0x0080 /* Synthesized from VALUES clause */ -#define SF_Materialize 0x0100 /* Force materialization of views */ +#define SF_Materialize 0x0100 /* NOT USED */ #define SF_NestedFrom 0x0200 /* Part of a parenthesized FROM clause */ #define SF_MaybeConvert 0x0400 /* Need convertCompoundSelectToSubquery() */ #define SF_Recursive 0x0800 /* The recursive part of a recursive CTE */ +#define SF_Compound 0x1000 /* Part of a compound query */ /* @@ -11666,12 +11762,12 @@ struct Parse { u8 checkSchema; /* Causes schema cookie check after an error */ u8 nested; /* Number of nested calls to the parser/code generator */ u8 nTempReg; /* Number of temporary registers in aTempReg[] */ - u8 nTempInUse; /* Number of aTempReg[] currently checked out */ u8 nColCache; /* Number of entries in aColCache[] */ u8 iColCache; /* Next entry in aColCache[] to replace */ u8 isMultiWrite; /* True if statement may modify/insert multiple rows */ u8 mayAbort; /* True if statement may throw an ABORT exception */ u8 hasCompound; /* Need to invoke convertCompoundSelectToSubquery() */ + u8 okConstFactor; /* OK to factor out constants */ int aTempReg[8]; /* Holding area for temporary registers */ int nRangeReg; /* Size of the temporary register block */ int iRangeReg; /* First register in temporary register block */ @@ -11681,30 +11777,29 @@ struct Parse { int nSet; /* Number of sets used so far */ int nOnce; /* Number of OP_Once instructions so far */ int nOpAlloc; /* Number of slots allocated for Vdbe.aOp[] */ - int nLabel; /* Number of labels used */ - int *aLabel; /* Space to hold the labels */ int iFixedOp; /* Never back out opcodes iFixedOp-1 or earlier */ int ckBase; /* Base register of data during check constraints */ int iPartIdxTab; /* Table corresponding to a partial index */ int iCacheLevel; /* ColCache valid when aColCache[].iLevel<=iCacheLevel */ int iCacheCnt; /* Counter used to generate aColCache[].lru values */ + int nLabel; /* Number of labels used */ + int *aLabel; /* Space to hold the labels */ struct yColCache { int iTable; /* Table cursor number */ - int iColumn; /* Table column number */ + i16 iColumn; /* Table column number */ u8 tempReg; /* iReg is a temp register that needs to be freed */ int iLevel; /* Nesting level */ 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 */ ExprList *pConstExpr;/* Constant expressions */ + Token constraintName;/* Name of the constraint currently being parsed */ yDbMask writeMask; /* Start a write transaction on these databases */ yDbMask cookieMask; /* Bitmask of schema verified databases */ - int cookieGoto; /* Address of OP_Goto to cookie verifier subroutine */ int cookieValue[SQLITE_MAX_ATTACHED+2]; /* Values of cookies to verify */ int regRowid; /* Register holding rowid of CREATE TABLE entry */ int regRoot; /* Register holding root page number for new objects */ int nMaxArg; /* Max args passed to user function by sub-program */ - Token constraintName;/* Name of the constraint currently being parsed */ #ifndef SQLITE_OMIT_SHARED_CACHE int nTableLock; /* Number of locks in aTableLock */ TableLock *aTableLock; /* Required table locks for shared-cache mode */ @@ -11723,12 +11818,17 @@ struct Parse { u8 eOrconf; /* Default ON CONFLICT policy for trigger steps */ u8 disableTriggers; /* True to disable triggers */ - /* Above is constant between recursions. Below is reset before and after - ** each recursion */ + /************************************************************************ + ** Above is constant between recursions. Below is reset before and after + ** each recursion. The boundary between these two regions is determined + ** using offsetof(Parse,nVar) so the nVar field must be the first field + ** in the recursive region. + ************************************************************************/ int nVar; /* Number of '?' variables seen in the SQL so far */ int nzVar; /* Number of available slots in azVar[] */ u8 iPkSortOrder; /* ASC or DESC for INTEGER PRIMARY KEY */ + u8 bFreeWith; /* True if pWith should be freed with parser */ u8 explain; /* True if the EXPLAIN flag is found on the query */ #ifndef SQLITE_OMIT_VIRTUALTABLE u8 declareVtab; /* True if inside sqlite3_declare_vtab() */ @@ -11755,7 +11855,6 @@ struct Parse { Table *pZombieTab; /* List of Table objects to delete after code gen */ TriggerPrg *pTriggerPrg; /* Linked list of coded triggers */ With *pWith; /* Current WITH clause, or NULL */ - u8 bFreeWith; /* True if pWith should be freed with parser */ }; /* @@ -11971,6 +12070,13 @@ struct Sqlite3Config { void(*xSqllog)(void*,sqlite3*,const char*, int); void *pSqllogArg; #endif +#ifdef SQLITE_VDBE_COVERAGE + /* The following callback (if not NULL) is invoked on every VDBE branch + ** operation. Set the callback using SQLITE_TESTCTRL_VDBE_COVERAGE. + */ + void (*xVdbeBranch)(void*,int iSrcLine,u8 eThis,u8 eMx); /* Callback */ + void *pVdbeBranchArg; /* 1st argument */ +#endif }; /* @@ -12304,7 +12410,6 @@ SQLITE_PRIVATE void sqlite3AutoincrementEnd(Parse *pParse); # define sqlite3AutoincrementBegin(X) # define sqlite3AutoincrementEnd(X) #endif -SQLITE_PRIVATE int sqlite3CodeCoroutine(Parse*, Select*, SelectDest*); SQLITE_PRIVATE void sqlite3Insert(Parse*, SrcList*, Select*, IdList*, int); SQLITE_PRIVATE void *sqlite3ArrayAllocate(sqlite3*,void*,int,int*,int*); SQLITE_PRIVATE IdList *sqlite3IdListAppend(sqlite3*, IdList*, Token*); @@ -12352,11 +12457,12 @@ SQLITE_PRIVATE void sqlite3ExprCachePop(Parse*, int); SQLITE_PRIVATE void sqlite3ExprCacheRemove(Parse*, int, int); SQLITE_PRIVATE void sqlite3ExprCacheClear(Parse*); SQLITE_PRIVATE void sqlite3ExprCacheAffinityChange(Parse*, int, int); -SQLITE_PRIVATE int sqlite3ExprCode(Parse*, Expr*, int); +SQLITE_PRIVATE void sqlite3ExprCode(Parse*, Expr*, int); +SQLITE_PRIVATE void sqlite3ExprCodeFactorable(Parse*, Expr*, int); SQLITE_PRIVATE void sqlite3ExprCodeAtInit(Parse*, Expr*, int, u8); SQLITE_PRIVATE int sqlite3ExprCodeTemp(Parse*, Expr*, int*); SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse*, Expr*, int); -SQLITE_PRIVATE int sqlite3ExprCodeAndCache(Parse*, Expr*, int); +SQLITE_PRIVATE void sqlite3ExprCodeAndCache(Parse*, Expr*, int); SQLITE_PRIVATE int sqlite3ExprCodeExprList(Parse*, ExprList*, int, u8); #define SQLITE_ECEL_DUP 0x01 /* Deep, not shallow copies */ #define SQLITE_ECEL_FACTOR 0x02 /* Factor out constant terms */ @@ -12394,7 +12500,6 @@ SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr*); SQLITE_PRIVATE int sqlite3ExprIsConstantOrFunction(Expr*); SQLITE_PRIVATE int sqlite3ExprIsInteger(Expr*, int*); SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr*); -SQLITE_PRIVATE void sqlite3ExprCodeIsNullJump(Vdbe*, const Expr*, int, int); SQLITE_PRIVATE int sqlite3ExprNeedsNoAffinityChange(const Expr*, char); SQLITE_PRIVATE int sqlite3IsRowid(const char*); SQLITE_PRIVATE void sqlite3GenerateRowDelete(Parse*,Table*,Trigger*,int,int,int,i16,u8,u8,u8); @@ -12538,7 +12643,7 @@ SQLITE_PRIVATE int sqlite3VarintLen(u64 v); SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(Vdbe *, Index *); -SQLITE_PRIVATE void sqlite3TableAffinityStr(Vdbe *, Table *); +SQLITE_PRIVATE void sqlite3TableAffinity(Vdbe*, Table*, int); SQLITE_PRIVATE char sqlite3CompareAffinity(Expr *pExpr, char aff2); SQLITE_PRIVATE int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity); SQLITE_PRIVATE char sqlite3ExprAffinity(Expr *pExpr); @@ -13351,6 +13456,9 @@ static const char * const azCompileOpt[] = { #ifdef SQLITE_OMIT_COMPOUND_SELECT "OMIT_COMPOUND_SELECT", #endif +#ifdef SQLITE_OMIT_CTE + "OMIT_CTE", +#endif #ifdef SQLITE_OMIT_DATETIME_FUNCS "OMIT_DATETIME_FUNCS", #endif @@ -13642,7 +13750,6 @@ struct VdbeCursor { Bool useRandomRowid:1;/* Generate new record numbers semi-randomly */ Bool isTable:1; /* True if a table requiring integer keys */ Bool isOrdered:1; /* True if the underlying table is BTREE_UNORDERED */ - Bool multiPseudo:1; /* Multi-register pseudo-cursor */ sqlite3_vtab_cursor *pVtabCursor; /* The cursor for a virtual table */ i64 seqCount; /* Sequence counter */ i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */ @@ -13736,7 +13843,6 @@ struct Mem { } u; 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 */ u8 enc; /* SQLITE_UTF8, SQLITE_UTF16BE, SQLITE_UTF16LE */ #ifdef SQLITE_DEBUG Mem *pScopyFrom; /* This Mem is a shallow copy of pScopyFrom */ @@ -13763,9 +13869,10 @@ struct Mem { #define MEM_Int 0x0004 /* Value is an integer */ #define MEM_Real 0x0008 /* Value is a real number */ #define MEM_Blob 0x0010 /* Value is a BLOB */ +#define MEM_AffMask 0x001f /* Mask of affinity bits */ #define MEM_RowSet 0x0020 /* Value is a RowSet object */ #define MEM_Frame 0x0040 /* Value is a VdbeFrame object */ -#define MEM_Invalid 0x0080 /* Value is undefined */ +#define MEM_Undefined 0x0080 /* Value is undefined */ #define MEM_Cleared 0x0100 /* NULL set by OP_Null, not from data */ #define MEM_TypeMask 0x01ff /* Mask of type bits */ @@ -13776,7 +13883,7 @@ struct Mem { ** string is \000 or \u0000 terminated */ #define MEM_Term 0x0200 /* String rep is nul terminated */ -#define MEM_Dyn 0x0400 /* Need to call sqliteFree() on Mem.z */ +#define MEM_Dyn 0x0400 /* Need to call Mem.xDel() on Mem.z */ #define MEM_Static 0x0800 /* Mem.z points to a static string */ #define MEM_Ephem 0x1000 /* Mem.z points to an ephemeral string */ #define MEM_Agg 0x2000 /* Mem.z points to an agg function context */ @@ -13797,7 +13904,7 @@ struct Mem { ** is for use inside assert() statements only. */ #ifdef SQLITE_DEBUG -#define memIsValid(M) ((M)->flags & MEM_Invalid)==0 +#define memIsValid(M) ((M)->flags & MEM_Undefined)==0 #endif /* @@ -13959,7 +14066,7 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialGet(const unsigned char*, u32, Mem*); SQLITE_PRIVATE void sqlite3VdbeDeleteAuxData(Vdbe*, int, int); int sqlite2BtreeKeyCompare(BtCursor *, const void *, int, int, int *); -SQLITE_PRIVATE int sqlite3VdbeIdxKeyCompare(VdbeCursor*,UnpackedRecord*,int*); +SQLITE_PRIVATE int sqlite3VdbeIdxKeyCompare(VdbeCursor*,const UnpackedRecord*,int*); SQLITE_PRIVATE int sqlite3VdbeIdxRowid(sqlite3*, BtCursor *, i64 *); SQLITE_PRIVATE int sqlite3MemCompare(const Mem*, const Mem*, const CollSeq*); SQLITE_PRIVATE int sqlite3VdbeExec(Vdbe*); @@ -13992,16 +14099,16 @@ SQLITE_PRIVATE int sqlite3VdbeMemNumerify(Mem*); SQLITE_PRIVATE int sqlite3VdbeMemFromBtree(BtCursor*,u32,u32,int,Mem*); SQLITE_PRIVATE void sqlite3VdbeMemRelease(Mem *p); SQLITE_PRIVATE void sqlite3VdbeMemReleaseExternal(Mem *p); +#define VdbeMemDynamic(X) \ + (((X)->flags&(MEM_Agg|MEM_Dyn|MEM_RowSet|MEM_Frame))!=0) #define VdbeMemRelease(X) \ - if((X)->flags&(MEM_Agg|MEM_Dyn|MEM_RowSet|MEM_Frame)) \ - sqlite3VdbeMemReleaseExternal(X); + if( VdbeMemDynamic(X) ) sqlite3VdbeMemReleaseExternal(X); SQLITE_PRIVATE int sqlite3VdbeMemFinalize(Mem*, FuncDef*); SQLITE_PRIVATE const char *sqlite3OpcodeName(int); SQLITE_PRIVATE int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve); SQLITE_PRIVATE int sqlite3VdbeCloseStatement(Vdbe *, int); SQLITE_PRIVATE void sqlite3VdbeFrameDelete(VdbeFrame*); SQLITE_PRIVATE int sqlite3VdbeFrameRestore(VdbeFrame *); -SQLITE_PRIVATE void sqlite3VdbeMemStoreType(Mem *pMem); SQLITE_PRIVATE int sqlite3VdbeTransferError(Vdbe *p); SQLITE_PRIVATE int sqlite3VdbeSorterInit(sqlite3 *, VdbeCursor *); @@ -14022,6 +14129,7 @@ SQLITE_PRIVATE void sqlite3VdbeLeave(Vdbe*); #ifdef SQLITE_DEBUG SQLITE_PRIVATE void sqlite3VdbeMemAboutToChange(Vdbe*,Mem*); +SQLITE_PRIVATE int sqlite3VdbeCheckMemInvariants(Mem*); #endif #ifndef SQLITE_OMIT_FOREIGN_KEY @@ -17770,6 +17878,12 @@ static void *memsys5MallocUnsafe(int nByte){ if( mem5.maxCount<mem5.currentCount ) mem5.maxCount = mem5.currentCount; if( mem5.maxOut<mem5.currentOut ) mem5.maxOut = mem5.currentOut; +#ifdef SQLITE_DEBUG + /* Make sure the allocated memory does not assume that it is set to zero + ** or retains a value from a previous allocation */ + memset(&mem5.zPool[i*mem5.szAtom], 0xAA, iFullSz); +#endif + /* Return a pointer to the allocated memory. */ return (void*)&mem5.zPool[i*mem5.szAtom]; } @@ -17827,6 +17941,13 @@ static void memsys5FreeUnsafe(void *pOld){ } size *= 2; } + +#ifdef SQLITE_DEBUG + /* Overwrite freed memory with the 0x55 bit pattern to verify that it is + ** not used after being freed */ + memset(&mem5.zPool[iBlock*mem5.szAtom], 0x55, size); +#endif + memsys5Link(iBlock, iLogsize); } @@ -21392,7 +21513,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemTranslate(Mem *pMem, u8 desiredEnc){ sqlite3VdbeMemRelease(pMem); pMem->flags &= ~(MEM_Static|MEM_Dyn|MEM_Ephem); pMem->enc = desiredEnc; - pMem->flags |= (MEM_Term|MEM_Dyn); + pMem->flags |= (MEM_Term); pMem->z = (char*)zOut; pMem->zMalloc = pMem->z; @@ -21520,7 +21641,6 @@ SQLITE_PRIVATE char *sqlite3Utf16to8(sqlite3 *db, const void *z, int nByte, u8 e } assert( (m.flags & MEM_Term)!=0 || db->mallocFailed ); assert( (m.flags & MEM_Str)!=0 || db->mallocFailed ); - assert( (m.flags & MEM_Dyn)!=0 || db->mallocFailed ); assert( m.z || db->mallocFailed ); return m.z; } @@ -22730,13 +22850,12 @@ SQLITE_PRIVATE int sqlite3AddInt64(i64 *pA, i64 iB){ 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; } + *pA += iB; return 0; } SQLITE_PRIVATE int sqlite3SubInt64(i64 *pA, i64 iB){ @@ -22760,9 +22879,18 @@ SQLITE_PRIVATE int sqlite3MulInt64(i64 *pA, i64 iB){ 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; + if( iA1==0 ){ + if( iB1==0 ){ + *pA *= iB; + return 0; + } + r = iA0*iB1; + }else if( iB1==0 ){ + r = iA1*iB0; + }else{ + /* If both iA1 and iB1 are non-zero, overflow will result */ + return 1; + } testcase( r==(-TWOPOWER31)-1 ); testcase( r==(-TWOPOWER31) ); testcase( r==TWOPOWER31 ); @@ -23208,62 +23336,62 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ /* 17 */ "Gosub" OpHelp(""), /* 18 */ "Return" OpHelp(""), /* 19 */ "Not" OpHelp("r[P2]= !r[P1]"), - /* 20 */ "Yield" OpHelp(""), - /* 21 */ "HaltIfNull" OpHelp("if r[P3] null then halt"), - /* 22 */ "Halt" OpHelp(""), - /* 23 */ "Integer" OpHelp("r[P2]=P1"), - /* 24 */ "Int64" OpHelp("r[P2]=P4"), - /* 25 */ "String" OpHelp("r[P2]='P4' (len=P1)"), - /* 26 */ "Null" OpHelp("r[P2..P3]=NULL"), - /* 27 */ "Blob" OpHelp("r[P2]=P4 (len=P1)"), - /* 28 */ "Variable" OpHelp("r[P2]=parameter(P1,P4)"), - /* 29 */ "Move" OpHelp("r[P2@P3]=r[P1@P3]"), - /* 30 */ "Copy" OpHelp("r[P2@P3+1]=r[P1@P3+1]"), - /* 31 */ "SCopy" OpHelp("r[P2]=r[P1]"), - /* 32 */ "ResultRow" OpHelp("output=r[P1@P2]"), - /* 33 */ "CollSeq" OpHelp(""), - /* 34 */ "AddImm" OpHelp("r[P1]=r[P1]+P2"), - /* 35 */ "MustBeInt" OpHelp(""), - /* 36 */ "RealAffinity" OpHelp(""), - /* 37 */ "Permutation" OpHelp(""), - /* 38 */ "Compare" OpHelp(""), - /* 39 */ "Jump" OpHelp(""), - /* 40 */ "Once" OpHelp(""), - /* 41 */ "If" OpHelp(""), - /* 42 */ "IfNot" OpHelp(""), - /* 43 */ "Column" OpHelp("r[P3]=PX"), - /* 44 */ "Affinity" OpHelp("affinity(r[P1@P2])"), - /* 45 */ "MakeRecord" OpHelp("r[P3]=mkrec(r[P1@P2])"), - /* 46 */ "Count" OpHelp("r[P2]=count()"), - /* 47 */ "ReadCookie" OpHelp(""), - /* 48 */ "SetCookie" OpHelp(""), - /* 49 */ "VerifyCookie" OpHelp(""), - /* 50 */ "OpenRead" OpHelp("root=P2 iDb=P3"), - /* 51 */ "OpenWrite" OpHelp("root=P2 iDb=P3"), - /* 52 */ "OpenAutoindex" OpHelp("nColumn=P2"), - /* 53 */ "OpenEphemeral" OpHelp("nColumn=P2"), - /* 54 */ "SorterOpen" OpHelp(""), - /* 55 */ "OpenPseudo" OpHelp("content in r[P2@P3]"), - /* 56 */ "Close" OpHelp(""), - /* 57 */ "SeekLt" OpHelp("key=r[P3@P4]"), - /* 58 */ "SeekLe" OpHelp("key=r[P3@P4]"), - /* 59 */ "SeekGe" OpHelp("key=r[P3@P4]"), - /* 60 */ "SeekGt" OpHelp("key=r[P3@P4]"), - /* 61 */ "Seek" OpHelp("intkey=r[P2]"), - /* 62 */ "NoConflict" OpHelp("key=r[P3@P4]"), - /* 63 */ "NotFound" OpHelp("key=r[P3@P4]"), - /* 64 */ "Found" OpHelp("key=r[P3@P4]"), - /* 65 */ "NotExists" OpHelp("intkey=r[P3]"), - /* 66 */ "Sequence" OpHelp("r[P2]=rowid"), - /* 67 */ "NewRowid" OpHelp("r[P2]=rowid"), - /* 68 */ "Insert" OpHelp("intkey=r[P3] data=r[P2]"), - /* 69 */ "InsertInt" OpHelp("intkey=P3 data=r[P2]"), - /* 70 */ "Delete" OpHelp(""), + /* 20 */ "InitCoroutine" OpHelp(""), + /* 21 */ "EndCoroutine" OpHelp(""), + /* 22 */ "Yield" OpHelp(""), + /* 23 */ "HaltIfNull" OpHelp("if r[P3]=null halt"), + /* 24 */ "Halt" OpHelp(""), + /* 25 */ "Integer" OpHelp("r[P2]=P1"), + /* 26 */ "Int64" OpHelp("r[P2]=P4"), + /* 27 */ "String" OpHelp("r[P2]='P4' (len=P1)"), + /* 28 */ "Null" OpHelp("r[P2..P3]=NULL"), + /* 29 */ "SoftNull" OpHelp("r[P1]=NULL"), + /* 30 */ "Blob" OpHelp("r[P2]=P4 (len=P1)"), + /* 31 */ "Variable" OpHelp("r[P2]=parameter(P1,P4)"), + /* 32 */ "Move" OpHelp("r[P2@P3]=r[P1@P3]"), + /* 33 */ "Copy" OpHelp("r[P2@P3+1]=r[P1@P3+1]"), + /* 34 */ "SCopy" OpHelp("r[P2]=r[P1]"), + /* 35 */ "ResultRow" OpHelp("output=r[P1@P2]"), + /* 36 */ "CollSeq" OpHelp(""), + /* 37 */ "AddImm" OpHelp("r[P1]=r[P1]+P2"), + /* 38 */ "MustBeInt" OpHelp(""), + /* 39 */ "RealAffinity" OpHelp(""), + /* 40 */ "Permutation" OpHelp(""), + /* 41 */ "Compare" OpHelp(""), + /* 42 */ "Jump" OpHelp(""), + /* 43 */ "Once" OpHelp(""), + /* 44 */ "If" OpHelp(""), + /* 45 */ "IfNot" OpHelp(""), + /* 46 */ "Column" OpHelp("r[P3]=PX"), + /* 47 */ "Affinity" OpHelp("affinity(r[P1@P2])"), + /* 48 */ "MakeRecord" OpHelp("r[P3]=mkrec(r[P1@P2])"), + /* 49 */ "Count" OpHelp("r[P2]=count()"), + /* 50 */ "ReadCookie" OpHelp(""), + /* 51 */ "SetCookie" OpHelp(""), + /* 52 */ "OpenRead" OpHelp("root=P2 iDb=P3"), + /* 53 */ "OpenWrite" OpHelp("root=P2 iDb=P3"), + /* 54 */ "OpenAutoindex" OpHelp("nColumn=P2"), + /* 55 */ "OpenEphemeral" OpHelp("nColumn=P2"), + /* 56 */ "SorterOpen" OpHelp(""), + /* 57 */ "OpenPseudo" OpHelp("P3 columns in r[P2]"), + /* 58 */ "Close" OpHelp(""), + /* 59 */ "SeekLT" OpHelp(""), + /* 60 */ "SeekLE" OpHelp(""), + /* 61 */ "SeekGE" OpHelp(""), + /* 62 */ "SeekGT" OpHelp(""), + /* 63 */ "Seek" OpHelp("intkey=r[P2]"), + /* 64 */ "NoConflict" OpHelp("key=r[P3@P4]"), + /* 65 */ "NotFound" OpHelp("key=r[P3@P4]"), + /* 66 */ "Found" OpHelp("key=r[P3@P4]"), + /* 67 */ "NotExists" OpHelp("intkey=r[P3]"), + /* 68 */ "Sequence" OpHelp("r[P2]=rowid"), + /* 69 */ "NewRowid" OpHelp("r[P2]=rowid"), + /* 70 */ "Insert" OpHelp("intkey=r[P3] data=r[P2]"), /* 71 */ "Or" OpHelp("r[P3]=(r[P1] || r[P2])"), /* 72 */ "And" OpHelp("r[P3]=(r[P1] && r[P2])"), - /* 73 */ "ResetCount" OpHelp(""), - /* 74 */ "SorterCompare" OpHelp("if key(P1)!=rtrim(r[P3],P4) goto P2"), - /* 75 */ "SorterData" OpHelp("r[P2]=data"), + /* 73 */ "InsertInt" OpHelp("intkey=P3 data=r[P2]"), + /* 74 */ "Delete" OpHelp(""), + /* 75 */ "ResetCount" OpHelp(""), /* 76 */ "IsNull" OpHelp("if r[P1]==NULL goto P2"), /* 77 */ "NotNull" OpHelp("if r[P1]!=NULL goto P2"), /* 78 */ "Ne" OpHelp("if r[P1]!=r[P3] goto P2"), @@ -23272,7 +23400,7 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ /* 81 */ "Le" OpHelp("if r[P1]<=r[P3] goto P2"), /* 82 */ "Lt" OpHelp("if r[P1]<r[P3] goto P2"), /* 83 */ "Ge" OpHelp("if r[P1]>=r[P3] goto P2"), - /* 84 */ "RowKey" OpHelp("r[P2]=key"), + /* 84 */ "SorterCompare" OpHelp("if key(P1)!=rtrim(r[P3],P4) goto P2"), /* 85 */ "BitAnd" OpHelp("r[P3]=r[P1]&r[P2]"), /* 86 */ "BitOr" OpHelp("r[P3]=r[P1]|r[P2]"), /* 87 */ "ShiftLeft" OpHelp("r[P3]=r[P2]<<r[P1]"), @@ -23283,64 +23411,68 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ /* 92 */ "Divide" OpHelp("r[P3]=r[P2]/r[P1]"), /* 93 */ "Remainder" OpHelp("r[P3]=r[P2]%r[P1]"), /* 94 */ "Concat" OpHelp("r[P3]=r[P2]+r[P1]"), - /* 95 */ "RowData" OpHelp("r[P2]=data"), + /* 95 */ "SorterData" OpHelp("r[P2]=data"), /* 96 */ "BitNot" OpHelp("r[P1]= ~r[P1]"), /* 97 */ "String8" OpHelp("r[P2]='P4'"), - /* 98 */ "Rowid" OpHelp("r[P2]=rowid"), - /* 99 */ "NullRow" OpHelp(""), - /* 100 */ "Last" OpHelp(""), - /* 101 */ "SorterSort" OpHelp(""), - /* 102 */ "Sort" OpHelp(""), - /* 103 */ "Rewind" OpHelp(""), - /* 104 */ "SorterInsert" OpHelp(""), - /* 105 */ "IdxInsert" OpHelp("key=r[P2]"), - /* 106 */ "IdxDelete" OpHelp("key=r[P2@P3]"), - /* 107 */ "IdxRowid" OpHelp("r[P2]=rowid"), - /* 108 */ "IdxLT" OpHelp("key=r[P3@P4]"), - /* 109 */ "IdxGE" OpHelp("key=r[P3@P4]"), - /* 110 */ "Destroy" OpHelp(""), - /* 111 */ "Clear" OpHelp(""), - /* 112 */ "CreateIndex" OpHelp("r[P2]=root iDb=P1"), - /* 113 */ "CreateTable" OpHelp("r[P2]=root iDb=P1"), - /* 114 */ "ParseSchema" OpHelp(""), - /* 115 */ "LoadAnalysis" OpHelp(""), - /* 116 */ "DropTable" OpHelp(""), - /* 117 */ "DropIndex" OpHelp(""), - /* 118 */ "DropTrigger" OpHelp(""), - /* 119 */ "IntegrityCk" OpHelp(""), - /* 120 */ "RowSetAdd" OpHelp("rowset(P1)=r[P2]"), - /* 121 */ "RowSetRead" OpHelp("r[P3]=rowset(P1)"), - /* 122 */ "RowSetTest" OpHelp("if r[P3] in rowset(P1) goto P2"), - /* 123 */ "Program" OpHelp(""), - /* 124 */ "Param" OpHelp(""), - /* 125 */ "FkCounter" OpHelp("fkctr[P1]+=P2"), - /* 126 */ "FkIfZero" OpHelp("if fkctr[P1]==0 goto P2"), - /* 127 */ "MemMax" OpHelp("r[P1]=max(r[P1],r[P2])"), - /* 128 */ "IfPos" OpHelp("if r[P1]>0 goto P2"), - /* 129 */ "IfNeg" OpHelp("if r[P1]<0 goto P2"), - /* 130 */ "IfZero" OpHelp("r[P1]+=P3, if r[P1]==0 goto P2"), - /* 131 */ "AggFinal" OpHelp("accum=r[P1] N=P2"), - /* 132 */ "IncrVacuum" OpHelp(""), + /* 98 */ "RowKey" OpHelp("r[P2]=key"), + /* 99 */ "RowData" OpHelp("r[P2]=data"), + /* 100 */ "Rowid" OpHelp("r[P2]=rowid"), + /* 101 */ "NullRow" OpHelp(""), + /* 102 */ "Last" OpHelp(""), + /* 103 */ "SorterSort" OpHelp(""), + /* 104 */ "Sort" OpHelp(""), + /* 105 */ "Rewind" OpHelp(""), + /* 106 */ "SorterInsert" OpHelp(""), + /* 107 */ "IdxInsert" OpHelp("key=r[P2]"), + /* 108 */ "IdxDelete" OpHelp("key=r[P2@P3]"), + /* 109 */ "IdxRowid" OpHelp("r[P2]=rowid"), + /* 110 */ "IdxLE" OpHelp("key=r[P3@P4]"), + /* 111 */ "IdxGT" OpHelp("key=r[P3@P4]"), + /* 112 */ "IdxLT" OpHelp("key=r[P3@P4]"), + /* 113 */ "IdxGE" OpHelp("key=r[P3@P4]"), + /* 114 */ "Destroy" OpHelp(""), + /* 115 */ "Clear" OpHelp(""), + /* 116 */ "CreateIndex" OpHelp("r[P2]=root iDb=P1"), + /* 117 */ "CreateTable" OpHelp("r[P2]=root iDb=P1"), + /* 118 */ "ParseSchema" OpHelp(""), + /* 119 */ "LoadAnalysis" OpHelp(""), + /* 120 */ "DropTable" OpHelp(""), + /* 121 */ "DropIndex" OpHelp(""), + /* 122 */ "DropTrigger" OpHelp(""), + /* 123 */ "IntegrityCk" OpHelp(""), + /* 124 */ "RowSetAdd" OpHelp("rowset(P1)=r[P2]"), + /* 125 */ "RowSetRead" OpHelp("r[P3]=rowset(P1)"), + /* 126 */ "RowSetTest" OpHelp("if r[P3] in rowset(P1) goto P2"), + /* 127 */ "Program" OpHelp(""), + /* 128 */ "Param" OpHelp(""), + /* 129 */ "FkCounter" OpHelp("fkctr[P1]+=P2"), + /* 130 */ "FkIfZero" OpHelp("if fkctr[P1]==0 goto P2"), + /* 131 */ "MemMax" OpHelp("r[P1]=max(r[P1],r[P2])"), + /* 132 */ "IfPos" OpHelp("if r[P1]>0 goto P2"), /* 133 */ "Real" OpHelp("r[P2]=P4"), - /* 134 */ "Expire" OpHelp(""), - /* 135 */ "TableLock" OpHelp("iDb=P1 root=P2 write=P3"), - /* 136 */ "VBegin" OpHelp(""), - /* 137 */ "VCreate" OpHelp(""), - /* 138 */ "VDestroy" OpHelp(""), - /* 139 */ "VOpen" OpHelp(""), - /* 140 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"), - /* 141 */ "VNext" OpHelp(""), - /* 142 */ "VRename" OpHelp(""), + /* 134 */ "IfNeg" OpHelp("if r[P1]<0 goto P2"), + /* 135 */ "IfZero" OpHelp("r[P1]+=P3, if r[P1]==0 goto P2"), + /* 136 */ "AggFinal" OpHelp("accum=r[P1] N=P2"), + /* 137 */ "IncrVacuum" OpHelp(""), + /* 138 */ "Expire" OpHelp(""), + /* 139 */ "TableLock" OpHelp("iDb=P1 root=P2 write=P3"), + /* 140 */ "VBegin" OpHelp(""), + /* 141 */ "VCreate" OpHelp(""), + /* 142 */ "VDestroy" OpHelp(""), /* 143 */ "ToText" OpHelp(""), /* 144 */ "ToBlob" OpHelp(""), /* 145 */ "ToNumeric" OpHelp(""), /* 146 */ "ToInt" OpHelp(""), /* 147 */ "ToReal" OpHelp(""), - /* 148 */ "Pagecount" OpHelp(""), - /* 149 */ "MaxPgcnt" OpHelp(""), - /* 150 */ "Trace" OpHelp(""), - /* 151 */ "Noop" OpHelp(""), - /* 152 */ "Explain" OpHelp(""), + /* 148 */ "VOpen" OpHelp(""), + /* 149 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"), + /* 150 */ "VNext" OpHelp(""), + /* 151 */ "VRename" OpHelp(""), + /* 152 */ "Pagecount" OpHelp(""), + /* 153 */ "MaxPgcnt" OpHelp(""), + /* 154 */ "Init" OpHelp("Start at P2"), + /* 155 */ "Noop" OpHelp(""), + /* 156 */ "Explain" OpHelp(""), }; return azName[i]; } @@ -23433,32 +23565,6 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ #endif /* -** These #defines should enable >2GB file support on Posix if the -** underlying operating system supports it. If the OS lacks -** large file support, these should be no-ops. -** -** Large file support can be disabled using the -DSQLITE_DISABLE_LFS switch -** on the compiler command line. This is necessary if you are compiling -** on a recent machine (ex: RedHat 7.2) but you want your code to work -** on an older machine (ex: RedHat 6.0). If you compile on RedHat 7.2 -** without this option, LFS is enable. But LFS does not exist in the kernel -** in RedHat 6.0, so the code won't work. Hence, for maximum binary -** portability you should omit LFS. -** -** The previous paragraph was written in 2005. (This paragraph is written -** on 2008-11-28.) These days, all Linux kernels support large files, so -** you should probably leave LFS enabled. But some embedded platforms might -** lack LFS in which case the SQLITE_DISABLE_LFS macro might still be useful. -*/ -#ifndef SQLITE_DISABLE_LFS -# define _LARGE_FILE 1 -# ifndef _FILE_OFFSET_BITS -# define _FILE_OFFSET_BITS 64 -# endif -# define _LARGEFILE_SOURCE 1 -#endif - -/* ** standard include files. */ #include <sys/types.h> @@ -34446,7 +34552,7 @@ static int winDeviceCharacteristics(sqlite3_file *id){ ** During sqlite3_os_init() we do a GetSystemInfo() ** to get the granularity size. */ -SYSTEM_INFO winSysInfo; +static SYSTEM_INFO winSysInfo; #ifndef SQLITE_OMIT_WAL @@ -36380,15 +36486,29 @@ static int winFullPathname( ** Interfaces for opening a shared library, finding entry points ** within the shared library, and closing the shared library. */ -/* -** Interfaces for opening a shared library, finding entry points -** within the shared library, and closing the shared library. -*/ static void *winDlOpen(sqlite3_vfs *pVfs, const char *zFilename){ HANDLE h; +#if defined(__CYGWIN__) + int nFull = pVfs->mxPathname+1; + char *zFull = sqlite3MallocZero( nFull ); + void *zConverted = 0; + if( zFull==0 ){ + OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)0)); + return 0; + } + if( winFullPathname(pVfs, zFilename, nFull, zFull)!=SQLITE_OK ){ + sqlite3_free(zFull); + OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)0)); + return 0; + } + zConverted = winConvertFromUtf8Filename(zFull); + sqlite3_free(zFull); +#else void *zConverted = winConvertFromUtf8Filename(zFilename); UNUSED_PARAMETER(pVfs); +#endif if( zConverted==0 ){ + OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)0)); return 0; } if( osIsNT() ){ @@ -36403,6 +36523,7 @@ static void *winDlOpen(sqlite3_vfs *pVfs, const char *zFilename){ h = osLoadLibraryA((char*)zConverted); } #endif + OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)h)); sqlite3_free(zConverted); return (void*)h; } @@ -36411,12 +36532,17 @@ static void winDlError(sqlite3_vfs *pVfs, int nBuf, char *zBufOut){ winGetLastErrorMsg(osGetLastError(), nBuf, zBufOut); } static void (*winDlSym(sqlite3_vfs *pVfs,void *pH,const char *zSym))(void){ + FARPROC proc; UNUSED_PARAMETER(pVfs); - return (void(*)(void))osGetProcAddressA((HANDLE)pH, zSym); + proc = osGetProcAddressA((HANDLE)pH, zSym); + OSTRACE(("DLSYM handle=%p, symbol=%s, address=%p\n", + (void*)pH, zSym, (void*)proc)); + return (void(*)(void))proc; } static void winDlClose(sqlite3_vfs *pVfs, void *pHandle){ UNUSED_PARAMETER(pVfs); osFreeLibrary((HANDLE)pHandle); + OSTRACE(("DLCLOSE handle=%p\n", (void*)pHandle)); } #else /* if SQLITE_OMIT_LOAD_EXTENSION is defined: */ #define winDlOpen 0 @@ -37112,7 +37238,8 @@ struct PCache { int szCache; /* Configured cache size */ int szPage; /* Size of every page in this cache */ int szExtra; /* Size of extra space for each page */ - int bPurgeable; /* True if pages are on backing store */ + u8 bPurgeable; /* True if pages are on backing store */ + u8 eCreate; /* eCreate value for for xFetch() */ int (*xStress)(void*,PgHdr*); /* Call to try make a page clean */ void *pStress; /* Argument to xStress */ sqlite3_pcache *pCache; /* Pluggable cache module */ @@ -37179,6 +37306,10 @@ static void pcacheRemoveFromDirtyList(PgHdr *pPage){ }else{ assert( pPage==p->pDirty ); p->pDirty = pPage->pDirtyNext; + if( p->pDirty==0 && p->bPurgeable ){ + assert( p->eCreate==1 ); + p->eCreate = 2; + } } pPage->pDirtyNext = 0; pPage->pDirtyPrev = 0; @@ -37199,6 +37330,9 @@ static void pcacheAddToDirtyList(PgHdr *pPage){ if( pPage->pDirtyNext ){ assert( pPage->pDirtyNext->pDirtyPrev==0 ); pPage->pDirtyNext->pDirtyPrev = pPage; + }else if( p->bPurgeable ){ + assert( p->eCreate==2 ); + p->eCreate = 1; } p->pDirty = pPage; if( !p->pDirtyTail ){ @@ -37268,6 +37402,7 @@ SQLITE_PRIVATE void sqlite3PcacheOpen( p->szPage = szPage; p->szExtra = szExtra; p->bPurgeable = bPurgeable; + p->eCreate = 2; p->xStress = xStress; p->pStress = pStress; p->szCache = 100; @@ -37307,7 +37442,7 @@ SQLITE_PRIVATE int sqlite3PcacheFetch( int createFlag, /* If true, create page if it does not exist already */ PgHdr **ppPage /* Write the page here */ ){ - sqlite3_pcache_page *pPage = 0; + sqlite3_pcache_page *pPage; PgHdr *pPgHdr = 0; int eCreate; @@ -37318,8 +37453,12 @@ SQLITE_PRIVATE int sqlite3PcacheFetch( /* If the pluggable cache (sqlite3_pcache*) has not been allocated, ** allocate it now. */ - if( !pCache->pCache && createFlag ){ + if( !pCache->pCache ){ sqlite3_pcache *p; + if( !createFlag ){ + *ppPage = 0; + return SQLITE_OK; + } p = sqlite3GlobalConfig.pcache2.xCreate( pCache->szPage, pCache->szExtra + sizeof(PgHdr), pCache->bPurgeable ); @@ -37330,11 +37469,16 @@ SQLITE_PRIVATE int sqlite3PcacheFetch( pCache->pCache = p; } - eCreate = createFlag * (1 + (!pCache->bPurgeable || !pCache->pDirty)); - if( pCache->pCache ){ - pPage = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, eCreate); - } - + /* eCreate defines what to do if the page does not exist. + ** 0 Do not allocate a new page. (createFlag==0) + ** 1 Allocate a new page if doing so is inexpensive. + ** (createFlag==1 AND bPurgeable AND pDirty) + ** 2 Allocate a new page even it doing so is difficult. + ** (createFlag==1 AND !(bPurgeable AND pDirty) + */ + eCreate = createFlag==0 ? 0 : pCache->eCreate; + assert( (createFlag*(1+(!pCache->bPurgeable||!pCache->pDirty)))==eCreate ); + pPage = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, eCreate); if( !pPage && eCreate==1 ){ PgHdr *pPg; @@ -44282,15 +44426,17 @@ static int hasHotJournal(Pager *pPager, int *pExists){ if( rc==SQLITE_OK && !locked ){ Pgno nPage; /* Number of pages in database file */ - /* Check the size of the database file. If it consists of 0 pages, - ** then delete the journal file. See the header comment above for - ** the reasoning here. Delete the obsolete journal file under - ** a RESERVED lock to avoid race conditions and to avoid violating - ** [H33020]. - */ rc = pagerPagecount(pPager, &nPage); if( rc==SQLITE_OK ){ - if( nPage==0 ){ + /* If the database is zero pages in size, that means that either (1) the + ** journal is a remnant from a prior database with the same name where + ** the database file but not the journal was deleted, or (2) the initial + ** transaction that populates a new database is being rolled back. + ** In either case, the journal file can be deleted. However, take care + ** not to delete the journal file if it is already open due to + ** journal_mode=PERSIST. + */ + if( nPage==0 && !jrnlOpen ){ sqlite3BeginBenignMalloc(); if( pagerLockDb(pPager, RESERVED_LOCK)==SQLITE_OK ){ sqlite3OsDelete(pVfs, pPager->zJournal, 0); @@ -47922,7 +48068,7 @@ SQLITE_PRIVATE int sqlite3WalOpen( sqlite3OsClose(pRet->pWalFd); sqlite3_free(pRet); }else{ - int iDC = sqlite3OsDeviceCharacteristics(pRet->pWalFd); + int iDC = sqlite3OsDeviceCharacteristics(pDbFd); if( iDC & SQLITE_IOCAP_SEQUENTIAL ){ pRet->syncHeader = 0; } if( iDC & SQLITE_IOCAP_POWERSAFE_OVERWRITE ){ pRet->padToSectorBoundary = 0; @@ -49293,7 +49439,7 @@ static int walWriteToLog( iAmt -= iFirstAmt; pContent = (void*)(iFirstAmt + (char*)pContent); assert( p->syncFlags & (SQLITE_SYNC_NORMAL|SQLITE_SYNC_FULL) ); - rc = sqlite3OsSync(p->pFd, p->syncFlags); + rc = sqlite3OsSync(p->pFd, p->syncFlags & SQLITE_SYNC_MASK); if( iAmt==0 || rc ) return rc; } rc = sqlite3OsWrite(p->pFd, pContent, iAmt, iOffset); @@ -50231,7 +50377,6 @@ struct BtCursor { Pgno *aOverflow; /* Cache of overflow page locations */ #endif 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 */ @@ -52215,13 +52360,12 @@ static void zeroPage(MemPage *pPage, int flags){ memset(&data[hdr], 0, pBt->usableSize - hdr); } data[hdr] = (char)flags; - first = hdr + 8 + 4*((flags&PTF_LEAF)==0 ?1:0); + first = hdr + ((flags&PTF_LEAF)==0 ? 12 : 8); memset(&data[hdr+1], 0, 4); data[hdr+7] = 0; put2byte(&data[hdr+5], pBt->usableSize); pPage->nFree = (u16)(pBt->usableSize - first); decodeFlags(pPage, flags); - pPage->hdrOffset = hdr; pPage->cellOffset = first; pPage->aDataEnd = &data[pBt->usableSize]; pPage->aCellIdx = &data[first]; @@ -54305,7 +54449,6 @@ static int btreeCursor( } pBt->pCursor = pCur; pCur->eState = CURSOR_INVALID; - pCur->cachedRowid = 0; return SQLITE_OK; } SQLITE_PRIVATE int sqlite3BtreeCursor( @@ -54347,36 +54490,6 @@ SQLITE_PRIVATE void sqlite3BtreeCursorZero(BtCursor *p){ } /* -** Set the cached rowid value of every cursor in the same database file -** as pCur and having the same root page number as pCur. The value is -** set to iRowid. -** -** Only positive rowid values are considered valid for this cache. -** The cache is initialized to zero, indicating an invalid cache. -** A btree will work fine with zero or negative rowids. We just cannot -** cache zero or negative rowids, which means tables that use zero or -** negative rowids might run a little slower. But in practice, zero -** or negative rowids are very uncommon so this should not be a problem. -*/ -SQLITE_PRIVATE void sqlite3BtreeSetCachedRowid(BtCursor *pCur, sqlite3_int64 iRowid){ - BtCursor *p; - for(p=pCur->pBt->pCursor; p; p=p->pNext){ - if( p->pgnoRoot==pCur->pgnoRoot ) p->cachedRowid = iRowid; - } - assert( pCur->cachedRowid==iRowid ); -} - -/* -** Return the cached rowid for the given cursor. A negative or zero -** return value indicates that the rowid cache is invalid and should be -** ignored. If the rowid cache has never before been set, then a -** zero is returned. -*/ -SQLITE_PRIVATE sqlite3_int64 sqlite3BtreeGetCachedRowid(BtCursor *pCur){ - return pCur->cachedRowid; -} - -/* ** Close a cursor. The read lock on the database file is released ** when the last cursor is closed. */ @@ -55252,6 +55365,7 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked( int *pRes /* Write search results here */ ){ int rc; + RecordCompare xRecordCompare; assert( cursorHoldsMutex(pCur) ); assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); @@ -55273,6 +55387,16 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked( } } + if( pIdxKey ){ + xRecordCompare = sqlite3VdbeFindCompare(pIdxKey); + assert( pIdxKey->default_rc==1 + || pIdxKey->default_rc==0 + || pIdxKey->default_rc==-1 + ); + }else{ + xRecordCompare = 0; /* All keys are integers */ + } + rc = moveToRoot(pCur); if( rc ){ return rc; @@ -55305,7 +55429,7 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked( assert( biasRight==0 || biasRight==1 ); idx = upr>>(1-biasRight); /* idx = biasRight ? upr : (lwr+upr)/2; */ pCur->aiIdx[pCur->iPage] = (u16)idx; - if( pPage->intKey ){ + if( xRecordCompare==0 ){ for(;;){ i64 nCellKey; pCell = findCell(pPage, idx) + pPage->childPtrSize; @@ -55357,14 +55481,14 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked( ** single byte varint and the record fits entirely on the main ** b-tree page. */ testcase( pCell+nCell+1==pPage->aDataEnd ); - c = sqlite3VdbeRecordCompare(nCell, (void*)&pCell[1], pIdxKey); + c = xRecordCompare(nCell, (void*)&pCell[1], pIdxKey, 0); }else if( !(pCell[1] & 0x80) && (nCell = ((nCell&0x7f)<<7) + pCell[1])<=pPage->maxLocal ){ /* The record-size field is a 2 byte varint and the record ** fits entirely on the main b-tree page. */ testcase( pCell+nCell+2==pPage->aDataEnd ); - c = sqlite3VdbeRecordCompare(nCell, (void*)&pCell[2], pIdxKey); + c = xRecordCompare(nCell, (void*)&pCell[2], pIdxKey, 0); }else{ /* The record flows over onto one or more overflow pages. In ** this case the whole cell needs to be parsed, a buffer allocated @@ -55385,7 +55509,7 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked( sqlite3_free(pCellKey); goto moveto_finish; } - c = sqlite3VdbeRecordCompare(nCell, pCellKey, pIdxKey); + c = xRecordCompare(nCell, pCellKey, pIdxKey, 0); sqlite3_free(pCellKey); } if( c<0 ){ @@ -55450,6 +55574,15 @@ SQLITE_PRIVATE int sqlite3BtreeEof(BtCursor *pCur){ ** successful then set *pRes=0. If the cursor ** was already pointing to the last entry in the database before ** this routine was called, then set *pRes=1. +** +** The calling function will set *pRes to 0 or 1. The initial *pRes value +** will be 1 if the cursor being stepped corresponds to an SQL index and +** if this routine could have been skipped if that SQL index had been +** a unique index. Otherwise the caller will have set *pRes to zero. +** Zero is the common case. The btree implementation is free to use the +** initial *pRes value as a hint to improve performance, but the current +** SQLite btree implementation does not. (Note that the comdb2 btree +** implementation does use this hint, however.) */ SQLITE_PRIVATE int sqlite3BtreeNext(BtCursor *pCur, int *pRes){ int rc; @@ -55458,6 +55591,7 @@ SQLITE_PRIVATE int sqlite3BtreeNext(BtCursor *pCur, int *pRes){ assert( cursorHoldsMutex(pCur) ); assert( pRes!=0 ); + assert( *pRes==0 || *pRes==1 ); assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID ); if( pCur->eState!=CURSOR_VALID ){ rc = restoreCursorPosition(pCur); @@ -55536,6 +55670,15 @@ SQLITE_PRIVATE int sqlite3BtreeNext(BtCursor *pCur, int *pRes){ ** successful then set *pRes=0. If the cursor ** was already pointing to the first entry in the database before ** this routine was called, then set *pRes=1. +** +** The calling function will set *pRes to 0 or 1. The initial *pRes value +** will be 1 if the cursor being stepped corresponds to an SQL index and +** if this routine could have been skipped if that SQL index had been +** a unique index. Otherwise the caller will have set *pRes to zero. +** Zero is the common case. The btree implementation is free to use the +** initial *pRes value as a hint to improve performance, but the current +** SQLite btree implementation does not. (Note that the comdb2 btree +** implementation does use this hint, however.) */ SQLITE_PRIVATE int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){ int rc; @@ -55543,6 +55686,7 @@ SQLITE_PRIVATE int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){ assert( cursorHoldsMutex(pCur) ); assert( pRes!=0 ); + assert( *pRes==0 || *pRes==1 ); assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID ); pCur->atLast = 0; if( pCur->eState!=CURSOR_VALID ){ @@ -57639,11 +57783,17 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur); if( rc ) return rc; - /* If this is an insert into a table b-tree, invalidate any incrblob - ** cursors open on the row being replaced (assuming this is a replace - ** operation - if it is not, the following is a no-op). */ if( pCur->pKeyInfo==0 ){ + /* If this is an insert into a table b-tree, invalidate any incrblob + ** cursors open on the row being replaced */ invalidateIncrblobCursors(p, nKey, 0); + + /* If the cursor is currently on the last row and we are appending a + ** new row onto the end, set the "loc" to avoid an unnecessary btreeMoveto() + ** call */ + if( pCur->validNKey && nKey>0 && pCur->info.nKey==nKey-1 ){ + loc = -1; + } } if( !loc ){ @@ -57713,8 +57863,8 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( ** row without seeking the cursor. This can be a big performance boost. */ pCur->info.nSize = 0; - pCur->validNKey = 0; if( rc==SQLITE_OK && pPage->nOverflow ){ + pCur->validNKey = 0; rc = balance(pCur); /* Must make sure nOverflow is reset to zero even if the balance() @@ -57769,7 +57919,7 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur){ ** sub-tree headed by the child page of the cell being deleted. This makes ** balancing the tree following the delete operation easier. */ if( !pPage->leaf ){ - int notUsed; + int notUsed = 0; rc = sqlite3BtreePrevious(pCur, ¬Used); if( rc ) return rc; } @@ -59923,6 +60073,42 @@ copy_finished: ** name sqlite_value */ +#ifdef SQLITE_DEBUG +/* +** Check invariants on a Mem object. +** +** This routine is intended for use inside of assert() statements, like +** this: assert( sqlite3VdbeCheckMemInvariants(pMem) ); +*/ +SQLITE_PRIVATE int sqlite3VdbeCheckMemInvariants(Mem *p){ + /* The MEM_Dyn bit is set if and only if Mem.xDel is a non-NULL destructor + ** function for Mem.z + */ + assert( (p->flags & MEM_Dyn)==0 || p->xDel!=0 ); + assert( (p->flags & MEM_Dyn)!=0 || p->xDel==0 ); + + /* If p holds a string or blob, the Mem.z must point to exactly + ** one of the following: + ** + ** (1) Memory in Mem.zMalloc and managed by the Mem object + ** (2) Memory to be freed using Mem.xDel + ** (3) An ephermal string or blob + ** (4) A static string or blob + */ + if( (p->flags & (MEM_Str|MEM_Blob)) && p->z!=0 ){ + assert( + ((p->z==p->zMalloc)? 1 : 0) + + ((p->flags&MEM_Dyn)!=0 ? 1 : 0) + + ((p->flags&MEM_Ephem)!=0 ? 1 : 0) + + ((p->flags&MEM_Static)!=0 ? 1 : 0) == 1 + ); + } + + return 1; +} +#endif + + /* ** If pMem is an object with a valid string representation, this routine ** ensures the internal encoding for the string representation is @@ -59972,12 +60158,7 @@ SQLITE_PRIVATE int sqlite3VdbeChangeEncoding(Mem *pMem, int desiredEnc){ ** in pMem->z is discarded. */ SQLITE_PRIVATE int sqlite3VdbeMemGrow(Mem *pMem, int n, int bPreserve){ - assert( 1 >= - ((pMem->zMalloc && pMem->zMalloc==pMem->z) ? 1 : 0) + - (((pMem->flags&MEM_Dyn)&&pMem->xDel) ? 1 : 0) + - ((pMem->flags&MEM_Ephem) ? 1 : 0) + - ((pMem->flags&MEM_Static) ? 1 : 0) - ); + assert( sqlite3VdbeCheckMemInvariants(pMem) ); assert( (pMem->flags&MEM_RowSet)==0 ); /* If the bPreserve flag is set to true, then the memory cell must already @@ -59995,7 +60176,8 @@ SQLITE_PRIVATE int sqlite3VdbeMemGrow(Mem *pMem, int n, int bPreserve){ pMem->zMalloc = sqlite3DbMallocRaw(pMem->db, n); } if( pMem->zMalloc==0 ){ - sqlite3VdbeMemRelease(pMem); + VdbeMemRelease(pMem); + pMem->z = 0; pMem->flags = MEM_Null; return SQLITE_NOMEM; } @@ -60004,13 +60186,13 @@ SQLITE_PRIVATE int sqlite3VdbeMemGrow(Mem *pMem, int n, int bPreserve){ if( pMem->z && bPreserve && pMem->z!=pMem->zMalloc ){ memcpy(pMem->zMalloc, pMem->z, pMem->n); } - if( (pMem->flags&MEM_Dyn)!=0 && pMem->xDel ){ - assert( pMem->xDel!=SQLITE_DYNAMIC ); + if( (pMem->flags&MEM_Dyn)!=0 ){ + assert( pMem->xDel!=0 && pMem->xDel!=SQLITE_DYNAMIC ); pMem->xDel((void *)(pMem->z)); } pMem->z = pMem->zMalloc; - pMem->flags &= ~(MEM_Ephem|MEM_Static); + pMem->flags &= ~(MEM_Dyn|MEM_Ephem|MEM_Static); pMem->xDel = 0; return SQLITE_OK; } @@ -60179,9 +60361,9 @@ SQLITE_PRIVATE void sqlite3VdbeMemReleaseExternal(Mem *p){ sqlite3VdbeMemFinalize(p, p->u.pDef); assert( (p->flags & MEM_Agg)==0 ); sqlite3VdbeMemRelease(p); - }else if( p->flags&MEM_Dyn && p->xDel ){ + }else if( p->flags&MEM_Dyn ){ assert( (p->flags&MEM_RowSet)==0 ); - assert( p->xDel!=SQLITE_DYNAMIC ); + assert( p->xDel!=SQLITE_DYNAMIC && p->xDel!=0 ); p->xDel((void *)p->z); p->xDel = 0; }else if( p->flags&MEM_RowSet ){ @@ -60194,9 +60376,10 @@ SQLITE_PRIVATE void sqlite3VdbeMemReleaseExternal(Mem *p){ /* ** Release any memory held by the Mem. This may leave the Mem in an ** inconsistent state, for example with (Mem.z==0) and -** (Mem.type==SQLITE_TEXT). +** (Mem.flags==MEM_Str). */ SQLITE_PRIVATE void sqlite3VdbeMemRelease(Mem *p){ + assert( sqlite3VdbeCheckMemInvariants(p) ); VdbeMemRelease(p); if( p->zMalloc ){ sqlite3DbFree(p->db, p->zMalloc); @@ -60385,7 +60568,6 @@ SQLITE_PRIVATE void sqlite3VdbeMemSetNull(Mem *pMem){ sqlite3RowSetClear(pMem->u.pRowSet); } MemSetTypeFlag(pMem, MEM_Null); - pMem->type = SQLITE_NULL; } SQLITE_PRIVATE void sqlite3ValueSetNull(sqlite3_value *p){ sqlite3VdbeMemSetNull((Mem*)p); @@ -60398,7 +60580,6 @@ SQLITE_PRIVATE void sqlite3ValueSetNull(sqlite3_value *p){ SQLITE_PRIVATE void sqlite3VdbeMemSetZeroBlob(Mem *pMem, int n){ sqlite3VdbeMemRelease(pMem); pMem->flags = MEM_Blob|MEM_Zero; - pMem->type = SQLITE_BLOB; pMem->n = 0; if( n<0 ) n = 0; pMem->u.nZero = n; @@ -60421,7 +60602,6 @@ SQLITE_PRIVATE void sqlite3VdbeMemSetInt64(Mem *pMem, i64 val){ sqlite3VdbeMemRelease(pMem); pMem->u.i = val; pMem->flags = MEM_Int; - pMem->type = SQLITE_INTEGER; } #ifndef SQLITE_OMIT_FLOATING_POINT @@ -60436,7 +60616,6 @@ SQLITE_PRIVATE void sqlite3VdbeMemSetDouble(Mem *pMem, double val){ sqlite3VdbeMemRelease(pMem); pMem->r = val; pMem->flags = MEM_Real; - pMem->type = SQLITE_FLOAT; } } #endif @@ -60492,7 +60671,7 @@ SQLITE_PRIVATE void sqlite3VdbeMemAboutToChange(Vdbe *pVdbe, Mem *pMem){ Mem *pX; for(i=1, pX=&pVdbe->aMem[1]; i<=pVdbe->nMem; i++, pX++){ if( pX->pScopyFrom==pMem ){ - pX->flags |= MEM_Invalid; + pX->flags |= MEM_Undefined; pX->pScopyFrom = 0; } } @@ -60534,6 +60713,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemCopy(Mem *pTo, const Mem *pFrom){ VdbeMemRelease(pTo); memcpy(pTo, pFrom, MEMCELLSIZE); pTo->flags &= ~MEM_Dyn; + pTo->xDel = 0; if( pTo->flags&(MEM_Str|MEM_Blob) ){ if( 0==(pFrom->flags&MEM_Static) ){ @@ -60644,7 +60824,6 @@ SQLITE_PRIVATE int sqlite3VdbeMemSetStr( pMem->n = nByte; pMem->flags = flags; pMem->enc = (enc==0 ? SQLITE_UTF8 : enc); - pMem->type = (enc==0 ? SQLITE_BLOB : SQLITE_TEXT); #ifndef SQLITE_OMIT_UTF16 if( pMem->enc!=SQLITE_UTF8 && sqlite3VdbeMemHandleBom(pMem) ){ @@ -60660,119 +60839,6 @@ SQLITE_PRIVATE int sqlite3VdbeMemSetStr( } /* -** Compare the values contained by the two memory cells, returning -** negative, zero or positive if pMem1 is less than, equal to, or greater -** than pMem2. Sorting order is NULL's first, followed by numbers (integers -** and reals) sorted numerically, followed by text ordered by the collating -** sequence pColl and finally blob's ordered by memcmp(). -** -** Two NULL values are considered equal by this function. -*/ -SQLITE_PRIVATE int sqlite3MemCompare(const Mem *pMem1, const Mem *pMem2, const CollSeq *pColl){ - int rc; - int f1, f2; - int combined_flags; - - f1 = pMem1->flags; - f2 = pMem2->flags; - combined_flags = f1|f2; - assert( (combined_flags & MEM_RowSet)==0 ); - - /* If one value is NULL, it is less than the other. If both values - ** are NULL, return 0. - */ - if( combined_flags&MEM_Null ){ - return (f2&MEM_Null) - (f1&MEM_Null); - } - - /* If one value is a number and the other is not, the number is less. - ** If both are numbers, compare as reals if one is a real, or as integers - ** if both values are integers. - */ - if( combined_flags&(MEM_Int|MEM_Real) ){ - double r1, r2; - if( (f1 & f2 & MEM_Int)!=0 ){ - if( pMem1->u.i < pMem2->u.i ) return -1; - if( pMem1->u.i > pMem2->u.i ) return 1; - return 0; - } - if( (f1&MEM_Real)!=0 ){ - r1 = pMem1->r; - }else if( (f1&MEM_Int)!=0 ){ - r1 = (double)pMem1->u.i; - }else{ - return 1; - } - if( (f2&MEM_Real)!=0 ){ - r2 = pMem2->r; - }else if( (f2&MEM_Int)!=0 ){ - r2 = (double)pMem2->u.i; - }else{ - return -1; - } - if( r1<r2 ) return -1; - if( r1>r2 ) return 1; - return 0; - } - - /* If one value is a string and the other is a blob, the string is less. - ** If both are strings, compare using the collating functions. - */ - if( combined_flags&MEM_Str ){ - if( (f1 & MEM_Str)==0 ){ - return 1; - } - if( (f2 & MEM_Str)==0 ){ - return -1; - } - - assert( pMem1->enc==pMem2->enc ); - assert( pMem1->enc==SQLITE_UTF8 || - pMem1->enc==SQLITE_UTF16LE || pMem1->enc==SQLITE_UTF16BE ); - - /* The collation sequence must be defined at this point, even if - ** the user deletes the collation sequence after the vdbe program is - ** compiled (this was not always the case). - */ - assert( !pColl || pColl->xCmp ); - - if( pColl ){ - if( pMem1->enc==pColl->enc ){ - /* The strings are already in the correct encoding. Call the - ** comparison function directly */ - return pColl->xCmp(pColl->pUser,pMem1->n,pMem1->z,pMem2->n,pMem2->z); - }else{ - const void *v1, *v2; - int n1, n2; - Mem c1; - Mem c2; - memset(&c1, 0, sizeof(c1)); - memset(&c2, 0, sizeof(c2)); - sqlite3VdbeMemShallowCopy(&c1, pMem1, MEM_Ephem); - sqlite3VdbeMemShallowCopy(&c2, pMem2, MEM_Ephem); - v1 = sqlite3ValueText((sqlite3_value*)&c1, pColl->enc); - n1 = v1==0 ? 0 : c1.n; - v2 = sqlite3ValueText((sqlite3_value*)&c2, pColl->enc); - n2 = v2==0 ? 0 : c2.n; - rc = pColl->xCmp(pColl->pUser, n1, v1, n2, v2); - sqlite3VdbeMemRelease(&c1); - sqlite3VdbeMemRelease(&c2); - return rc; - } - } - /* If a NULL pointer was passed as the collate function, fall through - ** to the blob case and use memcmp(). */ - } - - /* Both values must be blobs. Compare using memcmp(). */ - rc = memcmp(pMem1->z, pMem2->z, (pMem1->n>pMem2->n)?pMem2->n:pMem1->n); - if( rc==0 ){ - rc = pMem1->n - pMem2->n; - } - return rc; -} - -/* ** Move data out of a btree key or data field and into a Mem structure. ** The data or key is taken from the entry that pCur is currently pointing ** to. offset and amt determine what portion of the data or key to retrieve. @@ -60812,22 +60878,22 @@ SQLITE_PRIVATE int sqlite3VdbeMemFromBtree( sqlite3VdbeMemRelease(pMem); pMem->z = &zData[offset]; pMem->flags = MEM_Blob|MEM_Ephem; + pMem->n = (int)amt; }else if( SQLITE_OK==(rc = sqlite3VdbeMemGrow(pMem, amt+2, 0)) ){ - pMem->flags = MEM_Blob|MEM_Dyn|MEM_Term; - pMem->enc = 0; - pMem->type = SQLITE_BLOB; if( key ){ rc = sqlite3BtreeKey(pCur, offset, amt, pMem->z); }else{ rc = sqlite3BtreeData(pCur, offset, amt, pMem->z); } - pMem->z[amt] = 0; - pMem->z[amt+1] = 0; - if( rc!=SQLITE_OK ){ + if( rc==SQLITE_OK ){ + pMem->z[amt] = 0; + pMem->z[amt+1] = 0; + pMem->flags = MEM_Blob|MEM_Term; + pMem->n = (int)amt; + }else{ sqlite3VdbeMemRelease(pMem); } } - pMem->n = (int)amt; return rc; } @@ -60885,7 +60951,6 @@ SQLITE_PRIVATE sqlite3_value *sqlite3ValueNew(sqlite3 *db){ Mem *p = sqlite3DbMallocZero(db, sizeof(*p)); if( p ){ p->flags = MEM_Null; - p->type = SQLITE_NULL; p->db = db; } return p; @@ -60931,11 +60996,9 @@ static sqlite3_value *valueNew(sqlite3 *db, struct ValueNewStat4Ctx *p){ if( pRec->pKeyInfo ){ assert( pRec->pKeyInfo->nField+pRec->pKeyInfo->nXField==nCol ); assert( pRec->pKeyInfo->enc==ENC(db) ); - pRec->flags = UNPACKED_PREFIX_MATCH; pRec->aMem = (Mem *)((u8*)pRec + ROUND8(sizeof(UnpackedRecord))); for(i=0; i<nCol; i++){ pRec->aMem[i].flags = MEM_Null; - pRec->aMem[i].type = SQLITE_NULL; pRec->aMem[i].db = db; } }else{ @@ -61008,7 +61071,6 @@ static int valueFromExpr( zVal = sqlite3MPrintf(db, "%s%s", zNeg, pExpr->u.zToken); if( zVal==0 ) goto no_mem; sqlite3ValueSetStr(pVal, -1, zVal, SQLITE_UTF8, SQLITE_DYNAMIC); - if( op==TK_FLOAT ) pVal->type = SQLITE_FLOAT; } if( (op==TK_INTEGER || op==TK_FLOAT ) && affinity==SQLITE_AFF_NONE ){ sqlite3ValueApplyAffinity(pVal, SQLITE_AFF_NUMERIC, SQLITE_UTF8); @@ -61026,9 +61088,9 @@ static int valueFromExpr( ){ sqlite3VdbeMemNumerify(pVal); if( pVal->u.i==SMALLEST_INT64 ){ - pVal->flags &= MEM_Int; + pVal->flags &= ~MEM_Int; pVal->flags |= MEM_Real; - pVal->r = (double)LARGEST_INT64; + pVal->r = (double)SMALLEST_INT64; }else{ pVal->u.i = -pVal->u.i; } @@ -61054,9 +61116,6 @@ static int valueFromExpr( } #endif - if( pVal ){ - sqlite3VdbeMemStoreType(pVal); - } *ppVal = pVal; return rc; @@ -61220,7 +61279,6 @@ SQLITE_PRIVATE int sqlite3Stat4ProbeSetValue( sqlite3ValueApplyAffinity(pVal, affinity, ENC(db)); } pVal->db = pParse->db; - sqlite3VdbeMemStoreType((Mem*)pVal); } } }else{ @@ -61466,6 +61524,9 @@ SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3){ pOp->cycles = 0; pOp->cnt = 0; #endif +#ifdef SQLITE_VDBE_COVERAGE + pOp->iSrcLine = 0; +#endif return i; } SQLITE_PRIVATE int sqlite3VdbeAddOp0(Vdbe *p, int op){ @@ -61827,7 +61888,7 @@ SQLITE_PRIVATE VdbeOp *sqlite3VdbeTakeOpArray(Vdbe *p, int *pnOp, int *pnMaxArg) ** Add a whole list of operations to the operation stack. Return the ** address of the first operation added. */ -SQLITE_PRIVATE int sqlite3VdbeAddOpList(Vdbe *p, int nOp, VdbeOpList const *aOp){ +SQLITE_PRIVATE int sqlite3VdbeAddOpList(Vdbe *p, int nOp, VdbeOpList const *aOp, int iLineno){ int addr; assert( p->magic==VDBE_MAGIC_INIT ); if( p->nOp + nOp > p->pParse->nOpAlloc && growOpArray(p) ){ @@ -61855,6 +61916,11 @@ SQLITE_PRIVATE int sqlite3VdbeAddOpList(Vdbe *p, int nOp, VdbeOpList const *aOp) #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS pOut->zComment = 0; #endif +#ifdef SQLITE_VDBE_COVERAGE + pOut->iSrcLine = iLineno+i; +#else + (void)iLineno; +#endif #ifdef SQLITE_DEBUG if( p->db->flags & SQLITE_VdbeAddopTrace ){ sqlite3VdbePrintOp(0, i+addr, &p->aOp[i+addr]); @@ -62143,6 +62209,15 @@ SQLITE_PRIVATE void sqlite3VdbeNoopComment(Vdbe *p, const char *zFormat, ...){ } #endif /* NDEBUG */ +#ifdef SQLITE_VDBE_COVERAGE +/* +** Set the value if the iSrcLine field for the previously coded instruction. +*/ +SQLITE_PRIVATE void sqlite3VdbeSetLineNumber(Vdbe *v, int iLine){ + sqlite3VdbeGetOp(v,-1)->iSrcLine = iLine; +} +#endif /* SQLITE_VDBE_COVERAGE */ + /* ** Return the opcode for a given address. If the address is -1, then ** return the most recently inserted opcode. @@ -62155,14 +62230,6 @@ SQLITE_PRIVATE void sqlite3VdbeNoopComment(Vdbe *p, const char *zFormat, ...){ ** this routine is a valid pointer. But because the dummy.opcode is 0, ** dummy will never be written to. This is verified by code inspection and ** by running with Valgrind. -** -** About the #ifdef SQLITE_OMIT_TRACE: Normally, this routine is never called -** unless p->nOp>0. This is because in the absense of SQLITE_OMIT_TRACE, -** an OP_Trace instruction is always inserted by sqlite3VdbeGet() as soon as -** a new VDBE is created. So we are free to set addr to p->nOp-1 without -** having to double-check to make sure that the result is non-negative. But -** if SQLITE_OMIT_TRACE is defined, the OP_Trace is omitted and we do need to -** check the value of p->nOp-1 before continuing. */ SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetOp(Vdbe *p, int addr){ /* C89 specifies that the constant "dummy" will be initialized to all @@ -62170,9 +62237,6 @@ SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetOp(Vdbe *p, int addr){ static VdbeOp dummy; /* Ignore the MSVC warning about no initializer */ assert( p->magic==VDBE_MAGIC_INIT ); if( addr<0 ){ -#ifdef SQLITE_OMIT_TRACE - if( p->nOp==0 ) return (VdbeOp*)&dummy; -#endif addr = p->nOp - 1; } assert( (addr>=0 && addr<p->nOp) || p->db->mallocFailed ); @@ -62477,7 +62541,7 @@ SQLITE_PRIVATE void sqlite3VdbePrintOp(FILE *pOut, int pc, Op *pOp){ #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS displayComment(pOp, zP4, zCom, sizeof(zCom)); #else - zCom[0] = 0 + zCom[0] = 0; #endif /* NB: The sqlite3OpcodeName() function is implemented by code created ** by the mkopcodeh.awk and mkopcodec.awk scripts which extract the @@ -62506,6 +62570,7 @@ static void releaseMemArray(Mem *p, int N){ } for(pEnd=&p[N]; p<pEnd; p++){ assert( (&p[1])==pEnd || p[0].db==p[1].db ); + assert( sqlite3VdbeCheckMemInvariants(p) ); /* This block is really an inlined version of sqlite3VdbeMemRelease() ** that takes advantage of the fact that the memory cell value is @@ -62519,6 +62584,10 @@ static void releaseMemArray(Mem *p, int N){ ** with no indexes using a single prepared INSERT statement, bind() ** and reset(). Inserts are grouped into a transaction. */ + testcase( p->flags & MEM_Agg ); + testcase( p->flags & MEM_Dyn ); + testcase( p->flags & MEM_Frame ); + testcase( p->flags & MEM_RowSet ); if( p->flags&(MEM_Agg|MEM_Dyn|MEM_Frame|MEM_RowSet) ){ sqlite3VdbeMemRelease(p); }else if( p->zMalloc ){ @@ -62526,7 +62595,7 @@ static void releaseMemArray(Mem *p, int N){ p->zMalloc = 0; } - p->flags = MEM_Invalid; + p->flags = MEM_Undefined; } db->mallocFailed = malloc_failed; } @@ -62648,7 +62717,6 @@ SQLITE_PRIVATE int sqlite3VdbeList( } if( p->explain==1 ){ pMem->flags = MEM_Int; - pMem->type = SQLITE_INTEGER; pMem->u.i = i; /* Program counter */ pMem++; @@ -62656,7 +62724,6 @@ SQLITE_PRIVATE int sqlite3VdbeList( pMem->z = (char*)sqlite3OpcodeName(pOp->opcode); /* Opcode */ assert( pMem->z!=0 ); pMem->n = sqlite3Strlen30(pMem->z); - pMem->type = SQLITE_TEXT; pMem->enc = SQLITE_UTF8; pMem++; @@ -62682,24 +62749,21 @@ SQLITE_PRIVATE int sqlite3VdbeList( pMem->flags = MEM_Int; pMem->u.i = pOp->p1; /* P1 */ - pMem->type = SQLITE_INTEGER; pMem++; pMem->flags = MEM_Int; pMem->u.i = pOp->p2; /* P2 */ - pMem->type = SQLITE_INTEGER; pMem++; pMem->flags = MEM_Int; pMem->u.i = pOp->p3; /* P3 */ - pMem->type = SQLITE_INTEGER; pMem++; if( sqlite3VdbeMemGrow(pMem, 32, 0) ){ /* P4 */ assert( p->db->mallocFailed ); return SQLITE_ERROR; } - pMem->flags = MEM_Dyn|MEM_Str|MEM_Term; + pMem->flags = MEM_Str|MEM_Term; zP4 = displayP4(pOp, pMem->z, 32); if( zP4!=pMem->z ){ sqlite3VdbeMemSetStr(pMem, zP4, -1, SQLITE_UTF8, 0); @@ -62708,7 +62772,6 @@ SQLITE_PRIVATE int sqlite3VdbeList( pMem->n = sqlite3Strlen30(pMem->z); pMem->enc = SQLITE_UTF8; } - pMem->type = SQLITE_TEXT; pMem++; if( p->explain==1 ){ @@ -62716,10 +62779,9 @@ SQLITE_PRIVATE int sqlite3VdbeList( assert( p->db->mallocFailed ); return SQLITE_ERROR; } - pMem->flags = MEM_Dyn|MEM_Str|MEM_Term; + pMem->flags = MEM_Str|MEM_Term; pMem->n = 2; sqlite3_snprintf(3, pMem->z, "%.2x", pOp->p5); /* P5 */ - pMem->type = SQLITE_TEXT; pMem->enc = SQLITE_UTF8; pMem++; @@ -62728,13 +62790,11 @@ SQLITE_PRIVATE int sqlite3VdbeList( assert( p->db->mallocFailed ); return SQLITE_ERROR; } - pMem->flags = MEM_Dyn|MEM_Str|MEM_Term; + pMem->flags = MEM_Str|MEM_Term; pMem->n = displayComment(pOp, zP4, pMem->z, 500); - pMem->type = SQLITE_TEXT; pMem->enc = SQLITE_UTF8; #else pMem->flags = MEM_Null; /* Comment */ - pMem->type = SQLITE_NULL; #endif } @@ -62757,7 +62817,7 @@ SQLITE_PRIVATE void sqlite3VdbePrintSql(Vdbe *p){ z = p->zSql; }else if( p->nOp>=1 ){ const VdbeOp *pOp = &p->aOp[0]; - if( pOp->opcode==OP_Trace && pOp->p4.z!=0 ){ + if( pOp->opcode==OP_Init && pOp->p4.z!=0 ){ z = pOp->p4.z; while( sqlite3Isspace(*z) ) z++; } @@ -62776,7 +62836,7 @@ SQLITE_PRIVATE void sqlite3VdbeIOTraceSql(Vdbe *p){ if( sqlite3IoTrace==0 ) return; if( nOp<1 ) return; pOp = &p->aOp[0]; - if( pOp->opcode==OP_Trace && pOp->p4.z!=0 ){ + if( pOp->opcode==OP_Init && pOp->p4.z!=0 ){ int i, j; char z[1000]; sqlite3_snprintf(sizeof(z), z, "%s", pOp->p4.z); @@ -62994,7 +63054,7 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady( 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_Invalid; + p->aMem[n].flags = MEM_Undefined; p->aMem[n].db = db; } } @@ -63106,7 +63166,7 @@ static void Cleanup(Vdbe *p){ int i; if( p->apCsr ) for(i=0; i<p->nCursor; i++) assert( p->apCsr[i]==0 ); if( p->aMem ){ - for(i=1; i<=p->nMem; i++) assert( p->aMem[i].flags==MEM_Invalid ); + for(i=1; i<=p->nMem; i++) assert( p->aMem[i].flags==MEM_Undefined ); } #endif @@ -63855,12 +63915,24 @@ SQLITE_PRIVATE int sqlite3VdbeReset(Vdbe *p){ fprintf(out, "%02x", p->aOp[i].opcode); } fprintf(out, "\n"); + if( p->zSql ){ + char c, pc = 0; + fprintf(out, "-- "); + for(i=0; (c = p->zSql[i])!=0; i++){ + if( pc=='\n' ) fprintf(out, "-- "); + putc(c, out); + pc = c; + } + if( pc!='\n' ) fprintf(out, "\n"); + } for(i=0; i<p->nOp; i++){ - fprintf(out, "%6d %10lld %8lld ", + char zHdr[100]; + sqlite3_snprintf(sizeof(zHdr), zHdr, "%6u %12llu %8llu ", p->aOp[i].cnt, p->aOp[i].cycles, p->aOp[i].cnt>0 ? p->aOp[i].cycles/p->aOp[i].cnt : 0 ); + fprintf(out, "%s", zHdr); sqlite3VdbePrintOp(out, i, &p->aOp[i]); } fclose(out); @@ -64215,6 +64287,14 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialPut(u8 *buf, Mem *pMem, u32 serial_type){ return 0; } +/* Input "x" is a sequence of unsigned characters that represent a +** big-endian integer. Return the equivalent native integer +*/ +#define ONE_BYTE_INT(x) ((i8)(x)[0]) +#define TWO_BYTE_INT(x) (256*(i8)((x)[0])|(x)[1]) +#define THREE_BYTE_INT(x) (65536*(i8)((x)[0])|((x)[1]<<8)|(x)[2]) +#define FOUR_BYTE_UINT(x) (((u32)(x)[0]<<24)|((x)[1]<<16)|((x)[2]<<8)|(x)[3]) + /* ** Deserialize the data blob pointed to by buf as serial type serial_type ** and store the result in pMem. Return the number of bytes read. @@ -64226,7 +64306,6 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialGet( ){ u64 x; u32 y; - int i; switch( serial_type ){ case 10: /* Reserved for future use */ case 11: /* Reserved for future use */ @@ -64235,34 +64314,34 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialGet( break; } case 1: { /* 1-byte signed integer */ - pMem->u.i = (signed char)buf[0]; + pMem->u.i = ONE_BYTE_INT(buf); pMem->flags = MEM_Int; + testcase( pMem->u.i<0 ); return 1; } case 2: { /* 2-byte signed integer */ - i = 256*(signed char)buf[0] | buf[1]; - pMem->u.i = (i64)i; + pMem->u.i = TWO_BYTE_INT(buf); pMem->flags = MEM_Int; + testcase( pMem->u.i<0 ); return 2; } case 3: { /* 3-byte signed integer */ - i = 65536*(signed char)buf[0] | (buf[1]<<8) | buf[2]; - pMem->u.i = (i64)i; + pMem->u.i = THREE_BYTE_INT(buf); pMem->flags = MEM_Int; + testcase( pMem->u.i<0 ); return 3; } case 4: { /* 4-byte signed integer */ - y = ((unsigned)buf[0]<<24) | (buf[1]<<16) | (buf[2]<<8) | buf[3]; + y = FOUR_BYTE_UINT(buf); pMem->u.i = (i64)*(int*)&y; pMem->flags = MEM_Int; + testcase( pMem->u.i<0 ); return 4; } case 5: { /* 6-byte signed integer */ - x = 256*(signed char)buf[0] + buf[1]; - y = ((unsigned)buf[2]<<24) | (buf[3]<<16) | (buf[4]<<8) | buf[5]; - x = (x<<32) | y; - pMem->u.i = *(i64*)&x; + pMem->u.i = FOUR_BYTE_UINT(buf+2) + (((i64)1)<<32)*TWO_BYTE_INT(buf); pMem->flags = MEM_Int; + testcase( pMem->u.i<0 ); return 6; } case 6: /* 8-byte signed integer */ @@ -64279,12 +64358,13 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialGet( swapMixedEndianFloat(t2); assert( sizeof(r1)==sizeof(t2) && memcmp(&r1, &t2, sizeof(r1))==0 ); #endif - x = ((unsigned)buf[0]<<24) | (buf[1]<<16) | (buf[2]<<8) | buf[3]; - y = ((unsigned)buf[4]<<24) | (buf[5]<<16) | (buf[6]<<8) | buf[7]; + x = FOUR_BYTE_UINT(buf); + y = FOUR_BYTE_UINT(buf+4); x = (x<<32) | y; if( serial_type==6 ){ pMem->u.i = *(i64*)&x; pMem->flags = MEM_Int; + testcase( pMem->u.i<0 ); }else{ assert( sizeof(x)==8 && sizeof(pMem->r)==8 ); swapMixedEndianFloat(x); @@ -64376,7 +64456,7 @@ SQLITE_PRIVATE void sqlite3VdbeRecordUnpack( u32 szHdr; Mem *pMem = p->aMem; - p->flags = 0; + p->default_rc = 0; assert( EIGHT_BYTE_ALIGNMENT(pMem) ); idx = getVarint32(aKey, szHdr); d = szHdr; @@ -64397,26 +64477,18 @@ SQLITE_PRIVATE void sqlite3VdbeRecordUnpack( p->nField = u; } +#if SQLITE_DEBUG /* -** This function compares the two table rows or index records -** specified by {nKey1, pKey1} and pPKey2. It returns a negative, zero -** or positive integer if key1 is less than, equal to or -** greater than key2. The {nKey1, pKey1} key must be a blob -** created by th OP_MakeRecord opcode of the VDBE. The pPKey2 -** key must be a parsed key such as obtained from -** sqlite3VdbeParseRecord. -** -** Key1 and Key2 do not have to contain the same number of fields. -** The key with fewer fields is usually compares less than the -** longer key. However if the UNPACKED_INCRKEY flags in pPKey2 is set -** and the common prefixes are equal, then key1 is less than key2. -** Or if the UNPACKED_MATCH_PREFIX flag is set and the prefixes are -** equal, then the keys are considered to be equal and -** the parts beyond the common prefix are ignored. +** This function compares two index or table record keys in the same way +** as the sqlite3VdbeRecordCompare() routine. Unlike VdbeRecordCompare(), +** this function deserializes and compares values using the +** sqlite3VdbeSerialGet() and sqlite3MemCompare() functions. It is used +** in assert() statements to ensure that the optimized code in +** sqlite3VdbeRecordCompare() returns results with these two primitives. */ -SQLITE_PRIVATE int sqlite3VdbeRecordCompare( +static int vdbeRecordCompareDebug( int nKey1, const void *pKey1, /* Left key */ - UnpackedRecord *pPKey2 /* Right key */ + const UnpackedRecord *pPKey2 /* Right key */ ){ u32 d1; /* Offset into aKey[] of next data element */ u32 idx1; /* Offset into aKey[] of next header element */ @@ -64490,24 +64562,577 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompare( assert( mem1.zMalloc==0 ); /* rc==0 here means that one of the keys ran out of fields and - ** all the fields up to that point were equal. If the UNPACKED_INCRKEY - ** flag is set, then break the tie by treating key2 as larger. - ** If the UPACKED_PREFIX_MATCH flag is set, then keys with common prefixes - ** are considered to be equal. Otherwise, the longer key is the - ** larger. As it happens, the pPKey2 will always be the longer - ** if there is a difference. - */ - assert( rc==0 ); - if( pPKey2->flags & UNPACKED_INCRKEY ){ - rc = -1; - }else if( pPKey2->flags & UNPACKED_PREFIX_MATCH ){ - /* Leave rc==0 */ - }else if( idx1<szHdr1 ){ - rc = 1; + ** all the fields up to that point were equal. Return the the default_rc + ** value. */ + return pPKey2->default_rc; +} +#endif + +/* +** Both *pMem1 and *pMem2 contain string values. Compare the two values +** using the collation sequence pColl. As usual, return a negative , zero +** or positive value if *pMem1 is less than, equal to or greater than +** *pMem2, respectively. Similar in spirit to "rc = (*pMem1) - (*pMem2);". +*/ +static int vdbeCompareMemString( + const Mem *pMem1, + const Mem *pMem2, + const CollSeq *pColl +){ + if( pMem1->enc==pColl->enc ){ + /* The strings are already in the correct encoding. Call the + ** comparison function directly */ + return pColl->xCmp(pColl->pUser,pMem1->n,pMem1->z,pMem2->n,pMem2->z); + }else{ + int rc; + const void *v1, *v2; + int n1, n2; + Mem c1; + Mem c2; + memset(&c1, 0, sizeof(c1)); + memset(&c2, 0, sizeof(c2)); + sqlite3VdbeMemShallowCopy(&c1, pMem1, MEM_Ephem); + sqlite3VdbeMemShallowCopy(&c2, pMem2, MEM_Ephem); + v1 = sqlite3ValueText((sqlite3_value*)&c1, pColl->enc); + n1 = v1==0 ? 0 : c1.n; + v2 = sqlite3ValueText((sqlite3_value*)&c2, pColl->enc); + n2 = v2==0 ? 0 : c2.n; + rc = pColl->xCmp(pColl->pUser, n1, v1, n2, v2); + sqlite3VdbeMemRelease(&c1); + sqlite3VdbeMemRelease(&c2); + return rc; } - return rc; } + +/* +** Compare the values contained by the two memory cells, returning +** negative, zero or positive if pMem1 is less than, equal to, or greater +** than pMem2. Sorting order is NULL's first, followed by numbers (integers +** and reals) sorted numerically, followed by text ordered by the collating +** sequence pColl and finally blob's ordered by memcmp(). +** +** Two NULL values are considered equal by this function. +*/ +SQLITE_PRIVATE int sqlite3MemCompare(const Mem *pMem1, const Mem *pMem2, const CollSeq *pColl){ + int rc; + int f1, f2; + int combined_flags; + + f1 = pMem1->flags; + f2 = pMem2->flags; + combined_flags = f1|f2; + assert( (combined_flags & MEM_RowSet)==0 ); + + /* If one value is NULL, it is less than the other. If both values + ** are NULL, return 0. + */ + if( combined_flags&MEM_Null ){ + return (f2&MEM_Null) - (f1&MEM_Null); + } + + /* If one value is a number and the other is not, the number is less. + ** If both are numbers, compare as reals if one is a real, or as integers + ** if both values are integers. + */ + if( combined_flags&(MEM_Int|MEM_Real) ){ + double r1, r2; + if( (f1 & f2 & MEM_Int)!=0 ){ + if( pMem1->u.i < pMem2->u.i ) return -1; + if( pMem1->u.i > pMem2->u.i ) return 1; + return 0; + } + if( (f1&MEM_Real)!=0 ){ + r1 = pMem1->r; + }else if( (f1&MEM_Int)!=0 ){ + r1 = (double)pMem1->u.i; + }else{ + return 1; + } + if( (f2&MEM_Real)!=0 ){ + r2 = pMem2->r; + }else if( (f2&MEM_Int)!=0 ){ + r2 = (double)pMem2->u.i; + }else{ + return -1; + } + if( r1<r2 ) return -1; + if( r1>r2 ) return 1; + return 0; + } + + /* If one value is a string and the other is a blob, the string is less. + ** If both are strings, compare using the collating functions. + */ + if( combined_flags&MEM_Str ){ + if( (f1 & MEM_Str)==0 ){ + return 1; + } + if( (f2 & MEM_Str)==0 ){ + return -1; + } + + assert( pMem1->enc==pMem2->enc ); + assert( pMem1->enc==SQLITE_UTF8 || + pMem1->enc==SQLITE_UTF16LE || pMem1->enc==SQLITE_UTF16BE ); + + /* The collation sequence must be defined at this point, even if + ** the user deletes the collation sequence after the vdbe program is + ** compiled (this was not always the case). + */ + assert( !pColl || pColl->xCmp ); + + if( pColl ){ + return vdbeCompareMemString(pMem1, pMem2, pColl); + } + /* If a NULL pointer was passed as the collate function, fall through + ** to the blob case and use memcmp(). */ + } + /* Both values must be blobs. Compare using memcmp(). */ + rc = memcmp(pMem1->z, pMem2->z, (pMem1->n>pMem2->n)?pMem2->n:pMem1->n); + if( rc==0 ){ + rc = pMem1->n - pMem2->n; + } + return rc; +} + + +/* +** The first argument passed to this function is a serial-type that +** corresponds to an integer - all values between 1 and 9 inclusive +** except 7. The second points to a buffer containing an integer value +** serialized according to serial_type. This function deserializes +** and returns the value. +*/ +static i64 vdbeRecordDecodeInt(u32 serial_type, const u8 *aKey){ + u32 y; + assert( CORRUPT_DB || (serial_type>=1 && serial_type<=9 && serial_type!=7) ); + switch( serial_type ){ + case 0: + case 1: + testcase( aKey[0]&0x80 ); + return ONE_BYTE_INT(aKey); + case 2: + testcase( aKey[0]&0x80 ); + return TWO_BYTE_INT(aKey); + case 3: + testcase( aKey[0]&0x80 ); + return THREE_BYTE_INT(aKey); + case 4: { + testcase( aKey[0]&0x80 ); + y = FOUR_BYTE_UINT(aKey); + return (i64)*(int*)&y; + } + case 5: { + testcase( aKey[0]&0x80 ); + return FOUR_BYTE_UINT(aKey+2) + (((i64)1)<<32)*TWO_BYTE_INT(aKey); + } + case 6: { + u64 x = FOUR_BYTE_UINT(aKey); + testcase( aKey[0]&0x80 ); + x = (x<<32) | FOUR_BYTE_UINT(aKey+4); + return (i64)*(i64*)&x; + } + } + + return (serial_type - 8); +} + +/* +** This function compares the two table rows or index records +** specified by {nKey1, pKey1} and pPKey2. It returns a negative, zero +** or positive integer if key1 is less than, equal to or +** greater than key2. The {nKey1, pKey1} key must be a blob +** created by th OP_MakeRecord opcode of the VDBE. The pPKey2 +** key must be a parsed key such as obtained from +** sqlite3VdbeParseRecord. +** +** If argument bSkip is non-zero, it is assumed that the caller has already +** determined that the first fields of the keys are equal. +** +** Key1 and Key2 do not have to contain the same number of fields. If all +** fields that appear in both keys are equal, then pPKey2->default_rc is +** returned. +*/ +SQLITE_PRIVATE int sqlite3VdbeRecordCompare( + int nKey1, const void *pKey1, /* Left key */ + const UnpackedRecord *pPKey2, /* Right key */ + int bSkip /* If true, skip the first field */ +){ + u32 d1; /* Offset into aKey[] of next data element */ + int i; /* Index of next field to compare */ + u32 szHdr1; /* Size of record header in bytes */ + u32 idx1; /* Offset of first type in header */ + int rc = 0; /* Return value */ + Mem *pRhs = pPKey2->aMem; /* Next field of pPKey2 to compare */ + KeyInfo *pKeyInfo = pPKey2->pKeyInfo; + const unsigned char *aKey1 = (const unsigned char *)pKey1; + Mem mem1; + + /* If bSkip is true, then the caller has already determined that the first + ** two elements in the keys are equal. Fix the various stack variables so + ** that this routine begins comparing at the second field. */ + if( bSkip ){ + u32 s1; + idx1 = 1 + getVarint32(&aKey1[1], s1); + szHdr1 = aKey1[0]; + d1 = szHdr1 + sqlite3VdbeSerialTypeLen(s1); + i = 1; + pRhs++; + }else{ + idx1 = getVarint32(aKey1, szHdr1); + d1 = szHdr1; + if( d1>(unsigned)nKey1 ) return 1; /* Corruption */ + i = 0; + } + + VVA_ONLY( mem1.zMalloc = 0; ) /* Only needed by assert() statements */ + assert( pPKey2->pKeyInfo->nField+pPKey2->pKeyInfo->nXField>=pPKey2->nField + || CORRUPT_DB ); + assert( pPKey2->pKeyInfo->aSortOrder!=0 ); + assert( pPKey2->pKeyInfo->nField>0 ); + assert( idx1<=szHdr1 || CORRUPT_DB ); + do{ + u32 serial_type; + + /* RHS is an integer */ + if( pRhs->flags & MEM_Int ){ + serial_type = aKey1[idx1]; + testcase( serial_type==12 ); + if( serial_type>=12 ){ + rc = +1; + }else if( serial_type==0 ){ + rc = -1; + }else if( serial_type==7 ){ + double rhs = (double)pRhs->u.i; + sqlite3VdbeSerialGet(&aKey1[d1], serial_type, &mem1); + if( mem1.r<rhs ){ + rc = -1; + }else if( mem1.r>rhs ){ + rc = +1; + } + }else{ + i64 lhs = vdbeRecordDecodeInt(serial_type, &aKey1[d1]); + i64 rhs = pRhs->u.i; + if( lhs<rhs ){ + rc = -1; + }else if( lhs>rhs ){ + rc = +1; + } + } + } + + /* RHS is real */ + else if( pRhs->flags & MEM_Real ){ + serial_type = aKey1[idx1]; + if( serial_type>=12 ){ + rc = +1; + }else if( serial_type==0 ){ + rc = -1; + }else{ + double rhs = pRhs->r; + double lhs; + sqlite3VdbeSerialGet(&aKey1[d1], serial_type, &mem1); + if( serial_type==7 ){ + lhs = mem1.r; + }else{ + lhs = (double)mem1.u.i; + } + if( lhs<rhs ){ + rc = -1; + }else if( lhs>rhs ){ + rc = +1; + } + } + } + + /* RHS is a string */ + else if( pRhs->flags & MEM_Str ){ + getVarint32(&aKey1[idx1], serial_type); + testcase( serial_type==12 ); + if( serial_type<12 ){ + rc = -1; + }else if( !(serial_type & 0x01) ){ + rc = +1; + }else{ + mem1.n = (serial_type - 12) / 2; + testcase( (d1+mem1.n)==(unsigned)nKey1 ); + testcase( (d1+mem1.n+1)==(unsigned)nKey1 ); + if( (d1+mem1.n) > (unsigned)nKey1 ){ + rc = 1; /* Corruption */ + }else if( pKeyInfo->aColl[i] ){ + mem1.enc = pKeyInfo->enc; + mem1.db = pKeyInfo->db; + mem1.flags = MEM_Str; + mem1.z = (char*)&aKey1[d1]; + rc = vdbeCompareMemString(&mem1, pRhs, pKeyInfo->aColl[i]); + }else{ + int nCmp = MIN(mem1.n, pRhs->n); + rc = memcmp(&aKey1[d1], pRhs->z, nCmp); + if( rc==0 ) rc = mem1.n - pRhs->n; + } + } + } + + /* RHS is a blob */ + else if( pRhs->flags & MEM_Blob ){ + getVarint32(&aKey1[idx1], serial_type); + testcase( serial_type==12 ); + if( serial_type<12 || (serial_type & 0x01) ){ + rc = -1; + }else{ + int nStr = (serial_type - 12) / 2; + testcase( (d1+nStr)==(unsigned)nKey1 ); + testcase( (d1+nStr+1)==(unsigned)nKey1 ); + if( (d1+nStr) > (unsigned)nKey1 ){ + rc = 1; /* Corruption */ + }else{ + int nCmp = MIN(nStr, pRhs->n); + rc = memcmp(&aKey1[d1], pRhs->z, nCmp); + if( rc==0 ) rc = nStr - pRhs->n; + } + } + } + + /* RHS is null */ + else{ + serial_type = aKey1[idx1]; + rc = (serial_type!=0); + } + + if( rc!=0 ){ + if( pKeyInfo->aSortOrder[i] ){ + rc = -rc; + } + assert( CORRUPT_DB + || (rc<0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)<0) + || (rc>0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)>0) + || pKeyInfo->db->mallocFailed + ); + assert( mem1.zMalloc==0 ); /* See comment below */ + return rc; + } + + i++; + pRhs++; + d1 += sqlite3VdbeSerialTypeLen(serial_type); + idx1 += sqlite3VarintLen(serial_type); + }while( idx1<(unsigned)szHdr1 && i<pPKey2->nField && d1<=(unsigned)nKey1 ); + + /* No memory allocation is ever used on mem1. Prove this using + ** the following assert(). If the assert() fails, it indicates a + ** memory leak and a need to call sqlite3VdbeMemRelease(&mem1). */ + assert( mem1.zMalloc==0 ); + + /* rc==0 here means that one or both of the keys ran out of fields and + ** all the fields up to that point were equal. Return the the default_rc + ** value. */ + assert( CORRUPT_DB + || pPKey2->default_rc==vdbeRecordCompareDebug(nKey1, pKey1, pPKey2) + ); + return pPKey2->default_rc; +} + +/* +** This function is an optimized version of sqlite3VdbeRecordCompare() +** that (a) the first field of pPKey2 is an integer, and (b) the +** size-of-header varint at the start of (pKey1/nKey1) fits in a single +** byte (i.e. is less than 128). +*/ +static int vdbeRecordCompareInt( + int nKey1, const void *pKey1, /* Left key */ + const UnpackedRecord *pPKey2, /* Right key */ + int bSkip /* Ignored */ +){ + const u8 *aKey = &((const u8*)pKey1)[*(const u8*)pKey1 & 0x3F]; + int serial_type = ((const u8*)pKey1)[1]; + int res; + u32 y; + u64 x; + i64 v = pPKey2->aMem[0].u.i; + i64 lhs; + UNUSED_PARAMETER(bSkip); + + assert( bSkip==0 ); + switch( serial_type ){ + case 1: { /* 1-byte signed integer */ + lhs = ONE_BYTE_INT(aKey); + testcase( lhs<0 ); + break; + } + case 2: { /* 2-byte signed integer */ + lhs = TWO_BYTE_INT(aKey); + testcase( lhs<0 ); + break; + } + case 3: { /* 3-byte signed integer */ + lhs = THREE_BYTE_INT(aKey); + testcase( lhs<0 ); + break; + } + case 4: { /* 4-byte signed integer */ + y = FOUR_BYTE_UINT(aKey); + lhs = (i64)*(int*)&y; + testcase( lhs<0 ); + break; + } + case 5: { /* 6-byte signed integer */ + lhs = FOUR_BYTE_UINT(aKey+2) + (((i64)1)<<32)*TWO_BYTE_INT(aKey); + testcase( lhs<0 ); + break; + } + case 6: { /* 8-byte signed integer */ + x = FOUR_BYTE_UINT(aKey); + x = (x<<32) | FOUR_BYTE_UINT(aKey+4); + lhs = *(i64*)&x; + testcase( lhs<0 ); + break; + } + case 8: + lhs = 0; + break; + case 9: + lhs = 1; + break; + + /* This case could be removed without changing the results of running + ** this code. Including it causes gcc to generate a faster switch + ** statement (since the range of switch targets now starts at zero and + ** is contiguous) but does not cause any duplicate code to be generated + ** (as gcc is clever enough to combine the two like cases). Other + ** compilers might be similar. */ + case 0: case 7: + return sqlite3VdbeRecordCompare(nKey1, pKey1, pPKey2, 0); + + default: + return sqlite3VdbeRecordCompare(nKey1, pKey1, pPKey2, 0); + } + + if( v>lhs ){ + res = pPKey2->r1; + }else if( v<lhs ){ + res = pPKey2->r2; + }else if( pPKey2->nField>1 ){ + /* The first fields of the two keys are equal. Compare the trailing + ** fields. */ + res = sqlite3VdbeRecordCompare(nKey1, pKey1, pPKey2, 1); + }else{ + /* The first fields of the two keys are equal and there are no trailing + ** fields. Return pPKey2->default_rc in this case. */ + res = pPKey2->default_rc; + } + + assert( (res==0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)==0) + || (res<0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)<0) + || (res>0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)>0) + || CORRUPT_DB + ); + return res; +} + +/* +** This function is an optimized version of sqlite3VdbeRecordCompare() +** that (a) the first field of pPKey2 is a string, that (b) the first field +** uses the collation sequence BINARY and (c) that the size-of-header varint +** at the start of (pKey1/nKey1) fits in a single byte. +*/ +static int vdbeRecordCompareString( + int nKey1, const void *pKey1, /* Left key */ + const UnpackedRecord *pPKey2, /* Right key */ + int bSkip +){ + const u8 *aKey1 = (const u8*)pKey1; + int serial_type; + int res; + UNUSED_PARAMETER(bSkip); + + assert( bSkip==0 ); + getVarint32(&aKey1[1], serial_type); + + if( serial_type<12 ){ + res = pPKey2->r1; /* (pKey1/nKey1) is a number or a null */ + }else if( !(serial_type & 0x01) ){ + res = pPKey2->r2; /* (pKey1/nKey1) is a blob */ + }else{ + int nCmp; + int nStr; + int szHdr = aKey1[0]; + + nStr = (serial_type-12) / 2; + if( (szHdr + nStr) > nKey1 ) return 0; /* Corruption */ + nCmp = MIN( pPKey2->aMem[0].n, nStr ); + res = memcmp(&aKey1[szHdr], pPKey2->aMem[0].z, nCmp); + + if( res==0 ){ + res = nStr - pPKey2->aMem[0].n; + if( res==0 ){ + if( pPKey2->nField>1 ){ + res = sqlite3VdbeRecordCompare(nKey1, pKey1, pPKey2, 1); + }else{ + res = pPKey2->default_rc; + } + }else if( res>0 ){ + res = pPKey2->r2; + }else{ + res = pPKey2->r1; + } + }else if( res>0 ){ + res = pPKey2->r2; + }else{ + res = pPKey2->r1; + } + } + + assert( (res==0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)==0) + || (res<0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)<0) + || (res>0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)>0) + || CORRUPT_DB + ); + return res; +} + +/* +** Return a pointer to an sqlite3VdbeRecordCompare() compatible function +** suitable for comparing serialized records to the unpacked record passed +** as the only argument. +*/ +SQLITE_PRIVATE RecordCompare sqlite3VdbeFindCompare(UnpackedRecord *p){ + /* varintRecordCompareInt() and varintRecordCompareString() both assume + ** that the size-of-header varint that occurs at the start of each record + ** fits in a single byte (i.e. is 127 or less). varintRecordCompareInt() + ** also assumes that it is safe to overread a buffer by at least the + ** maximum possible legal header size plus 8 bytes. Because there is + ** guaranteed to be at least 74 (but not 136) bytes of padding following each + ** buffer passed to varintRecordCompareInt() this makes it convenient to + ** limit the size of the header to 64 bytes in cases where the first field + ** is an integer. + ** + ** The easiest way to enforce this limit is to consider only records with + ** 13 fields or less. If the first field is an integer, the maximum legal + ** header size is (12*5 + 1 + 1) bytes. */ + if( (p->pKeyInfo->nField + p->pKeyInfo->nXField)<=13 ){ + int flags = p->aMem[0].flags; + if( p->pKeyInfo->aSortOrder[0] ){ + p->r1 = 1; + p->r2 = -1; + }else{ + p->r1 = -1; + p->r2 = 1; + } + if( (flags & MEM_Int) ){ + return vdbeRecordCompareInt; + } + testcase( flags & MEM_Real ); + testcase( flags & MEM_Null ); + testcase( flags & MEM_Blob ); + if( (flags & (MEM_Real|MEM_Null|MEM_Blob))==0 && p->pKeyInfo->aColl[0]==0 ){ + assert( flags & MEM_Str ); + return vdbeRecordCompareString; + } + } + + return sqlite3VdbeRecordCompare; +} /* ** pCur points at an index entry created using the OP_MakeRecord opcode. @@ -64598,9 +65223,9 @@ idx_rowid_corruption: ** of the keys prior to the final rowid, not the entire key. */ SQLITE_PRIVATE int sqlite3VdbeIdxKeyCompare( - VdbeCursor *pC, /* The cursor to compare against */ - UnpackedRecord *pUnpacked, /* Unpacked version of key to compare against */ - int *res /* Write the comparison result here */ + VdbeCursor *pC, /* The cursor to compare against */ + const UnpackedRecord *pUnpacked, /* Unpacked version of key */ + int *res /* Write the comparison result here */ ){ i64 nCellKey = 0; int rc; @@ -64610,7 +65235,7 @@ SQLITE_PRIVATE int sqlite3VdbeIdxKeyCompare( assert( sqlite3BtreeCursorIsValid(pCur) ); VVA_ONLY(rc =) sqlite3BtreeKeySize(pCur, &nCellKey); assert( rc==SQLITE_OK ); /* pCur is always valid so KeySize cannot fail */ - /* nCellKey will always be between 0 and 0xffffffff because of the say + /* nCellKey will always be between 0 and 0xffffffff because of the way ** that btreeParseCellPtr() and sqlite3GetVarint32() are implemented */ if( nCellKey<=0 || nCellKey>0x7fffffff ){ *res = 0; @@ -64621,8 +65246,7 @@ SQLITE_PRIVATE int sqlite3VdbeIdxKeyCompare( if( rc ){ return rc; } - assert( pUnpacked->flags & UNPACKED_PREFIX_MATCH ); - *res = sqlite3VdbeRecordCompare(m.n, m.z, pUnpacked); + *res = sqlite3VdbeRecordCompare(m.n, m.z, pUnpacked, 0); sqlite3VdbeMemRelease(&m); return SQLITE_OK; } @@ -64686,7 +65310,6 @@ SQLITE_PRIVATE sqlite3_value *sqlite3VdbeGetBoundValue(Vdbe *v, int iVar, u8 aff if( pRet ){ sqlite3VdbeMemCopy((Mem *)pRet, pMem); sqlite3ValueApplyAffinity(pRet, aff, SQLITE_UTF8); - sqlite3VdbeMemStoreType((Mem *)pRet); } return pRet; } @@ -64860,7 +65483,6 @@ SQLITE_API const void *sqlite3_value_blob(sqlite3_value *pVal){ Mem *p = (Mem*)pVal; if( p->flags & (MEM_Blob|MEM_Str) ){ sqlite3VdbeMemExpandBlob(p); - p->flags &= ~MEM_Str; p->flags |= MEM_Blob; return p->n ? p->z : 0; }else{ @@ -64897,7 +65519,41 @@ SQLITE_API const void *sqlite3_value_text16le(sqlite3_value *pVal){ } #endif /* SQLITE_OMIT_UTF16 */ SQLITE_API int sqlite3_value_type(sqlite3_value* pVal){ - return pVal->type; + static const u8 aType[] = { + SQLITE_BLOB, /* 0x00 */ + SQLITE_NULL, /* 0x01 */ + SQLITE_TEXT, /* 0x02 */ + SQLITE_NULL, /* 0x03 */ + SQLITE_INTEGER, /* 0x04 */ + SQLITE_NULL, /* 0x05 */ + SQLITE_INTEGER, /* 0x06 */ + SQLITE_NULL, /* 0x07 */ + SQLITE_FLOAT, /* 0x08 */ + SQLITE_NULL, /* 0x09 */ + SQLITE_FLOAT, /* 0x0a */ + SQLITE_NULL, /* 0x0b */ + SQLITE_INTEGER, /* 0x0c */ + SQLITE_NULL, /* 0x0d */ + SQLITE_INTEGER, /* 0x0e */ + SQLITE_NULL, /* 0x0f */ + SQLITE_BLOB, /* 0x10 */ + SQLITE_NULL, /* 0x11 */ + SQLITE_TEXT, /* 0x12 */ + SQLITE_NULL, /* 0x13 */ + SQLITE_INTEGER, /* 0x14 */ + SQLITE_NULL, /* 0x15 */ + SQLITE_INTEGER, /* 0x16 */ + SQLITE_NULL, /* 0x17 */ + SQLITE_FLOAT, /* 0x18 */ + SQLITE_NULL, /* 0x19 */ + SQLITE_FLOAT, /* 0x1a */ + SQLITE_NULL, /* 0x1b */ + SQLITE_INTEGER, /* 0x1c */ + SQLITE_NULL, /* 0x1d */ + SQLITE_INTEGER, /* 0x1e */ + SQLITE_NULL, /* 0x1f */ + }; + return aType[pVal->flags&MEM_AffMask]; } /**************************** sqlite3_result_ ******************************* @@ -65418,6 +66074,30 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt){ return pVm->nResColumn; } +/* +** Return a pointer to static memory containing an SQL NULL value. +*/ +static const Mem *columnNullValue(void){ + /* Even though the Mem structure contains an element + ** of type i64, on certain architectures (x86) with certain compiler + ** switches (-Os), gcc may align this Mem object on a 4-byte boundary + ** instead of an 8-byte one. This all works fine, except that when + ** running with SQLITE_DEBUG defined the SQLite code sometimes assert()s + ** that a Mem structure is located on an 8-byte boundary. To prevent + ** these assert()s from failing, when building with SQLITE_DEBUG defined + ** using gcc, we force nullMem to be 8-byte aligned using the magical + ** __attribute__((aligned(8))) macro. */ + static const Mem nullMem +#if defined(SQLITE_DEBUG) && defined(__GNUC__) + __attribute__((aligned(8))) +#endif + = {0, "", (double)0, {0}, 0, MEM_Null, 0, +#ifdef SQLITE_DEBUG + 0, 0, /* pScopyFrom, pFiller */ +#endif + 0, 0 }; + return &nullMem; +} /* ** Check to see if column iCol of the given statement is valid. If @@ -65434,32 +66114,11 @@ static Mem *columnMem(sqlite3_stmt *pStmt, int i){ sqlite3_mutex_enter(pVm->db->mutex); pOut = &pVm->pResultSet[i]; }else{ - /* If the value passed as the second argument is out of range, return - ** a pointer to the following static Mem object which contains the - ** value SQL NULL. Even though the Mem structure contains an element - ** of type i64, on certain architectures (x86) with certain compiler - ** switches (-Os), gcc may align this Mem object on a 4-byte boundary - ** instead of an 8-byte one. This all works fine, except that when - ** running with SQLITE_DEBUG defined the SQLite code sometimes assert()s - ** that a Mem structure is located on an 8-byte boundary. To prevent - ** these assert()s from failing, when building with SQLITE_DEBUG defined - ** using gcc, we force nullMem to be 8-byte aligned using the magical - ** __attribute__((aligned(8))) macro. */ - static const Mem nullMem -#if defined(SQLITE_DEBUG) && defined(__GNUC__) - __attribute__((aligned(8))) -#endif - = {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); sqlite3Error(pVm->db, SQLITE_RANGE, 0); } - pOut = (Mem*)&nullMem; + pOut = (Mem*)columnNullValue(); } return pOut; } @@ -65856,7 +66515,7 @@ SQLITE_API int sqlite3_bind_text16( #endif /* SQLITE_OMIT_UTF16 */ SQLITE_API int sqlite3_bind_value(sqlite3_stmt *pStmt, int i, const sqlite3_value *pValue){ int rc; - switch( pValue->type ){ + switch( sqlite3_value_type((sqlite3_value*)pValue) ){ case SQLITE_INTEGER: { rc = sqlite3_bind_int64(pStmt, i, pValue->u.i); break; @@ -66357,33 +67016,8 @@ SQLITE_PRIVATE const char *sqlite3VdbeExplanation(Vdbe *pVdbe){ ** May you share freely, never taking more than you give. ** ************************************************************************* -** The code in this file implements execution method of the -** Virtual Database Engine (VDBE). A separate file ("vdbeaux.c") -** handles housekeeping details such as creating and deleting -** VDBE instances. This file is solely interested in executing -** the VDBE program. -** -** In the external interface, an "sqlite3_stmt*" is an opaque pointer -** to a VDBE. -** -** The SQL parser generates a program which is then executed by -** the VDBE to do the work of the SQL statement. VDBE programs are -** similar in form to assembly language. The program consists of -** a linear sequence of operations. Each operation has an opcode -** and 5 operands. Operands P1, P2, and P3 are integers. Operand P4 -** is a null-terminated string. Operand P5 is an unsigned character. -** Few opcodes use all 5 operands. -** -** Computation results are stored on a set of registers numbered beginning -** with 1 and going up to Vdbe.nMem. Each register can store -** either an integer, a null-terminated string, a floating point -** number, or the SQL "NULL" value. An implicit conversion from one -** type to the other occurs as necessary. -** -** Most of the code in this file is taken up by the sqlite3VdbeExec() -** function which does the work of interpreting a VDBE program. -** But other routines are also provided to help in building up -** a program instruction by instruction. +** The code in this file implements the function that runs the +** bytecode of a prepared statement. ** ** Various scripts scan this source file in order to generate HTML ** documentation, headers files, or other derived files. The formatting @@ -66395,7 +67029,11 @@ SQLITE_PRIVATE const char *sqlite3VdbeExplanation(Vdbe *pVdbe){ /* ** Invoke this macro on memory cells just prior to changing the ** value of the cell. This macro verifies that shallow copies are -** not misused. +** not misused. A shallow copy of a string or blob just copies a +** pointer to the string or blob, not the content. If the original +** is changed while the copy is still in use, the string or blob might +** be changed out from under the copy. This macro verifies that nothing +** like that ever happens. */ #ifdef SQLITE_DEBUG # define memAboutToChange(P,M) sqlite3VdbeMemAboutToChange(P,M) @@ -66454,7 +67092,7 @@ static void updateMaxBlobsize(Mem *p){ #endif /* -** The next global variable is incremented each type the OP_Found opcode +** The next global variable is incremented each time the OP_Found opcode ** is executed. This is used to test whether or not the foreign key ** operation implemented using OP_FkIsZero is working. This variable ** has no function other than to help verify the correct operation of the @@ -66475,6 +67113,34 @@ SQLITE_API int sqlite3_found_count = 0; #endif /* +** Invoke the VDBE coverage callback, if that callback is defined. This +** feature is used for test suite validation only and does not appear an +** production builds. +** +** M is an integer, 2 or 3, that indices how many different ways the +** branch can go. It is usually 2. "I" is the direction the branch +** goes. 0 means falls through. 1 means branch is taken. 2 means the +** second alternative branch is taken. +*/ +#if !defined(SQLITE_VDBE_COVERAGE) +# define VdbeBranchTaken(I,M) +#else +# define VdbeBranchTaken(I,M) vdbeTakeBranch(pOp->iSrcLine,I,M) + static void vdbeTakeBranch(int iSrcLine, u8 I, u8 M){ + if( iSrcLine<=2 && ALWAYS(iSrcLine>0) ){ + M = iSrcLine; + /* Assert the truth of VdbeCoverageAlwaysTaken() and + ** VdbeCoverageNeverTaken() */ + assert( (M & I)==I ); + }else{ + if( sqlite3GlobalConfig.xVdbeBranch==0 ) return; /*NO_TEST*/ + sqlite3GlobalConfig.xVdbeBranch(sqlite3GlobalConfig.pVdbeBranchArg, + iSrcLine,I,M); + } + } +#endif + +/* ** Convert the given register into a string if it isn't one ** already. Return non-zero if a malloc() fails. */ @@ -66491,38 +67157,14 @@ SQLITE_API int sqlite3_found_count = 0; ** ** This routine converts an ephemeral string into a dynamically allocated ** string that the register itself controls. In other words, it -** converts an MEM_Ephem string into an MEM_Dyn string. +** converts an MEM_Ephem string into a string with P.z==P.zMalloc. */ #define Deephemeralize(P) \ if( ((P)->flags&MEM_Ephem)!=0 \ && sqlite3VdbeMemMakeWriteable(P) ){ goto no_mem;} /* Return true if the cursor was opened using the OP_OpenSorter opcode. */ -# define isSorter(x) ((x)->pSorter!=0) - -/* -** Argument pMem points at a register that will be passed to a -** user-defined function or returned to the user as the result of a query. -** This routine sets the pMem->type variable used by the sqlite3_value_*() -** routines. -*/ -SQLITE_PRIVATE void sqlite3VdbeMemStoreType(Mem *pMem){ - int flags = pMem->flags; - if( flags & MEM_Null ){ - pMem->type = SQLITE_NULL; - } - else if( flags & MEM_Int ){ - pMem->type = SQLITE_INTEGER; - } - else if( flags & MEM_Real ){ - pMem->type = SQLITE_FLOAT; - } - else if( flags & MEM_Str ){ - pMem->type = SQLITE_TEXT; - }else{ - pMem->type = SQLITE_BLOB; - } -} +#define isSorter(x) ((x)->pSorter!=0) /* ** Allocate VdbeCursor number iCur. Return a pointer to it. Return NULL @@ -66652,12 +67294,13 @@ static void applyAffinity( ** loss of information and return the revised type of the argument. */ SQLITE_API int sqlite3_value_numeric_type(sqlite3_value *pVal){ - Mem *pMem = (Mem*)pVal; - if( pMem->type==SQLITE_TEXT ){ + int eType = sqlite3_value_type(pVal); + if( eType==SQLITE_TEXT ){ + Mem *pMem = (Mem*)pVal; applyNumericAffinity(pMem); - sqlite3VdbeMemStoreType(pMem); + eType = sqlite3_value_type(pVal); } - return pMem->type; + return eType; } /* @@ -66760,7 +67403,7 @@ SQLITE_PRIVATE void sqlite3VdbeMemPrettyPrint(Mem *pMem, char *zBuf){ ** Print the value of a register for tracing purposes: */ static void memTracePrint(Mem *p){ - if( p->flags & MEM_Invalid ){ + if( p->flags & MEM_Undefined ){ printf(" undefined"); }else if( p->flags & MEM_Null ){ printf(" NULL"); @@ -66893,20 +67536,6 @@ SQLITE_PRIVATE sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); } #endif -/* -** The CHECK_FOR_INTERRUPT macro defined here looks to see if the -** sqlite3_interrupt() routine has been called. If it has been, then -** processing of the VDBE program is interrupted. -** -** This macro added to every instruction that does a jump in order to -** implement a loop. This test used to be on every single instruction, -** but that meant we more testing than we needed. By only testing the -** flag on jump instructions, we get a (small) speed improvement. -*/ -#define CHECK_FOR_INTERRUPT \ - if( db->u1.isInterrupted ) goto abort_due_to_interrupt; - - #ifndef NDEBUG /* ** This function is only called from within an assert() expression. It @@ -66929,35 +67558,8 @@ static int checkSavepointCount(sqlite3 *db){ /* -** Execute as much of a VDBE program as we can then return. -** -** sqlite3VdbeMakeReady() must be called before this routine in order to -** close the program with a final OP_Halt and to set up the callbacks -** and the error message pointer. -** -** Whenever a row or result data is available, this routine will either -** invoke the result callback (if there is one) or return with -** SQLITE_ROW. -** -** If an attempt is made to open a locked database, then this routine -** will either invoke the busy callback (if there is one) or it will -** return SQLITE_BUSY. -** -** If an error occurs, an error message is written to memory obtained -** from sqlite3_malloc() and p->zErrMsg is made to point to that memory. -** The error code is stored in p->rc and this routine returns SQLITE_ERROR. -** -** If the callback ever returns non-zero, then the program exits -** immediately. There will be no error message but the p->rc field is -** set to SQLITE_ABORT and this routine will return SQLITE_ERROR. -** -** A memory allocation error causes p->rc to be set to SQLITE_NOMEM and this -** routine to return SQLITE_ERROR. -** -** Other fatal errors return SQLITE_ERROR. -** -** After this routine has finished, sqlite3VdbeFinalize() should be -** used to clean up the mess that was left behind. +** Execute as much of a VDBE program as we can. +** This is the core of sqlite3_step(). */ SQLITE_PRIVATE int sqlite3VdbeExec( Vdbe *p /* The VDBE */ @@ -66983,7 +67585,6 @@ SQLITE_PRIVATE int sqlite3VdbeExec( 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 */ #endif /*** INSERT STACK UNION HERE ***/ @@ -67001,7 +67602,7 @@ SQLITE_PRIVATE int sqlite3VdbeExec( assert( p->explain==0 ); p->pResultSet = 0; db->busyHandler.nBusy = 0; - CHECK_FOR_INTERRUPT; + if( db->u1.isInterrupted ) goto abort_due_to_interrupt; sqlite3VdbeIOTraceSql(p); #ifndef SQLITE_OMIT_PROGRESS_CALLBACK if( db->xProgress ){ @@ -67045,7 +67646,6 @@ SQLITE_PRIVATE int sqlite3VdbeExec( assert( pc>=0 && pc<p->nOp ); if( db->mallocFailed ) goto no_mem; #ifdef VDBE_PROFILE - origPc = pc; start = sqlite3Hwtime(); #endif nVmStep++; @@ -67093,18 +67693,21 @@ SQLITE_PRIVATE int sqlite3VdbeExec( assert( pOp->p1>0 ); assert( pOp->p1<=(p->nMem-p->nCursor) ); assert( memIsValid(&aMem[pOp->p1]) ); + assert( sqlite3VdbeCheckMemInvariants(&aMem[pOp->p1]) ); REGISTER_TRACE(pOp->p1, &aMem[pOp->p1]); } if( (pOp->opflags & OPFLG_IN2)!=0 ){ assert( pOp->p2>0 ); assert( pOp->p2<=(p->nMem-p->nCursor) ); assert( memIsValid(&aMem[pOp->p2]) ); + assert( sqlite3VdbeCheckMemInvariants(&aMem[pOp->p2]) ); REGISTER_TRACE(pOp->p2, &aMem[pOp->p2]); } if( (pOp->opflags & OPFLG_IN3)!=0 ){ assert( pOp->p3>0 ); assert( pOp->p3<=(p->nMem-p->nCursor) ); assert( memIsValid(&aMem[pOp->p3]) ); + assert( sqlite3VdbeCheckMemInvariants(&aMem[pOp->p3]) ); REGISTER_TRACE(pOp->p3, &aMem[pOp->p3]); } if( (pOp->opflags & OPFLG_OUT2)!=0 ){ @@ -67162,6 +67765,11 @@ SQLITE_PRIVATE int sqlite3VdbeExec( ** The next instruction executed will be ** the one at index P2 from the beginning of ** the program. +** +** The P1 parameter is not actually used by this opcode. However, it +** is sometimes set to 1 instead of 0 as a hint to the command-line shell +** that this Goto is the bottom of a loop and that the lines from P2 down +** to the current line should be indented for EXPLAIN output. */ case OP_Goto: { /* jump */ pc = pOp->p2 - 1; @@ -67177,7 +67785,7 @@ case OP_Goto: { /* jump */ ** checks on every opcode. This helps sqlite3_step() to run about 1.5% ** faster according to "valgrind --tool=cachegrind" */ check_for_interrupt: - CHECK_FOR_INTERRUPT; + if( db->u1.isInterrupted ) goto abort_due_to_interrupt; #ifndef SQLITE_OMIT_PROGRESS_CALLBACK /* Call the progress callback if it is configured and the required number ** of VDBE ops have been executed (either since this invocation of @@ -67206,7 +67814,7 @@ check_for_interrupt: case OP_Gosub: { /* jump */ assert( pOp->p1>0 && pOp->p1<=(p->nMem-p->nCursor) ); pIn1 = &aMem[pOp->p1]; - assert( (pIn1->flags & MEM_Dyn)==0 ); + assert( VdbeMemDynamic(pIn1)==0 ); memAboutToChange(p, pIn1); pIn1->flags = MEM_Int; pIn1->u.i = pc; @@ -67217,23 +67825,69 @@ case OP_Gosub: { /* jump */ /* Opcode: Return P1 * * * * ** -** Jump to the next instruction after the address in register P1. +** Jump to the next instruction after the address in register P1. After +** the jump, register P1 becomes undefined. */ case OP_Return: { /* in1 */ pIn1 = &aMem[pOp->p1]; - assert( pIn1->flags & MEM_Int ); + assert( pIn1->flags==MEM_Int ); pc = (int)pIn1->u.i; + pIn1->flags = MEM_Undefined; + break; +} + +/* Opcode: InitCoroutine P1 P2 P3 * * +** +** Set up register P1 so that it will OP_Yield to the co-routine +** located at address P3. +** +** If P2!=0 then the co-routine implementation immediately follows +** this opcode. So jump over the co-routine implementation to +** address P2. +*/ +case OP_InitCoroutine: { /* jump */ + assert( pOp->p1>0 && pOp->p1<=(p->nMem-p->nCursor) ); + assert( pOp->p2>=0 && pOp->p2<p->nOp ); + assert( pOp->p3>=0 && pOp->p3<p->nOp ); + pOut = &aMem[pOp->p1]; + assert( !VdbeMemDynamic(pOut) ); + pOut->u.i = pOp->p3 - 1; + pOut->flags = MEM_Int; + if( pOp->p2 ) pc = pOp->p2 - 1; + break; +} + +/* Opcode: EndCoroutine P1 * * * * +** +** The instruction at the address in register P1 is an OP_Yield. +** Jump to the P2 parameter of that OP_Yield. +** After the jump, register P1 becomes undefined. +*/ +case OP_EndCoroutine: { /* in1 */ + VdbeOp *pCaller; + pIn1 = &aMem[pOp->p1]; + assert( pIn1->flags==MEM_Int ); + assert( pIn1->u.i>=0 && pIn1->u.i<p->nOp ); + pCaller = &aOp[pIn1->u.i]; + assert( pCaller->opcode==OP_Yield ); + assert( pCaller->p2>=0 && pCaller->p2<p->nOp ); + pc = pCaller->p2 - 1; + pIn1->flags = MEM_Undefined; break; } -/* Opcode: Yield P1 * * * * +/* Opcode: Yield P1 P2 * * * ** ** Swap the program counter with the value in register P1. +** +** If the co-routine ends with OP_Yield or OP_Return then continue +** to the next instruction. But if the co-routine ends with +** OP_EndCoroutine, jump immediately to P2. */ -case OP_Yield: { /* in1 */ +case OP_Yield: { /* in1, jump */ int pcDest; pIn1 = &aMem[pOp->p1]; - assert( (pIn1->flags & MEM_Dyn)==0 ); + assert( VdbeMemDynamic(pIn1)==0 ); pIn1->flags = MEM_Int; pcDest = (int)pIn1->u.i; pIn1->u.i = pc; @@ -67243,7 +67897,7 @@ case OP_Yield: { /* in1 */ } /* Opcode: HaltIfNull P1 P2 P3 P4 P5 -** Synopsis: if r[P3] null then halt +** Synopsis: if r[P3]=null halt ** ** 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 @@ -67391,7 +68045,9 @@ case OP_Real: { /* same as TK_FLOAT, out2-prerelease */ ** Synopsis: r[P2]='P4' ** ** P4 points to a nul terminated UTF-8 string. This opcode is transformed -** into an OP_String before it is executed for the first time. +** into an OP_String before it is executed for the first time. During +** this transformation, the length of string P4 is computed and stored +** as the P1 parameter. */ case OP_String8: { /* same as TK_STRING, out2-prerelease */ assert( pOp->p4.z!=0 ); @@ -67404,10 +68060,9 @@ case OP_String8: { /* same as TK_STRING, out2-prerelease */ if( rc==SQLITE_TOOBIG ) goto too_big; if( SQLITE_OK!=sqlite3VdbeChangeEncoding(pOut, encoding) ) goto no_mem; assert( pOut->zMalloc==pOut->z ); - assert( pOut->flags & MEM_Dyn ); + assert( VdbeMemDynamic(pOut)==0 ); pOut->zMalloc = 0; pOut->flags |= MEM_Static; - pOut->flags &= ~MEM_Dyn; if( pOp->p4type==P4_DYNAMIC ){ sqlite3DbFree(db, pOp->p4.z); } @@ -67465,8 +68120,22 @@ case OP_Null: { /* out2-prerelease */ break; } +/* Opcode: SoftNull P1 * * * * +** Synopsis: r[P1]=NULL +** +** Set register P1 to have the value NULL as seen by the OP_MakeRecord +** instruction, but do not free any string or blob memory associated with +** the register, so that if the value was a string or blob that was +** previously copied using OP_SCopy, the copies will continue to be valid. +*/ +case OP_SoftNull: { + assert( pOp->p1>0 && pOp->p1<=(p->nMem-p->nCursor) ); + pOut = &aMem[pOp->p1]; + pOut->flags = (pOut->flags|MEM_Null)&~MEM_Undefined; + break; +} -/* Opcode: Blob P1 P2 * P4 +/* Opcode: Blob P1 P2 * P4 * ** Synopsis: r[P2]=P4 (len=P1) ** ** P4 points to a blob of data P1 bytes long. Store this @@ -67485,7 +68154,7 @@ case OP_Blob: { /* out2-prerelease */ ** ** Transfer the values of bound parameter P1 into register P2 ** -** If the parameter is named, then its name appears in P4 and P3==1. +** If the parameter is named, then its name appears in P4. ** The P4 value is used by sqlite3_bind_parameter_name(). */ case OP_Variable: { /* out2-prerelease */ @@ -67529,14 +68198,16 @@ case OP_Move: { assert( pIn1<=&aMem[(p->nMem-p->nCursor)] ); assert( memIsValid(pIn1) ); memAboutToChange(p, pOut); + VdbeMemRelease(pOut); zMalloc = pOut->zMalloc; - pOut->zMalloc = 0; - sqlite3VdbeMemMove(pOut, pIn1); + memcpy(pOut, pIn1, sizeof(Mem)); #ifdef SQLITE_DEBUG if( pOut->pScopyFrom>=&aMem[p1] && pOut->pScopyFrom<&aMem[p1+pOp->p3] ){ pOut->pScopyFrom += p1 - pOp->p2; } #endif + pIn1->flags = MEM_Undefined; + pIn1->xDel = 0; pIn1->zMalloc = zMalloc; REGISTER_TRACE(p2++, pOut); pIn1++; @@ -67604,8 +68275,8 @@ case OP_SCopy: { /* out2 */ ** The registers P1 through P1+P2-1 contain a single row of ** results. This opcode causes the sqlite3_step() call to terminate ** with an SQLITE_ROW return code and it sets up the sqlite3_stmt -** structure to provide access to the top P1 values as the result -** row. +** structure to provide access to the r(P1)..r(P1+P2-1) values as +** the result row. */ case OP_ResultRow: { Mem *pMem; @@ -67670,7 +68341,6 @@ case OP_ResultRow: { assert( (pMem[i].flags & MEM_Ephem)==0 || (pMem[i].flags & (MEM_Str|MEM_Blob))==0 ); sqlite3VdbeMemNulTerminate(&pMem[i]); - sqlite3VdbeMemStoreType(&pMem[i]); REGISTER_TRACE(pOp->p1+i, &pMem[i]); } if( db->mallocFailed ) goto no_mem; @@ -67713,10 +68383,10 @@ case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */ if( nByte>db->aLimit[SQLITE_LIMIT_LENGTH] ){ goto too_big; } - MemSetTypeFlag(pOut, MEM_Str); if( sqlite3VdbeMemGrow(pOut, (int)nByte+2, pOut==pIn2) ){ goto no_mem; } + MemSetTypeFlag(pOut, MEM_Str); if( pOut!=pIn2 ){ memcpy(pOut->z, pIn2->z, pIn2->n); } @@ -67916,7 +68586,6 @@ case OP_Function: { assert( memIsValid(pArg) ); apVal[i] = pArg; Deephemeralize(pArg); - sqlite3VdbeMemStoreType(pArg); REGISTER_TRACE(pOp->p2+i, pArg); } @@ -68095,6 +68764,7 @@ case OP_MustBeInt: { /* jump, in1 */ pIn1 = &aMem[pOp->p1]; if( (pIn1->flags & MEM_Int)==0 ){ applyAffinity(pIn1, SQLITE_AFF_NUMERIC, encoding); + VdbeBranchTaken((pIn1->flags&MEM_Int)==0, 2); if( (pIn1->flags & MEM_Int)==0 ){ if( pOp->p2==0 ){ rc = SQLITE_MISMATCH; @@ -68133,7 +68803,7 @@ case OP_RealAffinity: { /* in1 */ ** ** Force the value in register P1 to be text. ** If the value is numeric, convert it to a string using the -** equivalent of printf(). Blob values are unchanged and +** equivalent of sprintf(). Blob values are unchanged and ** are afterwards simply interpreted as text. ** ** A NULL value is not changed by this routine. It remains NULL. @@ -68335,6 +69005,7 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ */ assert( pOp->opcode==OP_Eq || pOp->opcode==OP_Ne ); assert( (flags1 & MEM_Cleared)==0 ); + assert( (pOp->p5 & SQLITE_JUMPIFNULL)==0 ); if( (flags1&MEM_Null)!=0 && (flags3&MEM_Null)!=0 && (flags3&MEM_Cleared)==0 @@ -68348,12 +69019,15 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ ** then the result is always NULL. ** The jump is taken if the SQLITE_JUMPIFNULL bit is set. */ - if( pOp->p5 & SQLITE_JUMPIFNULL ){ - pc = pOp->p2-1; - }else if( pOp->p5 & SQLITE_STOREP2 ){ + if( pOp->p5 & SQLITE_STOREP2 ){ pOut = &aMem[pOp->p2]; MemSetTypeFlag(pOut, MEM_Null); REGISTER_TRACE(pOp->p2, pOut); + }else{ + VdbeBranchTaken(2,3); + if( pOp->p5 & SQLITE_JUMPIFNULL ){ + pc = pOp->p2-1; + } } break; } @@ -68386,10 +69060,12 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ MemSetTypeFlag(pOut, MEM_Int); pOut->u.i = res; REGISTER_TRACE(pOp->p2, pOut); - }else if( res ){ - pc = pOp->p2-1; + }else{ + VdbeBranchTaken(res!=0, (pOp->p5 & SQLITE_NULLEQ)?2:3); + if( res ){ + pc = pOp->p2-1; + } } - /* Undo any changes made by applyAffinity() to the input registers. */ pIn1->flags = (pIn1->flags&~MEM_TypeMask) | (flags1&MEM_TypeMask); pIn3->flags = (pIn3->flags&~MEM_TypeMask) | (flags3&MEM_TypeMask); @@ -68486,11 +69162,11 @@ case OP_Compare: { */ case OP_Jump: { /* jump */ if( iCompare<0 ){ - pc = pOp->p1 - 1; + pc = pOp->p1 - 1; VdbeBranchTaken(0,3); }else if( iCompare==0 ){ - pc = pOp->p2 - 1; + pc = pOp->p2 - 1; VdbeBranchTaken(1,3); }else{ - pc = pOp->p3 - 1; + pc = pOp->p3 - 1; VdbeBranchTaken(2,3); } break; } @@ -68588,10 +69264,13 @@ case OP_BitNot: { /* same as TK_BITNOT, in1, out2 */ /* Opcode: Once P1 P2 * * * ** ** Check if OP_Once flag P1 is set. If so, jump to instruction P2. Otherwise, -** set the flag and fall through to the next instruction. +** set the flag and fall through to the next instruction. In other words, +** this opcode causes all following opcodes up through P2 (but not including +** P2) to run just once and to be skipped on subsequent times through the loop. */ case OP_Once: { /* jump */ assert( pOp->p1<p->nOnceFlag ); + VdbeBranchTaken(p->aOnceFlag[pOp->p1]!=0, 2); if( p->aOnceFlag[pOp->p1] ){ pc = pOp->p2-1; }else{ @@ -68626,6 +69305,7 @@ case OP_IfNot: { /* jump, in1 */ #endif if( pOp->opcode==OP_IfNot ) c = !c; } + VdbeBranchTaken(c!=0, 2); if( c ){ pc = pOp->p2-1; } @@ -68639,6 +69319,7 @@ case OP_IfNot: { /* jump, in1 */ */ case OP_IsNull: { /* same as TK_ISNULL, jump, in1 */ pIn1 = &aMem[pOp->p1]; + VdbeBranchTaken( (pIn1->flags & MEM_Null)!=0, 2); if( (pIn1->flags & MEM_Null)!=0 ){ pc = pOp->p2 - 1; } @@ -68652,6 +69333,7 @@ case OP_IsNull: { /* same as TK_ISNULL, jump, in1 */ */ case OP_NotNull: { /* same as TK_NOTNULL, jump, in1 */ pIn1 = &aMem[pOp->p1]; + VdbeBranchTaken( (pIn1->flags & MEM_Null)==0, 2); if( (pIn1->flags & MEM_Null)==0 ){ pc = pOp->p2 - 1; } @@ -68728,11 +69410,6 @@ case OP_Column: { if( pCrsr==0 ){ assert( pC->pseudoTableReg>0 ); pReg = &aMem[pC->pseudoTableReg]; - if( pC->multiPseudo ){ - sqlite3VdbeMemShallowCopy(pDest, pReg+p2, MEM_Ephem); - Deephemeralize(pDest); - goto op_column_out; - } assert( pReg->flags & MEM_Blob ); assert( memIsValid(pReg) ); pC->payloadSize = pC->szRow = avail = pReg->n; @@ -68883,6 +69560,7 @@ case OP_Column: { */ assert( p2<pC->nHdrParsed ); assert( rc==SQLITE_OK ); + assert( sqlite3VdbeCheckMemInvariants(pDest) ); if( pC->szRow>=aOffset[p2+1] ){ /* This is the common case where the desired content fits on the original ** page - where the content is not on an overflow page */ @@ -68920,8 +69598,8 @@ case OP_Column: { ** This prevents a memory copy. */ if( sMem.zMalloc ){ assert( sMem.z==sMem.zMalloc ); - assert( !(pDest->flags & MEM_Dyn) ); - assert( !(pDest->flags & (MEM_Blob|MEM_Str)) || pDest->z==sMem.z ); + assert( VdbeMemDynamic(pDest)==0 ); + assert( (pDest->flags & (MEM_Blob|MEM_Str))==0 || pDest->z==sMem.z ); pDest->flags &= ~(MEM_Ephem|MEM_Static); pDest->flags |= MEM_Term; pDest->z = sMem.z; @@ -68958,7 +69636,6 @@ case OP_Affinity: { while( (cAff = *(zAffinity++))!=0 ){ assert( pIn1 <= &p->aMem[(p->nMem-p->nCursor)] ); assert( memIsValid(pIn1) ); - ExpandBlob(pIn1); applyAffinity(pIn1, cAff, encoding); pIn1++; } @@ -69036,8 +69713,9 @@ case OP_MakeRecord: { if( zAffinity ){ pRec = pData0; do{ - applyAffinity(pRec, *(zAffinity++), encoding); - }while( (++pRec)<=pLast ); + applyAffinity(pRec++, *(zAffinity++), encoding); + assert( zAffinity[0]==0 || pRec<=pLast ); + }while( zAffinity[0] ); } /* Loop through the elements that will make up the record to figure @@ -69104,7 +69782,7 @@ case OP_MakeRecord: { assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) ); pOut->n = (int)nByte; - pOut->flags = MEM_Blob | MEM_Dyn; + pOut->flags = MEM_Blob; pOut->xDel = 0; if( nZero ){ pOut->u.nZero = nZero; @@ -69381,25 +70059,19 @@ case OP_AutoCommit: { break; } -/* Opcode: Transaction P1 P2 * * * +/* Opcode: Transaction P1 P2 P3 P4 P5 ** -** Begin a transaction. The transaction ends when a Commit or Rollback -** opcode is encountered. Depending on the ON CONFLICT setting, the -** transaction might also be rolled back if an error is encountered. +** Begin a transaction on database P1 if a transaction is not already +** active. +** If P2 is non-zero, then a write-transaction is started, or if a +** read-transaction is already active, it is upgraded to a write-transaction. +** If P2 is zero, then a read-transaction is started. ** ** P1 is the index of the database file on which the transaction is ** started. Index 0 is the main database file and index 1 is the ** file used for temporary tables. Indices of 2 or more are used for ** attached databases. ** -** If P2 is non-zero, then a write-transaction is started. A RESERVED lock is -** obtained on the database file when a write-transaction is started. No -** other process can start another write transaction while this transaction is -** underway. Starting a write transaction also creates a rollback journal. A -** write transaction must be started before any changes can be made to the -** database. If P2 is greater than or equal to 2 then an EXCLUSIVE lock is -** also obtained on the file. -** ** If a write-transaction is started and the Vdbe.usesStmtJournal flag is ** true (this flag is set if the Vdbe may modify more than one row and may ** throw an ABORT exception), a statement transaction may also be opened. @@ -69410,10 +70082,21 @@ case OP_AutoCommit: { ** entire transaction. If no error is encountered, the statement transaction ** will automatically commit when the VDBE halts. ** -** If P2 is zero, then a read-lock is obtained on the database file. +** If P5!=0 then this opcode also checks the schema cookie against P3 +** and the schema generation counter against P4. +** The cookie changes its value whenever the database schema changes. +** This operation is used to detect when that the cookie has changed +** and that the current process needs to reread the schema. If the schema +** cookie in P3 differs from the schema cookie in the database header or +** if the schema generation counter in P4 differs from the current +** generation counter, then an SQLITE_SCHEMA error is raised and execution +** halts. The sqlite3_step() wrapper function might then reprepare the +** statement and rerun it from the beginning. */ case OP_Transaction: { Btree *pBt; + int iMeta; + int iGen; assert( p->bIsReader ); assert( p->readOnly==0 || pOp->p2==0 ); @@ -69457,6 +70140,35 @@ case OP_Transaction: { p->nStmtDefCons = db->nDeferredCons; p->nStmtDefImmCons = db->nDeferredImmCons; } + + /* Gather the schema version number for checking */ + sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&iMeta); + iGen = db->aDb[pOp->p1].pSchema->iGeneration; + }else{ + iGen = iMeta = 0; + } + assert( pOp->p5==0 || pOp->p4type==P4_INT32 ); + if( pOp->p5 && (iMeta!=pOp->p3 || iGen!=pOp->p4.i) ){ + sqlite3DbFree(db, p->zErrMsg); + p->zErrMsg = sqlite3DbStrDup(db, "database schema has changed"); + /* If the schema-cookie from the database file matches the cookie + ** stored with the in-memory representation of the schema, do + ** not reload the schema from the database file. + ** + ** If virtual-tables are in use, this is not just an optimization. + ** Often, v-tables store their data in other SQLite tables, which + ** are queried from within xNext() and other v-table methods using + ** prepared queries. If such a query is out-of-date, we do not want to + ** discard the database schema, as the user code implementing the + ** v-table would have to be ready for the sqlite3_vtab structure itself + ** to be invalidated whenever sqlite3_step() is called from within + ** a v-table method. + */ + if( db->aDb[pOp->p1].pSchema->schema_cookie!=iMeta ){ + sqlite3ResetOneSchema(db, pOp->p1); + } + p->expired = 1; + rc = SQLITE_SCHEMA; } break; } @@ -69531,66 +70243,6 @@ case OP_SetCookie: { /* in3 */ break; } -/* 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 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. -** -** The cookie changes its value whenever the database schema changes. -** This operation is used to detect when that the cookie has changed -** and that the current process needs to reread the schema. -** -** Either a transaction needs to have been started or an OP_Open needs -** to be executed (to establish a read lock) before this opcode is -** invoked. -*/ -case OP_VerifyCookie: { - int iMeta; - int iGen; - Btree *pBt; - - assert( pOp->p1>=0 && pOp->p1<db->nDb ); - assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 ); - assert( sqlite3SchemaMutexHeld(db, pOp->p1, 0) ); - assert( p->bIsReader ); - pBt = db->aDb[pOp->p1].pBt; - if( pBt ){ - sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&iMeta); - iGen = db->aDb[pOp->p1].pSchema->iGeneration; - }else{ - iGen = iMeta = 0; - } - if( iMeta!=pOp->p2 || 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 - ** stored with the in-memory representation of the schema, do - ** not reload the schema from the database file. - ** - ** If virtual-tables are in use, this is not just an optimization. - ** Often, v-tables store their data in other SQLite tables, which - ** are queried from within xNext() and other v-table methods using - ** prepared queries. If such a query is out-of-date, we do not want to - ** discard the database schema, as the user code implementing the - ** v-table would have to be ready for the sqlite3_vtab structure itself - ** to be invalidated whenever sqlite3_step() is called from within - ** a v-table method. - */ - if( db->aDb[pOp->p1].pSchema->schema_cookie!=iMeta ){ - sqlite3ResetOneSchema(db, pOp->p1); - } - - p->expired = 1; - rc = SQLITE_SCHEMA; - } - break; -} - /* Opcode: OpenRead P1 P2 P3 P4 P5 ** Synopsis: root=P2 iDb=P3 ** @@ -69804,7 +70456,7 @@ case OP_OpenEphemeral: { break; } -/* Opcode: SorterOpen P1 * * P4 * +/* Opcode: SorterOpen P1 P2 * P4 * ** ** This opcode works like OP_OpenEphemeral except that it opens ** a transient index that is specifically designed to sort large @@ -69824,14 +70476,13 @@ case OP_SorterOpen: { break; } -/* Opcode: OpenPseudo P1 P2 P3 * P5 -** Synopsis: content in r[P2@P3] +/* Opcode: OpenPseudo P1 P2 P3 * * +** Synopsis: P3 columns in r[P2] ** ** Open a new cursor that points to a fake table that contains a single -** row of data. The content of that one row in the content of memory -** register P2 when P5==0. In other words, cursor P1 becomes an alias for the -** MEM_Blob content contained in register P2. When P5==1, then the -** row is represented by P3 consecutive registers beginning with P2. +** row of data. The content of that one row is the content of memory +** register P2. In other words, cursor P1 becomes an alias for the +** MEM_Blob content contained in register P2. ** ** A pseudo-table created by this opcode is used to hold a single ** row output from the sorter so that the row can be decomposed into @@ -69851,7 +70502,7 @@ case OP_OpenPseudo: { pCx->nullRow = 1; pCx->pseudoTableReg = pOp->p2; pCx->isTable = 1; - pCx->multiPseudo = pOp->p5; + assert( pOp->p5==0 ); break; } @@ -69923,10 +70574,10 @@ case OP_Close: { ** ** See also: Found, NotFound, Distinct, SeekGt, SeekGe, SeekLt */ -case OP_SeekLt: /* jump, in3 */ -case OP_SeekLe: /* jump, in3 */ -case OP_SeekGe: /* jump, in3 */ -case OP_SeekGt: { /* jump, in3 */ +case OP_SeekLT: /* jump, in3 */ +case OP_SeekLE: /* jump, in3 */ +case OP_SeekGE: /* jump, in3 */ +case OP_SeekGT: { /* jump, in3 */ int res; int oc; VdbeCursor *pC; @@ -69939,9 +70590,9 @@ case OP_SeekGt: { /* jump, in3 */ pC = p->apCsr[pOp->p1]; assert( pC!=0 ); assert( pC->pseudoTableReg==0 ); - assert( OP_SeekLe == OP_SeekLt+1 ); - assert( OP_SeekGe == OP_SeekLt+2 ); - assert( OP_SeekGt == OP_SeekLt+3 ); + assert( OP_SeekLE == OP_SeekLT+1 ); + assert( OP_SeekGE == OP_SeekLT+2 ); + assert( OP_SeekGT == OP_SeekLT+3 ); assert( pC->isOrdered ); assert( pC->pCursor!=0 ); oc = pOp->opcode; @@ -69961,7 +70612,7 @@ case OP_SeekGt: { /* jump, in3 */ if( (pIn3->flags & MEM_Real)==0 ){ /* If the P3 value cannot be converted into any kind of a number, ** then the seek is not possible, so jump to P2 */ - pc = pOp->p2 - 1; + pc = pOp->p2 - 1; VdbeBranchTaken(1,2); break; } @@ -69973,19 +70624,19 @@ case OP_SeekGt: { /* jump, in3 */ ** (x <= 4.9) -> (x < 5) */ if( pIn3->r<(double)iKey ){ - assert( OP_SeekGe==(OP_SeekGt-1) ); - assert( OP_SeekLt==(OP_SeekLe-1) ); - assert( (OP_SeekLe & 0x0001)==(OP_SeekGt & 0x0001) ); - if( (oc & 0x0001)==(OP_SeekGt & 0x0001) ) oc--; + assert( OP_SeekGE==(OP_SeekGT-1) ); + assert( OP_SeekLT==(OP_SeekLE-1) ); + assert( (OP_SeekLE & 0x0001)==(OP_SeekGT & 0x0001) ); + if( (oc & 0x0001)==(OP_SeekGT & 0x0001) ) oc--; } /* If the approximation iKey is smaller than the actual real search ** term, substitute <= for < and > for >=. */ else if( pIn3->r>(double)iKey ){ - assert( OP_SeekLe==(OP_SeekLt+1) ); - assert( OP_SeekGt==(OP_SeekGe+1) ); - assert( (OP_SeekLt & 0x0001)==(OP_SeekGe & 0x0001) ); - if( (oc & 0x0001)==(OP_SeekLt & 0x0001) ) oc++; + assert( OP_SeekLE==(OP_SeekLT+1) ); + assert( OP_SeekGT==(OP_SeekGE+1) ); + assert( (OP_SeekLT & 0x0001)==(OP_SeekGE & 0x0001) ); + if( (oc & 0x0001)==(OP_SeekLT & 0x0001) ) oc++; } } rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, 0, (u64)iKey, 0, &res); @@ -70004,17 +70655,17 @@ case OP_SeekGt: { /* jump, in3 */ r.nField = (u16)nField; /* The next line of code computes as follows, only faster: - ** if( oc==OP_SeekGt || oc==OP_SeekLe ){ - ** r.flags = UNPACKED_INCRKEY; + ** if( oc==OP_SeekGT || oc==OP_SeekLE ){ + ** r.default_rc = -1; ** }else{ - ** r.flags = 0; + ** r.default_rc = +1; ** } */ - r.flags = (u8)(UNPACKED_INCRKEY * (1 & (oc - OP_SeekLt))); - assert( oc!=OP_SeekGt || r.flags==UNPACKED_INCRKEY ); - assert( oc!=OP_SeekLe || r.flags==UNPACKED_INCRKEY ); - assert( oc!=OP_SeekGe || r.flags==0 ); - assert( oc!=OP_SeekLt || r.flags==0 ); + r.default_rc = ((1 & (oc - OP_SeekLT)) ? -1 : +1); + assert( oc!=OP_SeekGT || r.default_rc==-1 ); + assert( oc!=OP_SeekLE || r.default_rc==-1 ); + assert( oc!=OP_SeekGE || r.default_rc==+1 ); + assert( oc!=OP_SeekLT || r.default_rc==+1 ); r.aMem = &aMem[pOp->p3]; #ifdef SQLITE_DEBUG @@ -70032,8 +70683,9 @@ case OP_SeekGt: { /* jump, in3 */ #ifdef SQLITE_TEST sqlite3_search_count++; #endif - if( oc>=OP_SeekGe ){ assert( oc==OP_SeekGe || oc==OP_SeekGt ); - if( res<0 || (res==0 && oc==OP_SeekGt) ){ + if( oc>=OP_SeekGE ){ assert( oc==OP_SeekGE || oc==OP_SeekGT ); + if( res<0 || (res==0 && oc==OP_SeekGT) ){ + res = 0; rc = sqlite3BtreeNext(pC->pCursor, &res); if( rc!=SQLITE_OK ) goto abort_due_to_error; pC->rowidIsValid = 0; @@ -70041,8 +70693,9 @@ case OP_SeekGt: { /* jump, in3 */ res = 0; } }else{ - assert( oc==OP_SeekLt || oc==OP_SeekLe ); - if( res>0 || (res==0 && oc==OP_SeekLt) ){ + assert( oc==OP_SeekLT || oc==OP_SeekLE ); + if( res>0 || (res==0 && oc==OP_SeekLT) ){ + res = 0; rc = sqlite3BtreePrevious(pC->pCursor, &res); if( rc!=SQLITE_OK ) goto abort_due_to_error; pC->rowidIsValid = 0; @@ -70054,6 +70707,7 @@ case OP_SeekGt: { /* jump, in3 */ } } assert( pOp->p2>0 ); + VdbeBranchTaken(res!=0,2); if( res ){ pc = pOp->p2 - 1; } @@ -70162,16 +70816,13 @@ case OP_Found: { /* jump, in3 */ r.pKeyInfo = pC->pKeyInfo; r.nField = (u16)pOp->p4.i; r.aMem = pIn3; + for(ii=0; ii<r.nField; ii++){ + assert( memIsValid(&r.aMem[ii]) ); + ExpandBlob(&r.aMem[ii]); #ifdef SQLITE_DEBUG - { - int i; - for(i=0; i<r.nField; i++){ - assert( memIsValid(&r.aMem[i]) ); - if( i ) REGISTER_TRACE(pOp->p3+i, &r.aMem[i]); - } - } + if( ii ) REGISTER_TRACE(pOp->p3+ii, &r.aMem[ii]); #endif - r.flags = UNPACKED_PREFIX_MATCH; + } pIdxKey = &r; }else{ pIdxKey = sqlite3VdbeAllocUnpackedRecord( @@ -70181,15 +70832,15 @@ case OP_Found: { /* jump, in3 */ assert( pIn3->flags & MEM_Blob ); assert( (pIn3->flags & MEM_Zero)==0 ); /* zeroblobs already expanded */ sqlite3VdbeRecordUnpack(pC->pKeyInfo, pIn3->n, pIn3->z, pIdxKey); - pIdxKey->flags |= UNPACKED_PREFIX_MATCH; } + pIdxKey->default_rc = 0; if( pOp->opcode==OP_NoConflict ){ /* For the OP_NoConflict opcode, take the jump if any of the ** input fields are NULL, since any key with a NULL will not ** conflict */ for(ii=0; ii<r.nField; ii++){ if( r.aMem[ii].flags & MEM_Null ){ - pc = pOp->p2 - 1; + pc = pOp->p2 - 1; VdbeBranchTaken(1,2); break; } } @@ -70207,8 +70858,10 @@ case OP_Found: { /* jump, in3 */ pC->deferredMoveto = 0; pC->cacheStatus = CACHE_STALE; if( pOp->opcode==OP_Found ){ + VdbeBranchTaken(alreadyExists!=0,2); if( alreadyExists ) pc = pOp->p2 - 1; }else{ + VdbeBranchTaken(alreadyExists==0,2); if( !alreadyExists ) pc = pOp->p2 - 1; } break; @@ -70251,6 +70904,7 @@ case OP_NotExists: { /* jump, in3 */ pC->nullRow = 0; pC->cacheStatus = CACHE_STALE; pC->deferredMoveto = 0; + VdbeBranchTaken(res!=0,2); if( res!=0 ){ pc = pOp->p2 - 1; assert( pC->rowidIsValid==0 ); @@ -70332,59 +70986,54 @@ case OP_NewRowid: { /* out2-prerelease */ #endif if( !pC->useRandomRowid ){ - v = sqlite3BtreeGetCachedRowid(pC->pCursor); - if( v==0 ){ - rc = sqlite3BtreeLast(pC->pCursor, &res); - if( rc!=SQLITE_OK ){ - goto abort_due_to_error; - } - if( res ){ - v = 1; /* IMP: R-61914-48074 */ + rc = sqlite3BtreeLast(pC->pCursor, &res); + if( rc!=SQLITE_OK ){ + goto abort_due_to_error; + } + if( res ){ + v = 1; /* IMP: R-61914-48074 */ + }else{ + assert( sqlite3BtreeCursorIsValid(pC->pCursor) ); + rc = sqlite3BtreeKeySize(pC->pCursor, &v); + assert( rc==SQLITE_OK ); /* Cannot fail following BtreeLast() */ + if( v>=MAX_ROWID ){ + pC->useRandomRowid = 1; }else{ - assert( sqlite3BtreeCursorIsValid(pC->pCursor) ); - rc = sqlite3BtreeKeySize(pC->pCursor, &v); - assert( rc==SQLITE_OK ); /* Cannot fail following BtreeLast() */ - if( v>=MAX_ROWID ){ - pC->useRandomRowid = 1; - }else{ - v++; /* IMP: R-29538-34987 */ - } + v++; /* IMP: R-29538-34987 */ } } + } #ifndef SQLITE_OMIT_AUTOINCREMENT - if( pOp->p3 ){ + if( pOp->p3 ){ + /* Assert that P3 is a valid memory cell. */ + assert( pOp->p3>0 ); + if( p->pFrame ){ + for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent); /* Assert that P3 is a valid memory cell. */ - assert( pOp->p3>0 ); - if( p->pFrame ){ - for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent); - /* Assert that P3 is a valid memory cell. */ - assert( pOp->p3<=pFrame->nMem ); - pMem = &pFrame->aMem[pOp->p3]; - }else{ - /* Assert that P3 is a valid memory cell. */ - assert( pOp->p3<=(p->nMem-p->nCursor) ); - pMem = &aMem[pOp->p3]; - memAboutToChange(p, pMem); - } - assert( memIsValid(pMem) ); - - REGISTER_TRACE(pOp->p3, pMem); - sqlite3VdbeMemIntegerify(pMem); - assert( (pMem->flags & MEM_Int)!=0 ); /* mem(P3) holds an integer */ - if( pMem->u.i==MAX_ROWID || pC->useRandomRowid ){ - rc = SQLITE_FULL; /* IMP: R-12275-61338 */ - goto abort_due_to_error; - } - if( v<pMem->u.i+1 ){ - v = pMem->u.i + 1; - } - pMem->u.i = v; + assert( pOp->p3<=pFrame->nMem ); + pMem = &pFrame->aMem[pOp->p3]; + }else{ + /* Assert that P3 is a valid memory cell. */ + assert( pOp->p3<=(p->nMem-p->nCursor) ); + pMem = &aMem[pOp->p3]; + memAboutToChange(p, pMem); } -#endif + assert( memIsValid(pMem) ); - sqlite3BtreeSetCachedRowid(pC->pCursor, v<MAX_ROWID ? v+1 : 0); + REGISTER_TRACE(pOp->p3, pMem); + sqlite3VdbeMemIntegerify(pMem); + assert( (pMem->flags & MEM_Int)!=0 ); /* mem(P3) holds an integer */ + if( pMem->u.i==MAX_ROWID || pC->useRandomRowid ){ + rc = SQLITE_FULL; /* IMP: R-12275-61338 */ + goto abort_due_to_error; + } + if( v<pMem->u.i+1 ){ + v = pMem->u.i + 1; + } + pMem->u.i = v; } +#endif if( pC->useRandomRowid ){ /* IMPLEMENTATION-OF: R-07677-41881 If the largest ROWID is equal to the ** largest possible integer (9223372036854775807) then the database @@ -70518,7 +71167,6 @@ case OP_InsertInt: { }else{ nZero = 0; } - sqlite3BtreeSetCachedRowid(pC->pCursor, 0); rc = sqlite3BtreeInsert(pC->pCursor, 0, iKey, pData->z, pData->n, nZero, (pOp->p5 & OPFLAG_APPEND)!=0, seekResult @@ -70580,7 +71228,6 @@ case OP_Delete: { rc = sqlite3VdbeCursorMoveto(pC); if( NEVER(rc!=SQLITE_OK) ) goto abort_due_to_error; - sqlite3BtreeSetCachedRowid(pC->pCursor, 0); rc = sqlite3BtreeDelete(pC->pCursor); pC->cacheStatus = CACHE_STALE; @@ -70632,6 +71279,7 @@ case OP_SorterCompare: { pIn3 = &aMem[pOp->p3]; nIgnore = pOp->p4.i; rc = sqlite3VdbeSorterCompare(pC, pIn3, nIgnore, &res); + VdbeBranchTaken(res!=0,2); if( res ){ pc = pOp->p2-1; } @@ -70669,7 +71317,7 @@ case OP_SorterData: { ** ** Write into register P2 the complete row key for cursor P1. ** There is no interpretation of the data. -** The key is copied onto the P3 register exactly as +** The key is copied onto the P2 register exactly as ** it is found in the database file. ** ** If the P1 cursor must be pointing to a valid row (not a NULL row) @@ -70831,8 +71479,9 @@ case OP_Last: { /* jump */ pC->deferredMoveto = 0; pC->rowidIsValid = 0; pC->cacheStatus = CACHE_STALE; - if( pOp->p2>0 && res ){ - pc = pOp->p2 - 1; + if( pOp->p2>0 ){ + VdbeBranchTaken(res!=0,2); + if( res ) pc = pOp->p2 - 1; } break; } @@ -70889,13 +71538,14 @@ case OP_Rewind: { /* jump */ } pC->nullRow = (u8)res; assert( pOp->p2>0 && pOp->p2<p->nOp ); + VdbeBranchTaken(res!=0,2); if( res ){ pc = pOp->p2 - 1; } break; } -/* Opcode: Next P1 P2 * * P5 +/* Opcode: Next P1 P2 P3 P4 P5 ** ** Advance cursor P1 so that it points to the next key/data pair in its ** table or index. If there are no more key/value pairs then fall through @@ -70905,6 +71555,11 @@ case OP_Rewind: { /* jump */ ** The P1 cursor must be for a real table, not a pseudo-table. P1 must have ** been opened prior to this opcode or the program will segfault. ** +** The P3 value is a hint to the btree implementation. If P3==1, that +** means P1 is an SQL index and that this instruction could have been +** omitted if that index had been unique. P3 is usually 0. P3 is +** always either 0 or 1. +** ** P4 is always of type P4_ADVANCE. The function pointer points to ** sqlite3BtreeNext(). ** @@ -70913,12 +71568,12 @@ case OP_Rewind: { /* jump */ ** ** See also: Prev, NextIfOpen */ -/* Opcode: NextIfOpen P1 P2 * * P5 +/* Opcode: NextIfOpen P1 P2 P3 P4 P5 ** ** This opcode works just like OP_Next except that if cursor P1 is not ** open it behaves a no-op. */ -/* Opcode: Prev P1 P2 * * P5 +/* Opcode: Prev P1 P2 P3 P4 P5 ** ** Back up cursor P1 so that it points to the previous key/data pair in its ** table or index. If there is no previous key/value pairs then fall through @@ -70928,13 +71583,18 @@ case OP_Rewind: { /* jump */ ** The P1 cursor must be for a real table, not a pseudo-table. If P1 is ** not open then the behavior is undefined. ** +** The P3 value is a hint to the btree implementation. If P3==1, that +** means P1 is an SQL index and that this instruction could have been +** omitted if that index had been unique. P3 is usually 0. P3 is +** always either 0 or 1. +** ** P4 is always of type P4_ADVANCE. The function pointer points to ** sqlite3BtreePrevious(). ** ** If P5 is positive and the jump is taken, then event counter ** number P5-1 in the prepared statement is incremented. */ -/* Opcode: PrevIfOpen P1 P2 * * P5 +/* Opcode: PrevIfOpen P1 P2 P3 P4 P5 ** ** This opcode works just like OP_Prev except that if cursor P1 is not ** open it behaves a no-op. @@ -70956,9 +71616,12 @@ case OP_Next: /* jump */ assert( pOp->p1>=0 && pOp->p1<p->nCursor ); assert( pOp->p5<ArraySize(p->aCounter) ); pC = p->apCsr[pOp->p1]; + res = pOp->p3; assert( pC!=0 ); assert( pC->deferredMoveto==0 ); assert( pC->pCursor ); + assert( res==0 || (res==1 && pC->isTable==0) ); + testcase( res==1 ); assert( pOp->opcode!=OP_Next || pOp->p4.xAdvance==sqlite3BtreeNext ); assert( pOp->opcode!=OP_Prev || pOp->p4.xAdvance==sqlite3BtreePrevious ); assert( pOp->opcode!=OP_NextIfOpen || pOp->p4.xAdvance==sqlite3BtreeNext ); @@ -70966,6 +71629,7 @@ case OP_Next: /* jump */ rc = pOp->p4.xAdvance(pC->pCursor, &res); next_tail: pC->cacheStatus = CACHE_STALE; + VdbeBranchTaken(res==0,2); if( res==0 ){ pC->nullRow = 0; pc = pOp->p2 - 1; @@ -70990,6 +71654,14 @@ next_tail: ** P3 is a flag that provides a hint to the b-tree layer that this ** insert is likely to be an append. ** +** If P5 has the OPFLAG_NCHANGE bit set, then the change counter is +** incremented by this instruction. If the OPFLAG_NCHANGE bit is clear, +** then the change counter is unchanged. +** +** If P5 has the OPFLAG_USESEEKRESULT bit set, then the cursor must have +** just done a seek to the spot where the new entry is to be inserted. +** This flag avoids doing an extra seek. +** ** This instruction only works for indices. The equivalent instruction ** for tables is OP_Insert. */ @@ -71050,7 +71722,7 @@ case OP_IdxDelete: { assert( pOp->p5==0 ); r.pKeyInfo = pC->pKeyInfo; r.nField = (u16)pOp->p3; - r.flags = UNPACKED_PREFIX_MATCH; + r.default_rc = 0; r.aMem = &aMem[pOp->p2]; #ifdef SQLITE_DEBUG { int i; for(i=0; i<r.nField; i++) assert( memIsValid(&r.aMem[i]) ); } @@ -71104,32 +71776,50 @@ case OP_IdxRowid: { /* out2-prerelease */ ** Synopsis: key=r[P3@P4] ** ** The P4 register values beginning with P3 form an unpacked index -** key that omits the ROWID. Compare this key value against the index -** that P1 is currently pointing to, ignoring the ROWID on the P1 index. +** key that omits the PRIMARY KEY. Compare this key value against the index +** that P1 is currently pointing to, ignoring the PRIMARY KEY or ROWID +** fields at the end. ** ** If the P1 index entry is greater than or equal to the key value ** then jump to P2. Otherwise fall through to the next instruction. +*/ +/* Opcode: IdxGT P1 P2 P3 P4 P5 +** Synopsis: key=r[P3@P4] ** -** If P5 is non-zero then the key value is increased by an epsilon -** prior to the comparison. This make the opcode work like IdxGT except -** that if the key from register P3 is a prefix of the key in the cursor, -** the result is false whereas it would be true with IdxGT. +** The P4 register values beginning with P3 form an unpacked index +** key that omits the PRIMARY KEY. Compare this key value against the index +** that P1 is currently pointing to, ignoring the PRIMARY KEY or ROWID +** fields at the end. +** +** If the P1 index entry is greater than the key value +** then jump to P2. Otherwise fall through to the next instruction. */ /* Opcode: IdxLT P1 P2 P3 P4 P5 ** Synopsis: key=r[P3@P4] ** ** The P4 register values beginning with P3 form an unpacked index -** key that omits the ROWID. Compare this key value against the index -** that P1 is currently pointing to, ignoring the ROWID on the P1 index. +** key that omits the PRIMARY KEY or ROWID. Compare this key value against +** the index that P1 is currently pointing to, ignoring the PRIMARY KEY or +** ROWID on the P1 index. ** ** If the P1 index entry is less than the key value then jump to P2. ** Otherwise fall through to the next instruction. +*/ +/* Opcode: IdxLE P1 P2 P3 P4 P5 +** Synopsis: key=r[P3@P4] +** +** The P4 register values beginning with P3 form an unpacked index +** key that omits the PRIMARY KEY or ROWID. Compare this key value against +** the index that P1 is currently pointing to, ignoring the PRIMARY KEY or +** ROWID on the P1 index. ** -** If P5 is non-zero then the key value is increased by an epsilon prior -** to the comparison. This makes the opcode work like IdxLE. +** If the P1 index entry is less than or equal to the key value then jump +** to P2. Otherwise fall through to the next instruction. */ +case OP_IdxLE: /* jump */ +case OP_IdxGT: /* jump */ case OP_IdxLT: /* jump */ -case OP_IdxGE: { /* jump */ +case OP_IdxGE: { /* jump */ VdbeCursor *pC; int res; UnpackedRecord r; @@ -71144,10 +71834,12 @@ case OP_IdxGE: { /* jump */ assert( pOp->p4type==P4_INT32 ); r.pKeyInfo = pC->pKeyInfo; r.nField = (u16)pOp->p4.i; - if( pOp->p5 ){ - r.flags = UNPACKED_INCRKEY | UNPACKED_PREFIX_MATCH; + if( pOp->opcode<OP_IdxLT ){ + assert( pOp->opcode==OP_IdxLE || pOp->opcode==OP_IdxGT ); + r.default_rc = -1; }else{ - r.flags = UNPACKED_PREFIX_MATCH; + assert( pOp->opcode==OP_IdxGE || pOp->opcode==OP_IdxLT ); + r.default_rc = 0; } r.aMem = &aMem[pOp->p3]; #ifdef SQLITE_DEBUG @@ -71155,12 +71847,15 @@ case OP_IdxGE: { /* jump */ #endif res = 0; /* Not needed. Only used to silence a warning. */ rc = sqlite3VdbeIdxKeyCompare(pC, &r, &res); - if( pOp->opcode==OP_IdxLT ){ + assert( (OP_IdxLE&1)==(OP_IdxLT&1) && (OP_IdxGE&1)==(OP_IdxGT&1) ); + if( (pOp->opcode&1)==(OP_IdxLT&1) ){ + assert( pOp->opcode==OP_IdxLE || pOp->opcode==OP_IdxLT ); res = -res; }else{ - assert( pOp->opcode==OP_IdxGE ); + assert( pOp->opcode==OP_IdxGE || pOp->opcode==OP_IdxGT ); res++; } + VdbeBranchTaken(res>0,2); if( res>0 ){ pc = pOp->p2 - 1 ; } @@ -71253,7 +71948,6 @@ case OP_Clear: { nChange = 0; assert( p->readOnly==0 ); - assert( pOp->p1!=1 ); assert( (p->btreeMask & (((yDbMask)1)<<pOp->p2))!=0 ); rc = sqlite3BtreeClearTable( db->aDb[pOp->p2].pBt, pOp->p1, (pOp->p3 ? &nChange : 0) @@ -71522,9 +72216,11 @@ case OP_RowSetRead: { /* jump, in1, out3 */ /* The boolean index is empty */ sqlite3VdbeMemSetNull(pIn1); pc = pOp->p2 - 1; + VdbeBranchTaken(1,2); }else{ /* A value was pulled from the index */ sqlite3VdbeMemSetInt64(&aMem[pOp->p3], val); + VdbeBranchTaken(0,2); } goto check_for_interrupt; } @@ -71576,6 +72272,7 @@ case OP_RowSetTest: { /* jump, in1, in3 */ exists = sqlite3RowSetTest(pIn1->u.pRowSet, (u8)(iSet>=0 ? iSet & 0xf : 0xff), pIn3->u.i); + VdbeBranchTaken(exists!=0,2); if( exists ){ pc = pOp->p2 - 1; break; @@ -71590,7 +72287,7 @@ case OP_RowSetTest: { /* jump, in1, in3 */ #ifndef SQLITE_OMIT_TRIGGER -/* Opcode: Program P1 P2 P3 P4 * +/* Opcode: Program P1 P2 P3 P4 P5 ** ** Execute the trigger program passed as P4 (type P4_SUBPROGRAM). ** @@ -71602,6 +72299,8 @@ case OP_RowSetTest: { /* jump, in1, in3 */ ** memory required by the sub-vdbe at runtime. ** ** P4 is a pointer to the VM containing the trigger program. +** +** If P5 is non-zero, then recursive program invocation is enabled. */ case OP_Program: { /* jump */ int nMem; /* Number of memory registers for sub-program */ @@ -71679,7 +72378,7 @@ case OP_Program: { /* jump */ pEnd = &VdbeFrameMem(pFrame)[pFrame->nChildMem]; for(pMem=VdbeFrameMem(pFrame); pMem!=pEnd; pMem++){ - pMem->flags = MEM_Invalid; + pMem->flags = MEM_Undefined; pMem->db = db; } }else{ @@ -71766,8 +72465,10 @@ case OP_FkCounter: { */ case OP_FkIfZero: { /* jump */ if( pOp->p1 ){ + VdbeBranchTaken(db->nDeferredCons==0 && db->nDeferredImmCons==0, 2); if( db->nDeferredCons==0 && db->nDeferredImmCons==0 ) pc = pOp->p2-1; }else{ + VdbeBranchTaken(p->nFkConstraint==0 && db->nDeferredImmCons==0, 2); if( p->nFkConstraint==0 && db->nDeferredImmCons==0 ) pc = pOp->p2-1; } break; @@ -71816,6 +72517,7 @@ case OP_MemMax: { /* in2 */ case OP_IfPos: { /* jump, in1 */ pIn1 = &aMem[pOp->p1]; assert( pIn1->flags&MEM_Int ); + VdbeBranchTaken( pIn1->u.i>0, 2); if( pIn1->u.i>0 ){ pc = pOp->p2 - 1; } @@ -71833,6 +72535,7 @@ case OP_IfPos: { /* jump, in1 */ case OP_IfNeg: { /* jump, in1 */ pIn1 = &aMem[pOp->p1]; assert( pIn1->flags&MEM_Int ); + VdbeBranchTaken(pIn1->u.i<0, 2); if( pIn1->u.i<0 ){ pc = pOp->p2 - 1; } @@ -71852,6 +72555,7 @@ case OP_IfZero: { /* jump, in1 */ pIn1 = &aMem[pOp->p1]; assert( pIn1->flags&MEM_Int ); pIn1->u.i += pOp->p3; + VdbeBranchTaken(pIn1->u.i==0, 2); if( pIn1->u.i==0 ){ pc = pOp->p2 - 1; } @@ -71886,7 +72590,6 @@ case OP_AggStep: { assert( memIsValid(pRec) ); apVal[i] = pRec; memAboutToChange(p, pRec); - sqlite3VdbeMemStoreType(pRec); } ctx.pFunc = pOp->p4.pFunc; assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) ); @@ -71989,7 +72692,7 @@ case OP_Checkpoint: { #endif #ifndef SQLITE_OMIT_PRAGMA -/* Opcode: JournalMode P1 P2 P3 * P5 +/* Opcode: JournalMode P1 P2 P3 * * ** ** Change the journal mode of database P1 to P3. P3 must be one of the ** PAGER_JOURNALMODE_XXX values. If changing between the various rollback @@ -72123,6 +72826,7 @@ case OP_IncrVacuum: { /* jump */ assert( p->readOnly==0 ); pBt = db->aDb[pOp->p1].pBt; rc = sqlite3BtreeIncrVacuum(pBt); + VdbeBranchTaken(rc==SQLITE_DONE,2); if( rc==SQLITE_DONE ){ pc = pOp->p2 - 1; rc = SQLITE_OK; @@ -72319,7 +73023,6 @@ case OP_VFilter: { /* jump */ apArg = p->apArg; for(i = 0; i<nArg; i++){ apArg[i] = &pArgc[i+1]; - sqlite3VdbeMemStoreType(apArg[i]); } p->inVtabMethod = 1; @@ -72329,7 +73032,7 @@ case OP_VFilter: { /* jump */ if( rc==SQLITE_OK ){ res = pModule->xEof(pVtabCursor); } - + VdbeBranchTaken(res!=0,2); if( res ){ pc = pOp->p2 - 1; } @@ -72434,7 +73137,7 @@ case OP_VNext: { /* jump */ if( rc==SQLITE_OK ){ res = pModule->xEof(pCur->pVtabCursor); } - + VdbeBranchTaken(!res,2); if( !res ){ /* If there is data, jump to P2 */ pc = pOp->p2 - 1; @@ -72475,7 +73178,7 @@ case OP_VRename: { #endif #ifndef SQLITE_OMIT_VIRTUALTABLE -/* Opcode: VUpdate P1 P2 P3 P4 * +/* Opcode: VUpdate P1 P2 P3 P4 P5 ** Synopsis: data=r[P3@P2] ** ** P4 is a pointer to a virtual table object, an sqlite3_vtab structure. @@ -72498,6 +73201,9 @@ case OP_VRename: { ** P1 is a boolean flag. If it is set to true and the xUpdate call ** is successful, then the value returned by sqlite3_last_insert_rowid() ** is set to the value of the rowid for the row just inserted. +** +** P5 is the error actions (OE_Replace, OE_Fail, OE_Ignore, etc) to +** apply in the case of a constraint failure on an insert or update. */ case OP_VUpdate: { sqlite3_vtab *pVtab; @@ -72523,7 +73229,6 @@ case OP_VUpdate: { for(i=0; i<nArg; i++){ assert( memIsValid(pX) ); memAboutToChange(p, pX); - sqlite3VdbeMemStoreType(pX); apArg[i] = pX; pX++; } @@ -72586,16 +73291,26 @@ case OP_MaxPgcnt: { /* out2-prerelease */ #endif -#ifndef SQLITE_OMIT_TRACE -/* Opcode: Trace * * * P4 * +/* Opcode: Init * P2 * P4 * +** Synopsis: Start at P2 +** +** Programs contain a single instance of this opcode as the very first +** opcode. ** ** If tracing is enabled (by the sqlite3_trace()) interface, then ** the UTF-8 string contained in P4 is emitted on the trace callback. +** Or if P4 is blank, use the string returned by sqlite3_sql(). +** +** If P2 is not zero, jump to instruction P2. */ -case OP_Trace: { +case OP_Init: { /* jump */ char *zTrace; char *z; + if( pOp->p2 ){ + pc = pOp->p2 - 1; + } +#ifndef SQLITE_OMIT_TRACE if( db->xTrace && !p->doingRerun && (zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql))!=0 @@ -72621,9 +73336,9 @@ case OP_Trace: { sqlite3DebugPrintf("SQL-trace: %s\n", zTrace); } #endif /* SQLITE_DEBUG */ +#endif /* SQLITE_OMIT_TRACE */ break; } -#endif /* Opcode: Noop * * * * * @@ -72655,10 +73370,6 @@ default: { /* This is really OP_Noop and OP_Explain */ u64 elapsed = sqlite3Hwtime() - start; pOp->cycles += elapsed; pOp->cnt++; -#if 0 - fprintf(stdout, "%10llu ", elapsed); - sqlite3VdbePrintOp(stdout, origPc, &aOp[origPc]); -#endif } #endif @@ -72884,22 +73595,20 @@ SQLITE_API int sqlite3_blob_open( ** which closes the b-tree cursor and (possibly) commits the ** transaction. */ + static const int iLn = VDBE_OFFSET_LINENO(4); static const VdbeOpList openBlob[] = { - {OP_Transaction, 0, 0, 0}, /* 0: Start a transaction */ - {OP_VerifyCookie, 0, 0, 0}, /* 1: Check the schema cookie */ - {OP_TableLock, 0, 0, 0}, /* 2: Acquire a read or write lock */ - + /* {OP_Transaction, 0, 0, 0}, // 0: Inserted separately */ + {OP_TableLock, 0, 0, 0}, /* 1: Acquire a read or write lock */ /* One of the following two instructions is replaced by an OP_Noop. */ - {OP_OpenRead, 0, 0, 0}, /* 3: Open cursor 0 for reading */ - {OP_OpenWrite, 0, 0, 0}, /* 4: Open cursor 0 for read/write */ - - {OP_Variable, 1, 1, 1}, /* 5: Push the rowid to the stack */ - {OP_NotExists, 0, 10, 1}, /* 6: Seek the cursor */ - {OP_Column, 0, 0, 1}, /* 7 */ - {OP_ResultRow, 1, 0, 0}, /* 8 */ - {OP_Goto, 0, 5, 0}, /* 9 */ - {OP_Close, 0, 0, 0}, /* 10 */ - {OP_Halt, 0, 0, 0}, /* 11 */ + {OP_OpenRead, 0, 0, 0}, /* 2: Open cursor 0 for reading */ + {OP_OpenWrite, 0, 0, 0}, /* 3: Open cursor 0 for read/write */ + {OP_Variable, 1, 1, 1}, /* 4: Push the rowid to the stack */ + {OP_NotExists, 0, 10, 1}, /* 5: Seek the cursor */ + {OP_Column, 0, 0, 1}, /* 6 */ + {OP_ResultRow, 1, 0, 0}, /* 7 */ + {OP_Goto, 0, 4, 0}, /* 8 */ + {OP_Close, 0, 0, 0}, /* 9 */ + {OP_Halt, 0, 0, 0}, /* 10 */ }; int rc = SQLITE_OK; @@ -73012,36 +73721,31 @@ SQLITE_API int sqlite3_blob_open( Vdbe *v = (Vdbe *)pBlob->pStmt; int iDb = sqlite3SchemaToIndex(db, pTab->pSchema); - sqlite3VdbeAddOpList(v, sizeof(openBlob)/sizeof(VdbeOpList), openBlob); - - /* Configure the OP_Transaction */ - sqlite3VdbeChangeP1(v, 0, iDb); - sqlite3VdbeChangeP2(v, 0, flags); - - /* Configure the OP_VerifyCookie */ - sqlite3VdbeChangeP1(v, 1, iDb); - sqlite3VdbeChangeP2(v, 1, pTab->pSchema->schema_cookie); - sqlite3VdbeChangeP3(v, 1, pTab->pSchema->iGeneration); + sqlite3VdbeAddOp4Int(v, OP_Transaction, iDb, flags, + pTab->pSchema->schema_cookie, + pTab->pSchema->iGeneration); + sqlite3VdbeChangeP5(v, 1); + sqlite3VdbeAddOpList(v, ArraySize(openBlob), openBlob, iLn); /* Make sure a mutex is held on the table to be accessed */ sqlite3VdbeUsesBtree(v, iDb); /* Configure the OP_TableLock instruction */ #ifdef SQLITE_OMIT_SHARED_CACHE - sqlite3VdbeChangeToNoop(v, 2); + sqlite3VdbeChangeToNoop(v, 1); #else - sqlite3VdbeChangeP1(v, 2, iDb); - sqlite3VdbeChangeP2(v, 2, pTab->tnum); - sqlite3VdbeChangeP3(v, 2, flags); - sqlite3VdbeChangeP4(v, 2, pTab->zName, P4_TRANSIENT); + sqlite3VdbeChangeP1(v, 1, iDb); + sqlite3VdbeChangeP2(v, 1, pTab->tnum); + sqlite3VdbeChangeP3(v, 1, flags); + sqlite3VdbeChangeP4(v, 1, pTab->zName, P4_TRANSIENT); #endif /* Remove either the OP_OpenWrite or OpenRead. Set the P2 ** parameter of the other to pTab->tnum. */ - sqlite3VdbeChangeToNoop(v, 4 - flags); - sqlite3VdbeChangeP2(v, 3 + flags, pTab->tnum); - sqlite3VdbeChangeP3(v, 3 + flags, iDb); + sqlite3VdbeChangeToNoop(v, 3 - flags); + sqlite3VdbeChangeP2(v, 2 + flags, pTab->tnum); + sqlite3VdbeChangeP3(v, 2 + flags, iDb); /* Configure the number of columns. Configure the cursor to ** think that the table has one more column than it really @@ -73050,8 +73754,8 @@ SQLITE_API int sqlite3_blob_open( ** we can invoke OP_Column to fill in the vdbe cursors type ** and offset cache without causing any IO. */ - sqlite3VdbeChangeP4(v, 3+flags, SQLITE_INT_TO_PTR(pTab->nCol+1),P4_INT32); - sqlite3VdbeChangeP2(v, 7, pTab->nCol); + sqlite3VdbeChangeP4(v, 2+flags, SQLITE_INT_TO_PTR(pTab->nCol+1),P4_INT32); + sqlite3VdbeChangeP2(v, 6, pTab->nCol); if( !db->mallocFailed ){ pParse->nVar = 1; pParse->nMem = 1; @@ -73636,10 +74340,10 @@ static void vdbeSorterCompare( return; } } - r2->flags |= UNPACKED_PREFIX_MATCH; + assert( r2->default_rc==0 ); } - *pRes = sqlite3VdbeRecordCompare(nKey1, pKey1, r2); + *pRes = sqlite3VdbeRecordCompare(nKey1, pKey1, r2, 0); } /* @@ -75276,6 +75980,8 @@ static int lookupName( }else if( op!=TK_INSERT && sqlite3StrICmp("old",zTab)==0 ){ pExpr->iTable = 0; pTab = pParse->pTriggerTab; + }else{ + pTab = 0; } if( pTab ){ @@ -75319,8 +76025,8 @@ static int lookupName( /* ** Perhaps the name is a reference to the ROWID */ - assert( pTab!=0 || cntTab==0 ); - if( cnt==0 && cntTab==1 && sqlite3IsRowid(zCol) && HasRowid(pTab) ){ + if( cnt==0 && cntTab==1 && pMatch && sqlite3IsRowid(zCol) + && HasRowid(pMatch->pTab) ){ cnt = 1; pExpr->iColumn = -1; /* IMP: R-44911-55124 */ pExpr->affinity = SQLITE_AFF_INTEGER; @@ -77451,7 +78157,6 @@ SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3 *db, Select *p, int flags){ pNew->iLimit = 0; pNew->iOffset = 0; pNew->selFlags = p->selFlags & ~SF_UsesEphemeral; - pNew->pRightmost = 0; pNew->addrOpenEphm[0] = -1; pNew->addrOpenEphm[1] = -1; pNew->addrOpenEphm[2] = -1; @@ -77762,24 +78467,6 @@ SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr *p){ } /* -** Generate an OP_IsNull instruction that tests register iReg and jumps -** to location iDest if the value in iReg is NULL. The value in iReg -** was computed by pExpr. If we can look at pExpr at compile-time and -** determine that it can never generate a NULL, then the OP_IsNull operation -** can be omitted. -*/ -SQLITE_PRIVATE void sqlite3ExprCodeIsNullJump( - Vdbe *v, /* The VDBE under construction */ - const Expr *pExpr, /* Only generate OP_IsNull if this expr can be NULL */ - int iReg, /* Test the value in this register for NULL */ - int iDest /* Jump here if the value is null */ -){ - if( sqlite3ExprCanBeNull(pExpr) ){ - sqlite3VdbeAddOp2(v, OP_IsNull, iReg, iDest); - } -} - -/* ** Return TRUE if the given expression is a constant which would be ** unchanged by OP_Affinity with the affinity given in the second ** argument. @@ -77975,7 +78662,7 @@ SQLITE_PRIVATE int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){ pExpr = p->pEList->a[0].pExpr; iCol = (i16)pExpr->iColumn; - /* Code an OP_VerifyCookie and OP_TableLock for <table>. */ + /* Code an OP_Transaction and OP_TableLock for <table>. */ iDb = sqlite3SchemaToIndex(db, pTab->pSchema); sqlite3CodeVerifySchema(pParse, iDb); sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); @@ -77986,9 +78673,8 @@ SQLITE_PRIVATE int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){ */ assert(v); if( iCol<0 ){ - int iAddr; - - iAddr = sqlite3CodeOnce(pParse); + int iAddr = sqlite3CodeOnce(pParse); + VdbeCoverage(v); sqlite3OpenTable(pParse, iTab, iDb, pTab, OP_OpenRead); eType = IN_INDEX_ROWID; @@ -78013,18 +78699,18 @@ SQLITE_PRIVATE int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){ && sqlite3FindCollSeq(db, ENC(db), pIdx->azColl[0], 0)==pReq && (!mustBeUnique || (pIdx->nKeyCol==1 && pIdx->onError!=OE_None)) ){ - int iAddr = sqlite3CodeOnce(pParse); + int iAddr = sqlite3CodeOnce(pParse); VdbeCoverage(v); sqlite3VdbeAddOp3(v, OP_OpenRead, iTab, pIdx->tnum, iDb); sqlite3VdbeSetP4KeyInfo(pParse, pIdx); VdbeComment((v, "%s", pIdx->zName)); assert( IN_INDEX_INDEX_DESC == IN_INDEX_INDEX_ASC+1 ); eType = IN_INDEX_INDEX_ASC + pIdx->aSortOrder[0]; - sqlite3VdbeJumpHere(v, iAddr); if( prNotFound && !pTab->aCol[iCol].notNull ){ *prNotFound = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_Null, 0, *prNotFound); } + sqlite3VdbeJumpHere(v, iAddr); } } } @@ -78113,7 +78799,7 @@ SQLITE_PRIVATE int sqlite3CodeSubselect( ** save the results, and reuse the same result on subsequent invocations. */ if( !ExprHasProperty(pExpr, EP_VarSelect) ){ - testAddr = sqlite3CodeOnce(pParse); + testAddr = sqlite3CodeOnce(pParse); VdbeCoverage(v); } #ifndef SQLITE_OMIT_EXPLAIN @@ -78154,7 +78840,6 @@ SQLITE_PRIVATE int sqlite3CodeSubselect( */ pExpr->iTable = pParse->nTab++; addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pExpr->iTable, !isRowid); - if( rMayHaveNull==0 ) sqlite3VdbeChangeP5(v, BTREE_UNORDERED); pKeyInfo = isRowid ? 0 : sqlite3KeyInfoAlloc(pParse->db, 1, 1); if( ExprHasProperty(pExpr, EP_xIsSelect) ){ @@ -78230,6 +78915,7 @@ SQLITE_PRIVATE int sqlite3CodeSubselect( if( isRowid ){ sqlite3VdbeAddOp2(v, OP_MustBeInt, r3, sqlite3VdbeCurrentAddr(v)+2); + VdbeCoverage(v); sqlite3VdbeAddOp3(v, OP_Insert, pExpr->iTable, r2, r3); }else{ sqlite3VdbeAddOp4(v, OP_MakeRecord, r3, 1, r2, &affinity, 1); @@ -78353,10 +79039,11 @@ static void sqlite3ExprCodeIN( if( destIfNull==destIfFalse ){ /* Shortcut for the common case where the false and NULL outcomes are ** the same. */ - sqlite3VdbeAddOp2(v, OP_IsNull, r1, destIfNull); + sqlite3VdbeAddOp2(v, OP_IsNull, r1, destIfNull); VdbeCoverage(v); }else{ - int addr1 = sqlite3VdbeAddOp1(v, OP_NotNull, r1); + int addr1 = sqlite3VdbeAddOp1(v, OP_NotNull, r1); VdbeCoverage(v); sqlite3VdbeAddOp2(v, OP_Rewind, pExpr->iTable, destIfFalse); + VdbeCoverage(v); sqlite3VdbeAddOp2(v, OP_Goto, 0, destIfNull); sqlite3VdbeJumpHere(v, addr1); } @@ -78364,8 +79051,9 @@ static void sqlite3ExprCodeIN( if( eType==IN_INDEX_ROWID ){ /* In this case, the RHS is the ROWID of table b-tree */ - sqlite3VdbeAddOp2(v, OP_MustBeInt, r1, destIfFalse); + sqlite3VdbeAddOp2(v, OP_MustBeInt, r1, destIfFalse); VdbeCoverage(v); sqlite3VdbeAddOp3(v, OP_NotExists, pExpr->iTable, destIfFalse, r1); + VdbeCoverage(v); }else{ /* In this case, the RHS is an index b-tree. */ @@ -78386,19 +79074,20 @@ static void sqlite3ExprCodeIN( ** for this particular IN operator. */ sqlite3VdbeAddOp4Int(v, OP_NotFound, pExpr->iTable, destIfFalse, r1, 1); - + VdbeCoverage(v); }else{ /* In this branch, the RHS of the IN might contain a NULL and ** the presence of a NULL on the RHS makes a difference in the ** outcome. */ - int j1, j2, j3; + int j1, j2; /* First check to see if the LHS is contained in the RHS. If so, ** then the presence of NULLs in the RHS does not matter, so jump ** over all of the code that follows. */ j1 = sqlite3VdbeAddOp4Int(v, OP_Found, pExpr->iTable, 0, r1, 1); + VdbeCoverage(v); /* Here we begin generating code that runs if the LHS is not ** contained within the RHS. Generate additional code that @@ -78406,18 +79095,15 @@ static void sqlite3ExprCodeIN( ** jump to destIfNull. If there are no NULLs in the RHS then ** jump to destIfFalse. */ - j2 = sqlite3VdbeAddOp1(v, OP_NotNull, rRhsHasNull); - j3 = sqlite3VdbeAddOp4Int(v, OP_Found, pExpr->iTable, 0, rRhsHasNull, 1); - sqlite3VdbeAddOp2(v, OP_Integer, -1, rRhsHasNull); - sqlite3VdbeJumpHere(v, j3); - sqlite3VdbeAddOp2(v, OP_AddImm, rRhsHasNull, 1); - sqlite3VdbeJumpHere(v, j2); - - /* Jump to the appropriate target depending on whether or not - ** the RHS contains a NULL - */ - sqlite3VdbeAddOp2(v, OP_If, rRhsHasNull, destIfNull); + sqlite3VdbeAddOp2(v, OP_If, rRhsHasNull, destIfNull); VdbeCoverage(v); + sqlite3VdbeAddOp2(v, OP_IfNot, rRhsHasNull, destIfFalse); VdbeCoverage(v); + j2 = sqlite3VdbeAddOp4Int(v, OP_Found, pExpr->iTable, 0, rRhsHasNull, 1); + VdbeCoverage(v); + sqlite3VdbeAddOp2(v, OP_Integer, 0, rRhsHasNull); sqlite3VdbeAddOp2(v, OP_Goto, 0, destIfFalse); + sqlite3VdbeJumpHere(v, j2); + sqlite3VdbeAddOp2(v, OP_Integer, 1, rRhsHasNull); + sqlite3VdbeAddOp2(v, OP_Goto, 0, destIfNull); /* The OP_Found at the top of this branch jumps here when true, ** causing the overall IN expression evaluation to fall through. @@ -78938,22 +79624,16 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target) case TK_GE: case TK_NE: case TK_EQ: { - assert( TK_LT==OP_Lt ); - assert( TK_LE==OP_Le ); - assert( TK_GT==OP_Gt ); - assert( TK_GE==OP_Ge ); - assert( TK_EQ==OP_Eq ); - assert( TK_NE==OP_Ne ); - testcase( op==TK_LT ); - testcase( op==TK_LE ); - testcase( op==TK_GT ); - testcase( op==TK_GE ); - testcase( op==TK_EQ ); - testcase( op==TK_NE ); r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, ®Free2); codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, r1, r2, inReg, SQLITE_STOREP2); + assert(TK_LT==OP_Lt); testcase(op==OP_Lt); VdbeCoverageIf(v,op==OP_Lt); + assert(TK_LE==OP_Le); testcase(op==OP_Le); VdbeCoverageIf(v,op==OP_Le); + assert(TK_GT==OP_Gt); testcase(op==OP_Gt); VdbeCoverageIf(v,op==OP_Gt); + assert(TK_GE==OP_Ge); testcase(op==OP_Ge); VdbeCoverageIf(v,op==OP_Ge); + assert(TK_EQ==OP_Eq); testcase(op==OP_Eq); VdbeCoverageIf(v,op==OP_Eq); + assert(TK_NE==OP_Ne); testcase(op==OP_Ne); VdbeCoverageIf(v,op==OP_Ne); testcase( regFree1==0 ); testcase( regFree2==0 ); break; @@ -78967,6 +79647,8 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target) op = (op==TK_IS) ? TK_EQ : TK_NE; codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, r1, r2, inReg, SQLITE_STOREP2 | SQLITE_NULLEQ); + VdbeCoverageIf(v, op==TK_EQ); + VdbeCoverageIf(v, op==TK_NE); testcase( regFree1==0 ); testcase( regFree2==0 ); break; @@ -78983,28 +79665,17 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target) case TK_LSHIFT: case TK_RSHIFT: case TK_CONCAT: { - assert( TK_AND==OP_And ); - assert( TK_OR==OP_Or ); - assert( TK_PLUS==OP_Add ); - assert( TK_MINUS==OP_Subtract ); - assert( TK_REM==OP_Remainder ); - assert( TK_BITAND==OP_BitAnd ); - assert( TK_BITOR==OP_BitOr ); - assert( TK_SLASH==OP_Divide ); - assert( TK_LSHIFT==OP_ShiftLeft ); - assert( TK_RSHIFT==OP_ShiftRight ); - assert( TK_CONCAT==OP_Concat ); - testcase( op==TK_AND ); - testcase( op==TK_OR ); - testcase( op==TK_PLUS ); - testcase( op==TK_MINUS ); - testcase( op==TK_REM ); - testcase( op==TK_BITAND ); - testcase( op==TK_BITOR ); - testcase( op==TK_SLASH ); - testcase( op==TK_LSHIFT ); - testcase( op==TK_RSHIFT ); - testcase( op==TK_CONCAT ); + assert( TK_AND==OP_And ); testcase( op==TK_AND ); + assert( TK_OR==OP_Or ); testcase( op==TK_OR ); + assert( TK_PLUS==OP_Add ); testcase( op==TK_PLUS ); + assert( TK_MINUS==OP_Subtract ); testcase( op==TK_MINUS ); + assert( TK_REM==OP_Remainder ); testcase( op==TK_REM ); + assert( TK_BITAND==OP_BitAnd ); testcase( op==TK_BITAND ); + assert( TK_BITOR==OP_BitOr ); testcase( op==TK_BITOR ); + assert( TK_SLASH==OP_Divide ); testcase( op==TK_SLASH ); + assert( TK_LSHIFT==OP_ShiftLeft ); testcase( op==TK_LSHIFT ); + assert( TK_RSHIFT==OP_ShiftRight ); testcase( op==TK_RSHIFT ); + assert( TK_CONCAT==OP_Concat ); testcase( op==TK_CONCAT ); r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, ®Free2); sqlite3VdbeAddOp3(v, op, r2, r1, target); @@ -79036,10 +79707,8 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target) } case TK_BITNOT: case TK_NOT: { - assert( TK_BITNOT==OP_BitNot ); - assert( TK_NOT==OP_Not ); - testcase( op==TK_BITNOT ); - testcase( op==TK_NOT ); + assert( TK_BITNOT==OP_BitNot ); testcase( op==TK_BITNOT ); + assert( TK_NOT==OP_Not ); testcase( op==TK_NOT ); r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); testcase( regFree1==0 ); inReg = target; @@ -79049,14 +79718,14 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target) case TK_ISNULL: case TK_NOTNULL: { int addr; - assert( TK_ISNULL==OP_IsNull ); - assert( TK_NOTNULL==OP_NotNull ); - testcase( op==TK_ISNULL ); - testcase( op==TK_NOTNULL ); + assert( TK_ISNULL==OP_IsNull ); testcase( op==TK_ISNULL ); + assert( TK_NOTNULL==OP_NotNull ); testcase( op==TK_NOTNULL ); sqlite3VdbeAddOp2(v, OP_Integer, 1, target); r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); testcase( regFree1==0 ); addr = sqlite3VdbeAddOp1(v, op, r1); + VdbeCoverageIf(v, op==TK_ISNULL); + VdbeCoverageIf(v, op==TK_NOTNULL); sqlite3VdbeAddOp2(v, OP_AddImm, target, -1); sqlite3VdbeJumpHere(v, addr); break; @@ -79108,6 +79777,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target) sqlite3ExprCode(pParse, pFarg->a[0].pExpr, target); for(i=1; i<nFarg; i++){ sqlite3VdbeAddOp2(v, OP_NotNull, target, endCoalesce); + VdbeCoverage(v); sqlite3ExprCacheRemove(pParse, target, 1); sqlite3ExprCachePush(pParse); sqlite3ExprCode(pParse, pFarg->a[i].pExpr, target); @@ -79245,13 +79915,14 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target) r3 = sqlite3GetTempReg(pParse); r4 = sqlite3GetTempReg(pParse); codeCompare(pParse, pLeft, pRight, OP_Ge, - r1, r2, r3, SQLITE_STOREP2); + r1, r2, r3, SQLITE_STOREP2); VdbeCoverage(v); pLItem++; pRight = pLItem->pExpr; sqlite3ReleaseTempReg(pParse, regFree2); r2 = sqlite3ExprCodeTemp(pParse, pRight, ®Free2); testcase( regFree2==0 ); codeCompare(pParse, pLeft, pRight, OP_Le, r1, r2, r4, SQLITE_STOREP2); + VdbeCoverage(v); sqlite3VdbeAddOp3(v, OP_And, r3, r4, target); sqlite3ReleaseTempReg(pParse, r3); sqlite3ReleaseTempReg(pParse, r4); @@ -79418,6 +80089,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target) if( pExpr->affinity==OE_Ignore ){ sqlite3VdbeAddOp4( v, OP_Halt, SQLITE_OK, OE_Ignore, 0, pExpr->u.zToken,0); + VdbeCoverage(v); }else{ sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_TRIGGER, pExpr->affinity, pExpr->u.zToken, 0, 0); @@ -79505,7 +80177,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeTemp(Parse *pParse, Expr *pExpr, int *pReg){ ** results in register target. The results are guaranteed to appear ** in register target. */ -SQLITE_PRIVATE int sqlite3ExprCode(Parse *pParse, Expr *pExpr, int target){ +SQLITE_PRIVATE void sqlite3ExprCode(Parse *pParse, Expr *pExpr, int target){ int inReg; assert( target>0 && target<=pParse->nMem ); @@ -79518,7 +80190,20 @@ SQLITE_PRIVATE int sqlite3ExprCode(Parse *pParse, Expr *pExpr, int target){ sqlite3VdbeAddOp2(pParse->pVdbe, OP_SCopy, inReg, target); } } - return target; +} + +/* +** Generate code that will evaluate expression pExpr and store the +** results in register target. The results are guaranteed to appear +** in register target. If the expression is constant, then this routine +** might choose to code the expression at initialization time. +*/ +SQLITE_PRIVATE void sqlite3ExprCodeFactorable(Parse *pParse, Expr *pExpr, int target){ + if( pParse->okConstFactor && sqlite3ExprIsConstant(pExpr) ){ + sqlite3ExprCodeAtInit(pParse, pExpr, target, 0); + }else{ + sqlite3ExprCode(pParse, pExpr, target); + } } /* @@ -79533,25 +80218,16 @@ SQLITE_PRIVATE int sqlite3ExprCode(Parse *pParse, Expr *pExpr, int target){ ** times. They are evaluated once and the results of the expression ** are reused. */ -SQLITE_PRIVATE int sqlite3ExprCodeAndCache(Parse *pParse, Expr *pExpr, int target){ +SQLITE_PRIVATE void sqlite3ExprCodeAndCache(Parse *pParse, Expr *pExpr, int target){ Vdbe *v = pParse->pVdbe; - int inReg; - inReg = sqlite3ExprCode(pParse, pExpr, target); + int iMem; + assert( target>0 ); - /* The only place, other than this routine, where expressions can be - ** converted to TK_REGISTER is internal subexpressions in BETWEEN and - ** CASE operators. Neither ever calls this routine. And this routine - ** is never called twice on the same expression. Hence it is impossible - ** for the input to this routine to already be a register. Nevertheless, - ** it seems prudent to keep the ALWAYS() in case the conditions above - ** change with future modifications or enhancements. */ - if( ALWAYS(pExpr->op!=TK_REGISTER) ){ - int iMem; - iMem = ++pParse->nMem; - sqlite3VdbeAddOp2(v, OP_Copy, inReg, iMem); - exprToRegister(pExpr, iMem); - } - return inReg; + assert( pExpr->op!=TK_REGISTER ); + sqlite3ExprCode(pParse, pExpr, target); + iMem = ++pParse->nMem; + sqlite3VdbeAddOp2(v, OP_Copy, target, iMem); + exprToRegister(pExpr, iMem); } #if defined(SQLITE_ENABLE_TREE_EXPLAIN) @@ -79986,23 +80662,17 @@ SQLITE_PRIVATE void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int case TK_GE: case TK_NE: case TK_EQ: { - assert( TK_LT==OP_Lt ); - assert( TK_LE==OP_Le ); - assert( TK_GT==OP_Gt ); - assert( TK_GE==OP_Ge ); - assert( TK_EQ==OP_Eq ); - assert( TK_NE==OP_Ne ); - testcase( op==TK_LT ); - testcase( op==TK_LE ); - testcase( op==TK_GT ); - testcase( op==TK_GE ); - testcase( op==TK_EQ ); - testcase( op==TK_NE ); testcase( jumpIfNull==0 ); r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, ®Free2); codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, r1, r2, dest, jumpIfNull); + assert(TK_LT==OP_Lt); testcase(op==OP_Lt); VdbeCoverageIf(v,op==OP_Lt); + assert(TK_LE==OP_Le); testcase(op==OP_Le); VdbeCoverageIf(v,op==OP_Le); + assert(TK_GT==OP_Gt); testcase(op==OP_Gt); VdbeCoverageIf(v,op==OP_Gt); + assert(TK_GE==OP_Ge); testcase(op==OP_Ge); VdbeCoverageIf(v,op==OP_Ge); + assert(TK_EQ==OP_Eq); testcase(op==OP_Eq); VdbeCoverageIf(v,op==OP_Eq); + assert(TK_NE==OP_Ne); testcase(op==OP_Ne); VdbeCoverageIf(v,op==OP_Ne); testcase( regFree1==0 ); testcase( regFree2==0 ); break; @@ -80016,18 +80686,20 @@ SQLITE_PRIVATE void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int op = (op==TK_IS) ? TK_EQ : TK_NE; codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, r1, r2, dest, SQLITE_NULLEQ); + VdbeCoverageIf(v, op==TK_EQ); + VdbeCoverageIf(v, op==TK_NE); testcase( regFree1==0 ); testcase( regFree2==0 ); break; } case TK_ISNULL: case TK_NOTNULL: { - assert( TK_ISNULL==OP_IsNull ); - assert( TK_NOTNULL==OP_NotNull ); - testcase( op==TK_ISNULL ); - testcase( op==TK_NOTNULL ); + assert( TK_ISNULL==OP_IsNull ); testcase( op==TK_ISNULL ); + assert( TK_NOTNULL==OP_NotNull ); testcase( op==TK_NOTNULL ); r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); sqlite3VdbeAddOp2(v, op, r1, dest); + VdbeCoverageIf(v, op==TK_ISNULL); + VdbeCoverageIf(v, op==TK_NOTNULL); testcase( regFree1==0 ); break; } @@ -80054,6 +80726,7 @@ SQLITE_PRIVATE void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int }else{ r1 = sqlite3ExprCodeTemp(pParse, pExpr, ®Free1); sqlite3VdbeAddOp3(v, OP_If, r1, dest, jumpIfNull!=0); + VdbeCoverage(v); testcase( regFree1==0 ); testcase( jumpIfNull==0 ); } @@ -80145,17 +80818,17 @@ SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int case TK_GE: case TK_NE: case TK_EQ: { - testcase( op==TK_LT ); - testcase( op==TK_LE ); - testcase( op==TK_GT ); - testcase( op==TK_GE ); - testcase( op==TK_EQ ); - testcase( op==TK_NE ); testcase( jumpIfNull==0 ); r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, ®Free2); codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, r1, r2, dest, jumpIfNull); + assert(TK_LT==OP_Lt); testcase(op==OP_Lt); VdbeCoverageIf(v,op==OP_Lt); + assert(TK_LE==OP_Le); testcase(op==OP_Le); VdbeCoverageIf(v,op==OP_Le); + assert(TK_GT==OP_Gt); testcase(op==OP_Gt); VdbeCoverageIf(v,op==OP_Gt); + assert(TK_GE==OP_Ge); testcase(op==OP_Ge); VdbeCoverageIf(v,op==OP_Ge); + assert(TK_EQ==OP_Eq); testcase(op==OP_Eq); VdbeCoverageIf(v,op==OP_Eq); + assert(TK_NE==OP_Ne); testcase(op==OP_Ne); VdbeCoverageIf(v,op==OP_Ne); testcase( regFree1==0 ); testcase( regFree2==0 ); break; @@ -80169,16 +80842,18 @@ SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int op = (pExpr->op==TK_IS) ? TK_NE : TK_EQ; codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, r1, r2, dest, SQLITE_NULLEQ); + VdbeCoverageIf(v, op==TK_EQ); + VdbeCoverageIf(v, op==TK_NE); testcase( regFree1==0 ); testcase( regFree2==0 ); break; } case TK_ISNULL: case TK_NOTNULL: { - testcase( op==TK_ISNULL ); - testcase( op==TK_NOTNULL ); r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); sqlite3VdbeAddOp2(v, op, r1, dest); + testcase( op==TK_ISNULL ); VdbeCoverageIf(v, op==TK_ISNULL); + testcase( op==TK_NOTNULL ); VdbeCoverageIf(v, op==TK_NOTNULL); testcase( regFree1==0 ); break; } @@ -80207,6 +80882,7 @@ SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int }else{ r1 = sqlite3ExprCodeTemp(pParse, pExpr, ®Free1); sqlite3VdbeAddOp3(v, OP_IfNot, r1, dest, jumpIfNull!=0); + VdbeCoverage(v); testcase( regFree1==0 ); testcase( jumpIfNull==0 ); } @@ -80753,8 +81429,8 @@ static void renameTableFunc( assert( len>0 ); } while( token!=TK_LP && token!=TK_USING ); - zRet = sqlite3MPrintf(db, "%.*s\"%w\"%s", ((u8*)tname.z) - zSql, zSql, - zTableName, tname.z+tname.n); + zRet = sqlite3MPrintf(db, "%.*s\"%w\"%s", (int)(((u8*)tname.z) - zSql), + zSql, zTableName, tname.z+tname.n); sqlite3_result_text(context, zRet, -1, SQLITE_DYNAMIC); } } @@ -80806,7 +81482,7 @@ static void renameParentFunc( sqlite3Dequote(zParent); if( 0==sqlite3StrICmp((const char *)zOld, zParent) ){ char *zOut = sqlite3MPrintf(db, "%s%.*s\"%w\"", - (zOutput?zOutput:""), z-zInput, zInput, (const char *)zNew + (zOutput?zOutput:""), (int)(z-zInput), zInput, (const char *)zNew ); sqlite3DbFree(db, zOutput); zOutput = zOut; @@ -80892,8 +81568,8 @@ static void renameTriggerFunc( /* Variable tname now contains the token that is the old table-name ** in the CREATE TRIGGER statement. */ - zRet = sqlite3MPrintf(db, "%.*s\"%w\"%s", ((u8*)tname.z) - zSql, zSql, - zTableName, tname.z+tname.n); + zRet = sqlite3MPrintf(db, "%.*s\"%w\"%s", (int)(((u8*)tname.z) - zSql), + zSql, zTableName, tname.z+tname.n); sqlite3_result_text(context, zRet, -1, SQLITE_DYNAMIC); } } @@ -81145,7 +81821,7 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable( } #endif - /* Begin a transaction and code the VerifyCookie for database iDb. + /* Begin a transaction for database iDb. ** Then modify the schema cookie (since the ALTER TABLE modifies the ** schema). Open a statement transaction if the table is a virtual ** table. @@ -81281,6 +81957,7 @@ SQLITE_PRIVATE void sqlite3MinimumFileFormat(Parse *pParse, int iDb, int minForm sqlite3VdbeUsesBtree(v, iDb); sqlite3VdbeAddOp2(v, OP_Integer, minFormat, r2); j1 = sqlite3VdbeAddOp3(v, OP_Ge, r2, 0, r1); + sqlite3VdbeChangeP5(v, SQLITE_NOTNULL); VdbeCoverage(v); sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_FILE_FORMAT, r2); sqlite3VdbeJumpHere(v, j1); sqlite3ReleaseTempReg(pParse, r1); @@ -82581,6 +83258,7 @@ static void analyzeOneTable( ** */ addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur); + VdbeCoverage(v); sqlite3VdbeAddOp2(v, OP_Integer, 0, regChng); addrGotoChng0 = sqlite3VdbeAddOp0(v, OP_Goto); @@ -82602,6 +83280,7 @@ static void analyzeOneTable( aGotoChng[i] = sqlite3VdbeAddOp4(v, OP_Ne, regTemp, 0, regPrev+i, pColl, P4_COLLSEQ); sqlite3VdbeChangeP5(v, SQLITE_NULLEQ); + VdbeCoverage(v); } sqlite3VdbeAddOp2(v, OP_Integer, nCol, regChng); aGotoChng[nCol] = sqlite3VdbeAddOp0(v, OP_Goto); @@ -82648,7 +83327,7 @@ static void analyzeOneTable( sqlite3VdbeAddOp3(v, OP_Function, 1, regStat4, regTemp); sqlite3VdbeChangeP4(v, -1, (char*)&statPushFuncdef, P4_FUNCDEF); sqlite3VdbeChangeP5(v, 2+IsStat34); - sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, addrNextRow); + sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, addrNextRow); VdbeCoverage(v); /* Add the entry to the stat1 table. */ callStatGet(v, regStat4, STAT_GET_STAT1, regStat1); @@ -82675,10 +83354,15 @@ static void analyzeOneTable( addrNext = sqlite3VdbeCurrentAddr(v); callStatGet(v, regStat4, STAT_GET_ROWID, regSampleRowid); addrIsNull = sqlite3VdbeAddOp1(v, OP_IsNull, regSampleRowid); + VdbeCoverage(v); callStatGet(v, regStat4, STAT_GET_NEQ, regEq); callStatGet(v, regStat4, STAT_GET_NLT, regLt); callStatGet(v, regStat4, STAT_GET_NDLT, regDLt); sqlite3VdbeAddOp4Int(v, seekOp, iTabCur, addrNext, regSampleRowid, 0); + /* We know that the regSampleRowid row exists because it was read by + ** the previous loop. Thus the not-found jump of seekOp will never + ** be taken */ + VdbeCoverageNeverTaken(v); #ifdef SQLITE_ENABLE_STAT3 sqlite3ExprCodeGetColumnOfTable(v, pTab, iTabCur, pIdx->aiColumn[0], regSample); @@ -82689,10 +83373,10 @@ static void analyzeOneTable( } sqlite3VdbeAddOp3(v, OP_MakeRecord, regCol, nCol+1, regSample); #endif - sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 6, regTemp, "bbbbbb", 0); + sqlite3VdbeAddOp3(v, OP_MakeRecord, regTabname, 6, regTemp); sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur+1, regNewRowid); sqlite3VdbeAddOp3(v, OP_Insert, iStatCur+1, regTemp, regNewRowid); - sqlite3VdbeAddOp2(v, OP_Goto, 0, addrNext); + sqlite3VdbeAddOp2(v, OP_Goto, 1, addrNext); /* P1==1 for end-of-loop */ sqlite3VdbeJumpHere(v, addrIsNull); } #endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */ @@ -82709,7 +83393,7 @@ static void analyzeOneTable( if( pOnlyIdx==0 && needTableCnt ){ VdbeComment((v, "%s", pTab->zName)); sqlite3VdbeAddOp2(v, OP_Count, iTabCur, regStat1); - jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, regStat1); + jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, regStat1); VdbeCoverage(v); sqlite3VdbeAddOp2(v, OP_Null, 0, regIdxname); sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regTemp, "aaa", 0); sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regNewRowid); @@ -84247,20 +84931,22 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){ ** transaction on each used database and to verify the schema cookie ** on each used database. */ - if( pParse->cookieGoto>0 ){ + if( db->mallocFailed==0 && (pParse->cookieMask || pParse->pConstExpr) ){ yDbMask mask; - int iDb, i, addr; - sqlite3VdbeJumpHere(v, pParse->cookieGoto-1); + int iDb, i; + assert( sqlite3VdbeGetOp(v, 0)->opcode==OP_Init ); + sqlite3VdbeJumpHere(v, 0); for(iDb=0, mask=1; iDb<db->nDb; mask<<=1, iDb++){ if( (mask & pParse->cookieMask)==0 ) continue; sqlite3VdbeUsesBtree(v, iDb); - sqlite3VdbeAddOp2(v,OP_Transaction, iDb, (mask & pParse->writeMask)!=0); - if( db->init.busy==0 ){ - assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); - sqlite3VdbeAddOp3(v, OP_VerifyCookie, - iDb, pParse->cookieValue[iDb], - db->aDb[iDb].pSchema->iGeneration); - } + sqlite3VdbeAddOp4Int(v, + OP_Transaction, /* Opcode */ + iDb, /* P1 */ + (mask & pParse->writeMask)!=0, /* P2 */ + pParse->cookieValue[iDb], /* P3 */ + db->aDb[iDb].pSchema->iGeneration /* P4 */ + ); + if( db->init.busy==0 ) sqlite3VdbeChangeP5(v, 1); } #ifndef SQLITE_OMIT_VIRTUALTABLE for(i=0; i<pParse->nVtabLock; i++){ @@ -84281,17 +84967,16 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){ sqlite3AutoincrementBegin(pParse); /* Code constant expressions that where factored out of inner loops */ - addr = pParse->cookieGoto; if( pParse->pConstExpr ){ ExprList *pEL = pParse->pConstExpr; - pParse->cookieGoto = 0; + pParse->okConstFactor = 0; for(i=0; i<pEL->nExpr; i++){ sqlite3ExprCode(pParse, pEL->a[i].pExpr, pEL->a[i].u.iConstExprReg); } } /* Finally, jump back to the beginning of the executable code. */ - sqlite3VdbeAddOp2(v, OP_Goto, 0, addr); + sqlite3VdbeAddOp2(v, OP_Goto, 0, 1); } } @@ -84314,7 +84999,6 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){ pParse->nSet = 0; pParse->nVar = 0; pParse->cookieMask = 0; - pParse->cookieGoto = 0; } /* @@ -85046,7 +85730,7 @@ SQLITE_PRIVATE void sqlite3StartTable( reg3 = ++pParse->nMem; sqlite3VdbeAddOp3(v, OP_ReadCookie, iDb, reg3, BTREE_FILE_FORMAT); sqlite3VdbeUsesBtree(v, iDb); - j1 = sqlite3VdbeAddOp1(v, OP_If, reg3); + j1 = sqlite3VdbeAddOp1(v, OP_If, reg3); VdbeCoverage(v); fileFormat = (db->flags & SQLITE_LegacyFileFmt)!=0 ? 1 : SQLITE_MAX_FILE_FORMAT; sqlite3VdbeAddOp2(v, OP_Integer, fileFormat, reg3); @@ -86773,27 +87457,27 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){ /* Open the table. Loop through all rows of the table, inserting index ** records into the sorter. */ sqlite3OpenTable(pParse, iTab, iDb, pTab, OP_OpenRead); - addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iTab, 0); + addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iTab, 0); VdbeCoverage(v); regRecord = sqlite3GetTempReg(pParse); sqlite3GenerateIndexKey(pParse,pIndex,iTab,regRecord,0,&iPartIdxLabel,0,0); sqlite3VdbeAddOp2(v, OP_SorterInsert, iSorter, regRecord); sqlite3VdbeResolveLabel(v, iPartIdxLabel); - sqlite3VdbeAddOp2(v, OP_Next, iTab, addr1+1); + sqlite3VdbeAddOp2(v, OP_Next, iTab, addr1+1); VdbeCoverage(v); sqlite3VdbeJumpHere(v, addr1); if( memRootPage<0 ) sqlite3VdbeAddOp2(v, OP_Clear, tnum, iDb); sqlite3VdbeAddOp4(v, OP_OpenWrite, iIdx, tnum, iDb, (char *)pKey, P4_KEYINFO); sqlite3VdbeChangeP5(v, OPFLAG_BULKCSR|((memRootPage>=0)?OPFLAG_P2ISREG:0)); - addr1 = sqlite3VdbeAddOp2(v, OP_SorterSort, iSorter, 0); + addr1 = sqlite3VdbeAddOp2(v, OP_SorterSort, iSorter, 0); VdbeCoverage(v); assert( pKey!=0 || db->mallocFailed || pParse->nErr ); if( pIndex->onError!=OE_None && pKey!=0 ){ int j2 = sqlite3VdbeCurrentAddr(v) + 3; sqlite3VdbeAddOp2(v, OP_Goto, 0, j2); addr2 = sqlite3VdbeCurrentAddr(v); sqlite3VdbeAddOp4Int(v, OP_SorterCompare, iSorter, j2, regRecord, - pKey->nField - pIndex->nKeyCol); + pKey->nField - pIndex->nKeyCol); VdbeCoverage(v); sqlite3UniqueConstraint(pParse, OE_Abort, pIndex); }else{ addr2 = sqlite3VdbeCurrentAddr(v); @@ -86802,7 +87486,7 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){ sqlite3VdbeAddOp3(v, OP_IdxInsert, iIdx, regRecord, 1); sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); sqlite3ReleaseTempReg(pParse, regRecord); - sqlite3VdbeAddOp2(v, OP_SorterNext, iSorter, addr2); + sqlite3VdbeAddOp2(v, OP_SorterNext, iSorter, addr2); VdbeCoverage(v); sqlite3VdbeJumpHere(v, addr1); sqlite3VdbeAddOp1(v, OP_Close, iTab); @@ -87571,7 +88255,7 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListEnlarge( assert( iStart<=pSrc->nSrc ); /* Allocate additional space if needed */ - if( pSrc->nSrc+nExtra>pSrc->nAlloc ){ + if( (u32)pSrc->nSrc+nExtra>pSrc->nAlloc ){ SrcList *pNew; int nAlloc = pSrc->nSrc+nExtra; int nGot; @@ -87583,7 +88267,7 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListEnlarge( } pSrc = pNew; nGot = (sqlite3DbMallocSize(db, pNew) - sizeof(*pSrc))/sizeof(pSrc->a[0])+1; - pSrc->nAlloc = (u8)nGot; + pSrc->nAlloc = nGot; } /* Move existing slots that come after the newly inserted slots @@ -87591,7 +88275,7 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListEnlarge( for(i=pSrc->nSrc-1; i>=iStart; i--){ pSrc->a[i+nExtra] = pSrc->a[i]; } - pSrc->nSrc += (i8)nExtra; + pSrc->nSrc += nExtra; /* Zero the newly allocated slots */ memset(&pSrc->a[iStart], 0, sizeof(pSrc->a[0])*nExtra); @@ -87923,59 +88607,26 @@ SQLITE_PRIVATE int sqlite3OpenTempDatabase(Parse *pParse){ } /* -** Generate VDBE code that will verify the schema cookie and start -** a read-transaction for all named database files. -** -** It is important that all schema cookies be verified and all -** read transactions be started before anything else happens in -** the VDBE program. But this routine can be called after much other -** code has been generated. So here is what we do: -** -** The first time this routine is called, we code an OP_Goto that -** will jump to a subroutine at the end of the program. Then we -** record every database that needs its schema verified in the -** pParse->cookieMask field. Later, after all other code has been -** generated, the subroutine that does the cookie verifications and -** starts the transactions will be coded and the OP_Goto P2 value -** will be made to point to that subroutine. The generation of the -** cookie verification subroutine code happens in sqlite3FinishCoding(). -** -** If iDb<0 then code the OP_Goto only - don't set flag to verify the -** schema on any databases. This can be used to position the OP_Goto -** early in the code, before we know if any database tables will be used. +** Record the fact that the schema cookie will need to be verified +** for database iDb. The code to actually verify the schema cookie +** will occur at the end of the top-level VDBE and will be generated +** later, by sqlite3FinishCoding(). */ SQLITE_PRIVATE void sqlite3CodeVerifySchema(Parse *pParse, int iDb){ Parse *pToplevel = sqlite3ParseToplevel(pParse); + sqlite3 *db = pToplevel->db; + yDbMask mask; -#ifndef SQLITE_OMIT_TRIGGER - if( pToplevel!=pParse ){ - /* This branch is taken if a trigger is currently being coded. In this - ** case, set cookieGoto to a non-zero value to show that this function - ** has been called. This is used by the sqlite3ExprCodeConstants() - ** function. */ - pParse->cookieGoto = -1; - } -#endif - if( pToplevel->cookieGoto==0 ){ - Vdbe *v = sqlite3GetVdbe(pToplevel); - if( v==0 ) return; /* This only happens if there was a prior error */ - pToplevel->cookieGoto = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0)+1; - } - if( iDb>=0 ){ - sqlite3 *db = pToplevel->db; - yDbMask mask; - - assert( iDb<db->nDb ); - assert( db->aDb[iDb].pBt!=0 || iDb==1 ); - assert( iDb<SQLITE_MAX_ATTACHED+2 ); - 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; - if( !OMIT_TEMPDB && iDb==1 ){ - sqlite3OpenTempDatabase(pToplevel); - } + assert( iDb>=0 && iDb<db->nDb ); + assert( db->aDb[iDb].pBt!=0 || iDb==1 ); + assert( iDb<SQLITE_MAX_ATTACHED+2 ); + 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; + if( !OMIT_TEMPDB && iDb==1 ){ + sqlite3OpenTempDatabase(pToplevel); } } } @@ -88946,10 +89597,8 @@ SQLITE_PRIVATE void sqlite3MaterializeView( SrcList *pFrom; sqlite3 *db = pParse->db; int iDb = sqlite3SchemaToIndex(db, pView->pSchema); - pWhere = sqlite3ExprDup(db, pWhere, 0); pFrom = sqlite3SrcListAppend(db, 0, 0, 0); - if( pFrom ){ assert( pFrom->nSrc==1 ); pFrom->a[0].zName = sqlite3DbStrDup(db, pView->zName); @@ -88957,10 +89606,7 @@ SQLITE_PRIVATE void sqlite3MaterializeView( assert( pFrom->a[0].pOn==0 ); assert( pFrom->a[0].pUsing==0 ); } - pSel = sqlite3SelectNew(pParse, 0, pFrom, pWhere, 0, 0, 0, 0, 0, 0); - if( pSel ) pSel->selFlags |= SF_Materialize; - sqlite3SelectDestInit(&dest, SRT_EphemTab, iCur); sqlite3Select(pParse, pSel, &dest); sqlite3SelectDelete(db, pSel); @@ -89297,7 +89943,7 @@ SQLITE_PRIVATE void sqlite3DeleteFrom( iKey = ++pParse->nMem; nKey = 0; /* Zero tells OP_Found to use a composite key */ sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, nPk, iKey, - sqlite3IndexAffinityStr(v, pPk), P4_TRANSIENT); + sqlite3IndexAffinityStr(v, pPk), nPk); sqlite3VdbeAddOp2(v, OP_IdxInsert, iEphCur, iKey); }else{ /* Get the rowid of the row to be deleted and remember it in the RowSet */ @@ -89335,13 +89981,15 @@ SQLITE_PRIVATE void sqlite3DeleteFrom( if( aToOpen[iDataCur-iTabCur] ){ assert( pPk!=0 ); sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, addrBypass, iKey, nKey); + VdbeCoverage(v); } }else if( pPk ){ - addrLoop = sqlite3VdbeAddOp1(v, OP_Rewind, iEphCur); + addrLoop = sqlite3VdbeAddOp1(v, OP_Rewind, iEphCur); VdbeCoverage(v); sqlite3VdbeAddOp2(v, OP_RowKey, iEphCur, iKey); assert( nKey==0 ); /* OP_Found will use a composite key */ }else{ addrLoop = sqlite3VdbeAddOp3(v, OP_RowSetRead, iRowSet, 0, iKey); + VdbeCoverage(v); assert( nKey==1 ); } @@ -89365,7 +90013,7 @@ SQLITE_PRIVATE void sqlite3DeleteFrom( if( okOnePass ){ sqlite3VdbeResolveLabel(v, addrBypass); }else if( pPk ){ - sqlite3VdbeAddOp2(v, OP_Next, iEphCur, addrLoop+1); + sqlite3VdbeAddOp2(v, OP_Next, iEphCur, addrLoop+1); VdbeCoverage(v); sqlite3VdbeJumpHere(v, addrLoop); }else{ sqlite3VdbeAddOp2(v, OP_Goto, 0, addrLoop); @@ -89463,7 +90111,11 @@ SQLITE_PRIVATE void sqlite3GenerateRowDelete( ** not attempt to delete it or fire any DELETE triggers. */ iLabel = sqlite3VdbeMakeLabel(v); opSeek = HasRowid(pTab) ? OP_NotExists : OP_NotFound; - if( !bNoSeek ) sqlite3VdbeAddOp4Int(v, opSeek, iDataCur, iLabel, iPk, nPk); + if( !bNoSeek ){ + sqlite3VdbeAddOp4Int(v, opSeek, iDataCur, iLabel, iPk, nPk); + VdbeCoverageIf(v, opSeek==OP_NotExists); + VdbeCoverageIf(v, opSeek==OP_NotFound); + } /* If there are any triggers to fire, allocate a range of registers to ** use for the old.* references in the triggers. */ @@ -89505,6 +90157,8 @@ SQLITE_PRIVATE void sqlite3GenerateRowDelete( */ if( addrStart<sqlite3VdbeCurrentAddr(v) ){ sqlite3VdbeAddOp4Int(v, opSeek, iDataCur, iLabel, iPk, nPk); + VdbeCoverageIf(v, opSeek==OP_NotExists); + VdbeCoverageIf(v, opSeek==OP_NotFound); } /* Do FK processing. This call checks that any FK constraints that @@ -90687,7 +91341,7 @@ static void charFunc( ){ unsigned char *z, *zOut; int i; - zOut = z = sqlite3_malloc( argc*4 ); + zOut = z = sqlite3_malloc( argc*4+1 ); if( z==0 ){ sqlite3_result_error_nomem(context); return; @@ -91762,10 +92416,11 @@ static void fkLookupParent( ** search for a matching row in the parent table. */ if( nIncr<0 ){ sqlite3VdbeAddOp2(v, OP_FkIfZero, pFKey->isDeferred, iOk); + VdbeCoverage(v); } for(i=0; i<pFKey->nCol; i++){ int iReg = aiCol[i] + regData + 1; - sqlite3VdbeAddOp2(v, OP_IsNull, iReg, iOk); + sqlite3VdbeAddOp2(v, OP_IsNull, iReg, iOk); VdbeCoverage(v); } if( isIgnore==0 ){ @@ -91782,17 +92437,19 @@ static void fkLookupParent( ** will have INTEGER affinity applied to it, which may not be correct. */ sqlite3VdbeAddOp2(v, OP_SCopy, aiCol[0]+1+regData, regTemp); iMustBeInt = sqlite3VdbeAddOp2(v, OP_MustBeInt, regTemp, 0); + VdbeCoverage(v); /* 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. */ if( pTab==pFKey->pFrom && nIncr==1 ){ - sqlite3VdbeAddOp3(v, OP_Eq, regData, iOk, regTemp); + sqlite3VdbeAddOp3(v, OP_Eq, regData, iOk, regTemp); VdbeCoverage(v); + sqlite3VdbeChangeP5(v, SQLITE_NOTNULL); } sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenRead); - sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, regTemp); + sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, regTemp); VdbeCoverage(v); sqlite3VdbeAddOp2(v, OP_Goto, 0, iOk); sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2); sqlite3VdbeJumpHere(v, iMustBeInt); @@ -91828,15 +92485,15 @@ static void fkLookupParent( /* The parent key is a composite key that includes the IPK column */ iParent = regData; } - sqlite3VdbeAddOp3(v, OP_Ne, iChild, iJump, iParent); + sqlite3VdbeAddOp3(v, OP_Ne, iChild, iJump, iParent); VdbeCoverage(v); sqlite3VdbeChangeP5(v, SQLITE_JUMPIFNULL); } sqlite3VdbeAddOp2(v, OP_Goto, 0, iOk); } - sqlite3VdbeAddOp3(v, OP_MakeRecord, regTemp, nCol, regRec); - sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v,pIdx), P4_TRANSIENT); - sqlite3VdbeAddOp4Int(v, OP_Found, iCur, iOk, regRec, 0); + sqlite3VdbeAddOp4(v, OP_MakeRecord, regTemp, nCol, regRec, + sqlite3IndexAffinityStr(v,pIdx), nCol); + sqlite3VdbeAddOp4Int(v, OP_Found, iCur, iOk, regRec, 0); VdbeCoverage(v); sqlite3ReleaseTempReg(pParse, regRec); sqlite3ReleaseTempRange(pParse, regTemp, nCol); @@ -91974,6 +92631,7 @@ static void fkScanChildren( if( nIncr<0 ){ iFkIfZero = sqlite3VdbeAddOp2(v, OP_FkIfZero, pFKey->isDeferred, 0); + VdbeCoverage(v); } /* Create an Expr object representing an SQL expression like: @@ -92136,7 +92794,7 @@ SQLITE_PRIVATE void sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTa } if( !p ) return; iSkip = sqlite3VdbeMakeLabel(v); - sqlite3VdbeAddOp2(v, OP_FkIfZero, 1, iSkip); + sqlite3VdbeAddOp2(v, OP_FkIfZero, 1, iSkip); VdbeCoverage(v); } pParse->disableTriggers = 1; @@ -92154,6 +92812,7 @@ SQLITE_PRIVATE void sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTa */ if( (db->flags & SQLITE_DeferFKs)==0 ){ sqlite3VdbeAddOp2(v, OP_FkIfZero, 0, sqlite3VdbeCurrentAddr(v)+2); + VdbeCoverage(v); sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_FOREIGNKEY, OE_Abort, 0, P4_STATIC, P5_ConstraintFK); } @@ -92313,7 +92972,7 @@ SQLITE_PRIVATE void sqlite3FkCheck( int iJump = sqlite3VdbeCurrentAddr(v) + pFKey->nCol + 1; for(i=0; i<pFKey->nCol; i++){ int iReg = pFKey->aCol[i].iFrom + regOld + 1; - sqlite3VdbeAddOp2(v, OP_IsNull, iReg, iJump); + sqlite3VdbeAddOp2(v, OP_IsNull, iReg, iJump); VdbeCoverage(v); } sqlite3VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, -1); } @@ -92880,10 +93539,16 @@ SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(Vdbe *v, Index *pIdx){ } /* -** Set P4 of the most recently inserted opcode to a column affinity -** string for table pTab. A column affinity string has one character -** for each column indexed by the index, according to the affinity of the -** column: +** Compute the affinity string for table pTab, if it has not already been +** computed. As an optimization, omit trailing SQLITE_AFF_NONE affinities. +** +** If the affinity exists (if it is no entirely SQLITE_AFF_NONE values) and +** if iReg>0 then code an OP_Affinity opcode that will set the affinities +** for register iReg and following. Or if affinities exists and iReg==0, +** then just set the P4 operand of the previous opcode (which should be +** an OP_MakeRecord) to the affinity string. +** +** A column affinity string has one character per column: ** ** Character Column affinity ** ------------------------------ @@ -92893,19 +93558,11 @@ SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(Vdbe *v, Index *pIdx){ ** 'd' INTEGER ** 'e' REAL */ -SQLITE_PRIVATE void sqlite3TableAffinityStr(Vdbe *v, Table *pTab){ - /* The first time a column affinity string for a particular table - ** is required, it is allocated and populated here. It is then - ** stored as a member of the Table structure for subsequent use. - ** - ** The column affinity string will eventually be deleted by - ** sqlite3DeleteTable() when the Table structure itself is cleaned up. - */ - if( !pTab->zColAff ){ - char *zColAff; - int i; +SQLITE_PRIVATE void sqlite3TableAffinity(Vdbe *v, Table *pTab, int iReg){ + int i; + char *zColAff = pTab->zColAff; + if( zColAff==0 ){ sqlite3 *db = sqlite3VdbeDb(v); - zColAff = (char *)sqlite3DbMallocRaw(0, pTab->nCol+1); if( !zColAff ){ db->mallocFailed = 1; @@ -92915,22 +93572,28 @@ SQLITE_PRIVATE void sqlite3TableAffinityStr(Vdbe *v, Table *pTab){ for(i=0; i<pTab->nCol; i++){ zColAff[i] = pTab->aCol[i].affinity; } - zColAff[pTab->nCol] = '\0'; - + do{ + zColAff[i--] = 0; + }while( i>=0 && zColAff[i]==SQLITE_AFF_NONE ); pTab->zColAff = zColAff; } - - sqlite3VdbeChangeP4(v, -1, pTab->zColAff, P4_TRANSIENT); + i = sqlite3Strlen30(zColAff); + if( i ){ + if( iReg ){ + sqlite3VdbeAddOp4(v, OP_Affinity, iReg, i, 0, zColAff, i); + }else{ + sqlite3VdbeChangeP4(v, -1, zColAff, i); + } + } } /* ** Return non-zero if the table pTab in database iDb or any of its indices -** have been opened at any point in the VDBE program beginning at location -** iStartAddr throught the end of the program. This is used to see if +** have been opened at any point in the VDBE program. This is used to see if ** a statement of the form "INSERT INTO <iDb, pTab> SELECT ..." can -** run without using temporary table for the results of the SELECT. +** run without using a temporary table for the results of the SELECT. */ -static int readsTable(Parse *p, int iStartAddr, int iDb, Table *pTab){ +static int readsTable(Parse *p, int iDb, Table *pTab){ Vdbe *v = sqlite3GetVdbe(p); int i; int iEnd = sqlite3VdbeCurrentAddr(v); @@ -92938,7 +93601,7 @@ static int readsTable(Parse *p, int iStartAddr, int iDb, Table *pTab){ VTable *pVTab = IsVirtual(pTab) ? sqlite3GetVTable(p->db, pTab) : 0; #endif - for(i=iStartAddr; i<iEnd; i++){ + for(i=1; i<iEnd; i++){ VdbeOp *pOp = sqlite3VdbeGetOp(v, i); assert( pOp!=0 ); if( pOp->opcode==OP_OpenRead && pOp->p3==iDb ){ @@ -93039,14 +93702,14 @@ SQLITE_PRIVATE void sqlite3AutoincrementBegin(Parse *pParse){ sqlite3VdbeAddOp3(v, OP_Null, 0, memId, memId+1); addr = sqlite3VdbeCurrentAddr(v); sqlite3VdbeAddOp4(v, OP_String8, 0, memId-1, 0, p->pTab->zName, 0); - sqlite3VdbeAddOp2(v, OP_Rewind, 0, addr+9); + sqlite3VdbeAddOp2(v, OP_Rewind, 0, addr+9); VdbeCoverage(v); sqlite3VdbeAddOp3(v, OP_Column, 0, 0, memId); - sqlite3VdbeAddOp3(v, OP_Ne, memId-1, addr+7, memId); + sqlite3VdbeAddOp3(v, OP_Ne, memId-1, addr+7, memId); VdbeCoverage(v); sqlite3VdbeChangeP5(v, SQLITE_JUMPIFNULL); sqlite3VdbeAddOp2(v, OP_Rowid, 0, memId+1); sqlite3VdbeAddOp3(v, OP_Column, 0, 1, memId); sqlite3VdbeAddOp2(v, OP_Goto, 0, addr+9); - sqlite3VdbeAddOp2(v, OP_Next, 0, addr+2); + sqlite3VdbeAddOp2(v, OP_Next, 0, addr+2); VdbeCoverage(v); sqlite3VdbeAddOp2(v, OP_Integer, 0, memId); sqlite3VdbeAddOp0(v, OP_Close); } @@ -93081,25 +93744,16 @@ SQLITE_PRIVATE void sqlite3AutoincrementEnd(Parse *pParse){ assert( v ); for(p = pParse->pAinc; p; p = p->pNext){ Db *pDb = &db->aDb[p->iDb]; - int j1, j2, j3, j4, j5; + int j1; int iRec; 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); - j3 = sqlite3VdbeAddOp3(v, OP_Column, 0, 0, iRec); - j4 = sqlite3VdbeAddOp3(v, OP_Eq, memId-1, 0, iRec); - sqlite3VdbeAddOp2(v, OP_Next, 0, j3); - sqlite3VdbeJumpHere(v, j2); + j1 = sqlite3VdbeAddOp1(v, OP_NotNull, memId+1); VdbeCoverage(v); sqlite3VdbeAddOp2(v, OP_NewRowid, 0, memId+1); - j5 = sqlite3VdbeAddOp0(v, OP_Goto); - sqlite3VdbeJumpHere(v, j4); - sqlite3VdbeAddOp2(v, OP_Rowid, 0, memId+1); sqlite3VdbeJumpHere(v, j1); - sqlite3VdbeJumpHere(v, j5); sqlite3VdbeAddOp3(v, OP_MakeRecord, memId-1, 2, iRec); sqlite3VdbeAddOp3(v, OP_Insert, 0, iRec, memId+1); sqlite3VdbeChangeP5(v, OPFLAG_APPEND); @@ -93117,97 +93771,6 @@ SQLITE_PRIVATE void sqlite3AutoincrementEnd(Parse *pParse){ #endif /* SQLITE_OMIT_AUTOINCREMENT */ -/* -** Generate code for a co-routine that will evaluate a subquery one -** row at a time. -** -** The pSelect parameter is the subquery that the co-routine will evaluation. -** Information about the location of co-routine and the registers it will use -** is returned by filling in the pDest object. -** -** Registers are allocated as follows: -** -** pDest->iSDParm The register holding the next entry-point of the -** co-routine. Run the co-routine to its next breakpoint -** by calling "OP_Yield $X" where $X is pDest->iSDParm. -** -** pDest->iSDParm+1 The register holding the "completed" flag for the -** co-routine. This register is 0 if the previous Yield -** generated a new result row, or 1 if the subquery -** has completed. If the Yield is called again -** after this register becomes 1, then the VDBE will -** halt with an SQLITE_INTERNAL error. -** -** pDest->iSdst First result register. -** -** pDest->nSdst Number of result registers. -** -** This routine handles all of the register allocation and fills in the -** pDest structure appropriately. -** -** Here is a schematic of the generated code assuming that X is the -** co-routine entry-point register reg[pDest->iSDParm], that EOF is the -** completed flag reg[pDest->iSDParm+1], and R and S are the range of -** registers that hold the result set, reg[pDest->iSdst] through -** reg[pDest->iSdst+pDest->nSdst-1]: -** -** X <- A -** EOF <- 0 -** goto B -** A: setup for the SELECT -** loop rows in the SELECT -** load results into registers R..S -** yield X -** end loop -** cleanup after the SELECT -** EOF <- 1 -** yield X -** halt-error -** B: -** -** To use this subroutine, the caller generates code as follows: -** -** [ Co-routine generated by this subroutine, shown above ] -** S: yield X -** if EOF goto E -** if skip this row, goto C -** if terminate loop, goto E -** deal with this row -** C: goto S -** E: -*/ -SQLITE_PRIVATE int sqlite3CodeCoroutine(Parse *pParse, Select *pSelect, SelectDest *pDest){ - int regYield; /* Register holding co-routine entry-point */ - int regEof; /* Register holding co-routine completion flag */ - int addrTop; /* Top of the co-routine */ - int j1; /* Jump instruction */ - int rc; /* Result code */ - Vdbe *v; /* VDBE under construction */ - - regYield = ++pParse->nMem; - regEof = ++pParse->nMem; - v = sqlite3GetVdbe(pParse); - addrTop = sqlite3VdbeCurrentAddr(v); - sqlite3VdbeAddOp2(v, OP_Integer, addrTop+2, regYield); /* X <- A */ - VdbeComment((v, "Co-routine entry point")); - sqlite3VdbeAddOp2(v, OP_Integer, 0, regEof); /* EOF <- 0 */ - VdbeComment((v, "Co-routine completion flag")); - sqlite3SelectDestInit(pDest, SRT_Coroutine, regYield); - j1 = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0); - rc = sqlite3Select(pParse, pSelect, pDest); - assert( pParse->nErr==0 || rc ); - if( pParse->db->mallocFailed && rc==SQLITE_OK ) rc = SQLITE_NOMEM; - if( rc ) return rc; - sqlite3VdbeAddOp2(v, OP_Integer, 1, regEof); /* EOF <- 1 */ - sqlite3VdbeAddOp1(v, OP_Yield, regYield); /* yield X */ - sqlite3VdbeAddOp2(v, OP_Halt, SQLITE_INTERNAL, OE_Abort); - VdbeComment((v, "End of coroutine")); - sqlite3VdbeJumpHere(v, j1); /* label B: */ - return rc; -} - - - /* Forward declaration */ static int xferOptimization( Parse *pParse, /* Parser context */ @@ -93270,7 +93833,6 @@ static int xferOptimization( ** and the SELECT clause does not read from <table> at any time. ** The generated code follows this template: ** -** EOF <- 0 ** X <- A ** goto B ** A: setup for the SELECT @@ -93279,12 +93841,9 @@ static int xferOptimization( ** yield X ** end loop ** cleanup after the SELECT -** EOF <- 1 -** yield X -** goto A +** end-coroutine X ** B: open write cursor to <table> and its indices -** C: yield X -** if EOF goto D +** C: yield X, at EOF goto D ** insert the select result into <table> from R..R+n ** goto C ** D: cleanup @@ -93295,7 +93854,6 @@ static int xferOptimization( ** we have to use a intermediate table to store the results of ** the select. The template is like this: ** -** EOF <- 0 ** X <- A ** goto B ** A: setup for the SELECT @@ -93304,12 +93862,9 @@ static int xferOptimization( ** yield X ** end loop ** cleanup after the SELECT -** EOF <- 1 -** yield X -** halt-error +** end co-routine R ** B: open temp table -** L: yield X -** if EOF goto M +** L: yield X, at EOF goto M ** insert row from R..R+n into temp table ** goto L ** M: open write cursor to <table> and its indices @@ -93339,16 +93894,16 @@ SQLITE_PRIVATE void sqlite3Insert( int iIdxCur = 0; /* First index cursor */ int ipkColumn = -1; /* Column that is the INTEGER PRIMARY KEY */ int endOfLoop; /* Label for the end of the insertion loop */ - int useTempTable = 0; /* Store SELECT results in intermediate table */ int srcTab = 0; /* Data comes from this temporary cursor if >=0 */ int addrInsTop = 0; /* Jump to label "D" */ int addrCont = 0; /* Top of insert loop. Label "C" in templates 3 and 4 */ - int addrSelect = 0; /* Address of coroutine that implements the SELECT */ SelectDest dest; /* Destination for SELECT on rhs of INSERT */ int iDb; /* Index of database holding TABLE */ Db *pDb; /* The database containing table being inserted into */ - int appendFlag = 0; /* True if the insert is likely to be an append */ - int withoutRowid; /* 0 for normal table. 1 for WITHOUT ROWID table */ + u8 useTempTable = 0; /* Store SELECT results in intermediate table */ + u8 appendFlag = 0; /* True if the insert is likely to be an append */ + u8 withoutRowid; /* 0 for normal table. 1 for WITHOUT ROWID table */ + u8 bIdListInOrder = 1; /* True if IDLIST is in table order */ ExprList *pList = 0; /* List of VALUES() to be inserted */ /* Register allocations */ @@ -93358,7 +93913,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 regEof = 0; /* Register recording end of SELECT data */ int *aRegIdx = 0; /* One register allocated to each index */ #ifndef SQLITE_OMIT_TRIGGER @@ -93461,6 +94015,56 @@ SQLITE_PRIVATE void sqlite3Insert( */ regAutoinc = autoIncBegin(pParse, iDb, pTab); + /* Allocate registers for holding the rowid of the new row, + ** the content of the new row, and the assemblied row record. + */ + regRowid = regIns = pParse->nMem+1; + pParse->nMem += pTab->nCol + 1; + if( IsVirtual(pTab) ){ + regRowid++; + pParse->nMem++; + } + regData = regRowid+1; + + /* If the INSERT statement included an IDLIST term, then make sure + ** all elements of the IDLIST really are columns of the table and + ** remember the column indices. + ** + ** If the table has an INTEGER PRIMARY KEY column and that column + ** is named in the IDLIST, then record in the ipkColumn variable + ** the index into IDLIST of the primary key column. ipkColumn is + ** the index of the primary key as it appears in IDLIST, not as + ** is appears in the original table. (The index of the INTEGER + ** PRIMARY KEY in the original table is pTab->iPKey.) + */ + if( pColumn ){ + for(i=0; i<pColumn->nId; i++){ + pColumn->a[i].idx = -1; + } + for(i=0; i<pColumn->nId; i++){ + for(j=0; j<pTab->nCol; j++){ + if( sqlite3StrICmp(pColumn->a[i].zName, pTab->aCol[j].zName)==0 ){ + pColumn->a[i].idx = j; + if( i!=j ) bIdListInOrder = 0; + if( j==pTab->iPKey ){ + ipkColumn = i; assert( !withoutRowid ); + } + break; + } + } + if( j>=pTab->nCol ){ + if( sqlite3IsRowid(pColumn->a[i].zName) && !withoutRowid ){ + ipkColumn = i; + }else{ + sqlite3ErrorMsg(pParse, "table %S has no column named %s", + pTabList, 0, pColumn->a[i].zName); + pParse->checkSchema = 1; + goto insert_cleanup; + } + } + } + } + /* Figure out how many columns of data are supplied. If the data ** is coming from a SELECT statement, then generate a co-routine that ** produces a single row of the SELECT on each invocation. The @@ -93468,14 +94072,24 @@ SQLITE_PRIVATE void sqlite3Insert( */ if( pSelect ){ /* Data is coming from a SELECT. Generate a co-routine to run the SELECT */ - int rc = sqlite3CodeCoroutine(pParse, pSelect, &dest); - if( rc ) goto insert_cleanup; - - regEof = dest.iSDParm + 1; + int regYield; /* Register holding co-routine entry-point */ + int addrTop; /* Top of the co-routine */ + int rc; /* Result code */ + + regYield = ++pParse->nMem; + addrTop = sqlite3VdbeCurrentAddr(v) + 1; + sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop); + sqlite3SelectDestInit(&dest, SRT_Coroutine, regYield); + dest.iSdst = bIdListInOrder ? regData : 0; + dest.nSdst = pTab->nCol; + rc = sqlite3Select(pParse, pSelect, &dest); regFromSelect = dest.iSdst; + assert( pParse->nErr==0 || rc ); + if( rc || db->mallocFailed ) goto insert_cleanup; + sqlite3VdbeAddOp1(v, OP_EndCoroutine, regYield); + sqlite3VdbeJumpHere(v, addrTop - 1); /* label B: */ assert( pSelect->pEList ); nColumn = pSelect->pEList->nExpr; - assert( dest.nSdst==nColumn ); /* Set useTempTable to TRUE if the result of the SELECT statement ** should be written into a temporary table (template 4). Set to @@ -93486,7 +94100,7 @@ SQLITE_PRIVATE void sqlite3Insert( ** of the tables being read by the SELECT statement. Also use a ** temp table in the case of row triggers. */ - if( pTrigger || readsTable(pParse, addrSelect, iDb, pTab) ){ + if( pTrigger || readsTable(pParse, iDb, pTab) ){ useTempTable = 1; } @@ -93496,28 +94110,25 @@ SQLITE_PRIVATE void sqlite3Insert( ** here is from the 4th template: ** ** B: open temp table - ** L: yield X - ** if EOF goto M + ** L: yield X, goto M at EOF ** insert row from R..R+n into temp table ** goto L ** M: ... */ int regRec; /* Register to hold packed record */ int regTempRowid; /* Register to hold temp table ROWID */ - int addrTop; /* Label "L" */ - int addrIf; /* Address of jump to M */ + int addrL; /* Label "L" */ srcTab = pParse->nTab++; regRec = sqlite3GetTempReg(pParse); regTempRowid = sqlite3GetTempReg(pParse); sqlite3VdbeAddOp2(v, OP_OpenEphemeral, srcTab, nColumn); - addrTop = sqlite3VdbeAddOp1(v, OP_Yield, dest.iSDParm); - addrIf = sqlite3VdbeAddOp1(v, OP_If, regEof); + addrL = sqlite3VdbeAddOp1(v, OP_Yield, dest.iSDParm); VdbeCoverage(v); sqlite3VdbeAddOp3(v, OP_MakeRecord, regFromSelect, nColumn, regRec); sqlite3VdbeAddOp2(v, OP_NewRowid, srcTab, regTempRowid); sqlite3VdbeAddOp3(v, OP_Insert, srcTab, regRec, regTempRowid); - sqlite3VdbeAddOp2(v, OP_Goto, 0, addrTop); - sqlite3VdbeJumpHere(v, addrIf); + sqlite3VdbeAddOp2(v, OP_Goto, 0, addrL); + sqlite3VdbeJumpHere(v, addrL); sqlite3ReleaseTempReg(pParse, regRec); sqlite3ReleaseTempReg(pParse, regTempRowid); } @@ -93538,6 +94149,14 @@ SQLITE_PRIVATE void sqlite3Insert( } } + /* If there is no IDLIST term but the table has an integer primary + ** key, the set the ipkColumn variable to the integer primary key + ** column index in the original table definition. + */ + if( pColumn==0 && nColumn>0 ){ + ipkColumn = pTab->iPKey; + } + /* Make sure the number of columns in the source data matches the number ** of columns to be inserted into the table. */ @@ -93556,52 +94175,6 @@ SQLITE_PRIVATE void sqlite3Insert( sqlite3ErrorMsg(pParse, "%d values for %d columns", nColumn, pColumn->nId); goto insert_cleanup; } - - /* If the INSERT statement included an IDLIST term, then make sure - ** all elements of the IDLIST really are columns of the table and - ** remember the column indices. - ** - ** If the table has an INTEGER PRIMARY KEY column and that column - ** is named in the IDLIST, then record in the ipkColumn variable - ** the index into IDLIST of the primary key column. ipkColumn is - ** the index of the primary key as it appears in IDLIST, not as - ** is appears in the original table. (The index of the INTEGER - ** PRIMARY KEY in the original table is pTab->iPKey.) - */ - if( pColumn ){ - for(i=0; i<pColumn->nId; i++){ - pColumn->a[i].idx = -1; - } - for(i=0; i<pColumn->nId; i++){ - for(j=0; j<pTab->nCol; j++){ - if( sqlite3StrICmp(pColumn->a[i].zName, pTab->aCol[j].zName)==0 ){ - pColumn->a[i].idx = j; - if( j==pTab->iPKey ){ - ipkColumn = i; assert( !withoutRowid ); - } - break; - } - } - if( j>=pTab->nCol ){ - if( sqlite3IsRowid(pColumn->a[i].zName) && !withoutRowid ){ - ipkColumn = i; - }else{ - sqlite3ErrorMsg(pParse, "table %S has no column named %s", - pTabList, 0, pColumn->a[i].zName); - pParse->checkSchema = 1; - goto insert_cleanup; - } - } - } - } - - /* If there is no IDLIST term but the table has an integer primary - ** key, the set the ipkColumn variable to the integer primary key - ** column index in the original table definition. - */ - if( pColumn==0 && nColumn>0 ){ - ipkColumn = pTab->iPKey; - } /* Initialize the count of rows to be inserted */ @@ -93629,38 +94202,26 @@ SQLITE_PRIVATE void sqlite3Insert( /* This block codes the top of loop only. The complete loop is the ** following pseudocode (template 4): ** - ** rewind temp table + ** rewind temp table, if empty goto D ** C: loop over rows of intermediate table ** transfer values form intermediate table into <table> ** end loop ** D: ... */ - addrInsTop = sqlite3VdbeAddOp1(v, OP_Rewind, srcTab); + addrInsTop = sqlite3VdbeAddOp1(v, OP_Rewind, srcTab); VdbeCoverage(v); addrCont = sqlite3VdbeCurrentAddr(v); }else if( pSelect ){ /* This block codes the top of loop only. The complete loop is the ** following pseudocode (template 3): ** - ** C: yield X - ** if EOF goto D + ** C: yield X, at EOF goto D ** insert the select result into <table> from R..R+n ** goto C ** D: ... */ - addrCont = sqlite3VdbeAddOp1(v, OP_Yield, dest.iSDParm); - addrInsTop = sqlite3VdbeAddOp1(v, OP_If, regEof); - } - - /* Allocate registers for holding the rowid of the new row, - ** the content of the new row, and the assemblied row record. - */ - regRowid = regIns = pParse->nMem+1; - pParse->nMem += pTab->nCol + 1; - if( IsVirtual(pTab) ){ - regRowid++; - pParse->nMem++; + addrInsTop = addrCont = sqlite3VdbeAddOp1(v, OP_Yield, dest.iSDParm); + VdbeCoverage(v); } - regData = regRowid+1; /* Run the BEFORE and INSTEAD OF triggers, if there are any */ @@ -93685,10 +94246,10 @@ SQLITE_PRIVATE void sqlite3Insert( assert( pSelect==0 ); /* Otherwise useTempTable is true */ sqlite3ExprCode(pParse, pList->a[ipkColumn].pExpr, regCols); } - j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regCols); + j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regCols); VdbeCoverage(v); sqlite3VdbeAddOp2(v, OP_Integer, -1, regCols); sqlite3VdbeJumpHere(v, j1); - sqlite3VdbeAddOp1(v, OP_MustBeInt, regCols); + sqlite3VdbeAddOp1(v, OP_MustBeInt, regCols); VdbeCoverage(v); } /* Cannot have triggers on a virtual table. If it were possible, @@ -93722,8 +94283,7 @@ SQLITE_PRIVATE void sqlite3Insert( ** table column affinities. */ if( !isView ){ - sqlite3VdbeAddOp2(v, OP_Affinity, regCols+1, pTab->nCol); - sqlite3TableAffinityStr(v, pTab); + sqlite3TableAffinity(v, pTab, regCols+1); } /* Fire BEFORE or INSTEAD OF triggers */ @@ -93745,7 +94305,7 @@ SQLITE_PRIVATE void sqlite3Insert( if( useTempTable ){ sqlite3VdbeAddOp3(v, OP_Column, srcTab, ipkColumn, regRowid); }else if( pSelect ){ - sqlite3VdbeAddOp2(v, OP_SCopy, regFromSelect+ipkColumn, regRowid); + sqlite3VdbeAddOp2(v, OP_Copy, regFromSelect+ipkColumn, regRowid); }else{ VdbeOp *pOp; sqlite3ExprCode(pParse, pList->a[ipkColumn].pExpr, regRowid); @@ -93764,14 +94324,14 @@ SQLITE_PRIVATE void sqlite3Insert( if( !appendFlag ){ int j1; if( !IsVirtual(pTab) ){ - j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regRowid); + j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regRowid); VdbeCoverage(v); sqlite3VdbeAddOp3(v, OP_NewRowid, iDataCur, regRowid, regAutoinc); sqlite3VdbeJumpHere(v, j1); }else{ j1 = sqlite3VdbeCurrentAddr(v); - sqlite3VdbeAddOp2(v, OP_IsNull, regRowid, j1+2); + sqlite3VdbeAddOp2(v, OP_IsNull, regRowid, j1+2); VdbeCoverage(v); } - sqlite3VdbeAddOp1(v, OP_MustBeInt, regRowid); + sqlite3VdbeAddOp1(v, OP_MustBeInt, regRowid); VdbeCoverage(v); } }else if( IsVirtual(pTab) || withoutRowid ){ sqlite3VdbeAddOp2(v, OP_Null, 0, regRowid); @@ -93791,8 +94351,9 @@ SQLITE_PRIVATE void sqlite3Insert( /* The value of the INTEGER PRIMARY KEY column is always a NULL. ** Whenever this column is read, the rowid will be substituted ** in its place. Hence, fill this column with a NULL to avoid - ** taking up data space with information that will never be used. */ - sqlite3VdbeAddOp2(v, OP_Null, 0, iRegStore); + ** taking up data space with information that will never be used. + ** As there may be shallow copies of this value, make it a soft-NULL */ + sqlite3VdbeAddOp1(v, OP_SoftNull, iRegStore); continue; } if( pColumn==0 ){ @@ -93809,11 +94370,13 @@ SQLITE_PRIVATE void sqlite3Insert( } } if( j<0 || nColumn==0 || (pColumn && j>=pColumn->nId) ){ - sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, iRegStore); + sqlite3ExprCodeFactorable(pParse, pTab->aCol[i].pDflt, iRegStore); }else if( useTempTable ){ sqlite3VdbeAddOp3(v, OP_Column, srcTab, j, iRegStore); }else if( pSelect ){ - sqlite3VdbeAddOp2(v, OP_SCopy, regFromSelect+j, iRegStore); + if( regFromSelect!=regData ){ + sqlite3VdbeAddOp2(v, OP_SCopy, regFromSelect+j, iRegStore); + } }else{ sqlite3ExprCode(pParse, pList->a[j].pExpr, iRegStore); } @@ -93859,7 +94422,7 @@ SQLITE_PRIVATE void sqlite3Insert( */ sqlite3VdbeResolveLabel(v, endOfLoop); if( useTempTable ){ - sqlite3VdbeAddOp2(v, OP_Next, srcTab, addrCont); + sqlite3VdbeAddOp2(v, OP_Next, srcTab, addrCont); VdbeCoverage(v); sqlite3VdbeJumpHere(v, addrInsTop); sqlite3VdbeAddOp1(v, OP_Close, srcTab); }else if( pSelect ){ @@ -94026,6 +94589,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( int ipkTop = 0; /* Top of the rowid change constraint check */ int ipkBottom = 0; /* Bottom of the rowid change constraint check */ u8 isUpdate; /* True if this is an UPDATE operation */ + u8 bAffinityDone = 0; /* True if the OP_Affinity operation has been run */ int regRowid = -1; /* Register holding ROWID value */ isUpdate = regOldData!=0; @@ -94080,15 +94644,17 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( sqlite3VdbeAddOp4(v, OP_HaltIfNull, SQLITE_CONSTRAINT_NOTNULL, onError, regNewData+1+i, zMsg, P4_DYNAMIC); sqlite3VdbeChangeP5(v, P5_ConstraintNotNull); + VdbeCoverage(v); break; } case OE_Ignore: { sqlite3VdbeAddOp2(v, OP_IsNull, regNewData+1+i, ignoreDest); + VdbeCoverage(v); break; } default: { assert( onError==OE_Replace ); - j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regNewData+1+i); + j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regNewData+1+i); VdbeCoverage(v); sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, regNewData+1+i); sqlite3VdbeJumpHere(v, j1); break; @@ -94140,6 +94706,8 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( ** it might have changed. Skip the conflict logic below if the rowid ** is unchanged. */ sqlite3VdbeAddOp3(v, OP_Eq, regNewData, addrRowidOk, regOldData); + sqlite3VdbeChangeP5(v, SQLITE_NOTNULL); + VdbeCoverage(v); } /* If the response to a rowid conflict is REPLACE but the response @@ -94159,6 +94727,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( /* Check to see if the new rowid already exists in the table. Skip ** the following conflict logic if it does not. */ sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, addrRowidOk, regNewData); + VdbeCoverage(v); /* Generate code that deals with a rowid collision */ switch( onError ){ @@ -94237,6 +94806,10 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( int addrUniqueOk; /* Jump here if the UNIQUE constraint is satisfied */ if( aRegIdx[ix]==0 ) continue; /* Skip indices that do not change */ + if( bAffinityDone==0 ){ + sqlite3TableAffinity(v, pTab, regNewData+1); + bAffinityDone = 1; + } iThisCur = iIdxCur+ix; addrUniqueOk = sqlite3VdbeMakeLabel(v); @@ -94267,7 +94840,6 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( VdbeComment((v, "%s", iField<0 ? "rowid" : pTab->aCol[iField].zName)); } sqlite3VdbeAddOp3(v, OP_MakeRecord, regIdx, pIdx->nColumn, aRegIdx[ix]); - sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v, pIdx), P4_TRANSIENT); VdbeComment((v, "for %s", pIdx->zName)); sqlite3ExprCacheAffinityChange(pParse, regIdx, pIdx->nColumn); @@ -94295,7 +94867,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( /* Check to see if the new index entry will be unique */ sqlite3VdbeAddOp4Int(v, OP_NoConflict, iThisCur, addrUniqueOk, - regIdx, pIdx->nKeyCol); + regIdx, pIdx->nKeyCol); VdbeCoverage(v); /* Generate code to handle collisions */ regR = (pIdx==pPk) ? regIdx : sqlite3GetTempRange(pParse, nPkField); @@ -94306,6 +94878,8 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( ** is different from old-rowid */ if( isUpdate ){ sqlite3VdbeAddOp3(v, OP_Eq, regR, addrUniqueOk, regOldData); + sqlite3VdbeChangeP5(v, SQLITE_NOTNULL); + VdbeCoverage(v); } }else{ int x; @@ -94341,6 +94915,9 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( sqlite3VdbeAddOp4(v, op, regOldData+1+x, addrJump, regCmp+i, p4, P4_COLLSEQ ); + sqlite3VdbeChangeP5(v, SQLITE_NOTNULL); + VdbeCoverageIf(v, op==OP_Eq); + VdbeCoverageIf(v, op==OP_Ne); } } } @@ -94412,14 +94989,17 @@ SQLITE_PRIVATE void sqlite3CompleteInsertion( int regData; /* Content registers (after the rowid) */ int regRec; /* Register holding assemblied record for the table */ int i; /* Loop counter */ + u8 bAffinityDone = 0; /* True if OP_Affinity has been run already */ v = sqlite3GetVdbe(pParse); assert( v!=0 ); assert( pTab->pSelect==0 ); /* This table is not a VIEW */ for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ if( aRegIdx[i]==0 ) continue; + bAffinityDone = 1; if( pIdx->pPartIdxWhere ){ sqlite3VdbeAddOp2(v, OP_IsNull, aRegIdx[i], sqlite3VdbeCurrentAddr(v)+2); + VdbeCoverage(v); } sqlite3VdbeAddOp2(v, OP_IdxInsert, iIdxCur+i, aRegIdx[i]); pik_flags = 0; @@ -94434,7 +95014,7 @@ SQLITE_PRIVATE void sqlite3CompleteInsertion( regData = regNewData + 1; regRec = sqlite3GetTempReg(pParse); sqlite3VdbeAddOp3(v, OP_MakeRecord, regData, pTab->nCol, regRec); - sqlite3TableAffinityStr(v, pTab); + if( !bAffinityDone ) sqlite3TableAffinity(v, pTab, 0); sqlite3ExprCacheAffinityChange(pParse, regData, pTab->nCol); if( pParse->nested ){ pik_flags = 0; @@ -94803,16 +95383,17 @@ static int xferOptimization( ** ** (3) onError is something other than OE_Abort and OE_Rollback. */ - addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iDest, 0); + addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iDest, 0); VdbeCoverage(v); emptyDestTest = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0); sqlite3VdbeJumpHere(v, addr1); } if( HasRowid(pSrc) ){ sqlite3OpenTable(pParse, iSrc, iDbSrc, pSrc, OP_OpenRead); - emptySrcTest = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0); + emptySrcTest = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0); VdbeCoverage(v); if( pDest->iPKey>=0 ){ addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid); addr2 = sqlite3VdbeAddOp3(v, OP_NotExists, iDest, 0, regRowid); + VdbeCoverage(v); sqlite3RowidConstraint(pParse, onError, pDest); sqlite3VdbeJumpHere(v, addr2); autoIncStep(pParse, regAutoinc, regRowid); @@ -94826,7 +95407,7 @@ static int xferOptimization( sqlite3VdbeAddOp3(v, OP_Insert, iDest, regData, regRowid); sqlite3VdbeChangeP5(v, OPFLAG_NCHANGE|OPFLAG_LASTROWID|OPFLAG_APPEND); sqlite3VdbeChangeP4(v, -1, pDest->zName, 0); - sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1); + sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1); VdbeCoverage(v); sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0); sqlite3VdbeAddOp2(v, OP_Close, iDest, 0); }else{ @@ -94845,15 +95426,15 @@ static int xferOptimization( sqlite3VdbeSetP4KeyInfo(pParse, pDestIdx); sqlite3VdbeChangeP5(v, OPFLAG_BULKCSR); VdbeComment((v, "%s", pDestIdx->zName)); - addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0); + addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0); VdbeCoverage(v); sqlite3VdbeAddOp2(v, OP_RowKey, iSrc, regData); sqlite3VdbeAddOp3(v, OP_IdxInsert, iDest, regData, 1); - sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1+1); + sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1+1); VdbeCoverage(v); sqlite3VdbeJumpHere(v, addr1); sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0); sqlite3VdbeAddOp2(v, OP_Close, iDest, 0); } - sqlite3VdbeJumpHere(v, emptySrcTest); + if( emptySrcTest ) sqlite3VdbeJumpHere(v, emptySrcTest); sqlite3ReleaseTempReg(pParse, regRowid); sqlite3ReleaseTempReg(pParse, regData); if( emptyDestTest ){ @@ -97087,6 +97668,7 @@ SQLITE_PRIVATE void sqlite3Pragma( ** size of historical compatibility. */ case PragTyp_DEFAULT_CACHE_SIZE: { + static const int iLn = VDBE_OFFSET_LINENO(2); static const VdbeOpList getCacheSize[] = { { OP_Transaction, 0, 0, 0}, /* 0 */ { OP_ReadCookie, 0, 1, BTREE_DEFAULT_CACHE_SIZE}, /* 1 */ @@ -97104,7 +97686,7 @@ SQLITE_PRIVATE void sqlite3Pragma( sqlite3VdbeSetNumCols(v, 1); sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "cache_size", SQLITE_STATIC); pParse->nMem += 2; - addr = sqlite3VdbeAddOpList(v, ArraySize(getCacheSize), getCacheSize); + addr = sqlite3VdbeAddOpList(v, ArraySize(getCacheSize), getCacheSize,iLn); sqlite3VdbeChangeP1(v, addr, iDb); sqlite3VdbeChangeP1(v, addr+1, iDb); sqlite3VdbeChangeP1(v, addr+6, SQLITE_DEFAULT_CACHE_SIZE); @@ -97349,6 +97931,7 @@ SQLITE_PRIVATE void sqlite3Pragma( ** file. Before writing to meta[6], check that meta[3] indicates ** that this really is an auto-vacuum capable database. */ + static const int iLn = VDBE_OFFSET_LINENO(2); static const VdbeOpList setMeta6[] = { { OP_Transaction, 0, 1, 0}, /* 0 */ { OP_ReadCookie, 0, 1, BTREE_LARGEST_ROOT_PAGE}, @@ -97358,7 +97941,7 @@ SQLITE_PRIVATE void sqlite3Pragma( { OP_SetCookie, 0, BTREE_INCR_VACUUM, 1}, /* 5 */ }; int iAddr; - iAddr = sqlite3VdbeAddOpList(v, ArraySize(setMeta6), setMeta6); + iAddr = sqlite3VdbeAddOpList(v, ArraySize(setMeta6), setMeta6, iLn); sqlite3VdbeChangeP1(v, iAddr, iDb); sqlite3VdbeChangeP1(v, iAddr+1, iDb); sqlite3VdbeChangeP2(v, iAddr+2, iAddr+4); @@ -97384,10 +97967,10 @@ SQLITE_PRIVATE void sqlite3Pragma( } sqlite3BeginWriteOperation(pParse, 0, iDb); sqlite3VdbeAddOp2(v, OP_Integer, iLimit, 1); - addr = sqlite3VdbeAddOp1(v, OP_IncrVacuum, iDb); + addr = sqlite3VdbeAddOp1(v, OP_IncrVacuum, iDb); VdbeCoverage(v); sqlite3VdbeAddOp1(v, OP_ResultRow, 1); sqlite3VdbeAddOp2(v, OP_AddImm, 1, -1); - sqlite3VdbeAddOp2(v, OP_IfPos, 1, addr); + sqlite3VdbeAddOp2(v, OP_IfPos, 1, addr); VdbeCoverage(v); sqlite3VdbeJumpHere(v, addr); break; } @@ -97958,7 +98541,7 @@ SQLITE_PRIVATE void sqlite3Pragma( assert( pParse->nErr>0 || pFK==0 ); if( pFK ) break; if( pParse->nTab<i ) pParse->nTab = i; - addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, 0); + addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, 0); VdbeCoverage(v); for(i=1, pFK=pTab->pFKey; pFK; i++, pFK=pFK->pNextFrom){ pParent = sqlite3FindTable(db, pFK->zTo, zDb); pIdx = 0; @@ -97974,26 +98557,26 @@ SQLITE_PRIVATE void sqlite3Pragma( if( iKey!=pTab->iPKey ){ sqlite3VdbeAddOp3(v, OP_Column, 0, iKey, regRow); sqlite3ColumnDefault(v, pTab, iKey, regRow); - sqlite3VdbeAddOp2(v, OP_IsNull, regRow, addrOk); - sqlite3VdbeAddOp2(v, OP_MustBeInt, regRow, - sqlite3VdbeCurrentAddr(v)+3); + sqlite3VdbeAddOp2(v, OP_IsNull, regRow, addrOk); VdbeCoverage(v); + sqlite3VdbeAddOp2(v, OP_MustBeInt, regRow, + sqlite3VdbeCurrentAddr(v)+3); VdbeCoverage(v); }else{ sqlite3VdbeAddOp2(v, OP_Rowid, 0, regRow); } - sqlite3VdbeAddOp3(v, OP_NotExists, i, 0, regRow); + sqlite3VdbeAddOp3(v, OP_NotExists, i, 0, regRow); VdbeCoverage(v); sqlite3VdbeAddOp2(v, OP_Goto, 0, addrOk); sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2); }else{ for(j=0; j<pFK->nCol; j++){ sqlite3ExprCodeGetColumnOfTable(v, pTab, 0, aiCols ? aiCols[j] : pFK->aCol[j].iFrom, regRow+j); - sqlite3VdbeAddOp2(v, OP_IsNull, regRow+j, addrOk); + sqlite3VdbeAddOp2(v, OP_IsNull, regRow+j, addrOk); VdbeCoverage(v); } if( pParent ){ - sqlite3VdbeAddOp3(v, OP_MakeRecord, regRow, pFK->nCol, regKey); - sqlite3VdbeChangeP4(v, -1, - sqlite3IndexAffinityStr(v,pIdx), P4_TRANSIENT); + sqlite3VdbeAddOp4(v, OP_MakeRecord, regRow, pFK->nCol, regKey, + sqlite3IndexAffinityStr(v,pIdx), pFK->nCol); sqlite3VdbeAddOp4Int(v, OP_Found, i, addrOk, regKey, 0); + VdbeCoverage(v); } } sqlite3VdbeAddOp2(v, OP_Rowid, 0, regResult+1); @@ -98004,7 +98587,7 @@ SQLITE_PRIVATE void sqlite3Pragma( sqlite3VdbeResolveLabel(v, addrOk); sqlite3DbFree(db, aiCols); } - sqlite3VdbeAddOp2(v, OP_Next, 0, addrTop+1); + sqlite3VdbeAddOp2(v, OP_Next, 0, addrTop+1); VdbeCoverage(v); sqlite3VdbeJumpHere(v, addrTop); } } @@ -98051,6 +98634,7 @@ SQLITE_PRIVATE void sqlite3Pragma( ** messages have been generated, output OK. Otherwise output the ** error message */ + static const int iLn = VDBE_OFFSET_LINENO(2); static const VdbeOpList endCode[] = { { OP_AddImm, 1, 0, 0}, /* 0 */ { OP_IfNeg, 1, 0, 0}, /* 1 */ @@ -98099,6 +98683,7 @@ SQLITE_PRIVATE void sqlite3Pragma( sqlite3CodeVerifySchema(pParse, i); addr = sqlite3VdbeAddOp1(v, OP_IfPos, 1); /* Halt if out of errors */ + VdbeCoverage(v); sqlite3VdbeAddOp2(v, OP_Halt, 0, 0); sqlite3VdbeJumpHere(v, addr); @@ -98130,7 +98715,7 @@ SQLITE_PRIVATE void sqlite3Pragma( /* Do the b-tree integrity checks */ sqlite3VdbeAddOp3(v, OP_IntegrityCk, 2, cnt, 1); sqlite3VdbeChangeP5(v, (u8)i); - addr = sqlite3VdbeAddOp1(v, OP_IsNull, 2); + addr = sqlite3VdbeAddOp1(v, OP_IsNull, 2); VdbeCoverage(v); sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, sqlite3MPrintf(db, "*** in database %s ***\n", db->aDb[i].zName), P4_DYNAMIC); @@ -98152,6 +98737,7 @@ SQLITE_PRIVATE void sqlite3Pragma( if( pTab->pIndex==0 ) continue; pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab); addr = sqlite3VdbeAddOp1(v, OP_IfPos, 1); /* Stop if out of errors */ + VdbeCoverage(v); sqlite3VdbeAddOp2(v, OP_Halt, 0, 0); sqlite3VdbeJumpHere(v, addr); sqlite3ExprCacheClear(pParse); @@ -98162,7 +98748,7 @@ SQLITE_PRIVATE void sqlite3Pragma( sqlite3VdbeAddOp2(v, OP_Integer, 0, 8+j); /* index entries counter */ } pParse->nMem = MAX(pParse->nMem, 8+j); - sqlite3VdbeAddOp2(v, OP_Rewind, iDataCur, 0); + sqlite3VdbeAddOp2(v, OP_Rewind, iDataCur, 0); VdbeCoverage(v); loopTop = sqlite3VdbeAddOp2(v, OP_AddImm, 7, 1); for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ int jmp2, jmp3, jmp4; @@ -98172,7 +98758,7 @@ SQLITE_PRIVATE void sqlite3Pragma( pPrior = pIdx; sqlite3VdbeAddOp2(v, OP_AddImm, 8+j, 1); /* increment entry count */ jmp2 = sqlite3VdbeAddOp4Int(v, OP_Found, iIdxCur+j, 0, r1, - pIdx->nColumn); + pIdx->nColumn); VdbeCoverage(v); sqlite3VdbeAddOp2(v, OP_AddImm, 1, -1); /* Decrement error limit */ sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, "row ", P4_STATIC); sqlite3VdbeAddOp3(v, OP_Concat, 7, 3, 3); @@ -98182,13 +98768,13 @@ SQLITE_PRIVATE void sqlite3Pragma( sqlite3VdbeAddOp4(v, OP_String8, 0, 4, 0, pIdx->zName, P4_TRANSIENT); sqlite3VdbeAddOp3(v, OP_Concat, 4, 3, 3); sqlite3VdbeAddOp2(v, OP_ResultRow, 3, 1); - jmp4 = sqlite3VdbeAddOp1(v, OP_IfPos, 1); + jmp4 = sqlite3VdbeAddOp1(v, OP_IfPos, 1); VdbeCoverage(v); sqlite3VdbeAddOp0(v, OP_Halt); sqlite3VdbeJumpHere(v, jmp4); sqlite3VdbeJumpHere(v, jmp2); sqlite3VdbeResolveLabel(v, jmp3); } - sqlite3VdbeAddOp2(v, OP_Next, iDataCur, loopTop); + sqlite3VdbeAddOp2(v, OP_Next, iDataCur, loopTop); VdbeCoverage(v); sqlite3VdbeJumpHere(v, loopTop-1); #ifndef SQLITE_OMIT_BTREECOUNT sqlite3VdbeAddOp4(v, OP_String8, 0, 2, 0, @@ -98196,10 +98782,11 @@ SQLITE_PRIVATE void sqlite3Pragma( for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ if( pPk==pIdx ) continue; addr = sqlite3VdbeCurrentAddr(v); - sqlite3VdbeAddOp2(v, OP_IfPos, 1, addr+2); + sqlite3VdbeAddOp2(v, OP_IfPos, 1, addr+2); VdbeCoverage(v); sqlite3VdbeAddOp2(v, OP_Halt, 0, 0); sqlite3VdbeAddOp2(v, OP_Count, iIdxCur+j, 3); - sqlite3VdbeAddOp3(v, OP_Eq, 8+j, addr+8, 3); + sqlite3VdbeAddOp3(v, OP_Eq, 8+j, addr+8, 3); VdbeCoverage(v); + sqlite3VdbeChangeP5(v, SQLITE_NOTNULL); sqlite3VdbeAddOp2(v, OP_AddImm, 1, -1); sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, pIdx->zName, P4_TRANSIENT); sqlite3VdbeAddOp3(v, OP_Concat, 3, 2, 7); @@ -98208,7 +98795,7 @@ SQLITE_PRIVATE void sqlite3Pragma( #endif /* SQLITE_OMIT_BTREECOUNT */ } } - addr = sqlite3VdbeAddOpList(v, ArraySize(endCode), endCode); + addr = sqlite3VdbeAddOpList(v, ArraySize(endCode), endCode, iLn); sqlite3VdbeChangeP2(v, addr, -mxErr); sqlite3VdbeJumpHere(v, addr+1); sqlite3VdbeChangeP4(v, addr+2, "ok", P4_STATIC); @@ -98346,7 +98933,7 @@ SQLITE_PRIVATE void sqlite3Pragma( { OP_Integer, 0, 1, 0}, /* 1 */ { OP_SetCookie, 0, 0, 1}, /* 2 */ }; - int addr = sqlite3VdbeAddOpList(v, ArraySize(setCookie), setCookie); + int addr = sqlite3VdbeAddOpList(v, ArraySize(setCookie), setCookie, 0); sqlite3VdbeChangeP1(v, addr, iDb); sqlite3VdbeChangeP1(v, addr+1, sqlite3Atoi(zRight)); sqlite3VdbeChangeP1(v, addr+2, iDb); @@ -98358,7 +98945,7 @@ SQLITE_PRIVATE void sqlite3Pragma( { OP_ReadCookie, 0, 1, 0}, /* 1 */ { OP_ResultRow, 1, 1, 0} }; - int addr = sqlite3VdbeAddOpList(v, ArraySize(readCookie), readCookie); + int addr = sqlite3VdbeAddOpList(v, ArraySize(readCookie), readCookie, 0); sqlite3VdbeChangeP1(v, addr, iDb); sqlite3VdbeChangeP1(v, addr+1, iDb); sqlite3VdbeChangeP3(v, addr+1, iCookie); @@ -99566,6 +100153,14 @@ SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3 *db, Select *p){ } /* +** Return a pointer to the right-most SELECT statement in a compound. +*/ +static Select *findRightmost(Select *p){ + while( p->pNext ) p = p->pNext; + return p; +} + +/* ** Given 1 to 3 identifiers preceding the JOIN keyword, determine the ** type of join. Return an integer constant that expresses that type ** in terms of the following bit values: @@ -99903,7 +100498,7 @@ static void pushOntoSorter( }else{ iLimit = pSelect->iLimit; } - addr1 = sqlite3VdbeAddOp1(v, OP_IfZero, iLimit); + addr1 = sqlite3VdbeAddOp1(v, OP_IfZero, iLimit); VdbeCoverage(v); sqlite3VdbeAddOp2(v, OP_AddImm, iLimit, -1); addr2 = sqlite3VdbeAddOp0(v, OP_Goto); sqlite3VdbeJumpHere(v, addr1); @@ -99924,7 +100519,7 @@ static void codeOffset( if( iOffset>0 && iContinue!=0 ){ int addr; sqlite3VdbeAddOp2(v, OP_AddImm, iOffset, -1); - addr = sqlite3VdbeAddOp1(v, OP_IfNeg, iOffset); + addr = sqlite3VdbeAddOp1(v, OP_IfNeg, iOffset); VdbeCoverage(v); sqlite3VdbeAddOp2(v, OP_Goto, 0, iContinue); VdbeComment((v, "skip OFFSET records")); sqlite3VdbeJumpHere(v, addr); @@ -99952,7 +100547,7 @@ static void codeDistinct( v = pParse->pVdbe; r1 = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp4Int(v, OP_Found, iTab, addrRepeat, iMem, N); + sqlite3VdbeAddOp4Int(v, OP_Found, iTab, addrRepeat, iMem, N); VdbeCoverage(v); sqlite3VdbeAddOp3(v, OP_MakeRecord, iMem, N, r1); sqlite3VdbeAddOp2(v, OP_IdxInsert, iTab, r1); sqlite3ReleaseTempReg(pParse, r1); @@ -100033,13 +100628,19 @@ static void selectInnerLoop( /* Pull the requested columns. */ nResultCol = pEList->nExpr; + if( pDest->iSdst==0 ){ pDest->iSdst = pParse->nMem+1; - pDest->nSdst = nResultCol; pParse->nMem += nResultCol; - }else{ - assert( pDest->nSdst==nResultCol ); + }else if( pDest->iSdst+nResultCol > pParse->nMem ){ + /* This is an error condition that can result, for example, when a SELECT + ** on the right-hand side of an INSERT contains more result columns than + ** there are columns in the table on the left. The error will be caught + ** and reported later. But we need to make sure enough memory is allocated + ** to avoid other spurious errors in the meantime. */ + pParse->nMem += nResultCol; } + pDest->nSdst = nResultCol; regResult = pDest->iSdst; if( srcTab>=0 ){ for(i=0; i<nResultCol; i++){ @@ -100051,7 +100652,7 @@ static void selectInnerLoop( ** values returned by the SELECT are not required. */ sqlite3ExprCodeExprList(pParse, pEList, regResult, - (eDest==SRT_Output)?SQLITE_ECEL_DUP:0); + (eDest==SRT_Output||eDest==SRT_Coroutine)?SQLITE_ECEL_DUP:0); } /* If the DISTINCT keyword was present on the SELECT statement @@ -100086,9 +100687,11 @@ static void selectInnerLoop( CollSeq *pColl = sqlite3ExprCollSeq(pParse, pEList->a[i].pExpr); if( i<nResultCol-1 ){ sqlite3VdbeAddOp3(v, OP_Ne, regResult+i, iJump, regPrev+i); + VdbeCoverage(v); }else{ sqlite3VdbeAddOp3(v, OP_Eq, regResult+i, iContinue, regPrev+i); - } + VdbeCoverage(v); + } sqlite3VdbeChangeP4(v, -1, (const char *)pColl, P4_COLLSEQ); sqlite3VdbeChangeP5(v, SQLITE_NULLEQ); } @@ -100154,7 +100757,7 @@ static void selectInnerLoop( ** current row to the index and proceed with writing it to the ** output table as well. */ int addr = sqlite3VdbeCurrentAddr(v) + 4; - sqlite3VdbeAddOp4Int(v, OP_Found, iParm+1, addr, r1, 0); + sqlite3VdbeAddOp4Int(v, OP_Found, iParm+1, addr, r1, 0); VdbeCoverage(v); sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm+1, r1); assert( pOrderBy==0 ); } @@ -100221,12 +100824,8 @@ static void selectInnerLoop( } #endif /* #ifndef SQLITE_OMIT_SUBQUERY */ - /* Send the data to the callback function or to a subroutine. In the - ** case of a subroutine, the subroutine itself is responsible for - ** popping the data from the stack. - */ - case SRT_Coroutine: - case SRT_Output: { + case SRT_Coroutine: /* Send data to a co-routine */ + case SRT_Output: { /* Return the results */ testcase( eDest==SRT_Coroutine ); testcase( eDest==SRT_Output ); if( pOrderBy ){ @@ -100262,13 +100861,16 @@ static void selectInnerLoop( r1 = sqlite3GetTempReg(pParse); r2 = sqlite3GetTempRange(pParse, nKey+2); r3 = r2+nKey+1; - sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nResultCol, r3); if( eDest==SRT_DistQueue ){ /* If the destination is DistQueue, then cursor (iParm+1) is open ** on a second ephemeral index that holds all values every previously - ** added to the queue. Only add this new value if it has never before - ** been added */ - addrTest = sqlite3VdbeAddOp4Int(v, OP_Found, iParm+1, 0, r3, 0); + ** added to the queue. */ + addrTest = sqlite3VdbeAddOp4Int(v, OP_Found, iParm+1, 0, + regResult, nResultCol); + VdbeCoverage(v); + } + sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nResultCol, r3); + if( eDest==SRT_DistQueue ){ sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm+1, r3); sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); } @@ -100307,7 +100909,7 @@ static void selectInnerLoop( ** the output for us. */ if( pOrderBy==0 && p->iLimit ){ - sqlite3VdbeAddOp3(v, OP_IfZero, p->iLimit, iBreak, -1); + sqlite3VdbeAddOp3(v, OP_IfZero, p->iLimit, iBreak, -1); VdbeCoverage(v); } } @@ -100526,12 +101128,13 @@ static void generateSortTail( int ptab2 = pParse->nTab++; sqlite3VdbeAddOp3(v, OP_OpenPseudo, ptab2, regSortOut, pOrderBy->nExpr+2); addr = 1 + sqlite3VdbeAddOp2(v, OP_SorterSort, iTab, addrBreak); + VdbeCoverage(v); codeOffset(v, p->iOffset, addrContinue); sqlite3VdbeAddOp2(v, OP_SorterData, iTab, regSortOut); sqlite3VdbeAddOp3(v, OP_Column, ptab2, pOrderBy->nExpr+1, regRow); sqlite3VdbeChangeP5(v, OPFLAG_CLEARCACHE); }else{ - addr = 1 + sqlite3VdbeAddOp2(v, OP_Sort, iTab, addrBreak); + addr = 1 + sqlite3VdbeAddOp2(v, OP_Sort, iTab, addrBreak); VdbeCoverage(v); codeOffset(v, p->iOffset, addrContinue); sqlite3VdbeAddOp3(v, OP_Column, iTab, pOrderBy->nExpr+1, regRow); } @@ -100589,9 +101192,9 @@ static void generateSortTail( */ sqlite3VdbeResolveLabel(v, addrContinue); if( p->selFlags & SF_UseSorter ){ - sqlite3VdbeAddOp2(v, OP_SorterNext, iTab, addr); + sqlite3VdbeAddOp2(v, OP_SorterNext, iTab, addr); VdbeCoverage(v); }else{ - sqlite3VdbeAddOp2(v, OP_Next, iTab, addr); + sqlite3VdbeAddOp2(v, OP_Next, iTab, addr); VdbeCoverage(v); } sqlite3VdbeResolveLabel(v, addrBreak); if( eDest==SRT_Output || eDest==SRT_Coroutine ){ @@ -100962,7 +101565,7 @@ static int selectColumnsFromExprList( char *zNewName; int k; for(k=nName-1; k>1 && sqlite3Isdigit(zName[k]); k--){} - if( zName[k]==':' ) nName = k; + if( k>=0 && zName[k]==':' ) nName = k; zName[nName] = 0; zNewName = sqlite3MPrintf(db, "%s:%d", zName, ++cnt); sqlite3DbFree(db, zName); @@ -101075,11 +101678,13 @@ SQLITE_PRIVATE Vdbe *sqlite3GetVdbe(Parse *pParse){ Vdbe *v = pParse->pVdbe; if( v==0 ){ v = pParse->pVdbe = sqlite3VdbeCreate(pParse); -#ifndef SQLITE_OMIT_TRACE - if( v ){ - sqlite3VdbeAddOp0(v, OP_Trace); + if( v ) sqlite3VdbeAddOp0(v, OP_Init); + if( pParse->pToplevel==0 + && OptimizationEnabled(pParse->db,SQLITE_FactorOutConst) + ){ + pParse->okConstFactor = 1; } -#endif + } return v; } @@ -101137,22 +101742,22 @@ static void computeLimitRegisters(Parse *pParse, Select *p, int iBreak){ } }else{ sqlite3ExprCode(pParse, p->pLimit, iLimit); - sqlite3VdbeAddOp1(v, OP_MustBeInt, iLimit); + sqlite3VdbeAddOp1(v, OP_MustBeInt, iLimit); VdbeCoverage(v); VdbeComment((v, "LIMIT counter")); - sqlite3VdbeAddOp2(v, OP_IfZero, iLimit, iBreak); + sqlite3VdbeAddOp2(v, OP_IfZero, iLimit, iBreak); VdbeCoverage(v); } if( p->pOffset ){ p->iOffset = iOffset = ++pParse->nMem; pParse->nMem++; /* Allocate an extra register for limit+offset */ sqlite3ExprCode(pParse, p->pOffset, iOffset); - sqlite3VdbeAddOp1(v, OP_MustBeInt, iOffset); + sqlite3VdbeAddOp1(v, OP_MustBeInt, iOffset); VdbeCoverage(v); VdbeComment((v, "OFFSET counter")); - addr1 = sqlite3VdbeAddOp1(v, OP_IfPos, iOffset); + addr1 = sqlite3VdbeAddOp1(v, OP_IfPos, iOffset); VdbeCoverage(v); sqlite3VdbeAddOp2(v, OP_Integer, 0, iOffset); sqlite3VdbeJumpHere(v, addr1); sqlite3VdbeAddOp3(v, OP_Add, iLimit, iOffset, iOffset+1); VdbeComment((v, "LIMIT+OFFSET")); - addr1 = sqlite3VdbeAddOp1(v, OP_IfPos, iLimit); + addr1 = sqlite3VdbeAddOp1(v, OP_IfPos, iLimit); VdbeCoverage(v); sqlite3VdbeAddOp2(v, OP_Integer, -1, iOffset+1); sqlite3VdbeJumpHere(v, addr1); } @@ -101335,11 +101940,13 @@ static void generateWithRecursiveQuery( p->pOrderBy = 0; /* Store the results of the setup-query in Queue. */ + pSetup->pNext = 0; rc = sqlite3Select(pParse, pSetup, &destQueue); + pSetup->pNext = p; if( rc ) goto end_of_recursive_query; /* Find the next row in the Queue and output that row */ - addrTop = sqlite3VdbeAddOp2(v, OP_Rewind, iQueue, addrBreak); + addrTop = sqlite3VdbeAddOp2(v, OP_Rewind, iQueue, addrBreak); VdbeCoverage(v); /* Transfer the next row in Queue over to Current */ sqlite3VdbeAddOp1(v, OP_NullRow, iCurrent); /* To reset column cache */ @@ -101355,7 +101962,10 @@ static void generateWithRecursiveQuery( codeOffset(v, regOffset, addrCont); selectInnerLoop(pParse, p, p->pEList, iCurrent, 0, 0, pDest, addrCont, addrBreak); - if( regLimit ) sqlite3VdbeAddOp3(v, OP_IfZero, regLimit, addrBreak, -1); + if( regLimit ){ + sqlite3VdbeAddOp3(v, OP_IfZero, regLimit, addrBreak, -1); + VdbeCoverage(v); + } sqlite3VdbeResolveLabel(v, addrCont); /* Execute the recursive SELECT taking the single row in Current as @@ -101440,8 +102050,6 @@ static int multiSelect( assert( (p->selFlags & SF_Recursive)==0 || p->op==TK_ALL || p->op==TK_UNION ); db = pParse->db; pPrior = p->pPrior; - assert( pPrior->pRightmost!=pPrior ); - assert( pPrior->pRightmost==p->pRightmost ); dest = *pDest; if( pPrior->pOrderBy ){ sqlite3ErrorMsg(pParse,"ORDER BY clause should come after %s not before", @@ -101517,7 +102125,7 @@ static int multiSelect( p->iLimit = pPrior->iLimit; p->iOffset = pPrior->iOffset; if( p->iLimit ){ - addr = sqlite3VdbeAddOp1(v, OP_IfZero, p->iLimit); + addr = sqlite3VdbeAddOp1(v, OP_IfZero, p->iLimit); VdbeCoverage(v); VdbeComment((v, "Jump ahead if LIMIT reached")); } explainSetInteger(iSub2, pParse->iNextSelectId); @@ -101549,12 +102157,10 @@ static int multiSelect( testcase( p->op==TK_EXCEPT ); testcase( p->op==TK_UNION ); priorOp = SRT_Union; - if( dest.eDest==priorOp && ALWAYS(!p->pLimit &&!p->pOffset) ){ + if( dest.eDest==priorOp ){ /* We can reuse a temporary table generated by a SELECT to our ** right. */ - assert( p->pRightmost!=p ); /* Can only happen for leftward elements - ** of a 3-way or more compound */ assert( p->pLimit==0 ); /* Not allowed on leftward elements */ assert( p->pOffset==0 ); /* Not allowed on leftward elements */ unionTab = dest.iSDParm; @@ -101567,7 +102173,7 @@ static int multiSelect( addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, unionTab, 0); assert( p->addrOpenEphm[0] == -1 ); p->addrOpenEphm[0] = addr; - p->pRightmost->selFlags |= SF_UsesEphemeral; + findRightmost(p)->selFlags |= SF_UsesEphemeral; assert( p->pEList ); } @@ -101626,12 +102232,12 @@ static int multiSelect( iBreak = sqlite3VdbeMakeLabel(v); iCont = sqlite3VdbeMakeLabel(v); computeLimitRegisters(pParse, p, iBreak); - sqlite3VdbeAddOp2(v, OP_Rewind, unionTab, iBreak); + sqlite3VdbeAddOp2(v, OP_Rewind, unionTab, iBreak); VdbeCoverage(v); iStart = sqlite3VdbeCurrentAddr(v); selectInnerLoop(pParse, p, p->pEList, unionTab, 0, 0, &dest, iCont, iBreak); sqlite3VdbeResolveLabel(v, iCont); - sqlite3VdbeAddOp2(v, OP_Next, unionTab, iStart); + sqlite3VdbeAddOp2(v, OP_Next, unionTab, iStart); VdbeCoverage(v); sqlite3VdbeResolveLabel(v, iBreak); sqlite3VdbeAddOp2(v, OP_Close, unionTab, 0); } @@ -101656,7 +102262,7 @@ static int multiSelect( addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, tab1, 0); assert( p->addrOpenEphm[0] == -1 ); p->addrOpenEphm[0] = addr; - p->pRightmost->selFlags |= SF_UsesEphemeral; + findRightmost(p)->selFlags |= SF_UsesEphemeral; assert( p->pEList ); /* Code the SELECTs to our left into temporary table "tab1". @@ -101701,15 +102307,15 @@ static int multiSelect( iBreak = sqlite3VdbeMakeLabel(v); iCont = sqlite3VdbeMakeLabel(v); computeLimitRegisters(pParse, p, iBreak); - sqlite3VdbeAddOp2(v, OP_Rewind, tab1, iBreak); + sqlite3VdbeAddOp2(v, OP_Rewind, tab1, iBreak); VdbeCoverage(v); r1 = sqlite3GetTempReg(pParse); iStart = sqlite3VdbeAddOp2(v, OP_RowKey, tab1, r1); - sqlite3VdbeAddOp4Int(v, OP_NotFound, tab2, iCont, r1, 0); + sqlite3VdbeAddOp4Int(v, OP_NotFound, tab2, iCont, r1, 0); VdbeCoverage(v); sqlite3ReleaseTempReg(pParse, r1); selectInnerLoop(pParse, p, p->pEList, tab1, 0, 0, &dest, iCont, iBreak); sqlite3VdbeResolveLabel(v, iCont); - sqlite3VdbeAddOp2(v, OP_Next, tab1, iStart); + sqlite3VdbeAddOp2(v, OP_Next, tab1, iStart); VdbeCoverage(v); sqlite3VdbeResolveLabel(v, iBreak); sqlite3VdbeAddOp2(v, OP_Close, tab2, 0); sqlite3VdbeAddOp2(v, OP_Close, tab1, 0); @@ -101735,7 +102341,7 @@ static int multiSelect( CollSeq **apColl; /* For looping through pKeyInfo->aColl[] */ int nCol; /* Number of columns in result set */ - assert( p->pRightmost==p ); + assert( p->pNext==0 ); nCol = p->pEList->nExpr; pKeyInfo = sqlite3KeyInfoAlloc(db, nCol, 1); if( !pKeyInfo ){ @@ -101816,10 +102422,10 @@ static int generateOutputSubroutine( */ if( regPrev ){ int j1, j2; - j1 = sqlite3VdbeAddOp1(v, OP_IfNot, regPrev); + j1 = sqlite3VdbeAddOp1(v, OP_IfNot, regPrev); VdbeCoverage(v); j2 = sqlite3VdbeAddOp4(v, OP_Compare, pIn->iSdst, regPrev+1, pIn->nSdst, (char*)sqlite3KeyInfoRef(pKeyInfo), P4_KEYINFO); - sqlite3VdbeAddOp3(v, OP_Jump, j2+2, iContinue, j2+2); + sqlite3VdbeAddOp3(v, OP_Jump, j2+2, iContinue, j2+2); VdbeCoverage(v); sqlite3VdbeJumpHere(v, j1); sqlite3VdbeAddOp3(v, OP_Copy, pIn->iSdst, regPrev+1, pIn->nSdst-1); sqlite3VdbeAddOp2(v, OP_Integer, 1, regPrev); @@ -101920,7 +102526,7 @@ static int generateOutputSubroutine( /* Jump to the end of the loop if the LIMIT is reached. */ if( p->iLimit ){ - sqlite3VdbeAddOp3(v, OP_IfZero, p->iLimit, iBreak, -1); + sqlite3VdbeAddOp3(v, OP_IfZero, p->iLimit, iBreak, -1); VdbeCoverage(v); } /* Generate the subroutine return @@ -102028,9 +102634,7 @@ static int multiSelectOrderBy( SelectDest destA; /* Destination for coroutine A */ SelectDest destB; /* Destination for coroutine B */ int regAddrA; /* Address register for select-A coroutine */ - int regEofA; /* Flag to indicate when select-A is complete */ int regAddrB; /* Address register for select-B coroutine */ - int regEofB; /* Flag to indicate when select-B is complete */ int addrSelectA; /* Address of the select-A coroutine */ int addrSelectB; /* Address of the select-B coroutine */ int regOutA; /* Address register for the output-A subroutine */ @@ -102038,6 +102642,7 @@ static int multiSelectOrderBy( int addrOutA; /* Address of the output-A subroutine */ int addrOutB = 0; /* Address of the output-B subroutine */ int addrEofA; /* Address of the select-A-exhausted subroutine */ + int addrEofA_noB; /* Alternate addrEofA if B is uninitialized */ int addrEofB; /* Address of the select-B-exhausted subroutine */ int addrAltB; /* Address of the A<B subroutine */ int addrAeqB; /* Address of the A==B subroutine */ @@ -102152,6 +102757,7 @@ static int multiSelectOrderBy( /* Separate the left and the right query from one another */ p->pPrior = 0; + pPrior->pNext = 0; sqlite3ResolveOrderGroupBy(pParse, p, p->pOrderBy, "ORDER"); if( pPrior->pPrior==0 ){ sqlite3ResolveOrderGroupBy(pParse, pPrior, pPrior->pOrderBy, "ORDER"); @@ -102174,37 +102780,30 @@ static int multiSelectOrderBy( p->pOffset = 0; regAddrA = ++pParse->nMem; - regEofA = ++pParse->nMem; regAddrB = ++pParse->nMem; - regEofB = ++pParse->nMem; regOutA = ++pParse->nMem; regOutB = ++pParse->nMem; sqlite3SelectDestInit(&destA, SRT_Coroutine, regAddrA); sqlite3SelectDestInit(&destB, SRT_Coroutine, regAddrB); - /* Jump past the various subroutines and coroutines to the main - ** merge loop - */ - j1 = sqlite3VdbeAddOp0(v, OP_Goto); - addrSelectA = sqlite3VdbeCurrentAddr(v); - - /* Generate a coroutine to evaluate the SELECT statement to the ** left of the compound operator - the "A" select. */ - VdbeNoopComment((v, "Begin coroutine for left SELECT")); + addrSelectA = sqlite3VdbeCurrentAddr(v) + 1; + j1 = sqlite3VdbeAddOp3(v, OP_InitCoroutine, regAddrA, 0, addrSelectA); + VdbeComment((v, "left SELECT")); pPrior->iLimit = regLimitA; explainSetInteger(iSub1, pParse->iNextSelectId); sqlite3Select(pParse, pPrior, &destA); - sqlite3VdbeAddOp2(v, OP_Integer, 1, regEofA); - sqlite3VdbeAddOp1(v, OP_Yield, regAddrA); - VdbeNoopComment((v, "End coroutine for left SELECT")); + sqlite3VdbeAddOp1(v, OP_EndCoroutine, regAddrA); + sqlite3VdbeJumpHere(v, j1); /* Generate a coroutine to evaluate the SELECT statement on ** the right - the "B" select */ - addrSelectB = sqlite3VdbeCurrentAddr(v); - VdbeNoopComment((v, "Begin coroutine for right SELECT")); + addrSelectB = sqlite3VdbeCurrentAddr(v) + 1; + j1 = sqlite3VdbeAddOp3(v, OP_InitCoroutine, regAddrB, 0, addrSelectB); + VdbeComment((v, "right SELECT")); savedLimit = p->iLimit; savedOffset = p->iOffset; p->iLimit = regLimitB; @@ -102213,9 +102812,7 @@ static int multiSelectOrderBy( sqlite3Select(pParse, p, &destB); p->iLimit = savedLimit; p->iOffset = savedOffset; - sqlite3VdbeAddOp2(v, OP_Integer, 1, regEofB); - sqlite3VdbeAddOp1(v, OP_Yield, regAddrB); - VdbeNoopComment((v, "End coroutine for right SELECT")); + sqlite3VdbeAddOp1(v, OP_EndCoroutine, regAddrB); /* Generate a subroutine that outputs the current row of the A ** select as the next output row of the compound select. @@ -102239,13 +102836,13 @@ static int multiSelectOrderBy( /* Generate a subroutine to run when the results from select A ** are exhausted and only data in select B remains. */ - VdbeNoopComment((v, "eof-A subroutine")); if( op==TK_EXCEPT || op==TK_INTERSECT ){ - addrEofA = sqlite3VdbeAddOp2(v, OP_Goto, 0, labelEnd); + addrEofA_noB = addrEofA = labelEnd; }else{ - addrEofA = sqlite3VdbeAddOp2(v, OP_If, regEofB, labelEnd); - sqlite3VdbeAddOp2(v, OP_Gosub, regOutB, addrOutB); - sqlite3VdbeAddOp1(v, OP_Yield, regAddrB); + VdbeNoopComment((v, "eof-A subroutine")); + addrEofA = sqlite3VdbeAddOp2(v, OP_Gosub, regOutB, addrOutB); + addrEofA_noB = sqlite3VdbeAddOp2(v, OP_Yield, regAddrB, labelEnd); + VdbeCoverage(v); sqlite3VdbeAddOp2(v, OP_Goto, 0, addrEofA); p->nSelectRow += pPrior->nSelectRow; } @@ -102258,9 +102855,8 @@ static int multiSelectOrderBy( if( p->nSelectRow > pPrior->nSelectRow ) p->nSelectRow = pPrior->nSelectRow; }else{ VdbeNoopComment((v, "eof-B subroutine")); - addrEofB = sqlite3VdbeAddOp2(v, OP_If, regEofA, labelEnd); - sqlite3VdbeAddOp2(v, OP_Gosub, regOutA, addrOutA); - sqlite3VdbeAddOp1(v, OP_Yield, regAddrA); + addrEofB = sqlite3VdbeAddOp2(v, OP_Gosub, regOutA, addrOutA); + sqlite3VdbeAddOp2(v, OP_Yield, regAddrA, labelEnd); VdbeCoverage(v); sqlite3VdbeAddOp2(v, OP_Goto, 0, addrEofB); } @@ -102268,8 +102864,7 @@ static int multiSelectOrderBy( */ VdbeNoopComment((v, "A-lt-B subroutine")); addrAltB = sqlite3VdbeAddOp2(v, OP_Gosub, regOutA, addrOutA); - sqlite3VdbeAddOp1(v, OP_Yield, regAddrA); - sqlite3VdbeAddOp2(v, OP_If, regEofA, addrEofA); + sqlite3VdbeAddOp2(v, OP_Yield, regAddrA, addrEofA); VdbeCoverage(v); sqlite3VdbeAddOp2(v, OP_Goto, 0, labelCmpr); /* Generate code to handle the case of A==B @@ -102282,8 +102877,7 @@ static int multiSelectOrderBy( }else{ VdbeNoopComment((v, "A-eq-B subroutine")); addrAeqB = - sqlite3VdbeAddOp1(v, OP_Yield, regAddrA); - sqlite3VdbeAddOp2(v, OP_If, regEofA, addrEofA); + sqlite3VdbeAddOp2(v, OP_Yield, regAddrA, addrEofA); VdbeCoverage(v); sqlite3VdbeAddOp2(v, OP_Goto, 0, labelCmpr); } @@ -102294,19 +102888,14 @@ static int multiSelectOrderBy( if( op==TK_ALL || op==TK_UNION ){ sqlite3VdbeAddOp2(v, OP_Gosub, regOutB, addrOutB); } - sqlite3VdbeAddOp1(v, OP_Yield, regAddrB); - sqlite3VdbeAddOp2(v, OP_If, regEofB, addrEofB); + sqlite3VdbeAddOp2(v, OP_Yield, regAddrB, addrEofB); VdbeCoverage(v); sqlite3VdbeAddOp2(v, OP_Goto, 0, labelCmpr); /* This code runs once to initialize everything. */ sqlite3VdbeJumpHere(v, j1); - sqlite3VdbeAddOp2(v, OP_Integer, 0, regEofA); - sqlite3VdbeAddOp2(v, OP_Integer, 0, regEofB); - sqlite3VdbeAddOp2(v, OP_Gosub, regAddrA, addrSelectA); - sqlite3VdbeAddOp2(v, OP_Gosub, regAddrB, addrSelectB); - sqlite3VdbeAddOp2(v, OP_If, regEofA, addrEofA); - sqlite3VdbeAddOp2(v, OP_If, regEofB, addrEofB); + sqlite3VdbeAddOp2(v, OP_Yield, regAddrA, addrEofA_noB); VdbeCoverage(v); + sqlite3VdbeAddOp2(v, OP_Yield, regAddrB, addrEofB); VdbeCoverage(v); /* Implement the main merge loop */ @@ -102315,7 +102904,7 @@ static int multiSelectOrderBy( sqlite3VdbeAddOp4(v, OP_Compare, destA.iSdst, destB.iSdst, nOrderBy, (char*)pKeyMerge, P4_KEYINFO); sqlite3VdbeChangeP5(v, OPFLAG_PERMUTE); - sqlite3VdbeAddOp3(v, OP_Jump, addrAltB, addrAeqB, addrAgtB); + sqlite3VdbeAddOp3(v, OP_Jump, addrAltB, addrAeqB, addrAgtB); VdbeCoverage(v); /* Jump to the this point in order to terminate the query. */ @@ -102335,6 +102924,7 @@ static int multiSelectOrderBy( sqlite3SelectDelete(db, p->pPrior); } p->pPrior = pPrior; + pPrior->pNext = p; /*** TBD: Insert subroutine calls to close cursors on incomplete **** subqueries ****/ @@ -102600,7 +103190,7 @@ static int flattenSubquery( ** and (14). */ if( pSub->pLimit && p->pLimit ) return 0; /* Restriction (13) */ if( pSub->pOffset ) return 0; /* Restriction (14) */ - if( p->pRightmost && pSub->pLimit ){ + if( (p->selFlags & SF_Compound)!=0 && pSub->pLimit ){ return 0; /* Restriction (15) */ } if( pSubSrc->nSrc==0 ) return 0; /* Restriction (7) */ @@ -102751,14 +103341,14 @@ static int flattenSubquery( p->pOrderBy = pOrderBy; p->pSrc = pSrc; p->op = TK_ALL; - p->pRightmost = 0; if( pNew==0 ){ - pNew = pPrior; + p->pPrior = pPrior; }else{ pNew->pPrior = pPrior; - pNew->pRightmost = 0; + if( pPrior ) pPrior->pNext = pNew; + pNew->pNext = p; + p->pPrior = pNew; } - p->pPrior = pNew; if( db->mallocFailed ) return 1; } @@ -103097,6 +103687,10 @@ static int convertCompoundSelectToSubquery(Walker *pWalker, Select *p){ pNew->pHaving = 0; pNew->pOrderBy = 0; p->pPrior = 0; + p->pNext = 0; + p->selFlags &= ~SF_Compound; + assert( pNew->pPrior!=0 ); + pNew->pPrior->pNext = pNew; pNew->pLimit = 0; pNew->pOffset = 0; return WRC_Continue; @@ -103284,9 +103878,10 @@ static int withExpand( */ static void selectPopWith(Walker *pWalker, Select *p){ Parse *pParse = pWalker->pParse; - if( p->pWith ){ - assert( pParse->pWith==p->pWith ); - pParse->pWith = p->pWith->pOuter; + With *pWith = findRightmost(p)->pWith; + if( pWith!=0 ){ + assert( pParse->pWith==pWith ); + pParse->pWith = pWith->pOuter; } } #else @@ -103336,7 +103931,7 @@ static int selectExpander(Walker *pWalker, Select *p){ } pTabList = p->pSrc; pEList = p->pEList; - sqlite3WithPush(pParse, p->pWith, 0); + sqlite3WithPush(pParse, findRightmost(p)->pWith, 0); /* Make sure cursor numbers have been assigned to all entries in ** the FROM clause of the SELECT statement. @@ -103849,7 +104444,7 @@ static void updateAccumulator(Parse *pParse, AggInfo *pAggInfo){ ** values to an OP_Copy. */ if( regHit ){ - addrHitTest = sqlite3VdbeAddOp1(v, OP_If, regHit); + addrHitTest = sqlite3VdbeAddOp1(v, OP_If, regHit); VdbeCoverage(v); } sqlite3ExprCacheClear(pParse); for(i=0, pC=pAggInfo->aCol; i<pAggInfo->nAccumulator; i++, pC++){ @@ -104008,42 +104603,24 @@ SQLITE_PRIVATE int sqlite3Select( p->selFlags |= SF_Aggregate; } i = -1; - }else if( pTabList->nSrc==1 && (p->selFlags & SF_Materialize)==0 - && OptimizationEnabled(db, SQLITE_SubqCoroutine) + }else if( pTabList->nSrc==1 + && OptimizationEnabled(db, SQLITE_SubqCoroutine) ){ /* Implement a co-routine that will return a single row of the result ** set on each invocation. */ - int addrTop; - int addrEof; + int addrTop = sqlite3VdbeCurrentAddr(v)+1; pItem->regReturn = ++pParse->nMem; - addrEof = ++pParse->nMem; - /* Before coding the OP_Goto to jump to the start of the main routine, - ** ensure that the jump to the verify-schema routine has already - ** been coded. Otherwise, the verify-schema would likely be coded as - ** part of the co-routine. If the main routine then accessed the - ** database before invoking the co-routine for the first time (for - ** example to initialize a LIMIT register from a sub-select), it would - ** be doing so without having verified the schema version and obtained - ** the required db locks. See ticket d6b36be38. */ - sqlite3CodeVerifySchema(pParse, -1); - sqlite3VdbeAddOp0(v, OP_Goto); - addrTop = sqlite3VdbeAddOp1(v, OP_OpenPseudo, pItem->iCursor); - sqlite3VdbeChangeP5(v, 1); - VdbeComment((v, "coroutine for %s", pItem->pTab->zName)); + sqlite3VdbeAddOp3(v, OP_InitCoroutine, pItem->regReturn, 0, addrTop); + VdbeComment((v, "%s", pItem->pTab->zName)); pItem->addrFillSub = addrTop; - sqlite3VdbeAddOp2(v, OP_Integer, 0, addrEof); - sqlite3VdbeChangeP5(v, 1); sqlite3SelectDestInit(&dest, SRT_Coroutine, pItem->regReturn); explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId); sqlite3Select(pParse, pSub, &dest); pItem->pTab->nRowEst = (unsigned)pSub->nSelectRow; pItem->viaCoroutine = 1; - sqlite3VdbeChangeP2(v, addrTop, dest.iSdst); - sqlite3VdbeChangeP3(v, addrTop, dest.nSdst); - sqlite3VdbeAddOp2(v, OP_Integer, 1, addrEof); - sqlite3VdbeAddOp1(v, OP_Yield, pItem->regReturn); - VdbeComment((v, "end %s", pItem->pTab->zName)); + pItem->regResult = dest.iSdst; + sqlite3VdbeAddOp1(v, OP_EndCoroutine, pItem->regReturn); sqlite3VdbeJumpHere(v, addrTop-1); sqlite3ClearTempRegCache(pParse); }else{ @@ -104059,12 +104636,14 @@ SQLITE_PRIVATE int sqlite3Select( pItem->regReturn = ++pParse->nMem; topAddr = sqlite3VdbeAddOp2(v, OP_Integer, 0, pItem->regReturn); pItem->addrFillSub = topAddr+1; - VdbeNoopComment((v, "materialize %s", pItem->pTab->zName)); if( pItem->isCorrelated==0 ){ /* If the subquery is not correlated and if we are not inside of ** a trigger, then we only need to compute the value of the subquery ** once. */ - onceAddr = sqlite3CodeOnce(pParse); + onceAddr = sqlite3CodeOnce(pParse); VdbeCoverage(v); + VdbeComment((v, "materialize \"%s\"", pItem->pTab->zName)); + }else{ + VdbeNoopComment((v, "materialize \"%s\"", pItem->pTab->zName)); } sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor); explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId); @@ -104096,21 +104675,6 @@ SQLITE_PRIVATE int sqlite3Select( /* If there is are a sequence of queries, do the earlier ones first. */ if( p->pPrior ){ - if( p->pRightmost==0 ){ - Select *pLoop, *pRight = 0; - int cnt = 0; - int mxSelect; - for(pLoop=p; pLoop; pLoop=pLoop->pPrior, cnt++){ - pLoop->pRightmost = p; - pLoop->pNext = pRight; - pRight = pLoop; - } - mxSelect = db->aLimit[SQLITE_LIMIT_COMPOUND_SELECT]; - if( mxSelect && cnt>mxSelect ){ - sqlite3ErrorMsg(pParse, "too many terms in compound SELECT"); - goto select_end; - } - } rc = multiSelect(pParse, p, pDest); explainSetInteger(pParse->iSelectId, iRestoreSelectId); return rc; @@ -104414,7 +104978,7 @@ SQLITE_PRIVATE int sqlite3Select( sortOut = sqlite3GetTempReg(pParse); sqlite3VdbeAddOp3(v, OP_OpenPseudo, sortPTab, sortOut, nCol); sqlite3VdbeAddOp2(v, OP_SorterSort, sAggInfo.sortingIdx, addrEnd); - VdbeComment((v, "GROUP BY sort")); + VdbeComment((v, "GROUP BY sort")); VdbeCoverage(v); sAggInfo.useSortingIdx = 1; sqlite3ExprCacheClear(pParse); } @@ -104441,7 +105005,7 @@ SQLITE_PRIVATE int sqlite3Select( sqlite3VdbeAddOp4(v, OP_Compare, iAMem, iBMem, pGroupBy->nExpr, (char*)sqlite3KeyInfoRef(pKeyInfo), P4_KEYINFO); j1 = sqlite3VdbeCurrentAddr(v); - sqlite3VdbeAddOp3(v, OP_Jump, j1+1, 0, j1+1); + sqlite3VdbeAddOp3(v, OP_Jump, j1+1, 0, j1+1); VdbeCoverage(v); /* Generate code that runs whenever the GROUP BY changes. ** Changes in the GROUP BY are detected by the previous code @@ -104455,7 +105019,7 @@ SQLITE_PRIVATE int sqlite3Select( sqlite3ExprCodeMove(pParse, iBMem, iAMem, pGroupBy->nExpr); sqlite3VdbeAddOp2(v, OP_Gosub, regOutputRow, addrOutputRow); VdbeComment((v, "output one row")); - sqlite3VdbeAddOp2(v, OP_IfPos, iAbortFlag, addrEnd); + sqlite3VdbeAddOp2(v, OP_IfPos, iAbortFlag, addrEnd); VdbeCoverage(v); VdbeComment((v, "check abort flag")); sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset); VdbeComment((v, "reset accumulator")); @@ -104472,6 +105036,7 @@ SQLITE_PRIVATE int sqlite3Select( */ if( groupBySort ){ sqlite3VdbeAddOp2(v, OP_SorterNext, sAggInfo.sortingIdx, addrTopOfLoop); + VdbeCoverage(v); }else{ sqlite3WhereEnd(pWInfo); sqlite3VdbeChangeToNoop(v, addrSortingIdx); @@ -104499,7 +105064,7 @@ SQLITE_PRIVATE int sqlite3Select( sqlite3VdbeAddOp1(v, OP_Return, regOutputRow); sqlite3VdbeResolveLabel(v, addrOutputRow); addrOutputRow = sqlite3VdbeCurrentAddr(v); - sqlite3VdbeAddOp2(v, OP_IfPos, iUseFlag, addrOutputRow+2); + sqlite3VdbeAddOp2(v, OP_IfPos, iUseFlag, addrOutputRow+2); VdbeCoverage(v); VdbeComment((v, "Groupby result generator entry point")); sqlite3VdbeAddOp1(v, OP_Return, regOutputRow); finalizeAggFunctions(pParse, &sAggInfo); @@ -104772,10 +105337,6 @@ SQLITE_PRIVATE void sqlite3ExplainSelect(Vdbe *pVdbe, Select *p){ sqlite3ExplainPrintf(pVdbe, "(null-select)"); return; } - while( p->pPrior ){ - p->pPrior->pNext = p; - p = p->pPrior; - } sqlite3ExplainPush(pVdbe); while( p ){ explainOneSelect(pVdbe, p); @@ -105560,6 +106121,7 @@ SQLITE_PRIVATE void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){ assert( pTable!=0 ); if( (v = sqlite3GetVdbe(pParse))!=0 ){ int base; + static const int iLn = VDBE_OFFSET_LINENO(2); static const VdbeOpList dropTrigger[] = { { OP_Rewind, 0, ADDR(9), 0}, { OP_String8, 0, 1, 0}, /* 1 */ @@ -105574,7 +106136,7 @@ SQLITE_PRIVATE void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){ sqlite3BeginWriteOperation(pParse, 0, iDb); sqlite3OpenMasterTable(pParse, iDb); - base = sqlite3VdbeAddOpList(v, ArraySize(dropTrigger), dropTrigger); + base = sqlite3VdbeAddOpList(v, ArraySize(dropTrigger), dropTrigger, iLn); sqlite3VdbeChangeP4(v, base+1, pTrigger->zName, P4_TRANSIENT); sqlite3VdbeChangeP4(v, base+4, "trigger", P4_STATIC); sqlite3ChangeCookie(pParse, iDb); @@ -105720,15 +106282,7 @@ static int codeTriggerProgram( ** INSERT OR IGNORE INTO t1 ... ; -- insert into t2 uses IGNORE policy */ pParse->eOrconf = (orconf==OE_Default)?pStep->orconf:(u8)orconf; - - /* Clear the cookieGoto flag. When coding triggers, the cookieGoto - ** variable is used as a flag to indicate to sqlite3ExprCodeConstants() - ** that it is not safe to refactor constants (this happens after the - ** start of the first loop in the SQL statement is coded - at that - ** point code may be conditionally executed, so it is no longer safe to - ** initialize constant register values). */ - assert( pParse->cookieGoto==0 || pParse->cookieGoto==-1 ); - pParse->cookieGoto = 0; + assert( pParse->okConstFactor==0 ); switch( pStep->op ){ case TK_UPDATE: { @@ -106517,7 +107071,7 @@ SQLITE_PRIVATE void sqlite3Update( regKey = iPk; }else{ sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, nPk, regKey, - sqlite3IndexAffinityStr(v, pPk), P4_TRANSIENT); + sqlite3IndexAffinityStr(v, pPk), nPk); sqlite3VdbeAddOp2(v, OP_IdxInsert, iEph, regKey); } sqlite3WhereEnd(pWInfo); @@ -106561,18 +107115,23 @@ SQLITE_PRIVATE void sqlite3Update( if( aToOpen[iDataCur-iBaseCur] ){ assert( pPk!=0 ); sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelBreak, regKey, nKey); + VdbeCoverageNeverTaken(v); } labelContinue = labelBreak; sqlite3VdbeAddOp2(v, OP_IsNull, pPk ? regKey : regOldRowid, labelBreak); + VdbeCoverage(v); }else if( pPk ){ labelContinue = sqlite3VdbeMakeLabel(v); - sqlite3VdbeAddOp2(v, OP_Rewind, iEph, labelBreak); + sqlite3VdbeAddOp2(v, OP_Rewind, iEph, labelBreak); VdbeCoverage(v); addrTop = sqlite3VdbeAddOp2(v, OP_RowKey, iEph, regKey); sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelContinue, regKey, 0); + VdbeCoverage(v); }else{ labelContinue = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet, labelBreak, regOldRowid); + VdbeCoverage(v); sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue, regOldRowid); + VdbeCoverage(v); } /* If the record number will change, set register regNewRowid to @@ -106582,7 +107141,7 @@ SQLITE_PRIVATE void sqlite3Update( assert( chngKey || pTrigger || hasFK || regOldRowid==regNewRowid ); if( chngRowid ){ sqlite3ExprCode(pParse, pRowidExpr, regNewRowid); - sqlite3VdbeAddOp1(v, OP_MustBeInt, regNewRowid); + sqlite3VdbeAddOp1(v, OP_MustBeInt, regNewRowid); VdbeCoverage(v); } /* Compute the old pre-UPDATE content of the row being changed, if that @@ -106651,8 +107210,7 @@ SQLITE_PRIVATE void sqlite3Update( ** verified. One could argue that this is wrong. */ if( tmask&TRIGGER_BEFORE ){ - sqlite3VdbeAddOp2(v, OP_Affinity, regNew, pTab->nCol); - sqlite3TableAffinityStr(v, pTab); + sqlite3TableAffinity(v, pTab, regNew); sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges, TRIGGER_BEFORE, pTab, regOldRowid, onError, labelContinue); @@ -106664,8 +107222,10 @@ SQLITE_PRIVATE void sqlite3Update( */ if( pPk ){ sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelContinue,regKey,nKey); + VdbeCoverage(v); }else{ sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue, regOldRowid); + VdbeCoverage(v); } /* If it did not delete it, the row-trigger may still have modified @@ -106701,6 +107261,7 @@ SQLITE_PRIVATE void sqlite3Update( }else{ j1 = sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, 0, regOldRowid); } + VdbeCoverageNeverTaken(v); } sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, aRegIdx); @@ -106744,7 +107305,7 @@ SQLITE_PRIVATE void sqlite3Update( /* Nothing to do at end-of-loop for a single-pass */ }else if( pPk ){ sqlite3VdbeResolveLabel(v, labelContinue); - sqlite3VdbeAddOp2(v, OP_Next, iEph, addrTop); + sqlite3VdbeAddOp2(v, OP_Next, iEph, addrTop); VdbeCoverage(v); }else{ sqlite3VdbeAddOp2(v, OP_Goto, 0, labelContinue); } @@ -106873,7 +107434,7 @@ static void updateVirtualTable( /* Generate code to scan the ephemeral table and call VUpdate. */ iReg = ++pParse->nMem; pParse->nMem += pTab->nCol+1; - addr = sqlite3VdbeAddOp2(v, OP_Rewind, ephemTab, 0); + addr = sqlite3VdbeAddOp2(v, OP_Rewind, ephemTab, 0); VdbeCoverage(v); sqlite3VdbeAddOp3(v, OP_Column, ephemTab, 0, iReg); sqlite3VdbeAddOp3(v, OP_Column, ephemTab, (pRowid?1:0), iReg+1); for(i=0; i<pTab->nCol; i++){ @@ -106883,7 +107444,7 @@ static void updateVirtualTable( 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); + sqlite3VdbeAddOp2(v, OP_Next, ephemTab, addr+1); VdbeCoverage(v); sqlite3VdbeJumpHere(v, addr); sqlite3VdbeAddOp2(v, OP_Close, ephemTab, 0); @@ -108455,7 +109016,7 @@ struct WhereLevel { int addrFirst; /* First instruction of interior of the loop */ int addrBody; /* Beginning of the body of this loop */ u8 iFrom; /* Which entry in the FROM clause */ - u8 op, p5; /* Opcode and P5 of the opcode that ends the loop */ + u8 op, p3, p5; /* Opcode, P3 & P5 of the opcode that ends the loop */ int p1, p2; /* Operands of the opcode used to ends the loop */ union { /* Information that depends on pWLoop->wsFlags */ struct { @@ -108842,6 +109403,7 @@ struct WhereInfo { #define WHERE_MULTI_OR 0x00002000 /* OR using multiple indices */ #define WHERE_AUTO_INDEX 0x00004000 /* Uses an ephemeral index */ #define WHERE_SKIPSCAN 0x00008000 /* Uses the skip-scan algorithm */ +#define WHERE_UNQ_WANTED 0x00010000 /* WHERE_ONEROW would have been helpful*/ /************** End of whereInt.h ********************************************/ /************** Continuing where we left off in where.c **********************/ @@ -110428,7 +110990,7 @@ static void constructAutomaticIndex( ** transient index on 2nd and subsequent iterations of the loop. */ v = pParse->pVdbe; assert( v!=0 ); - addrInit = sqlite3CodeOnce(pParse); + addrInit = sqlite3CodeOnce(pParse); VdbeCoverage(v); /* Count the number of columns that will be added to the index ** and used to match WHERE clause constraints */ @@ -110535,12 +111097,12 @@ static void constructAutomaticIndex( VdbeComment((v, "for %s", pTable->zName)); /* Fill the automatic index with content */ - addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, pLevel->iTabCur); + addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, pLevel->iTabCur); VdbeCoverage(v); regRecord = sqlite3GetTempReg(pParse); sqlite3GenerateIndexKey(pParse, pIdx, pLevel->iTabCur, regRecord, 0, 0, 0, 0); sqlite3VdbeAddOp2(v, OP_IdxInsert, pLevel->iIdxCur, regRecord); sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); - sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1); + sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1); VdbeCoverage(v); sqlite3VdbeChangeP5(v, SQLITE_STMTSTATUS_AUTOINDEX); sqlite3VdbeJumpHere(v, addrTop); sqlite3ReleaseTempReg(pParse, regRecord); @@ -110740,7 +111302,7 @@ static void whereKeyStats( assert( pRec->nField>0 && iCol<pIdx->nSampleCol ); do{ iTest = (iMin+i)/2; - res = sqlite3VdbeRecordCompare(aSample[iTest].n, aSample[iTest].p, pRec); + res = sqlite3VdbeRecordCompare(aSample[iTest].n, aSample[iTest].p, pRec, 0); if( res<0 ){ iMin = iTest+1; }else{ @@ -110755,16 +111317,16 @@ static void whereKeyStats( if( res==0 ){ /* If (res==0) is true, then sample $i must be equal to pRec */ assert( i<pIdx->nSample ); - assert( 0==sqlite3VdbeRecordCompare(aSample[i].n, aSample[i].p, pRec) + assert( 0==sqlite3VdbeRecordCompare(aSample[i].n, aSample[i].p, pRec, 0) || pParse->db->mallocFailed ); }else{ /* Otherwise, pRec must be smaller than sample $i and larger than ** sample ($i-1). */ assert( i==pIdx->nSample - || sqlite3VdbeRecordCompare(aSample[i].n, aSample[i].p, pRec)>0 + || sqlite3VdbeRecordCompare(aSample[i].n, aSample[i].p, pRec, 0)>0 || pParse->db->mallocFailed ); assert( i==0 - || sqlite3VdbeRecordCompare(aSample[i-1].n, aSample[i-1].p, pRec)<0 + || sqlite3VdbeRecordCompare(aSample[i-1].n, aSample[i-1].p, pRec, 0)<0 || pParse->db->mallocFailed ); } #endif /* ifdef SQLITE_DEBUG */ @@ -111216,6 +111778,8 @@ static int codeEqualityTerm( } iTab = pX->iTable; sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iTab, 0); + VdbeCoverageIf(v, bRev); + VdbeCoverageIf(v, !bRev); assert( (pLoop->wsFlags & WHERE_MULTI_OR)==0 ); pLoop->wsFlags |= WHERE_IN_ABLE; if( pLevel->u.in.nIn==0 ){ @@ -111235,7 +111799,7 @@ static int codeEqualityTerm( pIn->addrInTop = sqlite3VdbeAddOp3(v, OP_Column, iTab, 0, iReg); } pIn->eEndLoopOp = bRev ? OP_PrevIfOpen : OP_NextIfOpen; - sqlite3VdbeAddOp1(v, OP_IsNull, iReg); + sqlite3VdbeAddOp1(v, OP_IsNull, iReg); VdbeCoverage(v); }else{ pLevel->u.in.nIn = 0; } @@ -111330,10 +111894,14 @@ static int codeAllEqualityTerms( if( nSkip ){ int iIdxCur = pLevel->iIdxCur; sqlite3VdbeAddOp1(v, (bRev?OP_Last:OP_Rewind), iIdxCur); + VdbeCoverageIf(v, bRev==0); + VdbeCoverageIf(v, bRev!=0); VdbeComment((v, "begin skip-scan on %s", pIdx->zName)); j = sqlite3VdbeAddOp0(v, OP_Goto); - pLevel->addrSkip = sqlite3VdbeAddOp4Int(v, (bRev?OP_SeekLt:OP_SeekGt), + pLevel->addrSkip = sqlite3VdbeAddOp4Int(v, (bRev?OP_SeekLT:OP_SeekGT), iIdxCur, 0, regBase, nSkip); + VdbeCoverageIf(v, bRev==0); + VdbeCoverageIf(v, bRev!=0); sqlite3VdbeJumpHere(v, j); for(j=0; j<nSkip; j++){ sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, j, regBase+j); @@ -111366,7 +111934,10 @@ static int codeAllEqualityTerms( testcase( pTerm->eOperator & WO_IN ); if( (pTerm->eOperator & (WO_ISNULL|WO_IN))==0 ){ Expr *pRight = pTerm->pExpr->pRight; - sqlite3ExprCodeIsNullJump(v, pRight, regBase+j, pLevel->addrBrk); + if( sqlite3ExprCanBeNull(pRight) ){ + sqlite3VdbeAddOp2(v, OP_IsNull, regBase+j, pLevel->addrBrk); + VdbeCoverage(v); + } if( zAff ){ if( sqlite3CompareAffinity(pRight, zAff[j])==SQLITE_AFF_NONE ){ zAff[j] = SQLITE_AFF_NONE; @@ -111612,10 +112183,10 @@ static Bitmask codeOneLoopStart( /* Special case of a FROM clause subquery implemented as a co-routine */ if( pTabItem->viaCoroutine ){ int regYield = pTabItem->regReturn; - sqlite3VdbeAddOp2(v, OP_Integer, pTabItem->addrFillSub-1, regYield); - pLevel->p2 = sqlite3VdbeAddOp1(v, OP_Yield, regYield); - VdbeComment((v, "next row of co-routine %s", pTabItem->pTab->zName)); - sqlite3VdbeAddOp2(v, OP_If, regYield+1, addrBrk); + sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pTabItem->addrFillSub); + pLevel->p2 = sqlite3VdbeAddOp2(v, OP_Yield, regYield, addrBrk); + VdbeCoverage(v); + VdbeComment((v, "next row of \"%s\"", pTabItem->pTab->zName)); pLevel->op = OP_Goto; }else @@ -111647,6 +112218,7 @@ static Bitmask codeOneLoopStart( sqlite3VdbeAddOp4(v, OP_VFilter, iCur, addrNotFound, iReg, pLoop->u.vtab.idxStr, pLoop->u.vtab.needFree ? P4_MPRINTF : P4_STATIC); + VdbeCoverage(v); pLoop->u.vtab.needFree = 0; for(j=0; j<nConstraint && j<16; j++){ if( (pLoop->u.vtab.omitMask>>j)&1 ){ @@ -111670,16 +112242,18 @@ static Bitmask codeOneLoopStart( ** construct. */ assert( pLoop->u.btree.nEq==1 ); - iReleaseReg = sqlite3GetTempReg(pParse); pTerm = pLoop->aLTerm[0]; assert( pTerm!=0 ); assert( pTerm->pExpr!=0 ); assert( omitTable==0 ); testcase( pTerm->wtFlags & TERM_VIRTUAL ); + iReleaseReg = ++pParse->nMem; iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, 0, bRev, iReleaseReg); + if( iRowidReg!=iReleaseReg ) sqlite3ReleaseTempReg(pParse, iReleaseReg); addrNxt = pLevel->addrNxt; - sqlite3VdbeAddOp2(v, OP_MustBeInt, iRowidReg, addrNxt); + sqlite3VdbeAddOp2(v, OP_MustBeInt, iRowidReg, addrNxt); VdbeCoverage(v); sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addrNxt, iRowidReg); + VdbeCoverage(v); sqlite3ExprCacheAffinityChange(pParse, iRowidReg, 1); sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg); VdbeComment((v, "pk")); @@ -111713,10 +112287,10 @@ static Bitmask codeOneLoopStart( ** seek opcodes. It depends on a particular ordering of TK_xx */ const u8 aMoveOp[] = { - /* TK_GT */ OP_SeekGt, - /* TK_LE */ OP_SeekLe, - /* TK_LT */ OP_SeekLt, - /* TK_GE */ OP_SeekGe + /* TK_GT */ OP_SeekGT, + /* TK_LE */ OP_SeekLE, + /* TK_LT */ OP_SeekLT, + /* TK_GE */ OP_SeekGE }; assert( TK_LE==TK_GT+1 ); /* Make sure the ordering.. */ assert( TK_LT==TK_GT+2 ); /* ... of the TK_xx values... */ @@ -111730,11 +112304,17 @@ static Bitmask codeOneLoopStart( r1 = sqlite3ExprCodeTemp(pParse, pX->pRight, &rTemp); sqlite3VdbeAddOp3(v, aMoveOp[pX->op-TK_GT], iCur, addrBrk, r1); VdbeComment((v, "pk")); + VdbeCoverageIf(v, pX->op==TK_GT); + VdbeCoverageIf(v, pX->op==TK_LE); + VdbeCoverageIf(v, pX->op==TK_LT); + VdbeCoverageIf(v, pX->op==TK_GE); sqlite3ExprCacheAffinityChange(pParse, r1, 1); sqlite3ReleaseTempReg(pParse, rTemp); disableTerm(pLevel, pStart); }else{ sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iCur, addrBrk); + VdbeCoverageIf(v, bRev==0); + VdbeCoverageIf(v, bRev!=0); } if( pEnd ){ Expr *pX; @@ -111758,10 +112338,14 @@ static Bitmask codeOneLoopStart( pLevel->p2 = start; assert( pLevel->p5==0 ); if( testOp!=OP_Noop ){ - iRowidReg = iReleaseReg = sqlite3GetTempReg(pParse); + iRowidReg = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_Rowid, iCur, iRowidReg); sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg); sqlite3VdbeAddOp3(v, testOp, memEndValue, addrBrk, iRowidReg); + VdbeCoverageIf(v, testOp==OP_Le); + VdbeCoverageIf(v, testOp==OP_Lt); + VdbeCoverageIf(v, testOp==OP_Ge); + VdbeCoverageIf(v, testOp==OP_Gt); sqlite3VdbeChangeP5(v, SQLITE_AFF_NUMERIC | SQLITE_JUMPIFNULL); } }else if( pLoop->wsFlags & WHERE_INDEXED ){ @@ -111801,20 +112385,19 @@ static Bitmask codeOneLoopStart( 0, OP_Rewind, /* 2: (!start_constraints && startEq && !bRev) */ OP_Last, /* 3: (!start_constraints && startEq && bRev) */ - OP_SeekGt, /* 4: (start_constraints && !startEq && !bRev) */ - OP_SeekLt, /* 5: (start_constraints && !startEq && bRev) */ - OP_SeekGe, /* 6: (start_constraints && startEq && !bRev) */ - OP_SeekLe /* 7: (start_constraints && startEq && bRev) */ + OP_SeekGT, /* 4: (start_constraints && !startEq && !bRev) */ + OP_SeekLT, /* 5: (start_constraints && !startEq && bRev) */ + OP_SeekGE, /* 6: (start_constraints && startEq && !bRev) */ + OP_SeekLE /* 7: (start_constraints && startEq && bRev) */ }; static const u8 aEndOp[] = { - OP_Noop, /* 0: (!end_constraints) */ - OP_IdxGE, /* 1: (end_constraints && !bRev) */ - OP_IdxLT /* 2: (end_constraints && bRev) */ + OP_IdxGE, /* 0: (end_constraints && !bRev && !endEq) */ + OP_IdxGT, /* 1: (end_constraints && !bRev && endEq) */ + OP_IdxLE, /* 2: (end_constraints && bRev && !endEq) */ + OP_IdxLT, /* 3: (end_constraints && bRev && endEq) */ }; u16 nEq = pLoop->u.btree.nEq; /* Number of == or IN terms */ - int isMinQuery = 0; /* If this is an optimized SELECT min(x).. */ int regBase; /* Base register holding constraint values */ - int r1; /* Temp register */ WhereTerm *pRangeStart = 0; /* Inequality constraint at range start */ WhereTerm *pRangeEnd = 0; /* Inequality constraint at range end */ int startEq; /* True if range start uses ==, >= or <= */ @@ -111827,6 +112410,8 @@ static Bitmask codeOneLoopStart( int op; /* Instruction opcode */ char *zStartAff; /* Affinity for start of range constraint */ char cEndAff = 0; /* Affinity for end of range constraint */ + u8 bSeekPastNull = 0; /* True to seek past initial nulls */ + u8 bStopAtNull = 0; /* Add condition to terminate at NULLs */ pIdx = pLoop->u.btree.pIndex; iIdxCur = pLevel->iIdxCur; @@ -111845,7 +112430,7 @@ static Bitmask codeOneLoopStart( && (pIdx->nKeyCol>nEq) ){ assert( pLoop->u.btree.nSkip==0 ); - isMinQuery = 1; + bSeekPastNull = 1; nExtraReg = 1; } @@ -111860,7 +112445,14 @@ static Bitmask codeOneLoopStart( if( pLoop->wsFlags & WHERE_TOP_LIMIT ){ pRangeEnd = pLoop->aLTerm[j++]; nExtraReg = 1; + if( pRangeStart==0 + && (j = pIdx->aiColumn[nEq])>=0 + && pIdx->pTable->aCol[j].notNull==0 + ){ + bSeekPastNull = 1; + } } + assert( pRangeEnd==0 || (pRangeEnd->wtFlags & TERM_VNULL)==0 ); /* Generate code to evaluate all constraint terms using == or IN ** and store the values of those terms in an array of registers @@ -111879,6 +112471,7 @@ static Bitmask codeOneLoopStart( || (bRev && pIdx->nKeyCol==nEq) ){ SWAP(WhereTerm *, pRangeEnd, pRangeStart); + SWAP(u8, bSeekPastNull, bStopAtNull); } testcase( pRangeStart && (pRangeStart->eOperator & WO_LE)!=0 ); @@ -111894,8 +112487,11 @@ static Bitmask codeOneLoopStart( if( pRangeStart ){ Expr *pRight = pRangeStart->pExpr->pRight; sqlite3ExprCode(pParse, pRight, regBase+nEq); - if( (pRangeStart->wtFlags & TERM_VNULL)==0 ){ - sqlite3ExprCodeIsNullJump(v, pRight, regBase+nEq, addrNxt); + if( (pRangeStart->wtFlags & TERM_VNULL)==0 + && sqlite3ExprCanBeNull(pRight) + ){ + sqlite3VdbeAddOp2(v, OP_IsNull, regBase+nEq, addrNxt); + VdbeCoverage(v); } if( zStartAff ){ if( sqlite3CompareAffinity(pRight, zStartAff[nEq])==SQLITE_AFF_NONE){ @@ -111910,22 +112506,23 @@ static Bitmask codeOneLoopStart( } nConstraint++; testcase( pRangeStart->wtFlags & TERM_VIRTUAL ); - }else if( isMinQuery ){ + }else if( bSeekPastNull ){ sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq); nConstraint++; startEq = 0; start_constraints = 1; } - codeApplyAffinity(pParse, regBase, nConstraint, zStartAff); + codeApplyAffinity(pParse, regBase, nConstraint - bSeekPastNull, zStartAff); op = aStartOp[(start_constraints<<2) + (startEq<<1) + bRev]; assert( op!=0 ); - testcase( op==OP_Rewind ); - testcase( op==OP_Last ); - testcase( op==OP_SeekGt ); - testcase( op==OP_SeekGe ); - testcase( op==OP_SeekLe ); - testcase( op==OP_SeekLt ); sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint); + VdbeCoverage(v); + VdbeCoverageIf(v, op==OP_Rewind); testcase( op==OP_Rewind ); + VdbeCoverageIf(v, op==OP_Last); testcase( op==OP_Last ); + VdbeCoverageIf(v, op==OP_SeekGT); testcase( op==OP_SeekGT ); + VdbeCoverageIf(v, op==OP_SeekGE); testcase( op==OP_SeekGE ); + VdbeCoverageIf(v, op==OP_SeekLE); testcase( op==OP_SeekLE ); + VdbeCoverageIf(v, op==OP_SeekLT); testcase( op==OP_SeekLT ); /* Load the value for the inequality constraint at the end of the ** range (if any). @@ -111935,8 +112532,11 @@ static Bitmask codeOneLoopStart( Expr *pRight = pRangeEnd->pExpr->pRight; sqlite3ExprCacheRemove(pParse, regBase+nEq, 1); sqlite3ExprCode(pParse, pRight, regBase+nEq); - if( (pRangeEnd->wtFlags & TERM_VNULL)==0 ){ - sqlite3ExprCodeIsNullJump(v, pRight, regBase+nEq, addrNxt); + if( (pRangeEnd->wtFlags & TERM_VNULL)==0 + && sqlite3ExprCanBeNull(pRight) + ){ + sqlite3VdbeAddOp2(v, OP_IsNull, regBase+nEq, addrNxt); + VdbeCoverage(v); } if( sqlite3CompareAffinity(pRight, cEndAff)!=SQLITE_AFF_NONE && !sqlite3ExprNeedsNoAffinityChange(pRight, cEndAff) @@ -111945,6 +112545,10 @@ static Bitmask codeOneLoopStart( } nConstraint++; testcase( pRangeEnd->wtFlags & TERM_VIRTUAL ); + }else if( bStopAtNull ){ + sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq); + endEq = 0; + nConstraint++; } sqlite3DbFree(db, zStartAff); @@ -111952,40 +112556,22 @@ static Bitmask codeOneLoopStart( pLevel->p2 = sqlite3VdbeCurrentAddr(v); /* Check if the index cursor is past the end of the range. */ - op = aEndOp[(pRangeEnd || nEq) * (1 + bRev)]; - testcase( op==OP_Noop ); - testcase( op==OP_IdxGE ); - testcase( op==OP_IdxLT ); - if( op!=OP_Noop ){ + if( nConstraint ){ + op = aEndOp[bRev*2 + endEq]; sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint); - sqlite3VdbeChangeP5(v, endEq!=bRev ?1:0); + testcase( op==OP_IdxGT ); VdbeCoverageIf(v, op==OP_IdxGT ); + testcase( op==OP_IdxGE ); VdbeCoverageIf(v, op==OP_IdxGE ); + testcase( op==OP_IdxLT ); VdbeCoverageIf(v, op==OP_IdxLT ); + testcase( op==OP_IdxLE ); VdbeCoverageIf(v, op==OP_IdxLE ); } - /* If there are inequality constraints, check that the value - ** of the table column that the inequality contrains is not NULL. - ** If it is, jump to the next iteration of the loop. - */ - r1 = sqlite3GetTempReg(pParse); - testcase( pLoop->wsFlags & WHERE_BTM_LIMIT ); - testcase( pLoop->wsFlags & WHERE_TOP_LIMIT ); - if( (pLoop->wsFlags & (WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0 - && (j = pIdx->aiColumn[nEq])>=0 - && pIdx->pTable->aCol[j].notNull==0 - && (nEq || (pLoop->wsFlags & WHERE_BTM_LIMIT)==0) - ){ - sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, nEq, r1); - VdbeComment((v, "%s", pIdx->pTable->aCol[j].zName)); - sqlite3VdbeAddOp2(v, OP_IsNull, r1, addrCont); - } - sqlite3ReleaseTempReg(pParse, r1); - /* Seek the table cursor, if required */ disableTerm(pLevel, pRangeStart); disableTerm(pLevel, pRangeEnd); if( omitTable ){ /* pIdx is a covering index. No need to access the main table. */ }else if( HasRowid(pIdx->pTable) ){ - iRowidReg = iReleaseReg = sqlite3GetTempReg(pParse); + iRowidReg = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, iRowidReg); sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg); sqlite3VdbeAddOp2(v, OP_Seek, iCur, iRowidReg); /* Deferred seek */ @@ -111997,7 +112583,7 @@ static Bitmask codeOneLoopStart( sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, k, iRowidReg+j); } sqlite3VdbeAddOp4Int(v, OP_NotFound, iCur, addrCont, - iRowidReg, pPk->nKeyCol); + iRowidReg, pPk->nKeyCol); VdbeCoverage(v); } /* Record the instruction used to terminate the loop. Disable @@ -112011,6 +112597,8 @@ static Bitmask codeOneLoopStart( pLevel->op = OP_Next; } pLevel->p1 = iIdxCur; + assert( (WHERE_UNQ_WANTED>>16)==1 ); + pLevel->p3 = (pLoop->wsFlags>>16)&1; if( (pLoop->wsFlags & WHERE_CONSTRAINT)==0 ){ pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP; }else{ @@ -112179,6 +112767,7 @@ static Bitmask codeOneLoopStart( regRowid, 0); sqlite3VdbeAddOp4Int(v, OP_RowSetTest, regRowset, sqlite3VdbeCurrentAddr(v)+2, r, iSet); + VdbeCoverage(v); } sqlite3VdbeAddOp2(v, OP_Gosub, regReturn, iLoopBody); @@ -112247,6 +112836,8 @@ static Bitmask codeOneLoopStart( pLevel->op = aStep[bRev]; pLevel->p1 = iCur; pLevel->p2 = 1 + sqlite3VdbeAddOp2(v, aStart[bRev], iCur, addrBrk); + VdbeCoverageIf(v, bRev==0); + VdbeCoverageIf(v, bRev!=0); pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP; } } @@ -112328,7 +112919,6 @@ static Bitmask codeOneLoopStart( pTerm->wtFlags |= TERM_CODED; } } - sqlite3ReleaseTempReg(pParse, iReleaseReg); return pLevel->notReady; } @@ -112773,7 +113363,10 @@ static int whereLoopAddBtreeIndex( pNew->aLTerm[pNew->nLTerm++] = 0; pNew->wsFlags |= WHERE_SKIPSCAN; nIter = sqlite3LogEst(pProbe->aiRowEst[0]/pProbe->aiRowEst[saved_nEq+1]); + pNew->rRun = rLogSize + nIter; + pNew->nOut += nIter; whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, nIter); + pNew->nOut = saved_nOut; } for(; rc==SQLITE_OK && pTerm!=0; pTerm = whereScanNext(&scan)){ int nIn = 0; @@ -112815,12 +113408,13 @@ static int whereLoopAddBtreeIndex( || nInMul==0 ); pNew->wsFlags |= WHERE_COLUMN_EQ; - if( iCol<0 - || (pProbe->onError!=OE_None && nInMul==0 - && pNew->u.btree.nEq==pProbe->nKeyCol-1) - ){ + if( iCol<0 || (nInMul==0 && pNew->u.btree.nEq==pProbe->nKeyCol-1)){ assert( (pNew->wsFlags & WHERE_COLUMN_IN)==0 || iCol<0 ); - pNew->wsFlags |= WHERE_ONEROW; + if( iCol>=0 && pProbe->onError==OE_None ){ + pNew->wsFlags |= WHERE_UNQ_WANTED; + }else{ + pNew->wsFlags |= WHERE_ONEROW; + } } pNew->u.btree.nEq++; pNew->nOut = nRowEst + nInMul; @@ -113699,9 +114293,12 @@ static int wherePathSatisfiesOrderBy( orderDistinctMask |= pLoop->maskSelf; for(i=0; i<nOrderBy; i++){ Expr *p; + Bitmask mTerm; if( MASKBIT(i) & obSat ) continue; p = pOrderBy->a[i].pExpr; - if( (exprTableUsage(&pWInfo->sMaskSet, p)&~orderDistinctMask)==0 ){ + mTerm = exprTableUsage(&pWInfo->sMaskSet,p); + if( mTerm==0 && !sqlite3ExprIsConstant(p) ) continue; + if( (mTerm&~orderDistinctMask)==0 ){ obSat |= MASKBIT(i); } } @@ -114263,7 +114860,6 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( initMaskSet(pMaskSet); whereClauseInit(&pWInfo->sWC, pWInfo); whereSplit(&pWInfo->sWC, pWhere, TK_AND); - sqlite3CodeVerifySchema(pParse, -1); /* Insert the cookie verifier Goto */ /* Special case: a WHERE clause that is constant. Evaluate the ** expression and either jump over all of the code or fall thru. @@ -114325,22 +114921,6 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( goto whereBeginError; } - /* If the ORDER BY (or GROUP BY) clause contains references to general - ** expressions, then we won't be able to satisfy it using indices, so - ** go ahead and disable it now. - */ - if( pOrderBy && (wctrlFlags & WHERE_WANT_DISTINCT)!=0 ){ - for(ii=0; ii<pOrderBy->nExpr; ii++){ - Expr *pExpr = sqlite3ExprSkipCollate(pOrderBy->a[ii].pExpr); - if( pExpr->op!=TK_COLUMN ){ - pWInfo->pOrderBy = pOrderBy = 0; - break; - }else if( pExpr->iColumn<0 ){ - break; - } - } - } - if( wctrlFlags & WHERE_WANT_DISTINCT ){ if( isDistinctRedundant(pParse, pTabList, &pWInfo->sWC, pResultSet) ){ /* The DISTINCT marking is pointless. Ignore it. */ @@ -114552,7 +115132,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( sqlite3VdbeSetP4KeyInfo(pParse, pIx); VdbeComment((v, "%s", pIx->zName)); } - sqlite3CodeVerifySchema(pParse, iDb); + if( iDb>=0 ) sqlite3CodeVerifySchema(pParse, iDb); notReady &= ~getMask(&pWInfo->sMaskSet, pTabItem->iCursor); } pWInfo->iTop = sqlite3VdbeCurrentAddr(v); @@ -114614,8 +115194,12 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ pLoop = pLevel->pWLoop; sqlite3VdbeResolveLabel(v, pLevel->addrCont); if( pLevel->op!=OP_Noop ){ - sqlite3VdbeAddOp2(v, pLevel->op, pLevel->p1, pLevel->p2); + sqlite3VdbeAddOp3(v, pLevel->op, pLevel->p1, pLevel->p2, pLevel->p3); sqlite3VdbeChangeP5(v, pLevel->p5); + VdbeCoverage(v); + VdbeCoverageIf(v, pLevel->op==OP_Next); + VdbeCoverageIf(v, pLevel->op==OP_Prev); + VdbeCoverageIf(v, pLevel->op==OP_VNext); } if( pLoop->wsFlags & WHERE_IN_ABLE && pLevel->u.in.nIn>0 ){ struct InLoop *pIn; @@ -114624,6 +115208,9 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ for(j=pLevel->u.in.nIn, pIn=&pLevel->u.in.aInLoop[j-1]; j>0; j--, pIn--){ sqlite3VdbeJumpHere(v, pIn->addrInTop+1); sqlite3VdbeAddOp2(v, pIn->eEndLoopOp, pIn->iCur, pIn->addrInTop); + VdbeCoverage(v); + VdbeCoverageIf(v, pIn->eEndLoopOp==OP_PrevIfOpen); + VdbeCoverageIf(v, pIn->eEndLoopOp==OP_NextIfOpen); sqlite3VdbeJumpHere(v, pIn->addrInTop-1); } sqlite3DbFree(db, pLevel->u.in.aInLoop); @@ -114636,7 +115223,7 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ sqlite3VdbeJumpHere(v, pLevel->addrSkip-2); } if( pLevel->iLeftJoin ){ - addr = sqlite3VdbeAddOp1(v, OP_IfPos, pLevel->iLeftJoin); + addr = sqlite3VdbeAddOp1(v, OP_IfPos, pLevel->iLeftJoin); VdbeCoverage(v); assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 || (pLoop->wsFlags & WHERE_INDEXED)!=0 ); if( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 ){ @@ -114663,12 +115250,38 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ assert( pWInfo->nLevel<=pTabList->nSrc ); for(i=0, pLevel=pWInfo->a; i<pWInfo->nLevel; i++, pLevel++){ + int k, last; + VdbeOp *pOp; Index *pIdx = 0; struct SrcList_item *pTabItem = &pTabList->a[pLevel->iFrom]; Table *pTab = pTabItem->pTab; assert( pTab!=0 ); pLoop = pLevel->pWLoop; + /* For a co-routine, change all OP_Column references to the table of + ** the co-routine into OP_SCopy of result contained in a register. + ** OP_Rowid becomes OP_Null. + */ + if( pTabItem->viaCoroutine && !db->mallocFailed ){ + last = sqlite3VdbeCurrentAddr(v); + k = pLevel->addrBody; + pOp = sqlite3VdbeGetOp(v, k); + for(; k<last; k++, pOp++){ + if( pOp->p1!=pLevel->iTabCur ) continue; + if( pOp->opcode==OP_Column ){ + pOp->opcode = OP_Copy; + pOp->p1 = pOp->p2 + pTabItem->regResult; + pOp->p2 = pOp->p3; + pOp->p3 = 0; + }else if( pOp->opcode==OP_Rowid ){ + pOp->opcode = OP_Null; + pOp->p1 = 0; + pOp->p3 = 0; + } + } + continue; + } + /* Close all of the cursors that were opened by sqlite3WhereBegin. ** Except, do not close cursors that will be reused by the OR optimization ** (WHERE_OMIT_OPEN_CLOSE). And do not close the OP_OpenWrite cursors @@ -114707,9 +115320,6 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ pIdx = pLevel->u.pCovidx; } if( pIdx && !db->mallocFailed ){ - int k, last; - VdbeOp *pOp; - last = sqlite3VdbeCurrentAddr(v); k = pLevel->addrBody; pOp = sqlite3VdbeGetOp(v, k); @@ -117123,13 +117733,26 @@ static void yy_reduce( } break; case 112: /* select ::= with selectnowith */ -{ - if( yymsp[0].minor.yy3 ){ - yymsp[0].minor.yy3->pWith = yymsp[-1].minor.yy59; +{ + Select *p = yymsp[0].minor.yy3, *pNext, *pLoop; + if( p ){ + int cnt = 0, mxSelect; + p->pWith = yymsp[-1].minor.yy59; + if( p->pPrior ){ + pNext = 0; + for(pLoop=p; pLoop; pNext=pLoop, pLoop=pLoop->pPrior, cnt++){ + pLoop->pNext = pNext; + pLoop->selFlags |= SF_Compound; + } + mxSelect = pParse->db->aLimit[SQLITE_LIMIT_COMPOUND_SELECT]; + if( mxSelect && cnt>mxSelect ){ + sqlite3ErrorMsg(pParse, "too many terms in compound SELECT"); + } + } }else{ sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy59); } - yygotominor.yy3 = yymsp[0].minor.yy3; + yygotominor.yy3 = p; } break; case 113: /* selectnowith ::= oneselect */ @@ -117138,14 +117761,22 @@ static void yy_reduce( break; case 114: /* selectnowith ::= selectnowith multiselect_op oneselect */ { - if( yymsp[0].minor.yy3 ){ - yymsp[0].minor.yy3->op = (u8)yymsp[-1].minor.yy328; - yymsp[0].minor.yy3->pPrior = yymsp[-2].minor.yy3; + Select *pRhs = yymsp[0].minor.yy3; + if( pRhs && pRhs->pPrior ){ + SrcList *pFrom; + Token x; + x.n = 0; + pFrom = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&x,pRhs,0,0); + pRhs = sqlite3SelectNew(pParse,0,pFrom,0,0,0,0,0,0,0); + } + if( pRhs ){ + pRhs->op = (u8)yymsp[-1].minor.yy328; + pRhs->pPrior = yymsp[-2].minor.yy3; if( yymsp[-1].minor.yy328!=TK_ALL ) pParse->hasCompound = 1; }else{ sqlite3SelectDelete(pParse->db, yymsp[-2].minor.yy3); } - yygotominor.yy3 = yymsp[0].minor.yy3; + yygotominor.yy3 = pRhs; } break; case 116: /* multiselect_op ::= UNION ALL */ @@ -122713,6 +123344,21 @@ SQLITE_API int sqlite3_test_control(int op, ...){ break; } + + /* sqlite3_test_control(SQLITE_TESTCTRL_VDBE_COVERAGE, xCallback, ptr); + ** + ** Set the VDBE coverage callback function to xCallback with context + ** pointer ptr. + */ + case SQLITE_TESTCTRL_VDBE_COVERAGE: { +#ifdef SQLITE_VDBE_COVERAGE + typedef void (*branch_callback)(void*,int,u8,u8); + sqlite3GlobalConfig.xVdbeBranch = va_arg(ap,branch_callback); + sqlite3GlobalConfig.pVdbeBranchArg = va_arg(ap,void*); +#endif + break; + } + } va_end(ap); #endif /* SQLITE_OMIT_BUILTIN_TEST */ diff --git a/ext/sqlite3/libsqlite/sqlite3.h b/ext/sqlite3/libsqlite/sqlite3.h index 6cf5977b1..0884ebad0 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.8.3.1" -#define SQLITE_VERSION_NUMBER 3008003 -#define SQLITE_SOURCE_ID "2014-02-11 14:52:19 ea3317a4803d71d88183b29f1d3086f46d68a00e" +#define SQLITE_VERSION "3.8.4.3" +#define SQLITE_VERSION_NUMBER 3008004 +#define SQLITE_SOURCE_ID "2014-04-03 16:53:12 a611fa96c4a848614efe899130359c9f6fb889c3" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -6122,7 +6122,8 @@ SQLITE_API int sqlite3_test_control(int op, ...); #define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 #define SQLITE_TESTCTRL_EXPLAIN_STMT 19 #define SQLITE_TESTCTRL_NEVER_CORRUPT 20 -#define SQLITE_TESTCTRL_LAST 20 +#define SQLITE_TESTCTRL_VDBE_COVERAGE 21 +#define SQLITE_TESTCTRL_LAST 21 /* ** CAPI3REF: SQLite Runtime Status diff --git a/ext/standard/array.c b/ext/standard/array.c index 6c3be814c..4f9833103 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -64,9 +64,6 @@ #define CASE_LOWER 0 #define CASE_UPPER 1 -#define COUNT_NORMAL 0 -#define COUNT_RECURSIVE 1 - #define DIFF_NORMAL 1 #define DIFF_KEY 2 #define DIFF_ASSOC 6 @@ -274,7 +271,7 @@ PHP_FUNCTION(ksort) } /* }}} */ -static int php_count_recursive(zval *array, long mode TSRMLS_DC) /* {{{ */ +PHPAPI int php_count_recursive(zval *array, long mode TSRMLS_DC) /* {{{ */ { long cnt = 0; zval **element; @@ -336,12 +333,17 @@ PHP_FUNCTION(count) #ifdef HAVE_SPL /* if not and the object implements Countable we call its count() method */ if (Z_OBJ_HT_P(array)->get_class_entry && instanceof_function(Z_OBJCE_P(array), spl_ce_Countable TSRMLS_CC)) { - zend_call_method_with_0_params(&array, NULL, NULL, "count", &retval); + zval *mode_zv; + MAKE_STD_ZVAL(mode_zv); + ZVAL_LONG(mode_zv, mode); + zend_call_method_with_1_params(&array, NULL, NULL, "count", &retval, mode_zv); if (retval) { convert_to_long_ex(&retval); RETVAL_LONG(Z_LVAL_P(retval)); zval_ptr_dtor(&retval); } + zval_dtor(mode_zv); + efree(mode_zv); return; } #endif diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index 897b1a9e4..e525ff2a8 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -957,11 +957,6 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_scandir, 0, 0, 1) ZEND_ARG_INFO(0, context) ZEND_END_ARG_INFO() /* }}} */ -/* {{{ arginfo ext/standard/dl.c */ -ZEND_BEGIN_ARG_INFO(arginfo_dl, 0) - ZEND_ARG_INFO(0, extension_filename) -ZEND_END_ARG_INFO() -/* }}} */ /* {{{ dns.c */ ZEND_BEGIN_ARG_INFO(arginfo_gethostbyaddr, 0) ZEND_ARG_INFO(0, ip_address) @@ -2651,7 +2646,7 @@ ZEND_BEGIN_ARG_INFO(arginfo_serialize, 0) ZEND_ARG_INFO(0, var) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO(arginfo_unserialize, 0) +ZEND_BEGIN_ARG_INFO_EX(arginfo_unserialize, 0, 0, 1) ZEND_ARG_INFO(0, variable_representation) ZEND_ARG_INFO(1, consumed) ZEND_END_ARG_INFO() diff --git a/ext/standard/file.c b/ext/standard/file.c index 74df6dc75..e1f24abf6 100644 --- a/ext/standard/file.c +++ b/ext/standard/file.c @@ -1668,7 +1668,7 @@ PHPAPI int php_copy_file_ctx(const char *src, const char *dest, int src_flg, php return FAILURE; } - switch (php_stream_stat_path_ex(dest, PHP_STREAM_URL_STAT_QUIET, &dest_s, ctx)) { + switch (php_stream_stat_path_ex(dest, PHP_STREAM_URL_STAT_QUIET | PHP_STREAM_URL_STAT_NOCACHE, &dest_s, ctx)) { case -1: /* non-statable stream */ goto safe_to_copy; diff --git a/ext/standard/image.c b/ext/standard/image.c index 27f60c95a..02246c626 100644 --- a/ext/standard/image.c +++ b/ext/standard/image.c @@ -30,6 +30,9 @@ #include <unistd.h> #endif #include "php_image.h" +#ifdef PHP_WIN32 +#include "win32/php_stdint.h" +#endif #if HAVE_ZLIB && !defined(COMPILE_DL_ZLIB) #include "zlib.h" @@ -163,6 +166,7 @@ static struct gfxinfo *php_handle_bmp (php_stream * stream TSRMLS_DC) result = (struct gfxinfo *) ecalloc (1, sizeof(struct gfxinfo)); result->width = (((unsigned int)dim[ 7]) << 24) + (((unsigned int)dim[ 6]) << 16) + (((unsigned int)dim[ 5]) << 8) + ((unsigned int) dim[ 4]); result->height = (((unsigned int)dim[11]) << 24) + (((unsigned int)dim[10]) << 16) + (((unsigned int)dim[ 9]) << 8) + ((unsigned int) dim[ 8]); + result->height = abs((int32_t)result->height); result->bits = (((unsigned int)dim[15]) << 8) + ((unsigned int)dim[14]); } else { return NULL; diff --git a/ext/standard/php_array.h b/ext/standard/php_array.h index deddeb487..1b30e3478 100644 --- a/ext/standard/php_array.h +++ b/ext/standard/php_array.h @@ -107,6 +107,7 @@ PHPAPI void php_splice(HashTable *ht, zend_uint offset, zend_uint length, zval * PHPAPI int php_array_merge(HashTable *dest, HashTable *src, int recursive TSRMLS_DC); PHPAPI int php_array_replace_recursive(HashTable *dest, HashTable *src TSRMLS_DC); PHPAPI int php_multisort_compare(const void *a, const void *b TSRMLS_DC); +PHPAPI int php_count_recursive(zval *array, long mode TSRMLS_DC); #define PHP_SORT_REGULAR 0 #define PHP_SORT_NUMERIC 1 @@ -117,6 +118,9 @@ PHPAPI int php_multisort_compare(const void *a, const void *b TSRMLS_DC); #define PHP_SORT_NATURAL 6 #define PHP_SORT_FLAG_CASE 8 +#define COUNT_NORMAL 0 +#define COUNT_RECURSIVE 1 + #define ARRAY_FILTER_USE_BOTH 1 #define ARRAY_FILTER_USE_KEY 2 diff --git a/ext/standard/php_rand.h b/ext/standard/php_rand.h index 2928d98c5..0e8abb361 100644 --- a/ext/standard/php_rand.h +++ b/ext/standard/php_rand.h @@ -27,6 +27,7 @@ #include <stdlib.h> #include "basic_functions.h" +#include "php_lcg.h" /* System Rand functions */ #ifndef RAND_MAX diff --git a/ext/standard/rand.c b/ext/standard/rand.c index b0af30aa9..702fec231 100644 --- a/ext/standard/rand.c +++ b/ext/standard/rand.c @@ -30,7 +30,6 @@ #include "php.h" #include "php_math.h" #include "php_rand.h" -#include "php_lcg.h" #include "basic_functions.h" diff --git a/ext/standard/tests/array/bug67064.phpt b/ext/standard/tests/array/bug67064.phpt new file mode 100644 index 000000000..2818516da --- /dev/null +++ b/ext/standard/tests/array/bug67064.phpt @@ -0,0 +1,17 @@ +--TEST-- +Bug #67064 (Countable interface prevents using 2nd parameter ($mode) of count() function) +--FILE-- +<?php +class Counter implements Countable { + public function count($mode = COUNT_NORMAL) { + var_dump($mode == COUNT_RECURSIVE); + return 1; + } +} + +$counter = new Counter; +var_dump(count($counter, COUNT_RECURSIVE)); +?> +--EXPECTF-- +bool(true) +int(1) diff --git a/ext/standard/tests/file/bug65701.phpt b/ext/standard/tests/file/bug65701.phpt new file mode 100644 index 000000000..2b1b5d491 --- /dev/null +++ b/ext/standard/tests/file/bug65701.phpt @@ -0,0 +1,30 @@ +--TEST-- +Test for bug #65701: copy() doesn't work when destination filename is created by tempnam() +--CREDITS-- +Boro Sitnikovski <buritomath@yahoo.com> +--FILE-- +<?php +$file_path = dirname(__FILE__) . "/bug65701/"; + +mkdir($file_path); + +$src = $file_path . '/srcbug65701_file.txt'; +$dst = tempnam($file_path, 'dstbug65701_file.txt'); + +file_put_contents($src, "Hello World"); + +copy($src, $dst); +var_dump(filesize($dst)); +?> +--CLEAN-- +<?php +$file_path = dirname(__FILE__) . "/bug65701/"; +foreach (scandir($file_path) as $file) { + if (strpos($file, "bug65701") !== false) { + unlink($file_path . $file); + } +} +rmdir($file_path); +?> +--EXPECT-- +int(11) diff --git a/ext/standard/tests/image/getimagesize.phpt b/ext/standard/tests/image/getimagesize.phpt index 6cd8275e0..04ddd8c82 100644 --- a/ext/standard/tests/image/getimagesize.phpt +++ b/ext/standard/tests/image/getimagesize.phpt @@ -23,7 +23,22 @@ GetImageSize() var_dump($result); ?> --EXPECT-- -array(12) { +array(13) { + ["test-1pix.bmp"]=> + array(6) { + [0]=> + int(1) + [1]=> + int(1) + [2]=> + int(6) + [3]=> + string(20) "width="1" height="1"" + ["bits"]=> + int(24) + ["mime"]=> + string(14) "image/x-ms-bmp" + } ["test1bpix.bmp"]=> array(6) { [0]=> diff --git a/ext/standard/tests/image/image_type_to_mime_type.phpt b/ext/standard/tests/image/image_type_to_mime_type.phpt index d83ab8d14..9f7ffa1aa 100644 --- a/ext/standard/tests/image/image_type_to_mime_type.phpt +++ b/ext/standard/tests/image/image_type_to_mime_type.phpt @@ -25,7 +25,9 @@ image_type_to_mime_type() var_dump($result); ?> --EXPECT-- -array(12) { +array(13) { + ["test-1pix.bmp"]=> + string(14) "image/x-ms-bmp" ["test1bpix.bmp"]=> string(14) "image/x-ms-bmp" ["test1pix.bmp"]=> diff --git a/ext/standard/tests/image/test-1pix.bmp b/ext/standard/tests/image/test-1pix.bmp Binary files differnew file mode 100644 index 000000000..ec68a571c --- /dev/null +++ b/ext/standard/tests/image/test-1pix.bmp diff --git a/ext/standard/tests/serialize/005.phpt b/ext/standard/tests/serialize/005.phpt index e7b23db70..2df270154 100644 --- a/ext/standard/tests/serialize/005.phpt +++ b/ext/standard/tests/serialize/005.phpt @@ -156,9 +156,11 @@ object(TestNAOld)#%d (0) { } ===NANew=== unserializer(TestNANew) -TestNew::__wakeup() -object(TestNANew)#%d (0) { -} + +Warning: Erroneous data format for unserializing 'TestNANew' in %s005.php on line %d + +Notice: unserialize(): Error at offset 19 of 20 bytes in %s005.php on line %d +bool(false) ===NANew2=== unserializer(TestNANew2) TestNew::unserialize() diff --git a/ext/standard/tests/serialize/bug67072.phpt b/ext/standard/tests/serialize/bug67072.phpt new file mode 100644 index 000000000..951db75a0 --- /dev/null +++ b/ext/standard/tests/serialize/bug67072.phpt @@ -0,0 +1,12 @@ +--TEST-- +Bug #67072 Echoing unserialized "SplFileObject" crash +--FILE-- +<?php + echo unserialize('O:13:"SplFileObject":1:{s:9:"*filename";s:15:"/home/flag/flag";}'); +?> +===DONE== +--EXPECTF-- +Warning: Erroneous data format for unserializing 'SplFileObject' in %sbug67072.php on line %d + +Notice: unserialize(): Error at offset 24 of 64 bytes in %sbug67072.php on line %d +===DONE== diff --git a/ext/standard/tests/url/parse_url_basic_001.phpt b/ext/standard/tests/url/parse_url_basic_001.phpt index 4c5b0944c..a6f4f7a25 100644 --- a/ext/standard/tests/url/parse_url_basic_001.phpt +++ b/ext/standard/tests/url/parse_url_basic_001.phpt @@ -845,6 +845,13 @@ echo "Done"; string(1) "/" } +--> /rest/Users?filter={"id":"123"}: array(2) { + ["path"]=> + string(11) "/rest/Users" + ["query"]=> + string(19) "filter={"id":"123"}" +} + --> http:///blah.com: bool(false) --> http://:80: bool(false) diff --git a/ext/standard/tests/url/parse_url_basic_002.phpt b/ext/standard/tests/url/parse_url_basic_002.phpt index ed0f08a84..c05d1f487 100644 --- a/ext/standard/tests/url/parse_url_basic_002.phpt +++ b/ext/standard/tests/url/parse_url_basic_002.phpt @@ -110,6 +110,7 @@ echo "Done"; --> http://[x:80]/ : string(4) "http" --> : NULL --> / : NULL +--> /rest/Users?filter={"id":"123"} : NULL --> http:///blah.com : bool(false) --> http://:80 : bool(false) --> http://user@:80 : bool(false) diff --git a/ext/standard/tests/url/parse_url_basic_003.phpt b/ext/standard/tests/url/parse_url_basic_003.phpt index a2bbfa648..88eda504d 100644 --- a/ext/standard/tests/url/parse_url_basic_003.phpt +++ b/ext/standard/tests/url/parse_url_basic_003.phpt @@ -109,6 +109,7 @@ echo "Done"; --> http://[x:80]/ : string(6) "[x:80]" --> : NULL --> / : NULL +--> /rest/Users?filter={"id":"123"} : NULL --> http:///blah.com : bool(false) --> http://:80 : bool(false) --> http://user@:80 : bool(false) diff --git a/ext/standard/tests/url/parse_url_basic_004.phpt b/ext/standard/tests/url/parse_url_basic_004.phpt index 839ebee55..e3b9abd91 100644 --- a/ext/standard/tests/url/parse_url_basic_004.phpt +++ b/ext/standard/tests/url/parse_url_basic_004.phpt @@ -109,6 +109,7 @@ echo "Done"; --> http://[x:80]/ : NULL --> : NULL --> / : NULL +--> /rest/Users?filter={"id":"123"} : NULL --> http:///blah.com : bool(false) --> http://:80 : bool(false) --> http://user@:80 : bool(false) diff --git a/ext/standard/tests/url/parse_url_basic_005.phpt b/ext/standard/tests/url/parse_url_basic_005.phpt index c113461fe..1fc946e5b 100644 --- a/ext/standard/tests/url/parse_url_basic_005.phpt +++ b/ext/standard/tests/url/parse_url_basic_005.phpt @@ -109,6 +109,7 @@ echo "Done"; --> http://[x:80]/ : NULL --> : NULL --> / : NULL +--> /rest/Users?filter={"id":"123"} : NULL --> http:///blah.com : bool(false) --> http://:80 : bool(false) --> http://user@:80 : bool(false) diff --git a/ext/standard/tests/url/parse_url_basic_006.phpt b/ext/standard/tests/url/parse_url_basic_006.phpt index 24de1cc23..510432619 100644 --- a/ext/standard/tests/url/parse_url_basic_006.phpt +++ b/ext/standard/tests/url/parse_url_basic_006.phpt @@ -109,6 +109,7 @@ echo "Done"; --> http://[x:80]/ : NULL --> : NULL --> / : NULL +--> /rest/Users?filter={"id":"123"} : NULL --> http:///blah.com : bool(false) --> http://:80 : bool(false) --> http://user@:80 : bool(false) diff --git a/ext/standard/tests/url/parse_url_basic_007.phpt b/ext/standard/tests/url/parse_url_basic_007.phpt index d4006879f..8e0455398 100644 --- a/ext/standard/tests/url/parse_url_basic_007.phpt +++ b/ext/standard/tests/url/parse_url_basic_007.phpt @@ -109,6 +109,7 @@ echo "Done"; --> http://[x:80]/ : string(1) "/" --> : string(0) "" --> / : string(1) "/" +--> /rest/Users?filter={"id":"123"} : string(11) "/rest/Users" --> http:///blah.com : bool(false) --> http://:80 : bool(false) --> http://user@:80 : bool(false) diff --git a/ext/standard/tests/url/parse_url_basic_008.phpt b/ext/standard/tests/url/parse_url_basic_008.phpt index b283829c4..0c7722146 100644 --- a/ext/standard/tests/url/parse_url_basic_008.phpt +++ b/ext/standard/tests/url/parse_url_basic_008.phpt @@ -109,6 +109,7 @@ echo "Done"; --> http://[x:80]/ : NULL --> : NULL --> / : NULL +--> /rest/Users?filter={"id":"123"} : string(19) "filter={"id":"123"}" --> http:///blah.com : bool(false) --> http://:80 : bool(false) --> http://user@:80 : bool(false) diff --git a/ext/standard/tests/url/parse_url_basic_009.phpt b/ext/standard/tests/url/parse_url_basic_009.phpt index a7d70f34d..487b27114 100644 --- a/ext/standard/tests/url/parse_url_basic_009.phpt +++ b/ext/standard/tests/url/parse_url_basic_009.phpt @@ -109,6 +109,7 @@ echo "Done"; --> http://[x:80]/ : NULL --> : NULL --> / : NULL +--> /rest/Users?filter={"id":"123"} : NULL --> http:///blah.com : bool(false) --> http://:80 : bool(false) --> http://user@:80 : bool(false) diff --git a/ext/standard/tests/url/urls.inc b/ext/standard/tests/url/urls.inc index 4192f4a86..d8ffe9137 100644 --- a/ext/standard/tests/url/urls.inc +++ b/ext/standard/tests/url/urls.inc @@ -89,6 +89,7 @@ $urls = array( 'http://[x:80]/', '', '/', +'/rest/Users?filter={"id":"123"}', // Severely malformed URLs that do not parse: 'http:///blah.com', diff --git a/ext/standard/var_unserializer.c b/ext/standard/var_unserializer.c index 59f6119da..eb1a90d1d 100644 --- a/ext/standard/var_unserializer.c +++ b/ext/standard/var_unserializer.c @@ -1,9 +1,9 @@ -/* Generated by re2c 0.13.5 */ +/* Generated by re2c 0.13.5 on Fri Apr 18 15:07:27 2014 */ /* +----------------------------------------------------------------------+ | PHP Version 5 | +----------------------------------------------------------------------+ - | Copyright (c) 1997-2014 The PHP Group | + | Copyright (c) 1997-2013 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | @@ -394,7 +394,15 @@ static inline long object_common1(UNSERIALIZE_PARAMETER, zend_class_entry *ce) (*p) += 2; - object_init_ex(*rval, ce); + if (ce->serialize == NULL) { + object_init_ex(*rval, ce); + } else { + /* If this class implements Serializable, it should not land here but in object_custom(). The passed string + obviously doesn't descend from the regular serializer. */ + zend_error(E_WARNING, "Erroneous data format for unserializing '%s'", ce->name); + return 0; + } + return elements; } @@ -406,6 +414,10 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, long elements) zval *retval_ptr = NULL; zval fname; + if (Z_TYPE_PP(rval) != IS_OBJECT) { + return 0; + } + if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_OBJPROP_PP(rval), elements, 1)) { return 0; } diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re index 76c501e1b..b3f5d29ca 100644 --- a/ext/standard/var_unserializer.re +++ b/ext/standard/var_unserializer.re @@ -400,7 +400,15 @@ static inline long object_common1(UNSERIALIZE_PARAMETER, zend_class_entry *ce) (*p) += 2; - object_init_ex(*rval, ce); + if (ce->serialize == NULL) { + object_init_ex(*rval, ce); + } else { + /* If this class implements Serializable, it should not land here but in object_custom(). The passed string + obviously doesn't descend from the regular serializer. */ + zend_error(E_WARNING, "Erroneous data format for unserializing '%s'", ce->name); + return 0; + } + return elements; } @@ -412,6 +420,10 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, long elements) zval *retval_ptr = NULL; zval fname; + if (Z_TYPE_PP(rval) != IS_OBJECT) { + return 0; + } + if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_OBJPROP_PP(rval), elements, 1)) { return 0; } diff --git a/ext/xmlrpc/xmlrpc-epi-php.c b/ext/xmlrpc/xmlrpc-epi-php.c index 799b47b3e..763ff6940 100644 --- a/ext/xmlrpc/xmlrpc-epi-php.c +++ b/ext/xmlrpc/xmlrpc-epi-php.c @@ -1431,7 +1431,6 @@ XMLRPC_VALUE_TYPE get_zval_xmlrpc_type(zval* value, zval** newvalue) /* {{{ */ type = xmlrpc_string; break; case IS_ARRAY: - case IS_CONSTANT_ARRAY: type = xmlrpc_vector; break; case IS_OBJECT: |