354 lines
7.8 KiB
PHP
354 lines
7.8 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Vvveb
|
|
*
|
|
* Copyright (C) 2022 Ziadin Givan
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as
|
|
* published by the Free Software Foundation, either version 3 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
namespace Vvveb\System\Db;
|
|
|
|
use Vvveb\System\Event;
|
|
|
|
class Pgsql extends DBDriver {
|
|
private static $link = null;
|
|
|
|
//public $error;
|
|
|
|
private $stmt;
|
|
|
|
private $last_res;
|
|
|
|
public $affected_rows = 0;
|
|
|
|
public $num_rows = 0;
|
|
|
|
public $insert_id = null;
|
|
|
|
private static $persistent = false;
|
|
|
|
public $prefix = ''; //'vv_';
|
|
|
|
public $quote = '"';
|
|
|
|
public static function version() {
|
|
if (self :: $link) {
|
|
return pg_version(self :: $link);
|
|
}
|
|
}
|
|
|
|
public static function info() {
|
|
if (self :: $link) {
|
|
return pg_version(self :: $link);
|
|
}
|
|
}
|
|
|
|
public function error() {
|
|
if (self :: $link) {
|
|
return pg_last_error(self :: $link) ?? '';
|
|
}
|
|
}
|
|
|
|
public function errorCode() {
|
|
if (self :: $link) {
|
|
return pg_last_error(self :: $link) ?? 0;
|
|
}
|
|
}
|
|
|
|
public function get_result($stmt) {
|
|
return $stmt;
|
|
}
|
|
|
|
public function __construct($host = DB_HOST, $dbname = DB_NAME, $user = DB_USER, $pass = DB_PASS, $port = DB_PORT, $prefix = DB_PREFIX) {
|
|
//return $this->connect($host, $dbname, $user, $pass, $port, $prefix);
|
|
}
|
|
|
|
public function connect($host = DB_HOST, $dbname = DB_NAME, $user = DB_USER, $pass = DB_PASS, $port = DB_PORT, $prefix = DB_PREFIX) {
|
|
if (! self :: $link) {
|
|
//port 5432 for direct pgsql connection 6432 for pgbouncer
|
|
$port = $port ?: 5432;
|
|
$connect_string = "host=$host port=$port dbname=$dbname user=$user password=$pass";
|
|
|
|
if (self :: $persistent) {
|
|
self :: $link = pg_pconnect($connect_string);
|
|
} else {
|
|
self :: $link = pg_connect($connect_string);
|
|
}
|
|
|
|
if (self :: $link) {
|
|
// pg_set_error_verbosity(self :: $link, PGSQL_ERRORS_VERBOSE);
|
|
} else {
|
|
}
|
|
}
|
|
|
|
//sync database time
|
|
//self :: $link->execute('SET `time_zone` = :zone', ['zone' => $this->escape(date('P'))]);
|
|
|
|
return self :: $link;
|
|
}
|
|
|
|
/*
|
|
* Get all columns for a table used for sanitizing input
|
|
*/
|
|
function getColumnsMeta($tableName, $comment = false) {
|
|
$sql =
|
|
"SELECT data_type as t, column_name as name, column_default as d, is_nullable as n, character_maximum_length as l FROM information_schema.columns WHERE table_name ='$tableName'";
|
|
|
|
if ($result = $this->query($sql)) {
|
|
//$columns = $result->fetch_all(MYSQLI_ASSOC);
|
|
$columns = [];
|
|
|
|
while ($row = pg_fetch_assoc($result)) {
|
|
$columns[$row['name']] = $row;
|
|
}
|
|
|
|
/* free result set */
|
|
return $columns;
|
|
} else {
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function getTableNames($db = DB_NAME) {
|
|
$sql =
|
|
"SELECT tablename as name FROM pg_catalog.pg_tables WHERE schemaname != 'pg_catalog' AND schemaname != 'information_schema' ORDER BY name";
|
|
|
|
if ($result = $this->query($sql)) {
|
|
//$names = $result->fetch_all(MYSQLI_ASSOC);
|
|
$names = [];
|
|
|
|
while ($row = pg_fetch_assoc($result)) {
|
|
//$columns[] = $row;
|
|
$names[] = $row['name'];
|
|
}
|
|
|
|
/* free result set */
|
|
return $names;
|
|
} else {
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public function escape($string) {
|
|
if (is_string($string)) {
|
|
return pg_escape_string(self :: $link, $string);
|
|
}
|
|
|
|
if (is_null($string)) {
|
|
return 'null';
|
|
}
|
|
|
|
return $string;
|
|
}
|
|
|
|
public function escapeLiteral($string) {
|
|
if (is_string($string)) {
|
|
return pg_escape_literal(self :: $link, $string);
|
|
}
|
|
|
|
if (is_null($string)) {
|
|
return 'null';
|
|
}
|
|
|
|
return $string;
|
|
}
|
|
|
|
public function sqlLimit($start, $limit) {
|
|
return "LIMIT $limit OFFSET $start";
|
|
}
|
|
|
|
public function fetchArray(&$result) {
|
|
if (pg_num_rows($result)) {
|
|
$values = pg_fetch_assoc($result);
|
|
|
|
return $values;
|
|
}
|
|
|
|
return [];
|
|
}
|
|
|
|
public function fetchAll(&$result) {
|
|
if (pg_num_rows($result)) {
|
|
$values = pg_fetch_all($result);
|
|
|
|
if ($result) {
|
|
pg_free_result($result);
|
|
$result = false;
|
|
}
|
|
|
|
return $values;
|
|
}
|
|
|
|
return [];
|
|
}
|
|
|
|
public function query($sql, $parameters = []) {
|
|
if (! self :: $link) {
|
|
$this->connect();
|
|
}
|
|
|
|
$result = false;
|
|
|
|
try {
|
|
if ($parameters) {
|
|
if (LOG_SQL_QUERIES) {
|
|
error_log($this->debugSql($sql, $parameters));
|
|
}
|
|
|
|
$this->last_res = $result = @pg_query_params(self :: $link, $sql, $parameters);
|
|
} else {
|
|
if (LOG_SQL_QUERIES) {
|
|
error_log($sql);
|
|
}
|
|
|
|
$this->last_res = $result = @pg_query(self :: $link, $sql);
|
|
}
|
|
|
|
if ($this->last_res == false) {
|
|
$errorMessage = pg_last_error(self :: $link);
|
|
$message = $errorMessage . "\n" . $this->debugSql($sql, $parameters) . "\n - " . $sql . "\n - " . print_r(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1), true);
|
|
|
|
throw new \Exception($message);
|
|
}
|
|
|
|
if ($result) {
|
|
$this->affected_rows = @pg_affected_rows($this->last_res);
|
|
}
|
|
|
|
return $this->last_res;
|
|
} catch (\Exception $e) {
|
|
$message = $e->getMessage() . "\n" . $this->debugSql($sql, $parameters) . "\n - " . $sql . "\n - " . print_r(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1), true);
|
|
|
|
throw new \Exception($message, $e->getCode());
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
public function multi_query($sql) {
|
|
return $this->query($sql);
|
|
}
|
|
|
|
public function close() {
|
|
if (self :: $link) {
|
|
return pg_close(self :: $link);
|
|
}
|
|
}
|
|
|
|
public function get_one($query, $parameters = null) {
|
|
$res = $this->query($query, $parameters);
|
|
|
|
if (pg_num_rows($res)) {
|
|
return pg_fetch_result($res, 0, 0);
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function get_row($query, $parameters) {
|
|
$res = $this->query($query . ' LIMIT 1', $parameters);
|
|
|
|
if ($res === null) {
|
|
$res = $this->last_res;
|
|
}
|
|
|
|
$values = pg_fetch_assoc($res);
|
|
|
|
if ($res) {
|
|
pg_free_result($res);
|
|
}
|
|
|
|
return $values;
|
|
}
|
|
|
|
public function get_all($query, $parameters = null) {
|
|
$res = $this->query($query, $parameters);
|
|
|
|
if (pg_num_rows($res)) {
|
|
$values = pg_fetch_all($res);
|
|
|
|
if ($res) {
|
|
pg_free_result($res);
|
|
}
|
|
|
|
return $values;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function fetch_row($res = null) {
|
|
if ($res === null) {
|
|
$res = $this->last_res;
|
|
}
|
|
|
|
$values = pg_fetch_assoc($res);
|
|
|
|
if ($res) {
|
|
pg_free_result($res);
|
|
}
|
|
|
|
return $values;
|
|
}
|
|
|
|
// Prepare
|
|
public function execute($sql, $params = [], $paramTypes = []) {
|
|
list($sql, $params) = Event::trigger(__CLASS__,__FUNCTION__, $sql, $params);
|
|
//save orig sql for debugging info
|
|
$origSql = $sql;
|
|
|
|
if (! self :: $link) {
|
|
$this->connect();
|
|
}
|
|
|
|
list($parameters, $types) = $this->paramsToQmark($sql, $params, $paramTypes, '$');
|
|
|
|
if (LOG_SQL_QUERIES) {
|
|
error_log($this->debugSql($origSql, $params, $paramTypes));
|
|
}
|
|
|
|
if ($parameters) {
|
|
$this->last_res = pg_query_params(self :: $link, $sql, $parameters);
|
|
} else {
|
|
$this->last_res = pg_query(self :: $link, $sql);
|
|
//pg_send_query(self :: $link, $sql);
|
|
//$this->last_res = pg_get_result(self :: $link);
|
|
}
|
|
|
|
if ($this->last_res) {
|
|
$this->affected_rows = @pg_affected_rows($this->last_res);
|
|
}
|
|
|
|
if ($this->last_res == false) {
|
|
$errorMessage = pg_last_error(self :: $link);
|
|
//error_log('pgsql error: ' . pg_last_error(self :: $link));
|
|
$message = $errorMessage . "\n" . $this->debugSql($origSql, $params, $paramTypes) . "\n - " . $origSql . "\n - " . print_r(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1), true);
|
|
|
|
throw new \Exception($message);
|
|
}
|
|
|
|
return $this->last_res;
|
|
}
|
|
|
|
// Bind
|
|
public function bind($param, $value, $type = null) {
|
|
$this->stmt->bindValue($param, $value, $type);
|
|
}
|
|
}
|