. * */ 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); } }