diff --git a/NEWS b/NEWS index 844b536239..d4311c61cc 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,50 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? 2014, PHP 5.6.0 Beta 2 +- Core: + . Fixed bug #64604 (parse_url is inconsistent with specified port). + (Ingo Walz) + . 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) + +- cURL: + . 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) + +- FPM: + . Fixed bug #66482 (unknown entry 'priority' in php-fpm.conf). + +- JSON: + . Fixed bug #66021 (Blank line inside empty array/object when + JSON_PRETTY_PRINT is set). (Kevin Israel) + +- mysqlnd: + . Added a new fetching mode to mysqlnd. (Andrey) + +- PDO: + . Fixed bug #66604 ('pdo/php_pdo_error.h' not copied to the include dir). + (Matteo) + +- 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: diff --git a/UPGRADING b/UPGRADING index ae73503b2f..a46bb6ecf4 100755 --- a/UPGRADING +++ b/UPGRADING @@ -20,6 +20,21 @@ PHP 5.6 UPGRADE NOTES 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 diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 474aed55f7..1bc359bfd0 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -9,6 +9,9 @@ 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 2. Build system changes a. Unix build system changes @@ -120,8 +123,71 @@ 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. ======================== 2. Build system changes 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-- + '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-- + +--FILE-- +"); +echo php_strip_whitespace(__DIR__."/bug66660.tmp.php"); +?> +--CLEAN-- + +--EXPECT-- + 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/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-- --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 096b80eaf7..1b7a7f00b4 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -589,14 +589,11 @@ typedef int (*zend_write_func_t)(const char *str, zend_size_t 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 56e2d4be22..9a6b9b7bd9 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -1077,7 +1077,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; } @@ -1086,7 +1086,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; } @@ -1100,7 +1100,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]) { @@ -3470,7 +3470,6 @@ ZEND_API int zend_declare_property_ex(zend_class_entry *ce, const char *name, ze 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 7c6f9eaeb6..b8a6d9630f 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 5686c21417..ef88eb32e0 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_IVAL_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_uint_t 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_STRSIZE(offset->u.constant)+3); - Z_STRVAL(offset->u.constant)[Z_STRSIZE(offset->u.constant)+1] = Z_TYPE(offset->u.constant); - Z_STRVAL(offset->u.constant)[Z_STRSIZE(offset->u.constant)+2] = 0; - zend_symtable_update(Z_ARRVAL(result->u.constant), Z_STRVAL(offset->u.constant), Z_STRSIZE(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_STRSIZE(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_STRSIZE_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_INT: case IS_BOOL: - zend_hash_index_update(Z_ARRVAL(result->u.constant), Z_IVAL(offset->u.constant), &element, sizeof(zval *), NULL); + zend_hash_index_update(Z_ARRVAL_P(result), Z_IVAL_P(offset), &expr, sizeof(zval *), NULL); break; case IS_DOUBLE: - zend_hash_index_update(Z_ARRVAL(result->u.constant), zend_dval_to_ival(Z_DVAL(offset->u.constant)), &element, sizeof(zval *), NULL); + zend_hash_index_update(Z_ARRVAL_P(result), zend_dval_to_ival(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 a5cdf217a3..c130acc35a 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -602,7 +602,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 d72e324ccc..181ef49afe 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, zend_size_t name_len, zval * 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, zend_size_t name_len, zval * 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 5eed0573d2..447f91c853 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 08af23947d..0b81399f7f 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, zend_size_t ZEND_API zend_class_entry *zend_fetch_class_by_name(const char *class_name, zend_size_t 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 08b9bb2dc2..e471dcc333 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; - zend_size_t str_index_len; - zend_uint_t 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_STRSIZE(const_value) + 1, HASH_UPDATE_KEY_IF_BEFORE); - break; - case IS_BOOL: - case IS_INT: - ret = zend_hash_update_current_key_ex(Z_ARRVAL_P(p), HASH_KEY_IS_INT, NULL, 0, Z_IVAL(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_INT, NULL, 0, zend_dval_to_ival(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 02051b3dbd..f41cd49194 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 d72f73e4f1..8887bac3cd 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_STRSIZE($2.u.constant)+1); memcpy(&(tmp[1]), Z_STRVAL($2.u.constant), Z_STRSIZE($2.u.constant)+1); tmp[0] = '\\'; efree(Z_STRVAL($2.u.constant)); Z_STRVAL($2.u.constant) = tmp; ++Z_STRSIZE($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 cf02340713..2d2be545da 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -170,6 +170,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 a5880221a5..9c602fa03c 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 ddc31b8416..cf453d64f4 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 1032fe73ae..ab2fb9dd8b 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_variant.c b/ext/com_dotnet/com_variant.c index 14578c1b84..357fd3cc2f 100644 --- a/ext/com_dotnet/com_variant.c +++ b/ext/com_dotnet/com_variant.c @@ -160,7 +160,7 @@ PHP_COM_DOTNET_API void php_com_variant_from_zval(VARIANT *v, zval *z, php_int_t 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/curl/multi.c b/ext/curl/multi.c index 794b3b18ad..15202800d6 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/date/php_date.c b/ext/date/php_date.c index 68aa60ebf8..02e5a87c90 100644 --- a/ext/date/php_date.c +++ b/ext/date/php_date.c @@ -2801,6 +2801,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 +--FILE-- + +--EXPECTF-- +Fatal error: Invalid serialization data for DateTime object in %s on line %d 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 32dfd55e08..9f47059e63 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 fa47189b36..e5f0c61782 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 d33cf47ede..41003eb2ae 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 a4a72fdb54..df64885a23 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 52047ce8b0..d1c6d17088 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 7d0f60648e..85a5d40a52 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 86d396f4f3..6809dc6795 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 @@ - += 0) die('skip for ICU < 53.1'); ?> --FILE-- 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-- + += 53.1'); ?> +--FILE-- +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-- -= 49 only'); + += 49'); ?> += 0) die('skip for ICU < 53.1'); ?> --FILE-- += 53.1'); ?> +--FILE-- += 4.8 +create() icu >= 4.8 && icu < 53.1 --SKIPIF-- - += 4.8'; ?> += 0) die('skip for ICU < 53.1'); ?> --FILE-- = 53.1 +--SKIPIF-- + += 53.1'); ?> +--FILE-- + +--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-- = 51.2'); ?> += 0) die('skip for ICU < 53.1'); ?> --FILE-- += 53.1'); ?> +--FILE-- + +--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-- - += 52.1'; ?> += 0) die('skip for ICU < 53.1'); ?> --FILE-- = 53.1 +--SKIPIF-- + += 53.1'); ?> +--FILE-- + '##.#####################', + 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-- = 52.1'); ?> += 0) die('skip for ICU < 53.1'); ?> --FILE-- = 53.1 +--SKIPIF-- + += 53.1'); ?> +--FILE-- + +--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-- - += 0) die('skip for ICU < 53.1'); ?> --FILE-- += 53.1'); ?> +--FILE-- + +==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 2794bf9344..4d434e0174 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, php_int_t options TS static void json_encode_array(smart_str *buf, zval **val, php_int_t 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, php_int_t options TSRM 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, php_int_t options TSRM php_size_t 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, php_int_t options TSRM 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, php_int_t options TSRM 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, php_int_t options TSRM } 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, php_int_t options TSRM 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-- + +--FILE-- + +--EXPECT-- +[ + [], + {}, + {} +] diff --git a/ext/mysql/php_mysql.c b/ext/mysql/php_mysql.c index 97a5815568..90269c4bdc 100644 --- a/ext/mysql/php_mysql.c +++ b/ext/mysql/php_mysql.c @@ -878,7 +878,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) { @@ -887,7 +887,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 */ @@ -935,7 +935,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"); @@ -997,7 +997,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"); @@ -1014,7 +1014,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 9e1ba3a691..a475e78468 100644 --- a/ext/mysqli/mysqli.c +++ b/ext/mysqli/mysqli.c @@ -721,6 +721,7 @@ PHP_MINIT_FUNCTION(mysqli) REGISTER_INT_CONSTANT("MYSQLI_USE_RESULT", MYSQLI_USE_RESULT, CONST_CS | CONST_PERSISTENT); #if defined (MYSQLI_USE_MYSQLND) REGISTER_INT_CONSTANT("MYSQLI_ASYNC", MYSQLI_ASYNC, CONST_CS | CONST_PERSISTENT); + REGISTER_INT_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 d7e63e5ecf..83287cd88b 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); @@ -2617,7 +2617,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) { @@ -2625,13 +2625,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 11a9833d40..679a2ce249 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 257679c87e..e5fcc0986c 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-- 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'); 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,65 +32,73 @@ 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 (!$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("[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"); + 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("[015] Expecting true, got %s/%s\n", gettype($tmp), $tmp); + + if (!$res = mysqli_query($link, 'SELECT database() AS dbname, user() AS user')) + printf("[016] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); + $tmp = mysqli_fetch_assoc($res); + mysqli_free_result($res); + + if ($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("[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')) @@ -98,13 +106,17 @@ require_once('skipifconnectfailure.inc'); $tmp = mysqli_fetch_assoc($res); mysqli_free_result($res); - if ($tmp['dbname'] != "") - printf("[025] Expecting database '', got database() '%s'\n", $tmp['dbname']); + 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']); - mysqli_close($link); - - if (NULL !== ($tmp = @mysqli_change_user($link, $user, $passwd, $db))) - printf("[026] Expecting NULL, got %s/%s\n", gettype($tmp), $tmp); + 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-- += 5.6.0"); +?> +--FILE-- +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..370bb344da --- /dev/null +++ b/ext/mysqli/tests/mysqli_change_user_old.phpt @@ -0,0 +1,39 @@ +--TEST-- +mysqli_change_user(), MySQL < 5.6 +--SKIPIF-- += 50600) + die("SKIP For MySQL < 5.6.0"); +?> +--FILE-- +fetch_assoc()); + + 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-- 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'); --EXPECTF-- -[010 + 6] Rejecting thread %d: 0/ -[011 + 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/ -[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-- + +--FILE-- +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-- +errno, $link->error)); +?> +--FILE-- +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-- + +--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-- += 5.6.0"); + +?> +--FILE-- + +--CLEAN-- + +--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-- = 50600) + die("SKIP For MySQL < 5.6.0"); ?> --FILE-- errno, $link->error)); +?> +--FILE-- + +--CLEAN-- + +--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..d580ec430f --- /dev/null +++ b/ext/mysqli/tests/mysqli_store_result_buffered_c.phpt @@ -0,0 +1,42 @@ +--TEST-- +mysqli_store_result() +--SKIPIF-- + +--INI-- +mysqlnd.debug=d:t:O,/tmp/mysqlnd.trace +--FILE-- + +--CLEAN-- + +--EXPECTF-- +Warning: mysqli_store_result(): Couldn't fetch mysqli in %s on line %d +done! \ No newline at end of file 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-- + +--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-- +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-- + +--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 65b39415b3..625d1112b4 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, php_size_t db_len, php_uint_t port, const char * socket_or_pipe, - php_uint_t mysql_flags + php_uint_t mysql_flags, + php_uint_t 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 a3b56c44c6..042bd1dc26 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, php_size_t passwd_len, const char * db, php_size_t db_len, php_uint_t port, const char * socket_or_pipe, - php_uint_t mysql_flags + php_uint_t mysql_flags, + php_uint_t 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) php_int_t debug_calloc_fail_threshold; php_int_t 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 61583f5fd3..550f7f0ed4 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 (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 *))); } - /* 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); - } - 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, php_uint_t f { 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; php_uint_t field_count = meta->field_count; @@ -742,81 +744,86 @@ mysqlnd_stmt_fetch_row_buffered(MYSQLND_RES * result, void * param, php_uint_t f 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) { - php_uint_t i; - zval **current_row = set->data_cursor; + 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) { + php_uint_t 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) { - php_uint_t len = Z_STRSIZE_P(current_row[i]); - if (meta->fields[i].max_length < len) { - meta->fields[i].max_length = len; + 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) { + php_size_t len = Z_STRSIZE_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. - */ + /* 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; + 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 78f40105c2..98cba21101 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) { php_uint_t i; - zval **data_cursor = result->data; - zval **data_begin = result->data; - php_uint_t 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) < (ptrdiff_t)(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_STRSIZE_P(current_row[i]); + if (meta->fields[i].max_length < len) { + meta->fields[i].max_length = len; + } + } + zval_ptr_dtor(¤t_row[i]); + } + } + mnd_efree(current_row); + } + DBG_RETURN(ret); +} +/* }}} */ + + /* {{{ mysqlnd_rset_zval_ptr_dtor */ static void mysqlnd_rset_zval_ptr_dtor(zval **zv, enum_mysqlnd_res_type type, zend_bool * copy_ctor_called TSRMLS_DC) @@ -205,25 +262,23 @@ MYSQLND_METHOD(mysqlnd_result_unbuffered, free_result)(MYSQLND_RES_UNBUFFERED * /* }}} */ -/* {{{ mysqlnd_result_buffered::free_result */ +/* {{{ mysqlnd_result_buffered_zval::free_result */ static void -MYSQLND_METHOD(mysqlnd_result_buffered, free_result)(MYSQLND_RES_BUFFERED * const set TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_result_buffered_zval, free_result)(MYSQLND_RES_BUFFERED_ZVAL * const set TSRMLS_DC) { - php_uint_t 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) { php_uint_t copy_on_write_performed = 0; php_uint_t copy_on_write_saved = 0; - zval **data = set->data; - set->data = NULL; /* prevent double free if following loop is interrupted */ - + php_uint_t 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 php_uint_t * -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) { - php_uint_t 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_STRSIZE_P(previous_row[i]); + +/* {{{ 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 */ } - - return result->lengths; + 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, php_uint_t flags, zend_bool * fetched_anything TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_result_unbuffered, fetch_row)(MYSQLND_RES * result, void * param, const php_uint_t flags, zend_bool * fetched_anything TSRMLS_DC) { enum_func_status ret; zval *row = (zval *) param; @@ -944,74 +1051,86 @@ static enum_func_status MYSQLND_METHOD(mysqlnd_result_buffered, fetch_row_c)(MYSQLND_RES * result, void * param, php_uint_t 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; php_uint_t field_count = meta->field_count; enum_func_status ret = FAIL; - DBG_ENTER("mysqlnd_result_buffered::fetch_row_c"); - /* 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; - php_uint_t i; + if (result->stored_data->type == MYSQLND_BUFFERED_TYPE_ZVAL) { + MYSQLND_RES_BUFFERED_ZVAL * set = (MYSQLND_RES_BUFFERED_ZVAL *) result->stored_data; - 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) { - php_uint_t len = Z_STRSIZE_P(current_row[i]); - if (field->max_length < len) { - field->max_length = len; + /* 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_STRSIZE_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 (ret) { - for (i = 0; i < field_count; i++) { - zval * data = current_row[i]; + /* 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]; - if (Z_TYPE_P(data) != IS_NULL) { - convert_to_string(data); - (*row)[i] = Z_STRVAL_P(data); - } else { - (*row)[i] = NULL; + set->lengths[i] = (Z_TYPE_P(data) == IS_NULL)? 0:Z_STRSIZE_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 */ - set->data_cursor += field_count; - 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"); + *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; - ret = PASS; + DBG_RETURN(FAIL); } DBG_INF_FMT("ret=PASS fetched=%u", *fetched_anything); DBG_RETURN(ret); @@ -1019,25 +1138,24 @@ MYSQLND_METHOD(mysqlnd_result_buffered, fetch_row_c)(MYSQLND_RES * result, void /* }}} */ -/* {{{ mysqlnd_result_buffered::fetch_row */ +/* {{{ mysqlnd_result_buffered_zval::fetch_row */ static enum_func_status -MYSQLND_METHOD(mysqlnd_result_buffered, fetch_row)(MYSQLND_RES * result, void * param, php_uint_t flags, zend_bool * fetched_anything TSRMLS_DC) +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; - MYSQLND_RES_BUFFERED * set = result->stored_data; const MYSQLND_RES_METADATA * const meta = result->meta; php_uint_t 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::fetch_row"); + 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; php_uint_t i; + zval **current_row = set->data_cursor; if (NULL == current_row[0]) { uint64_t row_num = (set->data_cursor - set->data) / field_count; @@ -1058,17 +1176,19 @@ MYSQLND_METHOD(mysqlnd_result_buffered, fetch_row)(MYSQLND_RES * result, void * Thus for NULL and zero-length we are quite efficient. */ if (Z_TYPE_P(current_row[i]) >= IS_STRING) { - php_uint_t len = Z_STRSIZE_P(current_row[i]); - if (field->max_length < len) { - field->max_length = len; + php_size_t len = Z_STRSIZE_P(current_row[i]); + 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_STRSIZE_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,12 +1204,12 @@ 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 { - zend_hash_index_update(Z_ARRVAL_P(row), + zend_hash_index_update(Z_ARRVAL_P(row), meta->zend_hash_keys[i].key, (void *) &data, sizeof(zval *), NULL); } @@ -1111,9 +1231,121 @@ MYSQLND_METHOD(mysqlnd_result_buffered, fetch_row)(MYSQLND_RES * result, void * /* }}} */ +/* {{{ mysqlnd_result_buffered_c::fetch_row */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_result_buffered_c, fetch_row)(MYSQLND_RES * result, void * param, const php_uint_t flags, zend_bool * fetched_anything TSRMLS_DC) +{ + zval * row = (zval *) param; + const MYSQLND_RES_METADATA * const meta = result->meta; + php_uint_t field_count = meta->field_count; + enum_func_status ret = FAIL; + + 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->current_row < set->row_count) { + zval **current_row; + enum_func_status rc; + php_uint_t i; + + 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 + 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) { + php_size_t len = Z_STRSIZE_P(current_row[i]); + if (meta->fields[i].max_length < len) { + meta->fields[i].max_length = len; + } + } + } + } + + for (i = 0; i < field_count; i++) { + zval * data = current_row[i]; + + set->lengths[i] = (Z_TYPE_P(data) == IS_NULL)? 0:Z_STRSIZE_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 { + zend_hash_index_update(Z_ARRVAL_P(row), + meta->zend_hash_keys[i].key, + (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); + } + mnd_efree(current_row); + set->current_row++; + MYSQLND_INC_GLOBAL_STATISTIC(STAT_ROWS_FETCHED_FROM_CLIENT_NORMAL_BUF); + *fetched_anything = TRUE; + ret = PASS; + } else { + 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); +} +/* }}} */ + + /* {{{ mysqlnd_res::fetch_row */ static enum_func_status -MYSQLND_METHOD(mysqlnd_res, fetch_row)(MYSQLND_RES * result, void * param, php_uint_t flags, zend_bool *fetched_anything TSRMLS_DC) +MYSQLND_METHOD(mysqlnd_res, fetch_row)(MYSQLND_RES * result, void * param, const php_uint_t 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, php_u 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 (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 *))); } - /* 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 *))); + /* 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, php_uint_t flags, +MYSQLND_METHOD(mysqlnd_res, fetch_into)(MYSQLND_RES * result, const php_uint_t 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, php_uint_t flags, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC) +MYSQLND_METHOD(mysqlnd_res, fetch_all)(MYSQLND_RES * result, const php_uint_t flags, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC) { zval *row; php_uint_t 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(php_uint_t field_count, zend_bool ps, zend_bool p 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,14 @@ mysqlnd_result_unbuffered_init(php_uint_t field_count, zend_bool ps, zend_bool p /* }}} */ -/* {{{ mysqlnd_result_buffered_init */ -PHPAPI MYSQLND_RES_BUFFERED * -mysqlnd_result_buffered_init(php_uint_t 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(php_uint_t 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_ZVAL) + mysqlnd_plugin_count() * sizeof(void *); + MYSQLND_RES_BUFFERED_ZVAL * ret = mnd_pecalloc(1, alloc_size, persistent); - DBG_ENTER("mysqlnd_result_buffered_init"); + DBG_ENTER("mysqlnd_result_buffered_zval_init"); if (!ret) { DBG_RETURN(NULL); @@ -1812,13 +2080,61 @@ mysqlnd_result_buffered_init(php_uint_t field_count, zend_bool ps, zend_bool per 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; + 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(php_uint_t field_count, zend_bool ps, zend_bool persistent TSRMLS_DC) +{ + 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_c_init"); + + if (!ret) { + DBG_RETURN(NULL); + } + if (!(ret->lengths = mnd_pecalloc(field_count, sizeof(php_uint_t), 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_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_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 09e0f81c9f..9c536e56ac 100644 --- a/ext/mysqlnd/mysqlnd_result.h +++ b/ext/mysqlnd/mysqlnd_result.h @@ -25,7 +25,8 @@ PHPAPI MYSQLND_RES * mysqlnd_result_init(php_uint_t field_count, zend_bool persistent TSRMLS_DC); PHPAPI MYSQLND_RES_UNBUFFERED * mysqlnd_result_unbuffered_init(php_uint_t field_count, zend_bool ps, zend_bool persistent TSRMLS_DC); -PHPAPI MYSQLND_RES_BUFFERED * mysqlnd_result_buffered_init(php_uint_t field_count, zend_bool ps, zend_bool persistent TSRMLS_DC); +PHPAPI MYSQLND_RES_BUFFERED_ZVAL * mysqlnd_result_buffered_zval_init(php_uint_t field_count, zend_bool ps, zend_bool persistent TSRMLS_DC); +PHPAPI MYSQLND_RES_BUFFERED_C * mysqlnd_result_buffered_c_init(php_uint_t 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 3b653e5e26..a21e71de49 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, - php_uint_t flags, + const php_uint_t 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 php_uint_t flags TSRMLS_DC); -typedef void (*func_mysqlnd_res__fetch_into)(MYSQLND_RES *result, php_uint_t 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 php_uint_t 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, php_uint_t flags, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC); +typedef void (*func_mysqlnd_res__fetch_all)(MYSQLND_RES *result, const php_uint_t flags, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC); typedef void (*func_mysqlnd_res__fetch_field_data)(MYSQLND_RES *result, php_uint_t offset, zval *return_value TSRMLS_DC); typedef uint64_t (*func_mysqlnd_res__num_rows)(const MYSQLND_RES * const result TSRMLS_DC); typedef php_uint_t (*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 php_uint_t * (*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 */ php_uint_t 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,50 @@ 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 - */ - php_uint_t *lengths; +struct st_mysqlnd_buffered_result_zval +{ + def_mysqlnd_buffered_result_parent; - MYSQLND_MEMORY_POOL *result_set_memory_pool; + zval **data; + zval **data_cursor; +}; - php_uint_t references; - MYSQLND_ERROR_INFO error_info; +struct st_mysqlnd_buffered_result_c +{ + def_mysqlnd_buffered_result_parent; - php_uint_t field_count; - zend_bool ps; - zend_bool persistent; - - 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 fe821e9a7d..375fa0989c 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, php_uint_t 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) { + php_uint_t 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; */ php_uint_t 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_INT) { bit_area += 1 + sprintf((char *)start, ZEND_INT_FMT, Z_IVAL_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_STRSIZE_PP(current_field)); bit_area += Z_STRSIZE_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 43aff57d8b..f658b25be0 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, + php_uint_t 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, php_uint_t 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 75c23f9501..9d1bcac8f2 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; } /* }}} */ @@ -229,8 +230,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) @@ -239,7 +240,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 b3d644a2d9..10860f4d44 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -2211,8 +2211,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 bd0ce1eda3..923ad4cc29 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 8de24260b8..d17bc99dd0 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_STRSIZE_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_STRSIZE_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_STRSIZE_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 1197f61d5d..e1a6d0d8cb 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 8d42b1da86..95da75169e 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_STRSIZE_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/xp_ssl.c b/ext/openssl/xp_ssl.c index e0bfcc8f9b..421c2fac5e 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 736a36415b..4776fbf6f8 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 diff --git a/ext/pdo_firebird/firebird_statement.c b/ext/pdo_firebird/firebird_statement.c index ebd5b242dc..7b1ac0b9f5 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 08a3138cb6..77ea1a528a 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 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 72c7b52f2a..831fa59726 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 2cb95bd6f3..66633295e6 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/pgsql/pgsql.c b/ext/pgsql/pgsql.c index fe422d099c..66b43d858d 100644 --- a/ext/pgsql/pgsql.c +++ b/ext/pgsql/pgsql.c @@ -5862,9 +5862,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 17d51e9f12..77ba903fe7 100644 --- a/ext/phar/phar_object.c +++ b/ext/phar/phar_object.c @@ -1939,10 +1939,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_INT(zend_hash_num_elements(&phar_obj->arc.archive->manifest)); diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 31101451ef..ac6f1e9ca8 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_IVAL_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/soap/php_encoding.c b/ext/soap/php_encoding.c index 8a37817377..45a5d97d3a 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/spl_observer.c b/ext/spl/spl_observer.c index 5f1f740e31..f7951b9c8e 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" @@ -623,11 +624,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) { + php_int_t mode = COUNT_NORMAL; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|i", &mode) == FAILURE) { return; } - + + if (mode == COUNT_RECURSIVE) { + php_int_t 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_INT(ret); + return; + } + RETURN_INT(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_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]<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.maxCountflags &= ~(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"), - /* 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]<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]; } @@ -23432,32 +23564,6 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ # endif #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. */ @@ -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( @@ -54346,36 +54489,6 @@ SQLITE_PRIVATE void sqlite3BtreeCursorZero(BtCursor *p){ memset(p, 0, offsetof(BtCursor, iPage)); } -/* -** Set the cached rowid value of every cursor in the same database file -** as pCur and having the same root page number as pCur. The value is -** set to iRowid. -** -** Only positive rowid values are considered valid for this cache. -** The cache is initialized to zero, indicating an invalid cache. -** A btree will work fine with zero or negative rowids. We just cannot -** cache zero or negative rowids, which means tables that use zero or -** negative rowids might run a little slower. But in practice, zero -** or negative rowids are very uncommon so this should not be a problem. -*/ -SQLITE_PRIVATE void sqlite3BtreeSetCachedRowid(BtCursor *pCur, sqlite3_int64 iRowid){ - BtCursor *p; - for(p=pCur->pBt->pCursor; p; p=p->pNext){ - if( p->pgnoRoot==pCur->pgnoRoot ) p->cachedRowid = iRowid; - } - assert( pCur->cachedRowid==iRowid ); -} - -/* -** Return the cached rowid for the given cursor. A negative or zero -** return value indicates that the rowid cache is invalid and should be -** ignored. If the rowid cache has never before been set, then a -** zero is returned. -*/ -SQLITE_PRIVATE sqlite3_int64 sqlite3BtreeGetCachedRowid(BtCursor *pCur){ - return pCur->cachedRowid; -} - /* ** Close a cursor. The read lock on the database file is released ** when the last cursor is closed. @@ -55252,6 +55365,7 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked( int *pRes /* Write search results here */ ){ int rc; + RecordCompare xRecordCompare; assert( cursorHoldsMutex(pCur) ); assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); @@ -55273,6 +55387,16 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked( } } + if( pIdxKey ){ + xRecordCompare = sqlite3VdbeFindCompare(pIdxKey); + assert( pIdxKey->default_rc==1 + || pIdxKey->default_rc==0 + || pIdxKey->default_rc==-1 + ); + }else{ + xRecordCompare = 0; /* All keys are integers */ + } + rc = moveToRoot(pCur); if( rc ){ return rc; @@ -55305,7 +55429,7 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked( assert( biasRight==0 || biasRight==1 ); idx = upr>>(1-biasRight); /* idx = biasRight ? upr : (lwr+upr)/2; */ pCur->aiIdx[pCur->iPage] = (u16)idx; - if( pPage->intKey ){ + if( xRecordCompare==0 ){ for(;;){ i64 nCellKey; pCell = findCell(pPage, idx) + pPage->childPtrSize; @@ -55357,14 +55481,14 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked( ** single byte varint and the record fits entirely on the main ** b-tree page. */ testcase( pCell+nCell+1==pPage->aDataEnd ); - c = sqlite3VdbeRecordCompare(nCell, (void*)&pCell[1], pIdxKey); + c = xRecordCompare(nCell, (void*)&pCell[1], pIdxKey, 0); }else if( !(pCell[1] & 0x80) && (nCell = ((nCell&0x7f)<<7) + pCell[1])<=pPage->maxLocal ){ /* The record-size field is a 2 byte varint and the record ** fits entirely on the main b-tree page. */ testcase( pCell+nCell+2==pPage->aDataEnd ); - c = sqlite3VdbeRecordCompare(nCell, (void*)&pCell[2], pIdxKey); + c = xRecordCompare(nCell, (void*)&pCell[2], pIdxKey, 0); }else{ /* The record flows over onto one or more overflow pages. In ** this case the whole cell needs to be parsed, a buffer allocated @@ -55385,7 +55509,7 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked( sqlite3_free(pCellKey); goto moveto_finish; } - c = sqlite3VdbeRecordCompare(nCell, pCellKey, pIdxKey); + c = xRecordCompare(nCell, pCellKey, pIdxKey, 0); sqlite3_free(pCellKey); } if( c<0 ){ @@ -55450,6 +55574,15 @@ SQLITE_PRIVATE int sqlite3BtreeEof(BtCursor *pCur){ ** successful then set *pRes=0. If the cursor ** was already pointing to the last entry in the database before ** this routine was called, then set *pRes=1. +** +** The calling function will set *pRes to 0 or 1. The initial *pRes value +** will be 1 if the cursor being stepped corresponds to an SQL index and +** if this routine could have been skipped if that SQL index had been +** a unique index. Otherwise the caller will have set *pRes to zero. +** Zero is the common case. The btree implementation is free to use the +** initial *pRes value as a hint to improve performance, but the current +** SQLite btree implementation does not. (Note that the comdb2 btree +** implementation does use this hint, however.) */ SQLITE_PRIVATE int sqlite3BtreeNext(BtCursor *pCur, int *pRes){ int rc; @@ -55458,6 +55591,7 @@ SQLITE_PRIVATE int sqlite3BtreeNext(BtCursor *pCur, int *pRes){ assert( cursorHoldsMutex(pCur) ); assert( pRes!=0 ); + assert( *pRes==0 || *pRes==1 ); assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID ); if( pCur->eState!=CURSOR_VALID ){ rc = restoreCursorPosition(pCur); @@ -55536,6 +55670,15 @@ SQLITE_PRIVATE int sqlite3BtreeNext(BtCursor *pCur, int *pRes){ ** successful then set *pRes=0. If the cursor ** was already pointing to the first entry in the database before ** this routine was called, then set *pRes=1. +** +** The calling function will set *pRes to 0 or 1. The initial *pRes value +** will be 1 if the cursor being stepped corresponds to an SQL index and +** if this routine could have been skipped if that SQL index had been +** a unique index. Otherwise the caller will have set *pRes to zero. +** Zero is the common case. The btree implementation is free to use the +** initial *pRes value as a hint to improve performance, but the current +** SQLite btree implementation does not. (Note that the comdb2 btree +** implementation does use this hint, however.) */ SQLITE_PRIVATE int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){ int rc; @@ -55543,6 +55686,7 @@ SQLITE_PRIVATE int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){ assert( cursorHoldsMutex(pCur) ); assert( pRes!=0 ); + assert( *pRes==0 || *pRes==1 ); assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID ); pCur->atLast = 0; if( pCur->eState!=CURSOR_VALID ){ @@ -57639,11 +57783,17 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur); if( rc ) return rc; - /* If this is an insert into a table b-tree, invalidate any incrblob - ** cursors open on the row being replaced (assuming this is a replace - ** operation - if it is not, the following is a no-op). */ if( pCur->pKeyInfo==0 ){ + /* If this is an insert into a table b-tree, invalidate any incrblob + ** cursors open on the row being replaced */ invalidateIncrblobCursors(p, nKey, 0); + + /* If the cursor is currently on the last row and we are appending a + ** new row onto the end, set the "loc" to avoid an unnecessary btreeMoveto() + ** call */ + if( pCur->validNKey && nKey>0 && pCur->info.nKey==nKey-1 ){ + loc = -1; + } } if( !loc ){ @@ -57713,8 +57863,8 @@ SQLITE_PRIVATE int sqlite3BtreeInsert( ** row without seeking the cursor. This can be a big performance boost. */ pCur->info.nSize = 0; - pCur->validNKey = 0; if( rc==SQLITE_OK && pPage->nOverflow ){ + pCur->validNKey = 0; rc = balance(pCur); /* Must make sure nOverflow is reset to zero even if the balance() @@ -57769,7 +57919,7 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur){ ** sub-tree headed by the child page of the cell being deleted. This makes ** balancing the tree following the delete operation easier. */ if( !pPage->leaf ){ - int notUsed; + int notUsed = 0; rc = sqlite3BtreePrevious(pCur, ¬Used); if( rc ) return rc; } @@ -59923,6 +60073,42 @@ copy_finished: ** name sqlite_value */ +#ifdef SQLITE_DEBUG +/* +** Check invariants on a Mem object. +** +** This routine is intended for use inside of assert() statements, like +** this: assert( sqlite3VdbeCheckMemInvariants(pMem) ); +*/ +SQLITE_PRIVATE int sqlite3VdbeCheckMemInvariants(Mem *p){ + /* The MEM_Dyn bit is set if and only if Mem.xDel is a non-NULL destructor + ** function for Mem.z + */ + assert( (p->flags & MEM_Dyn)==0 || p->xDel!=0 ); + assert( (p->flags & MEM_Dyn)!=0 || p->xDel==0 ); + + /* If p holds a string or blob, the Mem.z must point to exactly + ** one of the following: + ** + ** (1) Memory in Mem.zMalloc and managed by the Mem object + ** (2) Memory to be freed using Mem.xDel + ** (3) An ephermal string or blob + ** (4) A static string or blob + */ + if( (p->flags & (MEM_Str|MEM_Blob)) && p->z!=0 ){ + assert( + ((p->z==p->zMalloc)? 1 : 0) + + ((p->flags&MEM_Dyn)!=0 ? 1 : 0) + + ((p->flags&MEM_Ephem)!=0 ? 1 : 0) + + ((p->flags&MEM_Static)!=0 ? 1 : 0) == 1 + ); + } + + return 1; +} +#endif + + /* ** If pMem is an object with a valid string representation, this routine ** ensures the internal encoding for the string representation is @@ -59972,12 +60158,7 @@ SQLITE_PRIVATE int sqlite3VdbeChangeEncoding(Mem *pMem, int desiredEnc){ ** in pMem->z is discarded. */ SQLITE_PRIVATE int sqlite3VdbeMemGrow(Mem *pMem, int n, int bPreserve){ - assert( 1 >= - ((pMem->zMalloc && pMem->zMalloc==pMem->z) ? 1 : 0) + - (((pMem->flags&MEM_Dyn)&&pMem->xDel) ? 1 : 0) + - ((pMem->flags&MEM_Ephem) ? 1 : 0) + - ((pMem->flags&MEM_Static) ? 1 : 0) - ); + assert( sqlite3VdbeCheckMemInvariants(pMem) ); assert( (pMem->flags&MEM_RowSet)==0 ); /* If the bPreserve flag is set to true, then the memory cell must already @@ -59995,7 +60176,8 @@ SQLITE_PRIVATE int sqlite3VdbeMemGrow(Mem *pMem, int n, int bPreserve){ pMem->zMalloc = sqlite3DbMallocRaw(pMem->db, n); } if( pMem->zMalloc==0 ){ - sqlite3VdbeMemRelease(pMem); + VdbeMemRelease(pMem); + pMem->z = 0; pMem->flags = MEM_Null; return SQLITE_NOMEM; } @@ -60004,13 +60186,13 @@ SQLITE_PRIVATE int sqlite3VdbeMemGrow(Mem *pMem, int n, int bPreserve){ if( pMem->z && bPreserve && pMem->z!=pMem->zMalloc ){ memcpy(pMem->zMalloc, pMem->z, pMem->n); } - if( (pMem->flags&MEM_Dyn)!=0 && pMem->xDel ){ - assert( pMem->xDel!=SQLITE_DYNAMIC ); + if( (pMem->flags&MEM_Dyn)!=0 ){ + assert( pMem->xDel!=0 && pMem->xDel!=SQLITE_DYNAMIC ); pMem->xDel((void *)(pMem->z)); } pMem->z = pMem->zMalloc; - pMem->flags &= ~(MEM_Ephem|MEM_Static); + pMem->flags &= ~(MEM_Dyn|MEM_Ephem|MEM_Static); pMem->xDel = 0; return SQLITE_OK; } @@ -60179,9 +60361,9 @@ SQLITE_PRIVATE void sqlite3VdbeMemReleaseExternal(Mem *p){ sqlite3VdbeMemFinalize(p, p->u.pDef); assert( (p->flags & MEM_Agg)==0 ); sqlite3VdbeMemRelease(p); - }else if( p->flags&MEM_Dyn && p->xDel ){ + }else if( p->flags&MEM_Dyn ){ assert( (p->flags&MEM_RowSet)==0 ); - assert( p->xDel!=SQLITE_DYNAMIC ); + assert( p->xDel!=SQLITE_DYNAMIC && p->xDel!=0 ); p->xDel((void *)p->z); p->xDel = 0; }else if( p->flags&MEM_RowSet ){ @@ -60194,9 +60376,10 @@ SQLITE_PRIVATE void sqlite3VdbeMemReleaseExternal(Mem *p){ /* ** Release any memory held by the Mem. This may leave the Mem in an ** inconsistent state, for example with (Mem.z==0) and -** (Mem.type==SQLITE_TEXT). +** (Mem.flags==MEM_Str). */ SQLITE_PRIVATE void sqlite3VdbeMemRelease(Mem *p){ + assert( sqlite3VdbeCheckMemInvariants(p) ); VdbeMemRelease(p); if( p->zMalloc ){ sqlite3DbFree(p->db, p->zMalloc); @@ -60385,7 +60568,6 @@ SQLITE_PRIVATE void sqlite3VdbeMemSetNull(Mem *pMem){ sqlite3RowSetClear(pMem->u.pRowSet); } MemSetTypeFlag(pMem, MEM_Null); - pMem->type = SQLITE_NULL; } SQLITE_PRIVATE void sqlite3ValueSetNull(sqlite3_value *p){ sqlite3VdbeMemSetNull((Mem*)p); @@ -60398,7 +60580,6 @@ SQLITE_PRIVATE void sqlite3ValueSetNull(sqlite3_value *p){ SQLITE_PRIVATE void sqlite3VdbeMemSetZeroBlob(Mem *pMem, int n){ sqlite3VdbeMemRelease(pMem); pMem->flags = MEM_Blob|MEM_Zero; - pMem->type = SQLITE_BLOB; pMem->n = 0; if( n<0 ) n = 0; pMem->u.nZero = n; @@ -60421,7 +60602,6 @@ SQLITE_PRIVATE void sqlite3VdbeMemSetInt64(Mem *pMem, i64 val){ sqlite3VdbeMemRelease(pMem); pMem->u.i = val; pMem->flags = MEM_Int; - pMem->type = SQLITE_INTEGER; } #ifndef SQLITE_OMIT_FLOATING_POINT @@ -60436,7 +60616,6 @@ SQLITE_PRIVATE void sqlite3VdbeMemSetDouble(Mem *pMem, double val){ sqlite3VdbeMemRelease(pMem); pMem->r = val; pMem->flags = MEM_Real; - pMem->type = SQLITE_FLOAT; } } #endif @@ -60492,7 +60671,7 @@ SQLITE_PRIVATE void sqlite3VdbeMemAboutToChange(Vdbe *pVdbe, Mem *pMem){ Mem *pX; for(i=1, pX=&pVdbe->aMem[1]; i<=pVdbe->nMem; i++, pX++){ if( pX->pScopyFrom==pMem ){ - pX->flags |= MEM_Invalid; + pX->flags |= MEM_Undefined; pX->pScopyFrom = 0; } } @@ -60534,6 +60713,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemCopy(Mem *pTo, const Mem *pFrom){ VdbeMemRelease(pTo); memcpy(pTo, pFrom, MEMCELLSIZE); pTo->flags &= ~MEM_Dyn; + pTo->xDel = 0; if( pTo->flags&(MEM_Str|MEM_Blob) ){ if( 0==(pFrom->flags&MEM_Static) ){ @@ -60644,7 +60824,6 @@ SQLITE_PRIVATE int sqlite3VdbeMemSetStr( pMem->n = nByte; pMem->flags = flags; pMem->enc = (enc==0 ? SQLITE_UTF8 : enc); - pMem->type = (enc==0 ? SQLITE_BLOB : SQLITE_TEXT); #ifndef SQLITE_OMIT_UTF16 if( pMem->enc!=SQLITE_UTF8 && sqlite3VdbeMemHandleBom(pMem) ){ @@ -60659,119 +60838,6 @@ SQLITE_PRIVATE int sqlite3VdbeMemSetStr( return SQLITE_OK; } -/* -** 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( r1r2 ) 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 @@ -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; iaMem[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{ @@ -61465,6 +61523,9 @@ SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3){ #ifdef VDBE_PROFILE pOp->cycles = 0; pOp->cnt = 0; +#endif +#ifdef SQLITE_VDBE_COVERAGE + pOp->iSrcLine = 0; #endif return i; } @@ -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 && addrnOp) || 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]; pflags & 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; inCursor; 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; inOp; 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. + ** 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; + } +} + +/* +** 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. */ - assert( rc==0 ); - if( pPKey2->flags & UNPACKED_INCRKEY ){ - rc = -1; - }else if( pPKey2->flags & UNPACKED_PREFIX_MATCH ){ - /* Leave rc==0 */ - }else if( idx1u.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( r1r2 ) 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.rrhs ){ + rc = +1; + } + }else{ + i64 lhs = vdbeRecordDecodeInt(serial_type, &aKey1[d1]); + i64 rhs = pRhs->u.i; + if( lhsrhs ){ + 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( lhsrhs ){ + 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 && inField && 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( vr2; + }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 @@ -66474,6 +67112,34 @@ SQLITE_API int sqlite3_found_count = 0; # define UPDATE_MAX_BLOBSIZE(P) #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 && pcnOp ); 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: Yield P1 * * * * +/* 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->p2nOp ); + assert( pOp->p3>=0 && pOp->p3nOp ); + 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.inOp ); + pCaller = &aOp[pIn1->u.i]; + assert( pCaller->opcode==OP_Yield ); + assert( pCaller->p2>=0 && pCaller->p2nOp ); + pc = pCaller->p2 - 1; + pIn1->flags = MEM_Undefined; + break; +} + +/* 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->p1nOnceFlag ); + 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( p2nHdrParsed ); 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->p1nDb ); - assert( (p->btreeMask & (((yDbMask)1)<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; iip3+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; iip2 - 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( vu.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, vp3, 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( vu.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->p2nOp ); + 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->p1nCursor ); assert( pOp->p5aCounter) ); 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; ip4type==P4_INT32 ); r.pKeyInfo = pC->pKeyInfo; r.nField = (u16)pOp->p4.i; - if( pOp->p5 ){ - r.flags = UNPACKED_INCRKEY | UNPACKED_PREFIX_MATCH; + if( pOp->opcodeopcode==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)<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; iinVtabMethod = 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; ip2 ){ + 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; @@ -77761,24 +78466,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 @@ -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 . */ + /* Code an OP_Transaction and OP_TableLock for
. */ iDb = sqlite3SchemaToIndex(db, pTab->pSchema); sqlite3CodeVerifySchema(pParse, iDb); sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); @@ -77986,9 +78673,8 @@ SQLITE_PRIVATE int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){ */ assert(v); if( iCol<0 ){ - int iAddr; - - iAddr = sqlite3CodeOnce(pParse); + int iAddr = sqlite3CodeOnce(pParse); + VdbeCoverage(v); sqlite3OpenTable(pParse, iTab, iDb, pTab, OP_OpenRead); eType = IN_INDEX_ROWID; @@ -78013,18 +78699,18 @@ SQLITE_PRIVATE int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){ && sqlite3FindCollSeq(db, ENC(db), pIdx->azColl[0], 0)==pReq && (!mustBeUnique || (pIdx->nKeyCol==1 && pIdx->onError!=OE_None)) ){ - int iAddr = sqlite3CodeOnce(pParse); + int iAddr = sqlite3CodeOnce(pParse); VdbeCoverage(v); sqlite3VdbeAddOp3(v, OP_OpenRead, iTab, pIdx->tnum, iDb); sqlite3VdbeSetP4KeyInfo(pParse, pIdx); VdbeComment((v, "%s", pIdx->zName)); assert( IN_INDEX_INDEX_DESC == IN_INDEX_INDEX_ASC+1 ); eType = IN_INDEX_INDEX_ASC + pIdx->aSortOrder[0]; - sqlite3VdbeJumpHere(v, iAddr); if( prNotFound && !pTab->aCol[iCol].notNull ){ *prNotFound = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_Null, 0, *prNotFound); } + sqlite3VdbeJumpHere(v, iAddr); } } } @@ -78113,7 +78799,7 @@ SQLITE_PRIVATE int sqlite3CodeSubselect( ** save the results, and reuse the same result on subsequent invocations. */ if( !ExprHasProperty(pExpr, EP_VarSelect) ){ - testAddr = sqlite3CodeOnce(pParse); + testAddr = sqlite3CodeOnce(pParse); VdbeCoverage(v); } #ifndef SQLITE_OMIT_EXPLAIN @@ -78154,7 +78840,6 @@ SQLITE_PRIVATE int sqlite3CodeSubselect( */ pExpr->iTable = pParse->nTab++; addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pExpr->iTable, !isRowid); - if( rMayHaveNull==0 ) sqlite3VdbeChangeP5(v, BTREE_UNORDERED); pKeyInfo = isRowid ? 0 : sqlite3KeyInfoAlloc(pParse->db, 1, 1); if( ExprHasProperty(pExpr, EP_xIsSelect) ){ @@ -78230,6 +78915,7 @@ SQLITE_PRIVATE int sqlite3CodeSubselect( if( isRowid ){ sqlite3VdbeAddOp2(v, OP_MustBeInt, r3, sqlite3VdbeCurrentAddr(v)+2); + VdbeCoverage(v); sqlite3VdbeAddOp3(v, OP_Insert, pExpr->iTable, r2, r3); }else{ sqlite3VdbeAddOp4(v, OP_MakeRecord, r3, 1, r2, &affinity, 1); @@ -78353,10 +79039,11 @@ static void sqlite3ExprCodeIN( if( destIfNull==destIfFalse ){ /* Shortcut for the common case where the false and NULL outcomes are ** the same. */ - sqlite3VdbeAddOp2(v, OP_IsNull, r1, destIfNull); + sqlite3VdbeAddOp2(v, OP_IsNull, r1, destIfNull); VdbeCoverage(v); }else{ - int addr1 = sqlite3VdbeAddOp1(v, OP_NotNull, r1); + int addr1 = sqlite3VdbeAddOp1(v, OP_NotNull, r1); VdbeCoverage(v); sqlite3VdbeAddOp2(v, OP_Rewind, pExpr->iTable, destIfFalse); + VdbeCoverage(v); sqlite3VdbeAddOp2(v, OP_Goto, 0, destIfNull); sqlite3VdbeJumpHere(v, addr1); } @@ -78364,8 +79051,9 @@ static void sqlite3ExprCodeIN( if( eType==IN_INDEX_ROWID ){ /* In this case, the RHS is the ROWID of table b-tree */ - sqlite3VdbeAddOp2(v, OP_MustBeInt, r1, destIfFalse); + sqlite3VdbeAddOp2(v, OP_MustBeInt, r1, destIfFalse); VdbeCoverage(v); sqlite3VdbeAddOp3(v, OP_NotExists, pExpr->iTable, destIfFalse, r1); + VdbeCoverage(v); }else{ /* In this case, the RHS is an index b-tree. */ @@ -78386,19 +79074,20 @@ static void sqlite3ExprCodeIN( ** for this particular IN operator. */ sqlite3VdbeAddOp4Int(v, OP_NotFound, pExpr->iTable, destIfFalse, r1, 1); - + VdbeCoverage(v); }else{ /* In this branch, the RHS of the IN might contain a NULL and ** the presence of a NULL on the RHS makes a difference in the ** outcome. */ - int j1, j2, j3; + int j1, j2; /* First check to see if the LHS is contained in the RHS. If so, ** then the presence of NULLs in the RHS does not matter, so jump ** over all of the code that follows. */ j1 = sqlite3VdbeAddOp4Int(v, OP_Found, pExpr->iTable, 0, r1, 1); + VdbeCoverage(v); /* Here we begin generating code that runs if the LHS is not ** contained within the RHS. Generate additional code that @@ -78406,18 +79095,15 @@ static void sqlite3ExprCodeIN( ** jump to destIfNull. If there are no NULLs in the RHS then ** jump to destIfFalse. */ - j2 = sqlite3VdbeAddOp1(v, OP_NotNull, rRhsHasNull); - j3 = sqlite3VdbeAddOp4Int(v, OP_Found, pExpr->iTable, 0, rRhsHasNull, 1); - sqlite3VdbeAddOp2(v, OP_Integer, -1, rRhsHasNull); - sqlite3VdbeJumpHere(v, j3); - sqlite3VdbeAddOp2(v, OP_AddImm, rRhsHasNull, 1); - sqlite3VdbeJumpHere(v, j2); - - /* Jump to the appropriate target depending on whether or not - ** the RHS contains a NULL - */ - sqlite3VdbeAddOp2(v, OP_If, rRhsHasNull, destIfNull); + sqlite3VdbeAddOp2(v, OP_If, rRhsHasNull, destIfNull); VdbeCoverage(v); + sqlite3VdbeAddOp2(v, OP_IfNot, rRhsHasNull, destIfFalse); VdbeCoverage(v); + j2 = sqlite3VdbeAddOp4Int(v, OP_Found, pExpr->iTable, 0, rRhsHasNull, 1); + VdbeCoverage(v); + sqlite3VdbeAddOp2(v, OP_Integer, 0, rRhsHasNull); sqlite3VdbeAddOp2(v, OP_Goto, 0, destIfFalse); + sqlite3VdbeJumpHere(v, j2); + sqlite3VdbeAddOp2(v, OP_Integer, 1, rRhsHasNull); + sqlite3VdbeAddOp2(v, OP_Goto, 0, destIfNull); /* The OP_Found at the top of this branch jumps here when true, ** causing the overall IN expression evaluation to fall through. @@ -78938,22 +79624,16 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target) case TK_GE: case TK_NE: case TK_EQ: { - assert( TK_LT==OP_Lt ); - assert( TK_LE==OP_Le ); - assert( TK_GT==OP_Gt ); - assert( TK_GE==OP_Ge ); - assert( TK_EQ==OP_Eq ); - assert( TK_NE==OP_Ne ); - testcase( op==TK_LT ); - testcase( op==TK_LE ); - testcase( op==TK_GT ); - testcase( op==TK_GE ); - testcase( op==TK_EQ ); - testcase( op==TK_NE ); r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, ®Free2); codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, r1, r2, inReg, SQLITE_STOREP2); + assert(TK_LT==OP_Lt); testcase(op==OP_Lt); VdbeCoverageIf(v,op==OP_Lt); + assert(TK_LE==OP_Le); testcase(op==OP_Le); VdbeCoverageIf(v,op==OP_Le); + assert(TK_GT==OP_Gt); testcase(op==OP_Gt); VdbeCoverageIf(v,op==OP_Gt); + assert(TK_GE==OP_Ge); testcase(op==OP_Ge); VdbeCoverageIf(v,op==OP_Ge); + assert(TK_EQ==OP_Eq); testcase(op==OP_Eq); VdbeCoverageIf(v,op==OP_Eq); + assert(TK_NE==OP_Ne); testcase(op==OP_Ne); VdbeCoverageIf(v,op==OP_Ne); testcase( regFree1==0 ); testcase( regFree2==0 ); break; @@ -78967,6 +79647,8 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target) op = (op==TK_IS) ? TK_EQ : TK_NE; codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, r1, r2, inReg, SQLITE_STOREP2 | SQLITE_NULLEQ); + VdbeCoverageIf(v, op==TK_EQ); + VdbeCoverageIf(v, op==TK_NE); testcase( regFree1==0 ); testcase( regFree2==0 ); break; @@ -78983,28 +79665,17 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target) case TK_LSHIFT: case TK_RSHIFT: case TK_CONCAT: { - assert( TK_AND==OP_And ); - assert( TK_OR==OP_Or ); - assert( TK_PLUS==OP_Add ); - assert( TK_MINUS==OP_Subtract ); - assert( TK_REM==OP_Remainder ); - assert( TK_BITAND==OP_BitAnd ); - assert( TK_BITOR==OP_BitOr ); - assert( TK_SLASH==OP_Divide ); - assert( TK_LSHIFT==OP_ShiftLeft ); - assert( TK_RSHIFT==OP_ShiftRight ); - assert( TK_CONCAT==OP_Concat ); - testcase( op==TK_AND ); - testcase( op==TK_OR ); - testcase( op==TK_PLUS ); - testcase( op==TK_MINUS ); - testcase( op==TK_REM ); - testcase( op==TK_BITAND ); - testcase( op==TK_BITOR ); - testcase( op==TK_SLASH ); - testcase( op==TK_LSHIFT ); - testcase( op==TK_RSHIFT ); - testcase( op==TK_CONCAT ); + assert( TK_AND==OP_And ); testcase( op==TK_AND ); + assert( TK_OR==OP_Or ); testcase( op==TK_OR ); + assert( TK_PLUS==OP_Add ); testcase( op==TK_PLUS ); + assert( TK_MINUS==OP_Subtract ); testcase( op==TK_MINUS ); + assert( TK_REM==OP_Remainder ); testcase( op==TK_REM ); + assert( TK_BITAND==OP_BitAnd ); testcase( op==TK_BITAND ); + assert( TK_BITOR==OP_BitOr ); testcase( op==TK_BITOR ); + assert( TK_SLASH==OP_Divide ); testcase( op==TK_SLASH ); + assert( TK_LSHIFT==OP_ShiftLeft ); testcase( op==TK_LSHIFT ); + assert( TK_RSHIFT==OP_ShiftRight ); testcase( op==TK_RSHIFT ); + assert( TK_CONCAT==OP_Concat ); testcase( op==TK_CONCAT ); r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, ®Free2); sqlite3VdbeAddOp3(v, op, r2, r1, target); @@ -79036,10 +79707,8 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target) } case TK_BITNOT: case TK_NOT: { - assert( TK_BITNOT==OP_BitNot ); - assert( TK_NOT==OP_Not ); - testcase( op==TK_BITNOT ); - testcase( op==TK_NOT ); + assert( TK_BITNOT==OP_BitNot ); testcase( op==TK_BITNOT ); + assert( TK_NOT==OP_Not ); testcase( op==TK_NOT ); r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); testcase( regFree1==0 ); inReg = target; @@ -79049,14 +79718,14 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target) case TK_ISNULL: case TK_NOTNULL: { int addr; - assert( TK_ISNULL==OP_IsNull ); - assert( TK_NOTNULL==OP_NotNull ); - testcase( op==TK_ISNULL ); - testcase( op==TK_NOTNULL ); + assert( TK_ISNULL==OP_IsNull ); testcase( op==TK_ISNULL ); + assert( TK_NOTNULL==OP_NotNull ); testcase( op==TK_NOTNULL ); sqlite3VdbeAddOp2(v, OP_Integer, 1, target); r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); testcase( regFree1==0 ); addr = sqlite3VdbeAddOp1(v, op, r1); + VdbeCoverageIf(v, op==TK_ISNULL); + VdbeCoverageIf(v, op==TK_NOTNULL); sqlite3VdbeAddOp2(v, OP_AddImm, target, -1); sqlite3VdbeJumpHere(v, addr); break; @@ -79108,6 +79777,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target) sqlite3ExprCode(pParse, pFarg->a[0].pExpr, target); for(i=1; ia[i].pExpr, target); @@ -79245,13 +79915,14 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target) r3 = sqlite3GetTempReg(pParse); r4 = sqlite3GetTempReg(pParse); codeCompare(pParse, pLeft, pRight, OP_Ge, - r1, r2, r3, SQLITE_STOREP2); + r1, r2, r3, SQLITE_STOREP2); VdbeCoverage(v); pLItem++; pRight = pLItem->pExpr; sqlite3ReleaseTempReg(pParse, regFree2); r2 = sqlite3ExprCodeTemp(pParse, pRight, ®Free2); testcase( regFree2==0 ); codeCompare(pParse, pLeft, pRight, OP_Le, r1, r2, r4, SQLITE_STOREP2); + VdbeCoverage(v); sqlite3VdbeAddOp3(v, OP_And, r3, r4, target); sqlite3ReleaseTempReg(pParse, r3); sqlite3ReleaseTempReg(pParse, r4); @@ -79418,6 +80089,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target) if( pExpr->affinity==OE_Ignore ){ sqlite3VdbeAddOp4( v, OP_Halt, SQLITE_OK, OE_Ignore, 0, pExpr->u.zToken,0); + VdbeCoverage(v); }else{ sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_TRIGGER, pExpr->affinity, pExpr->u.zToken, 0, 0); @@ -79505,7 +80177,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeTemp(Parse *pParse, Expr *pExpr, int *pReg){ ** results in register target. The results are guaranteed to appear ** in register target. */ -SQLITE_PRIVATE int sqlite3ExprCode(Parse *pParse, Expr *pExpr, int target){ +SQLITE_PRIVATE void sqlite3ExprCode(Parse *pParse, Expr *pExpr, int target){ int inReg; assert( target>0 && target<=pParse->nMem ); @@ -79518,7 +80190,20 @@ SQLITE_PRIVATE int sqlite3ExprCode(Parse *pParse, Expr *pExpr, int target){ sqlite3VdbeAddOp2(pParse->pVdbe, OP_SCopy, inReg, target); } } - return target; +} + +/* +** Generate code that will evaluate expression pExpr and store the +** results in register target. The results are guaranteed to appear +** in register target. If the expression is constant, then this routine +** might choose to code the expression at initialization time. +*/ +SQLITE_PRIVATE void sqlite3ExprCodeFactorable(Parse *pParse, Expr *pExpr, int target){ + if( pParse->okConstFactor && sqlite3ExprIsConstant(pExpr) ){ + sqlite3ExprCodeAtInit(pParse, pExpr, target, 0); + }else{ + sqlite3ExprCode(pParse, pExpr, target); + } } /* @@ -79533,25 +80218,16 @@ SQLITE_PRIVATE int sqlite3ExprCode(Parse *pParse, Expr *pExpr, int target){ ** times. They are evaluated once and the results of the expression ** are reused. */ -SQLITE_PRIVATE int sqlite3ExprCodeAndCache(Parse *pParse, Expr *pExpr, int target){ +SQLITE_PRIVATE void sqlite3ExprCodeAndCache(Parse *pParse, Expr *pExpr, int target){ Vdbe *v = pParse->pVdbe; - int inReg; - inReg = sqlite3ExprCode(pParse, pExpr, target); + int iMem; + assert( target>0 ); - /* The only place, other than this routine, where expressions can be - ** converted to TK_REGISTER is internal subexpressions in BETWEEN and - ** CASE operators. Neither ever calls this routine. And this routine - ** is never called twice on the same expression. Hence it is impossible - ** for the input to this routine to already be a register. Nevertheless, - ** it seems prudent to keep the ALWAYS() in case the conditions above - ** change with future modifications or enhancements. */ - if( ALWAYS(pExpr->op!=TK_REGISTER) ){ - int iMem; - iMem = ++pParse->nMem; - sqlite3VdbeAddOp2(v, OP_Copy, inReg, iMem); - exprToRegister(pExpr, iMem); - } - return inReg; + assert( pExpr->op!=TK_REGISTER ); + sqlite3ExprCode(pParse, pExpr, target); + iMem = ++pParse->nMem; + sqlite3VdbeAddOp2(v, OP_Copy, target, iMem); + exprToRegister(pExpr, iMem); } #if defined(SQLITE_ENABLE_TREE_EXPLAIN) @@ -79986,23 +80662,17 @@ SQLITE_PRIVATE void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int case TK_GE: case TK_NE: case TK_EQ: { - assert( TK_LT==OP_Lt ); - assert( TK_LE==OP_Le ); - assert( TK_GT==OP_Gt ); - assert( TK_GE==OP_Ge ); - assert( TK_EQ==OP_Eq ); - assert( TK_NE==OP_Ne ); - testcase( op==TK_LT ); - testcase( op==TK_LE ); - testcase( op==TK_GT ); - testcase( op==TK_GE ); - testcase( op==TK_EQ ); - testcase( op==TK_NE ); testcase( jumpIfNull==0 ); r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, ®Free2); codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, r1, r2, dest, jumpIfNull); + assert(TK_LT==OP_Lt); testcase(op==OP_Lt); VdbeCoverageIf(v,op==OP_Lt); + assert(TK_LE==OP_Le); testcase(op==OP_Le); VdbeCoverageIf(v,op==OP_Le); + assert(TK_GT==OP_Gt); testcase(op==OP_Gt); VdbeCoverageIf(v,op==OP_Gt); + assert(TK_GE==OP_Ge); testcase(op==OP_Ge); VdbeCoverageIf(v,op==OP_Ge); + assert(TK_EQ==OP_Eq); testcase(op==OP_Eq); VdbeCoverageIf(v,op==OP_Eq); + assert(TK_NE==OP_Ne); testcase(op==OP_Ne); VdbeCoverageIf(v,op==OP_Ne); testcase( regFree1==0 ); testcase( regFree2==0 ); break; @@ -80016,18 +80686,20 @@ SQLITE_PRIVATE void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int op = (op==TK_IS) ? TK_EQ : TK_NE; codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, r1, r2, dest, SQLITE_NULLEQ); + VdbeCoverageIf(v, op==TK_EQ); + VdbeCoverageIf(v, op==TK_NE); testcase( regFree1==0 ); testcase( regFree2==0 ); break; } case TK_ISNULL: case TK_NOTNULL: { - assert( TK_ISNULL==OP_IsNull ); - assert( TK_NOTNULL==OP_NotNull ); - testcase( op==TK_ISNULL ); - testcase( op==TK_NOTNULL ); + assert( TK_ISNULL==OP_IsNull ); testcase( op==TK_ISNULL ); + assert( TK_NOTNULL==OP_NotNull ); testcase( op==TK_NOTNULL ); r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); sqlite3VdbeAddOp2(v, op, r1, dest); + VdbeCoverageIf(v, op==TK_ISNULL); + VdbeCoverageIf(v, op==TK_NOTNULL); testcase( regFree1==0 ); break; } @@ -80054,6 +80726,7 @@ SQLITE_PRIVATE void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int }else{ r1 = sqlite3ExprCodeTemp(pParse, pExpr, ®Free1); sqlite3VdbeAddOp3(v, OP_If, r1, dest, jumpIfNull!=0); + VdbeCoverage(v); testcase( regFree1==0 ); testcase( jumpIfNull==0 ); } @@ -80145,17 +80818,17 @@ SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int case TK_GE: case TK_NE: case TK_EQ: { - testcase( op==TK_LT ); - testcase( op==TK_LE ); - testcase( op==TK_GT ); - testcase( op==TK_GE ); - testcase( op==TK_EQ ); - testcase( op==TK_NE ); testcase( jumpIfNull==0 ); r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, ®Free2); codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, r1, r2, dest, jumpIfNull); + assert(TK_LT==OP_Lt); testcase(op==OP_Lt); VdbeCoverageIf(v,op==OP_Lt); + assert(TK_LE==OP_Le); testcase(op==OP_Le); VdbeCoverageIf(v,op==OP_Le); + assert(TK_GT==OP_Gt); testcase(op==OP_Gt); VdbeCoverageIf(v,op==OP_Gt); + assert(TK_GE==OP_Ge); testcase(op==OP_Ge); VdbeCoverageIf(v,op==OP_Ge); + assert(TK_EQ==OP_Eq); testcase(op==OP_Eq); VdbeCoverageIf(v,op==OP_Eq); + assert(TK_NE==OP_Ne); testcase(op==OP_Ne); VdbeCoverageIf(v,op==OP_Ne); testcase( regFree1==0 ); testcase( regFree2==0 ); break; @@ -80169,16 +80842,18 @@ SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int op = (pExpr->op==TK_IS) ? TK_NE : TK_EQ; codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, r1, r2, dest, SQLITE_NULLEQ); + VdbeCoverageIf(v, op==TK_EQ); + VdbeCoverageIf(v, op==TK_NE); testcase( regFree1==0 ); testcase( regFree2==0 ); break; } case TK_ISNULL: case TK_NOTNULL: { - testcase( op==TK_ISNULL ); - testcase( op==TK_NOTNULL ); r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); sqlite3VdbeAddOp2(v, op, r1, dest); + testcase( op==TK_ISNULL ); VdbeCoverageIf(v, op==TK_ISNULL); + testcase( op==TK_NOTNULL ); VdbeCoverageIf(v, op==TK_NOTNULL); testcase( regFree1==0 ); break; } @@ -80207,6 +80882,7 @@ SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int }else{ r1 = sqlite3ExprCodeTemp(pParse, pExpr, ®Free1); sqlite3VdbeAddOp3(v, OP_IfNot, r1, dest, jumpIfNull!=0); + VdbeCoverage(v); testcase( regFree1==0 ); testcase( jumpIfNull==0 ); } @@ -80753,8 +81429,8 @@ static void renameTableFunc( assert( len>0 ); } while( token!=TK_LP && token!=TK_USING ); - zRet = sqlite3MPrintf(db, "%.*s\"%w\"%s", ((u8*)tname.z) - zSql, zSql, - zTableName, tname.z+tname.n); + zRet = sqlite3MPrintf(db, "%.*s\"%w\"%s", (int)(((u8*)tname.z) - zSql), + zSql, zTableName, tname.z+tname.n); sqlite3_result_text(context, zRet, -1, SQLITE_DYNAMIC); } } @@ -80806,7 +81482,7 @@ static void renameParentFunc( sqlite3Dequote(zParent); if( 0==sqlite3StrICmp((const char *)zOld, zParent) ){ char *zOut = sqlite3MPrintf(db, "%s%.*s\"%w\"", - (zOutput?zOutput:""), z-zInput, zInput, (const char *)zNew + (zOutput?zOutput:""), (int)(z-zInput), zInput, (const char *)zNew ); sqlite3DbFree(db, zOutput); zOutput = zOut; @@ -80892,8 +81568,8 @@ static void renameTriggerFunc( /* Variable tname now contains the token that is the old table-name ** in the CREATE TRIGGER statement. */ - zRet = sqlite3MPrintf(db, "%.*s\"%w\"%s", ((u8*)tname.z) - zSql, zSql, - zTableName, tname.z+tname.n); + zRet = sqlite3MPrintf(db, "%.*s\"%w\"%s", (int)(((u8*)tname.z) - zSql), + zSql, zTableName, tname.z+tname.n); sqlite3_result_text(context, zRet, -1, SQLITE_DYNAMIC); } } @@ -81145,7 +81821,7 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable( } #endif - /* Begin a transaction and code the VerifyCookie for database iDb. + /* Begin a transaction for database iDb. ** Then modify the schema cookie (since the ALTER TABLE modifies the ** schema). Open a statement transaction if the table is a virtual ** table. @@ -81281,6 +81957,7 @@ SQLITE_PRIVATE void sqlite3MinimumFileFormat(Parse *pParse, int iDb, int minForm sqlite3VdbeUsesBtree(v, iDb); sqlite3VdbeAddOp2(v, OP_Integer, minFormat, r2); j1 = sqlite3VdbeAddOp3(v, OP_Ge, r2, 0, r1); + sqlite3VdbeChangeP5(v, SQLITE_NOTNULL); VdbeCoverage(v); sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_FILE_FORMAT, r2); sqlite3VdbeJumpHere(v, j1); sqlite3ReleaseTempReg(pParse, r1); @@ -82581,6 +83258,7 @@ static void analyzeOneTable( ** */ addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur); + VdbeCoverage(v); sqlite3VdbeAddOp2(v, OP_Integer, 0, regChng); addrGotoChng0 = sqlite3VdbeAddOp0(v, OP_Goto); @@ -82602,6 +83280,7 @@ static void analyzeOneTable( aGotoChng[i] = sqlite3VdbeAddOp4(v, OP_Ne, regTemp, 0, regPrev+i, pColl, P4_COLLSEQ); sqlite3VdbeChangeP5(v, SQLITE_NULLEQ); + VdbeCoverage(v); } sqlite3VdbeAddOp2(v, OP_Integer, nCol, regChng); aGotoChng[nCol] = sqlite3VdbeAddOp0(v, OP_Goto); @@ -82648,7 +83327,7 @@ static void analyzeOneTable( sqlite3VdbeAddOp3(v, OP_Function, 1, regStat4, regTemp); sqlite3VdbeChangeP4(v, -1, (char*)&statPushFuncdef, P4_FUNCDEF); sqlite3VdbeChangeP5(v, 2+IsStat34); - sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, addrNextRow); + sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, addrNextRow); VdbeCoverage(v); /* Add the entry to the stat1 table. */ callStatGet(v, regStat4, STAT_GET_STAT1, regStat1); @@ -82675,10 +83354,15 @@ static void analyzeOneTable( addrNext = sqlite3VdbeCurrentAddr(v); callStatGet(v, regStat4, STAT_GET_ROWID, regSampleRowid); addrIsNull = sqlite3VdbeAddOp1(v, OP_IsNull, regSampleRowid); + VdbeCoverage(v); callStatGet(v, regStat4, STAT_GET_NEQ, regEq); callStatGet(v, regStat4, STAT_GET_NLT, regLt); callStatGet(v, regStat4, STAT_GET_NDLT, regDLt); sqlite3VdbeAddOp4Int(v, seekOp, iTabCur, addrNext, regSampleRowid, 0); + /* We know that the regSampleRowid row exists because it was read by + ** the previous loop. Thus the not-found jump of seekOp will never + ** be taken */ + VdbeCoverageNeverTaken(v); #ifdef SQLITE_ENABLE_STAT3 sqlite3ExprCodeGetColumnOfTable(v, pTab, iTabCur, pIdx->aiColumn[0], regSample); @@ -82689,10 +83373,10 @@ static void analyzeOneTable( } sqlite3VdbeAddOp3(v, OP_MakeRecord, regCol, nCol+1, regSample); #endif - sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 6, regTemp, "bbbbbb", 0); + sqlite3VdbeAddOp3(v, OP_MakeRecord, regTabname, 6, regTemp); sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur+1, regNewRowid); sqlite3VdbeAddOp3(v, OP_Insert, iStatCur+1, regTemp, regNewRowid); - sqlite3VdbeAddOp2(v, OP_Goto, 0, addrNext); + sqlite3VdbeAddOp2(v, OP_Goto, 1, addrNext); /* P1==1 for end-of-loop */ sqlite3VdbeJumpHere(v, addrIsNull); } #endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */ @@ -82709,7 +83393,7 @@ static void analyzeOneTable( if( pOnlyIdx==0 && needTableCnt ){ VdbeComment((v, "%s", pTab->zName)); sqlite3VdbeAddOp2(v, OP_Count, iTabCur, regStat1); - jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, regStat1); + jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, regStat1); VdbeCoverage(v); sqlite3VdbeAddOp2(v, OP_Null, 0, regIdxname); sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regTemp, "aaa", 0); sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regNewRowid); @@ -84247,20 +84931,22 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){ ** transaction on each used database and to verify the schema cookie ** on each used database. */ - if( pParse->cookieGoto>0 ){ + if( db->mallocFailed==0 && (pParse->cookieMask || pParse->pConstExpr) ){ yDbMask mask; - int iDb, i, addr; - sqlite3VdbeJumpHere(v, pParse->cookieGoto-1); + int iDb, i; + assert( sqlite3VdbeGetOp(v, 0)->opcode==OP_Init ); + sqlite3VdbeJumpHere(v, 0); for(iDb=0, mask=1; iDbnDb; 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; inVtabLock; 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; inExpr; 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( iDbnDb ); - assert( db->aDb[iDb].pBt!=0 || iDb==1 ); - assert( iDbcookieMask & mask)==0 ){ - pToplevel->cookieMask |= mask; - pToplevel->cookieValue[iDb] = db->aDb[iDb].pSchema->schema_cookie; - if( !OMIT_TEMPDB && iDb==1 ){ - sqlite3OpenTempDatabase(pToplevel); - } + assert( iDb>=0 && iDbnDb ); + assert( db->aDb[iDb].pBt!=0 || iDb==1 ); + assert( iDbcookieMask & 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( addrStartisDeferred, iOk); + VdbeCoverage(v); } for(i=0; inCol; 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; inCol; 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; inCol; 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 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; iopcode==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
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
and its indices -** C: yield X -** if EOF goto D +** C: yield X, at EOF goto D ** insert the select result into
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
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,101 +94015,16 @@ SQLITE_PRIVATE void sqlite3Insert( */ regAutoinc = autoIncBegin(pParse, iDb, pTab); - /* 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 - ** co-routine is the common header to the 3rd and 4th templates. - */ - 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; - regFromSelect = dest.iSdst; - 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 - ** FALSE if each output row of the SELECT can be written directly into - ** the destination table (template 3). - ** - ** A temp table must be used if the table being updated is also one - ** 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) ){ - useTempTable = 1; - } - - if( useTempTable ){ - /* Invoke the coroutine to extract information from the SELECT - ** and add it to a transient table srcTab. The code generated - ** here is from the 4th template: - ** - ** B: open temp table - ** L: yield X - ** if EOF goto M - ** 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 */ - - 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); - 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); - sqlite3ReleaseTempReg(pParse, regRec); - sqlite3ReleaseTempReg(pParse, regTempRowid); - } - }else{ - /* This is the case if the data for the INSERT is coming from a VALUES - ** clause - */ - NameContext sNC; - memset(&sNC, 0, sizeof(sNC)); - sNC.pParse = pParse; - srcTab = -1; - assert( useTempTable==0 ); - nColumn = pList ? pList->nExpr : 0; - for(i=0; ia[i].pExpr) ){ - goto insert_cleanup; - } - } - } - - /* Make sure the number of columns in the source data matches the number - ** of columns to be inserted into the table. + /* 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) ){ - for(i=0; inCol; i++){ - nHidden += (IsHiddenColumn(&pTab->aCol[i]) ? 1 : 0); - } - } - if( pColumn==0 && nColumn && nColumn!=(pTab->nCol-nHidden) ){ - sqlite3ErrorMsg(pParse, - "table %S has %d columns but %d values were supplied", - pTabList, 0, pTab->nCol-nHidden, nColumn); - goto insert_cleanup; - } - if( pColumn!=0 && nColumn!=pColumn->nId ){ - sqlite3ErrorMsg(pParse, "%d values for %d columns", nColumn, pColumn->nId); - goto insert_cleanup; + 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 @@ -93576,6 +94045,7 @@ SQLITE_PRIVATE void sqlite3Insert( for(j=0; jnCol; 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 ); } @@ -93595,6 +94065,90 @@ SQLITE_PRIVATE void sqlite3Insert( } } + /* 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 + ** co-routine is the common header to the 3rd and 4th templates. + */ + if( pSelect ){ + /* Data is coming from a SELECT. Generate a co-routine to run the SELECT */ + 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; + + /* Set useTempTable to TRUE if the result of the SELECT statement + ** should be written into a temporary table (template 4). Set to + ** FALSE if each output row of the SELECT can be written directly into + ** the destination table (template 3). + ** + ** A temp table must be used if the table being updated is also one + ** of the tables being read by the SELECT statement. Also use a + ** temp table in the case of row triggers. + */ + if( pTrigger || readsTable(pParse, iDb, pTab) ){ + useTempTable = 1; + } + + if( useTempTable ){ + /* Invoke the coroutine to extract information from the SELECT + ** and add it to a transient table srcTab. The code generated + ** here is from the 4th template: + ** + ** B: open temp table + ** 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 addrL; /* Label "L" */ + + srcTab = pParse->nTab++; + regRec = sqlite3GetTempReg(pParse); + regTempRowid = sqlite3GetTempReg(pParse); + sqlite3VdbeAddOp2(v, OP_OpenEphemeral, srcTab, nColumn); + 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, addrL); + sqlite3VdbeJumpHere(v, addrL); + sqlite3ReleaseTempReg(pParse, regRec); + sqlite3ReleaseTempReg(pParse, regTempRowid); + } + }else{ + /* This is the case if the data for the INSERT is coming from a VALUES + ** clause + */ + NameContext sNC; + memset(&sNC, 0, sizeof(sNC)); + sNC.pParse = pParse; + srcTab = -1; + assert( useTempTable==0 ); + nColumn = pList ? pList->nExpr : 0; + for(i=0; ia[i].pExpr) ){ + 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. @@ -93602,6 +94156,25 @@ SQLITE_PRIVATE void sqlite3Insert( 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. + */ + if( IsVirtual(pTab) ){ + for(i=0; inCol; i++){ + nHidden += (IsHiddenColumn(&pTab->aCol[i]) ? 1 : 0); + } + } + if( pColumn==0 && nColumn && nColumn!=(pTab->nCol-nHidden) ){ + sqlite3ErrorMsg(pParse, + "table %S has %d columns but %d values were supplied", + pTabList, 0, pTab->nCol-nHidden, nColumn); + goto insert_cleanup; + } + if( pColumn!=0 && nColumn!=pColumn->nId ){ + sqlite3ErrorMsg(pParse, "%d values for %d columns", nColumn, pColumn->nId); + goto insert_cleanup; + } /* Initialize the count of rows to be inserted */ @@ -93629,39 +94202,27 @@ 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
** 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
from R..R+n ** goto C ** D: ... */ - addrCont = sqlite3VdbeAddOp1(v, OP_Yield, dest.iSDParm); - addrInsTop = sqlite3VdbeAddOp1(v, OP_If, regEof); + addrInsTop = addrCont = sqlite3VdbeAddOp1(v, OP_Yield, dest.iSDParm); + VdbeCoverage(v); } - /* 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; - /* Run the BEFORE and INSTEAD OF triggers, if there are any */ endOfLoop = sqlite3VdbeMakeLabel(v); @@ -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->nTabnTab = 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; jnCol; 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); @@ -99565,6 +100152,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 @@ -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; ia[i].pExpr); if( iiLimit ){ - 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 ApPrior = 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; inAccumulator; 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; inCol; 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 && iColnSampleCol ); 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( inSample ); - 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; jeOperator & 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; ju.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; ia[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; iinExpr; 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; inLevel; 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(; kp1!=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 646d122d00..357b905508 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 php_int_t php_count_recursive(zval *array, php_int_t mode TSRMLS_DC) /* {{{ */ +PHPAPI php_int_t php_count_recursive(zval *array, php_int_t mode TSRMLS_DC) /* {{{ */ { php_int_t 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_INT(mode_zv, mode); + zend_call_method_with_1_params(&array, NULL, NULL, "count", &retval, mode_zv); if (retval) { convert_to_int_ex(&retval); RETVAL_INT(Z_IVAL_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 9f921f06a7..ec86c3ccd9 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/image.c b/ext/standard/image.c index 004c0db13f..4c6ed3a9e8 100644 --- a/ext/standard/image.c +++ b/ext/standard/image.c @@ -30,6 +30,9 @@ #include #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 2f470c255a..7911a36a00 100644 --- a/ext/standard/php_array.h +++ b/ext/standard/php_array.h @@ -107,6 +107,7 @@ PHPAPI void php_splice(HashTable *ht, php_uint_t offset, php_size_t 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/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-- + +--EXPECTF-- +bool(true) +int(1) 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 Binary files /dev/null and b/ext/standard/tests/image/test-1pix.bmp differ diff --git a/ext/standard/tests/url/bug64604.phpt b/ext/standard/tests/url/bug64604.phpt new file mode 100644 index 0000000000..bbc3cb9d1b --- /dev/null +++ b/ext/standard/tests/url/bug64604.phpt @@ -0,0 +1,40 @@ +--TEST-- +Bug #64604 parse_url is inconsistent with specified port +--FILE-- + +--EXPECT-- +array(2) { + ["host"]=> + string(9) "localhost" + ["path"]=> + string(5) "/path" +} +array(3) { + ["host"]=> + string(9) "localhost" + ["port"]=> + int(80) + ["path"]=> + string(5) "/path" +} +array(2) { + ["host"]=> + string(9) "localhost" + ["path"]=> + string(5) "/path" +} +array(4) { + ["scheme"]=> + string(4) "http" + ["host"]=> + string(9) "localhost" + ["port"]=> + int(80) + ["path"]=> + string(5) "/path" +} diff --git a/ext/standard/url.c b/ext/standard/url.c index 7c027b7af9..f482d33ee6 100644 --- a/ext/standard/url.c +++ b/ext/standard/url.c @@ -181,6 +181,10 @@ PHPAPI php_url *php_url_parse_ex(char const *str, php_size_t length) p = e + 1; pp = p; + if (*s == '/' && *(s+1) == '/') { /* relative-scheme URL */ + s += 2; + } + while (pp-p < 6 && isdigit(*pp)) { pp++; } @@ -201,10 +205,6 @@ PHPAPI php_url *php_url_parse_ex(char const *str, php_size_t length) STR_FREE(ret->scheme); efree(ret); return NULL; - } else if (*s == '/' && *(s+1) == '/') { /* relative-scheme URL */ - s += 2; - } else { - goto just_path; } } else if (*s == '/' && *(s+1) == '/') { /* relative-scheme URL */ s += 2; diff --git a/ext/xmlrpc/xmlrpc-epi-php.c b/ext/xmlrpc/xmlrpc-epi-php.c index be0ca04789..d1cde25754 100644 --- a/ext/xmlrpc/xmlrpc-epi-php.c +++ b/ext/xmlrpc/xmlrpc-epi-php.c @@ -1432,7 +1432,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 b984bfd4de..cbaa25ee9e 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_variables.c b/main/php_variables.c index 8e2671d356..5ca52d122b 100644 --- a/main/php_variables.c +++ b/main/php_variables.c @@ -590,7 +590,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); 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 a94f599998..d981cae701 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); } } @@ -669,7 +669,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 14455ec7d6..2f376ccc11 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/fpm/php-fpm.conf.in b/sapi/fpm/php-fpm.conf.in index ab03736beb..acdff31f17 100644 --- a/sapi/fpm/php-fpm.conf.in +++ b/sapi/fpm/php-fpm.conf.in @@ -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/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-- +===DONE=== --EXPECTF-- - -Fatal error: Arrays are not allowed in class constants in %s on line 4 +===DONE===