mirror of
https://github.com/PostgreSQL-For-Wordpress/postgresql-for-wordpress.git
synced 2026-01-26 00:12:18 +01:00
561 lines
18 KiB
PHP
561 lines
18 KiB
PHP
<?php
|
|
|
|
/**
|
|
* @package PostgreSQL_For_Wordpress
|
|
* @version $Id$
|
|
* @author Hawk__, www.hawkix.net
|
|
*/
|
|
|
|
include_once PG4WP_ROOT . '/driver_pgsql_rewrite.php';
|
|
|
|
/**
|
|
* Provides a driver for PostgreSQL
|
|
*
|
|
* This file maps original mysql_* functions with PostgreSQL equivalents
|
|
*
|
|
* This was originally based on usleepless's original 'mysql2pgsql.php' file, many thanks to him
|
|
*/
|
|
// Check pgsql extension is loaded
|
|
if (!extension_loaded('pgsql')) {
|
|
wp_die('Your PHP installation appears to be missing the PostgreSQL extension which is required by WordPress with PG4WP.');
|
|
}
|
|
|
|
// Initializing some variables
|
|
$GLOBALS['pg4wp_version'] = '7.0';
|
|
$GLOBALS['pg4wp_result'] = 0;
|
|
$GLOBALS['pg4wp_numrows_query'] = '';
|
|
$GLOBALS['pg4wp_ins_table'] = '';
|
|
$GLOBALS['pg4wp_ins_field'] = '';
|
|
$GLOBALS['pg4wp_last_insert'] = '';
|
|
$GLOBALS['pg4wp_connstr'] = '';
|
|
$GLOBALS['pg4wp_conn'] = false;
|
|
|
|
/**
|
|
* Establishes a connection to a PostgreSQL database.
|
|
*
|
|
* Constructs a connection string using provided parameters and attempts to
|
|
* establish a connection to a PostgreSQL database.
|
|
*
|
|
* Differences from MySQL:
|
|
* - Must connect to a specific database, unlike MySQL where you can connect without specifying a database.
|
|
* - Uses `pg_connect` instead of `mysql_connect`.
|
|
* - Connection string is different, using key-value pairs like 'host=', 'port=', etc.
|
|
*
|
|
* @param string $dbserver The server hostname and port separated by a colon.
|
|
* @param string $dbuser The username for the database.
|
|
* @param string $dbpass The password for the database.
|
|
* @return resource|bool The PostgreSQL connection resource on success, or false on failure.
|
|
* @throws WP_Error If insecure connection is attempted without setting PG4WP_INSECURE.
|
|
*/
|
|
function wpsql_connect($dbserver, $dbuser, $dbpass)
|
|
{
|
|
$GLOBALS['pg4wp_connstr'] = '';
|
|
$hostport = explode(':', $dbserver);
|
|
|
|
if (!empty($hostport[0])) {
|
|
$GLOBALS['pg4wp_connstr'] .= ' host=' . $hostport[0];
|
|
}
|
|
|
|
if (!empty($hostport[1])) {
|
|
$GLOBALS['pg4wp_connstr'] .= ' port=' . $hostport[1];
|
|
}
|
|
|
|
if (!empty($dbuser)) {
|
|
$GLOBALS['pg4wp_connstr'] .= ' user=' . $dbuser;
|
|
}
|
|
|
|
if (!empty($dbpass)) {
|
|
$GLOBALS['pg4wp_connstr'] .= ' password=' . $dbpass;
|
|
} elseif (!PG4WP_INSECURE) {
|
|
wp_die('Connecting to your PostgreSQL database without a password is considered insecure.
|
|
<br />If you want to do it anyway, please set "PG4WP_INSECURE" to true in your "db.php" file.');
|
|
}
|
|
|
|
// Must connect to a specific database unlike MySQL
|
|
$dbname = defined('DB_NAME') && DB_NAME ? DB_NAME : 'template1';
|
|
return pg_connect($GLOBALS['pg4wp_connstr'] . ' dbname=' . $dbname);
|
|
}
|
|
|
|
/**
|
|
* Establishes a connection to a PostgreSQL database.
|
|
*
|
|
* This function connects to a PostgreSQL database by creating a connection string and using
|
|
* pg_connect. If the connection is successful, it stores the PostgreSQL server version into
|
|
* a global variable. The function also handles early transmitted SQL commands and initializes
|
|
* the connection with pg4wp_init().
|
|
*
|
|
* Note: Unlike the MySQL equivalent, pg_connect will return an existing connection if one
|
|
* exists with the same connection string.
|
|
*
|
|
* @param string $dbname The name of the database to connect to.
|
|
* @param int $connection_id The connection ID (Unused in this function).
|
|
*
|
|
* @return resource|bool The connection resource on success, or FALSE on failure.
|
|
*/
|
|
function wpsql_select_db($dbname, $connection_id = 0)
|
|
{
|
|
$pg_connstr = $GLOBALS['pg4wp_connstr'] . ' dbname=' . $dbname;
|
|
|
|
// pg_connect returns existing connection for same connection string
|
|
$GLOBALS['pg4wp_conn'] = $conn = pg_connect($pg_connstr);
|
|
|
|
// Return FALSE if connection failed
|
|
if(!$conn) {
|
|
return $conn;
|
|
}
|
|
|
|
// Get and store PostgreSQL server version
|
|
$ver = pg_version($conn);
|
|
if(isset($ver['server'])) {
|
|
$GLOBALS['pg4wp_version'] = $ver['server'];
|
|
}
|
|
|
|
// Clear the connection string unless this is a test connection
|
|
if(!defined('WP_INSTALLING') || !WP_INSTALLING) {
|
|
$GLOBALS['pg4wp_connstr'] = '';
|
|
}
|
|
|
|
// Execute any pre-defined SQL commands
|
|
if(!empty($GLOBALS['pg4wp_pre_sql'])) {
|
|
foreach($GLOBALS['pg4wp_pre_sql'] as $sql2run) {
|
|
wpsql_query($sql2run);
|
|
}
|
|
}
|
|
|
|
// Initialize connection with custom function
|
|
pg4wp_init($conn);
|
|
|
|
return $conn;
|
|
}
|
|
|
|
/**
|
|
* Initializes the database environment for pg4wp.
|
|
*
|
|
* This function sets up a MySQL-compatible `field` function in PostgreSQL.
|
|
* Note: In MySQL, the field function accepts arguments of heterogeneous types,
|
|
* but in PostgreSQL, it may not.
|
|
*
|
|
* Note: ROW_NUMBER()+unnest approach is used for performance but might not guarantee order.
|
|
* Refer to https://stackoverflow.com/a/8767450 if it breaks.
|
|
*/
|
|
function pg4wp_init()
|
|
{
|
|
// Database connection
|
|
$connection = $GLOBALS['pg4wp_conn'];
|
|
|
|
/**
|
|
* SQL query to create or replace a PostgreSQL function named "field"
|
|
* which imitates MySQL's FIELD() function behavior.
|
|
*
|
|
* - ROW_NUMBER() is a window function in Postgres, used to assign a unique integer to rows.
|
|
* - unnest() is a Postgres function that takes an array and returns a set of rows.
|
|
*
|
|
* The function takes anyelement as the first parameter and anyarray as the second.
|
|
* It returns a BIGINT.
|
|
*
|
|
* SQL is used as the procedural language, and the function is marked as IMMUTABLE.
|
|
*/
|
|
$sql = <<<'SQL'
|
|
CREATE OR REPLACE FUNCTION field(anyelement, VARIADIC anyarray)
|
|
RETURNS BIGINT AS $$
|
|
SELECT rownum
|
|
FROM (
|
|
SELECT ROW_NUMBER() OVER () AS rownum, elem
|
|
FROM unnest($2) elem
|
|
) AS numbered
|
|
WHERE numbered.elem = $1
|
|
UNION ALL
|
|
SELECT 0
|
|
$$
|
|
LANGUAGE SQL IMMUTABLE;
|
|
SQL;
|
|
|
|
// Execute the SQL query
|
|
$result = pg_query($connection, $sql);
|
|
|
|
if ((PG4WP_DEBUG || PG4WP_LOG_ERRORS) && $result === false) {
|
|
$error = pg_last_error();
|
|
error_log("[" . microtime(true) . "] Error creating MySQL-compatible field function: $error\n", 3, PG4WP_LOG . 'pg4wp_errors.log');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Executes a SQL query on a Postgres database.
|
|
*
|
|
* This function handles the SQL query by executing it on a Postgres database,
|
|
* if the global connection is available. Otherwise, it stores the SQL
|
|
* statement for later execution. It also handles errors and debugging.
|
|
*
|
|
* @param string $sql The SQL query string.
|
|
* @return resource|bool The query result resource on success, or FALSE on failure.
|
|
*
|
|
* Differences from MySQL equivalents:
|
|
* - Uses pg_query() instead of mysql_query() for executing the query.
|
|
* - Error handling is done using pg_last_error() instead of mysql_error().
|
|
* - SQL rewriting is performed by the pg4wp_rewrite() function.
|
|
*/
|
|
function wpsql_query($sql)
|
|
{
|
|
// Check if a connection to Postgres database is established
|
|
if (!$GLOBALS['pg4wp_conn']) {
|
|
// Store SQL query for later execution when connection is available
|
|
$GLOBALS['pg4wp_pre_sql'][] = $sql;
|
|
return true;
|
|
}
|
|
|
|
// Store the initial SQL query
|
|
$initial = $sql;
|
|
// Rewrite the SQL query for compatibility with Postgres
|
|
$sql = pg4wp_rewrite($sql);
|
|
|
|
// Execute the SQL query and store the result
|
|
if (PG4WP_DEBUG) {
|
|
$GLOBALS['pg4wp_result'] = pg_query($GLOBALS['pg4wp_conn'], $sql);
|
|
} else {
|
|
$GLOBALS['pg4wp_result'] = @pg_query($GLOBALS['pg4wp_conn'], $sql);
|
|
}
|
|
|
|
// Handle errors and logging
|
|
if ((PG4WP_DEBUG || PG4WP_LOG_ERRORS) && $GLOBALS['pg4wp_result'] === false && $err = pg_last_error($GLOBALS['pg4wp_conn'])) {
|
|
$ignore = false;
|
|
// Ignore errors if WordPress is in the installation process
|
|
if (defined('WP_INSTALLING') && WP_INSTALLING) {
|
|
global $table_prefix;
|
|
$ignore = strpos($err, 'relation "' . $table_prefix);
|
|
}
|
|
if (!$ignore) {
|
|
error_log('[' . microtime(true) . "] Error running :\n$initial\n---- converted to ----\n$sql\n----> $err\n---------------------\n", 3, PG4WP_LOG . 'pg4wp_errors.log');
|
|
}
|
|
}
|
|
|
|
// Return the query result
|
|
return $GLOBALS['pg4wp_result'];
|
|
}
|
|
|
|
/**
|
|
* Fetches a result row as an associative and numeric array from a Postgres query result.
|
|
*
|
|
* This function calls the pg_fetch_array() function to fetch the next row from a Postgres result set
|
|
* and trims any leading or trailing whitespace from each of the values.
|
|
*
|
|
* Differences from MySQL Equivalent:
|
|
* 1. It uses pg_fetch_array() instead of mysql_fetch_array().
|
|
* 2. It trims the values of the resulting array, which is specific to this implementation.
|
|
*
|
|
* @param resource $result The result resource returned by a Postgres query.
|
|
* @return array|bool An array of the next row in the result set, or false if there are no more rows.
|
|
*/
|
|
function wpsql_fetch_array($result)
|
|
{
|
|
// Fetch the next row as an array
|
|
$res = pg_fetch_array($result);
|
|
|
|
// Check if the result is an array and trim its values
|
|
if (is_array($res)) {
|
|
foreach ($res as $v => $k) {
|
|
$res[$v] = trim($k);
|
|
}
|
|
}
|
|
|
|
// Return the trimmed array or false if there are no more rows
|
|
return $res;
|
|
}
|
|
|
|
/**
|
|
* Fetches the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
|
|
*
|
|
* @param resource|null $lnk A PostgreSQL connection resource. Default is `null`.
|
|
*
|
|
* @return mixed The ID generated for an AUTO_INCREMENT column by the previous INSERT query on success; `false` on failure.
|
|
*
|
|
* Note:
|
|
* 1. In PostgreSQL, this function uses CURRVAL() on the appropriate sequence to get the last inserted ID.
|
|
* 2. In MySQL, last inserted ID is generally fetched using mysql_insert_id() or mysqli_insert_id().
|
|
*/
|
|
function wpsql_insert_id($lnk = null)
|
|
{
|
|
global $wpdb;
|
|
$data = null;
|
|
$ins_field = $GLOBALS['pg4wp_ins_field'];
|
|
$table = $GLOBALS['pg4wp_ins_table'];
|
|
$lastq = $GLOBALS['pg4wp_last_insert'];
|
|
$seq = $table . '_seq';
|
|
|
|
// Special case for 'term_relationships' table, which does not have a sequence in PostgreSQL.
|
|
if ($table == $wpdb->term_relationships) {
|
|
// PostgreSQL: Using CURRVAL() to get the current value of the sequence.
|
|
$sql = "SELECT CURRVAL('$seq')";
|
|
$res = pg_query($sql);
|
|
if (false !== $res) {
|
|
$data = pg_fetch_result($res, 0, 0);
|
|
}
|
|
}
|
|
// Special case when using WP_Import plugin where ID is defined in the query itself.
|
|
elseif ('post_author' == $ins_field && false !== strpos($lastq, 'ID')) {
|
|
// No PostgreSQL specific operation here.
|
|
$sql = 'ID was in query ';
|
|
$pattern = '/.+\'(\d+).+$/';
|
|
preg_match($pattern, $lastq, $matches);
|
|
$data = $matches[1];
|
|
|
|
// PostgreSQL: Setting the value of the sequence based on the latest inserted ID.
|
|
$GLOBALS['pg4wp_queued_query'] = "SELECT SETVAL('$seq',(SELECT MAX(\"ID\") FROM $table)+1);";
|
|
} else {
|
|
// PostgreSQL: Using CURRVAL() to get the current value of the sequence.
|
|
$sql = "SELECT CURRVAL('$seq')";
|
|
$res = pg_query($GLOBALS['pg4wp_conn'], $sql);
|
|
if (false !== $res) {
|
|
$data = pg_fetch_result($res, 0, 0);
|
|
} elseif (PG4WP_DEBUG || PG4WP_LOG) {
|
|
$log = '[' . microtime(true) . "] wpsql_insert_id() was called with '$table' and '$ins_field'" .
|
|
" and returned the error:\n" . pg_last_error($GLOBALS['pg4wp_conn']) .
|
|
"\nFor the query:\n" . $sql .
|
|
"\nThe latest INSERT query was :\n'$lastq'\n";
|
|
error_log($log, 3, PG4WP_LOG . 'pg4wp_errors.log');
|
|
}
|
|
}
|
|
|
|
if (PG4WP_DEBUG && $sql) {
|
|
error_log('[' . microtime(true) . "] Getting inserted ID for '$table' ('$ins_field') : $sql => $data\n", 3, PG4WP_LOG . 'pg4wp_insertid.log');
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Fetch a specific result row's field value from a PostgreSQL result resource.
|
|
* Quick fix for wpsql_result() error and missing wpsql_errno() function
|
|
* Source : http://vitoriodelage.wordpress.com/2014/06/06/add-missing-wpsql_errno-in-pg4wp-plugin/
|
|
*
|
|
* @param resource $result The Postgres query result resource.
|
|
* @param int $i The row number from which to get the value (0-based).
|
|
* @param string|null $fieldname Optional. The field name to fetch.
|
|
*
|
|
* @return mixed Field value or false if no such row exists.
|
|
*
|
|
* Note:
|
|
* 1. This function uses `pg_fetch_result` to get a single field's value from a row.
|
|
* 2. In MySQL, you could use `mysql_result` to accomplish something similar.
|
|
*/
|
|
function wpsql_result($result, $i, $fieldname = null)
|
|
{
|
|
if (is_resource($result)) {
|
|
if ($fieldname) {
|
|
return pg_fetch_result($result, $i, $fieldname);
|
|
} else {
|
|
return pg_fetch_result($result, $i);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the SQLSTATE error code for the last query executed on the connection.
|
|
*
|
|
* @param resource $connection The Postgres database connection resource.
|
|
*
|
|
* @return string|false SQLSTATE error code or false if no error.
|
|
*
|
|
* Note:
|
|
* 1. This function uses `pg_get_result` to get the result resource of the last query.
|
|
* 2. `pg_result_status` returns the status of the result.
|
|
* 3. `pg_result_error_field` is used to get the SQLSTATE error code.
|
|
* 4. In MySQL, you could use `mysqli_errno` to get the error code directly.
|
|
*/
|
|
function wpsql_errno($connection)
|
|
{
|
|
$result = pg_get_result($connection);
|
|
if ($result === false) {
|
|
return false;
|
|
}
|
|
|
|
$result_status = pg_result_status($result);
|
|
return pg_result_error_field($result_status, PGSQL_DIAG_SQLSTATE);
|
|
}
|
|
|
|
|
|
/**
|
|
* Checks if a connection to a PostgreSQL database is alive.
|
|
*
|
|
* @param resource $conn The PostgreSQL connection resource.
|
|
* @return bool Returns true if the connection is alive, false otherwise.
|
|
*/
|
|
function wpsql_ping($conn)
|
|
{
|
|
return pg_ping($conn);
|
|
}
|
|
|
|
/**
|
|
* Gets the number of rows in a PostgreSQL result.
|
|
*
|
|
* @param resource $result The PostgreSQL result resource.
|
|
* @return int Returns the number of rows.
|
|
*/
|
|
function wpsql_num_rows($result)
|
|
{
|
|
return pg_num_rows($result);
|
|
}
|
|
|
|
/**
|
|
* Alias for wpsql_num_rows. Gets the number of rows in a PostgreSQL result.
|
|
*
|
|
* @param resource $result The PostgreSQL result resource.
|
|
* @return int Returns the number of rows.
|
|
*/
|
|
function wpsql_numrows($result)
|
|
{
|
|
return pg_num_rows($result);
|
|
}
|
|
|
|
/**
|
|
* Gets the number of fields in a PostgreSQL result.
|
|
*
|
|
* @param resource $result The PostgreSQL result resource.
|
|
* @return int Returns the number of fields.
|
|
*/
|
|
function wpsql_num_fields($result)
|
|
{
|
|
return pg_num_fields($result);
|
|
}
|
|
|
|
/**
|
|
* Mock function to mimic MySQL's fetch_field function.
|
|
*
|
|
* @param resource $result The PostgreSQL result resource.
|
|
* @return string Returns 'tablename' as a placeholder.
|
|
*/
|
|
function wpsql_fetch_field($result)
|
|
{
|
|
return 'tablename';
|
|
}
|
|
|
|
/**
|
|
* Fetches one row from a PostgreSQL result as an object.
|
|
*
|
|
* @param resource $result The PostgreSQL result resource.
|
|
* @return object Returns an object containing the data.
|
|
*/
|
|
function wpsql_fetch_object($result)
|
|
{
|
|
return pg_fetch_object($result);
|
|
}
|
|
|
|
/**
|
|
* Frees a PostgreSQL result resource.
|
|
*
|
|
* @param resource $result The PostgreSQL result resource.
|
|
* @return bool Returns true on success, false otherwise.
|
|
*/
|
|
function wpsql_free_result($result)
|
|
{
|
|
if ($result === null) {
|
|
return true;
|
|
}
|
|
|
|
return pg_free_result($result);
|
|
}
|
|
|
|
/**
|
|
* Gets the number of affected rows by the last PostgreSQL query.
|
|
*
|
|
* @return int Returns the number of affected rows.
|
|
*/
|
|
function wpsql_affected_rows()
|
|
{
|
|
if($GLOBALS['pg4wp_result'] === false) {
|
|
return 0;
|
|
}
|
|
|
|
return pg_affected_rows($GLOBALS['pg4wp_result']);
|
|
}
|
|
|
|
/**
|
|
* Fetches one row from a PostgreSQL result as an enumerated array.
|
|
*
|
|
* @param resource $result The PostgreSQL result resource.
|
|
* @return array Returns an array containing the row data.
|
|
*/
|
|
function wpsql_fetch_row($result)
|
|
{
|
|
return pg_fetch_row($result);
|
|
}
|
|
|
|
/**
|
|
* Sets the row offset for a PostgreSQL result resource.
|
|
*
|
|
* @param resource $result The PostgreSQL result resource.
|
|
* @param int $offset The row offset.
|
|
* @return bool Returns true on success, false otherwise.
|
|
*/
|
|
function wpsql_data_seek($result, $offset)
|
|
{
|
|
return pg_result_seek($result, $offset);
|
|
}
|
|
|
|
/**
|
|
* Gets the last error message from a PostgreSQL connection.
|
|
*
|
|
* @return string Returns the error message.
|
|
*/
|
|
function wpsql_error()
|
|
{
|
|
if($GLOBALS['pg4wp_conn']) {
|
|
return pg_last_error($GLOBALS['pg4wp_conn']);
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* Fetches one row from a PostgreSQL result as an associative array.
|
|
*
|
|
* @param resource $result The PostgreSQL result resource.
|
|
* @return array Returns an associative array containing the row data.
|
|
*/
|
|
function wpsql_fetch_assoc($result)
|
|
{
|
|
return pg_fetch_assoc($result);
|
|
}
|
|
|
|
/**
|
|
* Escapes a string for use in a PostgreSQL query.
|
|
*
|
|
* @param string $s The string to escape.
|
|
* @return string Returns the escaped string.
|
|
*/
|
|
function wpsql_escape_string($s)
|
|
{
|
|
return pg_escape_string($GLOBALS['pg4wp_conn'], $s);
|
|
}
|
|
|
|
/**
|
|
* Escapes a string for use in a PostgreSQL query with a specified connection.
|
|
*
|
|
* @param string $s The string to escape.
|
|
* @param resource $c The PostgreSQL connection resource.
|
|
* @return string Returns the escaped string.
|
|
*/
|
|
function wpsql_real_escape_string($s, $c = null)
|
|
{
|
|
return pg_escape_string($c, $s);
|
|
}
|
|
|
|
/**
|
|
* Mock function to mimic MySQL's get_server_info function.
|
|
*
|
|
* @return string Returns '8.0.35' as a placeholder.
|
|
*/
|
|
function wpsql_get_server_info()
|
|
{
|
|
return '8.0.35'; // Just want to fool wordpress ...
|
|
}
|
|
|
|
|
|
/**
|
|
* Mock function to mimic MySQL's get_client_info function.
|
|
*
|
|
* @return string Returns '8.0.35' as a placeholder.
|
|
*/
|
|
function wpsql_get_client_info()
|
|
{
|
|
return '8.0.35'; // Just want to fool wordpress ...
|
|
} |