709 lines
21 KiB
PHP
709 lines
21 KiB
PHP
<?php
|
|
// File name: postgres.inc.php
|
|
// Description: PHP subroutines to handle PostgreSQL operation
|
|
// Date: 2004-05-22
|
|
// Author: imacat <imacat@pristine.com.tw>
|
|
// Copyright: Copyright (C) 2004-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
|
|
$_PG_CONN = null;
|
|
|
|
// xpg_connect: Connect to the PostgreSQL database
|
|
function xpg_connect($database = null)
|
|
{
|
|
global $_PG_CONN;
|
|
// Connected
|
|
if (!is_null($_PG_CONN)) {
|
|
return;
|
|
}
|
|
// Obtain the SQL log-in information
|
|
if (is_null($database)) {
|
|
if (getenv("PGDATABASE") !== false) {
|
|
$database = getenv("PGDATABASE");
|
|
} elseif (defined("PACKAGE")) {
|
|
$database = PACKAGE;
|
|
}
|
|
}
|
|
if (getenv("PGHOST") !== false) {
|
|
$host = getenv("PGHOST");
|
|
} 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_POSTGRESQL, $database, $host);
|
|
// Connect it
|
|
$_PG_CONN = pg_connect(sprintf("host=%s dbname=%s user=%s password=%s",
|
|
$r["PGHOST"], $r["PGDATABASE"], $r["PGUSER"], $r["PGPASSWORD"]));
|
|
if ($_PG_CONN === false) {
|
|
$_PG_CONN = null;
|
|
trigger_error("Failed connecting to the PostgreSQL server.\n"
|
|
. $GLOBALS["php_errormsg"], E_USER_ERROR);
|
|
}
|
|
|
|
// Ask the password from the console
|
|
} else {
|
|
$user = null;
|
|
$subseq = false;
|
|
set_error_handler("null_error_handler");
|
|
$_PG_CONN = pg_connect("dbname=$database");
|
|
restore_error_handler();
|
|
while ($_PG_CONN === false) {
|
|
global $php_errormsg;
|
|
if ($subseq) {
|
|
fprintf(STDERR, "%s\n", $GLOBALS["php_errormsg"]);
|
|
sleep(5);
|
|
}
|
|
$subseq = true;
|
|
// Obtain the current login user
|
|
if ( is_null($user)
|
|
&& preg_match("/ failed for user \"(.+?)\"/", $GLOBALS["php_errormsg"], $m)) {
|
|
$user = $m[1];
|
|
}
|
|
// Disable console echo
|
|
system("/bin/stty -echo");
|
|
fprintf(STDERR, !is_null($user)? "PostgreSQL password for $user: ":
|
|
"PostgreSQL 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");
|
|
$_PG_CONN = pg_connect("dbname=$database password=$passwd");
|
|
restore_error_handler();
|
|
}
|
|
}
|
|
|
|
// Set the client encoding
|
|
$chaset = defined("SQL_CHARSET")? SQL_CHARSET: "utf8";
|
|
$result = pg_set_client_encoding($_PG_CONN, $chaset);
|
|
if ($result == -1) {
|
|
trigger_error("Failed setting client encoding to $chaset.\n"
|
|
. (pg_last_error()? pg_last_error(): $GLOBALS["php_errormsg"]), E_USER_ERROR);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// xpg_close: Disconnect from the PostgreSQL database
|
|
function xpg_close()
|
|
{
|
|
global $_PG_CONN;
|
|
if (is_null($_PG_CONN)) {
|
|
return;
|
|
}
|
|
$result = pg_close($_PG_CONN);
|
|
if ($result !== true) {
|
|
trigger_error("Failed disconnecting from the PostgreSQL server.\n"
|
|
. (pg_last_error()? pg_last_error(): $GLOBALS["php_errormsg"]), E_USER_ERROR);
|
|
}
|
|
$_PG_CONN = null;
|
|
return;
|
|
}
|
|
|
|
|
|
/////////////////////////
|
|
// Concurrency Control: Transactions and Locks
|
|
/////////////////////////
|
|
$_PG_IN_TRANSACTION = false;
|
|
// pg_begin: Begin a PostgreSQL transaction
|
|
function pg_begin()
|
|
{
|
|
if (!$GLOBALS["_PG_IN_TRANSACTION"]) {
|
|
$begin = "START TRANSACTION;\n";
|
|
xpg_query($begin);
|
|
$GLOBALS["_PG_IN_TRANSACTION"] = true;
|
|
}
|
|
}
|
|
// pg_commit: Commit a PostgreSQL transaction
|
|
function pg_commit()
|
|
{
|
|
if ($GLOBALS["_PG_IN_TRANSACTION"]) {
|
|
$commit = "COMMIT;\n";
|
|
xpg_query($commit);
|
|
$GLOBALS["_PG_IN_TRANSACTION"] = false;
|
|
}
|
|
}
|
|
// pg_rollback: Rollback a PostgreSQL transaction
|
|
function pg_rollback()
|
|
{
|
|
if ($GLOBALS["_PG_IN_TRANSACTION"]) {
|
|
$rollback = "ROLLBACK;\n";
|
|
xpg_query($rollback);
|
|
$GLOBALS["_PG_IN_TRANSACTION"] = false;
|
|
}
|
|
}
|
|
|
|
// pg_lock: PostgreSQL table-locking handler
|
|
// PostgreSQL has no unlock
|
|
// 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: No effect
|
|
// null has no effect here.
|
|
// Return: None. Errors are directed to error handlers
|
|
function pg_lock($locks = null)
|
|
{
|
|
// Bounce for nothing
|
|
if (is_null($locks) || count($locks) == 0) {
|
|
return;
|
|
}
|
|
|
|
// Remove the table aliases - compatibility with stupid MySQL
|
|
$abslocks = array();
|
|
foreach (array_keys($locks) as $origtable) {
|
|
// Remove the table aliases
|
|
$abstable = preg_replace("/\s+AS\s+.+?$/i", "", $origtable);
|
|
// No override previous write lock
|
|
if ( array_key_exists($abstable, $abslocks)
|
|
&& $abslocks[$abstable] == LOCK_EX) {
|
|
continue;
|
|
}
|
|
// Set the lock
|
|
$abslocks[$abstable] = $locks[$origtable];
|
|
}
|
|
$locks = $abslocks;
|
|
|
|
// Split into different lock modes
|
|
$reads = array();
|
|
$writes = array();
|
|
foreach (array_keys($locks) as $table) {
|
|
switch ($locks[$table]) {
|
|
case LOCK_SH:
|
|
$reads[] = $table;
|
|
break;
|
|
case LOCK_EX:
|
|
$writes[] = $table;
|
|
break;
|
|
default:
|
|
trigger_error("Bad SQL lock request: \"$locks[$table]\" on table \"$table\".", E_USER_ERROR);
|
|
}
|
|
}
|
|
|
|
// Start transaction if not started yet. Table locks cannot
|
|
// live outside of transactions at all.
|
|
if (!$GLOBALS["_PG_IN_TRANSACTION"]) {
|
|
pg_begin();
|
|
}
|
|
|
|
// Request the locks
|
|
if (count($reads) > 0) {
|
|
$locktable = "LOCK TABLE " . implode(", ", $reads)
|
|
. " IN SHARE MODE;\n";
|
|
xpg_query($locktable);
|
|
}
|
|
if (count($writes) > 0) {
|
|
$locktable = "LOCK TABLE " . implode(", ", $writes)
|
|
. " IN ACCESS EXCLUSIVE MODE;\n";
|
|
xpg_query($locktable);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// xpg_query: Do a PostgreSQL query and report the error
|
|
function xpg_query($query)
|
|
{
|
|
set_error_handler("null_error_handler");
|
|
$result = pg_query($query);
|
|
restore_error_handler();
|
|
if ($result === false) {
|
|
trigger_error("Failed pg_query().\n$query\n"
|
|
. (pg_last_error()? pg_last_error(): $GLOBALS["php_errormsg"]), E_USER_ERROR);
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
// pg_seek: Move the PostgreSQL result pointer
|
|
function pg_seek($result, $offset)
|
|
{
|
|
$result = pg_result_seek($result, $offset);
|
|
if ($result === false) {
|
|
trigger_error("Failed pg_result_seek().\n"
|
|
. (pg_last_error()? pg_last_error(): $GLOBALS["php_errormsg"]), E_USER_ERROR);
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
// xpg_fetch_assoc: Return a PostgreSQL row as an associative array
|
|
function xpg_fetch_assoc($result)
|
|
{
|
|
// Fetch the raw data now
|
|
$row = pg_fetch_assoc($result);
|
|
// Return the error
|
|
if (!is_array($row)) {
|
|
return $row;
|
|
}
|
|
|
|
// Adjust the boolean columns
|
|
foreach (pg_cols_of_type($result, SQL_TYPE_BOOLEAN, SQL_FETCH_ASSOC) as $col) {
|
|
if (!is_null($row[$col])) {
|
|
$row[$col] = ($row[$col] == "t");
|
|
}
|
|
}
|
|
// Adjust the bytea columns
|
|
foreach (pg_cols_of_type($result, SQL_TYPE_BLOB, SQL_FETCH_ASSOC) as $col) {
|
|
if (!is_null($row[$col])) {
|
|
$row[$col] = pg_unescape_bytea($row[$col]);
|
|
}
|
|
}
|
|
// Adjust the integer columns
|
|
foreach (pg_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 (pg_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 (pg_cols_of_type($result, SQL_TYPE_FLOAT, SQL_FETCH_ASSOC) as $col) {
|
|
if (!is_null($row[$col])) {
|
|
settype($row[$col], "float");
|
|
}
|
|
}
|
|
|
|
return $row;
|
|
}
|
|
|
|
// xpg_fetch_row: Return a PostgreSQL row as an numeric array
|
|
function xpg_fetch_row($result)
|
|
{
|
|
// Fetch the raw data now
|
|
$row = pg_fetch_row($result);
|
|
// Return the error
|
|
if (!is_array($row)) {
|
|
return $row;
|
|
}
|
|
|
|
// Adjust the boolean columns
|
|
foreach (pg_cols_of_type($result, SQL_TYPE_BOOLEAN, SQL_FETCH_ROW) as $col) {
|
|
if (!is_null($row[$col])) {
|
|
$row[$col] = ($row[$col] == "t");
|
|
}
|
|
}
|
|
// Adjust the bytea columns
|
|
foreach (pg_cols_of_type($result, SQL_TYPE_BLOB, SQL_FETCH_ROW) as $col) {
|
|
if (!is_null($row[$col])) {
|
|
$row[$col] = pg_unescape_bytea($row[$col]);
|
|
}
|
|
}
|
|
// Adjust the integer columns
|
|
foreach (pg_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 (pg_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 (pg_cols_of_type($result, SQL_TYPE_FLOAT, SQL_FETCH_ROW) as $col) {
|
|
if (!is_null($row[$col])) {
|
|
settype($row[$col], "float");
|
|
}
|
|
}
|
|
|
|
return $row;
|
|
}
|
|
|
|
// pg_tables: Obtain a list of available PostgreSQL tables
|
|
// and report the error
|
|
function pg_tables($schema = null)
|
|
{
|
|
// Default to the current schema
|
|
if (is_null($schema)) {
|
|
$schema = _pg_current_schema();
|
|
}
|
|
$select = "SELECT tablename FROM pg_tables"
|
|
. " WHERE schemaname='" . pg_escape_string($schema) . "'"
|
|
. " ORDER BY tablename;\n";
|
|
$result = xpg_query($select);
|
|
$count = pg_num_rows($result);
|
|
for ($i = 0, $tables = array(); $i < $count; $i++) {
|
|
$row = pg_fetch_row($result);
|
|
$tables[] = $row[0];
|
|
}
|
|
return $tables;
|
|
}
|
|
|
|
// pg_cols: Obtain the column list of a PostgreSQL table
|
|
function pg_cols($table)
|
|
{
|
|
// Cache the result
|
|
static $cache = array();
|
|
// Return the cache
|
|
if (array_key_exists($table, $cache)) {
|
|
return $cache[$table];
|
|
}
|
|
global $_PG_CONN;
|
|
// Use pg_meta_data(). This is marked as experimental in PHP documentation yet.
|
|
$result = pg_meta_data($_PG_CONN, $table);
|
|
if ($result === false) {
|
|
trigger_error("Failed pg_meta_data(\$PG_CONN, \"$table\").\n"
|
|
. (pg_last_error()? pg_last_error(): $GLOBALS["php_errormsg"]), E_USER_ERROR);
|
|
}
|
|
$cache[$table] = array_keys($result);
|
|
return $cache[$table];
|
|
}
|
|
|
|
// pg_cols_ml: Return a list of multi-lingual columns in a PostgreSQL table
|
|
function pg_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 = pg_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];
|
|
}
|
|
|
|
// pg_cols_nl: Return a list of columns without their multi-lingual
|
|
// deviants in a PostgreSQL table
|
|
function pg_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 = pg_cols($table);
|
|
$langcols = pg_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];
|
|
}
|
|
|
|
// pg_cols_of_type: Return the columns in a certain data type
|
|
function pg_cols_of_type($result, $type, $format = SQL_FETCH_ASSOC)
|
|
{
|
|
$result_key = _pg_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 = pg_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 = pg_field_type($result, $i);
|
|
$colname = pg_field_name($result, $i);
|
|
switch ($coltype) {
|
|
case "bool":
|
|
$cols[SQL_TYPE_BOOLEAN][SQL_FETCH_ROW][] = $i;
|
|
$cols[SQL_TYPE_BOOLEAN][SQL_FETCH_ASSOC][] = $colname;
|
|
break;
|
|
case "bytea":
|
|
$cols[SQL_TYPE_BLOB][SQL_FETCH_ROW][] = $i;
|
|
$cols[SQL_TYPE_BLOB][SQL_FETCH_ASSOC][] = $colname;
|
|
break;
|
|
case "int2":
|
|
case "int4":
|
|
$cols[SQL_TYPE_INTEGER][SQL_FETCH_ROW][] = $i;
|
|
$cols[SQL_TYPE_INTEGER][SQL_FETCH_ASSOC][] = $colname;
|
|
break;
|
|
case "int8":
|
|
$cols[SQL_TYPE_BIGINT][SQL_FETCH_ROW][] = $i;
|
|
$cols[SQL_TYPE_BIGINT][SQL_FETCH_ASSOC][] = $colname;
|
|
break;
|
|
case "float4":
|
|
case "float8":
|
|
$cols[SQL_TYPE_FLOAT][SQL_FETCH_ROW][] = $i;
|
|
$cols[SQL_TYPE_FLOAT][SQL_FETCH_ASSOC][] = $colname;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Cache it
|
|
$cache[$result_key] = $cols;
|
|
return $cols[$type][$format];
|
|
}
|
|
|
|
// pg_col_lens: Obtain the column lengths of a PostgreSQL table
|
|
function pg_col_lens($table)
|
|
{
|
|
// Cache the result
|
|
static $cache = array();
|
|
// Return the cache
|
|
if (array_key_exists($table, $cache)) {
|
|
return $cache[$table];
|
|
}
|
|
$schema = _pg_current_schema();
|
|
$select = "SELECT pg_attribute.attname AS col,"
|
|
. " pg_type.typname AS type,"
|
|
. " pg_attribute.attlen AS len,"
|
|
. " pg_attribute.atttypmod AS typmod"
|
|
. " FROM pg_attribute"
|
|
. " INNER JOIN pg_class ON pg_attribute.attrelid=pg_class.oid"
|
|
. " INNER JOIN pg_type ON pg_attribute.atttypid=pg_type.oid"
|
|
. " INNER JOIN pg_namespace ON pg_class.relnamespace=pg_namespace.oid"
|
|
. " WHERE pg_namespace.nspname='" . pg_escape_string($schema) . "'"
|
|
. " AND pg_class.relname='" . pg_escape_string($table) . "'"
|
|
. " AND pg_class.relkind='r'"
|
|
. " AND pg_attribute.attnum>0"
|
|
. " ORDER BY pg_attribute.attnum;\n";
|
|
$result = xpg_query($select);
|
|
$count = pg_num_rows($result);
|
|
for ($i = 0, $cache[$table] = array(); $i < $count; $i++) {
|
|
$row = pg_fetch_assoc($result);
|
|
switch ($row["type"]) {
|
|
// Integer -- Digits of the largest number - 1
|
|
case "int2":
|
|
case "int4":
|
|
case "int8":
|
|
$cache[$table][$row["col"]] = floor(log10(pow(256, $row["len"])));
|
|
settype($cache[$table][$row["col"]], "integer");
|
|
break;
|
|
// Refer to typmod for char and varchar
|
|
case "varchar":
|
|
case "bpchar":
|
|
$cache[$table][$row["col"]] = $row["typmod"] - 4;
|
|
break;
|
|
// Set text and bytea to 4294967296 (2^32) (infinite actually)
|
|
case "text":
|
|
case "bytea":
|
|
$cache[$table][$row["col"]] = 4294967296;
|
|
break;
|
|
// Set timestamp to 19
|
|
case "timestamp":
|
|
$cache[$table][$row["col"]] = 26;
|
|
break;
|
|
// Set date to 10
|
|
case "date":
|
|
$cache[$table][$row["col"]] = 10;
|
|
break;
|
|
// Set time to 8
|
|
case "time":
|
|
$cache[$table][$row["col"]] = 8;
|
|
break;
|
|
// Set numeric to precision + 1 decimal point
|
|
// Refer to http://archives.postgresql.org/pgsql-hackers/1999-01/msg00127.php
|
|
case "numeric":
|
|
$typmod = $row["typmod"] - 4;
|
|
$scale = $typmod & 0xFFFF;
|
|
$precision = $typmod >> 16;
|
|
$cache[$table][$row["col"]] = $precision + 1;
|
|
break;
|
|
// Set boolean to 1
|
|
case "bool":
|
|
$cache[$table][$row["col"]] = 1;
|
|
break;
|
|
// Set inet to 18 (nnn.nnn.nnn.nnn/nn)
|
|
case "inet":
|
|
$cache[$table][$row["col"]] = 18;
|
|
break;
|
|
// Bounce for other columns, so that we know to fix it here
|
|
default:
|
|
trigger_error("Unknown column type " . $row["type"]
|
|
. " for table $table", E_USER_ERROR);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Hash the multi-lingual columns
|
|
$lndb = getlang(LN_DATABASE);
|
|
foreach (pg_cols_ml($table) as $col) {
|
|
$cache[$table][$col] = $cache[$table][$col . "_" . $lndb];
|
|
}
|
|
|
|
return $cache[$table];
|
|
}
|
|
|
|
// pg_strcat: Concatenate strings in PostgreSQL
|
|
// PostgreSQL uses the || operator to concatenate strings
|
|
function pg_strcat()
|
|
{
|
|
$strs = func_get_args();
|
|
return implode(" || ", $strs);
|
|
}
|
|
|
|
// pg_lastupd: Obtain the last updated time of a list of tables
|
|
function pg_lastupd($tables)
|
|
{
|
|
// Bounce if no tables supplied
|
|
if (is_null($tables) || count($tables) == 0) {
|
|
return;
|
|
}
|
|
// Remove the table aliases
|
|
for ($i = 0; $i < count($tables); $i++) {
|
|
$tables[$i] = preg_replace("/\s+AS\s+.+?$/i", "", $tables[$i]);
|
|
}
|
|
// Remove duplicates
|
|
$tables = array_values(array_unique($tables));
|
|
// Query
|
|
$conds = array();
|
|
foreach ($tables as $table) {
|
|
$conds[] = "tabname='" . pg_escape_string($table) . "'";
|
|
}
|
|
$select = "SELECT mtime FROM mtime"
|
|
. " WHERE " . implode(" OR ", $conds)
|
|
. " ORDER BY mtime DESC LIMIT 1;\n";
|
|
$result = xpg_query($select);
|
|
// Bounce if no data found
|
|
if (pg_num_rows($result) != 1) {
|
|
return;
|
|
}
|
|
// Return the result
|
|
$row = xpg_fetch_assoc($result);
|
|
return $row["mtime"];
|
|
}
|
|
|
|
// pg_dbsize: Obtain the size of the database
|
|
function pg_dbsize()
|
|
{
|
|
$select = "SELECT pg_database_size(datname) FROM pg_database"
|
|
. " WHERE datname=current_database();\n";
|
|
$result = xpg_query($select);
|
|
$row = xpg_fetch_row($result);
|
|
return $row[0];
|
|
}
|
|
|
|
// pg_date: Return date in a predefined format format
|
|
function pg_date($expr, $format)
|
|
{
|
|
switch ($format) {
|
|
case SQL_YYYYMMDD:
|
|
return "to_char($expr, 'YYYYMMDD')";
|
|
case SQL_YYYY_YYYYMMDD:
|
|
return "to_char($expr, 'YYYY/YYYYMMDD')";
|
|
case SQL_MM_DD:
|
|
return "to_char($expr, 'MM-DD')";
|
|
case SQL_M_D_EN:
|
|
return "to_char($expr, 'FMMM/FMDD')";
|
|
case SQL_M_D_ZHTW:
|
|
return "to_char($expr, 'FMMM月FMDD日')";
|
|
case SQL_M_D_DE:
|
|
return "to_char($expr, 'FMDD.FMMM')";
|
|
case SQL_HH_MM:
|
|
return "to_char($expr, 'HH24:MI')";
|
|
}
|
|
}
|
|
|
|
// pg_re: Return the PostgreSQL regular expression operator
|
|
function pg_re()
|
|
{
|
|
return "~";
|
|
}
|
|
|
|
// _pg_result_hashkey: Generate a hash key from a PostgreSQL query result
|
|
function _pg_result_hashkey($result)
|
|
{
|
|
// Use the output of var_dump
|
|
ob_start();
|
|
var_dump($result);
|
|
$key = ob_get_contents();
|
|
ob_end_clean();
|
|
return $key;
|
|
}
|
|
|
|
// _pg_current_schema: Obtain the current schema
|
|
function _pg_current_schema()
|
|
{
|
|
// Cache the result
|
|
static $cache;
|
|
// Return the cache
|
|
if (isset($cache)) {
|
|
return $cache;
|
|
}
|
|
$select = "SELECT current_schema();\n";
|
|
$result = xpg_query($select);
|
|
$row = pg_fetch_row($result);
|
|
$cache = $row[0];
|
|
return $cache;
|
|
}
|
|
|
|
?>
|