num_rows == 0) { self::$cache[$cacheKey] = false; } else { $primary = false; $unique = false; while ($row = $result->fetch_assoc()) { switch ($row['Key']) { case 'PRI': if ($primary === false) { $primary = $row['Field']; } else { if (is_scalar($primary)) { $primary = array($primary); } $primary[] = $row['Field']; } break; case 'UNI': $unique = $row['Field']; break; default: break; } } if ($primary !== false) { self::$cache[$cacheKey] = $primary; } else if ($unique !== false) { self::$cache[$cacheKey] = $unique; } else { self::$cache[$cacheKey] = false; } } $result->free(); } return self::$cache[$cacheKey]; } /** * * @param array $row * @param string|string[] $indexColumns * @return string|string[] */ public static function getOffsetFromRowAssoc($row, $indexColumns, $lastOffset) { if (is_array($indexColumns)) { $result = array(); foreach ($indexColumns as $col) { $result[$col] = isset($row[$col]) ? $row[$col] : 0; } return $result; } else if (strlen($indexColumns) > 0) { return isset($row[$indexColumns]) ? $row[$indexColumns] : 0; } else { return $lastOffset + 1; } } /** * This function performs a select by structuring the primary key as offset if the table has a primary key. * For optimization issues, no checks are performed on the input query and it is assumed that the select has at least a where value. * If there are no conditions, you still have to perform an always true condition, for example * SELECT * FROM `copy1_postmeta` WHERE 1 * * @param mysqli $dbh // Database connection handle * @param string $query * @param string $table * @param int $offset * @param int $limit // 0 no limit * @param mixed $lastRowOffset // last offset to use on next function call * @return mysqli_result * @throws Exception // exception on query fail */ public static function selectUsingPrimaryKeyAsOffset(\mysqli $dbh, $query, $table, $offset, $limit, &$lastRowOffset = null, $logCallback = null) { $where = ''; $orderby = ''; $offsetStr = ''; $limitStr = $limit > 0 ? ' LIMIT '.$limit : ''; if (($primaryColumn = self::getUniqueIndexColumn($dbh, $table, $logCallback)) == false) { $offsetStr = ' OFFSET '.(is_scalar($offset) ? $offset : 0); } else { if (is_array($primaryColumn)) { // COMPOSITE KEY $orderByCols = array(); foreach ($primaryColumn as $colIndex => $col) { $orderByCols[] = '`'.$col.'` ASC'; } $orderby = ' ORDER BY '.implode(',', $orderByCols); } else { $orderby = ' ORDER BY `'.$primaryColumn.'` ASC'; } $where = self::getOffsetKeyCondition($dbh, $primaryColumn, $offset); } $query .= $where.$orderby.$limitStr.$offsetStr; if (($result = mysqli_query($dbh, $query)) === false) { if (is_callable($logCallback)) { call_user_func($logCallback, $dbh, $result, $query); } throw new Exception('SELECT ERROR: '.mysqli_error($dbh)); } if (is_callable($logCallback)) { call_user_func($logCallback, $dbh, $result, $query); } if ($primaryColumn == false) { $lastRowOffset = $offset + $result->num_rows; } else { if ($result->num_rows == 0) { $lastRowOffset = $offset; } else { $result->data_seek(($result->num_rows - 1)); $row = $result->fetch_assoc(); if (is_array($primaryColumn)) { $lastRowOffset = array(); foreach ($primaryColumn as $col) { $lastRowOffset[$col] = $row[$col]; } } else { $lastRowOffset = $row[$primaryColumn]; } $result->data_seek(0); } } return $result; } /** * Depending on the structure type of the primary key returns the condition to position at the right offset * * @param string|string[] $primaryColumn * @param mixed $offset * @return string */ protected static function getOffsetKeyCondition(\mysqli $dbh, $primaryColumn, $offset) { $condition = ''; if ($offset === 0) { return ''; } // COUPOUND KEY if (is_array($primaryColumn)) { foreach ($primaryColumn as $colIndex => $col) { if (is_array($offset) && isset($offset[$col]) && $offset[$col] > 0) { $condition .= ($colIndex == 0 ? '' : ' OR '); $condition .= ' ('; for ($prevColIndex = 0; $prevColIndex < $colIndex; $prevColIndex++) { $condition .= ' `'.$primaryColumn[$prevColIndex].'` = "'.mysqli_real_escape_string($dbh, $offset[$primaryColumn[$prevColIndex]]).'" AND '; } $condition .= ' `'.$col.'` > "'.mysqli_real_escape_string($dbh, $offset[$col]).'")'; } } } else { $condition = '`'.$primaryColumn.'` > "'.mysqli_real_escape_string($dbh, (is_scalar($offset) ? $offset : 0)).'"'; } return (strlen($condition) ? ' AND ('.$condition.')' : ''); } public static function getDBEngine(\mysqli $dbh) { $result = mysqli_query($dbh, "SHOW VARIABLES LIKE 'version%'"); $rows = @mysqli_fetch_all($result); @mysqli_free_result($result); $version = isset($rows[0][1]) ? $rows[0][1] : false; $versionComment = isset($rows[1][1]) ? $rows[1][1] : false; //Default is mysql if ($version === false && $versionComment === false) { return self::DB_ENGINE_MYSQL; } if (stripos($version, 'maria') !== false || stripos($versionComment, 'maria') !== false) { return self::DB_ENGINE_MARIA; } if (stripos($version, 'percona') !== false || stripos($versionComment, 'percona') !== false) { return self::DB_ENGINE_PERCONA; } return self::DB_ENGINE_MYSQL; } }