diff --git a/.gitignore b/.gitignore index 1ac3d1c..0f299aa 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,4 @@ composer.lock composer.phar mysql_config.php *.pid -test/mysql_db/auto.cnf -test/mysql_db/ib_logfile* -test/mysql_db/ibdata* -test/mysql_db/ConnectionTest/tmp.* \ No newline at end of file +test/mysql_db \ No newline at end of file diff --git a/lib/Connection.php b/lib/Connection.php index e2ccd7f..651004d 100644 --- a/lib/Connection.php +++ b/lib/Connection.php @@ -5,124 +5,49 @@ use Amp\Deferred; use Amp\Success; -/* @TODO - * 14.2.3 Auth switch request?? - * 14.2.4 COM_CHANGE_USER - */ - -/** @see 14.1.3.4 Status Flags */ -class StatusFlags { - const SERVER_STATUS_IN_TRANS = 0x0001; // a transaction is active - const SERVER_STATUS_AUTOCOMMIT = 0x0002; // auto-commit is enabled - const SERVER_MORE_RESULTS_EXISTS = 0x0008; - const SERVER_STATUS_NO_GOOD_INDEX_USED = 0x0010; - const SERVER_STATUS_NO_INDEX_USED = 0x0020; - const SERVER_STATUS_CURSOR_EXISTS = 0x0040; // Used by Binary Protocol Resultset to signal that COM_STMT_FETCH has to be used to fetch the row-data. - const SERVER_STATUS_LAST_ROW_SENT = 0x0080; - const SERVER_STATUS_DB_DROPPED = 0x0100; - const SERVER_STATUS_NO_BACKSLASH_ESCAPES = 0x0200; - const SERVER_STATUS_METADATA_CHANGED = 0x0400; - const SERVER_QUERY_WAS_SLOW = 0x0800; - const SERVER_PS_OUT_PARAMS = 0x1000; - const SERVER_STATUS_IN_TRANS_READONLY = 0x2000; // in a read-only transaction - const SERVER_SESSION_STATE_CHANGED = 0x4000; // connection state information has changed -} - -/** @see 13.1.3.1.1 Session State Information */ -class SessionStateTypes { - const SESSION_TRACK_SYSTEM_VARIABLES = 0x00; - const SESSION_TRACK_SCHEMA = 0x01; - const SESSION_TRACK_STATE_CHANGE = 0x02; -} - class Connection { - private $out = []; - private $outBuf; - private $outBuflen = 0; - private $uncompressedOut = ""; - - private $processors = []; - - private $protocol; - private $seqId = -1; - private $compressionId = -1; - private $socket; - private $readGranularity = 8192; - private $readWatcher = null; - private $writeWatcher = null; - private $watcherEnabled = false; - private $authPluginDataLen; - private $query; - private $named = []; - private $parseCallback = null; - private $packetCallback = null; - - private $config; - private $deferreds = []; - private $onReady = []; - private $result; - private $oldDb = null; - - protected $connectionId; - protected $authPluginData; - protected $capabilities = 0; - protected $serverCapabilities = 0; - protected $authPluginName; - protected $connInfo; - - protected $connectionState = self::UNCONNECTED; - - const MAX_PACKET_SIZE = 0xffffff; - const MAX_UNCOMPRESSED_BUFLEN = 0xfffffb; - - const CLIENT_LONG_FLAG = 0x00000004; - const CLIENT_CONNECT_WITH_DB = 0x00000008; - const CLIENT_COMPRESS = 0x00000020; - const CLIENT_PROTOCOL_41 = 0x00000200; - const CLIENT_SSL = 0x00000800; - const CLIENT_TRANSACTIONS = 0x00002000; - const CLIENT_SECURE_CONNECTION = 0x00008000; - const CLIENT_MULTI_STATEMENTS = 0x00010000; - const CLIENT_MULTI_RESULTS = 0x00020000; - const CLIENT_PS_MULTI_RESULTS = 0x00040000; - const CLIENT_PLUGIN_AUTH = 0x00080000; - const CLIENT_CONNECT_ATTRS = 0x00100000; - const CLIENT_SESSION_TRACK = 0x00800000; - const CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA = 0x00200000; - const CLIENT_DEPRECATE_EOF = 0x01000000; - - const OK_PACKET = 0x00; - const EXTRA_AUTH_PACKET = 0x01; - const LOCAL_INFILE_REQUEST = 0xfb; - const EOF_PACKET = 0xfe; - const ERR_PACKET = 0xff; - - const UNCONNECTED = 0; - const ESTABLISHED = 1; - const READY = 2; - const CLOSING = 3; - const CLOSED = 4; - const REFRESH_GRANT = 0x01; const REFRESH_LOG = 0x02; const REFRESH_TABLES = 0x04; const REFRESH_HOSTS = 0x08; - const REFREHS_STATUS = 0x10; + const REFRESH_STATUS = 0x10; const REFRESH_THREADS = 0x20; const REFRESH_SLAVE = 0x40; const REFRESH_MASTER = 0x80; - public function __construct($config, $sslOptions = null) { - $this->connInfo = new ConnectionState; + protected $processor; + public function __construct($config, $sslOptions = null) { if (!$config instanceof ConnectionConfig) { $config = self::parseConnStr($config, $sslOptions); } if ($config->resolvedHost === null) { $this->resolveHost($config); } - $this->config = $config; + $hash = spl_object_hash($this); + $ready = static function() use ($hash, $config) { + $cb = $config->ready; + if (isset($cb)) { + $cb($hash); + } + }; + $busy = static function() use ($hash, $config) { + $cb = $config->busy; + if (isset($cb)) { + $cb($hash); + } + }; + $restore = static function() use ($hash, $config) { + $cb = $config->restore; + if (isset($cb)) { + $cb($hash); + } + }; + $this->processor = new Processor($ready, $busy, $restore); + $this->processor->config = $config; } + + public static function parseConnStr($connStr, $sslOptions = null) { $db = null; @@ -174,23 +99,23 @@ private function resolveHost($config) { } public function useExceptions($set) { - $this->config->exceptions = $set; + $this->processor->config->exceptions = $set; } public function alive() { - return $this->connectionState <= self::READY; + return $this->processor->alive(); } public function isReady() { - return $this->connectionState === self::READY; + return $this->processor->isReady(); } public function forceClose() { - $this->closeSocket(); + $this->processor->closeSocket(); } public function getConfig() { - return clone $this->config; + return clone $this->processor->config; } /* Technical function to be used in combination with Pool */ @@ -198,94 +123,12 @@ public function getThis() { return new Success($this); } - private function ready() { - if (empty($this->deferreds)) { - if (empty($this->onReady)) { - $cb = $this->config->ready; - $this->out[] = null; - $this->disableRead(); - } else { - list($key, $cb) = each($this->onReady); - unset($this->onReady[$key]); - } - if (isset($cb) && is_callable($cb)) { - $cb($this); - } - } - } - public function connect() { - \assert(!$this->deferreds && !$this->socket, self::class."::connect() should not be called twice"); - - $this->deferreds[] = $deferred = new Deferred; - \Amp\Socket\connect($this->config->resolvedHost)->when(function ($error, $socket) use ($deferred) { - if ($this->connectionState === self::CLOSED) { - $deferred->succeed(null); - if ($socket) { - fclose($socket); - } - return; - } - - if ($error) { - $deferred->fail($error); - if ($socket) { - fclose($socket); - } - return; - } - - $this->processors = [$this->parseMysql()]; - - $this->socket = $socket; - $this->readWatcher = \Amp\onReadable($this->socket, [$this, "onInit"]); - }); - return $deferred->promise(); - } - - public function onInit() { - // reset internal state - $this->out = []; - $this->seqId = $this->compressionId = -1; - - \Amp\cancel($this->readWatcher); - $this->readWatcher = null; - $this->enableRead(); - $this->onRead(); - } - - /** @return Deferred */ - private function getDeferred() { - list($key, $deferred) = each($this->deferreds); - unset($this->deferreds[$key]); - return $deferred; - } - - private function appendTask($callback) { - if ($this->packetCallback || $this->parseCallback || !empty($this->onReady) || !empty($this->deferreds) || $this->connectionState != self::READY) { - $this->onReady[] = $callback; - } else { - $cb = $this->config->busy; - if (isset($cb)) { - $cb($this); - } - $callback(); - } + return $this->processor->connect(); } - + public function getConnInfo() { - return clone $this->connInfo; - } - - private function startCommand($callback) { - $deferred = new Deferred; - $this->appendTask(function() use ($callback, $deferred) { - $this->enableRead(); - $this->seqId = $this->compressionId = -1; - $this->deferreds[] = $deferred; - $callback(); - }); - return $deferred->promise(); + return $this->processor->getConnInfo(); } public function setCharset($charset, $collate = "") { @@ -294,8 +137,8 @@ public function setCharset($charset, $collate = "") { $charset = substr($collate, 0, $off); } - $this->config->charset = $charset; - $this->config->collate = $collate; + $this->processor->config->charset = $charset; + $this->processor->config->collate = $collate; $query = "SET NAMES '$charset'".($collate == "" ? "" : " COLLATE '$collate'"); return $this->query($query); @@ -303,37 +146,39 @@ public function setCharset($charset, $collate = "") { /** @see 14.6.2 COM_QUIT */ public function close() { - return $this->startCommand(function() { - $this->sendPacket("\x01"); - $this->connectionState = self::CLOSING; - })->when(function() { - $this->closeSocket(); + $processor = $this->processor; + return $processor->startCommand(static function() use ($processor) { + $processor->sendPacket("\x01"); + $processor->initClosing(); + })->when(static function() use ($processor) { + $processor->closeSocket(); }); } /** @see 14.6.3 COM_INIT_DB */ public function useDb($db) { - return $this->startCommand(function() use ($db) { - $this->oldDb = $this->config->db; - $this->config->db = $db; - $this->sendPacket("\x02$db"); + $processor = $this->processor; + return $processor->startCommand(static function() use ($processor, $db) { + $processor->config->db = $db; + $processor->sendPacket("\x02$db"); }); } /** @see 14.6.4 COM_QUERY */ public function query($query) { - return $this->startCommand(function() use ($query) { - $this->sendPacket("\x03$query"); - $this->query = $query; - $this->packetCallback = [$this, "handleQuery"]; + $processor = $this->processor; + return $processor->startCommand(static function() use ($processor, $query) { + $processor->setQuery($query); + $processor->sendPacket("\x03$query"); }); } /** @see 14.6.5 COM_FIELD_LIST */ public function listFields($table, $like = "%") { - return $this->startCommand(function() use ($table, $like) { - $this->sendPacket("\x04$table\0$like"); - $this->parseCallback = [$this, "handleFieldlist"]; + $processor = $this->processor; + return $processor->startCommand(static function() use ($processor, $table, $like) { + $processor->sendPacket("\x04$table\0$like"); + $processor->setFieldListing(); }); } @@ -360,15 +205,17 @@ public function listAllFields($table, $like = "%") { /** @see 14.6.6 COM_CREATE_DB */ public function createDatabase($db) { - return $this->startCommand(function() use ($db) { - $this->sendPacket("\x05$db"); + $processor = $this->processor; + return $processor->startCommand(static function() use ($processor, $db) { + $processor->sendPacket("\x05$db"); }); } /** @see 14.6.7 COM_DROP_DB */ public function dropDatabase($db) { - return $this->startCommand(function() use ($db) { - $this->sendPacket("\x06$db"); + $processor = $this->processor; + return $processor->startCommand(static function() use ($processor, $db) { + $processor->sendPacket("\x06$db"); }); } @@ -377,52 +224,59 @@ public function dropDatabase($db) { * @see 14.6.8 COM_REFRESH */ public function refresh($subcommand) { - return $this->startCommand(function() use ($subcommand) { - $this->sendPacket("\x07" . chr($subcommand)); + $processor = $this->processor; + return $processor->startCommand(static function() use ($processor, $subcommand) { + $processor->sendPacket("\x07" . chr($subcommand)); }); } /** @see 14.6.9 COM_SHUTDOWN */ public function shutdown() { - return $this->startCommand(function() { - $this->sendPacket("\x08\x00"); /* SHUTDOWN_DEFAULT / SHUTDOWN_WAIT_ALL_BUFFERS, only one in use */ + $processor = $this->processor; + return $processor->startCommand(static function() use ($processor) { + $processor->sendPacket("\x08\x00"); /* SHUTDOWN_DEFAULT / SHUTDOWN_WAIT_ALL_BUFFERS, only one in use */ }); } /** @see 14.6.10 COM_STATISTICS */ public function statistics() { - return $this->startCommand(function() { - $this->sendPacket("\x09"); - $this->parseCallback = [$this, "readStatistics"]; + $processor = $this->processor; + return $processor->startCommand(static function() use ($processor) { + $processor->sendPacket("\x09"); + $processor->setStatisticsReading(); }); } /** @see 14.6.11 COM_PROCESS_INFO */ public function processInfo() { - return $this->startCommand(function() { - $this->sendPacket("\x0a"); - $this->packetCallback = [$this, "handleQuery"]; + $processor = $this->processor; + return $processor->startCommand(static function() use ($processor) { + $processor->sendPacket("\x0a"); + $processor->setQuery("SHOW PROCESSLIST"); }); } /** @see 14.6.13 COM_PROCESS_KILL */ public function killProcess($process) { - return $this->startCommand(function() use ($process) { - $this->sendPacket("\x0c" . DataTypes::encode_int32($process)); + $processor = $this->processor; + return $processor->startCommand(static function() use ($processor, $process) { + $processor->sendPacket("\x0c" . DataTypes::encode_int32($process)); }); } /** @see 14.6.14 COM_DEBUG */ public function debugStdout() { - return $this->startCommand(function() { - $this->sendPacket("\x0d"); + $processor = $this->processor; + return $processor->startCommand(static function() use ($processor) { + $processor->sendPacket("\x0d"); }); } /** @see 14.6.15 COM_PING */ public function ping() { - return $this->startCommand(function() { - $this->sendPacket("\x0e"); + $processor = $this->processor; + return $processor->startCommand(static function() use ($processor) { + $processor->sendPacket("\x0e"); }); } @@ -452,29 +306,30 @@ public function changeUser($user, $pass, $db = null) { /** @see 14.6.19 COM_RESET_CONNECTION */ public function resetConnection() { - return $this->startCommand(function() { - $this->sendPacket("\x1f"); + $processor = $this->processor; + return $processor->startCommand(static function() use ($processor) { + $processor->sendPacket("\x1f"); }); } /** @see 14.7.4 COM_STMT_PREPARE */ public function prepare($query, $data = null) { - $promise = $this->startCommand(function() use ($query) { - $this->query = $query; + $processor = $this->processor; + $promise = $processor->startCommand(static function() use ($processor, $query) { + $processor->setPrepare($query); $regex = <<<'REGEX' (["'`])(?:\\(?:\\|\1)|(?!\1).)*+\1(*SKIP)(*F)|(\?)|:([a-zA-Z_]+) REGEX; $index = 0; - $query = preg_replace_callback("~$regex~ms", function ($m) use (&$index) { + $query = preg_replace_callback("~$regex~ms", function ($m) use ($processor, &$index) { if ($m[2] !== "?") { - $this->named[$m[3]][] = $index; + $processor->named[$m[3]][] = $index; } $index++; return "?"; }, $query); - $this->sendPacket("\x16$query"); - $this->parseCallback = [$this, "handlePrepare"]; + $processor->sendPacket("\x16$query"); }); if ($data === null) { @@ -482,1032 +337,18 @@ public function prepare($query, $data = null) { } $retDeferred = new Deferred; - $promise->when(function($error, $stmt) use ($retDeferred, $data) { + $promise->when(static function($error, $stmt) use ($retDeferred, $data) { if ($error) { $retDeferred->fail($error); } else { - try { - $retDeferred->succeed($stmt->execute($data)); - } catch (\Exception $e) { - $retDeferred->fail($e); - } + $retDeferred->succeed($stmt->execute($data)); } }); return $retDeferred->promise(); } - - /** @see 14.7.5 COM_STMT_SEND_LONG_DATA */ - public function bindParam($stmtId, $paramId, $data) { - $payload = "\x18"; - $payload .= DataTypes::encode_int32($stmtId); - $payload .= DataTypes::encode_int16($paramId); - $payload .= $data; - $this->appendTask(function () use ($payload) { - $this->out[] = null; - $this->sendPacket($payload); - $this->ready(); - }); - } - - /** @see 14.7.6 COM_STMT_EXECUTE */ - // prebound params: null-bit set, type MYSQL_TYPE_LONG_BLOB, no value - // $params is by-ref, because the actual result object might not yet have been filled completely with data upon call of this method ... - public function execute($stmtId, $query, &$params, $prebound, $data = []) { - $deferred = new Deferred; - $this->appendTask(function () use ($stmtId, $query, &$params, $prebound, $data, $deferred) { - $payload = "\x17"; - $payload .= DataTypes::encode_int32($stmtId); - $payload .= chr(0); // cursor flag // @TODO cursor types?! - $payload .= DataTypes::encode_int32(1); - $paramCount = count($params); - $bound = !empty($data) || !empty($prebound); - $types = ""; - $values = ""; - if ($paramCount) { - $args = $data + array_fill(0, $paramCount, null); - ksort($args); - $args = array_slice($args, 0, $paramCount); - $nullOff = strlen($payload); - $payload .= str_repeat("\0", ($paramCount + 7) >> 3); - foreach ($args as $paramId => $param) { - if ($param === null) { - $off = $nullOff + ($paramId >> 3); - $payload[$off] = $payload[$off] | chr(1 << ($paramId % 8)); - } else { - $bound = 1; - } - list($unsigned, $type, $value) = DataTypes::encodeBinary($param); - if (isset($prebound[$paramId])) { - $types .= chr(DataTypes::MYSQL_TYPE_LONG_BLOB); - } else { - $types .= chr($type); - } - $types .= $unsigned?"\x80":"\0"; - $values .= $value; - } - $payload .= chr($bound); - if ($bound) { - $payload .= $types; - $payload .= $values; - } - } - - $this->query = $query; - - $this->out[] = null; - $this->deferreds[] = $deferred; - $this->sendPacket($payload); - $this->packetCallback = [$this, "handleExecute"]; - $this->enableRead(); - }); - return $deferred->promise(); // do not use $this->startCommand(), that might unexpectedly reset the seqId! - } - - /** @see 14.7.7 COM_STMT_CLOSE */ - public function closeStmt($stmtId) { - $payload = "\x19" . DataTypes::encode_int32($stmtId); - $this->appendTask(function () use ($payload) { - if ($this->connectionState === self::READY) { - $this->out[] = null; - $this->sendPacket($payload); - } - $this->ready(); - }); - } - - /** @see 14.7.8 COM_STMT_RESET */ - public function resetStmt($stmtId) { - $payload = "\x1a" . DataTypes::encode_int32($stmtId); - $deferred = new Deferred; - $this->appendTask(function () use ($payload, $deferred) { - $this->out[] = null; - $this->deferreds[] = $deferred; - $this->sendPacket($payload); - $this->enableRead(); - }); - return $deferred->promise(); - } - - /** @see 14.8.4 COM_STMT_FETCH */ - public function fetchStmt($stmtId) { - $payload = "\x1c" . DataTypes::encode_int32($stmtId) . DataTypes::encode_int32(1); - $deferred = new Deferred; - $this->appendTask(function () use ($payload, $deferred) { - $this->out[] = null; - $this->deferreds[] = $deferred; - $this->sendPacket($payload); - $this->enableRead(); - }); - return $deferred->promise(); - } - - private function established() { - // @TODO flags to use? - $this->capabilities |= self::CLIENT_SESSION_TRACK | self::CLIENT_TRANSACTIONS | self::CLIENT_PROTOCOL_41 | self::CLIENT_SECURE_CONNECTION | self::CLIENT_MULTI_RESULTS | self::CLIENT_PS_MULTI_RESULTS | self::CLIENT_MULTI_STATEMENTS | self::CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA; - - if (extension_loaded("zlib")) { - $this->capabilities |= self::CLIENT_COMPRESS; - } - } - - /** @see 14.1.3.2 ERR-Packet */ - private function handleError($packet) { - $off = 1; - - $this->connInfo->errorCode = DataTypes::decode_int16(substr($packet, $off, 2)); - $off += 2; - - if ($this->capabilities & self::CLIENT_PROTOCOL_41) { - $this->connInfo->errorState = substr($packet, $off, 6); - $off += 6; - } - - $this->connInfo->errorMsg = substr($packet, $off); - - $this->parseCallback = null; - if ($this->connectionState == self::READY) { - // normal error - if ($this->config->exceptions) { - $this->getDeferred()->fail(new QueryException("MySQL error ({$this->connInfo->errorCode}): {$this->connInfo->errorState} {$this->connInfo->errorMsg}", $this->query)); - } else { - $this->getDeferred()->succeed(false); - } - $this->query = null; - $this->ready(); - } elseif ($this->connectionState < self::READY) { - // connection failure - $this->closeSocket(); - $this->getDeferred()->fail(new InitializationException("Could not connect to {$this->config->resolvedHost}: {$this->connInfo->errorState} {$this->connInfo->errorMsg}")); - } - } - - /** @see 14.1.3.1 OK-Packet */ - private function parseOk($packet) { - $off = 1; - - $this->connInfo->affectedRows = DataTypes::decodeInt(substr($packet, $off), $intlen); - $off += $intlen; - - $this->connInfo->insertId = DataTypes::decodeInt(substr($packet, $off), $intlen); - $off += $intlen; - - if ($this->capabilities & (self::CLIENT_PROTOCOL_41 | self::CLIENT_TRANSACTIONS)) { - $this->connInfo->statusFlags = DataTypes::decode_int16(substr($packet, $off)); - $off += 2; - - $this->connInfo->warnings = DataTypes::decode_int16(substr($packet, $off)); - $off += 2; - } - - if ($this->capabilities & self::CLIENT_SESSION_TRACK) { - $this->connInfo->statusInfo = DataTypes::decodeStringOff($packet, $off); - - if ($this->connInfo->statusFlags & StatusFlags::SERVER_SESSION_STATE_CHANGED) { - $sessionState = DataTypes::decodeString(substr($packet, $off), $intlen, $sessionStateLen); - $len = 0; - while ($len < $sessionStateLen) { - $data = DataTypes::decodeString(substr($sessionState, $len + 1), $datalen, $intlen); - - switch ($type = DataTypes::decode_int8(substr($sessionState, $len))) { - case SessionStateTypes::SESSION_TRACK_SYSTEM_VARIABLES: - $this->connInfo->sessionState[SessionStateTypes::SESSION_TRACK_SYSTEM_VARIABLES][DataTypes::decodeString($data, $intlen, $strlen)] = DataTypes::decodeString(substr($data, $intlen + $strlen)); - break; - case SessionStateTypes::SESSION_TRACK_SCHEMA: - $this->connInfo->sessionState[SessionStateTypes::SESSION_TRACK_SCHEMA] = DataTypes::decodeString($data); - break; - case SessionStateTypes::SESSION_TRACK_STATE_CHANGE: - $this->connInfo->sessionState[SessionStateTypes::SESSION_TRACK_STATE_CHANGE] = DataTypes::decodeString($data); - break; - default: - throw new \UnexpectedValueException("$type is not a valid mysql session state type"); - } - - $len += 1 + $intlen + $datalen; - } - } - } else { - $this->connInfo->statusInfo = substr($packet, $off); - } - } - - private function handleOk($packet) { - $this->parseOk($packet); - $this->getDeferred()->succeed($this->getConnInfo()); - $this->ready(); - } - - /** @see 14.1.3.3 EOF-Packet */ - private function parseEof($packet) { - if ($this->capabilities & self::CLIENT_PROTOCOL_41) { - $this->connInfo->warnings = DataTypes::decode_int16(substr($packet, 1)); - - $this->connInfo->statusFlags = DataTypes::decode_int16(substr($packet, 3)); - } - } - - private function handleEof($packet) { - $this->parseEof($packet); - $this->getDeferred()->succeed($this->getConnInfo()); - $this->ready(); - } - - /** @see 14.2.5 Connection Phase Packets */ - private function handleHandshake($packet) { - $off = 1; - - $this->protocol = ord($packet); - if ($this->protocol !== 0x0a) { - throw new \UnexpectedValueException("Unsupported protocol version ".ord($packet)." (Expected: 10)"); - } - - $this->connInfo->serverVersion = DataTypes::decodeNullString(substr($packet, $off), $len); - $off += $len + 1; - - $this->connectionId = DataTypes::decode_int32(substr($packet, $off)); - $off += 4; - - $this->authPluginData = substr($packet, $off, 8); - $off += 8; - - $off += 1; // filler byte - - $this->serverCapabilities = DataTypes::decode_int16(substr($packet, $off)); - $off += 2; - - if (\strlen($packet) > $off) { - $this->connInfo->charset = ord(substr($packet, $off)); - $off += 1; - - $this->connInfo->statusFlags = DataTypes::decode_int16(substr($packet, $off)); - $off += 2; - - $this->serverCapabilities += DataTypes::decode_int16(substr($packet, $off)) << 16; - $off += 2; - - $this->authPluginDataLen = $this->serverCapabilities & self::CLIENT_PLUGIN_AUTH ? ord(substr($packet, $off)) : 0; - $off += 1; - - if ($this->serverCapabilities & self::CLIENT_SECURE_CONNECTION) { - $off += 10; - - $strlen = max(13, $this->authPluginDataLen - 8); - $this->authPluginData .= substr($packet, $off, $strlen); - $off += $strlen; - - if ($this->serverCapabilities & self::CLIENT_PLUGIN_AUTH) { - $this->authPluginName = DataTypes::decodeNullString(substr($packet, $off)); - } - } - } - - $this->sendHandshake(); - } - - /** @see 14.6.4.1.2 LOCAL INFILE Request */ - private function handleLocalInfileRequest($packet) { - // @TODO async file fetch @rdlowrey - $file = file_get_contents($packet); - if ($file != "") { - $this->sendPacket($file); - } - $this->sendPacket(""); - } - - /** @see 14.6.4.1.1 Text Resultset */ - private function handleQuery($packet) { - $this->parseCallback = [$this, "handleTextColumnDefinition"]; - $this->getDeferred()->succeed(new ResultSet($this->connInfo, $result = new ResultProxy)); - /* we need to succeed before assigning vars, so that a when() handler won't have a partial result available */ - $this->result = $result; - $this->result->setColumns(ord($packet)); - } - - /** @see 14.7.1 Binary Protocol Resultset */ - private function handleExecute($packet) { - $this->parseCallback = [$this, "handleBinaryColumnDefinition"]; - $this->getDeferred()->succeed(new ResultSet($this->connInfo, $result = new ResultProxy)); - /* we need to succeed before assigning vars, so that a when() handler won't have a partial result available */ - $this->result = $result; - $this->result->setColumns(ord($packet)); - } - - private function handleFieldList($packet) { - if (ord($packet) == self::ERR_PACKET) { - $this->parseCallback = null; - $this->handleError($packet); - } elseif (ord($packet) == self::EOF_PACKET) { - $this->parseCallback = null; - $this->parseEof($packet); - $this->getDeferred()->succeed(null); - $this->ready(); - } else { - $this->getDeferred()->succeed([$this->parseColumnDefinition($packet), $this->deferreds[] = new Deferred]); - } - } - - private function handleTextColumnDefinition($packet) { - $this->handleColumnDefinition($packet, "handleTextResultsetRow"); - } - - private function handleBinaryColumnDefinition($packet) { - $this->handleColumnDefinition($packet, "handleBinaryResultsetRow"); - } - - private function handleColumnDefinition($packet, $cbMethod) { - if (!$this->result->columnsToFetch--) { - $this->result->updateState(ResultProxy::COLUMNS_FETCHED); - if (ord($packet) == self::ERR_PACKET) { - $this->parseCallback = null; - $this->handleError($packet); - } else { - $cb = $this->parseCallback = [$this, $cbMethod]; - if ($this->capabilities & self::CLIENT_DEPRECATE_EOF) { - $cb($packet); - } else { - $this->parseEof($packet); - // we don't need the EOF packet, skip! - } - } - return; - } - - $this->result->columns[] = $this->parseColumnDefinition($packet); - } - - private function prepareParams($packet) { - if (!$this->result->columnsToFetch--) { - $this->result->columnsToFetch = $this->result->columnCount; - if (!$this->result->columnsToFetch) { - $this->prepareFields($packet); - } else { - $this->parseCallback = [$this, "prepareFields"]; - } - return; - } - - $this->result->params[] = $this->parseColumnDefinition($packet); - } - - private function prepareFields($packet) { - if (!$this->result->columnsToFetch--) { - $this->parseCallback = null; - $this->result->updateState(ResultProxy::COLUMNS_FETCHED); - $this->query = null; - $this->ready(); - - return; - } - - $this->result->columns[] = $this->parseColumnDefinition($packet); - } - - /** @see 14.6.4.1.1.2 Column Defintion */ - private function parseColumnDefinition($packet) { - $off = 0; - - $column = []; - - if ($this->capabilities & self::CLIENT_PROTOCOL_41) { - $column["catalog"] = DataTypes::decodeStringOff($packet, $off); - $column["schema"] = DataTypes::decodeStringOff($packet, $off); - $column["table"] = DataTypes::decodeStringOff($packet, $off); - $column["original_table"] = DataTypes::decodeStringOff($packet, $off); - $column["name"] = DataTypes::decodeStringOff($packet, $off); - $column["original_name"] = DataTypes::decodeStringOff($packet, $off); - $fixlen = DataTypes::decodeIntOff($packet, $off); - - $len = 0; - $column["charset"] = DataTypes::decode_int16(substr($packet, $off + $len)); - $len += 2; - $column["columnlen"] = DataTypes::decode_int32(substr($packet, $off + $len)); - $len += 4; - $column["type"] = ord($packet[$off + $len]); - $len += 1; - $column["flags"] = DataTypes::decode_int16(substr($packet, $off + $len)); - $len += 2; - $column["decimals"] = ord($packet[$off + $len]); - //$len += 1; - - $off += $fixlen; - } else { - $column["table"] = DataTypes::decodeStringOff($packet, $off); - $column["name"] = DataTypes::decodeStringOff($packet, $off); - - $collen = DataTypes::decodeIntOff($packet, $off); - $column["columnlen"] = DataTypes::decode_intByLen(substr($packet, $off), $collen); - $off += $collen; - - $typelen = DataTypes::decodeIntOff($packet, $off); - $column["type"] = DataTypes::decode_intByLen(substr($packet, $off), $typelen); - $off += $typelen; - - $len = 1; - $flaglen = $this->capabilities & self::CLIENT_LONG_FLAG ? DataTypes::decodeInt(substr($packet, $off, 9), $len) : ord($packet[$off]); - $off += $len; - - if ($flaglen > 2) { - $len = 2; - $column["flags"] = DataTypes::decode_int16(substr($packet, $off, 4)); - } else { - $len = 1; - $column["flags"] = ord($packet[$off]); - } - $column["decimals"] = ord($packet[$off + $len]); - $off += $flaglen; - } - - if ($off < \strlen($packet)) { - $column["defaults"] = DataTypes::decodeString(substr($packet, $off)); - } - - return $column; - } - - private function successfulResultsetFetch() { - $deferred = &$this->result->next; - if ($this->connInfo->statusFlags & StatusFlags::SERVER_MORE_RESULTS_EXISTS) { - $this->parseCallback = [$this, "handleQuery"]; - $this->deferreds[] = $deferred ?: $deferred = new Deferred; - } else { - if (!$deferred) { - $deferred = new Deferred; - } - $deferred->succeed(); - $this->parseCallback = null; - } - $this->query = null; - $this->ready(); - $this->result->updateState(ResultProxy::ROWS_FETCHED); - } - - /** @see 14.6.4.1.1.3 Resultset Row */ - private function handleTextResultsetRow($packet) { - switch ($type = ord($packet)) { - case self::OK_PACKET: - $this->parseOk($packet); - /* intentional fallthrough */ - case self::EOF_PACKET: - if ($type == self::EOF_PACKET) { - $this->parseEof($packet); - } - $this->successfulResultsetFetch(); - return; - } - - $off = 0; - - $fields = []; - while ($off < \strlen($packet)) { - if (ord($packet[$off]) == 0xfb) { - $fields[] = null; - $off += 1; - } else { - $fields[] = DataTypes::decodeStringOff($packet, $off); - } - } - $this->result->rowFetched($fields); - } - - /** @see 14.7.2 Binary Protocol Resultset Row */ - private function handleBinaryResultsetRow($packet) { - if (ord($packet) == self::EOF_PACKET) { - $this->parseEof($packet); - $this->successfulResultsetFetch(); - return; - } - - $off = 1; // skip first byte - - $columnCount = $this->result->columnCount; - $columns = $this->result->columns; - $fields = []; - - for ($i = 0; $i < $columnCount; $i++) { - if (ord($packet[$off + (($i + 2) >> 3)]) & (1 << (($i + 2) % 8))) { - $fields[$i] = null; - } - } - $off += ($columnCount + 9) >> 3; - - for ($i = 0; $off < \strlen($packet); $i++) { - while (array_key_exists($i, $fields)) $i++; - $fields[$i] = DataTypes::decodeBinary($columns[$i]["type"], substr($packet, $off), $len); - $off += $len; - } - ksort($fields); - $this->result->rowFetched($fields); - } - - /** @see 14.7.4.1 COM_STMT_PREPARE Response */ - private function handlePrepare($packet) { - switch (ord($packet)) { - case self::OK_PACKET: - break; - case self::ERR_PACKET: - $this->handleError($packet); - return; - default: - throw new \UnexpectedValueException("Unexpected value for first byte of COM_STMT_PREPARE Response"); - } - $off = 1; - - $stmtId = DataTypes::decode_int32(substr($packet, $off)); - $off += 4; - - $columns = DataTypes::decode_int16(substr($packet, $off)); - $off += 2; - - $params = DataTypes::decode_int16(substr($packet, $off)); - $off += 2; - - $off += 1; // filler - - $this->connInfo->warnings = DataTypes::decode_int16(substr($packet, $off)); - - $this->result = new ResultProxy; - $this->result->columnsToFetch = $params; - $this->result->columnCount = $columns; - $this->getDeferred()->succeed(new Stmt($this, $this->query, $stmtId, $this->named, $this->result)); - $this->named = []; - if ($params) { - $this->parseCallback = [$this, "prepareParams"]; - } else { - $this->prepareParams($packet); - } - } - - private function readStatistics($packet) { - $this->getDeferred()->succeed($packet); - $this->ready(); - $this->parseCallback = null; - } - - private function closeSocket() { - $this->disableRead(); - $this->disableWrite(); - @fclose($this->socket); - $this->connectionState = self::CLOSED; - } - - private function compilePacket() { - do { - $pending = current($this->out); - unset($this->out[key($this->out)]); - if ($pending !== null || empty($this->out)) { - break; - } - $this->seqId = $this->compressionId = -1; - } while (1); - if ($pending == "") { - return $pending; - } - - $packet = ""; - do { - $len = strlen($pending); - if ($len >= (1 << 24) - 1) { - $out = substr($pending, 0, (1 << 24) - 1); - $pending = substr($pending, (1 << 24) - 1); - $len = (1 << 24) - 1; - } else { - $out = $pending; - $pending = ""; - } - $packet .= substr_replace(pack("V", $len), chr(++$this->seqId), 3, 1) . $out; // expects $len < (1 << 24) - 1 - } while ($pending != ""); - - if (defined("MYSQL_DEBUG")) { - fwrite(STDERR, "out: "); - for ($i = 0; $i < min(strlen($packet), 200); $i++) - fwrite(STDERR, dechex(ord($packet[$i])) . " "); - $r = range("\0", "\x1f"); - unset($r[10], $r[9]); - fwrite(STDERR, "len: ".strlen($packet)." "); - ob_start(); - var_dump(str_replace($r, ".", substr($packet, 0, 200))); - fwrite(STDERR, ob_get_clean()); - } - - return $packet; - } - - private function compressPacket($packet) { - $packet = $this->uncompressedOut.$packet; - - if ($packet == "") { - return ""; - } - - $len = strlen($packet); - while ($len < self::MAX_UNCOMPRESSED_BUFLEN && !empty($this->out)) { - $packet .= $this->compilePacket(); - $len = strlen($this->uncompressedOut); - } - - $this->uncompressedOut = substr($packet, self::MAX_UNCOMPRESSED_BUFLEN); - $packet = substr($packet, 0, self::MAX_UNCOMPRESSED_BUFLEN); - $len = strlen($packet); - - $deflated = zlib_encode($packet, ZLIB_ENCODING_DEFLATE); - if ($len < strlen($deflated)) { - $out = substr_replace(pack("V", strlen($packet)), chr(++$this->compressionId), 3, 1) . "\0\0\0" . $packet; - } else { - $out = substr_replace(pack("V", strlen($deflated)), chr(++$this->compressionId), 3, 1) . substr(pack("V", $len), 0, 3) . $deflated; - } - - return $out; - } - - public function onWrite() { - if ($this->outBuflen == 0) { - $packet = $this->compilePacket(); - if (($this->capabilities & self::CLIENT_COMPRESS) && $this->connectionState >= self::READY) { - $packet = $this->compressPacket($packet); - } - - $this->outBuf = $packet; - $this->outBuflen = strlen($packet); - - if ($this->outBuflen == 0) { - $this->disableWrite(); - $this->watcherEnabled = false; - return; - } - } - - $bytes = @fwrite($this->socket, $this->outBuf); - $this->outBuflen -= $bytes; - if ($this->outBuflen > 0) { - if ($bytes == 0) { - $this->goneAway(); - } else { - $this->outBuf = substr($this->outBuf, $bytes); - } - } - } - - public function onRead() { - $bytes = @fread($this->socket, $this->readGranularity); - if ($bytes != "") { - foreach ($this->processors as $processor) { - if ("" == $bytes = $processor->send($bytes)) { - return; - } - } - - foreach ($bytes as $packet) { - $this->parsePayload($packet); - } - } else { - $this->goneAway(); - } - } - - private function goneAway() { - foreach ($this->deferreds as $deferred) { - if ($this->config->exceptions || $this->connectionState < self::READY) { - if ($this->query == "") { - $deferred->fail(new InitializationException("Connection went away")); - } else { - $deferred->fail(new QueryException("Connection went away... unable to fulfil this deferred ... It's unknown whether the query was executed...", $this->query)); - } - } else { - $deferred->succeed(false); - } - } - $this->closeSocket(); - if (null !== $cb = $this->config->restore) { - $cb($this, $this->connectionState < self::READY); - /* @TODO if packet not completely sent, resend? */ - } - } - - /** @see 14.4 Compression */ - private function parseCompression() { - $inflated = ""; - $buf = ""; - - while (true) { - while (\strlen($buf) < 4) { - $buf .= (yield $inflated); - $inflated = ""; - } - - $size = DataTypes::decode_int24($buf); - $this->compressionId = ord($buf[3]); - $uncompressed = DataTypes::decode_int24(substr($buf, 4, 3)); - - $buf = substr($buf, 7); - - if ($size > 0) { - while (\strlen($buf) < $size) { - $buf .= (yield $inflated); - $inflated = ""; - } - - if ($uncompressed == 0) { - $inflated .= substr($buf, 0, $size); - } else { - $inflated .= zlib_decode(substr($buf, 0, $size), $uncompressed); - } - - $buf = substr($buf, $size); - } - } - } - - /** - * @see 14.1.2 MySQL Packet - * @see 14.1.3 Generic Response Packets - */ - private function parseMysql() { - $buf = ""; - $parsed = []; - - while (true) { - $packet = ""; - - do { - while (\strlen($buf) < 4) { - $buf .= (yield $parsed); - $parsed = []; - } - - $len = DataTypes::decode_int24($buf); - $this->seqId = ord($buf[3]); - $buf = substr($buf, 4); - - while (\strlen($buf) < ($len & 0xffffff)) { - $buf .= (yield $parsed); - $parsed = []; - } - - $lastIn = $len != 0xffffff; - if ($lastIn) { - $size = $len % 0xffffff; - } else { - $size = 0xffffff; - } - - $packet .= substr($buf, 0, $size); - $buf = substr($buf, $size); - } while (!$lastIn); - - if (\strlen($packet) > 0) { - if (defined("MYSQL_DEBUG")) { - fwrite(STDERR, "in: "); - $print = substr_replace(pack("V", \strlen($packet)), chr($this->seqId), 3, 1); - for ($i = 0; $i < 4; $i++) - fwrite(STDERR, dechex(ord($print[$i])) . " "); - for ($i = 0; $i < min(200, \strlen($packet)); $i++) - fwrite(STDERR, dechex(ord($packet[$i])) . " "); - $r = range("\0", "\x1f"); - unset($r[10], $r[9]); - fwrite(STDERR, "len: " . \strlen($packet) . " "); - ob_start(); - var_dump(str_replace($r, ".", substr($packet, 0, 200))); - fwrite(STDERR, ob_get_clean()); - } - - $parsed[] = $packet; - - } - } - } - - private function parsePayload($packet) { - if ($this->connectionState === self::UNCONNECTED) { - $this->established(); - $this->connectionState = self::ESTABLISHED; - $this->handleHandshake($packet); - } elseif ($this->connectionState === self::ESTABLISHED) { - switch (ord($packet)) { - case self::OK_PACKET: - if ($this->capabilities & self::CLIENT_COMPRESS) { - $this->processors = array_merge([$this->parseCompression()], $this->processors); - } - $this->connectionState = self::READY; - $this->handleOk($packet); - break; - case self::ERR_PACKET: - $this->handleError($packet); - break; - case self::EXTRA_AUTH_PACKET: - /** @see 14.2.5 Connection Phase Packets (AuthMoreData) */ - switch ($this->authPluginName) { - case "sha256_password": - $key = substr($packet, 1); - $this->config->key = $key; - $this->sendHandshake(); - break; - default: - throw new \UnexpectedValueException("Unexpected EXTRA_AUTH_PACKET in authentication phase for method {$this->authPluginName}"); - } - break; - } - } else { - if ($this->parseCallback) { - $cb = $this->parseCallback; - $cb($packet); - return; - } - - $cb = $this->packetCallback; - $this->packetCallback = null; - switch (ord($packet)) { - case self::OK_PACKET: - $this->handleOk($packet); - break; - case self::LOCAL_INFILE_REQUEST: - $this->handleLocalInfileRequest($packet); - break; - case self::ERR_PACKET: - $this->handleError($packet); - break; - case self::EOF_PACKET: - if (\strlen($packet) < 6) { - $this->handleEof($packet); - break; - } - /* intentionally missing break */ - default: - if ($cb) { - $cb($packet); - } else { - throw new \UnexpectedValueException("Unexpected packet type: " . ord($packet)); - } - } - } - } - - private function secureAuth($pass, $scramble) { - $hash = sha1($pass, 1); - return $hash ^ sha1(substr($scramble, 0, 20) . sha1($hash, 1), 1); - } - - private function sha256Auth($pass, $scramble, $key) { - openssl_public_encrypt($pass ^ str_repeat($scramble, ceil(strlen($pass) / strlen($scramble))), $auth, $key, OPENSSL_PKCS1_OAEP_PADDING); - return $auth; - } - - private function authSwitchRequest($packet) { - $this->parseCallback = null; - switch (ord($packet)) { - case self::EOF_PACKET: - if (\strlen($packet) == 1) { - break; - } - $len = strpos($packet, "\0"); - $pluginName = substr($packet, 0, $len); // @TODO mysql_native_pass only now... - $authPluginData = substr($packet, $len + 1); - $this->sendPacket($this->secureAuth($this->config->pass, $authPluginData)); - break; - case self::ERR_PACKET: - $this->handleError($packet); - return; - default: - throw new \UnexpectedValueException("AuthSwitchRequest: Expecting 0xfe (or ERR_Packet), got 0x".dechex(ord($packet))); - } - } - - /** - * @see 14.2.5 Connection Phase Packets - * @see 14.3 Authentication Method - */ - private function sendHandshake($inSSL = false) { - if ($this->config->db !== null) { - $this->capabilities |= self::CLIENT_CONNECT_WITH_DB; - } - - if ($this->config->ssl !== null) { - $this->capabilities |= self::CLIENT_SSL; - } - - $this->capabilities &= $this->serverCapabilities; - - $payload = ""; - $payload .= pack("V", $this->capabilities); - $payload .= pack("V", 1 << 24 - 1); // max-packet size - $payload .= chr($this->config->binCharset); - $payload .= str_repeat("\0", 23); // reserved - - if (!$inSSL && ($this->capabilities & self::CLIENT_SSL)) { - $this->_sendPacket($payload); - \Amp\onWritable($this->socket, function ($watcherId, $socket) { - /* wait until main write watcher has written everything... */ - if ($this->outBuflen > 0 || !empty($this->out)) { - return; - } - - \Amp\cancel($watcherId); - \Amp\disable($this->readWatcher); // temporarily disable, reenable after establishing tls - \Amp\Socket\cryptoEnable($socket, $this->config->ssl + ['peer_name' => $this->config->host])->when(function ($error) { - if ($error) { - $this->getDeferred()->fail($error); - $this->closeSocket(); - return; - } - - \Amp\enable($this->readWatcher); - $this->sendHandshake(true); - }); - }); - return; - } - - $payload .= $this->config->user."\0"; - if ($this->config->pass == "") { - $auth = ""; - } elseif ($this->capabilities & self::CLIENT_PLUGIN_AUTH) { - switch ($this->authPluginName) { - case "mysql_native_password": - $auth = $this->secureAuth($this->config->pass, $this->authPluginData); - break; - case "mysql_clear_password": - $auth = $this->config->pass; - break; - case "sha256_password": - if ($this->config->pass === "") { - $auth = ""; - } else { - if (isset($this->config->key)) { - $auth = $this->sha256Auth($this->config->pass, $this->authPluginData, $this->config->key); - } else { - $auth = "\x01"; - } - } - break; - case "mysql_old_password": - throw new \UnexpectedValueException("mysql_old_password is outdated and insecure. Intentionally not implemented!"); - default: - throw new \UnexpectedValueException("Invalid (or unimplemented?) auth method requested by server: {$this->authPluginName}"); - } - } else { - $auth = $this->secureAuth($this->config->pass, $this->authPluginData); - } - if ($this->capabilities & self::CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA) { - $payload .= DataTypes::encodeInt(strlen($auth)); - $payload .= $auth; - } elseif ($this->capabilities & self::CLIENT_SECURE_CONNECTION) { - $payload .= chr(strlen($auth)); - $payload .= $auth; - } else { - $payload .= "$auth\0"; - } - if ($this->capabilities & self::CLIENT_CONNECT_WITH_DB) { - $payload .= "{$this->config->db}\0"; - } - if ($this->capabilities & self::CLIENT_PLUGIN_AUTH) { - $payload .= "\0"; // @TODO AUTH -// $payload .= "mysql_native_password\0"; - } - if ($this->capabilities & self::CLIENT_CONNECT_ATTRS) { - // connection attributes?! 5.6.6+ only! - } - $this->_sendPacket($payload); - } - - /** @see 14.1.2 MySQL Packet */ - private function sendPacket($payload) { - if ($this->connectionState !== self::READY) { - throw new \Exception("Connection not ready, cannot send any packets"); - } - - $this->_sendPacket($payload); - } - - private function _sendPacket($payload) { - $this->out[] = $payload; - if (!$this->watcherEnabled) { - $this->enableWrite(); - $this->watcherEnabled = true; - } - } - - private function enableWrite() { - if (!$this->writeWatcher) { - $this->writeWatcher = \Amp\onWritable($this->socket, [$this, "onWrite"]); - } - } - - private function disableWrite() { - if ($this->writeWatcher) { - \Amp\cancel($this->writeWatcher); - $this->writeWatcher = null; - } - } - - private function enableRead() { - if (!$this->readWatcher) { - $this->readWatcher = \Amp\onReadable($this->socket, [$this, "onRead"]); - } - } - - private function disableRead() { - if ($this->readWatcher) { - \Amp\cancel($this->readWatcher); - $this->readWatcher = null; - } + + public function __destruct() { + $this->processor->delRef(); } -} +} \ No newline at end of file diff --git a/lib/ConnectionPool.php b/lib/ConnectionPool.php index 2aa572c..948130b 100644 --- a/lib/ConnectionPool.php +++ b/lib/ConnectionPool.php @@ -13,17 +13,19 @@ class ConnectionPool { private $limit; public function __construct($config, $limit) { - $config->ready = function($conn) { $this->ready($conn); }; - /* @TODO ... pending queries ... */ - $config->restore = function($conn, $init) { - $this->unmapConnection($conn); + $config->ready = function($hash) { + $this->ready($hash); + }; + /* @TODO ... pending queries ... (!!) */ + $config->restore = function($hash, $init) { + $this->unmapConnection($hash); if ($init && empty($this->connections)) { $this->virtualConnection->fail(new \Exception("Connection failed")); } return $this->getReadyConnection(); }; - $config->busy = function($conn) { - if (isset($this->readyMap[$hash = spl_object_hash($conn)])) { + $config->busy = function($hash) { + if (isset($this->readyMap[$hash])) { unset($this->ready[$this->readyMap[$hash]]); } }; @@ -51,7 +53,7 @@ public function addConnection() { $this->connectionPromise = $conn->connect(); $this->connectionPromise->when(function ($error) use ($conn) { if ($error) { - $this->unmapConnection($conn); + $this->unmapConnection(spl_object_hash($conn)); if (empty($this->connections)) { $this->virtualConnection->fail($error); } @@ -89,13 +91,14 @@ public function useExceptions($set) { } } - private function ready($conn) { + private function ready($hash) { + $conn = $this->connections[$this->connectionMap[$hash]]; if (list($deferred, $method, $args) = $this->virtualConnection->getCall()) { $deferred->succeed(call_user_func_array([$conn, $method], $args)); } else { $this->ready[] = $conn; end($this->ready); - $this->readyMap[spl_object_hash($conn)] = key($this->ready); + $this->readyMap[$hash] = key($this->ready); reset($this->ready); } } @@ -123,13 +126,12 @@ public function getReadyConnection() { public function extractConnection() { return $this->getReadyConnection()->getThis()->when(function($e, $conn) { - $this->unmapConnection($conn); + $this->unmapConnection(spl_object_hash($conn)); }); } /* This method might be called multiple times with the same hash. Important is that it's unmapped immediately */ - private function unmapConnection($conn) { - $hash = spl_object_hash($conn); + private function unmapConnection($hash) { if (isset($this->connectionMap[$hash])) { unset($this->connections[$this->connectionMap[$hash]], $this->connectionMap[$hash]); } @@ -142,7 +144,7 @@ public function __destruct() { public function close() { foreach ($this->connections as $conn) { $conn->forceClose(); - $this->unmapConnection($conn); + $this->unmapConnection(spl_object_hash($conn)); } $this->ready = []; $this->readyMap = []; diff --git a/lib/Processor.php b/lib/Processor.php new file mode 100644 index 0000000..508b6d4 --- /dev/null +++ b/lib/Processor.php @@ -0,0 +1,1250 @@ +ready = $ready; + $this->busy = $busy; + $this->restore = $restore; + $this->connInfo = new ConnectionState; + } + + public function alive() { + return $this->connectionState <= self::READY; + } + + public function isReady() { + return $this->connectionState === self::READY; + } + + public function delRef() { + if (!--$this->refcount) { + $this->appendTask(function() { + $this->closeSocket(); + }); + } + } + + public function forceClose() { + $this->closeSocket(); + } + + private function ready() { + if (empty($this->deferreds)) { + if (empty($this->onReady)) { + $cb = $this->ready; + $this->out[] = null; + } else { + list($key, $cb) = each($this->onReady); + unset($this->onReady[$key]); + } + if (isset($cb) && is_callable($cb)) { + $cb(); + } + } + } + + public function connect() { + \assert(!$this->deferreds && !$this->socket, self::class."::connect() must not be called twice"); + + $this->deferreds[] = $deferred = new Deferred; + \Amp\Socket\connect($this->config->resolvedHost)->when(function ($error, $socket) use ($deferred) { + if ($this->connectionState === self::CLOSED) { + $deferred->succeed(null); + if ($socket) { + fclose($socket); + } + return; + } + + if ($error) { + $deferred->fail($error); + if ($socket) { + fclose($socket); + } + return; + } + + $this->processors = [$this->parseMysql()]; + + $this->socket = $socket; + $this->readWatcher = \Amp\onReadable($this->socket, [$this, "onInit"]); + }); + return $deferred->promise(); + } + + public function onInit() { + // reset internal state + $this->out = []; + $this->seqId = $this->compressionId = -1; + + \Amp\cancel($this->readWatcher); + $this->readWatcher = \Amp\onReadable($this->socket, [$this, "onRead"]); + $this->writeWatcher = \Amp\onWritable($this->socket, [$this, "onWrite"]); + $this->onRead(); + } + + /** @return Deferred */ + private function getDeferred() { + list($key, $deferred) = each($this->deferreds); + unset($this->deferreds[$key]); + return $deferred; + } + + private function appendTask($callback) { + if ($this->packetCallback || $this->parseCallback || !empty($this->onReady) || !empty($this->deferreds) || $this->connectionState != self::READY) { + $this->onReady[] = $callback; + } else { + $cb = $this->busy; + if (isset($cb)) { + $cb(); + } + $callback(); + } + } + + public function getConnInfo() { + return clone $this->connInfo; + } + + public function startCommand($callback) { + $deferred = new Deferred; + $this->appendTask(function() use ($callback, $deferred) { + $this->seqId = $this->compressionId = -1; + $this->deferreds[] = $deferred; + $callback(); + }); + return $deferred->promise(); + } + + public function setQuery($query) { + $this->query = $query; + $this->packetCallback = [$this, "handleQuery"]; + } + + public function setPrepare($query) { + $this->query = $query; + $this->parseCallback = [$this, "handlePrepare"]; + } + + public function setFieldListing() { + $this->parseCallback = [$this, "handleFieldlist"]; + } + + public function setStatisticsReading() { + $this->parseCallback = [$this, "readStatistics"]; + } + + /** @see 14.6.18 COM_CHANGE_USER */ + /* @TODO broken, my test server doesn't support that command, can't test now + public function changeUser($user, $pass, $db = null) { + return $this->startCommand(function() use ($user, $pass, $db) { + $this->config->user = $user; + $this->config->pass = $pass; + $this->config->db = $db; + $payload = "\x11"; + + $payload .= "$user\0"; + $auth = $this->secureAuth($this->config->pass, $this->authPluginData); + if ($this->capabilities & self::CLIENT_SECURE_CONNECTION) { + $payload .= ord($auth) . $auth; + } else { + $payload .= "$auth\0"; + } + $payload .= "$db\0"; + + $this->sendPacket($payload); + $this->parseCallback = [$this, "authSwitchRequest"]; + }); + } + */ + + /** @see 14.7.5 COM_STMT_SEND_LONG_DATA */ + public function bindParam($stmtId, $paramId, $data) { + $payload = "\x18"; + $payload .= DataTypes::encode_int32($stmtId); + $payload .= DataTypes::encode_int16($paramId); + $payload .= $data; + $this->appendTask(function () use ($payload) { + $this->out[] = null; + $this->sendPacket($payload); + $this->ready(); + }); + } + + /** @see 14.7.6 COM_STMT_EXECUTE */ + // prebound params: null-bit set, type MYSQL_TYPE_LONG_BLOB, no value + // $params is by-ref, because the actual result object might not yet have been filled completely with data upon call of this method ... + public function execute($stmtId, $query, &$params, $prebound, $data = []) { + $deferred = new Deferred; + $this->appendTask(function () use ($stmtId, $query, &$params, $prebound, $data, $deferred) { + $payload = "\x17"; + $payload .= DataTypes::encode_int32($stmtId); + $payload .= chr(0); // cursor flag // @TODO cursor types?! + $payload .= DataTypes::encode_int32(1); + $paramCount = count($params); + $bound = !empty($data) || !empty($prebound); + $types = ""; + $values = ""; + if ($paramCount) { + $args = $data + array_fill(0, $paramCount, null); + ksort($args); + $args = array_slice($args, 0, $paramCount); + $nullOff = strlen($payload); + $payload .= str_repeat("\0", ($paramCount + 7) >> 3); + foreach ($args as $paramId => $param) { + if ($param === null) { + $off = $nullOff + ($paramId >> 3); + $payload[$off] = $payload[$off] | chr(1 << ($paramId % 8)); + } else { + $bound = 1; + } + list($unsigned, $type, $value) = DataTypes::encodeBinary($param); + if (isset($prebound[$paramId])) { + $types .= chr(DataTypes::MYSQL_TYPE_LONG_BLOB); + } else { + $types .= chr($type); + } + $types .= $unsigned?"\x80":"\0"; + $values .= $value; + } + $payload .= chr($bound); + if ($bound) { + $payload .= $types; + $payload .= $values; + } + } + + $this->query = $query; + + $this->out[] = null; + $this->deferreds[] = $deferred; + $this->sendPacket($payload); + $this->packetCallback = [$this, "handleExecute"]; + }); + return $deferred->promise(); // do not use $this->startCommand(), that might unexpectedly reset the seqId! + } + + /** @see 14.7.7 COM_STMT_CLOSE */ + public function closeStmt($stmtId) { + $payload = "\x19" . DataTypes::encode_int32($stmtId); + $this->appendTask(function () use ($payload) { + if ($this->connectionState === self::READY) { + $this->out[] = null; + $this->sendPacket($payload); + } + $this->ready(); + }); + } + + /** @see 14.7.8 COM_STMT_RESET */ + public function resetStmt($stmtId) { + $payload = "\x1a" . DataTypes::encode_int32($stmtId); + $deferred = new Deferred; + $this->appendTask(function () use ($payload, $deferred) { + $this->out[] = null; + $this->deferreds[] = $deferred; + $this->sendPacket($payload); + }); + return $deferred->promise(); + } + + /** @see 14.8.4 COM_STMT_FETCH */ + public function fetchStmt($stmtId) { + $payload = "\x1c" . DataTypes::encode_int32($stmtId) . DataTypes::encode_int32(1); + $deferred = new Deferred; + $this->appendTask(function () use ($payload, $deferred) { + $this->out[] = null; + $this->deferreds[] = $deferred; + $this->sendPacket($payload); + }); + return $deferred->promise(); + } + + private function established() { + // @TODO flags to use? + $this->capabilities |= self::CLIENT_SESSION_TRACK | self::CLIENT_TRANSACTIONS | self::CLIENT_PROTOCOL_41 | self::CLIENT_SECURE_CONNECTION | self::CLIENT_MULTI_RESULTS | self::CLIENT_PS_MULTI_RESULTS | self::CLIENT_MULTI_STATEMENTS | self::CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA; + + if (extension_loaded("zlib")) { + $this->capabilities |= self::CLIENT_COMPRESS; + } + } + + /** @see 14.1.3.2 ERR-Packet */ + private function handleError($packet) { + $off = 1; + + $this->connInfo->errorCode = DataTypes::decode_int16(substr($packet, $off, 2)); + $off += 2; + + if ($this->capabilities & self::CLIENT_PROTOCOL_41) { + $this->connInfo->errorState = substr($packet, $off, 6); + $off += 6; + } + + $this->connInfo->errorMsg = substr($packet, $off); + + $this->parseCallback = null; + if ($this->connectionState == self::READY) { + // normal error + if ($this->config->exceptions) { + $this->getDeferred()->fail(new QueryException("MySQL error ({$this->connInfo->errorCode}): {$this->connInfo->errorState} {$this->connInfo->errorMsg}", $this->query)); + } else { + $this->getDeferred()->succeed(false); + } + $this->query = null; + $this->ready(); + } elseif ($this->connectionState < self::READY) { + // connection failure + $this->closeSocket(); + $this->getDeferred()->fail(new InitializationException("Could not connect to {$this->config->resolvedHost}: {$this->connInfo->errorState} {$this->connInfo->errorMsg}")); + } + } + + /** @see 14.1.3.1 OK-Packet */ + private function parseOk($packet) { + $off = 1; + + $this->connInfo->affectedRows = DataTypes::decodeInt(substr($packet, $off), $intlen); + $off += $intlen; + + $this->connInfo->insertId = DataTypes::decodeInt(substr($packet, $off), $intlen); + $off += $intlen; + + if ($this->capabilities & (self::CLIENT_PROTOCOL_41 | self::CLIENT_TRANSACTIONS)) { + $this->connInfo->statusFlags = DataTypes::decode_int16(substr($packet, $off)); + $off += 2; + + $this->connInfo->warnings = DataTypes::decode_int16(substr($packet, $off)); + $off += 2; + } + + if ($this->capabilities & self::CLIENT_SESSION_TRACK) { + $this->connInfo->statusInfo = DataTypes::decodeStringOff($packet, $off); + + if ($this->connInfo->statusFlags & StatusFlags::SERVER_SESSION_STATE_CHANGED) { + $sessionState = DataTypes::decodeString(substr($packet, $off), $intlen, $sessionStateLen); + $len = 0; + while ($len < $sessionStateLen) { + $data = DataTypes::decodeString(substr($sessionState, $len + 1), $datalen, $intlen); + + switch ($type = DataTypes::decode_int8(substr($sessionState, $len))) { + case SessionStateTypes::SESSION_TRACK_SYSTEM_VARIABLES: + $var = DataTypes::decodeString($data, $varintlen, $strlen); + $this->connInfo->sessionState[SessionStateTypes::SESSION_TRACK_SYSTEM_VARIABLES][$var] = DataTypes::decodeString(substr($data, $varintlen + $strlen)); + break; + case SessionStateTypes::SESSION_TRACK_SCHEMA: + $this->connInfo->sessionState[SessionStateTypes::SESSION_TRACK_SCHEMA] = DataTypes::decodeString($data); + break; + case SessionStateTypes::SESSION_TRACK_STATE_CHANGE: + $this->connInfo->sessionState[SessionStateTypes::SESSION_TRACK_STATE_CHANGE] = DataTypes::decodeString($data); + break; + default: + throw new \UnexpectedValueException("$type is not a valid mysql session state type"); + } + + $len += 1 + $intlen + $datalen; + } + } + } else { + $this->connInfo->statusInfo = substr($packet, $off); + } + } + + private function handleOk($packet) { + $this->parseOk($packet); + $this->getDeferred()->succeed($this->getConnInfo()); + $this->ready(); + } + + /** @see 14.1.3.3 EOF-Packet */ + private function parseEof($packet) { + if ($this->capabilities & self::CLIENT_PROTOCOL_41) { + $this->connInfo->warnings = DataTypes::decode_int16(substr($packet, 1)); + + $this->connInfo->statusFlags = DataTypes::decode_int16(substr($packet, 3)); + } + } + + private function handleEof($packet) { + $this->parseEof($packet); + $this->getDeferred()->succeed($this->getConnInfo()); + $this->ready(); + } + + /** @see 14.2.5 Connection Phase Packets */ + private function handleHandshake($packet) { + $off = 1; + + $this->protocol = ord($packet); + if ($this->protocol !== 0x0a) { + throw new \UnexpectedValueException("Unsupported protocol version ".ord($packet)." (Expected: 10)"); + } + + $this->connInfo->serverVersion = DataTypes::decodeNullString(substr($packet, $off), $len); + $off += $len + 1; + + $this->connectionId = DataTypes::decode_int32(substr($packet, $off)); + $off += 4; + + $this->authPluginData = substr($packet, $off, 8); + $off += 8; + + $off += 1; // filler byte + + $this->serverCapabilities = DataTypes::decode_int16(substr($packet, $off)); + $off += 2; + + if (\strlen($packet) > $off) { + $this->connInfo->charset = ord(substr($packet, $off)); + $off += 1; + + $this->connInfo->statusFlags = DataTypes::decode_int16(substr($packet, $off)); + $off += 2; + + $this->serverCapabilities += DataTypes::decode_int16(substr($packet, $off)) << 16; + $off += 2; + + $this->authPluginDataLen = $this->serverCapabilities & self::CLIENT_PLUGIN_AUTH ? ord(substr($packet, $off)) : 0; + $off += 1; + + if ($this->serverCapabilities & self::CLIENT_SECURE_CONNECTION) { + $off += 10; + + $strlen = max(13, $this->authPluginDataLen - 8); + $this->authPluginData .= substr($packet, $off, $strlen); + $off += $strlen; + + if ($this->serverCapabilities & self::CLIENT_PLUGIN_AUTH) { + $this->authPluginName = DataTypes::decodeNullString(substr($packet, $off)); + } + } + } + + $this->sendHandshake(); + } + + /** @see 14.6.4.1.2 LOCAL INFILE Request */ + private function handleLocalInfileRequest($packet) { + // @TODO async file fetch @rdlowrey + $file = file_get_contents($packet); + if ($file != "") { + $this->sendPacket($file); + } + $this->sendPacket(""); + } + + /** @see 14.6.4.1.1 Text Resultset */ + private function handleQuery($packet) { + $this->parseCallback = [$this, "handleTextColumnDefinition"]; + $this->getDeferred()->succeed(new ResultSet($this->connInfo, $result = new ResultProxy)); + /* we need to succeed before assigning vars, so that a when() handler won't have a partial result available */ + $this->result = $result; + $this->result->setColumns(DataTypes::decodeInt($packet)); + } + + /** @see 14.7.1 Binary Protocol Resultset */ + private function handleExecute($packet) { + $this->parseCallback = [$this, "handleBinaryColumnDefinition"]; + $this->getDeferred()->succeed(new ResultSet($this->connInfo, $result = new ResultProxy)); + /* we need to succeed before assigning vars, so that a when() handler won't have a partial result available */ + $this->result = $result; + $this->result->setColumns(ord($packet)); + } + + private function handleFieldList($packet) { + if (ord($packet) == self::ERR_PACKET) { + $this->parseCallback = null; + $this->handleError($packet); + } elseif (ord($packet) == self::EOF_PACKET) { + $this->parseCallback = null; + $this->parseEof($packet); + $this->getDeferred()->succeed(null); + $this->ready(); + } else { + $this->getDeferred()->succeed([$this->parseColumnDefinition($packet), $this->deferreds[] = new Deferred]); + } + } + + private function handleTextColumnDefinition($packet) { + $this->handleColumnDefinition($packet, "handleTextResultsetRow"); + } + + private function handleBinaryColumnDefinition($packet) { + $this->handleColumnDefinition($packet, "handleBinaryResultsetRow"); + } + + private function handleColumnDefinition($packet, $cbMethod) { + if (!$this->result->columnsToFetch--) { + $this->result->updateState(ResultProxy::COLUMNS_FETCHED); + if (ord($packet) == self::ERR_PACKET) { + $this->parseCallback = null; + $this->handleError($packet); + } else { + $cb = $this->parseCallback = [$this, $cbMethod]; + if ($this->capabilities & self::CLIENT_DEPRECATE_EOF) { + $cb($packet); + } else { + $this->parseEof($packet); + // we don't need the EOF packet, skip! + } + } + return; + } + + $this->result->columns[] = $this->parseColumnDefinition($packet); + } + + private function prepareParams($packet) { + if (!$this->result->columnsToFetch--) { + $this->result->columnsToFetch = $this->result->columnCount; + if (!$this->result->columnsToFetch) { + $this->prepareFields($packet); + } else { + $this->parseCallback = [$this, "prepareFields"]; + } + return; + } + + $this->result->params[] = $this->parseColumnDefinition($packet); + } + + private function prepareFields($packet) { + if (!$this->result->columnsToFetch--) { + $this->parseCallback = null; + $this->result->updateState(ResultProxy::COLUMNS_FETCHED); + $this->query = null; + $this->ready(); + + return; + } + + $this->result->columns[] = $this->parseColumnDefinition($packet); + } + + /** @see 14.6.4.1.1.2 Column Defintion */ + private function parseColumnDefinition($packet) { + $off = 0; + + $column = []; + + if ($this->capabilities & self::CLIENT_PROTOCOL_41) { + $column["catalog"] = DataTypes::decodeStringOff($packet, $off); + $column["schema"] = DataTypes::decodeStringOff($packet, $off); + $column["table"] = DataTypes::decodeStringOff($packet, $off); + $column["original_table"] = DataTypes::decodeStringOff($packet, $off); + $column["name"] = DataTypes::decodeStringOff($packet, $off); + $column["original_name"] = DataTypes::decodeStringOff($packet, $off); + $fixlen = DataTypes::decodeIntOff($packet, $off); + + $len = 0; + $column["charset"] = DataTypes::decode_int16(substr($packet, $off + $len)); + $len += 2; + $column["columnlen"] = DataTypes::decode_int32(substr($packet, $off + $len)); + $len += 4; + $column["type"] = ord($packet[$off + $len]); + $len += 1; + $column["flags"] = DataTypes::decode_int16(substr($packet, $off + $len)); + $len += 2; + $column["decimals"] = ord($packet[$off + $len]); + //$len += 1; + + $off += $fixlen; + } else { + $column["table"] = DataTypes::decodeStringOff($packet, $off); + $column["name"] = DataTypes::decodeStringOff($packet, $off); + + $collen = DataTypes::decodeIntOff($packet, $off); + $column["columnlen"] = DataTypes::decode_intByLen(substr($packet, $off), $collen); + $off += $collen; + + $typelen = DataTypes::decodeIntOff($packet, $off); + $column["type"] = DataTypes::decode_intByLen(substr($packet, $off), $typelen); + $off += $typelen; + + $len = 1; + $flaglen = $this->capabilities & self::CLIENT_LONG_FLAG ? DataTypes::decodeInt(substr($packet, $off, 9), $len) : ord($packet[$off]); + $off += $len; + + if ($flaglen > 2) { + $len = 2; + $column["flags"] = DataTypes::decode_int16(substr($packet, $off, 4)); + } else { + $len = 1; + $column["flags"] = ord($packet[$off]); + } + $column["decimals"] = ord($packet[$off + $len]); + $off += $flaglen; + } + + if ($off < \strlen($packet)) { + $column["defaults"] = DataTypes::decodeString(substr($packet, $off)); + } + + return $column; + } + + private function successfulResultsetFetch() { + $deferred = &$this->result->next; + if ($this->connInfo->statusFlags & StatusFlags::SERVER_MORE_RESULTS_EXISTS) { + $this->packetCallback = [$this, "handleQuery"]; + $this->deferreds[] = $deferred ?: $deferred = new Deferred; + } else { + if (!$deferred) { + $deferred = new Deferred; + } + $deferred->succeed(); + } + $this->parseCallback = null; + $this->query = null; + $this->ready(); + $this->result->updateState(ResultProxy::ROWS_FETCHED); + } + + /** @see 14.6.4.1.1.3 Resultset Row */ + private function handleTextResultsetRow($packet) { + switch ($type = ord($packet)) { + case self::OK_PACKET: + $this->parseOk($packet); + /* intentional fallthrough */ + case self::EOF_PACKET: + if ($type == self::EOF_PACKET) { + $this->parseEof($packet); + } + $this->successfulResultsetFetch(); + return; + } + + $off = 0; + + $fields = []; + while ($off < \strlen($packet)) { + if (ord($packet[$off]) == 0xfb) { + $fields[] = null; + $off += 1; + } else { + $fields[] = DataTypes::decodeStringOff($packet, $off); + } + } + $this->result->rowFetched($fields); + } + + /** @see 14.7.2 Binary Protocol Resultset Row */ + private function handleBinaryResultsetRow($packet) { + if (ord($packet) == self::EOF_PACKET) { + $this->parseEof($packet); + $this->successfulResultsetFetch(); + return; + } + + $off = 1; // skip first byte + + $columnCount = $this->result->columnCount; + $columns = $this->result->columns; + $fields = []; + + for ($i = 0; $i < $columnCount; $i++) { + if (ord($packet[$off + (($i + 2) >> 3)]) & (1 << (($i + 2) % 8))) { + $fields[$i] = null; + } + } + $off += ($columnCount + 9) >> 3; + + for ($i = 0; $off < \strlen($packet); $i++) { + while (array_key_exists($i, $fields)) $i++; + $fields[$i] = DataTypes::decodeBinary($columns[$i]["type"], substr($packet, $off), $len); + $off += $len; + } + ksort($fields); + $this->result->rowFetched($fields); + } + + /** @see 14.7.4.1 COM_STMT_PREPARE Response */ + private function handlePrepare($packet) { + switch (ord($packet)) { + case self::OK_PACKET: + break; + case self::ERR_PACKET: + $this->handleError($packet); + return; + default: + throw new \UnexpectedValueException("Unexpected value for first byte of COM_STMT_PREPARE Response"); + } + $off = 1; + + $stmtId = DataTypes::decode_int32(substr($packet, $off)); + $off += 4; + + $columns = DataTypes::decode_int16(substr($packet, $off)); + $off += 2; + + $params = DataTypes::decode_int16(substr($packet, $off)); + $off += 2; + + $off += 1; // filler + + $this->connInfo->warnings = DataTypes::decode_int16(substr($packet, $off)); + + $this->result = new ResultProxy; + $this->result->columnsToFetch = $params; + $this->result->columnCount = $columns; + $this->refcount++; + $this->getDeferred()->succeed(new Stmt($this, $this->query, $stmtId, $this->named, $this->result)); + $this->named = []; + if ($params) { + $this->parseCallback = [$this, "prepareParams"]; + } else { + $this->prepareParams($packet); + } + } + + private function readStatistics($packet) { + $this->getDeferred()->succeed($packet); + $this->ready(); + $this->parseCallback = null; + } + + public function initClosing() { + $this->connectionState = self::CLOSING; + } + + public function closeSocket() { + \Amp\cancel($this->readWatcher); + \Amp\cancel($this->writeWatcher); + @fclose($this->socket); + $this->connectionState = self::CLOSED; + } + + private function compilePacket() { + do { + $pending = current($this->out); + unset($this->out[key($this->out)]); + if ($pending !== null || empty($this->out)) { + break; + } + $this->seqId = $this->compressionId = -1; + } while (1); + if ($pending == "") { + return $pending; + } + + $packet = ""; + do { + $len = strlen($pending); + if ($len >= (1 << 24) - 1) { + $out = substr($pending, 0, (1 << 24) - 1); + $pending = substr($pending, (1 << 24) - 1); + $len = (1 << 24) - 1; + } else { + $out = $pending; + $pending = ""; + } + $packet .= substr_replace(pack("V", $len), chr(++$this->seqId), 3, 1) . $out; // expects $len < (1 << 24) - 1 + } while ($pending != ""); + + if (defined("MYSQL_DEBUG")) { + fwrite(STDERR, "out: "); + for ($i = 0; $i < min(strlen($packet), 200); $i++) + fwrite(STDERR, dechex(ord($packet[$i])) . " "); + $r = range("\0", "\x1f"); + unset($r[10], $r[9]); + fwrite(STDERR, "len: ".strlen($packet)." "); + ob_start(); + var_dump(str_replace($r, ".", substr($packet, 0, 200))); + fwrite(STDERR, ob_get_clean()); + } + + return $packet; + } + + private function compressPacket($packet) { + $packet = $this->uncompressedOut.$packet; + + if ($packet == "") { + return ""; + } + + $len = strlen($packet); + while ($len < self::MAX_UNCOMPRESSED_BUFLEN && !empty($this->out)) { + $packet .= $this->compilePacket(); + $len = strlen($this->uncompressedOut); + } + + $this->uncompressedOut = substr($packet, self::MAX_UNCOMPRESSED_BUFLEN); + $packet = substr($packet, 0, self::MAX_UNCOMPRESSED_BUFLEN); + $len = strlen($packet); + + $deflated = zlib_encode($packet, ZLIB_ENCODING_DEFLATE); + if ($len < strlen($deflated)) { + $out = substr_replace(pack("V", strlen($packet)), chr(++$this->compressionId), 3, 1) . "\0\0\0" . $packet; + } else { + $out = substr_replace(pack("V", strlen($deflated)), chr(++$this->compressionId), 3, 1) . substr(pack("V", $len), 0, 3) . $deflated; + } + + return $out; + } + + public function onWrite() { + if ($this->outBuflen == 0) { + $packet = $this->compilePacket(); + if (($this->capabilities & self::CLIENT_COMPRESS) && $this->connectionState >= self::READY) { + $packet = $this->compressPacket($packet); + } + + $this->outBuf = $packet; + $this->outBuflen = strlen($packet); + + if ($this->outBuflen == 0) { + \Amp\disable($this->writeWatcher); + return; + } + } + + $bytes = @fwrite($this->socket, $this->outBuf); + $this->outBuflen -= $bytes; + if ($this->outBuflen > 0) { + if ($bytes == 0) { + $this->goneAway(); + } else { + $this->outBuf = substr($this->outBuf, $bytes); + } + } + } + + public function onRead() { + $bytes = @fread($this->socket, $this->readGranularity); + if ($bytes != "") { + foreach ($this->processors as $processor) { + if ("" == $bytes = $processor->send($bytes)) { + return; + } + } + + foreach ($bytes as $packet) { + $this->parsePayload($packet); + } + } else { + $this->goneAway(); + } + } + + private function goneAway() { + foreach ($this->deferreds as $deferred) { + if ($this->config->exceptions || $this->connectionState < self::READY) { + if ($this->query == "") { + $deferred->fail(new InitializationException("Connection went away")); + } else { + $deferred->fail(new QueryException("Connection went away... unable to fulfil this deferred ... It's unknown whether the query was executed...", $this->query)); + } + } else { + $deferred->succeed(false); + } + } + $this->closeSocket(); + if (null !== $cb = $this->config->restore) { + $cb($this, $this->connectionState < self::READY); + /* @TODO if packet not completely sent, resend? */ + } + } + + /** @see 14.4 Compression */ + private function parseCompression() { + $inflated = ""; + $buf = ""; + + while (true) { + while (\strlen($buf) < 4) { + $buf .= (yield $inflated); + $inflated = ""; + } + + $size = DataTypes::decode_int24($buf); + $this->compressionId = ord($buf[3]); + $uncompressed = DataTypes::decode_int24(substr($buf, 4, 3)); + + $buf = substr($buf, 7); + + if ($size > 0) { + while (\strlen($buf) < $size) { + $buf .= (yield $inflated); + $inflated = ""; + } + + if ($uncompressed == 0) { + $inflated .= substr($buf, 0, $size); + } else { + $inflated .= zlib_decode(substr($buf, 0, $size), $uncompressed); + } + + $buf = substr($buf, $size); + } + } + } + + /** + * @see 14.1.2 MySQL Packet + * @see 14.1.3 Generic Response Packets + */ + private function parseMysql() { + $buf = ""; + $parsed = []; + + while (true) { + $packet = ""; + + do { + while (\strlen($buf) < 4) { + $buf .= (yield $parsed); + $parsed = []; + } + + $len = DataTypes::decode_int24($buf); + $this->seqId = ord($buf[3]); + $buf = substr($buf, 4); + + while (\strlen($buf) < ($len & 0xffffff)) { + $buf .= (yield $parsed); + $parsed = []; + } + + $lastIn = $len != 0xffffff; + if ($lastIn) { + $size = $len % 0xffffff; + } else { + $size = 0xffffff; + } + + $packet .= substr($buf, 0, $size); + $buf = substr($buf, $size); + } while (!$lastIn); + + if (\strlen($packet) > 0) { + if (defined("MYSQL_DEBUG")) { + fwrite(STDERR, "in: "); + $print = substr_replace(pack("V", \strlen($packet)), chr($this->seqId), 3, 1); + for ($i = 0; $i < 4; $i++) + fwrite(STDERR, dechex(ord($print[$i])) . " "); + for ($i = 0; $i < min(200, \strlen($packet)); $i++) + fwrite(STDERR, dechex(ord($packet[$i])) . " "); + $r = range("\0", "\x1f"); + unset($r[10], $r[9]); + fwrite(STDERR, "len: " . \strlen($packet) . " "); + ob_start(); + var_dump(str_replace($r, ".", substr($packet, 0, 200))); + fwrite(STDERR, ob_get_clean()); + } + + $parsed[] = $packet; + + } + } + } + + private function parsePayload($packet) { + if ($this->connectionState === self::UNCONNECTED) { + $this->established(); + $this->connectionState = self::ESTABLISHED; + $this->handleHandshake($packet); + } elseif ($this->connectionState === self::ESTABLISHED) { + switch (ord($packet)) { + case self::OK_PACKET: + if ($this->capabilities & self::CLIENT_COMPRESS) { + $this->processors = array_merge([$this->parseCompression()], $this->processors); + } + $this->connectionState = self::READY; + $this->handleOk($packet); + break; + case self::ERR_PACKET: + $this->handleError($packet); + break; + case self::EXTRA_AUTH_PACKET: + /** @see 14.2.5 Connection Phase Packets (AuthMoreData) */ + switch ($this->authPluginName) { + case "sha256_password": + $key = substr($packet, 1); + $this->config->key = $key; + $this->sendHandshake(); + break; + default: + throw new \UnexpectedValueException("Unexpected EXTRA_AUTH_PACKET in authentication phase for method {$this->authPluginName}"); + } + break; + } + } else { + if ($this->parseCallback) { + $cb = $this->parseCallback; + $cb($packet); + return; + } + + $cb = $this->packetCallback; + $this->packetCallback = null; + switch (ord($packet)) { + case self::OK_PACKET: + $this->handleOk($packet); + break; + case self::LOCAL_INFILE_REQUEST: + $this->handleLocalInfileRequest($packet); + break; + case self::ERR_PACKET: + $this->handleError($packet); + break; + case self::EOF_PACKET: + if (\strlen($packet) < 6) { + $this->handleEof($packet); + break; + } + /* intentionally missing break */ + default: + if ($cb) { + $cb($packet); + } else { + throw new \UnexpectedValueException("Unexpected packet type: " . ord($packet)); + } + } + } + } + + private function secureAuth($pass, $scramble) { + $hash = sha1($pass, 1); + return $hash ^ sha1(substr($scramble, 0, 20) . sha1($hash, 1), 1); + } + + private function sha256Auth($pass, $scramble, $key) { + openssl_public_encrypt($pass ^ str_repeat($scramble, ceil(strlen($pass) / strlen($scramble))), $auth, $key, OPENSSL_PKCS1_OAEP_PADDING); + return $auth; + } + + private function authSwitchRequest($packet) { + $this->parseCallback = null; + switch (ord($packet)) { + case self::EOF_PACKET: + if (\strlen($packet) == 1) { + break; + } + $len = strpos($packet, "\0"); + $pluginName = substr($packet, 0, $len); // @TODO mysql_native_pass only now... + $authPluginData = substr($packet, $len + 1); + $this->sendPacket($this->secureAuth($this->config->pass, $authPluginData)); + break; + case self::ERR_PACKET: + $this->handleError($packet); + return; + default: + throw new \UnexpectedValueException("AuthSwitchRequest: Expecting 0xfe (or ERR_Packet), got 0x".dechex(ord($packet))); + } + } + + /** + * @see 14.2.5 Connection Phase Packets + * @see 14.3 Authentication Method + */ + private function sendHandshake($inSSL = false) { + if ($this->config->db !== null) { + $this->capabilities |= self::CLIENT_CONNECT_WITH_DB; + } + + if ($this->config->ssl !== null) { + $this->capabilities |= self::CLIENT_SSL; + } + + $this->capabilities &= $this->serverCapabilities; + + $payload = ""; + $payload .= pack("V", $this->capabilities); + $payload .= pack("V", 1 << 24 - 1); // max-packet size + $payload .= chr($this->config->binCharset); + $payload .= str_repeat("\0", 23); // reserved + + if (!$inSSL && ($this->capabilities & self::CLIENT_SSL)) { + $this->_sendPacket($payload); + \Amp\onWritable($this->socket, function ($watcherId, $socket) { + /* wait until main write watcher has written everything... */ + if ($this->outBuflen > 0 || !empty($this->out)) { + return; + } + + \Amp\cancel($watcherId); + \Amp\disable($this->readWatcher); // temporarily disable, reenable after establishing tls + \Amp\Socket\cryptoEnable($socket, $this->config->ssl + ['peer_name' => $this->config->host])->when(function ($error) { + if ($error) { + $this->getDeferred()->fail($error); + $this->closeSocket(); + return; + } + + \Amp\enable($this->readWatcher); + $this->sendHandshake(true); + }); + }); + return; + } + + $payload .= $this->config->user."\0"; + if ($this->config->pass == "") { + $auth = ""; + } elseif ($this->capabilities & self::CLIENT_PLUGIN_AUTH) { + switch ($this->authPluginName) { + case "mysql_native_password": + $auth = $this->secureAuth($this->config->pass, $this->authPluginData); + break; + case "mysql_clear_password": + $auth = $this->config->pass; + break; + case "sha256_password": + if ($this->config->pass === "") { + $auth = ""; + } else { + if (isset($this->config->key)) { + $auth = $this->sha256Auth($this->config->pass, $this->authPluginData, $this->config->key); + } else { + $auth = "\x01"; + } + } + break; + case "mysql_old_password": + throw new \UnexpectedValueException("mysql_old_password is outdated and insecure. Intentionally not implemented!"); + default: + throw new \UnexpectedValueException("Invalid (or unimplemented?) auth method requested by server: {$this->authPluginName}"); + } + } else { + $auth = $this->secureAuth($this->config->pass, $this->authPluginData); + } + if ($this->capabilities & self::CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA) { + $payload .= DataTypes::encodeInt(strlen($auth)); + $payload .= $auth; + } elseif ($this->capabilities & self::CLIENT_SECURE_CONNECTION) { + $payload .= chr(strlen($auth)); + $payload .= $auth; + } else { + $payload .= "$auth\0"; + } + if ($this->capabilities & self::CLIENT_CONNECT_WITH_DB) { + $payload .= "{$this->config->db}\0"; + } + if ($this->capabilities & self::CLIENT_PLUGIN_AUTH) { + $payload .= "\0"; // @TODO AUTH +// $payload .= "mysql_native_password\0"; + } + if ($this->capabilities & self::CLIENT_CONNECT_ATTRS) { + // connection attributes?! 5.6.6+ only! + } + $this->_sendPacket($payload); + } + + /** @see 14.1.2 MySQL Packet */ + public function sendPacket($payload) { + if ($this->connectionState !== self::READY) { + throw new \Exception("Connection not ready, cannot send any packets"); + } + + $this->_sendPacket($payload); + } + + private function _sendPacket($payload) { + $this->out[] = $payload; + \Amp\enable($this->writeWatcher); + } +} diff --git a/lib/Stmt.php b/lib/Stmt.php index 53fcb19..f6a0fb1 100644 --- a/lib/Stmt.php +++ b/lib/Stmt.php @@ -18,7 +18,7 @@ class Stmt { private $result; - public function __construct(Connection $conn, $query, $stmtId, $named, ResultProxy $result) { + public function __construct(Processor $conn, $query, $stmtId, $named, ResultProxy $result) { $this->conn = $conn; $this->query = $query; $this->stmtId = $stmtId; @@ -37,7 +37,7 @@ private function conn() { if ($this->conn->alive()) { return $this->conn; } - $restore = $this->conn->getConfig()->restore; + $restore = $this->conn->restore; if (isset($restore)) { $restore()->prepare($this->query)->when(function($error, $stmt) { if ($error) { @@ -140,7 +140,7 @@ public function execute($data = []) { } public function close() { - if (isset($this->conn) && $this->conn instanceof Connection) { // might be already dtored + if (isset($this->conn)) { // might be already dtored $this->conn->closeStmt($this->stmtId); } } @@ -172,6 +172,9 @@ public function connInfo() { public function __destruct() { $this->close(); + if (isset($this->conn)) { + $this->conn->delRef(); + } } public function __debugInfo() { diff --git a/test/Mysql/ConnectionTest.php b/test/Mysql/ConnectionTest.php index cfc773f..26c96a3 100644 --- a/test/Mysql/ConnectionTest.php +++ b/test/Mysql/ConnectionTest.php @@ -76,7 +76,7 @@ function testMultiStmt() { $db->query("CREATE TABLE tmp SELECT 1 AS a, 2 AS b"); $db->query("INSERT INTO tmp VALUES (5, 6), (8, 9)"); - $resultset = (yield $db->query("SELECT a FROM tmp; SELECT b FROM tmp WHERE a = 5; SELECT b AS d, SUM(a) AS c FROM tmp WHERE b < 7")); + $resultset = (yield $db->query("SELECT a FROM tmp; SELECT b FROM tmp WHERE a = 5; SELECT b AS d, a + 1 AS c FROM tmp WHERE b < 7")); $this->assertEquals((yield $resultset->rowCount()), 3); $resultset = (yield $resultset->next()); @@ -89,7 +89,7 @@ function testMultiStmt() { $this->assertEquals($fields[0]["name"], "d"); $this->assertEquals($fields[0]["type"], DataTypes::MYSQL_TYPE_LONG); $this->assertEquals($fields[1]["name"], "c"); - $this->assertEquals($fields[1]["type"], DataTypes::MYSQL_TYPE_NEWDECIMAL); + $this->assertEquals($fields[1]["type"], DataTypes::MYSQL_TYPE_LONGLONG); yield $db->query("DROP DATABASE alt"); }); diff --git a/test/my.cnf b/test/my.cnf index 301328b..d7b3cce 100644 --- a/test/my.cnf +++ b/test/my.cnf @@ -3,6 +3,7 @@ datadir = ./mysql_db pid_file = ./mysql.pid port = 10101 socket = ./mysql.sock +innodb_buffer_pool_load_at_startup = Off [client] socket = ./mysql.sock \ No newline at end of file diff --git a/test/mysql_db/ConnectionTest/db.opt b/test/mysql_db/ConnectionTest/db.opt deleted file mode 100644 index d8429c4..0000000 --- a/test/mysql_db/ConnectionTest/db.opt +++ /dev/null @@ -1,2 +0,0 @@ -default-character-set=latin1 -default-collation=latin1_swedish_ci diff --git a/test/mysql_db/ConnectionTest/main.frm b/test/mysql_db/ConnectionTest/main.frm deleted file mode 100644 index 0aff840..0000000 Binary files a/test/mysql_db/ConnectionTest/main.frm and /dev/null differ diff --git a/test/mysql_db/ConnectionTest/main.ibd b/test/mysql_db/ConnectionTest/main.ibd deleted file mode 100644 index f3903b4..0000000 Binary files a/test/mysql_db/ConnectionTest/main.ibd and /dev/null differ diff --git a/test/mysql_db/mysql/columns_priv.MYD b/test/mysql_db/mysql/columns_priv.MYD deleted file mode 100644 index e69de29..0000000 diff --git a/test/mysql_db/mysql/columns_priv.MYI b/test/mysql_db/mysql/columns_priv.MYI deleted file mode 100644 index 732e23f..0000000 Binary files a/test/mysql_db/mysql/columns_priv.MYI and /dev/null differ diff --git a/test/mysql_db/mysql/columns_priv.frm b/test/mysql_db/mysql/columns_priv.frm deleted file mode 100644 index 1838955..0000000 Binary files a/test/mysql_db/mysql/columns_priv.frm and /dev/null differ diff --git a/test/mysql_db/mysql/db.MYD b/test/mysql_db/mysql/db.MYD deleted file mode 100644 index 75caf71..0000000 --- a/test/mysql_db/mysql/db.MYD +++ /dev/null @@ -1 +0,0 @@ -’% test ’% test\_%  \ No newline at end of file diff --git a/test/mysql_db/mysql/db.MYI b/test/mysql_db/mysql/db.MYI deleted file mode 100644 index 568741b..0000000 Binary files a/test/mysql_db/mysql/db.MYI and /dev/null differ diff --git a/test/mysql_db/mysql/db.frm b/test/mysql_db/mysql/db.frm deleted file mode 100644 index 60fe84e..0000000 Binary files a/test/mysql_db/mysql/db.frm and /dev/null differ diff --git a/test/mysql_db/mysql/event.MYD b/test/mysql_db/mysql/event.MYD deleted file mode 100644 index e69de29..0000000 diff --git a/test/mysql_db/mysql/event.MYI b/test/mysql_db/mysql/event.MYI deleted file mode 100644 index 77b1592..0000000 Binary files a/test/mysql_db/mysql/event.MYI and /dev/null differ diff --git a/test/mysql_db/mysql/event.frm b/test/mysql_db/mysql/event.frm deleted file mode 100644 index 5cb5bf5..0000000 Binary files a/test/mysql_db/mysql/event.frm and /dev/null differ diff --git a/test/mysql_db/mysql/func.MYD b/test/mysql_db/mysql/func.MYD deleted file mode 100644 index e69de29..0000000 diff --git a/test/mysql_db/mysql/func.MYI b/test/mysql_db/mysql/func.MYI deleted file mode 100644 index b99dc0e..0000000 Binary files a/test/mysql_db/mysql/func.MYI and /dev/null differ diff --git a/test/mysql_db/mysql/func.frm b/test/mysql_db/mysql/func.frm deleted file mode 100644 index 9c6eab1..0000000 Binary files a/test/mysql_db/mysql/func.frm and /dev/null differ diff --git a/test/mysql_db/mysql/general_log.CSM b/test/mysql_db/mysql/general_log.CSM deleted file mode 100644 index 8d08b8d..0000000 Binary files a/test/mysql_db/mysql/general_log.CSM and /dev/null differ diff --git a/test/mysql_db/mysql/general_log.CSV b/test/mysql_db/mysql/general_log.CSV deleted file mode 100644 index e69de29..0000000 diff --git a/test/mysql_db/mysql/general_log.frm b/test/mysql_db/mysql/general_log.frm deleted file mode 100644 index 6a002cc..0000000 Binary files a/test/mysql_db/mysql/general_log.frm and /dev/null differ diff --git a/test/mysql_db/mysql/help_category.MYD b/test/mysql_db/mysql/help_category.MYD deleted file mode 100644 index 9af2446..0000000 Binary files a/test/mysql_db/mysql/help_category.MYD and /dev/null differ diff --git a/test/mysql_db/mysql/help_category.MYI b/test/mysql_db/mysql/help_category.MYI deleted file mode 100644 index 58c8557..0000000 Binary files a/test/mysql_db/mysql/help_category.MYI and /dev/null differ diff --git a/test/mysql_db/mysql/help_category.frm b/test/mysql_db/mysql/help_category.frm deleted file mode 100644 index bf16102..0000000 Binary files a/test/mysql_db/mysql/help_category.frm and /dev/null differ diff --git a/test/mysql_db/mysql/help_keyword.MYD b/test/mysql_db/mysql/help_keyword.MYD deleted file mode 100644 index 324dde4..0000000 Binary files a/test/mysql_db/mysql/help_keyword.MYD and /dev/null differ diff --git a/test/mysql_db/mysql/help_keyword.MYI b/test/mysql_db/mysql/help_keyword.MYI deleted file mode 100644 index 99f2402..0000000 Binary files a/test/mysql_db/mysql/help_keyword.MYI and /dev/null differ diff --git a/test/mysql_db/mysql/help_keyword.frm b/test/mysql_db/mysql/help_keyword.frm deleted file mode 100644 index 94f3b27..0000000 Binary files a/test/mysql_db/mysql/help_keyword.frm and /dev/null differ diff --git a/test/mysql_db/mysql/help_relation.MYD b/test/mysql_db/mysql/help_relation.MYD deleted file mode 100644 index 5a09376..0000000 Binary files a/test/mysql_db/mysql/help_relation.MYD and /dev/null differ diff --git a/test/mysql_db/mysql/help_relation.MYI b/test/mysql_db/mysql/help_relation.MYI deleted file mode 100644 index 6fd9978..0000000 Binary files a/test/mysql_db/mysql/help_relation.MYI and /dev/null differ diff --git a/test/mysql_db/mysql/help_relation.frm b/test/mysql_db/mysql/help_relation.frm deleted file mode 100644 index 820870a..0000000 Binary files a/test/mysql_db/mysql/help_relation.frm and /dev/null differ diff --git a/test/mysql_db/mysql/help_topic.MYD b/test/mysql_db/mysql/help_topic.MYD deleted file mode 100644 index b95f795..0000000 Binary files a/test/mysql_db/mysql/help_topic.MYD and /dev/null differ diff --git a/test/mysql_db/mysql/help_topic.MYI b/test/mysql_db/mysql/help_topic.MYI deleted file mode 100644 index 0811039..0000000 Binary files a/test/mysql_db/mysql/help_topic.MYI and /dev/null differ diff --git a/test/mysql_db/mysql/help_topic.frm b/test/mysql_db/mysql/help_topic.frm deleted file mode 100644 index e8c9d7c..0000000 Binary files a/test/mysql_db/mysql/help_topic.frm and /dev/null differ diff --git a/test/mysql_db/mysql/innodb_index_stats.frm b/test/mysql_db/mysql/innodb_index_stats.frm deleted file mode 100644 index 1383691..0000000 Binary files a/test/mysql_db/mysql/innodb_index_stats.frm and /dev/null differ diff --git a/test/mysql_db/mysql/innodb_index_stats.ibd b/test/mysql_db/mysql/innodb_index_stats.ibd deleted file mode 100644 index 4e6fbdb..0000000 Binary files a/test/mysql_db/mysql/innodb_index_stats.ibd and /dev/null differ diff --git a/test/mysql_db/mysql/innodb_table_stats.frm b/test/mysql_db/mysql/innodb_table_stats.frm deleted file mode 100644 index 639898b..0000000 Binary files a/test/mysql_db/mysql/innodb_table_stats.frm and /dev/null differ diff --git a/test/mysql_db/mysql/innodb_table_stats.ibd b/test/mysql_db/mysql/innodb_table_stats.ibd deleted file mode 100644 index d1ed5a5..0000000 Binary files a/test/mysql_db/mysql/innodb_table_stats.ibd and /dev/null differ diff --git a/test/mysql_db/mysql/ndb_binlog_index.MYD b/test/mysql_db/mysql/ndb_binlog_index.MYD deleted file mode 100644 index e69de29..0000000 diff --git a/test/mysql_db/mysql/ndb_binlog_index.MYI b/test/mysql_db/mysql/ndb_binlog_index.MYI deleted file mode 100644 index f05bb90..0000000 Binary files a/test/mysql_db/mysql/ndb_binlog_index.MYI and /dev/null differ diff --git a/test/mysql_db/mysql/ndb_binlog_index.frm b/test/mysql_db/mysql/ndb_binlog_index.frm deleted file mode 100644 index 2dce1eb..0000000 Binary files a/test/mysql_db/mysql/ndb_binlog_index.frm and /dev/null differ diff --git a/test/mysql_db/mysql/plugin.MYD b/test/mysql_db/mysql/plugin.MYD deleted file mode 100644 index e69de29..0000000 diff --git a/test/mysql_db/mysql/plugin.MYI b/test/mysql_db/mysql/plugin.MYI deleted file mode 100644 index 2611026..0000000 Binary files a/test/mysql_db/mysql/plugin.MYI and /dev/null differ diff --git a/test/mysql_db/mysql/plugin.frm b/test/mysql_db/mysql/plugin.frm deleted file mode 100644 index 740e66e..0000000 Binary files a/test/mysql_db/mysql/plugin.frm and /dev/null differ diff --git a/test/mysql_db/mysql/proc.MYD b/test/mysql_db/mysql/proc.MYD deleted file mode 100644 index e69de29..0000000 diff --git a/test/mysql_db/mysql/proc.MYI b/test/mysql_db/mysql/proc.MYI deleted file mode 100644 index 61ef10c..0000000 Binary files a/test/mysql_db/mysql/proc.MYI and /dev/null differ diff --git a/test/mysql_db/mysql/proc.frm b/test/mysql_db/mysql/proc.frm deleted file mode 100644 index 003437e..0000000 Binary files a/test/mysql_db/mysql/proc.frm and /dev/null differ diff --git a/test/mysql_db/mysql/procs_priv.MYD b/test/mysql_db/mysql/procs_priv.MYD deleted file mode 100644 index e69de29..0000000 diff --git a/test/mysql_db/mysql/procs_priv.MYI b/test/mysql_db/mysql/procs_priv.MYI deleted file mode 100644 index 8c50d56..0000000 Binary files a/test/mysql_db/mysql/procs_priv.MYI and /dev/null differ diff --git a/test/mysql_db/mysql/procs_priv.frm b/test/mysql_db/mysql/procs_priv.frm deleted file mode 100644 index 7ce1cf1..0000000 Binary files a/test/mysql_db/mysql/procs_priv.frm and /dev/null differ diff --git a/test/mysql_db/mysql/proxies_priv.MYD b/test/mysql_db/mysql/proxies_priv.MYD deleted file mode 100644 index 0e900e7..0000000 --- a/test/mysql_db/mysql/proxies_priv.MYD +++ /dev/null @@ -1 +0,0 @@ -’localhost root  UO÷K’bobs-macbook-pro-2.local root  UO÷K \ No newline at end of file diff --git a/test/mysql_db/mysql/proxies_priv.MYI b/test/mysql_db/mysql/proxies_priv.MYI deleted file mode 100644 index 31f51f2..0000000 Binary files a/test/mysql_db/mysql/proxies_priv.MYI and /dev/null differ diff --git a/test/mysql_db/mysql/proxies_priv.frm b/test/mysql_db/mysql/proxies_priv.frm deleted file mode 100644 index 6d13f24..0000000 Binary files a/test/mysql_db/mysql/proxies_priv.frm and /dev/null differ diff --git a/test/mysql_db/mysql/servers.MYD b/test/mysql_db/mysql/servers.MYD deleted file mode 100644 index e69de29..0000000 diff --git a/test/mysql_db/mysql/servers.MYI b/test/mysql_db/mysql/servers.MYI deleted file mode 100644 index 2b61948..0000000 Binary files a/test/mysql_db/mysql/servers.MYI and /dev/null differ diff --git a/test/mysql_db/mysql/servers.frm b/test/mysql_db/mysql/servers.frm deleted file mode 100644 index 0192d74..0000000 Binary files a/test/mysql_db/mysql/servers.frm and /dev/null differ diff --git a/test/mysql_db/mysql/slave_master_info.frm b/test/mysql_db/mysql/slave_master_info.frm deleted file mode 100644 index 120e4a7..0000000 Binary files a/test/mysql_db/mysql/slave_master_info.frm and /dev/null differ diff --git a/test/mysql_db/mysql/slave_master_info.ibd b/test/mysql_db/mysql/slave_master_info.ibd deleted file mode 100644 index 42889dc..0000000 Binary files a/test/mysql_db/mysql/slave_master_info.ibd and /dev/null differ diff --git a/test/mysql_db/mysql/slave_relay_log_info.frm b/test/mysql_db/mysql/slave_relay_log_info.frm deleted file mode 100644 index a2f8bf1..0000000 Binary files a/test/mysql_db/mysql/slave_relay_log_info.frm and /dev/null differ diff --git a/test/mysql_db/mysql/slave_relay_log_info.ibd b/test/mysql_db/mysql/slave_relay_log_info.ibd deleted file mode 100644 index fa0a2d7..0000000 Binary files a/test/mysql_db/mysql/slave_relay_log_info.ibd and /dev/null differ diff --git a/test/mysql_db/mysql/slave_worker_info.frm b/test/mysql_db/mysql/slave_worker_info.frm deleted file mode 100644 index 0256cae..0000000 Binary files a/test/mysql_db/mysql/slave_worker_info.frm and /dev/null differ diff --git a/test/mysql_db/mysql/slave_worker_info.ibd b/test/mysql_db/mysql/slave_worker_info.ibd deleted file mode 100644 index 984e9e5..0000000 Binary files a/test/mysql_db/mysql/slave_worker_info.ibd and /dev/null differ diff --git a/test/mysql_db/mysql/slow_log.CSM b/test/mysql_db/mysql/slow_log.CSM deleted file mode 100644 index 8d08b8d..0000000 Binary files a/test/mysql_db/mysql/slow_log.CSM and /dev/null differ diff --git a/test/mysql_db/mysql/slow_log.CSV b/test/mysql_db/mysql/slow_log.CSV deleted file mode 100644 index e69de29..0000000 diff --git a/test/mysql_db/mysql/slow_log.frm b/test/mysql_db/mysql/slow_log.frm deleted file mode 100644 index acae93e..0000000 Binary files a/test/mysql_db/mysql/slow_log.frm and /dev/null differ diff --git a/test/mysql_db/mysql/tables_priv.MYD b/test/mysql_db/mysql/tables_priv.MYD deleted file mode 100644 index e69de29..0000000 diff --git a/test/mysql_db/mysql/tables_priv.MYI b/test/mysql_db/mysql/tables_priv.MYI deleted file mode 100644 index 6638f02..0000000 Binary files a/test/mysql_db/mysql/tables_priv.MYI and /dev/null differ diff --git a/test/mysql_db/mysql/tables_priv.frm b/test/mysql_db/mysql/tables_priv.frm deleted file mode 100644 index ab8f447..0000000 Binary files a/test/mysql_db/mysql/tables_priv.frm and /dev/null differ diff --git a/test/mysql_db/mysql/time_zone.MYD b/test/mysql_db/mysql/time_zone.MYD deleted file mode 100644 index e69de29..0000000 diff --git a/test/mysql_db/mysql/time_zone.MYI b/test/mysql_db/mysql/time_zone.MYI deleted file mode 100644 index e334d01..0000000 Binary files a/test/mysql_db/mysql/time_zone.MYI and /dev/null differ diff --git a/test/mysql_db/mysql/time_zone.frm b/test/mysql_db/mysql/time_zone.frm deleted file mode 100644 index 6ea46ad..0000000 Binary files a/test/mysql_db/mysql/time_zone.frm and /dev/null differ diff --git a/test/mysql_db/mysql/time_zone_leap_second.MYD b/test/mysql_db/mysql/time_zone_leap_second.MYD deleted file mode 100644 index e69de29..0000000 diff --git a/test/mysql_db/mysql/time_zone_leap_second.MYI b/test/mysql_db/mysql/time_zone_leap_second.MYI deleted file mode 100644 index 41f9273..0000000 Binary files a/test/mysql_db/mysql/time_zone_leap_second.MYI and /dev/null differ diff --git a/test/mysql_db/mysql/time_zone_leap_second.frm b/test/mysql_db/mysql/time_zone_leap_second.frm deleted file mode 100644 index 400025d..0000000 Binary files a/test/mysql_db/mysql/time_zone_leap_second.frm and /dev/null differ diff --git a/test/mysql_db/mysql/time_zone_name.MYD b/test/mysql_db/mysql/time_zone_name.MYD deleted file mode 100644 index e69de29..0000000 diff --git a/test/mysql_db/mysql/time_zone_name.MYI b/test/mysql_db/mysql/time_zone_name.MYI deleted file mode 100644 index 91eb49a..0000000 Binary files a/test/mysql_db/mysql/time_zone_name.MYI and /dev/null differ diff --git a/test/mysql_db/mysql/time_zone_name.frm b/test/mysql_db/mysql/time_zone_name.frm deleted file mode 100644 index 1912deb..0000000 Binary files a/test/mysql_db/mysql/time_zone_name.frm and /dev/null differ diff --git a/test/mysql_db/mysql/time_zone_transition.MYD b/test/mysql_db/mysql/time_zone_transition.MYD deleted file mode 100644 index e69de29..0000000 diff --git a/test/mysql_db/mysql/time_zone_transition.MYI b/test/mysql_db/mysql/time_zone_transition.MYI deleted file mode 100644 index 9abd16e..0000000 Binary files a/test/mysql_db/mysql/time_zone_transition.MYI and /dev/null differ diff --git a/test/mysql_db/mysql/time_zone_transition.frm b/test/mysql_db/mysql/time_zone_transition.frm deleted file mode 100644 index 4bee055..0000000 Binary files a/test/mysql_db/mysql/time_zone_transition.frm and /dev/null differ diff --git a/test/mysql_db/mysql/time_zone_transition_type.MYD b/test/mysql_db/mysql/time_zone_transition_type.MYD deleted file mode 100644 index e69de29..0000000 diff --git a/test/mysql_db/mysql/time_zone_transition_type.MYI b/test/mysql_db/mysql/time_zone_transition_type.MYI deleted file mode 100644 index 4c968ee..0000000 Binary files a/test/mysql_db/mysql/time_zone_transition_type.MYI and /dev/null differ diff --git a/test/mysql_db/mysql/time_zone_transition_type.frm b/test/mysql_db/mysql/time_zone_transition_type.frm deleted file mode 100644 index 0593a51..0000000 Binary files a/test/mysql_db/mysql/time_zone_transition_type.frm and /dev/null differ diff --git a/test/mysql_db/mysql/user.MYD b/test/mysql_db/mysql/user.MYD deleted file mode 100644 index 4de09c7..0000000 Binary files a/test/mysql_db/mysql/user.MYD and /dev/null differ diff --git a/test/mysql_db/mysql/user.MYI b/test/mysql_db/mysql/user.MYI deleted file mode 100644 index e4d2f2a..0000000 Binary files a/test/mysql_db/mysql/user.MYI and /dev/null differ diff --git a/test/mysql_db/mysql/user.frm b/test/mysql_db/mysql/user.frm deleted file mode 100644 index 9429a54..0000000 Binary files a/test/mysql_db/mysql/user.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/accounts.frm b/test/mysql_db/performance_schema/accounts.frm deleted file mode 100644 index e461446..0000000 Binary files a/test/mysql_db/performance_schema/accounts.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/cond_instances.frm b/test/mysql_db/performance_schema/cond_instances.frm deleted file mode 100644 index 3e0dddf..0000000 Binary files a/test/mysql_db/performance_schema/cond_instances.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/db.opt b/test/mysql_db/performance_schema/db.opt deleted file mode 100644 index 4ed6015..0000000 --- a/test/mysql_db/performance_schema/db.opt +++ /dev/null @@ -1,2 +0,0 @@ -default-character-set=utf8 -default-collation=utf8_general_ci diff --git a/test/mysql_db/performance_schema/events_stages_current.frm b/test/mysql_db/performance_schema/events_stages_current.frm deleted file mode 100644 index a5e548a..0000000 Binary files a/test/mysql_db/performance_schema/events_stages_current.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/events_stages_history.frm b/test/mysql_db/performance_schema/events_stages_history.frm deleted file mode 100644 index a5e548a..0000000 Binary files a/test/mysql_db/performance_schema/events_stages_history.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/events_stages_history_long.frm b/test/mysql_db/performance_schema/events_stages_history_long.frm deleted file mode 100644 index a5e548a..0000000 Binary files a/test/mysql_db/performance_schema/events_stages_history_long.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/events_stages_summary_by_account_by_event_name.frm b/test/mysql_db/performance_schema/events_stages_summary_by_account_by_event_name.frm deleted file mode 100644 index 77d4412..0000000 Binary files a/test/mysql_db/performance_schema/events_stages_summary_by_account_by_event_name.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/events_stages_summary_by_host_by_event_name.frm b/test/mysql_db/performance_schema/events_stages_summary_by_host_by_event_name.frm deleted file mode 100644 index 88c91f8..0000000 Binary files a/test/mysql_db/performance_schema/events_stages_summary_by_host_by_event_name.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/events_stages_summary_by_thread_by_event_name.frm b/test/mysql_db/performance_schema/events_stages_summary_by_thread_by_event_name.frm deleted file mode 100644 index 5cb4872..0000000 Binary files a/test/mysql_db/performance_schema/events_stages_summary_by_thread_by_event_name.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/events_stages_summary_by_user_by_event_name.frm b/test/mysql_db/performance_schema/events_stages_summary_by_user_by_event_name.frm deleted file mode 100644 index 54b637e..0000000 Binary files a/test/mysql_db/performance_schema/events_stages_summary_by_user_by_event_name.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/events_stages_summary_global_by_event_name.frm b/test/mysql_db/performance_schema/events_stages_summary_global_by_event_name.frm deleted file mode 100644 index e7a914a..0000000 Binary files a/test/mysql_db/performance_schema/events_stages_summary_global_by_event_name.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/events_statements_current.frm b/test/mysql_db/performance_schema/events_statements_current.frm deleted file mode 100644 index c6e5725..0000000 Binary files a/test/mysql_db/performance_schema/events_statements_current.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/events_statements_history.frm b/test/mysql_db/performance_schema/events_statements_history.frm deleted file mode 100644 index c6e5725..0000000 Binary files a/test/mysql_db/performance_schema/events_statements_history.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/events_statements_history_long.frm b/test/mysql_db/performance_schema/events_statements_history_long.frm deleted file mode 100644 index c6e5725..0000000 Binary files a/test/mysql_db/performance_schema/events_statements_history_long.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/events_statements_summary_by_account_by_event_name.frm b/test/mysql_db/performance_schema/events_statements_summary_by_account_by_event_name.frm deleted file mode 100644 index 75b4539..0000000 Binary files a/test/mysql_db/performance_schema/events_statements_summary_by_account_by_event_name.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/events_statements_summary_by_digest.frm b/test/mysql_db/performance_schema/events_statements_summary_by_digest.frm deleted file mode 100644 index e30ff06..0000000 Binary files a/test/mysql_db/performance_schema/events_statements_summary_by_digest.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/events_statements_summary_by_host_by_event_name.frm b/test/mysql_db/performance_schema/events_statements_summary_by_host_by_event_name.frm deleted file mode 100644 index 9df8882..0000000 Binary files a/test/mysql_db/performance_schema/events_statements_summary_by_host_by_event_name.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/events_statements_summary_by_thread_by_event_name.frm b/test/mysql_db/performance_schema/events_statements_summary_by_thread_by_event_name.frm deleted file mode 100644 index edd58e6..0000000 Binary files a/test/mysql_db/performance_schema/events_statements_summary_by_thread_by_event_name.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/events_statements_summary_by_user_by_event_name.frm b/test/mysql_db/performance_schema/events_statements_summary_by_user_by_event_name.frm deleted file mode 100644 index df86505..0000000 Binary files a/test/mysql_db/performance_schema/events_statements_summary_by_user_by_event_name.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/events_statements_summary_global_by_event_name.frm b/test/mysql_db/performance_schema/events_statements_summary_global_by_event_name.frm deleted file mode 100644 index 9a31772..0000000 Binary files a/test/mysql_db/performance_schema/events_statements_summary_global_by_event_name.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/events_waits_current.frm b/test/mysql_db/performance_schema/events_waits_current.frm deleted file mode 100644 index d677342..0000000 Binary files a/test/mysql_db/performance_schema/events_waits_current.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/events_waits_history.frm b/test/mysql_db/performance_schema/events_waits_history.frm deleted file mode 100644 index d677342..0000000 Binary files a/test/mysql_db/performance_schema/events_waits_history.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/events_waits_history_long.frm b/test/mysql_db/performance_schema/events_waits_history_long.frm deleted file mode 100644 index d677342..0000000 Binary files a/test/mysql_db/performance_schema/events_waits_history_long.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/events_waits_summary_by_account_by_event_name.frm b/test/mysql_db/performance_schema/events_waits_summary_by_account_by_event_name.frm deleted file mode 100644 index 77d4412..0000000 Binary files a/test/mysql_db/performance_schema/events_waits_summary_by_account_by_event_name.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/events_waits_summary_by_host_by_event_name.frm b/test/mysql_db/performance_schema/events_waits_summary_by_host_by_event_name.frm deleted file mode 100644 index 88c91f8..0000000 Binary files a/test/mysql_db/performance_schema/events_waits_summary_by_host_by_event_name.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/events_waits_summary_by_instance.frm b/test/mysql_db/performance_schema/events_waits_summary_by_instance.frm deleted file mode 100644 index 1cc74df..0000000 Binary files a/test/mysql_db/performance_schema/events_waits_summary_by_instance.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/events_waits_summary_by_thread_by_event_name.frm b/test/mysql_db/performance_schema/events_waits_summary_by_thread_by_event_name.frm deleted file mode 100644 index 5cb4872..0000000 Binary files a/test/mysql_db/performance_schema/events_waits_summary_by_thread_by_event_name.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/events_waits_summary_by_user_by_event_name.frm b/test/mysql_db/performance_schema/events_waits_summary_by_user_by_event_name.frm deleted file mode 100644 index 54b637e..0000000 Binary files a/test/mysql_db/performance_schema/events_waits_summary_by_user_by_event_name.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/events_waits_summary_global_by_event_name.frm b/test/mysql_db/performance_schema/events_waits_summary_global_by_event_name.frm deleted file mode 100644 index e7a914a..0000000 Binary files a/test/mysql_db/performance_schema/events_waits_summary_global_by_event_name.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/file_instances.frm b/test/mysql_db/performance_schema/file_instances.frm deleted file mode 100644 index de3de5d..0000000 Binary files a/test/mysql_db/performance_schema/file_instances.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/file_summary_by_event_name.frm b/test/mysql_db/performance_schema/file_summary_by_event_name.frm deleted file mode 100644 index edc4324..0000000 Binary files a/test/mysql_db/performance_schema/file_summary_by_event_name.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/file_summary_by_instance.frm b/test/mysql_db/performance_schema/file_summary_by_instance.frm deleted file mode 100644 index 4d77cea..0000000 Binary files a/test/mysql_db/performance_schema/file_summary_by_instance.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/host_cache.frm b/test/mysql_db/performance_schema/host_cache.frm deleted file mode 100644 index ad22698..0000000 Binary files a/test/mysql_db/performance_schema/host_cache.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/hosts.frm b/test/mysql_db/performance_schema/hosts.frm deleted file mode 100644 index 7a8d9b7..0000000 Binary files a/test/mysql_db/performance_schema/hosts.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/mutex_instances.frm b/test/mysql_db/performance_schema/mutex_instances.frm deleted file mode 100644 index 6893d75..0000000 Binary files a/test/mysql_db/performance_schema/mutex_instances.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/objects_summary_global_by_type.frm b/test/mysql_db/performance_schema/objects_summary_global_by_type.frm deleted file mode 100644 index a6bc193..0000000 Binary files a/test/mysql_db/performance_schema/objects_summary_global_by_type.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/performance_timers.frm b/test/mysql_db/performance_schema/performance_timers.frm deleted file mode 100644 index 58ba664..0000000 Binary files a/test/mysql_db/performance_schema/performance_timers.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/rwlock_instances.frm b/test/mysql_db/performance_schema/rwlock_instances.frm deleted file mode 100644 index 47233af..0000000 Binary files a/test/mysql_db/performance_schema/rwlock_instances.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/session_account_connect_attrs.frm b/test/mysql_db/performance_schema/session_account_connect_attrs.frm deleted file mode 100644 index 6b52e3c..0000000 Binary files a/test/mysql_db/performance_schema/session_account_connect_attrs.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/session_connect_attrs.frm b/test/mysql_db/performance_schema/session_connect_attrs.frm deleted file mode 100644 index 91314b7..0000000 Binary files a/test/mysql_db/performance_schema/session_connect_attrs.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/setup_actors.frm b/test/mysql_db/performance_schema/setup_actors.frm deleted file mode 100644 index 7e6dbe3..0000000 Binary files a/test/mysql_db/performance_schema/setup_actors.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/setup_consumers.frm b/test/mysql_db/performance_schema/setup_consumers.frm deleted file mode 100644 index 43d0bc1..0000000 Binary files a/test/mysql_db/performance_schema/setup_consumers.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/setup_instruments.frm b/test/mysql_db/performance_schema/setup_instruments.frm deleted file mode 100644 index 3049659..0000000 Binary files a/test/mysql_db/performance_schema/setup_instruments.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/setup_objects.frm b/test/mysql_db/performance_schema/setup_objects.frm deleted file mode 100644 index 653ca34..0000000 Binary files a/test/mysql_db/performance_schema/setup_objects.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/setup_timers.frm b/test/mysql_db/performance_schema/setup_timers.frm deleted file mode 100644 index a5e2056..0000000 Binary files a/test/mysql_db/performance_schema/setup_timers.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/socket_instances.frm b/test/mysql_db/performance_schema/socket_instances.frm deleted file mode 100644 index 418b770..0000000 Binary files a/test/mysql_db/performance_schema/socket_instances.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/socket_summary_by_event_name.frm b/test/mysql_db/performance_schema/socket_summary_by_event_name.frm deleted file mode 100644 index abaaf5f..0000000 Binary files a/test/mysql_db/performance_schema/socket_summary_by_event_name.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/socket_summary_by_instance.frm b/test/mysql_db/performance_schema/socket_summary_by_instance.frm deleted file mode 100644 index cc2b950..0000000 Binary files a/test/mysql_db/performance_schema/socket_summary_by_instance.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/table_io_waits_summary_by_index_usage.frm b/test/mysql_db/performance_schema/table_io_waits_summary_by_index_usage.frm deleted file mode 100644 index a7c12d5..0000000 Binary files a/test/mysql_db/performance_schema/table_io_waits_summary_by_index_usage.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/table_io_waits_summary_by_table.frm b/test/mysql_db/performance_schema/table_io_waits_summary_by_table.frm deleted file mode 100644 index 85e75df..0000000 Binary files a/test/mysql_db/performance_schema/table_io_waits_summary_by_table.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/table_lock_waits_summary_by_table.frm b/test/mysql_db/performance_schema/table_lock_waits_summary_by_table.frm deleted file mode 100644 index 7323315..0000000 Binary files a/test/mysql_db/performance_schema/table_lock_waits_summary_by_table.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/threads.frm b/test/mysql_db/performance_schema/threads.frm deleted file mode 100644 index 40aeedf..0000000 Binary files a/test/mysql_db/performance_schema/threads.frm and /dev/null differ diff --git a/test/mysql_db/performance_schema/users.frm b/test/mysql_db/performance_schema/users.frm deleted file mode 100644 index ccbf822..0000000 Binary files a/test/mysql_db/performance_schema/users.frm and /dev/null differ diff --git a/test/phpunit_bootstrap.php b/test/phpunit_bootstrap.php index 9673bc9..e398aad 100644 --- a/test/phpunit_bootstrap.php +++ b/test/phpunit_bootstrap.php @@ -10,20 +10,63 @@ /* cleanup in case it wasn't terminated properly... */ $pidfile = __DIR__."/mysql.pid"; if (file_exists($pidfile)) { - shell_exec("kill `cat '$pidfile'`"); + if (stripos(PHP_OS, "win") === 0) { + shell_exec("Taskkill /PID ".file_get_contents($pidfile)." /F"); + } else { + shell_exec("kill -9 `cat '$pidfile'`"); + } sleep(1); } -$proc = proc_open("mysqld --defaults-file=my.cnf", [2 => ["pipe", "w"]], $pipes, __DIR__); +if (file_exists(__DIR__."/mysql_db")) { + $rm_all = function ($dir) use (&$rm_all) { + $files = glob("$dir/*"); + if (is_array($files)) { + foreach ($files as $file) { + if (is_dir($file)) { + $rm_all($file); + rmdir($file); + } else { + unlink($file); + } + } + } + }; + $rm_all(__DIR__ . "/mysql_db"); +} else { + @mkdir(__DIR__ . "/mysql_db"); +} + +$proc = proc_open("mysqld --defaults-file=my.cnf --initialize-insecure", [2 => ["pipe", "w"]], $pipes, __DIR__); $stderr = $pipes[2]; -$buf = ""; do { if (!($row = fgets($stderr)) || preg_match("# \[ERROR\] #", $row)) { print "\nERROR: Aborting, couldn't start mysql successfully\n$buf$row"; exit(127); } $buf .= $row; -} while (!preg_match("(^Version: '[0-9.a-zA-Z]+')", $row)); +} while (!preg_match("(root@localhost is created with an empty password)", $row)); +sleep(3); // :-( +proc_terminate($proc, 9); +@proc_terminate($proc, 9); + +$proc = proc_open("mysqld --defaults-file=my.cnf --user=root", [2 => ["pipe", "w"]], $pipes, __DIR__); register_shutdown_function(function() use ($proc) { - proc_terminate($proc); -}); \ No newline at end of file + proc_terminate($proc, 9); +}); + +$stderr = $pipes[2]; +do { + if (!($row = fgets($stderr)) || preg_match("# \[ERROR\] #", $row)) { + print "\nERROR: Aborting, couldn't start mysql successfully\n$buf$row"; + exit(127); + } + $buf .= $row; +} while (!preg_match("(^Version: '[0-9.a-zA-Z]+')", $row)); + + +$db = new mysqli(DB_HOST, DB_USER, DB_PASS); +$db->query("CREATE DATABASE connectiontest"); +$db->query("CREATE TABLE connectiontest.main SELECT 1 AS a, 2 AS b"); +$db->query("INSERT INTO connectiontest.main VALUES (5, 6)"); +$db->close(); \ No newline at end of file