diff options
-rw-r--r-- | it_dbi.class | 184 |
1 files changed, 119 insertions, 65 deletions
diff --git a/it_dbi.class b/it_dbi.class index a00d236..eb5b10e 100644 --- a/it_dbi.class +++ b/it_dbi.class @@ -212,28 +212,20 @@ function _connect($p = array()) { # Force new link if same server/user was seen before (mysql ignores selected db) if ($GLOBALS['it_dbi']->_connected["{$p['server']}/{$p['user']}"]++) - $this->_link = @mysqli_connect($p['server'], $p['user'], $p['pw']); + list($this->_link, $error) = $this->_connect_db($p); else - $this->_link = @mysqli_connect($p['server'], $p['user'], $p['pw']); + list($this->_link, $error) = $this->_connect_db($p); if (!$this->_link) { # One retry after a short delay - it::log('sqllog', "it_dbi(): retrying DB link (mysqli_connect {$p['server']}, {$p['db']}): " . mysqli_connect_error()); + it::log('sqllog', "it_dbi(): retrying DB link (_connect_db {$p['server']}, {$p['db']}): $error"); sleep(1); - $this->_link = @mysqli_connect($p['server'], $p['user'], $p['pw']); + list($this->_link, $error) = $this->_connect_db($p); } if (!$this->_link) - $this->_fatal("_connect(): can't create DB link (mysqli_connect {$p['user']}@{$p['server']}, {$p['db']}): " . mysqli_connect_error()); - - if (!(@mysqli_select_db($this->_link, $p['db']))) - $this->_fatal("_connect(): can't select database \"{$p['db']}\""); - - # set charset used for this connection - if ($p['charset']) - if (!mysqli_set_charset($this->_link, $p['charset'])) - $this->_fatal("_connect(): can't set charset \"{$p['charset']}\""); + $this->_fatal("_connect(): can't create DB link (_connect_db {$p['user']}@{$p['server']}, {$p['db']}): $error"); # NOTE: This overwrites old state but that is on purpose. New link means we should refetch all info about connection $state['link'] = $this->_link; @@ -453,25 +445,15 @@ function _where($params) return $query; } - /** * Internal: Output class name::error message and terminate execution. */ function _fatal($text, $body = null) { - $text = get_class($this).'::'.$text; - - if ($this->_link && ($errstr = mysqli_error($this->_link))) - $text = "\"$errstr\" in $text [errno " . mysqli_errno($this->_link) . "]"; - - if ($this->_link && ($res = @mysqli_fetch_row(mysqli_query($this->_link, 'select database()')))) # dont create extra errs - $text .= ", DB: " . $res[0]; - - it::fatal(['title' => $text . ", Server: " . $this->_p['server'], 'body' => $body]); + it::fatal(['title' => $this->_error($text) . ", DB: " . $this->_p['db'] . ", Server: " . $this->_p['server'], 'body' => $body]); /* NOT REACHED */ } - /** * Post-process data after reading a record. * This is a stub-function that can be overloaded. @@ -521,10 +503,7 @@ static function _write_preprocess($data) */ function tables($p = array()) { - for ($qr = $this->query('SHOW TABLES', $p); $row = mysqli_fetch_row($qr);) - $result[] = $row[0]; - - return (array)$result; + return $this->_tables($p); } @@ -566,21 +545,11 @@ function query($query, $p = array()) debug("{$p['user']}@{$p['server']}:{$p['db']}" . '.' . get_class($this) . "::query(\"$query\")", 4); - if (!($result = mysqli_query($this->_link, $query, $p['unbuffered'] ? MYSQLI_USE_RESULT : MYSQLI_STORE_RESULT)) && $p['safety']) + if (!($result = $this->_query($query, $p))) { - $errno = mysqli_errno($this->_link); - if (($p['safety'] < 2) && ($errno == 1062)) # Duplicate entry - return false; - - if ($errno == 2006) # mysql server has gone away: retry - { - it::log('sqllog', "it_dbi(): reconnecting mysqli_connect {$p['server']}, {$p['db']}"); - $this->_connect(array('reconnect' => true)); - $result = mysqli_query($this->_link, $query, $p['unbuffered'] ? MYSQLI_USE_RESULT : MYSQLI_STORE_RESULT); - } - - if (!$result) - $this->_fatal("query(\"$query\") failed"); + if ($result === false) + return $result; + $this->_fatal("query(\"$query\") failed"); } else if (it::match('^(CREATE|ALTER|DROP) ', $query, array('utf8' => false))) { @@ -589,19 +558,6 @@ function query($query, $p = array()) it_dbi::_state_purgeshared($dbid); } - $this->_affectedrows = $this->_link->affected_rows; # get_warnings() clobbers this - $this->_insertid = mysqli_insert_id($this->_link); # get_warnings() clobbers this - if (($warning = $this->_link->get_warnings())) - { - do { - if (!it::match(trim($this->_p['ignored_warnings'] . "|1364|1261|1051|1062", "|"), $warning->errno)) - $messages[] = $warning->message . " [error $warning->errno]"; - } while ($warning->next() && ++$checked < 20); - - if ($messages) - it::error(['title' => "Mysql warning: " . $messages[0], 'body' => "$query\n\n" . join("\n", $messages) . "\n"]); - } - if ($writing && $this->_p['throttle_writes']) usleep(1000000 * (gettimeofday(true) - $start) * $this->_p['throttle_writes']); @@ -690,11 +646,11 @@ function select(/* $query = array|string, ... */) $this->clear(); if ($this->_result = $this->query($sql = "SELECT $what " . $this->_from($query) . " " . $this->_where($query))) { - $result = $this->_p['unbuffered'] ? true : mysqli_num_rows($this->_result); + $result = $this->_p['unbuffered'] ? true : $this->_num_rows($this->_result); if ($calc_found_rows) { - list($count) = mysqli_fetch_row($this->query('SELECT FOUND_ROWS()')); + $count = $this->_fetch_assoc($this->query('SELECT FOUND_ROWS() AS count'))['count']; $this->_found_rows = intval($count); } @@ -717,7 +673,7 @@ function iterate() { if (!$this->_nofetch) { - if ($this->_data = mysqli_fetch_assoc($this->_result)) + if ($this->_data = $this->_fetch_assoc($this->_result)) { if ($localizedfields = $this->_localizedfields) foreach ($localizedfields as $field => $dummy) @@ -922,7 +878,7 @@ function delete_untouched($query = null) function escape_string($str) { $this->_connect(); - return "'" . mysqli_real_escape_string($this->_link, $str) . "'"; + return $this->_escape_string($str); } @@ -939,11 +895,10 @@ function _get_field_info() if (!($this->_fields = $state['fields'][$this->_p['table']])) { debug("it_dbi(): no fields for {$dbid}.{$this->_p['table']}, calculating.", 5); - for ($res = $this->query('SHOW COLUMNS FROM ' . $this->_p['table']); $res && ($field = mysqli_fetch_assoc($res)); ) - { - $this->_fields[$field['Field']] = $field + array('Length' => preg_match('/date|time/', $field['Type']) ? 20 : intval(it::match('\d+', $field['Type']))); + foreach ($this->_get_field_defs() as $name => $field) { + $this->_fields[$name] = $field + array('Length' => preg_match('/date|time/', $field['Type']) ? 20 : intval(it::match('\d+', $field['Type']))); if (preg_match('/^(tiny|small|medium|)int|^float|^double/', $field['Type'])) - $this->_convertfunc[$field['Field']] = it::match('int', $field['Type']) ? "intval" : "floatval"; + $this->_convertfunc[$name] = it::match('int', $field['Type']) ? "intval" : "floatval"; } $this->_fieldnames = "," . join(",", array_keys((array)$this->_fields)) . ","; @@ -1025,8 +980,8 @@ function rewind() $this->select(); # Only rewind if not already at start and results present - if (!$this->_nofetch && mysqli_num_rows($this->_result)) - mysqli_data_seek($this->_result, 0); + if (!$this->_nofetch && $this->_num_rows($this->_result)) + $this->_seek($this->_result, 0); $this->_iteratorkey = 0; $this->iterate(); @@ -1057,4 +1012,103 @@ static function get($id) return isset($id) && ($rec = new static) && $rec->read($id) ? $rec : null; } + +/** + * Start of mysqli specific default implmementation + */ + +function _escape_string($str) +{ + return "'" . mysqli_real_escape_string($this->_link, $str) . "'"; +} + +function _connect_db($p) { + $result = @mysqli_connect($p['server'], $p['user'], $p['pw']); + + if ($result) + { + if (!(@mysqli_select_db($result, $p['db']))) + $this->_fatal("_connect(): can't select database \"{$p['db']}\""); + + # set charset used for this connection + if ($p['charset']) + if (!mysqli_set_charset($result, $p['charset'])) + $this->_fatal("_connect(): can't set charset \"{$p['charset']}\""); + } + + return [$result, mysqli_connect_error()]; +} + +function _get_field_defs() +{ + for ($res = $this->query('SHOW COLUMNS FROM ' . $this->_p['table']); $res && ($field = $this->_fetch_assoc($res)); ) + $result[$field['Field']] = it::filter_keys($field, ['Field', 'Type', 'Key', 'Extra']); + return $result; +} + +function _tables($p) { + for ($qr = $this->query('SHOW TABLES', $p); $row = mysqli_fetch_row($qr);) + $result[] = $row[0]; + + return (array)$result; +} + +function _query($query, $p) +{ + if (!($result = mysqli_query($this->_link, $query, $p['unbuffered'] ? MYSQLI_USE_RESULT : MYSQLI_STORE_RESULT)) && $p['safety']) + { + $errno = mysqli_errno($this->_link); + if (($p['safety'] < 2) && ($errno == 1062)) # Duplicate entry + return false; + + if ($errno == 2006) # mysql server has gone away: retry + { + it::log('sqllog', "it_dbi(): reconnecting mysqli_connect {$p['server']}, {$p['db']}"); + $this->_connect(array('reconnect' => true)); + $result = mysqli_query($this->_link, $query, $p['unbuffered'] ? MYSQLI_USE_RESULT : MYSQLI_STORE_RESULT); + } + } + + $this->_affectedrows = $this->_link->affected_rows; # get_warnings() clobbers this + $this->_insertid = mysqli_insert_id($this->_link); # get_warnings() clobbers this + if (($warning = $this->_link->get_warnings())) + { + do { + if (!it::match(trim($this->_p['ignored_warnings'] . "|1364|1261|1051|1062", "|"), $warning->errno)) + $messages[] = $warning->message . " [error $warning->errno]"; + } while ($warning->next() && ++$checked < 20); + + if ($messages) + it::error(['title' => "Mysql warning: " . $messages[0], 'body' => "$query\n\n" . join("\n", $messages) . "\n"]); + } + + return $result; +} + +function _fetch_assoc($res) +{ + return mysqli_fetch_assoc($res); +} + +function _num_rows($res) +{ + return mysqli_num_rows($res); +} + + +function _seek($res, $offset) +{ + return mysqli_data_seek($res, $offset); +} + +function _error($text) +{ + $text = get_class($this).'::'.$text; + + if ($this->_link && ($errstr = mysqli_error($this->_link))) + $text = "\"$errstr\" in $text [errno " . mysqli_errno($this->_link) . "]"; + + return $text; +} + } /* End class it_dbi */ |