// Copyright: Copyright (C) 2001-2008 Pristine Communications // Set the include path if (!defined("INCPATH_SET")) { require_once dirname(__FILE__) . "/incpath.inc.php"; } // Referenced subroutines with high precedence require_once "monica/sqlconst.inc.php"; // Referenced subroutines require_once "monica/errhndl.inc.php"; require_once "monica/getlang.inc.php"; require_once "monica/lninfo.inc.php"; require_once "monica/requri.inc.php"; require_once "monica/sqllogin.inc.php"; // Settings $_MYSQL_CONN = null; // xmysql_connect: Connect to the MySQL database function xmysql_connect($database = null) { global $_MYSQL_CONN; // Connected if (!is_null($_MYSQL_CONN)) { return; } // Obtain the SQL log-in information if (is_null($database)) { if (getenv("MYSQL_DB") !== false) { $database = getenv("MYSQL_DB"); } elseif (defined("PACKAGE")) { $database = PACKAGE; } } if (getenv("MYSQL_HOST") !== false) { $host = getenv("MYSQL_HOST"); } else { $host = null; } // Login with from SQLLOGIN environment variable as a web application // php-cgi does not have STDIN and STDERR even on console if (PHP_SAPI != "cli") { $r = get_sql_login_info(SQL_MYSQL, $database, $host); // Connect it $_MYSQL_CONN = mysql_connect($r["MYSQL_HOST"], $r["MYSQL_USER"], $r["MYSQL_PW"]); if ($_MYSQL_CONN === false) { $_MYSQL_CONN = null; trigger_error("Failed connecting to the MySQL server.\n" . mysql_errno() . " " . mysql_error(), E_USER_ERROR); } // Ask the password from the console } else { $user = null; $subseq = false; set_error_handler("null_error_handler"); $_MYSQL_CONN = mysql_connect(); restore_error_handler(); while ($_MYSQL_CONN === false) { if ($subseq) { fprintf(STDERR, "%s\n", $GLOBALS["php_errormsg"]); sleep(5); } // Obtain the current login user if ( is_null($user) && preg_match("/ denied for user '(.+?)'\@'.+?'/", $GLOBALS["php_errormsg"], $m)) { $user = $m[1]; } $subseq = true; // Disable console echo system("/bin/stty -echo"); fprintf(STDERR, !is_null($user)? "MySQL password for $user: ": "MySQL password: "); $passwd = fgets(STDIN); fprintf(STDERR, "\n"); // Restore console echo status system("/bin/stty echo"); // STDIN is not available if ($passwd === false) { die(THIS_FILE . ": Failed connecting to the PostgreSQL server\n"); } $passwd = trim($passwd); set_error_handler("null_error_handler"); $_MYSQL_CONN = mysql_connect(null, null, $passwd); restore_error_handler(); } } // Select the database $result = mysql_select_db($database); if ($result === false) { trigger_error("Failed mysql_select_db(\"$database\").\n" . mysql_errno() . " " . mysql_error(), E_USER_ERROR); } // Set the character set $charset = defined("SQL_CHARSET")? SQL_CHARSET: "utf8"; $set = "SET NAMES '" . mysql_escape_string($charset) . "';\n"; xmysql_query($set); return; } // xmysql_close: Disconnect from the MySQL database function xmysql_close() { global $_MYSQL_CONN; if (is_null($_MYSQL_CONN)) { return; } $result = mysql_close($_MYSQL_CONN); if ($result !== true) { trigger_error("Failed disconnecting from the MySQL server.\n" . mysql_errno() . " " . mysql_error(), E_USER_ERROR); } $_MYSQL_CONN = null; return; } ///////////////////////// // Concurrency Control: Transactions and Locks ///////////////////////// $_MYSQL_IN_TRANSACTION = false; // mysql_begin: Begin a MySQL transaction function mysql_begin() { if (!$GLOBALS["_MYSQL_IN_TRANSACTION"]) { $begin = "START TRANSACTION;\n"; xmysql_query($begin); $GLOBALS["_MYSQL_IN_TRANSACTION"] = true; } } // mysql_commit: Commit a MySQL transaction function mysql_commit() { if ($GLOBALS["_MYSQL_IN_TRANSACTION"]) { $commit = "COMMIT;\n"; xmysql_query($commit); $GLOBALS["_MYSQL_IN_TRANSACTION"] = false; } } // mysql_rollback: Rollback a MySQL transaction function mysql_rollback() { if ($GLOBALS["_MYSQL_IN_TRANSACTION"]) { $rollback = "ROLLBACK;\n"; // This generate errors under MyIASM //xmysql_query($rollback); mysql_query($rollback); $GLOBALS["_MYSQL_IN_TRANSACTION"] = false; } } // mysql_lock: MySQL table-locking handler // We need this to wrok around the stupid MySQL table locking mechanism // Input: An associative array, where its keys are the tables to lock, // and its values can be one of the following: // LOCK_SH: Request a read lock // LOCK_EX: Request a write lock // LOCK_UN: Unlock the previously obtained lock // Or null to unlock everything. // Return: None. Errors are directed to error handlers function mysql_lock($newlocks = null) { // Keep the current lock table static static $curlocks = array(); // Save the previous locks $lastlocks = $curlocks; // Unlock everything if (is_null($newlocks)) { $curlocks = array(); } else { // Adjust the current lock table foreach (array_keys($newlocks) as $table) { switch ($newlocks[$table]) { case LOCK_SH: case LOCK_EX: $curlocks[$table] = $newlocks[$table]; break; case LOCK_UN: unset($curlocks[$table]); break; default: trigger_error("Bad SQL lock request: \"$newlocks[$table]\" on table \"$table\".", E_USER_ERROR); } } } $lockstr = _mysql_locks2str($curlocks); // Return if nothing is changed if ($lockstr == _mysql_locks2str($lastlocks)) { return; } // Rollback everything if not committed yet // LOCK TABLE/UNLOCK TABLE implicitly COMMIT the previous transaction. // This is bad. if ($GLOBALS["_MYSQL_IN_TRANSACTION"]) { mysql_rollback(); } // Has something to lock if ($lockstr !== "") { $locktable = "LOCK TABLES $lockstr;\n"; // Nothing to lock - release all locks } else { $locktable = "UNLOCK TABLES;\n"; } xmysql_query($locktable); return; } // xmysql_query: Do a MySQL query and report the error function xmysql_query($query) { $result = mysql_query($query); if ($result === false) { trigger_error("Failed mysql_query().\n$query\n" . mysql_errno() . " " . mysql_error(), E_USER_ERROR); } return $result; } // mysql_seek: Move the MySQL result pointer function mysql_seek($result, $offset) { $result = mysql_data_seek($result, $offset); if ($result === false) { trigger_error("Failed mysql_data_seek().\n" . mysql_errno() . " " . mysql_error(), E_USER_ERROR); } return $result; } // xmysql_fetch_assoc: Return a MySQL row as an associative array function xmysql_fetch_assoc($result) { // Fetch the raw data now $row = mysql_fetch_assoc($result); // Return the error if (!is_array($row)) { return $row; } // Adjust the boolean columns foreach (mysql_cols_of_type($result, SQL_TYPE_BOOLEAN, SQL_FETCH_ASSOC) as $col) { $row[$col] = !is_null($row[$col]); } // Adjust the integer columns foreach (mysql_cols_of_type($result, SQL_TYPE_INTEGER, SQL_FETCH_ASSOC) as $col) { if (!is_null($row[$col])) { settype($row[$col], "integer"); } } // Adjust the big integer columns foreach (mysql_cols_of_type($result, SQL_TYPE_BIGINT, SQL_FETCH_ASSOC) as $col) { if ( !is_null($row[$col]) && $row[$col] >= -2147483647 && $row[$col] <= 2147483647) { settype($row[$col], "integer"); } } // Adjust the float columns foreach (mysql_cols_of_type($result, SQL_TYPE_FLOAT, SQL_FETCH_ASSOC) as $col) { if (!is_null($row[$col])) { settype($row[$col], "float"); } } return $row; } // xmysql_fetch_row: Return a MySQL row as an numeric array function xmysql_fetch_row($result) { // Fetch the raw data now $row = mysql_fetch_row($result); // Return the error if (!is_array($row)) { return $row; } // Adjust the boolean columns foreach (mysql_cols_of_type($result, SQL_TYPE_BOOLEAN, SQL_FETCH_ROW) as $col) { $row[$col] = !is_null($row[$col]); } // Adjust the integer columns foreach (mysql_cols_of_type($result, SQL_TYPE_INTEGER, SQL_FETCH_ROW) as $col) { if (!is_null($row[$col])) { settype($row[$col], "integer"); } } // Adjust the big integer columns foreach (mysql_cols_of_type($result, SQL_TYPE_BIGINT, SQL_FETCH_ROW) as $col) { if ( !is_null($row[$col]) && $row[$col] >= -2147483647 && $row[$col] <= 2147483647) { settype($row[$col], "integer"); } } // Adjust the float columns foreach (mysql_cols_of_type($result, SQL_TYPE_FLOAT, SQL_FETCH_ROW) as $col) { if (!is_null($row[$col])) { settype($row[$col], "float"); } } return $row; } // mysql_tables: Obtain a list of available MySQL tables // and report the error function mysql_tables($db = null) { if (is_null($db)) { $select = "SHOW TABLES;\n"; } else { $select = "SHOW TABLES FROM $db;\n"; } $result = xmysql_query($select); $count = mysql_num_rows($result); for ($i = 0, $tables = array(); $i < $count; $i++) { $row = mysql_fetch_row($result); $tables[] = $row[0]; } sort($tables); return $tables; } // mysql_cols: Obtain the column list of a MySQL table function mysql_cols($table) { // Cache the result static $cache = array(); // Return the cache if (array_key_exists($table, $cache)) { return $cache[$table]; } $select = "SHOW COLUMNS FROM $table;\n"; $result = xmysql_query($select); $count = mysql_num_rows($result); for ($i = 0, $cache[$table] = array(); $i < $count; $i++) { $row = mysql_fetch_row($result); $cache[$table][] = $row[0]; } return $cache[$table]; } // mysql_cols_ml: Return a list of multi-lingual columns in a MySQL table function mysql_cols_ml($table) { // Cache the result static $cache = array(); // Return the cache if (array_key_exists($table, $cache)) { return $cache[$table]; } // Get the columns that have language variants $cols = mysql_cols($table); $cache[$table] = array(); $suffix = "_" . getlang(LN_DATABASE); $len = strlen($suffix); for ($i = 0; $i < count($cols); $i++) { // It has a language suffix if (substr($cols[$i], -$len) == $suffix) { $cache[$table][] = substr($cols[$i], 0, -$len); } } return $cache[$table]; } // mysql_cols_nl: Return a list of columns without their multi-lingual // deviants in a MySQL table function mysql_cols_nl($table) { // Cache the result static $cache = array(); // Return the cache if (array_key_exists($table, $cache)) { return $cache[$table]; } // Get the columns that have language variants $cols = mysql_cols($table); $langcols = mysql_cols_ml($table); // Remove those language variants $cache[$table] = array(); for ($i = 0; $i < count($cols); $i++) { $pos = strrpos($cols[$i], "_"); // No suffix if ($pos === false) { $cache[$table][] = $cols[$i]; // Check the prefix } else { $prefix = substr($cols[$i], 0, $pos); // The prefix is one of the language columns if (in_array($prefix, $langcols)) { // Not counted yet if (!in_array($prefix, $cache[$table])) { $cache[$table][] = $prefix; } // An ordinary prefix } else { $cache[$table][] = $cols[$i]; } } } return $cache[$table]; } // mysql_cols_of_type: Return the columns in a certain data type function mysql_cols_of_type($result, $type, $format = SQL_FETCH_ASSOC) { $result_key = _mysql_result_hashkey($result); // Cache the result static $cache = array(); // Return the cache if (array_key_exists($result_key, $cache)) { return $cache[$result_key][$type][$format]; } // Check each field type $count = mysql_num_fields($result); $cols = array( SQL_TYPE_BOOLEAN => array( SQL_FETCH_ASSOC => array(), SQL_FETCH_ROW => array(), ), SQL_TYPE_BLOB => array( SQL_FETCH_ASSOC => array(), SQL_FETCH_ROW => array(), ), SQL_TYPE_INTEGER => array( SQL_FETCH_ASSOC => array(), SQL_FETCH_ROW => array(), ), SQL_TYPE_BIGINT => array( SQL_FETCH_ASSOC => array(), SQL_FETCH_ROW => array(), ), SQL_TYPE_FLOAT => array( SQL_FETCH_ASSOC => array(), SQL_FETCH_ROW => array(), ), ); for ($i = 0; $i < $count; $i++) { $coltype = mysql_field_type($result, $i); $colname = mysql_field_name($result, $i); $collen = mysql_field_len($result, $i); if ($coltype == "string" && $collen == 0) { $cols[SQL_TYPE_BOOLEAN][SQL_FETCH_ROW][] = $i; $cols[SQL_TYPE_BOOLEAN][SQL_FETCH_ASSOC][] = $colname; } elseif ($coltype == "blob") { $cols[SQL_TYPE_BLOB][SQL_FETCH_ROW][] = $i; $cols[SQL_TYPE_BLOB][SQL_FETCH_ASSOC][] = $colname; } elseif ($coltype == "int" && $collen <= 11) { $cols[SQL_TYPE_INTEGER][SQL_FETCH_ROW][] = $i; $cols[SQL_TYPE_INTEGER][SQL_FETCH_ASSOC][] = $colname; } elseif ($coltype == "int" && $collen == 20) { $cols[SQL_TYPE_BIGINT][SQL_FETCH_ROW][] = $i; $cols[SQL_TYPE_BIGINT][SQL_FETCH_ASSOC][] = $colname; } elseif ($coltype == "real") { $cols[SQL_TYPE_FLOAT][SQL_FETCH_ROW][] = $i; $cols[SQL_TYPE_FLOAT][SQL_FETCH_ASSOC][] = $colname; } } // Cache it $cache[$result_key] = $cols; return $cols[$type][$format]; } // mysql_col_lens: Obtain the column lengths of a MySQL table function mysql_col_lens($table) { // Cache the result static $cache = array(); // Return the cache if (array_key_exists($table, $cache)) { return $cache[$table]; } $select = "SELECT * FROM $table LIMIT 1;\n"; $result = xmysql_query($select); $count = mysql_num_fields($result); for ($i = 0, $cache[$table] = array(); $i < $count; $i++) { $cache[$table][mysql_field_name($result, $i)] = mysql_field_len($result, $i); } // Hash the multi-lingual columns $lndb = getlang(LN_DATABASE); foreach (mysql_cols_ml($table) as $col) { $cache[$table][$col] = $cache[$table][$col . "_" . $lndb]; } return $cache[$table]; } // mysql_strcat: Concatenate strings in MySQL // MySQL uses the CONCAT() function to concatenate strings function mysql_strcat() { $strs = func_get_args(); return "CONCAT(" . implode(", ", $strs) . ")"; } // mysql_lastupd: Obtain the last updated time of a list of tables function mysql_lastupd($tables) { // Bounce if no tables supplied if (is_null($tables) || count($tables) == 0) { return; } // Remove duplicates $tables = array_values(array_unique($tables)); // Query $conds = array(); foreach ($tables as $table) { $conds[] = "tabname='" . mysql_escape_string($table) . "'"; } $select = "SELECT mtime FROM mtime" . " WHERE " . implode(" OR ", $conds) . " ORDER BY mtime DESC LIMIT 1;\n"; $result = xmysql_query($select); // Bounce if no data found if (mysql_num_rows($result) != 1) { return; } // Return the result $row = xmysql_fetch_assoc($result); return $row["mtime"]; } // mysql_dbsize: Obtain the size of the database function mysql_dbsize() { $select = "SHOW TABLE STATUS;\n"; $result = xmysql_query($select); $count = mysql_num_rows($result); for ($i = 0, $size = 0; $i < $count; $i++) { $row = xmysql_fetch_row($result); $size += $row["Data_length"]; } return $size; } // mysql_date: Return date in a predefined format function mysql_date($expr, $format) { switch ($format) { case SQL_YYYYMMDD: return "DATE_FORMAT($expr, '%Y%m%d')"; case SQL_YYYY_YYYYMMDD: return "DATE_FORMAT($expr, '%Y/%Y%m%d')"; case SQL_MM_DD: return "DATE_FORMAT($expr, '%m-%d')"; case SQL_M_D_EN: return "DATE_FORMAT($expr, '%c/%e')"; case SQL_M_D_ZHTW: return "DATE_FORMAT($expr, '%c月%e日')"; case SQL_M_D_DE: return "DATE_FORMAT($expr, '%e.%c')"; case SQL_HH_MM: return "TIME_FORMAT($expr, '%H:%i')"; } } // mysql_re: Return the MySQL regular expression operator function mysql_re() { return "REGEXP"; } // _mysql_result_hashkey: Generate a hash key from a MySQL query result function _mysql_result_hashkey($result) { // Use the output of var_dump ob_start(); var_dump($result); $key = ob_get_contents(); ob_end_clean(); return $key; } // _mysql_locks2str: Convert lock array to SQL text string function _mysql_locks2str($locks) { $reads = array(); $writes = array(); // Adjust the current lock table foreach (array_keys($locks) as $table) { switch ($locks[$table]) { case LOCK_SH: $reads[] = $table; break; case LOCK_EX: $writes[] = $table; break; } } sort($reads); sort($writes); $phrases = array(); foreach ($writes as $table) { $phrases[] = "$table WRITE"; } foreach ($reads as $table) { $phrases[] = "$table READ"; } return implode(", ", $phrases); } ?>