├── EXPERIMENTAL ├── CREDITS ├── config.w32 ├── .gitignore ├── tests ├── README ├── mymemc_phpinfo.phpt ├── mymemc_result_num_rows.phpt ├── mymemc_result_field_count.phpt ├── mymemc_constants.phpt ├── mymemc_memcache_no_host.phpt ├── mymemc_reflection_functions.phpt ├── mymemc_verify_mapping.phpt ├── skipif.inc ├── mymemc_result_seek.phpt ├── mymemc_mysqlnd_memcache_get_config_params.phpt ├── mymemc_fetch_often.phpt ├── mymemc_match_sql_patterns.phpt ├── mymemc_column_subset.phpt ├── mymemc_reflection_extension.phpt ├── mymemc_query_async.phpt ├── mymemc_subselect.phpt ├── mymemc_select_sql_no_cache.phpt ├── mymemc_api_basics_mysqli.phpt ├── mymemc_mysqli_fetch_assoc.phpt ├── mymemc_mysqli_fetch_object.phpt ├── mymemc_column_superset.phpt ├── mymemc_mysqli_fetch_array.phpt ├── mymemc_null_values.phpt ├── mymemc_empty_string.phpt ├── mymemc_api_basics_pdo.phpt ├── mymemc_query_unbuffered.phpt ├── mymemc_update_and_fetch.phpt ├── mymemc_resultmeta_lengths.phpt ├── mymemc_server_resultmeta.phpt ├── mymemc_insert_and_fetch.phpt ├── mymemc_query_multi.phpt ├── mymemc_resultmeta_multiple.phpt ├── mymemc_query_multi_unbuffered.phpt ├── mymemc_mysqlnd_memcache_set_params.phpt ├── mymemc_resultmeta_multiple_plugin_disabled.phpt ├── mymemc_update_and_fetch_sql.phpt ├── mymemc_insert_and_fetch_sql.phpt ├── mymemc_column_alias.phpt ├── mymemc_query_multi_multi.phpt ├── util.inc ├── mymemc_column_order.phpt ├── mymemc_seperator_clash_fetch_assoc.phpt ├── mymemc_api_basics_mysql.phpt ├── mymemc_resultmeta_direct_access.phpt ├── mymemc_mysqlnd_memcache_get_config.phpt ├── mymemc_resultmeta_same_table.phpt ├── mymemc_unset_config.phpt ├── mymemc_resultmeta.phpt ├── mymemc_seperator_clash.phpt ├── mymemc_memcache_set_debug_cb.phpt ├── connect.inc └── table.inc ├── php_mysqlnd_memcache.h ├── config.m4 ├── LICENSE ├── package.xml └── mysqlnd_memcache.c /EXPERIMENTAL: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /CREDITS: -------------------------------------------------------------------------------- 1 | mysqlnd_memcache 2 | Johannes Schlüter 3 | -------------------------------------------------------------------------------- /config.w32: -------------------------------------------------------------------------------- 1 | // $Id$ 2 | // vim:ft=javascript 3 | 4 | // If your extension references something external, use ARG_WITH 5 | // ARG_WITH("mysqlnd_memcache", "for mysqlnd_memcache support", "no"); 6 | 7 | // Otherwise, use ARG_ENABLE 8 | // ARG_ENABLE("mysqlnd_memcache", "enable mysqlnd_memcache support", "no"); 9 | 10 | if (PHP_MYSQLND_MEMCACHE != "no") { 11 | EXTENSION("mysqlnd_memcache", "mysqlnd_memcache.c"); 12 | } 13 | 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .libs 2 | Makefile 3 | Makefile.fragments 4 | Makefile.global 5 | Makefile.objects 6 | acinclude.m4 7 | aclocal.m4 8 | autom4te.cache 9 | build 10 | config.guess 11 | config.h 12 | config.h.in 13 | config.log 14 | config.nice 15 | config.status 16 | config.sub 17 | configure 18 | configure.in 19 | install-sh 20 | libtool 21 | ltmain.sh 22 | missing 23 | mkinstalldirs 24 | modules 25 | run-tests.php 26 | .deps 27 | *.lo 28 | *.la 29 | tests/*.diff 30 | tests/*.exp 31 | tests/*.log 32 | tests/*.out 33 | tests/*.php 34 | tests/*.sh 35 | -------------------------------------------------------------------------------- /tests/README: -------------------------------------------------------------------------------- 1 | These test are based on the same enironment variables 2 | like other MySQL tests. There two new optional settings: 3 | MYSQL_TEST_MEMCACHE_HOST and MYSQL_TEST_MEMCACHE_PORT 4 | to set memcache configuration. By default memcache will connect 5 | to the MySQL host using port 11211. 6 | 7 | These tests also override gloal configuration tables in the 8 | innodb_memcache databse. If you need your data their make a 9 | backup. To confirm you're aware of this set 10 | MYSQLND_MEMCACHE_I_KNOW_WHAT_I_DO 11 | in your enironment before running tests. 12 | -------------------------------------------------------------------------------- /tests/mymemc_phpinfo.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | phpinfo() section 3 | --SKIPIF-- 4 | 7 | --INI-- 8 | mysqlnd_memcache.enable=1 9 | --FILE-- 10 | 27 | --EXPECTF-- 28 | done! -------------------------------------------------------------------------------- /tests/mymemc_result_num_rows.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Seek on result set 3 | --SKIPIF-- 4 | 15 | --INI-- 16 | mysqlnd_memcache.enable=1 17 | --FILE-- 18 | connect_errno) { 22 | printf("[001] [%d] %s\n", $link->connect_errno, $link->connect_error); 23 | } 24 | 25 | $memc = my_memcache_connect($memcache_host, $memcache_port); 26 | if (!mysqlnd_memcache_set($link, $memc)) { 27 | printf("[002] Failed to register connection, [%d] '%s'\n", 28 | $link->errno, $link->error); 29 | } 30 | 31 | if (!($res = $link->query("SELECT f1, f2, f3 FROM mymem_test WHERE id = 'key1'"))) { 32 | printf("[003] [%d] '%s'\n", $link->errno, $link->error); 33 | } 34 | 35 | var_dump($res->num_rows); 36 | 37 | print "done!"; 38 | ?> 39 | --EXPECT-- 40 | int(1) 41 | done! 42 | -------------------------------------------------------------------------------- /tests/mymemc_result_field_count.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Result field count 3 | --SKIPIF-- 4 | 15 | --INI-- 16 | mysqlnd_memcache.enable=1 17 | --FILE-- 18 | connect_errno) { 22 | printf("[001] [%d] %s\n", $link->connect_errno, $link->connect_error); 23 | } 24 | 25 | $memc = my_memcache_connect($memcache_host, $memcache_port); 26 | if (!mysqlnd_memcache_set($link, $memc)) { 27 | printf("[002] Failed to register connection, [%d] '%s'\n", 28 | $link->errno, $link->error); 29 | } 30 | 31 | if (!($res = $link->query("SELECT f1, f2, f3 FROM mymem_test WHERE id = 'key1'"))) { 32 | printf("[003] [%d] '%s'\n", $link->errno, $link->error); 33 | } 34 | 35 | var_dump($res->field_count); 36 | 37 | print "done!"; 38 | ?> 39 | --EXPECT-- 40 | int(3) 41 | done! 42 | -------------------------------------------------------------------------------- /tests/mymemc_constants.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Constants 3 | --SKIPIF-- 4 | 7 | --INI-- 8 | mysqlnd_memcache.enable=1 9 | --FILE-- 10 | true, 13 | "MYSQLND_MEMCACHE_VERSION" => true, 14 | "MYSQLND_MEMCACHE_VERSION_ID" => true, 15 | ); 16 | 17 | 18 | $constants = get_defined_constants(true); 19 | $constants = (isset($constants['mysqlnd_memcache'])) ? $constants['mysqlnd_memcache'] : array(); 20 | ksort($constants); 21 | foreach ($constants as $name => $value) { 22 | if (!isset($expected[$name])) { 23 | printf("[001] Unexpected constants: %s/%s\n", $name, $value); 24 | } else { 25 | if ($expected[$name]) 26 | printf("%s = '%s'\n", $name, $value); 27 | unset($expected[$name]); 28 | } 29 | } 30 | if (!empty($expected)) { 31 | printf("[002] Dumping list of missing constants\n"); 32 | var_dump($expected); 33 | } 34 | 35 | print "done!"; 36 | ?> 37 | --EXPECTF-- 38 | MYSQLND_MEMCACHE_DEFAULT_REGEXP = '/^\s*SELECT\s*(.+?)\s*FROM\s*`?([a-z0-9_]+)`?\s*WHERE\s*`?([a-z0-9_]+)`?\s*=\s*(?(?=["'])["']([^"']*)["']|([0-9e\.]*))\s*$/is' 39 | MYSQLND_MEMCACHE_VERSION = '%s' 40 | MYSQLND_MEMCACHE_VERSION_ID = '%d' 41 | done! 42 | -------------------------------------------------------------------------------- /tests/mymemc_memcache_no_host.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Don't connect to memcached host 3 | --SKIPIF-- 4 | 14 | --INI-- 15 | mysqlnd_memcache.enable=1 16 | --FILE-- 17 | connect_errno) { 22 | printf("[001] [%d] %s\n", $link->connect_errno, $link->connect_error); 23 | } 24 | 25 | /* Unconnected memcache */ 26 | $memc = new memcached(); 27 | if (!mysqlnd_memcache_set($link, $memc)) { 28 | printf("[002] Failed to register connection\n"); 29 | } 30 | 31 | /* Query is value but no memcache connection */ 32 | if ($res = $link->query("SELECT f1 FROM mymem_test WHERE id = 1")) { 33 | while ($row = $res->fetch_assoc()) { 34 | var_dump($row); 35 | } 36 | } else { 37 | printf("[003] %s, [%d] '%s'\n", var_export($res, true), $link->errno, $link->error); 38 | } 39 | 40 | print "done!"; 41 | ?> 42 | --EXPECTF-- 43 | Warning: mysqli::query(): libmemcached error %s in %s on line %d 44 | [003] false, [0] '' 45 | done! -------------------------------------------------------------------------------- /tests/mymemc_reflection_functions.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ReflectionFunction to check API 3 | --SKIPIF-- 4 | 7 | --INI-- 8 | mysqlnd_memcache.enable=1 9 | --FILE-- 10 | getFunctions(); 16 | asort($functions); 17 | printf("Functions:\n"); 18 | foreach ($functions as $func) { 19 | if (isset($ignore[$func->name])) 20 | continue; 21 | 22 | printf(" %s\n", $func->name); 23 | $rf = new ReflectionFunction($func->name); 24 | printf(" Deprecated: %s\n", $rf->isDeprecated() ? "yes" : "no"); 25 | printf(" Accepted parameters: %d\n", $rf->getNumberOfParameters()); 26 | printf(" Required parameters: %d\n", $rf->getNumberOfRequiredParameters()); 27 | foreach( $rf->getParameters() as $param ) { 28 | printf(" %s\n", $param); 29 | } 30 | } 31 | 32 | print "done!"; 33 | ?> 34 | --EXPECT-- 35 | Functions: 36 | mysqlnd_memcache_get_config 37 | Deprecated: no 38 | Accepted parameters: 1 39 | Required parameters: 1 40 | Parameter #0 [ $mysql_connection ] 41 | mysqlnd_memcache_set 42 | Deprecated: no 43 | Accepted parameters: 4 44 | Required parameters: 2 45 | Parameter #0 [ $mysql_connection ] 46 | Parameter #1 [ Memcached or NULL $memcached_connection ] 47 | Parameter #2 [ $select_pattern ] 48 | Parameter #3 [ $debug_callback ] 49 | done! -------------------------------------------------------------------------------- /tests/mymemc_verify_mapping.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Verify mapping 3 | --SKIPIF-- 4 | 15 | --INI-- 16 | mysqlnd_memcache.enable=1 17 | --FILE-- 18 | connect_errno) { 23 | printf("[001] [%d] %s\n", $link->connect_errno, $link->connect_error); 24 | } 25 | 26 | $memc = my_memcache_connect($memcache_host, $memcache_port); 27 | 28 | if (!mysqlnd_memcache_set($link, $memc)) { 29 | printf("[002] Failed to register connection, [%d] '%s'\n", 30 | $link->errno, $link->error); 31 | } 32 | 33 | var_dump(mysqlnd_memcache_get_config($link)["mappings"]); 34 | 35 | echo "done!"; 36 | ?> 37 | --EXPECTF-- 38 | array(1) { 39 | ["mymem_test"]=> 40 | array(6) { 41 | ["prefix"]=> 42 | string(0) "@@mymem_test." 43 | ["schema_name"]=> 44 | string(4) "%s" 45 | ["table_name"]=> 46 | string(10) "mymem_test" 47 | ["id_field_name"]=> 48 | string(2) "id" 49 | ["separator"]=> 50 | string(1) "|" 51 | ["fields"]=> 52 | array(3) { 53 | [0]=> 54 | string(2) "f1" 55 | [1]=> 56 | string(2) "f2" 57 | [2]=> 58 | string(2) "f3" 59 | } 60 | } 61 | } 62 | done! 63 | -------------------------------------------------------------------------------- /tests/skipif.inc: -------------------------------------------------------------------------------- 1 | $extension) { 9 | if (!extension_loaded($extension)) 10 | die(sprintf("SKIP ext/%s not available", $extension)); 11 | } 12 | 13 | } 14 | 15 | function _skipif_connect($host, $user, $passwd, $db, $port, $socket) { 16 | if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) 17 | die(sprintf("SKIP (h=%s,u=%s,p=%s,db=%s,P=%s,S=%s), [%d] %s\n", 18 | $host, $user, ($passwd) ? "***" : '', $db, $port, $socket, mysqli_connect_errno(), mysqli_connect_error())); 19 | } 20 | 21 | function _skipif_no_plugin($host, $user, $passwd, $db, $port, $socket) { 22 | if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) 23 | die(sprintf("SKIP (h=%s,u=%s,p=%s,db=%s,P=%s,S=%s), [%d] %s\n", 24 | $host, $user, ($passwd) ? "***" : '', $db, $port, $socket, mysqli_connect_errno(), mysqli_connect_error())); 25 | 26 | if (!($res = $link->query("SHOW PLUGINS"))) { 27 | die(sprintf("SKIP SHOW PLUGINS failed, [%d] %s\n", $link->errno, $link->error)); 28 | } 29 | 30 | $found = false; 31 | $plugins = array("daemon_memcached" => true); 32 | while ($row = $res->fetch_assoc()) { 33 | if (isset($plugins[$row['Name']])) { 34 | $found = true; 35 | break; 36 | } 37 | } 38 | 39 | if (!$found) 40 | die("SKIP No memcache server plugin installed"); 41 | } 42 | 43 | 44 | 45 | _skipif_check_extensions("memcached"); 46 | _skipif_check_extensions("mysqlnd_memcache"); 47 | 48 | -------------------------------------------------------------------------------- /tests/mymemc_result_seek.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Seek on result set 3 | --SKIPIF-- 4 | 15 | --INI-- 16 | mysqlnd_memcache.enable=1 17 | --FILE-- 18 | connect_errno) { 22 | printf("[001] [%d] %s\n", $link->connect_errno, $link->connect_error); 23 | } 24 | 25 | $memc = my_memcache_connect($memcache_host, $memcache_port); 26 | if (!mysqlnd_memcache_set($link, $memc)) { 27 | printf("[002] Failed to register connection, [%d] '%s'\n", 28 | $link->errno, $link->error); 29 | } 30 | 31 | if (!($res = $link->query("SELECT f1, f2, f3 FROM mymem_test WHERE id = 'key1'"))) { 32 | printf("[003] [%d] '%s'\n", $link->errno, $link->error); 33 | } 34 | 35 | var_dump($res->data_seek(-1)); 36 | var_dump(($row = $res->fetch_row())); 37 | var_dump($res->data_seek(1)); 38 | var_dump($res->fetch_row()); 39 | var_dump($res->data_seek(0)); 40 | var_dump($res->fetch_row()); 41 | 42 | print "done!"; 43 | ?> 44 | --EXPECT-- 45 | bool(false) 46 | array(3) { 47 | [0]=> 48 | string(1) "a" 49 | [1]=> 50 | string(1) "b" 51 | [2]=> 52 | string(1) "c" 53 | } 54 | bool(false) 55 | NULL 56 | bool(true) 57 | array(3) { 58 | [0]=> 59 | string(1) "a" 60 | [1]=> 61 | string(1) "b" 62 | [2]=> 63 | string(1) "c" 64 | } 65 | done! 66 | -------------------------------------------------------------------------------- /tests/mymemc_mysqlnd_memcache_get_config_params.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | mysqlnd_memcache_get_config() - parameter 3 | --SKIPIF-- 4 | 8 | --INI-- 9 | mysqlnd_memcache.enable=1 10 | --FILE-- 11 | kill($link->thread_id); 33 | var_dump(mysqlnd_memcache_get_config($link)); 34 | 35 | print "done!"; 36 | ?> 37 | --EXPECTF-- 38 | Warning: mysqlnd_memcache_get_config(): Passed variable is no mysqlnd-based MySQL connection in %s on line %d 39 | bool(false) 40 | 41 | Warning: mysqlnd_memcache_get_config(): Passed variable is no mysqlnd-based MySQL connection in %s on line %d 42 | bool(false) 43 | 44 | Warning: mysqlnd_memcache_get_config(): Passed variable is no mysqlnd-based MySQL connection in %s on line %d 45 | bool(false) 46 | 47 | Warning: mysqlnd_memcache_get_config(): Passed variable is no mysqlnd-based MySQL connection in %s on line %d 48 | bool(false) 49 | bool(false) 50 | bool(false) 51 | done! -------------------------------------------------------------------------------- /php_mysqlnd_memcache.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PECL mysqlnd_memcache | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 2012-2013 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: Johannes Schlüter | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | #ifndef PHP_MYSQLND_MEMCACHE_H 20 | #define PHP_MYSQLND_MEMCACHE_H 21 | 22 | extern zend_module_entry mysqlnd_memcache_module_entry; 23 | #define phpext_mysqlnd_memcache_ptr &mysqlnd_memcache_module_entry 24 | 25 | #define PHP_MYSQLND_MEMCACHE_VERSION "1.0.1" 26 | #define MYSQLND_MEMCACHE_VERSION_ID 10001 27 | 28 | #endif /* PHP_MYSQLND_MEMCACHE_H */ 29 | 30 | 31 | /* 32 | * Local variables: 33 | * tab-width: 4 34 | * c-basic-offset: 4 35 | * End: 36 | * vim600: noet sw=4 ts=4 fdm=marker 37 | * vim<600: noet sw=4 ts=4 38 | */ 39 | -------------------------------------------------------------------------------- /config.m4: -------------------------------------------------------------------------------- 1 | dnl $Id$ 2 | dnl config.m4 for extension mysqlnd_memcache 3 | 4 | PHP_ARG_ENABLE(mysqlnd_memcache, whether to enable mysqlnd_memcache support, 5 | [ --enable-mysqlnd_memcache Enable mysqlnd_memcache support]) 6 | 7 | PHP_ARG_WITH(libmemcached-dir, for libmemcached, 8 | [ --with-libmemcached-dir[=DIR] Set the path to libmemcached install prefix.], yes) 9 | 10 | if test "$PHP_MYSQLND_MEMCACHE" != "no"; then 11 | 12 | if test "$PHP_LIBMEMCACHED_DIR" != "no" && test "$PHP_LIBMEMCACHED_DIR" != "yes"; then 13 | if test -r "$PHP_LIBMEMCACHED_DIR/include/libmemcached/memcached.h"; then 14 | PHP_LIBMEMCACHED_DIR="$PHP_LIBMEMCACHED_DIR" 15 | else 16 | AC_MSG_ERROR([Can't find libmemcached headers under "$PHP_LIBMEMCACHED_DIR"]) 17 | fi 18 | else 19 | PHP_LIBMEMCACHED_DIR="no" 20 | for i in /usr /usr/local; do 21 | if test -r "$i/include/libmemcached/memcached.h"; then 22 | PHP_LIBMEMCACHED_DIR=$i 23 | break 24 | fi 25 | done 26 | fi 27 | 28 | AC_MSG_CHECKING([for libmemcached location]) 29 | if test "$PHP_LIBMEMCACHED_DIR" = "no"; then 30 | AC_MSG_ERROR([mysqlnd_memcached support requires libmemcached. Use --with-libmemcached-dir= to specify the prefix where libmemcached headers and library are located]) 31 | else 32 | AC_MSG_RESULT([$PHP_LIBMEMCACHED_DIR]) 33 | PHP_LIBMEMCACHED_INCDIR="$PHP_LIBMEMCACHED_DIR/include" 34 | PHP_ADD_INCLUDE($PHP_LIBMEMCACHED_INCDIR) 35 | 36 | PHP_ADD_LIBRARY_WITH_PATH(memcached, $PHP_LIBMEMCACHED_DIR/$PHP_LIBDIR, MEMCACHED_SHARED_LIBADD) 37 | fi 38 | 39 | PHP_NEW_EXTENSION(mysqlnd_memcache, mysqlnd_memcache.c, $ext_shared) 40 | 41 | PHP_ADD_EXTENSION_DEP(mysqlnd_memcache, mysqlnd) 42 | PHP_ADD_EXTENSION_DEP(mysqlnd_memcache, memcached) 43 | PHP_ADD_EXTENSION_DEP(mysqlnd_memcache, pcre) 44 | fi 45 | -------------------------------------------------------------------------------- /tests/mymemc_fetch_often.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Fetch rows often 3 | --SKIPIF-- 4 | 15 | --INI-- 16 | mysqlnd_memcache.enable=1 17 | --FILE-- 18 | connect_errno) { 22 | printf("[001] [%d] %s\n", $link->connect_errno, $link->connect_error); 23 | } 24 | 25 | $memc = my_memcache_connect($memcache_host, $memcache_port); 26 | if (!mysqlnd_memcache_set($link, $memc)) { 27 | printf("[002] Failed to register connection, [%d] '%s'\n", 28 | $link->errno, $link->error); 29 | } 30 | 31 | $rows = array(); 32 | if (!($res = $link->query("SELECT id, f1, f2, f3 FROM mymem_test"))) { 33 | printf("[003] [%d] %s\n", $link->errno, $link->error); 34 | } 35 | while ($row = $res->fetch_assoc()) { 36 | $rows[$row['id']] = array("f1" => $row['f1'], "f2" => $row['f2'], "f3" => $row['f3']); 37 | } 38 | 39 | for ($i = 1; $i <= 1000; $i++) { 40 | $key = sprintf("key%d", mt_rand(1, 3)); 41 | $sql = sprintf("SELECT %s f1, f2, f3 FROM mymem_test WHERE id = '%s'", 42 | ((mt_rand(0, 100) < 10) ? "SQL_NO_CACHE" : ""), 43 | $key 44 | ); 45 | if (!($res = $link->query($sql))) { 46 | break; 47 | } 48 | 49 | $row = $res->fetch_assoc(); 50 | if ($rows[$key] != $row) { 51 | printf("[004] Wrong results, dumping\n"); 52 | var_dump($rows[$key]); 53 | var_dump($row); 54 | break; 55 | } 56 | } 57 | 58 | printf("[005] [%d] '%s'\n", $link->errno, $link->error); 59 | print "done!"; 60 | ?> 61 | --EXPECT-- 62 | [005] [0] '' 63 | done! -------------------------------------------------------------------------------- /tests/mymemc_match_sql_patterns.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | MySQL Query pattern matching 3 | --SKIPIF-- 4 | 7 | --INI-- 8 | mysqlnd_memcache.enable=1 9 | --FILE-- 10 | $v) { 25 | printf(" %02d: '%s'\n", $k, $v); 26 | } 27 | } else { 28 | printf("'%s' - no match\n", $sql); 29 | } 30 | } 31 | 32 | print "done!"; 33 | ?> 34 | --EXPECT-- 35 | Default regexp: /^\s*SELECT\s*(.+?)\s*FROM\s*`?([a-z0-9_]+)`?\s*WHERE\s*`?([a-z0-9_]+)`?\s*=\s*(?(?=["'])["']([^"']*)["']|([0-9e\.]*))\s*$/is 36 | 'SELECT column FROM table WHERE key = 1' - match 37 | 00: 'SELECT column FROM table WHERE key = 1' 38 | 01: 'column' 39 | 02: 'table' 40 | 03: 'key' 41 | 04: '' 42 | 05: '1' 43 | 'SELECT column FROM table WHERE key = 'v'' - match 44 | 00: 'SELECT column FROM table WHERE key = 'v'' 45 | 01: 'column' 46 | 02: 'table' 47 | 03: 'key' 48 | 04: 'v' 49 | 'SELECT column FROM table WHERE key = "v"' - match 50 | 00: 'SELECT column FROM table WHERE key = "v"' 51 | 01: 'column' 52 | 02: 'table' 53 | 03: 'key' 54 | 04: 'v' 55 | 'SELECT column1, column2, column3 FROM table WHERE id = 1' - match 56 | 00: 'SELECT column1, column2, column3 FROM table WHERE id = 1' 57 | 01: 'column1, column2, column3' 58 | 02: 'table' 59 | 03: 'id' 60 | 04: '' 61 | 05: '1' 62 | 'SELECT column1, column2, column3 FROM table WHERE id = 1 AND foo = 2' - no match 63 | done! -------------------------------------------------------------------------------- /tests/mymemc_column_subset.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Column subset 3 | --SKIPIF-- 4 | 15 | --INI-- 16 | mysqlnd_memcache.enable=1 17 | --FILE-- 18 | $arg) { 25 | printf(" %02d: %s / %s\n", $k, gettype($arg), var_export($arg, true)); 26 | } 27 | } 28 | 29 | $link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket); 30 | if ($link->connect_errno) { 31 | printf("[001] [%d] %s\n", $link->connect_errno, $link->connect_error); 32 | } 33 | 34 | $memc = my_memcache_connect($memcache_host, $memcache_port); 35 | 36 | if (!mysqlnd_memcache_set($link, $memc, NULL, "debug_callback")) { 37 | printf("[002] Failed to register connection, [%d] '%s'\n", 38 | $link->errno, $link->error); 39 | } 40 | 41 | if (!($key1 = $memc->get("@@mymem_test.key1"))) { 42 | printf("[003] Failed to fetch 'key1' using native Memcache API.\n"); 43 | } 44 | $columns = explode("|", $key1); 45 | var_dump($columns); 46 | 47 | 48 | if ($res = $link->query("SELECT f1 FROM mymem_test WHERE id = 'key1'")) { 49 | var_dump($res->fetch_row()); 50 | var_dump($res->fetch_assoc()); 51 | 52 | } else { 53 | printf("[004] %d %s\n", $link->errno, $link->error); 54 | } 55 | 56 | print "done!"; 57 | ?> 58 | --EXPECT-- 59 | array(3) { 60 | [0]=> 61 | string(1) "a" 62 | [1]=> 63 | string(1) "b" 64 | [2]=> 65 | string(1) "c" 66 | } 67 | debug_callback() 00: boolean / false 68 | array(1) { 69 | [0]=> 70 | string(1) "a" 71 | } 72 | NULL 73 | done! 74 | -------------------------------------------------------------------------------- /tests/mymemc_reflection_extension.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Extension interface 3 | --SKIPIF-- 4 | 7 | --INI-- 8 | mysqlnd_memcache.enable=1 9 | --FILE-- 10 | name); 14 | 15 | /* TODO: version constants MYSQLND_MEMCACHE_VERSION, ... */ 16 | printf("Version: %s\n", $r->getVersion()); 17 | 18 | $classes = $r->getClasses(); 19 | if (!empty($classes)) { 20 | printf("[001] Expecting no class\n"); 21 | asort($classes); 22 | var_dump($classes); 23 | } 24 | 25 | $expected = array( 26 | 'memcached' => true, 27 | 'pcre' => true, 28 | 'mysqlnd' => true, 29 | ); 30 | 31 | 32 | $dependencies = $r->getDependencies(); 33 | asort($dependencies); 34 | printf("Dependencies:\n"); 35 | foreach ($dependencies as $what => $how) { 36 | printf(" %s - %s\n", $what, $how); 37 | if (isset($expected[$what])) { 38 | unset($expected[$what]); 39 | } else { 40 | printf("Unexpected extension dependency with %s - %s\n", $what, $how); 41 | } 42 | } 43 | if (!empty($expected)) { 44 | printf("Dumping list of missing extension dependencies\n"); 45 | var_dump($expected); 46 | } 47 | printf("\n"); 48 | 49 | $ignore = array(); 50 | 51 | $functions = $r->getFunctions(); 52 | asort($functions); 53 | printf("Functions:\n"); 54 | foreach ($functions as $func) { 55 | if (isset($ignore[$func->name])) { 56 | unset($ignore[$func->name]); 57 | } else { 58 | printf(" %s\n", $func->name); 59 | } 60 | } 61 | if (!empty($ignore)) { 62 | printf("Dumping version dependent and missing functions\n"); 63 | var_dump($ignore); 64 | } 65 | 66 | print "done!"; 67 | ?> 68 | --EXPECTF-- 69 | Name: mysqlnd_memcache 70 | Version: %s 71 | Dependencies: 72 | memcached - Required 73 | pcre - Required 74 | mysqlnd - Required 75 | 76 | Functions: 77 | mysqlnd_memcache_get_config 78 | mysqlnd_memcache_set 79 | done! 80 | -------------------------------------------------------------------------------- /tests/mymemc_query_async.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Async query 3 | --SKIPIF-- 4 | 15 | --INI-- 16 | mysqlnd_memcache.enable=1 17 | --FILE-- 18 | $arg) { 25 | printf(" %02d: %s / %s\n", $k, gettype($arg), var_export($arg, true)); 26 | } 27 | } 28 | 29 | $link1 = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket); 30 | if ($link1->connect_errno) { 31 | printf("[001] [%d] %s\n", $link1->connect_errno, $link1->connect_error); 32 | } 33 | 34 | $link2 = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket); 35 | if ($link2->connect_errno) { 36 | printf("[001] [%d] %s\n", $link2->connect_errno, $link2->connect_error); 37 | } 38 | 39 | 40 | $link1->query("SELECT f1, f2, f3 FROM mymem_test WHERE id = 'key1'", MYSQLI_ASYNC); 41 | $link2->query("SELECT f1, f2, f3 FROM mymem_test WHERE id = 'key1'", MYSQLI_ASYNC); 42 | 43 | $all_links = array($link1, $link2); 44 | $processed = 0; 45 | do { 46 | $links = $errors = $reject = array(); 47 | foreach ($all_links as $link) { 48 | $links[] = $errors[] = $reject[] = $link; 49 | } 50 | if (!mysqli_poll($handles, $errors, $reject, 1)) { 51 | continue; 52 | } 53 | foreach ($links as $link) { 54 | if ($res = $link->reap_async_query()) { 55 | while ($row = $res->fetch_assoc()) { 56 | var_dump($row); 57 | } 58 | $processed++; 59 | } 60 | } 61 | } while ($processed < count($all_links)); 62 | 63 | print "done!"; 64 | ?> 65 | --XFAIL-- 66 | Crash 67 | --EXPECT-- 68 | Anything but a crash 69 | -------------------------------------------------------------------------------- /tests/mymemc_subselect.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Subselect 3 | --SKIPIF-- 4 | 15 | --INI-- 16 | mysqlnd_memcache.enable=1 17 | --FILE-- 18 | $arg) { 25 | printf(" %02d: %s / %s\n", $k, gettype($arg), var_export($arg, true)); 26 | } 27 | } 28 | 29 | $link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket); 30 | if ($link->connect_errno) { 31 | printf("[001] [%d] %s\n", $link->connect_errno, $link->connect_error); 32 | } 33 | 34 | $memc = my_memcache_connect($memcache_host, $memcache_port); 35 | 36 | if (!mysqlnd_memcache_set($link, $memc, NULL, "debug_callback")) { 37 | printf("[002] Failed to register connection, [%d] '%s'\n", 38 | $link->errno, $link->error); 39 | } 40 | 41 | if (!($key1 = $memc->get("@@mymem_test.key1"))) { 42 | printf("[003] Failed to fetch 'key1' using native Memcache API.\n"); 43 | } 44 | $columns = explode("|", $key1); 45 | var_dump($columns); 46 | 47 | 48 | if ($res = $link->query("SELECT f1, f2, f3 FROM mymem_test WHERE id = (SELECT 'key1' FROM DUAL)")) { 49 | var_dump($res->fetch_row()); 50 | var_dump($res->fetch_assoc()); 51 | 52 | } else { 53 | printf("[004] %d %s\n", $link->errno, $link->error); 54 | } 55 | 56 | 57 | print "done!"; 58 | ?> 59 | --EXPECT-- 60 | array(3) { 61 | [0]=> 62 | string(1) "a" 63 | [1]=> 64 | string(1) "b" 65 | [2]=> 66 | string(1) "c" 67 | } 68 | debug_callback() 00: boolean / false 69 | array(3) { 70 | [0]=> 71 | string(1) "a" 72 | [1]=> 73 | string(1) "b" 74 | [2]=> 75 | string(1) "c" 76 | } 77 | NULL 78 | done! 79 | -------------------------------------------------------------------------------- /tests/mymemc_select_sql_no_cache.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | SELECT SQL_NO_CACHE 3 | --SKIPIF-- 4 | 15 | --INI-- 16 | mysqlnd_memcache.enable=1 17 | --FILE-- 18 | $arg) { 25 | printf(" %02d: %s / %s\n", $k, gettype($arg), var_export($arg, true)); 26 | } 27 | } 28 | 29 | $link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket); 30 | if ($link->connect_errno) { 31 | printf("[001] [%d] %s\n", $link->connect_errno, $link->connect_error); 32 | } 33 | 34 | $memc = my_memcache_connect($memcache_host, $memcache_port); 35 | 36 | if (!mysqlnd_memcache_set($link, $memc, NULL, "debug_callback")) { 37 | printf("[002] Failed to register connection, [%d] '%s'\n", 38 | $link->errno, $link->error); 39 | } 40 | 41 | if (!($key1 = $memc->get("@@mymem_test.key1"))) { 42 | printf("[003] Failed to fetch 'key1' using native Memcache API.\n"); 43 | } 44 | $columns = explode("|", $key1); 45 | var_dump($columns); 46 | 47 | 48 | if ($res = $link->query("SELECT SQL_NO_CACHE f1, f2, f3 FROM mymem_test WHERE id = 'key1'")) { 49 | var_dump($res->fetch_row()); 50 | var_dump($res->fetch_assoc()); 51 | } else { 52 | printf("[004] %d %s\n", $link->errno, $link->error); 53 | } 54 | 55 | 56 | print "done!"; 57 | ?> 58 | --EXPECT-- 59 | array(3) { 60 | [0]=> 61 | string(1) "a" 62 | [1]=> 63 | string(1) "b" 64 | [2]=> 65 | string(1) "c" 66 | } 67 | debug_callback() 00: boolean / false 68 | array(3) { 69 | [0]=> 70 | string(1) "a" 71 | [1]=> 72 | string(1) "b" 73 | [2]=> 74 | string(1) "c" 75 | } 76 | NULL 77 | done! 78 | -------------------------------------------------------------------------------- /tests/mymemc_api_basics_mysqli.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Simple mysqli test 3 | --SKIPIF-- 4 | 15 | --INI-- 16 | mysqlnd_memcache.enable=1 17 | --FILE-- 18 | $arg) { 25 | printf(" %02d: %s / %s\n", $k, gettype($arg), var_export($arg, true)); 26 | } 27 | } 28 | 29 | $link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket); 30 | if ($link->connect_errno) { 31 | printf("[001] [%d] %s\n", $link->connect_errno, $link->connect_error); 32 | } 33 | 34 | $memc = my_memcache_connect($memcache_host, $memcache_port); 35 | if (!mysqlnd_memcache_set($link, $memc, NULL, "debug_callback")) { 36 | printf("[002] Failed to register connection, [%d] '%s'\n", 37 | $link->errno, $link->error); 38 | } 39 | 40 | if (!($key1 = $memc->get("@@mymem_test.key1"))) { 41 | printf("[003] Failed to fetch 'key1' using native Memcache API.\n"); 42 | } 43 | $columns = explode("|", $key1); 44 | var_dump($columns); 45 | 46 | 47 | if ($res = $link->query("SELECT f1, f2, f3 FROM mymem_test WHERE id = 'key1'")) { 48 | $row = $res->fetch_row(); 49 | 50 | if ($row != $columns) { 51 | printf("[005] Native Memcache and SQL results differ\n"); 52 | var_dump(array_diff($row, $columns)); 53 | } 54 | 55 | var_dump($res->fetch_assoc()); 56 | 57 | } else { 58 | printf("[004] %d %s\n", $link->errno, $link->error); 59 | } 60 | 61 | print "done!"; 62 | ?> 63 | --EXPECT-- 64 | array(3) { 65 | [0]=> 66 | string(1) "a" 67 | [1]=> 68 | string(1) "b" 69 | [2]=> 70 | string(1) "c" 71 | } 72 | debug_callback() 00: boolean / true 73 | NULL 74 | done! 75 | -------------------------------------------------------------------------------- /tests/mymemc_mysqli_fetch_assoc.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | mysqli fetch assoc 3 | --SKIPIF-- 4 | 15 | --INI-- 16 | mysqlnd_memcache.enable=1 17 | --FILE-- 18 | $arg) { 25 | printf(" %02d: %s / %s\n", $k, gettype($arg), var_export($arg, true)); 26 | } 27 | } 28 | 29 | $link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket); 30 | if ($link->connect_errno) { 31 | printf("[001] [%d] %s\n", $link->connect_errno, $link->connect_error); 32 | } 33 | 34 | $memc = my_memcache_connect($memcache_host, $memcache_port); 35 | if (!mysqlnd_memcache_set($link, $memc, NULL, "debug_callback")) { 36 | printf("[004] Failed to register connection, [%d] '%s'\n", 37 | $link->errno, $link->error); 38 | } 39 | 40 | if (!($res = $link->query("SELECT SQL_NO_CACHE f1, f2, f3 FROM mymem_test WHERE id = 'key2'"))) { 41 | printf("[006] %d %s\n", $link->errno, $link->error); 42 | } 43 | $data = $res->fetch_assoc(); 44 | 45 | if ($res = $link->query("SELECT f1, f2, f3 FROM mymem_test WHERE id = 'key2'")) { 46 | if (!is_object($res)) { 47 | printf("[007] No resultset but %s\n", var_export($res, true)); 48 | } else { 49 | $row = $res->fetch_assoc(); 50 | if ($row != $data) { 51 | printf("[008] Wrong results\n"); 52 | var_dump($row); 53 | var_dump($data); 54 | var_dump(array_diff($row, $data)); 55 | } 56 | } 57 | } else { 58 | printf("[009] %d %s\n", $link->errno, $link->error); 59 | } 60 | 61 | print "done!"; 62 | ?> 63 | --EXPECT-- 64 | debug_callback() 00: boolean / false 65 | debug_callback() 00: boolean / true 66 | done! -------------------------------------------------------------------------------- /tests/mymemc_mysqli_fetch_object.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | mysqli fetch object 3 | --SKIPIF-- 4 | 15 | --INI-- 16 | mysqlnd_memcache.enable=1 17 | --FILE-- 18 | $arg) { 25 | printf(" %02d: %s / %s\n", $k, gettype($arg), var_export($arg, true)); 26 | } 27 | } 28 | 29 | $link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket); 30 | if ($link->connect_errno) { 31 | printf("[001] [%d] %s\n", $link->connect_errno, $link->connect_error); 32 | } 33 | 34 | $memc = my_memcache_connect($memcache_host, $memcache_port); 35 | if (!mysqlnd_memcache_set($link, $memc, NULL, "debug_callback")) { 36 | printf("[004] Failed to register connection, [%d] '%s'\n", 37 | $link->errno, $link->error); 38 | } 39 | 40 | if (!($res = $link->query("SELECT SQL_NO_CACHE f1, f2, f3 FROM mymem_test WHERE id = 'key2'"))) { 41 | printf("[006] %d %s\n", $link->errno, $link->error); 42 | } 43 | $data = $res->fetch_object(); 44 | 45 | if ($res = $link->query("SELECT f1, f2, f3 FROM mymem_test WHERE id = 'key2'")) { 46 | if (!is_object($res)) { 47 | printf("[007] No resultset but %s\n", var_export($res, true)); 48 | } else { 49 | $row = $res->fetch_object(); 50 | if ($row != $data) { 51 | printf("[008] Wrong results\n"); 52 | var_dump($row); 53 | var_dump($data); 54 | var_dump(array_diff($row, $data)); 55 | } 56 | } 57 | } else { 58 | printf("[009] %d %s\n", $link->errno, $link->error); 59 | } 60 | 61 | print "done!"; 62 | ?> 63 | --EXPECT-- 64 | debug_callback() 00: boolean / false 65 | debug_callback() 00: boolean / true 66 | done! -------------------------------------------------------------------------------- /tests/mymemc_column_superset.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Column superset 3 | --SKIPIF-- 4 | 15 | --INI-- 16 | mysqlnd_memcache.enable=1 17 | --FILE-- 18 | $arg) { 25 | printf(" %02d: %s / %s\n", $k, gettype($arg), var_export($arg, true)); 26 | } 27 | } 28 | 29 | $link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket); 30 | if ($link->connect_errno) { 31 | printf("[001] [%d] %s\n", $link->connect_errno, $link->connect_error); 32 | } 33 | 34 | $memc = my_memcache_connect($memcache_host, $memcache_port); 35 | 36 | if (!mysqlnd_memcache_set($link, $memc, NULL, "debug_callback")) { 37 | printf("[002] Failed to register connection, [%d] '%s'\n", 38 | $link->errno, $link->error); 39 | } 40 | 41 | if (!($key1 = $memc->get("@@mymem_test.key1"))) { 42 | printf("[003] Failed to fetch 'key1' using native Memcache API.\n"); 43 | } 44 | $columns = explode("|", $key1); 45 | var_dump($columns); 46 | 47 | 48 | if ($res = $link->query("SELECT f1, f2, f3, 4 AS _four FROM mymem_test WHERE id = 'key1'")) { 49 | var_dump($res->fetch_row()); 50 | var_dump($res->fetch_assoc()); 51 | 52 | } else { 53 | printf("[004] %d %s\n", $link->errno, $link->error); 54 | } 55 | 56 | print "done!"; 57 | ?> 58 | --EXPECT-- 59 | array(3) { 60 | [0]=> 61 | string(1) "a" 62 | [1]=> 63 | string(1) "b" 64 | [2]=> 65 | string(1) "c" 66 | } 67 | debug_callback() 00: boolean / false 68 | array(4) { 69 | [0]=> 70 | string(1) "a" 71 | [1]=> 72 | string(1) "b" 73 | [2]=> 74 | string(1) "c" 75 | [3]=> 76 | string(1) "4" 77 | } 78 | NULL 79 | done! 80 | -------------------------------------------------------------------------------- /tests/mymemc_mysqli_fetch_array.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | mysqli fetch array 3 | --SKIPIF-- 4 | 15 | --INI-- 16 | mysqlnd_memcache.enable=1 17 | --FILE-- 18 | $arg) { 25 | printf(" %02d: %s / %s\n", $k, gettype($arg), var_export($arg, true)); 26 | } 27 | } 28 | 29 | $link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket); 30 | if ($link->connect_errno) { 31 | printf("[001] [%d] %s\n", $link->connect_errno, $link->connect_error); 32 | } 33 | 34 | $memc = my_memcache_connect($memcache_host, $memcache_port); 35 | if (!mysqlnd_memcache_set($link, $memc, NULL, "debug_callback")) { 36 | printf("[004] Failed to register connection, [%d] '%s'\n", 37 | $link->errno, $link->error); 38 | } 39 | 40 | if (!($res = $link->query("SELECT SQL_NO_CACHE f1, f2, f3 FROM mymem_test WHERE id = 'key2'"))) { 41 | printf("[006] %d %s\n", $link->errno, $link->error); 42 | } 43 | $data = $res->fetch_array(MYSQLI_BOTH); 44 | 45 | if ($res = $link->query("SELECT f1, f2, f3 FROM mymem_test WHERE id = 'key2'")) { 46 | if (!is_object($res)) { 47 | printf("[007] No resultset but %s\n", var_export($res, true)); 48 | } else { 49 | $row = $res->fetch_array(MYSQLI_BOTH); 50 | if ($row != $data) { 51 | printf("[008] Wrong results\n"); 52 | var_dump($row); 53 | var_dump($data); 54 | var_dump(array_diff($row, $data)); 55 | } 56 | } 57 | } else { 58 | printf("[009] %d %s\n", $link->errno, $link->error); 59 | } 60 | 61 | print "done!"; 62 | ?> 63 | --EXPECT-- 64 | debug_callback() 00: boolean / false 65 | debug_callback() 00: boolean / true 66 | done! -------------------------------------------------------------------------------- /tests/mymemc_null_values.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | NULL columns 3 | --SKIPIF-- 4 | 15 | --INI-- 16 | mysqlnd_memcache.enable=1 17 | --FILE-- 18 | $arg) { 25 | printf(" %02d: %s / %s\n", $k, gettype($arg), var_export($arg, true)); 26 | } 27 | } 28 | 29 | $link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket); 30 | if ($link->connect_errno) { 31 | printf("[001] [%d] %s\n", $link->connect_errno, $link->connect_error); 32 | } 33 | 34 | $memc = my_memcache_connect($memcache_host, $memcache_port); 35 | if (!mysqlnd_memcache_set($link, $memc, NULL, "debug_callback")) { 36 | printf("[004] Failed to register connection, [%d] '%s'\n", 37 | $link->errno, $link->error); 38 | } 39 | 40 | if (!$link->query("UPDATE mymem_test SET f1=NULL WHERE id='key2'")) { 41 | printf("[005] %d %s\n", $link->errno, $link->error); 42 | } 43 | 44 | if (!($res = $link->query("SELECT SQL_NO_CACHE f1, f2, f3 FROM mymem_test WHERE id = 'key2'"))) { 45 | printf("[006] %d %s\n", $link->errno, $link->error); 46 | } 47 | $data = $res->fetch_row(); 48 | 49 | 50 | if ($res = $link->query("SELECT f1, f2, f3 FROM mymem_test WHERE id = 'key2'")) { 51 | $row = $res->fetch_row(); 52 | if ($row != $data) { 53 | printf("[006] Wrong results\n"); 54 | var_dump($row); 55 | var_dump($data); 56 | var_dump(array_diff($row, $data)); 57 | } 58 | } else { 59 | printf("[005] %d %s\n", $link->errno, $link->error); 60 | } 61 | 62 | print "done!"; 63 | ?> 64 | --EXPECT-- 65 | debug_callback() 00: boolean / false 66 | debug_callback() 00: boolean / false 67 | debug_callback() 00: boolean / true 68 | done! 69 | -------------------------------------------------------------------------------- /tests/mymemc_empty_string.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Empty string columns 3 | --SKIPIF-- 4 | 15 | --INI-- 16 | mysqlnd_memcache.enable=1 17 | --FILE-- 18 | $arg) { 25 | printf(" %02d: %s / %s\n", $k, gettype($arg), var_export($arg, true)); 26 | } 27 | } 28 | 29 | $link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket); 30 | if ($link->connect_errno) { 31 | printf("[001] [%d] %s\n", $link->connect_errno, $link->connect_error); 32 | } 33 | 34 | $memc = my_memcache_connect($memcache_host, $memcache_port); 35 | if (!mysqlnd_memcache_set($link, $memc, NULL, "debug_callback")) { 36 | printf("[004] Failed to register connection, [%d] '%s'\n", 37 | $link->errno, $link->error); 38 | } 39 | 40 | if (!$link->query("UPDATE mymem_test SET f1='' WHERE id='key2'")) { 41 | printf("[005] %d %s\n", $link->errno, $link->error); 42 | } 43 | 44 | if (!($res = $link->query("SELECT SQL_NO_CACHE f1, f2, f3 FROM mymem_test WHERE id = 'key2'"))) { 45 | printf("[006] %d %s\n", $link->errno, $link->error); 46 | } 47 | $data = $res->fetch_row(); 48 | 49 | 50 | if ($res = $link->query("SELECT f1, f2, f3 FROM mymem_test WHERE id = 'key2'")) { 51 | $row = $res->fetch_row(); 52 | if ($row != $data) { 53 | printf("[006] Wrong results\n"); 54 | var_dump($row); 55 | var_dump($data); 56 | var_dump(array_diff($row, $data)); 57 | } 58 | } else { 59 | printf("[005] %d %s\n", $link->errno, $link->error); 60 | } 61 | 62 | print "done!"; 63 | ?> 64 | --EXPECT-- 65 | debug_callback() 00: boolean / false 66 | debug_callback() 00: boolean / false 67 | debug_callback() 00: boolean / true 68 | done! 69 | -------------------------------------------------------------------------------- /tests/mymemc_api_basics_pdo.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Simple PDO test 3 | --SKIPIF-- 4 | 15 | --INI-- 16 | mysqlnd_memcache.enable=1 17 | --FILE-- 18 | $arg) { 25 | printf(" %02d: %s / %s\n", $k, gettype($arg), var_export($arg, true)); 26 | } 27 | } 28 | 29 | if (!$link = my_pdo_connect($host, $user, $passwd, $db, $port, $socket)) { 30 | printf("[001] Connection failed\n"); 31 | } 32 | 33 | /* Disable PS emulation - we do not monitor prepare() C API call */ 34 | $link->setAttribute(PDO::ATTR_EMULATE_PREPARES, 1); 35 | 36 | $memc = my_memcache_connect($memcache_host, $memcache_port); 37 | if (!mysqlnd_memcache_set($link, $memc, NULL, "debug_callback")) { 38 | printf("[002] Failed to register connection, [%d] '%s'\n", 39 | $link->errno, $link->error); 40 | } 41 | 42 | if (!($key1 = $memc->get("@@mymem_test.key1"))) { 43 | printf("[003] Failed to fetch 'key1' using native Memcache API.\n"); 44 | } 45 | $columns = explode("|", $key1); 46 | var_dump($columns); 47 | 48 | 49 | if ($res = $link->query("SELECT f1, f2, f3 FROM mymem_test WHERE id = 'key1'")) { 50 | $row = $res->fetch(PDO::FETCH_NUM); 51 | if ($row != $columns) { 52 | printf("[005] Native Memcache and SQL results differ\n"); 53 | var_dump(array_diff($row, $columns)); 54 | } 55 | 56 | var_dump($res->fetch()); 57 | } else { 58 | printf("[004] Fetch failed, %s\n", var_export($link->errorInfo(), true)); 59 | } 60 | 61 | print "done!"; 62 | ?> 63 | --EXPECT-- 64 | array(3) { 65 | [0]=> 66 | string(1) "a" 67 | [1]=> 68 | string(1) "b" 69 | [2]=> 70 | string(1) "c" 71 | } 72 | debug_callback() 00: boolean / true 73 | bool(false) 74 | done! 75 | -------------------------------------------------------------------------------- /tests/mymemc_query_unbuffered.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Unbuffered query 3 | --SKIPIF-- 4 | 15 | --INI-- 16 | mysqlnd_memcache.enable=1 17 | --FILE-- 18 | $arg) { 25 | printf(" %02d: %s / %s\n", $k, gettype($arg), var_export($arg, true)); 26 | } 27 | } 28 | 29 | $link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket); 30 | if ($link->connect_errno) { 31 | printf("[001] [%d] %s\n", $link->connect_errno, $link->connect_error); 32 | } 33 | 34 | $memc = my_memcache_connect($memcache_host, $memcache_port); 35 | 36 | if (!mysqlnd_memcache_set($link, $memc, NULL, "debug_callback")) { 37 | printf("[002] Failed to register connection, [%d] '%s'\n", 38 | $link->errno, $link->error); 39 | } 40 | 41 | if (!($key1 = $memc->get("@@mymem_test.key1"))) { 42 | printf("[003] Failed to fetch 'key1' using native Memcache API.\n"); 43 | } 44 | $columns = explode("|", $key1); 45 | var_dump($columns); 46 | 47 | 48 | if ($link->real_query("SELECT f1, f2, f3 FROM mymem_test WHERE id = 'key1'")) { 49 | if ($res = $link->use_result()) { 50 | $row = $res->fetch_row(); 51 | if ($row != $columns) { 52 | printf("[006] Native Memcache and SQL results differ\n"); 53 | var_dump(array_diff($row, $columns)); 54 | } 55 | var_dump($res->fetch_assoc()); 56 | } else { 57 | printf("[005] %d %s\n", $link->errno, $link->error); 58 | } 59 | } else { 60 | printf("[004] %d %s\n", $link->errno, $link->error); 61 | } 62 | 63 | 64 | print "done!"; 65 | ?> 66 | --EXPECT-- 67 | array(3) { 68 | [0]=> 69 | string(1) "a" 70 | [1]=> 71 | string(1) "b" 72 | [2]=> 73 | string(1) "c" 74 | } 75 | debug_callback() 00: boolean / true 76 | NULL 77 | done! 78 | -------------------------------------------------------------------------------- /tests/mymemc_update_and_fetch.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Update (SQL) and fetch (memcache) 3 | --SKIPIF-- 4 | 15 | --INI-- 16 | mysqlnd_memcache.enable=1 17 | --FILE-- 18 | $arg) { 27 | printf(" %02d: %s / %s\n", $k, gettype($arg), var_export($arg, true)); 28 | } 29 | } 30 | 31 | $link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket); 32 | if ($link->connect_errno) { 33 | printf("[001] [%d] %s\n", $link->connect_errno, $link->connect_error); 34 | } 35 | 36 | $memc = my_memcache_connect($memcache_host, $memcache_port); 37 | if (!mysqlnd_memcache_set($link, $memc, NULL, "debug_callback")) { 38 | printf("[004] Failed to register connection, [%d] '%s'\n", 39 | $link->errno, $link->error); 40 | } 41 | 42 | for ($i = 0; $i < 100; $i++) { 43 | if (!$link->query("UPDATE mymem_test SET f1='a' WHERE id='key2'")) { 44 | printf("[005] %d %s\n", $link->errno, $link->error); 45 | } 46 | } 47 | 48 | if (!($key1 = $memc->get("@@mymem_test.key2"))) { 49 | printf("[006] Failed to fetch 'key2' using native Memcache API.\n"); 50 | } 51 | $columns = explode("|", $key1); 52 | var_dump($columns); 53 | 54 | if ($res = $link->query("SELECT f1, f2, f3 FROM mymem_test WHERE id = 'key2'")) { 55 | if (!is_object($res)) { 56 | printf("[007] No resultset but %s\n", var_export($res, true)); 57 | } else { 58 | var_dump($res->fetch_assoc()); 59 | } 60 | } else { 61 | printf("[008] %d %s\n", $link->errno, $link->error); 62 | } 63 | 64 | print "done!"; 65 | ?> 66 | --XFAIL-- 67 | No results 68 | --EXPECT-- 69 | debug_callback() 00: boolean / false 70 | debug_callback() 00: boolean / true 71 | some resultset 72 | done! 73 | -------------------------------------------------------------------------------- /tests/mymemc_resultmeta_lengths.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Result meta: length 3 | --SKIPIF-- 4 | 15 | --INI-- 16 | mysqlnd_memcache.enable=1 17 | --FILE-- 18 | $arg) { 27 | printf(" %02d: %s / %s\n", $k, gettype($arg), var_export($arg, true)); 28 | } 29 | } 30 | 31 | $link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket); 32 | if ($link->connect_errno) { 33 | printf("[001] [%d] %s\n", $link->connect_errno, $link->connect_error); 34 | } 35 | 36 | $memc = my_memcache_connect($memcache_host, $memcache_port); 37 | if (!mysqlnd_memcache_set($link, $memc, NULL, "debug_callback")) { 38 | printf("[002] Failed to register connection, [%d] '%s'\n", 39 | $link->errno, $link->error); 40 | } 41 | 42 | if ($res = $link->query("SELECT f1, f2, f3 FROM mymem_test WHERE id = 'key1'")) { 43 | $row = $res->fetch_row(); 44 | foreach ($res->lengths as $k => $v) { 45 | printf("Field %d: '%s' (%d)\n", $k, $row[$k], $v); 46 | } 47 | } else { 48 | printf("[003] [%d] %s\n", $link->errno, $link->error); 49 | } 50 | 51 | if ($res = $link->query("SELECT f1, f2, f3 FROM mymem_test WHERE id = 'key2'")) { 52 | $row = $res->fetch_row(); 53 | foreach ($res->lengths as $k => $v) { 54 | printf("Field %d: '%s' (%d)\n", $k, $row[$k], $v); 55 | } 56 | } else { 57 | printf("[004] [%d] %s\n", $link->errno, $link->error); 58 | } 59 | 60 | 61 | print "done!"; 62 | ?> 63 | --XFAIL-- 64 | Not enough length meta 65 | --EXPECT-- 66 | debug_callback() 00: boolean / true 67 | Field 0: 'a' (%d) 68 | Field 1: 'b' (%d) 69 | Field 2: 'c' (%d) 70 | debug_callback() 00: boolean / true 71 | Field 0: 'foo' (%d) 72 | Field 1: 'bar' (%d) 73 | Field 2: 'baz' (%d) 74 | done! -------------------------------------------------------------------------------- /tests/mymemc_server_resultmeta.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Resultset meta from the server 3 | --SKIPIF-- 4 | 15 | --INI-- 16 | mysqlnd_memcache.enable=1 17 | --FILE-- 18 | connect_errno) { 24 | printf("[001] [%d] %s\n", $link->connect_errno, $link->connect_error); 25 | } 26 | 27 | if ($res = $link->query("SELECT f1, f2, f3 FROM mymem_test_meta WHERE id = 'key1'")) { 28 | $fields = $res->fetch_fields(); 29 | $res->free(); 30 | } else { 31 | printf("[004] [%d] %s\n", $link->errno, $link->error); 32 | } 33 | 34 | if ($res = $link->query("SELECT f1, f2, f3 FROM mymem_test_meta WHERE id = 'key1'")) { 35 | $fields_meta = $res->fetch_fields(); 36 | } else { 37 | printf("[005] [%d] %s\n", $link->errno, $link->error); 38 | } 39 | 40 | if ($res = $link->query("SELECT f1, f2, f3 FROM mymem_test WHERE id = 'key1'")) { 41 | $fields_memc = $res->fetch_fields(); 42 | } else { 43 | printf("[006] [%d] %s\n", $link->errno, $link->error); 44 | } 45 | 46 | printf("Shadow table checking\n"); 47 | my_memcache_compare_meta(7, $fields_meta, $fields); 48 | printf("Meta of Memcache mapped and shadow table\n"); 49 | my_memcache_compare_meta(8, $fields_meta, $fields_memc); 50 | 51 | print "done!"; 52 | ?> 53 | --EXPECT-- 54 | Shadow table checking 55 | Meta of Memcache mapped and shadow table 56 | [008] Field values for field 0 differ 57 | table: 'mymem_test_meta' != 'mymem_test' 58 | orgtable: 'mymem_test_meta' != 'mymem_test' 59 | [008] Field values for field 1 differ 60 | table: 'mymem_test_meta' != 'mymem_test' 61 | orgtable: 'mymem_test_meta' != 'mymem_test' 62 | [008] Field values for field 2 differ 63 | table: 'mymem_test_meta' != 'mymem_test' 64 | orgtable: 'mymem_test_meta' != 'mymem_test' 65 | done! -------------------------------------------------------------------------------- /tests/mymemc_insert_and_fetch.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Update (SQL) and fetch (memcache) 3 | --SKIPIF-- 4 | 15 | --INI-- 16 | mysqlnd_memcache.enable=1 17 | --FILE-- 18 | $arg) { 27 | printf(" %02d: %s / %s\n", $k, gettype($arg), var_export($arg, true)); 28 | } 29 | } 30 | 31 | 32 | $link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket); 33 | if ($link->connect_errno) { 34 | printf("[001] [%d] %s\n", $link->connect_errno, $link->connect_error); 35 | } 36 | 37 | $memc = my_memcache_connect($memcache_host, $memcache_port); 38 | if (!mysqlnd_memcache_set($link, $memc, NULL, "debug_callback")) { 39 | printf("[004] Failed to register connection, [%d] '%s'\n", 40 | $link->errno, $link->error); 41 | } 42 | 43 | for ($i = 4; $i < 104; $i++) { 44 | $query = sprintf("INSERT INTO mymem_test(id, f1, f2, f3) VALUES ('k%d', 'a', 'b', 'c')", $i); 45 | if (!$link->query($query)) { 46 | printf("[005] %d %s\n", $link->errno, $link->error); 47 | } 48 | } 49 | 50 | if (!($key1 = $memc->get("@@mymem_test.k4"))) { 51 | printf("[006] Failed to fetch 'k4' using native Memcache API.\n"); 52 | } 53 | $columns = explode("|", $key1); 54 | var_dump($columns); 55 | 56 | if ($res = $link->query("SELECT f1, f2, f3 FROM mymem_test WHERE id = 'k4'")) { 57 | if (!is_object($res)) { 58 | printf("[007] No resultset but %s\n", var_export($res, true)); 59 | } else { 60 | var_dump($res->fetch_assoc()); 61 | } 62 | } else { 63 | printf("[008] %d %s\n", $link->errno, $link->error); 64 | } 65 | 66 | print "done!"; 67 | ?> 68 | --XFAIL-- 69 | No results 70 | --EXPECT-- 71 | debug_callback() 00: boolean / false 72 | debug_callback() 00: boolean / true 73 | some resultset 74 | done! 75 | -------------------------------------------------------------------------------- /tests/mymemc_query_multi.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Multi query 3 | --SKIPIF-- 4 | 15 | --INI-- 16 | mysqlnd_memcache.enable=1 17 | --FILE-- 18 | $arg) { 25 | printf(" %02d: %s / %s\n", $k, gettype($arg), var_export($arg, true)); 26 | } 27 | } 28 | 29 | $link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket); 30 | if ($link->connect_errno) { 31 | printf("[001] [%d] %s\n", $link->connect_errno, $link->connect_error); 32 | } 33 | 34 | $memc = my_memcache_connect($memcache_host, $memcache_port); 35 | 36 | if (!mysqlnd_memcache_set($link, $memc, NULL, "debug_callback")) { 37 | printf("[002] Failed to register connection, [%d] '%s'\n", 38 | $link->errno, $link->error); 39 | } 40 | 41 | if (!($key1 = $memc->get("@@mymem_test.key1"))) { 42 | printf("[003] Failed to fetch 'key1' using native Memcache API.\n"); 43 | } 44 | $columns = explode("|", $key1); 45 | var_dump($columns); 46 | 47 | $resno = 0; 48 | $query = "SELECT f1, f2, f3 FROM mymem_test WHERE id = 'key1'"; 49 | if ($link->multi_query($query)) { 50 | do { 51 | if ($res = $link->store_result()) { 52 | printf("Result set %d\n", ++$resno); 53 | while ($row = $res->fetch_row()) { 54 | var_dump($row); 55 | } 56 | $res->free(); 57 | } 58 | } while ($link->more_results() && $link->next_result()); 59 | } else { 60 | printf("[004] [%d] %s\n", $link->errno, $link->error); 61 | } 62 | 63 | 64 | print "done!"; 65 | ?> 66 | --EXPECT-- 67 | array(3) { 68 | [0]=> 69 | string(1) "a" 70 | [1]=> 71 | string(1) "b" 72 | [2]=> 73 | string(1) "c" 74 | } 75 | debug_callback() 00: boolean / true 76 | Result set 1 77 | array(3) { 78 | [0]=> 79 | string(1) "a" 80 | [1]=> 81 | string(1) "b" 82 | [2]=> 83 | string(1) "c" 84 | } 85 | done! 86 | -------------------------------------------------------------------------------- /tests/mymemc_resultmeta_multiple.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Resultset meta - SQL vs. Memcache same table 3 | --SKIPIF-- 4 | 15 | --INI-- 16 | mysqlnd_memcache.enable=1 17 | --FILE-- 18 | $arg) { 27 | printf(" %02d: %s / %s\n", $k, gettype($arg), var_export($arg, true)); 28 | } 29 | } 30 | 31 | $link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket); 32 | if ($link->connect_errno) { 33 | printf("[001] [%d] %s\n", $link->connect_errno, $link->connect_error); 34 | } 35 | 36 | $memc = my_memcache_connect($memcache_host, $memcache_port); 37 | if (!mysqlnd_memcache_set($link, $memc, NULL, "debug_callback")) { 38 | printf("[002] Failed to register connection, [%d] '%s'\n", 39 | $link->errno, $link->error); 40 | } 41 | 42 | 43 | if ($res = $link->query("SELECT f1, f2, f3 FROM mymem_test WHERE id = 'key1'")) { 44 | $fields_mapped1 = $res->fetch_fields(); 45 | } else { 46 | printf("[003] [%d] %s\n", $link->errno, $link->error); 47 | } 48 | 49 | if ($res = $link->query("SELECT f1, f2, f3 FROM mymem_test WHERE id = 'key1'")) { 50 | $fields_mapped2 = $res->fetch_fields(); 51 | my_memcache_compare_meta(4, $fields_mapped1, $fields_mapped2); 52 | } else { 53 | printf("[005] [%d] %s\n", $link->errno, $link->error); 54 | } 55 | 56 | if ($res = $link->query("SELECT SQL_NO_CACHE f1, f2, f3 FROM mymem_test WHERE id = 'key1'")) { 57 | $fields_sql_shadow = $res->fetch_fields(); 58 | $res->free(); 59 | } else { 60 | printf("[006] [%d] %s\n", $link->errno, $link->error); 61 | } 62 | 63 | print "done!"; 64 | ?> 65 | --XFAIL-- 66 | Commands out of sync 67 | --EXPECT-- 68 | debug_callback() 00: boolean / true 69 | debug_callback() 00: boolean / true 70 | debug_callback() 00: boolean / false 71 | done! -------------------------------------------------------------------------------- /tests/mymemc_query_multi_unbuffered.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Multi query + use result 3 | --SKIPIF-- 4 | 15 | --INI-- 16 | mysqlnd_memcache.enable=1 17 | --FILE-- 18 | $arg) { 25 | printf(" %02d: %s / %s\n", $k, gettype($arg), var_export($arg, true)); 26 | } 27 | } 28 | 29 | $link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket); 30 | if ($link->connect_errno) { 31 | printf("[001] [%d] %s\n", $link->connect_errno, $link->connect_error); 32 | } 33 | 34 | $memc = my_memcache_connect($memcache_host, $memcache_port); 35 | 36 | if (!mysqlnd_memcache_set($link, $memc, NULL, "debug_callback")) { 37 | printf("[002] Failed to register connection, [%d] '%s'\n", 38 | $link->errno, $link->error); 39 | } 40 | 41 | if (!($key1 = $memc->get("@@mymem_test.key1"))) { 42 | printf("[003] Failed to fetch 'key1' using native Memcache API.\n"); 43 | } 44 | $columns = explode("|", $key1); 45 | var_dump($columns); 46 | 47 | $resno = 0; 48 | $query = "SELECT f1, f2, f3 FROM mymem_test WHERE id = 'key1'"; 49 | if ($link->multi_query($query)) { 50 | do { 51 | if ($res = $link->use_result()) { 52 | printf("Result set %d\n", ++$resno); 53 | while ($row = $res->fetch_row()) { 54 | var_dump($row); 55 | } 56 | $res->free(); 57 | } 58 | } while ($link->more_results() && $link->next_result()); 59 | } else { 60 | printf("[004] [%d] %s\n", $link->errno, $link->error); 61 | } 62 | 63 | 64 | print "done!"; 65 | ?> 66 | --EXPECT-- 67 | array(3) { 68 | [0]=> 69 | string(1) "a" 70 | [1]=> 71 | string(1) "b" 72 | [2]=> 73 | string(1) "c" 74 | } 75 | debug_callback() 00: boolean / true 76 | Result set 1 77 | array(3) { 78 | [0]=> 79 | string(1) "a" 80 | [1]=> 81 | string(1) "b" 82 | [2]=> 83 | string(1) "c" 84 | } 85 | done! 86 | -------------------------------------------------------------------------------- /tests/mymemc_mysqlnd_memcache_set_params.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | mysqlnd_memcache_set(), params 3 | --SKIPIF-- 4 | 14 | --INI-- 15 | mysqlnd_memcache.enable=1 16 | --FILE-- 17 | 52 | --EXPECTF-- 53 | [E_WARNING] mysqlnd_memcache_set() expects at least 2 parameters, 0 given in %s on line %d 54 | [E_WARNING] mysqlnd_memcache_set() expects at least 2 parameters, 1 given in %s on line %d 55 | [E_WARNING] mysqlnd_memcache_set() expects at most 4 parameters, 5 given in %s on line %d 56 | [E_RECOVERABLE_ERROR] Argument 2 passed to mysqlnd_memcache_set() must be an instance of Memcached, string given in %s on line %d 57 | [E_WARNING] mysqlnd_memcache_set() expects parameter 2 to be Memcached, string given in %s on line %d 58 | [E_WARNING] mysqlnd_memcache_set(): Passed variable is no mysqlnd-based MySQL connection in %s on line %d 59 | done! -------------------------------------------------------------------------------- /tests/mymemc_resultmeta_multiple_plugin_disabled.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Resultset meta - plugin disabled 3 | --SKIPIF-- 4 | 15 | --INI-- 16 | mysqlnd_memcache.enable=0 17 | --FILE-- 18 | $arg) { 27 | printf(" %02d: %s / %s\n", $k, gettype($arg), var_export($arg, true)); 28 | } 29 | } 30 | 31 | $link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket); 32 | if ($link->connect_errno) { 33 | printf("[001] [%d] %s\n", $link->connect_errno, $link->connect_error); 34 | } 35 | 36 | if ($res = $link->query("SELECT f1, f2, f3 FROM mymem_test WHERE id = 'key1'")) { 37 | $fields_mapped1 = $res->fetch_fields(); 38 | } else { 39 | printf("[002] [%d] %s\n", $link->errno, $link->error); 40 | } 41 | 42 | if ($res = $link->query("SELECT f1, f2, f3 FROM mymem_test WHERE id = 'key1'")) { 43 | $fields_mapped2 = $res->fetch_fields(); 44 | my_memcache_compare_meta(3, $fields_mapped1, $fields_mapped2); 45 | } else { 46 | printf("[004] [%d] %s\n", $link->errno, $link->error); 47 | } 48 | 49 | if ($res = $link->query("SELECT SQL_NO_CACHE f1, f2, f3 FROM mymem_test_meta WHERE id = 'key1'")) { 50 | $fields_sql_shadow = $res->fetch_fields(); 51 | my_memcache_compare_meta(3, $fields_mapped1, $fields_sql_shadow); 52 | $res->free(); 53 | } else { 54 | printf("[005] [%d] %s\n", $link->errno, $link->error); 55 | } 56 | 57 | print "done!"; 58 | ?> 59 | --EXPECT-- 60 | [003] Field values for field 0 differ 61 | table: 'mymem_test' != 'mymem_test_meta' 62 | orgtable: 'mymem_test' != 'mymem_test_meta' 63 | [003] Field values for field 1 differ 64 | table: 'mymem_test' != 'mymem_test_meta' 65 | orgtable: 'mymem_test' != 'mymem_test_meta' 66 | [003] Field values for field 2 differ 67 | table: 'mymem_test' != 'mymem_test_meta' 68 | orgtable: 'mymem_test' != 'mymem_test_meta' 69 | done! -------------------------------------------------------------------------------- /tests/mymemc_update_and_fetch_sql.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Update (SQL) and fetch (memcache) 3 | --SKIPIF-- 4 | 15 | --INI-- 16 | mysqlnd_memcache.enable=1 17 | --FILE-- 18 | $arg) { 27 | printf(" %02d: %s / %s\n", $k, gettype($arg), var_export($arg, true)); 28 | } 29 | } 30 | 31 | $link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket); 32 | if ($link->connect_errno) { 33 | printf("[001] [%d] %s\n", $link->connect_errno, $link->connect_error); 34 | } 35 | 36 | $memc = my_memcache_connect($memcache_host, $memcache_port); 37 | if (!mysqlnd_memcache_set($link, $memc, NULL, "debug_callback")) { 38 | printf("[004] Failed to register connection, [%d] '%s'\n", 39 | $link->errno, $link->error); 40 | } 41 | 42 | for ($i = 0; $i < 100; $i++) { 43 | if (!$link->query("UPDATE mymem_test SET f1='a' WHERE id='key2'")) { 44 | printf("[005] %d %s\n", $link->errno, $link->error); 45 | } 46 | } 47 | 48 | if (!($res = $link->query("SELECT SQL_NO_CACHE f1, f2, f3 FROM mymem_test WHERE id = 'key2'"))) { 49 | printf("[006][%d] %s\n", $link->errno, $link->error); 50 | } 51 | var_dump($res->fetch_assoc()); 52 | 53 | if ($res = $link->query("SELECT f1, f2, f3 FROM mymem_test WHERE id = 'key2'")) { 54 | if (!is_object($res)) { 55 | printf("[007] No resultset but %s\n", var_export($res, true)); 56 | } else { 57 | var_dump($res->fetch_assoc()); 58 | } 59 | } else { 60 | printf("[008] %d %s\n", $link->errno, $link->error); 61 | } 62 | 63 | print "done!"; 64 | ?> 65 | --EXPECT-- 66 | array(3) { 67 | ["f1"]=> 68 | string(1) "a" 69 | ["f2"]=> 70 | string(3) "bar" 71 | ["f3"]=> 72 | string(3) "baz" 73 | } 74 | debug_callback() 00: boolean / true 75 | array(3) { 76 | ["f1"]=> 77 | string(1) "a" 78 | ["f2"]=> 79 | string(3) "bar" 80 | ["f3"]=> 81 | string(3) "baz" 82 | } 83 | done! -------------------------------------------------------------------------------- /tests/mymemc_insert_and_fetch_sql.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Update (SQL) and fetch (memcache) 3 | --SKIPIF-- 4 | 15 | --INI-- 16 | mysqlnd_memcache.enable=1 17 | --FILE-- 18 | $arg) { 27 | printf(" %02d: %s / %s\n", $k, gettype($arg), var_export($arg, true)); 28 | } 29 | } 30 | 31 | 32 | $link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket); 33 | if ($link->connect_errno) { 34 | printf("[001] [%d] %s\n", $link->connect_errno, $link->connect_error); 35 | } 36 | 37 | $memc = my_memcache_connect($memcache_host, $memcache_port); 38 | if (!mysqlnd_memcache_set($link, $memc, NULL, "debug_callback")) { 39 | printf("[004] Failed to register connection, [%d] '%s'\n", 40 | $link->errno, $link->error); 41 | } 42 | 43 | for ($i = 4; $i < 104; $i++) { 44 | $query = sprintf("INSERT INTO mymem_test(id, f1, f2, f3) VALUES ('k%d', 'a', 'b', 'c')", $i); 45 | if (!$link->query($query)) { 46 | printf("[005] %d %s\n", $link->errno, $link->error); 47 | } 48 | } 49 | 50 | if (!($res = $link->query("SELECT SQL_NO_CACHE f1, f2, f3 FROM mymem_test WHERE id = 'k4'"))) { 51 | printf("[006][%d] %s\n", $link->errno, $link->error); 52 | } 53 | var_dump($res->fetch_assoc()); 54 | 55 | if ($res = $link->query("SELECT f1, f2, f3 FROM mymem_test WHERE id = 'k4'")) { 56 | if (!is_object($res)) { 57 | printf("[007] No resultset but %s\n", var_export($res, true)); 58 | } else { 59 | var_dump($res->fetch_assoc()); 60 | } 61 | } else { 62 | printf("[008] %d %s\n", $link->errno, $link->error); 63 | } 64 | 65 | print "done!"; 66 | ?> 67 | --EXPECT-- 68 | array(3) { 69 | ["f1"]=> 70 | string(1) "a" 71 | ["f2"]=> 72 | string(1) "b" 73 | ["f3"]=> 74 | string(1) "c" 75 | } 76 | debug_callback() 00: boolean / true 77 | array(3) { 78 | ["f1"]=> 79 | string(1) "a" 80 | ["f2"]=> 81 | string(1) "b" 82 | ["f3"]=> 83 | string(1) "c" 84 | } 85 | done! -------------------------------------------------------------------------------- /tests/mymemc_column_alias.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Column alias 3 | --SKIPIF-- 4 | 15 | --INI-- 16 | mysqlnd_memcache.enable=1 17 | --FILE-- 18 | $arg) { 25 | printf(" %02d: %s / %s\n", $k, gettype($arg), var_export($arg, true)); 26 | } 27 | } 28 | 29 | $link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket); 30 | if ($link->connect_errno) { 31 | printf("[001] [%d] %s\n", $link->connect_errno, $link->connect_error); 32 | } 33 | 34 | $memc = my_memcache_connect($memcache_host, $memcache_port); 35 | 36 | if (!mysqlnd_memcache_set($link, $memc, NULL, "debug_callback")) { 37 | printf("[002] Failed to register connection, [%d] '%s'\n", 38 | $link->errno, $link->error); 39 | } 40 | 41 | if (!($key1 = $memc->get("@@mymem_test.key1"))) { 42 | printf("[003] Failed to fetch 'key1' using native Memcache API.\n"); 43 | } 44 | $columns = explode("|", $key1); 45 | var_dump($columns); 46 | 47 | 48 | if ($res = $link->query("SELECT f1 AS f2, f3 FROM mymem_test WHERE id = 'key1'")) { 49 | var_dump($res->fetch_row()); 50 | var_dump($res->fetch_assoc()); 51 | 52 | } else { 53 | printf("[004] %d %s\n", $link->errno, $link->error); 54 | } 55 | 56 | if ($res = $link->query("SELECT 1 AS f1, 2 AS f2, 3 AS f3 FROM mymem_test WHERE id = 'key1'")) { 57 | var_dump($res->fetch_row()); 58 | var_dump($res->fetch_assoc()); 59 | 60 | } else { 61 | printf("[005] %d %s\n", $link->errno, $link->error); 62 | } 63 | 64 | print "done!"; 65 | ?> 66 | --EXPECT-- 67 | array(3) { 68 | [0]=> 69 | string(1) "a" 70 | [1]=> 71 | string(1) "b" 72 | [2]=> 73 | string(1) "c" 74 | } 75 | debug_callback() 00: boolean / false 76 | array(2) { 77 | [0]=> 78 | string(1) "a" 79 | [1]=> 80 | string(1) "c" 81 | } 82 | NULL 83 | debug_callback() 00: boolean / false 84 | array(3) { 85 | [0]=> 86 | string(1) "1" 87 | [1]=> 88 | string(1) "2" 89 | [2]=> 90 | string(1) "3" 91 | } 92 | NULL 93 | done! 94 | -------------------------------------------------------------------------------- /tests/mymemc_query_multi_multi.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Multi query 3 | --SKIPIF-- 4 | 15 | --INI-- 16 | mysqlnd_memcache.enable=1 17 | --FILE-- 18 | $arg) { 25 | printf(" %02d: %s / %s\n", $k, gettype($arg), var_export($arg, true)); 26 | } 27 | } 28 | 29 | $link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket); 30 | if ($link->connect_errno) { 31 | printf("[001] [%d] %s\n", $link->connect_errno, $link->connect_error); 32 | } 33 | 34 | $memc = my_memcache_connect($memcache_host, $memcache_port); 35 | 36 | if (!mysqlnd_memcache_set($link, $memc, NULL, "debug_callback")) { 37 | printf("[002] Failed to register connection, [%d] '%s'\n", 38 | $link->errno, $link->error); 39 | } 40 | 41 | if (!($key1 = $memc->get("@@mymem_test.key1"))) { 42 | printf("[003] Failed to fetch 'key1' using native Memcache API.\n"); 43 | } 44 | $columns = explode("|", $key1); 45 | var_dump($columns); 46 | 47 | $resno = 0; 48 | $query = "SELECT f1, f2, f3 FROM mymem_test WHERE id = 'key1'"; 49 | $query .= ";" . $query; 50 | if ($link->multi_query($query)) { 51 | do { 52 | if ($res = $link->store_result()) { 53 | printf("Result set %d\n", ++$resno); 54 | while ($row = $res->fetch_row()) { 55 | var_dump($row); 56 | } 57 | $res->free(); 58 | } 59 | } while ($link->more_results() && $link->next_result()); 60 | } else { 61 | printf("[004] [%d] %s\n", $link->errno, $link->error); 62 | } 63 | 64 | 65 | print "done!"; 66 | ?> 67 | --EXPECT-- 68 | array(3) { 69 | [0]=> 70 | string(1) "a" 71 | [1]=> 72 | string(1) "b" 73 | [2]=> 74 | string(1) "c" 75 | } 76 | debug_callback() 00: boolean / false 77 | Result set 1 78 | array(3) { 79 | [0]=> 80 | string(1) "a" 81 | [1]=> 82 | string(1) "b" 83 | [2]=> 84 | string(1) "c" 85 | } 86 | Result set 2 87 | array(3) { 88 | [0]=> 89 | string(1) "a" 90 | [1]=> 91 | string(1) "b" 92 | [2]=> 93 | string(1) "c" 94 | } 95 | done! 96 | -------------------------------------------------------------------------------- /tests/util.inc: -------------------------------------------------------------------------------- 1 | $field1) { 4 | 5 | if (isset($fields2[$k])) { 6 | $field2 = $fields2[$k]; 7 | 8 | $msg = ''; 9 | if ($field1->name != $field2->name) { 10 | $msg.= sprintf(" name: '%s' != '%s'\n", $field1->name, $field2->name); 11 | } 12 | if ($field1->orgname != $field2->orgname) { 13 | $msg.= sprintf(" orgname: '%s' != '%s'\n", $field1->orgname, $field2->orgname); 14 | } 15 | if ($field1->table != $field2->table) { 16 | $msg.= sprintf(" table: '%s' != '%s'\n", $field1->table, $field2->table); 17 | } 18 | if ($field1->orgtable != $field2->orgtable) { 19 | $msg.= sprintf(" orgtable: '%s' != '%s'\n", $field1->orgtable, $field2->orgtable); 20 | } 21 | if ($field1->def != $field2->def) { 22 | $msg.= sprintf(" def: '%s' != '%s'\n", $field1->def, $field2->def); 23 | } 24 | if ($field1->db != $field2->db) { 25 | $msg.= sprintf(" db: '%s' != '%s'\n", $field1->db, $field2->db); 26 | } 27 | if ($field1->catalog != $field2->catalog) { 28 | $msg.= sprintf(" name: '%s' != '%s'\n", $field1->name, $field2->name); 29 | } 30 | if ($field1->max_length != $field2->max_length) { 31 | $msg.= sprintf(" max_length: '%s' != '%s'\n", $field1->max_length, $field2->max_length); 32 | } 33 | if ($field1->length != $field2->length) { 34 | $msg.= sprintf(" length: '%s' != '%s'\n", $field1->length, $field2->length); 35 | } 36 | if ($field1->charsetnr != $field2->charsetnr) { 37 | $msg.= sprintf(" charsetnr: '%s' != '%s'\n", $field1->charsetnr, $field2->charsetnr); 38 | } 39 | if ($field1->flags != $field2->flags) { 40 | $msg.= sprintf(" flags: '%s' != '%s'\n", $field1->flags, $field2->flags); 41 | } 42 | if ($field1->type != $field2->type) { 43 | $msg.= sprintf(" type: '%s' != '%s'\n", $field1->type, $field2->type); 44 | } 45 | if ($field1->decimals != $field2->decimals) { 46 | $msg.= sprintf(" decimals: '%s' != '%s'\n", $field1->decimals, $field2->decimals); 47 | } 48 | 49 | unset($fields2[$k]); 50 | 51 | if (!$msg) 52 | continue; 53 | 54 | printf("[%03d] Field values for field %d differ\n", $offset, $k); 55 | echo $msg; 56 | } 57 | } 58 | 59 | if (!empty($fields2)) { 60 | printf("[%03d] Fields missing\n", $offset); 61 | var_dump($fields2); 62 | } 63 | } -------------------------------------------------------------------------------- /tests/mymemc_column_order.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Column order 3 | --SKIPIF-- 4 | 15 | --INI-- 16 | mysqlnd_memcache.enable=1 17 | --FILE-- 18 | $arg) { 25 | printf(" %02d: %s / %s\n", $k, gettype($arg), var_export($arg, true)); 26 | } 27 | } 28 | 29 | $link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket); 30 | if ($link->connect_errno) { 31 | printf("[001] [%d] %s\n", $link->connect_errno, $link->connect_error); 32 | } 33 | 34 | $memc = my_memcache_connect($memcache_host, $memcache_port); 35 | 36 | if (!mysqlnd_memcache_set($link, $memc, NULL, "debug_callback")) { 37 | printf("[002] Failed to register connection, [%d] '%s'\n", 38 | $link->errno, $link->error); 39 | } 40 | 41 | if (!($key1 = $memc->get("@@mymem_test.key1"))) { 42 | printf("[003] Failed to fetch 'key1' using native Memcache API.\n"); 43 | } 44 | $columns = explode("|", $key1); 45 | var_dump($columns); 46 | 47 | 48 | if ($res = $link->query("SELECT f1, f3, f2 FROM mymem_test WHERE id = 'key1'")) { 49 | var_dump($row = $res->fetch_row()); 50 | var_dump($res->fetch_assoc()); 51 | 52 | } else { 53 | printf("[004] %d %s\n", $link->errno, $link->error); 54 | } 55 | 56 | if ($res = $link->query("SELECT f1, f2, f3 FROM mymem_test WHERE id = 'key1'")) { 57 | var_dump($row = $res->fetch_row()); 58 | var_dump($res->fetch_assoc()); 59 | 60 | } else { 61 | printf("[005] %d %s\n", $link->errno, $link->error); 62 | } 63 | 64 | print "done!"; 65 | ?> 66 | --EXPECT-- 67 | array(3) { 68 | [0]=> 69 | string(1) "a" 70 | [1]=> 71 | string(1) "b" 72 | [2]=> 73 | string(1) "c" 74 | } 75 | debug_callback() 00: boolean / false 76 | array(3) { 77 | [0]=> 78 | string(1) "a" 79 | [1]=> 80 | string(1) "c" 81 | [2]=> 82 | string(1) "b" 83 | } 84 | NULL 85 | debug_callback() 00: boolean / true 86 | array(3) { 87 | [0]=> 88 | string(1) "a" 89 | [1]=> 90 | string(1) "b" 91 | [2]=> 92 | string(1) "c" 93 | } 94 | NULL 95 | done! 96 | -------------------------------------------------------------------------------- /tests/mymemc_seperator_clash_fetch_assoc.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Seperator clash and fetch assoc 3 | --SKIPIF-- 4 | 15 | --INI-- 16 | mysqlnd_memcache.enable=1 17 | --FILE-- 18 | $arg) { 25 | printf(" %02d: %s / %s\n", $k, gettype($arg), var_export($arg, true)); 26 | } 27 | } 28 | 29 | $link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket); 30 | if ($link->connect_errno) { 31 | printf("[001] [%d] %s\n", $link->connect_errno, $link->connect_error); 32 | } 33 | 34 | if (!$link->query("UPDATE mymem_test SET f1 = 'Hu|hu' WHERE id = 'key2'")) { 35 | printf("[002] [%d] %s\n", $link->errno, $link->error); 36 | } 37 | 38 | if (!($res = $link->query("SELECT SQL_NO_CACHE f1, f2, f3 FROM mymem_test WHERE id = 'key2'"))) { 39 | printf("[003] [%d] %s\n", $link->errno, $link->error); 40 | } 41 | $data = $res->fetch_assoc(); 42 | var_dump($data); 43 | 44 | $memc = my_memcache_connect($memcache_host, $memcache_port); 45 | if (!mysqlnd_memcache_set($link, $memc, NULL, "debug_callback")) { 46 | printf("[004] Failed to register connection, [%d] '%s'\n", 47 | $link->errno, $link->error); 48 | } 49 | 50 | if ($res = $link->query("SELECT f1, f2, f3 FROM mymem_test WHERE id = 'key2'")) { 51 | var_dump($res->fetch_assoc()); 52 | } else { 53 | printf("[005] %d %s\n", $link->errno, $link->error); 54 | } 55 | 56 | 57 | print "done!"; 58 | ?> 59 | --EXPECT-- 60 | array(3) { 61 | ["f1"]=> 62 | string(5) "Hu|hu" 63 | ["f2"]=> 64 | string(3) "bar" 65 | ["f3"]=> 66 | string(3) "baz" 67 | } 68 | debug_callback() 00: boolean / true 69 | array(4) { 70 | [0]=> 71 | string(2) "Hu" 72 | [1]=> 73 | string(2) "hu" 74 | [2]=> 75 | string(3) "bar" 76 | [3]=> 77 | string(3) "baz" 78 | } 79 | debug_callback() 00: boolean / false 80 | debug_callback() 00: boolean / false 81 | array(3) { 82 | ["f1"]=> 83 | NULL 84 | ["f2"]=> 85 | string(1) "|" 86 | ["f3"]=> 87 | NULL 88 | } 89 | debug_callback() 00: boolean / true 90 | array(0) { 91 | } 92 | done! -------------------------------------------------------------------------------- /tests/mymemc_api_basics_mysql.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Simple mysql test 3 | --SKIPIF-- 4 | 15 | --INI-- 16 | mysqlnd_memcache.enable=1 17 | --FILE-- 18 | $arg) { 25 | printf(" %02d: %s / %s\n", $k, gettype($arg), var_export($arg, true)); 26 | } 27 | } 28 | 29 | if (!$link = my_mysql_connect($host, $user, $passwd, $db, $port, $socket)) { 30 | printf("[001] [%d] %s\n", mysql_errno(), mysql_error()); 31 | } 32 | 33 | $memc = my_memcache_connect($memcache_host, $memcache_port); 34 | 35 | if (!mysqlnd_memcache_set($link, $memc, NULL, "debug_callback")) { 36 | printf("[002] Failed to register connection, [%d] '%s'\n", 37 | $link->errno, $link->error); 38 | } 39 | 40 | if (!($key1 = $memc->get("@@mymem_test.key1"))) { 41 | printf("[003] Failed to fetch 'key1' using native Memcache API.\n"); 42 | } 43 | $columns = explode("|", $key1); 44 | var_dump($columns); 45 | 46 | if (!$res = (mysql_query("SELECT f1, f2, f3 FROM mymem_test WHERE id = 'key1'", $link))) { 47 | printf("[004] [%d] %s\n", mysql_errno($link), mysql_error($link)); 48 | } else { 49 | $row = mysql_fetch_row($res); 50 | if ($columns != $row) { 51 | printf("[005] Native and SQL results differ\n"); 52 | var_dump(array_diff($columns, $row)); 53 | } 54 | 55 | /* more results ? */ 56 | var_dump(mysql_fetch_assoc($res)); 57 | 58 | } 59 | 60 | /* For the fun of it and for fetch_assoc */ 61 | if (!$res = (mysql_query("SELECT f1, f2, f3 FROM mymem_test WHERE id = 'key1'", $link))) { 62 | printf("[006] [%d] %s\n", mysql_errno($link), mysql_error($link)); 63 | } else { 64 | var_dump(mysql_fetch_assoc($res)); 65 | } 66 | print "done!"; 67 | ?> 68 | --EXPECT-- 69 | array(3) { 70 | [0]=> 71 | string(1) "a" 72 | [1]=> 73 | string(1) "b" 74 | [2]=> 75 | string(1) "c" 76 | } 77 | debug_callback() 00: boolean / true 78 | NULL 79 | debug_callback() 00: boolean / true 80 | array(3) { 81 | ["f1"]=> 82 | string(1) "a" 83 | ["f2"]=> 84 | string(1) "b" 85 | ["f3"]=> 86 | string(1) "c" 87 | } 88 | done! 89 | -------------------------------------------------------------------------------- /tests/mymemc_resultmeta_direct_access.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Resultset meta (fetch field direct) 3 | --SKIPIF-- 4 | 15 | --INI-- 16 | mysqlnd_memcache.enable=1 17 | --FILE-- 18 | $arg) { 27 | printf(" %02d: %s / %s\n", $k, gettype($arg), var_export($arg, true)); 28 | } 29 | } 30 | 31 | $link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket); 32 | if ($link->connect_errno) { 33 | printf("[001] [%d] %s\n", $link->connect_errno, $link->connect_error); 34 | } 35 | 36 | $memc = my_memcache_connect($memcache_host, $memcache_port); 37 | if (!mysqlnd_memcache_set($link, $memc, NULL, "debug_callback")) { 38 | printf("[002] Failed to register connection, [%d] '%s'\n", 39 | $link->errno, $link->error); 40 | } 41 | 42 | if ($res = $link->query("SELECT f1, f2, f3 FROM mymem_test WHERE id = 'key1'")) { 43 | $row = $res->fetch_row(); 44 | $fields_mapped = $res->fetch_fields(); 45 | } else { 46 | printf("[003] [%d] %s\n", $link->errno, $link->error); 47 | } 48 | 49 | if ($res = $link->query("SELECT f1, f2, f3 FROM mymem_test WHERE id = 'key1'")) { 50 | $row = $res->fetch_row(); 51 | $fields_direct = array(); 52 | for ($i = $res->field_count - 1; $i >= 0; $i--) { 53 | $fields_direct[$i] = $res->fetch_field_direct($i); 54 | } 55 | } else { 56 | printf("[004] [%d] %s\n", $link->errno, $link->error); 57 | } 58 | 59 | my_memcache_compare_meta(5, $fields_direct, $fields_mapped); 60 | 61 | if ($res = $link->query("SELECT f1, f2, f3 FROM mymem_test WHERE id = 'key1'")) { 62 | $row = $res->fetch_row(); 63 | $fields_direct = array(); 64 | for ($i = $res->field_count - 1; $i >= 0; $i--) { 65 | $res->field_seek($i); 66 | if ($res->current_field != $i) { 67 | printf("[006] Expecting %d got %d\n", $i, $res->current_field); 68 | } 69 | $fields_direct[$i] = $res->fetch_field(); 70 | } 71 | } else { 72 | printf("[007] [%d] %s\n", $link->errno, $link->error); 73 | } 74 | 75 | my_memcache_compare_meta(8, $fields_direct, $fields_mapped); 76 | 77 | print "done!"; 78 | ?> 79 | --EXPECT-- 80 | debug_callback() 00: boolean / true 81 | debug_callback() 00: boolean / true 82 | debug_callback() 00: boolean / true 83 | done! -------------------------------------------------------------------------------- /tests/mymemc_mysqlnd_memcache_get_config.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | mysqlnd_memcache_get_config() 3 | --SKIPIF-- 4 | 14 | --INI-- 15 | mysqlnd_memcache.enable=1 16 | --FILE-- 17 | 52 | array( 53 | "prefix" => "@@mymem_test.", 54 | "schema_name" => $db, 55 | "table_name" => "mymem_test", 56 | "id_field_name" => "id", 57 | "separator" => "|", 58 | "fields" => array("f1", "f2", "f3"), 59 | ) 60 | ); 61 | 62 | if (!isset($config['mappings'])) { 63 | printf("[006] No mappings entry. Dumping config.\n"); 64 | var_dump($config); 65 | } else { 66 | if ($config['mappings'] != $mappings) { 67 | printf("[007] Mappings differ. Dumping.\n"); 68 | var_dump($mappings); 69 | var_dump($config['mappings']); 70 | } 71 | unset($config['mappings']); 72 | } 73 | 74 | if (!isset($config['mapping_query'])) { 75 | printf("[008] No mapping_query entry. Dumping config.\n"); 76 | var_dump($config); 77 | } else { 78 | if (strlen($config['mapping_query']) < 20) { 79 | printf("[009] Mapping query is suspiciously short. Dumping config.\n"); 80 | var_dump($config); 81 | } 82 | unset($config['mapping_query']); 83 | } 84 | 85 | if (!empty($config)) { 86 | printf("[011] Dumping unexpected config elements\n"); 87 | var_dump($config); 88 | } 89 | 90 | print "done!"; 91 | ?> 92 | --EXPECT-- 93 | done! 94 | -------------------------------------------------------------------------------- /tests/mymemc_resultmeta_same_table.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Resultset meta - SQL vs. Memcache same table 3 | --SKIPIF-- 4 | 15 | --INI-- 16 | mysqlnd_memcache.enable=1 17 | --FILE-- 18 | $arg) { 27 | printf(" %02d: %s / %s\n", $k, gettype($arg), var_export($arg, true)); 28 | } 29 | } 30 | 31 | $link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket); 32 | if ($link->connect_errno) { 33 | printf("[001] [%d] %s\n", $link->connect_errno, $link->connect_error); 34 | } 35 | 36 | $memc = my_memcache_connect($memcache_host, $memcache_port); 37 | if (!mysqlnd_memcache_set($link, $memc, NULL, "debug_callback")) { 38 | printf("[002] Failed to register connection, [%d] '%s'\n", 39 | $link->errno, $link->error); 40 | } 41 | 42 | if ($res = $link->query("SELECT SQL_NO_CACHE f1, f2, f3 FROM mymem_test WHERE id = 'key1'")) { 43 | $fields_sql_shadow = $res->fetch_fields(); 44 | $res->free(); 45 | } else { 46 | printf("[003] [%d] %s\n", $link->errno, $link->error); 47 | } 48 | 49 | if ($res = $link->query("SELECT f1, f2, f3 FROM mymem_test WHERE id = 'key1'")) { 50 | $row = $res->fetch_row(); 51 | var_dump($row); 52 | 53 | $fields_mapped = $res->fetch_fields(); 54 | 55 | my_memcache_compare_meta(4, $fields_sql_shadow, $fields_mapped); 56 | 57 | } else { 58 | printf("[005] [%d] %s\n", $link->errno, $link->error); 59 | } 60 | 61 | print "done!"; 62 | ?> 63 | --XFAIL-- 64 | Meta incomplete, type looks suspicious, orgname missing 65 | --EXPECT-- 66 | debug_callback() 00: boolean / false 67 | debug_callback() 00: boolean / true 68 | array(3) { 69 | [0]=> 70 | string(1) "a" 71 | [1]=> 72 | string(1) "b" 73 | [2]=> 74 | string(1) "c" 75 | } 76 | [004] Field values for field 0 differ 77 | orgname: 'f1' != '' 78 | max_length: '%d' != '0' 79 | length: '%d' != '0' 80 | charsetnr: '%d' != '0' 81 | type: '253' != '254' 82 | [004] Field values for field 1 differ 83 | orgname: 'f2' != '' 84 | max_length: '%d' != '0' 85 | length: '%d' != '0' 86 | charsetnr: '%d' != '0' 87 | type: '253' != '254' 88 | [004] Field values for field 2 differ 89 | orgname: 'f3' != '' 90 | max_length: '%d' != '0' 91 | length: '%d' != '0' 92 | charsetnr: '%d' != '0' 93 | type: '253' != '254' 94 | done! 95 | -------------------------------------------------------------------------------- /tests/mymemc_unset_config.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Unsetting/Resetting mysqlnd_memcache data 3 | --SKIPIF-- 4 | 15 | --INI-- 16 | mysqlnd_memcache.enable=1 17 | --FILE-- 18 | connect_errno) { 27 | printf("[001] [%d] %s\n", $link->connect_errno, $link->connect_error); 28 | } 29 | 30 | $memc = my_memcache_connect($memcache_host, $memcache_port); 31 | 32 | echo "Setting mysqlnd_memcache:\n"; 33 | var_dump(mysqlnd_memcache_set($link, $memc, NULL, "callback")); 34 | 35 | echo "Querying SELECT f1 FROM mymem_test WHERE id = 'key1', then fetch_all:\n"; 36 | if ($r = $link->query("SELECT f1 FROM mymem_test WHERE id = 'key1'")) { 37 | var_dump($r->fetch_all()); 38 | } else { 39 | printf("[002] [%d] %s\n", $link->errno, $link->error); 40 | } 41 | 42 | echo "Unsetting mysqlnd_memcache:\n"; 43 | var_dump(mysqlnd_memcache_set($link, NULL)); 44 | 45 | echo "Querying SELECT f1 FROM mymem_test WHERE id = 'key1', then fetch_all:\n"; 46 | if ($r = $link->query("SELECT f1 FROM mymem_test WHERE id = 'key1'")) { 47 | var_dump($r->fetch_all()); 48 | } else { 49 | printf("[003] [%d] %s\n", $link->errno, $link->error); 50 | } 51 | 52 | echo "Resetting mysqlnd_memcache:\n"; 53 | var_dump(mysqlnd_memcache_set($link, $memc, NULL, "callback")); 54 | 55 | echo "Querying SELECT f1 FROM mymem_test WHERE id = 'key1', then fetch_all:\n"; 56 | if ($r = $link->query("SELECT f1 FROM mymem_test WHERE id = 'key1'")) { 57 | var_dump($r->fetch_all()); 58 | } else { 59 | printf("[004] [%d] %s\n", $link->errno, $link->error); 60 | } 61 | 62 | ?> 63 | --EXPECT-- 64 | Setting mysqlnd_memcache: 65 | bool(true) 66 | Querying SELECT f1 FROM mymem_test WHERE id = 'key1', then fetch_all: 67 | Went through memcache: Yes 68 | array(1) { 69 | [0]=> 70 | array(1) { 71 | [0]=> 72 | string(1) "a" 73 | } 74 | } 75 | Unsetting mysqlnd_memcache: 76 | bool(true) 77 | Querying SELECT f1 FROM mymem_test WHERE id = 'key1', then fetch_all: 78 | array(1) { 79 | [0]=> 80 | array(1) { 81 | [0]=> 82 | string(1) "a" 83 | } 84 | } 85 | Resetting mysqlnd_memcache: 86 | bool(true) 87 | Querying SELECT f1 FROM mymem_test WHERE id = 'key1', then fetch_all: 88 | Went through memcache: Yes 89 | array(1) { 90 | [0]=> 91 | array(1) { 92 | [0]=> 93 | string(1) "a" 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /tests/mymemc_resultmeta.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Resultset meta 3 | --SKIPIF-- 4 | 15 | --INI-- 16 | mysqlnd_memcache.enable=1 17 | --FILE-- 18 | $arg) { 27 | printf(" %02d: %s / %s\n", $k, gettype($arg), var_export($arg, true)); 28 | } 29 | } 30 | 31 | $link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket); 32 | if ($link->connect_errno) { 33 | printf("[001] [%d] %s\n", $link->connect_errno, $link->connect_error); 34 | } 35 | 36 | $memc = my_memcache_connect($memcache_host, $memcache_port); 37 | if (!mysqlnd_memcache_set($link, $memc, NULL, "debug_callback")) { 38 | printf("[002] Failed to register connection, [%d] '%s'\n", 39 | $link->errno, $link->error); 40 | } 41 | 42 | if ($res = $link->query("SELECT SQL_NO_CACHE f1, f2, f3 FROM mymem_test_meta WHERE id = 'key1'")) { 43 | $fields_sql_shadow = $res->fetch_fields(); 44 | $res->free(); 45 | } else { 46 | printf("[003] [%d] %s\n", $link->errno, $link->error); 47 | } 48 | 49 | if ($res = $link->query("SELECT f1, f2, f3 FROM mymem_test WHERE id = 'key1'")) { 50 | $row = $res->fetch_row(); 51 | var_dump($row); 52 | 53 | $fields_mapped = $res->fetch_fields(); 54 | 55 | my_memcache_compare_meta(4, $fields_sql_shadow, $fields_mapped); 56 | 57 | } else { 58 | printf("[005] [%d] %s\n", $link->errno, $link->error); 59 | } 60 | 61 | print "done!"; 62 | ?> 63 | --XFAIL-- 64 | Meta incomplete, type looks suspicious, orgname missing 65 | --EXPECT-- 66 | debug_callback() 00: boolean / false 67 | debug_callback() 00: boolean / true 68 | array(3) { 69 | [0]=> 70 | string(1) "a" 71 | [1]=> 72 | string(1) "b" 73 | [2]=> 74 | string(1) "c" 75 | } 76 | [004] Field values for field 0 differ 77 | orgname: 'f1' != '' 78 | table: 'mymem_test_meta' != 'mymem_test' 79 | orgtable: 'mymem_test_meta' != 'mymem_test' 80 | max_length: '%d' != '0' 81 | length: '%d' != '0' 82 | charsetnr: '%d' != '0' 83 | type: '253' != '254' 84 | [004] Field values for field 1 differ 85 | orgname: 'f2' != '' 86 | table: 'mymem_test_meta' != 'mymem_test' 87 | orgtable: 'mymem_test_meta' != 'mymem_test' 88 | max_length: '%d' != '0' 89 | length: '%d' != '0' 90 | charsetnr: '%d' != '0' 91 | type: '253' != '254' 92 | [004] Field values for field 2 differ 93 | orgname: 'f3' != '' 94 | table: 'mymem_test_meta' != 'mymem_test' 95 | orgtable: 'mymem_test_meta' != 'mymem_test' 96 | max_length: '%d' != '0' 97 | length: '%d' != '0' 98 | charsetnr: '%d' != '0' 99 | type: '253' != '254' 100 | done! -------------------------------------------------------------------------------- /tests/mymemc_seperator_clash.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Seperator clash (wrong results as expected) 3 | --SKIPIF-- 4 | 15 | --INI-- 16 | mysqlnd_memcache.enable=1 17 | --FILE-- 18 | $arg) { 25 | printf(" %02d: %s / %s\n", $k, gettype($arg), var_export($arg, true)); 26 | } 27 | } 28 | 29 | $link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket); 30 | if ($link->connect_errno) { 31 | printf("[001] [%d] %s\n", $link->connect_errno, $link->connect_error); 32 | } 33 | 34 | if (!$link->query("UPDATE mymem_test SET f1 = 'Hu|hu' WHERE id = 'key2'")) { 35 | printf("[002] [%d] %s\n", $link->errno, $link->error); 36 | } 37 | 38 | if (!($res = $link->query("SELECT SQL_NO_CACHE f1, f2, f3 FROM mymem_test WHERE id = 'key2'"))) { 39 | printf("[003] [%d] %s\n", $link->errno, $link->error); 40 | } 41 | $data = $res->fetch_assoc(); 42 | var_dump($data); 43 | 44 | $memc = my_memcache_connect($memcache_host, $memcache_port); 45 | if (!mysqlnd_memcache_set($link, $memc, NULL, "debug_callback")) { 46 | printf("[004] Failed to register connection, [%d] '%s'\n", 47 | $link->errno, $link->error); 48 | } 49 | 50 | if ($res = $link->query("SELECT f1, f2, f3 FROM mymem_test WHERE id = 'key2'")) { 51 | var_dump($res->fetch_row()); 52 | } else { 53 | printf("[005] %d %s\n", $link->errno, $link->error); 54 | } 55 | 56 | if (!$link->query("UPDATE mymem_test SET f1 = NULL, f2 = '|', f3 = NULL WHERE id = 'key3'")) { 57 | printf("[006] [%d] %s\n", $link->errno, $link->error); 58 | } 59 | 60 | if (!($res = $link->query("SELECT SQL_NO_CACHE f1, f2, f3 FROM mymem_test WHERE id = 'key3'"))) { 61 | printf("[007] [%d] %s\n", $link->errno, $link->error); 62 | } 63 | $data = $res->fetch_assoc(); 64 | var_dump($data); 65 | 66 | if ($res = $link->query("SELECT f1, f2, f3 FROM mymem_test WHERE id = 'key3'")) { 67 | var_dump($res->fetch_row()); 68 | } else { 69 | printf("[008] %d %s\n", $link->errno, $link->error); 70 | } 71 | 72 | 73 | print "done!"; 74 | ?> 75 | --EXPECT-- 76 | array(3) { 77 | ["f1"]=> 78 | string(5) "Hu|hu" 79 | ["f2"]=> 80 | string(3) "bar" 81 | ["f3"]=> 82 | string(3) "baz" 83 | } 84 | debug_callback() 00: boolean / true 85 | array(4) { 86 | [0]=> 87 | string(2) "Hu" 88 | [1]=> 89 | string(2) "hu" 90 | [2]=> 91 | string(3) "bar" 92 | [3]=> 93 | string(3) "baz" 94 | } 95 | debug_callback() 00: boolean / false 96 | debug_callback() 00: boolean / false 97 | array(3) { 98 | ["f1"]=> 99 | NULL 100 | ["f2"]=> 101 | string(1) "|" 102 | ["f3"]=> 103 | NULL 104 | } 105 | debug_callback() 00: boolean / true 106 | array(0) { 107 | } 108 | done! -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------- 2 | The PHP License, version 3.01 3 | Copyright (c) 1999 - 2010 The PHP Group. All rights reserved. 4 | -------------------------------------------------------------------- 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, is permitted provided that the following conditions 8 | are met: 9 | 10 | 1. Redistributions of source code must retain the above copyright 11 | notice, this list of conditions and the following disclaimer. 12 | 13 | 2. Redistributions in binary form must reproduce the above copyright 14 | notice, this list of conditions and the following disclaimer in 15 | the documentation and/or other materials provided with the 16 | distribution. 17 | 18 | 3. The name "PHP" must not be used to endorse or promote products 19 | derived from this software without prior written permission. For 20 | written permission, please contact group@php.net. 21 | 22 | 4. Products derived from this software may not be called "PHP", nor 23 | may "PHP" appear in their name, without prior written permission 24 | from group@php.net. You may indicate that your software works in 25 | conjunction with PHP by saying "Foo for PHP" instead of calling 26 | it "PHP Foo" or "phpfoo" 27 | 28 | 5. The PHP Group may publish revised and/or new versions of the 29 | license from time to time. Each version will be given a 30 | distinguishing version number. 31 | Once covered code has been published under a particular version 32 | of the license, you may always continue to use it under the terms 33 | of that version. You may also choose to use such covered code 34 | under the terms of any subsequent version of the license 35 | published by the PHP Group. No one other than the PHP Group has 36 | the right to modify the terms applicable to covered code created 37 | under this License. 38 | 39 | 6. Redistributions of any form whatsoever must retain the following 40 | acknowledgment: 41 | "This product includes PHP software, freely available from 42 | ". 43 | 44 | THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND 45 | ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 46 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 47 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PHP 48 | DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 49 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 50 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 51 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 52 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 53 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 54 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 55 | OF THE POSSIBILITY OF SUCH DAMAGE. 56 | 57 | -------------------------------------------------------------------- 58 | 59 | This software consists of voluntary contributions made by many 60 | individuals on behalf of the PHP Group. 61 | 62 | The PHP Group can be contacted via Email at group@php.net. 63 | 64 | For more information on the PHP Group and the PHP project, 65 | please see . 66 | 67 | PHP includes the Zend Engine, freely available at 68 | . 69 | -------------------------------------------------------------------------------- /tests/mymemc_memcache_set_debug_cb.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | mysqlnd_memcache_set(), params 3 | --SKIPIF-- 4 | 14 | --INI-- 15 | mysqlnd_memcache.enable=1 16 | --FILE-- 17 | $arg) { 24 | printf(" %02d: %s / %s\n", $k, gettype($arg), var_export($arg, true)); 25 | } 26 | } 27 | 28 | $link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket); 29 | if ($link->connect_errno) { 30 | printf("[001] [%d] %s\n", $link->connect_errno, $link->connect_error); 31 | } 32 | 33 | $memc = my_memcache_connect($memcache_host, $memcache_port); 34 | if (!mysqlnd_memcache_set($link, $memc, NULL, "debug_callback")) { 35 | printf("[002] Failed to register connection, [%d] '%s'\n", 36 | $link->errno, $link->error); 37 | } 38 | 39 | /* No mem access, simple function callback */ 40 | if ($res = $link->query("SELECT f1 FROM mymem_test")) { 41 | $res->free_result(); 42 | } else { 43 | printf("[003] [%d] %s\n", $link->errno, $link->error); 44 | } 45 | 46 | class debug { 47 | public static function static_db() { 48 | printf("%s()", __FUNCTION__); 49 | $args = func_get_args(); 50 | foreach ($args as $k => $arg) { 51 | printf(" %02d: %s / %s\n", $k, gettype($arg), var_export($arg, true)); 52 | } 53 | } 54 | public function cb() { 55 | printf("%s()", __FUNCTION__); 56 | $args = func_get_args(); 57 | foreach ($args as $k => $arg) { 58 | printf(" %02d: %s / %s\n", $k, gettype($arg), var_export($arg, true)); 59 | } 60 | } 61 | public function __call($a, $b) { 62 | printf("%s()", __FUNCTION__); 63 | $args = $b; 64 | foreach ($args as $k => $arg) { 65 | printf(" %02d: %s / %s\n", $k, gettype($arg), var_export($arg, true)); 66 | } 67 | } 68 | } 69 | 70 | /* static class function */ 71 | if (!mysqlnd_memcache_set($link, $memc, NULL, "debug::static_db")) { 72 | printf("[003] Failed to register connection, [%d] '%s'\n", 73 | $link->errno, $link->error); 74 | } 75 | 76 | if ($res = $link->query("SELECT f1, f2 FROM mymem_test")) { 77 | $res->free_result(); 78 | } else { 79 | printf("[004] [%d] %s\n", $link->errno, $link->error); 80 | } 81 | 82 | 83 | $d = new debug(); 84 | if (!mysqlnd_memcache_set($link, $memc, NULL, array($d, "cb"))) { 85 | printf("[005] Failed to register connection, [%d] '%s'\n", 86 | $link->errno, $link->error); 87 | } 88 | 89 | if ($res = $link->query("SELECT f1, f2 FROM mymem_test")) { 90 | $res->free_result(); 91 | } else { 92 | printf("[006] [%d] %s\n", $link->errno, $link->error); 93 | } 94 | 95 | if (!mysqlnd_memcache_set($link, $memc, NULL, array($d, "gurkensalat"))) { 96 | printf("[007] Failed to register connection, [%d] '%s'\n", 97 | $link->errno, $link->error); 98 | } 99 | 100 | if ($res = $link->query("SELECT 1 FROM DUAL")) { 101 | $res->free_result(); 102 | } else { 103 | printf("[008] [%d] %s\n", $link->errno, $link->error); 104 | } 105 | 106 | /* anonymous */ 107 | if (!mysqlnd_memcache_set($link, $memc, NULL, function ($bar) { echo "bar = '$bar'\n"; })) { 108 | printf("[009] Failed to register connection, [%d] '%s'\n", 109 | $link->errno, $link->error); 110 | } 111 | 112 | if ($res = $link->query("SELECT 1 FROM DUAL")) { 113 | $res->free_result(); 114 | } else { 115 | printf("[010] [%d] %s\n", $link->errno, $link->error); 116 | } 117 | print "done!"; 118 | ?> 119 | --EXPECTF-- 120 | debug_callback() 00: boolean / false 121 | static_db() 00: boolean / false 122 | cb() 00: boolean / false 123 | __call() 00: boolean / false 124 | bar = '' 125 | done! -------------------------------------------------------------------------------- /tests/connect.inc: -------------------------------------------------------------------------------- 1 | query("set session TRANSACTION ISOLATION LEVEL read uncommitted"); 50 | } 51 | 52 | return $link; 53 | } 54 | 55 | /** 56 | * Whenever possible, please use this wrapper to make testing ot MYSQLI_CLIENT_COMPRESS (and potentially SSL) possible 57 | * 58 | * @param enable_env_flags Enable setting of connection flags through env(MYSQL_TEST_CONNECT_FLAGS) 59 | */ 60 | function my_mysqli_real_connect($link, $host, $user, $passwd, $db, $port, $socket, $flags = 0, $enable_env_flags = true) { 61 | global $connect_flags; 62 | 63 | if ($enable_env_flags) 64 | $flags & $connect_flags; 65 | 66 | return mysqli_real_connect($link, $host, $user, $passwd, $db, $port, $socket, $flags); 67 | } 68 | 69 | class my_mysqli extends mysqli { 70 | public function __construct($host, $user, $passwd, $db, $port, $socket, $enable_env_flags = true) { 71 | global $connect_flags; 72 | 73 | $flags = ($enable_env_flags) ? $connect_flags : false; 74 | 75 | if ($flags !== false) { 76 | parent::init(); 77 | $this->real_connect($host, $user, $passwd, $db, $port, $socket, $flags); 78 | } else { 79 | parent::__construct($host, $user, $passwd, $db, $port, $socket); 80 | } 81 | $this->query("set session TRANSACTION ISOLATION LEVEL read uncommitted"); 82 | } 83 | } 84 | 85 | function my_memcache_connect($host, $port) { 86 | $memc = new Memcached(); 87 | $memc->addServer($host, $port); 88 | return $memc; 89 | } 90 | 91 | function my_error_handler($errno, $error, $file, $line) { 92 | /* KLUDGE */ 93 | static $errcodes = array(); 94 | 95 | if (empty($errcodes)) { 96 | $constants = get_defined_constants(); 97 | foreach ($constants as $name => $value) { 98 | if (substr($name, 0, 2) == "E_") 99 | $errcodes[$value] = $name; 100 | } 101 | } 102 | $msg = sprintf("[%s] %s in %s on line %s\n", 103 | (isset($errcodes[$errno])) ? $errcodes[$errno] : $errno, 104 | $error, $file, $line); 105 | 106 | echo $msg; 107 | 108 | return true; 109 | } 110 | 111 | } else { 112 | printf("skip Eeeek/BUG/FIXME I - connect.inc included twice! skipif bug?\n"); 113 | } 114 | 115 | if (extension_loaded('mysql')) { 116 | if (!function_exists('my_mysql_connect')) { 117 | /* wrapper to simplify test porting */ 118 | function my_mysql_connect($host, $user, $passwd, $db, $port, $socket, $flags = NULL, $persistent = false) { 119 | global $connect_flags; 120 | 121 | $flags = ($flags === NULL) ? $connect_flags : $flags; 122 | 123 | if ($socket) { 124 | $host = sprintf("%s:%s", $host, $socket); 125 | } else if ($port) { 126 | $host = sprintf("%s:%s", $host, $port); 127 | } 128 | if ($persistent) { 129 | $link = mysql_pconnect($host, $user, $passwd, $flags); 130 | } else { 131 | $link = mysql_connect($host, $user, $passwd, true, $flags); 132 | } 133 | 134 | if (!$link) { 135 | printf("[000-a] Cannot connect using host '%s', user '%s', password '****', persistent = %d, [%d] %s\n", 136 | $host, $user, ($persistent) ? 1 : 0, 137 | mysql_errno(), mysql_error()); 138 | return false; 139 | } 140 | 141 | if (!mysql_select_db($db, $link)) { 142 | printf("[000-b] [%d] %s\n", mysql_errno($link), mysql_error($link)); 143 | return false; 144 | } 145 | mysql_query("set session TRANSACTION ISOLATION LEVEL read uncommitted", $link); 146 | 147 | return $link; 148 | } 149 | } else { 150 | printf("skip Eeeek/BUG/FIXME II - connect.inc included twice! skipif bug?\n"); 151 | } 152 | } 153 | 154 | if (extension_loaded('pdo_mysql')) { 155 | function my_pdo_connect($host, $user, $passwd, $db, $port, $socket, $flags = NULL, $persistent = false) { 156 | $pdo = new PDO("mysql:hostname=$host;unix_socket=$socket;port=$port;dbname=$db", $user, $passwd); 157 | $pdo->query("set session TRANSACTION ISOLATION LEVEL read uncommitted"); 158 | return $pdo; 159 | } 160 | } 161 | 162 | 163 | ?> 164 | -------------------------------------------------------------------------------- /package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | mysqlnd_memcache 4 | pecl.php.net 5 | A PHP extension for transparently translating SQL into requests for the MySQL InnoDB Memcached Daemon Plugin 6 | The mysqlnd memcache plugin (mysqlnd_memcache) is an PHP extension for transparently translating SQL into requests for the MySQL InnoDB Memcached Daemon Plugin (server plugin). It includes experimental support for the MySQL Cluster Memcached Daemon. The server plugin provides access to data stored inside MySQL InnoDB (respectively MySQL Cluster NDB) tables using the Memcache protocol. This PHP extension, which supports all PHP MySQL extensions that use mysqlnd, will identify tables exported in this way and will translate specific SELECT queries into Memcache requests. 7 | 8 | Johannes Schlüter 9 | johannes 10 | johannes@php.net 11 | yes 12 | 13 | 14 | Andrey Hristov 15 | andrey 16 | andrey@php.net 17 | yes 18 | 19 | 20 | Ulf Wendel 21 | uw 22 | uw@php_net 23 | yes 24 | 25 | 2013-07-26 26 | 27 | 28 | 1.0.1 29 | 1.0.0 30 | 31 | 32 | stable 33 | stable 34 | 35 | PHP License 36 | 37 | Fixed build issues. 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 5.4.0 106 | 107 | 108 | 1.4.0 109 | 110 | 111 | 112 | mysqlnd_memcache 113 | 114 | 115 | 116 | 117 | 1.0.1 118 | 1.0.0 119 | 120 | 121 | stable 122 | stable 123 | 124 | 2013-07-18 125 | PHP License 126 | 127 | Fixed build issues 128 | 129 | 130 | 131 | 132 | 1.0.0 133 | 1.0.0 134 | 135 | 136 | beta 137 | beta 138 | 139 | 2012-09-27 140 | PHP License 141 | 142 | Initial package release. 143 | 144 | 145 | 146 | 147 | -------------------------------------------------------------------------------- /tests/table.inc: -------------------------------------------------------------------------------- 1 | create_test_table($id_is_string))) 16 | return $ret; 17 | 18 | if (is_string($res = self::$my_memcache_config->update_container($value_columns, $separator))) 19 | return $ret; 20 | 21 | return true; 22 | } 23 | 24 | public function connect($link = NULL) { 25 | global $host, $user, $passwd, $db, $port, $socket; 26 | 27 | if (!$link && !$this->link) { 28 | $link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket); 29 | if (mysqli_connect_errno()) { 30 | return sprintf("[%d] %s\n", mysqli_connect_errno(), mysqli_connect_error()); 31 | } 32 | $this->link = $link; 33 | } 34 | 35 | return true; 36 | } 37 | 38 | public function create_test_table($id_is_string = false, $max_retry = 1) { 39 | if ($ret = $this->_create_test_table("mymem_test", $id_is_string, $max_retry)) { 40 | $ret = $this->_create_test_table("mymem_test_meta", $id_is_string, $max_retry); 41 | } 42 | return $ret; 43 | } 44 | 45 | protected function _create_test_table($table_name, $id_is_string = false, $max_retry = 1) { 46 | 47 | $this->connect(); 48 | $sql = sprintf("DROP TABLE IF EXISTS %s", $this->link->real_escape_string($table_name)); 49 | if (!$this->link->query($sql)) { 50 | printf("[%d] %s\n", $this->link->errno, $this->link->error); 51 | return false; 52 | } 53 | 54 | $sql = sprintf("CREATE TABLE %s( 55 | id %s, 56 | f1 VARCHAR(255), 57 | f2 VARCHAR(255), 58 | f3 VARCHAR(255), 59 | flags INT NOT NULL, 60 | cas_column INT, 61 | expire_time_column INT, 62 | PRIMARY KEY(id)) ENGINE=InnoDB", 63 | $this->link->real_escape_string($table_name), 64 | ($id_is_string) ? "CHAR(255)" : "INT"); 65 | 66 | if (!$this->link->query($sql)) { 67 | 68 | if (($max_retry > 0) && (1050 == $this->link->errno)) { 69 | /* 70 | In the early days InnoDB did a background drop and it required 71 | a plugin reload to get re-create a table 72 | */ 73 | if (is_string($ret = $this->reload_plugin())) 74 | return $ret; 75 | 76 | $max_retry--; 77 | return $this->_create_test_table($table_name, $id_is_string, $max_retry); 78 | } 79 | return sprintf("(%s) [%d] %s\n", $sql, $this->link->errno, $this->link->error); 80 | } 81 | 82 | if ($id_is_string) { 83 | $sql = sprintf("INSERT INTO %s(id, f1, f2, f3) VALUES 84 | ('key1', 'a', 'b', 'c'), 85 | ('key2', 'foo', 'bar', 'baz'), 86 | ('key3', 'der Hund', 'liegt in der Kueche', 'und bellt')", 87 | $this->link->real_escape_string($table_name)); 88 | } else { 89 | $sql = sprintf("INSERT INTO %s(id, f1, f2, f3) VALUES 90 | (1, 'a', 'b', 'c'), 91 | (2, 'foo', 'bar', 'baz'), 92 | (3, 'der Hund', 'liegt in der Kueche', 'und bellt')", 93 | $this->link->real_escape_string($table_name)); 94 | } 95 | 96 | if (!$this->link->query($sql)) { 97 | return sprintf("(%s) [%d] %s\n", $sql, $this->link->errno, $this->link->error); 98 | } 99 | 100 | return true; 101 | } 102 | 103 | public function update_container($value_columns, $separator = '|', $table_map_delimiter = '.') { 104 | global $db; 105 | 106 | $this->connect(); 107 | 108 | $plugin_loaded = false; 109 | if (!($res = $this->link->query("SHOW PLUGINS"))) { 110 | return sprintf("SHOW PLUGINS failed, [%d] %s\n", $this->link->errno, $this->link->error); 111 | } 112 | while ($row = $res->fetch_assoc()) { 113 | if (($row['Name'] == 'daemon_memcached') && ($row['Status'] == 'ACTIVE')) { 114 | $plugin_loaded = true; 115 | break; 116 | } 117 | } 118 | 119 | if ($plugin_loaded) { 120 | $update_needed = $this->container_needs_update($value_columns, $separator); 121 | if (is_string($update_needed)) { 122 | /* error */ 123 | return $update_needed; 124 | } else if (false === $update_needed) { 125 | /* Yeah, we don't need to update the config and reload the plugin */ 126 | return true; 127 | } 128 | } 129 | 130 | /* Bad luck... */ 131 | $sql = sprintf("REPLACE INTO %s.containers( 132 | name, db_schema, db_table, key_columns, 133 | value_columns, flags, cas_column, 134 | expire_time_column, unique_idx_name_on_key 135 | ) VALUES ( 136 | 'mymem_test', '%s', 'mymem_test', 'id', 137 | '%s', 'flags', 'cas_column', 138 | 'expire_time_column', 'PRIMARY' 139 | )", 140 | $this->link->real_escape_string($this->innodb_schema), 141 | $this->link->real_escape_string($db), 142 | $this->link->real_escape_string(implode(',', $value_columns))); 143 | 144 | if (!$this->link->query($sql)) { 145 | return sprintf("%s, [%d] %s\n", $sql, $this->link->errno, $this->link->error); 146 | } 147 | 148 | /* early plugin version */ 149 | $sql = sprintf("REPLACE INTO %s.config_options(name, value) VALUES ('separator', '%s')", 150 | $this->link->real_escape_string($this->innodb_schema), 151 | $this->link->real_escape_string($separator)); 152 | 153 | if (!$this->link->query($sql)) { 154 | return sprintf("%s, [%d] %s\n", $sql, $this->link->errno, $this->link->error); 155 | } 156 | 157 | /* later plugin version */ 158 | $sql = sprintf("REPLACE INTO %s.config_options(name, value) VALUES ('table_map_delimiter', '%s')", 159 | $this->link->real_escape_string($this->innodb_schema), 160 | $this->link->real_escape_string($table_map_delimiter)); 161 | 162 | if (!$this->link->query($sql)) { 163 | return sprintf("%s, [%d] %s\n", $sql, $this->link->errno, $this->link->error); 164 | } 165 | 166 | return $this->reload_plugin(); 167 | } 168 | 169 | 170 | public function reload_plugin() { 171 | /* As of today, there seems no other way to notify the plugin of reloading its config */ 172 | $this->link->query("UNINSTALL PLUGIN daemon_memcached"); 173 | sleep(1); 174 | 175 | if (!$this->link->query("INSTALL PLUGIN daemon_memcached SONAME 'libmemcached.so'")) { 176 | return sprintf("INSTALL PLUGIN failed, [%d] %s\n", $this->link->errno, $this->link->error); 177 | } 178 | 179 | $plugin_loaded = false; 180 | if (!($res = $this->link->query("SHOW PLUGINS"))) { 181 | return sprintf("SHOW PLUGINS failed, [%d] %s\n", $this->link->errno, $this->link->error); 182 | } 183 | while ($row = $res->fetch_assoc()) { 184 | if (($row['Name'] == 'daemon_memcached') && ($row['Status'] == 'ACTIVE')) { 185 | $plugin_loaded = true; 186 | break; 187 | } 188 | } 189 | 190 | if (!$plugin_loaded) 191 | return sprintf("Failed to install pluginn"); 192 | 193 | return $plugin_loaded; 194 | } 195 | 196 | public function container_needs_update($value_columns, $separator = '|', $table_map_delimiter = '.') { 197 | global $db; 198 | 199 | $this->connect(); 200 | 201 | $update_needed = true; 202 | 203 | /* Are we lucky and columns are already configured...? */ 204 | $sql = sprintf("SELECT value_columns FROM %s.containers WHERE 205 | name = 'mymem_test' AND 206 | db_schema = '%s' AND 207 | db_table = 'mymem_test' AND 208 | key_columns = 'id' AND 209 | flags = 'flags' AND 210 | cas_column = 'cas_column' AND 211 | expire_time_column = 'expire_time_column' AND 212 | unique_idx_name_on_key = 'PRIMARY'", 213 | $this->link->real_escape_string($this->innodb_schema), 214 | $this->link->real_escape_string($db)); 215 | 216 | if ($res = $this->link->query($sql)) { 217 | $row = $res->fetch_assoc(); 218 | $columns = explode(",", $row['value_columns']); 219 | foreach ($columns as $k => $column) { 220 | $column = trim($column); 221 | foreach ($value_columns as $vk => $vcolumn) { 222 | $vcolumn = trim($vcolumn); 223 | if ($vcolumn == $column) { 224 | unset($columns[$k]); 225 | unset($value_columns[$vk]); 226 | } 227 | } 228 | } 229 | $update_needed = (empty($columns) && empty($value_columns)) ? false : true; 230 | } else if ($this->link->errno) { 231 | return sprintf("%s, [%d] %s\n", $sql, $this->link->errno, $this->link->error); 232 | } 233 | 234 | if (!$update_needed) { 235 | /* Are we lucky and separator is already configured...? */ 236 | $sql = sprintf("SELECT value FROM %s.config_options WHERE name = 'separator'", 237 | $this->link->real_escape_string($this->innodb_schema)); 238 | 239 | if ($res = $this->link->query($sql)) { 240 | $row = $res->fetch_assoc(); 241 | $update_needed = ($row['value'] == $separator) ? false : true; 242 | } else if ($this->link->errno) { 243 | return sprintf("%s, [%d] %s\n", $sql, $this->link->errno, $this->link->error); 244 | } 245 | 246 | 247 | $sql = sprintf("SELECT value FROM %s.config_options WHERE name = 'table_map_delimiter'", 248 | $this->link->real_escape_string($this->innodb_schema)); 249 | 250 | if ($res = $this->link->query($sql)) { 251 | $row = $res->fetch_assoc(); 252 | $update_needed = ($row['value'] == $table_map_delimiter) ? false : true; 253 | } else if ($this->link->errno) { 254 | return sprintf("%s, [%d] %s\n", $sql, $this->link->errno, $this->link->error); 255 | } 256 | 257 | } 258 | 259 | return $update_needed; 260 | } 261 | 262 | } 263 | -------------------------------------------------------------------------------- /mysqlnd_memcache.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PECL mysqlnd_memcache | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 2012-2013 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: Johannes Schlüter | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | /* $Id$ */ 20 | 21 | /* {{{ includes */ 22 | #ifdef HAVE_CONFIG_H 23 | #include "config.h" 24 | #endif 25 | 26 | #include "php.h" 27 | #include "php_ini.h" 28 | #include "ext/standard/info.h" 29 | #include "ext/standard/php_versioning.h" 30 | #include "ext/mysqlnd/mysqlnd.h" 31 | #include "ext/mysqlnd/mysqlnd_debug.h" 32 | #include "ext/mysqlnd/mysqlnd_result.h" 33 | #include "ext/mysqlnd/mysqlnd_ext_plugin.h" 34 | #include "ext/mysqlnd/mysqlnd_priv.h" 35 | #include "ext/mysqlnd/mysqlnd_alloc.h" 36 | #include "ext/mysqlnd/mysqlnd_reverse_api.h" 37 | #include "Zend/zend_interfaces.h" 38 | #include "php_mysqlnd_memcache.h" 39 | #include "ext/pcre/php_pcre.h" 40 | #include "libmemcached/memcached.h" 41 | /* }}} */ 42 | 43 | 44 | ZEND_BEGIN_MODULE_GLOBALS(mysqlnd_memcache) 45 | zend_bool enable; 46 | ZEND_END_MODULE_GLOBALS(mysqlnd_memcache) 47 | 48 | #ifdef ZTS 49 | #define MYSQLND_MEMCACHE_G(v) TSRMG(mysqlnd_memcache_globals_id, zend_mysqlnd_memcache_globals *, v) 50 | #else 51 | #define MYSQLND_MEMCACHE_G(v) (mysqlnd_memcache_globals.v) 52 | #endif 53 | 54 | ZEND_DECLARE_MODULE_GLOBALS(mysqlnd_memcache) 55 | 56 | 57 | static unsigned int mysqlnd_memcache_plugin_id; 58 | 59 | static func_mysqlnd_conn_data__query orig_mysqlnd_conn_data_query; 60 | static func_mysqlnd_conn__close orig_mysqlnd_conn_close; 61 | static func_mysqlnd_conn_data__end_psession orig_mysqlnd_conn_data_end_psession; 62 | 63 | #define SQL_FIELD_LIST "(.+?)" 64 | #define SQL_IDENTIFIER "`?([a-z0-9_]+)`?" 65 | #define SQL_STRING "[\"']([^\"']*)[\"']" 66 | #define SQL_NUMBER "([0-9e\\.]*)" 67 | #define SQL_PATTERN "/^\\s*SELECT\\s*" SQL_FIELD_LIST "\\s*FROM\\s*" SQL_IDENTIFIER "\\s*WHERE\\s*" SQL_IDENTIFIER "\\s*=\\s*(?(?=[\"'])" SQL_STRING "|" SQL_NUMBER ")\\s*$/is" 68 | #define SQL_PATTERN_LEN (sizeof(SQL_PATTERN)-1) 69 | 70 | /* {{{ QUERIES */ 71 | #define MAPPING_DECISION_QUERY "SELECT TABLE_SCHEMA " \ 72 | " FROM INFORMATION_SCHEMA.TABLES " \ 73 | " WHERE TABLE_NAME = 'containers' "\ 74 | " AND TABLE_SCHEMA IN ('innodb_memcache', 'ndbmemcache')" 75 | 76 | const char *MAPPING_QUERY_INNODB = " SELECT c.name, " 77 | " CONCAT('@@', c.name, (SELECT value FROM innodb_memcache.config_options WHERE name = 'table_map_delimiter')) AS key_prefix, " 78 | " c.db_schema, " 79 | " c.db_table, " 80 | " c.key_columns, " 81 | " c.value_columns, " 82 | " (SELECT value FROM innodb_memcache.config_options WHERE name = 'separator') AS sep " 83 | " FROM innodb_memcache.containers c "; 84 | 85 | const char *MAPPING_QUERY_NDB = " SELECT c.name, p.key_prefix, c.db_schema, c.db_table, c.key_columns, c.value_columns, '|' separator " 86 | " FROM ndbmemcache.containers c " 87 | " LEFT JOIN ndbmemcache.key_prefixes p " 88 | " ON p.container = c.name"; 89 | /* }}} */ 90 | 91 | /* {{{ STRUCTURES */ 92 | typedef struct { 93 | char *name; 94 | char *prefix; 95 | char *schema_name; 96 | char *table_name; 97 | char *id_field_name; 98 | struct { 99 | int num; 100 | char *to_free; 101 | char **v; 102 | } value_columns; 103 | char *separator; 104 | } mymem_mapping; 105 | 106 | typedef struct { 107 | struct { 108 | zval *zv; 109 | memcached_st *memc; 110 | } connection; 111 | struct { 112 | char *str; /* TODO: Actually the compiled and cached pattern is enough, but let's keep str, len and str_is_allocated for now to ease debugging */ 113 | int len; 114 | pcre_cache_entry* pattern; 115 | int str_is_allocated; 116 | } regexp; 117 | HashTable mapping; 118 | const char *mapping_query; 119 | struct { 120 | zend_fcall_info fci; 121 | zend_fcall_info_cache fcc; 122 | zend_bool exists; 123 | } callback; 124 | } mymem_connection_data_data; 125 | 126 | typedef struct { 127 | char *data; 128 | size_t data_len; 129 | int read; 130 | MYSQLND_FIELD *fields; 131 | MYSQLND_FIELD_OFFSET current_field_offset; 132 | unsigned long *lengths; 133 | mymem_mapping *mapping; 134 | } mymem_result_data; 135 | /* }}} */ 136 | 137 | /* {{{ php-memcache interface */ 138 | /* 139 | * I'd prefer having those exported from php-memcached. 140 | */ 141 | typedef struct { 142 | zend_object zo; 143 | struct { 144 | memcached_st *memc; 145 | } *obj; 146 | } fake_memcached_object; 147 | 148 | static zend_class_entry **memcached_ce = NULL; 149 | /* }}} */ 150 | 151 | static int count_char(char *pos, char v) /* {{{ */ 152 | { 153 | int retval = 0; 154 | 155 | while (*pos) { 156 | if (*pos == v) { 157 | retval++; 158 | } 159 | pos++; 160 | } 161 | 162 | return retval; 163 | } 164 | /* }}} */ 165 | 166 | 167 | #define BAILOUT_IF_CONN_DATA_UNSET(connection_data) \ 168 | if (UNEXPECTED(!connection_data)) { \ 169 | php_error_docref(NULL TSRMLS_CC, E_ERROR, "Connection data was unset but result set using it still exists"); \ 170 | } 171 | 172 | 173 | /* {{{ MYSQLND_MEMCACHE_RESULT */ 174 | static enum_func_status mymem_result_fetch_row(MYSQLND_RES *result, void *param, unsigned int flags, zend_bool *fetched_anything TSRMLS_DC) /* {{{ */ 175 | { 176 | // zval *row = (zval *) param; 177 | // zval *data; 178 | mymem_connection_data_data *connection_data = *mysqlnd_plugin_get_plugin_connection_data_data(result->conn, mysqlnd_memcache_plugin_id); 179 | mymem_result_data *result_data = *(mymem_result_data **)mysqlnd_plugin_get_plugin_result_data(result, mysqlnd_memcache_plugin_id); 180 | DBG_ENTER("mymem_result_fetch_row"); 181 | 182 | 183 | BAILOUT_IF_CONN_DATA_UNSET(connection_data) 184 | 185 | php_error_docref(NULL TSRMLS_CC, E_ERROR, "fetch_row in %s:%d is currently not implemented", __FILE__, __LINE__); 186 | 187 | if (result_data->read) { 188 | DBG_INF("Already fetched"); 189 | DBG_RETURN(FAIL); 190 | } 191 | 192 | result_data->read = 1; 193 | 194 | if (!param) { 195 | DBG_INF("No param"); 196 | DBG_RETURN(FAIL); 197 | } 198 | 199 | // pcre_cache_entry *pattern = pcre_get_compiled_regex_cache("/\\W*,\\W*/", sizeof("/\\W*,\\W*/")-1 TSRMLS_CC); 200 | // php_pcre_split_impl(pattern, result_data->data, result_data->data_len, row, 0, 0 TSRMLS_CC); 201 | /* 202 | ALLOC_INIT(data); 203 | ZVAL_STRINGL(data, result_data->data, result_data->data_len, 1); 204 | if (flags & MYSQLND_FETCH_NUM) { 205 | Z_ADDREF_P(data); 206 | zend_hash_next_index_insert(Z_ARRVAL_P(row), &data, sizeof(zval *), NULL); 207 | } 208 | if (flags & MYSQLND_FETCH_ASSOC) { 209 | Z_ADDREF_P(data); 210 | zend_hash_add(Z_ARRVAL_P(row), connection_data->mapping.id_field_name, strlen(connection_data->mapping.id_field_name)+1, &data, sizeof(zval *), NULL); 211 | } 212 | */ 213 | *fetched_anything = TRUE; 214 | 215 | DBG_RETURN(PASS); 216 | } 217 | /* }}} */ 218 | 219 | static MYSQLND_RES *mymem_result_use_result(MYSQLND_RES *const result, zend_bool ps_protocol TSRMLS_DC) /* {{{ */ 220 | { 221 | DBG_ENTER("mymem_result_use_result"); 222 | if (UNEXPECTED(ps_protocol)) { 223 | php_error_docref(NULL TSRMLS_CC, E_ERROR, "mysqlnd_memcache store result called with ps_protocol, not expected, bailing out"); 224 | } 225 | DBG_RETURN(result); 226 | } 227 | /* }}} */ 228 | 229 | static MYSQLND_RES *mymem_result_store_result(MYSQLND_RES *result, MYSQLND_CONN_DATA *conn, zend_bool ps_protocol TSRMLS_DC) /* {{{ */ 230 | { 231 | mymem_connection_data_data *connection_data = *mysqlnd_plugin_get_plugin_connection_data_data(conn, mysqlnd_memcache_plugin_id); 232 | BAILOUT_IF_CONN_DATA_UNSET(connection_data) 233 | if (UNEXPECTED(ps_protocol)) { 234 | php_error_docref(NULL TSRMLS_CC, E_ERROR, "mysqlnd_memcache store result called with ps_protocol, not expected, bailing out"); 235 | } 236 | return result; 237 | } 238 | /* }}} */ 239 | 240 | static void mymem_result_fetch_into(MYSQLND_RES *result, unsigned int flags, zval *return_value, enum_mysqlnd_extension ext TSRMLS_DC ZEND_FILE_LINE_DC) /* {{{ */ 241 | { 242 | mymem_connection_data_data *connection_data = *mysqlnd_plugin_get_plugin_connection_data_data(result->conn, mysqlnd_memcache_plugin_id); 243 | mymem_result_data *result_data = *(mymem_result_data **)mysqlnd_plugin_get_plugin_result_data(result, mysqlnd_memcache_plugin_id); 244 | 245 | int i = 0; 246 | zval *data; 247 | char *raw_data; 248 | char *value, *value_lasts; 249 | DBG_ENTER("mymem_result_fetch_into"); 250 | 251 | BAILOUT_IF_CONN_DATA_UNSET(connection_data) 252 | 253 | if (result_data->read || !result_data->data) { 254 | DBG_INF("Data already fetched or no data"); 255 | DBG_VOID_RETURN; 256 | } 257 | 258 | result_data->read = 1; 259 | 260 | /* We need this copy as strtok_r changes the data and seek might bring us back here*/ 261 | raw_data = estrndup(result_data->data, result_data->data_len); 262 | DBG_INF_FMT("raw_data len %u, separator '%s'", (raw_data) ? strlen(raw_data) : 0, result_data->mapping->separator); 263 | 264 | array_init(return_value); 265 | 266 | if (*raw_data == *result_data->mapping->separator) { 267 | ALLOC_INIT_ZVAL(data); 268 | ZVAL_EMPTY_STRING(data); 269 | if (flags & MYSQLND_FETCH_NUM) { 270 | Z_ADDREF_P(data); 271 | zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &data, sizeof(zval *), NULL); 272 | } 273 | if (flags & MYSQLND_FETCH_ASSOC) { 274 | Z_ADDREF_P(data); 275 | zend_hash_add(Z_ARRVAL_P(return_value), result_data->mapping->value_columns.v[i], strlen(result_data->mapping->value_columns.v[i])+1, &data, sizeof(zval *), NULL); 276 | } 277 | zval_ptr_dtor(&data); 278 | 279 | i++; 280 | } 281 | 282 | value = strtok_r(raw_data, result_data->mapping->separator, &value_lasts); 283 | 284 | while (value) { 285 | ALLOC_INIT_ZVAL(data); 286 | ZVAL_STRING(data, value, 1); 287 | 288 | if (flags & MYSQLND_FETCH_NUM) { 289 | Z_ADDREF_P(data); 290 | zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &data, sizeof(zval *), NULL); 291 | } 292 | if (flags & MYSQLND_FETCH_ASSOC) { 293 | Z_ADDREF_P(data); 294 | zend_hash_add(Z_ARRVAL_P(return_value), result_data->mapping->value_columns.v[i], strlen(result_data->mapping->value_columns.v[i])+1, &data, sizeof(zval *), NULL); 295 | } 296 | zval_ptr_dtor(&data); 297 | 298 | value = strtok_r(NULL, result_data->mapping->separator, &value_lasts); 299 | i++; 300 | } 301 | DBG_INF_FMT("values %d", i); 302 | 303 | CONN_SET_STATE(result->conn, CONN_READY); 304 | efree(raw_data); 305 | DBG_VOID_RETURN; 306 | } 307 | /* }}} */ 308 | 309 | static MYSQLND_ROW_C mymem_result_fetch_row_c(MYSQLND_RES *result TSRMLS_DC) /* {{{ */ 310 | { 311 | mymem_connection_data_data *connection_data = *mysqlnd_plugin_get_plugin_connection_data_data(result->conn, mysqlnd_memcache_plugin_id); 312 | mymem_result_data *result_data = *(mymem_result_data **)mysqlnd_plugin_get_plugin_result_data(result, mysqlnd_memcache_plugin_id); 313 | 314 | int field_count; 315 | char **retval; 316 | 317 | int i = 0; 318 | char *value, *value_lasts; 319 | 320 | DBG_ENTER("mymem_result_fetch_row_c"); 321 | 322 | BAILOUT_IF_CONN_DATA_UNSET(connection_data) 323 | 324 | field_count = result_data->mapping->value_columns.num; 325 | 326 | if (result_data->read || !result_data->data) { 327 | DBG_INF("Data already fetched or no data"); 328 | DBG_RETURN(NULL); 329 | } 330 | 331 | result_data->read = 1; 332 | retval = mnd_malloc(field_count * sizeof(char*)); 333 | 334 | value = strtok_r(result_data->data, result_data->mapping->separator, &value_lasts); 335 | 336 | while (value) { 337 | /* TODO - This can be optimized, data handling in general should be made binary safe ..*/ 338 | retval[i] = strdup(value); 339 | result_data->lengths[i++] = strlen(value); 340 | value = strtok_r(NULL, result_data->mapping->separator, &value_lasts); 341 | } 342 | DBG_INF_FMT("values %d", i); 343 | CONN_SET_STATE(result->conn, CONN_READY); 344 | 345 | DBG_RETURN(retval); 346 | } 347 | /* }}} */ 348 | 349 | static void mymem_result_fetch_all(MYSQLND_RES *result, unsigned int flags, zval *return_value TSRMLS_DC ZEND_FILE_LINE_DC) /* {{{ */ 350 | { 351 | zval *row; 352 | mymem_result_data *result_data_p = *(mymem_result_data **)mysqlnd_plugin_get_plugin_result_data(result, mysqlnd_memcache_plugin_id); 353 | DBG_ENTER("mymem_result_fetch_all"); 354 | 355 | array_init(return_value); 356 | if (result_data_p->data) { 357 | ALLOC_INIT_ZVAL(row); 358 | mymem_result_fetch_into(result, flags, row, MYSQLND_MYSQLI TSRMLS_CC ZEND_FILE_LINE_CC); 359 | zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &row, sizeof(zval *), NULL); 360 | } else { 361 | DBG_INF_FMT("No data"); 362 | } 363 | DBG_VOID_RETURN; 364 | } 365 | /* }}} */ 366 | 367 | static void mymem_result_fetch_field_data(MYSQLND_RES *result, unsigned int offset, zval *return_value TSRMLS_DC) /* {{{ */ 368 | { 369 | mymem_result_data *result_data = *(mymem_result_data **)mysqlnd_plugin_get_plugin_result_data(result, mysqlnd_memcache_plugin_id); 370 | DBG_ENTER("mymem_result_fetch_field_data"); 371 | 372 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Fetching fields is currently not supported, returning raw"); 373 | ZVAL_STRING(return_value, result_data->data, 1); 374 | DBG_VOID_RETURN; 375 | } 376 | /* }}} */ 377 | 378 | static uint64_t mymem_result_num_rows(const MYSQLND_RES * const result TSRMLS_DC) /* {{{ */ 379 | { 380 | mymem_result_data *result_data_p = *(mymem_result_data **)mysqlnd_plugin_get_plugin_result_data(result, mysqlnd_memcache_plugin_id); 381 | 382 | DBG_ENTER("mymem_result_num_rows"); 383 | DBG_RETURN(result_data_p->data ? 1 : 0); 384 | } 385 | /* }}} */ 386 | 387 | static unsigned int mymem_result_num_fields(const MYSQLND_RES * const result TSRMLS_DC) /* {{{ */ 388 | { 389 | mymem_connection_data_data *connection_data = *mysqlnd_plugin_get_plugin_connection_data_data(result->conn, mysqlnd_memcache_plugin_id); 390 | mymem_result_data *result_data = *(mymem_result_data **)mysqlnd_plugin_get_plugin_result_data(result, mysqlnd_memcache_plugin_id); 391 | 392 | DBG_ENTER("mymem_result_num_fields"); 393 | BAILOUT_IF_CONN_DATA_UNSET(connection_data) 394 | DBG_RETURN(result_data->mapping->value_columns.num); 395 | } 396 | /* }}} */ 397 | 398 | static enum_func_status mymem_result_seek_data(MYSQLND_RES * result, uint64_t row TSRMLS_DC) /* {{{ */ 399 | { 400 | DBG_ENTER("mymem_result_seek_data"); 401 | if (row == 0) { 402 | mymem_result_data *result_data = *mysqlnd_plugin_get_plugin_result_data(result, mysqlnd_memcache_plugin_id); 403 | result_data->read = 0; 404 | DBG_RETURN(PASS); 405 | } 406 | /* TODO - Johannes, should we have a warning here? 407 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Seek is currently not fully supported"); 408 | */ 409 | DBG_RETURN(FAIL); 410 | } 411 | /* }}} */ 412 | 413 | static MYSQLND_FIELD_OFFSET mymem_result_seek_field(MYSQLND_RES * const result, MYSQLND_FIELD_OFFSET field_offset TSRMLS_DC) /* {{{ */ 414 | { 415 | mymem_result_data *result_data = *mysqlnd_plugin_get_plugin_result_data(result, mysqlnd_memcache_plugin_id); 416 | MYSQLND_FIELD_OFFSET old_value = result_data->current_field_offset; 417 | 418 | DBG_ENTER("mymem_result_seek_field"); 419 | result_data->current_field_offset = field_offset; /* TODO: should we throw some error when out of bounds? */ 420 | DBG_RETURN(old_value); 421 | } 422 | /* }}} */ 423 | 424 | static MYSQLND_FIELD_OFFSET mymem_result_field_tell(const MYSQLND_RES * const result TSRMLS_DC) /* {{{ */ 425 | { 426 | mymem_result_data *result_data = *mysqlnd_plugin_get_plugin_result_data(result, mysqlnd_memcache_plugin_id); 427 | DBG_ENTER("mymem_result_field_tell"); 428 | DBG_RETURN(result_data->current_field_offset); 429 | } 430 | /* }}} */ 431 | 432 | static const MYSQLND_FIELD *mymem_result_fetch_field(MYSQLND_RES * const result TSRMLS_DC) /* {{{ */ 433 | { 434 | mymem_result_data *result_data = *mysqlnd_plugin_get_plugin_result_data(result, mysqlnd_memcache_plugin_id); 435 | DBG_ENTER("mymem_result_fetch_field"); 436 | DBG_RETURN(result_data->fields + (result_data->current_field_offset++)); 437 | } 438 | /* }}} */ 439 | 440 | static const MYSQLND_FIELD *mymem_result_fetch_field_direct(MYSQLND_RES * const result, MYSQLND_FIELD_OFFSET fieldnr TSRMLS_DC) /* {{{ */ 441 | { 442 | mymem_result_data *result_data = *mysqlnd_plugin_get_plugin_result_data(result, mysqlnd_memcache_plugin_id); 443 | DBG_ENTER("mymem_result_fetch_field_direct"); 444 | DBG_RETURN(result_data->fields + fieldnr); 445 | } 446 | /* }}} */ 447 | 448 | static const MYSQLND_FIELD *mymem_result_fetch_fields(MYSQLND_RES * const result TSRMLS_DC) /* {{{ */ 449 | { 450 | mymem_result_data *result_data = *mysqlnd_plugin_get_plugin_result_data(result, mysqlnd_memcache_plugin_id); 451 | DBG_ENTER("mymem_result_fetch_fields"); 452 | DBG_RETURN(result_data->fields); 453 | } 454 | /* }}} */ 455 | 456 | static unsigned long *mymem_result_fetch_lengths(MYSQLND_RES * const result TSRMLS_DC) /* {{{ */ 457 | { 458 | mymem_result_data *result_data = *mysqlnd_plugin_get_plugin_result_data(result, mysqlnd_memcache_plugin_id); 459 | DBG_ENTER("mymem_result_fetch_lengths"); 460 | DBG_RETURN(result_data->lengths); 461 | } 462 | /* }}} */ 463 | 464 | static enum_func_status mymem_result_free_result(MYSQLND_RES * result, zend_bool implicit TSRMLS_DC) /* {{{ */ 465 | { 466 | mymem_result_data *result_data = *mysqlnd_plugin_get_plugin_result_data(result, mysqlnd_memcache_plugin_id); 467 | 468 | DBG_ENTER("mymem_result_free_result"); 469 | free(result_data->data); 470 | efree(result_data->fields); 471 | efree(result_data->lengths); 472 | efree(result_data); 473 | mnd_pefree(result, result->conn->persistent); 474 | DBG_RETURN(PASS); 475 | } 476 | /* }}} */ 477 | 478 | static const struct st_mysqlnd_res_methods mymem_query_result_funcs = { /* {{{ */ 479 | mymem_result_fetch_row, /* mysqlnd_fetch_row_func fetch_row; */ 480 | NULL, /* mysqlnd_fetch_row_func fetch_row_normal_buffered; -- private */ 481 | NULL, /* mysqlnd_fetch_row_func fetch_row_normal_unbuffered; -- private */ 482 | 483 | mymem_result_use_result, /* func_mysqlnd_res__use_result use_result; */ 484 | mymem_result_store_result, /* func_mysqlnd_res__store_result store_result; */ 485 | mymem_result_fetch_into, /* func_mysqlnd_res__fetch_into fetch_into; */ 486 | mymem_result_fetch_row_c, /* func_mysqlnd_res__fetch_row_c fetch_row_c; */ 487 | mymem_result_fetch_all, /* func_mysqlnd_res__fetch_all fetch_all; */ 488 | mymem_result_fetch_field_data, /* func_mysqlnd_res__fetch_field_data fetch_field_data; */ 489 | mymem_result_num_rows, /* func_mysqlnd_res__num_rows num_rows; */ 490 | mymem_result_num_fields, /* func_mysqlnd_res__num_fields num_fields; */ 491 | NULL, /* func_mysqlnd_res__skip_result skip_result; */ 492 | mymem_result_seek_data, /* func_mysqlnd_res__seek_data seek_data; */ 493 | mymem_result_seek_field, /* func_mysqlnd_res__seek_field seek_field; */ 494 | mymem_result_field_tell, /* func_mysqlnd_res__field_tell field_tell; */ 495 | mymem_result_fetch_field, /* func_mysqlnd_res__fetch_field fetch_field; */ 496 | mymem_result_fetch_field_direct, /* func_mysqlnd_res__fetch_field_direct fetch_field_direct; */ 497 | mymem_result_fetch_fields, /* func_mysqlnd_res__fetch_fields fetch_fields; */ 498 | NULL, /* func_mysqlnd_res__read_result_metadata read_result_metadata; */ 499 | mymem_result_fetch_lengths, /* func_mysqlnd_res__fetch_lengths fetch_lengths; */ 500 | NULL, /* func_mysqlnd_res__store_result_fetch_data store_result_fetch_data; */ 501 | NULL, /* func_mysqlnd_res__initialize_result_set_rest initialize_result_set_rest; */ 502 | NULL, /* func_mysqlnd_res__free_result_buffers free_result_buffers; */ 503 | mymem_result_free_result, /* func_mysqlnd_res__free_result free_result; */ 504 | NULL, /* func_mysqlnd_res__free_result_internal free_result_internal; */ 505 | NULL, /* func_mysqlnd_res__free_result_contents free_result_contents; */ 506 | NULL, /* func_mysqlnd_res__free_buffered_data free_buffered_data; */ 507 | NULL, /* func_mysqlnd_res__unbuffered_free_last_data unbuffered_free_last_data; */ 508 | 509 | /* for decoding - binary or text protocol */ 510 | NULL, /* func_mysqlnd_res__row_decoder row_decoder; */ 511 | 512 | NULL, /* func_mysqlnd_res__result_meta_init result_meta_init; */ 513 | 514 | NULL, /* void * unused1; */ 515 | NULL, /* void * unused2; */ 516 | NULL, /* void * unused3; */ 517 | NULL, /* void * unused4; */ 518 | NULL, /* void * unused5; */ 519 | }; 520 | /* }}} */ 521 | /* }}} */ 522 | 523 | /* {{{ mysqlnd_data overrides */ 524 | /* {{{ query helpers */ 525 | static zend_bool mymem_check_field_list(char *list_s, char **list_c, int list_c_len) /* {{{ */ 526 | { 527 | /* list_s - from SQL statement, list_c from configuration */ 528 | char *end = list_c[list_c_len-1]; 529 | 530 | while (*list_s) { 531 | switch (*list_s) { 532 | case ' ': 533 | case '\n': 534 | case '\r': 535 | case '\t': 536 | case ',': 537 | /* we don't care about these, continue */ 538 | list_s++; 539 | break; 540 | default: 541 | /* we're at the beginning of a field name .. probably, and either we know and expect it, or not */ 542 | if (!memcmp(list_s, *list_c, strlen(*list_c))) { 543 | list_s += strlen(*list_c); 544 | if (*list_c == end) { 545 | if (*list_s == '\0') { 546 | return TRUE; 547 | } else { 548 | return FALSE; 549 | } 550 | } 551 | list_c++; 552 | } else { 553 | return FALSE; 554 | } 555 | break; 556 | } 557 | } 558 | return FALSE; 559 | } 560 | /* }}} */ 561 | 562 | static zval ** mymem_verify_patterns(MYSQLND_CONN_DATA *conn, mymem_connection_data_data *connection_data, char *query, unsigned int query_len, zval *subpats, mymem_mapping ***mapping TSRMLS_DC) /* {{{ */ 563 | { 564 | zval return_value; 565 | zval **table, **id_field, **tmp; 566 | zval **value; 567 | char *key; 568 | int key_len; 569 | DBG_ENTER("mymem_verify_patterns"); 570 | 571 | INIT_ZVAL(return_value); /* This will be a long or bool, no need for a zval_dtor */ 572 | 573 | php_pcre_match_impl(connection_data->regexp.pattern, query, query_len, &return_value, subpats, 0, 0, 0, 0 TSRMLS_CC); 574 | 575 | if (!Z_LVAL(return_value)) { 576 | DBG_INF("return value is no number"); 577 | DBG_RETURN(NULL); 578 | } 579 | 580 | if (zend_hash_index_find(Z_ARRVAL_P(subpats), 2, (void**)&table) == FAILURE || Z_TYPE_PP(table) != IS_STRING) { 581 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Pattern matched but no table name passed or not a string"); 582 | DBG_RETURN(NULL); 583 | } 584 | 585 | if (zend_hash_index_find(Z_ARRVAL_P(subpats), 3, (void**)&id_field) == FAILURE || Z_TYPE_PP(id_field) != IS_STRING) { 586 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Pattern matched but no id field name passed or not a string"); 587 | DBG_RETURN(NULL); 588 | } 589 | 590 | key_len = spprintf(&key, 0, "%s.%s.%s", conn->connect_or_select_db, Z_STRVAL_PP(table), Z_STRVAL_PP(id_field)); 591 | DBG_INF_FMT("key '%s'", key); 592 | 593 | if (zend_hash_find(&connection_data->mapping, key, key_len+1, (void**)mapping) == FAILURE) { 594 | efree(key); 595 | DBG_INF("not found"); 596 | DBG_RETURN(NULL); 597 | } 598 | efree(key); 599 | 600 | if (zend_hash_index_find(Z_ARRVAL_P(subpats), 5, (void**)&value) == FAILURE || Z_TYPE_PP(value) != IS_STRING) { 601 | if (zend_hash_index_find(Z_ARRVAL_P(subpats), 4, (void**)&value) == FAILURE || Z_TYPE_PP(value) != IS_STRING) { 602 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Pattern matched but no id value passed or not a string"); 603 | DBG_RETURN(NULL); 604 | } 605 | } 606 | 607 | if (zend_hash_index_find(Z_ARRVAL_P(subpats), 1, (void**)&tmp) == FAILURE || Z_TYPE_PP(tmp) != IS_STRING) { 608 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Pattern matched but no field list passed or not a string"); 609 | DBG_RETURN(NULL); 610 | } 611 | 612 | if (!mymem_check_field_list(Z_STRVAL_PP(tmp), (**mapping)->value_columns.v, (**mapping)->value_columns.num)) { 613 | DBG_RETURN(NULL); 614 | } 615 | 616 | 617 | DBG_RETURN(value); 618 | } 619 | /* }}} */ 620 | 621 | static void mymem_fill_field_data(mymem_result_data *result_data TSRMLS_DC) /* {{{ */ 622 | { 623 | int i; 624 | int field_count = result_data->mapping->value_columns.num; 625 | result_data->current_field_offset = 0; 626 | result_data->fields = safe_emalloc(field_count, sizeof(MYSQLND_FIELD), 0); 627 | DBG_ENTER("mymem_fill_field_data"); 628 | DBG_INF_FMT("field count %d", field_count); 629 | 630 | memset(result_data->fields, 0, field_count*sizeof(MYSQLND_FIELD)); 631 | 632 | result_data->lengths = safe_emalloc(field_count, sizeof(unsigned long), 0); 633 | memset(result_data->lengths, 0, field_count*sizeof(unsigned long)); 634 | 635 | for (i = 0; i < field_count; ++i) { 636 | result_data->fields[i].db = result_data->mapping->schema_name; 637 | result_data->fields[i].db_length = strlen(result_data->mapping->schema_name); 638 | result_data->fields[i].org_table = result_data->fields[i].table = result_data->mapping->table_name; 639 | result_data->fields[i].org_table_length = result_data->fields[i].table_length = strlen(result_data->mapping->table_name); 640 | result_data->fields[i].name = result_data->mapping->value_columns.v[i]; 641 | result_data->fields[i].name_length = strlen(result_data->mapping->value_columns.v[i]); 642 | result_data->fields[i].catalog = ""; 643 | result_data->fields[i].catalog_length = 0; 644 | result_data->fields[i].type = MYSQL_TYPE_STRING; 645 | } 646 | DBG_VOID_RETURN; 647 | } 648 | /* }}} */ 649 | 650 | static void mymem_notify_decision(mymem_connection_data_data *conn_data, zend_bool using_memcache TSRMLS_DC) /* {{{ */ 651 | { 652 | zval *retval = NULL, *arg; 653 | zval **args[1]; 654 | ALLOC_INIT_ZVAL(arg); 655 | ZVAL_BOOL(arg, using_memcache); 656 | args[0] = &arg; 657 | 658 | DBG_ENTER("mymem_notify_decision"); 659 | 660 | conn_data->callback.fci.no_separation = 0; 661 | conn_data->callback.fci.retval_ptr_ptr = &retval; 662 | conn_data->callback.fci.param_count = 1; 663 | conn_data->callback.fci.params = args; 664 | 665 | if (zend_call_function(&conn_data->callback.fci, &conn_data->callback.fcc TSRMLS_CC) == SUCCESS && retval) { 666 | zval_ptr_dtor(&retval); 667 | } 668 | zval_ptr_dtor(&arg); 669 | DBG_VOID_RETURN; 670 | } 671 | /* }}} */ 672 | /* }}} */ 673 | 674 | static enum_func_status MYSQLND_METHOD(mymem_conn_data, query)(MYSQLND_CONN_DATA *conn, const char *query, unsigned int query_len TSRMLS_DC) /* {{{ */ 675 | { 676 | zval subpats; 677 | zval **query_key = NULL; 678 | mymem_mapping **mapping; 679 | mymem_connection_data_data *connection_data = *mysqlnd_plugin_get_plugin_connection_data_data(conn, mysqlnd_memcache_plugin_id); 680 | DBG_ENTER("mymem_conn_dat::query"); 681 | 682 | INIT_ZVAL(subpats); 683 | 684 | if (connection_data) { 685 | query_key = mymem_verify_patterns(conn, connection_data, (char*)query, query_len, &subpats, &mapping TSRMLS_CC); 686 | 687 | if (UNEXPECTED(connection_data->callback.exists)) { 688 | mymem_notify_decision(connection_data, query_key ? TRUE : FALSE TSRMLS_CC); 689 | } 690 | } 691 | if (query_key) { 692 | void **result_data_vpp; 693 | mymem_result_data *result_data_p; 694 | 695 | uint32_t flags; 696 | size_t value_len; 697 | memcached_return error; 698 | char *key, *res; 699 | int key_len; 700 | 701 | if ((*mapping)->prefix && *(*mapping)->prefix) { 702 | int prefix_len = strlen((*mapping)->prefix); 703 | key_len = prefix_len + Z_STRLEN_PP(query_key); 704 | key = alloca(key_len+1); 705 | memcpy(key, (*mapping)->prefix, prefix_len); 706 | memcpy(key+prefix_len, Z_STRVAL_PP(query_key), Z_STRLEN_PP(query_key)); 707 | key[key_len] = '\0'; 708 | } else { 709 | /* This shouldn't happen anymore as we nowadays enforce getting a prefix, 710 | this was needed with MySQL 5.6.5, which couldn't use prefixes at all */ 711 | key = Z_STRVAL_PP(query_key); 712 | key_len = Z_STRLEN_PP(query_key); 713 | } 714 | 715 | res = memcached_get(connection_data->connection.memc, key, key_len, &value_len, &flags, &error); 716 | DBG_INF_FMT("query %s, key '%s', value_len %d", query, key, value_len); 717 | 718 | zval_dtor(&subpats); 719 | 720 | if (error != MEMCACHED_SUCCESS && error != MEMCACHED_NOTFOUND) { 721 | /* 722 | * Not found will be handled by having 0 rows in the end, we 723 | * can initialize things properly, other errors are more 724 | * interesting, maybe we could/should fall-back to MySQL 725 | * protocol for some? 726 | */ 727 | /* TODO: Map to MySQL error codes */ 728 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "libmemcached error %s (%i)", memcached_strerror(connection_data->connection.memc, error), (int)error); 729 | DBG_RETURN(FAIL); 730 | } 731 | 732 | if (conn->current_result) { 733 | conn->current_result->m.free_result(conn->current_result, TRUE TSRMLS_CC); 734 | mnd_efree(conn->current_result); 735 | } 736 | 737 | conn->current_result = mysqlnd_result_init(1, conn->persistent TSRMLS_CC); 738 | result_data_vpp = mysqlnd_plugin_get_plugin_result_data(conn->current_result, mysqlnd_memcache_plugin_id); 739 | *result_data_vpp = emalloc(sizeof(mymem_result_data)); 740 | result_data_p = *(mymem_result_data **)result_data_vpp; 741 | 742 | result_data_p->data = res; 743 | result_data_p->data_len = value_len; 744 | result_data_p->read = 0; 745 | result_data_p->mapping = *mapping; 746 | 747 | mymem_fill_field_data(result_data_p TSRMLS_CC); 748 | 749 | conn->upsert_status->affected_rows = (uint64_t)-1; 750 | conn->upsert_status->warning_count = 0; 751 | conn->upsert_status->server_status = 0; 752 | 753 | conn->current_result->conn = conn; 754 | conn->current_result->field_count = 1; 755 | conn->current_result->m = mymem_query_result_funcs; 756 | 757 | conn->last_query_type = QUERY_SELECT; 758 | CONN_SET_STATE(conn, CONN_FETCHING_DATA); 759 | 760 | DBG_RETURN(PASS); 761 | } else { 762 | DBG_INF_FMT("Not mapped"); 763 | zval_dtor(&subpats); 764 | DBG_RETURN(orig_mysqlnd_conn_data_query(conn, query, query_len TSRMLS_CC)); 765 | } 766 | } 767 | /* }}} */ 768 | 769 | static void mymem_free_connection_data_data(MYSQLND_CONN_DATA *conn TSRMLS_DC) /* {{{ */ 770 | { 771 | mymem_connection_data_data **conn_data_p = (mymem_connection_data_data **)mysqlnd_plugin_get_plugin_connection_data_data(conn, mysqlnd_memcache_plugin_id); 772 | mymem_connection_data_data *conn_data = *conn_data_p; 773 | DBG_ENTER("mymem_free_connection_data_data"); 774 | 775 | if (conn_data) { 776 | zend_hash_destroy(&conn_data->mapping); 777 | 778 | zval_ptr_dtor(&conn_data->connection.zv); 779 | 780 | if (conn_data->regexp.str_is_allocated) { 781 | efree(conn_data->regexp.str); 782 | } 783 | 784 | if (conn_data->callback.exists) { 785 | zval_ptr_dtor(&conn_data->callback.fci.function_name); 786 | conn_data->callback.fci.function_name = NULL; 787 | if (conn_data->callback.fci.object_ptr) { 788 | zval_ptr_dtor(&conn_data->callback.fci.object_ptr); 789 | conn_data->callback.fci.object_ptr = NULL; 790 | } 791 | } 792 | 793 | efree(conn_data); 794 | 795 | *conn_data_p = NULL; 796 | } 797 | DBG_VOID_RETURN; 798 | } 799 | /* }}} */ 800 | 801 | static enum_func_status MYSQLND_METHOD(mymem_conn_data, end_psession)(MYSQLND_CONN_DATA *conn TSRMLS_DC) /* {{{ */ 802 | { 803 | DBG_ENTER("mymem_conn_data::end_psession"); 804 | mymem_free_connection_data_data(conn TSRMLS_CC); 805 | DBG_RETURN(orig_mysqlnd_conn_data_end_psession(conn TSRMLS_CC)); 806 | } 807 | /* }}} */ 808 | 809 | static enum_func_status MYSQLND_METHOD(mymem_conn, close)(MYSQLND* conn, enum_connection_close_type close_type TSRMLS_DC) /* {{{ */ 810 | { 811 | DBG_ENTER("mymem_conn::close"); 812 | mymem_free_connection_data_data(conn->data TSRMLS_CC); 813 | DBG_RETURN(orig_mysqlnd_conn_close(conn, close_type TSRMLS_CC)); 814 | } 815 | /* }}} */ 816 | /* }}} */ 817 | 818 | /* {{{ User space functions and helpers */ 819 | static void mymem_split_columns(mymem_mapping *mapping, char *names, int names_len TSRMLS_DC) /* {{{ */ 820 | { 821 | int i = 0; 822 | char *pos_from = names, *pos_to; 823 | 824 | int count = count_char(names, ',') + 1; 825 | 826 | DBG_ENTER("mymem_split_columns"); 827 | DBG_INF_FMT("count %d", count); 828 | 829 | mapping->value_columns.num = count; 830 | pos_to = mapping->value_columns.to_free = emalloc(names_len + 1); 831 | mapping->value_columns.v = safe_emalloc(count, sizeof(char*), 0); 832 | 833 | mapping->value_columns.v[0] = mapping->value_columns.to_free; 834 | while (*pos_from) { 835 | switch (*pos_from) { 836 | case ' ': 837 | case '\n': 838 | case '\r': 839 | case '\t': 840 | pos_from++; 841 | break; 842 | case ',': 843 | *pos_to = '\0'; 844 | pos_to++; 845 | pos_from++; 846 | mapping->value_columns.v[++i] = pos_to; 847 | /* fall-through */ 848 | default: 849 | *pos_to = *pos_from; 850 | pos_from++; 851 | pos_to++; 852 | } 853 | } 854 | *pos_to = '\0'; 855 | 856 | DBG_VOID_RETURN; 857 | } 858 | /* }}} */ 859 | 860 | static void mymem_free_mapping(void *mapping_v) /* {{{ */ 861 | { 862 | mymem_mapping *mapping = *(mymem_mapping**)mapping_v; 863 | 864 | efree(mapping->name); 865 | efree(mapping->prefix); 866 | efree(mapping->schema_name); 867 | efree(mapping->table_name); 868 | efree(mapping->id_field_name); 869 | efree(mapping->value_columns.to_free); 870 | efree(mapping->value_columns.v); 871 | efree(mapping->separator); 872 | efree(mapping); 873 | 874 | } 875 | /* }}} */ 876 | 877 | static const char *mymem_pick_mapping_query(MYSQLND *conn, int *query_len TSRMLS_DC) /* {{{ */ 878 | { 879 | MYSQLND_ROW_C row; 880 | MYSQLND_RES *res; 881 | const char *retval; 882 | 883 | DBG_ENTER("mymem_pick_mapping_query"); 884 | 885 | if (FAIL == orig_mysqlnd_conn_data_query(conn->data, MAPPING_DECISION_QUERY, sizeof(MAPPING_DECISION_QUERY)-1 TSRMLS_CC)) { 886 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "MySQL decision query failed: %s", mysqlnd_error(conn)); 887 | DBG_RETURN(NULL); 888 | } 889 | res = mysqlnd_store_result(conn); 890 | if (!res) { 891 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to store result"); 892 | DBG_RETURN(NULL); 893 | } 894 | if (1 != mysqlnd_num_fields(res)) { 895 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Got an invalid result num_fields=%i, expected 1", mysqlnd_num_fields(res)); 896 | mysqlnd_free_result(res, 0); 897 | DBG_RETURN(NULL); 898 | } 899 | row = mysqlnd_fetch_row_c(res); 900 | if (!row) { 901 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Neither innodb_memcache.containers nor ndbmemcache.containers exists. Can't proceed"); 902 | mysqlnd_free_result(res, 0); 903 | DBG_RETURN(NULL); 904 | } 905 | if (!strncmp(row[0], "innodb_memcache", sizeof("innodb_memcache")-1)) { 906 | if (mysqlnd_get_server_version(conn) < 50606) { 907 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid MySQL Server version, require at least MySQL 5.6.6"); 908 | mysqlnd_free_result(res, 0); 909 | DBG_RETURN(NULL); 910 | } 911 | retval = MAPPING_QUERY_INNODB; 912 | *query_len = strlen(MAPPING_QUERY_INNODB); 913 | } else if (!strncmp(row[0], "ndbmemcache", sizeof("ndbmemcache")-1)) { 914 | retval = MAPPING_QUERY_NDB; 915 | *query_len = strlen(MAPPING_QUERY_NDB); 916 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "MySQL Cluster support is not fully tested, yet"); 917 | } else { 918 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid memcache configuration table found, this error should be impossible to hit"); 919 | /* We make a SQL query using IN("innodb_memcache", "ndmemcache") but something different is being returned, major bug */ 920 | mysqlnd_free_result(res, 0); 921 | DBG_RETURN(NULL); 922 | } 923 | mnd_free(row); 924 | if ((row = mysqlnd_fetch_row_c(res))) { 925 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "It seems like both, innodb-memcached and ndb-memcached are configured. This is not supported"); 926 | mnd_free(row); 927 | mysqlnd_free_result(res, 0); 928 | DBG_RETURN(NULL); 929 | } 930 | mysqlnd_free_result(res, 0); 931 | 932 | DBG_INF_FMT("retval %s", retval); 933 | DBG_RETURN(retval); 934 | } 935 | /* }}} */ 936 | 937 | static mymem_connection_data_data *mymem_init_mysqlnd(MYSQLND *conn TSRMLS_DC) /* {{{ */ 938 | { 939 | void **plugin_data_vpp; 940 | mymem_connection_data_data *plugin_data_p; 941 | MYSQLND_ROW_C row; 942 | MYSQLND_RES *res; 943 | int query_len; 944 | const char *query = NULL; 945 | DBG_ENTER("mymem_init_mysqlnd"); 946 | 947 | if (!MYSQLND_MEMCACHE_G(enable)) { 948 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "mysqlnd_memcache.enable not set in php.ini. Enable it and restart PHP"); 949 | DBG_RETURN(NULL); 950 | } 951 | 952 | query = mymem_pick_mapping_query(conn, &query_len TSRMLS_CC); 953 | if (!query) { 954 | DBG_INF("no mapping query"); 955 | DBG_RETURN(NULL); 956 | } 957 | if (FAIL == orig_mysqlnd_conn_data_query(conn->data, query, query_len TSRMLS_CC)) { 958 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "MySQL mapping query failed: %s", mysqlnd_error(conn)); 959 | DBG_RETURN(NULL); 960 | } 961 | 962 | res = mysqlnd_store_result(conn); 963 | if (!res) { 964 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to store result"); 965 | DBG_RETURN(NULL); 966 | } 967 | if (7 != mysqlnd_num_fields(res)) { 968 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Got an invalid result num_fields=%i, expected 7", mysqlnd_num_fields(res)); 969 | mysqlnd_free_result(res, 0); 970 | DBG_RETURN(NULL); 971 | } 972 | 973 | plugin_data_vpp = mysqlnd_plugin_get_plugin_connection_data_data(conn->data, mysqlnd_memcache_plugin_id); 974 | *plugin_data_vpp = emalloc(sizeof(mymem_connection_data_data)); 975 | plugin_data_p = *(mymem_connection_data_data **)plugin_data_vpp; 976 | zend_hash_init(&plugin_data_p->mapping, mysqlnd_num_rows(res), 0, mymem_free_mapping, 0); 977 | plugin_data_p->mapping_query = query; 978 | 979 | while ((row = mysqlnd_fetch_row_c(res))) { 980 | char *key = NULL; 981 | int key_len; 982 | mymem_mapping *mapping; 983 | 984 | if (UNEXPECTED(!row[1])) { 985 | if (query == MAPPING_QUERY_INNODB) { 986 | /* with InnoDB column 1 is a concat of the name and table_map_deimiter, if the result is NULL one of those has to be NULL */ 987 | if (!row[0]) { 988 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Found innodb_memcache.containers entry without name using %s.%s", row[3] ? row[3] : "(table null)", row[4] ? row[4] : "(id column null)"); 989 | continue; /* next one might work fine */ 990 | } else { 991 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "'table_map_delimiter' is not set in innodb_memcache.config_options"); 992 | break; /* This will hit any container, no point to continue */ 993 | } 994 | } 995 | } 996 | 997 | if (UNEXPECTED(!row[6])) { 998 | if (query == MAPPING_QUERY_INNODB) { 999 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "'separator' is not set in innodb_memcache.config_options"); 1000 | break; /* This will hit any container, no point to continue */ 1001 | } 1002 | /* With NDB this currently can't happen as this is a literal in the query */ 1003 | } 1004 | 1005 | if (UNEXPECTED(!row[2] || !row[3] || !row[4] || !row[5])) { 1006 | /* row[0] is handled along with row[1] above */ 1007 | const char *error_col = "(unknown)"; 1008 | int i; 1009 | for (i = 2; i <= 5; ++i) { 1010 | if (!row[i]) { 1011 | const MYSQLND_FIELD *f = mysqlnd_fetch_field_direct(res, i); 1012 | error_col = f->name; 1013 | } 1014 | } 1015 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Field '%s' for '%s' is NULL in innodb_memcache.containers", error_col, row[0]); 1016 | continue; 1017 | } 1018 | 1019 | mapping = emalloc(sizeof(mymem_mapping)); 1020 | 1021 | /* 1022 | For highest performance we might cache this persistently, globally, 1023 | this creates the risk of stuff going wrong if servers don't match 1024 | but if there's one PK lookup only per request we're gonna loose. 1025 | */ 1026 | 1027 | mapping->name = estrdup(row[0]); 1028 | mapping->prefix = estrdup(row[1]); 1029 | mapping->schema_name = estrdup(row[2]); 1030 | mapping->table_name = estrdup(row[3]); 1031 | mapping->id_field_name = estrdup(row[4]); 1032 | mymem_split_columns(mapping, row[5], strlen(row[5]) TSRMLS_CC); 1033 | mapping->separator = estrdup(row[6]); 1034 | 1035 | DBG_INF_FMT("name '%s', prefix '%s', schema_name '%s'", mapping->name, mapping->prefix, mapping->schema_name); 1036 | DBG_INF_FMT("table_name '%s', field_name '%s', separator '%s'", mapping->table_name, mapping->id_field_name, mapping->separator); 1037 | 1038 | /* TODO: We should add fields to the hash, too */ 1039 | key_len = spprintf(&key, 0, "%s.%s.%s", mapping->schema_name, mapping->table_name, mapping->id_field_name); 1040 | DBG_INF_FMT("key '%s'", key); 1041 | 1042 | zend_hash_add(&plugin_data_p->mapping, key, key_len+1, &mapping, sizeof(mymem_mapping*), NULL); 1043 | efree(key); 1044 | mnd_free(row); 1045 | } 1046 | mysqlnd_free_result(res, 0); 1047 | 1048 | DBG_RETURN(plugin_data_p); 1049 | } 1050 | /* }}} */ 1051 | 1052 | /* {{{ proto string mysqlnd_memcache_set(mixed mysql_connection, memcached memcached, string pattern, callback debug_notify) 1053 | Link a memcache connection to a MySQLconnection */ 1054 | static PHP_FUNCTION(mysqlnd_memcache_set) 1055 | { 1056 | zval *mysqlnd_conn_zv; 1057 | MYSQLND *mysqlnd_conn; 1058 | zval *memcached_zv = NULL; 1059 | fake_memcached_object *memcached_obj; 1060 | char *regexp = NULL; 1061 | int regexp_len; 1062 | mymem_connection_data_data *conn_data; 1063 | zend_fcall_info fci; 1064 | zend_fcall_info_cache fcc; 1065 | #if PHP_VERSION_ID >= 50600 1066 | unsigned int client_api_capabilities, tmp; 1067 | #endif 1068 | 1069 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zO!|s!f", &mysqlnd_conn_zv, &memcached_zv, *memcached_ce, ®exp, ®exp_len, &fci, &fcc) == FAILURE) { 1070 | return; 1071 | } 1072 | 1073 | #if PHP_VERSION_ID >= 50600 1074 | mysqlnd_conn = zval_to_mysqlnd(mysqlnd_conn_zv, 0, &client_api_capabilities TSRMLS_CC); 1075 | if (mysqlnd_conn) { 1076 | mysqlnd_conn = zval_to_mysqlnd(mysqlnd_conn_zv, client_api_capabilities, &tmp TSRMLS_CC); 1077 | } 1078 | #else 1079 | mysqlnd_conn = zval_to_mysqlnd(mysqlnd_conn_zv TSRMLS_CC); 1080 | #endif 1081 | 1082 | if (!mysqlnd_conn) { 1083 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Passed variable is no mysqlnd-based MySQL connection"); 1084 | RETURN_FALSE; 1085 | } 1086 | 1087 | mymem_free_connection_data_data(mysqlnd_conn->data TSRMLS_CC); 1088 | if (!memcached_zv) { 1089 | RETURN_TRUE; 1090 | } 1091 | 1092 | memcached_obj = (fake_memcached_object *)zend_object_store_get_object(memcached_zv TSRMLS_CC); 1093 | 1094 | conn_data = mymem_init_mysqlnd(mysqlnd_conn TSRMLS_CC); 1095 | if (!conn_data) { 1096 | RETURN_FALSE; 1097 | } 1098 | 1099 | conn_data->connection.memc = memcached_obj->obj->memc; 1100 | Z_ADDREF_P(memcached_zv); 1101 | conn_data->connection.zv = memcached_zv; 1102 | 1103 | if (regexp) { 1104 | conn_data->regexp.str = estrndup(regexp, regexp_len); 1105 | conn_data->regexp.len = regexp_len; 1106 | conn_data->regexp.str_is_allocated = 1; 1107 | } else { 1108 | conn_data->regexp.str = SQL_PATTERN; 1109 | conn_data->regexp.len = SQL_PATTERN_LEN; 1110 | conn_data->regexp.str_is_allocated = 0; 1111 | } 1112 | 1113 | conn_data->regexp.pattern = pcre_get_compiled_regex_cache(conn_data->regexp.str, conn_data->regexp.len TSRMLS_CC); 1114 | 1115 | if (ZEND_NUM_ARGS() == 4) { 1116 | Z_ADDREF_P(fci.function_name); 1117 | if (fci.object_ptr) { 1118 | Z_ADDREF_P(fci.object_ptr); 1119 | } 1120 | conn_data->callback.exists = TRUE; 1121 | conn_data->callback.fcc = fcc; 1122 | conn_data->callback.fci = fci; 1123 | } else { 1124 | conn_data->callback.exists = FALSE; 1125 | } 1126 | 1127 | RETURN_TRUE; 1128 | } 1129 | /* }}} */ 1130 | 1131 | static int mymemm_add_mapping_to_zv(void *mapping_v, void *retval_v TSRMLS_DC) /* {{{ */ 1132 | { 1133 | int i; 1134 | mymem_mapping *mapping = *(mymem_mapping**)mapping_v; 1135 | zval *retval = (zval*)retval_v; 1136 | zval *current, *fields; 1137 | 1138 | ALLOC_INIT_ZVAL(current); 1139 | ALLOC_INIT_ZVAL(fields); 1140 | array_init(current); 1141 | array_init(fields); 1142 | 1143 | #define ADD_MAPPING_STR(f) add_assoc_string(current, #f, mapping->f, 1) 1144 | ADD_MAPPING_STR(prefix); 1145 | ADD_MAPPING_STR(schema_name); 1146 | ADD_MAPPING_STR(table_name); 1147 | ADD_MAPPING_STR(id_field_name); 1148 | ADD_MAPPING_STR(separator); 1149 | 1150 | for (i = 0; i < mapping->value_columns.num; ++i) { 1151 | add_next_index_string(fields, mapping->value_columns.v[i], 1); 1152 | } 1153 | 1154 | add_assoc_zval(current, "fields", fields); 1155 | add_assoc_zval(retval, mapping->name, current); 1156 | 1157 | return ZEND_HASH_APPLY_KEEP; 1158 | } 1159 | /* }}} */ 1160 | 1161 | /* {{{ proto array mysqlnd_memcache_get_config(mixed mysql_connection) 1162 | Dump different aspects of the configuration */ 1163 | static PHP_FUNCTION(mysqlnd_memcache_get_config) 1164 | { 1165 | zval *mysqlnd_conn_zv, *mapping; 1166 | MYSQLND *mysqlnd_conn; 1167 | mymem_connection_data_data *conn_data; 1168 | #if PHP_VERSION_ID >= 50600 1169 | unsigned int client_api_capabilities, tmp; 1170 | #endif 1171 | 1172 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &mysqlnd_conn_zv) == FAILURE) { 1173 | return; 1174 | } 1175 | 1176 | #if PHP_VERSION_ID >= 50600 1177 | mysqlnd_conn = zval_to_mysqlnd(mysqlnd_conn_zv, 0, &client_api_capabilities TSRMLS_CC); 1178 | if (mysqlnd_conn) { 1179 | mysqlnd_conn = zval_to_mysqlnd(mysqlnd_conn_zv, client_api_capabilities, &tmp TSRMLS_CC); 1180 | } 1181 | #else 1182 | mysqlnd_conn = zval_to_mysqlnd(mysqlnd_conn_zv TSRMLS_CC); 1183 | #endif 1184 | 1185 | if (!mysqlnd_conn) { 1186 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Passed variable is no mysqlnd-based MySQL connection"); 1187 | RETURN_FALSE; 1188 | } 1189 | 1190 | conn_data = *mysqlnd_plugin_get_plugin_connection_data_data(mysqlnd_conn->data, mysqlnd_memcache_plugin_id); 1191 | if (!conn_data) { 1192 | RETURN_FALSE; 1193 | } 1194 | array_init(return_value); 1195 | Z_ADDREF_P(conn_data->connection.zv); 1196 | add_assoc_zval(return_value, "memcached", conn_data->connection.zv); 1197 | 1198 | add_assoc_stringl(return_value, "pattern", conn_data->regexp.str, conn_data->regexp.len, 1); 1199 | 1200 | ALLOC_INIT_ZVAL(mapping); 1201 | array_init(mapping); 1202 | 1203 | zend_hash_apply_with_argument(&conn_data->mapping, mymemm_add_mapping_to_zv, mapping TSRMLS_CC); 1204 | 1205 | add_assoc_zval(return_value, "mappings", mapping); 1206 | add_assoc_string(return_value, "mapping_query", (char*)conn_data->mapping_query, 1); 1207 | } 1208 | /* }}} */ 1209 | 1210 | /* {{{ ARGINFO */ 1211 | ZEND_BEGIN_ARG_INFO_EX(arginfo_mysqlnd_memcache_set, 0, 0, 2) 1212 | ZEND_ARG_INFO(0, mysql_connection) 1213 | ZEND_ARG_OBJ_INFO(0, memcached_connection, Memcached, 1) 1214 | ZEND_ARG_INFO(0, select_pattern) 1215 | ZEND_ARG_INFO(0, debug_callback) 1216 | ZEND_END_ARG_INFO() 1217 | 1218 | ZEND_BEGIN_ARG_INFO_EX(arginfo_mysqlnd_memcache_get_config, 0, 0, 1) 1219 | ZEND_ARG_INFO(0, mysql_connection) 1220 | ZEND_END_ARG_INFO() 1221 | /* }}} */ 1222 | 1223 | /* {{{ mysqlnd_memcache_functions[] */ 1224 | static const zend_function_entry mymem_functions[] = { 1225 | PHP_FE(mysqlnd_memcache_set, arginfo_mysqlnd_memcache_set) 1226 | PHP_FE(mysqlnd_memcache_get_config, arginfo_mysqlnd_memcache_get_config) 1227 | PHP_FE_END 1228 | }; 1229 | /* }}} */ 1230 | /* }}} */ 1231 | 1232 | /* {{{ PHP Infrastructure */ 1233 | 1234 | /* {{{ PHP_INI */ 1235 | PHP_INI_BEGIN() 1236 | STD_PHP_INI_BOOLEAN("mysqlnd_memcache.enable", "1", PHP_INI_SYSTEM, OnUpdateBool, enable, zend_mysqlnd_memcache_globals, mysqlnd_memcache_globals) 1237 | PHP_INI_END() 1238 | /* }}} */ 1239 | 1240 | /* {{{ php_mysqlnd_memcache_init_globals 1241 | */ 1242 | static void php_mysqlnd_memcache_init_globals(zend_mysqlnd_memcache_globals *mysqlnd_memcache_globals) 1243 | { 1244 | mysqlnd_memcache_globals->enable = TRUE; 1245 | } 1246 | /* }}} */ 1247 | 1248 | /* {{{ PHP_GINIT_FUNCTION */ 1249 | static PHP_GINIT_FUNCTION(mysqlnd_memcache) 1250 | { 1251 | php_mysqlnd_memcache_init_globals(mysqlnd_memcache_globals); 1252 | } 1253 | /* }}} */ 1254 | 1255 | /* {{{ PHP_MINIT_FUNCTION 1256 | */ 1257 | static PHP_MINIT_FUNCTION(mysqlnd_memcache) 1258 | { 1259 | char *pmversion; 1260 | 1261 | if (zend_hash_find(CG(class_table), "memcached", sizeof("memcached"), (void **) &memcached_ce)==FAILURE) { 1262 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "mysqlnd_memcache failed to get Memcached class"); 1263 | return FAILURE; 1264 | } 1265 | 1266 | pmversion = (char*)((*memcached_ce)->info.internal.module->version); 1267 | if (php_version_compare("2.0.0", pmversion) > 0 || php_version_compare(pmversion, "2.1.0") >= 0) { 1268 | /* As long as we're blindly casting a void* to fake_memcached_object* we have to be extra careful */ 1269 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "mysqlnd_memcache is only tested with php_memcached 2.0.x, %s might cause errors", pmversion); 1270 | } 1271 | 1272 | ZEND_INIT_MODULE_GLOBALS(mysqlnd_memcache, php_mysqlnd_memcache_init_globals, NULL); 1273 | REGISTER_INI_ENTRIES(); 1274 | 1275 | if (MYSQLND_MEMCACHE_G(enable)) { 1276 | struct st_mysqlnd_conn_methods *conn_methods; 1277 | struct st_mysqlnd_conn_data_methods *conn_data_methods; 1278 | 1279 | mysqlnd_memcache_plugin_id = mysqlnd_plugin_register(); 1280 | 1281 | conn_methods = mysqlnd_conn_get_methods(); 1282 | conn_data_methods = mysqlnd_conn_data_get_methods(); 1283 | 1284 | orig_mysqlnd_conn_data_query = conn_data_methods->query; 1285 | conn_data_methods->query = MYSQLND_METHOD(mymem_conn_data, query); 1286 | 1287 | orig_mysqlnd_conn_close = conn_methods->close; 1288 | conn_methods->close = MYSQLND_METHOD(mymem_conn, close); 1289 | 1290 | orig_mysqlnd_conn_data_end_psession = conn_data_methods->end_psession; 1291 | conn_data_methods->end_psession = MYSQLND_METHOD(mymem_conn_data, end_psession); 1292 | } 1293 | REGISTER_STRINGL_CONSTANT("MYSQLND_MEMCACHE_DEFAULT_REGEXP", SQL_PATTERN, SQL_PATTERN_LEN, CONST_CS | CONST_PERSISTENT); 1294 | REGISTER_STRING_CONSTANT("MYSQLND_MEMCACHE_VERSION", PHP_MYSQLND_MEMCACHE_VERSION, CONST_CS | CONST_PERSISTENT); 1295 | REGISTER_LONG_CONSTANT("MYSQLND_MEMCACHE_VERSION_ID", MYSQLND_MEMCACHE_VERSION_ID, CONST_CS | CONST_PERSISTENT); 1296 | 1297 | return SUCCESS; 1298 | } 1299 | /* }}} */ 1300 | 1301 | /* {{{ PHP_MSHUTDOWN_FUNCTION 1302 | */ 1303 | static PHP_MSHUTDOWN_FUNCTION(mysqlnd_memcache) 1304 | { 1305 | 1306 | UNREGISTER_INI_ENTRIES(); 1307 | 1308 | return SUCCESS; 1309 | } 1310 | /* }}} */ 1311 | 1312 | /* {{{ PHP_MINFO_FUNCTION 1313 | */ 1314 | static PHP_MINFO_FUNCTION(mysqlnd_memcache) 1315 | { 1316 | char buf[64]; 1317 | 1318 | php_info_print_table_start(); 1319 | php_info_print_table_header(2, "mysqlnd_memcache support", "enabled"); 1320 | snprintf(buf, sizeof(buf), "%s (%d)", PHP_MYSQLND_MEMCACHE_VERSION, MYSQLND_MEMCACHE_VERSION_ID); 1321 | php_info_print_table_row(2, "Plugin active", MYSQLND_MEMCACHE_G(enable) ? "yes" : "no"); 1322 | php_info_print_table_row(2, "php-memcached version", (*memcached_ce)->info.internal.module->version); 1323 | php_info_print_table_row(2, "libmemcached version", LIBMEMCACHED_VERSION_STRING); 1324 | php_info_print_table_end(); 1325 | 1326 | 1327 | DISPLAY_INI_ENTRIES(); 1328 | 1329 | } 1330 | /* }}} */ 1331 | 1332 | static const zend_module_dep mymem_deps[] = { /* {{{ */ 1333 | ZEND_MOD_REQUIRED("mysqlnd") 1334 | ZEND_MOD_REQUIRED("pcre") 1335 | ZEND_MOD_REQUIRED("memcached") 1336 | ZEND_MOD_END 1337 | }; 1338 | /* }}} */ 1339 | 1340 | /* {{{ mysqlnd_memcache_module_entry 1341 | */ 1342 | zend_module_entry mysqlnd_memcache_module_entry = { 1343 | STANDARD_MODULE_HEADER_EX, 1344 | NULL, 1345 | mymem_deps, 1346 | "mysqlnd_memcache", 1347 | mymem_functions, 1348 | PHP_MINIT(mysqlnd_memcache), 1349 | PHP_MSHUTDOWN(mysqlnd_memcache), 1350 | NULL, 1351 | NULL, 1352 | PHP_MINFO(mysqlnd_memcache), 1353 | PHP_MYSQLND_MEMCACHE_VERSION, 1354 | STANDARD_MODULE_PROPERTIES 1355 | }; 1356 | /* }}} */ 1357 | 1358 | #ifdef COMPILE_DL_MYSQLND_MEMCACHE 1359 | ZEND_GET_MODULE(mysqlnd_memcache) 1360 | #endif 1361 | /* }}} */ 1362 | 1363 | /* 1364 | * Local variables: 1365 | * tab-width: 4 1366 | * c-basic-offset: 4 1367 | * End: 1368 | * vim600: noet sw=4 ts=4 fdm=marker 1369 | * vim<600: noet sw=4 ts=4 1370 | */ 1371 | --------------------------------------------------------------------------------