summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFerenc Kovacs <tyrael@php.net>2014-05-01 16:40:20 +0200
committerFerenc Kovacs <tyrael@php.net>2014-05-01 16:40:20 +0200
commitce2028781012b65bb2d5364ebb3c984ad044eaf8 (patch)
treee2dec7a267dbcf8fc8075a014df92a2b5e3d54ce
parent11d102ebbcbbd008a6e50ddc65849dfcfb568da4 (diff)
parent0a738537983b77ddd3c02a0bbd82d98dc8ed06f9 (diff)
downloadphp-git-ce2028781012b65bb2d5364ebb3c984ad044eaf8.tar.gz
Merge branch 'PHP-5.6' into PHP-5.6.0
* PHP-5.6: (271 commits) one heading is enough add new NEWS block for alpha3 XFAIL the leaking multibyte tests, we should really get to the bottom of this Fixed numbering in UPGRADING file Updated NEWS for fixed phpdbg issue #85 Added stdion/stdout/stderr constsnts and their php:// wrappers Fixes issue #85 add missing NEWS entry to the correct release where it was added 5.5.12 was released yesterday add missing NEWS entry to the correct release where it was added Updated NEWS entry for phpdbg add missing NEWS entry to the correct release where it was added add some missing NEWS entries fix test locality inconsitency BFN fix bug #64247 (CURLOPT_INFILE doesn't allow reset) Added further notice about removal of IS_CONSTANT_INDEX/ARRAY macros Fix bug #67060: use default mode of 660 separate travis jobs for debug and non-debug builds separate travis jobs for TS and NTS builds put this test back to XFAIL, as it was before, since it is still leaking memory ... Conflicts: NEWS
-rw-r--r--.gitignore2
-rw-r--r--.travis.yml6
-rw-r--r--NEWS98
-rwxr-xr-xUPGRADING63
-rw-r--r--UPGRADING.INTERNALS102
-rw-r--r--Zend/tests/bug66015.phpt30
-rw-r--r--Zend/tests/bug66660.phpt13
-rw-r--r--Zend/tests/errmsg_040.phpt4
-rw-r--r--Zend/tests/generators/errors/serialize_unserialize_error.phpt9
-rw-r--r--Zend/tests/ns_059.phpt5
-rw-r--r--Zend/zend.h7
-rw-r--r--Zend/zend_API.c7
-rw-r--r--Zend/zend_ast.c79
-rw-r--r--Zend/zend_ast.h3
-rw-r--r--Zend/zend_compile.c57
-rw-r--r--Zend/zend_compile.h2
-rw-r--r--Zend/zend_constants.c4
-rw-r--r--Zend/zend_execute.c8
-rw-r--r--Zend/zend_execute.h10
-rw-r--r--Zend/zend_execute_API.c134
-rw-r--r--Zend/zend_highlight.c2
-rw-r--r--Zend/zend_language_parser.y21
-rw-r--r--Zend/zend_object_handlers.c2
-rw-r--r--Zend/zend_variables.c7
-rw-r--r--Zend/zend_vm_def.h26
-rw-r--r--Zend/zend_vm_execute.h86
-rw-r--r--ext/com_dotnet/com_olechar.c6
-rw-r--r--ext/com_dotnet/com_variant.c8
-rw-r--r--ext/com_dotnet/tests/bug66431_0.phpt42
-rw-r--r--ext/com_dotnet/tests/bug66431_1.phpt60
-rw-r--r--ext/curl/interface.c68
-rw-r--r--ext/curl/multi.c2
-rw-r--r--ext/curl/tests/bug54798.phpt18
-rw-r--r--ext/curl/tests/bug64267.phpt35
-rw-r--r--ext/curl/tests/curl_basic_008.phpt2
-rw-r--r--ext/curl/tests/curl_basic_010.phpt2
-rw-r--r--ext/curl/tests/curl_reset.phpt3
-rw-r--r--ext/date/php_date.c8
-rw-r--r--ext/date/tests/bug66721.phpt11
-rw-r--r--ext/date/tests/bug67118.phpt27
-rw-r--r--ext/dom/documenttype.c36
-rw-r--r--ext/dom/tests/DOMDocumentType_basic_001.phpt4
-rw-r--r--ext/dom/tests/bug67081.phpt43
-rw-r--r--ext/dom/tests/bug67081_0.xml6
-rw-r--r--ext/dom/tests/bug67081_1.xml7
-rw-r--r--ext/dom/tests/bug67081_2.xml5
-rw-r--r--ext/fileinfo/config.m435
-rw-r--r--ext/fileinfo/libmagic.patch16
-rw-r--r--ext/fileinfo/libmagic/readcdf.c2
-rw-r--r--ext/fileinfo/libmagic/strcasestr.c82
-rw-r--r--ext/fileinfo/tests/finfo_file_002.phpt4
-rw-r--r--ext/fileinfo/tests/resources/test.pptbin0 -> 99840 bytes
-rw-r--r--ext/gmp/gmp.c25
-rw-r--r--ext/interbase/config.m44
-rw-r--r--ext/interbase/config.w321
-rw-r--r--ext/interbase/ibase_blobs.c2
-rw-r--r--ext/interbase/ibase_events.c2
-rw-r--r--ext/interbase/ibase_query.c2
-rw-r--r--ext/interbase/ibase_service.c2
-rw-r--r--ext/interbase/interbase.c2
-rw-r--r--ext/interbase/php_ibase_includes.h2
-rw-r--r--ext/interbase/php_ibase_udf.c2
-rw-r--r--ext/interbase/php_interbase.h2
-rw-r--r--ext/interbase/tests/interbase.inc2
-rw-r--r--ext/interbase/tests/skipif.inc2
-rw-r--r--ext/intl/tests/breakiter_getLocale_basic.phpt7
-rw-r--r--ext/intl/tests/breakiter_getLocale_basic2.phpt20
-rw-r--r--ext/intl/tests/bug62070.phpt8
-rw-r--r--ext/intl/tests/bug62070_2.phpt15
-rw-r--r--ext/intl/tests/collator_create2.phpt5
-rw-r--r--ext/intl/tests/collator_create3.phpt82
-rw-r--r--ext/intl/tests/collator_get_sort_key_variant2.phpt1
-rw-r--r--ext/intl/tests/collator_get_sort_key_variant3.phpt98
-rw-r--r--ext/intl/tests/formatter_format3.phpt5
-rw-r--r--ext/intl/tests/formatter_format4.phpt130
-rw-r--r--ext/intl/tests/locale_get_display_name4.phpt3
-rw-r--r--ext/intl/tests/locale_get_display_name5.phpt342
-rw-r--r--ext/intl/tests/timezone_getCanonicalID_variant1.phpt7
-rw-r--r--ext/intl/tests/timezone_getCanonicalID_variant1_2.phpt26
-rw-r--r--ext/json/json.c18
-rw-r--r--ext/json/tests/bug66021.phpt20
-rw-r--r--ext/ldap/ldap.c10
-rw-r--r--ext/mbstring/tests/zend_multibyte-08.phpt3
-rw-r--r--ext/mbstring/tests/zend_multibyte-10.phpt3
-rw-r--r--ext/mbstring/tests/zend_multibyte-11.phpt3
-rw-r--r--ext/mysql/php_mysql.c10
-rw-r--r--ext/mysqli/mysqli.c1
-rw-r--r--ext/mysqli/mysqli_api.c16
-rw-r--r--ext/mysqli/mysqli_fe.c15
-rw-r--r--ext/mysqli/mysqli_nonapi.c15
-rw-r--r--ext/mysqli/mysqli_priv.h2
-rw-r--r--ext/mysqli/tests/bug66043.phpt4
-rw-r--r--ext/mysqli/tests/bug66762.phpt5
-rw-r--r--ext/mysqli/tests/mysqli_change_user.phpt92
-rw-r--r--ext/mysqli/tests/mysqli_change_user_new.phpt44
-rw-r--r--ext/mysqli/tests/mysqli_change_user_old.phpt119
-rw-r--r--ext/mysqli/tests/mysqli_change_user_oo.phpt3
-rw-r--r--ext/mysqli/tests/mysqli_class_mysqli_reflection.phpt9
-rw-r--r--ext/mysqli/tests/mysqli_constants.phpt4
-rw-r--r--ext/mysqli/tests/mysqli_fetch_all.phpt20
-rw-r--r--ext/mysqli/tests/mysqli_fetch_lengths.phpt6
-rw-r--r--ext/mysqli/tests/mysqli_poll.phpt31
-rw-r--r--ext/mysqli/tests/mysqli_poll_kill.phpt27
-rw-r--r--ext/mysqli/tests/mysqli_poll_mixing_insert_select.phpt6
-rw-r--r--ext/mysqli/tests/mysqli_reap_async_query.phpt97
-rw-r--r--ext/mysqli/tests/mysqli_release_savepoint.phpt84
-rw-r--r--ext/mysqli/tests/mysqli_report.phpt5
-rw-r--r--ext/mysqli/tests/mysqli_report_new.phpt50
-rw-r--r--ext/mysqli/tests/mysqli_report_wo_ps.phpt9
-rw-r--r--ext/mysqli/tests/mysqli_savepoint.phpt72
-rw-r--r--ext/mysqli/tests/mysqli_stmt_get_warnings.phpt31
-rw-r--r--ext/mysqli/tests/mysqli_store_result_buffered_c.phpt41
-rw-r--r--ext/mysqli/tests/mysqli_store_result_copy.phpt285
-rw-r--r--ext/mysqlnd/mysqlnd.c61
-rw-r--r--ext/mysqlnd/mysqlnd.h8
-rw-r--r--ext/mysqlnd/mysqlnd_enum_n_def.h10
-rw-r--r--ext/mysqlnd/mysqlnd_ext_plugin.c16
-rw-r--r--ext/mysqlnd/mysqlnd_ext_plugin.h6
-rw-r--r--ext/mysqlnd/mysqlnd_ps.c171
-rw-r--r--ext/mysqlnd/mysqlnd_result.c594
-rw-r--r--ext/mysqlnd/mysqlnd_result.h3
-rw-r--r--ext/mysqlnd/mysqlnd_reverse_api.c5
-rw-r--r--ext/mysqlnd/mysqlnd_reverse_api.h3
-rw-r--r--ext/mysqlnd/mysqlnd_structs.h84
-rw-r--r--ext/mysqlnd/mysqlnd_wireprotocol.c48
-rw-r--r--ext/mysqlnd/mysqlnd_wireprotocol.h6
-rw-r--r--ext/mysqlnd/php_mysqlnd.c9
-rw-r--r--ext/opcache/ZendAccelerator.c6
-rw-r--r--ext/opcache/zend_accelerator_module.c2
-rw-r--r--ext/opcache/zend_accelerator_util_funcs.c36
-rw-r--r--ext/opcache/zend_persist.c2
-rw-r--r--ext/opcache/zend_persist_calc.c2
-rwxr-xr-xext/openssl/openssl.c24
-rw-r--r--ext/openssl/xp_ssl.c4
-rwxr-xr-xext/pdo/config.m42
-rwxr-xr-xext/pdo/config.w322
-rw-r--r--ext/pdo_firebird/config.m44
-rw-r--r--ext/pdo_firebird/config.w321
-rw-r--r--ext/pdo_firebird/firebird_driver.c8
-rw-r--r--ext/pdo_firebird/firebird_statement.c2
-rw-r--r--ext/pdo_firebird/pdo_firebird.c8
-rw-r--r--ext/pdo_firebird/php_pdo_firebird.h2
-rw-r--r--ext/pdo_firebird/php_pdo_firebird_int.h2
-rw-r--r--ext/pdo_mysql/mysql_driver.c4
-rw-r--r--ext/pdo_odbc/odbc_stmt.c4
-rw-r--r--ext/pdo_odbc/php_pdo_odbc_int.h2
-rw-r--r--ext/pgsql/pgsql.c4
-rw-r--r--ext/phar/phar_object.c6
-rw-r--r--ext/phar/phar_path_check.c247
-rw-r--r--ext/phar/phar_path_check.re24
-rw-r--r--ext/phar/tests/create_new_phar.phpt8
-rw-r--r--ext/phar/tests/create_path_error.phpt35
-rw-r--r--ext/posix/tests/posix_kill_basic.phpt4
-rw-r--r--ext/reflection/php_reflection.c6
-rw-r--r--ext/session/mod_files.c37
-rw-r--r--ext/soap/php_encoding.c2
-rwxr-xr-xext/spl/README2
-rwxr-xr-xext/spl/spl.php3
-rw-r--r--ext/spl/spl_observer.c23
-rw-r--r--ext/sqlite3/libsqlite/sqlite3.c4334
-rw-r--r--ext/sqlite3/libsqlite/sqlite3.h9
-rw-r--r--ext/standard/array.c12
-rw-r--r--ext/standard/basic_functions.c7
-rw-r--r--ext/standard/file.c2
-rw-r--r--ext/standard/image.c4
-rw-r--r--ext/standard/php_array.h4
-rw-r--r--ext/standard/php_rand.h1
-rw-r--r--ext/standard/rand.c1
-rw-r--r--ext/standard/tests/array/bug67064.phpt17
-rw-r--r--ext/standard/tests/file/bug65701.phpt30
-rw-r--r--ext/standard/tests/image/getimagesize.phpt17
-rw-r--r--ext/standard/tests/image/image_type_to_mime_type.phpt4
-rw-r--r--ext/standard/tests/image/test-1pix.bmpbin0 -> 58 bytes
-rw-r--r--ext/standard/tests/serialize/005.phpt8
-rw-r--r--ext/standard/tests/serialize/bug67072.phpt12
-rw-r--r--ext/standard/tests/url/parse_url_basic_001.phpt7
-rw-r--r--ext/standard/tests/url/parse_url_basic_002.phpt1
-rw-r--r--ext/standard/tests/url/parse_url_basic_003.phpt1
-rw-r--r--ext/standard/tests/url/parse_url_basic_004.phpt1
-rw-r--r--ext/standard/tests/url/parse_url_basic_005.phpt1
-rw-r--r--ext/standard/tests/url/parse_url_basic_006.phpt1
-rw-r--r--ext/standard/tests/url/parse_url_basic_007.phpt1
-rw-r--r--ext/standard/tests/url/parse_url_basic_008.phpt1
-rw-r--r--ext/standard/tests/url/parse_url_basic_009.phpt1
-rw-r--r--ext/standard/tests/url/urls.inc1
-rw-r--r--ext/standard/var_unserializer.c78
-rw-r--r--ext/standard/var_unserializer.re14
-rw-r--r--ext/xmlrpc/xmlrpc-epi-php.c1
-rw-r--r--main/SAPI.c2
-rw-r--r--main/php_streams.h1
-rw-r--r--main/php_variables.c4
-rw-r--r--main/php_variables.h2
-rw-r--r--main/streams/streams.c48
-rw-r--r--php.ini-development4
-rw-r--r--php.ini-production4
-rw-r--r--sapi/apache2handler/sapi_apache2.c6
-rw-r--r--sapi/cgi/cgi_main.c2
-rw-r--r--sapi/cli/php_cli_server.c3
-rw-r--r--sapi/fpm/fpm/events/epoll.c6
-rw-r--r--sapi/fpm/fpm/fpm_unix.c2
-rw-r--r--sapi/fpm/php-fpm.conf.in6
-rw-r--r--sapi/litespeed/lsapi_main.c2
-rw-r--r--sapi/phpdbg/.gitignore2
-rw-r--r--sapi/phpdbg/Makefile.frag10
-rw-r--r--sapi/phpdbg/config.m415
-rw-r--r--sapi/phpdbg/config.w3212
-rw-r--r--sapi/phpdbg/phpdbg.c378
-rw-r--r--sapi/phpdbg/phpdbg.h76
-rw-r--r--sapi/phpdbg/phpdbg_bp.c102
-rw-r--r--sapi/phpdbg/phpdbg_bp.h2
-rw-r--r--sapi/phpdbg/phpdbg_break.c126
-rw-r--r--sapi/phpdbg/phpdbg_break.h23
-rw-r--r--sapi/phpdbg/phpdbg_btree.c221
-rw-r--r--sapi/phpdbg/phpdbg_btree.h65
-rw-r--r--sapi/phpdbg/phpdbg_cmd.c674
-rw-r--r--sapi/phpdbg/phpdbg_cmd.h112
-rw-r--r--sapi/phpdbg/phpdbg_frame.c4
-rw-r--r--sapi/phpdbg/phpdbg_help.c1393
-rw-r--r--sapi/phpdbg/phpdbg_help.h60
-rw-r--r--sapi/phpdbg/phpdbg_info.c16
-rw-r--r--sapi/phpdbg/phpdbg_info.h12
-rw-r--r--sapi/phpdbg/phpdbg_lexer.c2271
-rw-r--r--sapi/phpdbg/phpdbg_lexer.h348
-rw-r--r--sapi/phpdbg/phpdbg_lexer.l131
-rw-r--r--sapi/phpdbg/phpdbg_list.c187
-rw-r--r--sapi/phpdbg/phpdbg_list.h8
-rw-r--r--sapi/phpdbg/phpdbg_opcode.c5
-rw-r--r--sapi/phpdbg/phpdbg_parser.c1885
-rw-r--r--sapi/phpdbg/phpdbg_parser.h126
-rw-r--r--sapi/phpdbg/phpdbg_parser.y168
-rw-r--r--sapi/phpdbg/phpdbg_print.c175
-rw-r--r--sapi/phpdbg/phpdbg_print.h13
-rw-r--r--sapi/phpdbg/phpdbg_prompt.c883
-rw-r--r--sapi/phpdbg/phpdbg_prompt.h13
-rw-r--r--sapi/phpdbg/phpdbg_set.c258
-rw-r--r--sapi/phpdbg/phpdbg_set.h15
-rw-r--r--sapi/phpdbg/phpdbg_utils.c61
-rw-r--r--sapi/phpdbg/phpdbg_utils.h27
-rw-r--r--sapi/phpdbg/phpdbg_watch.c789
-rw-r--r--sapi/phpdbg/phpdbg_watch.h112
-rw-r--r--sapi/phpdbg/phpdbg_win.c42
-rw-r--r--sapi/phpdbg/phpdbg_win.h37
-rw-r--r--sapi/phpdbg/test.php46
-rw-r--r--tests/classes/constants_error_002.phpt12
-rwxr-xr-xtravis/compile.sh13
-rw-r--r--win32/build/libs_version.txt2
246 files changed, 15456 insertions, 4957 deletions
diff --git a/.gitignore b/.gitignore
index 8d0e7565f2..b9dd252267 100644
--- a/.gitignore
+++ b/.gitignore
@@ -246,6 +246,8 @@ sapi/fpm/php-fpm.1
sapi/fpm/init.d.php-fpm
sapi/fpm/php-fpm.conf
sapi/fpm/fpm/php-cgi
+sapi/phpdbg/phpdbg_parser.c
+sapi/phpdbg/phpdbg_parser.h
sapi/phpdbg/phpdbg
scripts/php-config
scripts/phpize
diff --git a/.travis.yml b/.travis.yml
index 07653040dd..87a8695673 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -15,8 +15,12 @@ env:
- PDO_MYSQL_TEST_USER=travis
- PDO_MYSQL_TEST_PASS=
- PDO_MYSQL_TEST_HOST=127.0.0.1
- matrix:
- REPORT_EXIT_STATUS=1
+ matrix:
+ - ENABLE_MAINTAINER_ZTS=0 ENABLE_DEBUG=0
+ - ENABLE_MAINTAINER_ZTS=1 ENABLE_DEBUG=0
+ - ENABLE_MAINTAINER_ZTS=0 ENABLE_DEBUG=1
+ - ENABLE_MAINTAINER_ZTS=1 ENABLE_DEBUG=1
before_script:
# Compile PHP
diff --git a/NEWS b/NEWS
index 9a749882f6..6bf5c1b25f 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,102 @@
PHP NEWS
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
+01 May 2014, PHP 5.6.0 Beta 2
+
+- CLI server:
+ . Fixed bug #67079 (Missing MIME types for XML/XSL files). (Anatol)
+
+- COM:
+ . Fixed bug #66431 (Special Character via COM Interface (CP_UTF8)). (Anatol)
+
+- Core:
+ . Fixed bug #65701 (copy() doesn't work when destination filename is created
+ by tempnam()). (Boro Sitnikovski)
+ . Fixed bug #66015 (Unexpected array indexing in class's static property). (Bob)
+ . Added (constant) string/array dereferencing to static scalar expressions
+ to complete the set; now possible thanks to bug #66015 being fixed. (Bob)
+ . Fixed bug #66568 (Update reflection information for unserialize() function).
+ (Ferenc)
+ . Fixed bug #66660 (Composer.phar install/update fails). (Ferenc)
+ . Fixed bug #67024 (getimagesize should recognize BMP files with negative
+ height). (Gabor Buella)
+ . Fixed bug #67064 (Countable interface prevents using 2nd parameter
+ ($mode) of count() function). (Bob)
+ . Fixed bug #67072 (Echoing unserialized "SplFileObject" crash). (Anatol)
+ . Fixed bug #67033 (Remove reference to Windows 95). (Anatol)
+
+- cURL:
+ . Fixed bug #64247 (CURLOPT_INFILE doesn't allow reset). (Mike)
+ . Fixed bug #66562 (curl_exec returns differently than curl_multi_getcontent).
+ (Freek Lijten)
+
+- Date:
+ . Fixed bug #66721 (__wakeup of DateTime segfaults when invalid object data is
+ supplied). (Boro Sitnikovski)
+ . Fixed bug #67118 (DateTime constructor crash with invalid data). (Anatol)
+
+- DOM:
+ . Fixed bug #67081 (DOMDocumentType->internalSubset returns entire DOCTYPE tag,
+ not only the subset). (Anatol)
+
+- Fileinfo:
+ . Fixed bug #66907 (Solaris 10 is missing strcasestr and needs substitute).
+ (Anatol)
+ . Fixed bug #66307 (Fileinfo crashes with powerpoint files). (Anatol)
+
+- FPM:
+ . Fixed bug #66482 (unknown entry 'priority' in php-fpm.conf).
+ . Fixed bug #66908 (php-fpm reload leaks epoll_create() file descriptor).
+ (Julio Pintos)
+ . Fixed bug #67060 (sapi/fpm: possible privilege escalation due to insecure
+ default configuration) (CVE-2014-0185). (Stas)
+
+- GMP:
+ . Fixed crashes in serialize/unserialize. (Stas)
+
+- JSON:
+ . Fixed bug #66021 (Blank line inside empty array/object when
+ JSON_PRETTY_PRINT is set). (Kevin Israel)
+
+- LDAP:
+ . Fixed issue with null bytes in LDAP bindings. (Matthew Daley)
+
+- litespeed
+ . Fixed bug #63228 (-Werror=format-security error in lsapi code).
+ (George Wang)
+
+- mysqlnd:
+ . Added a new fetching mode to mysqlnd. (Andrey)
+
+- OpenSSL:
+ . Fix bug #66942 (memory leak in openssl_seal()). (Chuan Ma)
+ . Fix bug #66952 (memory leak in openssl_open()). (Chuan Ma)
+ . Fix bug #66840 (Fix broken build when extension built separately).
+ (Daniel Lowrey)
+
+- phpdbg:
+ . Added watchpoints (watch command). (Bob)
+ . Renamed some commands (next => continue and how to step). (Joe)
+ . Fixed issue #85 (https://github.com/krakjoe/phpdbg/issues/85). (Bob)
+
+- PDO:
+ . Fixed bug #66604 ('pdo/php_pdo_error.h' not copied to the include dir).
+ (Matteo)
+
+- PDO-ODBC:
+ . Fixed bug #50444 (PDO-ODBC changes for 64-bit).
+
+- Phar:
+ . Fix bug #64498 ($phar->buildFromDirectory can't compress file with an accent
+ in its name). (PR #588)
+
+- SQLite:
+ . Fixed bug #66967 (Updated bundled libsqlite to 3.8.4.3). (Anatol)
+
+- Apache2 Handler SAPI:
+ . Fixed Apache log issue caused by APR's lack of support for %zu
+ (APR issue https://issues.apache.org/bugzilla/show_bug.cgi?id=56120).
+ (Jeff Trawick)
+
10 Apr 2014, PHP 5.6.0 Beta 1
- Core:
@@ -135,7 +232,6 @@ PHP NEWS
. Fixed bug #53965 (<xsl:include> cannot find files with relative paths
when loaded with "file://"). (Anatol)
-|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
27 Feb 2014, PHP 5.6.0 Alpha 3
- Core
diff --git a/UPGRADING b/UPGRADING
index ae73503b2f..d68bafde57 100755
--- a/UPGRADING
+++ b/UPGRADING
@@ -4,22 +4,37 @@ PHP 5.6 UPGRADE NOTES
1. Backward Incompatible Changes
2. New Features
-2. Changes in SAPI modules
-3. Deprecated Functionality
-4. Changed Functions
-5. New Functions
-6. New Classes and Interfaces
-7. Removed Extensions
-8. Other Changes to Extensions
-9. New Global Constants
-10. Changes to INI File Handling
-11. Other Changes
+3. Changes in SAPI modules
+4. Deprecated Functionality
+5. Changed Functions
+6. New Functions
+7. New Classes and Interfaces
+8. Removed Extensions
+9. Other Changes to Extensions
+10. New Global Constants
+11. Changes to INI File Handling
+12. Other Changes
========================================
1. Backward Incompatible Changes
========================================
+- Core:
+ By fixing bug #66015 it is no longer possible to overwrite keys in static scalar
+ arrays. Quick example to illustrate:
+ class Test {
+ const FIRST = 1;
+ public $array = array(
+ self::FIRST => 'first',
+ 'second',
+ 'third'
+ );
+ }
+ Test::$array will have as expected three array keys (1, 2, 3) and no longer
+ two (0, 1). self::FIRST will no longer overwrite 'third' having key 1 then,
+ but will mark the beginning of indexing.
+
- JSON:
json_decode() no longer accepts non-lowercase variants of lone JSON true,
false or null values. For example, True or FALSE will now cause json_decode to
@@ -137,7 +152,7 @@ PHP 5.6 UPGRADE NOTES
operations using the ext/pgsql extension.
========================================
-2. Changes in SAPI modules
+3. Changes in SAPI modules
========================================
- Added phpdbg SAPI.
@@ -146,8 +161,12 @@ PHP 5.6 UPGRADE NOTES
- Support for FPM workers changing the apparmor profile through the pool configuration.
(https://wiki.php.net/rfc/fpm_change_hat)
+- Support for several XML MIME types in the built-in CLI server. For static
+ files with extensions .xml, .xsl, .xsd the Content-Type header
+ application/xml is now sent automatically.
+
========================================
-3. Deprecated Functionality
+4. Deprecated Functionality
========================================
- Incompatible context calls:
@@ -176,7 +195,7 @@ PHP 5.6 UPGRADE NOTES
mbstring.internal_encoding
========================================
-4. Changed Functions
+5. Changed Functions
========================================
- cURL:
@@ -216,8 +235,12 @@ PHP 5.6 UPGRADE NOTES
. pg_send_query()
. pg_send_query_params()
+- unserialize:
+ Manipulated serialization strings for objects implementing Serializable by
+ replacing "C:" with "O:" at the start will now produce an error.
+
========================================
-5. New Functions
+6. New Functions
========================================
- GMP:
@@ -256,17 +279,17 @@ PHP 5.6 UPGRADE NOTES
Added SplFileObject::fread($length) to complement fwrite() method (>= 5.5.11)
========================================
-6. New Classes and Interfaces
+7. New Classes and Interfaces
========================================
========================================
-7. Removed Extensions
+8. Removed Extensions
========================================
========================================
-8. Other Changes to Extensions
+9. Other Changes to Extensions
========================================
- cURL:
@@ -330,7 +353,7 @@ PHP 5.6 UPGRADE NOTES
as described in https://bugs.php.net/bug.php?id=42614.
========================================
-9. New Global Constants
+10. New Global Constants
========================================
- LDAP:
@@ -364,7 +387,7 @@ PHP 5.6 UPGRADE NOTES
OPENSSL_DEFAULT_STREAM_CIPHERS string
========================================
-10. Changes to INI File Handling
+11. Changes to INI File Handling
========================================
- Core:
@@ -389,7 +412,7 @@ PHP 5.6 UPGRADE NOTES
global CA default specification as necessary.
========================================
-11. Other Changes
+12. Other Changes
========================================
- File upload:
diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS
index 474aed55f7..8b94783946 100644
--- a/UPGRADING.INTERNALS
+++ b/UPGRADING.INTERNALS
@@ -9,6 +9,12 @@ UPGRADE NOTES - PHP X.Y
d. Arginfo changes
e. tsrm_virtual_cwd.h moved to zend_virtual_cwd.h
f. empty strings are interned
+ g. Additional str_* APIs
+ h. Addition of zend_hash_reindex
+ i. Addition of zend_hash_splice
+ j. An additional parameter is sent to Countable::count()
+ k. Unserialization of manipulated object strings
+ l. Removal of IS_CONSTANT_ARRAY and IS_CONSTANT_INDEX hack
2. Build system changes
a. Unix build system changes
@@ -120,8 +126,95 @@ UPGRADE NOTES - PHP X.Y
String created using STR_EMPTY_ALLOC() are now interned.
convert_to_string use STR_EMPTY_ALLOC() for zval when IS_NULL.
- STR_FREE() shoud be prefered as efree on such strings can raise memory corruption.
+ str_efree() shoud be preferred as efree() on such strings can cause memory
+ corruption.
+ g. Additional str_* APIs
+
+ In addition to the previously existing str_free() and str_efree() macros, the
+ following macros have been introduced to simplify dealing with potentially
+ interned strings:
+
+ str_efree_rel(str) - efree_rel() if not interned
+ str_erealloc(str, new_len) - erealloc() or emalloc+memcpy if interned
+ str_estrndup(str, len) - estrndup() if not interned
+ str_strndup(str, len) - zend_strndup() if not interned
+ str_hash(str, len) - INTERNED_HASH(str) if interned,
+ zend_hash_func(str, len+1) otherwise
+
+ h. Addition of zend_hash_reindex
+
+ A zend_hash_reindex() function with the following prototype has been added:
+
+ void zend_hash_reindex(HashTable *ht, zend_bool only_integer_keys);
+
+ If only_integer_keys==0, this function will change all keys to be continuous,
+ zero-based integers in hash order. If only_integer_keys==1 the same will be
+ done only for keys that were already integers previously, while leaving
+ string keys alone.
+
+ i. Addition of zend_hash_splice
+
+ A zend_hash_splice() macro with the following prototype has been added:
+
+ void zend_hash_splice(
+ HashTable *ht, uint nDataSize, copy_ctor_func_t pCopyConstructor,
+ uint offset, uint length,
+ void **list, uint list_count, HashTable *removed
+ );
+
+ This function performs an in-place splice operation on a hashtable:
+
+ The elements between offset and offset+length are removed and the elements in
+ list[list_count] are inserted in their place. The removed elements can be
+ optionally collected into a hashtable.
+
+ This operation reindexes the hashtable, i.e. integer keys will be zero-based
+ and sequential, while string keys stay intact. The same applies to the
+ elements inserted into the removed HT.
+
+ As a side-effect of this addition the signature of the php_splice() function
+ changed:
+
+ void php_splice(
+ HashTable *ht, zend_uint offset, zend_uint length,
+ zval ***list, zend_uint list_count, HashTable *removed TSRMLS_DC
+ )
+
+ This function now directly forwards to zend_hash_splice(), resets the
+ IAP of ht (for compatibility with the previous implementation) and resets
+ CVs if the passed hashtable is the global symbol table.
+
+ j. An additional parameter is sent to Countable::count()
+
+ That parameter denotes the $mode passed to count; it shouldn't affect any
+ userland code, but any zend_parse_parameters() used with no arguments should
+ fail. Extensions which implement Countable internally, need to accept one
+ optional long as parameter.
+
+ k. Unserialization of manipulated object strings
+
+ Strings requiring unserialization of objects are now explicitly checked
+ whether the object they contain implements the Serializable interface.
+ This solves the situation where manipulated strings could be passed for
+ objects using Serializable to disallow serialization. An object
+ implementing Serializable will always start with "C:" in the serialized
+ string, all other objects are represented with starting "O:". Objects
+ implementing Serializable to disable serialization using
+ zend_class_unserialize_deny and zend_class_serialize_deny, when
+ instantiated from the serializer with a manipulated "O:" string at the
+ start, will most likely be defectively initialized. This is now
+ fixed at the appropriate place by checking for the presence of the
+ serialize callback in the class entry.
+
+ l. Removal of IS_CONSTANT_ARRAY and IS_CONSTANT_INDEX hack
+
+ These two #defines disappeared. Instead we have now IS_CONSTANT_AST which
+ covers also the functionality IS_CONSTANT_ARRAY bid and furthermore the
+ hack for marking zvals as constant index with IS_CONSTANT_INDEX is now
+ superfluous and so removed.
+ Please note that IS_CONSTANT_AST now has the same value than
+ IS_CONSTANT_ARRAY had.
========================
2. Build system changes
@@ -133,5 +226,8 @@ UPGRADE NOTES - PHP X.Y
variable. Previously `bison` was assumed to be in $PATH.
b. Windows build system changes
- -
-
+ - The configure option --enable-static-analyze isn't available anymore.
+ The new option was introduced --with-analyzer.
+ - It is possible to disable PGO for single extensions, to do that
+ define a global variable PHP_MYMODULE_PGO evaluating to false
+ inside config.w32
diff --git a/Zend/tests/bug66015.phpt b/Zend/tests/bug66015.phpt
new file mode 100644
index 0000000000..4f6d51e0dd
--- /dev/null
+++ b/Zend/tests/bug66015.phpt
@@ -0,0 +1,30 @@
+--TEST--
+Bug #66015 (wrong array indexing in class's static property)
+--FILE--
+<?php
+class Test
+{
+ const FIRST = 1;
+ const SECOND = 2;
+ const THIRD = 3;
+
+ protected static $array = [
+ self::FIRST => 'first',
+ 'second',
+ 'third'
+ ];
+
+ public function __construct()
+ {
+ var_export(self::$array);
+ }
+}
+
+$test = new Test();
+?>
+--EXPECTF--
+array (
+ 1 => 'first',
+ 2 => 'second',
+ 3 => 'third',
+)
diff --git a/Zend/tests/bug66660.phpt b/Zend/tests/bug66660.phpt
new file mode 100644
index 0000000000..9ae8a27336
--- /dev/null
+++ b/Zend/tests/bug66660.phpt
@@ -0,0 +1,13 @@
+--TEST--
+Bug #66660 (Composer.phar install/update fails)
+--STDIN--
+<?php __CLASS__ ?>
+--FILE--
+<?php
+file_put_contents(__DIR__."/bug66660.tmp.php", "<?php __CLASS__ ?>");
+echo php_strip_whitespace(__DIR__."/bug66660.tmp.php");
+?>
+--CLEAN--
+<?php unlink(__DIR__."/bug66660.tmp.php"); ?>
+--EXPECT--
+<?php __CLASS__ ?>
diff --git a/Zend/tests/errmsg_040.phpt b/Zend/tests/errmsg_040.phpt
index f3d0afcf0a..c3a007f8c1 100644
--- a/Zend/tests/errmsg_040.phpt
+++ b/Zend/tests/errmsg_040.phpt
@@ -7,7 +7,9 @@ class test {
const TEST = array(1,2,3);
}
+var_dump(test::TEST);
+
echo "Done\n";
?>
--EXPECTF--
-Fatal error: Arrays are not allowed in class constants in %s on line %d
+Fatal error: Arrays are not allowed in constants at run-time in %s on line %d
diff --git a/Zend/tests/generators/errors/serialize_unserialize_error.phpt b/Zend/tests/generators/errors/serialize_unserialize_error.phpt
index aa2d4693f7..b5e77e5028 100644
--- a/Zend/tests/generators/errors/serialize_unserialize_error.phpt
+++ b/Zend/tests/generators/errors/serialize_unserialize_error.phpt
@@ -32,12 +32,11 @@ Stack trace:
#0 %s(%d): serialize(Object(Generator))
#1 {main}
-exception 'Exception' with message 'Unserialization of 'Generator' is not allowed' in %s:%d
-Stack trace:
-#0 [internal function]: Generator->__wakeup()
-#1 %s(%d): unserialize('O:9:"Generator"...')
-#2 {main}
+Warning: Erroneous data format for unserializing 'Generator' in %sserialize_unserialize_error.php on line %d
+
+Notice: unserialize(): Error at offset 19 of 20 bytes in %sserialize_unserialize_error.php on line %s
+bool(false)
exception 'Exception' with message 'Unserialization of 'Generator' is not allowed' in %s:%d
Stack trace:
#0 %s(%d): unserialize('C:9:"Generator"...')
diff --git a/Zend/tests/ns_059.phpt b/Zend/tests/ns_059.phpt
index ea66037b43..48da40b3f6 100644
--- a/Zend/tests/ns_059.phpt
+++ b/Zend/tests/ns_059.phpt
@@ -3,6 +3,9 @@
--FILE--
<?php
const C = array();
+
+var_dump(C);
+?>
--EXPECTF--
-Fatal error: Arrays are not allowed as constants in %sns_059.php on line 2
+Fatal error: Arrays are not allowed in constants at run-time in %sns_059.php on line 4
diff --git a/Zend/zend.h b/Zend/zend.h
index 82e81370e8..9e79833cd7 100644
--- a/Zend/zend.h
+++ b/Zend/zend.h
@@ -589,14 +589,11 @@ typedef int (*zend_write_func_t)(const char *str, uint str_length);
#define IS_STRING 6
#define IS_RESOURCE 7
#define IS_CONSTANT 8
-#define IS_CONSTANT_ARRAY 9
-#define IS_CONSTANT_AST 10
-#define IS_CALLABLE 11
+#define IS_CONSTANT_AST 9
+#define IS_CALLABLE 10
-/* Ugly hack to support constants as static array indices */
#define IS_CONSTANT_TYPE_MASK 0x00f
#define IS_CONSTANT_UNQUALIFIED 0x010
-#define IS_CONSTANT_INDEX 0x080
#define IS_LEXICAL_VAR 0x020
#define IS_LEXICAL_REF 0x040
#define IS_CONSTANT_IN_NAMESPACE 0x100
diff --git a/Zend/zend_API.c b/Zend/zend_API.c
index ed13c6a7d6..b8dd3434ca 100644
--- a/Zend/zend_API.c
+++ b/Zend/zend_API.c
@@ -1070,7 +1070,7 @@ static int zval_update_class_constant(zval **pp, int is_static, int offset TSRML
int ret;
zend_class_entry *old_scope = *scope;
*scope = prop_info->ce;
- ret = zval_update_constant(pp, (void*)1 TSRMLS_CC);
+ ret = zval_update_constant(pp, 1 TSRMLS_CC);
*scope = old_scope;
return ret;
}
@@ -1079,7 +1079,7 @@ static int zval_update_class_constant(zval **pp, int is_static, int offset TSRML
} while (ce);
}
- return zval_update_constant(pp, (void*)1 TSRMLS_CC);
+ return zval_update_constant(pp, 1 TSRMLS_CC);
}
return 0;
}
@@ -1093,7 +1093,7 @@ ZEND_API void zend_update_class_constants(zend_class_entry *class_type TSRMLS_DC
int i;
*scope = class_type;
- zend_hash_apply_with_argument(&class_type->constants_table, (apply_func_arg_t) zval_update_constant, (void*)1 TSRMLS_CC);
+ zend_hash_apply_with_argument(&class_type->constants_table, (apply_func_arg_t) zval_update_constant, (void *)1 TSRMLS_CC);
for (i = 0; i < class_type->default_properties_count; i++) {
if (class_type->default_properties_table[i]) {
@@ -3463,7 +3463,6 @@ ZEND_API int zend_declare_property_ex(zend_class_entry *ce, const char *name, in
if (ce->type & ZEND_INTERNAL_CLASS) {
switch(Z_TYPE_P(property)) {
case IS_ARRAY:
- case IS_CONSTANT_ARRAY:
case IS_OBJECT:
case IS_RESOURCE:
zend_error(E_CORE_ERROR, "Internal zval's can't be arrays, objects or resources");
diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c
index 9a0808db9d..583a52321b 100644
--- a/Zend/zend_ast.c
+++ b/Zend/zend_ast.c
@@ -63,6 +63,27 @@ ZEND_API zend_ast* zend_ast_create_ternary(uint kind, zend_ast *op0, zend_ast *o
return ast;
}
+ZEND_API zend_ast* zend_ast_create_dynamic(uint kind)
+{
+ zend_ast *ast = emalloc(sizeof(zend_ast) + sizeof(zend_ast*) * 3); /* use 4 children as deafult */
+ ast->kind = kind;
+ ast->children = 0;
+ return ast;
+}
+
+ZEND_API void zend_ast_dynamic_add(zend_ast **ast, zend_ast *op)
+{
+ if ((*ast)->children >= 4 && (*ast)->children == ((*ast)->children & -(*ast)->children)) {
+ *ast = erealloc(*ast, sizeof(zend_ast) + sizeof(zend_ast*) * ((*ast)->children * 2 + 1));
+ }
+ (&(*ast)->u.child)[(*ast)->children++] = op;
+}
+
+ZEND_API void zend_ast_dynamic_shrink(zend_ast **ast)
+{
+ *ast = erealloc(*ast, sizeof(zend_ast) + sizeof(zend_ast*) * ((*ast)->children - 1));
+}
+
ZEND_API int zend_ast_is_ct_constant(zend_ast *ast)
{
int i;
@@ -233,7 +254,7 @@ ZEND_API void zend_ast_evaluate(zval *result, zend_ast *ast, zend_class_entry *s
*result = *ast->u.val;
zval_copy_ctor(result);
if (IS_CONSTANT_TYPE(Z_TYPE_P(result))) {
- zval_update_constant_ex(&result, (void *) 1, scope TSRMLS_CC);
+ zval_update_constant_ex(&result, 1, scope TSRMLS_CC);
}
break;
case ZEND_BOOL_AND:
@@ -284,6 +305,35 @@ ZEND_API void zend_ast_evaluate(zval *result, zend_ast *ast, zend_class_entry *s
sub_function(result, &op1, &op2 TSRMLS_CC);
zval_dtor(&op2);
break;
+ case ZEND_INIT_ARRAY:
+ INIT_PZVAL(result);
+ array_init(result);
+ {
+ int i;
+ zend_bool has_key;
+ for (i = 0; i < ast->children; i+=2) {
+ zval *expr;
+ MAKE_STD_ZVAL(expr);
+ if ((has_key = !!(&ast->u.child)[i])) {
+ zend_ast_evaluate(&op1, (&ast->u.child)[i], scope TSRMLS_CC);
+ }
+ zend_ast_evaluate(expr, (&ast->u.child)[i+1], scope TSRMLS_CC);
+ zend_do_add_static_array_element(result, has_key?&op1:NULL, expr);
+ }
+ }
+ break;
+ case ZEND_FETCH_DIM_R:
+ zend_ast_evaluate(&op1, (&ast->u.child)[0], scope TSRMLS_CC);
+ zend_ast_evaluate(&op2, (&ast->u.child)[1], scope TSRMLS_CC);
+ {
+ zval *tmp;
+ zend_fetch_dimension_by_zval(&tmp, &op1, &op2 TSRMLS_CC);
+ *result = *tmp;
+ efree(tmp);
+ }
+ zval_dtor(&op1);
+ zval_dtor(&op2);
+ break;
default:
zend_error(E_ERROR, "Unsupported constant expression");
}
@@ -297,26 +347,17 @@ ZEND_API zend_ast *zend_ast_copy(zend_ast *ast)
zend_ast *copy = zend_ast_create_constant(ast->u.val);
zval_copy_ctor(copy->u.val);
return copy;
- } else {
- switch (ast->children) {
- case 1:
- return zend_ast_create_unary(
- ast->kind,
- zend_ast_copy((&ast->u.child)[0]));
- case 2:
- return zend_ast_create_binary(
- ast->kind,
- zend_ast_copy((&ast->u.child)[0]),
- zend_ast_copy((&ast->u.child)[1]));
- case 3:
- return zend_ast_create_ternary(
- ast->kind,
- zend_ast_copy((&ast->u.child)[0]),
- zend_ast_copy((&ast->u.child)[1]),
- zend_ast_copy((&ast->u.child)[2]));
+ } else if (ast->children) {
+ zend_ast *new = emalloc(sizeof(zend_ast) + sizeof(zend_ast*) * (ast->children - 1));
+ int i;
+ new->kind = ast->kind;
+ new->children = ast->children;
+ for (i = 0; i < ast->children; i++) {
+ (&new->u.child)[i] = zend_ast_copy((&ast->u.child)[i]);
}
+ return new;
}
- return NULL;
+ return zend_ast_create_dynamic(ast->kind);
}
ZEND_API void zend_ast_destroy(zend_ast *ast)
diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h
index 927e99cff8..f8df15c95b 100644
--- a/Zend/zend_ast.h
+++ b/Zend/zend_ast.h
@@ -50,6 +50,9 @@ ZEND_API zend_ast *zend_ast_create_constant(zval *zv);
ZEND_API zend_ast *zend_ast_create_unary(uint kind, zend_ast *op0);
ZEND_API zend_ast *zend_ast_create_binary(uint kind, zend_ast *op0, zend_ast *op1);
ZEND_API zend_ast *zend_ast_create_ternary(uint kind, zend_ast *op0, zend_ast *op1, zend_ast *op2);
+ZEND_API zend_ast* zend_ast_create_dynamic(uint kind);
+ZEND_API void zend_ast_dynamic_add(zend_ast **ast, zend_ast *op);
+ZEND_API void zend_ast_dynamic_shrink(zend_ast **ast);
ZEND_API int zend_ast_is_ct_constant(zend_ast *ast);
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index 1bcd430840..beb53402ba 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -1933,7 +1933,7 @@ void zend_do_receive_param(zend_uchar op, znode *varname, const znode *initializ
if (op == ZEND_RECV_INIT) {
if (Z_TYPE(initialization->u.constant) == IS_NULL || (Z_TYPE(initialization->u.constant) == IS_CONSTANT && !strcasecmp(Z_STRVAL(initialization->u.constant), "NULL")) || Z_TYPE(initialization->u.constant) == IS_CONSTANT_AST) {
cur_arg_info->allow_null = 1;
- } else if (Z_TYPE(initialization->u.constant) != IS_ARRAY && Z_TYPE(initialization->u.constant) != IS_CONSTANT_ARRAY) {
+ } else if (Z_TYPE(initialization->u.constant) != IS_ARRAY) {
zend_error_noreturn(E_COMPILE_ERROR, "Default value for parameters with array type hint can only be an array or NULL");
}
}
@@ -3461,7 +3461,7 @@ static char * zend_get_function_declaration(zend_function *fptr TSRMLS_DC) /* {{
*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)) {
memcpy(offset, "true", 4);
@@ -5442,10 +5442,6 @@ void zend_do_declare_class_constant(znode *var_name, const znode *value TSRMLS_D
const char *cname = NULL;
zend_ulong hash;
- if(Z_TYPE(value->u.constant) == IS_CONSTANT_ARRAY) {
- zend_error_noreturn(E_COMPILE_ERROR, "Arrays are not allowed in class constants");
- return;
- }
if ((CG(active_class_entry)->ce_flags & ZEND_ACC_TRAIT) == ZEND_ACC_TRAIT) {
zend_error_noreturn(E_COMPILE_ERROR, "Traits cannot have constants");
return;
@@ -5887,57 +5883,30 @@ void zend_do_add_array_element(znode *result, const znode *expr, const znode *of
}
/* }}} */
-void zend_do_add_static_array_element(znode *result, znode *offset, const znode *expr) /* {{{ */
+void zend_do_add_static_array_element(zval *result, zval *offset, const zval *expr) /* {{{ */
{
- zval *element;
-
- ALLOC_ZVAL(element);
- *element = expr->u.constant;
if (offset) {
- switch (Z_TYPE(offset->u.constant) & IS_CONSTANT_TYPE_MASK) {
- case IS_CONSTANT:
- /* Ugly hack to denote that this value has a constant index */
- Z_TYPE_P(element) |= IS_CONSTANT_INDEX;
- Z_STRVAL(offset->u.constant) = erealloc(Z_STRVAL(offset->u.constant), Z_STRLEN(offset->u.constant)+3);
- Z_STRVAL(offset->u.constant)[Z_STRLEN(offset->u.constant)+1] = Z_TYPE(offset->u.constant);
- Z_STRVAL(offset->u.constant)[Z_STRLEN(offset->u.constant)+2] = 0;
- zend_symtable_update(Z_ARRVAL(result->u.constant), Z_STRVAL(offset->u.constant), Z_STRLEN(offset->u.constant)+3, &element, sizeof(zval *), NULL);
- zval_dtor(&offset->u.constant);
- break;
- case IS_CONSTANT_AST: {
- /* Another ugly hack to store the data about the AST in the array */
- char* key;
- int len = sizeof(zend_ast *);
- Z_TYPE_P(element) |= IS_CONSTANT_INDEX;
-
- key = emalloc(len + 2);
- *(zend_ast **)key = Z_AST(offset->u.constant);
- key[len] = Z_TYPE(offset->u.constant);
- key[len + 1] = 0;
- zend_symtable_update(Z_ARRVAL(result->u.constant), key, len + 2, &element, sizeof(zval *), NULL);
- efree(key);
- break;
- }
+ switch (Z_TYPE_P(offset)) {
case IS_STRING:
- zend_symtable_update(Z_ARRVAL(result->u.constant), Z_STRVAL(offset->u.constant), Z_STRLEN(offset->u.constant)+1, &element, sizeof(zval *), NULL);
- zval_dtor(&offset->u.constant);
+ zend_symtable_update(Z_ARRVAL_P(result), Z_STRVAL_P(offset), Z_STRLEN_P(offset)+1, &expr, sizeof(zval *), NULL);
+ zval_dtor(offset);
break;
case IS_NULL:
- zend_symtable_update(Z_ARRVAL(result->u.constant), "", 1, &element, sizeof(zval *), NULL);
+ zend_symtable_update(Z_ARRVAL_P(result), "", 1, &expr, sizeof(zval *), NULL);
break;
case IS_LONG:
case IS_BOOL:
- zend_hash_index_update(Z_ARRVAL(result->u.constant), Z_LVAL(offset->u.constant), &element, sizeof(zval *), NULL);
+ zend_hash_index_update(Z_ARRVAL_P(result), Z_LVAL_P(offset), &expr, sizeof(zval *), NULL);
break;
case IS_DOUBLE:
- zend_hash_index_update(Z_ARRVAL(result->u.constant), zend_dval_to_lval(Z_DVAL(offset->u.constant)), &element, sizeof(zval *), NULL);
+ zend_hash_index_update(Z_ARRVAL_P(result), zend_dval_to_lval(Z_DVAL_P(offset)), &expr, sizeof(zval *), NULL);
break;
- case IS_CONSTANT_ARRAY:
+ case IS_ARRAY:
zend_error(E_ERROR, "Illegal offset type");
break;
}
} else {
- zend_hash_next_index_insert(Z_ARRVAL(result->u.constant), &element, sizeof(zval *), NULL);
+ zend_hash_next_index_insert(Z_ARRVAL_P(result), &expr, sizeof(zval *), NULL);
}
}
/* }}} */
@@ -7318,10 +7287,6 @@ void zend_do_declare_constant(znode *name, znode *value TSRMLS_DC) /* {{{ */
zend_op *opline;
zval **ns_name;
- if(Z_TYPE(value->u.constant) == IS_CONSTANT_ARRAY) {
- zend_error_noreturn(E_COMPILE_ERROR, "Arrays are not allowed as constants");
- }
-
if (zend_get_ct_const(&name->u.constant, 0 TSRMLS_CC)) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot redeclare constant '%s'", Z_STRVAL(name->u.constant));
}
diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h
index 5362058fa2..99d3704b76 100644
--- a/Zend/zend_compile.h
+++ b/Zend/zend_compile.h
@@ -598,7 +598,7 @@ void zend_do_shell_exec(znode *result, const znode *cmd TSRMLS_DC);
void zend_do_init_array(znode *result, const znode *expr, const znode *offset, zend_bool is_ref TSRMLS_DC);
void zend_do_add_array_element(znode *result, const znode *expr, const znode *offset, zend_bool is_ref TSRMLS_DC);
-void zend_do_add_static_array_element(znode *result, znode *offset, const znode *expr);
+void zend_do_add_static_array_element(zval *result, zval *offset, const zval *expr);
void zend_do_list_init(TSRMLS_D);
void zend_do_list_end(znode *result, znode *expr TSRMLS_DC);
void zend_do_add_list_element(const znode *element TSRMLS_DC);
diff --git a/Zend/zend_constants.c b/Zend/zend_constants.c
index 03762b50d7..0f44f58a77 100644
--- a/Zend/zend_constants.c
+++ b/Zend/zend_constants.c
@@ -408,7 +408,7 @@ ZEND_API int zend_get_constant_ex(const char *name, uint name_len, zval *result,
efree(lcname);
if(found_const) {
*result = c->value;
- zval_update_constant_ex(&result, (void*)1, NULL TSRMLS_CC);
+ zval_update_constant_ex(&result, 1, NULL TSRMLS_CC);
zval_copy_ctor(result);
Z_SET_REFCOUNT_P(result, 1);
Z_UNSET_ISREF_P(result);
@@ -423,7 +423,7 @@ ZEND_API int zend_get_constant_ex(const char *name, uint name_len, zval *result,
retval = 0;
finish:
if (retval) {
- zval_update_constant_ex(ret_constant, (void*)1, ce TSRMLS_CC);
+ zval_update_constant_ex(ret_constant, 1, ce TSRMLS_CC);
*result = **ret_constant;
zval_copy_ctor(result);
INIT_PZVAL(result);
diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c
index 66accd61e9..bdf8b6e70f 100644
--- a/Zend/zend_execute.c
+++ b/Zend/zend_execute.c
@@ -1352,9 +1352,15 @@ static void zend_fetch_dimension_address_read(temp_variable *result, zval *conta
}
}
+ZEND_API void zend_fetch_dimension_by_zval(zval **result, zval *container, zval *dim TSRMLS_DC) {
+ temp_variable tmp;
+ zend_fetch_dimension_address_read(&tmp, container, dim, IS_TMP_VAR, BP_VAR_R TSRMLS_CC);
+ *result = tmp.var.ptr;
+}
+
static void zend_fetch_property_address(temp_variable *result, zval **container_ptr, zval *prop_ptr, const zend_literal *key, int type TSRMLS_DC)
{
- zval *container = *container_ptr;;
+ zval *container = *container_ptr;
if (Z_TYPE_P(container) != IS_OBJECT) {
if (container == &EG(error_zval)) {
diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h
index 0d3a908a15..9bde312e53 100644
--- a/Zend/zend_execute.h
+++ b/Zend/zend_execute.h
@@ -158,10 +158,10 @@ static zend_always_inline int i_zend_is_true(zval *op)
return result;
}
-ZEND_API int zval_update_constant(zval **pp, void *arg TSRMLS_DC);
-ZEND_API int zval_update_constant_inline_change(zval **pp, void *arg TSRMLS_DC);
-ZEND_API int zval_update_constant_no_inline_change(zval **pp, void *arg TSRMLS_DC);
-ZEND_API int zval_update_constant_ex(zval **pp, void *arg, zend_class_entry *scope TSRMLS_DC);
+ZEND_API int zval_update_constant(zval **pp, zend_bool inline_change TSRMLS_DC);
+ZEND_API int zval_update_constant_inline_change(zval **pp, zend_class_entry *scope TSRMLS_DC);
+ZEND_API int zval_update_constant_no_inline_change(zval **pp, zend_class_entry *scope TSRMLS_DC);
+ZEND_API int zval_update_constant_ex(zval **pp, zend_bool inline_change, zend_class_entry *scope TSRMLS_DC);
/* dedicated Zend executor functions - do not use! */
#define ZEND_VM_STACK_PAGE_SIZE ((16 * 1024) - 16)
@@ -362,6 +362,8 @@ ZEND_API zend_class_entry *zend_fetch_class(const char *class_name, uint class_n
ZEND_API zend_class_entry *zend_fetch_class_by_name(const char *class_name, uint class_name_len, const zend_literal *key, int fetch_type TSRMLS_DC);
void zend_verify_abstract_class(zend_class_entry *ce TSRMLS_DC);
+ZEND_API void zend_fetch_dimension_by_zval(zval **result, zval *container, zval *dim TSRMLS_DC);
+
#ifdef ZEND_WIN32
void zend_init_timeout_thread(void);
void zend_shutdown_timeout_thread(void);
diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c
index 5d5153dc13..c9de6b93ca 100644
--- a/Zend/zend_execute_API.c
+++ b/Zend/zend_execute_API.c
@@ -451,28 +451,14 @@ ZEND_API int zend_is_true(zval *op) /* {{{ */
#include "../TSRM/tsrm_strtok_r.h"
-#define IS_VISITED_CONSTANT IS_CONSTANT_INDEX
+#define IS_VISITED_CONSTANT 0x80
#define IS_CONSTANT_VISITED(p) (Z_TYPE_P(p) & IS_VISITED_CONSTANT)
#define Z_REAL_TYPE_P(p) (Z_TYPE_P(p) & ~IS_VISITED_CONSTANT)
#define MARK_CONSTANT_VISITED(p) Z_TYPE_P(p) |= IS_VISITED_CONSTANT
-static void zval_deep_copy(zval **p)
-{
- zval *value;
-
- ALLOC_ZVAL(value);
- *value = **p;
- Z_TYPE_P(value) &= ~IS_CONSTANT_INDEX;
- zval_copy_ctor(value);
- Z_TYPE_P(value) = Z_TYPE_PP(p);
- INIT_PZVAL(value);
- *p = value;
-}
-
-ZEND_API int zval_update_constant_ex(zval **pp, void *arg, zend_class_entry *scope TSRMLS_DC) /* {{{ */
+ZEND_API int zval_update_constant_ex(zval **pp, zend_bool inline_change, zend_class_entry *scope TSRMLS_DC) /* {{{ */
{
zval *p = *pp;
- zend_bool inline_change = (zend_bool) (zend_uintptr_t) arg;
zval const_value;
char *colon;
@@ -559,110 +545,6 @@ ZEND_API int zval_update_constant_ex(zval **pp, void *arg, zend_class_entry *sco
Z_SET_REFCOUNT_P(p, refcount);
Z_SET_ISREF_TO_P(p, is_ref);
- } else if (Z_TYPE_P(p) == IS_CONSTANT_ARRAY) {
- zval **element, *new_val;
- char *str_index;
- uint str_index_len;
- ulong num_index;
- int ret;
-
- SEPARATE_ZVAL_IF_NOT_REF(pp);
- p = *pp;
- Z_TYPE_P(p) = IS_ARRAY;
-
- if (!inline_change) {
- zval *tmp;
- HashTable *tmp_ht = NULL;
-
- ALLOC_HASHTABLE(tmp_ht);
- zend_hash_init(tmp_ht, zend_hash_num_elements(Z_ARRVAL_P(p)), NULL, ZVAL_PTR_DTOR, 0);
- zend_hash_copy(tmp_ht, Z_ARRVAL_P(p), (copy_ctor_func_t) zval_deep_copy, (void *) &tmp, sizeof(zval *));
- Z_ARRVAL_P(p) = tmp_ht;
- }
-
- /* First go over the array and see if there are any constant indices */
- zend_hash_internal_pointer_reset(Z_ARRVAL_P(p));
- while (zend_hash_get_current_data(Z_ARRVAL_P(p), (void **) &element) == SUCCESS) {
- if (!(Z_TYPE_PP(element) & IS_CONSTANT_INDEX)) {
- zend_hash_move_forward(Z_ARRVAL_P(p));
- continue;
- }
- Z_TYPE_PP(element) &= ~IS_CONSTANT_INDEX;
- if (zend_hash_get_current_key_ex(Z_ARRVAL_P(p), &str_index, &str_index_len, &num_index, 0, NULL) != HASH_KEY_IS_STRING) {
- zend_hash_move_forward(Z_ARRVAL_P(p));
- continue;
- }
- if (str_index[str_index_len - 2] == IS_CONSTANT_AST) {
- zend_ast_evaluate(&const_value, *(zend_ast **)str_index, scope TSRMLS_CC);
- zend_ast_destroy(*(zend_ast **)str_index);
- } else if (!zend_get_constant_ex(str_index, str_index_len - 3, &const_value, scope, str_index[str_index_len - 2] TSRMLS_CC)) {
- char *actual;
- const char *save = str_index;
- if ((colon = (char*)zend_memrchr(str_index, ':', str_index_len - 3))) {
- zend_error(E_ERROR, "Undefined class constant '%s'", str_index);
- str_index_len -= ((colon - str_index) + 1);
- str_index = colon;
- } else {
- if (str_index[str_index_len - 2] & IS_CONSTANT_UNQUALIFIED) {
- if ((actual = (char *)zend_memrchr(str_index, '\\', str_index_len - 3))) {
- actual++;
- str_index_len -= (actual - str_index);
- str_index = actual;
- }
- }
- if (str_index[0] == '\\') {
- ++str_index;
- --str_index_len;
- }
- if (save[0] == '\\') {
- ++save;
- }
- if ((str_index[str_index_len - 2] & IS_CONSTANT_UNQUALIFIED) == 0) {
- zend_error(E_ERROR, "Undefined constant '%s'", save);
- }
- zend_error(E_NOTICE, "Use of undefined constant %s - assumed '%s'", str_index, str_index);
- }
- ZVAL_STRINGL(&const_value, str_index, str_index_len-3, 1);
- }
-
- if (Z_REFCOUNT_PP(element) > 1) {
- ALLOC_ZVAL(new_val);
- *new_val = **element;
- zval_copy_ctor(new_val);
- Z_SET_REFCOUNT_P(new_val, 1);
- Z_UNSET_ISREF_P(new_val);
-
- /* preserve this bit for inheritance */
- Z_TYPE_PP(element) |= IS_CONSTANT_INDEX;
- zval_ptr_dtor(element);
- *element = new_val;
- }
-
- switch (Z_TYPE(const_value)) {
- case IS_STRING:
- ret = zend_symtable_update_current_key(Z_ARRVAL_P(p), Z_STRVAL(const_value), Z_STRLEN(const_value) + 1, HASH_UPDATE_KEY_IF_BEFORE);
- break;
- case IS_BOOL:
- case IS_LONG:
- ret = zend_hash_update_current_key_ex(Z_ARRVAL_P(p), HASH_KEY_IS_LONG, NULL, 0, Z_LVAL(const_value), HASH_UPDATE_KEY_IF_BEFORE, NULL);
- break;
- case IS_DOUBLE:
- ret = zend_hash_update_current_key_ex(Z_ARRVAL_P(p), HASH_KEY_IS_LONG, NULL, 0, zend_dval_to_lval(Z_DVAL(const_value)), HASH_UPDATE_KEY_IF_BEFORE, NULL);
- break;
- case IS_NULL:
- ret = zend_hash_update_current_key_ex(Z_ARRVAL_P(p), HASH_KEY_IS_STRING, "", 1, 0, HASH_UPDATE_KEY_IF_BEFORE, NULL);
- break;
- default:
- ret = SUCCESS;
- break;
- }
- if (ret == SUCCESS) {
- zend_hash_move_forward(Z_ARRVAL_P(p));
- }
- zval_dtor(&const_value);
- }
- zend_hash_apply_with_argument(Z_ARRVAL_P(p), (apply_func_arg_t) zval_update_constant_inline_change, (void *) scope TSRMLS_CC);
- zend_hash_internal_pointer_reset(Z_ARRVAL_P(p));
} else if (Z_TYPE_P(p) == IS_CONSTANT_AST) {
SEPARATE_ZVAL_IF_NOT_REF(pp);
p = *pp;
@@ -677,21 +559,21 @@ ZEND_API int zval_update_constant_ex(zval **pp, void *arg, zend_class_entry *sco
}
/* }}} */
-ZEND_API int zval_update_constant_inline_change(zval **pp, void *scope TSRMLS_DC) /* {{{ */
+ZEND_API int zval_update_constant_inline_change(zval **pp, zend_class_entry *scope TSRMLS_DC) /* {{{ */
{
- return zval_update_constant_ex(pp, (void*)1, scope TSRMLS_CC);
+ return zval_update_constant_ex(pp, 1, scope TSRMLS_CC);
}
/* }}} */
-ZEND_API int zval_update_constant_no_inline_change(zval **pp, void *scope TSRMLS_DC) /* {{{ */
+ZEND_API int zval_update_constant_no_inline_change(zval **pp, zend_class_entry *scope TSRMLS_DC) /* {{{ */
{
- return zval_update_constant_ex(pp, (void*)0, scope TSRMLS_CC);
+ return zval_update_constant_ex(pp, 0, scope TSRMLS_CC);
}
/* }}} */
-ZEND_API int zval_update_constant(zval **pp, void *arg TSRMLS_DC) /* {{{ */
+ZEND_API int zval_update_constant(zval **pp, zend_bool inline_change TSRMLS_DC) /* {{{ */
{
- return zval_update_constant_ex(pp, arg, NULL TSRMLS_CC);
+ return zval_update_constant_ex(pp, inline_change, NULL TSRMLS_CC);
}
/* }}} */
diff --git a/Zend/zend_highlight.c b/Zend/zend_highlight.c
index e4f8d0237a..68f2b7b72d 100644
--- a/Zend/zend_highlight.c
+++ b/Zend/zend_highlight.c
@@ -211,7 +211,7 @@ ZEND_API void zend_strip(TSRMLS_D)
break;
default:
- efree(token.value.str.val);
+ STR_FREE(token.value.str.val);
break;
}
}
diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y
index b7467b7552..56e702e8f9 100644
--- a/Zend/zend_language_parser.y
+++ b/Zend/zend_language_parser.y
@@ -986,9 +986,7 @@ static_class_constant:
;
static_scalar: /* compile-time evaluated scalars */
- static_scalar_value { zend_do_constant_expression(&$$, $1.u.ast TSRMLS_CC); }
- | T_ARRAY '(' static_array_pair_list ')' { $$ = $3; Z_TYPE($$.u.constant) = IS_CONSTANT_ARRAY; }
- | '[' static_array_pair_list ']' { $$ = $2; Z_TYPE($$.u.constant) = IS_CONSTANT_ARRAY; }
+ static_scalar_value { zend_do_constant_expression(&$$, $1.u.ast TSRMLS_CC); }
;
static_scalar_value:
@@ -997,13 +995,16 @@ static_scalar_value:
| namespace_name { zend_do_fetch_constant(&$$, NULL, &$1, ZEND_CT, 1 TSRMLS_CC); $$.u.ast = zend_ast_create_constant(&$$.u.constant); }
| T_NAMESPACE T_NS_SEPARATOR namespace_name { $$.op_type = IS_CONST; ZVAL_EMPTY_STRING(&$$.u.constant); zend_do_build_namespace_name(&$$, &$$, &$3 TSRMLS_CC); $3 = $$; zend_do_fetch_constant(&$$, NULL, &$3, ZEND_CT, 0 TSRMLS_CC); $$.u.ast = zend_ast_create_constant(&$$.u.constant); }
| T_NS_SEPARATOR namespace_name { char *tmp = estrndup(Z_STRVAL($2.u.constant), Z_STRLEN($2.u.constant)+1); memcpy(&(tmp[1]), Z_STRVAL($2.u.constant), Z_STRLEN($2.u.constant)+1); tmp[0] = '\\'; efree(Z_STRVAL($2.u.constant)); Z_STRVAL($2.u.constant) = tmp; ++Z_STRLEN($2.u.constant); zend_do_fetch_constant(&$$, NULL, &$2, ZEND_CT, 0 TSRMLS_CC); $$.u.ast = zend_ast_create_constant(&$$.u.constant); }
+ | T_ARRAY '(' static_array_pair_list ')' { $$ = $3; }
+ | '[' static_array_pair_list ']' { $$ = $2; }
| static_class_constant { $$.u.ast = zend_ast_create_constant(&$1.u.constant); }
| T_CLASS_C { $$.u.ast = zend_ast_create_constant(&$1.u.constant); }
| static_operation { $$ = $1; }
;
static_operation:
- static_scalar_value '+' static_scalar_value { $$.u.ast = zend_ast_create_binary(ZEND_ADD, $1.u.ast, $3.u.ast); }
+ static_scalar_value '[' static_scalar_value ']' { $$.u.ast = zend_ast_create_binary(ZEND_FETCH_DIM_R, $1.u.ast, $3.u.ast); }
+ | static_scalar_value '+' static_scalar_value { $$.u.ast = zend_ast_create_binary(ZEND_ADD, $1.u.ast, $3.u.ast); }
| static_scalar_value '-' static_scalar_value { $$.u.ast = zend_ast_create_binary(ZEND_SUB, $1.u.ast, $3.u.ast); }
| static_scalar_value '*' static_scalar_value { $$.u.ast = zend_ast_create_binary(ZEND_MUL, $1.u.ast, $3.u.ast); }
| static_scalar_value T_POW static_scalar_value { $$.u.ast = zend_ast_create_binary(ZEND_POW, $1.u.ast, $3.u.ast); }
@@ -1053,8 +1054,8 @@ scalar:
static_array_pair_list:
- /* empty */ { $$.op_type = IS_CONST; INIT_PZVAL(&$$.u.constant); array_init(&$$.u.constant); }
- | non_empty_static_array_pair_list possible_comma { $$ = $1; }
+ /* empty */ { $$.op_type = IS_CONST; INIT_PZVAL(&$$.u.constant); array_init(&$$.u.constant); $$.u.ast = zend_ast_create_constant(&$$.u.constant); }
+ | non_empty_static_array_pair_list possible_comma { zend_ast_dynamic_shrink(&$1.u.ast); $$ = $1; }
;
possible_comma:
@@ -1063,10 +1064,10 @@ possible_comma:
;
non_empty_static_array_pair_list:
- non_empty_static_array_pair_list ',' static_scalar T_DOUBLE_ARROW static_scalar { zend_do_add_static_array_element(&$$, &$3, &$5); }
- | non_empty_static_array_pair_list ',' static_scalar { zend_do_add_static_array_element(&$$, NULL, &$3); }
- | static_scalar T_DOUBLE_ARROW static_scalar { $$.op_type = IS_CONST; INIT_PZVAL(&$$.u.constant); array_init(&$$.u.constant); zend_do_add_static_array_element(&$$, &$1, &$3); }
- | static_scalar { $$.op_type = IS_CONST; INIT_PZVAL(&$$.u.constant); array_init(&$$.u.constant); zend_do_add_static_array_element(&$$, NULL, &$1); }
+ non_empty_static_array_pair_list ',' static_scalar_value T_DOUBLE_ARROW static_scalar_value { zend_ast_dynamic_add(&$$.u.ast, $3.u.ast); zend_ast_dynamic_add(&$$.u.ast, $5.u.ast); }
+ | non_empty_static_array_pair_list ',' static_scalar_value { zend_ast_dynamic_add(&$$.u.ast, NULL); zend_ast_dynamic_add(&$$.u.ast, $3.u.ast); }
+ | static_scalar_value T_DOUBLE_ARROW static_scalar_value { $$.u.ast = zend_ast_create_dynamic(ZEND_INIT_ARRAY); zend_ast_dynamic_add(&$$.u.ast, $1.u.ast); zend_ast_dynamic_add(&$$.u.ast, $3.u.ast); }
+ | static_scalar_value { $$.u.ast = zend_ast_create_dynamic(ZEND_INIT_ARRAY); zend_ast_dynamic_add(&$$.u.ast, NULL); zend_ast_dynamic_add(&$$.u.ast, $1.u.ast); }
;
expr:
diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c
index 2ba05399b4..c7adf38f3b 100644
--- a/Zend/zend_object_handlers.c
+++ b/Zend/zend_object_handlers.c
@@ -169,6 +169,8 @@ ZEND_API HashTable *zend_std_get_debug_info(zval *object, int *is_temp TSRMLS_DC
}
zend_error_noreturn(E_ERROR, ZEND_DEBUGINFO_FUNC_NAME "() must return an array");
+
+ return NULL; /* Compilers are dumb and don't understand that noreturn means that the function does NOT need a return value... */
}
/* }}} */
diff --git a/Zend/zend_variables.c b/Zend/zend_variables.c
index a18591fdf7..0f9e184b7e 100644
--- a/Zend/zend_variables.c
+++ b/Zend/zend_variables.c
@@ -36,8 +36,7 @@ ZEND_API void _zval_dtor_func(zval *zvalue ZEND_FILE_LINE_DC)
CHECK_ZVAL_STRING_REL(zvalue);
str_efree_rel(zvalue->value.str.val);
break;
- case IS_ARRAY:
- case IS_CONSTANT_ARRAY: {
+ case IS_ARRAY: {
TSRMLS_FETCH();
if (zvalue->value.ht && (zvalue->value.ht != &EG(symbol_table))) {
@@ -86,7 +85,6 @@ ZEND_API void _zval_internal_dtor(zval *zvalue ZEND_FILE_LINE_DC)
str_free(zvalue->value.str.val);
break;
case IS_ARRAY:
- case IS_CONSTANT_ARRAY:
case IS_CONSTANT_AST:
case IS_OBJECT:
case IS_RESOURCE:
@@ -128,8 +126,7 @@ ZEND_API void _zval_copy_ctor_func(zval *zvalue ZEND_FILE_LINE_DC)
zvalue->value.str.val = (char *) estrndup_rel(zvalue->value.str.val, zvalue->value.str.len);
}
break;
- case IS_ARRAY:
- case IS_CONSTANT_ARRAY: {
+ case IS_ARRAY: {
zval *tmp;
HashTable *original_ht = zvalue->value.ht;
HashTable *tmp_ht = NULL;
diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h
index ed353edbac..da4f03a2bd 100644
--- a/Zend/zend_vm_def.h
+++ b/Zend/zend_vm_def.h
@@ -1081,7 +1081,7 @@ ZEND_VM_HELPER_EX(zend_fetch_var_address_helper, CONST|TMP|VAR|CV, UNUSED|CONST|
FREE_OP1();
break;
case ZEND_FETCH_STATIC:
- zval_update_constant(retval, (void*) 1 TSRMLS_CC);
+ zval_update_constant(retval, 1 TSRMLS_CC);
break;
case ZEND_FETCH_GLOBAL_LOCK:
if (OP1_TYPE == IS_VAR && !free_op1.var) {
@@ -3710,8 +3710,6 @@ ZEND_VM_HANDLER(99, ZEND_FETCH_CONSTANT, VAR|CONST|UNUSED, CONST)
retval = &EX_T(opline->result.var).tmp_var;
ZVAL_COPY_VALUE(retval, &c->value);
zval_copy_ctor(retval);
- CHECK_EXCEPTION();
- ZEND_VM_NEXT_OPCODE();
} else {
/* class constant */
zend_class_entry *ce;
@@ -3722,8 +3720,7 @@ ZEND_VM_HANDLER(99, ZEND_FETCH_CONSTANT, VAR|CONST|UNUSED, CONST)
value = CACHED_PTR(opline->op2.literal->cache_slot);
ZVAL_COPY_VALUE(&EX_T(opline->result.var).tmp_var, *value);
zval_copy_ctor(&EX_T(opline->result.var).tmp_var);
- CHECK_EXCEPTION();
- ZEND_VM_NEXT_OPCODE();
+ goto constant_fetch_end;
} else if (CACHED_PTR(opline->op1.literal->cache_slot)) {
ce = CACHED_PTR(opline->op1.literal->cache_slot);
} else {
@@ -3741,8 +3738,7 @@ ZEND_VM_HANDLER(99, ZEND_FETCH_CONSTANT, VAR|CONST|UNUSED, CONST)
if ((value = CACHED_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, ce)) != NULL) {
ZVAL_COPY_VALUE(&EX_T(opline->result.var).tmp_var, *value);
zval_copy_ctor(&EX_T(opline->result.var).tmp_var);
- CHECK_EXCEPTION();
- ZEND_VM_NEXT_OPCODE();
+ goto constant_fetch_end;
}
}
@@ -3751,7 +3747,7 @@ ZEND_VM_HANDLER(99, ZEND_FETCH_CONSTANT, VAR|CONST|UNUSED, CONST)
zend_class_entry *old_scope = EG(scope);
EG(scope) = ce;
- zval_update_constant(value, (void *) 1 TSRMLS_CC);
+ zval_update_constant(value, 1 TSRMLS_CC);
EG(scope) = old_scope;
}
if (OP1_TYPE == IS_CONST) {
@@ -3767,10 +3763,13 @@ ZEND_VM_HANDLER(99, ZEND_FETCH_CONSTANT, VAR|CONST|UNUSED, CONST)
} else {
zend_error_noreturn(E_ERROR, "Undefined class constant '%s'", Z_STRVAL_P(opline->op2.zv));
}
-
- CHECK_EXCEPTION();
- ZEND_VM_NEXT_OPCODE();
}
+constant_fetch_end:
+ if (Z_TYPE(EX_T(opline->result.var).tmp_var) == IS_ARRAY) {
+ zend_error_noreturn(E_ERROR, "Arrays are not allowed in constants at run-time");
+ }
+ CHECK_EXCEPTION();
+ ZEND_VM_NEXT_OPCODE();
}
ZEND_VM_HANDLER(72, ZEND_ADD_ARRAY_ELEMENT, CONST|TMP|VAR|CV, CONST|TMP|VAR|UNUSED|CV)
@@ -5376,11 +5375,8 @@ ZEND_VM_HANDLER(143, ZEND_DECLARE_CONST, CONST, CONST)
zval *tmp_ptr = &tmp;
ZVAL_COPY_VALUE(&tmp, val);
- if (Z_TYPE_P(val) == IS_CONSTANT_ARRAY) {
- zval_copy_ctor(&tmp);
- }
INIT_PZVAL(&tmp);
- zval_update_constant(&tmp_ptr, NULL TSRMLS_CC);
+ zval_update_constant(&tmp_ptr, 0 TSRMLS_CC);
c.value = *tmp_ptr;
} else {
INIT_PZVAL_COPY(&c.value, val);
diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h
index a4e3350eaa..3fb5ab3ebe 100644
--- a/Zend/zend_vm_execute.h
+++ b/Zend/zend_vm_execute.h
@@ -3684,7 +3684,7 @@ static int ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_CONST_CONST(int type
break;
case ZEND_FETCH_STATIC:
- zval_update_constant(retval, (void*) 1 TSRMLS_CC);
+ zval_update_constant(retval, 1 TSRMLS_CC);
break;
case ZEND_FETCH_GLOBAL_LOCK:
if (IS_CONST == IS_VAR && !free_op1.var) {
@@ -3971,8 +3971,6 @@ static int ZEND_FASTCALL ZEND_FETCH_CONSTANT_SPEC_CONST_CONST_HANDLER(ZEND_OPCO
retval = &EX_T(opline->result.var).tmp_var;
ZVAL_COPY_VALUE(retval, &c->value);
zval_copy_ctor(retval);
- CHECK_EXCEPTION();
- ZEND_VM_NEXT_OPCODE();
} else {
/* class constant */
zend_class_entry *ce;
@@ -3983,8 +3981,7 @@ static int ZEND_FASTCALL ZEND_FETCH_CONSTANT_SPEC_CONST_CONST_HANDLER(ZEND_OPCO
value = CACHED_PTR(opline->op2.literal->cache_slot);
ZVAL_COPY_VALUE(&EX_T(opline->result.var).tmp_var, *value);
zval_copy_ctor(&EX_T(opline->result.var).tmp_var);
- CHECK_EXCEPTION();
- ZEND_VM_NEXT_OPCODE();
+ goto constant_fetch_end;
} else if (CACHED_PTR(opline->op1.literal->cache_slot)) {
ce = CACHED_PTR(opline->op1.literal->cache_slot);
} else {
@@ -4002,8 +3999,7 @@ static int ZEND_FASTCALL ZEND_FETCH_CONSTANT_SPEC_CONST_CONST_HANDLER(ZEND_OPCO
if ((value = CACHED_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, ce)) != NULL) {
ZVAL_COPY_VALUE(&EX_T(opline->result.var).tmp_var, *value);
zval_copy_ctor(&EX_T(opline->result.var).tmp_var);
- CHECK_EXCEPTION();
- ZEND_VM_NEXT_OPCODE();
+ goto constant_fetch_end;
}
}
@@ -4012,7 +4008,7 @@ static int ZEND_FASTCALL ZEND_FETCH_CONSTANT_SPEC_CONST_CONST_HANDLER(ZEND_OPCO
zend_class_entry *old_scope = EG(scope);
EG(scope) = ce;
- zval_update_constant(value, (void *) 1 TSRMLS_CC);
+ zval_update_constant(value, 1 TSRMLS_CC);
EG(scope) = old_scope;
}
if (IS_CONST == IS_CONST) {
@@ -4028,10 +4024,13 @@ static int ZEND_FASTCALL ZEND_FETCH_CONSTANT_SPEC_CONST_CONST_HANDLER(ZEND_OPCO
} else {
zend_error_noreturn(E_ERROR, "Undefined class constant '%s'", Z_STRVAL_P(opline->op2.zv));
}
-
- CHECK_EXCEPTION();
- ZEND_VM_NEXT_OPCODE();
}
+constant_fetch_end:
+ if (Z_TYPE(EX_T(opline->result.var).tmp_var) == IS_ARRAY) {
+ zend_error_noreturn(E_ERROR, "Arrays are not allowed in constants at run-time");
+ }
+ CHECK_EXCEPTION();
+ ZEND_VM_NEXT_OPCODE();
}
static int ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -4309,11 +4308,8 @@ static int ZEND_FASTCALL ZEND_DECLARE_CONST_SPEC_CONST_CONST_HANDLER(ZEND_OPCOD
zval *tmp_ptr = &tmp;
ZVAL_COPY_VALUE(&tmp, val);
- if (Z_TYPE_P(val) == IS_CONSTANT_ARRAY) {
- zval_copy_ctor(&tmp);
- }
INIT_PZVAL(&tmp);
- zval_update_constant(&tmp_ptr, NULL TSRMLS_CC);
+ zval_update_constant(&tmp_ptr, 0 TSRMLS_CC);
c.value = *tmp_ptr;
} else {
INIT_PZVAL_COPY(&c.value, val);
@@ -5572,7 +5568,7 @@ static int ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_CONST_VAR(int type,
break;
case ZEND_FETCH_STATIC:
- zval_update_constant(retval, (void*) 1 TSRMLS_CC);
+ zval_update_constant(retval, 1 TSRMLS_CC);
break;
case ZEND_FETCH_GLOBAL_LOCK:
if (IS_CONST == IS_VAR && !free_op1.var) {
@@ -6324,7 +6320,7 @@ static int ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_CONST_UNUSED(int typ
break;
case ZEND_FETCH_STATIC:
- zval_update_constant(retval, (void*) 1 TSRMLS_CC);
+ zval_update_constant(retval, 1 TSRMLS_CC);
break;
case ZEND_FETCH_GLOBAL_LOCK:
if (IS_CONST == IS_VAR && !free_op1.var) {
@@ -9094,7 +9090,7 @@ static int ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_TMP_CONST(int type,
zval_dtor(free_op1.var);
break;
case ZEND_FETCH_STATIC:
- zval_update_constant(retval, (void*) 1 TSRMLS_CC);
+ zval_update_constant(retval, 1 TSRMLS_CC);
break;
case ZEND_FETCH_GLOBAL_LOCK:
if (IS_TMP_VAR == IS_VAR && !free_op1.var) {
@@ -10850,7 +10846,7 @@ static int ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_TMP_VAR(int type, ZE
zval_dtor(free_op1.var);
break;
case ZEND_FETCH_STATIC:
- zval_update_constant(retval, (void*) 1 TSRMLS_CC);
+ zval_update_constant(retval, 1 TSRMLS_CC);
break;
case ZEND_FETCH_GLOBAL_LOCK:
if (IS_TMP_VAR == IS_VAR && !free_op1.var) {
@@ -11604,7 +11600,7 @@ static int ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_TMP_UNUSED(int type,
zval_dtor(free_op1.var);
break;
case ZEND_FETCH_STATIC:
- zval_update_constant(retval, (void*) 1 TSRMLS_CC);
+ zval_update_constant(retval, 1 TSRMLS_CC);
break;
case ZEND_FETCH_GLOBAL_LOCK:
if (IS_TMP_VAR == IS_VAR && !free_op1.var) {
@@ -15024,7 +15020,7 @@ static int ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_VAR_CONST(int type,
zval_ptr_dtor_nogc(&free_op1.var);
break;
case ZEND_FETCH_STATIC:
- zval_update_constant(retval, (void*) 1 TSRMLS_CC);
+ zval_update_constant(retval, 1 TSRMLS_CC);
break;
case ZEND_FETCH_GLOBAL_LOCK:
if (IS_VAR == IS_VAR && !free_op1.var) {
@@ -15924,8 +15920,6 @@ static int ZEND_FASTCALL ZEND_FETCH_CONSTANT_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE
retval = &EX_T(opline->result.var).tmp_var;
ZVAL_COPY_VALUE(retval, &c->value);
zval_copy_ctor(retval);
- CHECK_EXCEPTION();
- ZEND_VM_NEXT_OPCODE();
} else {
/* class constant */
zend_class_entry *ce;
@@ -15936,8 +15930,7 @@ static int ZEND_FASTCALL ZEND_FETCH_CONSTANT_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE
value = CACHED_PTR(opline->op2.literal->cache_slot);
ZVAL_COPY_VALUE(&EX_T(opline->result.var).tmp_var, *value);
zval_copy_ctor(&EX_T(opline->result.var).tmp_var);
- CHECK_EXCEPTION();
- ZEND_VM_NEXT_OPCODE();
+ goto constant_fetch_end;
} else if (CACHED_PTR(opline->op1.literal->cache_slot)) {
ce = CACHED_PTR(opline->op1.literal->cache_slot);
} else {
@@ -15955,8 +15948,7 @@ static int ZEND_FASTCALL ZEND_FETCH_CONSTANT_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE
if ((value = CACHED_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, ce)) != NULL) {
ZVAL_COPY_VALUE(&EX_T(opline->result.var).tmp_var, *value);
zval_copy_ctor(&EX_T(opline->result.var).tmp_var);
- CHECK_EXCEPTION();
- ZEND_VM_NEXT_OPCODE();
+ goto constant_fetch_end;
}
}
@@ -15965,7 +15957,7 @@ static int ZEND_FASTCALL ZEND_FETCH_CONSTANT_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE
zend_class_entry *old_scope = EG(scope);
EG(scope) = ce;
- zval_update_constant(value, (void *) 1 TSRMLS_CC);
+ zval_update_constant(value, 1 TSRMLS_CC);
EG(scope) = old_scope;
}
if (IS_VAR == IS_CONST) {
@@ -15981,10 +15973,13 @@ static int ZEND_FASTCALL ZEND_FETCH_CONSTANT_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE
} else {
zend_error_noreturn(E_ERROR, "Undefined class constant '%s'", Z_STRVAL_P(opline->op2.zv));
}
-
- CHECK_EXCEPTION();
- ZEND_VM_NEXT_OPCODE();
}
+constant_fetch_end:
+ if (Z_TYPE(EX_T(opline->result.var).tmp_var) == IS_ARRAY) {
+ zend_error_noreturn(E_ERROR, "Arrays are not allowed in constants at run-time");
+ }
+ CHECK_EXCEPTION();
+ ZEND_VM_NEXT_OPCODE();
}
static int ZEND_FASTCALL ZEND_ADD_ARRAY_ELEMENT_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -19622,7 +19617,7 @@ static int ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_VAR_VAR(int type, ZE
zval_ptr_dtor_nogc(&free_op1.var);
break;
case ZEND_FETCH_STATIC:
- zval_update_constant(retval, (void*) 1 TSRMLS_CC);
+ zval_update_constant(retval, 1 TSRMLS_CC);
break;
case ZEND_FETCH_GLOBAL_LOCK:
if (IS_VAR == IS_VAR && !free_op1.var) {
@@ -21610,7 +21605,7 @@ static int ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_VAR_UNUSED(int type,
zval_ptr_dtor_nogc(&free_op1.var);
break;
case ZEND_FETCH_STATIC:
- zval_update_constant(retval, (void*) 1 TSRMLS_CC);
+ zval_update_constant(retval, 1 TSRMLS_CC);
break;
case ZEND_FETCH_GLOBAL_LOCK:
if (IS_VAR == IS_VAR && !free_op1.var) {
@@ -25537,8 +25532,6 @@ static int ZEND_FASTCALL ZEND_FETCH_CONSTANT_SPEC_UNUSED_CONST_HANDLER(ZEND_OPC
retval = &EX_T(opline->result.var).tmp_var;
ZVAL_COPY_VALUE(retval, &c->value);
zval_copy_ctor(retval);
- CHECK_EXCEPTION();
- ZEND_VM_NEXT_OPCODE();
} else {
/* class constant */
zend_class_entry *ce;
@@ -25549,8 +25542,7 @@ static int ZEND_FASTCALL ZEND_FETCH_CONSTANT_SPEC_UNUSED_CONST_HANDLER(ZEND_OPC
value = CACHED_PTR(opline->op2.literal->cache_slot);
ZVAL_COPY_VALUE(&EX_T(opline->result.var).tmp_var, *value);
zval_copy_ctor(&EX_T(opline->result.var).tmp_var);
- CHECK_EXCEPTION();
- ZEND_VM_NEXT_OPCODE();
+ goto constant_fetch_end;
} else if (CACHED_PTR(opline->op1.literal->cache_slot)) {
ce = CACHED_PTR(opline->op1.literal->cache_slot);
} else {
@@ -25568,8 +25560,7 @@ static int ZEND_FASTCALL ZEND_FETCH_CONSTANT_SPEC_UNUSED_CONST_HANDLER(ZEND_OPC
if ((value = CACHED_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, ce)) != NULL) {
ZVAL_COPY_VALUE(&EX_T(opline->result.var).tmp_var, *value);
zval_copy_ctor(&EX_T(opline->result.var).tmp_var);
- CHECK_EXCEPTION();
- ZEND_VM_NEXT_OPCODE();
+ goto constant_fetch_end;
}
}
@@ -25578,7 +25569,7 @@ static int ZEND_FASTCALL ZEND_FETCH_CONSTANT_SPEC_UNUSED_CONST_HANDLER(ZEND_OPC
zend_class_entry *old_scope = EG(scope);
EG(scope) = ce;
- zval_update_constant(value, (void *) 1 TSRMLS_CC);
+ zval_update_constant(value, 1 TSRMLS_CC);
EG(scope) = old_scope;
}
if (IS_UNUSED == IS_CONST) {
@@ -25594,10 +25585,13 @@ static int ZEND_FASTCALL ZEND_FETCH_CONSTANT_SPEC_UNUSED_CONST_HANDLER(ZEND_OPC
} else {
zend_error_noreturn(E_ERROR, "Undefined class constant '%s'", Z_STRVAL_P(opline->op2.zv));
}
-
- CHECK_EXCEPTION();
- ZEND_VM_NEXT_OPCODE();
}
+constant_fetch_end:
+ if (Z_TYPE(EX_T(opline->result.var).tmp_var) == IS_ARRAY) {
+ zend_error_noreturn(E_ERROR, "Arrays are not allowed in constants at run-time");
+ }
+ CHECK_EXCEPTION();
+ ZEND_VM_NEXT_OPCODE();
}
static int ZEND_FASTCALL ZEND_INIT_ARRAY_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -32488,7 +32482,7 @@ static int ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_CV_CONST(int type, Z
break;
case ZEND_FETCH_STATIC:
- zval_update_constant(retval, (void*) 1 TSRMLS_CC);
+ zval_update_constant(retval, 1 TSRMLS_CC);
break;
case ZEND_FETCH_GLOBAL_LOCK:
if (IS_CV == IS_VAR && !free_op1.var) {
@@ -36712,7 +36706,7 @@ static int ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_CV_VAR(int type, ZEN
break;
case ZEND_FETCH_STATIC:
- zval_update_constant(retval, (void*) 1 TSRMLS_CC);
+ zval_update_constant(retval, 1 TSRMLS_CC);
break;
case ZEND_FETCH_GLOBAL_LOCK:
if (IS_CV == IS_VAR && !free_op1.var) {
@@ -38560,7 +38554,7 @@ static int ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_CV_UNUSED(int type,
break;
case ZEND_FETCH_STATIC:
- zval_update_constant(retval, (void*) 1 TSRMLS_CC);
+ zval_update_constant(retval, 1 TSRMLS_CC);
break;
case ZEND_FETCH_GLOBAL_LOCK:
if (IS_CV == IS_VAR && !free_op1.var) {
diff --git a/ext/com_dotnet/com_olechar.c b/ext/com_dotnet/com_olechar.c
index a3e81978bd..bf42b4fffb 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 4e044b20b8..3cc057127a 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 0000000000..daac328538
--- /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 0000000000..d97011c2bb
--- /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 1995f23a08..f8b04295d7 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 e5c026a513..9fbea5518f 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 f633a8ab2a..afd98cb87e 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 0000000000..1b115588ff
--- /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 29e3343707..4f0a29d7ff 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 0fc2fe6656..72a591ff16 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 c78a8e0953..3572e69543 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 5852bb2ca6..f81f87e4c9 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 0000000000..4806712437
--- /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 0000000000..6371757647
--- /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 a94cc31d74..9042457e86 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 8991ed97d4..6648a146ff 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 0000000000..56c2c8e58b
--- /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;#38;">
+<!ENTITY gt "&#62;">
+<!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 0000000000..604eea57b1
--- /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 0000000000..7ae542e977
--- /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 0000000000..c10af0966c
--- /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 4f34041259..a11dbf8dac 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 a9af5398f5..fa708b6c83 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 66a18dbb87..1600625eb8 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 0000000000..546ed3f96c
--- /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 feb4010f74..5cce7f3f9b 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
new file mode 100644
index 0000000000..713004c03d
--- /dev/null
+++ b/ext/fileinfo/tests/resources/test.ppt
Binary files differ
diff --git a/ext/gmp/gmp.c b/ext/gmp/gmp.c
index d34d78f01a..cd50896cc7 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 dfd850b00a..6aa1b368c0 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 5512125463..89cd67219c 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 7c27ccf325..94f32f87ed 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 09cdbfa404..ac3c328a0b 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 53eb7a885e..892745044f 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 bd6d4ad1aa..e4e190d50d 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 c78c522606..8a88efccaa 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 71990a5e4b..c994a92da5 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 497a862449..e96b734291 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 044896ac43..0518b586f9 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 42eb6e5915..fcf053213d 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 8f903a5ea5..11d0d80bc6 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 b0112cc847..2f5f586f16 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 0000000000..60972fa919
--- /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 3ab0078d70..645c32d9aa 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 0000000000..63f510793b
--- /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 90d71b0571..5d9f35741b 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 0000000000..c602e794cd
--- /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 bb09256d6b..3c526fbc3c 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 0000000000..cc2a23b2b8
--- /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 d52d15999c..b9d6eb5c3d 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 0000000000..88d457bdb3
--- /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 zwei­hundert­vier­und­dreißig­tausend­fünf­hundert­sieben­und­sechzig 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 470674997c..4b3255c0f6 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 0000000000..b633b1f62c
--- /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 92a7f07378..d600792d7c 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 0000000000..b60924332a
--- /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/json/json.c b/ext/json/json.c
index 9140096bf5..d4cb39f81d 100644
--- a/ext/json/json.c
+++ b/ext/json/json.c
@@ -230,7 +230,7 @@ static inline void json_pretty_print_indent(smart_str *buf, int options TSRMLS_D
static void json_encode_array(smart_str *buf, zval **val, int options TSRMLS_DC) /* {{{ */
{
- int i, r;
+ int i, r, need_comma = 0;
HashTable *myht;
if (Z_TYPE_PP(val) == IS_ARRAY) {
@@ -253,7 +253,6 @@ static void json_encode_array(smart_str *buf, zval **val, int options TSRMLS_DC)
smart_str_appendc(buf, '{');
}
- json_pretty_print_char(buf, options, '\n' TSRMLS_CC);
++JSON_G(encoder_depth);
i = myht ? zend_hash_num_elements(myht) : 0;
@@ -266,7 +265,6 @@ static void json_encode_array(smart_str *buf, zval **val, int options TSRMLS_DC)
uint key_len;
HashPosition pos;
HashTable *tmp_ht;
- int need_comma = 0;
zend_hash_internal_pointer_reset_ex(myht, &pos);
for (;; zend_hash_move_forward_ex(myht, &pos)) {
@@ -283,11 +281,11 @@ static void json_encode_array(smart_str *buf, zval **val, int options TSRMLS_DC)
if (r == PHP_JSON_OUTPUT_ARRAY) {
if (need_comma) {
smart_str_appendc(buf, ',');
- json_pretty_print_char(buf, options, '\n' TSRMLS_CC);
} else {
need_comma = 1;
}
+ json_pretty_print_char(buf, options, '\n' TSRMLS_CC);
json_pretty_print_indent(buf, options TSRMLS_CC);
php_json_encode(buf, *data, options TSRMLS_CC);
} else if (r == PHP_JSON_OUTPUT_OBJECT) {
@@ -302,11 +300,11 @@ static void json_encode_array(smart_str *buf, zval **val, int options TSRMLS_DC)
if (need_comma) {
smart_str_appendc(buf, ',');
- json_pretty_print_char(buf, options, '\n' TSRMLS_CC);
} else {
need_comma = 1;
}
+ json_pretty_print_char(buf, options, '\n' TSRMLS_CC);
json_pretty_print_indent(buf, options TSRMLS_CC);
json_escape_string(buf, key, key_len - 1, options & ~PHP_JSON_NUMERIC_CHECK TSRMLS_CC);
@@ -318,11 +316,11 @@ static void json_encode_array(smart_str *buf, zval **val, int options TSRMLS_DC)
} else {
if (need_comma) {
smart_str_appendc(buf, ',');
- json_pretty_print_char(buf, options, '\n' TSRMLS_CC);
} else {
need_comma = 1;
}
+ json_pretty_print_char(buf, options, '\n' TSRMLS_CC);
json_pretty_print_indent(buf, options TSRMLS_CC);
smart_str_appendc(buf, '"');
@@ -347,8 +345,12 @@ static void json_encode_array(smart_str *buf, zval **val, int options TSRMLS_DC)
JSON_G(error_code) = PHP_JSON_ERROR_DEPTH;
}
--JSON_G(encoder_depth);
- json_pretty_print_char(buf, options, '\n' TSRMLS_CC);
- json_pretty_print_indent(buf, options TSRMLS_CC);
+
+ /* Only keep closing bracket on same line for empty arrays/objects */
+ if (need_comma) {
+ json_pretty_print_char(buf, options, '\n' TSRMLS_CC);
+ json_pretty_print_indent(buf, options TSRMLS_CC);
+ }
if (r == PHP_JSON_OUTPUT_ARRAY) {
smart_str_appendc(buf, ']');
diff --git a/ext/json/tests/bug66021.phpt b/ext/json/tests/bug66021.phpt
new file mode 100644
index 0000000000..cf52e0c6df
--- /dev/null
+++ b/ext/json/tests/bug66021.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Bug #66021 (Blank line inside empty array/object when JSON_PRETTY_PRINT is set)
+--SKIPIF--
+<?php if (!extension_loaded("json")) print "skip"; ?>
+--FILE--
+<?php
+
+class Foo {
+ private $bar = 'baz';
+}
+
+echo json_encode([[], (object)[], new Foo], JSON_PRETTY_PRINT), "\n";
+
+?>
+--EXPECT--
+[
+ [],
+ {},
+ {}
+]
diff --git a/ext/ldap/ldap.c b/ext/ldap/ldap.c
index f5583cd8c0..50055a25a7 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 9e5b75eadd..561ad64038 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 138b4899fe..b4f85e6f35 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 6844d06370..eadbb2156d 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 daf712cd0d..56e48a0a67 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 c819b976ea..b23d7e3139 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 70bdfb3d59..309d9b9f06 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 3d31b8183c..e099fe7194 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 de9b3bf540..00cbcf204c 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 190572b689..e28caebf92 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 d0e8b1c3d3..52e42b6177 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 2b8a92c7fd..cf1309e5a2 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 bfea423c9e..7a4530f0d5 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 0000000000..a87afa84a3
--- /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 0000000000..ddb49cd189
--- /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 61444ae235..7ed2d08933 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 63ec7ca3c0..8f6c24900a 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 7c6dacd393..bed9d53419 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 63b6ad2848..854b8160f0 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 1abc61170e..6d0b698ee7 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 dd4f9b9710..8a49ba3f82 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 b068d64e8f..c69a251111 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 9c02cf9760..9068f6f708 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 0000000000..e3858e6172
--- /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 0000000000..ba4c564385
--- /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 f5d77e38bc..4d2d3553d1 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 0000000000..5cf5ca8e3b
--- /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 cc57511b5b..dae81b21cc 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 0000000000..5775b0eaec
--- /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 2b0e150ded..69755865e9 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 0000000000..2dec703b99
--- /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 0000000000..304300459b
--- /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 61be5ca17b..893a144c4b 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 551c5f34ac..f373ea5dd5 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 d771998577..e1fc5f8f01 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 706111346f..13f1e294dc 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 6f15eee8ce..d4a9d6cfc0 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 fc30bddcb5..bee8e1d0ee 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 fc6f48581c..0112a1394d 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(&current_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 0a54ea0454..f7e22a1d87 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 b461b49427..d8b1a41511 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 028a1f6b13..79c0feb60a 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 65f1c57a00..66920e47be 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 dfdba4084e..ecce71d9c2 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 be71d06508..7e8b9624ac 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 1a4e67f5eb..c2c53604e5 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 55f856be5a..faa3641eb1 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 53cf205f5e..1620c05621 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 6204290efe..ae33e765ca 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 47f8f88312..17f8e8798f 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 c899ec8ac9..a01d834a4e 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 1781cc1776..8badbf7e00 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 e251d2124a..c6a91570cf 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 f8515ddb1d..786c70382a 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 c85f2e55e4..b026590980 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 a89ab2aea5..f9188a09b7 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 9cf5ac1337..dc3ea0d17e 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 72595de9f1..1327861fe9 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 efb232bff6..5eb2e977e7 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 9de9048859..d59aa53fa3 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 3816497c2a..f7e9f17f12 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 b186eeb474..15004b8d33 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 825fe2647f..581766b38a 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 8b0ccf34e6..1d275cd654 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 87b2f9100d..5e42951f86 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 d36901d8c1..d867f433b2 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 99907f86dd..407edda0fb 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 db97efcbe3..ebf4aac58b 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 e43b3f846e..df64076871 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 ec57c27217..26794095e1 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 d3fc035860..886ba81925 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 c995031af4..b68d3cb5db 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 a24153dc20..703e14113f 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 47140436e7..33e177c5df 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 32e88510e7..b225a2d670 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 b2aeb596d0..28373a3eda 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 3638a5a2b7..c276f1f2ee 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 1a706f7642..91830ab000 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 d0057a618b..e5aa96f6b0 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, &notUsed);
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, &regFree1);
r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, &regFree2);
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, &regFree1);
r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, &regFree2);
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, &regFree1);
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, &regFree1);
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, &regFree2);
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, &regFree1);
r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, &regFree2);
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, &regFree1);
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, &regFree1);
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, &regFree1);
r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, &regFree2);
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, &regFree1);
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, &regFree1);
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 6cf5977b1a..0884ebad0e 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 6c3be814cb..4f98331035 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 897b1a9e49..e525ff2a80 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 74df6dc759..e1f24abf64 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 27f60c95a7..02246c6268 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 deddeb4871..1b30e34787 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 2928d98c5e..0e8abb3613 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 b0af30aa9d..702fec2311 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 0000000000..2818516dac
--- /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 0000000000..2b1b5d491d
--- /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 6cd8275e06..04ddd8c82b 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 d83ab8d146..9f7ffa1aa3 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
new file mode 100644
index 0000000000..ec68a571cc
--- /dev/null
+++ b/ext/standard/tests/image/test-1pix.bmp
Binary files differ
diff --git a/ext/standard/tests/serialize/005.phpt b/ext/standard/tests/serialize/005.phpt
index e7b23db701..2df270154d 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 0000000000..951db75a07
--- /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 4c5b0944c6..a6f4f7a252 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 ed0f08a84f..c05d1f487a 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 a2bbfa6482..88eda504d5 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 839ebee554..e3b9abd91c 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 c113461fe7..1fc946e5b3 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 24de1cc233..5104326198 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 d4006879f4..8e04553983 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 b283829c46..0c77221465 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 a7d70f34da..487b271149 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 4192f4a869..d8ffe91378 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 004274d406..003bac9547 100644
--- a/ext/standard/var_unserializer.c
+++ b/ext/standard/var_unserializer.c
@@ -1,10 +1,10 @@
-/* Generated by re2c 0.13.5 */
+/* Generated by re2c 0.13.5 on Fri Apr 18 15:07:27 2014 */
#line 1 "ext/standard/var_unserializer.re"
/*
+----------------------------------------------------------------------+
| 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 |
@@ -396,7 +396,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;
}
@@ -408,6 +416,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;
}
@@ -457,7 +469,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
-#line 461 "ext/standard/var_unserializer.c"
+#line 473 "ext/standard/var_unserializer.c"
{
YYCTYPE yych;
static const unsigned char yybm[] = {
@@ -517,9 +529,9 @@ yy2:
yych = *(YYMARKER = ++YYCURSOR);
if (yych == ':') goto yy95;
yy3:
-#line 812 "ext/standard/var_unserializer.re"
+#line 824 "ext/standard/var_unserializer.re"
{ return 0; }
-#line 523 "ext/standard/var_unserializer.c"
+#line 535 "ext/standard/var_unserializer.c"
yy4:
yych = *(YYMARKER = ++YYCURSOR);
if (yych == ':') goto yy89;
@@ -562,13 +574,13 @@ yy13:
goto yy3;
yy14:
++YYCURSOR;
-#line 806 "ext/standard/var_unserializer.re"
+#line 818 "ext/standard/var_unserializer.re"
{
/* this is the case where we have less data than planned */
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Unexpected end of serialized data");
return 0; /* not sure if it should be 0 or 1 here? */
}
-#line 572 "ext/standard/var_unserializer.c"
+#line 584 "ext/standard/var_unserializer.c"
yy16:
yych = *++YYCURSOR;
goto yy3;
@@ -598,7 +610,7 @@ yy20:
yych = *++YYCURSOR;
if (yych != '"') goto yy18;
++YYCURSOR;
-#line 660 "ext/standard/var_unserializer.re"
+#line 672 "ext/standard/var_unserializer.re"
{
size_t len, len2, len3, maxlen;
long elements;
@@ -744,7 +756,7 @@ yy20:
return object_common2(UNSERIALIZE_PASSTHRU, elements);
}
-#line 748 "ext/standard/var_unserializer.c"
+#line 760 "ext/standard/var_unserializer.c"
yy25:
yych = *++YYCURSOR;
if (yych <= ',') {
@@ -769,7 +781,7 @@ yy27:
yych = *++YYCURSOR;
if (yych != '"') goto yy18;
++YYCURSOR;
-#line 652 "ext/standard/var_unserializer.re"
+#line 664 "ext/standard/var_unserializer.re"
{
INIT_PZVAL(*rval);
@@ -777,7 +789,7 @@ yy27:
return object_common2(UNSERIALIZE_PASSTHRU,
object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR));
}
-#line 781 "ext/standard/var_unserializer.c"
+#line 793 "ext/standard/var_unserializer.c"
yy32:
yych = *++YYCURSOR;
if (yych == '+') goto yy33;
@@ -798,7 +810,7 @@ yy34:
yych = *++YYCURSOR;
if (yych != '{') goto yy18;
++YYCURSOR;
-#line 632 "ext/standard/var_unserializer.re"
+#line 644 "ext/standard/var_unserializer.re"
{
long elements = parse_iv(start + 2);
/* use iv() not uiv() in order to check data range */
@@ -818,7 +830,7 @@ yy34:
return finish_nested_data(UNSERIALIZE_PASSTHRU);
}
-#line 822 "ext/standard/var_unserializer.c"
+#line 834 "ext/standard/var_unserializer.c"
yy39:
yych = *++YYCURSOR;
if (yych == '+') goto yy40;
@@ -839,7 +851,7 @@ yy41:
yych = *++YYCURSOR;
if (yych != '"') goto yy18;
++YYCURSOR;
-#line 603 "ext/standard/var_unserializer.re"
+#line 615 "ext/standard/var_unserializer.re"
{
size_t len, maxlen;
char *str;
@@ -868,7 +880,7 @@ yy41:
ZVAL_STRINGL(*rval, str, len, 0);
return 1;
}
-#line 872 "ext/standard/var_unserializer.c"
+#line 884 "ext/standard/var_unserializer.c"
yy46:
yych = *++YYCURSOR;
if (yych == '+') goto yy47;
@@ -889,7 +901,7 @@ yy48:
yych = *++YYCURSOR;
if (yych != '"') goto yy18;
++YYCURSOR;
-#line 575 "ext/standard/var_unserializer.re"
+#line 587 "ext/standard/var_unserializer.re"
{
size_t len, maxlen;
char *str;
@@ -917,7 +929,7 @@ yy48:
ZVAL_STRINGL(*rval, str, len, 1);
return 1;
}
-#line 921 "ext/standard/var_unserializer.c"
+#line 933 "ext/standard/var_unserializer.c"
yy53:
yych = *++YYCURSOR;
if (yych <= '/') {
@@ -1005,7 +1017,7 @@ yy61:
}
yy63:
++YYCURSOR;
-#line 565 "ext/standard/var_unserializer.re"
+#line 577 "ext/standard/var_unserializer.re"
{
#if SIZEOF_LONG == 4
use_double:
@@ -1015,7 +1027,7 @@ use_double:
ZVAL_DOUBLE(*rval, zend_strtod((const char *)start + 2, NULL));
return 1;
}
-#line 1019 "ext/standard/var_unserializer.c"
+#line 1031 "ext/standard/var_unserializer.c"
yy65:
yych = *++YYCURSOR;
if (yych <= ',') {
@@ -1074,7 +1086,7 @@ yy73:
yych = *++YYCURSOR;
if (yych != ';') goto yy18;
++YYCURSOR;
-#line 550 "ext/standard/var_unserializer.re"
+#line 562 "ext/standard/var_unserializer.re"
{
*p = YYCURSOR;
INIT_PZVAL(*rval);
@@ -1089,7 +1101,7 @@ yy73:
return 1;
}
-#line 1093 "ext/standard/var_unserializer.c"
+#line 1105 "ext/standard/var_unserializer.c"
yy76:
yych = *++YYCURSOR;
if (yych == 'N') goto yy73;
@@ -1116,7 +1128,7 @@ yy79:
if (yych <= '9') goto yy79;
if (yych != ';') goto yy18;
++YYCURSOR;
-#line 523 "ext/standard/var_unserializer.re"
+#line 535 "ext/standard/var_unserializer.re"
{
#if SIZEOF_LONG == 4
int digits = YYCURSOR - start - 3;
@@ -1143,7 +1155,7 @@ yy79:
ZVAL_LONG(*rval, parse_iv(start + 2));
return 1;
}
-#line 1147 "ext/standard/var_unserializer.c"
+#line 1159 "ext/standard/var_unserializer.c"
yy83:
yych = *++YYCURSOR;
if (yych <= '/') goto yy18;
@@ -1151,24 +1163,24 @@ yy83:
yych = *++YYCURSOR;
if (yych != ';') goto yy18;
++YYCURSOR;
-#line 516 "ext/standard/var_unserializer.re"
+#line 528 "ext/standard/var_unserializer.re"
{
*p = YYCURSOR;
INIT_PZVAL(*rval);
ZVAL_BOOL(*rval, parse_iv(start + 2));
return 1;
}
-#line 1162 "ext/standard/var_unserializer.c"
+#line 1174 "ext/standard/var_unserializer.c"
yy87:
++YYCURSOR;
-#line 509 "ext/standard/var_unserializer.re"
+#line 521 "ext/standard/var_unserializer.re"
{
*p = YYCURSOR;
INIT_PZVAL(*rval);
ZVAL_NULL(*rval);
return 1;
}
-#line 1172 "ext/standard/var_unserializer.c"
+#line 1184 "ext/standard/var_unserializer.c"
yy89:
yych = *++YYCURSOR;
if (yych <= ',') {
@@ -1191,7 +1203,7 @@ yy91:
if (yych <= '9') goto yy91;
if (yych != ';') goto yy18;
++YYCURSOR;
-#line 486 "ext/standard/var_unserializer.re"
+#line 498 "ext/standard/var_unserializer.re"
{
long id;
@@ -1214,7 +1226,7 @@ yy91:
return 1;
}
-#line 1218 "ext/standard/var_unserializer.c"
+#line 1230 "ext/standard/var_unserializer.c"
yy95:
yych = *++YYCURSOR;
if (yych <= ',') {
@@ -1237,7 +1249,7 @@ yy97:
if (yych <= '9') goto yy97;
if (yych != ';') goto yy18;
++YYCURSOR;
-#line 465 "ext/standard/var_unserializer.re"
+#line 477 "ext/standard/var_unserializer.re"
{
long id;
@@ -1258,9 +1270,9 @@ yy97:
return 1;
}
-#line 1262 "ext/standard/var_unserializer.c"
+#line 1274 "ext/standard/var_unserializer.c"
}
-#line 814 "ext/standard/var_unserializer.re"
+#line 826 "ext/standard/var_unserializer.re"
return 0;
diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re
index 76c501e1b5..b3f5d29ca2 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 799b47b3ef..763ff69404 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:
diff --git a/main/SAPI.c b/main/SAPI.c
index b182909c71..7858779a62 100644
--- a/main/SAPI.c
+++ b/main/SAPI.c
@@ -721,7 +721,7 @@ SAPI_API int sapi_header_op(sapi_header_op_enum op, void *arg TSRMLS_DC)
header_line = estrndup(header_line, header_line_len);
- /* cut of trailing spaces, linefeeds and carriage-returns */
+ /* cut off trailing spaces, linefeeds and carriage-returns */
if (header_line_len && isspace(header_line[header_line_len-1])) {
do {
header_line_len--;
diff --git a/main/php_streams.h b/main/php_streams.h
index aa4c05c54f..d1efa71988 100644
--- a/main/php_streams.h
+++ b/main/php_streams.h
@@ -373,6 +373,7 @@ END_EXTERN_C()
/* Flags for url_stat method in wrapper ops */
#define PHP_STREAM_URL_STAT_LINK 1
#define PHP_STREAM_URL_STAT_QUIET 2
+#define PHP_STREAM_URL_STAT_NOCACHE 4
/* change the blocking mode of stream: value == 1 => blocking, value == 0 => non-blocking. */
#define PHP_STREAM_OPTION_BLOCKING 1
diff --git a/main/php_variables.c b/main/php_variables.c
index f529898bf0..90cfcb20bc 100644
--- a/main/php_variables.c
+++ b/main/php_variables.c
@@ -593,7 +593,7 @@ static void php_build_argv(char *s, zval *track_vars_array TSRMLS_DC)
Z_ADDREF_P(arr);
Z_ADDREF_P(argc);
zend_hash_update(&EG(symbol_table), "argv", sizeof("argv"), &arr, sizeof(zval *), NULL);
- zend_hash_add(&EG(symbol_table), "argc", sizeof("argc"), &argc, sizeof(zval *), NULL);
+ zend_hash_update(&EG(symbol_table), "argc", sizeof("argc"), &argc, sizeof(zval *), NULL);
}
if (track_vars_array) {
Z_ADDREF_P(arr);
@@ -694,7 +694,7 @@ static zend_bool php_auto_globals_create_request(const char *name, uint name_len
/* {{{ php_hash_environment
*/
-int php_hash_environment(TSRMLS_D)
+PHPAPI int php_hash_environment(TSRMLS_D)
{
memset(PG(http_globals), 0, sizeof(PG(http_globals)));
zend_activate_auto_globals(TSRMLS_C);
diff --git a/main/php_variables.h b/main/php_variables.h
index 37b2cf2a04..2331c582e6 100644
--- a/main/php_variables.h
+++ b/main/php_variables.h
@@ -41,7 +41,7 @@ PHPAPI void php_register_variable(char *var, char *val, zval *track_vars_array T
PHPAPI void php_register_variable_safe(char *var, char *val, int val_len, zval *track_vars_array TSRMLS_DC);
PHPAPI void php_register_variable_ex(char *var, zval *val, zval *track_vars_array TSRMLS_DC);
-int php_hash_environment(TSRMLS_D);
+PHPAPI int php_hash_environment(TSRMLS_D);
END_EXTERN_C()
#define NUM_TRACK_VARS 6
diff --git a/main/streams/streams.c b/main/streams/streams.c
index 9f9661dbfd..67a151014d 100644
--- a/main/streams/streams.c
+++ b/main/streams/streams.c
@@ -1924,16 +1924,18 @@ PHPAPI int _php_stream_stat_path(const char *path, int flags, php_stream_statbuf
const char *path_to_open = path;
int ret;
- /* Try to hit the cache first */
- if (flags & PHP_STREAM_URL_STAT_LINK) {
- if (BG(CurrentLStatFile) && strcmp(path, BG(CurrentLStatFile)) == 0) {
- memcpy(ssb, &BG(lssb), sizeof(php_stream_statbuf));
- return 0;
- }
- } else {
- if (BG(CurrentStatFile) && strcmp(path, BG(CurrentStatFile)) == 0) {
- memcpy(ssb, &BG(ssb), sizeof(php_stream_statbuf));
- return 0;
+ if (!(flags & PHP_STREAM_URL_STAT_NOCACHE)) {
+ /* Try to hit the cache first */
+ if (flags & PHP_STREAM_URL_STAT_LINK) {
+ if (BG(CurrentLStatFile) && strcmp(path, BG(CurrentLStatFile)) == 0) {
+ memcpy(ssb, &BG(lssb), sizeof(php_stream_statbuf));
+ return 0;
+ }
+ } else {
+ if (BG(CurrentStatFile) && strcmp(path, BG(CurrentStatFile)) == 0) {
+ memcpy(ssb, &BG(ssb), sizeof(php_stream_statbuf));
+ return 0;
+ }
}
}
@@ -1941,19 +1943,21 @@ PHPAPI int _php_stream_stat_path(const char *path, int flags, php_stream_statbuf
if (wrapper && wrapper->wops->url_stat) {
ret = wrapper->wops->url_stat(wrapper, path_to_open, flags, ssb, context TSRMLS_CC);
if (ret == 0) {
- /* Drop into cache */
- if (flags & PHP_STREAM_URL_STAT_LINK) {
- if (BG(CurrentLStatFile)) {
- efree(BG(CurrentLStatFile));
- }
- BG(CurrentLStatFile) = estrdup(path);
- memcpy(&BG(lssb), ssb, sizeof(php_stream_statbuf));
- } else {
- if (BG(CurrentStatFile)) {
- efree(BG(CurrentStatFile));
+ if (!(flags & PHP_STREAM_URL_STAT_NOCACHE)) {
+ /* Drop into cache */
+ if (flags & PHP_STREAM_URL_STAT_LINK) {
+ if (BG(CurrentLStatFile)) {
+ efree(BG(CurrentLStatFile));
+ }
+ BG(CurrentLStatFile) = estrdup(path);
+ memcpy(&BG(lssb), ssb, sizeof(php_stream_statbuf));
+ } else {
+ if (BG(CurrentStatFile)) {
+ efree(BG(CurrentStatFile));
+ }
+ BG(CurrentStatFile) = estrdup(path);
+ memcpy(&BG(ssb), ssb, sizeof(php_stream_statbuf));
}
- BG(CurrentStatFile) = estrdup(path);
- memcpy(&BG(ssb), ssb, sizeof(php_stream_statbuf));
}
}
return ret;
diff --git a/php.ini-development b/php.ini-development
index ac60db901f..148d194fef 100644
--- a/php.ini-development
+++ b/php.ini-development
@@ -573,7 +573,7 @@ html_errors = On
; http://php.net/error-log
; Example:
;error_log = php_errors.log
-; Log errors to syslog (Event Log on NT, not valid in Windows 95).
+; Log errors to syslog (Event Log on Windows).
;error_log = syslog
;windows.show_crt_warning
@@ -1044,7 +1044,7 @@ mail.add_x_header = On
; The path to a log file that will log all mail() calls. Log entries include
; the full path of the script, line number, To address and headers.
;mail.log =
-; Log mail to syslog (Event Log on NT, not valid in Windows 95).
+; Log mail to syslog (Event Log on Windows).
;mail.log = syslog
[SQL]
diff --git a/php.ini-production b/php.ini-production
index c5853ca566..5410b3de54 100644
--- a/php.ini-production
+++ b/php.ini-production
@@ -573,7 +573,7 @@ html_errors = On
; http://php.net/error-log
; Example:
;error_log = php_errors.log
-; Log errors to syslog (Event Log on NT, not valid in Windows 95).
+; Log errors to syslog (Event Log on Windows).
;error_log = syslog
;windows.show_crt_warning
@@ -1042,7 +1042,7 @@ mail.add_x_header = On
; The path to a log file that will log all mail() calls. Log entries include
; the full path of the script, line number, To address and headers.
;mail.log =
-; Log mail to syslog (Event Log on NT, not valid in Windows 95).
+; Log mail to syslog (Event Log on Windows).
;mail.log = syslog
[SQL]
diff --git a/sapi/apache2handler/sapi_apache2.c b/sapi/apache2handler/sapi_apache2.c
index d2b3a327ff..a0c24bd0e3 100644
--- a/sapi/apache2handler/sapi_apache2.c
+++ b/sapi/apache2handler/sapi_apache2.c
@@ -276,12 +276,12 @@ php_apache_sapi_register_variables(zval *track_vars_array TSRMLS_DC)
if (!val) {
val = "";
}
- if (sapi_module.input_filter(PARSE_SERVER, key, &val, strlen(val), &new_val_len TSRMLS_CC)) {
+ if (sapi_module.input_filter(PARSE_SERVER, key, &val, strlen(val), (unsigned int *)&new_val_len TSRMLS_CC)) {
php_register_variable_safe(key, val, new_val_len, track_vars_array TSRMLS_CC);
}
APR_ARRAY_FOREACH_CLOSE()
- if (sapi_module.input_filter(PARSE_SERVER, "PHP_SELF", &ctx->r->uri, strlen(ctx->r->uri), &new_val_len TSRMLS_CC)) {
+ if (sapi_module.input_filter(PARSE_SERVER, "PHP_SELF", &ctx->r->uri, strlen(ctx->r->uri), (unsigned int *)&new_val_len TSRMLS_CC)) {
php_register_variable_safe("PHP_SELF", ctx->r->uri, new_val_len, track_vars_array TSRMLS_CC);
}
}
@@ -670,7 +670,7 @@ zend_first_try {
}
apr_table_set(r->notes, "mod_php_memory_usage",
- apr_psprintf(ctx->r->pool, "%zu", zend_memory_peak_usage(1 TSRMLS_CC)));
+ apr_psprintf(ctx->r->pool, "%" APR_SIZE_T_FMT, zend_memory_peak_usage(1 TSRMLS_CC)));
}
} zend_end_try();
diff --git a/sapi/cgi/cgi_main.c b/sapi/cgi/cgi_main.c
index f464e1e92a..437d09cf32 100644
--- a/sapi/cgi/cgi_main.c
+++ b/sapi/cgi/cgi_main.c
@@ -1824,7 +1824,7 @@ int main(int argc, char *argv[])
unsigned char *p;
decoded_query_string = strdup(query_string);
php_url_decode(decoded_query_string, strlen(decoded_query_string));
- for (p = decoded_query_string; *p && *p <= ' '; p++) {
+ for (p = (unsigned char *)decoded_query_string; *p && *p <= ' '; p++) {
/* skip all leading spaces */
}
if(*p == '-') {
diff --git a/sapi/cli/php_cli_server.c b/sapi/cli/php_cli_server.c
index 3f2aaf0088..5c528a8d9a 100644
--- a/sapi/cli/php_cli_server.c
+++ b/sapi/cli/php_cli_server.c
@@ -312,6 +312,9 @@ static php_cli_server_ext_mime_type_pair mime_type_map[] = {
{ "xls", "application/vnd.ms-excel" },
{ "xlsx", "application/vnd.ms-excel" },
{ "zip", "application/x-zip-compressed" },
+ { "xml", "application/xml" },
+ { "xsl", "application/xml" },
+ { "xsd", "application/xml" },
{ NULL, NULL }
};
diff --git a/sapi/fpm/fpm/events/epoll.c b/sapi/fpm/fpm/events/epoll.c
index 2857ea2a09..b55cb44b15 100644
--- a/sapi/fpm/fpm/events/epoll.c
+++ b/sapi/fpm/fpm/events/epoll.c
@@ -46,7 +46,7 @@ static struct fpm_event_module_s epoll_module = {
static struct epoll_event *epollfds = NULL;
static int nepollfds = 0;
-static int epollfd = 0;
+static int epollfd = -1;
#endif /* HAVE_EPOLL */
@@ -103,6 +103,10 @@ static int fpm_event_epoll_clean() /* {{{ */
free(epollfds);
epollfds = NULL;
}
+ if (epollfd != -1) {
+ close(epollfd);
+ epollfd = -1;
+ }
nepollfds = 0;
diff --git a/sapi/fpm/fpm/fpm_unix.c b/sapi/fpm/fpm/fpm_unix.c
index b32213fa74..68978ee75d 100644
--- a/sapi/fpm/fpm/fpm_unix.c
+++ b/sapi/fpm/fpm/fpm_unix.c
@@ -39,7 +39,7 @@ int fpm_unix_resolve_socket_premissions(struct fpm_worker_pool_s *wp) /* {{{ */
/* uninitialized */
wp->socket_uid = -1;
wp->socket_gid = -1;
- wp->socket_mode = 0666;
+ wp->socket_mode = 0660;
if (!c) {
return 0;
diff --git a/sapi/fpm/php-fpm.conf.in b/sapi/fpm/php-fpm.conf.in
index ab03736beb..c5f4abc59c 100644
--- a/sapi/fpm/php-fpm.conf.in
+++ b/sapi/fpm/php-fpm.conf.in
@@ -166,10 +166,10 @@ listen = 127.0.0.1:9000
; permissions must be set in order to allow connections from a web server. Many
; BSD-derived systems allow connections regardless of permissions.
; Default Values: user and group are set as the running user
-; mode is set to 0666
+; mode is set to 0660
;listen.owner = @php_fpm_user@
;listen.group = @php_fpm_group@
-;listen.mode = 0666
+;listen.mode = 0660
; List of ipv4 addresses of FastCGI clients which are allowed to connect.
; Equivalent to the FCGI_WEB_SERVER_ADDRS environment variable in the original
@@ -185,7 +185,7 @@ listen = 127.0.0.1:9000
; - The pool processes will inherit the master process priority
; unless it specified otherwise
; Default Value: no set
-; priority = -19
+; process.priority = -19
; Choose how the process manager will control the number of child processes.
; Possible Values:
diff --git a/sapi/litespeed/lsapi_main.c b/sapi/litespeed/lsapi_main.c
index 33ebb988a6..34ae751b2c 100644
--- a/sapi/litespeed/lsapi_main.c
+++ b/sapi/litespeed/lsapi_main.c
@@ -615,7 +615,7 @@ static void cli_usage( TSRMLS_D )
" args... Arguments passed to script.\n";
php_output_startup();
php_output_activate(TSRMLS_C);
- php_printf( usage );
+ php_printf( "%s", usage );
#ifdef PHP_OUTPUT_NEWAPI
php_output_end_all(TSRMLS_C);
#else
diff --git a/sapi/phpdbg/.gitignore b/sapi/phpdbg/.gitignore
index 297efcbc42..5506a2652d 100644
--- a/sapi/phpdbg/.gitignore
+++ b/sapi/phpdbg/.gitignore
@@ -1,5 +1,5 @@
.libs/
-./phpdbg
+phpdbg
*.lo
*.o
build
diff --git a/sapi/phpdbg/Makefile.frag b/sapi/phpdbg/Makefile.frag
index 5be6d5b00f..45768de2ef 100644
--- a/sapi/phpdbg/Makefile.frag
+++ b/sapi/phpdbg/Makefile.frag
@@ -8,6 +8,15 @@ $(BUILD_SHARED): $(PHP_GLOBAL_OBJS) $(PHP_BINARY_OBJS) $(PHP_PHPDBG_OBJS)
$(BUILD_BINARY): $(PHP_GLOBAL_OBJS) $(PHP_BINARY_OBJS) $(PHP_PHPDBG_OBJS)
$(BUILD_PHPDBG)
+$(builddir)/sapi/phpdbg/phpdbg_lexer.lo: $(srcdir)/sapi/phpdbg/phpdbg_parser.h
+
+$(srcdir)/sapi/phpdbg/phpdbg_lexer.c: $(srcdir)/sapi/phpdbg/phpdbg_lexer.l
+ @(cd $(top_srcdir); $(RE2C) $(RE2C_FLAGS) --no-generation-date -cbdFo sapi/phpdbg/phpdbg_lexer.c sapi/phpdbg/phpdbg_lexer.l)
+
+$(srcdir)/sapi/phpdbg/phpdbg_parser.h: $(srcdir)/sapi/phpdbg/phpdbg_parser.c
+$(srcdir)/sapi/phpdbg/phpdbg_parser.c: $(srcdir)/sapi/phpdbg/phpdbg_parser.y
+ @$(YACC) -p phpdbg_ -v -d $(srcdir)/sapi/phpdbg/phpdbg_parser.y -o $@
+
install-phpdbg: $(BUILD_BINARY)
@echo "Installing phpdbg binary: $(INSTALL_ROOT)$(bindir)/"
@$(mkinstalldirs) $(INSTALL_ROOT)$(bindir)
@@ -25,4 +34,3 @@ test-phpdbg:
.PHONY: clean-phpdbg test-phpdbg
-
diff --git a/sapi/phpdbg/config.m4 b/sapi/phpdbg/config.m4
index 274e6409d0..3534d90124 100644
--- a/sapi/phpdbg/config.m4
+++ b/sapi/phpdbg/config.m4
@@ -3,10 +3,10 @@ dnl $Id$
dnl
PHP_ARG_ENABLE(phpdbg, for phpdbg support,
-[ --enable-phpdbg Build phpdbg], yes, yes)
+[ --enable-phpdbg Build phpdbg], no, no)
PHP_ARG_ENABLE(phpdbg-debug, for phpdbg debug build,
-[ --enable-phpdbg-debug Build phpdbg in debug mode], no, no)
+[ --enable-phpdbg-debug Build phpdbg in debug mode], no, no)
if test "$PHP_PHPDBG" != "no"; then
AC_DEFINE(HAVE_PHPDBG, 1, [ ])
@@ -18,17 +18,22 @@ if test "$PHP_PHPDBG" != "no"; then
fi
PHP_PHPDBG_CFLAGS="-D_GNU_SOURCE"
- PHP_PHPDBG_FILES="phpdbg.c phpdbg_prompt.c phpdbg_help.c phpdbg_break.c phpdbg_print.c phpdbg_bp.c phpdbg_opcode.c phpdbg_list.c phpdbg_utils.c phpdbg_info.c phpdbg_cmd.c phpdbg_set.c phpdbg_frame.c"
+ PHP_PHPDBG_FILES="phpdbg.c phpdbg_parser.c phpdbg_lexer.c phpdbg_prompt.c phpdbg_help.c phpdbg_break.c phpdbg_print.c phpdbg_bp.c phpdbg_opcode.c phpdbg_list.c phpdbg_utils.c phpdbg_info.c phpdbg_cmd.c phpdbg_set.c phpdbg_frame.c phpdbg_watch.c phpdbg_btree.c"
+ if test "$PHP_READLINE" != "no"; then
+ PHPDBG_EXTRA_LIBS="-lreadline"
+ fi
+
PHP_SUBST(PHP_PHPDBG_CFLAGS)
PHP_SUBST(PHP_PHPDBG_FILES)
-
+ PHP_SUBST(PHPDBG_EXTRA_LIBS)
+
PHP_ADD_MAKEFILE_FRAGMENT([$abs_srcdir/sapi/phpdbg/Makefile.frag])
PHP_SELECT_SAPI(phpdbg, program, $PHP_PHPDBG_FILES, $PHP_PHPDBG_CFLAGS, [$(SAPI_PHPDBG_PATH)])
BUILD_BINARY="sapi/phpdbg/phpdbg"
BUILD_SHARED="sapi/phpdbg/libphpdbg.la"
-
+
BUILD_PHPDBG="\$(LIBTOOL) --mode=link \
\$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) \
\$(PHP_GLOBAL_OBJS) \
diff --git a/sapi/phpdbg/config.w32 b/sapi/phpdbg/config.w32
index 29031507b3..17e15b6ced 100644
--- a/sapi/phpdbg/config.w32
+++ b/sapi/phpdbg/config.w32
@@ -1,19 +1,19 @@
-ARG_ENABLE('phpdbg', 'Build phpdbg', 'yes');
+ARG_ENABLE('phpdbg', 'Build phpdbg', 'no');
ARG_ENABLE('phpdbgs', 'Build phpdbg shared', 'no');
-PHPDBG_SOURCES='phpdbg.c phpdbg_prompt.c phpdbg_cmd.c phpdbg_info.c phpdbg_help.c phpdbg_break.c phpdbg_print.c phpdbg_bp.c phpdbg_opcode.c phpdbg_list.c phpdbg_utils.c phpdbg_set.c phpdbg_frame.c';
+PHPDBG_SOURCES='phpdbg.c phpdbg_prompt.c phpdbg_cmd.c phpdbg_info.c phpdbg_help.c phpdbg_break.c phpdbg_print.c phpdbg_bp.c phpdbg_opcode.c phpdbg_list.c phpdbg_utils.c phpdbg_set.c phpdbg_frame.c phpdbg_watch.c phpdbg_win.c phpdbg_btree.c phpdbg_parser.c phpdbg_lexer.c';
PHPDBG_DLL='php' + PHP_VERSION + 'phpdbg.dll';
PHPDBG_EXE='phpdbg.exe';
if (PHP_PHPDBG == "yes") {
- /* build phpdbg binary */
- SAPI('phpdbg', PHPDBG_SOURCES, PHPDBG_EXE);
- ADD_FLAG("LIBS_PHPDBG", "ws2_32.lib user32.lib");
+ SAPI('phpdbg', PHPDBG_SOURCES, PHPDBG_EXE);
+ ADD_FLAG("LIBS_PHPDBG", "ws2_32.lib user32.lib");
+ ADD_FLAG("CFLAGS_PHPDBG", "/D YY_NO_UNISTD_H");
}
if (PHP_PHPDBGS == "yes") {
SAPI('phpdbgs', PHPDBG_SOURCES, PHPDBG_DLL, '/D PHP_PHPDBG_EXPORTS /I win32');
ADD_FLAG("LIBS_PHPDBGS", "ws2_32.lib user32.lib");
+ ADD_FLAG("CFLAGS_PHPDBGS", "/D YY_NO_UNISTD_H");
}
-
diff --git a/sapi/phpdbg/phpdbg.c b/sapi/phpdbg/phpdbg.c
index 51a328d0b5..fc121839e8 100644
--- a/sapi/phpdbg/phpdbg.c
+++ b/sapi/phpdbg/phpdbg.c
@@ -18,7 +18,7 @@
+----------------------------------------------------------------------+
*/
-#ifndef ZEND_SIGNALS
+#if !defined(ZEND_SIGNALS) || defined(_WIN32)
# include <signal.h>
#endif
#include "phpdbg.h"
@@ -28,6 +28,7 @@
#include "phpdbg_list.h"
#include "phpdbg_utils.h"
#include "phpdbg_set.h"
+#include "zend_alloc.h"
/* {{{ remote console headers */
#ifndef _WIN32
@@ -61,16 +62,15 @@ static inline void php_phpdbg_globals_ctor(zend_phpdbg_globals *pg) /* {{{ */
pg->exec = NULL;
pg->exec_len = 0;
+ pg->buffer = NULL;
pg->ops = NULL;
pg->vmret = 0;
pg->bp_count = 0;
- pg->lcmd = NULL;
pg->flags = PHPDBG_DEFAULT_FLAGS;
pg->oplog = NULL;
pg->io[PHPDBG_STDIN] = NULL;
pg->io[PHPDBG_STDOUT] = NULL;
pg->io[PHPDBG_STDERR] = NULL;
- memset(&pg->lparam, 0, sizeof(phpdbg_param_t));
pg->frame.num = 0;
} /* }}} */
@@ -145,6 +145,7 @@ static void php_phpdbg_destroy_registered(void *data) /* {{{ */
function TSRMLS_CC);
} /* }}} */
+
static PHP_RINIT_FUNCTION(phpdbg) /* {{{ */
{
zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE], 8, NULL, php_phpdbg_destroy_bp_file, 0);
@@ -157,7 +158,9 @@ static PHP_RINIT_FUNCTION(phpdbg) /* {{{ */
zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD], 8, NULL, php_phpdbg_destroy_bp_methods, 0);
zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], 8, NULL, php_phpdbg_destroy_bp_condition, 0);
zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP], 8, NULL, NULL, 0);
-
+
+ phpdbg_setup_watchpoints(TSRMLS_C);
+
zend_hash_init(&PHPDBG_G(seek), 8, NULL, NULL, 0);
zend_hash_init(&PHPDBG_G(registered), 8, NULL, php_phpdbg_destroy_registered, 0);
@@ -178,7 +181,14 @@ static PHP_RSHUTDOWN_FUNCTION(phpdbg) /* {{{ */
zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP]);
zend_hash_destroy(&PHPDBG_G(seek));
zend_hash_destroy(&PHPDBG_G(registered));
+ zend_hash_destroy(&PHPDBG_G(watchpoints));
+ zend_llist_destroy(&PHPDBG_G(watchlist_mem));
+ if (PHPDBG_G(buffer)) {
+ efree(PHPDBG_G(buffer));
+ PHPDBG_G(buffer) = NULL;
+ }
+
if (PHPDBG_G(exec)) {
efree(PHPDBG_G(exec));
PHPDBG_G(exec) = NULL;
@@ -209,24 +219,24 @@ static PHP_RSHUTDOWN_FUNCTION(phpdbg) /* {{{ */
return SUCCESS;
} /* }}} */
-/* {{{ proto mixed phpdbg_exec(string context)
+/* {{{ proto mixed phpdbg_exec(string context)
Attempt to set the execution context for phpdbg
If the execution context was set previously it is returned
- If the execution context was not set previously boolean true is returned
+ If the execution context was not set previously boolean true is returned
If the request to set the context fails, boolean false is returned, and an E_WARNING raised */
-static PHP_FUNCTION(phpdbg_exec)
+static PHP_FUNCTION(phpdbg_exec)
{
char *exec = NULL;
- zend_ulong exec_len = 0L;
+ int exec_len = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &exec, &exec_len) == FAILURE) {
return;
}
-
+
{
struct stat sb;
zend_bool result = 1;
-
+
if (VCWD_STAT(exec, &sb) != FAILURE) {
if (sb.st_mode & (S_IFREG|S_IFLNK)) {
if (PHPDBG_G(exec)) {
@@ -234,11 +244,11 @@ static PHP_FUNCTION(phpdbg_exec)
efree(PHPDBG_G(exec));
result = 0;
}
-
+
PHPDBG_G(exec) = estrndup(exec, exec_len);
PHPDBG_G(exec_len) = exec_len;
-
- if (result)
+
+ if (result)
ZVAL_BOOL(return_value, 1);
} else {
zend_error(
@@ -259,9 +269,9 @@ static PHP_FUNCTION(phpdbg_exec)
static PHP_FUNCTION(phpdbg_break)
{
if (ZEND_NUM_ARGS() > 0) {
- long type;
+ long type = 0;
char *expr = NULL;
- zend_uint expr_len = 0;
+ int expr_len = 0;
phpdbg_param_t param;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &type, &expr, &expr_len) == FAILURE) {
@@ -269,28 +279,7 @@ static PHP_FUNCTION(phpdbg_break)
}
phpdbg_parse_param(expr, expr_len, &param TSRMLS_CC);
-
- switch (type) {
- case METHOD_PARAM:
- phpdbg_do_break_method(&param, NULL TSRMLS_CC);
- break;
-
- case FILE_PARAM:
- phpdbg_do_break_file(&param, NULL TSRMLS_CC);
- break;
-
- case NUMERIC_PARAM:
- phpdbg_do_break_lineno(&param, NULL TSRMLS_CC);
- break;
-
- case STR_PARAM:
- phpdbg_do_break_func(&param, NULL TSRMLS_CC);
- break;
-
- default: zend_error(
- E_WARNING, "unrecognized parameter type %ld", type);
- }
-
+ phpdbg_do_break(&param TSRMLS_CC);
phpdbg_clear_param(&param TSRMLS_CC);
} else if (EG(current_execute_data) && EG(active_op_array)) {
@@ -319,9 +308,9 @@ static PHP_FUNCTION(phpdbg_clear)
/* {{{ proto void phpdbg_color(integer element, string color) */
static PHP_FUNCTION(phpdbg_color)
{
- long element;
- char *color;
- zend_uint color_len;
+ long element = 0L;
+ char *color = NULL;
+ int color_len = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &element, &color, &color_len) == FAILURE) {
return;
@@ -341,8 +330,8 @@ static PHP_FUNCTION(phpdbg_color)
/* {{{ proto void phpdbg_prompt(string prompt) */
static PHP_FUNCTION(phpdbg_prompt)
{
- char *prompt;
- zend_uint prompt_len;
+ char *prompt = NULL;
+ int prompt_len = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &prompt, &prompt_len) == FAILURE) {
return;
@@ -403,9 +392,9 @@ static inline int php_sapi_phpdbg_module_startup(sapi_module_struct *module) /*
if (php_module_startup(module, &sapi_phpdbg_module_entry, 1) == FAILURE) {
return FAILURE;
}
-
+
phpdbg_booted=1;
-
+
return SUCCESS;
} /* }}} */
@@ -544,6 +533,69 @@ static inline void php_sapi_phpdbg_flush(void *context) /* {{{ */
fflush(PHPDBG_G(io)[PHPDBG_STDOUT]);
} /* }}} */
+/* copied from sapi/cli/php_cli.c cli_register_file_handles */
+static void phpdbg_register_file_handles(TSRMLS_D) /* {{{ */
+{
+ zval *zin, *zout, *zerr;
+ php_stream *s_in, *s_out, *s_err;
+ php_stream_context *sc_in=NULL, *sc_out=NULL, *sc_err=NULL;
+ zend_constant ic, oc, ec;
+
+ MAKE_STD_ZVAL(zin);
+ MAKE_STD_ZVAL(zout);
+ MAKE_STD_ZVAL(zerr);
+
+ s_in = php_stream_open_wrapper_ex("php://stdin", "rb", 0, NULL, sc_in);
+ s_out = php_stream_open_wrapper_ex("php://stdout", "wb", 0, NULL, sc_out);
+ s_err = php_stream_open_wrapper_ex("php://stderr", "wb", 0, NULL, sc_err);
+
+ if (s_in==NULL || s_out==NULL || s_err==NULL) {
+ FREE_ZVAL(zin);
+ FREE_ZVAL(zout);
+ FREE_ZVAL(zerr);
+ if (s_in) php_stream_close(s_in);
+ if (s_out) php_stream_close(s_out);
+ if (s_err) php_stream_close(s_err);
+ return;
+ }
+
+#if PHP_DEBUG
+ /* do not close stdout and stderr */
+ s_out->flags |= PHP_STREAM_FLAG_NO_CLOSE;
+ s_err->flags |= PHP_STREAM_FLAG_NO_CLOSE;
+#endif
+
+ php_stream_to_zval(s_in, zin);
+ php_stream_to_zval(s_out, zout);
+ php_stream_to_zval(s_err, zerr);
+
+ ic.value = *zin;
+ ic.flags = CONST_CS;
+ ic.name = zend_strndup(ZEND_STRL("STDIN"));
+ ic.name_len = sizeof("STDIN");
+ ic.module_number = 0;
+ zend_register_constant(&ic TSRMLS_CC);
+
+ oc.value = *zout;
+ oc.flags = CONST_CS;
+ oc.name = zend_strndup(ZEND_STRL("STDOUT"));
+ oc.name_len = sizeof("STDOUT");
+ oc.module_number = 0;
+ zend_register_constant(&oc TSRMLS_CC);
+
+ ec.value = *zerr;
+ ec.flags = CONST_CS;
+ ec.name = zend_strndup(ZEND_STRL("STDERR"));
+ ec.name_len = sizeof("STDERR");
+ ec.module_number = 0;
+ zend_register_constant(&ec TSRMLS_CC);
+
+ FREE_ZVAL(zin);
+ FREE_ZVAL(zout);
+ FREE_ZVAL(zerr);
+}
+/* }}} */
+
/* {{{ sapi_module_struct phpdbg_sapi_module
*/
static sapi_module_struct phpdbg_sapi_module = {
@@ -585,7 +637,6 @@ const opt_struct OPTIONS[] = { /* {{{ */
{'z', 1, "load zend_extension"},
/* phpdbg options */
{'q', 0, "no banner"},
- {'e', 1, "exec"},
{'v', 0, "disable quietness"},
{'s', 0, "enable stepping"},
{'b', 0, "boring colours"},
@@ -615,10 +666,10 @@ const char phpdbg_ini_hardcoded[] =
/* overwriteable ini defaults must be set in phpdbg_ini_defaults() */
#define INI_DEFAULT(name, value) \
- Z_SET_REFCOUNT(tmp, 0); \
- Z_UNSET_ISREF(tmp); \
- ZVAL_STRINGL(&tmp, zend_strndup(value, sizeof(value)-1), sizeof(value)-1, 0); \
- zend_hash_update(configuration_hash, name, sizeof(name), &tmp, sizeof(zval), NULL);
+ Z_SET_REFCOUNT(tmp, 0); \
+ Z_UNSET_ISREF(tmp); \
+ ZVAL_STRINGL(&tmp, zend_strndup(value, sizeof(value)-1), sizeof(value)-1, 0); \
+ zend_hash_update(configuration_hash, name, sizeof(name), &tmp, sizeof(zval), NULL);
void phpdbg_ini_defaults(HashTable *configuration_hash) /* {{{ */
{
@@ -666,11 +717,11 @@ static inline void phpdbg_sigint_handler(int signo) /* {{{ */
int phpdbg_open_socket(const char *interface, short port) /* {{{ */
{
int fd = socket(AF_INET, SOCK_STREAM, 0);
-
+
switch (fd) {
case -1:
return -1;
-
+
default: {
int reuse = 1;
@@ -678,27 +729,27 @@ int phpdbg_open_socket(const char *interface, short port) /* {{{ */
case -1:
close(fd);
return -2;
-
+
default: {
struct sockaddr_in address;
-
+
memset(&address, 0, sizeof(address));
-
+
address.sin_port = htons(port);
address.sin_family = AF_INET;
-
+
if ((*interface == '*')) {
- address.sin_addr.s_addr = htonl(INADDR_ANY);
+ address.sin_addr.s_addr = htonl(INADDR_ANY);
} else if (!inet_pton(AF_INET, interface, &address.sin_addr)) {
close(fd);
return -3;
}
-
+
switch (bind(fd, (struct sockaddr *)&address, sizeof(address))) {
case -1:
close(fd);
return -4;
-
+
default: {
listen(fd, 5);
}
@@ -707,28 +758,28 @@ int phpdbg_open_socket(const char *interface, short port) /* {{{ */
}
}
}
-
+
return fd;
} /* }}} */
static inline void phpdbg_close_sockets(int (*socket)[2], FILE *streams[2]) /* {{{ */
-{
+{
if ((*socket)[0] >= 0) {
shutdown(
(*socket)[0], SHUT_RDWR);
close((*socket)[0]);
}
-
+
if (streams[0]) {
fclose(streams[0]);
}
-
+
if ((*socket)[1] >= 0) {
shutdown(
(*socket)[1], SHUT_RDWR);
close((*socket)[1]);
}
-
+
if (streams[1]) {
fclose(streams[1]);
}
@@ -798,14 +849,34 @@ int phpdbg_open_sockets(char *address, int port[2], int (*listen)[2], int (*sock
dup2((*socket)[0], fileno(stdin));
dup2((*socket)[1], fileno(stdout));
-
+
setbuf(stdout, NULL);
streams[0] = fdopen((*socket)[0], "r");
streams[1] = fdopen((*socket)[1], "w");
-
+
return SUCCESS;
} /* }}} */
+
+void phpdbg_signal_handler(int sig, siginfo_t *info, void *context) {
+ int is_handled = FAILURE;
+ TSRMLS_FETCH();
+
+ switch (sig) {
+ case SIGBUS:
+ case SIGSEGV:
+ is_handled = phpdbg_watchpoint_segfault_handler(info, context TSRMLS_CC);
+ if (is_handled == FAILURE) {
+#ifdef ZEND_SIGNALS
+ zend_sigaction(sig, &PHPDBG_G(old_sigsegv_signal), NULL TSRMLS_CC);
+#else
+ sigaction(sig, &PHPDBG_G(old_sigsegv_signal), NULL);
+#endif
+ }
+ break;
+ }
+
+}
#endif
int main(int argc, char **argv) /* {{{ */
@@ -852,6 +923,10 @@ int main(int argc, char **argv) /* {{{ */
#endif
#ifndef _WIN32
+ struct sigaction signal_struct;
+ signal_struct.sa_sigaction = phpdbg_signal_handler;
+ signal_struct.sa_flags = SA_SIGINFO | SA_NODEFER;
+
address = strdup("127.0.0.1");
socket[0] = -1;
socket[1] = -1;
@@ -888,17 +963,17 @@ phpdbg_main:
bp_tmp_file = NULL;
}
}
+
+ if (!bp_tmp_file) {
+ phpdbg_error("Unable to create temporary file");
+ return 1;
+ }
#else
if (!mkstemp(bp_tmp_file)) {
memset(bp_tmp_file, 0, sizeof(bp_tmp_file));
}
#endif
- if (!bp_tmp_file) {
- phpdbg_error(
- "Unable to create temporary file");
- return 1;
- }
}
ini_entries = NULL;
ini_entries_len = 0;
@@ -920,8 +995,7 @@ phpdbg_main:
run = 0;
step = 0;
sapi_name = NULL;
-
-
+
while ((opt = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) {
switch (opt) {
case 'r':
@@ -965,7 +1039,7 @@ phpdbg_main:
ini_entries_len += len + sizeof("=1\n\0") - 2;
}
} break;
-
+
case 'z':
zend_extensions_len++;
if (zend_extensions) {
@@ -976,16 +1050,6 @@ phpdbg_main:
/* begin phpdbg options */
- case 'e': { /* set execution context */
- exec_len = strlen(php_optarg);
- if (exec_len) {
- if (exec) {
- free(exec);
- }
- exec = strdup(php_optarg);
- }
- } break;
-
case 'S': { /* set SAPI name */
if (sapi_name) {
free(sapi_name);
@@ -1001,7 +1065,7 @@ phpdbg_main:
if (init_file) {
free(init_file);
}
-
+
init_file_len = strlen(php_optarg);
if (init_file_len) {
init_file = strdup(php_optarg);
@@ -1038,7 +1102,7 @@ phpdbg_main:
#ifndef _WIN32
/* if you pass a listen port, we will accept input on listen port */
/* and write output to listen port * 2 */
-
+
case 'l': { /* set listen ports */
if (sscanf(php_optarg, "%d/%d", &listen[0], &listen[1]) != 2) {
if (sscanf(php_optarg, "%d", &listen[0]) != 1) {
@@ -1050,7 +1114,7 @@ phpdbg_main:
}
}
} break;
-
+
case 'a': { /* set bind address */
free(address);
if (!php_optarg) {
@@ -1076,6 +1140,19 @@ phpdbg_main:
} break;
}
}
+
+ /* set exec if present on command line */
+ if ((argc > php_optind) && (strcmp(argv[php_optind-1],"--") != SUCCESS))
+ {
+ exec_len = strlen(argv[php_optind]);
+ if (exec_len) {
+ if (exec) {
+ free(exec);
+ }
+ exec = strdup(argv[php_optind]);
+ }
+ php_optind++;
+ }
#ifndef _WIN32
/* setup remote server if necessary */
@@ -1093,7 +1170,7 @@ phpdbg_main:
if (sapi_name) {
phpdbg->name = sapi_name;
}
-
+
phpdbg->ini_defaults = phpdbg_ini_defaults;
phpdbg->phpinfo_as_text = 1;
phpdbg->php_ini_ignore_cwd = 1;
@@ -1114,14 +1191,14 @@ phpdbg_main:
memcpy(ini_entries, phpdbg_ini_hardcoded, sizeof(phpdbg_ini_hardcoded));
}
ini_entries_len += sizeof(phpdbg_ini_hardcoded) - 2;
-
+
if (zend_extensions_len) {
zend_ulong zend_extension = 0L;
-
+
while (zend_extension < zend_extensions_len) {
const char *ze = zend_extensions[zend_extension];
size_t ze_len = strlen(ze);
-
+
ini_entries = realloc(
ini_entries, ini_entries_len + (ze_len + (sizeof("zend_extension=\n"))));
memcpy(&ini_entries[ini_entries_len], "zend_extension=", (sizeof("zend_extension=\n")-1));
@@ -1133,40 +1210,81 @@ phpdbg_main:
free(zend_extensions[zend_extension]);
zend_extension++;
}
-
+
free(zend_extensions);
}
phpdbg->ini_entries = ini_entries;
-
+
if (phpdbg->startup(phpdbg) == SUCCESS) {
- php_request_startup(TSRMLS_C);
+#ifdef _WIN32
+ EXCEPTION_POINTERS *xp;
+ __try {
+#endif
+ zend_mm_heap *mm_heap = zend_mm_set_heap(NULL TSRMLS_CC);
+#if ZEND_DEBUG
+ if (!mm_heap->use_zend_alloc) {
+ mm_heap->_malloc = malloc;
+ mm_heap->_realloc = realloc;
+ mm_heap->_free = free;
+#endif
+ PHPDBG_G(original_free_function) = mm_heap->_free;
+ mm_heap->_free = phpdbg_watch_efree;
+ mm_heap->use_zend_alloc = 0;
+#if ZEND_DEBUG
+ }
+#endif
+ zend_mm_set_heap(mm_heap TSRMLS_CC);
+
+ zend_activate(TSRMLS_C);
+
+#if defined(ZEND_SIGNALS) && !defined(_WIN32)
+ zend_try {
+ zend_signal_activate(TSRMLS_C);
+ } zend_end_try();
+#endif
+
+#if defined(ZEND_SIGNALS) && !defined(_WIN32)
+ zend_try { zend_sigaction(SIGSEGV, &signal_struct, &PHPDBG_G(old_sigsegv_signal) TSRMLS_CC); } zend_end_try();
+ zend_try { zend_sigaction(SIGBUS, &signal_struct, &PHPDBG_G(old_sigsegv_signal) TSRMLS_CC); } zend_end_try();
+#elif !defined(_WIN32)
+ sigaction(SIGSEGV, &signal_struct, &PHPDBG_G(old_sigsegv_signal));
+ sigaction(SIGBUS, &signal_struct, &PHPDBG_G(old_sigsegv_signal));
+#endif
+
+ if (php_request_startup(TSRMLS_C) == SUCCESS) {
+ int i;
+
+ SG(request_info).argc = argc - php_optind + 1;
+ SG(request_info).argv = emalloc(SG(request_info).argc * sizeof(char *));
+ for (i = SG(request_info).argc; --i;) {
+ SG(request_info).argv[i] = estrdup(argv[php_optind - 1 + i]);
+ }
+ SG(request_info).argv[i] = exec ? estrndup(exec, exec_len) : estrdup("");
+
+ php_hash_environment(TSRMLS_C);
+ }
+
+ /* make sure to turn off buffer for ev command */
+ php_output_activate(TSRMLS_C);
+ php_output_deactivate(TSRMLS_C);
/* do not install sigint handlers for remote consoles */
/* sending SIGINT then provides a decent way of shutting down the server */
-#ifdef ZEND_SIGNALS
-# ifndef _WIN32
+#if defined(ZEND_SIGNALS) && !defined(_WIN32)
if (listen[0] < 0) {
-# endif
- zend_try {
- zend_signal_activate(TSRMLS_C);
- zend_signal(SIGINT, phpdbg_sigint_handler TSRMLS_CC);
- } zend_end_try();
-# ifndef _WIN32
+ zend_try { zend_signal(SIGINT, phpdbg_sigint_handler TSRMLS_CC); } zend_end_try();
}
-# endif
-#else
-# ifndef _WIN32
+#elif !defined(_WIN32)
if (listen[0] < 0) {
-# endif
+#endif
signal(SIGINT, phpdbg_sigint_handler);
#ifndef _WIN32
}
#endif
-#endif
PG(modules_activated) = 0;
-
+
/* set flags from command line */
PHPDBG_G(flags) = flags;
@@ -1182,10 +1300,9 @@ phpdbg_main:
PHPDBG_G(io)[PHPDBG_STDIN] = stdin;
PHPDBG_G(io)[PHPDBG_STDOUT] = stdout;
PHPDBG_G(io)[PHPDBG_STDERR] = stderr;
-
+
if (exec) { /* set execution context */
- PHPDBG_G(exec) = phpdbg_resolve_path(
- exec TSRMLS_CC);
+ PHPDBG_G(exec) = phpdbg_resolve_path(exec TSRMLS_CC);
PHPDBG_G(exec_len) = strlen(PHPDBG_G(exec));
free(exec);
@@ -1208,11 +1325,19 @@ phpdbg_main:
/* set default prompt */
phpdbg_set_prompt(PROMPT TSRMLS_CC);
+ /* Make stdin, stdout and stderr accessible from PHP scripts */
+ phpdbg_register_file_handles(TSRMLS_C);
+
if (show_banner) {
/* print blurb */
phpdbg_welcome((cleaning > 0) TSRMLS_CC);
}
+ /* auto compile */
+ if (PHPDBG_G(exec)) {
+ phpdbg_compile(TSRMLS_C);
+ }
+
/* initialize from file */
PHPDBG_G(flags) |= PHPDBG_IS_INITIALIZING;
zend_try {
@@ -1233,14 +1358,17 @@ phpdbg_main:
if (run) {
/* no need to try{}, run does it ... */
- PHPDBG_COMMAND_HANDLER(run)(NULL, NULL TSRMLS_CC);
+ PHPDBG_COMMAND_HANDLER(run)(NULL TSRMLS_CC);
if (run > 1) {
/* if -r is on the command line more than once just quit */
goto phpdbg_out;
}
}
+/* #ifndef for making compiler shutting up */
+#ifndef _WIN32
phpdbg_interact:
+#endif
/* phpdbg main() */
do {
zend_try {
@@ -1282,21 +1410,42 @@ phpdbg_interact:
} zend_end_try();
} while(!cleaning && !(PHPDBG_G(flags) & PHPDBG_IS_QUITTING));
-phpdbg_out:
+ /* this must be forced */
+ CG(unclean_shutdown) = 0;
+
+ /* this is just helpful */
+ PG(report_memleaks) = 0;
+
#ifndef _WIN32
+phpdbg_out:
if ((PHPDBG_G(flags) & PHPDBG_IS_DISCONNECTED)) {
PHPDBG_G(flags) &= ~PHPDBG_IS_DISCONNECTED;
goto phpdbg_interact;
}
#endif
+#ifdef _WIN32
+ } __except(phpdbg_exception_handler_win32(xp = GetExceptionInformation())) {
+ phpdbg_error("Access violation (Segementation fault) encountered\ntrying to abort cleanly...");
+ }
+phpdbg_out:
+#endif
+
+ {
+ int i;
+ /* free argv */
+ for (i = SG(request_info).argc; --i;) {
+ efree(SG(request_info).argv[i]);
+ }
+ efree(SG(request_info).argv);
+ }
+
#ifndef ZTS
/* force cleanup of auto and core globals */
zend_hash_clean(CG(auto_globals));
memset(
&core_globals, 0, sizeof(php_core_globals));
#endif
-
if (ini_entries) {
free(ini_entries);
}
@@ -1318,6 +1467,7 @@ phpdbg_out:
} zend_end_try();
sapi_shutdown();
+
}
if (cleaning || remote) {
@@ -1331,7 +1481,7 @@ phpdbg_out:
#ifndef _WIN32
if (address) {
- free(address);
+ free(address);
}
#endif
diff --git a/sapi/phpdbg/phpdbg.h b/sapi/phpdbg/phpdbg.h
index 68c505ca63..be009e40d0 100644
--- a/sapi/phpdbg/phpdbg.h
+++ b/sapi/phpdbg/phpdbg.h
@@ -39,6 +39,9 @@
#include "zend_globals.h"
#include "zend_ini_scanner.h"
#include "zend_stream.h"
+#ifndef _WIN32
+# include "zend_signal.h"
+#endif
#include "SAPI.h"
#include <fcntl.h>
#include <sys/types.h>
@@ -68,6 +71,8 @@
#include "phpdbg_cmd.h"
#include "phpdbg_utils.h"
+#include "phpdbg_btree.h"
+#include "phpdbg_watch.h"
#ifdef ZTS
# define PHPDBG_G(v) TSRMG(phpdbg_globals_id, zend_phpdbg_globals *, v)
@@ -116,23 +121,26 @@
#define PHPDBG_IN_EVAL (1<<11)
#define PHPDBG_IS_STEPPING (1<<12)
-#define PHPDBG_IS_QUIET (1<<13)
-#define PHPDBG_IS_QUITTING (1<<14)
-#define PHPDBG_IS_COLOURED (1<<15)
-#define PHPDBG_IS_CLEANING (1<<16)
-
-#define PHPDBG_IN_UNTIL (1<<17)
-#define PHPDBG_IN_FINISH (1<<18)
-#define PHPDBG_IN_LEAVE (1<<19)
-
-#define PHPDBG_IS_REGISTERED (1<<20)
-#define PHPDBG_IS_STEPONEVAL (1<<21)
-#define PHPDBG_IS_INITIALIZING (1<<22)
-#define PHPDBG_IS_SIGNALED (1<<23)
-#define PHPDBG_IS_INTERACTIVE (1<<24)
-#define PHPDBG_IS_BP_ENABLED (1<<25)
-#define PHPDBG_IS_REMOTE (1<<26)
-#define PHPDBG_IS_DISCONNECTED (1<<27)
+#define PHPDBG_STEP_OPCODE (1<<13)
+#define PHPDBG_IS_QUIET (1<<14)
+#define PHPDBG_IS_QUITTING (1<<15)
+#define PHPDBG_IS_COLOURED (1<<16)
+#define PHPDBG_IS_CLEANING (1<<17)
+
+#define PHPDBG_IN_UNTIL (1<<18)
+#define PHPDBG_IN_FINISH (1<<19)
+#define PHPDBG_IN_LEAVE (1<<20)
+
+#define PHPDBG_IS_REGISTERED (1<<21)
+#define PHPDBG_IS_STEPONEVAL (1<<22)
+#define PHPDBG_IS_INITIALIZING (1<<23)
+#define PHPDBG_IS_SIGNALED (1<<24)
+#define PHPDBG_IS_INTERACTIVE (1<<25)
+#define PHPDBG_IS_BP_ENABLED (1<<26)
+#define PHPDBG_IS_REMOTE (1<<27)
+#define PHPDBG_IS_DISCONNECTED (1<<28)
+
+#define PHPDBG_SHOW_REFCOUNTS (1<<29)
#define PHPDBG_SEEK_MASK (PHPDBG_IN_UNTIL|PHPDBG_IN_FINISH|PHPDBG_IN_LEAVE)
#define PHPDBG_BP_RESOLVE_MASK (PHPDBG_HAS_FUNCTION_OPLINE_BP|PHPDBG_HAS_METHOD_OPLINE_BP|PHPDBG_HAS_FILE_OPLINE_BP)
@@ -149,7 +157,7 @@
#define PHPDBG_AUTHORS "Felipe Pena, Joe Watkins and Bob Weinand" /* Ordered by last name */
#define PHPDBG_URL "http://phpdbg.com"
#define PHPDBG_ISSUES "http://github.com/krakjoe/phpdbg/issues"
-#define PHPDBG_VERSION "0.3.2"
+#define PHPDBG_VERSION "0.4.0"
#define PHPDBG_INIT_FILENAME ".phpdbginit"
/* }}} */
@@ -159,12 +167,25 @@
#define PHPDBG_STDERR 2
#define PHPDBG_IO_FDS 3 /* }}} */
+
/* {{{ structs */
ZEND_BEGIN_MODULE_GLOBALS(phpdbg)
HashTable bp[PHPDBG_BREAK_TABLES]; /* break points */
HashTable registered; /* registered */
HashTable seek; /* seek oplines */
phpdbg_frame_t frame; /* frame */
+ zend_uint last_line; /* last executed line */
+
+#ifndef _WIN32
+ struct sigaction old_sigsegv_signal; /* segv signal handler */
+#endif
+
+ phpdbg_btree watchpoint_tree; /* tree with watchpoints */
+ phpdbg_btree watch_HashTables; /* tree with original dtors of watchpoints */
+ HashTable watchpoints; /* watchpoints */
+ zend_llist watchlist_mem; /* triggered watchpoints */
+ zend_bool watchpoint_hit; /* a watchpoint was hit */
+ void (*original_free_function)(void *); /* the original AG(mm_heap)->_free function */
char *exec; /* file to execute */
size_t exec_len; /* size of exec */
@@ -178,11 +199,24 @@ ZEND_BEGIN_MODULE_GLOBALS(phpdbg)
char *prompt[2]; /* prompt */
const phpdbg_color_t *colors[PHPDBG_COLORS]; /* colors */
-
- phpdbg_command_t *lcmd; /* last command */
- phpdbg_param_t lparam; /* last param */
+ char *buffer; /* buffer */
zend_ulong flags; /* phpdbg flags */
ZEND_END_MODULE_GLOBALS(phpdbg) /* }}} */
+/* the beginning (= the important part) of the _zend_mm_heap struct defined in Zend/zend_alloc.c
+ Needed for realizing watchpoints */
+struct _zend_mm_heap {
+ int use_zend_alloc;
+ void *(*_malloc)(size_t);
+ void (*_free)(void *);
+ void *(*_realloc)(void *, size_t);
+ size_t free_bitmap;
+ size_t large_free_bitmap;
+ size_t block_size;
+ size_t compact_size;
+ zend_mm_segment *segments_list;
+ zend_mm_storage *storage;
+};
+
#endif /* PHPDBG_H */
diff --git a/sapi/phpdbg/phpdbg_bp.c b/sapi/phpdbg/phpdbg_bp.c
index 511d1db57d..a18316a228 100644
--- a/sapi/phpdbg/phpdbg_bp.c
+++ b/sapi/phpdbg/phpdbg_bp.c
@@ -135,27 +135,27 @@ PHPDBG_API void phpdbg_export_breakpoints(FILE *handle TSRMLS_DC) /* {{{ */
switch (brake->type) {
case PHPDBG_BREAK_FILE: {
fprintf(handle,
- "break file %s:%lu\n",
+ "break %s:%lu\n",
((phpdbg_breakfile_t*)brake)->filename,
((phpdbg_breakfile_t*)brake)->line);
} break;
case PHPDBG_BREAK_SYM: {
fprintf(handle,
- "break func %s\n",
+ "break %s\n",
((phpdbg_breaksymbol_t*)brake)->symbol);
} break;
case PHPDBG_BREAK_METHOD: {
fprintf(handle,
- "break method %s::%s\n",
+ "break %s::%s\n",
((phpdbg_breakmethod_t*)brake)->class_name,
((phpdbg_breakmethod_t*)brake)->func_name);
} break;
case PHPDBG_BREAK_METHOD_OPLINE: {
fprintf(handle,
- "break address %s::%s#%ld\n",
+ "break %s::%s#%ld\n",
((phpdbg_breakopline_t*)brake)->class_name,
((phpdbg_breakopline_t*)brake)->func_name,
((phpdbg_breakopline_t*)brake)->opline_num);
@@ -163,21 +163,21 @@ PHPDBG_API void phpdbg_export_breakpoints(FILE *handle TSRMLS_DC) /* {{{ */
case PHPDBG_BREAK_FUNCTION_OPLINE: {
fprintf(handle,
- "break address %s#%ld\n",
+ "break %s#%ld\n",
((phpdbg_breakopline_t*)brake)->func_name,
((phpdbg_breakopline_t*)brake)->opline_num);
} break;
case PHPDBG_BREAK_FILE_OPLINE: {
fprintf(handle,
- "break address %s:%ld\n",
+ "break %s:#%ld\n",
((phpdbg_breakopline_t*)brake)->class_name,
((phpdbg_breakopline_t*)brake)->opline_num);
} break;
case PHPDBG_BREAK_OPCODE: {
fprintf(handle,
- "break op %s\n",
+ "break %s\n",
((phpdbg_breakop_t*)brake)->name);
} break;
@@ -209,7 +209,7 @@ PHPDBG_API void phpdbg_export_breakpoints(FILE *handle TSRMLS_DC) /* {{{ */
}
} else {
fprintf(
- handle, "break on %s\n", conditional->code);
+ handle, "break if %s\n", conditional->code);
}
} break;
}
@@ -221,14 +221,20 @@ PHPDBG_API void phpdbg_export_breakpoints(FILE *handle TSRMLS_DC) /* {{{ */
PHPDBG_API void phpdbg_set_breakpoint_file(const char *path, long line_num TSRMLS_DC) /* {{{ */
{
- struct stat sb;
-
- if (VCWD_STAT(path, &sb) != FAILURE) {
- if (sb.st_mode & (S_IFREG|S_IFLNK)) {
+ php_stream_statbuf ssb;
+ char realpath[MAXPATHLEN];
+
+ if (php_stream_stat_path(path, &ssb) != FAILURE) {
+ if (ssb.sb.st_mode & (S_IFREG|S_IFLNK)) {
HashTable *broken;
phpdbg_breakfile_t new_break;
- size_t path_len = strlen(path);
-
+ size_t path_len = 0L;
+
+ if (VCWD_REALPATH(path, realpath)) {
+ path = realpath;
+ }
+ path_len = strlen(path);
+
if (zend_hash_find(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE],
path, path_len, (void**)&broken) == FAILURE) {
HashTable breaks;
@@ -324,9 +330,9 @@ PHPDBG_API void phpdbg_set_breakpoint_method(const char *class_name, const char
PHPDBG_BREAK_MAPPING(new_break.id, class_table);
} else {
phpdbg_notice("Breakpoint exists at %s::%s", class_name, func_name);
- }
+ }
- efree(lcname);
+ efree(lcname);
} /* }}} */
PHPDBG_API void phpdbg_set_breakpoint_opline(zend_ulong opline TSRMLS_DC) /* {{{ */
@@ -355,7 +361,7 @@ PHPDBG_API void phpdbg_set_breakpoint_opline(zend_ulong opline TSRMLS_DC) /* {{{
PHPDBG_API int phpdbg_resolve_op_array_break(phpdbg_breakopline_t *brake, zend_op_array *op_array TSRMLS_DC) /* {{{ */
{
phpdbg_breakline_t opline_break;
- if (op_array->last < brake->opline_num) {
+ if (op_array->last <= brake->opline_num) {
if (brake->class_name == NULL) {
phpdbg_error("There are only %d oplines in function %s (breaking at opline %ld impossible)", op_array->last, brake->func_name, brake->opline_num);
} else if (brake->func_name == NULL) {
@@ -760,56 +766,26 @@ PHPDBG_API void phpdbg_set_breakpoint_expression(const char *expr, size_t expr_l
}
} /* }}} */
-PHPDBG_API void phpdbg_set_breakpoint_at(const phpdbg_param_t *param, const phpdbg_input_t *input TSRMLS_DC) /* {{{ */
+PHPDBG_API void phpdbg_set_breakpoint_at(const phpdbg_param_t *param TSRMLS_DC) /* {{{ */
{
- if (input->argc > 3 && phpdbg_argv_is(2, "if")) {
- phpdbg_breakcond_t new_break;
- phpdbg_param_t new_param;
-
- zend_ulong expr_hash = 0L;
- size_t expr_len;
- const char *join = strstr(input->string, "if");
- const char *expr = (join) + sizeof("if");
-
- expr_len = strlen(expr);
- expr = phpdbg_trim(expr, expr_len, &expr_len);
- expr_hash = zend_inline_hash_func(expr, expr_len);
-
- {
- /* get a clean parameter from input string */
- size_t sparam_len = 0L;
- char *sparam = input->string;
-
- sparam[
- strstr(input->string, " ") - input->string] = 0;
- sparam_len = strlen(sparam);
-
- switch (phpdbg_parse_param(sparam, sparam_len, &new_param TSRMLS_CC)) {
- case EMPTY_PARAM:
- case NUMERIC_PARAM:
- phpdbg_clear_param(
- &new_param TSRMLS_CC);
- goto usage;
-
- default: { /* do nothing */ } break;
- }
-
- expr_hash += phpdbg_hash_param(&new_param TSRMLS_CC);
- }
-
- if (!zend_hash_index_exists(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], expr_hash)) {
+ phpdbg_breakcond_t new_break;
+ phpdbg_param_t *condition;
+ zend_ulong hash = 0L;
+
+ if (param->next) {
+ condition = param->next;
+ hash = zend_inline_hash_func(condition->str, condition->len);
+
+ if (!zend_hash_index_exists(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], hash)) {
phpdbg_create_conditional_break(
- &new_break, &new_param, expr, expr_len, expr_hash TSRMLS_CC);
+ &new_break, param,
+ condition->str, condition->len, hash TSRMLS_CC);
} else {
phpdbg_notice(
- "Conditional break %s exists at the specified location", expr);
- }
-
- phpdbg_clear_param(&new_param TSRMLS_CC);
- } else {
-usage:
- phpdbg_error("usage: break at <func|method|file:line|address> if <expression>");
+ "Conditional break %s exists at the specified location", condition->str);
+ }
}
+
} /* }}} */
static inline phpdbg_breakbase_t *phpdbg_find_breakpoint_file(zend_op_array *op_array TSRMLS_DC) /* {{{ */
@@ -992,7 +968,7 @@ static inline phpdbg_breakbase_t *phpdbg_find_conditional_breakpoint(zend_execut
for (zend_hash_internal_pointer_reset_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], &position);
zend_hash_get_current_data_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], (void*)&bp, &position) == SUCCESS;
- zend_hash_move_forward_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], &position)) {
+ zend_hash_move_forward_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], &position)) {
zval *retval = NULL;
int orig_interactive = CG(interactive);
zval **orig_retval = EG(return_value_ptr_ptr);
diff --git a/sapi/phpdbg/phpdbg_bp.h b/sapi/phpdbg/phpdbg_bp.h
index b1a9ddf474..97980e7ed7 100644
--- a/sapi/phpdbg/phpdbg_bp.h
+++ b/sapi/phpdbg/phpdbg_bp.h
@@ -119,7 +119,7 @@ PHPDBG_API void phpdbg_set_breakpoint_method_opline(const char *class, const cha
PHPDBG_API void phpdbg_set_breakpoint_function_opline(const char *function, zend_ulong opline TSRMLS_DC);
PHPDBG_API void phpdbg_set_breakpoint_file_opline(const char *file, zend_ulong opline TSRMLS_DC);
PHPDBG_API void phpdbg_set_breakpoint_expression(const char* expression, size_t expression_len TSRMLS_DC);
-PHPDBG_API void phpdbg_set_breakpoint_at(const phpdbg_param_t *param, const phpdbg_input_t *input TSRMLS_DC); /* }}} */
+PHPDBG_API void phpdbg_set_breakpoint_at(const phpdbg_param_t *param TSRMLS_DC); /* }}} */
/* {{{ Breakpoint Detection API */
PHPDBG_API phpdbg_breakbase_t* phpdbg_find_breakpoint(zend_execute_data* TSRMLS_DC); /* }}} */
diff --git a/sapi/phpdbg/phpdbg_break.c b/sapi/phpdbg/phpdbg_break.c
index f56f76facd..be76b22b05 100644
--- a/sapi/phpdbg/phpdbg_break.c
+++ b/sapi/phpdbg/phpdbg_break.c
@@ -24,132 +24,32 @@
#include "phpdbg_opcode.h"
#include "phpdbg_break.h"
#include "phpdbg_bp.h"
+#include "phpdbg_prompt.h"
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
-PHPDBG_BREAK(file) /* {{{ */
-{
- switch (param->type) {
- case FILE_PARAM:
- phpdbg_set_breakpoint_file(param->file.name, param->file.line TSRMLS_CC);
- break;
-
- phpdbg_default_switch_case();
- }
-
- return SUCCESS;
-} /* }}} */
-
-PHPDBG_BREAK(method) /* {{{ */
-{
- switch (param->type) {
- case METHOD_PARAM:
- phpdbg_set_breakpoint_method(param->method.class, param->method.name TSRMLS_CC);
- break;
-
- phpdbg_default_switch_case();
- }
-
- return SUCCESS;
-} /* }}} */
-
-PHPDBG_BREAK(address) /* {{{ */
-{
- switch (param->type) {
- case ADDR_PARAM:
- phpdbg_set_breakpoint_opline(param->addr TSRMLS_CC);
- break;
-
- case NUMERIC_METHOD_PARAM:
- phpdbg_set_breakpoint_method_opline(param->method.class, param->method.name, param->num TSRMLS_CC);
- break;
-
- case NUMERIC_FUNCTION_PARAM:
- phpdbg_set_breakpoint_function_opline(param->str, param->num TSRMLS_CC);
- break;
+#define PHPDBG_BREAK_COMMAND_D(f, h, a, m, l, s) \
+ PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[10])
- case FILE_PARAM:
- phpdbg_set_breakpoint_file_opline(param->file.name, param->file.line TSRMLS_CC);
- break;
-
- phpdbg_default_switch_case();
- }
-
- return SUCCESS;
-} /* }}} */
-
-PHPDBG_BREAK(on) /* {{{ */
-{
- switch (param->type) {
- case STR_PARAM:
- phpdbg_set_breakpoint_expression(param->str, param->len TSRMLS_CC);
- break;
-
- phpdbg_default_switch_case();
- }
-
- return SUCCESS;
-} /* }}} */
+/**
+ * Commands
+ */
+const phpdbg_command_t phpdbg_break_commands[] = {
+ PHPDBG_BREAK_COMMAND_D(at, "specify breakpoint by location and condition", '@', break_at, NULL, "*c"),
+ PHPDBG_BREAK_COMMAND_D(del, "delete breakpoint by identifier number", '~', break_del, NULL, "n"),
+ PHPDBG_END_COMMAND
+};
PHPDBG_BREAK(at) /* {{{ */
{
- phpdbg_set_breakpoint_at(param, input TSRMLS_CC);
-
- return SUCCESS;
-} /* }}} */
-
-PHPDBG_BREAK(lineno) /* {{{ */
-{
- switch (param->type) {
- case NUMERIC_PARAM: {
- if (PHPDBG_G(exec)) {
- phpdbg_set_breakpoint_file(phpdbg_current_file(TSRMLS_C), param->num TSRMLS_CC);
- } else {
- phpdbg_error("Execution context not set!");
- }
- } break;
-
- phpdbg_default_switch_case();
- }
-
- return SUCCESS;
-} /* }}} */
-
-PHPDBG_BREAK(func) /* {{{ */
-{
- switch (param->type) {
- case STR_PARAM:
- phpdbg_set_breakpoint_symbol(param->str, param->len TSRMLS_CC);
- break;
-
- phpdbg_default_switch_case();
- }
-
- return SUCCESS;
-} /* }}} */
-
-PHPDBG_BREAK(op) /* {{{ */
-{
- switch (param->type) {
- case STR_PARAM:
- phpdbg_set_breakpoint_opcode(param->str, param->len TSRMLS_CC);
- break;
-
- phpdbg_default_switch_case();
- }
+ phpdbg_set_breakpoint_at(param TSRMLS_CC);
return SUCCESS;
} /* }}} */
PHPDBG_BREAK(del) /* {{{ */
{
- switch (param->type) {
- case NUMERIC_PARAM: {
- phpdbg_delete_breakpoint(param->num TSRMLS_CC);
- } break;
-
- phpdbg_default_switch_case();
- }
+ phpdbg_delete_breakpoint(param->num TSRMLS_CC);
return SUCCESS;
} /* }}} */
diff --git a/sapi/phpdbg/phpdbg_break.h b/sapi/phpdbg/phpdbg_break.h
index f90e351d6d..dc06da62b7 100644
--- a/sapi/phpdbg/phpdbg_break.h
+++ b/sapi/phpdbg/phpdbg_break.h
@@ -29,30 +29,9 @@
/**
* Printer Forward Declarations
*/
-PHPDBG_BREAK(file);
-PHPDBG_BREAK(func);
-PHPDBG_BREAK(method);
-PHPDBG_BREAK(address);
PHPDBG_BREAK(at);
-PHPDBG_BREAK(op);
-PHPDBG_BREAK(on);
-PHPDBG_BREAK(lineno);
PHPDBG_BREAK(del);
-/**
- * Commands
- */
-static const phpdbg_command_t phpdbg_break_commands[] = {
- PHPDBG_COMMAND_D_EX(file, "specify breakpoint by file:line", 'F', break_file, NULL, 1),
- PHPDBG_COMMAND_D_EX(func, "specify breakpoint by global function name", 'f', break_func, NULL, 1),
- PHPDBG_COMMAND_D_EX(method, "specify breakpoint by class::method", 'm', break_method, NULL, 1),
- PHPDBG_COMMAND_D_EX(address, "specify breakpoint by address", 'a', break_address, NULL, 1),
- PHPDBG_COMMAND_D_EX(op, "specify breakpoint by opcode", 'O', break_op, NULL, 1),
- PHPDBG_COMMAND_D_EX(on, "specify breakpoint by condition", 'o', break_on, NULL, 1),
- PHPDBG_COMMAND_D_EX(at, "specify breakpoint by location and condition", 'A', break_at, NULL, 1),
- PHPDBG_COMMAND_D_EX(lineno, "specify breakpoint by line of currently executing file", 'l', break_lineno, NULL, 1),
- PHPDBG_COMMAND_D_EX(del, "delete breakpoint by identifier number", 'd', break_del, NULL, 1),
- PHPDBG_END_COMMAND
-};
+extern const phpdbg_command_t phpdbg_break_commands[];
#endif /* PHPDBG_BREAK_H */
diff --git a/sapi/phpdbg/phpdbg_btree.c b/sapi/phpdbg/phpdbg_btree.c
new file mode 100644
index 0000000000..8fc2561e04
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_btree.c
@@ -0,0 +1,221 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | 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 |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "phpdbg_btree.h"
+#include "phpdbg.h"
+
+#define CHOOSE_BRANCH(n) \
+ branch = branch->branches[!!(n)];
+
+#ifdef _Win32
+# define emalloc malloc
+# define efree free
+#endif
+
+/* depth in bits */
+void phpdbg_btree_init(phpdbg_btree *tree, zend_ulong depth) {
+ tree->depth = depth;
+ tree->branch = NULL;
+ tree->count = 0;
+}
+
+phpdbg_btree_result *phpdbg_btree_find(phpdbg_btree *tree, zend_ulong idx) {
+ phpdbg_btree_branch *branch = tree->branch;
+ int i = tree->depth - 1;
+
+ if (branch == NULL) {
+ return NULL;
+ }
+
+ do {
+ if ((idx >> i) % 2 == 1) {
+ if (branch->branches[1]) {
+ CHOOSE_BRANCH(1);
+ } else {
+ return NULL;
+ }
+ } else {
+ if (branch->branches[0]) {
+ CHOOSE_BRANCH(0);
+ } else {
+ return NULL;
+ }
+ }
+ } while (i--);
+
+ return &branch->result;
+}
+
+phpdbg_btree_result *phpdbg_btree_find_closest(phpdbg_btree *tree, zend_ulong idx) {
+ phpdbg_btree_branch *branch = tree->branch;
+ int i = tree->depth - 1, last_superior_i = -1;
+ zend_bool had_alternative_branch = 0;
+
+ if (branch == NULL) {
+ return NULL;
+ }
+
+ /* find nearest watchpoint */
+ do {
+ /* an impossible branch was found if: */
+ if (!had_alternative_branch && (idx >> i) % 2 == 0 && !branch->branches[0]) {
+ /* there's no lower branch than idx */
+ if (last_superior_i == -1) {
+ /* failure */
+ return NULL;
+ }
+ /* reset state */
+ branch = tree->branch;
+ i = tree->depth - 1;
+ /* follow branch according to bits in idx until the last lower branch before the impossible branch */
+ do {
+ CHOOSE_BRANCH((idx >> i) % 2 == 1 && branch->branches[1]);
+ } while (--i > last_superior_i);
+ /* use now the lower branch of which we can be sure that it contains only branches lower than idx */
+ CHOOSE_BRANCH(0);
+ /* and choose the highest possible branch in the branch containing only branches lower than idx */
+ while (i--) {
+ CHOOSE_BRANCH(branch->branches[1]);
+ }
+ break;
+ }
+ /* follow branch according to bits in idx until having found an impossible branch */
+ if (had_alternative_branch || (idx >> i) % 2 == 1) {
+ if (branch->branches[1]) {
+ if (branch->branches[0]) {
+ last_superior_i = i;
+ }
+ CHOOSE_BRANCH(1);
+ } else {
+ CHOOSE_BRANCH(0);
+ had_alternative_branch = 1;
+ }
+ } else {
+ CHOOSE_BRANCH(0);
+ }
+ } while (i--);
+
+ return &branch->result;
+}
+
+phpdbg_btree_position phpdbg_btree_find_between(phpdbg_btree *tree, zend_ulong lower_idx, zend_ulong higher_idx) {
+ phpdbg_btree_position pos;
+
+ pos.tree = tree;
+ pos.end = lower_idx;
+ pos.cur = higher_idx;
+
+ return pos;
+}
+
+phpdbg_btree_result *phpdbg_btree_next(phpdbg_btree_position *pos) {
+ phpdbg_btree_result *result = phpdbg_btree_find_closest(pos->tree, pos->cur);
+
+ if (result == NULL || result->idx < pos->end) {
+ return NULL;
+ }
+
+ pos->cur = result->idx - 1;
+
+ return result;
+}
+
+int phpdbg_btree_insert_or_update(phpdbg_btree *tree, zend_ulong idx, void *ptr, int flags) {
+ int i = tree->depth - 1;
+ phpdbg_btree_branch **branch = &tree->branch;
+
+ do {
+ if (*branch == NULL) {
+ break;
+ }
+ branch = &(*branch)->branches[(idx >> i) % 2];
+ } while (i--);
+
+ if (*branch == NULL) {
+ if (!(flags & PHPDBG_BTREE_INSERT)) {
+ return FAILURE;
+ }
+
+ {
+ phpdbg_btree_branch *memory = *branch = emalloc((i + 2) * sizeof(phpdbg_btree_branch));
+ do {
+ (*branch)->branches[!((idx >> i) % 2)] = NULL;
+ branch = &(*branch)->branches[(idx >> i) % 2];
+ *branch = ++memory;
+ } while (i--);
+ tree->count++;
+ }
+ } else if (!(flags & PHPDBG_BTREE_UPDATE)) {
+ return FAILURE;
+ }
+
+ (*branch)->result.idx = idx;
+ (*branch)->result.ptr = ptr;
+
+ return SUCCESS;
+}
+
+int phpdbg_btree_delete(phpdbg_btree *tree, zend_ulong idx) {
+ int i = tree->depth;
+ phpdbg_btree_branch *branch = tree->branch;
+ int i_last_dual_branch = -1, last_dual_branch_branch;
+ phpdbg_btree_branch *last_dual_branch = NULL;
+
+ goto check_branch_existence;
+ do {
+ if (branch->branches[0] && branch->branches[1]) {
+ last_dual_branch = branch;
+ i_last_dual_branch = i;
+ last_dual_branch_branch = (idx >> i) % 2;
+ }
+ branch = branch->branches[(idx >> i) % 2];
+
+check_branch_existence:
+ if (branch == NULL) {
+ return FAILURE;
+ }
+ } while (i--);
+
+ tree->count--;
+
+ if (i_last_dual_branch == -1) {
+ efree(tree->branch);
+ tree->branch = NULL;
+ } else {
+ if (last_dual_branch->branches[last_dual_branch_branch] == last_dual_branch + 1) {
+ phpdbg_btree_branch *original_branch = last_dual_branch->branches[!last_dual_branch_branch];
+
+ memcpy(last_dual_branch + 1, last_dual_branch->branches[!last_dual_branch_branch], (i_last_dual_branch + 1) * sizeof(phpdbg_btree_branch));
+ efree(last_dual_branch->branches[!last_dual_branch_branch]);
+ last_dual_branch->branches[!last_dual_branch_branch] = last_dual_branch + 1;
+
+ branch = last_dual_branch->branches[!last_dual_branch_branch];
+ for (i = i_last_dual_branch; i--;) {
+ branch = (branch->branches[branch->branches[1] == ++original_branch] = last_dual_branch + i_last_dual_branch - i + 1);
+ }
+ } else {
+ efree(last_dual_branch->branches[last_dual_branch_branch]);
+ }
+
+ last_dual_branch->branches[last_dual_branch_branch] = NULL;
+ }
+
+ return SUCCESS;
+}
diff --git a/sapi/phpdbg/phpdbg_btree.h b/sapi/phpdbg/phpdbg_btree.h
new file mode 100644
index 0000000000..5fb217db35
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_btree.h
@@ -0,0 +1,65 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | 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 |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHPDBG_BTREE_H
+#define PHPDBG_BTREE_H
+
+#include "zend.h"
+
+typedef struct {
+ zend_ulong idx;
+ void *ptr;
+} phpdbg_btree_result;
+
+typedef union _phpdbg_btree_branch phpdbg_btree_branch;
+union _phpdbg_btree_branch {
+ phpdbg_btree_branch *branches[2];
+ phpdbg_btree_result result;
+};
+
+typedef struct {
+ zend_ulong count;
+ zend_ulong depth;
+ phpdbg_btree_branch *branch;
+} phpdbg_btree;
+
+typedef struct {
+ phpdbg_btree *tree;
+ zend_ulong cur;
+ zend_ulong end;
+} phpdbg_btree_position;
+
+void phpdbg_btree_init(phpdbg_btree *tree, zend_ulong depth);
+phpdbg_btree_result *phpdbg_btree_find(phpdbg_btree *tree, zend_ulong idx);
+phpdbg_btree_result *phpdbg_btree_find_closest(phpdbg_btree *tree, zend_ulong idx);
+phpdbg_btree_position phpdbg_btree_find_between(phpdbg_btree *tree, zend_ulong lower_idx, zend_ulong higher_idx);
+phpdbg_btree_result *phpdbg_btree_next(phpdbg_btree_position *pos);
+int phpdbg_btree_delete(phpdbg_btree *tree, zend_ulong idx);
+
+#define PHPDBG_BTREE_INSERT 1
+#define PHPDBG_BTREE_UPDATE 2
+#define PHPDBG_BTREE_OVERWRITE (PHPDBG_BTREE_INSERT | PHPDBG_BTREE_UPDATE)
+
+int phpdbg_btree_insert_or_update(phpdbg_btree *tree, zend_ulong idx, void *ptr, int flags);
+#define phpdbg_btree_insert(tree, idx, ptr) phpdbg_btree_insert_or_update(tree, idx, ptr, PHPDBG_BTREE_INSERT)
+#define phpdbg_btree_update(tree, idx, ptr) phpdbg_btree_insert_or_update(tree, idx, ptr, PHPDBG_BTREE_UPDATE)
+#define phpdbg_btree_overwrite(tree, idx, ptr) phpdbg_btree_insert_or_update(tree, idx, ptr, PHPDBG_BTREE_OWERWRITE)
+
+#endif
diff --git a/sapi/phpdbg/phpdbg_cmd.c b/sapi/phpdbg/phpdbg_cmd.c
index 1d78c53321..d4ce8ebc55 100644
--- a/sapi/phpdbg/phpdbg_cmd.c
+++ b/sapi/phpdbg/phpdbg_cmd.c
@@ -22,12 +22,32 @@
#include "phpdbg_cmd.h"
#include "phpdbg_utils.h"
#include "phpdbg_set.h"
+#include "phpdbg_prompt.h"
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+static inline const char *phpdbg_command_name(const phpdbg_command_t *command, char *buffer) {
+ size_t pos = 0;
+
+ if (command->parent) {
+ memcpy(&buffer[pos], command->parent->name, command->parent->name_len);
+ pos += command->parent->name_len;
+ memcpy(&buffer[pos], " ", sizeof(" ")-1);
+ pos += (sizeof(" ")-1);
+ }
+
+ memcpy(&buffer[pos], command->name, command->name_len);
+ pos += command->name_len;
+ buffer[pos] = 0;
+
+ return buffer;
+}
+
PHPDBG_API const char *phpdbg_get_param_type(const phpdbg_param_t *param TSRMLS_DC) /* {{{ */
{
switch (param->type) {
+ case STACK_PARAM:
+ return "stack";
case EMPTY_PARAM:
return "empty";
case ADDR_PARAM:
@@ -208,10 +228,19 @@ PHPDBG_API char* phpdbg_param_tostring(const phpdbg_param_t *param, char **point
PHPDBG_API void phpdbg_copy_param(const phpdbg_param_t* src, phpdbg_param_t* dest TSRMLS_DC) /* {{{ */
{
switch ((dest->type = src->type)) {
+ case STACK_PARAM:
+ /* nope */
+ break;
+
case STR_PARAM:
dest->str = estrndup(src->str, src->len);
dest->len = src->len;
break;
+
+ case OP_PARAM:
+ dest->str = estrndup(src->str, src->len);
+ dest->len = src->len;
+ break;
case ADDR_PARAM:
dest->addr = src->addr;
@@ -226,6 +255,7 @@ PHPDBG_API void phpdbg_copy_param(const phpdbg_param_t* src, phpdbg_param_t* des
dest->method.name = estrdup(src->method.name);
break;
+ case NUMERIC_FILE_PARAM:
case FILE_PARAM:
dest->file.name = estrdup(src->file.name);
dest->file.line = src->file.line;
@@ -246,6 +276,10 @@ PHPDBG_API void phpdbg_copy_param(const phpdbg_param_t* src, phpdbg_param_t* des
break;
case EMPTY_PARAM: { /* do nothing */ } break;
+
+ default: {
+ /* not yet */
+ }
}
} /* }}} */
@@ -254,6 +288,10 @@ PHPDBG_API zend_ulong phpdbg_hash_param(const phpdbg_param_t *param TSRMLS_DC) /
zend_ulong hash = param->type;
switch (param->type) {
+ case STACK_PARAM:
+ /* nope */
+ break;
+
case STR_PARAM:
hash += zend_inline_hash_func(param->str, param->len);
break;
@@ -291,6 +329,10 @@ PHPDBG_API zend_ulong phpdbg_hash_param(const phpdbg_param_t *param TSRMLS_DC) /
break;
case EMPTY_PARAM: { /* do nothing */ } break;
+
+ default: {
+ /* not yet */
+ }
}
return hash;
@@ -301,7 +343,11 @@ PHPDBG_API zend_bool phpdbg_match_param(const phpdbg_param_t *l, const phpdbg_pa
if (l && r) {
if (l->type == r->type) {
switch (l->type) {
-
+ case STACK_PARAM:
+ /* nope, or yep */
+ return 1;
+ break;
+
case NUMERIC_FUNCTION_PARAM:
if (l->num != r->num) {
break;
@@ -356,112 +402,400 @@ PHPDBG_API zend_bool phpdbg_match_param(const phpdbg_param_t *l, const phpdbg_pa
case EMPTY_PARAM:
return 1;
+
+ default: {
+ /* not yet */
+ }
}
}
}
return 0;
} /* }}} */
-PHPDBG_API phpdbg_input_t **phpdbg_read_argv(char *buffer, int *argc TSRMLS_DC) /* {{{ */
-{
- char *p;
- char b[PHPDBG_MAX_CMD];
- int l=0;
- enum states {
- IN_BETWEEN,
- IN_WORD,
- IN_STRING
- } state = IN_BETWEEN;
- phpdbg_input_t **argv = NULL;
-
- argv = (phpdbg_input_t**) emalloc(sizeof(phpdbg_input_t*));
- (*argc) = 0;
-
-#define RESET_STATE() do { \
- phpdbg_input_t *arg = emalloc(sizeof(phpdbg_input_t)); \
- if (arg) { \
- b[l]=0; \
- arg->length = l; \
- arg->string = estrndup(b, arg->length); \
- arg->argv = NULL; \
- arg->argc = 0; \
- argv = (phpdbg_input_t**) erealloc(argv, sizeof(phpdbg_input_t*) * ((*argc)+1)); \
- argv[(*argc)++] = arg; \
- l = 0; \
- } \
- state = IN_BETWEEN; \
-} while (0)
-
- for (p = buffer; *p != '\0'; p++) {
- int c = (unsigned char) *p;
- switch (state) {
- case IN_BETWEEN:
- if (isspace(c)) {
- continue;
- }
- if (c == '"') {
- state = IN_STRING;
- continue;
- }
- state = IN_WORD;
- b[l++]=c;
- continue;
-
- case IN_STRING:
- if (c == '"') {
- if (buffer[(p - buffer)-1] == '\\') {
- b[l-1]=c;
- continue;
- }
- RESET_STATE();
- } else {
- b[l++]=c;
- }
- continue;
+/* {{{ */
+PHPDBG_API void phpdbg_param_debug(const phpdbg_param_t *param, const char *msg) {
+ if (param && param->type) {
+ switch (param->type) {
+ case STR_PARAM:
+ fprintf(stderr, "%s STR_PARAM(%s=%lu)\n", msg, param->str, param->len);
+ break;
+
+ case ADDR_PARAM:
+ fprintf(stderr, "%s ADDR_PARAM(%lu)\n", msg, param->addr);
+ break;
+
+ case NUMERIC_FILE_PARAM:
+ fprintf(stderr, "%s NUMERIC_FILE_PARAM(%s:#%lu)\n", msg, param->file.name, param->file.line);
+ break;
+
+ case FILE_PARAM:
+ fprintf(stderr, "%s FILE_PARAM(%s:%lu)\n", msg, param->file.name, param->file.line);
+ break;
+
+ case METHOD_PARAM:
+ fprintf(stderr, "%s METHOD_PARAM(%s::%s)\n", msg, param->method.class, param->method.name);
+ break;
+
+ case NUMERIC_METHOD_PARAM:
+ fprintf(stderr, "%s NUMERIC_METHOD_PARAM(%s::%s)\n", msg, param->method.class, param->method.name);
+ break;
+
+ case NUMERIC_FUNCTION_PARAM:
+ fprintf(stderr, "%s NUMERIC_FUNCTION_PARAM(%s::%ld)\n", msg, param->str, param->num);
+ break;
+
+ case NUMERIC_PARAM:
+ fprintf(stderr, "%s NUMERIC_PARAM(%ld)\n", msg, param->num);
+ break;
+
+ case COND_PARAM:
+ fprintf(stderr, "%s COND_PARAM(%s=%lu)\n", msg, param->str, param->len);
+ break;
+
+ case OP_PARAM:
+ fprintf(stderr, "%s OP_PARAM(%s=%lu)\n", msg, param->str, param->len);
+ break;
+
+ default: {
+ /* not yet */
+ }
+ }
+ }
+} /* }}} */
- case IN_WORD:
- if (isspace(c)) {
- RESET_STATE();
- } else {
- b[l++]=c;
+/* {{{ */
+PHPDBG_API void phpdbg_stack_free(phpdbg_param_t *stack) {
+ if (stack && stack->next) {
+ phpdbg_param_t *remove = stack->next;
+
+ while (remove) {
+ phpdbg_param_t *next = NULL;
+
+ if (remove->next)
+ next = remove->next;
+
+ switch (remove->type) {
+ case NUMERIC_METHOD_PARAM:
+ case METHOD_PARAM:
+ if (remove->method.class)
+ free(remove->method.class);
+ if (remove->method.name)
+ free(remove->method.name);
+ break;
+
+ case NUMERIC_FUNCTION_PARAM:
+ case STR_PARAM:
+ case OP_PARAM:
+ if (remove->str)
+ free(remove->str);
+ break;
+
+ case NUMERIC_FILE_PARAM:
+ case FILE_PARAM:
+ if (remove->file.name)
+ free(remove->file.name);
+ break;
+
+ default: {
+ /* nothing */
}
- continue;
+ }
+
+ free(remove);
+ remove = NULL;
+
+ if (next)
+ remove = next;
+ else break;
}
}
+
+ stack->next = NULL;
+} /* }}} */
- switch (state) {
- case IN_WORD: {
- RESET_STATE();
- } break;
+/* {{{ */
+PHPDBG_API void phpdbg_stack_push(phpdbg_param_t *stack, phpdbg_param_t *param) {
+ phpdbg_param_t *next = calloc(1, sizeof(phpdbg_param_t));
- case IN_STRING:
- phpdbg_error(
- "Malformed command line (unclosed quote) @ %ld: %s!",
- (p - buffer)-1, &buffer[(p - buffer)-1]);
- break;
+ if (!next)
+ return;
- case IN_BETWEEN:
- break;
+ *(next) = *(param);
+
+ next->next = NULL;
+
+ if (stack->top == NULL) {
+ stack->top = next;
+ next->top = NULL;
+ stack->next = next;
+ } else {
+ stack->top->next = next;
+ next->top = stack->top;
+ stack->top = next;
}
- if ((*argc) == 0) {
- /* not needed */
- efree(argv);
+ stack->len++;
+} /* }}} */
- /* to be sure */
- return NULL;
+PHPDBG_API int phpdbg_stack_verify(const phpdbg_command_t *command, phpdbg_param_t **stack, char **why TSRMLS_DC) {
+ if (command) {
+ char buffer[128] = {0,};
+ const phpdbg_param_t *top = (stack != NULL) ? *stack : NULL;
+ const char *arg = command->args;
+ size_t least = 0L,
+ received = 0L,
+ current = 0L;
+ zend_bool optional = 0;
+
+ /* check for arg spec */
+ if (!(arg) || !(*arg)) {
+ if (!top) {
+ return SUCCESS;
+ }
+
+ asprintf(why,
+ "The command \"%s\" expected no arguments",
+ phpdbg_command_name(command, buffer));
+ return FAILURE;
+ }
+
+ least = 0L;
+
+ /* count least amount of arguments */
+ while (arg && *arg) {
+ if (arg[0] == '|') {
+ break;
+ }
+ least++;
+ arg++;
+ }
+
+ arg = command->args;
+
+#define verify_arg(e, a, t) if (!(a)) { \
+ if (!optional) { \
+ asprintf(why, \
+ "The command \"%s\" expected %s and got nothing at parameter %lu", \
+ phpdbg_command_name(command, buffer), \
+ (e), \
+ current); \
+ return FAILURE;\
+ } \
+} else if ((a)->type != (t)) { \
+ asprintf(why, \
+ "The command \"%s\" expected %s and got %s at parameter %lu", \
+ phpdbg_command_name(command, buffer), \
+ (e),\
+ phpdbg_get_param_type((a) TSRMLS_CC), \
+ current); \
+ return FAILURE; \
+}
+
+ while (arg && *arg) {
+ current++;
+
+ switch (*arg) {
+ case '|': {
+ current--;
+ optional = 1;
+ arg++;
+ } continue;
+
+ case 'i': verify_arg("raw input", top, STR_PARAM); break;
+ case 's': verify_arg("string", top, STR_PARAM); break;
+ case 'n': verify_arg("number", top, NUMERIC_PARAM); break;
+ case 'm': verify_arg("method", top, METHOD_PARAM); break;
+ case 'a': verify_arg("address", top, ADDR_PARAM); break;
+ case 'f': verify_arg("file:line", top, FILE_PARAM); break;
+ case 'c': verify_arg("condition", top, COND_PARAM); break;
+ case 'o': verify_arg("opcode", top, OP_PARAM); break;
+ case 'b': verify_arg("boolean", top, NUMERIC_PARAM); break;
+
+ case '*': { /* do nothing */ } break;
+ }
+
+ if (top ) {
+ top = top->next;
+ } else break;
+
+ received++;
+ arg++;
+ }
+
+#undef verify_arg
+
+ if ((received < least)) {
+ asprintf(why,
+ "The command \"%s\" expected at least %lu arguments (%s) and received %lu",
+ phpdbg_command_name(command, buffer),
+ least,
+ command->args,
+ received);
+ return FAILURE;
+ }
}
+
+ return SUCCESS;
+}
+
+/* {{{ */
+PHPDBG_API const phpdbg_command_t* phpdbg_stack_resolve(const phpdbg_command_t *commands, const phpdbg_command_t *parent, phpdbg_param_t **top, char **why) {
+ const phpdbg_command_t *command = commands;
+ phpdbg_param_t *name = *top;
+ const phpdbg_command_t *matched[3] = {NULL, NULL, NULL};
+ ulong matches = 0L;
+
+ while (command && command->name && command->handler) {
+ if ((name->len == 1) || (command->name_len >= name->len)) {
+ /* match single letter alias */
+ if (command->alias && (name->len == 1)) {
+ if (command->alias == (*name->str)) {
+ matched[matches] = command;
+ matches++;
+ }
+ } else {
- return argv;
+ /* match full, case insensitive, command name */
+ if (strncasecmp(command->name, name->str, name->len) == SUCCESS) {
+ if (matches < 3) {
+
+ /* only allow abbreviating commands that can be aliased */
+ if (((name->len != command->name_len) && command->alias) ||
+ (name->len == command->name_len)) {
+ matched[matches] = command;
+ matches++;
+ }
+
+
+ /* exact match */
+ if (name->len == command->name_len)
+ break;
+ } else break;
+ }
+ }
+ }
+
+ command++;
+ }
+
+ switch (matches) {
+ case 0: {
+ if (parent) {
+ asprintf(
+ why,
+ "The command \"%s %s\" could not be found",
+ parent->name, name->str);
+ } else asprintf(
+ why,
+ "The command \"%s\" could not be found",
+ name->str);
+ } return parent;
+
+ case 1: {
+ (*top) = (*top)->next;
+
+ command = matched[0];
+ } break;
+
+ default: {
+ char *list = NULL;
+ zend_uint it = 0;
+ size_t pos = 0;
+
+ while (it < matches) {
+ if (!list) {
+ list = malloc(
+ matched[it]->name_len + 1 +
+ ((it+1) < matches ? sizeof(", ")-1 : 0));
+ } else {
+ list = realloc(list,
+ (pos + matched[it]->name_len) + 1 +
+ ((it+1) < matches ? sizeof(", ")-1 : 0));
+ }
+ memcpy(&list[pos], matched[it]->name, matched[it]->name_len);
+ pos += matched[it]->name_len;
+ if ((it+1) < matches) {
+ memcpy(&list[pos], ", ", sizeof(", ")-1);
+ pos += (sizeof(", ") - 1);
+ }
+
+ list[pos] = 0;
+ it++;
+ }
+
+ asprintf(
+ why,
+ "The command \"%s\" is ambigious, matching %lu commands (%s)",
+ name->str, matches, list);
+ free(list);
+ } return NULL;
+ }
+
+ if (command->subs && (*top) && ((*top)->type == STR_PARAM)) {
+ return phpdbg_stack_resolve(command->subs, command, top, why);
+ } else {
+ return command;
+ }
+
+ return NULL;
} /* }}} */
-PHPDBG_API phpdbg_input_t *phpdbg_read_input(char *buffered TSRMLS_DC) /* {{{ */
+/* {{{ */
+PHPDBG_API int phpdbg_stack_execute(phpdbg_param_t *stack, char **why TSRMLS_DC) {
+ phpdbg_param_t *top = NULL;
+ const phpdbg_command_t *handler = NULL;
+
+ if (stack->type != STACK_PARAM) {
+ asprintf(
+ why, "The passed argument was not a stack !!");
+ return FAILURE;
+ }
+
+ if (!stack->len) {
+ asprintf(
+ why, "The stack contains nothing !!");
+ return FAILURE;
+ }
+
+ top = (phpdbg_param_t*) stack->next;
+
+ switch (top->type) {
+ case EVAL_PARAM:
+ return PHPDBG_COMMAND_HANDLER(ev)(top TSRMLS_CC);
+
+ case RUN_PARAM:
+ return PHPDBG_COMMAND_HANDLER(run)(top TSRMLS_CC);
+
+ case SHELL_PARAM:
+ return PHPDBG_COMMAND_HANDLER(sh)(top TSRMLS_CC);
+
+ case STR_PARAM: {
+ handler = phpdbg_stack_resolve(
+ phpdbg_prompt_commands, NULL, &top, why);
+
+ if (handler) {
+ if (phpdbg_stack_verify(handler, &top, why TSRMLS_CC) == SUCCESS) {
+ return handler->handler(top TSRMLS_CC);
+ }
+ }
+ } return FAILURE;
+
+ default:
+ asprintf(
+ why, "The first parameter makes no sense !!");
+ return FAILURE;
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_API char* phpdbg_read_input(char *buffered TSRMLS_DC) /* {{{ */
{
- phpdbg_input_t *buffer = NULL;
char *cmd = NULL;
#ifndef HAVE_LIBREADLINE
char buf[PHPDBG_MAX_CMD];
#endif
+ char *buffer = NULL;
if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
if ((PHPDBG_G(flags) & PHPDBG_IS_REMOTE) &&
@@ -513,32 +847,8 @@ readline:
}
#endif
} else cmd = buffered;
-
- /* allocate and sanitize buffer */
- buffer = (phpdbg_input_t*) ecalloc(1, sizeof(phpdbg_input_t));
- if (!buffer) {
- return NULL;
- }
-
- buffer->string = phpdbg_trim(cmd, strlen(cmd), &buffer->length);
-
- /* store constant pointer to start of buffer */
- buffer->start = (char* const*) buffer->string;
-
- buffer->argv = phpdbg_read_argv(
- buffer->string, &buffer->argc TSRMLS_CC);
-
-#ifdef PHPDBG_DEBUG
- if (buffer->argc) {
- int arg = 0;
-
- while (arg < buffer->argc) {
- phpdbg_debug(
- "argv %d=%s", arg, buffer->argv[arg]->string);
- arg++;
- }
- }
-#endif
+
+ buffer = estrdup(cmd);
#ifdef HAVE_LIBREADLINE
if (!buffered && cmd &&
@@ -546,144 +856,34 @@ readline:
free(cmd);
}
#endif
-
- return buffer;
}
- return NULL;
-} /* }}} */
+ if (buffer && isspace(*buffer)) {
+ char *trimmed = buffer;
+ while (isspace(*trimmed))
+ trimmed++;
-PHPDBG_API void phpdbg_destroy_argv(phpdbg_input_t **argv, int argc TSRMLS_DC) /* {{{ */
-{
- if (argv) {
- if (argc) {
- int arg;
- for (arg=0; arg<argc; arg++) {
- phpdbg_destroy_input(
- &argv[arg] TSRMLS_CC);
- }
- }
- efree(argv);
+ trimmed = estrdup(trimmed);
+ efree(buffer);
+ buffer = trimmed;
}
-} /* }}} */
-
-PHPDBG_API void phpdbg_destroy_input(phpdbg_input_t **input TSRMLS_DC) /*{{{ */
-{
- if (*input) {
- if ((*input)->string) {
- efree((*input)->string);
+ if (buffer && strlen(buffer)) {
+ if (PHPDBG_G(buffer)) {
+ efree(PHPDBG_G(buffer));
+ }
+ PHPDBG_G(buffer) = estrdup(buffer);
+ } else {
+ if (PHPDBG_G(buffer)) {
+ buffer = estrdup(PHPDBG_G(buffer));
}
-
- phpdbg_destroy_argv(
- (*input)->argv, (*input)->argc TSRMLS_CC);
-
- efree(*input);
}
+
+ return buffer;
} /* }}} */
-PHPDBG_API int phpdbg_do_cmd(const phpdbg_command_t *command, phpdbg_input_t *input TSRMLS_DC) /* {{{ */
+PHPDBG_API void phpdbg_destroy_input(char **input TSRMLS_DC) /*{{{ */
{
- int rc = FAILURE;
-
- if (input->argc > 0) {
- while (command && command->name && command->handler) {
- if (((command->name_len == input->argv[0]->length) &&
- (memcmp(command->name, input->argv[0]->string, command->name_len) == SUCCESS)) ||
- (command->alias &&
- (input->argv[0]->length == 1) &&
- (command->alias == *input->argv[0]->string))) {
-
- phpdbg_param_t param;
- phpdbg_command_t *initial_last_cmd;
- phpdbg_param_t initial_last_param;
-
- param.type = EMPTY_PARAM;
-
- if (input->argc > 1) {
- if (command->subs) {
- phpdbg_input_t sub = *input;
-
- sub.string += input->argv[0]->length;
- sub.length -= input->argv[0]->length;
-
- sub.string = phpdbg_trim(
- sub.string, sub.length, &sub.length);
-
- sub.argc--;
- sub.argv++;
-
- phpdbg_debug(
- "trying sub commands in \"%s\" for \"%s\" with %d arguments",
- command->name, sub.argv[0]->string, sub.argc-1);
-
- if (phpdbg_do_cmd(command->subs, &sub TSRMLS_CC) == SUCCESS) {
- efree(sub.string);
- return SUCCESS;
- }
-
- efree(sub.string);
- }
-
- /* no sub command found */
- {
- char *store = input->string;
-
- input->string += input->argv[0]->length;
- input->length -= input->argv[0]->length;
-
- input->string = phpdbg_trim(
- input->string, input->length, &input->length);
-
- efree(store);
- }
-
- /* pass parameter on */
- phpdbg_parse_param(
- input->string,
- input->length,
- &param TSRMLS_CC);
- }
-
- phpdbg_debug(
- "found command %s for %s with %d arguments",
- command->name, input->argv[0]->string, input->argc-1);
- {
- int arg;
- for (arg=1; arg<input->argc; arg++) {
- phpdbg_debug(
- "\t#%d: [%s=%zu]",
- arg,
- input->argv[arg]->string,
- input->argv[arg]->length);
- }
- }
-
- initial_last_param = PHPDBG_G(lparam);
- initial_last_cmd = (phpdbg_command_t *)PHPDBG_G(lcmd);
- PHPDBG_G(lparam) = param;
- PHPDBG_G(lcmd) = (phpdbg_command_t *)command;
-
- rc = command->handler(&param, input TSRMLS_CC);
-
- /* only set last command when it is worth it! */
- if (rc != FAILURE && !(PHPDBG_G(flags) & PHPDBG_IS_INITIALIZING)) {
- phpdbg_clear_param(&initial_last_param TSRMLS_CC);
- } else if (PHPDBG_G(lcmd) == command && !memcmp(&PHPDBG_G(lparam),& initial_last_param, sizeof(phpdbg_param_t))) {
- PHPDBG_G(lparam) = initial_last_param;
- PHPDBG_G(lcmd) = initial_last_cmd;
- phpdbg_clear_param(&param TSRMLS_CC);
- }
- break;
- }
- command++;
- }
- } else {
- /* this should NEVER happen */
- phpdbg_error(
- "No function executed!!");
- }
-
- return rc;
+ efree(*input);
} /* }}} */
diff --git a/sapi/phpdbg/phpdbg_cmd.h b/sapi/phpdbg/phpdbg_cmd.h
index c86f92bb47..571d065f59 100644
--- a/sapi/phpdbg/phpdbg_cmd.h
+++ b/sapi/phpdbg/phpdbg_cmd.h
@@ -23,8 +23,6 @@
#include "TSRM.h"
-typedef struct _phpdbg_command_t phpdbg_command_t;
-
/* {{{ Command and Parameter */
enum {
NO_ARG = 0,
@@ -36,24 +34,23 @@ typedef enum {
EMPTY_PARAM = 0,
ADDR_PARAM,
FILE_PARAM,
+ NUMERIC_FILE_PARAM,
METHOD_PARAM,
STR_PARAM,
NUMERIC_PARAM,
NUMERIC_FUNCTION_PARAM,
- NUMERIC_METHOD_PARAM
+ NUMERIC_METHOD_PARAM,
+ STACK_PARAM,
+ EVAL_PARAM,
+ SHELL_PARAM,
+ COND_PARAM,
+ OP_PARAM,
+ ORIG_PARAM,
+ RUN_PARAM
} phpdbg_param_type;
-typedef struct _phpdbg_input_t phpdbg_input_t;
-
-struct _phpdbg_input_t {
- char * const *start;
- char *string;
- size_t length;
- phpdbg_input_t **argv;
- int argc;
-};
-
-typedef struct _phpdbg_param {
+typedef struct _phpdbg_param phpdbg_param_t;
+struct _phpdbg_param {
phpdbg_param_type type;
long num;
zend_ulong addr;
@@ -67,10 +64,31 @@ typedef struct _phpdbg_param {
} method;
char *str;
size_t len;
-} phpdbg_param_t;
+ phpdbg_param_t *next;
+ phpdbg_param_t *top;
+};
-typedef int (*phpdbg_command_handler_t)(const phpdbg_param_t*, const phpdbg_input_t* TSRMLS_DC);
+#define phpdbg_init_param(v, t) do{ \
+ (v)->type = (t); \
+ (v)->addr = 0; \
+ (v)->num = 0; \
+ (v)->file.name = NULL; \
+ (v)->file.line = 0; \
+ (v)->method.class = NULL; \
+ (v)->method.name = NULL; \
+ (v)->str = NULL; \
+ (v)->len = 0; \
+ (v)->next = NULL; \
+ (v)->top = NULL; \
+} while(0)
+
+#ifndef YYSTYPE
+#define YYSTYPE phpdbg_param_t
+#endif
+
+typedef int (*phpdbg_command_handler_t)(const phpdbg_param_t* TSRMLS_DC);
+typedef struct _phpdbg_command_t phpdbg_command_t;
struct _phpdbg_command_t {
const char *name; /* Command name */
size_t name_len; /* Command name length */
@@ -79,7 +97,8 @@ struct _phpdbg_command_t {
char alias; /* Alias */
phpdbg_command_handler_t handler; /* Command handler */
const phpdbg_command_t *subs; /* Sub Commands */
- char arg_type; /* Accept args? */
+ char *args; /* Argument Spec */
+ const phpdbg_command_t *parent; /* Parent Command */
};
/* }}} */
@@ -95,35 +114,29 @@ typedef struct {
} phpdbg_frame_t;
/* }}} */
-
-
/*
* Workflow:
-* 1) read input
-* input takes the line from console, creates argc/argv
-* 2) parse parameters into suitable types based on arg_type
-* takes input from 1) and arg_type and creates parameters
-* 3) do command
-* executes commands
-* 4) destroy parameters
-* cleans up what was allocated by creation of parameters
-* 5) destroy input
-* cleans up what was allocated by creation of input
+* 1) the lexer/parser creates a stack of commands and arguments from input
+* 2) the commands at the top of the stack are resolved sensibly using aliases, abbreviations and case insensitive matching
+* 3) the remaining arguments in the stack are verified (optionally) against the handlers declared argument specification
+* 4) the handler is called passing the top of the stack as the only parameter
+* 5) the stack is destroyed upon return from the handler
*/
/*
* Input Management
*/
-PHPDBG_API phpdbg_input_t* phpdbg_read_input(char *buffered TSRMLS_DC);
-PHPDBG_API void phpdbg_destroy_input(phpdbg_input_t** TSRMLS_DC);
+PHPDBG_API char* phpdbg_read_input(char *buffered TSRMLS_DC);
+PHPDBG_API void phpdbg_destroy_input(char** TSRMLS_DC);
-/*
-* Argument Management
-*/
-PHPDBG_API phpdbg_input_t** phpdbg_read_argv(char *buffer, int *argc TSRMLS_DC);
-PHPDBG_API void phpdbg_destroy_argv(phpdbg_input_t **argv, int argc TSRMLS_DC);
-#define phpdbg_argv_is(n, s) \
- (memcmp(input->argv[n]->string, s, input->argv[n]->length) == SUCCESS)
+/**
+ * Stack Management
+ */
+PHPDBG_API void phpdbg_stack_push(phpdbg_param_t *stack, phpdbg_param_t *param);
+PHPDBG_API const phpdbg_command_t* phpdbg_stack_resolve(const phpdbg_command_t *commands, const phpdbg_command_t *parent, phpdbg_param_t **top, char **why);
+PHPDBG_API int phpdbg_stack_verify(const phpdbg_command_t *command, phpdbg_param_t **stack, char **why TSRMLS_DC);
+PHPDBG_API int phpdbg_stack_execute(phpdbg_param_t *stack, char **why TSRMLS_DC);
+PHPDBG_API void phpdbg_stack_free(phpdbg_param_t *stack);
/*
* Parameter Management
@@ -135,28 +148,27 @@ PHPDBG_API zend_bool phpdbg_match_param(const phpdbg_param_t *, const phpdbg_par
PHPDBG_API zend_ulong phpdbg_hash_param(const phpdbg_param_t * TSRMLS_DC);
PHPDBG_API const char* phpdbg_get_param_type(const phpdbg_param_t* TSRMLS_DC);
PHPDBG_API char* phpdbg_param_tostring(const phpdbg_param_t *param, char **pointer TSRMLS_DC);
-
-/*
-* Command Executor
-*/
-PHPDBG_API int phpdbg_do_cmd(const phpdbg_command_t*, phpdbg_input_t* TSRMLS_DC);
+PHPDBG_API void phpdbg_param_debug(const phpdbg_param_t *param, const char *msg);
/**
* Command Declarators
*/
#define PHPDBG_COMMAND_HANDLER(name) phpdbg_do_##name
-#define PHPDBG_COMMAND_D_EX(name, tip, alias, handler, children, has_args) \
- {PHPDBG_STRL(#name), tip, sizeof(tip)-1, alias, phpdbg_do_##handler, children, has_args}
+#define PHPDBG_COMMAND_D_EXP(name, tip, alias, handler, children, args, parent) \
+ {PHPDBG_STRL(#name), tip, sizeof(tip)-1, alias, phpdbg_do_##handler, children, args, parent}
+
+#define PHPDBG_COMMAND_D_EX(name, tip, alias, handler, children, args) \
+ {PHPDBG_STRL(#name), tip, sizeof(tip)-1, alias, phpdbg_do_##handler, children, args, NULL}
-#define PHPDBG_COMMAND_D(name, tip, alias, children, has_args) \
- {PHPDBG_STRL(#name), tip, sizeof(tip)-1, alias, phpdbg_do_##name, children, has_args}
+#define PHPDBG_COMMAND_D(name, tip, alias, children, args) \
+ {PHPDBG_STRL(#name), tip, sizeof(tip)-1, alias, phpdbg_do_##name, children, args, NULL}
-#define PHPDBG_COMMAND(name) int phpdbg_do_##name(const phpdbg_param_t *param, const phpdbg_input_t *input TSRMLS_DC)
+#define PHPDBG_COMMAND(name) int phpdbg_do_##name(const phpdbg_param_t *param TSRMLS_DC)
-#define PHPDBG_COMMAND_ARGS param, input TSRMLS_CC
+#define PHPDBG_COMMAND_ARGS param TSRMLS_CC
-#define PHPDBG_END_COMMAND {NULL, 0, NULL, 0, '\0', NULL, NULL, '\0'}
+#define PHPDBG_END_COMMAND {NULL, 0, NULL, 0, '\0', NULL, NULL, '\0', NULL}
/*
* Default Switch Case
diff --git a/sapi/phpdbg/phpdbg_frame.c b/sapi/phpdbg/phpdbg_frame.c
index de02addc1b..a235fe8cb0 100644
--- a/sapi/phpdbg/phpdbg_frame.c
+++ b/sapi/phpdbg/phpdbg_frame.c
@@ -167,7 +167,7 @@ void phpdbg_dump_backtrace(size_t num TSRMLS_DC) /* {{{ */
zval **tmp;
zval **file, **line;
HashPosition position;
- int i = 1, limit = num;
+ int i = 0, limit = num;
int user_defined;
if (limit < 0) {
@@ -186,7 +186,7 @@ void phpdbg_dump_backtrace(size_t num TSRMLS_DC) /* {{{ */
if (zend_hash_get_current_data_ex(Z_ARRVAL(zbacktrace),
(void**)&tmp, &position) == FAILURE) {
- phpdbg_write("frame #0: {main} at %s:%ld", Z_STRVAL_PP(file), Z_LVAL_PP(line));
+ phpdbg_write("frame #%d: {main} at %s:%ld", i, Z_STRVAL_PP(file), Z_LVAL_PP(line));
break;
}
diff --git a/sapi/phpdbg/phpdbg_help.c b/sapi/phpdbg/phpdbg_help.c
index d2fea8d7c7..f2d074ded6 100644
--- a/sapi/phpdbg/phpdbg_help.c
+++ b/sapi/phpdbg/phpdbg_help.c
@@ -15,589 +15,916 @@
| Authors: Felipe Pena <felipe@php.net> |
| Authors: Joe Watkins <joe.watkins@live.co.uk> |
| Authors: Bob Weinand <bwoebi@php.net> |
+ | Authors: Terry Ellison <terry@ellisons.org.uk> |
+----------------------------------------------------------------------+
*/
#include "phpdbg.h"
#include "phpdbg_help.h"
-#include "phpdbg_print.h"
-#include "phpdbg_utils.h"
-#include "phpdbg_break.h"
-#include "phpdbg_list.h"
-#include "phpdbg_info.h"
-#include "phpdbg_set.h"
+#include "phpdbg_prompt.h"
+#include "zend.h"
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
-PHPDBG_HELP(exec) /* {{{ */
+/* {{{ Commands Table */
+#define PHPDBG_COMMAND_HELP_D(name, tip, alias, action) \
+ {PHPDBG_STRL(#name), tip, sizeof(tip)-1, alias, action, NULL, 0}
+
+const phpdbg_command_t phpdbg_help_commands[] = {
+ PHPDBG_COMMAND_HELP_D(aliases, "show alias list", 'a', phpdbg_do_help_aliases),
+ PHPDBG_COMMAND_HELP_D(options, "command line options", 0, NULL),
+ PHPDBG_COMMAND_HELP_D(overview, "help overview", 0, NULL),
+ PHPDBG_COMMAND_HELP_D(phpdbginit, "phpdbginit file format", 0, NULL),
+ PHPDBG_COMMAND_HELP_D(syntax, "syntax overview", 0, NULL),
+ PHPDBG_END_COMMAND
+}; /* }}} */
+
+/* {{{ pretty_print. Formatting escapes and wrapping text in a string before printing it. */
+void pretty_print(char *text TSRMLS_DC)
{
- phpdbg_help_header();
- phpdbg_writeln("\tWill attempt execution, if compilation has not yet taken place, it occurs now");
- phpdbg_writeln("The execution context must be set before execution can take place");
- phpdbg_help_footer();
- return SUCCESS;
-} /* }}} */
+ char *new, *p, *q;
+
+ const char *prompt_escape = phpdbg_get_prompt(TSRMLS_C);
+ unsigned int prompt_escape_len = strlen(prompt_escape);
+ unsigned int prompt_len = strlen(PHPDBG_G(prompt)[0]);
+
+ const char *bold_on_escape = PHPDBG_G(flags) & PHPDBG_IS_COLOURED ? "\033[1m" : "";
+ const char *bold_off_escape = PHPDBG_G(flags) & PHPDBG_IS_COLOURED ? "\033[0m" : "";
+ unsigned int bold_escape_len = strlen(bold_on_escape);
+
+ unsigned int term_width = phpdbg_get_terminal_width(TSRMLS_C);
+ unsigned int size = 0;
+
+ int in_bold = 0;
+
+ char *last_new_blank = NULL; /* position in new buffer of last blank char */
+ unsigned int last_blank_count = 0; /* printable char offset of last blank char */
+ unsigned int line_count = 0; /* number printable chars on current line */
+
+ /* First pass calculates a safe size for the pretty print version */
+ for (p = text; *p; p++) {
+ if (UNEXPECTED(p[0] == '*') && p[1] == '*') {
+ size += bold_escape_len - 2;
+ p++;
+ } else if (UNEXPECTED(p[0] == '$') && p[1] == 'P') {
+ size += prompt_escape_len - 2;
+ p++;
+ } else if (UNEXPECTED(p[0] == '\\')) {
+ p++;
+ }
+ }
+ size += (p-text)+1;
+
+ new = emalloc(size);
+ /*
+ * Second pass substitutes the bold and prompt escape sequences and line wrap
+ *
+ * ** toggles bold on and off if PHPDBG_IS_COLOURED flag is set
+ * $P substitutes the prompt sequence
+ * Lines are wrapped by replacing the last blank with a CR before <term width>
+ * characters. (This defaults to 100 if the width can't be detected). In the
+ * pathelogical case where no blanks are found, then the wrap occurs at the
+ * first blank.
+ */
+ for (p = text, q = new; *p; p++) {
+ if (UNEXPECTED(*p == ' ')) {
+ last_new_blank = q;
+ last_blank_count = line_count++;
+ *q++ = ' ';
+ } else if (UNEXPECTED(*p == '\n')) {
+ last_new_blank = NULL;
+ *q++ = *p;
+ last_blank_count = 0;
+ line_count = 0;
+ } else if (UNEXPECTED(p[0] == '*') && p[1] == '*') {
+ if (bold_escape_len) {
+ in_bold = !in_bold;
+ memcpy (q, in_bold ? bold_on_escape : bold_off_escape, bold_escape_len);
+ q += bold_escape_len;
+ /* bold on/off has zero print width so line count is unchanged */
+ }
+ p++;
+ } else if (UNEXPECTED(p[0] == '$') && p[1] == 'P') {
+ memcpy (q, prompt_escape, prompt_escape_len);
+ q += prompt_escape_len;
+ line_count += prompt_len;
+ p++;
+ } else if (UNEXPECTED(p[0] == '\\')) {
+ p++;
+ *q++ = *p;
+ line_count++;
+ } else {
+ *q++ = *p;
+ line_count++;
+ }
-PHPDBG_HELP(step) /* {{{ */
-{
- phpdbg_help_header();
- phpdbg_writeln("You can enable and disable stepping at any phpdbg prompt during execution");
- phpdbg_writeln(EMPTY);
- phpdbg_notice("Examples");
- phpdbg_writeln("\t%sstepping 1", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%ss 1", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill enable stepping");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("While stepping is enabled you are presented with a prompt after the execution of each opcode");
- phpdbg_help_footer();
- return SUCCESS;
-} /* }}} */
+ if (UNEXPECTED(line_count>=term_width) && last_new_blank) {
+ *last_new_blank = '\n';
+ last_new_blank = NULL;
+ line_count -= last_blank_count;
+ last_blank_count = 0;
+ }
+ }
+ *q++ = '\0';
-PHPDBG_HELP(next) /* {{{ */
-{
- phpdbg_help_header();
- phpdbg_write("Step back into the vm and execute the next opcode");
- phpdbg_writeln(EMPTY);
- phpdbg_notice("Examples");
- phpdbg_writeln("\t%snext", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sn", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill cause control to be passed back to the vm, continuing execution");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("Note: is only useful while executing");
- phpdbg_help_footer();
- return SUCCESS;
-} /* }}} */
+ if ((q-new)>size) {
+ phpdbg_error("Output overrun of %lu bytes", ((q-new) - size));
+ }
-PHPDBG_HELP(until) /* {{{ */
-{
- phpdbg_help_header();
- phpdbg_writeln("Step back into the vm, skipping breakpoints until the next source line");
- phpdbg_writeln(EMPTY);
- phpdbg_notice("Examples");
- phpdbg_writeln("\t%suntil", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%su", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill cause control to be passed back to the vm, continuing execution");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("Note: is only useful while executing");
- phpdbg_help_footer();
- return SUCCESS;
-} /* }}} */
+ phpdbg_write("%s\n", new);
+ efree(new);
+} /* }}} */
-PHPDBG_HELP(finish) /* {{{ */
+/* {{{ summary_print. Print a summary line giving, the command, its alias and tip */
+void summary_print(phpdbg_command_t const * const cmd TSRMLS_DC)
{
- phpdbg_help_header();
- phpdbg_writeln("Step back into the vm, skipping breakpoints until past the end of the current stack");
- phpdbg_writeln(EMPTY);
- phpdbg_notice("Examples");
- phpdbg_writeln("\t%sfinish", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sF", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill cause control to be passed back to the vm, continuing execution");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("Note: this allows all breakpoints that would otherwise break execution in the current scope to be skipped");
- phpdbg_help_footer();
- return SUCCESS;
-} /* }}} */
-
-PHPDBG_HELP(leave) /* {{{ */
+ char *summary;
+ spprintf(&summary, 0, "Command: **%s** Alias: **%c** **%s**\n", cmd->name, cmd->alias, cmd->tip);
+ pretty_print(summary TSRMLS_CC);
+ efree(summary);
+}
+
+/* {{{ get_help. Retries and formats text from the phpdbg help text table */
+static char *get_help(const char * const key TSRMLS_DC)
{
- phpdbg_help_header();
- phpdbg_writeln("Step back into the vm, skipping breakpoints until the current stack is returning");
- phpdbg_writeln(EMPTY);
- phpdbg_notice("Examples");
- phpdbg_writeln("\t%sleave", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sL", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill cause a break when instructed to leave the current context");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("Note: this allows inspection of the return value before it is returned");
- phpdbg_help_footer();
- return SUCCESS;
-} /* }}} */
+ phpdbg_help_text_t *p;
-PHPDBG_HELP(compile) /* {{{ */
-{
- phpdbg_help_header();
- phpdbg_writeln("Pre-compilation of the execution context provides the opportunity to inspect opcodes before execution");
- phpdbg_writeln("The execution context must be set for compilation to succeed");
- phpdbg_writeln(EMPTY);
- phpdbg_notice("Examples");
- phpdbg_writeln("\t%scompile", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sc", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill compile the current execution context, populating class/function/constant/etc tables");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("Note: It is a good idea to clean the environment between each compilation");
- phpdbg_help_footer();
- return SUCCESS;
-} /* }}} */
+ /* Note that phpdbg_help_text is not assumed to be collated in key order. This is an
+ inconvience that means that help can't be logically grouped Not worth
+ the savings */
-PHPDBG_HELP(print) /* {{{ */
-{
- phpdbg_help_header();
- phpdbg_writeln("By default, print will show information about the current execution context");
- phpdbg_writeln("Other printing commands give access to instruction information");
- phpdbg_writeln(EMPTY);
- phpdbg_notice("Examples");
- phpdbg_writeln("\t%sprint class \\my\\class", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sp c \\my\\class", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill print the instructions for the methods in \\my\\class");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%sprint method \\my\\class::method", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sp m \\my\\class::method", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill print the instructions for \\my\\class::method");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%sprint func .getSomething", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sp f .getSomething", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill print the instructions for ::getSomething in the active scope");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%sprint func my_function", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sp f my_function", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill print the instructions for the global function my_function");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%sprint opline", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sp o", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill print the instruction for the current opline");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%sprint exec", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sp e", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill print the instructions for the execution context");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%sprint stack", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sp s", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill print the instructions for the current stack");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("Specific printers loaded are show below:");
- phpdbg_notice("Commands");
- {
- const phpdbg_command_t *print_command = phpdbg_print_commands;
-
- phpdbg_writeln("\tAlias\tCommand\t\tPurpose");
- while (print_command && print_command->name) {
- if (print_command->alias) {
- phpdbg_writeln("\t[%c]\t%s\t\t%s", print_command->alias, print_command->name, print_command->tip);
- } else {
- phpdbg_writeln("\t[ ]\t%s\t\t%s", print_command->name, print_command->tip);
- }
- ++print_command;
+ for (p = phpdbg_help_text; p->key; p++) {
+ if (!strcmp(p->key, key)) {
+ return p->text;
}
}
- phpdbg_help_footer();
- return SUCCESS;
-} /* }}} */
-
-PHPDBG_HELP(run) /* {{{ */
-{
- phpdbg_help_header();
- phpdbg_writeln("Execute the current context inside the phpdbg vm");
- phpdbg_writeln(EMPTY);
- phpdbg_notice("Examples");
- phpdbg_writeln("\t%srun", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sr", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill cause execution of the context, if it is set.");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("Note: The execution context must be set, but not necessarily compiled before execution occurs");
- phpdbg_help_footer();
- return SUCCESS;
+ return ""; /* return empty string to denote no match found */
} /* }}} */
-PHPDBG_HELP(eval) /* {{{ */
+/* {{{ get_command. Return number of matching commands from a command table.
+ * Unlike the command parser, the help search is sloppy that is partial matches can occur
+ * * Any single character key is taken as an alias.
+ * * Other keys are matched again the table on the first len characters.
+ * * This means that non-unique keys can generate multiple matches.
+ * * The first matching command is returned as an OUT parameter. *
+ * The rationale here is to assist users in finding help on commands. So unique matches
+ * will be used to generate a help message but non-unique one will be used to list alternatives.
+ */
+static int get_command(
+ const char *key, size_t len, /* pointer and length of key */
+ phpdbg_command_t const **command, /* address of first matching command */
+ phpdbg_command_t const * commands /* command table to be scanned */
+ TSRMLS_DC)
{
- phpdbg_help_header();
- phpdbg_writeln("Access to eval() allows you to change the environment during execution, careful though!!");
- phpdbg_writeln(EMPTY);
- phpdbg_notice("Examples");
- phpdbg_writeln("\t%seval $variable", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sE $variable", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill print_r($variable) on the console, if it is defined");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%seval $variable = \"Hello phpdbg :)\"", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sE $variable = \"Hello phpdbg :)\"", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill set $variable in the current scope");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("Note: eval() will always show the result; do not prefix the code with \"return\"");
- phpdbg_help_footer();
- return SUCCESS;
-} /* }}} */
-
-PHPDBG_HELP(break) /* {{{ */
-{
- phpdbg_help_header();
- phpdbg_writeln("Setting a breakpoint stops execution at a specific stage");
- phpdbg_writeln(EMPTY);
- phpdbg_notice("Examples");
- phpdbg_writeln("\t%sbreak [file] test.php:1", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sb [F] test.php:1", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill break execution on line 1 of test.php");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%sbreak [func] my_function", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sb [f] my_function", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill break execution on entry to my_function");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%sbreak [method] \\my\\class::method", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sb [m] \\my\\class::method", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill break execution on entry to \\my\\class::method");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%sbreak [address] 0x7ff68f570e08", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sb [a] 0x7ff68f570e08", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill break at the opline with the address provided");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%sbreak [address] my_function#1", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sb [a] my_function#1", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill break at the opline number 1 of the function my_function");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%sbreak [address] \\my\\class::method#2", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sb [a] \\my\\class::method#2", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill break at the opline number 2 of the method \\my\\class::method");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%sbreak address test.php:3", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sb a test.php:3", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill break at the opline number 3 of test.php");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%sbreak [lineno] 200", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sb [l] 200", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill break at line 200 of the currently executing file");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%sbreak on ($expression == true)", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sb on ($expression == true)", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill break when the condition evaluates to true");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%sbreak at phpdbg::isGreat if ($expression == true)", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill break at every opcode in phpdbg::isGreat when the condition evaluates to true");
- phpdbg_writeln("\t%sbreak at test.php:20 if ($expression == true)", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill break at every opcode on line 20 of test.php when the condition evaluates to true");
- phpdbg_write("\t");
- phpdbg_notice("The location can be anything accepted by file, func, method, or address break commands");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%sbreak op ZEND_ADD", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sb O ZEND_ADD", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill break on every occurrence of the opcode provided");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%sbreak del 1", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sb d 1", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill remove the breakpoint with the given identifier");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("Note: An address is only valid for the current compilation");
- phpdbg_writeln(EMPTY);
- phpdbg_notice("The parameters enclosed by [] are usually optional, but help avoid ambigious commands");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("Specific breakers loaded are show below:");
- phpdbg_notice("Commands");
- {
- const phpdbg_command_t *break_command = phpdbg_break_commands;
-
- phpdbg_writeln("\tAlias\tCommand\t\tPurpose");
- while (break_command && break_command->name) {
- if (break_command->alias) {
- phpdbg_writeln("\t[%c]\t%s\t\t%s", break_command->alias, break_command->name, break_command->tip);
- } else {
- phpdbg_writeln("\t[ ]\t%s\t\t%s", break_command->name, break_command->tip);
+ const phpdbg_command_t *c;
+ unsigned int num_matches = 0;
+
+ if (len == 1) {
+ for (c=commands; c->name; c++) {
+ if (c->alias == key[0]) {
+ num_matches++;
+ if ( num_matches == 1 && command) {
+ *command = c;
+ }
}
- ++break_command;
}
- }
- phpdbg_writeln("Note: Conditional breaks are costly, use them sparingly!");
- phpdbg_help_footer();
- return SUCCESS;
-} /* }}} */
-
-PHPDBG_HELP(clean) /* {{{ */
-{
- phpdbg_help_header();
- phpdbg_writeln("While debugging you may experience errors because of attempts to redeclare classes, constants or functions");
- phpdbg_writeln("Cleaning the environment cleans these tables, so that files can be recompiled without exiting phpdbg");
- phpdbg_help_footer();
- return SUCCESS;
-} /* }}} */
-
-PHPDBG_HELP(clear) /* {{{ */
-{
- phpdbg_help_header();
- phpdbg_writeln("Clearing breakpoints means you can once again run code without interruption");
- phpdbg_writeln("Note: all breakpoints are lost; be sure debugging is complete before clearing");
- phpdbg_help_footer();
- return SUCCESS;
-} /* }}} */
-
-PHPDBG_HELP(info) /* {{{ */
-{
- phpdbg_help_header();
- phpdbg_writeln("info commands provide quick access to various types of information about the PHP environment");
- phpdbg_writeln("Specific info commands are show below:");
- phpdbg_notice("Commands");
- {
- const phpdbg_command_t *info_command = phpdbg_info_commands;
-
- phpdbg_writeln("\tAlias\tCommand\t\tPurpose");
- while (info_command && info_command->name) {
- if (info_command->alias) {
- phpdbg_writeln("\t[%c]\t%s\t\t%s", info_command->alias, info_command->name, info_command->tip);
- } else {
- phpdbg_writeln("\t[ ]\t%s\t\t%s", info_command->name, info_command->tip);
+ } else {
+ for (c=commands; c->name; c++) {
+ if (!strncmp(c->name, key, len)) {
+ ++num_matches;
+ if ( num_matches == 1 && command) {
+ *command = c;
+ }
}
- ++info_command;
}
}
- phpdbg_help_footer();
- return SUCCESS;
-} /* }}} */
+ return num_matches;
-PHPDBG_HELP(quiet) /* {{{ */
-{
- phpdbg_help_header();
- phpdbg_writeln("Setting quietness on will stop the OPLINE output during execution");
- phpdbg_writeln(EMPTY);
- phpdbg_notice("Examples");
- phpdbg_writeln("\t%squiet 1", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sQ 1", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill silence OPLINE output, while");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%squiet 0", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sQ 0", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill enable OPLINE output again");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("Note: Quietness is disabled automatically while stepping");
- phpdbg_help_footer();
- return SUCCESS;
-} /* }}} */
+} /* }}} */
-PHPDBG_HELP(back) /* {{{ */
+PHPDBG_COMMAND(help) /* {{{ */
{
- phpdbg_help_header();
- phpdbg_writeln("The backtrace is built with the default debug backtrace functionality");
- phpdbg_writeln(EMPTY);
- phpdbg_notice("Examples");
- phpdbg_writeln("\t%sback 5", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%st 5", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill limit the number of frames to 5, the default is no limit");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("Note: it is not necessary for an exception to be thrown to show a backtrace");
- phpdbg_help_footer();
- return SUCCESS;
-} /* }}} */
+ phpdbg_command_t const *cmd;
+ int n;
-PHPDBG_HELP(frame) /* {{{ */
-{
- phpdbg_help_header();
- phpdbg_writeln("When viewing a backtrace, it is sometimes useful to jump to a frame in that trace");
- phpdbg_writeln(EMPTY);
- phpdbg_notice("Examples");
- phpdbg_writeln("\t%sframe 2", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sf 2", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill go to frame 2, temporarily affecting scope and allowing access to the variables in that frame");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("Note: the current frame is restored when execution continues");
- phpdbg_help_footer();
- return SUCCESS;
-} /* }}} */
+ if (!param || param->type == EMPTY_PARAM) {
+ pretty_print(get_help("overview!" TSRMLS_CC) TSRMLS_CC);
+ return SUCCESS;
+ }
-PHPDBG_HELP(list) /* {{{ */
-{
- phpdbg_help_header();
- phpdbg_writeln("The list command displays source code for the given argument");
- phpdbg_writeln(EMPTY);
- phpdbg_notice("Examples");
- phpdbg_writeln("\t%slist [lines] 2", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sl [l] 2", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill print next 2 lines from the current file");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%slist [func] my_function", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sl [f] my_function", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill print the source of the global function \"my_function\"");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%slist [func] .mine", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sl [f] .mine", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill print the source of the method \"mine\" from the active scope");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%slist [method] my::method", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sl [m] my::method", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill print the source of \"my::method\"");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%slist c myClass", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sl c myClass", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill print the source of \"myClass\"");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("Note: before listing functions you must have a populated function table, try compile!!");
- phpdbg_writeln(EMPTY);
- phpdbg_notice("The parameters enclosed by [] are usually optional, but help avoid ambigious commands");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("Specific listers loaded are show below:");
- phpdbg_notice("Commands");
- {
- const phpdbg_command_t *list_command = phpdbg_list_commands;
-
- phpdbg_writeln("\tAlias\tCommand\t\tPurpose");
- while (list_command && list_command->name) {
- if (list_command->alias) {
- phpdbg_writeln("\t[%c]\t%s\t\t%s", list_command->alias, list_command->name, list_command->tip);
+ if (param && param->type == STR_PARAM) {
+ n = get_command(param->str, param->len, &cmd, phpdbg_prompt_commands TSRMLS_CC);
+
+ if (n==1) {
+ summary_print(cmd TSRMLS_CC);
+ pretty_print(get_help(cmd->name TSRMLS_CC) TSRMLS_CC);
+ return SUCCESS;
+
+ } else if (n>1) {
+ if (param->len > 1) {
+ for (cmd=phpdbg_prompt_commands; cmd->name; cmd++) {
+ if (!strncmp(cmd->name, param->str, param->len)) {
+ summary_print(cmd TSRMLS_CC);
+ }
+ }
+ pretty_print(get_help("duplicate!" TSRMLS_CC) TSRMLS_CC);
+ return SUCCESS;
} else {
- phpdbg_writeln("\t[ ]\t%s\t\t%s", list_command->name, list_command->tip);
+ phpdbg_error("Internal help error, non-unique alias \"%c\"", param->str[0]);
+ return FAILURE;
+ }
+
+ } else { /* no prompt command found so try help topic */
+ n = get_command( param->str, param->len, &cmd, phpdbg_help_commands TSRMLS_CC);
+
+ if (n>0) {
+ if (cmd->alias == 'a') { /* help aliases executes a canned routine */
+ return cmd->handler(param TSRMLS_CC);
+ } else {
+ pretty_print(get_help(cmd->name TSRMLS_CC) TSRMLS_CC);
+ return SUCCESS;
+ }
}
- ++list_command;
}
}
- phpdbg_help_footer();
- return SUCCESS;
-} /* }}} */
-PHPDBG_HELP(oplog) /* {{{ */
-{
- phpdbg_help_header();
- phpdbg_writeln("Even when quietness is enabled you may wish to save opline logs to a file");
- phpdbg_writeln("Setting a new oplog closes the previously open log");
- phpdbg_writeln("The log includes a high resolution timestamp on each entry");
- phpdbg_writeln(EMPTY);
- phpdbg_notice("Examples");
- phpdbg_writeln("\t%soplog /path/to/my.oplog", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sO /path/to/my.oplog", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill open the file /path/to/my.oplog for writing, creating it if it does not exist");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("\t%soplog 0", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sO 0", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill close the currently open log file, disabling oplog");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("Note: upon failure to open a new oplog, the last oplog is held open");
- phpdbg_help_footer();
- return SUCCESS;
+ return FAILURE;
+
} /* }}} */
-PHPDBG_HELP(set) /* {{{ */
+PHPDBG_HELP(aliases) /* {{{ */
{
- phpdbg_help_header();
- phpdbg_writeln("Configure how phpdbg looks and behaves with the set command");
- phpdbg_writeln("Specific set commands are show below:");
- phpdbg_notice("Commands");
- {
- const phpdbg_command_t *set_command = phpdbg_set_commands;
-
- phpdbg_writeln("\tAlias\tCommand\t\tPurpose");
- while (set_command && set_command->name) {
- if (set_command->alias) {
- phpdbg_writeln("\t[%c]\t%s\t\t%s", set_command->alias, set_command->name, set_command->tip);
- } else {
- phpdbg_writeln("\t[ ]\t%s\t\t%s", set_command->name, set_command->tip);
+ const phpdbg_command_t *c, *c_sub;
+ int len;
+
+ /* Print out aliases for all commands except help as this one comes last */
+ phpdbg_writeln("Below are the aliased, short versions of all supported commands");
+ for(c = phpdbg_prompt_commands; c->name; c++) {
+ if (c->alias && c->alias != 'h') {
+ phpdbg_writeln(" %c %-20s %s", c->alias, c->name, c->tip);
+ if (c->subs) {
+ len = 20 - 1 - c->name_len;
+ for(c_sub = c->subs; c_sub->alias; c_sub++) {
+ if (c_sub->alias) {
+ phpdbg_writeln(" %c %c %s %-*s %s",
+ c->alias, c_sub->alias, (char *)c->name, len, c_sub->name, c_sub->tip);
+ }
+ }
}
- ++set_command;
}
}
-#ifndef _WIN32
- phpdbg_notice("Colors");
- {
- const phpdbg_color_t *color = phpdbg_get_colors(TSRMLS_C);
-
- if (PHPDBG_G(flags) & PHPDBG_IS_COLOURED) {
- phpdbg_writeln("\t%-20s\t\tExample", "Name");
- } else {
- phpdbg_writeln("\tName");
- }
-
- while (color && color->name) {
- if (PHPDBG_G(flags) & PHPDBG_IS_COLOURED) {
- phpdbg_writeln(
- "\t%-20s\t\t\033[%smphpdbg rocks :)\033[0m", color->name, color->code);
- } else {
- phpdbg_writeln("\t%s", color->name);
- }
- ++color;
+
+ /* Print out aliases for help as this one comes last, with the added text on how aliases are used */
+ get_command("h", 1, &c, phpdbg_prompt_commands TSRMLS_CC);
+ phpdbg_writeln(" %c %-20s %s\n", c->alias, c->name, c->tip);
+
+ len = 20 - 1 - c->name_len;
+ for(c_sub = c->subs; c_sub->alias; c_sub++) {
+ if (c_sub->alias) {
+ phpdbg_writeln(" %c %c %s %-*s %s",
+ c->alias, c_sub->alias, c->name, len, c_sub->name, c_sub->tip);
}
}
- phpdbg_writeln("The <element> for set color can be \"prompt\", \"notice\", or \"error\"");
-#endif
- phpdbg_help_footer();
+
+ pretty_print(get_help("aliases!" TSRMLS_CC) TSRMLS_CC);
return SUCCESS;
} /* }}} */
-PHPDBG_HELP(register) /* {{{ */
-{
- phpdbg_help_header();
- phpdbg_writeln("Register any global function for use as a command in phpdbg console");
- phpdbg_writeln(EMPTY);
- phpdbg_notice("Examples");
- phpdbg_writeln("\t%sregister scandir", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%sR scandir", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill register the scandir function for use in phpdbg");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("Note: arguments passed as strings, return (if present) print_r'd on console");
- if (zend_hash_num_elements(&PHPDBG_G(registered))) {
- HashPosition position;
- char *name = NULL;
- zend_uint name_len = 0;
-
- phpdbg_notice("Registered Functions (%d)", zend_hash_num_elements(&PHPDBG_G(registered)));
- for (zend_hash_internal_pointer_reset_ex(&PHPDBG_G(registered), &position);
- zend_hash_get_current_key_ex(&PHPDBG_G(registered), &name, &name_len, NULL, 1, &position) == HASH_KEY_IS_STRING;
- zend_hash_move_forward_ex(&PHPDBG_G(registered), &position)) {
- phpdbg_writeln("|-------> %s", name);
- efree(name);
- }
- }
- phpdbg_help_footer();
- return SUCCESS;
-} /* }}} */
+/* {{{ Help Text Table
+ * Contains help text entries keyed by a lowercase ascii key.
+ * Text is in ascii and enriched by a simple markup:
+ * ** toggles bold font emphasis.
+ * $P insert an bold phpdbg> prompt.
+ * \ escapes the following character. Note that this is itself escaped inside string
+ * constants so \\\\ is required to output a single \ e.g. as in namespace names.
+ *
+ * Text will be wrapped according to the STDOUT terminal width, so paragraphs are
+ * flowed using the C stringizing and the CR definition. Also note that entries
+ * are collated in alphabetic order on key.
+ *
+ * Also note the convention that help text not directly referenceable as a help param
+ * has a key ending in !
+ */
+#define CR "\n"
+phpdbg_help_text_t phpdbg_help_text[] = {
+
+/******************************** General Help Topics ********************************/
+{"overview!", CR
+"**phpdbg** is a lightweight, powerful and easy to use debugging platform for PHP5.4+" CR
+"It supports the following commands:" CR CR
+
+"**Information**" CR
+" **list** list PHP source" CR
+" **info** displays information on the debug session" CR
+" **print** show opcodes " CR
+" **frame** select a stack frame and print a stack frame summary" CR
+" **help** provide help on a topic" CR CR
+
+"**Starting and Stopping Execution**" CR
+" **exec** set execution context" CR
+" **run** attempt execution" CR
+" **step** continue execution until other line is reached" CR
+" **continue** continue execution" CR
+" **until** continue execution up to the given location" CR
+" **finish** continue up to end of the current execution frame" CR
+" **leave** continue up to end of the current execution frame and halt after the calling instruction" CR
+" **break** set a breakpoint at the specified target" CR
+" **watch** set a watchpoint on $variable" CR
+" **clear** clear one or all breakpoints" CR
+" **clean** clean the execution environment" CR CR
+
+"**Miscellaneous**" CR
+" **set** set the phpdbg configuration" CR
+" **source** execute a phpdbginit script" CR
+" **register** register a phpdbginit function as a command alias" CR
+" **sh** shell a command" CR
+" **ev** evaluate some code" CR
+" **quit** exit phpdbg" CR CR
+
+"Type **help <command>** or (**help alias**) to get detailed help on any of the above commands, "
+"for example **help list** or **h l**. Note that help will also match partial commands if unique "
+"(and list out options if not unique), so **help clea** will give help on the **clean** command, "
+"but **help cl** will list the summary for **clean** and **clear**." CR CR
+
+"Type **help aliases** to show a full alias list, including any registered phpdginit functions" CR
+"Type **help syntax** for a general introduction to the command syntax." CR
+"Type **help options** for a list of phpdbg command line options." CR
+"Type **help phpdbginit** to show how to customise the debugger environment."
+},
+{"options", CR
+"Below are the command line options supported by phpdbg" CR CR
+ /* note the extra 4 space index in because of the extra **** */
+"**Command Line Options and Flags**" CR
+" **Option** **Example Argument** **Description**" CR
+" **-c** **-c**/my/php.ini Set php.ini file to load" CR
+" **-d** **-d**memory_limit=4G Set a php.ini directive" CR
+" **-n** Disable default php.ini" CR
+" **-q** Supress welcome banner" CR
+" **-v** Enable oplog output" CR
+" **-s** Enable stepping" CR
+" **-b** Disable colour" CR
+" **-i** **-i**my.init Set .phpdbginit file" CR
+" **-I** Ignore default .phpdbginit" CR
+" **-O** **-O**my.oplog Sets oplog output file" CR
+" **-r** Run execution context" CR
+" **-rr** Run execution context and quit after execution" CR
+" **-E** Enable step through eval, careful!" CR
+" **-S** **-S**cli Override SAPI name, careful!" CR
+" **-l** **-l**4000 Setup remote console ports" CR
+" **-a** **-a**192.168.0.3 Setup remote console bind address" CR
+" **-V** Print version number" CR
+" **--** **--** arg1 arg2 Use to delimit phpdbg arguments and php $argv; append any $argv "
+"argument after it" CR CR
+
+"**Remote Console Mode**" CR CR
+
+"This mode is enabled by specifying the **-a** option. Phpdbg will bind only to the loopback "
+"interface by default, and this can only be overridden by explicitly setting the remote console "
+"bind address using the **-a** option. If **-a** is specied without an argument, then phpdbg "
+"will bind to all available interfaces. You should be aware of the security implications of "
+"doing this, so measures should be taken to secure this service if bound to a publicly accessible "
+"interface/port." CR CR
+
+"Specify both stdin and stdout with -lstdin/stdout; by default stdout is stdin * 2."
+},
+
+{"phpdbginit", CR
+"Phpdgb uses an debugger script file to initialize the debugger context. By default, phpdbg looks "
+"for the file named **.phpdbginit** in the current working directory. This location can be "
+"overridden on the command line using the **-i** switch (see **help options** for a more "
+"details)." CR CR
+
+"Debugger scripts can also be executed using the **source** command." CR CR
+
+"A script file can contain a sequence of valid debugger commands, comments and embedded PHP "
+"code. " CR CR
+
+"Comment lines are prefixed by the **#** character. Note that comments are only allowed in script "
+"files and not in interactive sessions." CR CR
+
+"PHP code is delimited by the start and end escape tags **<:** and **:>**. PHP code can be used "
+"to define application context for a debugging session and also to extend the debugger by defining "
+"and **register** PHP functions as new commands." CR CR
+
+"Also note that executing a **clear** command will cause the current **phpdbginit** to be reparsed "
+"/ reloaded."
+},
+
+{"syntax", CR
+"Commands start with a keyword, and some (**break**, "
+"**info**, **set**, **print** and **list**) may include a subcommand keyword. All keywords are "
+"lower case but also have a single letter alias that may be used as an alternative to typing in the"
+"keyword in full. Note some aliases are uppercase, and that keywords cannot be abbreviated other "
+"than by substitution by the alias." CR CR
+
+"Some commands take an argument. Arguments are typed according to their format:" CR
+" * **omitted**" CR
+" * **address** **0x** followed by a hex string" CR
+" * **number** an optionally signed number" CR
+" * **method** a valid **Class::methodName** expression" CR
+" * **func#op** a valid **Function name** follow by # and an integer" CR
+" * **method#op** a valid **Class::methodName** follow by # and an integer" CR
+" * **string** a general string" CR
+" * **function** a valid **Function name**" CR
+" * **file:line** a valid **filename** follow by : and an integer" CR CR
+
+"In some cases the type of the argument enables the second keyword to be omitted." CR CR
+
+"Type **help** for an overview of all commands and type **help <command>** to get detailed help "
+"on any specific command." CR CR
+
+"**Valid Examples**" CR CR
+
+" $P quit" CR
+" $P q" CR
+" Quit the debugger" CR CR
+
+" $P ev $total[2]" CR
+" Evaluate and print the variable $total[2] in the current stack frame" CR
+" " CR
+" $P break 200" CR
+" $P b my_source.php:200" CR
+" Break at line 200 in the current source and in file **my_source.php**. " CR CR
+
+" $P b @ ClassX::get_args if $arg[0] == \"fred\"" CR
+" $P b ~ 3" CR
+" Break at ClassX::get_args() if $arg[0] == \"fred\" and delete breakpoint 3" CR CR
+
+"**Examples of invalid commands**" CR
+
+" $P #This is a comment" CR
+" Comments introduced by the **#** character are only allowed in **phpdbginit** script files."
+},
+
+/******************************** Help Codicils ********************************/
+{"aliases!", CR
+"Note that aliases can be used for either command or sub-command keywords or both, so **info b** "
+"is a synomyn for **info break** and **l func** for **list func**, etc." CR CR
+
+"Note that help will also accept any alias as a parameter and provide help on that command, for example **h p** will provide help on the print command."
+},
+
+{"duplicate!", CR
+"Parameter is not unique. For detailed help select help on one of the above commands."
+},
+
+/******************************** Help on Commands ********************************/
+{"back",
+"Provide a formatted backtrace using the standard debug_backtrace() functionality. An optional "
+"unsigned integer argument specifying the maximum number of frames to be traced; if omitted then "
+"a complete backtrace is given." CR CR
+
+"**Examples**" CR CR
+" $P back 5" CR
+" $P t " CR
+" " CR
+"A backtrace can be executed at any time during execution."
+},
+
+{"break",
+"Breakpoints can be set at a range of targets within the execution environment. Execution will "
+"be paused if the program flow hits a breakpoint. The break target can be one of the following "
+"types:" CR CR
+
+" **Target** **Alias** **Purpose**" CR
+" **at** **A** specify breakpoint by location and condition" CR
+" **del** **d** delete breakpoint by breakpoint identifier number" CR CR
+
+"**Break at** takes two arguments. The first is any valid target. The second "
+"is a valid PHP expression which will trigger the break in "
+"execution, if evaluated as true in a boolean context at the specified target." CR CR
-PHPDBG_HELP(source) /* {{{ */
-{
- phpdbg_help_header();
- phpdbg_writeln("Sourcing a phpdbginit during your debugging session might save some time");
- phpdbg_writeln("The source command can also be used to export breakpoints to a phpdbginit file");
- phpdbg_writeln(EMPTY);
- phpdbg_notice("Examples");
- phpdbg_writeln("\t%ssource /my/init", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%s. /my/init", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill execute the phpdbginit file at /my/init");
- phpdbg_writeln("\t%ssource export /my/init", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%s. export /my/init", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill export breakpoints to /my/init in phpdbginit file format");
- phpdbg_help_footer();
- return SUCCESS;
-} /* }}} */
+"Note that breakpoints can also be disabled and re-enabled by the **set break** command." CR CR
+
+"**Examples**" CR CR
+" $P break test.php:100" CR
+" $P b test.php:100" CR
+" Break execution at line 100 of test.php" CR CR
+
+" $P break 200" CR
+" $P b 200" CR
+" Break execution at line 200 of the currently PHP script file" CR CR
+
+" $P break \\\\mynamespace\\\\my_function" CR
+" $P b \\\\mynamespace\\\\my_function" CR
+" Break execution on entry to \\\\mynamespace\\\\my_function" CR CR
+
+" $P break classX::method" CR
+" $P b classX::method" CR
+" Break execution on entry to classX::method" CR CR
+
+" $P break 0x7ff68f570e08" CR
+" $P b 0x7ff68f570e08" CR
+" Break at the opline at the address 0x7ff68f570e08" CR CR
-PHPDBG_HELP(shell) /* {{{ */
-{
- phpdbg_help_header();
- phpdbg_writeln("Direct access to shell commands saves having to switch windows/consoles");
- phpdbg_writeln(EMPTY);
- phpdbg_notice("Examples");
- phpdbg_writeln("\t%sshell ls /usr/src/php-src", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\t%s- ls /usr/src/php-src", phpdbg_get_prompt(TSRMLS_C));
- phpdbg_writeln("\tWill execute ls /usr/src/php-src, displaying the output in the console");
- phpdbg_writeln(EMPTY);
- phpdbg_writeln("Note: read only commands please!");
- phpdbg_help_footer();
- return SUCCESS;
-} /* }}} */
+" $P break my_function#14" CR
+" $P b my_function#14" CR
+" Break at the opline #14 of the function my_function" CR CR
-PHPDBG_HELP(options) /* {{{ */
-{
- phpdbg_help_header();
- phpdbg_writeln("Below are the command line options supported by phpdbg");
- phpdbg_notice("Command Line Options and Flags");
- phpdbg_writeln(" -c\t-c/my/php.ini\t\tSet php.ini file to load");
- phpdbg_writeln(" -d\t-dmemory_limit=4G\tSet a php.ini directive");
- phpdbg_writeln(" -n\tN/A\t\t\tDisable default php.ini");
- phpdbg_writeln(" -q\tN/A\t\t\tSuppress welcome banner");
- phpdbg_writeln(" -e\t-emytest.php\t\tSet execution context");
- phpdbg_writeln(" -v\tN/A\t\t\tEnable oplog output");
- phpdbg_writeln(" -s\tN/A\t\t\tEnable stepping");
- phpdbg_writeln(" -b\tN/A\t\t\tDisable colour");
- phpdbg_writeln(" -i\t-imy.init\t\tSet .phpdbginit file");
- phpdbg_writeln(" -I\tN/A\t\t\tIgnore default .phpdbginit");
- phpdbg_writeln(" -O\t-Omy.oplog\t\tSets oplog output file");
- phpdbg_writeln(" -r\tN/A\t\t\tRun execution context");
- phpdbg_writeln(" -E\tN/A\t\t\tEnable step through eval, careful!");
- phpdbg_writeln(" -S\t-Scli\t\t\tOverride SAPI name, careful!");
-#ifndef _WIN32
- phpdbg_writeln(" -l\t-l4000\t\t\tSetup remote console ports");
- phpdbg_writeln(" -a\t-a192.168.0.3\t\tSetup remote console bind address");
-#endif
- phpdbg_writeln(" -V\tN/A\t\t\tVersion number");
- phpdbg_notice("Passing -rr will quit automatically after execution");
-#ifndef _WIN32
- phpdbg_writeln("Remote Console Mode");
- phpdbg_notice("For security, phpdbg will bind only to the loopback interface by default");
- phpdbg_writeln("-a without an argument implies all; phpdbg will bind to all available interfaces.");
- phpdbg_writeln("specify both stdin and stdout with -lstdin/stdout; by default stdout is stdin * 2.");
- phpdbg_notice("Steps should be taken to secure this service if bound to a public interface/port");
-#endif
- phpdbg_help_footer();
- return SUCCESS;
-} /* }}} */
+" $P break \\\\my\\\\class::method#2" CR
+" $P b \\\\my\\\\class::method#2" CR
+" Break at the opline #2 of the method \\\\my\\\\class::method" CR CR
+
+" $P break test.php:#3" CR
+" $P b test.php:#3" CR
+" Break at opline #3 in test.php" CR CR
+
+" $P break if $cnt > 10" CR
+" $P b if $cnt > 10" CR
+" Break when the condition ($cnt > 10) evaluates to true" CR CR
+
+" $P break at phpdbg::isGreat if $opt == 'S'" CR
+" $P break @ phpdbg::isGreat if $opt == 'S'" CR
+" Break at any opcode in phpdbg::isGreat when the condition ($opt == 'S') is true" CR CR
+
+" $P break at test.php:20 if !isset($x)" CR
+" Break at every opcode on line 20 of test.php when the condition evaluates to true" CR CR
+
+" $P break ZEND_ADD" CR
+" $P b ZEND_ADD" CR
+" Break on any occurence of the opcode ZEND_ADD" CR CR
+
+" $P break del 2" CR
+" $P b ~ 2" CR
+" Remove breakpoint 2" CR CR
+
+"Note: Conditional breaks are costly in terms of runtime overhead. Use them only when required "
+"as they significantly slow execution." CR CR
+
+"Note: An address is only valid for the current compilation."
+},
+
+{"clean",
+"Classes, constants or functions can only be declared once in PHP. You may experience errors "
+"during a debug session if you attempt to recompile a PHP source. The clean command clears "
+"the Zend runtime tables which holds the sets of compiled classes, constants and functions, "
+"releasing any associated storage back into the storage pool. This enables recompilation to "
+"take place." CR CR
+
+"Note that you cannot selectively trim any of these resource pools. You can only do a complete "
+"clean."
+},
+
+{"clear",
+"Clearing breakpoints means you can once again run code without interruption." CR CR
+
+"Note: use break delete N to clear a specific breakpoint." CR CR
+
+"Note: if all breakpoints are cleared, then the PHP script will run until normal completion."
+},
+
+{"ev",
+"The **ev** command takes a string expression which it evaluates and then displays. It "
+"evaluates in the context of the lowest (that is the executing) frame, unless this has first "
+"been explicitly changed by issuing a **frame** command. " CR CR
+
+"**Examples**" CR CR
+" $P ev $variable" CR
+" Will print_r($variable) on the console, if it is defined" CR CR
+
+" $P ev $variable = \"Hello phpdbg :)\"" CR
+" Will set $variable in the current scope" CR CR
+
+"Note that **ev** allows any valid PHP expression including assignments, function calls and "
+"other write statements. This enables you to change the environment during execution, so care "
+"is needed here. You can even call PHP functions which have breakpoints defined. " CR CR
+
+"Note: **ev** will always show the result, so do not prefix the code with **return**"
+},
+
+{"exec",
+"The **exec** command sets the execution context, that is the script to be executed. The "
+"execution context must be defined either by executing the **exec** command or by using the "
+"**-e** command line option." CR CR
+
+"Note that the **exec** command also can be used to replace a previously defined execution "
+"context." CR CR
+
+"**Examples**" CR CR
+
+" $P exec /tmp/script.php" CR
+" $P e /tmp/script.php" CR
+" Set the execution context to **/tmp/script.php**"
+},
+
+//*********** Does F skip any breakpoints lower stack frames or only the current??
+{"finish",
+"The **finish** command causes control to be passed back to the vm, continuing execution. Any "
+"breakpoints that are encountered within the current stack frame will be skipped. Execution "
+"will then continue until the next breakpoint after leaving the stack frame or unitil "
+"completion of the script" CR CR
+
+"Note when **step**ping is enabled, any opcode steps within the current stack frame are also "
+"skipped. "CR CR
+
+"Note **finish** will trigger a \"not executing\" error if not executing."
+},
+
+{"frame",
+"The **frame** takes an optional integer argument. If omitted, then the current frame is displayed "
+"If specified then the current scope is set to the corresponding frame listed in a **back** trace. " "This can be used to allowing access to the variables in a higher stack frame than that currently "
+"being executed." CR CR
+
+"**Examples**" CR CR
+" $P frame 2" CR
+" $P E $count" CR
+" Go to frame 2 and print out variable **$count** in that frame" CR CR
+
+"Note that this frame scope is discarded when execution continues, with the execution frame "
+"then reset to the lowest executiong frame."
+},
+
+{"info",
+"**info** commands provide quick access to various types of information about the PHP environment" CR
+"Specific info commands are show below:" CR CR
+
+" **Target** **Alias** **Purpose**" CR
+" **break** **b** show current breakpoints" CR
+" **files** **F** show included files" CR
+" **classes** **c** show loaded classes" CR
+" **funcs** **f** show loaded classes" CR
+" **error** **e** show last error" CR
+" **vars** **v** show active variables" CR
+" **literal** **l** show active literal constants" CR
+" **memory** **m** show memory manager stats"
+},
+
+// ******** same issue about breakpoints in called frames
+{"leave",
+"The **leave** command causes control to be passed back to the vm, continuing execution. Any "
+"breakpoints that are encountered within the current stack frame will be skipped. In effect a "
+"temporary breakpoint is associated with any return opcode, so that a break in execution occurs "
+"before leaving the current stack frame. This allows inspection / modification of any frame "
+"variables including the return value before it is returned" CR CR
+
+"**Examples**" CR CR
+
+" $P leave" CR
+" $P L" CR CR
+
+"Note when **step**ping is enabled, any opcode steps within the current stack frame are also "
+"skipped. "CR CR
+
+"Note **leave** will trigger a \"not executing\" error if not executing."
+},
+
+{"list",
+"The list command displays source code for the given argument. The target type is specficied by "
+"a second subcommand keyword:" CR CR
+
+" **Type** **Alias** **Purpose**" CR
+" **lines** **l** List N lines from the current execution point" CR
+" **func** **f** List the complete source for a specified function" CR
+" **method** **m** List the complete source for a specified class::method" CR
+" **class** **c** List the complete source for a specified class" CR CR
+
+"Note that the context of **lines**, **func** and **method** can be determined by parsing the "
+"argument, so these subcommands are optional. However, you must specify the **class** keyword "
+"to list off a class." CR CR
+
+"**Examples**" CR CR
+" $P list 2" CR
+" $P l l 2" CR
+" List the next 2 lines from the current file" CR CR
+
+" $P list my_function" CR
+" $P l f my_function" CR
+" List the source of the function **my_function**" CR CR
+
+//************ ????
+" $P list func .mine" CR
+" $P l f .mine" CR
+" List the source of the method **mine** from the active class in scope" CR CR
+
+" $P list m my::method" CR
+" $P l my::method" CR
+" List the source of **my::method**" CR CR
+
+" $P list c myClass" CR
+" $P l c myClass" CR
+" List the source of **myClass**" CR CR
+
+"Note that functions and classes can only be listed if the corresponding classes and functions "
+"table in the Zend executor has a corresponding entry. You can use the compile command to "
+"populate these tables for a given execution context."
+},
+
+{"continue",
+"Continue with execution after hitting a break or watchpoint" CR CR
+
+"**Examples**" CR CR
+" $P continue" CR
+" $P c" CR
+" Continue executing until the next break or watchpoint" CR CR
+
+"Note **continue** will trigger a \"not running\" error if not executing."
+},
+
+{"print",
+"By default, print will show information about the current execution context." CR
+"Other printing commands give access to instruction information." CR
+"Specific printers loaded are show below:" CR CR
+
+" **Type** **Alias** **Purpose**" CR
+" **exec** **e** print out the instructions in the execution context" CR
+" **opline** **o** print out the instruction in the current opline" CR
+" **class** **c** print out the instructions in the specified class" CR
+" **method** **m** print out the instructions in the specified method" CR
+" **func** **f** print out the instructions in the specified function" CR
+" **stack** **s** print out the instructions in the current stack" CR CR
+
+"**Examples**" CR CR
+" $P print class \\\\my\\\\class" CR
+" $P p c \\\\my\\\\class" CR
+" Print the instructions for the methods in \\\\my\\\\class" CR CR
+
+" $P print method \\\\my\\\\class::method" CR
+" $P p m \\\\my\\\\class::method" CR
+" Print the instructions for \\\\my\\\\class::method" CR CR
+
+" $P print func .getSomething" CR
+" $P p f .getSomething" CR
+//************* Check this local method scope
+" Print the instructions for ::getSomething in the active scope" CR CR
+
+" $P print func my_function" CR
+" $P p f my_function" CR
+" Print the instructions for the global function my_function" CR CR
+
+" $P print opline" CR
+" $P p o" CR
+" Print the instruction for the current opline" CR CR
+
+" $P print exec" CR
+" $P p e" CR
+" Print the instructions for the execution context" CR CR
+
+" $P print stack" CR
+" $P p s" CR
+" Print the instructions for the current stack"
+},
+
+{"register",
+//******* Needs a general explanation of the how registered functions work
+"Register any global function for use as a command in phpdbg console" CR CR
+
+"**Examples**" CR CR
+" $P register scandir" CR
+" $P R scandir" CR
+" Will register the scandir function for use in phpdbg" CR CR
+
+"Note: arguments passed as strings, return (if present) print_r'd on console"
+},
+
+{"run",
+"Enter the vm, startinging execution. Execution will then continue until the next breakpoint "
+"or completion of the script. Add parameters you want to use as $argv"
+"**Examples**" CR CR
+" $P run" CR
+" $P r" CR
+" Will cause execution of the context, if it is set" CR CR
+" $P r test" CR
+" Will execute with $argv[1] == \"test\"" CR CR
+
+"Note that the execution context must be set. If not previously compiled, then the script will "
+"be compiled before execution." CR CR
+
+"Note that attempting to run a script that is already executing will result in an \"execution "
+"in progress\" error."
+},
+
+{"set",
+"The **set** command is used to configure how phpdbg looks and behaves. Specific set commands "
+"are as follows:" CR CR
+
+" **Type** **Alias** **Purpose**" CR
+" **prompt** **p** set the prompt" CR
+" **color** **c** set color <element> <color>" CR
+" **colors** **C** set colors [<on|off>]" CR
+" **oplog** **O** set oplog [output]" CR
+" **break** **b** set break **id** <on|off>" CR
+" **breaks** **B** set breaks [<on|off>]" CR
+" **quiet** **q** set quiet [<on|off>]" CR
+" **stepping** **s** set stepping [<opcode|line>]" CR
+" **refcount** **r** set refcount [<on|off>] " CR CR
+
+"Valid colors are **none**, **white**, **red**, **green**, **yellow**, **blue**, **purple**, "
+"**cyan** and **black**. All colours except **none** can be followed by an optional "
+"**-bold** or **-underline** qualifier." CR CR
+
+"Color elements can be one of **prompt**, **notice**, or **error**." CR CR
+
+"**Examples**" CR CR
+" $P S C on" CR
+" Set colors on" CR CR
+
+" $P set p >" CR
+" $P set color prompt white-bold" CR
+" Set the prompt to a bold >" CR CR
+
+" $P S c error red-bold" CR
+" Use red bold for errors" CR CR
+
+" $P S refcount on" CR
+" Enable refcount display when hitting watchpoints" CR CR
+
+" $P S b 4 off" CR
+" Temporarily disable breakpoint 4. This can be subsequently reenabled by a **s b 4 on**." CR
+//*********** check oplog syntax
+},
+
+{"sh",
+"Direct access to shell commands saves having to switch windows/consoles" CR CR
+
+"**Examples**" CR CR
+" $P sh ls /usr/src/php-src" CR
+" Will execute ls /usr/src/php-src, displaying the output in the console"
+//*********** what does this mean????Note: read only commands please!
+},
+
+{"source",
+"Sourcing a **phpdbginit** script during your debugging session might save some time." CR CR
+
+"**Examples**" CR CR
+
+" $P source /my/init" CR
+" $P < /my/init" CR
+" Will execute the phpdbginit file at /my/init" CR CR
+},
+
+{"export",
+"Exporting breakpoints allows you to share, and or save your current debugging session" CR CR
+
+"**Examples**" CR CR
+
+" $P export /my/exports" CR
+" $P > /my/exports" CR
+" Will export all breakpoints to /my/exports" CR CR
+},
+
+{"step",
+"Execute opcodes until next line" CR CR
+
+"**Examples**" CR CR
+
+" $P s" CR
+" Will continue and break again in the next encountered line" CR CR
+},
+
+{"until",
+"The **until** command causes control to be passed back to the vm, continuing execution. Any "
+"breakpoints that are encountered before the next source line will be skipped. Execution "
+"will then continue until the next breakpoint or completion of the script" CR CR
+
+"Note when **step**ping is enabled, any opcode steps within the current line are also skipped. "CR CR
+
+"Note that if the next line is **not** executed then **all** subsequent breakpoints will be "
+"skipped. " CR CR
+
+"Note **until** will trigger a \"not executing\" error if not executing."
+
+},
+{"watch",
+"Sets watchpoints on variables as long as they are defined" CR
+"Passing no parameter to **watch**, lists all actually active watchpoints" CR CR
+
+"**Format for $variable**" CR CR
+" **$var** Variable $var" CR
+" **$var[]** All array elements of $var" CR
+" **$var->** All properties of $var" CR
+" **$var->a** Property $var->a" CR
+" **$var[b]** Array element with key b in array $var" CR CR
+
+"Subcommands of **watch**:" CR CR
+
+" **Type** **Alias** **Purpose**" CR
+" **array** **a** Sets watchpoint on array/object to observe if an entry is added or removed" CR
+" **recursive** **r** Watches variable recursively and automatically adds watchpoints if some entry is added to an array/object" CR
+" **delete** **d** Removes watchpoint" CR CR
+
+"Note when **recursive** watchpoints are removed, watchpoints on all the children are removed too" CR CR
+
+"**Examples**" CR CR
+" $P watch" CR
+" List currently active watchpoints" CR CR
+
+" $P watch $array" CR
+" $P w $array" CR
+" Set watchpoint on $array" CR CR
+
+" $P watch recursive $obj->" CR
+" $P w r $obj->" CR
+" Set recursive watchpoint on $obj->" CR CR
+
+" $P watch delete $obj->a" CR
+" $P w d $obj->a" CR
+" Remove watchpoint $obj->a" CR CR
+
+"Technical note: If using this feature with a debugger, you will get many segmentation faults, each time when a memory page containing a watched address is hit." CR
+" You then you can continue, phpdbg will remove the write protection, so that the program can continue." CR
+" If phpdbg could not handle that segfault, the same segfault is triggered again and this time phpdbg will abort."
+},
+{NULL, NULL /* end of table marker */}
+}; /* }}} */
diff --git a/sapi/phpdbg/phpdbg_help.h b/sapi/phpdbg/phpdbg_help.h
index 319142cb5b..16a1e771e3 100644
--- a/sapi/phpdbg/phpdbg_help.h
+++ b/sapi/phpdbg/phpdbg_help.h
@@ -30,63 +30,19 @@
/**
* Helper Forward Declarations
*/
-PHPDBG_HELP(exec);
-PHPDBG_HELP(compile);
-PHPDBG_HELP(step);
-PHPDBG_HELP(next);
-PHPDBG_HELP(run);
-PHPDBG_HELP(eval);
-PHPDBG_HELP(until);
-PHPDBG_HELP(finish);
-PHPDBG_HELP(leave);
-PHPDBG_HELP(print);
-PHPDBG_HELP(break);
-PHPDBG_HELP(clean);
-PHPDBG_HELP(clear);
-PHPDBG_HELP(info);
-PHPDBG_HELP(back);
-PHPDBG_HELP(frame);
-PHPDBG_HELP(quiet);
-PHPDBG_HELP(list);
-PHPDBG_HELP(set);
-PHPDBG_HELP(register);
-PHPDBG_HELP(options);
-PHPDBG_HELP(source);
-PHPDBG_HELP(shell);
+PHPDBG_HELP(aliases);
-/**
- * Commands
- */
-static const phpdbg_command_t phpdbg_help_commands[] = {
- PHPDBG_COMMAND_D_EX(exec, "the execution context should be a valid path", 'e', help_exec, NULL, 0),
- PHPDBG_COMMAND_D_EX(compile, "allow inspection of code before execution", 'c', help_compile, NULL, 0),
- PHPDBG_COMMAND_D_EX(step, "step through execution to break at every opcode", 's', help_step, NULL, 0),
- PHPDBG_COMMAND_D_EX(next, "continue executing while stepping or after breaking", 'n', help_next, NULL, 0),
- PHPDBG_COMMAND_D_EX(run, "execute inside the phpdbg vm", 'r', help_run, NULL, 0),
- PHPDBG_COMMAND_D_EX(eval, "access to eval() allows affecting the environment", 'E', help_eval, NULL, 0),
- PHPDBG_COMMAND_D_EX(until, "continue until the current line is executed", 'u', help_until, NULL, 0),
- PHPDBG_COMMAND_D_EX(finish, "continue until the current function has returned", 'F', help_finish, NULL, 0),
- PHPDBG_COMMAND_D_EX(leave, "continue until the current function is returning", 'L', help_leave, NULL, 0),
- PHPDBG_COMMAND_D_EX(print, "print context information or instructions", 'p', help_print, NULL, 0),
- PHPDBG_COMMAND_D_EX(break, "breakpoints allow execution interruption", 'b', help_break, NULL, 0),
- PHPDBG_COMMAND_D_EX(clean, "resetting the environment is useful while debugging", 'X', help_clean, NULL, 0),
- PHPDBG_COMMAND_D_EX(clear, "reset breakpoints to execute without interruption", 'c', help_clear, NULL, 0),
- PHPDBG_COMMAND_D_EX(info, "quick access to useful information on the console", 'i', help_info, NULL, 0),
- PHPDBG_COMMAND_D_EX(back, "show debug backtrace information during execution", 't', help_back, NULL, 0),
- PHPDBG_COMMAND_D_EX(frame, "switch to a frame in the current stack for inspection", 'f', help_frame, NULL, 0),
- PHPDBG_COMMAND_D_EX(quiet, "be quiet during execution", 'Q', help_quiet, NULL, 0),
- PHPDBG_COMMAND_D_EX(list, "list code gives you quick access to code", 'l', help_list, NULL, 0),
- PHPDBG_COMMAND_D_EX(set, "configure how phpdbg looks and behaves", 'S', help_set, NULL, 0),
- PHPDBG_COMMAND_D_EX(register, "register a function for use as a command", 'R', help_register,NULL, 0),
- PHPDBG_COMMAND_D_EX(options, "show information about command line options", 'o', help_options, NULL, 0),
- PHPDBG_COMMAND_D_EX(source, "load a phpdbginit file at the console", '.', help_source, NULL, 0),
- PHPDBG_COMMAND_D_EX(shell, "execute system commands with direct shell access", '-', help_shell, NULL, 0),
- PHPDBG_END_COMMAND
-};
+extern const phpdbg_command_t phpdbg_help_commands[];
#define phpdbg_help_header() \
phpdbg_notice("Welcome to phpdbg, the interactive PHP debugger, v%s", PHPDBG_VERSION);
#define phpdbg_help_footer() \
phpdbg_notice("Please report bugs to <%s>", PHPDBG_ISSUES);
+typedef struct _phpdbg_help_text_t {
+ char *key;
+ char *text;
+} phpdbg_help_text_t;
+
+extern phpdbg_help_text_t phpdbg_help_text[];
#endif /* PHPDBG_HELP_H */
diff --git a/sapi/phpdbg/phpdbg_info.c b/sapi/phpdbg/phpdbg_info.c
index 0f4233bf30..97f88bfa1e 100644
--- a/sapi/phpdbg/phpdbg_info.c
+++ b/sapi/phpdbg/phpdbg_info.c
@@ -23,9 +23,25 @@
#include "phpdbg_utils.h"
#include "phpdbg_info.h"
#include "phpdbg_bp.h"
+#include "phpdbg_prompt.h"
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+#define PHPDBG_INFO_COMMAND_D(f, h, a, m, l, s) \
+ PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[14])
+
+const phpdbg_command_t phpdbg_info_commands[] = {
+ PHPDBG_INFO_COMMAND_D(break, "show breakpoints", 'b', info_break, NULL, 0),
+ PHPDBG_INFO_COMMAND_D(files, "show included files", 'F', info_files, NULL, 0),
+ PHPDBG_INFO_COMMAND_D(classes, "show loaded classes", 'c', info_classes, NULL, 0),
+ PHPDBG_INFO_COMMAND_D(funcs, "show loaded classes", 'f', info_funcs, NULL, 0),
+ PHPDBG_INFO_COMMAND_D(error, "show last error", 'e', info_error, NULL, 0),
+ PHPDBG_INFO_COMMAND_D(vars, "show active variables", 'v', info_vars, NULL, 0),
+ PHPDBG_INFO_COMMAND_D(literal, "show active literal constants", 'l', info_literal, NULL, 0),
+ PHPDBG_INFO_COMMAND_D(memory, "show memory manager stats", 'm', info_memory, NULL, 0),
+ PHPDBG_END_COMMAND
+};
+
PHPDBG_INFO(break) /* {{{ */
{
phpdbg_print_breakpoints(PHPDBG_BREAK_FILE TSRMLS_CC);
diff --git a/sapi/phpdbg/phpdbg_info.h b/sapi/phpdbg/phpdbg_info.h
index a6b4e3719f..c36e6bebd6 100644
--- a/sapi/phpdbg/phpdbg_info.h
+++ b/sapi/phpdbg/phpdbg_info.h
@@ -34,16 +34,6 @@ PHPDBG_INFO(vars);
PHPDBG_INFO(literal);
PHPDBG_INFO(memory);
-static const phpdbg_command_t phpdbg_info_commands[] = {
- PHPDBG_COMMAND_D_EX(break, "show breakpoints", 'b', info_break, NULL, 0),
- PHPDBG_COMMAND_D_EX(files, "show included files", 'F', info_files, NULL, 0),
- PHPDBG_COMMAND_D_EX(classes, "show loaded classes", 'c', info_classes, NULL, 0),
- PHPDBG_COMMAND_D_EX(funcs, "show loaded classes", 'f', info_funcs, NULL, 0),
- PHPDBG_COMMAND_D_EX(error, "show last error", 'e', info_error, NULL, 0),
- PHPDBG_COMMAND_D_EX(vars, "show active variables", 'v', info_vars, NULL, 0),
- PHPDBG_COMMAND_D_EX(literal, "show active literal constants", 'l', info_literal, NULL, 0),
- PHPDBG_COMMAND_D_EX(memory, "show memory manager stats", 'm', info_memory, NULL, 0),
- PHPDBG_END_COMMAND
-};
+extern const phpdbg_command_t phpdbg_info_commands[];
#endif /* PHPDBG_INFO_H */
diff --git a/sapi/phpdbg/phpdbg_lexer.c b/sapi/phpdbg/phpdbg_lexer.c
new file mode 100644
index 0000000000..94a4ba9766
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_lexer.c
@@ -0,0 +1,2271 @@
+#line 2 "sapi/phpdbg/phpdbg_lexer.c"
+
+#line 4 "sapi/phpdbg/phpdbg_lexer.c"
+
+#define YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 37
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types.
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t;
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX (4294967295U)
+#endif
+
+#endif /* ! C99 */
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else /* ! __cplusplus */
+
+/* C99 requires __STDC__ to be defined as 1. */
+#if defined (__STDC__)
+
+#define YY_USE_CONST
+
+#endif /* defined (__STDC__) */
+#endif /* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index. If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* An opaque pointer. */
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+
+/* For convenience, these vars (plus the bison vars far below)
+ are macros in the reentrant scanner. */
+#define yyin yyg->yyin_r
+#define yyout yyg->yyout_r
+#define yyextra yyg->yyextra_r
+#define yyleng yyg->yyleng_r
+#define yytext yyg->yytext_r
+#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno)
+#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column)
+#define yy_flex_debug yyg->yy_flex_debug_r
+
+/* Enter a start condition. This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN yyg->yy_start = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state. The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START ((yyg->yy_start - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE yyrestart(yyin ,yyscanner )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#define YY_BUF_SIZE 16384
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+ #define YY_LESS_LINENO(n)
+
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ *yy_cp = yyg->yy_hold_char; \
+ YY_RESTORE_YY_MORE_OFFSET \
+ yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+ YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+ } \
+ while ( 0 )
+
+#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner )
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+ {
+ FILE *yy_input_file;
+
+ char *yy_ch_buf; /* input buffer */
+ char *yy_buf_pos; /* current position in input buffer */
+
+ /* Size of input buffer in bytes, not including room for EOB
+ * characters.
+ */
+ yy_size_t yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ yy_size_t yy_n_chars;
+
+ /* Whether we "own" the buffer - i.e., we know we created it,
+ * and can realloc() it to grow it, and should free() it to
+ * delete it.
+ */
+ int yy_is_our_buffer;
+
+ /* Whether this is an "interactive" input source; if so, and
+ * if we're using stdio for input, then we want to use getc()
+ * instead of fread(), to make sure we stop fetching input after
+ * each newline.
+ */
+ int yy_is_interactive;
+
+ /* Whether we're considered to be at the beginning of a line.
+ * If so, '^' rules will be active on the next match, otherwise
+ * not.
+ */
+ int yy_at_bol;
+
+ int yy_bs_lineno; /**< The line count. */
+ int yy_bs_column; /**< The column count. */
+
+ /* Whether to try to fill the input buffer when we reach the
+ * end of it.
+ */
+ int yy_fill_buffer;
+
+ int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+ /* When an EOF's been seen but there's still some text to process
+ * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+ * shouldn't try reading from the input source any more. We might
+ * still have a bunch of tokens to match, though, because of
+ * possible backing-up.
+ *
+ * When we actually see the EOF, we change the status to "new"
+ * (via yyrestart()), so that the user can continue scanning by
+ * just pointing yyin at a new input file.
+ */
+#define YY_BUFFER_EOF_PENDING 2
+
+ };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \
+ ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \
+ : NULL)
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top]
+
+void yyrestart (FILE *input_file ,yyscan_t yyscanner );
+void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+YY_BUFFER_STATE yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner );
+void yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+void yypop_buffer_state (yyscan_t yyscanner );
+
+static void yyensure_buffer_stack (yyscan_t yyscanner );
+static void yy_load_buffer_state (yyscan_t yyscanner );
+static void yy_init_buffer (YY_BUFFER_STATE b,FILE *file ,yyscan_t yyscanner );
+
+#define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER ,yyscanner)
+
+YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner );
+YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner );
+YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,yy_size_t len ,yyscan_t yyscanner );
+
+void *yyalloc (yy_size_t ,yyscan_t yyscanner );
+void *yyrealloc (void *,yy_size_t ,yyscan_t yyscanner );
+void yyfree (void * ,yyscan_t yyscanner );
+
+#define yy_new_buffer yy_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){ \
+ yyensure_buffer_stack (yyscanner); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+ }
+
+#define yy_set_bol(at_bol) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){\
+ yyensure_buffer_stack (yyscanner); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+ }
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+#define yywrap(yyscanner) 1
+#define YY_SKIP_YYWRAP
+
+typedef unsigned char YY_CHAR;
+
+typedef int yy_state_type;
+
+#define yytext_ptr yytext_r
+
+static yy_state_type yy_get_previous_state (yyscan_t yyscanner );
+static yy_state_type yy_try_NUL_trans (yy_state_type current_state ,yyscan_t yyscanner);
+static int yy_get_next_buffer (yyscan_t yyscanner );
+static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+ yyg->yytext_ptr = yy_bp; \
+ yyleng = (size_t) (yy_cp - yy_bp); \
+ yyg->yy_hold_char = *yy_cp; \
+ *yy_cp = '\0'; \
+ yyg->yy_c_buf_p = yy_cp;
+
+#define YY_NUM_RULES 18
+#define YY_END_OF_BUFFER 19
+/* This struct is not used in this scanner,
+ but its presence is necessary. */
+struct yy_trans_info
+ {
+ flex_int32_t yy_verify;
+ flex_int32_t yy_nxt;
+ };
+static yyconst flex_int16_t yy_acclist[229] =
+ { 0,
+ 19, 4, 15, 18, 4, 17, 18, 17, 18, 4,
+ 7, 18, 4, 12, 15, 18, 4, 12, 15, 18,
+ 4, 9, 18, 4, 15, 18, 4, 15, 18, 4,
+ 15, 18, 4, 15, 18, 4, 15, 18, 4, 15,
+ 18, 3, 4, 15, 18, 4, 15, 18, 4, 15,
+ 18, 4, 15, 18, 4, 15, 18, 16, 18, 16,
+ 17, 18, 15, 18, 7, 18, 12, 15, 18, 12,
+ 15, 18, 9, 18, 15, 18, 15, 18, 15, 18,
+ 15, 18, 15, 18, 15, 18, 15, 18, 15, 18,
+ 15, 18, 15, 18, 4, 15, 4, 4, 4, 17,
+
+ 17, 4, 12, 15, 4, 15, 4, 8, 4, 15,
+ 4, 15, 4, 15, 1, 4, 15, 4, 15, 4,
+ 11, 15, 4, 15, 4, 10, 15, 4, 15, 2,
+ 4, 15, 4, 15, 4, 15, 4, 15, 16, 16,
+ 17, 15, 12, 15, 15, 8, 15, 15, 15, 15,
+ 5, 15, 11, 15, 15, 10, 15, 15, 15, 15,
+ 4, 4, 13, 15, 4, 15, 4, 15, 4, 15,
+ 4, 15, 3, 4, 15, 4, 15, 4, 15, 13,
+ 15, 15, 15, 15, 15, 15, 15, 4, 6, 4,
+ 15, 4, 15, 4, 15, 4, 15, 4, 15, 6,
+
+ 15, 15, 15, 15, 15, 4, 15, 4, 15, 4,
+ 15, 15, 15, 15, 4, 14, 15, 4, 15, 4,
+ 15, 14, 15, 15, 15, 4, 15, 15
+ } ;
+
+static yyconst flex_int16_t yy_accept[127] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 2, 5, 8,
+ 10, 13, 17, 21, 24, 27, 30, 33, 36, 39,
+ 42, 46, 49, 52, 55, 58, 60, 63, 65, 67,
+ 70, 73, 75, 77, 79, 81, 83, 85, 87, 89,
+ 91, 93, 95, 97, 98, 99, 101, 102, 105, 107,
+ 109, 111, 113, 115, 118, 120, 123, 125, 128, 130,
+ 133, 135, 137, 139, 140, 142, 143, 143, 145, 146,
+ 147, 148, 149, 150, 151, 153, 155, 156, 158, 159,
+ 160, 161, 162, 165, 167, 169, 171, 173, 176, 178,
+ 180, 180, 182, 183, 184, 185, 186, 187, 188, 190,
+
+ 192, 194, 196, 198, 200, 201, 202, 203, 204, 205,
+ 206, 208, 210, 212, 213, 214, 215, 218, 220, 222,
+ 224, 225, 226, 228, 229, 229
+ } ;
+
+static yyconst flex_int32_t yy_ec[256] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
+ 1, 1, 2, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 2, 1, 1, 4, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 5, 6, 7, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 9, 1, 1,
+ 1, 1, 1, 1, 10, 10, 10, 11, 12, 10,
+ 13, 13, 13, 13, 13, 13, 13, 14, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 15,
+ 1, 1, 1, 1, 16, 1, 17, 18, 10, 19,
+
+ 20, 21, 13, 22, 23, 13, 13, 24, 13, 25,
+ 26, 13, 13, 27, 28, 29, 30, 31, 13, 32,
+ 33, 34, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1
+ } ;
+
+static yyconst flex_int32_t yy_meta[35] =
+ { 0,
+ 1, 2, 3, 2, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1
+ } ;
+
+static yyconst flex_int16_t yy_base[133] =
+ { 0,
+ 0, 0, 33, 35, 38, 0, 357, 71, 74, 76,
+ 352, 79, 87, 90, 96, 100, 108, 109, 112, 120,
+ 123, 126, 138, 142, 148, 0, 104, 316, 455, 151,
+ 162, 313, 73, 92, 65, 146, 122, 94, 152, 127,
+ 155, 163, 172, 318, 131, 176, 182, 288, 190, 314,
+ 178, 184, 187, 211, 212, 215, 221, 224, 225, 235,
+ 236, 239, 245, 0, 203, 260, 259, 248, 264, 455,
+ 237, 190, 243, 253, 255, 222, 259, 194, 249, 250,
+ 277, 229, 0, 285, 286, 289, 295, 304, 307, 310,
+ 184, 0, 252, 283, 292, 296, 306, 309, 163, 328,
+
+ 329, 332, 341, 344, 455, 326, 331, 330, 342, 343,
+ 362, 370, 371, 394, 368, 356, 410, 384, 404, 426,
+ 376, 378, 420, 411, 455, 442, 445, 447, 450, 148,
+ 452, 96
+ } ;
+
+static yyconst flex_int16_t yy_def[133] =
+ { 0,
+ 125, 1, 126, 126, 125, 5, 125, 127, 128, 125,
+ 128, 127, 127, 128, 127, 127, 127, 127, 127, 127,
+ 127, 127, 127, 127, 127, 129, 129, 130, 125, 130,
+ 130, 125, 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 130, 127, 128, 128, 128, 125, 13, 13, 128,
+ 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
+ 127, 127, 127, 129, 129, 130, 125, 130, 130, 125,
+ 130, 130, 130, 130, 130, 130, 130, 130, 130, 130,
+ 130, 128, 49, 127, 127, 127, 127, 127, 127, 127,
+ 125, 69, 130, 130, 130, 130, 130, 130, 128, 127,
+
+ 127, 127, 127, 127, 125, 130, 130, 130, 130, 130,
+ 131, 127, 127, 132, 130, 130, 131, 127, 127, 132,
+ 130, 130, 127, 130, 0, 125, 125, 125, 125, 125,
+ 125, 125
+ } ;
+
+static yyconst flex_int16_t yy_nxt[490] =
+ { 0,
+ 8, 9, 10, 11, 12, 8, 13, 12, 14, 8,
+ 8, 8, 8, 8, 15, 8, 8, 8, 16, 17,
+ 18, 8, 8, 8, 19, 20, 21, 22, 23, 8,
+ 8, 8, 24, 25, 27, 10, 27, 10, 28, 10,
+ 10, 29, 30, 28, 31, 30, 32, 28, 28, 28,
+ 28, 28, 33, 28, 28, 28, 34, 35, 36, 28,
+ 37, 28, 38, 39, 28, 28, 40, 28, 28, 28,
+ 41, 42, 44, 67, 44, 46, 47, 47, 47, 45,
+ 44, 67, 44, 48, 71, 48, 48, 45, 44, 73,
+ 44, 48, 125, 48, 48, 45, 120, 44, 50, 44,
+
+ 67, 44, 67, 44, 45, 65, 47, 51, 45, 44,
+ 44, 44, 44, 44, 72, 44, 45, 45, 49, 76,
+ 45, 44, 52, 44, 44, 55, 44, 44, 45, 44,
+ 67, 45, 53, 125, 45, 67, 82, 56, 54, 44,
+ 57, 44, 75, 44, 58, 44, 45, 60, 66, 44,
+ 45, 44, 59, 79, 67, 68, 45, 68, 68, 67,
+ 67, 62, 74, 67, 61, 125, 68, 63, 68, 68,
+ 67, 67, 77, 44, 80, 44, 78, 46, 47, 44,
+ 45, 44, 81, 47, 47, 44, 45, 44, 44, 105,
+ 44, 84, 45, 69, 43, 45, 83, 83, 67, 83,
+
+ 83, 83, 67, 86, 65, 47, 83, 83, 83, 83,
+ 83, 85, 44, 44, 44, 44, 44, 94, 44, 45,
+ 45, 43, 44, 45, 44, 44, 44, 44, 44, 45,
+ 67, 125, 45, 45, 99, 87, 44, 44, 44, 44,
+ 44, 56, 44, 45, 45, 67, 44, 45, 44, 88,
+ 93, 67, 68, 45, 68, 68, 67, 67, 67, 95,
+ 67, 67, 106, 67, 91, 89, 58, 67, 67, 90,
+ 92, 92, 67, 92, 92, 92, 96, 78, 97, 76,
+ 92, 92, 92, 92, 92, 67, 44, 44, 44, 44,
+ 44, 67, 44, 45, 45, 100, 44, 45, 44, 107,
+
+ 67, 98, 101, 45, 67, 44, 102, 44, 44, 108,
+ 44, 44, 45, 44, 67, 45, 125, 67, 45, 43,
+ 125, 70, 103, 109, 67, 78, 58, 110, 104, 44,
+ 44, 44, 44, 44, 67, 44, 45, 45, 67, 67,
+ 45, 114, 44, 111, 44, 44, 112, 44, 115, 45,
+ 67, 67, 45, 116, 125, 113, 125, 125, 114, 111,
+ 56, 76, 43, 44, 67, 44, 43, 43, 43, 43,
+ 45, 44, 44, 44, 44, 122, 67, 43, 45, 45,
+ 125, 125, 125, 125, 67, 44, 67, 44, 125, 125,
+ 119, 121, 45, 118, 66, 124, 78, 125, 66, 66,
+
+ 66, 66, 67, 123, 125, 44, 125, 44, 125, 66,
+ 43, 44, 45, 44, 43, 43, 43, 43, 45, 67,
+ 125, 44, 58, 44, 125, 43, 66, 125, 45, 76,
+ 66, 66, 66, 66, 67, 125, 125, 125, 56, 125,
+ 125, 66, 26, 26, 26, 43, 43, 44, 44, 44,
+ 64, 64, 117, 117, 7, 125, 125, 125, 125, 125,
+ 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
+ 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
+ 125, 125, 125, 125, 125, 125, 125, 125, 125
+ } ;
+
+static yyconst flex_int16_t yy_chk[490] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 3, 3, 4, 4, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 8, 35, 8, 9, 9, 10, 10, 8,
+ 12, 33, 12, 12, 33, 12, 12, 12, 13, 35,
+ 13, 13, 14, 13, 13, 13, 132, 15, 14, 15,
+
+ 34, 16, 38, 16, 15, 27, 27, 15, 16, 17,
+ 18, 17, 18, 19, 34, 19, 17, 18, 13, 38,
+ 19, 20, 16, 20, 21, 18, 21, 22, 20, 22,
+ 37, 21, 17, 45, 22, 40, 45, 19, 17, 23,
+ 20, 23, 37, 24, 20, 24, 23, 22, 130, 25,
+ 24, 25, 21, 40, 36, 30, 25, 30, 30, 30,
+ 39, 24, 36, 41, 23, 99, 31, 25, 31, 31,
+ 31, 42, 39, 43, 41, 43, 39, 46, 46, 51,
+ 43, 51, 42, 47, 47, 52, 51, 52, 53, 91,
+ 53, 51, 52, 31, 49, 53, 49, 49, 72, 49,
+
+ 49, 49, 78, 53, 65, 65, 49, 49, 49, 49,
+ 49, 52, 54, 55, 54, 55, 56, 72, 56, 54,
+ 55, 49, 57, 56, 57, 58, 59, 58, 59, 57,
+ 76, 82, 58, 59, 82, 55, 60, 61, 60, 61,
+ 62, 57, 62, 60, 61, 71, 63, 62, 63, 59,
+ 71, 73, 68, 63, 68, 68, 68, 79, 80, 73,
+ 93, 74, 93, 75, 67, 61, 62, 77, 66, 63,
+ 69, 69, 69, 69, 69, 69, 74, 80, 79, 77,
+ 69, 69, 69, 69, 69, 81, 84, 85, 84, 85,
+ 86, 94, 86, 84, 85, 84, 87, 86, 87, 94,
+
+ 95, 81, 85, 87, 96, 88, 86, 88, 89, 95,
+ 89, 90, 88, 90, 97, 89, 50, 98, 90, 48,
+ 44, 32, 87, 96, 28, 97, 89, 98, 90, 100,
+ 101, 100, 101, 102, 106, 102, 100, 101, 108, 107,
+ 102, 106, 103, 100, 103, 104, 101, 104, 107, 103,
+ 109, 110, 104, 108, 11, 102, 7, 0, 110, 104,
+ 103, 109, 111, 111, 116, 111, 111, 111, 111, 111,
+ 111, 112, 113, 112, 113, 116, 115, 111, 112, 113,
+ 0, 0, 0, 0, 121, 118, 122, 118, 0, 0,
+ 113, 115, 118, 112, 114, 121, 122, 0, 114, 114,
+
+ 114, 114, 114, 118, 0, 119, 0, 119, 0, 114,
+ 117, 117, 119, 117, 117, 117, 117, 117, 117, 124,
+ 0, 123, 119, 123, 0, 117, 120, 0, 123, 124,
+ 120, 120, 120, 120, 120, 0, 0, 0, 123, 0,
+ 0, 120, 126, 126, 126, 127, 127, 128, 128, 128,
+ 129, 129, 131, 131, 125, 125, 125, 125, 125, 125,
+ 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
+ 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
+ 125, 125, 125, 125, 125, 125, 125, 125, 125
+ } ;
+
+#define REJECT \
+{ \
+*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ \
+yy_cp = yyg->yy_full_match; /* restore poss. backed-over text */ \
+++yyg->yy_lp; \
+goto find_rule; \
+}
+
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+#line 1 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+#line 2 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+
+/*
+ * phpdbg_lexer.l
+ */
+
+#include "phpdbg.h"
+#include "phpdbg_cmd.h"
+#define YYSTYPE phpdbg_param_t
+
+#include "phpdbg_parser.h"
+
+
+
+#define YY_NO_UNISTD_H 1
+#line 629 "sapi/phpdbg/phpdbg_lexer.c"
+
+#define INITIAL 0
+#define RAW 1
+#define NORMAL 2
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+/* Holds the entire state of the reentrant scanner. */
+struct yyguts_t
+ {
+
+ /* User-defined. Not touched by flex. */
+ YY_EXTRA_TYPE yyextra_r;
+
+ /* The rest are the same as the globals declared in the non-reentrant scanner. */
+ FILE *yyin_r, *yyout_r;
+ size_t yy_buffer_stack_top; /**< index of top of stack. */
+ size_t yy_buffer_stack_max; /**< capacity of stack. */
+ YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */
+ char yy_hold_char;
+ yy_size_t yy_n_chars;
+ yy_size_t yyleng_r;
+ char *yy_c_buf_p;
+ int yy_init;
+ int yy_start;
+ int yy_did_buffer_switch_on_eof;
+ int yy_start_stack_ptr;
+ int yy_start_stack_depth;
+ int *yy_start_stack;
+ yy_state_type yy_last_accepting_state;
+ char* yy_last_accepting_cpos;
+
+ int yylineno_r;
+ int yy_flex_debug_r;
+
+ yy_state_type *yy_state_buf;
+ yy_state_type *yy_state_ptr;
+ char *yy_full_match;
+ int yy_lp;
+
+ /* These are only needed for trailing context rules,
+ * but there's no conditional variable for that yet. */
+ int yy_looking_for_trail_begin;
+ int yy_full_lp;
+ int *yy_full_state;
+
+ char *yytext_r;
+ int yy_more_flag;
+ int yy_more_len;
+
+ YYSTYPE * yylval_r;
+
+ }; /* end struct yyguts_t */
+
+static int yy_init_globals (yyscan_t yyscanner );
+
+ /* This must go here because YYSTYPE and YYLTYPE are included
+ * from bison output in section 1.*/
+ # define yylval yyg->yylval_r
+
+int yylex_init (yyscan_t* scanner);
+
+int yylex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner);
+
+/* Accessor methods to globals.
+ These are made visible to non-reentrant scanners for convenience. */
+
+int yylex_destroy (yyscan_t yyscanner );
+
+int yyget_debug (yyscan_t yyscanner );
+
+void yyset_debug (int debug_flag ,yyscan_t yyscanner );
+
+YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner );
+
+void yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner );
+
+FILE *yyget_in (yyscan_t yyscanner );
+
+void yyset_in (FILE * in_str ,yyscan_t yyscanner );
+
+FILE *yyget_out (yyscan_t yyscanner );
+
+void yyset_out (FILE * out_str ,yyscan_t yyscanner );
+
+yy_size_t yyget_leng (yyscan_t yyscanner );
+
+char *yyget_text (yyscan_t yyscanner );
+
+int yyget_lineno (yyscan_t yyscanner );
+
+void yyset_lineno (int line_number ,yyscan_t yyscanner );
+
+int yyget_column (yyscan_t yyscanner );
+
+void yyset_column (int column_no ,yyscan_t yyscanner );
+
+YYSTYPE * yyget_lval (yyscan_t yyscanner );
+
+void yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap (yyscan_t yyscanner );
+#else
+extern int yywrap (yyscan_t yyscanner );
+#endif
+#endif
+
+ static void yyunput (int c,char *buf_ptr ,yyscan_t yyscanner);
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner);
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner);
+#endif
+
+#ifndef YY_NO_INPUT
+
+#ifdef __cplusplus
+static int yyinput (yyscan_t yyscanner );
+#else
+static int input (yyscan_t yyscanner );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO do { if (fwrite( yytext, yyleng, 1, yyout )) {} } while (0)
+#endif
+
+/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+ { \
+ int c = '*'; \
+ size_t n; \
+ for ( n = 0; n < max_size && \
+ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+ buf[n] = (char) c; \
+ if ( c == '\n' ) \
+ buf[n++] = (char) c; \
+ if ( c == EOF && ferror( yyin ) ) \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ result = n; \
+ } \
+ else \
+ { \
+ errno=0; \
+ while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \
+ { \
+ if( errno != EINTR) \
+ { \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ break; \
+ } \
+ errno=0; \
+ clearerr(yyin); \
+ } \
+ }\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner)
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int yylex \
+ (YYSTYPE * yylval_param ,yyscan_t yyscanner);
+
+#define YY_DECL int yylex \
+ (YYSTYPE * yylval_param , yyscan_t yyscanner)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+ YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp, *yy_bp;
+ register int yy_act;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+#line 44 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+
+
+#line 882 "sapi/phpdbg/phpdbg_lexer.c"
+
+ yylval = yylval_param;
+
+ if ( !yyg->yy_init )
+ {
+ yyg->yy_init = 1;
+
+#ifdef YY_USER_INIT
+ YY_USER_INIT;
+#endif
+
+ /* Create the reject buffer large enough to save one state per allowed character. */
+ if ( ! yyg->yy_state_buf )
+ yyg->yy_state_buf = (yy_state_type *)yyalloc(YY_STATE_BUF_SIZE ,yyscanner);
+ if ( ! yyg->yy_state_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yylex()" );
+
+ if ( ! yyg->yy_start )
+ yyg->yy_start = 1; /* first start state */
+
+ if ( ! yyin )
+ yyin = stdin;
+
+ if ( ! yyout )
+ yyout = stdout;
+
+ if ( ! YY_CURRENT_BUFFER ) {
+ yyensure_buffer_stack (yyscanner);
+ YY_CURRENT_BUFFER_LVALUE =
+ yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner);
+ }
+
+ yy_load_buffer_state(yyscanner );
+ }
+
+ while ( 1 ) /* loops until end-of-file is reached */
+ {
+ yy_cp = yyg->yy_c_buf_p;
+
+ /* Support of yytext. */
+ *yy_cp = yyg->yy_hold_char;
+
+ /* yy_bp points to the position in yy_ch_buf of the start of
+ * the current run.
+ */
+ yy_bp = yy_cp;
+
+ yy_current_state = yyg->yy_start;
+
+ yyg->yy_state_ptr = yyg->yy_state_buf;
+ *yyg->yy_state_ptr++ = yy_current_state;
+
+yy_match:
+ do
+ {
+ register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 126 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ *yyg->yy_state_ptr++ = yy_current_state;
+ ++yy_cp;
+ }
+ while ( yy_current_state != 125 );
+
+yy_find_action:
+ yy_current_state = *--yyg->yy_state_ptr;
+ yyg->yy_lp = yy_accept[yy_current_state];
+find_rule: /* we branch to this label when backing up */
+ for ( ; ; ) /* until we find what rule we matched */
+ {
+ if ( yyg->yy_lp && yyg->yy_lp < yy_accept[yy_current_state + 1] )
+ {
+ yy_act = yy_acclist[yyg->yy_lp];
+ {
+ yyg->yy_full_match = yy_cp;
+ break;
+ }
+ }
+ --yy_cp;
+ yy_current_state = *--yyg->yy_state_ptr;
+ yyg->yy_lp = yy_accept[yy_current_state];
+ }
+
+ YY_DO_BEFORE_ACTION;
+
+do_action: /* This label is used only to access EOF actions. */
+
+ switch ( yy_act )
+ { /* beginning of action switch */
+
+case 1:
+YY_RULE_SETUP
+#line 47 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+{
+ BEGIN(RAW);
+ phpdbg_init_param(yylval, EMPTY_PARAM);
+ return T_EVAL;
+ }
+ YY_BREAK
+case 2:
+YY_RULE_SETUP
+#line 52 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+{
+ BEGIN(RAW);
+ phpdbg_init_param(yylval, EMPTY_PARAM);
+ return T_SHELL;
+ }
+ YY_BREAK
+case 3:
+YY_RULE_SETUP
+#line 57 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+{
+ BEGIN(RAW);
+ phpdbg_init_param(yylval, EMPTY_PARAM);
+ return T_RUN;
+ }
+ YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 63 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+{
+ BEGIN(NORMAL);
+ REJECT;
+ }
+ YY_BREAK
+
+
+case 5:
+YY_RULE_SETUP
+#line 70 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+{
+ BEGIN(RAW);
+ phpdbg_init_param(yylval, EMPTY_PARAM);
+ return T_IF;
+ }
+ YY_BREAK
+
+
+case 6:
+YY_RULE_SETUP
+#line 78 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+{
+ phpdbg_init_param(yylval, STR_PARAM);
+ yylval->str = zend_strndup(yytext, yyleng);
+ yylval->len = yyleng;
+ return T_PROTO;
+ }
+ YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 84 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+{ return T_POUND; }
+ YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 85 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+{ return T_DCOLON; }
+ YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 86 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+{ return T_COLON; }
+ YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 88 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+{
+ phpdbg_init_param(yylval, NUMERIC_PARAM);
+ yylval->num = 1;
+ return T_TRUTHY;
+ }
+ YY_BREAK
+case 11:
+YY_RULE_SETUP
+#line 93 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+{
+ phpdbg_init_param(yylval, NUMERIC_PARAM);
+ yylval->num = 0;
+ return T_FALSY;
+ }
+ YY_BREAK
+case 12:
+YY_RULE_SETUP
+#line 98 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+{
+ phpdbg_init_param(yylval, NUMERIC_PARAM);
+ yylval->num = atoi(yytext);
+ return T_DIGITS;
+ }
+ YY_BREAK
+case 13:
+YY_RULE_SETUP
+#line 103 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+{
+ phpdbg_init_param(yylval, ADDR_PARAM);
+ yylval->addr = strtoul(yytext, 0, 16);
+ return T_ADDR;
+ }
+ YY_BREAK
+case 14:
+YY_RULE_SETUP
+#line 108 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+{
+ phpdbg_init_param(yylval, OP_PARAM);
+ yylval->str = zend_strndup(yytext, yyleng);
+ yylval->len = yyleng;
+ return T_OPCODE;
+ }
+ YY_BREAK
+case 15:
+YY_RULE_SETUP
+#line 114 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+{
+ phpdbg_init_param(yylval, STR_PARAM);
+ yylval->str = zend_strndup(yytext, yyleng);
+ yylval->len = yyleng;
+ return T_ID;
+ }
+ YY_BREAK
+
+case 16:
+YY_RULE_SETUP
+#line 122 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+{
+ phpdbg_init_param(yylval, STR_PARAM);
+ yylval->str = zend_strndup(yytext, yyleng);
+ yylval->len = yyleng;
+ BEGIN(INITIAL);
+ return T_INPUT;
+}
+ YY_BREAK
+case 17:
+/* rule 17 can match eol */
+YY_RULE_SETUP
+#line 130 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+{ /* ignore whitespace */ }
+ YY_BREAK
+case 18:
+YY_RULE_SETUP
+#line 131 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+YY_FATAL_ERROR( "flex scanner jammed" );
+ YY_BREAK
+#line 1129 "sapi/phpdbg/phpdbg_lexer.c"
+ case YY_STATE_EOF(INITIAL):
+ case YY_STATE_EOF(RAW):
+ case YY_STATE_EOF(NORMAL):
+ yyterminate();
+
+ case YY_END_OF_BUFFER:
+ {
+ /* Amount of text matched not including the EOB char. */
+ int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1;
+
+ /* Undo the effects of YY_DO_BEFORE_ACTION. */
+ *yy_cp = yyg->yy_hold_char;
+ YY_RESTORE_YY_MORE_OFFSET
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+ {
+ /* We're scanning a new file or input source. It's
+ * possible that this happened because the user
+ * just pointed yyin at a new source and called
+ * yylex(). If so, then we have to assure
+ * consistency between YY_CURRENT_BUFFER and our
+ * globals. Here is the right place to do so, because
+ * this is the first action (other than possibly a
+ * back-up) that will match for the new input source.
+ */
+ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+ }
+
+ /* Note that here we test for yy_c_buf_p "<=" to the position
+ * of the first EOB in the buffer, since yy_c_buf_p will
+ * already have been incremented past the NUL character
+ * (since all states make transitions on EOB to the
+ * end-of-buffer state). Contrast this with the test
+ * in input().
+ */
+ if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+ { /* This was really a NUL. */
+ yy_state_type yy_next_state;
+
+ yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( yyscanner );
+
+ /* Okay, we're now positioned to make the NUL
+ * transition. We couldn't have
+ * yy_get_previous_state() go ahead and do it
+ * for us because it doesn't know how to deal
+ * with the possibility of jamming (and we don't
+ * want to build jamming into it because then it
+ * will run more slowly).
+ */
+
+ yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner);
+
+ yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+
+ if ( yy_next_state )
+ {
+ /* Consume the NUL. */
+ yy_cp = ++yyg->yy_c_buf_p;
+ yy_current_state = yy_next_state;
+ goto yy_match;
+ }
+
+ else
+ {
+ yy_cp = yyg->yy_c_buf_p;
+ goto yy_find_action;
+ }
+ }
+
+ else switch ( yy_get_next_buffer( yyscanner ) )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ yyg->yy_did_buffer_switch_on_eof = 0;
+
+ if ( yywrap(yyscanner ) )
+ {
+ /* Note: because we've taken care in
+ * yy_get_next_buffer() to have set up
+ * yytext, we can now set up
+ * yy_c_buf_p so that if some total
+ * hoser (like flex itself) wants to
+ * call the scanner after we return the
+ * YY_NULL, it'll still work - another
+ * YY_NULL will get returned.
+ */
+ yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ;
+
+ yy_act = YY_STATE_EOF(YY_START);
+ goto do_action;
+ }
+
+ else
+ {
+ if ( ! yyg->yy_did_buffer_switch_on_eof )
+ YY_NEW_FILE;
+ }
+ break;
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yyg->yy_c_buf_p =
+ yyg->yytext_ptr + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( yyscanner );
+
+ yy_cp = yyg->yy_c_buf_p;
+ yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+ goto yy_match;
+
+ case EOB_ACT_LAST_MATCH:
+ yyg->yy_c_buf_p =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars];
+
+ yy_current_state = yy_get_previous_state( yyscanner );
+
+ yy_cp = yyg->yy_c_buf_p;
+ yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+ goto yy_find_action;
+ }
+ break;
+ }
+
+ default:
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--no action found" );
+ } /* end of action switch */
+ } /* end of scanning one token */
+} /* end of yylex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ * EOB_ACT_LAST_MATCH -
+ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ * EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+ register char *source = yyg->yytext_ptr;
+ register int number_to_move, i;
+ int ret_val;
+
+ if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] )
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--end of buffer missed" );
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+ { /* Don't try to fill the buffer, so this is an EOF. */
+ if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 )
+ {
+ /* We matched a single character, the EOB, so
+ * treat this as a final EOF.
+ */
+ return EOB_ACT_END_OF_FILE;
+ }
+
+ else
+ {
+ /* We matched some text prior to the EOB, first
+ * process it.
+ */
+ return EOB_ACT_LAST_MATCH;
+ }
+ }
+
+ /* Try to read more data. */
+
+ /* First move last chars to start of buffer. */
+ number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1;
+
+ for ( i = 0; i < number_to_move; ++i )
+ *(dest++) = *(source++);
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+ /* don't do the read, it's not guaranteed to return an EOF,
+ * just force an EOF
+ */
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0;
+
+ else
+ {
+ yy_size_t num_to_read =
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+ while ( num_to_read <= 0 )
+ { /* Not enough room in the buffer - grow it. */
+
+ YY_FATAL_ERROR(
+"input buffer overflow, can't enlarge buffer because scanner uses REJECT" );
+
+ }
+
+ if ( num_to_read > YY_READ_BUF_SIZE )
+ num_to_read = YY_READ_BUF_SIZE;
+
+ /* Read in more data. */
+ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+ yyg->yy_n_chars, num_to_read );
+
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+
+ if ( yyg->yy_n_chars == 0 )
+ {
+ if ( number_to_move == YY_MORE_ADJ )
+ {
+ ret_val = EOB_ACT_END_OF_FILE;
+ yyrestart(yyin ,yyscanner);
+ }
+
+ else
+ {
+ ret_val = EOB_ACT_LAST_MATCH;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+ YY_BUFFER_EOF_PENDING;
+ }
+ }
+
+ else
+ ret_val = EOB_ACT_CONTINUE_SCAN;
+
+ if ((yy_size_t) (yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+ /* Extend the array by 50%, plus the number we really need. */
+ yy_size_t new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1);
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ,yyscanner );
+ if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+ }
+
+ yyg->yy_n_chars += number_to_move;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;
+
+ yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+ return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+ static yy_state_type yy_get_previous_state (yyscan_t yyscanner)
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ yy_current_state = yyg->yy_start;
+
+ yyg->yy_state_ptr = yyg->yy_state_buf;
+ *yyg->yy_state_ptr++ = yy_current_state;
+
+ for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp )
+ {
+ register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 126 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ *yyg->yy_state_ptr++ = yy_current_state;
+ }
+
+ return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ * next_state = yy_try_NUL_trans( current_state );
+ */
+ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner)
+{
+ register int yy_is_jam;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */
+
+ register YY_CHAR yy_c = 1;
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 126 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ yy_is_jam = (yy_current_state == 125);
+ if ( ! yy_is_jam )
+ *yyg->yy_state_ptr++ = yy_current_state;
+
+ (void)yyg;
+ return yy_is_jam ? 0 : yy_current_state;
+}
+
+ static void yyunput (int c, register char * yy_bp , yyscan_t yyscanner)
+{
+ register char *yy_cp;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ yy_cp = yyg->yy_c_buf_p;
+
+ /* undo effects of setting up yytext */
+ *yy_cp = yyg->yy_hold_char;
+
+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+ { /* need to shift things up to make room */
+ /* +2 for EOB chars. */
+ register yy_size_t number_to_move = yyg->yy_n_chars + 2;
+ register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2];
+ register char *source =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move];
+
+ while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ *--dest = *--source;
+
+ yy_cp += (int) (dest - source);
+ yy_bp += (int) (dest - source);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars =
+ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_buf_size;
+
+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+ YY_FATAL_ERROR( "flex scanner push-back overflow" );
+ }
+
+ *--yy_cp = (char) c;
+
+ yyg->yytext_ptr = yy_bp;
+ yyg->yy_hold_char = *yy_cp;
+ yyg->yy_c_buf_p = yy_cp;
+}
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+ static int yyinput (yyscan_t yyscanner)
+#else
+ static int input (yyscan_t yyscanner)
+#endif
+
+{
+ int c;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ *yyg->yy_c_buf_p = yyg->yy_hold_char;
+
+ if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR )
+ {
+ /* yy_c_buf_p now points to the character we want to return.
+ * If this occurs *before* the EOB characters, then it's a
+ * valid NUL; if not, then we've hit the end of the buffer.
+ */
+ if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+ /* This was really a NUL. */
+ *yyg->yy_c_buf_p = '\0';
+
+ else
+ { /* need more input */
+ yy_size_t offset = yyg->yy_c_buf_p - yyg->yytext_ptr;
+ ++yyg->yy_c_buf_p;
+
+ switch ( yy_get_next_buffer( yyscanner ) )
+ {
+ case EOB_ACT_LAST_MATCH:
+ /* This happens because yy_g_n_b()
+ * sees that we've accumulated a
+ * token and flags that we need to
+ * try matching the token before
+ * proceeding. But for input(),
+ * there's no matching to consider.
+ * So convert the EOB_ACT_LAST_MATCH
+ * to EOB_ACT_END_OF_FILE.
+ */
+
+ /* Reset buffer status. */
+ yyrestart(yyin ,yyscanner);
+
+ /*FALLTHROUGH*/
+
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( yywrap(yyscanner ) )
+ return EOF;
+
+ if ( ! yyg->yy_did_buffer_switch_on_eof )
+ YY_NEW_FILE;
+#ifdef __cplusplus
+ return yyinput(yyscanner);
+#else
+ return input(yyscanner);
+#endif
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yyg->yy_c_buf_p = yyg->yytext_ptr + offset;
+ break;
+ }
+ }
+ }
+
+ c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */
+ *yyg->yy_c_buf_p = '\0'; /* preserve yytext */
+ yyg->yy_hold_char = *++yyg->yy_c_buf_p;
+
+ return c;
+}
+#endif /* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ * @param yyscanner The scanner object.
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+ void yyrestart (FILE * input_file , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if ( ! YY_CURRENT_BUFFER ){
+ yyensure_buffer_stack (yyscanner);
+ YY_CURRENT_BUFFER_LVALUE =
+ yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner);
+ }
+
+ yy_init_buffer(YY_CURRENT_BUFFER,input_file ,yyscanner);
+ yy_load_buffer_state(yyscanner );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ * @param yyscanner The scanner object.
+ */
+ void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* TODO. We should be able to replace this entire function body
+ * with
+ * yypop_buffer_state();
+ * yypush_buffer_state(new_buffer);
+ */
+ yyensure_buffer_stack (yyscanner);
+ if ( YY_CURRENT_BUFFER == new_buffer )
+ return;
+
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *yyg->yy_c_buf_p = yyg->yy_hold_char;
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+ yy_load_buffer_state(yyscanner );
+
+ /* We don't actually know whether we did this switch during
+ * EOF (yywrap()) processing, but the only time this flag
+ * is looked at is after yywrap() is called, so it's safe
+ * to go ahead and always set it.
+ */
+ yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+static void yy_load_buffer_state (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+ yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+ yyg->yy_hold_char = *yyg->yy_c_buf_p;
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ * @param yyscanner The scanner object.
+ * @return the allocated buffer state.
+ */
+ YY_BUFFER_STATE yy_create_buffer (FILE * file, int size , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+
+ b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ b->yy_buf_size = size;
+
+ /* yy_ch_buf has to be 2 characters longer than the size given because
+ * we need to put in 2 end-of-buffer characters.
+ */
+ b->yy_ch_buf = (char *) yyalloc(b->yy_buf_size + 2 ,yyscanner );
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ b->yy_is_our_buffer = 1;
+
+ yy_init_buffer(b,file ,yyscanner);
+
+ return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with yy_create_buffer()
+ * @param yyscanner The scanner object.
+ */
+ void yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if ( ! b )
+ return;
+
+ if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+ if ( b->yy_is_our_buffer )
+ yyfree((void *) b->yy_ch_buf ,yyscanner );
+
+ yyfree((void *) b ,yyscanner );
+}
+
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a yyrestart() or at EOF.
+ */
+ static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner)
+
+{
+ int oerrno = errno;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ yy_flush_buffer(b ,yyscanner);
+
+ b->yy_input_file = file;
+ b->yy_fill_buffer = 1;
+
+ /* If b is the current buffer, then yy_init_buffer was _probably_
+ * called from yyrestart() or through yy_get_next_buffer.
+ * In that case, we don't want to reset the lineno or column.
+ */
+ if (b != YY_CURRENT_BUFFER){
+ b->yy_bs_lineno = 1;
+ b->yy_bs_column = 0;
+ }
+
+ b->yy_is_interactive = 0;
+
+ errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ * @param yyscanner The scanner object.
+ */
+ void yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if ( ! b )
+ return;
+
+ b->yy_n_chars = 0;
+
+ /* We always need two end-of-buffer characters. The first causes
+ * a transition to the end-of-buffer state. The second causes
+ * a jam in that state.
+ */
+ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+ b->yy_buf_pos = &b->yy_ch_buf[0];
+
+ b->yy_at_bol = 1;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ if ( b == YY_CURRENT_BUFFER )
+ yy_load_buffer_state(yyscanner );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ * the current state. This function will allocate the stack
+ * if necessary.
+ * @param new_buffer The new state.
+ * @param yyscanner The scanner object.
+ */
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if (new_buffer == NULL)
+ return;
+
+ yyensure_buffer_stack(yyscanner);
+
+ /* This block is copied from yy_switch_to_buffer. */
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *yyg->yy_c_buf_p = yyg->yy_hold_char;
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+
+ /* Only push if top exists. Otherwise, replace top. */
+ if (YY_CURRENT_BUFFER)
+ yyg->yy_buffer_stack_top++;
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+ /* copied from yy_switch_to_buffer. */
+ yy_load_buffer_state(yyscanner );
+ yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ * The next element becomes the new top.
+ * @param yyscanner The scanner object.
+ */
+void yypop_buffer_state (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if (!YY_CURRENT_BUFFER)
+ return;
+
+ yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner);
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ if (yyg->yy_buffer_stack_top > 0)
+ --yyg->yy_buffer_stack_top;
+
+ if (YY_CURRENT_BUFFER) {
+ yy_load_buffer_state(yyscanner );
+ yyg->yy_did_buffer_switch_on_eof = 1;
+ }
+}
+
+/* Allocates the stack if it does not exist.
+ * Guarantees space for at least one push.
+ */
+static void yyensure_buffer_stack (yyscan_t yyscanner)
+{
+ yy_size_t num_to_alloc;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if (!yyg->yy_buffer_stack) {
+
+ /* First allocation is just for 2 elements, since we don't know if this
+ * scanner will even need a stack. We use 2 instead of 1 to avoid an
+ * immediate realloc on the next call.
+ */
+ num_to_alloc = 1;
+ yyg->yy_buffer_stack = (struct yy_buffer_state**)yyalloc
+ (num_to_alloc * sizeof(struct yy_buffer_state*)
+ , yyscanner);
+ if ( ! yyg->yy_buffer_stack )
+ YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+
+ memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+
+ yyg->yy_buffer_stack_max = num_to_alloc;
+ yyg->yy_buffer_stack_top = 0;
+ return;
+ }
+
+ if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){
+
+ /* Increase the buffer to prepare for a possible push. */
+ int grow_size = 8 /* arbitrary grow size */;
+
+ num_to_alloc = yyg->yy_buffer_stack_max + grow_size;
+ yyg->yy_buffer_stack = (struct yy_buffer_state**)yyrealloc
+ (yyg->yy_buffer_stack,
+ num_to_alloc * sizeof(struct yy_buffer_state*)
+ , yyscanner);
+ if ( ! yyg->yy_buffer_stack )
+ YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+
+ /* zero only the new slots.*/
+ memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*));
+ yyg->yy_buffer_stack_max = num_to_alloc;
+ }
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+
+ if ( size < 2 ||
+ base[size-2] != YY_END_OF_BUFFER_CHAR ||
+ base[size-1] != YY_END_OF_BUFFER_CHAR )
+ /* They forgot to leave room for the EOB's. */
+ return 0;
+
+ b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" );
+
+ b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */
+ b->yy_buf_pos = b->yy_ch_buf = base;
+ b->yy_is_our_buffer = 0;
+ b->yy_input_file = 0;
+ b->yy_n_chars = b->yy_buf_size;
+ b->yy_is_interactive = 0;
+ b->yy_at_bol = 1;
+ b->yy_fill_buffer = 0;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ yy_switch_to_buffer(b ,yyscanner );
+
+ return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to yylex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ * yy_scan_bytes() instead.
+ */
+YY_BUFFER_STATE yy_scan_string (yyconst char * yystr , yyscan_t yyscanner)
+{
+
+ return yy_scan_bytes(yystr,strlen(yystr) ,yyscanner);
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to yylex() will
+ * scan from a @e copy of @a bytes.
+ * @param yybytes the byte buffer to scan
+ * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes.
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_bytes (yyconst char * yybytes, yy_size_t _yybytes_len , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+ char *buf;
+ yy_size_t n;
+ yy_size_t i;
+
+ /* Get memory for full buffer, including space for trailing EOB's. */
+ n = _yybytes_len + 2;
+ buf = (char *) yyalloc(n ,yyscanner );
+ if ( ! buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
+
+ for ( i = 0; i < _yybytes_len; ++i )
+ buf[i] = yybytes[i];
+
+ buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+ b = yy_scan_buffer(buf,n ,yyscanner);
+ if ( ! b )
+ YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" );
+
+ /* It's okay to grow etc. this buffer, and we should throw it
+ * away when we're done.
+ */
+ b->yy_is_our_buffer = 1;
+
+ return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner)
+{
+ (void) fprintf( stderr, "%s\n", msg );
+ exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ yytext[yyleng] = yyg->yy_hold_char; \
+ yyg->yy_c_buf_p = yytext + yyless_macro_arg; \
+ yyg->yy_hold_char = *yyg->yy_c_buf_p; \
+ *yyg->yy_c_buf_p = '\0'; \
+ yyleng = yyless_macro_arg; \
+ } \
+ while ( 0 )
+
+/* Accessor methods (get/set functions) to struct members. */
+
+/** Get the user-defined data for this scanner.
+ * @param yyscanner The scanner object.
+ */
+YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyextra;
+}
+
+/** Get the current line number.
+ * @param yyscanner The scanner object.
+ */
+int yyget_lineno (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if (! YY_CURRENT_BUFFER)
+ return 0;
+
+ return yylineno;
+}
+
+/** Get the current column number.
+ * @param yyscanner The scanner object.
+ */
+int yyget_column (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if (! YY_CURRENT_BUFFER)
+ return 0;
+
+ return yycolumn;
+}
+
+/** Get the input stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *yyget_in (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyin;
+}
+
+/** Get the output stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *yyget_out (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyout;
+}
+
+/** Get the length of the current token.
+ * @param yyscanner The scanner object.
+ */
+yy_size_t yyget_leng (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyleng;
+}
+
+/** Get the current token.
+ * @param yyscanner The scanner object.
+ */
+
+char *yyget_text (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yytext;
+}
+
+/** Set the user-defined data. This data is never touched by the scanner.
+ * @param user_defined The data to be associated with this scanner.
+ * @param yyscanner The scanner object.
+ */
+void yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyextra = user_defined ;
+}
+
+/** Set the current line number.
+ * @param line_number
+ * @param yyscanner The scanner object.
+ */
+void yyset_lineno (int line_number , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* lineno is only valid if an input buffer exists. */
+ if (! YY_CURRENT_BUFFER )
+ YY_FATAL_ERROR( "yyset_lineno called with no buffer" );
+
+ yylineno = line_number;
+}
+
+/** Set the current column.
+ * @param line_number
+ * @param yyscanner The scanner object.
+ */
+void yyset_column (int column_no , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* column is only valid if an input buffer exists. */
+ if (! YY_CURRENT_BUFFER )
+ YY_FATAL_ERROR( "yyset_column called with no buffer" );
+
+ yycolumn = column_no;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ * @param yyscanner The scanner object.
+ * @see yy_switch_to_buffer
+ */
+void yyset_in (FILE * in_str , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyin = in_str ;
+}
+
+void yyset_out (FILE * out_str , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyout = out_str ;
+}
+
+int yyget_debug (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yy_flex_debug;
+}
+
+void yyset_debug (int bdebug , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yy_flex_debug = bdebug ;
+}
+
+/* Accessor methods for yylval and yylloc */
+
+YYSTYPE * yyget_lval (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yylval;
+}
+
+void yyset_lval (YYSTYPE * yylval_param , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yylval = yylval_param;
+}
+
+/* User-visible API */
+
+/* yylex_init is special because it creates the scanner itself, so it is
+ * the ONLY reentrant function that doesn't take the scanner as the last argument.
+ * That's why we explicitly handle the declaration, instead of using our macros.
+ */
+
+int yylex_init(yyscan_t* ptr_yy_globals)
+
+{
+ if (ptr_yy_globals == NULL){
+ errno = EINVAL;
+ return 1;
+ }
+
+ *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), NULL );
+
+ if (*ptr_yy_globals == NULL){
+ errno = ENOMEM;
+ return 1;
+ }
+
+ /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */
+ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
+
+ return yy_init_globals ( *ptr_yy_globals );
+}
+
+/* yylex_init_extra has the same functionality as yylex_init, but follows the
+ * convention of taking the scanner as the last argument. Note however, that
+ * this is a *pointer* to a scanner, as it will be allocated by this call (and
+ * is the reason, too, why this function also must handle its own declaration).
+ * The user defined value in the first argument will be available to yyalloc in
+ * the yyextra field.
+ */
+
+int yylex_init_extra(YY_EXTRA_TYPE yy_user_defined,yyscan_t* ptr_yy_globals )
+
+{
+ struct yyguts_t dummy_yyguts;
+
+ yyset_extra (yy_user_defined, &dummy_yyguts);
+
+ if (ptr_yy_globals == NULL){
+ errno = EINVAL;
+ return 1;
+ }
+
+ *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts );
+
+ if (*ptr_yy_globals == NULL){
+ errno = ENOMEM;
+ return 1;
+ }
+
+ /* By setting to 0xAA, we expose bugs in
+ yy_init_globals. Leave at 0x00 for releases. */
+ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
+
+ yyset_extra (yy_user_defined, *ptr_yy_globals);
+
+ return yy_init_globals ( *ptr_yy_globals );
+}
+
+static int yy_init_globals (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ /* Initialization is the same as for the non-reentrant scanner.
+ * This function is called from yylex_destroy(), so don't allocate here.
+ */
+
+ yyg->yy_buffer_stack = 0;
+ yyg->yy_buffer_stack_top = 0;
+ yyg->yy_buffer_stack_max = 0;
+ yyg->yy_c_buf_p = (char *) 0;
+ yyg->yy_init = 0;
+ yyg->yy_start = 0;
+
+ yyg->yy_start_stack_ptr = 0;
+ yyg->yy_start_stack_depth = 0;
+ yyg->yy_start_stack = NULL;
+
+ yyg->yy_state_buf = 0;
+ yyg->yy_state_ptr = 0;
+ yyg->yy_full_match = 0;
+ yyg->yy_lp = 0;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+ yyin = stdin;
+ yyout = stdout;
+#else
+ yyin = (FILE *) 0;
+ yyout = (FILE *) 0;
+#endif
+
+ /* For future reference: Set errno on error, since we are called by
+ * yylex_init()
+ */
+ return 0;
+}
+
+/* yylex_destroy is for both reentrant and non-reentrant scanners. */
+int yylex_destroy (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* Pop the buffer stack, destroying each element. */
+ while(YY_CURRENT_BUFFER){
+ yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ yypop_buffer_state(yyscanner);
+ }
+
+ /* Destroy the stack itself. */
+ yyfree(yyg->yy_buffer_stack ,yyscanner);
+ yyg->yy_buffer_stack = NULL;
+
+ /* Destroy the start condition stack. */
+ yyfree(yyg->yy_start_stack ,yyscanner );
+ yyg->yy_start_stack = NULL;
+
+ yyfree ( yyg->yy_state_buf , yyscanner);
+ yyg->yy_state_buf = NULL;
+
+ /* Reset the globals. This is important in a non-reentrant scanner so the next time
+ * yylex() is called, initialization will occur. */
+ yy_init_globals( yyscanner);
+
+ /* Destroy the main struct (reentrant only). */
+ yyfree ( yyscanner , yyscanner );
+ yyscanner = NULL;
+ return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner)
+{
+ register int i;
+ for ( i = 0; i < n; ++i )
+ s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner)
+{
+ register int n;
+ for ( n = 0; s[n]; ++n )
+ ;
+
+ return n;
+}
+#endif
+
+void *yyalloc (yy_size_t size , yyscan_t yyscanner)
+{
+ return (void *) malloc( size );
+}
+
+void *yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner)
+{
+ /* The cast to (char *) in the following accommodates both
+ * implementations that use char* generic pointers, and those
+ * that use void* generic pointers. It works with the latter
+ * because both ANSI C and C++ allow castless assignment from
+ * any pointer type to void*, and deal with argument conversions
+ * as though doing an assignment.
+ */
+ return (void *) realloc( (char *) ptr, size );
+}
+
+void yyfree (void * ptr , yyscan_t yyscanner)
+{
+ free( (char *) ptr ); /* see yyrealloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+#line 131 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+
+
+
diff --git a/sapi/phpdbg/phpdbg_lexer.h b/sapi/phpdbg/phpdbg_lexer.h
new file mode 100644
index 0000000000..1958cef9a2
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_lexer.h
@@ -0,0 +1,348 @@
+#ifndef yyHEADER_H
+#define yyHEADER_H 1
+#define yyIN_HEADER 1
+
+#line 6 "sapi/phpdbg/phpdbg_lexer.h"
+
+#line 8 "sapi/phpdbg/phpdbg_lexer.h"
+
+#define YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 37
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types.
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t;
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX (4294967295U)
+#endif
+
+#endif /* ! C99 */
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else /* ! __cplusplus */
+
+/* C99 requires __STDC__ to be defined as 1. */
+#if defined (__STDC__)
+
+#define YY_USE_CONST
+
+#endif /* defined (__STDC__) */
+#endif /* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* An opaque pointer. */
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+
+/* For convenience, these vars (plus the bison vars far below)
+ are macros in the reentrant scanner. */
+#define yyin yyg->yyin_r
+#define yyout yyg->yyout_r
+#define yyextra yyg->yyextra_r
+#define yyleng yyg->yyleng_r
+#define yytext yyg->yytext_r
+#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno)
+#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column)
+#define yy_flex_debug yyg->yy_flex_debug_r
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#define YY_BUF_SIZE 16384
+#endif
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+ {
+ FILE *yy_input_file;
+
+ char *yy_ch_buf; /* input buffer */
+ char *yy_buf_pos; /* current position in input buffer */
+
+ /* Size of input buffer in bytes, not including room for EOB
+ * characters.
+ */
+ yy_size_t yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ yy_size_t yy_n_chars;
+
+ /* Whether we "own" the buffer - i.e., we know we created it,
+ * and can realloc() it to grow it, and should free() it to
+ * delete it.
+ */
+ int yy_is_our_buffer;
+
+ /* Whether this is an "interactive" input source; if so, and
+ * if we're using stdio for input, then we want to use getc()
+ * instead of fread(), to make sure we stop fetching input after
+ * each newline.
+ */
+ int yy_is_interactive;
+
+ /* Whether we're considered to be at the beginning of a line.
+ * If so, '^' rules will be active on the next match, otherwise
+ * not.
+ */
+ int yy_at_bol;
+
+ int yy_bs_lineno; /**< The line count. */
+ int yy_bs_column; /**< The column count. */
+
+ /* Whether to try to fill the input buffer when we reach the
+ * end of it.
+ */
+ int yy_fill_buffer;
+
+ int yy_buffer_status;
+
+ };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+void yyrestart (FILE *input_file ,yyscan_t yyscanner );
+void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+YY_BUFFER_STATE yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner );
+void yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+void yypop_buffer_state (yyscan_t yyscanner );
+
+YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner );
+YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner );
+YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,yy_size_t len ,yyscan_t yyscanner );
+
+void *yyalloc (yy_size_t ,yyscan_t yyscanner );
+void *yyrealloc (void *,yy_size_t ,yyscan_t yyscanner );
+void yyfree (void * ,yyscan_t yyscanner );
+
+/* Begin user sect3 */
+
+#define yywrap(yyscanner) 1
+#define YY_SKIP_YYWRAP
+
+#define yytext_ptr yytext_r
+
+#ifdef YY_HEADER_EXPORT_START_CONDITIONS
+#define INITIAL 0
+#define RAW 1
+#define NORMAL 2
+
+#endif
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+int yylex_init (yyscan_t* scanner);
+
+int yylex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner);
+
+/* Accessor methods to globals.
+ These are made visible to non-reentrant scanners for convenience. */
+
+int yylex_destroy (yyscan_t yyscanner );
+
+int yyget_debug (yyscan_t yyscanner );
+
+void yyset_debug (int debug_flag ,yyscan_t yyscanner );
+
+YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner );
+
+void yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner );
+
+FILE *yyget_in (yyscan_t yyscanner );
+
+void yyset_in (FILE * in_str ,yyscan_t yyscanner );
+
+FILE *yyget_out (yyscan_t yyscanner );
+
+void yyset_out (FILE * out_str ,yyscan_t yyscanner );
+
+yy_size_t yyget_leng (yyscan_t yyscanner );
+
+char *yyget_text (yyscan_t yyscanner );
+
+int yyget_lineno (yyscan_t yyscanner );
+
+void yyset_lineno (int line_number ,yyscan_t yyscanner );
+
+int yyget_column (yyscan_t yyscanner );
+
+void yyset_column (int column_no ,yyscan_t yyscanner );
+
+YYSTYPE * yyget_lval (yyscan_t yyscanner );
+
+void yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap (yyscan_t yyscanner );
+#else
+extern int yywrap (yyscan_t yyscanner );
+#endif
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner);
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner);
+#endif
+
+#ifndef YY_NO_INPUT
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int yylex \
+ (YYSTYPE * yylval_param ,yyscan_t yyscanner);
+
+#define YY_DECL int yylex \
+ (YYSTYPE * yylval_param , yyscan_t yyscanner)
+#endif /* !YY_DECL */
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+#undef YY_NEW_FILE
+#undef YY_FLUSH_BUFFER
+#undef yy_set_bol
+#undef yy_new_buffer
+#undef yy_set_interactive
+#undef YY_DO_BEFORE_ACTION
+
+#ifdef YY_DECL_IS_OURS
+#undef YY_DECL_IS_OURS
+#undef YY_DECL
+#endif
+
+#line 131 "/usr/src/php-src/sapi/phpdbg/phpdbg_lexer.l"
+
+
+#line 347 "sapi/phpdbg/phpdbg_lexer.h"
+#undef yyIN_HEADER
+#endif /* yyHEADER_H */
diff --git a/sapi/phpdbg/phpdbg_lexer.l b/sapi/phpdbg/phpdbg_lexer.l
new file mode 100644
index 0000000000..b3536feab8
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_lexer.l
@@ -0,0 +1,131 @@
+%{
+
+/*
+ * phpdbg_lexer.l
+ */
+
+#include "phpdbg.h"
+#include "phpdbg_cmd.h"
+#define YYSTYPE phpdbg_param_t
+
+#include "phpdbg_parser.h"
+
+%}
+
+%s RAW
+%s NORMAL
+
+%option outfile="sapi/phpdbg/phpdbg_lexer.c" header-file="sapi/phpdbg/phpdbg_lexer.h"
+%option warn nodefault
+
+%option reentrant noyywrap never-interactive nounistd
+%option bison-bridge
+
+T_TRUE "true"
+T_YES "yes"
+T_ON "on"
+T_ENABLED "enabled"
+T_FALSE "false"
+T_NO "no"
+T_OFF "off"
+T_DISABLED "disabled"
+T_EVAL "ev"
+T_SHELL "sh"
+T_IF "if"
+T_RUN "run"
+T_RUN_SHORT "r"
+
+WS [ \r\n\t]+
+DIGITS [0-9\.]+
+ID [^ \r\n\t:#]+
+ADDR 0x[a-fA-F0-9]+
+OPCODE (ZEND_|zend_)([A-Za-z])+
+INPUT [^\n]+
+%%
+
+<INITIAL>{
+ {T_EVAL} {
+ BEGIN(RAW);
+ phpdbg_init_param(yylval, EMPTY_PARAM);
+ return T_EVAL;
+ }
+ {T_SHELL} {
+ BEGIN(RAW);
+ phpdbg_init_param(yylval, EMPTY_PARAM);
+ return T_SHELL;
+ }
+ {T_RUN}|{T_RUN_SHORT} {
+ BEGIN(RAW);
+ phpdbg_init_param(yylval, EMPTY_PARAM);
+ return T_RUN;
+ }
+
+ .+ {
+ BEGIN(NORMAL);
+ REJECT;
+ }
+}
+
+<NORMAL>{
+ {T_IF} {
+ BEGIN(RAW);
+ phpdbg_init_param(yylval, EMPTY_PARAM);
+ return T_IF;
+ }
+}
+
+<INITIAL,NORMAL>{
+ {ID}[:]{1}[//]{2} {
+ phpdbg_init_param(yylval, STR_PARAM);
+ yylval->str = zend_strndup(yytext, yyleng);
+ yylval->len = yyleng;
+ return T_PROTO;
+ }
+ [#]{1} { return T_POUND; }
+ [:]{2} { return T_DCOLON; }
+ [:]{1} { return T_COLON; }
+
+ {T_YES}|{T_ON}|{T_ENABLED}|{T_TRUE} {
+ phpdbg_init_param(yylval, NUMERIC_PARAM);
+ yylval->num = 1;
+ return T_TRUTHY;
+ }
+ {T_NO}|{T_OFF}|{T_DISABLED}|{T_FALSE} {
+ phpdbg_init_param(yylval, NUMERIC_PARAM);
+ yylval->num = 0;
+ return T_FALSY;
+ }
+ {DIGITS} {
+ phpdbg_init_param(yylval, NUMERIC_PARAM);
+ yylval->num = atoi(yytext);
+ return T_DIGITS;
+ }
+ {ADDR} {
+ phpdbg_init_param(yylval, ADDR_PARAM);
+ yylval->addr = strtoul(yytext, 0, 16);
+ return T_ADDR;
+ }
+ {OPCODE} {
+ phpdbg_init_param(yylval, OP_PARAM);
+ yylval->str = zend_strndup(yytext, yyleng);
+ yylval->len = yyleng;
+ return T_OPCODE;
+ }
+ {ID} {
+ phpdbg_init_param(yylval, STR_PARAM);
+ yylval->str = zend_strndup(yytext, yyleng);
+ yylval->len = yyleng;
+ return T_ID;
+ }
+}
+
+<RAW>{INPUT} {
+ phpdbg_init_param(yylval, STR_PARAM);
+ yylval->str = zend_strndup(yytext, yyleng);
+ yylval->len = yyleng;
+ BEGIN(INITIAL);
+ return T_INPUT;
+}
+
+{WS} { /* ignore whitespace */ }
+%%
diff --git a/sapi/phpdbg/phpdbg_list.c b/sapi/phpdbg/phpdbg_list.c
index eb1091550b..037c6c38b2 100644
--- a/sapi/phpdbg/phpdbg_list.c
+++ b/sapi/phpdbg/phpdbg_list.c
@@ -29,9 +29,22 @@
#include "phpdbg.h"
#include "phpdbg_list.h"
#include "phpdbg_utils.h"
+#include "phpdbg_prompt.h"
+#include "php_streams.h"
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+#define PHPDBG_LIST_COMMAND_D(f, h, a, m, l, s) \
+ PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[13])
+
+const phpdbg_command_t phpdbg_list_commands[] = {
+ PHPDBG_LIST_COMMAND_D(lines, "lists the specified lines", 'l', list_lines, NULL, "l"),
+ PHPDBG_LIST_COMMAND_D(class, "lists the specified class", 'c', list_class, NULL, "s"),
+ PHPDBG_LIST_COMMAND_D(method, "lists the specified method", 'm', list_method, NULL, "m"),
+ PHPDBG_LIST_COMMAND_D(func, "lists the specified function", 'f', list_func, NULL, "s"),
+ PHPDBG_END_COMMAND
+};
+
PHPDBG_LIST(lines) /* {{{ */
{
if (!PHPDBG_G(exec) && !zend_is_executing(TSRMLS_C)) {
@@ -41,12 +54,12 @@ PHPDBG_LIST(lines) /* {{{ */
switch (param->type) {
case NUMERIC_PARAM:
- case EMPTY_PARAM:
phpdbg_list_file(phpdbg_current_file(TSRMLS_C),
- param->type == EMPTY_PARAM ? 0 : (param->num < 0 ? 1 - param->num : param->num),
- (param->type != EMPTY_PARAM && param->num < 0 ? param->num : 0) + zend_get_executed_lineno(TSRMLS_C),
+ (param->num < 0 ? 1 - param->num : param->num),
+ (param->num < 0 ? param->num : 0) + zend_get_executed_lineno(TSRMLS_C),
0 TSRMLS_CC);
break;
+
case FILE_PARAM:
phpdbg_list_file(param->file.name, param->file.line, 0, 0 TSRMLS_CC);
break;
@@ -59,41 +72,28 @@ PHPDBG_LIST(lines) /* {{{ */
PHPDBG_LIST(func) /* {{{ */
{
- switch (param->type) {
- case STR_PARAM:
- phpdbg_list_function_byname(
- param->str, param->len TSRMLS_CC);
- break;
-
- phpdbg_default_switch_case();
- }
+ phpdbg_list_function_byname(param->str, param->len TSRMLS_CC);
return SUCCESS;
} /* }}} */
PHPDBG_LIST(method) /* {{{ */
{
- switch (param->type) {
- case METHOD_PARAM: {
- zend_class_entry **ce;
-
- if (zend_lookup_class(param->method.class, strlen(param->method.class), &ce TSRMLS_CC) == SUCCESS) {
- zend_function *function;
- char *lcname = zend_str_tolower_dup(param->method.name, strlen(param->method.name));
+ zend_class_entry **ce;
- if (zend_hash_find(&(*ce)->function_table, lcname, strlen(lcname)+1, (void**) &function) == SUCCESS) {
- phpdbg_list_function(function TSRMLS_CC);
- } else {
- phpdbg_error("Could not find %s::%s", param->method.class, param->method.name);
- }
+ if (zend_lookup_class(param->method.class, strlen(param->method.class), &ce TSRMLS_CC) == SUCCESS) {
+ zend_function *function;
+ char *lcname = zend_str_tolower_dup(param->method.name, strlen(param->method.name));
- efree(lcname);
- } else {
- phpdbg_error("Could not find the class %s", param->method.class);
- }
- } break;
+ if (zend_hash_find(&(*ce)->function_table, lcname, strlen(lcname)+1, (void**) &function) == SUCCESS) {
+ phpdbg_list_function(function TSRMLS_CC);
+ } else {
+ phpdbg_error("Could not find %s::%s", param->method.class, param->method.name);
+ }
- phpdbg_default_switch_case();
+ efree(lcname);
+ } else {
+ phpdbg_error("Could not find the class %s", param->method.class);
}
return SUCCESS;
@@ -101,30 +101,24 @@ PHPDBG_LIST(method) /* {{{ */
PHPDBG_LIST(class) /* {{{ */
{
- switch (param->type) {
- case STR_PARAM: {
- zend_class_entry **ce;
-
- if (zend_lookup_class(param->str, param->len, &ce TSRMLS_CC) == SUCCESS) {
- if ((*ce)->type == ZEND_USER_CLASS) {
- if ((*ce)->info.user.filename) {
- phpdbg_list_file(
- (*ce)->info.user.filename,
- (*ce)->info.user.line_end - (*ce)->info.user.line_start + 1,
- (*ce)->info.user.line_start, 0 TSRMLS_CC
- );
- } else {
- phpdbg_error("The source of the requested class (%s) cannot be found", (*ce)->name);
- }
- } else {
- phpdbg_error("The class requested (%s) is not user defined", (*ce)->name);
- }
+ zend_class_entry **ce;
+
+ if (zend_lookup_class(param->str, param->len, &ce TSRMLS_CC) == SUCCESS) {
+ if ((*ce)->type == ZEND_USER_CLASS) {
+ if ((*ce)->info.user.filename) {
+ phpdbg_list_file(
+ (*ce)->info.user.filename,
+ (*ce)->info.user.line_end - (*ce)->info.user.line_start + 1,
+ (*ce)->info.user.line_start, 0 TSRMLS_CC
+ );
} else {
- phpdbg_error("The requested class (%s) could not be found", param->str);
+ phpdbg_error("The source of the requested class (%s) cannot be found", (*ce)->name);
}
- } break;
-
- phpdbg_default_switch_case();
+ } else {
+ phpdbg_error("The class requested (%s) is not user defined", (*ce)->name);
+ }
+ } else {
+ phpdbg_error("The requested class (%s) could not be found", param->str);
}
return SUCCESS;
@@ -132,97 +126,46 @@ PHPDBG_LIST(class) /* {{{ */
void phpdbg_list_file(const char *filename, long count, long offset, int highlight TSRMLS_DC) /* {{{ */
{
- unsigned char *mem, *pos, *last_pos, *end_pos;
struct stat st;
-#ifndef _WIN32
- int fd;
-#else
- HANDLE fd, map;
-#endif
- int all_content = (count == 0);
- int line = 0, displayed = 0;
-
+ char *opened = NULL;
+ char buffer[8096] = {0,};
+ long line = 0;
+
+ php_stream *stream = NULL;
+
if (VCWD_STAT(filename, &st) == FAILURE) {
phpdbg_error("Failed to stat file %s", filename);
return;
}
-
-#ifndef _WIN32
- if ((fd = VCWD_OPEN(filename, O_RDONLY)) == FAILURE) {
+
+ stream = php_stream_open_wrapper(filename, "rb", USE_PATH, &opened);
+
+ if (!stream) {
phpdbg_error("Failed to open file %s to list", filename);
return;
}
-
- pos = last_pos = mem = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
- end_pos = mem + st.st_size;
-#else
-
- fd = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
- if (fd == INVALID_HANDLE_VALUE) {
- phpdbg_error("Failed to open file!");
- return;
- }
-
- map = CreateFileMapping(fd, NULL, PAGE_READONLY, 0, 0, NULL);
- if (map == NULL) {
- phpdbg_error("Failed to map file!");
- CloseHandle(fd);
- return;
- }
-
- pos = last_pos = mem = (char*) MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0);
- if (mem == NULL) {
- phpdbg_error("Failed to map file in memory");
- CloseHandle(map);
- CloseHandle(fd);
- return;
- }
- end_pos = mem + st.st_size;
-#endif
- while (1) {
- if (pos == end_pos) {
- break;
- }
-
- pos = memchr(last_pos, '\n', end_pos - last_pos);
-
- if (!pos) {
- /* No more line breaks */
- pos = end_pos;
- }
-
+
+ while (php_stream_gets(stream, buffer, sizeof(buffer)) != NULL) {
++line;
-
+
if (!offset || offset <= line) {
/* Without offset, or offset reached */
if (!highlight) {
- phpdbg_writeln("%05u: %.*s", line, (int)(pos - last_pos), last_pos);
+ phpdbg_write("%05ld: %s", line, buffer);
} else {
if (highlight != line) {
- phpdbg_writeln(" %05u: %.*s", line, (int)(pos - last_pos), last_pos);
+ phpdbg_write(" %05ld: %s", line, buffer);
} else {
- phpdbg_writeln(">%05u: %.*s", line, (int)(pos - last_pos), last_pos);
+ phpdbg_write(">%05ld: %s", line, buffer);
}
}
- ++displayed;
}
-
- last_pos = pos + 1;
-
- if (!all_content && displayed == count) {
- /* Reached max line to display */
+
+ if ((count + (offset-1)) == line)
break;
- }
}
-
-#ifndef _WIN32
- munmap(mem, st.st_size);
- close(fd);
-#else
- UnmapViewOfFile(mem);
- CloseHandle(map);
- CloseHandle(fd);
-#endif
+
+ php_stream_close(stream);
} /* }}} */
void phpdbg_list_function(const zend_function *fbc TSRMLS_DC) /* {{{ */
diff --git a/sapi/phpdbg/phpdbg_list.h b/sapi/phpdbg/phpdbg_list.h
index f9d1885eaf..14905f6567 100644
--- a/sapi/phpdbg/phpdbg_list.h
+++ b/sapi/phpdbg/phpdbg_list.h
@@ -36,12 +36,6 @@ void phpdbg_list_function_byname(const char *, size_t TSRMLS_DC);
void phpdbg_list_function(const zend_function* TSRMLS_DC);
void phpdbg_list_file(const char*, long, long, int TSRMLS_DC);
-static const phpdbg_command_t phpdbg_list_commands[] = {
- PHPDBG_COMMAND_D_EX(lines, "lists the specified lines", 'l', list_lines, NULL, 1),
- PHPDBG_COMMAND_D_EX(class, "lists the specified class", 'c', list_class, NULL, 1),
- PHPDBG_COMMAND_D_EX(method, "lists the specified method", 'm', list_method, NULL, 1),
- PHPDBG_COMMAND_D_EX(func, "lists the specified function", 'f', list_func, NULL, 1),
- PHPDBG_END_COMMAND
-};
+extern const phpdbg_command_t phpdbg_list_commands[];
#endif /* PHPDBG_LIST_H */
diff --git a/sapi/phpdbg/phpdbg_opcode.c b/sapi/phpdbg/phpdbg_opcode.c
index 50073eb22b..6b13625fc1 100644
--- a/sapi/phpdbg/phpdbg_opcode.c
+++ b/sapi/phpdbg/phpdbg_opcode.c
@@ -183,6 +183,7 @@ void phpdbg_print_opline(zend_execute_data *execute_data, zend_bool ignore_flags
const char *phpdbg_decode_opcode(zend_uchar opcode) /* {{{ */
{
+#if ZEND_EXTENSION_API_NO <= PHP_5_5_API_NO
#define CASE(s) case s: return #s
switch (opcode) {
CASE(ZEND_NOP);
@@ -360,4 +361,8 @@ const char *phpdbg_decode_opcode(zend_uchar opcode) /* {{{ */
default:
return "UNKNOWN";
}
+#else
+ const char *ret = zend_get_opcode_name(opcode);
+ return ret?ret:"UNKNOWN";
+#endif
} /* }}} */
diff --git a/sapi/phpdbg/phpdbg_parser.c b/sapi/phpdbg/phpdbg_parser.c
new file mode 100644
index 0000000000..79af58f8c9
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_parser.c
@@ -0,0 +1,1885 @@
+/* A Bison parser, made by GNU Bison 2.7. */
+
+/* Bison implementation for Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989-1990, 2000-2012 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+ simplifying the original so-called "semantic" parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Bison version. */
+#define YYBISON_VERSION "2.7"
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers. */
+#define YYPURE 1
+
+/* Push parsers. */
+#define YYPUSH 0
+
+/* Pull parsers. */
+#define YYPULL 1
+
+
+
+
+/* Copy the first part of user declarations. */
+/* Line 371 of yacc.c */
+#line 2 "/usr/src/php-src/sapi/phpdbg/phpdbg_parser.y"
+
+
+/*
+ * phpdbg_parser.y
+ * (from php-src root)
+ * flex sapi/phpdbg/dev/phpdbg_lexer.l
+ * bison sapi/phpdbg/dev/phpdbg_parser.y
+ */
+
+#include "phpdbg.h"
+#include "phpdbg_cmd.h"
+#include "phpdbg_utils.h"
+#include "phpdbg_cmd.h"
+#include "phpdbg_prompt.h"
+
+#define YYSTYPE phpdbg_param_t
+
+#include "phpdbg_parser.h"
+#include "phpdbg_lexer.h"
+
+ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+
+int yyerror(phpdbg_param_t *stack, yyscan_t scanner, const char *msg) {
+ TSRMLS_FETCH();
+ phpdbg_error("Parse Error: %s", msg);
+ {
+ const phpdbg_param_t *top = stack;
+
+ while (top) {
+ phpdbg_param_debug(
+ top, "--> ");
+ top = top->next;
+ }
+ }
+ return 0;
+}
+
+/* Line 371 of yacc.c */
+#line 106 "sapi/phpdbg/phpdbg_parser.c"
+
+# ifndef YY_NULL
+# if defined __cplusplus && 201103L <= __cplusplus
+# define YY_NULL nullptr
+# else
+# define YY_NULL 0
+# endif
+# endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 1
+#endif
+
+/* In a future release of Bison, this section will be replaced
+ by #include "phpdbg_parser.h". */
+#ifndef YY_YY_SAPI_PHPDBG_PHPDBG_PARSER_H_INCLUDED
+# define YY_YY_SAPI_PHPDBG_PHPDBG_PARSER_H_INCLUDED
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+#if YYDEBUG
+extern int yydebug;
+#endif
+/* "%code requires" blocks. */
+/* Line 387 of yacc.c */
+#line 40 "/usr/src/php-src/sapi/phpdbg/phpdbg_parser.y"
+
+#include "phpdbg.h"
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+
+
+/* Line 387 of yacc.c */
+#line 147 "sapi/phpdbg/phpdbg_parser.c"
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ T_EVAL = 258,
+ T_RUN = 259,
+ T_SHELL = 260,
+ T_IF = 261,
+ T_TRUTHY = 262,
+ T_FALSY = 263,
+ T_STRING = 264,
+ T_COLON = 265,
+ T_DCOLON = 266,
+ T_POUND = 267,
+ T_PROTO = 268,
+ T_DIGITS = 269,
+ T_LITERAL = 270,
+ T_ADDR = 271,
+ T_OPCODE = 272,
+ T_ID = 273,
+ T_INPUT = 274,
+ T_UNEXPECTED = 275
+ };
+#endif
+/* Tokens. */
+#define T_EVAL 258
+#define T_RUN 259
+#define T_SHELL 260
+#define T_IF 261
+#define T_TRUTHY 262
+#define T_FALSY 263
+#define T_STRING 264
+#define T_COLON 265
+#define T_DCOLON 266
+#define T_POUND 267
+#define T_PROTO 268
+#define T_DIGITS 269
+#define T_LITERAL 270
+#define T_ADDR 271
+#define T_OPCODE 272
+#define T_ID 273
+#define T_INPUT 274
+#define T_UNEXPECTED 275
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef int YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void *YYPARSE_PARAM);
+#else
+int yyparse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int yyparse (phpdbg_param_t *stack, yyscan_t scanner);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+#endif /* !YY_YY_SAPI_PHPDBG_PHPDBG_PARSER_H_INCLUDED */
+
+/* Copy the second part of user declarations. */
+
+/* Line 390 of yacc.c */
+#line 224 "sapi/phpdbg/phpdbg_parser.c"
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#elif (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+typedef signed char yytype_int8;
+#else
+typedef short int yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+# define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+# define YYSIZE_T size_t
+# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# else
+# define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if defined YYENABLE_NLS && YYENABLE_NLS
+# if ENABLE_NLS
+# include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+# define YY_(Msgid) dgettext ("bison-runtime", Msgid)
+# endif
+# endif
+# ifndef YY_
+# define YY_(Msgid) Msgid
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E. */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(E) ((void) (E))
+#else
+# define YYUSE(E) /* empty */
+#endif
+
+/* Identity function, used to suppress warnings about constant conditions. */
+#ifndef lint
+# define YYID(N) (N)
+#else
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static int
+YYID (int yyi)
+#else
+static int
+YYID (yyi)
+ int yyi;
+#endif
+{
+ return yyi;
+}
+#endif
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# ifdef YYSTACK_USE_ALLOCA
+# if YYSTACK_USE_ALLOCA
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# elif defined __BUILTIN_VA_ARG_INCR
+# include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+# elif defined _AIX
+# define YYSTACK_ALLOC __alloca
+# elif defined _MSC_VER
+# include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+# define alloca _alloca
+# else
+# define YYSTACK_ALLOC alloca
+# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+ /* Use EXIT_SUCCESS as a witness for stdlib.h. */
+# ifndef EXIT_SUCCESS
+# define EXIT_SUCCESS 0
+# endif
+# endif
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's `empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
+# ifndef YYSTACK_ALLOC_MAXIMUM
+ /* The OS might guarantee only one guard page at the bottom of the stack,
+ and a page size can be as small as 4096 bytes. So we cannot safely
+ invoke alloca (N) if N exceeds 4096. Use a slightly smaller number
+ to allow for a few compiler-allocated temporary stack slots. */
+# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+# endif
+# else
+# define YYSTACK_ALLOC YYMALLOC
+# define YYSTACK_FREE YYFREE
+# ifndef YYSTACK_ALLOC_MAXIMUM
+# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+# endif
+# if (defined __cplusplus && ! defined EXIT_SUCCESS \
+ && ! ((defined YYMALLOC || defined malloc) \
+ && (defined YYFREE || defined free)))
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef EXIT_SUCCESS
+# define EXIT_SUCCESS 0
+# endif
+# endif
+# ifndef YYMALLOC
+# define YYMALLOC malloc
+# if ! defined malloc && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# ifndef YYFREE
+# define YYFREE free
+# if ! defined free && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+ && (! defined __cplusplus \
+ || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ yytype_int16 yyss_alloc;
+ YYSTYPE yyvs_alloc;
+};
+
+/* The size of the maximum gap between one aligned stack and the next. */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+ + YYSTACK_GAP_MAXIMUM)
+
+# define YYCOPY_NEEDED 1
+
+/* Relocate STACK from its old location to the new one. The
+ local variables YYSIZE and YYSTACKSIZE give the old and new number of
+ elements in the stack, and YYPTR gives the new location of the
+ stack. Advance YYPTR to a properly aligned location for the next
+ stack. */
+# define YYSTACK_RELOCATE(Stack_alloc, Stack) \
+ do \
+ { \
+ YYSIZE_T yynewbytes; \
+ YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \
+ Stack = &yyptr->Stack_alloc; \
+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+ yyptr += yynewbytes / sizeof (*yyptr); \
+ } \
+ while (YYID (0))
+
+#endif
+
+#if defined YYCOPY_NEEDED && YYCOPY_NEEDED
+/* Copy COUNT objects from SRC to DST. The source and destination do
+ not overlap. */
+# ifndef YYCOPY
+# if defined __GNUC__ && 1 < __GNUC__
+# define YYCOPY(Dst, Src, Count) \
+ __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src)))
+# else
+# define YYCOPY(Dst, Src, Count) \
+ do \
+ { \
+ YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (Dst)[yyi] = (Src)[yyi]; \
+ } \
+ while (YYID (0))
+# endif
+# endif
+#endif /* !YYCOPY_NEEDED */
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 24
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 33
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 21
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 4
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 24
+/* YYNRULES -- Number of states. */
+#define YYNSTATES 37
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 275
+
+#define YYTRANSLATE(YYX) \
+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */
+static const yytype_uint8 yytranslate[] =
+{
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+ YYRHS. */
+static const yytype_uint8 yyprhs[] =
+{
+ 0, 0, 3, 5, 6, 8, 11, 15, 20, 25,
+ 31, 35, 41, 45, 48, 51, 54, 56, 59, 61,
+ 63, 65, 67, 69, 71
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yytype_int8 yyrhs[] =
+{
+ 22, 0, -1, 23, -1, -1, 24, -1, 23, 24,
+ -1, 18, 10, 14, -1, 18, 10, 12, 14, -1,
+ 13, 18, 10, 14, -1, 13, 18, 10, 12, 14,
+ -1, 18, 11, 18, -1, 18, 11, 18, 12, 14,
+ -1, 18, 12, 14, -1, 6, 19, -1, 3, 19,
+ -1, 5, 19, -1, 4, -1, 4, 19, -1, 17,
+ -1, 16, -1, 15, -1, 7, -1, 8, -1, 14,
+ -1, 18, -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
+static const yytype_uint8 yyrline[] =
+{
+ 0, 76, 76, 77, 81, 82, 86, 91, 96, 107,
+ 118, 123, 129, 135, 140, 145, 150, 154, 159, 160,
+ 161, 162, 163, 164, 165
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || 1
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+ "$end", "error", "$undefined", "\"eval\"", "\"run\"", "\"shell\"",
+ "\"if (condition)\"", "\"truthy (true, on, yes or enabled)\"",
+ "\"falsy (false, off, no or disabled)\"",
+ "\"string (some input, perhaps)\"", "\": (colon)\"",
+ "\":: (double colon)\"", "\"# (pound sign)\"", "\"protocol (file://)\"",
+ "\"digits (numbers)\"", "\"literal (string)\"", "\"address\"",
+ "\"opcode\"", "\"identifier (command or function name)\"",
+ "\"input (input string or data)\"", "\"input\"", "$accept", "input",
+ "parameters", "parameter", YY_NULL
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+ token YYLEX-NUM. */
+static const yytype_uint16 yytoknum[] =
+{
+ 0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267, 268, 269, 270, 271, 272, 273, 274,
+ 275
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const yytype_uint8 yyr1[] =
+{
+ 0, 21, 22, 22, 23, 23, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
+static const yytype_uint8 yyr2[] =
+{
+ 0, 2, 1, 0, 1, 2, 3, 4, 4, 5,
+ 3, 5, 3, 2, 2, 2, 1, 2, 1, 1,
+ 1, 1, 1, 1, 1
+};
+
+/* YYDEFACT[STATE-NAME] -- Default reduction number in state STATE-NUM.
+ Performed when YYTABLE doesn't specify something else to do. Zero
+ means the default is an error. */
+static const yytype_uint8 yydefact[] =
+{
+ 3, 0, 16, 0, 0, 21, 22, 0, 23, 20,
+ 19, 18, 24, 0, 2, 4, 14, 17, 15, 13,
+ 0, 0, 0, 0, 1, 5, 0, 0, 6, 10,
+ 12, 0, 8, 7, 0, 9, 11
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const yytype_int8 yydefgoto[] =
+{
+ -1, 13, 14, 15
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+#define YYPACT_NINF -11
+static const yytype_int8 yypact[] =
+{
+ -3, -10, 1, 2, 3, -11, -11, 6, -11, -11,
+ -11, -11, -4, 23, -3, -11, -11, -11, -11, -11,
+ 15, 4, 8, 13, -11, -11, 5, 14, -11, 17,
+ -11, 16, -11, -11, 18, -11, -11
+};
+
+/* YYPGOTO[NTERM-NUM]. */
+static const yytype_int8 yypgoto[] =
+{
+ -11, -11, -11, 19
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
+ positive, shift that token. If negative, reduce the rule which
+ number is the opposite. If YYTABLE_NINF, syntax error. */
+#define YYTABLE_NINF -1
+static const yytype_uint8 yytable[] =
+{
+ 1, 2, 3, 4, 5, 6, 21, 22, 23, 16,
+ 7, 8, 9, 10, 11, 12, 27, 31, 28, 32,
+ 17, 18, 19, 24, 20, 26, 29, 30, 33, 34,
+ 35, 0, 36, 25
+};
+
+#define yypact_value_is_default(Yystate) \
+ (!!((Yystate) == (-11)))
+
+#define yytable_value_is_error(Yytable_value) \
+ YYID (0)
+
+static const yytype_int8 yycheck[] =
+{
+ 3, 4, 5, 6, 7, 8, 10, 11, 12, 19,
+ 13, 14, 15, 16, 17, 18, 12, 12, 14, 14,
+ 19, 19, 19, 0, 18, 10, 18, 14, 14, 12,
+ 14, -1, 14, 14
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+ symbol of state STATE-NUM. */
+static const yytype_uint8 yystos[] =
+{
+ 0, 3, 4, 5, 6, 7, 8, 13, 14, 15,
+ 16, 17, 18, 22, 23, 24, 19, 19, 19, 19,
+ 18, 10, 11, 12, 0, 24, 10, 12, 14, 18,
+ 14, 12, 14, 14, 12, 14, 14
+};
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY (-2)
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror. This remains here temporarily
+ to ease the transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. However,
+ YYFAIL appears to be in use. Nevertheless, it is formally deprecated
+ in Bison 2.4.2's NEWS entry, where a plan to phase it out is
+ discussed. */
+
+#define YYFAIL goto yyerrlab
+#if defined YYFAIL
+ /* This is here to suppress warnings from the GCC cpp's
+ -Wunused-macros. Normally we don't worry about that warning, but
+ some users do, and we want to make it easy for users to remove
+ YYFAIL uses, which will produce warnings from Bison 2.5. */
+#endif
+
+#define YYRECOVERING() (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value) \
+do \
+ if (yychar == YYEMPTY) \
+ { \
+ yychar = (Token); \
+ yylval = (Value); \
+ YYPOPSTACK (yylen); \
+ yystate = *yyssp; \
+ goto yybackup; \
+ } \
+ else \
+ { \
+ yyerror (stack, scanner, YY_("syntax error: cannot back up")); \
+ YYERROR; \
+ } \
+while (YYID (0))
+
+/* Error token number */
+#define YYTERROR 1
+#define YYERRCODE 256
+
+
+/* This macro is provided for backward compatibility. */
+#ifndef YY_LOCATION_PRINT
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments. */
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (&yylval, YYLEX_PARAM)
+#else
+# define YYLEX yylex (&yylval, scanner)
+#endif
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (YYID (0))
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yy_symbol_print (stderr, \
+ Type, Value, stack, scanner); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (YYID (0))
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, phpdbg_param_t *stack, yyscan_t scanner)
+#else
+static void
+yy_symbol_value_print (yyoutput, yytype, yyvaluep, stack, scanner)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+ phpdbg_param_t *stack;
+ yyscan_t scanner;
+#endif
+{
+ FILE *yyo = yyoutput;
+ YYUSE (yyo);
+ if (!yyvaluep)
+ return;
+ YYUSE (stack);
+ YYUSE (scanner);
+# ifdef YYPRINT
+ if (yytype < YYNTOKENS)
+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# else
+ YYUSE (yyoutput);
+# endif
+ switch (yytype)
+ {
+ default:
+ break;
+ }
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, phpdbg_param_t *stack, yyscan_t scanner)
+#else
+static void
+yy_symbol_print (yyoutput, yytype, yyvaluep, stack, scanner)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+ phpdbg_param_t *stack;
+ yyscan_t scanner;
+#endif
+{
+ if (yytype < YYNTOKENS)
+ YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+ else
+ YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+ yy_symbol_value_print (yyoutput, yytype, yyvaluep, stack, scanner);
+ YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included). |
+`------------------------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop)
+#else
+static void
+yy_stack_print (yybottom, yytop)
+ yytype_int16 *yybottom;
+ yytype_int16 *yytop;
+#endif
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (; yybottom <= yytop; yybottom++)
+ {
+ int yybot = *yybottom;
+ YYFPRINTF (stderr, " %d", yybot);
+ }
+ YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top) \
+do { \
+ if (yydebug) \
+ yy_stack_print ((Bottom), (Top)); \
+} while (YYID (0))
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_reduce_print (YYSTYPE *yyvsp, int yyrule, phpdbg_param_t *stack, yyscan_t scanner)
+#else
+static void
+yy_reduce_print (yyvsp, yyrule, stack, scanner)
+ YYSTYPE *yyvsp;
+ int yyrule;
+ phpdbg_param_t *stack;
+ yyscan_t scanner;
+#endif
+{
+ int yynrhs = yyr2[yyrule];
+ int yyi;
+ unsigned long int yylno = yyrline[yyrule];
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+ yyrule - 1, yylno);
+ /* The symbols being reduced. */
+ for (yyi = 0; yyi < yynrhs; yyi++)
+ {
+ YYFPRINTF (stderr, " $%d = ", yyi + 1);
+ yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
+ &(yyvsp[(yyi + 1) - (yynrhs)])
+ , stack, scanner);
+ YYFPRINTF (stderr, "\n");
+ }
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (yyvsp, Rule, stack, scanner); \
+} while (YYID (0))
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined __GLIBC__ && defined _STRING_H
+# define yystrlen strlen
+# else
+/* Return the length of YYSTR. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static YYSIZE_T
+yystrlen (const char *yystr)
+#else
+static YYSIZE_T
+yystrlen (yystr)
+ const char *yystr;
+#endif
+{
+ YYSIZE_T yylen;
+ for (yylen = 0; yystr[yylen]; yylen++)
+ continue;
+ return yylen;
+}
+# endif
+# endif
+
+# ifndef yystpcpy
+# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+#else
+static char *
+yystpcpy (yydest, yysrc)
+ char *yydest;
+ const char *yysrc;
+#endif
+{
+ char *yyd = yydest;
+ const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+ quotes and backslashes, so that it's suitable for yyerror. The
+ heuristic is that double-quoting is unnecessary unless the string
+ contains an apostrophe, a comma, or backslash (other than
+ backslash-backslash). YYSTR is taken from yytname. If YYRES is
+ null, do not copy; instead, return the length of what the result
+ would have been. */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+ if (*yystr == '"')
+ {
+ YYSIZE_T yyn = 0;
+ char const *yyp = yystr;
+
+ for (;;)
+ switch (*++yyp)
+ {
+ case '\'':
+ case ',':
+ goto do_not_strip_quotes;
+
+ case '\\':
+ if (*++yyp != '\\')
+ goto do_not_strip_quotes;
+ /* Fall through. */
+ default:
+ if (yyres)
+ yyres[yyn] = *yyp;
+ yyn++;
+ break;
+
+ case '"':
+ if (yyres)
+ yyres[yyn] = '\0';
+ return yyn;
+ }
+ do_not_strip_quotes: ;
+ }
+
+ if (! yyres)
+ return yystrlen (yystr);
+
+ return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message
+ about the unexpected token YYTOKEN for the state stack whose top is
+ YYSSP.
+
+ Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is
+ not large enough to hold the message. In that case, also set
+ *YYMSG_ALLOC to the required number of bytes. Return 2 if the
+ required number of bytes is too large to store. */
+static int
+yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg,
+ yytype_int16 *yyssp, int yytoken)
+{
+ YYSIZE_T yysize0 = yytnamerr (YY_NULL, yytname[yytoken]);
+ YYSIZE_T yysize = yysize0;
+ enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+ /* Internationalized format string. */
+ const char *yyformat = YY_NULL;
+ /* Arguments of yyformat. */
+ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+ /* Number of reported tokens (one for the "unexpected", one per
+ "expected"). */
+ int yycount = 0;
+
+ /* There are many possibilities here to consider:
+ - Assume YYFAIL is not used. It's too flawed to consider. See
+ <http://lists.gnu.org/archive/html/bison-patches/2009-12/msg00024.html>
+ for details. YYERROR is fine as it does not invoke this
+ function.
+ - If this state is a consistent state with a default action, then
+ the only way this function was invoked is if the default action
+ is an error action. In that case, don't check for expected
+ tokens because there are none.
+ - The only way there can be no lookahead present (in yychar) is if
+ this state is a consistent state with a default action. Thus,
+ detecting the absence of a lookahead is sufficient to determine
+ that there is no unexpected or expected token to report. In that
+ case, just report a simple "syntax error".
+ - Don't assume there isn't a lookahead just because this state is a
+ consistent state with a default action. There might have been a
+ previous inconsistent state, consistent state with a non-default
+ action, or user semantic action that manipulated yychar.
+ - Of course, the expected token list depends on states to have
+ correct lookahead information, and it depends on the parser not
+ to perform extra reductions after fetching a lookahead from the
+ scanner and before detecting a syntax error. Thus, state merging
+ (from LALR or IELR) and default reductions corrupt the expected
+ token list. However, the list is correct for canonical LR with
+ one exception: it will still contain any token that will not be
+ accepted due to an error action in a later state.
+ */
+ if (yytoken != YYEMPTY)
+ {
+ int yyn = yypact[*yyssp];
+ yyarg[yycount++] = yytname[yytoken];
+ if (!yypact_value_is_default (yyn))
+ {
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. In other words, skip the first -YYN actions for
+ this state because they are default actions. */
+ int yyxbegin = yyn < 0 ? -yyn : 0;
+ /* Stay within bounds of both yycheck and yytname. */
+ int yychecklim = YYLAST - yyn + 1;
+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ int yyx;
+
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR
+ && !yytable_value_is_error (yytable[yyx + yyn]))
+ {
+ if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+ {
+ yycount = 1;
+ yysize = yysize0;
+ break;
+ }
+ yyarg[yycount++] = yytname[yyx];
+ {
+ YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULL, yytname[yyx]);
+ if (! (yysize <= yysize1
+ && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+ return 2;
+ yysize = yysize1;
+ }
+ }
+ }
+ }
+
+ switch (yycount)
+ {
+# define YYCASE_(N, S) \
+ case N: \
+ yyformat = S; \
+ break
+ YYCASE_(0, YY_("syntax error"));
+ YYCASE_(1, YY_("syntax error, unexpected %s"));
+ YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s"));
+ YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s"));
+ YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s"));
+ YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"));
+# undef YYCASE_
+ }
+
+ {
+ YYSIZE_T yysize1 = yysize + yystrlen (yyformat);
+ if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+ return 2;
+ yysize = yysize1;
+ }
+
+ if (*yymsg_alloc < yysize)
+ {
+ *yymsg_alloc = 2 * yysize;
+ if (! (yysize <= *yymsg_alloc
+ && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM))
+ *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM;
+ return 1;
+ }
+
+ /* Avoid sprintf, as that infringes on the user's name space.
+ Don't have undefined behavior even if the translation
+ produced a string with the wrong number of "%s"s. */
+ {
+ char *yyp = *yymsg;
+ int yyi = 0;
+ while ((*yyp = *yyformat) != '\0')
+ if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount)
+ {
+ yyp += yytnamerr (yyp, yyarg[yyi++]);
+ yyformat += 2;
+ }
+ else
+ {
+ yyp++;
+ yyformat++;
+ }
+ }
+ return 0;
+}
+#endif /* YYERROR_VERBOSE */
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, phpdbg_param_t *stack, yyscan_t scanner)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep, stack, scanner)
+ const char *yymsg;
+ int yytype;
+ YYSTYPE *yyvaluep;
+ phpdbg_param_t *stack;
+ yyscan_t scanner;
+#endif
+{
+ YYUSE (yyvaluep);
+ YYUSE (stack);
+ YYUSE (scanner);
+
+ if (!yymsg)
+ yymsg = "Deleting";
+ YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+ switch (yytype)
+ {
+
+ default:
+ break;
+ }
+}
+
+
+
+
+/*----------.
+| yyparse. |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void *YYPARSE_PARAM)
+#else
+int
+yyparse (YYPARSE_PARAM)
+ void *YYPARSE_PARAM;
+#endif
+#else /* ! YYPARSE_PARAM */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (phpdbg_param_t *stack, yyscan_t scanner)
+#else
+int
+yyparse (stack, scanner)
+ phpdbg_param_t *stack;
+ yyscan_t scanner;
+#endif
+#endif
+{
+/* The lookahead symbol. */
+int yychar;
+
+
+#if defined __GNUC__ && 407 <= __GNUC__ * 100 + __GNUC_MINOR__
+/* Suppress an incorrect diagnostic about yylval being uninitialized. */
+# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \
+ _Pragma ("GCC diagnostic push") \
+ _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")\
+ _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"")
+# define YY_IGNORE_MAYBE_UNINITIALIZED_END \
+ _Pragma ("GCC diagnostic pop")
+#else
+/* Default value used for initialization, for pacifying older GCCs
+ or non-GCC compilers. */
+static YYSTYPE yyval_default;
+# define YY_INITIAL_VALUE(Value) = Value
+#endif
+#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+# define YY_IGNORE_MAYBE_UNINITIALIZED_END
+#endif
+#ifndef YY_INITIAL_VALUE
+# define YY_INITIAL_VALUE(Value) /* Nothing. */
+#endif
+
+/* The semantic value of the lookahead symbol. */
+YYSTYPE yylval YY_INITIAL_VALUE(yyval_default);
+
+ /* Number of syntax errors so far. */
+ int yynerrs;
+
+ int yystate;
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
+
+ /* The stacks and their tools:
+ `yyss': related to states.
+ `yyvs': related to semantic values.
+
+ Refer to the stacks through separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
+
+ /* The state stack. */
+ yytype_int16 yyssa[YYINITDEPTH];
+ yytype_int16 *yyss;
+ yytype_int16 *yyssp;
+
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs;
+ YYSTYPE *yyvsp;
+
+ YYSIZE_T yystacksize;
+
+ int yyn;
+ int yyresult;
+ /* Lookahead token as an internal (translated) token number. */
+ int yytoken = 0;
+ /* The variables used to return semantic value and location from the
+ action routines. */
+ YYSTYPE yyval;
+
+#if YYERROR_VERBOSE
+ /* Buffer for error messages, and its allocated size. */
+ char yymsgbuf[128];
+ char *yymsg = yymsgbuf;
+ YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N))
+
+ /* The number of symbols on the RHS of the reduced rule.
+ Keep to zero when no symbol should be popped. */
+ int yylen = 0;
+
+ yyssp = yyss = yyssa;
+ yyvsp = yyvs = yyvsa;
+ yystacksize = YYINITDEPTH;
+
+ YYDPRINTF ((stderr, "Starting parse\n"));
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+ goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+ yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. So pushing a state here evens the stacks. */
+ yyssp++;
+
+ yysetstate:
+ *yyssp = yystate;
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ yytype_int16 *yyss1 = yyss;
+
+ /* Each stack pointer address is followed by the size of the
+ data in use in that stack, in bytes. This used to be a
+ conditional around just the two extra args, but that might
+ be undefined if yyoverflow is a macro. */
+ yyoverflow (YY_("memory exhausted"),
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+ &yystacksize);
+
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyexhaustedlab;
+# else
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyexhaustedlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ yytype_int16 *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyexhaustedlab;
+ YYSTACK_RELOCATE (yyss_alloc, yyss);
+ YYSTACK_RELOCATE (yyvs_alloc, yyvs);
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+
+ YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+ (unsigned long int) yystacksize));
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+ if (yystate == YYFINAL)
+ YYACCEPT;
+
+ goto yybackup;
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+
+ /* Do appropriate processing given the current state. Read a
+ lookahead token if we need one and don't already have one. */
+
+ /* First try to decide what to do without reference to lookahead token. */
+ yyn = yypact[yystate];
+ if (yypact_value_is_default (yyn))
+ goto yydefault;
+
+ /* Not known => get a lookahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = YYLEX;
+ }
+
+ if (yychar <= YYEOF)
+ {
+ yychar = yytoken = YYEOF;
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yytoken = YYTRANSLATE (yychar);
+ YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+ }
+
+ /* If the proper action on seeing token YYTOKEN is to reduce or to
+ detect an error, take that action. */
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yytable_value_is_error (yyn))
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+
+ /* Count tokens shifted since error; after three, turn off error
+ status. */
+ if (yyerrstatus)
+ yyerrstatus--;
+
+ /* Shift the lookahead token. */
+ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+ /* Discard the shifted token. */
+ yychar = YYEMPTY;
+
+ yystate = yyn;
+ YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+ *++yyvsp = yylval;
+ YY_IGNORE_MAYBE_UNINITIALIZED_END
+
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ `$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+
+ YY_REDUCE_PRINT (yyn);
+ switch (yyn)
+ {
+ case 4:
+/* Line 1792 of yacc.c */
+#line 81 "/usr/src/php-src/sapi/phpdbg/phpdbg_parser.y"
+ { phpdbg_stack_push(stack, &(yyvsp[(1) - (1)])); }
+ break;
+
+ case 5:
+/* Line 1792 of yacc.c */
+#line 82 "/usr/src/php-src/sapi/phpdbg/phpdbg_parser.y"
+ { phpdbg_stack_push(stack, &(yyvsp[(2) - (2)])); }
+ break;
+
+ case 6:
+/* Line 1792 of yacc.c */
+#line 86 "/usr/src/php-src/sapi/phpdbg/phpdbg_parser.y"
+ {
+ (yyval).type = FILE_PARAM;
+ (yyval).file.name = (yyvsp[(2) - (3)]).str;
+ (yyval).file.line = (yyvsp[(3) - (3)]).num;
+ }
+ break;
+
+ case 7:
+/* Line 1792 of yacc.c */
+#line 91 "/usr/src/php-src/sapi/phpdbg/phpdbg_parser.y"
+ {
+ (yyval).type = NUMERIC_FILE_PARAM;
+ (yyval).file.name = (yyvsp[(1) - (4)]).str;
+ (yyval).file.line = (yyvsp[(4) - (4)]).num;
+ }
+ break;
+
+ case 8:
+/* Line 1792 of yacc.c */
+#line 96 "/usr/src/php-src/sapi/phpdbg/phpdbg_parser.y"
+ {
+ (yyval).type = FILE_PARAM;
+ (yyval).file.name = malloc((yyvsp[(1) - (4)]).len +
+ (yyvsp[(2) - (4)]).len + 1);
+ if ((yyval).file.name) {
+ memcpy(&(yyval).file.name[0], (yyvsp[(1) - (4)]).str, (yyvsp[(1) - (4)]).len);
+ memcpy(&(yyval).file.name[(yyvsp[(1) - (4)]).len], (yyvsp[(2) - (4)]).str, (yyvsp[(2) - (4)]).len);
+ (yyval).file.name[(yyvsp[(1) - (4)]).len + (yyvsp[(2) - (4)]).len] = '\0';
+ }
+ (yyval).file.line = (yyvsp[(4) - (4)]).num;
+ }
+ break;
+
+ case 9:
+/* Line 1792 of yacc.c */
+#line 107 "/usr/src/php-src/sapi/phpdbg/phpdbg_parser.y"
+ {
+ (yyval).type = NUMERIC_FILE_PARAM;
+ (yyval).file.name = malloc((yyvsp[(1) - (5)]).len +
+ (yyvsp[(2) - (5)]).len + 1);
+ if ((yyval).file.name) {
+ memcpy(&(yyval).file.name[0], (yyvsp[(1) - (5)]).str, (yyvsp[(1) - (5)]).len);
+ memcpy(&(yyval).file.name[(yyvsp[(1) - (5)]).len], (yyvsp[(2) - (5)]).str, (yyvsp[(2) - (5)]).len);
+ (yyval).file.name[(yyvsp[(1) - (5)]).len + (yyvsp[(2) - (5)]).len] = '\0';
+ }
+ (yyval).file.line = (yyvsp[(5) - (5)]).num;
+ }
+ break;
+
+ case 10:
+/* Line 1792 of yacc.c */
+#line 118 "/usr/src/php-src/sapi/phpdbg/phpdbg_parser.y"
+ {
+ (yyval).type = METHOD_PARAM;
+ (yyval).method.class = (yyvsp[(1) - (3)]).str;
+ (yyval).method.name = (yyvsp[(3) - (3)]).str;
+ }
+ break;
+
+ case 11:
+/* Line 1792 of yacc.c */
+#line 123 "/usr/src/php-src/sapi/phpdbg/phpdbg_parser.y"
+ {
+ (yyval).type = NUMERIC_METHOD_PARAM;
+ (yyval).method.class = (yyvsp[(1) - (5)]).str;
+ (yyval).method.name = (yyvsp[(3) - (5)]).str;
+ (yyval).num = (yyvsp[(5) - (5)]).num;
+ }
+ break;
+
+ case 12:
+/* Line 1792 of yacc.c */
+#line 129 "/usr/src/php-src/sapi/phpdbg/phpdbg_parser.y"
+ {
+ (yyval).type = NUMERIC_FUNCTION_PARAM;
+ (yyval).str = (yyvsp[(1) - (3)]).str;
+ (yyval).len = (yyvsp[(1) - (3)]).len;
+ (yyval).num = (yyvsp[(3) - (3)]).num;
+ }
+ break;
+
+ case 13:
+/* Line 1792 of yacc.c */
+#line 135 "/usr/src/php-src/sapi/phpdbg/phpdbg_parser.y"
+ {
+ (yyval).type = COND_PARAM;
+ (yyval).str = (yyvsp[(2) - (2)]).str;
+ (yyval).len = (yyvsp[(2) - (2)]).len;
+ }
+ break;
+
+ case 14:
+/* Line 1792 of yacc.c */
+#line 140 "/usr/src/php-src/sapi/phpdbg/phpdbg_parser.y"
+ {
+ (yyval).type = EVAL_PARAM;
+ (yyval).str = (yyvsp[(2) - (2)]).str;
+ (yyval).len = (yyvsp[(2) - (2)]).len;
+ }
+ break;
+
+ case 15:
+/* Line 1792 of yacc.c */
+#line 145 "/usr/src/php-src/sapi/phpdbg/phpdbg_parser.y"
+ {
+ (yyval).type = SHELL_PARAM;
+ (yyval).str = (yyvsp[(2) - (2)]).str;
+ (yyval).len = (yyvsp[(2) - (2)]).len;
+ }
+ break;
+
+ case 16:
+/* Line 1792 of yacc.c */
+#line 150 "/usr/src/php-src/sapi/phpdbg/phpdbg_parser.y"
+ {
+ (yyval).type = RUN_PARAM;
+ (yyval).len = 0;
+ }
+ break;
+
+ case 17:
+/* Line 1792 of yacc.c */
+#line 154 "/usr/src/php-src/sapi/phpdbg/phpdbg_parser.y"
+ {
+ (yyval).type = RUN_PARAM;
+ (yyval).str = (yyvsp[(2) - (2)]).str;
+ (yyval).len = (yyvsp[(2) - (2)]).len;
+ }
+ break;
+
+ case 18:
+/* Line 1792 of yacc.c */
+#line 159 "/usr/src/php-src/sapi/phpdbg/phpdbg_parser.y"
+ { (yyval) = (yyvsp[(1) - (1)]); }
+ break;
+
+ case 19:
+/* Line 1792 of yacc.c */
+#line 160 "/usr/src/php-src/sapi/phpdbg/phpdbg_parser.y"
+ { (yyval) = (yyvsp[(1) - (1)]); }
+ break;
+
+ case 20:
+/* Line 1792 of yacc.c */
+#line 161 "/usr/src/php-src/sapi/phpdbg/phpdbg_parser.y"
+ { (yyval) = (yyvsp[(1) - (1)]); }
+ break;
+
+ case 21:
+/* Line 1792 of yacc.c */
+#line 162 "/usr/src/php-src/sapi/phpdbg/phpdbg_parser.y"
+ { (yyval) = (yyvsp[(1) - (1)]); }
+ break;
+
+ case 22:
+/* Line 1792 of yacc.c */
+#line 163 "/usr/src/php-src/sapi/phpdbg/phpdbg_parser.y"
+ { (yyval) = (yyvsp[(1) - (1)]); }
+ break;
+
+ case 23:
+/* Line 1792 of yacc.c */
+#line 164 "/usr/src/php-src/sapi/phpdbg/phpdbg_parser.y"
+ { (yyval) = (yyvsp[(1) - (1)]); }
+ break;
+
+ case 24:
+/* Line 1792 of yacc.c */
+#line 165 "/usr/src/php-src/sapi/phpdbg/phpdbg_parser.y"
+ { (yyval) = (yyvsp[(1) - (1)]); }
+ break;
+
+
+/* Line 1792 of yacc.c */
+#line 1653 "sapi/phpdbg/phpdbg_parser.c"
+ default: break;
+ }
+ /* User semantic actions sometimes alter yychar, and that requires
+ that yytoken be updated with the new translation. We take the
+ approach of translating immediately before every use of yytoken.
+ One alternative is translating here after every semantic action,
+ but that translation would be missed if the semantic action invokes
+ YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or
+ if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an
+ incorrect destructor might then be invoked immediately. In the
+ case of YYERROR or YYBACKUP, subsequent parser actions might lead
+ to an incorrect destructor call or verbose syntax error message
+ before the lookahead is translated. */
+ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+
+ *++yyvsp = yyval;
+
+ /* Now `shift' the result of the reduction. Determine what state
+ that goes to, based on the state we popped back to and the rule
+ number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTOKENS];
+
+ goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+ /* Make sure we have latest lookahead translation. See comments at
+ user semantic actions for why this is necessary. */
+ yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar);
+
+ /* If not already recovering from an error, report this error. */
+ if (!yyerrstatus)
+ {
+ ++yynerrs;
+#if ! YYERROR_VERBOSE
+ yyerror (stack, scanner, YY_("syntax error"));
+#else
+# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \
+ yyssp, yytoken)
+ {
+ char const *yymsgp = YY_("syntax error");
+ int yysyntax_error_status;
+ yysyntax_error_status = YYSYNTAX_ERROR;
+ if (yysyntax_error_status == 0)
+ yymsgp = yymsg;
+ else if (yysyntax_error_status == 1)
+ {
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+ yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc);
+ if (!yymsg)
+ {
+ yymsg = yymsgbuf;
+ yymsg_alloc = sizeof yymsgbuf;
+ yysyntax_error_status = 2;
+ }
+ else
+ {
+ yysyntax_error_status = YYSYNTAX_ERROR;
+ yymsgp = yymsg;
+ }
+ }
+ yyerror (stack, scanner, yymsgp);
+ if (yysyntax_error_status == 2)
+ goto yyexhaustedlab;
+ }
+# undef YYSYNTAX_ERROR
+#endif
+ }
+
+
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse lookahead token after an
+ error, discard it. */
+
+ if (yychar <= YYEOF)
+ {
+ /* Return failure if at end of input. */
+ if (yychar == YYEOF)
+ YYABORT;
+ }
+ else
+ {
+ yydestruct ("Error: discarding",
+ yytoken, &yylval, stack, scanner);
+ yychar = YYEMPTY;
+ }
+ }
+
+ /* Else will try to reuse lookahead token after shifting the error
+ token. */
+ goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR. |
+`---------------------------------------------------*/
+yyerrorlab:
+
+ /* Pacify compilers like GCC when the user code never invokes
+ YYERROR and the label yyerrorlab therefore never appears in user
+ code. */
+ if (/*CONSTCOND*/ 0)
+ goto yyerrorlab;
+
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYERROR. */
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+ yystate = *yyssp;
+ goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR. |
+`-------------------------------------------------------------*/
+yyerrlab1:
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (!yypact_value_is_default (yyn))
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+
+ yydestruct ("Error: popping",
+ yystos[yystate], yyvsp, stack, scanner);
+ YYPOPSTACK (1);
+ yystate = *yyssp;
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+ *++yyvsp = yylval;
+ YY_IGNORE_MAYBE_UNINITIALIZED_END
+
+
+ /* Shift the error token. */
+ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+#if !defined yyoverflow || YYERROR_VERBOSE
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here. |
+`-------------------------------------------------*/
+yyexhaustedlab:
+ yyerror (stack, scanner, YY_("memory exhausted"));
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+ if (yychar != YYEMPTY)
+ {
+ /* Make sure we have latest lookahead translation. See comments at
+ user semantic actions for why this is necessary. */
+ yytoken = YYTRANSLATE (yychar);
+ yydestruct ("Cleanup: discarding lookahead",
+ yytoken, &yylval, stack, scanner);
+ }
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYABORT or YYACCEPT. */
+ YYPOPSTACK (yylen);
+ YY_STACK_PRINT (yyss, yyssp);
+ while (yyssp != yyss)
+ {
+ yydestruct ("Cleanup: popping",
+ yystos[*yyssp], yyvsp, stack, scanner);
+ YYPOPSTACK (1);
+ }
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+#endif
+ /* Make sure YYID is used. */
+ return YYID (yyresult);
+}
+
+
+/* Line 2055 of yacc.c */
+#line 168 "/usr/src/php-src/sapi/phpdbg/phpdbg_parser.y"
+
diff --git a/sapi/phpdbg/phpdbg_parser.h b/sapi/phpdbg/phpdbg_parser.h
new file mode 100644
index 0000000000..6acb8963c9
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_parser.h
@@ -0,0 +1,126 @@
+/* A Bison parser, made by GNU Bison 2.7. */
+
+/* Bison interface for Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989-1990, 2000-2012 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+#ifndef YY_YY_SAPI_PHPDBG_PHPDBG_PARSER_H_INCLUDED
+# define YY_YY_SAPI_PHPDBG_PHPDBG_PARSER_H_INCLUDED
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+#if YYDEBUG
+extern int yydebug;
+#endif
+/* "%code requires" blocks. */
+/* Line 2058 of yacc.c */
+#line 40 "/usr/src/php-src/sapi/phpdbg/phpdbg_parser.y"
+
+#include "phpdbg.h"
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+
+
+/* Line 2058 of yacc.c */
+#line 55 "sapi/phpdbg/phpdbg_parser.h"
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ T_EVAL = 258,
+ T_RUN = 259,
+ T_SHELL = 260,
+ T_IF = 261,
+ T_TRUTHY = 262,
+ T_FALSY = 263,
+ T_STRING = 264,
+ T_COLON = 265,
+ T_DCOLON = 266,
+ T_POUND = 267,
+ T_PROTO = 268,
+ T_DIGITS = 269,
+ T_LITERAL = 270,
+ T_ADDR = 271,
+ T_OPCODE = 272,
+ T_ID = 273,
+ T_INPUT = 274,
+ T_UNEXPECTED = 275
+ };
+#endif
+/* Tokens. */
+#define T_EVAL 258
+#define T_RUN 259
+#define T_SHELL 260
+#define T_IF 261
+#define T_TRUTHY 262
+#define T_FALSY 263
+#define T_STRING 264
+#define T_COLON 265
+#define T_DCOLON 266
+#define T_POUND 267
+#define T_PROTO 268
+#define T_DIGITS 269
+#define T_LITERAL 270
+#define T_ADDR 271
+#define T_OPCODE 272
+#define T_ID 273
+#define T_INPUT 274
+#define T_UNEXPECTED 275
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef int YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void *YYPARSE_PARAM);
+#else
+int yyparse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int yyparse (phpdbg_param_t *stack, yyscan_t scanner);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+#endif /* !YY_YY_SAPI_PHPDBG_PHPDBG_PARSER_H_INCLUDED */
diff --git a/sapi/phpdbg/phpdbg_parser.y b/sapi/phpdbg/phpdbg_parser.y
new file mode 100644
index 0000000000..4a84504e2e
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_parser.y
@@ -0,0 +1,168 @@
+%error-verbose
+%{
+
+/*
+ * phpdbg_parser.y
+ * (from php-src root)
+ * flex sapi/phpdbg/dev/phpdbg_lexer.l
+ * bison sapi/phpdbg/dev/phpdbg_parser.y
+ */
+
+#include "phpdbg.h"
+#include "phpdbg_cmd.h"
+#include "phpdbg_utils.h"
+#include "phpdbg_cmd.h"
+#include "phpdbg_prompt.h"
+
+#define YYSTYPE phpdbg_param_t
+
+#include "phpdbg_parser.h"
+#include "phpdbg_lexer.h"
+
+ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+
+int yyerror(phpdbg_param_t *stack, yyscan_t scanner, const char *msg) {
+ TSRMLS_FETCH();
+ phpdbg_error("Parse Error: %s", msg);
+ {
+ const phpdbg_param_t *top = stack;
+
+ while (top) {
+ phpdbg_param_debug(
+ top, "--> ");
+ top = top->next;
+ }
+ }
+ return 0;
+}
+%}
+
+%code requires {
+#include "phpdbg.h"
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+}
+%output "sapi/phpdbg/phpdbg_parser.c"
+%defines "sapi/phpdbg/phpdbg_parser.h"
+
+%define api.pure
+%lex-param { yyscan_t scanner }
+%parse-param { phpdbg_param_t *stack }
+%parse-param { yyscan_t scanner }
+
+%token T_EVAL "eval"
+%token T_RUN "run"
+%token T_SHELL "shell"
+%token T_IF "if (condition)"
+%token T_TRUTHY "truthy (true, on, yes or enabled)"
+%token T_FALSY "falsy (false, off, no or disabled)"
+%token T_STRING "string (some input, perhaps)"
+%token T_COLON ": (colon)"
+%token T_DCOLON ":: (double colon)"
+%token T_POUND "# (pound sign)"
+%token T_PROTO "protocol (file://)"
+%token T_DIGITS "digits (numbers)"
+%token T_LITERAL "literal (string)"
+%token T_ADDR "address"
+%token T_OPCODE "opcode"
+%token T_ID "identifier (command or function name)"
+%token T_INPUT "input (input string or data)"
+%token T_UNEXPECTED "input"
+%%
+
+input
+ : parameters
+ | /* nothing */
+ ;
+
+parameters
+ : parameter { phpdbg_stack_push(stack, &$1); }
+ | parameters parameter { phpdbg_stack_push(stack, &$2); }
+ ;
+
+parameter
+ : T_ID T_COLON T_DIGITS {
+ $$.type = FILE_PARAM;
+ $$.file.name = $2.str;
+ $$.file.line = $3.num;
+ }
+ | T_ID T_COLON T_POUND T_DIGITS {
+ $$.type = NUMERIC_FILE_PARAM;
+ $$.file.name = $1.str;
+ $$.file.line = $4.num;
+ }
+ | T_PROTO T_ID T_COLON T_DIGITS {
+ $$.type = FILE_PARAM;
+ $$.file.name = malloc($1.len +
+ $2.len + 1);
+ if ($$.file.name) {
+ memcpy(&$$.file.name[0], $1.str, $1.len);
+ memcpy(&$$.file.name[$1.len], $2.str, $2.len);
+ $$.file.name[$1.len + $2.len] = '\0';
+ }
+ $$.file.line = $4.num;
+ }
+ | T_PROTO T_ID T_COLON T_POUND T_DIGITS {
+ $$.type = NUMERIC_FILE_PARAM;
+ $$.file.name = malloc($1.len +
+ $2.len + 1);
+ if ($$.file.name) {
+ memcpy(&$$.file.name[0], $1.str, $1.len);
+ memcpy(&$$.file.name[$1.len], $2.str, $2.len);
+ $$.file.name[$1.len + $2.len] = '\0';
+ }
+ $$.file.line = $5.num;
+ }
+ | T_ID T_DCOLON T_ID {
+ $$.type = METHOD_PARAM;
+ $$.method.class = $1.str;
+ $$.method.name = $3.str;
+ }
+ | T_ID T_DCOLON T_ID T_POUND T_DIGITS {
+ $$.type = NUMERIC_METHOD_PARAM;
+ $$.method.class = $1.str;
+ $$.method.name = $3.str;
+ $$.num = $5.num;
+ }
+ | T_ID T_POUND T_DIGITS {
+ $$.type = NUMERIC_FUNCTION_PARAM;
+ $$.str = $1.str;
+ $$.len = $1.len;
+ $$.num = $3.num;
+ }
+ | T_IF T_INPUT {
+ $$.type = COND_PARAM;
+ $$.str = $2.str;
+ $$.len = $2.len;
+ }
+ | T_EVAL T_INPUT {
+ $$.type = EVAL_PARAM;
+ $$.str = $2.str;
+ $$.len = $2.len;
+ }
+ | T_SHELL T_INPUT {
+ $$.type = SHELL_PARAM;
+ $$.str = $2.str;
+ $$.len = $2.len;
+ }
+ | T_RUN {
+ $$.type = RUN_PARAM;
+ $$.len = 0;
+ }
+ | T_RUN T_INPUT {
+ $$.type = RUN_PARAM;
+ $$.str = $2.str;
+ $$.len = $2.len;
+ }
+ | T_OPCODE { $$ = $1; }
+ | T_ADDR { $$ = $1; }
+ | T_LITERAL { $$ = $1; }
+ | T_TRUTHY { $$ = $1; }
+ | T_FALSY { $$ = $1; }
+ | T_DIGITS { $$ = $1; }
+ | T_ID { $$ = $1; }
+ ;
+
+%%
diff --git a/sapi/phpdbg/phpdbg_print.c b/sapi/phpdbg/phpdbg_print.c
index c4925fe5fd..76321a5042 100644
--- a/sapi/phpdbg/phpdbg_print.c
+++ b/sapi/phpdbg/phpdbg_print.c
@@ -26,6 +26,19 @@
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+#define PHPDBG_PRINT_COMMAND_D(f, h, a, m, l, s) \
+ PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[9])
+
+const phpdbg_command_t phpdbg_print_commands[] = {
+ PHPDBG_PRINT_COMMAND_D(exec, "print out the instructions in the execution context", 'e', print_exec, NULL, 0),
+ PHPDBG_PRINT_COMMAND_D(opline, "print out the instruction in the current opline", 'o', print_opline, NULL, 0),
+ PHPDBG_PRINT_COMMAND_D(class, "print out the instructions in the specified class", 'c', print_class, NULL, "s"),
+ PHPDBG_PRINT_COMMAND_D(method, "print out the instructions in the specified method", 'm', print_method, NULL, "m"),
+ PHPDBG_PRINT_COMMAND_D(func, "print out the instructions in the specified function", 'f', print_func, NULL, "s"),
+ PHPDBG_PRINT_COMMAND_D(stack, "print out the instructions in the current stack", 's', print_stack, NULL, 0),
+ PHPDBG_END_COMMAND
+};
+
PHPDBG_PRINT(opline) /* {{{ */
{
if (EG(in_execution) && EG(current_execute_data)) {
@@ -77,7 +90,7 @@ static inline void phpdbg_print_function_helper(zend_function *method TSRMLS_DC)
phpdbg_error("\tFailed to decode opline %16p", opline);
}
opline++;
- } while (++opcode < end);
+ } while (opcode++ < end);
zend_hash_destroy(&vars);
}
} break;
@@ -141,36 +154,30 @@ PHPDBG_PRINT(class) /* {{{ */
{
zend_class_entry **ce;
- switch (param->type) {
- case STR_PARAM: {
- if (zend_lookup_class(param->str, param->len, &ce TSRMLS_CC) == SUCCESS) {
- phpdbg_notice("%s %s: %s",
- ((*ce)->type == ZEND_USER_CLASS) ?
- "User" : "Internal",
- ((*ce)->ce_flags & ZEND_ACC_INTERFACE) ?
- "Interface" :
- ((*ce)->ce_flags & ZEND_ACC_ABSTRACT) ?
- "Abstract Class" :
- "Class",
- (*ce)->name);
-
- phpdbg_writeln("Methods (%d):", zend_hash_num_elements(&(*ce)->function_table));
- if (zend_hash_num_elements(&(*ce)->function_table)) {
- HashPosition position;
- zend_function *method;
-
- for (zend_hash_internal_pointer_reset_ex(&(*ce)->function_table, &position);
- zend_hash_get_current_data_ex(&(*ce)->function_table, (void**) &method, &position) == SUCCESS;
- zend_hash_move_forward_ex(&(*ce)->function_table, &position)) {
- phpdbg_print_function_helper(method TSRMLS_CC);
- }
- }
- } else {
- phpdbg_error("The class %s could not be found", param->str);
+ if (zend_lookup_class(param->str, param->len, &ce TSRMLS_CC) == SUCCESS) {
+ phpdbg_notice("%s %s: %s",
+ ((*ce)->type == ZEND_USER_CLASS) ?
+ "User" : "Internal",
+ ((*ce)->ce_flags & ZEND_ACC_INTERFACE) ?
+ "Interface" :
+ ((*ce)->ce_flags & ZEND_ACC_ABSTRACT) ?
+ "Abstract Class" :
+ "Class",
+ (*ce)->name);
+
+ phpdbg_writeln("Methods (%d):", zend_hash_num_elements(&(*ce)->function_table));
+ if (zend_hash_num_elements(&(*ce)->function_table)) {
+ HashPosition position;
+ zend_function *method;
+
+ for (zend_hash_internal_pointer_reset_ex(&(*ce)->function_table, &position);
+ zend_hash_get_current_data_ex(&(*ce)->function_table, (void**) &method, &position) == SUCCESS;
+ zend_hash_move_forward_ex(&(*ce)->function_table, &position)) {
+ phpdbg_print_function_helper(method TSRMLS_CC);
}
- } break;
-
- phpdbg_default_switch_case();
+ }
+ } else {
+ phpdbg_error("The class %s could not be found", param->str);
}
return SUCCESS;
@@ -178,31 +185,25 @@ PHPDBG_PRINT(class) /* {{{ */
PHPDBG_PRINT(method) /* {{{ */
{
- switch (param->type) {
- case METHOD_PARAM: {
- zend_class_entry **ce;
-
- if (zend_lookup_class(param->method.class, strlen(param->method.class), &ce TSRMLS_CC) == SUCCESS) {
- zend_function *fbc;
- char *lcname = zend_str_tolower_dup(param->method.name, strlen(param->method.name));
+ zend_class_entry **ce;
- if (zend_hash_find(&(*ce)->function_table, lcname, strlen(lcname)+1, (void**)&fbc) == SUCCESS) {
- phpdbg_notice("%s Method %s",
- (fbc->type == ZEND_USER_FUNCTION) ? "User" : "Internal",
- fbc->common.function_name);
+ if (zend_lookup_class(param->method.class, strlen(param->method.class), &ce TSRMLS_CC) == SUCCESS) {
+ zend_function *fbc;
+ char *lcname = zend_str_tolower_dup(param->method.name, strlen(param->method.name));
- phpdbg_print_function_helper(fbc TSRMLS_CC);
- } else {
- phpdbg_error("The method %s could not be found", param->method.name);
- }
+ if (zend_hash_find(&(*ce)->function_table, lcname, strlen(lcname)+1, (void**)&fbc) == SUCCESS) {
+ phpdbg_notice("%s Method %s",
+ (fbc->type == ZEND_USER_FUNCTION) ? "User" : "Internal",
+ fbc->common.function_name);
- efree(lcname);
- } else {
- phpdbg_error("The class %s could not be found", param->method.class);
- }
- } break;
+ phpdbg_print_function_helper(fbc TSRMLS_CC);
+ } else {
+ phpdbg_error("The method %s could not be found", param->method.name);
+ }
- phpdbg_default_switch_case();
+ efree(lcname);
+ } else {
+ phpdbg_error("The class %s could not be found", param->method.class);
}
return SUCCESS;
@@ -210,49 +211,43 @@ PHPDBG_PRINT(method) /* {{{ */
PHPDBG_PRINT(func) /* {{{ */
{
- switch (param->type) {
- case STR_PARAM: {
- HashTable *func_table = EG(function_table);
- zend_function* fbc;
- const char *func_name = param->str;
- size_t func_name_len = param->len;
- char *lcname;
- /* search active scope if begins with period */
- if (func_name[0] == '.') {
- if (EG(scope)) {
- func_name++;
- func_name_len--;
-
- func_table = &EG(scope)->function_table;
- } else {
- phpdbg_error("No active class");
- return SUCCESS;
- }
- } else if (!EG(function_table)) {
- phpdbg_error("No function table loaded");
- return SUCCESS;
- } else {
- func_table = EG(function_table);
- }
-
- lcname = zend_str_tolower_dup(func_name, func_name_len);
-
- if (zend_hash_find(func_table, lcname, strlen(lcname)+1, (void**)&fbc) == SUCCESS) {
- phpdbg_notice("%s %s %s",
- (fbc->type == ZEND_USER_FUNCTION) ? "User" : "Internal",
- (fbc->common.scope) ? "Method" : "Function",
- fbc->common.function_name);
+ HashTable *func_table = EG(function_table);
+ zend_function* fbc;
+ const char *func_name = param->str;
+ size_t func_name_len = param->len;
+ char *lcname;
+ /* search active scope if begins with period */
+ if (func_name[0] == '.') {
+ if (EG(scope)) {
+ func_name++;
+ func_name_len--;
+
+ func_table = &EG(scope)->function_table;
+ } else {
+ phpdbg_error("No active class");
+ return SUCCESS;
+ }
+ } else if (!EG(function_table)) {
+ phpdbg_error("No function table loaded");
+ return SUCCESS;
+ } else {
+ func_table = EG(function_table);
+ }
- phpdbg_print_function_helper(fbc TSRMLS_CC);
- } else {
- phpdbg_error("The function %s could not be found", func_name);
- }
+ lcname = zend_str_tolower_dup(func_name, func_name_len);
- efree(lcname);
- } break;
+ if (zend_hash_find(func_table, lcname, strlen(lcname)+1, (void**)&fbc) == SUCCESS) {
+ phpdbg_notice("%s %s %s",
+ (fbc->type == ZEND_USER_FUNCTION) ? "User" : "Internal",
+ (fbc->common.scope) ? "Method" : "Function",
+ fbc->common.function_name);
- phpdbg_default_switch_case();
+ phpdbg_print_function_helper(fbc TSRMLS_CC);
+ } else {
+ phpdbg_error("The function %s could not be found", func_name);
}
+ efree(lcname);
+
return SUCCESS;
} /* }}} */
diff --git a/sapi/phpdbg/phpdbg_print.h b/sapi/phpdbg/phpdbg_print.h
index 80010d5e7c..ed85e965c1 100644
--- a/sapi/phpdbg/phpdbg_print.h
+++ b/sapi/phpdbg/phpdbg_print.h
@@ -35,17 +35,6 @@ PHPDBG_PRINT(method);
PHPDBG_PRINT(func);
PHPDBG_PRINT(stack);
-/**
- * Commands
- */
-static const phpdbg_command_t phpdbg_print_commands[] = {
- PHPDBG_COMMAND_D_EX(exec, "print out the instructions in the execution context", 'e', print_exec, NULL, 0),
- PHPDBG_COMMAND_D_EX(opline, "print out the instruction in the current opline", 'o', print_opline, NULL, 0),
- PHPDBG_COMMAND_D_EX(class, "print out the instructions in the specified class", 'c', print_class, NULL, 1),
- PHPDBG_COMMAND_D_EX(method, "print out the instructions in the specified method", 'm', print_method, NULL, 1),
- PHPDBG_COMMAND_D_EX(func, "print out the instructions in the specified function", 'f', print_func, NULL, 1),
- PHPDBG_COMMAND_D_EX(stack, "print out the instructions in the current stack", 's', print_stack, NULL, 0),
- PHPDBG_END_COMMAND
-};
+extern const phpdbg_command_t phpdbg_print_commands[];
#endif /* PHPDBG_PRINT_H */
diff --git a/sapi/phpdbg/phpdbg_prompt.c b/sapi/phpdbg/phpdbg_prompt.c
index f586bb5a84..7a28a75129 100644
--- a/sapi/phpdbg/phpdbg_prompt.c
+++ b/sapi/phpdbg/phpdbg_prompt.c
@@ -35,99 +35,154 @@
#include "phpdbg_cmd.h"
#include "phpdbg_set.h"
#include "phpdbg_frame.h"
+#include "phpdbg_lexer.h"
+#include "phpdbg_parser.h"
+
+int yyparse(phpdbg_param_t *stack, yyscan_t scanner);
/* {{{ command declarations */
const phpdbg_command_t phpdbg_prompt_commands[] = {
- PHPDBG_COMMAND_D(exec, "set execution context", 'e', NULL, 1),
- PHPDBG_COMMAND_D(compile, "attempt compilation", 'c', NULL, 0),
- PHPDBG_COMMAND_D(step, "step through execution", 's', NULL, 1),
- PHPDBG_COMMAND_D(next, "continue execution", 'n', NULL, 0),
- PHPDBG_COMMAND_D(run, "attempt execution", 'r', NULL, 0),
- PHPDBG_COMMAND_D(eval, "evaluate some code", 'E', NULL, 1),
+ PHPDBG_COMMAND_D(exec, "set execution context", 'e', NULL, "s"),
+ PHPDBG_COMMAND_D(step, "step through execution", 's', NULL, 0),
+ PHPDBG_COMMAND_D(continue,"continue execution", 'c', NULL, 0),
+ PHPDBG_COMMAND_D(run, "attempt execution", 'r', NULL, "|s"),
+ PHPDBG_COMMAND_D(ev, "evaluate some code", 0, NULL, "i"),
PHPDBG_COMMAND_D(until, "continue past the current line", 'u', NULL, 0),
PHPDBG_COMMAND_D(finish, "continue past the end of the stack", 'F', NULL, 0),
PHPDBG_COMMAND_D(leave, "continue until the end of the stack", 'L', NULL, 0),
- PHPDBG_COMMAND_D(print, "print something", 'p', phpdbg_print_commands, 2),
- PHPDBG_COMMAND_D(break, "set breakpoint", 'b', phpdbg_break_commands, 1),
- PHPDBG_COMMAND_D(back, "show trace", 't', NULL, 0),
- PHPDBG_COMMAND_D(frame, "switch to a frame", 'f', NULL, 1),
- PHPDBG_COMMAND_D(list, "lists some code", 'l', phpdbg_list_commands, 2),
- PHPDBG_COMMAND_D(info, "displays some informations", 'i', phpdbg_info_commands, 1),
+ PHPDBG_COMMAND_D(print, "print something", 'p', phpdbg_print_commands, 0),
+ PHPDBG_COMMAND_D(break, "set breakpoint", 'b', phpdbg_break_commands, "|*c"),
+ PHPDBG_COMMAND_D(back, "show trace", 't', NULL, "|n"),
+ PHPDBG_COMMAND_D(frame, "switch to a frame", 'f', NULL, "|n"),
+ PHPDBG_COMMAND_D(list, "lists some code", 'l', phpdbg_list_commands, "*"),
+ PHPDBG_COMMAND_D(info, "displays some informations", 'i', phpdbg_info_commands, "s"),
PHPDBG_COMMAND_D(clean, "clean the execution environment", 'X', NULL, 0),
PHPDBG_COMMAND_D(clear, "clear breakpoints", 'C', NULL, 0),
- PHPDBG_COMMAND_D(help, "show help menu", 'h', phpdbg_help_commands, 2),
- PHPDBG_COMMAND_D(quiet, "silence some output", 'Q', NULL, 1),
- PHPDBG_COMMAND_D(aliases, "show alias list", 'a', NULL, 0),
- PHPDBG_COMMAND_D(set, "set phpdbg configuration", 'S', phpdbg_set_commands, 1),
- PHPDBG_COMMAND_D(register,"register a function", 'R', NULL, 1),
- PHPDBG_COMMAND_D(source, "execute a phpdbginit", '.', NULL, 1),
- PHPDBG_COMMAND_D(shell, "shell a command", '-', NULL, 1),
+ PHPDBG_COMMAND_D(help, "show help menu", 'h', phpdbg_help_commands, "|s"),
+ PHPDBG_COMMAND_D(set, "set phpdbg configuration", 'S', phpdbg_set_commands, "s"),
+ PHPDBG_COMMAND_D(register,"register a function", 'R', NULL, "s"),
+ PHPDBG_COMMAND_D(source, "execute a phpdbginit", '<', NULL, "s"),
+ PHPDBG_COMMAND_D(export, "export breaks to a .phpdbginit script", '>', NULL, "s"),
+ PHPDBG_COMMAND_D(sh, "shell a command", 0, NULL, "i"),
PHPDBG_COMMAND_D(quit, "exit phpdbg", 'q', NULL, 0),
+ PHPDBG_COMMAND_D(watch, "set watchpoint", 'w', phpdbg_watch_commands, "|ss"),
PHPDBG_END_COMMAND
}; /* }}} */
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
-static inline int phpdbg_call_register(phpdbg_input_t *input TSRMLS_DC) /* {{{ */
+static inline int phpdbg_call_register(phpdbg_param_t *stack TSRMLS_DC) /* {{{ */
{
- phpdbg_input_t *function = input->argv[0];
-
- if (zend_hash_exists(
- &PHPDBG_G(registered), function->string, function->length+1)) {
-
- zval fname, *fretval;
- zend_fcall_info fci;
-
- ZVAL_STRINGL(&fname, function->string, function->length, 1);
-
- memset(&fci, 0, sizeof(zend_fcall_info));
-
- fci.size = sizeof(zend_fcall_info);
- fci.function_table = &PHPDBG_G(registered);
- fci.function_name = &fname;
- fci.symbol_table = EG(active_symbol_table);
- fci.object_ptr = NULL;
- fci.retval_ptr_ptr = &fretval;
- fci.no_separation = 1;
-
- if (input->argc > 1) {
- int param;
- zval params;
-
- array_init(&params);
+ phpdbg_param_t *name = NULL;
- for (param = 0; param < (input->argc-1); param++) {
- add_next_index_stringl(
- &params,
- input->argv[param+1]->string,
- input->argv[param+1]->length, 1);
+ if (stack->type == STACK_PARAM) {
+ name = stack->next;
+
+ if (!name || name->type != STR_PARAM) {
+ return FAILURE;
+ }
+
+ if (zend_hash_exists(
+ &PHPDBG_G(registered), name->str, name->len+1)) {
+
+ zval fname, *fretval;
+ zend_fcall_info fci;
+
+ ZVAL_STRINGL(&fname, name->str, name->len, 1);
+
+ memset(&fci, 0, sizeof(zend_fcall_info));
+
+ fci.size = sizeof(zend_fcall_info);
+ fci.function_table = &PHPDBG_G(registered);
+ fci.function_name = &fname;
+ fci.symbol_table = EG(active_symbol_table);
+ fci.object_ptr = NULL;
+ fci.retval_ptr_ptr = &fretval;
+ fci.no_separation = 1;
+
+ if (name->next) {
+ zval params;
+ phpdbg_param_t *next = name->next;
+
+ array_init(&params);
+
+ while (next) {
+ char *buffered = NULL;
+
+ switch (next->type) {
+ case OP_PARAM:
+ case COND_PARAM:
+ case STR_PARAM:
+ add_next_index_stringl(
+ &params,
+ next->str,
+ next->len, 1);
+ break;
+
+ case NUMERIC_PARAM:
+ add_next_index_long(&params, next->num);
+ break;
+
+ case METHOD_PARAM:
+ spprintf(&buffered, 0, "%s::%s",
+ next->method.class, next->method.name);
+ add_next_index_string(&params, buffered, 0);
+ break;
+
+ case NUMERIC_METHOD_PARAM:
+ spprintf(&buffered, 0, "%s::%s#%ld",
+ next->method.class, next->method.name, next->num);
+ add_next_index_string(&params, buffered, 0);
+ break;
+
+ case NUMERIC_FUNCTION_PARAM:
+ spprintf(&buffered, 0, "%s#%ld",
+ next->str, next->num);
+ add_next_index_string(&params, buffered, 0);
+ break;
+
+ case FILE_PARAM:
+ spprintf(&buffered, 0, "%s:%ld",
+ next->file.name, next->file.line);
+ add_next_index_string(&params, buffered, 0);
+ break;
+
+ case NUMERIC_FILE_PARAM:
+ spprintf(&buffered, 0, "%s:#%ld",
+ next->file.name, next->file.line);
+ add_next_index_string(&params, buffered, 0);
+ break;
+
+ default: {
+ /* not yet */
+ }
+ }
+
+ next = next->next;
+ }
- phpdbg_debug(
- "created param[%d] from argv[%d]: %s",
- param, param+1, input->argv[param+1]->string);
+ zend_fcall_info_args(&fci, &params TSRMLS_CC);
+ } else {
+ fci.params = NULL;
+ fci.param_count = 0;
}
- zend_fcall_info_args(&fci, &params TSRMLS_CC);
- } else {
- fci.params = NULL;
- fci.param_count = 0;
- }
-
- phpdbg_debug(
- "created %d params from %d arguments",
- fci.param_count, input->argc);
+ phpdbg_debug(
+ "created %d params from arguments",
+ fci.param_count);
- zend_call_function(&fci, NULL TSRMLS_CC);
+ zend_call_function(&fci, NULL TSRMLS_CC);
- if (fretval) {
- zend_print_zval_r(
- fretval, 0 TSRMLS_CC);
- phpdbg_writeln(EMPTY);
- }
+ if (fretval) {
+ zend_print_zval_r(
+ fretval, 0 TSRMLS_CC);
+ phpdbg_writeln(EMPTY);
+ }
- zval_dtor(&fname);
+ zval_dtor(&fname);
- return SUCCESS;
+ return SUCCESS;
+ }
}
return FAILURE;
@@ -136,7 +191,7 @@ static inline int phpdbg_call_register(phpdbg_input_t *input TSRMLS_DC) /* {{{ *
void phpdbg_try_file_init(char *init_file, size_t init_file_len, zend_bool free_init TSRMLS_DC) /* {{{ */
{
struct stat sb;
-
+
if (init_file && VCWD_STAT(init_file, &sb) != -1) {
FILE *fp = fopen(init_file, "r");
if (fp) {
@@ -190,19 +245,47 @@ void phpdbg_try_file_init(char *init_file, size_t init_file_len, zend_bool free_
}
{
- phpdbg_input_t *input = phpdbg_read_input(cmd TSRMLS_CC);
- switch (phpdbg_do_cmd(phpdbg_prompt_commands, input TSRMLS_CC)) {
- case FAILURE:
- if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
- if (phpdbg_call_register(input TSRMLS_CC) == FAILURE) {
- phpdbg_error("Unrecognized command in %s:%d: %s!", init_file, line, input->string);
- }
- }
+ char *why = NULL;
+ char *input = phpdbg_read_input(cmd TSRMLS_CC);
+ phpdbg_param_t stack;
+ yyscan_t scanner;
+ YY_BUFFER_STATE state;
+
+ phpdbg_init_param(&stack, STACK_PARAM);
+
+ if (yylex_init(&scanner)) {
+ phpdbg_error(
+ "could not initialize scanner");
break;
}
+
+ state = yy_scan_string(input, scanner);
+
+ if (yyparse(&stack, scanner) <= 0) {
+ switch (phpdbg_stack_execute(&stack, &why TSRMLS_CC)) {
+ case FAILURE:
+// if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
+ if (phpdbg_call_register(&stack TSRMLS_CC) == FAILURE) {
+ phpdbg_error(
+ "Unrecognized command in %s:%d: %s, %s!",
+ init_file, line, input, why);
+ }
+// }
+ break;
+ }
+ }
+
+ if (why) {
+ free(why);
+ why = NULL;
+ }
+
+ yy_delete_buffer(state, scanner);
+ yylex_destroy(scanner);
+
+ phpdbg_stack_free(&stack);
phpdbg_destroy_input(&input TSRMLS_CC);
}
-
}
next_line:
line++;
@@ -264,47 +347,47 @@ void phpdbg_init(char *init_file, size_t init_file_len, zend_bool use_default TS
PHPDBG_COMMAND(exec) /* {{{ */
{
- switch (param->type) {
- case STR_PARAM: {
- struct stat sb;
-
- if (VCWD_STAT(param->str, &sb) != FAILURE) {
- if (sb.st_mode & (S_IFREG|S_IFLNK)) {
- char *res = phpdbg_resolve_path(param->str TSRMLS_CC);
- size_t res_len = strlen(res);
-
- if ((res_len != PHPDBG_G(exec_len)) || (memcmp(res, PHPDBG_G(exec), res_len) != SUCCESS)) {
-
- if (PHPDBG_G(exec)) {
- phpdbg_notice("Unsetting old execution context: %s", PHPDBG_G(exec));
- efree(PHPDBG_G(exec));
- PHPDBG_G(exec) = NULL;
- PHPDBG_G(exec_len) = 0L;
- }
+ struct stat sb;
- if (PHPDBG_G(ops)) {
- phpdbg_notice("Destroying compiled opcodes");
- phpdbg_clean(0 TSRMLS_CC);
- }
+ if (VCWD_STAT(param->str, &sb) != FAILURE) {
+ if (sb.st_mode & (S_IFREG|S_IFLNK)) {
+ char *res = phpdbg_resolve_path(param->str TSRMLS_CC);
+ size_t res_len = strlen(res);
- PHPDBG_G(exec) = res;
- PHPDBG_G(exec_len) = res_len;
+ if ((res_len != PHPDBG_G(exec_len)) || (memcmp(res, PHPDBG_G(exec), res_len) != SUCCESS)) {
- phpdbg_notice("Set execution context: %s", PHPDBG_G(exec));
- } else {
- phpdbg_notice("Execution context not changed");
- }
- } else {
- phpdbg_error("Cannot use %s as execution context, not a valid file or symlink", param->str);
+ if (PHPDBG_G(exec)) {
+ phpdbg_notice("Unsetting old execution context: %s", PHPDBG_G(exec));
+ efree(PHPDBG_G(exec));
+ PHPDBG_G(exec) = NULL;
+ PHPDBG_G(exec_len) = 0L;
+ }
+
+ if (PHPDBG_G(ops)) {
+ phpdbg_notice("Destroying compiled opcodes");
+ phpdbg_clean(0 TSRMLS_CC);
+ }
+
+ PHPDBG_G(exec) = res;
+ PHPDBG_G(exec_len) = res_len;
+
+ *SG(request_info).argv = PHPDBG_G(exec);
+ php_hash_environment(TSRMLS_C);
+
+ phpdbg_notice("Set execution context: %s", PHPDBG_G(exec));
+
+ if (phpdbg_compile(TSRMLS_C) == FAILURE) {
+ phpdbg_error("Failed to compile %s", PHPDBG_G(exec));
}
} else {
- phpdbg_error("Cannot stat %s, ensure the file exists", param->str);
+ phpdbg_notice("Execution context not changed");
}
- } break;
-
- phpdbg_default_switch_case();
+ } else {
+ phpdbg_error("Cannot use %s as execution context, not a valid file or symlink", param->str);
+ }
+ } else {
+ phpdbg_error("Cannot stat %s, ensure the file exists", param->str);
}
-
return SUCCESS;
} /* }}} */
@@ -312,6 +395,11 @@ int phpdbg_compile(TSRMLS_D) /* {{{ */
{
zend_file_handle fh;
+ if (!PHPDBG_G(exec)) {
+ phpdbg_error("No execution context");
+ return SUCCESS;
+ }
+
if (EG(in_execution)) {
phpdbg_error("Cannot compile while in execution");
return FAILURE;
@@ -334,47 +422,16 @@ int phpdbg_compile(TSRMLS_D) /* {{{ */
return FAILURE;
} /* }}} */
-PHPDBG_COMMAND(compile) /* {{{ */
-{
- if (!PHPDBG_G(exec)) {
- phpdbg_error("No execution context");
- return SUCCESS;
- }
-
- if (!EG(in_execution)) {
- if (PHPDBG_G(ops)) {
- phpdbg_error("Destroying previously compiled opcodes");
- phpdbg_clean(0 TSRMLS_CC);
- }
- }
-
- phpdbg_compile(TSRMLS_C);
-
- return SUCCESS;
-} /* }}} */
-
PHPDBG_COMMAND(step) /* {{{ */
{
- switch (param->type) {
- case EMPTY_PARAM:
- case NUMERIC_PARAM: {
- if (param->type == NUMERIC_PARAM && param->num) {
- PHPDBG_G(flags) |= PHPDBG_IS_STEPPING;
- } else {
- PHPDBG_G(flags) &= ~PHPDBG_IS_STEPPING;
- }
-
- phpdbg_notice("Stepping %s",
- (PHPDBG_G(flags) & PHPDBG_IS_STEPPING) ? "on" : "off");
- } break;
-
- phpdbg_default_switch_case();
+ if (EG(in_execution)) {
+ PHPDBG_G(flags) |= PHPDBG_IS_STEPPING;
}
- return SUCCESS;
+ return PHPDBG_NEXT;
} /* }}} */
-PHPDBG_COMMAND(next) /* {{{ */
+PHPDBG_COMMAND(continue) /* {{{ */
{
return PHPDBG_NEXT;
} /* }}} */
@@ -475,17 +532,9 @@ PHPDBG_COMMAND(leave) /* {{{ */
PHPDBG_COMMAND(frame) /* {{{ */
{
- switch (param->type) {
- case NUMERIC_PARAM:
- phpdbg_switch_frame(param->num TSRMLS_CC);
- break;
-
- case EMPTY_PARAM:
- phpdbg_notice("Currently in frame #%d", PHPDBG_G(frame).num);
- break;
-
- phpdbg_default_switch_case();
- }
+ if (!param) {
+ phpdbg_notice("Currently in frame #%d", PHPDBG_G(frame).num);
+ } else phpdbg_switch_frame(param->num TSRMLS_CC);
return SUCCESS;
} /* }}} */
@@ -580,6 +629,31 @@ PHPDBG_COMMAND(run) /* {{{ */
/* reset hit counters */
phpdbg_reset_breakpoints(TSRMLS_C);
+ if (param && param->type != EMPTY_PARAM && param->len != 0) {
+ char **argv = emalloc(5 * sizeof(char *));
+ int argc = 0;
+ int i;
+ char *argv_str = strtok(param->str, " ");
+
+ while (argv_str) {
+ if (argc >= 4 && argc == (argc & -argc)) {
+ argv = erealloc(argv, (argc * 2 + 1) * sizeof(char *));
+ }
+ argv[++argc] = argv_str;
+ argv_str = strtok(0, " ");
+ argv[argc] = estrdup(argv[argc]);
+ }
+ argv[0] = SG(request_info).argv[0];
+ for (i = SG(request_info).argc; --i;) {
+ efree(SG(request_info).argv[i]);
+ }
+ efree(SG(request_info).argv);
+ SG(request_info).argv = erealloc(argv, ++argc * sizeof(char *));
+ SG(request_info).argc = argc;
+
+ php_hash_environment(TSRMLS_C);
+ }
+
zend_try {
php_output_activate(TSRMLS_C);
PHPDBG_G(flags) ^= PHPDBG_IS_INTERACTIVE;
@@ -615,42 +689,36 @@ out:
return SUCCESS;
} /* }}} */
-PHPDBG_COMMAND(eval) /* {{{ */
+PHPDBG_COMMAND(ev) /* {{{ */
{
- switch (param->type) {
- case STR_PARAM: {
- zend_bool stepping = ((PHPDBG_G(flags) & PHPDBG_IS_STEPPING)==PHPDBG_IS_STEPPING);
- zval retval;
-
- if (!(PHPDBG_G(flags) & PHPDBG_IS_STEPONEVAL)) {
- PHPDBG_G(flags) &= ~ PHPDBG_IS_STEPPING;
- }
-
- /* disable stepping while eval() in progress */
- PHPDBG_G(flags) |= PHPDBG_IN_EVAL;
- zend_try {
- if (zend_eval_stringl(param->str, param->len,
- &retval, "eval()'d code" TSRMLS_CC) == SUCCESS) {
- zend_print_zval_r(
- &retval, 0 TSRMLS_CC);
- phpdbg_writeln(EMPTY);
- zval_dtor(&retval);
- }
- } zend_end_try();
- PHPDBG_G(flags) &= ~PHPDBG_IN_EVAL;
+ zend_bool stepping = ((PHPDBG_G(flags) & PHPDBG_IS_STEPPING)==PHPDBG_IS_STEPPING);
+ zval retval;
- /* switch stepping back on */
- if (stepping &&
- !(PHPDBG_G(flags) & PHPDBG_IS_STEPONEVAL)) {
- PHPDBG_G(flags) |= PHPDBG_IS_STEPPING;
- }
+ if (!(PHPDBG_G(flags) & PHPDBG_IS_STEPONEVAL)) {
+ PHPDBG_G(flags) &= ~ PHPDBG_IS_STEPPING;
+ }
- CG(unclean_shutdown) = 0;
- } break;
+ /* disable stepping while eval() in progress */
+ PHPDBG_G(flags) |= PHPDBG_IN_EVAL;
+ zend_try {
+ if (zend_eval_stringl(param->str, param->len,
+ &retval, "eval()'d code" TSRMLS_CC) == SUCCESS) {
+ zend_print_zval_r(
+ &retval, 0 TSRMLS_CC);
+ phpdbg_writeln(EMPTY);
+ zval_dtor(&retval);
+ }
+ } zend_end_try();
+ PHPDBG_G(flags) &= ~PHPDBG_IN_EVAL;
- phpdbg_default_switch_case();
+ /* switch stepping back on */
+ if (stepping &&
+ !(PHPDBG_G(flags) & PHPDBG_IS_STEPONEVAL)) {
+ PHPDBG_G(flags) |= PHPDBG_IS_STEPPING;
}
+ CG(unclean_shutdown) = 0;
+
return SUCCESS;
} /* }}} */
@@ -661,14 +729,10 @@ PHPDBG_COMMAND(back) /* {{{ */
return SUCCESS;
}
- switch (param->type) {
- case EMPTY_PARAM:
- case NUMERIC_PARAM:
- phpdbg_dump_backtrace(
- (param->type == NUMERIC_PARAM) ? param->num : 0 TSRMLS_CC);
- break;
-
- phpdbg_default_switch_case();
+ if (!param) {
+ phpdbg_dump_backtrace(0 TSRMLS_CC);
+ } else {
+ phpdbg_dump_backtrace(param->num TSRMLS_CC);
}
return SUCCESS;
@@ -676,47 +740,41 @@ PHPDBG_COMMAND(back) /* {{{ */
PHPDBG_COMMAND(print) /* {{{ */
{
- switch (param->type) {
- case EMPTY_PARAM: {
- phpdbg_writeln(SEPARATE);
- phpdbg_notice("Execution Context Information");
+ phpdbg_writeln(SEPARATE);
+ phpdbg_notice("Execution Context Information");
#ifdef HAVE_LIBREADLINE
- phpdbg_writeln("Readline\tyes");
+ phpdbg_writeln("Readline\tyes");
#else
- phpdbg_writeln("Readline\tno");
+ phpdbg_writeln("Readline\tno");
#endif
- phpdbg_writeln("Exec\t\t%s", PHPDBG_G(exec) ? PHPDBG_G(exec) : "none");
- phpdbg_writeln("Compiled\t%s", PHPDBG_G(ops) ? "yes" : "no");
- phpdbg_writeln("Stepping\t%s", (PHPDBG_G(flags) & PHPDBG_IS_STEPPING) ? "on" : "off");
- phpdbg_writeln("Quietness\t%s", (PHPDBG_G(flags) & PHPDBG_IS_QUIET) ? "on" : "off");
- phpdbg_writeln("Oplog\t\t%s", PHPDBG_G(oplog) ? "on" : "off");
+ phpdbg_writeln("Exec\t\t%s", PHPDBG_G(exec) ? PHPDBG_G(exec) : "none");
+ phpdbg_writeln("Compiled\t%s", PHPDBG_G(ops) ? "yes" : "no");
+ phpdbg_writeln("Stepping\t%s", (PHPDBG_G(flags) & PHPDBG_IS_STEPPING) ? "on" : "off");
+ phpdbg_writeln("Quietness\t%s", (PHPDBG_G(flags) & PHPDBG_IS_QUIET) ? "on" : "off");
+ phpdbg_writeln("Oplog\t\t%s", PHPDBG_G(oplog) ? "on" : "off");
- if (PHPDBG_G(ops)) {
- phpdbg_writeln("Opcodes\t\t%d", PHPDBG_G(ops)->last);
-
- if (PHPDBG_G(ops)->last_var) {
- phpdbg_writeln("Variables\t%d", PHPDBG_G(ops)->last_var-1);
- } else {
- phpdbg_writeln("Variables\tNone");
- }
- }
+ if (PHPDBG_G(ops)) {
+ phpdbg_writeln("Opcodes\t\t%d", PHPDBG_G(ops)->last);
- phpdbg_writeln("Executing\t%s", EG(in_execution) ? "yes" : "no");
- if (EG(in_execution)) {
- phpdbg_writeln("VM Return\t%d", PHPDBG_G(vmret));
- }
+ if (PHPDBG_G(ops)->last_var) {
+ phpdbg_writeln("Variables\t%d", PHPDBG_G(ops)->last_var-1);
+ } else {
+ phpdbg_writeln("Variables\tNone");
+ }
+ }
- phpdbg_writeln("Classes\t\t%d", zend_hash_num_elements(EG(class_table)));
- phpdbg_writeln("Functions\t%d", zend_hash_num_elements(EG(function_table)));
- phpdbg_writeln("Constants\t%d", zend_hash_num_elements(EG(zend_constants)));
- phpdbg_writeln("Included\t%d", zend_hash_num_elements(&EG(included_files)));
+ phpdbg_writeln("Executing\t%s", EG(in_execution) ? "yes" : "no");
+ if (EG(in_execution)) {
+ phpdbg_writeln("VM Return\t%d", PHPDBG_G(vmret));
+ }
- phpdbg_writeln(SEPARATE);
- } break;
+ phpdbg_writeln("Classes\t\t%d", zend_hash_num_elements(EG(class_table)));
+ phpdbg_writeln("Functions\t%d", zend_hash_num_elements(EG(function_table)));
+ phpdbg_writeln("Constants\t%d", zend_hash_num_elements(EG(zend_constants)));
+ phpdbg_writeln("Included\t%d", zend_hash_num_elements(&EG(included_files)));
- phpdbg_default_switch_case();
- }
+ phpdbg_writeln(SEPARATE);
return SUCCESS;
} /* }}} */
@@ -732,19 +790,18 @@ PHPDBG_COMMAND(info) /* {{{ */
PHPDBG_COMMAND(set) /* {{{ */
{
phpdbg_error(
- "No information command selected!");
+ "No set command selected!");
return SUCCESS;
} /* }}} */
PHPDBG_COMMAND(break) /* {{{ */
{
- switch (param->type) {
- case EMPTY_PARAM:
- phpdbg_set_breakpoint_file(
+ if (!param) {
+ phpdbg_set_breakpoint_file(
zend_get_executed_filename(TSRMLS_C),
zend_get_executed_lineno(TSRMLS_C) TSRMLS_CC);
- break;
+ } else switch (param->type) {
case ADDR_PARAM:
phpdbg_set_breakpoint_opline(param->addr TSRMLS_CC);
break;
@@ -767,9 +824,18 @@ PHPDBG_COMMAND(break) /* {{{ */
case FILE_PARAM:
phpdbg_set_breakpoint_file(param->file.name, param->file.line TSRMLS_CC);
break;
+ case NUMERIC_FILE_PARAM:
+ phpdbg_set_breakpoint_file_opline(param->file.name, param->file.line TSRMLS_CC);
+ break;
+ case COND_PARAM:
+ phpdbg_set_breakpoint_expression(param->str, param->len TSRMLS_CC);
+ break;
case STR_PARAM:
phpdbg_set_breakpoint_symbol(param->str, param->len TSRMLS_CC);
break;
+ case OP_PARAM:
+ phpdbg_set_breakpoint_opcode(param->str, param->len TSRMLS_CC);
+ break;
phpdbg_default_switch_case();
}
@@ -777,83 +843,72 @@ PHPDBG_COMMAND(break) /* {{{ */
return SUCCESS;
} /* }}} */
-PHPDBG_COMMAND(shell) /* {{{ */
+PHPDBG_COMMAND(sh) /* {{{ */
{
- /* don't allow this to loop, ever ... */
- switch (param->type) {
- case STR_PARAM: {
- FILE *fd = NULL;
- if ((fd=VCWD_POPEN((char*)param->str, "w"))) {
- /* do something perhaps ?? do we want input ?? */
- fclose(fd);
- } else {
- phpdbg_error(
- "Failed to execute %s", param->str);
- }
- } break;
-
- phpdbg_default_switch_case();
+ FILE *fd = NULL;
+ if ((fd=VCWD_POPEN((char*)param->str, "w"))) {
+ /* do something perhaps ?? do we want input ?? */
+ fclose(fd);
+ } else {
+ phpdbg_error(
+ "Failed to execute %s", param->str);
}
+
return SUCCESS;
} /* }}} */
PHPDBG_COMMAND(source) /* {{{ */
{
- switch (param->type) {
- case STR_PARAM: {
- if (input->argc > 2) {
- if (phpdbg_argv_is(1, "export")) {
- FILE *h = VCWD_FOPEN(input->argv[2]->string, "w+");
- if (h) {
- phpdbg_export_breakpoints(h TSRMLS_CC);
- fclose(h);
- } else phpdbg_error("Failed to open %s", input->argv[1]->string);
- } else {
- phpdbg_error(
- "Incorrect usage of source command, see help");
- }
- } else {
- struct stat sb;
- if (VCWD_STAT(param->str, &sb) != -1) {
- phpdbg_try_file_init(param->str, param->len, 0 TSRMLS_CC);
- } else phpdbg_error("Cannot stat %s", param->str);
- }
- } break;
+ struct stat sb;
+
+ if (VCWD_STAT(param->str, &sb) != -1) {
+ phpdbg_try_file_init(param->str, param->len, 0 TSRMLS_CC);
+ } else {
+ phpdbg_error(
+ "Failed to stat %s, file does not exist", param->str);
+ }
+
+ return SUCCESS;
+} /* }}} */
- phpdbg_default_switch_case();
+PHPDBG_COMMAND(export) /* {{{ */
+{
+ FILE *handle = VCWD_FOPEN(param->str, "w+");
+
+ if (handle) {
+ phpdbg_export_breakpoints(handle TSRMLS_CC);
+ fclose(handle);
+ } else {
+ phpdbg_error(
+ "Failed to open or create %s, check path and permissions", param->str);
}
+
return SUCCESS;
} /* }}} */
PHPDBG_COMMAND(register) /* {{{ */
{
- switch (param->type) {
- case STR_PARAM: {
- zend_function *function;
- char *lcname = zend_str_tolower_dup(param->str, param->len);
- size_t lcname_len = strlen(lcname);
-
- if (!zend_hash_exists(&PHPDBG_G(registered), lcname, lcname_len+1)) {
- if (zend_hash_find(EG(function_table), lcname, lcname_len+1, (void**) &function) == SUCCESS) {
- zend_hash_update(
- &PHPDBG_G(registered), lcname, lcname_len+1, (void*)&function, sizeof(zend_function), NULL);
- function_add_ref(function);
-
- phpdbg_notice(
- "Registered %s", lcname);
- } else {
- phpdbg_error("The requested function (%s) could not be found", param->str);
- }
- } else {
- phpdbg_error(
- "The requested name (%s) is already in use", lcname);
- }
-
- efree(lcname);
- } break;
-
- phpdbg_default_switch_case();
+ zend_function *function;
+ char *lcname = zend_str_tolower_dup(param->str, param->len);
+ size_t lcname_len = strlen(lcname);
+
+ if (!zend_hash_exists(&PHPDBG_G(registered), lcname, lcname_len+1)) {
+ if (zend_hash_find(EG(function_table), lcname, lcname_len+1, (void**) &function) == SUCCESS) {
+ zend_hash_update(
+ &PHPDBG_G(registered), lcname, lcname_len+1, (void*)&function, sizeof(zend_function), NULL);
+ function_add_ref(function);
+
+ phpdbg_notice(
+ "Registered %s", lcname);
+ } else {
+ phpdbg_error("The requested function (%s) could not be found", param->str);
+ }
+ } else {
+ phpdbg_error(
+ "The requested name (%s) is already in use", lcname);
}
+
+ efree(lcname);
return SUCCESS;
} /* }}} */
@@ -861,14 +916,11 @@ PHPDBG_COMMAND(quit) /* {{{ */
{
/* don't allow this to loop, ever ... */
if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
-
- phpdbg_destroy_input((phpdbg_input_t**)&input TSRMLS_CC);
-
PHPDBG_G(flags) |= PHPDBG_IS_QUITTING;
zend_bailout();
}
- return SUCCESS;
+ return PHPDBG_NEXT;
} /* }}} */
PHPDBG_COMMAND(clean) /* {{{ */
@@ -908,100 +960,12 @@ PHPDBG_COMMAND(clear) /* {{{ */
return SUCCESS;
} /* }}} */
-PHPDBG_COMMAND(aliases) /* {{{ */
-{
- const phpdbg_command_t *prompt_command = phpdbg_prompt_commands;
-
- phpdbg_help_header();
- phpdbg_writeln("Below are the aliased, short versions of all supported commands");
- while (prompt_command && prompt_command->name) {
- if (prompt_command->alias) {
- if (prompt_command->subs) {
- const phpdbg_command_t *sub_command = prompt_command->subs;
- phpdbg_writeln(EMPTY);
- phpdbg_writeln(" %c -> %9s", prompt_command->alias, prompt_command->name);
- while (sub_command && sub_command->name) {
- if (sub_command->alias) {
- phpdbg_writeln(" |-------- %c -> %15s\t%s", sub_command->alias,
- sub_command->name, sub_command->tip);
- }
- ++sub_command;
- }
- phpdbg_writeln(EMPTY);
- } else {
- phpdbg_writeln(" %c -> %9s\t\t\t%s", prompt_command->alias,
- prompt_command->name, prompt_command->tip);
- }
- }
-
- ++prompt_command;
- }
- phpdbg_help_footer();
-
- return SUCCESS;
-} /* }}} */
-
-PHPDBG_COMMAND(help) /* {{{ */
-{
- switch (param->type) {
- case EMPTY_PARAM: {
- const phpdbg_command_t *prompt_command = phpdbg_prompt_commands;
- const phpdbg_command_t *help_command = phpdbg_help_commands;
-
- phpdbg_help_header();
- phpdbg_writeln("To get help regarding a specific command type \"help command\"");
-
- phpdbg_notice("Commands");
-
- while (prompt_command && prompt_command->name) {
- phpdbg_writeln(
- " %10s\t%s", prompt_command->name, prompt_command->tip);
- ++prompt_command;
- }
-
- phpdbg_notice("Help Commands");
-
- while (help_command && help_command->name) {
- phpdbg_writeln(" %10s\t%s", help_command->name, help_command->tip);
- ++help_command;
- }
-
- phpdbg_help_footer();
- } break;
-
- default: {
- phpdbg_error(
- "No help can be found for the subject \"%s\"", param->str);
- }
- }
-
- return SUCCESS;
-} /* }}} */
-
-PHPDBG_COMMAND(quiet) /* {{{ */
-{
- switch (param->type) {
- case NUMERIC_PARAM: {
- if (param->num) {
- PHPDBG_G(flags) |= PHPDBG_IS_QUIET;
- } else {
- PHPDBG_G(flags) &= ~PHPDBG_IS_QUIET;
- }
- phpdbg_notice("Quietness %s",
- (PHPDBG_G(flags) & PHPDBG_IS_QUIET) ? "enabled" : "disabled");
- } break;
-
- phpdbg_default_switch_case();
- }
-
- return SUCCESS;
-} /* }}} */
-
PHPDBG_COMMAND(list) /* {{{ */
{
- switch (param->type) {
+ if (!param) {
+ return PHPDBG_LIST_HANDLER(lines)(PHPDBG_COMMAND_ARGS);
+ } else switch (param->type) {
case NUMERIC_PARAM:
- case EMPTY_PARAM:
return PHPDBG_LIST_HANDLER(lines)(PHPDBG_COMMAND_ARGS);
case FILE_PARAM:
@@ -1020,54 +984,101 @@ PHPDBG_COMMAND(list) /* {{{ */
return SUCCESS;
} /* }}} */
+PHPDBG_COMMAND(watch) /* {{{ */
+{
+ if (!param || param->type == EMPTY_PARAM) {
+ phpdbg_list_watchpoints(TSRMLS_C);
+ } else switch (param->type) {
+ case STR_PARAM:
+ if (phpdbg_create_var_watchpoint(param->str, param->len TSRMLS_CC) != FAILURE) {
+ phpdbg_notice("Set watchpoint on %.*s", (int)param->len, param->str);
+ }
+ break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
+
int phpdbg_interactive(TSRMLS_D) /* {{{ */
{
int ret = SUCCESS;
- phpdbg_input_t *input;
+ char *why = NULL;
+ char *input = NULL;
+ phpdbg_param_t stack;
PHPDBG_G(flags) |= PHPDBG_IS_INTERACTIVE;
input = phpdbg_read_input(NULL TSRMLS_CC);
- if (input && input->length > 0L) {
+ if (input) {
do {
- switch (ret = phpdbg_do_cmd(phpdbg_prompt_commands, input TSRMLS_CC)) {
- case FAILURE:
- if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
- if (phpdbg_call_register(input TSRMLS_CC) == FAILURE) {
- phpdbg_error("Failed to execute %s!", input->string);
+ yyscan_t scanner;
+ YY_BUFFER_STATE state;
+
+ phpdbg_init_param(&stack, STACK_PARAM);
+
+ if (yylex_init(&scanner)) {
+ phpdbg_error(
+ "could not initialize scanner");
+ return FAILURE;
+ }
+
+ state = yy_scan_string(input, scanner);
+
+ if (yyparse(&stack, scanner) <= 0) {
+ switch (ret = phpdbg_stack_execute(&stack, &why TSRMLS_CC)) {
+ case FAILURE:
+ if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
+ if (phpdbg_call_register(&stack TSRMLS_CC) == FAILURE) {
+ if (why) {
+ phpdbg_error("%s", why);
+ }
+ }
}
- }
- break;
- case PHPDBG_LEAVE:
- case PHPDBG_FINISH:
- case PHPDBG_UNTIL:
- case PHPDBG_NEXT: {
- if (!EG(in_execution)) {
- phpdbg_error("Not running");
+ if (why) {
+ free(why);
+ why = NULL;
+ }
+ break;
+
+ case PHPDBG_LEAVE:
+ case PHPDBG_FINISH:
+ case PHPDBG_UNTIL:
+ case PHPDBG_NEXT: {
+ if (!EG(in_execution) && !(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
+ phpdbg_error("Not running");
+ }
+ goto out;
}
- goto out;
}
}
- phpdbg_destroy_input(&input TSRMLS_CC);
- } while ((input = phpdbg_read_input(NULL TSRMLS_CC)) && (input->length > 0L));
+ if (why) {
+ free(why);
+ why = NULL;
+ }
- if (input && !input->length)
- goto last;
+ yy_delete_buffer(state, scanner);
+ yylex_destroy(scanner);
- } else {
-last:
- if (PHPDBG_G(lcmd)) {
- ret = PHPDBG_G(lcmd)->handler(
- &PHPDBG_G(lparam), input TSRMLS_CC);
- goto out;
- }
+ phpdbg_stack_free(&stack);
+ phpdbg_destroy_input(&input TSRMLS_CC);
+
+ } while ((input = phpdbg_read_input(NULL TSRMLS_CC)));
}
out:
- phpdbg_destroy_input(&input TSRMLS_CC);
+ if (input) {
+ phpdbg_stack_free(&stack);
+ phpdbg_destroy_input(&input TSRMLS_CC);
+ }
+
+ if (why) {
+ free(why);
+ }
if (EG(in_execution)) {
phpdbg_restore_frame(TSRMLS_C);
@@ -1075,6 +1086,8 @@ out:
PHPDBG_G(flags) &= ~PHPDBG_IS_INTERACTIVE;
+ phpdbg_print_changed_zvals(TSRMLS_C);
+
return ret;
} /* }}} */
@@ -1214,7 +1227,7 @@ zend_vm_enter:
); \
} \
\
- do { \
+/* do { */\
switch (phpdbg_interactive(TSRMLS_C)) { \
case PHPDBG_LEAVE: \
case PHPDBG_FINISH: \
@@ -1223,7 +1236,7 @@ zend_vm_enter:
goto next; \
} \
} \
- } while (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)); \
+/* } while (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)); */\
} while (0)
/* allow conditional breakpoints and
@@ -1285,22 +1298,30 @@ zend_vm_enter:
phpdbg_print_opline_ex(
execute_data, &vars, 0 TSRMLS_CC);
+ if (PHPDBG_G(flags) & PHPDBG_IS_STEPPING && (PHPDBG_G(flags) & PHPDBG_STEP_OPCODE || execute_data->opline->lineno != PHPDBG_G(last_line))) {
+ PHPDBG_G(flags) &= ~PHPDBG_IS_STEPPING;
+ DO_INTERACTIVE();
+ }
+
+ /* check if some watchpoint was hit */
+ {
+ if (phpdbg_print_changed_zvals(TSRMLS_C) == SUCCESS) {
+ DO_INTERACTIVE();
+ }
+ }
+
/* search for breakpoints */
{
phpdbg_breakbase_t *brake;
- if ((PHPDBG_G(flags) & PHPDBG_BP_MASK) &&
- (brake = phpdbg_find_breakpoint(execute_data TSRMLS_CC))) {
- phpdbg_hit_breakpoint(
- brake, 1 TSRMLS_CC);
+ if ((PHPDBG_G(flags) & PHPDBG_BP_MASK)
+ && (brake = phpdbg_find_breakpoint(execute_data TSRMLS_CC))
+ && (brake->type != PHPDBG_BREAK_FILE || execute_data->opline->lineno != PHPDBG_G(last_line))) {
+ phpdbg_hit_breakpoint(brake, 1 TSRMLS_CC);
DO_INTERACTIVE();
}
}
- if (PHPDBG_G(flags) & PHPDBG_IS_STEPPING) {
- DO_INTERACTIVE();
- }
-
next:
if (PHPDBG_G(flags) & PHPDBG_IS_SIGNALED) {
phpdbg_writeln(EMPTY);
@@ -1309,6 +1330,8 @@ next:
DO_INTERACTIVE();
}
+ PHPDBG_G(last_line) = execute_data->opline->lineno;
+
PHPDBG_G(vmret) = execute_data->opline->handler(execute_data TSRMLS_CC);
if (PHPDBG_G(vmret) > 0) {
diff --git a/sapi/phpdbg/phpdbg_prompt.h b/sapi/phpdbg/phpdbg_prompt.h
index 6807d88f41..ef648aabeb 100644
--- a/sapi/phpdbg/phpdbg_prompt.h
+++ b/sapi/phpdbg/phpdbg_prompt.h
@@ -30,11 +30,10 @@ void phpdbg_clean(zend_bool full TSRMLS_DC); /* }}} */
/* {{{ phpdbg command handlers */
PHPDBG_COMMAND(exec);
-PHPDBG_COMMAND(compile);
PHPDBG_COMMAND(step);
-PHPDBG_COMMAND(next);
+PHPDBG_COMMAND(continue);
PHPDBG_COMMAND(run);
-PHPDBG_COMMAND(eval);
+PHPDBG_COMMAND(ev);
PHPDBG_COMMAND(until);
PHPDBG_COMMAND(finish);
PHPDBG_COMMAND(leave);
@@ -47,13 +46,13 @@ PHPDBG_COMMAND(info);
PHPDBG_COMMAND(clean);
PHPDBG_COMMAND(clear);
PHPDBG_COMMAND(help);
-PHPDBG_COMMAND(quiet);
-PHPDBG_COMMAND(aliases);
-PHPDBG_COMMAND(shell);
+PHPDBG_COMMAND(sh);
PHPDBG_COMMAND(set);
PHPDBG_COMMAND(source);
+PHPDBG_COMMAND(export);
PHPDBG_COMMAND(register);
-PHPDBG_COMMAND(quit); /* }}} */
+PHPDBG_COMMAND(quit);
+PHPDBG_COMMAND(watch); /* }}} */
/* {{{ prompt commands */
extern const phpdbg_command_t phpdbg_prompt_commands[]; /* }}} */
diff --git a/sapi/phpdbg/phpdbg_set.c b/sapi/phpdbg/phpdbg_set.c
index 7c4da12a46..54269a8193 100644
--- a/sapi/phpdbg/phpdbg_set.c
+++ b/sapi/phpdbg/phpdbg_set.c
@@ -23,56 +23,52 @@
#include "phpdbg_set.h"
#include "phpdbg_utils.h"
#include "phpdbg_bp.h"
+#include "phpdbg_prompt.h"
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
-PHPDBG_SET(prompt) /* {{{ */
-{
- switch (param->type) {
- case EMPTY_PARAM:
- phpdbg_writeln("%s", phpdbg_get_prompt(TSRMLS_C));
- break;
+#define PHPDBG_SET_COMMAND_D(f, h, a, m, l, s) \
+ PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[18])
- case STR_PARAM:
- phpdbg_set_prompt(param->str TSRMLS_CC);
- break;
-
- phpdbg_default_switch_case();
- }
+const phpdbg_command_t phpdbg_set_commands[] = {
+ PHPDBG_SET_COMMAND_D(prompt, "usage: set prompt [<string>]", 'p', set_prompt, NULL, "|s"),
+#ifndef _WIN32
+ PHPDBG_SET_COMMAND_D(color, "usage: set color <element> <color>", 'c', set_color, NULL, "ss"),
+ PHPDBG_SET_COMMAND_D(colors, "usage: set colors [<on|off>]", 'C', set_colors, NULL, "|b"),
+#endif
+ PHPDBG_SET_COMMAND_D(oplog, "usage: set oplog [<output>]", 'O', set_oplog, NULL, "|s"),
+ PHPDBG_SET_COMMAND_D(break, "usage: set break id [<on|off>]", 'b', set_break, NULL, "l|b"),
+ PHPDBG_SET_COMMAND_D(breaks, "usage: set breaks [<on|off>]", 'B', set_breaks, NULL, "|b"),
+ PHPDBG_SET_COMMAND_D(quiet, "usage: set quiet [<on|off>]", 'q', set_quiet, NULL, "|b"),
+ PHPDBG_SET_COMMAND_D(stepping, "usage: set stepping [<line|op>]", 's', set_stepping, NULL, "|s"),
+ PHPDBG_SET_COMMAND_D(refcount, "usage: set refcount [<on|off>]", 'r', set_refcount, NULL, "|b"),
+ PHPDBG_END_COMMAND
+};
+PHPDBG_SET(prompt) /* {{{ */
+{
+ if (!param || param->type == EMPTY_PARAM) {
+ phpdbg_writeln("%s", phpdbg_get_prompt(TSRMLS_C));
+ } else phpdbg_set_prompt(param->str TSRMLS_CC);
+
return SUCCESS;
} /* }}} */
PHPDBG_SET(break) /* {{{ */
{
switch (param->type) {
- case EMPTY_PARAM:
- phpdbg_writeln("%s",
- PHPDBG_G(flags) & PHPDBG_IS_BP_ENABLED ? "on" : "off");
- break;
-
- case STR_PARAM:
- if (strncasecmp(param->str, PHPDBG_STRL("on")) == 0) {
- phpdbg_enable_breakpoints(TSRMLS_C);
- } else if (strncasecmp(param->str, PHPDBG_STRL("off")) == 0) {
- phpdbg_disable_breakpoints(TSRMLS_C);
- }
- break;
-
case NUMERIC_PARAM: {
- if (input->argc > 2) {
- if (phpdbg_argv_is(2, "on")) {
- phpdbg_enable_breakpoint(param->num TSRMLS_CC);
- } else if (phpdbg_argv_is(2, "off")) {
- phpdbg_disable_breakpoint(param->num TSRMLS_CC);
- }
+ if (param->next) {
+ if (param->next->num) {
+ phpdbg_enable_breakpoint(param->num TSRMLS_CC);
+ } else phpdbg_disable_breakpoint(param->num TSRMLS_CC);
} else {
phpdbg_breakbase_t *brake = phpdbg_find_breakbase(param->num TSRMLS_CC);
if (brake) {
phpdbg_writeln(
"%s", brake->disabled ? "off" : "on");
} else {
- phpdbg_error("Failed to find breakpoint #%lx", param->num);
+ phpdbg_error("Failed to find breakpoint #%ld", param->num);
}
}
} break;
@@ -85,104 +81,96 @@ PHPDBG_SET(break) /* {{{ */
return SUCCESS;
} /* }}} */
-#ifndef _WIN32
-PHPDBG_SET(color) /* {{{ */
+PHPDBG_SET(breaks) /* {{{ */
{
- if ((param->type == STR_PARAM) && (input->argc == 3)) {
- const phpdbg_color_t *color = phpdbg_get_color(
- input->argv[2]->string, input->argv[2]->length TSRMLS_CC);
- int element = PHPDBG_COLOR_INVALID;
-
- /* @TODO(anyone) make this consistent with other set commands */
- if (color) {
- if (phpdbg_argv_is(1, "prompt")) {
- phpdbg_notice(
- "setting prompt color to %s (%s)", color->name, color->code);
- element = PHPDBG_COLOR_PROMPT;
- if (PHPDBG_G(prompt)[1]) {
- free(PHPDBG_G(prompt)[1]);
- PHPDBG_G(prompt)[1]=NULL;
- }
- } else if (phpdbg_argv_is(1, "error")) {
- phpdbg_notice(
- "setting error color to %s (%s)", color->name, color->code);
- element = PHPDBG_COLOR_ERROR;
+ if (!param || param->type == EMPTY_PARAM) {
+ phpdbg_writeln("%s",
+ PHPDBG_G(flags) & PHPDBG_IS_BP_ENABLED ? "on" : "off");
+ } else switch (param->type) {
+ case NUMERIC_PARAM: {
+ if (param->num) {
+ phpdbg_enable_breakpoints(TSRMLS_C);
+ } else phpdbg_disable_breakpoints(TSRMLS_C);
+ } break;
- } else if (phpdbg_argv_is(1, "notice")) {
- phpdbg_notice(
- "setting notice color to %s (%s)", color->name, color->code);
- element = PHPDBG_COLOR_NOTICE;
+ default:
+ phpdbg_error(
+ "set break used incorrectly: set break [id] <on|off>");
+ }
- } else goto usage;
+ return SUCCESS;
+} /* }}} */
- /* set color for element */
- phpdbg_set_color(element, color TSRMLS_CC);
- } else {
- phpdbg_error(
- "Failed to find the requested color (%s)", input->argv[2]->string);
- }
- } else {
-usage:
+#ifndef _WIN32
+PHPDBG_SET(color) /* {{{ */
+{
+ const phpdbg_color_t *color = phpdbg_get_color(
+ param->next->str, param->next->len TSRMLS_CC);
+
+ if (!color) {
phpdbg_error(
- "set color used incorrectly: set color <prompt|error|notice> <color>");
+ "Failed to find the requested color (%s)", param->next->str);
+ return SUCCESS;
}
+
+ switch (phpdbg_get_element(param->str, param->len TSRMLS_CC)) {
+ case PHPDBG_COLOR_PROMPT:
+ phpdbg_notice(
+ "setting prompt color to %s (%s)", color->name, color->code);
+ if (PHPDBG_G(prompt)[1]) {
+ free(PHPDBG_G(prompt)[1]);
+ PHPDBG_G(prompt)[1]=NULL;
+ }
+ phpdbg_set_color(PHPDBG_COLOR_PROMPT, color TSRMLS_CC);
+ break;
+
+ case PHPDBG_COLOR_ERROR:
+ phpdbg_notice(
+ "setting error color to %s (%s)", color->name, color->code);
+ phpdbg_set_color(PHPDBG_COLOR_ERROR, color TSRMLS_CC);
+ break;
+
+ case PHPDBG_COLOR_NOTICE:
+ phpdbg_notice(
+ "setting notice color to %s (%s)", color->name, color->code);
+ phpdbg_set_color(PHPDBG_COLOR_NOTICE, color TSRMLS_CC);
+ break;
+
+ default:
+ phpdbg_error(
+ "Failed to find the requested element (%s)", param->str);
+ }
+
return SUCCESS;
} /* }}} */
PHPDBG_SET(colors) /* {{{ */
{
- switch (param->type) {
- case EMPTY_PARAM: {
- phpdbg_writeln(
- "%s", PHPDBG_G(flags) & PHPDBG_IS_COLOURED ? "on" : "off");
- goto done;
- }
-
- case STR_PARAM: {
- if (strncasecmp(param->str, PHPDBG_STRL("on")) == 0) {
+ if (!param || param->type == EMPTY_PARAM) {
+ phpdbg_writeln("%s", PHPDBG_G(flags) & PHPDBG_IS_COLOURED ? "on" : "off");
+ } else switch (param->type) {
+ case NUMERIC_PARAM: {
+ if (param->num) {
PHPDBG_G(flags) |= PHPDBG_IS_COLOURED;
- goto done;
- } else if (strncasecmp(param->str, PHPDBG_STRL("off")) == 0) {
+ } else {
PHPDBG_G(flags) &= ~PHPDBG_IS_COLOURED;
- goto done;
}
- }
+ } break;
default:
phpdbg_error(
"set colors used incorrectly: set colors <on|off>");
}
-done:
return SUCCESS;
} /* }}} */
#endif
PHPDBG_SET(oplog) /* {{{ */
{
- switch (param->type) {
- case EMPTY_PARAM:
- phpdbg_notice(
- "Oplog %s", PHPDBG_G(oplog) ? "enabled" : "disabled");
- break;
-
- case NUMERIC_PARAM: switch (param->num) {
- case 1:
- phpdbg_error(
- "An output file must be provided to enable oplog");
- break;
-
- case 0: {
- if (PHPDBG_G(oplog)) {
- phpdbg_notice("Disabling oplog");
- fclose(
- PHPDBG_G(oplog));
- } else {
- phpdbg_error("Oplog is not enabled!");
- }
- } break;
- } break;
-
+ if (!param || param->type == EMPTY_PARAM) {
+ phpdbg_notice("Oplog %s", PHPDBG_G(oplog) ? "enabled" : "disabled");
+ } else switch (param->type) {
case STR_PARAM: {
/* open oplog */
FILE *old = PHPDBG_G(oplog);
@@ -206,3 +194,65 @@ PHPDBG_SET(oplog) /* {{{ */
return SUCCESS;
} /* }}} */
+PHPDBG_SET(quiet) /* {{{ */
+{
+ if (!param || param->type == EMPTY_PARAM) {
+ phpdbg_writeln("Quietness %s",
+ PHPDBG_G(flags) & PHPDBG_IS_QUIET ? "on" : "off");
+ } else switch (param->type) {
+ case NUMERIC_PARAM: {
+ if (param->num) {
+ PHPDBG_G(flags) |= PHPDBG_IS_QUIET;
+ } else {
+ PHPDBG_G(flags) &= ~PHPDBG_IS_QUIET;
+ }
+ } break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_SET(stepping) /* {{{ */
+{
+ if (!param || param->type == EMPTY_PARAM) {
+ phpdbg_writeln("Stepping %s",
+ PHPDBG_G(flags) & PHPDBG_STEP_OPCODE ? "opcode" : "line");
+ } else switch (param->type) {
+ case STR_PARAM: {
+ if ((param->len == sizeof("opcode")-1) &&
+ (memcmp(param->str, "opcode", sizeof("opcode")) == SUCCESS)) {
+ PHPDBG_G(flags) |= PHPDBG_STEP_OPCODE;
+ } else if ((param->len == sizeof("line")-1) &&
+ (memcmp(param->str, "line", sizeof("line")) == SUCCESS)) {
+ PHPDBG_G(flags) &= ~PHPDBG_STEP_OPCODE;
+ } else {
+ phpdbg_error("usage set stepping [<opcode|line>]");
+ }
+ } break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_SET(refcount) /* {{{ */
+{
+ if (!param || param->type == EMPTY_PARAM) {
+ phpdbg_writeln("Refcount %s", PHPDBG_G(flags) & PHPDBG_IS_QUIET ? "on" : "off");
+ } else switch (param->type) {
+ case NUMERIC_PARAM: {
+ if (param->num) {
+ PHPDBG_G(flags) |= PHPDBG_SHOW_REFCOUNTS;
+ } else {
+ PHPDBG_G(flags) &= ~PHPDBG_SHOW_REFCOUNTS;
+ }
+ } break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
diff --git a/sapi/phpdbg/phpdbg_set.h b/sapi/phpdbg/phpdbg_set.h
index 120aeb34f4..dea61ed382 100644
--- a/sapi/phpdbg/phpdbg_set.h
+++ b/sapi/phpdbg/phpdbg_set.h
@@ -32,16 +32,11 @@ PHPDBG_SET(colors);
#endif
PHPDBG_SET(oplog);
PHPDBG_SET(break);
+PHPDBG_SET(breaks);
+PHPDBG_SET(quiet);
+PHPDBG_SET(stepping);
+PHPDBG_SET(refcount);
-static const phpdbg_command_t phpdbg_set_commands[] = {
- PHPDBG_COMMAND_D_EX(prompt, "usage: set prompt <string>", 'p', set_prompt, NULL, 0),
-#ifndef _WIN32
- PHPDBG_COMMAND_D_EX(color, "usage: set color <element> <color>", 'c', set_color, NULL, 1),
- PHPDBG_COMMAND_D_EX(colors, "usage: set colors <on|off>", 'C', set_colors, NULL, 1),
-#endif
- PHPDBG_COMMAND_D_EX(oplog, "usage: set oplog <output>", 'O', set_oplog, NULL, 0),
- PHPDBG_COMMAND_D_EX(break, "usage: set break [id] <on|off>", 'b', set_break, NULL, 0),
- PHPDBG_END_COMMAND
-};
+extern const phpdbg_command_t phpdbg_set_commands[];
#endif /* PHPDBG_SET_H */
diff --git a/sapi/phpdbg/phpdbg_utils.c b/sapi/phpdbg/phpdbg_utils.c
index 1effcfccaf..c9b22a2039 100644
--- a/sapi/phpdbg/phpdbg_utils.c
+++ b/sapi/phpdbg/phpdbg_utils.c
@@ -30,6 +30,8 @@
#ifdef _WIN32
# include "win32/time.h"
+#elif defined(HAVE_SYS_IOCTL_H)
+# include "sys/ioctl.h"
#endif
ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
@@ -65,6 +67,14 @@ const static phpdbg_color_t colors[] = {
PHPDBG_COLOR_END
}; /* }}} */
+/* {{{ */
+const static phpdbg_element_t elements[] = {
+ PHPDBG_ELEMENT_D("prompt", PHPDBG_COLOR_PROMPT),
+ PHPDBG_ELEMENT_D("error", PHPDBG_COLOR_ERROR),
+ PHPDBG_ELEMENT_D("notice", PHPDBG_COLOR_NOTICE),
+ PHPDBG_ELEMENT_END
+}; /* }}} */
+
PHPDBG_API int phpdbg_is_numeric(const char *str) /* {{{ */
{
if (!str)
@@ -347,6 +357,21 @@ PHPDBG_API const phpdbg_color_t* phpdbg_get_colors(TSRMLS_D) /* {{{ */
return colors;
} /* }}} */
+PHPDBG_API int phpdbg_get_element(const char *name, size_t len TSRMLS_DC) {
+ const phpdbg_element_t *element = elements;
+
+ while (element && element->name) {
+ if (len == element->name_length) {
+ if (strncasecmp(name, element->name, len) == SUCCESS) {
+ return element->id;
+ }
+ }
+ element++;
+ }
+
+ return PHPDBG_COLOR_INVALID;
+}
+
PHPDBG_API void phpdbg_set_prompt(const char *prompt TSRMLS_DC) /* {{{ */
{
/* free formatted prompt */
@@ -385,3 +410,39 @@ PHPDBG_API const char *phpdbg_get_prompt(TSRMLS_D) /* {{{ */
return PHPDBG_G(prompt)[1];
} /* }}} */
+
+int phpdbg_rebuild_symtable(TSRMLS_D) {
+ if (!EG(active_op_array)) {
+ phpdbg_error("No active op array!");
+ return FAILURE;
+ }
+
+ if (!EG(active_symbol_table)) {
+ zend_rebuild_symbol_table(TSRMLS_C);
+
+ if (!EG(active_symbol_table)) {
+ phpdbg_error("No active symbol table!");
+ return FAILURE;
+ }
+ }
+
+ return SUCCESS;
+}
+
+PHPDBG_API int phpdbg_get_terminal_width(TSRMLS_D) /* {{{ */
+{
+ int columns;
+#ifdef _WIN32
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+
+ GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
+ columns = csbi.srWindow.Right - csbi.srWindow.Left + 1;
+#elif defined(HAVE_SYS_IOCTL_H)
+ struct winsize w;
+
+ columns = ioctl(fileno(stdout), TIOCGWINSZ, &w) == 0 ? w.ws_col : 100;
+#else
+ columns = 100;
+#endif
+ return columns;
+} /* }}} */
diff --git a/sapi/phpdbg/phpdbg_utils.h b/sapi/phpdbg/phpdbg_utils.h
index c5164c3ac3..68ae7e44a3 100644
--- a/sapi/phpdbg/phpdbg_utils.h
+++ b/sapi/phpdbg/phpdbg_utils.h
@@ -85,12 +85,17 @@ PHPDBG_API int phpdbg_rlog(FILE *stream, const char *fmt, ...);
{color, sizeof(color)-1, code}
#define PHPDBG_COLOR_END \
{NULL, 0L, {0}}
+#define PHPDBG_ELEMENT_LEN 3
+#define PHPDBG_ELEMENT_D(name, id) \
+ {name, sizeof(name)-1, id}
+#define PHPDBG_ELEMENT_END \
+ {NULL, 0L, 0}
#define PHPDBG_COLOR_INVALID -1
-#define PHPDBG_COLOR_PROMPT 0
-#define PHPDBG_COLOR_ERROR 1
-#define PHPDBG_COLOR_NOTICE 2
-#define PHPDBG_COLORS 3
+#define PHPDBG_COLOR_PROMPT 0
+#define PHPDBG_COLOR_ERROR 1
+#define PHPDBG_COLOR_NOTICE 2
+#define PHPDBG_COLORS 3
typedef struct _phpdbg_color_t {
char *name;
@@ -98,13 +103,25 @@ typedef struct _phpdbg_color_t {
const char code[PHPDBG_COLOR_LEN];
} phpdbg_color_t;
+typedef struct _phpdbg_element_t {
+ char *name;
+ size_t name_length;
+ int id;
+} phpdbg_element_t;
+
PHPDBG_API const phpdbg_color_t *phpdbg_get_color(const char *name, size_t name_length TSRMLS_DC);
PHPDBG_API void phpdbg_set_color(int element, const phpdbg_color_t *color TSRMLS_DC);
PHPDBG_API void phpdbg_set_color_ex(int element, const char *name, size_t name_length TSRMLS_DC);
-PHPDBG_API const phpdbg_color_t* phpdbg_get_colors(TSRMLS_D); /* }}} */
+PHPDBG_API const phpdbg_color_t* phpdbg_get_colors(TSRMLS_D);
+PHPDBG_API int phpdbg_get_element(const char *name, size_t len TSRMLS_DC); /* }}} */
/* {{{ Prompt Management */
PHPDBG_API void phpdbg_set_prompt(const char* TSRMLS_DC);
PHPDBG_API const char *phpdbg_get_prompt(TSRMLS_D); /* }}} */
+/* {{{ Console Width */
+PHPDBG_API int phpdbg_get_terminal_width(TSRMLS_D); /* }}} */
+
+int phpdbg_rebuild_symtable(TSRMLS_D);
+
#endif /* PHPDBG_UTILS_H */
diff --git a/sapi/phpdbg/phpdbg_watch.c b/sapi/phpdbg/phpdbg_watch.c
new file mode 100644
index 0000000000..2efbb1237c
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_watch.c
@@ -0,0 +1,789 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-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 |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "zend.h"
+#include "phpdbg.h"
+#include "phpdbg_btree.h"
+#include "phpdbg_watch.h"
+#include "phpdbg_utils.h"
+#ifndef _WIN32
+# include <unistd.h>
+# include <sys/mman.h>
+#endif
+
+ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
+
+
+typedef struct {
+ void *page;
+ size_t size;
+ char reenable_writing;
+ /* data must be last element */
+ void *data;
+} phpdbg_watch_memdump;
+
+#define MEMDUMP_SIZE(size) (sizeof(phpdbg_watch_memdump) - sizeof(void *) + (size))
+
+
+static phpdbg_watchpoint_t *phpdbg_check_for_watchpoint(void *addr TSRMLS_DC) {
+ phpdbg_watchpoint_t *watch;
+ phpdbg_btree_result *result = phpdbg_btree_find_closest(&PHPDBG_G(watchpoint_tree), (zend_ulong)phpdbg_get_page_boundary(addr) + phpdbg_pagesize - 1);
+
+ if (result == NULL) {
+ return NULL;
+ }
+
+ watch = result->ptr;
+
+ /* check if that addr is in a mprotect()'ed memory area */
+ if ((char *)phpdbg_get_page_boundary(watch->addr.ptr) > (char *)addr || (char *)phpdbg_get_page_boundary(watch->addr.ptr) + phpdbg_get_total_page_size(watch->addr.ptr, watch->size) < (char *)addr) {
+ /* failure */
+ return NULL;
+ }
+
+ return watch;
+}
+
+static void phpdbg_change_watchpoint_access(phpdbg_watchpoint_t *watch, int access TSRMLS_DC) {
+ int m;
+
+ /* pagesize is assumed to be in the range of 2^x */
+ m = mprotect(phpdbg_get_page_boundary(watch->addr.ptr), phpdbg_get_total_page_size(watch->addr.ptr, watch->size), access);
+}
+
+static inline void phpdbg_activate_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) {
+ phpdbg_change_watchpoint_access(watch, PROT_READ TSRMLS_CC);
+}
+
+static inline void phpdbg_deactivate_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) {
+ phpdbg_change_watchpoint_access(watch, PROT_READ | PROT_WRITE TSRMLS_CC);
+}
+
+static inline void phpdbg_store_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) {
+ phpdbg_btree_insert(&PHPDBG_G(watchpoint_tree), (zend_ulong)watch->addr.ptr, watch);
+}
+
+static inline void phpdbg_remove_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) {
+ phpdbg_btree_delete(&PHPDBG_G(watchpoint_tree), (zend_ulong)watch->addr.ptr);
+}
+
+void phpdbg_create_addr_watchpoint(void *addr, size_t size, phpdbg_watchpoint_t *watch) {
+ watch->addr.ptr = addr;
+ watch->size = size;
+}
+
+void phpdbg_create_zval_watchpoint(zval *zv, phpdbg_watchpoint_t *watch) {
+ phpdbg_create_addr_watchpoint(zv, sizeof(zval), watch);
+ watch->type = WATCH_ON_ZVAL;
+}
+
+void phpdbg_create_ht_watchpoint(HashTable *ht, phpdbg_watchpoint_t *watch) {
+ phpdbg_create_addr_watchpoint(ht, sizeof(HashTable), watch);
+ watch->type = WATCH_ON_HASHTABLE;
+}
+
+void phpdbg_watch_HashTable_dtor(zval **ptr);
+
+static int phpdbg_create_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) {
+ watch->flags |= PHPDBG_WATCH_SIMPLE;
+
+ phpdbg_store_watchpoint(watch TSRMLS_CC);
+ zend_hash_add(&PHPDBG_G(watchpoints), watch->str, watch->str_len, &watch, sizeof(phpdbg_watchpoint_t *), NULL);
+
+ if (watch->type == WATCH_ON_ZVAL) {
+ phpdbg_btree_insert(&PHPDBG_G(watch_HashTables), (zend_ulong)watch->parent_container, watch->parent_container->pDestructor);
+ watch->parent_container->pDestructor = (dtor_func_t)phpdbg_watch_HashTable_dtor;
+ }
+
+ phpdbg_activate_watchpoint(watch TSRMLS_CC);
+
+ return SUCCESS;
+}
+
+static int phpdbg_create_array_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) {
+ HashTable *ht;
+
+ switch (Z_TYPE_P(watch->addr.zv)) {
+ case IS_ARRAY:
+ ht = Z_ARRVAL_P(watch->addr.zv);
+ break;
+ case IS_OBJECT:
+ ht = Z_OBJPROP_P(watch->addr.zv);
+ break;
+ default:
+ return FAILURE;
+ }
+
+ phpdbg_create_ht_watchpoint(ht, watch);
+
+ phpdbg_create_watchpoint(watch TSRMLS_CC);
+
+ return SUCCESS;
+}
+
+static char *phpdbg_get_property_key(char *key) {
+ if (*key != 0) {
+ return key;
+ }
+ return strchr(key + 1, 0) + 1;
+}
+
+static int phpdbg_create_recursive_watchpoint(phpdbg_watchpoint_t *watch TSRMLS_DC) {
+ HashTable *ht;
+
+ if (watch->type != WATCH_ON_ZVAL) {
+ return FAILURE;
+ }
+
+ watch->flags |= PHPDBG_WATCH_RECURSIVE;
+ phpdbg_create_watchpoint(watch TSRMLS_CC);
+
+ switch (Z_TYPE_P(watch->addr.zv)) {
+ case IS_ARRAY:
+ ht = Z_ARRVAL_P(watch->addr.zv);
+ break;
+ case IS_OBJECT:
+ ht = Z_OBJPROP_P(watch->addr.zv);
+ break;
+ default:
+ return SUCCESS;
+ }
+
+ {
+ HashPosition position;
+ zval **zv;
+ zval key;
+
+ for (zend_hash_internal_pointer_reset_ex(ht, &position);
+ zend_hash_get_current_data_ex(ht, (void **)&zv, &position) == SUCCESS;
+ zend_hash_move_forward_ex(ht, &position)) {
+ phpdbg_watchpoint_t *new_watch = emalloc(sizeof(phpdbg_watchpoint_t));
+
+ new_watch->flags = PHPDBG_WATCH_RECURSIVE;
+ new_watch->parent = watch;
+ new_watch->parent_container = ht;
+
+ zend_hash_get_current_key_zval_ex(ht, &key, &position);
+ if (Z_TYPE(key) == IS_STRING) {
+ new_watch->name_in_parent = zend_strndup(Z_STRVAL(key), Z_STRLEN(key));
+ new_watch->name_in_parent_len = Z_STRLEN(key);
+ } else {
+ new_watch->name_in_parent = NULL;
+ new_watch->name_in_parent_len = asprintf(&new_watch->name_in_parent, "%ld", Z_LVAL(key));
+ }
+
+ new_watch->str = NULL;
+ new_watch->str_len = asprintf(&new_watch->str, "%.*s%s%s%s", (int)watch->str_len, watch->str, Z_TYPE_P(watch->addr.zv) == IS_ARRAY?"[":"->", phpdbg_get_property_key(new_watch->name_in_parent), Z_TYPE_P(watch->addr.zv) == IS_ARRAY?"]":"");
+
+ phpdbg_create_zval_watchpoint(*zv, new_watch);
+ phpdbg_create_recursive_watchpoint(new_watch TSRMLS_CC);
+ }
+ }
+
+ {
+ phpdbg_watchpoint_t *new_watch = emalloc(sizeof(phpdbg_watchpoint_t));
+
+ new_watch->parent = watch;
+ new_watch->parent_container = watch->parent_container;
+ new_watch->name_in_parent = zend_strndup(watch->name_in_parent, watch->name_in_parent_len);
+ new_watch->name_in_parent_len = watch->name_in_parent_len;
+ new_watch->str = NULL;
+ new_watch->str_len = asprintf(&new_watch->str, "%.*s[]", (int)watch->str_len, watch->str);
+ new_watch->flags = PHPDBG_WATCH_RECURSIVE;
+
+ phpdbg_create_ht_watchpoint(ht, new_watch);
+ phpdbg_create_watchpoint(new_watch TSRMLS_CC);
+ }
+
+ return SUCCESS;
+}
+
+static int phpdbg_delete_watchpoint_recursive(phpdbg_watchpoint_t *watch, zend_bool user_request TSRMLS_DC) {
+ if (watch->type == WATCH_ON_HASHTABLE || (watch->type == WATCH_ON_ZVAL && (Z_TYPE_P(watch->addr.zv) == IS_ARRAY || Z_TYPE_P(watch->addr.zv) == IS_OBJECT))) {
+ HashTable *ht;
+ phpdbg_btree_result *result;
+
+ if (watch->type == WATCH_ON_HASHTABLE && user_request) {
+ HashPosition position;
+ zval **zv;
+ zval key;
+ char *str;
+ int str_len;
+ phpdbg_watchpoint_t **watchpoint;
+
+ ht = watch->addr.ht;
+
+ for (zend_hash_internal_pointer_reset_ex(ht, &position);
+ zend_hash_get_current_data_ex(ht, (void **)&zv, &position) == SUCCESS;
+ zend_hash_move_forward_ex(ht, &position)) {
+ zend_hash_get_current_key_zval_ex(ht, &key, &position);
+ str = NULL;
+ if (Z_TYPE(key) == IS_STRING) {
+ str_len = asprintf(&str, "%.*s%s%s%s", (int)watch->parent->str_len, watch->parent->str, Z_TYPE_P(watch->parent->addr.zv) == IS_ARRAY?"[":"->", phpdbg_get_property_key(Z_STRVAL(key)), Z_TYPE_P(watch->parent->addr.zv) == IS_ARRAY?"]":"");
+ } else {
+ str_len = asprintf(&str, "%.*s%s%li%s", (int)watch->parent->str_len, watch->parent->str, Z_TYPE_P(watch->parent->addr.zv) == IS_ARRAY?"[":"->", Z_LVAL(key), Z_TYPE_P(watch->parent->addr.zv) == IS_ARRAY?"]":"");
+ }
+
+ if (zend_hash_find(&PHPDBG_G(watchpoints), str, str_len, (void **) &watchpoint) == SUCCESS) {
+ phpdbg_delete_watchpoint_recursive(*watchpoint, 1 TSRMLS_CC);
+ }
+ }
+ } else {
+ switch (Z_TYPE_P(watch->addr.zv)) {
+ case IS_ARRAY:
+ ht = Z_ARRVAL_P(watch->addr.zv);
+ break;
+ case IS_OBJECT:
+ ht = Z_OBJPROP_P(watch->addr.zv);
+ break;
+ }
+
+ if ((result = phpdbg_btree_find(&PHPDBG_G(watchpoint_tree), (zend_ulong) ht))) {
+ phpdbg_delete_watchpoint_recursive((phpdbg_watchpoint_t *) result->ptr, user_request TSRMLS_CC);
+ }
+ }
+ }
+
+ return zend_hash_del(&PHPDBG_G(watchpoints), watch->str, watch->str_len);
+}
+
+static int phpdbg_delete_watchpoint(phpdbg_watchpoint_t *tmp_watch TSRMLS_DC) {
+ int ret;
+ phpdbg_watchpoint_t *watch;
+ phpdbg_btree_result *result;
+
+ if ((result = phpdbg_btree_find(&PHPDBG_G(watchpoint_tree), (zend_ulong)tmp_watch->addr.ptr)) == NULL) {
+ return FAILURE;
+ }
+
+ watch = result->ptr;
+
+ if (watch->flags & PHPDBG_WATCH_RECURSIVE) {
+ ret = phpdbg_delete_watchpoint_recursive(watch, 1 TSRMLS_CC);
+ } else {
+ ret = zend_hash_del(&PHPDBG_G(watchpoints), watch->str, watch->str_len);
+ }
+
+ free(tmp_watch->str);
+ efree(tmp_watch);
+
+ return ret;
+}
+
+static int phpdbg_watchpoint_parse_input(char *input, size_t len, HashTable *parent, size_t i, int (*callback)(phpdbg_watchpoint_t * TSRMLS_DC), zend_bool silent TSRMLS_DC) {
+ int ret = FAILURE;
+ zend_bool new_index = 1;
+ char *last_index;
+ int index_len = 0;
+ zval **zv;
+
+ if (len < 2 || *input != '$') {
+ goto error;
+ }
+
+ while (i++ < len) {
+ if (i == len) {
+ new_index = 1;
+ } else {
+ switch (input[i]) {
+ case '[':
+ new_index = 1;
+ break;
+ case ']':
+ break;
+ case '>':
+ if (last_index[index_len - 1] == '-') {
+ new_index = 1;
+ index_len--;
+ }
+ break;
+
+ default:
+ if (new_index) {
+ last_index = input + i;
+ new_index = 0;
+ }
+ if (input[i - 1] == ']') {
+ goto error;
+ }
+ index_len++;
+ }
+ }
+
+ if (new_index && index_len == 0) {
+ HashPosition position;
+ for (zend_hash_internal_pointer_reset_ex(parent, &position);
+ zend_hash_get_current_data_ex(parent, (void **)&zv, &position) == SUCCESS;
+ zend_hash_move_forward_ex(parent, &position)) {
+ if (i == len || (i == len - 1 && input[len - 1] == ']')) {
+ zval *key = emalloc(sizeof(zval));
+ phpdbg_watchpoint_t *watch = emalloc(sizeof(phpdbg_watchpoint_t));
+ watch->flags = 0;
+ zend_hash_get_current_key_zval_ex(parent, key, &position);
+ convert_to_string(key);
+ watch->str = malloc(i + Z_STRLEN_P(key) + 2);
+ watch->str_len = sprintf(watch->str, "%.*s%s%s", (int)i, input, phpdbg_get_property_key(Z_STRVAL_P(key)), input[len - 1] == ']'?"]":"");
+ efree(key);
+ watch->name_in_parent = zend_strndup(last_index, index_len);
+ watch->name_in_parent_len = index_len;
+ watch->parent_container = parent;
+ phpdbg_create_zval_watchpoint(*zv, watch);
+
+ ret = callback(watch TSRMLS_CC) == SUCCESS || ret == SUCCESS?SUCCESS:FAILURE;
+ } else if (Z_TYPE_PP(zv) == IS_OBJECT) {
+ phpdbg_watchpoint_parse_input(input, len, Z_OBJPROP_PP(zv), i, callback, silent TSRMLS_CC);
+ } else if (Z_TYPE_PP(zv) == IS_ARRAY) {
+ phpdbg_watchpoint_parse_input(input, len, Z_ARRVAL_PP(zv), i, callback, silent TSRMLS_CC);
+ } else {
+ /* Ignore silently */
+ }
+ }
+ return ret;
+ } else if (new_index) {
+ char last_chr = last_index[index_len];
+ last_index[index_len] = 0;
+ if (zend_symtable_find(parent, last_index, index_len + 1, (void **)&zv) == FAILURE) {
+ if (!silent) {
+ phpdbg_error("%.*s is undefined", (int)i, input);
+ }
+ return FAILURE;
+ }
+ last_index[index_len] = last_chr;
+ if (i == len) {
+ phpdbg_watchpoint_t *watch = emalloc(sizeof(phpdbg_watchpoint_t));
+ watch->flags = 0;
+ watch->str = zend_strndup(input, len);
+ watch->str_len = len;
+ watch->name_in_parent = zend_strndup(last_index, index_len);
+ watch->name_in_parent_len = index_len;
+ watch->parent_container = parent;
+ phpdbg_create_zval_watchpoint(*zv, watch);
+
+ ret = callback(watch TSRMLS_CC) == SUCCESS || ret == SUCCESS?SUCCESS:FAILURE;
+ } else if (Z_TYPE_PP(zv) == IS_OBJECT) {
+ parent = Z_OBJPROP_PP(zv);
+ } else if (Z_TYPE_PP(zv) == IS_ARRAY) {
+ parent = Z_ARRVAL_PP(zv);
+ } else {
+ phpdbg_error("%.*s is nor an array nor an object", (int)i, input);
+ return FAILURE;
+ }
+ index_len = 0;
+ }
+ }
+
+ return ret;
+ error:
+ phpdbg_error("Malformed input");
+ return FAILURE;
+}
+
+static int phpdbg_watchpoint_parse_symtables(char *input, size_t len, int (*callback)(phpdbg_watchpoint_t * TSRMLS_DC) TSRMLS_DC) {
+ if (EG(This) && len >= 5 && !memcmp("$this", input, 5)) {
+ zend_hash_add(EG(active_symbol_table), "this", sizeof("this"), &EG(This), sizeof(zval *), NULL);
+ }
+
+ if (zend_is_auto_global(input, len TSRMLS_CC) && phpdbg_watchpoint_parse_input(input, len, &EG(symbol_table), 0, callback, 1 TSRMLS_CC) != FAILURE) {
+ return SUCCESS;
+ }
+
+ return phpdbg_watchpoint_parse_input(input, len, EG(active_symbol_table), 0, callback, 0 TSRMLS_CC);
+}
+
+PHPDBG_WATCH(delete) /* {{{ */
+{
+ switch (param->type) {
+ case STR_PARAM:
+ if (phpdbg_delete_var_watchpoint(param->str, param->len TSRMLS_CC) == FAILURE) {
+ phpdbg_error("Nothing was deleted, no corresponding watchpoint found");
+ } else {
+ phpdbg_notice("Removed watchpoint %.*s", (int)param->len, param->str);
+ }
+ break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_WATCH(recursive) /* {{{ */
+{
+ if (phpdbg_rebuild_symtable(TSRMLS_C) == FAILURE) {
+ return SUCCESS;
+ }
+
+ switch (param->type) {
+ case STR_PARAM:
+ if (phpdbg_watchpoint_parse_symtables(param->str, param->len, phpdbg_create_recursive_watchpoint TSRMLS_CC) != FAILURE) {
+ phpdbg_notice("Set recursive watchpoint on %.*s", (int)param->len, param->str);
+ }
+ break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+PHPDBG_WATCH(array) /* {{{ */
+{
+ if (phpdbg_rebuild_symtable(TSRMLS_C) == FAILURE) {
+ return SUCCESS;
+ }
+
+ switch (param->type) {
+ case STR_PARAM:
+ if (phpdbg_watchpoint_parse_symtables(param->str, param->len, phpdbg_create_array_watchpoint TSRMLS_CC) != FAILURE) {
+ phpdbg_notice("Set array watchpoint on %.*s", (int)param->len, param->str);
+ }
+ break;
+
+ phpdbg_default_switch_case();
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+void phpdbg_watch_HashTable_dtor(zval **zv) {
+ phpdbg_btree_result *result;
+ TSRMLS_FETCH();
+
+ zval_ptr_dtor_wrapper(zv);
+
+ if ((result = phpdbg_btree_find(&PHPDBG_G(watchpoint_tree), (zend_ulong)*zv))) {
+ phpdbg_watchpoint_t *watch = result->ptr;
+
+ PHPDBG_G(watchpoint_hit) = 1;
+
+ phpdbg_notice("%.*s was removed, removing watchpoint%s", (int)watch->str_len, watch->str, (watch->flags & PHPDBG_WATCH_RECURSIVE)?" recursively":"");
+
+ if (watch->flags & PHPDBG_WATCH_RECURSIVE) {
+ phpdbg_delete_watchpoint_recursive(watch, 0 TSRMLS_CC);
+ } else {
+ zend_hash_del(&PHPDBG_G(watchpoints), watch->str, watch->str_len);
+ }
+ }
+}
+
+
+int phpdbg_create_var_watchpoint(char *input, size_t len TSRMLS_DC) {
+ if (phpdbg_rebuild_symtable(TSRMLS_C) == FAILURE) {
+ return FAILURE;
+ }
+
+ return phpdbg_watchpoint_parse_symtables(input, len, phpdbg_create_watchpoint TSRMLS_CC);
+}
+
+int phpdbg_delete_var_watchpoint(char *input, size_t len TSRMLS_DC) {
+ if (phpdbg_rebuild_symtable(TSRMLS_C) == FAILURE) {
+ return FAILURE;
+ }
+
+ return phpdbg_watchpoint_parse_symtables(input, len, phpdbg_delete_watchpoint TSRMLS_CC);
+}
+
+#ifdef _WIN32
+int phpdbg_watchpoint_segfault_handler(void *addr TSRMLS_DC) {
+#else
+int phpdbg_watchpoint_segfault_handler(siginfo_t *info, void *context TSRMLS_DC) {
+#endif
+ void *page;
+ phpdbg_watch_memdump *dump;
+ phpdbg_watchpoint_t *watch;
+ size_t size;
+
+ watch = phpdbg_check_for_watchpoint(
+#ifdef _WIN32
+ addr
+#else
+ info->si_addr
+#endif
+ TSRMLS_CC);
+
+ if (watch == NULL) {
+ return FAILURE;
+ }
+
+ page = phpdbg_get_page_boundary(watch->addr.ptr);
+ size = phpdbg_get_total_page_size(watch->addr.ptr, watch->size);
+
+ /* re-enable writing */
+ mprotect(page, size, PROT_READ | PROT_WRITE);
+
+ dump = malloc(MEMDUMP_SIZE(size));
+ dump->page = page;
+ dump->size = size;
+
+ memcpy(&dump->data, page, size);
+
+ zend_llist_add_element(&PHPDBG_G(watchlist_mem), &dump);
+
+ return SUCCESS;
+}
+
+void phpdbg_watchpoints_clean(TSRMLS_D) {
+ zend_hash_clean(&PHPDBG_G(watchpoints));
+}
+
+static void phpdbg_watch_dtor(void *pDest) {
+ phpdbg_watchpoint_t *watch = *(phpdbg_watchpoint_t **)pDest;
+ TSRMLS_FETCH();
+
+ phpdbg_deactivate_watchpoint(watch TSRMLS_CC);
+ phpdbg_remove_watchpoint(watch TSRMLS_CC);
+
+ free(watch->str);
+ free(watch->name_in_parent);
+ efree(watch);
+}
+
+static void phpdbg_watch_mem_dtor(void *llist_data) {
+ phpdbg_watch_memdump *dump = *(phpdbg_watch_memdump **)llist_data;
+
+ /* Disble writing again */
+ if (dump->reenable_writing) {
+ mprotect(dump->page, dump->size, PROT_READ);
+ }
+
+ free(*(void **)llist_data);
+}
+
+void phpdbg_setup_watchpoints(TSRMLS_D) {
+#if _SC_PAGE_SIZE
+ phpdbg_pagesize = sysconf(_SC_PAGE_SIZE);
+#elif _SC_PAGESIZE
+ phpdbg_pagesize = sysconf(_SC_PAGESIZE);
+#elif _SC_NUTC_OS_PAGESIZE
+ phpdbg_pagesize = sysconf(_SC_NUTC_OS_PAGESIZE);
+#else
+ phpdbg_pagesize = 4096; /* common pagesize */
+#endif
+
+ zend_llist_init(&PHPDBG_G(watchlist_mem), sizeof(void *), phpdbg_watch_mem_dtor, 1);
+ phpdbg_btree_init(&PHPDBG_G(watchpoint_tree), sizeof(void *) * 8);
+ phpdbg_btree_init(&PHPDBG_G(watch_HashTables), sizeof(void *) * 8);
+ _zend_hash_init(&PHPDBG_G(watchpoints), 8, phpdbg_watch_dtor, 0 ZEND_FILE_LINE_CC);
+}
+
+static void phpdbg_print_changed_zval(phpdbg_watch_memdump *dump TSRMLS_DC) {
+ /* fetch all changes between dump->page and dump->page + dump->size */
+ phpdbg_btree_position pos = phpdbg_btree_find_between(&PHPDBG_G(watchpoint_tree), (zend_ulong)dump->page, (zend_ulong)dump->page + dump->size);
+ phpdbg_btree_result *result, *htresult;
+ int elementDiff;
+ void *curTest;
+
+ dump->reenable_writing = 0;
+
+ while ((result = phpdbg_btree_next(&pos))) {
+ phpdbg_watchpoint_t *watch = result->ptr, *htwatch;
+ void *oldPtr = (char *)&dump->data + ((size_t)watch->addr.ptr - (size_t)dump->page);
+ char reenable = 1;
+
+ if ((size_t)watch->addr.ptr < (size_t)dump->page || (size_t)watch->addr.ptr + watch->size > (size_t)dump->page + dump->size) {
+ continue;
+ }
+
+ /* Test if the zval was separated and if necessary move the watchpoint */
+ if (zend_hash_find(watch->parent_container, watch->name_in_parent, watch->name_in_parent_len + 1, &curTest) == SUCCESS) {
+ if (watch->type == WATCH_ON_HASHTABLE) {
+ switch (Z_TYPE_PP((zval **)curTest)) {
+ case IS_ARRAY:
+ curTest = (void *)Z_ARRVAL_PP((zval **)curTest);
+ break;
+ case IS_OBJECT:
+ curTest = (void *)Z_OBJPROP_PP((zval **)curTest);
+ break;
+ }
+ } else {
+ curTest = *(void **)curTest;
+ }
+
+ if (curTest != watch->addr.ptr) {
+ phpdbg_deactivate_watchpoint(watch TSRMLS_CC);
+ phpdbg_remove_watchpoint(watch TSRMLS_CC);
+ watch->addr.ptr = curTest;
+ phpdbg_store_watchpoint(watch TSRMLS_CC);
+ phpdbg_activate_watchpoint(watch TSRMLS_CC);
+
+ reenable = 0;
+ }
+ }
+
+ /* Show to the user what changed and delete watchpoint upon removal */
+ if (memcmp(oldPtr, watch->addr.ptr, watch->size) != SUCCESS) {
+ if (PHPDBG_G(flags) & PHPDBG_SHOW_REFCOUNTS || (watch->type == WATCH_ON_ZVAL && memcmp(oldPtr, watch->addr.zv, sizeof(zvalue_value))) || (watch->type == WATCH_ON_HASHTABLE
+#if ZEND_DEBUG
+ && !watch->addr.ht->inconsistent
+#endif
+ && zend_hash_num_elements((HashTable *)oldPtr) != zend_hash_num_elements(watch->addr.ht))) {
+ PHPDBG_G(watchpoint_hit) = 1;
+
+ phpdbg_notice("Breaking on watchpoint %s", watch->str);
+ }
+
+ switch (watch->type) {
+ case WATCH_ON_ZVAL: {
+ int removed = ((zval *)oldPtr)->refcount__gc != watch->addr.zv->refcount__gc && !zend_symtable_exists(watch->parent_container, watch->name_in_parent, watch->name_in_parent_len + 1);
+ int show_value = memcmp(oldPtr, watch->addr.zv, sizeof(zvalue_value));
+ int show_ref = ((zval *)oldPtr)->refcount__gc != watch->addr.zv->refcount__gc || ((zval *)oldPtr)->is_ref__gc != watch->addr.zv->is_ref__gc;
+
+ if (removed || show_value) {
+ phpdbg_write("Old value: ");
+ if ((Z_TYPE_P((zval *)oldPtr) == IS_ARRAY || Z_TYPE_P((zval *)oldPtr) == IS_OBJECT) && removed) {
+ phpdbg_writeln("Value inaccessible, HashTable already destroyed");
+ } else {
+ zend_print_flat_zval_r((zval *)oldPtr TSRMLS_CC);
+ phpdbg_writeln("");
+ }
+ }
+ if (PHPDBG_G(flags) & PHPDBG_SHOW_REFCOUNTS && (removed || show_ref)) {
+ phpdbg_writeln("Old refcount: %d; Old is_ref: %d", ((zval *)oldPtr)->refcount__gc, ((zval *)oldPtr)->is_ref__gc);
+ }
+
+ /* check if zval was removed */
+ if (removed) {
+ phpdbg_notice("Watchpoint %s was unset, removing watchpoint", watch->str);
+ zend_hash_del(&PHPDBG_G(watchpoints), watch->str, watch->str_len);
+
+ reenable = 0;
+
+ if (Z_TYPE_P((zval *)oldPtr) == IS_ARRAY || Z_TYPE_P((zval *)oldPtr) == IS_OBJECT) {
+ goto remove_ht_watch;
+ }
+
+ break;
+ }
+
+ if (show_value) {
+ phpdbg_write("New value: ");
+ zend_print_flat_zval_r(watch->addr.zv TSRMLS_CC);
+ phpdbg_writeln("");
+ }
+ if (PHPDBG_G(flags) & PHPDBG_SHOW_REFCOUNTS && show_ref) {
+ phpdbg_writeln("New refcount: %d; New is_ref: %d", watch->addr.zv->refcount__gc, watch->addr.zv->is_ref__gc);
+ }
+
+ if ((Z_TYPE_P(watch->addr.zv) == IS_ARRAY && Z_ARRVAL_P(watch->addr.zv) != Z_ARRVAL_P((zval *)oldPtr)) || (Z_TYPE_P(watch->addr.zv) != IS_OBJECT && Z_OBJ_HANDLE_P(watch->addr.zv) == Z_OBJ_HANDLE_P((zval *)oldPtr))) {
+ /* add new watchpoints if necessary */
+ if (watch->flags & PHPDBG_WATCH_RECURSIVE) {
+ phpdbg_create_recursive_watchpoint(watch TSRMLS_CC);
+ }
+ }
+
+ if ((Z_TYPE_P((zval *)oldPtr) != IS_ARRAY || Z_ARRVAL_P(watch->addr.zv) == Z_ARRVAL_P((zval *)oldPtr)) && (Z_TYPE_P((zval *)oldPtr) != IS_OBJECT || Z_OBJ_HANDLE_P(watch->addr.zv) == Z_OBJ_HANDLE_P((zval *)oldPtr))) {
+ break;
+ }
+
+remove_ht_watch:
+ if ((htresult = phpdbg_btree_find(&PHPDBG_G(watchpoint_tree), (zend_ulong)Z_ARRVAL_P((zval *)oldPtr)))) {
+ htwatch = htresult->ptr;
+ zend_hash_del(&PHPDBG_G(watchpoints), htwatch->str, htwatch->str_len);
+ }
+
+ break;
+ }
+ case WATCH_ON_HASHTABLE:
+
+#if ZEND_DEBUG
+ if (watch->addr.ht->inconsistent) {
+ phpdbg_notice("Watchpoint %s was unset, removing watchpoint", watch->str);
+ zend_hash_del(&PHPDBG_G(watchpoints), watch->str, watch->str_len);
+
+ reenable = 0;
+
+ break;
+ }
+#endif
+
+ elementDiff = zend_hash_num_elements((HashTable *)oldPtr) - zend_hash_num_elements(watch->addr.ht);
+ if (elementDiff) {
+ if (elementDiff > 0) {
+ phpdbg_writeln("%d elements were removed from the array", elementDiff);
+ } else {
+ phpdbg_writeln("%d elements were added to the array", -elementDiff);
+
+ /* add new watchpoints if necessary */
+ if (watch->flags & PHPDBG_WATCH_RECURSIVE) {
+ phpdbg_create_recursive_watchpoint(watch TSRMLS_CC);
+ }
+ }
+ }
+ if (((HashTable *)oldPtr)->pInternalPointer != watch->addr.ht->pInternalPointer) {
+ phpdbg_writeln("Internal pointer of array was changed");
+ }
+ break;
+ }
+ }
+
+ dump->reenable_writing = dump->reenable_writing | reenable;
+ }
+}
+
+int phpdbg_print_changed_zvals(TSRMLS_D) {
+ zend_llist_position pos;
+ phpdbg_watch_memdump **dump;
+ int ret;
+
+ if (zend_llist_count(&PHPDBG_G(watchlist_mem)) == 0) {
+ return FAILURE;
+ }
+
+ dump = (phpdbg_watch_memdump **)zend_llist_get_last_ex(&PHPDBG_G(watchlist_mem), &pos);
+
+ do {
+ phpdbg_print_changed_zval(*dump TSRMLS_CC);
+ } while ((dump = (phpdbg_watch_memdump **)zend_llist_get_prev_ex(&PHPDBG_G(watchlist_mem), &pos)));
+
+ zend_llist_clean(&PHPDBG_G(watchlist_mem));
+
+ ret = PHPDBG_G(watchpoint_hit)?SUCCESS:FAILURE;
+ PHPDBG_G(watchpoint_hit) = 0;
+
+ return ret;
+}
+
+void phpdbg_list_watchpoints(TSRMLS_D) {
+ HashPosition position;
+ phpdbg_watchpoint_t **watch;
+
+ for (zend_hash_internal_pointer_reset_ex(&PHPDBG_G(watchpoints), &position);
+ zend_hash_get_current_data_ex(&PHPDBG_G(watchpoints), (void**) &watch, &position) == SUCCESS;
+ zend_hash_move_forward_ex(&PHPDBG_G(watchpoints), &position)) {
+ phpdbg_writeln("%.*s", (int)(*watch)->str_len, (*watch)->str);
+ }
+}
+
+void phpdbg_watch_efree(void *ptr) {
+ phpdbg_btree_result *result;
+ TSRMLS_FETCH();
+
+ result = phpdbg_btree_find_closest(&PHPDBG_G(watchpoint_tree), (zend_ulong)ptr);
+
+ if (result) {
+ phpdbg_watchpoint_t *watch = result->ptr;
+
+ if ((size_t)watch->addr.ptr + watch->size > (size_t)ptr) {
+ zend_hash_del(&PHPDBG_G(watchpoints), watch->str, watch->str_len);
+ }
+ }
+
+ PHPDBG_G(original_free_function)(ptr);
+}
diff --git a/sapi/phpdbg/phpdbg_watch.h b/sapi/phpdbg/phpdbg_watch.h
new file mode 100644
index 0000000000..d00bcff77e
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_watch.h
@@ -0,0 +1,112 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-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 |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHPDBG_WATCH_H
+#define PHPDBG_WATCH_H
+
+#include "TSRM.h"
+#include "phpdbg_cmd.h"
+
+#ifdef _WIN32
+# include "phpdbg_win.h"
+#endif
+
+#define PHPDBG_WATCH(name) PHPDBG_COMMAND(watch_##name)
+
+/**
+ * Printer Forward Declarations
+ */
+PHPDBG_WATCH(array);
+PHPDBG_WATCH(delete);
+PHPDBG_WATCH(recursive);
+
+/**
+ * Commands
+ */
+
+static const phpdbg_command_t phpdbg_watch_commands[] = {
+ PHPDBG_COMMAND_D_EX(array, "create watchpoint on an array", 'a', watch_array, NULL, "s"),
+ PHPDBG_COMMAND_D_EX(delete, "delete watchpoint", 'd', watch_delete, NULL, "s"),
+ PHPDBG_COMMAND_D_EX(recursive, "create recursive watchpoints", 'r', watch_recursive, NULL, "s"),
+ PHPDBG_END_COMMAND
+};
+
+/* Watchpoint functions/typedefs */
+
+typedef enum {
+ WATCH_ON_ZVAL,
+ WATCH_ON_HASHTABLE,
+} phpdbg_watchtype;
+
+
+#define PHPDBG_WATCH_SIMPLE 0x0
+#define PHPDBG_WATCH_RECURSIVE 0x1
+
+typedef struct _phpdbg_watchpoint_t phpdbg_watchpoint_t;
+
+struct _phpdbg_watchpoint_t {
+ phpdbg_watchpoint_t *parent;
+ HashTable *parent_container;
+ char *name_in_parent;
+ size_t name_in_parent_len;
+ char *str;
+ size_t str_len;
+ union {
+ zval *zv;
+ HashTable *ht;
+ void *ptr;
+ } addr;
+ size_t size;
+ phpdbg_watchtype type;
+ char flags;
+};
+
+void phpdbg_setup_watchpoints(TSRMLS_D);
+
+#ifndef _WIN32
+int phpdbg_watchpoint_segfault_handler(siginfo_t *info, void *context TSRMLS_DC);
+#else
+int phpdbg_watchpoint_segfault_handler(void *addr TSRMLS_DC);
+#endif
+
+void phpdbg_create_addr_watchpoint(void *addr, size_t size, phpdbg_watchpoint_t *watch);
+void phpdbg_create_zval_watchpoint(zval *zv, phpdbg_watchpoint_t *watch);
+
+int phpdbg_delete_var_watchpoint(char *input, size_t len TSRMLS_DC);
+int phpdbg_create_var_watchpoint(char *input, size_t len TSRMLS_DC);
+
+int phpdbg_print_changed_zvals(TSRMLS_D);
+
+void phpdbg_list_watchpoints(TSRMLS_D);
+
+void phpdbg_watch_efree(void *ptr);
+
+
+static long phpdbg_pagesize;
+
+static zend_always_inline void *phpdbg_get_page_boundary(void *addr) {
+ return (void *)((size_t)addr & ~(phpdbg_pagesize - 1));
+}
+
+static zend_always_inline size_t phpdbg_get_total_page_size(void *addr, size_t size) {
+ return (size_t)phpdbg_get_page_boundary((void *)((size_t)addr + size - 1)) - (size_t)phpdbg_get_page_boundary(addr) + phpdbg_pagesize;
+}
+
+#endif
diff --git a/sapi/phpdbg/phpdbg_win.c b/sapi/phpdbg/phpdbg_win.c
new file mode 100644
index 0000000000..b0cbdf267a
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_win.c
@@ -0,0 +1,42 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-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 |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "zend.h"
+#include "phpdbg.h"
+
+int mprotect(void *addr, size_t size, int protection) {
+ int var;
+ return (int)VirtualProtect(addr, size, protection == (PROT_READ | PROT_WRITE) ? PAGE_READWRITE : PAGE_READONLY, &var);
+}
+
+int phpdbg_exception_handler_win32(EXCEPTION_POINTERS *xp) {
+ EXCEPTION_RECORD *xr = xp->ExceptionRecord;
+ CONTEXT *xc = xp->ContextRecord;
+
+ if(xr->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) {
+ TSRMLS_FETCH();
+
+ if (phpdbg_watchpoint_segfault_handler((void *)xr->ExceptionInformation[1] TSRMLS_CC) == SUCCESS) {
+ return EXCEPTION_CONTINUE_EXECUTION;
+ }
+ }
+
+ return EXCEPTION_CONTINUE_SEARCH;
+}
diff --git a/sapi/phpdbg/phpdbg_win.h b/sapi/phpdbg/phpdbg_win.h
new file mode 100644
index 0000000000..68c3052790
--- /dev/null
+++ b/sapi/phpdbg/phpdbg_win.h
@@ -0,0 +1,37 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-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 |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Felipe Pena <felipe@php.net> |
+ | Authors: Joe Watkins <joe.watkins@live.co.uk> |
+ | Authors: Bob Weinand <bwoebi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHPDBG_WIN_H
+#define PHPDBG_WIN_H
+
+#include "winbase.h"
+#include "windows.h"
+#include "excpt.h"
+
+#define PROT_READ 1
+#define PROT_WRITE 2
+
+int mprotect(void *addr, size_t size, int protection);
+
+void phpdbg_win_set_mm_heap(zend_mm_heap *heap);
+
+int phpdbg_exception_handler_win32(EXCEPTION_POINTERS *xp);
+
+#endif \ No newline at end of file
diff --git a/sapi/phpdbg/test.php b/sapi/phpdbg/test.php
index 5fdbcbe1a4..d93c81a89a 100644
--- a/sapi/phpdbg/test.php
+++ b/sapi/phpdbg/test.php
@@ -6,11 +6,16 @@ if (isset($include)) {
$stdout = fopen("php://stdout", "w+");
class phpdbg {
- public function isGreat($greeting = null) {
- printf(
- "%s: %s\n", __METHOD__, $greeting);
- return $this;
- }
+ private $sprintf = "%s: %s\n";
+
+ public function isGreat($greeting = null) {
+ printf($this->sprintf, __METHOD__, $greeting);
+ return $this;
+ }
+}
+
+function mine() {
+ var_dump(func_get_args());
}
function test($x, $y = 0) {
@@ -49,3 +54,34 @@ function phpdbg_test_ob()
echo 'End';
echo $b;
}
+
+$array = [
+ 1,
+ 2,
+ [3, 4],
+ [5, 6],
+];
+
+$array[] = 7;
+
+array_walk($array, function (&$item) {
+ if (is_array($item))
+ $item[0] += 2;
+ else
+ $item -= 1;
+});
+
+class testClass {
+ public $a = 2;
+ protected $b = [1, 3];
+ private $c = 7;
+}
+
+$obj = new testClass;
+
+$test = $obj->a;
+
+$obj->a += 2;
+$test -= 2;
+
+unset($obj);
diff --git a/tests/classes/constants_error_002.phpt b/tests/classes/constants_error_002.phpt
index be27971b87..610a42da9b 100644
--- a/tests/classes/constants_error_002.phpt
+++ b/tests/classes/constants_error_002.phpt
@@ -2,11 +2,11 @@
Error case: class constant as an array
--FILE--
<?php
- class myclass
- {
- const myConst = array();
- }
+class myclass
+{
+ const myConst = array();
+}
?>
+===DONE===
--EXPECTF--
-
-Fatal error: Arrays are not allowed in class constants in %s on line 4
+===DONE===
diff --git a/travis/compile.sh b/travis/compile.sh
index 2b53099651..8b54b714ad 100755
--- a/travis/compile.sh
+++ b/travis/compile.sh
@@ -1,6 +1,18 @@
#!/bin/bash
+if [[ "$ENABLE_MAINTAINER_ZTS" == 1 ]]; then
+ TS="--enable-maintainer-zts";
+else
+ TS="";
+fi
+if [[ "$ENABLE_DEBUG" == 1 ]]; then
+ DEBUG="--enable-debug";
+else
+ DEBUG="";
+fi
./buildconf
./configure --quiet \
+$DEBUG \
+$TS \
--with-pdo-mysql=mysqlnd \
--with-mysql=mysqlnd \
--with-mysqli=mysqlnd \
@@ -34,5 +46,6 @@
--enable-sockets \
--with-bz2 \
--with-openssl \
+--with-gmp \
--enable-bcmath
make --quiet
diff --git a/win32/build/libs_version.txt b/win32/build/libs_version.txt
index 71af7a1a0d..a22823d0a9 100644
--- a/win32/build/libs_version.txt
+++ b/win32/build/libs_version.txt
@@ -1,7 +1,7 @@
bz2-1.0.6
cclient-2007f
freetype-2.5.3
-icu-52.1
+icu-53.1
jpeglib-9a
libcurl-7.36.0
libiconv-1.14